HEX
Server: Apache/2.4.41
System: Linux mainweb 5.4.0-182-generic #202-Ubuntu SMP Fri Apr 26 12:29:36 UTC 2024 x86_64
User: nationalmedicaregrp (1119)
PHP: 8.3.7
Disabled: exec,passthru,shell_exec,system,popen,proc_open,pcntl_exec
Upload Files
File: /home/ubuntu/bin/firefox/browser/omni.ja
�PK
!<����pp$��}Ldefaults/preferences/webide-prefs.jsPK
!<��A�l<l<��/Pdefaults/preferences/firefox.jsPK
!<s
wVV$��،defaults/preferences/firefox-l10n.jsPK
!<���pp(��p�defaults/preferences/firefox-branding.jsPK
!<eMPd�?�? ��&�defaults/preferences/devtools.jsPK
!<nd;��.���defaults/preferences/devtools-startup-prefs.jsPK
!<f¯��� ��5�defaults/preferences/debugger.jsPK
!<�Y.HH
�chrome.manifestPK
!<�?��VV��chrome/chrome.manifestPK
!<��tL�%�%�components/components.manifestPK
!<*�0���.components/interfaces.xptPK
!<\A;���3��")chrome/browser/content/browser/built_in_addons.jsonPK
!<�AGG-��Y*chrome/en-US/locale/branding/brand.propertiesPK
!<�C�����.���+chrome/en-US/locale/browser/browser.propertiesPK
!<:�ll4���chrome/en-US/locale/browser-region/region.propertiesPK
!<�Ndd;���chrome/browser/content/browser/search/searchbarBindings.cssPK
!<�"�pp1���chrome/browser/skin/classic/browser/searchbar.cssPK
!<޶]I��@ chrome/en-US/locale/browser/customizableui/customizableWidgets.propertiesPK
!<�E��=	=	1���1chrome/en-US/locale/browser/tabbrowser.propertiesPK
!<S���5��M;chrome/en-US/locale/branding/browserconfig.propertiesPK
!<��S�k	k	@���<chrome/en-US/locale/en-US/devtools/shim/key-shortcuts.propertiesPK
!<iMu��:���Fchrome/en-US/locale/en-US/devtools/shim/startup.propertiesPK
!<�S��((��yHmodules/ContentObservers.jsPK
!<��碉��!���Scomponents/WebContentConverter.jsPK
!<OăD������components/nsBrowserGlue.jsPK
!<-j�X4X4����	components/nsSessionStartup.jsPK
!<��*:8:8 ��T
components/EnterprisePolicies.jsPK
!<�"'ϵ����P
defaults/permissionsPK
!<�]��S�S�*���T
chrome/browser/content/browser/browser.xulPK
!<���w����*��NN
chrome/browser/content/browser/browser.cssPK
!<�sQp�+�+6���
chrome/browser/content/browser/downloads/downloads.cssPK
!<��:i

0��Y!chrome/browser/content/browser/places/places.cssPK
!<���[AA:���%chrome/browser/content/browser/usercontext/usercontext.cssPK
!<�l��4�4;��M2chrome/browser/skin/classic/browser/controlcenter/panel.cssPK
!<�9M9J�J�>���gchrome/browser/skin/classic/browser/customizableui/panelUI.cssPK
!<j1�mT0T0;��'chrome/browser/skin/classic/browser/downloads/downloads.cssPK
!<��	��8�8/���Nchrome/browser/skin/classic/browser/browser.cssPK
!<^�$�`
`
-����chrome/browser/content/browser/tabbrowser.cssPK
!<ITp8==C����chrome/browser/skin/classic/browser/tabbrowser/tabDragIndicator.pngPK
!<;�z��2�C�chrome/browser/skin/classic/browser/arrow-left.svgPK
!<|���I��Q�chrome/browser/skin/classic/browser/tabbrowser/tab-overflow-indicator.pngPK
!<Y�O���9����chrome/browser/skin/classic/browser/tabbrowser/newtab.svgPK
!<����,����chrome/browser/skin/classic/browser/back.svgPK
!<�L<��/��Ýchrome/browser/skin/classic/browser/forward.svgPK
!<�l2&&.��
�chrome/browser/skin/classic/browser/reload.svgPK
!<qM���,���chrome/browser/skin/classic/browser/home.svgPK
!<o".���4��O�chrome/browser/skin/classic/browser/search-glass.svgPK
!<�
�TT/����chrome/browser/skin/classic/browser/library.svgPK
!<��|��,��6�chrome/browser/skin/classic/browser/menu.svgPK
!<U�,uu@��i�chrome/browser/skin/classic/browser/places/toolbarDropMarker.pngPK
!<�t$�II/��<�chrome/browser/skin/classic/browser/chevron.svgPK
!<䴅�II0��Ұchrome/browser/skin/classic/browser/sidebars.svgPK
!<,��X����i�modules/SchedulePressure.jsmPK
!<��Z�  ,�Q�chrome/browser/skin/classic/browser/stop.svgPK
!<^iu�77����modules/ReaderParent.jsmPK
!<�8������(�modules/webrtcUI.jsmPK
!<3���#��y�modules/syncedtabs/EventEmitter.jsmPK
!<�G�^�^����modules/SitePermissions.jsmPK
!<��_a""5큳�chrome/browser/skin/classic/browser/identity-icon.svgPK
!<���|��3��(�chrome/browser/skin/classic/browser/page-action.svgPK
!<4!�$$7��R�chrome/browser/skin/classic/browser/bookmark-hollow.svgPK
!<!�g���������modules/PanelMultiView.jsmPK
!<=n~̄�!����modules/FormValidationHandler.jsmPK
!<n�Lř�3����chrome/en-US/locale/browser/searchplugins/list.jsonPK
!<4�A�/
/
2����chrome/browser/content/browser/contentSearchUI.cssPK
!<b���v�v1���chrome/browser/content/browser/contentSearchUI.jsPK
!<a�89>H>H��smodules/ContentSearch.jsmPK
!<��;����chrome/en-US/locale/browser/searchplugins/images/amazon.icoPK
!<�	���9��^�chrome/en-US/locale/browser/searchplugins/images/ebay.icoPK
!<ee66>��d�chrome/en-US/locale/browser/searchplugins/images/wikipedia.icoPK
!<��H���-����chrome/en-US/locale/browser/search.propertiesPK
!<i��"�q�q����modules/BrowserUITelemetry.jsmPK
!<)�-������Jmodules/Sanitizer.jsmPK
!<Ǵ��1�1%����modules/sessionstore/SessionWorker.jsPK
!<��]��0���
chrome/browser/skin/classic/browser/settings.svgPK
!<��=ۉ,�,)���chrome/pdfjs/content/PdfjsChromeUtils.jsmPK
!<����'���;modules/sessionstore/SessionStorage.jsmPK
!<z��-�����[modules/DownloadsTaskbar.jsmPK
!<2�B���'��Kumodules/sessionstore/SessionCookies.jsmPK
!<�s~�� � -���chrome/devtools-shim/content/DevToolsShim.jsmPK
!<��	�qq&��M�modules/sessionstore/PrivacyFilter.jsmPK
!<�)j��:���chrome/browser/skin/classic/browser/tabbrowser/loading.svgPK
!<�S؈��2��1�chrome/browser/skin/classic/browser/readerMode.svgPK
!<�+QLL@���chrome/browser/skin/classic/browser/tabbrowser/loading-burst.svgPK
!<�h��3�36����chrome/browser/skin/classic/browser/reload-to-stop.svgPK
!<4\��%���localization/en-US/branding/brand.ftlPK
!<�xt�gg2���localization/en-US/browser/branding/sync-brand.ftlPK
!<��''6���localization/en-US/browser/preferences/preferences.ftlPK
!<X�"�\9\9���:components/FeedConverter.jsPK
!<�ahOQ�Q����tcomponents/FeedWriter.jsPK
!<�*X1�z�z%���components/nsBrowserContentHandler.jsPK
!<�W~n��!���pcomponents/nsSetDefaultBrowser.jsPK
!<q��r�����tcomponents/devtools-startup.jsPK
!<{6l��)��J�components/aboutdebugging-registration.jsPK
!<����  (��}�components/aboutdevtools-registration.jsPK
!<�he��/���components/aboutdevtoolstoolbox-registration.jsPK
!<����!���components/nsAboutCapabilities.jsPK
!<��%RR ���components/ExperimentsService.jsPK
!<O��Lk"k" ��� components/aboutNewTabService.jsPK
!<�eDž�
�
'��,Ccomponents/EnterprisePoliciesContent.jsPK
!<�������Mcomponents/nsSessionStore.jsPK
!<ၬ�		���Scomponents/ProfileMigrator.jsPK
!<��/�JJ#��)Wcomponents/ChromeProfileMigrator.jsPK
!<m�ޓ�*�*$����components/FirefoxProfileMigrator.jsPK
!<a�'������modules/AboutHome.jsmPK
!</<�vv����modules/AboutNewTab.jsmPK
!<���s�s���O�modules/AsyncTabSwitcher.jsmPK
!<��0g������modules/AttributionCode.jsmPK
!<���]�d�d����modules/AutoMigrate.jsmPK
!<�0N�� ���modules/BrowserErrorReporter.jsmPK
!<F<m��Y�Y!���modules/BrowserUsageTelemetry.jsmPK
!<1lV��'�' ���omodules/ChromeMigrationUtils.jsmPK
!<�M4u����ϗmodules/ContentClick.jsmPK
!<=�F7ΆΆ ����modules/ContentCrashHandlers.jsmPK
!<��Z��-�-���.modules/ContentLinkHandler.jsmPK
!<&�������\modules/ContentMetaHandler.jsmPK
!<ElЊ�@�@��irmodules/ContentWebRTC.jsmPK
!<�22i$�$���u�modules/ContextMenu.jsmPK
!<�(B�������<modules/CustomizableUI.jsmPK
!<v%Q:������modules/CustomizableWidgets.jsmPK
!<������\ modules/CustomizeMode.jsmPK
!<ou}~�~����!modules/DownloadsCommon.jsmPK
!<f�ثp=p=��Ɣ"modules/DownloadsSubview.jsmPK
!<�r�H�H��p�"modules/DownloadsViewUI.jsmPK
!<�A׊n-n-��5#modules/DragPositionManager.jsmPK
!<��7!<D<D���H#modules/ExtensionPopups.jsmPK
!<�	Ty=y=��U�#modules/ExtensionsUI.jsmPK
!<hF`�����#modules/Feeds.jsmPK
!<Ɯ8P//����#modules/FormSubmitObserver.jsmPK
!<N�YY��,�#modules/HeadlessShell.jsmPK
!<EZ44���$modules/LaterRun.jsmPK
!<kC�������"!$modules/MigrationUtils.jsmPK
!<��H�
�
����$modules/OpenInTabsUtils.jsmPK
!<�XQ�w�w����$modules/PageActions.jsmPK
!<+?_���&���{%modules/ParseBreakpadSymbols-worker.jsPK
!<�r0ff%���%modules/ParseCppFiltSymbols-worker.jsPK
!<X9t�
�
 ����%modules/ParseNMSymbols-worker.jsPK
!<��������%modules/ParseSymbols.jsmPK
!<$?�-]-]��n�%modules/PermissionUI.jsmPK
!<�''h^^���%modules/PingCentre.jsmPK
!<�M������c&modules/PlacesUIUtils.jsmPK
!<=�h�|�|���?�&modules/PluginContent.jsmPK
!<io���B�B���'modules/ProcessHangMonitor.jsmPK
!<��Ah]	]	�� �'modules/RecentWindow.jsmPK
!<h��_55����'modules/RemotePrompt.jsmPK
!<�Tn������'modules/ScrollbarSampler.jsmPK
!<��h�\
\
��R(modules/SearchWidgetTracker.jsmPK
!<y�-9���(modules/ShellService.jsmPK
!<_��H�9�9��#(modules/SiteDataManager.jsmPK
!<+�e�����U(modules/ThemeVariableMap.jsmPK
!<)ou:�����[(modules/TransientPrefs.jsmPK
!<�66���^(modules/UITour.jsmPK
!<��UU/��Ib)modules/UpdateTopLevelContentWindowIDHelper.jsmPK
!<� 7�����s)modules/ZoomUI.jsmPK
!<˧�αB�B���)modules/distribution.jsPK
!<��B
B
#����)modules/experiments/Experiments.jsmPK
!<��U��l�*modules/offlineAppCache.jsmPK
!<bF{!&&&����*modules/policies/BookmarksPolicies.jsmPK
!<��N�p�p���*modules/policies/Policies.jsmPK
!<$���&���n+modules/policies/PoliciesValidator.jsmPK
!<�bF�
�
"��À+modules/policies/ProxyPolicies.jsmPK
!<��u"����+modules/policies/WebsiteFilter.jsmPK
!<T�Rp9p9����+modules/policies/schema.jsmPK
!<j��E=F=F'����+modules/sessionstore/ContentRestore.jsmPK
!<�+�e-��",modules/sessionstore/DocShellCapabilities.jsmPK
!<6��ZZ$���$,modules/sessionstore/GlobalState.jsmPK
!<����M"M">��',,modules/sessionstore/RecentlyClosedTabsAndWindowsMenuUtils.jsmPK
!<��Z^;
;
!���N,modules/sessionstore/RunState.jsmPK
!<��;RBRB$��J\,modules/sessionstore/SessionFile.jsmPK
!<���
�
)��ޞ,modules/sessionstore/SessionMigration.jsmPK
!<I)�2�2%���,modules/sessionstore/SessionSaver.jsmPK
!<g�s�s�%����,modules/sessionstore/SessionStore.jsmPK
!<s���&����/modules/sessionstore/SessionWorker.jsmPK
!<v��ߴ!�!+��Ω/modules/sessionstore/StartupPerformance.jsmPK
!<5n�z		&����/modules/sessionstore/TabAttributes.jsmPK
!<�����!��-�/modules/sessionstore/TabState.jsmPK
!<X��jj&���/modules/sessionstore/TabStateCache.jsmPK
!<�`x��(���0modules/sessionstore/TabStateFlusher.jsmPK
!<�V�H��-���0modules/syncedtabs/SyncedTabsDeckComponent.jsPK
!<�ͩ{��)��130modules/syncedtabs/SyncedTabsDeckStore.jsPK
!<}O���
�
(��!:0modules/syncedtabs/SyncedTabsDeckView.jsPK
!<���p66)��6E0modules/syncedtabs/SyncedTabsListStore.jsPK
!<�����&���`0modules/syncedtabs/TabListComponent.jsPK
!<r���E�E!���q0modules/syncedtabs/TabListView.jsPK
!<!������0modules/syncedtabs/util.jsPK
!<h�ˎu:u:&��l�0modules/translation/BingTranslator.jsmPK
!<��VV(��%�0modules/translation/LanguageDetector.jsmPK
!<�uN�h7h7#���1modules/translation/Translation.jsmPK
!<���1��j@1modules/translation/TranslationContentHandler.jsmPK
!<�ȴ��Z�Z+��lX1modules/translation/TranslationDocument.jsmPK
!<�<�....(����1modules/translation/YandexTranslator.jsmPK
!<
�=A�_�_!���1modules/translation/cld-worker.jsPK
!<`ɖ/�/�%��)B4modules/translation/cld-worker.js.memPK
!<]��AqQqQ.���Echrome/browser/content/branding/about-logo.pngPK
!<E}�����1��XREchrome/browser/content/branding/[email protected]
!<O����2��NFchrome/browser/content/branding/about-wordmark.svgPK
!<��1�%A%A)���!Fchrome/browser/content/branding/about.pngPK
!<tߩh��/��cFchrome/browser/content/branding/aboutDialog.cssPK
!<��at{1{1+���gFchrome/browser/content/branding/icon128.pngPK
!<LF*����Fchrome/browser/content/branding/icon16.pngPK
!<��jaa*��	�Fchrome/browser/content/branding/icon32.pngPK
!<�^�*����Fchrome/browser/content/branding/icon48.pngPK
!<���00*���Fchrome/browser/content/branding/icon64.pngPK
!<�	�@hh8����Fchrome/browser/content/branding/identity-icons-brand.svgPK
!<�o昕�$��G�Fchrome/browser/content/briefcase.svgPK
!<�ה�!9!98���Fchrome/browser/content/browser/aboutDialog-appUpdater.jsPK
!<'����.���Gchrome/browser/content/browser/aboutDialog.cssPK
!<�Fn�k
k
-���Gchrome/browser/content/browser/aboutDialog.jsPK
!<����.��=*Gchrome/browser/content/browser/aboutDialog.xulPK
!<�e�q�q2���EGchrome/browser/content/browser/aboutNetError.xhtmlPK
!<ٚ����7����Gchrome/browser/content/browser/aboutPrivateBrowsing.cssPK
!<-�Q���6����Gchrome/browser/content/browser/aboutPrivateBrowsing.jsPK
!<�29����Gchrome/browser/content/browser/aboutPrivateBrowsing.xhtmlPK
!<
j�]��3����Gchrome/browser/content/browser/aboutRobots-icon.pngPK
!<k�O�VV:����Gchrome/browser/content/browser/aboutRobots-widget-left.pngPK
!<�x���.����Gchrome/browser/content/browser/aboutRobots.cssPK
!<��vR00-����Gchrome/browser/content/browser/aboutRobots.jsPK
!<pe

0���Gchrome/browser/content/browser/aboutRobots.xhtmlPK
!<,��.�.5��jHchrome/browser/content/browser/aboutSessionRestore.jsPK
!<�*b���8���6Hchrome/browser/content/browser/aboutSessionRestore.xhtmlPK
!<䉯���2���BHchrome/browser/content/browser/aboutTabCrashed.cssPK
!<T!V��!�!1���DHchrome/browser/content/browser/aboutTabCrashed.jsPK
!<Y�j�
�
4���fHchrome/browser/content/browser/aboutTabCrashed.xhtmlPK
!<\�b]

5���tHchrome/browser/content/browser/aboutWelcomeBack.xhtmlPK
!<�N���&�&6��I�Hchrome/browser/content/browser/abouthome/aboutHome.cssPK
!<X�í2�25��a�Hchrome/browser/content/browser/abouthome/aboutHome.jsPK
!<�
_8��a�Hchrome/browser/content/browser/abouthome/aboutHome.xhtmlPK
!<��;YY3����Hchrome/browser/content/browser/abouthome/addons.pngPK
!<���)FF6����Hchrome/browser/content/browser/abouthome/[email protected]
!<+�L��6���Hchrome/browser/content/browser/abouthome/bookmarks.pngPK
!<1gkC		9��KIchrome/browser/content/browser/abouthome/[email protected]
!<!ƣ�__6���
Ichrome/browser/content/browser/abouthome/downloads.pngPK
!<�\��^^9��^Ichrome/browser/content/browser/abouthome/[email protected]
!<��4��Ichrome/browser/content/browser/abouthome/history.pngPK
!<Ǵ�Qoo7��{Ichrome/browser/content/browser/abouthome/[email protected]
!<вr��
�
:��?1Ichrome/browser/content/browser/abouthome/restore-large.pngPK
!<4GZK=��!<Ichrome/browser/content/browser/abouthome/[email protected]
!<�>��4���WIchrome/browser/content/browser/abouthome/restore.pngPK
!<��X���7���^Ichrome/browser/content/browser/abouthome/[email protected]
!<䧅
��5���pIchrome/browser/content/browser/abouthome/settings.pngPK
!<�Zb]]8��-wIchrome/browser/content/browser/abouthome/[email protected]
!<}�\��5���Ichrome/browser/content/browser/abouthome/snippet1.pngPK
!<��ʧ��8����Ichrome/browser/content/browser/abouthome/[email protected]
!<�e��5����Ichrome/browser/content/browser/abouthome/snippet2.pngPK
!<i�	��)�)8���Ichrome/browser/content/browser/abouthome/[email protected]
!<R�o�

1��B�Ichrome/browser/content/browser/abouthome/sync.pngPK
!<;T8gg4����Ichrome/browser/content/browser/abouthome/[email protected]
!<��p;-$-$0��W�Ichrome/browser/content/browser/blockedSite.xhtmlPK
!<��e��:���Jchrome/browser/content/browser/bookmarks/bookmarksPanel.jsPK
!<�4���;���Jchrome/browser/content/browser/bookmarks/bookmarksPanel.xulPK
!<�*Ј\\8��Jchrome/browser/content/browser/bookmarks/sidebarUtils.jsPK
!<�;1ZhZh0���.Jchrome/browser/content/browser/browser-addons.jsPK
!<��#�=%=%7��h�Jchrome/browser/content/browser/browser-captivePortal.jsPK
!<<A�L�
�
6����Jchrome/browser/content/browser/browser-compacttheme.jsPK
!<	�ZwCECE1����Jchrome/browser/content/browser/browser-ctrlTab.jsPK
!<
��	�	7���
Kchrome/browser/content/browser/browser-customization.jsPK
!<����@@B���Kchrome/browser/content/browser/browser-data-submission-info-bar.jsPK
!<��;��Z�Z/��S'Kchrome/browser/content/browser/browser-feeds.jsPK
!<
�Ef�]�]B��M�Kchrome/browser/content/browser/browser-fullScreenAndPointerLock.jsPK
!<����J�J2��5�Kchrome/browser/content/browser/browser-fullZoom.jsPK
!<�M9�9�8��
+Lchrome/browser/content/browser/browser-gestureSupport.jsPK
!<���:�:/����Lchrome/browser/content/browser/browser-media.jsPK
!<f�p�ׅׅ5���Mchrome/browser/content/browser/browser-pageActions.jsPK
!<c����0���Mchrome/browser/content/browser/browser-places.jsPK
!<Hih�i�i1��ٙNchrome/browser/content/browser/browser-plugins.jsPK
!<��$LL6��Ochrome/browser/content/browser/browser-safebrowsing.jsPK
!<B�lvJvJ1���Ochrome/browser/content/browser/browser-sidebar.jsPK
!<�N��b�b.��mZOchrome/browser/content/browser/browser-sync.jsPK
!<��ALL6��m�Ochrome/browser/content/browser/browser-tabPreviews.xmlPK
!<����..8��
�Ochrome/browser/content/browser/browser-tabsintitlebar.jsPK
!<�S�W4��v�Ochrome/browser/content/browser/browser-thumbnails.jsPK
!<ώL!!<���
Pchrome/browser/content/browser/browser-trackingprotection.jsPK
!<i�ff3��J,Pchrome/browser/content/browser/browser-webrender.jsPK
!<|����/�/)��/Pchrome/browser/content/browser/browser.jsPK
!<z`��440��_Uchrome/browser/content/browser/content-UITour.jsPK
!<��c�{�{6���jUchrome/browser/content/browser/content-sessionStore.jsPK
!<P������)��w�Uchrome/browser/content/browser/content.jsPK
!<��4�u�u8����Vchrome/browser/content/browser/customizableui/panelUI.jsPK
!<u�W�119���Wchrome/browser/content/browser/customizableui/toolbar.xmlPK
!<b��&&5��-CWchrome/browser/content/browser/default-theme-icon.svgPK
!<�=\��9���GWchrome/browser/content/browser/defaultthemes/1.header.jpgPK
!<��EE7���X[chrome/browser/content/browser/defaultthemes/1.icon.jpgPK
!<�d:��5][chrome/browser/content/browser/defaultthemes/1.preview.jpgPK
!<Q����9���|[chrome/browser/content/browser/defaultthemes/2.header.jpgPK
!<��uM��7���$^chrome/browser/content/browser/defaultthemes/2.icon.jpgPK
!<X��==:���&^chrome/browser/content/browser/defaultthemes/2.preview.jpgPK
!<9�6�(Z(Z9��{2^chrome/browser/content/browser/defaultthemes/3.header.pngPK
!<J21@@7����achrome/browser/content/browser/defaultthemes/3.icon.pngPK
!<y�������:����achrome/browser/content/browser/defaultthemes/3.preview.pngPK
!<l�D�
�
9���Ibchrome/browser/content/browser/defaultthemes/4.header.pngPK
!<�T�L��7���hlchrome/browser/content/browser/defaultthemes/4.icon.pngPK
!<��Y0Y0:���klchrome/browser/content/browser/defaultthemes/4.preview.pngPK
!<�T�R��9��v�mchrome/browser/content/browser/defaultthemes/5.header.pngPK
!<*���7����mchrome/browser/content/browser/defaultthemes/5.icon.jpgPK
!<�Ě:���mchrome/browser/content/browser/defaultthemes/5.preview.jpgPK
!<���)):��W�mchrome/browser/content/browser/defaultthemes/dark.icon.svgPK
!<.5��99;��سmchrome/browser/content/browser/defaultthemes/light.icon.svgPK
!<�ir|�e�eC��j�mchrome/browser/content/browser/downloads/allDownloadsViewOverlay.jsPK
!<���XXD��jnchrome/browser/content/browser/downloads/allDownloadsViewOverlay.xulPK
!<]"��YYE��$4nchrome/browser/content/browser/downloads/contentAreaDownloadsView.cssPK
!<ޜPD���5nchrome/browser/content/browser/downloads/contentAreaDownloadsView.jsPK
!<<�CCE��B9nchrome/browser/content/browser/downloads/contentAreaDownloadsView.xulPK
!<��|��5���Gnchrome/browser/content/browser/downloads/download.xmlPK
!<��N�N�5���anchrome/browser/content/browser/downloads/downloads.jsPK
!<>�`A6N6N5��dochrome/browser/content/browser/downloads/indicator.jsPK
!<]\v��*�*/���fochrome/browser/content/browser/ext-bookmarks.jsPK
!<�}�BlBl-��6�ochrome/browser/content/browser/ext-browser.jsPK
!<\j�п�/���ochrome/browser/content/browser/ext-browser.jsonPK
!<�b��XX3���pchrome/browser/content/browser/ext-browserAction.jsPK
!<ON	��&�&2��&lpchrome/browser/content/browser/ext-browsingData.jsPK
!<�G'��/����pchrome/browser/content/browser/ext-c-browser.jsPK
!<!u�@��&�pchrome/browser/content/browser/ext-c-devtools-inspectedWindow.jsPK
!<}��3��8����pchrome/browser/content/browser/ext-c-devtools-network.jsPK
!<\y�k$k$7��¢pchrome/browser/content/browser/ext-c-devtools-panels.jsPK
!<�@����0����pchrome/browser/content/browser/ext-c-devtools.jsPK
!<�z�ZZ-����pchrome/browser/content/browser/ext-c-menus.jsPK
!<t<��/��X�pchrome/browser/content/browser/ext-c-omnibox.jsPK
!<�����,����pchrome/browser/content/browser/ext-c-tabs.jsPK
!<��B��$�$?��R�pchrome/browser/content/browser/ext-chrome-settings-overrides.jsPK
!<�;r�0�0.���qchrome/browser/content/browser/ext-commands.jsPK
!<S����>���@qchrome/browser/content/browser/ext-devtools-inspectedWindow.jsPK
!<��Pu		6���Hqchrome/browser/content/browser/ext-devtools-network.jsPK
!<UEC�yPyP5��,Rqchrome/browser/content/browser/ext-devtools-panels.jsPK
!<�;Z22.����qchrome/browser/content/browser/ext-devtools.jsPK
!<HB`���*��W�qchrome/browser/content/browser/ext-find.jsPK
!<�N��,�,3��O�qchrome/browser/content/browser/ext-geckoProfiler.jsPK
!<�Yq��"�"-��8rchrome/browser/content/browser/ext-history.jsPK
!<�g�uiui+��J7rchrome/browser/content/browser/ext-menus.jsPK
!<�ՙ��-���rchrome/browser/content/browser/ext-omnibox.jsPK
!<��7,�6�60��5�rchrome/browser/content/browser/ext-pageAction.jsPK
!<��O(��,��W�rchrome/browser/content/browser/ext-pkcs11.jsPK
!<C���.��J�rchrome/browser/content/browser/ext-sessions.jsPK
!<%��H5H53���schrome/browser/content/browser/ext-sidebarAction.jsPK
!<$ H�H�*��JQschrome/browser/content/browser/ext-tabs.jsPK
!<N}���$�$3����schrome/browser/content/browser/ext-url-overrides.jsPK
!<0�_�&�&-���tchrome/browser/content/browser/ext-windows.jsPK
!<טu�5�5,���@tchrome/browser/content/browser/extension.cssPK
!<ҍ�Xuu,���vtchrome/browser/content/browser/extension.svgPK
!<U�"�1��g|tchrome/browser/content/browser/feeds/subscribe.jsPK
!<x���4���tchrome/browser/content/browser/feeds/subscribe.xhtmlPK
!<��c/����tchrome/browser/content/browser/hiddenWindow.xulPK
!<���7��8��b�tchrome/browser/content/browser/history/history-panel.xulPK
!<�K~qzEzEI��H�tchrome/browser/content/browser/illustrations/error-connection-failure.svgPK
!<��m�VEVED��)�tchrome/browser/content/browser/illustrations/error-malformed-url.svgPK
!<C�)�J�JG���,uchrome/browser/content/browser/illustrations/error-server-not-found.svgPK
!<�OF�F�+��/xuchrome/browser/content/browser/license.htmlPK
!<������C���Fzchrome/browser/content/browser/microsoft-translator-attribution.pngPK
!<�����>�>5���Ozchrome/browser/content/browser/migration/migration.jsPK
!<�"��6����zchrome/browser/content/browser/migration/migration.xulPK
!<�`P,��/��U�zchrome/browser/content/browser/nsContextMenu.jsPK
!<�=@�gg0��u�{chrome/browser/content/browser/pageinfo/feeds.jsPK
!<&s۞��4��*�{chrome/browser/content/browser/pageinfo/pageInfo.cssPK
!<�4
�?�?�3��n�{chrome/browser/content/browser/pageinfo/pageInfo.jsPK
!<�֌)�F�F4���8|chrome/browser/content/browser/pageinfo/pageInfo.xulPK
!<���

6���|chrome/browser/content/browser/pageinfo/permissions.jsPK
!<�k���,�,3��N�|chrome/browser/content/browser/pageinfo/security.jsPK
!<�XdޣH�H;��K�|chrome/browser/content/browser/places/bookmarkProperties.jsPK
!<Fz���<��G}chrome/browser/content/browser/places/bookmarkProperties.xulPK
!<Fz���=���}chrome/browser/content/browser/places/bookmarkProperties2.xulPK
!<:w�^�<�<;���}chrome/browser/content/browser/places/browserPlacesViews.jsPK
!<TC#�#�3���W~chrome/browser/content/browser/places/controller.jsPK
!<�1}ff>��`%chrome/browser/content/browser/places/downloadsViewOverlay.xulPK
!<AE
�
�<��",chrome/browser/content/browser/places/editBookmarkOverlay.jsPK
!<&z
�| | =����chrome/browser/content/browser/places/editBookmarkOverlay.xulPK
!<�y?��6��]�chrome/browser/content/browser/places/history-panel.jsPK
!<���i`i`.����chrome/browser/content/browser/places/menu.xmlPK
!<&���3��L_�chrome/browser/content/browser/places/organizer.cssPK
!<�8���/���`�chrome/browser/content/browser/places/places.jsPK
!<�sSOIOI0���(�chrome/browser/content/browser/places/places.xulPK
!<q�H�X*X*7���r�chrome/browser/content/browser/places/placesOverlay.xulPK
!<��q`bxbx.��A��chrome/browser/content/browser/places/tree.xmlPK
!<��g1����chrome/browser/content/browser/places/treeView.jsPK
!<<�r((@��L�chrome/browser/content/browser/preferences/applicationManager.jsPK
!<��D�++A���&�chrome/browser/content/browser/preferences/applicationManager.xulPK
!<%e�SS8��\/�chrome/browser/content/browser/preferences/blocklists.jsPK
!<L�g��9��D�chrome/browser/content/browser/preferences/blocklists.xulPK
!<Qێ���<��CM�chrome/browser/content/browser/preferences/clearSiteData.cssPK
!<i/S�||;���O�chrome/browser/content/browser/preferences/clearSiteData.jsPK
!<wފ

<��d[�chrome/browser/content/browser/preferences/clearSiteData.xulPK
!<��c���4���e�chrome/browser/content/browser/preferences/colors.jsPK
!<��3���5���h�chrome/browser/content/browser/preferences/colors.xulPK
!<]W}�{-{-8���y�chrome/browser/content/browser/preferences/connection.jsPK
!<�3��$#$#9�����chrome/browser/content/browser/preferences/connection.xulPK
!<���^^8��2˃chrome/browser/content/browser/preferences/containers.jsPK
!<�߀��9���߃chrome/browser/content/browser/preferences/containers.xulPK
!<mu��5����chrome/browser/content/browser/preferences/cookies.jsPK
!<����oo6���h�chrome/browser/content/browser/preferences/cookies.xulPK
!<�_	3���{�chrome/browser/content/browser/preferences/fonts.jsPK
!<��`�;<;<4����chrome/browser/content/browser/preferences/fonts.xulPK
!<�/��7���΄chrome/browser/content/browser/preferences/handlers.cssPK
!<�l���7��ӄchrome/browser/content/browser/preferences/handlers.xmlPK
!<Q���..C��7�chrome/browser/content/browser/preferences/in-content/containers.jsPK
!<��^��L����chrome/browser/content/browser/preferences/in-content/extensionControlled.jsPK
!<��fLLC����chrome/browser/content/browser/preferences/in-content/findInPage.jsPK
!<n���AA=��>]�chrome/browser/content/browser/preferences/in-content/main.jsPK
!<�!_Y�9�9D���^�chrome/browser/content/browser/preferences/in-content/preferences.jsPK
!<�y�fMfME��ߘ�chrome/browser/content/browser/preferences/in-content/preferences.xulPK
!<�.F߼���@����chrome/browser/content/browser/preferences/in-content/privacy.jsPK
!<Y��z�X�X?����chrome/browser/content/browser/preferences/in-content/search.jsPK
!<Z)���W�WC����chrome/browser/content/browser/preferences/in-content/subdialogs.jsPK
!<T���H�H=���q�chrome/browser/content/browser/preferences/in-content/sync.jsPK
!<��	�k/k/7����chrome/browser/content/browser/preferences/languages.jsPK
!<��|8����chrome/browser/content/browser/preferences/languages.xulPK
!<�h�8�89��S��chrome/browser/content/browser/preferences/permissions.jsPK
!<>�����:��;7�chrome/browser/content/browser/preferences/permissions.xulPK
!<Z\���6��'G�chrome/browser/content/browser/preferences/sanitize.jsPK
!<?z.}TT7��xL�chrome/browser/content/browser/preferences/sanitize.xulPK
!<��`�<��!\�chrome/browser/content/browser/preferences/selectBookmark.jsPK
!<^��NN=���h�chrome/browser/content/browser/preferences/selectBookmark.xulPK
!<��5``D��4o�chrome/browser/content/browser/preferences/siteDataRemoveSelected.jsPK
!<�E���s�chrome/browser/content/browser/preferences/siteDataRemoveSelected.xulPK
!<FO�w>&>&>��u|�chrome/browser/content/browser/preferences/siteDataSettings.jsPK
!<0�>�{
{
?����chrome/browser/content/browser/preferences/siteDataSettings.xulPK
!<J*�N��>��筋chrome/browser/content/browser/preferences/sitePermissions.cssPK
!<RkF/F/=��ɰ�chrome/browser/content/browser/preferences/sitePermissions.jsPK
!<�48>��j�chrome/browser/content/browser/preferences/sitePermissions.xulPK
!<ӧR��9����chrome/browser/content/browser/preferences/translation.jsPK
!<����:���	�chrome/browser/content/browser/preferences/translation.xulPK
!<� Q��(����chrome/browser/content/browser/robot.icoPK
!<]����+��
 �chrome/browser/content/browser/safeMode.cssPK
!<eT.�9	9	*��R!�chrome/browser/content/browser/safeMode.jsPK
!<�&_��+���*�chrome/browser/content/browser/safeMode.xulPK
!<�E��+��
2�chrome/browser/content/browser/sanitize.xulPK
!<��P<<1���I�chrome/browser/content/browser/sanitizeDialog.cssPK
!<���%  0��lL�chrome/browser/content/browser/sanitizeDialog.jsPK
!<��dtoFoF5���l�chrome/browser/content/browser/schemas/bookmarks.jsonPK
!<u���==:��|��chrome/browser/content/browser/schemas/browser_action.jsonPK
!<�k+E�3�39����chrome/browser/content/browser/schemas/browsing_data.jsonPK
!<�&6�ccE��%�chrome/browser/content/browser/schemas/chrome_settings_overrides.jsonPK
!<bi�٢�4���4�chrome/browser/content/browser/schemas/commands.jsonPK
!<�6kk4���K�chrome/browser/content/browser/schemas/devtools.jsonPK
!<�����/�/E���N�chrome/browser/content/browser/schemas/devtools_inspected_window.jsonPK
!<,�c}}<���~�chrome/browser/content/browser/schemas/devtools_network.jsonPK
!<�T�b�:�:;��o��chrome/browser/content/browser/schemas/devtools_panels.jsonPK
!<mI�aa0��Yƍchrome/browser/content/browser/schemas/find.jsonPK
!<{f��\\9��Սchrome/browser/content/browser/schemas/geckoProfiler.jsonPK
!<j����)�)3����chrome/browser/content/browser/schemas/history.jsonPK
!<b�g�G�G1����chrome/browser/content/browser/schemas/menus.jsonPK
!<���w��:���W�chrome/browser/content/browser/schemas/menus_internal.jsonPK
!<'2����3��d�chrome/browser/content/browser/schemas/omnibox.jsonPK
!<���l�&�&7��,��chrome/browser/content/browser/schemas/page_action.jsonPK
!<�ZQC��2��M��chrome/browser/content/browser/schemas/pkcs11.jsonPK
!<O�t��&�&4��6��chrome/browser/content/browser/schemas/sessions.jsonPK
!<䀭� � :��n֎chrome/browser/content/browser/schemas/sidebar_action.jsonPK
!<��X����0�����chrome/browser/content/browser/schemas/tabs.jsonPK
!<_%��WW9����chrome/browser/content/browser/schemas/url_overrides.jsonPK
!<����U�U3��B�chrome/browser/content/browser/schemas/windows.jsonPK
!<�;Y=�y�y0��D�chrome/browser/content/browser/search/search.xmlPK
!<�T���4���chrome/browser/content/browser/search/searchReset.jsPK
!<\��d		7��9ʑchrome/browser/content/browser/search/searchReset.xhtmlPK
!<
0���6���ӑchrome/browser/content/browser/setDesktopBackground.jsPK
!<qz		7����chrome/browser/content/browser/setDesktopBackground.xulPK
!<�.����/��r��chrome/browser/content/browser/static-robot.pngPK
!<�Ӻ`4�����chrome/browser/content/browser/syncedtabs/sidebar.jsPK
!<����7���chrome/browser/content/browser/syncedtabs/sidebar.xhtmlPK
!<�]�1q1q-��=�chrome/browser/content/browser/tab-content.jsPK
!<�It�j�j,�����chrome/browser/content/browser/tabbrowser.jsPK
!<y�||i|i-����chrome/browser/content/browser/tabbrowser.xmlPK
!<hIu�K�K6��z[�chrome/browser/content/browser/translation-infobar.xmlPK
!<�Ne����1�����chrome/browser/content/browser/urlbarBindings.xmlPK
!<n�p1�1�0��iK�chrome/browser/content/browser/utilityOverlay.jsPK
!<'��g
g
,���Иchrome/browser/content/browser/web-panels.jsPK
!<��H��f�f-���ޘchrome/browser/content/browser/web-panels.xulPK
!<��zz/���E�chrome/browser/content/browser/webext-panels.jsPK
!<��ii0���U�chrome/browser/content/browser/webext-panels.xulPK
!<rg˜��1��羙chrome/browser/content/browser/webrtcIndicator.jsPK
!<4����2��ڙchrome/browser/content/browser/webrtcIndicator.xulPK
!<V
�
�����ݙchrome/browser/content/cart.svgPK
!<��Z99 ��)�chrome/browser/content/chill.svgPK
!<I�}�ll!����chrome/browser/content/circle.svgPK
!<$����!��K�chrome/browser/content/dollar.svgPK
!<���!%
%
&��e�chrome/browser/content/fingerprint.svgPK
!<*8�������chrome/browser/content/food.svgPK
!<{�L�� ����chrome/browser/content/fruit.svgPK
!<d�4``��b�chrome/browser/content/gift.svgPK
!<.b&I������chrome/browser/content/pet.svgPK
!<�h������
�chrome/browser/content/tree.svgPK
!<�E��#���
�chrome/browser/content/vacation.svgPK
!<�"�"gg5����chrome/browser/skin/classic/browser/aboutNetError.cssPK
!<�x�())G���%�chrome/browser/skin/classic/browser/aboutSessionRestore-window-icon.pngPK
!<����;��:'�chrome/browser/skin/classic/browser/aboutSessionRestore.cssPK
!<e��gg7���0�chrome/browser/skin/classic/browser/aboutTabCrashed.cssPK
!<�4Om$$8��B5�chrome/browser/skin/classic/browser/aboutWelcomeBack.cssPK
!<����<���;�chrome/browser/skin/classic/browser/accessibility-active.svgPK
!<ց��5���>�chrome/browser/skin/classic/browser/accessibility.svgPK
!<Hv����D��(B�chrome/browser/skin/classic/browser/addons/addon-install-blocked.svgPK
!<ҍ�XuuD��tJ�chrome/browser/skin/classic/browser/addons/addon-install-confirm.svgPK
!<"�[ggH��KP�chrome/browser/skin/classic/browser/addons/addon-install-downloading.svgPK
!<HQ2��B��Y�chrome/browser/skin/classic/browser/addons/addon-install-error.svgPK
!<���F��b�chrome/browser/skin/classic/browser/addons/addon-install-installed.svgPK
!<,�X

D���j�chrome/browser/skin/classic/browser/addons/addon-install-restart.svgPK
!<�1�x�	�	D��tx�chrome/browser/skin/classic/browser/addons/addon-install-warning.svgPK
!<�ee/��͂�chrome/browser/skin/classic/browser/back-12.svgPK
!<�����3����chrome/browser/skin/classic/browser/blockedSite.cssPK
!<:f�[����:��}��chrome/browser/skin/classic/browser/bookmark-animation.svgPK
!<�V~l__=���C�chrome/browser/skin/classic/browser/bookmark-star-on-tray.svgPK
!<a��`||0��FG�chrome/browser/skin/classic/browser/bookmark.svgPK
!<�Fn��9��J�chrome/browser/skin/classic/browser/bookmarks-toolbar.svgPK
!<[�FJ��2��OM�chrome/browser/skin/classic/browser/cert-error.svgPK
!<{���II9��|S�chrome/browser/skin/classic/browser/characterEncoding.svgPK
!<�V���7��Y�chrome/browser/skin/classic/browser/check-animation.svgPK
!<tK�-�"x�chrome/browser/skin/classic/browser/check.svgPK
!<��
�'I'I9��sz�chrome/browser/skin/classic/browser/chevron-animation.svgPK
!< B|R@@4���Ûchrome/browser/skin/classic/browser/compacttheme.cssPK
!<�� ��F���қchrome/browser/skin/classic/browser/connection-mixed-active-loaded.svgPK
!<�A��

G���՛chrome/browser/skin/classic/browser/connection-mixed-passive-loaded.svgPK
!<���_��9��ۛchrome/browser/skin/classic/browser/connection-secure.svgPK
!<�����E��ޛchrome/browser/skin/classic/browser/controlcenter/conn-not-secure.svgPK
!<3��22@���Ԝchrome/browser/skin/classic/browser/controlcenter/connection.svgPK
!<V=�?��*לchrome/browser/skin/classic/browser/controlcenter/extension.svgPK
!<�a���B���ۜchrome/browser/skin/classic/browser/controlcenter/mcb-disabled.svgPK
!<�;�|kkA���ޜchrome/browser/skin/classic/browser/controlcenter/permissions.svgPK
!<�a�5��I����chrome/browser/skin/classic/browser/controlcenter/tracking-protection.svgPK
!<f%%B���chrome/browser/skin/classic/browser/controlcenter/warning-gray.svgPK
!<V�D,%%D����chrome/browser/skin/classic/browser/controlcenter/warning-yellow.svgPK
!<˴�yppF���chrome/browser/skin/classic/browser/customizableui/density-compact.svgPK
!<#�5b��E���chrome/browser/skin/classic/browser/customizableui/density-normal.svgPK
!<ݬ(��D��D��chrome/browser/skin/classic/browser/customizableui/density-touch.svgPK
!<>/B6B6K��+�chrome/browser/skin/classic/browser/customizableui/empty-overflow-panel.pngPK
!<�I�*�*�N���6�chrome/browser/skin/classic/browser/customizableui/[email protected]
!<y�ff=��l��chrome/browser/skin/classic/browser/customizableui/whimsy.pngPK
!<\��1��-ԝchrome/browser/skin/classic/browser/customize.svgPK
!<��@�XX1�םchrome/browser/skin/classic/browser/developer.svgPK
!<}�6�&ڝchrome/browser/skin/classic/browser/device-desktop.svgPK
!<��&O5큅ܝchrome/browser/skin/classic/browser/device-mobile.svgPK
!<�r�cCC5���ޝchrome/browser/skin/classic/browser/device-tablet.svgPK
!<��R33I����chrome/browser/skin/classic/browser/downloads/allDownloadsViewOverlay.cssPK
!<����J��$��chrome/browser/skin/classic/browser/downloads/contentAreaDownloadsView.cssPK
!<9A�@@B��x��chrome/browser/skin/classic/browser/downloads/download-blocked.svgPK
!<F�@����chrome/browser/skin/classic/browser/downloads/download-icons.svgPK
!<�ڟh��B����chrome/browser/skin/classic/browser/downloads/download-summary.svgPK
!<c�J$J$N����chrome/browser/skin/classic/browser/downloads/notification-start-animation.svgPK
!<����yy0���*�chrome/browser/skin/classic/browser/drm-icon.svgPK
!<Mml��2��G0�chrome/browser/skin/classic/browser/[email protected]
!<T��8��1�`?�chrome/browser/skin/classic/browser/edit-copy.svgPK
!<|��0�?B�chrome/browser/skin/classic/browser/edit-cut.svgPK
!<a�(���2�wE�chrome/browser/skin/classic/browser/edit-paste.svgPK
!<J�Y���3���H�chrome/browser/skin/classic/browser/error-pages.cssPK
!<U���))9��ZQ�chrome/browser/skin/classic/browser/favicon-search-16.svgPK
!<��r�DD,���T�chrome/browser/skin/classic/browser/feed.svgPK
!<���KK6��hW�chrome/browser/skin/classic/browser/feeds/feedIcon.pngPK
!<6X��8��^�chrome/browser/skin/classic/browser/feeds/feedIcon16.pngPK
!<:���TT7��a�chrome/browser/skin/classic/browser/feeds/subscribe.cssPK
!<q���"".���m�chrome/browser/skin/classic/browser/folder.svgPK
!<(��k��.�"p�chrome/browser/skin/classic/browser/forget.svgPK
!<V%�%��;���r�chrome/browser/skin/classic/browser/fullscreen/insecure.svgPK
!<F9��dd9���y�chrome/browser/skin/classic/browser/fullscreen/secure.svgPK
!<,�0�UU7큷~�chrome/browser/skin/classic/browser/fullscreen-exit.svgPK
!<ô"�YY2��a��chrome/browser/skin/classic/browser/fullscreen.svgPK
!<�W

:��
��chrome/browser/skin/classic/browser/fxa/default-avatar.svgPK
!<���v v C��o��chrome/browser/skin/classic/browser/fxa/sync-illustration-issue.svgPK
!<Y�|##=�F��chrome/browser/skin/classic/browser/fxa/sync-illustration.svgPK
!<LӍ�/��ɞchrome/browser/skin/classic/browser/history.svgPK
!<�T#��6��̞chrome/browser/skin/classic/browser/icon-search-64.svgPK
!<�m���<��
Оchrome/browser/skin/classic/browser/identity-icon-notice.svgPK
!<;v�|�T�TK��Ӟchrome/browser/skin/classic/browser/illustrations/error-session-restore.svgPK
!<�p�T��,��(�chrome/browser/skin/classic/browser/info.svgPK
!<]zxururB��6*�chrome/browser/skin/classic/browser/library-bookmark-animation.svgPK
!<+Z&��,����chrome/browser/skin/classic/browser/link.svgPK
!<5�����,�נ�chrome/browser/skin/classic/browser/mail.svgPK
!<)����3�����chrome/browser/skin/classic/browser/menu-badged.svgPK
!<+jYY/����chrome/browser/skin/classic/browser/monitor.pngPK
!<���$$5�����chrome/browser/skin/classic/browser/monitor_16-10.pngPK
!<k+�AA/�џchrome/browser/skin/classic/browser/new-tab.svgPK
!<�.���2크ӟchrome/browser/skin/classic/browser/new-window.svgPK
!<��]ttI���֟chrome/browser/skin/classic/browser/notification-icons/camera-blocked.svgPK
!<��$BBA���ٟchrome/browser/skin/classic/browser/notification-icons/camera.svgPK
!<r�*�I��@ܟchrome/browser/skin/classic/browser/notification-icons/canvas-blocked.svgPK
!<axa���A��&�chrome/browser/skin/classic/browser/notification-icons/canvas.svgPK
!<�����G��4�chrome/browser/skin/classic/browser/notification-icons/default-info.svgPK
!<�/RbbW����chrome/browser/skin/classic/browser/notification-icons/desktop-notification-blocked.svgPK
!<�ﱷ&&O��[�chrome/browser/skin/classic/browser/notification-icons/desktop-notification.svgPK
!<��ڸ�N����chrome/browser/skin/classic/browser/notification-icons/focus-tab-by-prompt.svgPK
!<�JȋF���chrome/browser/skin/classic/browser/notification-icons/geo-blocked.svgPK
!<N��+,,G����chrome/browser/skin/classic/browser/notification-icons/geo-detailed.svgPK
!<w���EE>��"��chrome/browser/skin/classic/browser/notification-icons/geo.svgPK
!<g��KbbD����chrome/browser/skin/classic/browser/notification-icons/indexedDB.svgPK
!<�����I�����chrome/browser/skin/classic/browser/notification-icons/login-detailed.svgPK
!<
Ϊ0oo@����chrome/browser/skin/classic/browser/notification-icons/login.svgPK
!<�����M����chrome/browser/skin/classic/browser/notification-icons/microphone-blocked.svgPK
!<��$YffN����chrome/browser/skin/classic/browser/notification-icons/microphone-detailed.svgPK
!<蔢�44E����chrome/browser/skin/classic/browser/notification-icons/microphone.svgPK
!<%9�X?��M�chrome/browser/skin/classic/browser/notification-icons/midi.svgPK
!<�ԇU���
�chrome/browser/skin/classic/browser/notification-icons/persistent-storage-blocked.svgPK
!<F'6�CCM��'�chrome/browser/skin/classic/browser/notification-icons/persistent-storage.svgPK
!<X	7���G����chrome/browser/skin/classic/browser/notification-icons/plugin-badge.svgPK
!<�Mj+H����chrome/browser/skin/classic/browser/notification-icons/popup-subitem.svgPK
!<V� M��@��P�chrome/browser/skin/classic/browser/notification-icons/popup.svgPK
!<�?Hl��I��Z�chrome/browser/skin/classic/browser/notification-icons/screen-blocked.svgPK
!<ȳ*�A��Q�chrome/browser/skin/classic/browser/notification-icons/screen.svgPK
!<'|����A��� �chrome/browser/skin/classic/browser/notification-icons/update.svgPK
!<�����C��#�chrome/browser/skin/classic/browser/notification-icons/webauthn.svgPK
!<2:0`��,�-/�chrome/browser/skin/classic/browser/open.svgPK
!<h�3S~~6��A2�chrome/browser/skin/classic/browser/page-livemarks.pngPK
!<T0x�tt0��5�chrome/browser/skin/classic/browser/pageInfo.cssPK
!<f�R� � 0���C�chrome/browser/skin/classic/browser/pageInfo.pngPK
!<V5��=��e�chrome/browser/skin/classic/browser/panel-icon-arrow-left.svgPK
!<vΥ#��>���f�chrome/browser/skin/classic/browser/panel-icon-arrow-right.svgPK
!<��B���9���h�chrome/browser/skin/classic/browser/panel-icon-cancel.svgPK
!<�Q�"9���j�chrome/browser/skin/classic/browser/panel-icon-folder.svgPK
!<���448��Nn�chrome/browser/skin/classic/browser/panel-icon-retry.svgPK
!<��wnXX:���p�chrome/browser/skin/classic/browser/panic-panel/header.pngPK
!<8w���=���x�chrome/browser/skin/classic/browser/panic-panel/[email protected]
!<����9��ʉ�chrome/browser/skin/classic/browser/panic-panel/icons.pngPK
!<BNƝTT<�����chrome/browser/skin/classic/browser/panic-panel/[email protected]
!<*�,ZZ;��m��chrome/browser/skin/classic/browser/places/allBookmarks.pngPK
!<Zj����C�� ��chrome/browser/skin/classic/browser/places/bookmarks-menu-arrow.pngPK
!<v5��U
U
L��7��chrome/browser/skin/classic/browser/places/bookmarks-notification-finish.pngPK
!<���z!z!O�����chrome/browser/skin/classic/browser/places/[email protected]
!<�#J7<���Šchrome/browser/skin/classic/browser/places/bookmarksMenu.pngPK
!<\Ĉy��<��OǠchrome/browser/skin/classic/browser/places/bookmarksMenu.svgPK
!<t[l�YY?��|ʠchrome/browser/skin/classic/browser/places/bookmarksToolbar.pngPK
!<b��k��?��2̠chrome/browser/skin/classic/browser/places/bookmarksToolbar.svgPK
!<m����8��Рchrome/browser/skin/classic/browser/places/downloads.pngPK
!<�sW���B��JҠchrome/browser/skin/classic/browser/places/editBookmarkOverlay.cssPK
!<�
J:DD:��_ؠchrome/browser/skin/classic/browser/places/folder-live.svgPK
!<E݃#pp;���ڠchrome/browser/skin/classic/browser/places/folder-smart.svgPK
!<�U�R5���ޠchrome/browser/skin/classic/browser/places/folder.svgPK
!<�!�w��6���chrome/browser/skin/classic/browser/places/history.svgPK
!<�Ѐ�RR<���chrome/browser/skin/classic/browser/places/livemark-item.pngPK
!<���n 	 	8����chrome/browser/skin/classic/browser/places/organizer.cssPK
!<���jWW8��*�chrome/browser/skin/classic/browser/places/organizer.xmlPK
!<"G���5����chrome/browser/skin/classic/browser/places/places.cssPK
!<����2���
�chrome/browser/skin/classic/browser/places/tag.pngPK
!<D�}
[[?���
�chrome/browser/skin/classic/browser/places/unfiledBookmarks.svgPK
!<#��}��@��|�chrome/browser/skin/classic/browser/places/unsortedBookmarks.pngPK
!<M���ww=����chrome/browser/skin/classic/browser/preferences/alwaysAsk.pngPK
!<|�H��@����chrome/browser/skin/classic/browser/preferences/applications.cssPK
!<^�k~��>����chrome/browser/skin/classic/browser/preferences/containers.cssPK
!<d�M��I���-�chrome/browser/skin/classic/browser/preferences/in-content/containers.cssPK
!<�P`z�z�P��Y=�chrome/browser/skin/classic/browser/preferences/in-content/critters-postcard.jpgPK
!<��ÿE��A�chrome/browser/skin/classic/browser/preferences/in-content/dialog.cssPK
!<i�|���G큶�chrome/browser/skin/classic/browser/preferences/in-content/face-sad.svgPK
!<D�1I���chrome/browser/skin/classic/browser/preferences/in-content/face-smile.svgPK
!<)M���I�7��chrome/browser/skin/classic/browser/preferences/in-content/fxa-avatar.svgPK
!<�+�!��F��I��chrome/browser/skin/classic/browser/preferences/in-content/general.svgPK
!<)i�PFFK�@�chrome/browser/skin/classic/browser/preferences/in-content/logo-android.svgPK
!<7�sS��G���chrome/browser/skin/classic/browser/preferences/in-content/logo-ios.svgPK
!<B�ffL��
�chrome/browser/skin/classic/browser/preferences/in-content/no-search-bar.svgPK
!<��ߋM+M+P����chrome/browser/skin/classic/browser/preferences/in-content/no-search-results.svgPK
!<ծ���B�BJ���A�chrome/browser/skin/classic/browser/preferences/in-content/preferences.cssPK
!<];�eO��؄�chrome/browser/skin/classic/browser/preferences/in-content/privacy-security.svgPK
!<��oMMF��F��chrome/browser/skin/classic/browser/preferences/in-content/privacy.cssPK
!<xU��EEU�����chrome/browser/skin/classic/browser/preferences/in-content/search-arrow-indicator.svgPK
!<����
�
I�����chrome/browser/skin/classic/browser/preferences/in-content/search-bar.svgPK
!<H0z33E�����chrome/browser/skin/classic/browser/preferences/in-content/search.cssPK
!<CUeeE��0��chrome/browser/skin/classic/browser/preferences/in-content/search.svgPK
!<���9^^O�����chrome/browser/skin/classic/browser/preferences/in-content/siteDataSettings.cssPK
!<E�睽m�mK��è�chrome/browser/skin/classic/browser/preferences/in-content/sync-devices.svgPK
!<��"�''C����chrome/browser/skin/classic/browser/preferences/in-content/sync.svgPK
!<}�6?��q�chrome/browser/skin/classic/browser/preferences/preferences.cssPK
!<	~~-��!�chrome/browser/skin/classic/browser/print.svgPK
!< �LL8���$�chrome/browser/skin/classic/browser/private-browsing.svgPK
!<!T�..7�R(�chrome/browser/skin/classic/browser/privateBrowsing.svgPK
!<�@���L���+�chrome/browser/skin/classic/browser/privatebrowsing/aboutPrivateBrowsing.cssPK
!<���?��9�chrome/browser/skin/classic/browser/privatebrowsing/favicon.svgPK
!<�?����H���?�chrome/browser/skin/classic/browser/privatebrowsing/private-browsing.svgPK
!<�]ˮ��O���E�chrome/browser/skin/classic/browser/privatebrowsing/tracking-protection-off.svgPK
!<�DT��K��J�chrome/browser/skin/classic/browser/privatebrowsing/tracking-protection.svgPK
!<�r�l)),�N�chrome/browser/skin/classic/browser/quit.svgPK
!<`�Iii7���P�chrome/browser/skin/classic/browser/restore-session.svgPK
!<���%k	k	6��NS�chrome/browser/skin/classic/browser/sanitizeDialog.cssPK
!<t���,�
]�chrome/browser/skin/classic/browser/save.svgPK
!<�����A���_�chrome/browser/skin/classic/browser/search-engine-placeholder.pngPK
!<��+��D��Va�chrome/browser/skin/classic/browser/[email protected]
!<!�$B���c�chrome/browser/skin/classic/browser/search-indicator-badge-add.svgPK
!<8���  3���e�chrome/browser/skin/classic/browser/searchReset.cssPK
!<��06��ph�chrome/browser/skin/classic/browser/send-to-device.svgPK
!<���D99<���k�chrome/browser/skin/classic/browser/setDesktopBackground.cssPK
!<��e��6��cn�chrome/browser/skin/classic/browser/sidebars-right.svgPK
!<p���6��rq�chrome/browser/skin/classic/browser/slowStartup-16.pngPK
!<ֽ�;�;6���s�chrome/browser/skin/classic/browser/stop-to-reload.svgPK
!<���8��f��chrome/browser/skin/classic/browser/sync-desktopIcon.svgPK
!<Hm|EE7��ӱ�chrome/browser/skin/classic/browser/sync-mobileIcon.svgPK
!<Q���,�m��chrome/browser/skin/classic/browser/sync.svgPK
!<�jRR:�����chrome/browser/skin/classic/browser/syncedtabs/sidebar.cssPK
!<�]���3��?Ѥchrome/browser/skin/classic/browser/tab-crashed.svgPK
!<�����+�sפchrome/browser/skin/classic/browser/tab.svgPK
!<��``:��٤chrome/browser/skin/classic/browser/tabbrowser/crashed.svgPK
!<�8>�J��7ݤchrome/browser/skin/classic/browser/tabbrowser/indicator-tab-attention.svgPK
!<ȩS�'�'?���ޤchrome/browser/skin/classic/browser/tabbrowser/pendingpaint.pngPK
!<"�e�D����chrome/browser/skin/classic/browser/tabbrowser/tab-audio-blocked.svgPK
!<"ob��B��	�chrome/browser/skin/classic/browser/tabbrowser/tab-audio-muted.svgPK
!<��b���D����chrome/browser/skin/classic/browser/tabbrowser/tab-audio-playing.svgPK
!<��rzkkB��8�chrome/browser/skin/classic/browser/tabbrowser/tab-audio-small.svgPK
!<�`���A��!�chrome/browser/skin/classic/browser/tabbrowser/tab-connecting.pngPK
!<�#VVD��S7�chrome/browser/skin/classic/browser/tabbrowser/[email protected]
!<�I����G��T�chrome/browser/skin/classic/browser/tabbrowser/tab-loading-inverted.pngPK
!<�l�00J��Nj�chrome/browser/skin/classic/browser/tabbrowser/[email protected]
!<� ӱ��>��憥chrome/browser/skin/classic/browser/tabbrowser/tab-loading.pngPK
!<�k(@66A�� ��chrome/browser/skin/classic/browser/tabbrowser/[email protected]
!<�����>큵��chrome/browser/skin/classic/browser/tracking-protection-16.svgPK
!<��	�SS6���chrome/browser/skin/classic/browser/translating-16.pngPK
!<��d��t�t9��X�chrome/browser/skin/classic/browser/[email protected]
!<<��raa6��p��chrome/browser/skin/classic/browser/translation-16.pngPK
!<O��?��9��%��chrome/browser/skin/classic/browser/[email protected]
!<�^����;��c��chrome/browser/skin/classic/browser/update-badge-failed.svgPK
!<���<AA4�����chrome/browser/skin/classic/browser/update-badge.svgPK
!<��YS��5��!��chrome/browser/skin/classic/browser/warning-white.svgPK
!<蹔���/��;��chrome/browser/skin/classic/browser/warning.svgPK
!<$)��.��]��chrome/browser/skin/classic/browser/webIDE.svgPK
!<Os���8�����chrome/browser/skin/classic/browser/webRTC-indicator.cssPK
!<Y�}��4�����chrome/browser/skin/classic/browser/welcome-back.svgPK
!<~���nn,�����chrome/browser/skin/classic/browser/wifi.svgPK
!<,�E�.��D��chrome/browser/skin/classic/browser/window.svgPK
!<�޶��/�����chrome/browser/skin/classic/browser/zoom-in.svgPK
!<�� ��0���¦chrome/browser/skin/classic/browser/zoom-out.svgPK
!<�U��9��pĦchrome/browser/skin/classic/communicator/communicator.cssPK
!<Y�Ocw1w1���Ŧchrome/pdfjs/content/PdfJs.jsmPK
!<�s��XX%��o��chrome/pdfjs/content/PdfJsNetwork.jsmPK
!<�r�		'��
�chrome/pdfjs/content/PdfJsTelemetry.jsmPK
!<Œ�H�H�+��n�chrome/pdfjs/content/PdfStreamConverter.jsmPK
!<�kޠ�*�����chrome/pdfjs/content/PdfjsContentUtils.jsmPK
!<��!��粧chrome/pdfjs/content/build/pdf.jsPK
!<�W�KK(��+��chrome/pdfjs/content/build/pdf.worker.jsPK
!<{��[HH3�����chrome/pdfjs/content/pdfjschildbootstrap-enabled.jsPK
!<+�1���+��#�chrome/pdfjs/content/pdfjschildbootstrap.jsPK
!<%���d	d	-����chrome/pdfjs/content/web/cmaps/78-EUC-H.bcmapPK
!<8�uH��-����chrome/pdfjs/content/web/cmaps/78-EUC-V.bcmapPK
!<�U+K	K	)����chrome/pdfjs/content/web/cmaps/78-H.bcmapPK
!<�n�^	^	.��.�chrome/pdfjs/content/web/cmaps/78-RKSJ-H.bcmapPK
!<�@i��.���$�chrome/pdfjs/content/web/cmaps/78-RKSJ-V.bcmapPK
!<a�bة�)���%�chrome/pdfjs/content/web/cmaps/78-V.bcmapPK
!<jM��[
[
0���&�chrome/pdfjs/content/web/cmaps/78ms-RKSJ-H.bcmapPK
!<!��8""0��j1�chrome/pdfjs/content/web/cmaps/78ms-RKSJ-V.bcmapPK
!<�Ll��0���2�chrome/pdfjs/content/web/cmaps/83pv-RKSJ-H.bcmapPK
!<#a���0���6�chrome/pdfjs/content/web/cmaps/90ms-RKSJ-H.bcmapPK
!<��Q�""0���9�chrome/pdfjs/content/web/cmaps/90ms-RKSJ-V.bcmapPK
!<pNd��1��@;�chrome/pdfjs/content/web/cmaps/90msp-RKSJ-H.bcmapPK
!<�^G�##1��Z>�chrome/pdfjs/content/web/cmaps/90msp-RKSJ-V.bcmapPK
!<L�gV��0���?�chrome/pdfjs/content/web/cmaps/90pv-RKSJ-H.bcmapPK
!<��'�0���C�chrome/pdfjs/content/web/cmaps/90pv-RKSJ-V.bcmapPK
!<��;s	s	*��BE�chrome/pdfjs/content/web/cmaps/Add-H.bcmapPK
!<5�Pm	m	/���N�chrome/pdfjs/content/web/cmaps/Add-RKSJ-H.bcmapPK
!<��)/���X�chrome/pdfjs/content/web/cmaps/Add-RKSJ-V.bcmapPK
!<���v*��#Z�chrome/pdfjs/content/web/cmaps/Add-V.bcmapPK
!<�H�==1���[�chrome/pdfjs/content/web/cmaps/Adobe-CNS1-0.bcmapPK
!<h`�ss1��]�chrome/pdfjs/content/web/cmaps/Adobe-CNS1-1.bcmapPK
!<1HEYxx1���^�chrome/pdfjs/content/web/cmaps/Adobe-CNS1-2.bcmapPK
!<����1���`�chrome/pdfjs/content/web/cmaps/Adobe-CNS1-3.bcmapPK
!<�a���1��zb�chrome/pdfjs/content/web/cmaps/Adobe-CNS1-4.bcmapPK
!<QFC��1��^d�chrome/pdfjs/content/web/cmaps/Adobe-CNS1-5.bcmapPK
!<K��–�1��Cf�chrome/pdfjs/content/web/cmaps/Adobe-CNS1-6.bcmapPK
!<�Nd���4��(h�chrome/pdfjs/content/web/cmaps/Adobe-CNS1-UCS2.bcmapPK
!<�#��0��c	�chrome/pdfjs/content/web/cmaps/Adobe-GB1-0.bcmapPK
!<��{���0���
�chrome/pdfjs/content/web/cmaps/Adobe-GB1-1.bcmapPK
!<�?���0����chrome/pdfjs/content/web/cmaps/Adobe-GB1-2.bcmapPK
!<[�Q]��0���
�chrome/pdfjs/content/web/cmaps/Adobe-GB1-3.bcmapPK
!<ɮ5YY0���chrome/pdfjs/content/web/cmaps/Adobe-GB1-4.bcmapPK
!<�GOqq0����chrome/pdfjs/content/web/cmaps/Adobe-GB1-5.bcmapPK
!<r������3��{�chrome/pdfjs/content/web/cmaps/Adobe-GB1-UCS2.bcmapPK
!<�
&��3�����chrome/pdfjs/content/web/cmaps/Adobe-Japan1-0.bcmapPK
!<��V��3�����chrome/pdfjs/content/web/cmaps/Adobe-Japan1-1.bcmapPK
!<��}��3����chrome/pdfjs/content/web/cmaps/Adobe-Japan1-2.bcmapPK
!<�!�}��3��!��chrome/pdfjs/content/web/cmaps/Adobe-Japan1-3.bcmapPK
!<�h/�QQ3��d��chrome/pdfjs/content/web/cmaps/Adobe-Japan1-4.bcmapPK
!<DZ���3����chrome/pdfjs/content/web/cmaps/Adobe-Japan1-5.bcmapPK
!<�Ɣ���3����chrome/pdfjs/content/web/cmaps/Adobe-Japan1-6.bcmapPK
!<qW�����6��;��chrome/pdfjs/content/web/cmaps/Adobe-Japan1-UCS2.bcmapPK
!<�9��3���E�chrome/pdfjs/content/web/cmaps/Adobe-Korea1-0.bcmapPK
!<��=��3���F�chrome/pdfjs/content/web/cmaps/Adobe-Korea1-1.bcmapPK
!<��ȯ�3���H�chrome/pdfjs/content/web/cmaps/Adobe-Korea1-2.bcmapPK
!<:@.�Z�Z6��sJ�chrome/pdfjs/content/web/cmaps/Adobe-Korea1-UCS2.bcmapPK
!<Lv�M>>)��ĥ�chrome/pdfjs/content/web/cmaps/B5-H.bcmapPK
!<J#nŎ�)��I��chrome/pdfjs/content/web/cmaps/B5-V.bcmapPK
!<%��KK+����chrome/pdfjs/content/web/cmaps/B5pc-H.bcmapPK
!<9k,���+�����chrome/pdfjs/content/web/cmaps/B5pc-V.bcmapPK
!<��L��.�����chrome/pdfjs/content/web/cmaps/CNS-EUC-H.bcmapPK
!<�D��.��˷�chrome/pdfjs/content/web/cmaps/CNS-EUC-V.bcmapPK
!<����+�����chrome/pdfjs/content/web/cmaps/CNS1-H.bcmapPK
!<�[P��+�����chrome/pdfjs/content/web/cmaps/CNS1-V.bcmapPK
!<(TY���+��z��chrome/pdfjs/content/web/cmaps/CNS2-H.bcmapPK
!<����]]+�����chrome/pdfjs/content/web/cmaps/CNS2-V.bcmapPK
!<2W�jJJ.��a��chrome/pdfjs/content/web/cmaps/ETHK-B5-H.bcmapPK
!<h(-��.�����chrome/pdfjs/content/web/cmaps/ETHK-B5-V.bcmapPK
!<ӡ0�ee.�����chrome/pdfjs/content/web/cmaps/ETen-B5-H.bcmapPK
!<7S-E��.�����chrome/pdfjs/content/web/cmaps/ETen-B5-V.bcmapPK
!<�bt�ee0��|��chrome/pdfjs/content/web/cmaps/ETenms-B5-H.bcmapPK
!<�T�ݬ�0��/��chrome/pdfjs/content/web/cmaps/ETenms-B5-V.bcmapPK
!<�wBB*��)��chrome/pdfjs/content/web/cmaps/EUC-H.bcmapPK
!<��ͪ�*�����chrome/pdfjs/content/web/cmaps/EUC-V.bcmapPK
!<b_��	�	*�����chrome/pdfjs/content/web/cmaps/Ext-H.bcmapPK
!<����	�	/�����chrome/pdfjs/content/web/cmaps/Ext-RKSJ-H.bcmapPK
!<"D����/����chrome/pdfjs/content/web/cmaps/Ext-RKSJ-V.bcmapPK
!<U/����*��7��chrome/pdfjs/content/web/cmaps/Ext-V.bcmapPK
!<ܥ�8%%-��V��chrome/pdfjs/content/web/cmaps/GB-EUC-H.bcmapPK
!<fk1��-����chrome/pdfjs/content/web/cmaps/GB-EUC-V.bcmapPK
!<	��)����chrome/pdfjs/content/web/cmaps/GB-H.bcmapPK
!<�7n���)���chrome/pdfjs/content/web/cmaps/GB-V.bcmapPK
!<M��d9d9.���chrome/pdfjs/content/web/cmaps/GBK-EUC-H.bcmapPK
!<��޴�.���:�chrome/pdfjs/content/web/cmaps/GBK-EUC-V.bcmapPK
!<�p��L�L,���;�chrome/pdfjs/content/web/cmaps/GBK2K-H.bcmapPK
!<@%���,��و�chrome/pdfjs/content/web/cmaps/GBK2K-V.bcmapPK
!<�o�^9^9/�����chrome/pdfjs/content/web/cmaps/GBKp-EUC-H.bcmapPK
!<�ԏJ��/�����chrome/pdfjs/content/web/cmaps/GBKp-EUC-V.bcmapPK
!<o㛱zz.�����chrome/pdfjs/content/web/cmaps/GBT-EUC-H.bcmapPK
!<�Q���.��q��chrome/pdfjs/content/web/cmaps/GBT-EUC-V.bcmapPK
!<�d�ee*��q��chrome/pdfjs/content/web/cmaps/GBT-H.bcmapPK
!<��S���*����chrome/pdfjs/content/web/cmaps/GBT-V.bcmapPK
!<i.���0���chrome/pdfjs/content/web/cmaps/GBTpc-EUC-H.bcmapPK
!<4X�O��0����chrome/pdfjs/content/web/cmaps/GBTpc-EUC-V.bcmapPK
!<�g��--/����chrome/pdfjs/content/web/cmaps/GBpc-EUC-H.bcmapPK
!<s�cf��/��d �chrome/pdfjs/content/web/cmaps/GBpc-EUC-V.bcmapPK
!<��))&��f!�chrome/pdfjs/content/web/cmaps/H.bcmapPK
!<R�Ǿ^
^
/���#�chrome/pdfjs/content/web/cmaps/HKdla-B5-H.bcmapPK
!<}���/��~.�chrome/pdfjs/content/web/cmaps/HKdla-B5-V.bcmapPK
!<dXLn	n	/��_/�chrome/pdfjs/content/web/cmaps/HKdlb-B5-H.bcmapPK
!<�委�/��9�chrome/pdfjs/content/web/cmaps/HKdlb-B5-V.bcmapPK
!<O�z���0���9�chrome/pdfjs/content/web/cmaps/HKgccs-B5-H.bcmapPK
!<]??ϕ�0��=C�chrome/pdfjs/content/web/cmaps/HKgccs-B5-V.bcmapPK
!<��#y��0�� D�chrome/pdfjs/content/web/cmaps/HKm314-B5-H.bcmapPK
!<N����0��ZK�chrome/pdfjs/content/web/cmaps/HKm314-B5-V.bcmapPK
!<�$+v{{0��=L�chrome/pdfjs/content/web/cmaps/HKm471-B5-H.bcmapPK
!<S�#��0��U�chrome/pdfjs/content/web/cmaps/HKm471-B5-V.bcmapPK
!<f�dmUU/���U�chrome/pdfjs/content/web/cmaps/HKscs-B5-H.bcmapPK
!<����/���g�chrome/pdfjs/content/web/cmaps/HKscs-B5-V.bcmapPK
!<iF{τ�,��wh�chrome/pdfjs/content/web/cmaps/Hankaku.bcmapPK
!<J�F||-��Ei�chrome/pdfjs/content/web/cmaps/Hiragana.bcmapPK
!<o��88.��j�chrome/pdfjs/content/web/cmaps/KSC-EUC-H.bcmapPK
!<��w��.���q�chrome/pdfjs/content/web/cmaps/KSC-EUC-V.bcmapPK
!<k,�''*���r�chrome/pdfjs/content/web/cmaps/KSC-H.bcmapPK
!<�KR�A�A0���y�chrome/pdfjs/content/web/cmaps/KSC-Johab-H.bcmapPK
!<VK�Ǧ�0��Ի�chrome/pdfjs/content/web/cmaps/KSC-Johab-V.bcmapPK
!<ul�C��*��ȼ�chrome/pdfjs/content/web/cmaps/KSC-V.bcmapPK
!<�Q�
�
0�����chrome/pdfjs/content/web/cmaps/KSCms-UHC-H.bcmapPK
!<�.d�
�
3�����chrome/pdfjs/content/web/cmaps/KSCms-UHC-HW-H.bcmapPK
!<�*|���3����chrome/pdfjs/content/web/cmaps/KSCms-UHC-HW-V.bcmapPK
!<;"oۦ�0����chrome/pdfjs/content/web/cmaps/KSCms-UHC-V.bcmapPK
!<m�7���0����chrome/pdfjs/content/web/cmaps/KSCpc-EUC-H.bcmapPK
!<{�s��0��;��chrome/pdfjs/content/web/cmaps/KSCpc-EUC-V.bcmapPK
!<����dd-��/��chrome/pdfjs/content/web/cmaps/Katakana.bcmapPK
!<ٚ�  &�����chrome/pdfjs/content/web/cmaps/LICENSEPK
!<�^��
�
*��B��chrome/pdfjs/content/web/cmaps/NWP-H.bcmapPK
!<j�B���*��W��chrome/pdfjs/content/web/cmaps/NWP-V.bcmapPK
!<��CA+�����chrome/pdfjs/content/web/cmaps/RKSJ-H.bcmapPK
!<L3���+�����chrome/pdfjs/content/web/cmaps/RKSJ-V.bcmapPK
!<��,�``*����chrome/pdfjs/content/web/cmaps/Roman.bcmapPK
!<i�阼��2�����chrome/pdfjs/content/web/cmaps/UniCNS-UCS2-H.bcmapPK
!<k5'���2��}��chrome/pdfjs/content/web/cmaps/UniCNS-UCS2-V.bcmapPK
!<L�K����3��i��chrome/pdfjs/content/web/cmaps/UniCNS-UTF16-H.bcmapPK
!<wr���3���{�chrome/pdfjs/content/web/cmaps/UniCNS-UTF16-V.bcmapPK
!<�o�����3���|�chrome/pdfjs/content/web/cmaps/UniCNS-UTF32-H.bcmapPK
!<�B����3���J�chrome/pdfjs/content/web/cmaps/UniCNS-UTF32-V.bcmapPK
!< �T�}�}�2���K�chrome/pdfjs/content/web/cmaps/UniCNS-UTF8-H.bcmapPK
!<��<ܝ�2��p�chrome/pdfjs/content/web/cmaps/UniCNS-UTF8-V.bcmapPK
!<gYYf�f�1��]�chrome/pdfjs/content/web/cmaps/UniGB-UCS2-H.bcmapPK
!<�aZ��1����chrome/pdfjs/content/web/cmaps/UniGB-UCS2-V.bcmapPK
!<Vd�6�6�2��"��chrome/pdfjs/content/web/cmaps/UniGB-UTF16-H.bcmapPK
!<1#�X��2���u�chrome/pdfjs/content/web/cmaps/UniGB-UTF16-V.bcmapPK
!<1T縪���2���v�chrome/pdfjs/content/web/cmaps/UniGB-UTF32-H.bcmapPK
!<�9�L��2���)�chrome/pdfjs/content/web/cmaps/UniGB-UTF32-V.bcmapPK
!<F`PO����1���*�chrome/pdfjs/content/web/cmaps/UniGB-UTF8-H.bcmapPK
!<�P�A��1�����chrome/pdfjs/content/web/cmaps/UniGB-UTF8-V.bcmapPK
!<��_c_c2�����chrome/pdfjs/content/web/cmaps/UniJIS-UCS2-H.bcmapPK
!< ��_ww5���F�chrome/pdfjs/content/web/cmaps/UniJIS-UCS2-HW-H.bcmapPK
!<bq%��5��kG�chrome/pdfjs/content/web/cmaps/UniJIS-UCS2-HW-V.bcmapPK
!<�RP͘�2��fJ�chrome/pdfjs/content/web/cmaps/UniJIS-UCS2-V.bcmapPK
!<�i����3��NM�chrome/pdfjs/content/web/cmaps/UniJIS-UTF16-H.bcmapPK
!<<؃�3�����chrome/pdfjs/content/web/cmaps/UniJIS-UTF16-V.bcmapPK
!<���`[�[�3�����chrome/pdfjs/content/web/cmaps/UniJIS-UTF32-H.bcmapPK
!<��?��3��2��chrome/pdfjs/content/web/cmaps/UniJIS-UTF32-V.bcmapPK
!<���ߢߢ2��(��chrome/pdfjs/content/web/cmaps/UniJIS-UTF8-H.bcmapPK
!<���}��2��W/�chrome/pdfjs/content/web/cmaps/UniJIS-UTF8-V.bcmapPK
!<��Sbn�n�7��M2�chrome/pdfjs/content/web/cmaps/UniJIS2004-UTF16-H.bcmapPK
!<��Er��7����chrome/pdfjs/content/web/cmaps/UniJIS2004-UTF16-V.bcmapPK
!<:�嶞��7�����chrome/pdfjs/content/web/cmaps/UniJIS2004-UTF32-H.bcmapPK
!<>z���7���n�chrome/pdfjs/content/web/cmaps/UniJIS2004-UTF32-V.bcmapPK
!<���3�3�6���q�chrome/pdfjs/content/web/cmaps/UniJIS2004-UTF8-H.bcmapPK
!<9�Ǿ��6��|�chrome/pdfjs/content/web/cmaps/UniJIS2004-UTF8-V.bcmapPK
!<&h:���8��z�chrome/pdfjs/content/web/cmaps/UniJISPro-UCS2-HW-V.bcmapPK
!<��O��5����chrome/pdfjs/content/web/cmaps/UniJISPro-UCS2-V.bcmapPK
!<-M���5����chrome/pdfjs/content/web/cmaps/UniJISPro-UTF8-V.bcmapPK
!<5�P2E�E�8���!�chrome/pdfjs/content/web/cmaps/UniJISX0213-UTF32-H.bcmapPK
!<UybK��8��Y��chrome/pdfjs/content/web/cmaps/UniJISX0213-UTF32-V.bcmapPK
!<��j�����<��[��chrome/pdfjs/content/web/cmaps/UniJISX02132004-UTF32-H.bcmapPK
!<Z�o��<��Ub�chrome/pdfjs/content/web/cmaps/UniJISX02132004-UTF32-V.bcmapPK
!<��B��d�d1��_e�chrome/pdfjs/content/web/cmaps/UniKS-UCS2-H.bcmapPK
!<�e�Ʋ�1��e��chrome/pdfjs/content/web/cmaps/UniKS-UCS2-V.bcmapPK
!<�W���f�f2��f��chrome/pdfjs/content/web/cmaps/UniKS-UTF16-H.bcmapPK
!<.}�<��2���2�chrome/pdfjs/content/web/cmaps/UniKS-UTF16-V.bcmapPK
!<���SgSg2���3�chrome/pdfjs/content/web/cmaps/UniKS-UTF32-H.bcmapPK
!<>9Ѩ�2��$��chrome/pdfjs/content/web/cmaps/UniKS-UTF32-V.bcmapPK
!<>�`�l�l1����chrome/pdfjs/content/web/cmaps/UniKS-UTF8-H.bcmapPK
!<���B��1����chrome/pdfjs/content/web/cmaps/UniKS-UTF8-V.bcmapPK
!<��5e��&���	�chrome/pdfjs/content/web/cmaps/V.bcmapPK
!<�!�ų�.���
�chrome/pdfjs/content/web/cmaps/WP-Symbol.bcmapPK
!<���>kKkK$����chrome/pdfjs/content/web/debugger.jsPK
!<,ͽ���4���W�chrome/pdfjs/content/web/images/annotation-check.svgPK
!<h�ss6��xY�chrome/pdfjs/content/web/images/annotation-comment.svgPK
!<�ڰjxx3��?]�chrome/pdfjs/content/web/images/annotation-help.svgPK
!<�w2���5��f�chrome/pdfjs/content/web/images/annotation-insert.svgPK
!<����2���g�chrome/pdfjs/content/web/images/annotation-key.svgPK
!<�g{��;���m�chrome/pdfjs/content/web/images/annotation-newparagraph.svgPK
!<FJcX��5���o�chrome/pdfjs/content/web/images/annotation-noicon.svgPK
!<�ZS3���p�chrome/pdfjs/content/web/images/annotation-note.svgPK
!<	]J�ww8��Eu�chrome/pdfjs/content/web/images/annotation-paragraph.svgPK
!<
�]��:��z�chrome/pdfjs/content/web/images/findbarButton-next-rtl.pngPK
!<�V00=��1{�chrome/pdfjs/content/web/images/[email protected]
!<�<���6���|�chrome/pdfjs/content/web/images/findbarButton-next.pngPK
!<JMa((9���}�chrome/pdfjs/content/web/images/[email protected]
!<�<���>��P�chrome/pdfjs/content/web/images/findbarButton-previous-rtl.pngPK
!<JMa((A��m��chrome/pdfjs/content/web/images/[email protected]
!<
�]��:����chrome/pdfjs/content/web/images/findbarButton-previous.pngPK
!<�V00=����chrome/pdfjs/content/web/images/[email protected]
!<�ܝpFF(�����chrome/pdfjs/content/web/images/grab.curPK
!<b�<�FF,��*��chrome/pdfjs/content/web/images/grabbing.curPK
!<����	�	0�����chrome/pdfjs/content/web/images/loading-icon.gifPK
!<dpor��1�����chrome/pdfjs/content/web/images/loading-small.pngPK
!<��}�??4��2��chrome/pdfjs/content/web/images/[email protected]
!<k���M�����chrome/pdfjs/content/web/images/secondaryToolbarButton-documentProperties.pngPK
!<�����P�����chrome/pdfjs/content/web/images/[email protected]
!<�Q��D�����chrome/pdfjs/content/web/images/secondaryToolbarButton-firstPage.pngPK
!<�_�/

G�����chrome/pdfjs/content/web/images/[email protected]
!<�--C����chrome/pdfjs/content/web/images/secondaryToolbarButton-handTool.pngPK
!<ZŤeGGF�����chrome/pdfjs/content/web/images/[email protected]
!<��3���C��U��chrome/pdfjs/content/web/images/secondaryToolbarButton-lastPage.pngPK
!<l�+F��e��chrome/pdfjs/content/web/images/[email protected]
!<��[�hhD����chrome/pdfjs/content/web/images/secondaryToolbarButton-rotateCcw.pngPK
!<4ʠ��G�����chrome/pdfjs/content/web/images/[email protected]
!<��ggC����chrome/pdfjs/content/web/images/secondaryToolbarButton-rotateCw.pngPK
!<}�/��F����chrome/pdfjs/content/web/images/[email protected]
!<n����E����chrome/pdfjs/content/web/images/secondaryToolbarButton-selectTool.pngPK
!<!��++H��
�chrome/pdfjs/content/web/images/[email protected]
!<8r~�""*����chrome/pdfjs/content/web/images/shadow.pngPK
!<Z#��q	q	+���chrome/pdfjs/content/web/images/texture.pngPK
!<��K��:����chrome/pdfjs/content/web/images/toolbarButton-bookmark.pngPK
!<�|�=����chrome/pdfjs/content/web/images/[email protected]
!<]̪�:��'�chrome/pdfjs/content/web/images/toolbarButton-download.pngPK
!<��^4��=����chrome/pdfjs/content/web/images/[email protected]
!<�nϠkk<����chrome/pdfjs/content/web/images/toolbarButton-menuArrows.pngPK
!<�܉��?��K �chrome/pdfjs/content/web/images/[email protected]
!<�)>'':��@!�chrome/pdfjs/content/web/images/toolbarButton-openFile.pngPK
!<�/.&&=���"�chrome/pdfjs/content/web/images/[email protected]
!<����>��@%�chrome/pdfjs/content/web/images/toolbarButton-pageDown-rtl.pngPK
!<�z���A���&�chrome/pdfjs/content/web/images/[email protected]
!<�e3Q��:��{(�chrome/pdfjs/content/web/images/toolbarButton-pageDown.pngPK
!<��2��=���)�chrome/pdfjs/content/web/images/[email protected]
!<b�����<���+�chrome/pdfjs/content/web/images/toolbarButton-pageUp-rtl.pngPK
!<r�	���?���,�chrome/pdfjs/content/web/images/[email protected]
!<Q���8���.�chrome/pdfjs/content/web/images/toolbarButton-pageUp.pngPK
!<����;��50�chrome/pdfjs/content/web/images/[email protected]
!<א�WAAB��!2�chrome/pdfjs/content/web/images/toolbarButton-presentationMode.pngPK
!<*=+JJE���3�chrome/pdfjs/content/web/images/[email protected]
!<���7��o6�chrome/pdfjs/content/web/images/toolbarButton-print.pngPK
!<��u��:���7�chrome/pdfjs/content/web/images/[email protected]
!<N'�558���9�chrome/pdfjs/content/web/images/toolbarButton-search.pngPK
!< ݚߍ�;��x;�chrome/pdfjs/content/web/images/[email protected]
!<nI���L��^>�chrome/pdfjs/content/web/images/toolbarButton-secondaryToolbarToggle-rtl.pngPK
!<��~��O���?�chrome/pdfjs/content/web/images/[email protected]
!<�<����H���A�chrome/pdfjs/content/web/images/toolbarButton-secondaryToolbarToggle.pngPK
!<�|fC��K��LC�chrome/pdfjs/content/web/images/[email protected]
!<K�P���C��E�chrome/pdfjs/content/web/images/toolbarButton-sidebarToggle-rtl.pngPK
!<j��XXF���F�chrome/pdfjs/content/web/images/[email protected]
!<x�����?��}H�chrome/pdfjs/content/web/images/toolbarButton-sidebarToggle.pngPK
!<+oh�KKB���I�chrome/pdfjs/content/web/images/[email protected]
!<�Cj���A��fK�chrome/pdfjs/content/web/images/toolbarButton-viewAttachments.pngPK
!<s%��[[D��EM�chrome/pdfjs/content/web/images/[email protected]
!<�҆��A��Q�chrome/pdfjs/content/web/images/toolbarButton-viewOutline-rtl.pngPK
!<��c��D��R�chrome/pdfjs/content/web/images/[email protected]
!<�nβ�=���S�chrome/pdfjs/content/web/images/toolbarButton-viewOutline.pngPK
!<�7�,KK@��U�chrome/pdfjs/content/web/images/[email protected]
!<tݾ~��?���V�chrome/pdfjs/content/web/images/toolbarButton-viewThumbnail.pngPK
!<�i[��B���W�chrome/pdfjs/content/web/images/[email protected]
!<��,~��8��Y�chrome/pdfjs/content/web/images/toolbarButton-zoomIn.pngPK
!<�L?͠�;���Y�chrome/pdfjs/content/web/images/[email protected]
!<�xXX9���Z�chrome/pdfjs/content/web/images/toolbarButton-zoomOut.pngPK
!<4�mm<���[�chrome/pdfjs/content/web/images/[email protected]
!<ڈ�l��:��R\�chrome/pdfjs/content/web/images/treeitem-collapsed-rtl.pngPK
!<5�����=��9]�chrome/pdfjs/content/web/images/[email protected]
!<�J]��6��;^�chrome/pdfjs/content/web/images/treeitem-collapsed.pngPK
!<~*�X��9��_�chrome/pdfjs/content/web/images/[email protected]
!<+��}}5���_�chrome/pdfjs/content/web/images/treeitem-expanded.pngPK
!<����8���`�chrome/pdfjs/content/web/images/[email protected]
!<�T�d�d�#���a�chrome/pdfjs/content/web/viewer.cssPK
!<���&�G�G$��r$�chrome/pdfjs/content/web/viewer.htmlPK
!<z��77"��Rl�chrome/pdfjs/content/web/viewer.jsPK
!<:n���
�
�����chrome/webide/content/addons.jsPK
!<n!���"�����chrome/webide/content/addons.xhtmlPK
!<�*�gHH ��L��chrome/webide/content/details.jsPK
!<r���#�����chrome/webide/content/details.xhtmlPK
!<����|	|	*����chrome/webide/content/devicepreferences.jsPK
!<8�˗-�����chrome/webide/content/devicepreferences.xhtmlPK
!<�����4��chrome/webide/content/newapp.jsPK
!<s�?MAA �����chrome/webide/content/newapp.xulPK
!<
�%����chrome/webide/content/prefs.jsPK
!<;P7�ee!��J�chrome/webide/content/prefs.xhtmlPK
!<���ݢ�(���
�chrome/webide/content/project-listing.jsPK
!<#�:_��+����chrome/webide/content/project-listing.xhtmlPK
!<�؏��&����chrome/webide/content/project-panel.jsPK
!<[q��(����chrome/webide/content/runtime-listing.jsPK
!<�o����+��G �chrome/webide/content/runtime-listing.xhtmlPK
!<�K��&��|(�chrome/webide/content/runtime-panel.jsPK
!<���i��'���*�chrome/webide/content/runtimedetails.jsPK
!<��
��*���@�chrome/webide/content/runtimedetails.xhtmlPK
!<��
,ww���F�chrome/webide/content/webide.jsPK
!<Խ���(�( ��7��chrome/webide/content/webide.xulPK
!<�+		"��I��chrome/webide/content/wifi-auth.jsPK
!<�(��cc%�����chrome/webide/content/wifi-auth.xhtmlPK
!<�/�����8��chrome/webide/skin/addons.cssPK
!<���00"��N��chrome/webide/skin/config-view.cssPK
!</��&&�����chrome/webide/skin/deck.cssPK
!<�j�
�
'���chrome/webide/skin/default-app-icon.pngPK
!<�(����`�chrome/webide/skin/details.cssPK
!<E���c�c����chrome/webide/skin/icons.pngPK
!<N?������z�chrome/webide/skin/newapp.cssPK
!<qv�������~�chrome/webide/skin/noise.pngPK
!<',!

$�����chrome/webide/skin/panel-listing.cssPK
!<rEoI==����chrome/webide/skin/rocket.svgPK
!<�����%��b��chrome/webide/skin/runtimedetails.cssPK
!<ދ6�ww��a��chrome/webide/skin/throbber.svgPK
!<��VV����chrome/webide/skin/webide.cssPK
!<�딟{{ �����chrome/webide/skin/wifi-auth.cssPK
!<O2���<��_��chrome/devtools-shim/content/aboutdevtools/aboutdevtools.cssPK
!<*�y�$�$;�����chrome/devtools-shim/content/aboutdevtools/aboutdevtools.jsPK
!<`R�>�����chrome/devtools-shim/content/aboutdevtools/aboutdevtools.xhtmlPK
!<�.��.�.F��"�chrome/devtools-shim/content/aboutdevtools/images/dev-edition-logo.svgPK
!<�:e wwC��T5�chrome/devtools-shim/content/aboutdevtools/images/external-link.svgPK
!<�:�LLE�,8�chrome/devtools-shim/content/aboutdevtools/images/feature-console.svgPK
!<�a�L�
�
F��F�chrome/devtools-shim/content/aboutdevtools/images/feature-debugger.svgPK
!<�O�
__G��T�chrome/devtools-shim/content/aboutdevtools/images/feature-inspector.svgPK
!<��;�TTD큓d�chrome/devtools-shim/content/aboutdevtools/images/feature-memory.svgPK
!<��[�	�	E�Is�chrome/devtools-shim/content/aboutdevtools/images/feature-network.svgPK
!<��W""I큙}�chrome/devtools-shim/content/aboutdevtools/images/feature-performance.svgPK
!<��M

H�"��chrome/devtools-shim/content/aboutdevtools/images/feature-responsive.svgPK
!<��	11E큕��chrome/devtools-shim/content/aboutdevtools/images/feature-storage.svgPK
!<S^��K�)��chrome/devtools-shim/content/aboutdevtools/images/feature-visualediting.svgPK
!<=�-^{d{d;�����chrome/devtools-shim/content/aboutdevtools/images/otter.svgPK
!<�i��8��w#�chrome/devtools-shim/content/aboutdevtools/subscribe.cssPK
!<�.@��7���+�chrome/devtools-shim/content/aboutdevtools/subscribe.jsPK
!<��Q=]]9���?�chrome/devtools/content/aboutdebugging/aboutdebugging.cssPK
!<K�@�;��>Y�chrome/devtools/content/aboutdebugging/aboutdebugging.xhtmlPK
!<�� 995���]�chrome/devtools/content/aboutdebugging/initializer.jsPK
!<����4�4B��/f�chrome/devtools/content/animationinspector/animation-controller.jsPK
!<��9\\D��Y��chrome/devtools/content/animationinspector/animation-inspector.xhtmlPK
!<V���,�,=����chrome/devtools/content/animationinspector/animation-panel.jsPK
!<u�,�;H;H3��W��chrome/devtools/content/canvasdebugger/callslist.jsPK
!<7�yR--8����chrome/devtools/content/canvasdebugger/canvasdebugger.jsPK
!<D�����9��AG�chrome/devtools/content/canvasdebugger/canvasdebugger.xulPK
!<�0#�HFHF7��u]�chrome/devtools/content/canvasdebugger/snapshotslist.jsPK
!<�k����;����chrome/devtools/content/commandline/commandlineoutput.xhtmlPK
!<M H���<����chrome/devtools/content/commandline/commandlinetooltip.xhtmlPK
!<<R擾���7�����chrome/devtools/content/debugger/debugger-controller.jsPK
!<s���zz1��\�chrome/devtools/content/debugger/debugger-view.jsPK
!<�[0���-��_��chrome/devtools/content/debugger/debugger.cssPK
!<k�I�S�S-��y��chrome/devtools/content/debugger/debugger.xulPK
!<���,,/���0�chrome/devtools/content/debugger/new/index.htmlPK
!<�H�^{,{,)��/6�chrome/devtools/content/debugger/utils.jsPK
!<�7�w�z�z5���b�chrome/devtools/content/debugger/views/filter-view.jsPK
!<nHA�X�X<����chrome/devtools/content/debugger/views/global-search-view.jsPK
!<�R
���6��77�chrome/devtools/content/debugger/views/options-view.jsPK
!<��IC��V�chrome/devtools/content/debugger/views/stack-frames-classic-view.jsPK
!<�l�$�$�$;���i�chrome/devtools/content/debugger/views/stack-frames-view.jsPK
!<�Rs((6����chrome/devtools/content/debugger/views/toolbar-view.jsPK
!<�����+�+>����chrome/devtools/content/debugger/views/variable-bubble-view.jsPK
!<�#�&�&@����chrome/devtools/content/debugger/views/watch-expressions-view.jsPK
!<K�B��6��
�chrome/devtools/content/debugger/views/workers-view.jsPK
!<�/ii$��&�chrome/devtools/content/dom/dom.htmlPK
!<�ō�#����chrome/devtools/content/dom/main.jsPK
!<$33�5����chrome/devtools/content/framework/connect/connect.cssPK
!<1��^^4����chrome/devtools/content/framework/connect/connect.jsPK
!<‐���7���;�chrome/devtools/content/framework/connect/connect.xhtmlPK
!<�3JJH���D�chrome/devtools/content/framework/dev-edition-promo/dev-edition-logo.pngPK
!<�ܫa99I���_�chrome/devtools/content/framework/dev-edition-promo/dev-edition-promo.cssPK
!<���JJI��2g�chrome/devtools/content/framework/dev-edition-promo/dev-edition-promo.xulPK
!<7�b.�	�	3���l�chrome/devtools/content/framework/options-panel.cssPK
!<����221���v�chrome/devtools/content/framework/toolbox-init.jsPK
!<���W7"7"7��x��chrome/devtools/content/framework/toolbox-options.xhtmlPK
!<(,�F�"�";����chrome/devtools/content/framework/toolbox-process-window.jsPK
!<�:�<��)��chrome/devtools/content/framework/toolbox-process-window.xulPK
!<&���4�����chrome/devtools/content/framework/toolbox-window.xulPK
!<�"__-��z��chrome/devtools/content/framework/toolbox.xulPK
!<��a�<�<.��$��chrome/devtools/content/inspector/inspector.jsPK
!< �ӨYY1��\,�chrome/devtools/content/inspector/inspector.xhtmlPK
!<�ȝ>��5��K�chrome/devtools/content/inspector/markup/markup.xhtmlPK
!<#A{R!	!	-��QP�chrome/devtools/content/memory/initializer.jsPK
!<y=�E��+���Y�chrome/devtools/content/memory/memory.xhtmlPK
!<-��FF-���_�chrome/devtools/content/netmonitor/index.htmlPK
!<��enn1��Lb�chrome/devtools/content/netmonitor/initializer.jsPK
!<~g��<��	�chrome/devtools/content/netmonitor/src/assets/icons/play.svgPK
!<N2S�44@��\��chrome/devtools/content/netmonitor/src/assets/styles/MdnLink.cssPK
!<c��((L����chrome/devtools/content/netmonitor/src/assets/styles/NetworkDetailsPanel.cssPK
!<�TϮ5�5D��v��chrome/devtools/content/netmonitor/src/assets/styles/RequestList.cssPK
!<
��JhhH�����chrome/devtools/content/netmonitor/src/assets/styles/StatisticsPanel.cssPK
!<K���qqB��T��chrome/devtools/content/netmonitor/src/assets/styles/StatusBar.cssPK
!<9̑���@��%��chrome/devtools/content/netmonitor/src/assets/styles/Toolbar.cssPK
!<.τkhh>����chrome/devtools/content/netmonitor/src/assets/styles/httpi.cssPK
!<^�<��C��C�chrome/devtools/content/netmonitor/src/assets/styles/netmonitor.cssPK
!<�Y�		B��3
�chrome/devtools/content/netmonitor/src/assets/styles/variables.cssPK
!<(�]	�Q�Q=����chrome/devtools/content/performance/performance-controller.jsPK
!<}_�227���h�chrome/devtools/content/performance/performance-view.jsPK
!<��v��C�C3��!��chrome/devtools/content/performance/performance.xulPK
!<\�-(��E��*��chrome/devtools/content/performance/views/details-abstract-subview.jsPK
!<r��9//A��Y��chrome/devtools/content/performance/views/details-js-call-tree.jsPK
!<+�����B����chrome/devtools/content/performance/views/details-js-flamegraph.jsPK
!<�pVN]]E��� �chrome/devtools/content/performance/views/details-memory-call-tree.jsPK
!<k��F���0�chrome/devtools/content/performance/views/details-memory-flamegraph.jsPK
!<kE��>���?�chrome/devtools/content/performance/views/details-waterfall.jsPK
!<��̍R!R!4��7^�chrome/devtools/content/performance/views/details.jsPK
!<�2�{6{65����chrome/devtools/content/performance/views/overview.jsPK
!<p!pnn7�����chrome/devtools/content/performance/views/recordings.jsPK
!<�I>H__4��l��chrome/devtools/content/performance/views/toolbar.jsPK
!<�FŬ�7����chrome/devtools/content/performance-new/frame-script.jsPK
!<�u��OO6����chrome/devtools/content/performance-new/initializer.jsPK
!<�Ƌ��2�����chrome/devtools/content/performance-new/perf.xhtmlPK
!<��;uu0����chrome/devtools/content/responsive.html/index.jsPK
!<�4�''3��j�chrome/devtools/content/responsive.html/index.xhtmlPK
!<���QSS0����chrome/devtools/content/scratchpad/scratchpad.jsPK
!<(x.�1H1H1���6�chrome/devtools/content/scratchpad/scratchpad.xulPK
!<#h�P�P4���chrome/devtools/content/shadereditor/shadereditor.jsPK
!<��Z

5�����chrome/devtools/content/shadereditor/shadereditor.xulPK
!<�Q�*��,��<��chrome/devtools/content/shared/splitview.cssPK
!<aǹ��1��R��chrome/devtools/content/shared/theme-switching.jsPK
!<��>���+��b��chrome/devtools/content/shared/vendor/d3.jsPK
!<�۩����1�����chrome/devtools/content/shared/vendor/dagre-d3.jsPK
!<b,RR8�����chrome/devtools/content/shared/widgets/VariablesView.xulPK
!<�Q!!7��L��chrome/devtools/content/shared/widgets/color-widget.cssPK
!<�׺7��7����chrome/devtools/content/shared/widgets/cubic-bezier.cssPK
!<^�㽇�8����chrome/devtools/content/shared/widgets/filter-widget.cssPK
!<���W339��z*�chrome/devtools/content/shared/widgets/graphs-frame.xhtmlPK
!<���7�
�
3��.�chrome/devtools/content/shared/widgets/spectrum.cssPK
!<+{d�
�
2��*9�chrome/devtools/content/shared/widgets/widgets.cssPK
!<�H[���G��\D�chrome/devtools/content/sourceeditor/codemirror/addon/dialog/dialog.cssPK
!<S|��%>%>G���F�chrome/devtools/content/sourceeditor/codemirror/addon/hint/show-hint.jsPK
!<�s>%�a�aB��F��chrome/devtools/content/sourceeditor/codemirror/addon/tern/tern.jsPK
!<���%��=��i��chrome/devtools/content/sourceeditor/codemirror/cmiframe.htmlPK
!<��E�-
�-
D����chrome/devtools/content/sourceeditor/codemirror/codemirror.bundle.jsPK
!<���6^!^!B���chrome/devtools/content/sourceeditor/codemirror/lib/codemirror.cssPK
!<{�X��;��R:chrome/devtools/content/sourceeditor/codemirror/mozilla.cssPK
!<�6���@��YRchrome/devtools/content/sourceeditor/codemirror/old-debugger.cssPK
!</��d��+���Uchrome/devtools/content/storage/storage.xulPK
!<�qk�--3���echrome/devtools/content/styleeditor/styleeditor.xulPK
!<�|�>>4���chrome/devtools/content/webaudioeditor/controller.jsPK
!<�CMM2����chrome/devtools/content/webaudioeditor/includes.jsPK
!<R^����0��H�chrome/devtools/content/webaudioeditor/models.jsPK
!<�+ź00:��}�chrome/devtools/content/webaudioeditor/views/automation.jsPK
!<
��''7���chrome/devtools/content/webaudioeditor/views/context.jsPK
!<P�jN��9��xchrome/devtools/content/webaudioeditor/views/inspector.jsPK
!<S�,		:��t/chrome/devtools/content/webaudioeditor/views/properties.jsPK
!<C��
�
�
5���@chrome/devtools/content/webaudioeditor/views/utils.jsPK
!<5�0��9��%Lchrome/devtools/content/webaudioeditor/webaudioeditor.xulPK
!<�`��5��_dchrome/devtools/content/webconsole/browserconsole.xulPK
!<GJ|i��2���gchrome/devtools/content/webconsole/webconsole.htmlPK
!<��$4$41���ochrome/devtools/content/webconsole/webconsole.xulPK
!<����
�
S��
�chrome/devtools/modules/devtools/client/aboutdebugging/components/Aboutdebugging.jsPK
!<�k0�P��d�chrome/devtools/modules/devtools/client/aboutdebugging/components/PanelHeader.jsPK
!<f��{{N���chrome/devtools/modules/devtools/client/aboutdebugging/components/PanelMenu.jsPK
!<̻Y���S��ֻchrome/devtools/modules/devtools/client/aboutdebugging/components/PanelMenuEntry.jsPK
!<Y��W��O���chrome/devtools/modules/devtools/client/aboutdebugging/components/TargetList.jsPK
!<��T��T��9�chrome/devtools/modules/devtools/client/aboutdebugging/components/addons/Controls.jsPK
!<H�8�X��H�chrome/devtools/modules/devtools/client/aboutdebugging/components/addons/InstallError.jsPK
!<#q3>>Q����chrome/devtools/modules/devtools/client/aboutdebugging/components/addons/Panel.jsPK
!<�L�R��~�chrome/devtools/modules/devtools/client/aboutdebugging/components/addons/Target.jsPK
!<�)7�G
G
O��	chrome/devtools/modules/devtools/client/aboutdebugging/components/tabs/Panel.jsPK
!<�fN_��P���chrome/devtools/modules/devtools/client/aboutdebugging/components/tabs/Target.jsPK
!<����kk]���&chrome/devtools/modules/devtools/client/aboutdebugging/components/workers/MultiE10sWarning.jsPK
!<�"J5$$R���/chrome/devtools/modules/devtools/client/aboutdebugging/components/workers/Panel.jsPK
!<�����`��5Tchrome/devtools/modules/devtools/client/aboutdebugging/components/workers/ServiceWorkerTarget.jsPK
!<K��
UUS���schrome/devtools/modules/devtools/client/aboutdebugging/components/workers/Target.jsPK
!<�<�QQG��b{chrome/devtools/modules/devtools/client/aboutdebugging/modules/addon.jsPK
!<�D�RRI���chrome/devtools/modules/devtools/client/aboutdebugging/modules/connect.jsPK
!<��h	h	H��ѐchrome/devtools/modules/devtools/client/aboutdebugging/modules/worker.jsPK
!<�/LB�#�#Z����chrome/devtools/modules/devtools/client/animationinspector/components/animation-details.jsPK
!<a먑B
B
^����chrome/devtools/modules/devtools/client/animationinspector/components/animation-target-node.jsPK
!<��jK�d�d]��d�chrome/devtools/modules/devtools/client/animationinspector/components/animation-time-block.jsPK
!<x��bfbf[���.chrome/devtools/modules/devtools/client/animationinspector/components/animation-timeline.jsPK
!<RՕ8��R����chrome/devtools/modules/devtools/client/animationinspector/components/keyframes.jsPK
!<e���K
K
V����chrome/devtools/modules/devtools/client/animationinspector/components/rate-selector.jsPK
!<�M��<v<vJ��d�chrome/devtools/modules/devtools/client/animationinspector/graph-helper.jsPK
!<��v_+_+C��8chrome/devtools/modules/devtools/client/animationinspector/utils.jsPK
!<ңna��?���cchrome/devtools/modules/devtools/client/canvasdebugger/panel.jsPK
!<&��~��O��mchrome/devtools/modules/devtools/client/debugger/content/actions/breakpoints.jsPK
!<6'Ɖ�S��l�chrome/devtools/modules/devtools/client/debugger/content/actions/event-listeners.jsPK
!<���Y!Y!K��f�chrome/devtools/modules/devtools/client/debugger/content/actions/sources.jsPK
!<���6��E��(�chrome/devtools/modules/devtools/client/debugger/content/constants.jsPK
!<}[B--I��v�chrome/devtools/modules/devtools/client/debugger/content/globalActions.jsPK
!<�o��C��
�chrome/devtools/modules/devtools/client/debugger/content/queries.jsPK
!<]Lr�''S��#�chrome/devtools/modules/devtools/client/debugger/content/reducers/async-requests.jsPK
!<1�ĴP����chrome/devtools/modules/devtools/client/debugger/content/reducers/breakpoints.jsPK
!<9@T�T��=�chrome/devtools/modules/devtools/client/debugger/content/reducers/event-listeners.jsPK
!<T����J����chrome/devtools/modules/devtools/client/debugger/content/reducers/index.jsPK
!<�RΙ((L���chrome/devtools/modules/devtools/client/debugger/content/reducers/sources.jsPK
!<���BBA����chrome/devtools/modules/devtools/client/debugger/content/utils.jsPK
!<�i?�~*~*V��9�chrome/devtools/modules/devtools/client/debugger/content/views/event-listeners-view.jsPK
!<^^OγγN��+'chrome/devtools/modules/devtools/client/debugger/content/views/sources-view.jsPK
!<�����<�<E��e�chrome/devtools/modules/devtools/client/debugger/debugger-commands.jsPK
!<p�)YRRA���chrome/devtools/modules/devtools/client/debugger/new/debugger.cssPK
!<��>��6�6@���jchrome/devtools/modules/devtools/client/debugger/new/debugger.jsPK
!<���
�
=���!chrome/devtools/modules/devtools/client/debugger/new/panel.jsPK
!<B��绝��E��&�!chrome/devtools/modules/devtools/client/debugger/new/parser-worker.jsPK
!<_��h!h!K��DN0chrome/devtools/modules/devtools/client/debugger/new/pretty-print-worker.jsPK
!<��Qw�X�XE��p4chrome/devtools/modules/devtools/client/debugger/new/search-worker.jsPK
!<ݟx�rr9���4chrome/devtools/modules/devtools/client/debugger/panel.jsPK
!<�تN�N6����4chrome/devtools/modules/devtools/client/definitions.jsPK
!<�QL�ffE���.5chrome/devtools/modules/devtools/client/dom/content/actions/filter.jsPK
!<F�����D���15chrome/devtools/modules/devtools/client/dom/content/actions/grips.jsPK
!<i�

J���75chrome/devtools/modules/devtools/client/dom/content/components/dom-tree.jsPK
!<��1�ooL��B5chrome/devtools/modules/devtools/client/dom/content/components/main-frame.jsPK
!<ˬ�,  N���I5chrome/devtools/modules/devtools/client/dom/content/components/main-toolbar.jsPK
!<i�梭�@��mR5chrome/devtools/modules/devtools/client/dom/content/constants.jsPK
!<�����D��xT5chrome/devtools/modules/devtools/client/dom/content/dom-decorator.jsPK
!<�F�5

@���Y5chrome/devtools/modules/devtools/client/dom/content/dom-view.cssPK
!<�x��?��d5chrome/devtools/modules/devtools/client/dom/content/dom-view.jsPK
!<̋B

D��l5chrome/devtools/modules/devtools/client/dom/content/grip-provider.jsPK
!<�ײ���F��xv5chrome/devtools/modules/devtools/client/dom/content/reducers/filter.jsPK
!<1\6ӳ�E���y5chrome/devtools/modules/devtools/client/dom/content/reducers/grips.jsPK
!<�Fo���E����5chrome/devtools/modules/devtools/client/dom/content/reducers/index.jsPK
!<i�"��<��و5chrome/devtools/modules/devtools/client/dom/content/utils.jsPK
!<���'��8��ӌ5chrome/devtools/modules/devtools/client/dom/dom-panel.jsPK
!<����-4-4D��٥5chrome/devtools/modules/devtools/client/framework/ToolboxProcess.jsmPK
!<�����B��h�5chrome/devtools/modules/devtools/client/framework/attach-thread.jsPK
!<��<D��B��L�5chrome/devtools/modules/devtools/client/framework/browser-menus.jsPK
!<E3i�xxR���	6chrome/devtools/modules/devtools/client/framework/components/toolbox-controller.jsPK
!<z�m���K��o 6chrome/devtools/modules/devtools/client/framework/components/toolbox-tab.jsPK
!<�,U��L���)6chrome/devtools/modules/devtools/client/framework/components/toolbox-tabs.jsPK
!<_ߢ� � O��;6chrome/devtools/modules/devtools/client/framework/components/toolbox-toolbar.jsPK
!<�1�&�f�fE��-\6chrome/devtools/modules/devtools/client/framework/devtools-browser.jsPK
!<�G��&^&^=���6chrome/devtools/modules/devtools/client/framework/devtools.jsPK
!<���::?���!7chrome/devtools/modules/devtools/client/framework/gDevTools.jsmPK
!<�����>��017chrome/devtools/modules/devtools/client/framework/menu-item.jsPK
!<�K��9��:7chrome/devtools/modules/devtools/client/framework/menu.jsPK
!<�zl BB>���L7chrome/devtools/modules/devtools/client/framework/selection.jsPK
!<h;j�D�D<���e7chrome/devtools/modules/devtools/client/framework/sidebar.jsPK
!<wSG/G/K����7chrome/devtools/modules/devtools/client/framework/source-map-url-service.jsPK
!<� qD��D�7chrome/devtools/modules/devtools/client/framework/target-from-url.jsPK
!<��	l	l;����7chrome/devtools/modules/devtools/client/framework/target.jsPK
!<��
oT,T,N��Y8chrome/devtools/modules/devtools/client/framework/toolbox-highlighter-utils.jsPK
!<���y{ { I��ԅ8chrome/devtools/modules/devtools/client/framework/toolbox-host-manager.jsPK
!<Zi��*�*B����8chrome/devtools/modules/devtools/client/framework/toolbox-hosts.jsPK
!<P�KBBD���8chrome/devtools/modules/devtools/client/framework/toolbox-options.jsPK
!<-���b�b�<��f9chrome/devtools/modules/devtools/client/framework/toolbox.jsPK
!<,noe77Q��"�:chrome/devtools/modules/devtools/client/inspector/animation/actions/animations.jsPK
!<KVnŁ�L��ȴ:chrome/devtools/modules/devtools/client/inspector/animation/actions/index.jsPK
!<#���$�$H����:chrome/devtools/modules/devtools/client/inspector/animation/animation.jsPK
!<�ɠJ^����:chrome/devtools/modules/devtools/client/inspector/animation/components/AnimatedPropertyItem.jsPK
!<�e��$
$
^��x�:chrome/devtools/modules/devtools/client/inspector/animation/components/AnimatedPropertyList.jsPK
!<A��++g���:chrome/devtools/modules/devtools/client/inspector/animation/components/AnimatedPropertyListContainer.jsPK
!<Ջ�;��d����:chrome/devtools/modules/devtools/client/inspector/animation/components/AnimatedPropertyListHeader.jsPK
!<��'Q[[^��1�:chrome/devtools/modules/devtools/client/inspector/animation/components/AnimatedPropertyName.jsPK
!<�j���b���:chrome/devtools/modules/devtools/client/inspector/animation/components/AnimationDetailContainer.jsPK
!<�6�-_��q;chrome/devtools/modules/devtools/client/inspector/animation/components/AnimationDetailHeader.jsPK
!<+����W��
;chrome/devtools/modules/devtools/client/inspector/animation/components/AnimationItem.jsPK
!<������W��o;chrome/devtools/modules/devtools/client/inspector/animation/components/AnimationList.jsPK
!<>LKrr`���;chrome/devtools/modules/devtools/client/inspector/animation/components/AnimationListContainer.jsPK
!<���g��]���(;chrome/devtools/modules/devtools/client/inspector/animation/components/AnimationListHeader.jsPK
!<I3�1..Y��-;chrome/devtools/modules/devtools/client/inspector/animation/components/AnimationTarget.jsPK
!<C�뢔�c���9;chrome/devtools/modules/devtools/client/inspector/animation/components/AnimationTimelineTickItem.jsPK
!<�$���c���=;chrome/devtools/modules/devtools/client/inspector/animation/components/AnimationTimelineTickList.jsPK
!<�����
�
M���I;chrome/devtools/modules/devtools/client/inspector/animation/components/App.jsPK
!<<��.c���W;chrome/devtools/modules/devtools/client/inspector/animation/components/KeyframesProgressTickItem.jsPK
!<�%6���c��u\;chrome/devtools/modules/devtools/client/inspector/animation/components/KeyframesProgressTickList.jsPK
!<�v���Z���a;chrome/devtools/modules/devtools/client/inspector/animation/components/NoAnimationPanel.jsPK
!<����vv]���h;chrome/devtools/modules/devtools/client/inspector/animation/components/graph/AnimationName.jsPK
!<��J5��b���l;chrome/devtools/modules/devtools/client/inspector/animation/components/graph/ComputedTimingPath.jsPK
!<SY���Y��z;chrome/devtools/modules/devtools/client/inspector/animation/components/graph/DelaySign.jsPK
!<�(ד		`���;chrome/devtools/modules/devtools/client/inspector/animation/components/graph/EffectTimingPath.jsPK
!<��!p��\����;chrome/devtools/modules/devtools/client/inspector/animation/components/graph/EndDelaySign.jsPK
!<�룄�a����;chrome/devtools/modules/devtools/client/inspector/animation/components/graph/NegativeDelayPath.jsPK
!<�)���d���;chrome/devtools/modules/devtools/client/inspector/animation/components/graph/NegativeEndDelayPath.jsPK
!<8}�\��P�;chrome/devtools/modules/devtools/client/inspector/animation/components/graph/NegativePath.jsPK
!<7�H���\��ܦ;chrome/devtools/modules/devtools/client/inspector/animation/components/graph/SummaryGraph.jsPK
!<Q=<Q��`��P�;chrome/devtools/modules/devtools/client/inspector/animation/components/graph/SummaryGraphPath.jsPK
!<r�VP�1�1Z����;chrome/devtools/modules/devtools/client/inspector/animation/components/graph/TimingPath.jsPK
!<'��c���	<chrome/devtools/modules/devtools/client/inspector/animation/components/keyframes-graph/ColorPath.jsPK
!<��k���<chrome/devtools/modules/devtools/client/inspector/animation/components/keyframes-graph/ComputedStylePath.jsPK
!<8�f��v5<chrome/devtools/modules/devtools/client/inspector/animation/components/keyframes-graph/DiscretePath.jsPK
!<�W���f���;<chrome/devtools/modules/devtools/client/inspector/animation/components/keyframes-graph/DistancePath.jsPK
!<K��ool��_?<chrome/devtools/modules/devtools/client/inspector/animation/components/keyframes-graph/KeyframeMarkerItem.jsPK
!<�[}!��l��XC<chrome/devtools/modules/devtools/client/inspector/animation/components/keyframes-graph/KeyframeMarkerList.jsPK
!<�Q����h���G<chrome/devtools/modules/devtools/client/inspector/animation/components/keyframes-graph/KeyframesGraph.jsPK
!<-n��}}l���M<chrome/devtools/modules/devtools/client/inspector/animation/components/keyframes-graph/KeyframesGraphPath.jsPK
!<2��%��R���Y<chrome/devtools/modules/devtools/client/inspector/animation/reducers/animations.jsPK
!<�"�o$o$Q���_<chrome/devtools/modules/devtools/client/inspector/animation/utils/graph-helper.jsPK
!<u	C�ggI����<chrome/devtools/modules/devtools/client/inspector/animation/utils/l10n.jsPK
!<���N����<chrome/devtools/modules/devtools/client/inspector/animation/utils/timescale.jsPK
!<�埑d
d
J��
�<chrome/devtools/modules/devtools/client/inspector/animation/utils/utils.jsPK
!<������O��٣<chrome/devtools/modules/devtools/client/inspector/boxmodel/actions/box-model.jsPK
!<�Tl??K��(�<chrome/devtools/modules/devtools/client/inspector/boxmodel/actions/index.jsPK
!<�L��.�.G��Ъ<chrome/devtools/modules/devtools/client/inspector/boxmodel/box-model.jsPK
!<���4
4
Q����<chrome/devtools/modules/devtools/client/inspector/boxmodel/components/BoxModel.jsPK
!<���2T��x�<chrome/devtools/modules/devtools/client/inspector/boxmodel/components/BoxModelApp.jsPK
!<�4
�YYY����<chrome/devtools/modules/devtools/client/inspector/boxmodel/components/BoxModelEditable.jsPK
!<ǦK�		U���<chrome/devtools/modules/devtools/client/inspector/boxmodel/components/BoxModelInfo.jsPK
!<�ڈJ�O�OU��T�<chrome/devtools/modules/devtools/client/inspector/boxmodel/components/BoxModelMain.jsPK
!<H�����[���O=chrome/devtools/modules/devtools/client/inspector/boxmodel/components/BoxModelProperties.jsPK
!<��&,,Y���^=chrome/devtools/modules/devtools/client/inspector/boxmodel/components/ComputedProperty.jsPK
!<`��P���k=chrome/devtools/modules/devtools/client/inspector/boxmodel/reducers/box-model.jsPK
!<J��Յ�C��p=chrome/devtools/modules/devtools/client/inspector/boxmodel/types.jsPK
!<�*����S���r=chrome/devtools/modules/devtools/client/inspector/boxmodel/utils/editing-session.jsPK
!<�����o�o@��K�=chrome/devtools/modules/devtools/client/inspector/breadcrumbs.jsPK
!<3䥶��J����=chrome/devtools/modules/devtools/client/inspector/changes/actions/index.jsPK
!<+2.AAD����=chrome/devtools/modules/devtools/client/inspector/changes/changes.jsPK
!<�]S��R��,>chrome/devtools/modules/devtools/client/inspector/changes/components/ChangesApp.jsPK
!<��'���M��F>chrome/devtools/modules/devtools/client/inspector/changes/reducers/changes.jsPK
!<D+tK��x>chrome/devtools/modules/devtools/client/inspector/changes/reducers/index.jsPK
!<��4=��R���	>chrome/devtools/modules/devtools/client/inspector/components/InspectorTabPanel.cssPK
!<�S�Q���>chrome/devtools/modules/devtools/client/inspector/components/InspectorTabPanel.jsPK
!<o9׻׻F��Z>chrome/devtools/modules/devtools/client/inspector/computed/computed.jsPK
!<��5���I����>chrome/devtools/modules/devtools/client/inspector/events/actions/index.jsPK
!<z����P����>chrome/devtools/modules/devtools/client/inspector/events/components/EventsApp.jsPK
!<,[�A77B����>chrome/devtools/modules/devtools/client/inspector/events/events.jsPK
!<0�![��K��<�>chrome/devtools/modules/devtools/client/inspector/events/reducers/events.jsPK
!<S�KJ��g�>chrome/devtools/modules/devtools/client/inspector/events/reducers/index.jsPK
!<�C�wwM����>chrome/devtools/modules/devtools/client/inspector/extensions/actions/index.jsPK
!<m�-11O����>chrome/devtools/modules/devtools/client/inspector/extensions/actions/sidebar.jsPK
!<;�'<�	�	[��R�>chrome/devtools/modules/devtools/client/inspector/extensions/components/ExtensionSidebar.jsPK
!<엽�++Y��\�>chrome/devtools/modules/devtools/client/inspector/extensions/components/ObjectTreeView.jsPK
!<�1�
�
^����>chrome/devtools/modules/devtools/client/inspector/extensions/components/ObjectValueGripView.jsPK
!<�P��EEQ��C?chrome/devtools/modules/devtools/client/inspector/extensions/extension-sidebar.jsPK
!<om����P���?chrome/devtools/modules/devtools/client/inspector/extensions/reducers/sidebar.jsPK
!<������E��?chrome/devtools/modules/devtools/client/inspector/extensions/types.jsPK
!<���q��L��D ?chrome/devtools/modules/devtools/client/inspector/flexbox/actions/flexbox.jsPK
!<Sk��FFJ���$?chrome/devtools/modules/devtools/client/inspector/flexbox/actions/index.jsPK
!<��O��3'?chrome/devtools/modules/devtools/client/inspector/flexbox/components/Flexbox.jsPK
!<!�F   S���/?chrome/devtools/modules/devtools/client/inspector/flexbox/components/FlexboxItem.jsPK
!<y}� � D��E<?chrome/devtools/modules/devtools/client/inspector/flexbox/flexbox.jsPK
!<�F>M��5]?chrome/devtools/modules/devtools/client/inspector/flexbox/reducers/flexbox.jsPK
!<Q7�K���a?chrome/devtools/modules/devtools/client/inspector/flexbox/reducers/index.jsPK
!<� �XDDB��#c?chrome/devtools/modules/devtools/client/inspector/flexbox/types.jsPK
!<����O���e?chrome/devtools/modules/devtools/client/inspector/fonts/actions/font-options.jsPK
!<Y��M��H��h?chrome/devtools/modules/devtools/client/inspector/fonts/actions/fonts.jsPK
!<޶�q��H��Qj?chrome/devtools/modules/devtools/client/inspector/fonts/actions/index.jsPK
!<(\��}}J��`l?chrome/devtools/modules/devtools/client/inspector/fonts/components/Font.jsPK
!<da�N��E|?chrome/devtools/modules/devtools/client/inspector/fonts/components/FontList.jsPK
!<� 	 	Q��0�?chrome/devtools/modules/devtools/client/inspector/fonts/components/FontPreview.jsPK
!<O<�:$	$	N����?chrome/devtools/modules/devtools/client/inspector/fonts/components/FontsApp.jsPK
!<���x00@��O�?chrome/devtools/modules/devtools/client/inspector/fonts/fonts.jsPK
!<�*�:��P��ݭ?chrome/devtools/modules/devtools/client/inspector/fonts/reducers/font-options.jsPK
!<��L�||I����?chrome/devtools/modules/devtools/client/inspector/fonts/reducers/fonts.jsPK
!<�ö��@��ݳ?chrome/devtools/modules/devtools/client/inspector/fonts/types.jsPK
!<����E���?chrome/devtools/modules/devtools/client/inspector/fonts/utils/l10n.jsPK
!<�..H��8�?chrome/devtools/modules/devtools/client/inspector/grids/actions/grids.jsPK
!<5��.W����?chrome/devtools/modules/devtools/client/inspector/grids/actions/highlighter-settings.jsPK
!<���'@@H��]�?chrome/devtools/modules/devtools/client/inspector/grids/actions/index.jsPK
!<�̪066J���?chrome/devtools/modules/devtools/client/inspector/grids/components/Grid.jsPK
!<;4�a))Y����?chrome/devtools/modules/devtools/client/inspector/grids/components/GridDisplaySettings.jsPK
!<<<^���N��A�?chrome/devtools/modules/devtools/client/inspector/grids/components/GridItem.jsPK
!<ﳘ���N��0�?chrome/devtools/modules/devtools/client/inspector/grids/components/GridList.jsPK
!<
�'��0�0Q��k@chrome/devtools/modules/devtools/client/inspector/grids/components/GridOutline.jsPK
!<���ګa�aI���5@chrome/devtools/modules/devtools/client/inspector/grids/grid-inspector.jsPK
!<��7)JJI����@chrome/devtools/modules/devtools/client/inspector/grids/reducers/grids.jsPK
!<
�J���X��c�@chrome/devtools/modules/devtools/client/inspector/grids/reducers/highlighter-settings.jsPK
!<R~P��@����@chrome/devtools/modules/devtools/client/inspector/grids/types.jsPK
!<��l�F���@chrome/devtools/modules/devtools/client/inspector/grids/utils/utils.jsPK
!<�o��
�
G����@chrome/devtools/modules/devtools/client/inspector/inspector-commands.jsPK
!<�T��F�FE���@chrome/devtools/modules/devtools/client/inspector/inspector-search.jsPK
!<:x����Q���Achrome/devtools/modules/devtools/client/inspector/layout/components/Accordion.cssPK
!<Tw�Yo	o	P��K
Achrome/devtools/modules/devtools/client/inspector/layout/components/Accordion.jsPK
!<��*��P��(Achrome/devtools/modules/devtools/client/inspector/layout/components/LayoutApp.jsPK
!<��4�	
	
B��x$Achrome/devtools/modules/devtools/client/inspector/layout/layout.jsPK
!<�\�9��F���1Achrome/devtools/modules/devtools/client/inspector/layout/utils/l10n.jsPK
!<�&����B���4Achrome/devtools/modules/devtools/client/inspector/markup/markup.jsPK
!<ko��A��#!Bchrome/devtools/modules/devtools/client/inspector/markup/utils.jsPK
!<?�ҶaaS���2Bchrome/devtools/modules/devtools/client/inspector/markup/views/element-container.jsPK
!</9B�XXP��cMBchrome/devtools/modules/devtools/client/inspector/markup/views/element-editor.jsPK
!<�ѡ%��M��ܥBchrome/devtools/modules/devtools/client/inspector/markup/views/html-editor.jsPK
!<k�Y�YR��7�Bchrome/devtools/modules/devtools/client/inspector/markup/views/markup-container.jsPK
!<YO�K��U��@Cchrome/devtools/modules/devtools/client/inspector/markup/views/read-only-container.jsPK
!<���&&R��UCchrome/devtools/modules/devtools/client/inspector/markup/views/read-only-editor.jsPK
!<M���P��� Cchrome/devtools/modules/devtools/client/inspector/markup/views/root-container.jsPK
!<}�b��P��u'Cchrome/devtools/modules/devtools/client/inspector/markup/views/text-container.jsPK
!<g� IM���-Cchrome/devtools/modules/devtools/client/inspector/markup/views/text-editor.jsPK
!<u%?��:��1<Cchrome/devtools/modules/devtools/client/inspector/panel.jsPK
!<[vo��=���>Cchrome/devtools/modules/devtools/client/inspector/reducers.jsPK
!<;�(6(6O��vCCchrome/devtools/modules/devtools/client/inspector/rules/models/element-style.jsPK
!<����HRHRF��zCchrome/devtools/modules/devtools/client/inspector/rules/models/rule.jsPK
!<E�_u��O����Cchrome/devtools/modules/devtools/client/inspector/rules/models/text-property.jsPK
!<,b5D�D�@����Cchrome/devtools/modules/devtools/client/inspector/rules/rules.jsPK
!<>��$�+�+U����Dchrome/devtools/modules/devtools/client/inspector/rules/views/class-list-previewer.jsPK
!<��g`g`L���Dchrome/devtools/modules/devtools/client/inspector/rules/views/rule-editor.jsPK
!<�'�����U���WEchrome/devtools/modules/devtools/client/inspector/rules/views/text-property-editor.jsPK
!<��Y�(�(L��M�Echrome/devtools/modules/devtools/client/inspector/shared/dom-node-preview.jsPK
!<����}q}qP��_Fchrome/devtools/modules/devtools/client/inspector/shared/highlighters-overlay.jsPK
!<���Ŝ�F��J�Fchrome/devtools/modules/devtools/client/inspector/shared/node-types.jsPK
!<T��`�
�
J��J�Fchrome/devtools/modules/devtools/client/inspector/shared/reflow-tracker.jsPK
!<A_88P��6�Fchrome/devtools/modules/devtools/client/inspector/shared/style-inspector-menu.jsPK
!<l��9�,�,L����Fchrome/devtools/modules/devtools/client/inspector/shared/tooltips-overlay.jsPK
!<��M���A����Fchrome/devtools/modules/devtools/client/inspector/shared/utils.jsPK
!<x(�ss:��Gchrome/devtools/modules/devtools/client/inspector/store.jsPK
!<�C�|�$�$@���Gchrome/devtools/modules/devtools/client/inspector/toolsidebar.jsPK
!<�� KKF��9Gchrome/devtools/modules/devtools/client/jsonview/components/Headers.jsPK
!<&k��44K���DGchrome/devtools/modules/devtools/client/jsonview/components/HeadersPanel.jsPK
!<���ؚ�M��fKGchrome/devtools/modules/devtools/client/jsonview/components/HeadersToolbar.jsPK
!<�JQ���H��kQGchrome/devtools/modules/devtools/client/jsonview/components/JsonPanel.jsPK
!<��l��J���bGchrome/devtools/modules/devtools/client/jsonview/components/JsonToolbar.jsPK
!<>kƋ		G���iGchrome/devtools/modules/devtools/client/jsonview/components/LiveText.jsPK
!<��S��M��^oGchrome/devtools/modules/devtools/client/jsonview/components/MainTabbedArea.jsPK
!<#؇�$$H���|Gchrome/devtools/modules/devtools/client/jsonview/components/SearchBox.jsPK
!<�T�rrH��#�Gchrome/devtools/modules/devtools/client/jsonview/components/TextPanel.jsPK
!<��{TTJ����Gchrome/devtools/modules/devtools/client/jsonview/components/TextToolbar.jsPK
!<(a�9��K����Gchrome/devtools/modules/devtools/client/jsonview/components/reps/Toolbar.jsPK
!</�n�['['C����Gchrome/devtools/modules/devtools/client/jsonview/converter-child.jsPK
!<S����F����Gchrome/devtools/modules/devtools/client/jsonview/converter-observer.jsPK
!<fu���@����Gchrome/devtools/modules/devtools/client/jsonview/css/general.cssPK
!<��*��F����Gchrome/devtools/modules/devtools/client/jsonview/css/headers-panel.cssPK
!<�6� 

C��5�Gchrome/devtools/modules/devtools/client/jsonview/css/json-panel.cssPK
!<�Ai�jj=����Gchrome/devtools/modules/devtools/client/jsonview/css/main.cssPK
!<���4>>C��e�Gchrome/devtools/modules/devtools/client/jsonview/css/search-box.cssPK
!<F*��?���Gchrome/devtools/modules/devtools/client/jsonview/css/search.svgPK
!<mmC���Gchrome/devtools/modules/devtools/client/jsonview/css/text-panel.cssPK
!<�'S��@���Gchrome/devtools/modules/devtools/client/jsonview/css/toolbar.cssPK
!<�3.{��?���Hchrome/devtools/modules/devtools/client/jsonview/json-viewer.jsPK
!<�A��D�D?��7Hchrome/devtools/modules/devtools/client/jsonview/lib/require.jsPK
!<���SSA��]Ichrome/devtools/modules/devtools/client/jsonview/viewer-config.jsPK
!<w��]��E���eIchrome/devtools/modules/devtools/client/memory/actions/allocations.jsPK
!<(
�'<<H��iIchrome/devtools/modules/devtools/client/memory/actions/census-display.jsPK
!<ߵ����A���mIchrome/devtools/modules/devtools/client/memory/actions/diffing.jsPK
!<�j�HUU@���Ichrome/devtools/modules/devtools/client/memory/actions/filter.jsPK
!<��yy<����Ichrome/devtools/modules/devtools/client/memory/actions/io.jsPK
!<�M��G����Ichrome/devtools/modules/devtools/client/memory/actions/label-display.jsPK
!<��e���A����Ichrome/devtools/modules/devtools/client/memory/actions/refresh.jsPK
!<d5hb��?����Ichrome/devtools/modules/devtools/client/memory/actions/sizes.jsPK
!<eS6WdWdB��}�Ichrome/devtools/modules/devtools/client/memory/actions/snapshot.jsPK
!<<�́%
%
D��4Jchrome/devtools/modules/devtools/client/memory/actions/task-cache.jsPK
!<zf�ʄ�J���Jchrome/devtools/modules/devtools/client/memory/actions/tree-map-display.jsPK
!<�2q�88>���Jchrome/devtools/modules/devtools/client/memory/actions/view.jsPK
!<�z[��1�15��;Jchrome/devtools/modules/devtools/client/memory/app.jsPK
!<?��[w	w	C��=OJchrome/devtools/modules/devtools/client/memory/components/Census.jsPK
!<9䠙vvI��YJchrome/devtools/modules/devtools/client/memory/components/CensusHeader.jsPK
!<�I��ggK���`Jchrome/devtools/modules/devtools/client/memory/components/CensusTreeItem.jsPK
!<��/��J���vJchrome/devtools/modules/devtools/client/memory/components/DominatorTree.jsPK
!<]:Oe��P���Jchrome/devtools/modules/devtools/client/memory/components/DominatorTreeHeader.jsPK
!<~C9��N��I�Jchrome/devtools/modules/devtools/client/memory/components/DominatorTreeItem.jsPK
!<��T�D7D7A����Jchrome/devtools/modules/devtools/client/memory/components/Heap.jsPK
!<����DDH��'�Jchrome/devtools/modules/devtools/client/memory/components/Individuals.jsPK
!<kvqL��N����Jchrome/devtools/modules/devtools/client/memory/components/IndividualsHeader.jsPK
!<^��V��A���Jchrome/devtools/modules/devtools/client/memory/components/List.jsPK
!<�S�J��0�Jchrome/devtools/modules/devtools/client/memory/components/ShortestPaths.jsPK
!<D�rM���
Kchrome/devtools/modules/devtools/client/memory/components/SnapshotListItem.jsPK
!<}F��$�$D��(Kchrome/devtools/modules/devtools/client/memory/components/Toolbar.jsPK
!<��4o~~D��+>Kchrome/devtools/modules/devtools/client/memory/components/TreeMap.jsPK
!<Ӡ�R��FKchrome/devtools/modules/devtools/client/memory/components/tree-map/canvas-utils.jsPK
!< �W��|UKchrome/devtools/modules/devtools/client/memory/components/tree-map/color-coarse-type.jsPK
!<M�`2((O��	\Kchrome/devtools/modules/devtools/client/memory/components/tree-map/drag-zoom.jsPK
!<��k�?&?&J����Kchrome/devtools/modules/devtools/client/memory/components/tree-map/draw.jsPK
!<��K��3�Kchrome/devtools/modules/devtools/client/memory/components/tree-map/start.jsPK
!<v�u��-�-;����Kchrome/devtools/modules/devtools/client/memory/constants.jsPK
!<�a���N����Kchrome/devtools/modules/devtools/client/memory/dominator-tree-lazy-children.jsPK
!<���TCTC8����Kchrome/devtools/modules/devtools/client/memory/models.jsPK
!<�9@���7���(Lchrome/devtools/modules/devtools/client/memory/panel.jsPK
!<x�����F���0Lchrome/devtools/modules/devtools/client/memory/reducers/allocations.jsPK
!<&�q���I��6Lchrome/devtools/modules/devtools/client/memory/reducers/census-display.jsPK
!<��_��B��	9Lchrome/devtools/modules/devtools/client/memory/reducers/diffing.jsPK
!<}N�WWA��MLchrome/devtools/modules/devtools/client/memory/reducers/errors.jsPK
!<^�u��A���OLchrome/devtools/modules/devtools/client/memory/reducers/filter.jsPK
!<�%���F���QLchrome/devtools/modules/devtools/client/memory/reducers/individuals.jsPK
!<j'ttH��?[Lchrome/devtools/modules/devtools/client/memory/reducers/label-display.jsPK
!<R����@��^Lchrome/devtools/modules/devtools/client/memory/reducers/sizes.jsPK
!<�x`�_7_7D��aLchrome/devtools/modules/devtools/client/memory/reducers/snapshots.jsPK
!<ќ�i��K��ߘLchrome/devtools/modules/devtools/client/memory/reducers/tree-map-display.jsPK
!<.E��--?��ɛLchrome/devtools/modules/devtools/client/memory/reducers/view.jsPK
!<A�)J%%:��S�Lchrome/devtools/modules/devtools/client/memory/reducers.jsPK
!<���xx7��ФLchrome/devtools/modules/devtools/client/memory/store.jsPK
!<��HW�:�:7����Lchrome/devtools/modules/devtools/client/memory/utils.jsPK
!<챸�880����Lchrome/devtools/modules/devtools/client/menus.jsPK
!<�~**;��/�Lchrome/devtools/modules/devtools/client/netmonitor/panel.jsPK
!<��//J����Lchrome/devtools/modules/devtools/client/netmonitor/src/actions/batching.jsPK
!<9�q�uuI��I�Lchrome/devtools/modules/devtools/client/netmonitor/src/actions/filters.jsPK
!<��l~UUG��%Mchrome/devtools/modules/devtools/client/netmonitor/src/actions/index.jsPK
!<�tdA#	#	J���Mchrome/devtools/modules/devtools/client/netmonitor/src/actions/requests.jsPK
!<�*CrrK��jMchrome/devtools/modules/devtools/client/netmonitor/src/actions/selection.jsPK
!<D�Q&yyF��EMchrome/devtools/modules/devtools/client/netmonitor/src/actions/sort.jsPK
!<����P��"Mchrome/devtools/modules/devtools/client/netmonitor/src/actions/timing-markers.jsPK
!<�����D��uMchrome/devtools/modules/devtools/client/netmonitor/src/actions/ui.jsPK
!<h'J���H���,Mchrome/devtools/modules/devtools/client/netmonitor/src/components/App.jsPK
!<�"na�
�
Q���5Mchrome/devtools/modules/devtools/client/netmonitor/src/components/CookiesPanel.jsPK
!<׬(&H$H$W��)DMchrome/devtools/modules/devtools/client/netmonitor/src/components/CustomRequestPanel.jsPK
!<ֽÐ��S���hMchrome/devtools/modules/devtools/client/netmonitor/src/components/DropHarHandler.jsPK
!<&��%�%Q��IvMchrome/devtools/modules/devtools/client/netmonitor/src/components/HeadersPanel.jsPK
!<�ԗ��P��v�Mchrome/devtools/modules/devtools/client/netmonitor/src/components/HtmlPreview.jsPK
!<h�2)++L����Mchrome/devtools/modules/devtools/client/netmonitor/src/components/MdnLink.jsPK
!<��o���Q���Mchrome/devtools/modules/devtools/client/netmonitor/src/components/MonitorPanel.jsPK
!<CU
���X��;�Mchrome/devtools/modules/devtools/client/netmonitor/src/components/NetworkDetailsPanel.jsPK
!<i���P��f�Mchrome/devtools/modules/devtools/client/netmonitor/src/components/ParamsPanel.jsPK
!<�7k�22S����Mchrome/devtools/modules/devtools/client/netmonitor/src/components/PropertiesView.jsPK
!<�j
���P��+�Mchrome/devtools/modules/devtools/client/netmonitor/src/components/RequestList.jsPK
!<8� ��[����Mchrome/devtools/modules/devtools/client/netmonitor/src/components/RequestListColumnCause.jsPK
!<�
BEEa���Mchrome/devtools/modules/devtools/client/netmonitor/src/components/RequestListColumnContentSize.jsPK
!<7��Pii]���Nchrome/devtools/modules/devtools/client/netmonitor/src/components/RequestListColumnCookies.jsPK
!<@���\��yNchrome/devtools/modules/devtools/client/netmonitor/src/components/RequestListColumnDomain.jsPK
!<���{{Z���Nchrome/devtools/modules/devtools/client/netmonitor/src/components/RequestListColumnFile.jsPK
!<��M�\���Nchrome/devtools/modules/devtools/client/netmonitor/src/components/RequestListColumnMethod.jsPK
!<~�ԪII^���Nchrome/devtools/modules/devtools/client/netmonitor/src/components/RequestListColumnProtocol.jsPK
!<,�X��^���"Nchrome/devtools/modules/devtools/client/netmonitor/src/components/RequestListColumnRemoteIP.jsPK
!<
Y���d���'Nchrome/devtools/modules/devtools/client/netmonitor/src/components/RequestListColumnResponseHeader.jsPK
!<yu�!��\��/Nchrome/devtools/modules/devtools/client/netmonitor/src/components/RequestListColumnScheme.jsPK
!<���_��`��r3Nchrome/devtools/modules/devtools/client/netmonitor/src/components/RequestListColumnSetCookies.jsPK
!<rQ;G^
^
\��z;Nchrome/devtools/modules/devtools/client/netmonitor/src/components/RequestListColumnStatus.jsPK
!<�\�	�	Z��RFNchrome/devtools/modules/devtools/client/netmonitor/src/components/RequestListColumnTime.jsPK
!<Vi�-,,e���PNchrome/devtools/modules/devtools/client/netmonitor/src/components/RequestListColumnTransferredSize.jsPK
!<�yssZ��sXNchrome/devtools/modules/devtools/client/netmonitor/src/components/RequestListColumnType.jsPK
!<�an���_��^]Nchrome/devtools/modules/devtools/client/netmonitor/src/components/RequestListColumnWaterfall.jsPK
!<�����-�-W���mNchrome/devtools/modules/devtools/client/netmonitor/src/components/RequestListContent.jsPK
!<LY{}��[���Nchrome/devtools/modules/devtools/client/netmonitor/src/components/RequestListEmptyNotice.jsPK
!<k%���V����Nchrome/devtools/modules/devtools/client/netmonitor/src/components/RequestListHeader.jsPK
!<��&c#c#T����Nchrome/devtools/modules/devtools/client/netmonitor/src/components/RequestListItem.jsPK
!<��L��R����Nchrome/devtools/modules/devtools/client/netmonitor/src/components/ResponsePanel.jsPK
!<J}פR���Ochrome/devtools/modules/devtools/client/netmonitor/src/components/SecurityPanel.jsPK
!<�o�Q��Z"Ochrome/devtools/modules/devtools/client/netmonitor/src/components/SourceEditor.jsPK
!<��AAT���-Ochrome/devtools/modules/devtools/client/netmonitor/src/components/StackTracePanel.jsPK
!<��I�,,T���6Ochrome/devtools/modules/devtools/client/netmonitor/src/components/StatisticsPanel.jsPK
!<�9�_,,N��!cOchrome/devtools/modules/devtools/client/netmonitor/src/components/StatusBar.jsPK
!<�ؾW��P���rOchrome/devtools/modules/devtools/client/netmonitor/src/components/TabboxPanel.jsPK
!<T%N���Q����Ochrome/devtools/modules/devtools/client/netmonitor/src/components/TimingsPanel.jsPK
!<^?)�Y+Y+L����Ochrome/devtools/modules/devtools/client/netmonitor/src/components/Toolbar.jsPK
!<�U�G00U����Ochrome/devtools/modules/devtools/client/netmonitor/src/connector/firefox-connector.jsPK
!<d
TVMMY��0�Ochrome/devtools/modules/devtools/client/netmonitor/src/connector/firefox-data-provider.jsPK
!<�I I���8Pchrome/devtools/modules/devtools/client/netmonitor/src/connector/index.jsPK
!<ڃ��d!d!C��/EPchrome/devtools/modules/devtools/client/netmonitor/src/constants.jsPK
!<�/��F���fPchrome/devtools/modules/devtools/client/netmonitor/src/create-store.jsPK
!<�QQ���L��pPchrome/devtools/modules/devtools/client/netmonitor/src/har/har-automation.jsPK
!<�5�zzO���Pchrome/devtools/modules/devtools/client/netmonitor/src/har/har-builder-utils.jsPK
!<��,g�<�<I����Pchrome/devtools/modules/devtools/client/netmonitor/src/har/har-builder.jsPK
!<�k���3�3K��-�Pchrome/devtools/modules/devtools/client/netmonitor/src/har/har-collector.jsPK
!<�wf��J��PQchrome/devtools/modules/devtools/client/netmonitor/src/har/har-exporter.jsPK
!<W����J��HQchrome/devtools/modules/devtools/client/netmonitor/src/har/har-importer.jsPK
!<=�σG���-Qchrome/devtools/modules/devtools/client/netmonitor/src/har/har-utils.jsPK
!<�����M��BQchrome/devtools/modules/devtools/client/netmonitor/src/har/toolbox-overlay.jsPK
!<��^��M��-KQchrome/devtools/modules/devtools/client/netmonitor/src/middleware/batching.jsPK
!<�B�>>J��VWQchrome/devtools/modules/devtools/client/netmonitor/src/middleware/prefs.jsPK
!<ň'%��N���^Qchrome/devtools/modules/devtools/client/netmonitor/src/middleware/recording.jsPK
!<�F���J��CcQchrome/devtools/modules/devtools/client/netmonitor/src/middleware/thunk.jsPK
!<%�b�K��-fQchrome/devtools/modules/devtools/client/netmonitor/src/reducers/batching.jsPK
!<��#+	+	J���iQchrome/devtools/modules/devtools/client/netmonitor/src/reducers/filters.jsPK
!<�Ŭ���H��,sQchrome/devtools/modules/devtools/client/netmonitor/src/reducers/index.jsPK
!<Z�?A��K��fvQchrome/devtools/modules/devtools/client/netmonitor/src/reducers/requests.jsPK
!<T��@@G����Qchrome/devtools/modules/devtools/client/netmonitor/src/reducers/sort.jsPK
!<
Y�Z@@Q��&�Qchrome/devtools/modules/devtools/client/netmonitor/src/reducers/timing-markers.jsPK
!<މk�88E��՗Qchrome/devtools/modules/devtools/client/netmonitor/src/reducers/ui.jsPK
!<��"��I��p�Qchrome/devtools/modules/devtools/client/netmonitor/src/selectors/index.jsPK
!<[����L��h�Qchrome/devtools/modules/devtools/client/netmonitor/src/selectors/requests.jsPK
!<�|����R����Qchrome/devtools/modules/devtools/client/netmonitor/src/selectors/timing-markers.jsPK
!<Q%cۀ�F���Qchrome/devtools/modules/devtools/client/netmonitor/src/selectors/ui.jsPK
!<����\����Qchrome/devtools/modules/devtools/client/netmonitor/src/utils/filter-autocomplete-provider.jsPK
!<4Ȉ99Q��@�Qchrome/devtools/modules/devtools/client/netmonitor/src/utils/filter-predicates.jsPK
!<^����Q����Qchrome/devtools/modules/devtools/client/netmonitor/src/utils/filter-text-utils.jsPK
!<���11[��(Rchrome/devtools/modules/devtools/client/netmonitor/src/utils/firefox/open-request-in-tab.jsPK
!<^s���L���Rchrome/devtools/modules/devtools/client/netmonitor/src/utils/format-utils.jsPK
!<���sP���Rchrome/devtools/modules/devtools/client/netmonitor/src/utils/headers-provider.jsPK
!<eGp`��D���Rchrome/devtools/modules/devtools/client/netmonitor/src/utils/l10n.jsPK
!<d�I���Rchrome/devtools/modules/devtools/client/netmonitor/src/utils/mdn-utils.jsPK
!<-�zBnnD��+1Rchrome/devtools/modules/devtools/client/netmonitor/src/utils/menu.jsPK
!<h1|
S���4Rchrome/devtools/modules/devtools/client/netmonitor/src/utils/open-request-in-tab.jsPK
!<�4��qqE���:Rchrome/devtools/modules/devtools/client/netmonitor/src/utils/prefs.jsPK
!<��IA�7�7M��_=Rchrome/devtools/modules/devtools/client/netmonitor/src/utils/request-utils.jsPK
!<��^v66O���uRchrome/devtools/modules/devtools/client/netmonitor/src/utils/sort-predicates.jsPK
!<0���\\J��D�Rchrome/devtools/modules/devtools/client/netmonitor/src/utils/sort-utils.jsPK
!<D]�E88X���Rchrome/devtools/modules/devtools/client/netmonitor/src/widgets/RequestListContextMenu.jsPK
!<���	�	^����Rchrome/devtools/modules/devtools/client/netmonitor/src/widgets/RequestListHeaderContextMenu.jsPK
!<�
�ooU����Rchrome/devtools/modules/devtools/client/netmonitor/src/widgets/WaterfallBackground.jsPK
!<�B��X����Rchrome/devtools/modules/devtools/client/performance/components/jit-optimizations-item.jsPK
!<G�zllS���Schrome/devtools/modules/devtools/client/performance/components/jit-optimizations.jsPK
!<��/�**R���!Schrome/devtools/modules/devtools/client/performance/components/recording-button.jsPK
!<]h���T��,'Schrome/devtools/modules/devtools/client/performance/components/recording-controls.jsPK
!<#��w��U��~/Schrome/devtools/modules/devtools/client/performance/components/recording-list-item.jsPK
!<��e.P���7Schrome/devtools/modules/devtools/client/performance/components/recording-list.jsPK
!<��==R���;Schrome/devtools/modules/devtools/client/performance/components/waterfall-header.jsPK
!<y�8^$$T���DSchrome/devtools/modules/devtools/client/performance/components/waterfall-tree-row.jsPK
!<&b���P��BQSchrome/devtools/modules/devtools/client/performance/components/waterfall-tree.jsPK
!<�
����K���dSchrome/devtools/modules/devtools/client/performance/components/waterfall.jsPK
!<iP�9��=���iSchrome/devtools/modules/devtools/client/performance/events.jsPK
!<'Gr��I���|Schrome/devtools/modules/devtools/client/performance/modules/categories.jsPK
!<��WWH����Schrome/devtools/modules/devtools/client/performance/modules/constants.jsPK
!<����QQE����Schrome/devtools/modules/devtools/client/performance/modules/global.jsPK
!<�,O��A��i�Schrome/devtools/modules/devtools/client/performance/modules/io.jsPK
!<^�r�>>P��ƩSchrome/devtools/modules/devtools/client/performance/modules/logic/frame-utils.jsPK
!<�т.�0�0H��P�Schrome/devtools/modules/devtools/client/performance/modules/logic/jit.jsPK
!<��sN���Tchrome/devtools/modules/devtools/client/performance/modules/logic/telemetry.jsPK
!<g�.1I1IO��)Tchrome/devtools/modules/devtools/client/performance/modules/logic/tree-model.jsPK
!<IeUppT���rTchrome/devtools/modules/devtools/client/performance/modules/logic/waterfall-utils.jsPK
!<�o!�
�
U����Tchrome/devtools/modules/devtools/client/performance/modules/marker-blueprint-utils.jsPK
!<դ"��O����Tchrome/devtools/modules/devtools/client/performance/modules/marker-dom-utils.jsPK
!<�w��OOP����Tchrome/devtools/modules/devtools/client/performance/modules/marker-formatters.jsPK
!<��׃��F��m�Tchrome/devtools/modules/devtools/client/performance/modules/markers.jsPK
!<M�����D����Tchrome/devtools/modules/devtools/client/performance/modules/utils.jsPK
!<�c��N����Tchrome/devtools/modules/devtools/client/performance/modules/waterfall-ticks.jsPK
!<3��;�;M���Tchrome/devtools/modules/devtools/client/performance/modules/widgets/graphs.jsPK
!<Ug;�U��'1Uchrome/devtools/modules/devtools/client/performance/modules/widgets/marker-details.jsPK
!<8�pqS S W���BUchrome/devtools/modules/devtools/client/performance/modules/widgets/markers-overview.jsPK
!<��á�8�8P��{cUchrome/devtools/modules/devtools/client/performance/modules/widgets/tree-view.jsPK
!<ᕮ��<����Uchrome/devtools/modules/devtools/client/performance/panel.jsPK
!<]�<4��K��ީUchrome/devtools/modules/devtools/client/performance/test/helpers/actions.jsPK
!<�I�Y��M��/�Uchrome/devtools/modules/devtools/client/performance/test/helpers/dom-utils.jsPK
!<[�6}}O��j�Uchrome/devtools/modules/devtools/client/performance/test/helpers/event-utils.jsPK
!<�Q*��O��T�Uchrome/devtools/modules/devtools/client/performance/test/helpers/input-utils.jsPK
!<�n��O��s�Uchrome/devtools/modules/devtools/client/performance/test/helpers/panel-utils.jsPK
!<��VI����Uchrome/devtools/modules/devtools/client/performance/test/helpers/prefs.jsPK
!<�6�ZnnU���Uchrome/devtools/modules/devtools/client/performance/test/helpers/profiler-mm-utils.jsPK
!<��=�11S���Vchrome/devtools/modules/devtools/client/performance/test/helpers/recording-utils.jsPK
!<O��I	
	
O���
Vchrome/devtools/modules/devtools/client/performance/test/helpers/synth-utils.jsPK
!<�e9��M��Vchrome/devtools/modules/devtools/client/performance/test/helpers/tab-utils.jsPK
!<a�V�H��xVchrome/devtools/modules/devtools/client/performance/test/helpers/urls.jsPK
!<�_v;��N��� Vchrome/devtools/modules/devtools/client/performance/test/helpers/wait-utils.jsPK
!<�rA[�-�-J��J(Vchrome/devtools/modules/devtools/client/performance-new/components/Perf.jsPK
!<��eG��@���VVchrome/devtools/modules/devtools/client/performance-new/panel.jsPK
!<H��p��J���\Vchrome/devtools/modules/devtools/client/responsive.html/actions/devices.jsPK
!<���Ŝ�V���oVchrome/devtools/modules/devtools/client/responsive.html/actions/display-pixel-ratio.jsPK
!<ÿH�	�	H���rVchrome/devtools/modules/devtools/client/responsive.html/actions/index.jsPK
!<P#*U!!K��B}Vchrome/devtools/modules/devtools/client/responsive.html/actions/location.jsPK
!<me8��U���Vchrome/devtools/modules/devtools/client/responsive.html/actions/network-throttling.jsPK
!<X=�E��T����Vchrome/devtools/modules/devtools/client/responsive.html/actions/reload-conditions.jsPK
!<o�S��	�	M��i�Vchrome/devtools/modules/devtools/client/responsive.html/actions/screenshot.jsPK
!<�
�!��S��\�Vchrome/devtools/modules/devtools/client/responsive.html/actions/touch-simulation.jsPK
!<�[���L����Vchrome/devtools/modules/devtools/client/responsive.html/actions/viewports.jsPK
!<�5��>����Vchrome/devtools/modules/devtools/client/responsive.html/app.jsPK
!<�_�PPJ����Vchrome/devtools/modules/devtools/client/responsive.html/browser/content.jsPK
!<1yNY@Y@G����Vchrome/devtools/modules/devtools/client/responsive.html/browser/swap.jsPK
!<��.;|\|\I��kWchrome/devtools/modules/devtools/client/responsive.html/browser/tunnel.jsPK
!<���C55Q��NoWchrome/devtools/modules/devtools/client/responsive.html/browser/web-navigation.jsPK
!<�����C���Wchrome/devtools/modules/devtools/client/responsive.html/commands.jsPK
!<l>�qM����Wchrome/devtools/modules/devtools/client/responsive.html/components/Browser.jsPK
!<XvG���Q����Wchrome/devtools/modules/devtools/client/responsive.html/components/DeviceAdder.jsPK
!<G�`��Q����Wchrome/devtools/modules/devtools/client/responsive.html/components/DeviceModal.jsPK
!<���

^���Wchrome/devtools/modules/devtools/client/responsive.html/components/DevicePixelRatioSelector.jsPK
!<��
�
T����Wchrome/devtools/modules/devtools/client/responsive.html/components/DeviceSelector.jsPK
!<�S���Wchrome/devtools/modules/devtools/client/responsive.html/components/GlobalToolbar.jsPK
!<}�n�r	r	_��[Xchrome/devtools/modules/devtools/client/responsive.html/components/NetworkThrottlingSelector.jsPK
!<�r�3��V��JXchrome/devtools/modules/devtools/client/responsive.html/components/ReloadConditions.jsPK
!<�#�W���Xchrome/devtools/modules/devtools/client/responsive.html/components/ResizableViewport.jsPK
!<�l���
�
P��,Xchrome/devtools/modules/devtools/client/responsive.html/components/ToggleMenu.jsPK
!<)k�m++N��7Xchrome/devtools/modules/devtools/client/responsive.html/components/Viewport.jsPK
!<�<b���W���CXchrome/devtools/modules/devtools/client/responsive.html/components/ViewportDimension.jsPK
!<ݘ»�U���YXchrome/devtools/modules/devtools/client/responsive.html/components/ViewportToolbar.jsPK
!<x��q��O���`Xchrome/devtools/modules/devtools/client/responsive.html/components/Viewports.jsPK
!<v�,,D��*iXchrome/devtools/modules/devtools/client/responsive.html/constants.jsPK
!<cO�ڈ�K���jXchrome/devtools/modules/devtools/client/responsive.html/images/grippers.svgPK
!<9���vvR���lXchrome/devtools/modules/devtools/client/responsive.html/images/rotate-viewport.svgPK
!<ȭ9��M���pXchrome/devtools/modules/devtools/client/responsive.html/images/screenshot.svgPK
!<��EEO���sXchrome/devtools/modules/devtools/client/responsive.html/images/select-arrow.svgPK
!<x�̄��O���vXchrome/devtools/modules/devtools/client/responsive.html/images/touch-events.svgPK
!<BB��d0d0A���zXchrome/devtools/modules/devtools/client/responsive.html/index.cssPK
!<h\dOdOB��o�Xchrome/devtools/modules/devtools/client/responsive.html/manager.jsPK
!<|��sH	H	K��3�Xchrome/devtools/modules/devtools/client/responsive.html/reducers/devices.jsPK
!<ݕ1���W���Ychrome/devtools/modules/devtools/client/responsive.html/reducers/display-pixel-ratio.jsPK
!<#��TTL��
Ychrome/devtools/modules/devtools/client/responsive.html/reducers/location.jsPK
!<�¨_��V���
Ychrome/devtools/modules/devtools/client/responsive.html/reducers/network-throttling.jsPK
!<���x��U��Ychrome/devtools/modules/devtools/client/responsive.html/reducers/reload-conditions.jsPK
!<|�ޞ00N��PYchrome/devtools/modules/devtools/client/responsive.html/reducers/screenshot.jsPK
!<ޭ=o��T���Ychrome/devtools/modules/devtools/client/responsive.html/reducers/touch-simulation.jsPK
!<�\��	�	M��0Ychrome/devtools/modules/devtools/client/responsive.html/reducers/viewports.jsPK
!<��[6��C��h#Ychrome/devtools/modules/devtools/client/responsive.html/reducers.jsPK
!<�W3Q��I���&Ychrome/devtools/modules/devtools/client/responsive.html/responsive-ua.cssPK
!<��m[[@���'Ychrome/devtools/modules/devtools/client/responsive.html/store.jsPK
!<Q�JX99@��?+Ychrome/devtools/modules/devtools/client/responsive.html/types.jsPK
!<��b���D���;Ychrome/devtools/modules/devtools/client/responsive.html/utils/css.jsPK
!<�'�hhE���>Ychrome/devtools/modules/devtools/client/responsive.html/utils/e10s.jsPK
!<��h�$$D���KYchrome/devtools/modules/devtools/client/responsive.html/utils/key.jsPK
!<]�y��E��<OYchrome/devtools/modules/devtools/client/responsive.html/utils/l10n.jsPK
!<_t�wwH��DRYchrome/devtools/modules/devtools/client/responsive.html/utils/message.jsPK
!<P�*1��M��!XYchrome/devtools/modules/devtools/client/responsive.html/utils/notification.jsPK
!<��==G��`Ychrome/devtools/modules/devtools/client/responsive.html/utils/window.jsPK
!<>Heh��I���fYchrome/devtools/modules/devtools/client/scratchpad/scratchpad-commands.jsPK
!<�ٰSI���iYchrome/devtools/modules/devtools/client/scratchpad/scratchpad-manager.jsmPK
!<�E�Q��F��N|Ychrome/devtools/modules/devtools/client/scratchpad/scratchpad-panel.jsPK
!<F�$0��=����Ychrome/devtools/modules/devtools/client/shadereditor/panel.jsPK
!<��=�xKxK@��ԋYchrome/devtools/modules/devtools/client/shared/AppCacheUtils.jsmPK
!<2�����=����Ychrome/devtools/modules/devtools/client/shared/DOMHelpers.jsmPK
!<����$�$<����Ychrome/devtools/modules/devtools/client/shared/SplitView.jsmPK
!<8W^�qCqCD���Zchrome/devtools/modules/devtools/client/shared/autocomplete-popup.jsPK
!<D�?!!@���TZchrome/devtools/modules/devtools/client/shared/browser-loader.jsPK
!<z��kkN���uZchrome/devtools/modules/devtools/client/shared/components/AutoCompletePopup.jsPK
!<�&��B��ˆZchrome/devtools/modules/devtools/client/shared/components/Frame.jsPK
!<�/���F��֦Zchrome/devtools/modules/devtools/client/shared/components/HSplitBox.jsPK
!<g��%��M���Zchrome/devtools/modules/devtools/client/shared/components/NotificationBox.cssPK
!<�/�Q Q L��`�Zchrome/devtools/modules/devtools/client/shared/components/NotificationBox.jsPK
!<
q/�>>F���Zchrome/devtools/modules/devtools/client/shared/components/SearchBox.jsPK
!<BaD���K����Zchrome/devtools/modules/devtools/client/shared/components/SidebarToggle.cssPK
!<g��GGJ����Zchrome/devtools/modules/devtools/client/shared/components/SidebarToggle.jsPK
!<��S	S	G��e[chrome/devtools/modules/devtools/client/shared/components/StackTrace.jsPK
!<,��=b=bL��
[chrome/devtools/modules/devtools/client/shared/components/VirtualizedTree.jsPK
!<�v��^^N���o[chrome/devtools/modules/devtools/client/shared/components/VisibilityHandler.jsPK
!<o�����G���u[chrome/devtools/modules/devtools/client/shared/components/reps/reps.cssPK
!<Cm��e�e�F����[chrome/devtools/modules/devtools/client/shared/components/reps/reps.jsPK
!<�XQQO��(^chrome/devtools/modules/devtools/client/shared/components/splitter/Draggable.jsPK
!<믮[��O��=0^chrome/devtools/modules/devtools/client/shared/components/splitter/SplitBox.cssPK
!<�ǦE��N��^9^chrome/devtools/modules/devtools/client/shared/components/splitter/SplitBox.jsPK
!<�8CgLLI��UY^chrome/devtools/modules/devtools/client/shared/components/tabs/TabBar.cssPK
!<)$�|""H��_^chrome/devtools/modules/devtools/client/shared/components/tabs/TabBar.jsPK
!<pz���G���z^chrome/devtools/modules/devtools/client/shared/components/tabs/Tabs.cssPK
!<�@;��2�2F����^chrome/devtools/modules/devtools/client/shared/components/tabs/Tabs.jsPK
!<�s�
qqK����^chrome/devtools/modules/devtools/client/shared/components/tree/LabelCell.jsPK
!<����P����^chrome/devtools/modules/devtools/client/shared/components/tree/ObjectProvider.jsPK
!<!:3�J����^chrome/devtools/modules/devtools/client/shared/components/tree/TreeCell.jsPK
!<
�OwwL��*�^chrome/devtools/modules/devtools/client/shared/components/tree/TreeHeader.jsPK
!<�t�xxI���^chrome/devtools/modules/devtools/client/shared/components/tree/TreeRow.jsPK
!<��-p��K���_chrome/devtools/modules/devtools/client/shared/components/tree/TreeView.cssPK
!<�N
YDYDJ��9_chrome/devtools/modules/devtools/client/shared/components/tree/TreeView.jsPK
!<Gl��b$b$;���__chrome/devtools/modules/devtools/client/shared/css-angle.jsPK
!<"�G�P3P36����_chrome/devtools/modules/devtools/client/shared/curl.jsPK
!<��f
;
;:��Y�_chrome/devtools/modules/devtools/client/shared/demangle.jsPK
!<��a�<�<�C����cchrome/devtools/modules/devtools/client/shared/developer-toolbar.jsPK
!<P�vJ9��X�dchrome/devtools/modules/devtools/client/shared/devices.jsPK
!<k�J��<����dchrome/devtools/modules/devtools/client/shared/doorhanger.jsPK
!<�q556���dchrome/devtools/modules/devtools/client/shared/enum.jsPK
!<[OͶ�<��z�dchrome/devtools/modules/devtools/client/shared/file-saver.jsPK
!<���*��9����dchrome/devtools/modules/devtools/client/shared/getjson.jsPK
!<�)7�����@����dchrome/devtools/modules/devtools/client/shared/inplace-editor.jsPK
!<G]�I%%?��֖echrome/devtools/modules/devtools/client/shared/key-shortcuts.jsPK
!<Y���
�
:��X�echrome/devtools/modules/devtools/client/shared/keycodes.jsPK
!<��`�M
M
>��U�echrome/devtools/modules/devtools/client/shared/natural-sort.jsPK
!<v!����M����echrome/devtools/modules/devtools/client/shared/network-throttling-profiles.jsPK
!<"⶛�3�3G���echrome/devtools/modules/devtools/client/shared/node-attribute-parser.jsPK
!<>��>	fchrome/devtools/modules/devtools/client/shared/options-view.jsPK
!<�R������?���!fchrome/devtools/modules/devtools/client/shared/output-parser.jsPK
!<�R��8����fchrome/devtools/modules/devtools/client/shared/poller.jsPK
!<J�3'AA7���gchrome/devtools/modules/devtools/client/shared/prefs.jsPK
!<-�66=��Mgchrome/devtools/modules/devtools/client/shared/react-utils.jsPK
!<XY���D��� gchrome/devtools/modules/devtools/client/shared/redux/create-store.jsPK
!<�Ĕo�	�	K���'gchrome/devtools/modules/devtools/client/shared/redux/middleware/debounce.jsPK
!<�>*�]]J���1gchrome/devtools/modules/devtools/client/shared/redux/middleware/history.jsPK
!<b(1�55F���5gchrome/devtools/modules/devtools/client/shared/redux/middleware/log.jsPK
!<Fh#�PPJ��<8gchrome/devtools/modules/devtools/client/shared/redux/middleware/promise.jsPK
!<q��κ�G���>gchrome/devtools/modules/devtools/client/shared/redux/middleware/task.jsPK
!<G���H��Egchrome/devtools/modules/devtools/client/shared/redux/middleware/thunk.jsPK
!<_���O���Ggchrome/devtools/modules/devtools/client/shared/redux/middleware/wait-service.jsPK
!<�;?��L��BPgchrome/devtools/modules/devtools/client/shared/redux/non-react-subscriber.jsPK
!<W�nR���dgchrome/devtools/modules/devtools/client/shared/redux/visibility-handler-connect.jsPK
!<��|�``8��igchrome/devtools/modules/devtools/client/shared/scroll.jsPK
!<�@Rw"R"RB���qgchrome/devtools/modules/devtools/client/shared/source-map/index.jsPK
!<�96
�
�C��U�gchrome/devtools/modules/devtools/client/shared/source-map/worker.jsPK
!<�4D<P.P.>���`kchrome/devtools/modules/devtools/client/shared/source-utils.jsPK
!<���J))B��o�kchrome/devtools/modules/devtools/client/shared/stylesheet-utils.jsPK
!<H����C����kchrome/devtools/modules/devtools/client/shared/suggestion-picker.jsPK
!<��A�.�.;��٦kchrome/devtools/modules/devtools/client/shared/telemetry.jsPK
!<�����7����kchrome/devtools/modules/devtools/client/shared/theme.jsPK
!<�����6���kchrome/devtools/modules/devtools/client/shared/undo.jsPK
!<��c}�}�@����kchrome/devtools/modules/devtools/client/shared/vendor/WasmDis.jsPK
!<̛��CCC��Țlchrome/devtools/modules/devtools/client/shared/vendor/WasmParser.jsPK
!<pѤq�.�.B��7�mchrome/devtools/modules/devtools/client/shared/vendor/immutable.jsPK
!<?���=�m
pchrome/devtools/modules/devtools/client/shared/vendor/jsol.jsPK
!<�X�
����>��Wpchrome/devtools/modules/devtools/client/shared/vendor/jszip.jsPK
!<�k���;�;?��o�uchrome/devtools/modules/devtools/client/shared/vendor/lodash.jsPK
!<�p���L����}chrome/devtools/modules/devtools/client/shared/vendor/react-dom-factories.jsPK
!<������I���~chrome/devtools/modules/devtools/client/shared/vendor/react-dom-server.jsPK
!<>������M����~chrome/devtools/modules/devtools/client/shared/vendor/react-dom-test-utils.jsPK
!<j�����B���Rchrome/devtools/modules/devtools/client/shared/vendor/react-dom.jsPK
!<7��5��I����chrome/devtools/modules/devtools/client/shared/vendor/react-prop-types.jsPK
!<�[w-N N D��.��chrome/devtools/modules/devtools/client/shared/vendor/react-redux.jsPK
!<���1�1T��ަ�chrome/devtools/modules/devtools/client/shared/vendor/react-test-renderer-shallow.jsPK
!<r��m2z2z>��وchrome/devtools/modules/devtools/client/shared/vendor/react.jsPK
!<��E�zhzh>���S�chrome/devtools/modules/devtools/client/shared/vendor/redux.jsPK
!<�a7�SSA��j��chrome/devtools/modules/devtools/client/shared/vendor/reselect.jsPK
!<�wM�1�1K��ωchrome/devtools/modules/devtools/client/shared/vendor/seamless-immutable.jsPK
!<��|�22T��(�chrome/devtools/modules/devtools/client/shared/vendor/stringvalidator/util/assert.jsPK
!<.��p�p�R����chrome/devtools/modules/devtools/client/shared/vendor/stringvalidator/validator.jsPK
!<S���!!=����chrome/devtools/modules/devtools/client/shared/view-source.jsPK
!< �`�=��(�chrome/devtools/modules/devtools/client/shared/webgl-utils.jsPK
!<��c]��W���
�chrome/devtools/modules/devtools/client/shared/webpack/shims/platform-clipboard-stub.jsPK
!<X�H���S���
�chrome/devtools/modules/devtools/client/shared/webpack/shims/platform-stack-stub.jsPK
!<���wHwHK����chrome/devtools/modules/devtools/client/shared/widgets/AbstractTreeItem.jsmPK
!< 8?l=l=H���\�chrome/devtools/modules/devtools/client/shared/widgets/BarGraphWidget.jsPK
!<k��L�����chrome/devtools/modules/devtools/client/shared/widgets/BreadcrumbsWidget.jsmPK
!<d)ҥ?�??����chrome/devtools/modules/devtools/client/shared/widgets/Chart.jsPK
!<�Y\�V�VE����chrome/devtools/modules/devtools/client/shared/widgets/ColorWidget.jsPK
!<-
�$��L��O�chrome/devtools/modules/devtools/client/shared/widgets/CubicBezierPresets.jsPK
!<Ʒ�}g}gK��*X�chrome/devtools/modules/devtools/client/shared/widgets/CubicBezierWidget.jsPK
!<��9�H����chrome/devtools/modules/devtools/client/shared/widgets/FastListWidget.jsPK
!<	d��|�|F���یchrome/devtools/modules/devtools/client/shared/widgets/FilterWidget.jsPK
!<ҌGd����D���X�chrome/devtools/modules/devtools/client/shared/widgets/FlameGraph.jsPK
!<ւ�A����@��$�chrome/devtools/modules/devtools/client/shared/widgets/Graphs.jsPK
!<c |��F����chrome/devtools/modules/devtools/client/shared/widgets/GraphsWorker.jsPK
!<8��77I��_͎chrome/devtools/modules/devtools/client/shared/widgets/LineGraphWidget.jsPK
!<\���M����chrome/devtools/modules/devtools/client/shared/widgets/MountainGraphWidget.jsPK
!<ۼܗ�W�WI���chrome/devtools/modules/devtools/client/shared/widgets/SideMenuWidget.jsmPK
!<~�qa��K��_t�chrome/devtools/modules/devtools/client/shared/widgets/SimpleListWidget.jsmPK
!<ͼ�3&&B��X��chrome/devtools/modules/devtools/client/shared/widgets/Spectrum.jsPK
!<Gi�0!�!�E��ǵ�chrome/devtools/modules/devtools/client/shared/widgets/TableWidget.jsPK
!<�UyEyED��K��chrome/devtools/modules/devtools/client/shared/widgets/TreeWidget.jsPK
!<�ڜ?Y�Y�H��&ѐchrome/devtools/modules/devtools/client/shared/widgets/VariablesView.jsmPK
!<Ws@�HrHrR��寒chrome/devtools/modules/devtools/client/shared/widgets/VariablesViewController.jsmPK
!< 7��n+n+T���"�chrome/devtools/modules/devtools/client/shared/widgets/tooltip/EventTooltipHelper.jsPK
!<F.��BVBVM��}N�chrome/devtools/modules/devtools/client/shared/widgets/tooltip/HTMLTooltip.jsPK
!<�P�wOOT��*��chrome/devtools/modules/devtools/client/shared/widgets/tooltip/ImageTooltipHelper.jsPK
!<쥔�..O��빓chrome/devtools/modules/devtools/client/shared/widgets/tooltip/InlineTooltip.jsPK
!<����ccZ���“chrome/devtools/modules/devtools/client/shared/widgets/tooltip/SwatchBasedEditorTooltip.jsPK
!<�͓99Z��aޓchrome/devtools/modules/devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip.jsPK
!<�8�ggZ����chrome/devtools/modules/devtools/client/shared/widgets/tooltip/SwatchCubicBezierTooltip.jsPK
!<쮺S��U���
�chrome/devtools/modules/devtools/client/shared/widgets/tooltip/SwatchFilterTooltip.jsPK
!<��厳0�0I��<�chrome/devtools/modules/devtools/client/shared/widgets/tooltip/Tooltip.jsPK
!<��׳��O��VK�chrome/devtools/modules/devtools/client/shared/widgets/tooltip/TooltipToggle.jsPK
!<�l柙
�
W���d�chrome/devtools/modules/devtools/client/shared/widgets/tooltip/VariableContentHelper.jsPK
!<h7d��W���r�chrome/devtools/modules/devtools/client/shared/widgets/tooltip/VariableTooltipHelper.jsPK
!<�s+����F���v�chrome/devtools/modules/devtools/client/shared/widgets/view-helpers.jsPK
!<p�8ַ	�	;���8�chrome/devtools/modules/devtools/client/shared/zoom-keys.jsPK
!<�4���0�0D��C�chrome/devtools/modules/devtools/client/sourceeditor/autocomplete.jsPK
!<������I��;t�chrome/devtools/modules/devtools/client/sourceeditor/css-autocompleter.jsPK
!<l�O� � @��.�chrome/devtools/modules/devtools/client/sourceeditor/debugger.jsPK
!<�fyz	z	R���;�chrome/devtools/modules/devtools/client/sourceeditor/editor-commands-controller.jsPK
!<, [AS�S�>��sE�chrome/devtools/modules/devtools/client/sourceeditor/editor.jsPK
!<y��̲G�GD��"�chrome/devtools/modules/devtools/client/sourceeditor/tern/browser.jsPK
!<���KKD�6<�chrome/devtools/modules/devtools/client/sourceeditor/tern/comment.jsPK
!<�y�(�(E��G�chrome/devtools/modules/devtools/client/sourceeditor/tern/condense.jsPK
!<�P�2Z2Z@��p�chrome/devtools/modules/devtools/client/sourceeditor/tern/def.jsPK
!<�c�9�9�B��X˙chrome/devtools/modules/devtools/client/sourceeditor/tern/ecma5.jsPK
!<�?��V V B��chrome/devtools/modules/devtools/client/sourceeditor/tern/infer.jsPK
!<hp C큧��chrome/devtools/modules/devtools/client/sourceeditor/tern/signal.jsPK
!<�$�|����A�%��chrome/devtools/modules/devtools/client/sourceeditor/tern/tern.jsPK
!<?	��Q	Q	<��8K�chrome/devtools/modules/devtools/client/sourceeditor/wasm.jsPK
!<�np^��8���T�chrome/devtools/modules/devtools/client/storage/panel.jsPK
!<�*���5��"^�chrome/devtools/modules/devtools/client/storage/ui.jsPK
!<(��@�@�E����chrome/devtools/modules/devtools/client/styleeditor/StyleEditorUI.jsmPK
!<u�!$$G��,��chrome/devtools/modules/devtools/client/styleeditor/StyleEditorUtil.jsmPK
!<MSHKgKgH�����chrome/devtools/modules/devtools/client/styleeditor/StyleSheetEditor.jsmPK
!<�3ă�F��f�chrome/devtools/modules/devtools/client/styleeditor/original-source.jsPK
!<��]�K��M�chrome/devtools/modules/devtools/client/styleeditor/styleeditor-commands.jsPK
!<�J-���H���%�chrome/devtools/modules/devtools/client/styleeditor/styleeditor-panel.jsPK
!<օ��d�d@���6�chrome/devtools/modules/devtools/client/themes/audio/shutter.wavPK
!<��h���F��훞chrome/devtools/modules/devtools/client/themes/commandline-browser.cssPK
!<a�68II9��8��chrome/devtools/modules/devtools/client/themes/common.cssPK
!<��o��	�	<�����chrome/devtools/modules/devtools/client/themes/splitters.cssPK
!<+%����;����chrome/devtools/modules/devtools/client/themes/toolbars.cssPK
!<xQ�--<��"�chrome/devtools/modules/devtools/client/themes/variables.cssPK
!<�<]�?���K�chrome/devtools/modules/devtools/client/webaudioeditor/panel.jsPK
!<��`�F���S�chrome/devtools/modules/devtools/client/webconsole/console-commands.jsPK
!<���@�@�D��x`�chrome/devtools/modules/devtools/client/webconsole/console-output.jsPK
!<.�s�PPPP@���chrome/devtools/modules/devtools/client/webconsole/hudservice.jsPK
!<ڈ�!r�r�<���:�chrome/devtools/modules/devtools/client/webconsole/jsterm.jsPK
!<͛}WWP���'�chrome/devtools/modules/devtools/client/webconsole/net/components/cookies-tab.jsPK
!<�Un�		P��Y0�chrome/devtools/modules/devtools/client/webconsole/net/components/headers-tab.jsPK
!<v��ddS���9�chrome/devtools/modules/devtools/client/webconsole/net/components/net-info-body.cssPK
!<�@>99R���E�chrome/devtools/modules/devtools/client/webconsole/net/components/net-info-body.jsPK
!<?m�%%X��^Z�chrome/devtools/modules/devtools/client/webconsole/net/components/net-info-group-list.jsPK
!<#���??T���_�chrome/devtools/modules/devtools/client/webconsole/net/components/net-info-group.cssPK
!<�S���h�chrome/devtools/modules/devtools/client/webconsole/net/components/net-info-group.jsPK
!<�J^xnnU��*q�chrome/devtools/modules/devtools/client/webconsole/net/components/net-info-params.cssPK
!<������T��t�chrome/devtools/modules/devtools/client/webconsole/net/components/net-info-params.jsPK
!<ު�@��O��Nz�chrome/devtools/modules/devtools/client/webconsole/net/components/params-tab.jsPK
!<b^�+M��R�chrome/devtools/modules/devtools/client/webconsole/net/components/post-tab.jsPK
!<X��@@R���chrome/devtools/modules/devtools/client/webconsole/net/components/response-tab.cssPK
!<4��>��Q��s��chrome/devtools/modules/devtools/client/webconsole/net/components/response-tab.jsPK
!<��x���P�����chrome/devtools/modules/devtools/client/webconsole/net/components/size-limit.cssPK
!<�!|2��O��ؿ�chrome/devtools/modules/devtools/client/webconsole/net/components/size-limit.jsPK
!<ב�=��L��<Ǣchrome/devtools/modules/devtools/client/webconsole/net/components/spinner.jsPK
!<p���xxS��Kʢchrome/devtools/modules/devtools/client/webconsole/net/components/stacktrace-tab.jsPK
!<��H�!!G��4Ϣchrome/devtools/modules/devtools/client/webconsole/net/data-provider.jsPK
!<<݁ґ�>���բchrome/devtools/modules/devtools/client/webconsole/net/main.jsPK
!<],e��F����chrome/devtools/modules/devtools/client/webconsole/net/net-request.cssPK
!<���&&E����chrome/devtools/modules/devtools/client/webconsole/net/net-request.jsPK
!<�� ���F��G
�chrome/devtools/modules/devtools/client/webconsole/net/utils/events.jsPK
!<�ȹw��D��3�chrome/devtools/modules/devtools/client/webconsole/net/utils/json.jsPK
!<�&)00C���(�chrome/devtools/modules/devtools/client/webconsole/net/utils/net.jsPK
!<�t��UUX��7�chrome/devtools/modules/devtools/client/webconsole/new-console-output/actions/filters.jsPK
!<����V���?�chrome/devtools/modules/devtools/client/webconsole/new-console-output/actions/index.jsPK
!<���d��Y��LB�chrome/devtools/modules/devtools/client/webconsole/new-console-output/actions/messages.jsPK
!<z˕�S��\O�chrome/devtools/modules/devtools/client/webconsole/new-console-output/actions/ui.jsPK
!<�ޥ$llb��bX�chrome/devtools/modules/devtools/client/webconsole/new-console-output/components/CollapseButton.jsPK
!<
{J_��a��N\�chrome/devtools/modules/devtools/client/webconsole/new-console-output/components/ConsoleOutput.jsPK
!<�}�*��`���v�chrome/devtools/modules/devtools/client/webconsole/new-console-output/components/ConsoleTable.jsPK
!<"�#[Y#Y#]��׏�chrome/devtools/modules/devtools/client/webconsole/new-console-output/components/FilterBar.jsPK
!<sYDVV`�����chrome/devtools/modules/devtools/client/webconsole/new-console-output/components/FilterButton.jsPK
!<�ORRb����chrome/devtools/modules/devtools/client/webconsole/new-console-output/components/FilterCheckbox.jsPK
!<J�В�
�
c��Q��chrome/devtools/modules/devtools/client/webconsole/new-console-output/components/GripMessageBody.jsPK
!<��\��#�#[��Wʣchrome/devtools/modules/devtools/client/webconsole/new-console-output/components/Message.jsPK
!<%E�A�
�
d����chrome/devtools/modules/devtools/client/webconsole/new-console-output/components/MessageContainer.jsPK
!<���V_��+��chrome/devtools/modules/devtools/client/webconsole/new-console-output/components/MessageIcon.jsPK
!<+z�a����chrome/devtools/modules/devtools/client/webconsole/new-console-output/components/MessageIndent.jsPK
!<M�$$a��-�chrome/devtools/modules/devtools/client/webconsole/new-console-output/components/MessageRepeat.jsPK
!<)���	�	[����chrome/devtools/modules/devtools/client/webconsole/new-console-output/components/SideBar.jsPK
!<��Wo��p����chrome/devtools/modules/devtools/client/webconsole/new-console-output/components/message-types/ConsoleApiCall.jsPK
!<*���p���'�chrome/devtools/modules/devtools/client/webconsole/new-console-output/components/message-types/ConsoleCommand.jsPK
!<\��vRRq��`-�chrome/devtools/modules/devtools/client/webconsole/new-console-output/components/message-types/DefaultRenderer.jsPK
!<�X����r��A0�chrome/devtools/modules/devtools/client/webconsole/new-console-output/components/message-types/EvaluationResult.jsPK
!<�)�{{u��p9�chrome/devtools/modules/devtools/client/webconsole/new-console-output/components/message-types/NetworkEventMessage.jsPK
!<�z	��k��~N�chrome/devtools/modules/devtools/client/webconsole/new-console-output/components/message-types/PageError.jsPK
!<k D��R���U�chrome/devtools/modules/devtools/client/webconsole/new-console-output/constants.jsPK
!<�}f���M���e�chrome/devtools/modules/devtools/client/webconsole/new-console-output/main.jsPK
!<Y�L�C;C;c���i�chrome/devtools/modules/devtools/client/webconsole/new-console-output/new-console-output-wrapper.jsPK
!<���G[[Y�����chrome/devtools/modules/devtools/client/webconsole/new-console-output/reducers/filters.jsPK
!<ڊ�a  W�����chrome/devtools/modules/devtools/client/webconsole/new-console-output/reducers/index.jsPK
!<Mڥ��f�fZ��!��chrome/devtools/modules/devtools/client/webconsole/new-console-output/reducers/messages.jsPK
!<tZq�55W����chrome/devtools/modules/devtools/client/webconsole/new-console-output/reducers/prefs.jsPK
!<B�Z��T��6�chrome/devtools/modules/devtools/client/webconsole/new-console-output/reducers/ui.jsPK
!<Bz�J��Z��z �chrome/devtools/modules/devtools/client/webconsole/new-console-output/selectors/filters.jsPK
!<�a
���[���"�chrome/devtools/modules/devtools/client/webconsole/new-console-output/selectors/messages.jsPK
!<����X���(�chrome/devtools/modules/devtools/client/webconsole/new-console-output/selectors/prefs.jsPK
!<����U��+�chrome/devtools/modules/devtools/client/webconsole/new-console-output/selectors/ui.jsPK
!<g�3U�!�!N��-�chrome/devtools/modules/devtools/client/webconsole/new-console-output/store.jsPK
!<�5�ZZt��#O�chrome/devtools/modules/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/stub-snippets.jsPK
!<o]J<z�z�g��h�chrome/devtools/modules/devtools/client/webconsole/new-console-output/test/fixtures/stubs/consoleApi.jsPK
!<<�tB��g����chrome/devtools/modules/devtools/client/webconsole/new-console-output/test/fixtures/stubs/cssMessage.jsPK
!<�z�GGm��/
�chrome/devtools/modules/devtools/client/webconsole/new-console-output/test/fixtures/stubs/evaluationResult.jsPK
!<'WWb���Q�chrome/devtools/modules/devtools/client/webconsole/new-console-output/test/fixtures/stubs/index.jsPK
!<���1�1i���U�chrome/devtools/modules/devtools/client/webconsole/new-console-output/test/fixtures/stubs/networkEvent.jsPK
!<���?�?f�����chrome/devtools/modules/devtools/client/webconsole/new-console-output/test/fixtures/stubs/pageError.jsPK
!<�ƞ���N��
ȧchrome/devtools/modules/devtools/client/webconsole/new-console-output/types.jsPK
!<�i�Ƙ�[��;ϧchrome/devtools/modules/devtools/client/webconsole/new-console-output/utils/context-menu.jsPK
!<l3�`[��L�chrome/devtools/modules/devtools/client/webconsole/new-console-output/utils/id-generator.jsPK
!<�՝L,L,W����chrome/devtools/modules/devtools/client/webconsole/new-console-output/utils/messages.jsPK
!<�S`�
�
_����chrome/devtools/modules/devtools/client/webconsole/new-console-output/utils/object-inspector.jsPK
!<��}���T���"�chrome/devtools/modules/devtools/client/webconsole/new-console-output/utils/prefs.jsPK
!<���0((D��W(�chrome/devtools/modules/devtools/client/webconsole/new-webconsole.jsPK
!<��2�
�
;���P�chrome/devtools/modules/devtools/client/webconsole/panel.jsPK
!<ߚ���;��!_�chrome/devtools/modules/devtools/client/webconsole/utils.jsPK
!<��2�*=*=Q��n�chrome/devtools/modules/devtools/client/webconsole/webconsole-connection-proxy.jsPK
!<e�e�NNE����chrome/devtools/modules/devtools/client/webconsole/webconsole-l10n.jsPK
!<�L�?{?{@���Ũchrome/devtools/modules/devtools/client/webconsole/webconsole.jsPK
!<F)PP@��UA�chrome/devtools/modules/devtools/client/webide/modules/addons.jsPK
!<�����d�dE��Q�chrome/devtools/modules/devtools/client/webide/modules/app-manager.jsPK
!<QV���F��6��chrome/devtools/modules/devtools/client/webide/modules/app-projects.jsPK
!<J"l��#�#G��ZҪchrome/devtools/modules/devtools/client/webide/modules/app-validator.jsPK
!<9��^''E��?��chrome/devtools/modules/devtools/client/webide/modules/config-view.jsPK
!<��Y�k.k.F����chrome/devtools/modules/devtools/client/webide/modules/project-list.jsPK
!<e�(F���L�chrome/devtools/modules/devtools/client/webide/modules/runtime-list.jsPK
!<n0��7�7B��d�chrome/devtools/modules/devtools/client/webide/modules/runtimes.jsPK
!<�%9'))C��G��chrome/devtools/modules/devtools/client/webide/modules/tab-store.jsPK
!<�T����?��Ѳ�chrome/devtools/modules/devtools/client/webide/modules/utils.jsPK
!<U&((��F��)��chrome/devtools/modules/devtools/server/actors/accessibility-parent.jsPK
!<z"�%����?���ګchrome/devtools/modules/devtools/server/actors/accessibility.jsPK
!<�e����@��i^�chrome/devtools/modules/devtools/server/actors/actor-registry.jsPK
!<�KU�&�&7���d�chrome/devtools/modules/devtools/server/actors/addon.jsPK
!<�wf�nn8��ҋ�chrome/devtools/modules/devtools/server/actors/addons.jsPK
!<�g�u�u;�����chrome/devtools/modules/devtools/server/actors/animation.jsPK
!<X�p<����chrome/devtools/modules/devtools/server/actors/breakpoint.jsPK
!<��Q�Q>��� �chrome/devtools/modules/devtools/server/actors/call-watcher.jsPK
!<����i�i8��s�chrome/devtools/modules/devtools/server/actors/canvas.jsPK
!<���?��?��(ݭchrome/devtools/modules/devtools/server/actors/child-process.jsPK
!<��,t��8��(�chrome/devtools/modules/devtools/server/actors/chrome.jsPK
!<�Aw�A�A8��
�chrome/devtools/modules/devtools/server/actors/common.jsPK
!<0��0
0
9��SL�chrome/devtools/modules/devtools/server/actors/content.jsPK
!<��|
|
@���V�chrome/devtools/modules/devtools/server/actors/css-properties.jsPK
!<�.�ļU�U=���d�chrome/devtools/modules/devtools/server/actors/csscoverage.jsPK
!<����8��˺�chrome/devtools/modules/devtools/server/actors/device.jsPK
!<�)�O�,�,K���îchrome/devtools/modules/devtools/server/actors/emulation/touch-simulator.jsPK
!<�*�bb;����chrome/devtools/modules/devtools/server/actors/emulation.jsPK
!<$�.;;=����chrome/devtools/modules/devtools/server/actors/environment.jsPK
!<_�r�ll;��6&�chrome/devtools/modules/devtools/server/actors/errordocs.jsPK
!<왷��>���=�chrome/devtools/modules/devtools/server/actors/eventlooplag.jsPK
!<�#$��7���D�chrome/devtools/modules/devtools/server/actors/frame.jsPK
!<�#�;��&R�chrome/devtools/modules/devtools/server/actors/framerate.jsPK
!<�s??��6���W�chrome/devtools/modules/devtools/server/actors/gcli.jsPK
!<��J/,	,	D���s�chrome/devtools/modules/devtools/server/actors/heap-snapshot-file.jsPK
!<1�z
��I��*}�chrome/devtools/modules/devtools/server/actors/highlighters/accessible.jsPK
!<h��4 4 K�����chrome/devtools/modules/devtools/server/actors/highlighters/auto-refresh.jsPK
!<Dҵ�XVXVH��-��chrome/devtools/modules/devtools/server/actors/highlighters/box-model.jsPK
!<7��/�/�G����chrome/devtools/modules/devtools/server/actors/highlighters/css-grid.jsPK
!<��##L���chrome/devtools/modules/devtools/server/actors/highlighters/css-transform.jsPK
!<R��1@1@J����chrome/devtools/modules/devtools/server/actors/highlighters/eye-dropper.jsPK
!<��5HHF���<�chrome/devtools/modules/devtools/server/actors/highlighters/flexbox.jsPK
!<�ݑw�X�XN����chrome/devtools/modules/devtools/server/actors/highlighters/geometry-editor.jsPK
!<1�E��7�7M���ޱchrome/devtools/modules/devtools/server/actors/highlighters/measuring-tool.jsPK
!<�-��AAN����chrome/devtools/modules/devtools/server/actors/highlighters/paused-debugger.jsPK
!<a�=p�!�!E���"�chrome/devtools/modules/devtools/server/actors/highlighters/rulers.jsPK
!<�[A�{	{	G��lD�chrome/devtools/modules/devtools/server/actors/highlighters/selector.jsPK
!<Q~������E��LN�chrome/devtools/modules/devtools/server/actors/highlighters/shapes.jsPK
!<ƞ��M��E�chrome/devtools/modules/devtools/server/actors/highlighters/simple-outline.jsPK
!<�#�L��R����chrome/devtools/modules/devtools/server/actors/highlighters/utils/accessibility.jsPK
!<)�G�E�EK����chrome/devtools/modules/devtools/server/actors/highlighters/utils/canvas.jsPK
!<\��"4]4]K��.?�chrome/devtools/modules/devtools/server/actors/highlighters/utils/markup.jsPK
!<E�J�

M��˜�chrome/devtools/modules/devtools/server/actors/highlighters/xul-accessible.jsPK
!<=J��F�F?��C��chrome/devtools/modules/devtools/server/actors/highlighters.cssPK
!<U)d�\�\>��|��chrome/devtools/modules/devtools/server/actors/highlighters.jsPK
!<�dc��K���Y�chrome/devtools/modules/devtools/server/actors/inspector/document-walker.jsPK
!<R�
�-�-E��o�chrome/devtools/modules/devtools/server/actors/inspector/inspector.jsPK
!<���8X8X@��$��chrome/devtools/modules/devtools/server/actors/inspector/node.jsPK
!<��P!!A�����chrome/devtools/modules/devtools/server/actors/inspector/utils.jsPK
!<�qTh_�_�B��*�chrome/devtools/modules/devtools/server/actors/inspector/walker.jsPK
!<�=0���8���
�chrome/devtools/modules/devtools/server/actors/layout.jsPK
!<�I"�MM8���)�chrome/devtools/modules/devtools/server/actors/memory.jsPK
!<���֮1�18��b5�chrome/devtools/modules/devtools/server/actors/object.jsPK
!<�F�uu>��fg�chrome/devtools/modules/devtools/server/actors/pause-scoped.jsPK
!<�\���6��7w�chrome/devtools/modules/devtools/server/actors/perf.jsPK
!<�OfG��'��chrome/devtools/modules/devtools/server/actors/performance-recording.jsPK
!<��sdgg=�����chrome/devtools/modules/devtools/server/actors/performance.jsPK
!<��&	&	<��k��chrome/devtools/modules/devtools/server/actors/preference.jsPK
!<�-��E��븸chrome/devtools/modules/devtools/server/actors/pretty-print-worker.jsPK
!<��ç	�	9��N��chrome/devtools/modules/devtools/server/actors/process.jsPK
!<��n�tt:��Lʸchrome/devtools/modules/devtools/server/actors/promises.jsPK
!<�J��7�78���chrome/devtools/modules/devtools/server/actors/reflow.jsPK
!<�łc�X�X6��i�chrome/devtools/modules/devtools/server/actors/root.jsPK
!<BA7xA�A�8��|s�chrome/devtools/modules/devtools/server/actors/source.jsPK
!<�r?T?T9����chrome/devtools/modules/devtools/server/actors/storage.jsPK
!<�>N��8���K�chrome/devtools/modules/devtools/server/actors/string.jsPK
!<����K�K�8���P�chrome/devtools/modules/devtools/server/actors/styles.jsPK
!<+���k�k=��(-�chrome/devtools/modules/devtools/server/actors/stylesheets.jsPK
!<'�����5��u��chrome/devtools/modules/devtools/server/actors/tab.jsPK
!<bX�օ���8��nk�chrome/devtools/modules/devtools/server/actors/thread.jsPK
!<�/v��:��Ia�chrome/devtools/modules/devtools/server/actors/timeline.jsPK
!<�`qmqmB��}p�chrome/devtools/modules/devtools/server/actors/utils/TabSources.jsPK
!<x���	�	L��N޾chrome/devtools/modules/devtools/server/actors/utils/actor-registry-utils.jsPK
!<�b�^�	�	D����chrome/devtools/modules/devtools/server/actors/utils/audionodes.jsonPK
!<��.�.K����chrome/devtools/modules/devtools/server/actors/utils/automation-timeline.jsPK
!<\G�``L���!�chrome/devtools/modules/devtools/server/actors/utils/breakpoint-actor-map.jsPK
!<?��ˏ�F��]8�chrome/devtools/modules/devtools/server/actors/utils/css-grid-utils.jsPK
!<t��sSSB��PA�chrome/devtools/modules/devtools/server/actors/utils/event-loop.jsPK
!<>�XAhhE��S�chrome/devtools/modules/devtools/server/actors/utils/make-debugger.jsPK
!<��_�##K���b�chrome/devtools/modules/devtools/server/actors/utils/map-uri-to-addon-id.jsPK
!<T'����D��Zi�chrome/devtools/modules/devtools/server/actors/utils/shapes-utils.jsPK
!<-��d��J����chrome/devtools/modules/devtools/server/actors/utils/source-actor-store.jsPK
!<^I�=�����chrome/devtools/modules/devtools/server/actors/utils/stack.jsPK
!<à/�� � E��뛿chrome/devtools/modules/devtools/server/actors/utils/walker-search.jsPK
!<ӭ-�g�g:����chrome/devtools/modules/devtools/server/actors/webaudio.jsPK
!<��IR`}`}<��R%�chrome/devtools/modules/devtools/server/actors/webbrowser.jsPK
!<B1H�T����chrome/devtools/modules/devtools/server/actors/webconsole/content-process-forward.jsPK
!<�2
C
CF�����chrome/devtools/modules/devtools/server/actors/webconsole/listeners.jsPK
!<�㮰\F\FB����chrome/devtools/modules/devtools/server/actors/webconsole/utils.jsPK
!<�X����M���B�chrome/devtools/modules/devtools/server/actors/webconsole/worker-listeners.jsPK
!<���w�.�.<���G�chrome/devtools/modules/devtools/server/actors/webconsole.jsPK
!<
���Y�YO���v�chrome/devtools/modules/devtools/server/actors/webextension-inspected-window.jsPK
!<���}::E�����chrome/devtools/modules/devtools/server/actors/webextension-parent.jsPK
!<�+_9_9>��|��chrome/devtools/modules/devtools/server/actors/webextension.jsPK
!<�J����7��7$�chrome/devtools/modules/devtools/server/actors/webgl.jsPK
!<kkcغ	�	8��)��chrome/devtools/modules/devtools/server/actors/window.jsPK
!<�};���=��9��chrome/devtools/modules/devtools/server/actors/worker-list.jsPK
!<��Ŀ.�.8��n��chrome/devtools/modules/devtools/server/actors/worker.jsPK
!<���rr0����chrome/devtools/modules/devtools/server/child.jsPK
!<X�-��J��C,�chrome/devtools/modules/devtools/server/content-process-debugger-server.jsPK
!<wE���:��10�chrome/devtools/modules/devtools/server/content-server.jsmPK
!<����4��<�chrome/devtools/modules/devtools/server/css-logic.jsPK
!<��s�7�78��e��chrome/devtools/modules/devtools/server/event-parsers.jsPK
!<!�x^�^�/��u�chrome/devtools/modules/devtools/server/main.jsPK
!<}�&D�
�
@�� �chrome/devtools/modules/devtools/server/performance/framerate.jsPK
!<�~7�8�8=��!�chrome/devtools/modules/devtools/server/performance/memory.jsPK
!<>��rDrD?��CZ�chrome/devtools/modules/devtools/server/performance/profiler.jsPK
!<���]?]??����chrome/devtools/modules/devtools/server/performance/recorder.jsPK
!<:1f�e+e+?�����chrome/devtools/modules/devtools/server/performance/timeline.jsPK
!<���4���
�chrome/devtools/modules/devtools/server/primitive.jsPK
!<5��22?����chrome/devtools/modules/devtools/server/service-worker-child.jsPK
!<5)'~~;��q �chrome/devtools/modules/devtools/server/websocket-server.jsPK
!<8�f��
�
1��H9�chrome/devtools/modules/devtools/server/worker.jsPK
!<Q�O�e�e8��wG�chrome/devtools/modules/devtools/shared/DevToolsUtils.jsPK
!<��َgg2��T��chrome/devtools/modules/devtools/shared/Loader.jsmPK
!<�:�&\�\�2����chrome/devtools/modules/devtools/shared/Parser.jsmPK
!<�
Dh�&�&B�����chrome/devtools/modules/devtools/shared/ThreadSafeDevToolsUtils.jsPK
!<z���
�
�6�����chrome/devtools/modules/devtools/shared/acorn/acorn.jsPK
!<�@ʬ���<��A��chrome/devtools/modules/devtools/shared/acorn/acorn_loose.jsPK
!<ENoL445��Gz�chrome/devtools/modules/devtools/shared/acorn/walk.jsPK
!<�c6���8�����chrome/devtools/modules/devtools/shared/apps/Devices.jsmPK
!<o�&kee8�����chrome/devtools/modules/devtools/shared/async-storage.jsPK
!<eJ�ѻ�6�����chrome/devtools/modules/devtools/shared/async-utils.jsPK
!<��q��Y�Y6�����chrome/devtools/modules/devtools/shared/base-loader.jsPK
!<b�	ڵ'�':���*�chrome/devtools/modules/devtools/shared/builtin-modules.jsPK
!<�^ ��>���R�chrome/devtools/modules/devtools/shared/client/addon-client.jsPK
!<:���E��/W�chrome/devtools/modules/devtools/shared/client/array-buffer-client.jsPK
!<P�	��C���[�chrome/devtools/modules/devtools/shared/client/breakpoint-client.jsPK
!<A�$//D���j�chrome/devtools/modules/devtools/shared/client/connection-manager.jsPK
!<�g�5RR;����chrome/devtools/modules/devtools/shared/client/constants.jsPK
!<�ܬ�����A�����chrome/devtools/modules/devtools/shared/client/debugger-client.jsPK
!<d�o�rrD���M�chrome/devtools/modules/devtools/shared/client/environment-client.jsPK
!<��Bҳ�>���S�chrome/devtools/modules/devtools/shared/client/event-source.jsPK
!<YoooD���b�chrome/devtools/modules/devtools/shared/client/long-string-client.jsPK
!<�W=���?��ch�chrome/devtools/modules/devtools/shared/client/object-client.jsPK
!<��Y���J��u��chrome/devtools/modules/devtools/shared/client/property-iterator-client.jsPK
!<�=��r��chrome/devtools/modules/devtools/shared/client/root-client.jsPK
!<K*Mï�?��ϥ�chrome/devtools/modules/devtools/shared/client/source-client.jsPK
!<�u�8==H�����chrome/devtools/modules/devtools/shared/client/symbol-iterator-client.jsPK
!<�k�A��<��~��chrome/devtools/modules/devtools/shared/client/tab-client.jsPK
!<���BJBJ?�����chrome/devtools/modules/devtools/shared/client/thread-client.jsPK
!<P��)
)
>��]%�chrome/devtools/modules/devtools/shared/client/trace-client.jsPK
!<�U��?���/�chrome/devtools/modules/devtools/shared/client/worker-client.jsPK
!<��Bi��;���;�chrome/devtools/modules/devtools/shared/content-observer.jsPK
!<����7���D�chrome/devtools/modules/devtools/shared/css/color-db.jsPK
!<E�&كك4��nY�chrome/devtools/modules/devtools/shared/css/color.jsPK
!< ��L�L�F�����chrome/devtools/modules/devtools/shared/css/generated/properties-db.jsPK
!<`	q��4��Is�chrome/devtools/modules/devtools/shared/css/lexer.jsPK
!<��	�ưư<��X�chrome/devtools/modules/devtools/shared/css/parsing-utils.jsPK
!<�����<��x��chrome/devtools/modules/devtools/shared/css/properties-db.jsPK
!<�`q�ll3��e��chrome/devtools/modules/devtools/shared/debounce.jsPK
!<��6��0��"��chrome/devtools/modules/devtools/shared/defer.jsPK
!<(���33D��J��chrome/devtools/modules/devtools/shared/deprecated-sync-thenables.jsPK
!<C,ߺR3R3>�����chrome/devtools/modules/devtools/shared/discovery/discovery.jsPK
!<}�7���=����chrome/devtools/modules/devtools/shared/dom-node-constants.jsPK
!<�%r��D����chrome/devtools/modules/devtools/shared/dom-node-filter-constants.jsPK
!<*Box$x$8����chrome/devtools/modules/devtools/shared/event-emitter.jsPK
!<�$$1���:�chrome/devtools/modules/devtools/shared/extend.jsPK
!<�;U���0��7=�chrome/devtools/modules/devtools/shared/flags.jsPK
!<ʃ����?��0@�chrome/devtools/modules/devtools/shared/fronts/accessibility.jsPK
!<�/����@��US�chrome/devtools/modules/devtools/shared/fronts/actor-registry.jsPK
!<:���__8���[�chrome/devtools/modules/devtools/shared/fronts/addons.jsPK
!<�Lݳ�;��O^�chrome/devtools/modules/devtools/shared/fronts/animation.jsPK
!<3;X^^>��[n�chrome/devtools/modules/devtools/shared/fronts/call-watcher.jsPK
!<��T��
�
8����chrome/devtools/modules/devtools/shared/fronts/canvas.jsPK
!<!���/�/@��V��chrome/devtools/modules/devtools/shared/fronts/css-properties.jsPK
!<^�^q�
�
=��H��chrome/devtools/modules/devtools/shared/fronts/csscoverage.jsPK
!<��1*OO8����chrome/devtools/modules/devtools/shared/fronts/device.jsPK
!<gwB���;��$��chrome/devtools/modules/devtools/shared/fronts/emulation.jsPK
!<�F',]]>��|��chrome/devtools/modules/devtools/shared/fronts/eventlooplag.jsPK
!<���_��;��5��chrome/devtools/modules/devtools/shared/fronts/framerate.jsPK
!<i�:996��]��chrome/devtools/modules/devtools/shared/fronts/gcli.jsPK
!<�	G�((>�����chrome/devtools/modules/devtools/shared/fronts/highlighters.jsPK
!<`��1@1@;��n��chrome/devtools/modules/devtools/shared/fronts/inspector.jsPK
!<9���8���%�chrome/devtools/modules/devtools/shared/fronts/layout.jsPK
!<X�\��8���.�chrome/devtools/modules/devtools/shared/fronts/memory.jsPK
!<%G���1�16���;�chrome/devtools/modules/devtools/shared/fronts/node.jsPK
!<���_::6���m�chrome/devtools/modules/devtools/shared/fronts/perf.jsPK
!<����G��Op�chrome/devtools/modules/devtools/shared/fronts/performance-recording.jsPK
!<�ى���=�����chrome/devtools/modules/devtools/shared/fronts/performance.jsPK
!<1U�3��<����chrome/devtools/modules/devtools/shared/fronts/preference.jsPK
!<�{T��:����chrome/devtools/modules/devtools/shared/fronts/promises.jsPK
!<jIZ}}8��U��chrome/devtools/modules/devtools/shared/fronts/reflow.jsPK
!<DL気�9��(��chrome/devtools/modules/devtools/shared/fronts/storage.jsPK
!<�ҳ���8����chrome/devtools/modules/devtools/shared/fronts/string.jsPK
!</v���0�08��5��chrome/devtools/modules/devtools/shared/fronts/styles.jsPK
!<����=����chrome/devtools/modules/devtools/shared/fronts/stylesheets.jsPK
!<����:����chrome/devtools/modules/devtools/shared/fronts/timeline.jsPK
!<�x`��
�
:��Y��chrome/devtools/modules/devtools/shared/fronts/webaudio.jsPK
!<{���{{O�����chrome/devtools/modules/devtools/shared/fronts/webextension-inspected-window.jsPK
!<�1�H��7�����chrome/devtools/modules/devtools/shared/fronts/webgl.jsPK
!<�ƨ��	�	=����chrome/devtools/modules/devtools/shared/gcli/command-state.jsPK
!<H�N'N'>����chrome/devtools/modules/devtools/shared/gcli/commands/addon.jsPK
!<U32�33A��P6�chrome/devtools/modules/devtools/shared/gcli/commands/appcache.jsPK
!<_�;�		@���K�chrome/devtools/modules/devtools/shared/gcli/commands/calllog.jsPK
!<xs��<��If�chrome/devtools/modules/devtools/shared/gcli/commands/cmd.jsPK
!<A���,&,&?��Iy�chrome/devtools/modules/devtools/shared/gcli/commands/cookie.jsPK
!<݌��jjD��ҟ�chrome/devtools/modules/devtools/shared/gcli/commands/csscoverage.jsPK
!<FZV��?�����chrome/devtools/modules/devtools/shared/gcli/commands/folder.jsPK
!<�{����B�����chrome/devtools/modules/devtools/shared/gcli/commands/highlight.jsPK
!<�#��>�����chrome/devtools/modules/devtools/shared/gcli/commands/index.jsPK
!<Y-�^�	�	?�����chrome/devtools/modules/devtools/shared/gcli/commands/inject.jsPK
!<�����<��
��chrome/devtools/modules/devtools/shared/gcli/commands/jsb.jsPK
!<X"m��?��4�chrome/devtools/modules/devtools/shared/gcli/commands/listen.jsPK
!<����
�
@����chrome/devtools/modules/devtools/shared/gcli/commands/measure.jsPK
!<<�d���>����chrome/devtools/modules/devtools/shared/gcli/commands/media.jsPK
!<Oك���@���%�chrome/devtools/modules/devtools/shared/gcli/commands/pagemod.jsPK
!<�q}VhhF��2F�chrome/devtools/modules/devtools/shared/gcli/commands/paintflashing.jsPK
!<�/�@@<���Z�chrome/devtools/modules/devtools/shared/gcli/commands/qsa.jsPK
!<����@���]�chrome/devtools/modules/devtools/shared/gcli/commands/restart.jsPK
!<�%`
`
?���f�chrome/devtools/modules/devtools/shared/gcli/commands/rulers.jsPK
!<�`��	P	PC��>t�chrome/devtools/modules/devtools/shared/gcli/commands/screenshot.jsPK
!<��9-9-A�����chrome/devtools/modules/devtools/shared/gcli/commands/security.jsPK
!<
���C��@��chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/cli.jsPK
!<���{��N��i	�chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/commands/clear.jsPK
!<d���,D,DQ����chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/commands/commands.jsPK
!<z�0�11P��GU�chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/commands/context.jsPK
!<�b�V0V0M���\�chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/commands/help.jsPK
!<�a�!QQN�����chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/commands/mocks.jsPK
!<#�P^�	�	M��d��chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/commands/pref.jsPK
!<~�lp��Q�����chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/commands/preflist.jsPK
!<�8:/EEM����chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/commands/test.jsPK
!<9���U�����chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/connectors/connectors.jsPK
!<~ߦג�P�����chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/converters/basic.jsPK
!<t\HCCU�����chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/converters/converters.jsPK
!<W��WWO��{�chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/converters/html.jsPK
!<Iu�S��?�chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/converters/terminal.jsPK
!<�qx�	�	O��J�chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/fields/delegate.jsPK
!<q����M����chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/fields/fields.jsPK
!<������P���9�chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/fields/selection.jsPK
!<���
��E���H�chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/index.jsPK
!<�y���D��M�chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/l10n.jsPK
!<��c��S��V�chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/languages/command.htmlPK
!<�*��B�BQ��lX�chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/languages/command.jsPK
!<q?��	�	T��֛�chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/languages/javascript.jsPK
!<d��.DDS����chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/languages/languages.jsPK
!<��T''O�����chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/mozui/completer.jsPK
!<���MMN��T��chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/mozui/inputter.jsPK
!<�[;h�#�#M����chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/mozui/tooltip.jsPK
!<<6-��H���=�chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/settings.jsPK
!<
k - -F��\�chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/system.jsPK
!<��x�	�	K�����chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/types/array.jsPK
!<������M��œ�chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/types/boolean.jsPK
!<�$M��M����chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/types/command.jsPK
!<=19=��J��1��chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/types/date.jsPK
!<>�����N��t��chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/types/delegate.jsPK
!<�@ϾOOJ�����chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/types/file.jsPK
!<��[=��P��_��chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/types/fileparser.jsPK
!<�|3��:�:P��z��chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/types/javascript.jsPK
!<�U�E��J���9�chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/types/node.jsPK
!<(��(11L���Q�chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/types/number.jsPK
!<)y�}}N��Ef�chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/types/resource.jsPK
!<0�+�D/D/O��.��chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/types/selection.jsPK
!<t�?M��߲�chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/types/setting.jsPK
!<G�ZKKL��Y��chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/types/string.jsPK
!<K��!�!�K����chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/types/types.jsPK
!<�8��NNK���I�chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/types/union.jsPK
!<�i�j-	-	I��OY�chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/types/url.jsPK
!<�o*o*H���b�chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/ui/focus.jsPK
!<�Ǵ���J�����chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/ui/history.jsPK
!<����
�
H�����chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/ui/intro.jsPK
!<DD�,,H����chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/ui/menu.cssPK
!<4?���I�����chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/ui/menu.htmlPK
!<�(�M!M!G��|��chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/ui/menu.jsPK
!<��3���G��.��chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/ui/view.jsPK
!<�bE���P�����chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/util/domtemplate.jsPK
!<|�#�#O�����chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/util/fileparser.jsPK
!<~Y��
�
O����chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/util/filesystem.jsPK
!<-8�--I��h
�chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/util/host.jsPK
!<�+��V	V	I���$�chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/util/l10n.jsPK
!<G|��..K���.�chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/util/legacy.jsPK
!<9P��""J��P@�chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/util/prism.jsPK
!<2�d���J���b�chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/util/spell.jsPK
!<H��GGI��y�chrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/util/util.jsPK
!<���1GUGU9��s��chrome/devtools/modules/devtools/shared/gcli/templater.jsPK
!<�}W+��8���chrome/devtools/modules/devtools/shared/generate-uuid.jsPK
!<8�<�6�6C���chrome/devtools/modules/devtools/shared/heapsnapshot/CensusUtils.jsPK
!<��E�)�)I��
O�chrome/devtools/modules/devtools/shared/heapsnapshot/DominatorTreeNode.jsPK
!<r�ki,,J��y�chrome/devtools/modules/devtools/shared/heapsnapshot/HeapAnalysesClient.jsPK
!<
��G&G&J�����chrome/devtools/modules/devtools/shared/heapsnapshot/HeapAnalysesWorker.jsPK
!<7A��..M��O��chrome/devtools/modules/devtools/shared/heapsnapshot/HeapSnapshotFileUtils.jsPK
!<��M5^]^]H�����chrome/devtools/modules/devtools/shared/heapsnapshot/census-tree-node.jsPK
!<�>�`�
�
F���6�chrome/devtools/modules/devtools/shared/heapsnapshot/shortest-paths.jsPK
!<a�D|��6���A�chrome/devtools/modules/devtools/shared/indentation.jsPK
!<N��G

5��W�chrome/devtools/modules/devtools/shared/indexed-db.jsPK
!<oz��H>H>>��k]�chrome/devtools/modules/devtools/shared/inspector/css-logic.jsPK
!<�O<<>����chrome/devtools/modules/devtools/shared/jsbeautify/beautify.jsPK
!<P����D�����chrome/devtools/modules/devtools/shared/jsbeautify/lib/sanitytest.jsPK
!<wO�5��L�����chrome/devtools/modules/devtools/shared/jsbeautify/lib/urlencode_unpacker.jsPK
!<'`��4/4/F��ӷ�chrome/devtools/modules/devtools/shared/jsbeautify/src/beautify-css.jsPK
!<�fc+��G��k��chrome/devtools/modules/devtools/shared/jsbeautify/src/beautify-html.jsPK
!<��	��E����chrome/devtools/modules/devtools/shared/jsbeautify/src/beautify-js.jsPK
!<w���(�(�H��Tv�chrome/devtools/modules/devtools/shared/jsbeautify/src/beautify-tests.jsPK
!<�D{�#�#/����chrome/devtools/modules/devtools/shared/l10n.jsPK
!<�j�##?���chrome/devtools/modules/devtools/shared/layout/dom-matrix-2d.jsPK
!<��r�P�P7��eB�chrome/devtools/modules/devtools/shared/layout/utils.jsPK
!<����ff=�����chrome/devtools/modules/devtools/shared/loader-plugin-raw.jsmPK
!<JJع�K�KJ��\��chrome/devtools/modules/devtools/shared/node-properties/node-properties.jsPK
!<K��\RR<��g��chrome/devtools/modules/devtools/shared/old-event-emitter.jsPK
!<���$ff/����chrome/devtools/modules/devtools/shared/path.jsPK
!<�M���G����chrome/devtools/modules/devtools/shared/performance/recording-common.jsPK
!<ǟ�^D^DF����chrome/devtools/modules/devtools/shared/performance/recording-utils.jsPK
!<��M<<=���Q�chrome/devtools/modules/devtools/shared/platform/clipboard.jsPK
!<v�")  9�� X�chrome/devtools/modules/devtools/shared/platform/stack.jsPK
!<��ս�6���^�chrome/devtools/modules/devtools/shared/plural-form.jsPK
!<��
�]�]B���y�chrome/devtools/modules/devtools/shared/pretty-fast/pretty-fast.jsPK
!<�Og����3�����chrome/devtools/modules/devtools/shared/protocol.jsPK
!<�)��P\P\?��}��chrome/devtools/modules/devtools/shared/qrcode/decoder/index.jsPK
!<�L�f����?��*��chrome/devtools/modules/devtools/shared/qrcode/encoder/index.jsPK
!<��k�
�
7��n�chrome/devtools/modules/devtools/shared/qrcode/index.jsPK
!<���~O~O8��(|�chrome/devtools/modules/devtools/shared/security/auth.jsPK
!<�P��QQ8�����chrome/devtools/modules/devtools/shared/security/cert.jsPK
!<8sP���:�����chrome/devtools/modules/devtools/shared/security/prompt.jsPK
!<!N���a�a:�����chrome/devtools/modules/devtools/shared/security/socket.jsPK
!<��&ݡݡ?��Q�chrome/devtools/modules/devtools/shared/sourcemap/source-map.jsPK
!<�rRL==>��@��chrome/devtools/modules/devtools/shared/specs/accessibility.jsPK
!<]2�mm?����chrome/devtools/modules/devtools/shared/specs/actor-registry.jsPK
!<f��J  7����chrome/devtools/modules/devtools/shared/specs/addons.jsPK
!<�'�..:���chrome/devtools/modules/devtools/shared/specs/animation.jsPK
!<�����;����chrome/devtools/modules/devtools/shared/specs/breakpoint.jsPK
!<O�755=����chrome/devtools/modules/devtools/shared/specs/call-watcher.jsPK
!<X�����7��:!�chrome/devtools/modules/devtools/shared/specs/canvas.jsPK
!<�I1		?��z-�chrome/devtools/modules/devtools/shared/specs/css-properties.jsPK
!<r��
<���/�chrome/devtools/modules/devtools/shared/specs/csscoverage.jsPK
!<���7��U4�chrome/devtools/modules/devtools/shared/specs/device.jsPK
!<��c���:��*7�chrome/devtools/modules/devtools/shared/specs/emulation.jsPK
!<!p9�tt<��S?�chrome/devtools/modules/devtools/shared/specs/environment.jsPK
!<<����=��!B�chrome/devtools/modules/devtools/shared/specs/eventlooplag.jsPK
!<�����6��ZE�chrome/devtools/modules/devtools/shared/specs/frame.jsPK
!<rr����:���G�chrome/devtools/modules/devtools/shared/specs/framerate.jsPK
!<�$��225��~K�chrome/devtools/modules/devtools/shared/specs/gcli.jsPK
!<�҉w##C��T�chrome/devtools/modules/devtools/shared/specs/heap-snapshot-file.jsPK
!<0��,\\=���V�chrome/devtools/modules/devtools/shared/specs/highlighters.jsPK
!<5x��6��>\�chrome/devtools/modules/devtools/shared/specs/index.jsPK
!<\�J�#�#:��={�chrome/devtools/modules/devtools/shared/specs/inspector.jsPK
!<v����7��%��chrome/devtools/modules/devtools/shared/specs/layout.jsPK
!<�ײ�

7��5��chrome/devtools/modules/devtools/shared/specs/memory.jsPK
!<4HZr�
�
5�����chrome/devtools/modules/devtools/shared/specs/node.jsPK
!<	V����5�����chrome/devtools/modules/devtools/shared/specs/perf.jsPK
!<�Wus��F����chrome/devtools/modules/devtools/shared/specs/performance-recording.jsPK
!<��zk��<�����chrome/devtools/modules/devtools/shared/specs/performance.jsPK
!<�pX|��;��;��chrome/devtools/modules/devtools/shared/specs/preference.jsPK
!<�&4��9��9��chrome/devtools/modules/devtools/shared/specs/promises.jsPK
!<fdhFF7��Z��chrome/devtools/modules/devtools/shared/specs/reflow.jsPK
!<�k'���7�����chrome/devtools/modules/devtools/shared/specs/script.jsPK
!<6J�VV7�����chrome/devtools/modules/devtools/shared/specs/source.jsPK
!<P�Ȩ��8�����chrome/devtools/modules/devtools/shared/specs/storage.jsPK
!<Ӳ���7�����chrome/devtools/modules/devtools/shared/specs/string.jsPK
!<�T��((7����chrome/devtools/modules/devtools/shared/specs/styles.jsPK
!<�C�#��<��'�chrome/devtools/modules/devtools/shared/specs/stylesheets.jsPK
!<���9��R"�chrome/devtools/modules/devtools/shared/specs/timeline.jsPK
!<�h����9��./�chrome/devtools/modules/devtools/shared/specs/webaudio.jsPK
!<j.-�
�
N��5@�chrome/devtools/modules/devtools/shared/specs/webextension-inspected-window.jsPK
!<t`�bbD��tN�chrome/devtools/modules/devtools/shared/specs/webextension-parent.jsPK
!<������6��8Q�chrome/devtools/modules/devtools/shared/specs/webgl.jsPK
!<���{{7��GZ�chrome/devtools/modules/devtools/shared/specs/worker.jsPK
!<�� �.�.<��a�chrome/devtools/modules/devtools/shared/sprintfjs/sprintf.jsPK
!<v.j8"8"1�����chrome/devtools/modules/devtools/shared/system.jsPK
!<���B�B/�����chrome/devtools/modules/devtools/shared/task.jsPK
!<[4�3��3�����chrome/devtools/modules/devtools/shared/throttle.jsPK
!<��E�U/U/<����chrome/devtools/modules/devtools/shared/transport/packets.jsPK
!<��z���A���+�chrome/devtools/modules/devtools/shared/transport/stream-utils.jsPK
!<Hw@�
w
w>���K�chrome/devtools/modules/devtools/shared/transport/transport.jsPK
!<��ָ�H����chrome/devtools/modules/devtools/shared/transport/websocket-transport.jsPK
!<�!f�

:��&��chrome/devtools/modules/devtools/shared/wasm-source-map.jsPK
!<��@��\�\<�����chrome/devtools/modules/devtools/shared/webconsole/client.jsPK
!<��C��:�:J���1�chrome/devtools/modules/devtools/shared/webconsole/js-property-provider.jsPK
!<Z��k�l�lD��m�chrome/devtools/modules/devtools/shared/webconsole/network-helper.jsPK
!<��+Bl'l'E����chrome/devtools/modules/devtools/shared/webconsole/network-monitor.jsPK
!<|��K��K����chrome/devtools/modules/devtools/shared/webconsole/server-logger-monitor.jsPK
!<��&�8�8C���chrome/devtools/modules/devtools/shared/webconsole/server-logger.jsPK
!<�"���5�5>��rQ�chrome/devtools/modules/devtools/shared/webconsole/throttle.jsPK
!<l'����8�����chrome/devtools/modules/devtools/shared/worker/helper.jsPK
!<`���G�G8�����chrome/devtools/modules/devtools/shared/worker/loader.jsPK
!<to��MM8�����chrome/devtools/modules/devtools/shared/worker/worker.jsPK
!<���I'I'"�����chrome/devtools/skin/animation.cssPK
!<&
:%�Q�Q+���chrome/devtools/skin/animationinspector.cssPK
!<����!���n�chrome/devtools/skin/boxmodel.cssPK
!<�{�5��$��E��chrome/devtools/skin/breadcrumbs.cssPK
!<3_���'��p��chrome/devtools/skin/canvasdebugger.cssPK
!<���	
	
��E��chrome/devtools/skin/chart.cssPK
!<��0��$�����chrome/devtools/skin/commandline.cssPK
!<kc�hh)��Q��chrome/devtools/skin/components-frame.cssPK
!<�͘/����chrome/devtools/skin/components-h-split-box.cssPK
!<߸((!��d��chrome/devtools/skin/computed.cssPK
!<(ޤ�!!#����chrome/devtools/skin/dark-theme.cssPK
!<���=�5�5!���chrome/devtools/skin/debugger.cssPK
!<����)��DT�chrome/devtools/skin/devtools-browser.cssPK
!<�|���&��pW�chrome/devtools/skin/firebug-theme.cssPK
!<ٝ�]��7���q�chrome/devtools/skin/floating-scrollbars-dark-theme.cssPK
!<eyT�yy>���y�chrome/devtools/skin/floating-scrollbars-responsive-design.cssPK
!<4~��LL����chrome/devtools/skin/fonts.cssPK
!<�#H��#��8��chrome/devtools/skin/images/add.svgPK
!<��1��r��chrome/devtools/skin/images/alerticon-warning.pngPK
!<5C�z��4��ޏ�chrome/devtools/skin/images/[email protected]
!<�T��xx,��ݑ�chrome/devtools/skin/images/angle-swatch.svgPK
!<�r	gtt4�����chrome/devtools/skin/images/animation-fast-track.svgPK
!<���ǧ�'��e��chrome/devtools/skin/images/arrow-e.pngPK
!<�����*��Q��chrome/devtools/skin/images/[email protected]
!<�:�ff3��3��chrome/devtools/skin/images/breadcrumbs-divider.svgPK
!<�����8����chrome/devtools/skin/images/breadcrumbs-scrollbutton.svgPK
!<]�\Ixx*����chrome/devtools/skin/images/breakpoint.svgPK
!<[�o��%�����chrome/devtools/skin/images/clear.svgPK
!<�
2w%��ʩ�chrome/devtools/skin/images/close.svgPK
!<E���FF/�� ��chrome/devtools/skin/images/command-console.svgPK
!<�ב��2�����chrome/devtools/skin/images/command-eyedropper.svgPK
!<p�L���.�����chrome/devtools/skin/images/command-frames.svgPK
!<�N��/�����chrome/devtools/skin/images/command-measure.svgPK
!<TGI��2큧��chrome/devtools/skin/images/command-noautohide.svgPK
!<4	c���5�����chrome/devtools/skin/images/command-paintflashing.svgPK
!<Η�BB,��ξ�chrome/devtools/skin/images/command-pick.svgPK
!<��}�6��Z��chrome/devtools/skin/images/command-responsivemode.svgPK
!<���^~~.�����chrome/devtools/skin/images/command-rulers.svgPK
!<��H��2�����chrome/devtools/skin/images/command-screenshot.svgPK
!<4û�0��m��chrome/devtools/skin/images/commandline-icon.svgPK
!<��4��(��v��chrome/devtools/skin/images/controls.pngPK
!<g��QQ+�����chrome/devtools/skin/images/[email protected]
!<���)��$��J��chrome/devtools/skin/images/copy.svgPK
!<6O��3��]��chrome/devtools/skin/images/cubic-bezier-swatch.pngPK
!<�ӊ
ZZ6�����chrome/devtools/skin/images/[email protected]
!<��
U��.��z��chrome/devtools/skin/images/debugger/arrow.svgPK
!<Ku7���-�����chrome/devtools/skin/images/debugger/back.svgPK
!<�wuB��1�����chrome/devtools/skin/images/debugger/blackBox.svgPK
!<�\2���.�����chrome/devtools/skin/images/debugger/close.svgPK
!<������/�����chrome/devtools/skin/images/debugger/domain.svgPK
!<��zzvv-����chrome/devtools/skin/images/debugger/file.svgPK
!<�����/�����chrome/devtools/skin/images/debugger/folder.svgPK
!<N,c��0�����chrome/devtools/skin/images/debugger/forward.svgPK
!<���9����chrome/devtools/skin/images/debugger/pause-exceptions.svgPK
!<ZB\P.���chrome/devtools/skin/images/debugger/pause.svgPK
!<D��&554��y�chrome/devtools/skin/images/debugger/prettyPrint.svgPK
!<��2���.���chrome/devtools/skin/images/debugger/react.svgPK
!<�
�G((/��$�chrome/devtools/skin/images/debugger/resume.svgPK
!<Z
���/����chrome/devtools/skin/images/debugger/stepIn.svgPK
!<�[rr0��o!�chrome/devtools/skin/images/debugger/stepOut.svgPK
!<v��h��1��/$�chrome/devtools/skin/images/debugger/stepOver.svgPK
!<��겎�0��'�chrome/devtools/skin/images/debugger-step-in.svgPK
!<%��kk1���)�chrome/devtools/skin/images/debugger-step-out.svgPK
!<=Hz��2���,�chrome/devtools/skin/images/debugger-step-over.svgPK
!< �j���:���/�chrome/devtools/skin/images/debugger-toggleBreakpoints.svgPK
!<�jħ�0���5�chrome/devtools/skin/images/debugging-addons.svgPK
!<I�[���.���9�chrome/devtools/skin/images/debugging-tabs.svgPK
!<A�jE��1���;�chrome/devtools/skin/images/debugging-workers.svgPK
!<�ة)��$���?�chrome/devtools/skin/images/diff.svgPK
!<��~ss+���C�chrome/devtools/skin/images/dock-bottom.svgPK
!<;
|���)���F�chrome/devtools/skin/images/dock-side.svgPK
!<��~mUU+��gH�chrome/devtools/skin/images/dock-undock.svgPK
!<�]�xbb*��M�chrome/devtools/skin/images/dropmarker.svgPK
!<�Ldd,���N�chrome/devtools/skin/images/editor-error.pngPK
!<w%�3��]R�chrome/devtools/skin/images/filetypes/dir-close.svgPK
!<@��,��2���T�chrome/devtools/skin/images/filetypes/dir-open.svgPK
!<��ǽ��/���W�chrome/devtools/skin/images/filetypes/globe.svgPK
!<�(�==-���]�chrome/devtools/skin/images/filter-swatch.svgPK
!<�-&��r`�chrome/devtools/skin/images/filter.svgPK
!<kiL�FF'���c�chrome/devtools/skin/images/filters.svgPK
!<B�W���2��_i�chrome/devtools/skin/images/firebug/arrow-down.svgPK
!<�Ez���0��tk�chrome/devtools/skin/images/firebug/arrow-up.svgPK
!<�'��2���m�chrome/devtools/skin/images/firebug/breakpoint.svgPK
!<u�[<<-���p�chrome/devtools/skin/images/firebug/close.svgPK
!<D�7��Nw�chrome/devtools/skin/images/firebug/command-console.svgPK
!<���||:����chrome/devtools/skin/images/firebug/command-eyedropper.svgPK
!<�[�Occ6�����chrome/devtools/skin/images/firebug/command-frames.svgPK
!<f|�NN7��L��chrome/devtools/skin/images/firebug/command-measure.svgPK
!<�{����:����chrome/devtools/skin/images/firebug/command-noautohide.svgPK
!<T�j �	�	=��%��chrome/devtools/skin/images/firebug/command-paintflashing.svgPK
!<hOd?{{4��b��chrome/devtools/skin/images/firebug/command-pick.svgPK
!<9���>��/��chrome/devtools/skin/images/firebug/command-responsivemode.svgPK
!<��`�BB6��H��chrome/devtools/skin/images/firebug/command-rulers.svgPK
!<E"t�
�
:�����chrome/devtools/skin/images/firebug/command-scratchpad.svgPK
!<_�e��:�����chrome/devtools/skin/images/firebug/command-screenshot.svgPK
!<`��\8�����chrome/devtools/skin/images/firebug/commandline-icon.svgPK
!<|B+,,9��M��chrome/devtools/skin/images/firebug/debugger-blackbox.svgPK
!<o�	�	<�����chrome/devtools/skin/images/firebug/debugger-prettyprint.svgPK
!<�6�ww8����chrome/devtools/skin/images/firebug/debugger-step-in.svgPK
!<p�9���9�����chrome/devtools/skin/images/firebug/debugger-step-out.svgPK
!<"c��:����chrome/devtools/skin/images/firebug/debugger-step-over.svgPK
!<{�^��B��-�chrome/devtools/skin/images/firebug/debugger-toggleBreakpoints.svgPK
!<�hL��/����chrome/devtools/skin/images/firebug/disable.svgPK
!<�QV--3����chrome/devtools/skin/images/firebug/dock-bottom.svgPK
!<k�^�LL1��3�chrome/devtools/skin/images/firebug/dock-side.svgPK
!<O;zY3����chrome/devtools/skin/images/firebug/dock-undock.svgPK
!<���c))5��"%�chrome/devtools/skin/images/firebug/pane-collapse.svgPK
!<Q6��,,3���,�chrome/devtools/skin/images/firebug/pane-expand.svgPK
!<r�H$~~-��4�chrome/devtools/skin/images/firebug/pause.svgPK
!<]~W~,���;�chrome/devtools/skin/images/firebug/play.svgPK
!<��_
_
1��1@�chrome/devtools/skin/images/firebug/read-only.svgPK
!<p�S8%%.���J�chrome/devtools/skin/images/firebug/rewind.svgPK
!<��T���<��PO�chrome/devtools/skin/images/firebug/tool-debugger-paused.svgPK
!<ߚ�|��4���S�chrome/devtools/skin/images/firebug/tool-options.svgPK
!<J��JJJ=���\�chrome/devtools/skin/images/firebug/twisty-closed-firebug.svgPK
!<j�688;��,`�chrome/devtools/skin/images/firebug/twisty-open-firebug.svgPK
!<�Nϟ��,���c�chrome/devtools/skin/images/gcli_sec_bad.svgPK
!<Α����-��e�chrome/devtools/skin/images/gcli_sec_good.svgPK
!<*eǴ�1��Bf�chrome/devtools/skin/images/gcli_sec_moderate.svgPK
!<S{���/��Eg�chrome/devtools/skin/images/geometry-editor.svgPK
!<6��?����%��\m�chrome/devtools/skin/images/globe.svgPK
!<�\���$��Nb�chrome/devtools/skin/images/grid.svgPK
!<������$��d�chrome/devtools/skin/images/help.svgPK
!<��4���&��Ig�chrome/devtools/skin/images/import.svgPK
!<�����3��(k�chrome/devtools/skin/images/item-arrow-dark-ltr.svgPK
!<|�x��3��m�chrome/devtools/skin/images/item-arrow-dark-rtl.svgPK
!<5j����.��o�chrome/devtools/skin/images/item-arrow-ltr.svgPK
!<�V��.�p�chrome/devtools/skin/images/item-arrow-rtl.svgPK
!<g�\�||+���r�chrome/devtools/skin/images/item-toggle.svgPK
!<�/���y�chrome/devtools/skin/images/jump-definition.svgPK
!<�R���-��|�chrome/devtools/skin/images/pane-collapse.svgPK
!<����+��%�chrome/devtools/skin/images/pane-expand.svgPK
!<QB�2%��+��chrome/devtools/skin/images/pause.svgPK
!< s@=��}��chrome/devtools/skin/images/performance-details-call-tree.svgPK
!<���3>>>����chrome/devtools/skin/images/performance-details-flamegraph.svgPK
!<B4�1��=�����chrome/devtools/skin/images/performance-details-waterfall.svgPK
!<�R����$��ċ�chrome/devtools/skin/images/play.svgPK
!<ՙ�G��%��э�chrome/devtools/skin/images/power.svgPK
!<�Un�2����chrome/devtools/skin/images/profiler-stopwatch.svgPK
!<l��^,��P��chrome/devtools/skin/images/pseudo-class.svgPK
!<2l��II&�����chrome/devtools/skin/images/reload.svgPK
!<�^�,99&��8��chrome/devtools/skin/images/rewind.svgPK
!<�>� (�����chrome/devtools/skin/images/sad-face.svgPK
!<�Hվ��1�����chrome/devtools/skin/images/search-clear-dark.svgPK
!<$�͙��3����chrome/devtools/skin/images/search-clear-failed.svgPK
!<�C���2��ק�chrome/devtools/skin/images/search-clear-light.svgPK
!<e�.pp&��ī�chrome/devtools/skin/images/search.svgPK
!<f%%5��x��chrome/devtools/skin/images/security-state-broken.svgPK
!<�L!��7����chrome/devtools/skin/images/security-state-insecure.svgPK
!<�aa5����chrome/devtools/skin/images/security-state-secure.svgPK
!<S���xx3�����chrome/devtools/skin/images/security-state-weak.svgPK
!<*�_�rr4��a��chrome/devtools/skin/images/sort-ascending-arrow.svgPK
!<ɉrr5��%��chrome/devtools/skin/images/sort-descending-arrow.svgPK
!<d�dd,�����chrome/devtools/skin/images/toggle-tools.pngPK
!<S}�
/�����chrome/devtools/skin/images/[email protected]
!<,����+�����chrome/devtools/skin/images/tool-canvas.svgPK
!<��o�-�����chrome/devtools/skin/images/tool-debugger.svgPK
!<��߼BB(��O��chrome/devtools/skin/images/tool-dom.svgPK
!<�ۜw.�����chrome/devtools/skin/images/tool-inspector.svgPK
!<
5���+��@��chrome/devtools/skin/images/tool-memory.svgPK
!<��1�<<,����chrome/devtools/skin/images/tool-network.svgPK
!<̦'',�����chrome/devtools/skin/images/tool-options.svgPK
!<#St���-����chrome/devtools/skin/images/tool-profiler.svgPK
!<�w�h``/��8��chrome/devtools/skin/images/tool-scratchpad.svgPK
!<��<���1����chrome/devtools/skin/images/tool-shadereditor.svgPK
!<XJ ��,��0��chrome/devtools/skin/images/tool-storage.svgPK
!<���*��0��I�chrome/devtools/skin/images/tool-styleeditor.svgPK
!<�ԟ		-��S�chrome/devtools/skin/images/tool-webaudio.svgPK
!<"A(��/����chrome/devtools/skin/images/tool-webconsole.svgPK
!<:�
�""+���
�chrome/devtools/skin/images/tracer-icon.pngPK
!<�����.����chrome/devtools/skin/images/[email protected]
!<��o���,���chrome/devtools/skin/images/vview-delete.pngPK
!<\ڌ��/����chrome/devtools/skin/images/[email protected]
!<��=֠�*����chrome/devtools/skin/images/vview-edit.pngPK
!<o�P	..-����chrome/devtools/skin/images/[email protected]
!<ž>���*��7�chrome/devtools/skin/images/vview-lock.pngPK
!<݃g=-��0�chrome/devtools/skin/images/[email protected]
!<3��pbb4����chrome/devtools/skin/images/vview-open-inspector.pngPK
!<�Hstt7��?�chrome/devtools/skin/images/[email protected]
!<�$4�uu*���chrome/devtools/skin/images/webconsole.svgPK
!<s��"���+�chrome/devtools/skin/inspector.cssPK
!<�w%e
e
*��>�chrome/devtools/skin/jit-optimizations.cssPK
!<���Y	Y	���H�chrome/devtools/skin/layout.cssPK
!<�Ŵe  $��\R�chrome/devtools/skin/light-theme.cssPK
!<�w�%�%���r�chrome/devtools/skin/markup.cssPK
!<:!��6�6���chrome/devtools/skin/memory.cssPK
!<s~�`������chrome/devtools/skin/perf.cssPK
!<`E��GG$����chrome/devtools/skin/performance.cssPK
!<���>�>��.�chrome/devtools/skin/rules.cssPK
!<K�	++#��bX�chrome/devtools/skin/scratchpad.cssPK
!<ev��		%���Y�chrome/devtools/skin/shadereditor.cssPK
!<؁9�cc"��0c�chrome/devtools/skin/splitview.cssPK
!<P���� ���j�chrome/devtools/skin/storage.cssPK
!<n�[L��$���p�chrome/devtools/skin/styleeditor.cssPK
!<+%����!�����chrome/devtools/skin/toolbars.cssPK
!<;��O�#�# ���chrome/devtools/skin/toolbox.cssPK
!<";��tt6���chrome/devtools/skin/tooltip/arrow-horizontal-dark.pngPK
!<Dp�ܑ�9����chrome/devtools/skin/tooltip/[email protected]
!<��[���7����chrome/devtools/skin/tooltip/arrow-horizontal-light.pngPK
!<���lj�:����chrome/devtools/skin/tooltip/[email protected]
!<��o��4����chrome/devtools/skin/tooltip/arrow-vertical-dark.pngPK
!<���7����chrome/devtools/skin/tooltip/[email protected]
!<,7��\\5����chrome/devtools/skin/tooltip/arrow-vertical-light.pngPK
!<��2;;8��K�chrome/devtools/skin/tooltip/[email protected]
!<��HF''!����chrome/devtools/skin/tooltips.cssPK
!<k>�kk'��.�chrome/devtools/skin/webaudioeditor.cssPK
!<� �4f|f|#����chrome/devtools/skin/webconsole.cssPK
!<�I�5p5p �����chrome/devtools/skin/widgets.cssPK
!<f��YBB����defaults/blocklists/addons.jsonPK
!<ak�p��%��w�defaults/blocklists/certificates.jsonPK
!<�MS@L@L��Ť�defaults/blocklists/gfx.jsonPK
!<�!3;,;, ��?�defaults/blocklists/plugins.jsonPK
!<�d�^���defaults/pinning/pins.jsonPK
!<Jj0��&���chrome/en-US/locale/branding/brand.dtdPK
!</���+��3 chrome/en-US/locale/browser/aboutDialog.dtdPK
!<�UT��)��p<chrome/en-US/locale/browser/aboutHome.dtdPK
!<to�	�	4���Echrome/en-US/locale/browser/aboutPrivateBrowsing.dtdPK
!<wF�;��{Ochrome/en-US/locale/browser/aboutPrivateBrowsing.propertiesPK
!<��yaa+���Pchrome/en-US/locale/browser/aboutRobots.dtdPK
!<6/���0���Ychrome/en-US/locale/browser/aboutSearchReset.dtdPK
!<���,
,
3��z_chrome/en-US/locale/browser/aboutSessionRestore.dtdPK
!<⾜��/���ichrome/en-US/locale/browser/aboutTabCrashed.dtdPK
!<&���bb/���rchrome/en-US/locale/browser/accounts.propertiesPK
!<��'y��1��|�chrome/en-US/locale/browser/appstrings.propertiesPK
!<�*	�>>/����chrome/en-US/locale/browser/baseMenuOverlay.dtdPK
!<����3�3*��(�chrome/en-US/locale/browser/bookmarks.htmlPK
!<����'���chrome/en-US/locale/browser/browser.dtdPK
!<	�#��3��\�chrome/en-US/locale/browser/downloads/downloads.dtdPK
!<�:ee:��]�chrome/en-US/locale/browser/downloads/downloads.propertiesPK
!<��0--8���chrome/en-US/locale/browser/downloads/settingsChange.dtdPK
!<K[~^��4����chrome/en-US/locale/browser/engineManager.propertiesPK
!<s�T[[/����chrome/en-US/locale/browser/feeds/subscribe.dtdPK
!<aX�?	?	6��X�chrome/en-US/locale/browser/feeds/subscribe.propertiesPK
!<G�p��8���chrome/en-US/locale/browser/lightweightThemes.propertiesPK
!<Da��

3��&�chrome/en-US/locale/browser/migration/migration.dtdPK
!<]��^:���chrome/en-US/locale/browser/migration/migration.propertiesPK
!<}��&&(���chrome/en-US/locale/browser/netError.dtdPK
!<X���2
2
(��L6chrome/en-US/locale/browser/pageInfo.dtdPK
!<.�	q44/���Cchrome/en-US/locale/browser/pageInfo.propertiesPK
!<�y���@��EKchrome/en-US/locale/browser/places/bookmarkProperties.propertiesPK
!<1����:��ZNchrome/en-US/locale/browser/places/editBookmarkOverlay.dtdPK
!<�X.�rr-��nVchrome/en-US/locale/browser/places/places.dtdPK
!<g��t��4��+jchrome/en-US/locale/browser/places/places.propertiesPK
!<��Z���4��%zchrome/en-US/locale/browser/preferences/advanced.dtdPK
!<6���>��J�chrome/en-US/locale/browser/preferences/applicationManager.dtdPK
!<��A�//E��;�chrome/en-US/locale/browser/preferences/applicationManager.propertiesPK
!<U��`OO6��͊chrome/en-US/locale/browser/preferences/blocklists.dtdPK
!<��lqq9��p�chrome/en-US/locale/browser/preferences/clearSiteData.dtdPK
!<DO�)^^@��8�chrome/en-US/locale/browser/preferences/clearSiteData.propertiesPK
!<���2���chrome/en-US/locale/browser/preferences/colors.dtdPK
!<�r���
�
6���chrome/en-US/locale/browser/preferences/connection.dtdPK
!<���e��6���chrome/en-US/locale/browser/preferences/containers.dtdPK
!<:��XX=��#�chrome/en-US/locale/browser/preferences/containers.propertiesPK
!<H�8g��3��֮chrome/en-US/locale/browser/preferences/content.dtdPK
!<b����3��Զchrome/en-US/locale/browser/preferences/cookies.dtdPK
!<&ɿ�1���chrome/en-US/locale/browser/preferences/fonts.dtdPK
!<�҉��5����chrome/en-US/locale/browser/preferences/languages.dtdPK
!<j�z880��/�chrome/en-US/locale/browser/preferences/main.dtdPK
!<`�א		7����chrome/en-US/locale/browser/preferences/permissions.dtdPK
!<f�\�]]7���chrome/en-US/locale/browser/preferences/preferences.dtdPK
!<��ڡi;i;>����chrome/en-US/locale/browser/preferences/preferences.propertiesPK
!<	+�}}3���chrome/en-US/locale/browser/preferences/privacy.dtdPK
!<L
�		2��X3chrome/en-US/locale/browser/preferences/search.dtdPK
!<���"oo4���<chrome/en-US/locale/browser/preferences/security.dtdPK
!<b�b؟�:��qDchrome/en-US/locale/browser/preferences/selectBookmark.dtdPK
!<y�		<��hFchrome/en-US/locale/browser/preferences/siteDataSettings.dtdPK
!<�O��rr0���Kchrome/en-US/locale/browser/preferences/sync.dtdPK
!<�f���7���achrome/en-US/locale/browser/preferences/translation.dtdPK
!<���NN1���fchrome/en-US/locale/browser/quitDialog.propertiesPK
!<q@xW��(��Kichrome/en-US/locale/browser/safeMode.dtdPK
!<��q���O��-nchrome/en-US/locale/browser/safebrowsing/phishing-afterload-warning-message.dtdPK
!<R�l�<��g�chrome/en-US/locale/browser/safebrowsing/report-phishing.dtdPK
!<��8]]@��֊chrome/en-US/locale/browser/safebrowsing/safebrowsing.propertiesPK
!<-�o�!!(����chrome/en-US/locale/browser/sanitize.dtdPK
!<��I���7����chrome/en-US/locale/browser/searchplugins/amazon-au.xmlPK
!<Ĵ�"��7��J�chrome/en-US/locale/browser/searchplugins/amazon-ca.xmlPK
!<8��L��:����chrome/en-US/locale/browser/searchplugins/amazondotcom.xmlPK
!<�~�RR2��٣chrome/en-US/locale/browser/searchplugins/bing.xmlPK
!<�a��2!2!1��{�chrome/en-US/locale/browser/searchplugins/ddg.xmlPK
!<��H\\5����chrome/en-US/locale/browser/searchplugins/ebay-au.xmlPK
!<��1�ZZ5����chrome/en-US/locale/browser/searchplugins/ebay-be.xmlPK
!<
��TT5��X�chrome/en-US/locale/browser/searchplugins/ebay-ca.xmlPK
!<-�,�UU5����chrome/en-US/locale/browser/searchplugins/ebay-ch.xmlPK
!<<e5UU5����chrome/en-US/locale/browser/searchplugins/ebay-ie.xmlPK
!<�k�UU5��O�chrome/en-US/locale/browser/searchplugins/ebay-nl.xmlPK
!<�p_�ZZ5����chrome/en-US/locale/browser/searchplugins/ebay-uk.xmlPK
!<��qTT2����chrome/en-US/locale/browser/searchplugins/ebay.xmlPK
!<%i?1 1 9��H�chrome/en-US/locale/browser/searchplugins/google-2018.xmlPK
!<���ـ�<���chrome/en-US/locale/browser/searchplugins/google-nocodes.xmlPK
!<���- - 4���;chrome/en-US/locale/browser/searchplugins/google.xmlPK
!<5()�66:��)\chrome/en-US/locale/browser/searchplugins/images/yahoo.icoPK
!<"+=��>���qchrome/en-US/locale/browser/searchplugins/images/yandex-en.icoPK
!<�����>���xchrome/en-US/locale/browser/searchplugins/images/yandex-ru.icoPK
!<��]]5����chrome/en-US/locale/browser/searchplugins/twitter.xmlPK
!<��
e��7����chrome/en-US/locale/browser/searchplugins/wikipedia.xmlPK
!<|iH���7��Őchrome/en-US/locale/browser/searchplugins/yandex-en.xmlPK
!<�⌯��4���chrome/en-US/locale/browser/setDesktopBackground.dtdPK
!<�%�RRR3��L�chrome/en-US/locale/browser/shellservice.propertiesPK
!<I�/���chrome/en-US/locale/browser/siteData.propertiesPK
!<	�``6��B�chrome/en-US/locale/browser/sitePermissions.propertiesPK
!<V|@ll)����chrome/en-US/locale/browser/syncBrand.dtdPK
!<^x��0����chrome/en-US/locale/browser/syncSetup.propertiesPK
!<u�5�aa.���chrome/en-US/locale/browser/taskbar.propertiesPK
!<�����+����chrome/en-US/locale/browser/translation.dtdPK
!<l�֎�2����chrome/en-US/locale/browser/translation.propertiesPK
!<�=��0����chrome/en-US/locale/browser/uiDensity.propertiesPK
!<QDG776���chrome/en-US/locale/browser/webrtcIndicator.propertiesPK
!<��8Jhh;����chrome/en-US/locale/en-US/devtools/client/VariablesView.dtdPK
!<bg��$$<��`�chrome/en-US/locale/en-US/devtools/client/aboutdebugging.dtdPK
!<�8.o�"�"C����chrome/en-US/locale/en-US/devtools/client/aboutdebugging.propertiesPK
!<���0�&�&G���chrome/en-US/locale/en-US/devtools/client/animationinspector.propertiesPK
!<p"mm@��(chrome/en-US/locale/en-US/devtools/client/app-manager.propertiesPK
!<4	�##B���0chrome/en-US/locale/en-US/devtools/client/appcacheutils.propertiesPK
!<���		=��UMchrome/en-US/locale/en-US/devtools/client/boxmodel.propertiesPK
!<T�r̺	�	<���Vchrome/en-US/locale/en-US/devtools/client/canvasdebugger.dtdPK
!<�$�

C���`chrome/en-US/locale/en-US/devtools/client/canvasdebugger.propertiesPK
!<�(Ѥ�?��Anchrome/en-US/locale/en-US/devtools/client/components.propertiesPK
!<�e���?��Brchrome/en-US/locale/en-US/devtools/client/connection-screen.dtdPK
!<�2��F��Yxchrome/en-US/locale/en-US/devtools/client/connection-screen.propertiesPK
!<>Tń�,�,6��Szchrome/en-US/locale/en-US/devtools/client/debugger.dtdPK
!<.��mM�M�=��(�chrome/en-US/locale/en-US/devtools/client/debugger.propertiesPK
!<+��d��;���:chrome/en-US/locale/en-US/devtools/client/device.propertiesPK
!<�K�kk8���>chrome/en-US/locale/en-US/devtools/client/dom.propertiesPK
!<�~b�

A���Bchrome/en-US/locale/en-US/devtools/client/filterwidget.propertiesPK
!<��X��C���Lchrome/en-US/locale/en-US/devtools/client/font-inspector.propertiesPK
!<�Hdd;���Rchrome/en-US/locale/en-US/devtools/client/graphs.propertiesPK
!<�X��DD8���Wchrome/en-US/locale/en-US/devtools/client/har.propertiesPK
!<o���\�\>��S\chrome/en-US/locale/en-US/devtools/client/inspector.propertiesPK
!<�q��F��2�chrome/en-US/locale/en-US/devtools/client/jit-optimizations.propertiesPK
!<�I����=����chrome/en-US/locale/en-US/devtools/client/jsonview.propertiesPK
!<�a�}�
�
;����chrome/en-US/locale/en-US/devtools/client/layout.propertiesPK
!<4�D
v'v'<����chrome/en-US/locale/en-US/devtools/client/markers.propertiesPK
!<(��?OO;��V�chrome/en-US/locale/en-US/devtools/client/memory.propertiesPK
!<S}��:���I	chrome/en-US/locale/en-US/devtools/client/menus.propertiesPK
!<��,	T�T�?���Q	chrome/en-US/locale/en-US/devtools/client/netmonitor.propertiesPK
!<�
Nf$f$9���
chrome/en-US/locale/en-US/devtools/client/performance.dtdPK
!<�v��__@��[4
chrome/en-US/locale/en-US/devtools/client/performance.propertiesPK
!<5=8\��?��O
chrome/en-US/locale/en-US/devtools/client/responsive.propertiesPK
!<Z��<''8��Bo
chrome/en-US/locale/en-US/devtools/client/scratchpad.dtdPK
!</�&5��?����
chrome/en-US/locale/en-US/devtools/client/scratchpad.propertiesPK
!<)َO��:���
chrome/en-US/locale/en-US/devtools/client/shadereditor.dtdPK
!<��$�iiA���
chrome/en-US/locale/en-US/devtools/client/shadereditor.propertiesPK
!<����;��Щ
chrome/en-US/locale/en-US/devtools/client/shared.propertiesPK
!<���ݱ�:��>�
chrome/en-US/locale/en-US/devtools/client/sourceeditor.dtdPK
!<��]+A��G�
chrome/en-US/locale/en-US/devtools/client/sourceeditor.propertiesPK
!<��̎�,�,<����
chrome/en-US/locale/en-US/devtools/client/startup.propertiesPK
!<hՒ:��5��
�
chrome/en-US/locale/en-US/devtools/client/storage.dtdPK
!<MX~Z<��^�
chrome/en-US/locale/en-US/devtools/client/storage.propertiesPK
!<g��)R
R
9���
chrome/en-US/locale/en-US/devtools/client/styleeditor.dtdPK
!<VZѯ	�	@��mchrome/en-US/locale/en-US/devtools/client/styleeditor.propertiesPK
!<�|��+/+/5��z%chrome/en-US/locale/en-US/devtools/client/toolbox.dtdPK
!<��Y�#�#<���Tchrome/en-US/locale/en-US/devtools/client/toolbox.propertiesPK
!<R"�--8��ychrome/en-US/locale/en-US/devtools/client/webConsole.dtdPK
!<J�}�**<����chrome/en-US/locale/en-US/devtools/client/webaudioeditor.dtdPK
!<��C��#�chrome/en-US/locale/en-US/devtools/client/webaudioeditor.propertiesPK
!<����=�=?����chrome/en-US/locale/en-US/devtools/client/webconsole.propertiesPK
!<�� ���4����chrome/en-US/locale/en-US/devtools/client/webide.dtdPK
!<x�0LL;���chrome/en-US/locale/en-US/devtools/client/webide.propertiesPK
!<a��X
X
9��|chrome/en-US/locale/en-US/devtools/shared/csscoverage.dtdPK
!<�g��@��+chrome/en-US/locale/en-US/devtools/shared/csscoverage.propertiesPK
!<�:�
�
=��>chrome/en-US/locale/en-US/devtools/shared/debugger.propertiesPK
!<i�P?���$chrome/en-US/locale/en-US/devtools/shared/eyedropper.propertiesPK
!<xi/�9�99���'chrome/en-US/locale/en-US/devtools/shared/gcli.propertiesPK
!<�j�5-5-A��&bchrome/en-US/locale/en-US/devtools/shared/gclicommands.propertiesPK
!<�zx�;����
chrome/en-US/locale/en-US/devtools/shared/shared.propertiesPK
!<q���$�$C��+�
chrome/en-US/locale/en-US/devtools/shared/styleinspector.propertiesPK
!<�.��9��H�
chrome/en-US/locale/en-US/devtools/shim/aboutdevtools.dtdPK
!<�H6�	�	@��"�
chrome/en-US/locale/en-US/devtools/shim/aboutdevtools.propertiesPK
!<,����/��h�
chrome/en-US/locale/pdfviewer/chrome.propertiesPK
!<j]��ss/��9�
chrome/en-US/locale/pdfviewer/viewer.propertiesPKZ
Z
cLPK
!<����pp$defaults/preferences/webide-prefs.js/* This Source Code Form is subject to the terms of the Mozilla Public
   License, v. 2.0. If a copy of the MPL was not distributed with this
   file, You can obtain one at http://mozilla.org/MPL/2.0/. */

pref("devtools.webide.templatesURL", "https://code.cdn.mozilla.net/templates/list.json");
pref("devtools.webide.autoinstallADBHelper", true);
pref("devtools.webide.autoConnectRuntime", true);
pref("devtools.webide.restoreLastProject", true);
pref("devtools.webide.enableLocalRuntime", false);
pref("devtools.webide.adbAddonURL", "https://ftp.mozilla.org/pub/mozilla.org/labs/fxos-simulator/adb-helper/#OS#/adbhelper-#OS#-latest.xpi");
pref("devtools.webide.adbAddonID", "[email protected]");
pref("devtools.webide.lastConnectedRuntime", "");
pref("devtools.webide.lastSelectedProject", "");
pref("devtools.webide.zoom", "1");
pref("devtools.webide.busyTimeout", 10000);
PK
!<��A�l<l<defaults/preferences/firefox.js//@line 5 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

// XXX Toolkit-specific preferences should be moved into toolkit.js

//@line 9 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

//@line 17 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

//@line 23 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

pref("browser.chromeURL","chrome://browser/content/");
pref("browser.hiddenWindowChromeURL", "chrome://browser/content/hiddenWindow.xul");

// Enables some extra Extension System Logging (can reduce performance)
pref("extensions.logging.enabled", false);

// Disables strict compatibility, making addons compatible-by-default.
pref("extensions.strictCompatibility", false);

// Specifies a minimum maxVersion an addon needs to say it's compatible with
// for it to be compatible by default.
pref("extensions.minCompatibleAppVersion", "4.0");
// Temporary preference to forcibly make themes more safe with Australis even if
// extensions.checkCompatibility=false has been set.
pref("extensions.checkCompatibility.temporaryThemeOverride_minAppVersion", "29.0a1");

pref("xpinstall.customConfirmationUI", true);
pref("extensions.webextPermissionPrompts", true);
pref("extensions.webextOptionalPermissionPrompts", true);

// Preferences for AMO integration
pref("extensions.getAddons.cache.enabled", true);
pref("extensions.getAddons.get.url", "https://services.addons.mozilla.org/api/v3/addons/search/?guid=%IDS%&lang=%LOCALE%");
pref("extensions.getAddons.compatOverides.url", "https://services.addons.mozilla.org/api/v3/addons/compat-override/?guid=%IDS%&lang=%LOCALE%");
pref("extensions.getAddons.search.browseURL", "https://addons.mozilla.org/%LOCALE%/firefox/search?q=%TERMS%&platform=%OS%&appver=%VERSION%");
pref("extensions.webservice.discoverURL", "https://discovery.addons.mozilla.org/%LOCALE%/firefox/discovery/pane/%VERSION%/%OS%/%COMPATIBILITY_MODE%");
pref("extensions.getAddons.link.url", "https://addons.mozilla.org/%LOCALE%/firefox/");
pref("extensions.getAddons.themes.browseURL", "https://addons.mozilla.org/%LOCALE%/firefox/themes/?src=firefox");

pref("extensions.update.autoUpdateDefault", true);

// Check AUS for system add-on updates.
pref("extensions.systemAddon.update.url", "https://aus5.mozilla.org/update/3/SystemAddons/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml");

// Disable add-ons that are not installed by the user in all scopes by default.
// See the SCOPE constants in AddonManager.jsm for values to use here.
pref("extensions.autoDisableScopes", 15);
// Scopes to scan for changes at startup.
pref("extensions.startupScanScopes", 0);

// This is where the profiler WebExtension API will look for breakpad symbols.
// NOTE: deliberately http right now since https://symbols.mozilla.org is not supported.
pref("extensions.geckoProfiler.symbols.url", "http://symbols.mozilla.org/");
pref("extensions.geckoProfiler.acceptedExtensionIds", "[email protected],[email protected]");
//@line 69 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"
pref("extensions.geckoProfiler.getSymbolRules", "localBreakpad,remoteBreakpad,nm");
//@line 73 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"


// Add-on content security policies.
pref("extensions.webextensions.base-content-security-policy", "script-src 'self' https://* moz-extension: blob: filesystem: 'unsafe-eval' 'unsafe-inline'; object-src 'self' https://* moz-extension: blob: filesystem:;");
pref("extensions.webextensions.default-content-security-policy", "script-src 'self'; object-src 'self';");

//@line 84 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

// Extensions that should not be flagged as legacy in about:addons
pref("extensions.legacy.exceptions", "{972ce4c6-7e08-4474-a285-3208198ce6fd},[email protected],@testpilot-containers,jid1-NeEaf3sAHdKHPA@jetpack,@activity-streams,[email protected],@testpilot-addon,@min-vid,[email protected],[email protected],[email protected],[email protected]");

// Require signed add-ons by default
pref("xpinstall.signatures.required", true);
pref("xpinstall.signatures.devInfoURL", "https://wiki.mozilla.org/Addons/Extension_Signing");

// Dictionary download preference
pref("browser.dictionaries.download.url", "https://addons.mozilla.org/%LOCALE%/firefox/dictionaries/");

// At startup, should we check to see if the installation
// date is older than some threshold
pref("app.update.checkInstallTime", true);

// The number of days a binary is permitted to be old without checking is defined in
// firefox-branding.js (app.update.checkInstallTime.days)

// The minimum delay in seconds for the timer to fire between the notification
// of each consumer of the timer manager.
// minimum=30 seconds, default=120 seconds, and maximum=300 seconds
pref("app.update.timerMinimumDelay", 120);

// The minimum delay in milliseconds for the first firing after startup of the timer
// to notify consumers of the timer manager.
// minimum=10 seconds, default=30 seconds, and maximum=120 seconds
pref("app.update.timerFirstInterval", 30000);

// App-specific update preferences

// The interval to check for updates (app.update.interval) is defined in
// firefox-branding.js

// Alternative windowtype for an application update user interface window. When
// a window with this windowtype is open the application update service won't
// open the normal application update user interface window.
pref("app.update.altwindowtype", "Browser:About");

// Enables some extra Application Update Logging (can reduce performance)
pref("app.update.log", false);

// The number of general background check failures to allow before notifying the
// user of the failure. User initiated update checks always notify the user of
// the failure.
pref("app.update.backgroundMaxErrors", 10);

// Whether or not app updates are enabled
//@line 132 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"
pref("app.update.enabled", true);
//@line 136 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

// Whether or not to use the doorhanger application update UI.
pref("app.update.doorhanger", true);

// Ids of the links to the "What's new" update documentation
pref("app.update.link.updateAvailableWhatsNew", "update-available-whats-new");
pref("app.update.link.updateManualWhatsNew", "update-manual-whats-new");

// How many times we should let downloads fail before prompting the user to
// download a fresh installer.
pref("app.update.download.promptMaxAttempts", 2);

// How many times we should let an elevation prompt fail before prompting the user to
// download a fresh installer.
pref("app.update.elevation.promptMaxAttempts", 2);

// If set to true, the Update Service will automatically download updates when
// app updates are enabled per the app.update.enabled preference and if the user
// can apply updates.
pref("app.update.auto", true);

// If set to true, the Update Service will present no UI for any event.
pref("app.update.silent", false);

// app.update.badgeWaitTime is in branding section

// If set to true, the Update Service will apply updates in the background
// when it finishes downloading them. Disabled in bug 1397562.
pref("app.update.staging.enabled", false);

// Update service URL:
pref("app.update.url", "https://aus5.mozilla.org/update/6/%PRODUCT%/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%SYSTEM_CAPABILITIES%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml");
// app.update.url.manual is in branding section
// app.update.url.details is in branding section

// app.update.interval is in branding section
// app.update.promptWaitTime is in branding section

// Show the Update Checking/Ready UI when the user was idle for x seconds
pref("app.update.idletime", 60);

// Whether or not to attempt using the service for updates.
//@line 181 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

// Symmetric (can be overridden by individual extensions) update preferences.
// e.g.
//  extensions.{GUID}.update.enabled
//  extensions.{GUID}.update.url
//  .. etc ..
//
pref("extensions.update.enabled", true);
pref("extensions.update.url", "https://versioncheck.addons.mozilla.org/update/VersionCheck.php?reqVersion=%REQ_VERSION%&id=%ITEM_ID%&version=%ITEM_VERSION%&maxAppVersion=%ITEM_MAXAPPVERSION%&status=%ITEM_STATUS%&appID=%APP_ID%&appVersion=%APP_VERSION%&appOS=%APP_OS%&appABI=%APP_ABI%&locale=%APP_LOCALE%&currentAppVersion=%CURRENT_APP_VERSION%&updateType=%UPDATE_TYPE%&compatMode=%COMPATIBILITY_MODE%");
pref("extensions.update.background.url", "https://versioncheck-bg.addons.mozilla.org/update/VersionCheck.php?reqVersion=%REQ_VERSION%&id=%ITEM_ID%&version=%ITEM_VERSION%&maxAppVersion=%ITEM_MAXAPPVERSION%&status=%ITEM_STATUS%&appID=%APP_ID%&appVersion=%APP_VERSION%&appOS=%APP_OS%&appABI=%APP_ABI%&locale=%APP_LOCALE%&currentAppVersion=%CURRENT_APP_VERSION%&updateType=%UPDATE_TYPE%&compatMode=%COMPATIBILITY_MODE%");
pref("extensions.update.interval", 86400);  // Check for updates to Extensions and
                                            // Themes every day
// Non-symmetric (not shared by extensions) extension-specific [update] preferences
pref("extensions.dss.switchPending", false);    // Non-dynamic switch pending after next
                                                // restart.

pref("extensions.{972ce4c6-7e08-4474-a285-3208198ce6fd}.name", "chrome://browser/locale/browser.properties");
pref("extensions.{972ce4c6-7e08-4474-a285-3208198ce6fd}.description", "chrome://browser/locale/browser.properties");

pref("extensions.webextensions.themes.enabled", true);
pref("extensions.webextensions.themes.icons.buttons", "back,forward,reload,stop,bookmark_star,bookmark_menu,downloads,home,app_menu,cut,copy,paste,new_window,new_private_window,save_page,print,history,full_screen,find,options,addons,developer,synced_tabs,open_file,sidebars,share_page,subscribe,text_encoding,email_link,forget,pocket");

pref("lightweightThemes.update.enabled", true);
pref("lightweightThemes.getMoreURL", "https://addons.mozilla.org/%LOCALE%/firefox/themes");
pref("lightweightThemes.recommendedThemes", "[{\"id\":\"recommended-1\",\"homepageURL\":\"https://addons.mozilla.org/firefox/addon/a-web-browser-renaissance/\",\"headerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/1.header.jpg\",\"textcolor\":\"#000000\",\"accentcolor\":\"#834d29\",\"iconURL\":\"resource:///chrome/browser/content/browser/defaultthemes/1.icon.jpg\",\"previewURL\":\"resource:///chrome/browser/content/browser/defaultthemes/1.preview.jpg\",\"author\":\"Sean.Martell\",\"version\":\"0\"},{\"id\":\"recommended-2\",\"homepageURL\":\"https://addons.mozilla.org/firefox/addon/space-fantasy/\",\"headerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/2.header.jpg\",\"textcolor\":\"#ffffff\",\"accentcolor\":\"#d9d9d9\",\"iconURL\":\"resource:///chrome/browser/content/browser/defaultthemes/2.icon.jpg\",\"previewURL\":\"resource:///chrome/browser/content/browser/defaultthemes/2.preview.jpg\",\"author\":\"fx5800p\",\"version\":\"1.0\"},{\"id\":\"recommended-4\",\"homepageURL\":\"https://addons.mozilla.org/firefox/addon/pastel-gradient/\",\"headerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/4.header.png\",\"textcolor\":\"#000000\",\"accentcolor\":\"#000000\",\"iconURL\":\"resource:///chrome/browser/content/browser/defaultthemes/4.icon.png\",\"previewURL\":\"resource:///chrome/browser/content/browser/defaultthemes/4.preview.png\",\"author\":\"darrinhenein\",\"version\":\"1.0\"}]");

//@line 208 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"
pref("browser.eme.ui.enabled", true);
//@line 212 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

// UI tour experience.
pref("browser.uitour.enabled", true);
pref("browser.uitour.loglevel", "Error");
pref("browser.uitour.requireSecure", true);
pref("browser.uitour.themeOrigin", "https://addons.mozilla.org/%LOCALE%/firefox/themes/");
pref("browser.uitour.url", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/tour/");
// How long to show a Hearbeat survey (two hours, in seconds)
pref("browser.uitour.surveyDuration", 7200);

pref("keyword.enabled", true);
pref("browser.fixup.domainwhitelist.localhost", true);

pref("general.skins.selectedSkin", "classic/1.0");

pref("general.smoothScroll", true);
//@line 229 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"
pref("general.autoScroll", false);
//@line 233 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

pref("browser.stopReloadAnimation.enabled", true);
pref("browser.schedulePressure.enabled", true);
pref("browser.schedulePressure.defaultCount", 3);
pref("browser.schedulePressure.timeoutMs", 300);

// UI density of the browser chrome. This mostly affects toolbarbutton
// and urlbar spacing. The possible values are 0=normal, 1=compact, 2=touch.
pref("browser.uidensity", 0);
// Whether Firefox will automatically override the uidensity to "touch"
// while the user is in a touch environment (such as Windows tablet mode).
pref("browser.touchmode.auto", true);

// At startup, check if we're the default browser and prompt user if not.
pref("browser.shell.checkDefaultBrowser", true);
pref("browser.shell.shortcutFavicons",true);
pref("browser.shell.mostRecentDateSetAsDefault", "");
pref("browser.shell.skipDefaultBrowserCheckOnFirstRun", true);
pref("browser.shell.didSkipDefaultBrowserCheckOnFirstRun", false);
pref("browser.shell.defaultBrowserCheckCount", 0);
pref("browser.defaultbrowser.notificationbar", false);

// 0 = blank, 1 = home (browser.startup.homepage), 2 = last visited page, 3 = resume previous browser session
// The behavior of option 3 is detailed at: http://wiki.mozilla.org/Session_Restore
pref("browser.startup.page",                1);
pref("browser.startup.homepage",            "chrome://branding/locale/browserconfig.properties");
// Whether we should skip the homepage when opening the first-run page
pref("browser.startup.firstrunSkipsHomepage", true);

// Show an about:blank window as early as possible for quick startup feedback.
pref("browser.startup.blankWindow", false);

pref("browser.slowStartup.notificationDisabled", false);
pref("browser.slowStartup.timeThreshold", 20000);
pref("browser.slowStartup.maxSamples", 5);

// This url, if changed, MUST continue to point to an https url. Pulling arbitrary content to inject into
// this page over http opens us up to a man-in-the-middle attack that we'd rather not face. If you are a downstream
// repackager of this code using an alternate snippet url, please keep your users safe
pref("browser.aboutHomeSnippets.updateUrl", "https://snippets.cdn.mozilla.net/%STARTPAGE_VERSION%/%NAME%/%VERSION%/%APPBUILDID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/");

pref("browser.enable_automatic_image_resizing", true);
pref("browser.chrome.site_icons", true);
pref("browser.chrome.favicons", true);
// browser.warnOnQuit == false will override all other possible prompts when quitting or restarting
pref("browser.warnOnQuit", true);
// browser.showQuitWarning specifically controls the quit warning dialog. We
// might still show the window closing dialog with showQuitWarning == false.
pref("browser.showQuitWarning", false);
pref("browser.fullscreen.autohide", true);
pref("browser.overlink-delay", 80);

//@line 286 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"
pref("browser.urlbar.clickSelectsAll", false);
//@line 291 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"
pref("browser.urlbar.doubleClickSelectsAll", true);
//@line 295 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

// Control autoFill behavior
pref("browser.urlbar.autoFill", true);
pref("browser.urlbar.autoFill.typed", true);
pref("browser.urlbar.speculativeConnect.enabled", true);

// 0: Match anywhere (e.g., middle of words)
// 1: Match on word boundaries and then try matching anywhere
// 2: Match only on word boundaries (e.g., after / or .)
// 3: Match at the beginning of the url or title
pref("browser.urlbar.matchBehavior", 1);
pref("browser.urlbar.filter.javascript", true);

// the maximum number of results to show in autocomplete when doing richResults
pref("browser.urlbar.maxRichResults", 10);
// The amount of time (ms) to wait after the user has stopped typing
// before starting to perform autocomplete.  50 is the default set in
// autocomplete.xml.
pref("browser.urlbar.delay", 50);

// The maximum number of historical search results to show.
pref("browser.urlbar.maxHistoricalSearchSuggestions", 0);

// The default behavior for the urlbar can be configured to use any combination
// of the match filters with each additional filter adding more results (union).
pref("browser.urlbar.suggest.history",              true);
pref("browser.urlbar.suggest.bookmark",             true);
pref("browser.urlbar.suggest.openpage",             true);
pref("browser.urlbar.suggest.searches",             true);

// Whether the user made a choice in the old search suggestions opt-in bar.
pref("browser.urlbar.userMadeSearchSuggestionsChoice", false);
// The suggestion opt-out hint will be hidden after being shown 4 times.
pref("browser.urlbar.timesBeforeHidingSuggestionsHint", 4);

// Limit the number of characters sent to the current search engine to fetch
// suggestions.
pref("browser.urlbar.maxCharsForSearchSuggestions", 20);

// Restrictions to current suggestions can also be applied (intersection).
// Typed suggestion works only if history is set to true.
pref("browser.urlbar.suggest.history.onlyTyped",    false);

pref("browser.urlbar.formatting.enabled", true);
pref("browser.urlbar.trimURLs", true);

pref("browser.urlbar.oneOffSearches", true);

// If changed to true, copying the entire URL from the location bar will put the
// human readable (percent-decoded) URL on the clipboard.
pref("browser.urlbar.decodeURLsOnCopy", false);

// Whether or not to move tabs into the active window when using the "Switch to
// Tab" feature of the awesomebar.
pref("browser.urlbar.switchTabs.adoptIntoActiveWindow", false);

// Whether addresses and search results typed into the address bar
// should be opened in new tabs by default.
pref("browser.urlbar.openintab", false);

pref("browser.altClickSave", false);

// Enable logging downloads operations to the Console.
pref("browser.download.loglevel", "Error");

// Number of milliseconds to wait for the http headers (and thus
// the Content-Disposition filename) before giving up and falling back to
// picking a filename without that info in hand so that the user sees some
// feedback from their action.
pref("browser.download.saveLinkAsFilenameTimeout", 4000);

pref("browser.download.useDownloadDir", true);
pref("browser.download.folderList", 1);
pref("browser.download.manager.addToRecentDocs", true);
pref("browser.download.manager.resumeOnWakeDelay", 10000);

// This allows disabling the animated notifications shown by
// the Downloads Indicator when a download starts or completes.
pref("browser.download.animateNotifications", true);

// This records whether or not the panel has been shown at least once.
pref("browser.download.panel.shown", false);

// This controls whether the button is automatically shown/hidden depending
// on whether there are downloads to show.
pref("browser.download.autohideButton", true);

//@line 383 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"
pref("browser.helperApps.deleteTempFileOnExit", true);
//@line 385 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

// search engines URL
pref("browser.search.searchEnginesURL",      "https://addons.mozilla.org/%LOCALE%/firefox/search-engines/");

// pointer to the default engine name
pref("browser.search.defaultenginename",      "chrome://browser-region/locale/region.properties");

// Ordering of Search Engines in the Engine list.
pref("browser.search.order.1",                "chrome://browser-region/locale/region.properties");
pref("browser.search.order.2",                "chrome://browser-region/locale/region.properties");
pref("browser.search.order.3",                "chrome://browser-region/locale/region.properties");

// Market-specific search defaults
pref("browser.search.geoSpecificDefaults", true);
pref("browser.search.geoSpecificDefaults.url", "https://search.services.mozilla.com/1/%APP%/%VERSION%/%CHANNEL%/%LOCALE%/%REGION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%");

// US specific default (used as a fallback if the geoSpecificDefaults request fails).
pref("browser.search.defaultenginename.US",      "data:text/plain,browser.search.defaultenginename.US=Google");
pref("browser.search.order.US.1",                "data:text/plain,browser.search.order.US.1=Google");
pref("browser.search.order.US.2",                "data:text/plain,browser.search.order.US.2=Bing");

// search bar results always open in a new tab
pref("browser.search.openintab", false);

// context menu searches open in the foreground
pref("browser.search.context.loadInBackground", false);

// comma seperated list of of engines to hide in the search panel.
pref("browser.search.hiddenOneOffs", "");

// Mirrors whether the search-container widget is in the navigation toolbar.
pref("browser.search.widget.inNavBar", false);

//@line 421 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

pref("browser.sessionhistory.max_entries", 50);

// Built-in default permissions.
pref("permissions.manager.defaultsUrl", "resource://app/defaults/permissions");

// Set default fallback values for site permissions we want
// the user to be able to globally change.
pref("permissions.default.camera", 0);
pref("permissions.default.microphone", 0);
pref("permissions.default.geo", 0);
pref("permissions.default.desktop-notification", 0);
pref("permissions.default.shortcuts", 0);

// handle links targeting new windows
// 1=current window/tab, 2=new window, 3=new tab in most recent window
pref("browser.link.open_newwindow", 3);

// handle external links (i.e. links opened from a different application)
// default: use browser.link.open_newwindow
// 1-3: see browser.link.open_newwindow for interpretation
pref("browser.link.open_newwindow.override.external", -1);

// 0: no restrictions - divert everything
// 1: don't divert window.open at all
// 2: don't divert window.open with features
pref("browser.link.open_newwindow.restriction", 2);

// If true, this pref causes windows opened by window.open to be forced into new
// tabs (rather than potentially opening separate windows, depending on
// window.open arguments) when the browser is in fullscreen mode.
// We set this differently on Mac because the fullscreen implementation there is
// different.
//@line 457 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"
pref("browser.link.open_newwindow.disabled_in_fullscreen", false);
//@line 459 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

// Tabbed browser
pref("browser.tabs.closeWindowWithLastTab", true);
pref("browser.tabs.insertRelatedAfterCurrent", true);
pref("browser.tabs.warnOnClose", true);
pref("browser.tabs.warnOnCloseOtherTabs", true);
pref("browser.tabs.warnOnOpen", true);
pref("browser.tabs.maxOpenBeforeWarn", 15);
pref("browser.tabs.loadInBackground", true);
pref("browser.tabs.opentabfor.middleclick", true);
pref("browser.tabs.loadDivertedInBackground", false);
pref("browser.tabs.loadBookmarksInBackground", false);
pref("browser.tabs.loadBookmarksInTabs", false);
pref("browser.tabs.tabClipWidth", 140);
pref("browser.tabs.tabMinWidth", 76);
//@line 475 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"
pref("browser.tabs.drawInTitlebar", false);
//@line 479 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

// Offer additional drag space to the user. The drag space
// will only be shown if browser.tabs.drawInTitlebar is true.
pref("browser.tabs.extraDragSpace", false);

// 0 - Disable the tabbar session restore button.
// 1 - Enable the tabbar session restore button.
// 2 - Control group. The tabbar session restore button is disabled,
// but we will record data on other session restore usage.
// To be enabled with shield.
pref("browser.tabs.restorebutton", 0);

// When tabs opened by links in other tabs via a combination of
// browser.link.open_newwindow being set to 3 and target="_blank" etc are
// closed:
// true   return to the tab that opened this tab (its owner)
// false  return to the adjacent tab (old default)
pref("browser.tabs.selectOwnerOnClose", true);

pref("browser.tabs.showAudioPlayingIcon", true);
// This should match Chromium's audio indicator delay.
pref("browser.tabs.delayHidingAudioPlayingIconMS", 3000);

pref("browser.ctrlTab.previews", false);

// By default, do not export HTML at shutdown.
// If true, at shutdown the bookmarks in your menu and toolbar will
// be exported as HTML to the bookmarks.html file.
pref("browser.bookmarks.autoExportHTML",          false);

// The maximum number of daily bookmark backups to
// keep in {PROFILEDIR}/bookmarkbackups. Special values:
// -1: unlimited
//  0: no backups created (and deletes all existing backups)
pref("browser.bookmarks.max_backups",             15);

// Whether menu should close after Ctrl-click, middle-click, etc.
pref("browser.bookmarks.openInTabClosesMenu", true);

// Scripts & Windows prefs
pref("dom.disable_open_during_load",              true);
pref("javascript.options.showInConsole",          true);
//@line 524 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

// This is the pref to control the location bar, change this to true to
// force this - this makes the origin of popup windows more obvious to avoid
// spoofing. We would rather not do it by default because it affects UE for web
// applications, but without it there isn't a really good way to prevent chrome
// spoofing, see bug 337344
pref("dom.disable_window_open_feature.location",  true);
// allow JS to move and resize existing windows
pref("dom.disable_window_move_resize",            false);
// prevent JS from monkeying with window focus, etc
pref("dom.disable_window_flip",                   true);

// popups.policy 1=allow,2=reject
pref("privacy.popups.policy",               1);
pref("privacy.popups.usecustom",            true);
pref("privacy.popups.showBrowserMessage",   true);

pref("privacy.item.cookies",                false);

pref("privacy.clearOnShutdown.history",     true);
pref("privacy.clearOnShutdown.formdata",    true);
pref("privacy.clearOnShutdown.downloads",   true);
pref("privacy.clearOnShutdown.cookies",     true);
pref("privacy.clearOnShutdown.cache",       true);
pref("privacy.clearOnShutdown.sessions",    true);
pref("privacy.clearOnShutdown.offlineApps", false);
pref("privacy.clearOnShutdown.siteSettings", false);
pref("privacy.clearOnShutdown.openWindows", false);

pref("privacy.cpd.history",                 true);
pref("privacy.cpd.formdata",                true);
pref("privacy.cpd.passwords",               false);
pref("privacy.cpd.downloads",               true);
pref("privacy.cpd.cookies",                 true);
pref("privacy.cpd.cache",                   true);
pref("privacy.cpd.sessions",                true);
pref("privacy.cpd.offlineApps",             false);
pref("privacy.cpd.siteSettings",            false);
pref("privacy.cpd.openWindows",             false);

pref("privacy.history.custom",              false);

// What default should we use for the time span in the sanitizer:
// 0 - Clear everything
// 1 - Last Hour
// 2 - Last 2 Hours
// 3 - Last 4 Hours
// 4 - Today
// 5 - Last 5 minutes
// 6 - Last 24 hours
pref("privacy.sanitize.timeSpan", 1);
pref("privacy.sanitize.sanitizeOnShutdown", false);

pref("privacy.sanitize.migrateFx3Prefs",    false);

pref("privacy.panicButton.enabled",         true);

// Time until temporary permissions expire, in ms
pref("privacy.temporary_permission_expire_time_ms",  3600000);

// If Accept-Language should be spoofed by en-US
// 0 - will prompt
// 1 - don't spoof
// 2 - spoof
pref("privacy.spoof_english", 0);

pref("network.proxy.share_proxy_settings",  false); // use the same proxy settings for all protocols

// simple gestures support
pref("browser.gesture.swipe.left", "Browser:BackOrBackDuplicate");
pref("browser.gesture.swipe.right", "Browser:ForwardOrForwardDuplicate");
pref("browser.gesture.swipe.up", "cmd_scrollTop");
pref("browser.gesture.swipe.down", "cmd_scrollBottom");
//@line 601 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"
pref("browser.gesture.pinch.latched", false);
pref("browser.gesture.pinch.threshold", 25);
//@line 605 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"
// Enabled for touch input display zoom.
pref("browser.gesture.pinch.out", "cmd_fullZoomEnlarge");
pref("browser.gesture.pinch.in", "cmd_fullZoomReduce");
pref("browser.gesture.pinch.out.shift", "cmd_fullZoomReset");
pref("browser.gesture.pinch.in.shift", "cmd_fullZoomReset");
//@line 617 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"
pref("browser.gesture.twist.latched", false);
pref("browser.gesture.twist.threshold", 0);
pref("browser.gesture.twist.right", "cmd_gestureRotateRight");
pref("browser.gesture.twist.left", "cmd_gestureRotateLeft");
pref("browser.gesture.twist.end", "cmd_gestureRotateEnd");
pref("browser.gesture.tap", "cmd_fullZoomReset");

pref("browser.snapshots.limit", 0);

// 0: Nothing happens
// 1: Scrolling contents
// 2: Go back or go forward, in your history
// 3: Zoom in or out.
// 4: Treat vertical wheel as horizontal scroll
//@line 648 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"
// On the other platforms (non-macOS), user may use legacy mouse which supports
// only vertical wheel but want to scroll horizontally.  For such users, we
// should provide horizontal scroll with shift+wheel (same as Chrome).
// However, shift+wheel was used for navigating history.  For users who want
// to keep using this feature, let's enable it with alt+wheel.  This is better
// for consistency with macOS users.
pref("mousewheel.with_shift.action", 4);
pref("mousewheel.with_alt.action", 2);
pref("mousewheel.with_meta.action", 1); // win key on Win, Super/Hyper on Linux
//@line 658 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"
pref("mousewheel.with_control.action",3);
pref("mousewheel.with_win.action", 1);

pref("browser.xul.error_pages.enabled", true);
pref("browser.xul.error_pages.expert_bad_cert", false);
pref("browser.xul.error_pages.show_safe_browsing_details_on_load", false);

// Enable captive portal detection.
pref("network.captive-portal-service.enabled", true);

// If true, network link events will change the value of navigator.onLine
pref("network.manage-offline-status", true);

// We want to make sure mail URLs are handled externally...
pref("network.protocol-handler.external.mailto", true); // for mail
pref("network.protocol-handler.external.news", true);   // for news
pref("network.protocol-handler.external.snews", true);  // for secure news
pref("network.protocol-handler.external.nntp", true);   // also news
//@line 679 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

// ...without warning dialogs
pref("network.protocol-handler.warn-external.mailto", false);
pref("network.protocol-handler.warn-external.news", false);
pref("network.protocol-handler.warn-external.snews", false);
pref("network.protocol-handler.warn-external.nntp", false);
//@line 688 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

// By default, all protocol handlers are exposed.  This means that
// the browser will respond to openURL commands for all URL types.
// It will also try to open link clicks inside the browser before
// failing over to the system handlers.
pref("network.protocol-handler.expose-all", true);
pref("network.protocol-handler.expose.mailto", false);
pref("network.protocol-handler.expose.news", false);
pref("network.protocol-handler.expose.snews", false);
pref("network.protocol-handler.expose.nntp", false);

pref("accessibility.typeaheadfind", false);
pref("accessibility.typeaheadfind.timeout", 5000);
pref("accessibility.typeaheadfind.linksonly", false);
pref("accessibility.typeaheadfind.flashBar", 1);

// Accessibility indicator preferences such as support URL, enabled flag.
pref("accessibility.support.url", "https://support.mozilla.org/%LOCALE%/kb/accessibility-services");
pref("accessibility.indicator.enabled", false);

pref("plugins.click_to_play", true);
pref("plugins.testmode", false);

// Should plugins that are hidden show the infobar UI?
pref("plugins.show_infobar", false);

// Should dismissing the hidden plugin infobar suppress it permanently?
pref("plugins.remember_infobar_dismissal", true);

pref("plugin.default.state", 1);

// Plugins bundled in XPIs are enabled by default.
pref("plugin.defaultXpi.state", 2);

// Java is Click-to-Activate by default on all channels.
pref("plugin.state.java", 1);

// Flash is Click-to-Activate by default on all channels.
pref("plugin.state.flash", 1);

// Enables the download and use of the flash blocklists.
pref("plugins.flashBlock.enabled", true);

// Prefer HTML5 video over Flash content, and don't
// load plugin instances with no src declared.
// These prefs are documented in details on all.js.
// With the "follow-ctp" setting, this will only
// apply to users that have plugin.state.flash = 1.
pref("plugins.favorfallback.mode", "follow-ctp");
pref("plugins.favorfallback.rules", "nosrc,video");


//@line 743 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"
pref("browser.preferences.instantApply", true);
//@line 745 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

// Toggling Search bar on and off in about:preferences
pref("browser.preferences.search", true);

pref("browser.preferences.defaultPerformanceSettings.enabled", true);

pref("browser.download.show_plugins_in_list", true);
pref("browser.download.hide_plugins_without_extensions", true);

// Backspace and Shift+Backspace behavior
// 0 goes Back/Forward
// 1 act like PgUp/PgDown
// 2 and other values, nothing
//@line 759 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"
pref("browser.backspace_action", 2);
//@line 763 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

// this will automatically enable inline spellchecking (if it is available) for
// editable elements in HTML
// 0 = spellcheck nothing
// 1 = check multi-line controls [default]
// 2 = check multi/single line controls
pref("layout.spellcheckDefault", 1);

pref("browser.send_pings", false);

/* initial web feed readers list */
pref("browser.contentHandlers.types.0.title", "chrome://browser-region/locale/region.properties");
pref("browser.contentHandlers.types.0.uri", "chrome://browser-region/locale/region.properties");
pref("browser.contentHandlers.types.0.type", "application/vnd.mozilla.maybe.feed");
pref("browser.contentHandlers.types.1.title", "chrome://browser-region/locale/region.properties");
pref("browser.contentHandlers.types.1.uri", "chrome://browser-region/locale/region.properties");
pref("browser.contentHandlers.types.1.type", "application/vnd.mozilla.maybe.feed");
pref("browser.contentHandlers.types.2.title", "chrome://browser-region/locale/region.properties");
pref("browser.contentHandlers.types.2.uri", "chrome://browser-region/locale/region.properties");
pref("browser.contentHandlers.types.2.type", "application/vnd.mozilla.maybe.feed");
pref("browser.contentHandlers.types.3.title", "chrome://browser-region/locale/region.properties");
pref("browser.contentHandlers.types.3.uri", "chrome://browser-region/locale/region.properties");
pref("browser.contentHandlers.types.3.type", "application/vnd.mozilla.maybe.feed");
pref("browser.contentHandlers.types.4.title", "chrome://browser-region/locale/region.properties");
pref("browser.contentHandlers.types.4.uri", "chrome://browser-region/locale/region.properties");
pref("browser.contentHandlers.types.4.type", "application/vnd.mozilla.maybe.feed");
pref("browser.contentHandlers.types.5.title", "chrome://browser-region/locale/region.properties");
pref("browser.contentHandlers.types.5.uri", "chrome://browser-region/locale/region.properties");
pref("browser.contentHandlers.types.5.type", "application/vnd.mozilla.maybe.feed");

pref("browser.feeds.handler", "ask");
pref("browser.videoFeeds.handler", "ask");
pref("browser.audioFeeds.handler", "ask");

// At startup, if the handler service notices that the version number in the
// region.properties file is newer than the version number in the handler
// service datastore, it will add any new handlers it finds in the prefs (as
// seeded by this file) to its datastore.
pref("gecko.handlerService.defaultHandlersVersion", "chrome://browser-region/locale/region.properties");

// The default set of web-based protocol handlers shown in the application
// selection dialog for webcal: ; I've arbitrarily picked 4 default handlers
// per protocol, but if some locale wants more than that (or defaults for some
// protocol not currently listed here), we should go ahead and add those.

// webcal
pref("gecko.handlerService.schemes.webcal.0.name", "chrome://browser-region/locale/region.properties");
pref("gecko.handlerService.schemes.webcal.0.uriTemplate", "chrome://browser-region/locale/region.properties");
pref("gecko.handlerService.schemes.webcal.1.name", "chrome://browser-region/locale/region.properties");
pref("gecko.handlerService.schemes.webcal.1.uriTemplate", "chrome://browser-region/locale/region.properties");
pref("gecko.handlerService.schemes.webcal.2.name", "chrome://browser-region/locale/region.properties");
pref("gecko.handlerService.schemes.webcal.2.uriTemplate", "chrome://browser-region/locale/region.properties");
pref("gecko.handlerService.schemes.webcal.3.name", "chrome://browser-region/locale/region.properties");
pref("gecko.handlerService.schemes.webcal.3.uriTemplate", "chrome://browser-region/locale/region.properties");

// mailto
pref("gecko.handlerService.schemes.mailto.0.name", "chrome://browser-region/locale/region.properties");
pref("gecko.handlerService.schemes.mailto.0.uriTemplate", "chrome://browser-region/locale/region.properties");
pref("gecko.handlerService.schemes.mailto.1.name", "chrome://browser-region/locale/region.properties");
pref("gecko.handlerService.schemes.mailto.1.uriTemplate", "chrome://browser-region/locale/region.properties");
pref("gecko.handlerService.schemes.mailto.2.name", "chrome://browser-region/locale/region.properties");
pref("gecko.handlerService.schemes.mailto.2.uriTemplate", "chrome://browser-region/locale/region.properties");
pref("gecko.handlerService.schemes.mailto.3.name", "chrome://browser-region/locale/region.properties");
pref("gecko.handlerService.schemes.mailto.3.uriTemplate", "chrome://browser-region/locale/region.properties");

// irc
pref("gecko.handlerService.schemes.irc.0.name", "chrome://browser-region/locale/region.properties");
pref("gecko.handlerService.schemes.irc.0.uriTemplate", "chrome://browser-region/locale/region.properties");
pref("gecko.handlerService.schemes.irc.1.name", "chrome://browser-region/locale/region.properties");
pref("gecko.handlerService.schemes.irc.1.uriTemplate", "chrome://browser-region/locale/region.properties");
pref("gecko.handlerService.schemes.irc.2.name", "chrome://browser-region/locale/region.properties");
pref("gecko.handlerService.schemes.irc.2.uriTemplate", "chrome://browser-region/locale/region.properties");
pref("gecko.handlerService.schemes.irc.3.name", "chrome://browser-region/locale/region.properties");
pref("gecko.handlerService.schemes.irc.3.uriTemplate", "chrome://browser-region/locale/region.properties");

// ircs
pref("gecko.handlerService.schemes.ircs.0.name", "chrome://browser-region/locale/region.properties");
pref("gecko.handlerService.schemes.ircs.0.uriTemplate", "chrome://browser-region/locale/region.properties");
pref("gecko.handlerService.schemes.ircs.1.name", "chrome://browser-region/locale/region.properties");
pref("gecko.handlerService.schemes.ircs.1.uriTemplate", "chrome://browser-region/locale/region.properties");
pref("gecko.handlerService.schemes.ircs.2.name", "chrome://browser-region/locale/region.properties");
pref("gecko.handlerService.schemes.ircs.2.uriTemplate", "chrome://browser-region/locale/region.properties");
pref("gecko.handlerService.schemes.ircs.3.name", "chrome://browser-region/locale/region.properties");
pref("gecko.handlerService.schemes.ircs.3.uriTemplate", "chrome://browser-region/locale/region.properties");

pref("browser.geolocation.warning.infoURL", "https://www.mozilla.org/%LOCALE%/firefox/geolocation/");

pref("browser.EULA.version", 3);
pref("browser.rights.version", 3);
pref("browser.rights.3.shown", false);

//@line 858 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

pref("browser.sessionstore.resume_from_crash", true);
pref("browser.sessionstore.resume_session_once", false);

// Minimal interval between two save operations in milliseconds (while the user is active).
pref("browser.sessionstore.interval", 15000); // 15 seconds

// Minimal interval between two save operations in milliseconds (while the user is idle).
pref("browser.sessionstore.interval.idle", 3600000); // 1h

// Time (ms) before we assume that the user is idle and that we don't need to
// collect/save the session quite as often.
pref("browser.sessionstore.idleDelay", 180000); // 3 minutes

// on which sites to save text data, POSTDATA and cookies
// 0 = everywhere, 1 = unencrypted sites, 2 = nowhere
pref("browser.sessionstore.privacy_level", 0);
// how many tabs can be reopened (per window)
pref("browser.sessionstore.max_tabs_undo", 10);
// how many windows can be reopened (per session) - on non-OS X platforms this
// pref may be ignored when dealing with pop-up windows to ensure proper startup
pref("browser.sessionstore.max_windows_undo", 3);
// number of crashes that can occur before the about:sessionrestore page is displayed
// (this pref has no effect if more than 6 hours have passed since the last crash)
pref("browser.sessionstore.max_resumed_crashes", 1);
// number of back button session history entries to restore (-1 = all of them)
pref("browser.sessionstore.max_serialize_back", 10);
// number of forward button session history entries to restore (-1 = all of them)
pref("browser.sessionstore.max_serialize_forward", -1);
// restore_on_demand overrides MAX_CONCURRENT_TAB_RESTORES (sessionstore constant)
// and restore_hidden_tabs. When true, tabs will not be restored until they are
// focused (also applies to tabs that aren't visible). When false, the values
// for MAX_CONCURRENT_TAB_RESTORES and restore_hidden_tabs are respected.
// Selected tabs are always restored regardless of this pref.
pref("browser.sessionstore.restore_on_demand", true);
// Whether to automatically restore hidden tabs (i.e., tabs in other tab groups) or not
pref("browser.sessionstore.restore_hidden_tabs", false);
// If restore_on_demand is set, pinned tabs are restored on startup by default.
// When set to true, this pref overrides that behavior, and pinned tabs will only
// be restored when they are focused.
pref("browser.sessionstore.restore_pinned_tabs_on_demand", false);
// The version at which we performed the latest upgrade backup
pref("browser.sessionstore.upgradeBackup.latestBuildID", "");
// How many upgrade backups should be kept
pref("browser.sessionstore.upgradeBackup.maxUpgradeBackups", 3);
// End-users should not run sessionstore in debug mode
pref("browser.sessionstore.debug", false);
// Causes SessionStore to ignore non-final update messages from
// browser tabs that were not caused by a flush from the parent.
// This is a testing flag and should not be used by end-users.
pref("browser.sessionstore.debug.no_auto_updates", false);
// Forget closed windows/tabs after two weeks
pref("browser.sessionstore.cleanup.forget_closed_after", 1209600000);
// Maximum number of bytes of DOMSessionStorage data we collect per origin.
pref("browser.sessionstore.dom_storage_limit", 2048);
// Amount of failed SessionFile writes until we restart the worker.
pref("browser.sessionstore.max_write_failures", 5);

// allow META refresh by default
pref("accessibility.blockautorefresh", false);

// Whether history is enabled or not.
pref("places.history.enabled", true);

// the (maximum) number of the recent visits to sample
// when calculating frecency
pref("places.frecency.numVisits", 10);

// buckets (in days) for frecency calculation
pref("places.frecency.firstBucketCutoff", 4);
pref("places.frecency.secondBucketCutoff", 14);
pref("places.frecency.thirdBucketCutoff", 31);
pref("places.frecency.fourthBucketCutoff", 90);

// weights for buckets for frecency calculations
pref("places.frecency.firstBucketWeight", 100);
pref("places.frecency.secondBucketWeight", 70);
pref("places.frecency.thirdBucketWeight", 50);
pref("places.frecency.fourthBucketWeight", 30);
pref("places.frecency.defaultBucketWeight", 10);

// bonus (in percent) for visit transition types for frecency calculations
pref("places.frecency.embedVisitBonus", 0);
pref("places.frecency.framedLinkVisitBonus", 0);
pref("places.frecency.linkVisitBonus", 100);
pref("places.frecency.typedVisitBonus", 2000);
// The bookmarks bonus is always added on top of any other bonus, including
// the redirect source and the typed ones.
pref("places.frecency.bookmarkVisitBonus", 75);
// The redirect source bonus overwrites any transition bonus.
// 0 would hide these pages, instead we want them low ranked.  Thus we use
// linkVisitBonus - bookmarkVisitBonus, so that a bookmarked source is in par
// with a common link.
pref("places.frecency.redirectSourceVisitBonus", 25);
pref("places.frecency.downloadVisitBonus", 0);
// The perm/temp redirects here relate to redirect targets, not sources.
pref("places.frecency.permRedirectVisitBonus", 50);
pref("places.frecency.tempRedirectVisitBonus", 40);
pref("places.frecency.reloadVisitBonus", 0);
pref("places.frecency.defaultVisitBonus", 0);

// bonus (in percent) for place types for frecency calculations
pref("places.frecency.unvisitedBookmarkBonus", 140);
pref("places.frecency.unvisitedTypedBonus", 200);

// Controls behavior of the "Add Exception" dialog launched from SSL error pages
// 0 - don't pre-populate anything
// 1 - pre-populate site URL, but don't fetch certificate
// 2 - pre-populate site URL and pre-fetch certificate
pref("browser.ssl_override_behavior", 2);

// True if the user should be prompted when a web application supports
// offline apps.
pref("browser.offline-apps.notify", true);

// if true, use full page zoom instead of text zoom
pref("browser.zoom.full", true);

// Whether or not to save and restore zoom levels on a per-site basis.
pref("browser.zoom.siteSpecific", true);

// Whether or not to update background tabs to the current zoom level.
pref("browser.zoom.updateBackgroundTabs", true);

// The breakpad report server to link to in about:crashes
pref("breakpad.reportURL", "https://crash-stats.mozilla.com/report/index/");

// URL for "Learn More" for DataCollection
pref("toolkit.datacollection.infoURL",
     "https://www.mozilla.org/legal/privacy/firefox.html");

// URL for "Learn More" for Crash Reporter
pref("toolkit.crashreporter.infoURL",
     "https://www.mozilla.org/legal/privacy/firefox.html#crash-reporter");

// base URL for web-based support pages
pref("app.support.baseURL", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/");

// base url for web-based feedback pages
//@line 1000 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"
pref("app.feedback.baseURL", "https://input.mozilla.org/%LOCALE%/feedback/%APP%/%VERSION%/");
//@line 1002 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

// base URL for web-based marketing pages
pref("app.productInfo.baseURL", "https://www.mozilla.org/firefox/features/");

// Name of alternate about: page for certificate errors (when undefined, defaults to about:neterror)
pref("security.alternate_certificate_error_page", "certerror");

// Whether to start the private browsing mode at application startup
pref("browser.privatebrowsing.autostart", false);

// Don't try to alter this pref, it'll be reset the next time you use the
// bookmarking dialog
pref("browser.bookmarks.editDialog.firstEditField", "namePicker");

pref("dom.ipc.plugins.flash.disable-protected-mode", false);

// Feature-disable the protected-mode auto-flip
pref("browser.flash-protected-mode-flip.enable", false);

// Whether we've already flipped protected mode automatically
pref("browser.flash-protected-mode-flip.done", false);

pref("dom.ipc.shims.enabledWarnings", false);

//@line 1067 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

//@line 1085 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

//@line 1089 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

//@line 1091 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"
// This pref is introduced as part of bug 742434, the naming is inspired from
// its Windows/Mac counterpart, but on Linux it's an integer which means:
// 0 -> "no sandbox"
// 1 -> "content sandbox using seccomp-bpf when available"
// 2 -> "seccomp-bpf + write file broker"
// 3 -> "seccomp-bpf + read/write file brokering"
// 4 -> all of the above + network/socket restrictions
// Content sandboxing on Linux is currently in the stage of
// 'just getting it enabled', which includes a very permissive whitelist. We
// enable seccomp-bpf on nightly to see if everything is running, or if we need
// to whitelist more system calls.
//
// So the purpose of this setting is to allow nightly users to disable the
// sandbox while we fix their problems. This way, they won't have to wait for
// another nightly release which disables seccomp-bpf again.
//
// This setting may not be required anymore once we decide to permanently
// enable the content sandbox.
pref("security.sandbox.content.level", 4);
pref("security.sandbox.content.write_path_whitelist", "");
pref("security.sandbox.content.read_path_whitelist", "");
pref("security.sandbox.content.syscall_whitelist", "");
//@line 1114 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

//@line 1116 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"
// ID (a UUID when set by gecko) that is used to form the name of a
// sandbox-writable temporary directory to be used by content processes
// when a temporary writable file is required in a level 1 sandbox.
pref("security.sandbox.content.tempDirSuffix", "");
//@line 1121 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

//@line 1123 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"
// This pref determines if messages relevant to sandbox violations are
// logged.
//@line 1128 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"
pref("security.sandbox.logging.enabled", true);
//@line 1131 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

// This pref governs whether we attempt to work around problems caused by
// plugins using OS calls to manipulate the cursor while running out-of-
// process.  These workarounds all involve intercepting (hooking) certain
// OS calls in the plugin process, then arranging to make certain OS calls
// in the browser process.  Eventually plugins will be required to use the
// NPAPI to manipulate the cursor, and these workarounds will be removed.
// See bug 621117.
//@line 1142 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

//@line 1154 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

// Preferences to be synced by default
pref("services.sync.prefs.sync.accessibility.blockautorefresh", true);
pref("services.sync.prefs.sync.accessibility.browsewithcaret", true);
pref("services.sync.prefs.sync.accessibility.typeaheadfind", true);
pref("services.sync.prefs.sync.accessibility.typeaheadfind.linksonly", true);
pref("services.sync.prefs.sync.addons.ignoreUserEnabledChanges", true);
// The addons prefs related to repository verification are intentionally
// not synced for security reasons. If a system is compromised, a user
// could weaken the pref locally, install an add-on from an untrusted
// source, and this would propagate automatically to other,
// uncompromised Sync-connected devices.
pref("services.sync.prefs.sync.browser.ctrlTab.previews", true);
pref("services.sync.prefs.sync.browser.download.useDownloadDir", true);
pref("services.sync.prefs.sync.browser.formfill.enable", true);
pref("services.sync.prefs.sync.browser.link.open_newwindow", true);
pref("services.sync.prefs.sync.browser.newtabpage.enabled", true);
pref("services.sync.prefs.sync.browser.newtabpage.pinned", true);
pref("services.sync.prefs.sync.browser.offline-apps.notify", true);
pref("services.sync.prefs.sync.browser.safebrowsing.phishing.enabled", true);
pref("services.sync.prefs.sync.browser.safebrowsing.malware.enabled", true);
pref("services.sync.prefs.sync.browser.safebrowsing.downloads.enabled", true);
pref("services.sync.prefs.sync.browser.safebrowsing.passwords.enabled", true);
pref("services.sync.prefs.sync.browser.search.update", true);
pref("services.sync.prefs.sync.browser.sessionstore.restore_on_demand", true);
pref("services.sync.prefs.sync.browser.startup.homepage", true);
pref("services.sync.prefs.sync.browser.startup.page", true);
pref("services.sync.prefs.sync.browser.tabs.loadInBackground", true);
pref("services.sync.prefs.sync.browser.tabs.warnOnClose", true);
pref("services.sync.prefs.sync.browser.tabs.warnOnOpen", true);
pref("services.sync.prefs.sync.browser.urlbar.autocomplete.enabled", true);
pref("services.sync.prefs.sync.browser.urlbar.matchBuckets", true);
pref("services.sync.prefs.sync.browser.urlbar.maxRichResults", true);
pref("services.sync.prefs.sync.browser.urlbar.suggest.bookmark", true);
pref("services.sync.prefs.sync.browser.urlbar.suggest.history", true);
pref("services.sync.prefs.sync.browser.urlbar.suggest.history.onlyTyped", true);
pref("services.sync.prefs.sync.browser.urlbar.suggest.openpage", true);
pref("services.sync.prefs.sync.browser.urlbar.suggest.searches", true);
pref("services.sync.prefs.sync.dom.disable_open_during_load", true);
pref("services.sync.prefs.sync.dom.disable_window_flip", true);
pref("services.sync.prefs.sync.dom.disable_window_move_resize", true);
pref("services.sync.prefs.sync.dom.event.contextmenu.enabled", true);
pref("services.sync.prefs.sync.extensions.personas.current", true);
pref("services.sync.prefs.sync.extensions.update.enabled", true);
pref("services.sync.prefs.sync.intl.accept_languages", true);
pref("services.sync.prefs.sync.layout.spellcheckDefault", true);
pref("services.sync.prefs.sync.lightweightThemes.selectedThemeID", true);
pref("services.sync.prefs.sync.lightweightThemes.usedThemes", true);
pref("services.sync.prefs.sync.network.cookie.cookieBehavior", true);
pref("services.sync.prefs.sync.network.cookie.lifetimePolicy", true);
pref("services.sync.prefs.sync.network.cookie.lifetime.days", true);
pref("services.sync.prefs.sync.network.cookie.thirdparty.sessionOnly", true);
pref("services.sync.prefs.sync.permissions.default.image", true);
pref("services.sync.prefs.sync.pref.advanced.images.disable_button.view_image", true);
pref("services.sync.prefs.sync.pref.advanced.javascript.disable_button.advanced", true);
pref("services.sync.prefs.sync.pref.downloads.disable_button.edit_actions", true);
pref("services.sync.prefs.sync.pref.privacy.disable_button.cookie_exceptions", true);
pref("services.sync.prefs.sync.privacy.clearOnShutdown.cache", true);
pref("services.sync.prefs.sync.privacy.clearOnShutdown.cookies", true);
pref("services.sync.prefs.sync.privacy.clearOnShutdown.downloads", true);
pref("services.sync.prefs.sync.privacy.clearOnShutdown.formdata", true);
pref("services.sync.prefs.sync.privacy.clearOnShutdown.history", true);
pref("services.sync.prefs.sync.privacy.clearOnShutdown.offlineApps", true);
pref("services.sync.prefs.sync.privacy.clearOnShutdown.sessions", true);
pref("services.sync.prefs.sync.privacy.clearOnShutdown.siteSettings", true);
pref("services.sync.prefs.sync.privacy.donottrackheader.enabled", true);
pref("services.sync.prefs.sync.privacy.sanitize.sanitizeOnShutdown", true);
pref("services.sync.prefs.sync.privacy.trackingprotection.enabled", true);
pref("services.sync.prefs.sync.privacy.trackingprotection.pbmode.enabled", true);
pref("services.sync.prefs.sync.privacy.resistFingerprinting", true);
pref("services.sync.prefs.sync.privacy.reduceTimerPrecision", true);
pref("services.sync.prefs.sync.privacy.resistFingerprinting.reduceTimerPrecision.microseconds", true);
pref("services.sync.prefs.sync.privacy.resistFingerprinting.reduceTimerPrecision.jitter", true);
pref("services.sync.prefs.sync.security.OCSP.enabled", true);
pref("services.sync.prefs.sync.security.OCSP.require", true);
pref("services.sync.prefs.sync.security.default_personal_cert", true);
pref("services.sync.prefs.sync.security.tls.version.min", true);
pref("services.sync.prefs.sync.security.tls.version.max", true);
pref("services.sync.prefs.sync.services.sync.syncedTabs.showRemoteIcons", true);
pref("services.sync.prefs.sync.signon.rememberSignons", true);
pref("services.sync.prefs.sync.spellchecker.dictionary", true);
pref("services.sync.prefs.sync.xpinstall.whitelist.required", true);

// A preference that controls whether we should show the icon for a remote tab.
// This pref has no UI but exists because some people may be concerned that
// fetching these icons to show remote tabs may leak information about that
// user's tabs and bookmarks. Note this pref is also synced.
pref("services.sync.syncedTabs.showRemoteIcons", true);

// Developer edition preferences
//@line 1248 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"
pref("lightweightThemes.selectedThemeID", "", sticky);
//@line 1250 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

// Whether the character encoding menu is under the main Firefox button. This
// preference is a string so that localizers can alter it.
pref("browser.menu.showCharacterEncoding", "chrome://browser/locale/browser.properties");

// Allow using tab-modal prompts when possible.
pref("prompts.tab_modal.enabled", true);

// Activates preloading of the new tab url.
pref("browser.newtab.preload", true);

// Indicates if about:newtab shows content (enabled) or just blank
pref("browser.newtabpage.enabled", true);

// Activity Stream prefs that control to which page to redirect
pref("browser.newtabpage.activity-stream.prerender", true);
//@line 1273 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

pref("browser.library.activity-stream.enabled", true);

// Enable the DOM fullscreen API.
pref("full-screen-api.enabled", true);

// Startup Crash Tracking
// number of startup crashes that can occur before starting into safe mode automatically
// (this pref has no effect if more than 6 hours have passed since the last crash)
pref("toolkit.startup.max_resumed_crashes", 3);

// Whether we use pdfium to view content with the pdf mime type.
// Note: if the pref is set to false while Firefox is open, it won't
// take effect until there are no open pdfium tabs.
pref("pdfium.enabled", false);

// Completely disable pdf.js as an option to preview pdfs within firefox.
// Note: if this is not disabled it does not necessarily mean pdf.js is the pdf
// handler just that it is an option.
pref("pdfjs.disabled", false);
// Used by pdf.js to know the first time firefox is run with it installed so it
// can become the default pdf viewer.
pref("pdfjs.firstRun", true);
// The values of preferredAction and alwaysAskBeforeHandling before pdf.js
// became the default.
pref("pdfjs.previousHandler.preferredAction", 0);
pref("pdfjs.previousHandler.alwaysAskBeforeHandling", false);

// The maximum amount of decoded image data we'll willingly keep around (we
// might keep around more than this, but we'll try to get down to this value).
// (This is intentionally on the high side; see bug 746055.)
pref("image.mem.max_decoded_image_kb", 256000);

// Is the sidebar positioned ahead of the content browser
pref("sidebar.position_start", true);

// Block insecure active content on https pages
pref("security.mixed_content.block_active_content", true);

// Show degraded UI for http pages with password fields.
pref("security.insecure_password.ui.enabled", true);

// Show in-content login form warning UI for insecure login fields
pref("security.insecure_field_warning.contextual.enabled", true);

// Show degraded UI for http pages; disabled for now
pref("security.insecure_connection_icon.enabled", false);
// Show degraded UI for http pages in private mode only for Nightly: Bug 1434626
//@line 1324 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"
pref("security.insecure_connection_icon.pbmode.enabled", false);
//@line 1326 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

// Show "Not Secure" text for http pages; disabled for now
pref("security.insecure_connection_text.enabled", false);
pref("security.insecure_connection_text.pbmode.enabled", false);

// 1 = allow MITM for certificate pinning checks.
pref("security.cert_pinning.enforcement_level", 1);


// Override the Gecko-default value of false for Firefox.
pref("plain_text.wrap_long_lines", true);

// If this turns true, Moz*Gesture events are not called stopPropagation()
// before content.
pref("dom.debug.propagate_gesture_events_through_content", false);

// All the Geolocation preferences are here.
//

// Geolocation preferences for the RELEASE and "later" Beta channels.
// Some of these prefs are specified even though they are redundant; they are
// here for clarity and end-user experiments.
//@line 1349 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"
pref("geo.wifi.uri", "https://www.googleapis.com/geolocation/v1/geolocate?key=%GOOGLE_API_KEY%");

//@line 1354 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

//@line 1358 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

//@line 1360 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"
pref("geo.provider.use_gpsd", false);
//@line 1362 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

//@line 1383 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

// Necko IPC security checks only needed for app isolation for cookies/cache/etc:
// currently irrelevant for desktop e10s
pref("network.disable.ipc.security", true);

// CustomizableUI debug logging.
pref("browser.uiCustomization.debug", false);

// CustomizableUI state of the browser's user interface
pref("browser.uiCustomization.state", "");

// If set to false, FxAccounts and Sync will be unavailable.
// A restart is mandatory after flipping that preference.
pref("identity.fxaccounts.enabled", true);

// The remote FxA root content URL. Must use HTTPS.
pref("identity.fxaccounts.remote.root", "https://accounts.firefox.com/");

// The value of the context query parameter passed in fxa requests.
pref("identity.fxaccounts.contextParam", "fx_desktop_v3");

// The remote URL of the FxA Profile Server
pref("identity.fxaccounts.remote.profile.uri", "https://profile.accounts.firefox.com/v1");

// The remote URL of the FxA OAuth Server
pref("identity.fxaccounts.remote.oauth.uri", "https://oauth.accounts.firefox.com/v1");

// Token server used by the FxA Sync identity.
pref("identity.sync.tokenserver.uri", "https://token.services.mozilla.com/1.0/sync/1.5");

// URLs for promo links to mobile browsers. Note that consumers are expected to
// append a value for utm_campaign.
pref("identity.mobilepromo.android", "https://www.mozilla.org/firefox/android/?utm_source=firefox-browser&utm_medium=firefox-browser&utm_campaign=");
pref("identity.mobilepromo.ios", "https://www.mozilla.org/firefox/ios/?utm_source=firefox-browser&utm_medium=firefox-browser&utm_campaign=");

// Migrate any existing Firefox Account data from the default profile to the
// Developer Edition profile.
//@line 1423 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"
pref("identity.fxaccounts.migrateToDevEdition", false);
//@line 1425 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

// On GTK, we now default to showing the menubar only when alt is pressed:
//@line 1428 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"
pref("ui.key.menuAccessKeyFocuses", true);
//@line 1430 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

// Encrypted media extensions.
//@line 1433 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"
// On Linux EME is visible but disabled by default. This is so that the
// "Play DRM content" checkbox in the Firefox UI is unchecked by default.
// DRM requires downloading and installing proprietary binaries, which
// users on an open source operating systems didn't opt into. The first
// time a site using EME is encountered, the user will be prompted to
// enable DRM, whereupon the EME plugin binaries will be downloaded if
// permission is granted.
pref("media.eme.enabled", false);
//@line 1444 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

//@line 1448 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"
pref("media.eme.vp9-in-mp4.enabled", false);
//@line 1450 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

pref("media.eme.hdcp-policy-check.enabled", false);

// Whether we should run a test-pattern through EME GMPs before assuming they'll
// decode H.264.
pref("media.gmp.trial-create.enabled", true);

// Note: when media.gmp-*.visible is true, provided we're running on a
// supported platform/OS version, the corresponding CDM appears in the
// plugins list, Firefox will download the GMP/CDM if enabled, and our
// UI to re-enable EME prompts the user to re-enable EME if it's disabled
// and script requests EME. If *.visible is false, we won't show the UI
// to enable the CDM if its disabled; it's as if the keysystem is completely
// unsupported.

//@line 1466 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"
pref("media.gmp-widevinecdm.visible", true);
pref("media.gmp-widevinecdm.enabled", true);
//@line 1469 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

// Play with different values of the decay time and get telemetry,
// 0 means to randomize (and persist) the experiment value in users' profiles,
// -1 means no experiment is run and we use the preferred value for frecency (6h)
pref("browser.cache.frecency_experiment", 0);

pref("browser.translation.detectLanguage", false);
pref("browser.translation.neverForLanguages", "");
// Show the translation UI bits, like the info bar, notification icon and preferences.
pref("browser.translation.ui.show", false);
// Allows to define the translation engine. Bing is default, Yandex may optionally switched on.
pref("browser.translation.engine", "bing");

// Telemetry settings.
// Determines if Telemetry pings can be archived locally.
pref("toolkit.telemetry.archive.enabled", true);
// Enables sending the shutdown ping when Firefox shuts down.
pref("toolkit.telemetry.shutdownPingSender.enabled", true);
// Enables sending the shutdown ping using the pingsender from the first session.
pref("toolkit.telemetry.shutdownPingSender.enabledFirstSession", false);
// Enables sending a duplicate of the first shutdown ping from the first session.
pref("toolkit.telemetry.firstShutdownPing.enabled", true);
// Enables sending the 'new-profile' ping on new profiles.
pref("toolkit.telemetry.newProfilePing.enabled", true);
// Enables sending 'update' pings on Firefox updates.
pref("toolkit.telemetry.updatePing.enabled", true);
// Enables sending 'bhr' pings when the browser hangs.
pref("toolkit.telemetry.bhrPing.enabled", true);
// Enables using Hybrid Content Telemetry from Mozilla privileged pages.
pref("toolkit.telemetry.hybridContent.enabled", true);

// Telemetry experiments settings.
pref("experiments.enabled", true);
pref("experiments.manifest.fetchIntervalSeconds", 86400);
pref("experiments.manifest.uri", "https://telemetry-experiment.cdn.mozilla.net/manifest/v1/firefox/%VERSION%/%CHANNEL%");
// Whether experiments are supported by the current application profile.
pref("experiments.supported", true);

// Ping Centre Telemetry settings.
pref("browser.ping-centre.telemetry", true);
pref("browser.ping-centre.log", false);
pref("browser.ping-centre.staging.endpoint", "https://onyx_tiles.stage.mozaws.net/v3/links/ping-centre");
pref("browser.ping-centre.production.endpoint", "https://tiles.services.mozilla.com/v3/links/ping-centre");

// Enable GMP support in the addon manager.
pref("media.gmp-provider.enabled", true);

pref("privacy.trackingprotection.ui.enabled", true);
pref("privacy.trackingprotection.introCount", 0);
pref("privacy.trackingprotection.introURL", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/tracking-protection/start/");

// Enable Contextual Identity Containers
//@line 1529 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"
pref("privacy.userContext.enabled", false);
pref("privacy.userContext.ui.enabled", false);
pref("privacy.usercontext.about_newtab_segregation.enabled", false);

// 0 disables long press, 1 when clicked, the menu is shown, 2 the menu is shown after X milliseconds.
pref("privacy.userContext.longPressBehavior", 0);
//@line 1536 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"
pref("privacy.userContext.extension", "");

// Start the browser in e10s mode
pref("browser.tabs.remote.autostart", true);
pref("browser.tabs.remote.desktopbehavior", true);

// For speculatively warming up tabs to improve perceived
// performance while using the async tab switcher.
// Disabled until bug 1397426 is fixed.
pref("browser.tabs.remote.warmup.enabled", false);
pref("browser.tabs.remote.warmup.maxTabs", 3);
pref("browser.tabs.remote.warmup.unloadDelayMs", 2000);

// For the about:tabcrashed page
pref("browser.tabs.crashReporting.sendReport", true);
pref("browser.tabs.crashReporting.includeURL", false);
pref("browser.tabs.crashReporting.requestEmail", false);
pref("browser.tabs.crashReporting.emailMe", false);
pref("browser.tabs.crashReporting.email", "");

// But don't allow non-MPC extensions by default on Nightly
//@line 1560 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

pref("extensions.legacy.enabled", false);

// How often to check for CPOW timeouts. CPOWs are only timed out by
// the hang monitor.
pref("dom.ipc.cpow.timeout", 500);

// Causes access on unsafe CPOWs from browser code to throw by default.
pref("dom.ipc.cpows.forbid-unsafe-from-browser", true);

// Don't allow add-ons marked as multiprocessCompatible to use CPOWs.
pref("dom.ipc.cpows.forbid-cpows-in-compat-addons", true);

// ...except for these add-ons:
pref("dom.ipc.cpows.allow-cpows-in-compat-addons", "{b9db16a4-6edc-47ec-a1f4-b86292ed211d},[email protected],{DDC359D1-844A-42a7-9AA1-88A850A938A8},privateTab@infocatcher,mousegesturessuite@lemon_juice.addons.mozilla.org,[email protected],[email protected],{AE93811A-5C9A-4d34-8462-F7B864FC4696},[email protected],{EF522540-89F5-46b9-B6FE-1829E2B572C6},{677a8f98-fd64-40b0-a883-b8c95d0cbf17},[email protected],fx-devtools,[email protected],{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d},{dc572301-7619-498c-a57d-39143191b318},[email protected],{86095750-AD15-46d8-BF32-C0789F7E6A32},[email protected],{91aa5abe-9de4-4347-b7b5-322c38dd9271},[email protected],[email protected],[email protected],{7b1bf0b6-a1b9-42b0-b75d-252036438bdc},s3crypto@data,{1e0fd655-5aea-4b4c-a583-f76ef1e3af9c},[email protected],{aff87fa2-a58e-4edd-b852-0a20203c1e17},{1018e4d6-728f-4b20-ad56-37578a4de76b},[email protected],[email protected],{b2e69492-2358-071a-7056-24ad0c3defb1},[email protected],{e4a8a97b-f2ed-450b-b12d-ee082ba24781},jid1-f3mYMbCpz2AZYl@jetpack,{8c550e28-88c9-4764-bb52-aa489cf2efcd},{37fa1426-b82d-11db-8314-0800200c9a66},{ac2cfa60-bc96-11e0-962b-0800200c9a66},[email protected],[email protected],[email protected],{fc6339b8-9581-4fc7-b824-dffcb091fcb7},[email protected],[email protected],[email protected],{73a6fe31-595d-460b-a920-fcc0f8843232},{46551EC9-40F0-4e47-8E18-8E5CF550CFB8},[email protected],@screen_maker,[email protected],[email protected],s3google@translator,[email protected],{C1A2A613-35F1-4FCF-B27F-2840527B6556},[email protected],[email protected],[email protected],[email protected],[email protected],{9AA46F4F-4DC7-4c06-97AF-5035170634FE},jid1-CikLKKPVkw6ipw@jetpack,[email protected],[email protected],{A065A84F-95B6-433A-A0C8-4C040B77CE8A},[email protected]");

// Enable e10s hang monitoring (slow script checking and plugin hang
// detection).
pref("dom.ipc.processHangMonitor", true);

//@line 1585 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"
pref("dom.ipc.reportProcessHangs", true);
//@line 1587 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

// Don't limit how many nodes we care about on desktop:
pref("reader.parse-node-limit", 0);

// On desktop, we want the URLs to be included here for ease of debugging,
// and because (normally) these errors are not persisted anywhere.
pref("reader.errors.includeURLs", true);

pref("dom.serviceWorkers.enabled", true);

// Enable Push API.
pref("dom.push.enabled", true);

// These are the thumbnail width/height set in about:newtab.
// If you change this, ENSURE IT IS THE SAME SIZE SET
// by about:newtab. These values are in CSS pixels.
pref("toolkit.pageThumbs.minWidth", 280);
pref("toolkit.pageThumbs.minHeight", 190);

// Enable speech synthesis
pref("media.webspeech.synth.enabled", true);

pref("browser.esedbreader.loglevel", "Error");

pref("browser.laterrun.enabled", false);

pref("dom.ipc.processPrelaunch.enabled", true);

pref("browser.migrate.automigrate.enabled", false);
// 4 here means the suggestion notification will be automatically
// hidden the 4th day, so it will actually be shown on 3 different days.
pref("browser.migrate.automigrate.daysToOfferUndo", 4);
pref("browser.migrate.automigrate.ui.enabled", true);
pref("browser.migrate.automigrate.inpage.ui.enabled", false);

// See comments in bug 1340115 on how we got to these numbers.
pref("browser.migrate.chrome.history.limit", 2000);
pref("browser.migrate.chrome.history.maxAgeInDays", 180);

// Enable browser frames for use on desktop.  Only exposed to chrome callers.
pref("dom.mozBrowserFramesEnabled", true);

pref("extensions.pocket.enabled", true);

pref("signon.schemeUpgrades", true);

// Enable the "Simplify Page" feature in Print Preview. This feature
// is disabled by default in toolkit.
pref("print.use_simplify_page", true);

// Space separated list of URLS that are allowed to send objects (instead of
// only strings) through webchannels. This list is duplicated in mobile/android/app/mobile.js
pref("webchannel.allowObject.urlWhitelist", "https://content.cdn.mozilla.net https://input.mozilla.org https://support.mozilla.org https://install.mozilla.org");

// Whether or not the browser should scan for unsubmitted
// crash reports, and then show a notification for submitting
// those reports.
//@line 1647 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"
pref("browser.crashReports.unsubmittedCheck.enabled", false);
//@line 1649 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

// chancesUntilSuppress is how many times we'll show the unsubmitted
// crash report notification across different days and shutdown
// without a user choice before we suppress the notification for
// some number of days.
pref("browser.crashReports.unsubmittedCheck.chancesUntilSuppress", 4);
pref("browser.crashReports.unsubmittedCheck.autoSubmit2", false);

// Preferences for the form autofill system extension
// The truthy values of "extensions.formautofill.available" are "on" and "detect",
// any other value means autofill isn't available.
// "detect" means it's enabled if conditions defined in the extension are met.
//@line 1665 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"
pref("extensions.formautofill.available", "detect");
pref("extensions.formautofill.creditCards.available", false);
//@line 1668 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"
pref("extensions.formautofill.addresses.enabled", true);
pref("extensions.formautofill.creditCards.enabled", true);
// Pref for shield/heartbeat to recognize users who have used Credit Card
// Autofill. The valid values can be:
// 0: none
// 1: submitted a manually-filled credit card form (but didn't see the doorhanger
//    because of a duplicate profile in the storage)
// 2: saw the doorhanger
// 3: submitted an autofill'ed credit card form
pref("extensions.formautofill.creditCards.used", 0);
pref("extensions.formautofill.firstTimeUse", true);
pref("extensions.formautofill.heuristics.enabled", true);
pref("extensions.formautofill.section.enabled", true);
pref("extensions.formautofill.loglevel", "Warn");

//@line 1689 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"
pref("extensions.formautofill.supportedCountries", "US");
pref("extensions.formautofill.supportRTL", false);
//@line 1692 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"

// Whether or not to restore a session with lazy-browser tabs.
pref("browser.sessionstore.restore_tabs_lazily", true);

pref("browser.suppress_first_window_animation", true);

// Preferences for Photon onboarding system extension
pref("browser.onboarding.enabled", true);
// Mark this as an upgraded profile so we don't offer the initial new user onboarding tour.
pref("browser.onboarding.tourset-version", 2);
pref("browser.onboarding.state", "default");
// On the Activity-Stream page, the snippet's position overlaps with our notification.
// So use `browser.onboarding.notification.finished` to let the AS page know
// if our notification is finished and safe to show their snippet.
pref("browser.onboarding.notification.finished", false);
pref("browser.onboarding.notification.mute-duration-on-first-session-ms", 300000); // 5 mins
pref("browser.onboarding.notification.max-life-time-per-tour-ms", 432000000); // 5 days
pref("browser.onboarding.notification.max-life-time-all-tours-ms", 1209600000); // 14 days
pref("browser.onboarding.notification.max-prompt-count-per-tour", 8);
pref("browser.onboarding.newtour", "performance,private,screenshots,addons,customize,default");
pref("browser.onboarding.updatetour", "performance,library,screenshots,singlesearch,customize,sync");

// Preference that allows individual users to disable Screenshots.
pref("extensions.screenshots.disabled", false);
// Preference that allows individual users to leave Screenshots enabled, but
// disable uploading to the server.
pref("extensions.screenshots.upload-disabled", false);

// Preferences for BrowserErrorReporter.jsm
// Only collect errors on Nightly, and specifically not local builds
//@line 1725 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"
pref("browser.chrome.errorReporter.enabled", false);
//@line 1727 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"
pref("browser.chrome.errorReporter.sampleRate", "0.001");
pref("browser.chrome.errorReporter.publicKey", "c709cb7a2c0b4f0882fcc84a5af161ec");
pref("browser.chrome.errorReporter.projectId", "339");
pref("browser.chrome.errorReporter.submitUrl", "https://sentry.prod.mozaws.net/api/339/store/");
pref("browser.chrome.errorReporter.logLevel", "Error");

// URL for Learn More link for browser error logging in preferences
pref("browser.chrome.errorReporter.infoURL",
     "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/nightly-error-collection");

// Normandy client preferences
pref("app.normandy.api_url", "https://normandy.cdn.mozilla.net/api/v1");
pref("app.normandy.dev_mode", false);
pref("app.normandy.enabled", true);
pref("app.normandy.first_run", true);
pref("app.normandy.logging.level", 50); // Warn
pref("app.normandy.run_interval_seconds", 86400); // 24 hours
pref("app.normandy.shieldLearnMoreUrl", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/shield");
//@line 1746 "/builds/worker/workspace/build/src/browser/app/profile/firefox.js"
pref("app.shield.optoutstudies.enabled", true);
PK
!<s
wVV$defaults/preferences/firefox-l10n.js//@line 4 "/builds/worker/workspace/build/src/browser/locales/en-US/firefox-l10n.js"

PK
!<���pp(defaults/preferences/firefox-branding.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

pref("startup.homepage_override_url", "");
pref("startup.homepage_welcome_url", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/firstrun/");
pref("startup.homepage_welcome_url.additional", "");
// Interval: Time between checks for a new version (in seconds)
pref("app.update.interval", 43200); // 12 hours
// The time interval between the downloading of mar file chunks in the
// background (in seconds)
// 0 means "download everything at once"
pref("app.update.download.backgroundInterval", 0);
// Give the user x seconds to react before showing the big UI. default=192 hours
pref("app.update.promptWaitTime", 691200);
// app.update.url.manual: URL user can browse to manually if for some reason
// all update installation attempts fail.
// app.update.url.details: a default value for the "More information about this
// update" link supplied in the "An update is available" page of the update
// wizard.
//@line 25 "/builds/worker/workspace/build/src/browser/branding/official/pref/firefox-branding.js"
pref("app.update.url.manual", "https://www.mozilla.org/firefox/");
pref("app.update.url.details", "https://www.mozilla.org/%LOCALE%/firefox/notes");
//@line 28 "/builds/worker/workspace/build/src/browser/branding/official/pref/firefox-branding.js"

pref("app.releaseNotesURL", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/releasenotes/?utm_source=firefox-browser&utm_medium=firefox-browser&utm_campaign=whatsnew");

// The number of days a binary is permitted to be old
// without checking for an update.  This assumes that
// app.update.checkInstallTime is true.
pref("app.update.checkInstallTime.days", 63);

// Give the user x seconds to reboot before showing a badge on the hamburger
// button. default=4 days
pref("app.update.badgeWaitTime", 345600);

// Number of usages of the web console or scratchpad.
// If this is less than 5, then pasting code into the web console or scratchpad is disabled
pref("devtools.selfxss.count", 0);
PK
!<eMPd�?�? defaults/preferences/devtools.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Developer edition promo preferences
pref("devtools.devedition.promo.shown", false);
pref("devtools.devedition.promo.url", "https://www.mozilla.org/firefox/developer/?utm_source=firefox-dev-tools&utm_medium=firefox-browser&utm_content=betadoorhanger");

// Only potentially show in beta release
//@line 13 "/builds/worker/workspace/build/src/devtools/client/preferences/devtools.js"
  pref("devtools.devedition.promo.enabled", false);
//@line 15 "/builds/worker/workspace/build/src/devtools/client/preferences/devtools.js"

// Developer toolbar preferences
pref("devtools.toolbar.enabled", true);

// Enable DevTools WebIDE by default
pref("devtools.webide.enabled", true);

// Toolbox preferences
pref("devtools.toolbox.footer.height", 250);
pref("devtools.toolbox.sidebar.width", 500);
pref("devtools.toolbox.host", "bottom");
pref("devtools.toolbox.previousHost", "side");
pref("devtools.toolbox.selectedTool", "webconsole");
pref("devtools.toolbox.sideEnabled", true);
pref("devtools.toolbox.zoomValue", "1");
pref("devtools.toolbox.splitconsoleEnabled", false);
pref("devtools.toolbox.splitconsoleHeight", 100);

// Toolbox Button preferences
pref("devtools.command-button-pick.enabled", true);
pref("devtools.command-button-frames.enabled", true);
pref("devtools.command-button-splitconsole.enabled", true);
pref("devtools.command-button-paintflashing.enabled", false);
pref("devtools.command-button-scratchpad.enabled", false);
pref("devtools.command-button-responsive.enabled", true);
pref("devtools.command-button-screenshot.enabled", false);
pref("devtools.command-button-rulers.enabled", false);
pref("devtools.command-button-measure.enabled", false);
pref("devtools.command-button-noautohide.enabled", false);

// Inspector preferences
// Enable the Inspector
pref("devtools.inspector.enabled", true);
// What was the last active sidebar in the inspector
pref("devtools.inspector.activeSidebar", "ruleview");
pref("devtools.inspector.remote", false);
// Enable the split rule view sidebar toggle in the inspector
pref("devtools.inspector.split-sidebar-toggle", false);
// Enable the split rule view in the inspector
pref("devtools.inspector.split-rule-enabled", false);
// Collapse pseudo-elements by default in the rule-view
pref("devtools.inspector.show_pseudo_elements", false);
// The default size for image preview tooltips in the rule-view/computed-view/markup-view
pref("devtools.inspector.imagePreviewTooltipSize", 300);
// Enable user agent style inspection in rule-view
pref("devtools.inspector.showUserAgentStyles", false);
// Show all native anonymous content (like controls in <video> tags)
pref("devtools.inspector.showAllAnonymousContent", false);
// Enable the new color widget
pref("devtools.inspector.colorWidget.enabled", false);
// Enable the Flexbox highlighter
pref("devtools.inspector.flexboxHighlighter.enabled", false);
// Enable the CSS shapes highlighter
pref("devtools.inspector.shapesHighlighter.enabled", true);
// Enable the Changes View
pref("devtools.changesview.enabled", false);
// Enable the Events View
pref("devtools.eventsview.enabled", false);
// Enable the Flexbox Inspector panel
pref("devtools.flexboxinspector.enabled", false);
// Enable the new Animation Inspector
pref("devtools.new-animationinspector.enabled", false);

// Grid highlighter preferences
pref("devtools.gridinspector.gridOutlineMaxColumns", 50);
pref("devtools.gridinspector.gridOutlineMaxRows", 50);
pref("devtools.gridinspector.showGridAreas", false);
pref("devtools.gridinspector.showGridLineNumbers", false);
pref("devtools.gridinspector.showInfiniteLines", false);

// Whether or not the box model panel is opened in the computed view
pref("devtools.computed.boxmodel.opened", true);
// Whether or not the box model panel is opened in the layout view
pref("devtools.layout.boxmodel.opened", true);
// Whether or not the flexbox panel is opened in the layout view
pref("devtools.layout.flexbox.opened", true);
// Whether or not the grid inspector panel is opened in the layout view
pref("devtools.layout.grid.opened", true);

// By how many times eyedropper will magnify pixels
pref("devtools.eyedropper.zoom", 6);

// Enable to collapse attributes that are too long.
pref("devtools.markup.collapseAttributes", true);

// Length to collapse attributes
pref("devtools.markup.collapseAttributeLength", 120);

// DevTools default color unit
pref("devtools.defaultColorUnit", "authored");

// Enable the Memory tools
pref("devtools.memory.enabled", true);

pref("devtools.memory.custom-census-displays", "{}");
pref("devtools.memory.custom-label-displays", "{}");
pref("devtools.memory.custom-tree-map-displays", "{}");

pref("devtools.memory.max-individuals", 1000);
pref("devtools.memory.max-retaining-paths", 10);

// Enable the Performance tools
pref("devtools.performance.enabled", true);

// The default Performance UI settings
pref("devtools.performance.memory.sample-probability", "0.05");
// Can't go higher than this without causing internal allocation overflows while
// serializing the allocations data over the RDP.
pref("devtools.performance.memory.max-log-length", 125000);
pref("devtools.performance.timeline.hidden-markers",
  "[\"Composite\",\"CompositeForwardTransaction\"]");
pref("devtools.performance.profiler.buffer-size", 10000000);
pref("devtools.performance.profiler.sample-frequency-khz", 1);
pref("devtools.performance.ui.invert-call-tree", true);
pref("devtools.performance.ui.invert-flame-graph", false);
pref("devtools.performance.ui.flatten-tree-recursion", true);
pref("devtools.performance.ui.show-platform-data", false);
pref("devtools.performance.ui.show-idle-blocks", true);
pref("devtools.performance.ui.enable-memory", false);
pref("devtools.performance.ui.enable-allocations", false);
pref("devtools.performance.ui.enable-framerate", true);
pref("devtools.performance.ui.show-jit-optimizations", false);
pref("devtools.performance.ui.show-triggers-for-gc-types",
  "TOO_MUCH_MALLOC ALLOC_TRIGGER LAST_DITCH EAGER_ALLOC_TRIGGER");

// Temporary pref disabling memory flame views
// TODO remove once we have flame charts via bug 1148663
pref("devtools.performance.ui.enable-memory-flame", false);

// Enable experimental options in the UI only in Nightly
//@line 148 "/builds/worker/workspace/build/src/devtools/client/preferences/devtools.js"
pref("devtools.performance.ui.experimental", false);
//@line 150 "/builds/worker/workspace/build/src/devtools/client/preferences/devtools.js"

// The default cache UI setting
pref("devtools.cache.disabled", false);

// The default service workers UI setting
pref("devtools.serviceWorkers.testing.enabled", false);

// Enable the Network Monitor
pref("devtools.netmonitor.enabled", true);

// The default Network Monitor UI settings
pref("devtools.netmonitor.panes-network-details-width", 550);
pref("devtools.netmonitor.panes-network-details-height", 450);
pref("devtools.netmonitor.filters", "[\"all\"]");
pref("devtools.netmonitor.visibleColumns",
  "[\"status\",\"method\",\"file\",\"domain\",\"cause\",\"type\",\"transferred\",\"contentSize\",\"waterfall\"]"
);

// The default Network monitor HAR export setting
pref("devtools.netmonitor.har.defaultLogDir", "");
pref("devtools.netmonitor.har.defaultFileName", "Archive %date");
pref("devtools.netmonitor.har.jsonp", false);
pref("devtools.netmonitor.har.jsonpCallback", "");
pref("devtools.netmonitor.har.includeResponseBodies", true);
pref("devtools.netmonitor.har.compress", false);
pref("devtools.netmonitor.har.forceExport", false);
pref("devtools.netmonitor.har.pageLoadedTimeout", 1500);
pref("devtools.netmonitor.har.enableAutoExportToFile", false);

// Scratchpad settings
// - recentFileMax: The maximum number of recently-opened files
//                  stored. Setting this preference to 0 will not
//                  clear any recent files, but rather hide the
//                  'Open Recent'-menu.
// - lineNumbers: Whether to show line numbers or not.
// - wrapText: Whether to wrap text or not.
// - showTrailingSpace: Whether to highlight trailing space or not.
// - editorFontSize: Editor font size configuration.
// - enableAutocompletion: Whether to enable JavaScript autocompletion.
pref("devtools.scratchpad.recentFilesMax", 10);
pref("devtools.scratchpad.lineNumbers", true);
pref("devtools.scratchpad.wrapText", false);
pref("devtools.scratchpad.showTrailingSpace", false);
pref("devtools.scratchpad.editorFontSize", 12);
pref("devtools.scratchpad.enableAutocompletion", true);

// Enable the Storage Inspector
pref("devtools.storage.enabled", true);

// Enable the Style Editor.
pref("devtools.styleeditor.enabled", true);
pref("devtools.styleeditor.autocompletion-enabled", true);
pref("devtools.styleeditor.showMediaSidebar", true);
pref("devtools.styleeditor.mediaSidebarWidth", 238);
pref("devtools.styleeditor.navSidebarWidth", 245);
pref("devtools.styleeditor.transitions", true);

// Screenshot Option Settings.
pref("devtools.screenshot.clipboard.enabled", false);
pref("devtools.screenshot.audio.enabled", true);

// Enable the Shader Editor.
pref("devtools.shadereditor.enabled", false);

// Enable the Canvas Debugger.
pref("devtools.canvasdebugger.enabled", false);

// Enable the Web Audio Editor
pref("devtools.webaudioeditor.enabled", false);

// Enable Scratchpad
pref("devtools.scratchpad.enabled", false);

// Make sure the DOM panel is hidden by default
pref("devtools.dom.enabled", false);

// Web Audio Editor Inspector Width should be a preference
pref("devtools.webaudioeditor.inspectorWidth", 300);

// Web console filters
pref("devtools.webconsole.filter.error", true);
pref("devtools.webconsole.filter.warn", true);
pref("devtools.webconsole.filter.info", true);
pref("devtools.webconsole.filter.log", true);
pref("devtools.webconsole.filter.debug", true);
pref("devtools.webconsole.filter.css", false);
pref("devtools.webconsole.filter.net", false);
pref("devtools.webconsole.filter.netxhr", false);
// Deprecated - old console frontend
pref("devtools.webconsole.filter.network", true);
pref("devtools.webconsole.filter.networkinfo", false);
pref("devtools.webconsole.filter.netwarn", true);
pref("devtools.webconsole.filter.csserror", true);
pref("devtools.webconsole.filter.cssparser", false);
pref("devtools.webconsole.filter.csslog", false);
pref("devtools.webconsole.filter.exception", true);
pref("devtools.webconsole.filter.jswarn", true);
pref("devtools.webconsole.filter.jslog", false);
pref("devtools.webconsole.filter.secerror", true);
pref("devtools.webconsole.filter.secwarn", true);
pref("devtools.webconsole.filter.serviceworkers", true);
pref("devtools.webconsole.filter.sharedworkers", false);
pref("devtools.webconsole.filter.windowlessworkers", false);
pref("devtools.webconsole.filter.servererror", false);
pref("devtools.webconsole.filter.serverwarn", false);
pref("devtools.webconsole.filter.serverinfo", false);
pref("devtools.webconsole.filter.serverlog", false);

// Browser console filters
pref("devtools.browserconsole.filter.error", true);
pref("devtools.browserconsole.filter.warn", true);
pref("devtools.browserconsole.filter.info", true);
pref("devtools.browserconsole.filter.log", true);
pref("devtools.browserconsole.filter.debug", true);
pref("devtools.browserconsole.filter.css", false);
pref("devtools.browserconsole.filter.net", false);
pref("devtools.browserconsole.filter.netxhr", false);
// Remember the Browser Console filters (old frontend)
pref("devtools.browserconsole.filter.network", true);
pref("devtools.browserconsole.filter.networkinfo", false);
pref("devtools.browserconsole.filter.netwarn", true);
pref("devtools.browserconsole.filter.netxhr", false);
pref("devtools.browserconsole.filter.csserror", true);
pref("devtools.browserconsole.filter.cssparser", false);
pref("devtools.browserconsole.filter.csslog", false);
pref("devtools.browserconsole.filter.exception", true);
pref("devtools.browserconsole.filter.jswarn", true);
pref("devtools.browserconsole.filter.jslog", true);
pref("devtools.browserconsole.filter.error", true);
pref("devtools.browserconsole.filter.warn", true);
pref("devtools.browserconsole.filter.info", true);
pref("devtools.browserconsole.filter.log", true);
pref("devtools.browserconsole.filter.secerror", true);
pref("devtools.browserconsole.filter.secwarn", true);
pref("devtools.browserconsole.filter.serviceworkers", true);
pref("devtools.browserconsole.filter.sharedworkers", true);
pref("devtools.browserconsole.filter.windowlessworkers", true);
pref("devtools.browserconsole.filter.servererror", false);
pref("devtools.browserconsole.filter.serverwarn", false);
pref("devtools.browserconsole.filter.serverinfo", false);
pref("devtools.browserconsole.filter.serverlog", false);

// Web console filter bar settings
pref("devtools.webconsole.ui.filterbar", false);
// Browser console filter bar settings
pref("devtools.browserconsole.ui.filterbar", false);

// Max number of inputs to store in web console history.
pref("devtools.webconsole.inputHistoryCount", 50);

// Persistent logging: |true| if you want the relevant tool to keep all of the
// logged messages after reloading the page, |false| if you want the output to
// be cleared each time page navigation happens.
pref("devtools.webconsole.persistlog", false);
pref("devtools.netmonitor.persistlog", false);

// Web Console timestamp: |true| if you want the logs and instructions
// in the Web Console to display a timestamp, or |false| to not display
// any timestamps.
pref("devtools.webconsole.timestampMessages", false);

// Web Console automatic multiline mode: |true| if you want incomplete statements
// to automatically trigger multiline editing (equivalent to shift + enter).
pref("devtools.webconsole.autoMultiline", true);

// Enable the new webconsole frontend
pref("devtools.webconsole.new-frontend-enabled", true);

// Enable the new webconsole frontend in the browser console
pref("devtools.browserconsole.new-frontend-enabled", false);

// Enable the webconsole sidebar toggle
pref("devtools.webconsole.sidebarToggle", false);

// Disable the new performance recording panel by default
pref("devtools.performance.new-panel-enabled", false);

// Enable client-side mapping service for source maps
pref("devtools.source-map.client-service.enabled", true);

// The number of lines that are displayed in the web console.
pref("devtools.hud.loglimit", 10000);

// The number of lines that are displayed in the old web console for the Net,
// CSS, JS and Web Developer categories. These defaults should be kept in sync
// with DEFAULT_LOG_LIMIT in the old webconsole frontend.
pref("devtools.hud.loglimit.network", 1000);
pref("devtools.hud.loglimit.cssparser", 1000);
pref("devtools.hud.loglimit.exception", 1000);
pref("devtools.hud.loglimit.console", 1000);

// The developer tools editor configuration:
// - tabsize: how many spaces to use when a Tab character is displayed.
// - expandtab: expand Tab characters to spaces.
// - keymap: which keymap to use (can be 'default', 'emacs' or 'vim')
// - autoclosebrackets: whether to permit automatic bracket/quote closing.
// - detectindentation: whether to detect the indentation from the file
// - enableCodeFolding: Whether to enable code folding or not.
pref("devtools.editor.tabsize", 2);
pref("devtools.editor.expandtab", true);
pref("devtools.editor.keymap", "default");
pref("devtools.editor.autoclosebrackets", true);
pref("devtools.editor.detectindentation", true);
pref("devtools.editor.enableCodeFolding", true);
pref("devtools.editor.autocomplete", true);

// Pref to store the browser version at the time of a telemetry ping for an
// opened developer tool. This allows us to ping telemetry just once per browser
// version for each user.
pref("devtools.telemetry.tools.opened.version", "{}");

// Whether to reload when touch simulation is toggled
pref("devtools.responsive.reloadConditions.touchSimulation", false);
// Whether to reload when user agent is changed
pref("devtools.responsive.reloadConditions.userAgent", false);
// Whether to show the notification about reloading to apply emulation
pref("devtools.responsive.reloadNotification.enabled", true);
PK
!<nd;��.defaults/preferences/devtools-startup-prefs.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 Devtools preferences file will be loaded as a usual Firefox preferences file.
// Most DevTools prefs are included with the addon and loaded dynamically during the addon
// startup. For preferences that are required before the addon is loaded or that we can't
// process in JS, they can be defined in this file.
// Note that this preference file follows Firefox release cycle.

// Enable the JSON View tool (an inspector for application/json documents).
pref("devtools.jsonview.enabled", true);

// Default theme ("dark" or "light")
//@line 18 "/builds/worker/workspace/build/src/devtools/shim/devtools-startup-prefs.js"
pref("devtools.theme", "light", sticky);
//@line 20 "/builds/worker/workspace/build/src/devtools/shim/devtools-startup-prefs.js"

// Should the devtools toolbar be opened on startup
pref("devtools.toolbar.visible", false);

// Pref to drive the devtools onboarding flow experiment. States:
// - off: forces devtools.enabled to true
// - on: devtools.enabled is not forced to true.
// - force: devtools.enabled is not forced to true and cannot be set to true by checking
//   devtools.selfxss.count. User will have to go through onboarding to use DevTools.
pref("devtools.onboarding.experiment", "off");

// If devtools.onboarding.experiment is set to "on" or "force", we will flip the
// devtools.enabled preference to false once. The flag is used to make sure it is only
// flipped once.
pref("devtools.onboarding.experiment.flipped", false);

// Flag to check if we already logged the devtools onboarding related probe.
pref("devtools.onboarding.telemetry.logged", false);

// Completely disable DevTools entry points, as well as all DevTools command line
// arguments This should be merged with devtools.enabled, see Bug 1440675.
pref("devtools.policy.disabled", false);
PK
!<f¯��� defaults/preferences/debugger.js/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */

pref("devtools.debugger.new-debugger-frontend", true);

// Enable the Debugger
pref("devtools.debugger.enabled", true);
pref("devtools.debugger.chrome-debugging-host", "localhost");
pref("devtools.debugger.chrome-debugging-websocket", false);
pref("devtools.debugger.remote-host", "localhost");
pref("devtools.debugger.remote-timeout", 20000);
pref("devtools.debugger.pause-on-exceptions", false);
pref("devtools.debugger.ignore-caught-exceptions", false);
pref("devtools.debugger.source-maps-enabled", true);
pref("devtools.debugger.pretty-print-enabled", true);
pref("devtools.debugger.auto-pretty-print", false);
pref("devtools.debugger.auto-black-box", true);
pref("devtools.debugger.workers", false);

// The default Debugger UI settings
pref("devtools.debugger.prefs-schema-version", "1.0.0");
pref("devtools.debugger.ui.panes-workers-and-sources-width", 200);
pref("devtools.debugger.ui.panes-instruments-width", 300);
pref("devtools.debugger.ui.panes-visible-on-startup", false);
pref("devtools.debugger.ui.variables-sorting-enabled", true);
pref("devtools.debugger.ui.variables-only-enum-visible", false);
pref("devtools.debugger.ui.variables-searchbox-visible", false);
pref("devtools.debugger.ui.framework-grouping-on", true);
pref("devtools.debugger.call-stack-visible", true);
pref("devtools.debugger.scopes-visible", true);
pref("devtools.debugger.workers-visible", true);
pref("devtools.debugger.breakpoints-visible", true);
pref("devtools.debugger.expressions-visible", true);
pref("devtools.debugger.start-panel-collapsed", false);
pref("devtools.debugger.end-panel-collapsed", false);
pref("devtools.debugger.tabs", "[]");
pref("devtools.debugger.pending-selected-location", "{}");
pref("devtools.debugger.pending-breakpoints", "{}");
pref("devtools.debugger.expressions", "[]");
pref("devtools.debugger.file-search-case-sensitive", false);
pref("devtools.debugger.file-search-whole-word", false);
pref("devtools.debugger.file-search-regex-match", false);
pref("devtools.debugger.features.async-stepping", true);
pref("devtools.debugger.project-directory-root", "");

pref("devtools.debugger.features.wasm", true);
pref("devtools.debugger.features.shortcuts", true);
pref("devtools.debugger.features.root", true);
pref("devtools.debugger.features.column-breakpoints", false);
pref("devtools.debugger.features.chrome-scopes", false);
pref("devtools.debugger.features.map-scopes", false);
pref("devtools.debugger.features.breakpoints-dropdown", false);
pref("devtools.debugger.features.remove-command-bar-options", false);
pref("devtools.debugger.features.workers", true);
pref("devtools.debugger.features.code-coverage", false);
pref("devtools.debugger.features.event-listeners", false);
pref("devtools.debugger.features.code-folding", false);
pref("devtools.debugger.features.outline", true);
pref("devtools.debugger.features.replay", false);
PK
!<�Y.HHchrome.manifestmanifest chrome/chrome.manifest
manifest components/components.manifest
PK
!<�?��VVchrome/chrome.manifestcontent branding browser/content/branding/ contentaccessible=yes
content browser browser/content/browser/ contentaccessible=yes
skin browser classic/1.0 browser/skin/classic/browser/
skin communicator classic/1.0 browser/skin/classic/communicator/
content webide webide/content/
skin webide classic/1.0 webide/skin/
content devtools-shim devtools-shim/content/
content devtools devtools/content/
skin devtools classic/1.0 devtools/skin/
locale branding en-US en-US/locale/branding/
locale browser en-US en-US/locale/browser/
locale browser-region en-US en-US/locale/browser-region/
locale devtools en-US en-US/locale/en-US/devtools/client/
locale devtools-shared en-US en-US/locale/en-US/devtools/shared/
locale devtools-shim en-US en-US/locale/en-US/devtools/shim/
locale pdf.js en-US en-US/locale/pdfviewer/
overlay chrome://browser/content/places/places.xul chrome://browser/content/places/downloadsViewOverlay.xul
override chrome://global/content/license.html chrome://browser/content/license.html
override chrome://global/content/netError.xhtml chrome://browser/content/aboutNetError.xhtml
override chrome://global/locale/appstrings.properties chrome://browser/locale/appstrings.properties
override chrome://global/locale/netError.dtd chrome://browser/locale/netError.dtd
override chrome://global/skin/icons/warning-16.png moz-icon://stock/gtk-dialog-warning?size=menu
override chrome://mozapps/locale/downloads/settingsChange.dtd chrome://browser/locale/downloads/settingsChange.dtd
resource search-plugins chrome://browser/locale/searchplugins/
resource usercontext-content browser/content/ contentaccessible=yes
resource pdf.js pdfjs/content/
resource devtools devtools/modules/devtools/
resource devtools-client-jsonview resource://devtools/client/jsonview/ contentaccessible=yes
resource devtools-client-shared resource://devtools/client/shared/ contentaccessible=yes
PK
!<��tL�%�%components/components.manifestinterfaces interfaces.xpt
component {229fa115-9412-4d32-baf3-2fc407f76fb1} FeedConverter.js
contract @mozilla.org/streamconv;1?from=application/vnd.mozilla.maybe.feed&to=*/* {229fa115-9412-4d32-baf3-2fc407f76fb1}
contract @mozilla.org/streamconv;1?from=application/vnd.mozilla.maybe.video.feed&to=*/* {229fa115-9412-4d32-baf3-2fc407f76fb1}
contract @mozilla.org/streamconv;1?from=application/vnd.mozilla.maybe.audio.feed&to=*/* {229fa115-9412-4d32-baf3-2fc407f76fb1}
component {2376201c-bbc6-472f-9b62-7548040a61c6} FeedConverter.js
contract @mozilla.org/browser/feeds/result-service;1 {2376201c-bbc6-472f-9b62-7548040a61c6}
component {49bb6593-3aff-4eb3-a068-2712c28bd58e} FeedWriter.js
contract @mozilla.org/browser/feeds/result-writer;1 {49bb6593-3aff-4eb3-a068-2712c28bd58e}
component {792a7e82-06a0-437c-af63-b2d12e808acc} WebContentConverter.js
contract @mozilla.org/embeddor.implemented/web-content-handler-registrar;1 {792a7e82-06a0-437c-af63-b2d12e808acc}
category app-startup WebContentConverter service,@mozilla.org/embeddor.implemented/web-content-handler-registrar;1 application={3c2e2abc-06d4-11e1-ac3b-374f68613e61} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384} application={aa3c5121-dab2-40e2-81ca-7ea25febc110} application={a23983c0-fd0e-11dc-95ff-0800200c9a66} application={d1bfe7d9-c01e-4237-998b-7b5f960a4314}
component {5d0ce354-df01-421a-83fb-7ead0990c24e} nsBrowserContentHandler.js application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
contract @mozilla.org/browser/clh;1 {5d0ce354-df01-421a-83fb-7ead0990c24e} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
component {47cd0651-b1be-4a0f-b5c4-10e5a573ef71} nsBrowserContentHandler.js application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
contract @mozilla.org/browser/final-clh;1 {47cd0651-b1be-4a0f-b5c4-10e5a573ef71} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
contract @mozilla.org/uriloader/content-handler;1?type=text/html {5d0ce354-df01-421a-83fb-7ead0990c24e} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
contract @mozilla.org/uriloader/content-handler;1?type=application/vnd.mozilla.xul+xml {5d0ce354-df01-421a-83fb-7ead0990c24e} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
contract @mozilla.org/uriloader/content-handler;1?type=image/svg+xml {5d0ce354-df01-421a-83fb-7ead0990c24e} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
contract @mozilla.org/uriloader/content-handler;1?type=text/rdf {5d0ce354-df01-421a-83fb-7ead0990c24e} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
contract @mozilla.org/uriloader/content-handler;1?type=text/xml {5d0ce354-df01-421a-83fb-7ead0990c24e} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
contract @mozilla.org/uriloader/content-handler;1?type=application/xhtml+xml {5d0ce354-df01-421a-83fb-7ead0990c24e} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
contract @mozilla.org/uriloader/content-handler;1?type=text/css {5d0ce354-df01-421a-83fb-7ead0990c24e} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
contract @mozilla.org/uriloader/content-handler;1?type=text/plain {5d0ce354-df01-421a-83fb-7ead0990c24e} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
contract @mozilla.org/uriloader/content-handler;1?type=image/gif {5d0ce354-df01-421a-83fb-7ead0990c24e} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
contract @mozilla.org/uriloader/content-handler;1?type=image/jpeg {5d0ce354-df01-421a-83fb-7ead0990c24e} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
contract @mozilla.org/uriloader/content-handler;1?type=image/jpg {5d0ce354-df01-421a-83fb-7ead0990c24e} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
contract @mozilla.org/uriloader/content-handler;1?type=image/png {5d0ce354-df01-421a-83fb-7ead0990c24e} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
contract @mozilla.org/uriloader/content-handler;1?type=image/bmp {5d0ce354-df01-421a-83fb-7ead0990c24e} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
contract @mozilla.org/uriloader/content-handler;1?type=image/x-icon {5d0ce354-df01-421a-83fb-7ead0990c24e} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
contract @mozilla.org/uriloader/content-handler;1?type=image/vnd.microsoft.icon {5d0ce354-df01-421a-83fb-7ead0990c24e} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
contract @mozilla.org/uriloader/content-handler;1?type=application/http-index-format {5d0ce354-df01-421a-83fb-7ead0990c24e} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
category command-line-handler m-browser @mozilla.org/browser/clh;1 application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
category command-line-handler x-default @mozilla.org/browser/final-clh;1 application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
category command-line-validator b-browser @mozilla.org/browser/clh;1 application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
component {eab9012e-5f74-4cbc-b2b5-a590235513cc} nsBrowserGlue.js
contract @mozilla.org/browser/browserglue;1 {eab9012e-5f74-4cbc-b2b5-a590235513cc}
category app-startup nsBrowserGlue service,@mozilla.org/browser/browserglue;1 application={3c2e2abc-06d4-11e1-ac3b-374f68613e61} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384} application={aa3c5121-dab2-40e2-81ca-7ea25febc110} application={a23983c0-fd0e-11dc-95ff-0800200c9a66} application={d1bfe7d9-c01e-4237-998b-7b5f960a4314}
component {d8903bf6-68d5-4e97-bcd1-e4d3012f721a} nsBrowserGlue.js
contract @mozilla.org/content-permission/prompt;1 {d8903bf6-68d5-4e97-bcd1-e4d3012f721a}
component {F57899D0-4E2C-4ac6-9E29-50C736103B0C} nsSetDefaultBrowser.js
contract @mozilla.org/browser/default-browser-clh;1 {F57899D0-4E2C-4ac6-9E29-50C736103B0C}
category command-line-handler m-setdefaultbrowser @mozilla.org/browser/default-browser-clh;1
component {9e9a9283-0ce9-4e4a-8f1c-ba129a032c32} devtools-startup.js
contract @mozilla.org/devtools/startup-clh;1 {9e9a9283-0ce9-4e4a-8f1c-ba129a032c32}
category command-line-handler m-devtools @mozilla.org/devtools/startup-clh;1
component {1060afaf-dc9e-43da-8646-23a2faf48493} aboutdebugging-registration.js
contract @mozilla.org/network/protocol/about;1?what=debugging {1060afaf-dc9e-43da-8646-23a2faf48493}
component {3a16d383-92bd-4c24-ac10-0e2bd66883ab} aboutdevtools-registration.js
contract @mozilla.org/network/protocol/about;1?what=devtools {3a16d383-92bd-4c24-ac10-0e2bd66883ab}
component {11342911-3135-45a8-8d71-737a2b0ad469} aboutdevtoolstoolbox-registration.js
contract @mozilla.org/network/protocol/about;1?what=devtools-toolbox {11342911-3135-45a8-8d71-737a2b0ad469}
component {4c2b1f46-e637-4a91-8108-8a9fb7aab91d} nsAboutCapabilities.js
contract @mozilla.org/aboutcapabilities;1 {4c2b1f46-e637-4a91-8108-8a9fb7aab91d}
component {f7800463-3b97-47f9-9341-b7617e6d8d49} ExperimentsService.js
contract @mozilla.org/browser/experiments-service;1 {f7800463-3b97-47f9-9341-b7617e6d8d49}
category update-timer ExperimentsService @mozilla.org/browser/experiments-service;1,getService,experiments-update-timer,experiments.manifest.fetchIntervalSeconds,86400
category profile-after-change ExperimentsService @mozilla.org/browser/experiments-service;1
component {dfcd2adc-7867-4d3a-ba70-17501f208142} aboutNewTabService.js
contract @mozilla.org/browser/aboutnewtab-service;1 {dfcd2adc-7867-4d3a-ba70-17501f208142}
component {ea4e1414-779b-458b-9d1f-d18e8efbc145} EnterprisePolicies.js process=main
contract @mozilla.org/browser/enterprisepolicies;1 {ea4e1414-779b-458b-9d1f-d18e8efbc145} process=main
component {dc6358f8-d167-4566-bf5b-4350b5e6a7a2} EnterprisePoliciesContent.js process=content
contract @mozilla.org/browser/enterprisepolicies;1 {dc6358f8-d167-4566-bf5b-4350b5e6a7a2} process=content
component {5280606b-2510-4fe0-97ef-9b5a22eafe6b} nsSessionStore.js
contract @mozilla.org/browser/sessionstore;1 {5280606b-2510-4fe0-97ef-9b5a22eafe6b}
component {ec7a6c20-e081-11da-8ad9-0800200c9a66} nsSessionStartup.js
contract @mozilla.org/browser/sessionstartup;1 {ec7a6c20-e081-11da-8ad9-0800200c9a66}
category app-startup nsSessionStartup service,@mozilla.org/browser/sessionstartup;1 application={3c2e2abc-06d4-11e1-ac3b-374f68613e61} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384} application={aa3c5121-dab2-40e2-81ca-7ea25febc110} application={a23983c0-fd0e-11dc-95ff-0800200c9a66} application={d1bfe7d9-c01e-4237-998b-7b5f960a4314}
component {6F8BB968-C14F-4D6F-9733-6C6737B35DCE} ProfileMigrator.js
contract @mozilla.org/toolkit/profile-migrator;1 {6F8BB968-C14F-4D6F-9733-6C6737B35DCE}
component {47f75963-840b-4950-a1f0-d9c1864f8b8e} ChromeProfileMigrator.js
contract @mozilla.org/profile/migrator;1?app=browser&type=chrome-beta {47f75963-840b-4950-a1f0-d9c1864f8b8e}
component {7370a02a-4886-42c3-a4ec-d48c726ec30a} ChromeProfileMigrator.js
contract @mozilla.org/profile/migrator;1?app=browser&type=chrome-dev {7370a02a-4886-42c3-a4ec-d48c726ec30a}
component {4cec1de4-1671-4fc3-a53e-6c539dc77a26} ChromeProfileMigrator.js
contract @mozilla.org/profile/migrator;1?app=browser&type=chrome {4cec1de4-1671-4fc3-a53e-6c539dc77a26}
component {8cece922-9720-42de-b7db-7cef88cb07ca} ChromeProfileMigrator.js
contract @mozilla.org/profile/migrator;1?app=browser&type=chromium {8cece922-9720-42de-b7db-7cef88cb07ca}
component {91185366-ba97-4438-acba-48deaca63386} FirefoxProfileMigrator.js
contract @mozilla.org/profile/migrator;1?app=browser&type=firefox {91185366-ba97-4438-acba-48deaca63386}
category webextension-modules browser chrome://browser/content/ext-browser.json
category webextension-scripts c-browser chrome://browser/content/ext-browser.js
category webextension-scripts-devtools browser chrome://browser/content/ext-c-browser.js
category webextension-scripts-addon browser chrome://browser/content/ext-c-browser.js
category webextension-schemas menus_internal chrome://browser/content/schemas/menus_internal.json
PK
!<*�0���components/interfaces.xptXPCOM
TypeLib
�$�#1EP]ks�����"�o�1ICŵ����xޓ��+�H�ǃEޠ�D+̻E2h,����D=��κ��-��-��[�N밨�E_ңW��E���i=B=��,�
�.M�FgX���jL\��;��YC�"���v�Cּ��=��>�S��?Z��EH�7�wg�i�ʓF��8G���l�����		Õ0��{:G���\F
�s�
4
��|�n�xE˷�zNu�
����*�xgM:�pP �B ��6�QXK!��P�E��
H
mozIDOMWindowProxynsICommandLinensIDOMElementnsIDOMEventListenernsIDOMNodensIDOMWindownsIFeedResultnsIFilensIHandlerAppnsIProfileStartupnsIRequestnsISupportsnsIURInsIWebContentHandlerRegistrarnsIBrowserProfileMigratormigrategetMigrateDatagetLastUsedDateisSourceAvailablegetSourceProfilessourceLockedALLSETTINGSCOOKIESHISTORYFORMDATAPASSWORDSBOOKMARKSOTHERDATASESSION����
�����
`�`�`�"`��4`
�	AENV^gq {@���nsISessionStoreUtilsforEachNonDynamicChildFramecreateDynamicFrameEventFilter.�����J��`���nsIGNOMEShellServicecanSetDesktopBackground��`
��nsIShellServiceisDefaultBrowsersetDefaultBrowsersetDesktopBackgroundopenApplicationdesktopBackgroundColoropenApplicationWithURIdefaultFeedReaderBACKGROUND_TILEBACKGROUND_STRETCHBACKGROUND_CENTERBACKGROUND_FILLBACKGROUND_FITAPPLICATION_MAILAPPLICATION_NEWS��
�
`
���
�
����������%`�@%��<������S`��eu������nsISessionStorecanRestoreLastSessionrestoreLastSessiongetBrowserStatesetBrowserStategetWindowStatesetWindowStategetTabStatesetTabStateduplicateTabgetClosedTabCountgetClosedTabDataundoCloseTabforgetClosedTabgetClosedWindowCountgetClosedWindowDataundoCloseWindowforgetClosedWindowgetWindowValuesetWindowValuedeleteWindowValuegetTabValuesetTabValuedeleteTabValuegetGlobalValuesetGlobalValuedeleteGlobalValuepersistTabAttribute��`
�@��
�����������������������
�����������������`����`�1�����B���`��O���`��_`�t�����`����`����������������������������������������������������� ���2����nsIJSInspectorenterNestedEventLoopexitNestedEventLoopeventLoopNestLevellastNestRequestor��`��`���`��`��nsISessionStoreUtilsFrameCallbackhandleFrameu�����nsIBrowserHandlerstartPagedefaultArgsgetFeatures�����@���������@�����������nsISessionStartuponceInitializedstatedoRestoreisAutomaticRestoreEnabledwillOverrideHomepagesessionTypepreviousSessionCrashedNO_SESSIONRECOVER_SESSIONRESUME_SESSIONDEFER_SESSION�	`��	)`�	/`
�	9`
��	S`��	h`��	t`
�	�	�	�	��nsIFeedResultServiceforcePreviewPageaddToClientReaderaddFeedResultgetFeedResultremoveFeedResult�
I`
�@
I�
�
Z����������
l���
z��
`��
���
��nsIWebContentConverterServicesetAutoHandlergetAutoHandlergetWebContentHandlerByURIloadPreferredHandlerremoveProtocolHandlerremoveContentHandlergetContentHandlersresetHandlersForType�������`��*����`��D���Y�����o��������D`��������nsIAboutNewTabServicenewTabURLdefaultURLwillNotifyUseroverriddenactivityStreamEnabledactivityStreamPrerenderactivityStreamDebugactivityStreamLocaleresetNewTabURL�6���@6����@���@@����K`
�@K�
��Z`
��e`
��{`
���`
���������nsIWebContentHandlerInfocontentTypeurigetHandlerURI	�
a����
m���
q������PK
!<\A;���3chrome/browser/content/browser/built_in_addons.json{"system": ["[email protected]", "[email protected]", "[email protected]", "[email protected]", "[email protected]", "[email protected]", "[email protected]", "[email protected]"]}
PK
!<�AGG-chrome/en-US/locale/branding/brand.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

brandShorterName=Firefox
brandShortName=Firefox
brandFullName=Mozilla Firefox
vendorShortName=Mozilla

syncBrandShortName=Sync
PK
!<�C�����.chrome/en-US/locale/browser/browser.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

nv_timeout=Timed Out
openFile=Open File

droponhometitle=Set Home Page
droponhomemsg=Do you want this document to be your new home page?
droponhomemsgMultiple=Do you want these documents to be your new home pages?

# context menu strings

# LOCALIZATION NOTE (contextMenuSearch): %1$S is the search engine,
# %2$S is the selection string.
contextMenuSearch=Search %1$S for “%2$S”
contextMenuSearch.accesskey=S

# bookmark dialog strings

bookmarkAllTabsDefault=[Folder Name]

xpinstallPromptMessage=%S prevented this site from asking you to install software on your computer.
xpinstallPromptMessage.dontAllow=Don’t Allow
xpinstallPromptMessage.dontAllow.accesskey=D
xpinstallPromptAllowButton=Allow
# Accessibility Note:
# Be sure you do not choose an accesskey that is used elsewhere in the active context (e.g. main menu bar, submenu of the warning popup button)
# See http://www.mozilla.org/access/keyboard/accesskey for details
xpinstallPromptAllowButton.accesskey=A
xpinstallDisabledMessageLocked=Software installation has been disabled by your system administrator.
xpinstallDisabledMessage=Software installation is currently disabled. Click Enable and try again.
xpinstallDisabledButton=Enable
xpinstallDisabledButton.accesskey=n

# LOCALIZATION NOTE (webextPerms.header)
# This string is used as a header in the webextension permissions dialog,
# %S is replaced with the localized name of the extension being installed.
# See https://bug1308309.bmoattachments.org/attachment.cgi?id=8814612
# for an example of the full dialog.
# Note, this string will be used as raw markup. Avoid characters like <, >, &
webextPerms.header=Add %S?

webextPerms.unsignedWarning=Caution: This add-on is unverified. Malicious add-ons can steal your private information or compromise your computer. Only install this add-on if you trust the source.

# LOCALIZATION NOTE (webextPerms.listIntro)
# This string will be followed by a list of permissions requested
# by the webextension.
webextPerms.listIntro=It requires your permission to:
webextPerms.add.label=Add
webextPerms.add.accessKey=A
webextPerms.cancel.label=Cancel
webextPerms.cancel.accessKey=C

# LOCALIZATION NOTE (webextPerms.sideloadMenuItem)
# %1$S will be replaced with the localized name of the sideloaded add-on.
# %2$S will be replace with the name of the application (e.g., Firefox, Nightly)
webextPerms.sideloadMenuItem=%1$S added to %2$S

# LOCALIZATION NOTE (webextPerms.sideloadHeader)
# This string is used as a header in the webextension permissions dialog
# when the extension is side-loaded.
# %S is replaced with the localized name of the extension being installed.
# Note, this string will be used as raw markup. Avoid characters like <, >, &
webextPerms.sideloadHeader=%S added
webextPerms.sideloadText2=Another program on your computer installed an add-on that may affect your browser. Please review this add-on’s permissions requests and choose to Enable or Cancel (to leave it disabled).
webextPerms.sideloadTextNoPerms=Another program on your computer installed an add-on that may affect your browser. Please choose to Enable or Cancel (to leave it disabled).

webextPerms.sideloadEnable.label=Enable
webextPerms.sideloadEnable.accessKey=E
webextPerms.sideloadCancel.label=Cancel
webextPerms.sideloadCancel.accessKey=C

# LOCALIZATION NOTE (webextPerms.updateMenuItem)
# %S will be replaced with the localized name of the extension which
# has been updated.
webextPerms.updateMenuItem=%S requires new permissions

# LOCALIZATION NOTE (webextPerms.updateText)
# %S is replaced with the localized name of the updated extension.
# Note, this string will be used as raw markup. Avoid characters like <, >, &
webextPerms.updateText=%S has been updated. You must approve new permissions before the updated version will install. Choosing “Cancel” will maintain your current add-on version.

webextPerms.updateAccept.label=Update
webextPerms.updateAccept.accessKey=U

# LOCALIZATION NOTE (webextPerms.optionalPermsHheader)
# %S is replace with the localized name of the extension requested new
# permissions.
# Note, this string will be used as raw markup. Avoid characters like <, >, &
webextPerms.optionalPermsHeader=%S requests additional permissions.
webextPerms.optionalPermsListIntro=It wants to:
webextPerms.optionalPermsAllow.label=Allow
webextPerms.optionalPermsAllow.accessKey=A
webextPerms.optionalPermsDeny.label=Deny
webextPerms.optionalPermsDeny.accessKey=D

webextPerms.description.bookmarks=Read and modify bookmarks
webextPerms.description.browserSettings=Read and modify browser settings
webextPerms.description.browsingData=Clear recent browsing history, cookies, and related data
webextPerms.description.clipboardRead=Get data from the clipboard
webextPerms.description.clipboardWrite=Input data to the clipboard
webextPerms.description.devtools=Extend developer tools to access your data in open tabs
webextPerms.description.dns=Access IP address and hostname information
webextPerms.description.downloads=Download files and read and modify the browser’s download history
webextPerms.description.downloads.open=Open files downloaded to your computer
webextPerms.description.find=Read the text of all open tabs
webextPerms.description.geolocation=Access your location
webextPerms.description.history=Access browsing history
webextPerms.description.management=Monitor extension usage and manage themes
# LOCALIZATION NOTE (webextPerms.description.nativeMessaging)
# %S will be replaced with the name of the application
webextPerms.description.nativeMessaging=Exchange messages with programs other than %S
webextPerms.description.notifications=Display notifications to you
webextPerms.description.pkcs11=Provide cryptographic authentication services
webextPerms.description.privacy=Read and modify privacy settings
webextPerms.description.proxy=Control browser proxy settings
webextPerms.description.sessions=Access recently closed tabs
webextPerms.description.tabs=Access browser tabs
webextPerms.description.tabHide=Hide and show browser tabs
webextPerms.description.topSites=Access browsing history
webextPerms.description.unlimitedStorage=Store unlimited amount of client-side data
webextPerms.description.webNavigation=Access browser activity during navigation

webextPerms.hostDescription.allUrls=Access your data for all websites

# LOCALIZATION NOTE (webextPerms.hostDescription.wildcard)
# %S will be replaced by the DNS domain for which a webextension
# is requesting access (e.g., mozilla.org)
webextPerms.hostDescription.wildcard=Access your data for sites in the %S domain

# LOCALIZATION NOTE (webextPerms.hostDescription.tooManyWildcards):
# Semi-colon list of plural forms.
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
# #1 will be replaced by an integer indicating the number of additional
# domains for which this webextension is requesting permission.
webextPerms.hostDescription.tooManyWildcards=Access your data in #1 other domain;Access your data in #1 other domains

# LOCALIZATION NOTE (webextPerms.hostDescription.oneSite)
# %S will be replaced by the DNS host name for which a webextension
# is requesting access (e.g., www.mozilla.org)
webextPerms.hostDescription.oneSite=Access your data for %S

# LOCALIZATION NOTE (webextPerms.hostDescription.tooManySites)
# Semi-colon list of plural forms.
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
# #1 will be replaced by an integer indicating the number of additional
# hosts for which this webextension is requesting permission.
webextPerms.hostDescription.tooManySites=Access your data on #1 other site;Access your data on #1 other sites

# LOCALIZATION NOTE (webext.defaultSearch.description)
# %1$S is replaced with the localized named of the extension that is asking to change the default search engine.
# %2$S is replaced with the name of the current search engine
# %3$S is replaced with the name of the new search engine
webext.defaultSearch.description=%1$S would like to change your default search engine from %2$S to %3$S. Is that OK?
webext.defaultSearchYes.label=Yes
webext.defaultSearchYes.accessKey=Y
webext.defaultSearchNo.label=No
webext.defaultSearchNo.accessKey=N

# LOCALIZATION NOTE (addonPostInstall.message)
# %1$S is replaced with the localized named of the extension that was
# just installed.
# %2$S is replaced with the localized name of the application.
addonPostInstall.message1=%1$S has been added to %2$S.
addonPostInstall.okay.label=OK
addonPostInstall.okay.key=O

# LOCALIZATION NOTE (addonDownloadingAndVerifying):
# Semicolon-separated list of plural forms. See:
# http://developer.mozilla.org/en/docs/Localization_and_Plurals
# Also see https://bugzilla.mozilla.org/show_bug.cgi?id=570012 for mockups
addonDownloadingAndVerifying=Downloading and verifying add-on…;Downloading and verifying #1 add-ons…
addonDownloadVerifying=Verifying

addonInstall.unsigned=(Unverified)
addonInstall.cancelButton.label=Cancel
addonInstall.cancelButton.accesskey=C
addonInstall.acceptButton2.label=Add
addonInstall.acceptButton2.accesskey=A

# LOCALIZATION NOTE (addonConfirmInstallMessage,addonConfirmInstallUnsigned):
# Semicolon-separated list of plural forms. See:
# http://developer.mozilla.org/en/docs/Localization_and_Plurals
# #1 is brandShortName
# #2 is the number of add-ons being installed
addonConfirmInstall.message=This site would like to install an add-on in #1:;This site would like to install #2 add-ons in #1:
addonConfirmInstallUnsigned.message=Caution: This site would like to install an unverified add-on in #1. Proceed at your own risk.;Caution: This site would like to install #2 unverified add-ons in #1. Proceed at your own risk.

# LOCALIZATION NOTE (addonConfirmInstallSomeUnsigned.message):
# Semicolon-separated list of plural forms. See:
# http://developer.mozilla.org/en/docs/Localization_and_Plurals
# #1 is brandShortName
# #2 is the total number of add-ons being installed (at least 2)
addonConfirmInstallSomeUnsigned.message=;Caution: This site would like to install #2 add-ons in #1, some of which are unverified. Proceed at your own risk.

# LOCALIZATION NOTE (addonInstalled):
# %S is the name of the add-on
addonInstalled=%S has been installed successfully.
# LOCALIZATION NOTE (addonsGenericInstalled):
# Semicolon-separated list of plural forms. See:
# http://developer.mozilla.org/en/docs/Localization_and_Plurals
# #1 number of add-ons
addonsGenericInstalled=#1 add-on has been installed successfully.;#1 add-ons have been installed successfully.
# LOCALIZATION NOTE (addonInstalledNeedsRestart):
# %1$S is the name of the add-on, %2$S is the application's name
addonInstalledNeedsRestart=%1$S will be installed after you restart %2$S.
# LOCALIZATION NOTE (addonsGenericInstalledNeedsRestart):
# Semicolon-separated list of plural forms. See:
# http://developer.mozilla.org/en/docs/Localization_and_Plurals
# #1 number of add-ons. #2 application's name
addonsGenericInstalledNeedsRestart=#1 add-on will be installed after you restart #2.;#1 add-ons will be installed after you restart #2.
addonInstallRestartButton=Restart Now
addonInstallRestartButton.accesskey=R
addonInstallRestartIgnoreButton=Not Now
addonInstallRestartIgnoreButton.accesskey=N

# LOCALIZATION NOTE (addonInstallError-1, addonInstallError-2, addonInstallError-3, addonInstallError-4, addonInstallError-5, addonLocalInstallError-1, addonLocalInstallError-2, addonLocalInstallError-3, addonLocalInstallError-4, addonLocalInstallError-5):
# %1$S is the application name, %2$S is the add-on name
addonInstallError-1=The add-on could not be downloaded because of a connection failure.
addonInstallError-2=The add-on could not be installed because it does not match the add-on %1$S expected.
addonInstallError-3=The add-on downloaded from this site could not be installed because it appears to be corrupt.
addonInstallError-4=%2$S could not be installed because %1$S cannot modify the needed file.
addonInstallError-5=%1$S has prevented this site from installing an unverified add-on.
addonLocalInstallError-1=This add-on could not be installed because of a filesystem error.
addonLocalInstallError-2=This add-on could not be installed because it does not match the add-on %1$S expected.
addonLocalInstallError-3=This add-on could not be installed because it appears to be corrupt.
addonLocalInstallError-4=%2$S could not be installed because %1$S cannot modify the needed file.
addonLocalInstallError-5=This add-on could not be installed because it has not been verified.

# LOCALIZATION NOTE (addonInstallErrorIncompatible):
# %1$S is the application name, %2$S is the application version, %3$S is the add-on name
addonInstallErrorIncompatible=%3$S could not be installed because it is not compatible with %1$S %2$S.

# LOCALIZATION NOTE (addonInstallErrorBlocklisted): %S is add-on name
addonInstallErrorBlocklisted=%S could not be installed because it has a high risk of causing stability or security problems.

unsignedAddonsDisabled.message=One or more installed add-ons cannot be verified and have been disabled.
unsignedAddonsDisabled.learnMore.label=Learn More
unsignedAddonsDisabled.learnMore.accesskey=L

# LOCALIZATION NOTE (lightTheme.name): This is displayed in about:addons -> Appearance
lightTheme.name=Light
lightTheme.description=A theme with a light color scheme.

# LOCALIZATION NOTE (darkTheme.name): This is displayed in about:addons -> Appearance
darkTheme.name=Dark
darkTheme.description=A theme with a dark color scheme.

# LOCALIZATION NOTE (lwthemeInstallRequest.message2): %S will be replaced with
# the host name of the site.
lwthemeInstallRequest.message2=This site (%S) attempted to install a theme.
lwthemeInstallRequest.allowButton2=Allow
lwthemeInstallRequest.allowButton.accesskey2=a

# LOCALIZATION NOTE (lwthemeNeedsRestart.message):
# %S will be replaced with the new theme name.
lwthemeNeedsRestart.message=%S will be installed after you restart.
lwthemeNeedsRestart.button=Restart Now
lwthemeNeedsRestart.accesskey=R

# LOCALIZATION NOTE (popupWarning.message): Semicolon-separated list of plural forms.
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
# #1 is brandShortName and #2 is the number of pop-ups blocked.
popupWarning.message=#1 prevented this site from opening a pop-up window.;#1 prevented this site from opening #2 pop-up windows.
popupWarningButton=Options
popupWarningButton.accesskey=O
popupWarningButtonUnix=Preferences
popupWarningButtonUnix.accesskey=P
popupAllow=Allow pop-ups for %S
popupBlock=Block pop-ups for %S
popupWarningDontShowFromMessage=Don’t show this message when pop-ups are blocked
popupShowPopupPrefix=Show ‘%S’

# LOCALIZATION NOTE (popupShowBlockedPopupsIndicatorText): Semicolon seperated list of plural forms.
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
# #1 is the number of pop-ups blocked.
popupShowBlockedPopupsIndicatorText=Show #1 blocked pop-up…;Show #1 blocked pop-ups…

# Bad Content Blocker Doorhanger Notification
# %S is brandShortName
badContentBlocked.blocked.message=%S is blocking content on this page.
badContentBlocked.notblocked.message=%S is not blocking any content on this page.

crashedpluginsMessage.title=The %S plugin has crashed.
crashedpluginsMessage.reloadButton.label=Reload page
crashedpluginsMessage.reloadButton.accesskey=R
crashedpluginsMessage.submitButton.label=Submit a crash report
crashedpluginsMessage.submitButton.accesskey=S
crashedpluginsMessage.learnMore=Learn More…

# Keyword fixup messages
# LOCALIZATION NOTE (keywordURIFixup.message): Used when the user tries to visit
# a local host page, by the time the DNS request recognizes it, we have already
# loaded a search page for the given word.  An infobar then asks to the user
# whether he rather wanted to visit the host.  %S is the recognized host.
keywordURIFixup.message=Did you mean to go to %S?
keywordURIFixup.goTo=Yes, take me to %S
keywordURIFixup.goTo.accesskey=Y
keywordURIFixup.dismiss=No thanks
keywordURIFixup.dismiss.accesskey=N

## Plugin doorhanger strings
# LOCALIZATION NOTE (pluginActivate2.message):
# Used for normal plugin activation if we don't know of a specific security issue.
# %1$S is the plugin name, %2$S is the domain, and %3$S is brandShortName.
pluginActivate2.message=Would you like to allow %2$S to run %1$S? Plugins may slow %3$S.
pluginActivateMultiple.message=Allow %S to run plugins?

# LOCALIZATION NOTE (pluginActivationWarning.message): this should use the
# same string as "pluginActivationWarning" in pluginproblem.dtd
pluginActivationWarning.message=This site uses a plugin that may slow %S.

pluginActivate.learnMore=Learn More…
# LOCALIZATION NOTE (pluginActivateOutdated.message, pluginActivateOutdated.label):
# These strings are used when an unsafe plugin has an update available.
# %1$S is the plugin name, %2$S is the domain, and %3$S is brandShortName.
pluginActivateOutdated.message=%3$S has prevented the outdated plugin “%1$S” from running on %2$S.
pluginActivateOutdated.label=Outdated plugin
pluginActivate.updateLabel=Update now…
# LOCALIZATION NOTE (pluginActivateVulnerable.message):
# This string is used when an unsafe plugin has no update available.
# %1$S is the plugin name, %2$S is the domain, and %3$S is brandShortName.
pluginActivateVulnerable.message=%3$S has prevented the unsafe plugin “%1$S” from running on %2$S.
pluginInfo.unknownPlugin=Unknown

# LOCALIZATION NOTE (pluginActivateNow.label, pluginActivateAlways.label, pluginBlockNow.label): These should be the same as the matching strings in browser.dtd
# LOCALIZATION NOTE (pluginActivateNow.label): This button will enable the
# plugin in the current session for an short time (about an hour), auto-renewed
# if the site keeps using the plugin.
pluginActivateNow.label=Allow Now
pluginActivateNow.accesskey=N
# LOCALIZATION NOTE (pluginActivateAlways.label): This button will enable the
# plugin for a long while (90 days), auto-renewed if the site keeps using the
# plugin.
pluginActivateAlways.label=Allow and Remember
pluginActivateAlways.accesskey=R
pluginBlockNow.label=Block Plugin
pluginBlockNow.accesskey=B
pluginContinue.label=Continue Allowing
pluginContinue.accesskey=C

# Flash activation doorhanger UI
flashActivate.message=Do you want to allow Adobe Flash to run on this site? Only allow Adobe Flash on sites you trust.
flashActivate.outdated.message=Do you want to allow an outdated version of Adobe Flash to run on this site? An outdated version can affect browser performance and security.
flashActivate.remember=Remember this decision
flashActivate.noAllow=Don’t Allow
flashActivate.allow=Allow
flashActivate.noAllow.accesskey=D
flashActivate.allow.accesskey=A

# in-page UI
# LOCALIZATION NOTE (PluginClickToActivate2): Two changes were done to the
# previous version of the string. The first is that we changed the wording from
# "Activate" to "Run", because it's shorter and feels less technical in English.
# Feel free to keep using the previous wording in your language if it's already
# the best one.
# The second change is that we removed the period at the end of the phrase, because
# it's not natural in our UI, and the underline was removed from this, so it doesn't
# look like a link anymore. We suggest that everyone removes that period too.
PluginClickToActivate2=Run %S
PluginVulnerableUpdatable=This plugin is vulnerable and should be updated.
PluginVulnerableNoUpdate=This plugin has security vulnerabilities.

# infobar UI
pluginContinueBlocking.label=Continue Blocking
pluginContinueBlocking.accesskey=B
# LOCALIZATION NOTE (pluginActivateTrigger): Use the unicode ellipsis char, \u2026,
# or use "..." if \u2026 doesn't suit traditions in your locale.
pluginActivateTrigger.label=Allow…
pluginActivateTrigger.accesskey=A

# Sanitize
# LOCALIZATION NOTE (sanitizeDialog2.everything.title): When "Time range to
# clear" is set to "Everything", the Clear Recent History dialog's title is
# changed to this.  See UI mockup and comment 11 at bug 480169 -->
sanitizeDialog2.everything.title=Clear All History
sanitizeButtonOK=Clear Now
# LOCALIZATION NOTE (sanitizeButtonClearing): The label for the default
# button between the user clicking it and the window closing.  Indicates the
# items are being cleared.
sanitizeButtonClearing=Clearing

# LOCALIZATION NOTE (sanitizeEverythingWarning2): Warning that appears when
# "Time range to clear" is set to "Everything" in Clear Recent History dialog,
# provided that the user has not modified the default set of history items to clear.
sanitizeEverythingWarning2=All history will be cleared.
# LOCALIZATION NOTE (sanitizeSelectedWarning): Warning that appears when
# "Time range to clear" is set to "Everything" in Clear Recent History dialog,
# provided that the user has modified the default set of history items to clear.
sanitizeSelectedWarning=All selected items will be cleared.

# LOCALIZATION NOTE (downloadAndInstallButton.label): %S is replaced by the
# version of the update: "Update to 28.0".
update.downloadAndInstallButton.label=Update to %S
update.downloadAndInstallButton.accesskey=U

menuOpenAllInTabs.label=Open All in Tabs

# History menu
menuRestoreAllTabs.label=Restore All Tabs
# LOCALIZATION NOTE (menuRestoreAllTabsSubview.label): like menuRestoreAllTabs.label,
# but used in the history subview in the panel UI, so needs to mention these are *closed* tabs.
menuRestoreAllTabsSubview.label=Restore Closed Tabs
# LOCALIZATION NOTE (menuRestoreAllWindows, menuUndoCloseWindowLabel, menuUndoCloseWindowSingleTabLabel):
# see bug 394759
menuRestoreAllWindows.label=Restore All Windows
# LOCALIZATION NOTE (menuRestoreAllWindowsSubview.label): like menuRestoreAllWindows.label,
# but used in the history subview in the panel UI, so needs to mention these are *closed* windows.
menuRestoreAllWindowsSubview.label=Restore Closed Windows
# LOCALIZATION NOTE (menuUndoCloseWindowLabel): Semicolon-separated list of plural forms.
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
# #1 Window Title, #2 Number of tabs
menuUndoCloseWindowLabel=#1 (and #2 other tab);#1 (and #2 other tabs)
menuUndoCloseWindowSingleTabLabel=#1

# Unified Back-/Forward Popup
tabHistory.current=Stay on this page
tabHistory.goBack=Go back to this page
tabHistory.goForward=Go forward to this page

# URL Bar
pasteAndGo.label=Paste & Go
# LOCALIZATION NOTE (reloadButton.tooltip):
# %S is the keyboard shortcut for reloading the current page
reloadButton.tooltip=Reload current page (%S)
# LOCALIZATION NOTE (stopButton.tooltip):
# %S is the keyboard shortcut for stopping loading the page
stopButton.tooltip=Stop loading this page (%S)
# LOCALIZATION NOTE (urlbar-zoom-button.tooltip):
# %S is the keyboard shortcut for resetting the zoom level to 100%
urlbar-zoom-button.tooltip=Reset zoom level (%S)
# LOCALIZATION NOTE (reader-mode-button.tooltip):
# %S is the keyboard shortcut for entering/exiting reader view
reader-mode-button.tooltip=Toggle reader view (%S)
# LOCALIZATION NOTE (urlbar.placeholder):
# %S is the name of the user's current search engine
urlbar.placeholder=Search with %S or enter address
# LOCALIZATION NOTE (urlbarSearchTip.onboarding):
# %S is the name of the user's current search engine
urlbarSearchTip.onboarding=Type less, find more: Search %S right from your address bar.
# LOCALIZATION NOTE (urlbarSearchTip.engineIsCurrentPage):
# %S is the name of the user's current search engine
urlbarSearchTip.engineIsCurrentPage=Start your search here to see suggestions from %S and your browsing history.

# LOCALIZATION NOTE(zoom-button.label): %S is the current page zoom level,
# %% will be displayed as a single % character (% is commonly used to define
# format specifiers, so it needs to be escaped).
zoom-button.label = %S%%

# Block autorefresh
refreshBlocked.goButton=Allow
refreshBlocked.goButton.accesskey=A
refreshBlocked.refreshLabel=%S prevented this page from automatically reloading.
refreshBlocked.redirectLabel=%S prevented this page from automatically redirecting to another page.

# General bookmarks button
# LOCALIZATION NOTE (bookmarksMenuButton.tooltip):
# %S is the keyboard shortcut for "Show All Bookmarks"
bookmarksMenuButton.tooltip=Show your bookmarks (%S)
# Star button
starButtonOn.tooltip2=Edit this bookmark (%S)
starButtonOff.tooltip2=Bookmark this page (%S)

# Downloads button tooltip
# LOCALIZATION NOTE (downloads.tooltip):
# %S is the keyboard shortcut for "Downloads"
downloads.tooltip=Display the progress of ongoing downloads (%S)

# Print button tooltip on OS X
# LOCALIZATION NOTE (printButton.tooltip):
# Use the unicode ellipsis char, \u2026,
# or use "..." if \u2026 doesn't suit traditions in your locale.
# %S is the keyboard shortcut for "Print"
printButton.tooltip=Print this page… (%S)

# New Window button tooltip
# LOCALIZATION NOTE (newWindowButton.tooltip):
# %S is the keyboard shortcut for "New Window"
newWindowButton.tooltip=Open a new window (%S)

# New Tab button tooltip
# LOCALIZATION NOTE (newTabButton.tooltip):
# %S is the keyboard shortcut for "New Tab"
newTabButton.tooltip=Open a new tab (%S)

# Offline web applications
offlineApps.available2=Will you allow %S to store data on your computer?
offlineApps.allowStoring.label=Allow Storing Data
offlineApps.allowStoring.accesskey=A
offlineApps.dontAllow.label=Don’t Allow
offlineApps.dontAllow.accesskey=n

offlineApps.usage=This website (%S) is now storing more than %SMB of data on your computer for offline use.
offlineApps.manageUsage=Show settings
offlineApps.manageUsageAccessKey=S

# Canvas permission prompt
# LOCALIZATION NOTE (canvas.siteprompt): %S is hostname
canvas.siteprompt=Will you allow %S to use your HTML5 canvas image data? This may be used to uniquely identify your computer.
canvas.notAllow=Don’t Allow
canvas.notAllow.accesskey=n
canvas.allow=Allow Data Access
canvas.allow.accesskey=A
canvas.remember=Always remember my decision

# WebAuthn prompts
# LOCALIZATION NOTE (webauthn.registerPrompt): %S is hostname
webauthn.registerPrompt=%S wants to register an account with one of your security tokens. You can connect and authorize one now, or cancel.
# LOCALIZATION NOTE (webauthn.registerDirectPrompt):
# %1$S is hostname. %2$S is brandShortName.
# The website is asking for extended information about your
# hardware authenticator that shouldn't be generally necessary. Permitting
# this is safe if you only use one account at this website. If you have
# multiple accounts at this website, and you use the same hardware
# authenticator, then the website could link those accounts together.
# And this is true even if you use a different profile / browser (or even Tor
# Browser). To avoid this, you should use different hardware authenticators
# for different accounts on this website.
webauthn.registerDirectPrompt=%1$S is requesting extended information about your authenticator, which may affect your privacy.\n\n%2$S can anonymize this for you, but the website might decline this authenticator. If declined, you can try again.
# LOCALIZATION NOTE (webauthn.signPrompt): %S is hostname
webauthn.signPrompt=%S wants to authenticate you using a registered security token. You can connect and authorize one now, or cancel.
webauthn.cancel=Cancel
webauthn.cancel.accesskey=c
webauthn.proceed=Proceed
webauthn.proceed.accesskey=p
webauthn.anonymize=Anonymize anyway

# Spoof Accept-Language prompt
privacy.spoof_english=Changing your language setting to English will make you more difficult to identify and enhance your privacy. Do you want to request English language versions of web pages?

identity.identified.verifier=Verified by: %S
identity.identified.verified_by_you=You have added a security exception for this site.
identity.identified.state_and_country=%S, %S

# LOCALIZATION NOTE (identity.notSecure.label):
# Keep this string as short as possible, this is displayed in the URL bar
# use a synonym for "safe" or "private" if "secure" is too long.
identity.notSecure.label=Not Secure

identity.icon.tooltip=Show site information
identity.extension.label=Extension (%S)
identity.extension.tooltip=Loaded by extension: %S
identity.showDetails.tooltip=Show connection details

trackingProtection.intro.title=How Tracking Protection works
# LOCALIZATION NOTE (trackingProtection.intro.description2):
# %S is brandShortName. This string should match the one from Step 1 of the tour
# when it starts from the button shown when a new private window is opened.
trackingProtection.intro.description2=When you see the shield, %S is blocking some parts of the page that could track your browsing activity.
# LOCALIZATION NOTE (trackingProtection.intro.step1of3): Indicates that the intro panel is step one of three in a tour.
trackingProtection.intro.step1of3=1 of 3
trackingProtection.intro.nextButton.label=Next

trackingProtection.icon.activeTooltip=Tracking attempts blocked
trackingProtection.icon.disabledTooltip=Tracking content detected

# Edit Bookmark UI
editBookmarkPanel.pageBookmarkedTitle=Page Bookmarked
editBookmarkPanel.pageBookmarkedDescription=%S will always remember this page for you.
editBookmarkPanel.bookmarkedRemovedTitle=Bookmark Removed
editBookmarkPanel.editBookmarkTitle=Edit This Bookmark

# LOCALIZATION NOTE (editBookmark.removeBookmarks.label): Semicolon-separated list of plural forms.
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
# Replacement for #1 is the number of bookmarks to be removed.
# If this causes problems with localization you can also do "Remove Bookmarks (#1)"
# instead of "Remove #1 Bookmarks".
editBookmark.removeBookmarks.label=Remove Bookmark;Remove #1 Bookmarks

# Post Update Notifications
pu.notifyButton.label=Details…
pu.notifyButton.accesskey=D
# LOCALIZATION NOTE %S will be replaced by the short name of the application.
puNotifyText=%S has been updated
puAlertTitle=%S Updated
puAlertText=Click here for details

# Application menu

# LOCALIZATION NOTE(zoomReduce-button.tooltip): %S is the keyboard shortcut.
zoomReduce-button.tooltip = Zoom out (%S)
# LOCALIZATION NOTE(zoomReset-button.tooltip): %S is the keyboard shortcut.
zoomReset-button.tooltip = Reset zoom level (%S)
# LOCALIZATION NOTE(zoomEnlarge-button.tooltip): %S is the keyboard shortcut.
zoomEnlarge-button.tooltip = Zoom in (%S)

# LOCALIZATION NOTE (cut-button.tooltip): %S is the keyboard shortcut.
cut-button.tooltip = Cut (%S)
# LOCALIZATION NOTE (copy-button.tooltip): %S is the keyboard shortcut.
copy-button.tooltip = Copy (%S)
# LOCALIZATION NOTE (paste-button.tooltip): %S is the keyboard shortcut.
paste-button.tooltip = Paste (%S)

# Geolocation UI

geolocation.allowLocation=Allow Location Access
geolocation.allowLocation.accesskey=A
geolocation.dontAllowLocation=Don’t Allow
geolocation.dontAllowLocation.accesskey=n
geolocation.shareWithSite3=Will you allow %S to access your location?
geolocation.shareWithFile3=Will you allow this local file to access your location?
geolocation.remember=Remember this decision

# Persistent storage UI
persistentStorage.allow=Allow
persistentStorage.allow.accesskey=A
persistentStorage.dontAllow=Don’t Allow
persistentStorage.dontAllow.accesskey=n
persistentStorage.allowWithSite=Will you allow %S to store data in persistent storage?
persistentStorage.remember=Remember this decision

webNotifications.allow=Allow Notifications
webNotifications.allow.accesskey=A
webNotifications.notNow=Not Now
webNotifications.notNow.accesskey=n
webNotifications.never=Never Allow
webNotifications.never.accesskey=v
webNotifications.receiveFromSite2=Will you allow %S to send notifications?

# Phishing/Malware Notification Bar.
# LOCALIZATION NOTE (notADeceptiveSite, notAnAttack)
# The two button strings will never be shown at the same time, so
# it's okay for them to have the same access key
safebrowsing.getMeOutOfHereButton.label=Get me out of here!
safebrowsing.getMeOutOfHereButton.accessKey=G
safebrowsing.deceptiveSite=Deceptive Site!
safebrowsing.notADeceptiveSiteButton.label=This isn’t a deceptive site…
safebrowsing.notADeceptiveSiteButton.accessKey=D
safebrowsing.reportedAttackSite=Reported Attack Site!
safebrowsing.notAnAttackButton.label=This isn’t an attack site…
safebrowsing.notAnAttackButton.accessKey=A
safebrowsing.reportedUnwantedSite=Reported Unwanted Software Site!
safebrowsing.reportedHarmfulSite=Reported Harmful Site!

# Ctrl-Tab
# LOCALIZATION NOTE (ctrlTab.listAllTabs.label): #1 represents the number
# of tabs in the current browser window. It will always be 2 at least.
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
ctrlTab.listAllTabs.label=;List All #1 Tabs

# LOCALIZATION NOTE (addKeywordTitleAutoFill): %S will be replaced by the page's title
# Used as the bookmark name when saving a keyword for a search field.
addKeywordTitleAutoFill=Search %S

extensions.{972ce4c6-7e08-4474-a285-3208198ce6fd}.name=Default
extensions.{972ce4c6-7e08-4474-a285-3208198ce6fd}.description=The default theme.

# safeModeRestart
safeModeRestartPromptTitle=Restart with Add-ons Disabled
safeModeRestartPromptMessage=Are you sure you want to disable all add-ons and restart?
safeModeRestartButton=Restart

# LOCALIZATION NOTE (browser.menu.showCharacterEncoding): Set to the string
# "true" (spelled and capitalized exactly that way) to show the "Text
# Encoding" menu in the main Firefox button on Windows. Any other value will
# hide it. Regardless of the value of this setting, the "Text Encoding"
# menu will always be accessible via the "Web Developer" menu.
# This is not a string to translate; it just controls whether the menu shows
# up in the Firefox button. If users frequently use the "Text Encoding"
# menu, set this to "true". Otherwise, you can leave it as "false".
browser.menu.showCharacterEncoding=false

# Mozilla data reporting notification (Telemetry, Firefox Health Report, etc)
dataReportingNotification.message       = %1$S automatically sends some data to %2$S so that we can improve your experience.
dataReportingNotification.button.label  = Choose What I Share
dataReportingNotification.button.accessKey  = C

# Process hang reporter
processHang.label = A web page is slowing down your browser. What would you like to do?
# LOCALIZATION NOTE (processHang.add-on.label): %1$S is the name of the
# extension. %2$S is the name of the product (e.g., Firefox)
processHang.add-on.label = A script in the extension “%1$S” is causing %2$S to slow down.
processHang.add-on.learn-more.text = Learn more
processHang.button_stop.label = Stop It
processHang.button_stop.accessKey = S
processHang.button_stop_sandbox.label = Temporarily Disable Extension on Page
processHang.button_stop_sandbox.accessKey = A
processHang.button_wait.label = Wait
processHang.button_wait.accessKey = W
processHang.button_debug.label = Debug Script
processHang.button_debug.accessKey = D

# LOCALIZATION NOTE (fullscreenButton.tooltip): %S is the keyboard shortcut for full screen
fullscreenButton.tooltip=Display the window in full screen (%S)

# These are visible when opening the popup inside the bookmarks sidebar
sidebar.moveToLeft=Move Sidebar to Left
sidebar.moveToRight=Move Sidebar to Right

# LOCALIZATION NOTE (getUserMedia.shareCamera2.message,
#                    getUserMedia.shareMicrophone2.message,
#                    getUserMedia.shareScreen3.message,
#                    getUserMedia.shareCameraAndMicrophone2.message,
#                    getUserMedia.shareCameraAndAudioCapture2.message,
#                    getUserMedia.shareScreenAndMicrophone3.message,
#                    getUserMedia.shareScreenAndAudioCapture3.message,
#                    getUserMedia.shareAudioCapture2.message):
# %S is the website origin (e.g. www.mozilla.org)
getUserMedia.shareCamera2.message = Will you allow %S to use your camera?
getUserMedia.shareMicrophone2.message = Will you allow %S to use your microphone?
getUserMedia.shareScreen3.message = Will you allow %S to see your screen?
getUserMedia.shareCameraAndMicrophone2.message = Will you allow %S to use your camera and microphone?
getUserMedia.shareCameraAndAudioCapture2.message = Will you allow %S to use your camera and listen to this tab’s audio?
getUserMedia.shareScreenAndMicrophone3.message = Will you allow %S to use your microphone and see your screen?
getUserMedia.shareScreenAndAudioCapture3.message = Will you allow %S to listen to this tab’s audio and see your screen?
getUserMedia.shareAudioCapture2.message = Will you allow %S to listen to this tab’s audio?
# LOCALIZATION NOTE (getUserMedia.shareScreenWarning.message): NB: inserted via innerHTML, so please don't use <, > or & in this string.
# %S will be the 'learn more' link
getUserMedia.shareScreenWarning.message = Only share screens with sites you trust. Sharing can allow deceptive sites to browse as you and steal your private data. %S
# LOCALIZATION NOTE (getUserMedia.shareFirefoxWarning.message): NB: inserted via innerHTML, so please don't use <, > or & in this string.
# %1$S is brandShortName (eg. Firefox)
# %2$S will be the 'learn more' link
getUserMedia.shareFirefoxWarning.message = Only share %1$S with sites you trust. Sharing can allow deceptive sites to browse as you and steal your private data. %2$S
# LOCALIZATION NOTE(getUserMedia.shareScreen.learnMoreLabel): NB: inserted via innerHTML, so please don't use <, > or & in this string.
getUserMedia.shareScreen.learnMoreLabel = Learn More
getUserMedia.selectWindow.label=Window to share:
getUserMedia.selectWindow.accesskey=W
getUserMedia.selectScreen.label=Screen to share:
getUserMedia.selectScreen.accesskey=S
getUserMedia.selectApplication.label=Application to share:
getUserMedia.selectApplication.accesskey=A
getUserMedia.pickApplication.label = Select Application
getUserMedia.pickScreen.label = Select Screen
getUserMedia.pickWindow.label = Select Window
getUserMedia.shareEntireScreen.label = Entire screen
# LOCALIZATION NOTE (getUserMedia.shareMonitor.label):
# %S is screen number (digits 1, 2, etc)
# Example: Screen 1, Screen 2,..
getUserMedia.shareMonitor.label = Screen %S
# LOCALIZATION NOTE (getUserMedia.shareApplicationWindowCount.label):
# Semicolon-separated list of plural forms.
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
# Replacement for #1 is the name of the application.
# Replacement for #2 is the number of windows currently displayed by the application.
getUserMedia.shareApplicationWindowCount.label=#1 (#2 window);#1 (#2 windows)
# LOCALIZATION NOTE (getUserMedia.allow.label,
#                    getUserMedia.dontAllow.label):
# These two buttons are the possible answers to the various prompts in the
# "getUserMedia.share{device}.message" strings.
getUserMedia.allow.label = Allow
getUserMedia.allow.accesskey = A
getUserMedia.dontAllow.label = Don’t Allow
getUserMedia.dontAllow.accesskey = D
getUserMedia.remember=Remember this decision
# LOCALIZATION NOTE (getUserMedia.reasonForNoPermanentAllow.screen3,
#                    getUserMedia.reasonForNoPermanentAllow.audio,
#                    getUserMedia.reasonForNoPermanentAllow.insecure):
# %S is brandShortName
getUserMedia.reasonForNoPermanentAllow.screen3=%S can not allow permanent access to your screen.
getUserMedia.reasonForNoPermanentAllow.audio=%S can not allow permanent access to your tab’s audio without asking which tab to share.
getUserMedia.reasonForNoPermanentAllow.insecure=Your connection to this site is not secure. To protect you, %S will only allow access for this session.

getUserMedia.sharingMenu.label = Tabs sharing devices
getUserMedia.sharingMenu.accesskey = d
# LOCALIZATION NOTE (getUserMedia.sharingMenuCamera
#                    getUserMedia.sharingMenuMicrophone,
#                    getUserMedia.sharingMenuAudioCapture,
#                    getUserMedia.sharingMenuApplication,
#                    getUserMedia.sharingMenuScreen,
#                    getUserMedia.sharingMenuWindow,
#                    getUserMedia.sharingMenuBrowser,
#                    getUserMedia.sharingMenuCameraMicrophone,
#                    getUserMedia.sharingMenuCameraMicrophoneApplication,
#                    getUserMedia.sharingMenuCameraMicrophoneScreen,
#                    getUserMedia.sharingMenuCameraMicrophoneWindow,
#                    getUserMedia.sharingMenuCameraMicrophoneBrowser,
#                    getUserMedia.sharingMenuCameraAudioCapture,
#                    getUserMedia.sharingMenuCameraAudioCaptureApplication,
#                    getUserMedia.sharingMenuCameraAudioCaptureScreen,
#                    getUserMedia.sharingMenuCameraAudioCaptureWindow,
#                    getUserMedia.sharingMenuCameraAudioCaptureBrowser,
#                    getUserMedia.sharingMenuCameraApplication,
#                    getUserMedia.sharingMenuCameraScreen,
#                    getUserMedia.sharingMenuCameraWindow,
#                    getUserMedia.sharingMenuCameraBrowser,
#                    getUserMedia.sharingMenuMicrophoneApplication,
#                    getUserMedia.sharingMenuMicrophoneScreen,
#                    getUserMedia.sharingMenuMicrophoneWindow,
#                    getUserMedia.sharingMenuMicrophoneBrowser,
#                    getUserMedia.sharingMenuAudioCaptureApplication,
#                    getUserMedia.sharingMenuAudioCaptureScreen,
#                    getUserMedia.sharingMenuAudioCaptureWindow,
#                    getUserMedia.sharingMenuAudioCaptureBrowser):
# %S is the website origin (e.g. www.mozilla.org)
getUserMedia.sharingMenuCamera = %S (camera)
getUserMedia.sharingMenuMicrophone = %S (microphone)
getUserMedia.sharingMenuAudioCapture = %S (tab audio)
getUserMedia.sharingMenuApplication = %S (application)
getUserMedia.sharingMenuScreen = %S (screen)
getUserMedia.sharingMenuWindow = %S (window)
getUserMedia.sharingMenuBrowser = %S (tab)
getUserMedia.sharingMenuCameraMicrophone = %S (camera and microphone)
getUserMedia.sharingMenuCameraMicrophoneApplication = %S (camera, microphone and application)
getUserMedia.sharingMenuCameraMicrophoneScreen = %S (camera, microphone and screen)
getUserMedia.sharingMenuCameraMicrophoneWindow = %S (camera, microphone and window)
getUserMedia.sharingMenuCameraMicrophoneBrowser = %S (camera, microphone and tab)
getUserMedia.sharingMenuCameraAudioCapture = %S (camera and tab audio)
getUserMedia.sharingMenuCameraAudioCaptureApplication = %S (camera, tab audio and application)
getUserMedia.sharingMenuCameraAudioCaptureScreen = %S (camera, tab audio and screen)
getUserMedia.sharingMenuCameraAudioCaptureWindow = %S (camera, tab audio and window)
getUserMedia.sharingMenuCameraAudioCaptureBrowser = %S (camera, tab audio and tab)
getUserMedia.sharingMenuCameraApplication = %S (camera and application)
getUserMedia.sharingMenuCameraScreen = %S (camera and screen)
getUserMedia.sharingMenuCameraWindow = %S (camera and window)
getUserMedia.sharingMenuCameraBrowser = %S (camera and tab)
getUserMedia.sharingMenuMicrophoneApplication = %S (microphone and application)
getUserMedia.sharingMenuMicrophoneScreen = %S (microphone and screen)
getUserMedia.sharingMenuMicrophoneWindow = %S (microphone and window)
getUserMedia.sharingMenuMicrophoneBrowser = %S (microphone and tab)
getUserMedia.sharingMenuAudioCaptureApplication = %S (tab audio and application)
getUserMedia.sharingMenuAudioCaptureScreen = %S (tab audio and screen)
getUserMedia.sharingMenuAudioCaptureWindow = %S (tab audio and window)
getUserMedia.sharingMenuAudioCaptureBrowser = %S (tab audio and tab)
# LOCALIZATION NOTE(getUserMedia.sharingMenuUnknownHost): this is used for the website
# origin for the sharing menu if no readable origin could be deduced from the URL.
getUserMedia.sharingMenuUnknownHost = Unknown origin

# LOCALIZATION NOTE(emeNotifications.drmContentPlaying.message2): %S is brandShortName.
emeNotifications.drmContentPlaying.message2 = Some audio or video on this site uses DRM software, which may limit what %S can let you do with it.
emeNotifications.drmContentPlaying.button.label = Configure…
emeNotifications.drmContentPlaying.button.accesskey = C

# LOCALIZATION NOTE(emeNotifications.drmContentDisabled.message): NB: inserted via innerHTML, so please don't use <, > or & in this string. %S will be the 'learn more' link
emeNotifications.drmContentDisabled.message = You must enable DRM to play some audio or video on this page. %S
emeNotifications.drmContentDisabled.button.label = Enable DRM
emeNotifications.drmContentDisabled.button.accesskey = E
# LOCALIZATION NOTE(emeNotifications.drmContentDisabled.learnMoreLabel): NB: inserted via innerHTML, so please don't use <, > or & in this string.
emeNotifications.drmContentDisabled.learnMoreLabel = Learn More

# LOCALIZATION NOTE(emeNotifications.drmContentCDMInstalling.message): NB: inserted via innerHTML, so please don't use <, > or & in this string. %S is brandShortName
emeNotifications.drmContentCDMInstalling.message = %S is installing components needed to play the audio or video on this page. Please try again later.

emeNotifications.unknownDRMSoftware = Unknown

# LOCALIZATION NOTE - %S is brandShortName
slowStartup.message = %S seems slow… to… start.
slowStartup.helpButton.label = Learn How to Speed It Up
slowStartup.helpButton.accesskey = L
slowStartup.disableNotificationButton.label = Don’t Tell Me Again
slowStartup.disableNotificationButton.accesskey = A

# LOCALIZATION NOTE  - %S is brandShortName
flashHang.message = %S changed some Adobe Flash settings to improve performance.
flashHang.helpButton.label = Learn More…
flashHang.helpButton.accesskey = L

# LOCALIZATION NOTE (customizeMode.tabTitle): %S is brandShortName
customizeMode.tabTitle = Customize %S

# LOCALIZATION NOTE (e10s.accessibilityNotice.mainMessage,
#                    e10s.accessibilityNotice.enableAndRestart.label,
#                    e10s.accessibilityNotice.enableAndRestart.accesskey):
# These strings are related to the messages we display to offer e10s (Multi-process) to users
# on the pre-release channels. They won't be used in release but they will likely be used in
# beta starting from version 41, so it's still useful to have these strings properly localized.
# %S is brandShortName
e10s.accessibilityNotice.mainMessage2 = Accessibility support is partially disabled due to compatibility issues with new %S features.
e10s.accessibilityNotice.acceptButton.label = OK
e10s.accessibilityNotice.acceptButton.accesskey = O
e10s.accessibilityNotice.enableAndRestart.label = Enable (Requires Restart)
e10s.accessibilityNotice.enableAndRestart.accesskey = E

# LOCALIZATION NOTE (e10s.accessibilityNotice.jawsMessage): %S is brandShortName
e10s.accessibilityNotice.jawsMessage = Display of tab content is disabled due to incompatibility between %S and your accessibility software. Please update your screen reader or switch to Firefox Extended Support Release.

# LOCALIZATION NOTE (userContextPersonal.label,
#                    userContextWork.label,
#                    userContextShopping.label,
#                    userContextBanking.label,
#                    userContextNone.label):
# These strings specify the four predefined contexts included in support of the
# Contextual Identity / Containers project. Each context is meant to represent
# the context that the user is in when interacting with the site. Different
# contexts will store cookies and other information from those sites in
# different, isolated locations. You can enable the feature by typing
# about:config in the URL bar and changing privacy.userContext.enabled to true.
# Once enabled, you can open a new tab in a specific context by clicking
# File > New Container Tab > (1 of 4 contexts). Once opened, you will see these
# strings on the right-hand side of the URL bar.
userContextPersonal.label = Personal
userContextWork.label = Work
userContextBanking.label = Banking
userContextShopping.label = Shopping
userContextNone.label = No Container

userContextPersonal.accesskey = P
userContextWork.accesskey = W
userContextBanking.accesskey = B
userContextShopping.accesskey = S
userContextNone.accesskey = N

userContext.aboutPage.label = Manage containers
userContext.aboutPage.accesskey = O

userContextOpenLink.label = Open Link in New %S Tab

muteTab.label = Mute Tab
muteTab.accesskey = M
unmuteTab.label = Unmute Tab
unmuteTab.accesskey = m
playTab.label = Play Tab
playTab.accesskey = l

# LOCALIZATION NOTE (certErrorDetails*.label): These are text strings that
# appear in the about:certerror page, so that the user can copy and send them to
# the server administrators for troubleshooting.
certErrorDetailsHSTS.label = HTTP Strict Transport Security: %S
certErrorDetailsKeyPinning.label = HTTP Public Key Pinning: %S
certErrorDetailsCertChain.label = Certificate chain:

# LOCALIZATION NOTE (pendingCrashReports2.label): Semi-colon list of plural forms
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
# #1 is the number of pending crash reports
pendingCrashReports2.label = You have an unsent crash report;You have #1 unsent crash reports
pendingCrashReports.viewAll = View
pendingCrashReports.send = Send
pendingCrashReports.alwaysSend = Always Send

decoder.noCodecs.button = Learn how
decoder.noCodecs.accesskey = L
decoder.noCodecs.message = To play video, you may need to install Microsoft’s Media Feature Pack.
decoder.noCodecsLinux.message = To play video, you may need to install the required video codecs.
decoder.noHWAcceleration.message = To improve video quality, you may need to install Microsoft’s Media Feature Pack.
decoder.noPulseAudio.message = To play audio, you may need to install the required PulseAudio software.
decoder.unsupportedLibavcodec.message = libavcodec may be vulnerable or is not supported, and should be updated to play video.

decoder.decodeError.message = An error occurred while decoding a media resource.
decoder.decodeError.button = Report Site Issue
decoder.decodeError.accesskey = R
decoder.decodeWarning.message = A recoverable error occurred while decoding a media resource.

# LOCALIZATION NOTE (captivePortal.infoMessage3):
# Shown in a notification bar when we detect a captive portal is blocking network access
# and requires the user to log in before browsing.
captivePortal.infoMessage3 = You must log in to this network before you can access the Internet.
# LOCALIZATION NOTE (captivePortal.showLoginPage2):
# The label for a button shown in the info bar in all tabs except the login page tab.
# The button shows the portal login page tab when clicked.
captivePortal.showLoginPage2 = Open Network Login Page

permissions.remove.tooltip = Clear this permission and ask again

# LOCALIZATION NOTE (aboutDialog.architecture.*):
# The sixtyFourBit and thirtyTwoBit strings describe the architecture of the
# current Firefox build: 32-bit or 64-bit. These strings are used in parentheses
# between the Firefox version and the "What's new" link in the About dialog,
# e.g.: "48.0.2 (32-bit) <What's new>" or "51.0a1 (2016-09-05) (64-bit)".
aboutDialog.architecture.sixtyFourBit = 64-bit
aboutDialog.architecture.thirtyTwoBit = 32-bit

# LOCALIZATION NOTE (certImminentDistrust.message):
# Shown in the browser console when visiting a website that is trusted today,
# but won't be in the future unless the site operator makes a change.
certImminentDistrust.message = The security certificate in use on this website will no longer be trusted in a future release. For more information, visit https://wiki.mozilla.org/CA/Upcoming_Distrust_Actions

midi.Allow.label = Allow
midi.Allow.accesskey = A
midi.DontAllow.label = Don’t Allow
midi.DontAllow.accesskey = N
midi.remember=Remember this decision
midi.shareWithFile.message = Will you allow this local file to access your MIDI Devices?
# LOCALIZATION NOTE (midi.shareWithFile.message): %S is the name of the site URL (https://...) requesting MIDI access
midi.shareWithSite.message = Will you allow %S to access your MIDI Devices?
midi.shareSysexWithFile.message = Will you allow this local file to access your MIDI devices and send/receive SysEx messages?
# LOCALIZATION NOTE (midi.shareSysexWithFile.message): %S is the name of the site URL (https://...) requesting MIDI access
midi.shareSysexWithSite.message = Will you allow %S to access your MIDI devices and send/receive SysEx messages?

# LOCALIZATION NOTE (panel.back):
# This is used by screen readers to label the "back" button in various browser
# popup panels, including the sliding subviews of the main menu.
panel.back = Back
PK
!<:�ll4chrome/en-US/locale/browser-region/region.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# Default search engine
browser.search.defaultenginename=Google

# Search engine order (order displayed in the search bar dropdown)s
browser.search.order.1=Google
browser.search.order.2=Bing

# This is the default set of web based feed handlers shown in the reader
# selection UI
browser.contentHandlers.types.0.title=My Yahoo!
browser.contentHandlers.types.0.uri=https://add.my.yahoo.com/rss?url=%s

# increment this number when anything gets changed in the list below.  This will
# cause Firefox to re-read these prefs and inject any new handlers into the 
# profile database.  Note that "new" is defined as "has a different URL"; this
# means that it's not possible to update the name of existing handler, so 
# don't make any spelling errors here.
gecko.handlerService.defaultHandlersVersion=4

# The default set of protocol handlers for webcal:
gecko.handlerService.schemes.webcal.0.name=30 Boxes
gecko.handlerService.schemes.webcal.0.uriTemplate=https://30boxes.com/external/widget?refer=ff&url=%s

# The default set of protocol handlers for mailto:
gecko.handlerService.schemes.mailto.0.name=Yahoo! Mail
gecko.handlerService.schemes.mailto.0.uriTemplate=https://compose.mail.yahoo.com/?To=%s
gecko.handlerService.schemes.mailto.1.name=Gmail
gecko.handlerService.schemes.mailto.1.uriTemplate=https://mail.google.com/mail/?extsrc=mailto&url=%s

# The default set of protocol handlers for irc:
gecko.handlerService.schemes.irc.0.name=Mibbit
gecko.handlerService.schemes.irc.0.uriTemplate=https://www.mibbit.com/?url=%s

# The default set of protocol handlers for ircs:
gecko.handlerService.schemes.ircs.0.name=Mibbit
gecko.handlerService.schemes.ircs.0.uriTemplate=https://www.mibbit.com/?url=%s
PK
!<�Ndd;chrome/browser/content/browser/search/searchbarBindings.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");

.searchbar-textbox {
  -moz-binding: url("chrome://browser/content/search/search.xml#searchbar-textbox");
}

.search-one-offs {
  -moz-binding: url("chrome://browser/content/search/search.xml#search-one-offs");
}

.search-setting-button[compact=true],
.search-setting-button-compact:not([compact=true]) {
  display: none;
}
PK
!<�"�pp1chrome/browser/skin/classic/browser/searchbar.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

.searchbar-engine-image {
  width: 16px;
  height: 16px;
  list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.svg");
  -moz-context-properties: fill;
  fill: currentColor;
}

/**
 * The borders of the various elements are specified as follows.
 *
 * The current engine always has a bottom border.
 * The search results never have a border.
 *
 * When the search results are not collapsed:
 * - The elements underneath the search results all have a top border.
 *
 * When the search results are collapsed:
 * - The elements underneath the search results all have a bottom border, except
 *   the lowest one: search-setting-button.
 */

.search-panel-current-engine {
  -moz-box-align: center;
  border-top: none !important;
  border-bottom: 1px solid var(--panel-separator-color);
}

.search-panel-tree[collapsed=true] + .search-one-offs > .search-panel-header,
.search-panel-tree[collapsed=true] + .search-one-offs > .search-panel-one-offs,
.search-panel-tree[collapsed=true] + .search-one-offs > vbox > .addengine-item:first-of-type {
  border-top: none;
}

.search-panel-tree[collapsed=true] + .search-one-offs > .searchbar-engine-one-off-item,
.search-panel-tree[collapsed=true] + .search-one-offs > .search-panel-current-input,
.search-panel-tree[collapsed=true] + .search-one-offs > .search-panel-one-offs,
.search-panel-tree[collapsed=true] + .search-one-offs > vbox > .addengine-item:last-of-type {
  border-bottom: 1px solid var(--panel-separator-color);
}

.search-panel-header {
  font-weight: normal;
  background-color: var(--arrowpanel-dimmed);
  border-top: 1px solid var(--panel-separator-color);
  margin: 0;
  padding: 3px 6px;
  color: GrayText;
}

.search-panel-header > label {
  margin-top: 2px !important;
  margin-bottom: 1px !important;
}

.search-panel-current-input > label {
  margin: 2px 0 !important;
}

.search-panel-input-value {
  color: var(--autocomplete-popup-color);
}

.search-panel-one-offs {
  margin: 0 !important;
  border-top: 1px solid var(--panel-separator-color);
  background-color: var(--arrowpanel-dimmed);
  /* Bug 1108841: prevent font-size from affecting the layout */
  line-height: 0;
}

.searchbar-engine-one-off-item {
  -moz-appearance: none;
  display: inline-block;
  min-width: 48px;
  height: 32px;
  margin: 0;
  padding: 0;
  background: linear-gradient(transparent 15%, var(--panel-separator-color) 15%, var(--panel-separator-color) 85%, transparent 85%);
  background-size: 1px auto;
  background-repeat: no-repeat;
  background-position: right center;
  color: GrayText;
}

.searchbar-engine-one-off-item:-moz-locale-dir(rtl) {
  background-position-x: left;
}

.searchbar-engine-one-off-item:not(.last-row) {
  box-sizing: content-box;
  border-bottom: 1px solid var(--panel-separator-color);
}

.search-setting-button-compact {
  border-bottom: none !important;
}

.search-panel-one-offs:not([compact=true]) > .searchbar-engine-one-off-item.last-of-row,
.search-panel-one-offs[compact=true] > .searchbar-engine-one-off-item.last-of-row:not(.dummy),
.search-panel-one-offs[compact=true] > .searchbar-engine-one-off-item.dummy:not(.last-of-row),
.search-panel-one-offs[compact=true] > .searchbar-engine-one-off-item.last-engine,
.search-setting-button-compact {
  background-image: none;
}

.searchbar-engine-one-off-item:not([selected]):not(.dummy):hover,
.addengine-item:hover {
  background-color: var(--arrowpanel-dimmed-further);
  color: inherit;
}

.searchbar-engine-one-off-item[selected] {
  background-color: Highlight;
  background-image: none;
  color: HighlightText;
}

.searchbar-engine-one-off-item > .button-box {
  padding: 0;
}

.searchbar-engine-one-off-item > .button-box > .button-text {
  display: none;
}

.searchbar-engine-one-off-item > .button-box > .button-icon {
  margin-inline-start: 0;
  margin-inline-end: 0;
  width: 16px;
  height: 16px;
}

.search-add-engines {
  background-color: var(--arrowpanel-dimmed);
}

.addengine-item {
  -moz-appearance: none;
  color: inherit;
  height: 32px;
  margin: 0;
  padding: 0 10px;
}

.addengine-item:first-of-type {
  border-top: 1px solid var(--panel-separator-color);
}

.addengine-item[selected] {
  background-color: Highlight;
  color: HighlightText;
}

.addengine-item[type=menu][selected] {
  color: inherit;
  background-color: var(--arrowpanel-dimmed-further);
}

.addengine-item > .toolbarbutton-badge-stack > .toolbarbutton-icon {
  width: 16px;
  height: 16px;
}

.addengine-item > .toolbarbutton-badge-stack > .toolbarbutton-badge {
  display: -moz-box;
  background: url(chrome://browser/skin/search-indicator-badge-add.svg) no-repeat center;
  box-shadow: none;
  /* "!important" is necessary to override the rule in toolbarbutton.css */
  margin: -4px 0 0 !important;
  margin-inline-end: -4px !important;
  width: 11px;
  height: 11px;
  min-width: 11px;
  min-height: 11px;
}

.addengine-item > .toolbarbutton-text {
  text-align: start;
  padding-inline-start: 10px;
}

.addengine-item:not([image]) {
  list-style-image: url("chrome://browser/skin/search-engine-placeholder.png");
}

@media (min-resolution: 1.1dppx) {
  .addengine-item:not([image]) {
    list-style-image: url("chrome://browser/skin/[email protected]");
  }
}

.addengine-item[type=menu] > .toolbarbutton-menu-dropmarker {
  display: -moz-box;
  -moz-appearance: menuarrow !important;
  list-style-image: none;
}

.search-panel-tree {
  background: transparent;
  color: inherit;
}

.search-panel-tree > .autocomplete-richlistitem {
  padding: 1px;
}

.search-panel-tree > .autocomplete-richlistitem > .ac-type-icon {
  display: -moz-box;
  width: 14px;
  height: 14px;
}

.search-panel-tree > .autocomplete-richlistitem[originaltype="fromhistory"] > .ac-type-icon {
  list-style-image: url("chrome://browser/skin/history.svg");
  -moz-context-properties: fill;
  fill: currentColor;
  fill-opacity: 0.6;
}

.search-panel-tree > .autocomplete-richlistitem[originaltype="fromhistory"][selected] > .ac-type-icon {
  fill-opacity: 1;
}

.search-setting-button {
  -moz-appearance: none;
  margin: 0;
  min-height: 32px;
}

.search-setting-button:hover,
.search-setting-button[selected] {
  background-color: var(--arrowpanel-dimmed-further);
}

.search-setting-button-compact > .button-box > .button-icon {
  list-style-image: url("chrome://browser/skin/settings.svg");
  -moz-context-properties: fill;
  fill: currentColor;
}

menuitem[cmd="cmd_clearhistory"] {
  list-style-image: url("moz-icon://stock/gtk-clear?size=menu");
}

menuitem[cmd="cmd_clearhistory"][disabled] {
  list-style-image: url("moz-icon://stock/gtk-clear?size=menu&state=disabled");
}
PK
!<޶]Ichrome/en-US/locale/browser/customizableui/customizableWidgets.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

history-panelmenu.label = History
# LOCALIZATION NOTE(history-panelmenu.tooltiptext2): %S is the keyboard shortcut
history-panelmenu.tooltiptext2 = Show your history (%S)

remotetabs-panelmenu.label = Synced Tabs
remotetabs-panelmenu.tooltiptext2 = Show tabs from other devices

privatebrowsing-button.label = New Private Window
# LOCALIZATION NOTE(privatebrowsing-button.tooltiptext): %S is the keyboard shortcut
privatebrowsing-button.tooltiptext = Open a new Private Browsing window (%S)

save-page-button.label = Save Page
# LOCALIZATION NOTE(save-page-button.tooltiptext3): %S is the keyboard shortcut
save-page-button.tooltiptext3 = Save this page (%S)

find-button.label = Find
# LOCALIZATION NOTE(find-button.tooltiptext3): %S is the keyboard shortcut.
find-button.tooltiptext3 = Find in this page (%S)

open-file-button.label = Open File
# LOCALIZATION NOTE (open-file-button.tooltiptext3): %S is the keyboard shortcut.
open-file-button.tooltiptext3 = Open a file (%S)

developer-button.label = Developer
# LOCALIZATION NOTE(developer-button.tooltiptext): %S is the keyboard shortcut
developer-button.tooltiptext2 = Open Web developer tools (%S)

sidebar-button.label = Sidebars
sidebar-button.tooltiptext2 = Show sidebars

add-ons-button.label = Add-ons
# LOCALIZATION NOTE(add-ons-button.tooltiptext3): %S is the keyboard shortcut
add-ons-button.tooltiptext3 = Manage your add-ons (%S)

preferences-button.label = Preferences
preferences-button.tooltiptext2 = Open preferences
preferences-button.tooltiptext.withshortcut = Open preferences (%S)
# LOCALIZATION NOTE (preferences-button.labelWin): Windows-only label for Options
preferences-button.labelWin = Options
# LOCALIZATION NOTE (preferences-button.tooltipWin): Windows-only tooltip for Options
preferences-button.tooltipWin2 = Open options

zoom-controls.label = Zoom Controls
zoom-controls.tooltiptext2 = Zoom controls

zoom-out-button.label = Zoom out
# LOCALIZATION NOTE(zoom-out-button.tooltiptext2): %S is the keyboard shortcut.
zoom-out-button.tooltiptext2 = Zoom out (%S)

# LOCALIZATION NOTE(zoom-reset-button.tooltiptext2): %S is the keyboard shortcut.
zoom-reset-button.tooltiptext2 = Reset zoom level (%S)

zoom-in-button.label = Zoom in
# LOCALIZATION NOTE(zoom-in-button.tooltiptext2): %S is the keyboard shortcut.
zoom-in-button.tooltiptext2 = Zoom in (%S)

edit-controls.label = Edit Controls
edit-controls.tooltiptext2 = Edit controls

cut-button.label = Cut
# LOCALIZATION NOTE(cut-button.tooltiptext2): %S is the keyboard shortcut.
cut-button.tooltiptext2 = Cut (%S)

copy-button.label = Copy
# LOCALIZATION NOTE(copy-button.tooltiptext2): %S is the keyboard shortcut.
copy-button.tooltiptext2 = Copy (%S)

paste-button.label = Paste
# LOCALIZATION NOTE(paste-button.tooltiptext2): %S is the keyboard shortcut.
paste-button.tooltiptext2 = Paste (%S)

feed-button.label = Subscribe
feed-button.tooltiptext2 = Subscribe to this page

# LOCALIZATION NOTE (characterencoding-button2.label): The \u00ad text at the beginning
# of the string is used to disable auto hyphenation on the button text when it is displayed
# in the menu panel.
characterencoding-button2.label = \u00adText Encoding
characterencoding-button2.tooltiptext = Show text encoding options

email-link-button.label = Email Link
email-link-button.tooltiptext3 = Email a link to this page

# LOCALIZATION NOTE(quit-button.tooltiptext.linux2): %1$S is the brand name (e.g. Firefox),
# %2$S is the keyboard shortcut
quit-button.tooltiptext.linux2 = Quit %1$S (%2$S)
# LOCALIZATION NOTE(quit-button.tooltiptext.mac): %1$S is the brand name (e.g. Firefox),
# %2$S is the keyboard shortcut
quit-button.tooltiptext.mac = Quit %1$S (%2$S)

panic-button.label = Forget
panic-button.tooltiptext = Forget about some browsing history

# LOCALIZATION NOTE(devtools-webide-button.label, devtools-webide-button.tooltiptext):
# widget is only visible after WebIDE has been started once (Tools > Web Developers > WebIDE)
# %S is the keyboard shortcut
devtools-webide-button2.label = WebIDE
devtools-webide-button2.tooltiptext = Open WebIDE (%S)

toolbarspring.label = Flexible Space
toolbarseparator.label = Separator
toolbarspacer.label = Space
PK
!<�E��=	=	1chrome/en-US/locale/browser/tabbrowser.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.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.restoreLastTabs=Restore Tabs From Last Time
tabs.emptyTabTitle=New Tab
tabs.closeTab=Close Tab
tabs.close=Close
tabs.closeWarningTitle=Confirm close
# LOCALIZATION NOTE (tabs.closeWarningMultiple):
# Semicolon-separated list of plural forms. See:
# http://developer.mozilla.org/en/docs/Localization_and_Plurals
# The singular form is not considered since this string is used only for
# multiple tabs.
tabs.closeWarningMultiple=;You are about to close #1 tabs. Are you sure you want to continue?
tabs.closeButtonMultiple=Close tabs
tabs.closeWarningPromptMe=Warn me when I attempt to close multiple tabs

tabs.closeTab.tooltip=Close tab
# LOCALIZATION NOTE (tabs.closeSelectedTab.tooltip):
# %S is the keyboard shortcut for closing the current tab
tabs.closeSelectedTab.tooltip=Close tab (%S)
# LOCALIZATION NOTE (tabs.muteAudio.tooltip):
# %S is the keyboard shortcut for "Mute tab"
tabs.muteAudio.tooltip=Mute tab (%S)
# LOCALIZATION NOTE (tabs.unmuteAudio.tooltip):
# %S is the keyboard shortcut for "Unmute tab"
tabs.unmuteAudio.tooltip=Unmute tab (%S)
tabs.muteAudio.background.tooltip=Mute tab
tabs.unmuteAudio.background.tooltip=Unmute tab

tabs.unblockAudio.tooltip=Play tab

# LOCALIZATION NOTE (tabs.allowTabFocusByPromptForSite):
# %S is the hostname of the site where dialogs are allowed to switch tabs
tabs.allowTabFocusByPromptForSite=Allow dialogs from %S to take you to their tab

# LOCALIZATION NOTE (tabs.containers.tooltip):
# Displayed as a tooltip on container tabs
# %1$S is the title of the current tab
# %2$S is the name of the current container
tabs.containers.tooltip=%1$S - %2$S

# LOCALIZATION NOTE (tabs.openWarningTitle, tabs.openWarningMultipleBranded, tabs.openButtonMultiple, tabs.openWarningPromptMeBranded):
# These items are moved from chrome/browser/places/places.properties
# Now they are not specific to bookmark.
tabs.openWarningTitle=Confirm open
tabs.openWarningMultipleBranded=You are about to open %S tabs.  This might slow down %S while the pages are loading.  Are you sure you want to continue?
tabs.openButtonMultiple=Open tabs
tabs.openWarningPromptMeBranded=Warn me when opening multiple tabs might slow down %S
PK
!<S���5chrome/en-US/locale/branding/browserconfig.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# Do NOT localize or otherwise change these values
browser.startup.homepage=about:home
PK
!<��S�k	k	@chrome/en-US/locale/en-US/devtools/shim/key-shortcuts.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE (toggleToolbox.commandkey):
# Key pressed to open a toolbox with the default panel selected
toggleToolbox.commandkey=I

# LOCALIZATION NOTE (toggleToolboxF12.commandkey):
# Alternative key pressed to open a toolbox with the default panel selected
toggleToolboxF12.commandkey=VK_F12

# LOCALIZATION NOTE (toggleToolbar.commandkey):
# Key pressed to open the Developer Toolbar (a.k.a gcli)
toggleToolbar.commandkey=VK_F2

# LOCALIZATION NOTE (webide.commandkey):
# Key pressed to open the Web IDE window
webide.commandkey=VK_F8

# LOCALIZATION NOTE (browserToolbox.commandkey):
# Key pressed to open the Browser Toolbox, used for debugging Firefox itself
browserToolbox.commandkey=I

# LOCALIZATION NOTE (browserConsole.commandkey):
# Key pressed to open the Browser Console, used for debugging Firefox itself
browserConsole.commandkey=J

# LOCALIZATION NOTE (responsiveDesignMode.commandkey):
# Key pressed to toggle on the Responsive Design Mode
responsiveDesignMode.commandkey=M

# LOCALIZATION NOTE (scratchpad.commandkey):
# Key pressed to open the Scratchpad in its own window
scratchpad.commandkey=VK_F4

# LOCALIZATION NOTE (inspector.commandkey):
# Key pressed to open a toolbox with the inspector panel selected
inspector.commandkey=C

# LOCALIZATION NOTE (webconsole.commandkey):
# Key pressed to open a toolbox with the web console panel selected
webconsole.commandkey=K

# LOCALIZATION NOTE (debugger.commandkey):
# Key pressed to open a toolbox with the debugger panel selected
debugger.commandkey=S

# LOCALIZATION NOTE (netmonitor.commandkey):
# Key pressed to open a toolbox with the network monitor panel selected
netmonitor.commandkey=E

# LOCALIZATION NOTE (styleeditor.commandkey):
# Key pressed to open a toolbox with the style editor panel selected
styleeditor.commandkey=VK_F7

# LOCALIZATION NOTE (performance.commandkey):
# Key pressed to open a toolbox with the performance panel selected
performance.commandkey=VK_F5

# LOCALIZATION NOTE (storage.commandkey):
# Key pressed to open a toolbox with the storage panel selected
storage.commandkey=VK_F9

# LOCALIZATION NOTE (dom.commandkey):
# Key pressed to open a toolbox with the DOM panel selected
dom.commandkey=W
PK
!<iMu��:chrome/en-US/locale/en-US/devtools/shim/startup.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE (enableDevTools.label):
# Label for the menu item displayed in Tools > Developer Tools when DevTools are disabled.
enableDevTools.label=Enable Developer Tools…
enableDevTools.accesskey=E
PK
!<�S��((modules/ContentObservers.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
 * This process script is for small observers that we want to register
 * once per content process, usually in order to forward content-based
 * observer service notifications to the chrome process through
 * message passing. Using a process script avoids having them in
 * content.js and thereby registering N observers for N open tabs,
 * which is bad for perf.
 */

"use strict";

ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");

ChromeUtils.defineModuleGetter(this, "ContentWebRTC",
  "resource:///modules/ContentWebRTC.jsm");

var gEMEUIObserver = function(subject, topic, data) {
  let win = subject.top;
  let mm = getMessageManagerForWindow(win);
  if (mm) {
    mm.sendAsyncMessage("EMEVideo:ContentMediaKeysRequest", data);
  }
};

var gDecoderDoctorObserver = function(subject, topic, data) {
  let win = subject.top;
  let mm = getMessageManagerForWindow(win);
  if (mm) {
    mm.sendAsyncMessage("DecoderDoctor:Notification", data);
  }
};

function getMessageManagerForWindow(aContentWindow) {
  let ir = aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsIDocShell)
                         .sameTypeRootTreeItem
                         .QueryInterface(Ci.nsIInterfaceRequestor);
  try {
    // If e10s is disabled, this throws NS_NOINTERFACE for closed tabs.
    return ir.getInterface(Ci.nsIContentFrameMessageManager);
  } catch (e) {
    if (e.result == Cr.NS_NOINTERFACE) {
      return null;
    }
    throw e;
  }
}

Services.obs.addObserver(gEMEUIObserver, "mediakeys-request");
Services.obs.addObserver(gDecoderDoctorObserver, "decoder-doctor-notification");


// ContentWebRTC observer registration.
const kWebRTCObserverTopics = ["getUserMedia:request",
                               "recording-device-stopped",
                               "PeerConnection:request",
                               "recording-device-events",
                               "recording-window-ended"];

function webRTCObserve(aSubject, aTopic, aData) {
  ContentWebRTC.observe(aSubject, aTopic, aData);
}

for (let topic of kWebRTCObserverTopics) {
  Services.obs.addObserver(webRTCObserve, topic);
}

if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT)
  Services.obs.addObserver(processShutdown, "content-child-shutdown");

function processShutdown() {
  for (let topic of kWebRTCObserverTopics) {
    Services.obs.removeObserver(webRTCObserve, topic);
  }
  Services.obs.removeObserver(processShutdown, "content-child-shutdown");
}
PK
!<��碉��!components/WebContentConverter.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
  "resource://gre/modules/PrivateBrowsingUtils.jsm");

function LOG(str) {
  // dump("*** " + str + "\n");
}

const WCCR_CONTRACTID = "@mozilla.org/embeddor.implemented/web-content-handler-registrar;1";
const WCCR_CLASSID = Components.ID("{792a7e82-06a0-437c-af63-b2d12e808acc}");

const WCC_CLASSID = Components.ID("{db7ebf28-cc40-415f-8a51-1b111851df1e}");
const WCC_CLASSNAME = "Web Service Handler";

const TYPE_MAYBE_FEED = "application/vnd.mozilla.maybe.feed";
const TYPE_ANY = "*/*";

const PREF_CONTENTHANDLERS_AUTO = "browser.contentHandlers.auto.";
const PREF_CONTENTHANDLERS_BRANCH = "browser.contentHandlers.types.";
const PREF_SELECTED_WEB = "browser.feeds.handlers.webservice";
const PREF_SELECTED_ACTION = "browser.feeds.handler";
const PREF_SELECTED_READER = "browser.feeds.handler.default";
const PREF_HANDLER_EXTERNAL_PREFIX = "network.protocol-handler.external";

const STRING_BUNDLE_URI = "chrome://browser/locale/feeds/subscribe.properties";

const NS_ERROR_MODULE_DOM = 2152923136;
const NS_ERROR_DOM_SYNTAX_ERR = NS_ERROR_MODULE_DOM + 12;

function WebContentConverter() {
}
WebContentConverter.prototype = {
  convert() { },
  asyncConvertData() { },
  onDataAvailable() { },
  onStopRequest() { },

  onStartRequest(request, context) {
    let wccr =
        Cc[WCCR_CONTRACTID].
        getService(Ci.nsIWebContentConverterService);
    wccr.loadPreferredHandler(request);
  },

  QueryInterface(iid) {
    if (iid.equals(Ci.nsIStreamConverter) ||
        iid.equals(Ci.nsIStreamListener) ||
        iid.equals(Ci.nsISupports))
      return this;
    throw Cr.NS_ERROR_NO_INTERFACE;
  }
};

let WebContentConverterFactory = {
  createInstance(outer, iid) {
    if (outer != null)
      throw Cr.NS_ERROR_NO_AGGREGATION;
    return new WebContentConverter().QueryInterface(iid);
  },

  QueryInterface(iid) {
    if (iid.equals(Ci.nsIFactory) ||
        iid.equals(Ci.nsISupports))
      return this;
    throw Cr.NS_ERROR_NO_INTERFACE;
  }
};

function ServiceInfo(contentType, uri, name) {
  this._contentType = contentType;
  this._uri = uri;
  this._name = name;
}
ServiceInfo.prototype = {
  /**
   * See nsIHandlerApp
   */
  get name() {
    return this._name;
  },

  /**
   * See nsIHandlerApp
   */
  equals(aHandlerApp) {
    if (!aHandlerApp)
      throw Cr.NS_ERROR_NULL_POINTER;

    if (aHandlerApp instanceof Ci.nsIWebContentHandlerInfo &&
        aHandlerApp.contentType == this.contentType &&
        aHandlerApp.uri == this.uri)
      return true;

    return false;
  },

  /**
   * See nsIWebContentHandlerInfo
   */
  get contentType() {
    return this._contentType;
  },

  /**
   * See nsIWebContentHandlerInfo
   */
  get uri() {
    return this._uri;
  },

  /**
   * See nsIWebContentHandlerInfo
   */
  getHandlerURI(uri) {
    return this._uri.replace(/%s/gi, encodeURIComponent(uri));
  },

  QueryInterface(iid) {
    if (iid.equals(Ci.nsIWebContentHandlerInfo) ||
        iid.equals(Ci.nsISupports))
      return this;
    throw Cr.NS_ERROR_NO_INTERFACE;
  }
};

const Utils = {
  makeURI(aURL, aOriginCharset, aBaseURI) {
    return Services.io.newURI(aURL, aOriginCharset, aBaseURI);
  },

  checkAndGetURI(aURIString, aContentWindow) {
    let uri;
    try {
      let baseURI = aContentWindow.document.baseURIObject;
      uri = this.makeURI(aURIString, null, baseURI);
    } catch (ex) {
      throw NS_ERROR_DOM_SYNTAX_ERR;
    }

    // For security reasons we reject non-http(s) urls (see bug 354316),
    // we may need to revise this once we support more content types
    if (uri.scheme != "http" && uri.scheme != "https") {
      throw this.getSecurityError(
        "Permission denied to add " + uri.spec + " as a content or protocol handler",
        aContentWindow);
    }

    // We also reject handlers registered from a different host (see bug 402287)
    if (!["http:", "https:"].includes(aContentWindow.location.protocol) ||
        aContentWindow.location.hostname != uri.host) {
      throw this.getSecurityError(
        "Permission denied to add " + uri.spec + " as a content or protocol handler",
        aContentWindow);
    }

    // If the uri doesn't contain '%s', it won't be a good handler
    if (!uri.spec.includes("%s"))
      throw NS_ERROR_DOM_SYNTAX_ERR;

    return uri;
  },

  // NB: Throws if aProtocol is not allowed.
  checkProtocolHandlerAllowed(aProtocol, aURIString, aWindowOrNull) {
    // First, check to make sure this isn't already handled internally (we don't
    // want to let them take over, say "chrome").
    let handler = Services.io.getProtocolHandler(aProtocol);
    if (!(handler instanceof Ci.nsIExternalProtocolHandler)) {
      // This is handled internally, so we don't want them to register
      throw this.getSecurityError(
        `Permission denied to add ${aURIString} as a protocol handler`,
        aWindowOrNull);
    }

    // check if it is in the black list
    let pb = Services.prefs;
    let allowed =
      pb.getBoolPref(PREF_HANDLER_EXTERNAL_PREFIX + "." + aProtocol,
                     pb.getBoolPref(PREF_HANDLER_EXTERNAL_PREFIX + "-default"));
    if (!allowed) {
      throw this.getSecurityError(
        `Not allowed to register a protocol handler for ${aProtocol}`,
        aWindowOrNull);
    }
  },

  // Return a SecurityError exception from the given Window if one is given.  If
  // none is given, just return the given error string, for lack of anything
  // better.
  getSecurityError(errorString, aWindowOrNull) {
    if (!aWindowOrNull) {
      return errorString;
    }

    return new aWindowOrNull.DOMException(errorString, "SecurityError");
  },

  /**
   * Mappings from known feed types to our internal content type.
   */
  _mappings: {
    "application/rss+xml": TYPE_MAYBE_FEED,
    "application/atom+xml": TYPE_MAYBE_FEED,
  },

  resolveContentType(aContentType) {
    if (aContentType in this._mappings)
      return this._mappings[aContentType];
    return aContentType;
  }
};

function WebContentConverterRegistrar() {
  this._contentTypes = {};
  this._autoHandleContentTypes = {};
}

WebContentConverterRegistrar.prototype = {
  get stringBundle() {
    let sb = Services.strings.createBundle(STRING_BUNDLE_URI);
    delete WebContentConverterRegistrar.prototype.stringBundle;
    return WebContentConverterRegistrar.prototype.stringBundle = sb;
  },

  _getFormattedString(key, params) {
    return this.stringBundle.formatStringFromName(key, params, params.length);
  },

  _getString(key) {
    return this.stringBundle.GetStringFromName(key);
  },

  /**
   * See nsIWebContentConverterService
   */
  getAutoHandler(contentType) {
    contentType = Utils.resolveContentType(contentType);
    if (contentType in this._autoHandleContentTypes)
      return this._autoHandleContentTypes[contentType];
    return null;
  },

  /**
   * See nsIWebContentConverterService
   */
  setAutoHandler(contentType, handler) {
    if (handler && !this._typeIsRegistered(contentType, handler.uri))
      throw Cr.NS_ERROR_NOT_AVAILABLE;

    contentType = Utils.resolveContentType(contentType);
    this._setAutoHandler(contentType, handler);

    let ps = Services.prefs;
    let autoBranch = ps.getBranch(PREF_CONTENTHANDLERS_AUTO);
    if (handler)
      autoBranch.setCharPref(contentType, handler.uri);
    else if (autoBranch.prefHasUserValue(contentType))
      autoBranch.clearUserPref(contentType);

    ps.savePrefFile(null);
  },

  /**
   * Update the internal data structure (not persistent)
   */
  _setAutoHandler(contentType, handler) {
    if (handler)
      this._autoHandleContentTypes[contentType] = handler;
    else if (contentType in this._autoHandleContentTypes)
      delete this._autoHandleContentTypes[contentType];
  },

  /**
   * See nsIWebContentConverterService
   */
  getWebContentHandlerByURI(contentType, uri) {
    return this.getContentHandlers(contentType)
               .find(e => e.uri == uri) || null;
  },

  /**
   * See nsIWebContentConverterService
   */
  loadPreferredHandler(request) {
    let channel = request.QueryInterface(Ci.nsIChannel);
    let contentType = Utils.resolveContentType(channel.contentType);
    let handler = this.getAutoHandler(contentType);
    if (handler) {
      request.cancel(Cr.NS_ERROR_FAILURE);

      let triggeringPrincipal = channel.loadInfo
        ? channel.loadInfo.triggeringPrincipal
        : Services.scriptSecurityManager.getSystemPrincipal();

      let webNavigation =
          channel.notificationCallbacks.getInterface(Ci.nsIWebNavigation);
      webNavigation.loadURI(handler.getHandlerURI(channel.URI.spec),
                            Ci.nsIWebNavigation.LOAD_FLAGS_NONE,
                            null, null, null, triggeringPrincipal);
    }
  },

  /**
   * See nsIWebContentConverterService
   */
  removeProtocolHandler(aProtocol, aURITemplate) {
    let eps = Cc["@mozilla.org/uriloader/external-protocol-service;1"].
              getService(Ci.nsIExternalProtocolService);
    let handlerInfo = eps.getProtocolHandlerInfo(aProtocol);
    let handlers =  handlerInfo.possibleApplicationHandlers;
    for (let i = 0; i < handlers.length; i++) {
      try { // We only want to test web handlers
        let handler = handlers.queryElementAt(i, Ci.nsIWebHandlerApp);
        if (handler.uriTemplate == aURITemplate) {
          handlers.removeElementAt(i);
          let hs = Cc["@mozilla.org/uriloader/handler-service;1"].
                   getService(Ci.nsIHandlerService);
          hs.store(handlerInfo);
          return;
        }
      } catch (e) { /* it wasn't a web handler */ }
    }
  },

  /**
   * See nsIWebContentConverterService
   */
  removeContentHandler(contentType, uri) {
    function notURI(serviceInfo) {
      return serviceInfo.uri != uri;
    }

    if (contentType in this._contentTypes) {
      this._contentTypes[contentType] =
        this._contentTypes[contentType].filter(notURI);
    }
  },

  /**
   * These are types for which there is a separate content converter aside
   * from our built in generic one. We should not automatically register
   * a factory for creating a converter for these types.
   */
  _blockedTypes: {
    "application/vnd.mozilla.maybe.feed": true,
  },

  /**
   * Determines if a web handler is already registered.
   *
   * @param aProtocol
   *        The scheme of the web handler we are checking for.
   * @param aURITemplate
   *        The URI template that the handler uses to handle the protocol.
   * @return true if it is already registered, false otherwise.
   */
  _protocolHandlerRegistered(aProtocol, aURITemplate) {
    let eps = Cc["@mozilla.org/uriloader/external-protocol-service;1"].
              getService(Ci.nsIExternalProtocolService);
    let handlerInfo = eps.getProtocolHandlerInfo(aProtocol);
    let handlers =  handlerInfo.possibleApplicationHandlers;
    for (let i = 0; i < handlers.length; i++) {
      try { // We only want to test web handlers
        let handler = handlers.queryElementAt(i, Ci.nsIWebHandlerApp);
        if (handler.uriTemplate == aURITemplate)
          return true;
      } catch (e) { /* it wasn't a web handler */ }
    }
    return false;
  },

  /**
   * See nsIWebContentHandlerRegistrar
   */
  registerProtocolHandler(aProtocol, aURIString, aTitle, aBrowserOrWindow) {
    LOG("registerProtocolHandler(" + aProtocol + "," + aURIString + "," + aTitle + ")");
    let haveWindow = (aBrowserOrWindow instanceof Ci.nsIDOMWindow);
    let uri;
    if (haveWindow) {
      uri = Utils.checkAndGetURI(aURIString, aBrowserOrWindow);
    } else {
      // aURIString must not be a relative URI.
      uri = Utils.makeURI(aURIString, null);
    }

    // If the protocol handler is already registered, just return early.
    if (this._protocolHandlerRegistered(aProtocol, uri.spec)) {
      return;
    }

    let browser;
    if (haveWindow) {
      let browserWindow =
        this._getBrowserWindowForContentWindow(aBrowserOrWindow);
      browser = this._getBrowserForContentWindow(browserWindow,
                                                 aBrowserOrWindow);
    } else {
      browser = aBrowserOrWindow;
    }
    if (PrivateBrowsingUtils.isBrowserPrivate(browser)) {
      // Inside the private browsing mode, we don't want to alert the user to save
      // a protocol handler.  We log it to the error console so that web developers
      // would have some way to tell what's going wrong.
      Services.console.
      logStringMessage("Web page denied access to register a protocol handler inside private browsing mode");
      return;
    }

    Utils.checkProtocolHandlerAllowed(aProtocol, aURIString,
                                      haveWindow ? aBrowserOrWindow : null);

    // Now Ask the user and provide the proper callback
    let message = this._getFormattedString("addProtocolHandler",
                                           [aTitle, uri.host, aProtocol]);

    let notificationIcon = uri.prePath + "/favicon.ico";
    let notificationValue = "Protocol Registration: " + aProtocol;
    let addButton = {
      label: this._getString("addProtocolHandlerAddButton"),
      accessKey: this._getString("addProtocolHandlerAddButtonAccesskey"),
      protocolInfo: { protocol: aProtocol, uri: uri.spec, name: aTitle },

      callback(aNotification, aButtonInfo) {
          let protocol = aButtonInfo.protocolInfo.protocol;
          let name     = aButtonInfo.protocolInfo.name;

          let handler = Cc["@mozilla.org/uriloader/web-handler-app;1"].
                        createInstance(Ci.nsIWebHandlerApp);
          handler.name = name;
          handler.uriTemplate = aButtonInfo.protocolInfo.uri;

          let eps = Cc["@mozilla.org/uriloader/external-protocol-service;1"].
                    getService(Ci.nsIExternalProtocolService);
          let handlerInfo = eps.getProtocolHandlerInfo(protocol);
          handlerInfo.possibleApplicationHandlers.appendElement(handler);

          // Since the user has agreed to add a new handler, chances are good
          // that the next time they see a handler of this type, they're going
          // to want to use it.  Reset the handlerInfo to ask before the next
          // use.
          handlerInfo.alwaysAskBeforeHandling = true;

          let hs = Cc["@mozilla.org/uriloader/handler-service;1"].
                   getService(Ci.nsIHandlerService);
          hs.store(handlerInfo);
        }
    };
    let notificationBox = browser.getTabBrowser().getNotificationBox(browser);
    notificationBox.appendNotification(message,
                                       notificationValue,
                                       notificationIcon,
                                       notificationBox.PRIORITY_INFO_LOW,
                                       [addButton]);
  },

  /**
   * See nsIWebContentHandlerRegistrar
   * If a DOM window is provided, then the request came from content, so we
   * prompt the user to confirm the registration.
   */
  registerContentHandler(aContentType, aURIString, aTitle, aWindowOrBrowser) {
    LOG("registerContentHandler(" + aContentType + "," + aURIString + "," + aTitle + ")");

    // Make sure to do our URL checks up front, before our content type check,
    // just like the WebContentConverterRegistrarContent does.
    let haveWindow = aWindowOrBrowser &&
                     (aWindowOrBrowser instanceof Ci.nsIDOMWindow);
    let uri;
    if (haveWindow) {
      uri = Utils.checkAndGetURI(aURIString, aWindowOrBrowser);
    } else if (aWindowOrBrowser) {
      // uri was vetted in the content process.
      uri = Utils.makeURI(aURIString, null);
    }

    // We only support feed types at present.
    let contentType = Utils.resolveContentType(aContentType);
    // XXX We should be throwing a Utils.getSecurityError() here in at least
    // some cases.  See bug 1266492.
    if (contentType != TYPE_MAYBE_FEED) {
      return;
    }

    if (aWindowOrBrowser) {
      let notificationBox;
      if (haveWindow) {
        let browserWindow = this._getBrowserWindowForContentWindow(aWindowOrBrowser);
        let browserElement = this._getBrowserForContentWindow(browserWindow, aWindowOrBrowser);
        notificationBox = browserElement.getTabBrowser().getNotificationBox(browserElement);
      } else {
        notificationBox = aWindowOrBrowser.getTabBrowser()
                                          .getNotificationBox(aWindowOrBrowser);
      }

      this._appendFeedReaderNotification(uri, aTitle, notificationBox);
    } else {
      this._registerContentHandler(contentType, aURIString, aTitle);
    }
  },

  /**
   * Returns the browser chrome window in which the content window is in
   */
  _getBrowserWindowForContentWindow(aContentWindow) {
    return aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsIWebNavigation)
                         .QueryInterface(Ci.nsIDocShellTreeItem)
                         .rootTreeItem
                         .QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsIDOMWindow)
                         .wrappedJSObject;
  },

  /**
   * Returns the <xul:browser> element associated with the given content
   * window.
   *
   * @param aBrowserWindow
   *        The browser window in which the content window is in.
   * @param aContentWindow
   *        The content window. It's possible to pass a child content window
   *        (i.e. the content window of a frame/iframe).
   */
  _getBrowserForContentWindow(aBrowserWindow, aContentWindow) {
    // This depends on pseudo APIs of browser.js and tabbrowser.xml
    aContentWindow = aContentWindow.top;
    return aBrowserWindow.gBrowser.browsers.find((browser) =>
      browser.contentWindow == aContentWindow);
  },

  /**
   * Appends a notifcation for the given feed reader details.
   *
   * The notification could be either a pseudo-dialog which lets
   * the user to add the feed reader:
   * [ [icon] Add %feed-reader-name% (%feed-reader-host%) as a Feed Reader?  (Add) [x] ]
   *
   * or a simple message for the case where the feed reader is already registered:
   * [ [icon] %feed-reader-name% is already registered as a Feed Reader             [x] ]
   *
   * A new notification isn't appended if the given notificationbox has a
   * notification for the same feed reader.
   *
   * @param aURI
   *        The url of the feed reader as a nsIURI object
   * @param aName
   *        The feed reader name as it was passed to registerContentHandler
   * @param aNotificationBox
   *        The notification box to which a notification might be appended
   * @return true if a notification has been appended, false otherwise.
   */
  _appendFeedReaderNotification(aURI, aName, aNotificationBox) {
    let uriSpec = aURI.spec;
    let notificationValue = "feed reader notification: " + uriSpec;
    let notificationIcon = aURI.prePath + "/favicon.ico";

    // Don't append a new notification if the notificationbox
    // has a notification for the given feed reader already
    if (aNotificationBox.getNotificationWithValue(notificationValue))
      return false;

    let buttons;
    let message;
    if (this.getWebContentHandlerByURI(TYPE_MAYBE_FEED, uriSpec))
      message = this._getFormattedString("handlerRegistered", [aName]);
    else {
      message = this._getFormattedString("addHandler", [aName, aURI.host]);
      let self = this;
      let addButton = {
        _outer: self,
        label: self._getString("addHandlerAddButton"),
        accessKey: self._getString("addHandlerAddButtonAccesskey"),
        feedReaderInfo: { uri: uriSpec, name: aName },

        /* static */
        callback(aNotification, aButtonInfo) {
          let uri = aButtonInfo.feedReaderInfo.uri;
          let name = aButtonInfo.feedReaderInfo.name;
          let outer = aButtonInfo._outer;

          // The reader could have been added from another window mean while
          if (!outer.getWebContentHandlerByURI(TYPE_MAYBE_FEED, uri))
            outer._registerContentHandler(TYPE_MAYBE_FEED, uri, name);

          // avoid reference cycles
          aButtonInfo._outer = null;

          return false;
        }
      };
      buttons = [addButton];
    }

    aNotificationBox.appendNotification(message,
                                        notificationValue,
                                        notificationIcon,
                                        aNotificationBox.PRIORITY_INFO_LOW,
                                        buttons);
    return true;
  },

  /**
   * Save Web Content Handler metadata to persistent preferences.
   * @param   contentType
   *          The content Type being handled
   * @param   uri
   *          The uri of the web service
   * @param   title
   *          The human readable name of the web service
   *
   * This data is stored under:
   *
   *    browser.contentHandlers.type0 = content/type
   *    browser.contentHandlers.uri0 = http://www.foo.com/q=%s
   *    browser.contentHandlers.title0 = Foo 2.0alphr
   */
  _saveContentHandlerToPrefs(contentType, uri, title) {
    let ps = Services.prefs;
    let i = 0;
    let typeBranch = null;
    while (true) {
      typeBranch =
        ps.getBranch(PREF_CONTENTHANDLERS_BRANCH + i + ".");
      try {
        typeBranch.getCharPref("type");
        ++i;
      } catch (e) {
        // No more handlers
        break;
      }
    }
    if (typeBranch) {
      typeBranch.setCharPref("type", contentType);
      let pls =
          Cc["@mozilla.org/pref-localizedstring;1"].
          createInstance(Ci.nsIPrefLocalizedString);
      pls.data = uri;
      typeBranch.setComplexValue("uri", Ci.nsIPrefLocalizedString, pls);
      pls.data = title;
      typeBranch.setComplexValue("title", Ci.nsIPrefLocalizedString, pls);

      ps.savePrefFile(null);
    }
  },

  /**
   * Determines if there is a type with a particular uri registered for the
   * specified content type already.
   * @param   contentType
   *          The content type that the uri handles
   * @param   uri
   *          The uri of the content type
   */
  _typeIsRegistered(contentType, uri) {
    if (!(contentType in this._contentTypes))
      return false;

    return this._contentTypes[contentType]
               .some(t => t.uri == uri);
  },

  /**
   * Gets a stream converter contract id for the specified content type.
   * @param   contentType
   *          The source content type for the conversion.
   * @returns A contract id to construct a converter to convert between the
   *          contentType and *\/*.
   */
  _getConverterContractID(contentType) {
    const template = "@mozilla.org/streamconv;1?from=%s&to=*/*";
    return template.replace(/%s/, contentType);
  },

  /**
   * Register a web service handler for a content type.
   *
   * @param   contentType
   *          the content type being handled
   * @param   uri
   *          the URI of the web service
   * @param   title
   *          the human readable name of the web service
   */
  _registerContentHandler(contentType, uri, title) {
    this._updateContentTypeHandlerMap(contentType, uri, title);
    this._saveContentHandlerToPrefs(contentType, uri, title);

    if (contentType == TYPE_MAYBE_FEED) {
      // Make the new handler the last-selected reader in the preview page
      // and make sure the preview page is shown the next time a feed is visited
      let pb = Services.prefs.getBranch(null);
      pb.setCharPref(PREF_SELECTED_READER, "web");

      pb.setStringPref(PREF_SELECTED_WEB, uri);
      pb.setCharPref(PREF_SELECTED_ACTION, "ask");
      this._setAutoHandler(TYPE_MAYBE_FEED, null);
    }
  },

  /**
   * Update the content type -> handler map. This mapping is not persisted, use
   * registerContentHandler or _saveContentHandlerToPrefs for that purpose.
   * @param   contentType
   *          The content Type being handled
   * @param   uri
   *          The uri of the web service
   * @param   title
   *          The human readable name of the web service
   */
  _updateContentTypeHandlerMap(contentType, uri, title) {
    if (!(contentType in this._contentTypes))
      this._contentTypes[contentType] = [];

    // Avoid adding duplicates
    if (this._typeIsRegistered(contentType, uri))
      return;

    this._contentTypes[contentType].push(new ServiceInfo(contentType, uri, title));

    if (!(contentType in this._blockedTypes)) {
      let converterContractID = this._getConverterContractID(contentType);
      let cr = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
      cr.registerFactory(WCC_CLASSID, WCC_CLASSNAME, converterContractID,
                         WebContentConverterFactory);
    }
  },

  /**
   * See nsIWebContentConverterService
   */
  getContentHandlers(contentType, countRef) {
    if (countRef) {
      countRef.value = 0;
    }
    if (!(contentType in this._contentTypes))
      return [];

    let handlers = this._contentTypes[contentType];
    if (countRef) {
      countRef.value = handlers.length;
    }
    return handlers;
  },

  /**
   * See nsIWebContentConverterService
   */
  resetHandlersForType(contentType) {
    // currently unused within the tree, so only useful for extensions; previous
    // impl. was buggy (and even infinite-looped!), so I argue that this is a
    // definite improvement
    throw Cr.NS_ERROR_NOT_IMPLEMENTED;
  },

  /**
   * Registers a handler from the settings on a preferences branch.
   *
   * Since we support up to six predefined readers, we need to handle gaps
   * better, since the first branch with user-added values will be .6
   *
   * How we deal with that is to check to see if there's no prefs in the
   * branch and stop cycling once that's true.  This doesn't fix the case
   * where a user manually removes a reader, but that's not supported yet!
   *
   * @param branch
   *        an nsIPrefBranch containing "type", "uri", and "title" preferences
   *        corresponding to the content handler to be registered
   */
  _registerContentHandlerHavingBranch(branch) {
    let vals = branch.getChildList("");
    if (vals.length == 0)
      return;

    let type = branch.getCharPref("type");
    let uri = branch.getComplexValue("uri", Ci.nsIPrefLocalizedString).data;
    let title = branch.getComplexValue("title",
                                       Ci.nsIPrefLocalizedString).data;
    this._updateContentTypeHandlerMap(type, uri, title);
  },

  /**
   * Load the auto handler, content handler and protocol tables from
   * preferences.
   */
  _init() {
    let ps = Services.prefs;

    let children = ps.getBranch(PREF_CONTENTHANDLERS_BRANCH)
                     .getChildList("");

    // first get the numbers of the providers by getting all ###.uri prefs
    let nums = children.map((child) => {
      let match = /^(\d+)\.uri$/.exec(child);
      return match ? match[1] : "";
    }).filter(child => !!child)
      .sort();


    // now register them
    for (let num of nums) {
      let branch = ps.getBranch(PREF_CONTENTHANDLERS_BRANCH + num + ".");
      try {
        this._registerContentHandlerHavingBranch(branch);
      } catch (ex) {
        // do nothing, the next branch might have values
      }
    }

    // We need to do this _after_ registering all of the available handlers,
    // so that getWebContentHandlerByURI can return successfully.
    let autoBranch;
    try {
      autoBranch = ps.getBranch(PREF_CONTENTHANDLERS_AUTO);
    } catch (e) {
      // No auto branch yet, that's fine
      // LOG("WCCR.init: There is no auto branch, benign");
    }

    if (autoBranch) {
      for (let type of autoBranch.getChildList("")) {
        let uri = autoBranch.getCharPref(type);
        if (uri) {
          let handler = this.getWebContentHandlerByURI(type, uri);
          if (handler) {
            this._setAutoHandler(type, handler);
          }
        }
      }
    }
  },

  /**
   * See nsIObserver
   */
  observe(subject, topic, data) {
    let os = Services.obs;
    switch (topic) {
    case "app-startup":
      os.addObserver(this, "browser-ui-startup-complete");
      break;
    case "browser-ui-startup-complete":
      os.removeObserver(this, "browser-ui-startup-complete");
      this._init();
      break;
    }
  },

  /**
   * See nsIFactory
   */
  createInstance(outer, iid) {
    if (outer != null)
      throw Cr.NS_ERROR_NO_AGGREGATION;
    return this.QueryInterface(iid);
  },

  classID: WCCR_CLASSID,

  /**
   * See nsISupports
   */
  QueryInterface: XPCOMUtils.generateQI(
     [Ci.nsIWebContentConverterService,
      Ci.nsIWebContentHandlerRegistrar,
      Ci.nsIObserver,
      Ci.nsIFactory]),

  _xpcom_categories: [{
    category: "app-startup",
    service: true
  }]
};

function WebContentConverterRegistrarContent() {
  this._contentTypes = {};
}

WebContentConverterRegistrarContent.prototype = {

  /**
   * Load the auto handler, content handler and protocol tables from
   * preferences.
   */
  _init() {
    let ps = Services.prefs;

    let children = ps.getBranch(PREF_CONTENTHANDLERS_BRANCH)
                     .getChildList("");

    // first get the numbers of the providers by getting all ###.uri prefs
    let nums = children.map((child) => {
      let match = /^(\d+)\.uri$/.exec(child);
      return match ? match[1] : "";
    }).filter(child => !!child)
      .sort();

    // now register them
    for (let num of nums) {
      let branch = ps.getBranch(PREF_CONTENTHANDLERS_BRANCH + num + ".");
      try {
        this._registerContentHandlerHavingBranch(branch);
      } catch (ex) {
        // do nothing, the next branch might have values
      }
    }
  },

  _typeIsRegistered(contentType, uri) {
    return this._contentTypes[contentType]
               .some(e => e.uri == uri);
  },

  /**
   * Since we support up to six predefined readers, we need to handle gaps
   * better, since the first branch with user-added values will be .6
   *
   * How we deal with that is to check to see if there's no prefs in the
   * branch and stop cycling once that's true.  This doesn't fix the case
   * where a user manually removes a reader, but that's not supported yet!
   *
   * @param   branch
   *          The pref branch to register the content handler under
   *
   */
  _registerContentHandlerHavingBranch(branch) {
    let vals = branch.getChildList("");
    if (vals.length == 0)
      return;

    let type = branch.getCharPref("type");
    let uri = branch.getComplexValue("uri", Ci.nsIPrefLocalizedString).data;
    let title = branch.getComplexValue("title",
                                       Ci.nsIPrefLocalizedString).data;
    this._updateContentTypeHandlerMap(type, uri, title);
  },

  _updateContentTypeHandlerMap(contentType, uri, title) {
    if (!(contentType in this._contentTypes))
      this._contentTypes[contentType] = [];

    // Avoid adding duplicates
    if (this._typeIsRegistered(contentType, uri))
      return;

    this._contentTypes[contentType].push(new ServiceInfo(contentType, uri, title));

    if (!(contentType in this._blockedTypes)) {
      let converterContractID = this._getConverterContractID(contentType);
      let cr = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
      cr.registerFactory(WCC_CLASSID, WCC_CLASSNAME, converterContractID,
                         WebContentConverterFactory);
    }
  },

  /**
   * See nsIWebContentConverterService
   */
  getContentHandlers(contentType, countRef) {
    this._init();
    if (countRef) {
      countRef.value = 0;
    }

    if (!(contentType in this._contentTypes))
      return [];

    let handlers = this._contentTypes[contentType];
    if (countRef) {
      countRef.value = handlers.length;
    }
    return handlers;
  },

  setAutoHandler(contentType, handler) {
    Services.cpmm.sendAsyncMessage("WCCR:setAutoHandler",
                                   { contentType, handler });
  },

  getWebContentHandlerByURI(contentType, uri) {
    return this.getContentHandlers(contentType)
               .find(e => e.uri == uri) || null;
  },

  /**
   * See nsIWebContentHandlerRegistrar
   */
  registerContentHandler(aContentType, aURIString, aTitle, aBrowserOrWindow) {
    // aBrowserOrWindow must be a window.
    let messageManager = aBrowserOrWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                                         .getInterface(Ci.nsIWebNavigation)
                                         .QueryInterface(Ci.nsIDocShell)
                                         .QueryInterface(Ci.nsIInterfaceRequestor)
                                         .getInterface(Ci.nsITabChild)
                                         .messageManager;

    let uri = Utils.checkAndGetURI(aURIString, aBrowserOrWindow);
    // XXX We should be throwing a Utils.getSecurityError() here in at least
    // some cases.  See bug 1266492.
    if (Utils.resolveContentType(aContentType) != TYPE_MAYBE_FEED) {
      return;
    }

    messageManager.sendAsyncMessage("WCCR:registerContentHandler",
                                    { contentType: aContentType,
                                      uri: uri.spec,
                                      title: aTitle });
  },

  registerProtocolHandler(aProtocol, aURIString, aTitle, aBrowserOrWindow) {
    // aBrowserOrWindow must be a window.
    let messageManager = aBrowserOrWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                                         .getInterface(Ci.nsIWebNavigation)
                                         .QueryInterface(Ci.nsIDocShell)
                                         .QueryInterface(Ci.nsIInterfaceRequestor)
                                         .getInterface(Ci.nsITabChild)
                                         .messageManager;

    let uri = Utils.checkAndGetURI(aURIString, aBrowserOrWindow);
    Utils.checkProtocolHandlerAllowed(aProtocol, aURIString, aBrowserOrWindow);

    messageManager.sendAsyncMessage("WCCR:registerProtocolHandler",
                                    { protocol: aProtocol,
                                      uri: uri.spec,
                                      title: aTitle });
  },

  /**
   * See nsIFactory
   */
  createInstance(outer, iid) {
    if (outer != null)
      throw Cr.NS_ERROR_NO_AGGREGATION;
    return this.QueryInterface(iid);
  },

  classID: WCCR_CLASSID,

  /**
   * See nsISupports
   */
  QueryInterface: XPCOMUtils.generateQI(
                     [Ci.nsIWebContentHandlerRegistrar,
                      Ci.nsIWebContentConverterService,
                      Ci.nsIFactory])
};

this.NSGetFactory =
  (Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_CONTENT) ?
    XPCOMUtils.generateNSGetFactory([WebContentConverterRegistrarContent]) :
    XPCOMUtils.generateNSGetFactory([WebContentConverterRegistrar]);
PK
!<OăD��components/nsBrowserGlue.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/AppConstants.jsm");

(function earlyBlankFirstPaint() {
  if (!Services.prefs.getBoolPref("browser.startup.blankWindow", false))
    return;

  let store = Cc["@mozilla.org/xul/xulstore;1"].getService(Ci.nsIXULStore);
  let getValue = attr =>
    store.getValue("chrome://browser/content/browser.xul", "main-window", attr);
  let width = getValue("width");
  let height = getValue("height");

  // The clean profile case isn't handled yet. Return early for now.
  if (!width || !height)
    return;

  let screenX = getValue("screenX");
  let screenY = getValue("screenY");
  let browserWindowFeatures =
    "chrome,all,dialog=no,extrachrome,menubar,resizable,scrollbars,status," +
    "location,toolbar,personalbar," +
    `left=${screenX},top=${screenY}`;

  if (Services.prefs.getBoolPref("browser.suppress_first_window_animation"))
    browserWindowFeatures += ",suppressanimation";

  let win = Services.ww.openWindow(null, "about:blank", null,
                                   browserWindowFeatures, null);

  // Hide the titlebar if the actual browser window will draw in it.
  if (Services.prefs.getBoolPref("browser.tabs.drawInTitlebar")) {
    win.QueryInterface(Ci.nsIInterfaceRequestor)
       .getInterface(Ci.nsIDOMWindowUtils)
       .setChromeMargin(0, 2, 2, 2);
  }

  if (AppConstants.platform != "macosx") {
    // On Windows/Linux the position is in device pixels rather than CSS pixels.
    let scale = win.devicePixelRatio;
    if (scale > 1)
      win.moveTo(screenX / scale, screenY / scale);
  }

  // The sizemode="maximized" attribute needs to be set before first paint.
  let docElt = win.document.documentElement;
  let sizemode = getValue("sizemode");
  if (sizemode == "maximized") {
    docElt.setAttribute("sizemode", sizemode);

    // Needed for when the user leaves the maximized mode.
    docElt.setAttribute("height", height);
    docElt.setAttribute("width", width);
  } else {
    // Setting the size of the window in the features string instead of here
    // causes the window to grow by the size of the titlebar.
    win.resizeTo(width, height);
  }

  // The window becomes visible after OnStopRequest, so make this happen now.
  win.stop();

  // Used in nsBrowserContentHandler.js to close unwanted blank windows.
  docElt.setAttribute("windowtype", "navigator:blank");
})();

Cu.importGlobalProperties(["fetch"]);

XPCOMUtils.defineLazyServiceGetter(this, "WindowsUIUtils", "@mozilla.org/windows-ui-utils;1", "nsIWindowsUIUtils");
XPCOMUtils.defineLazyGetter(this, "WeaveService", () =>
  Cc["@mozilla.org/weave/service;1"].getService().wrappedJSObject
);

// lazy module getters

XPCOMUtils.defineLazyModuleGetters(this, {
  AddonManager: "resource://gre/modules/AddonManager.jsm",
  AppMenuNotifications: "resource://gre/modules/AppMenuNotifications.jsm",
  AsyncPrefs: "resource://gre/modules/AsyncPrefs.jsm",
  AsyncShutdown: "resource://gre/modules/AsyncShutdown.jsm",
  AutoCompletePopup: "resource://gre/modules/AutoCompletePopup.jsm",
  BookmarkHTMLUtils: "resource://gre/modules/BookmarkHTMLUtils.jsm",
  BookmarkJSONUtils: "resource://gre/modules/BookmarkJSONUtils.jsm",
  BrowserErrorReporter: "resource:///modules/BrowserErrorReporter.jsm",
  BrowserUITelemetry: "resource:///modules/BrowserUITelemetry.jsm",
  BrowserUsageTelemetry: "resource:///modules/BrowserUsageTelemetry.jsm",
  ContentClick: "resource:///modules/ContentClick.jsm",
  ContextualIdentityService: "resource://gre/modules/ContextualIdentityService.jsm",
  CustomizableUI: "resource:///modules/CustomizableUI.jsm",
  DateTimePickerHelper: "resource://gre/modules/DateTimePickerHelper.jsm",
  ExtensionsUI: "resource:///modules/ExtensionsUI.jsm",
  Feeds: "resource:///modules/Feeds.jsm",
  FileUtils: "resource://gre/modules/FileUtils.jsm",
  FileSource: "resource://gre/modules/L10nRegistry.jsm",
  FormValidationHandler: "resource:///modules/FormValidationHandler.jsm",
  FxAccounts: "resource://gre/modules/FxAccounts.jsm",
  HybridContentTelemetry: "resource://gre/modules/HybridContentTelemetry.jsm",
  Integration: "resource://gre/modules/Integration.jsm",
  L10nRegistry: "resource://gre/modules/L10nRegistry.jsm",
  LanguagePrompt: "resource://gre/modules/LanguagePrompt.jsm",
  LightweightThemeManager: "resource://gre/modules/LightweightThemeManager.jsm",
  LoginHelper: "resource://gre/modules/LoginHelper.jsm",
  LoginManagerParent: "resource://gre/modules/LoginManagerParent.jsm",
  NetUtil: "resource://gre/modules/NetUtil.jsm",
  NewTabUtils: "resource://gre/modules/NewTabUtils.jsm",
  Normandy: "resource://normandy/Normandy.jsm",
  ObjectUtils: "resource://gre/modules/ObjectUtils.jsm",
  OS: "resource://gre/modules/osfile.jsm",
  PageActions: "resource:///modules/PageActions.jsm",
  PageThumbs: "resource://gre/modules/PageThumbs.jsm",
  PdfJs: "resource://pdf.js/PdfJs.jsm",
  PermissionUI: "resource:///modules/PermissionUI.jsm",
  PingCentre: "resource:///modules/PingCentre.jsm",
  PlacesBackups: "resource://gre/modules/PlacesBackups.jsm",
  PlacesUtils: "resource://gre/modules/PlacesUtils.jsm",
  PluralForm: "resource://gre/modules/PluralForm.jsm",
  PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
  ProcessHangMonitor: "resource:///modules/ProcessHangMonitor.jsm",
  ReaderParent: "resource:///modules/ReaderParent.jsm",
  RecentWindow: "resource:///modules/RecentWindow.jsm",
  RemotePrompt: "resource:///modules/RemotePrompt.jsm",
  SafeBrowsing: "resource://gre/modules/SafeBrowsing.jsm",
  Sanitizer: "resource:///modules/Sanitizer.jsm",
  SessionStore: "resource:///modules/sessionstore/SessionStore.jsm",
  ShellService: "resource:///modules/ShellService.jsm",
  SimpleServiceDiscovery: "resource://gre/modules/SimpleServiceDiscovery.jsm",
  TabCrashHandler: "resource:///modules/ContentCrashHandlers.jsm",
  UIState: "resource://services-sync/UIState.jsm",
  UITour: "resource:///modules/UITour.jsm",
  WebChannel: "resource://gre/modules/WebChannel.jsm",
  WindowsRegistry: "resource://gre/modules/WindowsRegistry.jsm",
});

/* global AboutHome:false, ContentPrefServiceParent:false, ContentSearch:false,
          UpdateListener:false, webrtcUI:false */

/**
 * IF YOU ADD OR REMOVE FROM THIS LIST, PLEASE UPDATE THE LIST ABOVE AS WELL.
 * XXX Bug 1325373 is for making eslint detect these automatically.
 */

let initializedModules = {};

[
  ["AboutHome", "resource:///modules/AboutHome.jsm", "init"],
  ["ContentPrefServiceParent", "resource://gre/modules/ContentPrefServiceParent.jsm", "alwaysInit"],
  ["ContentSearch", "resource:///modules/ContentSearch.jsm", "init"],
  ["UpdateListener", "resource://gre/modules/UpdateListener.jsm", "init"],
  ["webrtcUI", "resource:///modules/webrtcUI.jsm", "init"],
].forEach(([name, resource, init]) => {
  XPCOMUtils.defineLazyGetter(this, name, () => {
    ChromeUtils.import(resource, initializedModules);
    initializedModules[name][init]();
    return initializedModules[name];
  });
});

if (AppConstants.MOZ_CRASHREPORTER) {
  XPCOMUtils.defineLazyModuleGetters(this, {
    PluginCrashReporter: "resource:///modules/ContentCrashHandlers.jsm",
    UnsubmittedCrashHandler: "resource:///modules/ContentCrashHandlers.jsm",
    CrashSubmit: "resource://gre/modules/CrashSubmit.jsm",
  });
}

XPCOMUtils.defineLazyGetter(this, "gBrandBundle", function() {
  return Services.strings.createBundle("chrome://branding/locale/brand.properties");
});

XPCOMUtils.defineLazyGetter(this, "gBrowserBundle", function() {
  return Services.strings.createBundle("chrome://browser/locale/browser.properties");
});

const global = this;

const listeners = {
  observers: {
    "update-staged": ["UpdateListener"],
    "update-downloaded": ["UpdateListener"],
    "update-available": ["UpdateListener"],
    "update-error": ["UpdateListener"],
  },

  ppmm: {
    // PLEASE KEEP THIS LIST IN SYNC WITH THE LISTENERS ADDED IN ContentPrefServiceParent.init
    "ContentPrefs:FunctionCall": ["ContentPrefServiceParent"],
    "ContentPrefs:AddObserverForName": ["ContentPrefServiceParent"],
    "ContentPrefs:RemoveObserverForName": ["ContentPrefServiceParent"],
    // PLEASE KEEP THIS LIST IN SYNC WITH THE LISTENERS ADDED IN ContentPrefServiceParent.init

    // PLEASE KEEP THIS LIST IN SYNC WITH THE LISTENERS ADDED IN AsyncPrefs.init
    "AsyncPrefs:SetPref": ["AsyncPrefs"],
    "AsyncPrefs:ResetPref": ["AsyncPrefs"],
    // PLEASE KEEP THIS LIST IN SYNC WITH THE LISTENERS ADDED IN AsyncPrefs.init

    "FeedConverter:addLiveBookmark": ["Feeds"],
    "WCCR:setAutoHandler": ["Feeds"],
    "webrtc:UpdateGlobalIndicators": ["webrtcUI"],
    "webrtc:UpdatingIndicators": ["webrtcUI"],
  },

  mm: {
    "AboutHome:MaybeShowMigrateMessage": ["AboutHome"],
    "AboutHome:RequestUpdate": ["AboutHome"],
    "Content:Click": ["ContentClick"],
    "ContentSearch": ["ContentSearch"],
    "FormValidation:ShowPopup": ["FormValidationHandler"],
    "FormValidation:HidePopup": ["FormValidationHandler"],
    "Prompt:Open": ["RemotePrompt"],
    "Reader:FaviconRequest": ["ReaderParent"],
    "Reader:UpdateReaderButton": ["ReaderParent"],
    // PLEASE KEEP THIS LIST IN SYNC WITH THE MOBILE LISTENERS IN BrowserCLH.js
    "RemoteLogins:findLogins": ["LoginManagerParent"],
    "RemoteLogins:findRecipes": ["LoginManagerParent"],
    "RemoteLogins:onFormSubmit": ["LoginManagerParent"],
    "RemoteLogins:autoCompleteLogins": ["LoginManagerParent"],
    "RemoteLogins:removeLogin": ["LoginManagerParent"],
    "RemoteLogins:insecureLoginFormPresent": ["LoginManagerParent"],
    // PLEASE KEEP THIS LIST IN SYNC WITH THE MOBILE LISTENERS IN BrowserCLH.js
    "WCCR:registerProtocolHandler": ["Feeds"],
    "WCCR:registerContentHandler": ["Feeds"],
    "rtcpeer:CancelRequest": ["webrtcUI"],
    "rtcpeer:Request": ["webrtcUI"],
    "webrtc:CancelRequest": ["webrtcUI"],
    "webrtc:Request": ["webrtcUI"],
    "webrtc:StopRecording": ["webrtcUI"],
    "webrtc:UpdateBrowserIndicators": ["webrtcUI"],
  },

  observe(subject, topic, data) {
    for (let module of this.observers[topic]) {
      try {
        global[module].observe(subject, topic, data);
      } catch (e) {
        Cu.reportError(e);
      }
    }
  },

  receiveMessage(modules, data) {
    let val;
    for (let module of modules[data.name]) {
      try {
        val = global[module].receiveMessage(data) || val;
      } catch (e) {
        Cu.reportError(e);
      }
    }
    return val;
  },

  init() {
    for (let observer of Object.keys(this.observers)) {
      Services.obs.addObserver(this, observer);
    }

    let receiveMessageMM = this.receiveMessage.bind(this, this.mm);
    for (let message of Object.keys(this.mm)) {
      Services.mm.addMessageListener(message, receiveMessageMM);
    }

    let receiveMessagePPMM = this.receiveMessage.bind(this, this.ppmm);
    for (let message of Object.keys(this.ppmm)) {
      Services.ppmm.addMessageListener(message, receiveMessagePPMM);
    }
  }
};

// Seconds of idle before trying to create a bookmarks backup.
const BOOKMARKS_BACKUP_IDLE_TIME_SEC = 8 * 60;
// Minimum interval between backups.  We try to not create more than one backup
// per interval.
const BOOKMARKS_BACKUP_MIN_INTERVAL_DAYS = 1;
// Maximum interval between backups.  If the last backup is older than these
// days we will try to create a new one more aggressively.
const BOOKMARKS_BACKUP_MAX_INTERVAL_DAYS = 3;
// Seconds of idle time before the late idle tasks will be scheduled.
const LATE_TASKS_IDLE_TIME_SEC = 20;
// Time after we stop tracking startup crashes.
const STARTUP_CRASHES_END_DELAY_MS = 30 * 1000;

/*
 * OS X has the concept of zero-window sessions and therefore ignores the
 * browser-lastwindow-close-* topics.
 */
const OBSERVE_LASTWINDOW_CLOSE_TOPICS = AppConstants.platform != "macosx";

function BrowserGlue() {
  XPCOMUtils.defineLazyServiceGetter(this, "_idleService",
                                     "@mozilla.org/widget/idleservice;1",
                                     "nsIIdleService");

  XPCOMUtils.defineLazyGetter(this, "_distributionCustomizer", function() {
                                ChromeUtils.import("resource:///modules/distribution.js");
                                return new DistributionCustomizer();
                              });

  XPCOMUtils.defineLazyServiceGetter(this, "AlertsService", "@mozilla.org/alerts-service;1", "nsIAlertsService");

  this._init();
}

BrowserGlue.prototype = {
  _saveSession: false,
  _migrationImportsDefaultBookmarks: false,
  _placesBrowserInitComplete: false,

  _setPrefToSaveSession: function BG__setPrefToSaveSession(aForce) {
    if (!this._saveSession && !aForce)
      return;

    if (!PrivateBrowsingUtils.permanentPrivateBrowsing) {
      Services.prefs.setBoolPref("browser.sessionstore.resume_session_once", true);
    }

    // This method can be called via [NSApplication terminate:] on Mac, which
    // ends up causing prefs not to be flushed to disk, so we need to do that
    // explicitly here. See bug 497652.
    Services.prefs.savePrefFile(null);
  },

  _setSyncAutoconnectDelay: function BG__setSyncAutoconnectDelay() {
    // Assume that a non-zero value for services.sync.autoconnectDelay should override
    if (Services.prefs.prefHasUserValue("services.sync.autoconnectDelay")) {
      let prefDelay = Services.prefs.getIntPref("services.sync.autoconnectDelay");

      if (prefDelay > 0)
        return;
    }

    // delays are in seconds
    const MAX_DELAY = 300;
    let delay = 3;
    let browserEnum = Services.wm.getEnumerator("navigator:browser");
    while (browserEnum.hasMoreElements()) {
      delay += browserEnum.getNext().gBrowser.tabs.length;
    }
    delay = delay <= MAX_DELAY ? delay : MAX_DELAY;

    ChromeUtils.import("resource://services-sync/main.js");
    Weave.Service.scheduler.delayedAutoConnect(delay);
  },

  /**
   * Lazily initialize PingCentre
   */
  get pingCentre() {
    const MAIN_TOPIC_ID = "main";
    Object.defineProperty(this, "pingCentre", {
      value: new PingCentre({ topic: MAIN_TOPIC_ID })
    });
    return this.pingCentre;
  },

  /**
   * Lazily initialize BrowserErrorReporter
   */
  get browserErrorReporter() {
    Object.defineProperty(this, "browserErrorReporter", {
      value: new BrowserErrorReporter(),
    });
    return this.browserErrorReporter;
  },

  _sendMainPingCentrePing() {
    const ACTIVITY_STREAM_ID = "activity-stream";
    const payload = {
      event: "AS_ENABLED",
      value: true
    };
    const options = {filter: ACTIVITY_STREAM_ID};
    this.pingCentre.sendPing(payload, options);
  },

  // nsIObserver implementation
  observe: function BG_observe(subject, topic, data) {
    switch (topic) {
      case "notifications-open-settings":
        this._openPreferences("privacy", { origin: "notifOpenSettings" });
        break;
      case "prefservice:after-app-defaults":
        this._onAppDefaults();
        break;
      case "final-ui-startup":
        this._beforeUIStartup();
        break;
      case "browser-delayed-startup-finished":
        this._onFirstWindowLoaded(subject);
        Services.obs.removeObserver(this, "browser-delayed-startup-finished");
        break;
      case "sessionstore-windows-restored":
        this._onWindowsRestored();
        break;
      case "browser:purge-session-history":
        // reset the console service's error buffer
        Services.console.logStringMessage(null); // clear the console (in case it's open)
        Services.console.reset();
        break;
      case "restart-in-safe-mode":
        this._onSafeModeRestart();
        break;
      case "quit-application-requested":
        this._onQuitRequest(subject, data);
        break;
      case "quit-application-granted":
        this._onQuitApplicationGranted();
        break;
      case "browser-lastwindow-close-requested":
        if (OBSERVE_LASTWINDOW_CLOSE_TOPICS) {
          // The application is not actually quitting, but the last full browser
          // window is about to be closed.
          this._onQuitRequest(subject, "lastwindow");
        }
        break;
      case "browser-lastwindow-close-granted":
        if (OBSERVE_LASTWINDOW_CLOSE_TOPICS) {
          this._setPrefToSaveSession();
        }
        break;
      case "weave:service:ready":
        this._setSyncAutoconnectDelay();
        break;
      case "fxaccounts:onverified":
        this._showSyncStartedDoorhanger();
        break;
      case "fxaccounts:device_connected":
        this._onDeviceConnected(data);
        break;
      case "fxaccounts:verify_login":
        this._onVerifyLoginNotification(JSON.parse(data));
        break;
      case "fxaccounts:device_disconnected":
        data = JSON.parse(data);
        if (data.isLocalDevice) {
          this._onDeviceDisconnected();
        }
        break;
      case "weave:engine:clients:display-uris":
        this._onDisplaySyncURIs(subject);
        break;
      case "session-save":
        this._setPrefToSaveSession(true);
        subject.QueryInterface(Ci.nsISupportsPRBool);
        subject.data = true;
        break;
      case "places-init-complete":
        Services.obs.removeObserver(this, "places-init-complete");
        if (!this._migrationImportsDefaultBookmarks)
          this._initPlaces(false);
        break;
      case "idle":
        this._backupBookmarks();
        break;
      case "distribution-customization-complete":
        Services.obs.removeObserver(this, "distribution-customization-complete");
        // Customization has finished, we don't need the customizer anymore.
        delete this._distributionCustomizer;
        break;
      case "browser-glue-test": // used by tests
        if (data == "post-update-notification") {
          if (Services.prefs.prefHasUserValue("app.update.postupdate"))
            this._showUpdateNotification();
        } else if (data == "force-ui-migration") {
          this._migrateUI();
        } else if (data == "force-distribution-customization") {
          this._distributionCustomizer.applyPrefDefaults();
          this._distributionCustomizer.applyCustomizations();
          // To apply distribution bookmarks use "places-init-complete".
        } else if (data == "force-places-init") {
          this._initPlaces(false);
        } else if (data == "smart-bookmarks-init") {
          this.ensurePlacesDefaultQueriesInitialized().then(() => {
            Services.obs.notifyObservers(null, "test-smart-bookmarks-done");
          });
        } else if (data == "mock-alerts-service") {
          Object.defineProperty(this, "AlertsService", {
            value: subject.wrappedJSObject
          });
        } else if (data == "places-browser-init-complete") {
          if (this._placesBrowserInitComplete) {
            Services.obs.notifyObservers(null, "places-browser-init-complete");
          }
        } else if (data == "migrateMatchBucketsPrefForUI66") {
          this._migrateMatchBucketsPrefForUI66().then(() => {
            Services.obs.notifyObservers(null, "browser-glue-test",
                                         "migrateMatchBucketsPrefForUI66-done");
          });
        }
        break;
      case "initial-migration-will-import-default-bookmarks":
        this._migrationImportsDefaultBookmarks = true;
        break;
      case "initial-migration-did-import-default-bookmarks":
        this._initPlaces(true);
        break;
      case "handle-xul-text-link":
        let linkHandled = subject.QueryInterface(Ci.nsISupportsPRBool);
        if (!linkHandled.data) {
          let win = RecentWindow.getMostRecentBrowserWindow();
          if (win) {
            data = JSON.parse(data);
            let where = win.whereToOpenLink(data);
            // Preserve legacy behavior of non-modifier left-clicks
            // opening in a new selected tab.
            if (where == "current") {
              where = "tab";
            }
            win.openUILinkIn(data.href, where);
            linkHandled.data = true;
          }
        }
        break;
      case "profile-before-change":
         // Any component depending on Places should be finalized in
         // _onPlacesShutdown.  Any component that doesn't need to act after
         // the UI has gone should be finalized in _onQuitApplicationGranted.
        this._dispose();
        break;
      case "keyword-search":
        // This notification is broadcast by the docshell when it "fixes up" a
        // URI that it's been asked to load into a keyword search.
        let engine = null;
        try {
          engine = subject.QueryInterface(Ci.nsISearchEngine);
        } catch (ex) {
          Cu.reportError(ex);
        }
        let win = RecentWindow.getMostRecentBrowserWindow();
        win.BrowserSearch.recordSearchInTelemetry(engine, "urlbar");
        break;
      case "browser-search-engine-modified":
        // Ensure we cleanup the hiddenOneOffs pref when removing
        // an engine, and that newly added engines are visible.
        if (data == "engine-added" || data == "engine-removed") {
          let engineName = subject.QueryInterface(Ci.nsISearchEngine).name;
          let pref = Services.prefs.getStringPref("browser.search.hiddenOneOffs");
          let hiddenList = pref ? pref.split(",") : [];
          hiddenList = hiddenList.filter(x => x !== engineName);
          Services.prefs.setStringPref("browser.search.hiddenOneOffs", hiddenList.join(","));
        }
        break;
      case "flash-plugin-hang":
        this._handleFlashHang();
        break;
      case "xpi-signature-changed":
        let disabledAddons = JSON.parse(data).disabled;
        AddonManager.getAddonsByIDs(disabledAddons, (addons) => {
          for (let addon of addons) {
            if (addon.type != "experiment") {
              this._notifyUnsignedAddonsDisabled();
              break;
            }
          }
        });
        break;
      case "sync-ui-state:update":
        this._updateFxaBadges();
        break;
      case "handlersvc-store-initialized":
        // Initialize PdfJs when running in-process and remote. This only
        // happens once since PdfJs registers global hooks. If the PdfJs
        // extension is installed the init method below will be overridden
        // leaving initialization to the extension.
        // parent only: configure default prefs, set up pref observers, register
        // pdf content handler, and initializes parent side message manager
        // shim for privileged api access.
        PdfJs.init(true);
        break;
      case "shield-init-complete":
        this._shieldInitComplete = true;
        this._sendMainPingCentrePing();
        break;
    }
  },

  // initialization (called on application startup)
  _init: function BG__init() {
    let os = Services.obs;
    os.addObserver(this, "notifications-open-settings");
    os.addObserver(this, "prefservice:after-app-defaults");
    os.addObserver(this, "final-ui-startup");
    os.addObserver(this, "browser-delayed-startup-finished");
    os.addObserver(this, "sessionstore-windows-restored");
    os.addObserver(this, "browser:purge-session-history");
    os.addObserver(this, "quit-application-requested");
    os.addObserver(this, "quit-application-granted");
    if (OBSERVE_LASTWINDOW_CLOSE_TOPICS) {
      os.addObserver(this, "browser-lastwindow-close-requested");
      os.addObserver(this, "browser-lastwindow-close-granted");
    }
    os.addObserver(this, "weave:service:ready");
    os.addObserver(this, "fxaccounts:onverified");
    os.addObserver(this, "fxaccounts:device_connected");
    os.addObserver(this, "fxaccounts:verify_login");
    os.addObserver(this, "fxaccounts:device_disconnected");
    os.addObserver(this, "weave:engine:clients:display-uris");
    os.addObserver(this, "session-save");
    os.addObserver(this, "places-init-complete");
    os.addObserver(this, "distribution-customization-complete");
    os.addObserver(this, "handle-xul-text-link");
    os.addObserver(this, "profile-before-change");
    os.addObserver(this, "keyword-search");
    os.addObserver(this, "browser-search-engine-modified");
    os.addObserver(this, "restart-in-safe-mode");
    os.addObserver(this, "flash-plugin-hang");
    os.addObserver(this, "xpi-signature-changed");
    os.addObserver(this, "sync-ui-state:update");
    os.addObserver(this, "handlersvc-store-initialized");
    os.addObserver(this, "shield-init-complete");

    this._flashHangCount = 0;
    this._firstWindowReady = new Promise(resolve => this._firstWindowLoaded = resolve);
    if (AppConstants.platform == "win") {
      JawsScreenReaderVersionCheck.init();
    }
  },

  // cleanup (called on application shutdown)
  _dispose: function BG__dispose() {
    let os = Services.obs;
    os.removeObserver(this, "notifications-open-settings");
    os.removeObserver(this, "prefservice:after-app-defaults");
    os.removeObserver(this, "final-ui-startup");
    os.removeObserver(this, "sessionstore-windows-restored");
    os.removeObserver(this, "browser:purge-session-history");
    os.removeObserver(this, "quit-application-requested");
    os.removeObserver(this, "quit-application-granted");
    os.removeObserver(this, "restart-in-safe-mode");
    if (OBSERVE_LASTWINDOW_CLOSE_TOPICS) {
      os.removeObserver(this, "browser-lastwindow-close-requested");
      os.removeObserver(this, "browser-lastwindow-close-granted");
    }
    os.removeObserver(this, "weave:service:ready");
    os.removeObserver(this, "fxaccounts:onverified");
    os.removeObserver(this, "fxaccounts:device_connected");
    os.removeObserver(this, "fxaccounts:verify_login");
    os.removeObserver(this, "fxaccounts:device_disconnected");
    os.removeObserver(this, "weave:engine:clients:display-uris");
    os.removeObserver(this, "session-save");
    if (this._bookmarksBackupIdleTime) {
      this._idleService.removeIdleObserver(this, this._bookmarksBackupIdleTime);
      delete this._bookmarksBackupIdleTime;
    }
    if (this._lateTasksIdleObserver) {
      this._idleService.removeIdleObserver(this._lateTasksIdleObserver, LATE_TASKS_IDLE_TIME_SEC);
      delete this._lateTasksIdleObserver;
    }
    if (this._gmpInstallManager) {
      this._gmpInstallManager.uninit();
      delete this._gmpInstallManager;
    }
    try {
      os.removeObserver(this, "places-init-complete");
    } catch (ex) { /* Could have been removed already */ }
    os.removeObserver(this, "handle-xul-text-link");
    os.removeObserver(this, "profile-before-change");
    os.removeObserver(this, "keyword-search");
    os.removeObserver(this, "browser-search-engine-modified");
    os.removeObserver(this, "flash-plugin-hang");
    os.removeObserver(this, "xpi-signature-changed");
    os.removeObserver(this, "sync-ui-state:update");
    os.removeObserver(this, "shield-init-complete");
  },

  _onAppDefaults: function BG__onAppDefaults() {
    // apply distribution customizations (prefs)
    // other customizations are applied in _beforeUIStartup()
    this._distributionCustomizer.applyPrefDefaults();
  },

  // runs on startup, before the first command line handler is invoked
  // (i.e. before the first window is opened)
  _beforeUIStartup: function BG__beforeUIStartup() {
    // check if we're in safe mode
    if (Services.appinfo.inSafeMode) {
      Services.ww.openWindow(null, "chrome://browser/content/safeMode.xul",
                             "_blank", "chrome,centerscreen,modal,resizable=no", null);
    }

    // apply distribution customizations
    // prefs are applied in _onAppDefaults()
    this._distributionCustomizer.applyCustomizations();

    // handle any UI migration
    this._migrateUI();

    listeners.init();

    SessionStore.init();

    let vendorShortName = gBrandBundle.GetStringFromName("vendorShortName");

    LightweightThemeManager.addBuiltInTheme({
      id: "[email protected]",
      name: gBrowserBundle.GetStringFromName("lightTheme.name"),
      description: gBrowserBundle.GetStringFromName("lightTheme.description"),
      iconURL: "resource:///chrome/browser/content/browser/defaultthemes/light.icon.svg",
      textcolor: "black",
      accentcolor: "white",
      author: vendorShortName,
    });
    LightweightThemeManager.addBuiltInTheme({
      id: "[email protected]",
      name: gBrowserBundle.GetStringFromName("darkTheme.name"),
      description: gBrowserBundle.GetStringFromName("darkTheme.description"),
      iconURL: "resource:///chrome/browser/content/browser/defaultthemes/dark.icon.svg",
      textcolor: "white",
      accentcolor: "black",
      author: vendorShortName,
    });

    Normandy.init();

    // Initialize the default l10n resource sources for L10nRegistry.
    let locales = Services.locale.getPackagedLocales();
    const appSource = new FileSource("app", locales, "resource://app/localization/{locale}/");
    L10nRegistry.registerSource(appSource);

    Services.obs.notifyObservers(null, "browser-ui-startup-complete");
  },

  _checkForOldBuildUpdates() {
    // check for update if our build is old
    if (AppConstants.MOZ_UPDATER &&
        Services.prefs.getBoolPref("app.update.enabled") &&
        Services.prefs.getBoolPref("app.update.checkInstallTime")) {

      let buildID = Services.appinfo.appBuildID;
      let today = new Date().getTime();
      /* eslint-disable no-multi-spaces */
      let buildDate = new Date(buildID.slice(0, 4),     // year
                               buildID.slice(4, 6) - 1, // months are zero-based.
                               buildID.slice(6, 8),     // day
                               buildID.slice(8, 10),    // hour
                               buildID.slice(10, 12),   // min
                               buildID.slice(12, 14))   // ms
      .getTime();
      /* eslint-enable no-multi-spaces */

      const millisecondsIn24Hours = 86400000;
      let acceptableAge = Services.prefs.getIntPref("app.update.checkInstallTime.days") * millisecondsIn24Hours;

      if (buildDate + acceptableAge < today) {
        Cc["@mozilla.org/updates/update-service;1"].getService(Ci.nsIApplicationUpdateService).checkForBackgroundUpdates();
      }
    }
  },

  _onSafeModeRestart: function BG_onSafeModeRestart() {
    // prompt the user to confirm
    let strings = gBrowserBundle;
    let promptTitle = strings.GetStringFromName("safeModeRestartPromptTitle");
    let promptMessage = strings.GetStringFromName("safeModeRestartPromptMessage");
    let restartText = strings.GetStringFromName("safeModeRestartButton");
    let buttonFlags = (Services.prompt.BUTTON_POS_0 *
                       Services.prompt.BUTTON_TITLE_IS_STRING) +
                      (Services.prompt.BUTTON_POS_1 *
                       Services.prompt.BUTTON_TITLE_CANCEL) +
                      Services.prompt.BUTTON_POS_0_DEFAULT;

    let rv = Services.prompt.confirmEx(null, promptTitle, promptMessage,
                                       buttonFlags, restartText, null, null,
                                       null, {});
    if (rv != 0)
      return;

    let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]
                       .createInstance(Ci.nsISupportsPRBool);
    Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");

    if (!cancelQuit.data) {
      Services.startup.restartInSafeMode(Ci.nsIAppStartup.eAttemptQuit);
    }
  },

  _trackSlowStartup() {
    if (Services.startup.interrupted ||
        Services.prefs.getBoolPref("browser.slowStartup.notificationDisabled"))
      return;

    let currentTime = Date.now() - Services.startup.getStartupInfo().process;
    let averageTime = 0;
    let samples = 0;
    try {
      averageTime = Services.prefs.getIntPref("browser.slowStartup.averageTime");
      samples = Services.prefs.getIntPref("browser.slowStartup.samples");
    } catch (e) { }

    let totalTime = (averageTime * samples) + currentTime;
    samples++;
    averageTime = totalTime / samples;

    if (samples >= Services.prefs.getIntPref("browser.slowStartup.maxSamples")) {
      if (averageTime > Services.prefs.getIntPref("browser.slowStartup.timeThreshold"))
        this._calculateProfileAgeInDays().then(this._showSlowStartupNotification, null);
      averageTime = 0;
      samples = 0;
    }

    Services.prefs.setIntPref("browser.slowStartup.averageTime", averageTime);
    Services.prefs.setIntPref("browser.slowStartup.samples", samples);
  },

  async _calculateProfileAgeInDays() {
    let ProfileAge = ChromeUtils.import("resource://gre/modules/ProfileAge.jsm", {}).ProfileAge;
    let profileAge = new ProfileAge(null, null);

    let creationDate = await profileAge.created;
    let resetDate = await profileAge.reset;

    // if the profile was reset, consider the
    // reset date for its age.
    let profileDate = resetDate || creationDate;

    const ONE_DAY = 24 * 60 * 60 * 1000;
    return (Date.now() - profileDate) / ONE_DAY;
  },

  _showSlowStartupNotification(profileAge) {
    if (profileAge < 90) // 3 months
      return;

    let win = RecentWindow.getMostRecentBrowserWindow();
    if (!win)
      return;

    let productName = gBrandBundle.GetStringFromName("brandFullName");
    let message = win.gNavigatorBundle.getFormattedString("slowStartup.message", [productName]);

    let buttons = [
      {
        label:     win.gNavigatorBundle.getString("slowStartup.helpButton.label"),
        accessKey: win.gNavigatorBundle.getString("slowStartup.helpButton.accesskey"),
        callback() {
          win.openUILinkIn("https://support.mozilla.org/kb/reset-firefox-easily-fix-most-problems", "tab");
        }
      },
      {
        label:     win.gNavigatorBundle.getString("slowStartup.disableNotificationButton.label"),
        accessKey: win.gNavigatorBundle.getString("slowStartup.disableNotificationButton.accesskey"),
        callback() {
          Services.prefs.setBoolPref("browser.slowStartup.notificationDisabled", true);
        }
      }
    ];

    let nb = win.document.getElementById("global-notificationbox");
    nb.appendNotification(message, "slow-startup",
                          "chrome://browser/skin/slowStartup-16.png",
                          nb.PRIORITY_INFO_LOW, buttons);
  },

  /**
   * Show a notification bar offering a reset.
   *
   * @param reason
   *        String of either "unused" or "uninstall", specifying the reason
   *        why a profile reset is offered.
   */
  _resetProfileNotification(reason) {
    let win = RecentWindow.getMostRecentBrowserWindow();
    if (!win)
      return;

    ChromeUtils.import("resource://gre/modules/ResetProfile.jsm");
    if (!ResetProfile.resetSupported())
      return;

    let productName = gBrandBundle.GetStringFromName("brandShortName");
    let resetBundle = Services.strings
                              .createBundle("chrome://global/locale/resetProfile.properties");

    let message;
    if (reason == "unused") {
      message = resetBundle.formatStringFromName("resetUnusedProfile.message", [productName], 1);
    } else if (reason == "uninstall") {
      message = resetBundle.formatStringFromName("resetUninstalled.message", [productName], 1);
    } else {
      throw new Error(`Unknown reason (${reason}) given to _resetProfileNotification.`);
    }
    let buttons = [
      {
        label:     resetBundle.formatStringFromName("refreshProfile.resetButton.label", [productName], 1),
        accessKey: resetBundle.GetStringFromName("refreshProfile.resetButton.accesskey"),
        callback() {
          ResetProfile.openConfirmationDialog(win);
        }
      },
    ];

    let nb = win.document.getElementById("global-notificationbox");
    nb.appendNotification(message, "reset-profile-notification",
                          "chrome://global/skin/icons/question-16.png",
                          nb.PRIORITY_INFO_LOW, buttons);
  },

  _notifyUnsignedAddonsDisabled() {
    let win = RecentWindow.getMostRecentBrowserWindow();
    if (!win)
      return;

    let message = win.gNavigatorBundle.getString("unsignedAddonsDisabled.message");
    let buttons = [
      {
        label:     win.gNavigatorBundle.getString("unsignedAddonsDisabled.learnMore.label"),
        accessKey: win.gNavigatorBundle.getString("unsignedAddonsDisabled.learnMore.accesskey"),
        callback() {
          win.BrowserOpenAddonsMgr("addons://list/extension?unsigned=true");
        }
      },
    ];

    let nb = win.document.getElementById("high-priority-global-notificationbox");
    nb.appendNotification(message, "unsigned-addons-disabled", "",
                          nb.PRIORITY_WARNING_MEDIUM, buttons);
  },

  _firstWindowTelemetry(aWindow) {
    let scaling = aWindow.devicePixelRatio * 100;
    try {
      Services.telemetry.getHistogramById("DISPLAY_SCALING").add(scaling);
    } catch (ex) {}
  },

  // the first browser window has finished initializing
  _onFirstWindowLoaded: function BG__onFirstWindowLoaded(aWindow) {
    // Set up listeners and, if PdfJs is enabled, register the PDF stream converter.
    // We delay all of the parent's initialization other than stream converter
    // registration, because it requires file IO from nsHandlerService-json.js
    Services.ppmm.loadProcessScript("resource://pdf.js/pdfjschildbootstrap.js", true);
    if (PdfJs.enabled) {
      PdfJs.ensureRegistered();
      Services.ppmm.loadProcessScript("resource://pdf.js/pdfjschildbootstrap-enabled.js", true);
    }

    TabCrashHandler.init();
    if (AppConstants.MOZ_CRASHREPORTER) {
      PluginCrashReporter.init();
    }

    ProcessHangMonitor.init();

    // A channel for "remote troubleshooting" code...
    let channel = new WebChannel("remote-troubleshooting", "remote-troubleshooting");
    channel.listen((id, data, target) => {
      if (data.command == "request") {
        let {Troubleshoot} = ChromeUtils.import("resource://gre/modules/Troubleshoot.jsm", {});
        Troubleshoot.snapshot(snapshotData => {
          // for privacy we remove crash IDs and all preferences (but bug 1091944
          // exists to expose prefs once we are confident of privacy implications)
          delete snapshotData.crashes;
          delete snapshotData.modifiedPreferences;
          channel.send(snapshotData, target);
        });
      }
    });

    this._trackSlowStartup();

    // Offer to reset a user's profile if it hasn't been used for 60 days.
    const OFFER_PROFILE_RESET_INTERVAL_MS = 60 * 24 * 60 * 60 * 1000;
    let lastUse = Services.appinfo.replacedLockTime;
    let disableResetPrompt = Services.prefs.getBoolPref("browser.disableResetPrompt", false);

    if (!disableResetPrompt && lastUse &&
        Date.now() - lastUse >= OFFER_PROFILE_RESET_INTERVAL_MS) {
      this._resetProfileNotification("unused");
    } else if (AppConstants.platform == "win" && !disableResetPrompt) {
      // Check if we were just re-installed and offer Firefox Reset
      let updateChannel;
      try {
        updateChannel = ChromeUtils.import("resource://gre/modules/UpdateUtils.jsm", {}).UpdateUtils.UpdateChannel;
      } catch (ex) {}
      if (updateChannel) {
        let uninstalledValue =
          WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
                                     "Software\\Mozilla\\Firefox",
                                     `Uninstalled-${updateChannel}`);
        let removalSuccessful =
          WindowsRegistry.removeRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
                                       "Software\\Mozilla\\Firefox",
                                       `Uninstalled-${updateChannel}`);
        if (removalSuccessful && uninstalledValue == "True") {
          this._resetProfileNotification("uninstall");
        }
      }
    }

    this._checkForOldBuildUpdates();

    AutoCompletePopup.init();
    DateTimePickerHelper.init();
    // Check if Sync is configured
    if (Services.prefs.prefHasUserValue("services.sync.username")) {
      WeaveService.init();
    }

    PageThumbs.init();

    NewTabUtils.init();

    PageActions.init();

    this._firstWindowTelemetry(aWindow);
    this._firstWindowLoaded();

    // Set the default favicon size for UI views that use the page-icon protocol.
    PlacesUtils.favicons.setDefaultIconURIPreferredSize(16 * aWindow.devicePixelRatio);
  },

  _sendMediaTelemetry() {
    let win = Services.appShell.hiddenDOMWindow;
    let v = win.document.createElementNS("http://www.w3.org/1999/xhtml", "video");
    v.reportCanPlayTelemetry();
  },

  /**
   * Application shutdown handler.
   */
  _onQuitApplicationGranted() {
    // This pref must be set here because SessionStore will use its value
    // on quit-application.
    this._setPrefToSaveSession();

    // Call trackStartupCrashEnd here in case the delayed call on startup hasn't
    // yet occurred (see trackStartupCrashEnd caller in browser.js).
    try {
      Services.startup.trackStartupCrashEnd();
    } catch (e) {
      Cu.reportError("Could not end startup crash tracking in quit-application-granted: " + e);
    }

    if (this._bookmarksBackupIdleTime) {
      this._idleService.removeIdleObserver(this, this._bookmarksBackupIdleTime);
      delete this._bookmarksBackupIdleTime;
    }

    for (let mod of Object.values(initializedModules)) {
      if (mod.uninit) {
        mod.uninit();
      }
    }

    BrowserUsageTelemetry.uninit();
    // Only uninit PingCentre if the getter has initialized it
    if (Object.prototype.hasOwnProperty.call(this, "pingCentre")) {
      this.pingCentre.uninit();
    }

    PageThumbs.uninit();
    NewTabUtils.uninit();
    AutoCompletePopup.uninit();
    DateTimePickerHelper.uninit();

    // Browser errors are only collected on Nightly
    if (AppConstants.NIGHTLY_BUILD && AppConstants.MOZ_DATA_REPORTING) {
      this.browserErrorReporter.uninit();
    }

    Normandy.uninit();
  },

  // All initial windows have opened.
  _onWindowsRestored: function BG__onWindowsRestored() {
    if (this._windowsWereRestored) {
      return;
    }
    this._windowsWereRestored = true;

    // Browser errors are only collected on Nightly
    if (AppConstants.NIGHTLY_BUILD && AppConstants.MOZ_DATA_REPORTING) {
      this.browserErrorReporter.init();
    }

    BrowserUsageTelemetry.init();
    BrowserUITelemetry.init();

    // Show update notification, if needed.
    if (Services.prefs.prefHasUserValue("app.update.postupdate"))
      this._showUpdateNotification();

    ExtensionsUI.init();

    let signingRequired;
    if (AppConstants.MOZ_REQUIRE_SIGNING) {
      signingRequired = true;
    } else {
      signingRequired = Services.prefs.getBoolPref("xpinstall.signatures.required");
    }

    if (signingRequired) {
      let disabledAddons = AddonManager.getStartupChanges(AddonManager.STARTUP_CHANGE_DISABLED);
      AddonManager.getAddonsByIDs(disabledAddons, (addons) => {
        for (let addon of addons) {
          if (addon.type == "experiment")
            continue;

          if (addon.signedState <= AddonManager.SIGNEDSTATE_MISSING) {
            this._notifyUnsignedAddonsDisabled();
            break;
          }
        }
      });
    }

    if (AppConstants.MOZ_CRASHREPORTER) {
      UnsubmittedCrashHandler.init();
    }

    Sanitizer.onStartup();
    this._scheduleStartupIdleTasks();
    this._lateTasksIdleObserver = (idleService, topic, data) => {
      if (topic == "idle") {
        idleService.removeIdleObserver(this._lateTasksIdleObserver,
                                       LATE_TASKS_IDLE_TIME_SEC);
        delete this._lateTasksIdleObserver;
        this._scheduleArbitrarilyLateIdleTasks();
      }
    };
    this._idleService.addIdleObserver(
      this._lateTasksIdleObserver, LATE_TASKS_IDLE_TIME_SEC);
  },

  /**
   * Use this function as an entry point to schedule tasks that
   * need to run only once after startup, and can be scheduled
   * by using an idle callback.
   *
   * The functions scheduled here will fire from idle callbacks
   * once every window has finished being restored by session
   * restore, and it's guaranteed that they will run before
   * the equivalent per-window idle tasks
   * (from _schedulePerWindowIdleTasks in browser.js).
   *
   * If you have something that can wait even further than the
   * per-window initialization, please schedule them using
   * _scheduleArbitrarilyLateIdleTasks.
   * Don't be fooled by thinking that the use of the timeout parameter
   * will delay your function: it will just ensure that it potentially
   * happens _earlier_ than expected (when the timeout limit has been reached),
   * but it will not make it happen later (and out of order) compared
   * to the other ones scheduled together.
   */
  _scheduleStartupIdleTasks() {
    Services.tm.idleDispatchToMainThread(() => {
      ContextualIdentityService.load();
    });

    // Load the Login Manager data from disk off the main thread, some time
    // after startup.  If the data is required before this runs, for example
    // because a restored page contains a password field, it will be loaded on
    // the main thread, and this initialization request will be ignored.
    Services.tm.idleDispatchToMainThread(() => {
      try {
        Services.logins;
      } catch (ex) {
        Cu.reportError(ex);
      }
    }, 3000);

    // It's important that SafeBrowsing is initialized reasonably
    // early, so we use a maximum timeout for it.
    Services.tm.idleDispatchToMainThread(() => {
      SafeBrowsing.init();
    }, 5000);

    if (AppConstants.MOZ_CRASHREPORTER) {
      UnsubmittedCrashHandler.scheduleCheckForUnsubmittedCrashReports();
    }

    if (AppConstants.platform == "win") {
      Services.tm.idleDispatchToMainThread(() => {
        // For Windows 7, initialize the jump list module.
        const WINTASKBAR_CONTRACTID = "@mozilla.org/windows-taskbar;1";
        if (WINTASKBAR_CONTRACTID in Cc &&
            Cc[WINTASKBAR_CONTRACTID].getService(Ci.nsIWinTaskbar).available) {
          let temp = {};
          ChromeUtils.import("resource:///modules/WindowsJumpLists.jsm", temp);
          temp.WinTaskbarJumpList.startup();
        }
      });
    }

    if (AppConstants.MOZ_DEV_EDITION) {
      Services.tm.idleDispatchToMainThread(() => {
        this._createExtraDefaultProfile();
      });
    }

    Services.tm.idleDispatchToMainThread(() => {
      this._checkForDefaultBrowser();
    });

    Services.tm.idleDispatchToMainThread(() => {
      let {setTimeout} = ChromeUtils.import("resource://gre/modules/Timer.jsm", {});
      setTimeout(function() {
        Services.tm.idleDispatchToMainThread(Services.startup.trackStartupCrashEnd);
      }, STARTUP_CRASHES_END_DELAY_MS);
    });

    Services.tm.idleDispatchToMainThread(() => {
      let handlerService = Cc["@mozilla.org/uriloader/handler-service;1"].
                           getService(Ci.nsIHandlerService);
      handlerService.asyncInit();
    });

    if (AppConstants.platform == "win") {
      Services.tm.idleDispatchToMainThread(() => {
        JawsScreenReaderVersionCheck.onWindowsRestored();
      });
    }

    Services.tm.idleDispatchToMainThread(() => {
      LanguagePrompt.init();
    });

    Services.tm.idleDispatchToMainThread(() => {
      Services.blocklist.loadBlocklistAsync();
    });
  },

  /**
   * Use this function as an entry point to schedule tasks that need
   * to run once per session, at any arbitrary point in time.
   * This function will be called from an idle observer. Check the value of
   * LATE_TASKS_IDLE_TIME_SEC to see the current value for this idle
   * observer.
   *
   * Note: this function may never be called if the user is never idle for the
   * full length of the period of time specified. But given a reasonably low
   * value, this is unlikely.
   */
  _scheduleArbitrarilyLateIdleTasks() {
    Services.tm.idleDispatchToMainThread(() => {
      this._sendMediaTelemetry();
    });

    Services.tm.idleDispatchToMainThread(() => {
      // Telemetry for master-password - we do this after a delay as it
      // can cause IO if NSS/PSM has not already initialized.
      let tokenDB = Cc["@mozilla.org/security/pk11tokendb;1"]
                .getService(Ci.nsIPK11TokenDB);
      let token = tokenDB.getInternalKeyToken();
      let mpEnabled = token.hasPassword;
      if (mpEnabled) {
        Services.telemetry.getHistogramById("MASTER_PASSWORD_ENABLED").add(mpEnabled);
      }
    });

    Services.tm.idleDispatchToMainThread(() => {
      let obj = {};
      ChromeUtils.import("resource://gre/modules/GMPInstallManager.jsm", obj);
      this._gmpInstallManager = new obj.GMPInstallManager();
      // We don't really care about the results, if someone is interested they
      // can check the log.
      this._gmpInstallManager.simpleCheckAndInstall().catch(() => {});
    });
  },

  _createExtraDefaultProfile() {
    if (!AppConstants.MOZ_DEV_EDITION) {
      return;
    }
    // If Developer Edition is the only installed Firefox version and no other
    // profiles are present, create a second one for use by other versions.
    // This helps Firefox versions earlier than 35 avoid accidentally using the
    // unsuitable Developer Edition profile.
    let profileService = Cc["@mozilla.org/toolkit/profile-service;1"]
                         .getService(Ci.nsIToolkitProfileService);
    let profileCount = profileService.profileCount;
    if (profileCount == 1 && profileService.selectedProfile.name != "default") {
      let newProfile;
      try {
        newProfile = profileService.createProfile(null, "default");
        profileService.defaultProfile = newProfile;
        profileService.flush();
      } catch (e) {
        Cu.reportError("Could not create profile 'default': " + e);
      }
      if (newProfile) {
        // We don't want a default profile with Developer Edition settings, an
        // empty profile directory will do. The profile service of the other
        // Firefox will populate it with its own stuff.
        let newProfilePath = newProfile.rootDir.path;
        OS.File.removeDir(newProfilePath).then(() => {
          return OS.File.makeDir(newProfilePath);
        }).catch(e => {
          Cu.reportError("Could not empty profile 'default': " + e);
        });
      }
    }
  },

  _onQuitRequest: function BG__onQuitRequest(aCancelQuit, aQuitType) {
    // If user has already dismissed quit request, then do nothing
    if ((aCancelQuit instanceof Ci.nsISupportsPRBool) && aCancelQuit.data)
      return;

    // There are several cases where we won't show a dialog here:
    // 1. There is only 1 tab open in 1 window
    // 2. The session will be restored at startup, indicated by
    //    browser.startup.page == 3 or browser.sessionstore.resume_session_once == true
    // 3. browser.warnOnQuit == false
    // 4. The browser is currently in Private Browsing mode
    // 5. The browser will be restarted.
    //
    // Otherwise these are the conditions and the associated dialogs that will be shown:
    // 1. aQuitType == "lastwindow" or "quit" and browser.showQuitWarning == true
    //    - The quit dialog will be shown
    // 2. aQuitType == "lastwindow" && browser.tabs.warnOnClose == true
    //    - The "closing multiple tabs" dialog will be shown
    //
    // aQuitType == "lastwindow" is overloaded. "lastwindow" is used to indicate
    // "the last window is closing but we're not quitting (a non-browser window is open)"
    // and also "we're quitting by closing the last window".

    if (aQuitType == "restart")
      return;

    var windowcount = 0;
    var pagecount = 0;
    var browserEnum = Services.wm.getEnumerator("navigator:browser");
    let allWindowsPrivate = true;
    while (browserEnum.hasMoreElements()) {
      // XXXbz should we skip closed windows here?
      windowcount++;

      var browser = browserEnum.getNext();
      if (!PrivateBrowsingUtils.isWindowPrivate(browser))
        allWindowsPrivate = false;
      var tabbrowser = browser.ownerGlobal.gBrowser;
      if (tabbrowser)
        pagecount += tabbrowser.browsers.length - tabbrowser._numPinnedTabs;
    }

    this._saveSession = false;
    if (pagecount < 2)
      return;

    if (!aQuitType)
      aQuitType = "quit";

    // browser.warnOnQuit is a hidden global boolean to override all quit prompts
    // browser.showQuitWarning specifically covers quitting
    // browser.tabs.warnOnClose is the global "warn when closing multiple tabs" pref

    var sessionWillBeRestored = Services.prefs.getIntPref("browser.startup.page") == 3 ||
                                Services.prefs.getBoolPref("browser.sessionstore.resume_session_once");
    if (sessionWillBeRestored || !Services.prefs.getBoolPref("browser.warnOnQuit"))
      return;

    let win = Services.wm.getMostRecentWindow("navigator:browser");

    // On last window close or quit && showQuitWarning, we want to show the
    // quit warning.
    if (!Services.prefs.getBoolPref("browser.showQuitWarning")) {
      if (aQuitType == "lastwindow") {
        // If aQuitType is "lastwindow" and we aren't showing the quit warning,
        // we should show the window closing warning instead. warnAboutClosing
        // tabs checks browser.tabs.warnOnClose and returns if it's ok to close
        // the window. It doesn't actually close the window.
        aCancelQuit.data =
          !win.gBrowser.warnAboutClosingTabs(win.gBrowser.closingTabsEnum.ALL);
      }
      return;
    }

    let prompt = Services.prompt;
    let quitBundle = Services.strings.createBundle("chrome://browser/locale/quitDialog.properties");
    let appName = gBrandBundle.GetStringFromName("brandShortName");
    let quitDialogTitle = quitBundle.formatStringFromName("quitDialogTitle",
                                                          [appName], 1);
    let neverAskText = quitBundle.GetStringFromName("neverAsk2");
    let neverAsk = {value: false};

    let choice;
    if (allWindowsPrivate) {
      let text = quitBundle.formatStringFromName("messagePrivate", [appName], 1);
      let flags = prompt.BUTTON_TITLE_IS_STRING * prompt.BUTTON_POS_0 +
                  prompt.BUTTON_TITLE_IS_STRING * prompt.BUTTON_POS_1 +
                  prompt.BUTTON_POS_0_DEFAULT;
      choice = prompt.confirmEx(win, quitDialogTitle, text, flags,
                                quitBundle.GetStringFromName("quitTitle"),
                                quitBundle.GetStringFromName("cancelTitle"),
                                null,
                                neverAskText, neverAsk);

      // The order of the buttons differs between the prompt.confirmEx calls
      // here so we need to fix this for proper handling below.
      if (choice == 0) {
        choice = 2;
      }
    } else {
      let text = quitBundle.formatStringFromName(
        windowcount == 1 ? "messageNoWindows" : "message", [appName], 1);
      let flags = prompt.BUTTON_TITLE_IS_STRING * prompt.BUTTON_POS_0 +
                  prompt.BUTTON_TITLE_IS_STRING * prompt.BUTTON_POS_1 +
                  prompt.BUTTON_TITLE_IS_STRING * prompt.BUTTON_POS_2 +
                  prompt.BUTTON_POS_0_DEFAULT;
      choice = prompt.confirmEx(win, quitDialogTitle, text, flags,
                                quitBundle.GetStringFromName("saveTitle"),
                                quitBundle.GetStringFromName("cancelTitle"),
                                quitBundle.GetStringFromName("quitTitle"),
                                neverAskText, neverAsk);
    }

    switch (choice) {
    case 2: // Quit
      if (neverAsk.value)
        Services.prefs.setBoolPref("browser.showQuitWarning", false);
      break;
    case 1: // Cancel
      aCancelQuit.QueryInterface(Ci.nsISupportsPRBool);
      aCancelQuit.data = true;
      break;
    case 0: // Save & Quit
      this._saveSession = true;
      if (neverAsk.value) {
        // always save state when shutting down
        Services.prefs.setIntPref("browser.startup.page", 3);
      }
      break;
    }
  },

  _showUpdateNotification: function BG__showUpdateNotification() {
    Services.prefs.clearUserPref("app.update.postupdate");

    var um = Cc["@mozilla.org/updates/update-manager;1"].
             getService(Ci.nsIUpdateManager);
    try {
      // If the updates.xml file is deleted then getUpdateAt will throw.
      var update = um.getUpdateAt(0).QueryInterface(Ci.nsIPropertyBag);
    } catch (e) {
      // This should never happen.
      Cu.reportError("Unable to find update: " + e);
      return;
    }

    var actions = update.getProperty("actions");
    if (!actions || actions.includes("silent"))
      return;

    var appName = gBrandBundle.GetStringFromName("brandShortName");

    function getNotifyString(aPropData) {
      var propValue = update.getProperty(aPropData.propName);
      if (!propValue) {
        if (aPropData.prefName)
          propValue = Services.urlFormatter.formatURLPref(aPropData.prefName);
        else if (aPropData.stringParams)
          propValue = gBrowserBundle.formatStringFromName(aPropData.stringName,
                                                          aPropData.stringParams,
                                                          aPropData.stringParams.length);
        else
          propValue = gBrowserBundle.GetStringFromName(aPropData.stringName);
      }
      return propValue;
    }

    if (actions.includes("showNotification")) {
      let text = getNotifyString({propName: "notificationText",
                                  stringName: "puNotifyText",
                                  stringParams: [appName]});
      let url = getNotifyString({propName: "notificationURL",
                                 prefName: "startup.homepage_override_url"});
      let label = getNotifyString({propName: "notificationButtonLabel",
                                   stringName: "pu.notifyButton.label"});
      let key = getNotifyString({propName: "notificationButtonAccessKey",
                                 stringName: "pu.notifyButton.accesskey"});

      let win = RecentWindow.getMostRecentBrowserWindow();
      let notifyBox = win.document.getElementById("high-priority-global-notificationbox");

      let buttons = [
                      {
                        label,
                        accessKey: key,
                        popup:     null,
                        callback(aNotificationBar, aButton) {
                          win.openUILinkIn(url, "tab");
                        }
                      }
                    ];

      notifyBox.appendNotification(text, "post-update-notification",
                                   null, notifyBox.PRIORITY_INFO_LOW,
                                   buttons);
    }

    if (!actions.includes("showAlert"))
      return;

    let title = getNotifyString({propName: "alertTitle",
                                 stringName: "puAlertTitle",
                                 stringParams: [appName]});
    let text = getNotifyString({propName: "alertText",
                                stringName: "puAlertText",
                                stringParams: [appName]});
    let url = getNotifyString({propName: "alertURL",
                               prefName: "startup.homepage_override_url"});

    function clickCallback(subject, topic, data) {
      // This callback will be called twice but only once with this topic
      if (topic != "alertclickcallback")
        return;
      let win = RecentWindow.getMostRecentBrowserWindow();
      win.openUILinkIn(data, "tab");
    }

    try {
      // This will throw NS_ERROR_NOT_AVAILABLE if the notification cannot
      // be displayed per the idl.
      this.AlertsService.showAlertNotification(null, title, text,
                                          true, url, clickCallback);
    } catch (e) {
      Cu.reportError(e);
    }
  },

  /**
   * Initialize Places
   * - imports the bookmarks html file if bookmarks database is empty, try to
   *   restore bookmarks from a JSON backup if the backend indicates that the
   *   database was corrupt.
   *
   * These prefs can be set up by the frontend:
   *
   * WARNING: setting these preferences to true will overwite existing bookmarks
   *
   * - browser.places.importBookmarksHTML
   *   Set to true will import the bookmarks.html file from the profile folder.
   * - browser.places.smartBookmarksVersion
   *   Set during HTML import to indicate that Smart Bookmarks were created.
   *   Set to -1 to disable Smart Bookmarks creation.
   *   Set to 0 to restore current Smart Bookmarks.
   * - browser.bookmarks.restore_default_bookmarks
   *   Set to true by safe-mode dialog to indicate we must restore default
   *   bookmarks.
   */
  _initPlaces: function BG__initPlaces(aInitialMigrationPerformed) {
    // We must instantiate the history service since it will tell us if we
    // need to import or restore bookmarks due to first-run, corruption or
    // forced migration (due to a major schema change).
    // If the database is corrupt or has been newly created we should
    // import bookmarks.
    let dbStatus = PlacesUtils.history.databaseStatus;

    // Show a notification with a "more info" link for a locked places.sqlite.
    if (dbStatus == PlacesUtils.history.DATABASE_STATUS_LOCKED) {
      // Note: initPlaces should always happen when the first window is ready,
      // in any case, better safe than sorry.
      this._firstWindowReady.then(() => {
        this._showPlacesLockedNotificationBox();
        this._placesBrowserInitComplete = true;
        Services.obs.notifyObservers(null, "places-browser-init-complete");
      });
      return;
    }

    let importBookmarks = !aInitialMigrationPerformed &&
                          (dbStatus == PlacesUtils.history.DATABASE_STATUS_CREATE ||
                           dbStatus == PlacesUtils.history.DATABASE_STATUS_CORRUPT);

    // Check if user or an extension has required to import bookmarks.html
    let importBookmarksHTML = false;
    try {
      importBookmarksHTML =
        Services.prefs.getBoolPref("browser.places.importBookmarksHTML");
      if (importBookmarksHTML)
        importBookmarks = true;
    } catch (ex) {}

    // Support legacy bookmarks.html format for apps that depend on that format.
    let autoExportHTML = Services.prefs.getBoolPref("browser.bookmarks.autoExportHTML", false); // Do not export.
    if (autoExportHTML) {
      // Sqlite.jsm and Places shutdown happen at profile-before-change, thus,
      // to be on the safe side, this should run earlier.
      AsyncShutdown.profileChangeTeardown.addBlocker(
        "Places: export bookmarks.html",
        () => BookmarkHTMLUtils.exportToFile(BookmarkHTMLUtils.defaultPath));
    }

    (async () => {
      // Check if Safe Mode or the user has required to restore bookmarks from
      // default profile's bookmarks.html
      let restoreDefaultBookmarks = false;
      try {
        restoreDefaultBookmarks =
          Services.prefs.getBoolPref("browser.bookmarks.restore_default_bookmarks");
        if (restoreDefaultBookmarks) {
          // Ensure that we already have a bookmarks backup for today.
          await this._backupBookmarks();
          importBookmarks = true;
        }
      } catch (ex) {}

      // This may be reused later, check for "=== undefined" to see if it has
      // been populated already.
      let lastBackupFile;

      // If the user did not require to restore default bookmarks, or import
      // from bookmarks.html, we will try to restore from JSON
      if (importBookmarks && !restoreDefaultBookmarks && !importBookmarksHTML) {
        // get latest JSON backup
        lastBackupFile = await PlacesBackups.getMostRecentBackup();
        if (lastBackupFile) {
          // restore from JSON backup
          await BookmarkJSONUtils.importFromFile(lastBackupFile, true);
          importBookmarks = false;
        } else {
          // We have created a new database but we don't have any backup available
          importBookmarks = true;
          if (await OS.File.exists(BookmarkHTMLUtils.defaultPath)) {
            // If bookmarks.html is available in current profile import it...
            importBookmarksHTML = true;
          } else {
            // ...otherwise we will restore defaults
            restoreDefaultBookmarks = true;
          }
        }
      }

      // If bookmarks are not imported, then initialize smart bookmarks.  This
      // happens during a common startup.
      // Otherwise, if any kind of import runs, smart bookmarks creation should be
      // delayed till the import operations has finished.  Not doing so would
      // cause them to be overwritten by the newly imported bookmarks.
      if (!importBookmarks) {
        // Now apply distribution customized bookmarks.
        // This should always run after Places initialization.
        try {
          await this._distributionCustomizer.applyBookmarks();
          await this.ensurePlacesDefaultQueriesInitialized();
        } catch (e) {
          Cu.reportError(e);
        }
      } else {
        // An import operation is about to run.
        // Don't try to recreate smart bookmarks if autoExportHTML is true or
        // smart bookmarks are disabled.
        let smartBookmarksVersion = Services.prefs.getIntPref("browser.places.smartBookmarksVersion", 0);
        if (!autoExportHTML && smartBookmarksVersion != -1)
          Services.prefs.setIntPref("browser.places.smartBookmarksVersion", 0);

        let bookmarksUrl = null;
        if (restoreDefaultBookmarks) {
          // User wants to restore bookmarks.html file from default profile folder
          bookmarksUrl = "chrome://browser/locale/bookmarks.html";
        } else if (await OS.File.exists(BookmarkHTMLUtils.defaultPath)) {
          bookmarksUrl = OS.Path.toFileURI(BookmarkHTMLUtils.defaultPath);
        }

        if (bookmarksUrl) {
          // Import from bookmarks.html file.
          try {
            if (Services.policies.isAllowed("defaultBookmarks")) {
              await BookmarkHTMLUtils.importFromURL(bookmarksUrl, true);
            }
          } catch (e) {
            Cu.reportError("Bookmarks.html file could be corrupt. " + e);
          }
          try {
            // Now apply distribution customized bookmarks.
            // This should always run after Places initialization.
            await this._distributionCustomizer.applyBookmarks();
            // Ensure that smart bookmarks are created once the operation is
            // complete.
            await this.ensurePlacesDefaultQueriesInitialized();
          } catch (e) {
            Cu.reportError(e);
          }

        } else {
          Cu.reportError(new Error("Unable to find bookmarks.html file."));
        }

        // Reset preferences, so we won't try to import again at next run
        if (importBookmarksHTML)
          Services.prefs.setBoolPref("browser.places.importBookmarksHTML", false);
        if (restoreDefaultBookmarks)
          Services.prefs.setBoolPref("browser.bookmarks.restore_default_bookmarks",
                                     false);
      }

      // Initialize bookmark archiving on idle.
      if (!this._bookmarksBackupIdleTime) {
        this._bookmarksBackupIdleTime = BOOKMARKS_BACKUP_IDLE_TIME_SEC;

        // If there is no backup, or the last bookmarks backup is too old, use
        // a more aggressive idle observer.
        if (lastBackupFile === undefined)
          lastBackupFile = await PlacesBackups.getMostRecentBackup();
        if (!lastBackupFile) {
            this._bookmarksBackupIdleTime /= 2;
        } else {
          let lastBackupTime = PlacesBackups.getDateForFile(lastBackupFile);
          let profileLastUse = Services.appinfo.replacedLockTime || Date.now();

          // If there is a backup after the last profile usage date it's fine,
          // regardless its age.  Otherwise check how old is the last
          // available backup compared to that session.
          if (profileLastUse > lastBackupTime) {
            let backupAge = Math.round((profileLastUse - lastBackupTime) / 86400000);
            // Report the age of the last available backup.
            try {
              Services.telemetry
                      .getHistogramById("PLACES_BACKUPS_DAYSFROMLAST")
                      .add(backupAge);
            } catch (ex) {
              Cu.reportError(new Error("Unable to report telemetry."));
            }

            if (backupAge > BOOKMARKS_BACKUP_MAX_INTERVAL_DAYS)
              this._bookmarksBackupIdleTime /= 2;
          }
        }
        this._idleService.addIdleObserver(this, this._bookmarksBackupIdleTime);
      }

    })().catch(ex => {
      Cu.reportError(ex);
    }).then(() => {
      // NB: deliberately after the catch so that we always do this, even if
      // we threw halfway through initializing in the Task above.
      this._placesBrowserInitComplete = true;
      Services.obs.notifyObservers(null, "places-browser-init-complete");
    });
  },

  /**
   * If a backup for today doesn't exist, this creates one.
   */
  _backupBookmarks: function BG__backupBookmarks() {
    return (async function() {
      let lastBackupFile = await PlacesBackups.getMostRecentBackup();
      // Should backup bookmarks if there are no backups or the maximum
      // interval between backups elapsed.
      if (!lastBackupFile ||
          new Date() - PlacesBackups.getDateForFile(lastBackupFile) > BOOKMARKS_BACKUP_MIN_INTERVAL_DAYS * 86400000) {
        let maxBackups = Services.prefs.getIntPref("browser.bookmarks.max_backups");
        await PlacesBackups.create(maxBackups);
      }
    })();
  },

  /**
   * Show the notificationBox for a locked places database.
   */
  _showPlacesLockedNotificationBox: function BG__showPlacesLockedNotificationBox() {
    var applicationName = gBrandBundle.GetStringFromName("brandShortName");
    var placesBundle = Services.strings.createBundle("chrome://browser/locale/places/places.properties");
    var title = placesBundle.GetStringFromName("lockPrompt.title");
    var text = placesBundle.formatStringFromName("lockPrompt.text", [applicationName], 1);
    var buttonText = placesBundle.GetStringFromName("lockPromptInfoButton.label");
    var accessKey = placesBundle.GetStringFromName("lockPromptInfoButton.accessKey");

    var helpTopic = "places-locked";
    var url = Services.urlFormatter.formatURLPref("app.support.baseURL");
    url += helpTopic;

    var win = RecentWindow.getMostRecentBrowserWindow();

    var buttons = [
                    {
                      label:     buttonText,
                      accessKey,
                      popup:     null,
                      callback(aNotificationBar, aButton) {
                        win.openUILinkIn(url, "tab");
                      }
                    }
                  ];

    var notifyBox = win.gBrowser.getNotificationBox();
    var notification = notifyBox.appendNotification(text, title, null,
                                                    notifyBox.PRIORITY_CRITICAL_MEDIUM,
                                                    buttons);
    notification.persistence = -1; // Until user closes it
  },

  _showSyncStartedDoorhanger() {
    let bundle = Services.strings.createBundle("chrome://browser/locale/accounts.properties");
    let productName = gBrandBundle.GetStringFromName("brandShortName");
    let title = bundle.GetStringFromName("syncStartNotification.title");
    let body = bundle.formatStringFromName("syncStartNotification.body2",
                                            [productName], 1);

    let clickCallback = (subject, topic, data) => {
      if (topic != "alertclickcallback")
        return;
      this._openPreferences("sync", { origin: "doorhanger" });
    };
    this.AlertsService.showAlertNotification(null, title, body, true, null, clickCallback);
  },

  /**
   * Uncollapses PersonalToolbar if its collapsed status is not
   * persisted, and user customized it or changed default bookmarks.
   *
   * If the user does not have a persisted value for the toolbar's
   * "collapsed" attribute, try to determine whether it's customized.
   */
  _maybeToggleBookmarkToolbarVisibility() {
    const BROWSER_DOCURL = "chrome://browser/content/browser.xul";
    const NUM_TOOLBAR_BOOKMARKS_TO_UNHIDE = 3;
    let xulStore = Cc["@mozilla.org/xul/xulstore;1"].getService(Ci.nsIXULStore);

    if (!xulStore.hasValue(BROWSER_DOCURL, "PersonalToolbar", "collapsed")) {
      // We consider the toolbar customized if it has more than NUM_TOOLBAR_BOOKMARKS_TO_UNHIDE
      // children, or if it has a persisted currentset value.
      let toolbarIsCustomized = xulStore.hasValue(BROWSER_DOCURL, "PersonalToolbar", "currentset");
      let getToolbarFolderCount = () => {
        let toolbarFolder = PlacesUtils.getFolderContents(PlacesUtils.toolbarFolderId).root;
        let toolbarChildCount = toolbarFolder.childCount;
        toolbarFolder.containerOpen = false;
        return toolbarChildCount;
      };

      if (toolbarIsCustomized || getToolbarFolderCount() > NUM_TOOLBAR_BOOKMARKS_TO_UNHIDE) {
        xulStore.setValue(BROWSER_DOCURL, "PersonalToolbar", "collapsed", "false");
      }
    }
  },

  // eslint-disable-next-line complexity
  _migrateUI: function BG__migrateUI() {
    const UI_VERSION = 66;
    const BROWSER_DOCURL = "chrome://browser/content/browser.xul";

    let currentUIVersion;
    if (Services.prefs.prefHasUserValue("browser.migration.version")) {
      currentUIVersion = Services.prefs.getIntPref("browser.migration.version");
    } else {
      // This is a new profile, nothing to migrate.
      Services.prefs.setIntPref("browser.migration.version", UI_VERSION);

      try {
        // New profiles may have existing bookmarks (imported from another browser or
        // copied into the profile) and we want to show the bookmark toolbar for them
        // in some cases.
        this._maybeToggleBookmarkToolbarVisibility();
      } catch (ex) {
        Cu.reportError(ex);
      }
      return;
    }

    if (currentUIVersion >= UI_VERSION)
      return;

    let xulStore = Cc["@mozilla.org/xul/xulstore;1"].getService(Ci.nsIXULStore);

    if (currentUIVersion < 14) {
      // DOM Storage doesn't specially handle about: pages anymore.
      let path = OS.Path.join(OS.Constants.Path.profileDir,
                              "chromeappsstore.sqlite");
      OS.File.remove(path);
    }

    if (currentUIVersion < 16) {
      xulStore.removeValue(BROWSER_DOCURL, "nav-bar", "collapsed");
    }

    // Insert the bookmarks-menu-button into the nav-bar if it isn't already
    // there.
    if (currentUIVersion < 17) {
      let currentset = xulStore.getValue(BROWSER_DOCURL, "nav-bar", "currentset");
      // Need to migrate only if toolbar is customized.
      if (currentset) {
        if (!currentset.includes("bookmarks-menu-button")) {
          // The button isn't in the nav-bar, so let's look for an appropriate
          // place to put it.
          if (currentset.includes("bookmarks-menu-button-container")) {
            currentset = currentset.replace(/(^|,)bookmarks-menu-button-container($|,)/,
                                            "$1bookmarks-menu-button$2");
          } else if (currentset.includes("downloads-button")) {
            currentset = currentset.replace(/(^|,)downloads-button($|,)/,
                                            "$1bookmarks-menu-button,downloads-button$2");
          } else if (currentset.includes("home-button")) {
            currentset = currentset.replace(/(^|,)home-button($|,)/,
                                            "$1bookmarks-menu-button,home-button$2");
          } else {
            // Just append.
            currentset = currentset.replace(/(^|,)window-controls($|,)/,
                                            "$1bookmarks-menu-button,window-controls$2");
          }
          xulStore.setValue(BROWSER_DOCURL, "nav-bar", "currentset", currentset);
        }
      }
    }

    if (currentUIVersion < 19) {
      let detector = null;
      try {
        detector = Services.prefs.getComplexValue("intl.charset.detector",
                                                  Ci.nsIPrefLocalizedString).data;
      } catch (ex) {}
      if (!(detector == "" ||
            detector == "ja_parallel_state_machine" ||
            detector == "ruprob" ||
            detector == "ukprob")) {
        // If the encoding detector pref value is not reachable from the UI,
        // reset to default (varies by localization).
        Services.prefs.clearUserPref("intl.charset.detector");
      }
    }

    if (currentUIVersion < 20) {
      // Remove persisted collapsed state from TabsToolbar.
      xulStore.removeValue(BROWSER_DOCURL, "TabsToolbar", "collapsed");
    }

    if (currentUIVersion < 24) {
      // Reset homepage pref for users who have it set to start.mozilla.org
      // or google.com/firefox.
      const HOMEPAGE_PREF = "browser.startup.homepage";
      if (Services.prefs.prefHasUserValue(HOMEPAGE_PREF)) {
        const DEFAULT =
          Services.prefs.getDefaultBranch(HOMEPAGE_PREF)
                        .getComplexValue("", Ci.nsIPrefLocalizedString).data;
        let value = Services.prefs.getStringPref(HOMEPAGE_PREF);
        let updated =
          value.replace(/https?:\/\/start\.mozilla\.org[^|]*/i, DEFAULT)
               .replace(/https?:\/\/(www\.)?google\.[a-z.]+\/firefox[^|]*/i,
                        DEFAULT);
        if (updated != value) {
          if (updated == DEFAULT) {
            Services.prefs.clearUserPref(HOMEPAGE_PREF);
          } else {
            Services.prefs.setStringPref(HOMEPAGE_PREF, updated);
          }
        }
      }
    }

    if (currentUIVersion < 25) {
      // Make sure the doNotTrack value conforms to the conversion from
      // three-state to two-state. (This reverts a setting of "please track me"
      // to the default "don't say anything").
      try {
        if (Services.prefs.getBoolPref("privacy.donottrackheader.enabled") &&
            Services.prefs.getIntPref("privacy.donottrackheader.value") != 1) {
          Services.prefs.clearUserPref("privacy.donottrackheader.enabled");
          Services.prefs.clearUserPref("privacy.donottrackheader.value");
        }
      } catch (ex) {}
    }

    if (currentUIVersion < 26) {
      // Refactor urlbar suggestion preferences to make it extendable and
      // allow new suggestion types (e.g: search suggestions).
      let types = ["history", "bookmark", "openpage"];
      let defaultBehavior = Services.prefs.getIntPref("browser.urlbar.default.behavior", 0);
      try {
        let autocompleteEnabled = Services.prefs.getBoolPref("browser.urlbar.autocomplete.enabled");
        if (!autocompleteEnabled) {
          defaultBehavior = -1;
        }
      } catch (ex) {}

      // If the default behavior is:
      //    -1  - all new "...suggest.*" preferences will be false
      //     0  - all new "...suggest.*" preferences will use the default values
      //   > 0  - all new "...suggest.*" preferences will be inherited
      for (let type of types) {
        let prefValue = defaultBehavior == 0;
        if (defaultBehavior > 0) {
          prefValue = !!(defaultBehavior & Ci.mozIPlacesAutoComplete["BEHAVIOR_" + type.toUpperCase()]);
        }
        Services.prefs.setBoolPref("browser.urlbar.suggest." + type, prefValue);
      }

      // Typed behavior will be used only for results from history.
      if (defaultBehavior != -1 &&
          !!(defaultBehavior & Ci.mozIPlacesAutoComplete.BEHAVIOR_TYPED)) {
        Services.prefs.setBoolPref("browser.urlbar.suggest.history.onlyTyped", true);
      }
    }

    if (currentUIVersion < 27) {
      // Fix up document color use:
      const kOldColorPref = "browser.display.use_document_colors";
      if (Services.prefs.prefHasUserValue(kOldColorPref) &&
          !Services.prefs.getBoolPref(kOldColorPref)) {
        Services.prefs.setIntPref("browser.display.document_color_use", 2);
      }
    }

    if (currentUIVersion < 29) {
      let group = null;
      try {
        group = Services.prefs.getComplexValue("font.language.group",
                                               Ci.nsIPrefLocalizedString);
      } catch (ex) {}
      if (group &&
          ["tr", "x-baltic", "x-central-euro"].some(g => g == group.data)) {
        // Latin groups were consolidated.
        group.data = "x-western";
        Services.prefs.setComplexValue("font.language.group",
                                       Ci.nsIPrefLocalizedString, group);
      }
    }

    if (currentUIVersion < 30) {
      // Convert old devedition theme pref to lightweight theme storage
      let lightweightThemeSelected = false;
      let selectedThemeID = null;
      try {
        lightweightThemeSelected = Services.prefs.prefHasUserValue("lightweightThemes.selectedThemeID");
        selectedThemeID = Services.prefs.getCharPref("lightweightThemes.selectedThemeID");
      } catch (e) {}

      let defaultThemeSelected = false;
      try {
         defaultThemeSelected = Services.prefs.getCharPref("general.skins.selectedSkin") == "classic/1.0";
      } catch (e) {}

      // If we are on the devedition channel, the devedition theme is on by
      // default.  But we need to handle the case where they didn't want it
      // applied, and unapply the theme.
      let userChoseToNotUseDeveditionTheme =
        !defaultThemeSelected ||
        (lightweightThemeSelected && selectedThemeID != "[email protected]");

      if (userChoseToNotUseDeveditionTheme && selectedThemeID == "[email protected]") {
        Services.prefs.setCharPref("lightweightThemes.selectedThemeID", "");
      }

      Services.prefs.clearUserPref("browser.devedition.showCustomizeButton");
    }

    if (currentUIVersion < 31) {
      xulStore.removeValue(BROWSER_DOCURL, "bookmarks-menu-button", "class");
      xulStore.removeValue(BROWSER_DOCURL, "home-button", "class");
    }

    if (currentUIVersion < 36) {
      xulStore.removeValue("chrome://passwordmgr/content/passwordManager.xul",
                           "passwordCol",
                           "hidden");
    }

    if (currentUIVersion < 37) {
      Services.prefs.clearUserPref("browser.sessionstore.restore_on_demand");
    }

    if (currentUIVersion < 38) {
      LoginHelper.removeLegacySignonFiles();
    }

    if (currentUIVersion < 39) {
      // Remove the 'defaultset' value for all the toolbars
      let toolbars = ["nav-bar", "PersonalToolbar",
                      "TabsToolbar", "toolbar-menubar"];
      for (let toolbarId of toolbars) {
        xulStore.removeValue(BROWSER_DOCURL, toolbarId, "defaultset");
      }
    }

    if (currentUIVersion < 40) {
      const kOldSafeBrowsingPref = "browser.safebrowsing.enabled";
      // Default value is set to true, a user pref means that the pref was
      // set to false.
      if (Services.prefs.prefHasUserValue(kOldSafeBrowsingPref) &&
          !Services.prefs.getBoolPref(kOldSafeBrowsingPref)) {
        Services.prefs.setBoolPref("browser.safebrowsing.phishing.enabled",
                                   false);
        // Should just remove support for the pref entirely, even if it's
        // only in about:config
        Services.prefs.clearUserPref(kOldSafeBrowsingPref);
      }
    }

    if (currentUIVersion < 41) {
      const Preferences = ChromeUtils.import("resource://gre/modules/Preferences.jsm", {}).Preferences;
      Preferences.resetBranch("loop.");
    }

    if (currentUIVersion < 42) {
      let backupFile = Services.dirsvc.get("ProfD", Ci.nsIFile);
      backupFile.append("tabgroups-session-backup.json");
      OS.File.remove(backupFile.path, {ignoreAbsent: true}).catch(ex => Cu.reportError(ex));
    }

    if (currentUIVersion < 43) {
      let currentTheme = Services.prefs.getCharPref("lightweightThemes.selectedThemeID", "");
      if (currentTheme == "[email protected]") {
        let newTheme = Services.prefs.getCharPref("devtools.theme") == "dark" ?
          "[email protected]" : "[email protected]";
        Services.prefs.setCharPref("lightweightThemes.selectedThemeID", newTheme);
      }
    }

    if (currentUIVersion < 44) {
      // Merge the various cosmetic animation prefs into one. If any were set to
      // disable animations, we'll disabled cosmetic animations entirely.
      let animate = Services.prefs.getBoolPref("browser.tabs.animate", true) &&
                    Services.prefs.getBoolPref("browser.fullscreen.animate", true) &&
                    !Services.prefs.getBoolPref("alerts.disableSlidingEffect", false);

      Services.prefs.setBoolPref("toolkit.cosmeticAnimations.enabled", animate);

      Services.prefs.clearUserPref("browser.tabs.animate");
      Services.prefs.clearUserPref("browser.fullscreen.animate");
      Services.prefs.clearUserPref("alerts.disableSlidingEffect");
    }

    if (currentUIVersion < 45) {
      const LEGACY_PREF = "browser.shell.skipDefaultBrowserCheck";
      if (Services.prefs.prefHasUserValue(LEGACY_PREF)) {
        Services.prefs.setBoolPref("browser.shell.didSkipDefaultBrowserCheckOnFirstRun",
                                   !Services.prefs.getBoolPref(LEGACY_PREF));
        Services.prefs.clearUserPref(LEGACY_PREF);
      }
    }

    // Version 46 has been replaced by 47
    if (currentUIVersion < 47) {
      // Search suggestions are now on by default.
      // For privacy reasons, we want to respect previously made user's choice
      // regarding the feature, so if it's known reflect that choice into the
      // current pref.
      // Note that in case of downgrade/upgrade we won't guarantee anything.
      try {
        if (Services.prefs.prefHasUserValue("browser.urlbar.searchSuggestionsChoice")) {
          Services.prefs.setBoolPref(
            "browser.urlbar.suggest.searches",
            Services.prefs.getBoolPref("browser.urlbar.searchSuggestionsChoice")
          );
        } else if (Services.prefs.getBoolPref("browser.urlbar.userMadeSearchSuggestionsChoice")) {
          // If the user made a choice but searchSuggestionsChoice is not set,
          // something went wrong in the upgrade path. For example, due to a
          // now fixed bug, some profilespicking "no" at the opt-in bar and
          // upgrading in the same session wouldn't mirror the pref.
          // Users could also lack the mirrored pref due to skipping one version.
          // In this case just fallback to the safest side and disable suggestions.
          Services.prefs.setBoolPref("browser.urlbar.suggest.searches", false);
        }
      } catch (ex) {
        // A missing pref is not a fatal error.
      }
    }

    if (currentUIVersion < 49) {
      // Annotate that a user haven't seen any onboarding tour
      Services.prefs.setIntPref("browser.onboarding.seen-tourset-version", 0);
    }

    if (currentUIVersion < 50) {
      try {
        // Transform prefs related to old DevTools Console.
        // The following prefs might be missing when the old DevTools Console
        // front-end is removed.
        // See also: https://bugzilla.mozilla.org/show_bug.cgi?id=1381834
        if (Services.prefs.getBoolPref("devtools.webconsole.filter.networkinfo")) {
          Services.prefs.setBoolPref("devtools.webconsole.filter.net", true);
        }
        if (Services.prefs.getBoolPref("devtools.webconsole.filter.cssparser")) {
          Services.prefs.setBoolPref("devtools.webconsole.filter.css", true);
        }
      } catch (ex) {
        // It's ok if a pref is missing.
      }
    }

    if (currentUIVersion < 51) {
      // Switch to compact UI density if the user is using a formerly compact
      // dark or light theme.
      let currentTheme = Services.prefs.getCharPref("lightweightThemes.selectedThemeID", "");
      if (currentTheme == "[email protected]" ||
          currentTheme == "[email protected]") {
        Services.prefs.setIntPref("browser.uidensity", 1);
      }
    }

    if (currentUIVersion < 52) {
      // Keep old devtools log persistence behavior after splitting netmonitor and
      // webconsole prefs (bug 1307881).
      if (Services.prefs.getBoolPref("devtools.webconsole.persistlog", false)) {
        Services.prefs.setBoolPref("devtools.netmonitor.persistlog", true);
      }
    }

    // Update user customizations that will interfere with the Safe Browsing V2
    // to V4 migration (bug 1395419).
    if (currentUIVersion < 53) {
      const MALWARE_PREF = "urlclassifier.malwareTable";
      if (Services.prefs.prefHasUserValue(MALWARE_PREF)) {
        let malwareList = Services.prefs.getCharPref(MALWARE_PREF);
        if (malwareList.includes("goog-malware-shavar")) {
          malwareList.replace("goog-malware-shavar", "goog-malware-proto");
          Services.prefs.setCharPref(MALWARE_PREF, malwareList);
        }
      }
    }

    if (currentUIVersion < 54) {
      // Migrate browser.onboarding.hidden to browser.onboarding.state.
      if (Services.prefs.prefHasUserValue("browser.onboarding.hidden")) {
        let state = Services.prefs.getBoolPref("browser.onboarding.hidden") ? "watermark" : "default";
        Services.prefs.setStringPref("browser.onboarding.state", state);
        Services.prefs.clearUserPref("browser.onboarding.hidden");
      }
    }

    if (currentUIVersion < 55) {
      Services.prefs.clearUserPref("browser.customizemode.tip0.shown");
    }

    if (currentUIVersion < 56) {
      // Prior to the end of the Firefox 57 cycle, the sidebarcommand being present
      // or not was the only thing that distinguished whether the sidebar was open.
      // Now, the sidebarcommand always indicates the last opened sidebar, and we
      // correctly persist the checked attribute to indicate whether or not the
      // sidebar was open. We should set the checked attribute in case it wasn't:
      if (xulStore.getValue(BROWSER_DOCURL, "sidebar-box", "sidebarcommand")) {
        xulStore.setValue(BROWSER_DOCURL, "sidebar-box", "checked", "true");
      }
    }

    if (currentUIVersion < 57) {
      // Beginning Firefox 57, the theme accent color is shown as highlight
      // on top of tabs. This didn't look too good with the "A Web Browser Renaissance"
      // theme, so we're changing its accent color.
      let lwthemePrefs = Services.prefs.getBranch("lightweightThemes.");
      if (lwthemePrefs.prefHasUserValue("usedThemes")) {
        try {
          let usedThemes = lwthemePrefs.getStringPref("usedThemes");
          usedThemes = JSON.parse(usedThemes);
          let renaissanceTheme = usedThemes.find(theme => theme.id == "recommended-1");
          if (renaissanceTheme) {
            renaissanceTheme.accentcolor = "#834d29";
            lwthemePrefs.setStringPref("usedThemes", JSON.stringify(usedThemes));
          }
        } catch (e) { /* Don't panic if this pref isn't what we expect it to be. */ }
      }
    }

    if (currentUIVersion < 58) {
      // With Firefox 57, we are doing a one time reset of the geo prefs due to bug 1413652
      Services.prefs.clearUserPref("browser.search.countryCode");
      Services.prefs.clearUserPref("browser.search.region");
      Services.prefs.clearUserPref("browser.search.isUS");
    }

    if (currentUIVersion < 59) {
      let searchInitializedPromise = new Promise(resolve => {
        if (Services.search.isInitialized) {
          resolve();
        }
        const SEARCH_SERVICE_TOPIC = "browser-search-service";
        Services.obs.addObserver(function observer(subject, topic, data) {
          if (data != "init-complete") {
            return;
          }
          Services.obs.removeObserver(observer, SEARCH_SERVICE_TOPIC);
          resolve();
        }, SEARCH_SERVICE_TOPIC);
      });
      searchInitializedPromise.then(() => {
        let currentEngine = Services.search.currentEngine.wrappedJSObject;
        // Only reset the current engine if it wasn't set by a WebExtension
        // and it is not one of the default engines.
        // If the original default is not a default, the user has a weird
        // configuration probably involving langpacks, it's not worth
        // attempting to reset their settings.
        if (currentEngine._extensionID || currentEngine._isDefault ||
            !Services.search.originalDefaultEngine.wrappedJSObject._isDefault)
          return;

        if (currentEngine._loadPath.startsWith("[https]")) {
          Services.prefs.setCharPref("browser.search.reset.status", "pending");
        } else {
          Services.search.resetToOriginalDefaultEngine();
          Services.prefs.setCharPref("browser.search.reset.status", "silent");
        }
      });

      // Migrate the old requested locales prefs to use the new model
      const SELECTED_LOCALE_PREF = "general.useragent.locale";
      const MATCHOS_LOCALE_PREF = "intl.locale.matchOS";

      if (Services.prefs.prefHasUserValue(MATCHOS_LOCALE_PREF) ||
          Services.prefs.prefHasUserValue(SELECTED_LOCALE_PREF)) {
        if (Services.prefs.getBoolPref(MATCHOS_LOCALE_PREF, false)) {
          Services.locale.setRequestedLocales([]);
        } else {
          let locale = Services.prefs.getComplexValue(SELECTED_LOCALE_PREF,
            Ci.nsIPrefLocalizedString);
          if (locale) {
            try {
              Services.locale.setRequestedLocales([locale.data]);
            } catch (e) { /* Don't panic if the value is not a valid locale code. */ }
          }
        }
        Services.prefs.clearUserPref(SELECTED_LOCALE_PREF);
        Services.prefs.clearUserPref(MATCHOS_LOCALE_PREF);
      }
    }

    if (currentUIVersion < 60) {
      // This version is superseded by version 66.  See bug 1444965.
    }

    if (currentUIVersion < 61) {
      // Remove persisted toolbarset from navigator toolbox
      xulStore.removeValue(BROWSER_DOCURL, "navigator-toolbox", "toolbarset");
    }

    if (currentUIVersion < 62) {
      // Remove iconsize and mode from all the toolbars
      let toolbars = ["navigator-toolbox", "nav-bar", "PersonalToolbar",
                      "TabsToolbar", "toolbar-menubar"];
      for (let resourceName of ["mode", "iconsize"]) {
        for (let toolbarId of toolbars) {
          xulStore.removeValue(BROWSER_DOCURL, toolbarId, resourceName);
        }
      }
    }

    if (currentUIVersion < 64) {
      OS.File.remove(OS.Path.join(OS.Constants.Path.profileDir,
                                  "directoryLinks.json"), {ignoreAbsent: true});
    }

    if (currentUIVersion < 65 &&
        Services.prefs.getCharPref("general.config.filename", "") == "dsengine.cfg") {
      let searchInitializedPromise = new Promise(resolve => {
        if (Services.search.isInitialized) {
          resolve();
        }
        const SEARCH_SERVICE_TOPIC = "browser-search-service";
        Services.obs.addObserver(function observer(subject, topic, data) {
          if (data != "init-complete") {
            return;
          }
          Services.obs.removeObserver(observer, SEARCH_SERVICE_TOPIC);
          resolve();
        }, SEARCH_SERVICE_TOPIC);
      });
      searchInitializedPromise.then(() => {
        let engineNames = ["Bing Search Engine",
                           "Yahoo! Search Engine",
                           "Yandex Search Engine"];
        for (let engineName of engineNames) {
          let engine = Services.search.getEngineByName(engineName);
          if (engine) {
            Services.search.removeEngine(engine);
          }
        }
      });
    }

    if (currentUIVersion < 66) {
      // Set whether search suggestions or history/bookmarks results come first
      // in the urlbar results, and uninstall a related Shield study.
      this._migrateMatchBucketsPrefForUI66();
    }

    // Update the migration version.
    Services.prefs.setIntPref("browser.migration.version", UI_VERSION);
  },

  _checkForDefaultBrowser() {
    // Perform default browser checking.
    if (!ShellService) {
      return;
    }

    let shouldCheck = AppConstants.DEBUG ? false :
                                           ShellService.shouldCheckDefaultBrowser;

    const skipDefaultBrowserCheck =
      Services.prefs.getBoolPref("browser.shell.skipDefaultBrowserCheckOnFirstRun") &&
      !Services.prefs.getBoolPref("browser.shell.didSkipDefaultBrowserCheckOnFirstRun");

    const usePromptLimit = !AppConstants.RELEASE_OR_BETA;
    let promptCount =
      usePromptLimit ? Services.prefs.getIntPref("browser.shell.defaultBrowserCheckCount") : 0;

    let willRecoverSession = false;
    try {
      let ss = Cc["@mozilla.org/browser/sessionstartup;1"].
               getService(Ci.nsISessionStartup);
      willRecoverSession =
        (ss.sessionType == Ci.nsISessionStartup.RECOVER_SESSION);
    } catch (ex) { /* never mind; suppose SessionStore is broken */ }

    // startup check, check all assoc
    let isDefault = false;
    let isDefaultError = false;
    try {
      isDefault = ShellService.isDefaultBrowser(true, false);
    } catch (ex) {
      isDefaultError = true;
    }

    if (isDefault) {
      let now = (Math.floor(Date.now() / 1000)).toString();
      Services.prefs.setCharPref("browser.shell.mostRecentDateSetAsDefault", now);
    }

    let willPrompt = shouldCheck && !isDefault && !willRecoverSession;

    // Skip the "Set Default Browser" check during first-run or after the
    // browser has been run a few times.
    if (willPrompt) {
      if (skipDefaultBrowserCheck) {
        Services.prefs.setBoolPref("browser.shell.didSkipDefaultBrowserCheckOnFirstRun", true);
        willPrompt = false;
      } else {
        promptCount++;
      }
      if (usePromptLimit && promptCount > 3) {
        willPrompt = false;
      }
    }

    if (usePromptLimit && willPrompt) {
      Services.prefs.setIntPref("browser.shell.defaultBrowserCheckCount", promptCount);
    }

    try {
      // Report default browser status on startup to telemetry
      // so we can track whether we are the default.
      Services.telemetry.getHistogramById("BROWSER_IS_USER_DEFAULT")
                        .add(isDefault);
      Services.telemetry.getHistogramById("BROWSER_IS_USER_DEFAULT_ERROR")
                        .add(isDefaultError);
      Services.telemetry.getHistogramById("BROWSER_SET_DEFAULT_ALWAYS_CHECK")
                        .add(shouldCheck);
      Services.telemetry.getHistogramById("BROWSER_SET_DEFAULT_DIALOG_PROMPT_RAWCOUNT")
                        .add(promptCount);
    } catch (ex) { /* Don't break the default prompt if telemetry is broken. */ }

    if (willPrompt) {
      DefaultBrowserCheck.prompt(RecentWindow.getMostRecentBrowserWindow());
    }
  },

  async _migrateMatchBucketsPrefForUI66() {
    // This does two related things.
    //
    // (1) Profiles created on or after Firefox 57's release date were eligible
    // for a Shield study that changed the browser.urlbar.matchBuckets pref in
    // order to show search suggestions above history/bookmarks in the urlbar
    // popup.  This uninstalls that study.  (It's actually slightly more
    // complex.  The study set the pref to several possible values, but the
    // overwhelming number of profiles in the study got search suggestions
    // first, followed by history/bookmarks.)
    //
    // (2) This also ensures that (a) new users see search suggestions above
    // history/bookmarks, thereby effectively making the study permanent, and
    // (b) old users (including those in the study) continue to see whatever
    // they were seeing before.  This works together with UnifiedComplete.js.
    // By default, the browser.urlbar.matchBuckets pref does not exist, and
    // UnifiedComplete.js internally hardcodes a default value for it.  Before
    // Firefox 60, the hardcoded default was to show history/bookmarks first.
    // After 60, it's to show search suggestions first.

    // Wait for Shield init to complete.
    await new Promise(resolve => {
      if (this._shieldInitComplete) {
        resolve();
        return;
      }
      let topic = "shield-init-complete";
      Services.obs.addObserver(function obs() {
        Services.obs.removeObserver(obs, topic);
        resolve();
      }, topic);
    });

    // Now get the pref's value.  If the study is active, the value will have
    // just been set (on the default branch) as part of Shield's init.  The pref
    // should not exist otherwise (normally).
    let prefName = "browser.urlbar.matchBuckets";
    let prefValue = Services.prefs.getCharPref(prefName, "");

    // Get the study (aka experiment).  It may not be installed.
    let experiment = null;
    let experimentName = "pref-flip-search-composition-57-release-1413565";
    let {PreferenceExperiments} =
      ChromeUtils.import("resource://normandy/lib/PreferenceExperiments.jsm", {});
    try {
      experiment = await PreferenceExperiments.get(experimentName);
    } catch (e) {}

    // Uninstall the study, resetting the pref to its state before the study.
    if (experiment && !experiment.expired) {
      await PreferenceExperiments.stop(experimentName, {
        resetValue: true,
        reason: "external:search-ui-migration",
      });
    }

    // At this point, normally the pref should not exist.  If it does, then it
    // either has a user value, or something unexpectedly set its value on the
    // default branch.  Either way, preserve that value.
    if (Services.prefs.getCharPref(prefName, "")) {
      return;
    }

    // The new default is "suggestion:4,general:5" (show search suggestions
    // before history/bookmarks), but we implement that by leaving the pref
    // undefined, and UnifiedComplete.js hardcodes that value internally.  So if
    // the pref was "suggestion:4,general:5" (modulo whitespace), we're done.
    if (prefValue) {
      let buckets = PlacesUtils.convertMatchBucketsStringToArray(prefValue);
      if (ObjectUtils.deepEqual(buckets, [["suggestion", 4], ["general", 5]])) {
        return;
      }
    }

    // Set the pref on the user branch.  If the pref had a value, then preserve
    // it.  Otherwise, set the previous default value, which was to show history
    // and bookmarks before search suggestions.
    prefValue = prefValue || "general:5,suggestion:Infinity";
    Services.prefs.setCharPref(prefName, prefValue);
  },

  async ensurePlacesDefaultQueriesInitialized() {
    // This is the current smart bookmarks version, it must be increased every
    // time they change.
    // When adding a new smart bookmark below, its newInVersion property must
    // be set to the version it has been added in.  We will compare its value
    // to users' smartBookmarksVersion and add new smart bookmarks without
    // recreating old deleted ones.
    const SMART_BOOKMARKS_VERSION = 8;
    const SMART_BOOKMARKS_ANNO = "Places/SmartBookmark";
    const SMART_BOOKMARKS_PREF = "browser.places.smartBookmarksVersion";

    // TODO bug 399268: should this be a pref?
    const MAX_RESULTS = 10;

    // Get current smart bookmarks version.  If not set, create them.
    let smartBookmarksCurrentVersion = Services.prefs.getIntPref(SMART_BOOKMARKS_PREF, 0);

    // If version is current, or smart bookmarks are disabled, bail out.
    if (smartBookmarksCurrentVersion == -1 ||
        !Services.policies.isAllowed("defaultBookmarks") ||
        smartBookmarksCurrentVersion >= SMART_BOOKMARKS_VERSION) {
      return;
    }

    try {
      let menuIndex = 0;
      let toolbarIndex = 0;
      let bundle = Services.strings.createBundle("chrome://browser/locale/places/places.properties");
      let queryOptions = Ci.nsINavHistoryQueryOptions;

      let smartBookmarks = {
        MostVisited: {
          title: bundle.GetStringFromName("mostVisitedTitle"),
          url: "place:sort=" + queryOptions.SORT_BY_VISITCOUNT_DESCENDING +
                    "&maxResults=" + MAX_RESULTS,
          parentGuid: PlacesUtils.bookmarks.toolbarGuid,
          newInVersion: 1
        },
        RecentTags: {
          title: bundle.GetStringFromName("recentTagsTitle"),
          url: "place:type=" + queryOptions.RESULTS_AS_TAG_QUERY +
                    "&sort=" + queryOptions.SORT_BY_LASTMODIFIED_DESCENDING +
                    "&maxResults=" + MAX_RESULTS,
          parentGuid: PlacesUtils.bookmarks.menuGuid,
          newInVersion: 1
        },
      };

      // Set current guid, parentGuid and index of existing Smart Bookmarks.
      // We will use those to create a new version of the bookmark at the same
      // position.
      let smartBookmarkItemIds = PlacesUtils.annotations.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO);
      for (let itemId of smartBookmarkItemIds) {
        let queryId = PlacesUtils.annotations.getItemAnnotation(itemId, SMART_BOOKMARKS_ANNO);
        if (queryId in smartBookmarks) {
          // Known smart bookmark.
          let smartBookmark = smartBookmarks[queryId];
          smartBookmark.guid = await PlacesUtils.promiseItemGuid(itemId);

          if (!smartBookmark.url) {
            await PlacesUtils.bookmarks.remove(smartBookmark.guid);
            continue;
          }

          let bm = await PlacesUtils.bookmarks.fetch(smartBookmark.guid);
          smartBookmark.parentGuid = bm.parentGuid;
          smartBookmark.index = bm.index;
        } else {
          // We don't remove old Smart Bookmarks because user could still
          // find them useful, or could have personalized them.
          // Instead we remove the Smart Bookmark annotation.
          PlacesUtils.annotations.removeItemAnnotation(itemId, SMART_BOOKMARKS_ANNO);
        }
      }

      for (let queryId of Object.keys(smartBookmarks)) {
        let smartBookmark = smartBookmarks[queryId];

        // We update or create only changed or new smart bookmarks.
        // Also we respect user choices, so we won't try to create a smart
        // bookmark if it has been removed.
        if (smartBookmarksCurrentVersion > 0 &&
            smartBookmark.newInVersion <= smartBookmarksCurrentVersion &&
            !smartBookmark.guid || !smartBookmark.url)
          continue;

        // Remove old version of the smart bookmark if it exists, since it
        // will be replaced in place.
        if (smartBookmark.guid) {
          await PlacesUtils.bookmarks.remove(smartBookmark.guid);
        }

        // Create the new smart bookmark and store its updated guid.
        if (!("index" in smartBookmark)) {
          if (smartBookmark.parentGuid == PlacesUtils.bookmarks.toolbarGuid)
            smartBookmark.index = toolbarIndex++;
          else if (smartBookmark.parentGuid == PlacesUtils.bookmarks.menuGuid)
            smartBookmark.index = menuIndex++;
        }
        smartBookmark = await PlacesUtils.bookmarks.insert(smartBookmark);
        let itemId = await PlacesUtils.promiseItemId(smartBookmark.guid);
        PlacesUtils.annotations.setItemAnnotation(itemId,
                                                  SMART_BOOKMARKS_ANNO,
                                                  queryId, 0,
                                                  PlacesUtils.annotations.EXPIRE_NEVER);
      }

      // If we are creating all Smart Bookmarks from ground up, add a
      // separator below them in the bookmarks menu.
      if (smartBookmarksCurrentVersion == 0 &&
          smartBookmarkItemIds.length == 0) {
        let bm = await PlacesUtils.bookmarks.fetch({ parentGuid: PlacesUtils.bookmarks.menuGuid,
                                                     index: menuIndex });
        // Don't add a separator if the menu was empty or there is one already.
        if (bm && bm.type != PlacesUtils.bookmarks.TYPE_SEPARATOR) {
          await PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_SEPARATOR,
                                               parentGuid: PlacesUtils.bookmarks.menuGuid,
                                               index: menuIndex });
        }
      }
    } catch (ex) {
      Cu.reportError(ex);
    } finally {
      Services.prefs.setIntPref(SMART_BOOKMARKS_PREF, SMART_BOOKMARKS_VERSION);
      Services.prefs.savePrefFile(null);
    }
  },

  /**
   * Open preferences even if there are no open windows.
   */
  _openPreferences(...args) {
    if (Services.appShell.hiddenDOMWindow.openPreferences) {
      Services.appShell.hiddenDOMWindow.openPreferences(...args);
      return;
    }

    let chromeWindow = RecentWindow.getMostRecentBrowserWindow();
    chromeWindow.openPreferences(...args);
  },

  _openURLInNewWindow(url) {
    let urlString = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
    urlString.data = url;
    return new Promise(resolve => {
      let win = Services.ww.openWindow(null, Services.prefs.getCharPref("browser.chromeURL"),
                                       "_blank", "chrome,all,dialog=no", urlString);
      win.addEventListener("load", () => { resolve(win); }, {once: true});
    });
  },

  /**
   * Called as an observer when Sync's "display URIs" notification is fired.
   *
   * We open the received URIs in background tabs.
   */
  async _onDisplaySyncURIs(data) {
    try {
      // The payload is wrapped weirdly because of how Sync does notifications.
      const URIs = data.wrappedJSObject.object;

      // win can be null, but it's ok, we'll assign it later in openTab()
      let win = RecentWindow.getMostRecentBrowserWindow({private: false});

      const openTab = async (URI) => {
        let tab;
        if (!win) {
          win = await this._openURLInNewWindow(URI.uri);
          let tabs = win.gBrowser.tabs;
          tab = tabs[tabs.length - 1];
        } else {
          tab = win.gBrowser.addTab(URI.uri);
        }
        tab.setAttribute("attention", true);
        return tab;
      };

      const firstTab = await openTab(URIs[0]);
      await Promise.all(URIs.slice(1).map(URI => openTab(URI)));

      let title, body;
      const deviceName = Weave.Service.clientsEngine.getClientName(URIs[0].clientId);
      const bundle = Services.strings.createBundle("chrome://browser/locale/accounts.properties");
      if (URIs.length == 1) {
        // Due to bug 1305895, tabs from iOS may not have device information, so
        // we have separate strings to handle those cases. (See Also
        // unnamedTabsArrivingNotificationNoDevice.body below)
        if (deviceName) {
          title = bundle.formatStringFromName("tabArrivingNotificationWithDevice.title", [deviceName], 1);
        } else {
          title = bundle.GetStringFromName("tabArrivingNotification.title");
        }
        // Use the page URL as the body. We strip the fragment and query (after
        // the `?` and `#` respectively) to reduce size, and also format it the
        // same way that the url bar would.
        body = URIs[0].uri.replace(/([?#]).*$/, "$1");
        let wasTruncated = body.length < URIs[0].uri.length;
        if (win.gURLBar) {
          body = win.gURLBar.trimValue(body);
        }
        if (wasTruncated) {
          body = bundle.formatStringFromName("singleTabArrivingWithTruncatedURL.body", [body], 1);
        }
      } else {
        title = bundle.GetStringFromName("multipleTabsArrivingNotification.title");
        const allSameDevice = URIs.every(URI => URI.clientId == URIs[0].clientId);
        const unknownDevice = allSameDevice && !deviceName;
        let tabArrivingBody;
        if (unknownDevice) {
          tabArrivingBody = "unnamedTabsArrivingNotificationNoDevice.body";
        } else if (allSameDevice) {
          tabArrivingBody = "unnamedTabsArrivingNotification2.body";
        } else {
          tabArrivingBody = "unnamedTabsArrivingNotificationMultiple2.body";
        }

        body = bundle.GetStringFromName(tabArrivingBody);
        body = PluralForm.get(URIs.length, body);
        body = body.replace("#1", URIs.length);
        body = body.replace("#2", deviceName);
      }

      const clickCallback = (obsSubject, obsTopic, obsData) => {
        if (obsTopic == "alertclickcallback") {
          win.gBrowser.selectedTab = firstTab;
        }
      };

      // Specify an icon because on Windows no icon is shown at the moment
      let imageURL;
      if (AppConstants.platform == "win") {
        imageURL = "chrome://branding/content/icon64.png";
      }
      this.AlertsService.showAlertNotification(imageURL, title, body, true, null, clickCallback);
    } catch (ex) {
      Cu.reportError("Error displaying tab(s) received by Sync: " + ex);
    }
  },

  async _onVerifyLoginNotification({body, title, url}) {
    let tab;
    let imageURL;
    if (AppConstants.platform == "win") {
      imageURL = "chrome://branding/content/icon64.png";
    }
    let win = RecentWindow.getMostRecentBrowserWindow({private: false});
    if (!win) {
      win = await this._openURLInNewWindow(url);
      let tabs = win.gBrowser.tabs;
      tab = tabs[tabs.length - 1];
    } else {
      tab = win.gBrowser.addTab(url);
    }
    tab.setAttribute("attention", true);
    let clickCallback = (subject, topic, data) => {
      if (topic != "alertclickcallback")
        return;
      win.gBrowser.selectedTab = tab;
    };

    try {
      this.AlertsService.showAlertNotification(imageURL, title, body, true, null, clickCallback);
    } catch (ex) {
      Cu.reportError("Error notifying of a verify login event: " + ex);
    }
  },

  _onDeviceConnected(deviceName) {
    let accountsBundle = Services.strings.createBundle(
      "chrome://browser/locale/accounts.properties"
    );
    let title = accountsBundle.GetStringFromName("deviceConnectedTitle");
    let body = accountsBundle.formatStringFromName("deviceConnectedBody" +
                                                   (deviceName ? "" : ".noDeviceName"),
                                                   [deviceName], 1);

    let clickCallback = async (subject, topic, data) => {
      if (topic != "alertclickcallback")
        return;
      let url = await FxAccounts.config.promiseManageDevicesURI("device-connected-notification");
      let win = RecentWindow.getMostRecentBrowserWindow({private: false});
      if (!win) {
        this._openURLInNewWindow(url);
      } else {
        win.gBrowser.addTab(url);
      }
    };

    try {
      this.AlertsService.showAlertNotification(null, title, body, true, null, clickCallback);
    } catch (ex) {
      Cu.reportError("Error notifying of a new Sync device: " + ex);
    }
  },

  _onDeviceDisconnected() {
    let bundle = Services.strings.createBundle("chrome://browser/locale/accounts.properties");
    let title = bundle.GetStringFromName("deviceDisconnectedNotification.title");
    let body = bundle.GetStringFromName("deviceDisconnectedNotification.body");

    let clickCallback = (subject, topic, data) => {
      if (topic != "alertclickcallback")
        return;
      this._openPreferences("sync", { origin: "devDisconnectedAlert"});
    };
    this.AlertsService.showAlertNotification(null, title, body, true, null, clickCallback);
  },

  _handleFlashHang() {
    ++this._flashHangCount;
    if (this._flashHangCount < 2) {
      return;
    }
    // protected mode only applies to win32
    if (Services.appinfo.XPCOMABI != "x86-msvc") {
      return;
    }

    if (Services.prefs.getBoolPref("dom.ipc.plugins.flash.disable-protected-mode")) {
      return;
    }
    if (!Services.prefs.getBoolPref("browser.flash-protected-mode-flip.enable")) {
      return;
    }
    if (Services.prefs.getBoolPref("browser.flash-protected-mode-flip.done")) {
      return;
    }
    Services.prefs.setBoolPref("dom.ipc.plugins.flash.disable-protected-mode", true);
    Services.prefs.setBoolPref("browser.flash-protected-mode-flip.done", true);

    let win = RecentWindow.getMostRecentBrowserWindow();
    if (!win) {
      return;
    }
    let productName = gBrandBundle.GetStringFromName("brandShortName");
    let message = win.gNavigatorBundle.
      getFormattedString("flashHang.message", [productName]);
    let buttons = [{
      label: win.gNavigatorBundle.getString("flashHang.helpButton.label"),
      accessKey: win.gNavigatorBundle.getString("flashHang.helpButton.accesskey"),
      callback() {
        win.openUILinkIn("https://support.mozilla.org/kb/flash-protected-mode-autodisabled", "tab");
      }
    }];
    let nb = win.document.getElementById("global-notificationbox");
    nb.appendNotification(message, "flash-hang", null,
                          nb.PRIORITY_INFO_MEDIUM, buttons);
  },

  _updateFxaBadges() {
    let state = UIState.get();
    if (state.status == UIState.STATUS_LOGIN_FAILED ||
        state.status == UIState.STATUS_NOT_VERIFIED) {
      AppMenuNotifications.showBadgeOnlyNotification("fxa-needs-authentication");
    } else {
      AppMenuNotifications.removeNotification("fxa-needs-authentication");
    }
  },

  // for XPCOM
  classID:          Components.ID("{eab9012e-5f74-4cbc-b2b5-a590235513cc}"),

  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                         Ci.nsISupportsWeakReference]),

  _xpcom_factory: XPCOMUtils.generateSingletonFactory(BrowserGlue),
};

/**
 * ContentPermissionIntegration is responsible for showing the user
 * simple permission prompts when content requests additional
 * capabilities.
 *
 * While there are some built-in permission prompts, createPermissionPrompt
 * can also be overridden by system add-ons or tests to provide new ones.
 *
 * This override ability is provided by Integration.jsm. See
 * PermissionUI.jsm for an example of how to provide a new prompt
 * from an add-on.
 */
const ContentPermissionIntegration = {
  /**
   * Creates a PermissionPrompt for a given permission type and
   * nsIContentPermissionRequest.
   *
   * @param {string} type
   *        The type of the permission request from content. This normally
   *        matches the "type" field of an nsIContentPermissionType, but it
   *        can be something else if the permission does not use the
   *        nsIContentPermissionRequest model. Note that this type might also
   *        be different from the permission key used in the permissions
   *        database.
   *        Example: "geolocation"
   * @param {nsIContentPermissionRequest} request
   *        The request for a permission from content.
   * @return {PermissionPrompt} (see PermissionUI.jsm),
   *         or undefined if the type cannot be handled.
   */
  createPermissionPrompt(type, request) {
    switch (type) {
      case "geolocation": {
        return new PermissionUI.GeolocationPermissionPrompt(request);
      }
      case "desktop-notification": {
        return new PermissionUI.DesktopNotificationPermissionPrompt(request);
      }
      case "persistent-storage": {
        if (Services.prefs.getBoolPref("browser.storageManager.enabled")) {
          return new PermissionUI.PersistentStoragePermissionPrompt(request);
        }
      }
      case "midi": {
        return new PermissionUI.MIDIPermissionPrompt(request);
      }
    }
    return undefined;
  },
};

function ContentPermissionPrompt() {}

ContentPermissionPrompt.prototype = {
  classID:          Components.ID("{d8903bf6-68d5-4e97-bcd1-e4d3012f721a}"),

  QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionPrompt]),

  /**
   * This implementation of nsIContentPermissionPrompt.prompt ensures
   * that there's only one nsIContentPermissionType in the request,
   * and that it's of type nsIContentPermissionType. Failing to
   * satisfy either of these conditions will result in this method
   * throwing NS_ERRORs. If the combined ContentPermissionIntegration
   * cannot construct a prompt for this particular request, an
   * NS_ERROR_FAILURE will be thrown.
   *
   * Any time an error is thrown, the nsIContentPermissionRequest is
   * cancelled automatically.
   *
   * @param {nsIContentPermissionRequest} request
   *        The request that we're to show a prompt for.
   */
  prompt(request) {
    try {
      // Only allow exactly one permission request here.
      let types = request.types.QueryInterface(Ci.nsIArray);
      if (types.length != 1) {
        throw Components.Exception(
          "Expected an nsIContentPermissionRequest with only 1 type.",
          Cr.NS_ERROR_UNEXPECTED);
      }

      let type = types.queryElementAt(0, Ci.nsIContentPermissionType).type;
      let combinedIntegration =
        Integration.contentPermission.getCombined(ContentPermissionIntegration);

      let permissionPrompt =
        combinedIntegration.createPermissionPrompt(type, request);
      if (!permissionPrompt) {
        throw Components.Exception(
          `Failed to handle permission of type ${type}`,
          Cr.NS_ERROR_FAILURE);
      }

      permissionPrompt.prompt();

      let schemeHistogram = Services.telemetry.getKeyedHistogramById("PERMISSION_REQUEST_ORIGIN_SCHEME");
      let scheme = 0;
      // URI is null for system principals.
      if (request.principal.URI) {
        switch (request.principal.URI.scheme) {
          case "http":
            scheme = 1;
            break;
          case "https":
            scheme = 2;
            break;
        }
      }
      schemeHistogram.add(type, scheme);

      // request.element should be the browser element in e10s.
      if (request.element && request.element.contentPrincipal) {
        let thirdPartyHistogram = Services.telemetry.getKeyedHistogramById("PERMISSION_REQUEST_THIRD_PARTY_ORIGIN");
        let isThirdParty = request.principal.origin != request.element.contentPrincipal.origin;
        thirdPartyHistogram.add(type, isThirdParty);
      }

      let userInputHistogram = Services.telemetry.getKeyedHistogramById("PERMISSION_REQUEST_HANDLING_USER_INPUT");
      userInputHistogram.add(type, request.isHandlingUserInput);

    } catch (ex) {
      Cu.reportError(ex);
      request.cancel();
      throw ex;
    }
  },
};

var DefaultBrowserCheck = {
  get OPTIONPOPUP() { return "defaultBrowserNotificationPopup"; },

  closePrompt(aNode) {
    if (this._notification) {
      this._notification.close();
    }
  },

  setAsDefault() {
    let claimAllTypes = true;
    let setAsDefaultError = false;
    if (AppConstants.platform == "win") {
      try {
        // In Windows 8+, the UI for selecting default protocol is much
        // nicer than the UI for setting file type associations. So we
        // only show the protocol association screen on Windows 8+.
        // Windows 8 is version 6.2.
        let version = Services.sysinfo.getProperty("version");
        claimAllTypes = (parseFloat(version) < 6.2);
      } catch (ex) { }
    }
    try {
      ShellService.setDefaultBrowser(claimAllTypes, false);
    } catch (ex) {
      setAsDefaultError = true;
      Cu.reportError(ex);
    }
    // Here BROWSER_IS_USER_DEFAULT and BROWSER_SET_USER_DEFAULT_ERROR appear
    // to be inverse of each other, but that is only because this function is
    // called when the browser is set as the default. During startup we record
    // the BROWSER_IS_USER_DEFAULT value without recording BROWSER_SET_USER_DEFAULT_ERROR.
    Services.telemetry.getHistogramById("BROWSER_IS_USER_DEFAULT")
                      .add(!setAsDefaultError);
    Services.telemetry.getHistogramById("BROWSER_SET_DEFAULT_ERROR")
                      .add(setAsDefaultError);
  },

  _createPopup(win, notNowStrings, neverStrings) {
    let doc = win.document;
    let popup = doc.createElement("menupopup");
    popup.id = this.OPTIONPOPUP;

    let notNowItem = doc.createElement("menuitem");
    notNowItem.id = "defaultBrowserNotNow";
    notNowItem.setAttribute("label", notNowStrings.label);
    notNowItem.setAttribute("accesskey", notNowStrings.accesskey);
    popup.appendChild(notNowItem);

    let neverItem = doc.createElement("menuitem");
    neverItem.id = "defaultBrowserNever";
    neverItem.setAttribute("label", neverStrings.label);
    neverItem.setAttribute("accesskey", neverStrings.accesskey);
    popup.appendChild(neverItem);

    popup.addEventListener("command", this);

    let popupset = doc.getElementById("mainPopupSet");
    popupset.appendChild(popup);
  },

  handleEvent(event) {
    if (event.type == "command") {
      if (event.target.id == "defaultBrowserNever") {
        ShellService.shouldCheckDefaultBrowser = false;
      }
      this.closePrompt();
    }
  },

  prompt(win) {
    let useNotificationBar = Services.prefs.getBoolPref("browser.defaultbrowser.notificationbar");

    let brandBundle = win.document.getElementById("bundle_brand");
    let brandShortName = brandBundle.getString("brandShortName");

    let shellBundle = win.document.getElementById("bundle_shell");
    let buttonPrefix = "setDefaultBrowser" + (useNotificationBar ? "" : "Alert");
    let yesButton = shellBundle.getFormattedString(buttonPrefix + "Confirm.label",
                                                   [brandShortName]);
    let notNowButton = shellBundle.getString(buttonPrefix + "NotNow.label");

    if (useNotificationBar) {
      let promptMessage = shellBundle.getFormattedString("setDefaultBrowserMessage2",
                                                         [brandShortName]);
      let optionsMessage = shellBundle.getString("setDefaultBrowserOptions.label");
      let optionsKey = shellBundle.getString("setDefaultBrowserOptions.accesskey");

      let neverLabel = shellBundle.getString("setDefaultBrowserNever.label");
      let neverKey = shellBundle.getString("setDefaultBrowserNever.accesskey");

      let yesButtonKey = shellBundle.getString("setDefaultBrowserConfirm.accesskey");
      let notNowButtonKey = shellBundle.getString("setDefaultBrowserNotNow.accesskey");

      let notificationBox = win.document.getElementById("high-priority-global-notificationbox");

      this._createPopup(win, {
        label: notNowButton,
        accesskey: notNowButtonKey
      }, {
        label: neverLabel,
        accesskey: neverKey
      });

      let buttons = [
        {
          label: yesButton,
          accessKey: yesButtonKey,
          callback: () => {
            this.setAsDefault();
            this.closePrompt();
          }
        },
        {
          label: optionsMessage,
          accessKey: optionsKey,
          popup: this.OPTIONPOPUP
        }
      ];

      let iconPixels = win.devicePixelRatio > 1 ? "32" : "16";
      let iconURL = "chrome://branding/content/icon" + iconPixels + ".png";
      const priority = notificationBox.PRIORITY_WARNING_HIGH;
      let callback = this._onNotificationEvent.bind(this);
      this._notification = notificationBox.appendNotification(promptMessage, "default-browser",
                                                              iconURL, priority, buttons,
                                                              callback);
    } else {
      // Modal prompt
      let promptTitle = shellBundle.getString("setDefaultBrowserTitle");
      let promptMessage = shellBundle.getFormattedString("setDefaultBrowserMessage",
                                                         [brandShortName]);
      let askLabel = shellBundle.getFormattedString("setDefaultBrowserDontAsk",
                                                    [brandShortName]);

      let ps = Services.prompt;
      let shouldAsk = { value: true };
      let buttonFlags = (ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_0) +
                        (ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_1) +
                        ps.BUTTON_POS_0_DEFAULT;
      let rv = ps.confirmEx(win, promptTitle, promptMessage, buttonFlags,
                            yesButton, notNowButton, null, askLabel, shouldAsk);
      if (rv == 0) {
        this.setAsDefault();
      } else if (!shouldAsk.value) {
        ShellService.shouldCheckDefaultBrowser = false;
      }

      try {
        let resultEnum = rv * 2 + shouldAsk.value;
        Services.telemetry.getHistogramById("BROWSER_SET_DEFAULT_RESULT")
                          .add(resultEnum);
      } catch (ex) { /* Don't break if Telemetry is acting up. */ }
    }
  },

  _onNotificationEvent(eventType) {
    if (eventType == "removed") {
      let doc = this._notification.ownerDocument;
      let popup = doc.getElementById(this.OPTIONPOPUP);
      popup.removeEventListener("command", this);
      popup.remove();
      delete this._notification;
    }
  },
};

/*
 * Prompts users who have an outdated JAWS screen reader informing
 * them they need to update JAWS or switch to esr. Can be removed
 * 12/31/2018.
 */
var JawsScreenReaderVersionCheck = {
  _prompted: false,

  init() {
    Services.obs.addObserver(this, "a11y-init-or-shutdown", true);
  },

  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]),

  observe(subject, topic, data) {
    if (topic == "a11y-init-or-shutdown" && data == "1") {
      Services.tm.dispatchToMainThread(() => this._checkVersionAndPrompt());
    }
  },

  onWindowsRestored() {
    Services.tm.dispatchToMainThread(() => this._checkVersionAndPrompt());
  },

  _checkVersionAndPrompt() {
    // Make sure we only prompt for versions of JAWS we do not
    // support and never prompt if e10s is disabled or if we're on
    // nightly.
    if (!Services.appinfo.shouldBlockIncompatJaws ||
        !Services.appinfo.browserTabsRemoteAutostart ||
        AppConstants.NIGHTLY_BUILD) {
      return;
    }

    let win = RecentWindow.getMostRecentBrowserWindow();
    if (!win || !win.gBrowser || !win.gBrowser.selectedBrowser) {
      Services.console.logStringMessage(
          "Content access support for older versions of JAWS is disabled " +
          "due to compatibility issues with this version of Firefox.");
      this._prompted = false;
      return;
    }

    // Only prompt once per session
    if (this._prompted) {
      return;
    }
    this._prompted = true;

    let browser = win.gBrowser.selectedBrowser;

    // Prompt JAWS users to let them know they need to update
    let promptMessage = win.gNavigatorBundle.getFormattedString(
                          "e10s.accessibilityNotice.jawsMessage",
                          [gBrandBundle.GetStringFromName("brandShortName")]
                        );
    let notification;
    // main option: an Ok button, keeps running with content accessibility disabled
    let mainAction = {
      label: win.gNavigatorBundle.getString("e10s.accessibilityNotice.acceptButton.label"),
      accessKey: win.gNavigatorBundle.getString("e10s.accessibilityNotice.acceptButton.accesskey"),
      callback() {
        // If the user invoked the button option remove the notification,
        // otherwise keep the alert icon around in the address bar.
        notification.remove();
      }
    };
    let options = {
      popupIconURL: "chrome://browser/skin/[email protected]",
      persistWhileVisible: true,
      persistent: true,
      persistence: 100
    };

    notification =
      win.PopupNotifications.show(browser, "e10s_enabled_with_incompat_jaws",
                                  promptMessage, null, mainAction,
                                  null, options);
  },
};

var components = [BrowserGlue, ContentPermissionPrompt];
this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components);

// Listen for UITour messages.
// Do it here instead of the UITour module itself so that the UITour module is lazy loaded
// when the first message is received.
var globalMM = Cc["@mozilla.org/globalmessagemanager;1"].getService(Ci.nsIMessageListenerManager);
globalMM.addMessageListener("UITour:onPageEvent", function(aMessage) {
  UITour.onPageEvent(aMessage, aMessage.data);
});

// Listen for HybridContentTelemetry messages.
// Do it here instead of HybridContentTelemetry.init() so that
// the module can be lazily loaded on the first message.
globalMM.addMessageListener("HybridContentTelemetry:onTelemetryMessage", aMessage => {
  HybridContentTelemetry.onTelemetryMessage(aMessage, aMessage.data);
});
PK
!<-j�X4X4components/nsSessionStartup.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

/**
 * Session Storage and Restoration
 *
 * Overview
 * This service reads user's session file at startup, and makes a determination
 * as to whether the session should be restored. It will restore the session
 * under the circumstances described below.  If the auto-start Private Browsing
 * mode is active, however, the session is never restored.
 *
 * Crash Detection
 * The CrashMonitor is used to check if the final session state was successfully
 * written at shutdown of the last session. If we did not reach
 * 'sessionstore-final-state-write-complete', then it's assumed that the browser
 * has previously crashed and we should restore the session.
 *
 * Forced Restarts
 * In the event that a restart is required due to application update or extension
 * installation, set the browser.sessionstore.resume_session_once pref to true,
 * and the session will be restored the next time the browser starts.
 *
 * Always Resume
 * This service will always resume the session if the integer pref
 * browser.startup.page is set to 3.
 */

/* :::::::: Constants and Helpers ::::::::::::::: */

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");

ChromeUtils.defineModuleGetter(this, "SessionFile",
  "resource:///modules/sessionstore/SessionFile.jsm");
ChromeUtils.defineModuleGetter(this, "StartupPerformance",
  "resource:///modules/sessionstore/StartupPerformance.jsm");
ChromeUtils.defineModuleGetter(this, "CrashMonitor",
  "resource://gre/modules/CrashMonitor.jsm");
ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
  "resource://gre/modules/PrivateBrowsingUtils.jsm");

const STATE_RUNNING_STR = "running";

// 'browser.startup.page' preference value to resume the previous session.
const BROWSER_STARTUP_RESUME_SESSION = 3;

function debug(aMsg) {
  aMsg = ("SessionStartup: " + aMsg).replace(/\S{80}/g, "$&\n");
  Services.console.logStringMessage(aMsg);
}
function warning(aMsg, aException) {
  let consoleMsg = Cc["@mozilla.org/scripterror;1"].createInstance(Ci.nsIScriptError);
consoleMsg.init(aMsg, aException.fileName, null, aException.lineNumber, 0, Ci.nsIScriptError.warningFlag, "component javascript");
  Services.console.logMessage(consoleMsg);
}

var gOnceInitializedDeferred = (function() {
  let deferred = {};

  deferred.promise = new Promise((resolve, reject) => {
    deferred.resolve = resolve;
    deferred.reject = reject;
  });

  return deferred;
})();

/* :::::::: The Service ::::::::::::::: */

function SessionStartup() {
}

SessionStartup.prototype = {

  // the state to restore at startup
  _initialState: null,
  _sessionType: Ci.nsISessionStartup.NO_SESSION,
  _initialized: false,

  // Stores whether the previous session crashed.
  _previousSessionCrashed: null,

  _resumeSessionEnabled: null,

/* ........ Global Event Handlers .............. */

  /**
   * Initialize the component
   */
  init: function sss_init() {
    Services.obs.notifyObservers(null, "sessionstore-init-started");
    StartupPerformance.init();

    // do not need to initialize anything in auto-started private browsing sessions
    if (PrivateBrowsingUtils.permanentPrivateBrowsing) {
      this._initialized = true;
      gOnceInitializedDeferred.resolve();
      return;
    }

    this._resumeSessionEnabled =
      Services.prefs.getBoolPref("browser.sessionstore.resume_session_once") ||
      Services.prefs.getIntPref("browser.startup.page") == BROWSER_STARTUP_RESUME_SESSION;

    SessionFile.read().then(
      this._onSessionFileRead.bind(this),
      console.error
    );
  },

  // Wrap a string as a nsISupports
  _createSupportsString: function ssfi_createSupportsString(aData) {
    let string = Cc["@mozilla.org/supports-string;1"]
                   .createInstance(Ci.nsISupportsString);
    string.data = aData;
    return string;
  },

  /**
   * Complete initialization once the Session File has been read
   *
   * @param source The Session State string read from disk.
   * @param parsed The object obtained by parsing |source| as JSON.
   */
  _onSessionFileRead({source, parsed, noFilesFound}) {
    this._initialized = true;

    // Let observers modify the state before it is used
    let supportsStateString = this._createSupportsString(source);
    Services.obs.notifyObservers(supportsStateString, "sessionstore-state-read");
    let stateString = supportsStateString.data;

    if (stateString != source) {
      // The session has been modified by an add-on, reparse.
      try {
        this._initialState = JSON.parse(stateString);
      } catch (ex) {
        // That's not very good, an add-on has rewritten the initial
        // state to something that won't parse.
        warning("Observer rewrote the state to something that won't parse", ex);
      }
    } else {
      // No need to reparse
      this._initialState = parsed;
    }

    if (this._initialState == null) {
      // No valid session found.
      this._sessionType = Ci.nsISessionStartup.NO_SESSION;
      Services.obs.notifyObservers(null, "sessionstore-state-finalized");
      gOnceInitializedDeferred.resolve();
      return;
    }

    let initialState = this._initialState;
    Services.tm.idleDispatchToMainThread(() => {
      let pinnedTabCount = initialState.windows.reduce((winAcc, win) => {
        return winAcc + win.tabs.reduce((tabAcc, tab) => {
          return tabAcc + (tab.pinned ? 1 : 0);
        }, 0);
      }, 0);
      Services.telemetry.scalarSet("browser.engagement.restored_pinned_tabs_count", pinnedTabCount);
    }, 60000);

    // If this is a normal restore then throw away any previous session
    if (!this._resumeSessionEnabled && this._initialState) {
      delete this._initialState.lastSessionState;
    }

    let resumeFromCrash = Services.prefs.getBoolPref("browser.sessionstore.resume_from_crash");

    CrashMonitor.previousCheckpoints.then(checkpoints => {
      if (checkpoints) {
        // If the previous session finished writing the final state, we'll
        // assume there was no crash.
        this._previousSessionCrashed = !checkpoints["sessionstore-final-state-write-complete"];
      } else if (noFilesFound) {
        // If the Crash Monitor could not load a checkpoints file it will
        // provide null. This could occur on the first run after updating to
        // a version including the Crash Monitor, or if the checkpoints file
        // was removed, or on first startup with this profile, or after Firefox Reset.

        // There was no checkpoints file and no sessionstore.js or its backups
        // so we will assume that this was a fresh profile.
        this._previousSessionCrashed = false;
      } else {
        // If this is the first run after an update, sessionstore.js should
        // still contain the session.state flag to indicate if the session
        // crashed. If it is not present, we will assume this was not the first
        // run after update and the checkpoints file was somehow corrupted or
        // removed by a crash.
        //
        // If the session.state flag is present, we will fallback to using it
        // for crash detection - If the last write of sessionstore.js had it
        // set to "running", we crashed.
        let stateFlagPresent = (this._initialState.session &&
                                this._initialState.session.state);

        this._previousSessionCrashed = !stateFlagPresent ||
          (this._initialState.session.state == STATE_RUNNING_STR);
      }

      // Report shutdown success via telemetry. Shortcoming here are
      // being-killed-by-OS-shutdown-logic, shutdown freezing after
      // session restore was written, etc.
      Services.telemetry.getHistogramById("SHUTDOWN_OK").add(!this._previousSessionCrashed);

      // set the startup type
      if (this._previousSessionCrashed && resumeFromCrash)
        this._sessionType = Ci.nsISessionStartup.RECOVER_SESSION;
      else if (!this._previousSessionCrashed && this._resumeSessionEnabled)
        this._sessionType = Ci.nsISessionStartup.RESUME_SESSION;
      else if (this._initialState)
        this._sessionType = Ci.nsISessionStartup.DEFER_SESSION;
      else
        this._initialState = null; // reset the state

      Services.obs.addObserver(this, "sessionstore-windows-restored", true);

      if (this._sessionType != Ci.nsISessionStartup.NO_SESSION)
        Services.obs.addObserver(this, "browser:purge-session-history", true);

      // We're ready. Notify everyone else.
      Services.obs.notifyObservers(null, "sessionstore-state-finalized");
      gOnceInitializedDeferred.resolve();
    });
  },

  /**
   * Handle notifications
   */
  observe: function sss_observe(aSubject, aTopic, aData) {
    switch (aTopic) {
    case "app-startup":
      Services.obs.addObserver(this, "final-ui-startup", true);
      Services.obs.addObserver(this, "quit-application", true);
      break;
    case "final-ui-startup":
      Services.obs.removeObserver(this, "final-ui-startup");
      Services.obs.removeObserver(this, "quit-application");
      this.init();
      break;
    case "quit-application":
      // no reason for initializing at this point (cf. bug 409115)
      Services.obs.removeObserver(this, "final-ui-startup");
      Services.obs.removeObserver(this, "quit-application");
      if (this._sessionType != Ci.nsISessionStartup.NO_SESSION)
        Services.obs.removeObserver(this, "browser:purge-session-history");
      break;
    case "sessionstore-windows-restored":
      Services.obs.removeObserver(this, "sessionstore-windows-restored");
      // free _initialState after nsSessionStore is done with it
      this._initialState = null;
      break;
    case "browser:purge-session-history":
      Services.obs.removeObserver(this, "browser:purge-session-history");
      // reset all state on sanitization
      this._sessionType = Ci.nsISessionStartup.NO_SESSION;
      break;
    }
  },

/* ........ Public API ................*/

  get onceInitialized() {
    return gOnceInitializedDeferred.promise;
  },

  /**
   * Get the session state as a jsval
   */
  get state() {
    return this._initialState;
  },

  /**
   * Determines whether there is a pending session restore. Should only be
   * called after initialization has completed.
   * @returns bool
   */
  doRestore: function sss_doRestore() {
    return this._willRestore();
  },

  /**
   * Determines whether automatic session restoration is enabled for this
   * launch of the browser. This does not include crash restoration. In
   * particular, if session restore is configured to restore only in case of
   * crash, this method returns false.
   * @returns bool
   */
  isAutomaticRestoreEnabled() {
    if (PrivateBrowsingUtils.permanentPrivateBrowsing) {
      return false;
    }

    return Services.prefs.getBoolPref("browser.sessionstore.resume_session_once") ||
           Services.prefs.getIntPref("browser.startup.page") == BROWSER_STARTUP_RESUME_SESSION;
  },

  /**
   * Determines whether there is a pending session restore.
   * @returns bool
   */
  _willRestore() {
    return this._sessionType == Ci.nsISessionStartup.RECOVER_SESSION ||
           this._sessionType == Ci.nsISessionStartup.RESUME_SESSION;
  },

  /**
   * Returns a boolean or a promise that resolves to a boolean, indicating
   * whether we will restore a session that ends up replacing the homepage.
   * True guarantees that we'll restore a session; false means that we
   * /probably/ won't do so.
   * The browser uses this to avoid unnecessarily loading the homepage when
   * restoring a session.
   */
  get willOverrideHomepage() {
    // If the session file hasn't been read yet and resuming the session isn't
    // enabled via prefs, go ahead and load the homepage. We may still replace
    // it when recovering from a crash, which we'll only know after reading the
    // session file, but waiting for that would delay loading the homepage in
    // the non-crash case.
    if (!this._initialState && !this._resumeSessionEnabled) {
      return false;
    }

    return new Promise(resolve => {
      this.onceInitialized.then(() => {
        // If there are valid windows with not only pinned tabs, signal that we
        // will override the default homepage by restoring a session.
        resolve(this._willRestore() &&
                this._initialState &&
                this._initialState.windows &&
                this._initialState.windows.some(w => w.tabs.some(t => !t.pinned)));
      });
    });
  },

  /**
   * Get the type of pending session store, if any.
   */
  get sessionType() {
    return this._sessionType;
  },

  /**
   * Get whether the previous session crashed.
   */
  get previousSessionCrashed() {
    return this._previousSessionCrashed;
  },

  /* ........ QueryInterface .............. */
  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                         Ci.nsISupportsWeakReference,
                                         Ci.nsISessionStartup]),
  classID: Components.ID("{ec7a6c20-e081-11da-8ad9-0800200c9a66}")
};

this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SessionStartup]);
PK
!<��*:8:8 components/EnterprisePolicies.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/AppConstants.jsm");

XPCOMUtils.defineLazyModuleGetters(this, {
  WindowsGPOParser: "resource:///modules/policies/WindowsGPOParser.jsm",
  Policies: "resource:///modules/policies/Policies.jsm",
  PoliciesValidator: "resource:///modules/policies/PoliciesValidator.jsm",
});

// This is the file that will be searched for in the
// ${InstallDir}/distribution folder.
const POLICIES_FILENAME = "policies.json";

// For easy testing, modify the helpers/sample.json file,
// and set PREF_ALTERNATE_PATH in firefox.js as:
// /your/repo/browser/components/enterprisepolicies/helpers/sample.json
const PREF_ALTERNATE_PATH     = "browser.policies.alternatePath";
// For testing, we may want to set PREF_ALTERNATE_PATH to point to a file
// relative to the test root directory. In order to enable this, the string
// below may be placed at the beginning of that preference value and it will
// be replaced with the path to the test root directory.
const MAGIC_TEST_ROOT_PREFIX  = "<test-root>";
const PREF_TEST_ROOT          = "mochitest.testRoot";

const PREF_LOGLEVEL           = "browser.policies.loglevel";

// To force disallowing enterprise-only policies during tests
const PREF_DISALLOW_ENTERPRISE = "browser.policies.testing.disallowEnterprise";

XPCOMUtils.defineLazyGetter(this, "log", () => {
  let { ConsoleAPI } = ChromeUtils.import("resource://gre/modules/Console.jsm", {});
  return new ConsoleAPI({
    prefix: "Enterprise Policies",
    // tip: set maxLogLevel to "debug" and use log.debug() to create detailed
    // messages during development. See LOG_LEVELS in Console.jsm for details.
    maxLogLevel: "error",
    maxLogLevelPref: PREF_LOGLEVEL,
  });
});

// ==== Start XPCOM Boilerplate ==== \\

// Factory object
const EnterprisePoliciesFactory = {
  _instance: null,
  createInstance: function BGSF_createInstance(outer, iid) {
    if (outer != null)
      throw Cr.NS_ERROR_NO_AGGREGATION;
    return this._instance == null ?
      this._instance = new EnterprisePoliciesManager() : this._instance;
  }
};

// ==== End XPCOM Boilerplate ==== //

// Constructor
function EnterprisePoliciesManager() {
  Services.obs.addObserver(this, "profile-after-change", true);
  Services.obs.addObserver(this, "final-ui-startup", true);
  Services.obs.addObserver(this, "sessionstore-windows-restored", true);
  Services.obs.addObserver(this, "EnterprisePolicies:Restart", true);
}

EnterprisePoliciesManager.prototype = {
  // for XPCOM
  classID:          Components.ID("{ea4e1414-779b-458b-9d1f-d18e8efbc145}"),
  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                         Ci.nsISupportsWeakReference,
                                         Ci.nsIEnterprisePolicies]),

  // redefine the default factory for XPCOMUtils
  _xpcom_factory: EnterprisePoliciesFactory,

  _initialize() {
    let provider = this._chooseProvider();

    if (!provider) {
      this.status = Ci.nsIEnterprisePolicies.INACTIVE;
      return;
    }

    if (provider.failed) {
      this.status = Ci.nsIEnterprisePolicies.FAILED;
      return;
    }

    this.status = Ci.nsIEnterprisePolicies.ACTIVE;
    this._activatePolicies(provider.policies);
  },

  _chooseProvider() {
    if (AppConstants.platform == "win") {
      let gpoProvider = new GPOPoliciesProvider();
      if (gpoProvider.hasPolicies) {
        return gpoProvider;
      }
    }

    let jsonProvider = new JSONPoliciesProvider();
    if (jsonProvider.hasPolicies) {
      return jsonProvider;
    }

    return null;
  },

  _activatePolicies(unparsedPolicies) {
    let { schema } = ChromeUtils.import("resource:///modules/policies/schema.jsm", {});

    for (let policyName of Object.keys(unparsedPolicies)) {
      let policySchema = schema.properties[policyName];
      let policyParameters = unparsedPolicies[policyName];

      if (!policySchema) {
        log.error(`Unknown policy: ${policyName}`);
        continue;
      }

      if (policySchema.enterprise_only && !areEnterpriseOnlyPoliciesAllowed()) {
        log.error(`Policy ${policyName} is only allowed on ESR`);
        continue;
      }

      let [parametersAreValid, parsedParameters] =
        PoliciesValidator.validateAndParseParameters(policyParameters,
                                                     policySchema);

      if (!parametersAreValid) {
        log.error(`Invalid parameters specified for ${policyName}.`);
        continue;
      }

      let policyImpl = Policies[policyName];

      for (let timing of Object.keys(this._callbacks)) {
        let policyCallback = policyImpl[timing];
        if (policyCallback) {
          this._schedulePolicyCallback(
            timing,
            policyCallback.bind(policyImpl,
                                this, /* the EnterprisePoliciesManager */
                                parsedParameters));
        }
      }
    }
  },

  _callbacks: {
    // The earlist that a policy callback can run. This will
    // happen right after the Policy Engine itself has started,
    // and before the Add-ons Manager has started.
    onBeforeAddons: [],

    // This happens after all the initialization related to
    // the profile has finished (prefs, places database, etc.).
    onProfileAfterChange: [],

    // Just before the first browser window gets created.
    onBeforeUIStartup: [],

    // Called after all windows from the last session have been
    // restored (or the default window and homepage tab, if the
    // session is not being restored).
    // The content of the tabs themselves have not necessarily
    // finished loading.
    onAllWindowsRestored: [],
  },

  _schedulePolicyCallback(timing, callback) {
    this._callbacks[timing].push(callback);
  },

  _runPoliciesCallbacks(timing) {
    let callbacks = this._callbacks[timing];
    while (callbacks.length > 0) {
      let callback = callbacks.shift();
      try {
        callback();
      } catch (ex) {
        log.error("Error running ", callback, `for ${timing}:`, ex);
      }
    }
  },

  async _restart() {
    if (!Cu.isInAutomation) {
      return;
    }

    DisallowedFeatures = {};

    this._status = Ci.nsIEnterprisePolicies.UNINITIALIZED;
    for (let timing of Object.keys(this._callbacks)) {
      this._callbacks[timing] = [];
    }
    delete Services.ppmm.initialProcessData.policies;
    Services.ppmm.broadcastAsyncMessage("EnterprisePolicies:Restart", null);

    let { PromiseUtils } = ChromeUtils.import("resource://gre/modules/PromiseUtils.jsm",
                                              {});

    // Simulate the startup process. This step-by-step is a bit ugly but it
    // tries to emulate the same behavior as of a normal startup.

    await PromiseUtils.idleDispatch(() => {
      this.observe(null, "policies-startup", null);
    });

    await PromiseUtils.idleDispatch(() => {
      this.observe(null, "profile-after-change", null);
    });

    await PromiseUtils.idleDispatch(() => {
      this.observe(null, "final-ui-startup", null);
    });

    await PromiseUtils.idleDispatch(() => {
      this.observe(null, "sessionstore-windows-restored", null);
    });
  },

  // nsIObserver implementation
  observe: function BG_observe(subject, topic, data) {
    switch (topic) {
      case "policies-startup":
        // Before the first set of policy callbacks runs, we must
        // initialize the service.
        this._initialize();

        this._runPoliciesCallbacks("onBeforeAddons");
        break;

      case "profile-after-change":
        this._runPoliciesCallbacks("onProfileAfterChange");
        break;

      case "final-ui-startup":
        this._runPoliciesCallbacks("onBeforeUIStartup");
        break;

      case "sessionstore-windows-restored":
        this._runPoliciesCallbacks("onAllWindowsRestored");

        // After the last set of policy callbacks ran, notify the test observer.
        Services.obs.notifyObservers(null,
                                     "EnterprisePolicies:AllPoliciesApplied");
        break;

      case "EnterprisePolicies:Restart":
        this._restart().then(null, Cu.reportError);
        break;
    }
  },

  disallowFeature(feature, neededOnContentProcess = false) {
    DisallowedFeatures[feature] = true;

    // NOTE: For optimization purposes, only features marked as needed
    // on content process will be passed onto the child processes.
    if (neededOnContentProcess) {
      Services.ppmm.initialProcessData.policies
                                      .disallowedFeatures.push(feature);

      if (Services.ppmm.childCount > 1) {
        // If there has been a content process already initialized, let's
        // broadcast the newly disallowed feature.
        Services.ppmm.broadcastAsyncMessage(
          "EnterprisePolicies:DisallowFeature", {feature}
        );
      }
    }
  },

  // ------------------------------
  // public nsIEnterprisePolicies members
  // ------------------------------

  _status: Ci.nsIEnterprisePolicies.UNINITIALIZED,

  set status(val) {
    this._status = val;
    if (val != Ci.nsIEnterprisePolicies.INACTIVE) {
      Services.ppmm.initialProcessData.policies = {
        status: val,
        disallowedFeatures: [],
      };
    }
    return val;
  },

  get status() {
    return this._status;
  },

  isAllowed: function BG_sanitize(feature) {
    return !(feature in DisallowedFeatures);
  },
};

let DisallowedFeatures = {};

/**
 * areEnterpriseOnlyPoliciesAllowed
 *
 * Checks whether the policies marked as enterprise_only in the
 * schema are allowed to run on this browser.
 *
 * This is meant to only allow policies to run on ESR, but in practice
 * we allow it to run on channels different than release, to allow
 * these policies to be tested on pre-release channels.
 *
 * @returns {Bool} Whether the policy can run.
 */
function areEnterpriseOnlyPoliciesAllowed() {
  if (Services.prefs.getBoolPref(PREF_DISALLOW_ENTERPRISE, false)) {
    // This is used as an override to test the "enterprise_only"
    // functionality itself on tests, which would always return
    // true due to the Cu.isInAutomation check below.
    return false;
  }

  if (AppConstants.MOZ_UPDATE_CHANNEL != "release" ||
      Cu.isInAutomation) {
    return true;
  }

  return false;
}

/*
 * JSON PROVIDER OF POLICIES
 *
 * This is a platform-agnostic provider which looks for
 * policies specified through a policies.json file stored
 * in the installation's distribution folder.
 */

class JSONPoliciesProvider {
  constructor() {
    this._policies = null;
    this._failed = false;
    this._readData();
  }

  get hasPolicies() {
    return this._policies !== null || this._failed;
  }

  get policies() {
    return this._policies;
  }

  get failed() {
    return this._failed;
  }

  _getConfigurationFile() {
    let configFile = null;
    try {
      configFile = Services.dirsvc.get("XREAppDist", Ci.nsIFile);
      configFile.append(POLICIES_FILENAME);
    } catch (ex) {
      // Getting the correct directory will fail in xpcshell tests. This should
      // be handled the same way as if the configFile simply does not exist.
    }

    let alternatePath = Services.prefs.getStringPref(PREF_ALTERNATE_PATH, "");

    if (alternatePath && (!configFile || !configFile.exists())) {
      // We only want to use the alternate file path if the file on the install
      // folder doesn't exist. Otherwise it'd be possible for a user to override
      // the admin-provided policies by changing the user-controlled prefs.
      // This pref is only meant for tests, so it's fine to use this extra
      // synchronous configFile.exists() above.
      if (alternatePath.startsWith(MAGIC_TEST_ROOT_PREFIX)) {
        // Intentionally not using a default value on this pref lookup. If no
        // test root is set, we are not currently testing and this function
        // should throw rather than returning something.
        let testRoot = Services.prefs.getStringPref(PREF_TEST_ROOT);
        let relativePath = alternatePath.substring(MAGIC_TEST_ROOT_PREFIX.length);
        if (AppConstants.platform == "win") {
          relativePath = relativePath.replace(/\//g, "\\");
        }
        alternatePath = testRoot + relativePath;
      }

      configFile = Cc["@mozilla.org/file/local;1"]
                     .createInstance(Ci.nsIFile);
      configFile.initWithPath(alternatePath);
    }

    return configFile;
  }

  _readData() {
    try {
      let data = Cu.readUTF8File(this._getConfigurationFile());
      if (data) {
        this._policies = JSON.parse(data).policies;
      }
    } catch (ex) {
      if (ex instanceof Components.Exception &&
          ex.result == Cr.NS_ERROR_FILE_NOT_FOUND) {
        // Do nothing, _policies will remain null
      } else if (ex instanceof SyntaxError) {
        log.error("Error parsing JSON file");
        this._failed = true;
      } else {
        log.error("Error reading file");
        this._failed = true;
      }
    }
  }
}

class GPOPoliciesProvider {
  constructor() {
    this._policies = null;

    let wrk = Cc["@mozilla.org/windows-registry-key;1"].createInstance(Ci.nsIWindowsRegKey);
    // Machine policies override user policies, so we read
    // user policies first and then replace them if necessary.
    wrk.open(wrk.ROOT_KEY_CURRENT_USER,
             "SOFTWARE\\Policies",
             wrk.ACCESS_READ);
    if (wrk.hasChild("Mozilla\\Firefox")) {
      this._readData(wrk);
    }
    wrk.close();

    wrk.open(wrk.ROOT_KEY_LOCAL_MACHINE,
             "SOFTWARE\\Policies",
             wrk.ACCESS_READ);
    if (wrk.hasChild("Mozilla\\Firefox")) {
      this._readData(wrk);
    }
    wrk.close();
  }

  get hasPolicies() {
    return this._policies !== null;
  }

  get policies() {
    return this._policies;
  }

  get failed() {
    return this._failed;
  }

  _readData(wrk) {
    this._policies = WindowsGPOParser.readPolicies(wrk, this._policies);
  }
}

var components = [EnterprisePoliciesManager];
this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components);
PK
!<�"'ϵ�defaults/permissions# This file has default permissions for the permission manager.
# The file-format is strict:
# * matchtype \t type \t permission \t host
# * "origin" should be used for matchtype, "host" is supported for legacy reasons
# * type is a string that identifies the type of permission (e.g. "cookie")
# * permission is an integer between 1 and 15
# See nsPermissionManager.cpp for more...

# UITour
origin	uitour	1	https://www.mozilla.org
origin	uitour	1	https://screenshots.firefox.com
origin	uitour	1	https://support.mozilla.org
origin	uitour	1	https://addons.mozilla.org
origin	uitour	1	https://discovery.addons.mozilla.org
origin	uitour	1	about:home
origin	uitour	1	about:newtab

# XPInstall
origin	install	1	https://addons.mozilla.org
origin	install	1	https://testpilot.firefox.com

# Remote troubleshooting
origin	remote-troubleshooting	1	https://input.mozilla.org
origin	remote-troubleshooting	1	https://support.mozilla.org
PK
!<�]��S�S�*chrome/browser/content/browser/browser.xul<?xml version="1.0"?>

<?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/downloads/downloads.css"?>
<?xml-stylesheet href="chrome://browser/content/places/places.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/usercontext/usercontext.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/controlcenter/panel.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/customizableui/panelUI.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/downloads/downloads.css"?>
<?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/tabbrowser.css" type="text/css"?>

<?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?>

<!DOCTYPE window [
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
%brandDTD;
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd" >
%browserDTD;
<!ENTITY % baseMenuDTD SYSTEM "chrome://browser/locale/baseMenuOverlay.dtd" >
%baseMenuDTD;
<!ENTITY % charsetDTD SYSTEM "chrome://global/locale/charsetMenu.dtd" >
%charsetDTD;
<!ENTITY % textcontextDTD SYSTEM "chrome://global/locale/textcontext.dtd" >
%textcontextDTD;
<!ENTITY % downloadsDTD SYSTEM "chrome://browser/locale/downloads/downloads.dtd">
%downloadsDTD;
<!ENTITY % placesDTD SYSTEM "chrome://browser/locale/places/places.dtd">
%placesDTD;
<!ENTITY % safebrowsingDTD SYSTEM "chrome://browser/locale/safebrowsing/phishing-afterload-warning-message.dtd">
%safebrowsingDTD;
<!ENTITY % aboutHomeDTD SYSTEM "chrome://browser/locale/aboutHome.dtd">
%aboutHomeDTD;
<!ENTITY % syncBrandDTD SYSTEM "chrome://browser/locale/syncBrand.dtd">
%syncBrandDTD;
<!ENTITY % reportphishDTD SYSTEM "chrome://browser/locale/safebrowsing/report-phishing.dtd">
%reportphishDTD;
]>


<window id="main-window"
        xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
        xmlns:svg="http://www.w3.org/2000/svg"
        xmlns:html="http://www.w3.org/1999/xhtml"
        xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        onload="gBrowserInit.onLoad()" onunload="gBrowserInit.onUnload()" onclose="return WindowIsClosing();"
        title="&mainWindow.title;"
        title_normal="&mainWindow.title;"
        title_privatebrowsing="&mainWindow.titlemodifier; &mainWindow.titlePrivateBrowsingSuffix;"
        titlemodifier="&mainWindow.titlemodifier;"
        titlemodifier_normal="&mainWindow.titlemodifier;"
        titlemodifier_privatebrowsing="&mainWindow.titlemodifier; &mainWindow.titlePrivateBrowsingSuffix;"
        chromemargin="0,-1,-1,-1"
        tabsintitlebar="true"
        titlemenuseparator="&mainWindow.titlemodifiermenuseparator;"
        windowtype="navigator:browser"
        macanimationtype="document"
        screenX="4" screenY="4"
        fullscreenbutton="true"
        sizemode="normal"
        retargetdocumentfocus="urlbar"
        persist="screenX screenY width height sizemode">



<script type="application/javascript">
Components.utils.import("resource://gre/modules/Services.jsm");

for (let script of [
  "chrome://browser/content/browser.js",

  "chrome://browser/content/browser-captivePortal.js",
  "chrome://browser/content/browser-compacttheme.js",
  "chrome://browser/content/browser-feeds.js",
  "chrome://browser/content/browser-media.js",
  "chrome://browser/content/browser-pageActions.js",
  "chrome://browser/content/browser-places.js",
  "chrome://browser/content/browser-plugins.js",
  "chrome://browser/content/browser-sidebar.js",
  "chrome://browser/content/browser-tabsintitlebar.js",
  "chrome://browser/content/browser-trackingprotection.js",
  "chrome://browser/content/browser-data-submission-info-bar.js",
]) {
  Services.scriptloader.loadSubScript(script, this);
}
</script>

<script type="application/javascript">
  Services.scriptloader.loadSubScript("chrome://global/content/contentAreaUtils.js", this);
  Services.scriptloader.loadSubScript("chrome://browser/content/tabbrowser.js", this);
</script>



  <stringbundleset id="stringbundleset">
    <stringbundle id="bundle_brand" src="chrome://branding/locale/brand.properties"/>
    <stringbundle id="bundle_shell" src="chrome://browser/locale/shellservice.properties"/>
    <stringbundle id="bundle_preferences" src="chrome://browser/locale/preferences/preferences.properties"/>
  </stringbundleset>

  <commandset id="mainCommandSet">
    <command id="cmd_newNavigator" oncommand="OpenBrowserWindow()"/>
    <command id="cmd_handleBackspace" oncommand="BrowserHandleBackspace();" />
    <command id="cmd_handleShiftBackspace" oncommand="BrowserHandleShiftBackspace();" />

    <command id="cmd_newNavigatorTab" oncommand="BrowserOpenTab(event);"/>
    <command id="cmd_newNavigatorTabNoEvent" oncommand="BrowserOpenTab();"/>
    <command id="Browser:OpenFile"  oncommand="BrowserOpenFileWindow();"/>
    <command id="Browser:SavePage" oncommand="saveBrowser(gBrowser.selectedBrowser);"/>

    <command id="Browser:SendLink"
             oncommand="MailIntegration.sendLinkForBrowser(gBrowser.selectedBrowser);"/>

    <command id="cmd_pageSetup" oncommand="PrintUtils.showPageSetup();"/>
    <command id="cmd_print" oncommand="PrintUtils.printWindow(window.gBrowser.selectedBrowser.outerWindowID, window.gBrowser.selectedBrowser);"/>
    <command id="cmd_printPreview" oncommand="PrintUtils.printPreview(PrintPreviewListener);"/>
    <command id="cmd_close" oncommand="BrowserCloseTabOrWindow(event);"/>
    <command id="cmd_closeWindow" oncommand="BrowserTryToCloseWindow()"/>
    <command id="cmd_toggleMute" oncommand="gBrowser.selectedTab.toggleMuteAudio()"/>
    <command id="cmd_CustomizeToolbars" oncommand="gCustomizeMode.enter()"/>
    <command id="cmd_quitApplication" oncommand="goQuitApplication()"/>

<script type="application/javascript" src="chrome://global/content/editMenuOverlay.js"/>
<commandset id="editMenuCommands">
  <commandset id="editMenuCommandSetAll" commandupdater="true" events="focus,select"
              oncommandupdate="goUpdateGlobalEditMenuItems()"/>
  <commandset id="editMenuCommandSetUndo" commandupdater="true" events="undo"
              oncommandupdate="goUpdateUndoEditMenuItems()"/>
  <commandset id="editMenuCommandSetPaste" commandupdater="true" events="clipboard"
              oncommandupdate="goUpdatePasteMenuItems()"/>
  <command id="cmd_undo" oncommand="goDoCommand('cmd_undo')"/>
  <command id="cmd_redo" oncommand="goDoCommand('cmd_redo')"/>
  <command id="cmd_cut" oncommand="goDoCommand('cmd_cut')"/>
  <command id="cmd_copy" oncommand="goDoCommand('cmd_copy')"/>
  <command id="cmd_paste" oncommand="goDoCommand('cmd_paste')"/>
  <command id="cmd_delete" oncommand="goDoCommand('cmd_delete')"/>
  <command id="cmd_selectAll" oncommand="goDoCommand('cmd_selectAll')"/>
  <command id="cmd_switchTextDirection" oncommand="goDoCommand('cmd_switchTextDirection');"/>
</commandset>

    <command id="View:PageSource" oncommand="BrowserViewSource(window.gBrowser.selectedBrowser);" observes="canViewSource"/>
    <command id="View:PageInfo" oncommand="BrowserPageInfo();"/>
    <command id="View:FullScreen" oncommand="BrowserFullScreen();"/>
    <command id="View:ReaderView" oncommand="ReaderParent.toggleReaderMode(event);"/>
    <command id="cmd_find"
             oncommand="gFindBar.onFindCommand();"
             observes="isImage"/>
    <command id="cmd_findAgain"
             oncommand="gFindBar.onFindAgainCommand(false);"
             observes="isImage"/>
    <command id="cmd_findPrevious"
             oncommand="gFindBar.onFindAgainCommand(true);"
             observes="isImage"/>
    <!-- work-around bug 392512 -->
    <command id="Browser:AddBookmarkAs"
             oncommand="PlacesCommandHook.bookmarkPage(gBrowser.selectedBrowser, true);"/>
    <!-- The command disabled state must be manually updated through
         PlacesCommandHook.updateBookmarkAllTabsCommand() -->
    <command id="Browser:BookmarkAllTabs"
             oncommand="PlacesCommandHook.bookmarkCurrentPages();"/>
    <command id="Browser:Home"    oncommand="BrowserHome();"/>
    <command id="Browser:Back"    oncommand="BrowserBack();" disabled="true"/>
    <command id="Browser:BackOrBackDuplicate" oncommand="BrowserBack(event);" disabled="true">
      <observes element="Browser:Back" attribute="disabled"/>
    </command>
    <command id="Browser:Forward" oncommand="BrowserForward();" disabled="true"/>
    <command id="Browser:ForwardOrForwardDuplicate" oncommand="BrowserForward(event);" disabled="true">
      <observes element="Browser:Forward" attribute="disabled"/>
    </command>
    <command id="Browser:Stop"    oncommand="BrowserStop();" disabled="true"/>
    <command id="Browser:Reload"  oncommand="if (event.shiftKey) BrowserReloadSkipCache(); else BrowserReload()" disabled="true"/>
    <command id="Browser:ReloadOrDuplicate" oncommand="BrowserReloadOrDuplicate(event)" disabled="true">
      <observes element="Browser:Reload" attribute="disabled"/>
    </command>
    <command id="Browser:ReloadSkipCache" oncommand="BrowserReloadSkipCache()" disabled="true">
      <observes element="Browser:Reload" attribute="disabled"/>
    </command>
    <command id="Browser:NextTab" oncommand="gBrowser.tabContainer.advanceSelectedTab(1, true);"/>
    <command id="Browser:PrevTab" oncommand="gBrowser.tabContainer.advanceSelectedTab(-1, true);"/>
    <command id="Browser:ShowAllTabs" oncommand="allTabs.open();"/>
    <command id="cmd_fullZoomReduce"  oncommand="FullZoom.reduce()"/>
    <command id="cmd_fullZoomEnlarge" oncommand="FullZoom.enlarge()"/>
    <command id="cmd_fullZoomReset"   oncommand="FullZoom.reset()"/>
    <command id="cmd_fullZoomToggle"  oncommand="ZoomManager.toggleZoom();"/>
    <command id="cmd_gestureRotateLeft" oncommand="gGestureSupport.rotate(event.sourceEvent)"/>
    <command id="cmd_gestureRotateRight" oncommand="gGestureSupport.rotate(event.sourceEvent)"/>
    <command id="cmd_gestureRotateEnd" oncommand="gGestureSupport.rotateEnd()"/>
    <command id="Browser:OpenLocation" oncommand="openLocation();"/>
    <command id="Browser:RestoreLastSession" oncommand="restoreLastSession();" disabled="true"/>
    <command id="Browser:NewUserContextTab" oncommand="openNewUserContextTab(event.sourceEvent);"/>
    <command id="Browser:OpenAboutContainers" oncommand="openPreferences('paneContainers', {origin: 'ContainersCommand'});"/>

    <command id="Tools:Search" oncommand="BrowserSearch.webSearch();"/>
    <command id="Tools:Downloads" oncommand="BrowserDownloadsUI();"/>
    <command id="Tools:Addons" oncommand="BrowserOpenAddonsMgr();"/>
    <command id="Tools:Sanitize" oncommand="Sanitizer.showUI(window);"/>
    <command id="Tools:PrivateBrowsing"
      oncommand="OpenBrowserWindow({private: true});"/>
    <command id="History:UndoCloseTab" oncommand="undoCloseTab();"/>
    <command id="History:UndoCloseWindow" oncommand="undoCloseWindow();"/>

  </commandset>

  <commandset id="placesCommands">
    <command id="Browser:ShowAllBookmarks"
             oncommand="PlacesCommandHook.showPlacesOrganizer('UnfiledBookmarks');"/>
    <command id="Browser:ShowAllHistory"
             oncommand="PlacesCommandHook.showPlacesOrganizer('History');"/>
  </commandset>

  <broadcasterset id="mainBroadcasterSet">
    <broadcaster id="viewBookmarksSidebar" autoCheck="false" label="&bookmarksButton.label;"
                 type="checkbox" group="sidebar" sidebarurl="chrome://browser/content/bookmarks/bookmarksPanel.xul"
                 oncommand="SidebarUI.toggle('viewBookmarksSidebar');"/>

    <!-- for both places and non-places, the sidebar lives at
         chrome://browser/content/history/history-panel.xul so there are no
         problems when switching between versions -->
    <broadcaster id="viewHistorySidebar" autoCheck="false" sidebartitle="&historyButton.label;"
                 type="checkbox" group="sidebar"
                 sidebarurl="chrome://browser/content/history/history-panel.xul"
                 oncommand="SidebarUI.toggle('viewHistorySidebar');"/>

    <broadcaster id="viewWebPanelsSidebar" autoCheck="false"
                 type="checkbox" group="sidebar" sidebarurl="chrome://browser/content/web-panels.xul"
                 oncommand="SidebarUI.toggle('viewWebPanelsSidebar');"/>

    <broadcaster id="bookmarkThisPageBroadcaster"
                 label="&bookmarkThisPageCmd.label;"
                 bookmarklabel="&bookmarkThisPageCmd.label;"
                 editlabel="&editThisBookmarkCmd.label;"/>

    <!-- popup blocking menu items -->
    <broadcaster id="blockedPopupAllowSite"
                 accesskey="&allowPopups.accesskey;"
                 oncommand="gPopupBlockerObserver.toggleAllowPopupsForSite(event);"/>
    <broadcaster id="blockedPopupEditSettings"
                 label="&editPopupSettingsUnix.label;"
                 accesskey="&editPopupSettings.accesskey;"
                 oncommand="gPopupBlockerObserver.editPopupSettings();"/>
    <broadcaster id="blockedPopupDontShowMessage"
                 accesskey="&dontShowMessage.accesskey;"
                 type="checkbox"
                 oncommand="gPopupBlockerObserver.dontShowMessage();"/>
    <broadcaster id="blockedPopupsSeparator"/>
    <broadcaster id="isImage"/>
    <broadcaster id="canViewSource"/>
    <broadcaster id="isFrameImage"/>
    <broadcaster id="singleFeedMenuitemState" disabled="true"/>
    <broadcaster id="multipleFeedsMenuState" hidden="true"/>

    <!-- Sync broadcasters -->
    <!-- A broadcaster of a number of attributes suitable for "sync now" UI -
        A 'syncstatus' attribute is set while actively syncing, and the label
        attribute which changes from "sync now" to "syncing" etc. -->
    <broadcaster id="sync-status"/>
    <!-- broadcasters of the "hidden" attribute to reflect setup state for
         menus -->
    <broadcaster id="sync-setup-state" hidden="true"/>
    <broadcaster id="sync-unverified-state" hidden="true"/>
    <broadcaster id="sync-syncnow-state" hidden="true"/>
    <broadcaster id="sync-reauth-state" hidden="true"/>
    <broadcaster id="viewTabsSidebar" autoCheck="false" sidebartitle="&syncedTabs.sidebar.label;"
                 type="checkbox" group="sidebar"
                 sidebarurl="chrome://browser/content/syncedtabs/sidebar.xhtml"
                 oncommand="SidebarUI.toggle('viewTabsSidebar');"/>
    <broadcaster id="workOfflineMenuitemState"/>

    <broadcaster id="devtoolsMenuBroadcaster_PageSource"
                 label="&pageSourceCmd.label;"
                 key="key_viewSource"
                 command="View:PageSource">
      <observes element="canViewSource" attribute="disabled"/>
    </broadcaster>
    <broadcaster id="reportPhishingBroadcaster" disabled="true"/>
    <broadcaster id="reportPhishingErrorBroadcaster" disabled="true"/>
  </broadcasterset>

  <keyset id="mainKeyset">
    <key id="key_newNavigator"
         key="&newNavigatorCmd.key;"
         command="cmd_newNavigator"
         modifiers="accel" reserved="true"/>
    <key id="key_newNavigatorTab" key="&tabCmd.commandkey;" modifiers="accel"
         command="cmd_newNavigatorTabNoEvent" reserved="true"/>
    <key id="focusURLBar" key="&openCmd.commandkey;" command="Browser:OpenLocation"
         modifiers="accel"/>
    <key id="focusURLBar2" key="&urlbar.accesskey;" command="Browser:OpenLocation"
         modifiers="alt"/>

    <key id="key_search" key="&searchFocus.commandkey;" command="Tools:Search" modifiers="accel"/>
    <key id="key_search2" key="&searchFocusUnix.commandkey;" command="Tools:Search" modifiers="accel"/>
    <key id="key_openDownloads" key="&downloadsUnix.commandkey;" command="Tools:Downloads" modifiers="accel,shift"/>
    <key id="key_openAddons" key="&addons.commandkey;" command="Tools:Addons" modifiers="accel,shift"/>
    <key id="openFileKb" key="&openFileCmd.commandkey;" command="Browser:OpenFile"  modifiers="accel"/>
    <key id="key_savePage" key="&savePageCmd.commandkey;" command="Browser:SavePage" modifiers="accel"/>
    <key id="printKb" key="&printCmd.commandkey;" command="cmd_print"  modifiers="accel"/>
    <key id="key_close" key="&closeCmd.key;" command="cmd_close" modifiers="accel" reserved="true"/>
    <key id="key_closeWindow" key="&closeCmd.key;" command="cmd_closeWindow" modifiers="accel,shift" reserved="true"/>
    <key id="key_toggleMute" key="&toggleMuteCmd.key;" command="cmd_toggleMute" modifiers="control"/>
    <key id="key_undo"
         key="&undoCmd.key;"
         modifiers="accel"/>
    <key id="key_redo" key="&undoCmd.key;" modifiers="accel,shift"/>
    <key id="key_cut"
         key="&cutCmd.key;"
         modifiers="accel"/>
    <key id="key_copy"
         key="&copyCmd.key;"
         modifiers="accel"/>
    <key id="key_paste"
         key="&pasteCmd.key;"
         modifiers="accel"/>
    <key id="key_delete" keycode="VK_DELETE" command="cmd_delete"/>
    <key id="key_selectAll" key="&selectAllCmd.key;" modifiers="accel"/>

    <key keycode="VK_BACK" command="cmd_handleBackspace"/>
    <key keycode="VK_BACK" command="cmd_handleShiftBackspace" modifiers="shift"/>
    <key id="goBackKb"  keycode="VK_LEFT" command="Browser:Back" modifiers="alt"/>
    <key id="goForwardKb"  keycode="VK_RIGHT" command="Browser:Forward" modifiers="alt"/>
    <key id="goBackKb2" key="&goBackCmd.commandKey;" command="Browser:Back" modifiers="accel"/>
    <key id="goForwardKb2" key="&goForwardCmd.commandKey;" command="Browser:Forward" modifiers="accel"/>
    <key id="goHome" keycode="VK_HOME" command="Browser:Home" modifiers="alt"/>
    <key keycode="VK_F5" command="Browser:Reload"/>
    <key id="showAllHistoryKb" key="&showAllHistoryCmd.commandkey;" command="Browser:ShowAllHistory" modifiers="accel,shift"/>
    <key keycode="VK_F5" command="Browser:ReloadSkipCache" modifiers="accel"/>
    <key id="key_fullScreen" keycode="VK_F11" command="View:FullScreen"/>
    <key id="key_toggleReaderMode" key="&toggleReaderMode.key;" command="View:ReaderView" modifiers="accel,alt" disabled="true"/>
    <key key="&reloadCmd.commandkey;" command="Browser:Reload" modifiers="accel" id="key_reload"/>
    <key key="&reloadCmd.commandkey;" command="Browser:ReloadSkipCache" modifiers="accel,shift"/>
    <key id="key_viewSource" key="&pageSourceCmd.commandkey;" command="View:PageSource" modifiers="accel"/>
    <key id="key_viewInfo"   key="&pageInfoCmd.commandkey;"   command="View:PageInfo"   modifiers="accel"/>
    <key id="key_find" key="&findOnCmd.commandkey;" command="cmd_find" modifiers="accel"/>
    <key id="key_findAgain" key="&findAgainCmd.commandkey;" command="cmd_findAgain" modifiers="accel"/>
    <key id="key_findPrevious" key="&findAgainCmd.commandkey;" command="cmd_findPrevious" modifiers="accel,shift"/>
    <key keycode="&findAgainCmd.commandkey2;" command="cmd_findAgain"/>
    <key keycode="&findAgainCmd.commandkey2;"  command="cmd_findPrevious" modifiers="shift"/>

    <key id="addBookmarkAsKb" key="&bookmarkThisPageCmd.commandkey;" command="Browser:AddBookmarkAs" modifiers="accel"/>
    <key id="manBookmarkKb" key="&bookmarksGtkCmd.commandkey;" command="Browser:ShowAllBookmarks" modifiers="accel,shift"/>
    <key id="viewBookmarksSidebarKb" key="&bookmarksCmd.commandkey;" command="viewBookmarksSidebar" modifiers="accel"/>

    <key id="key_stop" keycode="VK_ESCAPE" command="Browser:Stop"/>


    <key id="key_gotoHistory"
         key="&historySidebarCmd.commandKey;"
         modifiers="accel"
         command="viewHistorySidebar"/>

    <key id="key_fullZoomReduce"  key="&fullZoomReduceCmd.commandkey;"   command="cmd_fullZoomReduce"  modifiers="accel"/>
    <key                          key="&fullZoomReduceCmd.commandkey2;"  command="cmd_fullZoomReduce"  modifiers="accel"/>
    <key id="key_fullZoomEnlarge" key="&fullZoomEnlargeCmd.commandkey;"  command="cmd_fullZoomEnlarge" modifiers="accel"/>
    <key                          key="&fullZoomEnlargeCmd.commandkey2;" command="cmd_fullZoomEnlarge" modifiers="accel"/>
    <key                          key="&fullZoomEnlargeCmd.commandkey3;" command="cmd_fullZoomEnlarge" modifiers="accel"/>
    <key id="key_fullZoomReset"   key="&fullZoomResetCmd.commandkey;"    command="cmd_fullZoomReset"   modifiers="accel"/>
    <key                          key="&fullZoomResetCmd.commandkey2;"   command="cmd_fullZoomReset"   modifiers="accel"/>

    <key id="key_showAllTabs" command="Browser:ShowAllTabs" keycode="VK_TAB" modifiers="control,shift" disabled="true"/>

    <key id="key_switchTextDirection" key="&bidiSwitchTextDirectionItem.commandkey;" command="cmd_switchTextDirection" modifiers="accel,shift" />

    <key id="key_privatebrowsing" command="Tools:PrivateBrowsing" key="&privateBrowsingCmd.commandkey;"
         modifiers="accel,shift" reserved="true"/>
    <key id="key_sanitize" command="Tools:Sanitize" keycode="VK_DELETE" modifiers="accel,shift"/>
    <key id="key_quitApplication" key="&quitApplicationCmd.key;"
         modifiers="accel"
         command="cmd_quitApplication"
         reserved="true"/>

    <key id="key_undoCloseTab" command="History:UndoCloseTab" key="&tabCmd.commandkey;" modifiers="accel,shift"/>
    <key id="key_undoCloseWindow" command="History:UndoCloseWindow" key="&newNavigatorCmd.key;" modifiers="accel,shift"/>


<key id="key_selectTab1" oncommand="gBrowser.selectTabAtIndex(0, event);" key="1" modifiers="alt"/>
<key id="key_selectTab2" oncommand="gBrowser.selectTabAtIndex(1, event);" key="2" modifiers="alt"/>
<key id="key_selectTab3" oncommand="gBrowser.selectTabAtIndex(2, event);" key="3" modifiers="alt"/>
<key id="key_selectTab4" oncommand="gBrowser.selectTabAtIndex(3, event);" key="4" modifiers="alt"/>
<key id="key_selectTab5" oncommand="gBrowser.selectTabAtIndex(4, event);" key="5" modifiers="alt"/>
<key id="key_selectTab6" oncommand="gBrowser.selectTabAtIndex(5, event);" key="6" modifiers="alt"/>
<key id="key_selectTab7" oncommand="gBrowser.selectTabAtIndex(6, event);" key="7" modifiers="alt"/>
<key id="key_selectTab8" oncommand="gBrowser.selectTabAtIndex(7, event);" key="8" modifiers="alt"/>
<key id="key_selectLastTab" oncommand="gBrowser.selectTabAtIndex(-1, event);" key="9" modifiers="alt"/>

  </keyset>

  <popupset id="mainPopupSet">
    <menupopup id="tabContextMenu"
               onpopupshowing="if (event.target == this) TabContextMenu.updateContextMenu(this);"
               onpopuphidden="if (event.target == this) TabContextMenu.contextTab = null;">
      <menuitem id="context_reloadTab" label="&reloadTab.label;" accesskey="&reloadTab.accesskey;"
                oncommand="gBrowser.reloadTab(TabContextMenu.contextTab);"/>
      <menuitem id="context_toggleMuteTab" oncommand="TabContextMenu.contextTab.toggleMuteAudio();"/>
      <menuseparator/>
      <menuitem id="context_pinTab" label="&pinTab.label;"
                accesskey="&pinTab.accesskey;"
                oncommand="gBrowser.pinTab(TabContextMenu.contextTab);"/>
      <menuitem id="context_unpinTab" label="&unpinTab.label;" hidden="true"
                accesskey="&unpinTab.accesskey;"
                oncommand="gBrowser.unpinTab(TabContextMenu.contextTab);"/>
      <menuitem id="context_duplicateTab" label="&duplicateTab.label;"
                accesskey="&duplicateTab.accesskey;"
                oncommand="duplicateTabIn(TabContextMenu.contextTab, 'tab');"/>
      <menuitem id="context_openTabInWindow" label="&moveToNewWindow.label;"
                accesskey="&moveToNewWindow.accesskey;"
                tbattr="tabbrowser-multiple"
                oncommand="gBrowser.replaceTabWithWindow(TabContextMenu.contextTab);"/>
      <menuseparator id="context_sendTabToDevice_separator" class="sync-ui-item"/>
      <menu id="context_sendTabToDevice" label="&sendTabToDevice.label;"
            class="sync-ui-item"
            accesskey="&sendTabToDevice.accesskey;">
        <menupopup id="context_sendTabToDevicePopupMenu"
                   onpopupshowing="gSync.populateSendTabToDevicesMenu(event.target, TabContextMenu.contextTab.linkedBrowser.currentURI.spec, TabContextMenu.contextTab.linkedBrowser.contentTitle);"/>
      </menu>
      <menuseparator/>
      <menuitem id="context_reloadAllTabs" label="&reloadAllTabs.label;" accesskey="&reloadAllTabs.accesskey;"
                tbattr="tabbrowser-multiple-visible"
                oncommand="gBrowser.reloadAllTabs();"/>
      <menuitem id="context_bookmarkAllTabs"
                label="&bookmarkAllTabs.label;"
                accesskey="&bookmarkAllTabs.accesskey;"
                command="Browser:BookmarkAllTabs"/>
      <menuitem id="context_closeTabsToTheEnd" label="&closeTabsToTheEnd.label;" accesskey="&closeTabsToTheEnd.accesskey;"
                oncommand="gBrowser.removeTabsToTheEndFrom(TabContextMenu.contextTab, {animate: true});"/>
      <menuitem id="context_closeOtherTabs" label="&closeOtherTabs.label;" accesskey="&closeOtherTabs.accesskey;"
                oncommand="gBrowser.removeAllTabsBut(TabContextMenu.contextTab);"/>
      <menuseparator/>
      <menuitem id="context_undoCloseTab"
                label="&undoCloseTab.label;"
                accesskey="&undoCloseTab.accesskey;"
                observes="History:UndoCloseTab"/>
      <menuitem id="context_closeTab" label="&closeTab.label;" accesskey="&closeTab.accesskey;"
                oncommand="gBrowser.removeTab(TabContextMenu.contextTab, { animate: true });"/>
    </menupopup>

    <!-- bug 415444/582485: event.stopPropagation is here for the cloned version
         of this menupopup -->
    <menupopup id="backForwardMenu"
               onpopupshowing="return FillHistoryMenu(event.target);"
               oncommand="gotoHistoryIndex(event); event.stopPropagation();"
               onclick="checkForMiddleClick(this, event);"/>
    <tooltip id="aHTMLTooltip" page="true"/>
    <tooltip id="remoteBrowserTooltip"/>

    <!-- for search and content formfill/pw manager -->

    <panel type="autocomplete-richlistbox"
           id="PopupAutoComplete"
           role="group"
           noautofocus="true"
           hidden="true"
           overflowpadding="4"
           norolluponanchor="true"
           nomaxresults="true" />

    <!-- for search with one-off buttons -->
    <panel type="autocomplete-richlistbox"
           id="PopupSearchAutoComplete"
           role="group"
           noautofocus="true"
           hidden="true" />

    <!-- for url bar autocomplete -->
    <panel type="autocomplete-richlistbox"
           id="PopupAutoCompleteRichResult"
           role="group"
           noautofocus="true"
           hidden="true"
           flip="none"
           level="parent"
           overflowpadding="15" />

   <!-- for date/time picker. consumeoutsideclicks is set to never, so that
        clicks on the anchored input box are never consumed. -->
    <panel id="DateTimePickerPanel"
           type="arrow"
           hidden="true"
           orient="vertical"
           noautofocus="true"
           norolluponanchor="true"
           consumeoutsideclicks="never"
           level="parent"
           tabspecific="true">
    </panel>

    <!-- for select dropdowns. The menupopup is what shows the list of options,
         and the popuponly menulist makes things like the menuactive attributes
         work correctly on the menupopup. ContentSelectDropdown expects the
         popuponly menulist to be its immediate parent. -->
    <menulist popuponly="true" id="ContentSelectDropdown" hidden="true">
      <menupopup rolluponmousewheel="true"
                 activateontab="true" position="after_start"
                 level="parent"
        />
    </menulist>

    <!-- for invalid form error message -->
    <panel id="invalid-form-popup" type="arrow" orient="vertical" noautofocus="true" hidden="true" level="parent">
      <description/>
    </panel>

    <panel id="editBookmarkPanel"
           type="arrow"
           orient="vertical"
           ignorekeys="true"
           hidden="true"
           tabspecific="true"
           onpopupshown="StarUI.panelShown(event);"
           aria-labelledby="editBookmarkPanelTitle">
      <row id="editBookmarkPanelHeader" align="center" hidden="true">
        <vbox align="center">
          <image id="editBookmarkPanelStarIcon"/>
        </vbox>
        <vbox>
          <label id="editBookmarkPanelTitle"/>
          <description id="editBookmarkPanelDescription"/>
        </vbox>
      </row>
      <vbox id="editBookmarkPanelContent" flex="1" hidden="true"/>
      <hbox id="editBookmarkPanelBottomButtons" pack="end">
        <button id="editBookmarkPanelRemoveButton"
                class="editBookmarkPanelBottomButton"
                oncommand="StarUI.removeBookmarkButtonCommand();"
                accesskey="&editBookmark.removeBookmark.accessKey;"/>
        <button id="editBookmarkPanelDoneButton"
                class="editBookmarkPanelBottomButton"
                label="&editBookmark.done.label;"
                default="true"
                oncommand="StarUI.panel.hidePopup();"/>
      </hbox>
    </panel>

    <!-- UI tour experience -->
    <panel id="UITourTooltip"
           type="arrow"
           hidden="true"
           noautofocus="true"
           noautohide="true"
           align="start"
           orient="vertical"
           role="alert">
     <vbox>
      <hbox id="UITourTooltipBody">
        <image id="UITourTooltipIcon"/>
        <vbox flex="1">
          <hbox id="UITourTooltipTitleContainer">
            <label id="UITourTooltipTitle" flex="1"/>
            <toolbarbutton id="UITourTooltipClose" class="close-icon"
                           tooltiptext="&uiTour.infoPanel.close;"/>
          </hbox>
          <description id="UITourTooltipDescription" flex="1"/>
        </vbox>
      </hbox>
      <hbox id="UITourTooltipButtons" flex="1" align="center"/>
     </vbox>
    </panel>
    <!-- type="default" forces frames to be created so that the panel's size can be determined -->
    <panel id="UITourHighlightContainer"
           type="default"
           hidden="true"
           noautofocus="true"
           noautohide="true"
           flip="none"
           consumeoutsideclicks="false"
           mousethrough="always">
      <box id="UITourHighlight"></box>
    </panel>

    <panel id="sidebarMenu-popup"
           class="cui-widget-panel"
           role="group"
           type="arrow"
           hidden="true"
           flip="slide"
           orient="vertical"
           position="bottomcenter topleft">
      <toolbarbutton id="sidebar-switcher-bookmarks"
                     class="subviewbutton subviewbutton-iconic"
                     key="viewBookmarksSidebarKb"
                     observes="viewBookmarksSidebar"
                     oncommand="SidebarUI.show('viewBookmarksSidebar');">
        <observes element="viewBookmarksSidebar" attribute="checked"/>
      </toolbarbutton>
      <toolbarbutton id="sidebar-switcher-history"
                     label="&historyButton.label;"
                     class="subviewbutton subviewbutton-iconic"
                     key="key_gotoHistory"
                     observes="viewHistorySidebar"
                     oncommand="SidebarUI.show('viewHistorySidebar');">
        <observes element="viewHistorySidebar" attribute="checked"/>
      </toolbarbutton>
      <toolbarbutton id="sidebar-switcher-tabs"
                     label="&syncedTabs.sidebar.label;"
                     class="subviewbutton subviewbutton-iconic sync-ui-item"
                     observes="viewTabsSidebar"
                     oncommand="SidebarUI.show('viewTabsSidebar');">
        <observes element="viewTabsSidebar" attribute="checked"/>
      </toolbarbutton>
      <toolbarseparator/>
      <!-- Extension toolbarbuttons go here. -->
      <toolbarseparator id="sidebar-extensions-separator"/>
      <toolbarbutton id="sidebar-reverse-position"
                     class="subviewbutton"
                     oncommand="SidebarUI.reversePosition()"/>
      <toolbarseparator/>
      <toolbarbutton label="&sidebarMenuClose.label;"
                     class="subviewbutton"
                     oncommand="SidebarUI.hide()"/>
    </panel>

    <menupopup id="toolbar-context-menu"
               onpopupshowing="onViewToolbarsPopupShowing(event, document.getElementById('viewToolbarsMenuSeparator'));">
      <menuitem oncommand="gCustomizeMode.addToPanel(document.popupNode)"
                accesskey="&customizeMenu.pinToOverflowMenu.accesskey;"
                label="&customizeMenu.pinToOverflowMenu.label;"
                contexttype="toolbaritem"
                class="customize-context-moveToPanel"/>
      <menuitem oncommand="gCustomizeMode.removeFromArea(document.popupNode)"
                accesskey="&customizeMenu.removeFromToolbar.accesskey;"
                label="&customizeMenu.removeFromToolbar.label;"
                contexttype="toolbaritem"
                class="customize-context-removeFromToolbar"/>
      <menuitem id="toolbar-context-reloadAllTabs"
                class="toolbaritem-tabsmenu"
                contexttype="tabbar"
                oncommand="gBrowser.reloadAllTabs();"
                label="&toolbarContextMenu.reloadAllTabs.label;"
                accesskey="&toolbarContextMenu.reloadAllTabs.accesskey;"/>
      <menuitem id="toolbar-context-bookmarkAllTabs"
                class="toolbaritem-tabsmenu"
                contexttype="tabbar"
                command="Browser:BookmarkAllTabs"
                label="&toolbarContextMenu.bookmarkAllTabs.label;"
                accesskey="&toolbarContextMenu.bookmarkAllTabs.accesskey;"/>
      <menuitem id="toolbar-context-undoCloseTab"
                class="toolbaritem-tabsmenu"
                contexttype="tabbar"
                label="&toolbarContextMenu.undoCloseTab.label;"
                accesskey="&toolbarContextMenu.undoCloseTab.accesskey;"
                observes="History:UndoCloseTab"/>
      <menuseparator/>
      <menuseparator id="viewToolbarsMenuSeparator"/>
      <!-- XXXgijs: we're using oncommand handler here to avoid the event being
                    redirected to the command element, thus preventing
                    listeners on the menupopup or further up the tree from
                    seeing the command event pass by. The observes attribute is
                    here so that the menuitem is still disabled and re-enabled
                    correctly. -->
      <menuitem oncommand="gCustomizeMode.enter()"
                observes="cmd_CustomizeToolbars"
                class="viewCustomizeToolbar"
                label="&viewCustomizeToolbar.label;"
                accesskey="&viewCustomizeToolbar.accesskey;"/>
    </menupopup>

    <menupopup id="blockedPopupOptions"
               onpopupshowing="gPopupBlockerObserver.fillPopupList(event);"
               onpopuphiding="gPopupBlockerObserver.onPopupHiding(event);">
      <menuitem observes="blockedPopupAllowSite"/>
      <menuitem observes="blockedPopupEditSettings"/>
      <menuitem observes="blockedPopupDontShowMessage"/>
      <menuseparator observes="blockedPopupsSeparator"/>
    </menupopup>

    <menupopup id="autohide-context"
           onpopupshowing="FullScreen.getAutohide(this.firstChild);">
      <menuitem type="checkbox" label="&fullScreenAutohide.label;"
                accesskey="&fullScreenAutohide.accesskey;"
                oncommand="FullScreen.setAutohide();"/>
      <menuseparator/>
      <menuitem label="&fullScreenExit.label;"
                accesskey="&fullScreenExit.accesskey;"
                oncommand="BrowserFullScreen();"/>
    </menupopup>

    <menupopup id="contentAreaContextMenu" pagemenu="#page-menu-separator"
               onpopupshowing="if (event.target != this)
                                 return true;
                               gContextMenu = new nsContextMenu(this, event.shiftKey);
                               if (gContextMenu.shouldDisplay)
                                 updateEditUIVisibility();
                               return gContextMenu.shouldDisplay;"
               onpopuphiding="if (event.target != this)
                                return;
                              gContextMenu.hiding();
                              gContextMenu = null;
                              updateEditUIVisibility();">


      <menugroup id="context-navigation">
        <menuitem id="context-back"
                  class="menuitem-iconic"
                  tooltiptext="&backButton.tooltip;"
                  aria-label="&backCmd.label;"
                  command="Browser:BackOrBackDuplicate"
                  accesskey="&backCmd.accesskey;"
                  onclick="checkForMiddleClick(this, event);"/>
        <menuitem id="context-forward"
                  class="menuitem-iconic"
                  tooltiptext="&forwardButton.tooltip;"
                  aria-label="&forwardCmd.label;"
                  command="Browser:ForwardOrForwardDuplicate"
                  accesskey="&forwardCmd.accesskey;"
                  onclick="checkForMiddleClick(this, event);"/>
        <menuitem id="context-reload"
                  class="menuitem-iconic"
                  tooltip="dynamic-shortcut-tooltip"
                  aria-label="&reloadCmd.label;"
                  oncommand="gContextMenu.reload(event);"
                  accesskey="&reloadCmd.accesskey;"
                  onclick="checkForMiddleClick(this, event);"/>
        <menuitem id="context-stop"
                  class="menuitem-iconic"
                  tooltip="dynamic-shortcut-tooltip"
                  aria-label="&stopCmd.label;"
                  accesskey="&stopCmd.accesskey;"
                  command="Browser:Stop"/>
        <menuitem id="context-bookmarkpage"
                  class="menuitem-iconic"
                  observes="bookmarkThisPageBroadcaster"
                  aria-label="&bookmarkPageCmd2.label;"
                  accesskey="&bookmarkPageCmd2.accesskey;"
                  oncommand="gContextMenu.bookmarkThisPage();"/>
      </menugroup>
      <menuseparator id="context-sep-navigation"/>
      <menuseparator id="page-menu-separator"/>
      <menuitem id="spell-no-suggestions"
                disabled="true"
                label="&spellNoSuggestions.label;"/>
      <menuitem id="spell-add-to-dictionary"
                label="&spellAddToDictionary.label;"
                accesskey="&spellAddToDictionary.accesskey;"
                oncommand="InlineSpellCheckerUI.addToDictionary();"/>
      <menuitem id="spell-undo-add-to-dictionary"
                label="&spellUndoAddToDictionary.label;"
                accesskey="&spellUndoAddToDictionary.accesskey;"
                oncommand="InlineSpellCheckerUI.undoAddToDictionary();" />
      <menuseparator id="spell-suggestions-separator"/>
      <menuitem id="context-openlinkincurrent"
                label="&openLinkCmdInCurrent.label;"
                accesskey="&openLinkCmdInCurrent.accesskey;"
                oncommand="gContextMenu.openLinkInCurrent();"/>
      <menuitem id="context-openlinkincontainertab"
                accesskey="&openLinkCmdInTab.accesskey;"
                oncommand="gContextMenu.openLinkInTab(event);"/>
      <menuitem id="context-openlinkintab"
                label="&openLinkCmdInTab.label;"
                accesskey="&openLinkCmdInTab.accesskey;"
                data-usercontextid="0"
                oncommand="gContextMenu.openLinkInTab(event);"/>

      <menu id="context-openlinkinusercontext-menu"
            label="&openLinkCmdInContainerTab.label;"
            accesskey="&openLinkCmdInContainerTab.accesskey;"
            hidden="true">
        <menupopup oncommand="gContextMenu.openLinkInTab(event);"
                   onpopupshowing="return gContextMenu.createContainerMenu(event);" />
      </menu>

      <menuitem id="context-openlink"
                label="&openLinkCmd.label;"
                accesskey="&openLinkCmd.accesskey;"
                oncommand="gContextMenu.openLink();"/>
      <menuitem id="context-openlinkprivate"
                label="&openLinkInPrivateWindowCmd.label;"
                accesskey="&openLinkInPrivateWindowCmd.accesskey;"
                oncommand="gContextMenu.openLinkInPrivateWindow();"/>
      <menuseparator id="context-sep-open"/>
      <menuitem id="context-bookmarklink"
                label="&bookmarkThisLinkCmd.label;"
                accesskey="&bookmarkThisLinkCmd.accesskey;"
                oncommand="gContextMenu.bookmarkLink();"/>
      <menuitem id="context-savelink"
                label="&saveLinkCmd.label;"
                accesskey="&saveLinkCmd.accesskey;"
                oncommand="gContextMenu.saveLink();"/>
      <menuitem id="context-copyemail"
                label="&copyEmailCmd.label;"
                accesskey="&copyEmailCmd.accesskey;"
                oncommand="gContextMenu.copyEmail();"/>
      <menuitem id="context-copylink"
                label="&copyLinkCmd.label;"
                accesskey="&copyLinkCmd.accesskey;"
                oncommand="gContextMenu.copyLink();"/>
      <menuseparator id="context-sep-copylink"/>
      <menuitem id="context-media-play"
                label="&mediaPlay.label;"
                accesskey="&mediaPlay.accesskey;"
                oncommand="gContextMenu.mediaCommand('play');"/>
      <menuitem id="context-media-pause"
                label="&mediaPause.label;"
                accesskey="&mediaPause.accesskey;"
                oncommand="gContextMenu.mediaCommand('pause');"/>
      <menuitem id="context-media-mute"
                label="&mediaMute.label;"
                accesskey="&mediaMute.accesskey;"
                oncommand="gContextMenu.mediaCommand('mute');"/>
      <menuitem id="context-media-unmute"
                label="&mediaUnmute.label;"
                accesskey="&mediaUnmute.accesskey;"
                oncommand="gContextMenu.mediaCommand('unmute');"/>
      <menu id="context-media-playbackrate" label="&mediaPlaybackRate2.label;" accesskey="&mediaPlaybackRate2.accesskey;">
        <menupopup>
          <menuitem id="context-media-playbackrate-050x"
                    label="&mediaPlaybackRate050x2.label;"
                    accesskey="&mediaPlaybackRate050x2.accesskey;"
                    type="radio"
                    name="playbackrate"
                    oncommand="gContextMenu.mediaCommand('playbackRate', 0.5);"/>
          <menuitem id="context-media-playbackrate-100x"
                    label="&mediaPlaybackRate100x2.label;"
                    accesskey="&mediaPlaybackRate100x2.accesskey;"
                    type="radio"
                    name="playbackrate"
                    checked="true"
                    oncommand="gContextMenu.mediaCommand('playbackRate', 1.0);"/>
          <menuitem id="context-media-playbackrate-125x"
                    label="&mediaPlaybackRate125x2.label;"
                    accesskey="&mediaPlaybackRate125x2.accesskey;"
                    type="radio"
                    name="playbackrate"
                    oncommand="gContextMenu.mediaCommand('playbackRate', 1.25);"/>
          <menuitem id="context-media-playbackrate-150x"
                    label="&mediaPlaybackRate150x2.label;"
                    accesskey="&mediaPlaybackRate150x2.accesskey;"
                    type="radio"
                    name="playbackrate"
                    oncommand="gContextMenu.mediaCommand('playbackRate', 1.5);"/>
          <menuitem id="context-media-playbackrate-200x"
                    label="&mediaPlaybackRate200x2.label;"
                    accesskey="&mediaPlaybackRate200x2.accesskey;"
                    type="radio"
                    name="playbackrate"
                    oncommand="gContextMenu.mediaCommand('playbackRate', 2.0);"/>
        </menupopup>
      </menu>
      <menuitem id="context-media-loop"
                label="&mediaLoop.label;"
                accesskey="&mediaLoop.accesskey;"
                type="checkbox"
                oncommand="gContextMenu.mediaCommand('loop');"/>
      <menuitem id="context-media-showcontrols"
                label="&mediaShowControls.label;"
                accesskey="&mediaShowControls.accesskey;"
                oncommand="gContextMenu.mediaCommand('showcontrols');"/>
      <menuitem id="context-media-hidecontrols"
                label="&mediaHideControls.label;"
                accesskey="&mediaHideControls.accesskey;"
                oncommand="gContextMenu.mediaCommand('hidecontrols');"/>
      <menuitem id="context-video-fullscreen"
                accesskey="&videoFullScreen.accesskey;"
                label="&videoFullScreen.label;"
                oncommand="gContextMenu.mediaCommand('fullscreen');"/>
      <menuitem id="context-leave-dom-fullscreen"
                accesskey="&leaveDOMFullScreen.accesskey;"
                label="&leaveDOMFullScreen.label;"
                oncommand="gContextMenu.leaveDOMFullScreen();"/>
      <menuseparator id="context-media-sep-commands"/>
      <menuitem id="context-reloadimage"
                label="&reloadImageCmd.label;"
                accesskey="&reloadImageCmd.accesskey;"
                oncommand="gContextMenu.reloadImage();"/>
      <menuitem id="context-viewimage"
                label="&viewImageCmd.label;"
                accesskey="&viewImageCmd.accesskey;"
                oncommand="gContextMenu.viewMedia(event);"
                onclick="checkForMiddleClick(this, event);"/>
      <menuitem id="context-viewvideo"
                label="&viewVideoCmd.label;"
                accesskey="&viewVideoCmd.accesskey;"
                oncommand="gContextMenu.viewMedia(event);"
                onclick="checkForMiddleClick(this, event);"/>
      <menuitem id="context-copyimage-contents"
                label="&copyImageContentsCmd.label;"
                accesskey="&copyImageContentsCmd.accesskey;"
                oncommand="goDoCommand('cmd_copyImage');"/>
      <menuitem id="context-copyimage"
                label="&copyImageCmd.label;"
                accesskey="&copyImageCmd.accesskey;"
                oncommand="gContextMenu.copyMediaLocation();"/>
      <menuitem id="context-copyvideourl"
                label="&copyVideoURLCmd.label;"
                accesskey="&copyVideoURLCmd.accesskey;"
                oncommand="gContextMenu.copyMediaLocation();"/>
      <menuitem id="context-copyaudiourl"
                label="&copyAudioURLCmd.label;"
                accesskey="&copyAudioURLCmd.accesskey;"
                oncommand="gContextMenu.copyMediaLocation();"/>
      <menuseparator id="context-sep-copyimage"/>
      <menuitem id="context-saveimage"
                label="&saveImageCmd.label;"
                accesskey="&saveImageCmd.accesskey;"
                oncommand="gContextMenu.saveMedia();"/>
      <menuitem id="context-sendimage"
                label="&emailImageCmd.label;"
                accesskey="&emailImageCmd.accesskey;"
                oncommand="gContextMenu.sendMedia();"/>
      <menuitem id="context-setDesktopBackground"
                label="&setDesktopBackgroundCmd.label;"
                accesskey="&setDesktopBackgroundCmd.accesskey;"
                oncommand="gContextMenu.setDesktopBackground();"/>
      <menuitem id="context-viewimageinfo"
                label="&viewImageInfoCmd.label;"
                accesskey="&viewImageInfoCmd.accesskey;"
                oncommand="gContextMenu.viewImageInfo();"/>
      <menuitem id="context-viewimagedesc"
                label="&viewImageDescCmd.label;"
                accesskey="&viewImageDescCmd.accesskey;"
                oncommand="gContextMenu.viewImageDesc(event);"
                onclick="checkForMiddleClick(this, event);"/>
      <menuitem id="context-savevideo"
                label="&saveVideoCmd.label;"
                accesskey="&saveVideoCmd.accesskey;"
                oncommand="gContextMenu.saveMedia();"/>
      <menuitem id="context-saveaudio"
                label="&saveAudioCmd.label;"
                accesskey="&saveAudioCmd.accesskey;"
                oncommand="gContextMenu.saveMedia();"/>
      <menuitem id="context-video-saveimage"
                accesskey="&videoSaveImage.accesskey;"
                label="&videoSaveImage.label;"
                oncommand="gContextMenu.saveVideoFrameAsImage();"/>
      <menuitem id="context-sendvideo"
                label="&emailVideoCmd.label;"
                accesskey="&emailVideoCmd.accesskey;"
                oncommand="gContextMenu.sendMedia();"/>
      <menuitem id="context-sendaudio"
                label="&emailAudioCmd.label;"
                accesskey="&emailAudioCmd.accesskey;"
                oncommand="gContextMenu.sendMedia();"/>
      <menuitem id="context-ctp-play"
                label="&playPluginCmd.label;"
                accesskey="&playPluginCmd.accesskey;"
                oncommand="gContextMenu.playPlugin();"/>
      <menuitem id="context-ctp-hide"
                label="&hidePluginCmd.label;"
                accesskey="&hidePluginCmd.accesskey;"
                oncommand="gContextMenu.hidePlugin();"/>
      <menuseparator id="context-sep-ctp"/>
      <menuitem id="context-savepage"
                label="&savePageCmd.label;"
                accesskey="&savePageCmd.accesskey2;"
                oncommand="gContextMenu.savePageAs();"/>
      <menuseparator id="context-sep-sendpagetodevice" class="sync-ui-item"
                     hidden="true"/>
      <menu id="context-sendpagetodevice"
            class="sync-ui-item"
            label="&sendPageToDevice.label;"
            accesskey="&sendPageToDevice.accesskey;"
            hidden="true">
        <menupopup id="context-sendpagetodevice-popup"
                   onpopupshowing="(() => { let browser = gBrowser || getPanelBrowser(); gSync.populateSendTabToDevicesMenu(event.target, browser.currentURI.spec, browser.contentTitle); })()"/>
      </menu>
      <menuseparator id="context-sep-viewbgimage"/>
      <menuitem id="context-viewbgimage"
                label="&viewBGImageCmd.label;"
                accesskey="&viewBGImageCmd.accesskey;"
                oncommand="gContextMenu.viewBGImage(event);"
                onclick="checkForMiddleClick(this, event);"/>
      <menuitem id="context-undo"
                label="&undoCmd.label;"
                accesskey="&undoCmd.accesskey;"
                command="cmd_undo"/>
      <menuseparator id="context-sep-undo"/>
      <menuitem id="context-cut"
                label="&cutCmd.label;"
                accesskey="&cutCmd.accesskey;"
                command="cmd_cut"/>
      <menuitem id="context-copy"
                label="&copyCmd.label;"
                accesskey="&copyCmd.accesskey;"
                command="cmd_copy"/>
      <menuitem id="context-paste"
                label="&pasteCmd.label;"
                accesskey="&pasteCmd.accesskey;"
                command="cmd_paste"/>
      <menuitem id="context-delete"
                label="&deleteCmd.label;"
                accesskey="&deleteCmd.accesskey;"
                command="cmd_delete"/>
      <menuseparator id="context-sep-paste"/>
      <menuitem id="context-selectall"
                label="&selectAllCmd.label;"
                accesskey="&selectAllCmd.accesskey;"
                command="cmd_selectAll"/>
      <menuseparator id="context-sep-selectall"/>
      <menuitem id="context-keywordfield"
                label="&keywordfield.label;"
                accesskey="&keywordfield.accesskey;"
                oncommand="AddKeywordForSearchField();"/>
      <menuitem id="context-searchselect"
                oncommand="BrowserSearch.loadSearchFromContext(this.searchTerms);"/>
      <menuseparator id="context-sep-sendlinktodevice" class="sync-ui-item"
                     hidden="true"/>
      <menu id="context-sendlinktodevice"
            class="sync-ui-item"
            label="&sendLinkToDevice.label;"
            accesskey="&sendLinkToDevice.accesskey;"
            hidden="true">
        <menupopup id="context-sendlinktodevice-popup"
                   onpopupshowing="gSync.populateSendTabToDevicesMenu(event.target, gContextMenu.linkURL, gContextMenu.linkTextStr);"/>
      </menu>
      <menuseparator id="frame-sep"/>
      <menu id="frame" label="&thisFrameMenu.label;" accesskey="&thisFrameMenu.accesskey;">
        <menupopup>
          <menuitem id="context-showonlythisframe"
                    label="&showOnlyThisFrameCmd.label;"
                    accesskey="&showOnlyThisFrameCmd.accesskey;"
                    oncommand="gContextMenu.showOnlyThisFrame();"/>
          <menuitem id="context-openframeintab"
                    label="&openFrameCmdInTab.label;"
                    accesskey="&openFrameCmdInTab.accesskey;"
                    oncommand="gContextMenu.openFrameInTab();"/>
          <menuitem id="context-openframe"
                    label="&openFrameCmd.label;"
                    accesskey="&openFrameCmd.accesskey;"
                    oncommand="gContextMenu.openFrame();"/>
          <menuseparator id="open-frame-sep"/>
          <menuitem id="context-reloadframe"
                    label="&reloadFrameCmd.label;"
                    accesskey="&reloadFrameCmd.accesskey;"
                    oncommand="gContextMenu.reloadFrame(event);"/>
          <menuseparator/>
          <menuitem id="context-bookmarkframe"
                    label="&bookmarkThisFrameCmd.label;"
                    accesskey="&bookmarkThisFrameCmd.accesskey;"
                    oncommand="gContextMenu.addBookmarkForFrame();"/>
          <menuitem id="context-saveframe"
                    label="&saveFrameCmd.label;"
                    accesskey="&saveFrameCmd.accesskey;"
                    oncommand="gContextMenu.saveFrame();"/>
          <menuseparator/>
          <menuitem id="context-printframe"
                    label="&printFrameCmd.label;"
                    accesskey="&printFrameCmd.accesskey;"
                    oncommand="gContextMenu.printFrame();"/>
          <menuseparator/>
          <menuitem id="context-viewframesource"
                    label="&viewFrameSourceCmd.label;"
                    accesskey="&viewFrameSourceCmd.accesskey;"
                    oncommand="gContextMenu.viewFrameSource();"
                    observes="isFrameImage"/>
          <menuitem id="context-viewframeinfo"
                    label="&viewFrameInfoCmd.label;"
                    accesskey="&viewFrameInfoCmd.accesskey;"
                    oncommand="gContextMenu.viewFrameInfo();"/>
        </menupopup>
      </menu>
      <menuitem id="context-viewpartialsource-selection"
                label="&viewPartialSourceForSelectionCmd.label;"
                accesskey="&viewPartialSourceCmd.accesskey;"
                oncommand="gContextMenu.viewPartialSource('selection');"
                observes="isImage"/>
      <menuitem id="context-viewpartialsource-mathml"
                label="&viewPartialSourceForMathMLCmd.label;"
                accesskey="&viewPartialSourceCmd.accesskey;"
                oncommand="gContextMenu.viewPartialSource('mathml');"
                observes="isImage"/>
      <menuseparator id="context-sep-viewsource"/>
      <menuitem id="context-viewsource"
                label="&viewPageSourceCmd.label;"
                accesskey="&viewPageSourceCmd.accesskey;"
                oncommand="BrowserViewSource(gContextMenu.browser);"
                observes="canViewSource"/>
      <menuitem id="context-viewinfo"
                label="&viewPageInfoCmd.label;"
                accesskey="&viewPageInfoCmd.accesskey;"
                oncommand="gContextMenu.viewInfo();"/>
      <menuseparator id="spell-separator"/>
      <menuitem id="spell-check-enabled"
                label="&spellCheckToggle.label;"
                type="checkbox"
                accesskey="&spellCheckToggle.accesskey;"
                oncommand="InlineSpellCheckerUI.toggleEnabled(window);"/>
      <menuitem id="spell-add-dictionaries-main"
                label="&spellAddDictionaries.label;"
                accesskey="&spellAddDictionaries.accesskey;"
                oncommand="gContextMenu.addDictionaries();"/>
      <menu id="spell-dictionaries"
            label="&spellDictionaries.label;"
            accesskey="&spellDictionaries.accesskey;">
          <menupopup id="spell-dictionaries-menu">
              <menuseparator id="spell-language-separator"/>
              <menuitem id="spell-add-dictionaries"
                        label="&spellAddDictionaries.label;"
                        accesskey="&spellAddDictionaries.accesskey;"
                        oncommand="gContextMenu.addDictionaries();"/>
          </menupopup>
      </menu>
      <menuseparator hidden="true" id="context-sep-bidi"/>
      <menuitem hidden="true" id="context-bidi-text-direction-toggle"
                label="&bidiSwitchTextDirectionItem.label;"
                accesskey="&bidiSwitchTextDirectionItem.accesskey;"
                command="cmd_switchTextDirection"/>
      <menuitem hidden="true" id="context-bidi-page-direction-toggle"
                label="&bidiSwitchPageDirectionItem.label;"
                accesskey="&bidiSwitchPageDirectionItem.accesskey;"
                oncommand="gContextMenu.switchPageDirection();"/>
      <menuseparator id="fill-login-separator" hidden="true"/>
      <menu id="fill-login"
            label="&fillLoginMenu.label;"
            label-login="&fillLoginMenu.label;"
            label-password="&fillPasswordMenu.label;"
            label-username="&fillUsernameMenu.label;"
            accesskey="&fillLoginMenu.accesskey;"
            accesskey-login="&fillLoginMenu.accesskey;"
            accesskey-password="&fillPasswordMenu.accesskey;"
            accesskey-username="&fillUsernameMenu.accesskey;"
            hidden="true">
        <menupopup id="fill-login-popup">
          <menuitem id="fill-login-no-logins"
                    label="&noLoginSuggestions.label;"
                    disabled="true"
                    hidden="true"/>
          <menuseparator id="saved-logins-separator"/>
          <menuitem id="fill-login-saved-passwords"
                    label="&viewSavedLogins.label;"
                    oncommand="gContextMenu.openPasswordManager();"/>
        </menupopup>
      </menu>
      <menuseparator id="inspect-separator" hidden="true"/>
      <menuitem id="context-inspect"
                hidden="true"
                label="&inspectContextMenu.label;"
                accesskey="&inspectContextMenu.accesskey;"
                oncommand="gContextMenu.inspectNode();"/>
      <menuseparator id="context-media-eme-separator" hidden="true"/>
      <menuitem id="context-media-eme-learnmore"
                class="menuitem-iconic"
                hidden="true"
                label="&emeLearnMoreContextMenu.label;"
                accesskey="&emeLearnMoreContextMenu.accesskey;"
                oncommand="gContextMenu.drmLearnMore(event);"
                onclick="checkForMiddleClick(this, event);"/>
    </menupopup>

    <menupopup id="placesContext"/>

    <panel id="ctrlTab-panel" hidden="true" norestorefocus="true" level="top">
      <hbox>
        <button class="ctrlTab-preview" flex="1"/>
        <button class="ctrlTab-preview" flex="1"/>
        <button class="ctrlTab-preview" flex="1"/>
        <button class="ctrlTab-preview" flex="1"/>
        <button class="ctrlTab-preview" flex="1"/>
        <button class="ctrlTab-preview" flex="1"/>
      </hbox>
      <hbox pack="center">
        <button id="ctrlTab-showAll" class="ctrlTab-preview" noicon="true"/>
      </hbox>
    </panel>

    <panel id="pageActionPanel"
           class="cui-widget-panel"
           role="group"
           type="arrow"
           hidden="true"
           flip="slide"
           photon="true"
           position="bottomcenter topright"
           tabspecific="true"
           noautofocus="true"
           copyURL-title="&pageAction.copyLink.label;"
           emailLink-title="&emailPageCmd.label;"
           sendToDevice-title="&pageAction.sendTabToDevice.label;"
           sendToDevice-notReadyTitle="&sendToDevice.syncNotReady.label;">
      <panelmultiview id="pageActionPanelMultiView"
                      mainViewId="pageActionPanelMainView"
                      viewCacheId="appMenu-viewCache">
        <panelview id="pageActionPanelMainView"
                   context="pageActionContextMenu"
                   class="PanelUI-subView">
          <vbox class="panel-subview-body"/>
        </panelview>
      </panelmultiview>
    </panel>
    <panel id="pageActionFeedback"
           role="alert"
           type="arrow"
           hidden="true"
           flip="slide"
           position="bottomcenter topright"
           tabspecific="true"
           noautofocus="true"
           copyURLFeedback="&copyURLFeedback.label;"
           sendToDeviceFeedback="&sendToDeviceFeedback.label;"
           sendToDeviceOfflineFeedback="&sendToDeviceOfflineFeedback.label;">
      <hbox id="pageActionFeedbackAnimatableBox">
        <image id="pageActionFeedbackAnimatableImage"/>
      </hbox>
      <label id="pageActionFeedbackMessage"/>
    </panel>

    <menupopup id="pageActionContextMenu"
               onpopupshowing="BrowserPageActions.onContextMenuShowing(event, this);">
      <menuitem class="pageActionContextMenuItem builtInUnpinned"
                label="&pageAction.addToUrlbar.label;"
                oncommand="BrowserPageActions.togglePinningForContextAction();"/>
      <menuitem class="pageActionContextMenuItem builtInPinned"
                label="&pageAction.removeFromUrlbar.label;"
                oncommand="BrowserPageActions.togglePinningForContextAction();"/>
      <menuitem class="pageActionContextMenuItem extensionUnpinned"
                label="&pageAction.allowInUrlbar.label;"
                oncommand="BrowserPageActions.togglePinningForContextAction();"/>
      <menuitem class="pageActionContextMenuItem extensionPinned"
                label="&pageAction.disallowInUrlbar.label;"
                oncommand="BrowserPageActions.togglePinningForContextAction();"/>
      <menuseparator class="pageActionContextMenuItem extensionPinned extensionUnpinned"/>
      <menuitem class="pageActionContextMenuItem extensionPinned extensionUnpinned"
                label="&pageAction.manageExtension.label;"
                oncommand="BrowserPageActions.openAboutAddonsForContextAction();"/>
    </menupopup>

    <!-- Bookmarks and history tooltip -->
    <tooltip id="bhTooltip"/>

    <tooltip id="tabbrowser-tab-tooltip" onpopupshowing="gBrowser.createTooltip(event);"/>

    <tooltip id="back-button-tooltip">
      <label class="tooltip-label" value="&backButton.tooltip;"/>
      <label class="tooltip-label" value="&backForwardButtonMenu.tooltip;"/>
    </tooltip>

    <tooltip id="forward-button-tooltip">
      <label class="tooltip-label" value="&forwardButton.tooltip;"/>
      <label class="tooltip-label" value="&backForwardButtonMenu.tooltip;"/>
    </tooltip>


    <panel id="notification-popup"
           type="arrow"
           position="after_start"
           hidden="true"
           orient="vertical"
           noautofocus="true"
           role="alert"/>

    <popupnotification id="webRTC-shareDevices-notification" hidden="true">
      <popupnotificationcontent id="webRTC-selectCamera" orient="vertical">
        <label value="&getUserMedia.selectCamera.label;"
               accesskey="&getUserMedia.selectCamera.accesskey;"
               control="webRTC-selectCamera-menulist"/>
        <menulist id="webRTC-selectCamera-menulist">
          <menupopup id="webRTC-selectCamera-menupopup"/>
        </menulist>
      </popupnotificationcontent>

      <popupnotificationcontent id="webRTC-selectWindowOrScreen" orient="vertical">
        <label id="webRTC-selectWindow-label"
               control="webRTC-selectWindow-menulist"/>
        <menulist id="webRTC-selectWindow-menulist"
                  oncommand="webrtcUI.updateWarningLabel(this);">
          <menupopup id="webRTC-selectWindow-menupopup"/>
        </menulist>
        <description id="webRTC-all-windows-shared" hidden="true">&getUserMedia.allWindowsShared.message;</description>
      </popupnotificationcontent>

      <popupnotificationcontent id="webRTC-preview" hidden="true">
        <html:video id="webRTC-previewVideo"/>
        <vbox id="webRTC-previewWarningBox">
          <spacer flex="1"/>
          <description id="webRTC-previewWarning"/>
        </vbox>
      </popupnotificationcontent>

      <popupnotificationcontent id="webRTC-selectMicrophone" orient="vertical">
        <label value="&getUserMedia.selectMicrophone.label;"
               accesskey="&getUserMedia.selectMicrophone.accesskey;"
               control="webRTC-selectMicrophone-menulist"/>
        <menulist id="webRTC-selectMicrophone-menulist">
          <menupopup id="webRTC-selectMicrophone-menupopup"/>
        </menulist>
      </popupnotificationcontent>
    </popupnotification>

    <popupnotification id="servicesInstall-notification" hidden="true">
      <popupnotificationcontent orient="vertical" align="start">
        <!-- XXX bug 974146, tests are looking for this, can't remove yet. -->
      </popupnotificationcontent>
    </popupnotification>

    <popupnotification id="password-notification" hidden="true">
      <popupnotificationcontent orient="vertical">
        <textbox id="password-notification-username"/>
        <textbox id="password-notification-password" type="password" show-content=""/>
        <checkbox id="password-notification-visibilityToggle" hidden="true"/>
      </popupnotificationcontent>
    </popupnotification>


    <popupnotification id="addon-progress-notification" hidden="true">
      <popupnotificationcontent orient="vertical">
        <progressmeter id="addon-progress-notification-progressmeter"/>
        <label id="addon-progress-notification-progresstext" crop="end"/>
      </popupnotificationcontent>
    </popupnotification>

    <popupnotification id="addon-install-confirmation-notification" hidden="true">
      <popupnotificationcontent id="addon-install-confirmation-content" orient="vertical"/>
    </popupnotification>

    <popupnotification id="addon-webext-permissions-notification" hidden="true">
      <popupnotificationcontent class="addon-webext-perm-notification-content" orient="vertical">
        <description id="addon-webext-perm-text" class="addon-webext-perm-text"/>
        <label id="addon-webext-perm-intro" class="addon-webext-perm-text"/>
        <html:ul id="addon-webext-perm-list" class="addon-webext-perm-list"/>
      </popupnotificationcontent>
    </popupnotification>

    <popupnotification id="addon-installed-notification" hidden="true">
      <popupnotificationcontent class="addon-installed-notification-content" orient="vertical">
        <description>&addonPostInstallMessage.label;</description>
      </popupnotificationcontent>
    </popupnotification>

<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<panel id="widget-overflow"
       role="group"
       type="arrow"
       noautofocus="true"
       position="bottomcenter topright"
       photon="true"
       hidden="true">
  <panelmultiview mainViewId="widget-overflow-mainView" disablekeynav="true">
    <panelview id="widget-overflow-mainView"
               context="toolbar-context-menu">
      <vbox class="panel-subview-body">
        <vbox id="widget-overflow-list" class="widget-overflow-list"
              overflowfortoolbar="nav-bar"/>
        <toolbarseparator id="widget-overflow-fixed-separator" hidden="true"/>
        <vbox id="widget-overflow-fixed-list" class="widget-overflow-list" hidden="true" />
      </vbox>
      <toolbarbutton command="cmd_CustomizeToolbars"
                      id="overflowMenu-customize-button"
                      class="subviewbutton panel-subview-footer"
                      accesskey="&overflowCustomizeToolbar.accesskey;"
                      label="&overflowCustomizeToolbar.label;"/>
    </panelview>
  </panelmultiview>
  <!-- This menu is here because not having it in the menu in which it's used flickers
       when hover styles overlap. See https://bugzilla.mozilla.org/show_bug.cgi?id=1378427 .
       -->
  <menupopup id="customizationPanelItemContextMenu"
             onpopupshowing="gCustomizeMode.onPanelContextMenuShowing(event)">
    <menuitem oncommand="gCustomizeMode.addToPanel(document.popupNode)"
              id="customizationPanelItemContextMenuPin"
              accesskey="&customizeMenu.pinToOverflowMenu.accesskey;"
              label="&customizeMenu.pinToOverflowMenu.label;"
              closemenu="single"
              class="customize-context-moveToPanel"/>
    <menuitem oncommand="gCustomizeMode.addToToolbar(document.popupNode)"
              id="customizationPanelItemContextMenuUnpin"
              closemenu="single"
              class="customize-context-moveToToolbar"
              accesskey="&customizeMenu.unpinFromOverflowMenu.accesskey;"
              label="&customizeMenu.unpinFromOverflowMenu.label;"/>
    <menuitem oncommand="gCustomizeMode.removeFromArea(document.popupNode)"
              closemenu="single"
              class="customize-context-removeFromPanel"
              accesskey="&customizeMenu.removeFromToolbar.accesskey;"
              label="&customizeMenu.removeFromToolbar.label;"/>
    <menuseparator/>
    <menuitem command="cmd_CustomizeToolbars"
              class="viewCustomizeToolbar"
              accesskey="&viewCustomizeToolbar.accesskey;"
              label="&viewCustomizeToolbar.label;"/>
  </menupopup>
</panel>

<panel id="panic-button-success-notification"
       type="arrow"
       position="bottomcenter topright"
       hidden="true"
       role="alert"
       orient="vertical">
  <hbox id="panic-button-success-header">
    <image id="panic-button-success-icon" alt=""/>
    <vbox>
      <description>&panicButton.thankyou.msg1;</description>
      <description>&panicButton.thankyou.msg2;</description>
    </vbox>
  </hbox>
  <button label="&panicButton.thankyou.buttonlabel;"
          id="panic-button-success-closebutton"
          oncommand="PanicButtonNotifier.close()"/>
</panel>

<panel id="appMenu-notification-popup"
       class="popup-notification-panel"
       type="arrow"
       position="after_start"
       hidden="true"
       flip="slide"
       orient="vertical"
       noautofocus="true"
       noautohide="true"
       nopreventnavboxhide="true"
       role="alert">
  <popupnotification id="appMenu-update-available-notification"
                     popupid="update-available"
                     label="&updateAvailable.header.message;"
                     buttonlabel="&updateAvailable.acceptButton.label;"
                     buttonaccesskey="&updateAvailable.acceptButton.accesskey;"
                     closebuttonhidden="true"
                     secondarybuttonlabel="&updateAvailable.cancelButton.label;"
                     secondarybuttonaccesskey="&updateAvailable.cancelButton.accesskey;"
                     dropmarkerhidden="true"
                     checkboxhidden="true"
                     buttonhighlight="true"
                     hidden="true">
    <popupnotificationcontent id="update-available-notification-content" orient="vertical">
      <description id="update-available-description">&updateAvailable.message;</description>
      <label id="update-available-whats-new" class="text-link" value="&updateAvailable.whatsnew.label;" />
    </popupnotificationcontent>
  </popupnotification>

  <popupnotification id="appMenu-update-manual-notification"
                     popupid="update-manual"
                     label="&updateManual.header.message;"
                     buttonlabel="&updateManual.acceptButton.label;"
                     buttonaccesskey="&updateManual.acceptButton.accesskey;"
                     closebuttonhidden="true"
                     secondarybuttonlabel="&updateManual.cancelButton.label;"
                     secondarybuttonaccesskey="&updateManual.cancelButton.accesskey;"
                     dropmarkerhidden="true"
                     checkboxhidden="true"
                     buttonhighlight="true"
                     hidden="true">
    <popupnotificationcontent id="update-manual-notification-content" orient="vertical">
      <description id="update-manual-description">&updateManual.message;</description>
      <label id="update-manual-whats-new" class="text-link" value="&updateManual.whatsnew.label;" />
    </popupnotificationcontent>
  </popupnotification>

  <popupnotification id="appMenu-update-restart-notification"
                     popupid="update-restart"
                     label="&updateRestart.header.message2;"
                     buttonlabel="&updateRestart.acceptButton.label;"
                     buttonaccesskey="&updateRestart.acceptButton.accesskey;"
                     closebuttonhidden="true"
                     secondarybuttonlabel="&updateRestart.cancelButton.label;"
                     secondarybuttonaccesskey="&updateRestart.cancelButton.accesskey;"
                     dropmarkerhidden="true"
                     checkboxhidden="true"
                     buttonhighlight="true"
                     hidden="true">
    <popupnotificationcontent id="update-restart-notification-content" orient="vertical">
      <description id="update-restart-description">&updateRestart.message2;</description>
    </popupnotificationcontent>
  </popupnotification>
</panel>

<menupopup id="customizationPaletteItemContextMenu"
           onpopupshowing="gCustomizeMode.onPaletteContextMenuShowing(event)">
  <menuitem oncommand="gCustomizeMode.addToToolbar(document.popupNode)"
            class="customize-context-addToToolbar"
            accesskey="&customizeMenu.addToToolbar.accesskey;"
            label="&customizeMenu.addToToolbar.label;"/>
  <menuitem oncommand="gCustomizeMode.addToPanel(document.popupNode)"
            class="customize-context-addToPanel"
            accesskey="&customizeMenu.addToOverflowMenu.accesskey;"
            label="&customizeMenu.addToOverflowMenu.label;"/>
</menupopup>

<menupopup id="customizationPanelContextMenu">
  <menuitem command="cmd_CustomizeToolbars"
            accesskey="&customizeMenu.addMoreItems.accesskey;"
            label="&customizeMenu.addMoreItems.label;"/>
</menupopup>

<panel id="appMenu-popup"
       class="cui-widget-panel"
       role="group"
       type="arrow"
       hidden="true"
       flip="slide"
       position="bottomcenter topright"
       noautofocus="true">
  <panelmultiview id="appMenu-multiView" mainViewId="appMenu-mainView"
                  viewCacheId="appMenu-viewCache">
    <panelview id="appMenu-mainView" class="PanelUI-subView"
               descriptionheightworkaround="true">
      <vbox class="panel-subview-body">
        <vbox id="appMenu-addon-banners"/>
        <toolbarbutton class="panel-banner-item"
                       label-update-available="&updateAvailable.panelUI.label;"
                       label-update-manual="&updateManual.panelUI.label;"
                       label-update-restart="&updateRestart.panelUI.label2;"
                       oncommand="PanelUI._onBannerItemSelected(event)"
                       wrap="true"
                       hidden="true"/>
        <toolbaritem id="appMenu-fxa-container" class="toolbaritem-combined-buttons sync-ui-item">
          <hbox id="appMenu-fxa-status"
                flex="1"
                defaultlabel="&fxaSignIn.label;"
                signedinTooltiptext="&fxaSignedIn.tooltip;"
                tooltiptext="&fxaSignedIn.tooltip;"
                errorlabel="&fxaSignInError.label;"
                unverifiedlabel="&fxaUnverified.label;"
                onclick="if (event.which == 1) gSync.onMenuPanelCommand();">
            <image id="appMenu-fxa-avatar"/>
            <toolbarbutton id="appMenu-fxa-label"
                           class="subviewbutton subviewbutton-iconic"
                           label="&fxaSignIn.label;"
                           fxabrandname="&syncBrand.fxAccount.label;"/>
          </hbox>
          <toolbarseparator orient="vertical"/>
          <toolbarbutton id="appMenu-fxa-icon"
                         class="subviewbutton subviewbutton-iconic"
                         oncommand="gSync.doSync();"
                         closemenu="none">
            <observes element="sync-status" attribute="syncstatus"/>
            <observes element="sync-status" attribute="tooltiptext"/>
          </toolbarbutton>
        </toolbaritem>
        <toolbarseparator class="sync-ui-item"/>
        <toolbarbutton id="appMenu-new-window-button"
                       class="subviewbutton subviewbutton-iconic"
                       label="&newNavigatorCmd.label;"
                       key="key_newNavigator"
                       command="cmd_newNavigator"/>
        <toolbarbutton id="appMenu-private-window-button"
                       class="subviewbutton subviewbutton-iconic"
                       label="&newPrivateWindow.label;"
                       key="key_privatebrowsing"
                       command="Tools:PrivateBrowsing"/>
        <toolbarbutton id="appMenuRestoreLastSession"
                       label="&appMenuHistory.restoreSession.label;"
                       class="subviewbutton subviewbutton-iconic"
                       command="Browser:RestoreLastSession"/>
        <toolbarseparator/>
        <toolbaritem id="appMenu-zoom-controls" class="toolbaritem-combined-buttons" closemenu="none">
          <!-- Use a spacer, because panel sizing code gets confused when using CSS methods. -->
          <spacer class="before-label"/>
          <label value="&fullZoom.label;"/>
          <!-- This spacer keeps the scrollbar from overlapping the view. -->
          <spacer class="after-label"/>
          <toolbarbutton id="appMenu-zoomReduce-button"
                         class="subviewbutton subviewbutton-iconic"
                         command="cmd_fullZoomReduce"
                         tooltip="dynamic-shortcut-tooltip"/>
          <toolbarbutton id="appMenu-zoomReset-button"
                         class="subviewbutton"
                         command="cmd_fullZoomReset"
                         tooltip="dynamic-shortcut-tooltip"/>
          <toolbarbutton id="appMenu-zoomEnlarge-button"
                         class="subviewbutton subviewbutton-iconic"
                         command="cmd_fullZoomEnlarge"
                         tooltip="dynamic-shortcut-tooltip"/>
          <toolbarseparator orient="vertical"/>
          <toolbarbutton id="appMenu-fullscreen-button"
                         class="subviewbutton subviewbutton-iconic"
                         observes="View:FullScreen"
                         type="checkbox"
                         closemenu="auto"
                         onclick="if (event.button == 0) this.closest('panel').hidePopup();"
                         tooltip="dynamic-shortcut-tooltip"/>
        </toolbaritem>
        <toolbarseparator/>
        <toolbaritem id="appMenu-edit-controls" class="toolbaritem-combined-buttons" closemenu="none">
          <!-- Use a spacer, because panel sizing code gets confused when using CSS methods. -->
          <spacer class="before-label"/>
          <label value="&editMenu.label;"/>
          <!-- This spacer keeps the scrollbar from overlapping the view. -->
          <spacer class="after-label"/>
          <toolbarbutton id="appMenu-cut-button"
                         class="subviewbutton subviewbutton-iconic"
                         command="cmd_cut"
                         tooltip="dynamic-shortcut-tooltip"/>
          <toolbarbutton id="appMenu-copy-button"
                         class="subviewbutton subviewbutton-iconic"
                         command="cmd_copy"
                         tooltip="dynamic-shortcut-tooltip"/>
          <toolbarbutton id="appMenu-paste-button"
                         class="subviewbutton subviewbutton-iconic"
                         command="cmd_paste"
                         tooltip="dynamic-shortcut-tooltip"/>
        </toolbaritem>
        <toolbarseparator/>
        <toolbarbutton id="appMenu-library-button"
                       class="subviewbutton subviewbutton-iconic subviewbutton-nav"
                       label="&places.library.title;"
                       closemenu="none"
                       oncommand="PanelUI.showSubView('appMenu-libraryView', this)"/>
        <toolbarbutton id="appMenu-addons-button"
                       class="subviewbutton subviewbutton-iconic"
                       label="&addons.label;"
                       key="key_openAddons"
                       command="Tools:Addons"
                       />
        <toolbarbutton id="appMenu-preferences-button"
                       class="subviewbutton subviewbutton-iconic"
                       label="&preferencesCmdUnix.label;"
                       oncommand="openPreferences()"
                       />
        <toolbarbutton id="appMenu-customize-button"
                       class="subviewbutton subviewbutton-iconic"
                       label="&viewCustomizeToolbar.label;"
                       command="cmd_CustomizeToolbars"
                       />
        <toolbarseparator/>
        <toolbarbutton id="appMenu-open-file-button"
                       class="subviewbutton"
                       label="&openFileCmd.label;"
                       key="openFileKb"
                       command="Browser:OpenFile"
                       />
        <toolbarbutton id="appMenu-save-file-button"
                       class="subviewbutton"
                       label="&savePageCmd.label;"
                       key="key_savePage"
                       command="Browser:SavePage"
                       />
        <toolbarbutton id="appMenu-print-button"
                       class="subviewbutton subviewbutton-iconic"
                       label="&printCmd.label;"
                       command="cmd_printPreview"
                       />
        <toolbarseparator/>
        <toolbarbutton id="appMenu-find-button"
                       class="subviewbutton subviewbutton-iconic"
                       label="&findOnCmd.label;"
                       key="key_find"
                       command="cmd_find"/>
        <toolbarbutton id="appMenu-more-button"
                       class="subviewbutton subviewbutton-nav"
                       label="&moreMenu.label;"
                       closemenu="none"
                       oncommand="PanelUI.showSubView('appMenu-moreView', this)"/>
        <toolbarbutton id="appMenu-developer-button"
                       class="subviewbutton subviewbutton-nav"
                       label="&webDeveloperMenu.label;"
                       closemenu="none"
                       oncommand="PanelUI.showSubView('PanelUI-developer', this)"/>
        <toolbarbutton id="appMenu-help-button"
                       class="subviewbutton subviewbutton-iconic subviewbutton-nav"
                       label="&appMenuHelp.label;"
                       closemenu="none"
                       oncommand="PanelUI.showSubView('PanelUI-helpView', this)"/>
        <toolbarseparator/>
        <toolbarbutton id="appMenu-quit-button"
                       class="subviewbutton subviewbutton-iconic"
                       label="&quitApplicationCmd.label;"
                       key="key_quitApplication"
                       command="cmd_quitApplication"/>
      </vbox>
    </panelview>
    <panelview id="PanelUI-history" flex="1">
      <vbox class="panel-subview-body">
        <toolbarbutton id="appMenuViewHistorySidebar"
                       label="&appMenuHistory.viewSidebar.label;"
                       type="checkbox"
                       class="subviewbutton subviewbutton-iconic"
                       key="key_gotoHistory"
                       oncommand="SidebarUI.toggle('viewHistorySidebar'); PanelUI.hide();">
          <observes element="viewHistorySidebar" attribute="checked"/>
        </toolbarbutton>
        <toolbarbutton id="appMenuClearRecentHistory"
                       label="&appMenuHistory.clearRecent.label;"
                       class="subviewbutton subviewbutton-iconic"
                       command="Tools:Sanitize"/>
        <toolbarseparator/>
        <toolbarbutton id="appMenuRecentlyClosedTabs"
                       label="&historyUndoMenu.label;"
                       class="subviewbutton subviewbutton-iconic subviewbutton-nav"
                       closemenu="none"
                       oncommand="PanelUI.showSubView('appMenu-library-recentlyClosedTabs', this)"/>
        <toolbarbutton id="appMenuRecentlyClosedWindows"
                       label="&historyUndoWindowMenu.label;"
                       class="subviewbutton subviewbutton-iconic subviewbutton-nav"
                       closemenu="none"
                       oncommand="PanelUI.showSubView('appMenu-library-recentlyClosedWindows', this)"/>
        <toolbarseparator/>
        <label value="&appMenuHistory.recentHistory.label;"
               class="subview-subheader"/>
        <toolbaritem id="appMenu_historyMenu"
                     orient="vertical"
                     smoothscroll="false"
                     flatList="true"
                     tooltip="bhTooltip">
          <!-- history menu items will go here -->
        </toolbaritem>
      </vbox>
      <toolbarbutton id="PanelUI-historyMore"
                     class="panel-subview-footer subviewbutton"
                     label="&appMenuHistory.showAll.label;"
                     oncommand="PlacesCommandHook.showPlacesOrganizer('History'); CustomizableUI.hidePanelForNode(this);"/>
    </panelview>

    <panelview id="appMenu-library-recentlyClosedTabs"/>
    <panelview id="appMenu-library-recentlyClosedWindows"/>

    <panelview id="PanelUI-remotetabs" flex="1" class="PanelUI-subView"
               descriptionheightworkaround="true">
      <vbox class="panel-subview-body">
        <!-- this widget has 3 boxes in the body, but only 1 is ever visible -->
        <!-- When Sync is ready to sync -->
        <vbox id="PanelUI-remotetabs-main" observes="sync-syncnow-state">
          <vbox id="PanelUI-remotetabs-buttons">
            <toolbarbutton id="PanelUI-remotetabs-view-sidebar"
                           class="subviewbutton subviewbutton-iconic"
                           observes="viewTabsSidebar"
                           label="&appMenuRemoteTabs.sidebar.label;"/>
            <toolbarbutton id="PanelUI-remotetabs-view-managedevices"
                           class="subviewbutton subviewbutton-iconic"
                           label="&appMenuRemoteTabs.managedevices.label;"
                           oncommand="gSync.openDevicesManagementPage('syncedtabs-menupanel');"/>
            <toolbarbutton id="PanelUI-remotetabs-syncnow"
                           observes="sync-status"
                           class="subviewbutton subviewbutton-iconic"
                           oncommand="gSync.doSync();"
                           closemenu="none"/>
            <menuseparator id="PanelUI-remotetabs-separator"/>
          </vbox>
          <deck id="PanelUI-remotetabs-deck">
            <!-- Sync is ready to Sync and the "tabs" engine is enabled -->
            <vbox id="PanelUI-remotetabs-tabspane">
              <vbox id="PanelUI-remotetabs-tabslist"
                    showAllLabel="&appMenuRemoteTabs.showAll.label;"
                    showAllTooltipText="&appMenuRemoteTabs.showAll.tooltip;"
                    showMoreLabel="&appMenuRemoteTabs.showMore.label;"
                    showMoreTooltipText="&appMenuRemoteTabs.showMore.tooltip;"
                    notabsforclientlabel="&appMenuRemoteTabs.notabs.label;"
                    />
            </vbox>
            <!-- Sync is ready to Sync but the "tabs" engine isn't enabled-->
            <hbox id="PanelUI-remotetabs-tabsdisabledpane" pack="center" flex="1">
              <vbox class="PanelUI-remotetabs-instruction-box" align="center">
                <hbox pack="center">
                  <image class="fxaSyncIllustrationIssue"/>
                </hbox>
                <label class="PanelUI-remotetabs-instruction-label">&appMenuRemoteTabs.tabsnotsyncing.label;</label>
                <hbox pack="center">
                  <toolbarbutton class="PanelUI-remotetabs-button"
                                 label="&appMenuRemoteTabs.opensyncprefs.label;"
                                 oncommand="gSync.openPrefs('synced-tabs');"/>
                </hbox>
              </vbox>
            </hbox>
            <!-- Sync is ready to Sync but we are still fetching the tabs to show -->
            <vbox id="PanelUI-remotetabs-fetching">
              <!-- Show intentionally blank panel, see bug 1239845 -->
            </vbox>
            <!-- Sync has only 1 (ie, this) device connected -->
            <hbox id="PanelUI-remotetabs-nodevicespane" pack="center" flex="1">
              <vbox class="PanelUI-remotetabs-instruction-box" align="center">
                <hbox pack="center">
                  <image class="fxaSyncIllustrationIssue"/>
                </hbox>
                <label class="PanelUI-remotetabs-instruction-label">&appMenuRemoteTabs.noclients.subtitle;</label>
                <toolbarbutton id="PanelUI-remotetabs-connect-device-button"
                               class="PanelUI-remotetabs-button"
                               label="&appMenuRemoteTabs.connectdevice.label;"
                               oncommand="gSync.openConnectAnotherDevice('synced-tabs');"/>
              </vbox>
            </hbox>
          </deck>
        </vbox>
        <!-- a box to ensure contained boxes are centered horizonally -->
        <hbox pack="center" flex="1">
          <!-- When Sync is not configured -->
          <vbox id="PanelUI-remotetabs-setupsync"
                flex="1"
                align="center"
                class="PanelUI-remotetabs-instruction-box"
                observes="sync-setup-state">
            <image class="fxaSyncIllustration"/>
            <label class="PanelUI-remotetabs-instruction-label">&appMenuRemoteTabs.notsignedin.label;</label>
            <toolbarbutton class="PanelUI-remotetabs-button"
                           label="&appMenuRemoteTabs.signin.label;"
                           oncommand="gSync.openPrefs('synced-tabs');"/>
          </vbox>
          <!-- When Sync needs re-authentication. This uses the exact same messaging
               as "Sync is not configured" but remains a separate box so we get
               the goodness of observing broadcasters to manage the hidden states -->
          <vbox id="PanelUI-remotetabs-reauthsync"
                flex="1"
                align="center"
                class="PanelUI-remotetabs-instruction-box"
                observes="sync-reauth-state">
            <image class="fxaSyncIllustrationIssue"/>
            <label class="PanelUI-remotetabs-instruction-label">&appMenuRemoteTabs.notsignedin.label;</label>
            <toolbarbutton class="PanelUI-remotetabs-button"
                           label="&appMenuRemoteTabs.signin.label;"
                           oncommand="gSync.openPrefs('synced-tabs');"/>
          </vbox>
          <vbox id="PanelUI-remotetabs-unverified"
                flex="1"
                align="center"
                class="PanelUI-remotetabs-instruction-box"
                observes="sync-unverified-state">
            <image class="fxaSyncIllustrationIssue"/>
            <label class="PanelUI-remotetabs-instruction-label">&appMenuRemoteTabs.unverified.label;</label>
            <toolbarbutton class="PanelUI-remotetabs-button"
                           label="&appMenuRemoteTabs.opensyncprefs.label;"
                           oncommand="gSync.openPrefs('synced-tabs');"/>
          </vbox>
        </hbox>
      </vbox>
    </panelview>

    <panelview id="PanelUI-bookmarks" flex="1" class="PanelUI-subView">
      <vbox class="panel-subview-body">
        <toolbarbutton id="panelMenuBookmarkThisPage"
                       class="subviewbutton subviewbutton-iconic"
                       observes="bookmarkThisPageBroadcaster"
                       command="Browser:AddBookmarkAs"
                       onclick="PanelUI.hide();"/>
        <toolbarbutton id="panelMenu_bookmarkingTools"
                       label="&bookmarkingTools.label;"
                       class="subviewbutton subviewbutton-iconic subviewbutton-nav"
                       closemenu="none"
                       oncommand="BookmarkingUI.showBookmarkingTools(this);"/>
        <toolbarbutton id="panelMenu_searchBookmarks"
                       label="&searchBookmarks.label;"
                       class="subviewbutton subviewbutton-iconic"
                       oncommand="PlacesCommandHook.searchBookmarks(); PanelUI.hide();"/>
        <toolbarseparator/>
        <label id="panelMenu_recentBookmarks"
               value="&recentBookmarks.label;"
               class="subview-subheader"/>
        <toolbaritem id="panelMenu_bookmarksMenu"
                     orient="vertical"
                     smoothscroll="false"
                     flatList="true"
                     tooltip="bhTooltip">
          <!-- bookmarks menu items will go here -->
        </toolbaritem>
      </vbox>
      <toolbarbutton id="panelMenu_showAllBookmarks"
                     label="&showAllBookmarks2.label;"
                     class="subviewbutton panel-subview-footer"
                     command="Browser:ShowAllBookmarks"
                     onclick="PanelUI.hide();"/>
    </panelview>

    <panelview id="PanelUI-feeds" flex="1" oncommand="FeedHandler.subscribeToFeed(null, event);">
    </panelview>

    <panelview id="PanelUI-containers" flex="1">
      <vbox id="PanelUI-containersItems"/>
    </panelview>

    <panelview id="PanelUI-helpView" flex="1" class="PanelUI-subView">
      <vbox id="PanelUI-helpItems" class="panel-subview-body"/>
    </panelview>

    <panelview id="PanelUI-developer" flex="1">
      <vbox id="PanelUI-developerItems" class="panel-subview-body"/>
    </panelview>

    <panelview id="PanelUI-characterEncodingView" flex="1">
      <vbox class="panel-subview-body">
        <vbox id="PanelUI-characterEncodingView-pinned"
              class="PanelUI-characterEncodingView-list"/>
        <toolbarseparator/>
        <vbox id="PanelUI-characterEncodingView-charsets"
              class="PanelUI-characterEncodingView-list"/>
        <toolbarseparator/>
        <vbox>
          <label id="PanelUI-characterEncodingView-autodetect-label"/>
          <vbox id="PanelUI-characterEncodingView-autodetect"
                class="PanelUI-characterEncodingView-list"/>
        </vbox>
      </vbox>
    </panelview>

    <panelview id="PanelUI-panicView" flex="1"
               descriptionheightworkaround="true">
      <vbox class="panel-subview-body">
        <hbox id="PanelUI-panic-timeframe">
          <image id="PanelUI-panic-timeframe-icon" alt=""/>
          <vbox flex="1">
            <description id="PanelUI-panic-mainDesc">&panicButton.view.mainTimeframeDesc;</description>
            <radiogroup id="PanelUI-panic-timeSpan" aria-labelledby="PanelUI-panic-mainDesc" closemenu="none">
              <radio id="PanelUI-panic-5min" label="&panicButton.view.5min;" selected="true"
                     value="5" class="subviewradio"/>
              <radio id="PanelUI-panic-2hr" label="&panicButton.view.2hr;"
                     value="2" class="subviewradio"/>
              <radio id="PanelUI-panic-day" label="&panicButton.view.day;"
                     value="6" class="subviewradio"/>
            </radiogroup>
          </vbox>
        </hbox>
        <vbox id="PanelUI-panic-explanations">
          <label id="PanelUI-panic-actionlist-main-label">&panicButton.view.mainActionDesc;</label>

          <label id="PanelUI-panic-actionlist-windows" class="PanelUI-panic-actionlist">&panicButton.view.deleteTabsAndWindows;</label>
          <label id="PanelUI-panic-actionlist-cookies" class="PanelUI-panic-actionlist">&panicButton.view.deleteCookies;</label>
          <label id="PanelUI-panic-actionlist-history" class="PanelUI-panic-actionlist">&panicButton.view.deleteHistory;</label>
          <label id="PanelUI-panic-actionlist-newwindow" class="PanelUI-panic-actionlist">&panicButton.view.openNewWindow;</label>

          <label id="PanelUI-panic-warning">&panicButton.view.undoWarning;</label>
        </vbox>
        <button id="PanelUI-panic-view-button"
                label="&panicButton.view.forgetButton;"/>
      </vbox>
    </panelview>

    <panelview id="appMenu-moreView" title="&moreMenu.label;" class="PanelUI-subView">
      <vbox class="panel-subview-body">
        <toolbarbutton id="appMenu-characterencoding-button"
                       class="subviewbutton subviewbutton-nav"
                       label="&charsetMenu2.label;"
                       closemenu="none"
                       oncommand="PanelUI.showSubView('PanelUI-characterEncodingView', this)"/>
        <toolbarbutton id="appMenu-workoffline-button"
                       class="subviewbutton"
                       label="&goOfflineCmd.label;"
                       type="checkbox"
                       observes="workOfflineMenuitemState"
                       oncommand="BrowserOffline.toggleOfflineStatus();"/>
      </vbox>
    </panelview>
    <panelview id="appMenu-libraryView" class="PanelUI-subView">
      <vbox class="panel-subview-body">
        <toolbarbutton id="appMenu-library-bookmarks-button"
                       class="subviewbutton subviewbutton-iconic subviewbutton-nav"
                       label="&bookmarksSubview.label;"
                       closemenu="none"
                       oncommand="BookmarkingUI.showSubView(this);"/>
        <toolbarbutton id="appMenu-library-history-button"
                       class="subviewbutton subviewbutton-iconic subviewbutton-nav"
                       label="&historyMenu.label;"
                       closemenu="none"
                       oncommand="PanelUI.showSubView('PanelUI-history', this)"/>
        <toolbarbutton id="appMenu-library-downloads-button"
                       class="subviewbutton subviewbutton-iconic subviewbutton-nav"
                       label="&libraryDownloads.label;"
                       closemenu="none"
                       oncommand="DownloadsSubview.show(this);"/>
        <toolbarbutton id="appMenu-library-remotetabs-button"
                       class="subviewbutton subviewbutton-iconic subviewbutton-nav sync-ui-item"
                       label="&appMenuRemoteTabs.label;"
                       closemenu="none"
                       oncommand="PanelUI.showSubView('PanelUI-remotetabs', this)"/>
        <toolbarseparator hidden="true"/>
        <label value="&appMenuRecentHighlights.label;"
               hidden="true"
               class="subview-subheader"/>
        <toolbaritem id="appMenu-library-recentHighlights"
                     hidden="true"
                     orient="vertical"
                     smoothscroll="false"
                     flatList="true"
                     tooltip="bhTooltip">
          <!-- Recent Highlights will go here -->
        </toolbaritem>
      </vbox>
    </panelview>

    <panelview id="PanelUI-bookmarkingTools" class="PanelUI-subView">
      <vbox class="panel-subview-body">
        <toolbarbutton id="panelMenu_toggleBookmarksMenu"
                       class="subviewbutton subviewbutton-iconic"
                       label-show="&addBookmarksMenu.label;"
                       label-hide="&removeBookmarksMenu.label;"
                       oncommand="BookmarkingUI.toggleMenuButtonInToolbar(this);"/>
        <toolbarbutton id="panelMenu_viewBookmarksSidebar"
                       class="subviewbutton subviewbutton-iconic"
                       label-show="&viewBookmarksSidebar2.label;"
                       label-hide="&hideBookmarksSidebar.label;"
                       key="viewBookmarksSidebarKb"
                       oncommand="SidebarUI.toggle('viewBookmarksSidebar', this);"/>
        <toolbarbutton id="panelMenu_viewBookmarksToolbar"
                       class="subviewbutton subviewbutton-iconic"
                       label-show="&viewBookmarksToolbar.label;"
                       label-hide="&hideBookmarksToolbar.label;"
                       oncommand="BookmarkingUI.toggleBookmarksToolbar();"/>
      </vbox>
    </panelview>
  </panelmultiview>
</panel>

<panel id="downloads-button-autohide-panel"
       role="group"
       type="arrow"
       hidden="true"
       onpopupshown="gCustomizeMode._downloadPanelAutoHideTimeout = setTimeout(() => event.target.hidePopup(), 4000);"
       onmouseover="clearTimeout(gCustomizeMode._downloadPanelAutoHideTimeout);"
       onmouseout="gCustomizeMode._downloadPanelAutoHideTimeout = setTimeout(() => event.target.hidePopup(), 2000);"
       onpopuphidden="clearTimeout(gCustomizeMode._downloadPanelAutoHideTimeout);"
       >
  <checkbox id="downloads-button-autohide-checkbox"
            label="&customizeMode.autoHideDownloadsButton.label;" checked="true"
            oncommand="gCustomizeMode.onDownloadsAutoHideChange(event)"/>
</panel>

<panel id="extension-notification-panel"
       role="group"
       type="arrow"
       hidden="true"
       flip="slide"
       position="bottomcenter topright"
       tabspecific="true">
  <popupnotification id="extension-new-tab-notification"
                     popupid="extension-new-tab"
                     label="&newTabControlled.header.message;"
                     buttonlabel="&newTabControlled.keepButton.label;"
                     buttonaccesskey="&newTabControlled.keepButton.accesskey;"
                     secondarybuttonlabel="&newTabControlled.disableButton.label;"
                     secondarybuttonaccesskey="&newTabControlled.disableButton.accesskey;"
                     closebuttonhidden="true"
                     dropmarkerhidden="true"
                     checkboxhidden="true">
    <popupnotificationcontent orient="vertical">
      <description id="extension-new-tab-notification-description"/>
    </popupnotificationcontent>
  </popupnotification>
</panel>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<panel id="identity-popup"
       type="arrow"
       hidden="true"
       photon="true"
       role="alertdialog"
       onpopupshown="gIdentityHandler.onPopupShown(event);"
       onpopuphidden="gIdentityHandler.onPopupHidden(event);"
       orient="vertical">

  <broadcasterset>
    <broadcaster id="identity-popup-mcb-learn-more" class="text-link plain" value="&identity.learnMore;"/>
    <broadcaster id="identity-popup-insecure-login-forms-learn-more" class="text-link plain" value="&identity.learnMore;"/>
  </broadcasterset>

  <panelmultiview id="identity-popup-multiView"
                  mainViewId="identity-popup-mainView">
    <panelview id="identity-popup-mainView"
               descriptionheightworkaround="true">
      <!-- Security Section -->
      <hbox id="identity-popup-security" class="identity-popup-section">
        <vbox class="identity-popup-security-content" flex="1">
          <label class="plain">
            <label class="identity-popup-headline identity-popup-host"></label>
            <label class="identity-popup-headline identity-popup-hostless" crop="end"/>
          </label>
          <description class="identity-popup-connection-not-secure"
                       when-connection="not-secure secure-cert-user-overridden">&identity.connectionNotSecure;</description>
          <description class="identity-popup-connection-secure"
                       when-connection="secure secure-ev">&identity.connectionSecure;</description>
          <description when-connection="chrome">&identity.connectionInternal;</description>
          <description when-connection="file">&identity.connectionFile;</description>
          <description when-connection="extension">&identity.extensionPage;</description>

          <vbox id="identity-popup-security-descriptions">
            <description class="identity-popup-warning-gray"
                         when-mixedcontent="active-blocked">&identity.activeBlocked;</description>
            <description class="identity-popup-warning-yellow"
                         when-mixedcontent="passive-loaded">&identity.passiveLoaded;</description>
            <description when-mixedcontent="active-loaded">&identity.activeLoaded;</description>
            <description class="identity-popup-warning-yellow"
                         when-ciphers="weak">&identity.weakEncryption;</description>
            <description when-loginforms="insecure">&identity.insecureLoginForms2;</description>
          </vbox>
        </vbox>
        <button id="identity-popup-security-expander"
                class="identity-popup-expander"
                when-connection="not-secure secure secure-ev secure-cert-user-overridden"
                oncommand="gIdentityHandler.showSecuritySubView();"/>
      </hbox>

      <!-- Tracking Protection Section -->
      <hbox id="tracking-protection-container"
            class="identity-popup-section"
            when-connection="not-secure secure secure-ev secure-cert-user-overridden file">
        <vbox id="tracking-protection-content" flex="1">
          <label class="identity-popup-headline"
                 crop="end"
                 value="&trackingProtection.title;" />

          <description id="tracking-blocked"
                       crop="end">&trackingProtection.detectedBlocked3;</description>
          <description id="tracking-loaded"
                       crop="end">&trackingProtection.detectedNotBlocked3;</description>
          <description id="tracking-not-detected"
                       crop="end">&trackingProtection.notDetected3;</description>

          <button id="tracking-action-unblock"
                  label="&trackingProtection.unblock.label;"
                  accesskey="&trackingProtection.unblock.accesskey;"
                  oncommand="TrackingProtection.disableForCurrentPage();" />
          <button id="tracking-action-unblock-private"
                  label="&trackingProtection.unblockPrivate.label;"
                  accesskey="&trackingProtection.unblockPrivate.accesskey;"
                  oncommand="TrackingProtection.disableForCurrentPage();" />
          <button id="tracking-action-block"
                  label="&trackingProtection.block2.label;"
                  accesskey="&trackingProtection.block2.accesskey;"
                  oncommand="TrackingProtection.enableForCurrentPage();" />
        </vbox>
      </hbox>

      <!-- Permissions Section -->
      <hbox class="identity-popup-section"
            when-connection="not-secure secure secure-ev secure-cert-user-overridden file extension">
        <vbox id="identity-popup-permissions-content" flex="1">
          <label id="identity-popup-permissions-headline"
                 class="identity-popup-headline"
                 value="&identity.permissions;"/>
          <vbox id="identity-popup-permission-list"/>
          <description id="identity-popup-permission-reload-hint">&identity.permissionsReloadHint;</description>
          <description id="identity-popup-permission-empty-hint">&identity.permissionsEmpty;</description>
        </vbox>
      </hbox>
    </panelview>

    <!-- Security SubView -->
    <panelview id="identity-popup-securityView"
               title="&identity.securityView.label;"
               descriptionheightworkaround="true">
      <vbox class="identity-popup-security-content">
        <label class="plain">
          <label class="identity-popup-headline identity-popup-host"></label>
          <label class="identity-popup-headline identity-popup-hostless" crop="end"/>
        </label>
        <description class="identity-popup-connection-not-secure"
                     when-connection="not-secure secure-cert-user-overridden">&identity.connectionNotSecure;</description>
        <description class="identity-popup-connection-secure"
                     when-connection="secure secure-ev">&identity.connectionSecure;</description>
      </vbox>

      <vbox id="identity-popup-securityView-body" class="panel-view-body-unscrollable">
        <!-- (EV) Certificate Information -->
        <description id="identity-popup-content-verified-by"
                     when-connection="secure-ev">&identity.connectionVerified2;</description>
        <description id="identity-popup-content-owner"
                     when-connection="secure-ev"
                     class="header"/>
        <description id="identity-popup-content-supplemental"
                     when-connection="secure-ev"/>
        <description id="identity-popup-content-verifier"
                     when-connection="secure secure-ev secure-cert-user-overridden"/>

        <!-- Remove Certificate Exception -->
        <button when-connection="secure-cert-user-overridden"
                label="&identity.removeCertException.label;"
                accesskey="&identity.removeCertException.accesskey;"
                oncommand="gIdentityHandler.removeCertException()"/>

        <!-- Connection is Not Secure -->
        <description when-connection="not-secure"
                     and-when-loginforms="secure">&identity.description.insecure;</description>

        <!-- Insecure login forms -->
        <description when-loginforms="insecure">&identity.description.insecureLoginForms; <label observes="identity-popup-insecure-login-forms-learn-more"/></description>

        <!-- Weak Cipher -->
        <description when-ciphers="weak">&identity.description.weakCipher;</description>
        <description class="identity-popup-warning-yellow"
                     when-ciphers="weak">&identity.description.weakCipher2;</description>

        <!-- Active Mixed Content Blocked -->
        <description class="identity-popup-warning-gray"
                     when-mixedcontent="active-blocked">&identity.description.activeBlocked; <label observes="identity-popup-mcb-learn-more"/></description>

        <!-- Passive Mixed Content Loaded -->
        <description when-mixedcontent="passive-loaded">&identity.description.passiveLoaded;</description>
        <description class="identity-popup-warning-yellow"
                     when-mixedcontent="passive-loaded">&identity.description.passiveLoaded2; <label observes="identity-popup-mcb-learn-more"/></description>

        <!-- Passive Mixed Content Loaded, Active Mixed Content Blocked -->
        <description when-mixedcontent="passive-loaded active-blocked">&identity.description.passiveLoaded;</description>
        <description when-mixedcontent="passive-loaded active-blocked"
                     class="identity-popup-warning-yellow">&identity.description.passiveLoaded3; <label observes="identity-popup-mcb-learn-more"/></description>

        <!-- Active Mixed Content Blocking Disabled -->
        <description when-mixedcontent="active-loaded"
                     and-when-loginforms="secure">&identity.description.activeLoaded;</description>
        <description when-mixedcontent="active-loaded"
                     and-when-loginforms="secure">&identity.description.activeLoaded2; <label observes="identity-popup-mcb-learn-more"/></description>
        <!-- Show only the first message when there are insecure login forms,
             and make sure the Learn More link is included. -->
        <description when-mixedcontent="active-loaded"
                     and-when-loginforms="insecure">&identity.description.activeLoaded; <label observes="identity-popup-mcb-learn-more"/></description>

        <!-- Buttons to enable/disable mixed content blocking. -->
        <button when-mixedcontent="active-blocked"
                label="&identity.disableMixedContentBlocking.label;"
                accesskey="&identity.disableMixedContentBlocking.accesskey;"
                oncommand="gIdentityHandler.disableMixedContentProtection()"/>
        <button when-mixedcontent="active-loaded"
                label="&identity.enableMixedContentBlocking.label;"
                accesskey="&identity.enableMixedContentBlocking.accesskey;"
                oncommand="gIdentityHandler.enableMixedContentProtection()"/>
      </vbox>

      <vbox id="identity-popup-securityView-footer">
        <!-- More Security Information -->
        <button label="&identity.moreInfoLinkText2;"
                oncommand="gIdentityHandler.handleMoreInfoClick(event);"/>
      </vbox>

    </panelview>
  </panelmultiview>
</panel>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<commandset>
  <command id="downloadsCmd_doDefault"
           oncommand="goDoCommand('downloadsCmd_doDefault')"/>
  <command id="downloadsCmd_pauseResume"
           oncommand="goDoCommand('downloadsCmd_pauseResume')"/>
  <command id="downloadsCmd_cancel"
           oncommand="goDoCommand('downloadsCmd_cancel')"/>
  <command id="downloadsCmd_unblock"
           oncommand="goDoCommand('downloadsCmd_unblock')"/>
  <command id="downloadsCmd_chooseUnblock"
           oncommand="goDoCommand('downloadsCmd_chooseUnblock')"/>
  <command id="downloadsCmd_unblockAndOpen"
           oncommand="goDoCommand('downloadsCmd_unblockAndOpen')"/>
  <command id="downloadsCmd_confirmBlock"
           oncommand="goDoCommand('downloadsCmd_confirmBlock')"/>
  <command id="downloadsCmd_open"
           oncommand="goDoCommand('downloadsCmd_open')"/>
  <command id="downloadsCmd_show"
           oncommand="goDoCommand('downloadsCmd_show')"/>
  <command id="downloadsCmd_retry"
           oncommand="goDoCommand('downloadsCmd_retry')"/>
  <command id="downloadsCmd_openReferrer"
           oncommand="goDoCommand('downloadsCmd_openReferrer')"/>
  <command id="downloadsCmd_copyLocation"
           oncommand="goDoCommand('downloadsCmd_copyLocation')"/>
  <command id="downloadsCmd_clearList"
           oncommand="goDoCommand('downloadsCmd_clearList')"/>
</commandset>

<!-- The panel has level="top" to ensure that it is never hidden by the
     taskbar on Windows.  See bug 672365.  For accessibility to screen
     readers, we use a label on the panel instead of the anchor because the
     panel can also be displayed without an anchor. -->
<panel id="downloadsPanel"
       aria-label="&downloads.title;"
       role="group"
       type="arrow"
       orient="vertical"
       level="top"
       onpopupshown="DownloadsPanel.onPopupShown(event);"
       onpopuphidden="DownloadsPanel.onPopupHidden(event);"
       hidden="true">
  <!-- The following popup menu should be a child of the panel element,
       otherwise flickering may occur when the cursor is moved over the area
       of a disabled menu item that overlaps the panel.  See bug 492960. -->
  <menupopup id="downloadsContextMenu"
             onpopupshown="DownloadsView.onContextPopupShown(event);"
             onpopuphidden="DownloadsView.onContextPopupHidden(event);"
             class="download-state">
    <menuitem command="downloadsCmd_pauseResume"
              class="downloadPauseMenuItem"
              label="&cmd.pause.label;"
              accesskey="&cmd.pause.accesskey;"/>
    <menuitem command="downloadsCmd_pauseResume"
              class="downloadResumeMenuItem"
              label="&cmd.resume.label;"
              accesskey="&cmd.resume.accesskey;"/>
    <menuitem command="downloadsCmd_unblock"
              class="downloadUnblockMenuItem"
              label="&cmd.unblock2.label;"
              accesskey="&cmd.unblock2.accesskey;"/>
    <menuitem command="downloadsCmd_show"
              class="downloadShowMenuItem"
              label="&cmd.show.label;"
              accesskey="&cmd.show.accesskey;"
              />

    <menuseparator class="downloadCommandsSeparator"/>

    <menuitem command="downloadsCmd_openReferrer"
              label="&cmd.goToDownloadPage.label;"
              accesskey="&cmd.goToDownloadPage.accesskey;"/>
    <menuitem command="downloadsCmd_copyLocation"
              label="&cmd.copyDownloadLink.label;"
              accesskey="&cmd.copyDownloadLink.accesskey;"/>

    <menuseparator/>

    <menuitem command="cmd_delete"
              class="downloadRemoveFromHistoryMenuItem"
              label="&cmd.removeFromHistory.label;"
              accesskey="&cmd.removeFromHistory.accesskey;"/>
    <menuitem command="downloadsCmd_clearList"
              label="&cmd.clearList2.label;"
              accesskey="&cmd.clearList2.accesskey;"/>
    <menuitem command="downloadsCmd_clearDownloads"
              hidden="true"
              label="&cmd.clearDownloads.label;"/>
  </menupopup>

  <panelmultiview id="downloadsPanel-multiView"
                  mainViewId="downloadsPanel-mainView">

    <panelview id="downloadsPanel-mainView">
      <vbox class="panel-view-body-unscrollable">
        <richlistbox id="downloadsListBox"
                     context="downloadsContextMenu"
                     onmouseover="DownloadsView.onDownloadMouseOver(event);"
                     onmouseout="DownloadsView.onDownloadMouseOut(event);"
                     oncontextmenu="DownloadsView.onDownloadContextMenu(event);"
                     ondragstart="DownloadsView.onDownloadDragStart(event);"/>
        <description id="emptyDownloads"
                     mousethrough="always"
                     value="&downloadsPanelEmpty.label;"/>
      </vbox>
      <vbox id="downloadsFooter"
            class="downloadsPanelFooter">
        <stack>
          <hbox id="downloadsSummary"
                align="center"
                orient="horizontal"
                onkeydown="DownloadsSummary.onKeyDown(event);"
                onclick="DownloadsSummary.onClick(event);">
            <image class="downloadTypeIcon" />
            <vbox pack="center"
                  class="downloadContainer"
                  style="width: &downloadDetails.width;">
              <description id="downloadsSummaryDescription"
                           style="min-width: &downloadsSummary.minWidth2;"/>
              <progressmeter id="downloadsSummaryProgress"
                             class="downloadProgress"
                             min="0"
                             max="100"
                             mode="normal" />
              <description id="downloadsSummaryDetails"
                           crop="end"/>
            </vbox>
          </hbox>
          <hbox id="downloadsFooterButtons">
            <button id="downloadsHistory"
                    class="downloadsPanelFooterButton"
                    label="&downloadsHistory.label;"
                    accesskey="&downloadsHistory.accesskey;"
                    flex="1"
                    oncommand="DownloadsPanel.showDownloadsHistory();"
                    pack="start"/>
          </hbox>
        </stack>
      </vbox>
    </panelview>

    <panelview id="downloadsPanel-blockedSubview"
               descriptionheightworkaround="true"
               class="PanelUI-subView"
               title="&downloadDetails.label;">
      <vbox class="panel-view-body-unscrollable">
        <description id="downloadsPanel-blockedSubview-title"/>
        <description id="downloadsPanel-blockedSubview-details1"/>
        <description id="downloadsPanel-blockedSubview-details2"/>
      </vbox>
      <hbox id="downloadsPanel-blockedSubview-buttons"
            class="downloadsPanelFooter"
            align="stretch">
        <button id="downloadsPanel-blockedSubview-openButton"
                class="downloadsPanelFooterButton"
                command="downloadsCmd_unblockAndOpen"
                flex="1"/>
        <toolbarseparator/>
        <button id="downloadsPanel-blockedSubview-deleteButton"
                class="downloadsPanelFooterButton"
                oncommand="DownloadsBlockedSubview.confirmBlock();"
                default="true"
                flex="1"/>
      </hbox>
    </panelview>

    <panelview id="PanelUI-downloads" class="PanelUI-subView">
      <vbox class="panel-subview-body">
        <toolbarbutton id="appMenu-library-downloads-show-button"
                       class="subviewbutton subviewbutton-iconic"
                       label="&cmd.showDownloads.label;"
                       closemenu="none"
                       oncommand="DownloadsSubview.onShowDownloads(this);"/>
        <toolbarseparator/>
        <toolbaritem id="panelMenu_downloadsMenu"
                     orient="vertical"
                     smoothscroll="false"
                     flatList="true"
                     tooltip="bhTooltip">
          <!-- downloads menu items will go here -->
        </toolbaritem>
      </vbox>
      <toolbarbutton id="PanelUI-downloadsMore"
                     class="panel-subview-footer subviewbutton"
                     label="&downloadsHistory.label;"
                     oncommand="BrowserDownloadsUI(); CustomizableUI.hidePanelForNode(this);"/>
    </panelview>

  </panelmultiview>

</panel>

    <hbox id="downloads-animation-container" mousethrough="always">
      <vbox id="downloads-notification-anchor" hidden="true">
        <vbox id="downloads-indicator-notification"/>
      </vbox>
    </hbox>

    <hbox id="bookmarked-notification-container" mousethrough="always">
      <vbox id="bookmarked-notification-anchor">
        <vbox id="bookmarked-notification"/>
      </vbox>
      <vbox id="bookmarked-notification-dropmarker-anchor">
        <image id="bookmarked-notification-dropmarker-icon"/>
      </vbox>
    </hbox>

    <tooltip id="dynamic-shortcut-tooltip"
             onpopupshowing="UpdateDynamicShortcutTooltipText(this);"/>

    <menupopup id="SyncedTabsSidebarContext">
      <menuitem label="&syncedTabs.context.open.label;"
                accesskey="&syncedTabs.context.open.accesskey;"
                id="syncedTabsOpenSelected" where="current"/>
      <menuitem label="&syncedTabs.context.openInNewTab.label;"
                accesskey="&syncedTabs.context.openInNewTab.accesskey;"
                id="syncedTabsOpenSelectedInTab" where="tab"/>
      <menuitem label="&syncedTabs.context.openInNewWindow.label;"
                accesskey="&syncedTabs.context.openInNewWindow.accesskey;"
                id="syncedTabsOpenSelectedInWindow" where="window"/>
      <menuitem label="&syncedTabs.context.openInNewPrivateWindow.label;"
                accesskey="&syncedTabs.context.openInNewPrivateWindow.accesskey;"
                id="syncedTabsOpenSelectedInPrivateWindow" where="window" private="true"/>
      <menuseparator/>
      <menuitem label="&syncedTabs.context.bookmarkSingleTab.label;"
                accesskey="&syncedTabs.context.bookmarkSingleTab.accesskey;"
                id="syncedTabsBookmarkSelected"/>
      <menuitem label="&syncedTabs.context.copy.label;"
                accesskey="&syncedTabs.context.copy.accesskey;"
                id="syncedTabsCopySelected"/>
      <menuseparator/>
      <menuitem label="&syncedTabs.context.openAllInTabs.label;"
                accesskey="&syncedTabs.context.openAllInTabs.accesskey;"
                id="syncedTabsOpenAllInTabs"/>
      <menuitem label="&syncedTabs.context.managedevices.label;"
                accesskey="&syncedTabs.context.managedevices.accesskey;"
                id="syncedTabsManageDevices"
                oncommand="gSync.openDevicesManagementPage('syncedtabs-sidebar');"/>
      <menuitem label="&syncSyncNowItem.label;"
                accesskey="&syncSyncNowItem.accesskey;"
                id="syncedTabsRefresh"/>
    </menupopup>
    <menupopup id="SyncedTabsSidebarTabsFilterContext"
               class="textbox-contextmenu">
      <menuitem label="&undoCmd.label;"
                accesskey="&undoCmd.accesskey;"
                cmd="cmd_undo"/>
      <menuseparator/>
      <menuitem label="&cutCmd.label;"
                accesskey="&cutCmd.accesskey;"
                cmd="cmd_cut"/>
      <menuitem label="&copyCmd.label;"
                accesskey="&copyCmd.accesskey;"
                cmd="cmd_copy"/>
      <menuitem label="&pasteCmd.label;"
                accesskey="&pasteCmd.accesskey;"
                cmd="cmd_paste"/>
      <menuitem label="&deleteCmd.label;"
                accesskey="&deleteCmd.accesskey;"
                cmd="cmd_delete"/>
      <menuseparator/>
      <menuitem label="&selectAllCmd.label;"
                accesskey="&selectAllCmd.accesskey;"
                cmd="cmd_selectAll"/>
      <menuseparator/>
      <menuitem label="&syncSyncNowItem.label;"
                accesskey="&syncSyncNowItem.accesskey;"
                id="syncedTabsRefreshFilter"/>
    </menupopup>
  </popupset>
  <box id="appMenu-viewCache" hidden="true"/>

<vbox id="titlebar">
  <hbox id="titlebar-content">
    <spacer id="titlebar-spacer" flex="1"/>
    <hbox id="titlebar-buttonbox-container">
      <hbox id="titlebar-buttonbox" class="titlebar-color">
        <toolbarbutton class="titlebar-button" id="titlebar-min" oncommand="window.minimize();"/>
        <toolbarbutton class="titlebar-button" id="titlebar-max" oncommand="onTitlebarMaxClick();"/>
        <toolbarbutton class="titlebar-button" id="titlebar-close" command="cmd_closeWindow"/>
      </hbox>
    </hbox>
  </hbox>
</vbox>

  <toolbox id="navigator-toolbox">
    <!-- Menu -->
    <toolbar type="menubar" id="toolbar-menubar"
             class="chromeclass-menubar titlebar-color"
             customizable="true"
             mode="icons"
             toolbarname="&menubarCmd.label;"
             accesskey="&menubarCmd.accesskey;"
             autohide="true"
             context="toolbar-context-menu">
      <toolbaritem id="menubar-items" align="center">

       <menubar id="main-menubar"
                onpopupshowing="if (event.target.parentNode.parentNode == this &amp;&amp;
                                    !('@mozilla.org/widget/nativemenuservice;1' in Cc))
                                  this.setAttribute('openedwithkey',
                                                    event.target.parentNode.openedWithKey);"
                style="border:0px;padding:0px;margin:0px;-moz-appearance:none">
            <menu id="file-menu" label="&fileMenu.label;"
                  accesskey="&fileMenu.accesskey;">
              <menupopup id="menu_FilePopup"
                         onpopupshowing="updateUserContextUIVisibility();">
                <menuitem id="menu_newNavigatorTab"
                          label="&tabCmd.label;"
                          command="cmd_newNavigatorTab"
                          key="key_newNavigatorTab"
                          accesskey="&tabCmd.accesskey;"/>
                <menu id="menu_newUserContext"
                      label="&newUserContext.label;"
                      accesskey="&newUserContext.accesskey;"
                      hidden="true">
                  <menupopup onpopupshowing="return createUserContextMenu(event);" />
                </menu>
                <menuitem id="menu_newNavigator"
                          label="&newNavigatorCmd.label;"
                          accesskey="&newNavigatorCmd.accesskey;"
                          key="key_newNavigator"
                          command="cmd_newNavigator"/>
                <menuitem id="menu_newPrivateWindow"
                          label="&newPrivateWindow.label;"
                          accesskey="&newPrivateWindow.accesskey;"
                          command="Tools:PrivateBrowsing"
                          key="key_privatebrowsing"/>
                <menuitem id="menu_openFile"
                          label="&openFileCmd.label;"
                          command="Browser:OpenFile"
                          key="openFileKb"
                          accesskey="&openFileCmd.accesskey;"/>
                <menuitem id="menu_close"
                          class="show-only-for-keyboard"
                          label="&closeCmd.label;"
                          key="key_close"
                          accesskey="&closeCmd.accesskey;"
                          command="cmd_close"/>
                <menuitem id="menu_closeWindow"
                          class="show-only-for-keyboard"
                          hidden="true"
                          command="cmd_closeWindow"
                          key="key_closeWindow"
                          label="&closeWindow.label;"
                          accesskey="&closeWindow.accesskey;"/>
                <menuseparator/>
                <menuitem id="menu_savePage"
                          label="&savePageCmd.label;"
                          accesskey="&savePageCmd.accesskey;"
                          key="key_savePage"
                          command="Browser:SavePage"/>
                <menuitem id="menu_sendLink"
                          label="&emailPageCmd.label;"
                          accesskey="&emailPageCmd.accesskey;"
                          command="Browser:SendLink"/>
                <menuseparator/>
                <menuitem id="menu_printPreview"
                          label="&printPreviewCmd.label;"
                          accesskey="&printPreviewCmd.accesskey;"
                          command="cmd_printPreview"/>
                <menuitem id="menu_print"
                          label="&printCmd.label;"
                          accesskey="&printCmd.accesskey;"
                          key="printKb"
                          command="cmd_print"/>
                <menuseparator/>
                <menuitem id="goOfflineMenuitem"
                          label="&goOfflineCmd.label;"
                          accesskey="&goOfflineCmd.accesskey;"
                          type="checkbox"
                          observes="workOfflineMenuitemState"
                          oncommand="BrowserOffline.toggleOfflineStatus();"/>
                <menuitem id="menu_FileQuitItem"
                          label="&quitApplicationCmd.label;"
                          accesskey="&quitApplicationCmd.accesskey;"
                          key="key_quitApplication"
                          command="cmd_quitApplication"/>
              </menupopup>
            </menu>

            <menu id="edit-menu" label="&editMenu.label;"
                  accesskey="&editMenu.accesskey;">
              <menupopup id="menu_EditPopup"
                         onpopupshowing="updateEditUIVisibility()"
                         onpopuphidden="updateEditUIVisibility()">
                <menuitem id="menu_undo"
                          label="&undoCmd.label;"
                          key="key_undo"
                          accesskey="&undoCmd.accesskey;"
                          command="cmd_undo"/>
                <menuitem id="menu_redo"
                          label="&redoCmd.label;"
                          key="key_redo"
                          accesskey="&redoCmd.accesskey;"
                          command="cmd_redo"/>
                <menuseparator/>
                <menuitem id="menu_cut"
                          label="&cutCmd.label;"
                          key="key_cut"
                          accesskey="&cutCmd.accesskey;"
                          command="cmd_cut"/>
                <menuitem id="menu_copy"
                          label="&copyCmd.label;"
                          key="key_copy"
                          accesskey="&copyCmd.accesskey;"
                          command="cmd_copy"/>
                <menuitem id="menu_paste"
                          label="&pasteCmd.label;"
                          key="key_paste"
                          accesskey="&pasteCmd.accesskey;"
                          command="cmd_paste"/>
                <menuitem id="menu_delete"
                          label="&deleteCmd.label;"
                          key="key_delete"
                          accesskey="&deleteCmd.accesskey;"
                          command="cmd_delete"/>
                <menuseparator/>
                <menuitem id="menu_selectAll"
                          label="&selectAllCmd.label;"
                          key="key_selectAll"
                          accesskey="&selectAllCmd.accesskey;"
                          command="cmd_selectAll"/>
                <menuseparator/>
                <menuitem id="menu_find"
                          label="&findOnCmd.label;"
                          accesskey="&findOnCmd.accesskey;"
                          key="key_find"
                          command="cmd_find"/>
                <menuitem id="menu_findAgain"
                          class="show-only-for-keyboard"
                          label="&findAgainCmd.label;"
                          accesskey="&findAgainCmd.accesskey;"
                          key="key_findAgain"
                          command="cmd_findAgain"/>
                <menuseparator hidden="true" id="textfieldDirection-separator"/>
                <menuitem id="textfieldDirection-swap"
                          command="cmd_switchTextDirection"
                          key="key_switchTextDirection"
                          label="&bidiSwitchTextDirectionItem.label;"
                          accesskey="&bidiSwitchTextDirectionItem.accesskey;"
                          hidden="true"/>
                <menuseparator/>
                <menuitem id="menu_preferences"
                          label="&preferencesCmdUnix.label;"
                          accesskey="&preferencesCmdUnix.accesskey;"
                          oncommand="openPreferences(undefined, {origin: 'menubar'});"/>
              </menupopup>
            </menu>

            <menu id="view-menu" label="&viewMenu.label;"
                  accesskey="&viewMenu.accesskey;">
              <menupopup id="menu_viewPopup"
                         onpopupshowing="updateCharacterEncodingMenuState();">
                <menu id="viewToolbarsMenu"
                      label="&viewToolbarsMenu.label;"
                      accesskey="&viewToolbarsMenu.accesskey;">
                  <menupopup onpopupshowing="onViewToolbarsPopupShowing(event);">
                    <menuseparator/>
                    <menuitem id="menu_customizeToolbars"
                              label="&viewCustomizeToolbar.label;"
                              accesskey="&viewCustomizeToolbar.accesskey;"
                              command="cmd_CustomizeToolbars"/>
                  </menupopup>
                </menu>
                <menu id="viewSidebarMenuMenu"
                      label="&viewSidebarMenu.label;"
                      accesskey="&viewSidebarMenu.accesskey;">
                  <menupopup id="viewSidebarMenu">
                    <menuitem id="menu_bookmarksSidebar"
                              key="viewBookmarksSidebarKb"
                              observes="viewBookmarksSidebar"/>
                    <menuitem id="menu_historySidebar"
                              key="key_gotoHistory"
                              observes="viewHistorySidebar"
                              label="&historyButton.label;"/>
                    <menuitem id="menu_tabsSidebar"
                              class="sync-ui-item"
                              observes="viewTabsSidebar"
                              label="&syncedTabs.sidebar.label;"/>
                  </menupopup>
                </menu>
                <menuseparator/>
                <menu id="viewFullZoomMenu" label="&fullZoom.label;"
                      accesskey="&fullZoom.accesskey;"
                      onpopupshowing="FullZoom.updateMenu();">
                  <menupopup>
                    <menuitem id="menu_zoomEnlarge"
                              key="key_fullZoomEnlarge"
                              label="&fullZoomEnlargeCmd.label;"
                              accesskey="&fullZoomEnlargeCmd.accesskey;"
                              command="cmd_fullZoomEnlarge"/>
                    <menuitem id="menu_zoomReduce"
                              key="key_fullZoomReduce"
                              label="&fullZoomReduceCmd.label;"
                              accesskey="&fullZoomReduceCmd.accesskey;"
                              command="cmd_fullZoomReduce"/>
                    <menuseparator/>
                    <menuitem id="menu_zoomReset"
                              key="key_fullZoomReset"
                              label="&fullZoomResetCmd.label;"
                              accesskey="&fullZoomResetCmd.accesskey;"
                              command="cmd_fullZoomReset"/>
                    <menuseparator/>
                    <menuitem id="toggle_zoom"
                              label="&fullZoomToggleCmd.label;"
                              accesskey="&fullZoomToggleCmd.accesskey;"
                              type="checkbox"
                              command="cmd_fullZoomToggle"
                              checked="false"/>
                  </menupopup>
                </menu>
                <menu id="pageStyleMenu" label="&pageStyleMenu.label;"
                      accesskey="&pageStyleMenu.accesskey;" observes="isImage">
                  <menupopup onpopupshowing="gPageStyleMenu.fillPopup(this);">
                    <menuitem id="menu_pageStyleNoStyle"
                              label="&pageStyleNoStyle.label;"
                              accesskey="&pageStyleNoStyle.accesskey;"
                              oncommand="gPageStyleMenu.disableStyle();"
                              type="radio"/>
                    <menuitem id="menu_pageStylePersistentOnly"
                              label="&pageStylePersistentOnly.label;"
                              accesskey="&pageStylePersistentOnly.accesskey;"
                              oncommand="gPageStyleMenu.switchStyleSheet('');"
                              type="radio"
                              checked="true"/>
                    <menuseparator/>
                  </menupopup>
                </menu>
                <menu id="charsetMenu"
                    label="&charsetMenu2.label;"
                    accesskey="&charsetMenu2.accesskey;"
                    oncommand="BrowserSetForcedCharacterSet(event.target.getAttribute('charset'));"
                    onpopupshowing="CharsetMenu.build(event.target); UpdateCurrentCharset(this);">
                  <menupopup>
                  </menupopup>
                </menu>
                <menuseparator/>
                <menuitem id="fullScreenItem"
                          accesskey="&fullScreenCmd.accesskey;"
                          label="&fullScreenCmd.label;"
                          key="key_fullScreen"
                          type="checkbox"
                          observes="View:FullScreen"/>
                <menuitem id="menu_readerModeItem"
                          observes="View:ReaderView"
                          key="key_toggleReaderMode"
                          hidden="true"/>
                <menuitem id="menu_showAllTabs"
                          hidden="true"
                          accesskey="&showAllTabsCmd.accesskey;"
                          label="&showAllTabsCmd.label;"
                          command="Browser:ShowAllTabs"
                          key="key_showAllTabs"/>
                <menuseparator hidden="true" id="documentDirection-separator"/>
                <menuitem id="documentDirection-swap"
                          hidden="true"
                          label="&bidiSwitchPageDirectionItem.label;"
                          accesskey="&bidiSwitchPageDirectionItem.accesskey;"
                          oncommand="gBrowser.selectedBrowser
                                             .messageManager
                                             .sendAsyncMessage('SwitchDocumentDirection');"/>
              </menupopup>
            </menu>

            <menu id="history-menu"
                  label="&historyMenu.label;"
                  accesskey="&historyMenu.accesskey;">
              <menupopup id="goPopup"
                         placespopup="true"
                         oncommand="this.parentNode._placesView._onCommand(event);"
                         onclick="checkForMiddleClick(this, event);"
                         onpopupshowing="if (!this.parentNode._placesView)
                                           new HistoryMenu(event);"
                         tooltip="bhTooltip"
                         popupsinherittooltip="true">
                <menuitem id="menu_showAllHistory"
                          label="&showAllHistoryCmd2.label;"
                          key="showAllHistoryKb"
                          command="Browser:ShowAllHistory"/>
                <menuitem id="sanitizeItem"
                          label="&clearRecentHistory.label;"
                          key="key_sanitize"
                          command="Tools:Sanitize"/>
                <menuseparator id="sanitizeSeparator"/>
                <menuitem id="sync-tabs-menuitem"
                          class="syncTabsMenuItem"
                          label="&syncTabsMenu3.label;"
                          oncommand="BrowserOpenSyncTabs();"
                          hidden="true"/>
                <menuitem id="historyRestoreLastSession"
                          label="&historyRestoreLastSession.label;"
                          command="Browser:RestoreLastSession"/>
                <menu id="historyUndoMenu"
                      class="recentlyClosedTabsMenu"
                      label="&historyUndoMenu.label;"
                      disabled="true">
                  <menupopup id="historyUndoPopup"
                             placespopup="true"
                             onpopupshowing="document.getElementById('history-menu')._placesView.populateUndoSubmenu();"/>
                </menu>
                <menu id="historyUndoWindowMenu"
                      class="recentlyClosedWindowsMenu"
                      label="&historyUndoWindowMenu.label;"
                      disabled="true">
                  <menupopup id="historyUndoWindowPopup"
                             placespopup="true"
                             onpopupshowing="document.getElementById('history-menu')._placesView.populateUndoWindowSubmenu();"/>
                </menu>
                <menuseparator id="startHistorySeparator"
                               class="hide-if-empty-places-result"/>
              </menupopup>
            </menu>

  <menu id="bookmarksMenu"
        label="&bookmarksMenu.label;"
        accesskey="&bookmarksMenu.accesskey;"
        ondragenter="PlacesMenuDNDHandler.onDragEnter(event);"
        ondragover="PlacesMenuDNDHandler.onDragOver(event);"
        ondrop="PlacesMenuDNDHandler.onDrop(event);">
    <menupopup id="bookmarksMenuPopup"
               placespopup="true"
               context="placesContext"
               openInTabs="children"
               onmouseup="BookmarksEventHandler.onMouseUp(event);"
               oncommand="BookmarksEventHandler.onCommand(event);"
               onclick="BookmarksEventHandler.onClick(event, this.parentNode._placesView);"
               onpopupshowing="BookmarkingUI.onMainMenuPopupShowing(event);
                               if (!this.parentNode._placesView)
                                 new PlacesMenu(event, 'place:folder=BOOKMARKS_MENU');"
               tooltip="bhTooltip" popupsinherittooltip="true">
      <menuitem id="bookmarksShowAll"
                label="&showAllBookmarks2.label;"
                command="Browser:ShowAllBookmarks"
                key="manBookmarkKb"/>
      <menuseparator id="organizeBookmarksSeparator"/>
      <menuitem id="menu_bookmarkThisPage"
                command="Browser:AddBookmarkAs"
                observes="bookmarkThisPageBroadcaster"
                key="addBookmarkAsKb"/>
      <menuitem id="subscribeToPageMenuitem"
                class="menuitem-iconic"
                label="&subscribeToPageMenuitem.label;"
                oncommand="return FeedHandler.subscribeToFeed(null, event);"
                onclick="checkForMiddleClick(this, event);"
                observes="singleFeedMenuitemState"/>
      <menu id="subscribeToPageMenupopup"
            class="menu-iconic"
            label="&subscribeToPageMenupopup.label;"
            observes="multipleFeedsMenuState">
        <menupopup id="subscribeToPageSubmenuMenupopup"
                   onpopupshowing="return FeedHandler.buildFeedList(event.target);"
                   oncommand="return FeedHandler.subscribeToFeed(null, event);"
                   onclick="checkForMiddleClick(this, event);"/>
      </menu>
      <menuitem id="menu_bookmarkAllTabs"
                label="&addCurPagesCmd.label;"
                class="show-only-for-keyboard"
                command="Browser:BookmarkAllTabs"
                key="bookmarkAllTabsKb"/>
      <menuseparator id="bookmarksToolbarSeparator"/>
      <menu id="bookmarksToolbarFolderMenu"
            class="menu-iconic bookmark-item"
            label="&personalbarCmd.label;"
            container="true">
        <menupopup id="bookmarksToolbarFolderPopup"
                   placespopup="true"
                   context="placesContext"
                   onpopupshowing="if (!this.parentNode._placesView)
                                     new PlacesMenu(event, 'place:folder=TOOLBAR');"/>
      </menu>
      <menu id="menu_unsortedBookmarks"
            class="menu-iconic bookmark-item"
            label="&otherBookmarksCmd.label;"
            container="true">
        <menupopup id="otherBookmarksFolderPopup"
                   placespopup="true"
                   context="placesContext"
                   onpopupshowing="if (!this.parentNode._placesView)
                                     new PlacesMenu(event, 'place:folder=UNFILED_BOOKMARKS');"/>
      </menu>
      <menu id="menu_mobileBookmarks"
            class="menu-iconic bookmark-item"
            label="&mobileBookmarksCmd.label;"
            hidden="true"
            container="true">
        <menupopup id="mobileBookmarksFolderPopup"
                   placespopup="true"
                   context="placesContext"
                   onpopupshowing="if (!this.parentNode._placesView)
                                     new PlacesMenu(event, 'place:folder=MOBILE_BOOKMARKS');"/>
      </menu>
      <menuseparator id="bookmarksMenuItemsSeparator"/>
      <!-- Bookmarks menu items -->
    </menupopup>
  </menu>

            <menu id="tools-menu"
                  label="&toolsMenu.label;"
                  accesskey="&toolsMenu.accesskey;">
              <menupopup id="menu_ToolsPopup">
              <menuitem id="menu_openDownloads"
                        label="&downloads.label;"
                        accesskey="&downloads.accesskey;"
                        key="key_openDownloads"
                        command="Tools:Downloads"/>
              <menuitem id="menu_openAddons"
                        label="&addons.label;"
                        accesskey="&addons.accesskey;"
                        key="key_openAddons"
                        command="Tools:Addons"/>

              <!-- only one of sync-setup, sync-unverifieditem, sync-syncnowitem or sync-reauthitem will be showing at once -->
              <menuitem id="sync-setup"
                        class="sync-ui-item"
                        label="&syncSignIn.label;"
                        accesskey="&syncSignIn.accesskey;"
                        observes="sync-setup-state"
                        oncommand="gSync.openPrefs('menubar')"/>
              <menuitem id="sync-unverifieditem"
                        class="sync-ui-item"
                        label="&syncSignIn.label;"
                        accesskey="&syncSignIn.accesskey;"
                        observes="sync-unverified-state"
                        oncommand="gSync.openPrefs('menubar')"/>
              <menuitem id="sync-syncnowitem"
                        class="sync-ui-item"
                        label="&syncSyncNowItem.label;"
                        accesskey="&syncSyncNowItem.accesskey;"
                        observes="sync-syncnow-state"
                        oncommand="gSync.doSync(event);"/>
              <menuitem id="sync-reauthitem"
                        class="sync-ui-item"
                        label="&syncReAuthItem.label;"
                        accesskey="&syncReAuthItem.accesskey;"
                        observes="sync-reauth-state"
                        oncommand="gSync.openSignInAgainPage('menubar');"/>
              <menuseparator id="devToolsSeparator"/>
              <menu id="webDeveloperMenu"
                    label="&webDeveloperMenu.label;"
                    accesskey="&webDeveloperMenu.accesskey;">
                <menupopup id="menuWebDeveloperPopup">
                  <menuitem id="menu_pageSource"
                            observes="devtoolsMenuBroadcaster_PageSource"
                            accesskey="&pageSourceCmd.accesskey;"/>
                </menupopup>
              </menu>
              <menuitem id="menu_pageInfo"
                        accesskey="&pageInfoCmd.accesskey;"
                        label="&pageInfoCmd.label;"
                        key="key_viewInfo"
                        command="View:PageInfo"/>
              </menupopup>
            </menu>
            <menu id="helpMenu"
                  label="&helpMenu.label;"
                  accesskey="&helpMenu.accesskey;"
                  >
              <menupopup id="menu_HelpPopup" onpopupshowing="buildHelpMenu();">
                <menuitem id="menu_openHelp"
                          oncommand="openHelpLink('firefox-help')"
                          onclick="checkForMiddleClick(this, event);"
                          label="&productHelp2.label;"
                          accesskey="&productHelp2.accesskey;"
                          />
                <menuitem id="menu_openTour"
                          oncommand="openTourPage();"
                          label="&helpShowTour2.label;"
                          accesskey="&helpShowTour2.accesskey;"/>
                <menuitem id="menu_keyboardShortcuts"
                          oncommand="openHelpLink('keyboard-shortcuts')"
                          onclick="checkForMiddleClick(this, event);"
                          label="&helpKeyboardShortcuts.label;"
                          accesskey="&helpKeyboardShortcuts.accesskey;"/>
                <menuitem id="troubleShooting"
                          accesskey="&helpTroubleshootingInfo.accesskey;"
                          label="&helpTroubleshootingInfo.label;"
                          oncommand="openTroubleshootingPage()"
                          onclick="checkForMiddleClick(this, event);"/>
                <menuitem id="feedbackPage"
                          accesskey="&helpFeedbackPage.accesskey;"
                          label="&helpFeedbackPage.label;"
                          oncommand="openFeedbackPage()"
                          onclick="checkForMiddleClick(this, event);"/>
                <menuitem id="helpSafeMode"
                          accesskey="&helpSafeMode.accesskey;"
                          label="&helpSafeMode.label;"
                          stopaccesskey="&helpSafeMode.stop.accesskey;"
                          stoplabel="&helpSafeMode.stop.label;"
                          oncommand="safeModeRestart();"/>
                <menuitem id="menu_HelpPopup_reportPhishingtoolmenu"
                          label="&reportDeceptiveSiteMenu.title;"
                          accesskey="&reportDeceptiveSiteMenu.accesskey;"
                          observes="reportPhishingBroadcaster"
                          oncommand="openUILink(gSafeBrowsing.getReportURL('Phish'), event);"
                          onclick="checkForMiddleClick(this, event);"
                          hidden="true"/>
                <menuitem id="menu_HelpPopup_reportPhishingErrortoolmenu"
                          label="&safeb.palm.notdeceptive.label;"
                          accesskey="&safeb.palm.notdeceptive.accesskey;"
                          observes="reportPhishingErrorBroadcaster"
                          oncommand="ReportFalseDeceptiveSite();"
                          onclick="checkForMiddleClick(this, event);"
                          hidden="true"/>
                <menuseparator id="aboutSeparator"/>
                <menuitem id="aboutName"
                          accesskey="&aboutProduct2.accesskey;"
                          label="&aboutProduct2.label;"
                          oncommand="openAboutDialog();"/>
              </menupopup>
          </menu>
        </menubar>
      </toolbaritem>

      <hbox class="titlebar-placeholder" type="caption-buttons" ordinal="1000"
            skipintoolbarset="true"/>
    </toolbar>

    <toolbar id="TabsToolbar"
             class="titlebar-color"
             fullscreentoolbar="true"
             customizable="true"
             mode="icons"
             aria-label="&tabsToolbar.label;"
             context="toolbar-context-menu"
             collapsed="true">

      <hbox class="titlebar-placeholder" type="pre-tabs"
            skipintoolbarset="true"/>

      <tabs id="tabbrowser-tabs"
            flex="1"
            setfocus="false"
            tooltip="tabbrowser-tab-tooltip"
            stopwatchid="FX_TAB_CLICK_MS">
        <tab class="tabbrowser-tab" selected="true" visuallyselected="true" fadein="true"/>
      </tabs>

      <toolbarbutton id="new-tab-button"
                     class="toolbarbutton-1 chromeclass-toolbar-additional"
                     label="&tabCmd.label;"
                     command="cmd_newNavigatorTab"
                     onclick="checkForMiddleClick(this, event);"
                     tooltip="dynamic-shortcut-tooltip"
                     ondrop="newTabButtonObserver.onDrop(event)"
                     ondragover="newTabButtonObserver.onDragOver(event)"
                     ondragenter="newTabButtonObserver.onDragOver(event)"
                     ondragexit="newTabButtonObserver.onDragExit(event)"
                     cui-areatype="toolbar"
                     removable="true"/>

      <toolbarbutton id="alltabs-button"
                     class="toolbarbutton-1 chromeclass-toolbar-additional tabs-alltabs-button"
                     type="menu"
                     label="&listAllTabs.label;"
                     tooltiptext="&listAllTabs.label;"
                     removable="false">
        <menupopup id="alltabs-popup"
                   position="after_end">
          <menuitem id="alltabs_undoCloseTab"
                    key="key_undoCloseTab"
                    label="&undoCloseTab.label;"
                    observes="History:UndoCloseTab"/>
          <menuseparator id="alltabs-popup-separator-1"/>
          <menu id="alltabs_containersTab"
                label="&newUserContext.label;">
            <menupopup id="alltabs_containersMenuTab" />
          </menu>
          <menuseparator id="alltabs-popup-separator-2"/>
        </menupopup>
      </toolbarbutton>

      <hbox class="titlebar-placeholder" type="post-tabs"
            ordinal="1000"
            skipintoolbarset="true"/>

      <button class="accessibility-indicator" tooltiptext="&accessibilityIndicator.tooltip;"
              ordinal="1000"
              aria-live="polite" skipintoolbarset="true"/>
      <hbox class="private-browsing-indicator" skipintoolbarset="true"
            ordinal="1000"/>
      <hbox class="titlebar-placeholder" type="caption-buttons"
            ordinal="1000"
            skipintoolbarset="true"/>

    </toolbar>

    <toolbar id="nav-bar"
             aria-label="&navbarCmd.label;"
             fullscreentoolbar="true" mode="icons" customizable="true"
             customizationtarget="nav-bar-customization-target"
             overflowable="true"
             overflowbutton="nav-bar-overflow-button"
             overflowtarget="widget-overflow-list"
             overflowpanel="widget-overflow"
             context="toolbar-context-menu">

      <hbox id="nav-bar-customization-target" flex="1">
        <toolbarbutton id="back-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
                       label="&backCmd.label;"
                       removable="false" overflows="false"
                       keepbroadcastattributeswhencustomizing="true"
                       command="Browser:BackOrBackDuplicate"
                       onclick="checkForMiddleClick(this, event);"
                       tooltip="back-button-tooltip"
                       context="backForwardMenu"/>
        <toolbarbutton id="forward-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
                       label="&forwardCmd.label;"
                       removable="false" overflows="false"
                       keepbroadcastattributeswhencustomizing="true"
                       command="Browser:ForwardOrForwardDuplicate"
                       onclick="checkForMiddleClick(this, event);"
                       tooltip="forward-button-tooltip"
                       context="backForwardMenu"/>
        <toolbaritem id="stop-reload-button" class="chromeclass-toolbar-additional"
                     title="&reloadCmd.label;"
                     removable="true" overflows="false">
          <toolbarbutton id="reload-button" class="toolbarbutton-1"
                         label="&reloadCmd.label;"
                         command="Browser:ReloadOrDuplicate"
                         onclick="checkForMiddleClick(this, event);"
                         tooltip="dynamic-shortcut-tooltip">
            <box class="toolbarbutton-animatable-box">
              <image class="toolbarbutton-animatable-image"/>
            </box>
          </toolbarbutton>
          <toolbarbutton id="stop-button" class="toolbarbutton-1"
                         label="&stopCmd.label;"
                         command="Browser:Stop"
                         tooltip="dynamic-shortcut-tooltip">
            <box class="toolbarbutton-animatable-box">
              <image class="toolbarbutton-animatable-image"/>
            </box>
          </toolbarbutton>
        </toolbaritem>
        <toolbarbutton id="home-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
                       removable="true"
                       label="&homeButton.label;"
                       ondragover="homeButtonObserver.onDragOver(event)"
                       ondragenter="homeButtonObserver.onDragOver(event)"
                       ondrop="homeButtonObserver.onDrop(event)"
                       ondragexit="homeButtonObserver.onDragExit(event)"
                       key="goHome"
                       onclick="BrowserGoHome(event);"
                       cui-areatype="toolbar"
                       aboutHomeOverrideTooltip="&abouthome.pageTitle;"/>
        <toolbarspring cui-areatype="toolbar" class="chromeclass-toolbar-additional"/>
        <toolbaritem id="urlbar-container" flex="400" persist="width"
                     removable="false"
                     class="chromeclass-location" overflows="false">
            <textbox id="urlbar" flex="1"
                     placeholder="&urlbar.placeholder2;"
                     defaultPlaceholder="&urlbar.placeholder2;"
                     focused="true"
                     type="autocomplete"
                     autocompletesearch="unifiedcomplete"
                     autocompletesearchparam="enable-actions"
                     autocompletepopup="PopupAutoCompleteRichResult"
                     completeselectedindex="true"
                     shrinkdelay="250"
                     tabscrolling="true"
                     newlines="stripsurroundingwhitespace"
                     ontextentered="this.handleCommand(param);"
                     ontextreverted="return this.handleRevert();"
                     pageproxystate="invalid">
              <!-- Use onclick instead of normal popup= syntax since the popup
                   code fires onmousedown, and hence eats our favicon drag events. -->
              <box id="identity-box" role="button"
                   align="center"
                   aria-label="&urlbar.viewSiteInfo.label;"
                   onclick="gIdentityHandler.handleIdentityButtonEvent(event);"
                   onkeypress="gIdentityHandler.handleIdentityButtonEvent(event);"
                   ondragstart="gIdentityHandler.onDragStart(event);">
                <image id="identity-icon"
                       consumeanchor="identity-box"
                       onclick="PageProxyClickHandler(event);"/>
                <image id="sharing-icon" mousethrough="always"/>
                <image id="tracking-protection-icon"/>
                <box id="blocked-permissions-container" align="center">
                  <image data-permission-id="geo" class="blocked-permission-icon geo-icon" role="button"
                         tooltiptext="&urlbar.geolocationBlocked.tooltip;"/>
                  <image data-permission-id="desktop-notification" class="blocked-permission-icon desktop-notification-icon" role="button"
                         tooltiptext="&urlbar.webNotificationsBlocked.tooltip;"/>
                  <image data-permission-id="camera" class="blocked-permission-icon camera-icon" role="button"
                         tooltiptext="&urlbar.cameraBlocked.tooltip;"/>
                  <image data-permission-id="microphone" class="blocked-permission-icon microphone-icon" role="button"
                         tooltiptext="&urlbar.microphoneBlocked.tooltip;"/>
                  <image data-permission-id="screen" class="blocked-permission-icon screen-icon" role="button"
                         tooltiptext="&urlbar.screenBlocked.tooltip;"/>
                  <image data-permission-id="persistent-storage" class="blocked-permission-icon persistent-storage-icon" role="button"
                         tooltiptext="&urlbar.persistentStorageBlocked.tooltip;"/>
                  <image data-permission-id="popup" class="blocked-permission-icon popup-icon" role="button"
                         tooltiptext="&urlbar.popupBlocked.tooltip;"/>
                  <image data-permission-id="canvas" class="blocked-permission-icon canvas-icon" role="button"
                         tooltiptext="&urlbar.canvasBlocked.tooltip;"/>
                  <image data-permission-id="plugin:flash" class="blocked-permission-icon plugin-icon" role="button"
                         tooltiptext="&urlbar.flashPluginBlocked.tooltip;"/>
                  <image data-permission-id="midi" class="blocked-permission-icon midi-icon" role="button"
                         tooltiptext="&urlbar.midiBlocked.tooltip;"/>
                </box>
                <box id="notification-popup-box"
                     hidden="true"
                     onmouseover="document.getElementById('identity-box').classList.add('no-hover');"
                     onmouseout="document.getElementById('identity-box').classList.remove('no-hover');"
                     align="center">
                  <image id="default-notification-icon" class="notification-anchor-icon" role="button"
                         tooltiptext="&urlbar.defaultNotificationAnchor.tooltip;"/>
                  <image id="geo-notification-icon" class="notification-anchor-icon geo-icon" role="button"
                         tooltiptext="&urlbar.geolocationNotificationAnchor.tooltip;"/>
                  <image id="addons-notification-icon" class="notification-anchor-icon install-icon" role="button"
                         tooltiptext="&urlbar.addonsNotificationAnchor.tooltip;"/>
                  <image id="canvas-notification-icon" class="notification-anchor-icon" role="button"
                         tooltiptext="&urlbar.canvasNotificationAnchor.tooltip;"/>
                  <image id="indexedDB-notification-icon" class="notification-anchor-icon indexedDB-icon" role="button"
                         tooltiptext="&urlbar.indexedDBNotificationAnchor.tooltip;"/>
                  <image id="password-notification-icon" class="notification-anchor-icon login-icon" role="button"
                         tooltiptext="&urlbar.passwordNotificationAnchor.tooltip;"/>
                  <stack id="plugins-notification-icon" class="notification-anchor-icon" role="button" align="center"
                         tooltiptext="&urlbar.pluginsNotificationAnchor.tooltip;">
                    <image class="plugin-icon" />
                    <image id="plugin-icon-badge" />
                  </stack>
                  <image id="web-notifications-notification-icon" class="notification-anchor-icon desktop-notification-icon" role="button"
                         tooltiptext="&urlbar.webNotificationAnchor.tooltip;"/>
                  <image id="webRTC-shareDevices-notification-icon" class="notification-anchor-icon camera-icon" role="button"
                         tooltiptext="&urlbar.webRTCShareDevicesNotificationAnchor.tooltip;"/>
                  <image id="webRTC-shareMicrophone-notification-icon" class="notification-anchor-icon microphone-icon" role="button"
                         tooltiptext="&urlbar.webRTCShareMicrophoneNotificationAnchor.tooltip;"/>
                  <image id="webRTC-shareScreen-notification-icon" class="notification-anchor-icon screen-icon" role="button"
                         tooltiptext="&urlbar.webRTCShareScreenNotificationAnchor.tooltip;"/>
                  <image id="servicesInstall-notification-icon" class="notification-anchor-icon service-icon" role="button"
                         tooltiptext="&urlbar.servicesNotificationAnchor.tooltip;"/>
                  <image id="translate-notification-icon" class="notification-anchor-icon translation-icon" role="button"
                         tooltiptext="&urlbar.translateNotificationAnchor.tooltip;"/>
                  <image id="translated-notification-icon" class="notification-anchor-icon translation-icon in-use" role="button"
                         tooltiptext="&urlbar.translatedNotificationAnchor.tooltip;"/>
                  <image id="eme-notification-icon" class="notification-anchor-icon drm-icon" role="button"
                         tooltiptext="&urlbar.emeNotificationAnchor.tooltip;"/>
                  <image id="persistent-storage-notification-icon" class="notification-anchor-icon persistent-storage-icon" role="button"
                         tooltiptext="&urlbar.persistentStorageNotificationAnchor.tooltip;"/>
                  <image id="midi-notification-icon" class="notification-anchor-icon midi-icon" role="button"
                         tooltiptext="&urlbar.midiNotificationAnchor.tooltip;"/>
                  <image id="webauthn-notification-icon" class="notification-anchor-icon" role="button"
                         tooltiptext="&urlbar.defaultNotificationAnchor.tooltip;"/>
                </box>
                <image id="connection-icon"/>
                <image id="extension-icon"/>
                <image id="remote-control-icon"
                       tooltiptext="&urlbar.remoteControlNotificationAnchor.tooltip;"/>
                <hbox id="identity-icon-labels">
                  <label id="identity-icon-label" class="plain" flex="1"/>
                  <label id="identity-icon-country-label" class="plain"/>
                </hbox>
              </box>
              <box id="urlbar-display-box" align="center">
                <label id="switchtab" class="urlbar-display urlbar-display-switchtab" value="&urlbar.switchToTab.label;"/>
                <label id="extension" class="urlbar-display urlbar-display-extension" value="&urlbar.extension.label;"/>
              </box>
              <hbox id="page-action-buttons" context="pageActionContextMenu">
                <hbox id="userContext-icons" hidden="true">
                  <label id="userContext-label"/>
                  <image id="userContext-indicator"/>
                </hbox>
                <image id="reader-mode-button"
                       class="urlbar-icon urlbar-page-action"
                       tooltip="dynamic-shortcut-tooltip"
                       role="button"
                       hidden="true"
                       onclick="ReaderParent.buttonClick(event);"/>
                <toolbarbutton id="urlbar-zoom-button"
                       onclick="FullZoom.reset();"
                       tooltip="dynamic-shortcut-tooltip"
                       hidden="true"/>
                <box id="pageActionSeparator" class="urlbar-page-action"/>
                <image id="pageActionButton"
                       class="urlbar-icon urlbar-page-action"
                       role="button"
                       tooltiptext="&pageActionButton.tooltip;"
                       onmousedown="BrowserPageActions.mainButtonClicked(event);"/>
                <hbox id="star-button-box"
                      hidden="true"
                      class="urlbar-icon-wrapper urlbar-page-action"
                      onclick="BrowserPageActions.doCommandForAction(PageActions.actionForID('bookmark'), event, this);">
                  <image id="star-button"
                         class="urlbar-icon"
                         role="button"
                         observes="bookmarkThisPageBroadcaster"/>
                  <hbox id="star-button-animatable-box">
                    <image id="star-button-animatable-image"
                           role="presentation"
                           observes="bookmarkThisPageBroadcaster"/>
                  </hbox>
                </hbox>
              </hbox>
            </textbox>
        </toolbaritem>

        <toolbarspring cui-areatype="toolbar" class="chromeclass-toolbar-additional"/>

        <!-- This is a placeholder for the Downloads Indicator.  It is visible
             during the customization of the toolbar, in the palette, and before
             the Downloads Indicator overlay is loaded. -->
        <toolbarbutton id="downloads-button"
                       class="toolbarbutton-1 chromeclass-toolbar-additional badged-button"
                       key="key_openDownloads"
                       onmousedown="DownloadsIndicatorView.onCommand(event);"
                       ondrop="DownloadsIndicatorView.onDrop(event);"
                       ondragover="DownloadsIndicatorView.onDragOver(event);"
                       ondragenter="DownloadsIndicatorView.onDragOver(event);"
                       label="&downloads.label;"
                       removable="true"
                       overflows="false"
                       cui-areatype="toolbar"
                       hidden="true"
                       tooltip="dynamic-shortcut-tooltip"
                       indicator="true">
            <!-- The panel's anchor area is smaller than the outer button, but must
                 always be visible and must not move or resize when the indicator
                 state changes, otherwise the panel could change its position or lose
                 its arrow unexpectedly. -->
            <stack id="downloads-indicator-anchor"
                   consumeanchor="downloads-button">
              <box id="downloads-indicator-icon"/>
              <stack id="downloads-indicator-progress-outer">
                <box id="downloads-indicator-progress-inner"/>
              </stack>
            </stack>
          </toolbarbutton>

        <toolbarbutton id="library-button" class="toolbarbutton-1 chromeclass-toolbar-additional subviewbutton-nav"
                       removable="true"
                       onmousedown="PanelUI.showSubView('appMenu-libraryView', this, event);"
                       closemenu="none"
                       cui-areatype="toolbar"
                       tooltiptext="&libraryButton.tooltip;"
                       label="&places.library.title;"/>

      </hbox>

      <toolbarbutton id="nav-bar-overflow-button"
                     class="toolbarbutton-1 chromeclass-toolbar-additional overflow-button"
                     skipintoolbarset="true"
                     tooltiptext="&navbarOverflow.label;">
        <box class="toolbarbutton-animatable-box">
          <image class="toolbarbutton-animatable-image"/>
        </box>
      </toolbarbutton>

      <toolbaritem id="PanelUI-button"
                   removable="false">
        <toolbarbutton id="PanelUI-menu-button"
                       class="toolbarbutton-1 badged-button"
                       consumeanchor="PanelUI-button"
                       label="&brandShortName;"
                       tooltiptext="&appmenu.tooltip;"/>
      </toolbaritem>

      <hbox id="window-controls" hidden="true" pack="end" skipintoolbarset="true"
            ordinal="1000">
        <toolbarbutton id="minimize-button"
                       tooltiptext="&fullScreenMinimize.tooltip;"
                       oncommand="window.minimize();"/>

        <toolbarbutton id="restore-button"
                       tooltiptext="&fullScreenRestore.tooltip;"
                       oncommand="BrowserFullScreen();"/>

        <toolbarbutton id="close-button"
                       tooltiptext="&fullScreenClose.tooltip;"
                       oncommand="BrowserTryToCloseWindow();"/>
      </hbox>
    </toolbar>

    <toolbar id="PersonalToolbar"
             mode="icons"
             class="chromeclass-directories"
             context="toolbar-context-menu"
             toolbarname="&personalbarCmd.label;" accesskey="&personalbarCmd.accesskey;"
             collapsed="true"
             customizable="true">
      <toolbaritem id="personal-bookmarks"
                   title="&bookmarksToolbarItem.label;"
                   cui-areatype="toolbar"
                   removable="true">
        <toolbarbutton id="bookmarks-toolbar-placeholder"
                       class="bookmark-item"
                       label="&bookmarksToolbarItem.label;"/>
        <toolbarbutton id="bookmarks-toolbar-button"
                       class="toolbarbutton-1"
                       flex="1"
                       label="&bookmarksToolbarItem.label;"
                       oncommand="PlacesToolbarHelper.onPlaceholderCommand();"/>
        <hbox flex="1"
              id="PlacesToolbar"
              context="placesContext"
              onmouseup="BookmarksEventHandler.onMouseUp(event);"
              onclick="BookmarksEventHandler.onClick(event, this._placesView);"
              oncommand="BookmarksEventHandler.onCommand(event);"
              tooltip="bhTooltip"
              popupsinherittooltip="true">
          <hbox flex="1">
            <hbox id="PlacesToolbarDropIndicatorHolder" align="center" collapsed="true">
              <image id="PlacesToolbarDropIndicator"
                     mousethrough="always"
                     collapsed="true"/>
            </hbox>
            <scrollbox orient="horizontal"
                       id="PlacesToolbarItems"
                       flex="1"/>
            <toolbarbutton type="menu"
                           id="PlacesChevron"
                           class="toolbarbutton-1"
                           mousethrough="never"
                           collapsed="true"
                           tooltiptext="&bookmarksToolbarChevron.tooltip;"
                           onpopupshowing="document.getElementById('PlacesToolbar')
                                                   ._placesView._onChevronPopupShowing(event);">
              <menupopup id="PlacesChevronPopup"
                         placespopup="true"
                         tooltip="bhTooltip" popupsinherittooltip="true"
                         context="placesContext"/>
            </toolbarbutton>
          </hbox>
        </hbox>
      </toolbaritem>
    </toolbar>

    <toolbarpalette id="BrowserToolbarPalette">

      <toolbarbutton id="print-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
                     command="cmd_printPreview"
                     tooltiptext="&printButton.tooltip;"
                     label="&printButton.label;"/>


      <toolbarbutton id="new-window-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
                     label="&newNavigatorCmd.label;"
                     command="cmd_newNavigator"
                     tooltip="dynamic-shortcut-tooltip"
                     ondrop="newWindowButtonObserver.onDrop(event)"
                     ondragover="newWindowButtonObserver.onDragOver(event)"
                     ondragenter="newWindowButtonObserver.onDragOver(event)"
                     ondragexit="newWindowButtonObserver.onDragExit(event)"/>

      <toolbarbutton id="fullscreen-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
                     observes="View:FullScreen"
                     type="checkbox"
                     label="&fullScreenCmd.label;"
                     tooltip="dynamic-shortcut-tooltip"/>

      <toolbarbutton id="bookmarks-menu-button"
                     class="toolbarbutton-1 chromeclass-toolbar-additional subviewbutton-nav"
                     type="menu"
                     label="&bookmarksMenuButton2.label;"
                     tooltip="dynamic-shortcut-tooltip"
                     anchor="dropmarker"
                     ondragenter="PlacesMenuDNDHandler.onDragEnter(event);"
                     ondragover="PlacesMenuDNDHandler.onDragOver(event);"
                     ondragleave="PlacesMenuDNDHandler.onDragLeave(event);"
                     ondrop="PlacesMenuDNDHandler.onDrop(event);"
                     oncommand="BookmarkingUI.onCommand(event);">
        <observes element="bookmarkThisPageBroadcaster" attribute="starred"/>
        <observes element="bookmarkThisPageBroadcaster" attribute="buttontooltiptext"/>
        <menupopup id="BMB_bookmarksPopup"
                   class="cui-widget-panel cui-widget-panelview cui-widget-panelWithFooter PanelUI-subView"
                   placespopup="true"
                   context="placesContext"
                   openInTabs="children"
                   onmouseup="BookmarksEventHandler.onMouseUp(event);"
                   oncommand="BookmarksEventHandler.onCommand(event);"
                   onclick="BookmarksEventHandler.onClick(event, this.parentNode._placesView);"
                   onpopupshowing="BookmarkingUI.onPopupShowing(event);
                                   BookmarkingUI.attachPlacesView(event, this);"
                   tooltip="bhTooltip" popupsinherittooltip="true">
          <menuitem id="BMB_viewBookmarksSidebar"
                    class="menuitem-iconic subviewbutton"
                    label-show="&viewBookmarksSidebar2.label;"
                    label-hide="&hideBookmarksSidebar.label;"
                    oncommand="SidebarUI.toggle('viewBookmarksSidebar');"/>
          <!-- NB: temporary solution for bug 985024, this should go away soon. -->
          <menuitem id="BMB_bookmarksShowAllTop"
                    class="menuitem-iconic subviewbutton"
                    label="&showAllBookmarks2.label;"
                    command="Browser:ShowAllBookmarks"
                    key="manBookmarkKb"/>
          <menuseparator/>
          <menu id="BMB_bookmarksToolbar"
                class="menu-iconic bookmark-item subviewbutton"
                label="&personalbarCmd.label;"
                container="true">
            <menupopup id="BMB_bookmarksToolbarPopup"
                       placespopup="true"
                       context="placesContext"
                       onpopupshowing="if (!this.parentNode._placesView)
                                         new PlacesMenu(event, 'place:folder=TOOLBAR',
                                                        PlacesUIUtils.getViewForNode(this.parentNode.parentNode).options);">
              <menuitem id="BMB_viewBookmarksToolbar"
                        class="menuitem-iconic subviewbutton"
                        label-show="&viewBookmarksToolbar.label;"
                        label-hide="&hideBookmarksToolbar.label;"
                        oncommand="BookmarkingUI.toggleBookmarksToolbar();"/>
              <menuseparator/>
              <!-- Bookmarks toolbar items -->
            </menupopup>
          </menu>
          <menu id="BMB_unsortedBookmarks"
                class="menu-iconic bookmark-item subviewbutton"
                label="&bookmarksMenuButton.other.label;"
                container="true">
            <menupopup id="BMB_unsortedBookmarksPopup"
                       placespopup="true"
                       context="placesContext"
                       onpopupshowing="if (!this.parentNode._placesView)
                                         new PlacesMenu(event, 'place:folder=UNFILED_BOOKMARKS',
                                                        PlacesUIUtils.getViewForNode(this.parentNode.parentNode).options);"/>
          </menu>
          <menu id="BMB_mobileBookmarks"
                class="menu-iconic bookmark-item subviewbutton"
                label="&bookmarksMenuButton.mobile.label;"
                hidden="true"
                container="true">
            <menupopup id="BMB_mobileBookmarksPopup"
                       placespopup="true"
                       context="placesContext"
                       onpopupshowing="if (!this.parentNode._placesView)
                                         new PlacesMenu(event, 'place:folder=MOBILE_BOOKMARKS',
                                                        PlacesUIUtils.getViewForNode(this.parentNode.parentNode).options);"/>
          </menu>

          <menuseparator/>
          <!-- Bookmarks menu items will go here -->
          <menuitem id="BMB_bookmarksShowAll"
                    class="subviewbutton panel-subview-footer"
                    label="&showAllBookmarks2.label;"
                    command="Browser:ShowAllBookmarks"
                    key="manBookmarkKb"/>
        </menupopup>
      </toolbarbutton>

      <toolbaritem id="search-container"
                   class="chromeclass-toolbar-additional"
                   title="&searchItem.title;"
                   align="center"
                   flex="100"
                   persist="width">
        <searchbar id="searchbar" flex="1"/>
      </toolbaritem>
    </toolbarpalette>
    <box id="library-animatable-box" class="toolbarbutton-animatable-box">
      <image class="toolbarbutton-animatable-image"/>
    </box>
  </toolbox>

  <hbox id="fullscr-toggler" hidden="true"/>

  <deck id="content-deck" flex="1">
    <hbox flex="1" id="browser">
      <vbox id="browser-border-start" hidden="true" layer="true"/>
      <vbox id="sidebar-box" hidden="true" class="chromeclass-extrachrome">
        <sidebarheader id="sidebar-header" align="center">
          <toolbarbutton id="sidebar-switcher-target" flex="1" class="tabbable">
            <image id="sidebar-icon" consumeanchor="sidebar-switcher-target"/>
            <label id="sidebar-title" persist="value" crop="end" flex="1" control="sidebar"/>
            <image id="sidebar-switcher-arrow"/>
          </toolbarbutton>
          <image id="sidebar-throbber"/>
          <spacer flex="1000"/>
          <toolbarbutton id="sidebar-close" class="close-icon tabbable" tooltiptext="&sidebarCloseButton.tooltip;" oncommand="SidebarUI.hide();"/>
        </sidebarheader>
        <browser id="sidebar" flex="1" autoscroll="false" disablehistory="true" disablefullscreen="true"
                  style="min-width: 14em; width: 18em; max-width: 36em;" tooltip="aHTMLTooltip"/>
      </vbox>

      <splitter id="sidebar-splitter" class="chromeclass-extrachrome sidebar-splitter" hidden="true"/>
      <vbox id="appcontent" flex="1">
        <notificationbox id="high-priority-global-notificationbox" notificationside="top"/>
        <tabbox id="tabbrowser-tabbox"
                    flex="1" eventnode="document" tabcontainer="tabbrowser-tabs"
                    onselect="if (event.target.localName == 'tabpanels') gBrowser.updateCurrentBrowser();">
          <tabpanels flex="1" class="plain" selectedIndex="0" id="tabbrowser-tabpanels">
            <notificationbox flex="1" notificationside="top">
              <hbox flex="1" class="browserSidebarContainer">
                <vbox flex="1" class="browserContainer">
                  <stack flex="1" class="browserStack">
                    <browser id="tabbrowser-initialBrowser" type="content"
                             message="true" messagemanagergroup="browsers"
                             primary="true" blank="true"
                             tooltip="aHTMLTooltip"
                             contextmenu="contentAreaContextMenu"
                             autocompletepopup="PopupAutoComplete"
                             selectmenulist="ContentSelectDropdown"
                             datetimepicker="DateTimePickerPanel"/>
                  </stack>
                </vbox>
              </hbox>
            </notificationbox>
          </tabpanels>
        </tabbox>
      </vbox>
      <vbox id="browser-border-end" hidden="true" layer="true"/>
    </hbox>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<box id="customization-container" flex="1" hidden="true">
  <box id="customization-content-container">
    <box flex="1" id="customization-palette-container">
      <label id="customization-header">
        &customizeMode.menuAndToolbars.header3;
      </label>
      <vbox id="customization-palette" class="customization-palette" hidden="true"/>
      <vbox id="customization-pong-arena" hidden="true"/>
      <spacer id="customization-spacer"/>
    </box>
    <vbox id="customization-panel-container">
      <vbox id="customization-panelWrapper">
        <box class="panel-arrowbox">
          <image class="panel-arrow" side="top"/>
        </box>
        <box class="panel-arrowcontent" side="top" flex="1">
          <vbox id="customization-panelHolder">
            <description id="customization-panelHeader">&customizeMode.overflowList.title2;</description>
            <description id="customization-panelDescription">&customizeMode.overflowList.description;</description>
          </vbox>
          <box class="panel-inner-arrowcontentfooter" hidden="true"/>
        </box>
      </vbox>
    </vbox>
  </box>
  <hbox id="customization-footer">
    <checkbox id="customization-titlebar-visibility-checkbox" class="customizationmode-checkbox"
              label="&customizeMode.titlebar;"
              oncommand="gCustomizeMode.toggleTitlebar(this.checked)"/>
    <checkbox id="customization-extra-drag-space-checkbox" class="customizationmode-checkbox"
              label="&customizeMode.extraDragSpace;"
              oncommand="gCustomizeMode.toggleDragSpace(this.checked)"/>
    <button id="customization-toolbar-visibility-button" label="&customizeMode.toolbars2;" class="customizationmode-button" type="menu">
      <menupopup id="customization-toolbar-menu" onpopupshowing="onViewToolbarsPopupShowing(event)"/>
    </button>
    <button id="customization-lwtheme-button" label="&customizeMode.lwthemes;" class="customizationmode-button" type="menu">
      <panel type="arrow" id="customization-lwtheme-menu"
             onpopupshowing="gCustomizeMode.onLWThemesMenuShowing(event);"
             position="topcenter bottomleft"
             flip="none"
             role="menu">
        <label id="customization-lwtheme-menu-header" value="&customizeMode.lwthemes.myThemes;"/>
        <label id="customization-lwtheme-menu-recommended" value="&customizeMode.lwthemes.recommended;"/>
        <hbox id="customization-lwtheme-menu-footer">
          <toolbarbutton class="customization-lwtheme-menu-footeritem"
                         label="&customizeMode.lwthemes.menuManage;"
                         accesskey="&customizeMode.lwthemes.menuManage.accessKey;"
                         tabindex="0"
                         oncommand="gCustomizeMode.openAddonsManagerThemes(event);"/>
          <toolbarbutton class="customization-lwtheme-menu-footeritem"
                         label="&customizeMode.lwthemes.menuGetMore;"
                         accesskey="&customizeMode.lwthemes.menuGetMore.accessKey;"
                         tabindex="0"
                         oncommand="gCustomizeMode.getMoreThemes(event);"/>
        </hbox>
      </panel>
    </button>
    <button id="customization-uidensity-button"
            label="&customizeMode.uidensity;"
            class="customizationmode-button"
            type="menu">
      <panel type="arrow" id="customization-uidensity-menu"
             onpopupshowing="gCustomizeMode.onUIDensityMenuShowing();"
             position="topcenter bottomleft"
             flip="none"
             role="menu">
        <menuitem id="customization-uidensity-menuitem-compact"
                  class="menuitem-iconic customization-uidensity-menuitem"
                  role="menuitemradio"
                  label="&customizeMode.uidensity.menuCompact.label;"
                  accesskey="&customizeMode.uidensity.menuCompact.accessKey;"
                  tooltiptext="&customizeMode.uidensity.menuCompact.tooltip;"
                  tabindex="0"
                  onfocus="gCustomizeMode.updateUIDensity(this.mode);"
                  onmouseover="gCustomizeMode.updateUIDensity(this.mode);"
                  onblur="gCustomizeMode.resetUIDensity();"
                  onmouseout="gCustomizeMode.resetUIDensity();"
                  oncommand="gCustomizeMode.setUIDensity(this.mode);" />
        <menuitem id="customization-uidensity-menuitem-normal"
                  class="menuitem-iconic customization-uidensity-menuitem"
                  role="menuitemradio"
                  label="&customizeMode.uidensity.menuNormal.label;"
                  accesskey="&customizeMode.uidensity.menuNormal.accessKey;"
                  tooltiptext="&customizeMode.uidensity.menuNormal.tooltip;"
                  tabindex="0"
                  onfocus="gCustomizeMode.updateUIDensity(this.mode);"
                  onmouseover="gCustomizeMode.updateUIDensity(this.mode);"
                  onblur="gCustomizeMode.resetUIDensity();"
                  onmouseout="gCustomizeMode.resetUIDensity();"
                  oncommand="gCustomizeMode.setUIDensity(this.mode);" />
        <menuitem id="customization-uidensity-menuitem-touch"
                  class="menuitem-iconic customization-uidensity-menuitem"
                  role="menuitemradio"
                  label="&customizeMode.uidensity.menuTouch.label;"
                  accesskey="&customizeMode.uidensity.menuTouch.accessKey;"
                  tooltiptext="&customizeMode.uidensity.menuTouch.tooltip;"
                  tabindex="0"
                  onfocus="gCustomizeMode.updateUIDensity(this.mode);"
                  onmouseover="gCustomizeMode.updateUIDensity(this.mode);"
                  onblur="gCustomizeMode.resetUIDensity();"
                  onmouseout="gCustomizeMode.resetUIDensity();"
                  oncommand="gCustomizeMode.setUIDensity(this.mode);">
        </menuitem>
        <spacer hidden="true" id="customization-uidensity-touch-spacer"/>
        <checkbox id="customization-uidensity-autotouchmode-checkbox"
                  hidden="true"
                  label="&customizeMode.uidensity.autoTouchMode.checkbox.label;"
                  oncommand="gCustomizeMode.updateAutoTouchMode(this.checked)"/>
      </panel>
    </button>

    <button id="whimsy-button"
            type="checkbox"
            class="customizationmode-button"
            oncommand="gCustomizeMode.togglePong(this.checked);"
            hidden="true"/>

    <spacer id="customization-footer-spacer"/>
    <button id="customization-undo-reset-button"
            class="customizationmode-button"
            hidden="true"
            oncommand="gCustomizeMode.undoReset();"
            label="&undoCmd.label;"/>
    <button id="customization-reset-button"
            oncommand="gCustomizeMode.reset();"
            label="&customizeMode.restoreDefaults;"
            class="customizationmode-button"/>
    <button id="customization-done-button"
            oncommand="gCustomizeMode.exit();"
            label="&customizeMode.done;"
            class="customizationmode-button"/>
  </hbox>
</box>
  </deck>

  <html:div id="fullscreen-warning" class="pointerlockfswarning" hidden="true">
    <html:div class="pointerlockfswarning-domain-text">
      &fullscreenWarning.beforeDomain.label;
      <html:span class="pointerlockfswarning-domain"/>
      &fullscreenWarning.afterDomain.label;
    </html:div>
    <html:div class="pointerlockfswarning-generic-text">
      &fullscreenWarning.generic.label;
    </html:div>
    <html:button id="fullscreen-exit-button"
                 onclick="FullScreen.exitDomFullScreen();">
            &exitDOMFullscreen.button;
    </html:button>
  </html:div>

  <html:div id="pointerlock-warning" class="pointerlockfswarning" hidden="true">
    <html:div class="pointerlockfswarning-domain-text">
      &pointerlockWarning.beforeDomain.label;
      <html:span class="pointerlockfswarning-domain"/>
      &pointerlockWarning.afterDomain.label;
    </html:div>
    <html:div class="pointerlockfswarning-generic-text">
      &pointerlockWarning.generic.label;
    </html:div>
  </html:div>

  <vbox id="browser-bottombox" layer="true">
    <notificationbox id="global-notificationbox" notificationside="bottom"/>
  </vbox>

</window>
PK
!<���w����*chrome/browser/content/browser/browser.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
@namespace html url("http://www.w3.org/1999/xhtml");

:root {
  --panelui-subview-transition-duration: 150ms;
  --lwt-additional-images: none;
  --lwt-background-alignment: right top;
  --lwt-background-tiling: no-repeat;
}

:root:-moz-lwtheme {
  color: var(--lwt-text-color) !important;
}

:root:-moz-lwtheme {
  background-color: var(--lwt-accent-color) !important;
  background-image: var(--lwt-additional-images) !important;
  background-position: var(--lwt-background-alignment) !important;
  background-repeat: var(--lwt-background-tiling) !important;
}

:root:-moz-lwtheme[lwtheme-image] {
  background-image: var(--lwt-header-image), var(--lwt-additional-images) !important;
}

:root:-moz-lwtheme:-moz-window-inactive {
  background-color: var(--lwt-accent-color-inactive, var(--lwt-accent-color)) !important;
}

#main-window:not([chromehidden~="toolbar"]) {
  min-width: 300px;
}

#main-window[customize-entered] {
  min-width: -moz-fit-content;
}

searchbar {
  -moz-binding: url("chrome://browser/content/search/search.xml#searchbar");
}

/* Prevent shrinking the page content to 0 height and width */
.browserStack > browser {
  min-height: 25px;
  min-width: 25px;
}

.browserStack > browser {
  -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-browser");
}

.browserStack > browser[remote="true"] {
  -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-remote-browser");
}

toolbar[customizable="true"] {
  -moz-binding: url("chrome://browser/content/customizableui/toolbar.xml#toolbar");
}


#toolbar-menubar[autohide="true"] {
  -moz-binding: url("chrome://browser/content/customizableui/toolbar.xml#toolbar-menubar-autohide");
}

panelmultiview {
  -moz-box-align: start;
}

panelmultiview[transitioning] {
  pointer-events: none;
}

panelview {
  -moz-box-orient: vertical;
}

panelview:not([visible]) {
  visibility: collapse;
}

/* Hide the header when a subview is reused as a main view. */
panelview[mainview] > .panel-header {
  display: none;
}

.panel-viewcontainer {
  overflow: hidden;
}

.panel-viewcontainer[panelopen] {
  transition-property: height;
  transition-timing-function: var(--animation-easing-function);
  transition-duration: var(--panelui-subview-transition-duration);
  will-change: height;
}

.panel-viewcontainer.offscreen {
  position: absolute;
}

.panel-viewstack {
  overflow: visible;
  transition: height var(--panelui-subview-transition-duration);
}

#tabbrowser-tabs {
  -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-tabs");
}

@supports -moz-bool-pref("layout.css.emulate-moz-box-with-flex") {
  #tabbrowser-tabs {
    /* Without this, the tabs container width extends beyond the window width */
    width: 0;
  }
  .tab-stack {
    /* Without this, pinned tabs get a bit too tall when the tabstrip overflows. */
    vertical-align: top;
  }
}


#tabbrowser-tabs:not([overflow="true"]) ~ #alltabs-button,
#tabbrowser-tabs[hasadjacentnewtabbutton]:not([overflow="true"]) ~ #new-tab-button,
#tabbrowser-tabs[overflow="true"] > .tabbrowser-arrowscrollbox > .tabs-newtab-button,
#tabbrowser-tabs:not([hasadjacentnewtabbutton]) > .tabbrowser-arrowscrollbox > .tabs-newtab-button,
#TabsToolbar[customizing="true"] > #tabbrowser-tabs > .tabbrowser-arrowscrollbox > .tabs-newtab-button {
  visibility: collapse;
}

#tabbrowser-tabs:not([overflow="true"])[using-closing-tabs-spacer] ~ #alltabs-button {
  visibility: hidden; /* temporary space to keep a tab's close button under the cursor */
}

.tabbrowser-tab {
  -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-tab");
}

.tabbrowser-tab:not([pinned]) {
  -moz-box-flex: 100;
  max-width: 225px;
  min-width: var(--tab-min-width);
  width: 0;
  transition: min-width 100ms ease-out,
              max-width 100ms ease-out;
}

:root[uidensity=touch] .tabbrowser-tab:not([pinned]) {
  /* Touch mode needs additional space for the close button. */
  min-width: calc(var(--tab-min-width) + 10px);
}

.tabbrowser-tab:not([pinned]):not([fadein]) {
  max-width: 0.1px;
  min-width: 0.1px;
  visibility: hidden;
}

.tab-icon-image,
.tab-close-button,
.tabbrowser-tab::after,
.tab-background {
  /* Explicitly set the visibility to override the value (collapsed)
   * we inherit from #TabsToolbar[collapsed] upon opening a browser window. */
  visibility: visible;
}

.tab-icon-image[fadein],
.tab-close-button[fadein],
.tabbrowser-tab[fadein]::after,
.tab-background[fadein] {
  /* This transition is only wanted for opening tabs. */
  transition: visibility 0ms 25ms;
}

.tab-icon-image:not([fadein]),
.tab-close-button:not([fadein]),
.tabbrowser-tab:not([fadein])::after,
.tab-background:not([fadein]) {
  visibility: hidden;
}

.tab-label:not([fadein]),
.tab-throbber:not([fadein]),
.tab-throbber-fallback:not([fadein]) {
  display: none;
}

#tabbrowser-tabs[positionpinnedtabs] > .tabbrowser-tab[pinned] {
  position: fixed !important;
  display: block; /* position:fixed already does this (bug 579776), but let's be explicit */
}

#tabbrowser-tabs[movingtab] > .tabbrowser-tab[selected] {
  position: relative;
  z-index: 2;
  pointer-events: none; /* avoid blocking dragover events on scroll buttons */
}

.tabbrowser-tab[tabdrop-samewindow],
#tabbrowser-tabs[movingtab] > .tabbrowser-tab[fadein]:not([selected]) {
  transition: transform 200ms var(--animation-easing-function);
}

/* The next 3 rules allow dragging tabs slightly outside of the tabstrip
 * to make it easier to drag tabs. */
#TabsToolbar[movingtab] {
  padding-bottom: 15px;
}

#TabsToolbar[movingtab] > #tabbrowser-tabs {
  padding-bottom: 15px;
  margin-bottom: -15px;
}

#TabsToolbar[movingtab] + #nav-bar {
  margin-top: -15px;
  pointer-events: none;
}

/* Allow dropping a tab on buttons with associated drop actions. */
#TabsToolbar[movingtab] + #nav-bar > #nav-bar-customization-target > #personal-bookmarks,
#TabsToolbar[movingtab] + #nav-bar > #nav-bar-customization-target > #home-button,
#TabsToolbar[movingtab] + #nav-bar > #nav-bar-customization-target > #downloads-button,
#TabsToolbar[movingtab] + #nav-bar > #nav-bar-customization-target > #bookmarks-menu-button {
  pointer-events: auto;
}

.new-tab-popup,
#alltabs-popup {
  -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-alltabs-popup");
}

toolbar[printpreview="true"] {
  -moz-binding: url("chrome://global/content/printPreviewBindings.xml#printpreviewtoolbar");
}

toolbar[overflowable] > .customization-target {
  overflow: hidden;
}

toolbar:not([overflowing]) > .overflow-button,
toolbar[customizing] > .overflow-button {
  display: none;
}

window:not([chromehidden~="toolbar"]) #nav-bar[nonemptyoverflow] > .overflow-button,
#nav-bar[customizing] > .overflow-button {
  display: -moz-box;
}

/* The ids are ugly, but this should be reasonably performant, and
 * using a tagname as the last item would be less so.
 */
#widget-overflow-list:empty + #widget-overflow-fixed-separator,
#widget-overflow:not([hasfixeditems]) #widget-overflow-fixed-separator {
  display: none;
}


#toolbar-menubar:not([autohide=true]) + #TabsToolbar > .titlebar-placeholder,
#main-window:not([sizemode=normal]) .titlebar-placeholder[type="pre-tabs"],
#main-window:not([chromemargin]) > #titlebar,
#main-window[inFullscreen] > #titlebar,
#main-window[inFullscreen] .titlebar-placeholder,
#main-window:not([tabsintitlebar]) .titlebar-placeholder {
  display: none;
}

#titlebar {
  -moz-window-dragging: drag;
}

#titlebar-spacer {
  pointer-events: none;
}

#main-window[tabsintitlebar] #titlebar-buttonbox {
  position: relative;
}

#titlebar-buttonbox {
  -moz-appearance: -moz-window-button-box;
}

#personal-bookmarks {
  -moz-window-dragging: inherit;
}

toolbarpaletteitem {
  -moz-window-dragging: no-drag;
}




#main-window[inFullscreen][inDOMFullscreen] #navigator-toolbox,
#main-window[inFullscreen][inDOMFullscreen] #fullscr-toggler,
#main-window[inFullscreen][inDOMFullscreen] #sidebar-box,
#main-window[inFullscreen][inDOMFullscreen] #sidebar-splitter,
#main-window[inFullscreen][inDOMFullscreen] #developer-toolbar,
#main-window[inFullscreen]:not([OSXLionFullscreen]) toolbar:not([fullscreentoolbar=true]),
#main-window[inFullscreen] #global-notificationbox,
#main-window[inFullscreen] #high-priority-global-notificationbox {
  visibility: collapse;
}

#navigator-toolbox[fullscreenShouldAnimate] {
  transition: 1.5s margin-top ease-out;
}

/* Rules to help integrate WebExtension buttons */

.webextension-browser-action > .toolbarbutton-badge-stack > .toolbarbutton-icon {
  height: 16px;
  width: 16px;
}

@media not all and (min-resolution: 1.1dppx) {
  .webextension-browser-action {
    list-style-image: var(--webextension-toolbar-image, inherit);
  }

  .webextension-browser-action:-moz-lwtheme-brighttext {
    list-style-image: var(--webextension-toolbar-image-light, inherit);
  }

  .webextension-browser-action:-moz-lwtheme-darktext {
    list-style-image: var(--webextension-toolbar-image-dark, inherit);
  }

  .webextension-browser-action[cui-areatype="menu-panel"],
  toolbarpaletteitem[place="palette"] > .webextension-browser-action {
    list-style-image: var(--webextension-menupanel-image, inherit);
  }

  toolbarpaletteitem[place="palette"] > .webextension-browser-action:-moz-lwtheme-brighttext {
    list-style-image: var(--webextension-menupanel-image-light, inherit);
  }

  .webextension-browser-action[cui-areatype="menu-panel"]:-moz-lwtheme-darktext,
  toolbarpaletteitem[place="palette"] > .webextension-browser-action:-moz-lwtheme-darktext {
    list-style-image: var(--webextension-menupanel-image-dark, inherit);
  }

  .webextension-menuitem {
    list-style-image: var(--webextension-menuitem-image, inherit) !important;
  }
}

@media (min-resolution: 1.1dppx) {
  .webextension-browser-action {
    list-style-image: var(--webextension-toolbar-image-2x, inherit);
  }

  .webextension-browser-action:-moz-lwtheme-brighttext {
    list-style-image: var(--webextension-toolbar-image-2x-light, inherit);
  }

  .webextension-browser-action:-moz-lwtheme-darktext {
    list-style-image: var(--webextension-toolbar-image-2x-dark, inherit);
  }

  .webextension-browser-action[cui-areatype="menu-panel"],
  toolbarpaletteitem[place="palette"] > .webextension-browser-action {
    list-style-image: var(--webextension-menupanel-image-2x, inherit);
  }

  toolbarpaletteitem[place="palette"] > .webextension-browser-action:-moz-lwtheme-brighttext {
    list-style-image: var(--webextension-menupanel-image-2x-light, inherit);
  }

  .webextension-browser-action[cui-areatype="menu-panel"]:-moz-lwtheme-darktext,
  toolbarpaletteitem[place="palette"] > .webextension-browser-action:-moz-lwtheme-darktext {
    list-style-image: var(--webextension-menupanel-image-2x-dark, inherit);
  }

  .webextension-menuitem {
    list-style-image: var(--webextension-menuitem-image-2x, inherit) !important;
  }
}

toolbarbutton.webextension-menuitem > .toolbarbutton-icon {
  width: 16px;
  height: 16px;
}

toolbarpaletteitem[removable="false"] {
  opacity: 0.5;
}

toolbarpaletteitem[place="palette"],
toolbarpaletteitem[place="menu-panel"],
toolbarpaletteitem[place="toolbar"] {
  -moz-user-focus: normal;
}

#bookmarks-toolbar-placeholder,
#bookmarks-toolbar-button,
toolbarpaletteitem > #personal-bookmarks > #PlacesToolbar,
#personal-bookmarks:-moz-any([overflowedItem=true], [cui-areatype="menu-panel"]) > #PlacesToolbar {
  display: none;
}

toolbarpaletteitem[place="toolbar"] > #personal-bookmarks > #bookmarks-toolbar-placeholder,
toolbarpaletteitem[place="palette"] > #personal-bookmarks > #bookmarks-toolbar-button,
#personal-bookmarks:-moz-any([overflowedItem=true], [cui-areatype="menu-panel"]) > #bookmarks-toolbar-button {
  display: -moz-box;
}

#personal-bookmarks {
  position: relative;
}

#PlacesToolbarDropIndicatorHolder {
  position: absolute;
}

#nav-bar-customization-target > #personal-bookmarks,
toolbar:not(#TabsToolbar) > #wrapper-personal-bookmarks,
toolbar:not(#TabsToolbar) > #personal-bookmarks {
  -moz-box-flex: 1;
}

#zoom-controls[cui-areatype="toolbar"]:not([overflowedItem=true]) > #zoom-reset-button > .toolbarbutton-text {
  display: -moz-box;
}

#reload-button:not([displaystop]) + #stop-button,
#reload-button[displaystop] {
  display: none;
}

/* The reload-button is only disabled temporarily when it becomes visible
   to prevent users from accidentally clicking it. We don't however need
   to show this disabled state, as the flicker that it generates is short
   enough to be visible but not long enough to explain anything to users. */
#reload-button[disabled]:not(:-moz-window-inactive) > .toolbarbutton-icon {
  opacity: 1 !important;
}

/* Ensure stop-button and reload-button are displayed correctly when in the overflow menu */
.widget-overflow-list > #stop-reload-button > .toolbarbutton-1 {
  -moz-box-flex: 1;
}

#PanelUI-feeds > .feed-toolbarbutton:-moz-locale-dir(rtl) {
  direction: rtl;
}

#appMenu_historyMenu > .bookmark-item,
#appMenu-library-recentlyClosedTabs > .panel-subview-body > .bookmark-item,
#appMenu-library-recentlyClosedWindows > .panel-subview-body > .bookmark-item,
#appMenu-library-recentHighlights > .bookmark-item,
#panelMenu_bookmarksMenu > .bookmark-item {
  max-width: none;
}

#main-window:-moz-lwtheme {
  background-repeat: no-repeat;
  background-position: top right;
}


:root[lwthemefooter=true] #browser-bottombox:-moz-lwtheme {
  background-repeat: no-repeat;
  background-position: bottom left;
  background-color: var(--lwt-accent-color);
  background-image: var(--lwt-footer-image);
}

/* Hide menu elements intended for keyboard access support */
#main-menubar[openedwithkey=false] .show-only-for-keyboard {
  display: none;
}

/* ::::: location bar & search bar ::::: */

/* url bar min-width is defined further down, together with the maximum size
 * of the identity icon block, for different window sizes.
 */
#search-container {
  min-width: 25ch;
}

#urlbar,
.searchbar-textbox {
  /* Setting a width and min-width to let the location & search bars maintain
     a constant width in case they haven't be resized manually. (bug 965772) */
  width: 1px;
  min-width: 1px;
}

#urlbar {
  -moz-binding: url(chrome://browser/content/urlbarBindings.xml#urlbar);
}

/* Display URLs left-to-right but right aligned in RTL mode. */
html|input.urlbar-input:-moz-locale-dir(rtl) {
  direction: ltr !important;
  text-align: right !important;
}

/* Make sure that the location bar's alignment in RTL mode changes according
   to the input box direction if the user switches the text direction using
   cmd_switchTextDirection (which applies a dir attribute to the <input>). */
html|input.urlbar-input[dir=ltr]:-moz-locale-dir(rtl) {
  text-align: left !important;
}

/*
 * Display visual cue that browser is under remote control by Marionette.
 * This is to help users visually distinguish a user agent session that
 * is under remote control from those used for normal browsing sessions.
 *
 * Attribute is controlled by browser.js:/gRemoteControl.
 */
#main-window[remotecontrol] #urlbar {
  background: repeating-linear-gradient(
    -45deg,
    transparent,
    transparent 25px,
    rgba(255,255,255,.3) 25px,
    rgba(255,255,255,.3) 50px);
  background-color: rgba(255,170,68,.8);
  color: black;
}
#main-window[remotecontrol] #urlbar #identity-box {
  background: white;
}

/* Fade out URL on overflow */
html|input.urlbar-input[textoverflow]:not([focused]) {
  /* Don't need to worry about RTL here since we use direction:ltr for the
     input field. */
  mask-image: linear-gradient(to left, transparent, black 3em);
}

/* Apply crisp rendering for favicons at exactly 2dppx resolution */
@media (resolution: 2dppx) {
  .searchbar-engine-image {
    image-rendering: -moz-crisp-edges;
  }
}

/* Always show URLs LTR. */
.ac-url-text:-moz-locale-dir(rtl),
.ac-title-text[lookslikeurl]:-moz-locale-dir(rtl) {
  direction: ltr !important;
}

/* Never show a scrollbar for the Location Bar popup.  This overrides the
   richlistbox inline overflow: auto style.*/
#PopupAutoCompleteRichResult > richlistbox > scrollbox {
  overflow: hidden !important;
}

/* For non-action items, hide the action text; for action items, hide the URL
   text. Don't show the separator for keyword results. */
#PopupAutoCompleteRichResult > richlistbox > richlistitem > .ac-type-icon,
#PopupAutoCompleteRichResult > richlistbox > richlistitem > .ac-site-icon,
#PopupAutoCompleteRichResult > richlistbox > richlistitem > .ac-tags:not([empty]),
#PopupAutoCompleteRichResult > richlistbox > richlistitem > .ac-separator:not([type=keyword]),
#PopupAutoCompleteRichResult > richlistbox > richlistitem > .ac-url:not([actiontype]),
#PopupAutoCompleteRichResult > richlistbox > richlistitem > .ac-action[actiontype] {
  display: -moz-box;
}

/* Only show the "Search with" label on hover or selection. */
#PopupAutoCompleteRichResult > richlistbox > richlistitem:not([selected]):not(:hover) > .ac-action[actiontype=searchengine],
#PopupAutoCompleteRichResult > richlistbox > richlistitem:not([selected]):not(:hover) > .ac-separator[actiontype=searchengine] {
  display: none;
}

#PopupAutoCompleteRichResult > richlistbox > richlistitem > .ac-site-icon {
  margin-inline-start: 0;
}

/* For action items in a noactions popup, show the URL text and hide the action
   text and type icon. */
#PopupAutoCompleteRichResult[noactions] > richlistbox > richlistitem.overridable-action > .ac-url {
  display: -moz-box;
}
#PopupAutoCompleteRichResult[noactions] > richlistbox > richlistitem.overridable-action > .ac-action {
  display: none;
}
#PopupAutoCompleteRichResult[noactions] > richlistbox > richlistitem.overridable-action > .ac-type-icon {
  list-style-image: none;
}

#urlbar:not([actiontype="switchtab"]):not([actiontype="extension"]) > #urlbar-display-box,
#urlbar:not([actiontype="switchtab"]) > #urlbar-display-box > #switchtab,
#urlbar:not([actiontype="extension"]) > #urlbar-display-box > #extension {
  display: none;
}

#PopupAutoComplete[firstresultstyle="insecureWarning"] {
  min-width: 200px;
}

#PopupAutoComplete > richlistbox > richlistitem[originaltype="insecureWarning"] {
  -moz-binding: url("chrome://global/content/bindings/autocomplete.xml#autocomplete-richlistitem-insecure-field");
  height: auto;
}

#PopupAutoComplete > richlistbox > richlistitem[originaltype="insecureWarning"] > .ac-site-icon {
  margin-inline-start: 0;
  display: initial;
}

#PopupAutoComplete > richlistbox > richlistitem[originaltype="insecureWarning"] > .ac-title > .ac-text-overflow-container > .ac-title-text {
  text-overflow: initial;
  white-space: initial;
}

#PopupAutoComplete > richlistbox > richlistitem[originaltype="insecureWarning"] > .ac-title > label {
  margin-inline-start: 0;
}

#PopupSearchAutoComplete {
  -moz-binding: url("chrome://browser/content/search/search.xml#browser-search-autocomplete-result-popup");
}

#PopupAutoCompleteRichResult {
  -moz-binding: url("chrome://browser/content/urlbarBindings.xml#urlbar-rich-result-popup");
}

#PopupAutoCompleteRichResult.showSearchSuggestionsNotification {
  transition: height 100ms;
}

#PopupAutoCompleteRichResult > deck[anonid="search-suggestions-notification"] {
  display: none;
  transition: margin-top 100ms;
}

#PopupAutoCompleteRichResult.showSearchSuggestionsNotification > deck[anonid="search-suggestions-notification"] {
  display: -moz-deck;
}

#PopupAutoCompleteRichResult > richlistbox {
  transition: height 100ms;
}

#PopupAutoCompleteRichResult.showSearchSuggestionsNotification > richlistbox {
  transition: none;
}

#DateTimePickerPanel[active="true"] {
  -moz-binding: url("chrome://global/content/bindings/datetimepopup.xml#datetime-popup");
}

#urlbar[pageproxystate=invalid] > #page-action-buttons > .urlbar-page-action,
#identity-box.chromeUI ~ #page-action-buttons > .urlbar-page-action:not(#star-button-box),
.urlbar-history-dropmarker[usertyping],
.urlbar-go-button:not([usertyping]),
.urlbar-go-button:not([parentfocused="true"]),
#urlbar[pageproxystate="invalid"] > #identity-box > #blocked-permissions-container,
#urlbar[pageproxystate="invalid"] > #identity-box > #notification-popup-box,
#urlbar[pageproxystate="invalid"] > #identity-box > #identity-icon-labels {
  display: none;
}

#identity-box {
  -moz-user-focus: normal;
}

#urlbar[pageproxystate="invalid"] > #identity-box {
  pointer-events: none;
  -moz-user-focus: ignore;
}


/* We leave 49ch plus whatever space the download button will need when it
 * appears. Normally this should be 16px for the icon, plus 2 * 2px padding
 * plus the toolbarbutton-inner-padding. We're adding 4px to ensure things
 * like rounding on hidpi don't accidentally result in the button going
 * into overflow.
 */
#urlbar-container {
  min-width: calc(49ch + 24px + 2 * var(--toolbarbutton-inner-padding));
}

#nav-bar[downloadsbuttonshown] #urlbar-container {
  min-width: 49ch;
}

#identity-icon-labels {
  max-width: 17em;
}
@media (max-width: 700px) {
  #urlbar-container {
    min-width: calc(44ch + 24px + 2 * var(--toolbarbutton-inner-padding));
  }
  #nav-bar[downloadsbuttonshown] #urlbar-container {
    min-width: 44ch;
  }

  #identity-icon-labels {
    max-width: 60px;
  }
}
@media (max-width: 600px) {
  #urlbar-container {
    min-width: calc(39ch + 24px + 2 * var(--toolbarbutton-inner-padding));
  }
  #nav-bar[downloadsbuttonshown] #urlbar-container {
    min-width: 39ch;
  }
  #identity-icon-labels {
    max-width: 50px;
  }
}
@media (max-width: 500px) {
  #urlbar-container {
    min-width: calc(34ch + 24px + 2 * var(--toolbarbutton-inner-padding));
  }
  #nav-bar[downloadsbuttonshown] #urlbar-container {
    min-width: 34ch;
  }
  #identity-icon-labels {
    max-width: 40px;
  }
}
@media (max-width: 400px) {
  #urlbar-container {
    min-width: calc(27ch + 24px + 2 * var(--toolbarbutton-inner-padding));
  }
  #nav-bar[downloadsbuttonshown] #urlbar-container {
    min-width: 27ch;
  }
  #identity-icon-labels {
    max-width: 30px;
  }
}

#identity-icon-country-label {
  direction: ltr;
}

#identity-box.verifiedIdentity > #identity-icon-labels > #identity-icon-label {
  margin-inline-end: 0.25em !important;
}

/* Flexible spacer sizing (matching url bar) */
toolbarpaletteitem[place=toolbar][id^=wrapper-customizableui-special-spring],
toolbarspring {
  -moz-box-flex: 1;
  min-width: 28px;
  max-width: 112px;
}

#nav-bar toolbarpaletteitem[id^=wrapper-customizableui-special-spring],
#nav-bar toolbarspring {
  -moz-box-flex: 80;
}

#widget-overflow-list > toolbarspring {
  display: none;
}

/* ::::: Unified Back-/Forward Button ::::: */
.unified-nav-current {
  font-weight: bold;
}

.bookmark-item > label {
  /* ensure we use the direction of the bookmarks label instead of the
   * browser locale */
  unicode-bidi: plaintext;
}

/* Apply crisp rendering for favicons at exactly 2dppx resolution */
@media (resolution: 2dppx) {
  #alltabs-popup > .menuitem-iconic > .menu-iconic-left > .menu-iconic-icon,
  .menuitem-with-favicon > .menu-iconic-left > .menu-iconic-icon {
    image-rendering: -moz-crisp-edges;
  }

  .bookmark-item > .toolbarbutton-icon,
  .bookmark-item > .menu-iconic-left > .menu-iconic-icon {
    image-rendering: -moz-crisp-edges;
  }
  /* Synced Tabs sidebar */
  html|*.tabs-container html|*.item-tabs-list html|*.item-icon-container {
    image-rendering: -moz-crisp-edges;
  }
}

#editBMPanel_tagsSelector {
  /* override default listbox width from xul.css */
  width: auto;
}

menupopup[emptyplacesresult="true"] > .hide-if-empty-places-result {
  display: none;
}
menuitem.spell-suggestion {
  font-weight: bold;
}

/* Hide extension toolbars that neglected to set the proper class */
window[chromehidden~="location"][chromehidden~="toolbar"] toolbar:not(.chromeclass-menubar),
window[chromehidden~="toolbar"] toolbar:not(#nav-bar):not(#TabsToolbar):not(#print-preview-toolbar):not(.chromeclass-menubar) {
  display: none;
}

#navigator-toolbox ,
#mainPopupSet {
  min-width: 1px;
}

/* History Swipe Animation */

#historySwipeAnimationContainer {
  overflow: hidden;
}

#historySwipeAnimationPreviousPage,
#historySwipeAnimationCurrentPage,
#historySwipeAnimationNextPage {
  background: none top left no-repeat white;
}

#historySwipeAnimationPreviousPage {
  background-image: -moz-element(#historySwipeAnimationPreviousPageSnapshot);
}

#historySwipeAnimationCurrentPage {
  background-image: -moz-element(#historySwipeAnimationCurrentPageSnapshot);
}

#historySwipeAnimationNextPage {
  background-image: -moz-element(#historySwipeAnimationNextPageSnapshot);
}

/*  Full Screen UI */

#fullscr-toggler {
  height: 1px;
  background: black;
}

html|*.pointerlockfswarning {
  position: fixed;
  z-index: 2147483647 !important;
  visibility: visible;
  transition: transform 300ms ease-in;
  /* To center the warning box horizontally,
     we use left: 50% with translateX(-50%). */
  top: 0; left: 50%;
  transform: translate(-50%, -100%);
  box-sizing: border-box;
  width: -moz-max-content;
  max-width: 95%;
  pointer-events: none;
}
html|*.pointerlockfswarning:not([hidden]) {
  display: flex;
  will-change: transform;
}
html|*.pointerlockfswarning[onscreen] {
  transform: translate(-50%, 50px);
}
html|*.pointerlockfswarning[ontop] {
  /* Use -10px to hide the border and border-radius on the top */
  transform: translate(-50%, -10px);
}
#main-window[OSXLionFullscreen] html|*.pointerlockfswarning[ontop] {
  transform: translate(-50%, 80px);
}

html|*.pointerlockfswarning-domain-text,
html|*.pointerlockfswarning-generic-text {
  word-wrap: break-word;
  /* We must specify a min-width, otherwise word-wrap:break-word doesn't work. Bug 630864. */
  min-width: 1px
}
html|*.pointerlockfswarning-domain-text:not([hidden]) + html|*.pointerlockfswarning-generic-text {
  display: none;
}

html|*#fullscreen-exit-button {
  pointer-events: auto;
}

/* ::::: Ctrl-Tab Panel ::::: */

.ctrlTab-preview {
  -moz-binding: url("chrome://browser/content/browser-tabPreviews.xml#ctrlTab-preview");
}


/* notification anchors should only be visible when their associated
   notifications are */
.notification-anchor-icon {
  -moz-user-focus: normal;
}

#blocked-permissions-container > .blocked-permission-icon:not([showing]),
.notification-anchor-icon:not([showing]) {
  display: none;
}

#invalid-form-popup > description {
  max-width: 280px;
}

.popup-anchor {
  /* should occupy space but not be visible */
  opacity: 0;
  pointer-events: none;
  -moz-stack-sizing: ignore;
}

#addon-progress-notification {
  -moz-binding: url("chrome://browser/content/urlbarBindings.xml#addon-progress-notification");
}

browser[tabmodalPromptShowing] {
  -moz-user-focus: none !important;
}

/* Status panel */

statuspanel {
  -moz-binding: url("chrome://browser/content/tabbrowser.xml#statuspanel");
  position: fixed;
  margin-top: -3em;
  max-width: calc(100% - 5px);
  pointer-events: none;
}

statuspanel[mirror] {
  offset-inline-start: auto;
  offset-inline-end: 0;
}

statuspanel[sizelimit] {
  max-width: 50%;
}

statuspanel[type=status] {
  min-width: 23em;
}

@media all and (max-width: 800px) {
  statuspanel[type=status] {
    min-width: 33%;
  }
}

statuspanel[type=overLink] {
  transition: opacity 120ms ease-out;
}

statuspanel[type=overLink] > .statuspanel-inner {
  direction: ltr;
}

statuspanel[inactive] {
  transition: none;
  opacity: 0;
}

statuspanel[inactive][previoustype=overLink] {
  transition: opacity 200ms ease-out;
}

.statuspanel-inner {
  height: 3em;
  width: 100%;
  -moz-box-align: end;
}

/* gcli */

html|*#gcli-tooltip-frame,
html|*#gcli-output-frame,
#gcli-output,
#gcli-tooltip {
  overflow-x: hidden;
}

.gclitoolbar-input-node,
.gclitoolbar-complete-node {
  direction: ltr;
}

#developer-toolbar-toolbox-button[error-count] > .toolbarbutton-icon {
  display: none;
}

#developer-toolbar-toolbox-button[error-count]:before {
  content: attr(error-count);
  display: -moz-box;
  -moz-box-pack: center;
}

/* Translation */
notification[value="translation"] {
  -moz-binding: url("chrome://browser/content/translation-infobar.xml#translationbar");
}

/*** Visibility of downloads indicator controls ***/

/* Bug 924050: If we've loaded the indicator, for now we hide it in the menu panel,
   and just show the icon. This is a hack to side-step very weird layout bugs that
   seem to be caused by the indicator stack interacting with the menu panel. */
#downloads-button[indicator]:not([cui-areatype="menu-panel"]) > .toolbarbutton-badge-stack > image.toolbarbutton-icon,
#downloads-button[indicator][cui-areatype="menu-panel"] > #downloads-indicator-anchor {
  display: none;
}

toolbarpaletteitem[place="palette"] > #downloads-button[indicator] > .toolbarbutton-badge-stack > image.toolbarbutton-icon {
  display: -moz-box;
}

toolbarpaletteitem[place="palette"] > #downloads-button[indicator] > #downloads-indicator-anchor {
  display: none;
}

/* Combobox dropdown renderer */
#ContentSelectDropdown > menupopup {
  /* The menupopup itself should always be rendered LTR to ensure the scrollbar aligns with
   * the dropdown arrow on the dropdown widget. If a menuitem is RTL, its style will be set accordingly */
  direction: ltr;
}

/* Indent options in optgroups */
.contentSelectDropdown-ingroup .menu-iconic-text {
  padding-inline-start: 2em;
}

/* Give this menupopup an arrow panel styling */
#BMB_bookmarksPopup {
  -moz-appearance: none;
  -moz-binding: url("chrome://browser/content/places/menu.xml#places-popup-arrow");
  background: transparent;
  border: none;
  /* The popup inherits -moz-image-region from the button, must reset it */
  -moz-image-region: auto;
}


/* Apply crisp rendering for favicons at exactly 2dppx resolution */
@media (resolution: 2dppx) {
  #PanelUI-remotetabs-tabslist > toolbarbutton > .toolbarbutton-icon,
  #PanelUI-recentlyClosedWindows > toolbarbutton > .toolbarbutton-icon,
  #PanelUI-recentlyClosedTabs > toolbarbutton > .toolbarbutton-icon,
  #PanelUI-historyItems > toolbarbutton > .toolbarbutton-icon {
    image-rendering: -moz-crisp-edges;
  }
}

#customization-container {
  -moz-box-orient: horizontal;
  flex-direction: column;
}

#customization-container:not([hidden]) {
  /* In a separate rule to avoid 'display:flex' causing the node to be
   * displayed while the container is still hidden. */
  display: flex;
}

#customization-content-container {
  display: flex;
  flex-grow: 1; /* Grow so there isn't empty space below the footer */
  min-height: 0; /* Allow this to shrink so the footer doesn't get pushed out. */
}

#customization-panelHolder {
  padding-top: 10px;
  padding-bottom: 10px;
}

#customization-panelHolder > #widget-overflow-fixed-list {
  flex: 1 1 auto; /* Grow within the available space, and allow ourselves to shrink */
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  overflow-x: hidden;
}

#customization-panelWrapper,
#customization-panelWrapper > .panel-arrowcontent,
#customization-panelHolder {
  flex-direction: column;
  display: flex;
  min-height: calc(174px + 8em);
}

#customization-panelWrapper {
  flex: 1 1 auto;
  height: 0; /* Don't let my contents affect ancestors' content-based sizing */
  align-items: end; /* align to the end on the cross-axis (affects arrow) */
}

#customization-panelWrapper,
#customization-panelWrapper > .panel-arrowcontent {
  -moz-box-flex: 1;
}

#customization-panel-container {
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  flex: none;
}

toolbarpaletteitem[dragover] {
  border-left-color: transparent;
  border-right-color: transparent;
}

#customization-palette-container {
  display: flex;
  flex-direction: column;
  flex-grow: 1;
}

#customization-palette:not([hidden]) {
  display: block;
  flex: 1 1 auto;
  overflow: auto;
  min-height: 3em;
}

#customization-footer-spacer,
#customization-spacer {
  flex: 1 1 auto;
}

#customization-footer {
  display: flex;
  flex-shrink: 0;
  flex-wrap: wrap;
}

#customization-toolbar-visibility-button > .box-inherit > .button-menu-dropmarker {
  display: -moz-box;
}

toolbarpaletteitem[place="palette"] {
  -moz-binding: url("chrome://global/content/bindings/toolbar.xml#toolbarpaletteitem-palette-wrapping-label");
  width: 7em;
  /* icon (16) + margin (9 + 12) + 3 lines of text: */
  height: calc(39px + 3em);
  margin-bottom: 5px;
  margin-inline-end: 24px;
  overflow: visible;
  display: inline-block;
  vertical-align: top;
}

toolbarpaletteitem[place="palette"][hidden] {
  display: none;
}

#customization-palette .toolbarpaletteitem-box {
  -moz-box-pack: center;
  width: 7em;
  max-width: 7em;
}

#main-window[customizing=true] .addon-banner-item,
#main-window[customizing=true] .panel-banner-item {
  display: none;
}

/* UI Tour */

@keyframes uitour-wobble {
  from {
    transform: rotate(0deg) translateX(3px) rotate(0deg);
  }
  50% {
    transform: rotate(360deg) translateX(3px) rotate(-360deg);
  }
  to {
    transform: rotate(720deg) translateX(0px) rotate(-720deg);
  }
}

@keyframes uitour-zoom {
  from {
    transform: scale(0.8);
  }
  50% {
    transform: scale(1.0);
  }
  to {
    transform: scale(0.8);
  }
}

@keyframes uitour-color {
  from {
    border-color: #5B9CD9;
  }
  50% {
    border-color: #FF0000;
  }
  to {
    border-color: #5B9CD9;
  }
}

#UITourHighlightContainer,
#UITourHighlight {
  pointer-events: none;
}

#UITourHighlight[active] {
  animation-delay: 2s;
  animation-fill-mode: forwards;
  animation-iteration-count: infinite;
  animation-timing-function: linear;
}

#UITourHighlight[active="wobble"] {
  animation-name: uitour-wobble;
  animation-delay: 0s;
  animation-duration: 1.5s;
  animation-iteration-count: 1;
}
#UITourHighlight[active="zoom"] {
  animation-name: uitour-zoom;
  animation-duration: 1s;
}
#UITourHighlight[active="color"] {
  animation-name: uitour-color;
  animation-duration: 2s;
}

/* Combined context-menu items */
#context-navigation > .menuitem-iconic > .menu-iconic-text,
#context-navigation > .menuitem-iconic > .menu-accel-container {
  display: none;
}

.popup-notification-invalid-input {
  box-shadow: 0 0 1.5px 1px red;
}

.popup-notification-invalid-input[focused] {
  box-shadow: 0 0 2px 2px rgba(255,0,0,0.4);
}

.popup-notification-description[popupid=webauthn-prompt-register-direct] {
  white-space: pre-line;
}

.dragfeedback-tab {
  -moz-appearance: none;
  opacity: 0.65;
  -moz-window-shadow: none;
}

/* Page action panel */
#pageAction-panel-sendToDevice-subview-body:not([state="notready"]) > #pageAction-panel-sendToDevice-notReady,
#pageAction-urlbar-sendToDevice-subview-body:not([state="notready"]) > #pageAction-urlbar-sendToDevice-notReady {
  display: none;
}

/* Page action buttons */
.pageAction-panel-button > .toolbarbutton-icon {
  list-style-image: var(--pageAction-image-16px, inherit);
}
.urlbar-page-action {
  list-style-image: var(--pageAction-image-16px, inherit);
}
@media (min-resolution: 1.1dppx) {
  .pageAction-panel-button > .toolbarbutton-icon {
    list-style-image: var(--pageAction-image-32px, inherit);
  }
  .urlbar-page-action {
    list-style-image: var(--pageAction-image-32px, inherit);
  }
}

/* Page action context menu */
#pageActionContextMenu > .pageActionContextMenuItem {
  visibility: collapse;
}
#pageActionContextMenu[state=builtInPinned] > .pageActionContextMenuItem.builtInPinned,
#pageActionContextMenu[state=builtInUnpinned] > .pageActionContextMenuItem.builtInUnpinned,
#pageActionContextMenu[state=extensionPinned] > .pageActionContextMenuItem.extensionPinned,
#pageActionContextMenu[state=extensionUnpinned] > .pageActionContextMenuItem.extensionUnpinned {
  visibility: visible;
}


/* Favicon in Open Bookmark in Sidebar */
#sidebar-box[sidebarcommand="viewWebPanelsSidebar"] > #sidebar-header > #sidebar-switcher-target > #sidebar-icon {
  list-style-image: var(--sidebar-webpage-icon, url(chrome://mozapps/skin/places/defaultFavicon.svg) );
  -moz-context-properties: fill;
  fill: currentColor;
  width: 16px;
  height: 16px;
}

/* WebExtension Sidebars */
#sidebar-box[sidebarcommand$="-sidebar-action"] > #sidebar-header > #sidebar-switcher-target > #sidebar-icon {
  list-style-image: var(--webextension-menuitem-image, inherit);
  -moz-context-properties: fill;
  fill: currentColor;
  width: 16px;
  height: 16px;
}

@media (min-resolution: 1.1dppx) {
  #sidebar-box[sidebarcommand$="-sidebar-action"] > #sidebar-header > #sidebar-switcher-target > #sidebar-icon {
    list-style-image: var(--webextension-menuitem-image-2x, inherit);
  }
}


:root[lwthemeicons~="--back-icon"] #back-button:-moz-lwtheme {
  list-style-image: var(--back-icon) !important;
}

:root[lwthemeicons~="--forward-icon"] #forward-button:-moz-lwtheme {
  list-style-image: var(--forward-icon) !important;
}

:root[lwthemeicons~="--reload-icon"] #reload-button:-moz-lwtheme {
  list-style-image: var(--reload-icon) !important;
}

:root[lwthemeicons~="--stop-icon"] #stop-button:-moz-lwtheme {
  list-style-image: var(--stop-icon) !important;
}

:root[lwthemeicons~="--bookmark_star-icon"] #star-button:-moz-lwtheme,
:root[lwthemeicons~="--bookmark_star-icon"] #bookmarks-menu-button:-moz-lwtheme {
  list-style-image: var(--bookmark_star-icon) !important;
}

:root[lwthemeicons~="--bookmark_menu-icon"] #bookmarks-menu-button:-moz-lwtheme {
  list-style-image: var(--bookmark_menu-icon) !important;
}

:root[lwthemeicons~="--downloads-icon"] #downloads-button:-moz-lwtheme {
  list-style-image: var(--downloads-icon) !important;
}

:root[lwthemeicons~="--home-icon"] #home-button:-moz-lwtheme {
  list-style-image: var(--home-icon) !important;
}

:root[lwthemeicons~="--app_menu-icon"] #PanelUI-menu-button:-moz-lwtheme {
  list-style-image: var(--app_menu-icon) !important;
}

:root[lwthemeicons~="--cut-icon"] #cut-button:-moz-lwtheme {
  list-style-image: var(--cut-icon) !important;
}

:root[lwthemeicons~="--copy-icon"] #copy-button:-moz-lwtheme {
  list-style-image: var(--copy-icon) !important;
}

:root[lwthemeicons~="--paste-icon"] #paste-button:-moz-lwtheme {
  list-style-image: var(--paste-icon) !important;
}

:root[lwthemeicons~="--new_window-icon"] #new-window-button:-moz-lwtheme {
  list-style-image: var(--new_window-icon) !important;
}

:root[lwthemeicons~="--new_private_window-icon"] #privatebrowsing-button:-moz-lwtheme {
  list-style-image: var(--new_private_window-icon) !important;
}

:root[lwthemeicons~="--save_page-icon"] #save-page-button:-moz-lwtheme {
  list-style-image: var(--save_page-icon) !important;
}

:root[lwthemeicons~="--print-icon"] #print-button:-moz-lwtheme {
  list-style-image: var(--print-icon) !important;
}

:root[lwthemeicons~="--history-icon"] #history-panelmenu:-moz-lwtheme {
  list-style-image: var(--history-icon) !important;
}

:root[lwthemeicons~="--full_screen-icon"] #fullscreen-button:-moz-lwtheme {
  list-style-image: var(--full_screen-icon) !important;
}

:root[lwthemeicons~="--find-icon"] #find-button:-moz-lwtheme {
  list-style-image: var(--find-icon) !important;
}

:root[lwthemeicons~="--options-icon"] #preferences-button:-moz-lwtheme {
  list-style-image: var(--options-icon) !important;
}

:root[lwthemeicons~="--addons-icon"] #add-ons-button:-moz-lwtheme {
  list-style-image: var(--addons-icon) !important;
}

:root[lwthemeicons~="--developer-icon"] #developer-button:-moz-lwtheme {
  list-style-image: var(--developer-icon) !important;
}

:root[lwthemeicons~="--synced_tabs-icon"] #sync-button:-moz-lwtheme {
  list-style-image: var(--synced_tabs-icon) !important;
}

:root[lwthemeicons~="--open_file-icon"] #open-file-button:-moz-lwtheme {
  list-style-image: var(--open_file-icon) !important;
}

:root[lwthemeicons~="--sidebars-icon"] #sidebar-button:-moz-lwtheme {
  list-style-image: var(--sidebars-icon) !important;
}

:root[lwthemeicons~="--subscribe-icon"] #feed-button:-moz-lwtheme {
  list-style-image: var(--subscribe-icon) !important;
}

:root[lwthemeicons~="--text_encoding-icon"] #characterencoding-button:-moz-lwtheme {
  list-style-image: var(--text_encoding-icon) !important;
}

:root[lwthemeicons~="--email_link-icon"] #email-link-button:-moz-lwtheme {
  list-style-image: var(--email_link-icon) !important;
}

:root[lwthemeicons~="--forget-icon"] #panic-button:-moz-lwtheme {
  list-style-image: var(--forget-icon) !important;
}

:root[lwthemeicons~="--bookmark_star-icon"] #star-button:-moz-lwtheme,
:root[lwthemeicons~="--bookmark_menu-icon"] #bookmarks-menu-button:-moz-lwtheme,
:root[lwthemeicons~="--back-icon"] #back-button:-moz-lwtheme,
:root[lwthemeicons~="--forward-icon"] #forward-button:-moz-lwtheme,
:root[lwthemeicons~="--reload-icon"] #reload-button:-moz-lwtheme,
:root[lwthemeicons~="--stop-icon"] #stop-button:-moz-lwtheme,
:root[lwthemeicons~="--bookmark_star-icon"] #bookmarks-menu-button:-moz-lwtheme,
:root[lwthemeicons~="--downloads-icon"] #downloads-button:-moz-lwtheme,
:root[lwthemeicons~="--home-icon"] #home-button:-moz-lwtheme,
:root[lwthemeicons~="--app_menu-icon"] #PanelUI-menu-button:-moz-lwtheme,
:root[lwthemeicons~="--cut-icon"] #cut-button:-moz-lwtheme,
:root[lwthemeicons~="--copy-icon"] #copy-button:-moz-lwtheme,
:root[lwthemeicons~="--paste-icon"] #paste-button:-moz-lwtheme,
:root[lwthemeicons~="--new_window-icon"] #new-window-button:-moz-lwtheme,
:root[lwthemeicons~="--new_private_window-icon"] #privatebrowsing-button:-moz-lwtheme,
:root[lwthemeicons~="--save_page-icon"] #save-page-button:-moz-lwtheme,
:root[lwthemeicons~="--print-icon"] #print-button:-moz-lwtheme,
:root[lwthemeicons~="--history-icon"] #history-panelmenu:-moz-lwtheme,
:root[lwthemeicons~="--full_screen-icon"] #fullscreen-button:-moz-lwtheme,
:root[lwthemeicons~="--find-icon"] #find-button:-moz-lwtheme,
:root[lwthemeicons~="--options-icon"] #preferences-button:-moz-lwtheme,
:root[lwthemeicons~="--addons-icon"] #add-ons-button:-moz-lwtheme,
:root[lwthemeicons~="--developer-icon"] #developer-button:-moz-lwtheme,
:root[lwthemeicons~="--synced_tabs-icon"] #sync-button:-moz-lwtheme,
:root[lwthemeicons~="--open_file-icon"] #open-file-button:-moz-lwtheme,
:root[lwthemeicons~="--sidebars-icon"] #sidebar-button:-moz-lwtheme,
:root[lwthemeicons~="--subscribe-icon"] #feed-button:-moz-lwtheme,
:root[lwthemeicons~="--text_encoding-icon"] #characterencoding-button:-moz-lwtheme,
:root[lwthemeicons~="--email_link-icon"] #email-link-button:-moz-lwtheme,
:root[lwthemeicons~="--forget-icon"] #panic-button:-moz-lwtheme {
  -moz-image-region: rect(0, 16px, 16px, 0) !important;
}
PK
!<�sQp�+�+6chrome/browser/content/browser/downloads/downloads.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

/*** Downloads Panel ***/

richlistitem[type="download"] {
  -moz-binding: url('chrome://browser/content/downloads/download.xml#download');
}

richlistitem[type="download"]:not([selected]) button {
  /* Only focus buttons in the selected item. */
  -moz-user-focus: none;
}

richlistitem[type="download"].download-state[state="1"]:not([exists]) > .downloadButtonArea,
richlistitem[type="download"].download-state[state="1"]:not([exists]) > toolbarseparator {
  display: none;
}

#downloadsSummary:not([inprogress]) > vbox > #downloadsSummaryProgress,
#downloadsSummary:not([inprogress]) > vbox > #downloadsSummaryDetails,
#downloadsFooter:not([showingsummary]) #downloadsSummary {
  display: none;
}

#downloadsFooter[showingsummary] > stack:hover > #downloadsSummary,
#downloadsFooter[showingsummary] > stack:not(:hover) > #downloadsFooterButtons {
  /* If we used "visibility: hidden;" then the mouseenter event of
     #downloadsHistory wouldn't be triggered immediately, and the hover styling
     of the button would not apply until the mouse is moved again.

     "-moz-user-focus: ignore;" prevents the elements with "opacity: 0;" from
     being focused with the keyboard. */
  opacity: 0;
  -moz-user-focus: ignore;
}

/*** Downloads View ***/

/**
 * The downloads richlistbox may list thousands of items, and it turns out
 * XBL binding attachment, and even more so detachment, is a performance hog.
 * This hack makes sure we don't apply any binding to inactive items (inactive
 * items are history downloads that haven't been in the visible area).
 * We can do this because the richlistbox implementation does not interact
 * much with the richlistitem binding.  However, this may turn out to have
 * some side effects (see bug 828111 for the details).
 *
 * We might be able to do away with this workaround once bug 653881 is fixed.
 */
richlistitem.download {
  -moz-binding: none;
}

richlistitem.download[active] {
  -moz-binding: url("chrome://browser/content/downloads/download.xml#download");
}

richlistitem.download button {
  /* These buttons should never get focus, as that would "disable"
     the downloads view controller (it's only used when the richlistbox
     is focused). */
  -moz-user-focus: none;
}

/*** Visibility of controls inside download items ***/
.download-state:not(:-moz-any([state="6"], /* Blocked (parental) */
                              [state="8"], /* Blocked (dirty)    */
                              [state="9"]) /* Blocked (policy)   */)
                                           .downloadBlockedBadge,

.download-state:not(:-moz-any([state="-1"],/* Starting (initial) */
                              [state="5"], /* Starting (queued)  */
                              [state="0"], /* Downloading        */
                              [state="4"], /* Paused             */
                              [state="7"]) /* Scanning           */)
                                           .downloadProgress,

.download-state:not(          [state="0"]  /* Downloading        */)
                                           .downloadPauseMenuItem,

.download-state:not(          [state="4"]  /* Paused             */)
                                           .downloadResumeMenuItem,

/* Blocked (dirty) downloads that have not been confirmed and
   have temporary data. */
.download-state:not(          [state="8"]  /* Blocked (dirty)    */)
                                           .downloadUnblockMenuItem,
.download-state[state="8"]:not(.temporary-block)
                                           .downloadUnblockMenuItem,

.download-state:not(:-moz-any([state="1"], /* Finished           */
                              [state="2"], /* Failed             */
                              [state="3"], /* Canceled           */
                              [state="6"], /* Blocked (parental) */
                              [state="8"], /* Blocked (dirty)    */
                              [state="9"]) /* Blocked (policy)   */)
                                           .downloadRemoveFromHistoryMenuItem,

.download-state:not(:-moz-any([state="-1"],/* Starting (initial) */
                              [state="0"], /* Downloading        */
                              [state="1"], /* Finished           */
                              [state="4"], /* Paused             */
                              [state="5"]) /* Starting (queued)  */)
                                           .downloadShowMenuItem,
.download-state[state="1"]:not([exists])
                                           .downloadShowMenuItem,

.download-state:not(:-moz-any([state="-1"],/* Starting (initial) */
                              [state="0"], /* Downloading        */
                              [state="1"], /* Finished           */
                              [state="4"], /* Paused             */
                              [state="5"], /* Starting (queued)  */
                              [state="8"]) /* Blocked (dirty)    */)
                                           .downloadCommandsSeparator,
.download-state[state="1"]:not([exists])
                                           .downloadCommandsSeparator,
.download-state[state="8"]:not(.temporary-block)
                                           .downloadCommandsSeparator

{
  display: none;
}

/*** Visibility of download buttons ***/

.download-state:not(:-moz-any([state="-1"],/* Starting (initial) */
                              [state="5"], /* Starting (queued)  */
                              [state="0"], /* Downloading        */
                              [state="4"]) /* Paused             */)
                                           .downloadCancel,

/* Blocked (dirty) downloads that have not been confirmed and
   have temporary data, for the Malware case. */
.download-state:not(          [state="8"]  /* Blocked (dirty)    */)
                                           .downloadConfirmBlock,
.download-state[state="8"]:not(.temporary-block)
                                           .downloadConfirmBlock,
.download-state[state="8"].temporary-block:not([verdict="Malware"])
                                           .downloadConfirmBlock,

/* Blocked (dirty) downloads that have not been confirmed and
   have temporary data, for the Potentially Unwanted case. */
.download-state:not(          [state="8"]  /* Blocked (dirty)    */)
                                           .downloadChooseUnblock,
.download-state[state="8"]:not(.temporary-block)
                                           .downloadChooseUnblock,
.download-state[state="8"].temporary-block:not([verdict="PotentiallyUnwanted"])
                                           .downloadChooseUnblock,

/* Blocked (dirty) downloads that have not been confirmed and
   have temporary data, for the Uncommon case. */
.download-state:not(          [state="8"]  /* Blocked (dirty)    */)
                                           .downloadChooseOpen,
.download-state[state="8"]:not(.temporary-block)
                                           .downloadChooseOpen,
.download-state[state="8"].temporary-block:not([verdict="Uncommon"])
                                           .downloadChooseOpen,

.download-state:not(:-moz-any([state="2"], /* Failed             */
                              [state="3"]) /* Canceled           */)
                                           .downloadRetry,

.download-state:not(          [state="1"]  /* Finished           */)
                                           .downloadShow,

.download-state:-moz-any(     [state="6"], /* Blocked (parental) */
                              [state="7"], /* Scanning           */
                              [state="9"]) /* Blocked (policy)   */
                                           > toolbarseparator,

/* The "show blocked info" button is shown only in the downloads panel. */
.downloadShowBlockedInfo
{
  display: none;
}

/*** Downloads panel ***/

#downloadsPanel[hasdownloads] #emptyDownloads,
#downloadsPanel:not([hasdownloads]) #downloadsListBox {
  display: none;
}

/*** Downloads panel multiview (main view and blocked-downloads subview) ***/

/* Hide all the usual buttons. */
#downloadsPanel-mainView .download-state[state="8"] .downloadCancel,
#downloadsPanel-mainView .download-state[state="8"] .downloadConfirmBlock,
#downloadsPanel-mainView .download-state[state="8"] .downloadChooseUnblock,
#downloadsPanel-mainView .download-state[state="8"] .downloadChooseOpen,
#downloadsPanel-mainView .download-state[state="8"] .downloadRetry,
#downloadsPanel-mainView .download-state[state="8"] .downloadShow {
  display: none;
}

/* Make the panel wide enough to show the download list items without improperly
   truncating them. */
#downloadsPanel-multiView > .panel-viewcontainer,
#downloadsPanel-multiView > .panel-viewcontainer > .panel-viewstack {
  max-width: unset;
}

/* Show the "show blocked info" button. */
#downloadsPanel-mainView .download-state[state="8"] .downloadShowBlockedInfo {
  display: inline;
}

/* DownloadsSubview styles: */

.subviewbutton.download {
  -moz-binding: url("chrome://browser/content/downloads/download.xml#download-subview-toolbarbutton");
}

/* Hide all status labels by default and selectively display one at a time,
   depending on the state of the Download. */
.subviewbutton.download > .toolbarbutton-text > .status-text,
/* When a Download is not hovered at all, hide the secondary action button. */
.subviewbutton.download:not(:hover) > .action-button,
/* Always hide the label of the secondary action button. */
.subviewbutton.download > .action-button > .toolbarbutton-text {
  display: none;
}

/* When a Download is _not_ hovered, display the full status message. */
.subviewbutton.download:not(:hover) > .toolbarbutton-text > .status-full,
/* When a Download is hovered when the file doesn't exist and cannot be retried,
   keep showing the full status message. */
.subviewbutton.download:hover:-moz-any(:not([openLabel]),:not([exists])):not([retryLabel]) > .toolbarbutton-text > .status-full,
/* When a Download is hovered and the it can be retried, but the action button
   is _not_ hovered, keep showing the full status message. */
.subviewbutton.download:hover[retryLabel]:not([buttonover]) > .toolbarbutton-text > .status-full,
/* When a Download is hovered and the file can be opened, but the action button
   is _not_ hovered, show the 'Open File' status label. */
.subviewbutton.download:hover[openLabel][exists]:not([buttonover]) > .toolbarbutton-text > .status-open,
/* When a Download is hovered - its action button explicitly - and it can be
   retried, show the 'Retry Download' label. */
.subviewbutton.download:hover[retryLabel][buttonover] > .toolbarbutton-text > .status-retry,
/* When a Download is hovered - its action button explicitly - and the file can
   be shown in the OS's shell, show the 'Open Containing Folder' label. */
.subviewbutton.download:hover[openLabel][exists][buttonover] > .toolbarbutton-text > .status-show {
  display: inline;
}
PK
!<��:i

0chrome/browser/content/browser/places/places.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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[type="places"] {
  -moz-binding: url("chrome://browser/content/places/tree.xml#places-tree");
}

tree[type="places"] > treechildren::-moz-tree-cell {
  /* ensure we use the direction of the website title / url instead of the
   * browser locale */
  unicode-bidi: plaintext;
}

#bhtTitleText {
  /* ensure we use the direction of the website title instead of the
   * browser locale */
  unicode-bidi: plaintext;
}

.toolbar-drop-indicator {
  position: relative;
  z-index: 1;
}

menupopup[placespopup="true"] {
  -moz-binding: url("chrome://browser/content/places/menu.xml#places-popup-base");
}

/* Apply crisp rendering for favicons at exactly 2dppx resolution */
@media (resolution: 2dppx) {
  #bookmarksChildren,
  .sidebar-placesTreechildren,
  .placesTree > treechildren {
    image-rendering: -moz-crisp-edges;
  }
}
PK
!<���[AA:chrome/browser/content/browser/usercontext/usercontext.css[data-identity-color="blue"] {
  --identity-tab-color: #37adff;
  --identity-icon-color: #37adff;
}

[data-identity-color="turquoise"] {
  --identity-tab-color: #00c79a;
  --identity-icon-color: #00c79a;
}

[data-identity-color="green"] {
  --identity-tab-color: #51cd00;
  --identity-icon-color: #51cd00;
}

[data-identity-color="yellow"] {
  --identity-tab-color: #ffcb00;
  --identity-icon-color: #ffcb00;
}

[data-identity-color="orange"] {
  --identity-tab-color: #ff9f00;
  --identity-icon-color: #ff9f00;
}

[data-identity-color="red"] {
  --identity-tab-color: #ff613d;
  --identity-icon-color: #ff613d;
}

[data-identity-color="pink"] {
  --identity-tab-color: #ff4bda;
  --identity-icon-color: #ff4bda;
}

[data-identity-color="purple"] {
  --identity-tab-color: #af51f5;
  --identity-icon-color: #af51f5;
}

[data-identity-icon="fingerprint"] {
  --identity-icon: url("resource://usercontext-content/fingerprint.svg");
}

[data-identity-icon="briefcase"] {
  --identity-icon: url("resource://usercontext-content/briefcase.svg");
}

[data-identity-icon="dollar"] {
  --identity-icon: url("resource://usercontext-content/dollar.svg");
}

[data-identity-icon="cart"] {
  --identity-icon: url("resource://usercontext-content/cart.svg");
}

[data-identity-icon="circle"] {
  --identity-icon: url("resource://usercontext-content/circle.svg");
}

[data-identity-icon="vacation"] {
  --identity-icon: url("resource://usercontext-content/vacation.svg");
}

[data-identity-icon="gift"] {
  --identity-icon: url("resource://usercontext-content/gift.svg");
}

[data-identity-icon="food"] {
  --identity-icon: url("resource://usercontext-content/food.svg");
}

[data-identity-icon="fruit"] {
  --identity-icon: url("resource://usercontext-content/fruit.svg");
}

[data-identity-icon="pet"] {
  --identity-icon: url("resource://usercontext-content/pet.svg");
}

[data-identity-icon="tree"] {
  --identity-icon: url("resource://usercontext-content/tree.svg");
}

[data-identity-icon="chill"] {
  --identity-icon: url("resource://usercontext-content/chill.svg");
}

#userContext-indicator {
  height: 16px;
  width: 16px;
}

#userContext-label {
  margin-inline-start: 0;
  margin-inline-end: 3px;
  color: var(--identity-tab-color);
}

#userContext-icons {
  -moz-box-align: center;
}

.tabbrowser-tab[usercontextid] > .tab-stack > .tab-background > .tab-bottom-line {
  background: linear-gradient(to right, transparent 10%, var(--identity-tab-color) 10%, var(--identity-tab-color) 90%, transparent 90%);
  height: 2px;
}

.tabbrowser-tab[pinned][usercontextid] > .tab-stack > .tab-background > .tab-bottom-line {
  background: linear-gradient(to right, transparent 30%, var(--identity-tab-color) 30%, var(--identity-tab-color) 70%, transparent 70%);
}

.userContext-icon,
.menuitem-iconic[data-usercontextid] > .menu-iconic-left > .menu-iconic-icon,
.subviewbutton[usercontextid] > .toolbarbutton-icon,
#userContext-indicator {
  background-image: var(--identity-icon);
  -moz-context-properties: fill;
  fill: var(--identity-icon-color);
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center center;
}
PK
!<�l��4�4;chrome/browser/skin/classic/browser/controlcenter/panel.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


/* Hide all conditional elements by default. */
:-moz-any([when-connection],[when-mixedcontent],[when-ciphers],[when-loginforms]) {
  display: none;
}

/* This is used by screenshots tests to hide intermittently different
 * identity popup shadows (see bug 1425253). */
#identity-popup.no-shadow {
  -moz-window-shadow: none;
}

/* Show the right elements for the right connection states. */
#identity-popup[connection=not-secure] [when-connection~=not-secure],
#identity-popup[connection=secure-cert-user-overridden] [when-connection~=secure-cert-user-overridden],
#identity-popup[connection=secure-ev] [when-connection~=secure-ev],
#identity-popup[connection=secure] [when-connection~=secure],
#identity-popup[connection=chrome] [when-connection~=chrome],
#identity-popup[connection=file] [when-connection~=file],
#identity-popup[connection=extension] [when-connection~=extension],
/* Show insecure login forms messages when needed. */
#identity-popup[loginforms=insecure] [when-loginforms=insecure],
/* Show weak cipher messages when needed. */
#identity-popup[ciphers=weak] [when-ciphers~=weak],
/* Show mixed content warnings when needed */
#identity-popup[mixedcontent~=active-loaded] [when-mixedcontent=active-loaded],
#identity-popup[mixedcontent~=passive-loaded]:not([mixedcontent~=active-loaded]) [when-mixedcontent=passive-loaded],
#identity-popup[mixedcontent~=active-blocked]:not([mixedcontent~=passive-loaded]) [when-mixedcontent=active-blocked],
/* Show the right elements when there is mixed passive content loaded and active blocked. */
#identity-popup[mixedcontent~=active-blocked][mixedcontent~=passive-loaded] [when-mixedcontent~=active-blocked][when-mixedcontent~=passive-loaded],
/* Show 'disable MCB' button always when there is mixed active content blocked. */
#identity-popup-securityView-body[mixedcontent~=active-blocked] > button[when-mixedcontent=active-blocked] {
  display: inherit;
}

/* Hide redundant messages based on insecure login forms presence. */
#identity-popup[loginforms=secure] [and-when-loginforms=insecure] {
  display: none;
}
#identity-popup[loginforms=insecure] [and-when-loginforms=secure] {
  display: none;
}

/* Hide 'not secure' message in subview when weak cipher or mixed content messages are shown. */
#identity-popup-securityView-body:-moz-any([mixedcontent],[ciphers]) > description[when-connection=not-secure],
/* Hide 'passive-loaded (only)' message when there is mixed passive content loaded and active blocked. */
#identity-popup-securityView-body[mixedcontent~=passive-loaded][mixedcontent~=active-blocked] > description[when-mixedcontent=passive-loaded] {
  display: none;
}

/* Make sure hidden elements don't accidentally become visible from one of the
   above selectors (see Bug 1194258) */
#identity-popup [hidden] {
  display: none !important;
}

#identity-popup > .panel-arrowcontainer > .panel-arrowcontent {
  padding: 0;
  /* Set default fill for icons in the identity popup.
     Individual icons can override this. */
  fill: currentColor;
  fill-opacity: .6;
}

#identity-popup-mainView {
  min-width: 30em;
  max-width: 30em;
}

.identity-popup-section:not(:first-child) {
  border-top: 1px solid var(--panel-separator-color);
}

.identity-popup-security-content,
#identity-popup-permissions-content,
#tracking-protection-content {
  background-repeat: no-repeat;
  background-position: 1em 1em;
  background-size: 24px auto;
  -moz-context-properties: fill, fill-opacity;
  fill: currentColor;
  fill-opacity: .6;
  padding: 0.5em 0 1em;
  /* .identity-popup-host depends on this width */
  padding-inline-start: calc(2em + 24px);
  padding-inline-end: 1em;
}

.identity-popup-security-content:-moz-locale-dir(rtl),
#identity-popup-permissions-content:-moz-locale-dir(rtl),
#tracking-protection-content:-moz-locale-dir(rtl) {
  background-position: calc(100% - 1em) 1em;
}

/* EXPAND BUTTON */

.identity-popup-expander {
  margin: 0;
  padding: 4px 0;
  min-width: auto;
  width: 38px;
  border-style: none;
  -moz-appearance: none;
  background: url("chrome://browser/skin/arrow-left.svg") center no-repeat;
  background-size: 16px, auto;
  -moz-context-properties: fill;
  fill: currentColor;
  color: inherit;
}

.identity-popup-expander:-moz-locale-dir(ltr) {
  transform: scaleX(-1);
}

.identity-popup-expander > .button-box {
  border-right: 1px solid var(--panel-separator-color);
  padding: 0;
}

.identity-popup-expander:hover {
  background-color: var(--arrowpanel-dimmed);
}

.identity-popup-expander:hover:active {
  background-color: var(--arrowpanel-dimmed-further);
  box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset;
}

/* CONTENT */

.identity-popup-permission-label,
.identity-popup-permission-state-label,
.identity-popup-security-content > description,
#identity-popup-security-descriptions > description,
#identity-popup-securityView-body > description,
#identity-popup-permissions-content > description,
#tracking-protection-content > description {
  font-size: 110%;
  margin: 0;
}

/* This element needs the pre-wrap because we add newlines to it in the code. */
#identity-popup-content-supplemental {
  white-space: pre-wrap;
}

.identity-popup-headline {
  margin: 3px 0 4px;
  font-size: 150%;
}

.identity-popup-host {
  word-wrap: break-word;
  /* 1em + 2em + 24px is .identity-popup-security-content padding
   * 30em is the panel width */
  max-width: calc(30rem - 3rem - 24px - 38px);
}

.identity-popup-warning-gray {
  padding-inline-start: 24px;
  background: url(chrome://browser/skin/controlcenter/warning-gray.svg) no-repeat 0 50%;
}

.identity-popup-warning-yellow {
  padding-inline-start: 24px;
  background: url(chrome://browser/skin/controlcenter/warning-yellow.svg) no-repeat 0 50%;
}

.identity-popup-warning-gray:-moz-locale-dir(rtl),
.identity-popup-warning-yellow:-moz-locale-dir(rtl) {
  background-position: 100% 50%;
}
/* SECURITY */
.identity-popup-connection-secure {
  color: #058B00;
}
.identity-popup-connection-not-secure {
  color: #d74345;
}

.identity-popup-security-content {
  background-image: url(chrome://browser/skin/controlcenter/conn-not-secure.svg);
}

#identity-popup[connection=chrome] .identity-popup-security-content {
  background-image: url(chrome://branding/content/icon48.png);
}
#identity-popup[connection^=secure] .identity-popup-security-content {
  background-image: url(chrome://browser/skin/controlcenter/connection.svg);
  -moz-context-properties: fill;
  fill: #12BC00;
}
/* Use [isbroken] to make sure we don't show a lock on an http page. See Bug 1192162. */
#identity-popup[ciphers=weak] .identity-popup-security-content,
#identity-popup[mixedcontent~=passive-loaded][isbroken] .identity-popup-security-content {
  background-image: url(chrome://browser/skin/controlcenter/connection.svg);
  -moz-context-properties: fill, fill-opacity;
}

#identity-popup[connection=secure-cert-user-overridden] .identity-popup-security-content {
  background-image: url(chrome://browser/skin/connection-mixed-passive-loaded.svg);
  fill: unset;
  -moz-context-properties: fill, fill-opacity;
}

#identity-popup[loginforms=insecure] .identity-popup-security-content,
#identity-popup[mixedcontent~=active-loaded][isbroken] .identity-popup-security-content {
  background-image: url(chrome://browser/skin/controlcenter/mcb-disabled.svg);
  -moz-context-properties: fill, fill-opacity;
}

#identity-popup[connection=extension] .identity-popup-security-content {
  background-image: url(chrome://browser/skin/controlcenter/extension.svg);
  -moz-context-properties: fill;
  fill: #60bf4c;
}

#identity-popup-security-descriptions > description {
  margin-top: 6px;
  color: Graytext;
}

#identity-popup-securityView-body {
  margin-inline-start: calc(2em + 24px);
  margin-inline-end: 1em;
  border-top: 1px solid var(--panel-separator-color);
  padding-inline-end: 1em;
}

#identity-popup-securityView-footer {
  margin-top: 1em;
  background-color: var(--arrowpanel-dimmed);
}

#identity-popup-securityView-footer > button {
  -moz-appearance: none;
  margin: 0;
  border: none;
  border-top: 1px solid var(--panel-separator-color);
  padding: 8px 20px;
  color: inherit;
  background-color: transparent;
}

#identity-popup-securityView-footer > button:hover,
#identity-popup-securityView-footer > button:focus {
  background-color: var(--arrowpanel-dimmed);
}

#identity-popup-securityView-footer > button:hover:active {
  background-color: var(--arrowpanel-dimmed-further);
}

#identity-popup-content-verifier ~ description {
  margin-top: 1em;
  color: Graytext;
}

description#identity-popup-content-verified-by,
description#identity-popup-content-owner,
description#identity-popup-content-verifier,
#identity-popup-securityView-body > button {
  margin-top: 1em;
}

#identity-popup-securityView-body > button {
  margin-inline-start: 0;
  margin-inline-end: 0;
}

/* TRACKING PROTECTION */

#tracking-protection-content {
  background-image: url("chrome://browser/skin/controlcenter/tracking-protection.svg#enabled");
}

#tracking-protection-content[state="loaded-tracking-content"]  {
  background-image: url("chrome://browser/skin/controlcenter/tracking-protection.svg#disabled");
}

#tracking-action-block,
#tracking-action-unblock,
#tracking-action-unblock-private {
  margin: 1em 0 0;
}

#tracking-protection-content[state] > #tracking-not-detected,
#tracking-protection-content:not([state="blocked-tracking-content"]) > #tracking-blocked,
#main-window[privatebrowsingmode] #tracking-action-unblock,
#main-window:not([privatebrowsingmode]) #tracking-action-unblock-private,
#tracking-protection-content:not([state="blocked-tracking-content"]) #tracking-action-unblock,
#tracking-protection-content:not([state="blocked-tracking-content"]) #tracking-action-unblock-private,
#tracking-protection-content:not([state="loaded-tracking-content"]) > #tracking-loaded,
#tracking-protection-content:not([state="loaded-tracking-content"]) #tracking-action-block,
#tracking-protection-content:not([state]) > #tracking-actions {
  display: none;
}

/* PERMISSIONS */

#identity-popup-permissions-content {
  background-image: url(chrome://browser/skin/controlcenter/permissions.svg);
  padding-bottom: 1.5em;
}

@supports -moz-bool-pref("layout.css.emulate-moz-box-with-flex") {
  /* The extra padding-bottom is there to work around XUL flex (Bug 1368281).
     This rule and the 1.5em above can both be removed once we are only using CSS flex. */
  #identity-popup-permissions-content {
    padding-bottom: 1em;
  }
}

#identity-popup-permissions-headline {
  /* Make sure the label is as tall as the icon so that the permission list
     which is aligned with the icon doesn't cover it up. */
  min-height: 24px;
}

#identity-popup-permission-list {
  /* Offset the padding set on #identity-popup-permissions-content so that it
     shows up just below the section. The permission icons are 16px wide and
     should be right aligned with the section icon. */
  margin-inline-start: calc(-1em - 16px);
}

.identity-popup-permission-item {
  min-height: 24px;
}

#identity-popup-permission-list:not(:empty) {
  margin-top: 5px;
}

.identity-popup-permission-icon {
  width: 16px;
  height: 16px;
}

.identity-popup-permission-icon.in-use {
  -moz-context-properties: fill;
  fill: rgb(224, 41, 29);
  animation: 1.5s ease in-use-blink infinite;
}

@keyframes in-use-blink {
  50% { opacity: 0; }
}

.identity-popup-permission-label,
.identity-popup-permission-state-label {
  /* We need to align the action buttons and permission icons with the text.
     This is tricky because the icon height is defined in pixels, while the
     font height can vary with platform and system settings, and at least on
     Windows the default font metrics reserve more extra space for accents.
     This value is a good compromise for different platforms and font sizes. */
  margin-top: -0.1em;
}

.identity-popup-permission-label {
  margin-inline-start: 1em;
}

.identity-popup-permission-state-label {
  margin-inline-end: 5px;
  text-align: end;
  color: graytext;
}

.identity-popup-permission-remove-button {
  -moz-appearance: none;
  margin: 0;
  border-width: 0;
  border-radius: 50%;
  min-width: 0;
  padding: 2px;
  background-color: transparent;
  color: inherit;
  opacity: 0.6;
}

.identity-popup-permission-remove-button > .button-box {
  padding: 0;
}

.identity-popup-permission-remove-button > .button-box > .button-icon {
  margin: 0;
  width: 16px;
  height: 16px;
  list-style-image: url(chrome://browser/skin/panel-icon-cancel.svg);
  -moz-context-properties: fill;
  fill: currentColor;
}

.identity-popup-permission-remove-button > .button-box > .button-text {
  display: none;
}

/* swap foreground / background colors on hover */
.identity-popup-permission-remove-button:not(:-moz-focusring):hover {
  background-color: currentColor;
}

.identity-popup-permission-remove-button:not(:-moz-focusring):hover > .button-box > .button-icon {
  fill: var(--arrowpanel-background);
}

.identity-popup-permission-remove-button:not(:-moz-focusring):hover:active {
  opacity: 0.8;
}

.identity-popup-expander > .button-box,
.identity-popup-permission-remove-button > .button-box {
  -moz-appearance: none;
}

.identity-popup-expander:-moz-focusring,
.identity-popup-permission-remove-button:-moz-focusring {
  outline: 1px -moz-dialogtext dotted;
  outline-offset: -1px;
}
PK
!<�9M9J�J�>chrome/browser/skin/classic/browser/customizableui/panelUI.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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:not([uidensity=compact]):not([chromehidden~="toolbar"]) #PanelUI-button {
  margin-inline-start: 3px;
  border-inline-start: 1px solid;
  border-image: linear-gradient(
    transparent 4px,
    var(--lwt-toolbar-vertical-separator, rgba(0,0,0,.1)) 4px,
    var(--lwt-toolbar-vertical-separator, rgba(0,0,0,.1)) calc(100% - 4px),
    transparent calc(100% - 4px)
  );
  border-image-slice: 1;
}

:root:not([uidensity=compact]):not([chromehidden~="toolbar"]) #nav-bar[brighttext] > #PanelUI-button {
  border-image-source: linear-gradient(
    transparent 4px,
    var(--lwt-toolbar-vertical-separator, rgba(255,255,255,.2)) 4px,
    var(--lwt-toolbar-vertical-separator, rgba(255,255,255,.2)) calc(100% - 4px),
    transparent calc(100% - 4px)
  );
}

#PanelUI-menu-button[badge-status] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
  display: -moz-box;
  height: 10px;
  width: 10px;
  background-size: contain;
  border: none;
}

#PanelUI-menu-button[badge-status="extension-new-tab"] > .toolbarbutton-badge-stack > .toolbarbutton-badge,
#PanelUI-menu-button[badge-status="download-success"] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
  display: none;
}

#PanelUI-menu-button[badge-status="update-available"] > .toolbarbutton-badge-stack > .toolbarbutton-badge,
#PanelUI-menu-button[badge-status="update-manual"] > .toolbarbutton-badge-stack > .toolbarbutton-badge,
#PanelUI-menu-button[badge-status="update-restart"] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
  background: #74BF43 url(chrome://browser/skin/update-badge.svg) no-repeat center;
  border-radius: 50%;
  box-shadow: none;
  /* "!important" is necessary to override the rule in toolbarbutton.css */
  margin: -7px 0 0 !important;
  margin-inline-end: -4px !important;
  min-width: 12px;
  min-height: 12px;
}

.panel-banner-item[notificationid^=update]::after {
  background: #74BF43 url(chrome://browser/skin/update-badge.svg) no-repeat center;
  border-radius: 50%;
}

.panel-banner-item[notificationid^=update] {
  list-style-image: url(chrome://branding/content/icon16.png);
}

#PanelUI-menu-button[badge-status="fxa-needs-authentication"] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
  box-shadow: none;
  filter: drop-shadow(0 1px 0 hsla(206, 50%, 10%, .15));
}

#PanelUI-menu-button[badge-status="fxa-needs-authentication"] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
  height: 13px;
  background: transparent url(chrome://browser/skin/warning.svg) no-repeat center;
}

#PanelUI-menu-button[badge-status="fxa-needs-authentication"] > .toolbarbutton-badge-stack > .toolbarbutton-badge:-moz-window-inactive {
  filter: none;
}

#PanelUI-menu-button[badge-status="addon-alert"] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
  height: 13px;
  background: #FFBF00 url(chrome://browser/skin/update-badge-failed.svg) no-repeat center;
}

panelview {
  -moz-box-orient: vertical;
  -moz-box-flex: 1;
  background: var(--arrowpanel-background);
  padding: 0;
}

/* This section is to anchor all the drop down panels at the same height, shift the
  panel`s top margin according to its positioning and the uidensity of the window. */
#downloadsPanel,
#widget-overflow,
#appMenu-popup,
#customizationui-widget-panel {
  margin-top: -6px;
}

/* The difference between the toolbar buttons` padding and the urlbar-icons` padding,
   then subtract 6px. */
#pageActionActivatedActionPanel,
#pageActionPanel,
#editBookmarkPanel,
.browser-extension-panel {
  margin-top: calc(var(--toolbarbutton-inner-padding) - var(--urlbar-icon-padding) - 6px)
}

/* The identity popup does not have any padding of its own,
   otherwise would use the same formula as above. */
#identity-popup {
  margin-top: calc(var(--toolbarbutton-inner-padding) - 6px);
}

/* The bookmarks toolbar is too thin to have the panels overlap 6px. */
#downloadsPanel.bookmarks-toolbar,
#widget-overflow.bookmarks-toolbar,
#appMenu-popup.bookmarks-toolbar,
#customizationui-widget-panel.bookmarks-toolbar {
  margin-top: -1px;
}

/* The BMB_bookmarksPopup is unique because it is built into the
   bookmarks-menu-button, resulting in many edge cases. */
#BMB_bookmarksPopup {
  margin-top: -8px;
}

:root:not([uidensity]) #nav-bar #BMB_bookmarksPopup {
  margin-top: -11px;
}

:root[uidensity=touch] #nav-bar #BMB_bookmarksPopup,
:root[uidensity=touch] #TabsToolbar #BMB_bookmarksPopup,
:root[uidensity=compact] #BMB_bookmarksPopup {
  margin-top: -9px;
}

:root[uidensity=compact] #TabsToolbar #BMB_bookmarksPopup {
  margin-top: -7px;
}

#PersonalToolbar #BMB_bookmarksPopup  {
  margin-top: -2px
}

:root[uidensity=touch] #PersonalToolbar #BMB_bookmarksPopup {
  margin-top: -5px;
}

.panel-subview-body {
  overflow-y: auto;
  overflow-x: hidden;
  -moz-box-flex: 1;
  padding: 6px 0;
}

.panel-view-body-unscrollable {
  overflow: hidden;
  -moz-box-flex: 1;
}

.subviewbutton.panel-subview-footer {
  box-sizing: border-box;
  min-height: 41px;
}

.cui-widget-panelview menuitem.subviewbutton.panel-subview-footer {
  margin: 4px 0 0;
}

.cui-widget-panelview .subviewbutton.panel-subview-footer > .menu-text {
  -moz-box-flex: 1;
}

#appMenu-popup > arrowscrollbox > autorepeatbutton {
  display: none;
}

#appMenu-popup > arrowscrollbox > scrollbox {
  overflow: visible;
}

#appMenu-popup > .panel-arrowcontainer > .panel-arrowcontent,
panel[photon] > .panel-arrowcontainer > .panel-arrowcontent {
  overflow: hidden;
}

.cui-widget-panel > .panel-arrowcontainer > .panel-arrowcontent > .popup-internal-box {
  padding: 0;
}

#wrapper-edit-controls:-moz-any([place="palette"],[place="menu-panel"]) > #edit-controls,
#wrapper-zoom-controls:-moz-any([place="palette"],[place="menu-panel"]) > #zoom-controls {
  margin-inline-start: 0;
}

#BMB_bookmarksPopup {
  max-width: 30em;
}

#pageActionFeedback > .panel-arrowcontainer > .panel-arrowbox {
  /* Don't display the arrow but keep the popup at the same vertical
     offset as other arrow panels. */
  visibility: hidden;
}

#pageActionFeedback > .panel-arrowcontainer > .panel-arrowcontent {
  background-color: #058b00;
  background-image: none;
  border-radius: 2px;
  color: #fff;
  font-weight: 400;
  font-size: 1.1rem;
  -moz-box-align: center;
  padding: 6px 10px;
}

#pageActionFeedbackAnimatableBox {
  position: relative;
  overflow: hidden;
  width: 14px;
  height: 14px;
}

#pageActionFeedbackAnimatableBox[animate] > #pageActionFeedbackAnimatableImage {
  position: absolute;
  background-image: url(chrome://browser/skin/check-animation.svg);
  background-repeat: no-repeat;
  min-width: 266px;
  max-width: 266px;
  min-height: 14px;
  max-height: 14px;
  animation-name: page-action-feedback-animation;
  animation-duration: 300ms;
  animation-delay: 60ms;
  animation-fill-mode: forwards;
  animation-timing-function: steps(18);
}

#pageActionFeedbackAnimatableBox[animate] > #pageActionFeedbackAnimatableImage:-moz-locale-dir(rtl) {
  animation-name: page-action-feedback-animation-rtl;
  transform: translateX(252px);
}

@keyframes page-action-feedback-animation {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(-252px);
  }
}

@keyframes page-action-feedback-animation-rtl {
  from {
    transform: translateX(252px);
  }
  to {
    transform: translateX(0);
  }
}

#pageActionFeedbackMessage {
  margin-inline-start: 7px;
  margin-inline-end: 0;
  transform: scale(.8);
  opacity: 0;
  transition: transform 120ms cubic-bezier(.25,1.27,.35,1.18),
              opacity 60ms linear;
}

#pageActionFeedbackAnimatableBox[animate] + #pageActionFeedbackMessage {
  transform: scale(1);
  opacity: 1;
}

.cui-widget-panel[viewId^=PanelUI-webext-] > .panel-arrowcontainer > .panel-arrowcontent {
  padding: 0;
}

panelview[id^=PanelUI-webext-] {
  overflow: hidden;
}

panelview:not([mainview]) .toolbarbutton-text,
.cui-widget-panel toolbarbutton:not([wrap]) > .toolbarbutton-text,
#overflowMenu-customize-button > .toolbarbutton-text {
  text-align: start;
  display: -moz-box;
}

.cui-widget-panel > .panel-arrowcontainer > .panel-arrowcontent {
  padding: 4px 0;
}

/* START notification popups for extension controlled content */
#extension-notification-panel > .panel-arrowcontainer > .panel-arrowcontent {
  padding: 0;
}

#extension-new-tab-notification > .popup-notification-body-container > .popup-notification-body {
  width: 30em;
}

#extension-new-tab-notification > .popup-notification-body-container > .popup-notification-body > hbox > vbox > .popup-notification-description {
  font-size: 1.3em;
  font-weight: lighter;
}

#extension-new-tab-notification-description {
  margin-bottom: 0;
}

#extension-new-tab-notification-description > .extension-controlled-icon {
  height: 16px;
  width: 16px;
  vertical-align: bottom;
}

#extension-new-tab-notification > .popup-notification-body-container > .popup-notification-body > .popup-notification-warning,
#extension-new-tab-notification > .popup-notification-body-container > .popup-notification-icon {
  display: none;
}
/* END notification popups for extension controlled content */

#appMenu-popup > .panel-arrowcontainer > .panel-arrowcontent,
panel[photon] > .panel-arrowcontainer > .panel-arrowcontent {
  padding: 0;
}

#appMenu-popup panelview,
#customizationui-widget-multiview panelview:not([extension]) {
  min-width: 22.35em;
  max-width: 35em;
}

#customizationui-widget-multiview #appMenu-libraryView,
#pageActionPanel panelview,
#widget-overflow panelview {
  min-width: 29em;
  max-width: 29em;
}

/* Add 2 * 16px extra width for touch mode button padding. */
#appMenu-popup[touchmode] panelview {
  min-width: calc(22.35em + 32px);
}

.cui-widget-panel.cui-widget-panelWithFooter > .panel-arrowcontainer > .panel-arrowcontent {
  padding-bottom: 0;
}

.toolbaritem-combined-buttons:-moz-any(:not([cui-areatype="toolbar"]), [overflowedItem=true]) > toolbarbutton > .toolbarbutton-icon {
  min-width: 0;
  min-height: 0;
  margin: 0;
}

.animate-out {
  animation-name: widget-animate-out;
  animation-fill-mode: forwards;
  animation-duration: 500ms;
  animation-timing-function: var(--animation-easing-function);
}

@keyframes widget-animate-out {
  0% {
    opacity: 1;
    transform: scale(1);
  }
  100% {
    opacity: 0 ;
    transform: scale(.5);
  }
}

toolbarpaletteitem[place="menu-panel"] > .toolbarbutton-1 {
  -moz-box-flex: 1;
}

/* Help webextension buttons fit in. */
toolbarpaletteitem[place="palette"] > toolbarbutton[constrain-size="true"] > .toolbarbutton-icon,
toolbarpaletteitem[place="palette"] > toolbarbutton[constrain-size="true"] > .toolbarbutton-badge-stack > .toolbarbutton-icon,
toolbarbutton[constrain-size="true"][cui-areatype="menu-panel"] > .toolbarbutton-icon,
toolbarbutton[constrain-size="true"][cui-areatype="menu-panel"] > .toolbarbutton-badge-stack > .toolbarbutton-icon {
  height: 16px;
  width: 16px;
}

#customization-palette .toolbarbutton-1 {
  -moz-appearance: none;
  -moz-box-orient: vertical;
  padding: 12px 0 9px;
  margin: 0;
}

/* above we treat the container as the icon for the margins, that is so the
/* badge itself is positioned correctly. Here we make sure that the icon itself
/* has the minimum size we want, but no padding/margin. */
.customization-palette .toolbarbutton-1 > .toolbarbutton-badge-stack > .toolbarbutton-icon {
  width: 16px;
  height: 16px;
  min-width: 16px;
  min-height: 16px;
  margin: 0;
  padding: 0;
}

#edit-controls:-moz-any(:not([cui-areatype="toolbar"]), [overflowedItem=true]) > #copy-button,
#zoom-controls:-moz-any(:not([cui-areatype="toolbar"]), [overflowedItem=true]) > #zoom-reset-button {
  border-left: none;
  border-right: none;
  border-radius: 0;
}

#zoom-in-button > .toolbarbutton-text,
#zoom-out-button > .toolbarbutton-text,
#zoom-reset-button > .toolbarbutton-icon {
  display: none;
}

.addon-banner-item::after,
.panel-banner-item::after {
  content: "";
  width: 16px;
  height: 16px;
  margin-inline-start: 10px;
  margin-inline-end: 12px;
  display: -moz-box;
}

.addon-banner-item {
  background-color: #FFEFBF;
  color: black;
  /* Force border to override `.addon-banner-item` selector below */
  border-top: 1px solid hsl(45,100%,77%) !important;
  display: flex;
  flex: 1 1 0%;
  width: calc(22.35em + 30px);
  padding-inline-start: 15px;
  border-inline-start-style: none;
  -moz-image-region: rect(0, 16px, 16px, 0);
}

.addon-banner-item:last-child {
  border-bottom: 1px solid hsl(45,100%,77%);
}

.addon-banner-item:hover {
  background-color: #FFE8A2;
}

.addon-banner-item:hover:active {
  background-color: #FFE38F;
}

.addon-banner-item > .toolbarbutton-icon {
  width: 16px;
  height: 16px;
}

.addon-banner-item::after {
  background: #FFBF00 url(chrome://browser/skin/update-badge-failed.svg) no-repeat center;
}

.addon-banner-item,
.panel-banner-item {
  margin: 0;
  padding: 11px 0;
  box-sizing: border-box;
  min-height: 40px;
  -moz-appearance: none;
  box-shadow: none;
  border: none;
  border-radius: 0;
  transition: background-color;
  -moz-box-orient: horizontal;
}

.panel-banner-item {
  border-top: 1px solid var(--panel-separator-color);
  border-bottom: 1px solid var(--panel-separator-color);
  padding-inline-start: 12px;
}

#appMenu-addon-banners:not(:empty) + .panel-banner-item {
  /* Overlap the .addon-banner-item border so there's one border. */
  margin-top: -1px;
}

#appMenu-addon-banners > .addon-banner-item {
  padding-inline-start: 12px;
}

.panel-banner-item > .toolbarbutton-text {
  width: 0; /* Fancy cropping solution for flexbox. */
}

.panel-banner-item > .toolbarbutton-icon {
  width: 16px;
}

#appMenu-addon-banners:not(:empty) ~ #appMenu-fxa-container,
.panel-banner-item:not([hidden]) + #appMenu-fxa-container {
  margin-top: 6px;
}

/* FxAccount indicator bits. */

/* Add the .toolbaritem-combined-buttons class to increase the specificity so as
 * to override the end margin for .toolbaritem-combined-buttons items further down. */
#appMenu-fxa-container.toolbaritem-combined-buttons:not([fxastatus="signedin"]) {
  margin-inline-end: 0;
}

#appMenu-fxa-label,
#appMenu-fxa-icon {
  -moz-context-properties: fill;
  fill: currentColor;
  list-style-image: url(chrome://browser/skin/sync.svg);
}

#appMenu-fxa-label {
  -moz-box-flex: 1;
}

@keyframes syncRotate {
  from { transform: rotate(0); }
  to { transform: rotate(360deg); }
}

#appMenu-fxa-icon[syncstatus="active"] > .toolbarbutton-icon,
#PanelUI-remotetabs-syncnow[syncstatus="active"] > .toolbarbutton-icon {
  animation: syncRotate 0.8s linear infinite;
  fill: #0a84ff;
}

#appMenu-fxa-status {
  -moz-box-align: center;
}

#appMenu-fxa-avatar {
  pointer-events: none;
  list-style-image: url(chrome://browser/skin/fxa/default-avatar.svg);
}

/* Handle different UI states. */
#appMenu-fxa-container[fxastatus="signedin"] > #appMenu-fxa-status > #appMenu-fxa-label > .toolbarbutton-icon,
#appMenu-fxa-container:not([fxastatus="signedin"]) > toolbarseparator,
#appMenu-fxa-container:not([fxastatus="signedin"]) > #appMenu-fxa-icon,
#appMenu-fxa-container:not([fxastatus="signedin"]) > #appMenu-fxa-status > #appMenu-fxa-avatar {
  display: none;
}

#appMenu-fxa-container[fxastatus="signedin"] > #appMenu-fxa-status > #appMenu-fxa-label {
  /* 12px space before the avatar, then 16px for the avatar */
  padding-inline-start: 28px;
  margin-inline-start: -28px;
}

#appMenu-fxa-container[fxastatus="signedin"] > #appMenu-fxa-status > #appMenu-fxa-avatar {
  margin-inline-start: 12px;
}

/* Error states */
#appMenu-fxa-container[fxastatus="unverified"] > #appMenu-fxa-status > #appMenu-fxa-label,
#appMenu-fxa-container[fxastatus="login-failed"] > #appMenu-fxa-status > #appMenu-fxa-label {
  list-style-image: url(chrome://browser/skin/warning.svg);
  -moz-image-region: rect(0, 16px, 16px, 0);
}

#appMenu-fxa-container[fxastatus="login-failed"],
#appMenu-fxa-container[fxastatus="unverified"] {
  background-color: #FFEFBF;
  color: black;
  border-top: 1px solid hsl(45,100%,77%);
  border-bottom: 1px solid hsl(45,100%,77%);
}

#appMenu-fxa-container[fxastatus="login-failed"] > #appMenu-fxa-status:hover,
#appMenu-fxa-container[fxastatus="unverified"] > #appMenu-fxa-status:hover {
  background-color: #FFE8A2;
}

#appMenu-fxa-container[fxastatus="login-failed"] > #appMenu-fxa-status:hover:active,
#appMenu-fxa-container[fxastatus="unverified"] > #appMenu-fxa-status:hover:active {
  background-color: #FFE38F;
}

.addon-banner-item > .toolbarbutton-text,
.panel-banner-item > .toolbarbutton-text {
  margin: 0;
  padding: 0 6px;
  text-align: start;
}

.addon-banner-item > .toolbarbutton-icon,
.panel-banner-item > .toolbarbutton-icon {
  margin-inline-end: 0;
}

.addon-banner-item {
  flex: 1;
  padding-inline-start: 15px;
  border-inline-start-style: none;
}

#PanelUI-remotetabs {
  --panel-ui-sync-illustration-height: 91px;
}

.PanelUI-remotetabs-instruction-label {
  /* If you change the margin here, the min-height of the synced tabs panel
    (e.g. #PanelUI-remotetabs[mainview] #PanelUI-remotetabs-setupsync, etc) may
    need adjusting (see bug 1248506) */
  margin: 0;
  text-align: center;
  text-shadow: none;
  max-width: 15em;
  color: GrayText;
}

/* The boxes with "instructions" get extra top and bottom padding for space
   around the illustration and buttons */
.PanelUI-remotetabs-instruction-box {
  /* If you change the padding here, the min-height of the synced tabs panel
    (e.g. #PanelUI-remotetabs[mainview] #PanelUI-remotetabs-setupsync, etc) may
    need adjusting (see bug 1248506) */
  padding-bottom: 30px;
}

.PanelUI-remotetabs-button {
  -moz-appearance: none;
  background-color: #0060df;
  /* !important for the color as an OSX specific rule when a lightweight theme
     is used for buttons in the toolbox overrides. See bug 1238531 for details */
  color: white !important;
  border-radius: 2px;
  /* If you change the margin or padding below, the min-height of the synced tabs
     panel (e.g. #PanelUI-remotetabs[mainview] #PanelUI-remotetabs-setupsync,
     etc) may need adjusting (see bug 1248506) */
  margin-top: 15px;
  margin-bottom: 15px;
  padding: 8px;
  text-shadow: none;
  min-width: 200px;
}

.PanelUI-remotetabs-button:hover {
  background-color: #003eaa;
}

.PanelUI-remotetabs-button:hover:active {
  background-color: #002275;
}

.remotetabs-promo-link {
  margin: 0;
}

.PanelUI-remotetabs-notabsforclient-label {
  color: GrayText;
  /* This margin is to line this label up with the labels in toolbarbuttons. */
  margin-left: 28px;
}

#PanelUI-remotetabs[mainview] .PanelUI-remotetabs-notabsforclient-label {
  margin-left: 32px;
}

.fxaSyncIllustration,
.fxaSyncIllustrationIssue {
  /* If you change the margin here, the min-height of the synced tabs panel
    (e.g. #PanelUI-remotetabs[mainview] #PanelUI-remotetabs-setupsync, etc) may
    need adjusting (see bug 1248506) */
  width: 104px;
  height: var(--panel-ui-sync-illustration-height);
  margin: 38px 0 15px;
  -moz-context-properties: fill;
  fill: #cdcdcd;
}

.fxaSyncIllustration {
  list-style-image: url(chrome://browser/skin/fxa/sync-illustration.svg);
}

.fxaSyncIllustrationIssue {
  list-style-image: url(chrome://browser/skin/fxa/sync-illustration-issue.svg);
}

.PanelUI-remotetabs-button > .toolbarbutton-text {
  /* !important to override ".cui-widget-panel toolbarbutton > .toolbarbutton-text" above. */
  text-align: center !important;
  text-shadow: none;
}

#PanelUI-remotetabs[mainview] { /* panel anchored to toolbar button might be too skinny */
  min-width: 19em;
}

/* Work around bug 1224412 - these boxes will cause scrollbars to appear when
   the panel is anchored to a toolbar button.
*/
#PanelUI-remotetabs[mainview] #PanelUI-remotetabs-setupsync,
#PanelUI-remotetabs[mainview] #PanelUI-remotetabs-reauthsync,
#PanelUI-remotetabs[mainview] #PanelUI-remotetabs-unverified,
#PanelUI-remotetabs[mainview] #PanelUI-remotetabs-nodevicespane,
#PanelUI-remotetabs[mainview] #PanelUI-remotetabs-tabsdisabledpane {
  min-height: calc(var(--panel-ui-sync-illustration-height) +
                   53px + /* margin of .fxaSyncIllustration */
                   30px + /* margin of .PanelUI-remotetabs-button */
                   16px + /* padding of .PanelUI-remotetabs-button */
                   30px + /* padding of .PanelUI-remotetabs-instruction-box */
                   11em);
}

#PanelUI-remotetabs-tabslist > label[itemtype="client"] {
  color: GrayText;
}

/* Collapse the non-active vboxes in the remotetabs deck to use only the
   height the active box needs */
#PanelUI-remotetabs-deck:not([selectedIndex="1"]) > #PanelUI-remotetabs-tabsdisabledpane,
#PanelUI-remotetabs-deck:not([selectedIndex="2"]) > #PanelUI-remotetabs-fetching,
#PanelUI-remotetabs-deck:not([selectedIndex="3"]) > #PanelUI-remotetabs-nodevicespane {
  visibility: collapse;
}

#PanelUI-remotetabs-main[devices-status="single"] > #PanelUI-remotetabs-buttons {
  display: none;
}

.panel-banner-item {
  color: black;
  background-color: hsla(96,65%,75%,.5);
}

.panel-banner-item:not([disabled]):hover {
  background-color: hsla(96,65%,75%,.8);
}

.panel-banner-item:not([disabled]):hover:active {
  background-color: hsl(96,65%,75%);
}

#customization-palette .toolbarbutton-multiline-text,
#customization-palette .toolbarbutton-text {
  display: none;
}

.subview-subheader,
panelview .toolbarbutton-1,
.subviewbutton,
.widget-overflow-list .toolbarbutton-1 {
  -moz-appearance: none;
  margin: 0;
  min-height: 24px;
  padding: 4px 12px;
  background-color: transparent;
}

.subviewbutton:focus {
  outline: 0;
}

.subviewbutton > .toolbarbutton-text {
  padding: 0;
  padding-inline-start: 24px; /* This is 16px for the icon + 8px for the padding as defined below. */
}

.subviewbutton > .menu-right,
.subviewbutton > .menu-accel-container > .menu-iconic-accel,
.subviewbutton > .menu-iconic-left,
.subviewbutton > .menu-iconic-text {
  padding-bottom: 0;
  padding-top: 0;
}

.addon-banner-item > .toolbarbutton-text,
.subviewbutton-iconic > .toolbarbutton-text,
.cui-withicon > .toolbarbutton-text,
.subviewbutton[image] > .toolbarbutton-text,
.subviewbutton[targetURI] > .toolbarbutton-text,
.subviewbutton.restoreallitem > .toolbarbutton-text,
.subviewbutton.bookmark-item > .toolbarbutton-text,
.subviewbutton[checked="true"] > .toolbarbutton-text {
  padding-inline-start: 8px; /* See '.subviewbutton-iconic > .toolbarbutton-text' rule above. */
}

.panel-banner-item > .toolbarbutton-multiline-text {
  font: menu;
  margin: 0;
  padding: 0;
  padding-inline-start: 8px; /* See '.subviewbutton-iconic > .toolbarbutton-text' rule above. */
}

.subviewbutton-iconic > .toolbarbutton-icon {
  width: 16px;
  height: 16px;
  -moz-context-properties: fill;
  fill: currentColor;
}

/* We don't always display: none this item, and if it has forced width (like above)
 * or margin, that impacts the position of the label. Fix:
 */
.subviewbutton > .toolbarbutton-icon {
  margin: 0;
}

.subviewbutton.panel-subview-footer > .menu-text {
  -moz-appearance: none;
  padding-inline-end: 6px;
  padding-inline-start: 0 !important; /* to override menu.css on Windows */
  -moz-box-flex: 0;
}

.subviewbutton.panel-subview-footer > .toolbarbutton-text {
  padding-inline-start: 0;
}

.subviewbutton.panel-subview-footer > .menu-accel-container {
  padding-inline-start: 6px;
}

.subviewbutton > .menu-accel-container > .menu-iconic-accel,
.subviewbutton > .menu-accel-container > .menu-accel {
  margin-inline-end: 0 !important; /* to override menu.css on Windows */
}

#widget-overflow-fixed-list .toolbarbutton-1 > .toolbarbutton-text,
#widget-overflow-list .toolbarbutton-1 > .toolbarbutton-text,
.subviewbutton:not(.panel-subview-footer) > .toolbarbutton-text,
.addon-banner-item > .toolbarbutton-text,
/* Bookmark items need a more specific selector. */
.PanelUI-subView .subviewbutton:not(.panel-subview-footer) > .menu-text,
.PanelUI-subView .subviewbutton:not(.panel-subview-footer) > .menu-iconic-text {
  font: menu;
}

.subviewbutton[shortcut]::after {
  content: attr(shortcut);
  float: right;
  color: GrayText;
}

.PanelUI-subView .subviewbutton-nav::after {
  -moz-context-properties: fill, fill-opacity;
  content: url(chrome://browser/skin/back-12.svg);
  fill: currentColor;
  fill-opacity: 0.6;
  float: right;
  transform: translateY(1px);
}

#main-window:not([customizing]) .subviewbutton-nav[disabled=true]::after {
  opacity: 0.4;
}

.PanelUI-subView .subviewbutton-nav:-moz-locale-dir(ltr)::after {
  transform: scaleX(-1) translateY(1px);
}

.subviewbutton[shortcut]::after,
.subviewbutton[shortcut]::after,
.PanelUI-subView .subviewbutton-nav::after {
  margin-inline-start: 10px;
}

.subviewbutton[type="highlight-bookmark"]::after {
  content: url("chrome://browser/skin/bookmark-hollow.svg");
}

.subviewbutton[type="highlight-history"]::after {
  content: url("chrome://browser/skin/history.svg");
}

.subviewbutton[type="highlight-bookmark"]::after,
.subviewbutton[type="highlight-history"]::after {
  -moz-context-properties: fill, fill-opacity;
  fill: currentColor;
  fill-opacity: 0.4;
  float: right;
  /* Centers the icon and resizes it to 12px square. */
  transform: translateY(2px) scaleX(.75);
}

/* This is a <label> but it should fit in with the menu font- and colorwise. */
#PanelUI-characterEncodingView-autodetect-label {
  font: menu;
  color: inherit;
}

.subviewbutton[checked="true"] {
  list-style-image: url(chrome://browser/skin/check.svg);
  -moz-context-properties: fill;
  fill: currentColor;
}

#appMenu-popup .toolbaritem-combined-buttons {
  -moz-box-align: center;
  -moz-box-orient: horizontal;
  border: 0;
  border-radius: 0;
  margin-inline-end: 8px;
}

panelmultiview .toolbaritem-combined-buttons > label {
  -moz-box-flex: 1;
  font: menu;
  margin: 0;
  padding: 4px 0px;
}

panelmultiview .toolbaritem-combined-buttons > spacer.before-label {
  width: 36px; /* 12px toolbarbutton padding + 16px icon + 8px label padding start */
}

panelmultiview .toolbaritem-combined-buttons > spacer.after-label {
  -moz-box-flex: 1;
  width: 20px; /* a little bigger than the width of the scrollbar */
}

.PanelUI-subView .toolbaritem-combined-buttons > .subviewbutton {
  -moz-box-flex: 0;
  height: auto;
  margin-inline-start: 18px;
  min-width: auto;
  padding: 4px;
}

#appMenu-zoom-controls > .subviewbutton {
  margin-inline-start: 10px;
}

/* Unset the min-height constraint, because that works better for a text-only button. */
#appMenu-zoomReset-button {
  min-height: unset;
  border: 1px solid var(--panel-separator-color);
  border-radius: 10000px;
  padding: 1px 8px;
  background-color: var(--arrowpanel-dimmed);
}

#appMenu-zoomReset-button:not(:-moz-any([disabled],[open],:active)):-moz-any(:hover,:focus) {
  background-color: var(--arrowpanel-dimmed-further);
}

#appMenu-zoomReset-button:not([disabled]):-moz-any([open],:hover:active) {
  background-color: var(--arrowpanel-dimmed-even-further);
}

#appMenu-zoomReset-button > .toolbarbutton-text {
  min-width: calc(3ch + 8px);
  text-align: center;
}

.toolbaritem-combined-buttons > toolbarseparator[orient="vertical"] + .subviewbutton,
#appMenu-zoom-controls > toolbarseparator[orient="vertical"] + .subviewbutton {
  margin-inline-start: 0;
}

.PanelUI-subView .toolbaritem-combined-buttons > .subviewbutton-iconic > .toolbarbutton-text,
.PanelUI-subView .toolbaritem-combined-buttons > .subviewbutton:not(.subviewbutton-iconic) > .toolbarbutton-icon {
  display: none;
}

.toolbaritem-combined-buttons > .subviewbutton:not(.subviewbutton-iconic) > .toolbarbutton-text {
  font-size: 1em;
  padding-inline-start: 0;
}

.subview-subheader {
  color: GrayText;
}

.subview-subheader,
.panel-subview-footer {
  font: menu;
}

panelview .toolbarbutton-1 {
  margin-top: 6px;
}

panelview .toolbarbutton-1:not(:-moz-any([disabled],[open],:active)):-moz-any(:hover,:focus),
toolbarbutton.subviewbutton:not(:-moz-any([disabled],[open],:active)):-moz-any(:hover,:focus),
menu.subviewbutton:not(:-moz-any([disabled],:active))[_moz-menuactive],
menuitem.subviewbutton:not(:-moz-any([disabled],:active))[_moz-menuactive],
.widget-overflow-list .toolbarbutton-1:not(:-moz-any([disabled],[open],:active)):-moz-any(:hover,:focus),
.toolbaritem-combined-buttons:-moz-any(:not([cui-areatype="toolbar"]), [overflowedItem=true]) > toolbarbutton:not(:-moz-any([disabled],[open],:active)):-moz-any(:hover,:focus) {
  color: inherit;
  background-color: var(--arrowpanel-dimmed);
}

panelview .toolbarbutton-1:-moz-any(:not([disabled]):-moz-any([open],:hover:active),[checked=true]),
toolbarbutton.subviewbutton:not([disabled]):-moz-any([open],:hover:active),
menu.subviewbutton:not([disabled])[_moz-menuactive]:active,
menuitem.subviewbutton:not([disabled])[_moz-menuactive]:active,
.widget-overflow-list .toolbarbutton-1:not([disabled]):-moz-any([open],:hover:active),
.toolbaritem-combined-buttons:-moz-any(:not([cui-areatype="toolbar"]), [overflowedItem=true]) > toolbarbutton:not([disabled]):-moz-any([open],:hover:active) {
  color: inherit;
  background-color: var(--arrowpanel-dimmed-further);
  box-shadow: 0 1px 0 hsla(210,4%,10%,.03) inset;
}

.subviewbutton.panel-subview-footer {
  margin: 0;
  background-color: var(--arrowpanel-dimmed);
  border-top: 1px solid var(--panel-separator-color);
  border-radius: 0;
}

menuitem.panel-subview-footer:not(:-moz-any([disabled],:active))[_moz-menuactive],
.subviewbutton.panel-subview-footer:not(:-moz-any([disabled],[open],:active)):-moz-any(:hover,:focus) {
  background-color: var(--arrowpanel-dimmed-further);
}

menuitem.panel-subview-footer:not([disabled])[_moz-menuactive]:active,
.subviewbutton.panel-subview-footer:not([disabled]):-moz-any([open],:hover:active) {
  background-color: var(--arrowpanel-dimmed-even-further);
  box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset;
}

#BMB_bookmarksPopup .subviewbutton {
  font: menu;
  font-weight: normal;
}

#BMB_bookmarksPopup .subviewbutton:not([disabled="true"]) {
  color: inherit;
}

#BMB_bookmarksPopup .panel-arrowcontainer > .panel-arrowcontent > .popup-internal-box > .autorepeatbutton-up,
#BMB_bookmarksPopup .panel-arrowcontainer > .panel-arrowcontent > .popup-internal-box > .autorepeatbutton-down {
  -moz-appearance: none;
  margin-top: 0;
  margin-bottom: 0;
}

/* Remove padding on xul:arrowscrollbox to avoid extra padding on footer */
#BMB_bookmarksPopup arrowscrollbox {
  padding-bottom: 0px;
}

#BMB_bookmarksPopup menupopup > .bookmarks-actions-menuseparator {
  /* Hide bottom separator as the styled footer includes a top border serving the same purpose */
  display: none;
}

/* Popups with only one item don't have a footer */
#BMB_bookmarksPopup menupopup[placespopup=true][singleitempopup=true] > hbox > .popup-internal-box > .arrowscrollbox-scrollbox > .scrollbox-innerbox,
/* These popups never have a footer */
#BMB_bookmarksToolbarPopup > hbox > .popup-internal-box > .arrowscrollbox-scrollbox > .scrollbox-innerbox,
#BMB_unsortedBookmarksPopup > hbox > .popup-internal-box > .arrowscrollbox-scrollbox > .scrollbox-innerbox,
#BMB_mobileBookmarksPopup > hbox > .popup-internal-box > .arrowscrollbox-scrollbox > .scrollbox-innerbox {
  /* And so they need some bottom padding: */
  padding-bottom: 4px;
}

/* Disabled (empty) item is always alone and never has an icon, so fix its left padding */
#BMB_bookmarksPopup menupopup[emptyplacesresult] .bookmark-item.subviewbutton {
  padding-left: 6px;
}

#widget-overflow-mainView > .panel-subview-body > toolbarseparator,
.PanelUI-subView menuseparator,
.PanelUI-subView toolbarseparator,
.cui-widget-panelview menuseparator,
.cui-widget-panel toolbarseparator {
  -moz-appearance: none;
  min-height: 0;
  border-top: 1px solid var(--panel-separator-color);
  border-bottom: none;
  margin: 6px 0;
  padding: 0;
}

.PanelUI-subView menuseparator {
  margin-inline-start: -5px;
  margin-inline-end: -4px;
}

.PanelUI-subView toolbarseparator {
  margin-inline-start: 0;
  margin-inline-end: 0;
}

.PanelUI-subView menuseparator.small-separator,
.PanelUI-subView toolbarseparator.small-separator {
  margin-left: 5px;
  margin-right: 5px;
}

.cui-widget-panelview menuseparator.small-separator {
  margin-left: 10px;
  margin-right: 10px;
}

.PanelUI-subView toolbarseparator[orient="vertical"] {
  height: 24px;
  border-inline-start: 1px solid var(--panel-separator-color);
  border-top: none;
  margin: 0;
  margin-inline-start: 6px;
  margin-inline-end: 7px;
}

.subviewbutton > .menu-accel-container {
  -moz-box-pack: end;
  margin-inline-start: 10px;
  margin-inline-end: auto;
  color: GrayText;
}

#PanelUI-remotetabs-tabslist > toolbarbutton[itemtype="tab"],
#PanelUI-historyItems > toolbarbutton {
  list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.svg");
  -moz-context-properties: fill;
  fill: currentColor;
}

#appMenu-fxa-avatar,
#appMenu-fxa-label > .toolbarbutton-icon,
#appMenu-fxa-icon > .toolbarbutton-icon,
#PanelUI-containersItems > .subviewbutton > .toolbarbutton-icon,
#PanelUI-remotetabs-tabslist > toolbarbutton[itemtype="tab"] > .toolbarbutton-icon,
#PanelUI-recentlyClosedWindows > toolbarbutton > .toolbarbutton-icon,
#PanelUI-recentlyClosedTabs > toolbarbutton > .toolbarbutton-icon,
#PanelUI-historyItems > toolbarbutton > .toolbarbutton-icon {
  width: 16px;
  height: 16px;
}

toolbarpaletteitem[place="palette"] > .toolbarbutton-1 > .toolbarbutton-menu-dropmarker,
#bookmarks-menu-button[cui-areatype="menu-panel"] > .toolbarbutton-menu-dropmarker,
#bookmarks-menu-button[overflowedItem] > .toolbarbutton-menu-dropmarker,
toolbarpaletteitem[place="palette"] > .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker,
#bookmarks-menu-button[cui-areatype="menu-panel"] > .toolbarbutton-menubutton-dropmarker {
  display: none;
}

#search-container[cui-areatype="menu-panel"] {
  padding-top: 6px;
  padding-bottom: 6px;
}

toolbarpaletteitem[place="palette"] > #search-container {
  min-width: 7em;
  width: 7em;
  min-height: 37px;
}

.toolbaritem-combined-buttons:-moz-any(:not([cui-areatype="toolbar"]), [overflowedItem=true]) > toolbarbutton {
  border: 0;
  margin: 0;
  -moz-box-flex: 1;
  padding-top: 4px;
  padding-bottom: 4px;
  -moz-box-orient: horizontal;
}

/* In customize mode, extend the buttons *only* in the panel, just to make them not look stupid */
toolbarpaletteitem[place="menu-panel"] > .toolbaritem-combined-buttons > toolbarbutton {
  min-width: calc((22.35em / 3 - 0.1px) - 1px);
  max-width: calc((22.35em / 3 - 0.1px) - 1px);
}

.toolbaritem-combined-buttons:-moz-any(:not([cui-areatype="toolbar"]), [overflowedItem=true]) > toolbarbutton[disabled] {
  /* Override toolbarbutton.css which sets the color to GrayText */
  color: inherit;
}

#main-window:not([customizing]) .toolbaritem-combined-buttons:-moz-any(:not([cui-areatype="toolbar"]), [overflowedItem=true]) > toolbarbutton[disabled] > .toolbarbutton-icon {
  opacity: .25;
}

#zoom-controls[cui-areatype="toolbar"] > #zoom-reset-button > .toolbarbutton-text {
  min-width: 7ch;
}

#edit-controls:-moz-any(:not([cui-areatype="toolbar"]), [overflowedItem=true]) > #cut-button:-moz-locale-dir(ltr),
#edit-controls:-moz-any(:not([cui-areatype="toolbar"]), [overflowedItem=true]) > #paste-button:-moz-locale-dir(rtl),
#zoom-controls:-moz-any(:not([cui-areatype="toolbar"]), [overflowedItem=true]) > #zoom-out-button:-moz-locale-dir(ltr),
#zoom-controls:-moz-any(:not([cui-areatype="toolbar"]), [overflowedItem=true]) > #zoom-in-button:-moz-locale-dir(rtl) {
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}

#edit-controls:-moz-any(:not([cui-areatype="toolbar"]), [overflowedItem=true]) > #cut-button:-moz-locale-dir(rtl),
#edit-controls:-moz-any(:not([cui-areatype="toolbar"]), [overflowedItem=true]) > #paste-button:-moz-locale-dir(ltr),
#zoom-controls:-moz-any(:not([cui-areatype="toolbar"]), [overflowedItem=true]) > #zoom-out-button:-moz-locale-dir(rtl),
#zoom-controls:-moz-any(:not([cui-areatype="toolbar"]), [overflowedItem=true]) > #zoom-in-button:-moz-locale-dir(ltr) {
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
}

.toolbaritem-combined-buttons:-moz-any(:not([cui-areatype="toolbar"]), [overflowedItem=true]) > separator {
  -moz-appearance: none;
  -moz-box-align: stretch;
  margin: .5em 0;
  width: 1px;
  height: auto;
  background: var(--panel-separator-color);
  transition-property: margin;
  transition-duration: 10ms;
  transition-timing-function: ease;
}

.toolbaritem-combined-buttons:-moz-any(:not([cui-areatype="toolbar"]), [overflowedItem=true]):hover > separator {
  margin: 0;
}

#widget-overflow > .panel-arrowcontainer > .panel-arrowcontent {
  padding: 0;
}

#widget-overflow-mainView .panel-subview-body {
  overflow-y: auto;
  overflow-x: hidden;
}

.widget-overflow-list {
  width: 29em;
}

/* In customize mode, the overflow list is constrained by its container,
 * so we set width: auto to avoid the scrollbar not fitting.
 */
#customization-panelHolder > .widget-overflow-list {
  width: auto;
}

toolbaritem[overflowedItem=true],
.widget-overflow-list .toolbarbutton-1 {
  width: 100%;
  max-width: 29em;
  background-repeat: no-repeat;
  background-position: 0 center;
}

.widget-overflow-list .toolbarbutton-1 {
  -moz-box-align: center;
  -moz-box-orient: horizontal;
}

.widget-overflow-list .subviewbutton-nav:-moz-locale-dir(ltr)::after {
  transform: scaleX(-1);
}

.widget-overflow-list .subviewbutton-nav::after {
  margin-inline-start: 10px;
  -moz-context-properties: fill, fill-opacity;
  content: url(chrome://browser/skin/back-12.svg);
  fill: currentColor;
  fill-opacity: 0.6;
  float: right;
}

toolbarpaletteitem[place="menu-panel"] > .subviewbutton-nav::after {
  opacity: 0.5;
}

.widget-overflow-list .toolbarbutton-1:not(.toolbarbutton-combined) > .toolbarbutton-text {
  text-align: start;
  padding-inline-start: .5em;
}

.subviewbutton > .menu-iconic-left {
  -moz-appearance: none;
  margin-inline-end: 0;
}

menuitem[checked="true"].subviewbutton > .menu-iconic-left {
  visibility: hidden;
}

#PanelUI-panicView > .panel-subview-body,
#PanelUI-panicView {
  overflow: visible;
}

#PanelUI-panicView.cui-widget-panelview {
  min-width: 280px;
}

#PanelUI-panic-timeframe {
  padding: 15px;
  border-bottom: 1px solid var(--panel-separator-color);
}

#panic-button-success-icon,
#PanelUI-panic-timeframe-icon {
  background-color: transparent;
  margin-inline-end: 10px;
}

#panic-button-success-icon,
#PanelUI-panic-timeframe-icon {
  list-style-image: url(chrome://browser/skin/panic-panel/header.png);
  max-height: 48px;
  width: 48px;
}

#panic-button-success-header {
  -moz-box-align: center;
  margin-bottom: 5px;
}

#PanelUI-panic-timeframe-icon:-moz-locale-dir(rtl) {
  transform: scaleX(-1);
}

.subviewradio {
  -moz-appearance: none;
  -moz-box-align: center;
  padding: 1px;
  margin: 0 0 2px;
  background-color: transparent;
  border-radius: 2px;
  border: 1px solid transparent;
}

.subviewradio:not(:-moz-any([disabled],[open],:active)):-moz-any(:hover,:focus) {
  background-color: var(--arrowpanel-dimmed);
  border-color: var(--panel-separator-color);
}

.subviewradio[selected],
.subviewradio[selected]:hover,
.subviewradio:not([disabled]):-moz-any([open],:hover:active) {
  background-color: var(--arrowpanel-dimmed-further);
  border-color: var(--panel-separator-color);
  box-shadow: 0 1px 0 hsla(210,4%,10%,.03) inset;
}

.subviewradio > .radio-check {
  -moz-appearance: none;
  width: 16px;
  height: 16px;
  border: 1px solid #e7e7e7;
  border-radius: 50%;
  margin: 1px 5px;
  background-color: #f1f1f1;
}

.subviewradio > .radio-check[selected] {
  background-color: #fff;
  border: 4px solid #177ee6;
}

#PanelUI-panic-explanations {
  padding: 10px 10px 0;
}

#PanelUI-panic-actionlist-main-label {
  color: GrayText;
  font-size: 0.9em;
}

.PanelUI-panic-actionlist {
  padding-inline-start: 20px;
  padding-top: 2px;
  padding-bottom: 2px;
  background-size: 16px 16px;
  background-repeat: no-repeat;
  background-color: transparent;
  background-position: center left;
}

.PanelUI-panic-actionlist:-moz-locale-dir(rtl) {
  background-position: center right;
}

#PanelUI-panic-actionlist-cookies {
  background-image: -moz-image-rect(url(chrome://browser/skin/panic-panel/icons.png), 0, 16, 16, 0);
}

#PanelUI-panic-actionlist-history {
  background-image: -moz-image-rect(url(chrome://browser/skin/panic-panel/icons.png), 0, 32, 16, 16);
}

#PanelUI-panic-actionlist-windows {
  background-image: -moz-image-rect(url(chrome://browser/skin/panic-panel/icons.png), 0, 48, 16, 32);
}

#PanelUI-panic-actionlist-newwindow {
  background-image: -moz-image-rect(url(chrome://browser/skin/panic-panel/icons.png), 0, 64, 16, 48);
}

#PanelUI-panic-warning {
  color: #C11F14;
  text-align: center;
  width: 100%;
  margin-top: 20px;
}

#PanelUI-panic-view-button {
  -moz-appearance: none;
  background-color: #d92316;
  color: white;
  margin: 5px 15px 11px;
  border: 1px solid #c92014;
  border-radius: 3px;
  padding: 10px;
}

#PanelUI-panic-view-button:hover {
  background-color: #bf1f13;
  border-color: #b81d12;
}

#PanelUI-panic-view-button:hover:active {
  background-color: #99180f;
  border-color: #91170f;
}

#PanelUI-panic-view-button > .toolbarbutton-text {
  text-align: center;
  text-shadow: none;
}

#panic-button-success-closebutton {
  background-color: #e5e5e5;
  color: black;
  margin: 5px 0 0;
  border: 1px solid #ccc;
  border-radius: 3px;
  padding: 10px;
  -moz-appearance: none;
}

#panic-button-success-closebutton:hover {
  background-color: #dedede;
  border-color: #bbb;
}

#panic-button-success-closebutton:hover:active {
  background-color: #d0d0d0;
  border-color: #aaa;
}

@media (min-resolution: 1.1dppx) {
  .panel-banner-item[notificationid^=update] {
    list-style-image: url(chrome://branding/content/icon32.png);
  }

  #panic-button-success-icon,
  #PanelUI-panic-timeframe-icon {
    list-style-image: url(chrome://browser/skin/panic-panel/[email protected]);
  }

  #PanelUI-panic-actionlist-cookies {
    background-image: -moz-image-rect(url(chrome://browser/skin/panic-panel/[email protected]), 0, 32, 32, 0);
  }

  #PanelUI-panic-actionlist-history {
    background-image: -moz-image-rect(url(chrome://browser/skin/panic-panel/[email protected]), 0, 64, 32, 32);
  }

  #PanelUI-panic-actionlist-windows {
    background-image: -moz-image-rect(url(chrome://browser/skin/panic-panel/[email protected]), 0, 96, 32, 64);
  }

  #PanelUI-panic-actionlist-newwindow {
    background-image: -moz-image-rect(url(chrome://browser/skin/panic-panel/[email protected]), 0, 128, 32, 96);
  }
}

.panel-header {
  align-items: center;
  border-bottom: 1px solid var(--panel-separator-color);
  display: flex;
  flex: 1 auto;
  height: 40px; /* fixed item height to prevent flex sizing; height + 2*4px padding */
  padding: 4px;
}

.panel-header > label {
  flex: auto;
  font-size: 13px;
  font-weight: 500;
  margin: 0;
  /* Add the size of the back button to center properly. */
  margin-inline-end: 32px;
  text-align: center;
}

.panel-header > .subviewbutton-back {
  -moz-context-properties: fill;
  fill: var(--arrowpanel-color);
  list-style-image: url(chrome://browser/skin/arrow-left.svg);
  padding: 8px;
}

.subviewbutton-back:-moz-locale-dir(rtl) {
  transform: scaleX(-1);
}

.subviewbutton-back > .toolbarbutton-text {
  /* !important to override .cui-widget-panel toolbarbutton:not([wrap]) > .toolbarbutton-text
   * selector further down. */
  display: none !important;
}

.subviewbutton.download {
  -moz-box-align: start;
  min-height: 48px;
  padding-inline-start: 8px;
}

.subviewbutton.download > .toolbarbutton-icon,
.subviewbutton.download > .toolbarbutton-text > label {
  margin: 4px 0 0;
}

.subviewbutton.download > .toolbarbutton-icon {
  width: 32px;
  height: 32px;
}

.subviewbutton.download > .toolbarbutton-text > .status-text {
  color: GrayText;
  font-size: .9em;
}

.subviewbutton.download > .action-button {
  -moz-appearance: none; /* To avoid native Windows hover styling */
  -moz-context-properties: fill, fill-opacity;
  fill: currentColor;
  fill-opacity: 1;
  list-style-image: url("chrome://browser/skin/folder.svg");
  /* Measurement to vertically center this button: 1 line of text minus half of 4px top margin. */
  margin: calc(1em - 2px) 0 0;
  padding: 4px;
}

.subviewbutton.download[retryLabel] > .action-button {
  list-style-image: url("chrome://browser/skin/reload.svg");
}

.subviewbutton.download:not([openLabel]):not([retryLabel]) > .action-button {
  fill: currentColor;
  fill-opacity: 0.4;
}

.subviewbutton.download:-moz-any([openLabel],[retryLabel]) > .action-button:not(:-moz-any([disabled],[open],:active)):-moz-any(:hover,:focus) {
  background-color: var(--arrowpanel-dimmed-further);
}

.subviewbutton.download:-moz-any([openLabel],[retryLabel]) > .action-button:not([disabled]):-moz-any([open],:hover:active) {
  background-color: var(--arrowpanel-dimmed-even-further);
}

#BMB_bookmarksPopup > menuitem[type="checkbox"] {
  -moz-appearance: none !important; /* important, to override toolkit rule */
}

#BMB_bookmarksPopup menupopup {
  -moz-appearance: none;
  background: var(--arrowpanel-background);
  color: var(--arrowpanel-color);
  border: 1px solid var(--arrowpanel-border-color);
  margin-top: -6px;
  padding-top: 1px;
}

/* Add some space at the top because there are no headers: */
#BMB_bookmarksPopup menupopup > hbox > .popup-internal-box > .arrowscrollbox-scrollbox > .scrollbox-innerbox  {
  padding-top: 4px;
}

menuitem.subviewbutton {
  -moz-appearance: none !important;
}

menu.subviewbutton > .menu-right {
  -moz-appearance: none;
  list-style-image: url(chrome://browser/skin/places/bookmarks-menu-arrow.png);
  -moz-image-region: rect(0, 16px, 16px, 0);
  width: 16px;
  height: 16px;
}

menu[disabled="true"].subviewbutton > .menu-right {
  -moz-image-region: rect(0, 32px, 16px, 16px);
}

menu.subviewbutton > .menu-right:-moz-locale-dir(rtl) {
  transform: scaleX(-1);
}

.subviewradio > .radio-label-box {
  -moz-appearance: none;
}

/*
 * #pageActionFeedbackAnimatableImage is wider than the panel, and due to a
 * bug in panels on Linux, a box-shadow appears where the image would be if
 * overflow:hidden wasn't applied. Disabling the box-shadow for this panel on
 * Linux works around this issue. This bug is on file as 1394575.
 */
#pageActionFeedback > .panel-arrowcontainer > .panel-arrowcontent {
  box-shadow: none;
}

PK
!<j1�mT0T0;chrome/browser/skin/classic/browser/downloads/downloads.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */



/*** Panel and outer controls ***/

#downloadsPanel > .panel-arrowcontainer > .panel-arrowcontent {
  overflow: hidden;
  padding: 0;
}

#downloadsListBox {
  background: transparent;
  color: inherit;
  -moz-appearance: none;
  margin: 0;
}

#emptyDownloads {
  padding: 16px 16px;
  margin: 0;
}

.downloadsPanelFooter {
  background-color: var(--arrowpanel-dimmed);
  border-top: 1px solid var(--panel-separator-color);
}

.downloadsPanelFooter toolbarseparator,
richlistitem[type="download"] > toolbarseparator {
  margin: 0;
  border: 0;
  min-width: 0;
  border-left: 1px solid var(--panel-separator-color);
  -moz-appearance: none;
}

.downloadsPanelFooterButton {
  -moz-appearance: none;
  background-color: transparent;
  color: inherit;
  margin: 0;
  padding: 0;
  min-width: 0;
  min-height: 40px;
  border: none;
}

.downloadsPanelFooterButton:hover {
  outline: 1px solid var(--arrowpanel-dimmed);
  background-color: var(--arrowpanel-dimmed);
}

.downloadsPanelFooterButton:hover:active,
.downloadsPanelFooterButton[open="true"] {
  outline: 1px solid var(--arrowpanel-dimmed-further);
  background-color: var(--arrowpanel-dimmed-further);
  box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset;
}

.downloadsPanelFooterButton[default] {
  background-color: #0996f8;
  color: white;
}

.downloadsPanelFooterButton[default]:hover {
  background-color: #0675d3;
}

.downloadsPanelFooterButton[default]:hover:active {
  background-color: #0568ba;
}

.downloadsPanelFooterButton > .button-box {
  padding: 0;
}

#downloadsHistory {
  padding-inline-start: 14px;
  padding-inline-end: 14px;
}

richlistitem[type="download"] > toolbarseparator {
  margin: 10px 0;
}

richlistitem[type="download"]:hover > toolbarseparator {
  margin: 0;
}

#downloadsSummary {
  -moz-user-focus: normal;
}

#downloadsSummary > .downloadTypeIcon {
  list-style-image: url("chrome://browser/skin/downloads/download-summary.svg");
}

#downloadsSummaryDescription {
  color: -moz-nativehyperlinktext;
}

/*** List items and similar elements in the summary ***/

#downloadsSummary,
richlistitem[type="download"] {
  height: var(--downloads-item-height);
}

richlistitem[type="download"] {
  border-bottom: 1px solid var(--panel-separator-color);
  background: transparent;
  color: inherit;
}

richlistitem[type="download"]:last-child {
  border-bottom: none;
}

.downloadTypeIcon {
  margin: 8px 13px;
  width: 32px;
  height: 32px;
}

.downloadBlockedBadge {
  margin: 0 5px;
  background: url("chrome://browser/skin/downloads/download-blocked.svg") top right / 16px no-repeat;
}

.downloadBlockedBadge:-moz-locale-dir(rtl) {
  background-position-x: left;
}

richlistitem[type="download"][verdict="PotentiallyUnwanted"] .downloadBlockedBadge {
  background-image: url("chrome://browser/skin/warning.svg");
}

richlistitem[type="download"][verdict="Uncommon"] .downloadBlockedBadge {
  background-image: url("chrome://browser/skin/info.svg");
}

/* We hold .downloadTarget, .downloadProgress and .downloadDetails inside of
   a vbox with class .downloadContainer. We set the font-size of the entire
   container to --downloads-item-font-size-factor because:

   1) This is the size that we want .downloadDetails to be
   2) The container's width is set by localizers by &downloadDetails.width;,
      which is a ch unit. Since this is the value that should control the
      panel width, we apply it to the outer container to constrain
      .downloadTarget and .downloadProgress.

   Finally, since we want .downloadTarget's font-size to be at 100% of the
   font-size of .downloadContainer's parent, we use calc to go from the
   smaller font-size back to the original font-size.
 */
.downloadContainer {
  font-size: calc(100% * var(--downloads-item-font-size-factor));
  margin-inline-end: 13px;
}

#downloadsSummaryDescription,
.downloadTarget {
  margin: 0;
  font-size: calc(100% / var(--downloads-item-font-size-factor));
}

#downloadsSummaryDetails,
.downloadDetails {
  opacity: var(--downloads-item-details-opacity);
  /* Use calc() to keep the height consistent with .downloadTarget, so that the
     progress bar can be vertically centered. */
  margin: 4px 0 calc(1em / var(--downloads-item-font-size-factor) - 1em);
}

/* The following rules control which message is shown under the name of the
   download, using a set of elements that share the class ".downloadDetails".
   At any given time, only one of these elements is displayed. We use a set of
   rules to hide the elements that shouldn't be displayed in each case. */

/* The full status message is only displayed in the Downloads View. */
.downloadDetailsFull {
  display: none;
}

/* When hovering the mouse pointer over the item, instead of the normal message
   we display a more detailed one. */
richlistitem[type="download"]:hover > .downloadMainArea > .downloadContainer > .downloadDetailsNormal,
richlistitem[type="download"]:not(:hover) > .downloadMainArea > .downloadContainer > .downloadDetailsHover {
  display: none;
}

/* When hovering the action button in particular, instead of the usual hover
   message we display the command associated with the button. */
richlistitem[type="download"].downloadHoveringButton > .downloadMainArea > .downloadContainer > .downloadDetailsHover,
richlistitem[type="download"]:not(.downloadHoveringButton) > .downloadMainArea > .downloadContainer > .downloadButtonLabels {
  display: none;
}

/* When hovering the main area of a finished download whose target exists,
   instead of the usual hover message we display the "Open File" command. */
richlistitem[type="download"][state="1"][exists] > .downloadMainArea:hover > .downloadContainer > .downloadDetailsHover,
richlistitem[type="download"]:not([state="1"]) > .downloadMainArea > .downloadContainer > .downloadOpenFile,
richlistitem[type="download"]:not([exists]) > .downloadMainArea > .downloadContainer > .downloadOpenFile,
.downloadMainArea:not(:hover) > .downloadContainer > .downloadOpenFile {
  display: none;
}

/* When hovering items blocked by Application Reputation, instead of the other
   hover messages we display the "Show more information" label. */
richlistitem[type="download"][verdict] > .downloadMainArea > .downloadContainer > .downloadDetailsHover,
richlistitem[type="download"][verdict] > .downloadMainArea > .downloadContainer > .downloadButtonLabels,
richlistitem[type="download"]:not([verdict]) > .downloadMainArea > .downloadContainer > .downloadShowMoreInfo,
richlistitem[type="download"]:not(:hover) > .downloadMainArea > .downloadContainer > .downloadShowMoreInfo {
  display: none;
}

richlistitem[type="download"][verdict] > toolbarseparator {
  visibility: hidden;
}

.downloadButton {
  -moz-appearance: none;
  min-width: 58px;
  margin: 0;
  border: none;
  background: transparent;
  padding: 0;
  color: inherit;
}

.downloadButton > .button-box > .button-icon {
  width: 16px;
  height: 16px;
  margin: 1px;
  -moz-context-properties: fill;
  fill: currentColor;
}

.downloadButton > .button-box > .button-text {
  margin: 0 !important;
  padding: 0;
}

richlistitem[type="download"][state="1"][exists] .downloadMainArea:hover,
richlistitem[type="download"]:not([verdict]) > .downloadButtonArea:hover,
richlistitem[type="download"][verdict]:hover {
  background-color: var(--arrowpanel-dimmed);
}

richlistitem[type="download"][state="1"][exists] > .downloadMainArea:hover:active,
richlistitem[type="download"]:not([verdict]) > .downloadButtonArea:hover:active,
richlistitem[type="download"][verdict]:hover:active {
  background-color: var(--arrowpanel-dimmed-further);
}

richlistitem[type="download"][verdict="Malware"]:hover,
richlistitem[type="download"][verdict="Malware"]:hover:active {
  background-color: #aa1b08;
  color: white;
}

/*** Button icons ***/

.downloadIconCancel > .button-box > .button-icon {
  list-style-image: url("chrome://browser/skin/panel-icon-cancel.svg");
}

.downloadIconShow > .button-box > .button-icon {
  list-style-image: url("chrome://browser/skin/panel-icon-folder.svg");
}

.downloadIconRetry > .button-box > .button-icon {
  list-style-image: url("chrome://browser/skin/panel-icon-retry.svg");
}

.downloadShowBlockedInfo > .button-box > .button-icon {
  list-style-image: url("chrome://browser/skin/panel-icon-arrow-right.svg");
}

.downloadShowBlockedInfo > .button-box > .button-icon:-moz-locale-dir(rtl) {
  list-style-image: url("chrome://browser/skin/panel-icon-arrow-left.svg");
}

/*** Blocked subview ***/

#downloadsPanel-blockedSubview > .panel-view-body-unscrollable {
  background-image: url("chrome://browser/skin/warning.svg");
  background-size: 32px 32px;
  background-position: 16px 16px;
  background-repeat: no-repeat;
}

#downloadsPanel-blockedSubview:-moz-locale-dir(rtl) {
  background-position: calc(100% - 16px) 16px;
}

#downloadsPanel-blockedSubview[verdict=Malware] > .panel-view-body-unscrollable {
  background-image: url("chrome://browser/skin/downloads/download-blocked.svg");
}

#downloadsPanel-blockedSubview-title {
  margin-top: 16px;
  margin-bottom: 16px;
  font-size: calc(100% / var(--downloads-item-font-size-factor));
}

#downloadsPanel-blockedSubview-details1,
#downloadsPanel-blockedSubview-details2 {
  font-size: calc(100% * var(--downloads-item-font-size-factor));
  margin-bottom: 16px;
  opacity: var(--downloads-item-details-opacity);
}

#downloadsPanel-blockedSubview-title,
#downloadsPanel-blockedSubview-details1,
#downloadsPanel-blockedSubview-details2 {
  -moz-margin-start: 64px;
  -moz-margin-end: 16px;
}

/*** Progressmeter ***/
/*** Common-styled progressmeter ***/
.downloadProgress {
  height: 8px;
  border-radius: 1px;
  margin: 4px 0 0;
  margin-inline-end: 12px;

  /* for overriding rules in progressmeter.css */
  -moz-appearance: none;
  border-style: none;
  background-color: transparent;
  min-width: initial;
  min-height: initial;
}

.downloadProgress[mode="undetermined"] {
  /* for overriding rules on global.css in Linux. */
  -moz-binding: url("chrome://global/content/bindings/progressmeter.xml#progressmeter");
}

.downloadProgress > .progress-bar {
  background-color: Highlight;

  /* for overriding rules in progressmeter.css */
  -moz-appearance: none;
}

.downloadProgress[paused="true"] > .progress-bar {
  background-color: GrayText;
}

.downloadProgress[mode="undetermined"] > .progress-bar {
  /* Make a white reflecting animation.
     Create a gradient with 2 identical pattern, and enlarge the size to 200%.
     This allows us to animate background-position with percentage. */
  background-image: linear-gradient(90deg, transparent 0%,
                                           rgba(255,255,255,0.5) 25%,
                                           transparent 50%,
                                           rgba(255,255,255,0.5) 75%,
                                           transparent 100%);
  background-blend-mode: lighten;
  background-size: 200% 100%;
  animation: downloadProgressSlideX 1.5s linear infinite;
}

.downloadProgress > .progress-remainder {
  border: solid ButtonShadow;
  border-block-start-width: 1px;
  border-block-end-width: 1px;
  border-inline-start-width: 0;
  border-inline-end-width: 1px;
  background-color: ButtonFace;
}

.downloadProgress[value="0"] > .progress-remainder {
  border-width: 1px;
}

.downloadProgress > .progress-remainder[mode="undetermined"] {
  border: none;
}

@keyframes downloadProgressSlideX {
  0% {
    background-position: 0 0;
  }
  100% {
    background-position: -100% 0;
  }
}

/*** List items and similar elements in the summary ***/

:root {
  --downloads-item-height: 5.5em;
  --downloads-item-font-size-factor: 0.9;
  --downloads-item-details-opacity: 0.6;
}

#downloadsPanel[keyfocus] #downloadsListBox:focus > richlistitem[type="download"][selected],
#downloadsPanel[keyfocus] #downloadsSummary:focus,
#downloadsPanel[keyfocus] .downloadsPanelFooterButton:focus,
.downloadButton:focus {
  outline: 1px -moz-dialogtext dotted;
  outline-offset: -1px;
}
PK
!<��	��8�8/chrome/browser/skin/classic/browser/browser.css
@import url("chrome://global/skin/");

@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
@namespace html url("http://www.w3.org/1999/xhtml");

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/*** Status and progress indicator ***/

#downloads-indicator-anchor {
  min-width: 16px;
  min-height: 16px;
}

#downloads-indicator-progress-outer {
  width: 16px;
  height: 16px;
  background-size: 16px;
  background: url("chrome://browser/skin/downloads/download-icons.svg#default-bar") center no-repeat;
}

#downloads-button[attention="success"] > #downloads-indicator-anchor > #downloads-indicator-icon,
#downloads-button[attention="success"] > #downloads-indicator-anchor > #downloads-indicator-progress-outer {
  -moz-context-properties: fill, fill-opacity;
  fill: var(--toolbarbutton-icon-fill-attention);
  fill-opacity: 1;
}
#downloads-button[progress] > #downloads-indicator-anchor > #downloads-indicator-progress-outer {
  background: url("chrome://browser/skin/downloads/download-icons.svg#progress-bar-bg") center no-repeat;
}

#downloads-indicator-icon {
  -moz-context-properties: fill, fill-opacity;
  background-image: url("chrome://browser/skin/downloads/download-icons.svg#arrow");
  width: 16px;
  height: 16px;
}

#downloads-indicator-progress-inner {
  background: url("chrome://browser/skin/downloads/download-icons.svg#progress-bar-fg") left no-repeat;
  margin-right: 16px;
  -moz-context-properties: fill;
  fill: var(--toolbarbutton-icon-fill-attention);
  background-size: 16px;
  /* From javascript side we use animation delay from 0s to -100s to show
   * corresponding frames needed for progress.
   * animation-delay is set to a positive value to make nothing shown.
   */
  animation-play-state: paused;
  animation-delay: 1s;
  animation-duration: 100s;
  animation-timing-function: linear;
  animation-name: indicatorArrowProgress;
}

#downloads-indicator-progress-inner:-moz-locale-dir(rtl) {
  background-position: right;
  animation-name: indicatorArrowProgressRTL;
  margin-left: 16px;
  margin-right: 0;
}

@keyframes indicatorArrowProgress {
  0% {
    margin-right: 15px;
  }
  100% {
    margin-right: 1px;
  }
}

@keyframes indicatorArrowProgressRTL {
  0% {
    margin-left: 15px;
  }
  100% {
    margin-left: 1px;
  }
}

/*** Status badges ***/

#downloads-button[attention="warning"] > .toolbarbutton-badge-stack > .toolbarbutton-badge,
#downloads-button[attention="severe"] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
  display: -moz-box;
  height: 8px;
  width: 8px;
  min-width: 0;
  border-radius: 50%;
  /* "!important" is necessary to override the rule in toolbarbutton.css */
  margin-top: -1px !important;
  margin-right: -2px !important;
}

#downloads-button[cui-areatype="toolbar"] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
  height: 7px;
  width: 7px;
}

#downloads-button[attention="severe"] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
  background: #D90000;
}

#downloads-button[attention="warning"] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
  background: #FFBF00;
}

#downloads-button[attention="severe"] > .toolbarbutton-badge-stack > .toolbarbutton-badge:-moz-window-inactive,
#downloads-button[attention="warning"] > .toolbarbutton-badge-stack > .toolbarbutton-badge:-moz-window-inactive {
  filter: none;
}

/*** Download notifications ***/

#downloads-button[notification="start"] > #downloads-indicator-anchor > #downloads-indicator-icon {
  animation-name: downloadsIndicatorStartDip;
  /* Upon changing the duration_delay below, please keep the delay time of
     setTimeout() identical in indicator.js for this animation.

     Timing here needs to align with the animation on #downloads-indicator-notification
  */
  animation-duration: 360ms;
  animation-delay: 400ms;
  animation-iteration-count: 1;
}

@keyframes downloadsIndicatorStartDip {
  0% {
    transform: translateY(0);
    animation-timing-function: linear;
  }
  50% {
    transform: translateY(0);
    animation-timing-function: ease-out;
  }
  88% {
    transform: translateY(3px);
    animation-timing-function: ease-out;
  }
  100% {
    transform: translateY(0);
  }
}

@keyframes downloadsIndicatorFinishPulse {
  from  { transform: scale(1); }
  37.5% { transform: scale(1.4); animation-timing-function: ease-out; }
  to    { transform: scale(1); animation-timing-function: ease-in; }
}

#downloads-button[notification="finish"] > #downloads-indicator-anchor > #downloads-indicator-icon {
  animation-name: downloadsIndicatorFinishPulse;
  animation-delay: 250ms;
  animation-duration: 300ms;
  animation-iteration-count: 2;
}

#downloads-animation-container {
  min-height: 1px;
  min-width: 1px;
  height: 1px;
  margin-bottom: -1px;
  /* Makes the outermost animation container element positioned, so that its
     contents are rendered over the main browser window in the Z order.
     This is required by the animated event notification. */
  position: relative;
  /* The selected tab may overlap #downloads-indicator-notification */
  z-index: 5;
}

/* download start animation */

#downloads-notification-anchor {
  width: 42px; /* Width of each frame within the SVG sprite */
  height: 98px; /* Height of each frame within the SVG sprite */
  overflow: hidden;
  /* animation is not directional and shouldn't be reversed in RTL */
  direction: ltr;
}

#downloads-indicator-notification {
  opacity: 0;
  min-width: 1344px;
  height: 98px; /* Height is equal to height of each frame in the SVG animation */
  -moz-context-properties: fill;
  fill: #737373;
}

@keyframes downloadsIndicatorNotificationStart {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(-1302px);
  }
}

#downloads-notification-anchor[notification="start"] > #downloads-indicator-notification {
  opacity: 1;
  background: url("chrome://browser/skin/downloads/notification-start-animation.svg") 0 center no-repeat;
  transform: translateX(0px);
  animation-name: downloadsIndicatorNotificationStart;
  animation-duration: 540ms;
  animation-delay: 64ms;
  animation-timing-function: steps(31);
}


:root {
  /* Note: Setting this to 0 (without px) breaks CSS calculations for OSX. */
  --space-above-tabbar: 0px;
}

:root[extradragspace][tabsintitlebar]:not([inFullscreen]) {
  --space-above-tabbar: 8px;
}

/* Toolbar / content area border */

#navigator-toolbox::after {
  content: "";
  display: -moz-box;
  border-bottom: 1px solid var(--toolbox-border-bottom-color);
}

:root[customizing] #navigator-toolbox::after {
  border-bottom-style: none;
}

/* Bookmark toolbar */

#navigator-toolbox > toolbar:not(#toolbar-menubar):not(#TabsToolbar):not(#nav-bar) {
  overflow: -moz-hidden-unscrollable;
  max-height: 4em;
  transition: min-height 170ms ease-out, max-height 170ms ease-out;
  padding: 0 6px 2px;
}

#navigator-toolbox > toolbar:not(#toolbar-menubar):not(#TabsToolbar):not(#nav-bar)[collapsed=true] {
  min-height: 0.1px;
  max-height: 0;
  transition: min-height 170ms ease-out, max-height 170ms ease-out, visibility 170ms linear;
}

#navigator-toolbox > toolbar[customizing]:not(#toolbar-menubar):not(#TabsToolbar):not(#nav-bar) {
  outline: 1px dashed;
  outline-offset: -3px;
  -moz-outline-radius: 2px;
}

#navigator-toolbox > toolbar[customizing]:not(#toolbar-menubar):not(#TabsToolbar):not(#nav-bar):empty {
  /* Avoid the toolbar having no height when there's no items in it */
  min-height: 22px;
}

/* Required for Library animation */

#navigator-toolbox {
  position: relative;
}

#library-animatable-box {
  display: none;
}

#library-animatable-box[animate] {
  display: -moz-box;
}

/* Back / Forward context menu */

.unified-nav-back,
.unified-nav-forward {
  -moz-context-properties: fill;
  fill: currentColor;
}

.unified-nav-back[_moz-menuactive]:-moz-locale-dir(ltr),
.unified-nav-forward[_moz-menuactive]:-moz-locale-dir(rtl) {
  list-style-image: url("chrome://browser/skin/back.svg") !important;
}

.unified-nav-forward[_moz-menuactive]:-moz-locale-dir(ltr),
.unified-nav-back[_moz-menuactive]:-moz-locale-dir(rtl) {
  list-style-image: url("chrome://browser/skin/forward.svg") !important;
}

/* Private browsing and accessibility indicators */

.accessibility-indicator,
.private-browsing-indicator {
  background-repeat: no-repeat;
  background-size: 100% auto;
  background-position: center;
  width: 24px;
  height: 24px;
  margin-left: 7px;
  margin-right: 7px;
  position: relative;
  /* Need to ensure this gets positioned on top of the position:relative #navigator-toolbox
   * in case the dark/light themes give that item a background. */
  z-index: 1;
}

.accessibility-indicator {
  background-image: url("chrome://browser/skin/accessibility.svg");
  -moz-user-focus: normal;
  /* Clear default button styling */
  -moz-appearance: none;
  margin-top: unset;
  margin-bottom: unset;
  min-width: unset;
  color: unset;
  text-shadow: unset;
}

.accessibility-indicator:-moz-any(:hover, :active, :focus, :-moz-focusring) {
  background-image: url("chrome://browser/skin/accessibility-active.svg");
  outline: 0;
}

.private-browsing-indicator {
  background-image: url("chrome://browser/skin/private-browsing.svg");
}

:root:not([accessibilitymode]) .accessibility-indicator,
:root:not([privatebrowsingmode=temporary]) .private-browsing-indicator {
  display: none;
}

/* End private browsing and accessibility indicators */

:root {
  --toolbar-non-lwt-bgcolor: -moz-dialog;
  --toolbar-non-lwt-textcolor: -moz-dialogtext;
  --toolbar-non-lwt-bgimage: linear-gradient(rgba(255,255,255,.15), rgba(255,255,255,.15));
  --toolbar-bgcolor: var(--toolbar-non-lwt-bgcolor);
  --toolbar-bgimage: var(--toolbar-non-lwt-bgimage);

  --toolbarbutton-border-radius: 4px;
  --toolbarbutton-vertical-text-padding: calc(var(--toolbarbutton-inner-padding) - 1px);
  --toolbarbutton-icon-fill-opacity: .85;

  --panel-separator-color: ThreeDShadow;
  --arrowpanel-dimmed: hsla(0,0%,80%,.3);
  --arrowpanel-dimmed-further: hsla(0,0%,80%,.45);
  --arrowpanel-dimmed-even-further: hsla(0,0%,80%,.8);

  --urlbar-separator-color: ThreeDShadow;

  --toolbox-border-bottom-color: ThreeDShadow;
}

#navigator-toolbox {
  --tabs-border-color: rgba(0,0,0,.3);
}

#tabbrowser-tabs {
  --tab-line-color: highlight;
}

:root:-moz-lwtheme {
  --toolbar-bgcolor: rgba(255,255,255,.4);
  --toolbar-bgimage: none;

  --toolbox-border-bottom-color: rgba(0,0,0,.3);
}

#menubar-items {
  -moz-box-orient: vertical; /* for flex hack */
}

#main-menubar {
  -moz-box-flex: 1; /* make menu items expand to fill toolbar height */
}

#navigator-toolbox {
  -moz-appearance: none;
  background-color: transparent;
  border-top: none;
}

#navigator-toolbox > toolbar {
  padding: 0;
}

#navigator-toolbox > toolbar:not(#toolbar-menubar):not(#TabsToolbar) {
  background-color: var(--toolbar-bgcolor);
  background-image: var(--toolbar-bgimage);
  color: var(--toolbar-color, inherit);
  -moz-appearance: none;
  border-style: none;
}

#TabsToolbar:not([collapsed="true"]) + #nav-bar {
  box-shadow: 0 -1px 0 var(--tabs-border-color);
  /* This is needed for some toolbar button animations. Gross :( */
  position: relative;
}

#browser-bottombox {
  /* opaque for layers optimization */
  background-color: -moz-Dialog;
}

/* Bookmark menus */
menu.bookmark-item,
menuitem.bookmark-item {
  min-width: 0;
  max-width: 32em;
}

.bookmark-item:not(.subviewbutton) > .menu-iconic-left {
  margin-top: 0;
  margin-bottom: 0;
}

.bookmark-item > .menu-iconic-left > .menu-iconic-icon {
  padding-inline-start: 0px;
}

/* Bookmark drag and drop styles */
.bookmark-item[dragover-into="true"] {
  background: Highlight !important;
  color: HighlightText !important;
}

/* rules for menupopup drop indicators */
.menupopup-drop-indicator-bar {
  position: relative;
  /* these two margins must together compensate the indicator's height */
  margin-top: -1px;
  margin-bottom: -1px;
}

.menupopup-drop-indicator {
  list-style-image: none;
  height: 2px;
  margin-inline-end: -4em;
  background-color: Highlight;
}

/* Bookmarks toolbar */
#PlacesToolbarDropIndicator {
  list-style-image: url(chrome://browser/skin/places/toolbarDropMarker.png);
}

.bookmark-item[cutting] > .toolbarbutton-icon,
.bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-icon {
  opacity: 0.5;
}

.bookmark-item[cutting] > .toolbarbutton-text,
.bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-text {
  opacity: 0.7;
}

/* ----- BOOKMARK STAR ANIMATION ----- */

@keyframes animation-bookmarkAdded {
  from { transform: rotate(0deg) translateX(-16px) rotate(0deg) scale(1); opacity: 0; }
  60%  { transform: rotate(180deg) translateX(-16px) rotate(-180deg) scale(2.2); opacity: 1; }
  80%  { opacity: 1; }
  to   { transform: rotate(180deg) translateX(-16px) rotate(-180deg) scale(1); opacity: 0; }
}

@keyframes animation-bookmarkPulse {
  from { transform: scale(1); }
  50%  { transform: scale(1.3); }
  to   { transform: scale(1); }
}

#bookmarked-notification-container {
  min-height: 1px;
  min-width: 1px;
  height: 1px;
  margin-bottom: -1px;
  z-index: 5;
  position: relative;
}

#bookmarked-notification {
  background-size: 16px;
  background-position: center;
  background-repeat: no-repeat;
  width: 16px;
  height: 16px;
  opacity: 0;
}

#bookmarked-notification-dropmarker-anchor {
  z-index: -1;
  position: relative;
}

#bookmarked-notification-dropmarker-icon {
  -moz-context-properties: fill;
  width: 18px;
  height: 18px;
  visibility: hidden;
}

#bookmarked-notification-anchor[notification="finish"] > #bookmarked-notification {
  background-image: url("chrome://browser/skin/places/bookmarks-notification-finish.png");
  animation: animation-bookmarkAdded 800ms;
  animation-timing-function: ease, ease, ease;
}

@media (min-resolution: 2dppx) {
  #bookmarked-notification-anchor[notification="finish"] > #bookmarked-notification {
    background-image: url("chrome://browser/skin/places/[email protected]");
  }
}

#bookmarks-menu-button[notification="finish"] > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon {
  fill: transparent;
}

#bookmarked-notification-dropmarker-anchor[notification="finish"] > #bookmarked-notification-dropmarker-icon {
  visibility: visible;
  animation: animation-bookmarkPulse 300ms;
  animation-delay: 600ms;
  animation-timing-function: ease-out;
}

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 {
  --toolbarbutton-hover-transition-duration: 150ms;

  --toolbarbutton-outer-padding: 2px;
  --toolbarbutton-inner-padding: 6px;

  /* These hover and active colors should work on both light and dark
     backgrounds. We'll later set colors that cater for light and dark
     backgrounds specifically when we can detect them. */
  --toolbarbutton-hover-background: var(--lwt-toolbarbutton-hover-background, hsla(0,0%,70%,.4));
  --toolbarbutton-active-background: var(--lwt-toolbarbutton-active-background, hsla(0,0%,70%,.6));

  --backbutton-background: hsla(0,100%,100%,.8);
  --backbutton-hover-background: var(--backbutton-background);
  --backbutton-active-background: var(--toolbarbutton-active-background);
  --backbutton-border-color: hsla(240,5%,5%,.3);

  /* This default value of --toolbarbutton-height is defined to prevent
     CSS errors for an invalid variable. The value should not get used,
     as a more specific value should be set when the value will be used. */
  --toolbarbutton-height: 0;
}

:root[uidensity=compact] {
  --toolbarbutton-outer-padding: 1px;
}

:root[uidensity=touch] {
  --toolbarbutton-inner-padding: 9px;
}

/* We use :-moz-lwtheme-* for toolbarbuttons that aren't inside a toolbar, and
   [brighttext] to cater for OS themes where :-moz-lwtheme-* doesn't apply. */
:root:-moz-lwtheme-darktext,
toolbar:not([brighttext]) {
  --toolbarbutton-hover-background: var(--lwt-toolbarbutton-hover-background, hsla(240,5%,5%,.1));
  --toolbarbutton-active-background: var(--lwt-toolbarbutton-active-background, hsla(240,5%,5%,.15));
}

:root:-moz-lwtheme-brighttext,
toolbar[brighttext] {
  --toolbarbutton-hover-background: var(--lwt-toolbarbutton-hover-background, hsla(0,0%,70%,.4));
  --toolbarbutton-active-background: var(--lwt-toolbarbutton-active-background, hsla(0,0%,70%,.6));

  --backbutton-background: var(--toolbarbutton-hover-background);
  --backbutton-hover-background: var(--toolbarbutton-active-background);
  --backbutton-active-background: hsla(0,0%,100%,.4);
}

/* ::::: primary toolbar buttons ::::: */

.tabbrowser-arrowscrollbox > .scrollbutton-up[disabled=true],
.tabbrowser-arrowscrollbox > .scrollbutton-down[disabled=true],
:root:not([customizing]) .toolbarbutton-1[disabled=true],
/* specialcase the overflow and the hamburger button so they show up disabled in customize mode. */
#nav-bar-overflow-button[disabled=true],
#PanelUI-menu-button[disabled=true] {
  opacity: 0.4;
}

.toolbarbutton-1 > .toolbarbutton-icon {
  margin-inline-end: 0;
}

.toolbarbutton-1 > .toolbarbutton-icon,
.toolbarbutton-1 > .toolbarbutton-badge-stack > .toolbarbutton-icon {
  width: 16px;
}

#TabsToolbar .toolbarbutton-1,
.tabbrowser-arrowscrollbox > .scrollbutton-up,
.tabbrowser-arrowscrollbox > .scrollbutton-down {
  margin: 0 0 1px;
}

.tabbrowser-arrowscrollbox > .scrollbutton-up,
.tabbrowser-arrowscrollbox > .scrollbutton-down {
  -moz-appearance: none;
  padding: 0 var(--toolbarbutton-inner-padding);
}

#navigator-toolbox:not(:hover) > #TabsToolbar > #tabbrowser-tabs > .tabbrowser-arrowscrollbox > .scrollbutton-down:not([highlight]) {
  transition: 1s background-color ease-out;
}

.tabbrowser-arrowscrollbox > .scrollbutton-down[highlight] {
  background-color: Highlight;
}

.findbar-button {
  -moz-appearance: none;
  padding: 0;
}

toolbar .toolbarbutton-1 {
  -moz-appearance: none;
  margin: 0;
  padding: 0 var(--toolbarbutton-outer-padding);
  -moz-box-pack: center;
}

:root:not([uidensity=compact]) #PanelUI-menu-button {
  padding-inline-start: 5px;
  padding-inline-end: 5px;
}

toolbar .toolbarbutton-1 > menupopup {
  margin-top: -3px;
}

.findbar-button > .toolbarbutton-text,
toolbarbutton.bookmark-item:not(.subviewbutton),
toolbar .toolbarbutton-1 > .toolbarbutton-icon,
toolbar .toolbarbutton-1 > .toolbarbutton-text,
toolbar .toolbarbutton-1 > .toolbarbutton-badge-stack {
  padding: var(--toolbarbutton-inner-padding);
  border-radius: var(--toolbarbutton-border-radius);
}

toolbar .toolbarbutton-1 > .toolbarbutton-icon {
  /* horizontal padding + actual icon width */
  width: calc(2 * var(--toolbarbutton-inner-padding) + 16px);
  height: calc(2 * var(--toolbarbutton-inner-padding) + 16px);
}

.bookmark-item > .toolbarbutton-menu-dropmarker,
toolbar .toolbarbutton-1 > .toolbarbutton-menu-dropmarker {
  display: none;
}

toolbar .toolbarbutton-1 > .toolbarbutton-text {
  padding-top: var(--toolbarbutton-vertical-text-padding);
  padding-bottom: 0;
  /* To make the hover feedback line up with sibling buttons, it needs the same
   * height as the button icons and the same vertical padding, but as a minimum,
   * because otherwise an increase in text sizes would break things.
   */
  min-height: calc(16px + 2 * var(--toolbarbutton-inner-padding));
}

toolbar .toolbaritem-combined-buttons {
  margin-left: 2px;
  margin-right: 2px;
}

toolbar .toolbaritem-combined-buttons > .toolbarbutton-1 {
  padding-left: 0;
  padding-right: 0;
}

toolbar .toolbaritem-combined-buttons:not(:hover) > separator {
  content: "";
  display: -moz-box;
  width: 1px;
  height: 16px;
  margin-inline-end: -1px;
  background-image: linear-gradient(currentColor 0, currentColor 100%);
  background-position: center;
  background-repeat: no-repeat;
  background-size: 1px 16px;
  opacity: .2;
}

toolbar[brighttext] .toolbaritem-combined-buttons > separator {
  opacity: .3;
}

#PersonalToolbar .toolbarbutton-1:not([disabled=true]):not([checked]):not([open]):not(:active):hover,
.tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled=true]):hover,
.tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled=true]):hover,
.findbar-button:not(:-moz-any([checked="true"],[disabled="true"])):hover > .toolbarbutton-text,
toolbarbutton.bookmark-item:not(.subviewbutton):hover:not([disabled="true"]):not([open]),
toolbar .toolbarbutton-1:not([disabled=true]):not([checked]):not([open]):not(:active):hover > .toolbarbutton-icon,
toolbar .toolbarbutton-1:not([disabled=true]):not([checked]):not([open]):not(:active):hover > .toolbarbutton-text,
toolbar .toolbarbutton-1:not([disabled=true]):not([checked]):not([open]):not(:active):hover > .toolbarbutton-badge-stack {
  background-color: var(--toolbarbutton-hover-background);
  color: inherit;
}

#PersonalToolbar .toolbarbutton-1:not([disabled=true]):-moz-any([open],[checked],:hover:active),
.findbar-button:not([disabled=true]):-moz-any([checked="true"],:hover:active) > .toolbarbutton-text,
toolbarbutton.bookmark-item:not(.subviewbutton):hover:active:not([disabled="true"]),
toolbarbutton.bookmark-item[open="true"],
toolbar .toolbarbutton-1:not([disabled=true]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-icon,
toolbar .toolbarbutton-1:not([disabled=true]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-text,
toolbar .toolbarbutton-1:not([disabled=true]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-badge-stack {
  background-color: var(--toolbarbutton-active-background);
  color: inherit;
}

toolbar .toolbarbutton-1[checked]:not(:active):hover > .toolbarbutton-icon {
  background-color: var(--toolbarbutton-hover-background);
  transition: background-color .4s;
}

:root:not([uidensity=compact]) #back-button {
  padding-top: 3px;
  padding-bottom: 3px;
  padding-inline-start: 3px;
  padding-inline-end: 0 !important;
  position: relative !important;
  z-index: 1 !important;
  border-radius: 0 10000px 10000px 0;
}

:root:not([uidensity=compact]) #back-button:-moz-locale-dir(rtl) {
  border-radius: 10000px 0 0 10000px;
}

#back-button > menupopup {
  margin-top: -1px !important;
}

:root:not([uidensity=compact]) #back-button > .toolbarbutton-icon {
  background-color: var(--backbutton-background);
  background-origin: padding-box;
  background-clip: padding-box;
  border: 1px solid var(--backbutton-border-color);
  border-radius: 10000px;
  width: 34px;
  height: 34px;
  padding: 8px;
  transition-property: box-shadow;
  transition-duration: var(--toolbarbutton-hover-transition-duration);
}

:root[uidensity=touch] #back-button {
  padding-top: 1px;
  padding-bottom: 1px;
  padding-inline-start: 1px;
}

:root[uidensity=touch] #back-button > .toolbarbutton-icon {
  width: 38px;
  height: 38px;
  padding: 10px;
}

:root:not([uidensity=compact]) #back-button:not([disabled]):not([open]):hover > .toolbarbutton-icon {
  background-color: var(--backbutton-hover-background) !important;
  box-shadow: 0 1px 6px hsla(0,0%,0%,.1);
  border-color: hsla(240,5%,5%,.35);
}

:root:not([uidensity=compact]) #back-button[open] > .toolbarbutton-icon,
:root:not([uidensity=compact]) #back-button:not([disabled]):hover:active > .toolbarbutton-icon {
  background-color: var(--backbutton-active-background) !important;
  border-color: hsla(240,5%,5%,.40);
}

/* bookmarks menu button */

#BMB_bookmarksPopup[side="top"],
#BMB_bookmarksPopup[side="bottom"] {
  margin-left: -20px;
  margin-right: -20px;
}

#BMB_bookmarksPopup[side="left"],
#BMB_bookmarksPopup[side="right"] {
  margin-top: -20px;
  margin-bottom: -20px;
}

/* ::::: bookmark buttons ::::: */

toolbarbutton.bookmark-item:not(.subviewbutton) {
  margin: 0 2px;
  padding: 2px 4px;
  -moz-appearance: none;
}

:root[uidensity=compact] toolbarbutton.bookmark-item:not(.subviewbutton) {
  margin: 0 1px;
  padding: 1px 4px;
}

:root[uidensity=touch] toolbarbutton.bookmark-item:not(.subviewbutton) {
  padding: 4px;
}

toolbarbutton.bookmark-item {
  max-width: 13em;
}

/* Not in a :not(...) clause in the rule above to avoid unduly increasing
 * that rule's specificity. */
#bookmarks-toolbar-placeholder {
  max-width: unset;
}

#PersonalToolbar .toolbarbutton-1 > .toolbarbutton-icon,
#PersonalToolbar .toolbarbutton-1 > .toolbarbutton-badge-stack,
.bookmark-item > .toolbarbutton-icon {
  width: 16px;
  height: 16px;
}

/* Force the display of the label for bookmarks */
.bookmark-item > .toolbarbutton-text {
  display: -moz-box !important;
}

#PlacesToolbarItems > .bookmark-item > .toolbarbutton-icon {
  margin-inline-end: 0;
}

#PlacesToolbarItems > .bookmark-item > .toolbarbutton-icon[label]:not([label=""]) {
  margin-inline-end: 4px;
}

/* The bookmarks toolbar is smaller than the other toolbars, so we
 * need to override the badge position to not be cut off. */
#PersonalToolbar .toolbarbutton-badge {
  margin-top: -1px !important;
}

:root[uidensity=touch] #PersonalToolbar .toolbarbutton-badge {
  margin-top: -4px !important;
}

/* Remove a pixel of margin on the end so that the badge doesn't
 * overflow the toolbar and push the button into the overflow menu. */
:root[uidensity=compact] .toolbarbutton-badge {
  margin-inline-end: -7px !important;
}

/* To allow toolbarbuttons in the bookmarks toolbar to grow in
 * height with the toolbar (like bookmark items), we apply background
 * and padding to the buttons, not the button contents. This rule
 * overrides attributes that would otherwise be duplicated. */
#PersonalToolbar .toolbarbutton-1 > .toolbarbutton-icon,
#PersonalToolbar .toolbarbutton-1 > .toolbarbutton-text,
#PersonalToolbar .toolbarbutton-1 > .toolbarbutton-badge-stack {
  padding: 0 !important;
  background: none !important;
  min-height: 16px;
}

#PersonalToolbar .toolbarbutton-1 {
  padding: 1px var(--toolbarbutton-inner-padding);
  border-radius: var(--toolbarbutton-border-radius);
}
:root {
  --toolbarbutton-icon-fill-attention: var(--lwt-toolbarbutton-icon-fill-attention, #0a84ff);
}

:root:-moz-lwtheme {
  --toolbarbutton-icon-fill-opacity: 1;
}

toolbar[brighttext] {
  --toolbarbutton-icon-fill-attention: var(--lwt-toolbarbutton-icon-fill-attention, #45a1ff);
}

.toolbarbutton-animatable-box,
.toolbarbutton-1 {
  color: inherit;
  -moz-context-properties: fill, fill-opacity;
  fill: var(--lwt-toolbarbutton-icon-fill, currentColor);
  fill-opacity: var(--toolbarbutton-icon-fill-opacity);
}

#back-button:-moz-locale-dir(rtl) > .toolbarbutton-icon,
#forward-button:-moz-locale-dir(rtl) > .toolbarbutton-icon,
#reload-button:-moz-locale-dir(rtl) > .toolbarbutton-icon,
#nav-bar-overflow-button:-moz-locale-dir(rtl) > .toolbarbutton-icon,
#PlacesChevron:-moz-locale-dir(rtl) > .toolbarbutton-icon,
#panic-button:-moz-locale-dir(rtl) > .toolbarbutton-icon,
#PanelUI-menu-button:-moz-locale-dir(rtl) > .toolbarbutton-badge-stack > .toolbarbutton-icon {
  transform: scaleX(-1);
}

#back-button {
  list-style-image: url("chrome://browser/skin/back.svg");
}

#forward-button {
  list-style-image: url("chrome://browser/skin/forward.svg");
}

.toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
  animation-fill-mode: forwards;
  animation-iteration-count: 1;
  list-style-image: none;
}

/* The animations for the reload-button and stop-button are disabled
   outside of the nav-bar due to bug 1382894. */
:-moz-any(#reload-button, #stop-button) > .toolbarbutton-animatable-box {
  display: none;
}
#nav-bar-customization-target > #stop-reload-button > :-moz-any(#reload-button, #stop-button) > .toolbarbutton-animatable-box {
  display: -moz-box;
}

#stop-reload-button[animate] > #reload-button > .toolbarbutton-icon,
#stop-reload-button[animate] > #reload-button[displaystop] + #stop-button > .toolbarbutton-icon {
  fill: transparent;
}

@keyframes reload-to-stop {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(-450px);
  }
}

@keyframes reload-to-stop-rtl {
  from {
    transform: scaleX(-1) translateX(0);
  }
  to {
    transform: scaleX(-1) translateX(-450px);
  }
}

@keyframes stop-to-reload {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(-450px);
  }
}

@keyframes stop-to-reload-rtl {
  from {
    transform: scaleX(-1) translateX(0);
  }
  to {
    transform: scaleX(-1) translateX(-450px);
  }
}

#stop-reload-button[animate] > #reload-button,
#stop-reload-button[animate] > #stop-button {
  position: relative;
}

#reload-button > .toolbarbutton-animatable-box,
#stop-button > .toolbarbutton-animatable-box {
  position: absolute;
  overflow: hidden;
  top: calc(50% - 10px); /* Vertically center the 20px tall animatable image */
  /* Since .toolbarbutton-icon uses a different width than the animatable-box,
     we need to set a padding relative to the difference in widths. */
  margin-inline-start: calc((16px + 2 * var(--toolbarbutton-inner-padding) - 18px) / 2);
  width: 18px; /* Width of each frame within the SVG sprite */
  height: 20px; /* Height of each frame within the SVG sprite */
}

#reload-button > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image,
#stop-button > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
  height: var(--toolbarbutton-height); /* Height must be equal to height of toolbarbutton padding-box */
}

#stop-reload-button[animate] > #reload-button > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
  background-image: url("chrome://browser/skin/reload-to-stop.svg");
  width: 468px;
}

#stop-reload-button[animate] > #reload-button:not([displaystop]) > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
  animation-name: reload-to-stop;
}

#stop-reload-button[animate] > #reload-button:not([displaystop]):-moz-locale-dir(rtl) > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
  animation-name: reload-to-stop-rtl;
}

#reload-button:not([displaystop]) > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
  animation-timing-function: steps(25);
  animation-duration: 417ms;
}

#stop-reload-button[animate] > #reload-button[displaystop] + #stop-button > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
  background-image: url("chrome://browser/skin/stop-to-reload.svg");
  width: 468px;
}

#stop-reload-button[animate] > #reload-button[displaystop] + #stop-button > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
  animation-name: stop-to-reload;
}

#stop-reload-button[animate] > #reload-button[displaystop] + #stop-button:-moz-locale-dir(rtl) > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
  animation-name: stop-to-reload-rtl;
}

#reload-button[displaystop] + #stop-button > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
  animation-timing-function: steps(25);
  animation-duration: 417ms;
}

#reload-button > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
  transform: translateX(-450px);
}

#reload-button:-moz-locale-dir(rtl) > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
  transform: scaleX(-1) translateX(-450px);
}

#reload-button[displaystop] + #stop-button > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
  transform: translateX(-450px);
}

#reload-button[displaystop] + #stop-button:-moz-locale-dir(rtl) > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
  transform: scaleX(-1) translateX(-450px);
}

#reload-button {
  list-style-image: url("chrome://browser/skin/reload.svg");
}

#stop-button {
  list-style-image: url("chrome://browser/skin/stop.svg");
}

#home-button {
  list-style-image: url("chrome://browser/skin/home.svg");
}

#bookmarks-toolbar-button,
#bookmarks-toolbar-placeholder {
  list-style-image: url("chrome://browser/skin/bookmarks-toolbar.svg");
}

#bookmarks-menu-button {
  list-style-image: url("chrome://browser/skin/bookmark-star-on-tray.svg");
}

#history-panelmenu {
  list-style-image: url("chrome://browser/skin/history.svg");
}

#downloads-button {
  list-style-image: url("chrome://browser/skin/downloads/download-icons.svg#arrow-with-bar");
}

#add-ons-button {
  list-style-image: url("chrome://mozapps/skin/extensions/extensionGeneric-16.svg");
}

#open-file-button {
  list-style-image: url("chrome://browser/skin/open.svg");
}

#save-page-button {
  list-style-image: url("chrome://browser/skin/save.svg");
}

#sync-button {
  list-style-image: url("chrome://browser/skin/tab.svg");
}

#feed-button {
  list-style-image: url("chrome://browser/skin/feed.svg");
}

#characterencoding-button {
  list-style-image: url("chrome://browser/skin/characterEncoding.svg");
}

#new-window-button {
  list-style-image: url("chrome://browser/skin/new-window.svg");
}

#new-tab-button {
  list-style-image: url("chrome://browser/skin/new-tab.svg");
}

#privatebrowsing-button {
  list-style-image: url("chrome://browser/skin/privateBrowsing.svg");
}

#find-button {
  list-style-image: url("chrome://browser/skin/search-glass.svg");
}

#print-button {
  list-style-image: url("chrome://browser/skin/print.svg");
}

#fullscreen-button {
  list-style-image: url("chrome://browser/skin/fullscreen.svg");
}

#developer-button {
  list-style-image: url("chrome://browser/skin/developer.svg");
}

#preferences-button {
  list-style-image: url("chrome://browser/skin/settings.svg");
}

#PanelUI-menu-button {
  list-style-image: url("chrome://browser/skin/menu.svg");
}

#PanelUI-menu-button[badge-status="update-available"],
#PanelUI-menu-button[badge-status="update-manual"],
#PanelUI-menu-button[badge-status="update-restart"] {
  list-style-image: url("chrome://browser/skin/menu-badged.svg");
}

#cut-button {
  list-style-image: url("chrome://browser/skin/edit-cut.svg");
}

#copy-button {
  list-style-image: url("chrome://browser/skin/edit-copy.svg");
}

#paste-button {
  list-style-image: url("chrome://browser/skin/edit-paste.svg");
}

#zoom-out-button {
  list-style-image: url("chrome://browser/skin/zoom-out.svg");
}

#zoom-in-button {
  list-style-image: url("chrome://browser/skin/zoom-in.svg");
}

#PlacesChevron,
#nav-bar-overflow-button {
  list-style-image: url("chrome://browser/skin/chevron.svg");
}

#nav-bar-overflow-button[animate] > .toolbarbutton-icon {
  fill: transparent;
}

@keyframes overflow-animation {
  from {
    transform: translateX(0);
  }
  50% {
    fill: var(--toolbarbutton-icon-fill-attention);
    fill-opacity: 1;
  }
  to {
    transform: translateX(-1260px);
    fill: var(--toolbarbutton-icon-fill-attention);
    fill-opacity: 1;
  }
}

@keyframes overflow-animation-rtl {
  from {
    transform: scaleX(-1) translateX(0);
  }
  50% {
    fill: var(--toolbarbutton-icon-fill-attention);
    fill-opacity: 1;
  }
  to {
    transform: scaleX(-1) translateX(-1260px);
    fill: var(--toolbarbutton-icon-fill-attention);
    fill-opacity: 1;
  }
}

/* The animation is supposed to show the blue fill color for 520ms, then the
   fade to the toolbarbutton-fill color for the remaining 210ms. Thus with an
   animation-duration of 730ms, 71% is the point where we start the fade out. */
@keyframes overflow-fade {
  from {
    fill: var(--toolbarbutton-icon-fill-attention);
    fill-opacity: 1;
  }
  71% {
    fill: var(--toolbarbutton-icon-fill-attention);
    fill-opacity: 1;
  }
  to {
    fill: inherit;
  }
}

#nav-bar-overflow-button > .toolbarbutton-animatable-box {
  position: absolute;
  overflow: hidden;
  top: calc(50% - 18px); /* Vertically center the 36px tall animatable image */
  /* Since .toolbarbutton-icon uses a different width than the animatable box,
     we need to set a padding relative to the difference in widths. */
  margin-inline-start: calc((16px + 2 * var(--toolbarbutton-inner-padding) - 18px) / 2);
  width: 18px; /* Width of each frame within the SVG sprite */
  height: 36px; /* Height of each frame within the SVG sprite */
}

#nav-bar-overflow-button > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
  /* Height must be equal to height of toolbarbutton padding-box. --toolbarbutton-height
     is calculated and set during runtime by BrowserUtils.setToolbarButtonHeightProperty()
     before the animation begins. */
  height: var(--toolbarbutton-height);
  min-height: 36px; /* Minimum height must be equal to the height of the SVG sprite */
}

#nav-bar-overflow-button[animate] > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
  animation-name: overflow-animation;
  animation-timing-function: steps(70);
  animation-duration: 1167ms;
  background-image: url("chrome://browser/skin/chevron-animation.svg");
  width: 1278px;
}

#nav-bar-overflow-button[animate]:-moz-locale-dir(rtl) > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
  animation-name: overflow-animation-rtl;
}

#nav-bar-overflow-button[animate][fade] > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
  animation-name: overflow-fade;
  animation-timing-function: ease-out;
  animation-duration: 730ms;
}

#nav-bar-overflow-button[animate][fade]:-moz-locale-dir(rtl) > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
  transform: scaleX(-1);
}

#email-link-button {
  list-style-image: url("chrome://browser/skin/mail.svg");
}

#sidebar-button {
  list-style-image: url("chrome://browser/skin/sidebars-right.svg");
}

#sidebar-button:-moz-locale-dir(ltr):not([positionend]),
#sidebar-button:-moz-locale-dir(rtl)[positionend] {
  list-style-image: url("chrome://browser/skin/sidebars.svg");
}

#panic-button {
  list-style-image: url("chrome://browser/skin/forget.svg");
}

#panic-button[open] {
  fill: rgb(213, 32, 20);
}

#webide-button {
  list-style-image: url("chrome://browser/skin/webIDE.svg");
}

#library-button {
  list-style-image: url("chrome://browser/skin/library.svg");
}

@keyframes library-bookmark-animation {
  from {
    transform: translateX(0);
    fill: inherit;
  }
  25% {
    fill: inherit;
  }
  50% {
    fill: var(--toolbarbutton-icon-fill-attention);
    fill-opacity: 1;
  }
  to {
    transform: translateX(-1056px);
    fill: var(--toolbarbutton-icon-fill-attention);
    fill-opacity: 1;
  }
}

@keyframes library-bookmark-animation-rtl {
  from {
    transform: translateX(1056px);
    fill: inherit;
  }
  25% {
    fill: inherit;
  }
  50% {
    fill: var(--toolbarbutton-icon-fill-attention);
    fill-opacity: 1;
  }
  to {
    transform: translateX(0);
    fill: var(--toolbarbutton-icon-fill-attention);
    fill-opacity: 1;
  }
}

@keyframes library-bookmark-fade {
  from {
    fill: var(--toolbarbutton-icon-fill-attention);
    fill-opacity: 1;
  }
  to {
    fill: inherit;
  }
}

#library-button[animate] > .toolbarbutton-icon {
  fill: transparent;
}

.toolbarbutton-animatable-box[animate] {
  position: absolute;
  overflow: hidden;
  /* Position the sprite at the y-position of the library-button, then adjust
     based on the size difference between half of the button height and half
     of the sprite height. The titlebar may have padding from -moz-appearance
     that needs to be accounted for. */
  top: calc(var(--library-button-y) + var(--library-button-height) / 2 - 27px - var(--toolbox-y));
  /* Set a margin relative to the difference in widths of the .toolbarbutton-icon
     and the .toolbar-animatable-box. This is correct even in RTL because the item
     is positioned using `left`. */
  left: calc(var(--library-icon-x) + (16px + 2 * var(--toolbarbutton-inner-padding) - 22px) / 2);
  width: 22px; /* Width of each frame within the SVG sprite */
  height: 54px; /* Height of each frame within the SVG sprite */
  z-index: 2;
}

.toolbarbutton-animatable-box[animate] > .toolbarbutton-animatable-image {
  height: var(--toolbarbutton-height);
  min-height: 54px; /* Minimum height must be equal to the height of the SVG sprite */
}

.toolbarbutton-animatable-box[animate="bookmark"] > .toolbarbutton-animatable-image {
  background-image: url("chrome://browser/skin/library-bookmark-animation.svg");
  width: 1078px;
  animation-name: library-bookmark-animation;
  animation-duration: 800ms;
  animation-timing-function: steps(48);
  -moz-context-properties: fill, fill-opacity, stroke;
  stroke: var(--toolbarbutton-icon-fill-attention);
}

.toolbarbutton-animatable-box[animate="bookmark"]:-moz-locale-dir(rtl) > .toolbarbutton-animatable-image {
  animation-name: library-bookmark-animation-rtl;
}

.toolbarbutton-animatable-box[animate="bookmark"][fade] > .toolbarbutton-animatable-image {
  animation-name: library-bookmark-fade;
  animation-duration: 2s;
  animation-timing-function: ease-out;
}

/* ----- BOOKMARK BUTTONS ----- */

.bookmark-item {
  list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.svg");
  -moz-context-properties: fill, fill-opacity;
  fill: var(--lwt-toolbarbutton-icon-fill, currentColor);
  fill-opacity: var(--toolbarbutton-icon-fill-opacity);
}

.bookmark-item[container] {
  list-style-image: url("chrome://browser/skin/places/folder.svg");
}

.bookmark-item[container][livemark] {
  list-style-image: url("chrome://browser/skin/places/folder-live.svg");
}

.bookmark-item[container][livemark] .bookmark-item {
  list-style-image: url("chrome://browser/skin/places/livemark-item.png");
  -moz-image-region: rect(0px, 16px, 16px, 0px);
}

.bookmark-item[container][livemark] .bookmark-item[visited] {
  -moz-image-region: rect(0px, 32px, 16px, 16px);
}

.bookmark-item[container][query] {
  list-style-image: url("chrome://browser/skin/places/folder-smart.svg");
}

.bookmark-item[query][tagContainer] {
  list-style-image: url("chrome://browser/skin/places/tag.png");
  -moz-image-region: auto;
}

.bookmark-item[query][dayContainer] {
  list-style-image: url("chrome://browser/skin/places/history.svg");
  -moz-image-region: auto;
}

.bookmark-item[query][hostContainer] {
  list-style-image: url("chrome://browser/skin/places/folder.svg");
}

/* Menu panel and palette styles */

#appMenu-new-window-button {
  list-style-image: url(chrome://browser/skin/new-window.svg);
}

#appMenu-private-window-button {
  list-style-image: url(chrome://browser/skin/privateBrowsing.svg);
}

#appMenu-print-button {
  list-style-image: url(chrome://browser/skin/print.svg);
}

#appMenu-library-button {
  list-style-image: url(chrome://browser/skin/library.svg);
}

#appMenu-addons-button {
  list-style-image: url(chrome://mozapps/skin/extensions/extensionGeneric-16.svg);
}

#appMenu-preferences-button {
  list-style-image: url(chrome://browser/skin/settings.svg);
}

#appMenu-customize-button {
  list-style-image: url(chrome://browser/skin/customize.svg);
}

#appMenu-find-button,
#panelMenu_searchBookmarks {
  list-style-image: url(chrome://browser/skin/search-glass.svg);
}

#appMenu-help-button {
  list-style-image: url(chrome://global/skin/icons/help.svg);
}

#appMenu-cut-button {
  list-style-image: url(chrome://browser/skin/edit-cut.svg);
}

#appMenu-copy-button {
  list-style-image: url(chrome://browser/skin/edit-copy.svg);
}

#appMenu-paste-button {
  list-style-image: url(chrome://browser/skin/edit-paste.svg);
}

#appMenu-quit-button {
  list-style-image: url(chrome://browser/skin/quit.svg);
}

#appMenu-zoomEnlarge-button {
  list-style-image: url(chrome://browser/skin/zoom-in.svg);
}

#appMenu-zoomReduce-button {
  list-style-image: url(chrome://browser/skin/zoom-out.svg);
}

#appMenu-fullscreen-button {
  list-style-image: url(chrome://browser/skin/fullscreen.svg);
}

#appMenu-fullscreen-button[checked] {
  list-style-image: url(chrome://browser/skin/fullscreen-exit.svg);
}

#appMenu-library-history-button {
  list-style-image: url(chrome://browser/skin/history.svg);
}

#appMenuRecentlyClosedTabs,
#appMenu-library-remotetabs-button {
  list-style-image: url("chrome://browser/skin/tab.svg");
}

#PanelUI-remotetabs-syncnow {
  list-style-image: url("chrome://browser/skin/sync.svg");
}

#PanelUI-remotetabs-view-managedevices {
  list-style-image: url("chrome://browser/skin/device-mobile.svg");
}

#appMenuViewHistorySidebar,
#PanelUI-remotetabs-view-sidebar,
#panelMenu_viewBookmarksSidebar {
  list-style-image: url("chrome://browser/skin/sidebars.svg");
}

#panelMenu_bookmarkingTools {
  list-style-image: url("chrome://browser/skin/developer.svg");
}

#panelMenu_viewBookmarksToolbar {
  list-style-image: url("chrome://browser/skin/bookmarks-toolbar.svg");
}

#appMenu-library-bookmarks-button,
#panelMenuBookmarkThisPage {
  list-style-image: url("chrome://browser/skin/bookmark-hollow.svg");
}

#panelMenuBookmarkThisPage[starred] {
  list-style-image: url("chrome://browser/skin/bookmark.svg");
}

#bookmarks-menu-button[cui-areatype="menu-panel"],
toolbarpaletteitem[place="palette"] > #bookmarks-menu-button,
#panelMenu_toggleBookmarksMenu {
  list-style-image: url("chrome://browser/skin/bookmark-star-on-tray.svg");
}

#appMenuClearRecentHistory {
  list-style-image: url("chrome://browser/skin/forget.svg");
}

#appMenuRestoreLastSession {
  list-style-image: url("chrome://browser/skin/restore-session.svg");
}

#appMenuRecentlyClosedWindows {
  list-style-image: url(chrome://browser/skin/window.svg);
}

#appMenu-library-downloads-button {
  list-style-image: url("chrome://browser/skin/downloads/download-icons.svg#arrow-with-bar");
}

#appMenu-library-downloads-show-button {
  list-style-image: url("chrome://browser/skin/folder.svg");
}

/* Fullscreen window controls */
#window-controls {
  -moz-box-align: start;
  margin-inline-start: 10px;
}

#minimize-button {
  list-style-image: url("chrome://global/skin/icons/Minimize.gif");
}
#restore-button {
  list-style-image: url("chrome://global/skin/icons/Restore.gif");
}
#close-button {
  list-style-image: url("chrome://global/skin/icons/Close.gif");
}

/* Location bar */



:root {
  /* 28x28 box - 16x16 image = 12x12 padding, 6 on each side */
  --urlbar-icon-padding: 6px;
}

:root[uidensity=compact] {
  /* 24x24 box - 16x16 image = 8x8 padding, 4 on each side */
  --urlbar-icon-padding: 4px;
}

:root[uidensity=touch] {
  /* 30x30 box - 16x16 image = 14x14 padding, 7 on each side */
  --urlbar-icon-padding: 7px;
}

#urlbar,
.searchbar-textbox {
  -moz-appearance: none;
  background-clip: content-box;
  border: 1px solid hsla(240,5%,5%,.25);
  border-radius: var(--toolbarbutton-border-radius);
  box-shadow: 0 1px 4px rgba(0,0,0,.05);
  padding: 0;
  margin: 3px 5px;
  min-height: 30px;
  cursor: default;
  overflow: -moz-hidden-unscrollable;
}

#urlbar:hover,
.searchbar-textbox:hover {
  border-color: hsla(240,5%,5%,.35);
  box-shadow: 0 1px 6px rgba(0,0,0,.1);
}

#urlbar:-moz-lwtheme,
#navigator-toolbox .searchbar-textbox:-moz-lwtheme {
  background-color: var(--url-and-searchbar-background-color, hsla(0,0%,100%,.8));
  color: var(--url-and-searchbar-color, black);
}

#urlbar:not([focused="true"]):-moz-lwtheme,
#navigator-toolbox .searchbar-textbox:not([focused="true"]):-moz-lwtheme {
  border-color: var(--lwt-toolbar-field-border-color, hsla(240,5%,5%,.25));
}

#urlbar:not([focused="true"]):-moz-lwtheme:hover,
#navigator-toolbox .searchbar-textbox:not([focused="true"]):-moz-lwtheme:hover {
  border-color: var(--lwt-toolbar-field-border-color, hsla(240,5%,5%,.35));
}

#urlbar:-moz-lwtheme:hover,
#urlbar:-moz-lwtheme[focused="true"],
#navigator-toolbox .searchbar-textbox:-moz-lwtheme:hover,
#navigator-toolbox .searchbar-textbox:-moz-lwtheme[focused="true"] {
  background-color: var(--url-and-searchbar-background-color, white);
}

:root[uidensity=compact] #urlbar,
:root[uidensity=compact] .searchbar-textbox {
  min-height: 26px;
  margin-top: 3px;
  margin-bottom: 3px;
}

:root[uidensity=touch] #urlbar,
:root[uidensity=touch] .searchbar-textbox {
  min-height: 32px;
}

:root[chromehidden~="toolbar"] #urlbar {
  /* Remove excess space between the address bar and the menu button in popups. */
  margin-inline-end: 0;
}

:root[customizing] .urlbar-input-box {
  visibility: hidden;
}

#urlbar-container {
  -moz-box-align: center;
}

#urlbar-search-splitter {
  /* The splitter width should equal the location and search bars' combined
     neighboring margin and border width. */
  min-width: 12px;
  margin: 0 -6px;
  position: relative;
  border: none;
  background: transparent;
  -moz-appearance: none;
}

/* Page action panel */
.pageAction-panel-button > .toolbarbutton-icon {
  width: 16px;
  height: 16px;
}

#pageAction-panel-bookmark,
#star-button {
  list-style-image: url("chrome://browser/skin/bookmark-hollow.svg");
}
#pageAction-panel-bookmark[starred],
#star-button[starred] {
  list-style-image: url("chrome://browser/skin/bookmark.svg");
}
#star-button[starred] {
  fill-opacity: 1;
  fill: var(--toolbarbutton-icon-fill-attention);
}

#pageAction-panel-copyURL,
#pageAction-urlbar-copyURL {
  list-style-image: url("chrome://browser/skin/link.svg");
}

#pageAction-panel-emailLink,
#pageAction-urlbar-emailLink {
  list-style-image: url("chrome://browser/skin/mail.svg");
}

#pageAction-panel-sendToDevice,
#pageAction-urlbar-sendToDevice {
  list-style-image: url("chrome://browser/skin/send-to-device.svg");
}

#pageAction-panel-sendToDevice:-moz-locale-dir(rtl) > .toolbarbutton-icon,
#pageAction-urlbar-sendToDevice:-moz-locale-dir(rtl) {
  transform: scaleX(-1);
}

.pageAction-sendToDevice-device[clientType=mobile] {
  list-style-image: url("chrome://browser/skin/device-mobile.svg");
}

.pageAction-sendToDevice-device[clientType=tablet] {
  list-style-image: url("chrome://browser/skin/device-tablet.svg");
}

.pageAction-sendToDevice-device[clientType=desktop] {
  list-style-image: url("chrome://browser/skin/device-desktop.svg");
}

.pageAction-sendToDevice-device.signintosync,
#pageAction-panel-sendToDevice-fxa,
#pageAction-urlbar-sendToDevice-fxa {
  list-style-image: url("chrome://browser/skin/sync.svg");
}

/* URL bar and page action buttons */

#page-action-buttons {
  -moz-box-align: center;
}

#pageActionSeparator {
  /* This draws the separator the same way that #urlbar-display-box draws its
     left and right borders, which end up looking like separators.  It might not
     be the best way in this case, but it makes sure that all these vertical
     lines in the urlbar look the same: same height, vertical position, etc. */
  border-inline-start: 1px solid var(--urlbar-separator-color);
  border-image: linear-gradient(transparent 15%, var(--urlbar-separator-color) 15%, var(--urlbar-separator-color) 85%, transparent 85%);
  border-image-slice: 1;
  width: 1px;
  height: 28px;
  visibility: hidden;
}

:root[uidensity=compact] #pageActionSeparator {
  height: 24px;
}

:root[uidensity=touch] #pageActionSeparator {
  height: 30px;
}

:not(#pageActionSeparator):not([hidden]) ~ #pageActionSeparator {
  /* Show the separator between the page actions and other elements when at
     least of the latter is shown. */
  visibility: visible;
  margin-left: 6px;
  margin-right: 6px;
}

#userContext-icons,
#urlbar-zoom-button {
  margin-left: 6px;
  margin-right: 6px;
}

.urlbar-icon {
  width: 28px;
  height: 28px;
  padding: var(--urlbar-icon-padding);
  -moz-context-properties: fill, fill-opacity;
  fill: currentColor;
  fill-opacity: 0.6;
  color: inherit;
}

.urlbar-page-action[disabled] {
  fill-opacity: 0.3;
}

:root[uidensity=compact] .urlbar-icon {
  width: 24px;
  height: 24px;
}

:root[uidensity=touch] .urlbar-icon {
  width: 30px;
  height: 30px;
}

.urlbar-icon:not([disabled]):hover,
.urlbar-icon-wrapper:not([disabled]):hover {
  background-color: hsla(0,0%,70%,.2);
}

.urlbar-icon[open],
.urlbar-icon-wrapper[open],
.urlbar-icon:not([disabled]):hover:active,
.urlbar-icon-wrapper:hover:active {
  background-color: hsla(0,0%,70%,.3);
}

.urlbar-icon-wrapper[open] > .urlbar-icon,
.urlbar-icon-wrapper > .urlbar-icon:hover,
.urlbar-icon-wrapper > .urlbar-icon:hover:active {
  background-color: transparent;
}

.urlbar-go-button,
.search-go-button {
  list-style-image: url("chrome://browser/skin/forward.svg");
}

.urlbar-go-button:-moz-locale-dir(rtl),
.search-go-button:-moz-locale-dir(rtl) {
  transform: scaleX(-1);
}

.urlbar-history-dropmarker {
  -moz-appearance: none;
  list-style-image: url(chrome://global/skin/icons/arrow-dropdown-16.svg);
  transition: opacity 0.15s ease;
}

/* Avoid re-opening the popup when the dropmarker is clicked while the popup is still open. */
.urlbar-history-dropmarker[open] {
  pointer-events: none;
}

#urlbar[switchingtabs] > .urlbar-textbox-container > .urlbar-history-dropmarker {
  transition: none;
}

#nav-bar:not([customizing="true"]) > #nav-bar-customization-target > #urlbar-container:not(:hover) > #urlbar:not([focused]) > .urlbar-textbox-container > .urlbar-history-dropmarker {
  opacity: 0;
}

#pageActionButton {
  list-style-image: url("chrome://browser/skin/page-action.svg");
}

@keyframes bookmark-animation {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(-627px);
  }
}

@keyframes bookmark-animation-rtl {
  from {
    transform: scaleX(-1) translateX(0);
  }
  to {
    transform: scaleX(-1) translateX(-627px);
  }
}

#star-button-box[animationsenabled] {
  position: relative;
}

/* Preload the bookmark animations to prevent a flicker during the first playing
   of the animations. */
#star-button[preloadanimations] + #star-button-animatable-box > #star-button-animatable-image {
  background-image: url("chrome://browser/skin/bookmark-animation.svg"),
                    url("chrome://browser/skin/library-bookmark-animation.svg");
  background-size: 0, 0;
}

#star-button-box[animationsenabled] > #star-button[starred][animate] {
  fill: transparent;
  position: relative;
}

#star-button-box[animationsenabled] > #star-button[starred][animate] + #star-button-animatable-box {
  position: absolute;
  overflow: hidden;
  top: calc(50% - 16.5px); /* 16.5px is half the height of the sprite */
  /* .urlbar-icon has width 28px. Each frame is 33px wide. Set margin-inline-start
     to be half the difference, -2.5px. */
  margin-inline-start: -2.5px;
  width: 33px; /* Width of each frame within the SVG sprite */
  height: 33px; /* Height of each frame within the SVG sprite */
}

:root[uidensity=compact] #star-button-box[animationsenabled] > #star-button[starred][animate] + #star-button-animatable-box {
  /* .urlbar-icon has width 24px in this case */
  margin-inline-start: -4.5px;
}

:root[uidensity=touch] #star-button-box[animationsenabled] > #star-button[starred][animate] + #star-button-animatable-box {
  /* .urlbar-icon has width 30px in this case */
  margin-inline-start: -1.5px;
}

#star-button-box[animationsenabled] > #star-button[starred][animate] + #star-button-animatable-box > #star-button-animatable-image {
  background-image: url("chrome://browser/skin/bookmark-animation.svg");
  background-size: auto;
  list-style-image: none;
  height: var(--toolbarbutton-height);
  min-height: 33px; /* Minimum height must be equal to the height of the SVG sprite */
  animation-name: bookmark-animation;
  animation-fill-mode: forwards;
  animation-iteration-count: 1;
  animation-timing-function: steps(19);
  animation-duration: 317ms;
  width: 660px;
  -moz-context-properties: fill, stroke;
  stroke: var(--toolbarbutton-icon-fill-attention);
}

#star-button-box[animationsenabled] > #star-button[starred][animate]:-moz-locale-dir(rtl) + #star-button-animatable-box > #star-button-animatable-image {
  animation-name: bookmark-animation-rtl;
}

/* Reader mode icon */

#reader-mode-button {
  list-style-image: url(chrome://browser/skin/readerMode.svg);
}

#reader-mode-button[readeractive] {
  fill: var(--toolbarbutton-icon-fill-attention);
  fill-opacity: 1;
}

/* Zoom button */

#urlbar-zoom-button {
  font-size: .8em;
  padding: 0 8px;
  border-radius: 1em;
  background-color: hsla(0,0%,0%,.05);
  border: 1px solid hsla(210,4%,50%,.3);
}

#urlbar-zoom-button[animate="true"] {
  animation-name: urlbar-zoom-reset-pulse;
  animation-duration: 250ms;
}

#urlbar-zoom-button:hover {
  background-color: hsla(0,0%,0%,.1);
}

#urlbar-zoom-button:hover:active {
  background-color: hsla(0,0%,0%,.15);
}

#urlbar-zoom-button > .toolbarbutton-text {
  display: -moz-box;
}

#urlbar-zoom-button > .toolbarbutton-icon {
  display: none;
}

/* Search bar */

.searchbar-search-button {
  -moz-box-align: center;
  fill: currentColor;
}

.searchbar-search-icon {
  list-style-image: url(chrome://browser/skin/search-glass.svg);
  -moz-context-properties: fill, fill-opacity;
  pointer-events: none;
  margin-inline-start: 8px;
  margin-inline-end: 6px;
  width: 16px;
  fill-opacity: .4;
}

.searchbar-search-button:hover > .searchbar-search-icon {
  fill-opacity: .6;
}

.searchbar-search-button:hover:active > .searchbar-search-icon {
  fill-opacity: .8;
}

.searchbar-search-icon-overlay {
  pointer-events: none;
}

.searchbar-search-button[addengines=true] > .searchbar-search-icon-overlay {
  list-style-image: url(chrome://browser/skin/search-indicator-badge-add.svg);
  margin-inline-start: -15px;
  margin-inline-end: 4px;
  margin-top: -10px;
  width: 11px;
  height: 11px;
}

.searchbar-search-button[addengines=true] > .searchbar-search-icon-overlay:-moz-locale-dir(rtl) {
  margin-inline-start: -25px;
  margin-inline-end: 14px;
}

.searchbar-search-button:hover:not([addengines=true]) > .searchbar-search-icon-overlay {
  list-style-image: url(chrome://global/skin/icons/arrow-dropdown-12.svg);
  -moz-context-properties: fill;
  margin-inline-start: -8px;
  margin-inline-end: 2px;
  width: 6px;
  height: 6px;
}

.searchbar-search-button:hover:not([addengines=true]) > .searchbar-search-icon-overlay:-moz-locale-dir(rtl) {
  margin-inline-start: -26px;
  margin-inline-end: 20px;
}

#urlbar:not(:-moz-lwtheme):not([focused="true"]),
.searchbar-textbox:not(:-moz-lwtheme):not([focused="true"]) {
  border-color: ThreeDShadow;
}

#urlbar[focused="true"],
.searchbar-textbox[focused="true"] {
  border-color: Highlight;
}

/* ::::: URL Bar Zoom Reset Button ::::: */
@keyframes urlbar-zoom-reset-pulse {
  0% {
    transform: scale(0);
  }
  75% {
    transform: scale(1.5);
  }
  100% {
    transform: scale(1.0);
  }
}

#urlbar-zoom-button {
  -moz-appearance: none;
  color: inherit;
}

#urlbar-search-footer {
  border-top: 1px solid var(--panel-separator-color);
  background-color: var(--arrowpanel-dimmed);
}

#urlbar-search-settings {
  -moz-appearance: none;
  -moz-user-focus: ignore;
  color: GrayText;
  margin: 0;
  border: 0;
  padding: 8px 20px;
  background: transparent;
}

#urlbar-search-settings:hover {
  background-color: var(--arrowpanel-dimmed);
}

#urlbar-search-settings:hover:active {
  background-color: var(--arrowpanel-dimmed-further);
}

#urlbar-display-box {
  margin-top: -1px;
  margin-bottom: -1px;
}

.urlbar-display {
  margin-top: 0;
  margin-bottom: 0;
  margin-inline-start: 0;
  color: GrayText;
}

#PopupAutoCompleteRichResult > deck[anonid="search-suggestions-notification"] {
  border-bottom: 1px solid var(--panel-separator-color);
  padding-inline-start: 0;
  padding-inline-end: 6px;
  min-height: 3em;
}

/* Limit the size of the hidden description, since a deck takes the size of the biggest child */
#PopupAutoCompleteRichResult > deck[anonid="search-suggestions-notification"][selectedIndex="0"] #search-suggestions-hint,
#PopupAutoCompleteRichResult > deck[anonid="search-suggestions-notification"][selectedIndex="1"] #search-suggestions-question {
  max-height: 5em;
}

/* Opt-out hint */

#PopupAutoCompleteRichResult > deck[anonid="search-suggestions-notification"] > hbox[anonid="search-suggestions-opt-out"] {
  font: message-box;
  display: flex;
  flex-direction: row;
  align-items: center;
  flex-wrap: nowrap;
}

#PopupAutoCompleteRichResult > deck[anonid="search-suggestions-notification"] hbox[anonid="search-suggestions-hint-box"] {
  flex-basis: 100%;
}

#PopupAutoCompleteRichResult > deck[anonid="search-suggestions-notification"] hbox[anonid="search-suggestions-hint-box"] > description {
  margin: auto;
  padding: 4px 8px;
  background-color: #ffeebe;
  border: 1px solid #ffdf81;
  border-radius: 4px;
  color: #7d3500;
}

#PopupAutoCompleteRichResult > deck[anonid="search-suggestions-notification"] hbox[anonid="search-suggestions-hint-box"] > description > html|span {
  unicode-bidi: embed;
}

#PopupAutoCompleteRichResult > deck[anonid="search-suggestions-notification"] hbox[anonid="search-suggestions-hint-box"] > description > html|span.prefix {
  font-weight: bold;
}

#PopupAutoCompleteRichResult > deck[anonid="search-suggestions-notification"][animate] > hbox[anonid="search-suggestions-opt-out"] > .ac-site-icon {
  transform: scale(0);
  animation-name: search-suggestions-hint-grow;
  animation-duration: 500ms;
  animation-delay: 500ms;
  animation-iteration-count: 1;
  animation-timing-function: ease-in-out;
  animation-fill-mode: forwards;
  min-width: 16px;
}

@keyframes search-suggestions-hint-grow {
  0%   { transform: scale(0); }
  40%  { transform: scale(1.5); }
  60%  { transform: scale(1); }
  80%  { transform: scale(1.25); }
  100% { transform: scale(1); }
}

#PopupAutoCompleteRichResult > deck[anonid="search-suggestions-notification"][animate] hbox[anonid="search-suggestions-hint-typing"] > .ac-title-text {
  text-overflow: clip;
}

#PopupAutoCompleteRichResult > deck[anonid="search-suggestions-notification"][animate] hbox[anonid="search-suggestions-hint-typing"] {
  overflow: hidden;
  max-width: 12ch;
  width: 0;
  animation-name: search-suggestions-hint-typing;
  animation-duration: 500ms;
  animation-delay: 750ms;
  animation-iteration-count: 1;
  animation-fill-mode: forwards;
}

@media all and (max-width: 800px) {
  /* Hide the typing animation block */
  #PopupAutoCompleteRichResult > deck[anonid="search-suggestions-notification"] hbox[anonid="search-suggestions-hint-typing"] {
    display: none;
  }
}

@keyframes search-suggestions-hint-typing {
  from { width: 0; }
  to   { width: 12ch; }
}

#PopupAutoCompleteRichResult > deck[anonid="search-suggestions-notification"][animate] hbox[anonid="search-suggestions-hint-box"] {
  opacity: 0;
  animation-duration: 250ms;
  animation-delay: 1500ms;
  animation-iteration-count: 1;
  animation-fill-mode: forwards;
}

/* Margin-inline-start can't be animated yet */
#PopupAutoCompleteRichResult > deck[anonid="search-suggestions-notification"][animate] hbox[anonid="search-suggestions-hint-box"]:-moz-locale-dir(ltr) {
  margin-left: 160px;
  animation-name: search-suggestions-hint-buildin-ltr;
}

@keyframes search-suggestions-hint-buildin-ltr {
  from  { margin-left: 160px; opacity: 0; }
  to    { margin-left: 0; opacity: 1; }
}

#PopupAutoCompleteRichResult > deck[anonid="search-suggestions-notification"][animate] hbox[anonid="search-suggestions-hint-box"]:-moz-locale-dir(rtl) {
  /* Should be margin-inline-start but that can't be animated yet */
  margin-right: 160px;
  animation-name: search-suggestions-hint-buildin-rtl;
}

@keyframes search-suggestions-hint-buildin-rtl {
  from  { margin-right: 160px; opacity: 0; }
  to    { margin-right: 0; opacity: 1; }
}

#search-suggestions-change-settings {
  opacity: 0;
  animation-name: search-suggestions-hint-fadein;
  animation-duration: 500ms;
  animation-delay: 1800ms;
  animation-iteration-count: 1;
  animation-fill-mode: forwards;
}

@keyframes search-suggestions-hint-fadein {
  from  { opacity: 0 }
  to    { opacity: 1 }
}

#search-container {
  min-width: calc(54px + 11ch);
}

/* identity box */

#identity-box:not(:active):-moz-focusring {
  outline: 1px dotted;
  outline-offset: -3px;
}


#identity-box {
  padding-inline-start: 8px;
  padding-inline-end: 4px;
  margin-inline-end: 4px;
  /* Set default fill for icons in the identity block.
     Individual icons can override this. */
  fill: currentColor;
  fill-opacity: .6;
  /* This is for tracking-protection-icon's slide-in animation. */
  overflow: hidden;
}

#identity-box:hover:not(.no-hover):not([open=true]) {
  background-color: hsla(0,0%,70%,.2);
  fill-opacity: .8;
}

#identity-box:hover:active:not(.no-hover),
#identity-box[open=true] {
  background-color: hsla(0,0%,70%,.3);
  fill-opacity: .8;
}

#urlbar[pageproxystate="valid"] > #identity-box.verifiedIdentity > #identity-icon-labels {
  color: #058B00;
}
#urlbar[pageproxystate="valid"] > #identity-box.chromeUI > #identity-icon-labels {
  color: rgb(229,115,0);
}

#identity-icon-labels:-moz-locale-dir(ltr) {
  padding-left: 4px;
}
#identity-icon-labels:-moz-locale-dir(rtl) {
  padding-right: 4px;
}
#identity-box:not(.chromeUI):not(.extensionPage):not(.notSecureText) {
  --urlbar-separator-color: transparent;
}
#urlbar[pageproxystate=valid] > #identity-box.verifiedIdentity {
  --urlbar-separator-color: rgba(18, 188, 0, .5);
}

#urlbar[pageproxystate=valid] > #identity-box.notSecureText,
#urlbar[pageproxystate=valid] > #identity-box.verifiedIdentity,
#urlbar[pageproxystate=valid] > #identity-box.chromeUI,
#urlbar[pageproxystate=valid] > #identity-box.extensionPage,
#urlbar-display-box {
  margin-inline-end: 8px;
  border-inline-end: 1px solid var(--urlbar-separator-color);
  border-image: linear-gradient(transparent 15%, var(--urlbar-separator-color) 15%, var(--urlbar-separator-color) 85%, transparent 85%);
  border-image-slice: 1;
}

#urlbar[pageproxystate=valid] > #identity-box.notSecureText,
#urlbar[pageproxystate=valid] > #identity-box.verifiedIdentity,
#urlbar[pageproxystate=valid] > #identity-box.chromeUI,
#urlbar[pageproxystate=valid] > #identity-box.extensionPage {
  padding-inline-end: 8px;
}

#urlbar-display-box {
  padding-inline-start: 4px;
  border-inline-start: 1px solid var(--urlbar-separator-color);
  border-image: linear-gradient(transparent 15%, var(--urlbar-separator-color) 15%, var(--urlbar-separator-color) 85%, transparent 85%);
  border-image-slice: 1;
}

#sharing-icon,
#identity-icon,
#tracking-protection-icon,
#connection-icon,
.notification-anchor-icon,
#blocked-permissions-container > .blocked-permission-icon,
#extension-icon {
  width: 16px;
  height: 16px;
  margin-inline-start: 4px;
  -moz-context-properties: fill, fill-opacity;
}

/* MAIN IDENTITY ICON */
#identity-icon {
  margin-inline-start: 0;
  list-style-image: url(chrome://browser/skin/identity-icon.svg);
}

#identity-box.grantedPermissions > #identity-icon {
  list-style-image: url(chrome://browser/skin/identity-icon-notice.svg);
}

#urlbar[pageproxystate="valid"] > #identity-box.chromeUI > #identity-icon {
  list-style-image: url(chrome://branding/content/identity-icons-brand.svg);
}

#urlbar[pageproxystate="invalid"] > #identity-box > #identity-icon {
  list-style-image: url(chrome://browser/skin/search-glass.svg);
  fill-opacity: .4;
}

#urlbar[actiontype="extension"] > #identity-box > #identity-icon {
  list-style-image: url(chrome://mozapps/skin/extensions/extensionGeneric-16.svg);
}

/* SHARING ICON */

#identity-box[sharing="camera"] > #sharing-icon {
  list-style-image: url("chrome://browser/skin/notification-icons/camera.svg");
}

#identity-box[sharing="microphone"] > #sharing-icon {
  list-style-image: url("chrome://browser/skin/notification-icons/microphone.svg");
}

#identity-box[sharing="screen"] > #sharing-icon {
  list-style-image: url("chrome://browser/skin/notification-icons/screen.svg");
}

#identity-box:not([sharing]) > #sharing-icon {
  display: none;
}

#identity-box[sharing]:not([paused]) > #sharing-icon {
  animation: 1.5s ease in-use-blink infinite;
  -moz-context-properties: fill;
  fill: rgb(224, 41, 29);
}

@keyframes in-use-blink {
  50% { opacity: 0; }
}

/* TRACKING PROTECTION ICON */

#tracking-protection-icon {
  list-style-image: url(chrome://browser/skin/tracking-protection-16.svg#enabled);
  margin-inline-end: 0;
}

#tracking-protection-icon[state="loaded-tracking-content"] {
  list-style-image: url(chrome://browser/skin/tracking-protection-16.svg#disabled);
}

#tracking-protection-icon[animate] {
  transition: margin-left 200ms ease-out, margin-right 200ms ease-out;
}

#tracking-protection-icon:not([state]) {
  margin-inline-end: -20px;
  pointer-events: none;
  opacity: 0;
  /* Only animate the shield in, when it disappears hide it immediately. */
  transition: none;
  list-style-image: none;
}

#urlbar[pageproxystate="invalid"] > #identity-box > #extension-icon,
#urlbar[pageproxystate="invalid"] > #identity-box > #tracking-protection-icon {
  visibility: collapse;
}

/* CONNECTION ICON, EXTENSION ICON */

#connection-icon,
#extension-icon {
  visibility: collapse;
}
#urlbar[pageproxystate="valid"] > #identity-box.verifiedDomain > #connection-icon,
#urlbar[pageproxystate="valid"] > #identity-box.verifiedIdentity > #connection-icon,
#urlbar[pageproxystate="valid"] > #identity-box.mixedActiveBlocked > #connection-icon {
  list-style-image: url(chrome://browser/skin/connection-secure.svg);
  visibility: visible;
  -moz-context-properties: fill;
  fill: #12BC00;
}
#urlbar[pageproxystate="valid"] > #identity-box.weakCipher > #connection-icon,
#urlbar[pageproxystate="valid"] > #identity-box.mixedDisplayContent > #connection-icon,
#urlbar[pageproxystate="valid"] > #identity-box.mixedDisplayContentLoadedActiveBlocked > #connection-icon,
#urlbar[pageproxystate="valid"] > #identity-box.certUserOverridden > #connection-icon {
  list-style-image: url(chrome://browser/skin/connection-mixed-passive-loaded.svg);
  visibility: visible;
  fill: unset;
  -moz-context-properties: fill, fill-opacity;
}

#urlbar[pageproxystate="valid"] > #identity-box.notSecure > #connection-icon,
#urlbar[pageproxystate="valid"] > #identity-box.insecureLoginForms > #connection-icon,
#urlbar[pageproxystate="valid"] > #identity-box.mixedActiveContent > #connection-icon {
  list-style-image: url(chrome://browser/skin/connection-mixed-active-loaded.svg);
  visibility: visible;
}

#identity-box.extensionPage > #extension-icon {
  list-style-image: url(chrome://mozapps/skin/extensions/extensionGeneric-16.svg);
  visibility: visible;
}

/* REMOTE CONTROL ICON */

#main-window[remotecontrol] #remote-control-icon {
  list-style-image: url(chrome://browser/content/static-robot.png);
  visibility: visible;
  width: 16px;
  height: 16px;
  margin-inline-start: 2px;
}


.popup-notification-icon,
.identity-popup-permission-icon {
  -moz-context-properties: fill, fill-opacity;
  fill: currentColor;
  fill-opacity: 0.6;
}

#notification-popup-box {
  padding: 5px 0px;
  margin: -5px 0px;
  margin-inline-end: -5px;
  padding-inline-end: 5px;
}

/* This class can be used alone or in combination with the class defining the
   type of icon displayed. This rule must be defined before the others in order
   for its list-style-image to be overridden. */
.notification-anchor-icon {
  list-style-image: url(chrome://browser/skin/notification-icons/default-info.svg);
}

/* INDIVIDUAL NOTIFICATIONS */

.focus-tab-by-prompt-icon {
  list-style-image: url(chrome://browser/skin/notification-icons/focus-tab-by-prompt.svg);
}

.popup-notification-icon[popupid="persistent-storage"],
.persistent-storage-icon {
  list-style-image: url(chrome://browser/skin/notification-icons/persistent-storage.svg);
}

.persistent-storage-icon.blocked-permission-icon {
  list-style-image: url(chrome://browser/skin/notification-icons/persistent-storage-blocked.svg);
}

.popup-notification-icon[popupid="web-notifications"],
.desktop-notification-icon {
  list-style-image: url(chrome://browser/skin/notification-icons/desktop-notification.svg);
}

.desktop-notification-icon.blocked-permission-icon {
  list-style-image: url(chrome://browser/skin/notification-icons/desktop-notification-blocked.svg);
}

.geo-icon {
  list-style-image: url(chrome://browser/skin/notification-icons/geo.svg);
}

.geo-icon.blocked-permission-icon {
  list-style-image: url(chrome://browser/skin/notification-icons/geo-blocked.svg);
}

.popup-notification-icon[popupid="geolocation"] {
  list-style-image: url(chrome://browser/skin/notification-icons/geo-detailed.svg);
}

.popup-notification-icon[popupid="indexedDB-permissions-prompt"],
.indexedDB-icon {
  list-style-image: url(chrome://browser/skin/notification-icons/indexedDB.svg);
}

.login-icon {
  list-style-image: url(chrome://browser/skin/notification-icons/login.svg);
}

.popup-notification-icon[popupid="password"] {
  list-style-image: url(chrome://browser/skin/notification-icons/login-detailed.svg);
}

.camera-icon {
  list-style-image: url(chrome://browser/skin/notification-icons/camera.svg);
}

.camera-icon.in-use {
  list-style-image: url(chrome://browser/skin/notification-icons/camera.svg);
}

.camera-icon.blocked-permission-icon {
  list-style-image: url(chrome://browser/skin/notification-icons/camera-blocked.svg);
}

.microphone-icon {
  list-style-image: url(chrome://browser/skin/notification-icons/microphone.svg);
}

.microphone-icon.in-use {
  list-style-image: url(chrome://browser/skin/notification-icons/microphone.svg);
}

.microphone-icon.blocked-permission-icon {
  list-style-image: url(chrome://browser/skin/notification-icons/microphone-blocked.svg);
}

.popup-notification-icon.microphone-icon {
  list-style-image: url(chrome://browser/skin/notification-icons/microphone-detailed.svg);
}

.screen-icon {
  list-style-image: url(chrome://browser/skin/notification-icons/screen.svg);
}

.screen-icon.in-use {
  list-style-image: url(chrome://browser/skin/notification-icons/screen.svg);
}

.screen-icon.blocked-permission-icon {
  list-style-image: url(chrome://browser/skin/notification-icons/screen-blocked.svg);
}

.midi-icon,
.midi-sysex-icon,
.popup-notification-icon[popupid="midi"],
.popup-notification-icon[popupid="midi-sysex"] {
  list-style-image: url(chrome://browser/skin/notification-icons/midi.svg);
}

#canvas-notification-icon,
.popup-notification-icon[popupid="canvas-permissions-prompt"],
.canvas-icon {
  list-style-image: url(chrome://browser/skin/notification-icons/canvas.svg);
}

.canvas-icon.blocked-permission-icon {
  list-style-image: url(chrome://browser/skin/notification-icons/canvas-blocked.svg);
}

#webauthn-notification-icon,
.popup-notification-icon[popupid^="webauthn-prompt-"] {
  list-style-image: url(chrome://browser/skin/notification-icons/webauthn.svg);
}

#webRTC-preview:not([hidden]) {
  display: -moz-stack;
  border-radius: 4px;
  border: 1px solid GrayText;
  overflow: hidden;
  min-width: 300px;
  min-height: 10em;
}

html|*#webRTC-previewVideo {
  width: 300px;
  /* If we don't set the min-width, width is ignored. */
  min-width: 300px;
  max-height: 200px;
}

#webRTC-previewWarning {
  background: rgba(255, 217, 99, .8) url("chrome://browser/skin/warning-white.svg") no-repeat .75em .75em;
  margin: 0;
  padding: .5em;
  padding-inline-start: calc(1.5em + 16px);
  border-top: 1px solid GrayText;
}

#webRTC-previewWarning > .text-link {
  margin-inline-start: 0;
}

/* This icon has a block sign in it, so we don't need a blocked version. */
.popup-icon {
  list-style-image: url("chrome://browser/skin/notification-icons/popup.svg");
}

.popup-subitem {
  list-style-image: url("chrome://browser/skin/notification-icons/popup-subitem.svg");
  margin-inline-start: 30px;
}

.identity-popup-popup-menulist {
  min-width: 6.5em;
}

/* EME */

.popup-notification-icon[popupid="drmContentPlaying"],
.drm-icon {
  list-style-image: url("chrome://browser/skin/drm-icon.svg");
}

#eme-notification-icon[firstplay=true] {
  animation: emeTeachingMoment 0.2s linear 0s 5 normal;
}

@keyframes emeTeachingMoment {
  0% {transform: translateX(0); }
  25% {transform: translateX(3px) }
  75% {transform: translateX(-3px) }
  100% { transform: translateX(0); }
}

/* INSTALL ADDONS */

.install-icon {
  list-style-image: url(chrome://mozapps/skin/extensions/extensionGeneric-16.svg);
}

.popup-notification-icon[popupid="xpinstall-disabled"],
.popup-notification-icon[popupid="addon-install-blocked"],
.popup-notification-icon[popupid="addon-install-origin-blocked"] {
  list-style-image: url(chrome://browser/skin/addons/addon-install-blocked.svg);
}

.popup-notification-icon[popupid="addon-progress"] {
  list-style-image: url(chrome://browser/skin/addons/addon-install-downloading.svg);
}

.popup-notification-icon[popupid="addon-install-failed"] {
  list-style-image: url(chrome://browser/skin/addons/addon-install-error.svg);
}

.popup-notification-icon[popupid="addon-install-confirmation"] {
  list-style-image: url(chrome://browser/skin/addons/addon-install-confirm.svg);
}

#addon-install-confirmation-notification[warning] .popup-notification-icon[popupid="addon-install-confirmation"] {
  list-style-image: url(chrome://browser/skin/addons/addon-install-warning.svg);
}

.popup-notification-icon[popupid="addon-install-complete"] {
  list-style-image: url(chrome://browser/skin/addons/addon-install-installed.svg);
}

.popup-notification-icon[popupid="addon-install-restart"] {
  list-style-image: url(chrome://browser/skin/addons/addon-install-restart.svg);
}
/* OFFLINE APPS */

.popup-notification-icon[popupid*="offline-app-requested"],
.popup-notification-icon[popupid="offline-app-usage"] {
  list-style-image: url(chrome://global/skin/icons/question-64.png);
}

/* PLUGINS */

.plugin-icon {
  list-style-image: url(chrome://mozapps/skin/plugins/plugin.svg);
  transition: fill 1.5s;
}

.plugin-icon.blocked-permission-icon {
  list-style-image: url(chrome://mozapps/skin/plugins/plugin-blocked.svg);
}

#plugin-icon-badge {
  list-style-image: url(chrome://browser/skin/notification-icons/plugin-badge.svg);
  opacity: 0;
  transition: opacity 1.5s;
}

#plugins-notification-icon[extraAttr="inactive"] > .plugin-icon {
  fill: #bfbfbf;
}

#plugins-notification-icon[extraAttr="inactive"] > #plugin-icon-badge {
  opacity: 1;
}

#plugins-notification-icon[extraAttr="inactive"] > #plugin-icon-badge[animate] {
  animation: blink-badge 1000ms ease 0s 5 alternate both;
}

@keyframes blink-badge {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

.plugin-blocked > .plugin-icon {
  list-style-image: url(chrome://mozapps/skin/plugins/plugin-blocked.svg);
  fill: #d92215 !important;
}

.plugin-blocked > #plugin-icon-badge {
  visibility: collapse;
}

#notification-popup-box[hidden] {
  /* Override display:none to make the pluginBlockedNotification animation work
     when showing the notification repeatedly. */
  display: -moz-box;
  visibility: collapse;
}

#plugins-notification-icon.plugin-blocked[showing] {
  animation: pluginBlockedNotification 500ms ease 0s 5 alternate both;
}

@keyframes pluginBlockedNotification {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

/* TRANSLATION */

.translation-icon {
  list-style-image: url(chrome://browser/skin/translation-16.png);
  -moz-image-region: rect(0px, 16px, 16px, 0px);
}

.translation-icon.in-use {
  -moz-image-region: rect(0px, 32px, 16px, 16px);
}


/* UPDATE */
.popup-notification-icon[popupid="update-available"],
.popup-notification-icon[popupid="update-manual"],
.popup-notification-icon[popupid="update-restart"] {
  background: #74BF43 url(chrome://browser/skin/notification-icons/update.svg) no-repeat center;
  border-radius: 50%;
}

.popup-notification-body[popupid="addon-progress"],
.popup-notification-body[popupid="addon-install-confirmation"] {
  width: 28em;
  max-width: 28em;
}

.addon-install-confirmation-name {
  font-weight: bold;
}

html|*.addon-webext-perm-list {
  margin-block-end: 0;
  padding-inline-start: 10px;
}

.addon-webext-perm-text {
  margin-inline-start: 0;
}

.popup-notification-description[popupid="addon-webext-permissions"],
.popup-notification-description[popupid="addon-webext-permissions-notification"] {
  margin-inline-start: -1px;
}

.addon-webext-perm-notification-content,
.addon-installed-notification-content {
  margin-top: 0;
}

.addon-webext-name {
  display: inline;
  font-weight: bold;
  margin: 0;
}

.addon-addon-icon,
.addon-toolbar-icon {
  width: 14px;
  height: 14px;
  vertical-align: bottom;
  margin-bottom: 1px;
  -moz-context-properties: fill;
  fill: currentColor;
}

.addon-addon-icon {
  list-style-image: url("chrome://mozapps/skin/extensions/extensionGeneric-16.svg");
}

.addon-toolbar-icon {
  list-style-image: url("chrome://browser/skin/menu.svg");
}

/* Notification icon box */

.notification-anchor-icon:-moz-focusring {
  outline: 1px dotted -moz-DialogText;
}

/* Translation infobar */

notification[value="translation"] .messageImage {
  list-style-image: url(chrome://browser/skin/translation-16.png);
  -moz-image-region: rect(0, 32px, 16px, 16px);
}

@media (min-resolution: 1.25dppx) {
  notification[value="translation"] .messageImage {
    list-style-image: url(chrome://browser/skin/[email protected]);
    -moz-image-region: rect(0, 64px, 32px, 32px);
  }
}

notification[value="translation"][state="translating"] .messageImage {
  list-style-image: url(chrome://browser/skin/translating-16.png);
  -moz-image-region: auto;
}

@media (min-resolution: 1.25dppx) {
  notification[value="translation"][state="translating"] .messageImage {
    list-style-image: url(chrome://browser/skin/[email protected]);
  }
}

notification[value="translation"] hbox[anonid="details"] {
  overflow: hidden;
}

notification[value="translation"] button,
notification[value="translation"] menulist {
  -moz-appearance: none;
  border-radius: 2px;
  min-width: 0;
  box-shadow: 0 1px rgba(255, 255, 255, 0.5), 0 1px rgba(255, 255, 255, 0.5) inset;
}

notification[value="translation"] menulist > .menulist-dropmarker {
  -moz-appearance: toolbarbutton-dropdown;
  border: none;
  background-color: transparent;
  margin: auto;
  padding: 5px 0;
}

.translation-menupopup arrowscrollbox {
  padding-bottom: 0;
}

.translation-attribution {
  cursor: pointer;
  -moz-box-align: end;
  font-size: small;
}

.translation-attribution > label {
  margin-bottom: 0;
}

.translation-attribution > image {
  width: 70px;
}

.translation-welcome-panel {
  width: 305px;
}

.translation-welcome-logo {
  height: 32px;
  width: 32px;
  list-style-image: url(chrome://browser/skin/[email protected]);
  -moz-image-region: rect(0, 64px, 32px, 32px);
}

.translation-welcome-content {
  margin-inline-start: 16px;
}

.translation-welcome-headline {
  font-size: larger;
  font-weight: bold;
}

.translation-welcome-body {
  padding: 1em 0;
  margin: 0 0;
}

notification[value="translation"] {
  min-height: 40px;
}

notification[value="translation"],
notification[value="translation"] button,
notification[value="translation"] menulist {
  min-height: 30px;
  color: #5A5959;
}

notification[value="translation"] {
  background-color: #F2F1F0;
}

notification[value="translation"] button,
notification[value="translation"] menulist {
  padding-inline-end: 1ch;
}

notification[value="translation"] menulist {
  border: 1px solid #C1C1C1;
  background-color: #FFF;
}

notification[value="translation"] button {
  border: 1px solid #C1C1C1;
  background-color: #F2F1F0;
}

notification[value="translation"] button,
notification[value="translation"] menulist,
notification[value="translation"] menulist > .menulist-label-box {
  margin-inline-start: 1ch;
  margin-inline-end: 1ch;
}

notification[value="translation"] button:hover,
notification[value="translation"] button:active,
notification[value="translation"] menulist:hover,
notification[value="translation"] menulist:active {
  background-color: #E2E1E0;
}

notification[value="translation"] button[anonid="translate"] {
  color: #FFF;
  background-image: linear-gradient(#9FB938, #8DA726);
  box-shadow: none;
  border: 1px solid #829C1C;
}

notification[value="translation"] button[anonid="translate"]:hover,
notification[value="translation"] button[anonid="translate"]:active {
  background-image: linear-gradient(#8DA726, #8DA726);
}

notification[value="translation"] button > .button-box,
notification[value="translation"] button[type="menu"] > .button-box > .button-menu-dropmarker {
  padding: 0;
  margin-inline-start: 3ch;
}

notification[value="translation"] button:not([type="menu"]) > .button-box {
  margin-inline-end: 3ch;
}

notification[value="translation"] menulist > .menulist-dropmarker {
  display: block;
}

/* AutoComplete */


/* General popup rules */

#PopupAutoComplete > richlistbox > richlistitem {
  height: 20px;
  min-height: 20px;
  border: 0;
  border-radius: 0;
  padding: 0px 1px 0px 1px;
}

#PopupAutoComplete > richlistbox > richlistitem > .ac-site-icon {
  margin-inline-start: 4px;
  margin-inline-end: 0;
}

#PopupAutoComplete > richlistbox > richlistitem > .ac-title {
  font: icon;
  margin-inline-start: 4px;
}

#PopupAutoComplete > richlistbox {
  padding: 0;
}

/* Popup states */

.autocomplete-richlistitem:hover {
  background-color: var(--arrowpanel-dimmed);
}

.autocomplete-richlistitem[selected] {
  background-color: Highlight;
  color: HighlightText;
}

/* Login form autocompletion */
#PopupAutoComplete > richlistbox > richlistitem[originaltype="login"] > .ac-site-icon {
  display: initial;
  list-style-image: url(chrome://browser/skin/notification-icons/login.svg);
  -moz-context-properties: fill;
  fill: GrayText;
}

#PopupAutoComplete > richlistbox > richlistitem[originaltype="login"] > .ac-site-icon[selected] {
  fill: HighlightText;
}


/* Insecure field warning */
#PopupAutoComplete > richlistbox > richlistitem[originaltype="insecureWarning"] {
  background-color: var(--arrowpanel-dimmed);
  border-bottom: 1px solid var(--panel-separator-color);
  padding-bottom: 4px;
  padding-top: 4px;
}

#PopupAutoComplete > richlistbox > richlistitem[originaltype="insecureWarning"][selected] {
  background-color: var(--arrowpanel-dimmed-further);
  color: -moz-DialogText;
}

#PopupAutoComplete > richlistbox > richlistitem[originaltype="insecureWarning"] > .ac-title {
  color: GrayText;
  font-size: 1em;
}

#PopupAutoComplete > richlistbox > richlistitem[originaltype="insecureWarning"][selected] > .ac-title {
  color: inherit;
}

#PopupAutoComplete > richlistbox > richlistitem[originaltype="insecureWarning"] > .ac-site-icon {
  list-style-image: url(chrome://browser/skin/connection-mixed-active-loaded.svg);
  -moz-context-properties: fill;
  fill: GrayText;
}

:root {
  --autocomplete-popup-background: -moz-field;
  --autocomplete-popup-color: -moz-fieldtext;
  --autocomplete-popup-highlight-background: Highlight;
  --autocomplete-popup-highlight-color: HighlightText;
}

#PopupAutoCompleteRichResult,
#PopupSearchAutoComplete {
  background: var(--autocomplete-popup-background);
  color: var(--autocomplete-popup-color);
}

#PopupAutoCompleteRichResult .autocomplete-richlistbox {
  padding: 4px 3px;
  background: transparent;
  color: inherit;
}

#PopupAutoCompleteRichResult .autocomplete-richlistitem {
  min-height: 30px;
  font: message-box;
  border-radius: 2px;
  padding-inline-start: var(--item-padding-start);
  /* For the space after the autocomplete text we have to use a transparent
     border instead of padding, because the latter would considered part of the
     scrollable area when generating the overflow events that we use to
     constrain the autocomplete result item size. */
  border-inline-end: var(--item-padding-end) solid transparent;
}

#PopupAutoCompleteRichResult .autocomplete-richlistitem[selected],
#PopupSearchAutoComplete .autocomplete-richlistitem[selected] {
  background: var(--autocomplete-popup-highlight-background);
  color: var(--autocomplete-popup-highlight-color);
}

:root[uidensity=touch] #PopupAutoCompleteRichResult .autocomplete-richlistitem {
  min-height: 40px;
}

/* Awesomebar popup items */

.ac-separator:not([selected=true]) {
  color: GrayText;
}

.ac-url:not([selected=true]) {
  color: var(--urlbar-popup-url-color);
}

.ac-action:not([selected=true]) {
  color: var(--urlbar-popup-action-color);
}

html|span.ac-tag {
  background-color: var(--arrowpanel-dimmed);
  border-radius: 2px;
  border: 1px solid var(--panel-separator-color);
  padding: 0 1px;
}

.ac-tags-text[selected] > html|span.ac-tag {
  background-color: var(--autocomplete-popup-highlight-color);
  color: var(--autocomplete-popup-highlight-background);
}

html|span.ac-emphasize-text-title,
html|span.ac-emphasize-text-tag,
html|span.ac-emphasize-text-url {
  font-weight: 600;
}

.ac-site-icon,
.ac-type-icon {
  -moz-context-properties: fill, fill-opacity;
  fill: currentColor;
  fill-opacity: 0.6;
}

.ac-site-icon[selected],
.ac-type-icon[selected] {
  fill-opacity: 1;
}

.ac-type-icon[type=bookmark] {
  list-style-image: url("chrome://browser/skin/bookmark.svg");
}

.ac-type-icon[type=keyword],
.ac-site-icon[type=searchengine] {
  list-style-image: url(chrome://browser/skin/search-glass.svg);
}

.ac-type-icon[type=switchtab],
.ac-type-icon[type=remotetab] {
  list-style-image: url("chrome://browser/skin/tab.svg");
}

:root {
  --urlbar-popup-url-color: -moz-nativehyperlinktext;
  --urlbar-popup-action-color: -moz-nativehyperlinktext;
}

#PopupAutoComplete > richlistbox > richlistitem[originaltype~="datalist-first"] {
  border-top: 1px solid ThreeDShadow;
}

.ac-title {
  font-size: 1.05em;
}

.ac-separator,
.ac-url,
.ac-action,
.ac-tags {
  font-size: 0.9em;
}

/* Bookmarking panel */
#editBookmarkPanelStarIcon {
  list-style-image: url("chrome://browser/skin/bookmark.svg");
  -moz-context-properties: fill;
  fill: var(--toolbarbutton-icon-fill-attention);
  width: 40px;
  height: 40px;
  margin: 4px;
}

#editBookmarkPanelTitle {
  font-size: 130%;
}

#editBookmarkPanelHeader,
#editBookmarkPanelContent {
  margin-bottom: .5em;
}

/* Implements editBookmarkPanel resizing on folderTree un-collapse. */
#editBMPanel_folderTree {
  min-width: 27em;
}

/* Content area */


.sidebar-header,
#sidebar-header {
  font-size: 1.333em;
  font-weight: lighter;
  padding: 8px;
}


#sidebar-throbber[loading="true"] {
  list-style-image: url("chrome://global/skin/icons/loading.png");
}

@media (min-resolution: 2dppx) {
  .sidebar-throbber[loading="true"],
  #sidebar-throbber[loading="true"] {
    list-style-image: url("chrome://global/skin/icons/[email protected]");
    width: 16px;
  }
}

#sidebar-title {
  margin: 0;
  padding: 0;
  padding-inline-start: 8px;
  padding-inline-end: 4px;
}

#sidebar-switcher-arrow {
  -moz-context-properties: fill;
  fill: currentColor;
  opacity: 0.8;
}

#sidebar-switcher-arrow {
  list-style-image: url(chrome://global/skin/icons/arrow-dropdown-12.svg);
  width: 12px;
  height: 12px;
}

#sidebar-switcher-target {
  -moz-appearance: none;
  color: inherit;
  margin-inline-end: 4px;
  border-radius: var(--toolbarbutton-border-radius);
  border: 1px solid transparent;
  padding: 2px 4px;
}

#sidebar-switcher-target:hover {
  background: var(--toolbarbutton-hover-background);
}

#sidebar-switcher-target:hover:active,
#sidebar-switcher-target.active {
  background: var(--toolbarbutton-active-background);
}

/* Ensure we do not lose contrast between lightweight and OS theme colours */
#sidebar-switcher-target:hover:-moz-lwtheme {
  background: hsla(240, 5%, 5%, 0.1);
}
#sidebar-switcher-target:hover:active:-moz-lwtheme,
#sidebar-switcher-target.active:-moz-lwtheme {
  background: hsla(240, 5%, 5%, 0.15);
}

#sidebarMenu-popup .subviewbutton {
  min-width: 190px;
}

toolbarseparator + #sidebar-extensions-separator {
  display: none;
}

#sidebarMenu-popup > .subviewbutton[checked="true"] {
  list-style-image: none;
  background: url(chrome://browser/skin/check.svg) no-repeat transparent;
  background-size: 11px 11px;
  color: var(--arrowpanel-color);
}


#sidebarMenu-popup > .subviewbutton[checked="true"] {
  background-position: center left 7px;
}

#sidebarMenu-popup > .subviewbutton[checked="true"]:-moz-locale-dir(rtl) {
  background-position: center right 7px;
}

/* Allow room for the checkbox drawn as a background image at the start of the toolbarbutton */
#sidebarMenu-popup > .subviewbutton-iconic > .toolbarbutton-icon {
  margin-inline-start: 16px;
}
/* Align items without icons to the start of the icons: */
#sidebarMenu-popup > .subviewbutton:not(.subviewbutton-iconic) > .toolbarbutton-text {
  padding-inline-start: 16px;
}


#sidebar-switcher-bookmarks > .toolbarbutton-icon,
#sidebar-box[sidebarcommand="viewBookmarksSidebar"] > #sidebar-header > #sidebar-switcher-target > #sidebar-icon {
  list-style-image: url(chrome://browser/skin/bookmark.svg);
  -moz-context-properties: fill;
  fill: currentColor;
  opacity: 0.8;
}

#sidebar-switcher-history > .toolbarbutton-icon,
#sidebar-box[sidebarcommand="viewHistorySidebar"] > #sidebar-header > #sidebar-switcher-target > #sidebar-icon {
  list-style-image: url(chrome://browser/skin/history.svg);
  -moz-context-properties: fill;
  fill: currentColor;
  opacity: 0.8;
}

#sidebar-switcher-tabs > .toolbarbutton-icon,
#sidebar-box[sidebarcommand="viewTabsSidebar"] > #sidebar-header > #sidebar-switcher-target > #sidebar-icon {
  list-style-image: url(chrome://browser/skin/tab.svg);
  -moz-context-properties: fill;
  fill: currentColor;
  opacity: 0.8;
}

#sidebar-box {
  background-color: -moz-Field;
  color: -moz-FieldText;
}

#sidebar-header {
  border-bottom: 1px solid ThreeDShadow;
}

.sidebar-splitter {
  -moz-appearance: none;
  width: 6px;
  background-color: -moz-dialog;
  border: 1px ThreeDShadow;
  border-style: none solid;
}

.browserContainer > findbar {
  background-color: -moz-dialog;
  color: -moz-DialogText;
  text-shadow: none;
}

/* Tabstrip */


#titlebar,
#tabbrowser-tabs {
  --tab-min-height: 33px;
}

:root[uidensity=compact] #titlebar,
:root[uidensity=compact] #tabbrowser-tabs {
  --tab-min-height: 29px;
}

:root[uidensity=touch] #titlebar,
:root[uidensity=touch] #tabbrowser-tabs {
  --tab-min-height: 41px;
}

#tabbrowser-tabs {
  --tabs-top-border-width: 0px;
  --tab-min-width: 76px;
  --tab-loading-fill: #0A84FF;
}

#tabbrowser-tabs:-moz-lwtheme {
  --tab-line-color: var(--lwt-accent-color);
}

#tabbrowser-tabpanels {
  /* Value for --in-content-page-background in in-content/common.inc.css */
  background-color: #f9f9fa;
}

:root[privatebrowsingmode=temporary] #tabbrowser-tabpanels {
  /* Value for --in-content-page-background in aboutPrivateBrowsing.css */
  background-color: #25003e;
}

#tabbrowser-tabs,
#tabbrowser-tabs > .tabbrowser-arrowscrollbox,
#tabbrowser-tabs[positionpinnedtabs] > .tabbrowser-tab[pinned] {
  min-height: var(--tab-min-height);
}

.tab-stack {
  min-height: inherit;
}

@supports -moz-bool-pref("layout.css.emulate-moz-box-with-flex") {
  .tab-stack {
    /* Needed to allow tabs to shrink to be skinnier than their page-title: */
    min-width: 0;
  }

  .tabbrowser-arrowscrollbox > .arrowscrollbox-scrollbox > .scrollbox-innerbox {
    /* Needed to prevent tabstrip from growing as wide as the sum of the tabs'
       page-title widths (when we'd rather have it be as wide as the window and
       compress the tabs to their minimum size): */
    max-width: 100%;
  }
}

.tabbrowser-tab {
  -moz-appearance: none;
  background-color: transparent;
  border-radius: 0;
  border-width: 0;
  margin: 0;
  padding: 0;
  -moz-box-align: stretch;
}

/* The selected tab should appear above the border between the tabs toolbar and
   the navigation toolbar. */
.tabbrowser-tab[visuallyselected=true] {
  position: relative;
  z-index: 2;
}

.tab-content {
  padding: 0 9px;
}

:root:not([uidensity=compact]) .tab-content[pinned] {
  padding: 0 12px;
}

:root[sessionrestored] .tab-loading-burst {
  position: relative;
  overflow: hidden;
}

:root[sessionrestored] .tab-loading-burst::before {
  position: absolute;
  content: "";
  /* We set the width to be a percentage of the tab's width so that we can use
     the `scale` transform to scale it up to the full size of the tab when the
     burst occurs. We also need to set the margin-inline-start so that the
     center of the burst matches the center of the favicon. */
  width: 5%;
  height: 100%;
  /* Center the burst over the .tab-loading-burst; it's 9px from the edge thanks
     to .tab-content, plus 8px more since .tab-loading-burst is 16px wide. */
  margin-inline-start: calc(17px - 2.5%);
}

:root[sessionrestored] .tab-loading-burst[pinned]::before {
  /* This is like the margin-inline-start rule above, except that icons for
     pinned tabs are 12px from the edge. */
  margin-inline-start: calc(20px - 2.5%);
}

:root[sessionrestored] .tab-loading-burst[bursting]::before {
  background-image: url("chrome://browser/skin/tabbrowser/loading-burst.svg");
  background-position: center center;
  background-size: 100% auto;
  background-repeat: no-repeat;
  animation: tab-burst-animation 375ms cubic-bezier(0,0,.58,1);
  -moz-context-properties: fill;
  fill: var(--tab-loading-fill);
}

:root[sessionrestored] .tab-loading-burst[bursting][notselectedsinceload]::before {
  animation-name: tab-burst-animation-light;
}

@keyframes tab-burst-animation {
  0% {
    opacity: 0.4;
    transform: scale(1);
  }
  100% {
    opacity: 0;
    transform: scale(40);
  }
}

@keyframes tab-burst-animation-light {
  0% {
    opacity: 0.2;
    transform: scale(1);
  }
  100% {
    opacity: 0;
    transform: scale(40);
  }
}

.tab-throbber,
.tab-throbber-fallback,
.tab-icon-image,
.tab-sharing-icon-overlay,
.tab-icon-sound,
.tab-close-button {
  margin-top: 1px;
}

.tab-throbber,
.tab-throbber-fallback,
.tab-icon-image,
.tab-sharing-icon-overlay {
  height: 16px;
  width: 16px;
}

.tab-throbber:not([pinned]),
.tab-throbber-fallback:not([pinned]),
.tab-sharing-icon-overlay:not([pinned]),
.tab-icon-image:not([pinned]) {
  margin-inline-end: 6px;
}

:root[sessionrestored] .tab-throbber[busy] {
  position: relative;
  overflow: hidden;
}

:root[sessionrestored] .tab-throbber[busy]::before {
  content: "";
  position: absolute;
  background-image: url("chrome://browser/skin/tabbrowser/loading.svg");
  background-position: left center;
  background-repeat: no-repeat;
  width: 960px;
  height: 100%;
  animation: tab-throbber-animation 1.05s steps(60) infinite;
  -moz-context-properties: fill;

  /* XXX: It would be nice to transition between the "connecting" color and the
     "loading" color (see the `.tab-throbber[progress]::before` rule below);
     however, that currently forces main thread painting. When this is fixed
     (after WebRender lands), we can do something like
     `transition: fill 0.333s, opacity 0.333s;` */

  fill: currentColor;
  opacity: 0.7;
}

:root[sessionrestored] .tab-throbber[busy]:-moz-locale-dir(rtl)::before {
  animation-name: tab-throbber-animation-rtl;
}

@keyframes tab-throbber-animation {
  0% { transform: translateX(0); }
  100% { transform: translateX(-100%); }
}

@keyframes tab-throbber-animation-rtl {
  0% { transform: translateX(0); }
  100% { transform: translateX(100%); }
}

#TabsToolbar[brighttext] .tab-throbber[busy]:not([selected=true])::before {
  /* Don't change the --tab-loading-fill variable because this should only affect
     tabs that are not visually selected. */
  fill: #717171;
  opacity: 1;
}

:root[sessionrestored] .tab-throbber[progress]::before {
  fill: var(--tab-loading-fill);
  opacity: 1;
}

#TabsToolbar[brighttext] .tab-throbber[progress]:not([selected=true])::before {
  /* Don't change the --tab-loading-fill variable because this should only affect
     tabs that are not visually selected. */
  fill: #84c1ff;
}

#tabbrowser-tabs[schedulepressure] .tab-throbber,
#tabbrowser-tabs:not([schedulepressure]) .tab-throbber-fallback {
  display: none;
}

.tab-icon-image {
  list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.svg");
  -moz-context-properties: fill;
  fill: currentColor;
}

.tab-icon-image[sharing]:not([selected]),
.tab-sharing-icon-overlay {
  animation: 3s linear tab-sharing-icon-pulse infinite;
}

@keyframes tab-sharing-icon-pulse {
  0%, 16.66%, 83.33%, 100% {
    opacity: 0;
  }
  33.33%, 66.66% {
    opacity: 1;
  }
}

.tab-icon-image[sharing]:not([selected]) {
  animation-delay: -1.5s;
}

.tab-sharing-icon-overlay {
  /* 16px of the icon + 6px of margin-inline-end of .tab-icon-image */
  margin-inline-start: -22px;
  position: relative;
  -moz-context-properties: fill;
  fill: rgb(224, 41, 29);
}

.tab-sharing-icon-overlay[pinned] {
  margin-inline-start: -16px;
}

.tab-sharing-icon-overlay[sharing="camera"] {
  list-style-image: url("chrome://browser/skin/notification-icons/camera.svg");
}

.tab-sharing-icon-overlay[sharing="microphone"] {
  list-style-image: url("chrome://browser/skin/notification-icons/microphone.svg");
}

.tab-sharing-icon-overlay[sharing="screen"] {
  list-style-image: url("chrome://browser/skin/notification-icons/screen.svg");
}

.tab-icon-overlay {
  width: 16px;
  height: 16px;
  margin-top: -8px;
  margin-inline-start: -15px;
  margin-inline-end: -1px;
  position: relative;
}

.tab-icon-overlay[pinned] {
  margin-inline-start: -6px;
  margin-inline-end: -10px;
}

.tab-icon-overlay[crashed] {
  list-style-image: url("chrome://browser/skin/tabbrowser/crashed.svg");
}

.tab-icon-overlay[soundplaying],
.tab-icon-overlay[muted]:not([crashed]),
.tab-icon-overlay[activemedia-blocked]:not([crashed]) {
  border-radius: 10px;
}

.tab-icon-overlay[soundplaying]:hover,
.tab-icon-overlay[muted]:not([crashed]):hover,
.tab-icon-overlay[activemedia-blocked]:not([crashed]):hover {
  background-color: white;
}

.tab-icon-overlay[soundplaying] {
  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio");
}

.tab-icon-overlay[muted]:not([crashed]) {
  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-muted");
}

.tab-icon-overlay[activemedia-blocked]:not([crashed]) {
  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-blocked");
}

#TabsToolbar[brighttext] .tab-icon-overlay[soundplaying]:not([selected]):not(:hover),
.tab-icon-overlay[soundplaying][selected]:-moz-lwtheme-brighttext:not(:hover) {
  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-white");
}

#TabsToolbar[brighttext] .tab-icon-overlay[muted]:not([crashed]):not([selected]):not(:hover),
.tab-icon-overlay[muted][selected]:-moz-lwtheme-brighttext:not(:hover) {
  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-white-muted");
}

#TabsToolbar[brighttext] .tab-icon-overlay[activemedia-blocked]:not([crashed]):not([selected]):not(:hover),
.tab-icon-overlay[activemedia-blocked][selected]:-moz-lwtheme-brighttext:not(:hover) {
  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-white-blocked");
}

.tab-throbber-fallback[busy] {
  list-style-image: url("chrome://browser/skin/tabbrowser/tab-connecting.png");
}

.tab-throbber-fallback[progress] {
  list-style-image: url("chrome://browser/skin/tabbrowser/tab-loading.png");
}

#TabsToolbar[brighttext] .tab-throbber-fallback[progress]:not([selected=true]) {
  list-style-image: url("chrome://browser/skin/tabbrowser/tab-loading-inverted.png");
}

@media (min-resolution: 1.1dppx) {
  .tab-throbber-fallback[busy] {
    list-style-image: url("chrome://browser/skin/tabbrowser/[email protected]");
  }

  .tab-throbber-fallback[progress] {
    list-style-image: url("chrome://browser/skin/tabbrowser/[email protected]");
  }

  #TabsToolbar[brighttext] .tab-throbber-fallback[progress]:not([selected=true]) {
    list-style-image: url("chrome://browser/skin/tabbrowser/[email protected]");
  }
}

.tab-label {
  margin-inline-end: 0;
  margin-inline-start: 0;
  /* Maintain consistent alignment in case of font fallback for non-Latin characters. */
  line-height: 1.7em;
}

.tab-close-button {
  margin-inline-end: -2px;
  -moz-context-properties: fill, fill-opacity, stroke-opacity;
  stroke-opacity: var(--toolbarbutton-icon-fill-opacity);
}

:root[uidensity=touch] .tab-close-button {
  margin-inline-end: -9px;
  padding: 10px calc(9px - 2px);
}

.tab-icon-sound {
  margin-inline-start: 1px;
  width: 16px;
  height: 16px;
  padding: 0;
}

.tab-icon-sound[soundplaying],
.tab-icon-sound[muted],
.tab-icon-sound[activemedia-blocked] {
  list-style-image: url(chrome://browser/skin/tabbrowser/tab-audio-playing.svg);
  -moz-context-properties: fill;
  fill: currentColor;
}

.tab-icon-sound[muted] {
  list-style-image: url(chrome://browser/skin/tabbrowser/tab-audio-muted.svg);
}

.tab-icon-sound[activemedia-blocked] {
  list-style-image: url(chrome://browser/skin/tabbrowser/tab-audio-blocked.svg);
}

:root[lwtheme-image] .tab-icon-sound:-moz-lwtheme-darktext[soundplaying],
:root[lwtheme-image] .tab-icon-sound:-moz-lwtheme-darktext[muted],
:root[lwtheme-image] .tab-icon-sound:-moz-lwtheme-darktext[activemedia-blocked] {
  filter: drop-shadow(1px 1px 1px white);
}

:root[lwtheme-image] .tab-icon-sound:-moz-lwtheme-brighttext[soundplaying],
:root[lwtheme-image] .tab-icon-sound:-moz-lwtheme-brighttext[muted],
:root[lwtheme-image] .tab-icon-sound:-moz-lwtheme-brighttext[activemedia-blocked] {
  filter: drop-shadow(1px 1px 1px black);
}

.tab-icon-sound[soundplaying]:not(:hover),
.tab-icon-sound[muted]:not(:hover),
.tab-icon-sound[activemedia-blocked]:not(:hover) {
  opacity: .8;
}

.tab-icon-sound[soundplaying-scheduledremoval]:not([muted]):not(:hover),
.tab-icon-overlay[soundplaying-scheduledremoval]:not([muted]):not(:hover) {
  transition: opacity .3s linear var(--soundplaying-removal-delay);
  opacity: 0;
}

/* Tab Overflow */
.tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-start-indicator:not([collapsed]),
.tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-end-indicator:not([collapsed]) {
  width: 18px;
  background-image: url(chrome://browser/skin/tabbrowser/tab-overflow-indicator.png);
  background-size: 17px 100%;
  background-repeat: no-repeat;
  border-left: 1px solid;
  border-image: linear-gradient(rgba(255,255,255,.2),
                                rgba(255,255,255,.2) calc(100% - 1px - 1px),
                                transparent calc(100% - 1px - 1px));
  border-image-slice: 1;
  pointer-events: none;
  position: relative;
  z-index: 3; /* the selected tab's z-index + 1 */
}

.tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-start-indicator:-moz-locale-dir(rtl),
.tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-end-indicator:-moz-locale-dir(ltr) {
  transform: scaleX(-1);
}

.tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-start-indicator:not([collapsed]) {
  margin-inline-start: -1px;
  margin-inline-end: -17px;
}

.tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-end-indicator:not([collapsed]) {
  margin-inline-start: -17px;
  margin-inline-end: -1px;
}

.tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-start-indicator[collapsed],
.tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-end-indicator[collapsed] {
  opacity: 0;
}

.tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-start-indicator,
.tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-end-indicator {
  transition: opacity 150ms ease;
}

.tabbrowser-tab:not([visuallyselected=true]),
.tabbrowser-tab:-moz-lwtheme {
  color: inherit;
}

.tabbrowser-tab[visuallyselected=true]:-moz-lwtheme {
  color: var(--lwt-tab-text, var(--toolbar-color, inherit));
}

.tab-line {
  height: 2px;
}

/* Selected tab */

.tab-background {
  border: 1px none transparent;
  background-clip: padding-box;
}

#toolbar-menubar:not([autohide=true]) + #TabsToolbar,
:root:not([tabsintitlebar]),
:root[extradragspace] {
  --tabs-top-border-width: 1px;
}

#toolbar-menubar:not([autohide=true]) + #TabsToolbar > #tabbrowser-tabs > .tabbrowser-tab > .tab-stack > .tab-background,
:root:not([tabsintitlebar]) .tab-background,
:root[extradragspace] .tab-background {
  border-top-style: solid;
}

.tab-background[selected=true] {
  border-top-color: var(--tabs-border-color);
  background-color: var(--toolbar-bgcolor);
  background-image: var(--toolbar-bgimage);
  background-repeat: repeat-x;
}

.tab-line[selected=true] {
  background-color: var(--tab-line-color);
}

/*
 * LightweightThemeConsumer will set the current lightweight theme's header
 * image to the lwt-header-image variable, used in each of the following rulesets.
 */

/* Lightweight theme on tabs */
#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background[selected=true]:-moz-lwtheme { 
  background-attachment: scroll, scroll, fixed;
  background-color: transparent;
  background-image: linear-gradient(var(--lwt-selected-tab-background-color, transparent), var(--lwt-selected-tab-background-color, transparent)),
                    linear-gradient(var(--toolbar-bgcolor), var(--toolbar-bgcolor)),
                    var(--lwt-header-image, none);
  background-position: 0 0, 0 0, right top;
  background-repeat: repeat-x, repeat-x, no-repeat;
  background-size: auto 100%, auto 100%, auto auto;
}

/* Tab hover */

.tabbrowser-tab:hover > .tab-stack > .tab-background:not([selected=true]) {
  background-color: rgba(0,0,0,.1);
}

#TabsToolbar[brighttext] > #tabbrowser-tabs > .tabbrowser-tab:hover > .tab-stack > .tab-background:not([selected=true]) {
  background-color: rgba(255,255,255,.1);
}

.tab-line:not([selected=true]) {
  opacity: 0;
  transform: scaleX(0);
  transition: transform 250ms var(--animation-easing-function), opacity 250ms var(--animation-easing-function);
}

.tabbrowser-tab:hover > .tab-stack > .tab-background > .tab-line:not([selected=true]) {
  background-color: rgba(0,0,0,.2);
  opacity: 1;
  transform: none;
}

#TabsToolbar[brighttext] > #tabbrowser-tabs > .tabbrowser-tab:hover > .tab-stack > .tab-background > .tab-line:not([selected=true]) {
  background-color: rgba(255,255,255,.2);
}

/* Pinned tabs */

/* Pinned tab separators need position: absolute when positioned (during overflow). */
#tabbrowser-tabs[positionpinnedtabs] > .tabbrowser-tab[pinned]::after {
  position: absolute;
  top: 0;
  bottom: 0;
  right: 0;
}

#tabbrowser-tabs[positionpinnedtabs] > .tabbrowser-tab[pinned]:-moz-locale-dir(rtl)::after {
  right: unset;
  left: 0;
}

#tabbrowser-tabs[positionpinnedtabs] > .tabbrowser-tab[pinned] > .tab-stack {
  border-inline-end: 1px solid transparent;
}

.tabbrowser-tab:-moz-any([image], [pinned]) > .tab-stack > .tab-content[attention]:not([selected="true"]),
.tabbrowser-tab > .tab-stack > .tab-content[pinned][titlechanged]:not([selected="true"]) {
  background-image: url(chrome://browser/skin/tabbrowser/indicator-tab-attention.svg);
  background-position: center bottom calc(-4px + 1px);
  background-repeat: no-repeat;
}

.tabbrowser-tab[image] > .tab-stack > .tab-content[attention]:not([pinned]):not([selected="true"]) {
  background-position-x: left 11px;
}

.tabbrowser-tab[image] > .tab-stack > .tab-content[attention]:not([pinned]):not([selected="true"]):-moz-locale-dir(rtl) {
  background-position-x: right 11px;
}

.tab-label[attention]:not([selected="true"]) {
  font-weight: bold;
}

/* Drag space */

.titlebar-placeholder[type="pre-tabs"],
.titlebar-placeholder[type="post-tabs"] {
  width: 40px;
}

@media (max-width: 500px) {
  .titlebar-placeholder[type="post-tabs"] {
    display: none;
  }
}

/* Tab separators */

.titlebar-placeholder[type="pre-tabs"] {
  border-inline-end: 1px solid;
  opacity: 0.2;
}

.tabbrowser-tab::after,
.tabbrowser-tab::before {
  border-left: 1px solid;
  margin-top: 5px;
  margin-bottom: 4px;
  opacity: 0.3;
}

/* Move the ::before pseudo-element on tabs 1px to the left
 * to avoid resizing the tab when the pseudo-element is removed again
 * (this currently happens when a tab is dragged and dropped).
 *
 * Note that this doesn't perfectly solve the issue (dragged tabs
 * may still resize very slightly) on some DPI settings with uneven
 * scaling factors on Windows, because of bug 477157.
 */
.tabbrowser-tab::before {
  margin-inline-start: -1px;
}

/* Show full height tab separators on hover. */
.tabbrowser-tab:hover::after,
#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab[beforehovered]::after {
  margin-top: var(--tabs-top-border-width);
  margin-bottom: 0;
}

/* Show full height tab separators on selected tabs. */
#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab[beforeselected-visible]::after,
#tabbrowser-tabs[movingtab] > .tabbrowser-tab[visuallyselected]::before,
.tabbrowser-tab[visuallyselected]::after {
  border-color: var(--tabs-border-color);
  margin-top: 0;
  margin-bottom: 1px;
  opacity: 1;
}

.tabbrowser-tab::after,
/* Also show separators beside the selected tab when dragging it. */
#tabbrowser-tabs[movingtab] > .tabbrowser-tab[visuallyselected]::before {
  content: "";
  display: -moz-box;
}

/* Tab bar scroll arrows */

.tabbrowser-arrowscrollbox > .scrollbutton-up,
.tabbrowser-arrowscrollbox > .scrollbutton-down {
  list-style-image: url(chrome://browser/skin/arrow-left.svg);
  -moz-context-properties: fill, fill-opacity;
  fill: currentColor;
  fill-opacity: var(--toolbarbutton-icon-fill-opacity);
  color: inherit;
}

.tabbrowser-arrowscrollbox > .scrollbutton-up:-moz-locale-dir(rtl),
.tabbrowser-arrowscrollbox > .scrollbutton-down:-moz-locale-dir(ltr) {
  transform: scaleX(-1);
}

/* New tab button */

.tabs-newtab-button,
#TabsToolbar > #new-tab-button ,
#TabsToolbar > toolbarpaletteitem > #new-tab-button {
  list-style-image: url(chrome://browser/skin/tabbrowser/newtab.svg);
}

/* All tabs button and menupopup */

#alltabs-button {
  list-style-image: url(chrome://global/skin/icons/arrow-dropdown-16.svg);
}

.alltabs-item > .menu-iconic-left > .menu-iconic-icon {
  list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.svg");
  -moz-context-properties: fill;
  fill: currentColor;
}

.alltabs-item[busy] > .menu-iconic-left > .menu-iconic-icon {
  list-style-image: url("chrome://global/skin/icons/loading.png");
}

@media (min-resolution: 1.1dppx) {
  .alltabs-item[busy] > .menu-iconic-left > .menu-iconic-icon {
    list-style-image: url("chrome://global/skin/icons/[email protected]");
  }
}

.alltabs-item[tabIsVisible] {
  /* box-shadow instead of background-color to work around native styling */
  box-shadow: inset -5px 0 ThreeDShadow;
}

.alltabs-endimage[soundplaying],
.alltabs-endimage[muted],
.alltabs-endimage[activemedia-blocked] {
  list-style-image: url(chrome://browser/skin/tabbrowser/tab-audio-playing.svg);
  -moz-context-properties: fill;
  fill: currentColor;
}

.alltabs-endimage[muted] {
  list-style-image: url(chrome://browser/skin/tabbrowser/tab-audio-muted.svg);
}

.alltabs-endimage[activemedia-blocked] {
  list-style-image: url(chrome://browser/skin/tabbrowser/tab-audio-blocked.svg);
}

.restore-tabs-button-wrapper {
  visibility: hidden;
  position: fixed; /* so the button does not take up actual space and cause overflow buttons in the tabbar when hidden */
}

.restore-tabs-button-wrapper[shown] {
  visibility: visible;
  position: initial;
}

.restore-tabs-button {
  box-sizing: border-box;
  -moz-appearance: none;
  background-color: hsl(0,0%,0%,.04);
  border: 1px solid hsla(0,0%,16%,.2);
  border-radius: 3px;
  margin: 3px;
  margin-inline-start: 9px;
  transition: max-width 100ms;
  padding: 0 5px;
}

.restore-tabs-button:hover {
  background-color: hsl(0,0%,0%,.08);
}

.restore-tabs-button:active {
  background-color: hsl(0,0%,0%,.11);
}

#TabsToolbar[brighttext] .restore-tabs-button {
  background-color: hsl(0,0%,100%,.07);
  border-color:currentColor;
  color: currentColor;
  opacity: .7;
}

#TabsToolbar[brighttext] .restore-tabs-button:hover {
  background-color: hsl(0,0%,100%,.17);
}

#TabsToolbar[brighttext] .restore-tabs-button:active {
  background-color: hsl(0,0%,100%,.27);
}

.restore-tabs-button > .toolbarbutton-icon {
  display: none;
}

.restore-tabs-button > .toolbarbutton-text {
  display: -moz-box;
}

#tabbrowser-tabs {
  /* override the global style to allow the selected tab to be above the nav-bar */
  z-index: auto;
}

#TabsToolbar {
  min-height: 0;
}

#TabsToolbar:not(:-moz-lwtheme) {
  -moz-appearance: menubar;
  color: -moz-menubartext;
}

#nav-bar {
  -moz-binding: url("chrome://browser/content/customizableui/toolbar.xml#toolbar-drag");
}

@media (-moz-menubar-drag) {
  #toolbar-menubar:not([autohide="true"]),
  #TabsToolbar {
    -moz-binding: url("chrome://browser/content/customizableui/toolbar.xml#toolbar-drag");
  }
}

.tabbrowser-tab:focus > .tab-stack > .tab-content {
  outline: 1px dotted;
  outline-offset: -6px;
}

#context_reloadTab {
  list-style-image: url("moz-icon://stock/gtk-refresh?size=menu");
}

#context_closeOtherTabs {
  list-style-image: url("moz-icon://stock/gtk-clear?size=menu");
}

#context_closeOtherTabs[disabled] {
  list-style-image: url("moz-icon://stock/gtk-clear?size=menu&state=disabled");
}

#context_undoCloseTab {
  list-style-image: url("moz-icon://stock/gtk-undelete?size=menu");
}

#context_closeTab {
  list-style-image: url("moz-icon://stock/gtk-close?size=menu");
}

/* Tab drag and drop */
.tab-drop-indicator {
  list-style-image: url(chrome://browser/skin/tabbrowser/tabDragIndicator.png);
  margin-bottom: -9px;
  z-index: 3;
}

/* Tab bar scroll arrows */

.tabbrowser-arrowscrollbox > .scrollbutton-up > .toolbarbutton-icon,
.tabbrowser-arrowscrollbox > .scrollbutton-down > .toolbarbutton-icon {
  -moz-appearance: none;
}

/* All tabs menupopup */

.alltabs-item[selected="true"] {
  font-weight: bold;
}

/* Status panel */

.statuspanel-label {
  margin: 0;
  padding: 2px 4px;
  background-color: -moz-dialog;
  border: 1px none ThreeDShadow;
  border-top-style: solid;
  color: -moz-dialogText;
  text-shadow: none;
}

.statuspanel-label:-moz-locale-dir(ltr):not([mirror]),
.statuspanel-label:-moz-locale-dir(rtl)[mirror] {
  border-right-style: solid;
  border-top-right-radius: .3em;
  margin-right: 1em;
}

.statuspanel-label:-moz-locale-dir(rtl):not([mirror]),
.statuspanel-label:-moz-locale-dir(ltr)[mirror] {
  border-left-style: solid;
  border-top-left-radius: .3em;
  margin-left: 1em;
}


html|*.pointerlockfswarning {
  align-items: center;
  background: rgba(45, 62, 72, 0.9);
  border: 2px solid #fafafa;
  box-shadow: 0px 0px 5px 2px rgba(0, 0, 0, 0.5);
  border-radius: 8px;
  padding: 24px 16px;
  font: message-box;
}

html|*.pointerlockfswarning::before {
  margin: 0;
  width: 24px; height: 24px;
}

html|*.pointerlockfswarning[data-identity="verifiedIdentity"]::before,
html|*.pointerlockfswarning[data-identity="verifiedDomain"]::before {
  content: url("chrome://browser/skin/fullscreen/secure.svg");
}

html|*.pointerlockfswarning[data-identity="unknownIdentity"]::before {
  content: url("chrome://browser/skin/fullscreen/insecure.svg");
}

html|*.pointerlockfswarning-domain-text,
html|*.pointerlockfswarning-generic-text {
  font-size: 21px;
  font-weight: lighter;
  color: #fafafa;
  margin: 0 16px;
}

html|*.pointerlockfswarning-domain {
  font-weight: bold;
  margin: 0;
}

html|*#fullscreen-exit-button {
  margin: 0;
}


/* Ctrl-Tab */

#ctrlTab-panel {
  -moz-appearance: none;
  background: hsla(0,0%,40%,.85);
  color: white;
  border-style: none;
  padding: 20px 10px 10px;
  font-weight: bold;
  text-shadow: 0 0 1px hsl(0,0%,12%), 0 0 2px hsl(0,0%,12%);
}

.ctrlTab-preview > html|img,
.ctrlTab-preview > html|canvas {
  min-width: inherit;
  max-width: inherit;
  min-height: inherit;
  max-height: inherit;
}

.ctrlTab-favicon-container {
  position: relative;
  -moz-box-pack: end;
}

.ctrlTab-favicon[src] {
  width: 42px;
  height: 42px;
  margin-top: -44px;
  margin-bottom: 2px;
  margin-inline-end: -6px;
  padding: 5px;
  background-color: #F9F9FA;
  border-radius: 6px;
  box-shadow: inset 0 0 0 1px rgba(0,0,0,.1);
}

.ctrlTab-preview:not(#ctrlTab-showAll) > .ctrlTab-preview-inner > .ctrlTab-canvas {
  box-shadow: 1px 1px 2px hsl(0,0%,12%);
  margin-bottom: 8px;
}

.ctrlTab-preview-inner {
  padding: 8px;
  border: 2px solid transparent;
  border-radius: .5em;
}

.ctrlTab-preview:not(#ctrlTab-showAll) > .ctrlTab-preview-inner {
  margin: -10px -10px 0;
}

#ctrlTab-showAll:not(:focus) > .ctrlTab-preview-inner {
  background-color: rgba(255,255,255,.2);
}

.ctrlTab-preview:focus > .ctrlTab-preview-inner {
  color: white;
  background-color: rgba(0,0,0,.75);
  text-shadow: none;
  border-color: #45a1ff;
}

.ctrlTab-label {
  text-align: center;
}

#ctrlTab-showAll {
  margin-top: .5em;
}

.messageImage[value="plugin-hidden"] {
  list-style-image: url(chrome://mozapps/skin/plugins/plugin.svg);
}

/* Keep any changes to this style in sync with pluginProblem.css */
notification.pluginVulnerable {
  background-color: rgb(72,72,72);
  background-image: url(chrome://mozapps/skin/plugins/contentPluginStripe.png);
  color: white;
}

notification.pluginVulnerable .messageImage {
  list-style-image: url(chrome://mozapps/skin/plugins/plugin-blocked.svg);
}

.gcli-panel {
  padding: 0;
}

.gclitoolbar-input-node > .textbox-input-box > html|*.textbox-input::-moz-selection {
  color: hsl(210,11%,16%);
}

/* Error counter */

#developer-toolbar-toolbox-button[error-count]:before {
  color: #FDF3DE;
  min-width: 16px;
  text-shadow: none;
  background-image: linear-gradient(#B4211B, #8A1915);
  border-radius: 1px;
  margin-inline-end: 2px;
}

/* Customization mode */

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* Customization mode */

:root {
  --drag-drop-transition-duration: .3s;
}

#main-window[customizing] .customization-target:not(#widget-overflow-fixed-list) {
  min-width: 100px;
}

#customization-container {
  background-color: var(--toolbar-non-lwt-bgcolor);
  background-image: var(--toolbar-non-lwt-bgimage);
  color: var(--toolbar-non-lwt-textcolor);
  text-shadow: none;
}

:root[lwtheme-image] #customization-container {
  background-color: transparent;
  background-image: linear-gradient(var(--toolbar-bgcolor), var(--toolbar-non-lwt-bgcolor) 45px);
}

#customization-palette {
  padding: 5px 20px 20px;
}

#customization-header {
  font-weight: 500;
  font-size: 1.2em;
  margin: 20px 20px 15px;
}

:root[lwtheme-image] #customization-header {
  text-shadow: 0 0 1em var(--toolbar-non-lwt-bgcolor),
               0 0 1em var(--toolbar-non-lwt-bgcolor),
               0 0 .5em var(--toolbar-non-lwt-bgcolor);
}

#customization-panel-container {
  padding: 0 20px 25px;
}

#customization-footer {
  border-top: 1px solid rgba(0,0,0,.15);
  padding: 10px;
}


.customizationmode-button > .box-inherit > .box-inherit > .button-icon,
.customizationmode-button > .button-box > .button-icon {
  height: 16px;
}

#customization-uidensity-button > .box-inherit > .box-inherit > .button-text,
#customization-lwtheme-button > .box-inherit > .box-inherit > .button-text {
  /* Sadly, button.css thinks its margins are perfect for everyone. */
  margin-inline-start: 6px !important;
}

#customization-uidensity-button > .box-inherit > .box-inherit > .button-icon,
#customization-lwtheme-button > .box-inherit > .box-inherit > .button-icon {
  width: 16px;
  height: 16px;
  border-radius: 2px;
  background-size: contain;
}

#customization-lwtheme-button > .box-inherit > .box-inherit > .button-icon {
  background-image: url("chrome://browser/content/default-theme-icon.svg");
}

#customization-uidensity-button > .box-inherit > .box-inherit > .button-icon {
  background-image: url("chrome://browser/skin/customizableui/density-normal.svg");
}

#widget-overflow-fixed-list > toolbarpaletteitem[place="menu-panel"],
toolbarpaletteitem[place="toolbar"] {
  transition: border-width 250ms ease-in-out;
}

toolbarpaletteitem[mousedown] {
  cursor: -moz-grabbing;
}

toolbarpaletteitem[place="palette"],
toolbarpaletteitem[place="menu-panel"] {
  transition: transform var(--drag-drop-transition-duration) ease-in-out;
}

#customization-palette {
  transition: opacity .3s ease-in-out;
  opacity: 0;
}

#customization-palette[showing="true"] {
  opacity: 1;
}

toolbarpaletteitem toolbarbutton[disabled] {
  color: inherit !important;
}

#widget-overflow-fixed-list > toolbarpaletteitem[notransition][place="menu-panel"],
toolbarpaletteitem[notransition][place="toolbar"],
toolbarpaletteitem[notransition][place="palette"],
toolbarpaletteitem[notransition][place="menu-panel"] {
  transition: none;
}

toolbarpaletteitem > toolbarbutton > .toolbarbutton-icon,
toolbarpaletteitem > toolbarbutton > .toolbarbutton-badge-stack > .toolbarbutton-icon,
toolbarpaletteitem > #search-container,
toolbarpaletteitem > toolbaritem.toolbaritem-combined-buttons {
  transition: transform var(--drag-drop-transition-duration) cubic-bezier(.6, 2, .75, 1.5) !important;
}

toolbarpaletteitem[mousedown] > toolbarbutton > .toolbarbutton-icon,
toolbarpaletteitem[mousedown] > toolbarbutton > .toolbarbutton-badge-stack > .toolbarbutton-icon {
  transform: scale(1.3);
}

toolbarpaletteitem[mousedown] > #search-container,
toolbarpaletteitem[mousedown] > toolbaritem.toolbaritem-combined-buttons {
  transform: scale(1.1);
}

/* Override the toolkit styling for items being dragged over. */
toolbarpaletteitem[place="toolbar"] {
  border-left-width: 0;
  border-right-width: 0;
  margin-right: 0;
  margin-left: 0;
}
#widget-overflow-fixed-list > toolbarpaletteitem[place="menu-panel"] {
  border-top: 0px solid transparent;
  border-bottom: 0px solid transparent;
}

#customization-palette:not([hidden]) {
  margin-bottom: 20px;
}

#customization-palette .toolbarbutton-1 {
  fill: currentColor;
}

toolbarpaletteitem[place="palette"]:-moz-focusring,
toolbarpaletteitem[place="menu-panel"]:-moz-focusring,
toolbarpaletteitem[place="toolbar"]:-moz-focusring {
  outline-width: 0;
}

toolbarpaletteitem[place="palette"]:not([mousedown="true"]):-moz-focusring,
toolbarpaletteitem[place="menu-panel"]:not([mousedown="true"]):-moz-focusring,
toolbarpaletteitem[place="toolbar"]:not([mousedown="true"]):-moz-focusring {
  /* Delay adding the focusring back until after the transform transition completes. */
  transition: outline-width .01s linear var(--drag-drop-transition-duration);
  outline: 1px dotted;
  -moz-outline-radius: 2.5px;
}

toolbarpaletteitem[place="toolbar"]:not([mousedown="true"]):-moz-focusring {
  outline-offset: -5px;
}

toolbarpaletteitem[place=palette] > toolbarspring {
  width: 7em;
  min-width: 7em;
  outline: 1px solid;
  outline-offset: -8px;
  opacity: .6;
  height: 37px;
}

toolbarpaletteitem[place=toolbar] > toolbarspring {
  outline: 1px solid;
  outline-offset: -2px;
  opacity: .6;
  margin-top: 5px;
  margin-bottom: 5px;
}

#wrapper-edit-controls > #edit-controls > toolbarbutton > .toolbarbutton-icon {
  opacity: 1; /* To ensure these buttons always look enabled in customize mode */
}

#wrapper-zoom-controls[place="palette"] > #zoom-controls > #zoom-reset-button,
#wrapper-zoom-controls[place="palette"] > #zoom-controls > #zoom-reset-button + separator {
  display: none;
}

#customization-palette > toolbarpaletteitem > label {
  text-align: center;
  margin-left: 0;
  margin-right: 0;
}

#customization-uidensity-touch-spacer {
  border-top: 1px solid ThreeDLightShadow;
  margin: 0 -10px 10px;
}

#customization-uidensity-autotouchmode-checkbox {
  margin-bottom: 10px;
}

#customization-uidensity-menu > .panel-arrowcontainer > .panel-arrowcontent,
#customization-lwtheme-menu > .panel-arrowcontainer > .panel-arrowcontent {
  -moz-box-orient: vertical;
  font: menu;
}

#customization-lwtheme-menu > .panel-arrowcontainer > .panel-arrowcontent {
  /* Make the panel padding uniform across all platforms due to the
     styling of the section headers and footer. */
  padding: 10px;
}

#customization-uidensity-menu > .panel-arrowcontainer > .panel-arrowcontent {
  padding: 5px 10px;
}

.customization-uidensity-menuitem > .menu-iconic-left > .menu-iconic-icon,
.customization-lwtheme-menu-theme > .toolbarbutton-icon {
  width: 32px;
  height: 32px;
  margin: 5px;
}

.customization-uidensity-menuitem,
.customization-lwtheme-menu-theme {
  -moz-appearance: none;
  border: 1px solid transparent;
  margin: 0 -5px 5px;
  padding-top: 0;
  padding-inline-end: 5px;
  padding-bottom: 0;
  padding-inline-start: 0;
}

.customization-uidensity-menuitem {
  color: inherit;
}

.customization-lwtheme-menu-theme[defaulttheme] {
  list-style-image: url(chrome://browser/content/default-theme-icon.svg);
}

#customization-uidensity-menuitem-normal {
  list-style-image: url("chrome://browser/skin/customizableui/density-normal.svg");
}

#customization-uidensity-menuitem-compact {
  list-style-image: url("chrome://browser/skin/customizableui/density-compact.svg");
}

#customization-uidensity-menuitem-touch {
  list-style-image: url("chrome://browser/skin/customizableui/density-touch.svg");
}

.customization-uidensity-menuitem[active="true"],
.customization-uidensity-menuitem:hover,
.customization-lwtheme-menu-theme[active="true"],
.customization-lwtheme-menu-theme:hover {
  background-color: var(--arrowpanel-dimmed);
  border-color: var(--panel-separator-color);
}

.customization-uidensity-menuitem[active="true"],
.customization-uidensity-menuitem:hover:active,
.customization-lwtheme-menu-theme[active="true"],
.customization-lwtheme-menu-theme:hover:active {
  background-color: var(--arrowpanel-dimmed-further);
}

.customization-uidensity-menuitem > .menu-iconic-text,
.customization-lwtheme-menu-theme > .toolbarbutton-text {
  text-align: start;
}

.customization-uidensity-menuitem > .menu-iconic-text {
  font: menu;
}

#customization-lwtheme-menu-header,
#customization-lwtheme-menu-recommended {
  padding: 10px;
  margin-bottom: 5px;
  text-align: center;
  font-weight: 500;
  border-bottom: 1px solid var(--panel-separator-color);
}

#customization-lwtheme-menu-header,
#customization-lwtheme-menu-recommended,
#customization-lwtheme-menu-footer {
  margin-right: -10px;
  margin-left: -10px;
}

#customization-lwtheme-menu-header {
  margin-top: -10px;
}

#customization-lwtheme-menu-recommended {
  border-top: 1px solid var(--panel-separator-color);
}

#customization-lwtheme-menu-footer {
  background: linear-gradient(var(--arrowpanel-dimmed) 60%, transparent) border-box;
  border-top: 1px solid var(--arrowpanel-dimmed);
  margin-bottom: -10px;
}

.customization-lwtheme-menu-footeritem {
  -moz-appearance: none;
  -moz-box-flex: 1;
  color: inherit;
  border-style: none;
  padding: 10px;
  margin-left: 0;
  margin-right: 0;
}

.customization-lwtheme-menu-footeritem:hover {
  background: linear-gradient(var(--arrowpanel-dimmed) 40%, transparent) padding-box;
}

.customization-lwtheme-menu-footeritem:first-child {
  border-inline-end: 1px solid var(--panel-separator-color);
}

#customization-panelWrapper > .panel-arrowcontent {
  color: var(--arrowpanel-color);
  background: var(--arrowpanel-background);
  background-clip: padding-box;
  border: 1px solid var(--arrowpanel-border-color);
  box-shadow: 0 0 4px hsla(0,0%,0%,.2);
  max-width: 29em;
}

#customization-panelWrapper > .panel-arrowbox {
  position: relative;
  height: 10px;
  margin-bottom: -1px;
}

/* In customize mode, the overflow button should look both 'disabled' and 'open'.
 * So we make the button fully opaque but fill the icon semi-transparent. */
#nav-bar[customizing] > .overflow-button > .toolbarbutton-icon {
  background-color: var(--toolbarbutton-active-background);
  opacity: 1;
  fill-opacity: 0.4;
}

#customization-panelWrapper > .panel-arrowbox > .panel-arrow[side="top"] {
  list-style-image: url("chrome://global/skin/arrow/panelarrow-vertical.svg");
  -moz-context-properties: fill, stroke;
  fill: var(--arrowpanel-background);
  stroke: var(--arrowpanel-border-color);
  /* The arrow needs to point to the overflow button. The numbers involved
   * are:
   * overflow button width: (16px + 2 * 2px padding
   * + 2 * toolbarbutton-inner-padding)
   * hamburger button width: (16px + 2 * 5px padding
   * + 2 * toolbarbutton-inner-padding)
   * hamburger button border + margin: 1px + 2px
   * The total desired offset from the right edge of the window is thus:
   * 10px + 1 * toolbarbutton-inner-padding (center of overflow button) +
   * 29px + 2 * toolbarbutton-inner-padding
   * The #customization-panel-container has a 20px margin, so that leaves:
   * 19px + 3 * toolbarbutton-inner-padding
   * Finally, we need to center the arrow, which is 20px wide, so subtract
   * 10px.
   */
  margin-inline-end: calc(9px + 3 * var(--toolbarbutton-inner-padding));
  vertical-align: top;
}

#customization-panelDescription {
  font-size: 1.1em;
  padding: 2px 12px 10px;
  margin: 0;
}

#customization-panelHeader {
  font-size: 1.3em;
  font-weight: 500;
  padding: 2px 12px;
  margin: 0;
}

#customization-panelHolder > #widget-overflow-fixed-list {
  padding-top: 10px;
}

/**
 * We create a ::before pseudoelement that contains a background image to show the
 * drop dragon. This element fades in and out depending on whether the containing
 * panel list is empty and unhovered, or not.
 */
#customization-panelHolder > #widget-overflow-fixed-list:not(:empty) {
  padding-bottom: 50px; /* Make sure there's always space to drop stuff. */
  border-top: 1px solid rgba(0,0,0,0.1);
}

#customization-panelHolder > #widget-overflow-fixed-list::before {
  display: block;
  content: "";
  background-image: url("chrome://browser/skin/customizableui/empty-overflow-panel.png");
  background-position: center bottom 10px;
  background-size: 218px 134px;
  background-repeat: no-repeat;
  opacity: 0;
  transition: opacity 300ms ease-out;
  padding-bottom: 154px; /* 154 = 134 for the image, 10px space on either side. */
  margin-bottom: -154px; /* don't affect positioning of the actual contents */
  pointer-events: none;
}

@media (min-resolution: 1.1dppx) {
  #customization-panelHolder > #widget-overflow-fixed-list::before {
    background-image: url("chrome://browser/skin/customizableui/[email protected]");
  }
}

#customization-panelHolder > #widget-overflow-fixed-list:empty::before {
  opacity: 1;
}
#customization-panelHolder > #widget-overflow-fixed-list[draggingover]:empty::before {
  opacity: 0;
}

#downloads-button-autohide-panel > .panel-arrowcontainer > .panel-arrowcontent {
  padding: 5px 12px;
}

#downloads-button-autohide-checkbox {
  margin: 0;
  padding: 0;
}

#whimsy-button {
  /* Don't need HiDPI versions since the size used will be scaled down to 20x20. */
  list-style-image: url("chrome://browser/skin/customizableui/whimsy.png");
}

#whimsy-button > .button-box > .button-icon {
  width: 16px;
  height: 16px;
}

#customization-palette[whimsypong] {
  /* Keep the palette in the render tree but invisible
     so -moz-element() will work. */
  padding: 0;
  min-height: 0;
  max-height: 0;
}

#customization-palette[whimsypong] > toolbarpaletteitem > toolbarspring {
  margin: 0 -7px;
}

#customization-palette[whimsypong] > toolbarpaletteitem > toolbarspring {
  font-size: 12px;
}

#wp-lives,
#wp-ball {
  /* Don't need HiDPI versions since the size used will be scaled down to 20x20. */
  background-image: url("chrome://browser/skin/customizableui/whimsy.png");
  background-size: contain;
  width: 20px;
}

#customization-pong-arena {
  width: 300px;
  height: 300px;
  border-left: 1px solid currentColor;
  border-right: 1px solid currentColor;
  margin: 16px auto 0;
  box-sizing: content-box;
}

#customization-palette[whimsypong] > toolbarpaletteitem[kcode],
#customization-pong-arena[kcode] {
  animation-name: kcode;
  animation-timing-function: steps(5);
  animation-duration: 1s;
  animation-iteration-count: infinite;
}

#wp-ball {
  margin-inline-start: -10px;
  margin-top: -10px;
  height: 20px;
}

#wp-player1,
#wp-player2 {
  width: 84px;
  height: calc(39px + 3em);
}

#wp-player1,
#wp-player2,
#wp-ball,
#wp-score {
  position: fixed;
}

#wp-score {
  transform: translateX(-4ch);
}

#wp-score:-moz-locale-dir(rtl) {
  transform: translateX(4ch);
}

#wp-lives {
  transform: translate(-4ch, 1ch);
}

#wp-lives:-moz-locale-dir(rtl) {
  transform: translate(4ch, 1ch);
}

#wp-lives[lives="5"] {
  height: 100px;
}

#wp-lives[lives="4"] {
  height: 80px;
}

#wp-lives[lives="3"] {
  height: 60px;
}

#wp-lives[lives="2"] {
  height: 40px;
}

#wp-lives[lives="1"] {
  height: 20px;
}

#customization-pong-arena[lives="0"] > #wp-ball {
  animation: game-over 4s forwards ease;
}

@keyframes game-over {
  0% {
    transform: scale(1) translateY(0) rotateZ(0);
    opacity: 1;
  }
  15% {
    transform: scale(2) translateY(-10px) rotateZ(-90deg);
  }
  35% {
    opacity: 1;
  }
  65% {
    opacity: 0;
  }
  100% {
    transform: scale(2) translateY(300px) rotateZ(-2.5turn);
    opacity: 0;
  }
}

@keyframes kcode {
  0% { border-color: rgb(195,17,206); color: rgb(195,17,206); }
  20% { border-color: rgb(252,82,27); color: rgb(252,82,27); }
  40% { border-color: rgb(251,179,0); color: rgb(251,179,0); }
  60% { border-color: rgb(105,211,0); color: rgb(105,211,0); }
  80% { border-color: rgb(20,155,249); color: rgb(20,155,249); }
  100% { border-color: rgb(195,17,206); color: rgb(195,17,206); }
}

/* End customization mode */


/* UI Tour */

#UITourHighlightContainer {
  -moz-appearance: none;
  -moz-window-shadow: none;
  border: none;
  background-color: transparent;
  /* This is a buffer to compensate for the movement in the "wobble" effect,
     and for the box-shadow of #UITourHighlight. */
  padding: 4px;
  /* Compensate the displacement caused by padding. */
  margin: -4px;
}

#UITourHighlight {
  background-color: rgba(0, 200, 215, 0.3);
  min-height: 24px;
  min-width: 24px;
}

#UITourHighlight.rounded-highlight {
  border-radius: 4px;
}

#UITourTooltipBody {
  -moz-box-align: start;
}

#UITourTooltipTitleContainer {
  -moz-box-align: start;
  margin-bottom: 10px;
}

#UITourTooltipIcon {
  width: 48px;
  height: 48px;
  margin-inline-end: 10px;
}

#UITourTooltipTitle,
#UITourTooltipDescription {
  max-width: 20rem;
}

#UITourTooltipTitle {
  font-size: 1.45rem;
  font-weight: bold;
  margin: 0;
}

#UITourTooltipDescription {
  margin-inline-start: 0;
  margin-inline-end: 0;
  font-size: 1.15rem;
  line-height: 1.8rem;
  margin-bottom: 0; /* Override global.css */
}

#UITourTooltipClose {
  position: relative;
  -moz-appearance: none;
  border: none;
  background-color: transparent;
  min-width: 0;
  margin-inline-start: 4px;
  margin-top: -2px;
}

#UITourTooltipClose > .toolbarbutton-text {
  display: none;
}

#UITourTooltipButtons {
  -moz-box-pack: end;
  background-color: hsla(210,4%,10%,.07);
  border-top: 1px solid hsla(210,4%,10%,.14);
  margin: 10px -16px -16px;
  padding: 16px;
}

#UITourTooltipButtons > label,
#UITourTooltipButtons > button {
  margin: 0 15px;
}

#UITourTooltipButtons > label:first-child,
#UITourTooltipButtons > button:first-child {
  margin-inline-start: 0;
}

#UITourTooltipButtons > label:last-child,
#UITourTooltipButtons > button:last-child {
  margin-inline-end: 0;
}

#UITourTooltipButtons > button[image] > .button-box > .button-icon {
  width: 16px;
  height: 16px;
  margin-inline-end: 5px;
}

#UITourTooltipButtons > label,
#UITourTooltipButtons > button .button-text {
  font-size: 1.15rem;
}

#UITourTooltipButtons > button:not(.button-link) {
  -moz-appearance: none;
  background-color: rgb(251,251,251);
  border-radius: 3px;
  border: 1px solid;
  border-color: rgb(192,192,192);
  color: rgb(71,71,71);
  padding: 4px 30px;
  transition-property: background-color, border-color;
  transition-duration: 150ms;
}

#UITourTooltipButtons > button:not(.button-link):not(:active):hover {
  background-color: hsla(210,4%,10%,.15);
  border-color: hsla(210,4%,10%,.15);
  box-shadow: 0 1px 0 0 hsla(210,4%,10%,.05) inset;
}

#UITourTooltipButtons > label,
#UITourTooltipButtons > button.button-link {
  -moz-appearance: none;
  background: transparent;
  border: none;
  box-shadow: none;
  color: rgba(0,0,0,0.35);
  padding-left: 10px;
  padding-right: 10px;
}

#UITourTooltipButtons > button.button-link:hover {
  color: black;
}

/* The primary button gets the same color as the customize button. */
#UITourTooltipButtons > button.button-primary {
  background-color: rgb(116,191,67);
  color: white;
  padding-left: 30px;
  padding-right: 30px;
}

#UITourTooltipButtons > button.button-primary:not(:active):hover {
  background-color: rgb(105,173,61);
}

#UITourHighlight {
  /* Below are some fixes for people without an X compositor on Linux.
     This is why we can't have nice things: */
  /* Animations don't repaint properly without an X compositor. */
  animation-name: none !important;
  /* Opacity rounds to 0 or 1 on Linux without an X compositor, making the
     background color not visible. Anti-aliasing is not available either. Make a
     thicker outline and cancel border-radius for that case. */
     outline: 4px solid rgb(0,200,215);
     border-radius: 0 !important;
}

#UITourTooltipDescription {
  font-size: 1.05rem;
}

#UITourTooltipClose {
  margin-inline-end: -4px;
}

/**
 * Override the --arrowpanel-padding so the background extends
 * to the sides and bottom of the panel.
 */
#UITourTooltipButtons {
  margin-left: -10px;
  margin-bottom: -10px;
}

#context-navigation > .menuitem-iconic {
  -moz-box-flex: 1;
  -moz-box-pack: center;
  -moz-box-align: center;
}

#context-navigation > .menuitem-iconic > .menu-iconic-left {
  -moz-appearance: none;
}

#context-navigation > .menuitem-iconic > .menu-iconic-left > .menu-iconic-icon {
  width: 16px;
  height: 16px;
  margin: 7px;
  -moz-context-properties: fill;
  fill: currentColor;
}

#context-back {
  list-style-image: url("chrome://browser/skin/back.svg");
}

#context-forward {
  list-style-image: url("chrome://browser/skin/forward.svg");
}

#context-reload {
  list-style-image: url("chrome://browser/skin/reload.svg");
}

#context-stop {
  list-style-image: url("chrome://browser/skin/stop.svg");
}

#context-bookmarkpage {
  list-style-image: url("chrome://browser/skin/bookmark-hollow.svg");
}

#context-bookmarkpage[starred=true] {
  list-style-image: url("chrome://browser/skin/bookmark.svg");
}

#context-back:-moz-locale-dir(rtl),
#context-forward:-moz-locale-dir(rtl),
#context-reload:-moz-locale-dir(rtl) {
  transform: scaleX(-1);
}

#context-media-eme-learnmore {
  list-style-image: url("chrome://browser/skin/drm-icon.svg");
  -moz-context-properties: fill;
  fill: currentColor;
}

#context-navigation > .menuitem-iconic > .menu-iconic-left {
  /* override toolkit/themes/linux/global/menu.css */
  padding-inline-end: 0 !important;
  margin-inline-end: 0 !important;
}

.browser-extension-panel > .panel-arrowcontainer > .panel-arrowcontent {
  padding: 0;
  overflow: hidden;
}

.webextension-popup-browser,
.webextension-popup-stack {
  border-radius: inherit;
}

/* Prevent movement in the restore-tabs-button when it's clicked. */
.restore-tabs-button:hover:active:not([disabled="true"]) {
  padding: 3px;
}

/* Hide the titlebar explicitly on versions of GTK+ where
 * it's rendered by window manager. */
@media (-moz-gtk-csd-available: 0) {
  #titlebar {
    display: none;
  }
}

/* We draw to titlebar when Gkt+ CSD is available */
@media (-moz-gtk-csd-available) {
  :root[tabsintitlebar] > #titlebar:-moz-lwtheme {
    visibility: hidden;
  }
  :root[tabsintitlebar] #titlebar-content:-moz-lwtheme {
    visibility: visible;
  }

  :root[tabsintitlebar] > #titlebar {
    -moz-appearance: -moz-window-titlebar-maximized;
  }
  :root[tabsintitlebar][sizemode="normal"] > #titlebar {
    -moz-appearance: -moz-window-titlebar;
  }

  /* Add extra space to titlebar for dragging */
  :root[sizemode="normal"][chromehidden~="menubar"] #TabsToolbar,
  :root[sizemode="normal"] #toolbar-menubar[autohide="true"][inactive] + #TabsToolbar {
    padding-top: var(--space-above-tabbar);
  }

  /* Private browsing and accessibility indicators */
  :root[sizemode="normal"][chromehidden~="menubar"] #TabsToolbar > .private-browsing-indicator,
  :root[sizemode="normal"][chromehidden~="menubar"] #TabsToolbar > .accessibility-indicator,
  :root[sizemode="normal"] #toolbar-menubar[autohide="true"][inactive] + #TabsToolbar > .private-browsing-indicator,
  :root[sizemode="normal"] #toolbar-menubar[autohide="true"][inactive] + #TabsToolbar > .accessibility-indicator {
    padding-top: calc(-1 * var(--space-above-tabbar));
  }

  /* Make #TabsToolbar transparent as we style underlying #titlebar with
   * -moz-window-titlebar (Gtk+ theme).
   */
  :root[tabsintitlebar][sizemode="normal"]:not([inFullscreen]) #TabsToolbar,
  :root[tabsintitlebar][sizemode="maximized"] #TabsToolbar,
  :root[tabsintitlebar] #toolbar-menubar {
    -moz-appearance: none;
  }

  /* The button box must appear on top of the navigator-toolbox in order for
   * click and hover mouse events to work properly for the button in the restored
   * window state. Otherwise, elements in the navigator-toolbox, like the menubar,
   * can swallow those events.
   */
  #titlebar-buttonbox {
    z-index: 1;
    -moz-box-align: center;
    padding-top: 2px;
    padding-bottom: 2px;
  }

  /* Render titlebar command buttons according to system config.
   * Use full scale icons here as the Gtk+ does.
   */
  @media (-moz-gtk-csd-minimize-button) {
    #titlebar-min {
      -moz-appearance: -moz-window-button-minimize;
    }
  }
  @media (-moz-gtk-csd-minimize-button: 0) {
    #titlebar-min {
      display: none;
    }
  }

  @media (-moz-gtk-csd-maximize-button) {
    #titlebar-max {
      -moz-appearance: -moz-window-button-maximize;
    }
    :root[sizemode="maximized"] #titlebar-max {
      -moz-appearance: -moz-window-button-restore;
    }
  }
  @media (-moz-gtk-csd-maximize-button: 0) {
    #titlebar-max {
      display: none;
    }
  }

  @media (-moz-gtk-csd-close-button) {
    #titlebar-close {
      -moz-appearance: -moz-window-button-close;
    }
  }
  @media (-moz-gtk-csd-close-button: 0) {
    #titlebar-close {
      display: none;
    }
  }
}
PK
!<^�$�`
`
-chrome/browser/content/browser/tabbrowser.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

.tabbrowser-tabpanels {
  -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-tabpanels");
}

.tabbrowser-arrowscrollbox {
  -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-arrowscrollbox");
}

.tab-close-button[pinned],
#tabbrowser-tabs[closebuttons="activetab"] > .tabbrowser-tab > .tab-stack > .tab-content > .tab-close-button:not([selected="true"]),
.tab-icon-image:not([src]):not([pinned]):not([crashed])[selected],
.tab-icon-image:not([src]):not([pinned]):not([crashed]):not([sharing]),
.tab-icon-image[busy],
.tab-throbber:not([busy]),
.tab-throbber-fallback:not([busy]),
.tab-icon-sound:not([soundplaying]):not([muted]):not([activemedia-blocked]),
.tab-icon-sound[pinned],
.tab-sharing-icon-overlay,
.tab-icon-overlay {
  display: none;
}

.tab-sharing-icon-overlay[sharing]:not([selected]),
.tab-icon-overlay[soundplaying][pinned],
.tab-icon-overlay[muted][pinned],
.tab-icon-overlay[activemedia-blocked][pinned],
.tab-icon-overlay[crashed] {
  display: -moz-box;
}

.tab-label {
  white-space: nowrap;
}

.tab-label-container {
  overflow: hidden;
}

.tab-label-container[pinned] {
  width: 0;
}

.tab-label-container[textoverflow][labeldirection=ltr]:not([pinned]),
.tab-label-container[textoverflow]:not([labeldirection]):not([pinned]):-moz-locale-dir(ltr) {
  direction: ltr;
  mask-image: linear-gradient(to left, transparent, black 2em);
}

.tab-label-container[textoverflow][labeldirection=rtl]:not([pinned]),
.tab-label-container[textoverflow]:not([labeldirection]):not([pinned]):-moz-locale-dir(rtl) {
  direction: rtl;
  mask-image: linear-gradient(to right, transparent, black 2em);
}

tabpanels {
  background-color: transparent;
}

.tab-drop-indicator-box {
  -moz-box-align: end;
}

.tab-drop-indicator {
  position: relative;
  z-index: 2;
}

/* Apply crisp rendering for favicons at exactly 2dppx resolution */
@media (resolution: 2dppx) {
  .tab-icon-image {
    image-rendering: -moz-crisp-edges;
  }
}

.closing-tabs-spacer {
  pointer-events: none;
}

#tabbrowser-tabs:not(:hover) > .tabbrowser-arrowscrollbox > .closing-tabs-spacer {
  transition: width .15s ease-out;
}

browser[blank],
browser[pendingpaint] {
  opacity: 0;
}

#tabbrowser-tabpanels[pendingpaint] {
  background-image: url(chrome://browser/skin/tabbrowser/pendingpaint.png);
  background-repeat: no-repeat;
  background-position: center center;
  background-color: #f9f9f9 !important;
  background-size: 30px;
}
PK
!<ITp8==Cchrome/browser/skin/classic/browser/tabbrowser/tabDragIndicator.png�PNG


IHDR��w&sRGB����IDATx��nQEgM5�:��+�m#�l�v�ڶm�[��>|{�t�y��q�����u�c��3�x�&!��@ܝ�c�kW7f!��`0^n^�8����a��9�3��P6��Õ,��b���>*NP��,�����g.<^<ao��ӻؚ�T�,.��� $�}S��i5hUZ��G*W�7xf��o�$����!OW�΀��|>y�Ur5d?r|>y�˿m�vۜ>NRC��ڌIEND�B`�PK
!<;�z��2chrome/browser/skin/classic/browser/arrow-left.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M6.414 8l4.293-4.293a1 1 0 0 0-1.414-1.414l-5 5a1 1 0 0 0 0 1.414l5 5a1 1 0 0 0 1.414-1.414z"/>
</svg>
PK
!<|���Ichrome/browser/skin/classic/browser/tabbrowser/tab-overflow-indicator.png�PNG


IHDR"H�('��IDATx͘��b1D�\��c�@��2�6�n�d{�ۏ觌X�K�9#hj�k�,�0�RC��k
�P��L�5�F�`ޝ��H2�4���h��gDt���&��l�h_,��-=�~�US���њ8ꌒ0�q�{��"#4�*W<�G��mt�@��հ~e��?i�JXSޅ��a-t����す��G#�f3rt塍�h�y�W�L��Jw�F3{yu�����F=��Ĉ�(����Jnh�F�h�⩻v�"����nf^�l�a�AOs�;p�I���Z�s��Bh��&�
ʓ�]�Dh��|4�O����
��,ػ�cjW�!Fc!t�f#�~j�e�J�90���d�(�WE���40+������z�ȳX.0���P(׆�h��;�
�������PWp�Z��JS�Wu1{v'M�b#;�#�=��c=��~q�.-����X�#�p.rp�YSH�8�%(}�0b��WXi�!|���͂]1jɥ�2���~��c���a��--�Њ%�V��I�6F�o�o�n�ddO*3�.…:����
�2C}�o�����V7���X�ny�I��Z�j��^"�	���O��TI�J#糮7R,WA�հ@z���Q��h�^��x!�CIEND�B`�PK
!<Y�O���9chrome/browser/skin/classic/browser/tabbrowser/newtab.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" fill="context-fill" fill-opacity="context-fill-opacity">
  <path d="M14 7H9V2a1 1 0 0 0-2 0v5H2a1 1 0 1 0 0 2h5v5a1 1 0 0 0 2 0V9h5a1 1 0 0 0 0-2z"/>
</svg>
PK
!<����,chrome/browser/skin/classic/browser/back.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M15,7H3.414L7.707,2.707A1,1,0,0,0,6.293,1.293l-6,6a1,1,0,0,0,0,1.414l6,6a1,1,0,0,0,1.414-1.414L3.414,9H15a1,1,0,0,0,0-2Z"/>
</svg>
PK
!<�L<��/chrome/browser/skin/classic/browser/forward.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M15.707,7.293l-6-6A1,1,0,0,0,8.293,2.707L12.586,7H1A1,1,0,0,0,1,9H12.586L8.293,13.293a1,1,0,1,0,1.414,1.414l6-6A1,1,0,0,0,15.707,7.293Z"/>
</svg>
PK
!<�l2&&.chrome/browser/skin/classic/browser/reload.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M15,1a1,1,0,0,0-1,1V4.418A6.995,6.995,0,1,0,8,15a6.954,6.954,0,0,0,4.95-2.05,1,1,0,0,0-1.414-1.414A5.019,5.019,0,1,1,12.549,6H10a1,1,0,0,0,0,2h5a1,1,0,0,0,1-1V2A1,1,0,0,0,15,1Z"/>
</svg>
PK
!<qM���,chrome/browser/skin/classic/browser/home.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M15.707,7.293l-7-7a1,1,0,0,0-1.414,0l-7,7A1,1,0,0,0,1.707,8.707L2,8.414V14a2,2,0,0,0,2,2h8a2,2,0,0,0,2-2V8.414l.293.293a1,1,0,0,0,1.414-1.414ZM8,11.5a.5.5,0,1,1,.5.5A.5.5,0,0,1,8,11.5ZM12,13a1,1,0,0,1-1,1H10V9A1,1,0,0,0,9,8H7A1,1,0,0,0,6,9v5H5a1,1,0,0,1-1-1V6.414l4-4,4,4Z"/>
</svg>
PK
!<o".���4chrome/browser/skin/classic/browser/search-glass.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M15.707 14.293l-4.822-4.822a6.019 6.019 0 1 0-1.414 1.414l4.822 4.822a1 1 0 0 0 1.414-1.414zM6 10a4 4 0 1 1 4-4 4 4 0 0 1-4 4z"/>
</svg>
PK
!<�
�TT/chrome/browser/skin/classic/browser/library.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M5 3a1 1 0 0 0-1 1v10a1 1 0 0 0 2 0V4a1 1 0 0 0-1-1zm3-1a1 1 0 0 0-1 1v11a1 1 0 0 0 2 0V3a1 1 0 0 0-1-1zm7.939 11.658l-4-11a1 1 0 1 0-1.879.684l4 11a1 1 0 1 0 1.879-.684zM2 1a1 1 0 0 0-1 1v12a1 1 0 0 0 2 0V2a1 1 0 0 0-1-1z"/>
</svg>
PK
!<��|��,chrome/browser/skin/classic/browser/menu.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M3,4H13a1,1,0,0,0,0-2H3A1,1,0,0,0,3,4ZM13,7H3A1,1,0,0,0,3,9H13a1,1,0,0,0,0-2Zm0,5H3a1,1,0,0,0,0,2H13a1,1,0,0,0,0-2Z"/>
</svg>
PK
!<U�,uu@chrome/browser/skin/classic/browser/places/toolbarDropMarker.png�PNG


IHDR	���H<IDATx�R�A܏��kp��kl;�����m۶�[��'֡�\U<d]�w�|q�u|����9��~��f��\<O9�K{o�OϽ�gs,Ad"Y�;�xL3�;�Z��V��f������ƍT(��kϒ2��K�TGܗ��ʏ��80����u�֜A)U�]�D&�Ñ�[~l|�էvD������8u\}`�)�a��N]4�9�8;a2����o�Bp�q�눸b(��dB
f��D>Q�A`"+Ɂ
$`���
uBˠ7D��Qh�QlAܛ ���N��#��A�x���%�;�Z�$�*�wR�'�}��1a'BpIEND�B`�PK
!<�t$�II/chrome/browser/skin/classic/browser/chevron.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M8.707,7.293l-5-5A1,1,0,0,0,2.293,3.707L6.586,8,2.293,12.293a1,1,0,1,0,1.414,1.414l5-5A1,1,0,0,0,8.707,7.293Zm6,0-5-5A1,1,0,0,0,8.293,3.707L12.586,8,8.293,12.293a1,1,0,1,0,1.414,1.414l5-5A1,1,0,0,0,14.707,7.293Z"/>
</svg>
PK
!<䴅�II0chrome/browser/skin/classic/browser/sidebars.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16" fill="context-fill" fill-opacity="context-fill-opacity">
  <path d="M3 1h10c1.655.004 2.996 1.345 3 3v8c-.005 1.655-1.345 2.995-3 3H3c-1.656-.003-2.997-1.344-3-3V4c.007-1.654 1.346-2.993 3-3zm11 11V4c0-.552-.448-1-1-1H8v10h5c.552 0 1-.448 1-1zM2 12c0 .552.448 1 1 1h4V3H3c-.552 0-1 .448-1 1v8z"/>
  <path d="M3.5 5h2c.276 0 .5-.224.5-.5S5.776 4 5.5 4h-2c-.276 0-.5.224-.5.5s.224.5.5.5zm0 2h2c.276 0 .5-.224.5-.5S5.776 6 5.5 6h-2c-.276 0-.5.224-.5.5s.224.5.5.5zm1 2h1c.276 0 .5-.224.5-.5S5.776 8 5.5 8h-1c-.276 0-.5.224-.5.5s.224.5.5.5z"/>
</svg>
PK
!<,��X��modules/SchedulePressure.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = ["SchedulePressure"];

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.defineModuleGetter(this, "TelemetryStopwatch",
  "resource://gre/modules/TelemetryStopwatch.jsm");
XPCOMUtils.defineLazyPreferenceGetter(this, "SCHEDULE_PRESSURE_ENABLED",
  "browser.schedulePressure.enabled", true);
XPCOMUtils.defineLazyPreferenceGetter(this, "TIMEOUT_AMOUNT",
  "browser.schedulePressure.timeoutMs", 300);

/**
 * The SchedulePressure object provides the ability to alter
 * the behavior of a program based on the idle activity of the
 * host machine.
 */
var SchedulePressure = {
  _idleCallbackWeakMap: new WeakMap(),
  _setTimeoutWeakMap: new WeakMap(),
  _telemetryCallbackWeakMap: new WeakMap(),

  _createTimeoutFn(window, callbackFn) {
    return () => {
      if (window.closed) {
        TelemetryStopwatch.cancel("FX_SCHEDULE_PRESSURE_IDLE_SAMPLE_MS", window);
        this._telemetryCallbackWeakMap.delete(window);
        return;
      }
      let nextCallbackId = window.requestIdleCallback(callbackFn, {timeout: TIMEOUT_AMOUNT});
      this._idleCallbackWeakMap.set(window, nextCallbackId);

      // Don't create another timeout-less idle callback if the first
      // one hasn't completed yet.
      if (!this._telemetryCallbackWeakMap.has(window) &&
          TelemetryStopwatch.start("FX_SCHEDULE_PRESSURE_IDLE_SAMPLE_MS", window)) {
        let telemetryCallbackId = window.requestIdleCallback(() => {
          TelemetryStopwatch.finish("FX_SCHEDULE_PRESSURE_IDLE_SAMPLE_MS", window);
          this._telemetryCallbackWeakMap.delete(window);
        });
        this._telemetryCallbackWeakMap.set(window, telemetryCallbackId);
      }
    };
  },

  /**
   * Starts an interval timeout that periodically waits for
   * an idle callback. If the idle callback fails to get called
   * within the timeout specified by TIMEOUT_AMOUNT, the
   * highPressureFn callback will get called. Otherwise the
   * lowPressureFn callback will get called.
   *
   * @param  window
   *         The DOM window of the requestee.
   * @param  options
   *           highPressureFn
   *           A function that will be called when the idle callback
   *           fails to be called within the time specified by TIMEOUT_AMOUNT.
   *           Returning an object with property of `continueMonitoring` set
   *           to `false` will prevent further monitoring.
   *           lowPressureFn
   *           A function that will be called when the idle callback
   *           gets called within the time specified by TIMEOUT_AMOUNT.
   *           Returning an object with property of `continueMonitoring` set
   *           to `false` will prevent further monitoring.
   */
  startMonitoring(window, {highPressureFn, lowPressureFn}) {
    if (!SCHEDULE_PRESSURE_ENABLED ||
        this._setTimeoutWeakMap.has(window) ||
        this._idleCallbackWeakMap.has(window)) {
      return;
    }

    let callbackFn = idleDeadline => {
      if (window.closed) {
        return;
      }

      let result;
      if (idleDeadline.didTimeout) {
        try {
          result = highPressureFn();
        } catch (ex) {}
      } else {
        try {
          result = lowPressureFn();
        } catch (ex) {}
      }

      if (result && !result.continueMonitoring) {
        return;
      }

      this._setTimeoutWeakMap.set(window,
        window.setTimeout(this._createTimeoutFn(window, callbackFn), TIMEOUT_AMOUNT));
    };

    this._setTimeoutWeakMap.set(window,
      window.setTimeout(this._createTimeoutFn(window, callbackFn), TIMEOUT_AMOUNT));
  },

  /**
   * Stops the interval timeout that periodically waits for
   * an idle callback.
   *
   * @param  window
   *         The DOM window of the requestee.
   */
  stopMonitoring(window) {
    function removeFromMapAndCancelTimeout(map, cancelFn) {
      if (map.has(window)) {
        cancelFn(map.get(window));
        map.delete(window);
      }
    }

    TelemetryStopwatch.cancel("FX_SCHEDULE_PRESSURE_IDLE_SAMPLE_MS", window);
    removeFromMapAndCancelTimeout(this._setTimeoutWeakMap, window.clearTimeout);
    removeFromMapAndCancelTimeout(this._idleCallbackWeakMap, window.cancelIdleCallback);
    removeFromMapAndCancelTimeout(this._telemetryCallbackWeakMap, window.cancelIdleCallback);
  },
};
PK
!<��Z�  ,chrome/browser/skin/classic/browser/stop.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M9.414 8l5.293-5.293a1 1 0 0 0-1.414-1.414L8 6.586 2.707 1.293a1 1 0 0 0-1.414 1.414L6.586 8l-5.293 5.293a1 1 0 1 0 1.414 1.414L8 9.414l5.293 5.293a1 1 0 0 0 1.414-1.414z"/>
</svg>
PK
!<^iu�77modules/ReaderParent.jsm// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = [ "ReaderParent" ];

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");

ChromeUtils.defineModuleGetter(this, "PlacesUtils", "resource://gre/modules/PlacesUtils.jsm");
ChromeUtils.defineModuleGetter(this, "ReaderMode", "resource://gre/modules/ReaderMode.jsm");
ChromeUtils.defineModuleGetter(this, "UITour", "resource:///modules/UITour.jsm");

const gStringBundle = Services.strings.createBundle("chrome://global/locale/aboutReader.properties");

var ReaderParent = {
  // Listeners are added in nsBrowserGlue.js
  receiveMessage(message) {
    switch (message.name) {
      case "Reader:FaviconRequest": {
        if (message.target.messageManager) {
          let faviconUrl = PlacesUtils.promiseFaviconLinkUrl(message.data.url);
          faviconUrl.then(function onResolution(favicon) {
            message.target.messageManager.sendAsyncMessage("Reader:FaviconReturn", {
              url: message.data.url,
              faviconUrl: favicon.pathQueryRef.replace(/^favicon:/, "")
            });
          },
          function onRejection(reason) {
            Cu.reportError("Error requesting favicon URL for about:reader content: " + reason);
          }).catch(Cu.reportError);
        }
        break;
      }

      case "Reader:UpdateReaderButton": {
        let browser = message.target;
        if (message.data && message.data.isArticle !== undefined) {
          browser.isArticle = message.data.isArticle;
        }
        this.updateReaderButton(browser);
        break;
      }
    }
  },

  updateReaderButton(browser) {
    let win = browser.ownerGlobal;
    if (browser != win.gBrowser.selectedBrowser) {
      return;
    }

    let button = win.document.getElementById("reader-mode-button");
    let command = win.document.getElementById("View:ReaderView");
    let key = win.document.getElementById("key_toggleReaderMode");
    // aria-reader is not a real ARIA attribute. However, this will cause
    // Gecko accessibility to expose the "reader" object attribute. We do this
    // so that the reader state is easy for accessibility clients to access
    // programmatically.
    if (browser.currentURI.spec.startsWith("about:reader")) {
      button.setAttribute("readeractive", true);
      button.hidden = false;
      let closeText = gStringBundle.GetStringFromName("readerView.close");
      command.setAttribute("label", closeText);
      command.setAttribute("hidden", false);
      command.setAttribute("accesskey", gStringBundle.GetStringFromName("readerView.close.accesskey"));
      key.setAttribute("disabled", false);
      browser.setAttribute("aria-reader", "active");
    } else {
      button.removeAttribute("readeractive");
      button.hidden = !browser.isArticle;
      let enterText = gStringBundle.GetStringFromName("readerView.enter");
      command.setAttribute("label", enterText);
      command.setAttribute("hidden", !browser.isArticle);
      command.setAttribute("accesskey", gStringBundle.GetStringFromName("readerView.enter.accesskey"));
      key.setAttribute("disabled", !browser.isArticle);
      if (browser.isArticle) {
        browser.setAttribute("aria-reader", "available");
      } else {
        browser.removeAttribute("aria-reader");
      }
    }
  },

  forceShowReaderIcon(browser) {
    browser.isArticle = true;
    this.updateReaderButton(browser);
  },

  buttonClick(event) {
    if (event.button != 0) {
      return;
    }
    this.toggleReaderMode(event);
  },

  toggleReaderMode(event) {
    let win = event.target.ownerGlobal;
    let browser = win.gBrowser.selectedBrowser;
    browser.messageManager.sendAsyncMessage("Reader:ToggleReaderMode");
  },

  /**
   * Gets an article for a given URL. This method will download and parse a document.
   *
   * @param url The article URL.
   * @param browser The browser where the article is currently loaded.
   * @return {Promise}
   * @resolves JS object representing the article, or null if no article is found.
   */
  async _getArticle(url, browser) {
    return ReaderMode.downloadAndParseDocument(url).catch(e => {
      if (e && e.newURL) {
        // Pass up the error so we can navigate the browser in question to the new URL:
        throw e;
      }
      Cu.reportError("Error downloading and parsing document: " + e);
      return null;
    });
  }
};
PK
!<�8����modules/webrtcUI.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = ["webrtcUI"];

ChromeUtils.import("resource:///modules/syncedtabs/EventEmitter.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");

ChromeUtils.defineModuleGetter(this, "AppConstants",
                               "resource://gre/modules/AppConstants.jsm");
ChromeUtils.defineModuleGetter(this, "PluralForm",
                               "resource://gre/modules/PluralForm.jsm");
ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
                               "resource://gre/modules/PrivateBrowsingUtils.jsm");
ChromeUtils.defineModuleGetter(this, "SitePermissions",
                               "resource:///modules/SitePermissions.jsm");

XPCOMUtils.defineLazyGetter(this, "gBrandBundle", function() {
  return Services.strings.createBundle("chrome://branding/locale/brand.properties");
});

var webrtcUI = {
  peerConnectionBlockers: new Set(),
  emitter: new EventEmitter(),

  init() {
    Services.obs.addObserver(maybeAddMenuIndicator, "browser-delayed-startup-finished");
    Services.ppmm.addMessageListener("child-process-shutdown", this);
  },

  uninit() {
    Services.obs.removeObserver(maybeAddMenuIndicator, "browser-delayed-startup-finished");

    if (gIndicatorWindow) {
      gIndicatorWindow.close();
      gIndicatorWindow = null;
    }
  },

  processIndicators: new Map(),
  activePerms: new Map(),

  get showGlobalIndicator() {
    for (let [, indicators] of this.processIndicators) {
      if (indicators.showGlobalIndicator)
        return true;
    }
    return false;
  },

  get showCameraIndicator() {
    for (let [, indicators] of this.processIndicators) {
      if (indicators.showCameraIndicator)
        return true;
    }
    return false;
  },

  get showMicrophoneIndicator() {
    for (let [, indicators] of this.processIndicators) {
      if (indicators.showMicrophoneIndicator)
        return true;
    }
    return false;
  },

  get showScreenSharingIndicator() {
    let list = [""];
    for (let [, indicators] of this.processIndicators) {
      if (indicators.showScreenSharingIndicator)
        list.push(indicators.showScreenSharingIndicator);
    }

    let precedence =
      ["Screen", "Window", "Application", "Browser", ""];

    list.sort((a, b) => {
 return precedence.indexOf(a) -
                                 precedence.indexOf(b);
});

    return list[0];
  },

  _streams: [],
  // The boolean parameters indicate which streams should be included in the result.
  getActiveStreams(aCamera, aMicrophone, aScreen) {
    return webrtcUI._streams.filter(aStream => {
      let state = aStream.state;
      return aCamera && state.camera ||
             aMicrophone && state.microphone ||
             aScreen && state.screen;
    }).map(aStream => {
      let state = aStream.state;
      let types = {camera: state.camera, microphone: state.microphone,
                   screen: state.screen};
      let browser = aStream.browser;
      let browserWindow = browser.ownerGlobal;
      let tab = browserWindow.gBrowser &&
                browserWindow.gBrowser.getTabForBrowser(browser);
      return {uri: state.documentURI, tab, browser, types};
    });
  },

  swapBrowserForNotification(aOldBrowser, aNewBrowser) {
    for (let stream of this._streams) {
      if (stream.browser == aOldBrowser)
        stream.browser = aNewBrowser;
    }
  },

  forgetActivePermissionsFromBrowser(aBrowser) {
    webrtcUI.activePerms.delete(aBrowser.outerWindowID);
  },

  forgetStreamsFromBrowser(aBrowser) {
    this._streams = this._streams.filter(stream => stream.browser != aBrowser);
    webrtcUI.forgetActivePermissionsFromBrowser(aBrowser);
  },

  forgetStreamsFromProcess(aProcessMM) {
    // stream.processMM is null when e10s is disabled.
    this._streams =
      this._streams.filter(stream => stream.processMM &&
                                     stream.processMM != aProcessMM);
  },

  showSharingDoorhanger(aActiveStream) {
    let browserWindow = aActiveStream.browser.ownerGlobal;
    if (aActiveStream.tab) {
      browserWindow.gBrowser.selectedTab = aActiveStream.tab;
    } else {
      aActiveStream.browser.focus();
    }
    browserWindow.focus();
    let identityBox = browserWindow.document.getElementById("identity-box");
    if (AppConstants.platform == "macosx" && !Services.focus.activeWindow) {
      browserWindow.addEventListener("activate", function() {
        Services.tm.dispatchToMainThread(function() {
          identityBox.click();
        });
      }, {once: true});
      Cc["@mozilla.org/widget/macdocksupport;1"].getService(Ci.nsIMacDockSupport)
        .activateApplication(true);
      return;
    }
    identityBox.click();
  },

  updateWarningLabel(aMenuList) {
    let type = aMenuList.selectedItem.getAttribute("devicetype");
    let document = aMenuList.ownerDocument;
    document.getElementById("webRTC-all-windows-shared").hidden = type != "Screen";
  },

  // Add-ons can override stock permission behavior by doing:
  //
  //   webrtcUI.addPeerConnectionBlocker(function(aParams) {
  //     // new permission checking logic
  //   }));
  //
  // The blocking function receives an object with origin, callID, and windowID
  // parameters.  If it returns the string "deny" or a Promise that resolves
  // to "deny", the connection is immediately blocked.  With any other return
  // value (though the string "allow" is suggested for consistency), control
  // is passed to other registered blockers.  If no registered blockers block
  // the connection (or of course if there are no registered blockers), then
  // the connection is allowed.
  //
  // Add-ons may also use webrtcUI.on/off to listen to events without
  // blocking anything:
  //   peer-request-allowed is emitted when a new peer connection is
  //                        established (and not blocked).
  //   peer-request-blocked is emitted when a peer connection request is
  //                        blocked by some blocking connection handler.
  //   peer-request-cancel is emitted when a peer-request connection request
  //                       is canceled.  (This would typically be used in
  //                       conjunction with a blocking handler to cancel
  //                       a user prompt or other work done by the handler)
  addPeerConnectionBlocker(aCallback) {
    this.peerConnectionBlockers.add(aCallback);
  },

  removePeerConnectionBlocker(aCallback) {
    this.peerConnectionBlockers.delete(aCallback);
  },

  on(...args) {
    return this.emitter.on(...args);
  },

  off(...args) {
    return this.emitter.off(...args);
  },

  // Listeners and observers are registered in nsBrowserGlue.js
  receiveMessage(aMessage) {
    switch (aMessage.name) {

      case "rtcpeer:Request": {
        let params = Object.freeze(Object.assign({
          origin: aMessage.target.contentPrincipal.origin
        }, aMessage.data));

        let blockers = Array.from(this.peerConnectionBlockers);

        (async function() {
          for (let blocker of blockers) {
            try {
              let result = await blocker(params);
              if (result == "deny") {
                return false;
              }
            } catch (err) {
              Cu.reportError(`error in PeerConnection blocker: ${err.message}`);
            }
          }
          return true;
        })().then(decision => {
          let message;
          if (decision) {
            this.emitter.emit("peer-request-allowed", params);
            message = "rtcpeer:Allow";
          } else {
            this.emitter.emit("peer-request-blocked", params);
            message = "rtcpeer:Deny";
          }

          aMessage.target.messageManager.sendAsyncMessage(message, {
            callID: params.callID,
            windowID: params.windowID,
          });
        });
        break;
      }
      case "rtcpeer:CancelRequest": {
        let params = Object.freeze({
          origin: aMessage.target.contentPrincipal.origin,
          callID: aMessage.data
        });
        this.emitter.emit("peer-request-cancel", params);
        break;
      }
      case "webrtc:Request":
        prompt(aMessage.target, aMessage.data);
        break;
      case "webrtc:StopRecording":
        stopRecording(aMessage.target, aMessage.data);
        break;
      case "webrtc:CancelRequest":
        removePrompt(aMessage.target, aMessage.data);
        break;
      case "webrtc:UpdatingIndicators":
        webrtcUI.forgetStreamsFromProcess(aMessage.target);
        break;
      case "webrtc:UpdateGlobalIndicators":
        updateIndicators(aMessage.data, aMessage.target);
        break;
      case "webrtc:UpdateBrowserIndicators":
        let id = aMessage.data.windowId;
        aMessage.targetFrameLoader.QueryInterface(Ci.nsIFrameLoader);
        let processMM =
          aMessage.targetFrameLoader.messageManager.processMessageManager;
        let index;
        for (index = 0; index < webrtcUI._streams.length; ++index) {
          let stream = webrtcUI._streams[index];
          if (stream.state.windowId == id && stream.processMM == processMM)
            break;
        }
        // If there's no documentURI, the update is actually a removal of the
        // stream, triggered by the recording-window-ended notification.
        if (!aMessage.data.documentURI && index < webrtcUI._streams.length) {
          webrtcUI._streams.splice(index, 1);
        } else {
          webrtcUI._streams[index] = {
            browser: aMessage.target,
            processMM,
            state: aMessage.data
          };
        }
        let tabbrowser = aMessage.target.ownerGlobal.gBrowser;
        if (tabbrowser)
          tabbrowser.setBrowserSharing(aMessage.target, aMessage.data);
        break;
      case "child-process-shutdown":
        webrtcUI.processIndicators.delete(aMessage.target);
        webrtcUI.forgetStreamsFromProcess(aMessage.target);
        updateIndicators(null, null);
        break;
    }
  }
};

function denyRequest(aBrowser, aRequest) {
  aBrowser.messageManager.sendAsyncMessage("webrtc:Deny",
                                           {callID: aRequest.callID,
                                            windowID: aRequest.windowID});
}

function getHost(uri, href) {
  let host;
  try {
    if (!uri) {
      uri = Services.io.newURI(href);
    }
    host = uri.host;
  } catch (ex) {}
  if (!host) {
    if (uri && uri.scheme.toLowerCase() == "about") {
      // For about URIs, just use the full spec, without any #hash parts.
      host = uri.specIgnoringRef;
    } else {
      // This is unfortunate, but we should display *something*...
      const kBundleURI = "chrome://browser/locale/browser.properties";
      let bundle = Services.strings.createBundle(kBundleURI);
      host = bundle.GetStringFromName("getUserMedia.sharingMenuUnknownHost");
    }
  }
  return host;
}

function stopRecording(aBrowser, aRequest) {
  let outerWindowID = aBrowser.outerWindowID;

  if (!webrtcUI.activePerms.has(outerWindowID)) {
    return;
  }

  if (!aRequest.rawID) {
    webrtcUI.activePerms.delete(outerWindowID);
  } else {
    let set = webrtcUI.activePerms.get(outerWindowID);
    set.delete(aRequest.windowID + aRequest.mediaSource + aRequest.rawID);
  }
}

function prompt(aBrowser, aRequest) {
  let { audioDevices, videoDevices, sharingScreen, sharingAudio,
        requestTypes } = aRequest;

  // If the user has already denied access once in this tab,
  // deny again without even showing the notification icon.
  if ((audioDevices.length && SitePermissions
        .get(null, "microphone", aBrowser).state == SitePermissions.BLOCK) ||
      (videoDevices.length && SitePermissions
        .get(null, sharingScreen ? "screen" : "camera", aBrowser).state == SitePermissions.BLOCK)) {
    denyRequest(aBrowser, aRequest);
    return;
  }

  // Tell the browser to refresh the identity block display in case there
  // are expired permission states.
  aBrowser.dispatchEvent(new aBrowser.ownerGlobal
                                     .CustomEvent("PermissionStateChange"));

  let uri;
  try {
    // This fails for principals that serialize to "null", e.g. file URIs.
    uri = Services.io.newURI(aRequest.origin);
  } catch (e) {
    uri = Services.io.newURI(aRequest.documentURI);
  }

  let chromeDoc = aBrowser.ownerDocument;
  let stringBundle = chromeDoc.defaultView.gNavigatorBundle;

  // Mind the order, because for simplicity we're iterating over the list using
  // "includes()". This allows the rotation of string identifiers. We list the
  // full identifiers here so they can be cross-referenced more easily.
  let joinedRequestTypes = requestTypes.join("And");
  let stringId = [
    // Individual request types first.
    "getUserMedia.shareCamera2.message",
    "getUserMedia.shareMicrophone2.message",
    "getUserMedia.shareScreen3.message",
    "getUserMedia.shareAudioCapture2.message",
    // Combinations of the above request types last.
    "getUserMedia.shareCameraAndMicrophone2.message",
    "getUserMedia.shareCameraAndAudioCapture2.message",
    "getUserMedia.shareScreenAndMicrophone3.message",
    "getUserMedia.shareScreenAndAudioCapture3.message",
  ].find(id => id.includes(joinedRequestTypes));

  let message = stringBundle.getFormattedString(stringId, ["<>"], 1);

  let notification; // Used by action callbacks.
  let mainAction = {
    label: stringBundle.getString("getUserMedia.allow.label"),
    accessKey: stringBundle.getString("getUserMedia.allow.accesskey"),
    // The real callback will be set during the "showing" event. The
    // empty function here is so that PopupNotifications.show doesn't
    // reject the action.
    callback() {}
  };

  let secondaryActions = [
    {
      label: stringBundle.getString("getUserMedia.dontAllow.label"),
      accessKey: stringBundle.getString("getUserMedia.dontAllow.accesskey"),
      callback(aState) {
        denyRequest(notification.browser, aRequest);
        let scope = SitePermissions.SCOPE_TEMPORARY;
        if (aState && aState.checkboxChecked) {
          scope = SitePermissions.SCOPE_PERSISTENT;
        }
        if (audioDevices.length)
          SitePermissions.set(uri, "microphone",
                              SitePermissions.BLOCK, scope, notification.browser);
        if (videoDevices.length)
          SitePermissions.set(uri, sharingScreen ? "screen" : "camera",
                              SitePermissions.BLOCK, scope, notification.browser);
      }
    }
  ];

  let productName = gBrandBundle.GetStringFromName("brandShortName");

  let options = {
    name: getHost(uri),
    persistent: true,
    hideClose: !Services.prefs.getBoolPref("privacy.permissionPrompts.showCloseButton"),
    eventCallback(aTopic, aNewBrowser) {
      if (aTopic == "swapping")
        return true;

      let doc = this.browser.ownerDocument;

      // Clean-up video streams of screensharing previews.
      if ((aTopic == "dismissed" || aTopic == "removed") &&
          requestTypes.includes("Screen")) {
        let video = doc.getElementById("webRTC-previewVideo");
        video.deviceId = undefined;
        if (video.stream) {
          video.stream.getTracks().forEach(t => t.stop());
          video.stream = null;
          video.src = null;
          doc.getElementById("webRTC-preview").hidden = true;
        }
        let menupopup = doc.getElementById("webRTC-selectWindow-menupopup");
        if (menupopup._commandEventListener) {
          menupopup.removeEventListener("command", menupopup._commandEventListener);
          menupopup._commandEventListener = null;
        }
      }

      if (aTopic != "showing")
        return false;

      // BLOCK is handled immediately by MediaManager if it has been set
      // persistently in the permission manager. If it has been set on the tab,
      // it is handled synchronously before we add the notification.
      // Handling of ALLOW is delayed until the popupshowing event,
      // to avoid granting permissions automatically to background tabs.
      if (aRequest.secure) {
        let micAllowed =
          SitePermissions.get(uri, "microphone").state == SitePermissions.ALLOW;
        let camAllowed =
          SitePermissions.get(uri, "camera").state == SitePermissions.ALLOW;

        let perms = Services.perms;
        let mediaManagerPerm =
          perms.testExactPermission(uri, "MediaManagerVideo");
        if (mediaManagerPerm) {
          perms.remove(uri, "MediaManagerVideo");
        }

        // Screen sharing shouldn't follow the camera permissions.
        if (videoDevices.length && sharingScreen)
          camAllowed = false;

        let activeCamera;
        let activeMic;

        // Always prompt for screen sharing
        if (!sharingScreen) {
          for (let device of videoDevices) {
            let set = webrtcUI.activePerms.get(aBrowser.outerWindowID);
            if (set && set.has(aRequest.windowID + device.mediaSource + device.id)) {
              activeCamera = device;
              break;
            }
          }

          for (let device of audioDevices) {
            let set = webrtcUI.activePerms.get(aBrowser.outerWindowID);
            if (set && set.has(aRequest.windowID + device.mediaSource + device.id)) {
              activeMic = device;
              break;
            }
          }
        }

        if ((!audioDevices.length || micAllowed || activeMic) &&
            (!videoDevices.length || camAllowed || activeCamera)) {
          let allowedDevices = [];
          if (videoDevices.length) {
            allowedDevices.push((activeCamera || videoDevices[0]).deviceIndex);
            Services.perms.add(uri, "MediaManagerVideo",
                               Services.perms.ALLOW_ACTION,
                               Services.perms.EXPIRE_SESSION);
          }
          if (audioDevices.length) {
            allowedDevices.push((activeMic || audioDevices[0]).deviceIndex);
          }

          // Remember on which URIs we found persistent permissions so that we
          // can remove them if the user clicks 'Stop Sharing'. There's no
          // other way for the stop sharing code to know the hostnames of frames
          // using devices until bug 1066082 is fixed.
          let browser = this.browser;
          browser._devicePermissionURIs = browser._devicePermissionURIs || [];
          browser._devicePermissionURIs.push(uri);

          let mm = browser.messageManager;
          mm.sendAsyncMessage("webrtc:Allow", {callID: aRequest.callID,
                                               windowID: aRequest.windowID,
                                               devices: allowedDevices});
          this.remove();
          return true;
        }
      }

      function listDevices(menupopup, devices) {
        while (menupopup.lastChild)
          menupopup.removeChild(menupopup.lastChild);
        // Removing the child nodes of the menupopup doesn't clear the value
        // attribute of the menulist. This can have unfortunate side effects
        // when the list is rebuilt with a different content, so we remove
        // the value attribute explicitly.
        menupopup.parentNode.removeAttribute("value");

        for (let device of devices)
          addDeviceToList(menupopup, device.name, device.deviceIndex);
      }

      function checkDisabledWindowMenuItem() {
        let list = doc.getElementById("webRTC-selectWindow-menulist");
        let item = list.selectedItem;
        let notificationElement = doc.getElementById("webRTC-shareDevices-notification");
        if (!item || item.hasAttribute("disabled")) {
          notificationElement.setAttribute("invalidselection", "true");
        } else {
          notificationElement.removeAttribute("invalidselection");
        }
      }

      function listScreenShareDevices(menupopup, devices) {
        while (menupopup.lastChild)
          menupopup.removeChild(menupopup.lastChild);

        let type = devices[0].mediaSource;
        let typeName = type.charAt(0).toUpperCase() + type.substr(1);

        let label = doc.getElementById("webRTC-selectWindow-label");
        let gumStringId = "getUserMedia.select" + typeName;
        label.setAttribute("value",
                           stringBundle.getString(gumStringId + ".label"));
        label.setAttribute("accesskey",
                           stringBundle.getString(gumStringId + ".accesskey"));

        // "Select <type>" is the default because we can't pick a
        // 'default' window to share.
        addDeviceToList(menupopup,
                        stringBundle.getString("getUserMedia.pick" + typeName + ".label"),
                        "-1");
        menupopup.appendChild(doc.createElement("menuseparator"));

        // Build the list of 'devices'.
        let monitorIndex = 1;
        for (let i = 0; i < devices.length; ++i) {
          let device = devices[i];

          let name;
          // Building screen list from available screens.
          if (type == "screen") {
            if (device.name == "Primary Monitor") {
              name = stringBundle.getString("getUserMedia.shareEntireScreen.label");
            } else {
              name = stringBundle.getFormattedString("getUserMedia.shareMonitor.label",
                                                     [monitorIndex]);
              ++monitorIndex;
            }
          } else {
            name = device.name;
            if (type == "application") {
              // The application names returned by the platform are of the form:
              // <window count>\x1e<application name>
              let sepIndex = name.indexOf("\x1e");
              let count = name.slice(0, sepIndex);
              let sawcStringId = "getUserMedia.shareApplicationWindowCount.label";
              name = PluralForm.get(parseInt(count), stringBundle.getString(sawcStringId))
                               .replace("#1", name.slice(sepIndex + 1))
                               .replace("#2", count);
            }
          }
          let item = addDeviceToList(menupopup, name, i, typeName);
          item.deviceId = device.id;
          if (device.scary)
            item.scary = true;
        }

        // Always re-select the "No <type>" item.
        doc.getElementById("webRTC-selectWindow-menulist").removeAttribute("value");
        doc.getElementById("webRTC-all-windows-shared").hidden = true;

        menupopup._commandEventListener = event => {
          checkDisabledWindowMenuItem();
          let video = doc.getElementById("webRTC-previewVideo");
          if (video.stream) {
            video.stream.getTracks().forEach(t => t.stop());
            video.stream = null;
          }

          let deviceId = event.target.deviceId;
          if (deviceId == undefined) {
            doc.getElementById("webRTC-preview").hidden = true;
            video.src = null;
            return;
          }

          let scary = event.target.scary;
          let warning = doc.getElementById("webRTC-previewWarning");
          warning.hidden = !scary;
          let chromeWin = doc.defaultView;
          if (scary) {
            warning.hidden = false;
            let string;
            let bundle = chromeWin.gNavigatorBundle;

            let learnMoreText =
              bundle.getString("getUserMedia.shareScreen.learnMoreLabel");
            let baseURL =
              Services.urlFormatter.formatURLPref("app.support.baseURL");

            let learnMore = chromeWin.document.createElement("label");
            learnMore.className = "text-link";
            learnMore.setAttribute("href", baseURL + "screenshare-safety");
            learnMore.textContent = learnMoreText;

            if (type == "screen") {
              string = bundle.getFormattedString("getUserMedia.shareScreenWarning.message",
                                                 ["<>"]);
            } else {
              let brand =
                doc.getElementById("bundle_brand").getString("brandShortName");
              string = bundle.getFormattedString("getUserMedia.shareFirefoxWarning.message",
                                                 [brand, "<>"]);
            }

            let [pre, post] = string.split("<>");
            warning.textContent = pre;
            warning.appendChild(learnMore);
            warning.appendChild(chromeWin.document.createTextNode(post));
          }

          let perms = Services.perms;
          let chromeUri = Services.io.newURI(doc.documentURI);
          perms.add(chromeUri, "MediaManagerVideo", perms.ALLOW_ACTION,
                    perms.EXPIRE_SESSION);

          video.deviceId = deviceId;
          let constraints = { video: { mediaSource: type, deviceId: {exact: deviceId } } };
          chromeWin.navigator.mediaDevices.getUserMedia(constraints).then(stream => {
            if (video.deviceId != deviceId) {
              // The user has selected a different device or closed the panel
              // before getUserMedia finished.
              stream.getTracks().forEach(t => t.stop());
              return;
            }
            video.src = chromeWin.URL.createObjectURL(stream);
            video.stream = stream;
            doc.getElementById("webRTC-preview").hidden = false;
            video.onloadedmetadata = function(e) {
              video.play();
            };
          });
        };
        menupopup.addEventListener("command", menupopup._commandEventListener);
      }

      function addDeviceToList(menupopup, deviceName, deviceIndex, type) {
        let menuitem = doc.createElement("menuitem");
        menuitem.setAttribute("value", deviceIndex);
        menuitem.setAttribute("label", deviceName);
        menuitem.setAttribute("tooltiptext", deviceName);
        if (type)
          menuitem.setAttribute("devicetype", type);

        if (deviceIndex == "-1")
          menuitem.setAttribute("disabled", true);

        menupopup.appendChild(menuitem);
        return menuitem;
      }

      doc.getElementById("webRTC-selectCamera").hidden = !videoDevices.length || sharingScreen;
      doc.getElementById("webRTC-selectWindowOrScreen").hidden = !sharingScreen || !videoDevices.length;
      doc.getElementById("webRTC-selectMicrophone").hidden = !audioDevices.length || sharingAudio;

      let camMenupopup = doc.getElementById("webRTC-selectCamera-menupopup");
      let windowMenupopup = doc.getElementById("webRTC-selectWindow-menupopup");
      let micMenupopup = doc.getElementById("webRTC-selectMicrophone-menupopup");
      if (sharingScreen) {
        listScreenShareDevices(windowMenupopup, videoDevices);
        checkDisabledWindowMenuItem();
      } else {
        listDevices(camMenupopup, videoDevices);
        doc.getElementById("webRTC-shareDevices-notification").removeAttribute("invalidselection");
      }

      if (!sharingAudio)
        listDevices(micMenupopup, audioDevices);

      this.mainAction.callback = function(aState) {
        let remember = aState && aState.checkboxChecked;
        let allowedDevices = [];
        let perms = Services.perms;
        if (videoDevices.length) {
          let listId = "webRTC-select" + (sharingScreen ? "Window" : "Camera") + "-menulist";
          let videoDeviceIndex = doc.getElementById(listId).value;
          let allowVideoDevice = videoDeviceIndex != "-1";
          if (allowVideoDevice) {
            allowedDevices.push(videoDeviceIndex);
            // Session permission will be removed after use
            // (it's really one-shot, not for the entire session)
            perms.add(uri, "MediaManagerVideo", perms.ALLOW_ACTION,
                      perms.EXPIRE_SESSION);
            if (!webrtcUI.activePerms.has(aBrowser.outerWindowID)) {
              webrtcUI.activePerms.set(aBrowser.outerWindowID, new Set());
            }

            for (let device of videoDevices) {
              if (device.deviceIndex == videoDeviceIndex) {
                webrtcUI.activePerms.get(aBrowser.outerWindowID)
                        .add(aRequest.windowID + device.mediaSource + device.id);
                break;
              }
            }
            if (remember)
              SitePermissions.set(uri, "camera", SitePermissions.ALLOW);
          }
        }
        if (audioDevices.length) {
          if (!sharingAudio) {
            let audioDeviceIndex = doc.getElementById("webRTC-selectMicrophone-menulist").value;
            let allowMic = audioDeviceIndex != "-1";
            if (allowMic) {
              allowedDevices.push(audioDeviceIndex);
              if (!webrtcUI.activePerms.has(aBrowser.outerWindowID)) {
                webrtcUI.activePerms.set(aBrowser.outerWindowID, new Set());
              }

              for (let device of audioDevices) {
                if (device.deviceIndex == audioDeviceIndex) {
                  webrtcUI.activePerms.get(aBrowser.outerWindowID)
                          .add(aRequest.windowID + device.mediaSource + device.id);
                  break;
                }
              }
              if (remember)
                SitePermissions.set(uri, "microphone", SitePermissions.ALLOW);
            }
          } else {
            // Only one device possible for audio capture.
            allowedDevices.push(0);
          }
        }

        if (!allowedDevices.length) {
          denyRequest(notification.browser, aRequest);
          return;
        }

        if (remember) {
          // Remember on which URIs we set persistent permissions so that we
          // can remove them if the user clicks 'Stop Sharing'.
          aBrowser._devicePermissionURIs = aBrowser._devicePermissionURIs || [];
          aBrowser._devicePermissionURIs.push(uri);
        }

        let mm = notification.browser.messageManager;
        mm.sendAsyncMessage("webrtc:Allow", {callID: aRequest.callID,
                                             windowID: aRequest.windowID,
                                             devices: allowedDevices});
      };
      return false;
    }
  };

  // Don't offer "always remember" action in PB mode.
  if (!PrivateBrowsingUtils.isBrowserPrivate(aBrowser)) {

    // Disable the permanent 'Allow' action if the connection isn't secure, or for
    // screen/audio sharing (because we can't guess which window the user wants to
    // share without prompting).
    let reasonForNoPermanentAllow = "";
    if (sharingScreen) {
      reasonForNoPermanentAllow = "getUserMedia.reasonForNoPermanentAllow.screen3";
    } else if (sharingAudio) {
      reasonForNoPermanentAllow = "getUserMedia.reasonForNoPermanentAllow.audio";
    } else if (!aRequest.secure) {
      reasonForNoPermanentAllow = "getUserMedia.reasonForNoPermanentAllow.insecure";
    }

    options.checkbox = {
      label: stringBundle.getString("getUserMedia.remember"),
      checkedState: reasonForNoPermanentAllow ? {
        disableMainAction: true,
        warningLabel: stringBundle.getFormattedString(reasonForNoPermanentAllow,
                                                      [productName])
      } : undefined,
    };
  }

  let iconType = "Devices";
  if (requestTypes.length == 1 && (requestTypes[0] == "Microphone" ||
                                   requestTypes[0] == "AudioCapture"))
    iconType = "Microphone";
  if (requestTypes.includes("Screen"))
    iconType = "Screen";
  let anchorId = "webRTC-share" + iconType + "-notification-icon";

  let iconClass = iconType.toLowerCase();
  if (iconClass == "devices")
    iconClass = "camera";
  options.popupIconClass = iconClass + "-icon";

  notification =
    chromeDoc.defaultView
             .PopupNotifications
             .show(aBrowser, "webRTC-shareDevices", message,
                   anchorId, mainAction, secondaryActions,
                   options);
  notification.callID = aRequest.callID;

  let schemeHistogram = Services.telemetry.getKeyedHistogramById("PERMISSION_REQUEST_ORIGIN_SCHEME");
  let thirdPartyHistogram = Services.telemetry.getKeyedHistogramById("PERMISSION_REQUEST_THIRD_PARTY_ORIGIN");
  let userInputHistogram = Services.telemetry.getKeyedHistogramById("PERMISSION_REQUEST_HANDLING_USER_INPUT");

  let docURI = aRequest.documentURI;
  let scheme = 0;
  if (docURI.startsWith("https")) {
    scheme = 2;
  } else if (docURI.startsWith("http")) {
    scheme = 1;
  }

  for (let requestType of requestTypes) {
    if (requestType == "AudioCapture") {
      requestType = "Microphone";
    }
    requestType = requestType.toLowerCase();

    schemeHistogram.add(requestType, scheme);
    thirdPartyHistogram.add(requestType, aRequest.isThirdPartyOrigin);
    userInputHistogram.add(requestType, aRequest.isHandlingUserInput);
  }
}

function removePrompt(aBrowser, aCallId) {
  let chromeWin = aBrowser.ownerGlobal;
  let notification =
    chromeWin.PopupNotifications.getNotification("webRTC-shareDevices", aBrowser);
  if (notification && notification.callID == aCallId)
    notification.remove();
}

function getGlobalIndicator() {
  if (AppConstants.platform != "macosx") {
    const INDICATOR_CHROME_URI = "chrome://browser/content/webrtcIndicator.xul";
    const features = "chrome,dialog=yes,titlebar=no,popup=yes";

    return Services.ww.openWindow(null, INDICATOR_CHROME_URI, "_blank", features, []);
  }

  let indicator = {
    _camera: null,
    _microphone: null,
    _screen: null,

    _hiddenDoc: Services.appShell.hiddenDOMWindow.document,
    _statusBar: Cc["@mozilla.org/widget/macsystemstatusbar;1"]
                  .getService(Ci.nsISystemStatusBar),

    _command(aEvent) {
      webrtcUI.showSharingDoorhanger(aEvent.target.stream);
    },

    _popupShowing(aEvent) {
      let type = this.getAttribute("type");
      let activeStreams;
      if (type == "Camera") {
        activeStreams = webrtcUI.getActiveStreams(true, false, false);
      } else if (type == "Microphone") {
        activeStreams = webrtcUI.getActiveStreams(false, true, false);
      } else if (type == "Screen") {
        activeStreams = webrtcUI.getActiveStreams(false, false, true);
        type = webrtcUI.showScreenSharingIndicator;
      }

      let bundle =
        Services.strings.createBundle("chrome://browser/locale/webrtcIndicator.properties");

      if (activeStreams.length == 1) {
        let stream = activeStreams[0];

        let menuitem = this.ownerDocument.createElement("menuitem");
        let labelId = "webrtcIndicator.sharing" + type + "With.menuitem";
        let label = stream.browser.contentTitle || stream.uri;
        menuitem.setAttribute("label", bundle.formatStringFromName(labelId, [label], 1));
        menuitem.setAttribute("disabled", "true");
        this.appendChild(menuitem);

        menuitem = this.ownerDocument.createElement("menuitem");
        menuitem.setAttribute("label",
                              bundle.GetStringFromName("webrtcIndicator.controlSharing.menuitem"));
        menuitem.stream = stream;
        menuitem.addEventListener("command", indicator._command);

        this.appendChild(menuitem);
        return true;
      }

      // We show a different menu when there are several active streams.
      let menuitem = this.ownerDocument.createElement("menuitem");
      let labelId = "webrtcIndicator.sharing" + type + "WithNTabs.menuitem";
      let count = activeStreams.length;
      let label = PluralForm.get(count, bundle.GetStringFromName(labelId)).replace("#1", count);
      menuitem.setAttribute("label", label);
      menuitem.setAttribute("disabled", "true");
      this.appendChild(menuitem);

      for (let stream of activeStreams) {
        let item = this.ownerDocument.createElement("menuitem");
        labelId = "webrtcIndicator.controlSharingOn.menuitem";
        label = stream.browser.contentTitle || stream.uri;
        item.setAttribute("label", bundle.formatStringFromName(labelId, [label], 1));
        item.stream = stream;
        item.addEventListener("command", indicator._command);
        this.appendChild(item);
      }

      return true;
    },

    _popupHiding(aEvent) {
      while (this.firstChild)
        this.firstChild.remove();
    },

    _setIndicatorState(aName, aState) {
      let field = "_" + aName.toLowerCase();
      if (aState && !this[field]) {
        let menu = this._hiddenDoc.createElement("menu");
        menu.setAttribute("id", "webRTC-sharing" + aName + "-menu");

        // The CSS will only be applied if the menu is actually inserted in the DOM.
        this._hiddenDoc.documentElement.appendChild(menu);

        this._statusBar.addItem(menu);

        let menupopup = this._hiddenDoc.createElement("menupopup");
        menupopup.setAttribute("type", aName);
        menupopup.addEventListener("popupshowing", this._popupShowing);
        menupopup.addEventListener("popuphiding", this._popupHiding);
        menupopup.addEventListener("command", this._command);
        menu.appendChild(menupopup);

        this[field] = menu;
      } else if (this[field] && !aState) {
        this._statusBar.removeItem(this[field]);
        this[field].remove();
        this[field] = null;
      }
    },
    updateIndicatorState() {
      this._setIndicatorState("Camera", webrtcUI.showCameraIndicator);
      this._setIndicatorState("Microphone", webrtcUI.showMicrophoneIndicator);
      this._setIndicatorState("Screen", webrtcUI.showScreenSharingIndicator);
    },
    close() {
      this._setIndicatorState("Camera", false);
      this._setIndicatorState("Microphone", false);
      this._setIndicatorState("Screen", false);
    }
  };

  indicator.updateIndicatorState();
  return indicator;
}

function onTabSharingMenuPopupShowing(e) {
  let streams = webrtcUI.getActiveStreams(true, true, true);
  for (let streamInfo of streams) {
    let stringName = "getUserMedia.sharingMenu";
    let types = streamInfo.types;
    if (types.camera)
      stringName += "Camera";
    if (types.microphone)
      stringName += "Microphone";
    if (types.screen)
      stringName += types.screen;

    let doc = e.target.ownerDocument;
    let bundle = doc.defaultView.gNavigatorBundle;

    let origin = getHost(null, streamInfo.uri);
    let menuitem = doc.createElement("menuitem");
    menuitem.setAttribute("label", bundle.getFormattedString(stringName, [origin]));
    menuitem.stream = streamInfo;
    menuitem.addEventListener("command", onTabSharingMenuPopupCommand);
    e.target.appendChild(menuitem);
  }
}

function onTabSharingMenuPopupHiding(e) {
  while (this.lastChild)
    this.lastChild.remove();
}

function onTabSharingMenuPopupCommand(e) {
  webrtcUI.showSharingDoorhanger(e.target.stream);
}

function showOrCreateMenuForWindow(aWindow) {
  let document = aWindow.document;
  let menu = document.getElementById("tabSharingMenu");
  if (!menu) {
    let stringBundle = aWindow.gNavigatorBundle;
    menu = document.createElement("menu");
    menu.id = "tabSharingMenu";
    let labelStringId = "getUserMedia.sharingMenu.label";
    menu.setAttribute("label", stringBundle.getString(labelStringId));

    let container, insertionPoint;
    if (AppConstants.platform == "macosx") {
      container = document.getElementById("windowPopup");
      insertionPoint = document.getElementById("sep-window-list");
      let separator = document.createElement("menuseparator");
      separator.id = "tabSharingSeparator";
      container.insertBefore(separator, insertionPoint);
    } else {
      let accesskeyStringId = "getUserMedia.sharingMenu.accesskey";
      menu.setAttribute("accesskey", stringBundle.getString(accesskeyStringId));
      container = document.getElementById("main-menubar");
      insertionPoint = document.getElementById("helpMenu");
    }
    let popup = document.createElement("menupopup");
    popup.id = "tabSharingMenuPopup";
    popup.addEventListener("popupshowing", onTabSharingMenuPopupShowing);
    popup.addEventListener("popuphiding", onTabSharingMenuPopupHiding);
    menu.appendChild(popup);
    container.insertBefore(menu, insertionPoint);
  } else {
    menu.hidden = false;
    if (AppConstants.platform == "macosx") {
      document.getElementById("tabSharingSeparator").hidden = false;
    }
  }
}

function maybeAddMenuIndicator(window) {
  if (webrtcUI.showGlobalIndicator) {
    showOrCreateMenuForWindow(window);
  }
}

var gIndicatorWindow = null;

function updateIndicators(data, target) {
  if (data) {
    // the global indicators specific to this process
    let indicators;
    if (webrtcUI.processIndicators.has(target)) {
      indicators = webrtcUI.processIndicators.get(target);
    } else {
      indicators = {};
      webrtcUI.processIndicators.set(target, indicators);
    }

    indicators.showGlobalIndicator = data.showGlobalIndicator;
    indicators.showCameraIndicator = data.showCameraIndicator;
    indicators.showMicrophoneIndicator = data.showMicrophoneIndicator;
    indicators.showScreenSharingIndicator = data.showScreenSharingIndicator;
  }

  let browserWindowEnum = Services.wm.getEnumerator("navigator:browser");
  while (browserWindowEnum.hasMoreElements()) {
    let chromeWin = browserWindowEnum.getNext();
    if (webrtcUI.showGlobalIndicator) {
      showOrCreateMenuForWindow(chromeWin);
    } else {
      let doc = chromeWin.document;
      let existingMenu = doc.getElementById("tabSharingMenu");
      if (existingMenu) {
        existingMenu.hidden = true;
      }
      if (AppConstants.platform == "macosx") {
        let separator = doc.getElementById("tabSharingSeparator");
        if (separator) {
          separator.hidden = true;
        }
      }
    }
  }

  if (webrtcUI.showGlobalIndicator) {
    if (!gIndicatorWindow) {
      gIndicatorWindow = getGlobalIndicator();
    } else {
      try {
        gIndicatorWindow.updateIndicatorState();
      } catch (err) {
        Cu.reportError(`error in gIndicatorWindow.updateIndicatorState(): ${err.message}`);
      }
    }
  } else if (gIndicatorWindow) {
    gIndicatorWindow.close();
    gIndicatorWindow = null;
  }
}
PK
!<3���#modules/syncedtabs/EventEmitter.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = [
  "EventEmitter"
];

// Simple event emitter abstraction for storage objects to use.
function EventEmitter() {
  this._events = new Map();
}

EventEmitter.prototype = {
  on(event, listener) {
    if (this._events.has(event)) {
      this._events.get(event).add(listener);
    } else {
      this._events.set(event, new Set([listener]));
    }
  },
  off(event, listener) {
    if (!this._events.has(event)) {
      return;
    }
    this._events.get(event).delete(listener);
  },
  emit(event, ...args) {
    if (!this._events.has(event)) {
      return;
    }
    for (let listener of this._events.get(event).values()) {
      try {
        listener.apply(this, args);
      } catch (e) {
        Cu.reportError(e);
      }
    }
  },
};

PK
!<�G�^�^modules/SitePermissions.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

var EXPORTED_SYMBOLS = [ "SitePermissions" ];

ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");

var gStringBundle =
  Services.strings.createBundle("chrome://browser/locale/sitePermissions.properties");

/**
 * A helper module to manage temporarily blocked permissions.
 *
 * Permissions are keyed by browser, so methods take a Browser
 * element to identify the corresponding permission set.
 *
 * This uses a WeakMap to key browsers, so that entries are
 * automatically cleared once the browser stops existing
 * (once there are no other references to the browser object);
 */
const TemporaryBlockedPermissions = {
  // This is a three level deep map with the following structure:
  //
  // Browser => {
  //   <prePath>: {
  //     <permissionID>: {Number} <timeStamp>
  //   }
  // }
  //
  // Only the top level browser elements are stored via WeakMap. The WeakMap
  // value is an object with URI prePaths as keys. The keys of that object
  // are ids that identify permissions that were set for the specific URI.
  // The final value is an object containing the timestamp of when the permission
  // was set (in order to invalidate after a certain amount of time has passed).
  _stateByBrowser: new WeakMap(),

  // Private helper method that bundles some shared behavior for
  // get() and getAll(), e.g. deleting permissions when they have expired.
  _get(entry, prePath, id, timeStamp) {
    if (timeStamp == null) {
      delete entry[prePath][id];
      return null;
    }
    if (timeStamp + SitePermissions.temporaryPermissionExpireTime < Date.now()) {
      delete entry[prePath][id];
      return null;
    }
    return {id, state: SitePermissions.BLOCK, scope: SitePermissions.SCOPE_TEMPORARY};
  },

  // Sets a new permission for the specified browser.
  set(browser, id) {
    if (!browser) {
      return;
    }
    if (!this._stateByBrowser.has(browser)) {
      this._stateByBrowser.set(browser, {});
    }
    let entry = this._stateByBrowser.get(browser);
    let prePath = browser.currentURI.prePath;
    if (!entry[prePath]) {
      entry[prePath] = {};
    }
    entry[prePath][id] = Date.now();
  },

  // Removes a permission with the specified id for the specified browser.
  remove(browser, id) {
    if (!browser) {
      return;
    }
    let entry = this._stateByBrowser.get(browser);
    let prePath = browser.currentURI.prePath;
    if (entry && entry[prePath]) {
      delete entry[prePath][id];
    }
  },

  // Gets a permission with the specified id for the specified browser.
  get(browser, id) {
    if (!browser || !browser.currentURI) {
      return null;
    }
    let entry = this._stateByBrowser.get(browser);
    let prePath = browser.currentURI.prePath;
    if (entry && entry[prePath]) {
      let permission = entry[prePath][id];
      return this._get(entry, prePath, id, permission);
    }
    return null;
  },

  // Gets all permissions for the specified browser.
  // Note that only permissions that apply to the current URI
  // of the passed browser element will be returned.
  getAll(browser) {
    let permissions = [];
    let entry = this._stateByBrowser.get(browser);
    let prePath = browser.currentURI.prePath;
    if (entry && entry[prePath]) {
      let timeStamps = entry[prePath];
      for (let id of Object.keys(timeStamps)) {
        let permission = this._get(entry, prePath, id, timeStamps[id]);
        // _get() returns null when the permission has expired.
        if (permission) {
          permissions.push(permission);
        }
      }
    }
    return permissions;
  },

  // Clears all permissions for the specified browser.
  // Unlike other methods, this does NOT clear only for
  // the currentURI but the whole browser state.
  clear(browser) {
    this._stateByBrowser.delete(browser);
  },

  // Copies the temporary permission state of one browser
  // into a new entry for the other browser.
  copy(browser, newBrowser) {
    let entry = this._stateByBrowser.get(browser);
    if (entry) {
      this._stateByBrowser.set(newBrowser, entry);
    }
  },
};

/**
 * A module to manage permanent and temporary permissions
 * by URI and browser.
 *
 * Some methods have the side effect of dispatching a "PermissionStateChange"
 * event on changes to temporary permissions, as mentioned in the respective docs.
 */
var SitePermissions = {
  // Permission states.
  // PROMPT_HIDE state is only used to show the "Hide Prompt" state in the identity panel
  // for the "plugin:flash" permission and not in pageinfo.
  UNKNOWN: Services.perms.UNKNOWN_ACTION,
  ALLOW: Services.perms.ALLOW_ACTION,
  BLOCK: Services.perms.DENY_ACTION,
  PROMPT: Services.perms.PROMPT_ACTION,
  ALLOW_COOKIES_FOR_SESSION: Ci.nsICookiePermission.ACCESS_SESSION,
  PROMPT_HIDE: Ci.nsIObjectLoadingContent.PLUGIN_PERMISSION_PROMPT_ACTION_QUIET,

  // Permission scopes.
  SCOPE_REQUEST: "{SitePermissions.SCOPE_REQUEST}",
  SCOPE_TEMPORARY: "{SitePermissions.SCOPE_TEMPORARY}",
  SCOPE_SESSION: "{SitePermissions.SCOPE_SESSION}",
  SCOPE_PERSISTENT: "{SitePermissions.SCOPE_PERSISTENT}",
  SCOPE_POLICY: "{SitePermissions.SCOPE_POLICY}",

  _defaultPrefBranch: Services.prefs.getBranch("permissions.default."),

  /**
   * Gets all custom permissions for a given URI.
   * Install addon permission is excluded, check bug 1303108.
   *
   * @return {Array} a list of objects with the keys:
   *          - id: the permissionId of the permission
   *          - scope: the scope of the permission (e.g. SitePermissions.SCOPE_TEMPORARY)
   *          - state: a constant representing the current permission state
   *            (e.g. SitePermissions.ALLOW)
   */
  getAllByURI(uri) {
    let result = [];
    if (!this.isSupportedURI(uri)) {
      return result;
    }

    let permissions = Services.perms.getAllForURI(uri);
    while (permissions.hasMoreElements()) {
      let permission = permissions.getNext();

      // filter out unknown permissions
      if (gPermissionObject[permission.type]) {
        // XXX Bug 1303108 - Control Center should only show non-default permissions
        if (permission.type == "install") {
          continue;
        }

        // Hide canvas permission when privacy.resistFingerprinting is false.
        if ((permission.type == "canvas") &&
            !Services.prefs.getBoolPref("privacy.resistFingerprinting")) {
          continue;
        }

        let scope = this.SCOPE_PERSISTENT;
        if (permission.expireType == Services.perms.EXPIRE_SESSION) {
          scope = this.SCOPE_SESSION;
        } else if (permission.expireType == Services.perms.EXPIRE_POLICY) {
          scope = this.SCOPE_POLICY;
        }

        result.push({
          id: permission.type,
          scope,
          state: permission.capability,
        });
      }
    }

    return result;
  },

  /**
   * Returns all custom permissions for a given browser.
   *
   * To receive a more detailed, albeit less performant listing see
   * SitePermissions.getAllPermissionDetailsForBrowser().
   *
   * @param {Browser} browser
   *        The browser to fetch permission for.
   *
   * @return {Array} a list of objects with the keys:
   *         - id: the permissionId of the permission
   *         - state: a constant representing the current permission state
   *           (e.g. SitePermissions.ALLOW)
   *         - scope: a constant representing how long the permission will
   *           be kept.
   */
  getAllForBrowser(browser) {
    let permissions = {};

    for (let permission of TemporaryBlockedPermissions.getAll(browser)) {
      permission.scope = this.SCOPE_TEMPORARY;
      permissions[permission.id] = permission;
    }

    for (let permission of this.getAllByURI(browser.currentURI)) {
      permissions[permission.id] = permission;
    }

    return Object.values(permissions);
  },

  /**
   * Returns a list of objects with detailed information on all permissions
   * that are currently set for the given browser.
   *
   * @param {Browser} browser
   *        The browser to fetch permission for.
   *
   * @return {Array<Object>} a list of objects with the keys:
   *           - id: the permissionID of the permission
   *           - state: a constant representing the current permission state
   *             (e.g. SitePermissions.ALLOW)
   *           - scope: a constant representing how long the permission will
   *             be kept.
   *           - label: the localized label
   */
  getAllPermissionDetailsForBrowser(browser) {
    return this.getAllForBrowser(browser).map(({id, scope, state}) =>
      ({id, scope, state, label: this.getPermissionLabel(id)}));
  },

  /**
   * Checks whether a UI for managing permissions should be exposed for a given
   * URI. This excludes file URIs, for instance, as they don't have a host,
   * even though nsIPermissionManager can still handle them.
   *
   * @param {nsIURI} uri
   *        The URI to check.
   *
   * @return {boolean} if the URI is supported.
   */
  isSupportedURI(uri) {
    return uri && ["http", "https", "moz-extension"].includes(uri.scheme);
  },

  /**
   * Gets an array of all permission IDs.
   *
   * @return {Array<String>} an array of all permission IDs.
   */
  listPermissions() {
    let permissions = Object.keys(gPermissionObject);

    // Hide canvas permission when privacy.resistFingerprinting is false.
    if (!Services.prefs.getBoolPref("privacy.resistFingerprinting")) {
      permissions = permissions.filter(permission => permission !== "canvas");
    }

    return permissions;
  },

  /**
   * Returns an array of permission states to be exposed to the user for a
   * permission with the given ID.
   *
   * @param {string} permissionID
   *        The ID to get permission states for.
   *
   * @return {Array<SitePermissions state>} an array of all permission states.
   */
  getAvailableStates(permissionID) {
    if (permissionID in gPermissionObject &&
        gPermissionObject[permissionID].states)
      return gPermissionObject[permissionID].states;

    /* Since the permissions we are dealing with have adopted the convention
     * of treating UNKNOWN == PROMPT, we only include one of either UNKNOWN
     * or PROMPT in this list, to avoid duplicating states. */
    if (this.getDefault(permissionID) == this.UNKNOWN)
      return [ SitePermissions.UNKNOWN, SitePermissions.ALLOW, SitePermissions.BLOCK ];

    return [ SitePermissions.PROMPT, SitePermissions.ALLOW, SitePermissions.BLOCK ];
  },

  /**
   * Returns the default state of a particular permission.
   *
   * @param {string} permissionID
   *        The ID to get the default for.
   *
   * @return {SitePermissions.state} the default state.
   */
  getDefault(permissionID) {
    // If the permission has custom logic for getting its default value,
    // try that first.
    if (permissionID in gPermissionObject &&
        gPermissionObject[permissionID].getDefault)
      return gPermissionObject[permissionID].getDefault();

    // Otherwise try to get the default preference for that permission.
    return this._defaultPrefBranch.getIntPref(permissionID, this.UNKNOWN);
  },

  /**
   * Returns the state and scope of a particular permission for a given URI.
   *
   * This method will NOT dispatch a "PermissionStateChange" event on the specified
   * browser if a temporary permission was removed because it has expired.
   *
   * @param {nsIURI} uri
   *        The URI to check.
   * @param {String} permissionID
   *        The id of the permission.
   * @param {Browser} browser (optional)
   *        The browser object to check for temporary permissions.
   *
   * @return {Object} an object with the keys:
   *           - state: The current state of the permission
   *             (e.g. SitePermissions.ALLOW)
   *           - scope: The scope of the permission
   *             (e.g. SitePermissions.SCOPE_PERSISTENT)
   */
  get(uri, permissionID, browser) {
    let defaultState = this.getDefault(permissionID);
    let result = { state: defaultState, scope: this.SCOPE_PERSISTENT };
    if (this.isSupportedURI(uri)) {
      let permission = null;
      if (permissionID in gPermissionObject &&
        gPermissionObject[permissionID].exactHostMatch) {
        permission = Services.perms.getPermissionObjectForURI(uri, permissionID, true);
      } else {
        permission = Services.perms.getPermissionObjectForURI(uri, permissionID, false);
      }

      if (permission) {
        result.state = permission.capability;
        if (permission.expireType == Services.perms.EXPIRE_SESSION) {
          result.scope = this.SCOPE_SESSION;
        } else if (permission.expireType == Services.perms.EXPIRE_POLICY) {
          result.scope = this.SCOPE_POLICY;
        }
      }
    }

    if (result.state == defaultState) {
      // If there's no persistent permission saved, check if we have something
      // set temporarily.
      let value = TemporaryBlockedPermissions.get(browser, permissionID);

      if (value) {
        result.state = value.state;
        result.scope = this.SCOPE_TEMPORARY;
      }
    }

    return result;
  },

  /**
   * Sets the state of a particular permission for a given URI or browser.
   * This method will dispatch a "PermissionStateChange" event on the specified
   * browser if a temporary permission was set
   *
   * @param {nsIURI} uri
   *        The URI to set the permission for.
   *        Note that this will be ignored if the scope is set to SCOPE_TEMPORARY
   * @param {String} permissionID
   *        The id of the permission.
   * @param {SitePermissions state} state
   *        The state of the permission.
   * @param {SitePermissions scope} scope (optional)
   *        The scope of the permission. Defaults to SCOPE_PERSISTENT.
   * @param {Browser} browser (optional)
   *        The browser object to set temporary permissions on.
   *        This needs to be provided if the scope is SCOPE_TEMPORARY!
   */
  set(uri, permissionID, state, scope = this.SCOPE_PERSISTENT, browser = null) {
    if (state == this.UNKNOWN || state == this.getDefault(permissionID)) {
      this.remove(uri, permissionID, browser);
      return;
    }

    if (state == this.ALLOW_COOKIES_FOR_SESSION && permissionID != "cookie") {
      throw "ALLOW_COOKIES_FOR_SESSION can only be set on the cookie permission";
    }

    // Save temporary permissions.
    if (scope == this.SCOPE_TEMPORARY) {
      // We do not support setting temp ALLOW for security reasons.
      // In its current state, this permission could be exploited by subframes
      // on the same page. This is because for BLOCK we ignore the request
      // URI and only consider the current browser URI, to avoid notification spamming.
      //
      // If you ever consider removing this line, you likely want to implement
      // a more fine-grained TemporaryBlockedPermissions that temporarily blocks for the
      // entire browser, but temporarily allows only for specific frames.
      if (state != this.BLOCK) {
        throw "'Block' is the only permission we can save temporarily on a browser";
      }

      if (!browser) {
        throw "TEMPORARY scoped permissions require a browser object";
      }

      TemporaryBlockedPermissions.set(browser, permissionID);

      browser.dispatchEvent(new browser.ownerGlobal
                                       .CustomEvent("PermissionStateChange"));
    } else if (this.isSupportedURI(uri)) {
      let perms_scope = Services.perms.EXPIRE_NEVER;
      if (scope == this.SCOPE_SESSION) {
        perms_scope = Services.perms.EXPIRE_SESSION;
      } else if (scope == this.SCOPE_POLICY) {
        perms_scope = Services.perms.EXPIRE_POLICY;
      }

      Services.perms.add(uri, permissionID, state, perms_scope);
    }
  },

  /**
   * Removes the saved state of a particular permission for a given URI and/or browser.
   * This method will dispatch a "PermissionStateChange" event on the specified
   * browser if a temporary permission was removed.
   *
   * @param {nsIURI} uri
   *        The URI to remove the permission for.
   * @param {String} permissionID
   *        The id of the permission.
   * @param {Browser} browser (optional)
   *        The browser object to remove temporary permissions on.
   */
  remove(uri, permissionID, browser) {
    if (this.isSupportedURI(uri))
      Services.perms.remove(uri, permissionID);

    // TemporaryBlockedPermissions.get() deletes expired permissions automatically,
    if (TemporaryBlockedPermissions.get(browser, permissionID)) {
      // If it exists but has not expired, remove it explicitly.
      TemporaryBlockedPermissions.remove(browser, permissionID);
      // Send a PermissionStateChange event only if the permission hasn't expired.
      browser.dispatchEvent(new browser.ownerGlobal
                                       .CustomEvent("PermissionStateChange"));
    }
  },

  /**
   * Clears all permissions that were temporarily saved.
   *
   * @param {Browser} browser
   *        The browser object to clear.
   */
  clearTemporaryPermissions(browser) {
    TemporaryBlockedPermissions.clear(browser);
  },

  /**
   * Copy all permissions that were temporarily saved on one
   * browser object to a new browser.
   *
   * @param {Browser} browser
   *        The browser object to copy from.
   * @param {Browser} newBrowser
   *        The browser object to copy to.
   */
  copyTemporaryPermissions(browser, newBrowser) {
    TemporaryBlockedPermissions.copy(browser, newBrowser);
  },

  /**
   * Returns the localized label for the permission with the given ID, to be
   * used in a UI for managing permissions.
   *
   * @param {string} permissionID
   *        The permission to get the label for.
   *
   * @return {String} the localized label.
   */
  getPermissionLabel(permissionID) {
    let labelID = gPermissionObject[permissionID].labelID || permissionID;
    return gStringBundle.GetStringFromName("permission." + labelID + ".label");
  },

  /**
   * Returns the localized label for the given permission state, to be used in
   * a UI for managing permissions.
   *
   * @param {SitePermissions state} state
   *        The state to get the label for.
   *
   * @return {String|null} the localized label or null if an
   *         unknown state was passed.
   */
  getMultichoiceStateLabel(state) {
    switch (state) {
      case this.UNKNOWN:
      case this.PROMPT:
        return gStringBundle.GetStringFromName("state.multichoice.alwaysAsk");
      case this.ALLOW:
        return gStringBundle.GetStringFromName("state.multichoice.allow");
      case this.ALLOW_COOKIES_FOR_SESSION:
        return gStringBundle.GetStringFromName("state.multichoice.allowForSession");
      case this.BLOCK:
        return gStringBundle.GetStringFromName("state.multichoice.block");
      default:
        return null;
    }
  },

  /**
   * Returns the localized label for a permission's current state.
   *
   * @param {SitePermissions state} state
   *        The state to get the label for.
   * @param {string} id
   *        The permission to get the state label for.
   * @param {SitePermissions scope} scope (optional)
   *        The scope to get the label for.
   *
   * @return {String|null} the localized label or null if an
   *         unknown state was passed.
   */
  getCurrentStateLabel(state, id, scope = null) {
    // We try to avoid a collision between SitePermissions.PROMPT_HIDE
    // and SitePermissions.ALLOW_COOKIES_FOR_SESSION which share the same const value.
    if (id.startsWith("plugin") && state == SitePermissions.PROMPT_HIDE) {
      return gStringBundle.GetStringFromName("state.current.hide");
    }

    switch (state) {
      case this.PROMPT:
        return gStringBundle.GetStringFromName("state.current.prompt");
      case this.ALLOW:
        if (scope && scope != this.SCOPE_PERSISTENT && scope != this.SCOPE_POLICY)
          return gStringBundle.GetStringFromName("state.current.allowedTemporarily");
        return gStringBundle.GetStringFromName("state.current.allowed");
      case this.ALLOW_COOKIES_FOR_SESSION:
        return gStringBundle.GetStringFromName("state.current.allowedForSession");
      case this.BLOCK:
        if (scope && scope != this.SCOPE_PERSISTENT && scope != this.SCOPE_POLICY)
          return gStringBundle.GetStringFromName("state.current.blockedTemporarily");
        return gStringBundle.GetStringFromName("state.current.blocked");
      default:
        return null;
    }
  },
};

var gPermissionObject = {
  /* Holds permission ID => options pairs.
   *
   * Supported options:
   *
   *  - exactHostMatch
   *    Allows sub domains to have their own permissions.
   *    Defaults to false.
   *
   *  - getDefault
   *    Called to get the permission's default state.
   *    Defaults to UNKNOWN, indicating that the user will be asked each time
   *    a page asks for that permissions.
   *
   *  - labelID
   *    Use the given ID instead of the permission name for looking up strings.
   *    e.g. "desktop-notification2" to use permission.desktop-notification2.label
   *
   *  - states
   *    Array of permission states to be exposed to the user.
   *    Defaults to ALLOW, BLOCK and the default state (see getDefault).
   *    The PROMPT_HIDE state is deliberately excluded from "plugin:flash" since we
   *    don't want to expose a "Hide Prompt" button to the user through pageinfo.
   */

  "image": {
    states: [ SitePermissions.ALLOW, SitePermissions.BLOCK ],
  },

  "cookie": {
    states: [ SitePermissions.ALLOW, SitePermissions.ALLOW_COOKIES_FOR_SESSION, SitePermissions.BLOCK ],
    getDefault() {
      if (Services.prefs.getIntPref("network.cookie.cookieBehavior") == 2)
        return SitePermissions.BLOCK;

      if (Services.prefs.getIntPref("network.cookie.lifetimePolicy") == 2)
        return SitePermissions.ALLOW_COOKIES_FOR_SESSION;

      return SitePermissions.ALLOW;
    }
  },

  "desktop-notification": {
    exactHostMatch: true,
    labelID: "desktop-notification2",
  },

  "camera": {
    exactHostMatch: true,
  },

  "microphone": {
    exactHostMatch: true,
  },

  "screen": {
    exactHostMatch: true,
    states: [ SitePermissions.UNKNOWN, SitePermissions.BLOCK ],
  },

  "popup": {
    getDefault() {
      return Services.prefs.getBoolPref("dom.disable_open_during_load") ?
               SitePermissions.BLOCK : SitePermissions.ALLOW;
    },
    states: [ SitePermissions.ALLOW, SitePermissions.BLOCK ],
  },

  "install": {
    getDefault() {
      return Services.prefs.getBoolPref("xpinstall.whitelist.required") ?
               SitePermissions.BLOCK : SitePermissions.ALLOW;
    },
    states: [ SitePermissions.ALLOW, SitePermissions.BLOCK ],
  },

  "geo": {
    exactHostMatch: true
  },

  "focus-tab-by-prompt": {
    exactHostMatch: true,
    states: [ SitePermissions.UNKNOWN, SitePermissions.ALLOW ],
  },
  "persistent-storage": {
    exactHostMatch: true
  },

  "shortcuts": {
    states: [ SitePermissions.ALLOW, SitePermissions.BLOCK ],
  },

  "canvas": {
  },

  "plugin:flash": {
    labelID: "flash-plugin",
    states: [ SitePermissions.UNKNOWN, SitePermissions.ALLOW, SitePermissions.BLOCK ],
  },

  "midi": {
    exactHostMatch: true
  },

  "midi-sysex": {
    exactHostMatch: true
  }
};

// Delete this entry while being pre-off
// or the persistent-storage permission would appear in Page info's Permission section
if (!Services.prefs.getBoolPref("browser.storageManager.enabled")) {
  delete gPermissionObject["persistent-storage"];
}

if (!Services.prefs.getBoolPref("dom.webmidi.enabled")) {
  // ESLint gets angry about array versus dot notation here, but some permission
  // names use hyphens. Disabling rule for line to keep things consistent.
  // eslint-disable-next-line dot-notation
  delete gPermissionObject["midi"];
  delete gPermissionObject["midi-sysex"];
}

XPCOMUtils.defineLazyPreferenceGetter(SitePermissions, "temporaryPermissionExpireTime",
                                      "privacy.temporary_permission_expire_time_ms", 3600 * 1000);
PK
!<��_a""5chrome/browser/skin/classic/browser/identity-icon.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" fill-rule="evenodd" d="M8 15a7 7 0 1 1 7-7 7 7 0 0 1-7 7zM8 2a6 6 0 1 0 6 6 6 6 0 0 0-6-6zm0 10a1 1 0 0 1-1-1V8a1 1 0 0 1 2 0v3a1 1 0 0 1-1 1zm0-6a1 1 0 1 1 1-1 1 1 0 0 1-1 1z"/>
</svg>
PK
!<���|��3chrome/browser/skin/classic/browser/page-action.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M2 6a2 2 0 1 0 2 2 2 2 0 0 0-2-2zm6 0a2 2 0 1 0 2 2 2 2 0 0 0-2-2zm6 0a2 2 0 1 0 2 2 2 2 0 0 0-2-2z"/>
</svg>
PK
!<4!�$$7chrome/browser/skin/classic/browser/bookmark-hollow.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M3.8 15.922a1.1 1.1 0 0 1-1.09-1.253l.609-4.36L.393 7.163a1.1 1.1 0 0 1 .616-1.833l4.08-.73L7.016.734a1.1 1.1 0 0 1 1.969 0L10.911 4.6 15 5.331a1.1 1.1 0 0 1 .611 1.833L12.68 10.31l.609 4.359a1.1 1.1 0 0 1-1.6 1.127L8 13.873 4.308 15.8a1.093 1.093 0 0 1-.508.122zm-.415-1.9zm9.228 0zM2.981 7.01l2.451 2.635-.5 3.572L8 11.618l3.067 1.6-.5-3.572 2.452-2.636-3.45-.616L8 3.244l-1.569 3.15zm11.659.29zm-13.278 0zm12.78-1.5zm-12.286 0z"/>
</svg>
PK
!<!�g�����modules/PanelMultiView.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
 * Allows a popup panel to host multiple subviews. The main view shown when the
 * panel is opened may slide out to display a subview, which in turn may lead to
 * other subviews in a cascade menu pattern.
 *
 * The <panel> element should contain a <panelmultiview> element. Views are
 * declared using <panelview> elements that are usually children of the main
 * <panelmultiview> element, although they don't need to be, as views can also
 * be imported into the panel from other panels or popup sets.
 *
 * The panel should be opened asynchronously using the openPopup static method
 * on the PanelMultiView object. This will display the view specified using the
 * mainViewId attribute on the contained <panelmultiview> element.
 *
 * Specific subviews can slide in using the showSubView method, and backwards
 * navigation can be done using the goBack method or through a button in the
 * subview headers.
 *
 * The process of displaying the main view or a new subview requires multiple
 * steps to be completed, hence at any given time the <panelview> element may
 * be in different states:
 *
 * -- Open or closed
 *
 *    All the <panelview> elements start "closed", meaning that they are not
 *    associated to a <panelmultiview> element and can be located anywhere in
 *    the document. When the openPopup or showSubView methods are called, the
 *    relevant view becomes "open" and the <panelview> element may be moved to
 *    ensure it is a descendant of the <panelmultiview> element.
 *
 *    The "ViewShowing" event is fired at this point, when the view is not
 *    visible yet. The event is allowed to cancel the operation, in which case
 *    the view is closed immediately.
 *
 *    Closing the view does not move the node back to its original position.
 *
 * -- Visible or invisible
 *
 *    This indicates whether the view is visible in the document from a layout
 *    perspective, regardless of whether it is currently scrolled into view. In
 *    fact, all subviews are already visible before they start sliding in.
 *
 *    Before scrolling into view, a view may become visible but be placed in a
 *    special off-screen area of the document where layout and measurements can
 *    take place asyncronously.
 *
 *    When navigating forward, an open view may become invisible but stay open
 *    after sliding out of view. The last known size of these views is still
 *    taken into account for determining the overall panel size.
 *
 *    When navigating backwards, an open subview will first become invisible and
 *    then will be closed.
 *
 * -- Active or inactive
 *
 *    This indicates whether the view is fully scrolled into the visible area
 *    and ready to receive mouse and keyboard events. An active view is always
 *    visible, but a visible view may be inactive. For example, during a scroll
 *    transition, both views will be inactive.
 *
 *    When a view becomes active, the ViewShown event is fired synchronously,
 *    and the showSubView and goBack methods can be called for navigation.
 *
 *    For the main view of the panel, the ViewShown event is dispatched during
 *    the "popupshown" event, which means that other "popupshown" handlers may
 *    be called before the view is active. Thus, code that needs to perform
 *    further navigation automatically should either use the ViewShown event or
 *    wait for an event loop tick, like BrowserTestUtils.waitForEvent does.
 *
 * -- Navigating with the keyboard
 *
 *    An open view may keep state related to keyboard navigation, even if it is
 *    invisible. When a view is closed, keyboard navigation state is cleared.
 *
 * This diagram shows how <panelview> nodes move during navigation:
 *
 *   In this <panelmultiview>     In other panels    Action
 *             ┌───┬───┬───┐        ┌───┬───┐
 *             │(A)│ B │ C │        │ D │ E │          Open panel
 *             └───┴───┴───┘        └───┴───┘
 *         ┌───┬───┬───┐            ┌───┬───┐
 *         │{A}│(C)│ B │            │ D │ E │          Show subview C
 *         └───┴───┴───┘            └───┴───┘
 *     ┌───┬───┬───┬───┐            ┌───┐
 *     │{A}│{C}│(D)│ B │            │ E │              Show subview D
 *     └───┴───┴───┴───┘            └───┘
 *       │ ┌───┬───┬───┬───┐        ┌───┐
 *       │ │{A}│(C)│ D │ B │        │ E │              Go back
 *       │ └───┴───┴───┴───┘        └───┘
 *       │   │   │
 *       │   │   └── Currently visible view
 *       │   │   │
 *       └───┴───┴── Open views
 */

"use strict";

var EXPORTED_SYMBOLS = [
  "PanelMultiView",
  "PanelView",
];

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.defineModuleGetter(this, "AppConstants",
  "resource://gre/modules/AppConstants.jsm");
ChromeUtils.defineModuleGetter(this, "CustomizableUI",
  "resource:///modules/CustomizableUI.jsm");

XPCOMUtils.defineLazyGetter(this, "gBundle", function() {
  return Services.strings.createBundle(
    "chrome://browser/locale/browser.properties");
});

/**
 * Safety timeout after which asynchronous events will be canceled if any of the
 * registered blockers does not return.
 */
const BLOCKERS_TIMEOUT_MS = 10000;

const TRANSITION_PHASES = Object.freeze({
  START: 1,
  PREPARE: 2,
  TRANSITION: 3,
});

let gNodeToObjectMap = new WeakMap();
let gWindowsWithUnloadHandler = new WeakSet();
let gMultiLineElementsMap = new WeakMap();

/**
 * Allows associating an object to a node lazily using a weak map.
 *
 * Classes deriving from this one may be easily converted to Custom Elements,
 * although they would lose the ability of being associated lazily.
 */
var AssociatedToNode = class {
  constructor(node) {
    /**
     * Node associated to this object.
     */
    this.node = node;

    /**
     * This promise is resolved when the current set of blockers set by event
     * handlers have all been processed.
     */
    this._blockersPromise = Promise.resolve();
  }

  /**
   * Retrieves the instance associated with the given node, constructing a new
   * one if necessary. When the last reference to the node is released, the
   * object instance will be garbage collected as well.
   */
  static forNode(node) {
    let associatedToNode = gNodeToObjectMap.get(node);
    if (!associatedToNode) {
      associatedToNode = new this(node);
      gNodeToObjectMap.set(node, associatedToNode);
    }
    return associatedToNode;
  }

  get document() {
    return this.node.ownerDocument;
  }

  get window() {
    return this.node.ownerGlobal;
  }

  /**
   * nsIDOMWindowUtils for the window of this node.
   */
  get _dwu() {
    if (this.__dwu)
      return this.__dwu;
    return this.__dwu = this.window.QueryInterface(Ci.nsIInterfaceRequestor)
                                   .getInterface(Ci.nsIDOMWindowUtils);
  }

  /**
   * Dispatches a custom event on this element.
   *
   * @param  {String}    eventName Name of the event to dispatch.
   * @param  {Object}    [detail]  Event detail object. Optional.
   * @param  {Boolean}   cancelable If the event can be canceled.
   * @return {Boolean} `true` if the event was canceled by an event handler, `false`
   *                   otherwise.
   */
  dispatchCustomEvent(eventName, detail, cancelable = false) {
    let event = new this.window.CustomEvent(eventName, {
      detail,
      bubbles: true,
      cancelable,
    });
    this.node.dispatchEvent(event);
    return event.defaultPrevented;
  }

  /**
   * Dispatches a custom event on this element and waits for any blocking
   * promises registered using the "addBlocker" function on the details object.
   * If this function is called again, the event is only dispatched after all
   * the previously registered blockers have returned.
   *
   * The event can be canceled either by resolving any blocking promise to the
   * boolean value "false" or by calling preventDefault on the event. Rejections
   * and exceptions will be reported and will cancel the event.
   *
   * Blocking should be used sporadically because it slows down the interface.
   * Also, non-reentrancy is not strictly guaranteed because a safety timeout of
   * BLOCKERS_TIMEOUT_MS is implemented, after which the event will be canceled.
   * This helps to prevent deadlocks if any of the event handlers does not
   * resolve a blocker promise.
   *
   * @note Since there is no use case for dispatching different asynchronous
   *       events in parallel for the same element, this function will also wait
   *       for previous blockers when the event name is different.
   *
   * @param eventName
   *        Name of the custom event to dispatch.
   *
   * @resolves True if the event was canceled by a handler, false otherwise.
   */
  async dispatchAsyncEvent(eventName) {
    // Wait for all the previous blockers before dispatching the event.
    let blockersPromise = this._blockersPromise.catch(() => {});
    return this._blockersPromise = blockersPromise.then(async () => {
      let blockers = new Set();
      let cancel = this.dispatchCustomEvent(eventName, {
        addBlocker(promise) {
          // Any exception in the blocker will cancel the operation.
          blockers.add(promise.catch(ex => {
            Cu.reportError(ex);
            return true;
          }));
        },
      }, true);
      if (blockers.size) {
        let timeoutPromise = new Promise((resolve, reject) => {
          this.window.setTimeout(reject, BLOCKERS_TIMEOUT_MS);
        });
        try {
          let results = await Promise.race([Promise.all(blockers),
                                            timeoutPromise]);
          cancel = cancel || results.some(result => result === false);
        } catch (ex) {
          Cu.reportError(new Error(
            `One of the blockers for ${eventName} timed out.`));
          return true;
        }
      }
      return cancel;
    });
  }
};

/**
 * This is associated to <panelmultiview> elements.
 */
var PanelMultiView = class extends AssociatedToNode {
  /**
   * Tries to open the specified <panel> and displays the main view specified
   * with the "mainViewId" attribute on the <panelmultiview> node it contains.
   *
   * If the panel does not contain a <panelmultiview>, it is opened directly.
   * This allows consumers like page actions to accept different panel types.
   *
   * @see The non-static openPopup method for details.
   */
  static async openPopup(panelNode, ...args) {
    let panelMultiViewNode = panelNode.querySelector("panelmultiview");
    if (panelMultiViewNode) {
      return this.forNode(panelMultiViewNode).openPopup(...args);
    }
    panelNode.openPopup(...args);
    return true;
  }

  /**
   * Closes the specified <panel> which contains a <panelmultiview> node.
   *
   * If the panel does not contain a <panelmultiview>, it is closed directly.
   * This allows consumers like page actions to accept different panel types.
   *
   * @see The non-static hidePopup method for details.
   */
  static hidePopup(panelNode) {
    let panelMultiViewNode = panelNode.querySelector("panelmultiview");
    if (panelMultiViewNode) {
      this.forNode(panelMultiViewNode).hidePopup();
    } else {
      panelNode.hidePopup();
    }
  }

  /**
   * Removes the specified <panel> from the document, ensuring that any
   * <panelmultiview> node it contains is destroyed properly.
   *
   * If the viewCacheId attribute is present on the <panelmultiview> element,
   * imported subviews will be moved out again to the element it specifies, so
   * that the panel element can be removed safely.
   *
   * If the panel does not contain a <panelmultiview>, it is removed directly.
   * This allows consumers like page actions to accept different panel types.
   */
  static removePopup(panelNode) {
    try {
      let panelMultiViewNode = panelNode.querySelector("panelmultiview");
      if (panelMultiViewNode) {
        let panelMultiView = this.forNode(panelMultiViewNode);
        panelMultiView._moveOutKids();
        panelMultiView.disconnect();
      }
    } finally {
      // Make sure to remove the panel element even if disconnecting fails.
      panelNode.remove();
    }
  }

  /**
   * Ensures that when the specified window is closed all the <panelmultiview>
   * node it contains are destroyed properly.
   */
  static ensureUnloadHandlerRegistered(window) {
    if (gWindowsWithUnloadHandler.has(window)) {
      return;
    }

    window.addEventListener("unload", () => {
      for (let panelMultiViewNode of
           window.document.querySelectorAll("panelmultiview")) {
        this.forNode(panelMultiViewNode).disconnect();
      }
    }, { once: true });

    gWindowsWithUnloadHandler.add(window);
  }

  get _panel() {
    return this.node.parentNode;
  }

  set _transitioning(val) {
    if (val) {
      this.node.setAttribute("transitioning", "true");
    } else {
      this.node.removeAttribute("transitioning");
    }
  }

  get _screenManager() {
    if (this.__screenManager)
      return this.__screenManager;
    return this.__screenManager = Cc["@mozilla.org/gfx/screenmanager;1"]
                                    .getService(Ci.nsIScreenManager);
  }

  constructor(node) {
    super(node);
    this._openPopupPromise = Promise.resolve(false);
    this._openPopupCancelCallback = () => {};
  }

  connect() {
    this.connected = true;

    PanelMultiView.ensureUnloadHandlerRegistered(this.window);

    let viewContainer = this._viewContainer =
      this.document.createElement("box");
    viewContainer.classList.add("panel-viewcontainer");

    let viewStack = this._viewStack = this.document.createElement("box");
    viewStack.classList.add("panel-viewstack");
    viewContainer.append(viewStack);

    let offscreenViewContainer = this.document.createElement("box");
    offscreenViewContainer.classList.add("panel-viewcontainer", "offscreen");

    let offscreenViewStack = this._offscreenViewStack =
      this.document.createElement("box");
    offscreenViewStack.classList.add("panel-viewstack");
    offscreenViewContainer.append(offscreenViewStack);

    this.node.prepend(offscreenViewContainer);
    this.node.prepend(viewContainer);

    this.openViews = [];

    this._panel.addEventListener("popupshowing", this);
    this._panel.addEventListener("popuppositioned", this);
    this._panel.addEventListener("popuphidden", this);
    this._panel.addEventListener("popupshown", this);
    let cs = this.window.getComputedStyle(this.document.documentElement);
    // Set CSS-determined attributes now to prevent a layout flush when we do
    // it when transitioning between panels.
    this._dir = cs.direction;

    // Proxy these public properties and methods, as used elsewhere by various
    // parts of the browser, to this instance.
    ["goBack", "showSubView"].forEach(method => {
      Object.defineProperty(this.node, method, {
        enumerable: true,
        value: (...args) => this[method](...args)
      });
    });
  }

  disconnect() {
    // Guard against re-entrancy.
    if (!this.node || !this.connected)
      return;

    this._panel.removeEventListener("mousemove", this);
    this._panel.removeEventListener("popupshowing", this);
    this._panel.removeEventListener("popuppositioned", this);
    this._panel.removeEventListener("popupshown", this);
    this._panel.removeEventListener("popuphidden", this);
    this.window.removeEventListener("keydown", this);
    this.node = this._openPopupPromise = this._openPopupCancelCallback =
      this._viewContainer = this._viewStack = this.__dwu =
      this._transitionDetails = null;
  }

  /**
   * Tries to open the panel associated with this PanelMultiView, and displays
   * the main view specified with the "mainViewId" attribute.
   *
   * The hidePopup method can be called while the operation is in progress to
   * prevent the panel from being displayed. View events may also cancel the
   * operation, so there is no guarantee that the panel will become visible.
   *
   * The "popuphidden" event will be fired either when the operation is canceled
   * or when the popup is closed later. This event can be used for example to
   * reset the "open" state of the anchor or tear down temporary panels.
   *
   * If this method is called again before the panel is shown, the result
   * depends on the operation currently in progress. If the operation was not
   * canceled, the panel is opened using the arguments from the previous call,
   * and this call is ignored. If the operation was canceled, it will be
   * retried again using the arguments from this call.
   *
   * It's not necessary for the <panelmultiview> binding to be connected when
   * this method is called, but the containing panel must have its display
   * turned on, for example it shouldn't have the "hidden" attribute.
   *
   * @param args
   *        Arguments to be forwarded to the openPopup method of the panel.
   *
   * @resolves With true as soon as the request to display the panel has been
   *           sent, or with false if the operation was canceled. The state of
   *           the panel at this point is not guaranteed. It may be still
   *           showing, completely shown, or completely hidden.
   * @rejects If an exception is thrown at any point in the process before the
   *          request to display the panel is sent.
   */
  async openPopup(...args) {
    // Set up the function that allows hidePopup or a second call to showPopup
    // to cancel the specific panel opening operation that we're starting below.
    // This function must be synchronous, meaning we can't use Promise.race,
    // because hidePopup wants to dispatch the "popuphidden" event synchronously
    // even if the panel has not been opened yet.
    let canCancel = true;
    let cancelCallback = this._openPopupCancelCallback = () => {
      // If the cancel callback is called and the panel hasn't been prepared
      // yet, cancel showing it. Setting canCancel to false will prevent the
      // popup from opening. If the panel has opened by the time the cancel
      // callback is called, canCancel will be false already, and we will not
      // fire the "popuphidden" event.
      if (canCancel && this.node) {
        canCancel = false;
        this.dispatchCustomEvent("popuphidden");
      }
    };

    // Create a promise that is resolved with the result of the last call to
    // this method, where errors indicate that the panel was not opened.
    let openPopupPromise = this._openPopupPromise.catch(() => {
      return false;
    });

    // Make the preparation done before showing the panel non-reentrant. The
    // promise created here will be resolved only after the panel preparation is
    // completed, even if a cancellation request is received in the meantime.
    return this._openPopupPromise = openPopupPromise.then(async wasShown => {
      // The panel may have been destroyed in the meantime.
      if (!this.node) {
        return false;
      }
      // If the panel has been already opened there is nothing more to do. We
      // check the actual state of the panel rather than setting some state in
      // our handler of the "popuphidden" event because this has a lower chance
      // of locking indefinitely if events aren't raised in the expected order.
      if (wasShown && ["open", "showing"].includes(this._panel.state)) {
        return true;
      }
      try {
        if (!this.connected) {
          this.connect();
        }
        // Allow any of the ViewShowing handlers to prevent showing the main view.
        if (!(await this._showMainView())) {
          cancelCallback();
        }
      } catch (ex) {
        cancelCallback();
        throw ex;
      }
      // If a cancellation request was received there is nothing more to do.
      if (!canCancel || !this.node) {
        return false;
      }
      // We have to set canCancel to false before opening the popup because the
      // hidePopup method of PanelMultiView can be re-entered by event handlers.
      // If the openPopup call fails, however, we still have to dispatch the
      // "popuphidden" event even if canCancel was set to false.
      try {
        canCancel = false;
        this._panel.openPopup(...args);
        return true;
      } catch (ex) {
        this.dispatchCustomEvent("popuphidden");
        throw ex;
      }
    });
  }

  /**
   * Closes the panel associated with this PanelMultiView.
   *
   * If the openPopup method was called but the panel has not been displayed
   * yet, the operation is canceled and the panel will not be displayed, but the
   * "popuphidden" event is fired synchronously anyways.
   *
   * This means that by the time this method returns all the operations handled
   * by the "popuphidden" event are completed, for example resetting the "open"
   * state of the anchor, and the panel is already invisible.
   */
  hidePopup() {
    if (!this.node || !this.connected) {
      return;
    }

    // If we have already reached the _panel.openPopup call in the openPopup
    // method, we can call hidePopup. Otherwise, we have to cancel the latest
    // request to open the panel, which will have no effect if the request has
    // been canceled already.
    if (["open", "showing"].includes(this._panel.state)) {
      this._panel.hidePopup();
    } else {
      this._openPopupCancelCallback();
    }

    // We close all the views synchronously, so that they are ready to be opened
    // in other PanelMultiView instances. The "popuphidden" handler may also
    // call this function, but the second time openViews will be empty.
    this.closeAllViews();
  }

  /**
   * Move any child subviews into the element defined by "viewCacheId" to make
   * sure they will not be removed together with the <panelmultiview> element.
   */
  _moveOutKids() {
    let viewCacheId = this.node.getAttribute("viewCacheId");
    if (!viewCacheId) {
      return;
    }

    // Node.children and Node.childNodes is live to DOM changes like the
    // ones we're about to do, so iterate over a static copy:
    let subviews = Array.from(this._viewStack.childNodes);
    let viewCache = this.document.getElementById(viewCacheId);
    for (let subview of subviews) {
      viewCache.appendChild(subview);
    }
  }

  /**
   * Slides in the specified view as a subview.
   *
   * @param viewIdOrNode
   *        DOM element or string ID of the <panelview> to display.
   * @param anchor
   *        DOM element that triggered the subview, which will be highlighted
   *        and whose "label" attribute will be used for the title of the
   *        subview when a "title" attribute is not specified.
   */
  showSubView(viewIdOrNode, anchor) {
    this._showSubView(viewIdOrNode, anchor).catch(Cu.reportError);
  }
  async _showSubView(viewIdOrNode, anchor) {
    let viewNode = typeof viewIdOrNode == "string" ?
                   this.document.getElementById(viewIdOrNode) : viewIdOrNode;
    if (!viewNode) {
      Cu.reportError(new Error(`Subview ${viewIdOrNode} doesn't exist.`));
      return;
    }

    if (!this.openViews.length) {
      Cu.reportError(new Error(`Cannot show a subview in a closed panel.`));
      return;
    }

    let prevPanelView = this.openViews[this.openViews.length - 1];
    let nextPanelView = PanelView.forNode(viewNode);
    if (this.openViews.includes(nextPanelView)) {
      Cu.reportError(new Error(`Subview ${viewNode.id} is already open.`));
      return;
    }

    // Do not re-enter the process if navigation is already in progress. Since
    // there is only one active view at any given time, we can do this check
    // safely, even considering that during the navigation process the actual
    // view to which prevPanelView refers will change.
    if (!prevPanelView.active) {
      return;
    }
    // Marking the view that is about to scrolled out of the visible area as
    // inactive will prevent re-entrancy and also disable keyboard navigation.
    // From this point onwards, "await" statements can be used safely.
    prevPanelView.active = false;

    // Provide visual feedback while navigation is in progress, starting before
    // the transition starts and ending when the previous view is invisible.
    if (anchor) {
      anchor.setAttribute("open", "true");
    }
    try {
      // If the ViewShowing event cancels the operation we have to re-enable
      // keyboard navigation, but this must be avoided if the panel was closed.
      if (!(await this._openView(nextPanelView))) {
        if (prevPanelView.isOpenIn(this)) {
          // We don't raise a ViewShown event because nothing actually changed.
          // Technically we should use a different state flag just because there
          // is code that could check the "active" property to determine whether
          // to wait for a ViewShown event later, but this only happens in
          // regression tests and is less likely to be a technique used in
          // production code, where use of ViewShown is less common.
          prevPanelView.active = true;
        }
        return;
      }

      prevPanelView.captureKnownSize();

      // The main view of a panel can be a subview in another one. Make sure to
      // reset all the properties that may be set on a subview.
      nextPanelView.mainview = false;
      // The header may change based on how the subview was opened.
      nextPanelView.headerText = viewNode.getAttribute("title") ||
                                 (anchor && anchor.getAttribute("label"));
      // The constrained width of subviews may also vary between panels.
      nextPanelView.minMaxWidth = prevPanelView.knownWidth;

      if (anchor) {
        viewNode.classList.add("PanelUI-subView");
      }

      await this._transitionViews(prevPanelView.node, viewNode, false, anchor);
    } finally {
      if (anchor) {
        anchor.removeAttribute("open");
      }
    }

    this._activateView(nextPanelView);
  }

  /**
   * Navigates backwards by sliding out the most recent subview.
   */
  goBack() {
    this._goBack().catch(Cu.reportError);
  }
  async _goBack() {
    if (this.openViews.length < 2) {
      // This may be called by keyboard navigation or external code when only
      // the main view is open.
      return;
    }

    let prevPanelView = this.openViews[this.openViews.length - 1];
    let nextPanelView = this.openViews[this.openViews.length - 2];

    // Like in the showSubView method, do not re-enter navigation while it is
    // in progress, and make the view inactive immediately. From this point
    // onwards, "await" statements can be used safely.
    if (!prevPanelView.active) {
      return;
    }
    prevPanelView.active = false;

    prevPanelView.captureKnownSize();
    await this._transitionViews(prevPanelView.node, nextPanelView.node, true);

    this._closeLatestView();

    this._activateView(nextPanelView);
  }

  /**
   * Prepares the main view before showing the panel.
   */
  async _showMainView() {
    let nextPanelView = PanelView.forNode(this.document.getElementById(
      this.node.getAttribute("mainViewId")));

    // If the view is already open in another panel, close the panel first.
    let oldPanelMultiViewNode = nextPanelView.node.panelMultiView;
    if (oldPanelMultiViewNode) {
      PanelMultiView.forNode(oldPanelMultiViewNode).hidePopup();
      // Wait for a layout flush after hiding the popup, otherwise the view may
      // not be displayed correctly for some time after the new panel is opened.
      // This is filed as bug 1441015.
      await this.window.promiseDocumentFlushed(() => {});
    }

    if (!(await this._openView(nextPanelView))) {
      return false;
    }

    // The main view of a panel can be a subview in another one. Make sure to
    // reset all the properties that may be set on a subview.
    nextPanelView.mainview = true;
    nextPanelView.headerText = "";
    nextPanelView.minMaxWidth = 0;

    // Ensure the view will be visible once the panel is opened.
    nextPanelView.visible = true;
    nextPanelView.descriptionHeightWorkaround();

    return true;
  }

  /**
   * Opens the specified PanelView and dispatches the ViewShowing event, which
   * can be used to populate the subview or cancel the operation.
   *
   * This also clears all the attributes and styles that may be left by a
   * transition that was interrupted.
   *
   * @resolves With true if the view was opened, false otherwise.
   */
  async _openView(panelView) {
    if (panelView.node.parentNode != this._viewStack) {
      this._viewStack.appendChild(panelView.node);
    }

    panelView.node.panelMultiView = this.node;
    this.openViews.push(panelView);

    let canceled = await panelView.dispatchAsyncEvent("ViewShowing");

    // The panel can be hidden while we are processing the ViewShowing event.
    // This results in all the views being closed synchronously, and at this
    // point the ViewHiding event has already been dispatched for all of them.
    if (!this.openViews.length) {
      return false;
    }

    // Check if the event requested cancellation but the panel is still open.
    if (canceled) {
      // Handlers for ViewShowing can't know if a different handler requested
      // cancellation, so this will dispatch a ViewHiding event to give a chance
      // to clean up.
      this._closeLatestView();
      return false;
    }

    // Clean up all the attributes and styles related to transitions. We do this
    // here rather than when the view is closed because we are likely to make
    // other DOM modifications soon, which isn't the case when closing.
    let { style } = panelView.node;
    style.removeProperty("outline");
    style.removeProperty("width");

    return true;
  }

  /**
   * Activates the specified view and raises the ViewShown event, unless the
   * view was closed in the meantime.
   */
  _activateView(panelView) {
    if (panelView.isOpenIn(this)) {
      panelView.active = true;
      panelView.dispatchCustomEvent("ViewShown");
    }
  }

  /**
   * Closes the most recent PanelView and raises the ViewHiding event.
   *
   * @note The ViewHiding event is not cancelable and should probably be renamed
   *       to ViewHidden or ViewClosed instead, see bug 1438507.
   */
  _closeLatestView() {
    let panelView = this.openViews.pop();
    panelView.clearNavigation();
    panelView.dispatchCustomEvent("ViewHiding");
    panelView.node.panelMultiView = null;
    // Views become invisible synchronously when they are closed, and they won't
    // become visible again until they are opened. When this is called at the
    // end of backwards navigation, the view is already invisible.
    panelView.visible = false;
  }

  /**
   * Closes all the views that are currently open.
   */
  closeAllViews() {
    // Raise ViewHiding events for open views in reverse order.
    while (this.openViews.length) {
      this._closeLatestView();
    }
  }

  /**
   * Apply a transition to 'slide' from the currently active view to the next
   * one.
   * Sliding the next subview in means that the previous panelview stays where it
   * is and the active panelview slides in from the left in LTR mode, right in
   * RTL mode.
   *
   * @param {panelview} previousViewNode Node that is currently displayed, but
   *                                     is about to be transitioned away. This
   *                                     must be already inactive at this point.
   * @param {panelview} viewNode         Node that will becode the active view,
   *                                     after the transition has finished.
   * @param {Boolean}   reverse          Whether we're navigation back to a
   *                                     previous view or forward to a next view.
   */
  async _transitionViews(previousViewNode, viewNode, reverse) {
    const { window } = this;

    let nextPanelView = PanelView.forNode(viewNode);
    let prevPanelView = PanelView.forNode(previousViewNode);

    let details = this._transitionDetails = {
      phase: TRANSITION_PHASES.START,
    };

    // Set the viewContainer dimensions to make sure only the current view is
    // visible.
    let olderView = reverse ? nextPanelView : prevPanelView;
    this._viewContainer.style.minHeight = olderView.knownHeight + "px";
    this._viewContainer.style.height = prevPanelView.knownHeight + "px";
    this._viewContainer.style.width = prevPanelView.knownWidth + "px";
    // Lock the dimensions of the window that hosts the popup panel.
    let rect = this._panel.popupBoxObject.getOuterScreenRect();
    this._panel.setAttribute("width", rect.width);
    this._panel.setAttribute("height", rect.height);

    let viewRect;
    if (reverse) {
      // Use the cached size when going back to a previous view, but not when
      // reopening a subview, because its contents may have changed.
      viewRect = { width: nextPanelView.knownWidth,
                   height: nextPanelView.knownHeight };
      nextPanelView.visible = true;
    } else if (viewNode.customRectGetter) {
      // We use a customRectGetter for WebExtensions panels, because they need
      // to query the size from an embedded browser. The presence of this
      // getter also provides an indication that the view node shouldn't be
      // moved around, otherwise the state of the browser would get disrupted.
      let width = prevPanelView.knownWidth;
      let height = prevPanelView.knownHeight;
      viewRect = Object.assign({height, width}, viewNode.customRectGetter());
      nextPanelView.visible = true;
      // Until the header is visible, it has 0 height.
      // Wait for layout before measuring it
      let header = viewNode.firstChild;
      if (header && header.classList.contains("panel-header")) {
        viewRect.height += await window.promiseDocumentFlushed(() => {
          return this._dwu.getBoundsWithoutFlushing(header).height;
        });
      }
      await nextPanelView.descriptionHeightWorkaround();
    } else {
      this._offscreenViewStack.style.minHeight = olderView.knownHeight + "px";
      this._offscreenViewStack.appendChild(viewNode);
      nextPanelView.visible = true;

      // Now that the subview is visible, we can check the height of the
      // description elements it contains.
      await nextPanelView.descriptionHeightWorkaround();

      viewRect = await window.promiseDocumentFlushed(() => {
        return this._dwu.getBoundsWithoutFlushing(viewNode);
      });
      // Bail out if the panel was closed in the meantime.
      if (!nextPanelView.isOpenIn(this)) {
        return;
      }

      // Place back the view after all the other views that are already open in
      // order for the transition to work as expected.
      this._viewStack.appendChild(viewNode);

      this._offscreenViewStack.style.removeProperty("min-height");
    }

    this._transitioning = true;
    details.phase = TRANSITION_PHASES.PREPARE;

    // The 'magic' part: build up the amount of pixels to move right or left.
    let moveToLeft = (this._dir == "rtl" && !reverse) || (this._dir == "ltr" && reverse);
    let deltaX = prevPanelView.knownWidth;
    let deepestNode = reverse ? previousViewNode : viewNode;

    // With a transition when navigating backwards - user hits the 'back'
    // button - we need to make sure that the views are positioned in a way
    // that a translateX() unveils the previous view from the right direction.
    if (reverse)
      this._viewStack.style.marginInlineStart = "-" + deltaX + "px";

    // Set the transition style and listen for its end to clean up and make sure
    // the box sizing becomes dynamic again.
    // Somehow, putting these properties in PanelUI.css doesn't work for newly
    // shown nodes in a XUL parent node.
    this._viewStack.style.transition = "transform var(--animation-easing-function)" +
      " var(--panelui-subview-transition-duration)";
    this._viewStack.style.willChange = "transform";
    // Use an outline instead of a border so that the size is not affected.
    deepestNode.style.outline = "1px solid var(--panel-separator-color)";

    // Now that all the elements are in place for the start of the transition,
    // give the layout code a chance to set the initial values.
    await window.promiseDocumentFlushed(() => {});
    // Bail out if the panel was closed in the meantime.
    if (!nextPanelView.isOpenIn(this)) {
      return;
    }

    // Now set the viewContainer dimensions to that of the new view, which
    // kicks of the height animation.
    this._viewContainer.style.height = viewRect.height + "px";
    this._viewContainer.style.width = viewRect.width + "px";
    this._panel.removeAttribute("width");
    this._panel.removeAttribute("height");
    // We're setting the width property to prevent flickering during the
    // sliding animation with smaller views.
    viewNode.style.width = viewRect.width + "px";

    // Kick off the transition!
    details.phase = TRANSITION_PHASES.TRANSITION;
    this._viewStack.style.transform = "translateX(" + (moveToLeft ? "" : "-") + deltaX + "px)";

    await new Promise(resolve => {
      details.resolve = resolve;
      this._viewContainer.addEventListener("transitionend", details.listener = ev => {
        // It's quite common that `height` on the view container doesn't need
        // to transition, so we make sure to do all the work on the transform
        // transition-end, because that is guaranteed to happen.
        if (ev.target != this._viewStack || ev.propertyName != "transform")
          return;
        this._viewContainer.removeEventListener("transitionend", details.listener);
        delete details.listener;
        resolve();
      });
      this._viewContainer.addEventListener("transitioncancel", details.cancelListener = ev => {
        if (ev.target != this._viewStack)
          return;
        this._viewContainer.removeEventListener("transitioncancel", details.cancelListener);
        delete details.cancelListener;
        resolve();
      });
    });

    // Bail out if the panel was closed during the transition.
    if (!nextPanelView.isOpenIn(this)) {
      return;
    }
    prevPanelView.visible = false;

    // This will complete the operation by removing any transition properties.
    nextPanelView.node.style.removeProperty("width");
    deepestNode.style.removeProperty("outline");
    this._cleanupTransitionPhase();

    nextPanelView.focusSelectedElement();
  }

  /**
   * Attempt to clean up the attributes and properties set by `_transitionViews`
   * above. Which attributes and properties depends on the phase the transition
   * was left from.
   */
  _cleanupTransitionPhase() {
    if (!this._transitionDetails) {
      return;
    }

    let {phase, resolve, listener, cancelListener} = this._transitionDetails;
    this._transitionDetails = null;

    if (phase >= TRANSITION_PHASES.START) {
      this._panel.removeAttribute("width");
      this._panel.removeAttribute("height");
      this._viewContainer.style.removeProperty("height");
      this._viewContainer.style.removeProperty("width");
    }
    if (phase >= TRANSITION_PHASES.PREPARE) {
      this._transitioning = false;
      this._viewStack.style.removeProperty("margin-inline-start");
      this._viewStack.style.removeProperty("transition");
    }
    if (phase >= TRANSITION_PHASES.TRANSITION) {
      this._viewStack.style.removeProperty("transform");
      if (listener)
        this._viewContainer.removeEventListener("transitionend", listener);
      if (cancelListener)
        this._viewContainer.removeEventListener("transitioncancel", cancelListener);
      if (resolve)
        resolve();
    }
  }

  _calculateMaxHeight() {
    // While opening the panel, we have to limit the maximum height of any
    // view based on the space that will be available. We cannot just use
    // window.screen.availTop and availHeight because these may return an
    // incorrect value when the window spans multiple screens.
    let anchorBox = this._panel.anchorNode.boxObject;
    let screen = this._screenManager.screenForRect(anchorBox.screenX,
                                                   anchorBox.screenY,
                                                   anchorBox.width,
                                                   anchorBox.height);
    let availTop = {}, availHeight = {};
    screen.GetAvailRect({}, availTop, {}, availHeight);
    let cssAvailTop = availTop.value / screen.defaultCSSScaleFactor;

    // The distance from the anchor to the available margin of the screen is
    // based on whether the panel will open towards the top or the bottom.
    let maxHeight;
    if (this._panel.alignmentPosition.startsWith("before_")) {
      maxHeight = anchorBox.screenY - cssAvailTop;
    } else {
      let anchorScreenBottom = anchorBox.screenY + anchorBox.height;
      let cssAvailHeight = availHeight.value / screen.defaultCSSScaleFactor;
      maxHeight = cssAvailTop + cssAvailHeight - anchorScreenBottom;
    }

    // To go from the maximum height of the panel to the maximum height of
    // the view stack, we need to subtract the height of the arrow and the
    // height of the opposite margin, but we cannot get their actual values
    // because the panel is not visible yet. However, we know that this is
    // currently 11px on Mac, 13px on Windows, and 13px on Linux. We also
    // want an extra margin, both for visual reasons and to prevent glitches
    // due to small rounding errors. So, we just use a value that makes
    // sense for all platforms. If the arrow visuals change significantly,
    // this value will be easy to adjust.
    const EXTRA_MARGIN_PX = 20;
    maxHeight -= EXTRA_MARGIN_PX;
    return maxHeight;
  }

  handleEvent(aEvent) {
    if (aEvent.type.startsWith("popup") && aEvent.target != this._panel) {
      // Shouldn't act on e.g. context menus being shown from within the panel.
      return;
    }
    switch (aEvent.type) {
      case "keydown":
        // Since we start listening for the "keydown" event when the popup is
        // already showing and stop listening when the panel is hidden, we
        // always have at least one view open.
        let currentView = this.openViews[this.openViews.length - 1];
        currentView.keyNavigation(aEvent, this._dir);
        break;
      case "mousemove":
        this.openViews.forEach(panelView => panelView.clearNavigation());
        break;
      case "popupshowing": {
        this._viewContainer.setAttribute("panelopen", "true");
        if (!this.node.hasAttribute("disablekeynav")) {
          this.window.addEventListener("keydown", this);
          this._panel.addEventListener("mousemove", this);
        }
        break;
      }
      case "popuppositioned": {
        // When autoPosition is true, the popup window manager would attempt to re-position
        // the panel as subviews are opened and it changes size. The resulting popoppositioned
        // events triggers the binding's arrow position adjustment - and its reflow.
        // This is not needed here, as we calculated and set maxHeight so it is known
        // to fit the screen while open.
        // autoPosition gets reset after each popuppositioned event, and when the
        // popup closes, so we must set it back to false each time.
        this._panel.autoPosition = false;

        if (this._panel.state == "showing") {
          let maxHeight = this._calculateMaxHeight();
          this._viewStack.style.maxHeight = maxHeight + "px";
          this._offscreenViewStack.style.maxHeight = maxHeight + "px";
        }
        break;
      }
      case "popupshown":
        // The main view is always open and visible when the panel is first
        // shown, so we can check the height of the description elements it
        // contains and notify consumers using the ViewShown event. In order to
        // minimize flicker we need to allow synchronous reflows, and we still
        // make sure the ViewShown event is dispatched synchronously.
        let mainPanelView = this.openViews[0];
        mainPanelView.descriptionHeightWorkaround(true).catch(Cu.reportError);
        this._activateView(mainPanelView);
        break;
      case "popuphidden": {
        // WebExtensions consumers can hide the popup from viewshowing, or
        // mid-transition, which disrupts our state:
        this._transitioning = false;
        this._viewContainer.removeAttribute("panelopen");
        this._cleanupTransitionPhase();
        this.window.removeEventListener("keydown", this);
        this._panel.removeEventListener("mousemove", this);
        this.closeAllViews();

        // Clear the main view size caches. The dimensions could be different
        // when the popup is opened again, e.g. through touch mode sizing.
        this._viewContainer.style.removeProperty("min-height");
        this._viewStack.style.removeProperty("max-height");
        this._viewContainer.style.removeProperty("width");
        this._viewContainer.style.removeProperty("height");

        this.dispatchCustomEvent("PanelMultiViewHidden");
        break;
      }
    }
  }
};

/**
 * This is associated to <panelview> elements.
 */
var PanelView = class extends AssociatedToNode {
  constructor(node) {
    super(node);

    /**
     * Indicates whether the view is active. When this is false, consumers can
     * wait for the ViewShown event to know when the view becomes active.
     */
    this.active = false;
  }

  /**
   * Indicates whether the view is open in the specified PanelMultiView object.
   */
  isOpenIn(panelMultiView) {
    return this.node.panelMultiView == panelMultiView.node;
  }

  /**
   * The "mainview" attribute is set before the panel is opened when this view
   * is displayed as the main view, and is removed before the <panelview> is
   * displayed as a subview. The same view element can be displayed as a main
   * view and as a subview at different times.
   */
  set mainview(value) {
    if (value) {
      this.node.setAttribute("mainview", true);
    } else {
      this.node.removeAttribute("mainview");
    }
  }

  /**
   * Determines whether the view is visible. Setting this to false also resets
   * the "active" property.
   */
  set visible(value) {
    if (value) {
      this.node.setAttribute("visible", true);
    } else {
      this.node.removeAttribute("visible");
      this.active = false;
    }
  }

  /**
   * Constrains the width of this view using the "min-width" and "max-width"
   * styles. Setting this to zero removes the constraints.
   */
  set minMaxWidth(value) {
    let style = this.node.style;
    if (value) {
      style.minWidth = style.maxWidth = value + "px";
    } else {
      style.removeProperty("min-width");
      style.removeProperty("max-width");
    }
  }

  /**
   * Adds a header with the given title, or removes it if the title is empty.
   */
  set headerText(value) {
    // If the header already exists, update or remove it as requested.
    let header = this.node.firstChild;
    if (header && header.classList.contains("panel-header")) {
      if (value) {
        header.querySelector("label").setAttribute("value", value);
      } else {
        header.remove();
      }
      return;
    }

    // The header doesn't exist, only create it if needed.
    if (!value) {
      return;
    }

    header = this.document.createElement("box");
    header.classList.add("panel-header");

    let backButton = this.document.createElement("toolbarbutton");
    backButton.className =
      "subviewbutton subviewbutton-iconic subviewbutton-back";
    backButton.setAttribute("closemenu", "none");
    backButton.setAttribute("tabindex", "0");
    backButton.setAttribute("aria-label",
      gBundle.GetStringFromName("panel.back"));
    backButton.addEventListener("command", () => {
      // The panelmultiview element may change if the view is reused.
      this.node.panelMultiView.goBack();
      backButton.blur();
    });

    let label = this.document.createElement("label");
    label.setAttribute("value", value);

    header.append(backButton, label);
    this.node.prepend(header);
  }

  /**
   * Also make sure that the correct method is called on CustomizableWidget.
   */
  dispatchCustomEvent(...args) {
    CustomizableUI.ensureSubviewListeners(this.node);
    return super.dispatchCustomEvent(...args);
  }

  /**
   * Populates the "knownWidth" and "knownHeight" properties with the current
   * dimensions of the view. These may be zero if the view is invisible.
   *
   * These values are relevant during transitions and are retained for backwards
   * navigation if the view is still open but is invisible.
   */
  captureKnownSize() {
    let rect = this._dwu.getBoundsWithoutFlushing(this.node);
    this.knownWidth = rect.width;
    this.knownHeight = rect.height;
  }

  /**
   * If the main view or a subview contains wrapping elements, the attribute
   * "descriptionheightworkaround" should be set on the view to force all the
   * wrapping "description", "label" or "toolbarbutton" elements to a fixed
   * height. If the attribute is set and the visibility, contents, or width
   * of any of these elements changes, this function should be called to
   * refresh the calculated heights.
   *
   * @param allowSyncReflows
   *        If set to true, the function takes a path that allows synchronous
   *        reflows, but minimizes flickering. This is used for the main view
   *        because we cannot use the workaround off-screen.
   */
  async descriptionHeightWorkaround(allowSyncReflows = false) {
    if (!this.node.hasAttribute("descriptionheightworkaround")) {
      // This view does not require the workaround.
      return;
    }

    // We batch DOM changes together in order to reduce synchronous layouts.
    // First we reset any change we may have made previously. The first time
    // this is called, and in the best case scenario, this has no effect.
    let items = [];
    let collectItems = () => {
      // Non-hidden <label> or <description> elements that also aren't empty
      // and also don't have a value attribute can be multiline (if their
      // text content is long enough).
      let isMultiline = ":not(:-moz-any([hidden],[value],:empty))";
      let selector = [
        "description" + isMultiline,
        "label" + isMultiline,
        "toolbarbutton[wrap]:not([hidden])",
      ].join(",");
      for (let element of this.node.querySelectorAll(selector)) {
        // Ignore items in hidden containers.
        if (element.closest("[hidden]")) {
          continue;
        }
        // Take the label for toolbarbuttons; it only exists on those elements.
        element = element.labelElement || element;

        let bounds = element.getBoundingClientRect();
        let previous = gMultiLineElementsMap.get(element);
        // We don't need to (re-)apply the workaround for invisible elements or
        // on elements we've seen before and haven't changed in the meantime.
        if (!bounds.width || !bounds.height ||
            (previous && element.textContent == previous.textContent &&
                         bounds.width == previous.bounds.width)) {
          continue;
        }

        items.push({ element });
      }
    };
    if (allowSyncReflows) {
      collectItems();
    } else {
      await this.window.promiseDocumentFlushed(collectItems);
      // Bail out if the panel was closed in the meantime.
      if (!this.node.panelMultiView) {
        return;
      }
    }

    // Removing the 'height' property will only cause a layout flush in the next
    // loop below if it was set.
    for (let item of items) {
      item.element.style.removeProperty("height");
    }

    // We now read the computed style to store the height of any element that
    // may contain wrapping text.
    let measureItems = () => {
      for (let item of items) {
        item.bounds = item.element.getBoundingClientRect();
      }
    };
    if (allowSyncReflows) {
      measureItems();
    } else {
      await this.window.promiseDocumentFlushed(measureItems);
      // Bail out if the panel was closed in the meantime.
      if (!this.node.panelMultiView) {
        return;
      }
    }

    // Now we can make all the necessary DOM changes at once.
    for (let { element, bounds } of items) {
      gMultiLineElementsMap.set(element, { bounds, textContent: element.textContent });
      element.style.height = bounds.height + "px";
    }
  }

  /**
   * Retrieves the button elements that can be used for navigation using the
   * keyboard, that is all enabled buttons including the back button if visible.
   *
   * @return {Array}
   */
  getNavigableElements() {
    let buttons = Array.from(this.node.querySelectorAll(".subviewbutton:not([disabled])"));
    let dwu = this._dwu;
    return buttons.filter(button => {
      let bounds = dwu.getBoundsWithoutFlushing(button);
      return bounds.width > 0 && bounds.height > 0;
    });
  }

  /**
   * Element that is currently selected with the keyboard, or null if no element
   * is selected. Since the reference is held weakly, it can become null or
   * undefined at any time.
   *
   * The element is usually, but not necessarily, in the "buttons" property
   * which in turn is initialized from the getNavigableElements list.
   */
  get selectedElement() {
    return this._selectedElement && this._selectedElement.get();
  }
  set selectedElement(value) {
    if (!value) {
      delete this._selectedElement;
    } else {
      this._selectedElement = Cu.getWeakReference(value);
    }
  }

  /**
   * Based on going up or down, select the previous or next focusable button.
   *
   * @param {Boolean} isDown   whether we're going down (true) or up (false).
   *
   * @return {DOMNode} the button we selected.
   */
  moveSelection(isDown) {
    let buttons = this.buttons;
    let lastSelected = this.selectedElement;
    let newButton = null;
    let maxIdx = buttons.length - 1;
    if (lastSelected) {
      let buttonIndex = buttons.indexOf(lastSelected);
      if (buttonIndex != -1) {
        // Buttons may get selected whilst the panel is shown, so add an extra
        // check here.
        do {
          buttonIndex = buttonIndex + (isDown ? 1 : -1);
        } while (buttons[buttonIndex] && buttons[buttonIndex].disabled);
        if (isDown && buttonIndex > maxIdx)
          buttonIndex = 0;
        else if (!isDown && buttonIndex < 0)
          buttonIndex = maxIdx;
        newButton = buttons[buttonIndex];
      } else {
        // The previously selected item is no longer selectable. Find the next item:
        let allButtons = lastSelected.closest("panelview").getElementsByTagName("toolbarbutton");
        let maxAllButtonIdx = allButtons.length - 1;
        let allButtonIndex = allButtons.indexOf(lastSelected);
        while (allButtonIndex >= 0 && allButtonIndex <= maxAllButtonIdx) {
          allButtonIndex++;
          // Check if the next button is in the list of focusable buttons.
          buttonIndex = buttons.indexOf(allButtons[allButtonIndex]);
          if (buttonIndex != -1) {
            // If it is, just use that button if we were going down, or the previous one
            // otherwise. If this was the first button, newButton will end up undefined,
            // which is fine because we'll fall back to using the last button at the
            // bottom of this method.
            newButton = buttons[isDown ? buttonIndex : buttonIndex - 1];
            break;
          }
        }
      }
    }

    // If we couldn't find something, select the first or last item:
    if (!newButton) {
      newButton = buttons[isDown ? 0 : maxIdx];
    }
    this.selectedElement = newButton;
    return newButton;
  }

  /**
   * Allow for navigating subview buttons using the arrow keys and the Enter key.
   * The Up and Down keys can be used to navigate the list up and down and the
   * Enter, Right or Left - depending on the text direction - key can be used to
   * simulate a click on the currently selected button.
   * The Right or Left key - depending on the text direction - can be used to
   * navigate to the previous view, functioning as a shortcut for the view's
   * back button.
   * Thus, in LTR mode:
   *  - The Right key functions the same as the Enter key, simulating a click
   *  - The Left key triggers a navigation back to the previous view.
   *
   * Key navigation is only enabled while the view is active, meaning that this
   * method will return early if it is invoked during a sliding transition.
   *
   * @param {KeyEvent} event
   * @param {String} dir
   *        Direction for arrow navigation, either "ltr" or "rtl".
   */
  keyNavigation(event, dir) {
    if (!this.active) {
      return;
    }

    let buttons = this.buttons;
    if (!buttons || !buttons.length) {
      buttons = this.buttons = this.getNavigableElements();
      // Set the 'tabindex' attribute on the buttons to make sure they're focussable.
      for (let button of buttons) {
        if (!button.classList.contains("subviewbutton-back") &&
            !button.hasAttribute("tabindex")) {
          button.setAttribute("tabindex", 0);
        }
      }
    }
    if (!buttons.length)
      return;

    let stop = () => {
      event.stopPropagation();
      event.preventDefault();
    };

    let keyCode = event.code;
    switch (keyCode) {
      case "ArrowDown":
      case "ArrowUp":
      case "Tab": {
        stop();
        let isDown = (keyCode == "ArrowDown") ||
                     (keyCode == "Tab" && !event.shiftKey);
        let button = this.moveSelection(isDown);
        button.focus();
        break;
      }
      case "ArrowLeft":
      case "ArrowRight": {
        stop();
        if ((dir == "ltr" && keyCode == "ArrowLeft") ||
            (dir == "rtl" && keyCode == "ArrowRight")) {
          this.node.panelMultiView.goBack();
          break;
        }
        // If the current button is _not_ one that points to a subview, pressing
        // the arrow key shouldn't do anything.
        let button = this.selectedElement;
        if (!button || !button.classList.contains("subviewbutton-nav")) {
          break;
        }
        // Fall-through...
      }
      case "Space":
      case "Enter": {
        let button = this.selectedElement;
        if (!button)
          break;
        stop();

        // Unfortunately, 'tabindex' doesn't execute the default action, so
        // we explicitly do this here.
        // We are sending a command event and then a click event.
        // This is done in order to mimic a "real" mouse click event.
        // The command event executes the action, then the click event closes the menu.
        button.doCommand();
        let clickEvent = new event.target.ownerGlobal.MouseEvent("click", {"bubbles": true});
        button.dispatchEvent(clickEvent);
        break;
      }
    }
  }

  /**
   * Focus the last selected element in the view, if any.
   */
  focusSelectedElement() {
    let selected = this.selectedElement;
    if (selected) {
      selected.focus();
    }
  }

  /**
   * Clear all traces of keyboard navigation happening right now.
   */
  clearNavigation() {
    delete this.buttons;
    let selected = this.selectedElement;
    if (selected) {
      selected.blur();
      this.selectedElement = null;
    }
  }
};
PK
!<=n~̄�!modules/FormValidationHandler.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/*
 * Chrome side handling of form validation popup.
 */

"use strict";

var EXPORTED_SYMBOLS = [ "FormValidationHandler" ];

ChromeUtils.import("resource://gre/modules/Services.jsm");

var FormValidationHandler =
{
  _panel: null,
  _anchor: null,

  /*
   * Public apis
   */

  uninit() {
    this._panel = null;
    this._anchor = null;
  },

  hidePopup() {
    this._hidePopup();
  },

  /*
   * Events
   */

  // Listeners are added in nsBrowserGlue.js
  receiveMessage(aMessage) {
    let window = aMessage.target.ownerGlobal;
    let json = aMessage.json;
    let tabBrowser = window.gBrowser;
    switch (aMessage.name) {
      case "FormValidation:ShowPopup":
        // target is the <browser>, make sure we're receiving a message
        // from the foreground tab.
        if (tabBrowser && aMessage.target != tabBrowser.selectedBrowser) {
          return;
        }
        this._showPopup(window, json);
        break;
      case "FormValidation:HidePopup":
        this._hidePopup();
        break;
    }
  },

  observe(aSubject, aTopic, aData) {
    this._hidePopup();
  },

  handleEvent(aEvent) {
    switch (aEvent.type) {
      case "FullZoomChange":
      case "TextZoomChange":
      case "ZoomChangeUsingMouseWheel":
      case "scroll":
        this._hidePopup();
        break;
      case "popuphiding":
        this._onPopupHiding(aEvent);
        break;
    }
  },

  /*
   * Internal
   */

  _onPopupHiding(aEvent) {
    aEvent.originalTarget.removeEventListener("popuphiding", this, true);
    let tabBrowser = aEvent.originalTarget.ownerGlobal.gBrowser;
    tabBrowser.selectedBrowser.removeEventListener("scroll", this, true);
    tabBrowser.selectedBrowser.removeEventListener("FullZoomChange", this);
    tabBrowser.selectedBrowser.removeEventListener("TextZoomChange", this);
    tabBrowser.selectedBrowser.removeEventListener("ZoomChangeUsingMouseWheel", this);

    this._panel.hidden = true;
    this._panel = null;
    this._anchor.hidden = true;
    this._anchor = null;
  },

  /*
   * Shows the form validation popup at a specified position or updates the
   * messaging and position if the popup is already displayed.
   *
   * @aWindow - the chrome window
   * @aPanelData - Object that contains popup information
   *  aPanelData stucture detail:
   *   contentRect - the bounding client rect of the target element. If
   *    content is remote, this is relative to the browser, otherwise its
   *    relative to the window.
   *   position - popup positional string constants.
   *   message - the form element validation message text.
   */
  _showPopup(aWindow, aPanelData) {
    let previouslyShown = !!this._panel;
    this._panel = aWindow.document.getElementById("invalid-form-popup");
    this._panel.firstChild.textContent = aPanelData.message;
    this._panel.hidden = false;

    let tabBrowser = aWindow.gBrowser;
    this._anchor = tabBrowser.popupAnchor;
    this._anchor.left = aPanelData.contentRect.left;
    this._anchor.top = aPanelData.contentRect.top;
    this._anchor.width = aPanelData.contentRect.width;
    this._anchor.height = aPanelData.contentRect.height;
    this._anchor.hidden = false;

    // Display the panel if it isn't already visible.
    if (!previouslyShown) {
      // Cleanup after the popup is hidden
      this._panel.addEventListener("popuphiding", this, true);

      // Hide if the user scrolls the page
      tabBrowser.selectedBrowser.addEventListener("scroll", this, true);
      tabBrowser.selectedBrowser.addEventListener("FullZoomChange", this);
      tabBrowser.selectedBrowser.addEventListener("TextZoomChange", this);
      tabBrowser.selectedBrowser.addEventListener("ZoomChangeUsingMouseWheel", this);

      // Open the popup
      this._panel.openPopup(this._anchor, aPanelData.position, 0, 0, false);
    }
  },

  /*
   * Hide the popup if currently displayed. Will fire an event to onPopupHiding
   * above if visible.
   */
  _hidePopup() {
    if (this._panel) {
      this._panel.hidePopup();
    }
  }
};
PK
!<n�Lř�3chrome/en-US/locale/browser/searchplugins/list.json{"RU": {"visibleDefaultEngines": ["google-nocodes", "amazondotcom", "bing", "ddg", "ebay", "twitter", "wikipedia"]}, "CH": {"visibleDefaultEngines": ["google", "amazondotcom", "bing", "ddg", "ebay-ch", "twitter", "wikipedia"]}, "NL": {"visibleDefaultEngines": ["google", "amazondotcom", "bing", "ddg", "ebay-nl", "twitter", "wikipedia"]}, "CN": {"visibleDefaultEngines": ["google-nocodes", "amazondotcom", "bing", "ddg", "ebay", "twitter", "wikipedia"]}, "BE": {"visibleDefaultEngines": ["google", "amazondotcom", "bing", "ddg", "ebay-be", "twitter", "wikipedia"]}, "default": {"visibleDefaultEngines": ["google", "amazondotcom", "bing", "ddg", "ebay", "twitter", "wikipedia"]}, "CA": {"visibleDefaultEngines": ["google", "amazondotcom", "bing", "ddg", "ebay-ca", "twitter", "wikipedia"]}, "TR": {"visibleDefaultEngines": ["google-nocodes", "amazondotcom", "bing", "ddg", "ebay", "twitter", "wikipedia"]}, "US": {"visibleDefaultEngines": ["google-2018", "amazondotcom", "bing", "ddg", "ebay", "twitter", "wikipedia"]}, "experimental-hidden": {"visibleDefaultEngines": ["amazon-ca", "amazon-au", "google-2018", "yandex-en"]}, "GB": {"visibleDefaultEngines": ["google", "amazondotcom", "bing", "ddg", "ebay-uk", "twitter", "wikipedia"]}, "KZ": {"visibleDefaultEngines": ["google-nocodes", "amazondotcom", "bing", "ddg", "ebay", "twitter", "wikipedia"]}, "AU": {"visibleDefaultEngines": ["google", "amazondotcom", "bing", "ddg", "ebay-au", "twitter", "wikipedia"]}, "IE": {"visibleDefaultEngines": ["google", "amazondotcom", "bing", "ddg", "ebay-ie", "twitter", "wikipedia"]}, "BY": {"visibleDefaultEngines": ["google-nocodes", "amazondotcom", "bing", "ddg", "ebay", "twitter", "wikipedia"]}}PK
!<4�A�/
/
2chrome/browser/content/browser/contentSearchUI.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

.contentSearchSuggestionTable {
  background-color: hsla(0,0%,100%,.99);
  border: 1px solid hsla(0, 0%, 0%, .2);
  border-top: none;
  box-shadow: 0 5px 10px hsla(0, 0%, 0%, .1);
  position: absolute;
  left: 0;
  z-index: 1001;
  -moz-user-select: none;
  cursor: default;
}

.contentSearchSuggestionTable:dir(rtl) {
  left: auto;
  right: 0;
}

.contentSearchSuggestionsList {
  border-bottom: 1px solid hsl(0, 0%, 92%);
  width: 100%;
  height: 100%;
}

.contentSearchSuggestionTable,
.contentSearchSuggestionsList {
  border-spacing: 0;
  overflow: hidden;
  padding: 0;
  margin: 0;
  text-align: start;
}

.contentSearchHeaderRow,
.contentSearchSuggestionRow {
  margin: 0;
  max-width: inherit;
  padding: 0;
}

.contentSearchHeaderRow > td > img,
.contentSearchSuggestionRow > td > .historyIcon {
  margin-inline-end: 8px;
  margin-bottom: -3px;
}

.contentSearchSuggestionTable .historyIcon {
  width: 16px;
  height: 16px;
  display: inline-block;
  background-image: url("chrome://browser/skin/history.svg");
  -moz-context-properties: fill;
  fill: graytext;
}

.contentSearchSuggestionRow.selected > td > .historyIcon {
  fill: HighlightText;
}

.contentSearchHeader > img {
  height: 16px;
  width: 16px;
  margin: 0;
  padding: 0;
}

.contentSearchSuggestionRow.remote > td > .historyIcon {
  visibility: hidden;
}

.contentSearchSuggestionRow.selected {
  background-color: Highlight;
  color: HighlightText;
}

.contentSearchHeader,
.contentSearchSuggestionEntry {
  margin: 0;
  max-width: inherit;
  overflow: hidden;
  padding: 4px 10px;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 75%;
}

.contentSearchHeader {
  background-color: hsl(0, 0%, 97%);
  color: #666;
  border-bottom: 1px solid hsl(0, 0%, 92%);
}

.contentSearchSuggestionsContainer {
  margin: 0;
  padding: 0;
  border-spacing: 0;
  width: 100%;
}

.contentSearchSearchWithHeaderSearchText {
  white-space: pre;
  font-weight: bold;
}

.contentSearchOneOffItem {
  -moz-appearance: none;
  height: 32px;
  margin: 0;
  padding: 0;
  border: none;
  background: none;
  background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAWCAYAAAABxvaqAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gofECQNNVW2/AAAABBJREFUGFdjOHPmzH8GehEA/KpKg9YTf4AAAAAASUVORK5CYII=');
  background-repeat: no-repeat;
  background-position: right center;
}

.contentSearchOneOffItem:dir(rtl) {
  background-position: left center;
}

.contentSearchOneOffItem > img {
  width: 16px;
  height: 16px;
  margin-bottom: -2px;
}

.contentSearchOneOffItem:not(.last-row) {
  border-bottom: 1px solid hsl(0, 0%, 92%);
}

.contentSearchOneOffItem.end-of-row {
  background-image: none;
}

.contentSearchOneOffItem.selected {
  background-color: Highlight;
  background-image: none;
}

.contentSearchOneOffsTable {
  width: 100%;
}

.contentSearchSettingsButton {
  margin: 0;
  padding: 0;
  height: 32px;
  border: none;
  border-top: 1px solid hsla(0, 0%, 0%, .08);
  text-align: center;
  width: 100%;
}

.contentSearchSettingsButton.selected {
  background-color: hsl(0, 0%, 90%);
}

.contentSearchSettingsButton:active {
  background-color: hsl(0, 0%, 85%);
}
PK
!<b���v�v1chrome/browser/content/browser/contentSearchUI.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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.ContentSearchUIController = (function() {

const MAX_DISPLAYED_SUGGESTIONS = 6;
const SUGGESTION_ID_PREFIX = "searchSuggestion";
const ONE_OFF_ID_PREFIX = "oneOff";
const DEFAULT_INPUT_ICON = "chrome://browser/skin/search-glass.svg";

const HTML_NS = "http://www.w3.org/1999/xhtml";

/**
 * Creates a new object that manages search suggestions and their UI for a text
 * box.
 *
 * The UI consists of an html:table that's inserted into the DOM after the given
 * text box and styled so that it appears as a dropdown below the text box.
 *
 * @param inputElement
 *        Search suggestions will be based on the text in this text box.
 *        Assumed to be an html:input.  xul:textbox is untested but might work.
 * @param tableParent
 *        The suggestion table is appended as a child to this element.  Since
 *        the table is absolutely positioned and its top and left values are set
 *        to be relative to the top and left of the page, either the parent and
 *        all its ancestors should not be positioned elements (i.e., their
 *        positions should be "static"), or the parent's position should be the
 *        top left of the page.
 * @param healthReportKey
 *        This will be sent with the search data for FHR to record the search.
 * @param searchPurpose
 *        Sent with search data, see nsISearchEngine.getSubmission.
 * @param idPrefix
 *        The IDs of elements created by the object will be prefixed with this
 *        string.
 */
function ContentSearchUIController(inputElement, tableParent, healthReportKey,
                                   searchPurpose, idPrefix = "") {
  this.input = inputElement;
  this._idPrefix = idPrefix;
  this._healthReportKey = healthReportKey;
  this._searchPurpose = searchPurpose;

  let tableID = idPrefix + "searchSuggestionTable";
  this.input.autocomplete = "off";
  this.input.setAttribute("aria-autocomplete", "true");
  this.input.setAttribute("aria-controls", tableID);
  tableParent.appendChild(this._makeTable(tableID));

  this.input.addEventListener("keypress", this);
  this.input.addEventListener("input", this);
  this.input.addEventListener("focus", this);
  this.input.addEventListener("blur", this);
  window.addEventListener("ContentSearchService", this);

  this._stickyInputValue = "";
  this._hideSuggestions();

  this._getSearchEngines();
  this._getStrings();
}

ContentSearchUIController.prototype = {

  _oneOffButtons: [],
  // Setting up the one off buttons causes an uninterruptible reflow. If we
  // receive the list of engines while the newtab page is loading, this reflow
  // may regress performance - so we set this flag and only set up the buttons
  // if it's set when the suggestions table is actually opened.
  _pendingOneOffRefresh: undefined,

  get defaultEngine() {
    return this._defaultEngine;
  },

  set defaultEngine(engine) {
    if (this._defaultEngine && this._defaultEngine.icon) {
      URL.revokeObjectURL(this._defaultEngine.icon);
    }
    let icon;
    if (engine.iconBuffer) {
      icon = this._getFaviconURIFromBuffer(engine.iconBuffer);
    } else {
      icon = "chrome://mozapps/skin/places/defaultFavicon.svg";
    }
    this._defaultEngine = {
      name: engine.name,
      icon,
    };
    this._updateDefaultEngineHeader();
    this._updateDefaultEngineIcon();

    if (engine && document.activeElement == this.input) {
      this._speculativeConnect();
    }
  },

  get engines() {
    return this._engines;
  },

  set engines(val) {
    this._engines = val;
    this._pendingOneOffRefresh = true;
  },

  // The selectedIndex is the index of the element with the "selected" class in
  // the list obtained by concatenating the suggestion rows, one-off buttons, and
  // search settings button.
  get selectedIndex() {
    let allElts = [...this._suggestionsList.children,
                   ...this._oneOffButtons,
                   document.getElementById("contentSearchSettingsButton")];
    for (let i = 0; i < allElts.length; ++i) {
      let elt = allElts[i];
      if (elt.classList.contains("selected")) {
        return i;
      }
    }
    return -1;
  },

  set selectedIndex(idx) {
    // Update the table's rows, and the input when there is a selection.
    this._table.removeAttribute("aria-activedescendant");
    this.input.removeAttribute("aria-activedescendant");

    let allElts = [...this._suggestionsList.children,
                   ...this._oneOffButtons,
                   document.getElementById("contentSearchSettingsButton")];
    // If we are selecting a suggestion and a one-off is selected, don't deselect it.
    let excludeIndex = idx < this.numSuggestions && this.selectedButtonIndex > -1 ?
                       this.numSuggestions + this.selectedButtonIndex : -1;
    for (let i = 0; i < allElts.length; ++i) {
      let elt = allElts[i];
      let ariaSelectedElt = i < this.numSuggestions ? elt.firstChild : elt;
      if (i == idx) {
        elt.classList.add("selected");
        ariaSelectedElt.setAttribute("aria-selected", "true");
        this.input.setAttribute("aria-activedescendant", ariaSelectedElt.id);
      } else if (i != excludeIndex) {
        elt.classList.remove("selected");
        ariaSelectedElt.setAttribute("aria-selected", "false");
      }
    }
  },

  get selectedButtonIndex() {
    let elts = [...this._oneOffButtons,
                document.getElementById("contentSearchSettingsButton")];
    for (let i = 0; i < elts.length; ++i) {
      if (elts[i].classList.contains("selected")) {
        return i;
      }
    }
    return -1;
  },

  set selectedButtonIndex(idx) {
    let elts = [...this._oneOffButtons,
                document.getElementById("contentSearchSettingsButton")];
    for (let i = 0; i < elts.length; ++i) {
      let elt = elts[i];
      if (i == idx) {
        elt.classList.add("selected");
        elt.setAttribute("aria-selected", "true");
      } else {
        elt.classList.remove("selected");
        elt.setAttribute("aria-selected", "false");
      }
    }
  },

  get selectedEngineName() {
    let selectedElt = this._oneOffsTable.querySelector(".selected");
    if (selectedElt) {
      return selectedElt.engineName;
    }
    return this.defaultEngine.name;
  },

  get numSuggestions() {
    return this._suggestionsList.children.length;
  },

  selectAndUpdateInput(idx) {
    this.selectedIndex = idx;
    let newValue = this.suggestionAtIndex(idx) || this._stickyInputValue;
    // Setting the input value when the value has not changed commits the current
    // IME composition, which we don't want to do.
    if (this.input.value != newValue) {
      this.input.value = newValue;
    }
    this._updateSearchWithHeader();
  },

  suggestionAtIndex(idx) {
    let row = this._suggestionsList.children[idx];
    return row ? row.textContent : null;
  },

  deleteSuggestionAtIndex(idx) {
    // Only form history suggestions can be deleted.
    if (this.isFormHistorySuggestionAtIndex(idx)) {
      let suggestionStr = this.suggestionAtIndex(idx);
      this._sendMsg("RemoveFormHistoryEntry", suggestionStr);
      this._suggestionsList.children[idx].remove();
      this.selectAndUpdateInput(-1);
    }
  },

  isFormHistorySuggestionAtIndex(idx) {
    let row = this._suggestionsList.children[idx];
    return row && row.classList.contains("formHistory");
  },

  addInputValueToFormHistory() {
    this._sendMsg("AddFormHistoryEntry", this.input.value);
  },

  handleEvent(event) {
    this["_on" + event.type[0].toUpperCase() + event.type.substr(1)](event);
  },

  _onCommand(aEvent) {
    if (this.selectedButtonIndex == this._oneOffButtons.length) {
      // Settings button was selected.
      this._sendMsg("ManageEngines");
      return;
    }

    this.search(aEvent);

    if (aEvent) {
      aEvent.preventDefault();
    }
  },

  search(aEvent) {
    if (!this.defaultEngine) {
      return; // Not initialized yet.
    }

    let searchText = this.input;
    let searchTerms;
    if (this._table.hidden ||
        (aEvent.originalTarget &&
          aEvent.originalTarget.id == "contentSearchDefaultEngineHeader") ||
        aEvent instanceof KeyboardEvent) {
      searchTerms = searchText.value;
    } else {
      searchTerms = this.suggestionAtIndex(this.selectedIndex) || searchText.value;
    }
    // Send an event that will perform a search and Firefox Health Report will
    // record that a search from the healthReportKey passed to the constructor.
    let eventData = {
      engineName: this.selectedEngineName,
      searchString: searchTerms,
      healthReportKey: this._healthReportKey,
      searchPurpose: this._searchPurpose,
      originalEvent: {
        shiftKey: aEvent.shiftKey,
        ctrlKey: aEvent.ctrlKey,
        metaKey: aEvent.metaKey,
        altKey: aEvent.altKey,
      },
    };
    if ("button" in aEvent) {
      eventData.originalEvent.button = aEvent.button;
    }

    if (this.suggestionAtIndex(this.selectedIndex)) {
      eventData.selection = {
        index: this.selectedIndex,
        kind: undefined,
      };
      if (aEvent instanceof MouseEvent) {
        eventData.selection.kind = "mouse";
      } else if (aEvent instanceof KeyboardEvent) {
        eventData.selection.kind = "key";
      }
    }

    this._sendMsg("Search", eventData);
    this.addInputValueToFormHistory();
  },

  _onInput() {
    if (!this.input.value) {
      this._stickyInputValue = "";
      this._hideSuggestions();
    } else if (this.input.value != this._stickyInputValue) {
      // Only fetch new suggestions if the input value has changed.
      this._getSuggestions();
      this.selectAndUpdateInput(-1);
    }
    this._updateSearchWithHeader();
  },

  _onKeypress(event) {
    let selectedIndexDelta = 0;
    let selectedSuggestionDelta = 0;
    let selectedOneOffDelta = 0;

    switch (event.keyCode) {
    case event.DOM_VK_UP:
      if (this._table.hidden) {
        return;
      }
      if (event.getModifierState("Accel")) {
        if (event.shiftKey) {
          selectedSuggestionDelta = -1;
          break;
        }
        this._cycleCurrentEngine(true);
        break;
      }
      if (event.altKey) {
        selectedOneOffDelta = -1;
        break;
      }
      selectedIndexDelta = -1;
      break;
    case event.DOM_VK_DOWN:
      if (this._table.hidden) {
        this._getSuggestions();
        return;
      }
      if (event.getModifierState("Accel")) {
        if (event.shiftKey) {
          selectedSuggestionDelta = 1;
          break;
        }
        this._cycleCurrentEngine(false);
        break;
      }
      if (event.altKey) {
        selectedOneOffDelta = 1;
        break;
      }
      selectedIndexDelta = 1;
      break;
    case event.DOM_VK_TAB:
      if (this._table.hidden) {
        return;
      }
      // Shift+tab when either the first or no one-off is selected, as well as
      // tab when the settings button is selected, should change focus as normal.
      if ((this.selectedButtonIndex <= 0 && event.shiftKey) ||
          this.selectedButtonIndex == this._oneOffButtons.length && !event.shiftKey) {
        return;
      }
      selectedOneOffDelta = event.shiftKey ? -1 : 1;
      break;
    case event.DOM_VK_RIGHT:
      // Allow normal caret movement until the caret is at the end of the input.
      if (this.input.selectionStart != this.input.selectionEnd ||
          this.input.selectionEnd != this.input.value.length) {
        return;
      }
      if (this.numSuggestions && this.selectedIndex >= 0 &&
          this.selectedIndex < this.numSuggestions) {
        this.input.value = this.suggestionAtIndex(this.selectedIndex);
        this.input.setAttribute("selection-index", this.selectedIndex);
        this.input.setAttribute("selection-kind", "key");
      } else {
        // If we didn't select anything, make sure to remove the attributes
        // in case they were populated last time.
        this.input.removeAttribute("selection-index");
        this.input.removeAttribute("selection-kind");
      }
      this._stickyInputValue = this.input.value;
      this._hideSuggestions();
      return;
    case event.DOM_VK_RETURN:
      this._onCommand(event);
      return;
    case event.DOM_VK_DELETE:
      if (this.selectedIndex >= 0) {
        this.deleteSuggestionAtIndex(this.selectedIndex);
      }
      return;
    case event.DOM_VK_ESCAPE:
      if (!this._table.hidden) {
        this._hideSuggestions();
      }
      return;
    default:
      return;
    }

    let currentIndex = this.selectedIndex;
    if (selectedIndexDelta) {
      let newSelectedIndex = currentIndex + selectedIndexDelta;
      if (newSelectedIndex < -1) {
        newSelectedIndex = this.numSuggestions + this._oneOffButtons.length;
      }
      // If are moving up from the first one off, we have to deselect the one off
      // manually because the selectedIndex setter tries to exclude the selected
      // one-off (which is desirable for accel+shift+up/down).
      if (currentIndex == this.numSuggestions && selectedIndexDelta == -1) {
        this.selectedButtonIndex = -1;
      }
      this.selectAndUpdateInput(newSelectedIndex);
    } else if (selectedSuggestionDelta) {
      let newSelectedIndex;
      if (currentIndex >= this.numSuggestions || currentIndex == -1) {
        // No suggestion already selected, select the first/last one appropriately.
        newSelectedIndex = selectedSuggestionDelta == 1 ?
                           0 : this.numSuggestions - 1;
      } else {
        newSelectedIndex = currentIndex + selectedSuggestionDelta;
      }
      if (newSelectedIndex >= this.numSuggestions) {
        newSelectedIndex = -1;
      }
      this.selectAndUpdateInput(newSelectedIndex);
    } else if (selectedOneOffDelta) {
      let newSelectedIndex;
      let currentButton = this.selectedButtonIndex;
      if (currentButton == -1 || currentButton == this._oneOffButtons.length) {
        // No one-off already selected, select the first/last one appropriately.
        newSelectedIndex = selectedOneOffDelta == 1 ?
                           0 : this._oneOffButtons.length - 1;
      } else {
        newSelectedIndex = currentButton + selectedOneOffDelta;
      }
      // Allow selection of the settings button via the tab key.
      if (newSelectedIndex == this._oneOffButtons.length &&
          event.keyCode != event.DOM_VK_TAB) {
        newSelectedIndex = -1;
      }
      this.selectedButtonIndex = newSelectedIndex;
    }

    // Prevent the input's caret from moving.
    event.preventDefault();
  },

  _currentEngineIndex: -1,
  _cycleCurrentEngine(aReverse) {
    if ((this._currentEngineIndex == this._engines.length - 1 && !aReverse) ||
        (this._currentEngineIndex == 0 && aReverse)) {
      return;
    }
    this._currentEngineIndex += aReverse ? -1 : 1;
    let engineName = this._engines[this._currentEngineIndex].name;
    this._sendMsg("SetCurrentEngine", engineName);
  },

  _onFocus() {
    if (this._mousedown) {
      return;
    }
    // When the input box loses focus to something in our table, we refocus it
    // immediately. This causes the focus highlight to flicker, so we set a
    // custom attribute which consumers should use for focus highlighting. This
    // attribute is removed only when we do not immediately refocus the input
    // box, thus eliminating flicker.
    this.input.setAttribute("keepfocus", "true");
    this._speculativeConnect();
  },

  _onBlur() {
    if (this._mousedown) {
      // At this point, this.input has lost focus, but a new element has not yet
      // received it. If we re-focus this.input directly, the new element will
      // steal focus immediately, so we queue it instead.
      setTimeout(() => this.input.focus(), 0);
      return;
    }
    this.input.removeAttribute("keepfocus");
    this._hideSuggestions();
  },

  _onMousemove(event) {
    let idx = this._indexOfTableItem(event.target);
    if (idx >= this.numSuggestions) {
      // Deselect any search suggestion that has been selected.
      this.selectedIndex = -1;
      this.selectedButtonIndex = idx - this.numSuggestions;
      return;
    }
    this.selectedIndex = idx;
  },

  _onMouseup(event) {
    if (event.button == 2) {
      return;
    }
    this._onCommand(event);
  },

  _onMouseout(event) {
    // We only deselect one-off buttons and the settings button when they are
    // moused out.
    let idx = this._indexOfTableItem(event.originalTarget);
    if (idx >= this.numSuggestions) {
      this.selectedButtonIndex = -1;
    }
  },

  _onClick(event) {
    this._onMouseup(event);
  },

  _onContentSearchService(event) {
    let methodName = "_onMsg" + event.detail.type;
    if (methodName in this) {
      this[methodName](event.detail.data);
    }
  },

  _onMsgFocusInput(event) {
    this.input.focus();
  },

  _onMsgBlur(event) {
    this.input.blur();
    this._hideSuggestions();
  },

  _onMsgSuggestions(suggestions) {
    // Ignore the suggestions if their search string or engine doesn't match
    // ours.  Due to the async nature of message passing, this can easily happen
    // when the user types quickly.
    if (this._stickyInputValue != suggestions.searchString ||
        this.defaultEngine.name != suggestions.engineName) {
      return;
    }

    this._clearSuggestionRows();

    // Position and size the table.
    let { left } = this.input.getBoundingClientRect();
    this._table.style.top = this.input.offsetHeight + "px";
    this._table.style.minWidth = this.input.offsetWidth + "px";
    this._table.style.maxWidth = (window.innerWidth - left - 40) + "px";

    // Add the suggestions to the table.
    let searchWords =
      new Set(suggestions.searchString.trim().toLowerCase().split(/\s+/));
    for (let i = 0; i < MAX_DISPLAYED_SUGGESTIONS; i++) {
      let type, idx;
      if (i < suggestions.formHistory.length) {
        [type, idx] = ["formHistory", i];
      } else {
        let j = i - suggestions.formHistory.length;
        if (j < suggestions.remote.length) {
          [type, idx] = ["remote", j];
        } else {
          break;
        }
      }
      this._suggestionsList.appendChild(
        this._makeTableRow(type, suggestions[type][idx], i, searchWords));
    }

    if (this._table.hidden) {
      this.selectedIndex = -1;
      if (this._pendingOneOffRefresh) {
        this._setUpOneOffButtons();
        delete this._pendingOneOffRefresh;
      }
      this._currentEngineIndex =
        this._engines.findIndex(aEngine => aEngine.name == this.defaultEngine.name);
      this._table.hidden = false;
      this.input.setAttribute("aria-expanded", "true");
      this._originalDefaultEngine = {
        name: this.defaultEngine.name,
        icon: this.defaultEngine.icon,
      };
    }
  },

  _onMsgSuggestionsCancelled() {
    if (!this._table.hidden) {
      this._hideSuggestions();
    }
  },

  _onMsgState(state) {
    this.engines = state.engines;
    // No point updating the default engine (and the header) if there's no change.
    if (this.defaultEngine &&
        this.defaultEngine.name == state.currentEngine.name &&
        this.defaultEngine.icon == state.currentEngine.icon) {
      return;
    }
    this.defaultEngine = state.currentEngine;
  },

  _onMsgCurrentState(state) {
    this._onMsgState(state);
  },

  _onMsgCurrentEngine(engine) {
    this.defaultEngine = engine;
    this._pendingOneOffRefresh = true;
  },

  _onMsgStrings(strings) {
    this._strings = strings;
    this._updateDefaultEngineHeader();
    this._updateSearchWithHeader();
    document.getElementById("contentSearchSettingsButton").textContent =
      this._strings.searchSettings;
  },

  _updateDefaultEngineIcon() {
    let eng = this._engines.find(engine => engine.name === this.defaultEngine.name);
    if (eng) {
      // We only show the engines icon for default engines, otherwise show
      // a default; default engines have an identifier
      let icon = eng.identifier ? this.defaultEngine.icon : DEFAULT_INPUT_ICON;
      document.body.style.setProperty("--newtab-search-icon", "url(" + icon + ")");
    }
  },

  _updateDefaultEngineHeader() {
    let header = document.getElementById("contentSearchDefaultEngineHeader");
    header.firstChild.setAttribute("src", this.defaultEngine.icon);
    if (!this._strings) {
      return;
    }
    while (header.firstChild.nextSibling) {
      header.firstChild.nextSibling.remove();
    }
    header.appendChild(document.createTextNode(
      this._strings.searchHeader.replace("%S", this.defaultEngine.name)));
  },

  _updateSearchWithHeader() {
    if (!this._strings) {
      return;
    }
    let searchWithHeader = document.getElementById("contentSearchSearchWithHeader");
    if (this.input.value) {
      // eslint-disable-next-line no-unsanitized/property
      searchWithHeader.innerHTML = this._strings.searchForSomethingWith;
      searchWithHeader.querySelector(".contentSearchSearchWithHeaderSearchText").textContent = this.input.value;
    } else {
      searchWithHeader.textContent = this._strings.searchWithHeader;
    }
  },

  _speculativeConnect() {
    if (this.defaultEngine) {
      this._sendMsg("SpeculativeConnect", this.defaultEngine.name);
    }
  },

  _makeTableRow(type, suggestionStr, currentRow, searchWords) {
    let row = document.createElementNS(HTML_NS, "tr");
    row.dir = "auto";
    row.classList.add("contentSearchSuggestionRow");
    row.classList.add(type);
    row.setAttribute("role", "presentation");
    row.addEventListener("mousemove", this);
    row.addEventListener("mouseup", this);

    let entry = document.createElementNS(HTML_NS, "td");
    let img = document.createElementNS(HTML_NS, "div");
    img.setAttribute("class", "historyIcon");
    entry.appendChild(img);
    entry.classList.add("contentSearchSuggestionEntry");
    entry.setAttribute("role", "option");
    entry.id = this._idPrefix + SUGGESTION_ID_PREFIX + currentRow;
    entry.setAttribute("aria-selected", "false");

    let suggestionWords = suggestionStr.trim().toLowerCase().split(/\s+/);
    for (let i = 0; i < suggestionWords.length; i++) {
      let word = suggestionWords[i];
      let wordSpan = document.createElementNS(HTML_NS, "span");
      if (searchWords.has(word)) {
        wordSpan.classList.add("typed");
      }
      wordSpan.textContent = word;
      entry.appendChild(wordSpan);
      if (i < suggestionWords.length - 1) {
        entry.appendChild(document.createTextNode(" "));
      }
    }

    row.appendChild(entry);
    return row;
  },

  // Converts favicon array buffer into a data URI.
  _getFaviconURIFromBuffer(buffer) {
    let blob = new Blob([buffer]);
    return URL.createObjectURL(blob);
  },

  // Adds "@2x" to the name of the given PNG url for "retina" screens.
  _getImageURIForCurrentResolution(uri) {
    if (window.devicePixelRatio > 1) {
      return uri.replace(/\.png$/, "@2x.png");
    }
    return uri;
  },

  _getSearchEngines() {
    this._sendMsg("GetState");
  },

  _getStrings() {
    this._sendMsg("GetStrings");
  },

  _getSuggestions() {
    this._stickyInputValue = this.input.value;
    if (this.defaultEngine) {
      this._sendMsg("GetSuggestions", {
        engineName: this.defaultEngine.name,
        searchString: this.input.value,
      });
    }
  },

  _clearSuggestionRows() {
    while (this._suggestionsList.firstElementChild) {
      this._suggestionsList.firstElementChild.remove();
    }
  },

  _hideSuggestions() {
    this.input.setAttribute("aria-expanded", "false");
    this.selectedIndex = -1;
    this.selectedButtonIndex = -1;
    this._currentEngineIndex = -1;
    this._table.hidden = true;
  },

  _indexOfTableItem(elt) {
    if (elt.classList.contains("contentSearchOneOffItem")) {
      return this.numSuggestions + this._oneOffButtons.indexOf(elt);
    }
    if (elt.classList.contains("contentSearchSettingsButton")) {
      return this.numSuggestions + this._oneOffButtons.length;
    }
    while (elt && elt.localName != "tr") {
      elt = elt.parentNode;
    }
    if (!elt) {
      throw new Error("Element is not a row");
    }
    return elt.rowIndex;
  },

  _makeTable(id) {
    this._table = document.createElementNS(HTML_NS, "table");
    this._table.id = id;
    this._table.hidden = true;
    this._table.classList.add("contentSearchSuggestionTable");
    this._table.setAttribute("role", "presentation");

    // When the search input box loses focus, we want to immediately give focus
    // back to it if the blur was because the user clicked somewhere in the table.
    // onBlur uses the _mousedown flag to detect this.
    this._table.addEventListener("mousedown", () => { this._mousedown = true; });
    document.addEventListener("mouseup", () => { delete this._mousedown; });

    // Deselect the selected element on mouseout if it wasn't a suggestion.
    this._table.addEventListener("mouseout", this);

    let headerRow = document.createElementNS(HTML_NS, "tr");
    let header = document.createElementNS(HTML_NS, "td");
    headerRow.setAttribute("class", "contentSearchHeaderRow");
    header.setAttribute("class", "contentSearchHeader");
    let iconImg = document.createElementNS(HTML_NS, "img");
    header.appendChild(iconImg);
    header.id = "contentSearchDefaultEngineHeader";
    headerRow.appendChild(header);
    headerRow.addEventListener("click", this);
    this._table.appendChild(headerRow);

    let row = document.createElementNS(HTML_NS, "tr");
    row.setAttribute("class", "contentSearchSuggestionsContainer");
    let cell = document.createElementNS(HTML_NS, "td");
    cell.setAttribute("class", "contentSearchSuggestionsContainer");
    this._suggestionsList = document.createElementNS(HTML_NS, "table");
    this._suggestionsList.setAttribute("class", "contentSearchSuggestionsList");
    cell.appendChild(this._suggestionsList);
    row.appendChild(cell);
    this._table.appendChild(row);
    this._suggestionsList.setAttribute("role", "listbox");

    this._oneOffsTable = document.createElementNS(HTML_NS, "table");
    this._oneOffsTable.setAttribute("class", "contentSearchOneOffsTable");
    this._oneOffsTable.classList.add("contentSearchSuggestionsContainer");
    this._oneOffsTable.setAttribute("role", "group");
    this._table.appendChild(this._oneOffsTable);

    headerRow = document.createElementNS(HTML_NS, "tr");
    header = document.createElementNS(HTML_NS, "td");
    headerRow.setAttribute("class", "contentSearchHeaderRow");
    header.setAttribute("class", "contentSearchHeader");
    headerRow.appendChild(header);
    header.id = "contentSearchSearchWithHeader";
    this._oneOffsTable.appendChild(headerRow);

    let button = document.createElementNS(HTML_NS, "button");
    button.setAttribute("class", "contentSearchSettingsButton");
    button.classList.add("contentSearchHeaderRow");
    button.classList.add("contentSearchHeader");
    button.id = "contentSearchSettingsButton";
    button.addEventListener("click", this);
    button.addEventListener("mousemove", this);
    this._table.appendChild(button);

    return this._table;
  },

  _setUpOneOffButtons() {
    // Sometimes we receive a CurrentEngine message from the ContentSearch service
    // before we've received a State message - i.e. before we have our engines.
    if (!this._engines) {
      return;
    }

    while (this._oneOffsTable.firstChild.nextSibling) {
      this._oneOffsTable.firstChild.nextSibling.remove();
    }

    this._oneOffButtons = [];

    let engines = this._engines.filter(aEngine => aEngine.name != this.defaultEngine.name)
                               .filter(aEngine => !aEngine.hidden);
    if (!engines.length) {
      this._oneOffsTable.hidden = true;
      return;
    }

    const kDefaultButtonWidth = 49; // 48px + 1px border.
    let rowWidth = this.input.offsetWidth - 2; // 2px border.
    let enginesPerRow = Math.floor(rowWidth / kDefaultButtonWidth);
    let buttonWidth = Math.floor(rowWidth / enginesPerRow);

    let row = document.createElementNS(HTML_NS, "tr");
    let cell = document.createElementNS(HTML_NS, "td");
    row.setAttribute("class", "contentSearchSuggestionsContainer");
    cell.setAttribute("class", "contentSearchSuggestionsContainer");

    for (let i = 0; i < engines.length; ++i) {
      let engine = engines[i];
      if (i > 0 && i % enginesPerRow == 0) {
        row.appendChild(cell);
        this._oneOffsTable.appendChild(row);
        row = document.createElementNS(HTML_NS, "tr");
        cell = document.createElementNS(HTML_NS, "td");
        row.setAttribute("class", "contentSearchSuggestionsContainer");
        cell.setAttribute("class", "contentSearchSuggestionsContainer");
      }
      let button = document.createElementNS(HTML_NS, "button");
      button.setAttribute("class", "contentSearchOneOffItem");
      let img = document.createElementNS(HTML_NS, "img");
      let uri;
      if (engine.iconBuffer) {
        uri = this._getFaviconURIFromBuffer(engine.iconBuffer);
      } else {
        uri = this._getImageURIForCurrentResolution(
          "chrome://browser/skin/search-engine-placeholder.png");
      }
      img.setAttribute("src", uri);
      img.addEventListener("load", function() {
        URL.revokeObjectURL(uri);
      }, {once: true});
      button.appendChild(img);
      button.style.width = buttonWidth + "px";
      button.setAttribute("title", engine.name);

      button.engineName = engine.name;
      button.addEventListener("click", this);
      button.addEventListener("mousemove", this);

      if (engines.length - i <= enginesPerRow - (i % enginesPerRow)) {
        button.classList.add("last-row");
      }

      if ((i + 1) % enginesPerRow == 0) {
        button.classList.add("end-of-row");
      }

      button.id = ONE_OFF_ID_PREFIX + i;
      cell.appendChild(button);
      this._oneOffButtons.push(button);
    }
    row.appendChild(cell);
    this._oneOffsTable.appendChild(row);
    this._oneOffsTable.hidden = false;
  },

  _sendMsg(type, data = null) {
    dispatchEvent(new CustomEvent("ContentSearchClient", {
      detail: {
        type,
        data,
      },
    }));
  },
};

return ContentSearchUIController;
})();
PK
!<a�89>H>Hmodules/ContentSearch.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";

var EXPORTED_SYMBOLS = [
  "ContentSearch",
];

ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");

Cu.importGlobalProperties(["XMLHttpRequest"]);

ChromeUtils.defineModuleGetter(this, "FormHistory",
  "resource://gre/modules/FormHistory.jsm");
ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
  "resource://gre/modules/PrivateBrowsingUtils.jsm");
ChromeUtils.defineModuleGetter(this, "SearchSuggestionController",
  "resource://gre/modules/SearchSuggestionController.jsm");

const INBOUND_MESSAGE = "ContentSearch";
const OUTBOUND_MESSAGE = INBOUND_MESSAGE;
const MAX_LOCAL_SUGGESTIONS = 3;
const MAX_SUGGESTIONS = 6;

/**
 * ContentSearch receives messages named INBOUND_MESSAGE and sends messages
 * named OUTBOUND_MESSAGE.  The data of each message is expected to look like
 * { type, data }.  type is the message's type (or subtype if you consider the
 * type of the message itself to be INBOUND_MESSAGE), and data is data that is
 * specific to the type.
 *
 * Inbound messages have the following types:
 *
 *   AddFormHistoryEntry
 *     Adds an entry to the search form history.
 *     data: the entry, a string
 *   GetSuggestions
 *     Retrieves an array of search suggestions given a search string.
 *     data: { engineName, searchString }
 *   GetState
 *     Retrieves the current search engine state.
 *     data: null
 *   GetStrings
 *     Retrieves localized search UI strings.
 *     data: null
 *   ManageEngines
 *     Opens the search engine management window.
 *     data: null
 *   RemoveFormHistoryEntry
 *     Removes an entry from the search form history.
 *     data: the entry, a string
 *   Search
 *     Performs a search.
 *     Any GetSuggestions messages in the queue from the same target will be
 *     cancelled.
 *     data: { engineName, searchString, healthReportKey, searchPurpose }
 *   SetCurrentEngine
 *     Sets the current engine.
 *     data: the name of the engine
 *   SpeculativeConnect
 *     Speculatively connects to an engine.
 *     data: the name of the engine
 *
 * Outbound messages have the following types:
 *
 *   CurrentEngine
 *     Broadcast when the current engine changes.
 *     data: see _currentEngineObj
 *   CurrentState
 *     Broadcast when the current search state changes.
 *     data: see currentStateObj
 *   State
 *     Sent in reply to GetState.
 *     data: see currentStateObj
 *   Strings
 *     Sent in reply to GetStrings
 *     data: Object containing string names and values for the current locale.
 *   Suggestions
 *     Sent in reply to GetSuggestions.
 *     data: see _onMessageGetSuggestions
 *   SuggestionsCancelled
 *     Sent in reply to GetSuggestions when pending GetSuggestions events are
 *     cancelled.
 *     data: null
 */

var ContentSearch = {

  // Inbound events are queued and processed in FIFO order instead of handling
  // them immediately, which would result in non-FIFO responses due to the
  // asynchrononicity added by converting image data URIs to ArrayBuffers.
  _eventQueue: [],
  _currentEventPromise: null,

  // This is used to handle search suggestions.  It maps xul:browsers to objects
  // { controller, previousFormHistoryResult }.  See _onMessageGetSuggestions.
  _suggestionMap: new WeakMap(),

  // Resolved when we finish shutting down.
  _destroyedPromise: null,

  // The current controller and browser in _onMessageGetSuggestions.  Allows
  // fetch cancellation from _cancelSuggestions.
  _currentSuggestion: null,

  init() {
    Services.obs.addObserver(this, "browser-search-engine-modified");
    Services.obs.addObserver(this, "shutdown-leaks-before-check");
    Services.prefs.addObserver("browser.search.hiddenOneOffs", this);
    this._stringBundle = Services.strings.createBundle("chrome://global/locale/autocomplete.properties");
  },

  get searchSuggestionUIStrings() {
    if (this._searchSuggestionUIStrings) {
      return this._searchSuggestionUIStrings;
    }
    this._searchSuggestionUIStrings = {};
    let searchBundle = Services.strings.createBundle("chrome://browser/locale/search.properties");
    let stringNames = ["searchHeader", "searchForSomethingWith",
                       "searchWithHeader", "searchSettings"];

    for (let name of stringNames) {
      this._searchSuggestionUIStrings[name] = searchBundle.GetStringFromName(name);
    }
    return this._searchSuggestionUIStrings;
  },

  destroy() {
    if (this._destroyedPromise) {
      return this._destroyedPromise;
    }

    Services.obs.removeObserver(this, "browser-search-engine-modified");
    Services.obs.removeObserver(this, "shutdown-leaks-before-check");

    this._eventQueue.length = 0;
    this._destroyedPromise = Promise.resolve(this._currentEventPromise);
    return this._destroyedPromise;
  },

  /**
   * Focuses the search input in the page with the given message manager.
   * @param  messageManager
   *         The MessageManager object of the selected browser.
   */
  focusInput(messageManager) {
    messageManager.sendAsyncMessage(OUTBOUND_MESSAGE, {
      type: "FocusInput"
    });
  },

  // Listeners and observers are added in nsBrowserGlue.js
  receiveMessage(msg) {
    // Add a temporary event handler that exists only while the message is in
    // the event queue.  If the message's source docshell changes browsers in
    // the meantime, then we need to update msg.target.  event.detail will be
    // the docshell's new parent <xul:browser> element.
    msg.handleEvent = event => {
      let browserData = this._suggestionMap.get(msg.target);
      if (browserData) {
        this._suggestionMap.delete(msg.target);
        this._suggestionMap.set(event.detail, browserData);
      }
      msg.target.removeEventListener("SwapDocShells", msg, true);
      msg.target = event.detail;
      msg.target.addEventListener("SwapDocShells", msg, true);
    };
    msg.target.addEventListener("SwapDocShells", msg, true);

    // Search requests cause cancellation of all Suggestion requests from the
    // same browser.
    if (msg.data.type === "Search") {
      this._cancelSuggestions(msg);
    }

    this._eventQueue.push({
      type: "Message",
      data: msg,
    });
    this._processEventQueue();
  },

  observe(subj, topic, data) {
    switch (topic) {
    case "nsPref:changed":
    case "browser-search-engine-modified":
      this._eventQueue.push({
        type: "Observe",
        data,
      });
      this._processEventQueue();
      break;
    case "shutdown-leaks-before-check":
      subj.wrappedJSObject.client.addBlocker(
        "ContentSearch: Wait until the service is destroyed", () => this.destroy());
      break;
    }
  },

  removeFormHistoryEntry(msg, entry) {
    let browserData = this._suggestionDataForBrowser(msg.target);
    if (browserData && browserData.previousFormHistoryResult) {
      let { previousFormHistoryResult } = browserData;
      for (let i = 0; i < previousFormHistoryResult.matchCount; i++) {
        if (previousFormHistoryResult.getValueAt(i) === entry) {
          previousFormHistoryResult.removeValueAt(i, true);
          break;
        }
      }
    }
  },

  performSearch(msg, data) {
    this._ensureDataHasProperties(data, [
      "engineName",
      "searchString",
      "healthReportKey",
      "searchPurpose",
    ]);
    let engine = Services.search.getEngineByName(data.engineName);
    let submission = engine.getSubmission(data.searchString, "", data.searchPurpose);
    let browser = msg.target;
    let win = browser.ownerGlobal;
    if (!win) {
      // The browser may have been closed between the time its content sent the
      // message and the time we handle it.
      return;
    }
    let where = win.whereToOpenLink(data.originalEvent);

    // There is a chance that by the time we receive the search message, the user
    // has switched away from the tab that triggered the search. If, based on the
    // event, we need to load the search in the same tab that triggered it (i.e.
    // where === "current"), openUILinkIn will not work because that tab is no
    // longer the current one. For this case we manually load the URI.
    if (where === "current") {
      // Since we're going to load the search in the same browser, blur the search
      // UI to prevent further interaction before we start loading.
      this._reply(msg, "Blur");
      browser.loadURIWithFlags(submission.uri.spec,
                               Ci.nsIWebNavigation.LOAD_FLAGS_NONE, null, null,
                               submission.postData);
    } else {
      let params = {
        postData: submission.postData,
        inBackground: Services.prefs.getBoolPref("browser.tabs.loadInBackground"),
      };
      win.openUILinkIn(submission.uri.spec, where, params);
    }
    win.BrowserSearch.recordSearchInTelemetry(engine, data.healthReportKey,
                                              { selection: data.selection });
  },

  async getSuggestions(engineName, searchString, browser) {
    let engine = Services.search.getEngineByName(engineName);
    if (!engine) {
      throw new Error("Unknown engine name: " + engineName);
    }

    let browserData = this._suggestionDataForBrowser(browser, true);
    let { controller } = browserData;
    let ok = SearchSuggestionController.engineOffersSuggestions(engine);
    controller.maxLocalResults = ok ? MAX_LOCAL_SUGGESTIONS : MAX_SUGGESTIONS;
    controller.maxRemoteResults = ok ? MAX_SUGGESTIONS : 0;
    let priv = PrivateBrowsingUtils.isBrowserPrivate(browser);
    // fetch() rejects its promise if there's a pending request, but since we
    // process our event queue serially, there's never a pending request.
    this._currentSuggestion = { controller, target: browser };
    let suggestions = await controller.fetch(searchString, priv, engine);
    this._currentSuggestion = null;

    // suggestions will be null if the request was cancelled
    let result = {};
    if (!suggestions) {
      return result;
    }

    // Keep the form history result so RemoveFormHistoryEntry can remove entries
    // from it.  Keeping only one result isn't foolproof because the client may
    // try to remove an entry from one set of suggestions after it has requested
    // more but before it's received them.  In that case, the entry may not
    // appear in the new suggestions.  But that should happen rarely.
    browserData.previousFormHistoryResult = suggestions.formHistoryResult;
    result = {
      engineName,
      term: suggestions.term,
      local: suggestions.local,
      remote: suggestions.remote,
    };
    return result;
  },

  async addFormHistoryEntry(browser, entry = "") {
    let isPrivate = false;
    try {
      // isBrowserPrivate assumes that the passed-in browser has all the normal
      // properties, which won't be true if the browser has been destroyed.
      // That may be the case here due to the asynchronous nature of messaging.
      isPrivate = PrivateBrowsingUtils.isBrowserPrivate(browser.target);
    } catch (err) {
      return false;
    }
    if (isPrivate || entry === "") {
      return false;
    }
    let browserData = this._suggestionDataForBrowser(browser.target, true);
    FormHistory.update({
      op: "bump",
      fieldname: browserData.controller.formHistoryParam,
      value: entry,
    }, {
      handleCompletion: () => {},
      handleError: err => {
        Cu.reportError("Error adding form history entry: " + err);
      },
    });
    return true;
  },

  async currentStateObj(uriFlag = false) {
    let state = {
      engines: [],
      currentEngine: await this._currentEngineObj(),
    };
    if (uriFlag) {
      state.currentEngine.iconBuffer = Services.search.currentEngine.getIconURLBySize(16, 16);
    }
    let pref = Services.prefs.getCharPref("browser.search.hiddenOneOffs");
    let hiddenList = pref ? pref.split(",") : [];
    for (let engine of Services.search.getVisibleEngines()) {
      let uri = engine.getIconURLBySize(16, 16);
      let iconBuffer = uri;
      if (!uriFlag) {
        iconBuffer = await this._arrayBufferFromDataURI(uri);
      }
      state.engines.push({
        name: engine.name,
        iconBuffer,
        hidden: hiddenList.includes(engine.name),
        identifier: engine.identifier
      });
    }
    return state;
  },

  _processEventQueue() {
    if (this._currentEventPromise || !this._eventQueue.length) {
      return;
    }

    let event = this._eventQueue.shift();

    this._currentEventPromise = (async () => {
      try {
        await this["_on" + event.type](event.data);
      } catch (err) {
        Cu.reportError(err);
      } finally {
        this._currentEventPromise = null;
        this._processEventQueue();
      }
    })();
  },

  _cancelSuggestions(msg) {
    let cancelled = false;
    // cancel active suggestion request
    if (this._currentSuggestion && this._currentSuggestion.target === msg.target) {
      this._currentSuggestion.controller.stop();
      cancelled = true;
    }
    // cancel queued suggestion requests
    for (let i = 0; i < this._eventQueue.length; i++) {
      let m = this._eventQueue[i].data;
      if (msg.target === m.target && m.data.type === "GetSuggestions") {
        this._eventQueue.splice(i, 1);
        cancelled = true;
        i--;
      }
    }
    if (cancelled) {
      this._reply(msg, "SuggestionsCancelled");
    }
  },

  async _onMessage(msg) {
    let methodName = "_onMessage" + msg.data.type;
    if (methodName in this) {
      await this._initService();
      await this[methodName](msg, msg.data.data);
      if (!Cu.isDeadWrapper(msg.target)) {
        msg.target.removeEventListener("SwapDocShells", msg, true);
      }
    }
  },

  _onMessageGetState(msg, data) {
    return this.currentStateObj().then(state => {
      this._reply(msg, "State", state);
    });
  },

  _onMessageGetStrings(msg, data) {
    this._reply(msg, "Strings", this.searchSuggestionUIStrings);
  },

  _onMessageSearch(msg, data) {
    this.performSearch(msg, data);
  },

  _onMessageSetCurrentEngine(msg, data) {
    Services.search.currentEngine = Services.search.getEngineByName(data);
  },

  _onMessageManageEngines(msg) {
    msg.target.ownerGlobal.openPreferences("paneSearch", { origin: "contentSearch" });
  },

  async _onMessageGetSuggestions(msg, data) {
    this._ensureDataHasProperties(data, [
      "engineName",
      "searchString",
    ]);
    let {engineName, searchString} = data;
    let suggestions = await this.getSuggestions(engineName, searchString, msg.target);

    this._reply(msg, "Suggestions", {
      engineName: data.engineName,
      searchString: suggestions.term,
      formHistory: suggestions.local,
      remote: suggestions.remote,
    });
  },

  async _onMessageAddFormHistoryEntry(msg, entry) {
    await this.addFormHistoryEntry(msg, entry);
  },

  _onMessageRemoveFormHistoryEntry(msg, entry) {
    this.removeFormHistoryEntry(msg, entry);
  },

  _onMessageSpeculativeConnect(msg, engineName) {
    let engine = Services.search.getEngineByName(engineName);
    if (!engine) {
      throw new Error("Unknown engine name: " + engineName);
    }
    if (msg.target.contentWindow) {
      engine.speculativeConnect({
        window: msg.target.contentWindow,
        originAttributes: msg.target.contentPrincipal.originAttributes
      });
    }
  },

  async _onObserve(data) {
    if (data === "engine-current") {
      let engine = await this._currentEngineObj();
      this._broadcast("CurrentEngine", engine);
    } else if (data !== "engine-default") {
      // engine-default is always sent with engine-current and isn't otherwise
      // relevant to content searches.
      let state = await this.currentStateObj();
      this._broadcast("CurrentState", state);
    }
  },

  _suggestionDataForBrowser(browser, create = false) {
    let data = this._suggestionMap.get(browser);
    if (!data && create) {
      // Since one SearchSuggestionController instance is meant to be used per
      // autocomplete widget, this means that we assume each xul:browser has at
      // most one such widget.
      data = {
        controller: new SearchSuggestionController(),
      };
      this._suggestionMap.set(browser, data);
    }
    return data;
  },

  _reply(msg, type, data) {
    // We reply asyncly to messages, and by the time we reply the browser we're
    // responding to may have been destroyed.  messageManager is null then.
    if (!Cu.isDeadWrapper(msg.target) && msg.target.messageManager) {
      msg.target.messageManager.sendAsyncMessage(...this._msgArgs(type, data));
    }
  },

  _broadcast(type, data) {
    Cc["@mozilla.org/globalmessagemanager;1"].
      getService(Ci.nsIMessageListenerManager).
      broadcastAsyncMessage(...this._msgArgs(type, data));
  },

  _msgArgs(type, data) {
    return [OUTBOUND_MESSAGE, {
      type,
      data,
    }];
  },

  async _currentEngineObj() {
    let engine = Services.search.currentEngine;
    let favicon = engine.getIconURLBySize(16, 16);
    let placeholder = this._stringBundle.formatStringFromName(
      "searchWithEngine", [engine.name], 1);
    let obj = {
      name: engine.name,
      placeholder,
      iconBuffer: await this._arrayBufferFromDataURI(favicon),
    };
    return obj;
  },

  _arrayBufferFromDataURI(uri) {
    if (!uri) {
      return Promise.resolve(null);
    }
    return new Promise(resolve => {
      let xhr = new XMLHttpRequest();
      xhr.open("GET", uri, true);
      xhr.responseType = "arraybuffer";
      xhr.onload = () => {
        resolve(xhr.response);
      };
      xhr.onerror = xhr.onabort = xhr.ontimeout = () => {
        resolve(null);
      };
      try {
        // This throws if the URI is erroneously encoded.
        xhr.send();
      } catch (err) {
        resolve(null);
      }
    });
  },

  _ensureDataHasProperties(data, requiredProperties) {
    for (let prop of requiredProperties) {
      if (!(prop in data)) {
        throw new Error("Message data missing required property: " + prop);
      }
    }
  },

  _initService() {
    if (!this._initServicePromise) {
      this._initServicePromise =
        new Promise(resolve => Services.search.init(resolve));
    }
    return this._initServicePromise;
  },
};
PK
!<��;chrome/en-US/locale/browser/searchplugins/images/amazon.ico�&   ���PNG


IHDR�atIDAT8��S=K�P=��.�N��T'���*��ՎB��w�Y���E+�
�)%S�`�X��X���K+���|hE���w�y���$��dB�"��$���N��Z���n��ϣX,&�0˲(�J�-%I��J%�BP�uڶ�3��H����8\ׅ�y8���h4��d�۶�S�D��`
��`NU�8���#�RTї\�0�N��j����h��¼|M�C�����q/HX�Ō����&�B�����<�sF6�~7Yr���1��}��锼�A��O4�!51�{d��?�r��;xm��`�v�0<��{`p
�\b���c�һ�O��(���v�Ѐ��m�+H�Lpm`�Lݙ�-�1���'�U�4�+2)IEND�B`��PNG


IHDR  szz�sIDATX���OhW�?���`�V]�Ph#��$���I��Ң�����h`nQb-�<�P��K�'Jk�K�m2�m�]�P�2���a2q'�v�%!_��߼�g~�{o�$"�E�OD�����R���KD�78��@-�Loc�Z����c��t�t:m:���~D�)��8r4��D2����r�8�j2ч����+/۶M
�x���ؔH�Bfb>�qm��e���9�˙H,�����)�m[a���JU)�`����{zzVS��ED���N)Eggg�O��������0�lmm���ky\�T(���������6c��޸y���a�Ţ�g�����qّ���)��������{gϜ!�s'�;:��ر��&5���ei��R��[k"\��N\(B~�ryc����)���#��*�J�
D����^��B��CCZ����X��%�>�[���`�*kY�y^�D4[�`r�ּ>0�v�ƣ8
h�mG&#�����H}�m���B�bֲb���A6%��,���Z��Kқχ�~�$R},RY�Dx'��kؒ�]��W����0v
�Q�\�ݧ�t���7o�|w�'^�=�����~��o�B.�}�w���/���O��;�"�ƪ�A�"��s%�MA�P�E��������W��|m{ u�ޅDk�3�M�l��=�Ep�$�B�|�c�S�+��׾�L�>�Ƀ�~%������v��~fgC�V��+=T"�\���.L��`AP���®� �7rZ �5�	��y3_��1�����PA��_�7ރ��7.�^�R�/ ��{�/{c��T��e��	�c`/��b�x)��
�/'�>�?	NIEND�B`�PK
!<�	���9chrome/en-US/locale/browser/searchplugins/images/ebay.ico �&   ���PNG


IHDR�ajIDAT8�͓;H[aFύ�1P� �.�¥������Y;��C�E-H�� �`)tpqWA��W"J�PA�U�b꣭�1����s��8�ų�3}��$�J2�̩S��9�Y>Z�P��p,I��������,���f��-�["8�wH���N��O�26f���t
���,����!�b����!�/zpF���br�ǣ�$�������0��-��]#��^`�\�s�Lg��#�|5�8�L��@�4��{���99L�˓�T1P{N��交��X�U^&���� ���D;e7��Qy����*��1��7�%I�c�|�|)!��<'owΟ:��cn8B�^�IEND�B`��PNG


IHDR  szz��IDATX��KL\e��{���N�K)�V���hA�D�ĦmM$ҧ!1����jS�4ƆV���W��F�J�Ĥ���XJ��#)c���̔;��0-���N=ɿ�9���s�ܓa~ՈH��x%~�g8��8B�+�DED��$&ji�$С��,�� �""� 8j"�wn=E��uN%F ��i7>:��0�w/��
�ܸ7�.?s�(�y��$]���t��D;k9�ܧ�S�D%��dCU����U�0�QDD�����
���a*-�p�@O/I�c��S]���9��FbE�>��ц<'	��Zjf)�$�u�̩�ǩ.6�Cin}l�����YVY+�,|	DD<�����짟�&��Gdt}���O�� ��X�Ia[����/�.z�
���zW�L�0ˡ�by�T���s�\��My�,�h@�8��:�Ɉ�x��-(騩��[ɷ�bo�X���t�/����~��N����mkѿ��
`���;��c�=�ՠ��Ǭ�vP`{��@��w��dZCE���������-J�F0$��%�\<���n���aJV<M�p3�h��Wc�T���,�>M�3��W=3�81.�\��ȝEt7Ƣ�����B[Y�ZqE�ꞽ������mz�<���[������R�>I��f��N��f�|�ű��,��}����	o�擓XS4����B�����	
F|C1@�j�8G> ������0<2�́���g�l�DZ�34�j�n}��~�����r	{����Ht����D��Pf��FTk�7h�Q���O�ڷ��vP�����c?9������bZ�(�_��ʷ�N�cê��[ԁ���{��U��}*�L��9hN�@s�/��H�\�:����3j���܅H�o�XIEND�B`�PK
!<ee66>chrome/en-US/locale/browser/searchplugins/images/wikipedia.ico  �&(( @�'''555GGGYYYeeexxx��������������������������������������������������������������������������������������������������������������������`^���
������������������������0��������������n��������	�n���
���������������������&_�������������������������>������O
������
������^�����4���������P�������������^���n����������������>��p
�$���������n��Q���������,��_]����/�������^���������������������������������������������������������������������������������������������������������������������������������( �''';;;IIIiiiyyy��������������������������������������������������N����N�;����&�r�����������N��n��.�N��C���
�(������=�L��������������������������������PK
!<��H���-chrome/en-US/locale/browser/search.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

searchtip=Search using %S

# LOCALIZATION NOTE (searchPlaceholder): this is shown in the searchbox when
# the user hasn't typed anything yet.
searchPlaceholder=Search

# LOCALIZATION NOTE (searchHeader): this is displayed at the top of the panel
# showing search suggestions.
# %S is replaced with the name of the current default search engine.
searchHeader=%S Search

# LOCALIZATION NOTE (cmd_pasteAndSearch): "Search" is a verb, this is the
# search bar equivalent to the url bar's "Paste & Go"
cmd_pasteAndSearch=Paste & Search

cmd_clearHistory=Clear Search History
cmd_clearHistory_accesskey=H

cmd_showSuggestions=Show Suggestions
cmd_showSuggestions_accesskey=S

# LOCALIZATION NOTE (cmd_addFoundEngine): %S is replaced by the name of
# a search engine offered by a web page. Each engine is displayed as a
# menuitem at the bottom of the search panel.
cmd_addFoundEngine=Add “%S”
# LOCALIZATION NOTE (cmd_addFoundEngineMenu): When more than 5 engines
# are offered by a web page, instead of listing all of them in the
# search panel using the cmd_addFoundEngine string, they will be
# grouped in a submenu using cmd_addFoundEngineMenu as a label.
cmd_addFoundEngineMenu=Add search engine

# LOCALIZATION NOTE (searchForSomethingWith):
# This string is used to build the header above the list of one-click
# search providers:  "Search for <user-typed string> with:"
# NB: please leave the <span> and its class exactly as it is in English.
searchForSomethingWith=Search for <span class='contentSearchSearchWithHeaderSearchText'></span> with:

# LOCALIZATION NOTE (searchWithHeader):
# The wording of this string should be as close as possible to
# searchForSomethingWith. This string will be used when the user
# has not typed anything.
searchWithHeader=Search with:

# LOCALIZATION NOTE (searchSettings):
# This is the label for the button that opens Search preferences.
searchSettings=Change Search Settings

# LOCALIZATION NOTE (searchReset.intro):
# %S is the name of the user's current search engine.
searchReset.intro=Did you want to search using %S?
# LOCALIZATION NOTE (searchReset.message):
# %1$S is brandShortName. %2$S is the name of the user's current search engine.
searchReset.message=It appears that your default search engine has changed. Should %1$S make %2$S the default search engine?
# LOCALIZATION NOTE (searchReset.doNotResetButton):
# This string is used as a button label in a notification popup.
searchReset.doNotResetButton=No Thanks
# LOCALIZATION NOTE (searchReset.resetButton):
# %S is the name of the user's current search engine. This string is used as a
# button label in a notification popup, where space is limited.  If necessary,
# translate simply as "Yes, Use %S" (e.g., "Yes, use Google")
searchReset.resetButton=Yes, Use %S to Search
PK
!<i��"�q�qmodules/BrowserUITelemetry.jsm// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

"use strict";

var EXPORTED_SYMBOLS = ["BrowserUITelemetry"];

ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");

ChromeUtils.defineModuleGetter(this, "AppConstants",
  "resource://gre/modules/AppConstants.jsm");
ChromeUtils.defineModuleGetter(this, "UITelemetry",
  "resource://gre/modules/UITelemetry.jsm");
ChromeUtils.defineModuleGetter(this, "RecentWindow",
  "resource:///modules/RecentWindow.jsm");
ChromeUtils.defineModuleGetter(this, "CustomizableUI",
  "resource:///modules/CustomizableUI.jsm");
ChromeUtils.defineModuleGetter(this, "UITour",
  "resource:///modules/UITour.jsm");
XPCOMUtils.defineLazyGetter(this, "Timer", function() {
  let timer = {};
  ChromeUtils.import("resource://gre/modules/Timer.jsm", timer);
  return timer;
});

const MS_SECOND = 1000;
const MS_MINUTE = MS_SECOND * 60;
const MS_HOUR = MS_MINUTE * 60;

const LEGACY_PANEL_PLACEMENTS = [
  "edit-controls",
  "zoom-controls",
  "new-window-button",
  "privatebrowsing-button",
  "save-page-button",
  "print-button",
  "history-panelmenu",
  "fullscreen-button",
  "find-button",
  "preferences-button",
  "add-ons-button",
  "sync-button",
  "developer-button"
];

XPCOMUtils.defineLazyGetter(this, "DEFAULT_AREA_PLACEMENTS", function() {
  let result = {
    "nav-bar": [
      "back-button",
      "forward-button",
      "stop-reload-button",
      "home-button",
      "urlbar-container",
      "downloads-button",
      "library-button",
      "sidebar-button",
    ],
    // It's true that toolbar-menubar is not visible
    // on OS X, but the XUL node is definitely present
    // in the document.
    "toolbar-menubar": [
      "menubar-items",
    ],
    "TabsToolbar": [
      "tabbrowser-tabs",
      "new-tab-button",
      "alltabs-button",
    ],
    "PersonalToolbar": [
      "personal-bookmarks",
    ],
    "widget-overflow-fixed-list": [
    ],
  };

  return result;
});

XPCOMUtils.defineLazyGetter(this, "DEFAULT_AREAS", function() {
  return Object.keys(DEFAULT_AREA_PLACEMENTS);
});

XPCOMUtils.defineLazyGetter(this, "PALETTE_ITEMS", function() {
  let result = [
    "bookmarks-menu-button",
    "search-container",
    "open-file-button",
    "developer-button",
    "feed-button",
    "email-link-button",
    ...LEGACY_PANEL_PLACEMENTS,
    "characterencoding-button",
  ];

  if (Services.prefs.getBoolPref("privacy.panicButton.enabled")) {
    result.push("panic-button");
  }

  return result;
});

XPCOMUtils.defineLazyGetter(this, "DEFAULT_ITEMS", function() {
  let result = [];
  for (let [, buttons] of Object.entries(DEFAULT_AREA_PLACEMENTS)) {
    result = result.concat(buttons);
  }
  return result;
});

XPCOMUtils.defineLazyGetter(this, "ALL_BUILTIN_ITEMS", function() {
  // These special cases are for click events on built-in items that are
  // contained within customizable items (like the navigation widget).
  const SPECIAL_CASES = [
    "back-button",
    "forward-button",
    "stop-button",
    "urlbar-go-button",
    "reload-button",
    "searchbar",
    "cut-button",
    "copy-button",
    "paste-button",
    "zoom-out-button",
    "zoom-reset-button",
    "zoom-in-button",
    "BMB_bookmarksPopup",
    "BMB_unsortedBookmarksPopup",
    "BMB_bookmarksToolbarPopup",
    "search-go-button",
    "soundplaying-icon",
    "restore-tabs-button",
  ];
  return DEFAULT_ITEMS.concat(PALETTE_ITEMS)
                      .concat(SPECIAL_CASES);
});

const OTHER_MOUSEUP_MONITORED_ITEMS = [
  "PlacesChevron",
  "PlacesToolbarItems",
  "menubar-items",
];

// Items that open arrow panels will often be overlapped by
// the panel that they're opening by the time the mouseup
// event is fired, so for these items, we monitor mousedown.
const MOUSEDOWN_MONITORED_ITEMS = [
  "PanelUI-menu-button",
];

// Weakly maps browser windows to objects whose keys are relative
// timestamps for when some kind of session started. For example,
// when a customization session started. That way, when the window
// exits customization mode, we can determine how long the session
// lasted.
const WINDOW_DURATION_MAP = new WeakMap();

// Default bucket name, when no other bucket is active.
const BUCKET_DEFAULT = "__DEFAULT__";
// Bucket prefix, for named buckets.
const BUCKET_PREFIX = "bucket_";
// Standard separator to use between different parts of a bucket name, such
// as primary name and the time step string.
const BUCKET_SEPARATOR = "|";

var BrowserUITelemetry = {
  init() {
    UITelemetry.addSimpleMeasureFunction("toolbars",
                                         this.getToolbarMeasures.bind(this));
    UITelemetry.addSimpleMeasureFunction("contextmenu",
                                         this.getContextMenuInfo.bind(this));
    // Ensure that UITour.jsm remains lazy-loaded, yet always registers its
    // simple measure function with UITelemetry.
    UITelemetry.addSimpleMeasureFunction("UITour",
                                         () => UITour.getTelemetry());

    UITelemetry.addSimpleMeasureFunction("syncstate",
                                         this.getSyncState.bind(this));

    Services.obs.addObserver(this, "autocomplete-did-enter-text");
    CustomizableUI.addListener(this);

    // Register existing windows
    let browserEnum = Services.wm.getEnumerator("navigator:browser");
    while (browserEnum.hasMoreElements()) {
      this._registerWindow(browserEnum.getNext());
    }
    Services.obs.addObserver(this, "browser-delayed-startup-finished");

    this._gatherFirstWindowMeasurements();
  },

  observe(aSubject, aTopic, aData) {
    switch (aTopic) {
      case "browser-delayed-startup-finished":
        this._registerWindow(aSubject);
        break;
      case "autocomplete-did-enter-text":
        let input = aSubject.QueryInterface(Ci.nsIAutoCompleteInput);
        if (input && input.id == "urlbar" && !input.inPrivateContext &&
            input.popup.selectedIndex != -1) {
          this._logAwesomeBarSearchResult(input.textValue);
        }
        break;
    }
  },

  /**
   * For the _countableEvents object, constructs a chain of
   * Javascript Objects with the keys in aKeys, with the final
   * key getting the value in aEndWith. If the final key already
   * exists in the final object, its value is not set. In either
   * case, a reference to the second last object in the chain is
   * returned.
   *
   * Example - suppose I want to store:
   * _countableEvents: {
   *   a: {
   *     b: {
   *       c: 0
   *     }
   *   }
   * }
   *
   * And then increment the "c" value by 1, you could call this
   * function like this:
   *
   * let example = this._ensureObjectChain([a, b, c], 0);
   * example["c"]++;
   *
   * Subsequent repetitions of these last two lines would
   * simply result in the c value being incremented again
   * and again.
   *
   * @param aKeys the Array of keys to chain Objects together with.
   * @param aEndWith the value to assign to the last key.
   * @param aRoot the root object onto which we create/get the object chain
   *              designated by aKeys.
   * @returns a reference to the second last object in the chain -
   *          so in our example, that'd be "b".
   */
  _ensureObjectChain(aKeys, aEndWith, aRoot) {
    let current = aRoot;
    let parent = null;
    aKeys.unshift(this._bucket);
    for (let [i, key] of aKeys.entries()) {
      if (!(key in current)) {
        if (i == aKeys.length - 1) {
          current[key] = aEndWith;
        } else {
          current[key] = {};
        }
      }
      parent = current;
      current = current[key];
    }
    return parent;
  },

  _countableEvents: {},
  _countEvent(aKeyArray, root = this._countableEvents) {
    let countObject = this._ensureObjectChain(aKeyArray, 0, root);
    let lastItemKey = aKeyArray[aKeyArray.length - 1];
    countObject[lastItemKey]++;
  },

  _countMouseUpEvent(aCategory, aAction, aButton) {
    const BUTTONS = ["left", "middle", "right"];
    let buttonKey = BUTTONS[aButton];
    if (buttonKey) {
      this._countEvent([aCategory, aAction, buttonKey]);
    }
  },

  _firstWindowMeasurements: null,
  _gatherFirstWindowMeasurements() {
    // We'll gather measurements as soon as the session has restored.
    // We do this here instead of waiting for UITelemetry to ask for
    // our measurements because at that point all browser windows have
    // probably been closed, since the vast majority of saved-session
    // pings are gathered during shutdown.
    Services.search.init(rv => {
      let win = RecentWindow.getMostRecentBrowserWindow({
        private: false,
        allowPopups: false,
      });
      // If there are no such windows, we're out of luck. :(
      this._firstWindowMeasurements = win ? this._getWindowMeasurements(win, rv)
                                          : {};
    });
  },

  _registerWindow(aWindow) {
    aWindow.addEventListener("unload", this);
    let document = aWindow.document;

    for (let areaID of CustomizableUI.areas) {
      let areaNode = document.getElementById(areaID);
      if (areaNode) {
        (areaNode.customizationTarget || areaNode).addEventListener("mouseup", this);
      }
    }

    for (let itemID of OTHER_MOUSEUP_MONITORED_ITEMS) {
      let item = document.getElementById(itemID);
      if (item) {
        item.addEventListener("mouseup", this);
      }
    }

    for (let itemID of MOUSEDOWN_MONITORED_ITEMS) {
      let item = document.getElementById(itemID);
      if (item) {
        item.addEventListener("mousedown", this);
      }
    }

    WINDOW_DURATION_MAP.set(aWindow, {});
  },

  _unregisterWindow(aWindow) {
    aWindow.removeEventListener("unload", this);
    let document = aWindow.document;

    for (let areaID of CustomizableUI.areas) {
      let areaNode = document.getElementById(areaID);
      if (areaNode) {
        (areaNode.customizationTarget || areaNode).removeEventListener("mouseup", this);
      }
    }

    for (let itemID of OTHER_MOUSEUP_MONITORED_ITEMS) {
      let item = document.getElementById(itemID);
      if (item) {
        item.removeEventListener("mouseup", this);
      }
    }

    for (let itemID of MOUSEDOWN_MONITORED_ITEMS) {
      let item = document.getElementById(itemID);
      if (item) {
        item.removeEventListener("mousedown", this);
      }
    }
  },

  handleEvent(aEvent) {
    switch (aEvent.type) {
      case "unload":
        this._unregisterWindow(aEvent.currentTarget);
        break;
      case "mouseup":
        this._handleMouseUp(aEvent);
        break;
      case "mousedown":
        this._handleMouseDown(aEvent);
        break;
    }
  },

  _handleMouseUp(aEvent) {
    let targetID = aEvent.currentTarget.id;

    switch (targetID) {
      case "PlacesToolbarItems":
        this._PlacesToolbarItemsMouseUp(aEvent);
        break;
      case "PlacesChevron":
        this._PlacesChevronMouseUp(aEvent);
        break;
      case "menubar-items":
        this._menubarMouseUp(aEvent);
        break;
      default:
        this._checkForBuiltinItem(aEvent);
    }
  },

  _handleMouseDown(aEvent) {
    if (aEvent.currentTarget.id == "PanelUI-menu-button") {
      // _countMouseUpEvent expects a detail for the second argument,
      // but we don't really have any details to give. Just passing in
      // "button" is probably simpler than trying to modify
      // _countMouseUpEvent for this particular case.
      this._countMouseUpEvent("click-menu-button", "button", aEvent.button);
    }
  },

  _PlacesChevronMouseUp(aEvent) {
    let target = aEvent.originalTarget;
    let result = target.id == "PlacesChevron" ? "chevron" : "overflowed-item";
    this._countMouseUpEvent("click-bookmarks-bar", result, aEvent.button);
  },

  _PlacesToolbarItemsMouseUp(aEvent) {
    let target = aEvent.originalTarget;
    // If this isn't a bookmark-item, we don't care about it.
    if (!target.classList.contains("bookmark-item")) {
      return;
    }

    let result = target.hasAttribute("container") ? "container" : "item";
    this._countMouseUpEvent("click-bookmarks-bar", result, aEvent.button);
  },

  _menubarMouseUp(aEvent) {
    let target = aEvent.originalTarget;
    let tag = target.localName;
    let result = (tag == "menu" || tag == "menuitem") ? tag : "other";
    this._countMouseUpEvent("click-menubar", result, aEvent.button);
  },

  _bookmarksMenuButtonMouseUp(aEvent) {
    let bookmarksWidget = CustomizableUI.getWidget("bookmarks-menu-button");
    if (bookmarksWidget.areaType == CustomizableUI.TYPE_MENU_PANEL) {
      // In the menu panel, only the star is visible, and that opens up the
      // bookmarks subview.
      this._countMouseUpEvent("click-bookmarks-menu-button", "in-panel",
                              aEvent.button);
    } else {
      let clickedItem = aEvent.originalTarget;
      // Did we click on the star, or the dropmarker? The star
      // has an anonid of "button". If we don't find that, we'll
      // assume we clicked on the dropmarker.
      let action = "menu";
      if (clickedItem.getAttribute("anonid") == "button") {
        // We clicked on the star - now we just need to record
        // whether or not we're adding a bookmark or editing an
        // existing one.
        let bookmarksMenuNode =
          bookmarksWidget.forWindow(aEvent.target.ownerGlobal).node;
        action = bookmarksMenuNode.hasAttribute("starred") ? "edit" : "add";
      }
      this._countMouseUpEvent("click-bookmarks-menu-button", action,
                              aEvent.button);
    }
  },

  _checkForBuiltinItem(aEvent) {
    let item = aEvent.originalTarget;

    // We don't want to count clicks on the private browsing
    // button for privacy reasons. See bug 1176391.
    if (item.id == "privatebrowsing-button") {
      return;
    }

    // We special-case the bookmarks-menu-button, since we want to
    // monitor more than just clicks on it.
    if (item.id == "bookmarks-menu-button" ||
        getIDBasedOnFirstIDedAncestor(item) == "bookmarks-menu-button") {
      this._bookmarksMenuButtonMouseUp(aEvent);
      return;
    }

    // Perhaps we're seeing one of the default toolbar items
    // being clicked.
    if (ALL_BUILTIN_ITEMS.includes(item.id)) {
      // Base case - we clicked directly on one of our built-in items,
      // and we can go ahead and register that click.
      this._countMouseUpEvent("click-builtin-item", item.id, aEvent.button);
      return;
    }

    // If not, we need to check if the item's anonid is in our list
    // of built-in items to check.
    if (ALL_BUILTIN_ITEMS.includes(item.getAttribute("anonid"))) {
      this._countMouseUpEvent("click-builtin-item", item.getAttribute("anonid"), aEvent.button);
      return;
    }

    // If not, we need to check if one of the ancestors of the clicked
    // item is in our list of built-in items to check.
    let candidate = getIDBasedOnFirstIDedAncestor(item);
    if (ALL_BUILTIN_ITEMS.includes(candidate)) {
      this._countMouseUpEvent("click-builtin-item", candidate, aEvent.button);
    }
  },

  _getWindowMeasurements(aWindow, searchResult) {
    let document = aWindow.document;
    let result = {};

    // Determine if the window is in the maximized, normal or
    // fullscreen state.
    result.sizemode = document.documentElement.getAttribute("sizemode");

    // Determine if the Bookmarks bar is currently visible
    let bookmarksBar = document.getElementById("PersonalToolbar");
    result.bookmarksBarEnabled = bookmarksBar && !bookmarksBar.collapsed;

    // Determine if the menubar is currently visible. On OS X, the menubar
    // is never shown, despite not having the collapsed attribute set.
    let menuBar = document.getElementById("toolbar-menubar");
    result.menuBarEnabled =
      menuBar && Services.appinfo.OS != "Darwin"
              && menuBar.getAttribute("autohide") != "true";

    // Determine if the titlebar is currently visible.
    result.titleBarEnabled = !Services.prefs.getBoolPref("browser.tabs.drawInTitlebar");

    // Examine all customizable areas and see what default items
    // are present and missing.
    let defaultKept = [];
    let defaultMoved = [];
    let nondefaultAdded = [];

    for (let areaID of CustomizableUI.areas) {
      let items = CustomizableUI.getWidgetIdsInArea(areaID);
      for (let item of items) {
        // Is this a default item?
        if (DEFAULT_ITEMS.includes(item)) {
          // Ok, it's a default item - but is it in its default
          // toolbar? We use Array.isArray instead of checking for
          // toolbarID in DEFAULT_AREA_PLACEMENTS because an add-on might
          // be clever and give itself the id of "toString" or something.
          if (Array.isArray(DEFAULT_AREA_PLACEMENTS[areaID]) &&
              DEFAULT_AREA_PLACEMENTS[areaID].includes(item)) {
            // The item is in its default toolbar
            defaultKept.push(item);
          } else {
            defaultMoved.push(item);
          }
        } else if (PALETTE_ITEMS.includes(item)) {
          // It's a palette item that's been moved into a toolbar
          nondefaultAdded.push(item);
        }
        // else, it's provided by an add-on, and we won't record it.
      }
    }

    // Now go through the items in the palette to see what default
    // items are in there.
    let paletteItems =
      CustomizableUI.getUnusedWidgets(aWindow.gNavToolbox.palette);
    let defaultRemoved = [];
    for (let item of paletteItems) {
      if (DEFAULT_ITEMS.includes(item.id)) {
        defaultRemoved.push(item.id);
      }
    }

    result.defaultKept = defaultKept;
    result.defaultMoved = defaultMoved;
    result.nondefaultAdded = nondefaultAdded;
    result.defaultRemoved = defaultRemoved;

    // Next, determine how many add-on provided toolbars exist.
    let addonToolbars = 0;
    let toolbars = document.querySelectorAll("toolbar[customizable=true]");
    for (let toolbar of toolbars) {
      if (!DEFAULT_AREAS.includes(toolbar.id)) {
        addonToolbars++;
      }
    }
    result.addonToolbars = addonToolbars;

    // Find out how many open tabs we have in each window
    let winEnumerator = Services.wm.getEnumerator("navigator:browser");
    let visibleTabs = [];
    let hiddenTabs = [];
    while (winEnumerator.hasMoreElements()) {
      let someWin = winEnumerator.getNext();
      if (someWin.gBrowser) {
        let visibleTabsNum = someWin.gBrowser.visibleTabs.length;
        visibleTabs.push(visibleTabsNum);
        hiddenTabs.push(someWin.gBrowser.tabs.length - visibleTabsNum);
      }
    }
    result.visibleTabs = visibleTabs;
    result.hiddenTabs = hiddenTabs;

    if (Components.isSuccessCode(searchResult)) {
      result.currentSearchEngine = Services.search.currentEngine.name;
    }

    return result;
  },

  getToolbarMeasures() {
    let result = this._firstWindowMeasurements || {};
    result.countableEvents = this._countableEvents;
    result.durations = this._durations;
    return result;
  },

  getSyncState() {
    let result = {};
    for (let sub of ["desktop", "mobile"]) {
      let count = Services.prefs.getIntPref("services.sync.clients.devices." + sub, 0);
      result[sub] = count;
    }
    return result;
  },

  countCustomizationEvent(aEventType) {
    this._countEvent(["customize", aEventType]);
  },

  countSearchEvent(source, query, selection) {
    this._countEvent(["search", source]);
    if ((/^[a-zA-Z]+:[^\/\\]/).test(query)) {
      this._countEvent(["search", "urlbar-keyword"]);
    }
    if (selection) {
      this._countEvent(["search", "selection", source, selection.index, selection.kind]);
    }
  },

  countOneoffSearchEvent(id, type, where) {
    this._countEvent(["search-oneoff", id, type, where]);
  },

  countSearchSettingsEvent(source) {
    this._countEvent(["click-builtin-item", source, "search-settings"]);
  },

  countPanicEvent(timeId) {
    this._countEvent(["forget-button", timeId]);
  },

  countTabMutingEvent(action, reason) {
    this._countEvent(["tab-audio-control", action, reason || "no reason given"]);
  },

  countSyncedTabEvent(what, where) {
    // "what" will be, eg, "open"
    // "where" will be "toolbarbutton-subview" or "sidebar"
    this._countEvent(["synced-tabs", what, where]);
  },

  countSidebarEvent(sidebarID, action) {
    // sidebarID is the ID of the sidebar (duh!)
    // action will be "hide" or "show"
    this._countEvent(["sidebar", sidebarID, action]);
  },

  _logAwesomeBarSearchResult(url) {
    let spec = Services.search.parseSubmissionURL(url);
    if (spec.engine) {
      let matchedEngine = "default";
      if (spec.engine.name !== Services.search.currentEngine.name) {
        matchedEngine = "other";
      }
      this.countSearchEvent("autocomplete-" + matchedEngine);
    }
  },

  _durations: {
    customization: [],
  },

  onCustomizeStart(aWindow) {
    this._countEvent(["customize", "start"]);
    let durationMap = WINDOW_DURATION_MAP.get(aWindow);
    if (!durationMap) {
      durationMap = {};
      WINDOW_DURATION_MAP.set(aWindow, durationMap);
    }

    durationMap.customization = {
      start: aWindow.performance.now(),
      bucket: this._bucket,
    };
  },

  onCustomizeEnd(aWindow) {
    let durationMap = WINDOW_DURATION_MAP.get(aWindow);
    if (durationMap && "customization" in durationMap) {
      let duration = aWindow.performance.now() - durationMap.customization.start;
      this._durations.customization.push({
        duration,
        bucket: durationMap.customization.bucket,
      });
      delete durationMap.customization;
    }
  },

  _contextMenuItemWhitelist: new Set([
    "close-without-interaction", // for closing the menu without clicking it.
    "custom-page-item", // The ID we use for page-provided items
    "unknown", // The bucket for stuff with no id.
    // Everything we know of so far (which will exclude add-on items):
    "navigation", "back", "forward", "reload", "stop", "bookmarkpage",
    "spell-no-suggestions", "spell-add-to-dictionary",
    "spell-undo-add-to-dictionary", "openlinkincurrent", "openlinkintab",
    "openlink",
    // "openlinkprivate" intentionally omitted for privacy reasons. See bug 1176391.
    "bookmarklink", "savelink",
    "marklinkMenu", "copyemail", "copylink", "media-play", "media-pause",
    "media-mute", "media-unmute", "media-playbackrate",
    "media-playbackrate-050x", "media-playbackrate-100x",
    "media-playbackrate-125x", "media-playbackrate-150x", "media-playbackrate-200x",
    "media-showcontrols", "media-hidecontrols",
    "video-fullscreen", "leave-dom-fullscreen",
    "reloadimage", "viewimage", "viewvideo", "copyimage-contents", "copyimage",
    "copyvideourl", "copyaudiourl", "saveimage", "sendimage",
    "setDesktopBackground", "viewimageinfo", "viewimagedesc", "savevideo",
    "saveaudio", "video-saveimage", "sendvideo", "sendaudio",
    "ctp-play", "ctp-hide", "savepage", "pocket", "markpageMenu",
    "viewbgimage", "undo", "cut", "copy", "paste", "delete", "selectall",
    "keywordfield", "searchselect", "frame", "showonlythisframe",
    "openframeintab", "openframe", "reloadframe", "bookmarkframe", "saveframe",
    "printframe", "viewframesource", "viewframeinfo",
    "viewpartialsource-selection", "viewpartialsource-mathml",
    "viewsource", "viewinfo", "spell-check-enabled",
    "spell-add-dictionaries-main", "spell-dictionaries",
    "spell-dictionaries-menu", "spell-add-dictionaries",
    "bidi-text-direction-toggle", "bidi-page-direction-toggle", "inspect",
    "media-eme-learn-more"
  ]),

  _contextMenuInteractions: {},

  registerContextMenuInteraction(keys, itemID) {
    if (itemID) {
      if (itemID == "openlinkprivate") {
        // Don't record anything, not even an other-item count
        // if the user chose to open in a private window. See
        // bug 1176391.
        return;
      }

      if (!this._contextMenuItemWhitelist.has(itemID)) {
        itemID = "other-item";
      }
      keys.push(itemID);
    }

    this._countEvent(keys, this._contextMenuInteractions);
  },

  getContextMenuInfo() {
    return this._contextMenuInteractions;
  },

  _bucket: BUCKET_DEFAULT,
  _bucketTimer: null,

  /**
   * Default bucket name, when no other bucket is active.
   */
  get BUCKET_DEFAULT() {
    return BUCKET_DEFAULT;
  },

  /**
   * Bucket prefix, for named buckets.
   */
  get BUCKET_PREFIX() {
    return BUCKET_PREFIX;
  },

  /**
   * Standard separator to use between different parts of a bucket name, such
   * as primary name and the time step string.
   */
  get BUCKET_SEPARATOR() {
    return BUCKET_SEPARATOR;
  },

  get currentBucket() {
    return this._bucket;
  },

  /**
   * Sets a named bucket for all countable events and select durections to be
   * put into.
   *
   * @param aName  Name of bucket, or null for default bucket name (__DEFAULT__)
   */
  setBucket(aName) {
    if (this._bucketTimer) {
      Timer.clearTimeout(this._bucketTimer);
      this._bucketTimer = null;
    }

    if (aName)
      this._bucket = BUCKET_PREFIX + aName;
    else
      this._bucket = BUCKET_DEFAULT;
  },

  /**
  * Sets a bucket that expires at the rate of a given series of time steps.
  * Once the bucket expires, the current bucket will automatically revert to
  * the default bucket. While the bucket is expiring, it's name is postfixed
  * by '|' followed by a short string representation of the time step it's
  * currently in.
  * If any other bucket (expiring or normal) is set while an expiring bucket is
  * still expiring, the old expiring bucket stops expiring and the new bucket
  * immediately takes over.
  *
  * @param aName       Name of bucket.
  * @param aTimeSteps  An array of times in milliseconds to count up to before
  *                    reverting back to the default bucket. The array of times
  *                    is expected to be pre-sorted in ascending order.
  *                    For example, given a bucket name of 'bucket', the times:
  *                      [60000, 300000, 600000]
  *                    will result in the following buckets:
  *                    * bucket|1m - for the first 1 minute
  *                    * bucket|5m - for the following 4 minutes
  *                                  (until 5 minutes after the start)
  *                    * bucket|10m - for the following 5 minutes
  *                                   (until 10 minutes after the start)
  *                    * __DEFAULT__ - until a new bucket is set
  * @param aTimeOffset Time offset, in milliseconds, from which to start
  *                    counting. For example, if the first time step is 1000ms,
  *                    and the time offset is 300ms, then the next time step
  *                    will become active after 700ms. This affects all
  *                    following time steps also, meaning they will also all be
  *                    timed as though they started expiring 300ms before
  *                    setExpiringBucket was called.
  */
  setExpiringBucket(aName, aTimeSteps, aTimeOffset = 0) {
    if (aTimeSteps.length === 0) {
      this.setBucket(null);
      return;
    }

    if (this._bucketTimer) {
      Timer.clearTimeout(this._bucketTimer);
      this._bucketTimer = null;
    }

    // Make a copy of the time steps array, so we can safely modify it without
    // modifying the original array that external code has passed to us.
    let steps = [...aTimeSteps];
    let msec = steps.shift();
    let postfix = this._toTimeStr(msec);
    this.setBucket(aName + BUCKET_SEPARATOR + postfix);

    this._bucketTimer = Timer.setTimeout(() => {
      this._bucketTimer = null;
      this.setExpiringBucket(aName, steps, aTimeOffset + msec);
    }, msec - aTimeOffset);
  },

  /**
   * Formats a time interval, in milliseconds, to a minimal non-localized string
   * representation. Format is: 'h' for hours, 'm' for minutes, 's' for seconds,
   * 'ms' for milliseconds.
   * Examples:
   *   65 => 65ms
   *   1000 => 1s
   *   60000 => 1m
   *   61000 => 1m01s
   *
   * @param aTimeMS  Time in milliseconds
   *
   * @return Minimal string representation.
   */
  _toTimeStr(aTimeMS) {
    let timeStr = "";

    function reduce(aUnitLength, aSymbol) {
      if (aTimeMS >= aUnitLength) {
        let units = Math.floor(aTimeMS / aUnitLength);
        aTimeMS = aTimeMS - (units * aUnitLength);
        timeStr += units + aSymbol;
      }
    }

    reduce(MS_HOUR, "h");
    reduce(MS_MINUTE, "m");
    reduce(MS_SECOND, "s");
    reduce(1, "ms");

    return timeStr;
  },
};

/**
 * Returns the id of the first ancestor of aNode that has an id. If aNode
 * has no parent, or no ancestor has an id, returns null.
 *
 * @param aNode the node to find the first ID'd ancestor of
 */
function getIDBasedOnFirstIDedAncestor(aNode) {
  while (!aNode.id) {
    aNode = aNode.parentNode;
    if (!aNode) {
      return null;
    }
  }

  return aNode.id;
}
PK
!<)�-����modules/Sanitizer.jsm// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

var EXPORTED_SYMBOLS = ["Sanitizer"];

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");

XPCOMUtils.defineLazyModuleGetters(this, {
  AppConstants: "resource://gre/modules/AppConstants.jsm",
  PlacesUtils: "resource://gre/modules/PlacesUtils.jsm",
  FormHistory: "resource://gre/modules/FormHistory.jsm",
  Downloads: "resource://gre/modules/Downloads.jsm",
  DownloadsCommon: "resource:///modules/DownloadsCommon.jsm",
  TelemetryStopwatch: "resource://gre/modules/TelemetryStopwatch.jsm",
  setTimeout: "resource://gre/modules/Timer.jsm",
});

XPCOMUtils.defineLazyServiceGetter(this, "serviceWorkerManager",
                                   "@mozilla.org/serviceworkers/manager;1",
                                   "nsIServiceWorkerManager");
XPCOMUtils.defineLazyServiceGetter(this, "quotaManagerService",
                                   "@mozilla.org/dom/quota-manager-service;1",
                                   "nsIQuotaManagerService");

// Used as unique id for pending sanitizations.
var gPendingSanitizationSerial = 0;

/**
 * A number of iterations after which to yield time back
 * to the system.
 */
const YIELD_PERIOD = 10;

var Sanitizer = {
  /**
   * Whether we should sanitize on shutdown.
   */
  PREF_SANITIZE_ON_SHUTDOWN: "privacy.sanitize.sanitizeOnShutdown",

  /**
   * During a sanitization this is set to a JSON containing an array of the
   * pending sanitizations. This allows to retry sanitizations on startup in
   * case they dind't run or were interrupted by a crash.
   * Use addPendingSanitization and removePendingSanitization to manage it.
   */
  PREF_PENDING_SANITIZATIONS: "privacy.sanitize.pending",

  /**
   * Pref branches to fetch sanitization options from.
   */
  PREF_CPD_BRANCH: "privacy.cpd.",
  PREF_SHUTDOWN_BRANCH: "privacy.clearOnShutdown.",

  /**
   * The fallback timestamp used when no argument is given to
   * Sanitizer.getClearRange.
   */
  PREF_TIMESPAN: "privacy.sanitize.timeSpan",

  /**
   * Time span constants corresponding to values of the privacy.sanitize.timeSpan
   * pref.  Used to determine how much history to clear, for various items
   */
  TIMESPAN_EVERYTHING: 0,
  TIMESPAN_HOUR:       1,
  TIMESPAN_2HOURS:     2,
  TIMESPAN_4HOURS:     3,
  TIMESPAN_TODAY:      4,
  TIMESPAN_5MIN:       5,
  TIMESPAN_24HOURS:    6,

  /**
   * Whether we should sanitize on shutdown.
   * When this is set, a pending sanitization should also be added and removed
   * when shutdown sanitization is complete. This allows to retry incomplete
   * sanitizations on startup.
   */
  shouldSanitizeOnShutdown: false,

  /**
   * Shows a sanitization dialog to the user.
   *
   * @param [optional] parentWindow the window to use as
   *                   parent for the created dialog.
   */
  showUI(parentWindow) {
    let win = AppConstants.platform == "macosx" ?
      null : // make this an app-modal window on Mac
      parentWindow;
    Services.ww.openWindow(win,
                           "chrome://browser/content/sanitize.xul",
                           "Sanitize",
                           "chrome,titlebar,dialog,centerscreen,modal",
                           null);
  },

  /**
   * Performs startup tasks:
   *  - Checks if sanitizations were not completed during the last session.
   *  - Registers sanitize-on-shutdown.
   */
  async onStartup() {
    // First, collect pending sanitizations from the last session, before we
    // add pending sanitizations for this session.
    let pendingSanitizations = getAndClearPendingSanitizations();

    // Check if we should sanitize on shutdown.
    this.shouldSanitizeOnShutdown =
      Services.prefs.getBoolPref(Sanitizer.PREF_SANITIZE_ON_SHUTDOWN, false);
    Services.prefs.addObserver(Sanitizer.PREF_SANITIZE_ON_SHUTDOWN, this, true);
    // Add a pending shutdown sanitization, if necessary.
    if (this.shouldSanitizeOnShutdown) {
      let itemsToClear = getItemsToClearFromPrefBranch(Sanitizer.PREF_SHUTDOWN_BRANCH);
      addPendingSanitization("shutdown", itemsToClear, {});
    }
    // Shutdown sanitization is always pending, but the user may change the
    // sanitize on shutdown prefs during the session. Then the pending
    // sanitization would become stale and must be updated.
    Services.prefs.addObserver(Sanitizer.PREF_SHUTDOWN_BRANCH, this, true);

    // Make sure that we are triggered during shutdown.
    let shutdownClient = PlacesUtils.history.shutdownClient.jsclient;
    // We need to pass to sanitize() (through sanitizeOnShutdown) a state object
    // that tracks the status of the shutdown blocker. This `progress` object
    // will be updated during sanitization and reported with the crash in case of
    // a shutdown timeout.
    // We use the `options` argument to pass the `progress` object to sanitize().
    let progress = { isShutdown: true };
    shutdownClient.addBlocker("sanitize.js: Sanitize on shutdown",
      () => sanitizeOnShutdown(progress),
      {fetchState: () => ({ progress })}
    );

    // Finally, run the sanitizations that were left pending, because we crashed
    // before completing them.
    for (let {itemsToClear, options} of pendingSanitizations) {
      try {
        await this.sanitize(itemsToClear, options);
      } catch (ex) {
        Cu.reportError("A previously pending sanitization failed: " + itemsToClear + "\n" + ex);
      }
    }
  },

  /**
   * Returns a 2 element array representing the start and end times,
   * in the uSec-since-epoch format that PRTime likes. If we should
   * clear everything, this function returns null.
   *
   * @param ts [optional] a timespan to convert to start and end time.
   *                      Falls back to the privacy.sanitize.timeSpan preference
   *                      if this argument is omitted.
   *                      If this argument is provided, it has to be one of the
   *                      Sanitizer.TIMESPAN_* constants. This function will
   *                      throw an error otherwise.
   *
   * @return {Array} a 2-element Array containing the start and end times.
   */
  getClearRange(ts) {
    if (ts === undefined)
      ts = Services.prefs.getIntPref(Sanitizer.PREF_TIMESPAN);
    if (ts === Sanitizer.TIMESPAN_EVERYTHING)
      return null;

    // PRTime is microseconds while JS time is milliseconds
    var endDate = Date.now() * 1000;
    switch (ts) {
      case Sanitizer.TIMESPAN_5MIN :
        var startDate = endDate - 300000000; // 5*60*1000000
        break;
      case Sanitizer.TIMESPAN_HOUR :
        startDate = endDate - 3600000000; // 1*60*60*1000000
        break;
      case Sanitizer.TIMESPAN_2HOURS :
        startDate = endDate - 7200000000; // 2*60*60*1000000
        break;
      case Sanitizer.TIMESPAN_4HOURS :
        startDate = endDate - 14400000000; // 4*60*60*1000000
        break;
      case Sanitizer.TIMESPAN_TODAY :
        var d = new Date(); // Start with today
        d.setHours(0); // zero us back to midnight...
        d.setMinutes(0);
        d.setSeconds(0);
        startDate = d.valueOf() * 1000; // convert to epoch usec
        break;
      case Sanitizer.TIMESPAN_24HOURS :
        startDate = endDate - 86400000000; // 24*60*60*1000000
        break;
      default:
        throw "Invalid time span for clear private data: " + ts;
    }
    return [startDate, endDate];
  },

  /**
   * Deletes privacy sensitive data in a batch, according to user preferences.
   * Returns a promise which is resolved if no errors occurred.  If an error
   * occurs, a message is reported to the console and all other items are still
   * cleared before the promise is finally rejected.
   *
   * @param [optional] itemsToClear
   *        Array of items to be cleared. if specified only those
   *        items get cleared, irrespectively of the preference settings.
   * @param [optional] options
   *        Object whose properties are options for this sanitization:
   *         - ignoreTimespan (default: true): Time span only makes sense in
   *           certain cases.  Consumers who want to only clear some private
   *           data can opt in by setting this to false, and can optionally
   *           specify a specific range.
   *           If timespan is not ignored, and range is not set, sanitize() will
   *           use the value of the timespan pref to determine a range.
   *         - range (default: null)
   *         - privateStateForNewWindow (default: "non-private"): when clearing
   *           open windows, defines the private state for the newly opened window.
   */
  async sanitize(itemsToClear = null, options = {}) {
    let progress = options.progress || {};
    if (!itemsToClear)
      itemsToClear = getItemsToClearFromPrefBranch(this.PREF_CPD_BRANCH);
    let promise = sanitizeInternal(this.items, itemsToClear, progress, options);

    // Depending on preferences, the sanitizer may perform asynchronous
    // work before it starts cleaning up the Places database (e.g. closing
    // windows). We need to make sure that the connection to that database
    // hasn't been closed by the time we use it.
    // Though, if this is a sanitize on shutdown, we already have a blocker.
    if (!progress.isShutdown) {
      let shutdownClient = Cc["@mozilla.org/browser/nav-history-service;1"]
                             .getService(Ci.nsPIPlacesDatabase)
                             .shutdownClient
                             .jsclient;
      shutdownClient.addBlocker("sanitize.js: Sanitize",
        promise,
        {
          fetchState: () => ({ progress })
        }
      );
    }

    try {
      await promise;
    } finally {
      Services.obs.notifyObservers(null, "sanitizer-sanitization-complete");
    }
  },

  observe(subject, topic, data) {
    if (topic == "nsPref:changed") {
      if (data.startsWith(this.PREF_SHUTDOWN_BRANCH) &&
          this.shouldSanitizeOnShutdown) {
        // Update the pending shutdown sanitization.
        removePendingSanitization("shutdown");
        let itemsToClear = getItemsToClearFromPrefBranch(Sanitizer.PREF_SHUTDOWN_BRANCH);
        addPendingSanitization("shutdown", itemsToClear, {});
      } else if (data == this.PREF_SANITIZE_ON_SHUTDOWN) {
        this.shouldSanitizeOnShutdown =
          Services.prefs.getBoolPref(Sanitizer.PREF_SANITIZE_ON_SHUTDOWN, false);
        removePendingSanitization("shutdown");
        if (this.shouldSanitizeOnShutdown) {
          let itemsToClear = getItemsToClearFromPrefBranch(Sanitizer.PREF_SHUTDOWN_BRANCH);
          addPendingSanitization("shutdown", itemsToClear, {});
        }
      }
    }
  },

  QueryInterface: XPCOMUtils.generateQI([
    Ci.nsiObserver,
    Ci.nsISupportsWeakReference
  ]),

  items: {
    cache: {
      async clear(range) {
        let seenException;
        let refObj = {};
        TelemetryStopwatch.start("FX_SANITIZE_CACHE", refObj);

        try {
          // Cache doesn't consult timespan, nor does it have the
          // facility for timespan-based eviction.  Wipe it.
          Services.cache2.clear();
        } catch (ex) {
          seenException = ex;
        }

        try {
          let imageCache = Cc["@mozilla.org/image/tools;1"]
                             .getService(Ci.imgITools)
                             .getImgCacheForDocument(null);
          imageCache.clearCache(false); // true=chrome, false=content
        } catch (ex) {
          seenException = ex;
        }

        TelemetryStopwatch.finish("FX_SANITIZE_CACHE", refObj);
        if (seenException) {
          throw seenException;
        }
      }
    },

    cookies: {
      async clear(range) {
        let seenException;
        let yieldCounter = 0;
        let refObj = {};

        // Clear cookies.
        TelemetryStopwatch.start("FX_SANITIZE_COOKIES_2", refObj);
        try {
          if (range) {
            // Iterate through the cookies and delete any created after our cutoff.
            let cookiesEnum = Services.cookies.enumerator;
            while (cookiesEnum.hasMoreElements()) {
              let cookie = cookiesEnum.getNext().QueryInterface(Ci.nsICookie2);

              if (cookie.creationTime > range[0]) {
                // This cookie was created after our cutoff, clear it
                Services.cookies.remove(cookie.host, cookie.name, cookie.path,
                                        false, cookie.originAttributes);

                if (++yieldCounter % YIELD_PERIOD == 0) {
                  await new Promise(resolve => setTimeout(resolve, 0)); // Don't block the main thread too long
                }
              }
            }
          } else {
            // Remove everything
            Services.cookies.removeAll();
            await new Promise(resolve => setTimeout(resolve, 0)); // Don't block the main thread too long
          }
        } catch (ex) {
          seenException = ex;
        } finally {
          TelemetryStopwatch.finish("FX_SANITIZE_COOKIES_2", refObj);
        }

        // Clear deviceIds. Done asynchronously (returns before complete).
        try {
          let mediaMgr = Cc["@mozilla.org/mediaManagerService;1"]
                           .getService(Ci.nsIMediaManagerService);
          mediaMgr.sanitizeDeviceIds(range && range[0]);
        } catch (ex) {
          seenException = ex;
        }

        // Clear plugin data.
        try {
          await clearPluginData(range);
        } catch (ex) {
          seenException = ex;
        }

        if (seenException) {
          throw seenException;
        }
      },
    },

    offlineApps: {
      async clear(range) {
        // AppCache
        ChromeUtils.import("resource:///modules/offlineAppCache.jsm");
        // This doesn't wait for the cleanup to be complete.
        OfflineAppCacheHelper.clear();

        // LocalStorage
        Services.obs.notifyObservers(null, "extension:purge-localStorage");

        // ServiceWorkers
        let promises = [];
        let serviceWorkers = serviceWorkerManager.getAllRegistrations();
        for (let i = 0; i < serviceWorkers.length; i++) {
          let sw = serviceWorkers.queryElementAt(i, Ci.nsIServiceWorkerRegistrationInfo);

          promises.push(new Promise(resolve => {
            let unregisterCallback = {
              unregisterSucceeded: () => { resolve(true); },
              // We don't care about failures.
              unregisterFailed: () => { resolve(true); },
              QueryInterface: XPCOMUtils.generateQI(
                [Ci.nsIServiceWorkerUnregisterCallback])
            };

            serviceWorkerManager.propagateUnregister(sw.principal, unregisterCallback, sw.scope);
          }));
        }

        await Promise.all(promises);

        // QuotaManager
        promises = [];
        await new Promise(resolve => {
          quotaManagerService.getUsage(request => {
            if (request.resultCode != Cr.NS_OK) {
              // We are probably shutting down. We don't want to propagate the
              // error, rejecting the promise.
              resolve();
              return;
            }

            for (let item of request.result) {
              let principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(item.origin);
              let uri = principal.URI;
              if (uri.scheme == "http" || uri.scheme == "https" || uri.scheme == "file") {
                promises.push(new Promise(r => {
                  let req = quotaManagerService.clearStoragesForPrincipal(principal, null, false);
                  req.callback = () => { r(); };
                }));
              }
            }
            resolve();
          });
        });

        return Promise.all(promises);
      }
    },

    history: {
      async clear(range) {
        let seenException;
        let refObj = {};
        TelemetryStopwatch.start("FX_SANITIZE_HISTORY", refObj);
        try {
          if (range) {
            await PlacesUtils.history.removeVisitsByFilter({
              beginDate: new Date(range[0] / 1000),
              endDate: new Date(range[1] / 1000)
            });
          } else {
            // Remove everything.
            await PlacesUtils.history.clear();
          }
        } catch (ex) {
          seenException = ex;
        } finally {
          TelemetryStopwatch.finish("FX_SANITIZE_HISTORY", refObj);
        }

        try {
          let clearStartingTime = range ? String(range[0]) : "";
          Services.obs.notifyObservers(null, "browser:purge-session-history", clearStartingTime);
        } catch (ex) {
          seenException = ex;
        }

        try {
          let predictor = Cc["@mozilla.org/network/predictor;1"]
                            .getService(Ci.nsINetworkPredictor);
          predictor.reset();
        } catch (ex) {
          seenException = ex;
        }

        if (seenException) {
          throw seenException;
        }
      }
    },

    formdata: {
      async clear(range) {
        let seenException;
        let refObj = {};
        TelemetryStopwatch.start("FX_SANITIZE_FORMDATA", refObj);
        try {
          // Clear undo history of all search bars.
          let windows = Services.wm.getEnumerator("navigator:browser");
          while (windows.hasMoreElements()) {
            let currentWindow = windows.getNext();
            let currentDocument = currentWindow.document;

            // searchBar.textbox may not exist due to the search bar binding
            // not having been constructed yet if the search bar is in the
            // overflow or menu panel. It won't have a value or edit history in
            // that case.
            let searchBar = currentDocument.getElementById("searchbar");
            if (searchBar && searchBar.textbox)
              searchBar.textbox.reset();

            let tabBrowser = currentWindow.gBrowser;
            if (!tabBrowser) {
              // No tab browser? This means that it's too early during startup (typically,
              // Session Restore hasn't completed yet). Since we don't have find
              // bars at that stage and since Session Restore will not restore
              // find bars further down during startup, we have nothing to clear.
              continue;
            }
            for (let tab of tabBrowser.tabs) {
              if (tabBrowser.isFindBarInitialized(tab))
                tabBrowser.getFindBar(tab).clear();
            }
            // Clear any saved find value
            tabBrowser._lastFindValue = "";
          }
        } catch (ex) {
          seenException = ex;
        }

        try {
          let change = { op: "remove" };
          if (range) {
            [ change.firstUsedStart, change.firstUsedEnd ] = range;
          }
          await new Promise(resolve => {
            FormHistory.update(change, {
              handleError(e) {
                seenException = new Error("Error " + e.result + ": " + e.message);
              },
              handleCompletion() {
                resolve();
              }
            });
          });
        } catch (ex) {
          seenException = ex;
        }

        TelemetryStopwatch.finish("FX_SANITIZE_FORMDATA", refObj);
        if (seenException) {
          throw seenException;
        }
      }
    },

    downloads: {
      async clear(range) {
        let refObj = {};
        TelemetryStopwatch.start("FX_SANITIZE_DOWNLOADS", refObj);
        try {
          let filterByTime = null;
          if (range) {
            // Convert microseconds back to milliseconds for date comparisons.
            let rangeBeginMs = range[0] / 1000;
            let rangeEndMs = range[1] / 1000;
            filterByTime = download => download.startTime >= rangeBeginMs &&
                                       download.startTime <= rangeEndMs;
          }

          // Clear all completed/cancelled downloads
          let list = await Downloads.getList(Downloads.ALL);
          list.removeFinished(filterByTime);
        } finally {
          TelemetryStopwatch.finish("FX_SANITIZE_DOWNLOADS", refObj);
        }
      }
    },

    sessions: {
      async clear(range) {
        let refObj = {};
        TelemetryStopwatch.start("FX_SANITIZE_SESSIONS", refObj);

        try {
          // clear all auth tokens
          let sdr = Cc["@mozilla.org/security/sdr;1"]
                      .getService(Ci.nsISecretDecoderRing);
          sdr.logoutAndTeardown();

          // clear FTP and plain HTTP auth sessions
          Services.obs.notifyObservers(null, "net:clear-active-logins");
        } finally {
          TelemetryStopwatch.finish("FX_SANITIZE_SESSIONS", refObj);
        }
      }
    },

    siteSettings: {
      async clear(range) {
        let seenException;
        let refObj = {};
        TelemetryStopwatch.start("FX_SANITIZE_SITESETTINGS", refObj);

        let startDateMS = range ? range[0] / 1000 : null;

        try {
          // Clear site-specific permissions like "Allow this site to open popups"
          // we ignore the "end" range and hope it is now() - none of the
          // interfaces used here support a true range anyway.
          if (startDateMS == null) {
            Services.perms.removeAll();
          } else {
            Services.perms.removeAllSince(startDateMS);
          }
        } catch (ex) {
          seenException = ex;
        }

        try {
          // Clear site-specific settings like page-zoom level
          let cps = Cc["@mozilla.org/content-pref/service;1"]
                      .getService(Ci.nsIContentPrefService2);
          if (startDateMS == null) {
            cps.removeAllDomains(null);
          } else {
            cps.removeAllDomainsSince(startDateMS, null);
          }
        } catch (ex) {
          seenException = ex;
        }

        try {
          // Clear site security settings - no support for ranges in this
          // interface either, so we clearAll().
          let sss = Cc["@mozilla.org/ssservice;1"]
                      .getService(Ci.nsISiteSecurityService);
          sss.clearAll();
        } catch (ex) {
          seenException = ex;
        }

        // Clear all push notification subscriptions
        try {
          await new Promise((resolve, reject) => {
            let push = Cc["@mozilla.org/push/Service;1"]
                         .getService(Ci.nsIPushService);
            push.clearForDomain("*", status => {
              if (Components.isSuccessCode(status)) {
                resolve();
              } else {
                reject(new Error("Error clearing push subscriptions: " +
                                 status));
              }
            });
          });
        } catch (ex) {
          seenException = ex;
        }

        TelemetryStopwatch.finish("FX_SANITIZE_SITESETTINGS", refObj);
        if (seenException) {
          throw seenException;
        }
      }
    },

    openWindows: {
      _canCloseWindow(win) {
        if (win.CanCloseWindow()) {
          // We already showed PermitUnload for the window, so let's
          // make sure we don't do it again when we actually close the
          // window.
          win.skipNextCanClose = true;
          return true;
        }
        return false;
      },
      _resetAllWindowClosures(windowList) {
        for (let win of windowList) {
          win.skipNextCanClose = false;
        }
      },
      async clear(range, privateStateForNewWindow = "non-private") {
        // NB: this closes all *browser* windows, not other windows like the library, about window,
        // browser console, etc.

        // Keep track of the time in case we get stuck in la-la-land because of onbeforeunload
        // dialogs
        let existingWindow = Services.appShell.hiddenDOMWindow;
        let startDate = existingWindow.performance.now();

        // First check if all these windows are OK with being closed:
        let windowEnumerator = Services.wm.getEnumerator("navigator:browser");
        let windowList = [];
        while (windowEnumerator.hasMoreElements()) {
          let someWin = windowEnumerator.getNext();
          windowList.push(someWin);
          // If someone says "no" to a beforeunload prompt, we abort here:
          if (!this._canCloseWindow(someWin)) {
            this._resetAllWindowClosures(windowList);
            throw new Error("Sanitize could not close windows: cancelled by user");
          }

          // ...however, beforeunload prompts spin the event loop, and so the code here won't get
          // hit until the prompt has been dismissed. If more than 1 minute has elapsed since we
          // started prompting, stop, because the user might not even remember initiating the
          // 'forget', and the timespans will be all wrong by now anyway:
          if (existingWindow.performance.now() > (startDate + 60 * 1000)) {
            this._resetAllWindowClosures(windowList);
            throw new Error("Sanitize could not close windows: timeout");
          }
        }

        // If/once we get here, we should actually be able to close all windows.

        let refObj = {};
        TelemetryStopwatch.start("FX_SANITIZE_OPENWINDOWS", refObj);

        // First create a new window. We do this first so that on non-mac, we don't
        // accidentally close the app by closing all the windows.
        let handler = Cc["@mozilla.org/browser/clh;1"].getService(Ci.nsIBrowserHandler);
        let defaultArgs = handler.defaultArgs;
        let features = "chrome,all,dialog=no," + privateStateForNewWindow;
        let newWindow = existingWindow.openDialog("chrome://browser/content/", "_blank",
                                                  features, defaultArgs);

        let onFullScreen = null;
        if (AppConstants.platform == "macosx") {
          onFullScreen = function(e) {
            newWindow.removeEventListener("fullscreen", onFullScreen);
            let docEl = newWindow.document.documentElement;
            let sizemode = docEl.getAttribute("sizemode");
            if (!newWindow.fullScreen && sizemode == "fullscreen") {
              docEl.setAttribute("sizemode", "normal");
              e.preventDefault();
              e.stopPropagation();
              return false;
            }
            return undefined;
          };
          newWindow.addEventListener("fullscreen", onFullScreen);
        }

        let promiseReady = new Promise(resolve => {
          // Window creation and destruction is asynchronous. We need to wait
          // until all existing windows are fully closed, and the new window is
          // fully open, before continuing. Otherwise the rest of the sanitizer
          // could run too early (and miss new cookies being set when a page
          // closes) and/or run too late (and not have a fully-formed window yet
          // in existence). See bug 1088137.
          let newWindowOpened = false;
          let onWindowOpened = function(subject, topic, data) {
            if (subject != newWindow)
              return;

            Services.obs.removeObserver(onWindowOpened, "browser-delayed-startup-finished");
            if (AppConstants.platform == "macosx") {
              newWindow.removeEventListener("fullscreen", onFullScreen);
            }
            newWindowOpened = true;
            // If we're the last thing to happen, invoke callback.
            if (numWindowsClosing == 0) {
              TelemetryStopwatch.finish("FX_SANITIZE_OPENWINDOWS", refObj);
              resolve();
            }
          };

          let numWindowsClosing = windowList.length;
          let onWindowClosed = function() {
            numWindowsClosing--;
            if (numWindowsClosing == 0) {
              Services.obs.removeObserver(onWindowClosed, "xul-window-destroyed");
              // If we're the last thing to happen, invoke callback.
              if (newWindowOpened) {
                TelemetryStopwatch.finish("FX_SANITIZE_OPENWINDOWS", refObj);
                resolve();
              }
            }
          };
          Services.obs.addObserver(onWindowOpened, "browser-delayed-startup-finished");
          Services.obs.addObserver(onWindowClosed, "xul-window-destroyed");
        });

        // Start the process of closing windows
        while (windowList.length) {
          windowList.pop().close();
        }
        newWindow.focus();
        await promiseReady;
      }
    },

    pluginData: {
      async clear(range) {
        await clearPluginData(range);
      },
    },
  },
};

async function sanitizeInternal(items, aItemsToClear, progress, options = {}) {
  let { ignoreTimespan = true, range } = options;
  let seenError = false;
  // Shallow copy the array, as we are going to modify it in place later.
  if (!Array.isArray(aItemsToClear))
    throw new Error("Must pass an array of items to clear.");
  let itemsToClear = [...aItemsToClear];

  // Store the list of items to clear, in case we are killed before we
  // get a chance to complete.
  let uid = gPendingSanitizationSerial++;
  // Shutdown sanitization is managed outside.
  if (!progress.isShutdown)
    addPendingSanitization(uid, itemsToClear, options);

  // Store the list of items to clear, for debugging/forensics purposes
  for (let k of itemsToClear) {
    progress[k] = "ready";
  }

  // Ensure open windows get cleared first, if they're in our list, so that
  // they don't stick around in the recently closed windows list, and so we
  // can cancel the whole thing if the user selects to keep a window open
  // from a beforeunload prompt.
  let openWindowsIndex = itemsToClear.indexOf("openWindows");
  if (openWindowsIndex != -1) {
    itemsToClear.splice(openWindowsIndex, 1);
    await items.openWindows.clear(null, options);
    progress.openWindows = "cleared";
  }

  // If we ignore timespan, clear everything,
  // otherwise, pick a range.
  if (!ignoreTimespan && !range) {
    range = Sanitizer.getClearRange();
  }

  // For performance reasons we start all the clear tasks at once, then wait
  // for their promises later.
  // Some of the clear() calls may raise exceptions (for example bug 265028),
  // we catch and store them, but continue to sanitize as much as possible.
  // Callers should check returned errors and give user feedback
  // about items that could not be sanitized
  let refObj = {};
  TelemetryStopwatch.start("FX_SANITIZE_TOTAL", refObj);

  let annotateError = (name, ex) => {
    progress[name] = "failed";
    seenError = true;
    console.error("Error sanitizing " + name, ex);
  };

  // Array of objects in form { name, promise }.
  // `name` is the item's name and `promise` may be a promise, if the
  // sanitization is asynchronous, or the function return value, otherwise.
  let handles = [];
  for (let name of itemsToClear) {
    let item = items[name];
    try {
      // Catch errors here, so later we can just loop through these.
      handles.push({ name,
                     promise: item.clear(range, options)
                                  .then(() => progress[name] = "cleared",
                                        ex => annotateError(name, ex))
                   });
    } catch (ex) {
      annotateError(name, ex);
    }
  }
  for (let handle of handles) {
    progress[handle.name] = "blocking";
    await handle.promise;
  }

  // Sanitization is complete.
  TelemetryStopwatch.finish("FX_SANITIZE_TOTAL", refObj);
  if (!progress.isShutdown)
    removePendingSanitization(uid);
  progress = {};
  if (seenError) {
    throw new Error("Error sanitizing");
  }
}

async function clearPluginData(range) {
  // Clear plugin data.
  // As evidenced in bug 1253204, clearing plugin data can sometimes be
  // very, very long, for mysterious reasons. Unfortunately, this is not
  // something actionable by Mozilla, so crashing here serves no purpose.
  //
  // For this reason, instead of waiting for sanitization to always
  // complete, we introduce a soft timeout. Once this timeout has
  // elapsed, we proceed with the shutdown of Firefox.
  let seenException;

  let promiseClearPluginData = async function() {
    const FLAG_CLEAR_ALL = Ci.nsIPluginHost.FLAG_CLEAR_ALL;
    let ph = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);

    // Determine age range in seconds. (-1 means clear all.) We don't know
    // that range[1] is actually now, so we compute age range based
    // on the lower bound. If range results in a negative age, do nothing.
    let age = range ? (Date.now() / 1000 - range[0] / 1000000) : -1;
    if (!range || age >= 0) {
      let tags = ph.getPluginTags();
      for (let tag of tags) {
        try {
          let rv = await new Promise(resolve =>
            ph.clearSiteData(tag, null, FLAG_CLEAR_ALL, age, resolve)
          );
          // If the plugin doesn't support clearing by age, clear everything.
          if (rv == Cr.NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED) {
            await new Promise(resolve =>
              ph.clearSiteData(tag, null, FLAG_CLEAR_ALL, -1, resolve)
            );
          }
        } catch (ex) {
          // Ignore errors from plug-ins
        }
      }
    }
  };

  try {
    // We don't want to wait for this operation to complete...
    promiseClearPluginData = promiseClearPluginData(range);

    // ... at least, not for more than 10 seconds.
    await Promise.race([
      promiseClearPluginData,
      new Promise(resolve => setTimeout(resolve, 10000 /* 10 seconds */))
    ]);
  } catch (ex) {
    seenException = ex;
  }

  // Detach waiting for plugin data to be cleared.
  promiseClearPluginData.catch(() => {
    // If this exception is raised before the soft timeout, it
    // will appear in `seenException`. Otherwise, it's too late
    // to do anything about it.
  });

  if (seenException) {
    throw seenException;
  }
}

async function sanitizeOnShutdown(progress) {
  if (!Sanitizer.shouldSanitizeOnShutdown) {
    return;
  }
  // Need to sanitize upon shutdown
  let itemsToClear = getItemsToClearFromPrefBranch(Sanitizer.PREF_SHUTDOWN_BRANCH);
  await Sanitizer.sanitize(itemsToClear, { progress });
  // We didn't crash during shutdown sanitization, so annotate it to avoid
  // sanitizing again on startup.
  removePendingSanitization("shutdown");
  Services.prefs.savePrefFile(null);
}

/**
 * Gets an array of items to clear from the given pref branch.
 * @param branch The pref branch to fetch.
 * @return Array of items to clear
 */
function getItemsToClearFromPrefBranch(branch) {
  branch = Services.prefs.getBranch(branch);
  return Object.keys(Sanitizer.items).filter(itemName => {
    try {
      return branch.getBoolPref(itemName);
    } catch (ex) {
      return false;
    }
  });
}

/**
 * These functions are used to track pending sanitization on the next startup
 * in case of a crash before a sanitization could happen.
 * @param id A unique id identifying the sanitization
 * @param itemsToClear The items to clear
 * @param options The Sanitize options
 */
function addPendingSanitization(id, itemsToClear, options) {
  let pendingSanitizations = safeGetPendingSanitizations();
  pendingSanitizations.push({id, itemsToClear, options});
  Services.prefs.setStringPref(Sanitizer.PREF_PENDING_SANITIZATIONS,
                               JSON.stringify(pendingSanitizations));
}
function removePendingSanitization(id) {
  let pendingSanitizations = safeGetPendingSanitizations();
  let i = pendingSanitizations.findIndex(s => s.id == id);
  let [s] = pendingSanitizations.splice(i, 1);
  Services.prefs.setStringPref(Sanitizer.PREF_PENDING_SANITIZATIONS,
    JSON.stringify(pendingSanitizations));
  return s;
}
function getAndClearPendingSanitizations() {
  let pendingSanitizations = safeGetPendingSanitizations();
  if (pendingSanitizations.length)
    Services.prefs.clearUserPref(Sanitizer.PREF_PENDING_SANITIZATIONS);
  return pendingSanitizations;
}
function safeGetPendingSanitizations() {
  try {
    return JSON.parse(
      Services.prefs.getStringPref(Sanitizer.PREF_PENDING_SANITIZATIONS, "[]"));
  } catch (ex) {
    Cu.reportError("Invalid JSON value for pending sanitizations: " + ex);
    return [];
  }
}
PK
!<Ǵ��1�1%modules/sessionstore/SessionWorker.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 worker */

/**
 * A worker dedicated to handle I/O for Session Store.
 */

"use strict";

importScripts("resource://gre/modules/osfile.jsm");

var PromiseWorker = require("resource://gre/modules/workers/PromiseWorker.js");

var File = OS.File;
var Encoder = new TextEncoder();
var Decoder = new TextDecoder();

var worker = new PromiseWorker.AbstractWorker();
worker.dispatch = function(method, args = []) {
  return Agent[method](...args);
};
worker.postMessage = function(result, ...transfers) {
  self.postMessage(result, ...transfers);
};
worker.close = function() {
  self.close();
};

self.addEventListener("message", msg => worker.handleMessage(msg));

// The various possible states

/**
 * We just started (we haven't written anything to disk yet) from
 * `Paths.clean`. The backup directory may not exist.
 */
const STATE_CLEAN = "clean";
/**
 * We know that `Paths.recovery` is good, either because we just read
 * it (we haven't written anything to disk yet) or because have
 * already written once to `Paths.recovery` during this session.
 * `Paths.clean` is absent or invalid. The backup directory exists.
 */
const STATE_RECOVERY = "recovery";
/**
 * We just started from `Paths.recoverBackupy` (we haven't written
 * anything to disk yet). Both `Paths.clean` and `Paths.recovery` are
 * absent or invalid. The backup directory exists.
 */
const STATE_RECOVERY_BACKUP = "recoveryBackup";
/**
 * We just started from `Paths.upgradeBackup` (we haven't written
 * anything to disk yet). Both `Paths.clean`, `Paths.recovery` and
 * `Paths.recoveryBackup` are absent or invalid. The backup directory
 * exists.
 */
const STATE_UPGRADE_BACKUP = "upgradeBackup";
/**
 * We just started without a valid session store file (we haven't
 * written anything to disk yet). The backup directory may not exist.
 */
const STATE_EMPTY = "empty";

var Agent = {
  // Path to the files used by the SessionWorker
  Paths: null,

  /**
   * The current state of the worker, as one of the following strings:
   * - "permanent", once the first write has been completed;
   * - "empty", before the first write has been completed,
   *   if we have started without any sessionstore;
   * - one of "clean", "recovery", "recoveryBackup", "cleanBackup",
   *   "upgradeBackup", before the first write has been completed, if
   *   we have started by loading the corresponding file.
   */
  state: null,

  /**
   * A flag that indicates we loaded a session file with the deprecated .js extension.
   */
  useOldExtension: false,

  /**
   * Number of old upgrade backups that are being kept
   */
  maxUpgradeBackups: null,

  /**
   * Initialize (or reinitialize) the worker
   *
   * @param {string} origin Which of sessionstore.js or its backups
   *   was used. One of the `STATE_*` constants defined above.
   * @param {boolean} a flag indicate whether we loaded a session file with ext .js
   * @param {object} paths The paths at which to find the various files.
   * @param {object} prefs The preferences the worker needs to known.
   */
  init(origin, useOldExtension, paths, prefs = {}) {
    if (!(origin in paths || origin == STATE_EMPTY)) {
      throw new TypeError("Invalid origin: " + origin);
    }

    // Check that all required preference values were passed.
    for (let pref of ["maxUpgradeBackups", "maxSerializeBack", "maxSerializeForward"]) {
      if (!prefs.hasOwnProperty(pref)) {
        throw new TypeError(`Missing preference value for ${pref}`);
      }
    }

    this.useOldExtension = useOldExtension;
    this.state = origin;
    this.Paths = paths;
    this.maxUpgradeBackups = prefs.maxUpgradeBackups;
    this.maxSerializeBack = prefs.maxSerializeBack;
    this.maxSerializeForward = prefs.maxSerializeForward;
    this.upgradeBackupNeeded = paths.nextUpgradeBackup != paths.upgradeBackup;
    return {result: true};
  },

  /**
   * Write the session to disk.
   * Write the session to disk, performing any necessary backup
   * along the way.
   *
   * @param {object} state The state to write to disk.
   * @param {object} options
   *  - performShutdownCleanup If |true|, we should
   *    perform shutdown-time cleanup to ensure that private data
   *    is not left lying around;
   *  - isFinalWrite If |true|, write to Paths.clean instead of
   *    Paths.recovery
   */
  write(state, options = {}) {
    let exn;
    let telemetry = {};

    // Cap the number of backward and forward shistory entries on shutdown.
    if (options.isFinalWrite) {
      for (let window of state.windows) {
        for (let tab of window.tabs) {
          let lower = 0;
          let upper = tab.entries.length;

          if (this.maxSerializeBack > -1) {
            lower = Math.max(lower, tab.index - this.maxSerializeBack - 1);
          }
          if (this.maxSerializeForward > -1) {
            upper = Math.min(upper, tab.index + this.maxSerializeForward);
          }

          tab.entries = tab.entries.slice(lower, upper);
          tab.index -= lower;
        }
      }
    }

    let stateString = JSON.stringify(state);
    let data = Encoder.encode(stateString);

    try {

      if (this.state == STATE_CLEAN || this.state == STATE_EMPTY) {
        // The backups directory may not exist yet. In all other cases,
        // we have either already read from or already written to this
        // directory, so we are satisfied that it exists.
        File.makeDir(this.Paths.backups);
      }

      if (this.state == STATE_CLEAN) {
        // Move $Path.clean out of the way, to avoid any ambiguity as
        // to which file is more recent.
        if (!this.useOldExtension) {
          File.move(this.Paths.clean, this.Paths.cleanBackup);
        } else {
          // Since we are migrating from .js to .jsonlz4,
          // we need to compress the deprecated $Path.clean
          // and write it to $Path.cleanBackup.
          let oldCleanPath = this.Paths.clean.replace("jsonlz4", "js");
          let d = File.read(oldCleanPath);
          File.writeAtomic(this.Paths.cleanBackup, d, {compression: "lz4"});
        }
      }

      let startWriteMs = Date.now();
      let fileStat;

      if (options.isFinalWrite) {
        // We are shutting down. At this stage, we know that
        // $Paths.clean is either absent or corrupted. If it was
        // originally present and valid, it has been moved to
        // $Paths.cleanBackup a long time ago. We can therefore write
        // with the guarantees that we erase no important data.
        File.writeAtomic(this.Paths.clean, data, {
          tmpPath: this.Paths.clean + ".tmp",
          compression: "lz4"
        });
        fileStat = File.stat(this.Paths.clean);
      } else if (this.state == STATE_RECOVERY) {
        // At this stage, either $Paths.recovery was written >= 15
        // seconds ago during this session or we have just started
        // from $Paths.recovery left from the previous session. Either
        // way, $Paths.recovery is good. We can move $Path.backup to
        // $Path.recoveryBackup without erasing a good file with a bad
        // file.
        File.writeAtomic(this.Paths.recovery, data, {
          tmpPath: this.Paths.recovery + ".tmp",
          backupTo: this.Paths.recoveryBackup,
          compression: "lz4"
        });
        fileStat = File.stat(this.Paths.recovery);
      } else {
        // In other cases, either $Path.recovery is not necessary, or
        // it doesn't exist or it has been corrupted. Regardless,
        // don't backup $Path.recovery.
        File.writeAtomic(this.Paths.recovery, data, {
          tmpPath: this.Paths.recovery + ".tmp",
          compression: "lz4"
        });
        fileStat = File.stat(this.Paths.recovery);
      }

      telemetry.FX_SESSION_RESTORE_WRITE_FILE_MS = Date.now() - startWriteMs;
      telemetry.FX_SESSION_RESTORE_FILE_SIZE_BYTES = fileStat.size;

    } catch (ex) {
      // Don't throw immediately
      exn = exn || ex;
    }

    // If necessary, perform an upgrade backup
    let upgradeBackupComplete = false;
    if (this.upgradeBackupNeeded
      && (this.state == STATE_CLEAN || this.state == STATE_UPGRADE_BACKUP)) {
      try {
        // If we loaded from `clean`, the file has since then been renamed to `cleanBackup`.
        let path = this.state == STATE_CLEAN ? this.Paths.cleanBackup : this.Paths.upgradeBackup;
        File.copy(path, this.Paths.nextUpgradeBackup);
        this.upgradeBackupNeeded = false;
        upgradeBackupComplete = true;
      } catch (ex) {
        // Don't throw immediately
        exn = exn || ex;
      }

      // Find all backups
      let iterator;
      let backups = []; // array that will contain the paths to all upgrade backup
      let upgradeBackupPrefix = this.Paths.upgradeBackupPrefix; // access for forEach callback

      try {
        iterator = new File.DirectoryIterator(this.Paths.backups);
        iterator.forEach(function(file) {
          if (file.path.startsWith(upgradeBackupPrefix)) {
            backups.push(file.path);
          }
        }, this);
      } catch (ex) {
          // Don't throw immediately
          exn = exn || ex;
      } finally {
        if (iterator) {
          iterator.close();
        }
      }

      // If too many backups exist, delete them
      if (backups.length > this.maxUpgradeBackups) {
        // Use alphanumerical sort since dates are in YYYYMMDDHHMMSS format
        backups.sort().forEach((file, i) => {
          // remove backup file if it is among the first (n-maxUpgradeBackups) files
          if (i < backups.length - this.maxUpgradeBackups) {
            File.remove(file);
          }
        });
      }
    }

    if (options.performShutdownCleanup && !exn) {

      // During shutdown, if auto-restore is disabled, we need to
      // remove possibly sensitive data that has been stored purely
      // for crash recovery. Note that this slightly decreases our
      // ability to recover from OS-level/hardware-level issue.

      // If an exception was raised, we assume that we still need
      // these files.
      File.remove(this.Paths.recoveryBackup);
      File.remove(this.Paths.recovery);
    }

    this.state = STATE_RECOVERY;

    if (exn) {
      throw exn;
    }

    return {
      result: {
        upgradeBackup: upgradeBackupComplete
      },
      telemetry,
    };
  },

  /**
   * Wipes all files holding session data from disk.
   */
  wipe() {

    // Don't stop immediately in case of error.
    let exn = null;

    // Erase main session state file
    try {
      File.remove(this.Paths.clean);
      // Remove old extension ones.
      File.remove(this.Paths.clean.replace("jsonlz4", "js"), {ignoreAbsent: true});
    } catch (ex) {
      // Don't stop immediately.
      exn = exn || ex;
    }

    // Wipe the Session Restore directory
    try {
      this._wipeFromDir(this.Paths.backups, null);
    } catch (ex) {
      exn = exn || ex;
    }

    try {
      File.removeDir(this.Paths.backups);
    } catch (ex) {
      exn = exn || ex;
    }

    // Wipe legacy Ression Restore files from the profile directory
    try {
      this._wipeFromDir(OS.Constants.Path.profileDir, "sessionstore.bak");
    } catch (ex) {
      exn = exn || ex;
    }


    this.state = STATE_EMPTY;
    if (exn) {
      throw exn;
    }

    return { result: true };
  },

  /**
   * Wipe a number of files from a directory.
   *
   * @param {string} path The directory.
   * @param {string|null} prefix If provided, only remove files whose
   * name starts with a specific prefix.
   */
  _wipeFromDir(path, prefix) {
    // Sanity check
    if (typeof prefix == "undefined" || prefix == "") {
      throw new TypeError();
    }

    let exn = null;

    let iterator = new File.DirectoryIterator(path);
    try {
      if (!iterator.exists()) {
        return;
      }
      for (let entry of iterator) {
        if (entry.isDir) {
          continue;
        }
        if (!prefix || entry.name.startsWith(prefix)) {
          try {
            File.remove(entry.path);
          } catch (ex) {
            // Don't stop immediately
            exn = exn || ex;
          }
        }
      }

      if (exn) {
        throw exn;
      }
    } finally {
      iterator.close();
    }
  },
};

function isNoSuchFileEx(aReason) {
  return aReason instanceof OS.File.Error && aReason.becauseNoSuchFile;
}

/**
 * Estimate the number of bytes that a data structure will use on disk
 * once serialized.
 */
function getByteLength(str) {
  return Encoder.encode(JSON.stringify(str)).byteLength;
}
PK
!<��]��0chrome/browser/skin/classic/browser/settings.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M15 7h-2.1a4.967 4.967 0 0 0-.732-1.753l1.49-1.49a1 1 0 0 0-1.414-1.414l-1.49 1.49A4.968 4.968 0 0 0 9 3.1V1a1 1 0 0 0-2 0v2.1a4.968 4.968 0 0 0-1.753.732l-1.49-1.49a1 1 0 0 0-1.414 1.415l1.49 1.49A4.967 4.967 0 0 0 3.1 7H1a1 1 0 0 0 0 2h2.1a4.968 4.968 0 0 0 .737 1.763c-.014.013-.032.017-.045.03l-1.45 1.45a1 1 0 1 0 1.414 1.414l1.45-1.45c.013-.013.018-.031.03-.045A4.968 4.968 0 0 0 7 12.9V15a1 1 0 0 0 2 0v-2.1a4.968 4.968 0 0 0 1.753-.732l1.49 1.49a1 1 0 0 0 1.414-1.414l-1.49-1.49A4.967 4.967 0 0 0 12.9 9H15a1 1 0 0 0 0-2zM5 8a3 3 0 1 1 3 3 3 3 0 0 1-3-3z"/>
</svg>
PK
!<��=ۉ,�,)chrome/pdfjs/content/PdfjsChromeUtils.jsm/* Copyright 2012 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.
 */

"use strict";

var EXPORTED_SYMBOLS = ["PdfjsChromeUtils"];

const PREF_PREFIX = "pdfjs";
const PDF_CONTENT_TYPE = "application/pdf";

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");

var Svc = {};
XPCOMUtils.defineLazyServiceGetter(Svc, "mime",
                                   "@mozilla.org/mime;1",
                                   "nsIMIMEService");

/* eslint-disable semi */
var DEFAULT_PREFERENCES =
{
  "showPreviousViewOnLoad": true,
  "defaultZoomValue": "",
  "sidebarViewOnLoad": 0,
  "cursorToolOnLoad": 0,
  "enableWebGL": false,
  "pdfBugEnabled": false,
  "disableRange": false,
  "disableStream": false,
  "disableAutoFetch": false,
  "disableFontFace": false,
  "textLayerMode": 1,
  "useOnlyCssZoom": false,
  "externalLinkTarget": 0,
  "renderer": "canvas",
  "renderInteractiveForms": false,
  "enablePrintAutoRotate": false,
  "disablePageMode": false,
  "disablePageLabels": false
}

/* eslint-enable semi */

var PdfjsChromeUtils = {
  // For security purposes when running remote, we restrict preferences
  // content can access.
  _allowedPrefNames: Object.keys(DEFAULT_PREFERENCES),
  _ppmm: null,
  _mmg: null,

  /*
   * Public API
   */

  init() {
    this._browsers = new WeakSet();
    if (!this._ppmm) {
      // global parent process message manager (PPMM)
      this._ppmm = Services.ppmm;
      this._ppmm.addMessageListener("PDFJS:Parent:clearUserPref", this);
      this._ppmm.addMessageListener("PDFJS:Parent:setIntPref", this);
      this._ppmm.addMessageListener("PDFJS:Parent:setBoolPref", this);
      this._ppmm.addMessageListener("PDFJS:Parent:setCharPref", this);
      this._ppmm.addMessageListener("PDFJS:Parent:setStringPref", this);
      this._ppmm.addMessageListener("PDFJS:Parent:isDefaultHandlerApp", this);

      // global dom message manager (MMg)
      this._mmg = Services.mm;
      this._mmg.addMessageListener("PDFJS:Parent:displayWarning", this);

      this._mmg.addMessageListener("PDFJS:Parent:addEventListener", this);
      this._mmg.addMessageListener("PDFJS:Parent:removeEventListener", this);
      this._mmg.addMessageListener("PDFJS:Parent:updateControlState", this);

      // Observer to handle shutdown.
      Services.obs.addObserver(this, "quit-application");
    }
  },

  uninit() {
    if (this._ppmm) {
      this._ppmm.removeMessageListener("PDFJS:Parent:clearUserPref", this);
      this._ppmm.removeMessageListener("PDFJS:Parent:setIntPref", this);
      this._ppmm.removeMessageListener("PDFJS:Parent:setBoolPref", this);
      this._ppmm.removeMessageListener("PDFJS:Parent:setCharPref", this);
      this._ppmm.removeMessageListener("PDFJS:Parent:setStringPref", this);
      this._ppmm.removeMessageListener("PDFJS:Parent:isDefaultHandlerApp",
                                       this);

      this._mmg.removeMessageListener("PDFJS:Parent:displayWarning", this);

      this._mmg.removeMessageListener("PDFJS:Parent:addEventListener", this);
      this._mmg.removeMessageListener("PDFJS:Parent:removeEventListener", this);
      this._mmg.removeMessageListener("PDFJS:Parent:updateControlState", this);

      Services.obs.removeObserver(this, "quit-application");

      this._mmg = null;
      this._ppmm = null;
    }
  },

  /*
   * Called by the main module when preference changes are picked up
   * in the parent process. Observers don't propagate so we need to
   * instruct the child to refresh its configuration and (possibly)
   * the module's registration.
   */
  notifyChildOfSettingsChange(enabled) {
    if (Services.appinfo.processType ===
        Services.appinfo.PROCESS_TYPE_DEFAULT && this._ppmm) {
      // XXX kinda bad, we want to get the parent process mm associated
      // with the content process. _ppmm is currently the global process
      // manager, which means this is going to fire to every child process
      // we have open. Unfortunately I can't find a way to get at that
      // process specific mm from js.
      this._ppmm.broadcastAsyncMessage("PDFJS:Child:updateSettings",
                                       { enabled, });
    }
  },

  /*
   * Events
   */

  observe(aSubject, aTopic, aData) {
    if (aTopic === "quit-application") {
      this.uninit();
    }
  },

  receiveMessage(aMsg) {
    switch (aMsg.name) {
      case "PDFJS:Parent:clearUserPref":
        this._clearUserPref(aMsg.data.name);
        break;
      case "PDFJS:Parent:setIntPref":
        this._setIntPref(aMsg.data.name, aMsg.data.value);
        break;
      case "PDFJS:Parent:setBoolPref":
        this._setBoolPref(aMsg.data.name, aMsg.data.value);
        break;
      case "PDFJS:Parent:setCharPref":
        this._setCharPref(aMsg.data.name, aMsg.data.value);
        break;
      case "PDFJS:Parent:setStringPref":
        this._setStringPref(aMsg.data.name, aMsg.data.value);
        break;
      case "PDFJS:Parent:isDefaultHandlerApp":
        return this.isDefaultHandlerApp();
      case "PDFJS:Parent:displayWarning":
        this._displayWarning(aMsg);
        break;

      case "PDFJS:Parent:updateControlState":
        return this._updateControlState(aMsg);
      case "PDFJS:Parent:addEventListener":
        return this._addEventListener(aMsg);
      case "PDFJS:Parent:removeEventListener":
        return this._removeEventListener(aMsg);
    }
    return undefined;
  },

  /*
   * Internal
   */

  _findbarFromMessage(aMsg) {
    let browser = aMsg.target;
    let tabbrowser = browser.getTabBrowser();
    let tab = tabbrowser.getTabForBrowser(browser);
    return tabbrowser.getFindBar(tab);
  },

  _updateControlState(aMsg) {
    let data = aMsg.data;
    this._findbarFromMessage(aMsg)
        .updateControlState(data.result, data.findPrevious);
  },

  handleEvent(aEvent) {
    // To avoid forwarding the message as a CPOW, create a structured cloneable
    // version of the event for both performance, and ease of usage, reasons.
    let type = aEvent.type;
    let detail = {
      query: aEvent.detail.query,
      caseSensitive: aEvent.detail.caseSensitive,
      highlightAll: aEvent.detail.highlightAll,
      findPrevious: aEvent.detail.findPrevious,
    };

    let browser = aEvent.currentTarget.browser;
    if (!this._browsers.has(browser)) {
      throw new Error("FindEventManager was not bound " +
                      "for the current browser.");
    }
    // Only forward the events if the current browser is a registered browser.
    let mm = browser.messageManager;
    mm.sendAsyncMessage("PDFJS:Child:handleEvent", { type, detail, });
    aEvent.preventDefault();
  },

  _types: ["find",
           "findagain",
           "findhighlightallchange",
           "findcasesensitivitychange"],

  _addEventListener(aMsg) {
    let browser = aMsg.target;
    if (this._browsers.has(browser)) {
      throw new Error("FindEventManager was bound 2nd time " +
                      "without unbinding it first.");
    }

    // Since this jsm is global, we need to store all the browsers
    // we have to forward the messages for.
    this._browsers.add(browser);

    // And we need to start listening to find events.
    for (var i = 0; i < this._types.length; i++) {
      var type = this._types[i];
      this._findbarFromMessage(aMsg)
          .addEventListener(type, this, true);
    }
  },

  _removeEventListener(aMsg) {
    let browser = aMsg.target;
    if (!this._browsers.has(browser)) {
      throw new Error("FindEventManager was unbound without binding it first.");
    }

    this._browsers.delete(browser);

    // No reason to listen to find events any longer.
    for (var i = 0; i < this._types.length; i++) {
      var type = this._types[i];
      this._findbarFromMessage(aMsg)
          .removeEventListener(type, this, true);
    }
  },

  _ensurePreferenceAllowed(aPrefName) {
    let unPrefixedName = aPrefName.split(PREF_PREFIX + ".");
    if (unPrefixedName[0] !== "" ||
        !this._allowedPrefNames.includes(unPrefixedName[1])) {
      let msg = "\"" + aPrefName + "\" " +
                "can't be accessed from content. See PdfjsChromeUtils.";
      throw new Error(msg);
    }
  },

  _clearUserPref(aPrefName) {
    this._ensurePreferenceAllowed(aPrefName);
    Services.prefs.clearUserPref(aPrefName);
  },

  _setIntPref(aPrefName, aPrefValue) {
    this._ensurePreferenceAllowed(aPrefName);
    Services.prefs.setIntPref(aPrefName, aPrefValue);
  },

  _setBoolPref(aPrefName, aPrefValue) {
    this._ensurePreferenceAllowed(aPrefName);
    Services.prefs.setBoolPref(aPrefName, aPrefValue);
  },

  _setCharPref(aPrefName, aPrefValue) {
    this._ensurePreferenceAllowed(aPrefName);
    Services.prefs.setCharPref(aPrefName, aPrefValue);
  },

  _setStringPref(aPrefName, aPrefValue) {
    this._ensurePreferenceAllowed(aPrefName);
    Services.prefs.setStringPref(aPrefName, aPrefValue);
  },

  /*
   * Svc.mime doesn't have profile information in the child, so
   * we bounce this pdfjs enabled configuration check over to the
   * parent.
   */
  isDefaultHandlerApp() {
    var handlerInfo = Svc.mime.getFromTypeAndExtension(PDF_CONTENT_TYPE, "pdf");
    return (!handlerInfo.alwaysAskBeforeHandling &&
            handlerInfo.preferredAction === Ci.nsIHandlerInfo.handleInternally);
  },

  /*
   * Display a notification warning when the renderer isn't sure
   * a pdf displayed correctly.
   */
  _displayWarning(aMsg) {
    let data = aMsg.data;
    let browser = aMsg.target;

    let tabbrowser = browser.getTabBrowser();
    let notificationBox = tabbrowser.getNotificationBox(browser);

    // Flag so we don't send the message twice, since if the user clicks
    // "open with different viewer" both the button callback and
    // eventCallback will be called.
    let messageSent = false;
    function sendMessage(download) {
      let mm = browser.messageManager;
      mm.sendAsyncMessage("PDFJS:Child:fallbackDownload", { download, });
    }
    let buttons = [{
      label: data.label,
      accessKey: data.accessKey,
      callback() {
        messageSent = true;
        sendMessage(true);
      },
    }];
    notificationBox.appendNotification(data.message, "pdfjs-fallback", null,
                                       notificationBox.PRIORITY_INFO_LOW,
                                       buttons,
                                       function eventsCallback(eventType) {
      // Currently there is only one event "removed" but if there are any other
      // added in the future we still only care about removed at the moment.
      if (eventType !== "removed") {
        return;
      }
      // Don't send a response again if we already responded when the button was
      // clicked.
      if (messageSent) {
        return;
      }
      sendMessage(false);
    });
  },
};

PK
!<����'modules/sessionstore/SessionStorage.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = ["SessionStorage"];

ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");

const ssu = Cc["@mozilla.org/browser/sessionstore/utils;1"]
              .createInstance(Ci.nsISessionStoreUtils);

// A bound to the size of data to store for DOM Storage.
const DOM_STORAGE_LIMIT_PREF = "browser.sessionstore.dom_storage_limit";

// Returns the principal for a given |frame| contained in a given |docShell|.
function getPrincipalForFrame(docShell, frame) {
  let ssm = Services.scriptSecurityManager;
  let uri = frame.document.documentURIObject;
  return ssm.getDocShellCodebasePrincipal(uri, docShell);
}

var SessionStorage = Object.freeze({
  /**
   * Updates all sessionStorage "super cookies"
   * @param content
   *        A tab's global, i.e. the root frame we want to collect for.
   * @return Returns a nested object that will have hosts as keys and per-origin
   *         session storage data as strings. For example:
   *         {"https://example.com^userContextId=1": {"key": "value", "my_number": "123"}}
   */
  collect(content) {
    return SessionStorageInternal.collect(content);
  },

  /**
   * Restores all sessionStorage "super cookies".
   * @param aDocShell
   *        A tab's docshell (containing the sessionStorage)
   * @param aStorageData
   *        A nested object with storage data to be restored that has hosts as
   *        keys and per-origin session storage data as strings. For example:
   *        {"https://example.com^userContextId=1": {"key": "value", "my_number": "123"}}
   */
  restore(aDocShell, aStorageData) {
    SessionStorageInternal.restore(aDocShell, aStorageData);
  },
});

/**
 * Calls the given callback |cb|, passing |frame| and each of its descendants.
 */
function forEachNonDynamicChildFrame(frame, cb) {
  // Call for current frame.
  cb(frame);

  // Call the callback recursively for each descendant.
  ssu.forEachNonDynamicChildFrame(frame, subframe => {
    return forEachNonDynamicChildFrame(subframe, cb);
  });
}

var SessionStorageInternal = {
  /**
   * Reads all session storage data from the given docShell.
   * @param content
   *        A tab's global, i.e. the root frame we want to collect for.
   * @return Returns a nested object that will have hosts as keys and per-origin
   *         session storage data as strings. For example:
   *         {"https://example.com^userContextId=1": {"key": "value", "my_number": "123"}}
   */
  collect(content) {
    let data = {};
    let visitedOrigins = new Set();
    let docShell = content.QueryInterface(Ci.nsIInterfaceRequestor)
                          .getInterface(Ci.nsIWebNavigation)
                          .QueryInterface(Ci.nsIDocShell);

    forEachNonDynamicChildFrame(content, frame => {
      let principal = getPrincipalForFrame(docShell, frame);
      if (!principal) {
        return;
      }

      // Get the origin of the current history entry
      // and use that as a key for the per-principal storage data.
      let origin;
      try {
        // The origin getter may throw for about:blank iframes as of bug 1340710,
        // but we should ignore them anyway.
        origin = principal.origin;
      } catch (e) {
        return;
      }
      if (visitedOrigins.has(origin)) {
        // Don't read a host twice.
        return;
      }

      // Mark the current origin as visited.
      visitedOrigins.add(origin);

      let originData = this._readEntry(principal, docShell);
      if (Object.keys(originData).length) {
        data[origin] = originData;
      }
    });

    return Object.keys(data).length ? data : null;
  },

  /**
   * Writes session storage data to the given tab.
   * @param aDocShell
   *        A tab's docshell (containing the sessionStorage)
   * @param aStorageData
   *        A nested object with storage data to be restored that has hosts as
   *        keys and per-origin session storage data as strings. For example:
   *        {"https://example.com^userContextId=1": {"key": "value", "my_number": "123"}}
   */
  restore(aDocShell, aStorageData) {
    for (let origin of Object.keys(aStorageData)) {
      let data = aStorageData[origin];

      let principal;

      try {
        // NOTE: In capture() we record the full origin for the URI which the
        // sessionStorage is being captured for. As of bug 1235657 this code
        // stopped parsing any origins which have originattributes correctly, as
        // it decided to use the origin attributes from the docshell, and try to
        // interpret the origin as a URI. Since bug 1353844 this code now correctly
        // parses the full origin, and then discards the origin attributes, to
        // make the behavior line up with the original intentions in bug 1235657
        // while preserving the ability to read all session storage from
        // previous versions. In the future, if this behavior is desired, we may
        // want to use the spec instead of the origin as the key, and avoid
        // transmitting origin attribute information which we then discard when
        // restoring.
        //
        // If changing this logic, make sure to also change the principal
        // computation logic in SessionStore::_sendRestoreHistory.
        let attrs = aDocShell.getOriginAttributes();
        let dataPrincipal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(origin);
        principal = Services.scriptSecurityManager.createCodebasePrincipal(dataPrincipal.URI, attrs);
      } catch (e) {
        console.error(e);
        continue;
      }

      let storageManager = aDocShell.QueryInterface(Ci.nsIDOMStorageManager);
      let window = aDocShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);

      // There is no need to pass documentURI, it's only used to fill documentURI property of
      // domstorage event, which in this case has no consumer. Prevention of events in case
      // of missing documentURI will be solved in a followup bug to bug 600307.
      let storage = storageManager.createStorage(window, principal, "", aDocShell.usePrivateBrowsing);

      for (let key of Object.keys(data)) {
        try {
          storage.setItem(key, data[key]);
        } catch (e) {
          // throws e.g. for URIs that can't have sessionStorage
          console.error(e);
        }
      }
    }
  },

  /**
   * Reads an entry in the session storage data contained in a tab's history.
   * @param aURI
   *        That history entry uri
   * @param aDocShell
   *        A tab's docshell (containing the sessionStorage)
   */
  _readEntry(aPrincipal, aDocShell) {
    let hostData = {};
    let storage;

    let window = aDocShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);

    try {
      let storageManager = aDocShell.QueryInterface(Ci.nsIDOMStorageManager);
      storage = storageManager.getStorage(window, aPrincipal);
      storage.length; // XXX: Bug 1232955 - storage.length can throw, catch that failure
    } catch (e) {
      // sessionStorage might throw if it's turned off, see bug 458954
      storage = null;
    }

    if (!storage || !storage.length) {
      return hostData;
    }

    // If the DOMSessionStorage contains too much data, ignore it.
    let usage = window.QueryInterface(Ci.nsIInterfaceRequestor)
                      .getInterface(Ci.nsIDOMWindowUtils)
                      .getStorageUsage(storage);
    if (usage > Services.prefs.getIntPref(DOM_STORAGE_LIMIT_PREF)) {
      return hostData;
    }

    for (let i = 0; i < storage.length; i++) {
      try {
        let key = storage.key(i);
        hostData[key] = storage.getItem(key);
      } catch (e) {
        // This currently throws for secured items (cf. bug 442048).
      }
    }

    return hostData;
  }
};
PK
!<z��-��modules/DownloadsTaskbar.jsm/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
 * Handles the download progress indicator in the taskbar.
 */

"use strict";

var EXPORTED_SYMBOLS = [
  "DownloadsTaskbar",
];

// Globals

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");

ChromeUtils.defineModuleGetter(this, "Downloads",
                               "resource://gre/modules/Downloads.jsm");
ChromeUtils.defineModuleGetter(this, "RecentWindow",
                               "resource:///modules/RecentWindow.jsm");
ChromeUtils.defineModuleGetter(this, "Services",
                               "resource://gre/modules/Services.jsm");

XPCOMUtils.defineLazyGetter(this, "gWinTaskbar", function() {
  if (!("@mozilla.org/windows-taskbar;1" in Cc)) {
    return null;
  }
  let winTaskbar = Cc["@mozilla.org/windows-taskbar;1"]
                     .getService(Ci.nsIWinTaskbar);
  return winTaskbar.available && winTaskbar;
});

XPCOMUtils.defineLazyGetter(this, "gMacTaskbarProgress", function() {
  return ("@mozilla.org/widget/macdocksupport;1" in Cc) &&
         Cc["@mozilla.org/widget/macdocksupport;1"]
           .getService(Ci.nsITaskbarProgress);
});

// DownloadsTaskbar

/**
 * Handles the download progress indicator in the taskbar.
 */
var DownloadsTaskbar = {
  /**
   * Underlying DownloadSummary providing the aggregate download information, or
   * null if the indicator has never been initialized.
   */
  _summary: null,

  /**
   * nsITaskbarProgress object to which download information is dispatched.
   * This can be null if the indicator has never been initialized or if the
   * indicator is currently hidden on Windows.
   */
  _taskbarProgress: null,

  /**
   * This method is called after a new browser window is opened, and ensures
   * that the download progress indicator is displayed in the taskbar.
   *
   * On Windows, the indicator is attached to the first browser window that
   * calls this method.  When the window is closed, the indicator is moved to
   * another browser window, if available, in no particular order.  When there
   * are no browser windows visible, the indicator is hidden.
   *
   * On Mac OS X, the indicator is initialized globally when this method is
   * called for the first time.  Subsequent calls have no effect.
   *
   * @param aBrowserWindow
   *        nsIDOMWindow object of the newly opened browser window to which the
   *        indicator may be attached.
   */
  registerIndicator(aBrowserWindow) {
    if (!this._taskbarProgress) {
      if (gMacTaskbarProgress) {
        // On Mac OS X, we have to register the global indicator only once.
        this._taskbarProgress = gMacTaskbarProgress;
        // Free the XPCOM reference on shutdown, to prevent detecting a leak.
        Services.obs.addObserver(() => {
          this._taskbarProgress = null;
          gMacTaskbarProgress = null;
        }, "quit-application-granted");
      } else if (gWinTaskbar) {
        // On Windows, the indicator is currently hidden because we have no
        // previous browser window, thus we should attach the indicator now.
        this._attachIndicator(aBrowserWindow);
      } else {
        // The taskbar indicator is not available on this platform.
        return;
      }
    }

    // Ensure that the DownloadSummary object will be created asynchronously.
    if (!this._summary) {
      Downloads.getSummary(Downloads.ALL).then(summary => {
        // In case the method is re-entered, we simply ignore redundant
        // invocations of the callback, instead of keeping separate state.
        if (this._summary) {
          return undefined;
        }
        this._summary = summary;
        return this._summary.addView(this);
      }).catch(Cu.reportError);
    }
  },

  /**
   * On Windows, attaches the taskbar indicator to the specified browser window.
   */
  _attachIndicator(aWindow) {
    // Activate the indicator on the specified window.
    let docShell = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                          .getInterface(Ci.nsIWebNavigation)
                          .QueryInterface(Ci.nsIDocShellTreeItem).treeOwner
                          .QueryInterface(Ci.nsIInterfaceRequestor)
                          .getInterface(Ci.nsIXULWindow).docShell;
    this._taskbarProgress = gWinTaskbar.getTaskbarProgress(docShell);

    // If the DownloadSummary object has already been created, we should update
    // the state of the new indicator, otherwise it will be updated as soon as
    // the DownloadSummary view is registered.
    if (this._summary) {
      this.onSummaryChanged();
    }

    aWindow.addEventListener("unload", () => {
      // Locate another browser window, excluding the one being closed.
      let browserWindow = RecentWindow.getMostRecentBrowserWindow();
      if (browserWindow) {
        // Move the progress indicator to the other browser window.
        this._attachIndicator(browserWindow);
      } else {
        // The last browser window has been closed.  We remove the reference to
        // the taskbar progress object so that the indicator will be registered
        // again on the next browser window that is opened.
        this._taskbarProgress = null;
      }
    });
  },

  // DownloadSummary view

  onSummaryChanged() {
    // If the last browser window has been closed, we have no indicator any more.
    if (!this._taskbarProgress) {
      return;
    }

    if (this._summary.allHaveStopped || this._summary.progressTotalBytes == 0) {
      this._taskbarProgress.setProgressState(
                               Ci.nsITaskbarProgress.STATE_NO_PROGRESS, 0, 0);
    } else {
      // For a brief moment before completion, some download components may
      // report more transferred bytes than the total number of bytes.  Thus,
      // ensure that we never break the expectations of the progress indicator.
      let progressCurrentBytes = Math.min(this._summary.progressTotalBytes,
                                          this._summary.progressCurrentBytes);
      this._taskbarProgress.setProgressState(
                               Ci.nsITaskbarProgress.STATE_NORMAL,
                               progressCurrentBytes,
                               this._summary.progressTotalBytes);
    }
  },
};
PK
!<2�B���'modules/sessionstore/SessionCookies.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = ["SessionCookies"];

ChromeUtils.import("resource://gre/modules/Services.jsm", this);
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", this);

ChromeUtils.defineModuleGetter(this, "Utils",
  "resource://gre/modules/sessionstore/Utils.jsm");
ChromeUtils.defineModuleGetter(this, "PrivacyLevel",
  "resource://gre/modules/sessionstore/PrivacyLevel.jsm");

const MAX_EXPIRY = Number.MAX_SAFE_INTEGER;

/**
 * The external API implemented by the SessionCookies module.
 */
var SessionCookies = Object.freeze({
  collect() {
    return SessionCookiesInternal.collect();
  },

  restore(cookies) {
    SessionCookiesInternal.restore(cookies);
  }
});

/**
 * The internal API.
 */
var SessionCookiesInternal = {
  /**
   * Stores whether we're initialized, yet.
   */
  _initialized: false,

  /**
   * Retrieve an array of all stored session cookies.
   */
  collect() {
    this._ensureInitialized();
    return CookieStore.toArray();
  },

  /**
   * Restores a given list of session cookies.
   */
  restore(cookies) {
    for (let cookie of cookies) {
      let expiry = "expiry" in cookie ? cookie.expiry : MAX_EXPIRY;
      let cookieObj = {
        host: cookie.host,
        path: cookie.path || "",
        name: cookie.name || ""
      };

      let originAttributes = cookie.originAttributes || {};
      if (!Services.cookies.cookieExists(cookieObj, originAttributes)) {
        Services.cookies.add(cookie.host, cookie.path || "", cookie.name || "",
                             cookie.value, !!cookie.secure, !!cookie.httponly,
                             /* isSession = */ true, expiry, originAttributes);
      }
    }
  },

  /**
   * Handles observers notifications that are sent whenever cookies are added,
   * changed, or removed. Ensures that the storage is updated accordingly.
   */
  observe(subject, topic, data) {
    switch (data) {
      case "added":
        this._addCookie(subject);
        break;
      case "changed":
        this._updateCookie(subject);
        break;
      case "deleted":
        this._removeCookie(subject);
        break;
      case "cleared":
        CookieStore.clear();
        break;
      case "batch-deleted":
        this._removeCookies(subject);
        break;
      default:
        throw new Error("Unhandled session-cookie-changed notification.");
    }
  },

  /**
   * If called for the first time in a session, iterates all cookies in the
   * cookies service and puts them into the store if they're session cookies.
   */
  _ensureInitialized() {
    if (this._initialized) {
      return;
    }
    this._reloadCookies();
    this._initialized = true;
    Services.obs.addObserver(this, "session-cookie-changed");

    // Listen for privacy level changes to reload cookies when needed.
    Services.prefs.addObserver("browser.sessionstore.privacy_level", () => {
      this._reloadCookies();
    });
  },

  /**
   * Adds a given cookie to the store.
   */
  _addCookie(cookie) {
    cookie.QueryInterface(Ci.nsICookie2);

    // Store only session cookies, obey the privacy level.
    if (cookie.isSession && PrivacyLevel.canSave(cookie.isSecure)) {
      CookieStore.add(cookie);
    }
  },

  /**
   * Updates a given cookie.
   */
  _updateCookie(cookie) {
    cookie.QueryInterface(Ci.nsICookie2);

    // Store only session cookies, obey the privacy level.
    if (cookie.isSession && PrivacyLevel.canSave(cookie.isSecure)) {
      CookieStore.add(cookie);
    } else {
      CookieStore.delete(cookie);
    }
  },

  /**
   * Removes a given cookie from the store.
   */
  _removeCookie(cookie) {
    cookie.QueryInterface(Ci.nsICookie2);

    if (cookie.isSession) {
      CookieStore.delete(cookie);
    }
  },

  /**
   * Removes a given list of cookies from the store.
   */
  _removeCookies(cookies) {
    for (let i = 0; i < cookies.length; i++) {
      this._removeCookie(cookies.queryElementAt(i, Ci.nsICookie2));
    }
  },

  /**
   * Iterates all cookies in the cookies service and puts them into the store
   * if they're session cookies. Obeys the user's chosen privacy level.
   */
  _reloadCookies() {
    CookieStore.clear();

    // Bail out if we're not supposed to store cookies at all.
    if (!PrivacyLevel.canSave(false)) {
      return;
    }

    let iter = Services.cookies.sessionEnumerator;
    while (iter.hasMoreElements()) {
      this._addCookie(iter.getNext());
    }
  }
};

/**
 * The internal storage that keeps track of session cookies.
 */
var CookieStore = {
  /**
   * The internal map holding all known session cookies.
   */
  _entries: new Map(),

  /**
   * Stores a given cookie.
   *
   * @param cookie
   *        The nsICookie2 object to add to the storage.
   */
  add(cookie) {
    let jscookie = {host: cookie.host, value: cookie.value};

    // Only add properties with non-default values to save a few bytes.
    if (cookie.path) {
      jscookie.path = cookie.path;
    }

    if (cookie.name) {
      jscookie.name = cookie.name;
    }

    if (cookie.isSecure) {
      jscookie.secure = true;
    }

    if (cookie.isHttpOnly) {
      jscookie.httponly = true;
    }

    if (cookie.expiry < MAX_EXPIRY) {
      jscookie.expiry = cookie.expiry;
    }

    if (cookie.originAttributes) {
      jscookie.originAttributes = cookie.originAttributes;
    }

    this._entries.set(this._getKeyForCookie(cookie), jscookie);
  },

  /**
   * Removes a given cookie.
   *
   * @param cookie
   *        The nsICookie2 object to be removed from storage.
   */
  delete(cookie) {
    this._entries.delete(this._getKeyForCookie(cookie));
  },

  /**
   * Removes all cookies.
   */
  clear() {
    this._entries.clear();
  },

  /**
   * Return all cookies as an array.
   */
  toArray() {
    return [...this._entries.values()];
  },

  /**
   * Returns the key needed to properly store and identify a given cookie.
   * A cookie is uniquely identified by the combination of its host, name,
   * path, and originAttributes properties.
   *
   * @param cookie
   *        The nsICookie2 object to compute a key for.
   * @return string
   */
  _getKeyForCookie(cookie) {
    return JSON.stringify({
      host: cookie.host,
      name: cookie.name,
      path: cookie.path,
      attr: ChromeUtils.originAttributesToSuffix(cookie.originAttributes)
    });
  }
};
PK
!<�s~�� � -chrome/devtools-shim/content/DevToolsShim.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm", {});

const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", {});
XPCOMUtils.defineLazyGetter(this, "DevtoolsStartup", () => {
  return Cc["@mozilla.org/devtools/startup-clh;1"]
            .getService(Ci.nsICommandLineHandler)
            .wrappedJSObject;
});

const DEVTOOLS_ENABLED_PREF = "devtools.enabled";
const DEVTOOLS_POLICY_DISABLED_PREF = "devtools.policy.disabled";

this.EXPORTED_SYMBOLS = [
  "DevToolsShim",
];

function removeItem(array, callback) {
  let index = array.findIndex(callback);
  if (index >= 0) {
    array.splice(index, 1);
  }
}

/**
 * The DevToolsShim is a part of the DevTools go faster project, which moves the Firefox
 * DevTools outside of mozilla-central to an add-on. It aims to bridge the gap for
 * existing mozilla-central code that still needs to interact with DevTools (such as
 * web-extensions).
 *
 * DevToolsShim is a singleton that provides a set of helpers to interact with DevTools,
 * that work whether the DevTools addon is installed or not. It can be used to start
 * listening to events. As soon as a DevTools addon is installed the DevToolsShim will
 * forward all the requests received until then to the real DevTools instance.
 *
 * DevToolsShim.isInstalled() can also be used to know if DevTools are currently
 * installed.
 */
this.DevToolsShim = {
  _gDevTools: null,
  listeners: [],

  /**
   * Check if DevTools are currently installed (but not necessarily initialized).
   *
   * @return {Boolean} true if DevTools are installed.
   */
  isInstalled: function () {
    return Services.io.getProtocolHandler("resource")
             .QueryInterface(Ci.nsIResProtocolHandler)
             .hasSubstitution("devtools");
  },

  /**
   * Returns true if DevTools are enabled for the current profile. If devtools are not
   * enabled, initializing DevTools will open the onboarding page. Some entry points
   * should no-op in this case.
   */
  isEnabled: function () {
    let enabled = Services.prefs.getBoolPref(DEVTOOLS_ENABLED_PREF);
    return enabled && !this.isDisabledByPolicy();
  },

  /**
   * Returns true if the devtools are completely disabled and can not be enabled. All
   * entry points should return without throwing, initDevTools should never be called.
   */
  isDisabledByPolicy: function () {
    return Services.prefs.getBoolPref(DEVTOOLS_POLICY_DISABLED_PREF, false);
  },

  /**
   * Check if DevTools have already been initialized.
   *
   * @return {Boolean} true if DevTools are initialized.
   */
  isInitialized: function () {
    return !!this._gDevTools;
  },

  /**
   * Register an instance of gDevTools. Should be called by DevTools during startup.
   *
   * @param {DevTools} a devtools instance (from client/framework/devtools)
   */
  register: function (gDevTools) {
    this._gDevTools = gDevTools;
    this._onDevToolsRegistered();
    this._gDevTools.emit("devtools-registered");
  },

  /**
   * Unregister the current instance of gDevTools. Should be called by DevTools during
   * shutdown.
   */
  unregister: function () {
    if (this.isInitialized()) {
      this._gDevTools.emit("devtools-unregistered");
      this._gDevTools = null;
    }
  },

  /**
   * The following methods can be called before DevTools are initialized:
   * - on
   * - off
   *
   * If DevTools are not initialized when calling the method, DevToolsShim will call the
   * appropriate method as soon as a gDevTools instance is registered.
   */

  /**
   * This method is used by browser/components/extensions/ext-devtools.js for the events:
   * - toolbox-created
   * - toolbox-destroyed
   */
  on: function (event, listener) {
    if (this.isInitialized()) {
      this._gDevTools.on(event, listener);
    } else {
      this.listeners.push([event, listener]);
    }
  },

  /**
   * This method is currently only used by devtools code, but is kept here for consistency
   * with on().
   */
  off: function (event, listener) {
    if (this.isInitialized()) {
      this._gDevTools.off(event, listener);
    } else {
      removeItem(this.listeners, ([e, l]) => e === event && l === listener);
    }
  },

  /**
   * Called from SessionStore.jsm in mozilla-central when saving the current state.
   *
   * @param {Object} state
   *                 A SessionStore state object that gets modified by reference
   */
  saveDevToolsSession: function (state) {
    if (!this.isInitialized()) {
      return;
    }

    this._gDevTools.saveDevToolsSession(state);
  },

  /**
   * Called from SessionStore.jsm in mozilla-central when restoring a previous session.
   * Will always be called, even if the session does not contain DevTools related items.
   */
  restoreDevToolsSession: function (session) {
    if (!this.isEnabled()) {
      return;
    }

    let {scratchpads, browserConsole, browserToolbox} = session;
    let hasDevToolsData = browserConsole || browserToolbox ||
                          (scratchpads && scratchpads.length);
    if (!hasDevToolsData) {
      // Do not initialize DevTools unless there is DevTools specific data in the session.
      return;
    }

    this.initDevTools();
    this._gDevTools.restoreDevToolsSession(session);
  },

  /**
   * Called from nsContextMenu.js in mozilla-central when using the Inspect Element
   * context menu item.
   *
   * @param {XULTab} tab
   *        The browser tab on which inspect node was used.
   * @param {Array} selectors
   *        An array of CSS selectors to find the target node. Several selectors can be
   *        document. The selectors are ordered starting with the root document and
   *        ending with the deepest nested frame.
   * @return {Promise} a promise that resolves when the node is selected in the inspector
   *         markup view or that resolves immediately if DevTools are not installed.
   */
  inspectNode: function (tab, selectors) {
    if (!this.isEnabled()) {
      if (!this.isDisabledByPolicy()) {
        DevtoolsStartup.openInstallPage("ContextMenu");
      }
      return Promise.resolve();
    }

    // Record the timing at which this event started in order to compute later in
    // gDevTools.showToolbox, the complete time it takes to open the toolbox.
    // i.e. especially take `DevtoolsStartup.initDevTools` into account.
    let { performance } = Services.appShell.hiddenDOMWindow;
    let startTime = performance.now();

    this.initDevTools("ContextMenu");

    return this._gDevTools.inspectNode(tab, selectors, startTime);
  },

  _onDevToolsRegistered: function () {
    // Register all pending event listeners on the real gDevTools object.
    for (let [event, listener] of this.listeners) {
      this._gDevTools.on(event, listener);
    }

    this.listeners = [];
  },

  /**
   * Initialize DevTools via DevToolsStartup if needed. This method throws if DevTools are
   * not enabled.. If the entry point is supposed to trigger the onboarding, call it
   * explicitly via DevtoolsStartup.openInstallPage().
   *
   * @param {String} reason
   *        optional, if provided should be a valid entry point for DEVTOOLS_ENTRY_POINT
   *        in toolkit/components/telemetry/Histograms.json
   */
  initDevTools: function (reason) {
    if (!this.isEnabled()) {
      throw new Error("DevTools are not enabled and can not be initialized.");
    }

    if (!this.isInitialized()) {
      DevtoolsStartup.initDevTools(reason);
    }
  }
};

/**
 * Compatibility layer for webextensions.
 *
 * Those methods are called only after a DevTools webextension was loaded in DevTools,
 * therefore DevTools should always be available when they are called.
 */
let webExtensionsMethods = [
  "createTargetForTab",
  "createWebExtensionInspectedWindowFront",
  "getTargetForTab",
  "getTheme",
  "openBrowserConsole",
];

for (let method of webExtensionsMethods) {
  this.DevToolsShim[method] = function () {
    if (!this.isEnabled()) {
      throw new Error("Could not call a DevToolsShim webextension method ('" + method +
        "'): DevTools are not initialized.");
    }

    this.initDevTools();
    return this._gDevTools[method].apply(this._gDevTools, arguments);
  };
}
PK
!<��	�qq&modules/sessionstore/PrivacyFilter.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = ["PrivacyFilter"];

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", this);

ChromeUtils.defineModuleGetter(this, "PrivacyLevel",
  "resource://gre/modules/sessionstore/PrivacyLevel.jsm");

/**
 * A module that provides methods to filter various kinds of data collected
 * from a tab by the current privacy level as set by the user.
 */
var PrivacyFilter = Object.freeze({
  /**
   * Filters the given (serialized) session storage |data| according to the
   * current privacy level and returns a new object containing only data that
   * we're allowed to store.
   *
   * @param data The session storage data as collected from a tab.
   * @return object
   */
  filterSessionStorageData(data) {
    let retval = {};

    for (let host of Object.keys(data)) {
      if (PrivacyLevel.check(host)) {
        retval[host] = data[host];
      }
    }

    return Object.keys(retval).length ? retval : null;
  },

  /**
   * Filters the given (serialized) form |data| according to the current
   * privacy level and returns a new object containing only data that we're
   * allowed to store.
   *
   * @param data The form data as collected from a tab.
   * @return object
   */
  filterFormData(data) {
    // If the given form data object has an associated URL that we are not
    // allowed to store data for, bail out. We explicitly discard data for any
    // children as well even if storing data for those frames would be allowed.
    if (data.url && !PrivacyLevel.check(data.url)) {
      return null;
    }

    let retval = {};

    for (let key of Object.keys(data)) {
      if (key === "children") {
        let recurse = child => this.filterFormData(child);
        let children = data.children.map(recurse).filter(child => child);

        if (children.length) {
          retval.children = children;
        }
      // Only copy keys other than "children" if we have a valid URL in
      // data.url and we thus passed the privacy level check.
      } else if (data.url) {
        retval[key] = data[key];
      }
    }

    return Object.keys(retval).length ? retval : null;
  },

  /**
   * Removes any private windows and tabs from a given browser state object.
   *
   * @param browserState (object)
   *        The browser state for which we remove any private windows and tabs.
   *        The given object will be modified.
   */
  filterPrivateWindowsAndTabs(browserState) {
    // Remove private opened windows.
    for (let i = browserState.windows.length - 1; i >= 0; i--) {
      let win = browserState.windows[i];

      if (win.isPrivate) {
        browserState.windows.splice(i, 1);

        if (browserState.selectedWindow >= i) {
          browserState.selectedWindow--;
        }
      } else {
        // Remove private tabs from all open non-private windows.
        this.filterPrivateTabs(win);
      }
    }

    // Remove private closed windows.
    browserState._closedWindows =
      browserState._closedWindows.filter(win => !win.isPrivate);

    // Remove private tabs from all remaining closed windows.
    browserState._closedWindows.forEach(win => this.filterPrivateTabs(win));
  },

  /**
   * Removes open private tabs from a given window state object.
   *
   * @param winState (object)
   *        The window state for which we remove any private tabs.
   *        The given object will be modified.
   */
  filterPrivateTabs(winState) {
    // Remove open private tabs.
    for (let i = winState.tabs.length - 1; i >= 0 ; i--) {
      let tab = winState.tabs[i];

      if (tab.isPrivate) {
        winState.tabs.splice(i, 1);

        if (winState.selected >= i) {
          winState.selected--;
        }
      }
    }

    // Note that closed private tabs are only stored for private windows.
    // There is no need to call this function for private windows as the
    // whole window state should just be discarded so we explicitly don't
    // try to remove closed private tabs as an optimization.
  }
});
PK
!<�)j��:chrome/browser/skin/classic/browser/tabbrowser/loading.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="960" height="16" fill="context-fill">
  <path d="M2 6a2.001 2.001 0 0 1 0 4 2.001 2.001 0 0 1 0-4zM18.392 6a2.001 2.001 0 0 1 0 4 2.001 2.001 0 0 1 0-4zM35.565 6a2.001 2.001 0 0 1 0 4 2.001 2.001 0 0 1 0-4zM53.024 6.049c1.198 0 2.17.874 2.17 1.951s-.972 1.951-2.17 1.951-2.17-.874-2.17-1.951.972-1.951 2.17-1.951zM70.33 6.099c1.295 0 2.346.852 2.346 1.901 0 1.05-1.05 1.901-2.345 1.901-1.295 0-2.346-.852-2.346-1.901 0-1.05 1.051-1.901 2.346-1.901zM87.423 6.149c1.391 0 2.52.83 2.52 1.851 0 1.022-1.129 1.851-2.52 1.851s-2.52-.83-2.52-1.851c0-1.022 1.129-1.851 2.52-1.851zM104.337 6.199c1.488 0 2.696.807 2.696 1.801 0 .994-1.208 1.801-2.696 1.801-1.488 0-2.696-.807-2.696-1.801 0-.994 1.208-1.801 2.696-1.801zM121.112 6.249c1.584 0 2.87.784 2.87 1.751 0 .967-1.286 1.751-2.87 1.751-1.585 0-2.872-.784-2.872-1.751 0-.967 1.286-1.751 2.87-1.751zM137.776 6.299c1.68 0 3.045.762 3.045 1.701 0 .939-1.364 1.701-3.045 1.701-1.681 0-3.046-.761-3.046-1.701 0-.939 1.365-1.701 3.046-1.701zM154.351 6.349c1.778 0 3.221.74 3.221 1.651s-1.443 1.651-3.22 1.651c-1.779 0-3.222-.74-3.222-1.651s1.443-1.651 3.22-1.651zM170.854 6.399c1.874 0 3.395.717 3.395 1.601s-1.521 1.601-3.395 1.601-3.396-.717-3.396-1.601 1.522-1.601 3.396-1.601zM187.294 6.344c1.769 0 3.205.742 3.205 1.656 0 .914-1.436 1.656-3.205 1.656-1.768 0-3.204-.742-3.204-1.656 0-.914 1.436-1.656 3.204-1.656zM203.683 6.287c1.657 0 3.003.767 3.003 1.713 0 .946-1.346 1.713-3.003 1.713-1.658 0-3.004-.767-3.004-1.713 0-.946 1.346-1.713 3.004-1.713zM220.025 6.23c1.547 0 2.803.793 2.803 1.77s-1.256 1.77-2.803 1.77-2.803-.792-2.803-1.77 1.256-1.77 2.803-1.77zM236.328 6.172c1.436 0 2.602.82 2.602 1.828 0 1.009-1.166 1.828-2.602 1.828s-2.602-.82-2.602-1.828c0-1.009 1.166-1.828 2.602-1.828zM252.596 6.115c1.325 0 2.401.845 2.401 1.885s-1.076 1.885-2.401 1.885c-1.326 0-2.402-.845-2.402-1.885s1.076-1.885 2.402-1.885zM268.832 6.057c1.214 0 2.2.87 2.2 1.943 0 1.072-.986 1.943-2.2 1.943-1.215 0-2.2-.87-2.2-1.943 0-1.072.985-1.943 2.2-1.943zM285.04 6a2.001 2.001 0 0 1 0 4 2.001 2.001 0 0 1 0-4zM301.222 6a2.001 2.001 0 0 1 0 4 2.001 2.001 0 0 1 0-4zM317.38 6a2.001 2.001 0 0 1 0 4 2.001 2.001 0 0 1 0-4zM333.517 6a2.001 2.001 0 0 1 0 4 2.001 2.001 0 0 1 0-4zM349.635 6a2.001 2.001 0 0 1 0 4 2.001 2.001 0 0 1 0-4zM365.734 6a2.001 2.001 0 0 1 0 4 2.001 2.001 0 0 1 0-4zM381.816 6a2.001 2.001 0 0 1 0 4 2.001 2.001 0 0 1 0-4zM397.883 6a2.001 2.001 0 0 1 0 4 2.001 2.001 0 0 1 0-4zM413.934 6a2.001 2.001 0 0 1 0 4 2.001 2.001 0 0 1 0-4zM429.972 6a2.001 2.001 0 0 1 0 4 2.001 2.001 0 0 1 0-4zM445.997 6a2.001 2.001 0 0 1 0 4 2.001 2.001 0 0 1 0-4zM462.01 6a2.001 2.001 0 0 1 0 4 2.001 2.001 0 0 1 0-4zM478.01 6a2.001 2.001 0 0 1 0 4 2.001 2.001 0 0 1 0-4zM494 6a2.001 2.001 0 0 1 0 4 2.001 2.001 0 0 1 0-4zM509.84 6a2.001 2.001 0 0 1 0 4 2.001 2.001 0 0 1 0-4zM525.344 6a2.001 2.001 0 0 1 0 4 2.001 2.001 0 0 1 0-4zM540.294 6.049c1.171 0 2.122.874 2.122 1.951s-.95 1.951-2.122 1.951c-1.171 0-2.122-.874-2.122-1.951s.95-1.951 2.122-1.951zM554.717 6.1c1.24 0 2.248.85 2.248 1.9 0 1.05-1.008 1.9-2.248 1.9-1.24 0-2.248-.85-2.248-1.9 0-1.05 1.008-1.9 2.248-1.9zM569.174 6.15c1.31 0 2.373.829 2.373 1.85 0 1.021-1.063 1.85-2.373 1.85-1.31 0-2.374-.828-2.374-1.85 0-1.021 1.063-1.85 2.373-1.85zM583.938 6.2c1.378 0 2.498.806 2.498 1.8s-1.12 1.8-2.498 1.8c-1.38 0-2.499-.806-2.499-1.8s1.12-1.8 2.499-1.8zM598.964 6.25c1.448 0 2.624.784 2.624 1.75s-1.176 1.75-2.624 1.75c-1.448 0-2.624-.784-2.624-1.75s1.176-1.75 2.624-1.75zM614.18 6.3c1.517 0 2.75.762 2.75 1.7s-1.234 1.7-2.75 1.7-2.75-.762-2.75-1.7 1.233-1.7 2.75-1.7zM629.534 6.35c1.587 0 2.875.74 2.875 1.65 0 .91-1.288 1.65-2.875 1.65-1.586 0-2.874-.74-2.874-1.65 0-.91 1.289-1.65 2.875-1.65zM644.992 6.4c1.656 0 3 .717 3 1.6 0 .883-1.344 1.6-3 1.6-1.655 0-3-.717-3-1.6 0-.883 1.345-1.6 3-1.6zM660.532 6.343c1.578 0 2.858.742 2.858 1.657s-1.28 1.657-2.858 1.657c-1.577 0-2.857-.742-2.857-1.657s1.28-1.657 2.857-1.657zM676.138 6.286c1.498 0 2.714.768 2.714 1.714s-1.216 1.714-2.714 1.714-2.714-.768-2.714-1.714 1.216-1.714 2.714-1.714zM691.798 6.229c1.419 0 2.571.793 2.571 1.771 0 .978-1.152 1.771-2.571 1.771-1.42 0-2.572-.793-2.572-1.771 0-.978 1.152-1.771 2.572-1.771zM707.503 6.171c1.34 0 2.428.82 2.428 1.829 0 1.01-1.088 1.829-2.428 1.829-1.34 0-2.429-.82-2.429-1.829 0-1.01 1.088-1.829 2.429-1.829zM723.247 6.114c1.261 0 2.285.845 2.285 1.886 0 1.04-1.024 1.886-2.285 1.886-1.262 0-2.287-.846-2.287-1.886s1.024-1.886 2.286-1.886zM739.024 6.057c1.183 0 2.143.87 2.143 1.943 0 1.072-.96 1.943-2.143 1.943-1.182 0-2.143-.87-2.143-1.943 0-1.072.96-1.943 2.143-1.943zM754.831 6a2.001 2.001 0 0 1 0 4 2.001 2.001 0 0 1 0-4zM770.665 6a2.001 2.001 0 0 1 0 4 2.001 2.001 0 0 1 0-4zM786.521 6a2.001 2.001 0 0 1 0 4 2.001 2.001 0 0 1 0-4zM802.399 6a2.001 2.001 0 0 1 0 4 2.001 2.001 0 0 1 0-4zM818.295 6a2.001 2.001 0 0 1 0 4 2.001 2.001 0 0 1 0-4zM834.21 6a2.001 2.001 0 0 1 0 4 2.001 2.001 0 0 1 0-4zM850.139 6a2.001 2.001 0 0 1 0 4 2.001 2.001 0 0 1 0-4zM866.083 6a2.001 2.001 0 0 1 0 4 2.001 2.001 0 0 1 0-4zM882.04 6a2.001 2.001 0 0 1 0 4 2.001 2.001 0 0 1 0-4zM898.01 6a2.001 2.001 0 0 1 0 4 2.001 2.001 0 0 1 0-4zM913.993 6a2.001 2.001 0 0 1 0 4 2.001 2.001 0 0 1 0-4zM929.985 6a2.001 2.001 0 0 1 0 4 2.001 2.001 0 0 1 0-4zM945.988 6a2.001 2.001 0 0 1 0 4 2.001 2.001 0 0 1 0-4zM962 6a2.001 2.001 0 0 1 0 4 2.001 2.001 0 0 1 0-4z"/>
</svg>
PK
!<�S؈��2chrome/browser/skin/classic/browser/readerMode.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M13 0H3a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zm0 13a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1h8a1 1 0 0 1 1 1zm-2.5-9h-5a.5.5 0 0 0 0 1h5a.5.5 0 0 0 0-1zm0 2h-5a.5.5 0 0 0 0 1h5a.5.5 0 1 0 0-1zm0 2h-5a.5.5 0 0 0 0 1h5a.5.5 0 1 0 0-1zm-3 2h-2a.5.5 0 0 0 0 1h2a.5.5 0 1 0 0-1z"/>
</svg>
PK
!<�+QLL@chrome/browser/skin/classic/browser/tabbrowser/loading-burst.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 viewBox="0 0 10 10" xmlns="http://www.w3.org/2000/svg">
  <circle cx="5" cy="5" r="5" fill="context-fill"/>
</svg>
PK
!<�h��3�36chrome/browser/skin/classic/browser/reload-to-stop.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="468" height="20" fill="context-fill" fill-opacity="context-fill-opacity">
  <path d="M10.414 10l5.293-5.293a1 1 0 0 0-1.414-1.414L9 8.586 3.707 3.293a1 1 0 0 0-1.414 1.414L7.586 10l-5.293 5.293a1 1 0 1 0 1.414 1.414L9 11.414l5.293 5.293a1 1 0 0 0 1.414-1.414z"/>
  <svg x="18">
    <path d="M10.414 10l5.293-5.293a1 1 0 0 0-1.414-1.414L9 8.586 3.707 3.293a1 1 0 0 0-1.414 1.414L7.586 10l-5.293 5.293a1 1 0 1 0 1.414 1.414L9 11.414l5.293 5.293a1 1 0 0 0 1.414-1.414z"/>
  </svg>
  <svg x="36">
    <path d="M10.414 10l5.293-5.293a1 1 0 0 0-1.414-1.414L9 8.586 3.707 3.293a1 1 0 0 0-1.414 1.414L7.586 10l-5.293 5.293a1 1 0 1 0 1.414 1.414L9 11.414l5.293 5.293a1 1 0 0 0 1.414-1.414z"/>
  </svg>
  <svg x="54">
    <path d="M10.414 10l5.293-5.293a1 1 0 0 0-1.414-1.414L9 8.586 3.707 3.293a1 1 0 0 0-1.414 1.414L7.586 10l-5.293 5.293a1 1 0 1 0 1.414 1.414L9 11.414l5.293 5.293a1 1 0 0 0 1.414-1.414z"/>
  </svg>
  <svg x="72">
    <defs>
      <mask id="a" mask-type="alpha" fill="#fff">
        <path d="M0 -2.906a2.907 2.907 0 0 1 2.906 2.906 2.907 2.907 0 0 1 -2.906 2.906 2.907 2.907 0 0 1 -2.906 -2.906 2.907 2.907 0 0 1 2.906 -2.906z" transform="translate(1.177 18.622) scale(6.7782)"/>
      </mask>
    </defs>
    <path d="M10.414 10l5.293-5.293a1 1 0 0 0-1.414-1.414L9 8.586 3.707 3.293a1 1 0 0 0-1.414 1.414L7.586 10l-5.293 5.293a1 1 0 1 0 1.414 1.414L9 11.414l5.293 5.293a1 1 0 0 0 1.414-1.414z" mask="url(#a)"/>
  </svg>
  <svg x="90">
    <defs>
      <mask id="b" mask-type="alpha" fill="#fff">
        <path d="M0 -2.906a2.907 2.907 0 0 1 2.906 2.906 2.907 2.907 0 0 1 -2.906 2.906 2.907 2.907 0 0 1 -2.906 -2.906 2.907 2.907 0 0 1 2.906 -2.906z" transform="translate(1.133 18.622) scale(6.49626)"/>
      </mask>
    </defs>
    <path d="M10.414 10l5.293-5.293a1 1 0 0 0-1.414-1.414L9 8.586 3.707 3.293a1 1 0 0 0-1.414 1.414L7.586 10l-5.293 5.293a1 1 0 1 0 1.414 1.414L9 11.414l5.293 5.293a1 1 0 0 0 1.414-1.414z" mask="url(#b)"/>
  </svg>
  <svg x="108">
    <defs>
      <mask id="c" mask-type="alpha" fill="#fff">
        <path d="M0 -2.906a2.907 2.907 0 0 1 2.906 2.906 2.907 2.907 0 0 1 -2.906 2.906 2.907 2.907 0 0 1 -2.906 -2.906 2.907 2.907 0 0 1 2.906 -2.906z" transform="translate(1.075 18.622) scale(6.1231)"/>
      </mask>
    </defs>
    <path d="M10.414 10l5.293-5.293a1 1 0 0 0-1.414-1.414L9 8.586 3.707 3.293a1 1 0 0 0-1.414 1.414L7.586 10l-5.293 5.293a1 1 0 1 0 1.414 1.414L9 11.414l5.293 5.293a1 1 0 0 0 1.414-1.414z" mask="url(#c)"/>
  </svg>
  <svg x="126">
    <defs>
      <mask id="d" mask-type="alpha" fill="#fff">
        <path d="M0 -2.906a2.907 2.907 0 0 1 2.906 2.906 2.907 2.907 0 0 1 -2.906 2.906 2.907 2.907 0 0 1 -2.906 -2.906 2.907 2.907 0 0 1 2.906 -2.906z" transform="translate(.996 18.622) scale(5.61712)"/>
      </mask>
    </defs>
    <path d="M10.414 10l5.293-5.293a1 1 0 0 0-1.414-1.414L9 8.586 3.707 3.293a1 1 0 0 0-1.414 1.414L7.586 10l-5.293 5.293a1 1 0 1 0 1.414 1.414L9 11.414l5.293 5.293a1 1 0 0 0 1.414-1.414z" mask="url(#d)"/>
  </svg>
  <svg x="144">
    <defs>
      <mask id="J" mask-type="alpha" fill="#fff">
        <path d="M0 -2.906a2.907 2.907 0 0 1 2.906 2.906 2.907 2.907 0 0 1 -2.906 2.906 2.907 2.907 0 0 1 -2.906 -2.906 2.907 2.907 0 0 1 2.906 -2.906z" transform="translate(.884 18.622) scale(4.89783)"/>
      </mask>
      <mask id="I" mask-type="alpha" fill="#fff">
        <path d="M0 -2.906a2.907 2.907 0 0 1 2.906 2.906 2.907 2.907 0 0 1 -2.906 2.906 2.907 2.907 0 0 1 -2.906 -2.906 2.907 2.907 0 0 1 2.906 -2.906z" transform="translate(1.097 1.705) scale(7.12503)"/>
      </mask>
    </defs>
    <g mask="url(#I)">
      <path d="M9 11.413l5.293 5.293a0.999 0.999 0 0 0 1.414 -0.024 1.001 1.001 0 0 0 0 -1.39l-5.293 -5.293"/>
      <path d="M10.973 10.558l-7.266 -7.265a1 1 0 0 0 -1.414 0.024 1.001 1.001 0 0 0 0 1.39l7.266 7.265"/>
    </g>
    <path d="M7.687 9.906l-5.394 5.387a1 1 0 0 0 -0.025 1.414 1 1 0 0 0 1.439 0l5.394 -5.387m1.212 -1.232l5.393 -5.381a0.999 0.999 0 0 0 -0.024 -1.414 1.001 1.001 0 0 0 -1.39 0l-5.393 5.381" mask="url(#J)"/>
  </svg>
  <svg x="162">
    <defs>
      <mask id="g" mask-type="alpha" fill="#fff">
        <path d="M0 -2.906a2.907 2.907 0 0 1 2.906 2.906 2.907 2.907 0 0 1 -2.906 2.906 2.907 2.907 0 0 1 -2.906 -2.906 2.907 2.907 0 0 1 2.906 -2.906z" transform="translate(.71 18.622) scale(3.7902)"/>
      </mask>
      <mask id="f" mask-type="alpha" fill="#fff">
        <path d="M0 -2.906a2.907 2.907 0 0 1 2.906 2.906 2.907 2.907 0 0 1 -2.906 2.906 2.907 2.907 0 0 1 -2.906 -2.906 2.907 2.907 0 0 1 2.906 -2.906z" transform="translate(1.07 1.705) scale(6.95173)"/>
      </mask>
    </defs>
    <g mask="url(#f)">
      <path d="M9 11.413l5.293 5.293a0.999 0.999 0 0 0 1.414 -0.024 1.001 1.001 0 0 0 0 -1.39l-5.293 -5.293"/>
      <path d="M10.973 10.558l-7.266 -7.265a1 1 0 0 0 -1.414 0.024 1.001 1.001 0 0 0 0 1.39l7.266 7.265"/>
    </g>
    <path d="M7.687 9.906l-5.394 5.387a1 1 0 0 0 -0.025 1.414 1 1 0 0 0 1.439 0l5.394 -5.387m1.212 -1.232l5.393 -5.381a0.999 0.999 0 0 0 -0.024 -1.414 1.001 1.001 0 0 0 -1.39 0l-5.393 5.381" mask="url(#g)"/>
  </svg>
  <svg x="180">
    <defs>
      <mask id="i" mask-type="alpha" fill="#fff">
        <path d="M0 -2.906a2.907 2.907 0 0 1 2.906 2.906 2.907 2.907 0 0 1 -2.906 2.906 2.907 2.907 0 0 1 -2.906 -2.906 2.907 2.907 0 0 1 2.906 -2.906z" transform="translate(.454 18.622) scale(2.13915)"/>
      </mask>
      <mask id="h" mask-type="alpha" fill="#fff">
        <path d="M0 -2.906a2.907 2.907 0 0 1 2.906 2.906 2.907 2.907 0 0 1 -2.906 2.906 2.907 2.907 0 0 1 -2.906 -2.906 2.907 2.907 0 0 1 2.906 -2.906z" transform="translate(1.034 1.705) scale(6.72397)"/>
      </mask>
    </defs>
    <g mask="url(#h)">
      <path d="M9 11.413l5.293 5.293a0.999 0.999 0 0 0 1.414 -0.024 1.001 1.001 0 0 0 0 -1.39l-5.293 -5.293"/>
      <path d="M10.973 10.558l-7.266 -7.265a1 1 0 0 0 -1.414 0.024 1.001 1.001 0 0 0 0 1.39l7.266 7.265"/>
    </g>
    <path d="M7.687 9.906l-5.394 5.387a1 1 0 0 0 -0.025 1.414 1 1 0 0 0 1.439 0l5.394 -5.387m1.212 -1.232l5.393 -5.381a0.999 0.999 0 0 0 -0.024 -1.414 1.001 1.001 0 0 0 -1.39 0l-5.393 5.381" mask="url(#i)"/>
  </svg>
  <svg x="198">
    <defs>
      <mask id="j" mask-type="alpha" fill="#fff">
        <path d="M0 -2.906a2.907 2.907 0 0 1 2.906 2.906 2.907 2.907 0 0 1 -2.906 2.906 2.907 2.907 0 0 1 -2.906 -2.906 2.907 2.907 0 0 1 2.906 -2.906z" transform="translate(.987 1.705) scale(6.42492)"/>
      </mask>
    </defs>
    <g mask="url(#j)">
      <path d="M9 11.413l5.293 5.293a0.999 0.999 0 0 0 1.414 -0.024 1.001 1.001 0 0 0 0 -1.39l-5.293 -5.293"/>
      <path d="M10.973 10.558l-7.266 -7.265a1 1 0 0 0 -1.414 0.024 1.001 1.001 0 0 0 0 1.39l7.266 7.265"/>
    </g>
  </svg>
  <svg x="216">
    <defs>
      <mask id="k" mask-type="alpha" fill="#fff">
        <path d="M0 -2.906a2.907 2.907 0 0 1 2.906 2.906 2.907 2.907 0 0 1 -2.906 2.906 2.907 2.907 0 0 1 -2.906 -2.906 2.907 2.907 0 0 1 2.906 -2.906z" transform="translate(.925 1.705) scale(6.02764)"/>
      </mask>
    </defs>
    <g mask="url(#k)">
      <path d="M9 11.413l5.293 5.293a0.999 0.999 0 0 0 1.414 -0.024 1.001 1.001 0 0 0 0 -1.39l-5.293 -5.293"/>
      <path d="M10.973 10.558l-7.266 -7.265a1 1 0 0 0 -1.414 0.024 1.001 1.001 0 0 0 0 1.39l7.266 7.265"/>
    </g>
  </svg>
  <svg x="234">
    <defs>
      <mask id="l" mask-type="alpha" fill="#fff">
        <path d="M0 -2.906a2.907 2.907 0 0 1 2.906 2.906 2.907 2.907 0 0 1 -2.906 2.906 2.907 2.907 0 0 1 -2.906 -2.906 2.907 2.907 0 0 1 2.906 -2.906z" transform="translate(.84 1.705) scale(5.48475)"/>
      </mask>
    </defs>
    <g mask="url(#l)">
      <path d="M9 11.413l5.293 5.293a0.999 0.999 0 0 0 1.414 -0.024 1.001 1.001 0 0 0 0 -1.39l-5.293 -5.293"/>
      <path d="M10.973 10.558l-7.266 -7.265a1 1 0 0 0 -1.414 0.024 1.001 1.001 0 0 0 0 1.39l7.266 7.265"/>
    </g>
  </svg>
  <svg x="252">
    <defs>
      <mask id="m" mask-type="alpha" fill="#fff">
        <path d="M0 -2.906a2.907 2.907 0 0 1 2.906 2.906 2.907 2.907 0 0 1 -2.906 2.906 2.907 2.907 0 0 1 -2.906 -2.906 2.907 2.907 0 0 1 2.906 -2.906z" transform="translate(.718 1.705) scale(4.70182)"/>
      </mask>
    </defs>
    <g mask="url(#m)">
      <path d="M9 11.413l5.293 5.293a0.999 0.999 0 0 0 1.414 -0.024 1.001 1.001 0 0 0 0 -1.39l-5.293 -5.293"/>
      <path d="M10.973 10.558l-7.266 -7.265a1 1 0 0 0 -1.414 0.024 1.001 1.001 0 0 0 0 1.39l7.266 7.265"/>
    </g>
  </svg>
  <svg x="270">
    <defs>
      <mask id="n" mask-type="alpha" fill="#fff">
        <path d="M0 -2.906a2.907 2.907 0 0 1 2.906 2.906 2.907 2.907 0 0 1 -2.906 2.906 2.907 2.907 0 0 1 -2.906 -2.906 2.907 2.907 0 0 1 2.906 -2.906z" transform="translate(.527 1.705) scale(3.47447)"/>
      </mask>
    </defs>
    <path d="M10.973 10.558l-7.266 -7.265a1 1 0 0 0 -1.414 0.024 1.001 1.001 0 0 0 0 1.39l7.266 7.265" mask="url(#n)"/>
  </svg>
  <svg x="288">
    <defs>
      <mask id="o" mask-type="alpha" fill="#fff">
        <path d="M0 -2.906a2.907 2.907 0 0 1 2.906 2.906 2.907 2.907 0 0 1 -2.906 2.906 2.907 2.907 0 0 1 -2.906 -2.906 2.907 2.907 0 0 1 2.906 -2.906z" transform="translate(.267 1.705) scale(1.80625)"/>
      </mask>
    </defs>
    <path d="M10.973 10.558l-7.266 -7.265a1 1 0 0 0 -1.414 0.024 1.001 1.001 0 0 0 0 1.39l7.266 7.265" mask="url(#o)"/>
  </svg>
  <svg x="306">
    <path d="M6.148 16.38c3.527 1.576 7.664 -0.007 9.24 -3.533 1.576 -3.528 -0.005 -7.664 -3.532 -9.24a6.997 6.997 0 0 0 -6.878 0.664 6.954 6.954 0 0 0 -2.874 4.522 1 1 0 1 0 1.97 0.345 5.02 5.02 0 1 1 2.352 5.113" opacity=".066"/>
  </svg>
  <svg x="324">
    <path d="M4.934 13.615l0.584 0.08a0.231 0.231 0 0 0 0.062 -0.458l-1.144 -0.155a0.231 0.231 0 0 0 -0.26 0.198l-0.155 1.144a0.231 0.231 0 0 0 0.457 0.062l0.075 -0.554" opacity=".231"/>
    <path d="M3.416 14.202c2.324 3.086 6.71 3.704 9.796 1.38 3.086 -2.323 3.705 -6.708 1.381 -9.795a6.997 6.997 0 0 0 -6.34 -2.747 6.954 6.954 0 0 0 -4.703 2.566 1 1 0 1 0 1.557 1.255 5.02 5.02 0 1 1 -0.417 5.612" opacity=".186"/>
  </svg>
  <svg x="342">
    <path d="M3.778 11.038l0.737 0.506a0.35 0.35 0 0 0 0.397 -0.578l-1.444 -0.993a0.35 0.35 0 0 0 -0.488 0.09l-0.993 1.445a0.35 0.35 0 0 0 0.578 0.397l0.48 -0.699" opacity=".351"/>
    <path d="M2.08 10.974c0.54 3.826 4.079 6.49 7.904 5.95s6.489 -4.078 5.95 -7.903a6.997 6.997 0 0 0 -4.219 -5.473 6.954 6.954 0 0 0 -5.358 -0.03 1 1 0 1 0 0.755 1.852 5.02 5.02 0 1 1 -3.081 4.71" opacity=".322"/>
  </svg>
  <svg x="360">
    <path d="M4.073 8.272l0.576 1.051a0.47 0.47 0 0 0 0.825 -0.451l-1.129 -2.062a0.47 0.47 0 0 0 -0.638 -0.187l-2.062 1.129a0.47 0.47 0 0 0 0.451 0.825l0.998 -0.546" opacity=".47"/>
    <path d="M2.473 7.503c-1.38 3.61 0.429 7.653 4.037 9.032 3.608 1.38 7.652 -0.427 9.031 -4.036a6.997 6.997 0 0 0 -1.042 -6.83 6.954 6.954 0 0 0 -4.674 -2.62 1 1 0 1 0 -0.236 1.986 5.02 5.02 0 1 1 -4.976 2.63" opacity=".464"/>
  </svg>
  <svg x="378">
    <path d="M5.703 6.065l0.05 1.503a0.59 0.59 0 0 0 1.179 -0.04l-0.098 -2.946a0.59 0.59 0 0 0 -0.609 -0.57l-2.947 0.097a0.59 0.59 0 0 0 0.04 1.18l1.424 -0.048" opacity=".59"/>
    <path d="M4.497 4.656c-2.954 2.49 -3.329 6.904 -0.839 9.857 2.49 2.954 6.903 3.33 9.857 0.84a6.997 6.997 0 0 0 2.393 -6.482 6.954 6.954 0 0 0 -2.821 -4.555 1 1 0 1 0 -1.167 1.624 5.02 5.02 0 1 1 -5.627 -0.107" opacity=".607"/>
  </svg>
  <svg x="396">
    <path d="M8.198 5.001l-0.761 1.64a0.71 0.71 0 0 0 1.287 0.597l1.493 -3.217a0.71 0.71 0 0 0 -0.345 -0.942l-3.217 -1.493a0.71 0.71 0 0 0 -0.597 1.287l1.556 0.722" opacity=".709"/>
    <path d="M7.646 3.145c-3.79 0.749 -6.254 4.43 -5.505 8.219 0.75 3.79 4.429 6.254 8.219 5.505a6.997 6.997 0 0 0 5.232 -4.514 6.954 6.954 0 0 0 -0.265 -5.35 1 1 0 1 0 -1.807 0.856 5.02 5.02 0 1 1 -4.872 -2.817" opacity=".747"/>
  </svg>
  <svg x="414">
    <path d="M10.865 5.35l-1.658 1.31a0.83 0.83 0 0 0 1.027 1.3l3.253 -2.57a0.83 0.83 0 0 0 0.136 -1.164l-2.57 -3.252a0.83 0.83 0 0 0 -1.3 1.028l1.243 1.573" opacity=".829"/>
    <path d="M11.133 3.346c-3.68 -1.18 -7.617 0.849 -8.795 4.527 -1.179 3.68 0.848 7.617 4.527 8.796a6.997 6.997 0 0 0 6.763 -1.417 6.954 6.954 0 0 0 2.358 -4.811 1 1 0 1 0 -1.996 -0.126 5.02 5.02 0 1 1 -2.9 -4.823" opacity=".877"/>
  </svg>
  <svg x="432">
    <path d="M12.97 6.998l-2.37 0.482a0.949 0.949 0 0 0 0.38 1.86l4.647 -0.948a0.949 0.949 0 0 0 0.74 -1.118l-0.947 -4.648a0.949 0.949 0 0 0 -1.859 0.379l0.458 2.247" opacity=".949"/>
    <path d="M14.087 5.21c-2.649 -2.813 -7.076 -2.944 -9.887 -0.295 -2.813 2.649 -2.945 7.075 -0.296 9.888a6.997 6.997 0 0 0 6.604 2.033 6.954 6.954 0 0 0 4.392 -3.069 1 1 0 1 0 -1.685 -1.076 5.02 5.02 0 1 1 -0.204 -5.624" opacity=".981"/>
  </svg>
  <svg x="450">
    <path d="M13.548 7.997l-2.549 0.001a1 1 0 0 0 0.001 2l5 -0.003a1 1 0 0 0 0.999 -1l-0.003 -5a1 1 0 0 0 -2 0.002l0.002 2.417"/>
    <path d="M14.998 6.414c-1.983 -3.316 -6.279 -4.395 -9.594 -2.412 -3.316 1.983 -4.396 6.278 -2.413 9.594a6.997 6.997 0 0 0 6.013 3.404 6.954 6.954 0 0 0 4.95 -2.053 1 1 0 1 0 -1.415 -1.413 5.02 5.02 0 1 1 1.01 -5.537"/>
  </svg>
</svg>
PK
!<4\��%localization/en-US/branding/brand.ftl# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

## Firefox Brand
##
## Firefox must be treated as a brand, and kept in English.
## It cannot be:
## - Declined to adapt to grammatical case.
## - Transliterated.
## - Translated.
##
## Reference: https://www.mozilla.org/styleguide/communications/translation/

-brand-short-name = Firefox
PK
!<�xt�gg2localization/en-US/browser/branding/sync-brand.ftl# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

-sync-brand-short-name = Sync

# “Sync” can be localized, “Firefox” must be treated as a brand,
# and kept in English.
-sync-brand-name = Firefox Sync
PK
!<��''6localization/en-US/browser/preferences/preferences.ftl# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

do-not-track-description = Send websites a “Do Not Track” signal that you don’t want to be tracked
do-not-track-learn-more = Learn more
do-not-track-option-default =
    .label = Only when using Tracking Protection
do-not-track-option-always =
    .label = Always

pref-page =
    .title =
        { PLATFORM() ->
            [windows] Options
           *[other] Preferences
        }

# This is used to determine the width of the search field in about:preferences,
# in order to make the entire placeholder string visible
#
# Notice: The value of the `.style` attribute is a CSS string, and the `width`
# is the name of the CSS property. It is intended only to adjust the element's width.
# Do not translate.
search-input =
    .style = width: 15.4em

policies-notice =
    { PLATFORM() ->
        [windows] Your organization has disabled the ability to change some options.
       *[other] Your organization has disabled the ability to change some preferences.
    }

pane-general-title = General
category-general =
    .tooltiptext = { pane-general-title }

pane-search-title = Search
category-search =
    .tooltiptext = { pane-search-title }

pane-privacy-title = Privacy & Security
category-privacy =
    .tooltiptext = { pane-privacy-title }

# The word "account" can be translated, do not translate or transliterate "Firefox".
pane-sync-title = Firefox Account
category-sync =
    .tooltiptext = { pane-sync-title }

help-button-label = { -brand-short-name } Support

focus-search =
    .key = f

close-button =
    .aria-label = Close

## Browser Restart Dialog

feature-enable-requires-restart = { -brand-short-name } must restart to enable this feature.
feature-disable-requires-restart = { -brand-short-name } must restart to disable this feature.
should-restart-title = Restart { -brand-short-name }
should-restart-ok = Restart { -brand-short-name } now
cancel-no-restart-button = Cancel
restart-later = Restart Later

## General Section

startup-header = Startup

# { -brand-short-name } will be 'Firefox Developer Edition',
# since this setting is only exposed in Firefox Developer Edition
separate-profile-mode =
    .label = Allow { -brand-short-name } and Firefox to run at the same time
use-firefox-sync = Tip: This uses separate profiles. Use { -sync-brand-short-name } to share data between them.
get-started-not-logged-in = Sign in to { -sync-brand-short-name }…
get-started-configured = Open { -sync-brand-short-name } preferences

always-check-default =
    .label = Always check if { -brand-short-name } is your default browser
    .accesskey = y

is-default = { -brand-short-name } is currently your default browser
is-not-default = { -brand-short-name } is not your default browser

set-as-my-default-browser =
    .label = Make Default…
    .accesskey = D

startup-page = When { -brand-short-name } starts
    .accesskey = s

startup-user-homepage =
    .label = Show your home page
startup-blank-page =
    .label = Show a blank page
startup-prev-session =
    .label = Show your windows and tabs from last time

disable-extension =
    .label = Disable Extension

home-page-header = Home page

# This string has a special case for '1' and [other] (default). If necessary for
# your language, you can add {$tabCount} to your translations and use the
# standard CLDR forms, or only use the form for [other] if both strings should
# be identical.
use-current-pages =
    .label =
        { $tabCount ->
            [1] Use Current Page
           *[other] Use Current Pages
        }
    .accesskey = C

choose-bookmark =
    .label = Use Bookmark…
    .accesskey = B

restore-default =
    .label = Restore to Default
    .accesskey = R

tabs-group-header = Tabs

ctrl-tab-recently-used-order =
    .label = Ctrl+Tab cycles through tabs in recently used order
    .accesskey = T

open-new-link-as-tabs =
    .label = Open links in tabs instead of new windows
    .accesskey = w

warn-on-close-multiple-tabs =
    .label = Warn you when closing multiple tabs
    .accesskey = m

warn-on-open-many-tabs =
    .label = Warn you when opening multiple tabs might slow down { -brand-short-name }
    .accesskey = d

switch-links-to-new-tabs =
    .label = When you open a link in a new tab, switch to it immediately
    .accesskey = h

show-tabs-in-taskbar =
    .label = Show tab previews in the Windows taskbar
    .accesskey = k

browser-containers-enabled =
    .label = Enable Container Tabs
    .accesskey = n

browser-containers-learn-more = Learn more

browser-containers-settings =
    .label = Settings…
    .accesskey = i

containers-disable-alert-title = Close All Container Tabs?
containers-disable-alert-desc =
    { $tabCount ->
        [one] If you disable Container Tabs now, { $tabCount } container tab will be closed. Are you sure you want to disable Container Tabs?
       *[other] If you disable Container Tabs now, { $tabCount } container tabs will be closed. Are you sure you want to disable Container Tabs?
    }

containers-disable-alert-ok-button =
    { $tabCount ->
        [one] Close { $tabCount } Container Tab
       *[other] Close { $tabCount } Container Tabs
    }
containers-disable-alert-cancel-button = Keep enabled

## General Section - Language & Appearance

language-and-appearance-header = Language and Appearance

fonts-and-colors-header = Fonts & Colors

default-font = Default font
    .accesskey = D
default-font-size = Size
    .accesskey = S

advanced-fonts =
    .label = Advanced…
    .accesskey = A

colors-settings =
    .label = Colors…
    .accesskey = C

language-header = Language

choose-language-description = Choose your preferred language for displaying pages

choose-button =
    .label = Choose…
    .accesskey = o

translate-web-pages =
    .label = Translate web content
    .accesskey = T

translate-exceptions =
    .label = Exceptions…
    .accesskey = x

check-user-spelling =
    .label = Check your spelling as you type
    .accesskey = t

## General Section - Files and Applications

files-and-applications-title = Files and Applications

download-header = Downloads

download-save-to =
    .label = Save files to
    .accesskey = v

download-choose-folder =
    .label =
        { PLATFORM() ->
            [macos] Choose…
           *[other] Browse…
        }
    .accesskey =
        { PLATFORM() ->
            [macos] e
           *[other] o
        }

download-always-ask-where =
    .label = Always ask you where to save files
    .accesskey = A

applications-header = Applications

applications-description = Choose how { -brand-short-name } handles the files you download from the web or the applications you use while browsing.

applications-filter =
    .placeholder = Search file types or applications

applications-type-column =
    .label = Content Type
    .accesskey = T

applications-action-column =
    .label = Action
    .accesskey = A

drm-content-header = Digital Rights Management (DRM) Content

play-drm-content =
    .label = Play DRM-controlled content
    .accesskey = P

play-drm-content-learn-more = Learn more

update-application-title = { -brand-short-name } Updates

update-application-description = Keep { -brand-short-name } up to date for the best performance, stability, and security.

update-application-info = Version { $version } <a>What's new</a>

update-history =
    .label = Show Update History…
    .accesskey = p

update-application-allow-description = Allow { -brand-short-name } to

update-application-auto =
    .label = Automatically install updates (recommended)
    .accesskey = A

update-application-check-choose =
    .label = Check for updates but let you choose to install them
    .accesskey = C

update-application-manual =
    .label = Never check for updates (not recommended)
    .accesskey = N

update-application-use-service =
    .label = Use a background service to install updates
    .accesskey = b

update-enable-search-update =
    .label = Automatically update search engines
    .accesskey = e

## General Section - Performance

performance-title = Performance

performance-use-recommended-settings-checkbox =
    .label = Use recommended performance settings
    .accesskey = U

performance-use-recommended-settings-desc = These settings are tailored to your computer’s hardware and operating system.

performance-settings-learn-more = Learn more

performance-allow-hw-accel =
    .label = Use hardware acceleration when available
    .accesskey = r

performance-limit-content-process-option = Content process limit
    .accesskey = l

performance-limit-content-process-enabled-desc = Additional content processes can improve performance when using multiple tabs, but will also use more memory.
performance-limit-content-process-disabled-desc = Modifying the number of content processes is only possible with multiprocess { -brand-short-name }. <a>Learn how to check if multiprocess is enabled</a>

# Variables:
#   $num - default value of the `dom.ipc.processCount` pref.
performance-default-content-process-count =
    .label = { $num } (default)

## General Section - Browsing

browsing-title = Browsing

browsing-use-autoscroll =
    .label = Use autoscrolling
    .accesskey = a

browsing-use-smooth-scrolling =
    .label = Use smooth scrolling
    .accesskey = m

browsing-use-onscreen-keyboard =
    .label = Show a touch keyboard when necessary
    .accesskey = c

browsing-use-cursor-navigation =
    .label = Always use the cursor keys to navigate within pages
    .accesskey = k

browsing-search-on-start-typing =
    .label = Search for text when you start typing
    .accesskey = x

## General Section - Proxy

network-proxy-title = Network Proxy

network-proxy-connection-learn-more = Learn More

network-proxy-connection-settings =
    .label = Settings…
    .accesskey = e
PK
!<X�"�\9\9components/FeedConverter.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/debug.js");
ChromeUtils.import("resource://gre/modules/Services.jsm");

function LOG(str) {
  dump("*** " + str + "\n");
}

const FS_CONTRACTID = "@mozilla.org/browser/feeds/result-service;1";
const FPH_CONTRACTID = "@mozilla.org/network/protocol;1?name=feed";
const PCPH_CONTRACTID = "@mozilla.org/network/protocol;1?name=pcast";

const TYPE_MAYBE_FEED = "application/vnd.mozilla.maybe.feed";
const TYPE_MAYBE_VIDEO_FEED = "application/vnd.mozilla.maybe.video.feed";
const TYPE_MAYBE_AUDIO_FEED = "application/vnd.mozilla.maybe.audio.feed";
const TYPE_ANY = "*/*";

const PREF_SELECTED_APP = "browser.feeds.handlers.application";
const PREF_SELECTED_WEB = "browser.feeds.handlers.webservice";
const PREF_SELECTED_ACTION = "browser.feeds.handler";
const PREF_SELECTED_READER = "browser.feeds.handler.default";

const PREF_VIDEO_SELECTED_APP = "browser.videoFeeds.handlers.application";
const PREF_VIDEO_SELECTED_WEB = "browser.videoFeeds.handlers.webservice";
const PREF_VIDEO_SELECTED_ACTION = "browser.videoFeeds.handler";
const PREF_VIDEO_SELECTED_READER = "browser.videoFeeds.handler.default";

const PREF_AUDIO_SELECTED_APP = "browser.audioFeeds.handlers.application";
const PREF_AUDIO_SELECTED_WEB = "browser.audioFeeds.handlers.webservice";
const PREF_AUDIO_SELECTED_ACTION = "browser.audioFeeds.handler";
const PREF_AUDIO_SELECTED_READER = "browser.audioFeeds.handler.default";

function getPrefAppForType(t) {
  switch (t) {
    case Ci.nsIFeed.TYPE_VIDEO:
      return PREF_VIDEO_SELECTED_APP;

    case Ci.nsIFeed.TYPE_AUDIO:
      return PREF_AUDIO_SELECTED_APP;

    default:
      return PREF_SELECTED_APP;
  }
}

function getPrefWebForType(t) {
  switch (t) {
    case Ci.nsIFeed.TYPE_VIDEO:
      return PREF_VIDEO_SELECTED_WEB;

    case Ci.nsIFeed.TYPE_AUDIO:
      return PREF_AUDIO_SELECTED_WEB;

    default:
      return PREF_SELECTED_WEB;
  }
}

function getPrefActionForType(t) {
  switch (t) {
    case Ci.nsIFeed.TYPE_VIDEO:
      return PREF_VIDEO_SELECTED_ACTION;

    case Ci.nsIFeed.TYPE_AUDIO:
      return PREF_AUDIO_SELECTED_ACTION;

    default:
      return PREF_SELECTED_ACTION;
  }
}

function getPrefReaderForType(t) {
  switch (t) {
    case Ci.nsIFeed.TYPE_VIDEO:
      return PREF_VIDEO_SELECTED_READER;

    case Ci.nsIFeed.TYPE_AUDIO:
      return PREF_AUDIO_SELECTED_READER;

    default:
      return PREF_SELECTED_READER;
  }
}

function FeedConverter() {
}
FeedConverter.prototype = {
  classID: Components.ID("{229fa115-9412-4d32-baf3-2fc407f76fb1}"),

  /**
   * This is the downloaded text data for the feed.
   */
  _data: null,

  /**
   * This is the object listening to the conversion, which is ultimately the
   * docshell for the load.
   */
  _listener: null,

  /**
   * Records if the feed was sniffed
   */
  _sniffed: false,

  /**
   * See nsIStreamConverter.idl
   */
  convert(sourceStream, sourceType, destinationType,
          context) {
    throw Cr.NS_ERROR_NOT_IMPLEMENTED;
  },

  /**
   * See nsIStreamConverter.idl
   */
  asyncConvertData(sourceType, destinationType,
                   listener, context) {
    this._listener = listener;
  },

  /**
   * Whether or not the preview page is being forced.
   */
  _forcePreviewPage: false,

  /**
   * Release our references to various things once we're done using them.
   */
  _releaseHandles() {
    this._listener = null;
    this._request = null;
    this._processor = null;
  },

  /**
   * See nsIFeedResultListener.idl
   */
  handleResult(result) {
    // Feeds come in various content types, which our feed sniffer coerces to
    // the maybe.feed type. However, feeds are used as a transport for
    // different data types, e.g. news/blogs (traditional feed), video/audio
    // (podcasts) and photos (photocasts, photostreams). Each of these is
    // different in that there's a different class of application suitable for
    // handling feeds of that type, but without a content-type differentiation
    // it is difficult for us to disambiguate.
    //
    // The other problem is that if the user specifies an auto-action handler
    // for one feed application, the fact that the content type is shared means
    // that all other applications will auto-load with that handler too,
    // regardless of the content-type.
    //
    // This means that content-type alone is not enough to determine whether
    // or not a feed should be auto-handled. This means that for feeds we need
    // to always use this stream converter, even when an auto-action is
    // specified, not the basic one provided by WebContentConverter. This
    // converter needs to consume all of the data and parse it, and based on
    // that determination make a judgment about type.
    //
    // Since there are no content types for this content, and I'm not going to
    // invent any, the upshot is that while a user can set an auto-handler for
    // generic feed content, the system will prevent them from setting an auto-
    // handler for other stream types. In those cases, the user will always see
    // the preview page and have to select a handler. We can guess and show
    // a client handler, but will not be able to show web handlers for those
    // types.
    //
    // If this is just a feed, not some kind of specialized application, then
    // auto-handlers can be set and we should obey them.
    try {
      let feedService =
          Cc["@mozilla.org/browser/feeds/result-service;1"].
          getService(Ci.nsIFeedResultService);
      if (!this._forcePreviewPage && result.doc) {
        let feed = result.doc.QueryInterface(Ci.nsIFeed);
        let handler = Services.prefs.getCharPref(getPrefActionForType(feed.type), "ask");

        if (handler != "ask") {
          if (handler == "reader")
            handler = Services.prefs.getCharPref(getPrefReaderForType(feed.type), "bookmarks");
          switch (handler) {
            case "web":
              let wccr =
                  Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"].
                  getService(Ci.nsIWebContentConverterService);
              if ((feed.type == Ci.nsIFeed.TYPE_FEED &&
                   wccr.getAutoHandler(TYPE_MAYBE_FEED)) ||
                  (feed.type == Ci.nsIFeed.TYPE_VIDEO &&
                   wccr.getAutoHandler(TYPE_MAYBE_VIDEO_FEED)) ||
                  (feed.type == Ci.nsIFeed.TYPE_AUDIO &&
                   wccr.getAutoHandler(TYPE_MAYBE_AUDIO_FEED))) {
                wccr.loadPreferredHandler(this._request);
                return;
              }
              break;

            default:
              LOG("unexpected handler: " + handler);
              // fall through -- let feed service handle error
            case "bookmarks":
            case "client":
            case "default":
              try {
                let title = feed.title ? feed.title.plainText() : "";
                let desc = feed.subtitle ? feed.subtitle.plainText() : "";
                feedService.addToClientReader(result.uri.spec, title, desc, feed.type, handler);
                return;
              } catch (ex) { /* fallback to preview mode */ }
          }
        }
      }

      let chromeChannel;

      // handling a redirect, hence forwarding the loadInfo from the old channel
      // to the newchannel.
      let oldChannel = this._request.QueryInterface(Ci.nsIChannel);
      let loadInfo = oldChannel.loadInfo;

      // If there was no automatic handler, or this was a podcast,
      // photostream or some other kind of application, show the preview page
      // if the parser returned a document.
      if (result.doc) {

        // Store the result in the result service so that the display
        // page can access it.
        feedService.addFeedResult(result);

        // Now load the actual XUL document.
        let aboutFeedsURI = Services.io.newURI("about:feeds");
        chromeChannel = Services.io.newChannelFromURIWithLoadInfo(aboutFeedsURI, loadInfo);
        chromeChannel.originalURI = result.uri;

        // carry the origin attributes from the channel that loaded the feed.
        chromeChannel.owner =
          Services.scriptSecurityManager.createCodebasePrincipal(aboutFeedsURI,
                                                                 loadInfo.originAttributes);
      } else {
        chromeChannel = Services.io.newChannelFromURIWithLoadInfo(result.uri, loadInfo);
      }

      chromeChannel.loadGroup = this._request.loadGroup;
      chromeChannel.asyncOpen2(this._listener);
    } finally {
      this._releaseHandles();
    }
  },

  /**
   * See nsIStreamListener.idl
   */
  onDataAvailable(request, context, inputStream,
                  sourceOffset, count) {
    if (this._processor)
      this._processor.onDataAvailable(request, context, inputStream,
                                      sourceOffset, count);
  },

  /**
   * See nsIRequestObserver.idl
   */
  onStartRequest(request, context) {
    let channel = request.QueryInterface(Ci.nsIChannel);

    // Check for a header that tells us there was no sniffing
    // The value doesn't matter.
    try {
      let httpChannel = channel.QueryInterface(Ci.nsIHttpChannel);
      // Make sure to check requestSucceeded before the potentially-throwing
      // getResponseHeader.
      if (!httpChannel.requestSucceeded) {
        // Just give up, but don't forget to cancel the channel first!
        request.cancel(Cr.NS_BINDING_ABORTED);
        return;
      }

      // Note: this throws if the header is not set.
      httpChannel.getResponseHeader("X-Moz-Is-Feed");
    } catch (ex) {
      this._sniffed = true;
    }

    this._request = request;

    // Save and reset the forced state bit early, in case there's some kind of
    // error.
    let feedService =
        Cc["@mozilla.org/browser/feeds/result-service;1"].
        getService(Ci.nsIFeedResultService);
    this._forcePreviewPage = feedService.forcePreviewPage;
    feedService.forcePreviewPage = false;

    // Parse feed data as it comes in
    this._processor =
        Cc["@mozilla.org/feed-processor;1"].
        createInstance(Ci.nsIFeedProcessor);
    this._processor.listener = this;
    this._processor.parseAsync(null, channel.URI);

    this._processor.onStartRequest(request, context);
  },

  /**
   * See nsIRequestObserver.idl
   */
  onStopRequest(request, context, status) {
    if (this._processor)
      this._processor.onStopRequest(request, context, status);
  },

  /**
   * See nsISupports.idl
   */
  QueryInterface(iid) {
    if (iid.equals(Ci.nsIFeedResultListener) ||
        iid.equals(Ci.nsIStreamConverter) ||
        iid.equals(Ci.nsIStreamListener) ||
        iid.equals(Ci.nsIRequestObserver) ||
        iid.equals(Ci.nsISupports))
      return this;
    throw Cr.NS_ERROR_NO_INTERFACE;
  },
};

/**
 * Keeps parsed FeedResults around for use elsewhere in the UI after the stream
 * converter completes.
 */
function FeedResultService() {
}

FeedResultService.prototype = {
  classID: Components.ID("{2376201c-bbc6-472f-9b62-7548040a61c6}"),

  /**
   * A URI spec -> [nsIFeedResult] hash. We have to keep a list as the
   * value in case the same URI is requested concurrently.
   */
  _results: { },

  /**
   * See nsIFeedResultService.idl
   */
  forcePreviewPage: false,

  /**
   * See nsIFeedResultService.idl
   */
  addToClientReader(spec, title, subtitle, feedType, feedReader) {
    if (!feedReader) {
      feedReader = "default";
    }

    let handler = Services.prefs.getCharPref(getPrefActionForType(feedType), "bookmarks");
    if (handler == "ask" || handler == "reader")
      handler = feedReader;

    switch (handler) {
    case "client":
      Services.cpmm.sendAsyncMessage("FeedConverter:ExecuteClientApp",
                                     { spec,
                                       title,
                                       subtitle,
                                       feedHandler: getPrefAppForType(feedType) });
      break;
    case "default":
      // Default system feed reader
      Services.cpmm.sendAsyncMessage("FeedConverter:ExecuteClientApp",
                                     { spec,
                                       title,
                                       subtitle,
                                       feedHandler: "default" });
      break;
    default:
      // "web" should have been handled elsewhere
      LOG("unexpected handler: " + handler);
      // fall through
    case "bookmarks":
      Services.cpmm.sendAsyncMessage("FeedConverter:addLiveBookmark",
                                     { spec, title, subtitle });
      break;
    }
  },

  /**
   * See nsIFeedResultService.idl
   */
  addFeedResult(feedResult) {
    NS_ASSERT(feedResult.uri != null, "null URI!");
    NS_ASSERT(feedResult.uri != null, "null feedResult!");
    let spec = feedResult.uri.spec;
    if (!this._results[spec])
      this._results[spec] = [];
    this._results[spec].push(feedResult);
  },

  /**
   * See nsIFeedResultService.idl
   */
  getFeedResult(uri) {
    NS_ASSERT(uri != null, "null URI!");
    let resultList = this._results[uri.spec];
    for (let result of resultList) {
      if (result.uri == uri)
        return result;
    }
    return null;
  },

  /**
   * See nsIFeedResultService.idl
   */
  removeFeedResult(uri) {
    NS_ASSERT(uri != null, "null URI!");
    let resultList = this._results[uri.spec];
    if (!resultList)
      return;
    let deletions = 0;
    for (let i = 0; i < resultList.length; ++i) {
      if (resultList[i].uri == uri) {
        delete resultList[i];
        ++deletions;
      }
    }

    // send the holes to the end
    resultList.sort();
    // and trim the list
    resultList.splice(resultList.length - deletions, deletions);
    if (resultList.length == 0)
      delete this._results[uri.spec];
  },

  createInstance(outer, iid) {
    if (outer != null)
      throw Cr.NS_ERROR_NO_AGGREGATION;
    return this.QueryInterface(iid);
  },

  QueryInterface(iid) {
    if (iid.equals(Ci.nsIFeedResultService) ||
        iid.equals(Ci.nsIFactory) ||
        iid.equals(Ci.nsISupports))
      return this;
    throw Cr.NS_ERROR_NOT_IMPLEMENTED;
  },
};


var components = [FeedConverter,
                  FeedResultService];


this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components);
PK
!<�ahOQ�Q�components/FeedWriter.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/NetUtil.jsm");

const FEEDWRITER_CID = Components.ID("{49bb6593-3aff-4eb3-a068-2712c28bd58e}");
const FEEDWRITER_CONTRACTID = "@mozilla.org/browser/feeds/result-writer;1";

function LOG(str) {
  let shouldLog = Services.prefs.getBoolPref("feeds.log", false);

  if (shouldLog)
    dump("*** Feeds: " + str + "\n");
}

/**
 * Wrapper function for nsIIOService::newURI.
 * @param aURLSpec
 *        The URL string from which to create an nsIURI.
 * @returns an nsIURI object, or null if the creation of the URI failed.
 */
function makeURI(aURLSpec, aCharset) {
  try {
    return Services.io.newURI(aURLSpec, aCharset);
  } catch (ex) { }

  return null;
}

const XML_NS = "http://www.w3.org/XML/1998/namespace";
const HTML_NS = "http://www.w3.org/1999/xhtml";
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const URI_BUNDLE = "chrome://browser/locale/feeds/subscribe.properties";

const TITLE_ID = "feedTitleText";
const SUBTITLE_ID = "feedSubtitleText";

/**
 * Converts a number of bytes to the appropriate unit that results in a
 * number that needs fewer than 4 digits
 *
 * @return a pair: [new value with 3 sig. figs., its unit]
  */
function convertByteUnits(aBytes) {
  let units = ["bytes", "kilobyte", "megabyte", "gigabyte"];
  let unitIndex = 0;

  // convert to next unit if it needs 4 digits (after rounding), but only if
  // we know the name of the next unit
  while ((aBytes >= 999.5) && (unitIndex < units.length - 1)) {
    aBytes /= 1024;
    unitIndex++;
  }

  // Get rid of insignificant bits by truncating to 1 or 0 decimal points
  // 0 -> 0; 1.2 -> 1.2; 12.3 -> 12.3; 123.4 -> 123; 234.5 -> 235
  aBytes = aBytes.toFixed((aBytes > 0) && (aBytes < 100) ? 1 : 0);

  return [aBytes, units[unitIndex]];
}

function FeedWriter() {
  this._selectedApp = undefined;
  this._selectedAppMenuItem = null;
  this._subscribeCallback = null;
  this._defaultHandlerMenuItem = null;

  Services.telemetry.scalarAdd("browser.feeds.preview_loaded", 1);

  XPCOMUtils.defineLazyGetter(this, "_mm", () =>
    this._window.QueryInterface(Ci.nsIInterfaceRequestor).
                 getInterface(Ci.nsIDocShell).
                 QueryInterface(Ci.nsIInterfaceRequestor).
                 getInterface(Ci.nsIContentFrameMessageManager));
}

FeedWriter.prototype = {
  _getPropertyAsBag(container, property) {
    return container.fields.getProperty(property).
                     QueryInterface(Ci.nsIPropertyBag2);
  },

  _getPropertyAsString(container, property) {
    try {
      return container.fields.getPropertyAsAString(property);
    } catch (e) {
    }
    return "";
  },

  _setContentText(id, text) {
    let element = this._document.getElementById(id);
    let textNode = text.createDocumentFragment(element);
    while (element.hasChildNodes())
      element.firstChild.remove();
    element.appendChild(textNode);
    if (text.base) {
      element.setAttributeNS(XML_NS, "base", text.base.spec);
    }
  },

  /**
   * Safely sets the href attribute on an anchor tag, providing the URI
   * specified can be loaded according to rules.
   * @param   element
   *          The element to set a URI attribute on
   * @param   attribute
   *          The attribute of the element to set the URI to, e.g. href or src
   * @param   uri
   *          The URI spec to set as the href
   */
  _safeSetURIAttribute(element, attribute, uri) {
    const flags = Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL;
    try {
      // TODO Is this necessary?
      Services.scriptSecurityManager.checkLoadURIStrWithPrincipal(this._feedPrincipal, uri, flags);
      // checkLoadURIStrWithPrincipal will throw if the link URI should not be
      // loaded, either because our feedURI isn't allowed to load it or per
      // the rules specified in |flags|, so we'll never "linkify" the link...
    } catch (e) {
      // Not allowed to load this link because secman.checkLoadURIStr threw
      return;
    }

    element.setAttribute(attribute, uri);
  },

  __bundle: null,
  get _bundle() {
    if (!this.__bundle) {
      this.__bundle = Services.strings.createBundle(URI_BUNDLE);
    }
    return this.__bundle;
  },

  _getFormattedString(key, params) {
    return this._bundle.formatStringFromName(key, params, params.length);
  },

  _getString(key) {
    return this._bundle.GetStringFromName(key);
  },

  _setCheckboxCheckedState(aValue) {
    let checkbox = this._document.getElementById("alwaysUse");
    if (checkbox) {
      // see checkbox.xml, xbl bindings are not applied within the sandbox! TODO
      let change = (aValue != (checkbox.getAttribute("checked") == "true"));
      if (aValue)
        checkbox.setAttribute("checked", "true");
      else
        checkbox.removeAttribute("checked");

      if (change) {
        let event = this._document.createEvent("Events");
        event.initEvent("CheckboxStateChange", true, true);
        checkbox.dispatchEvent(event);
      }
    }
  },

   /**
   * Returns a date suitable for displaying in the feed preview.
   * If the date cannot be parsed, the return value is "false".
   * @param   dateString
   *          A date as extracted from a feed entry. (entry.updated)
   */
  _parseDate(dateString) {
    // Convert the date into the user's local time zone
    let dateObj = new Date(dateString);

    // Make sure the date we're given is valid.
    if (!dateObj.getTime())
      return false;

    return this._dateFormatter.format(dateObj);
  },

  __dateFormatter: null,
  get _dateFormatter() {
    if (!this.__dateFormatter) {
      const dtOptions = {
        timeStyle: "short",
        dateStyle: "long"
      };
      this.__dateFormatter = new Services.intl.DateTimeFormat(undefined, dtOptions);
    }
    return this.__dateFormatter;
  },

  /**
   * Returns the feed type.
   */
  __feedType: null,
  _getFeedType() {
    if (this.__feedType != null)
      return this.__feedType;

    try {
      // grab the feed because it's got the feed.type in it.
      let container = this._getContainer();
      let feed = container.QueryInterface(Ci.nsIFeed);
      this.__feedType = feed.type;
      return feed.type;
    } catch (ex) { }

    return Ci.nsIFeed.TYPE_FEED;
  },

  /**
   * Writes the feed title into the preview document.
   * @param   container
   *          The feed container
   */
  _setTitleText(container) {
    if (container.title) {
      let title = container.title.plainText();
      this._setContentText(TITLE_ID, container.title);
      this._document.title = title;
    }

    let feed = container.QueryInterface(Ci.nsIFeed);
    if (feed && feed.subtitle)
      this._setContentText(SUBTITLE_ID, container.subtitle);
  },

  /**
   * Writes the title image into the preview document if one is present.
   * @param   container
   *          The feed container
   */
  _setTitleImage(container) {
    try {
      let parts = container.image;

      // Set up the title image (supplied by the feed)
      let feedTitleImage = this._document.getElementById("feedTitleImage");
      this._safeSetURIAttribute(feedTitleImage, "src",
                                parts.getPropertyAsAString("url"));

      // Set up the title image link
      let feedTitleLink = this._document.getElementById("feedTitleLink");

      let titleText = this._getFormattedString("linkTitleTextFormat",
                                               [parts.getPropertyAsAString("title")]);
      let feedTitleText = this._document.getElementById("feedTitleText");
      let titleImageWidth = parseInt(parts.getPropertyAsAString("width")) + 15;

      // Fix the margin on the main title, so that the image doesn't run over
      // the underline
      feedTitleLink.setAttribute("title", titleText);
      feedTitleText.style.marginRight = titleImageWidth + "px";

      this._safeSetURIAttribute(feedTitleLink, "href",
                                parts.getPropertyAsAString("link"));
    } catch (e) {
      LOG("Failed to set Title Image (this is benign): " + e);
    }
  },

  /**
   * Writes all entries contained in the feed.
   * @param   container
   *          The container of entries in the feed
   */
  _writeFeedContent(container) {
    // Build the actual feed content
    let feed = container.QueryInterface(Ci.nsIFeed);
    if (feed.items.length == 0)
      return;

    let feedContent = this._document.getElementById("feedContent");

    for (let i = 0; i < feed.items.length; ++i) {
      let entry = feed.items.queryElementAt(i, Ci.nsIFeedEntry);
      entry.QueryInterface(Ci.nsIFeedContainer);

      let entryContainer = this._document.createElementNS(HTML_NS, "div");
      entryContainer.className = "entry";

      // If the entry has a title, make it a link
      if (entry.title) {
        let a = this._document.createElementNS(HTML_NS, "a");
        let span = this._document.createElementNS(HTML_NS, "span");
        a.appendChild(span);
        if (entry.title.base)
          span.setAttributeNS(XML_NS, "base", entry.title.base.spec);
        span.appendChild(entry.title.createDocumentFragment(a));

        // Entries are not required to have links, so entry.link can be null.
        if (entry.link)
          this._safeSetURIAttribute(a, "href", entry.link.spec);

        let title = this._document.createElementNS(HTML_NS, "h3");
        title.appendChild(a);

        let lastUpdated = this._parseDate(entry.updated);
        if (lastUpdated) {
          let dateDiv = this._document.createElementNS(HTML_NS, "div");
          dateDiv.className = "lastUpdated";
          dateDiv.textContent = lastUpdated;
          title.appendChild(dateDiv);
        }

        entryContainer.appendChild(title);
      }

      let body = this._document.createElementNS(HTML_NS, "div");
      let summary = entry.summary || entry.content;
      let docFragment = null;
      if (summary) {
        if (summary.base)
          body.setAttributeNS(XML_NS, "base", summary.base.spec);
        else
          LOG("no base?");
        docFragment = summary.createDocumentFragment(body);
        if (docFragment)
          body.appendChild(docFragment);

        // If the entry doesn't have a title, append a # permalink
        // See http://scripting.com/rss.xml for an example
        if (!entry.title && entry.link) {
          let a = this._document.createElementNS(HTML_NS, "a");
          a.appendChild(this._document.createTextNode("#"));
          this._safeSetURIAttribute(a, "href", entry.link.spec);
          body.appendChild(this._document.createTextNode(" "));
          body.appendChild(a);
        }

      }
      body.className = "feedEntryContent";
      entryContainer.appendChild(body);

      if (entry.enclosures && entry.enclosures.length > 0) {
        let enclosuresDiv = this._buildEnclosureDiv(entry);
        entryContainer.appendChild(enclosuresDiv);
      }

      let clearDiv = this._document.createElementNS(HTML_NS, "div");
      clearDiv.style.clear = "both";

      feedContent.appendChild(entryContainer);
      feedContent.appendChild(clearDiv);
    }
  },

  /**
   * Takes a url to a media item and returns the best name it can come up with.
   * Frequently this is the filename portion (e.g. passing in
   * http://example.com/foo.mpeg would return "foo.mpeg"), but in more complex
   * cases, this will return the entire url (e.g. passing in
   * http://example.com/somedirectory/ would return
   * http://example.com/somedirectory/).
   * @param aURL
   *        The URL string from which to create a display name
   * @returns a string
   */
  _getURLDisplayName(aURL) {
    let url = makeURI(aURL);
    url.QueryInterface(Ci.nsIURL);
    if (url == null || url.fileName.length == 0)
      return decodeURIComponent(aURL);

    return decodeURIComponent(url.fileName);
  },

  /**
   * Takes a FeedEntry with enclosures, generates the HTML code to represent
   * them, and returns that.
   * @param   entry
   *          FeedEntry with enclosures
   * @returns element
   */
  _buildEnclosureDiv(entry) {
    let enclosuresDiv = this._document.createElementNS(HTML_NS, "div");
    enclosuresDiv.className = "enclosures";

    enclosuresDiv.appendChild(this._document.createTextNode(this._getString("mediaLabel")));

    for (let i_enc = 0; i_enc < entry.enclosures.length; ++i_enc) {
      let enc = entry.enclosures.queryElementAt(i_enc, Ci.nsIWritablePropertyBag2);

      if (!(enc.hasKey("url")))
        continue;

      let enclosureDiv = this._document.createElementNS(HTML_NS, "div");
      enclosureDiv.setAttribute("class", "enclosure");

      let mozicon = "moz-icon://.txt?size=16";
      let type_text = null;
      let size_text = null;

      if (enc.hasKey("type")) {
        type_text = enc.get("type");
        if (enc.hasKey("typeDesc"))
          type_text = enc.get("typeDesc");

        if (type_text && type_text.length > 0)
          mozicon = "moz-icon://goat?size=16&contentType=" + enc.get("type");
      }

      if (enc.hasKey("length") && /^[0-9]+$/.test(enc.get("length"))) {
        let enc_size = convertByteUnits(parseInt(enc.get("length")));

        size_text = this._getFormattedString("enclosureSizeText",
                                             [enc_size[0],
                                             this._getString(enc_size[1])]);
      }

      let iconimg = this._document.createElementNS(HTML_NS, "img");
      iconimg.setAttribute("src", mozicon);
      iconimg.setAttribute("class", "type-icon");
      enclosureDiv.appendChild(iconimg);

      enclosureDiv.appendChild(this._document.createTextNode( " " ));

      let enc_href = this._document.createElementNS(HTML_NS, "a");
      enc_href.appendChild(this._document.createTextNode(this._getURLDisplayName(enc.get("url"))));
      this._safeSetURIAttribute(enc_href, "href", enc.get("url"));
      enclosureDiv.appendChild(enc_href);

      if (type_text && size_text)
        enclosureDiv.appendChild(this._document.createTextNode( " (" + type_text + ", " + size_text + ")"));

      else if (type_text)
        enclosureDiv.appendChild(this._document.createTextNode( " (" + type_text + ")"));

      else if (size_text)
        enclosureDiv.appendChild(this._document.createTextNode( " (" + size_text + ")"));

      enclosuresDiv.appendChild(enclosureDiv);
    }

    return enclosuresDiv;
  },

  /**
   * Gets a valid nsIFeedContainer object from the parsed nsIFeedResult.
   * Displays error information if there was one.
   * @returns A valid nsIFeedContainer object containing the contents of
   *          the feed.
   */
  _getContainer() {
    let feedService =
        Cc["@mozilla.org/browser/feeds/result-service;1"].
        getService(Ci.nsIFeedResultService);

    let result;
    try {
      result =
        feedService.getFeedResult(this._getOriginalURI(this._window));
    } catch (e) {
      LOG("Subscribe Preview: feed not available?!");
    }

    if (result.bozo) {
      LOG("Subscribe Preview: feed result is bozo?!");
    }

    let container;
    try {
      container = result.doc;
    } catch (e) {
      LOG("Subscribe Preview: no result.doc? Why didn't the original reload?");
      return null;
    }
    return container;
  },

  /**
   * Get moz-icon url for a file
   * @param   file
   *          A nsIFile object for which the moz-icon:// is returned
   * @returns moz-icon url of the given file as a string
   */
  _getFileIconURL(file) {
    let fph = Services.io.getProtocolHandler("file")
                 .QueryInterface(Ci.nsIFileProtocolHandler);
    let urlSpec = fph.getURLSpecFromFile(file);
    return "moz-icon://" + urlSpec + "?size=16";
  },

  /**
   * Displays a prompt from which the user may choose a (client) feed reader.
   * @param aCallback the callback method, passes in true if a feed reader was
   *        selected, false otherwise.
   */
  _chooseClientApp(aCallback) {
    this._subscribeCallback = aCallback;
    this._mm.sendAsyncMessage("FeedWriter:ChooseClientApp",
                              { title: this._getString("chooseApplicationDialogTitle"),
                                feedType: this._getFeedType() });
  },

  _setSubscribeUsingLabel() {
    let stringLabel = "subscribeFeedUsing";
    switch (this._getFeedType()) {
      case Ci.nsIFeed.TYPE_VIDEO:
        stringLabel = "subscribeVideoPodcastUsing";
        break;

      case Ci.nsIFeed.TYPE_AUDIO:
        stringLabel = "subscribeAudioPodcastUsing";
        break;
    }

    let subscribeUsing = this._document.getElementById("subscribeUsingDescription");
    let textNode = this._document.createTextNode(this._getString(stringLabel));
    subscribeUsing.insertBefore(textNode, subscribeUsing.firstChild);
  },

  _setAlwaysUseLabel() {
    let checkbox = this._document.getElementById("alwaysUse");
    if (checkbox && this._handlersList) {
      let handlerName = this._handlersList.selectedOptions[0]
                            .textContent;
      let stringLabel = "alwaysUseForFeeds";
      switch (this._getFeedType()) {
        case Ci.nsIFeed.TYPE_VIDEO:
          stringLabel = "alwaysUseForVideoPodcasts";
          break;

        case Ci.nsIFeed.TYPE_AUDIO:
          stringLabel = "alwaysUseForAudioPodcasts";
          break;
      }

      let label = this._getFormattedString(stringLabel, [handlerName]);

      let checkboxText = this._document.getElementById("checkboxText");
      if (checkboxText.lastChild.nodeType == checkboxText.TEXT_NODE) {
        checkboxText.lastChild.textContent = label;
      } else {
        LOG("FeedWriter._setAlwaysUseLabel: Expected textNode as lastChild of alwaysUse label");
        let textNode = this._document.createTextNode(label);
        checkboxText.appendChild(textNode);
      }
    }
  },

  // nsIDomEventListener
  handleEvent(event) {
    if (event.target.ownerDocument != this._document) {
      LOG("FeedWriter.handleEvent: Someone passed the feed writer as a listener to the events of another document!");
      return;
    }

    switch (event.type) {
      case "click":
        if (event.target.id == "subscribeButton") {
          this.subscribe();
        }
      break;
      case "change":
        LOG("Change fired");
        if (event.target.selectedOptions[0].id == "chooseApplicationMenuItem") {
          this._chooseClientApp(() => {
            // Select the (per-prefs) selected handler if no application
            // was selected
            LOG("Selected handler after callback");
            this._setAlwaysUseLabel();
          });
        } else {
          this._setAlwaysUseLabel();
        }
        break;
    }
  },

  _getWebHandlerElementsForURL(aURL) {
    return this._handlersList.querySelectorAll('[webhandlerurl="' + aURL + '"]');
  },

  _setSelectedHandlerResponse(handler, url) {
    LOG(`Selecting handler response ${handler} ${url}`);
    switch (handler) {
      case "web": {
        if (this._handlersList) {
          let handlers =
            this._getWebHandlerElementsForURL(url);
          if (handlers.length == 0) {
            LOG(`Selected web handler isn't in the menulist ${url}`);
            return;
          }

          handlers[0].selected = true;
        }
        break;
      }
      case "client":
      case "default":
        // do nothing, these are handled by the onchange event
        break;
      case "bookmarks":
      default: {
        let liveBookmarksMenuItem = this._document.getElementById("liveBookmarksMenuItem");
        if (liveBookmarksMenuItem)
          liveBookmarksMenuItem.selected = true;
      }
    }
  },

  _initSubscriptionUI(setupMessage) {
    if (!this._handlersList)
      return;
    LOG("UI init");

    let feedType = this._getFeedType();

    // change the background
    let header = this._document.getElementById("feedHeader");
    switch (feedType) {
      case Ci.nsIFeed.TYPE_VIDEO:
        header.className = "videoPodcastBackground";
        break;

      case Ci.nsIFeed.TYPE_AUDIO:
        header.className = "audioPodcastBackground";
        break;

      default:
        header.className = "feedBackground";
    }

    let liveBookmarksMenuItem = this._document.getElementById("liveBookmarksMenuItem");

    // Last-selected application
    let menuItem = liveBookmarksMenuItem.cloneNode(false);
    menuItem.removeAttribute("selected");
    menuItem.setAttribute("id", "selectedAppMenuItem");
    menuItem.setAttribute("handlerType", "client");

    // Hide the menuitem until we select an app
    menuItem.style.display = "none";
    this._selectedAppMenuItem = menuItem;

    this._handlersList.appendChild(this._selectedAppMenuItem);

    // Create the menuitem for the default reader, but don't show/populate it until
    // we get confirmation of what it is from the parent
    menuItem = liveBookmarksMenuItem.cloneNode(false);
    menuItem.removeAttribute("selected");
    menuItem.setAttribute("id", "defaultHandlerMenuItem");
    menuItem.setAttribute("handlerType", "client");
    menuItem.style.display = "none";

    this._defaultHandlerMenuItem = menuItem;
    this._handlersList.appendChild(this._defaultHandlerMenuItem);

    // "Choose Application..." menuitem
    menuItem = liveBookmarksMenuItem.cloneNode(false);
    menuItem.removeAttribute("selected");
    menuItem.setAttribute("id", "chooseApplicationMenuItem");
    menuItem.textContent = this._getString("chooseApplicationMenuItem");

    this._handlersList.appendChild(menuItem);

    // separator
    let chooseAppSep = liveBookmarksMenuItem.nextElementSibling.cloneNode(false);
    chooseAppSep.textContent = liveBookmarksMenuItem.nextElementSibling.textContent;
    this._handlersList.appendChild(chooseAppSep);

    for (let handler of setupMessage.handlers) {
      if (!handler.uri) {
        LOG("Handler with name " + handler.name + " has no URI!? Skipping...");
        continue;
      }
      menuItem = liveBookmarksMenuItem.cloneNode(false);
      menuItem.removeAttribute("selected");
      menuItem.className = "menuitem-iconic";
      menuItem.textContent = handler.name;
      menuItem.setAttribute("handlerType", "web");
      menuItem.setAttribute("webhandlerurl", handler.uri);
      this._handlersList.appendChild(menuItem);
    }

    this._setSelectedHandlerResponse(setupMessage.reader.handler, setupMessage.reader.url);

    if (setupMessage.defaultMenuItem) {
      LOG(`Setting default menu item ${setupMessage.defaultMenuItem}`);
      this._setApplicationLauncherMenuItem(this._defaultHandlerMenuItem, setupMessage.defaultMenuItem);
    }
    if (setupMessage.selectedMenuItem) {
      LOG(`Setting selected menu item ${setupMessage.selectedMenuItem}`);
      this._setApplicationLauncherMenuItem(this._selectedAppMenuItem, setupMessage.selectedMenuItem);
    }

    // "Subscribe using..."
    this._setSubscribeUsingLabel();

    // "Always use..." checkbox initial state
    this._setCheckboxCheckedState(setupMessage.reader.alwaysUse);
    this._setAlwaysUseLabel();

    // We update the "Always use.." checkbox label whenever the selected item
    // in the list is changed
    this._handlersList.addEventListener("change", this);

    // Set up the "Subscribe Now" button
    this._document.getElementById("subscribeButton")
        .addEventListener("click", this);

    // first-run ui
    if (setupMessage.showFirstRunUI) {
      let textfeedinfo1, textfeedinfo2;
      switch (feedType) {
        case Ci.nsIFeed.TYPE_VIDEO:
          textfeedinfo1 = "feedSubscriptionVideoPodcast1";
          textfeedinfo2 = "feedSubscriptionVideoPodcast2";
          break;
        case Ci.nsIFeed.TYPE_AUDIO:
          textfeedinfo1 = "feedSubscriptionAudioPodcast1";
          textfeedinfo2 = "feedSubscriptionAudioPodcast2";
          break;
        default:
          textfeedinfo1 = "feedSubscriptionFeed1";
          textfeedinfo2 = "feedSubscriptionFeed2";
      }

      let feedinfo1 = this._document.getElementById("feedSubscriptionInfo1");
      let feedinfo1Str = this._getString(textfeedinfo1);
      let feedinfo2 = this._document.getElementById("feedSubscriptionInfo2");
      let feedinfo2Str = this._getString(textfeedinfo2);

      feedinfo1.textContent = feedinfo1Str;
      feedinfo2.textContent = feedinfo2Str;

      header.setAttribute("firstrun", "true");

      this._mm.sendAsyncMessage("FeedWriter:ShownFirstRun");
    }
  },

  /**
   * Returns the original URI object of the feed and ensures that this
   * component is only ever invoked from the preview document.
   * @param aWindow
   *        The window of the document invoking the BrowserFeedWriter
   */
  _getOriginalURI(aWindow) {
    let docShell = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                          .getInterface(Ci.nsIWebNavigation)
                          .QueryInterface(Ci.nsIDocShell);
    let chan = docShell.currentDocumentChannel;

    // We probably need to call Inherit() for this, but right now we can't call
    // it from JS.
    let attrs = docShell.getOriginAttributes();
    let ssm = Services.scriptSecurityManager;
    let nullPrincipal = ssm.createNullPrincipal(attrs);

    // this channel is not going to be openend, use a nullPrincipal
    // and the most restrctive securityFlag.
    let resolvedURI = NetUtil.newChannel({
      uri: "about:feeds",
      loadingPrincipal: nullPrincipal,
      securityFlags: Ci.nsILoadInfo.SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED,
      contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER
    }).URI;

    if (resolvedURI.equals(chan.URI))
      return chan.originalURI;

    return null;
  },

  _window: null,
  _document: null,
  _feedURI: null,
  _feedPrincipal: null,
  _handlersList: null,

  // BrowserFeedWriter WebIDL methods
  init(aWindow) {
    let window = aWindow;
    this._feedURI = this._getOriginalURI(window);
    if (!this._feedURI)
      return;

    this._window = window;
    this._document = window.document;
    this._handlersList = this._document.getElementById("handlersMenuList");

    this._feedPrincipal = Services.scriptSecurityManager.createCodebasePrincipal(this._feedURI, {});

    LOG("Subscribe Preview: feed uri = " + this._window.location.href);


    this._mm.addMessageListener("FeedWriter:PreferenceUpdated", this);
    this._mm.addMessageListener("FeedWriter:SetApplicationLauncherMenuItem", this);
    this._mm.addMessageListener("FeedWriter:GetSubscriptionUIResponse", this);
    this._mm.addMessageListener("FeedWriter:SetFeedPrefsAndSubscribeResponse", this);

    const feedType = this._getFeedType();
    this._mm.sendAsyncMessage("FeedWriter:GetSubscriptionUI",
                              { feedType });
  },

  receiveMessage(msg) {
    if (!this._window) {
      // this._window is null unless this.init was called with a trusted
      // window object.
      return;
    }
    LOG(`received message from parent ${msg.name}`);
    switch (msg.name) {
      case "FeedWriter:PreferenceUpdated":
        // This is called when browser-feeds.js spots a pref change
        // This will happen when
        // - about:preferences#general changes
        // - another feed reader page changes the preference
        // - when this page itself changes the select and there isn't a redirect
        //   bookmarks and launching an external app means the page stays open after subscribe
        const feedType = this._getFeedType();
        LOG(`Got prefChange! ${JSON.stringify(msg.data)} current type: ${feedType}`);
        let feedTypePref = msg.data.default;
        if (feedType in msg.data) {
          feedTypePref = msg.data[feedType];
        }
        LOG(`Got pref ${JSON.stringify(feedTypePref)}`);
        this._setCheckboxCheckedState(feedTypePref.alwaysUse);
        this._setSelectedHandlerResponse(feedTypePref.handler, feedTypePref.url);
        this._setAlwaysUseLabel();
        break;
      case "FeedWriter:SetFeedPrefsAndSubscribeResponse":
        LOG(`FeedWriter:SetFeedPrefsAndSubscribeResponse - Redirecting ${msg.data.redirect}`);
        this._window.location.href = msg.data.redirect;
        break;
      case "FeedWriter:GetSubscriptionUIResponse":
        // Set up the subscription UI
        this._initSubscriptionUI(msg.data);
        break;
      case "FeedWriter:SetApplicationLauncherMenuItem":
        LOG(`FeedWriter:SetApplicationLauncherMenuItem - picked ${msg.data.name}`);
        this._setApplicationLauncherMenuItem(this._selectedAppMenuItem, msg.data.name);
        // Potentially a bit racy, but I don't think we can get into a state where this callback is set and
        // we're not coming back from ChooseClientApp in browser-feeds.js
        if (this._subscribeCallback) {
          this._subscribeCallback();
          this._subscribeCallback = null;
        }
        break;
    }
  },

  _setApplicationLauncherMenuItem(menuItem, aName) {
    /* unselect all handlers */
    [...this._handlersList.children].forEach((option) => {
      option.removeAttribute("selected");
    });
    menuItem.textContent = aName;
    menuItem.style.display = "";
    menuItem.selected = true;
  },

  writeContent() {
    if (!this._window)
      return;

    try {
      // Set up the feed content
      let container = this._getContainer();
      if (!container)
        return;

      this._setTitleText(container);
      this._setTitleImage(container);
      this._writeFeedContent(container);
    } finally {
      this._removeFeedFromCache();
    }
  },

  close() {
    this._document.getElementById("subscribeButton")
        .removeEventListener("click", this);
    this._handlersList
        .removeEventListener("change", this);
    this._document = null;
    this._window = null;
    this._handlersList = null;

    this._removeFeedFromCache();
    this.__bundle = null;
    this._feedURI = null;

    this._selectedApp = undefined;
    this._selectedAppMenuItem = null;
    this._defaultHandlerMenuItem = null;
  },

  _removeFeedFromCache() {
    if (this._feedURI) {
      let feedService = Cc["@mozilla.org/browser/feeds/result-service;1"].
                        getService(Ci.nsIFeedResultService);
      feedService.removeFeedResult(this._feedURI);
      this._feedURI = null;
    }
  },

  subscribe() {
    let feedType = this._getFeedType();

    // Subscribe to the feed using the selected handler and save prefs
    let defaultHandler = "reader";
    let useAsDefault = this._document.getElementById("alwaysUse").getAttribute("checked");

    let selectedItem = this._handlersList.selectedOptions[0];
    let subscribeCallback = () => {
      let feedReader = null;
      let settings = {
        feedType,
        useAsDefault,
        // Pull the title and subtitle out of the document
        feedTitle: this._document.getElementById(TITLE_ID).textContent,
        feedSubtitle: this._document.getElementById(SUBTITLE_ID).textContent,
        feedLocation: this._window.location.href
      };
      if (selectedItem.hasAttribute("webhandlerurl")) {
        feedReader = "web";
        settings.uri = selectedItem.getAttribute("webhandlerurl");
      } else {
        switch (selectedItem.id) {
          case "selectedAppMenuItem":
            feedReader = "client";
            break;
          case "defaultHandlerMenuItem":
            feedReader = "default";
            break;
          case "liveBookmarksMenuItem":
            defaultHandler = "bookmarks";
            feedReader = "bookmarks";
            break;
        }
      }
      settings.reader = feedReader;

      // If "Always use..." is checked, we should set PREF_*SELECTED_ACTION
      // to either "reader" (If a web reader or if an application is selected),
      // or to "bookmarks" (if the live bookmarks option is selected).
      // Otherwise, we should set it to "ask"
      if (!useAsDefault) {
        defaultHandler = "ask";
      }
      settings.action = defaultHandler;
      LOG(`FeedWriter:SetFeedPrefsAndSubscribe - ${JSON.stringify(settings)}`);
      this._mm.sendAsyncMessage("FeedWriter:SetFeedPrefsAndSubscribe",
                                settings);
    };

    // Show the file picker before subscribing if the
    // choose application menuitem was chosen using the keyboard
    if (selectedItem.id == "chooseApplicationMenuItem") {
      this._chooseClientApp(aResult => {
        if (aResult) {
          selectedItem =
            this._handlersList.selectedOptions[0];
          subscribeCallback();
        }
      });
    } else {
      subscribeCallback();
    }
  },

  classID: FEEDWRITER_CID,
  QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMEventListener, Ci.nsIObserver,
                                         Ci.nsIDOMGlobalPropertyInitializer])
};

this.NSGetFactory = XPCOMUtils.generateNSGetFactory([FeedWriter]);
PK
!<�*X1�z�z%components/nsBrowserContentHandler.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/AppConstants.jsm");

ChromeUtils.defineModuleGetter(this, "HeadlessShell",
                               "resource:///modules/HeadlessShell.jsm");
ChromeUtils.defineModuleGetter(this, "LaterRun",
                               "resource:///modules/LaterRun.jsm");
ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
                               "resource://gre/modules/PrivateBrowsingUtils.jsm");
ChromeUtils.defineModuleGetter(this, "RecentWindow",
                               "resource:///modules/RecentWindow.jsm");
ChromeUtils.defineModuleGetter(this, "ShellService",
                               "resource:///modules/ShellService.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "WindowsUIUtils",
                                   "@mozilla.org/windows-ui-utils;1", "nsIWindowsUIUtils");
ChromeUtils.defineModuleGetter(this, "UpdatePing",
                               "resource://gre/modules/UpdatePing.jsm");

const nsISupports            = Ci.nsISupports;

const nsIBrowserDOMWindow    = Ci.nsIBrowserDOMWindow;
const nsIBrowserHandler      = Ci.nsIBrowserHandler;
const nsIBrowserHistory      = Ci.nsIBrowserHistory;
const nsIChannel             = Ci.nsIChannel;
const nsICommandLine         = Ci.nsICommandLine;
const nsICommandLineHandler  = Ci.nsICommandLineHandler;
const nsIContentHandler      = Ci.nsIContentHandler;
const nsIDocShellTreeItem    = Ci.nsIDocShellTreeItem;
const nsIDOMChromeWindow     = Ci.nsIDOMChromeWindow;
const nsIDOMWindow           = Ci.nsIDOMWindow;
const nsIFileURL             = Ci.nsIFileURL;
const nsIInterfaceRequestor  = Ci.nsIInterfaceRequestor;
const nsINetUtil             = Ci.nsINetUtil;
const nsIPrefLocalizedString = Ci.nsIPrefLocalizedString;
const nsISupportsString      = Ci.nsISupportsString;
const nsIWebNavigation       = Ci.nsIWebNavigation;
const nsIWebNavigationInfo   = Ci.nsIWebNavigationInfo;
const nsICommandLineValidator = Ci.nsICommandLineValidator;

const NS_BINDING_ABORTED = Cr.NS_BINDING_ABORTED;
const NS_ERROR_WONT_HANDLE_CONTENT = 0x805d0001;
const NS_ERROR_ABORT = Cr.NS_ERROR_ABORT;

function shouldLoadURI(aURI) {
  if (aURI && !aURI.schemeIs("chrome"))
    return true;

  dump("*** Preventing external load of chrome: URI into browser window\n");
  dump("    Use --chrome <uri> instead\n");
  return false;
}

function resolveURIInternal(aCmdLine, aArgument) {
  var uri = aCmdLine.resolveURI(aArgument);
  var uriFixup = Services.uriFixup;

  if (!(uri instanceof nsIFileURL)) {
    return uriFixup.createFixupURI(aArgument,
                                   uriFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS);
  }

  try {
    if (uri.file.exists())
      return uri;
  } catch (e) {
    Cu.reportError(e);
  }

  // We have interpreted the argument as a relative file URI, but the file
  // doesn't exist. Try URI fixup heuristics: see bug 290782.

  try {
    uri = uriFixup.createFixupURI(aArgument, 0);
  } catch (e) {
    Cu.reportError(e);
  }

  return uri;
}

var gFirstWindow = false;

const OVERRIDE_NONE        = 0;
const OVERRIDE_NEW_PROFILE = 1;
const OVERRIDE_NEW_MSTONE  = 2;
const OVERRIDE_NEW_BUILD_ID = 3;
/**
 * Determines whether a home page override is needed.
 * Returns:
 *  OVERRIDE_NEW_PROFILE if this is the first run with a new profile.
 *  OVERRIDE_NEW_MSTONE if this is the first run with a build with a different
 *                      Gecko milestone (i.e. right after an upgrade).
 *  OVERRIDE_NEW_BUILD_ID if this is the first run with a new build ID of the
 *                        same Gecko milestone (i.e. after a nightly upgrade).
 *  OVERRIDE_NONE otherwise.
 */
function needHomepageOverride(prefb) {
  var savedmstone = prefb.getCharPref("browser.startup.homepage_override.mstone", "");

  if (savedmstone == "ignore")
    return OVERRIDE_NONE;

  var mstone = Services.appinfo.platformVersion;

  var savedBuildID = prefb.getCharPref("browser.startup.homepage_override.buildID", "");

  var buildID = Services.appinfo.platformBuildID;

  if (mstone != savedmstone) {
    // Bug 462254. Previous releases had a default pref to suppress the EULA
    // agreement if the platform's installer had already shown one. Now with
    // about:rights we've removed the EULA stuff and default pref, but we need
    // a way to make existing profiles retain the default that we removed.
    if (savedmstone)
      prefb.setBoolPref("browser.rights.3.shown", true);

    prefb.setCharPref("browser.startup.homepage_override.mstone", mstone);
    prefb.setCharPref("browser.startup.homepage_override.buildID", buildID);
    return (savedmstone ? OVERRIDE_NEW_MSTONE : OVERRIDE_NEW_PROFILE);
  }

  if (buildID != savedBuildID) {
    prefb.setCharPref("browser.startup.homepage_override.buildID", buildID);
    return OVERRIDE_NEW_BUILD_ID;
  }

  return OVERRIDE_NONE;
}

/**
 * Gets the override page for the first run after the application has been
 * updated.
 * @param  defaultOverridePage
 *         The default override page.
 * @return The override page.
 */
function getPostUpdateOverridePage(defaultOverridePage) {
  var um = Cc["@mozilla.org/updates/update-manager;1"]
             .getService(Ci.nsIUpdateManager);
  // The active update should be present when this code is called. If for
  // whatever reason it isn't fallback to the latest update in the update
  // history.
  if (um.activeUpdate) {
    var update = um.activeUpdate
                   .QueryInterface(Ci.nsIPropertyBag);
  } else {
    // If the updates.xml file is deleted then getUpdateAt will throw.
    try {
      update = um.getUpdateAt(0)
                 .QueryInterface(Ci.nsIPropertyBag);
    } catch (e) {
      Cu.reportError("Unable to find update: " + e);
      return defaultOverridePage;
    }
  }

  let actions = update.getProperty("actions");
  // When the update doesn't specify actions fallback to the original behavior
  // of displaying the default override page.
  if (!actions)
    return defaultOverridePage;

  // The existence of silent or the non-existence of showURL in the actions both
  // mean that an override page should not be displayed.
  if (actions.includes("silent") || !actions.includes("showURL"))
    return "";

  // If a policy was set to not allow the update.xml-provided
  // URL to be used, use the default fallback (which will also
  // be provided by the policy).
  if (!Services.policies.isAllowed("postUpdateCustomPage")) {
    return defaultOverridePage;
  }

  return update.getProperty("openURL") || defaultOverridePage;
}

// Flag used to indicate that the arguments to openWindow can be passed directly.
const NO_EXTERNAL_URIS = 1;

function openWindow(parent, url, target, features, args, noExternalArgs) {
  if (noExternalArgs == NO_EXTERNAL_URIS) {
    // Just pass in the defaultArgs directly
    var argstring;
    if (args) {
      argstring = Cc["@mozilla.org/supports-string;1"]
                    .createInstance(nsISupportsString);
      argstring.data = args;
    }

    return Services.ww.openWindow(parent, url, target, features, argstring);
  }

  // Pass an array to avoid the browser "|"-splitting behavior.
  var argArray = Cc["@mozilla.org/array;1"]
                    .createInstance(Ci.nsIMutableArray);

  // add args to the arguments array
  var stringArgs = null;
  if (args instanceof Array) // array
    stringArgs = args;
  else if (args) // string
    stringArgs = [args];

  if (stringArgs) {
    // put the URIs into argArray
    var uriArray = Cc["@mozilla.org/array;1"]
                       .createInstance(Ci.nsIMutableArray);
    stringArgs.forEach(function(uri) {
      var sstring = Cc["@mozilla.org/supports-string;1"]
                      .createInstance(nsISupportsString);
      sstring.data = uri;
      uriArray.appendElement(sstring);
    });
    argArray.appendElement(uriArray);
  } else {
    argArray.appendElement(null);
  }

  // Pass these as null to ensure that we always trigger the "single URL"
  // behavior in browser.js's gBrowserInit.onLoad (which handles the window
  // arguments)
  argArray.appendElement(null); // charset
  argArray.appendElement(null); // referer
  argArray.appendElement(null); // postData
  argArray.appendElement(null); // allowThirdPartyFixup

  return Services.ww.openWindow(parent, url, target, features, argArray);
}

function openPreferences(extraArgs) {
  if (extraArgs && extraArgs.origin) {
    Services.telemetry.getHistogramById("FX_PREFERENCES_OPENED_VIA").add(extraArgs.origin);
  } else {
    Services.telemetry.getHistogramById("FX_PREFERENCES_OPENED_VIA").add("other");
  }
  var args = Cc["@mozilla.org/array;1"]
                     .createInstance(Ci.nsIMutableArray);

  var wuri = Cc["@mozilla.org/supports-string;1"]
               .createInstance(Ci.nsISupportsString);
  wuri.data = "about:preferences";

  args.appendElement(wuri);

  Services.ww.openWindow(null, gBrowserContentHandler.chromeURL,
                         "_blank",
                         "chrome,dialog=no,all",
                         args);
}

function logSystemBasedSearch(engine) {
  var countId = (engine.identifier || ("other-" + engine.name)) + ".system";
  var count = Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS");
  count.add(countId);
}

function doSearch(searchTerm, cmdLine) {
  var engine = Services.search.defaultEngine;
  logSystemBasedSearch(engine);

  var submission = engine.getSubmission(searchTerm, null, "system");

  // fill our nsIMutableArray with uri-as-wstring, null, null, postData
  var args = Cc["@mozilla.org/array;1"]
                     .createInstance(Ci.nsIMutableArray);

  var wuri = Cc["@mozilla.org/supports-string;1"]
               .createInstance(Ci.nsISupportsString);
  wuri.data = submission.uri.spec;

  args.appendElement(wuri);
  args.appendElement(null);
  args.appendElement(null);
  args.appendElement(submission.postData);

  // XXXbsmedberg: use handURIToExistingBrowser to obey tabbed-browsing
  // preferences, but need nsIBrowserDOMWindow extensions

  return Services.ww.openWindow(null, gBrowserContentHandler.chromeURL,
                                "_blank",
                                "chrome,dialog=no,all" +
                                gBrowserContentHandler.getFeatures(cmdLine),
                                args);
}

function nsBrowserContentHandler() {
}
nsBrowserContentHandler.prototype = {
  classID: Components.ID("{5d0ce354-df01-421a-83fb-7ead0990c24e}"),

  _xpcom_factory: {
    createInstance: function bch_factory_ci(outer, iid) {
      if (outer)
        throw Cr.NS_ERROR_NO_AGGREGATION;
      return gBrowserContentHandler.QueryInterface(iid);
    }
  },

  /* helper functions */

  mChromeURL: null,

  get chromeURL() {
    if (this.mChromeURL) {
      return this.mChromeURL;
    }

    this.mChromeURL = Services.prefs.getCharPref("browser.chromeURL");

    return this.mChromeURL;
  },

  /* nsISupports */
  QueryInterface: XPCOMUtils.generateQI([nsICommandLineHandler,
                                         nsIBrowserHandler,
                                         nsIContentHandler,
                                         nsICommandLineValidator]),

  /* nsICommandLineHandler */
  handle: function bch_handle(cmdLine) {
    if (cmdLine.handleFlag("browser", false)) {
      // Passing defaultArgs, so use NO_EXTERNAL_URIS
      openWindow(null, this.chromeURL, "_blank",
                 "chrome,dialog=no,all" + this.getFeatures(cmdLine),
                 this.defaultArgs, NO_EXTERNAL_URIS);
      cmdLine.preventDefault = true;
    }

    // In the past, when an instance was not already running, the -remote
    // option returned an error code. Any script or application invoking the
    // -remote option is expected to be handling this case, otherwise they
    // wouldn't be doing anything when there is no Firefox already running.
    // Making the -remote option always return an error code makes those
    // scripts or applications handle the situation as if Firefox was not
    // already running.
    if (cmdLine.handleFlag("remote", true)) {
      throw NS_ERROR_ABORT;
    }

    var uriparam;
    try {
      while ((uriparam = cmdLine.handleFlagWithParam("new-window", false))) {
        let uri = resolveURIInternal(cmdLine, uriparam);
        if (!shouldLoadURI(uri))
          continue;
        openWindow(null, this.chromeURL, "_blank",
                   "chrome,dialog=no,all" + this.getFeatures(cmdLine),
                   uri.spec);
        cmdLine.preventDefault = true;
      }
    } catch (e) {
      Cu.reportError(e);
    }

    try {
      while ((uriparam = cmdLine.handleFlagWithParam("new-tab", false))) {
        let uri = resolveURIInternal(cmdLine, uriparam);
        handURIToExistingBrowser(uri, nsIBrowserDOMWindow.OPEN_NEWTAB, cmdLine, false,
                                 Services.scriptSecurityManager.getSystemPrincipal());
        cmdLine.preventDefault = true;
      }
    } catch (e) {
      Cu.reportError(e);
    }

    var chromeParam = cmdLine.handleFlagWithParam("chrome", false);
    if (chromeParam) {

      // Handle old preference dialog URLs.
      if (chromeParam == "chrome://browser/content/pref/pref.xul" ||
          chromeParam == "chrome://browser/content/preferences/preferences.xul") {
        openPreferences({origin: "commandLineLegacy"});
        cmdLine.preventDefault = true;
      } else try {
        let resolvedURI = resolveURIInternal(cmdLine, chromeParam);
        let isLocal = uri => {
          let localSchemes = new Set(["chrome", "file", "resource"]);
          if (uri instanceof Ci.nsINestedURI) {
            uri = uri.QueryInterface(Ci.nsINestedURI).innerMostURI;
          }
          return localSchemes.has(uri.scheme);
        };
        if (isLocal(resolvedURI)) {
          // If the URI is local, we are sure it won't wrongly inherit chrome privs
          let features = "chrome,dialog=no,all" + this.getFeatures(cmdLine);
          openWindow(null, resolvedURI.spec, "_blank", features);
          cmdLine.preventDefault = true;
        } else {
          dump("*** Preventing load of web URI as chrome\n");
          dump("    If you're trying to load a webpage, do not pass --chrome.\n");
        }
      } catch (e) {
        Cu.reportError(e);
      }
    }
    if (cmdLine.handleFlag("preferences", false)) {
      openPreferences({origin: "commandLineLegacy"});
      cmdLine.preventDefault = true;
    }
    if (cmdLine.handleFlag("silent", false))
      cmdLine.preventDefault = true;

    try {
      var privateWindowParam = cmdLine.handleFlagWithParam("private-window", false);
      if (privateWindowParam) {
        let forcePrivate = true;
        let resolvedURI;
        if (!PrivateBrowsingUtils.enabled) {
          // Load about:privatebrowsing in a normal tab, which will display an error indicating
          // access to private browsing has been disabled.
          forcePrivate = false;
          resolvedURI = Services.io.newURI("about:privatebrowsing");
        } else {
          resolvedURI = resolveURIInternal(cmdLine, privateWindowParam);
        }
        handURIToExistingBrowser(resolvedURI, nsIBrowserDOMWindow.OPEN_NEWTAB, cmdLine, forcePrivate,
                                 Services.scriptSecurityManager.getSystemPrincipal());
        cmdLine.preventDefault = true;
      }
    } catch (e) {
      if (e.result != Cr.NS_ERROR_INVALID_ARG) {
        throw e;
      }
      // NS_ERROR_INVALID_ARG is thrown when flag exists, but has no param.
      if (cmdLine.handleFlag("private-window", false)) {
        let features = "chrome,dialog=no,all";
        if (PrivateBrowsingUtils.enabled) {
          features += ",private";
        }
        openWindow(null, this.chromeURL, "_blank",
          features + this.getFeatures(cmdLine),
          "about:privatebrowsing");
        cmdLine.preventDefault = true;
      }
    }

    var searchParam = cmdLine.handleFlagWithParam("search", false);
    if (searchParam) {
      doSearch(searchParam, cmdLine);
      cmdLine.preventDefault = true;
    }

    // The global PB Service consumes this flag, so only eat it in per-window
    // PB builds.
    if (cmdLine.handleFlag("private", false) && PrivateBrowsingUtils.enabled) {
      PrivateBrowsingUtils.enterTemporaryAutoStartMode();
    }

    var fileParam = cmdLine.handleFlagWithParam("file", false);
    if (fileParam) {
      var file = cmdLine.resolveFile(fileParam);
      var fileURI = Services.io.newFileURI(file);
      openWindow(null, this.chromeURL, "_blank",
                 "chrome,dialog=no,all" + this.getFeatures(cmdLine),
                 fileURI.spec);
      cmdLine.preventDefault = true;
    }

    if (AppConstants.platform == "win") {
      // Handle "? searchterm" for Windows Vista start menu integration
      for (var i = cmdLine.length - 1; i >= 0; --i) {
        var param = cmdLine.getArgument(i);
        if (param.match(/^\? /)) {
          cmdLine.removeArguments(i, i);
          cmdLine.preventDefault = true;

          searchParam = param.substr(2);
          doSearch(searchParam, cmdLine);
        }
      }
    }
  },

  get helpInfo() {
    let info =
              "  --browser          Open a browser window.\n" +
              "  --new-window <url> Open <url> in a new window.\n" +
              "  --new-tab <url>    Open <url> in a new tab.\n" +
              "  --private-window <url> Open <url> in a new private window.\n";
    if (AppConstants.platform == "win") {
      info += "  --preferences      Open Options dialog.\n";
    } else {
      info += "  --preferences      Open Preferences dialog.\n";
    }
    info += "  --screenshot [<path>] Save screenshot to <path> or in working directory.\n";
    info += "  --window-size width[,height] Width and optionally height of screenshot.\n";
    info += "  --search <term>    Search <term> with your default search engine.\n";
    return info;
  },

  /* nsIBrowserHandler */

  get defaultArgs() {
    var prefb = Services.prefs;

    if (!gFirstWindow) {
      gFirstWindow = true;
      if (PrivateBrowsingUtils.isInTemporaryAutoStartMode) {
        return "about:privatebrowsing";
      }
    }

    var override;
    var overridePage = "";
    var additionalPage = "";
    var willRestoreSession = false;
    try {
      // Read the old value of homepage_override.mstone before
      // needHomepageOverride updates it, so that we can later add it to the
      // URL if we do end up showing an overridePage. This makes it possible
      // to have the overridePage's content vary depending on the version we're
      // upgrading from.
      let old_mstone = Services.prefs.getCharPref("browser.startup.homepage_override.mstone", "unknown");
      let old_buildId = Services.prefs.getCharPref("browser.startup.homepage_override.buildID", "unknown");
      override = needHomepageOverride(prefb);
      if (override != OVERRIDE_NONE) {
        switch (override) {
          case OVERRIDE_NEW_PROFILE:
            // New profile.
            overridePage = Services.urlFormatter.formatURLPref("startup.homepage_welcome_url");
            additionalPage = Services.urlFormatter.formatURLPref("startup.homepage_welcome_url.additional");
            // Turn on 'later run' pages for new profiles.
            LaterRun.enabled = true;
            break;
          case OVERRIDE_NEW_MSTONE:
            // Check whether we will restore a session. If we will, we assume
            // that this is an "update" session. This does not take crashes
            // into account because that requires waiting for the session file
            // to be read. If a crash occurs after updating, before restarting,
            // we may open the startPage in addition to restoring the session.
            var ss = Cc["@mozilla.org/browser/sessionstartup;1"]
                       .getService(Ci.nsISessionStartup);
            willRestoreSession = ss.isAutomaticRestoreEnabled();

            overridePage = Services.urlFormatter.formatURLPref("startup.homepage_override_url");
            if (prefb.prefHasUserValue("app.update.postupdate")) {
              overridePage = getPostUpdateOverridePage(overridePage);
              // Send the update ping to signal that the update was successful.
              UpdatePing.handleUpdateSuccess(old_mstone, old_buildId);
            }

            overridePage = overridePage.replace("%OLD_VERSION%", old_mstone);
            break;
          case OVERRIDE_NEW_BUILD_ID:
            if (prefb.prefHasUserValue("app.update.postupdate")) {
              // Send the update ping to signal that the update was successful.
              UpdatePing.handleUpdateSuccess(old_mstone, old_buildId);
            }
            break;
        }
      }
    } catch (ex) {}

    // formatURLPref might return "about:blank" if getting the pref fails
    if (overridePage == "about:blank")
      overridePage = "";

    if (!additionalPage) {
      additionalPage = LaterRun.getURL() || "";
    }

    if (additionalPage && additionalPage != "about:blank") {
      if (overridePage) {
        overridePage += "|" + additionalPage;
      } else {
        overridePage = additionalPage;
      }
    }

    var startPage = "";
    try {
      var choice = prefb.getIntPref("browser.startup.page");
      if (choice == 1 || choice == 3)
        startPage = this.startPage;
    } catch (e) {
      Cu.reportError(e);
    }

    if (startPage == "about:blank")
      startPage = "";

    let skipStartPage = override == OVERRIDE_NEW_PROFILE &&
      prefb.getBoolPref("browser.startup.firstrunSkipsHomepage");
    // Only show the startPage if we're not restoring an update session and are
    // not set to skip the start page on this profile
    if (overridePage && startPage && !willRestoreSession && !skipStartPage)
      return overridePage + "|" + startPage;

    return overridePage || startPage || "about:blank";
  },

  get startPage() {
    var uri = Services.prefs.getComplexValue("browser.startup.homepage",
                                             nsIPrefLocalizedString).data;
    if (!uri) {
      Services.prefs.clearUserPref("browser.startup.homepage");
      uri = Services.prefs.getComplexValue("browser.startup.homepage",
                                           nsIPrefLocalizedString).data;
    }
    return uri;
  },

  mFeatures: null,

  getFeatures: function bch_features(cmdLine) {
    if (this.mFeatures === null) {
      this.mFeatures = "";

      try {
        var width = cmdLine.handleFlagWithParam("width", false);
        var height = cmdLine.handleFlagWithParam("height", false);

        if (width)
          this.mFeatures += ",width=" + width;
        if (height)
          this.mFeatures += ",height=" + height;
      } catch (e) {
      }

      // The global PB Service consumes this flag, so only eat it in per-window
      // PB builds.
      if (PrivateBrowsingUtils.isInTemporaryAutoStartMode) {
        this.mFeatures += ",private";
      }

      if (Services.prefs.getBoolPref("browser.suppress_first_window_animation") &&
          !Services.wm.getMostRecentWindow("navigator:browser")) {
        this.mFeatures += ",suppressanimation";
      }
    }

    return this.mFeatures;
  },

  /* nsIContentHandler */

  handleContent: function bch_handleContent(contentType, context, request) {
    try {
      var webNavInfo = Cc["@mozilla.org/webnavigation-info;1"]
                         .getService(nsIWebNavigationInfo);
      if (!webNavInfo.isTypeSupported(contentType, null)) {
        throw NS_ERROR_WONT_HANDLE_CONTENT;
      }
    } catch (e) {
      throw NS_ERROR_WONT_HANDLE_CONTENT;
    }

    request.QueryInterface(nsIChannel);
    handURIToExistingBrowser(request.URI, nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW, null, false,
                             request.loadInfo.triggeringPrincipal);
    request.cancel(NS_BINDING_ABORTED);
  },

  /* nsICommandLineValidator */
  validate: function bch_validate(cmdLine) {
    // Other handlers may use osint so only handle the osint flag if the url
    // flag is also present and the command line is valid.
    var osintFlagIdx = cmdLine.findFlag("osint", false);
    var urlFlagIdx = cmdLine.findFlag("url", false);
    if (urlFlagIdx > -1 && (osintFlagIdx > -1 ||
        cmdLine.state == nsICommandLine.STATE_REMOTE_EXPLICIT)) {
      var urlParam = cmdLine.getArgument(urlFlagIdx + 1);
      if (cmdLine.length != urlFlagIdx + 2 || /firefoxurl:/i.test(urlParam))
        throw NS_ERROR_ABORT;
      var isDefault = false;
      try {
        var url = Services.urlFormatter.formatURLPref("app.support.baseURL") +
                  "win10-default-browser";
        if (urlParam == url) {
          isDefault = ShellService.isDefaultBrowser(false, false);
        }
      } catch (ex) {}
      if (isDefault) {
        // Firefox is already the default HTTP handler.
        // We don't have to show the instruction page.
        throw NS_ERROR_ABORT;
      }
      cmdLine.handleFlag("osint", false);
    }
  },
};
var gBrowserContentHandler = new nsBrowserContentHandler();

function handURIToExistingBrowser(uri, location, cmdLine, forcePrivate, triggeringPrincipal) {
  if (!shouldLoadURI(uri))
    return;

  // Unless using a private window is forced, open external links in private
  // windows only if we're in perma-private mode.
  var allowPrivate = forcePrivate || PrivateBrowsingUtils.permanentPrivateBrowsing;
  var navWin = RecentWindow.getMostRecentBrowserWindow({private: allowPrivate});
  if (!navWin) {
    // if we couldn't load it in an existing window, open a new one
    var features = "chrome,dialog=no,all" + gBrowserContentHandler.getFeatures(cmdLine);
    if (forcePrivate) {
      features += ",private";
    }
    openWindow(null, gBrowserContentHandler.chromeURL, "_blank", features, uri.spec);
    return;
  }

  var navNav = navWin.QueryInterface(nsIInterfaceRequestor)
                     .getInterface(nsIWebNavigation);
  var rootItem = navNav.QueryInterface(nsIDocShellTreeItem).rootTreeItem;
  var rootWin = rootItem.QueryInterface(nsIInterfaceRequestor)
                        .getInterface(nsIDOMWindow);
  var bwin = rootWin.QueryInterface(nsIDOMChromeWindow).browserDOMWindow;
  bwin.openURI(uri, null, location,
               nsIBrowserDOMWindow.OPEN_EXTERNAL, triggeringPrincipal);
}

function nsDefaultCommandLineHandler() {
}

nsDefaultCommandLineHandler.prototype = {
  classID: Components.ID("{47cd0651-b1be-4a0f-b5c4-10e5a573ef71}"),

  /* nsISupports */
  QueryInterface: function dch_QI(iid) {
    if (!iid.equals(nsISupports) &&
        !iid.equals(nsICommandLineHandler))
      throw Cr.NS_ERROR_NO_INTERFACE;

    return this;
  },

  _haveProfile: false,

  /* nsICommandLineHandler */
  handle: function dch_handle(cmdLine) {
    var urilist = [];

    if (AppConstants.platform == "win") {
      // If we don't have a profile selected yet (e.g. the Profile Manager is
      // displayed) we will crash if we open an url and then select a profile. To
      // prevent this handle all url command line flags and set the command line's
      // preventDefault to true to prevent the display of the ui. The initial
      // command line will be retained when nsAppRunner calls LaunchChild though
      // urls launched after the initial launch will be lost.
      if (!this._haveProfile) {
        try {
          // This will throw when a profile has not been selected.
          Services.dirsvc.get("ProfD", Ci.nsIFile);
          this._haveProfile = true;
        } catch (e) {
          while ((ar = cmdLine.handleFlagWithParam("url", false)));
          cmdLine.preventDefault = true;
        }
      }
    }

    try {
      var ar;
      while ((ar = cmdLine.handleFlagWithParam("url", false))) {
        var uri = resolveURIInternal(cmdLine, ar);
        urilist.push(uri);
      }
    } catch (e) {
      Cu.reportError(e);
    }

    if (cmdLine.findFlag("screenshot", true) != -1) {
      HeadlessShell.handleCmdLineArgs(cmdLine, urilist.filter(shouldLoadURI).map(u => u.spec));
      return;
    }

    for (let i = 0; i < cmdLine.length; ++i) {
      var curarg = cmdLine.getArgument(i);
      if (curarg.match(/^-/)) {
        Cu.reportError("Warning: unrecognized command line flag " + curarg + "\n");
        // To emulate the pre-nsICommandLine behavior, we ignore
        // the argument after an unrecognized flag.
        ++i;
      } else {
        try {
          urilist.push(resolveURIInternal(cmdLine, curarg));
        } catch (e) {
          Cu.reportError("Error opening URI '" + curarg + "' from the command line: " + e + "\n");
        }
      }
    }

    if (urilist.length) {
      if (cmdLine.state != nsICommandLine.STATE_INITIAL_LAUNCH &&
          urilist.length == 1) {
        // Try to find an existing window and load our URI into the
        // current tab, new tab, or new window as prefs determine.
        try {
          handURIToExistingBrowser(urilist[0], nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW, cmdLine, false,
                                   Services.scriptSecurityManager.getSystemPrincipal());
          return;
        } catch (e) {
        }
      }

      var URLlist = urilist.filter(shouldLoadURI).map(u => u.spec);
      if (URLlist.length) {
        openWindow(null, gBrowserContentHandler.chromeURL, "_blank",
                   "chrome,dialog=no,all" + gBrowserContentHandler.getFeatures(cmdLine),
                   URLlist);
      }

    } else if (!cmdLine.preventDefault) {
      if (AppConstants.isPlatformAndVersionAtLeast("win", "10") &&
          cmdLine.state != nsICommandLine.STATE_INITIAL_LAUNCH &&
          WindowsUIUtils.inTabletMode) {
        // In windows 10 tablet mode, do not create a new window, but reuse the existing one.
        let win = RecentWindow.getMostRecentBrowserWindow();
        if (win) {
          win.focus();
          return;
        }
      }
      if (cmdLine.state == nsICommandLine.STATE_INITIAL_LAUNCH) {
        let win = Services.wm.getMostRecentWindow("navigator:blank");
        if (win) {
          win.location = gBrowserContentHandler.chromeURL;
          win.arguments = [gBrowserContentHandler.defaultArgs];
          return;
        }
      }

      // Passing defaultArgs, so use NO_EXTERNAL_URIS
      openWindow(null, gBrowserContentHandler.chromeURL, "_blank",
                 "chrome,dialog=no,all" + gBrowserContentHandler.getFeatures(cmdLine),
                 gBrowserContentHandler.defaultArgs, NO_EXTERNAL_URIS);
    } else {
      // Need a better solution in the future to avoid opening the blank window
      // when command line parameters say we are not going to show a browser
      // window, but for now the blank window getting closed quickly (and
      // causing only a slight flicker) is better than leaving it open.
      let win = Services.wm.getMostRecentWindow("navigator:blank");
      if (win)
        win.close();
    }
  },

  helpInfo: "",
};

var components = [nsBrowserContentHandler, nsDefaultCommandLineHandler];
this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components);
PK
!<�W~n��!components/nsSetDefaultBrowser.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/*
 * --setDefaultBrowser commandline handler
 * Makes the current executable the "default browser".
 */

ChromeUtils.import("resource:///modules/ShellService.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");

function nsSetDefaultBrowser() {}

nsSetDefaultBrowser.prototype = {
  handle: function nsSetDefault_handle(aCmdline) {
    if (aCmdline.handleFlag("setDefaultBrowser", false)) {
      ShellService.setDefaultBrowser(true, true);
    }
  },

  helpInfo: "  --setDefaultBrowser Set this app as the default browser.\n",

  classID: Components.ID("{F57899D0-4E2C-4ac6-9E29-50C736103B0C}"),
  QueryInterface: XPCOMUtils.generateQI([Ci.nsICommandLineHandler]),
};

this.NSGetFactory = XPCOMUtils.generateNSGetFactory([nsSetDefaultBrowser]);
PK
!<q��r��components/devtools-startup.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 XPCOM component is loaded very early.
 * Be careful to lazy load dependencies as much as possible.
 *
 * It manages all the possible entry points for DevTools:
 * - Handles command line arguments like -jsconsole,
 * - Register all key shortcuts,
 * - Listen for "Web Developer" system menu opening, under "Tools",
 * - Inject the wrench icon in toolbar customization, which is used
 *   by the "Web Developer" list displayed in the hamburger menu,
 * - Register the JSON Viewer protocol handler.
 *
 * Only once any of these entry point is fired, this module ensures starting
 * core modules like 'devtools-browser.js' that hooks the browser windows
 * and ensure setting up tools.
 **/

"use strict";

const kDebuggerPrefs = [
  "devtools.debugger.remote-enabled",
  "devtools.chrome.enabled"
];

// If devtools.toolbar.visible is set to true, the developer toolbar should appear on
// startup.
const TOOLBAR_VISIBLE_PREF = "devtools.toolbar.visible";

const DEVTOOLS_ENABLED_PREF = "devtools.enabled";

const DEVTOOLS_POLICY_DISABLED_PREF = "devtools.policy.disabled";

const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", {});
ChromeUtils.defineModuleGetter(this, "Services",
                               "resource://gre/modules/Services.jsm");
ChromeUtils.defineModuleGetter(this, "AppConstants",
                               "resource://gre/modules/AppConstants.jsm");
ChromeUtils.defineModuleGetter(this, "CustomizableUI",
                               "resource:///modules/CustomizableUI.jsm");
ChromeUtils.defineModuleGetter(this, "CustomizableWidgets",
                               "resource:///modules/CustomizableWidgets.jsm");

XPCOMUtils.defineLazyGetter(this, "StartupBundle", function () {
  const url = "chrome://devtools-shim/locale/startup.properties";
  return Services.strings.createBundle(url);
});

XPCOMUtils.defineLazyGetter(this, "KeyShortcutsBundle", function () {
  const url = "chrome://devtools-shim/locale/key-shortcuts.properties";
  return Services.strings.createBundle(url);
});

XPCOMUtils.defineLazyGetter(this, "KeyShortcuts", function () {
  const isMac = AppConstants.platform == "macosx";

  // Common modifier shared by most key shortcuts
  const modifiers = isMac ? "accel,alt" : "accel,shift";

  // List of all key shortcuts triggering installation UI
  // `id` should match tool's id from client/definitions.js
  return [
    // The following keys are also registered in /client/menus.js
    // And should be synced.

    // Both are toggling the toolbox on the last selected panel
    // or the default one.
    {
      id: "toggleToolbox",
      shortcut: KeyShortcutsBundle.GetStringFromName("toggleToolbox.commandkey"),
      modifiers
    },
    // All locales are using F12
    {
      id: "toggleToolboxF12",
      shortcut: KeyShortcutsBundle.GetStringFromName("toggleToolboxF12.commandkey"),
      modifiers: "" // F12 is the only one without modifiers
    },
    // Toggle the visibility of the Developer Toolbar (=gcli)
    {
      id: "toggleToolbar",
      shortcut: KeyShortcutsBundle.GetStringFromName("toggleToolbar.commandkey"),
      modifiers: "shift"
    },
    // Open WebIDE window
    {
      id: "webide",
      shortcut: KeyShortcutsBundle.GetStringFromName("webide.commandkey"),
      modifiers: "shift"
    },
    // Open the Browser Toolbox
    {
      id: "browserToolbox",
      shortcut: KeyShortcutsBundle.GetStringFromName("browserToolbox.commandkey"),
      modifiers: "accel,alt,shift"
    },
    // Open the Browser Console
    {
      id: "browserConsole",
      shortcut: KeyShortcutsBundle.GetStringFromName("browserConsole.commandkey"),
      modifiers: "accel,shift"
    },
    // Toggle the Responsive Design Mode
    {
      id: "responsiveDesignMode",
      shortcut: KeyShortcutsBundle.GetStringFromName("responsiveDesignMode.commandkey"),
      modifiers
    },
    // Open ScratchPad window
    {
      id: "scratchpad",
      shortcut: KeyShortcutsBundle.GetStringFromName("scratchpad.commandkey"),
      modifiers: "shift"
    },

    // The following keys are also registered in /client/definitions.js
    // and should be synced.

    // Key for opening the Inspector
    {
      toolId: "inspector",
      shortcut: KeyShortcutsBundle.GetStringFromName("inspector.commandkey"),
      modifiers
    },
    // Key for opening the Web Console
    {
      toolId: "webconsole",
      shortcut: KeyShortcutsBundle.GetStringFromName("webconsole.commandkey"),
      modifiers
    },
    // Key for opening the Debugger
    {
      toolId: "jsdebugger",
      shortcut: KeyShortcutsBundle.GetStringFromName("debugger.commandkey"),
      modifiers
    },
    // Key for opening the Network Monitor
    {
      toolId: "netmonitor",
      shortcut: KeyShortcutsBundle.GetStringFromName("netmonitor.commandkey"),
      modifiers
    },
    // Key for opening the Style Editor
    {
      toolId: "styleeditor",
      shortcut: KeyShortcutsBundle.GetStringFromName("styleeditor.commandkey"),
      modifiers: "shift"
    },
    // Key for opening the Performance Panel
    {
      toolId: "performance",
      shortcut: KeyShortcutsBundle.GetStringFromName("performance.commandkey"),
      modifiers: "shift"
    },
    // Key for opening the Storage Panel
    {
      toolId: "storage",
      shortcut: KeyShortcutsBundle.GetStringFromName("storage.commandkey"),
      modifiers: "shift"
    },
    // Key for opening the DOM Panel
    {
      toolId: "dom",
      shortcut: KeyShortcutsBundle.GetStringFromName("dom.commandkey"),
      modifiers
    },
  ];
});

function DevToolsStartup() {
  this.onEnabledPrefChanged = this.onEnabledPrefChanged.bind(this);
  this.onWindowReady = this.onWindowReady.bind(this);
}

DevToolsStartup.prototype = {
  /**
   * Boolean flag to check if DevTools have been already initialized or not.
   * By initialized, we mean that its main modules are loaded.
   */
  initialized: false,

  /**
   * Boolean flag to check if the devtools initialization was already sent to telemetry.
   * We only want to record one devtools entry point per Firefox run, but we are not
   * interested in all the entry points (e.g. devtools.toolbar.visible).
   */
  recorded: false,

  /**
   * Flag that indicates if the developer toggle was already added to customizableUI.
   */
  developerToggleCreated: false,

  isDisabledByPolicy: function () {
    return Services.prefs.getBoolPref(DEVTOOLS_POLICY_DISABLED_PREF, false);
  },

  handle: function (cmdLine) {
    let flags = this.readCommandLineFlags(cmdLine);

    // handle() can be called after browser startup (e.g. opening links from other apps).
    let isInitialLaunch = cmdLine.state == Ci.nsICommandLine.STATE_INITIAL_LAUNCH;
    if (isInitialLaunch) {
      // Execute only on first launch of this browser instance.
      let hasDevToolsFlag = flags.console || flags.devtools || flags.debugger;
      this.setupEnabledPref(hasDevToolsFlag);

      // Store devtoolsFlag to check it later in onWindowReady.
      this.devtoolsFlag = flags.devtools;
      // Only top level Firefox Windows fire a browser-delayed-startup-finished event
      Services.obs.addObserver(this.onWindowReady, "browser-delayed-startup-finished");

      if (AppConstants.MOZ_DEV_EDITION && !this.isDisabledByPolicy()) {
        // On DevEdition, the developer toggle is displayed by default in the navbar area
        // and should be created before the first paint.
        this.hookDeveloperToggle();
      }

      // Update menu items when devtools.enabled changes.
      Services.prefs.addObserver(DEVTOOLS_ENABLED_PREF, this.onEnabledPrefChanged);
    }

    if (flags.console) {
      this.handleConsoleFlag(cmdLine);
    }
    if (flags.debugger) {
      this.handleDebuggerFlag(cmdLine);
    }

    if (flags.debuggerServer) {
      this.handleDebuggerServerFlag(cmdLine, flags.debuggerServer);
    }
  },

  readCommandLineFlags(cmdLine) {
    // All command line flags are disabled if DevTools are disabled by policy.
    if (this.isDisabledByPolicy()) {
      return { console: false, debugger: false, devtools: false, debuggerServer: false };
    }

    let console = cmdLine.handleFlag("jsconsole", false);
    let debuggerFlag = cmdLine.handleFlag("jsdebugger", false);
    let devtools = cmdLine.handleFlag("devtools", false);

    let debuggerServer;
    try {
      debuggerServer =
        cmdLine.handleFlagWithParam("start-debugger-server", false);
    } catch (e) {
      // We get an error if the option is given but not followed by a value.
      // By catching and trying again, the value is effectively optional.
      debuggerServer = cmdLine.handleFlag("start-debugger-server", false);
    }

    return { console, debugger: debuggerFlag, devtools, debuggerServer };
  },

  /**
   * Called when receiving the "browser-delayed-startup-finished" event for a new
   * top-level window.
   */
  onWindowReady(window) {
    if (this.isDisabledByPolicy()) {
      this.removeDevToolsMenus(window);
      return;
    }

    this.hookWindow(window);

    if (Services.prefs.getBoolPref(TOOLBAR_VISIBLE_PREF, false)) {
      // Loading devtools-browser will open the developer toolbar by also checking this
      // pref.
      this.initDevTools();
    }

    // This listener is called for all Firefox windows, but we want to execute some code
    // only once.
    if (!this._firstWindowReadyReceived) {
      this.onFirstWindowReady(window);
      this._firstWindowReadyReceived = true;
    }

    JsonView.initialize();
  },

  removeDevToolsMenus(window) {
    // This will hide the "Tools > Web Developer" menu.
    window.document.getElementById("webDeveloperMenu").setAttribute("hidden", "true");
    // This will hide the "Web Developer" item in the hamburger menu.
    window.document.getElementById("appMenu-developer-button").setAttribute("hidden",
      "true");
  },

  onFirstWindowReady(window) {
    if (this.devtoolsFlag) {
      this.handleDevToolsFlag(window);
    }

    // Wait until we get a window before sending a ping to telemetry to avoid slowing down
    // the startup phase.
    this.pingOnboardingTelemetry();
  },

  /**
   * Check if the user is being flagged as DevTools users or not. This probe should only
   * be logged once per profile.
   */
  pingOnboardingTelemetry() {
    // Only ping telemetry once per profile.
    let alreadyLoggedPref = "devtools.onboarding.telemetry.logged";
    if (Services.prefs.getBoolPref(alreadyLoggedPref)) {
      return;
    }

    let scalarId = "devtools.onboarding.is_devtools_user";
    Services.telemetry.scalarSet(scalarId, this.isDevToolsUser());
    Services.prefs.setBoolPref(alreadyLoggedPref, true);
  },

  /**
   * Register listeners to all possible entry points for Developer Tools.
   * But instead of implementing the actual actions, defer to DevTools codebase.
   * In most cases, it only needs to call this.initDevTools which handles the rest.
   * We do that to prevent loading any DevTools module until the user intent to use them.
   */
  hookWindow(window) {
    // Key Shortcuts need to be added on all the created windows.
    this.hookKeyShortcuts(window);

    // In some situations (e.g. starting Firefox with --jsconsole) DevTools will be
    // initialized before the first browser-delayed-startup-finished event is received.
    // We use a dedicated flag because we still need to hook the developer toggle.
    this.hookDeveloperToggle();

    // The developer menu hook only needs to be added if devtools have not been
    // initialized yet.
    if (!this.initialized) {
      this.hookWebDeveloperMenu(window);
    }

    this.createDevToolsEnableMenuItem(window);
    this.updateDevToolsMenuItems(window);
  },

  /**
   * Dynamically register a wrench icon in the customization menu.
   * You can use this button by right clicking on Firefox toolbar
   * and dragging it from the customization panel to the toolbar.
   * (i.e. this isn't displayed by default to users!)
   *
   * _But_, the "Web Developer" entry in the hamburger menu (the menu with
   * 3 horizontal lines), is using this "developer-button" view to populate
   * its menu. So we have to register this button for the menu to work.
   *
   * Also, this menu duplicates its own entries from the "Web Developer"
   * menu in the system menu, under "Tools" main menu item. The system
   * menu is being hooked by "hookWebDeveloperMenu" which ends up calling
   * devtools/client/framework/browser-menu to create the items for real,
   * initDevTools, from onViewShowing is also calling browser-menu.
   */
  hookDeveloperToggle() {
    if (this.developerToggleCreated) {
      return;
    }

    let id = "developer-button";
    let widget = CustomizableUI.getWidget(id);
    if (widget && widget.provider == CustomizableUI.PROVIDER_API) {
      return;
    }
    let item = {
      id: id,
      type: "view",
      viewId: "PanelUI-developer",
      shortcutId: "key_toggleToolbox",
      tooltiptext: "developer-button.tooltiptext2",
      onViewShowing: (event) => {
        if (Services.prefs.getBoolPref(DEVTOOLS_ENABLED_PREF)) {
          // If DevTools are enabled, initialize DevTools to create all menuitems in the
          // system menu before trying to copy them.
          this.initDevTools("HamburgerMenu");
        }

        // Populate the subview with whatever menuitems are in the developer
        // menu. We skip menu elements, because the menu panel has no way
        // of dealing with those right now.
        let doc = event.target.ownerDocument;

        let menu = doc.getElementById("menuWebDeveloperPopup");

        let itemsToDisplay = [...menu.children];
        // Hardcode the addition of the "work offline" menuitem at the bottom:
        itemsToDisplay.push({localName: "menuseparator", getAttribute: () => {}});
        itemsToDisplay.push(doc.getElementById("goOfflineMenuitem"));

        let developerItems = doc.getElementById("PanelUI-developerItems");
        CustomizableUI.clearSubview(developerItems);
        CustomizableUI.fillSubviewFromMenuItems(itemsToDisplay, developerItems);
      },
      onInit(anchor) {
        // Since onBeforeCreated already bails out when initialized, we can call
        // it right away.
        this.onBeforeCreated(anchor.ownerDocument);
      },
      onBeforeCreated: (doc) => {
        // The developer toggle needs the "key_toggleToolbox" <key> element.
        // In DEV EDITION, the toggle is added before 1st paint and hookKeyShortcuts() is
        // not called yet when CustomizableUI creates the widget.
        this.hookKeyShortcuts(doc.defaultView);

        // Bug 1223127, CUI should make this easier to do.
        if (doc.getElementById("PanelUI-developerItems")) {
          return;
        }
        let view = doc.createElement("panelview");
        view.id = "PanelUI-developerItems";
        let panel = doc.createElement("vbox");
        panel.setAttribute("class", "panel-subview-body");
        view.appendChild(panel);
        doc.getElementById("PanelUI-multiView").appendChild(view);
      }
    };
    CustomizableUI.createWidget(item);
    CustomizableWidgets.push(item);

    this.developerToggleCreated = true;
  },

  /*
   * We listen to the "Web Developer" system menu, which is under "Tools" main item.
   * This menu item is hardcoded empty in Firefox UI. We listen for its opening to
   * populate it lazily. Loading main DevTools module is going to populate it.
   */
  hookWebDeveloperMenu(window) {
    let menu = window.document.getElementById("webDeveloperMenu");
    let onPopupShowing = () => {
      if (!Services.prefs.getBoolPref(DEVTOOLS_ENABLED_PREF)) {
        return;
      }
      menu.removeEventListener("popupshowing", onPopupShowing);
      this.initDevTools("SystemMenu");
    };
    menu.addEventListener("popupshowing", onPopupShowing);
  },

  /**
   * Create a new menu item to enable DevTools and insert it DevTools's submenu in the
   * System Menu.
   */
  createDevToolsEnableMenuItem(window) {
    let {document} = window;

    // Create the menu item.
    let item = document.createElement("menuitem");
    item.id = "enableDeveloperTools";
    item.setAttribute("label", StartupBundle.GetStringFromName("enableDevTools.label"));
    item.setAttribute("accesskey",
      StartupBundle.GetStringFromName("enableDevTools.accesskey"));

    // The menu item should open the install page for DevTools.
    item.addEventListener("command", () => {
      this.openInstallPage("SystemMenu");
    });

    // Insert the menu item in the DevTools submenu.
    let systemMenuItem = document.getElementById("menuWebDeveloperPopup");
    systemMenuItem.appendChild(item);
  },

  /**
   * Update the visibility the menu item to enable DevTools.
   */
  updateDevToolsMenuItems(window) {
    let item = window.document.getElementById("enableDeveloperTools");
    item.hidden = Services.prefs.getBoolPref(DEVTOOLS_ENABLED_PREF);
  },

  /**
   * Loop on all windows and update the hidden attribute of the "enable DevTools" menu
   * item.
   */
  onEnabledPrefChanged() {
    let enumerator = Services.wm.getEnumerator("navigator:browser");
    while (enumerator.hasMoreElements()) {
      let window = enumerator.getNext();
      if (window.gBrowserInit && window.gBrowserInit.delayedStartupFinished) {
        this.updateDevToolsMenuItems(window);
      }
    }
  },

  /**
   * Check if the user is a DevTools user by looking at our selfxss pref.
   * This preference is incremented everytime the console is used (up to 5).
   *
   * @return {Boolean} true if the user can be considered as a devtools user.
   */
  isDevToolsUser() {
    let selfXssCount = Services.prefs.getIntPref("devtools.selfxss.count", 0);
    return selfXssCount > 0;
  },

  /**
   * Depending on some runtime parameters (command line arguments as well as existing
   * preferences), the DEVTOOLS_ENABLED_PREF might be forced to true.
   *
   * @param {Boolean} hasDevToolsFlag
   *        true if any DevTools command line argument was passed when starting Firefox.
   */
  setupEnabledPref(hasDevToolsFlag) {
    // Read the current experiment state.
    let experimentState = Services.prefs.getCharPref("devtools.onboarding.experiment");
    let isRegularExperiment = experimentState == "on";
    let isForcedExperiment = experimentState == "force";
    let isInExperiment = isRegularExperiment || isForcedExperiment;

    // Force devtools.enabled to true for users that are not part of the experiment.
    if (!isInExperiment) {
      Services.prefs.setBoolPref(DEVTOOLS_ENABLED_PREF, true);
      return;
    }

    // Force devtools.enabled to false once for each experiment user.
    if (!Services.prefs.getBoolPref("devtools.onboarding.experiment.flipped")) {
      Services.prefs.setBoolPref(DEVTOOLS_ENABLED_PREF, false);
      Services.prefs.setBoolPref("devtools.onboarding.experiment.flipped", true);
    }

    if (Services.prefs.getBoolPref(DEVTOOLS_ENABLED_PREF)) {
      // Nothing to do if DevTools are already enabled.
      return;
    }

    // We only consider checking the actual isDevToolsUser() if the user is in the
    // "regular" experiment group.
    let isDevToolsUser = isRegularExperiment && this.isDevToolsUser();

    let hasToolbarPref = Services.prefs.getBoolPref(TOOLBAR_VISIBLE_PREF, false);
    if (hasDevToolsFlag || hasToolbarPref || isDevToolsUser) {
      Services.prefs.setBoolPref(DEVTOOLS_ENABLED_PREF, true);
    }
  },

  hookKeyShortcuts(window) {
    let doc = window.document;

    // hookKeyShortcuts can be called both from hookWindow and from the developer toggle
    // onBeforeCreated. Make sure shortcuts are only added once per window.
    if (doc.getElementById("devtoolsKeyset")) {
      return;
    }

    let keyset = doc.createElement("keyset");
    keyset.setAttribute("id", "devtoolsKeyset");

    for (let key of KeyShortcuts) {
      let xulKey = this.createKey(doc, key, () => this.onKey(window, key));
      keyset.appendChild(xulKey);
    }

    // Appending a <key> element is not always enough. The <keyset> needs
    // to be detached and reattached to make sure the <key> is taken into
    // account (see bug 832984).
    let mainKeyset = doc.getElementById("mainKeyset");
    mainKeyset.parentNode.insertBefore(keyset, mainKeyset);
  },

  onKey(window, key) {
    if (!Services.prefs.getBoolPref(DEVTOOLS_ENABLED_PREF)) {
      let id = key.toolId || key.id;
      this.openInstallPage("KeyShortcut", id);
    } else {
      // Record the timing at which this event started in order to compute later in
      // gDevTools.showToolbox, the complete time it takes to open the toolbox.
      // i.e. especially take `initDevTools` into account.
      let startTime = window.performance.now();
      let require = this.initDevTools("KeyShortcut");
      let { gDevToolsBrowser } = require("devtools/client/framework/devtools-browser");
      gDevToolsBrowser.onKeyShortcut(window, key, startTime);
    }
  },

  // Create a <xul:key> DOM Element
  createKey(doc, { id, toolId, shortcut, modifiers: mod }, oncommand) {
    let k = doc.createElement("key");
    k.id = "key_" + (id || toolId);

    if (shortcut.startsWith("VK_")) {
      k.setAttribute("keycode", shortcut);
    } else {
      k.setAttribute("key", shortcut);
    }

    if (mod) {
      k.setAttribute("modifiers", mod);
    }

    // Bug 371900: command event is fired only if "oncommand" attribute is set.
    k.setAttribute("oncommand", ";");
    k.addEventListener("command", oncommand);

    return k;
  },

  initDevTools: function (reason) {
    // If an entry point is fired and tools are not enabled open the installation page
    if (!Services.prefs.getBoolPref(DEVTOOLS_ENABLED_PREF)) {
      this.openInstallPage(reason);
      return null;
    }

    if (reason && !this.recorded) {
      // Only save the first call for each firefox run as next call
      // won't necessarely start the tool. For example key shortcuts may
      // only change the currently selected tool.
      try {
        Services.telemetry.getHistogramById("DEVTOOLS_ENTRY_POINT")
                          .add(reason);
      } catch (e) {
        dump("DevTools telemetry entry point failed: " + e + "\n");
      }
      this.recorded = true;
    }

    this.initialized = true;
    let { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
    // Ensure loading main devtools module that hooks up into browser UI
    // and initialize all devtools machinery.
    require("devtools/client/framework/devtools-browser");
    return require;
  },

  /**
   * Open about:devtools to start the onboarding flow.
   *
   * @param {String} reason
   *        One of "KeyShortcut", "SystemMenu", "HamburgerMenu", "ContextMenu",
   *        "CommandLine".
   * @param {String} keyId
   *        Optional. If the onboarding flow was triggered by a keyboard shortcut, pass
   *        the shortcut key id (or toolId) to about:devtools.
   */
  openInstallPage: function (reason, keyId) {
    // If DevTools are completely disabled, bail out here as this might be called directly
    // from other files.
    if (this.isDisabledByPolicy()) {
      return;
    }

    let { gBrowser } = Services.wm.getMostRecentWindow("navigator:browser");

    // Focus about:devtools tab if there is already one opened in the current window.
    for (let tab of gBrowser.tabs) {
      let browser = tab.linkedBrowser;
      // browser.documentURI might be undefined if the browser tab is still loading.
      let location = browser.documentURI ? browser.documentURI.spec : "";
      if (location.startsWith("about:devtools") &&
          !location.startsWith("about:devtools-toolbox")) {
        // Focus the existing about:devtools tab and bail out.
        gBrowser.selectedTab = tab;
        return;
      }
    }

    let url = "about:devtools";

    let params = [];
    if (reason) {
      params.push("reason=" + encodeURIComponent(reason));
    }

    let selectedBrowser = gBrowser.selectedBrowser;
    if (selectedBrowser) {
      params.push("tabid=" + selectedBrowser.outerWindowID);
    }

    if (keyId) {
      params.push("keyid=" + keyId);
    }

    if (params.length > 0) {
      url += "?" + params.join("&");
    }

    // Set relatedToCurrent: true to open the tab next to the current one.
    gBrowser.selectedTab = gBrowser.addTab(url, {relatedToCurrent: true});
  },

  handleConsoleFlag: function (cmdLine) {
    let window = Services.wm.getMostRecentWindow("devtools:webconsole");
    if (!window) {
      let require = this.initDevTools("CommandLine");
      let { HUDService } = require("devtools/client/webconsole/hudservice");
      HUDService.toggleBrowserConsole().catch(console.error);
    } else {
      // the Browser Console was already open
      window.focus();
    }

    if (cmdLine.state == Ci.nsICommandLine.STATE_REMOTE_AUTO) {
      cmdLine.preventDefault = true;
    }
  },

  // Open the toolbox on the selected tab once the browser starts up.
  handleDevToolsFlag: function (window) {
    const require = this.initDevTools("CommandLine");
    const {gDevTools} = require("devtools/client/framework/devtools");
    const {TargetFactory} = require("devtools/client/framework/target");
    let target = TargetFactory.forTab(window.gBrowser.selectedTab);
    gDevTools.showToolbox(target);
  },

  _isRemoteDebuggingEnabled() {
    let remoteDebuggingEnabled = false;
    try {
      remoteDebuggingEnabled = kDebuggerPrefs.every(pref => {
        return Services.prefs.getBoolPref(pref);
      });
    } catch (ex) {
      console.error(ex);
      return false;
    }
    if (!remoteDebuggingEnabled) {
      let errorMsg = "Could not run chrome debugger! You need the following " +
                     "prefs to be set to true: " + kDebuggerPrefs.join(", ");
      console.error(new Error(errorMsg));
      // Dump as well, as we're doing this from a commandline, make sure people
      // don't miss it:
      dump(errorMsg + "\n");
    }
    return remoteDebuggingEnabled;
  },

  handleDebuggerFlag: function (cmdLine) {
    if (!this._isRemoteDebuggingEnabled()) {
      return;
    }

    let devtoolsThreadResumed = false;
    let pauseOnStartup = cmdLine.handleFlag("wait-for-jsdebugger", false);
    if (pauseOnStartup) {
      let observe = function (subject, topic, data) {
        devtoolsThreadResumed = true;
        Services.obs.removeObserver(observe, "devtools-thread-resumed");
      };
      Services.obs.addObserver(observe, "devtools-thread-resumed");
    }

    const { BrowserToolboxProcess } = ChromeUtils.import("resource://devtools/client/framework/ToolboxProcess.jsm", {});
    BrowserToolboxProcess.init();

    if (pauseOnStartup) {
      // Spin the event loop until the debugger connects.
      let tm = Cc["@mozilla.org/thread-manager;1"].getService();
      tm.spinEventLoopUntil(() => {
        return devtoolsThreadResumed;
      });
    }

    if (cmdLine.state == Ci.nsICommandLine.STATE_REMOTE_AUTO) {
      cmdLine.preventDefault = true;
    }
  },

  /**
   * Handle the --start-debugger-server command line flag. The options are:
   * --start-debugger-server
   *   The portOrPath parameter is boolean true in this case. Reads and uses the defaults
   *   from devtools.debugger.remote-port and devtools.debugger.remote-websocket prefs.
   *   The default values of these prefs are port 6000, WebSocket disabled.
   *
   * --start-debugger-server 6789
   *   Start the non-WebSocket server on port 6789.
   *
   * --start-debugger-server /path/to/filename
   *   Start the server on a Unix domain socket.
   *
   * --start-debugger-server ws:6789
   *   Start the WebSocket server on port 6789.
   *
   * --start-debugger-server ws:
   *   Start the WebSocket server on the default port (taken from d.d.remote-port)
   */
  handleDebuggerServerFlag: function (cmdLine, portOrPath) {
    if (!this._isRemoteDebuggingEnabled()) {
      return;
    }

    let webSocket = false;
    let defaultPort = Services.prefs.getIntPref("devtools.debugger.remote-port");
    if (portOrPath === true) {
      // Default to pref values if no values given on command line
      webSocket = Services.prefs.getBoolPref("devtools.debugger.remote-websocket");
      portOrPath = defaultPort;
    } else if (portOrPath.startsWith("ws:")) {
      webSocket = true;
      let port = portOrPath.slice(3);
      portOrPath = Number(port) ? port : defaultPort;
    }

    let { DevToolsLoader } =
      ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});

    try {
      // Create a separate loader instance, so that we can be sure to receive
      // a separate instance of the DebuggingServer from the rest of the
      // devtools.  This allows us to safely use the tools against even the
      // actors and DebuggingServer itself, especially since we can mark
      // serverLoader as invisible to the debugger (unlike the usual loader
      // settings).
      let serverLoader = new DevToolsLoader();
      serverLoader.invisibleToDebugger = true;
      let { DebuggerServer: debuggerServer } =
        serverLoader.require("devtools/server/main");
      debuggerServer.init();
      debuggerServer.registerAllActors();
      debuggerServer.allowChromeProcess = true;

      let listener = debuggerServer.createListener();
      listener.portOrPath = portOrPath;
      listener.webSocket = webSocket;
      listener.open();
      dump("Started debugger server on " + portOrPath + "\n");
    } catch (e) {
      dump("Unable to start debugger server on " + portOrPath + ": " + e);
    }

    if (cmdLine.state == Ci.nsICommandLine.STATE_REMOTE_AUTO) {
      cmdLine.preventDefault = true;
    }
  },

  // Used by tests and the toolbox to register the same key shortcuts in toolboxes loaded
  // in a window window.
  get KeyShortcuts() {
    return KeyShortcuts;
  },
  get wrappedJSObject() {
    return this;
  },

  /* eslint-disable max-len */
  helpInfo: "  --jsconsole        Open the Browser Console.\n" +
            "  --jsdebugger       Open the Browser Toolbox.\n" +
            "  --wait-for-jsdebugger Spin event loop until JS debugger connects.\n" +
            "                     Enables debugging (some) application startup code paths.\n" +
            "                     Only has an effect when `--jsdebugger` is also supplied.\n" +
            "  --devtools         Open DevTools on initial load.\n" +
            "  --start-debugger-server [ws:][ <port> | <path> ] Start the debugger server on\n" +
            "                     a TCP port or Unix domain socket path. Defaults to TCP port\n" +
            "                     6000. Use WebSocket protocol if ws: prefix is specified.\n",
  /* eslint-disable max-len */

  classID: Components.ID("{9e9a9283-0ce9-4e4a-8f1c-ba129a032c32}"),
  QueryInterface: XPCOMUtils.generateQI([Ci.nsICommandLineHandler]),
};

/**
 * Singleton object that represents the JSON View in-content tool.
 * It has the same lifetime as the browser.
 */
const JsonView = {
  initialized: false,

  initialize: function () {
    // Prevent loading the frame script multiple times if we call this more than once.
    if (this.initialized) {
      return;
    }
    this.initialized = true;

    // Load JSON converter module. This converter is responsible
    // for handling 'application/json' documents and converting
    // them into a simple web-app that allows easy inspection
    // of the JSON data.
    Services.ppmm.loadProcessScript(
      "resource://devtools/client/jsonview/converter-observer.js",
      true);

    // Register for messages coming from the child process.
    // This is never removed as there is no particular need to unregister
    // it during shutdown.
    Services.ppmm.addMessageListener(
      "devtools:jsonview:save", this.onSave);
  },

  // Message handlers for events from child processes

  /**
   * Save JSON to a file needs to be implemented here
   * in the parent process.
   */
  onSave: function (message) {
    let chrome = Services.wm.getMostRecentWindow("navigator:browser");
    let browser = chrome.gBrowser.selectedBrowser;
    if (message.data === null) {
      // Save original contents
      chrome.saveBrowser(browser);
    } else {
      // The following code emulates saveBrowser, but:
      // - Uses the given blob URL containing the custom contents to save.
      // - Obtains the file name from the URL of the document, not the blob.
      let persistable = browser.frameLoader;
      persistable.startPersistence(0, {
        onDocumentReady(doc) {
          let uri = chrome.makeURI(doc.documentURI, doc.characterSet);
          let filename = chrome.getDefaultFileName(undefined, uri, doc, null);
          chrome.internalSave(message.data, doc, filename, null, doc.contentType,
            false, null, null, null, doc, false, null, undefined);
        },
        onError(status) {
          throw new Error("JSON Viewer's onSave failed in startPersistence");
        }
      });
    }
  }
};

this.NSGetFactory = XPCOMUtils.generateNSGetFactory(
  [DevToolsStartup]);
PK
!<{6l��)components/aboutdebugging-registration.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

// Register the about:debugging URL, that allows to debug various targets such as addons,
// workers and tabs by launching a dedicated DevTools toolbox for the selected target.
// If DevTools are not installed, this about page will display a shim landing page
// encouraging the user to download and install DevTools.
const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", {});
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm", {});

const { nsIAboutModule } = Ci;

function AboutDebugging() {}

AboutDebugging.prototype = {
  uri: Services.io.newURI("chrome://devtools/content/aboutdebugging/aboutdebugging.xhtml"),
  classDescription: "about:debugging",
  classID: Components.ID("1060afaf-dc9e-43da-8646-23a2faf48493"),
  contractID: "@mozilla.org/network/protocol/about;1?what=debugging",

  QueryInterface: XPCOMUtils.generateQI([nsIAboutModule]),

  newChannel: function (uri, loadInfo) {
    let chan = Services.io.newChannelFromURIWithLoadInfo(
      this.uri,
      loadInfo
    );
    chan.owner = Services.scriptSecurityManager.getSystemPrincipal();
    return chan;
  },

  getURIFlags: function (uri) {
    return nsIAboutModule.ALLOW_SCRIPT;
  }
};

this.NSGetFactory = XPCOMUtils.generateNSGetFactory([
  AboutDebugging
]);
PK
!<����  (components/aboutdevtools-registration.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

// Register the about:devtools URL, that is opened whenever a user attempts to open
// DevTools for the first time.
const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", {});
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm", {});

const { nsIAboutModule } = Ci;

function AboutDevtools() {}

AboutDevtools.prototype = {
  uri: Services.io.newURI("chrome://devtools-shim/content/aboutdevtools/aboutdevtools.xhtml"),
  classDescription: "about:devtools",
  classID: Components.ID("3a16d383-92bd-4c24-ac10-0e2bd66883ab"),
  contractID: "@mozilla.org/network/protocol/about;1?what=devtools",

  QueryInterface: XPCOMUtils.generateQI([nsIAboutModule]),

  newChannel: function (uri, loadInfo) {
    let chan = Services.io.newChannelFromURIWithLoadInfo(
      this.uri,
      loadInfo
    );
    chan.owner = Services.scriptSecurityManager.getSystemPrincipal();
    return chan;
  },

  getURIFlags: function (uri) {
    return nsIAboutModule.ALLOW_SCRIPT;
  }
};

this.NSGetFactory = XPCOMUtils.generateNSGetFactory([
  AboutDevtools
]);
PK
!<�he��/components/aboutdevtoolstoolbox-registration.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

// Register about:devtools-toolbox which allows to open a devtools toolbox
// in a Firefox tab or a custom html iframe in browser.html

const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", {});
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm", {});

const { nsIAboutModule } = Ci;

function AboutDevtoolsToolbox() {}

AboutDevtoolsToolbox.prototype = {
  uri: Services.io.newURI("chrome://devtools/content/framework/toolbox.xul"),
  classDescription: "about:devtools-toolbox",
  classID: Components.ID("11342911-3135-45a8-8d71-737a2b0ad469"),
  contractID: "@mozilla.org/network/protocol/about;1?what=devtools-toolbox",

  QueryInterface: XPCOMUtils.generateQI([nsIAboutModule]),

  newChannel: function (uri, loadInfo) {
    let chan = Services.io.newChannelFromURIWithLoadInfo(this.uri, loadInfo);
    chan.owner = Services.scriptSecurityManager.getSystemPrincipal();
    return chan;
  },

  getURIFlags: function (uri) {
    return nsIAboutModule.ALLOW_SCRIPT |
           nsIAboutModule.ENABLE_INDEXED_DB |
           nsIAboutModule.HIDE_FROM_ABOUTABOUT;
  }
};

this.NSGetFactory = XPCOMUtils.generateNSGetFactory([
  AboutDevtoolsToolbox
]);
PK
!<����!components/nsAboutCapabilities.js/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
ChromeUtils.defineModuleGetter(this, "AsyncPrefs",
  "resource://gre/modules/AsyncPrefs.jsm");

function nsAboutCapabilities() {
}

nsAboutCapabilities.prototype = {
  init(window) {
    this.window = window;
    try {
      let docShell = window.document.docShell
                           .QueryInterface(Ci.nsIInterfaceRequestor);
      this.mm = docShell.getInterface(Ci.nsIContentFrameMessageManager);
    } catch (e) {
      Cu.reportError(e);
    }
  },

  getBoolPref(aPref, aDefaultValue) {
    return Services.prefs.getBoolPref(aPref, aDefaultValue);
  },

  setBoolPref(aPref, aValue) {
    return new this.window.Promise(function(resolve) {
      AsyncPrefs.set(aPref, aValue).then(function() {
        resolve();
      });
    });
  },

  getCharPref(aPref, aDefaultValue) {
    return Services.prefs.getCharPref(aPref, aDefaultValue);
  },

  setCharPref(aPref, aValue) {
    return new this.window.Promise(function(resolve) {
      AsyncPrefs.set(aPref, aValue).then(function() {
        resolve();
      });
    });
  },

  getStringFromBundle(aStrBundle, aStr) {
    let bundle = Services.strings.createBundle(aStrBundle);
    return bundle.GetStringFromName(aStr);
  },

  formatURLPref(aFormatURL) {
    return Services.urlFormatter.formatURLPref(aFormatURL);
  },

  sendAsyncMessage(aMessage, aParams) {
    this.mm.sendAsyncMessage("AboutCapabilities:" + aMessage, aParams);
  },

  isWindowPrivate() {
    return PrivateBrowsingUtils.isContentWindowPrivate(this.window);
  },

  contractID: "@mozilla.org/aboutcapabilities;1",
  classID: Components.ID("{4c2b1f46-e637-4a91-8108-8a9fb7aab91d}"),
  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
                                         Ci.nsIDOMGlobalPropertyInitializer])
};

this.NSGetFactory = XPCOMUtils.generateNSGetFactory([nsAboutCapabilities]);
PK
!<��%RR components/ExperimentsService.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");

ChromeUtils.defineModuleGetter(this, "Experiments",
                               "resource:///modules/experiments/Experiments.jsm");
ChromeUtils.defineModuleGetter(this, "OS",
                               "resource://gre/modules/osfile.jsm");
ChromeUtils.defineModuleGetter(this, "CommonUtils",
                               "resource://services-common/utils.js");
ChromeUtils.defineModuleGetter(this, "TelemetryUtils",
                               "resource://gre/modules/TelemetryUtils.jsm");


const PREF_EXPERIMENTS_ENABLED  = "experiments.enabled";
const PREF_ACTIVE_EXPERIMENT    = "experiments.activeExperiment"; // whether we have an active experiment
const DELAY_INIT_MS             = 30 * 1000;

function ExperimentsService() {
  this._initialized = false;
  this._delayedInitTimer = null;
}

ExperimentsService.prototype = {
  classID: Components.ID("{f7800463-3b97-47f9-9341-b7617e6d8d49}"),
  QueryInterface: XPCOMUtils.generateQI([Ci.nsITimerCallback, Ci.nsIObserver]),

  get _experimentsEnabled() {
    // We can enable experiments if either unified Telemetry or FHR is on, and the user
    // has opted into Telemetry.
    return Services.prefs.getBoolPref(PREF_EXPERIMENTS_ENABLED, false) &&
           TelemetryUtils.isTelemetryEnabled;
  },

  notify(timer) {
    if (!this._experimentsEnabled) {
      return;
    }
    if (OS.Constants.Path.profileDir === undefined) {
      throw Error("Update timer fired before profile was initialized?");
    }
    let instance = Experiments.instance();
    if (instance.isReady) {
      instance.updateManifest().catch(error => {
        // Don't throw, as this breaks tests. In any case the best we can do here
        // is to log the failure.
        Cu.reportError(error);
      });
    }
  },

  _delayedInit() {
    if (!this._initialized) {
      this._initialized = true;
      Experiments.instance(); // for side effects
    }
  },

  observe(subject, topic, data) {
    switch (topic) {
      case "profile-after-change":
        if (this._experimentsEnabled) {
          Services.obs.addObserver(this, "quit-application");
          Services.obs.addObserver(this, "sessionstore-state-finalized");
          Services.obs.addObserver(this, "EM-loaded");

          if (Services.prefs.getBoolPref(PREF_ACTIVE_EXPERIMENT, false)) {
            this._initialized = true;
            Experiments.instance(); // for side effects
          }
        }
        break;
      case "sessionstore-state-finalized":
        if (!this._initialized) {
          CommonUtils.namedTimer(this._delayedInit, DELAY_INIT_MS, this, "_delayedInitTimer");
        }
        break;
      case "EM-loaded":
        if (!this._initialized) {
          Experiments.instance(); // for side effects
          this._initialized = true;

          if (this._delayedInitTimer) {
            this._delayedInitTimer.clear();
          }
        }
        break;
      case "quit-application":
        Services.obs.removeObserver(this, "quit-application");
        Services.obs.removeObserver(this, "sessionstore-state-finalized");
        Services.obs.removeObserver(this, "EM-loaded");
        if (this._delayedInitTimer) {
          this._delayedInitTimer.clear();
        }
        break;
    }
  },
};

this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ExperimentsService]);
PK
!<O��Lk"k" components/aboutNewTabService.js/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

"use strict";

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/AppConstants.jsm");

ChromeUtils.defineModuleGetter(this, "AboutNewTab",
                               "resource:///modules/AboutNewTab.jsm");

// Dummy references to the files that this service no longer allows loading.
// Bug 1409054 to remove "chrome://browser/content/abouthome/aboutHome.xhtml"

const TOPIC_APP_QUIT = "quit-application-granted";
const TOPIC_LOCALES_CHANGE = "intl:app-locales-changed";

// Automated tests ensure packaged locales are in this list. Copied output of:
// https://github.com/mozilla/activity-stream/blob/master/bin/render-activity-stream-html.js
const ACTIVITY_STREAM_LOCALES = "en-US ach an ar ast az be bg bn-BD bn-IN br bs ca cak crh cs cy da de dsb el en-GB eo es-AR es-CL es-ES es-MX et eu fa ff fi fr fy-NL ga-IE gd gl gn gu-IN he hi-IN hr hsb hu hy-AM ia id it ja ka kab kk km kn ko lij lo lt ltg lv mk ml mr ms my nb-NO ne-NP nl nn-NO pa-IN pl pt-BR pt-PT rm ro ru si sk sl sq sr sv-SE ta te th tl tr uk ur uz vi zh-CN zh-TW".split(" ");

const ABOUT_URL = "about:newtab";

const IS_MAIN_PROCESS = Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_DEFAULT;

const IS_RELEASE_OR_BETA = AppConstants.RELEASE_OR_BETA;

const PREF_ACTIVITY_STREAM_PRERENDER_ENABLED = "browser.newtabpage.activity-stream.prerender";
const PREF_ACTIVITY_STREAM_DEBUG = "browser.newtabpage.activity-stream.debug";


function AboutNewTabService() {
  Services.obs.addObserver(this, TOPIC_APP_QUIT);
  Services.obs.addObserver(this, TOPIC_LOCALES_CHANGE);
  Services.prefs.addObserver(PREF_ACTIVITY_STREAM_PRERENDER_ENABLED, this);
  if (!IS_RELEASE_OR_BETA) {
    Services.prefs.addObserver(PREF_ACTIVITY_STREAM_DEBUG, this);
  }

  // More initialization happens here
  this.toggleActivityStream(true);
  this.initialized = true;

  if (IS_MAIN_PROCESS) {
    AboutNewTab.init();
  }
}

/*
 * A service that allows for the overriding, at runtime, of the newtab page's url.
 *
 * There is tight coupling with browser/about/AboutRedirector.cpp.
 *
 * 1. Browser chrome access:
 *
 * When the user issues a command to open a new tab page, usually clicking a button
 * in the browser chrome or using shortcut keys, the browser chrome code invokes the
 * service to obtain the newtab URL. It then loads that URL in a new tab.
 *
 * When not overridden, the default URL emitted by the service is "about:newtab".
 * When overridden, it returns the overriden URL.
 *
 * 2. Redirector Access:
 *
 * When the URL loaded is about:newtab, the default behavior, or when entered in the
 * URL bar, the redirector is hit. The service is then called to return the
 * appropriate activity stream url based on prefs and locales.
 *
 * NOTE: "about:newtab" will always result in a default newtab page, and never an overridden URL.
 *
 * Access patterns:
 *
 * The behavior is different when accessing the service via browser chrome or via redirector
 * largely to maintain compatibility with expectations of add-on developers.
 *
 * Loading a chrome resource, or an about: URL in the redirector with either the
 * LOAD_NORMAL or LOAD_REPLACE flags yield unexpected behaviors, so a roundtrip
 * to the redirector from browser chrome is avoided.
 */
AboutNewTabService.prototype = {

  _newTabURL: ABOUT_URL,
  _activityStreamEnabled: false,
  _activityStreamPrerender: false,
  _activityStreamPath: "",
  _activityStreamDebug: false,
  _overridden: false,
  willNotifyUser: false,

  classID: Components.ID("{dfcd2adc-7867-4d3a-ba70-17501f208142}"),
  QueryInterface: XPCOMUtils.generateQI([
    Ci.nsIAboutNewTabService,
    Ci.nsIObserver
  ]),
  _xpcom_categories: [{
    service: true
  }],

  observe(subject, topic, data) {
    switch (topic) {
      case "nsPref:changed":
        if (data === PREF_ACTIVITY_STREAM_PRERENDER_ENABLED) {
          this._activityStreamPrerender = Services.prefs.getBoolPref(PREF_ACTIVITY_STREAM_PRERENDER_ENABLED);
          this.notifyChange();
        } else if (!IS_RELEASE_OR_BETA && data === PREF_ACTIVITY_STREAM_DEBUG) {
          this._activityStreamDebug = Services.prefs.getBoolPref(PREF_ACTIVITY_STREAM_DEBUG, false);
          this.updatePrerenderedPath();
          this.notifyChange();
        }
        break;
      case TOPIC_APP_QUIT:
        this.uninit();
        if (IS_MAIN_PROCESS) {
          AboutNewTab.uninit();
        }
        break;
      case TOPIC_LOCALES_CHANGE:
        this.updatePrerenderedPath();
        this.notifyChange();
        break;
    }
  },

  notifyChange() {
    Services.obs.notifyObservers(null, "newtab-url-changed", this._newTabURL);
  },

  /**
   * React to changes to the activity stream being enabled or not.
   *
   * This will only act if there is a change of state and if not overridden.
   *
   * @returns {Boolean} Returns if there has been a state change
   *
   * @param {Boolean}   stateEnabled    activity stream enabled state to set to
   * @param {Boolean}   forceState      force state change
   */
  toggleActivityStream(stateEnabled, forceState = false) {

    if (!forceState && (this.overridden || stateEnabled === this.activityStreamEnabled)) {
      // exit there is no change of state
      return false;
    }
    if (stateEnabled) {
      this._activityStreamEnabled = true;
    } else {
      this._activityStreamEnabled = false;
    }
    this._activityStreamPrerender = Services.prefs.getBoolPref(PREF_ACTIVITY_STREAM_PRERENDER_ENABLED);
    if (!IS_RELEASE_OR_BETA) {
      this._activityStreamDebug = Services.prefs.getBoolPref(PREF_ACTIVITY_STREAM_DEBUG, false);
    }
    this.updatePrerenderedPath();
    this._newtabURL = ABOUT_URL;
    return true;
  },

  /**
   * Figure out what path under prerendered to use based on current state.
   */
  updatePrerenderedPath() {
    // Debug files are specially packaged in a non-localized directory
    this._activityStreamPath = `${this._activityStreamDebug ? "static" :
      this.activityStreamLocale}/`;
  },

  /*
   * Returns the default URL.
   *
   * This URL depends on various activity stream prefs and locales. Overriding
   * the newtab page has no effect on the result of this function.
   */
  get defaultURL() {
    // Generate the desired activity stream resource depending on state, e.g.,
    // resource://activity-stream/prerendered/ar/activity-stream.html
    // resource://activity-stream/prerendered/en-US/activity-stream-prerendered.html
    // resource://activity-stream/prerendered/static/activity-stream-debug.html
    return [
      "resource://activity-stream/prerendered/",
      this._activityStreamPath,
      "activity-stream",
      this._activityStreamPrerender ? "-prerendered" : "",
      this._activityStreamDebug ? "-debug" : "",
      ".html"
    ].join("");
  },

  get newTabURL() {
    return this._newTabURL;
  },

  set newTabURL(aNewTabURL) {
    aNewTabURL = aNewTabURL.trim();
    if (aNewTabURL === ABOUT_URL) {
      // avoid infinite redirects in case one sets the URL to about:newtab
      this.resetNewTabURL();
      return;
    } else if (aNewTabURL === "") {
      aNewTabURL = "about:blank";
    }

    this.toggleActivityStream(false);
    this._newTabURL = aNewTabURL;
    this._overridden = true;
    this.notifyChange();
  },

  get overridden() {
    return this._overridden;
  },

  get activityStreamEnabled() {
    return this._activityStreamEnabled;
  },

  get activityStreamPrerender() {
    return this._activityStreamPrerender;
  },

  get activityStreamDebug() {
    return this._activityStreamDebug;
  },

  get activityStreamLocale() {
    // Pick the best available locale to match the app locales
    return Services.locale.negotiateLanguages(
      Services.locale.getAppLocalesAsLangTags(),
      ACTIVITY_STREAM_LOCALES,
      // defaultLocale's strings aren't necessarily packaged, but en-US' are
      "en-US"
    )[0];
  },

  resetNewTabURL() {
    this._overridden = false;
    this._newTabURL = ABOUT_URL;
    this.toggleActivityStream(true, true);
    this.notifyChange();
  },

  uninit() {
    if (!this.initialized) {
      return;
    }
    Services.obs.removeObserver(this, TOPIC_APP_QUIT);
    Services.obs.removeObserver(this, TOPIC_LOCALES_CHANGE);
    Services.prefs.removeObserver(PREF_ACTIVITY_STREAM_PRERENDER_ENABLED, this);
    if (!IS_RELEASE_OR_BETA) {
      Services.prefs.removeObserver(PREF_ACTIVITY_STREAM_DEBUG, this);
    }
    this.initialized = false;
  }
};

this.NSGetFactory = XPCOMUtils.generateNSGetFactory([AboutNewTabService]);
PK
!<�eDž�
�
'components/EnterprisePoliciesContent.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");

const PREF_LOGLEVEL           = "browser.policies.loglevel";

XPCOMUtils.defineLazyGetter(this, "log", () => {
  let { ConsoleAPI } = ChromeUtils.import("resource://gre/modules/Console.jsm", {});
  return new ConsoleAPI({
    prefix: "Enterprise Policies Child",
    // tip: set maxLogLevel to "debug" and use log.debug() to create detailed
    // messages during development. See LOG_LEVELS in Console.jsm for details.
    maxLogLevel: "error",
    maxLogLevelPref: PREF_LOGLEVEL,
  });
});


// ==== Start XPCOM Boilerplate ==== \\

// Factory object
const EnterprisePoliciesFactory = {
  _instance: null,
  createInstance: function BGSF_createInstance(outer, iid) {
    if (outer != null)
      throw Cr.NS_ERROR_NO_AGGREGATION;
    return this._instance == null ?
      this._instance = new EnterprisePoliciesManagerContent() : this._instance;
  }
};

// ==== End XPCOM Boilerplate ==== //


function EnterprisePoliciesManagerContent() {
  let policies = Services.cpmm.initialProcessData.policies;
  if (policies) {
    this._status = policies.status;
    // make a copy of the array so that we can keep adding to it
    // in a way that is not confusing.
    this._disallowedFeatures = policies.disallowedFeatures.slice();
  }

  Services.cpmm.addMessageListener("EnterprisePolicies:DisallowFeature", this);
  Services.cpmm.addMessageListener("EnterprisePolicies:Restart", this);
}

EnterprisePoliciesManagerContent.prototype = {
  // for XPCOM
  classID:          Components.ID("{dc6358f8-d167-4566-bf5b-4350b5e6a7a2}"),
  QueryInterface: XPCOMUtils.generateQI([Ci.nsIMessageListener,
                                         Ci.nsIEnterprisePolicies]),

  // redefine the default factory for XPCOMUtils
  _xpcom_factory: EnterprisePoliciesFactory,

  _status: Ci.nsIEnterprisePolicies.INACTIVE,

  _disallowedFeatures: [],

  receiveMessage({name, data}) {
    switch (name) {
      case "EnterprisePolicies:DisallowFeature":
        this._disallowedFeatures.push(data.feature);
        break;

      case "EnterprisePolicies:Restart":
        this._disallowedFeatures = [];
        break;
    }
  },

  get status() {
    return this._status;
  },

  isAllowed(feature) {
    return !this._disallowedFeatures.includes(feature);
  }
};

var components = [EnterprisePoliciesManagerContent];
this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components);
PK
!<����components/nsSessionStore.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

/**
 * Session Storage and Restoration
 *
 * Overview
 * This service keeps track of a user's session, storing the various bits
 * required to return the browser to its current state. The relevant data is
 * stored in memory, and is periodically saved to disk in a file in the
 * profile directory. The service is started at first window load, in
 * delayedStartup, and will restore the session from the data received from
 * the nsSessionStartup service.
 */

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource:///modules/sessionstore/SessionStore.jsm");

function SessionStoreService() {}

// The SessionStore module's object is frozen. We need to modify our prototype
// and add some properties so let's just copy the SessionStore object.
Object.keys(SessionStore).forEach(function(aName) {
  let desc = Object.getOwnPropertyDescriptor(SessionStore, aName);
  Object.defineProperty(SessionStoreService.prototype, aName, desc);
});

SessionStoreService.prototype.classID =
  Components.ID("{5280606b-2510-4fe0-97ef-9b5a22eafe6b}");
SessionStoreService.prototype.QueryInterface =
  XPCOMUtils.generateQI([Ci.nsISessionStore]);

this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SessionStoreService]);
PK
!<ၬ�		components/ProfileMigrator.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource:///modules/MigrationUtils.jsm");

function ProfileMigrator() {
}

ProfileMigrator.prototype = {
  migrate: MigrationUtils.startupMigration.bind(MigrationUtils),
  QueryInterface: XPCOMUtils.generateQI([Ci.nsIProfileMigrator]),
  classDescription: "Profile Migrator",
  contractID: "@mozilla.org/toolkit/profile-migrator;1",
  classID: Components.ID("6F8BB968-C14F-4D6F-9733-6C6737B35DCE"),
};

this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ProfileMigrator]);
PK
!<��/�JJ#components/ChromeProfileMigrator.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
 * vim: sw=2 ts=2 sts=2 et */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 S100NS_FROM1601TO1970 = 0x19DB1DED53E8000;
const S100NS_PER_MS = 10;

const AUTH_TYPE = {
  SCHEME_HTML: 0,
  SCHEME_BASIC: 1,
  SCHEME_DIGEST: 2,
};

ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
ChromeUtils.import("resource://gre/modules/osfile.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource:///modules/ChromeMigrationUtils.jsm");
ChromeUtils.import("resource:///modules/MigrationUtils.jsm");

ChromeUtils.defineModuleGetter(this, "PlacesUtils",
                               "resource://gre/modules/PlacesUtils.jsm");
ChromeUtils.defineModuleGetter(this, "OSCrypto",
                               "resource://gre/modules/OSCrypto.jsm");

/**
 * Convert Chrome time format to Date object
 *
 * @param   aTime
 *          Chrome time
 * @return  converted Date object
 * @note    Google Chrome uses FILETIME / 10 as time.
 *          FILETIME is based on same structure of Windows.
 */
function chromeTimeToDate(aTime) {
  return new Date((aTime * S100NS_PER_MS - S100NS_FROM1601TO1970) / 10000);
}

/**
 * Convert Date object to Chrome time format
 *
 * @param   aDate
 *          Date object or integer equivalent
 * @return  Chrome time
 * @note    For details on Chrome time, see chromeTimeToDate.
 */
function dateToChromeTime(aDate) {
  return (aDate * 10000 + S100NS_FROM1601TO1970) / S100NS_PER_MS;
}

/**
 * Converts an array of chrome bookmark objects into one our own places code
 * understands.
 *
 * @param   items
 *          bookmark items to be inserted on this parent
 * @param   errorAccumulator
 *          function that gets called with any errors thrown so we don't drop them on the floor.
 */
function convertBookmarks(items, errorAccumulator) {
  let itemsToInsert = [];
  for (let item of items) {
    try {
      if (item.type == "url") {
        if (item.url.trim().startsWith("chrome:")) {
          // Skip invalid chrome URIs. Creating an actual URI always reports
          // messages to the console, so we avoid doing that.
          continue;
        }
        itemsToInsert.push({url: item.url, title: item.name});
      } else if (item.type == "folder") {
        let folderItem = {type: PlacesUtils.bookmarks.TYPE_FOLDER, title: item.name};
        folderItem.children = convertBookmarks(item.children, errorAccumulator);
        itemsToInsert.push(folderItem);
      }
    } catch (ex) {
      Cu.reportError(ex);
      errorAccumulator(ex);
    }
  }
  return itemsToInsert;
}

function ChromeProfileMigrator() {
  this._chromeUserDataPathSuffix = "Chrome";
}

ChromeProfileMigrator.prototype = Object.create(MigratorPrototype);

ChromeProfileMigrator.prototype._getChromeUserDataPathIfExists = async function() {
  if (this._chromeUserDataPath) {
    return this._chromeUserDataPath;
  }
  let path = ChromeMigrationUtils.getDataPath(this._chromeUserDataPathSuffix);
  let exists = await OS.File.exists(path);
  if (exists) {
    this._chromeUserDataPath = path;
  } else {
    this._chromeUserDataPath = null;
  }
  return this._chromeUserDataPath;
};

ChromeProfileMigrator.prototype.getResources =
  async function Chrome_getResources(aProfile) {
    let chromeUserDataPath = await this._getChromeUserDataPathIfExists();
    if (chromeUserDataPath) {
      let profileFolder = OS.Path.join(chromeUserDataPath, aProfile.id);
      if (await OS.File.exists(profileFolder)) {
        let possibleResourcePromises = [
          GetBookmarksResource(profileFolder),
          GetHistoryResource(profileFolder),
          GetCookiesResource(profileFolder),
        ];
        if (AppConstants.platform == "win") {
          possibleResourcePromises.push(GetWindowsPasswordsResource(profileFolder));
        }
        let possibleResources = await Promise.all(possibleResourcePromises);
        return possibleResources.filter(r => r != null);
      }
    }
    return [];
  };

ChromeProfileMigrator.prototype.getLastUsedDate =
  async function Chrome_getLastUsedDate() {
    let sourceProfiles = await this.getSourceProfiles();
    let chromeUserDataPath = await this._getChromeUserDataPathIfExists();
    if (!chromeUserDataPath) {
      return new Date(0);
    }
    let datePromises = sourceProfiles.map(async profile => {
      let basePath = OS.Path.join(chromeUserDataPath, profile.id);
      let fileDatePromises = ["Bookmarks", "History", "Cookies"].map(async leafName => {
        let path = OS.Path.join(basePath, leafName);
        let info = await OS.File.stat(path).catch(() => null);
        return info ? info.lastModificationDate : 0;
      });
      let dates = await Promise.all(fileDatePromises);
      return Math.max(...dates);
    });
    let datesOuter = await Promise.all(datePromises);
    datesOuter.push(0);
    return new Date(Math.max(...datesOuter));
  };

ChromeProfileMigrator.prototype.getSourceProfiles =
  async function Chrome_getSourceProfiles() {
    if ("__sourceProfiles" in this)
      return this.__sourceProfiles;

    let chromeUserDataPath = await this._getChromeUserDataPathIfExists();
    if (!chromeUserDataPath)
      return [];

    let profiles = [];
    try {
      let localState = await ChromeMigrationUtils.getLocalState(this._chromeUserDataPathSuffix);
      let info_cache = localState.profile.info_cache;
      for (let profileFolderName in info_cache) {
        profiles.push({
          id: profileFolderName,
          name: info_cache[profileFolderName].name || profileFolderName,
        });
      }
    } catch (e) {
      Cu.reportError("Error detecting Chrome profiles: " + e);
      // If we weren't able to detect any profiles above, fallback to the Default profile.
      let defaultProfilePath = OS.Path.join(chromeUserDataPath, "Default");
      if (await OS.File.exists(defaultProfilePath)) {
        profiles = [{
          id: "Default",
          name: "Default",
        }];
      }
    }

    let profileResources = await Promise.all(profiles.map(async profile => ({
      profile,
      resources: await this.getResources(profile),
    })));

    // Only list profiles from which any data can be imported
    this.__sourceProfiles = profileResources.filter(({resources}) => {
      return resources && resources.length > 0;
    }, this).map(({profile}) => profile);
    return this.__sourceProfiles;
  };

Object.defineProperty(ChromeProfileMigrator.prototype, "sourceLocked", {
  get: function Chrome_sourceLocked() {
    // There is an exclusive lock on some SQLite databases. Assume they are locked for now.
    return true;
  },
});

async function GetBookmarksResource(aProfileFolder) {
  let bookmarksPath = OS.Path.join(aProfileFolder, "Bookmarks");
  if (!(await OS.File.exists(bookmarksPath)))
    return null;

  return {
    type: MigrationUtils.resourceTypes.BOOKMARKS,

    migrate(aCallback) {
      return (async function() {
        let gotErrors = false;
        let errorGatherer = function() { gotErrors = true; };
        // Parse Chrome bookmark file that is JSON format
        let bookmarkJSON = await OS.File.read(bookmarksPath, {encoding: "UTF-8"});
        let roots = JSON.parse(bookmarkJSON).roots;

        // Importing bookmark bar items
        if (roots.bookmark_bar.children &&
            roots.bookmark_bar.children.length > 0) {
          // Toolbar
          let parentGuid = PlacesUtils.bookmarks.toolbarGuid;
          let bookmarks = convertBookmarks(roots.bookmark_bar.children, errorGatherer);
          if (!MigrationUtils.isStartupMigration) {
            parentGuid =
              await MigrationUtils.createImportedBookmarksFolder("Chrome", parentGuid);
          }
          await MigrationUtils.insertManyBookmarksWrapper(bookmarks, parentGuid);
        }

        // Importing bookmark menu items
        if (roots.other.children &&
            roots.other.children.length > 0) {
          // Bookmark menu
          let parentGuid = PlacesUtils.bookmarks.menuGuid;
          let bookmarks = convertBookmarks(roots.other.children, errorGatherer);
          if (!MigrationUtils.isStartupMigration) {
            parentGuid
              = await MigrationUtils.createImportedBookmarksFolder("Chrome", parentGuid);
          }
          await MigrationUtils.insertManyBookmarksWrapper(bookmarks, parentGuid);
        }
        if (gotErrors) {
          throw new Error("The migration included errors.");
        }
      })().then(() => aCallback(true),
              () => aCallback(false));
    },
  };
}

async function GetHistoryResource(aProfileFolder) {
  let historyPath = OS.Path.join(aProfileFolder, "History");
  if (!(await OS.File.exists(historyPath)))
    return null;

  return {
    type: MigrationUtils.resourceTypes.HISTORY,

    migrate(aCallback) {
      (async function() {
        const MAX_AGE_IN_DAYS = Services.prefs.getIntPref("browser.migrate.chrome.history.maxAgeInDays");
        const LIMIT = Services.prefs.getIntPref("browser.migrate.chrome.history.limit");

        let query = "SELECT url, title, last_visit_time, typed_count FROM urls WHERE hidden = 0";
        if (MAX_AGE_IN_DAYS) {
          let maxAge = dateToChromeTime(Date.now() - MAX_AGE_IN_DAYS * 24 * 60 * 60 * 1000);
          query += " AND last_visit_time > " + maxAge;
        }
        if (LIMIT) {
          query += " ORDER BY last_visit_time DESC LIMIT " + LIMIT;
        }

        let rows =
          await MigrationUtils.getRowsFromDBWithoutLocks(historyPath, "Chrome history", query);
        let places = [];
        for (let row of rows) {
          try {
            // if having typed_count, we changes transition type to typed.
            let transType = PlacesUtils.history.TRANSITION_LINK;
            if (row.getResultByName("typed_count") > 0)
              transType = PlacesUtils.history.TRANSITION_TYPED;

            places.push({
              uri: NetUtil.newURI(row.getResultByName("url")),
              title: row.getResultByName("title"),
              visits: [{
                transitionType: transType,
                visitDate: chromeTimeToDate(
                             row.getResultByName(
                               "last_visit_time")) * 1000,
              }],
            });
          } catch (e) {
            Cu.reportError(e);
          }
        }

        if (places.length > 0) {
          await new Promise((resolve, reject) => {
            MigrationUtils.insertVisitsWrapper(places, {
              ignoreErrors: true,
              ignoreResults: true,
              handleCompletion(updatedCount) {
                if (updatedCount > 0) {
                  resolve();
                } else {
                  reject(new Error("Couldn't add visits"));
                }
              },
            });
          });
        }
      })().then(() => { aCallback(true); },
              ex => {
                Cu.reportError(ex);
                aCallback(false);
              });
    },
  };
}

async function GetCookiesResource(aProfileFolder) {
  let cookiesPath = OS.Path.join(aProfileFolder, "Cookies");
  if (!(await OS.File.exists(cookiesPath)))
    return null;

  return {
    type: MigrationUtils.resourceTypes.COOKIES,

    async migrate(aCallback) {
      // We don't support decrypting cookies yet so only import plaintext ones.
      let rows = await MigrationUtils.getRowsFromDBWithoutLocks(cookiesPath, "Chrome cookies",
       `SELECT host_key, name, value, path, expires_utc, secure, httponly, encrypted_value
        FROM cookies
        WHERE length(encrypted_value) = 0`).catch(ex => {
          Cu.reportError(ex);
          aCallback(false);
        });
      // If the promise was rejected we will have already called aCallback,
      // so we can just return here.
      if (!rows) {
        return;
      }

      for (let row of rows) {
        let host_key = row.getResultByName("host_key");
        if (host_key.match(/^\./)) {
          // 1st character of host_key may be ".", so we have to remove it
          host_key = host_key.substr(1);
        }

        try {
          let expiresUtc =
            chromeTimeToDate(row.getResultByName("expires_utc")) / 1000;
          Services.cookies.add(host_key,
                               row.getResultByName("path"),
                               row.getResultByName("name"),
                               row.getResultByName("value"),
                               row.getResultByName("secure"),
                               row.getResultByName("httponly"),
                               false,
                               parseInt(expiresUtc),
                               {});
        } catch (e) {
          Cu.reportError(e);
        }
      }
      aCallback(true);
    },
  };
}

async function GetWindowsPasswordsResource(aProfileFolder) {
  let loginPath = OS.Path.join(aProfileFolder, "Login Data");
  if (!(await OS.File.exists(loginPath)))
    return null;

  return {
    type: MigrationUtils.resourceTypes.PASSWORDS,

    async migrate(aCallback) {
      let rows = await MigrationUtils.getRowsFromDBWithoutLocks(loginPath, "Chrome passwords",
       `SELECT origin_url, action_url, username_element, username_value,
        password_element, password_value, signon_realm, scheme, date_created,
        times_used FROM logins WHERE blacklisted_by_user = 0`).catch(ex => {
          Cu.reportError(ex);
          aCallback(false);
        });
      // If the promise was rejected we will have already called aCallback,
      // so we can just return here.
      if (!rows) {
        return;
      }
      let crypto = new OSCrypto();
      let logins = [];
      for (let row of rows) {
        try {
          let origin_url = NetUtil.newURI(row.getResultByName("origin_url"));
          // Ignore entries for non-http(s)/ftp URLs because we likely can't
          // use them anyway.
          const kValidSchemes = new Set(["https", "http", "ftp"]);
          if (!kValidSchemes.has(origin_url.scheme)) {
            continue;
          }
          let loginInfo = {
            username: row.getResultByName("username_value"),
            password: crypto.
                      decryptData(crypto.arrayToString(row.getResultByName("password_value")),
                                                       null),
            hostname: origin_url.prePath,
            formSubmitURL: null,
            httpRealm: null,
            usernameElement: row.getResultByName("username_element"),
            passwordElement: row.getResultByName("password_element"),
            timeCreated: chromeTimeToDate(row.getResultByName("date_created") + 0).getTime(),
            timesUsed: row.getResultByName("times_used") + 0,
          };

          switch (row.getResultByName("scheme")) {
            case AUTH_TYPE.SCHEME_HTML:
              let action_url = NetUtil.newURI(row.getResultByName("action_url"));
              if (!kValidSchemes.has(action_url.scheme)) {
                continue; // This continues the outer for loop.
              }
              loginInfo.formSubmitURL = action_url.prePath;
              break;
            case AUTH_TYPE.SCHEME_BASIC:
            case AUTH_TYPE.SCHEME_DIGEST:
              // signon_realm format is URIrealm, so we need remove URI
              loginInfo.httpRealm = row.getResultByName("signon_realm")
                                       .substring(loginInfo.hostname.length + 1);
              break;
            default:
              throw new Error("Login data scheme type not supported: " +
                              row.getResultByName("scheme"));
          }
          logins.push(loginInfo);
        } catch (e) {
          Cu.reportError(e);
        }
      }
      try {
        if (logins.length > 0) {
          await MigrationUtils.insertLoginsWrapper(logins);
        }
      } catch (e) {
        Cu.reportError(e);
      }
      crypto.finalize();
      aCallback(true);
    },
  };
}

ChromeProfileMigrator.prototype.classDescription = "Chrome Profile Migrator";
ChromeProfileMigrator.prototype.contractID = "@mozilla.org/profile/migrator;1?app=browser&type=chrome";
ChromeProfileMigrator.prototype.classID = Components.ID("{4cec1de4-1671-4fc3-a53e-6c539dc77a26}");


/**
 *  Chromium migration
 **/
function ChromiumProfileMigrator() {
  this._chromeUserDataPathSuffix = "Chromium";
}

ChromiumProfileMigrator.prototype = Object.create(ChromeProfileMigrator.prototype);
ChromiumProfileMigrator.prototype.classDescription = "Chromium Profile Migrator";
ChromiumProfileMigrator.prototype.contractID = "@mozilla.org/profile/migrator;1?app=browser&type=chromium";
ChromiumProfileMigrator.prototype.classID = Components.ID("{8cece922-9720-42de-b7db-7cef88cb07ca}");

var componentsArray = [ChromeProfileMigrator, ChromiumProfileMigrator];

/**
 * Chrome Canary
 * Not available on Linux
 **/
function CanaryProfileMigrator() {
  this._chromeUserDataPathSuffix = "Canary";
}
CanaryProfileMigrator.prototype = Object.create(ChromeProfileMigrator.prototype);
CanaryProfileMigrator.prototype.classDescription = "Chrome Canary Profile Migrator";
CanaryProfileMigrator.prototype.contractID = "@mozilla.org/profile/migrator;1?app=browser&type=canary";
CanaryProfileMigrator.prototype.classID = Components.ID("{4bf85aa5-4e21-46ca-825f-f9c51a5e8c76}");

if (AppConstants.platform == "win" || AppConstants.platform == "macosx") {
  componentsArray.push(CanaryProfileMigrator);
}

/**
 * Chrome Dev / Unstable and Beta. Only separate from `regular` chrome on Linux
 */
if (AppConstants.platform != "win" && AppConstants.platform != "macosx") {
  function ChromeDevMigrator() {
    this._chromeUserDataPathSuffix = "Chrome Dev";
  }
  ChromeDevMigrator.prototype = Object.create(ChromeProfileMigrator.prototype);
  ChromeDevMigrator.prototype.classDescription = "Chrome Dev Profile Migrator";
  ChromeDevMigrator.prototype.contractID = "@mozilla.org/profile/migrator;1?app=browser&type=chrome-dev";
  ChromeDevMigrator.prototype.classID = Components.ID("{7370a02a-4886-42c3-a4ec-d48c726ec30a}");

  function ChromeBetaMigrator() {
    this._chromeUserDataPathSuffix = "Chrome Beta";
  }
  ChromeBetaMigrator.prototype = Object.create(ChromeProfileMigrator.prototype);
  ChromeBetaMigrator.prototype.classDescription = "Chrome Beta Profile Migrator";
  ChromeBetaMigrator.prototype.contractID = "@mozilla.org/profile/migrator;1?app=browser&type=chrome-beta";
  ChromeBetaMigrator.prototype.classID = Components.ID("{47f75963-840b-4950-a1f0-d9c1864f8b8e}");

  componentsArray.push(ChromeDevMigrator, ChromeBetaMigrator);
}

this.NSGetFactory = XPCOMUtils.generateNSGetFactory(componentsArray);
PK
!<m�ޓ�*�*$components/FirefoxProfileMigrator.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sw=2 ts=2 sts=2 et */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

/*
 * Migrates from a Firefox profile in a lossy manner in order to clean up a
 * user's profile.  Data is only migrated where the benefits outweigh the
 * potential problems caused by importing undesired/invalid configurations
 * from the source profile.
 */

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource:///modules/MigrationUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");

ChromeUtils.defineModuleGetter(this, "PlacesBackups",
                               "resource://gre/modules/PlacesBackups.jsm");
ChromeUtils.defineModuleGetter(this, "SessionMigration",
                               "resource:///modules/sessionstore/SessionMigration.jsm");
ChromeUtils.defineModuleGetter(this, "OS",
                               "resource://gre/modules/osfile.jsm");
ChromeUtils.defineModuleGetter(this, "FileUtils",
                               "resource://gre/modules/FileUtils.jsm");
ChromeUtils.defineModuleGetter(this, "ProfileAge",
                               "resource://gre/modules/ProfileAge.jsm");
ChromeUtils.defineModuleGetter(this, "AppConstants",
                               "resource://gre/modules/AppConstants.jsm");


function FirefoxProfileMigrator() {
  this.wrappedJSObject = this; // for testing...
}

FirefoxProfileMigrator.prototype = Object.create(MigratorPrototype);

FirefoxProfileMigrator.prototype._getAllProfiles = function() {
  let allProfiles = new Map();
  let profiles =
    Cc["@mozilla.org/toolkit/profile-service;1"]
      .getService(Ci.nsIToolkitProfileService)
      .profiles;
  while (profiles.hasMoreElements()) {
    let profile = profiles.getNext().QueryInterface(Ci.nsIToolkitProfile);
    let rootDir = profile.rootDir;

    if (rootDir.exists() && rootDir.isReadable() &&
        !rootDir.equals(MigrationUtils.profileStartup.directory)) {
      allProfiles.set(profile.name, rootDir);
    }
  }
  return allProfiles;
};

function sorter(a, b) {
  return a.id.toLocaleLowerCase().localeCompare(b.id.toLocaleLowerCase());
}

FirefoxProfileMigrator.prototype.getSourceProfiles = function() {
  return [...this._getAllProfiles().keys()].map(x => ({id: x, name: x})).sort(sorter);
};

FirefoxProfileMigrator.prototype._getFileObject = function(dir, fileName) {
  let file = dir.clone();
  file.append(fileName);

  // File resources are monolithic.  We don't make partial copies since
  // they are not expected to work alone. Return null to avoid trying to
  // copy non-existing files.
  return file.exists() ? file : null;
};

FirefoxProfileMigrator.prototype.getResources = function(aProfile) {
  let sourceProfileDir = aProfile ? this._getAllProfiles().get(aProfile.id) :
    Cc["@mozilla.org/toolkit/profile-service;1"]
      .getService(Ci.nsIToolkitProfileService)
      .selectedProfile.rootDir;
  if (!sourceProfileDir || !sourceProfileDir.exists() ||
      !sourceProfileDir.isReadable())
    return null;

  // Being a startup-only migrator, we can rely on
  // MigrationUtils.profileStartup being set.
  let currentProfileDir = MigrationUtils.profileStartup.directory;

  // Surely data cannot be imported from the current profile.
  if (sourceProfileDir.equals(currentProfileDir))
    return null;

  return this._getResourcesInternal(sourceProfileDir, currentProfileDir);
};

FirefoxProfileMigrator.prototype.getLastUsedDate = function() {
  // We always pretend we're really old, so that we don't mess
  // up the determination of which browser is the most 'recent'
  // to import from.
  return Promise.resolve(new Date(0));
};

FirefoxProfileMigrator.prototype._getResourcesInternal = function(sourceProfileDir, currentProfileDir) {
  let getFileResource = (aMigrationType, aFileNames) => {
    let files = [];
    for (let fileName of aFileNames) {
      let file = this._getFileObject(sourceProfileDir, fileName);
      if (file)
        files.push(file);
    }
    if (!files.length) {
      return null;
    }
    return {
      type: aMigrationType,
      migrate(aCallback) {
        for (let file of files) {
          file.copyTo(currentProfileDir, "");
        }
        aCallback(true);
      },
    };
  };

  function savePrefs() {
    // If we've used the pref service to write prefs for the new profile, it's too
    // early in startup for the service to have a profile directory, so we have to
    // manually tell it where to save the prefs file.
    let newPrefsFile = currentProfileDir.clone();
    newPrefsFile.append("prefs.js");
    Services.prefs.savePrefFile(newPrefsFile);
  }

  let types = MigrationUtils.resourceTypes;
  let places = getFileResource(types.HISTORY, ["places.sqlite", "places.sqlite-wal"]);
  let favicons = getFileResource(types.HISTORY, ["favicons.sqlite", "favicons.sqlite-wal"]);
  let cookies = getFileResource(types.COOKIES, ["cookies.sqlite", "cookies.sqlite-wal"]);
  let passwords = getFileResource(types.PASSWORDS,
    ["signons.sqlite", "logins.json", "key3.db", "key4.db"]);
  let formData = getFileResource(types.FORMDATA, [
    "formhistory.sqlite",
    "autofill-profiles.json",
  ]);
  let bookmarksBackups = getFileResource(types.OTHERDATA,
    [PlacesBackups.profileRelativeFolderPath]);
  let dictionary = getFileResource(types.OTHERDATA, ["persdict.dat"]);

  let session;
  let env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
  if (env.get("MOZ_RESET_PROFILE_MIGRATE_SESSION")) {
    // We only want to restore the previous firefox session if the profile refresh was
    // triggered by user. The MOZ_RESET_PROFILE_MIGRATE_SESSION would be set when a user-triggered
    // profile refresh happened in nsAppRunner.cpp. Hence, we detect the MOZ_RESET_PROFILE_MIGRATE_SESSION
    // to see if session data migration is required.
    env.set("MOZ_RESET_PROFILE_MIGRATE_SESSION", "");
    let sessionCheckpoints = this._getFileObject(sourceProfileDir, "sessionCheckpoints.json");
    let sessionFile = this._getFileObject(sourceProfileDir, "sessionstore.jsonlz4");
    if (sessionFile) {
      session = {
        type: types.SESSION,
        migrate(aCallback) {
          sessionCheckpoints.copyTo(currentProfileDir, "sessionCheckpoints.json");
          let newSessionFile = currentProfileDir.clone();
          newSessionFile.append("sessionstore.jsonlz4");
          let migrationPromise = SessionMigration.migrate(sessionFile.path, newSessionFile.path);
          migrationPromise.then(function() {
            let buildID = Services.appinfo.platformBuildID;
            let mstone = Services.appinfo.platformVersion;
            // Force the browser to one-off resume the session that we give it:
            Services.prefs.setBoolPref("browser.sessionstore.resume_session_once", true);
            // Reset the homepage_override prefs so that the browser doesn't override our
            // session with the "what's new" page:
            Services.prefs.setCharPref("browser.startup.homepage_override.mstone", mstone);
            Services.prefs.setCharPref("browser.startup.homepage_override.buildID", buildID);
            savePrefs();
            aCallback(true);
          }, function() {
            aCallback(false);
          });
        },
      };
    }
  }

  // Sync/FxA related data
  let sync = {
    name: "sync", // name is used only by tests.
    type: types.OTHERDATA,
    migrate: async aCallback => {
        // Try and parse a signedInUser.json file from the source directory and
        // if we can, copy it to the new profile and set sync's username pref
        // (which acts as a de-facto flag to indicate if sync is configured)
      try {
        let oldPath = OS.Path.join(sourceProfileDir.path, "signedInUser.json");
        let exists = await OS.File.exists(oldPath);
        if (exists) {
          let raw = await OS.File.read(oldPath, {encoding: "utf-8"});
          let data = JSON.parse(raw);
          if (data && data.accountData && data.accountData.email) {
            let username = data.accountData.email;
            // Write it to prefs.js and flush the file.
            Services.prefs.setStringPref("services.sync.username", username);
            savePrefs();
            // and copy the file itself.
            await OS.File.copy(oldPath, OS.Path.join(currentProfileDir.path, "signedInUser.json"));
          }
        }
      } catch (ex) {
        aCallback(false);
        return;
      }
      aCallback(true);
    },
  };

  // Telemetry related migrations.
  let times = {
    name: "times", // name is used only by tests.
    type: types.OTHERDATA,
    migrate: aCallback => {
      let file = this._getFileObject(sourceProfileDir, "times.json");
      if (file) {
        file.copyTo(currentProfileDir, "");
      }
      // And record the fact a migration (ie, a reset) happened.
      let timesAccessor = new ProfileAge(currentProfileDir.path);
      timesAccessor.recordProfileReset().then(
        () => aCallback(true),
        () => aCallback(false)
      );
    },
  };
  let telemetry = {
    name: "telemetry", // name is used only by tests...
    type: types.OTHERDATA,
    migrate: aCallback => {
      let createSubDir = (name) => {
        let dir = currentProfileDir.clone();
        dir.append(name);
        dir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
        return dir;
      };

      // If the 'datareporting' directory exists we migrate files from it.
      let dataReportingDir = this._getFileObject(sourceProfileDir, "datareporting");
      if (dataReportingDir && dataReportingDir.isDirectory()) {
        // Copy only specific files.
        let toCopy = ["state.json", "session-state.json"];

        let dest = createSubDir("datareporting");
        let enumerator = dataReportingDir.directoryEntries;
        while (enumerator.hasMoreElements()) {
          let file = enumerator.getNext().QueryInterface(Ci.nsIFile);
          if (file.isDirectory() || !toCopy.includes(file.leafName)) {
            continue;
          }
          file.copyTo(dest, "");
        }
      }

      aCallback(true);
    },
  };

  return [places, cookies, passwords, formData, dictionary, bookmarksBackups,
          session, sync, times, telemetry, favicons].filter(r => r);
};

Object.defineProperty(FirefoxProfileMigrator.prototype, "startupOnlyMigrator", {
  get: () => true,
});


FirefoxProfileMigrator.prototype.classDescription = "Firefox Profile Migrator";
FirefoxProfileMigrator.prototype.contractID = "@mozilla.org/profile/migrator;1?app=browser&type=firefox";
FirefoxProfileMigrator.prototype.classID = Components.ID("{91185366-ba97-4438-acba-48deaca63386}");

this.NSGetFactory = XPCOMUtils.generateNSGetFactory([FirefoxProfileMigrator]);
PK
!<a�'��modules/AboutHome.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = [ "AboutHomeUtils", "AboutHome" ];

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");

ChromeUtils.defineModuleGetter(this, "AppConstants",
  "resource://gre/modules/AppConstants.jsm");
ChromeUtils.defineModuleGetter(this, "AutoMigrate",
  "resource:///modules/AutoMigrate.jsm");
ChromeUtils.defineModuleGetter(this, "fxAccounts",
  "resource://gre/modules/FxAccounts.jsm");
ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
  "resource://gre/modules/PrivateBrowsingUtils.jsm");

// Url to fetch snippets, in the urlFormatter service format.
const SNIPPETS_URL_PREF = "browser.aboutHomeSnippets.updateUrl";

// Should be bumped up if the snippets content format changes.
const STARTPAGE_VERSION = 4;

var AboutHomeUtils = {
  get snippetsVersion() {
    return STARTPAGE_VERSION;
  },

  /*
   * showKnowYourRights - Determines if the user should be shown the
   * about:rights notification. The notification should *not* be shown if
   * we've already shown the current version, or if the override pref says to
   * never show it. The notification *should* be shown if it's never been seen
   * before, if a newer version is available, or if the override pref says to
   * always show it.
   */
  get showKnowYourRights() {
    // Look for an unconditional override pref. If set, do what it says.
    // (true --> never show, false --> always show)
    try {
      return !Services.prefs.getBoolPref("browser.rights.override");
    } catch (e) { }
    // Ditto, for the legacy EULA pref.
    try {
      return !Services.prefs.getBoolPref("browser.EULA.override");
    } catch (e) { }

    if (!AppConstants.MOZILLA_OFFICIAL) {
      // Non-official builds shouldn't show the notification.
      return false;
    }

    // Look to see if the user has seen the current version or not.
    var currentVersion = Services.prefs.getIntPref("browser.rights.version");
    try {
      return !Services.prefs.getBoolPref("browser.rights." + currentVersion + ".shown");
    } catch (e) { }

    // Legacy: If the user accepted a EULA, we won't annoy them with the
    // equivalent about:rights page until the version changes.
    try {
      return !Services.prefs.getBoolPref("browser.EULA." + currentVersion + ".accepted");
    } catch (e) { }

    // We haven't shown the notification before, so do so now.
    return true;
  }
};

/**
 * Returns the URL to fetch snippets from, in the urlFormatter service format.
 */
XPCOMUtils.defineLazyGetter(AboutHomeUtils, "snippetsURL", function() {
  let updateURL = Services.prefs
                          .getCharPref(SNIPPETS_URL_PREF)
                          .replace("%STARTPAGE_VERSION%", STARTPAGE_VERSION);
  return Services.urlFormatter.formatURL(updateURL);
});

/**
 * This code provides services to the about:home page. Whenever
 * about:home needs to do something chrome-privileged, it sends a
 * message that's handled here.
 */
var AboutHome = {
  MESSAGES: [
    "AboutHome:RestorePreviousSession",
    "AboutHome:Downloads",
    "AboutHome:Bookmarks",
    "AboutHome:History",
    "AboutHome:Addons",
    "AboutHome:Sync",
    "AboutHome:Settings",
  ],

  init() {
    for (let msg of this.MESSAGES) {
      Services.mm.addMessageListener(msg, this);
    }
  },

  // Additional listeners are registered in nsBrowserGlue.js
  receiveMessage(aMessage) {
    let window = aMessage.target.ownerGlobal;

    switch (aMessage.name) {
      case "AboutHome:RestorePreviousSession":
        let ss = Cc["@mozilla.org/browser/sessionstore;1"].
                 getService(Ci.nsISessionStore);
        if (ss.canRestoreLastSession) {
          ss.restoreLastSession();
        }
        break;

      case "AboutHome:Downloads":
        window.BrowserDownloadsUI();
        break;

      case "AboutHome:Bookmarks":
        window.PlacesCommandHook.showPlacesOrganizer("UnfiledBookmarks");
        break;

      case "AboutHome:History":
        window.PlacesCommandHook.showPlacesOrganizer("History");
        break;

      case "AboutHome:Addons":
        window.BrowserOpenAddonsMgr();
        break;

      case "AboutHome:Sync":
        window.openPreferences("paneSync", { urlParams: { entrypoint: "abouthome" }, origin: "aboutHome"  });
        break;

      case "AboutHome:Settings":
        window.openPreferences(undefined, {origin: "aboutHome"} );
        break;

      case "AboutHome:RequestUpdate":
        this.sendAboutHomeData(aMessage.target);
        break;

      case "AboutHome:MaybeShowMigrateMessage":
        AutoMigrate.shouldShowMigratePrompt(aMessage.target).then((prompt) => {
          if (prompt) {
            AutoMigrate.showUndoNotificationBar(aMessage.target);
          }
        });
        break;
    }
  },

  // Send all the chrome-privileged data needed by about:home. This
  // gets re-sent when the search engine changes.
  sendAboutHomeData(target) {
    let wrapper = {};
    ChromeUtils.import("resource:///modules/sessionstore/SessionStore.jsm",
      wrapper);
    let ss = wrapper.SessionStore;

    ss.promiseInitialized.then(function() {
      let data = {
        showRestoreLastSession: ss.canRestoreLastSession,
        snippetsURL: AboutHomeUtils.snippetsURL,
        showKnowYourRights: AboutHomeUtils.showKnowYourRights,
        snippetsVersion: AboutHomeUtils.snippetsVersion,
      };

      if (AboutHomeUtils.showKnowYourRights) {
        // Set pref to indicate we've shown the notification.
        let currentVersion = Services.prefs.getIntPref("browser.rights.version");
        Services.prefs.setBoolPref("browser.rights." + currentVersion + ".shown", true);
      }

      if (target && target.messageManager) {
        target.messageManager.sendAsyncMessage("AboutHome:Update", data);
      } else {
        let mm = Cc["@mozilla.org/globalmessagemanager;1"].getService(Ci.nsIMessageListenerManager);
        mm.broadcastAsyncMessage("AboutHome:Update", data);
      }
    }).catch(function onError(x) {
      Cu.reportError("Error in AboutHome.sendAboutHomeData: " + x);
    });
  },

};
PK
!</<�vvmodules/AboutNewTab.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = [ "AboutNewTab" ];

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");

ChromeUtils.defineModuleGetter(this, "AutoMigrate",
  "resource:///modules/AutoMigrate.jsm");
ChromeUtils.defineModuleGetter(this, "NewTabUtils",
  "resource://gre/modules/NewTabUtils.jsm");
ChromeUtils.defineModuleGetter(this, "RemotePages",
  "resource://gre/modules/RemotePageManager.jsm");

var AboutNewTab = {

  pageListener: null,

  isOverridden: false,

  init(pageListener) {
    if (this.isOverridden) {
      return;
    }
    this.pageListener = pageListener || new RemotePages(["about:home", "about:newtab"]);
    this.pageListener.addMessageListener("NewTab:Customize", this.customize);
    this.pageListener.addMessageListener("NewTab:MaybeShowMigrateMessage",
      this.maybeShowMigrateMessage);
  },

  maybeShowMigrateMessage({ target }) {
    AutoMigrate.shouldShowMigratePrompt(target.browser).then((prompt) => {
      if (prompt) {
        AutoMigrate.showUndoNotificationBar(target.browser);
      }
    });
  },

  customize(message) {
    NewTabUtils.allPages.enabled = message.data.enabled;
    NewTabUtils.allPages.enhanced = message.data.enhanced;
  },

  uninit() {
    if (this.pageListener) {
      this.pageListener.destroy();
      this.pageListener = null;
    }
  },

  override(shouldPassPageListener) {
    this.isOverridden = true;
    const pageListener = this.pageListener;
    if (!pageListener)
      return null;
    if (shouldPassPageListener) {
      this.pageListener = null;
      pageListener.removeMessageListener("NewTab:Customize", this.customize);
      pageListener.removeMessageListener("NewTab:MaybeShowMigrateMessage",
        this.maybeShowMigrateMessage);
      return pageListener;
    }
    this.uninit();
    return null;
  },

  reset(pageListener) {
    this.isOverridden = false;
    this.init(pageListener);
  }
};
PK
!<���s�s�modules/AsyncTabSwitcher.jsm/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = ["AsyncTabSwitcher"];

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetters(this, {
  AppConstants: "resource://gre/modules/AppConstants.jsm",
  Services: "resource://gre/modules/Services.jsm",
  TelemetryStopwatch: "resource://gre/modules/TelemetryStopwatch.jsm",
});

/**
 * The tab switcher is responsible for asynchronously switching
 * tabs in e10s. It waits until the new tab is ready (i.e., the
 * layer tree is available) before switching to it. Then it
 * unloads the layer tree for the old tab.
 *
 * The tab switcher is a state machine. For each tab, it
 * maintains state about whether the layer tree for the tab is
 * available, being loaded, being unloaded, or unavailable. It
 * also keeps track of the tab currently being displayed, the tab
 * it's trying to load, and the tab the user has asked to switch
 * to. The switcher object is created upon tab switch. It is
 * released when there are no pending tabs to load or unload.
 *
 * The following general principles have guided the design:
 *
 * 1. We only request one layer tree at a time. If the user
 * switches to a different tab while waiting, we don't request
 * the new layer tree until the old tab has loaded or timed out.
 *
 * 2. If loading the layers for a tab times out, we show the
 * spinner and possibly request the layer tree for another tab if
 * the user has requested one.
 *
 * 3. We discard layer trees on a delay. This way, if the user is
 * switching among the same tabs frequently, we don't continually
 * load the same tabs.
 *
 * It's important that we always show either the spinner or a tab
 * whose layers are available. Otherwise the compositor will draw
 * an entirely black frame, which is very jarring. To ensure this
 * never happens when switching away from a tab, we assume the
 * old tab might still be drawn until a MozAfterPaint event
 * occurs. Because layout and compositing happen asynchronously,
 * we don't have any other way of knowing when the switch
 * actually takes place. Therefore, we don't unload the old tab
 * until the next MozAfterPaint event.
 */
class AsyncTabSwitcher {
  constructor(tabbrowser) {
    this.log("START");

    // How long to wait for a tab's layers to load. After this
    // time elapses, we're free to put up the spinner and start
    // trying to load a different tab.
    this.TAB_SWITCH_TIMEOUT = 400; // ms

    // When the user hasn't switched tabs for this long, we unload
    // layers for all tabs that aren't in use.
    this.UNLOAD_DELAY = 300; // ms

    // The next three tabs form the principal state variables.
    // See the assertions in postActions for their invariants.

    // Tab the user requested most recently.
    this.requestedTab = tabbrowser.selectedTab;

    // Tab we're currently trying to load.
    this.loadingTab = null;

    // We show this tab in case the requestedTab hasn't loaded yet.
    this.lastVisibleTab = tabbrowser.selectedTab;

    // Auxilliary state variables:

    this.visibleTab = tabbrowser.selectedTab; // Tab that's on screen.
    this.spinnerTab = null; // Tab showing a spinner.
    this.blankTab = null; // Tab showing blank.
    this.lastPrimaryTab = tabbrowser.selectedTab; // Tab with primary="true"

    this.tabbrowser = tabbrowser;
    this.window = tabbrowser.ownerGlobal;
    this.loadTimer = null; // TAB_SWITCH_TIMEOUT nsITimer instance.
    this.unloadTimer = null; // UNLOAD_DELAY nsITimer instance.

    // Map from tabs to STATE_* (below).
    this.tabState = new Map();

    // True if we're in the midst of switching tabs.
    this.switchInProgress = false;

    // Keep an exact list of content processes (tabParent) in which
    // we're actively suppressing the display port. This gives a robust
    // way to make sure we don't forget to un-suppress.
    this.activeSuppressDisplayport = new Set();

    // Set of tabs that might be visible right now. We maintain
    // this set because we can't be sure when a tab is actually
    // drawn. A tab is added to this set when we ask to make it
    // visible. All tabs but the most recently shown tab are
    // removed from the set upon MozAfterPaint.
    this.maybeVisibleTabs = new Set([tabbrowser.selectedTab]);

    // This holds onto the set of tabs that we've been asked to warm up.
    // This is used only for Telemetry and logging, and (in order to not
    // over-complicate the async tab switcher any further) has nothing to do
    // with how warmed tabs are loaded and unloaded.
    this.warmingTabs = new WeakSet();

    this.STATE_UNLOADED = 0;
    this.STATE_LOADING = 1;
    this.STATE_LOADED = 2;
    this.STATE_UNLOADING = 3;

    // re-entrancy guard:
    this._processing = false;

    this._useDumpForLogging = false;
    this._logInit = false;

    this.window.addEventListener("MozAfterPaint", this);
    this.window.addEventListener("MozLayerTreeReady", this);
    this.window.addEventListener("MozLayerTreeCleared", this);
    this.window.addEventListener("TabRemotenessChange", this);
    this.window.addEventListener("sizemodechange", this);
    this.window.addEventListener("occlusionstatechange", this);
    this.window.addEventListener("SwapDocShells", this, true);
    this.window.addEventListener("EndSwapDocShells", this, true);

    let initialTab = this.requestedTab;
    let initialBrowser = initialTab.linkedBrowser;

    let tabIsLoaded = !initialBrowser.isRemoteBrowser ||
      initialBrowser.frameLoader.tabParent.hasLayers;

    // If we minimized the window before the switcher was activated,
    // we might have set  the preserveLayers flag for the current
    // browser. Let's clear it.
    initialBrowser.preserveLayers(false);

    if (!this.minimizedOrFullyOccluded) {
      this.log("Initial tab is loaded?: " + tabIsLoaded);
      this.setTabState(initialTab, tabIsLoaded ? this.STATE_LOADED
                                               : this.STATE_LOADING);
    }

    for (let ppBrowser of this.tabbrowser._printPreviewBrowsers) {
      let ppTab = this.tabbrowser.getTabForBrowser(ppBrowser);
      let state = ppBrowser.hasLayers ? this.STATE_LOADED
                                      : this.STATE_LOADING;
      this.setTabState(ppTab, state);
    }
  }

  destroy() {
    if (this.unloadTimer) {
      this.clearTimer(this.unloadTimer);
      this.unloadTimer = null;
    }
    if (this.loadTimer) {
      this.clearTimer(this.loadTimer);
      this.loadTimer = null;
    }

    this.window.removeEventListener("MozAfterPaint", this);
    this.window.removeEventListener("MozLayerTreeReady", this);
    this.window.removeEventListener("MozLayerTreeCleared", this);
    this.window.removeEventListener("TabRemotenessChange", this);
    this.window.removeEventListener("sizemodechange", this);
    this.window.removeEventListener("occlusionstatechange", this);
    this.window.removeEventListener("SwapDocShells", this, true);
    this.window.removeEventListener("EndSwapDocShells", this, true);

    this.tabbrowser._switcher = null;

    this.activeSuppressDisplayport.forEach(function(tabParent) {
      tabParent.suppressDisplayport(false);
    });
    this.activeSuppressDisplayport.clear();
  }

  // Wraps nsITimer. Must not use the vanilla setTimeout and
  // clearTimeout, because they will be blocked by nsIPromptService
  // dialogs.
  setTimer(callback, timeout) {
    let event = {
      notify: callback
    };

    var timer = Cc["@mozilla.org/timer;1"]
      .createInstance(Ci.nsITimer);
    timer.initWithCallback(event, timeout, Ci.nsITimer.TYPE_ONE_SHOT);
    return timer;
  }

  clearTimer(timer) {
    timer.cancel();
  }

  getTabState(tab) {
    let state = this.tabState.get(tab);

    // As an optimization, we lazily evaluate the state of tabs
    // that we've never seen before. Once we've figured it out,
    // we stash it in our state map.
    if (state === undefined) {
      state = this.STATE_UNLOADED;

      if (tab && tab.linkedPanel) {
        let b = tab.linkedBrowser;
        if (b.renderLayers && b.hasLayers) {
          state = this.STATE_LOADED;
        } else if (b.renderLayers && !b.hasLayers) {
          state = this.STATE_LOADING;
        } else if (!b.renderLayers && b.hasLayers) {
          state = this.STATE_UNLOADING;
        }
      }

      this.setTabStateNoAction(tab, state);
    }

    return state;
  }

  setTabStateNoAction(tab, state) {
    if (state == this.STATE_UNLOADED) {
      this.tabState.delete(tab);
    } else {
      this.tabState.set(tab, state);
    }
  }

  setTabState(tab, state) {
    if (state == this.getTabState(tab)) {
      return;
    }

    this.setTabStateNoAction(tab, state);

    let browser = tab.linkedBrowser;
    let { tabParent } = browser.frameLoader;
    if (state == this.STATE_LOADING) {
      this.assert(!this.minimizedOrFullyOccluded);

      if (!this.tabbrowser.tabWarmingEnabled) {
        browser.docShellIsActive = true;
      }

      if (tabParent) {
        browser.renderLayers = true;
      } else {
        this.onLayersReady(browser);
      }
    } else if (state == this.STATE_UNLOADING) {
      this.unwarmTab(tab);
      // Setting the docShell to be inactive will also cause it
      // to stop rendering layers.
      browser.docShellIsActive = false;
      if (!tabParent) {
        this.onLayersCleared(browser);
      }
    } else if (state == this.STATE_LOADED) {
      this.maybeActivateDocShell(tab);
    }

    if (!tab.linkedBrowser.isRemoteBrowser) {
      // setTabState is potentially re-entrant in the non-remote case,
      // so we must re-get the state for this assertion.
      let nonRemoteState = this.getTabState(tab);
      // Non-remote tabs can never stay in the STATE_LOADING
      // or STATE_UNLOADING states. By the time this function
      // exits, a non-remote tab must be in STATE_LOADED or
      // STATE_UNLOADED, since the painting and the layer
      // upload happen synchronously.
      this.assert(nonRemoteState == this.STATE_UNLOADED ||
        nonRemoteState == this.STATE_LOADED);
    }
  }

  get minimizedOrFullyOccluded() {
    return this.window.windowState == this.window.STATE_MINIMIZED ||
           this.window.isFullyOccluded;
  }

  finish() {
    this.log("FINISH");

    this.assert(this.tabbrowser._switcher);
    this.assert(this.tabbrowser._switcher === this);
    this.assert(!this.spinnerTab);
    this.assert(!this.blankTab);
    this.assert(!this.loadTimer);
    this.assert(!this.loadingTab);
    this.assert(this.lastVisibleTab === this.requestedTab);
    this.assert(this.minimizedOrFullyOccluded ||
      this.getTabState(this.requestedTab) == this.STATE_LOADED);

    this.destroy();

    this.window.document.commandDispatcher.unlock();

    let event = new this.window.CustomEvent("TabSwitchDone", {
      bubbles: true,
      cancelable: true
    });
    this.tabbrowser.dispatchEvent(event);
  }

  // This function is called after all the main state changes to
  // make sure we display the right tab.
  updateDisplay() {
    let requestedTabState = this.getTabState(this.requestedTab);
    let requestedBrowser = this.requestedTab.linkedBrowser;

    // It is often more desirable to show a blank tab when appropriate than
    // the tab switch spinner - especially since the spinner is usually
    // preceded by a perceived lag of TAB_SWITCH_TIMEOUT ms in the
    // tab switch. We can hide this lag, and hide the time being spent
    // constructing TabChild's, layer trees, etc, by showing a blank
    // tab instead and focusing it immediately.
    let shouldBeBlank = false;
    if (requestedBrowser.isRemoteBrowser) {
      // If a tab is remote and the window is not minimized, we can show a
      // blank tab instead of a spinner in the following cases:
      //
      // 1. The tab has just crashed, and we haven't started showing the
      //    tab crashed page yet (in this case, the TabParent is null)
      // 2. The tab has never presented, and has not finished loading
      //    a non-local-about: page.
      //
      // For (2), "finished loading a non-local-about: page" is
      // determined by the busy state on the tab element and checking
      // if the loaded URI is local.
      let hasSufficientlyLoaded = !this.requestedTab.hasAttribute("busy") &&
        !this.tabbrowser._isLocalAboutURI(requestedBrowser.currentURI);

      let fl = requestedBrowser.frameLoader;
      shouldBeBlank = !this.minimizedOrFullyOccluded &&
        (!fl.tabParent ||
          (!hasSufficientlyLoaded && !fl.tabParent.hasPresented));
    }

    this.log("Tab should be blank: " + shouldBeBlank);
    this.log("Requested tab is remote?: " + requestedBrowser.isRemoteBrowser);

    // Figure out which tab we actually want visible right now.
    let showTab = null;
    if (requestedTabState != this.STATE_LOADED &&
        this.lastVisibleTab && this.loadTimer && !shouldBeBlank) {
      // If we can't show the requestedTab, and lastVisibleTab is
      // available, show it.
      showTab = this.lastVisibleTab;
    } else {
      // Show the requested tab. If it's not available, we'll show the spinner or a blank tab.
      showTab = this.requestedTab;
    }

    // First, let's deal with blank tabs, which we show instead
    // of the spinner when the tab is not currently set up
    // properly in the content process.
    if (!shouldBeBlank && this.blankTab) {
      this.blankTab.linkedBrowser.removeAttribute("blank");
      this.blankTab = null;
    } else if (shouldBeBlank && this.blankTab !== showTab) {
      if (this.blankTab) {
        this.blankTab.linkedBrowser.removeAttribute("blank");
      }
      this.blankTab = showTab;
      this.blankTab.linkedBrowser.setAttribute("blank", "true");
    }

    // Show or hide the spinner as needed.
    let needSpinner = this.getTabState(showTab) != this.STATE_LOADED &&
                      !this.minimizedOrFullyOccluded &&
                      !shouldBeBlank;

    if (!needSpinner && this.spinnerTab) {
      this.spinnerHidden();
      this.tabbrowser.mPanelContainer.removeAttribute("pendingpaint");
      this.spinnerTab.linkedBrowser.removeAttribute("pendingpaint");
      this.spinnerTab = null;
    } else if (needSpinner && this.spinnerTab !== showTab) {
      if (this.spinnerTab) {
        this.spinnerTab.linkedBrowser.removeAttribute("pendingpaint");
      } else {
        this.spinnerDisplayed();
      }
      this.spinnerTab = showTab;
      this.tabbrowser.mPanelContainer.setAttribute("pendingpaint", "true");
      this.spinnerTab.linkedBrowser.setAttribute("pendingpaint", "true");
    }

    // Switch to the tab we've decided to make visible.
    if (this.visibleTab !== showTab) {
      this.tabbrowser._adjustFocusBeforeTabSwitch(this.visibleTab, showTab);
      this.visibleTab = showTab;

      this.maybeVisibleTabs.add(showTab);

      let tabs = this.tabbrowser.tabbox.tabs;
      let tabPanel = this.tabbrowser.mPanelContainer;
      let showPanel = tabs.getRelatedElement(showTab);
      let index = Array.indexOf(tabPanel.childNodes, showPanel);
      if (index != -1) {
        this.log(`Switch to tab ${index} - ${this.tinfo(showTab)}`);
        tabPanel.setAttribute("selectedIndex", index);
        if (showTab === this.requestedTab) {
          if (this._requestingTab) {
            /*
             * If _requestingTab is set, that means that we're switching the
             * visibility of the tab synchronously, and we need to wait for
             * the "select" event before shifting focus so that
             * _adjustFocusAfterTabSwitch runs with the right information for
             * the tab switch.
             */
            this.tabbrowser.addEventListener("select", () => {
              this.tabbrowser._adjustFocusAfterTabSwitch(showTab);
            }, { once: true });
          } else {
            this.tabbrowser._adjustFocusAfterTabSwitch(showTab);
          }

          this.maybeActivateDocShell(this.requestedTab);
        }
      }

      // This doesn't necessarily exist if we're a new window and haven't switched tabs yet
      if (this.lastVisibleTab)
        this.lastVisibleTab._visuallySelected = false;

      this.visibleTab._visuallySelected = true;
    }

    this.lastVisibleTab = this.visibleTab;
  }

  assert(cond) {
    if (!cond) {
      dump("Assertion failure\n" + Error().stack);

      // Don't break a user's browser if an assertion fails.
      if (AppConstants.DEBUG) {
        throw new Error("Assertion failure");
      }
    }
  }

  // We've decided to try to load requestedTab.
  loadRequestedTab() {
    this.assert(!this.loadTimer);
    this.assert(!this.minimizedOrFullyOccluded);

    // loadingTab can be non-null here if we timed out loading the current tab.
    // In that case we just overwrite it with a different tab; it's had its chance.
    this.loadingTab = this.requestedTab;
    this.log("Loading tab " + this.tinfo(this.loadingTab));

    this.loadTimer = this.setTimer(() => this.onLoadTimeout(), this.TAB_SWITCH_TIMEOUT);
    this.setTabState(this.requestedTab, this.STATE_LOADING);
  }

  maybeActivateDocShell(tab) {
    // If we've reached the point where the requested tab has entered
    // the loaded state, but the DocShell is still not yet active, we
    // should activate it.
    let browser = tab.linkedBrowser;
    let state = this.getTabState(tab);
    let canCheckDocShellState = !browser.mDestroyed &&
      (browser.docShell || browser.frameLoader.tabParent);
    if (tab == this.requestedTab &&
        canCheckDocShellState &&
        state == this.STATE_LOADED &&
        !browser.docShellIsActive &&
        !this.minimizedOrFullyOccluded) {
      browser.docShellIsActive = true;
      this.logState("Set requested tab docshell to active and preserveLayers to false");
      // If we minimized the window before the switcher was activated,
      // we might have set the preserveLayers flag for the current
      // browser. Let's clear it.
      browser.preserveLayers(false);
    }
  }

  // This function runs before every event. It fixes up the state
  // to account for closed tabs.
  preActions() {
    this.assert(this.tabbrowser._switcher);
    this.assert(this.tabbrowser._switcher === this);

    for (let [tab, ] of this.tabState) {
      if (!tab.linkedBrowser) {
        this.tabState.delete(tab);
        this.unwarmTab(tab);
      }
    }

    if (this.lastVisibleTab && !this.lastVisibleTab.linkedBrowser) {
      this.lastVisibleTab = null;
    }
    if (this.lastPrimaryTab && !this.lastPrimaryTab.linkedBrowser) {
      this.lastPrimaryTab = null;
    }
    if (this.blankTab && !this.blankTab.linkedBrowser) {
      this.blankTab = null;
    }
    if (this.spinnerTab && !this.spinnerTab.linkedBrowser) {
      this.spinnerHidden();
      this.spinnerTab = null;
    }
    if (this.loadingTab && !this.loadingTab.linkedBrowser) {
      this.loadingTab = null;
      this.clearTimer(this.loadTimer);
      this.loadTimer = null;
    }
  }

  // This code runs after we've responded to an event or requested a new
  // tab. It's expected that we've already updated all the principal
  // state variables. This function takes care of updating any auxilliary
  // state.
  postActions() {
    // Once we finish loading loadingTab, we null it out. So the state should
    // always be LOADING.
    this.assert(!this.loadingTab ||
      this.getTabState(this.loadingTab) == this.STATE_LOADING);

    // We guarantee that loadingTab is non-null iff loadTimer is non-null. So
    // the timer is set only when we're loading something.
    this.assert(!this.loadTimer || this.loadingTab);
    this.assert(!this.loadingTab || this.loadTimer);

    // If we're switching to a non-remote tab, there's no need to wait
    // for it to send layers to the compositor, as this will happen
    // synchronously. Clearing this here means that in the next step,
    // we can load the non-remote browser immediately.
    if (!this.requestedTab.linkedBrowser.isRemoteBrowser) {
      this.loadingTab = null;
      if (this.loadTimer) {
        this.clearTimer(this.loadTimer);
        this.loadTimer = null;
      }
    }

    // If we're not loading anything, try loading the requested tab.
    let stateOfRequestedTab = this.getTabState(this.requestedTab);
    if (!this.loadTimer && !this.minimizedOrFullyOccluded &&
        (stateOfRequestedTab == this.STATE_UNLOADED ||
        stateOfRequestedTab == this.STATE_UNLOADING ||
        this.warmingTabs.has(this.requestedTab))) {
      this.assert(stateOfRequestedTab != this.STATE_LOADED);
      this.loadRequestedTab();
    }

    // See how many tabs still have work to do.
    let numPending = 0;
    let numWarming = 0;
    for (let [tab, state] of this.tabState) {
      // Skip print preview browsers since they shouldn't affect tab switching.
      if (this.tabbrowser._printPreviewBrowsers.has(tab.linkedBrowser)) {
        continue;
      }

      if (state == this.STATE_LOADED && tab !== this.requestedTab) {
        numPending++;

        if (tab !== this.visibleTab) {
          numWarming++;
        }
      }
      if (state == this.STATE_LOADING || state == this.STATE_UNLOADING) {
        numPending++;
      }
    }

    this.updateDisplay();

    // It's possible for updateDisplay to trigger one of our own event
    // handlers, which might cause finish() to already have been called.
    // Check for that before calling finish() again.
    if (!this.tabbrowser._switcher) {
      return;
    }

    this.maybeFinishTabSwitch();

    if (numWarming > this.tabbrowser.tabWarmingMax) {
      this.logState("Hit tabWarmingMax");
      if (this.unloadTimer) {
        this.clearTimer(this.unloadTimer);
      }
      this.unloadNonRequiredTabs();
    }

    if (numPending == 0) {
      this.finish();
    }

    this.logState("done");
  }

  // Fires when we're ready to unload unused tabs.
  onUnloadTimeout() {
    this.logState("onUnloadTimeout");
    this.preActions();
    this.unloadTimer = null;

    this.unloadNonRequiredTabs();

    this.postActions();
  }

  // If there are any non-visible and non-requested tabs in
  // STATE_LOADED, sets them to STATE_UNLOADING. Also queues
  // up the unloadTimer to run onUnloadTimeout if there are still
  // tabs in the process of unloading.
  unloadNonRequiredTabs() {
    this.warmingTabs = new WeakSet();
    let numPending = 0;

    // Unload any tabs that can be unloaded.
    for (let [tab, state] of this.tabState) {
      if (this.tabbrowser._printPreviewBrowsers.has(tab.linkedBrowser)) {
        continue;
      }

      if (state == this.STATE_LOADED &&
          !this.maybeVisibleTabs.has(tab) &&
          tab !== this.lastVisibleTab &&
          tab !== this.loadingTab &&
          tab !== this.requestedTab) {
        this.setTabState(tab, this.STATE_UNLOADING);
      }

      if (state != this.STATE_UNLOADED && tab !== this.requestedTab) {
        numPending++;
      }
    }

    if (numPending) {
      // Keep the timer going since there may be more tabs to unload.
      this.unloadTimer = this.setTimer(() => this.onUnloadTimeout(), this.UNLOAD_DELAY);
    }
  }

  // Fires when an ongoing load has taken too long.
  onLoadTimeout() {
    this.logState("onLoadTimeout");
    this.preActions();
    this.loadTimer = null;
    this.loadingTab = null;
    this.postActions();
  }

  // Fires when the layers become available for a tab.
  onLayersReady(browser) {
    let tab = this.tabbrowser.getTabForBrowser(browser);
    if (!tab) {
      // We probably got a layer update from a tab that got before
      // the switcher was created, or for browser that's not being
      // tracked by the async tab switcher (like the preloaded about:newtab).
      return;
    }

    this.logState(`onLayersReady(${tab._tPos}, ${browser.isRemoteBrowser})`);

    this.assert(this.getTabState(tab) == this.STATE_LOADING ||
      this.getTabState(tab) == this.STATE_LOADED);
    this.setTabState(tab, this.STATE_LOADED);
    this.unwarmTab(tab);

    if (this.loadingTab === tab) {
      this.clearTimer(this.loadTimer);
      this.loadTimer = null;
      this.loadingTab = null;
    }
  }

  // Fires when we paint the screen. Any tab switches we initiated
  // previously are done, so there's no need to keep the old layers
  // around.
  onPaint() {
    this.maybeVisibleTabs.clear();
  }

  // Called when we're done clearing the layers for a tab.
  onLayersCleared(browser) {
    let tab = this.tabbrowser.getTabForBrowser(browser);
    if (tab) {
      this.logState(`onLayersCleared(${tab._tPos})`);
      this.assert(this.getTabState(tab) == this.STATE_UNLOADING ||
        this.getTabState(tab) == this.STATE_UNLOADED);
      this.setTabState(tab, this.STATE_UNLOADED);
    }
  }

  // Called when a tab switches from remote to non-remote. In this case
  // a MozLayerTreeReady notification that we requested may never fire,
  // so we need to simulate it.
  onRemotenessChange(tab) {
    this.logState(`onRemotenessChange(${tab._tPos}, ${tab.linkedBrowser.isRemoteBrowser})`);
    if (!tab.linkedBrowser.isRemoteBrowser) {
      if (this.getTabState(tab) == this.STATE_LOADING) {
        this.onLayersReady(tab.linkedBrowser);
      } else if (this.getTabState(tab) == this.STATE_UNLOADING) {
        this.onLayersCleared(tab.linkedBrowser);
      }
    } else if (this.getTabState(tab) == this.STATE_LOADED) {
      // A tab just changed from non-remote to remote, which means
      // that it's gone back into the STATE_LOADING state until
      // it sends up a layer tree.
      this.setTabState(tab, this.STATE_LOADING);
    }
  }

  // Called when a tab has been removed, and the browser node is
  // about to be removed from the DOM.
  onTabRemoved(tab) {
    if (this.lastVisibleTab == tab) {
      // The browser that was being presented to the user is
      // going to be removed during this tick of the event loop.
      // This will cause us to show a tab spinner instead.
      this.preActions();
      this.lastVisibleTab = null;
      this.postActions();
    }
  }

  onSizeModeOrOcclusionStateChange() {
    if (this.minimizedOrFullyOccluded) {
      for (let [tab, state] of this.tabState) {
        // Skip print preview browsers since they shouldn't affect tab switching.
        if (this.tabbrowser._printPreviewBrowsers.has(tab.linkedBrowser)) {
          continue;
        }

        if (state == this.STATE_LOADING || state == this.STATE_LOADED) {
          this.setTabState(tab, this.STATE_UNLOADING);
        }
      }
      if (this.loadTimer) {
        this.clearTimer(this.loadTimer);
        this.loadTimer = null;
      }
      this.loadingTab = null;
    } else {
      // We're no longer minimized or occluded. This means we might want
      // to activate the current tab's docShell.
      this.maybeActivateDocShell(this.tabbrowser.selectedTab);
    }
  }

  onSwapDocShells(ourBrowser, otherBrowser) {
    // This event fires before the swap. ourBrowser is from
    // our window. We save the state of otherBrowser since ourBrowser
    // needs to take on that state at the end of the swap.

    let otherTabbrowser = otherBrowser.ownerGlobal.gBrowser;
    let otherState;
    if (otherTabbrowser && otherTabbrowser._switcher) {
      let otherTab = otherTabbrowser.getTabForBrowser(otherBrowser);
      let otherSwitcher = otherTabbrowser._switcher;
      otherState = otherSwitcher.getTabState(otherTab);
    } else {
      otherState = otherBrowser.docShellIsActive ? this.STATE_LOADED : this.STATE_UNLOADED;
    }
    if (!this.swapMap) {
      this.swapMap = new WeakMap();
    }
    this.swapMap.set(otherBrowser, {
      state: otherState,
    });
  }

  onEndSwapDocShells(ourBrowser, otherBrowser) {
    // The swap has happened. We reset the loadingTab in
    // case it has been swapped. We also set ourBrowser's state
    // to whatever otherBrowser's state was before the swap.

    if (this.loadTimer) {
      // Clearing the load timer means that we will
      // immediately display a spinner if ourBrowser isn't
      // ready yet. Typically it will already be ready
      // though. If it's not, we're probably in a new window,
      // in which case we have no other tabs to display anyway.
      this.clearTimer(this.loadTimer);
      this.loadTimer = null;
    }
    this.loadingTab = null;

    let { state: otherState } = this.swapMap.get(otherBrowser);

    this.swapMap.delete(otherBrowser);

    let ourTab = this.tabbrowser.getTabForBrowser(ourBrowser);
    if (ourTab) {
      this.setTabStateNoAction(ourTab, otherState);
    }
  }

  shouldActivateDocShell(browser) {
    let tab = this.tabbrowser.getTabForBrowser(browser);
    let state = this.getTabState(tab);
    return state == this.STATE_LOADING || state == this.STATE_LOADED;
  }

  activateBrowserForPrintPreview(browser) {
    let tab = this.tabbrowser.getTabForBrowser(browser);
    let state = this.getTabState(tab);
    if (state != this.STATE_LOADING &&
        state != this.STATE_LOADED) {
      this.setTabState(tab, this.STATE_LOADING);
      this.logState("Activated browser " + this.tinfo(tab) + " for print preview");
    }
  }

  canWarmTab(tab) {
    if (!this.tabbrowser.tabWarmingEnabled) {
      return false;
    }

    if (!tab) {
      return false;
    }

    // If the tab is not yet inserted, closing, not remote,
    // crashed, already visible, or already requested, warming
    // up the tab makes no sense.
    if (this.minimizedOrFullyOccluded ||
        !tab.linkedPanel ||
        tab.closing ||
        !tab.linkedBrowser.isRemoteBrowser ||
        !tab.linkedBrowser.frameLoader.tabParent) {
      return false;
    }

    // Similarly, if the tab is already in STATE_LOADING or
    // STATE_LOADED somehow, there's no point in trying to
    // warm it up.
    let state = this.getTabState(tab);
    if (state === this.STATE_LOADING ||
      state === this.STATE_LOADED) {
      return false;
    }

    return true;
  }

  unwarmTab(tab) {
    this.warmingTabs.delete(tab);
  }

  warmupTab(tab) {
    if (!this.canWarmTab(tab)) {
      return;
    }

    this.logState("warmupTab " + this.tinfo(tab));

    this.warmingTabs.add(tab);
    this.setTabState(tab, this.STATE_LOADING);
    this.suppressDisplayPortAndQueueUnload(tab,
      this.tabbrowser.tabWarmingUnloadDelay);
  }

  // Called when the user asks to switch to a given tab.
  requestTab(tab) {
    if (tab === this.requestedTab) {
      return;
    }

    if (this.tabbrowser.tabWarmingEnabled) {
      let warmingState = "disqualified";

      if (this.warmingTabs.has(tab)) {
        let tabState = this.getTabState(tab);
        if (tabState == this.STATE_LOADING) {
          warmingState = "stillLoading";
        } else if (tabState == this.STATE_LOADED) {
          warmingState = "loaded";
        }
      } else if (this.canWarmTab(tab)) {
        warmingState = "notWarmed";
      }

      Services.telemetry
        .getHistogramById("FX_TAB_SWITCH_REQUEST_TAB_WARMING_STATE")
        .add(warmingState);
    }

    this._requestingTab = true;
    this.logState("requestTab " + this.tinfo(tab));
    this.startTabSwitch();

    this.requestedTab = tab;

    tab.linkedBrowser.setAttribute("primary", "true");
    if (this.lastPrimaryTab && this.lastPrimaryTab != tab) {
      this.lastPrimaryTab.linkedBrowser.removeAttribute("primary");
    }
    this.lastPrimaryTab = tab;

    this.suppressDisplayPortAndQueueUnload(this.requestedTab, this.UNLOAD_DELAY);
    this._requestingTab = false;
  }

  suppressDisplayPortAndQueueUnload(tab, unloadTimeout) {
    let browser = tab.linkedBrowser;
    let fl = browser.frameLoader;

    if (fl && fl.tabParent && !this.activeSuppressDisplayport.has(fl.tabParent)) {
      fl.tabParent.suppressDisplayport(true);
      this.activeSuppressDisplayport.add(fl.tabParent);
    }

    this.preActions();

    if (this.unloadTimer) {
      this.clearTimer(this.unloadTimer);
    }
    this.unloadTimer = this.setTimer(() => this.onUnloadTimeout(), unloadTimeout);

    this.postActions();
  }

  handleEvent(event, delayed = false) {
    if (this._processing) {
      this.setTimer(() => this.handleEvent(event, true), 0);
      return;
    }
    if (delayed && this.tabbrowser._switcher != this) {
      // if we delayed processing this event, we might be out of date, in which
      // case we drop the delayed events
      return;
    }
    this._processing = true;
    this.preActions();

    if (event.type == "MozLayerTreeReady") {
      this.onLayersReady(event.originalTarget);
    }
    if (event.type == "MozAfterPaint") {
      this.onPaint();
    } else if (event.type == "MozLayerTreeCleared") {
      this.onLayersCleared(event.originalTarget);
    } else if (event.type == "TabRemotenessChange") {
      this.onRemotenessChange(event.target);
    } else if (event.type == "sizemodechange" ||
      event.type == "occlusionstatechange") {
      this.onSizeModeOrOcclusionStateChange();
    } else if (event.type == "SwapDocShells") {
      this.onSwapDocShells(event.originalTarget, event.detail);
    } else if (event.type == "EndSwapDocShells") {
      this.onEndSwapDocShells(event.originalTarget, event.detail);
    }

    this.postActions();
    this._processing = false;
  }

  /*
   * Telemetry and Profiler related helpers for recording tab switch
   * timing.
   */

  startTabSwitch() {
    TelemetryStopwatch.cancel("FX_TAB_SWITCH_TOTAL_E10S_MS", this.window);
    TelemetryStopwatch.start("FX_TAB_SWITCH_TOTAL_E10S_MS", this.window);
    this.addMarker("AsyncTabSwitch:Start");
    this.switchInProgress = true;
  }

  /**
   * Something has occurred that might mean that we've completed
   * the tab switch (layers are ready, paints are done, spinners
   * are hidden). This checks to make sure all conditions are
   * satisfied, and then records the tab switch as finished.
   */
  maybeFinishTabSwitch() {
    if (this.switchInProgress && this.requestedTab &&
        (this.getTabState(this.requestedTab) == this.STATE_LOADED ||
          this.requestedTab === this.blankTab)) {
      // After this point the tab has switched from the content thread's point of view.
      // The changes will be visible after the next refresh driver tick + composite.
      let time = TelemetryStopwatch.timeElapsed("FX_TAB_SWITCH_TOTAL_E10S_MS", this.window);
      if (time != -1) {
        TelemetryStopwatch.finish("FX_TAB_SWITCH_TOTAL_E10S_MS", this.window);
        this.log("DEBUG: tab switch time = " + time);
        this.addMarker("AsyncTabSwitch:Finish");
      }
      this.switchInProgress = false;
    }
  }

  spinnerDisplayed() {
    this.assert(!this.spinnerTab);
    let browser = this.requestedTab.linkedBrowser;
    this.assert(browser.isRemoteBrowser);
    TelemetryStopwatch.start("FX_TAB_SWITCH_SPINNER_VISIBLE_MS", this.window);
    // We have a second, similar probe for capturing recordings of
    // when the spinner is displayed for very long periods.
    TelemetryStopwatch.start("FX_TAB_SWITCH_SPINNER_VISIBLE_LONG_MS", this.window);
    this.addMarker("AsyncTabSwitch:SpinnerShown");
  }

  spinnerHidden() {
    this.assert(this.spinnerTab);
    this.log("DEBUG: spinner time = " +
      TelemetryStopwatch.timeElapsed("FX_TAB_SWITCH_SPINNER_VISIBLE_MS", this.window));
    TelemetryStopwatch.finish("FX_TAB_SWITCH_SPINNER_VISIBLE_MS", this.window);
    TelemetryStopwatch.finish("FX_TAB_SWITCH_SPINNER_VISIBLE_LONG_MS", this.window);
    this.addMarker("AsyncTabSwitch:SpinnerHidden");
    // we do not get a onPaint after displaying the spinner
  }

  addMarker(marker) {
    if (Services.profiler) {
      Services.profiler.AddMarker(marker);
    }
  }

  /*
   * Debug related logging for switcher.
   */
  logging() {
    if (this._useDumpForLogging)
      return true;
    if (this._logInit)
      return this._shouldLog;
    let result = Services.prefs.getBoolPref("browser.tabs.remote.logSwitchTiming", false);
    this._shouldLog = result;
    this._logInit = true;
    return this._shouldLog;
  }

  tinfo(tab) {
    if (tab) {
      return tab._tPos + "(" + tab.linkedBrowser.currentURI.spec + ")";
    }
    return "null";
  }

  log(s) {
    if (!this.logging())
      return;
    if (this._useDumpForLogging) {
      dump(s + "\n");
    } else {
      Services.console.logStringMessage(s);
    }
  }

  logState(prefix) {
    if (!this.logging())
      return;

    let accum = prefix + " ";
    for (let i = 0; i < this.tabbrowser.tabs.length; i++) {
      let tab = this.tabbrowser.tabs[i];
      let state = this.getTabState(tab);
      let isWarming = this.warmingTabs.has(tab);

      accum += i + ":";
      if (tab === this.lastVisibleTab) accum += "V";
      if (tab === this.loadingTab) accum += "L";
      if (tab === this.requestedTab) accum += "R";
      if (tab === this.blankTab) accum += "B";
      if (isWarming) accum += "(W)";
      if (state == this.STATE_LOADED) accum += "(+)";
      if (state == this.STATE_LOADING) accum += "(+?)";
      if (state == this.STATE_UNLOADED) accum += "(-)";
      if (state == this.STATE_UNLOADING) accum += "(-?)";
      accum += " ";
    }
    if (this._useDumpForLogging) {
      dump(accum + "\n");
    } else {
      Services.console.logStringMessage(accum);
    }
  }
}

PK
!<��0g��modules/AttributionCode.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";

var EXPORTED_SYMBOLS = ["AttributionCode"];

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.defineModuleGetter(this, "AppConstants",
  "resource://gre/modules/AppConstants.jsm");
ChromeUtils.defineModuleGetter(this, "OS",
  "resource://gre/modules/osfile.jsm");
ChromeUtils.defineModuleGetter(this, "Services",
  "resource://gre/modules/Services.jsm");

const ATTR_CODE_MAX_LENGTH = 200;
const ATTR_CODE_KEYS_REGEX = /^source|medium|campaign|content$/;
const ATTR_CODE_VALUE_REGEX = /[a-zA-Z0-9_%\\-\\.\\(\\)]*/;
const ATTR_CODE_FIELD_SEPARATOR = "%26"; // URL-encoded &
const ATTR_CODE_KEY_VALUE_SEPARATOR = "%3D"; // URL-encoded =

let gCachedAttrData = null;

/**
 * Returns an nsIFile for the file containing the attribution data.
 */
function getAttributionFile() {
  let file = Services.dirsvc.get("LocalAppData", Ci.nsIFile);
  // appinfo does not exist in xpcshell, so we need defaults.
  file.append(Services.appinfo.vendor || "mozilla");
  file.append(AppConstants.MOZ_APP_NAME);
  file.append("postSigningData");
  return file;
}

/**
 * Returns an object containing a key-value pair for each piece of attribution
 * data included in the passed-in attribution code string.
 * If the string isn't a valid attribution code, returns an empty object.
 */
function parseAttributionCode(code) {
  if (code.length > ATTR_CODE_MAX_LENGTH) {
    return {};
  }

  let isValid = true;
  let parsed = {};
  for (let param of code.split(ATTR_CODE_FIELD_SEPARATOR)) {
    let [key, value] = param.split(ATTR_CODE_KEY_VALUE_SEPARATOR, 2);
    if (key && ATTR_CODE_KEYS_REGEX.test(key)) {
      if (value && ATTR_CODE_VALUE_REGEX.test(value)) {
        parsed[key] = value;
      }
    } else {
      isValid = false;
      break;
    }
  }
  return isValid ? parsed : {};
}

var AttributionCode = {
  /**
   * Reads the attribution code, either from disk or a cached version.
   * Returns a promise that fulfills with an object containing the parsed
   * attribution data if the code could be read and is valid,
   * or an empty object otherwise.
   */
  getAttrDataAsync() {
    return (async function() {
      if (gCachedAttrData != null) {
        return gCachedAttrData;
      }

      let code = "";
      try {
        let bytes = await OS.File.read(getAttributionFile().path);
        let decoder = new TextDecoder();
        code = decoder.decode(bytes);
      } catch (ex) {
        // The attribution file may already have been deleted,
        // or it may have never been installed at all;
        // failure to open or read it isn't an error.
      }

      gCachedAttrData = parseAttributionCode(code);
      return gCachedAttrData;
    })();
  },

  /**
   * Return the cached attribution data synchronously without hitting
   * the disk.
   * @returns A dictionary with the attribution data if it's available,
   *          null otherwise.
   */
  getCachedAttributionData() {
    return gCachedAttrData;
  },

  /**
   * Deletes the attribution data file.
   * Returns a promise that resolves when the file is deleted,
   * or if the file couldn't be deleted (the promise is never rejected).
   */
  deleteFileAsync() {
    return (async function() {
      try {
        await OS.File.remove(getAttributionFile().path);
      } catch (ex) {
        // The attribution file may already have been deleted,
        // or it may have never been installed at all;
        // failure to delete it isn't an error.
      }
    })();
  },

  /**
   * Clears the cached attribution code value, if any.
   * Does nothing if called from outside of an xpcshell test.
   */
  _clearCache() {
    let env = Cc["@mozilla.org/process/environment;1"]
              .getService(Ci.nsIEnvironment);
    if (env.exists("XPCSHELL_TEST_PROFILE_DIR")) {
      gCachedAttrData = null;
    }
  },
};
PK
!<���]�d�dmodules/AutoMigrate.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = ["AutoMigrate"];

const kAutoMigrateEnabledPref = "browser.migrate.automigrate.enabled";
const kUndoUIEnabledPref = "browser.migrate.automigrate.ui.enabled";

const kInPageUIEnabledPref = "browser.migrate.automigrate.inpage.ui.enabled";

const kAutoMigrateBrowserPref = "browser.migrate.automigrate.browser";
const kAutoMigrateImportedItemIds = "browser.migrate.automigrate.imported-items";

const kAutoMigrateLastUndoPromptDateMsPref = "browser.migrate.automigrate.lastUndoPromptDateMs";
const kAutoMigrateDaysToOfferUndoPref = "browser.migrate.automigrate.daysToOfferUndo";

const kAutoMigrateUndoSurveyPref = "browser.migrate.automigrate.undo-survey";
const kAutoMigrateUndoSurveyLocalePref = "browser.migrate.automigrate.undo-survey-locales";

const kNotificationId = "automigration-undo";

ChromeUtils.import("resource:///modules/MigrationUtils.jsm");
ChromeUtils.import("resource://gre/modules/Preferences.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");

ChromeUtils.defineModuleGetter(this, "AsyncShutdown",
                               "resource://gre/modules/AsyncShutdown.jsm");
ChromeUtils.defineModuleGetter(this, "LoginHelper",
                               "resource://gre/modules/LoginHelper.jsm");
ChromeUtils.defineModuleGetter(this, "NewTabUtils",
                               "resource://gre/modules/NewTabUtils.jsm");
ChromeUtils.defineModuleGetter(this, "OS",
                               "resource://gre/modules/osfile.jsm");
ChromeUtils.defineModuleGetter(this, "PlacesUtils",
                               "resource://gre/modules/PlacesUtils.jsm");
ChromeUtils.defineModuleGetter(this, "TelemetryStopwatch",
                               "resource://gre/modules/TelemetryStopwatch.jsm");

XPCOMUtils.defineLazyGetter(this, "gBrandBundle", function() {
  const kBrandBundle = "chrome://branding/locale/brand.properties";
  return Services.strings.createBundle(kBrandBundle);
});

Cu.importGlobalProperties(["URL"]);

XPCOMUtils.defineLazyGetter(this, "kUndoStateFullPath", function() {
  return OS.Path.join(OS.Constants.Path.profileDir, "initialMigrationMetadata.jsonlz4");
});

const AutoMigrate = {
  get resourceTypesToUse() {
    let {BOOKMARKS, HISTORY, PASSWORDS} = Ci.nsIBrowserProfileMigrator;
    return BOOKMARKS | HISTORY | PASSWORDS;
  },

  _checkIfEnabled() {
    let pref = Preferences.get(kAutoMigrateEnabledPref, false);
    // User-set values should take precedence:
    if (Services.prefs.prefHasUserValue(kAutoMigrateEnabledPref)) {
      return pref;
    }
    // If we're using the default value, make sure the distribution.ini
    // value is taken into account even early on startup.
    try {
      let distributionFile = Services.dirsvc.get("XREAppDist", Ci.nsIFile);
      distributionFile.append("distribution.ini");
      let parser = Cc["@mozilla.org/xpcom/ini-parser-factory;1"].
                 getService(Ci.nsIINIParserFactory).
                 createINIParser(distributionFile);
      return JSON.parse(parser.getString("Preferences", kAutoMigrateEnabledPref));
    } catch (ex) { /* ignore exceptions (file doesn't exist, invalid value, etc.) */ }

    return pref;
  },

  init() {
    this.enabled = this._checkIfEnabled();
  },

  /**
   * Automatically pick a migrator and resources to migrate,
   * then migrate those and start up.
   *
   * @throws if automatically deciding on migrators/data
   *         failed for some reason.
   */
  async migrate(profileStartup, migratorKey, profileToMigrate) {
    let histogram = Services.telemetry.getHistogramById(
      "FX_STARTUP_MIGRATION_AUTOMATED_IMPORT_PROCESS_SUCCESS");
    histogram.add(0);
    let {migrator, pickedKey} = await this.pickMigrator(migratorKey);
    histogram.add(5);

    profileToMigrate = await this.pickProfile(migrator, profileToMigrate);
    histogram.add(10);

    let resourceTypes = await migrator.getMigrateData(profileToMigrate, profileStartup);
    if (!(resourceTypes & this.resourceTypesToUse)) {
      throw new Error("No usable resources were found for the selected browser!");
    }
    histogram.add(15);

    let sawErrors = false;
    let migrationObserver = (subject, topic) => {
      if (topic == "Migration:ItemError") {
        sawErrors = true;
      } else if (topic == "Migration:Ended") {
        histogram.add(25);
        if (sawErrors) {
          histogram.add(26);
        }
        Services.obs.removeObserver(migrationObserver, "Migration:Ended");
        Services.obs.removeObserver(migrationObserver, "Migration:ItemError");
        Services.prefs.setCharPref(kAutoMigrateBrowserPref, pickedKey);
        // Save the undo history and block shutdown on that save completing.
        AsyncShutdown.profileBeforeChange.addBlocker(
          "AutoMigrate Undo saving", this.saveUndoState(), () => {
            return {state: this._saveUndoStateTrackerForShutdown};
          });
      }
    };

    MigrationUtils.initializeUndoData();
    Services.obs.addObserver(migrationObserver, "Migration:Ended");
    Services.obs.addObserver(migrationObserver, "Migration:ItemError");
    await migrator.migrate(this.resourceTypesToUse, profileStartup, profileToMigrate);
    histogram.add(20);
  },

  /**
   * Pick and return a migrator to use for automatically migrating.
   *
   * @param {String} migratorKey   optional, a migrator key to prefer/pick.
   * @returns {Object}             an object with the migrator to use for migrating, as
   *                               well as the key we eventually ended up using to obtain it.
   */
  async pickMigrator(migratorKey) {
    if (!migratorKey) {
      let defaultKey = MigrationUtils.getMigratorKeyForDefaultBrowser();
      if (!defaultKey) {
        throw new Error("Could not determine default browser key to migrate from");
      }
      migratorKey = defaultKey;
    }
    if (migratorKey == "firefox") {
      throw new Error("Can't automatically migrate from Firefox.");
    }

    let migrator = await MigrationUtils.getMigrator(migratorKey);
    if (!migrator) {
      throw new Error("Migrator specified or a default was found, but the migrator object is not available (or has no data).");
    }
    return {migrator, pickedKey: migratorKey};
  },

  /**
   * Pick a source profile (from the original browser) to use.
   *
   * @param {Migrator} migrator     the migrator object to use
   * @param {String}   suggestedId  the id of the profile to migrate, if pre-specified, or null
   * @returns                       the profile to migrate, or null if migrating
   *                                from the default profile.
   */
  async pickProfile(migrator, suggestedId) {
    let profiles = await migrator.getSourceProfiles();
    if (profiles && !profiles.length) {
      throw new Error("No profile data found to migrate.");
    }
    if (suggestedId) {
      if (!profiles) {
        throw new Error("Profile specified but only a default profile found.");
      }
      let suggestedProfile = profiles.find(profile => profile.id == suggestedId);
      if (!suggestedProfile) {
        throw new Error("Profile specified was not found.");
      }
      return suggestedProfile;
    }
    if (profiles && profiles.length > 1) {
      throw new Error("Don't know how to pick a profile when more than 1 profile is present.");
    }
    return profiles ? profiles[0] : null;
  },

  _pendingUndoTasks: false,
  async canUndo() {
    if (this._savingPromise) {
      await this._savingPromise;
    }
    if (this._pendingUndoTasks) {
      return false;
    }
    let fileExists = false;
    try {
      fileExists = await OS.File.exists(kUndoStateFullPath);
    } catch (ex) {
      Cu.reportError(ex);
    }
    return fileExists;
  },

  async undo() {
    let browserId = Preferences.get(kAutoMigrateBrowserPref, "unknown");
    TelemetryStopwatch.startKeyed("FX_STARTUP_MIGRATION_UNDO_TOTAL_MS", browserId);
    let histogram = Services.telemetry.getHistogramById("FX_STARTUP_MIGRATION_AUTOMATED_IMPORT_UNDO");
    histogram.add(0);
    if (!(await this.canUndo())) {
      histogram.add(5);
      throw new Error("Can't undo!");
    }

    this._pendingUndoTasks = true;
    this._removeNotificationBars();
    histogram.add(10);

    let readPromise = OS.File.read(kUndoStateFullPath, {
      encoding: "utf-8",
      compression: "lz4",
    });
    let stateData = this._dejsonifyUndoState(await readPromise);
    histogram.add(12);

    this._errorMap = {bookmarks: 0, visits: 0, logins: 0};
    let reportErrorTelemetry = (type) => {
      let histogramId = `FX_STARTUP_MIGRATION_UNDO_${type.toUpperCase()}_ERRORCOUNT`;
      Services.telemetry.getKeyedHistogramById(histogramId).add(browserId, this._errorMap[type]);
    };

    let startTelemetryStopwatch = resourceType => {
      let histogramId = `FX_STARTUP_MIGRATION_UNDO_${resourceType.toUpperCase()}_MS`;
      TelemetryStopwatch.startKeyed(histogramId, browserId);
    };
    let stopTelemetryStopwatch = resourceType => {
      let histogramId = `FX_STARTUP_MIGRATION_UNDO_${resourceType.toUpperCase()}_MS`;
      TelemetryStopwatch.finishKeyed(histogramId, browserId);
    };
    startTelemetryStopwatch("bookmarks");
    await this._removeUnchangedBookmarks(stateData.get("bookmarks")).catch(ex => {
      Cu.reportError("Uncaught exception when removing unchanged bookmarks!");
      Cu.reportError(ex);
    });
    stopTelemetryStopwatch("bookmarks");
    reportErrorTelemetry("bookmarks");
    histogram.add(15);

    startTelemetryStopwatch("visits");
    await this._removeSomeVisits(stateData.get("visits")).catch(ex => {
      Cu.reportError("Uncaught exception when removing history visits!");
      Cu.reportError(ex);
    });
    stopTelemetryStopwatch("visits");
    reportErrorTelemetry("visits");
    histogram.add(20);

    startTelemetryStopwatch("logins");
    await this._removeUnchangedLogins(stateData.get("logins")).catch(ex => {
      Cu.reportError("Uncaught exception when removing unchanged logins!");
      Cu.reportError(ex);
    });
    stopTelemetryStopwatch("logins");
    reportErrorTelemetry("logins");
    histogram.add(25);

    // This is async, but no need to wait for it.
    NewTabUtils.links.populateCache(() => {
      NewTabUtils.allPages.update();
    }, true);

    this._purgeUndoState(this.UNDO_REMOVED_REASON_UNDO_USED);
    histogram.add(30);
    TelemetryStopwatch.finishKeyed("FX_STARTUP_MIGRATION_UNDO_TOTAL_MS", browserId);
  },

  _removeNotificationBars() {
    let browserWindows = Services.wm.getEnumerator("navigator:browser");
    while (browserWindows.hasMoreElements()) {
      let win = browserWindows.getNext();
      if (!win.closed) {
        for (let browser of win.gBrowser.browsers) {
          let nb = win.gBrowser.getNotificationBox(browser);
          let notification = nb.getNotificationWithValue(kNotificationId);
          if (notification) {
            nb.removeNotification(notification);
          }
        }
      }
    }
  },

  _purgeUndoState(reason) {
    // We don't wait for the off-main-thread removal to complete. OS.File will
    // ensure it happens before shutdown.
    OS.File.remove(kUndoStateFullPath, {ignoreAbsent: true}).then(() => {
      this._pendingUndoTasks = false;
    });

    let migrationBrowser = Preferences.get(kAutoMigrateBrowserPref, "unknown");
    Services.prefs.clearUserPref(kAutoMigrateBrowserPref);

    let histogram =
      Services.telemetry.getKeyedHistogramById("FX_STARTUP_MIGRATION_UNDO_REASON");
    histogram.add(migrationBrowser, reason);
  },

  getBrowserUsedForMigration() {
    let browserId = Services.prefs.getCharPref(kAutoMigrateBrowserPref);
    if (browserId) {
      return MigrationUtils.getBrowserName(browserId);
    }
    return null;
  },

  /**
   * Decide if we need to show [the user] a prompt indicating we automatically
   * imported their data.
   * @param target (xul:browser)
   *        The browser in which we should show the notification.
   * @returns {Boolean} return true when need to show the prompt.
   */
  async shouldShowMigratePrompt(target) {
    if (!(await this.canUndo())) {
      return false;
    }

    // The tab might have navigated since we requested the undo state:
    let canUndoFromThisPage = ["about:home", "about:newtab"].includes(target.currentURI.spec);
    if (!canUndoFromThisPage ||
        !Preferences.get(kUndoUIEnabledPref, false)) {
      return false;
    }

    // At this stage we're committed to show the prompt - unless we shouldn't,
    // in which case we remove the undo prefs (which will cause canUndo() to
    // return false from now on.):
    if (this.isMigratePromptExpired()) {
      this._purgeUndoState(this.UNDO_REMOVED_REASON_OFFER_EXPIRED);
      this._removeNotificationBars();
      return false;
    }

    let remainingDays = Preferences.get(kAutoMigrateDaysToOfferUndoPref, 0);
    Services.telemetry.getHistogramById("FX_STARTUP_MIGRATION_UNDO_OFFERED").add(4 - remainingDays);

    return true;
  },

  /**
   * Return the message that denotes the user data is migrated from the other browser.
   * @returns {String} imported message with the brand and the browser name
   */
  getUndoMigrationMessage() {
    let browserName = this.getBrowserUsedForMigration();
    if (!browserName) {
      browserName = MigrationUtils.getLocalizedString("automigration.undo.unknownbrowser");
    }
    const kMessageId = "automigration.undo.message2." +
                      Preferences.get(kAutoMigrateImportedItemIds, "all");
    const kBrandShortName = gBrandBundle.GetStringFromName("brandShortName");
    return MigrationUtils.getLocalizedString(kMessageId,
                                             [kBrandShortName, browserName]);
  },

  /**
   * Show the user a notification bar indicating we automatically imported
   * their data and offering them the possibility of removing it.
   * @param target (xul:browser)
   *        The browser in which we should show the notification.
   */
  showUndoNotificationBar(target) {
    let isInPage = Preferences.get(kInPageUIEnabledPref, false);
    let win = target.ownerGlobal;
    let notificationBox = win.gBrowser.getNotificationBox(target);
    if (isInPage || !notificationBox || notificationBox.getNotificationWithValue(kNotificationId)) {
      return;
    }
    let message = this.getUndoMigrationMessage();
    let buttons = [
      {
        label: MigrationUtils.getLocalizedString("automigration.undo.keep2.label"),
        accessKey: MigrationUtils.getLocalizedString("automigration.undo.keep2.accesskey"),
        callback: () => {
          this.keepAutoMigration();
          this._removeNotificationBars();
        },
      },
      {
        label: MigrationUtils.getLocalizedString("automigration.undo.dontkeep2.label"),
        accessKey: MigrationUtils.getLocalizedString("automigration.undo.dontkeep2.accesskey"),
        callback: () => {
          this.undoAutoMigration(win);
        },
      },
    ];
    notificationBox.appendNotification(
      message, kNotificationId, null, notificationBox.PRIORITY_INFO_HIGH, buttons
    );
  },


  /**
   * Return true if we have shown the prompt to user several days.
   * (defined in kAutoMigrateDaysToOfferUndoPref)
   */
  isMigratePromptExpired() {
    let today = new Date();
    // Round down to midnight:
    today = new Date(today.getFullYear(), today.getMonth(), today.getDate());
    // We store the unix timestamp corresponding to midnight on the last day
    // on which we prompted. Fetch that and compare it to today's date.
    // (NB: stored as a string because int prefs are too small for unix
    // timestamps.)
    let previousPromptDateMsStr = Preferences.get(kAutoMigrateLastUndoPromptDateMsPref, "0");
    let previousPromptDate = new Date(parseInt(previousPromptDateMsStr, 10));
    if (previousPromptDate < today) {
      let remainingDays = Preferences.get(kAutoMigrateDaysToOfferUndoPref, 4) - 1;
      Preferences.set(kAutoMigrateDaysToOfferUndoPref, remainingDays);
      Preferences.set(kAutoMigrateLastUndoPromptDateMsPref, today.valueOf().toString());
      if (remainingDays <= 0) {
        return true;
      }
    }
    return false;
  },

  UNDO_REMOVED_REASON_UNDO_USED: 0,
  UNDO_REMOVED_REASON_SYNC_SIGNIN: 1,
  UNDO_REMOVED_REASON_PASSWORD_CHANGE: 2,
  UNDO_REMOVED_REASON_BOOKMARK_CHANGE: 3,
  UNDO_REMOVED_REASON_OFFER_EXPIRED: 4,
  UNDO_REMOVED_REASON_OFFER_REJECTED: 5,

  _jsonifyUndoState(state) {
    if (!state) {
      return "null";
    }
    // Deal with date serialization.
    let bookmarks = state.get("bookmarks");
    for (let bm of bookmarks) {
      bm.lastModified = bm.lastModified.getTime();
    }
    let serializableState = {
      bookmarks,
      logins: state.get("logins"),
      visits: state.get("visits"),
    };
    return JSON.stringify(serializableState);
  },

  _dejsonifyUndoState(state) {
    state = JSON.parse(state);
    if (!state) {
      return new Map();
    }
    for (let bm of state.bookmarks) {
      bm.lastModified = new Date(bm.lastModified);
    }
    return new Map([
      ["bookmarks", state.bookmarks],
      ["logins", state.logins],
      ["visits", state.visits],
    ]);
  },

  /**
   * Store the items we've saved into a pref. We use this to be able to show
   * a detailed message to the user indicating what we've imported.
   * @param state (Map)
   *        The 'undo' state for the import, which contains info about
   *        how many items of each kind we've (tried to) import.
   */
  _setImportedItemPrefFromState(state) {
    let itemsWithData = [];
    if (state) {
      for (let itemType of state.keys()) {
        if (state.get(itemType).length) {
          itemsWithData.push(itemType);
        }
      }
    }
    if (itemsWithData.length == 3) {
      itemsWithData = "all";
    } else {
      itemsWithData = itemsWithData.sort().join(".");
    }
    if (itemsWithData) {
      Preferences.set(kAutoMigrateImportedItemIds, itemsWithData);
    }
  },

  /**
   * Used for the shutdown blocker's information field.
   */
  _saveUndoStateTrackerForShutdown: "not running",
  /**
   * Store the information required for using 'undo' of the automatic
   * migration in the user's profile.
   */
  async saveUndoState() {
    let resolveSavingPromise;
    this._saveUndoStateTrackerForShutdown = "processing undo history";
    this._savingPromise = new Promise(resolve => { resolveSavingPromise = resolve; });
    let state = await MigrationUtils.stopAndRetrieveUndoData();

    if (!state || ![...state.values()].some(ary => ary.length > 0)) {
      // If we didn't import anything, abort now.
      resolveSavingPromise();
      return Promise.resolve();
    }

    this._saveUndoStateTrackerForShutdown = "saving imported item list";
    this._setImportedItemPrefFromState(state);

    this._saveUndoStateTrackerForShutdown = "writing undo history";
    this._undoSavePromise = OS.File.writeAtomic(
      kUndoStateFullPath, this._jsonifyUndoState(state), {
        encoding: "utf-8",
        compression: "lz4",
        tmpPath: kUndoStateFullPath + ".tmp",
      });
    this._undoSavePromise.then(
      rv => {
        resolveSavingPromise(rv);
        delete this._savingPromise;
      },
      e => {
        Cu.reportError("Could not write undo state for automatic migration.");
        throw e;
      });
    return this._undoSavePromise;
  },

  async _removeUnchangedBookmarks(bookmarks) {
    if (!bookmarks.length) {
      return;
    }

    let guidToLMMap = new Map(bookmarks.map(b => [b.guid, b.lastModified]));
    let bookmarksFromDB = [];
    let bmPromises = Array.from(guidToLMMap.keys()).map(guid => {
      // Ignore bookmarks where the promise doesn't resolve (ie that are missing)
      // Also check that the bookmark fetch returns isn't null before adding it.
      try {
        return PlacesUtils.bookmarks.fetch(guid).then(bm => bm && bookmarksFromDB.push(bm), () => {});
      } catch (ex) {
        // Ignore immediate exceptions, too.
      }
      return Promise.resolve();
    });
    // We can't use the result of Promise.all because that would include nulls
    // for bookmarks that no longer exist (which we're catching above).
    await Promise.all(bmPromises);
    let unchangedBookmarks = bookmarksFromDB.filter(bm => {
      return bm.lastModified.getTime() == guidToLMMap.get(bm.guid).getTime();
    });

    // We need to remove items without children first, followed by their
    // parents, etc. In order to do this, find out how many ancestors each item
    // has that also appear in our list of things to remove, and sort the items
    // by those numbers. This ensures that children are always removed before
    // their parents.
    function determineAncestorCount(bm) {
      if (bm._ancestorCount) {
        return bm._ancestorCount;
      }
      let myCount = 0;
      let parentBM = unchangedBookmarks.find(item => item.guid == bm.parentGuid);
      if (parentBM) {
        myCount = determineAncestorCount(parentBM) + 1;
      }
      bm._ancestorCount = myCount;
      return myCount;
    }
    unchangedBookmarks.forEach(determineAncestorCount);
    unchangedBookmarks.sort((a, b) => b._ancestorCount - a._ancestorCount);
    for (let {guid} of unchangedBookmarks) {
      // Can't just use a .catch() because Bookmarks.remove() can throw (rather
      // than returning rejected promises).
      try {
        await PlacesUtils.bookmarks.remove(guid, {preventRemovalOfNonEmptyFolders: true});
      } catch (err) {
        if (err && err.message != "Cannot remove a non-empty folder.") {
          this._errorMap.bookmarks++;
          Cu.reportError(err);
        }
      }
    }
  },

  async _removeUnchangedLogins(logins) {
    for (let login of logins) {
      let foundLogins = LoginHelper.searchLoginsWithObject({guid: login.guid});
      if (foundLogins.length) {
        let foundLogin = foundLogins[0];
        foundLogin.QueryInterface(Ci.nsILoginMetaInfo);
        if (foundLogin.timePasswordChanged == login.timePasswordChanged) {
          try {
            Services.logins.removeLogin(foundLogin);
          } catch (ex) {
            Cu.reportError("Failed to remove a login for " + foundLogins.hostname);
            Cu.reportError(ex);
            this._errorMap.logins++;
          }
        }
      }
    }
  },

  async _removeSomeVisits(visits) {
    for (let urlVisits of visits) {
      let urlObj;
      try {
        urlObj = new URL(urlVisits.url);
      } catch (ex) {
        continue;
      }
      let visitData = {
        url: urlObj,
        beginDate: PlacesUtils.toDate(urlVisits.first),
        endDate: PlacesUtils.toDate(urlVisits.last),
        limit: urlVisits.visitCount,
      };
      try {
        await PlacesUtils.history.removeVisitsByFilter(visitData);
      } catch (ex) {
        this._errorMap.visits++;
        try {
          visitData.url = visitData.url.href;
        } catch (ignoredEx) {}
        Cu.reportError("Failed to remove a visit: " + JSON.stringify(visitData));
        Cu.reportError(ex);
      }
    }
  },

  /**
   * Maybe open a new tab with a survey. The tab will only be opened if all of
   * the following are true:
   * - the 'browser.migrate.automigrate.undo-survey' pref is not empty.
   *   It should contain the URL of the survey to open.
   * - the 'browser.migrate.automigrate.undo-survey-locales' pref, a
   *   comma-separated list of language codes, contains the language code
   *   that is currently in use for the 'global' chrome pacakge (ie the
   *   locale in which the user is currently using Firefox).
   *   The URL will be passed through nsIURLFormatter to allow including
   *   build ids etc. The special additional formatting variable
   *   "%IMPORTEDBROWSER" is also replaced with the name of the browser
   *   from which we imported data.
   *
   * @param {Window} chromeWindow   A reference to the window in which to open a link.
   */
  _maybeOpenUndoSurveyTab(chromeWindow) {
    let canDoSurveyInLocale = false;
    try {
      let surveyLocales = Preferences.get(kAutoMigrateUndoSurveyLocalePref, "");
      surveyLocales = surveyLocales.split(",").map(str => str.trim());
      // Strip out any empty elements, so an empty pref doesn't
      // lead to a an array with 1 empty string in it.
      surveyLocales = new Set(surveyLocales.filter(str => !!str));
      canDoSurveyInLocale =
        surveyLocales.has(Services.locale.getAppLocaleAsLangTag());
    } catch (ex) {
      /* ignore exceptions and just don't do the survey. */
    }

    let migrationBrowser = this.getBrowserUsedForMigration();
    let rawURL = Preferences.get(kAutoMigrateUndoSurveyPref, "");
    if (!canDoSurveyInLocale || !migrationBrowser || !rawURL) {
      return;
    }

    let url = Services.urlFormatter.formatURL(rawURL);
    url = url.replace("%IMPORTEDBROWSER%", encodeURIComponent(migrationBrowser));
    chromeWindow.openUILinkIn(url, "tab");
  },

  QueryInterface: XPCOMUtils.generateQI(
    [Ci.nsIObserver, Ci.nsINavBookmarkObserver, Ci.nsISupportsWeakReference]
  ),

  /**
   * Undo action called by the UndoNotification or by the newtab
   * @param chromeWindow A reference to the window in which to open a link.
   */
  undoAutoMigration(chromeWindow) {
    this._maybeOpenUndoSurveyTab(chromeWindow);
    this.undo();
  },

  /**
   * Keep the automigration result and not prompt anymore
   */
  keepAutoMigration() {
    this._purgeUndoState(this.UNDO_REMOVED_REASON_OFFER_REJECTED);
  },
};

AutoMigrate.init();
PK
!<�0N�� modules/BrowserErrorReporter.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/Timer.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");

ChromeUtils.defineModuleGetter(this, "Log", "resource://gre/modules/Log.jsm");
ChromeUtils.defineModuleGetter(this, "UpdateUtils", "resource://gre/modules/UpdateUtils.jsm");

Cu.importGlobalProperties(["fetch", "URL"]);

var EXPORTED_SYMBOLS = ["BrowserErrorReporter"];

const CONTEXT_LINES = 5;
const ERROR_PREFIX_RE = /^[^\W]+:/m;
const PREF_ENABLED = "browser.chrome.errorReporter.enabled";
const PREF_LOG_LEVEL = "browser.chrome.errorReporter.logLevel";
const PREF_PROJECT_ID = "browser.chrome.errorReporter.projectId";
const PREF_PUBLIC_KEY = "browser.chrome.errorReporter.publicKey";
const PREF_SAMPLE_RATE = "browser.chrome.errorReporter.sampleRate";
const PREF_SUBMIT_URL = "browser.chrome.errorReporter.submitUrl";
const SDK_NAME = "firefox-error-reporter";
const SDK_VERSION = "1.0.0";

// https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIScriptError#Categories
const REPORTED_CATEGORIES = new Set([
  "XPConnect JavaScript",
  "component javascript",
  "chrome javascript",
  "chrome registration",
  "XBL",
  "XBL Prototype Handler",
  "XBL Content Sink",
  "xbl javascript",
  "FrameConstructor",
]);

/**
 * Collects nsIScriptError messages logged to the browser console and reports
 * them to a remotely-hosted error collection service.
 *
 * This is a PROTOTYPE; it will be removed in the future and potentially
 * replaced with a more robust implementation. It is meant to only collect
 * errors from Nightly (and local builds if enabled for development purposes)
 * and has not been reviewed for use outside of Nightly.
 *
 * The outgoing requests are designed to be compatible with Sentry. See
 * https://docs.sentry.io/clientdev/ for details on the data format that Sentry
 * expects.
 *
 * Errors may contain PII, such as in messages or local file paths in stack
 * traces; see bug 1426482 for privacy review and server-side mitigation.
 */
class BrowserErrorReporter {
  constructor(fetchMethod = this._defaultFetch) {
    // A fake fetch is passed by the tests to avoid network connections
    this.fetch = fetchMethod;

    // Values that don't change between error reports.
    this.requestBodyTemplate = {
      logger: "javascript",
      platform: "javascript",
      release: Services.appinfo.version,
      environment: UpdateUtils.getUpdateChannel(false),
      tags: {
        appBuildID: Services.appinfo.appBuildID,
        changeset: AppConstants.SOURCE_REVISION_URL,
      },
      sdk: {
        name: SDK_NAME,
        version: SDK_VERSION,
      },
    };

    XPCOMUtils.defineLazyPreferenceGetter(
      this,
      "collectionEnabled",
      PREF_ENABLED,
      false,
      this.handleEnabledPrefChanged.bind(this),
    );
  }

  /**
   * Lazily-created logger
   */
  get logger() {
    const logger = Log.repository.getLogger("BrowserErrorReporter");
    logger.addAppender(new Log.ConsoleAppender(new Log.BasicFormatter()));
    logger.manageLevelFromPref(PREF_LOG_LEVEL);

    Object.defineProperty(this, "logger", {value: logger});
    return this.logger;
  }

  init() {
    if (this.collectionEnabled) {
      Services.console.registerListener(this);

      // Processing already-logged messages in case any errors occurred before
      // startup.
      for (const message of Services.console.getMessageArray()) {
        this.observe(message);
      }
    }
  }

  uninit() {
    try {
      Services.console.unregisterListener(this);
    } catch (err) {} // It probably wasn't registered.
  }

  handleEnabledPrefChanged(prefName, previousValue, newValue) {
    if (newValue) {
      Services.console.registerListener(this);
    } else {
      try {
        Services.console.unregisterListener(this);
      } catch (err) {} // It probably wasn't registered.
    }
  }

  async observe(message) {
    try {
      message.QueryInterface(Ci.nsIScriptError);
    } catch (err) {
      return; // Not an error
    }

    const isWarning = message.flags & message.warningFlag;
    const isFromChrome = REPORTED_CATEGORIES.has(message.category);
    if (!isFromChrome || isWarning) {
      return;
    }

    // Sample the amount of errors we send out
    const sampleRate = Number.parseFloat(Services.prefs.getCharPref(PREF_SAMPLE_RATE));
    if (!Number.isFinite(sampleRate) || (Math.random() >= sampleRate)) {
      return;
    }

    // Parse the error type from the message if present (e.g. "TypeError: Whoops").
    let errorMessage = message.errorMessage;
    let errorName = "Error";
    if (message.errorMessage.match(ERROR_PREFIX_RE)) {
      const parts = message.errorMessage.split(":");
      errorName = parts[0];
      errorMessage = parts.slice(1).join(":").trim();
    }

    const frames = [];
    let frame = message.stack;
    // Avoid an infinite loop by limiting traces to 100 frames.
    while (frame && frames.length < 100) {
      frames.push(await this.normalizeStackFrame(frame));
      frame = frame.parent;
    }
    // Frames are sent in order from oldest to newest.
    frames.reverse();

    const requestBody = Object.assign({}, this.requestBodyTemplate, {
      timestamp: new Date().toISOString().slice(0, -1), // Remove trailing "Z"
      project: Services.prefs.getCharPref(PREF_PROJECT_ID),
      exception: {
        values: [
          {
            type: errorName,
            value: errorMessage,
            stacktrace: {
              frames,
            }
          },
        ],
      },
      culprit: message.sourceName,
    });

    const url = new URL(Services.prefs.getCharPref(PREF_SUBMIT_URL));
    url.searchParams.set("sentry_client", `${SDK_NAME}/${SDK_VERSION}`);
    url.searchParams.set("sentry_version", "7");
    url.searchParams.set("sentry_key", Services.prefs.getCharPref(PREF_PUBLIC_KEY));

    try {
      await this.fetch(url, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          "Accept": "application/json",
        },
        // Sentry throws an auth error without a referrer specified.
        referrer: "https://fake.mozilla.org",
        body: JSON.stringify(requestBody)
      });
      this.logger.debug("Sent error successfully.");
    } catch (error) {
      this.logger.warn(`Failed to send error: ${error}`);
    }
  }

  async normalizeStackFrame(frame) {
    const normalizedFrame = {
      function: frame.functionDisplayName,
      module: frame.source,
      lineno: frame.line,
      colno: frame.column,
    };

    try {
      const response = await fetch(frame.source);
      const sourceCode = await response.text();
      const sourceLines = sourceCode.split(/\r?\n/);
      // HTML pages and some inline event handlers have 0 as their line number
      let lineIndex = Math.max(frame.line - 1, 0);

      // XBL line numbers are off by one, and pretty much every XML file with JS
      // in it is an XBL file.
      if (frame.source.endsWith(".xml") && lineIndex > 0) {
        lineIndex--;
      }

      normalizedFrame.context_line = sourceLines[lineIndex];
      normalizedFrame.pre_context = sourceLines.slice(
        Math.max(lineIndex - CONTEXT_LINES, 0),
        lineIndex,
      );
      normalizedFrame.post_context = sourceLines.slice(
        lineIndex + 1,
        Math.min(lineIndex + 1 + CONTEXT_LINES, sourceLines.length),
      );
    } catch (err) {
      // Could be a fetch issue, could be a line index issue. Not much we can
      // do to recover in either case.
    }

    return normalizedFrame;
  }

  async _defaultFetch(...args) {
    // Do not make network requests while running in automation
    if (Cu.isInAutomation) {
      return null;
    }

    return fetch(...args);
  }
}
PK
!<F<m��Y�Y!modules/BrowserUsageTelemetry.jsm/* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = [
  "BrowserUsageTelemetry",
  "URLBAR_SELECTED_RESULT_TYPES",
  "URLBAR_SELECTED_RESULT_METHODS",
  "MINIMUM_TAB_COUNT_INTERVAL_MS",
 ];

ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");

ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
                               "resource://gre/modules/PrivateBrowsingUtils.jsm");

// The upper bound for the count of the visited unique domain names.
const MAX_UNIQUE_VISITED_DOMAINS = 100;

// Observed topic names.
const TAB_RESTORING_TOPIC = "SSTabRestoring";
const TELEMETRY_SUBSESSIONSPLIT_TOPIC = "internal-telemetry-after-subsession-split";
const DOMWINDOW_OPENED_TOPIC = "domwindowopened";
const AUTOCOMPLETE_ENTER_TEXT_TOPIC = "autocomplete-did-enter-text";

// Probe names.
const MAX_TAB_COUNT_SCALAR_NAME = "browser.engagement.max_concurrent_tab_count";
const MAX_WINDOW_COUNT_SCALAR_NAME = "browser.engagement.max_concurrent_window_count";
const TAB_OPEN_EVENT_COUNT_SCALAR_NAME = "browser.engagement.tab_open_event_count";
const WINDOW_OPEN_EVENT_COUNT_SCALAR_NAME = "browser.engagement.window_open_event_count";
const UNIQUE_DOMAINS_COUNT_SCALAR_NAME = "browser.engagement.unique_domains_count";
const TOTAL_URI_COUNT_SCALAR_NAME = "browser.engagement.total_uri_count";
const UNFILTERED_URI_COUNT_SCALAR_NAME = "browser.engagement.unfiltered_uri_count";

// A list of known search origins.
const KNOWN_SEARCH_SOURCES = [
  "abouthome",
  "contextmenu",
  "newtab",
  "searchbar",
  "urlbar",
];

const KNOWN_ONEOFF_SOURCES = [
  "oneoff-urlbar",
  "oneoff-searchbar",
  "unknown", // Edge case: this is the searchbar (see bug 1195733 comment 7).
];

/**
 * The buckets used for logging telemetry to the FX_URLBAR_SELECTED_RESULT_TYPE
 * histogram.
 */
const URLBAR_SELECTED_RESULT_TYPES = {
  autofill: 0,
  bookmark: 1,
  history: 2,
  keyword: 3,
  searchengine: 4,
  searchsuggestion: 5,
  switchtab: 6,
  tag: 7,
  visiturl: 8,
  remotetab: 9,
  extension: 10,
  "preloaded-top-site": 11,
};

/**
 * This maps the categories used by the FX_URLBAR_SELECTED_RESULT_METHOD and
 * FX_SEARCHBAR_SELECTED_RESULT_METHOD histograms to their indexes in the
 * `labels` array.  This only needs to be used by tests that need to map from
 * category names to indexes in histogram snapshots.  Actual app code can use
 * these category names directly when they add to a histogram.
 */
const URLBAR_SELECTED_RESULT_METHODS = {
  enter: 0,
  enterSelection: 1,
  click: 2,
  arrowEnterSelection: 3,
  tabEnterSelection: 4,
};


const MINIMUM_TAB_COUNT_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes, in ms


function getOpenTabsAndWinsCounts() {
  let tabCount = 0;
  let winCount = 0;

  let browserEnum = Services.wm.getEnumerator("navigator:browser");
  while (browserEnum.hasMoreElements()) {
    let win = browserEnum.getNext();
    winCount++;
    tabCount += win.gBrowser.tabs.length;
  }

  return { tabCount, winCount };
}

function getTabCount() {
  return getOpenTabsAndWinsCounts().tabCount;
}

function getSearchEngineId(engine) {
  if (engine) {
    if (engine.identifier) {
      return engine.identifier;
    }
    if (engine.name) {
      return "other-" + engine.name;
    }
  }
  return "other";
}

let URICountListener = {
  // A set containing the visited domains, see bug 1271310.
  _domainSet: new Set(),
  // A map to keep track of the URIs loaded from the restored tabs.
  _restoredURIsMap: new WeakMap(),

  isHttpURI(uri) {
    // Only consider http(s) schemas.
    return uri.schemeIs("http") || uri.schemeIs("https");
  },

  addRestoredURI(browser, uri) {
    if (!this.isHttpURI(uri)) {
      return;
    }

    this._restoredURIsMap.set(browser, uri.spec);
  },

  onLocationChange(browser, webProgress, request, uri, flags) {
    // Don't count this URI if it's an error page.
    if (flags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE) {
      return;
    }

    // We only care about top level loads.
    if (!webProgress.isTopLevel) {
      return;
    }

    // The SessionStore sets the URI of a tab first, firing onLocationChange the
    // first time, then manages content loading using its scheduler. Once content
    // loads, we will hit onLocationChange again.
    // We can catch the first case by checking for null requests: be advised that
    // this can also happen when navigating page fragments, so account for it.
    if (!request &&
        !(flags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT)) {
      return;
    }

    // Track URI loads, even if they're not http(s).
    let uriSpec = null;
    try {
      uriSpec = uri.spec;
    } catch (e) {
      // If we have troubles parsing the spec, still count this as
      // an unfiltered URI.
      Services.telemetry.scalarAdd(UNFILTERED_URI_COUNT_SCALAR_NAME, 1);
      return;
    }


    // Don't count about:blank and similar pages, as they would artificially
    // inflate the counts.
    if (browser.ownerGlobal.gInitialPages.includes(uriSpec)) {
      return;
    }

    // If the URI we're loading is in the _restoredURIsMap, then it comes from a
    // restored tab. If so, let's skip it and remove it from the map as we want to
    // count page refreshes.
    if (this._restoredURIsMap.get(browser) === uriSpec) {
      this._restoredURIsMap.delete(browser);
      return;
    }

    // The URI wasn't from a restored tab. Count it among the unfiltered URIs.
    // If this is an http(s) URI, this also gets counted by the "total_uri_count"
    // probe.
    Services.telemetry.scalarAdd(UNFILTERED_URI_COUNT_SCALAR_NAME, 1);

    if (!this.isHttpURI(uri)) {
      return;
    }

    // Update the URI counts.
    Services.telemetry.scalarAdd(TOTAL_URI_COUNT_SCALAR_NAME, 1);

    // Update tab count
    BrowserUsageTelemetry._recordTabCount();

    // We only want to count the unique domains up to MAX_UNIQUE_VISITED_DOMAINS.
    if (this._domainSet.size == MAX_UNIQUE_VISITED_DOMAINS) {
      return;
    }

    // Unique domains should be aggregated by (eTLD + 1): x.test.com and y.test.com
    // are counted once as test.com.
    try {
      // Even if only considering http(s) URIs, |getBaseDomain| could still throw
      // due to the URI containing invalid characters or the domain actually being
      // an ipv4 or ipv6 address.
      this._domainSet.add(Services.eTLD.getBaseDomain(uri));
    } catch (e) {
      return;
    }

    Services.telemetry.scalarSet(UNIQUE_DOMAINS_COUNT_SCALAR_NAME, this._domainSet.size);
  },

  /**
   * Reset the counts. This should be called when breaking a session in Telemetry.
   */
  reset() {
    this._domainSet.clear();
  },

  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
                                         Ci.nsISupportsWeakReference]),
};

let urlbarListener = {

  // This is needed for recordUrlbarSelectedResultMethod().
  selectedIndex: -1,

  init() {
    Services.obs.addObserver(this, AUTOCOMPLETE_ENTER_TEXT_TOPIC, true);
  },

  uninit() {
    Services.obs.removeObserver(this, AUTOCOMPLETE_ENTER_TEXT_TOPIC);
  },

  observe(subject, topic, data) {
    switch (topic) {
      case AUTOCOMPLETE_ENTER_TEXT_TOPIC:
        this._handleURLBarTelemetry(subject.QueryInterface(Ci.nsIAutoCompleteInput));
        break;
    }
  },

  /**
   * Used to log telemetry when the user enters text in the urlbar.
   *
   * @param {nsIAutoCompleteInput} input  The autocomplete element where the
   *                                      text was entered.
   */
  _handleURLBarTelemetry(input) {
    if (!input || input.id != "urlbar") {
      return;
    }
    if (input.inPrivateContext || input.popup.selectedIndex < 0) {
      this.selectedIndex = -1;
      return;
    }

    // Except for the history popup, the urlbar always has a selection.  The
    // first result at index 0 is the "heuristic" result that indicates what
    // will happen when you press the Enter key.  Treat it as no selection.
    this.selectedIndex =
      input.popup.selectedIndex > 0 || !input.popup._isFirstResultHeuristic ?
      input.popup.selectedIndex :
      -1;

    let controller =
      input.popup.view.QueryInterface(Ci.nsIAutoCompleteController);
    let idx = input.popup.selectedIndex;
    let value = controller.getValueAt(idx);
    let action = input._parseActionUrl(value);
    let actionType;
    if (action) {
      actionType =
        action.type == "searchengine" && action.params.searchSuggestion ?
          "searchsuggestion" :
        action.type;
    }
    if (!actionType) {
      let styles = new Set(controller.getStyleAt(idx).split(/\s+/));
      let style = ["preloaded-top-site", "autofill", "tag", "bookmark"].find(s => styles.has(s));
      actionType = style || "history";
    }

    Services.telemetry
            .getHistogramById("FX_URLBAR_SELECTED_RESULT_INDEX")
            .add(idx);

    // You can add values but don't change any of the existing values.
    // Otherwise you'll break our data.
    if (actionType in URLBAR_SELECTED_RESULT_TYPES) {
      Services.telemetry
              .getHistogramById("FX_URLBAR_SELECTED_RESULT_TYPE")
              .add(URLBAR_SELECTED_RESULT_TYPES[actionType]);
      Services.telemetry
              .getKeyedHistogramById("FX_URLBAR_SELECTED_RESULT_INDEX_BY_TYPE")
              .add(actionType, idx);
    } else {
      Cu.reportError("Unknown FX_URLBAR_SELECTED_RESULT_TYPE type: " +
                     actionType);
    }
  },

  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                         Ci.nsISupportsWeakReference]),
};

let BrowserUsageTelemetry = {
  _inited: false,

  init() {
    this._lastRecordTabCount = 0;
    urlbarListener.init();
    this._setupAfterRestore();
    this._inited = true;
  },

  /**
   * Handle subsession splits in the parent process.
   */
  afterSubsessionSplit() {
    // Scalars just got cleared due to a subsession split. We need to set the maximum
    // concurrent tab and window counts so that they reflect the correct value for the
    // new subsession.
    const counts = getOpenTabsAndWinsCounts();
    Services.telemetry.scalarSetMaximum(MAX_TAB_COUNT_SCALAR_NAME, counts.tabCount);
    Services.telemetry.scalarSetMaximum(MAX_WINDOW_COUNT_SCALAR_NAME, counts.winCount);

    // Reset the URI counter.
    URICountListener.reset();
  },

  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                         Ci.nsISupportsWeakReference]),

  uninit() {
    if (!this._inited) {
      return;
    }
    Services.obs.removeObserver(this, DOMWINDOW_OPENED_TOPIC);
    Services.obs.removeObserver(this, TELEMETRY_SUBSESSIONSPLIT_TOPIC);
    urlbarListener.uninit();
  },

  observe(subject, topic, data) {
    switch (topic) {
      case DOMWINDOW_OPENED_TOPIC:
        this._onWindowOpen(subject);
        break;
      case TELEMETRY_SUBSESSIONSPLIT_TOPIC:
        this.afterSubsessionSplit();
        break;
    }
  },

  handleEvent(event) {
    switch (event.type) {
      case "TabOpen":
        this._onTabOpen();
        break;
      case "unload":
        this._unregisterWindow(event.target);
        break;
      case TAB_RESTORING_TOPIC:
        // We're restoring a new tab from a previous or crashed session.
        // We don't want to track the URIs from these tabs, so let
        // |URICountListener| know about them.
        let browser = event.target.linkedBrowser;
        URICountListener.addRestoredURI(browser, browser.currentURI);
        break;
    }
  },

  /**
   * The main entry point for recording search related Telemetry. This includes
   * search counts and engagement measurements.
   *
   * Telemetry records only search counts per engine and action origin, but
   * nothing pertaining to the search contents themselves.
   *
   * @param {nsISearchEngine} engine
   *        The engine handling the search.
   * @param {String} source
   *        Where the search originated from. See KNOWN_SEARCH_SOURCES for allowed
   *        values.
   * @param {Object} [details] Options object.
   * @param {Boolean} [details.isOneOff=false]
   *        true if this event was generated by a one-off search.
   * @param {Boolean} [details.isSuggestion=false]
   *        true if this event was generated by a suggested search.
   * @param {Boolean} [details.isAlias=false]
   *        true if this event was generated by a search using an alias.
   * @param {Object} [details.type=null]
   *        The object describing the event that triggered the search.
   * @throws if source is not in the known sources list.
   */
  recordSearch(engine, source, details = {}) {
    const isOneOff = !!details.isOneOff;
    const countId = getSearchEngineId(engine) + "." + source;

    if (isOneOff) {
      if (!KNOWN_ONEOFF_SOURCES.includes(source)) {
        // Silently drop the error if this bogus call
        // came from 'urlbar' or 'searchbar'. They're
        // calling |recordSearch| twice from two different
        // code paths because they want to record the search
        // in SEARCH_COUNTS.
        if (["urlbar", "searchbar"].includes(source)) {
          Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS").add(countId);
          return;
        }
        throw new Error("Unknown source for one-off search: " + source);
      }
    } else {
      if (!KNOWN_SEARCH_SOURCES.includes(source)) {
        throw new Error("Unknown source for search: " + source);
      }
      Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS").add(countId);
    }

    // Dispatch the search signal to other handlers.
    this._handleSearchAction(engine, source, details);
  },

  _recordSearch(engine, source, action = null) {
    let scalarKey = action ? "search_" + action : "search";
    Services.telemetry.keyedScalarAdd("browser.engagement.navigation." + source,
                                      scalarKey, 1);
    Services.telemetry.recordEvent("navigation", "search", source, action,
                                   { engine: getSearchEngineId(engine) });
  },

  _handleSearchAction(engine, source, details) {
    switch (source) {
      case "urlbar":
      case "oneoff-urlbar":
      case "searchbar":
      case "oneoff-searchbar":
      case "unknown": // Edge case: this is the searchbar (see bug 1195733 comment 7).
        this._handleSearchAndUrlbar(engine, source, details);
        break;
      case "abouthome":
        this._recordSearch(engine, "about_home", "enter");
        break;
      case "newtab":
        this._recordSearch(engine, "about_newtab", "enter");
        break;
      case "contextmenu":
        this._recordSearch(engine, "contextmenu");
        break;
    }
  },

  /**
   * This function handles the "urlbar", "urlbar-oneoff", "searchbar" and
   * "searchbar-oneoff" sources.
   */
  _handleSearchAndUrlbar(engine, source, details) {
    // We want "urlbar" and "urlbar-oneoff" (and similar cases) to go in the same
    // scalar, but in a different key.

    // When using one-offs in the searchbar we get an "unknown" source. See bug
    // 1195733 comment 7 for the context. Fix-up the label here.
    const sourceName =
      (source === "unknown") ? "searchbar" : source.replace("oneoff-", "");

    const isOneOff = !!details.isOneOff;
    if (isOneOff) {
      // We will receive a signal from the "urlbar"/"searchbar" even when the
      // search came from "oneoff-urlbar". That's because both signals
      // are propagated from search.xml. Skip it if that's the case.
      // Moreover, we skip the "unknown" source that comes from the searchbar
      // when performing searches from the default search engine. See bug 1195733
      // comment 7 for context.
      if (["urlbar", "searchbar", "unknown"].includes(source)) {
        return;
      }

      // If that's a legit one-off search signal, record it using the relative key.
      this._recordSearch(engine, sourceName, "oneoff");
      return;
    }

    // The search was not a one-off. It was a search with the default search engine.
    if (details.isSuggestion) {
      // It came from a suggested search, so count it as such.
      this._recordSearch(engine, sourceName, "suggestion");
      return;
    } else if (details.isAlias) {
      // This one came from a search that used an alias.
      this._recordSearch(engine, sourceName, "alias");
      return;
    }

    // The search signal was generated by typing something and pressing enter.
    this._recordSearch(engine, sourceName, "enter");
  },

  /**
   * Records the method by which the user selected a urlbar result.
   *
   * @param {nsIDOMEvent} event
   *        The event that triggered the selection.
   * @param {string} userSelectionBehavior
   *        How the user cycled through results before picking the current match.
   *        Could be one of "tab", "arrow" or "none".
   */
  recordUrlbarSelectedResultMethod(event, userSelectionBehavior = "none") {
    // The reason this method relies on urlbarListener instead of having the
    // caller pass in an index is that by the time the urlbar handles a
    // selection, the selection in its popup has been cleared, so it's not easy
    // to tell which popup index was selected.  Fortunately this file already
    // has urlbarListener, which gets notified of selections in the urlbar
    // before the popup selection is cleared, so just use that.

    this._recordUrlOrSearchbarSelectedResultMethod(
      event, urlbarListener.selectedIndex,
      "FX_URLBAR_SELECTED_RESULT_METHOD",
      userSelectionBehavior
    );
  },

  /**
   * Records the method by which the user selected a searchbar result.
   *
   * @param {nsIDOMEvent} event
   *        The event that triggered the selection.
   * @param {number} highlightedIndex
   *        The index that the user chose in the popup, or -1 if there wasn't a
   *        selection.
   */
  recordSearchbarSelectedResultMethod(event, highlightedIndex) {
    this._recordUrlOrSearchbarSelectedResultMethod(
      event, highlightedIndex,
      "FX_SEARCHBAR_SELECTED_RESULT_METHOD",
      "none"
    );
  },

  _recordUrlOrSearchbarSelectedResultMethod(event, highlightedIndex, histogramID, userSelectionBehavior) {
    let histogram = Services.telemetry.getHistogramById(histogramID);
    // command events are from the one-off context menu.  Treat them as clicks.
    let isClick = event instanceof Ci.nsIDOMMouseEvent ||
                  (event && event.type == "command");
    let category;
    if (isClick) {
      category = "click";
    } else if (highlightedIndex >= 0) {
      switch (userSelectionBehavior) {
      case "tab":
        category = "tabEnterSelection";
        break;
      case "arrow":
        category = "arrowEnterSelection";
        break;
      default:
        category = "enterSelection";
      }
    } else {
      category = "enter";
    }
    histogram.add(category);
  },

  /**
   * This gets called shortly after the SessionStore has finished restoring
   * windows and tabs. It counts the open tabs and adds listeners to all the
   * windows.
   */
  _setupAfterRestore() {
    // Make sure to catch new chrome windows and subsession splits.
    Services.obs.addObserver(this, DOMWINDOW_OPENED_TOPIC, true);
    Services.obs.addObserver(this, TELEMETRY_SUBSESSIONSPLIT_TOPIC, true);

    // Attach the tabopen handlers to the existing Windows.
    let browserEnum = Services.wm.getEnumerator("navigator:browser");
    while (browserEnum.hasMoreElements()) {
      this._registerWindow(browserEnum.getNext());
    }

    // Get the initial tab and windows max counts.
    const counts = getOpenTabsAndWinsCounts();
    Services.telemetry.scalarSetMaximum(MAX_TAB_COUNT_SCALAR_NAME, counts.tabCount);
    Services.telemetry.scalarSetMaximum(MAX_WINDOW_COUNT_SCALAR_NAME, counts.winCount);
  },

  /**
   * Adds listeners to a single chrome window.
   */
  _registerWindow(win) {
    win.addEventListener("unload", this);
    win.addEventListener("TabOpen", this, true);

    // Don't include URI and domain counts when in private mode.
    if (PrivateBrowsingUtils.isWindowPrivate(win)) {
      return;
    }
    win.gBrowser.tabContainer.addEventListener(TAB_RESTORING_TOPIC, this);
    win.gBrowser.addTabsProgressListener(URICountListener);
  },

  /**
   * Removes listeners from a single chrome window.
   */
  _unregisterWindow(win) {
    win.removeEventListener("unload", this);
    win.removeEventListener("TabOpen", this, true);

    // Don't include URI and domain counts when in private mode.
    if (PrivateBrowsingUtils.isWindowPrivate(win.defaultView)) {
      return;
    }
    win.defaultView.gBrowser.tabContainer.removeEventListener(TAB_RESTORING_TOPIC, this);
    win.defaultView.gBrowser.removeTabsProgressListener(URICountListener);
  },

  /**
   * Updates the tab counts.
   * @param {Number} [newTabCount=0] The count of the opened tabs across all windows. This
   *        is computed manually if not provided.
   */
  _onTabOpen(tabCount = 0) {
    // Use the provided tab count if available. Otherwise, go on and compute it.
    tabCount = tabCount || getOpenTabsAndWinsCounts().tabCount;
    // Update the "tab opened" count and its maximum.
    Services.telemetry.scalarAdd(TAB_OPEN_EVENT_COUNT_SCALAR_NAME, 1);
    Services.telemetry.scalarSetMaximum(MAX_TAB_COUNT_SCALAR_NAME, tabCount);

    this._recordTabCount(tabCount);
  },

  /**
   * Tracks the window count and registers the listeners for the tab count.
   * @param{Object} win The window object.
   */
  _onWindowOpen(win) {
    // Make sure to have a |nsIDOMWindow|.
    if (!(win instanceof Ci.nsIDOMWindow)) {
      return;
    }

    let onLoad = () => {
      win.removeEventListener("load", onLoad);

      // Ignore non browser windows.
      if (win.document.documentElement.getAttribute("windowtype") != "navigator:browser") {
        return;
      }

      this._registerWindow(win);
      // Track the window open event and check the maximum.
      const counts = getOpenTabsAndWinsCounts();
      Services.telemetry.scalarAdd(WINDOW_OPEN_EVENT_COUNT_SCALAR_NAME, 1);
      Services.telemetry.scalarSetMaximum(MAX_WINDOW_COUNT_SCALAR_NAME, counts.winCount);

      // We won't receive the "TabOpen" event for the first tab within a new window.
      // Account for that.
      this._onTabOpen(counts.tabCount);
    };
    win.addEventListener("load", onLoad);
  },

  _recordTabCount(tabCount) {
    let currentTime = Date.now();
    if (currentTime > this._lastRecordTabCount + MINIMUM_TAB_COUNT_INTERVAL_MS) {
      if (tabCount === undefined) {
        tabCount = getTabCount();
      }
      Services.telemetry.getHistogramById("TAB_COUNT").add(tabCount);
      this._lastRecordTabCount = currentTime;
    }
  }
};
PK
!<1lV��'�' modules/ChromeMigrationUtils.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";

var EXPORTED_SYMBOLS = ["ChromeMigrationUtils"];

ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
ChromeUtils.import("resource://gre/modules/osfile.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");

var ChromeMigrationUtils = {
  _extensionVersionDirectoryNames: {},

  // The cache for the locale strings.
  // For example, the data could be:
  // {
  //   "profile-id-1": {
  //     "extension-id-1": {
  //       "name": {
  //         "message": "Fake App 1"
  //       }
  //   },
  // }
  _extensionLocaleStrings: {},

  /**
   * Get all extensions installed in a specific profile.
   * @param {String} profileId - A Chrome user profile ID. For example, "Profile 1".
   * @returns {Array} All installed Chrome extensions information.
   */
  async getExtensionList(profileId) {
    if (profileId === undefined) {
      profileId = await this.getLastUsedProfileId();
    }
    let path = this.getExtensionPath(profileId);
    let iterator = new OS.File.DirectoryIterator(path);
    let extensionList = [];
    await iterator.forEach(async entry => {
      if (entry.isDir) {
        let extensionInformation = await this.getExtensionInformation(entry.name, profileId);
        if (extensionInformation) {
          extensionList.push(extensionInformation);
        }
      }
    }).catch(ex => Cu.reportError(ex));
    return extensionList;
  },

  /**
   * Get information of a specific Chrome extension.
   * @param {String} extensionId - The extension ID.
   * @param {String} profileId - The user profile's ID.
   * @retruns {Object} The Chrome extension information.
   */
  async getExtensionInformation(extensionId, profileId) {
    if (profileId === undefined) {
      profileId = await this.getLastUsedProfileId();
    }
    let extensionInformation = null;
    try {
      let manifestPath = this.getExtensionPath(profileId);
      manifestPath = OS.Path.join(manifestPath, extensionId);
      // If there are multiple sub-directories in the extension directory,
      // read the files in the latest directory.
      let directories = await this._getSortedByVersionSubDirectoryNames(manifestPath);
      if (!directories[0]) {
        return null;
      }

      manifestPath = OS.Path.join(manifestPath, directories[0], "manifest.json");
      let manifest = await OS.File.read(manifestPath, { encoding: "utf-8" });
      manifest = JSON.parse(manifest);
      // No app attribute means this is a Chrome extension not a Chrome app.
      if (!manifest.app) {
        const DEFAULT_LOCALE = manifest.default_locale;
        let name = await this._getLocaleString(manifest.name, DEFAULT_LOCALE, extensionId, profileId);
        let description = await this._getLocaleString(manifest.description, DEFAULT_LOCALE, extensionId, profileId);
        if (name) {
          extensionInformation = {
            id: extensionId,
            name,
            description,
          };
        } else {
          throw new Error("Cannot read the Chrome extension's name property.");
        }
      }
    } catch (ex) {
      Cu.reportError(ex);
    }
    return extensionInformation;
  },

  /**
   * Get the manifest's locale string.
   * @param {String} key - The key of a locale string, for example __MSG_name__.
   * @param {String} locale - The specific language of locale string.
   * @param {String} extensionId - The extension ID.
   * @param {String} profileId - The user profile's ID.
   * @retruns {String} The locale string.
   */
  async _getLocaleString(key, locale, extensionId, profileId) {
    // Return the key string if it is not a locale key.
    // The key string starts with "__MSG_" and ends with "__".
    // For example, "__MSG_name__".
    // https://developer.chrome.com/apps/i18n
    if (!key.startsWith("__MSG_") || !key.endsWith("__")) {
      return key;
    }

    let localeString = null;
    try {
      let localeFile;
      if (this._extensionLocaleStrings[profileId] &&
          this._extensionLocaleStrings[profileId][extensionId]) {
        localeFile = this._extensionLocaleStrings[profileId][extensionId];
      } else {
        if (!this._extensionLocaleStrings[profileId]) {
          this._extensionLocaleStrings[profileId] = {};
        }
        let localeFilePath = this.getExtensionPath(profileId);
        localeFilePath = OS.Path.join(localeFilePath, extensionId);
        let directories = await this._getSortedByVersionSubDirectoryNames(localeFilePath);
        // If there are multiple sub-directories in the extension directory,
        // read the files in the latest directory.
        localeFilePath = OS.Path.join(localeFilePath, directories[0], "_locales", locale, "messages.json");
        localeFile = await OS.File.read(localeFilePath, { encoding: "utf-8" });
        localeFile = JSON.parse(localeFile);
        this._extensionLocaleStrings[profileId][extensionId] = localeFile;
      }
      const PREFIX_LENGTH = 6;
      const SUFFIX_LENGTH = 2;
      // Get the locale key from the string with locale prefix and suffix.
      // For example, it will get the "name" sub-string from the "__MSG_name__" string.
      key = key.substring(PREFIX_LENGTH, key.length - SUFFIX_LENGTH);
      if (localeFile[key] && localeFile[key].message) {
        localeString = localeFile[key].message;
      }
    } catch (ex) {
      Cu.reportError(ex);
    }
    return localeString;
  },

  /**
   * Check that a specific extension is installed or not.
   * @param {String} extensionId - The extension ID.
   * @param {String} profileId - The user profile's ID.
   * @returns {Boolean} Return true if the extension is installed otherwise return false.
   */
  async isExtensionInstalled(extensionId, profileId) {
    if (profileId === undefined) {
      profileId = await this.getLastUsedProfileId();
    }
    let extensionPath = this.getExtensionPath(profileId);
    let isInstalled = await OS.File.exists(OS.Path.join(extensionPath, extensionId));
    return isInstalled;
  },

  /**
   * Get the last used user profile's ID.
   * @returns {String} The last used user profile's ID.
   */
  async getLastUsedProfileId() {
    let localState = await this.getLocalState();
    return localState ? localState.profile.last_used : "Default";
  },

  /**
   * Get the local state file content.
   * @param {String} dataPath the type of Chrome data we're looking for (Chromium, Canary, etc.)
   * @returns {Object} The JSON-based content.
   */
  async getLocalState(dataPath = "Chrome") {
    let localState = null;
    try {
      let localStatePath = OS.Path.join(this.getDataPath(dataPath), "Local State");
      let localStateJson = await OS.File.read(localStatePath, { encoding: "utf-8" });
      localState = JSON.parse(localStateJson);
    } catch (ex) {
      Cu.reportError(ex);
      throw ex;
    }
    return localState;
  },

  /**
   * Get the path of Chrome extension directory.
   * @param {String} profileId - The user profile's ID.
   * @returns {String} The path of Chrome extension directory.
   */
  getExtensionPath(profileId) {
    return OS.Path.join(this.getDataPath(), profileId, "Extensions");
  },

  /**
   * Get the path of an application data directory.
   * @param {String} chromeProjectName - The Chrome project name, e.g. "Chrome", "Canary", etc.
   *                                     Defaults to "Chrome".
   * @returns {String} The path of application data directory.
   */
  getDataPath(chromeProjectName = "Chrome") {
    const SUB_DIRECTORIES = {
      win: {
        Chrome: ["Google", "Chrome"],
        Chromium: ["Chromium"],
        Canary: ["Google", "Chrome SxS"],
      },
      macosx: {
        Chrome: ["Google", "Chrome"],
        Chromium: ["Chromium"],
        Canary: ["Google", "Chrome Canary"],
      },
      linux: {
        Chrome: ["google-chrome"],
        "Chrome Beta": ["google-chrome-beta"],
        "Chrome Dev": ["google-chrome-unstable"],
        Chromium: ["chromium"],
        // Canary is not available on Linux.
      },
    };
    let dirKey, subfolders;
    subfolders = SUB_DIRECTORIES[AppConstants.platform][chromeProjectName];
    if (!subfolders) {
      return null;
    }

    if (AppConstants.platform == "win") {
      dirKey = "winLocalAppDataDir";
      subfolders = subfolders.concat(["User Data"]);
    } else if (AppConstants.platform == "macosx") {
      dirKey = "macUserLibDir";
      subfolders = ["Application Support"].concat(subfolders);
    } else {
      dirKey = "homeDir";
      subfolders = [".config"].concat(subfolders);
    }
    subfolders.unshift(OS.Constants.Path[dirKey]);
    return OS.Path.join(...subfolders);
  },

  /**
   * Get the directory objects sorted by version number.
   * @param {String} path - The path to the extension directory.
   * otherwise return all file/directory object.
   * @returns {Array} The file/directory object array.
   */
  async _getSortedByVersionSubDirectoryNames(path) {
    if (this._extensionVersionDirectoryNames[path]) {
      return this._extensionVersionDirectoryNames[path];
    }

    let iterator = new OS.File.DirectoryIterator(path);
    let entries = [];
    await iterator.forEach(async entry => {
      if (entry.isDir) {
        entries.push(entry.name);
      }
    }).catch(ex => {
      Cu.reportError(ex);
      entries = [];
    });
    // The directory name is the version number string of the extension.
    // For example, it could be "1.0_0", "1.0_1", "1.0_2", 1.1_0, 1.1_1, or 1.1_2.
    // The "1.0_1" strings mean that the "1.0_0" directory is existed and you install the version 1.0 again.
    // https://chromium.googlesource.com/chromium/src/+/0b58a813992b539a6b555ad7959adfad744b095a/chrome/common/extensions/extension_file_util_unittest.cc
    entries.sort((a, b) => Services.vc.compare(b, a));

    this._extensionVersionDirectoryNames[path] = entries;
    return entries;
  },
};
PK
!<�M4u��modules/ContentClick.jsm/* -*- mode: js; indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = [ "ContentClick" ];

ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");

ChromeUtils.defineModuleGetter(this, "PlacesUIUtils",
                               "resource:///modules/PlacesUIUtils.jsm");
ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
                               "resource://gre/modules/PrivateBrowsingUtils.jsm");

var ContentClick = {
  // Listeners are added in nsBrowserGlue.js
  receiveMessage(message) {
    switch (message.name) {
      case "Content:Click":
        // Bug 1446913 - Ensure this happens in the next tick so TabOpen/TabMove
        // events are processed correctly in webextensions.
        Promise.resolve().then(() => {
          this.contentAreaClick(message.json, message.target);
        });
        break;
    }
  },

  /**
   * Handles clicks in the content area.
   *
   * @param json {Object} JSON object that looks like an Event
   * @param browser {Element<browser>}
   */
  contentAreaClick(json, browser) {
    // This is heavily based on contentAreaClick from browser.js (Bug 903016)
    // The json is set up in a way to look like an Event.
    let window = browser.ownerGlobal;

    if (!json.href) {
      // Might be middle mouse navigation.
      if (Services.prefs.getBoolPref("middlemouse.contentLoadURL") &&
          !Services.prefs.getBoolPref("general.autoScroll")) {
        window.middleMousePaste(json);
      }
      return;
    }

    if (json.bookmark) {
      // This is the Opera convention for a special link that, when clicked,
      // allows to add a sidebar panel.  The link's title attribute contains
      // the title that should be used for the sidebar panel.
      PlacesUIUtils.showBookmarkDialog({ action: "add",
                                         type: "bookmark",
                                         uri: Services.io.newURI(json.href),
                                         title: json.title,
                                         loadBookmarkInSidebar: true,
                                         hiddenRows: [ "description",
                                                        "location",
                                                        "keyword" ]
                                       }, window);
      return;
    }

    // Note: We don't need the sidebar code here.

    // Mark the page as a user followed link.  This is done so that history can
    // distinguish automatic embed visits from user activated ones.  For example
    // pages loaded in frames are embed visits and lost with the session, while
    // visits across frames should be preserved.
    try {
      if (!PrivateBrowsingUtils.isWindowPrivate(window))
        PlacesUIUtils.markPageAsFollowedLink(json.href);
    } catch (ex) { /* Skip invalid URIs. */ }

    // This part is based on handleLinkClick.
    var where = window.whereToOpenLink(json);
    if (where == "current")
      return;

    // Todo(903022): code for where == save

    let params = {
      charset: browser.characterSet,
      referrerURI: browser.documentURI,
      referrerPolicy: json.referrerPolicy,
      noReferrer: json.noReferrer,
      allowMixedContent: json.allowMixedContent,
      isContentWindowPrivate: json.isContentWindowPrivate,
      originPrincipal: json.originPrincipal,
      triggeringPrincipal: json.triggeringPrincipal,
      frameOuterWindowID: json.frameOuterWindowID,
    };

    // The new tab/window must use the same userContextId.
    if (json.originAttributes.userContextId) {
      params.userContextId = json.originAttributes.userContextId;
    }

    window.openLinkIn(json.href, where, params);
  }
};
PK
!<=�F7ΆΆ modules/ContentCrashHandlers.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = [ "TabCrashHandler",
                         "PluginCrashReporter",
                         "UnsubmittedCrashHandler" ];

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");

ChromeUtils.defineModuleGetter(this, "CrashSubmit",
  "resource://gre/modules/CrashSubmit.jsm");
ChromeUtils.defineModuleGetter(this, "AppConstants",
  "resource://gre/modules/AppConstants.jsm");
ChromeUtils.defineModuleGetter(this, "RemotePages",
  "resource://gre/modules/RemotePageManager.jsm");
ChromeUtils.defineModuleGetter(this, "SessionStore",
  "resource:///modules/sessionstore/SessionStore.jsm");
ChromeUtils.defineModuleGetter(this, "RecentWindow",
  "resource:///modules/RecentWindow.jsm");
ChromeUtils.defineModuleGetter(this, "PluralForm",
  "resource://gre/modules/PluralForm.jsm");
ChromeUtils.defineModuleGetter(this, "setTimeout",
  "resource://gre/modules/Timer.jsm");
ChromeUtils.defineModuleGetter(this, "clearTimeout",
  "resource://gre/modules/Timer.jsm");

XPCOMUtils.defineLazyGetter(this, "gNavigatorBundle", function() {
  const url = "chrome://browser/locale/browser.properties";
  return Services.strings.createBundle(url);
});

// We don't process crash reports older than 28 days, so don't bother
// submitting them
const PENDING_CRASH_REPORT_DAYS = 28;
const DAY = 24 * 60 * 60 * 1000; // milliseconds
const DAYS_TO_SUPPRESS = 30;
const MAX_UNSEEN_CRASHED_CHILD_IDS = 20;

// Time after which we will begin scanning for unsubmitted crash reports
const CHECK_FOR_UNSUBMITTED_CRASH_REPORTS_DELAY_MS = 60 * 10000; // 10 minutes

/**
 * BrowserWeakMap is exactly like a WeakMap, but expects <xul:browser>
 * objects only.
 *
 * Under the hood, BrowserWeakMap keys the map off of the <xul:browser>
 * permanentKey. If, however, the browser has never gotten a permanentKey,
 * it falls back to keying on the <xul:browser> element itself.
 */
class BrowserWeakMap extends WeakMap {
  get(browser) {
    if (browser.permanentKey) {
      return super.get(browser.permanentKey);
    }
    return super.get(browser);
  }

  set(browser, value) {
    if (browser.permanentKey) {
      return super.set(browser.permanentKey, value);
    }
    return super.set(browser, value);
  }

  delete(browser) {
    if (browser.permanentKey) {
      return super.delete(browser.permanentKey);
    }
    return super.delete(browser);
  }
}

var TabCrashHandler = {
  _crashedTabCount: 0,
  childMap: new Map(),
  browserMap: new BrowserWeakMap(),
  unseenCrashedChildIDs: [],
  crashedBrowserQueues: new Map(),

  get prefs() {
    delete this.prefs;
    return this.prefs = Services.prefs.getBranch("browser.tabs.crashReporting.");
  },

  init() {
    if (this.initialized)
      return;
    this.initialized = true;

    Services.obs.addObserver(this, "ipc:content-shutdown");
    Services.obs.addObserver(this, "oop-frameloader-crashed");

    this.pageListener = new RemotePages("about:tabcrashed");
    // LOAD_BACKGROUND pages don't fire load events, so the about:tabcrashed
    // content will fire up its own message when its initial scripts have
    // finished running.
    this.pageListener.addMessageListener("Load", this.receiveMessage.bind(this));
    this.pageListener.addMessageListener("RemotePage:Unload", this.receiveMessage.bind(this));
    this.pageListener.addMessageListener("closeTab", this.receiveMessage.bind(this));
    this.pageListener.addMessageListener("restoreTab", this.receiveMessage.bind(this));
    this.pageListener.addMessageListener("restoreAll", this.receiveMessage.bind(this));
  },

  observe(aSubject, aTopic, aData) {
    switch (aTopic) {
      case "ipc:content-shutdown": {
        aSubject.QueryInterface(Ci.nsIPropertyBag2);

        if (!aSubject.get("abnormal")) {
          return;
        }

        let childID = aSubject.get("childID");
        let dumpID = aSubject.get("dumpID");

        if (!dumpID) {
          Services.telemetry
                  .getHistogramById("FX_CONTENT_CRASH_DUMP_UNAVAILABLE")
                  .add(1);
        } else if (AppConstants.MOZ_CRASHREPORTER) {
          this.childMap.set(childID, dumpID);
        }

        if (!this.flushCrashedBrowserQueue(childID)) {
          this.unseenCrashedChildIDs.push(childID);
          // The elements in unseenCrashedChildIDs will only be removed if
          // the tab crash page is shown. However, ipc:content-shutdown might
          // be fired for processes for which we'll never show the tab crash
          // page - for example, the thumbnailing process. Another case to
          // consider is if the user is configured to submit backlogged crash
          // reports automatically, and a background tab crashes. In that case,
          // we will never show the tab crash page, and never remove the element
          // from the list.
          //
          // Instead of trying to account for all of those cases, we prevent
          // this list from getting too large by putting a reasonable upper
          // limit on how many childIDs we track. It's unlikely that this
          // array would ever get so large as to be unwieldy (that'd be a lot
          // or crashes!), but a leak is a leak.
          if (this.unseenCrashedChildIDs.length > MAX_UNSEEN_CRASHED_CHILD_IDS) {
            this.unseenCrashedChildIDs.shift();
          }
        }

        // check for environment affecting crash reporting
        let env = Cc["@mozilla.org/process/environment;1"]
                    .getService(Ci.nsIEnvironment);
        let shutdown = env.exists("MOZ_CRASHREPORTER_SHUTDOWN");

        if (shutdown) {
          dump("A content process crashed and MOZ_CRASHREPORTER_SHUTDOWN is " +
               "set, shutting down\n");
          Services.startup.quit(Ci.nsIAppStartup.eForceQuit);
        }

        break;
      }
      case "oop-frameloader-crashed": {
        aSubject.QueryInterface(Ci.nsIFrameLoader);

        let browser = aSubject.ownerElement;
        if (!browser) {
          return;
        }

        this.browserMap.set(browser, aSubject.childID);
        break;
      }
    }
  },

  receiveMessage(message) {
    let browser = message.target.browser;
    let gBrowser = browser.ownerGlobal.gBrowser;
    let tab = gBrowser.getTabForBrowser(browser);

    switch (message.name) {
      case "Load": {
        this.onAboutTabCrashedLoad(message);
        break;
      }

      case "RemotePage:Unload": {
        this.onAboutTabCrashedUnload(message);
        break;
      }

      case "closeTab": {
        this.maybeSendCrashReport(message);
        gBrowser.removeTab(tab, { animate: true });
        break;
      }

      case "restoreTab": {
        this.maybeSendCrashReport(message);
        SessionStore.reviveCrashedTab(tab);
        break;
      }

      case "restoreAll": {
        this.maybeSendCrashReport(message);
        SessionStore.reviveAllCrashedTabs();
        break;
      }
    }
  },

  /**
   * This should be called once a content process has finished
   * shutting down abnormally. Any tabbrowser browsers that were
   * selected at the time of the crash will then be sent to
   * the crashed tab page.
   *
   * @param childID (int)
   *        The childID of the content process that just crashed.
   * @returns boolean
   *        True if one or more browsers were sent to the tab crashed
   *        page.
   */
  flushCrashedBrowserQueue(childID) {
    let browserQueue = this.crashedBrowserQueues.get(childID);
    if (!browserQueue) {
      return false;
    }

    this.crashedBrowserQueues.delete(childID);

    let sentBrowser = false;
    for (let weakBrowser of browserQueue) {
      let browser = weakBrowser.get();
      if (browser) {
        this.sendToTabCrashedPage(browser);
        sentBrowser = true;
      }
    }

    return sentBrowser;
  },

  /**
   * Called by a tabbrowser when it notices that its selected browser
   * has crashed. This will queue the browser to show the tab crash
   * page once the content process has finished tearing down.
   *
   * @param browser (<xul:browser>)
   *        The selected browser that just crashed.
   */
  onSelectedBrowserCrash(browser) {
    if (!browser.isRemoteBrowser) {
      Cu.reportError("Selected crashed browser is not remote.");
      return;
    }
    if (!browser.frameLoader) {
      Cu.reportError("Selected crashed browser has no frameloader.");
      return;
    }

    let childID = browser.frameLoader.childID;
    let browserQueue = this.crashedBrowserQueues.get(childID);
    if (!browserQueue) {
      browserQueue = [];
      this.crashedBrowserQueues.set(childID, browserQueue);
    }
    // It's probably unnecessary to store this browser as a
    // weak reference, since the content process should complete
    // its teardown in the same tick of the event loop, and then
    // this queue will be flushed. The weak reference is to avoid
    // leaking browsers in case anything goes wrong during this
    // teardown process.
    browserQueue.push(Cu.getWeakReference(browser));
  },

  /**
   * This method is exposed for SessionStore to call if the user selects
   * a tab which will restore on demand. It's possible that the tab
   * is in this state because it recently crashed. If that's the case, then
   * it's also possible that the user has not seen the tab crash page for
   * that particular crash, in which case, we might show it to them instead
   * of restoring the tab.
   *
   * @param browser (<xul:browser>)
   *        A browser from a browser tab that the user has just selected
   *        to restore on demand.
   * @returns (boolean)
   *        True if TabCrashHandler will send the user to the tab crash
   *        page instead.
   */
  willShowCrashedTab(browser) {
    let childID = this.browserMap.get(browser);
    // We will only show the tab crash page if:
    // 1) We are aware that this browser crashed
    // 2) We know we've never shown the tab crash page for the
    //    crash yet
    // 3) The user is not configured to automatically submit backlogged
    //    crash reports. If they are, we'll send the crash report
    //    immediately.
    if (childID &&
        this.unseenCrashedChildIDs.includes(childID)) {
      if (UnsubmittedCrashHandler.autoSubmit) {
        let dumpID = this.childMap.get(childID);
        if (dumpID) {
          UnsubmittedCrashHandler.submitReports([dumpID]);
        }
      } else {
        this.sendToTabCrashedPage(browser);
        return true;
      }
    }

    return false;
  },

  /**
   * We show a special page to users when a normal browser tab has crashed.
   * This method should be called to send a browser to that page once the
   * process has completely closed.
   *
   * @param browser (<xul:browser>)
   *        The browser that has recently crashed.
   */
  sendToTabCrashedPage(browser) {
    let title = browser.contentTitle;
    let uri = browser.currentURI;
    let gBrowser = browser.ownerGlobal.gBrowser;
    let tab = gBrowser.getTabForBrowser(browser);
    // The tab crashed page is non-remote by default.
    gBrowser.updateBrowserRemoteness(browser, false);

    browser.setAttribute("crashedPageTitle", title);
    browser.docShell.displayLoadError(Cr.NS_ERROR_CONTENT_CRASHED, uri, null);
    browser.removeAttribute("crashedPageTitle");
    tab.setAttribute("crashed", true);
  },

  /**
   * Submits a crash report from about:tabcrashed, if the crash
   * reporter is enabled and a crash report can be found.
   *
   * @param aBrowser
   *        The <xul:browser> that the report was sent from.
   * @param aFormData
   *        An Object with the following properties:
   *
   *        includeURL (bool):
   *          Whether to include the URL that the user was on
   *          in the crashed tab before the crash occurred.
   *        URL (String)
   *          The URL that the user was on in the crashed tab
   *          before the crash occurred.
   *        emailMe (bool):
   *          Whether or not to include the user's email address
   *          in the crash report.
   *        email (String):
   *          The email address of the user.
   *        comments (String):
   *          Any additional comments from the user.
   *
   *        Note that it is expected that all properties are set,
   *        even if they are empty.
   */
  maybeSendCrashReport(message) {
    if (!AppConstants.MOZ_CRASHREPORTER) {
      return;
    }

    if (!message.data.hasReport) {
      // There was no report, so nothing to do.
      return;
    }

    let browser = message.target.browser;

    if (message.data.autoSubmit) {
      // The user has opted in to autosubmitted backlogged
      // crash reports in the future.
      UnsubmittedCrashHandler.autoSubmit = true;
    }

    let childID = this.browserMap.get(browser);
    let dumpID = this.childMap.get(childID);
    if (!dumpID) {
      return;
    }

    if (!message.data.sendReport) {
      Services.telemetry.getHistogramById("FX_CONTENT_CRASH_NOT_SUBMITTED").add(1);
      this.prefs.setBoolPref("sendReport", false);
      return;
    }

    let {
      includeURL,
      comments,
      email,
      emailMe,
      URL,
    } = message.data;

    let extraExtraKeyVals = {
      "Comments": comments,
      "Email": email,
      "URL": URL,
    };

    // For the entries in extraExtraKeyVals, we only want to submit the
    // extra data values where they are not the empty string.
    for (let key in extraExtraKeyVals) {
      let val = extraExtraKeyVals[key].trim();
      if (!val) {
        delete extraExtraKeyVals[key];
      }
    }

    // URL is special, since it's already been written to extra data by
    // default. In order to make sure we don't send it, we overwrite it
    // with the empty string.
    if (!includeURL) {
      extraExtraKeyVals.URL = "";
    }

    CrashSubmit.submit(dumpID, {
      recordSubmission: true,
      extraExtraKeyVals,
    }).catch(Cu.reportError);

    this.prefs.setBoolPref("sendReport", true);
    this.prefs.setBoolPref("includeURL", includeURL);
    this.prefs.setBoolPref("emailMe", emailMe);
    if (emailMe) {
      this.prefs.setCharPref("email", email);
    } else {
      this.prefs.setCharPref("email", "");
    }

    this.childMap.set(childID, null); // Avoid resubmission.
    this.removeSubmitCheckboxesForSameCrash(childID);
  },

  removeSubmitCheckboxesForSameCrash(childID) {
    let enumerator = Services.wm.getEnumerator("navigator:browser");
    while (enumerator.hasMoreElements()) {
      let window = enumerator.getNext();
      if (!window.gMultiProcessBrowser)
        continue;

      for (let browser of window.gBrowser.browsers) {
        if (browser.isRemoteBrowser)
          continue;

        let doc = browser.contentDocument;
        if (!doc.documentURI.startsWith("about:tabcrashed"))
          continue;

        if (this.browserMap.get(browser) == childID) {
          this.browserMap.delete(browser);
          let ports = this.pageListener.portsForBrowser(browser);
          if (ports.length) {
            // For about:tabcrashed, we don't expect subframes. We can
            // assume sending to the first port is sufficient.
            ports[0].sendAsyncMessage("CrashReportSent");
          }
        }
      }
    }
  },

  onAboutTabCrashedLoad(message) {
    this._crashedTabCount++;

    // Broadcast to all about:tabcrashed pages a count of
    // how many about:tabcrashed pages exist, so that they
    // can decide whether or not to display the "Restore All
    // Crashed Tabs" button.
    this.pageListener.sendAsyncMessage("UpdateCount", {
      count: this._crashedTabCount,
    });

    let browser = message.target.browser;

    let childID = this.browserMap.get(browser);
    let index = this.unseenCrashedChildIDs.indexOf(childID);
    if (index != -1) {
      this.unseenCrashedChildIDs.splice(index, 1);
    }

    let dumpID = this.getDumpID(browser);
    if (!dumpID) {
      message.target.sendAsyncMessage("SetCrashReportAvailable", {
        hasReport: false,
      });
      return;
    }

    let requestAutoSubmit = !UnsubmittedCrashHandler.autoSubmit;
    let requestEmail = this.prefs.getBoolPref("requestEmail");
    let sendReport = this.prefs.getBoolPref("sendReport");
    let includeURL = this.prefs.getBoolPref("includeURL");
    let emailMe = this.prefs.getBoolPref("emailMe");

    let data = {
      hasReport: true,
      sendReport,
      includeURL,
      emailMe,
      requestAutoSubmit,
      requestEmail,
    };

    if (emailMe) {
      data.email = this.prefs.getCharPref("email");
    }

    // Make sure to only count once even if there are multiple windows
    // that will all show about:tabcrashed.
    if (this._crashedTabCount == 1) {
      Services.telemetry.getHistogramById("FX_CONTENT_CRASH_PRESENTED").add(1);
    }

    message.target.sendAsyncMessage("SetCrashReportAvailable", data);
  },

  onAboutTabCrashedUnload(message) {
    if (!this._crashedTabCount) {
      Cu.reportError("Can not decrement crashed tab count to below 0");
      return;
    }
    this._crashedTabCount--;

    // Broadcast to all about:tabcrashed pages a count of
    // how many about:tabcrashed pages exist, so that they
    // can decide whether or not to display the "Restore All
    // Crashed Tabs" button.
    this.pageListener.sendAsyncMessage("UpdateCount", {
      count: this._crashedTabCount,
    });

    let browser = message.target.browser;
    let childID = this.browserMap.get(browser);

    // Make sure to only count once even if there are multiple windows
    // that will all show about:tabcrashed.
    if (this._crashedTabCount == 0 && childID) {
      Services.telemetry.getHistogramById("FX_CONTENT_CRASH_NOT_SUBMITTED").add(1);
    }
  },

  /**
   * For some <xul:browser>, return a crash report dump ID for that browser
   * if we have been informed of one. Otherwise, return null.
   *
   * @param browser (<xul:browser)
   *        The browser to try to get the dump ID for
   * @returns dumpID (String)
   */
  getDumpID(browser) {
    if (!AppConstants.MOZ_CRASHREPORTER) {
      return null;
    }

    return this.childMap.get(this.browserMap.get(browser));
  },
};

/**
 * This component is responsible for scanning the pending
 * crash report directory for reports, and (if enabled), to
 * prompt the user to submit those reports. It might also
 * submit those reports automatically without prompting if
 * the user has opted in.
 */
var UnsubmittedCrashHandler = {
  get prefs() {
    delete this.prefs;
    return this.prefs =
      Services.prefs.getBranch("browser.crashReports.unsubmittedCheck.");
  },

  get enabled() {
    return this.prefs.getBoolPref("enabled");
  },

  // showingNotification is set to true once a notification
  // is successfully shown, and then set back to false if
  // the notification is dismissed by an action by the user.
  showingNotification: false,
  // suppressed is true if we've determined that we've shown
  // the notification too many times across too many days without
  // user interaction, so we're suppressing the notification for
  // some number of days. See the documentation for
  // shouldShowPendingSubmissionsNotification().
  suppressed: false,

  _checkTimeout: null,

  init() {
    if (this.initialized) {
      return;
    }

    this.initialized = true;

    // UnsubmittedCrashHandler can be initialized but still be disabled.
    // This is intentional, as this makes simulating UnsubmittedCrashHandler's
    // reactions to browser startup and shutdown easier in test automation.
    //
    // UnsubmittedCrashHandler, when initialized but not enabled, is inert.
    if (this.enabled) {
      if (this.prefs.prefHasUserValue("suppressUntilDate")) {
        if (this.prefs.getCharPref("suppressUntilDate") > this.dateString()) {
          // We'll be suppressing any notifications until after suppressedDate,
          // so there's no need to do anything more.
          this.suppressed = true;
          return;
        }

        // We're done suppressing, so we don't need this pref anymore.
        this.prefs.clearUserPref("suppressUntilDate");
      }

      Services.obs.addObserver(this, "profile-before-change");
    }
  },

  uninit() {
    if (!this.initialized) {
      return;
    }

    this.initialized = false;

    if (this._checkTimeout) {
      clearTimeout(this._checkTimeout);
      this._checkTimeout = null;
    }

    if (!this.enabled) {
      return;
    }

    if (this.suppressed) {
      this.suppressed = false;
      // No need to do any more clean-up, since we were suppressed.
      return;
    }

    if (this.showingNotification) {
      this.prefs.setBoolPref("shutdownWhileShowing", true);
      this.showingNotification = false;
    }

    Services.obs.removeObserver(this, "profile-before-change");
  },

  observe(subject, topic, data) {
    switch (topic) {
      case "profile-before-change": {
        this.uninit();
        break;
      }
    }
  },

  scheduleCheckForUnsubmittedCrashReports() {
    this._checkTimeout = setTimeout(() => {
      Services.tm.idleDispatchToMainThread(() => {
        this.checkForUnsubmittedCrashReports();
      });
    }, CHECK_FOR_UNSUBMITTED_CRASH_REPORTS_DELAY_MS);
  },

  /**
   * Scans the profile directory for unsubmitted crash reports
   * within the past PENDING_CRASH_REPORT_DAYS days. If it
   * finds any, it will, if necessary, attempt to open a notification
   * bar to prompt the user to submit them.
   *
   * @returns Promise
   *          Resolves with the <xul:notification> after it tries to
   *          show a notification on the most recent browser window.
   *          If a notification cannot be shown, will resolve with null.
   */
  async checkForUnsubmittedCrashReports() {
    if (!this.enabled || this.suppressed) {
      return null;
    }

    let dateLimit = new Date();
    dateLimit.setDate(dateLimit.getDate() - PENDING_CRASH_REPORT_DAYS);

    let reportIDs = [];
    try {
      reportIDs = await CrashSubmit.pendingIDs(dateLimit);
    } catch (e) {
      Cu.reportError(e);
      return null;
    }

    if (reportIDs.length) {
      if (this.autoSubmit) {
        this.submitReports(reportIDs);
      } else if (this.shouldShowPendingSubmissionsNotification()) {
        return this.showPendingSubmissionsNotification(reportIDs);
      }
    }
    return null;
  },

  /**
   * Returns true if the notification should be shown.
   * shouldShowPendingSubmissionsNotification makes this decision
   * by looking at whether or not the user has seen the notification
   * over several days without ever interacting with it. If this occurs
   * too many times, we suppress the notification for DAYS_TO_SUPPRESS
   * days.
   *
   * @returns bool
   */
  shouldShowPendingSubmissionsNotification() {
    if (!this.prefs.prefHasUserValue("shutdownWhileShowing")) {
      return true;
    }

    let shutdownWhileShowing = this.prefs.getBoolPref("shutdownWhileShowing");
    this.prefs.clearUserPref("shutdownWhileShowing");

    if (!this.prefs.prefHasUserValue("lastShownDate")) {
      // This isn't expected, but we're being defensive here. We'll
      // opt for showing the notification in this case.
      return true;
    }

    let lastShownDate = this.prefs.getCharPref("lastShownDate");
    if (this.dateString() > lastShownDate && shutdownWhileShowing) {
      // We're on a newer day then when we last showed the
      // notification without closing it. We don't want to do
      // this too many times, so we'll decrement a counter for
      // this situation. Too many of these, and we'll assume the
      // user doesn't know or care about unsubmitted notifications,
      // and we'll suppress the notification for a while.
      let chances = this.prefs.getIntPref("chancesUntilSuppress");
      if (--chances < 0) {
        // We're out of chances!
        this.prefs.clearUserPref("chancesUntilSuppress");
        // We'll suppress for DAYS_TO_SUPPRESS days.
        let suppressUntil =
          this.dateString(new Date(Date.now() + (DAY * DAYS_TO_SUPPRESS)));
        this.prefs.setCharPref("suppressUntilDate", suppressUntil);
        return false;
      }
      this.prefs.setIntPref("chancesUntilSuppress", chances);
    }

    return true;
  },

  /**
   * Given an array of unsubmitted crash report IDs, try to open
   * up a notification asking the user to submit them.
   *
   * @param reportIDs (Array<string>)
   *        The Array of report IDs to offer the user to send.
   * @returns The <xul:notification> if one is shown. null otherwise.
   */
  showPendingSubmissionsNotification(reportIDs) {
    let count = reportIDs.length;
    if (!count) {
      return null;
    }

    let messageTemplate =
      gNavigatorBundle.GetStringFromName("pendingCrashReports2.label");

    let message = PluralForm.get(count, messageTemplate).replace("#1", count);

    let notification = this.show({
      notificationID: "pending-crash-reports",
      message,
      reportIDs,
      onAction: () => {
        this.showingNotification = false;
      },
    });

    if (notification) {
      this.showingNotification = true;
      this.prefs.setCharPref("lastShownDate", this.dateString());
    }

    return notification;
  },

  /**
   * Returns a string representation of a Date in the format
   * YYYYMMDD.
   *
   * @param someDate (Date, optional)
   *        The Date to convert to the string. If not provided,
   *        defaults to today's date.
   * @returns String
   */
  dateString(someDate = new Date()) {
    let year = String(someDate.getFullYear()).padStart(4, "0");
    let month = String(someDate.getMonth() + 1).padStart(2, "0");
    let day = String(someDate.getDate()).padStart(2, "0");
    return year + month + day;
  },

  /**
   * Attempts to show a notification bar to the user in the most
   * recent browser window asking them to submit some crash report
   * IDs. If a notification cannot be shown (for example, there
   * is no browser window), this method exits silently.
   *
   * The notification will allow the user to submit their crash
   * reports. If the user dismissed the notification, the crash
   * reports will be marked to be ignored (though they can
   * still be manually submitted via about:crashes).
   *
   * @param JS Object
   *        An Object with the following properties:
   *
   *        notificationID (string)
   *          The ID for the notification to be opened.
   *
   *        message (string)
   *          The message to be displayed in the notification.
   *
   *        reportIDs (Array<string>)
   *          The array of report IDs to offer to the user.
   *
   *        onAction (function, optional)
   *          A callback to fire once the user performs an
   *          action on the notification bar (this includes
   *          dismissing the notification).
   *
   * @returns The <xul:notification> if one is shown. null otherwise.
   */
  show({ notificationID, message, reportIDs, onAction }) {
    let chromeWin = RecentWindow.getMostRecentBrowserWindow();
    if (!chromeWin) {
      // Can't show a notification in this case. We'll hopefully
      // get another opportunity to have the user submit their
      // crash reports later.
      return null;
    }

    let nb =  chromeWin.document.getElementById("global-notificationbox");
    let notification = nb.getNotificationWithValue(notificationID);
    if (notification) {
      return null;
    }

    let buttons = [{
      label: gNavigatorBundle.GetStringFromName("pendingCrashReports.send"),
      callback: () => {
        this.submitReports(reportIDs);
        if (onAction) {
          onAction();
        }
      },
    },
    {
      label: gNavigatorBundle.GetStringFromName("pendingCrashReports.alwaysSend"),
      callback: () => {
        this.autoSubmit = true;
        this.submitReports(reportIDs);
        if (onAction) {
          onAction();
        }
      },
    },
    {
      label: gNavigatorBundle.GetStringFromName("pendingCrashReports.viewAll"),
      callback() {
        chromeWin.openUILinkIn("about:crashes", "tab");
        return true;
      },
    }];

    let eventCallback = (eventType) => {
      if (eventType == "dismissed") {
        // The user intentionally dismissed the notification,
        // which we interpret as meaning that they don't care
        // to submit the reports. We'll ignore these particular
        // reports going forward.
        reportIDs.forEach(function(reportID) {
          CrashSubmit.ignore(reportID);
        });
        if (onAction) {
          onAction();
        }
      }
    };

    return nb.appendNotification(message, notificationID,
                                 "chrome://browser/skin/tab-crashed.svg",
                                 nb.PRIORITY_INFO_HIGH, buttons,
                                 eventCallback);
  },

  get autoSubmit() {
    return Services.prefs
                   .getBoolPref("browser.crashReports.unsubmittedCheck.autoSubmit2");
  },

  set autoSubmit(val) {
    Services.prefs.setBoolPref("browser.crashReports.unsubmittedCheck.autoSubmit2",
                               val);
  },

  /**
   * Attempt to submit reports to the crash report server. Each
   * report will have the "SubmittedFromInfobar" extra key set
   * to true.
   *
   * @param reportIDs (Array<string>)
   *        The array of reportIDs to submit.
   */
  submitReports(reportIDs) {
    for (let reportID of reportIDs) {
      CrashSubmit.submit(reportID, {
        extraExtraKeyVals: {
          "SubmittedFromInfobar": true,
        },
      }).catch(Cu.reportError);
    }
  },
};

var PluginCrashReporter = {
  /**
   * Makes the PluginCrashReporter ready to hear about and
   * submit crash reports.
   */
  init() {
    if (this.initialized) {
      return;
    }

    this.initialized = true;
    this.crashReports = new Map();

    Services.obs.addObserver(this, "plugin-crashed");
    Services.obs.addObserver(this, "gmp-plugin-crash");
    Services.obs.addObserver(this, "profile-after-change");
  },

  uninit() {
    Services.obs.removeObserver(this, "plugin-crashed");
    Services.obs.removeObserver(this, "gmp-plugin-crash");
    Services.obs.removeObserver(this, "profile-after-change");
    this.initialized = false;
  },

  observe(subject, topic, data) {
    switch (topic) {
      case "plugin-crashed": {
        let propertyBag = subject;
        if (!(propertyBag instanceof Ci.nsIPropertyBag2) ||
            !(propertyBag instanceof Ci.nsIWritablePropertyBag2) ||
            !propertyBag.hasKey("runID") ||
            !propertyBag.hasKey("pluginDumpID")) {
          Cu.reportError("PluginCrashReporter can not read plugin information.");
          return;
        }

        let runID = propertyBag.getPropertyAsUint32("runID");
        let pluginDumpID = propertyBag.getPropertyAsAString("pluginDumpID");
        let browserDumpID = propertyBag.getPropertyAsAString("browserDumpID");
        if (pluginDumpID) {
          this.crashReports.set(runID, { pluginDumpID, browserDumpID });
        }
        break;
      }
      case "gmp-plugin-crash": {
        let propertyBag = subject;
        if (!(propertyBag instanceof Ci.nsIWritablePropertyBag2) ||
            !propertyBag.hasKey("pluginID") ||
            !propertyBag.hasKey("pluginDumpID") ||
            !propertyBag.hasKey("pluginName")) {
          Cu.reportError("PluginCrashReporter can not read plugin information.");
          return;
        }

        let pluginID = propertyBag.getPropertyAsUint32("pluginID");
        let pluginDumpID = propertyBag.getPropertyAsAString("pluginDumpID");
        if (pluginDumpID) {
          this.crashReports.set(pluginID, { pluginDumpID });
        }

        // Only the parent process gets the gmp-plugin-crash observer
        // notification, so we need to inform any content processes that
        // the GMP has crashed.
        if (Cc["@mozilla.org/parentprocessmessagemanager;1"]) {
          let pluginName = propertyBag.getPropertyAsAString("pluginName");
          let mm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
            .getService(Ci.nsIMessageListenerManager);
          mm.broadcastAsyncMessage("gmp-plugin-crash",
                                   { pluginName, pluginID });
        }
        break;
      }
      case "profile-after-change":
        this.uninit();
        break;
    }
  },

  /**
   * Submit a crash report for a crashed NPAPI plugin.
   *
   * @param runID
   *        The runID of the plugin that crashed. A run ID is a unique
   *        identifier for a particular run of a plugin process - and is
   *        analogous to a process ID (though it is managed by Gecko instead
   *        of the operating system).
   * @param keyVals
   *        An object whose key-value pairs will be merged
   *        with the ".extra" file submitted with the report.
   *        The properties of htis object will override properties
   *        of the same name in the .extra file.
   */
  submitCrashReport(runID, keyVals) {
    if (!this.crashReports.has(runID)) {
      Cu.reportError(`Could not find plugin dump IDs for run ID ${runID}.` +
                     `It is possible that a report was already submitted.`);
      return;
    }

    keyVals = keyVals || {};
    let { pluginDumpID, browserDumpID } = this.crashReports.get(runID);

    let submissionPromise = CrashSubmit.submit(pluginDumpID, {
      recordSubmission: true,
      extraExtraKeyVals: keyVals,
    });

    if (browserDumpID)
      CrashSubmit.submit(browserDumpID).catch(Cu.reportError);

    this.broadcastState(runID, "submitting");

    submissionPromise.then(() => {
      this.broadcastState(runID, "success");
    }, () => {
      this.broadcastState(runID, "failed");
    });

    this.crashReports.delete(runID);
  },

  broadcastState(runID, state) {
    let enumerator = Services.wm.getEnumerator("navigator:browser");
    while (enumerator.hasMoreElements()) {
      let window = enumerator.getNext();
      let mm = window.messageManager;
      mm.broadcastAsyncMessage("BrowserPlugins:CrashReportSubmitted",
                               { runID, state });
    }
  },

  hasCrashReport(runID) {
    return this.crashReports.has(runID);
  },
};
PK
!<��Z��-�-modules/ContentLinkHandler.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = [ "ContentLinkHandler" ];

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");

ChromeUtils.defineModuleGetter(this, "Feeds",
  "resource:///modules/Feeds.jsm");
ChromeUtils.defineModuleGetter(this, "BrowserUtils",
  "resource://gre/modules/BrowserUtils.jsm");

const SIZES_TELEMETRY_ENUM = {
  NO_SIZES: 0,
  ANY: 1,
  DIMENSION: 2,
  INVALID: 3,
};

const FAVICON_PARSING_TIMEOUT = 100;
const FAVICON_RICH_ICON_MIN_WIDTH = 96;

const TYPE_ICO = "image/x-icon";
const TYPE_SVG = "image/svg+xml";

/*
 * Create a nsITimer.
 *
 * @param {function} aCallback A timeout callback function.
 * @param {Number} aDelay A timeout interval in millisecond.
 * @return {nsITimer} A nsITimer object.
 */
function setTimeout(aCallback, aDelay) {
  let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
  timer.initWithCallback(aCallback, aDelay, Ci.nsITimer.TYPE_ONE_SHOT);
  return timer;
}

/*
 * Extract the icon width from the size attribute. It also sends the telemetry
 * about the size type and size dimension info.
 *
 * @param {Array} aSizes An array of strings about size.
 * @return {Number} A width of the icon in pixel.
 */
function extractIconSize(aSizes) {
  let width = -1;
  let sizesType;
  const re = /^([1-9][0-9]*)x[1-9][0-9]*$/i;

  if (aSizes.length) {
    for (let size of aSizes) {
      if (size.toLowerCase() == "any") {
        sizesType = SIZES_TELEMETRY_ENUM.ANY;
        break;
      } else {
        let values = re.exec(size);
        if (values && values.length > 1) {
          sizesType = SIZES_TELEMETRY_ENUM.DIMENSION;
          width = parseInt(values[1]);
          break;
        } else {
          sizesType = SIZES_TELEMETRY_ENUM.INVALID;
          break;
        }
      }
    }
  } else {
    sizesType = SIZES_TELEMETRY_ENUM.NO_SIZES;
  }

  // Telemetry probes for measuring the sizes attribute
  // usage and available dimensions.
  Services.telemetry.getHistogramById("LINK_ICON_SIZES_ATTR_USAGE").add(sizesType);
  if (width > 0)
    Services.telemetry.getHistogramById("LINK_ICON_SIZES_ATTR_DIMENSION").add(width);

  return width;
}

/*
 * Get link icon URI from a link dom node.
 *
 * @param {DOMNode} aLink A link dom node.
 * @return {nsIURI} A uri of the icon.
 */
function getLinkIconURI(aLink) {
  let targetDoc = aLink.ownerDocument;
  let uri = Services.io.newURI(aLink.href, targetDoc.characterSet);
  try {
    uri = uri.mutate().setUserPass("").finalize();
  } catch (e) {
    // some URIs are immutable
  }
  return uri;
}

/*
 * Set the icon via sending the "Link:Seticon" message.
 *
 * @param {Object} aIconInfo The IconInfo object looks like {
 *   iconUri: icon URI,
 *   loadingPrincipal: icon loading principal
 * }.
 * @param {Object} aChromeGlobal A global chrome object.
 */
function setIconForLink(aIconInfo, aChromeGlobal) {
  aChromeGlobal.sendAsyncMessage(
    "Link:SetIcon",
    { url: aIconInfo.iconUri.spec,
      loadingPrincipal: aIconInfo.loadingPrincipal,
      requestContextID: aIconInfo.requestContextID,
      canUseForTab: !aIconInfo.isRichIcon,
    });
}

/**
 * Guess a type for an icon based on its declared type or file extension.
 */
function guessType(icon) {
  // No type with no icon
  if (!icon) {
    return "";
  }

  // Use the file extension to guess at a type we're interested in
  if (!icon.type) {
    let extension = icon.iconUri.filePath.split(".").pop();
    switch (extension) {
      case "ico":
        return TYPE_ICO;
      case "svg":
        return TYPE_SVG;
    }
  }

  // Fuzzily prefer the type or fall back to the declared type
  return icon.type == "image/vnd.microsoft.icon" ? TYPE_ICO : icon.type || "";
}

/*
 * Timeout callback function for loading favicon.
 *
 * @param {Map} aFaviconLoads A map of page URL and FaviconLoad object pairs,
 *   where the FaviconLoad object looks like {
 *     timer: a nsITimer object,
 *     iconInfos: an array of IconInfo objects
 *   }
 * @param {String} aPageUrl A page URL string for this callback.
 * @param {Object} aChromeGlobal A global chrome object.
 */
function faviconTimeoutCallback(aFaviconLoads, aPageUrl, aChromeGlobal) {
  let load = aFaviconLoads.get(aPageUrl);
  if (!load)
    return;

  let preferredIcon;
  let preferredWidth = 16 * Math.ceil(aChromeGlobal.content.devicePixelRatio);
  let bestSizedIcon;
  // Other links with the "icon" tag are the default icons
  let defaultIcon;
  // Rich icons are either apple-touch or fluid icons, or the ones of the
  // dimension 96x96 or greater
  let largestRichIcon;

  for (let icon of load.iconInfos) {
    if (!icon.isRichIcon) {
      // First check for svg. If it's not available check for an icon with a
      // size adapt to the current resolution. If both are not available, prefer
      // ico files. When multiple icons are in the same set, the latest wins.
      if (guessType(icon) == TYPE_SVG) {
        preferredIcon = icon;
      } else if (icon.width == preferredWidth && guessType(preferredIcon) != TYPE_SVG) {
        preferredIcon = icon;
      } else if (guessType(icon) == TYPE_ICO && (!preferredIcon || guessType(preferredIcon) == TYPE_ICO)) {
        preferredIcon = icon;
      }

      // Check for an icon larger yet closest to preferredWidth, that can be
      // downscaled efficiently.
      if (icon.width >= preferredWidth &&
          (!bestSizedIcon || bestSizedIcon.width >= icon.width)) {
        bestSizedIcon = icon;
      }
    }

    // Note that some sites use hi-res icons without specifying them as
    // apple-touch or fluid icons.
    if (icon.isRichIcon || icon.width >= FAVICON_RICH_ICON_MIN_WIDTH) {
      if (!largestRichIcon || largestRichIcon.width < icon.width) {
        largestRichIcon = icon;
      }
    } else {
      defaultIcon = icon;
    }
  }

  // Now set the favicons for the page in the following order:
  // 1. Set the best rich icon if any.
  // 2. Set the preferred one if any, otherwise check if there's a better
  //    sized fit.
  // This order allows smaller icon frames to eventually override rich icon
  // frames.
  if (largestRichIcon) {
    setIconForLink(largestRichIcon, aChromeGlobal);
  }
  if (preferredIcon) {
    setIconForLink(preferredIcon, aChromeGlobal);
  } else if (bestSizedIcon) {
    setIconForLink(bestSizedIcon, aChromeGlobal);
  } else if (defaultIcon) {
    setIconForLink(defaultIcon, aChromeGlobal);
  }

  load.timer = null;
  aFaviconLoads.delete(aPageUrl);
}

/*
 * Get request context ID of the link dom node's document.
 *
 * @param {DOMNode} aLink A link dom node.
 * @return {Number} The request context ID.
 *                  Return null when document's load group is not available.
 */
function getLinkRequestContextID(aLink) {
  try {
    return aLink.ownerDocument.documentLoadGroup.requestContextID;
  } catch (e) {
    return null;
  }
}

/*
 * Favicon link handler.
 *
 * @param {DOMNode} aLink A link dom node.
 * @param {bool} aIsRichIcon A bool to indicate if the link is rich icon.
 * @param {Object} aChromeGlobal A global chrome object.
 * @param {Map} aFaviconLoads A map of page URL and FaviconLoad object pairs.
 * @return {bool} Returns true if the link is successfully handled.
 */
function handleFaviconLink(aLink, aIsRichIcon, aChromeGlobal, aFaviconLoads) {
  let pageUrl = aLink.ownerDocument.documentURI;
  let iconUri = getLinkIconURI(aLink);
  if (!iconUri)
    return false;

  // Extract the size type and width.
  let width = extractIconSize(aLink.sizes);
  let iconInfo = {
    iconUri,
    width,
    isRichIcon: aIsRichIcon,
    type: aLink.type,
    loadingPrincipal: aLink.ownerDocument.nodePrincipal,
    requestContextID: getLinkRequestContextID(aLink)
  };

  if (aFaviconLoads.has(pageUrl)) {
    let load = aFaviconLoads.get(pageUrl);
    load.iconInfos.push(iconInfo);
    // Re-initialize the timer
    load.timer.delay = FAVICON_PARSING_TIMEOUT;
  } else {
    let timer = setTimeout(() => faviconTimeoutCallback(aFaviconLoads, pageUrl, aChromeGlobal),
                                                        FAVICON_PARSING_TIMEOUT);
    let load = { timer, iconInfos: [iconInfo] };
    aFaviconLoads.set(pageUrl, load);
  }
  return true;
}

var ContentLinkHandler = {
  init(chromeGlobal) {
    const faviconLoads = new Map();
    chromeGlobal.addEventListener("DOMLinkAdded", event => {
      this.onLinkEvent(event, chromeGlobal, faviconLoads);
    });
    chromeGlobal.addEventListener("DOMLinkChanged", event => {
      this.onLinkEvent(event, chromeGlobal, faviconLoads);
    });
    chromeGlobal.addEventListener("unload", event => {
      for (const [pageUrl, load] of faviconLoads) {
        load.timer.cancel();
        load.timer = null;
        faviconLoads.delete(pageUrl);
      }
    });
  },

  onLinkEvent(event, chromeGlobal, faviconLoads) {
    var link = event.originalTarget;
    var rel = link.rel && link.rel.toLowerCase();
    if (!link || !link.ownerDocument || !rel || !link.href)
      return;

    // Ignore sub-frames (bugs 305472, 479408).
    let window = link.ownerGlobal;
    if (window != window.top)
      return;

    // Note: following booleans only work for the current link, not for the
    // whole content
    var feedAdded = false;
    var iconAdded = false;
    var searchAdded = false;
    var rels = {};
    for (let relString of rel.split(/\s+/))
      rels[relString] = true;

    for (let relVal in rels) {
      let isRichIcon = true;

      switch (relVal) {
        case "feed":
        case "alternate":
          if (!feedAdded && event.type == "DOMLinkAdded") {
            if (!rels.feed && rels.alternate && rels.stylesheet)
              break;

            if (Feeds.isValidFeed(link, link.ownerDocument.nodePrincipal, "feed" in rels)) {
              chromeGlobal.sendAsyncMessage("Link:AddFeed",
                                            {type: link.type,
                                             href: link.href,
                                             title: link.title});
              feedAdded = true;
            }
          }
          break;
        case "icon":
          isRichIcon = false;
          // Fall through to rich icon handling
        case "apple-touch-icon":
        case "apple-touch-icon-precomposed":
        case "fluid-icon":
          if (link.hasAttribute("mask") || // Masked icons are not supported yet.
              iconAdded ||
              !Services.prefs.getBoolPref("browser.chrome.site_icons")) {
            break;
          }

          iconAdded = handleFaviconLink(link, isRichIcon, chromeGlobal, faviconLoads);
          break;
        case "search":
          if (Services.policies &&
              !Services.policies.isAllowed("installSearchEngine")) {
            break;
          }
          if (!searchAdded && event.type == "DOMLinkAdded") {
            var type = link.type && link.type.toLowerCase();
            type = type.replace(/^\s+|\s*(?:;.*)?$/g, "");

            let re = /^(?:https?|ftp):/i;
            if (type == "application/opensearchdescription+xml" && link.title &&
                re.test(link.href)) {
              let engine = { title: link.title, href: link.href };
              chromeGlobal.sendAsyncMessage("Link:AddSearch",
                                            {engine,
                                             url: link.ownerDocument.documentURI});
              searchAdded = true;
            }
          }
          break;
      }
    }
  },
};
PK
!<&����modules/ContentMetaHandler.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";
Cu.importGlobalProperties(["URL"]);
ChromeUtils.import("resource://gre/modules/Services.jsm");

// Debounce time in milliseconds - this should be long enough to account for
// sync script tags that could appear between desired meta tags
const TIMEOUT_DELAY = 1000;

const ACCEPTED_PROTOCOLS = ["http:", "https:"];

// Possible description tags, listed in order from least favourable to most favourable
const DESCRIPTION_RULES = [
  "twitter:description",
  "description",
  "og:description"
];

// Possible image tags, listed in order from least favourable to most favourable
const PREVIEW_IMAGE_RULES = [
  "thumbnail",
  "twitter:image",
  "og:image",
  "og:image:url",
  "og:image:secure_url"
];

/*
 * Checks if the incoming meta tag has a greater score than the current best
 * score by checking the index of the meta tag in the list of rules provided.
 *
 * @param {Array} aRules
 *          The list of rules for a given type of meta tag
 * @param {String} aTag
 *          The name or property of the incoming meta tag
 * @param {String} aEntry
 *          The current best entry for the given meta tag
 *
 * @returns {Boolean} true if the incoming meta tag is better than the current
 *                    best meta tag of that same kind, false otherwise
 */
function shouldExtractMetadata(aRules, aTag, aEntry) {
  return aRules.indexOf(aTag) > aEntry.currMaxScore;
}

/*
 * Ensure that the preview image URL is safe and valid before storing
 *
 * @param {URL} aURL
 *          A URL object that needs to be checked for valid principal and protocol
 *
 * @returns {Boolean} true if the preview URL is safe and can be stored, false otherwise
 */
function checkLoadURIStr(aURL) {
  if (!ACCEPTED_PROTOCOLS.includes(aURL.protocol)) {
    return false;
  }
  try {
    let ssm = Services.scriptSecurityManager;
    let principal = ssm.createNullPrincipal({});
    ssm.checkLoadURIStrWithPrincipal(principal, aURL.href, ssm.DISALLOW_INHERIT_PRINCIPAL);
  } catch (e) {
    return false;
  }
  return true;
}

var EXPORTED_SYMBOLS = [ "ContentMetaHandler" ];

/*
 * This listens to DOMMetaAdded events and collects relevant metadata about the
 * meta tag received. Then, it sends the metadata gathered from the meta tags
 * and the url of the page as it's payload to be inserted into moz_places.
 */

var ContentMetaHandler = {
  init(chromeGlobal) {
    // Store a locally-scoped (for this chromeGlobal) mapping of the best
    // description and preview image collected so far for a given URL
    const metaTags = new Map();
    chromeGlobal.addEventListener("DOMMetaAdded", event => {
      const metaTag = event.originalTarget;
      const window = metaTag.ownerGlobal;

      // If there's no meta tag, or we're in a sub-frame, ignore this
      if (!metaTag || !metaTag.ownerDocument || window != window.top) {
        return;
      }
      this.handleMetaTag(metaTag, chromeGlobal, metaTags);
    });
  },


  handleMetaTag(metaTag, chromeGlobal, metaTags) {
    const url = metaTag.ownerDocument.documentURI;

    let name = metaTag.name;
    let prop = metaTag.getAttributeNS(null, "property");
    if (!name && !prop) {
      return;
    }

    let tag = name || prop;

    const entry = metaTags.get(url) || {
      description: {value: null, currMaxScore: -1},
      image: {value: null, currMaxScore: -1},
      timeout: null
    };

    // Malformed meta tag - do not store it
    const content = metaTag.getAttributeNS(null, "content");
    if (!content) {
      return;
    }

    if (shouldExtractMetadata(DESCRIPTION_RULES, tag, entry.description)) {
      // Extract the description
      entry.description.value = content;
      entry.description.currMaxScore = DESCRIPTION_RULES.indexOf(tag);
    } else if (shouldExtractMetadata(PREVIEW_IMAGE_RULES, tag, entry.image)) {
      // Extract the preview image
      let value;
      try {
        value = new URL(content, url);
      } catch (e) {
        return;
      }
      if (value && checkLoadURIStr(value)) {
        entry.image.value = value.href;
        entry.image.currMaxScore = PREVIEW_IMAGE_RULES.indexOf(tag);
      }
    } else {
      // We don't care about other meta tags
      return;
    }

    if (!metaTags.has(url)) {
      metaTags.set(url, entry);
    }

    if (entry.timeout) {
      entry.timeout.delay = TIMEOUT_DELAY;
    } else {
      // We want to debounce incoming meta tags until we're certain we have the
      // best one for description and preview image, and only store that one
      entry.timeout = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
      entry.timeout.initWithCallback(() => {
        entry.timeout = null;

        // Save description and preview image to moz_places
        chromeGlobal.sendAsyncMessage("Meta:SetPageInfo", {
          url,
          description: entry.description.value,
          previewImageURL: entry.image.value
        });

        // Telemetry for recording the size of page metadata
        let metadataSize = entry.description.value ? entry.description.value.length : 0;
        metadataSize += entry.image.value ? entry.image.value.length : 0;
        Services.telemetry.getHistogramById("PAGE_METADATA_SIZE").add(metadataSize);
        metaTags.delete(url);
      }, TIMEOUT_DELAY, Ci.nsITimer.TYPE_ONE_SHOT);
    }
  }
};
PK
!<ElЊ�@�@modules/ContentWebRTC.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = [ "ContentWebRTC" ];

ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "MediaManagerService",
                                   "@mozilla.org/mediaManagerService;1",
                                   "nsIMediaManagerService");

const kBrowserURL = "chrome://browser/content/browser.xul";

var ContentWebRTC = {
  // Called only for 'unload' to remove pending gUM prompts in reloaded frames.
  handleEvent(aEvent) {
    let contentWindow = aEvent.target.defaultView;
    let mm = getMessageManagerForWindow(contentWindow);
    for (let key of contentWindow.pendingGetUserMediaRequests.keys()) {
      mm.sendAsyncMessage("webrtc:CancelRequest", key);
    }
    for (let key of contentWindow.pendingPeerConnectionRequests.keys()) {
      mm.sendAsyncMessage("rtcpeer:CancelRequest", key);
    }
  },

  // This observer is registered in ContentObservers.js to avoid
  // loading this .jsm when WebRTC is not in use.
  observe(aSubject, aTopic, aData) {
    switch (aTopic) {
      case "getUserMedia:request":
        handleGUMRequest(aSubject, aTopic, aData);
        break;
      case "recording-device-stopped":
        handleGUMStop(aSubject, aTopic, aData);
        break;
      case "PeerConnection:request":
        handlePCRequest(aSubject, aTopic, aData);
        break;
      case "recording-device-events":
        updateIndicators(aSubject, aTopic, aData);
        break;
      case "recording-window-ended":
        removeBrowserSpecificIndicator(aSubject, aTopic, aData);
        break;
    }
  },

  receiveMessage(aMessage) {
    switch (aMessage.name) {
      case "rtcpeer:Allow":
      case "rtcpeer:Deny": {
        let callID = aMessage.data.callID;
        let contentWindow = Services.wm.getOuterWindowWithId(aMessage.data.windowID);
        forgetPCRequest(contentWindow, callID);
        let topic = (aMessage.name == "rtcpeer:Allow") ? "PeerConnection:response:allow" :
                                                         "PeerConnection:response:deny";
        Services.obs.notifyObservers(null, topic, callID);
        break;
      }
      case "webrtc:Allow": {
        let callID = aMessage.data.callID;
        let contentWindow = Services.wm.getOuterWindowWithId(aMessage.data.windowID);
        let devices = contentWindow.pendingGetUserMediaRequests.get(callID);
        forgetGUMRequest(contentWindow, callID);

        let allowedDevices = Cc["@mozilla.org/array;1"]
                               .createInstance(Ci.nsIMutableArray);
        for (let deviceIndex of aMessage.data.devices)
           allowedDevices.appendElement(devices[deviceIndex]);

        Services.obs.notifyObservers(allowedDevices, "getUserMedia:response:allow", callID);
        break;
      }
      case "webrtc:Deny":
        denyGUMRequest(aMessage.data);
        break;
      case "webrtc:StopSharing":
        Services.obs.notifyObservers(null, "getUserMedia:revoke", aMessage.data);
        break;
    }
  }
};

function handlePCRequest(aSubject, aTopic, aData) {
  let { windowID, innerWindowID, callID, isSecure } = aSubject;
  let contentWindow = Services.wm.getOuterWindowWithId(windowID);

  let mm = getMessageManagerForWindow(contentWindow);
  if (!mm) {
    // Workaround for Bug 1207784. To use WebRTC, add-ons right now use
    // hiddenWindow.mozRTCPeerConnection which is only privileged on OSX. Other
    // platforms end up here without a message manager.
    // TODO: Remove once there's a better way (1215591).

    // Skip permission check in the absence of a message manager.
    Services.obs.notifyObservers(null, "PeerConnection:response:allow", callID);
    return;
  }

  if (!contentWindow.pendingPeerConnectionRequests) {
    setupPendingListsInitially(contentWindow);
  }
  contentWindow.pendingPeerConnectionRequests.add(callID);

  let request = {
    windowID,
    innerWindowID,
    callID,
    documentURI: contentWindow.document.documentURI,
    secure: isSecure,
  };
  mm.sendAsyncMessage("rtcpeer:Request", request);
}

function handleGUMStop(aSubject, aTopic, aData) {
  let contentWindow = Services.wm.getOuterWindowWithId(aSubject.windowID);

  let request = {
    windowID: aSubject.windowID,
    rawID: aSubject.rawID,
    mediaSource: aSubject.mediaSource,
  };

  let mm = getMessageManagerForWindow(contentWindow);
  if (mm)
    mm.sendAsyncMessage("webrtc:StopRecording", request);
}

function handleGUMRequest(aSubject, aTopic, aData) {
  let constraints = aSubject.getConstraints();
  let secure = aSubject.isSecure;
  let isHandlingUserInput = aSubject.isHandlingUserInput;
  let contentWindow = Services.wm.getOuterWindowWithId(aSubject.windowID);

  contentWindow.navigator.mozGetUserMediaDevices(
    constraints,
    function(devices) {
      // If the window has been closed while we were waiting for the list of
      // devices, there's nothing to do in the callback anymore.
      if (contentWindow.closed)
        return;

      prompt(contentWindow, aSubject.windowID, aSubject.callID,
             constraints, devices, secure, isHandlingUserInput);
    },
    function(error) {
      // bug 827146 -- In the future, the UI should catch NotFoundError
      // and allow the user to plug in a device, instead of immediately failing.
      denyGUMRequest({callID: aSubject.callID}, error);
    },
    aSubject.innerWindowID,
    aSubject.callID);
}

function prompt(aContentWindow, aWindowID, aCallID, aConstraints, aDevices, aSecure, aIsHandlingUserInput) {
  let audioDevices = [];
  let videoDevices = [];
  let devices = [];

  // MediaStreamConstraints defines video as 'boolean or MediaTrackConstraints'.
  let video = aConstraints.video || aConstraints.picture;
  let audio = aConstraints.audio;
  let sharingScreen = video && typeof(video) != "boolean" &&
                      video.mediaSource != "camera";
  let sharingAudio = audio && typeof(audio) != "boolean" &&
                     audio.mediaSource != "microphone";
  for (let device of aDevices) {
    device = device.QueryInterface(Ci.nsIMediaDevice);
    switch (device.type) {
      case "audio":
        // Check that if we got a microphone, we have not requested an audio
        // capture, and if we have requested an audio capture, we are not
        // getting a microphone instead.
        if (audio && (device.mediaSource == "microphone") != sharingAudio) {
          audioDevices.push({name: device.name, deviceIndex: devices.length,
                             id: device.rawId, mediaSource: device.mediaSource});
          devices.push(device);
        }
        break;
      case "video":
        // Verify that if we got a camera, we haven't requested a screen share,
        // or that if we requested a screen share we aren't getting a camera.
        if (video && (device.mediaSource == "camera") != sharingScreen) {
          let deviceObject = {name: device.name, deviceIndex: devices.length,
                              id: device.rawId, mediaSource: device.mediaSource};
          if (device.scary)
            deviceObject.scary = true;
          videoDevices.push(deviceObject);
          devices.push(device);
        }
        break;
    }
  }

  let requestTypes = [];
  if (videoDevices.length)
    requestTypes.push(sharingScreen ? "Screen" : "Camera");
  if (audioDevices.length)
    requestTypes.push(sharingAudio ? "AudioCapture" : "Microphone");

  if (!requestTypes.length) {
    denyGUMRequest({callID: aCallID}, "NotFoundError");
    return;
  }

  if (!aContentWindow.pendingGetUserMediaRequests) {
    setupPendingListsInitially(aContentWindow);
  }
  aContentWindow.pendingGetUserMediaRequests.set(aCallID, devices);

  // Record third party origins for telemetry.
  let isThirdPartyOrigin =
    aContentWindow.document.location.origin != aContentWindow.top.document.location.origin;

  let request = {
    callID: aCallID,
    windowID: aWindowID,
    origin: aContentWindow.origin,
    documentURI: aContentWindow.document.documentURI,
    secure: aSecure,
    isHandlingUserInput: aIsHandlingUserInput,
    isThirdPartyOrigin,
    requestTypes,
    sharingScreen,
    sharingAudio,
    audioDevices,
    videoDevices
  };

  let mm = getMessageManagerForWindow(aContentWindow);
  mm.sendAsyncMessage("webrtc:Request", request);
}

function denyGUMRequest(aData, aError) {
  let msg = null;
  if (aError) {
    msg = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
    msg.data = aError;
  }
  Services.obs.notifyObservers(msg, "getUserMedia:response:deny", aData.callID);

  if (!aData.windowID)
    return;
  let contentWindow = Services.wm.getOuterWindowWithId(aData.windowID);
  if (contentWindow.pendingGetUserMediaRequests)
    forgetGUMRequest(contentWindow, aData.callID);
}

function forgetGUMRequest(aContentWindow, aCallID) {
  aContentWindow.pendingGetUserMediaRequests.delete(aCallID);
  forgetPendingListsEventually(aContentWindow);
}

function forgetPCRequest(aContentWindow, aCallID) {
  aContentWindow.pendingPeerConnectionRequests.delete(aCallID);
  forgetPendingListsEventually(aContentWindow);
}

function setupPendingListsInitially(aContentWindow) {
  if (aContentWindow.pendingGetUserMediaRequests) {
    return;
  }
  aContentWindow.pendingGetUserMediaRequests = new Map();
  aContentWindow.pendingPeerConnectionRequests = new Set();
  aContentWindow.addEventListener("unload", ContentWebRTC);
}

function forgetPendingListsEventually(aContentWindow) {
  if (aContentWindow.pendingGetUserMediaRequests.size ||
      aContentWindow.pendingPeerConnectionRequests.size) {
    return;
  }
  aContentWindow.pendingGetUserMediaRequests = null;
  aContentWindow.pendingPeerConnectionRequests = null;
  aContentWindow.removeEventListener("unload", ContentWebRTC);
}

function updateIndicators(aSubject, aTopic, aData) {
  if (aSubject instanceof Ci.nsIPropertyBag &&
      aSubject.getProperty("requestURL") == kBrowserURL) {
    // Ignore notifications caused by the browser UI showing previews.
    return;
  }

  let contentWindowArray = MediaManagerService.activeMediaCaptureWindows;
  let count = contentWindowArray.length;

  let state = {
    showGlobalIndicator: count > 0,
    showCameraIndicator: false,
    showMicrophoneIndicator: false,
    showScreenSharingIndicator: ""
  };

  Services.cpmm.sendAsyncMessage("webrtc:UpdatingIndicators");

  // If several iframes in the same page use media streams, it's possible to
  // have the same top level window several times. We use a Set to avoid
  // sending duplicate notifications.
  let contentWindows = new Set();
  for (let i = 0; i < count; ++i) {
    contentWindows.add(contentWindowArray.queryElementAt(i, Ci.nsISupports).top);
  }

  for (let contentWindow of contentWindows) {
    if (contentWindow.document.documentURI == kBrowserURL) {
      // There may be a preview shown at the same time as other streams.
      continue;
    }

    let tabState = getTabStateForContentWindow(contentWindow);
    if (tabState.camera == MediaManagerService.STATE_CAPTURE_ENABLED ||
        tabState.camera == MediaManagerService.STATE_CAPTURE_DISABLED) {
      state.showCameraIndicator = true;
    }
    if (tabState.microphone == MediaManagerService.STATE_CAPTURE_ENABLED ||
        tabState.microphone == MediaManagerService.STATE_CAPTURE_DISABLED) {
      state.showMicrophoneIndicator = true;
    }
    if (tabState.screen) {
      if (tabState.screen.startsWith("Screen")) {
        state.showScreenSharingIndicator = "Screen";
      } else if (tabState.screen.startsWith("Window")) {
        if (state.showScreenSharingIndicator != "Screen")
          state.showScreenSharingIndicator = "Window";
      } else if (tabState.screen.startsWith("Application")) {
        if (!state.showScreenSharingIndicator)
          state.showScreenSharingIndicator = "Application";
      } else if (tabState.screen.startsWith("Browser")) {
        if (!state.showScreenSharingIndicator)
          state.showScreenSharingIndicator = "Browser";
      }
    }

    let mm = getMessageManagerForWindow(contentWindow);
    mm.sendAsyncMessage("webrtc:UpdateBrowserIndicators", tabState);
  }

  Services.cpmm.sendAsyncMessage("webrtc:UpdateGlobalIndicators", state);
}

function removeBrowserSpecificIndicator(aSubject, aTopic, aData) {
  let contentWindow = Services.wm.getOuterWindowWithId(aData).top;
  if (contentWindow.document.documentURI == kBrowserURL) {
    // Ignore notifications caused by the browser UI showing previews.
    return;
  }

  let tabState = getTabStateForContentWindow(contentWindow);
  if (tabState.camera == MediaManagerService.STATE_NOCAPTURE &&
      tabState.microphone == MediaManagerService.STATE_NOCAPTURE &&
      !tabState.screen)
    tabState = {windowId: tabState.windowId};

  let mm = getMessageManagerForWindow(contentWindow);
  if (mm)
    mm.sendAsyncMessage("webrtc:UpdateBrowserIndicators", tabState);
}

function getTabStateForContentWindow(aContentWindow) {
  let camera = {}, microphone = {}, screen = {}, window = {}, app = {}, browser = {};
  MediaManagerService.mediaCaptureWindowState(aContentWindow,
                                              camera, microphone,
                                              screen, window, app, browser);
  let tabState = {camera: camera.value, microphone: microphone.value};
  if (screen.value == MediaManagerService.STATE_CAPTURE_ENABLED)
    tabState.screen = "Screen";
  else if (window.value == MediaManagerService.STATE_CAPTURE_ENABLED)
    tabState.screen = "Window";
  else if (app.value == MediaManagerService.STATE_CAPTURE_ENABLED)
    tabState.screen = "Application";
  else if (browser.value == MediaManagerService.STATE_CAPTURE_ENABLED)
    tabState.screen = "Browser";
  else if (screen.value == MediaManagerService.STATE_CAPTURE_DISABLED)
    tabState.screen = "ScreenPaused";
  else if (window.value == MediaManagerService.STATE_CAPTURE_DISABLED)
    tabState.screen = "WindowPaused";
  else if (app.value == MediaManagerService.STATE_CAPTURE_DISABLED)
    tabState.screen = "ApplicationPaused";
  else if (browser.value == MediaManagerService.STATE_CAPTURE_DISABLED)
    tabState.screen = "BrowserPaused";

  let screenEnabled = tabState.screen && !tabState.screen.includes("Paused");
  let cameraEnabled = tabState.camera == MediaManagerService.STATE_CAPTURE_ENABLED;
  let microphoneEnabled = tabState.microphone == MediaManagerService.STATE_CAPTURE_ENABLED;

  // tabState.sharing controls which global indicator should be shown
  // for the tab. It should always be set to the _enabled_ device which
  // we consider most intrusive (screen > camera > microphone).
  if (screenEnabled) {
    tabState.sharing = "screen";
  } else if (cameraEnabled) {
    tabState.sharing = "camera";
  } else if (microphoneEnabled) {
    tabState.sharing = "microphone";
  } else if (tabState.screen) {
    tabState.sharing = "screen";
  } else if (tabState.camera) {
    tabState.sharing = "camera";
  } else if (tabState.microphone) {
    tabState.sharing = "microphone";
  }

  // The stream is considered paused when we're sharing something
  // but all devices are off or set to disabled.
  tabState.paused = tabState.sharing &&
    !screenEnabled && !cameraEnabled && !microphoneEnabled;

  tabState.windowId = getInnerWindowIDForWindow(aContentWindow);
  tabState.documentURI = aContentWindow.document.documentURI;

  return tabState;
}

function getInnerWindowIDForWindow(aContentWindow) {
  return aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                       .getInterface(Ci.nsIDOMWindowUtils)
                       .currentInnerWindowID;
}

function getMessageManagerForWindow(aContentWindow) {
  aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor);

  let docShell;
  try {
    // This throws NS_NOINTERFACE for closed tabs.
    docShell = aContentWindow.getInterface(Ci.nsIDocShell);
  } catch (e) {
    if (e.result == Cr.NS_NOINTERFACE) {
      return null;
    }
    throw e;
  }

  let ir = docShell.sameTypeRootTreeItem
                   .QueryInterface(Ci.nsIInterfaceRequestor);
  try {
    // This throws NS_NOINTERFACE for closed tabs (only with e10s enabled).
    return ir.getInterface(Ci.nsIContentFrameMessageManager);
  } catch (e) {
    if (e.result == Cr.NS_NOINTERFACE) {
      return null;
    }
    throw e;
  }
}
PK
!<�22i$�$�modules/ContextMenu.jsm/* -*- mode: js; indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=2 sw=2 sts=2 et tw=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";

var EXPORTED_SYMBOLS = ["ContextMenu"];

Cu.importGlobalProperties(["URL"]);

ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");

XPCOMUtils.defineLazyModuleGetters(this, {
  E10SUtils: "resource://gre/modules/E10SUtils.jsm",
  BrowserUtils: "resource://gre/modules/BrowserUtils.jsm",
  PlacesUIUtils: "resource:///modules/PlacesUIUtils.jsm",
  findCssSelector: "resource://gre/modules/css-selector.js",
  SpellCheckHelper: "resource://gre/modules/InlineSpellChecker.jsm",
  LoginManagerContent: "resource://gre/modules/LoginManagerContent.jsm",
  WebNavigationFrames: "resource://gre/modules/WebNavigationFrames.jsm",
  PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
  InlineSpellCheckerContent: "resource://gre/modules/InlineSpellCheckerContent.jsm",
});

XPCOMUtils.defineLazyGetter(this, "PageMenuChild", () => {
  let tmp = {};
  ChromeUtils.import("resource://gre/modules/PageMenu.jsm", tmp);
  return new tmp.PageMenuChild();
});

const messageListeners = {
  "ContextMenu:BookmarkFrame": function(aMessage) {
    let frame = this.getTarget(aMessage).ownerDocument;

    this.global.sendAsyncMessage("ContextMenu:BookmarkFrame:Result",
                                 { title: frame.title,
                                 description: PlacesUIUtils.getDescriptionFromDocument(frame) });
  },

  "ContextMenu:Canvas:ToBlobURL": function(aMessage) {
    this.getTarget(aMessage).toBlob((blob) => {
      let blobURL = URL.createObjectURL(blob);
      this.global.sendAsyncMessage("ContextMenu:Canvas:ToBlobURL:Result", { blobURL });
    });
  },

  "ContextMenu:DoCustomCommand": function(aMessage) {
    E10SUtils.wrapHandlingUserInput(
      this.content,
      aMessage.data.handlingUserInput,
      () => PageMenuChild.executeMenu(aMessage.data.generatedItemId)
    );
  },

  "ContextMenu:Hiding": function() {
    this.context = null;
    this.target = null;
  },

  "ContextMenu:MediaCommand": function(aMessage) {
    E10SUtils.wrapHandlingUserInput(
      this.content, aMessage.data.handlingUserInput, () => {
        let media = this.getTarget(aMessage, "element");

        switch (aMessage.data.command) {
          case "play":
            media.play();
            break;
          case "pause":
            media.pause();
            break;
          case "loop":
            media.loop = !media.loop;
            break;
          case "mute":
            media.muted = true;
            break;
          case "unmute":
            media.muted = false;
            break;
          case "playbackRate":
            media.playbackRate = aMessage.data.data;
            break;
          case "hidecontrols":
            media.removeAttribute("controls");
            break;
          case "showcontrols":
            media.setAttribute("controls", "true");
            break;
          case "fullscreen":
            if (this.content.document.fullscreenEnabled) {
              media.requestFullscreen();
            }

            break;
        }
      }
    );
  },

  "ContextMenu:ReloadFrame": function(aMessage) {
    let forceReload = aMessage.objects && aMessage.objects.forceReload;
    this.getTarget(aMessage).ownerDocument.location.reload(forceReload);
  },

  "ContextMenu:ReloadImage": function(aMessage) {
    let image = this.getTarget(aMessage);

    if (image instanceof Ci.nsIImageLoadingContent) {
      image.forceReload();
    }
  },

  "ContextMenu:SearchFieldBookmarkData": function(aMessage) {
    let node = this.getTarget(aMessage);
    let charset = node.ownerDocument.characterSet;
    let formBaseURI = Services.io.newURI(node.form.baseURI, charset);
    let formURI = Services.io.newURI(node.form.getAttribute("action"),
                                     charset, formBaseURI);
    let spec = formURI.spec;
    let isURLEncoded =  (node.form.method.toUpperCase() == "POST" &&
                         (node.form.enctype == "application/x-www-form-urlencoded" ||
                          node.form.enctype == ""));
    let title = node.ownerDocument.title;
    let description = PlacesUIUtils.getDescriptionFromDocument(node.ownerDocument);
    let formData = [];

    function escapeNameValuePair(aName, aValue, aIsFormUrlEncoded) {
      if (aIsFormUrlEncoded) {
        return escape(aName + "=" + aValue);
      }

      return escape(aName) + "=" + escape(aValue);
    }

    for (let el of node.form.elements) {
      if (!el.type) // happens with fieldsets
        continue;

      if (el == node) {
        formData.push((isURLEncoded) ? escapeNameValuePair(el.name, "%s", true) :
                                       // Don't escape "%s", just append
                                       escapeNameValuePair(el.name, "", false) + "%s");
        continue;
      }

      let type = el.type.toLowerCase();

      if (((el instanceof this.content.HTMLInputElement && el.mozIsTextField(true)) ||
          type == "hidden" || type == "textarea") ||
          ((type == "checkbox" || type == "radio") && el.checked)) {
        formData.push(escapeNameValuePair(el.name, el.value, isURLEncoded));
      } else if (el instanceof this.content.HTMLSelectElement && el.selectedIndex >= 0) {
        for (let j = 0; j < el.options.length; j++) {
          if (el.options[j].selected)
            formData.push(escapeNameValuePair(el.name, el.options[j].value,
                                              isURLEncoded));
        }
      }
    }

    let postData;

    if (isURLEncoded) {
      postData = formData.join("&");
    } else {
      let separator = spec.includes("?") ? "&" : "?";
      spec += separator + formData.join("&");
    }

    this.global.sendAsyncMessage("ContextMenu:SearchFieldBookmarkData:Result",
                                 { spec, title, description, postData, charset });
  },

  "ContextMenu:SaveVideoFrameAsImage": function(aMessage) {
    let video = this.getTarget(aMessage);
    let canvas = this.content.document.createElementNS("http://www.w3.org/1999/xhtml",
                                                       "canvas");
    canvas.width = video.videoWidth;
    canvas.height = video.videoHeight;

    let ctxDraw = canvas.getContext("2d");
    ctxDraw.drawImage(video, 0, 0);

    this.global.sendAsyncMessage("ContextMenu:SaveVideoFrameAsImage:Result", {
      dataURL: canvas.toDataURL("image/jpeg", ""),
    });
  },

  "ContextMenu:SetAsDesktopBackground": function(aMessage) {
    let target = this.getTarget(aMessage);

    // Paranoia: check disableSetDesktopBackground again, in case the
    // image changed since the context menu was initiated.
    let disable = this._disableSetDesktopBackground(target);

    if (!disable) {
      try {
        BrowserUtils.urlSecurityCheck(target.currentURI.spec,
                                      target.ownerDocument.nodePrincipal);
        let canvas = this.content.document.createElement("canvas");
        canvas.width = target.naturalWidth;
        canvas.height = target.naturalHeight;
        let ctx = canvas.getContext("2d");
        ctx.drawImage(target, 0, 0);
        let dataUrl = canvas.toDataURL();
        let url = (new URL(target.ownerDocument.location.href)).pathname;
        let imageName = url.substr(url.lastIndexOf("/") + 1);
        this.global.sendAsyncMessage("ContextMenu:SetAsDesktopBackground:Result",
                                     { dataUrl, imageName });
      } catch (e) {
        Cu.reportError(e);
        disable = true;
      }
    }

    if (disable) {
      this.global.sendAsyncMessage("ContextMenu:SetAsDesktopBackground:Result",
                                   { disable });
    }
  },
};

class ContextMenu {
  // PUBLIC
  constructor(global) {
    this.target = null;
    this.context = null;
    this.global = global;
    this.content = global.content;

    Services.els.addSystemEventListener(global, "contextmenu",
                                        this._handleContentContextMenu.bind(this), false);

    Object.keys(messageListeners).forEach(key =>
      global.addMessageListener(key, messageListeners[key].bind(this))
    );
  }

  /**
   * Returns the event target of the context menu, using a locally stored
   * reference if possible. If not, and aMessage.objects is defined,
   * aMessage.objects[aKey] is returned. Otherwise null.
   * @param  {Object} aMessage Message with a objects property
   * @param  {String} aKey     Key for the target on aMessage.objects
   * @return {Object}          Context menu target
   */
  getTarget(aMessage, aKey = "target") {
    return this.target || (aMessage.objects && aMessage.objects[aKey]);
  }

  // PRIVATE
  _isXULTextLinkLabel(aNode) {
    const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
    return aNode.namespaceURI == XUL_NS &&
           aNode.tagName == "label" &&
           aNode.classList.contains("text-link") &&
           aNode.href;
  }

  // Generate fully qualified URL for clicked-on link.
  _getLinkURL() {
    let href = this.context.link.href;

    if (href) {
      // Handle SVG links:
      if (typeof href == "object" && href.animVal) {
        return this._makeURLAbsolute(this.context.link.baseURI, href.animVal);
      }

      return href;
    }

    href = this.context.link.getAttribute("href") ||
           this.context.link.getAttributeNS("http://www.w3.org/1999/xlink", "href");

    if (!href || !href.match(/\S/)) {
      // Without this we try to save as the current doc,
      // for example, HTML case also throws if empty
      throw "Empty href";
    }

    return this._makeURLAbsolute(this.context.link.baseURI, href);
  }

  _getLinkURI() {
    try {
      return Services.io.newURI(this.context.linkURL);
    } catch (ex) {
     // e.g. empty URL string
    }

    return null;
  }

  // Get text of link.
  _getLinkText() {
    let text = this._gatherTextUnder(this.context.link);

    if (!text || !text.match(/\S/)) {
      text = this.context.link.getAttribute("title");
      if (!text || !text.match(/\S/)) {
        text = this.context.link.getAttribute("alt");
        if (!text || !text.match(/\S/)) {
          text = this.context.linkURL;
        }
      }
    }

    return text;
  }

  _getLinkProtocol() {
    if (this.context.linkURI) {
      return this.context.linkURI.scheme; // can be |undefined|
    }

    return null;
  }

  // Returns true if clicked-on link targets a resource that can be saved.
  _isLinkSaveable(aLink) {
    // We don't do the Right Thing for news/snews yet, so turn them off
    // until we do.
    return this.context.linkProtocol && !(
           this.context.linkProtocol == "mailto" ||
           this.context.linkProtocol == "javascript" ||
           this.context.linkProtocol == "news" ||
           this.context.linkProtocol == "snews");
  }

  // Gather all descendent text under given document node.
  _gatherTextUnder(root) {
    let text = "";
    let node = root.firstChild;
    let depth = 1;
    while (node && depth > 0) {
      // See if this node is text.
      if (node.nodeType == Ci.nsIDOMNode.TEXT_NODE) {
        // Add this text to our collection.
        text += " " + node.data;
      } else if (node instanceof this.content.HTMLImageElement) {
        // If it has an "alt" attribute, add that.
        let altText = node.getAttribute( "alt" );
        if ( altText && altText != "" ) {
          text += " " + altText;
        }
      }
      // Find next node to test.
      // First, see if this node has children.
      if (node.hasChildNodes()) {
        // Go to first child.
        node = node.firstChild;
        depth++;
      } else {
        // No children, try next sibling (or parent next sibling).
        while (depth > 0 && !node.nextSibling) {
          node = node.parentNode;
          depth--;
        }
        if (node.nextSibling) {
          node = node.nextSibling;
        }
      }
    }

    // Strip leading and tailing whitespace.
    text = text.trim();
    // Compress remaining whitespace.
    text = text.replace(/\s+/g, " ");
    return text;
  }

  // Returns a "url"-type computed style attribute value, with the url() stripped.
  _getComputedURL(aElem, aProp) {
    let url = aElem.ownerGlobal.getComputedStyle(aElem).getPropertyCSSValue(aProp);

    if (url instanceof this.content.CSSValueList) {
      if (url.length != 1) {
        throw "found multiple URLs";
      }

      url = url[0];
    }

    return url.primitiveType == this.content.CSSPrimitiveValue.CSS_URI ?
           url.getStringValue() : null;
  }

  _makeURLAbsolute(aBase, aUrl) {
    return Services.io.newURI(aUrl, null, Services.io.newURI(aBase)).spec;
  }

  _isProprietaryDRM() {
    return this.context.target.isEncrypted && this.context.target.mediaKeys &&
           this.context.target.mediaKeys.keySystem != "org.w3.clearkey";
  }

  _isMediaURLReusable(aURL) {
    if (aURL.startsWith("blob:")) {
      return URL.isValidURL(aURL);
    }

    return true;
  }

  _isTargetATextBox(node) {
    if (node instanceof this.content.HTMLInputElement) {
      return node.mozIsTextField(false);
    }

    return (node instanceof this.content.HTMLTextAreaElement);
  }

  _isSpellCheckEnabled(aNode) {
    // We can always force-enable spellchecking on textboxes
    if (this._isTargetATextBox(aNode)) {
      return true;
    }

    // We can never spell check something which is not content editable
    let editable = aNode.isContentEditable;

    if (!editable && aNode.ownerDocument) {
      editable = aNode.ownerDocument.designMode == "on";
    }

    if (!editable) {
      return false;
    }

    // Otherwise make sure that nothing in the parent chain disables spellchecking
    return aNode.spellcheck;
  }

  _disableSetDesktopBackground(aTarget) {
    // Disable the Set as Desktop Background menu item if we're still trying
    // to load the image or the load failed.
    if (!(aTarget instanceof Ci.nsIImageLoadingContent)) {
      return true;
    }

    if (("complete" in aTarget) && !aTarget.complete) {
      return true;
    }

    if (aTarget.currentURI.schemeIs("javascript")) {
      return true;
    }

    let request = aTarget.getRequest(Ci.nsIImageLoadingContent.CURRENT_REQUEST);

    if (!request) {
      return true;
    }

    return false;
  }

  /**
   * Retrieve the array of CSS selectors corresponding to the provided node.
   *
   * The selectors are ordered starting with the root document and ending with the deepest
   * nested frame. Additional items are used if the node is inside a frame, each
   * representing the CSS selector for finding the frame element in its parent document.
   *
   * This format is expected by DevTools in order to handle the Inspect Node context menu
   * item.
   *
   * @param  {aNode}
   *         The node for which the CSS selectors should be computed
   * @return {Array}
   *         An array of CSS selectors to find the target node. Several selectors can be
   *         needed if the element is nested in frames and not directly in the root
   *         document. The selectors are ordered starting with the root document and
   *         ending with the deepest nested frame.
   */
  _getNodeSelectors(aNode) {
    let selectors = [];
    while (aNode) {
      selectors.unshift(findCssSelector(aNode));
      aNode = aNode.ownerGlobal.frameElement;
    }

    return selectors;
  }

  _handleContentContextMenu(aEvent) {
    let defaultPrevented = aEvent.defaultPrevented;

    if (!Services.prefs.getBoolPref("dom.event.contextmenu.enabled")) {
      let plugin = null;

      try {
        plugin = aEvent.target.QueryInterface(Ci.nsIObjectLoadingContent);
      } catch (e) {}

      if (plugin && plugin.displayedType == Ci.nsIObjectLoadingContent.TYPE_PLUGIN) {
        // Don't open a context menu for plugins.
        return;
      }

      defaultPrevented = false;
    }

    if (defaultPrevented) {
      return;
    }

    let doc = aEvent.target.ownerDocument;
    let {
      mozDocumentURIIfNotForErrorPages: docLocation,
      characterSet: charSet,
      baseURI,
      referrer,
      referrerPolicy
    } = doc;
    docLocation = docLocation && docLocation.spec;
    let frameOuterWindowID = WebNavigationFrames.getFrameId(doc.defaultView);
    let loginFillInfo = LoginManagerContent.getFieldContext(aEvent.target);

    // The same-origin check will be done in nsContextMenu.openLinkInTab.
    let parentAllowsMixedContent = !!this.global.docShell.mixedContentChannel;

    // Get referrer attribute from clicked link and parse it
    let referrerAttrValue = Services.netUtils.parseAttributePolicyString(aEvent.target.
                            getAttribute("referrerpolicy"));

    if (referrerAttrValue !== Ci.nsIHttpChannel.REFERRER_POLICY_UNSET) {
      referrerPolicy = referrerAttrValue;
    }

    let disableSetDesktopBg = null;

    // Media related cache info parent needs for saving
    let contentType = null;
    let contentDisposition = null;
    if (aEvent.target.nodeType == Ci.nsIDOMNode.ELEMENT_NODE &&
        aEvent.target instanceof Ci.nsIImageLoadingContent &&
        aEvent.target.currentRequestFinalURI) {
      disableSetDesktopBg = this._disableSetDesktopBackground(aEvent.target);

      try {
        let imageCache = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
                                                         .getImgCacheForDocument(doc);
        let props = imageCache.findEntryProperties(aEvent.target.currentRequestFinalURI, doc);

        try {
          contentType = props.get("type", Ci.nsISupportsCString).data;
        } catch (e) {}

        try {
          contentDisposition = props.get("content-disposition", Ci.nsISupportsCString).data;
        } catch (e) {}
      } catch (e) {}
    }

    let selectionInfo = BrowserUtils.getSelectionDetails(this.content);
    let loadContext = this.global.docShell.QueryInterface(Ci.nsILoadContext);
    let userContextId = loadContext.originAttributes.userContextId;
    let popupNodeSelectors = this._getNodeSelectors(aEvent.target);

    this._setContext(aEvent);
    let context = this.context;
    this.target = context.target;

    let spellInfo = null;
    let editFlags = null;
    let principal = null;
    let customMenuItems = null;

    let targetAsCPOW = context.target;
    if (targetAsCPOW) {
      this._cleanContext();
    }

    let isRemote = Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT;

    if (isRemote) {
      editFlags = SpellCheckHelper.isEditable(aEvent.target, this.content);

      if (editFlags & SpellCheckHelper.SPELLCHECKABLE) {
        spellInfo = InlineSpellCheckerContent.initContextMenu(aEvent, editFlags, this.global);
      }

      // Set the event target first as the copy image command needs it to
      // determine what was context-clicked on. Then, update the state of the
      // commands on the context menu.
      this.global.docShell.contentViewer.QueryInterface(Ci.nsIContentViewerEdit)
                          .setCommandNode(aEvent.target);
      aEvent.target.ownerGlobal.updateCommands("contentcontextmenu");

      customMenuItems = PageMenuChild.build(aEvent.target);
      principal = doc.nodePrincipal;
    }

    let data = {
      context,
      charSet,
      baseURI,
      isRemote,
      referrer,
      editFlags,
      principal,
      spellInfo,
      contentType,
      docLocation,
      loginFillInfo,
      selectionInfo,
      userContextId,
      referrerPolicy,
      customMenuItems,
      contentDisposition,
      frameOuterWindowID,
      popupNodeSelectors,
      disableSetDesktopBg,
      parentAllowsMixedContent,
    };

    if (isRemote) {
      this.global.sendAsyncMessage("contextmenu", data, {
        targetAsCPOW,
      });
    } else {
      let browser = this.global.docShell.chromeEventHandler;
      let mainWin = browser.ownerGlobal;

      data.documentURIObject = doc.documentURIObject;
      data.disableSetDesktopBackground = data.disableSetDesktopBg;
      delete data.disableSetDesktopBg;

      data.context.targetAsCPOW = targetAsCPOW;

      mainWin.setContextMenuContentData(data);
    }
  }

  /**
   * Some things are not serializable, so we either have to only send
   * their needed data or regenerate them in nsContextMenu.js
   * - target and target.ownerDocument
   * - link
   * - linkURI
   */
  _cleanContext(aEvent) {
    const context = this.context;
    const cleanTarget = Object.create(null);

    cleanTarget.ownerDocument = {
      // used for nsContextMenu.initLeaveDOMFullScreenItems and
      // nsContextMenu.initMediaPlayerItems
      fullscreen: context.target.ownerDocument.fullscreen,

      // used for nsContextMenu.initMiscItems
      contentType: context.target.ownerDocument.contentType,

      // used for nsContextMenu.saveLink
      isPrivate: PrivateBrowsingUtils.isContentWindowPrivate(context.target.ownerGlobal),
    };

    // used for nsContextMenu.initMediaPlayerItems
    Object.assign(cleanTarget, {
      ended: context.target.ended,
      muted: context.target.muted,
      paused: context.target.paused,
      controls: context.target.controls,
      duration: context.target.duration,
    });

    const onMedia = context.onVideo || context.onAudio;

    if (onMedia) {
      Object.assign(cleanTarget, {
        loop: context.target.loop,
        error: context.target.error,
        networkState: context.target.networkState,
        playbackRate: context.target.playbackRate,
        NETWORK_NO_SOURCE: context.target.NETWORK_NO_SOURCE,
      });

      if (context.onVideo) {
        Object.assign(cleanTarget, {
          readyState: context.target.readyState,
          HAVE_CURRENT_DATA: context.target.HAVE_CURRENT_DATA,
        });
      }
    }

    context.target = cleanTarget;

    if (context.link) {
      context.link = { href: context.linkURL };
    }

    delete context.linkURI;
  }

  _setContext(aEvent) {
    this.context = Object.create(null);
    const context = this.context;

    context.screenX = aEvent.screenX;
    context.screenY = aEvent.screenY;
    context.mozInputSource = aEvent.mozInputSource;

    const node = aEvent.target;

    const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";

    context.shouldDisplay = true;

    if (node.nodeType == Ci.nsIDOMNode.DOCUMENT_NODE ||
        // Don't display for XUL element unless <label class="text-link">
        (node.namespaceURI == XUL_NS && !this._isXULTextLinkLabel(node))) {
      context.shouldDisplay = false;
      return;
    }

    // Initialize context to be sent to nsContextMenu
    // Keep this consistent with the similar code in nsContextMenu's setContext
    context.bgImageURL          = "";
    context.imageDescURL        = "";
    context.imageInfo           = null;
    context.mediaURL            = "";
    context.webExtBrowserType   = "";

    context.canSpellCheck       = false;
    context.hasBGImage          = false;
    context.hasMultipleBGImages = false;
    context.isDesignMode        = false;
    context.inFrame             = false;
    context.inSrcdocFrame       = false;
    context.inSyntheticDoc      = false;
    context.inTabBrowser        = true;
    context.inWebExtBrowser     = false;

    context.link                = null;
    context.linkDownload        = "";
    context.linkHasNoReferrer   = false;
    context.linkProtocol        = "";
    context.linkTextStr         = "";
    context.linkURL             = "";
    context.linkURI             = null;

    context.onAudio             = false;
    context.onCanvas            = false;
    context.onCompletedImage    = false;
    context.onCTPPlugin         = false;
    context.onDRMMedia          = false;
    context.onEditable          = false;
    context.onImage             = false;
    context.onKeywordField      = false;
    context.onLink              = false;
    context.onLoadedImage       = false;
    context.onMailtoLink        = false;
    context.onMathML            = false;
    context.onMozExtLink        = false;
    context.onNumeric           = false;
    context.onPassword          = false;
    context.onSaveableLink      = false;
    context.onSpellcheckable    = false;
    context.onTextInput         = false;
    context.onVideo             = false;

    // Remember the node and its owner document that was clicked
    // This may be modifed before sending to nsContextMenu
    context.target = node;

    context.principal = context.target.ownerDocument.nodePrincipal;
    context.frameOuterWindowID = WebNavigationFrames.getFrameId(context.target.ownerGlobal);

    // Check if we are in a synthetic document (stand alone image, video, etc.).
    context.inSyntheticDoc = context.target.ownerDocument.mozSyntheticDocument;

    context.shouldInitInlineSpellCheckerUINoChildren = false;
    context.shouldInitInlineSpellCheckerUIWithChildren = false;

    let editFlags = SpellCheckHelper.isEditable(context.target, this.content);
    this._setContextForNodesNoChildren(editFlags);
    this._setContextForNodesWithChildren(editFlags);
  }

  /**
   * Sets up the parts of the context menu for when when nodes have no children.
   *
   * @param {Integer} editFlags The edit flags for the node. See SpellCheckHelper
   *                            for the details.
   */
  _setContextForNodesNoChildren(editFlags) {
    const context = this.context;

    if (context.target.nodeType == Ci.nsIDOMNode.TEXT_NODE) {
      // For text nodes, look at the parent node to determine the spellcheck attribute.
      context.canSpellCheck = context.target.parentNode &&
                              this._isSpellCheckEnabled(context.target);
      return;
    }

    // We only deal with TEXT_NODE and ELEMENT_NODE in this function, so return
    // early if we don't have one.
    if (context.target.nodeType != Ci.nsIDOMNode.ELEMENT_NODE) {
      return;
    }

    // See if the user clicked on an image. This check mirrors
    // nsDocumentViewer::GetInImage. Make sure to update both if this is
    // changed.
    if (context.target instanceof Ci.nsIImageLoadingContent &&
        context.target.currentRequestFinalURI) {
      context.onImage = true;

      context.imageInfo = {
        currentSrc: context.target.currentSrc,
        width: context.target.width,
        height: context.target.height,
        imageText: context.target.title || context.target.alt
      };

      const request = context.target.getRequest(Ci.nsIImageLoadingContent.CURRENT_REQUEST);

      if (request && (request.imageStatus & request.STATUS_SIZE_AVAILABLE)) {
        context.onLoadedImage = true;
      }

      if (request &&
          (request.imageStatus & request.STATUS_LOAD_COMPLETE) &&
          !(request.imageStatus & request.STATUS_ERROR)) {
        context.onCompletedImage = true;
      }

      context.mediaURL = context.target.currentRequestFinalURI.spec;

      const descURL = context.target.getAttribute("longdesc");

      if (descURL) {
        context.imageDescURL = this._makeURLAbsolute(context.target.ownerDocument.body.baseURI,
                                                    descURL);
      }
    } else if (context.target instanceof this.content.HTMLCanvasElement) {
      context.onCanvas = true;
    } else if (context.target instanceof this.content.HTMLVideoElement) {
      const mediaURL = context.target.currentSrc || context.target.src;

      if (this._isMediaURLReusable(mediaURL)) {
        context.mediaURL = mediaURL;
      }

      if (this._isProprietaryDRM()) {
        context.onDRMMedia = true;
      }

      // Firefox always creates a HTMLVideoElement when loading an ogg file
      // directly. If the media is actually audio, be smarter and provide a
      // context menu with audio operations.
      if (context.target.readyState >= context.target.HAVE_METADATA &&
          (context.target.videoWidth == 0 || context.target.videoHeight == 0)) {
        context.onAudio = true;
      } else {
        context.onVideo = true;
      }
    } else if (context.target instanceof this.content.HTMLAudioElement) {
      context.onAudio = true;
      const mediaURL = context.target.currentSrc || context.target.src;

      if (this._isMediaURLReusable(mediaURL)) {
        context.mediaURL = mediaURL;
      }

      if (this._isProprietaryDRM()) {
        context.onDRMMedia = true;
      }
    } else if (editFlags & (SpellCheckHelper.INPUT | SpellCheckHelper.TEXTAREA)) {
      context.onTextInput = (editFlags & SpellCheckHelper.TEXTINPUT) !== 0;
      context.onNumeric = (editFlags & SpellCheckHelper.NUMERIC) !== 0;
      context.onEditable = (editFlags & SpellCheckHelper.EDITABLE) !== 0;
      context.onPassword = (editFlags & SpellCheckHelper.PASSWORD) !== 0;
      context.onSpellcheckable = (editFlags & SpellCheckHelper.SPELLCHECKABLE) !== 0;

      // This is guaranteed to be an input or textarea because of the condition above,
      // so the no-children flag is always correct. We deal with contenteditable elsewhere.
      if (context.onSpellcheckable) {
        context.shouldInitInlineSpellCheckerUINoChildren = true;
      }

      context.onKeywordField = (editFlags & SpellCheckHelper.KEYWORD);
    } else if (context.target instanceof this.content.HTMLHtmlElement) {
      const bodyElt = context.target.ownerDocument.body;

      if (bodyElt) {
        let computedURL;

        try {
          computedURL = this._getComputedURL(bodyElt, "background-image");
          context.hasMultipleBGImages = false;
        } catch (e) {
          context.hasMultipleBGImages = true;
        }

        if (computedURL) {
          context.hasBGImage = true;
          context.bgImageURL = this._makeURLAbsolute(bodyElt.baseURI,
                                                    computedURL);
        }
      }
    } else if ((context.target instanceof this.content.HTMLEmbedElement ||
               context.target instanceof this.content.HTMLObjectElement) &&
               context.target.displayedType == this.content.HTMLObjectElement.TYPE_NULL &&
               context.target.pluginFallbackType == this.content.HTMLObjectElement.PLUGIN_CLICK_TO_PLAY) {
      context.onCTPPlugin = true;
    }

    context.canSpellCheck = this._isSpellCheckEnabled(context.target);
  }

  /**
   * Sets up the parts of the context menu for when when nodes have children.
   *
   * @param {Integer} editFlags The edit flags for the node. See SpellCheckHelper
   *                            for the details.
   */
  _setContextForNodesWithChildren(editFlags) {
    const context = this.context;

    // Second, bubble out, looking for items of interest that can have childen.
    // Always pick the innermost link, background image, etc.
    let elem = context.target;

    while (elem) {
      if (elem.nodeType == Ci.nsIDOMNode.ELEMENT_NODE) {
        // Link?
        const XLINK_NS = "http://www.w3.org/1999/xlink";

        if (!context.onLink &&
            // Be consistent with what hrefAndLinkNodeForClickEvent
            // does in browser.js
            (this._isXULTextLinkLabel(elem) ||
            (elem instanceof this.content.HTMLAnchorElement && elem.href) ||
            (elem instanceof this.content.SVGAElement &&
            (elem.href || elem.hasAttributeNS(XLINK_NS, "href"))) ||
            (elem instanceof this.content.HTMLAreaElement && elem.href) ||
            elem instanceof this.content.HTMLLinkElement ||
            elem.getAttributeNS(XLINK_NS, "type") == "simple")) {

          // Target is a link or a descendant of a link.
          context.onLink = true;

          // Remember corresponding element.
          context.link = elem;
          context.linkURL = this._getLinkURL();
          context.linkURI = this._getLinkURI();
          context.linkTextStr = this._getLinkText();
          context.linkProtocol = this._getLinkProtocol();
          context.onMailtoLink = (context.linkProtocol == "mailto");
          context.onMozExtLink = (context.linkProtocol == "moz-extension");
          context.onSaveableLink = this._isLinkSaveable(context.link);
          context.linkHasNoReferrer = BrowserUtils.linkHasNoReferrer(elem);

          try {
            if (elem.download) {
              // Ignore download attribute on cross-origin links
              context.principal.checkMayLoad(context.linkURI, false, true);
              context.linkDownload = elem.download;
            }
          } catch (ex) {}
        }

        // Background image?  Don't bother if we've already found a
        // background image further down the hierarchy.  Otherwise,
        // we look for the computed background-image style.
        if (!context.hasBGImage &&
            !context.hasMultipleBGImages) {
          let bgImgUrl = null;

          try {
            bgImgUrl = this._getComputedURL(elem, "background-image");
            context.hasMultipleBGImages = false;
          } catch (e) {
            context.hasMultipleBGImages = true;
          }

          if (bgImgUrl) {
            context.hasBGImage = true;
            context.bgImageURL = this._makeURLAbsolute(elem.baseURI,
                                                      bgImgUrl);
          }
        }
      }

      elem = elem.parentNode;
    }

    // See if the user clicked on MathML
    const MathML_NS = "http://www.w3.org/1998/Math/MathML";

    if ((context.target.nodeType == Ci.nsIDOMNode.TEXT_NODE &&
         context.target.parentNode.namespaceURI == MathML_NS) ||
         (context.target.namespaceURI == MathML_NS)) {
      context.onMathML = true;
    }

    // See if the user clicked in a frame.
    const docDefaultView = context.target.ownerGlobal;

    if (docDefaultView != docDefaultView.top) {
      context.inFrame = true;

      if (context.target.ownerDocument.isSrcdocDocument) {
          context.inSrcdocFrame = true;
      }
    }

    // if the document is editable, show context menu like in text inputs
    if (!context.onEditable) {
      if (editFlags & SpellCheckHelper.CONTENTEDITABLE) {
        // If this.onEditable is false but editFlags is CONTENTEDITABLE, then
        // the document itself must be editable.
        context.onTextInput       = true;
        context.onKeywordField    = false;
        context.onImage           = false;
        context.onLoadedImage     = false;
        context.onCompletedImage  = false;
        context.onMathML          = false;
        context.inFrame           = false;
        context.inSrcdocFrame     = false;
        context.hasBGImage        = false;
        context.isDesignMode      = true;
        context.onEditable        = true;
        context.onSpellcheckable  = true;
        context.shouldInitInlineSpellCheckerUIWithChildren = true;
      }
    }
  }
}
PK
!<�(B����modules/CustomizableUI.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = ["CustomizableUI"];

ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/AppConstants.jsm");

XPCOMUtils.defineLazyModuleGetters(this, {
  SearchWidgetTracker: "resource:///modules/SearchWidgetTracker.jsm",
  CustomizableWidgets: "resource:///modules/CustomizableWidgets.jsm",
  DeferredTask: "resource://gre/modules/DeferredTask.jsm",
  PanelMultiView: "resource:///modules/PanelMultiView.jsm",
  PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
  ShortcutUtils: "resource://gre/modules/ShortcutUtils.jsm",
  LightweightThemeManager: "resource://gre/modules/LightweightThemeManager.jsm",
});

XPCOMUtils.defineLazyGetter(this, "gWidgetsBundle", function() {
  const kUrl = "chrome://browser/locale/customizableui/customizableWidgets.properties";
  return Services.strings.createBundle(kUrl);
});

XPCOMUtils.defineLazyServiceGetter(this, "gELS",
  "@mozilla.org/eventlistenerservice;1", "nsIEventListenerService");

const kNSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";

const kSpecialWidgetPfx = "customizableui-special-";

const kPrefCustomizationState        = "browser.uiCustomization.state";
const kPrefCustomizationAutoAdd      = "browser.uiCustomization.autoAdd";
const kPrefCustomizationDebug        = "browser.uiCustomization.debug";
const kPrefDrawInTitlebar            = "browser.tabs.drawInTitlebar";
const kPrefExtraDragSpace            = "browser.tabs.extraDragSpace";
const kPrefUIDensity                 = "browser.uidensity";
const kPrefAutoTouchMode             = "browser.touchmode.auto";
const kPrefAutoHideDownloadsButton   = "browser.download.autohideButton";

const kExpectedWindowURL = "chrome://browser/content/browser.xul";

/**
 * The keys are the handlers that are fired when the event type (the value)
 * is fired on the subview. A widget that provides a subview has the option
 * of providing onViewShowing and onViewHiding event handlers.
 */
const kSubviewEvents = [
  "ViewShowing",
  "ViewHiding"
];

/**
 * The current version. We can use this to auto-add new default widgets as necessary.
 * (would be const but isn't because of testing purposes)
 */
var kVersion = 14;

/**
 * Buttons removed from built-ins by version they were removed. kVersion must be
 * bumped any time a new id is added to this. Use the button id as key, and
 * version the button is removed in as the value.  e.g. "pocket-button": 5
 */
var ObsoleteBuiltinButtons = {
};

/**
 * gPalette is a map of every widget that CustomizableUI.jsm knows about, keyed
 * on their IDs.
 */
var gPalette = new Map();

/**
 * gAreas maps area IDs to Sets of properties about those areas. An area is a
 * place where a widget can be put.
 */
var gAreas = new Map();

/**
 * gPlacements maps area IDs to Arrays of widget IDs, indicating that the widgets
 * are placed within that area (either directly in the area node, or in the
 * customizationTarget of the node).
 */
var gPlacements = new Map();

/**
 * gFuturePlacements represent placements that will happen for areas that have
 * not yet loaded (due to lazy-loading). This can occur when add-ons register
 * widgets.
 */
var gFuturePlacements = new Map();

// XXXunf Temporary. Need a nice way to abstract functions to build widgets
//       of these types.
var gSupportedWidgetTypes = new Set(["button", "view", "custom"]);

/**
 * gPanelsForWindow is a list of known panels in a window which we may need to close
 * should command events fire which target them.
 */
var gPanelsForWindow = new WeakMap();

/**
 * gSeenWidgets remembers which widgets the user has seen for the first time
 * before. This way, if a new widget is created, and the user has not seen it
 * before, it can be put in its default location. Otherwise, it remains in the
 * palette.
 */
var gSeenWidgets = new Set();

/**
 * gDirtyAreaCache is a set of area IDs for areas where items have been added,
 * moved or removed at least once. This set is persisted, and is used to
 * optimize building of toolbars in the default case where no toolbars should
 * be "dirty".
 */
var gDirtyAreaCache = new Set();

/**
 * gPendingBuildAreas is a map from area IDs to map from build nodes to their
 * existing children at the time of node registration, that are waiting
 * for the area to be registered
 */
var gPendingBuildAreas = new Map();

var gSavedState = null;
var gRestoring = false;
var gDirty = false;
var gInBatchStack = 0;
var gResetting = false;
var gUndoResetting = false;

/**
 * gBuildAreas maps area IDs to actual area nodes within browser windows.
 */
var gBuildAreas = new Map();

/**
 * gBuildWindows is a map of windows that have registered build areas, mapped
 * to a Set of known toolboxes in that window.
 */
var gBuildWindows = new Map();

var gNewElementCount = 0;
var gGroupWrapperCache = new Map();
var gSingleWrapperCache = new WeakMap();
var gListeners = new Set();

var gUIStateBeforeReset = {
  uiCustomizationState: null,
  drawInTitlebar: null,
  extraDragSpace: null,
  currentTheme: null,
  uiDensity: null,
  autoTouchMode: null,
};

XPCOMUtils.defineLazyPreferenceGetter(this, "gDebuggingEnabled", kPrefCustomizationDebug, false,
  (pref, oldVal, newVal) => {
    if (typeof log != "undefined") {
      log.maxLogLevel = newVal ? "all" : "log";
    }
  }
);

XPCOMUtils.defineLazyGetter(this, "log", () => {
  let scope = {};
  ChromeUtils.import("resource://gre/modules/Console.jsm", scope);
  let consoleOptions = {
    maxLogLevel: gDebuggingEnabled ? "all" : "log",
    prefix: "CustomizableUI",
  };
  return new scope.ConsoleAPI(consoleOptions);
});

var CustomizableUIInternal = {
  initialize() {
    log.debug("Initializing");

    this.addListener(this);
    this._defineBuiltInWidgets();
    this.loadSavedState();
    this._updateForNewVersion();
    this._markObsoleteBuiltinButtonsSeen();

    this.registerArea(CustomizableUI.AREA_FIXED_OVERFLOW_PANEL, {
      type: CustomizableUI.TYPE_MENU_PANEL,
      defaultPlacements: [],
      anchor: "nav-bar-overflow-button",
    }, true);

    let navbarPlacements = [
      "back-button",
      "forward-button",
      "stop-reload-button",
      "home-button",
      "spring",
      "urlbar-container",
      "spring",
      "downloads-button",
      "library-button",
      "sidebar-button",
    ];

    if (AppConstants.MOZ_DEV_EDITION) {
      navbarPlacements.splice(2, 0, "developer-button");
    }

    this.registerArea(CustomizableUI.AREA_NAVBAR, {
      legacy: true,
      type: CustomizableUI.TYPE_TOOLBAR,
      overflowable: true,
      defaultPlacements: navbarPlacements,
      defaultCollapsed: false,
    }, true);

    if (AppConstants.MENUBAR_CAN_AUTOHIDE) {
      this.registerArea(CustomizableUI.AREA_MENUBAR, {
        legacy: true,
        type: CustomizableUI.TYPE_TOOLBAR,
        defaultPlacements: [
          "menubar-items",
        ],
        defaultCollapsed: true,
      }, true);
    }

    this.registerArea(CustomizableUI.AREA_TABSTRIP, {
      legacy: true,
      type: CustomizableUI.TYPE_TOOLBAR,
      defaultPlacements: [
        "tabbrowser-tabs",
        "new-tab-button",
        "alltabs-button",
      ],
      defaultCollapsed: null,
    }, true);
    this.registerArea(CustomizableUI.AREA_BOOKMARKS, {
      legacy: true,
      type: CustomizableUI.TYPE_TOOLBAR,
      defaultPlacements: [
        "personal-bookmarks",
      ],
      defaultCollapsed: true,
    }, true);

    SearchWidgetTracker.init();
  },

  get _builtinAreas() {
    return new Set([
      ...this._builtinToolbars,
      CustomizableUI.AREA_FIXED_OVERFLOW_PANEL,
    ]);
  },

  get _builtinToolbars() {
    let toolbars = new Set([
      CustomizableUI.AREA_NAVBAR,
      CustomizableUI.AREA_BOOKMARKS,
      CustomizableUI.AREA_TABSTRIP,
    ]);
    if (AppConstants.platform != "macosx") {
      toolbars.add(CustomizableUI.AREA_MENUBAR);
    }
    return toolbars;
  },

  _defineBuiltInWidgets() {
    for (let widgetDefinition of CustomizableWidgets) {
      this.createBuiltinWidget(widgetDefinition);
    }
  },

  _updateForNewVersion() {
    // We should still enter even if gSavedState.currentVersion >= kVersion
    // because the per-widget pref facility is independent of versioning.
    if (!gSavedState) {
      // Flip all the prefs so we don't try to re-introduce later:
      for (let [, widget] of gPalette) {
        if (widget.defaultArea && widget._introducedInVersion === "pref") {
          let prefId = "browser.toolbarbuttons.introduced." + widget.id;
          Services.prefs.setBoolPref(prefId, true);
        }
      }
      return;
    }

    let currentVersion = gSavedState.currentVersion;
    for (let [id, widget] of gPalette) {
      if (widget.defaultArea) {
        let shouldAdd = false;
        let shouldSetPref = false;
        let prefId = "browser.toolbarbuttons.introduced." + widget.id;
        if (widget._introducedInVersion === "pref") {
          try {
            shouldAdd = !Services.prefs.getBoolPref(prefId);
          } catch (ex) {
            // Pref doesn't exist:
            shouldAdd = true;
          }
          shouldSetPref = shouldAdd;
        } else if (widget._introducedInVersion > currentVersion) {
          shouldAdd = true;
        }

        if (shouldAdd) {
          let futurePlacements = gFuturePlacements.get(widget.defaultArea);
          if (futurePlacements) {
            futurePlacements.add(id);
          } else {
            gFuturePlacements.set(widget.defaultArea, new Set([id]));
          }
          if (shouldSetPref) {
            Services.prefs.setBoolPref(prefId, true);
          }
        }
      }
    }

    if (currentVersion < 7 && gSavedState.placements &&
        gSavedState.placements[CustomizableUI.AREA_NAVBAR]) {
      let placements = gSavedState.placements[CustomizableUI.AREA_NAVBAR];
      let newPlacements = ["back-button", "forward-button", "stop-reload-button", "home-button"];
      for (let button of placements) {
        if (!newPlacements.includes(button)) {
          newPlacements.push(button);
        }
      }

      if (!newPlacements.includes("sidebar-button")) {
        newPlacements.push("sidebar-button");
      }

      gSavedState.placements[CustomizableUI.AREA_NAVBAR] = newPlacements;
    }

    if (currentVersion < 8 && gSavedState.placements &&
        gSavedState.placements["PanelUI-contents"]) {
      let savedPanelPlacements = gSavedState.placements["PanelUI-contents"];
      delete gSavedState.placements["PanelUI-contents"];
      let defaultPlacements = [
        "edit-controls",
        "zoom-controls",
        "new-window-button",
        "privatebrowsing-button",
        "save-page-button",
        "print-button",
        "history-panelmenu",
        "fullscreen-button",
        "find-button",
        "preferences-button",
        "add-ons-button",
        "sync-button",
      ];

      if (!AppConstants.MOZ_DEV_EDITION) {
        defaultPlacements.splice(-1, 0, "developer-button");
      }

      let showCharacterEncoding = Services.prefs.getComplexValue(
        "browser.menu.showCharacterEncoding",
        Ci.nsIPrefLocalizedString
      ).data;
      if (showCharacterEncoding == "true") {
        defaultPlacements.push("characterencoding-button");
      }

      savedPanelPlacements = savedPanelPlacements.filter(id => !defaultPlacements.includes(id));

      if (savedPanelPlacements.length) {
        gSavedState.placements[CustomizableUI.AREA_FIXED_OVERFLOW_PANEL] = savedPanelPlacements;
      }
    }

    if (currentVersion < 9 && gSavedState.placements && gSavedState.placements["nav-bar"]) {
      let placements = gSavedState.placements["nav-bar"];
      if (placements.includes("urlbar-container")) {
        let urlbarIndex = placements.indexOf("urlbar-container");
        let secondSpringIndex = urlbarIndex + 1;
        // Insert if there isn't already a spring before the urlbar
        if (urlbarIndex == 0 || !placements[urlbarIndex - 1].startsWith(kSpecialWidgetPfx + "spring")) {
          placements.splice(urlbarIndex, 0, "spring");
          // The url bar is now 1 index later, so increment the insertion point for
          // the second spring.
          secondSpringIndex++;
        }
        // If the search container is present, insert after the search container
        // instead of after the url bar
        let searchContainerIndex = placements.indexOf("search-container");
        if (searchContainerIndex != -1) {
          secondSpringIndex = searchContainerIndex + 1;
        }
        if (secondSpringIndex == placements.length ||
            !placements[secondSpringIndex].startsWith(kSpecialWidgetPfx + "spring")) {
          placements.splice(secondSpringIndex, 0, "spring");
        }
      }

      // Finally, replace the bookmarks menu button with the library one if present
      if (placements.includes("bookmarks-menu-button")) {
        let bmbIndex = placements.indexOf("bookmarks-menu-button");
        placements.splice(bmbIndex, 1);
        let downloadButtonIndex = placements.indexOf("downloads-button");
        let libraryIndex = downloadButtonIndex == -1 ? bmbIndex : (downloadButtonIndex + 1);
        placements.splice(libraryIndex, 0, "library-button");
      }
    }

    if (currentVersion < 10 && gSavedState.placements) {
      for (let placements of Object.values(gSavedState.placements)) {
        if (placements.includes("webcompat-reporter-button")) {
          placements.splice(placements.indexOf("webcompat-reporter-button"), 1);
          break;
        }
      }
    }

    // Move the downloads button to the default position in the navbar if it's
    // not there already.
    if (currentVersion < 11 && gSavedState.placements) {
      let navbarPlacements = gSavedState.placements[CustomizableUI.AREA_NAVBAR];
      // First remove from wherever it currently lives, if anywhere:
      for (let placements of Object.values(gSavedState.placements)) {
        let existingIndex = placements.indexOf("downloads-button");
        if (existingIndex != -1) {
          placements.splice(existingIndex, 1);
          break; // It can only be in 1 place, so no point looking elsewhere.
        }
      }

      // Now put the button in the navbar in the correct spot:
      if (navbarPlacements) {
        let insertionPoint = navbarPlacements.indexOf("urlbar-container");
        // Deliberately iterate to 1 past the end of the array to insert at the
        // end if need be.
        while (++insertionPoint < navbarPlacements.length) {
          let widget = navbarPlacements[insertionPoint];
          // If we find a non-searchbar, non-spacer node, break out of the loop:
          if (widget != "search-container" && !this.matchingSpecials(widget, "spring")) {
            break;
          }
        }
        // We either found the right spot, or reached the end of the
        // placements, so insert here:
        navbarPlacements.splice(insertionPoint, 0, "downloads-button");
      }
    }

    if (currentVersion < 12 && gSavedState.placements) {
      const removedButtons = ["loop-call-button", "loop-button-throttled", "pocket-button"];
      for (let placements of Object.values(gSavedState.placements)) {
        for (let button of removedButtons) {
          let buttonIndex = placements.indexOf(button);
          if (buttonIndex != -1) {
            placements.splice(buttonIndex, 1);
          }
        }
      }
    }

    // Remove the old placements from the now-gone Nightly-only
    // "New non-e10s window" button.
    if (currentVersion < 13 && gSavedState.placements) {
      for (let placements of Object.values(gSavedState.placements)) {
        let buttonIndex = placements.indexOf("e10s-button");
        if (buttonIndex != -1) {
          placements.splice(buttonIndex, 1);
        }
      }
    }

    // Remove unsupported custom toolbar saved placements
    if (currentVersion < 14 && gSavedState.placements) {
      for (let area in gSavedState.placements) {
        if (!this._builtinAreas.has(area)) {
          delete gSavedState.placements[area];
        }
      }
    }
  },

  /**
   * _markObsoleteBuiltinButtonsSeen
   * when upgrading, ensure obsoleted buttons are in seen state.
   */
  _markObsoleteBuiltinButtonsSeen() {
    if (!gSavedState)
      return;
    let currentVersion = gSavedState.currentVersion;
    if (currentVersion >= kVersion)
      return;
    // we're upgrading, update state if necessary
    for (let id in ObsoleteBuiltinButtons) {
      let version = ObsoleteBuiltinButtons[id];
      if (version == kVersion) {
        gSeenWidgets.add(id);
        gDirty = true;
      }
    }
  },

  _placeNewDefaultWidgetsInArea(aArea) {
    let futurePlacedWidgets = gFuturePlacements.get(aArea);
    let savedPlacements = gSavedState && gSavedState.placements && gSavedState.placements[aArea];
    let defaultPlacements = gAreas.get(aArea).get("defaultPlacements");
    if (!savedPlacements || !savedPlacements.length || !futurePlacedWidgets || !defaultPlacements ||
        !defaultPlacements.length) {
      return;
    }
    let defaultWidgetIndex = -1;

    for (let widgetId of futurePlacedWidgets) {
      let widget = gPalette.get(widgetId);
      if (!widget || widget.source !== CustomizableUI.SOURCE_BUILTIN ||
          !widget.defaultArea || !widget._introducedInVersion ||
          savedPlacements.includes(widget.id)) {
        continue;
      }
      defaultWidgetIndex = defaultPlacements.indexOf(widget.id);
      if (defaultWidgetIndex === -1) {
        continue;
      }
      // Now we know that this widget should be here by default, was newly introduced,
      // and we have a saved state to insert into, and a default state to work off of.
      // Try introducing after widgets that come before it in the default placements:
      for (let i = defaultWidgetIndex; i >= 0; i--) {
        // Special case: if the defaults list this widget as coming first, insert at the beginning:
        if (i === 0 && i === defaultWidgetIndex) {
          savedPlacements.splice(0, 0, widget.id);
          // Before you ask, yes, deleting things inside a let x of y loop where y is a Set is
          // safe, and we won't skip any items.
          futurePlacedWidgets.delete(widget.id);
          gDirty = true;
          break;
        }
        // Otherwise, if we're somewhere other than the beginning, check if the previous
        // widget is in the saved placements.
        if (i) {
          let previousWidget = defaultPlacements[i - 1];
          let previousWidgetIndex = savedPlacements.indexOf(previousWidget);
          if (previousWidgetIndex != -1) {
            savedPlacements.splice(previousWidgetIndex + 1, 0, widget.id);
            futurePlacedWidgets.delete(widget.id);
            gDirty = true;
            break;
          }
        }
      }
      // The loop above either inserts the item or doesn't - either way, we can get away
      // with doing nothing else now; if the item remains in gFuturePlacements, we'll
      // add it at the end in restoreStateForArea.
    }
    this.saveState();
  },

  wrapWidget(aWidgetId) {
    if (gGroupWrapperCache.has(aWidgetId)) {
      return gGroupWrapperCache.get(aWidgetId);
    }

    let provider = this.getWidgetProvider(aWidgetId);
    if (!provider) {
      return null;
    }

    if (provider == CustomizableUI.PROVIDER_API) {
      let widget = gPalette.get(aWidgetId);
      if (!widget.wrapper) {
        widget.wrapper = new WidgetGroupWrapper(widget);
        gGroupWrapperCache.set(aWidgetId, widget.wrapper);
      }
      return widget.wrapper;
    }

    // PROVIDER_SPECIAL gets treated the same as PROVIDER_XUL.
    // XXXgijs: this causes bugs in code that depends on widgetWrapper.provider
    // giving an accurate answer... filed as bug 1379821
    let wrapper = new XULWidgetGroupWrapper(aWidgetId);
    gGroupWrapperCache.set(aWidgetId, wrapper);
    return wrapper;
  },

  registerArea(aName, aProperties, aInternalCaller) {
    if (typeof aName != "string" || !/^[a-z0-9-_]{1,}$/i.test(aName)) {
      throw new Error("Invalid area name");
    }

    let areaIsKnown = gAreas.has(aName);
    let props = areaIsKnown ? gAreas.get(aName) : new Map();
    const kImmutableProperties = new Set(["type", "legacy", "overflowable"]);
    for (let key in aProperties) {
      if (areaIsKnown && kImmutableProperties.has(key) &&
          props.get(key) != aProperties[key]) {
        throw new Error("An area cannot change the property for '" + key + "'");
      }
      props.set(key, aProperties[key]);
    }
    // Default to a toolbar:
    if (!props.has("type")) {
      props.set("type", CustomizableUI.TYPE_TOOLBAR);
    }
    if (props.get("type") == CustomizableUI.TYPE_TOOLBAR) {
      // Check aProperties instead of props because this check is only interested
      // in the passed arguments, not the state of a potentially pre-existing area.
      if (!aInternalCaller && aProperties.defaultCollapsed) {
        throw new Error("defaultCollapsed is only allowed for default toolbars.");
      }
      if (!props.has("defaultCollapsed")) {
        props.set("defaultCollapsed", true);
      }
    } else if (props.has("defaultCollapsed")) {
      throw new Error("defaultCollapsed only applies for TYPE_TOOLBAR areas.");
    }
    // Sanity check type:
    let allTypes = [CustomizableUI.TYPE_TOOLBAR, CustomizableUI.TYPE_MENU_PANEL];
    if (!allTypes.includes(props.get("type"))) {
      throw new Error("Invalid area type " + props.get("type"));
    }

    // And to no placements:
    if (!props.has("defaultPlacements")) {
      props.set("defaultPlacements", []);
    }
    // Sanity check default placements array:
    if (!Array.isArray(props.get("defaultPlacements"))) {
      throw new Error("Should provide an array of default placements");
    }

    if (!areaIsKnown) {
      gAreas.set(aName, props);

      // Reconcile new default widgets. Have to do this before we start restoring things.
      this._placeNewDefaultWidgetsInArea(aName);

      if (props.get("legacy") && !gPlacements.has(aName)) {
        // Guarantee this area exists in gFuturePlacements, to avoid checking it in
        // various places elsewhere.
        if (!gFuturePlacements.has(aName)) {
          gFuturePlacements.set(aName, new Set());
        }
      } else {
        this.restoreStateForArea(aName);
      }

      // If we have pending build area nodes, register all of them
      if (gPendingBuildAreas.has(aName)) {
        let pendingNodes = gPendingBuildAreas.get(aName);
        for (let [pendingNode, existingChildren] of pendingNodes) {
          this.registerToolbarNode(pendingNode, existingChildren);
        }
        gPendingBuildAreas.delete(aName);
      }
    }
  },

  unregisterArea(aName, aDestroyPlacements) {
    if (typeof aName != "string" || !/^[a-z0-9-_]{1,}$/i.test(aName)) {
      throw new Error("Invalid area name");
    }
    if (!gAreas.has(aName) && !gPlacements.has(aName)) {
      throw new Error("Area not registered");
    }

    // Move all the widgets out
    this.beginBatchUpdate();
    try {
      let placements = gPlacements.get(aName);
      if (placements) {
        // Need to clone this array so removeWidgetFromArea doesn't modify it
        placements = [...placements];
        placements.forEach(this.removeWidgetFromArea, this);
      }

      // Delete all remaining traces.
      gAreas.delete(aName);
      // Only destroy placements when necessary:
      if (aDestroyPlacements) {
        gPlacements.delete(aName);
      } else {
        // Otherwise we need to re-set them, as removeFromArea will have emptied
        // them out:
        gPlacements.set(aName, placements);
      }
      gFuturePlacements.delete(aName);
      let existingAreaNodes = gBuildAreas.get(aName);
      if (existingAreaNodes) {
        for (let areaNode of existingAreaNodes) {
          this.notifyListeners("onAreaNodeUnregistered", aName, areaNode.customizationTarget,
                               CustomizableUI.REASON_AREA_UNREGISTERED);
        }
      }
      gBuildAreas.delete(aName);
    } finally {
      this.endBatchUpdate(true);
    }
  },

  registerToolbarNode(aToolbar, aExistingChildren) {
    let area = aToolbar.id;
    if (gBuildAreas.has(area) && gBuildAreas.get(area).has(aToolbar)) {
      return;
    }
    let areaProperties = gAreas.get(area);

    // If this area is not registered, try to do it automatically:
    if (!areaProperties) {
      // If there's no default set attribute at all, we assume that we should
      // wait for registerArea to be called:
      if (!aToolbar.hasAttribute("defaultset")) {
        if (!gPendingBuildAreas.has(area)) {
          gPendingBuildAreas.set(area, new Map());
        }
        let pendingNodes = gPendingBuildAreas.get(area);
        pendingNodes.set(aToolbar, aExistingChildren);
        return;
      }
      let props = {type: CustomizableUI.TYPE_TOOLBAR, legacy: true};
      let defaultsetAttribute = aToolbar.getAttribute("defaultset") || "";
      props.defaultPlacements = defaultsetAttribute.split(",").filter(s => s);
      this.registerArea(area, props);
      areaProperties = gAreas.get(area);
    }

    this.beginBatchUpdate();
    try {
      let placements = gPlacements.get(area);
      if (!placements && areaProperties.has("legacy")) {
        let legacyState = aToolbar.getAttribute("currentset");
        if (legacyState) {
          legacyState = legacyState.split(",").filter(s => s);
        }

        // Manually restore the state here, so the legacy state can be converted.
        this.restoreStateForArea(area, legacyState);
        placements = gPlacements.get(area);
      }

      // Check that the current children and the current placements match. If
      // not, mark it as dirty:
      if (aExistingChildren.length != placements.length ||
          aExistingChildren.every((id, i) => id == placements[i])) {
        gDirtyAreaCache.add(area);
      }

      if (areaProperties.has("overflowable")) {
        aToolbar.overflowable = new OverflowableToolbar(aToolbar);
      }

      this.registerBuildArea(area, aToolbar);

      // We only build the toolbar if it's been marked as "dirty". Dirty means
      // one of the following things:
      // 1) Items have been added, moved or removed from this toolbar before.
      // 2) The number of children of the toolbar does not match the length of
      //    the placements array for that area.
      //
      // This notion of being "dirty" is stored in a cache which is persisted
      // in the saved state.
      if (gDirtyAreaCache.has(area)) {
        this.buildArea(area, placements, aToolbar);
      }
      this.notifyListeners("onAreaNodeRegistered", area, aToolbar.customizationTarget);
      aToolbar.setAttribute("currentset", placements.join(","));
    } finally {
      this.endBatchUpdate();
    }
  },

  buildArea(aArea, aPlacements, aAreaNode) {
    let document = aAreaNode.ownerDocument;
    let window = document.defaultView;
    let inPrivateWindow = PrivateBrowsingUtils.isWindowPrivate(window);
    let container = aAreaNode.customizationTarget;
    let areaIsPanel = gAreas.get(aArea).get("type") == CustomizableUI.TYPE_MENU_PANEL;

    if (!container) {
      throw new Error("Expected area " + aArea
                      + " to have a customizationTarget attribute.");
    }

    // Restore nav-bar visibility since it may have been hidden
    // through a migration path (bug 938980) or an add-on.
    if (aArea == CustomizableUI.AREA_NAVBAR) {
      aAreaNode.collapsed = false;
    }

    this.beginBatchUpdate();

    try {
      let currentNode = container.firstChild;
      let placementsToRemove = new Set();
      for (let id of aPlacements) {
        while (currentNode && currentNode.getAttribute("skipintoolbarset") == "true") {
          currentNode = currentNode.nextSibling;
        }

        // Fix ids for specials and continue, for correctly placed specials.
        if (currentNode && (!currentNode.id || CustomizableUI.isSpecialWidget(currentNode)) &&
            this.matchingSpecials(id, currentNode)) {
          currentNode.id = id;
        }
        if (currentNode && currentNode.id == id) {
          currentNode = currentNode.nextSibling;
          continue;
        }

        if (this.isSpecialWidget(id) && areaIsPanel) {
          placementsToRemove.add(id);
          continue;
        }

        let [provider, node] = this.getWidgetNode(id, window);
        if (!node) {
          log.debug("Unknown widget: " + id);
          continue;
        }

        let widget = null;
        // If the placements have items in them which are (now) no longer removable,
        // we shouldn't be moving them:
        if (provider == CustomizableUI.PROVIDER_API) {
          widget = gPalette.get(id);
          if (!widget.removable && aArea != widget.defaultArea) {
            placementsToRemove.add(id);
            continue;
          }
        } else if (provider == CustomizableUI.PROVIDER_XUL &&
                   node.parentNode != container && !this.isWidgetRemovable(node)) {
          placementsToRemove.add(id);
          continue;
        } // Special widgets are always removable, so no need to check them

        if (inPrivateWindow && widget && !widget.showInPrivateBrowsing) {
          continue;
        }

        this.ensureButtonContextMenu(node, aAreaNode);

        // This needs updating in case we're resetting / undoing a reset.
        if (widget) {
          widget.currentArea = aArea;
        }
        this.insertWidgetBefore(node, currentNode, container, aArea);
        if (gResetting) {
          this.notifyListeners("onWidgetReset", node, container);
        } else if (gUndoResetting) {
          this.notifyListeners("onWidgetUndoMove", node, container);
        }
      }

      if (currentNode) {
        let palette = aAreaNode.toolbox ? aAreaNode.toolbox.palette : null;
        let limit = currentNode.previousSibling;
        let node = container.lastChild;
        while (node && node != limit) {
          let previousSibling = node.previousSibling;
          // Nodes opt-in to removability. If they're removable, and we haven't
          // seen them in the placements array, then we toss them into the palette
          // if one exists. If no palette exists, we just remove the node. If the
          // node is not removable, we leave it where it is. However, we can only
          // safely touch elements that have an ID - both because we depend on
          // IDs (or are specials), and because such elements are not intended to
          // be widgets (eg, titlebar-placeholder elements).
          if ((node.id || this.isSpecialWidget(node)) &&
              node.getAttribute("skipintoolbarset") != "true") {
            if (this.isWidgetRemovable(node)) {
              if (palette && !this.isSpecialWidget(node.id)) {
                palette.appendChild(node);
                this.removeLocationAttributes(node);
              } else {
                container.removeChild(node);
              }
            } else {
              node.setAttribute("removable", false);
              log.debug("Adding non-removable widget to placements of " + aArea + ": " +
                        node.id);
              gPlacements.get(aArea).push(node.id);
              gDirty = true;
            }
          }
          node = previousSibling;
        }
      }

      // If there are placements in here which aren't removable from their original area,
      // we remove them from this area's placement array. They will (have) be(en) added
      // to their original area's placements array in the block above this one.
      if (placementsToRemove.size) {
        let placementAry = gPlacements.get(aArea);
        for (let id of placementsToRemove) {
          let index = placementAry.indexOf(id);
          placementAry.splice(index, 1);
        }
      }

      if (gResetting) {
        this.notifyListeners("onAreaReset", aArea, container);
      }
    } finally {
      this.endBatchUpdate();
    }
  },

  addPanelCloseListeners(aPanel) {
    gELS.addSystemEventListener(aPanel, "click", this, false);
    gELS.addSystemEventListener(aPanel, "keypress", this, false);
    let win = aPanel.ownerGlobal;
    if (!gPanelsForWindow.has(win)) {
      gPanelsForWindow.set(win, new Set());
    }
    gPanelsForWindow.get(win).add(this._getPanelForNode(aPanel));
  },

  removePanelCloseListeners(aPanel) {
    gELS.removeSystemEventListener(aPanel, "click", this, false);
    gELS.removeSystemEventListener(aPanel, "keypress", this, false);
    let win = aPanel.ownerGlobal;
    let panels = gPanelsForWindow.get(win);
    if (panels) {
      panels.delete(this._getPanelForNode(aPanel));
    }
  },

  ensureButtonContextMenu(aNode, aAreaNode, forcePanel) {
    const kPanelItemContextMenu = "customizationPanelItemContextMenu";

    let currentContextMenu = aNode.getAttribute("context") ||
                             aNode.getAttribute("contextmenu");
    let contextMenuForPlace =
      (forcePanel || "menu-panel" == CustomizableUI.getPlaceForItem(aAreaNode)) ?
      kPanelItemContextMenu :
      null;
    if (contextMenuForPlace && !currentContextMenu) {
      aNode.setAttribute("context", contextMenuForPlace);
    } else if (currentContextMenu == kPanelItemContextMenu &&
               contextMenuForPlace != kPanelItemContextMenu) {
      aNode.removeAttribute("context");
      aNode.removeAttribute("contextmenu");
    }
  },

  getWidgetProvider(aWidgetId) {
    if (this.isSpecialWidget(aWidgetId)) {
      return CustomizableUI.PROVIDER_SPECIAL;
    }
    if (gPalette.has(aWidgetId)) {
      return CustomizableUI.PROVIDER_API;
    }
    // If this was an API widget that was destroyed, return null:
    if (gSeenWidgets.has(aWidgetId)) {
      return null;
    }

    // We fall back to the XUL provider, but we don't know for sure (at this
    // point) whether it exists there either. So the API is technically lying.
    // Ideally, it would be able to return an error value (or throw an
    // exception) if it really didn't exist. Our code calling this function
    // handles that fine, but this is a public API.
    return CustomizableUI.PROVIDER_XUL;
  },

  getWidgetNode(aWidgetId, aWindow) {
    let document = aWindow.document;

    if (this.isSpecialWidget(aWidgetId)) {
      let widgetNode = document.getElementById(aWidgetId) ||
                       this.createSpecialWidget(aWidgetId, document);
      return [ CustomizableUI.PROVIDER_SPECIAL, widgetNode];
    }

    let widget = gPalette.get(aWidgetId);
    if (widget) {
      // If we have an instance of this widget already, just use that.
      if (widget.instances.has(document)) {
        log.debug("An instance of widget " + aWidgetId + " already exists in this "
                  + "document. Reusing.");
        return [ CustomizableUI.PROVIDER_API,
                 widget.instances.get(document) ];
      }

      return [ CustomizableUI.PROVIDER_API,
               this.buildWidget(document, widget) ];
    }

    log.debug("Searching for " + aWidgetId + " in toolbox.");
    let node = this.findWidgetInWindow(aWidgetId, aWindow);
    if (node) {
      return [ CustomizableUI.PROVIDER_XUL, node ];
    }

    log.debug("No node for " + aWidgetId + " found.");
    return [null, null];
  },

  registerMenuPanel(aPanelContents, aArea) {
    if (gBuildAreas.has(aArea) && gBuildAreas.get(aArea).has(aPanelContents)) {
      return;
    }

    let document = aPanelContents.ownerDocument;

    aPanelContents.toolbox = document.getElementById("navigator-toolbox");
    aPanelContents.customizationTarget = aPanelContents;

    this.addPanelCloseListeners(this._getPanelForNode(aPanelContents));

    let placements = gPlacements.get(aArea);
    this.buildArea(aArea, placements, aPanelContents);
    this.notifyListeners("onAreaNodeRegistered", aArea, aPanelContents);

    for (let child of aPanelContents.children) {
      if (child.localName != "toolbarbutton") {
        if (child.localName == "toolbaritem") {
          this.ensureButtonContextMenu(child, aPanelContents, true);
        }
        continue;
      }
      this.ensureButtonContextMenu(child, aPanelContents, true);
    }

    this.registerBuildArea(aArea, aPanelContents);
  },

  onWidgetAdded(aWidgetId, aArea, aPosition) {
    this.insertNode(aWidgetId, aArea, aPosition, true);

    if (!gResetting) {
      this._clearPreviousUIState();
    }
  },

  onWidgetRemoved(aWidgetId, aArea) {
    let areaNodes = gBuildAreas.get(aArea);
    if (!areaNodes) {
      return;
    }

    let area = gAreas.get(aArea);
    let isToolbar = area.get("type") == CustomizableUI.TYPE_TOOLBAR;
    let isOverflowable = isToolbar && area.get("overflowable");
    let showInPrivateBrowsing = gPalette.has(aWidgetId)
                              ? gPalette.get(aWidgetId).showInPrivateBrowsing
                              : true;

    for (let areaNode of areaNodes) {
      let window = areaNode.ownerGlobal;
      if (!showInPrivateBrowsing &&
          PrivateBrowsingUtils.isWindowPrivate(window)) {
        continue;
      }

      let container = areaNode.customizationTarget;
      let widgetNode = window.document.getElementById(aWidgetId);
      if (widgetNode && isOverflowable) {
        container = areaNode.overflowable.getContainerFor(widgetNode);
      }

      if (!widgetNode || !container.contains(widgetNode)) {
        log.info("Widget " + aWidgetId + " not found, unable to remove from " + aArea);
        continue;
      }

      this.notifyListeners("onWidgetBeforeDOMChange", widgetNode, null, container, true);

      // We remove location attributes here to make sure they're gone too when a
      // widget is removed from a toolbar to the palette. See bug 930950.
      this.removeLocationAttributes(widgetNode);
      // We also need to remove the panel context menu if it's there:
      this.ensureButtonContextMenu(widgetNode);
      if (gPalette.has(aWidgetId) || this.isSpecialWidget(aWidgetId)) {
        container.removeChild(widgetNode);
      } else {
        areaNode.toolbox.palette.appendChild(widgetNode);
      }
      this.notifyListeners("onWidgetAfterDOMChange", widgetNode, null, container, true);

      if (isToolbar) {
        areaNode.setAttribute("currentset", gPlacements.get(aArea).join(","));
      }

      let windowCache = gSingleWrapperCache.get(window);
      if (windowCache) {
        windowCache.delete(aWidgetId);
      }
    }
    if (!gResetting) {
      this._clearPreviousUIState();
    }
  },

  onWidgetMoved(aWidgetId, aArea, aOldPosition, aNewPosition) {
    this.insertNode(aWidgetId, aArea, aNewPosition);
    if (!gResetting) {
      this._clearPreviousUIState();
    }
  },

  onCustomizeEnd(aWindow) {
    this._clearPreviousUIState();
  },

  registerBuildArea(aArea, aNode) {
    // We ensure that the window is registered to have its customization data
    // cleaned up when unloading.
    let window = aNode.ownerGlobal;
    if (window.closed) {
      return;
    }
    this.registerBuildWindow(window);

    // Also register this build area's toolbox.
    if (aNode.toolbox) {
      gBuildWindows.get(window).add(aNode.toolbox);
    }

    if (!gBuildAreas.has(aArea)) {
      gBuildAreas.set(aArea, new Set());
    }

    gBuildAreas.get(aArea).add(aNode);

    // Give a class to all customize targets to be used for styling in Customize Mode
    let customizableNode = this.getCustomizeTargetForArea(aArea, window);
    customizableNode.classList.add("customization-target");
  },

  registerBuildWindow(aWindow) {
    if (!gBuildWindows.has(aWindow)) {
      gBuildWindows.set(aWindow, new Set());

      aWindow.addEventListener("unload", this);
      aWindow.addEventListener("command", this, true);

      this.notifyListeners("onWindowOpened", aWindow);
    }
  },

  unregisterBuildWindow(aWindow) {
    aWindow.removeEventListener("unload", this);
    aWindow.removeEventListener("command", this, true);
    gPanelsForWindow.delete(aWindow);
    gBuildWindows.delete(aWindow);
    gSingleWrapperCache.delete(aWindow);
    let document = aWindow.document;

    for (let [areaId, areaNodes] of gBuildAreas) {
      let areaProperties = gAreas.get(areaId);
      for (let node of areaNodes) {
        if (node.ownerDocument == document) {
          this.notifyListeners("onAreaNodeUnregistered", areaId, node.customizationTarget,
                               CustomizableUI.REASON_WINDOW_CLOSED);
          if (areaProperties.has("overflowable")) {
            node.overflowable.uninit();
            node.overflowable = null;
          }
          areaNodes.delete(node);
        }
      }
    }

    for (let [, widget] of gPalette) {
      widget.instances.delete(document);
      this.notifyListeners("onWidgetInstanceRemoved", widget.id, document);
    }

    for (let [, areaMap] of gPendingBuildAreas) {
      let toDelete = [];
      for (let [areaNode, ] of areaMap) {
        if (areaNode.ownerDocument == document) {
          toDelete.push(areaNode);
        }
      }
      for (let areaNode of toDelete) {
        areaMap.delete(areaNode);
      }
    }

    this.notifyListeners("onWindowClosed", aWindow);
  },

  setLocationAttributes(aNode, aArea) {
    let props = gAreas.get(aArea);
    if (!props) {
      throw new Error("Expected area " + aArea + " to have a properties Map " +
                      "associated with it.");
    }

    aNode.setAttribute("cui-areatype", props.get("type") || "");
    let anchor = props.get("anchor");
    if (anchor) {
      aNode.setAttribute("cui-anchorid", anchor);
    } else {
      aNode.removeAttribute("cui-anchorid");
    }
  },

  removeLocationAttributes(aNode) {
    aNode.removeAttribute("cui-areatype");
    aNode.removeAttribute("cui-anchorid");
  },

  insertNode(aWidgetId, aArea, aPosition, isNew) {
    let areaNodes = gBuildAreas.get(aArea);
    if (!areaNodes) {
      return;
    }

    let placements = gPlacements.get(aArea);
    if (!placements) {
      log.error("Could not find any placements for " + aArea +
                " when moving a widget.");
      return;
    }

    // Go through each of the nodes associated with this area and move the
    // widget to the requested location.
    for (let areaNode of areaNodes) {
      this.insertNodeInWindow(aWidgetId, areaNode, isNew);
    }
  },

  insertNodeInWindow(aWidgetId, aAreaNode, isNew) {
    let window = aAreaNode.ownerGlobal;
    let showInPrivateBrowsing = gPalette.has(aWidgetId)
                              ? gPalette.get(aWidgetId).showInPrivateBrowsing
                              : true;

    if (!showInPrivateBrowsing && PrivateBrowsingUtils.isWindowPrivate(window)) {
      return;
    }

    let [, widgetNode] = this.getWidgetNode(aWidgetId, window);
    if (!widgetNode) {
      log.error("Widget '" + aWidgetId + "' not found, unable to move");
      return;
    }

    let areaId = aAreaNode.id;
    if (isNew) {
      this.ensureButtonContextMenu(widgetNode, aAreaNode);
    }

    let [insertionContainer, nextNode] = this.findInsertionPoints(widgetNode, aAreaNode);
    this.insertWidgetBefore(widgetNode, nextNode, insertionContainer, areaId);

    if (gAreas.get(areaId).get("type") == CustomizableUI.TYPE_TOOLBAR) {
      aAreaNode.setAttribute("currentset", gPlacements.get(areaId).join(","));
    }
  },

  findInsertionPoints(aNode, aAreaNode) {
    let areaId = aAreaNode.id;
    let props = gAreas.get(areaId);

    // For overflowable toolbars, rely on them (because the work is more complicated):
    if (props.get("type") == CustomizableUI.TYPE_TOOLBAR && props.get("overflowable")) {
      return aAreaNode.overflowable.findOverflowedInsertionPoints(aNode);
    }

    let container = aAreaNode.customizationTarget;
    let placements = gPlacements.get(areaId);
    let nodeIndex = placements.indexOf(aNode.id);

    while (++nodeIndex < placements.length) {
      let nextNodeId = placements[nodeIndex];
      let nextNode = aNode.ownerDocument.getElementById(nextNodeId);
      // If the next placed widget exists, and is a direct child of the
      // container, or wrapped in a customize mode wrapper (toolbarpaletteitem)
      // inside the container, insert beside it.
      // We have to check the parent to avoid errors when the placement ids
      // are for nodes that are no longer customizable.
      if (nextNode && (nextNode.parentNode == container ||
                       (nextNode.parentNode.localName == "toolbarpaletteitem" &&
                        nextNode.parentNode.parentNode == container))) {
        return [container, nextNode];
      }
    }

    return [container, null];
  },

  insertWidgetBefore(aNode, aNextNode, aContainer, aArea) {
    this.notifyListeners("onWidgetBeforeDOMChange", aNode, aNextNode, aContainer);
    this.setLocationAttributes(aNode, aArea);
    aContainer.insertBefore(aNode, aNextNode);
    this.notifyListeners("onWidgetAfterDOMChange", aNode, aNextNode, aContainer);
  },

  handleEvent(aEvent) {
    switch (aEvent.type) {
      case "command":
        if (!this._originalEventInPanel(aEvent)) {
          break;
        }
        aEvent = aEvent.sourceEvent;
        // Fall through
      case "click":
      case "keypress":
        this.maybeAutoHidePanel(aEvent);
        break;
      case "unload":
        this.unregisterBuildWindow(aEvent.currentTarget);
        break;
    }
  },

  _originalEventInPanel(aEvent) {
    let e = aEvent.sourceEvent;
    if (!e) {
      return false;
    }
    let node = this._getPanelForNode(e.target);
    if (!node) {
      return false;
    }
    let win = e.view;
    let panels = gPanelsForWindow.get(win);
    return !!panels && panels.has(node);
  },

  _getSpecialIdForNode(aNode) {
    if (typeof aNode == "object" && aNode.localName) {
      if (aNode.id) {
        return aNode.id;
      }
      if (aNode.localName.startsWith("toolbar")) {
        return aNode.localName.substring(7);
      }
      return "";
    }
    return aNode;
  },

  isSpecialWidget(aId) {
    aId = this._getSpecialIdForNode(aId);
    return (aId.startsWith(kSpecialWidgetPfx) ||
            aId.startsWith("separator") ||
            aId.startsWith("spring") ||
            aId.startsWith("spacer"));
  },

  matchingSpecials(aId1, aId2) {
    aId1 = this._getSpecialIdForNode(aId1);
    aId2 = this._getSpecialIdForNode(aId2);

    return this.isSpecialWidget(aId1) &&
           this.isSpecialWidget(aId2) &&
           aId1.match(/spring|spacer|separator/)[0] == aId2.match(/spring|spacer|separator/)[0];
  },

  ensureSpecialWidgetId(aId) {
    let nodeType = aId.match(/spring|spacer|separator/)[0];
    // If the ID we were passed isn't a generated one, generate one now:
    if (nodeType == aId) {
      // Ids are differentiated through a unique count suffix.
      return kSpecialWidgetPfx + aId + (++gNewElementCount);
    }
    return aId;
  },

  createSpecialWidget(aId, aDocument) {
    let nodeName = "toolbar" + aId.match(/spring|spacer|separator/)[0];
    let node = aDocument.createElementNS(kNSXUL, nodeName);
    node.className = "chromeclass-toolbar-additional";
    node.id = this.ensureSpecialWidgetId(aId);
    return node;
  },

  /* Find a XUL-provided widget in a window. Don't try to use this
   * for an API-provided widget or a special widget.
   */
  findWidgetInWindow(aId, aWindow) {
    if (!gBuildWindows.has(aWindow)) {
      throw new Error("Build window not registered");
    }

    if (!aId) {
      log.error("findWidgetInWindow was passed an empty string.");
      return null;
    }

    let document = aWindow.document;

    // look for a node with the same id, as the node may be
    // in a different toolbar.
    let node = document.getElementById(aId);
    if (node) {
      let parent = node.parentNode;
      while (parent && !(parent.customizationTarget ||
                         parent == aWindow.gNavToolbox.palette)) {
        parent = parent.parentNode;
      }

      if (parent) {
        let nodeInArea = node.parentNode.localName == "toolbarpaletteitem" ?
                         node.parentNode : node;
        // Check if we're in a customization target, or in the palette:
        if ((parent.customizationTarget == nodeInArea.parentNode &&
             gBuildWindows.get(aWindow).has(parent.toolbox)) ||
            aWindow.gNavToolbox.palette == nodeInArea.parentNode) {
          // Normalize the removable attribute. For backwards compat, if
          // the widget is not located in a toolbox palette then absence
          // of the "removable" attribute means it is not removable.
          if (!node.hasAttribute("removable")) {
            // If we first see this in customization mode, it may be in the
            // customization palette instead of the toolbox palette.
            node.setAttribute("removable", !parent.customizationTarget);
          }
          return node;
        }
      }
    }

    let toolboxes = gBuildWindows.get(aWindow);
    for (let toolbox of toolboxes) {
      if (toolbox.palette) {
        // Attempt to locate an element with a matching ID within
        // the palette.
        let element = toolbox.palette.getElementsByAttribute("id", aId)[0];
        if (element) {
          // Normalize the removable attribute. For backwards compat, this
          // is optional if the widget is located in the toolbox palette,
          // and defaults to *true*, unlike if it was located elsewhere.
          if (!element.hasAttribute("removable")) {
            element.setAttribute("removable", true);
          }
          return element;
        }
      }
    }
    return null;
  },

  buildWidget(aDocument, aWidget) {
    if (aDocument.documentURI != kExpectedWindowURL) {
      throw new Error("buildWidget was called for a non-browser window!");
    }
    if (typeof aWidget == "string") {
      aWidget = gPalette.get(aWidget);
    }
    if (!aWidget) {
      throw new Error("buildWidget was passed a non-widget to build.");
    }

    log.debug("Building " + aWidget.id + " of type " + aWidget.type);

    let node;
    if (aWidget.type == "custom") {
      if (aWidget.onBuild) {
        node = aWidget.onBuild(aDocument);
      }
      if (!node || !(node instanceof aDocument.defaultView.XULElement))
        log.error("Custom widget with id " + aWidget.id + " does not return a valid node");
    } else {
      if (aWidget.onBeforeCreated) {
        aWidget.onBeforeCreated(aDocument);
      }
      node = aDocument.createElementNS(kNSXUL, "toolbarbutton");

      node.setAttribute("id", aWidget.id);
      node.setAttribute("widget-id", aWidget.id);
      node.setAttribute("widget-type", aWidget.type);
      if (aWidget.disabled) {
        node.setAttribute("disabled", true);
      }
      node.setAttribute("removable", aWidget.removable);
      node.setAttribute("overflows", aWidget.overflows);
      if (aWidget.tabSpecific) {
        node.setAttribute("tabspecific", aWidget.tabSpecific);
      }
      node.setAttribute("label", this.getLocalizedProperty(aWidget, "label"));
      let additionalTooltipArguments = [];
      if (aWidget.shortcutId) {
        let keyEl = aDocument.getElementById(aWidget.shortcutId);
        if (keyEl) {
          additionalTooltipArguments.push(ShortcutUtils.prettifyShortcut(keyEl));
        } else {
          log.error("Key element with id '" + aWidget.shortcutId + "' for widget '" + aWidget.id +
                    "' not found!");
        }
      }

      let tooltip = this.getLocalizedProperty(aWidget, "tooltiptext", additionalTooltipArguments);
      if (tooltip) {
        node.setAttribute("tooltiptext", tooltip);
      }

      let commandHandler = this.handleWidgetCommand.bind(this, aWidget, node);
      node.addEventListener("command", commandHandler);
      let clickHandler = this.handleWidgetClick.bind(this, aWidget, node);
      node.addEventListener("click", clickHandler);

      let nodeClasses = ["toolbarbutton-1", "chromeclass-toolbar-additional"];

      // If the widget has a view, and has view showing / hiding listeners,
      // hook those up to this widget.
      if (aWidget.type == "view") {
        log.debug("Widget " + aWidget.id + " has a view. Auto-registering event handlers.");
        let viewNode = aDocument.getElementById(aWidget.viewId);

        if (viewNode) {
          // PanelUI relies on the .PanelUI-subView class to be able to show only
          // one sub-view at a time.
          viewNode.classList.add("PanelUI-subView");
          if (aWidget.source == CustomizableUI.SOURCE_BUILTIN) {
            nodeClasses.push("subviewbutton-nav");
          }
          this.ensureSubviewListeners(viewNode);
        } else {
          log.error("Could not find the view node with id: " + aWidget.viewId +
                    ", for widget: " + aWidget.id + ".");
        }
      }
      node.setAttribute("class", nodeClasses.join(" "));

      if (aWidget.onCreated) {
        aWidget.onCreated(node);
      }
    }

    aWidget.instances.set(aDocument, node);
    return node;
  },

  ensureSubviewListeners(viewNode) {
    if (viewNode._addedEventListeners) {
      return;
    }
    let viewId = viewNode.id;
    let widget = [...gPalette.values()].find(w => w.viewId == viewId);
    if (!widget) {
      return;
    }
    for (let eventName of kSubviewEvents) {
      let handler = "on" + eventName;
      if (typeof widget[handler] == "function") {
        viewNode.addEventListener(eventName, widget[handler]);
      }
    }
    viewNode._addedEventListeners = true;
    log.debug("Widget " + widget.id + " showing and hiding event handlers set.");
  },

  getLocalizedProperty(aWidget, aProp, aFormatArgs, aDef) {
    const kReqStringProps = ["label"];

    if (typeof aWidget == "string") {
      aWidget = gPalette.get(aWidget);
    }
    if (!aWidget) {
      throw new Error("getLocalizedProperty was passed a non-widget to work with.");
    }
    let def, name;
    // Let widgets pass their own string identifiers or strings, so that
    // we can use strings which aren't the default (in case string ids change)
    // and so that non-builtin-widgets can also provide labels, tooltips, etc.
    if (aWidget[aProp] != null) {
      name = aWidget[aProp];
      // By using this as the default, if a widget provides a full string rather
      // than a string ID for localization, we will fall back to that string
      // and return that.
      def = aDef || name;
    } else {
      name = aWidget.id + "." + aProp;
      def = aDef || "";
    }
    if (aWidget.localized === false) {
      return def;
    }
    try {
      if (Array.isArray(aFormatArgs) && aFormatArgs.length) {
        return gWidgetsBundle.formatStringFromName(name, aFormatArgs,
          aFormatArgs.length) || def;
      }
      return gWidgetsBundle.GetStringFromName(name) || def;
    } catch (ex) {
      // If an empty string was explicitly passed, treat it as an actual
      // value rather than a missing property.
      if (!def && (name != "" || kReqStringProps.includes(aProp))) {
        log.error("Could not localize property '" + name + "'.");
      }
    }
    return def;
  },

  addShortcut(aShortcutNode, aTargetNode = aShortcutNode) {
    // Detect if we've already been here before.
    if (aTargetNode.hasAttribute("shortcut"))
      return;

    let document = aShortcutNode.ownerDocument;
    let shortcutId = aShortcutNode.getAttribute("key");
    let shortcut;
    if (shortcutId) {
      shortcut = document.getElementById(shortcutId);
    } else {
      let commandId = aShortcutNode.getAttribute("command");
      if (commandId)
        shortcut = ShortcutUtils.findShortcut(document.getElementById(commandId));
    }
    if (!shortcut) {
      return;
    }

    aTargetNode.setAttribute("shortcut", ShortcutUtils.prettifyShortcut(shortcut));
  },

  handleWidgetCommand(aWidget, aNode, aEvent) {
    log.debug("handleWidgetCommand");

    if (aWidget.onBeforeCommand) {
      try {
        aWidget.onBeforeCommand.call(null, aEvent);
      } catch (e) {
        log.error(e);
      }
    }

    if (aWidget.type == "button") {
      if (aWidget.onCommand) {
        try {
          aWidget.onCommand.call(null, aEvent);
        } catch (e) {
          log.error(e);
        }
      } else {
        // XXXunf Need to think this through more, and formalize.
        Services.obs.notifyObservers(aNode,
                                     "customizedui-widget-command",
                                     aWidget.id);
      }
    } else if (aWidget.type == "view") {
      let ownerWindow = aNode.ownerGlobal;
      let area = this.getPlacementOfWidget(aNode.id).area;
      let areaType = CustomizableUI.getAreaType(area);
      let anchor = aNode;
      if (areaType != CustomizableUI.TYPE_MENU_PANEL) {
        let wrapper = this.wrapWidget(aWidget.id).forWindow(ownerWindow);

        let hasMultiView = !!aNode.closest("panelmultiview");
        if (wrapper && !hasMultiView && wrapper.anchor) {
          this.hidePanelForNode(aNode);
          anchor = wrapper.anchor;
        }
      }

      ownerWindow.PanelUI.showSubView(aWidget.viewId, anchor, aEvent);
    }
  },

  handleWidgetClick(aWidget, aNode, aEvent) {
    log.debug("handleWidgetClick");
    if (aWidget.onClick) {
      try {
        aWidget.onClick.call(null, aEvent);
      } catch (e) {
        Cu.reportError(e);
      }
    } else {
      // XXXunf Need to think this through more, and formalize.
      Services.obs.notifyObservers(aNode, "customizedui-widget-click", aWidget.id);
    }
  },

  _getPanelForNode(aNode) {
    return aNode.closest("panel");
  },

  /*
   * If people put things in the panel which need more than single-click interaction,
   * we don't want to close it. Right now we check for text inputs and menu buttons.
   * We also check for being outside of any toolbaritem/toolbarbutton, ie on a blank
   * part of the menu.
   */
  _isOnInteractiveElement(aEvent) {
    function getMenuPopupForDescendant(aNode) {
      let lastPopup = null;
      while (aNode && aNode.parentNode &&
             aNode.parentNode.localName.startsWith("menu")) {
        lastPopup = aNode.localName == "menupopup" ? aNode : lastPopup;
        aNode = aNode.parentNode;
      }
      return lastPopup;
    }

    let target = aEvent.originalTarget;
    let panel = this._getPanelForNode(aEvent.currentTarget);
    // This can happen in e.g. customize mode. If there's no panel,
    // there's clearly nothing for us to close; pretend we're interactive.
    if (!panel) {
      return true;
    }
    // We keep track of:
    // whether we're in an input container (text field)
    let inInput = false;
    // whether we're in a popup/context menu
    let inMenu = false;
    // whether we're in a toolbarbutton/toolbaritem
    let inItem = false;
    // whether the current menuitem has a valid closemenu attribute
    let menuitemCloseMenu = "auto";

    // While keeping track of that, we go from the original target back up,
    // to the panel if we have to. We bail as soon as we find an input,
    // a toolbarbutton/item, or the panel:
    while (true && target) {
      // Skip out of iframes etc:
      if (target.nodeType == target.DOCUMENT_NODE) {
        if (!target.defaultView) {
          // Err, we're done.
          break;
        }
        // Cue some voodoo
        target = target.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
                                   .getInterface(Ci.nsIWebNavigation)
                                   .QueryInterface(Ci.nsIDocShell)
                                   .chromeEventHandler;
        if (!target) {
          break;
        }
      }
      let tagName = target.localName;
      inInput = tagName == "input" || tagName == "textbox";
      inItem = tagName == "toolbaritem" || tagName == "toolbarbutton";
      let isMenuItem = tagName == "menuitem";
      inMenu = inMenu || isMenuItem;

      if (isMenuItem && target.hasAttribute("closemenu")) {
        let closemenuVal = target.getAttribute("closemenu");
        menuitemCloseMenu = (closemenuVal == "single" || closemenuVal == "none") ?
                            closemenuVal : "auto";
      }
      // Break out of the loop immediately for disabled items, as we need to
      // keep the menu open in that case.
      if (target.getAttribute("disabled") == "true") {
        return true;
      }

      // This isn't in the loop condition because we want to break before
      // changing |target| if any of these conditions are true
      if (inInput || inItem || target == panel) {
        break;
      }
      // We need specific code for popups: the item on which they were invoked
      // isn't necessarily in their parentNode chain:
      if (isMenuItem) {
        let topmostMenuPopup = getMenuPopupForDescendant(target);
        target = (topmostMenuPopup && topmostMenuPopup.triggerNode) ||
                 target.parentNode;
      } else {
        target = target.parentNode;
      }
    }

    // If the user clicked a menu item...
    if (inMenu) {
      // We care if we're in an input also,
      // or if the user specified closemenu!="auto":
      if (inInput || menuitemCloseMenu != "auto") {
        return true;
      }
      // Otherwise, we're probably fine to close the panel
      return false;
    }
    // If we're not in a menu, and we *are* in a type="menu" toolbarbutton,
    // we'll now interact with the menu
    if (inItem && target.getAttribute("type") == "menu") {
      return true;
    }
    return inInput || !inItem;
  },

  hidePanelForNode(aNode) {
    let panel = this._getPanelForNode(aNode);
    if (panel) {
      PanelMultiView.hidePopup(panel);
    }
  },

  maybeAutoHidePanel(aEvent) {
    if (aEvent.type == "keypress") {
      if (aEvent.keyCode != aEvent.DOM_VK_RETURN) {
        return;
      }
      // If the user hit enter/return, we don't check preventDefault - it makes sense
      // that this was prevented, but we probably still want to close the panel.
      // If consumers don't want this to happen, they should specify the closemenu
      // attribute.

    } else if (aEvent.type != "command") { // mouse events:
      if (aEvent.defaultPrevented || aEvent.button != 0) {
        return;
      }
      let isInteractive = this._isOnInteractiveElement(aEvent);
      log.debug("maybeAutoHidePanel: interactive ? " + isInteractive);
      if (isInteractive) {
        return;
      }
    }

    // We can't use event.target because we might have passed an anonymous
    // content boundary as well, and so target points to the outer element in
    // that case. Unfortunately, this means we get anonymous child nodes instead
    // of the real ones, so looking for the 'stoooop, don't close me' attributes
    // is more involved.
    let target = aEvent.originalTarget;
    while (target.parentNode && target.localName != "panel") {
      if (target.getAttribute("closemenu") == "none" ||
          target.getAttribute("widget-type") == "view") {
        return;
      }
      target = target.parentNode;
    }

    // If we get here, we can actually hide the popup:
    this.hidePanelForNode(aEvent.target);
  },

  getUnusedWidgets(aWindowPalette) {
    let window = aWindowPalette.ownerGlobal;
    let isWindowPrivate = PrivateBrowsingUtils.isWindowPrivate(window);
    // We use a Set because there can be overlap between the widgets in
    // gPalette and the items in the palette, especially after the first
    // customization, since programmatically generated widgets will remain
    // in the toolbox palette.
    let widgets = new Set();

    // It's possible that some widgets have been defined programmatically and
    // have not been overlayed into the palette. We can find those inside
    // gPalette.
    for (let [id, widget] of gPalette) {
      if (!widget.currentArea) {
        if (widget.showInPrivateBrowsing || !isWindowPrivate) {
          widgets.add(id);
        }
      }
    }

    log.debug("Iterating the actual nodes of the window palette");
    for (let node of aWindowPalette.children) {
      log.debug("In palette children: " + node.id);
      if (node.id && !this.getPlacementOfWidget(node.id)) {
        widgets.add(node.id);
      }
    }

    return [...widgets];
  },

  getPlacementOfWidget(aWidgetId, aOnlyRegistered, aDeadAreas) {
    if (aOnlyRegistered && !this.widgetExists(aWidgetId)) {
      return null;
    }

    for (let [area, placements] of gPlacements) {
      if (!gAreas.has(area) && !aDeadAreas) {
        continue;
      }
      let index = placements.indexOf(aWidgetId);
      if (index != -1) {
        return { area, position: index };
      }
    }

    return null;
  },

  widgetExists(aWidgetId) {
    if (gPalette.has(aWidgetId) || this.isSpecialWidget(aWidgetId)) {
      return true;
    }

    // Destroyed API widgets are in gSeenWidgets, but not in gPalette:
    if (gSeenWidgets.has(aWidgetId)) {
      return false;
    }

    // We're assuming XUL widgets always exist, as it's much harder to check,
    // and checking would be much more error prone.
    return true;
  },

  addWidgetToArea(aWidgetId, aArea, aPosition, aInitialAdd) {
    if (!gAreas.has(aArea)) {
      throw new Error("Unknown customization area: " + aArea);
    }

    // Hack: don't want special widgets in the panel (need to check here as well
    // as in canWidgetMoveToArea because the menu panel is lazy):
    if (gAreas.get(aArea).get("type") == CustomizableUI.TYPE_MENU_PANEL &&
        this.isSpecialWidget(aWidgetId)) {
      return;
    }

    // If this is a lazy area that hasn't been restored yet, we can't yet modify
    // it - would would at least like to add to it. So we keep track of it in
    // gFuturePlacements,  and use that to add it when restoring the area. We
    // throw away aPosition though, as that can only be bogus if the area hasn't
    // yet been restorted (caller can't possibly know where its putting the
    // widget in relation to other widgets).
    if (this.isAreaLazy(aArea)) {
      gFuturePlacements.get(aArea).add(aWidgetId);
      return;
    }

    if (this.isSpecialWidget(aWidgetId)) {
      aWidgetId = this.ensureSpecialWidgetId(aWidgetId);
    }

    let oldPlacement = this.getPlacementOfWidget(aWidgetId, false, true);
    if (oldPlacement && oldPlacement.area == aArea) {
      this.moveWidgetWithinArea(aWidgetId, aPosition);
      return;
    }

    // Do nothing if the widget is not allowed to move to the target area.
    if (!this.canWidgetMoveToArea(aWidgetId, aArea)) {
      return;
    }

    if (oldPlacement) {
      this.removeWidgetFromArea(aWidgetId);
    }

    if (!gPlacements.has(aArea)) {
      gPlacements.set(aArea, [aWidgetId]);
      aPosition = 0;
    } else {
      let placements = gPlacements.get(aArea);
      if (typeof aPosition != "number") {
        aPosition = placements.length;
      }
      if (aPosition < 0) {
        aPosition = 0;
      }
      placements.splice(aPosition, 0, aWidgetId);
    }

    let widget = gPalette.get(aWidgetId);
    if (widget) {
      widget.currentArea = aArea;
      widget.currentPosition = aPosition;
    }

    // We initially set placements with addWidgetToArea, so in that case
    // we don't consider the area "dirtied".
    if (!aInitialAdd) {
      gDirtyAreaCache.add(aArea);
    }

    gDirty = true;
    this.saveState();

    this.notifyListeners("onWidgetAdded", aWidgetId, aArea, aPosition);
  },

  removeWidgetFromArea(aWidgetId) {
    let oldPlacement = this.getPlacementOfWidget(aWidgetId, false, true);
    if (!oldPlacement) {
      return;
    }

    if (!this.isWidgetRemovable(aWidgetId)) {
      return;
    }

    let placements = gPlacements.get(oldPlacement.area);
    let position = placements.indexOf(aWidgetId);
    if (position != -1) {
      placements.splice(position, 1);
    }

    let widget = gPalette.get(aWidgetId);
    if (widget) {
      widget.currentArea = null;
      widget.currentPosition = null;
    }

    gDirty = true;
    this.saveState();
    gDirtyAreaCache.add(oldPlacement.area);

    this.notifyListeners("onWidgetRemoved", aWidgetId, oldPlacement.area);
  },

  moveWidgetWithinArea(aWidgetId, aPosition) {
    let oldPlacement = this.getPlacementOfWidget(aWidgetId);
    if (!oldPlacement) {
      return;
    }

    let placements = gPlacements.get(oldPlacement.area);
    if (typeof aPosition != "number") {
      aPosition = placements.length;
    } else if (aPosition < 0) {
      aPosition = 0;
    } else if (aPosition > placements.length) {
      aPosition = placements.length;
    }

    let widget = gPalette.get(aWidgetId);
    if (widget) {
      widget.currentPosition = aPosition;
      widget.currentArea = oldPlacement.area;
    }

    if (aPosition == oldPlacement.position) {
      return;
    }

    placements.splice(oldPlacement.position, 1);
    // If we just removed the item from *before* where it is now added,
    // we need to compensate the position offset for that:
    if (oldPlacement.position < aPosition) {
      aPosition--;
    }
    placements.splice(aPosition, 0, aWidgetId);

    gDirty = true;
    gDirtyAreaCache.add(oldPlacement.area);

    this.saveState();

    this.notifyListeners("onWidgetMoved", aWidgetId, oldPlacement.area,
                         oldPlacement.position, aPosition);
  },

  // Note that this does not populate gPlacements, which is done lazily so that
  // the legacy state can be migrated, which is only available once a browser
  // window is openned.
  // The panel area is an exception here, since it has no legacy state.
  loadSavedState() {
    let state = Services.prefs.getCharPref(kPrefCustomizationState, "");
    if (!state) {
      log.debug("No saved state found");
      // Nothing has been customized, so silently fall back to the defaults.
      return;
    }
    try {
      gSavedState = JSON.parse(state);
      if (typeof gSavedState != "object" || gSavedState === null) {
        throw "Invalid saved state";
      }
    } catch (e) {
      Services.prefs.clearUserPref(kPrefCustomizationState);
      gSavedState = {};
      log.debug("Error loading saved UI customization state, falling back to defaults.");
    }

    if (!("placements" in gSavedState)) {
      gSavedState.placements = {};
    }

    if (!("currentVersion" in gSavedState)) {
      gSavedState.currentVersion = 0;
    }

    gSeenWidgets = new Set(gSavedState.seen || []);
    gDirtyAreaCache = new Set(gSavedState.dirtyAreaCache || []);
    gNewElementCount = gSavedState.newElementCount || 0;
  },

  restoreStateForArea(aArea, aLegacyState) {
    let placementsPreexisted = gPlacements.has(aArea);

    this.beginBatchUpdate();
    try {
      gRestoring = true;

      let restored = false;
      if (placementsPreexisted) {
        log.debug("Restoring " + aArea + " from pre-existing placements");
        for (let [position, id] of gPlacements.get(aArea).entries()) {
          this.moveWidgetWithinArea(id, position);
        }
        gDirty = false;
        restored = true;
      } else {
        gPlacements.set(aArea, []);
      }

      if (!restored && gSavedState && aArea in gSavedState.placements) {
        log.debug("Restoring " + aArea + " from saved state");
        let placements = gSavedState.placements[aArea];
        for (let id of placements)
          this.addWidgetToArea(id, aArea);
        gDirty = false;
        restored = true;
      }

      if (!restored && aLegacyState) {
        log.debug("Restoring " + aArea + " from legacy state");
        for (let id of aLegacyState)
          this.addWidgetToArea(id, aArea);
        // Don't override dirty state, to ensure legacy state is saved here and
        // therefore only used once.
        restored = true;
      }

      if (!restored) {
        log.debug("Restoring " + aArea + " from default state");
        let defaults = gAreas.get(aArea).get("defaultPlacements");
        if (defaults) {
          for (let id of defaults)
            this.addWidgetToArea(id, aArea, null, true);
        }
        gDirty = false;
      }

      // Finally, add widgets to the area that were added before the it was able
      // to be restored. This can occur when add-ons register widgets for a
      // lazily-restored area before it's been restored.
      if (gFuturePlacements.has(aArea)) {
        for (let id of gFuturePlacements.get(aArea))
          this.addWidgetToArea(id, aArea);
        gFuturePlacements.delete(aArea);
      }

      log.debug("Placements for " + aArea + ":\n\t" + gPlacements.get(aArea).join("\n\t"));

      gRestoring = false;
    } finally {
      this.endBatchUpdate();
    }
  },

  saveState() {
    if (gInBatchStack || !gDirty) {
      return;
    }
    // Clone because we want to modify this map:
    let state = { placements: new Map(gPlacements),
                  seen: gSeenWidgets,
                  dirtyAreaCache: gDirtyAreaCache,
                  currentVersion: kVersion,
                  newElementCount: gNewElementCount };

    // Merge in previously saved areas if not present in gPlacements.
    // This way, state is still persisted for e.g. temporarily disabled
    // add-ons - see bug 989338.
    if (gSavedState && gSavedState.placements) {
      for (let area of Object.keys(gSavedState.placements)) {
        if (!state.placements.has(area)) {
          let placements = gSavedState.placements[area];
          state.placements.set(area, placements);
        }
      }
    }

    log.debug("Saving state.");
    let serialized = JSON.stringify(state, this.serializerHelper);
    log.debug("State saved as: " + serialized);
    Services.prefs.setCharPref(kPrefCustomizationState, serialized);
    gDirty = false;
  },

  serializerHelper(aKey, aValue) {
    if (typeof aValue == "object" && aValue.constructor.name == "Map") {
      let result = {};
      for (let [mapKey, mapValue] of aValue)
        result[mapKey] = mapValue;
      return result;
    }

    if (typeof aValue == "object" && aValue.constructor.name == "Set") {
      return [...aValue];
    }

    return aValue;
  },

  beginBatchUpdate() {
    gInBatchStack++;
  },

  endBatchUpdate(aForceDirty) {
    gInBatchStack--;
    if (aForceDirty === true) {
      gDirty = true;
    }
    if (gInBatchStack == 0) {
      this.saveState();
    } else if (gInBatchStack < 0) {
      throw new Error("The batch editing stack should never reach a negative number.");
    }
  },

  addListener(aListener) {
    gListeners.add(aListener);
  },

  removeListener(aListener) {
    if (aListener == this) {
      return;
    }

    gListeners.delete(aListener);
  },

  notifyListeners(aEvent, ...aArgs) {
    if (gRestoring) {
      return;
    }

    for (let listener of gListeners) {
      try {
        if (typeof listener[aEvent] == "function") {
          listener[aEvent].apply(listener, aArgs);
        }
      } catch (e) {
        log.error(e + " -- " + e.fileName + ":" + e.lineNumber);
      }
    }
  },

  _dispatchToolboxEventToWindow(aEventType, aDetails, aWindow) {
    let evt = new aWindow.CustomEvent(aEventType, {
      bubbles: true,
      cancelable: true,
      detail: aDetails
    });
    aWindow.gNavToolbox.dispatchEvent(evt);
  },

  dispatchToolboxEvent(aEventType, aDetails = {}, aWindow = null) {
    if (aWindow) {
      this._dispatchToolboxEventToWindow(aEventType, aDetails, aWindow);
      return;
    }
    for (let [win, ] of gBuildWindows) {
      this._dispatchToolboxEventToWindow(aEventType, aDetails, win);
    }
  },

  createWidget(aProperties) {
    let widget = this.normalizeWidget(aProperties, CustomizableUI.SOURCE_EXTERNAL);
    // XXXunf This should probably throw.
    if (!widget) {
      log.error("unable to normalize widget");
      return undefined;
    }

    gPalette.set(widget.id, widget);

    // Clear our caches:
    gGroupWrapperCache.delete(widget.id);
    for (let [win, ] of gBuildWindows) {
      let cache = gSingleWrapperCache.get(win);
      if (cache) {
        cache.delete(widget.id);
      }
    }

    this.notifyListeners("onWidgetCreated", widget.id);

    if (widget.defaultArea) {
      let addToDefaultPlacements = false;
      let area = gAreas.get(widget.defaultArea);
      if (!CustomizableUI.isBuiltinToolbar(widget.defaultArea) &&
          widget.defaultArea != CustomizableUI.AREA_FIXED_OVERFLOW_PANEL) {
        addToDefaultPlacements = true;
      }

      if (addToDefaultPlacements) {
        if (area.has("defaultPlacements")) {
          area.get("defaultPlacements").push(widget.id);
        } else {
          area.set("defaultPlacements", [widget.id]);
        }
      }
    }

    // Look through previously saved state to see if we're restoring a widget.
    let seenAreas = new Set();
    let widgetMightNeedAutoAdding = true;
    for (let [area, ] of gPlacements) {
      seenAreas.add(area);
      let areaIsRegistered = gAreas.has(area);
      let index = gPlacements.get(area).indexOf(widget.id);
      if (index != -1) {
        widgetMightNeedAutoAdding = false;
        if (areaIsRegistered) {
          widget.currentArea = area;
          widget.currentPosition = index;
        }
        break;
      }
    }

    // Also look at saved state data directly in areas that haven't yet been
    // restored. Can't rely on this for restored areas, as they may have
    // changed.
    if (widgetMightNeedAutoAdding && gSavedState) {
      for (let area of Object.keys(gSavedState.placements)) {
        if (seenAreas.has(area)) {
          continue;
        }

        let areaIsRegistered = gAreas.has(area);
        let index = gSavedState.placements[area].indexOf(widget.id);
        if (index != -1) {
          widgetMightNeedAutoAdding = false;
          if (areaIsRegistered) {
            widget.currentArea = area;
            widget.currentPosition = index;
          }
          break;
        }
      }
    }

    // If we're restoring the widget to it's old placement, fire off the
    // onWidgetAdded event - our own handler will take care of adding it to
    // any build areas.
    this.beginBatchUpdate();
    try {
      if (widget.currentArea) {
        this.notifyListeners("onWidgetAdded", widget.id, widget.currentArea,
                             widget.currentPosition);
      } else if (widgetMightNeedAutoAdding) {
        let autoAdd = Services.prefs.getBoolPref(kPrefCustomizationAutoAdd, true);

        // If the widget doesn't have an existing placement, and it hasn't been
        // seen before, then add it to its default area so it can be used.
        // If the widget is not removable, we *have* to add it to its default
        // area here.
        let canBeAutoAdded = autoAdd && !gSeenWidgets.has(widget.id);
        if (!widget.currentArea && (!widget.removable || canBeAutoAdded)) {
          if (widget.defaultArea) {
            if (this.isAreaLazy(widget.defaultArea)) {
              gFuturePlacements.get(widget.defaultArea).add(widget.id);
            } else {
              this.addWidgetToArea(widget.id, widget.defaultArea);
            }
          }
        }
      }
    } finally {
      // Ensure we always have this widget in gSeenWidgets, and save
      // state in case this needs to be done here.
      gSeenWidgets.add(widget.id);
      this.endBatchUpdate(true);
    }

    this.notifyListeners("onWidgetAfterCreation", widget.id, widget.currentArea);
    return widget.id;
  },

  createBuiltinWidget(aData) {
    // This should only ever be called on startup, before any windows are
    // opened - so we know there's no build areas to handle. Also, builtin
    // widgets are expected to be (mostly) static, so shouldn't affect the
    // current placement settings.

    // This allows a widget to be both built-in by default but also able to be
    // destroyed and removed from the area based on criteria that may not be
    // available when the widget is created -- for example, because some other
    // feature in the browser supersedes the widget.
    let conditionalDestroyPromise = aData.conditionalDestroyPromise || null;
    delete aData.conditionalDestroyPromise;

    let widget = this.normalizeWidget(aData, CustomizableUI.SOURCE_BUILTIN);
    if (!widget) {
      log.error("Error creating builtin widget: " + aData.id);
      return;
    }

    log.debug("Creating built-in widget with id: " + widget.id);
    gPalette.set(widget.id, widget);

    if (conditionalDestroyPromise) {
      conditionalDestroyPromise.then(shouldDestroy => {
        if (shouldDestroy) {
          this.destroyWidget(widget.id);
          this.removeWidgetFromArea(widget.id);
        }
      }, err => {
        Cu.reportError(err);
      });
    }
  },

  // Returns true if the area will eventually lazily restore (but hasn't yet).
  isAreaLazy(aArea) {
    if (gPlacements.has(aArea)) {
      return false;
    }
    return gAreas.get(aArea).has("legacy");
  },

  // XXXunf Log some warnings here, when the data provided isn't up to scratch.
  normalizeWidget(aData, aSource) {
    let widget = {
      implementation: aData,
      source: aSource || CustomizableUI.SOURCE_EXTERNAL,
      instances: new Map(),
      currentArea: null,
      localized: true,
      removable: true,
      overflows: true,
      defaultArea: null,
      shortcutId: null,
      tabSpecific: false,
      tooltiptext: null,
      showInPrivateBrowsing: true,
      _introducedInVersion: -1,
    };

    if (typeof aData.id != "string" || !/^[a-z0-9-_]{1,}$/i.test(aData.id)) {
      log.error("Given an illegal id in normalizeWidget: " + aData.id);
      return null;
    }

    delete widget.implementation.currentArea;
    widget.implementation.__defineGetter__("currentArea", () => widget.currentArea);

    const kReqStringProps = ["id"];
    for (let prop of kReqStringProps) {
      if (typeof aData[prop] != "string") {
        log.error("Missing required property '" + prop + "' in normalizeWidget: "
                  + aData.id);
        return null;
      }
      widget[prop] = aData[prop];
    }

    const kOptStringProps = ["label", "tooltiptext", "shortcutId"];
    for (let prop of kOptStringProps) {
      if (typeof aData[prop] == "string") {
        widget[prop] = aData[prop];
      }
    }

    const kOptBoolProps = ["removable", "showInPrivateBrowsing", "overflows", "tabSpecific",
                           "localized"];
    for (let prop of kOptBoolProps) {
      if (typeof aData[prop] == "boolean") {
        widget[prop] = aData[prop];
      }
    }

    // When we normalize builtin widgets, areas have not yet been registered:
    if (aData.defaultArea &&
        (aSource == CustomizableUI.SOURCE_BUILTIN || gAreas.has(aData.defaultArea))) {
      widget.defaultArea = aData.defaultArea;
    } else if (!widget.removable) {
      log.error("Widget '" + widget.id + "' is not removable but does not specify " +
                "a valid defaultArea. That's not possible; it must specify a " +
                "valid defaultArea as well.");
      return null;
    }

    if ("type" in aData && gSupportedWidgetTypes.has(aData.type)) {
      widget.type = aData.type;
    } else {
      widget.type = "button";
    }

    widget.disabled = aData.disabled === true;

    if (aSource == CustomizableUI.SOURCE_BUILTIN) {
      widget._introducedInVersion = aData.introducedInVersion || 0;
    }

    this.wrapWidgetEventHandler("onBeforeCreated", widget);
    this.wrapWidgetEventHandler("onClick", widget);
    this.wrapWidgetEventHandler("onCreated", widget);
    this.wrapWidgetEventHandler("onDestroyed", widget);

    if (typeof aData.onBeforeCommand == "function") {
      widget.onBeforeCommand = aData.onBeforeCommand;
    }

    if (widget.type == "button") {
      widget.onCommand = typeof aData.onCommand == "function" ?
                           aData.onCommand :
                           null;
    } else if (widget.type == "view") {
      if (typeof aData.viewId != "string") {
        log.error("Expected a string for widget " + widget.id + " viewId, but got "
                  + aData.viewId);
        return null;
      }
      widget.viewId = aData.viewId;

      this.wrapWidgetEventHandler("onViewShowing", widget);
      this.wrapWidgetEventHandler("onViewHiding", widget);
    } else if (widget.type == "custom") {
      this.wrapWidgetEventHandler("onBuild", widget);
    }

    if (gPalette.has(widget.id)) {
      return null;
    }

    return widget;
  },

  wrapWidgetEventHandler(aEventName, aWidget) {
    if (typeof aWidget.implementation[aEventName] != "function") {
      aWidget[aEventName] = null;
      return;
    }
    aWidget[aEventName] = function(...aArgs) {
      // Wrap inside a try...catch to properly log errors, until bug 862627 is
      // fixed, which in turn might help bug 503244.
      try {
        // Don't copy the function to the normalized widget object, instead
        // keep it on the original object provided to the API so that
        // additional methods can be implemented and used by the event
        // handlers.
        return aWidget.implementation[aEventName].apply(aWidget.implementation,
                                                        aArgs);
      } catch (e) {
        Cu.reportError(e);
        return undefined;
      }
    };
  },

  destroyWidget(aWidgetId) {
    let widget = gPalette.get(aWidgetId);
    if (!widget) {
      gGroupWrapperCache.delete(aWidgetId);
      for (let [window, ] of gBuildWindows) {
        let windowCache = gSingleWrapperCache.get(window);
        if (windowCache) {
          windowCache.delete(aWidgetId);
        }
      }
      return;
    }

    // Remove it from the default placements of an area if it was added there:
    if (widget.defaultArea) {
      let area = gAreas.get(widget.defaultArea);
      if (area) {
        let defaultPlacements = area.get("defaultPlacements");
        // We can assume this is present because if a widget has a defaultArea,
        // we automatically create a defaultPlacements array for that area.
        let widgetIndex = defaultPlacements.indexOf(aWidgetId);
        if (widgetIndex != -1) {
          defaultPlacements.splice(widgetIndex, 1);
        }
      }
    }

    // This will not remove the widget from gPlacements - we want to keep the
    // setting so the widget gets put back in it's old position if/when it
    // returns.
    for (let [window, ] of gBuildWindows) {
      let windowCache = gSingleWrapperCache.get(window);
      if (windowCache) {
        windowCache.delete(aWidgetId);
      }
      let widgetNode = window.document.getElementById(aWidgetId) ||
                       window.gNavToolbox.palette.getElementsByAttribute("id", aWidgetId)[0];
      if (widgetNode) {
        let container = widgetNode.parentNode;
        this.notifyListeners("onWidgetBeforeDOMChange", widgetNode, null,
                             container, true);
        widgetNode.remove();
        this.notifyListeners("onWidgetAfterDOMChange", widgetNode, null,
                             container, true);
      }
      if (widget.type == "view") {
        let viewNode = window.document.getElementById(widget.viewId);
        if (viewNode) {
          for (let eventName of kSubviewEvents) {
            let handler = "on" + eventName;
            if (typeof widget[handler] == "function") {
              viewNode.removeEventListener(eventName, widget[handler]);
            }
          }
        }
      }
      if (widgetNode && widget.onDestroyed) {
        widget.onDestroyed(window.document);
      }
    }

    gPalette.delete(aWidgetId);
    gGroupWrapperCache.delete(aWidgetId);

    this.notifyListeners("onWidgetDestroyed", aWidgetId);
  },

  getCustomizeTargetForArea(aArea, aWindow) {
    let buildAreaNodes = gBuildAreas.get(aArea);
    if (!buildAreaNodes) {
      return null;
    }

    for (let node of buildAreaNodes) {
      if (node.ownerGlobal == aWindow) {
        return node.customizationTarget ? node.customizationTarget : node;
      }
    }

    return null;
  },

  reset() {
    gResetting = true;
    this._resetUIState();

    // Rebuild each registered area (across windows) to reflect the state that
    // was reset above.
    this._rebuildRegisteredAreas();

    for (let [widgetId, widget] of gPalette) {
      if (widget.source == CustomizableUI.SOURCE_EXTERNAL) {
        gSeenWidgets.add(widgetId);
      }
    }
    if (gSeenWidgets.size || gNewElementCount) {
      gDirty = true;
      this.saveState();
    }

    gResetting = false;
  },

  _resetUIState() {
    try {
      gUIStateBeforeReset.drawInTitlebar = Services.prefs.getBoolPref(kPrefDrawInTitlebar);
      gUIStateBeforeReset.extraDragSpace = Services.prefs.getBoolPref(kPrefExtraDragSpace);
      gUIStateBeforeReset.uiCustomizationState = Services.prefs.getCharPref(kPrefCustomizationState);
      gUIStateBeforeReset.uiDensity = Services.prefs.getIntPref(kPrefUIDensity);
      gUIStateBeforeReset.autoTouchMode = Services.prefs.getBoolPref(kPrefAutoTouchMode);
      gUIStateBeforeReset.currentTheme = LightweightThemeManager.currentTheme;
      gUIStateBeforeReset.autoHideDownloadsButton = Services.prefs.getBoolPref(kPrefAutoHideDownloadsButton);
      gUIStateBeforeReset.newElementCount = gNewElementCount;
    } catch (e) { }

    Services.prefs.clearUserPref(kPrefCustomizationState);
    Services.prefs.clearUserPref(kPrefDrawInTitlebar);
    Services.prefs.clearUserPref(kPrefExtraDragSpace);
    Services.prefs.clearUserPref(kPrefUIDensity);
    Services.prefs.clearUserPref(kPrefAutoTouchMode);
    Services.prefs.clearUserPref(kPrefAutoHideDownloadsButton);
    LightweightThemeManager.currentTheme = null;
    gNewElementCount = 0;
    log.debug("State reset");

    // Reset placements to make restoring default placements possible.
    gPlacements = new Map();
    gDirtyAreaCache = new Set();
    gSeenWidgets = new Set();
    // Clear the saved state to ensure that defaults will be used.
    gSavedState = null;
    // Restore the state for each area to its defaults
    for (let [areaId, ] of gAreas) {
      this.restoreStateForArea(areaId);
    }
  },

  _rebuildRegisteredAreas() {
    for (let [areaId, areaNodes] of gBuildAreas) {
      let placements = gPlacements.get(areaId);
      let isFirstChangedToolbar = true;
      for (let areaNode of areaNodes) {
        this.buildArea(areaId, placements, areaNode);

        let area = gAreas.get(areaId);
        if (area.get("type") == CustomizableUI.TYPE_TOOLBAR) {
          let defaultCollapsed = area.get("defaultCollapsed");
          let win = areaNode.ownerGlobal;
          if (defaultCollapsed !== null) {
            win.setToolbarVisibility(areaNode, !defaultCollapsed, isFirstChangedToolbar);
          }
        }
        isFirstChangedToolbar = false;
      }
    }
  },

  /**
   * Undoes a previous reset, restoring the state of the UI to the state prior to the reset.
   */
  undoReset() {
    if (gUIStateBeforeReset.uiCustomizationState == null ||
        gUIStateBeforeReset.drawInTitlebar == null) {
      return;
    }
    gUndoResetting = true;

    const {
      uiCustomizationState, drawInTitlebar, currentTheme, uiDensity,
      autoTouchMode, autoHideDownloadsButton, extraDragSpace,
    } = gUIStateBeforeReset;
    gNewElementCount = gUIStateBeforeReset.newElementCount;

    // Need to clear the previous state before setting the prefs
    // because pref observers may check if there is a previous UI state.
    this._clearPreviousUIState();

    Services.prefs.setCharPref(kPrefCustomizationState, uiCustomizationState);
    Services.prefs.setBoolPref(kPrefDrawInTitlebar, drawInTitlebar);
    Services.prefs.setBoolPref(kPrefExtraDragSpace, extraDragSpace);
    Services.prefs.setIntPref(kPrefUIDensity, uiDensity);
    Services.prefs.setBoolPref(kPrefAutoTouchMode, autoTouchMode);
    Services.prefs.setBoolPref(kPrefAutoHideDownloadsButton, autoHideDownloadsButton);
    LightweightThemeManager.currentTheme = currentTheme;
    this.loadSavedState();
    // If the user just customizes toolbar/titlebar visibility, gSavedState will be null
    // and we don't need to do anything else here:
    if (gSavedState) {
      for (let areaId of Object.keys(gSavedState.placements)) {
        let placements = gSavedState.placements[areaId];
        gPlacements.set(areaId, placements);
      }
      this._rebuildRegisteredAreas();
    }

    gUndoResetting = false;
  },

  _clearPreviousUIState() {
    Object.getOwnPropertyNames(gUIStateBeforeReset).forEach((prop) => {
      gUIStateBeforeReset[prop] = null;
    });
  },

  /**
   * @param {String|Node} aWidget - widget ID or a widget node (preferred for performance).
   * @return {Boolean} whether the widget is removable
   */
  isWidgetRemovable(aWidget) {
    let widgetId;
    let widgetNode;
    if (typeof aWidget == "string") {
      widgetId = aWidget;
    } else {
      // Skipped items could just not have ids.
      if (!aWidget.id && aWidget.getAttribute("skipintoolbarset") == "true") {
        return false;
      }
      if (!aWidget.id &&
          !["toolbarspring", "toolbarspacer", "toolbarseparator"].includes(aWidget.nodeName)) {
        throw new Error("No nodes without ids that aren't special widgets should ever come into contact with CUI");
      }
      // Use "spring" / "spacer" / "separator" for special widgets without ids
      widgetId = aWidget.id || aWidget.nodeName.substring(7 /* "toolbar".length */);
      widgetNode = aWidget;
    }
    let provider = this.getWidgetProvider(widgetId);

    if (provider == CustomizableUI.PROVIDER_API) {
      return gPalette.get(widgetId).removable;
    }

    if (provider == CustomizableUI.PROVIDER_XUL) {
      if (gBuildWindows.size == 0) {
        // We don't have any build windows to look at, so just assume for now
        // that its removable.
        return true;
      }

      if (!widgetNode) {
        // Pick any of the build windows to look at.
        let [window, ] = [...gBuildWindows][0];
        [, widgetNode] = this.getWidgetNode(widgetId, window);
      }
      // If we don't have a node, we assume it's removable. This can happen because
      // getWidgetProvider returns PROVIDER_XUL by default, but this will also happen
      // for API-provided widgets which have been destroyed.
      if (!widgetNode) {
        return true;
      }
      return widgetNode.getAttribute("removable") == "true";
    }

    // Otherwise this is either a special widget, which is always removable, or
    // an API widget which has already been removed from gPalette. Returning true
    // here allows us to then remove its ID from any placements where it might
    // still occur.
    return true;
  },

  canWidgetMoveToArea(aWidgetId, aArea) {
    // Special widgets can't move to the menu panel.
    if (this.isSpecialWidget(aWidgetId) && gAreas.has(aArea) &&
        gAreas.get(aArea).get("type") == CustomizableUI.TYPE_MENU_PANEL) {
      return false;
    }
    let placement = this.getPlacementOfWidget(aWidgetId);
    // Items in the palette can move, and items can move within their area:
    if (!placement || placement.area == aArea) {
      return true;
    }
    // For everything else, just return whether the widget is removable.
    return this.isWidgetRemovable(aWidgetId);
  },

  ensureWidgetPlacedInWindow(aWidgetId, aWindow) {
    let placement = this.getPlacementOfWidget(aWidgetId);
    if (!placement) {
      return false;
    }
    let areaNodes = gBuildAreas.get(placement.area);
    if (!areaNodes) {
      return false;
    }
    let container = [...areaNodes].filter((n) => n.ownerGlobal == aWindow);
    if (!container.length) {
      return false;
    }
    let existingNode = container[0].getElementsByAttribute("id", aWidgetId)[0];
    if (existingNode) {
      return true;
    }

    this.insertNodeInWindow(aWidgetId, container[0], true);
    return true;
  },

  get inDefaultState() {
    for (let [areaId, props] of gAreas) {
      let defaultPlacements = props.get("defaultPlacements");
      // Areas without default placements (like legacy ones?) get skipped
      if (!defaultPlacements) {
        continue;
      }

      let currentPlacements = gPlacements.get(areaId);
      // We're excluding all of the placement IDs for items that do not exist,
      // and items that have removable="false",
      // because we don't want to consider them when determining if we're
      // in the default state. This way, if an add-on introduces a widget
      // and is then uninstalled, the leftover placement doesn't cause us to
      // automatically assume that the buttons are not in the default state.
      let buildAreaNodes = gBuildAreas.get(areaId);
      if (buildAreaNodes && buildAreaNodes.size) {
        let container = [...buildAreaNodes][0];
        let removableOrDefault = (itemNodeOrItem) => {
          let item = (itemNodeOrItem && itemNodeOrItem.id) || itemNodeOrItem;
          let isRemovable = this.isWidgetRemovable(itemNodeOrItem);
          let isInDefault = defaultPlacements.includes(item);
          return isRemovable || isInDefault;
        };
        // Toolbars have a currentSet property which also deals correctly with overflown
        // widgets (if any) - use that instead:
        if (props.get("type") == CustomizableUI.TYPE_TOOLBAR) {
          let currentSet = container.currentSet;
          currentPlacements = currentSet ? currentSet.split(",") : [];
          currentPlacements = currentPlacements.filter(removableOrDefault);
        } else {
          // Clone the array so we don't modify the actual placements...
          currentPlacements = [...currentPlacements];
          currentPlacements = currentPlacements.filter((item) => {
            let itemNode = container.getElementsByAttribute("id", item)[0];
            return itemNode && removableOrDefault(itemNode || item);
          });
        }

        if (props.get("type") == CustomizableUI.TYPE_TOOLBAR) {
          let attribute = container.getAttribute("type") == "menubar" ? "autohide" : "collapsed";
          let collapsed = container.getAttribute(attribute) == "true";
          let defaultCollapsed = props.get("defaultCollapsed");
          if (defaultCollapsed !== null && collapsed != defaultCollapsed) {
            log.debug("Found " + areaId + " had non-default toolbar visibility (expected " + defaultCollapsed + ", was " + collapsed + ")");
            return false;
          }
        }
      }
      log.debug("Checking default state for " + areaId + ":\n" + currentPlacements.join(",") +
                "\nvs.\n" + defaultPlacements.join(","));

      if (currentPlacements.length != defaultPlacements.length) {
        return false;
      }

      for (let i = 0; i < currentPlacements.length; ++i) {
        if (currentPlacements[i] != defaultPlacements[i] &&
            !this.matchingSpecials(currentPlacements[i], defaultPlacements[i])) {
          log.debug("Found " + currentPlacements[i] + " in " + areaId + " where " +
                    defaultPlacements[i] + " was expected!");
          return false;
        }
      }
    }

    if (Services.prefs.prefHasUserValue(kPrefUIDensity)) {
      log.debug(kPrefUIDensity + " pref is non-default");
      return false;
    }

    if (Services.prefs.prefHasUserValue(kPrefAutoTouchMode)) {
      log.debug(kPrefAutoTouchMode + " pref is non-default");
      return false;
    }

    if (Services.prefs.prefHasUserValue(kPrefDrawInTitlebar)) {
      log.debug(kPrefDrawInTitlebar + " pref is non-default");
      return false;
    }

    if (Services.prefs.prefHasUserValue(kPrefExtraDragSpace)) {
      log.debug(kPrefExtraDragSpace + " pref is non-default");
      return false;
    }

    if (LightweightThemeManager.currentTheme) {
      log.debug(LightweightThemeManager.currentTheme + " theme is non-default");
      return false;
    }

    return true;
  },

  setToolbarVisibility(aToolbarId, aIsVisible) {
    // We only persist the attribute the first time.
    let isFirstChangedToolbar = true;
    for (let window of CustomizableUI.windows) {
      let toolbar = window.document.getElementById(aToolbarId);
      if (toolbar) {
        window.setToolbarVisibility(toolbar, aIsVisible, isFirstChangedToolbar);
        isFirstChangedToolbar = false;
      }
    }
  },
};
Object.freeze(CustomizableUIInternal);

var CustomizableUI = {
  /**
   * Constant reference to the ID of the navigation toolbar.
   */
  AREA_NAVBAR: "nav-bar",
  /**
   * Constant reference to the ID of the menubar's toolbar.
   */
  AREA_MENUBAR: "toolbar-menubar",
  /**
   * Constant reference to the ID of the tabstrip toolbar.
   */
  AREA_TABSTRIP: "TabsToolbar",
  /**
   * Constant reference to the ID of the bookmarks toolbar.
   */
  AREA_BOOKMARKS: "PersonalToolbar",
  /**
   * Constant reference to the ID of the non-dymanic (fixed) list in the overflow panel.
   */
  AREA_FIXED_OVERFLOW_PANEL: "widget-overflow-fixed-list",

  /**
   * Constant indicating the area is a menu panel.
   */
  TYPE_MENU_PANEL: "menu-panel",
  /**
   * Constant indicating the area is a toolbar.
   */
  TYPE_TOOLBAR: "toolbar",

  /**
   * Constant indicating a XUL-type provider.
   */
  PROVIDER_XUL: "xul",
  /**
   * Constant indicating an API-type provider.
   */
  PROVIDER_API: "api",
  /**
   * Constant indicating dynamic (special) widgets: spring, spacer, and separator.
   */
  PROVIDER_SPECIAL: "special",

  /**
   * Constant indicating the widget is built-in
   */
  SOURCE_BUILTIN: "builtin",
  /**
   * Constant indicating the widget is externally provided
   * (e.g. by add-ons or other items not part of the builtin widget set).
   */
  SOURCE_EXTERNAL: "external",

  /**
   * Constant indicating the reason the event was fired was a window closing
   */
  REASON_WINDOW_CLOSED: "window-closed",
  /**
   * Constant indicating the reason the event was fired was an area being
   * unregistered separately from window closing mechanics.
   */
  REASON_AREA_UNREGISTERED: "area-unregistered",


  /**
   * An iteratable property of windows managed by CustomizableUI.
   * Note that this can *only* be used as an iterator. ie:
   *     for (let window of CustomizableUI.windows) { ... }
   */
  windows: {
    * [Symbol.iterator]() {
      for (let [window, ] of gBuildWindows)
        yield window;
    }
  },

  /**
   * Add a listener object that will get fired for various events regarding
   * customization.
   *
   * @param aListener the listener object to add
   *
   * Not all event handler methods need to be defined.
   * CustomizableUI will catch exceptions. Events are dispatched
   * synchronously on the UI thread, so if you can delay any/some of your
   * processing, that is advisable. The following event handlers are supported:
   *   - onWidgetAdded(aWidgetId, aArea, aPosition)
   *     Fired when a widget is added to an area. aWidgetId is the widget that
   *     was added, aArea the area it was added to, and aPosition the position
   *     in which it was added.
   *   - onWidgetMoved(aWidgetId, aArea, aOldPosition, aNewPosition)
   *     Fired when a widget is moved within its area. aWidgetId is the widget
   *     that was moved, aArea the area it was moved in, aOldPosition its old
   *     position, and aNewPosition its new position.
   *   - onWidgetRemoved(aWidgetId, aArea)
   *     Fired when a widget is removed from its area. aWidgetId is the widget
   *     that was removed, aArea the area it was removed from.
   *
   *   - onWidgetBeforeDOMChange(aNode, aNextNode, aContainer, aIsRemoval)
   *     Fired *before* a widget's DOM node is acted upon by CustomizableUI
   *     (to add, move or remove it). aNode is the DOM node changed, aNextNode
   *     the DOM node (if any) before which a widget will be inserted,
   *     aContainer the *actual* DOM container (could be an overflow panel in
   *     case of an overflowable toolbar), and aWasRemoval is true iff the
   *     action about to happen is the removal of the DOM node.
   *   - onWidgetAfterDOMChange(aNode, aNextNode, aContainer, aWasRemoval)
   *     Like onWidgetBeforeDOMChange, but fired after the change to the DOM
   *     node of the widget.
   *
   *   - onWidgetReset(aNode, aContainer)
   *     Fired after a reset to default placements moves a widget's node to a
   *     different location. aNode is the widget's node, aContainer is the
   *     area it was moved into (NB: it might already have been there and been
   *     moved to a different position!)
   *   - onWidgetUndoMove(aNode, aContainer)
   *     Fired after undoing a reset to default placements moves a widget's
   *     node to a different location. aNode is the widget's node, aContainer
   *     is the area it was moved into (NB: it might already have been there
   *     and been moved to a different position!)
   *   - onAreaReset(aArea, aContainer)
   *     Fired after a reset to default placements is complete on an area's
   *     DOM node. Note that this is fired for each DOM node. aArea is the area
   *     that was reset, aContainer the DOM node that was reset.
   *
   *   - onWidgetCreated(aWidgetId)
   *     Fired when a widget with id aWidgetId has been created, but before it
   *     is added to any placements or any DOM nodes have been constructed.
   *     Only fired for API-based widgets.
   *   - onWidgetAfterCreation(aWidgetId, aArea)
   *     Fired after a widget with id aWidgetId has been created, and has been
   *     added to either its default area or the area in which it was placed
   *     previously. If the widget has no default area and/or it has never
   *     been placed anywhere, aArea may be null. Only fired for API-based
   *     widgets.
   *   - onWidgetDestroyed(aWidgetId)
   *     Fired when widgets are destroyed. aWidgetId is the widget that is
   *     being destroyed. Only fired for API-based widgets.
   *   - onWidgetInstanceRemoved(aWidgetId, aDocument)
   *     Fired when a window is unloaded and a widget's instance is destroyed
   *     because of this. Only fired for API-based widgets.
   *
   *   - onWidgetDrag(aWidgetId, aArea)
   *     Fired both when and after customize mode drag handling system tries
   *     to determine the width and height of widget aWidgetId when dragged to a
   *     different area. aArea will be the area the item is dragged to, or
   *     undefined after the measurements have been done and the node has been
   *     moved back to its 'regular' area.
   *
   *   - onCustomizeStart(aWindow)
   *     Fired when opening customize mode in aWindow.
   *   - onCustomizeEnd(aWindow)
   *     Fired when exiting customize mode in aWindow.
   *
   *   - onWidgetOverflow(aNode, aContainer)
   *     Fired when a widget's DOM node is overflowing its container, a toolbar,
   *     and will be displayed in the overflow panel.
   *   - onWidgetUnderflow(aNode, aContainer)
   *     Fired when a widget's DOM node is *not* overflowing its container, a
   *     toolbar, anymore.
   *   - onWindowOpened(aWindow)
   *     Fired when a window has been opened that is managed by CustomizableUI,
   *     once all of the prerequisite setup has been done.
   *   - onWindowClosed(aWindow)
   *     Fired when a window that has been managed by CustomizableUI has been
   *     closed.
   *   - onAreaNodeRegistered(aArea, aContainer)
   *     Fired after an area node is first built when it is registered. This
   *     is often when the window has opened, but in the case of add-ons,
   *     could fire when the node has just been registered with CustomizableUI
   *     after an add-on update or disable/enable sequence.
   *   - onAreaNodeUnregistered(aArea, aContainer, aReason)
   *     Fired when an area node is explicitly unregistered by an API caller,
   *     or by a window closing. The aReason parameter indicates which of
   *     these is the case.
   */
  addListener(aListener) {
    CustomizableUIInternal.addListener(aListener);
  },
  /**
   * Remove a listener added with addListener
   * @param aListener the listener object to remove
   */
  removeListener(aListener) {
    CustomizableUIInternal.removeListener(aListener);
  },

  /**
   * Register a customizable area with CustomizableUI.
   * @param aName   the name of the area to register. Can only contain
   *                alphanumeric characters, dashes (-) and underscores (_).
   * @param aProps  the properties of the area. The following properties are
   *                recognized:
   *                - type:   the type of area. Either TYPE_TOOLBAR (default) or
   *                          TYPE_MENU_PANEL;
   *                - anchor: for a menu panel or overflowable toolbar, the
   *                          anchoring node for the panel.
   *                - legacy: set to true if you want customizableui to
   *                          automatically migrate the currentset attribute
   *                - overflowable: set to true if your toolbar is overflowable.
   *                                This requires an anchor, and only has an
   *                                effect for toolbars.
   *                - defaultPlacements: an array of widget IDs making up the
   *                                     default contents of the area
   *                - defaultCollapsed: (INTERNAL ONLY) applies if the type is TYPE_TOOLBAR, specifies
   *                                    if toolbar is collapsed by default (default to true).
   *                                    Specify null to ensure that reset/inDefaultArea don't care
   *                                    about a toolbar's collapsed state
   */
  registerArea(aName, aProperties) {
    CustomizableUIInternal.registerArea(aName, aProperties);
  },
  /**
   * Register a concrete node for a registered area. This method is automatically
   * called from any toolbar in the main browser window that has its
   * "customizable" attribute set to true. There should normally be no need to
   * call it yourself.
   *
   * Note that ideally, you should register your toolbar using registerArea
   * before any of the toolbars have their XBL bindings constructed (which
   * will happen when they're added to the DOM and are not hidden). If you
   * don't, and your toolbar has a defaultset attribute, CustomizableUI will
   * register it automatically. If your toolbar does not have a defaultset
   * attribute, the node will be saved for processing when you call
   * registerArea. Note that CustomizableUI won't restore state in the area,
   * allow the user to customize it in customize mode, or otherwise deal
   * with it, until the area has been registered.
   */
  registerToolbarNode(aToolbar, aExistingChildren) {
    CustomizableUIInternal.registerToolbarNode(aToolbar, aExistingChildren);
  },
  /**
   * Register the menu panel node. This method should not be called by anyone
   * apart from the built-in PanelUI.
   * @param aPanelContents the panel contents DOM node being registered.
   * @param aArea the area for which to register this node.
   */
  registerMenuPanel(aPanelContents, aArea) {
    CustomizableUIInternal.registerMenuPanel(aPanelContents, aArea);
  },
  /**
   * Unregister a customizable area. The inverse of registerArea.
   *
   * Unregistering an area will remove all the (removable) widgets in the
   * area, which will return to the panel, and destroy all other traces
   * of the area within CustomizableUI. Note that this means the *contents*
   * of the area's DOM nodes will be moved to the panel or removed, but
   * the area's DOM nodes *themselves* will stay.
   *
   * Furthermore, by default the placements of the area will be kept in the
   * saved state (!) and restored if you re-register the area at a later
   * point. This is useful for e.g. add-ons that get disabled and then
   * re-enabled (e.g. when they update).
   *
   * You can override this last behaviour (and destroy the placements
   * information in the saved state) by passing true for aDestroyPlacements.
   *
   * @param aName              the name of the area to unregister
   * @param aDestroyPlacements whether to destroy the placements information
   *                           for the area, too.
   */
  unregisterArea(aName, aDestroyPlacements) {
    CustomizableUIInternal.unregisterArea(aName, aDestroyPlacements);
  },
  /**
   * Add a widget to an area.
   * If the area to which you try to add is not known to CustomizableUI,
   * this will throw.
   * If the area to which you try to add has not yet been restored from its
   * legacy state, this will postpone the addition.
   * If the area to which you try to add is the same as the area in which
   * the widget is currently placed, this will do the same as
   * moveWidgetWithinArea.
   * If the widget cannot be removed from its original location, this will
   * no-op.
   *
   * This will fire an onWidgetAdded notification,
   * and an onWidgetBeforeDOMChange and onWidgetAfterDOMChange notification
   * for each window CustomizableUI knows about.
   *
   * @param aWidgetId the ID of the widget to add
   * @param aArea     the ID of the area to add the widget to
   * @param aPosition the position at which to add the widget. If you do not
   *                  pass a position, the widget will be added to the end
   *                  of the area.
   */
  addWidgetToArea(aWidgetId, aArea, aPosition) {
    CustomizableUIInternal.addWidgetToArea(aWidgetId, aArea, aPosition);
  },
  /**
   * Remove a widget from its area. If the widget cannot be removed from its
   * area, or is not in any area, this will no-op. Otherwise, this will fire an
   * onWidgetRemoved notification, and an onWidgetBeforeDOMChange and
   * onWidgetAfterDOMChange notification for each window CustomizableUI knows
   * about.
   *
   * @param aWidgetId the ID of the widget to remove
   */
  removeWidgetFromArea(aWidgetId) {
    CustomizableUIInternal.removeWidgetFromArea(aWidgetId);
  },
  /**
   * Move a widget within an area.
   * If the widget is not in any area, this will no-op.
   * If the widget is already at the indicated position, this will no-op.
   *
   * Otherwise, this will move the widget and fire an onWidgetMoved notification,
   * and an onWidgetBeforeDOMChange and onWidgetAfterDOMChange notification for
   * each window CustomizableUI knows about.
   *
   * @param aWidgetId the ID of the widget to move
   * @param aPosition the position to move the widget to.
   *                  Negative values or values greater than the number of
   *                  widgets will be interpreted to mean moving the widget to
   *                  respectively the first or last position.
   */
  moveWidgetWithinArea(aWidgetId, aPosition) {
    CustomizableUIInternal.moveWidgetWithinArea(aWidgetId, aPosition);
  },
  /**
   * Ensure a XUL-based widget created in a window after areas were
   * initialized moves to its correct position.
   * This is roughly equivalent to manually looking up the position and using
   * insertItem in the old API, but a lot less work for consumers.
   * Always prefer this over using toolbar.insertItem (which might no-op
   * because it delegates to addWidgetToArea) or, worse, moving items in the
   * DOM yourself.
   *
   * @param aWidgetId the ID of the widget that was just created
   * @param aWindow the window in which you want to ensure it was added.
   *
   * NB: why is this API per-window, you wonder? Because if you need this,
   * presumably you yourself need to create the widget in all the windows
   * and need to loop through them anyway.
   */
  ensureWidgetPlacedInWindow(aWidgetId, aWindow) {
    return CustomizableUIInternal.ensureWidgetPlacedInWindow(aWidgetId, aWindow);
  },
  /**
   * Start a batch update of items.
   * During a batch update, the customization state is not saved to the user's
   * preferences file, in order to reduce (possibly sync) IO.
   * Calls to begin/endBatchUpdate may be nested.
   *
   * Callers should ensure that NO MATTER WHAT they call endBatchUpdate once
   * for each call to beginBatchUpdate, even if there are exceptions in the
   * code in the batch update. Otherwise, for the duration of the
   * Firefox session, customization state is never saved. Typically, you
   * would do this using a try...finally block.
   */
  beginBatchUpdate() {
    CustomizableUIInternal.beginBatchUpdate();
  },
  /**
   * End a batch update. See the documentation for beginBatchUpdate above.
   *
   * State is not saved if we believe it is identical to the last known
   * saved state. State is only ever saved when all batch updates have
   * finished (ie there has been 1 endBatchUpdate call for each
   * beginBatchUpdate call). If any of the endBatchUpdate calls pass
   * aForceDirty=true, we will flush to the prefs file.
   *
   * @param aForceDirty force CustomizableUI to flush to the prefs file when
   *                    all batch updates have finished.
   */
  endBatchUpdate(aForceDirty) {
    CustomizableUIInternal.endBatchUpdate(aForceDirty);
  },
  /**
   * Create a widget.
   *
   * To create a widget, you should pass an object with its desired
   * properties. The following properties are supported:
   *
   * - id:            the ID of the widget (required).
   * - type:          a string indicating the type of widget. Possible types
   *                  are:
   *                  'button' - for simple button widgets (the default)
   *                  'view'   - for buttons that open a panel or subview,
   *                             depending on where they are placed.
   *                  'custom' - for fine-grained control over the creation
   *                             of the widget.
   * - viewId:        Only useful for views (and required there): the id of the
   *                  <panelview> that should be shown when clicking the widget.
   * - onBuild(aDoc): Only useful for custom widgets (and required there); a
   *                  function that will be invoked with the document in which
   *                  to build a widget. Should return the DOM node that has
   *                  been constructed.
   * - onBeforeCreated(aDoc): Attached to all non-custom widgets; a function
   *                  that will be invoked before the widget gets a DOM node
   *                  constructed, passing the document in which that will happen.
   *                  This is useful especially for 'view' type widgets that need
   *                  to construct their views on the fly (e.g. from bootstrapped
   *                  add-ons)
   * - onCreated(aNode): Attached to all widgets; a function that will be invoked
   *                  whenever the widget has a DOM node constructed, passing the
   *                  constructed node as an argument.
   * - onDestroyed(aDoc): Attached to all non-custom widgets; a function that
   *                  will be invoked after the widget has a DOM node destroyed,
   *                  passing the document from which it was removed. This is
   *                  useful especially for 'view' type widgets that need to
   *                  cleanup after views that were constructed on the fly.
   * - onBeforeCommand(aEvt): A function that will be invoked when the user
   *                          activates the button but before the command
   *                          is evaluated. Useful if code needs to run to
   *                          change the button's icon in preparation to the
   *                          pending command action. Called for both type=button
   *                          and type=view.
   * - onCommand(aEvt): Only useful for button widgets; a function that will be
   *                    invoked when the user activates the button.
   * - onClick(aEvt): Attached to all widgets; a function that will be invoked
   *                  when the user clicks the widget.
   * - onViewShowing(aEvt): Only useful for views; a function that will be
   *                  invoked when a user shows your view. If any event
   *                  handler calls aEvt.preventDefault(), the view will
   *                  not be shown.
   *
   *                  The event's `detail` property is an object with an
   *                  `addBlocker` method. Handlers which need to
   *                  perform asynchronous operations before the view is
   *                  shown may pass this method a Promise, which will
   *                  prevent the view from showing until it resolves.
   *                  Additionally, if the promise resolves to the exact
   *                  value `false`, the view will not be shown.
   * - onViewHiding(aEvt): Only useful for views; a function that will be
   *                  invoked when a user hides your view.
   * - tooltiptext:   string to use for the tooltip of the widget
   * - label:         string to use for the label of the widget
   * - localized:     If true, or undefined, attempt to retrieve the
   *                  widget's string properties from the customizable
   *                  widgets string bundle.
   * - removable:     whether the widget is removable (optional, default: true)
   *                  NB: if you specify false here, you must provide a
   *                  defaultArea, too.
   * - overflows:     whether widget can overflow when in an overflowable
   *                  toolbar (optional, default: true)
   * - defaultArea:   default area to add the widget to
   *                  (optional, default: none; required if non-removable)
   * - shortcutId:    id of an element that has a shortcut for this widget
   *                  (optional, default: null). This is only used to display
   *                  the shortcut as part of the tooltip for builtin widgets
   *                  (which have strings inside
   *                  customizableWidgets.properties). If you're in an add-on,
   *                  you should not set this property.
   * - showInPrivateBrowsing: whether to show the widget in private browsing
   *                          mode (optional, default: true)
   *
   * @param aProperties the specifications for the widget.
   * @return a wrapper around the created widget (see getWidget)
   */
  createWidget(aProperties) {
    return CustomizableUIInternal.wrapWidget(
      CustomizableUIInternal.createWidget(aProperties)
    );
  },
  /**
   * Destroy a widget
   *
   * If the widget is part of the default placements in an area, this will
   * remove it from there. It will also remove any DOM instances. However,
   * it will keep the widget in the placements for whatever area it was
   * in at the time. You can remove it from there yourself by calling
   * CustomizableUI.removeWidgetFromArea(aWidgetId).
   *
   * @param aWidgetId the ID of the widget to destroy
   */
  destroyWidget(aWidgetId) {
    CustomizableUIInternal.destroyWidget(aWidgetId);
  },
  /**
   * Get a wrapper object with information about the widget.
   * The object provides the following properties
   * (all read-only unless otherwise indicated):
   *
   * - id:            the widget's ID;
   * - type:          the type of widget (button, view, custom). For
   *                  XUL-provided widgets, this is always 'custom';
   * - provider:      the provider type of the widget, id est one of
   *                  PROVIDER_API or PROVIDER_XUL;
   * - forWindow(w):  a method to obtain a single window wrapper for a widget,
   *                  in the window w passed as the only argument;
   * - instances:     an array of all instances (single window wrappers)
   *                  of the widget. This array is NOT live;
   * - areaType:      the type of the widget's current area
   * - isGroup:       true; will be false for wrappers around single widget nodes;
   * - source:        for API-provided widgets, whether they are built-in to
   *                  Firefox or add-on-provided;
   * - disabled:      for API-provided widgets, whether the widget is currently
   *                  disabled. NB: this property is writable, and will toggle
   *                  all the widgets' nodes' disabled states;
   * - label:         for API-provied widgets, the label of the widget;
   * - tooltiptext:   for API-provided widgets, the tooltip of the widget;
   * - showInPrivateBrowsing: for API-provided widgets, whether the widget is
   *                          visible in private browsing;
   *
   * Single window wrappers obtained through forWindow(someWindow) or from the
   * instances array have the following properties
   * (all read-only unless otherwise indicated):
   *
   * - id:            the widget's ID;
   * - type:          the type of widget (button, view, custom). For
   *                  XUL-provided widgets, this is always 'custom';
   * - provider:      the provider type of the widget, id est one of
   *                  PROVIDER_API or PROVIDER_XUL;
   * - node:          reference to the corresponding DOM node;
   * - anchor:        the anchor on which to anchor panels opened from this
   *                  node. This will point to the overflow chevron on
   *                  overflowable toolbars if and only if your widget node
   *                  is overflowed, to the anchor for the panel menu
   *                  if your widget is inside the panel menu, and to the
   *                  node itself in all other cases;
   * - overflowed:    boolean indicating whether the node is currently in the
   *                  overflow panel of the toolbar;
   * - isGroup:       false; will be true for the group widget;
   * - label:         for API-provided widgets, convenience getter for the
   *                  label attribute of the DOM node;
   * - tooltiptext:   for API-provided widgets, convenience getter for the
   *                  tooltiptext attribute of the DOM node;
   * - disabled:      for API-provided widgets, convenience getter *and setter*
   *                  for the disabled state of this single widget. Note that
   *                  you may prefer to use the group wrapper's getter/setter
   *                  instead.
   *
   * @param aWidgetId the ID of the widget whose information you need
   * @return a wrapper around the widget as described above, or null if the
   *         widget is known not to exist (anymore). NB: non-null return
   *         is no guarantee the widget exists because we cannot know in
   *         advance if a XUL widget exists or not.
   */
  getWidget(aWidgetId) {
    return CustomizableUIInternal.wrapWidget(aWidgetId);
  },
  /**
   * Get an array of widget wrappers (see getWidget) for all the widgets
   * which are currently not in any area (so which are in the palette).
   *
   * @param aWindowPalette the palette (and by extension, the window) in which
   *                       CustomizableUI should look. This matters because of
   *                       course XUL-provided widgets could be available in
   *                       some windows but not others, and likewise
   *                       API-provided widgets might not exist in a private
   *                       window (because of the showInPrivateBrowsing
   *                       property).
   *
   * @return an array of widget wrappers (see getWidget)
   */
  getUnusedWidgets(aWindowPalette) {
    return CustomizableUIInternal.getUnusedWidgets(aWindowPalette).map(
      CustomizableUIInternal.wrapWidget,
      CustomizableUIInternal
    );
  },
  /**
   * Get an array of all the widget IDs placed in an area. This is roughly
   * equivalent to fetching the currentset attribute and splitting by commas
   * in the legacy APIs. Modifying the array will not affect CustomizableUI.
   *
   * @param aArea the ID of the area whose placements you want to obtain.
   * @return an array containing the widget IDs that are in the area.
   *
   * NB: will throw if called too early (before placements have been fetched)
   *     or if the area is not currently known to CustomizableUI.
   */
  getWidgetIdsInArea(aArea) {
    if (!gAreas.has(aArea)) {
      throw new Error("Unknown customization area: " + aArea);
    }
    if (!gPlacements.has(aArea)) {
      throw new Error("Area not yet restored");
    }

    // We need to clone this, as we don't want to let consumers muck with placements
    return [...gPlacements.get(aArea)];
  },
  /**
   * Get an array of widget wrappers for all the widgets in an area. This is
   * the same as calling getWidgetIdsInArea and .map() ing the result through
   * CustomizableUI.getWidget. Careful: this means that if there are IDs in there
   * which don't have corresponding DOM nodes (like in the old-style currentset
   * attribute), there might be nulls in this array, or items for which
   * wrapper.forWindow(win) will return null.
   *
   * @param aArea the ID of the area whose widgets you want to obtain.
   * @return an array of widget wrappers and/or null values for the widget IDs
   *         placed in an area.
   *
   * NB: will throw if called too early (before placements have been fetched)
   *     or if the area is not currently known to CustomizableUI.
   */
  getWidgetsInArea(aArea) {
    return this.getWidgetIdsInArea(aArea).map(
      CustomizableUIInternal.wrapWidget,
      CustomizableUIInternal
    );
  },

  /**
   * Ensure the customizable widget that matches up with this view node
   * will get the right subview showing/shown/hiding/hidden events when
   * they fire.
   * @param aViewNode the view node to add listeners to if they haven't
   *                  been added already.
   */
  ensureSubviewListeners(aViewNode) {
    return CustomizableUIInternal.ensureSubviewListeners(aViewNode);
  },
  /**
   * Obtain an array of all the area IDs known to CustomizableUI.
   * This array is created for you, so is modifiable without CustomizableUI
   * being affected.
   */
  get areas() {
    return [...gAreas.keys()];
  },
  /**
   * Check what kind of area (toolbar or menu panel) an area is. This is
   * useful if you have a widget that needs to behave differently depending
   * on its location. Note that widget wrappers have a convenience getter
   * property (areaType) for this purpose.
   *
   * @param aArea the ID of the area whose type you want to know
   * @return TYPE_TOOLBAR or TYPE_MENU_PANEL depending on the area, null if
   *         the area is unknown.
   */
  getAreaType(aArea) {
    let area = gAreas.get(aArea);
    return area ? area.get("type") : null;
  },
  /**
   * Check if a toolbar is collapsed by default.
   *
   * @param aArea the ID of the area whose default-collapsed state you want to know.
   * @return `true` or `false` depending on the area, null if the area is unknown,
   *         or its collapsed state cannot normally be controlled by the user
   */
  isToolbarDefaultCollapsed(aArea) {
    let area = gAreas.get(aArea);
    return area ? area.get("defaultCollapsed") : null;
  },
  /**
   * Obtain the DOM node that is the customize target for an area in a
   * specific window.
   *
   * Areas can have a customization target that does not correspond to the
   * node itself. In particular, toolbars that have a customizationtarget
   * attribute set will have their customization target set to that node.
   * This means widgets will end up in the customization target, not in the
   * DOM node with the ID that corresponds to the area ID. This is useful
   * because it lets you have fixed content in a toolbar (e.g. the panel
   * menu item in the navbar) and have all the customizable widgets use
   * the customization target.
   *
   * Using this API yourself is discouraged; you should generally not need
   * to be asking for the DOM container node used for a particular area.
   * In particular, if you're wanting to check it in relation to a widget's
   * node, your DOM node might not be a direct child of the customize target
   * in a window if, for instance, the window is in customization mode, or if
   * this is an overflowable toolbar and the widget has been overflowed.
   *
   * @param aArea   the ID of the area whose customize target you want to have
   * @param aWindow the window where you want to fetch the DOM node.
   * @return the customize target DOM node for aArea in aWindow
   */
  getCustomizeTargetForArea(aArea, aWindow) {
    return CustomizableUIInternal.getCustomizeTargetForArea(aArea, aWindow);
  },
  /**
   * Reset the customization state back to its default.
   *
   * This is the nuclear option. You should never call this except if the user
   * explicitly requests it. Firefox does this when the user clicks the
   * "Restore Defaults" button in customize mode.
   */
  reset() {
    CustomizableUIInternal.reset();
  },

  /**
   * Undo the previous reset, can only be called immediately after a reset.
   * @return a promise that will be resolved when the operation is complete.
   */
  undoReset() {
    CustomizableUIInternal.undoReset();
  },

  /**
   * Remove a custom toolbar added in a previous version of Firefox or using
   * an add-on. NB: only works on the customizable toolbars generated by
   * the toolbox itself. Intended for use from CustomizeMode, not by
   * other consumers.
   * @param aToolbarId the ID of the toolbar to remove
   */
  removeExtraToolbar(aToolbarId) {
    CustomizableUIInternal.removeExtraToolbar(aToolbarId);
  },

  /**
   * Can the last Restore Defaults operation be undone.
   *
   * @return A boolean stating whether an undo of the
   *         Restore Defaults can be performed.
   */
  get canUndoReset() {
    return gUIStateBeforeReset.uiCustomizationState != null ||
           gUIStateBeforeReset.drawInTitlebar != null ||
           gUIStateBeforeReset.extraDragSpace != null ||
           gUIStateBeforeReset.currentTheme != null ||
           gUIStateBeforeReset.autoTouchMode != null ||
           gUIStateBeforeReset.uiDensity != null;
  },

  /**
   * Get the placement of a widget. This is by far the best way to obtain
   * information about what the state of your widget is. The internals of
   * this call are cheap (no DOM necessary) and you will know where the user
   * has put your widget.
   *
   * @param aWidgetId the ID of the widget whose placement you want to know
   * @return
   *   {
   *     area: "somearea", // The ID of the area where the widget is placed
   *     position: 42 // the index in the placements array corresponding to
   *                  // your widget.
   *   }
   *
   *   OR
   *
   *   null // if the widget is not placed anywhere (ie in the palette)
   */
  getPlacementOfWidget(aWidgetId, aOnlyRegistered = true, aDeadAreas = false) {
    return CustomizableUIInternal.getPlacementOfWidget(aWidgetId, aOnlyRegistered, aDeadAreas);
  },
  /**
   * Check if a widget can be removed from the area it's in.
   *
   * Note that if you're wanting to move the widget somewhere, you should
   * generally be checking canWidgetMoveToArea, because that will return
   * true if the widget is already in the area where you want to move it (!).
   *
   * NB: oh, also, this method might lie if the widget in question is a
   *     XUL-provided widget and there are no windows open, because it
   *     can obviously not check anything in this case. It will return
   *     true. You will be able to move the widget elsewhere. However,
   *     once the user reopens a window, the widget will move back to its
   *     'proper' area automagically.
   *
   * @param aWidgetId a widget ID or DOM node to check
   * @return true if the widget can be removed from its area,
   *          false otherwise.
   */
  isWidgetRemovable(aWidgetId) {
    return CustomizableUIInternal.isWidgetRemovable(aWidgetId);
  },
  /**
   * Check if a widget can be moved to a particular area. Like
   * isWidgetRemovable but better, because it'll return true if the widget
   * is already in the right area.
   *
   * @param aWidgetId the widget ID or DOM node you want to move somewhere
   * @param aArea     the area ID you want to move it to.
   * @return true if this is possible, false if it is not. The same caveats as
   *              for isWidgetRemovable apply, however, if no windows are open.
   */
  canWidgetMoveToArea(aWidgetId, aArea) {
    return CustomizableUIInternal.canWidgetMoveToArea(aWidgetId, aArea);
  },
  /**
   * Whether we're in a default state. Note that non-removable non-default
   * widgets and non-existing widgets are not taken into account in determining
   * whether we're in the default state.
   *
   * NB: this is a property with a getter. The getter is NOT cheap, because
   * it does smart things with non-removable non-default items, non-existent
   * items, and so forth. Please don't call unless necessary.
   */
  get inDefaultState() {
    return CustomizableUIInternal.inDefaultState;
  },

  /**
   * Set a toolbar's visibility state in all windows.
   * @param aToolbarId    the toolbar whose visibility should be adjusted
   * @param aIsVisible    whether the toolbar should be visible
   */
  setToolbarVisibility(aToolbarId, aIsVisible) {
    CustomizableUIInternal.setToolbarVisibility(aToolbarId, aIsVisible);
  },

  /**
   * Get a localized property off a (widget?) object.
   *
   * NB: this is unlikely to be useful unless you're in Firefox code, because
   *     this code uses the builtin widget stringbundle, and can't be told
   *     to use add-on-provided strings. It's mainly here as convenience for
   *     custom builtin widgets that build their own DOM but use the same
   *     stringbundle as the other builtin widgets.
   *
   * @param aWidget     the object whose property we should use to fetch a
   *                    localizable string;
   * @param aProp       the property on the object to use for the fetching;
   * @param aFormatArgs (optional) any extra arguments to use for a formatted
   *                    string;
   * @param aDef        (optional) the default to return if we don't find the
   *                    string in the stringbundle;
   *
   * @return the localized string, or aDef if the string isn't in the bundle.
   *         If no default is provided,
   *           if aProp exists on aWidget, we'll return that,
   *           otherwise we'll return the empty string
   *
   */
  getLocalizedProperty(aWidget, aProp, aFormatArgs, aDef) {
    return CustomizableUIInternal.getLocalizedProperty(aWidget, aProp,
      aFormatArgs, aDef);
  },
  /**
   * Utility function to detect, find and set a keyboard shortcut for a menuitem
   * or (toolbar)button.
   *
   * @param aShortcutNode the XUL node where the shortcut will be derived from;
   * @param aTargetNode   (optional) the XUL node on which the `shortcut`
   *                      attribute will be set. If NULL, the shortcut will be
   *                      set on aShortcutNode;
   */
  addShortcut(aShortcutNode, aTargetNode) {
    return CustomizableUIInternal.addShortcut(aShortcutNode, aTargetNode);
  },
  /**
   * Given a node, walk up to the first panel in its ancestor chain, and
   * close it.
   *
   * @param aNode a node whose panel should be closed;
   */
  hidePanelForNode(aNode) {
    CustomizableUIInternal.hidePanelForNode(aNode);
  },
  /**
   * Check if a widget is a "special" widget: a spring, spacer or separator.
   *
   * @param aWidgetId the widget ID to check.
   * @return true if the widget is 'special', false otherwise.
   */
  isSpecialWidget(aWidgetId) {
    return CustomizableUIInternal.isSpecialWidget(aWidgetId);
  },
  /**
   * Add listeners to a panel that will close it. For use from the menu panel
   * and overflowable toolbar implementations, unlikely to be useful for
   * consumers.
   *
   * @param aPanel the panel to which listeners should be attached.
   */
  addPanelCloseListeners(aPanel) {
    CustomizableUIInternal.addPanelCloseListeners(aPanel);
  },
  /**
   * Remove close listeners that have been added to a panel with
   * addPanelCloseListeners. For use from the menu panel and overflowable
   * toolbar implementations, unlikely to be useful for consumers.
   *
   * @param aPanel the panel from which listeners should be removed.
   */
  removePanelCloseListeners(aPanel) {
    CustomizableUIInternal.removePanelCloseListeners(aPanel);
  },
  /**
   * Notify listeners a widget is about to be dragged to an area. For use from
   * Customize Mode only, do not use otherwise.
   *
   * @param aWidgetId the ID of the widget that is being dragged to an area.
   * @param aArea     the ID of the area to which the widget is being dragged.
   */
  onWidgetDrag(aWidgetId, aArea) {
    CustomizableUIInternal.notifyListeners("onWidgetDrag", aWidgetId, aArea);
  },
  /**
   * Notify listeners that a window is entering customize mode. For use from
   * Customize Mode only, do not use otherwise.
   * @param aWindow the window entering customize mode
   */
  notifyStartCustomizing(aWindow) {
    CustomizableUIInternal.notifyListeners("onCustomizeStart", aWindow);
  },
  /**
   * Notify listeners that a window is exiting customize mode. For use from
   * Customize Mode only, do not use otherwise.
   * @param aWindow the window exiting customize mode
   */
  notifyEndCustomizing(aWindow) {
    CustomizableUIInternal.notifyListeners("onCustomizeEnd", aWindow);
  },

  /**
   * Notify toolbox(es) of a particular event. If you don't pass aWindow,
   * all toolboxes will be notified. For use from Customize Mode only,
   * do not use otherwise.
   * @param aEvent the name of the event to send.
   * @param aDetails optional, the details of the event.
   * @param aWindow optional, the window in which to send the event.
   */
  dispatchToolboxEvent(aEvent, aDetails = {}, aWindow = null) {
    CustomizableUIInternal.dispatchToolboxEvent(aEvent, aDetails, aWindow);
  },

  /**
   * Check whether an area is overflowable.
   *
   * @param aAreaId the ID of an area to check for overflowable-ness
   * @return true if the area is overflowable, false otherwise.
   */
  isAreaOverflowable(aAreaId) {
    let area = gAreas.get(aAreaId);
    return area ? area.get("type") == this.TYPE_TOOLBAR && area.get("overflowable")
                : false;
  },
  /**
   * Obtain a string indicating the place of an element. This is intended
   * for use from customize mode; You should generally use getPlacementOfWidget
   * instead, which is cheaper because it does not use the DOM.
   *
   * @param aElement the DOM node whose place we need to check
   * @return "toolbar" if the node is in a toolbar, "panel" if it is in the
   *         menu panel, "palette" if it is in the (visible!) customization
   *         palette, undefined otherwise.
   */
  getPlaceForItem(aElement) {
    let place;
    let node = aElement;
    while (node && !place) {
      if (node.localName == "toolbar")
        place = "toolbar";
      else if (node.id == CustomizableUI.AREA_FIXED_OVERFLOW_PANEL)
        place = "menu-panel";
      else if (node.id == "customization-palette")
        place = "palette";

      node = node.parentNode;
    }
    return place;
  },

  /**
   * Check if a toolbar is builtin or not.
   * @param aToolbarId the ID of the toolbar you want to check
   */
  isBuiltinToolbar(aToolbarId) {
    return CustomizableUIInternal._builtinToolbars.has(aToolbarId);
  },

  /**
   * Create an instance of a spring, spacer or separator.
   * @param aId       the type of special widget (spring, spacer or separator)
   * @param aDocument the document in which to create it.
   */
  createSpecialWidget(aId, aDocument) {
    return CustomizableUIInternal.createSpecialWidget(aId, aDocument);
  },

  /**
   * Fills a submenu with menu items.
   * @param aMenuItems the menu items to display.
   * @param aSubview   the subview to fill.
   */
  fillSubviewFromMenuItems(aMenuItems, aSubview) {
    let attrs = ["oncommand", "onclick", "label", "key", "disabled",
                 "command", "observes", "hidden", "class", "origin",
                 "image", "checked", "style"];

    let doc = aSubview.ownerDocument;
    let fragment = doc.createDocumentFragment();
    for (let menuChild of aMenuItems) {
      if (menuChild.hidden)
        continue;

      let subviewItem;
      if (menuChild.localName == "menuseparator") {
        // Don't insert duplicate or leading separators. This can happen if there are
        // menus (which we don't copy) above the separator.
        if (!fragment.lastChild || fragment.lastChild.localName == "menuseparator") {
          continue;
        }
        subviewItem = doc.createElementNS(kNSXUL, "menuseparator");
      } else if (menuChild.localName == "menuitem") {
        subviewItem = doc.createElementNS(kNSXUL, "toolbarbutton");
        CustomizableUI.addShortcut(menuChild, subviewItem);

        let item = menuChild;
        if (!item.hasAttribute("onclick")) {
          subviewItem.addEventListener("click", event => {
            let newEvent = new doc.defaultView.MouseEvent(event.type, event);
            item.dispatchEvent(newEvent);
          });
        }

        if (!item.hasAttribute("oncommand")) {
          subviewItem.addEventListener("command", event => {
            let newEvent = doc.createEvent("XULCommandEvent");
            newEvent.initCommandEvent(
              event.type, event.bubbles, event.cancelable, event.view,
              event.detail, event.ctrlKey, event.altKey, event.shiftKey,
              event.metaKey, event.sourceEvent, 0);
            item.dispatchEvent(newEvent);
          });
        }
      } else {
        continue;
      }
      for (let attr of attrs) {
        let attrVal = menuChild.getAttribute(attr);
        if (attrVal)
          subviewItem.setAttribute(attr, attrVal);
      }
      // We do this after so the .subviewbutton class doesn't get overriden.
      if (menuChild.localName == "menuitem") {
        subviewItem.classList.add("subviewbutton");
      }
      fragment.appendChild(subviewItem);
    }
    aSubview.appendChild(fragment);
  },

  /**
   * A helper function for clearing subviews.
   * @param aSubview the subview to clear.
   */
  clearSubview(aSubview) {
    let parent = aSubview.parentNode;
    // We'll take the container out of the document before cleaning it out
    // to avoid reflowing each time we remove something.
    parent.removeChild(aSubview);

    while (aSubview.firstChild) {
      aSubview.firstChild.remove();
    }

    parent.appendChild(aSubview);
  },
};
Object.freeze(this.CustomizableUI);
Object.freeze(this.CustomizableUI.windows);

/**
 * All external consumers of widgets are really interacting with these wrappers
 * which provide a common interface.
 */

/**
 * WidgetGroupWrapper is the common interface for interacting with an entire
 * widget group - AKA, all instances of a widget across a series of windows.
 * This particular wrapper is only used for widgets created via the provider
 * API.
 */
function WidgetGroupWrapper(aWidget) {
  this.isGroup = true;

  const kBareProps = ["id", "source", "type", "disabled", "label", "tooltiptext",
                      "showInPrivateBrowsing", "viewId"];
  for (let prop of kBareProps) {
    let propertyName = prop;
    this.__defineGetter__(propertyName, () => aWidget[propertyName]);
  }

  this.__defineGetter__("provider", () => CustomizableUI.PROVIDER_API);

  this.__defineSetter__("disabled", function(aValue) {
    aValue = !!aValue;
    aWidget.disabled = aValue;
    for (let [, instance] of aWidget.instances) {
      instance.disabled = aValue;
    }
  });

  this.forWindow = function WidgetGroupWrapper_forWindow(aWindow) {
    let wrapperMap;
    if (!gSingleWrapperCache.has(aWindow)) {
      wrapperMap = new Map();
      gSingleWrapperCache.set(aWindow, wrapperMap);
    } else {
      wrapperMap = gSingleWrapperCache.get(aWindow);
    }
    if (wrapperMap.has(aWidget.id)) {
      return wrapperMap.get(aWidget.id);
    }

    let instance = aWidget.instances.get(aWindow.document);
    if (!instance &&
        (aWidget.showInPrivateBrowsing || !PrivateBrowsingUtils.isWindowPrivate(aWindow))) {
      instance = CustomizableUIInternal.buildWidget(aWindow.document,
                                                    aWidget);
    }

    let wrapper = new WidgetSingleWrapper(aWidget, instance);
    wrapperMap.set(aWidget.id, wrapper);
    return wrapper;
  };

  this.__defineGetter__("instances", function() {
    // Can't use gBuildWindows here because some areas load lazily:
    let placement = CustomizableUIInternal.getPlacementOfWidget(aWidget.id);
    if (!placement) {
      return [];
    }
    let area = placement.area;
    let buildAreas = gBuildAreas.get(area);
    if (!buildAreas) {
      return [];
    }
    return Array.from(buildAreas, (node) => this.forWindow(node.ownerGlobal));
  });

  this.__defineGetter__("areaType", function() {
    let areaProps = gAreas.get(aWidget.currentArea);
    return areaProps && areaProps.get("type");
  });

  Object.freeze(this);
}

/**
 * A WidgetSingleWrapper is a wrapper around a single instance of a widget in
 * a particular window.
 */
function WidgetSingleWrapper(aWidget, aNode) {
  this.isGroup = false;

  this.node = aNode;
  this.provider = CustomizableUI.PROVIDER_API;

  const kGlobalProps = ["id", "type"];
  for (let prop of kGlobalProps) {
    this[prop] = aWidget[prop];
  }

  const kNodeProps = ["label", "tooltiptext"];
  for (let prop of kNodeProps) {
    let propertyName = prop;
    // Look at the node for these, instead of the widget data, to ensure the
    // wrapper always reflects this live instance.
    this.__defineGetter__(propertyName,
                          () => aNode.getAttribute(propertyName));
  }

  this.__defineGetter__("disabled", () => aNode.disabled);
  this.__defineSetter__("disabled", function(aValue) {
    aNode.disabled = !!aValue;
  });

  this.__defineGetter__("anchor", function() {
    let anchorId;
    // First check for an anchor for the area:
    let placement = CustomizableUIInternal.getPlacementOfWidget(aWidget.id);
    if (placement) {
      anchorId = gAreas.get(placement.area).get("anchor");
    }
    if (!anchorId) {
      anchorId = aNode.getAttribute("cui-anchorid");
    }

    return anchorId ? aNode.ownerDocument.getElementById(anchorId)
                    : aNode;
  });

  this.__defineGetter__("overflowed", function() {
    return aNode.getAttribute("overflowedItem") == "true";
  });

  Object.freeze(this);
}

/**
 * XULWidgetGroupWrapper is the common interface for interacting with an entire
 * widget group - AKA, all instances of a widget across a series of windows.
 * This particular wrapper is only used for widgets created via the old-school
 * XUL method (overlays, or programmatically injecting toolbaritems, or other
 * such things).
 */
// XXXunf Going to need to hook this up to some events to keep it all live.
function XULWidgetGroupWrapper(aWidgetId) {
  this.isGroup = true;
  this.id = aWidgetId;
  this.type = "custom";
  this.provider = CustomizableUI.PROVIDER_XUL;

  this.forWindow = function XULWidgetGroupWrapper_forWindow(aWindow) {
    let wrapperMap;
    if (!gSingleWrapperCache.has(aWindow)) {
      wrapperMap = new Map();
      gSingleWrapperCache.set(aWindow, wrapperMap);
    } else {
      wrapperMap = gSingleWrapperCache.get(aWindow);
    }
    if (wrapperMap.has(aWidgetId)) {
      return wrapperMap.get(aWidgetId);
    }

    let instance = aWindow.document.getElementById(aWidgetId);
    if (!instance) {
      // Toolbar palettes aren't part of the document, so elements in there
      // won't be found via document.getElementById().
      instance = aWindow.gNavToolbox.palette.getElementsByAttribute("id", aWidgetId)[0];
    }

    let wrapper = new XULWidgetSingleWrapper(aWidgetId, instance, aWindow.document);
    wrapperMap.set(aWidgetId, wrapper);
    return wrapper;
  };

  this.__defineGetter__("areaType", function() {
    let placement = CustomizableUIInternal.getPlacementOfWidget(aWidgetId);
    if (!placement) {
      return null;
    }

    let areaProps = gAreas.get(placement.area);
    return areaProps && areaProps.get("type");
  });

  this.__defineGetter__("instances", function() {
    return Array.from(gBuildWindows, (wins) => this.forWindow(wins[0]));
  });

  Object.freeze(this);
}

/**
 * A XULWidgetSingleWrapper is a wrapper around a single instance of a XUL
 * widget in a particular window.
 */
function XULWidgetSingleWrapper(aWidgetId, aNode, aDocument) {
  this.isGroup = false;

  this.id = aWidgetId;
  this.type = "custom";
  this.provider = CustomizableUI.PROVIDER_XUL;

  let weakDoc = Cu.getWeakReference(aDocument);
  // If we keep a strong ref, the weak ref will never die, so null it out:
  aDocument = null;

  this.__defineGetter__("node", function() {
    // If we've set this to null (further down), we're sure there's nothing to
    // be gotten here, so bail out early:
    if (!weakDoc) {
      return null;
    }
    if (aNode) {
      // Return the last known node if it's still in the DOM...
      if (aNode.ownerDocument.contains(aNode)) {
        return aNode;
      }
      // ... or the toolbox
      let toolbox = aNode.ownerGlobal.gNavToolbox;
      if (toolbox && toolbox.palette && aNode.parentNode == toolbox.palette) {
        return aNode;
      }
      // If it isn't, clear the cached value and fall through to the "slow" case:
      aNode = null;
    }

    let doc = weakDoc.get();
    if (doc) {
      // Store locally so we can cache the result:
      aNode = CustomizableUIInternal.findWidgetInWindow(aWidgetId, doc.defaultView);
      return aNode;
    }
    // The weakref to the document is dead, we're done here forever more:
    weakDoc = null;
    return null;
  });

  this.__defineGetter__("anchor", function() {
    let anchorId;
    // First check for an anchor for the area:
    let placement = CustomizableUIInternal.getPlacementOfWidget(aWidgetId);
    if (placement) {
      anchorId = gAreas.get(placement.area).get("anchor");
    }

    let node = this.node;
    if (!anchorId && node) {
      anchorId = node.getAttribute("cui-anchorid");
    }

    return (anchorId && node) ? node.ownerDocument.getElementById(anchorId) : node;
  });

  this.__defineGetter__("overflowed", function() {
    let node = this.node;
    if (!node) {
      return false;
    }
    return node.getAttribute("overflowedItem") == "true";
  });

  Object.freeze(this);
}

const LAZY_RESIZE_INTERVAL_MS = 200;
const OVERFLOW_PANEL_HIDE_DELAY_MS = 500;

function OverflowableToolbar(aToolbarNode) {
  this._toolbar = aToolbarNode;
  this._collapsed = new Map();
  this._enabled = true;

  this._toolbar.setAttribute("overflowable", "true");
  let doc = this._toolbar.ownerDocument;
  this._target = this._toolbar.customizationTarget;
  this._list = doc.getElementById(this._toolbar.getAttribute("overflowtarget"));
  this._list.toolbox = this._toolbar.toolbox;
  this._list.customizationTarget = this._list;

  let window = this._toolbar.ownerGlobal;
  if (window.gBrowserInit.delayedStartupFinished) {
    this.init();
  } else {
    Services.obs.addObserver(this, "browser-delayed-startup-finished");
  }
}

OverflowableToolbar.prototype = {
  initialized: false,
  _forceOnOverflow: false,
  _addedListener: false,

  observe(aSubject, aTopic, aData) {
    if (aTopic == "browser-delayed-startup-finished" &&
        aSubject == this._toolbar.ownerGlobal) {
      Services.obs.removeObserver(this, "browser-delayed-startup-finished");
      this.init();
    }
  },

  init() {
    let doc = this._toolbar.ownerDocument;
    let window = doc.defaultView;
    window.addEventListener("resize", this);
    window.gNavToolbox.addEventListener("customizationstarting", this);
    window.gNavToolbox.addEventListener("aftercustomization", this);

    let chevronId = this._toolbar.getAttribute("overflowbutton");
    this._chevron = doc.getElementById(chevronId);
    this._chevron.addEventListener("mousedown", this);
    this._chevron.addEventListener("dragover", this);
    this._chevron.addEventListener("dragend", this);

    let panelId = this._toolbar.getAttribute("overflowpanel");
    this._panel = doc.getElementById(panelId);
    this._panel.addEventListener("popuphiding", this);
    CustomizableUIInternal.addPanelCloseListeners(this._panel);

    CustomizableUI.addListener(this);
    this._addedListener = true;

    // The 'overflow' event may have been fired before init was called.
    if (this._toolbar.overflowedDuringConstruction) {
      this.onOverflow(this._toolbar.overflowedDuringConstruction);
      this._toolbar.overflowedDuringConstruction = null;
    }

    this.initialized = true;
  },

  uninit() {
    this._toolbar.removeEventListener("overflow", this._toolbar);
    this._toolbar.removeEventListener("underflow", this._toolbar);
    this._toolbar.removeAttribute("overflowable");

    if (!this.initialized) {
      Services.obs.removeObserver(this, "browser-delayed-startup-finished");
      return;
    }

    this._disable();

    let window = this._toolbar.ownerGlobal;
    window.removeEventListener("resize", this);
    window.gNavToolbox.removeEventListener("customizationstarting", this);
    window.gNavToolbox.removeEventListener("aftercustomization", this);
    this._chevron.removeEventListener("command", this);
    this._chevron.removeEventListener("dragover", this);
    this._chevron.removeEventListener("dragend", this);
    this._panel.removeEventListener("popuphiding", this);
    CustomizableUI.removeListener(this);
    this._addedListener = false;
    CustomizableUIInternal.removePanelCloseListeners(this._panel);
  },

  handleEvent(aEvent) {
    switch (aEvent.type) {
      case "aftercustomization":
        this._enable();
        break;
      case "mousedown":
        if (aEvent.button != 0) {
          break;
        }
        if (aEvent.target == this._chevron) {
          this._onClickChevron(aEvent);
        } else {
          PanelMultiView.hidePopup(this._panel);
        }
        break;
      case "customizationstarting":
        this._disable();
        break;
      case "dragover":
        if (this._enabled) {
          this._showWithTimeout();
        }
        break;
      case "dragend":
        PanelMultiView.hidePopup(this._panel);
        break;
      case "popuphiding":
        this._onPanelHiding(aEvent);
        break;
      case "resize":
        this._onResize(aEvent);
    }
  },

  show(aEvent) {
    if (this._panel.state == "open") {
      return Promise.resolve();
    }
    return new Promise(resolve => {
      let doc = this._panel.ownerDocument;
      this._panel.hidden = false;
      let multiview = this._panel.querySelector("panelmultiview");
      let mainViewId = multiview.getAttribute("mainViewId");
      let mainView = doc.getElementById(mainViewId);
      let contextMenu = doc.getElementById(mainView.getAttribute("context"));
      gELS.addSystemEventListener(contextMenu, "command", this, true);
      let anchor = doc.getAnonymousElementByAttribute(this._chevron, "class", "toolbarbutton-icon");
      // Ensure we update the gEditUIVisible flag when opening the popup, in
      // case the edit controls are in it.
      this._panel.addEventListener("popupshowing", () => doc.defaultView.updateEditUIVisibility(), {once: true});
      PanelMultiView.openPopup(this._panel, anchor || this._chevron, {
        triggerEvent: aEvent,
      }).catch(Cu.reportError);
      this._chevron.open = true;

      this._panel.addEventListener("popupshown", () => {
        this._panel.addEventListener("dragover", this);
        this._panel.addEventListener("dragend", this);
        // Wait until the next tick to resolve so all popupshown
        // handlers have a chance to run before our promise resolution
        // handlers do.
        Services.tm.dispatchToMainThread(resolve);
      }, {once: true});
    });
  },

  _onClickChevron(aEvent) {
    if (this._chevron.open) {
      this._chevron.open = false;
      PanelMultiView.hidePopup(this._panel);
    } else if (this._panel.state != "hiding" && !this._chevron.disabled) {
      this.show(aEvent);
    }
  },

  _onPanelHiding(aEvent) {
    if (aEvent.target != this._panel) {
      // Ignore context menus, <select> popups, etc.
      return;
    }
    this._chevron.open = false;
    this._panel.removeEventListener("dragover", this);
    this._panel.removeEventListener("dragend", this);
    let doc = aEvent.target.ownerDocument;
    doc.defaultView.updateEditUIVisibility();
    let contextMenuId = this._panel.getAttribute("context");
    if (contextMenuId) {
      let contextMenu = doc.getElementById(contextMenuId);
      gELS.removeSystemEventListener(contextMenu, "command", this, true);
    }
  },

  onOverflow(aEvent) {
    // The rangeParent check is here because of bug 1111986 and ensuring that
    // overflow events from the bookmarks toolbar items or similar things that
    // manage their own overflow don't trigger an overflow on the entire toolbar
    if (!this._enabled ||
        (aEvent && aEvent.target != this._toolbar.customizationTarget) ||
        (aEvent && aEvent.rangeParent))
      return;

    let child = this._target.lastChild;

    while (child && this._target.scrollLeftMin != this._target.scrollLeftMax) {
      let prevChild = child.previousSibling;

      if (child.getAttribute("overflows") != "false") {
        this._collapsed.set(child.id, this._target.clientWidth);
        child.setAttribute("overflowedItem", true);
        child.setAttribute("cui-anchorid", this._chevron.id);
        CustomizableUIInternal.ensureButtonContextMenu(child, this._toolbar, true);
        CustomizableUIInternal.notifyListeners("onWidgetOverflow", child, this._target);

        this._list.insertBefore(child, this._list.firstChild);
        if (!this._addedListener) {
          CustomizableUI.addListener(this);
        }
        if (!CustomizableUI.isSpecialWidget(child.id)) {
          this._toolbar.setAttribute("overflowing", "true");
        }
      }
      child = prevChild;
    }

    let win = this._target.ownerGlobal;
    win.UpdateUrlbarSearchSplitterState();
  },

  _onResize(aEvent) {
    if (!this._lazyResizeHandler) {
      this._lazyResizeHandler = new DeferredTask(this._onLazyResize.bind(this),
                                                 LAZY_RESIZE_INTERVAL_MS, 0);
    }
    this._lazyResizeHandler.arm();
  },

  _moveItemsBackToTheirOrigin(shouldMoveAllItems) {
    let placements = gPlacements.get(this._toolbar.id);
    while (this._list.firstChild) {
      let child = this._list.firstChild;
      let minSize = this._collapsed.get(child.id);

      if (!shouldMoveAllItems &&
          minSize &&
          this._target.clientWidth <= minSize) {
        break;
      }

      this._collapsed.delete(child.id);
      let beforeNodeIndex = placements.indexOf(child.id) + 1;
      // If this is a skipintoolbarset item, meaning it doesn't occur in the placements list,
      // we're inserting it at the end. This will mean first-in, first-out (more or less)
      // leading to as little change in order as possible.
      if (beforeNodeIndex == 0) {
        beforeNodeIndex = placements.length;
      }
      let inserted = false;
      for (; beforeNodeIndex < placements.length; beforeNodeIndex++) {
        let beforeNode = this._target.getElementsByAttribute("id", placements[beforeNodeIndex])[0];
        // Unfortunately, XUL add-ons can mess with nodes after they are inserted,
        // and this breaks the following code if the button isn't where we expect
        // it to be (ie not a child of the target). In this case, ignore the node.
        if (beforeNode && this._target == beforeNode.parentElement) {
          this._target.insertBefore(child, beforeNode);
          inserted = true;
          break;
        }
      }
      if (!inserted) {
        this._target.appendChild(child);
      }
      child.removeAttribute("cui-anchorid");
      child.removeAttribute("overflowedItem");
      CustomizableUIInternal.ensureButtonContextMenu(child, this._target);
      CustomizableUIInternal.notifyListeners("onWidgetUnderflow", child, this._target);
    }

    let win = this._target.ownerGlobal;
    win.UpdateUrlbarSearchSplitterState();

    let collapsedWidgetIds = Array.from(this._collapsed.keys());
    if (collapsedWidgetIds.every(w => CustomizableUI.isSpecialWidget(w))) {
      this._toolbar.removeAttribute("overflowing");
    }
    if (this._addedListener && !this._collapsed.size) {
      CustomizableUI.removeListener(this);
      this._addedListener = false;
    }
  },

  _onLazyResize() {
    if (!this._enabled)
      return;

    if (this._target.scrollLeftMin != this._target.scrollLeftMax) {
      this.onOverflow();
    } else {
      this._moveItemsBackToTheirOrigin();
    }
  },

  _disable() {
    this._enabled = false;
    this._moveItemsBackToTheirOrigin(true);
    if (this._lazyResizeHandler) {
      this._lazyResizeHandler.disarm();
    }
  },

  _enable() {
    this._enabled = true;
    this.onOverflow();
  },

  onWidgetBeforeDOMChange(aNode, aNextNode, aContainer) {
    if (aContainer != this._target && aContainer != this._list) {
      return;
    }
    // When we (re)move an item, update all the items that come after it in the list
    // with the minsize *of the item before the to-be-removed node*. This way, we
    // ensure that we try to move items back as soon as that's possible.
    if (aNode.parentNode == this._list) {
      let updatedMinSize;
      if (aNode.previousSibling) {
        updatedMinSize = this._collapsed.get(aNode.previousSibling.id);
      } else {
        // Force (these) items to try to flow back into the bar:
        updatedMinSize = 1;
      }
      let nextItem = aNode.nextSibling;
      while (nextItem) {
        this._collapsed.set(nextItem.id, updatedMinSize);
        nextItem = nextItem.nextSibling;
      }
    }
  },

  onWidgetAfterDOMChange(aNode, aNextNode, aContainer) {
    if (aContainer != this._target && aContainer != this._list) {
      return;
    }

    let nowInBar = aNode.parentNode == aContainer;
    let nowOverflowed = aNode.parentNode == this._list;
    let wasOverflowed = this._collapsed.has(aNode.id);

    // If this wasn't overflowed before...
    if (!wasOverflowed) {
      // ... but it is now, then we added to the overflow panel. Exciting stuff:
      if (nowOverflowed) {
        // NB: we're guaranteed that it has a previousSibling, because if it didn't,
        // we would have added it to the toolbar instead. See getOverflowedNextNode.
        let prevId = aNode.previousSibling.id;
        let minSize = this._collapsed.get(prevId);
        this._collapsed.set(aNode.id, minSize);
        aNode.setAttribute("cui-anchorid", this._chevron.id);
        aNode.setAttribute("overflowedItem", true);
        CustomizableUIInternal.ensureButtonContextMenu(aNode, aContainer, true);
        CustomizableUIInternal.notifyListeners("onWidgetOverflow", aNode, this._target);
      } else if (!nowInBar) {
        // If it is not overflowed and not in the toolbar, and was not overflowed
        // either, it moved out of the toolbar. That means there's now space in there!
        // Let's try to move stuff back:
        this._moveItemsBackToTheirOrigin(true);
      }
      // If it's in the toolbar now, then we don't care. An overflow event may
      // fire afterwards; that's ok!
    } else if (!nowOverflowed) {
      // If it used to be overflowed...
      // ... and isn't anymore, let's remove our bookkeeping:
      this._collapsed.delete(aNode.id);
      aNode.removeAttribute("cui-anchorid");
      aNode.removeAttribute("overflowedItem");
      CustomizableUIInternal.ensureButtonContextMenu(aNode, aContainer);
      CustomizableUIInternal.notifyListeners("onWidgetUnderflow", aNode, this._target);

      let collapsedWidgetIds = Array.from(this._collapsed.keys());
      if (collapsedWidgetIds.every(w => CustomizableUI.isSpecialWidget(w))) {
        this._toolbar.removeAttribute("overflowing");
      }
      if (this._addedListener && !this._collapsed.size) {
        CustomizableUI.removeListener(this);
        this._addedListener = false;
      }
    } else if (aNode.previousSibling) {
      // but if it still is, it must have changed places. Bookkeep:
      let prevId = aNode.previousSibling.id;
      let minSize = this._collapsed.get(prevId);
      this._collapsed.set(aNode.id, minSize);
    } else {
      // If it's now the first item in the overflow list,
      // maybe we can return it:
      this._moveItemsBackToTheirOrigin();
    }
  },

  findOverflowedInsertionPoints(aNode) {
    let newNodeCanOverflow = aNode.getAttribute("overflows") != "false";
    let areaId = this._toolbar.id;
    let placements = gPlacements.get(areaId);
    let nodeIndex = placements.indexOf(aNode.id);
    let nodeBeforeNewNodeIsOverflown = false;

    let loopIndex = -1;
    // Loop through placements to find where to insert this item.
    // As soon as we find an overflown widget, we will only
    // insert in the overflow panel (this is why we check placements
    // before the desired location for the new node). Once we pass
    // the desired location of the widget, we look for placement ids
    // that actually have DOM equivalents to insert before. If all
    // else fails, we insert at the end of either the overflow list
    // or the toolbar target.
    while (++loopIndex < placements.length) {
      let nextNodeId = placements[loopIndex];
      if (loopIndex > nodeIndex) {
        let nextNode = aNode.ownerDocument.getElementById(nextNodeId);
        // If the node we're inserting can overflow, and the next node
        // in the toolbar is overflown, we should insert this node
        // in the overflow panel before it.
        if (newNodeCanOverflow && this._collapsed.has(nextNodeId) &&
            nextNode && nextNode.parentNode == this._list) {
          return [this._list, nextNode];
        }
        // Otherwise (if either we can't overflow, or the previous node
        // wasn't overflown), and the next node is in the toolbar itself,
        // insert the node in the toolbar.
        if ((!nodeBeforeNewNodeIsOverflown || !newNodeCanOverflow) && nextNode &&
            (nextNode.parentNode == this._target ||
             // Also check if the next node is in a customization wrapper
             // (toolbarpaletteitem). We don't need to do this for the
             // overflow case because overflow is disabled in customize mode.
             (nextNode.parentNode.localName == "toolbarpaletteitem" &&
              nextNode.parentNode.parentNode == this._target))) {
          return [this._target, nextNode];
        }
      } else if (loopIndex < nodeIndex && this._collapsed.has(nextNodeId)) {
        nodeBeforeNewNodeIsOverflown = true;
      }
    }

    let containerForAppending = (this._collapsed.size && newNodeCanOverflow) ?
                                this._list : this._target;
    return [containerForAppending, null];
  },

  getContainerFor(aNode) {
    if (aNode.getAttribute("overflowedItem") == "true") {
      return this._list;
    }
    return this._target;
  },

  _hideTimeoutId: null,
  _showWithTimeout() {
    this.show().then(() => {
      let window = this._toolbar.ownerGlobal;
      if (this._hideTimeoutId) {
        window.clearTimeout(this._hideTimeoutId);
      }
      this._hideTimeoutId = window.setTimeout(() => {
        if (!this._panel.firstChild.matches(":hover")) {
          PanelMultiView.hidePopup(this._panel);
        }
      }, OVERFLOW_PANEL_HIDE_DELAY_MS);
    });
  },
};

CustomizableUIInternal.initialize();
PK
!<v%Q:��modules/CustomizableWidgets.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = ["CustomizableWidgets"];

ChromeUtils.import("resource:///modules/CustomizableUI.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/AppConstants.jsm");

XPCOMUtils.defineLazyModuleGetters(this, {
  BrowserUITelemetry: "resource:///modules/BrowserUITelemetry.jsm",
  PanelView: "resource:///modules/PanelMultiView.jsm",
  PlacesUtils: "resource://gre/modules/PlacesUtils.jsm",
  PlacesUIUtils: "resource:///modules/PlacesUIUtils.jsm",
  RecentlyClosedTabsAndWindowsMenuUtils: "resource:///modules/sessionstore/RecentlyClosedTabsAndWindowsMenuUtils.jsm",
  ShortcutUtils: "resource://gre/modules/ShortcutUtils.jsm",
  CharsetMenu: "resource://gre/modules/CharsetMenu.jsm",
  PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
  Sanitizer: "resource:///modules/Sanitizer.jsm",
  SyncedTabs: "resource://services-sync/SyncedTabs.jsm",
});

XPCOMUtils.defineLazyGetter(this, "CharsetBundle", function() {
  const kCharsetBundle = "chrome://global/locale/charsetMenu.properties";
  return Services.strings.createBundle(kCharsetBundle);
});
XPCOMUtils.defineLazyGetter(this, "BrandBundle", function() {
  const kBrandBundle = "chrome://branding/locale/brand.properties";
  return Services.strings.createBundle(kBrandBundle);
});

const kNSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const kPrefCustomizationDebug = "browser.uiCustomization.debug";

XPCOMUtils.defineLazyGetter(this, "log", () => {
  let scope = {};
  ChromeUtils.import("resource://gre/modules/Console.jsm", scope);
  let debug = Services.prefs.getBoolPref(kPrefCustomizationDebug, false);
  let consoleOptions = {
    maxLogLevel: debug ? "all" : "log",
    prefix: "CustomizableWidgets",
  };
  return new scope.ConsoleAPI(consoleOptions);
});



function setAttributes(aNode, aAttrs) {
  let doc = aNode.ownerDocument;
  for (let [name, value] of Object.entries(aAttrs)) {
    if (!value) {
      if (aNode.hasAttribute(name))
        aNode.removeAttribute(name);
    } else {
      if (name == "shortcutId") {
        continue;
      }
      if (name == "label" || name == "tooltiptext") {
        let stringId = (typeof value == "string") ? value : name;
        let additionalArgs = [];
        if (aAttrs.shortcutId) {
          let shortcut = doc.getElementById(aAttrs.shortcutId);
          if (shortcut) {
            additionalArgs.push(ShortcutUtils.prettifyShortcut(shortcut));
          }
        }
        value = CustomizableUI.getLocalizedProperty({id: aAttrs.id}, stringId, additionalArgs);
      }
      aNode.setAttribute(name, value);
    }
  }
}

const CustomizableWidgets = [
  {
    id: "history-panelmenu",
    type: "view",
    viewId: "PanelUI-history",
    shortcutId: "key_gotoHistory",
    tooltiptext: "history-panelmenu.tooltiptext2",
    recentlyClosedTabsPanel: "appMenu-library-recentlyClosedTabs",
    recentlyClosedWindowsPanel: "appMenu-library-recentlyClosedWindows",
    handleEvent(event) {
      switch (event.type) {
        case "PanelMultiViewHidden":
          this.onPanelMultiViewHidden(event);
          break;
        case "ViewShowing":
          this.onSubViewShowing(event);
          break;
        default:
          throw new Error(`Unsupported event for '${this.id}'`);
      }
    },
    onViewShowing(event) {
      if (this._panelMenuView)
        return;

      let panelview = event.target;
      let document = panelview.ownerDocument;
      let window = document.defaultView;

      // We restrict the amount of results to 42. Not 50, but 42. Why? Because 42.
      let query = "place:queryType=" + Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY +
        "&sort=" + Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING +
        "&maxResults=42&excludeQueries=1";

      this._panelMenuView = new window.PlacesPanelview(document.getElementById("appMenu_historyMenu"),
        panelview, query);
      // When either of these sub-subviews show, populate them with recently closed
      // objects data.
      document.getElementById(this.recentlyClosedTabsPanel).addEventListener("ViewShowing", this);
      document.getElementById(this.recentlyClosedWindowsPanel).addEventListener("ViewShowing", this);
      // When the popup is hidden (thus the panelmultiview node as well), make
      // sure to stop listening to PlacesDatabase updates.
      panelview.panelMultiView.addEventListener("PanelMultiViewHidden", this);
    },
    onViewHiding(event) {
      log.debug("History view is being hidden!");
    },
    onPanelMultiViewHidden(event) {
      let panelMultiView = event.target;
      let document = panelMultiView.ownerDocument;
      if (this._panelMenuView) {
        this._panelMenuView.uninit();
        delete this._panelMenuView;
        document.getElementById(this.recentlyClosedTabsPanel).removeEventListener("ViewShowing", this);
        document.getElementById(this.recentlyClosedWindowsPanel).removeEventListener("ViewShowing", this);
      }
      panelMultiView.removeEventListener("PanelMultiViewHidden", this);
    },
    onSubViewShowing(event) {
      let panelview = event.target;
      let document = event.target.ownerDocument;
      let window = document.defaultView;
      let viewType = panelview.id == this.recentlyClosedTabsPanel ? "Tabs" : "Windows";

      this._panelMenuView.clearAllContents(panelview);

      let utils = RecentlyClosedTabsAndWindowsMenuUtils;
      let method = `get${viewType}Fragment`;
      let fragment = utils[method](window, "toolbarbutton", true);
      let elementCount = fragment.childElementCount;
      this._panelMenuView._setEmptyPopupStatus(panelview, !elementCount);
      if (!elementCount)
        return;

      let body = document.createElement("vbox");
      body.className = "panel-subview-body";
      body.appendChild(fragment);
      let footer;
      while (--elementCount >= 0) {
        let element = body.childNodes[elementCount];
        CustomizableUI.addShortcut(element);
        element.classList.add("subviewbutton");
        if (element.classList.contains("restoreallitem")) {
          footer = element;
          element.classList.add("panel-subview-footer");
        } else {
          element.classList.add("subviewbutton-iconic", "bookmark-item");
        }
      }
      panelview.appendChild(body);
      panelview.appendChild(footer);
    }
  }, {
    id: "save-page-button",
    shortcutId: "key_savePage",
    tooltiptext: "save-page-button.tooltiptext3",
    onCommand(aEvent) {
      let win = aEvent.target.ownerGlobal;
      win.saveBrowser(win.gBrowser.selectedBrowser);
    }
  }, {
    id: "find-button",
    shortcutId: "key_find",
    tooltiptext: "find-button.tooltiptext3",
    onCommand(aEvent) {
      let win = aEvent.target.ownerGlobal;
      if (win.gFindBar) {
        win.gFindBar.onFindCommand();
      }
    }
  }, {
    id: "open-file-button",
    shortcutId: "openFileKb",
    tooltiptext: "open-file-button.tooltiptext3",
    onCommand(aEvent) {
      let win = aEvent.target.ownerGlobal;
      win.BrowserOpenFileWindow();
    }
  }, {
    id: "sidebar-button",
    tooltiptext: "sidebar-button.tooltiptext2",
    onCommand(aEvent) {
      let win = aEvent.target.ownerGlobal;
      win.SidebarUI.toggle();
    },
    onCreated(aNode) {
      // Add an observer so the button is checked while the sidebar is open
      let doc = aNode.ownerDocument;
      let obChecked = doc.createElementNS(kNSXUL, "observes");
      obChecked.setAttribute("element", "sidebar-box");
      obChecked.setAttribute("attribute", "checked");
      let obPosition = doc.createElementNS(kNSXUL, "observes");
      obPosition.setAttribute("element", "sidebar-box");
      obPosition.setAttribute("attribute", "positionend");

      aNode.appendChild(obChecked);
      aNode.appendChild(obPosition);
    }
  }, {
    id: "add-ons-button",
    shortcutId: "key_openAddons",
    tooltiptext: "add-ons-button.tooltiptext3",
    onCommand(aEvent) {
      let win = aEvent.target.ownerGlobal;
      win.BrowserOpenAddonsMgr();
    }
  }, {
    id: "zoom-controls",
    type: "custom",
    tooltiptext: "zoom-controls.tooltiptext2",
    onBuild(aDocument) {
      let buttons = [{
        id: "zoom-out-button",
        command: "cmd_fullZoomReduce",
        label: true,
        closemenu: "none",
        tooltiptext: "tooltiptext2",
        shortcutId: "key_fullZoomReduce",
        "class": "toolbarbutton-1 toolbarbutton-combined",
      }, {
        id: "zoom-reset-button",
        command: "cmd_fullZoomReset",
        closemenu: "none",
        tooltiptext: "tooltiptext2",
        shortcutId: "key_fullZoomReset",
        "class": "toolbarbutton-1 toolbarbutton-combined",
      }, {
        id: "zoom-in-button",
        command: "cmd_fullZoomEnlarge",
        closemenu: "none",
        label: true,
        tooltiptext: "tooltiptext2",
        shortcutId: "key_fullZoomEnlarge",
        "class": "toolbarbutton-1 toolbarbutton-combined",
      }];

      let node = aDocument.createElementNS(kNSXUL, "toolbaritem");
      node.setAttribute("id", "zoom-controls");
      node.setAttribute("label", CustomizableUI.getLocalizedProperty(this, "label"));
      node.setAttribute("title", CustomizableUI.getLocalizedProperty(this, "tooltiptext"));
      // Set this as an attribute in addition to the property to make sure we can style correctly.
      node.setAttribute("removable", "true");
      node.classList.add("chromeclass-toolbar-additional");
      node.classList.add("toolbaritem-combined-buttons");

      buttons.forEach(function(aButton, aIndex) {
        if (aIndex != 0)
          node.appendChild(aDocument.createElementNS(kNSXUL, "separator"));
        let btnNode = aDocument.createElementNS(kNSXUL, "toolbarbutton");
        setAttributes(btnNode, aButton);
        node.appendChild(btnNode);
      });
      return node;
    }
  }, {
    id: "edit-controls",
    type: "custom",
    tooltiptext: "edit-controls.tooltiptext2",
    onBuild(aDocument) {
      let buttons = [{
        id: "cut-button",
        command: "cmd_cut",
        label: true,
        tooltiptext: "tooltiptext2",
        shortcutId: "key_cut",
        "class": "toolbarbutton-1 toolbarbutton-combined",
      }, {
        id: "copy-button",
        command: "cmd_copy",
        label: true,
        tooltiptext: "tooltiptext2",
        shortcutId: "key_copy",
        "class": "toolbarbutton-1 toolbarbutton-combined",
      }, {
        id: "paste-button",
        command: "cmd_paste",
        label: true,
        tooltiptext: "tooltiptext2",
        shortcutId: "key_paste",
        "class": "toolbarbutton-1 toolbarbutton-combined",
      }];

      let node = aDocument.createElementNS(kNSXUL, "toolbaritem");
      node.setAttribute("id", "edit-controls");
      node.setAttribute("label", CustomizableUI.getLocalizedProperty(this, "label"));
      node.setAttribute("title", CustomizableUI.getLocalizedProperty(this, "tooltiptext"));
      // Set this as an attribute in addition to the property to make sure we can style correctly.
      node.setAttribute("removable", "true");
      node.classList.add("chromeclass-toolbar-additional");
      node.classList.add("toolbaritem-combined-buttons");

      buttons.forEach(function(aButton, aIndex) {
        if (aIndex != 0)
          node.appendChild(aDocument.createElementNS(kNSXUL, "separator"));
        let btnNode = aDocument.createElementNS(kNSXUL, "toolbarbutton");
        setAttributes(btnNode, aButton);
        node.appendChild(btnNode);
      });

      let listener = {
        onWidgetInstanceRemoved: (aWidgetId, aDoc) => {
          if (aWidgetId != this.id || aDoc != aDocument)
            return;
          CustomizableUI.removeListener(listener);
        },
        onWidgetOverflow(aWidgetNode) {
          if (aWidgetNode == node) {
            node.ownerGlobal.updateEditUIVisibility();
          }
        },
        onWidgetUnderflow(aWidgetNode) {
          if (aWidgetNode == node) {
            node.ownerGlobal.updateEditUIVisibility();
          }
        },
      };
      CustomizableUI.addListener(listener);

      return node;
    }
  },
  {
    id: "feed-button",
    type: "view",
    viewId: "PanelUI-feeds",
    tooltiptext: "feed-button.tooltiptext2",
    onClick(aEvent) {
      let win = aEvent.target.ownerGlobal;
      let feeds = win.gBrowser.selectedBrowser.feeds;

      // Here, we only care about the case where we have exactly 1 feed and the
      // user clicked...
      let isClick = (aEvent.button == 0 || aEvent.button == 1);
      if (feeds && feeds.length == 1 && isClick) {
        aEvent.preventDefault();
        aEvent.stopPropagation();
        win.FeedHandler.subscribeToFeed(feeds[0].href, aEvent);
        CustomizableUI.hidePanelForNode(aEvent.target);
      }
    },
    onViewShowing(aEvent) {
      let doc = aEvent.target.ownerDocument;
      let container = doc.getElementById("PanelUI-feeds");
      let gotView = doc.defaultView.FeedHandler.buildFeedList(container, true);

      // For no feeds or only a single one, don't show the panel.
      if (!gotView) {
        aEvent.preventDefault();
        aEvent.stopPropagation();
      }
    },
    onCreated(node) {
      let win = node.ownerGlobal;
      let selectedBrowser = win.gBrowser.selectedBrowser;
      let feeds = selectedBrowser && selectedBrowser.feeds;
      if (!feeds || !feeds.length) {
        node.setAttribute("disabled", "true");
      }
    }
  }, {
    id: "characterencoding-button",
    label: "characterencoding-button2.label",
    type: "view",
    viewId: "PanelUI-characterEncodingView",
    tooltiptext: "characterencoding-button2.tooltiptext",
    maybeDisableMenu(aDocument) {
      let window = aDocument.defaultView;
      return !(window.gBrowser &&
               window.gBrowser.selectedBrowser.mayEnableCharacterEncodingMenu);
    },
    populateList(aDocument, aContainerId, aSection) {
      let containerElem = aDocument.getElementById(aContainerId);

      containerElem.addEventListener("command", this.onCommand);

      let list = this.charsetInfo[aSection];

      for (let item of list) {
        let elem = aDocument.createElementNS(kNSXUL, "toolbarbutton");
        elem.setAttribute("label", item.label);
        elem.setAttribute("type", "checkbox");
        elem.section = aSection;
        elem.value = item.value;
        elem.setAttribute("class", "subviewbutton");
        containerElem.appendChild(elem);
      }
    },
    updateCurrentCharset(aDocument) {
      let currentCharset = aDocument.defaultView.gBrowser.selectedBrowser.characterSet;
      currentCharset = CharsetMenu.foldCharset(currentCharset);

      let pinnedContainer = aDocument.getElementById("PanelUI-characterEncodingView-pinned");
      let charsetContainer = aDocument.getElementById("PanelUI-characterEncodingView-charsets");
      let elements = [...(pinnedContainer.childNodes), ...(charsetContainer.childNodes)];

      this._updateElements(elements, currentCharset);
    },
    updateCurrentDetector(aDocument) {
      let detectorContainer = aDocument.getElementById("PanelUI-characterEncodingView-autodetect");
      let currentDetector;
      try {
        currentDetector = Services.prefs.getComplexValue(
          "intl.charset.detector", Ci.nsIPrefLocalizedString).data;
      } catch (e) {}

      this._updateElements(detectorContainer.childNodes, currentDetector);
    },
    _updateElements(aElements, aCurrentItem) {
      if (!aElements.length) {
        return;
      }
      let disabled = this.maybeDisableMenu(aElements[0].ownerDocument);
      for (let elem of aElements) {
        if (disabled) {
          elem.setAttribute("disabled", "true");
        } else {
          elem.removeAttribute("disabled");
        }
        if (elem.value.toLowerCase() == aCurrentItem.toLowerCase()) {
          elem.setAttribute("checked", "true");
        } else {
          elem.removeAttribute("checked");
        }
      }
    },
    onViewShowing(aEvent) {
      if (!this._inited) {
        this.onInit();
      }
      let document = aEvent.target.ownerDocument;

      let autoDetectLabelId = "PanelUI-characterEncodingView-autodetect-label";
      let autoDetectLabel = document.getElementById(autoDetectLabelId);
      if (!autoDetectLabel.hasAttribute("value")) {
        let label = CharsetBundle.GetStringFromName("charsetMenuAutodet");
        autoDetectLabel.setAttribute("value", label);
        this.populateList(document,
                          "PanelUI-characterEncodingView-pinned",
                          "pinnedCharsets");
        this.populateList(document,
                          "PanelUI-characterEncodingView-charsets",
                          "otherCharsets");
        this.populateList(document,
                          "PanelUI-characterEncodingView-autodetect",
                          "detectors");
      }
      this.updateCurrentDetector(document);
      this.updateCurrentCharset(document);
    },
    onCommand(aEvent) {
      let node = aEvent.target;
      if (!node.hasAttribute || !node.section) {
        return;
      }

      let window = node.ownerGlobal;
      let section = node.section;
      let value = node.value;

      // The behavior as implemented here is directly based off of the
      // `MultiplexHandler()` method in browser.js.
      if (section != "detectors") {
        window.BrowserSetForcedCharacterSet(value);
      } else {
        // Set the detector pref.
        try {
          Services.prefs.setStringPref("intl.charset.detector", value);
        } catch (e) {
          Cu.reportError("Failed to set the intl.charset.detector preference.");
        }
        // Prepare a browser page reload with a changed charset.
        window.BrowserCharsetReload();
      }
    },
    onCreated(aNode) {
      let document = aNode.ownerDocument;

      let updateButton = () => {
        if (this.maybeDisableMenu(document))
          aNode.setAttribute("disabled", "true");
        else
          aNode.removeAttribute("disabled");
      };

      let getPanel = () => {
        let {PanelUI} = document.ownerGlobal;
        return PanelUI.overflowPanel;
      };

      if (CustomizableUI.getAreaType(this.currentArea) == CustomizableUI.TYPE_MENU_PANEL) {
        getPanel().addEventListener("popupshowing", updateButton);
      }

      let listener = {
        onWidgetAdded: (aWidgetId, aArea) => {
          if (aWidgetId != this.id)
            return;
          if (CustomizableUI.getAreaType(aArea) == CustomizableUI.TYPE_MENU_PANEL) {
            getPanel().addEventListener("popupshowing", updateButton);
          }
        },
        onWidgetRemoved: (aWidgetId, aPrevArea) => {
          if (aWidgetId != this.id)
            return;
          aNode.removeAttribute("disabled");
          if (CustomizableUI.getAreaType(aPrevArea) == CustomizableUI.TYPE_MENU_PANEL) {
            getPanel().removeEventListener("popupshowing", updateButton);
          }
        },
        onWidgetInstanceRemoved: (aWidgetId, aDoc) => {
          if (aWidgetId != this.id || aDoc != document)
            return;

          CustomizableUI.removeListener(listener);
          getPanel().removeEventListener("popupshowing", updateButton);
        }
      };
      CustomizableUI.addListener(listener);
      this.onInit();
    },
    onInit() {
      this._inited = true;
      if (!this.charsetInfo) {
        this.charsetInfo = CharsetMenu.getData();
      }
    }
  }, {
    id: "email-link-button",
    tooltiptext: "email-link-button.tooltiptext3",
    onCommand(aEvent) {
      let win = aEvent.view;
      win.MailIntegration.sendLinkForBrowser(win.gBrowser.selectedBrowser);
    }
  }];

if (Services.prefs.getBoolPref("identity.fxaccounts.enabled")) {
  CustomizableWidgets.push({
    id: "sync-button",
    label: "remotetabs-panelmenu.label",
    tooltiptext: "remotetabs-panelmenu.tooltiptext2",
    type: "view",
    viewId: "PanelUI-remotetabs",
    deckIndices: {
      DECKINDEX_TABS: 0,
      DECKINDEX_TABSDISABLED: 1,
      DECKINDEX_FETCHING: 2,
      DECKINDEX_NOCLIENTS: 3,
    },
    TABS_PER_PAGE: 25,
    NEXT_PAGE_MIN_TABS: 5, // Minimum number of tabs displayed when we click "Show All"
    onCreated(aNode) {
      this._initialize(aNode);
    },
    _initialize(aNode) {
      if (this._initialized) {
        return;
      }
      // Add an observer to the button so we get the animation during sync.
      // (Note the observer sets many attributes, including label and
      // tooltiptext, but we only want the 'syncstatus' attribute for the
      // animation)
      let doc = aNode.ownerDocument;
      let obnode = doc.createElementNS(kNSXUL, "observes");
      obnode.setAttribute("element", "sync-status");
      obnode.setAttribute("attribute", "syncstatus");
      aNode.appendChild(obnode);
      this._initialized = true;
    },
    onViewShowing(aEvent) {
      this._initialize(aEvent.target);
      let doc = aEvent.target.ownerDocument;
      this._tabsList = doc.getElementById("PanelUI-remotetabs-tabslist");
      Services.obs.addObserver(this, SyncedTabs.TOPIC_TABS_CHANGED);

      if (SyncedTabs.isConfiguredToSyncTabs) {
        if (SyncedTabs.hasSyncedThisSession) {
          this.setDeckIndex(this.deckIndices.DECKINDEX_TABS);
        } else {
          // Sync hasn't synced tabs yet, so show the "fetching" panel.
          this.setDeckIndex(this.deckIndices.DECKINDEX_FETCHING);
        }
        // force a background sync.
        SyncedTabs.syncTabs().catch(ex => {
          Cu.reportError(ex);
        });
        // show the current list - it will be updated by our observer.
        this._showTabs();
      } else {
        // not configured to sync tabs, so no point updating the list.
        this.setDeckIndex(this.deckIndices.DECKINDEX_TABSDISABLED);
      }
    },
    onViewHiding() {
      Services.obs.removeObserver(this, SyncedTabs.TOPIC_TABS_CHANGED);
      this._tabsList = null;
    },
    _tabsList: null,
    observe(subject, topic, data) {
      switch (topic) {
        case SyncedTabs.TOPIC_TABS_CHANGED:
          this._showTabs();
          break;
        default:
          break;
      }
    },
    setDeckIndex(index) {
      let deck = this._tabsList.ownerDocument.getElementById("PanelUI-remotetabs-deck");
      // We call setAttribute instead of relying on the XBL property setter due
      // to things going wrong when we try and set the index before the XBL
      // binding has been created - see bug 1241851 for the gory details.
      deck.setAttribute("selectedIndex", index);
    },

    _showTabsPromise: Promise.resolve(),
    // Update the tab list after any existing in-flight updates are complete.
    _showTabs(paginationInfo) {
      this._showTabsPromise = this._showTabsPromise.then(() => {
        return this.__showTabs(paginationInfo);
      }, e => {
        Cu.reportError(e);
      });
    },
    // Return a new promise to update the tab list.
    __showTabs(paginationInfo) {
      if (!this._tabsList) {
        // Closed between the previous `this._showTabsPromise`
        // resolving and now.
        return undefined;
      }
      let doc = this._tabsList.ownerDocument;
      return SyncedTabs.getTabClients().then(clients => {
        // The view may have been hidden while the promise was resolving.
        if (!this._tabsList) {
          return;
        }
        if (clients.length === 0 && !SyncedTabs.hasSyncedThisSession) {
          // the "fetching tabs" deck is being shown - let's leave it there.
          // When that first sync completes we'll be notified and update.
          return;
        }

        if (clients.length === 0) {
          this.setDeckIndex(this.deckIndices.DECKINDEX_NOCLIENTS);
          return;
        }

        this.setDeckIndex(this.deckIndices.DECKINDEX_TABS);
        this._clearTabList();
        SyncedTabs.sortTabClientsByLastUsed(clients);
        let fragment = doc.createDocumentFragment();

        for (let client of clients) {
          // add a menu separator for all clients other than the first.
          if (fragment.lastChild) {
            let separator = doc.createElementNS(kNSXUL, "menuseparator");
            fragment.appendChild(separator);
          }
          if (paginationInfo && paginationInfo.clientId == client.id) {
            this._appendClient(client, fragment, paginationInfo.maxTabs);
          } else {
            this._appendClient(client, fragment);
          }
        }
        this._tabsList.appendChild(fragment);
        PanelView.forNode(this._tabsList.closest("panelview"))
                 .descriptionHeightWorkaround();
      }).catch(err => {
        Cu.reportError(err);
      }).then(() => {
        // an observer for tests.
        Services.obs.notifyObservers(null, "synced-tabs-menu:test:tabs-updated");
      });
    },
    _clearTabList() {
      let list = this._tabsList;
      while (list.lastChild) {
        list.lastChild.remove();
      }
    },
    _showNoClientMessage() {
      this._appendMessageLabel("notabslabel");
    },
    _appendMessageLabel(messageAttr, appendTo = null) {
      if (!appendTo) {
        appendTo = this._tabsList;
      }
      let message = this._tabsList.getAttribute(messageAttr);
      let doc = this._tabsList.ownerDocument;
      let messageLabel = doc.createElementNS(kNSXUL, "label");
      messageLabel.textContent = message;
      appendTo.appendChild(messageLabel);
      return messageLabel;
    },
    _appendClient(client, attachFragment, maxTabs = this.TABS_PER_PAGE) {
      let doc = attachFragment.ownerDocument;
      // Create the element for the remote client.
      let clientItem = doc.createElementNS(kNSXUL, "label");
      clientItem.setAttribute("itemtype", "client");
      let window = doc.defaultView;
      clientItem.setAttribute("tooltiptext",
        window.gSync.formatLastSyncDate(new Date(client.lastModified)));
      clientItem.textContent = client.name;

      attachFragment.appendChild(clientItem);

      if (client.tabs.length == 0) {
        let label = this._appendMessageLabel("notabsforclientlabel", attachFragment);
        label.setAttribute("class", "PanelUI-remotetabs-notabsforclient-label");
      } else {
        // If this page will display all tabs, show no additional buttons.
        // If the next page will display all the remaining tabs, show a "Show All" button
        // Otherwise, show a "Shore More" button
        let hasNextPage = client.tabs.length > maxTabs;
        let nextPageIsLastPage = hasNextPage && maxTabs + this.TABS_PER_PAGE >= client.tabs.length;
        if (nextPageIsLastPage) {
          // When the user clicks "Show All", try to have at least NEXT_PAGE_MIN_TABS more tabs
          // to display in order to avoid user frustration
          maxTabs = Math.min(client.tabs.length - this.NEXT_PAGE_MIN_TABS, maxTabs);
        }
        if (hasNextPage) {
          client.tabs = client.tabs.slice(0, maxTabs);
        }
        for (let tab of client.tabs) {
          let tabEnt = this._createTabElement(doc, tab);
          attachFragment.appendChild(tabEnt);
        }
        if (hasNextPage) {
          let showAllEnt = this._createShowMoreElement(doc, client.id,
                                                       nextPageIsLastPage ?
                                                       Infinity :
                                                       maxTabs + this.TABS_PER_PAGE);
          attachFragment.appendChild(showAllEnt);
        }
      }
    },
    _createTabElement(doc, tabInfo) {
      let item = doc.createElementNS(kNSXUL, "toolbarbutton");
      let tooltipText = (tabInfo.title ? tabInfo.title + "\n" : "") + tabInfo.url;
      item.setAttribute("itemtype", "tab");
      item.setAttribute("class", "subviewbutton");
      item.setAttribute("targetURI", tabInfo.url);
      item.setAttribute("label", tabInfo.title != "" ? tabInfo.title : tabInfo.url);
      item.setAttribute("image", tabInfo.icon);
      item.setAttribute("tooltiptext", tooltipText);
      // We need to use "click" instead of "command" here so openUILink
      // respects different buttons (eg, to open in a new tab).
      item.addEventListener("click", e => {
        doc.defaultView.openUILink(tabInfo.url, e);
        if (doc.defaultView.whereToOpenLink(e) != "current") {
          e.preventDefault();
          e.stopPropagation();
        } else {
          CustomizableUI.hidePanelForNode(item);
        }
        BrowserUITelemetry.countSyncedTabEvent("open", "toolbarbutton-subview");
      });
      return item;
    },
    _createShowMoreElement(doc, clientId, showCount) {
      let labelAttr, tooltipAttr;
      if (showCount === Infinity) {
        labelAttr = "showAllLabel";
        tooltipAttr = "showAllTooltipText";
      } else {
        labelAttr = "showMoreLabel";
        tooltipAttr = "showMoreTooltipText";
      }
      let showAllItem = doc.createElementNS(kNSXUL, "toolbarbutton");
      showAllItem.setAttribute("itemtype", "showmorebutton");
      showAllItem.setAttribute("class", "subviewbutton");
      let label = this._tabsList.getAttribute(labelAttr);
      showAllItem.setAttribute("label", label);
      let tooltipText = this._tabsList.getAttribute(tooltipAttr);
      showAllItem.setAttribute("tooltiptext", tooltipText);
      showAllItem.addEventListener("click", e => {
        e.preventDefault();
        e.stopPropagation();
        this._showTabs({ clientId, maxTabs: showCount });
      });
      return showAllItem;
    }
  });
}

let preferencesButton = {
  id: "preferences-button",
  onCommand(aEvent) {
    let win = aEvent.target.ownerGlobal;
    win.openPreferences(undefined, {origin: "preferencesButton"});
  }
};
if (AppConstants.platform == "win") {
  preferencesButton.label = "preferences-button.labelWin";
  preferencesButton.tooltiptext = "preferences-button.tooltipWin2";
} else if (AppConstants.platform == "macosx") {
  preferencesButton.tooltiptext = "preferences-button.tooltiptext.withshortcut";
  preferencesButton.shortcutId = "key_preferencesCmdMac";
} else {
  preferencesButton.tooltiptext = "preferences-button.tooltiptext2";
}
CustomizableWidgets.push(preferencesButton);

if (Services.prefs.getBoolPref("privacy.panicButton.enabled")) {
  CustomizableWidgets.push({
    id: "panic-button",
    type: "view",
    viewId: "PanelUI-panicView",

    forgetButtonCalled(aEvent) {
      let doc = aEvent.target.ownerDocument;
      let group = doc.getElementById("PanelUI-panic-timeSpan");
      BrowserUITelemetry.countPanicEvent(group.selectedItem.id);
      let itemsToClear = [
        "cookies", "history", "openWindows", "formdata", "sessions", "cache", "downloads"
      ];
      let newWindowPrivateState = PrivateBrowsingUtils.isWindowPrivate(doc.defaultView) ?
                                  "private" : "non-private";
      let promise = Sanitizer.sanitize(itemsToClear, {
        ignoreTimespan: false,
        range: Sanitizer.getClearRange(+group.value),
        privateStateForNewWindow: newWindowPrivateState,
      });
      promise.then(function() {
        let otherWindow = Services.wm.getMostRecentWindow("navigator:browser");
        if (otherWindow.closed) {
          Cu.reportError("Got a closed window!");
        }
        if (otherWindow.PanicButtonNotifier) {
          otherWindow.PanicButtonNotifier.notify();
        } else {
          otherWindow.PanicButtonNotifierShouldNotify = true;
        }
      });
    },
    handleEvent(aEvent) {
      switch (aEvent.type) {
        case "command":
          this.forgetButtonCalled(aEvent);
          break;
      }
    },
    onViewShowing(aEvent) {
      let forgetButton = aEvent.target.querySelector("#PanelUI-panic-view-button");
      let doc = aEvent.target.ownerDocument;
      let group = doc.getElementById("PanelUI-panic-timeSpan");
      group.selectedItem = doc.getElementById("PanelUI-panic-5min");
      forgetButton.addEventListener("command", this);
    },
    onViewHiding(aEvent) {
      let forgetButton = aEvent.target.querySelector("#PanelUI-panic-view-button");
      forgetButton.removeEventListener("command", this);
    },
  });
}

if (PrivateBrowsingUtils.enabled) {
  CustomizableWidgets.push({
    id: "privatebrowsing-button",
    shortcutId: "key_privatebrowsing",
    onCommand(e) {
      let win = e.target.ownerGlobal;
      win.OpenBrowserWindow({private: true});
    }
  });
}
PK
!<���modules/CustomizeMode.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = ["CustomizeMode"];

const kPrefCustomizationDebug = "browser.uiCustomization.debug";
const kPaletteId = "customization-palette";
const kDragDataTypePrefix = "text/toolbarwrapper-id/";
const kSkipSourceNodePref = "browser.uiCustomization.skipSourceNodeCheck";
const kDrawInTitlebarPref = "browser.tabs.drawInTitlebar";
const kExtraDragSpacePref = "browser.tabs.extraDragSpace";
const kKeepBroadcastAttributes = "keepbroadcastattributeswhencustomizing";

const kPanelItemContextMenu = "customizationPanelItemContextMenu";
const kPaletteItemContextMenu = "customizationPaletteItemContextMenu";

const kDownloadAutohideCheckboxId = "downloads-button-autohide-checkbox";
const kDownloadAutohidePanelId = "downloads-button-autohide-panel";
const kDownloadAutoHidePref = "browser.download.autohideButton";

ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource:///modules/CustomizableUI.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
ChromeUtils.import("resource://gre/modules/AppConstants.jsm");

Cu.importGlobalProperties(["CSS"]);

ChromeUtils.defineModuleGetter(this, "DragPositionManager",
                               "resource:///modules/DragPositionManager.jsm");
ChromeUtils.defineModuleGetter(this, "BrowserUITelemetry",
                               "resource:///modules/BrowserUITelemetry.jsm");
ChromeUtils.defineModuleGetter(this, "BrowserUtils",
                               "resource://gre/modules/BrowserUtils.jsm");
ChromeUtils.defineModuleGetter(this, "LightweightThemeManager",
                               "resource://gre/modules/LightweightThemeManager.jsm");
ChromeUtils.defineModuleGetter(this, "SessionStore",
                               "resource:///modules/sessionstore/SessionStore.jsm");
XPCOMUtils.defineLazyGetter(this, "gWidgetsBundle", function() {
  const kUrl = "chrome://browser/locale/customizableui/customizableWidgets.properties";
  return Services.strings.createBundle(kUrl);
});
XPCOMUtils.defineLazyPreferenceGetter(this, "gCosmeticAnimationsEnabled",
                                      "toolkit.cosmeticAnimations.enabled");

let gDebug;
XPCOMUtils.defineLazyGetter(this, "log", () => {
  let scope = {};
  ChromeUtils.import("resource://gre/modules/Console.jsm", scope);
  gDebug = Services.prefs.getBoolPref(kPrefCustomizationDebug, false);
  let consoleOptions = {
    maxLogLevel: gDebug ? "all" : "log",
    prefix: "CustomizeMode",
  };
  return new scope.ConsoleAPI(consoleOptions);
});

var gDraggingInToolbars;

var gTab;

function closeGlobalTab() {
  let win = gTab.ownerGlobal;
  if (win.gBrowser.browsers.length == 1) {
    win.BrowserOpenTab();
  }
  win.gBrowser.removeTab(gTab);
  gTab = null;
}

var gTabsProgressListener = {
  onLocationChange(aBrowser, aWebProgress, aRequest, aLocation, aFlags) {
    if (!gTab || gTab.linkedBrowser != aBrowser) {
      return;
    }

    unregisterGlobalTab();
  },
};

function unregisterGlobalTab() {
  gTab.removeEventListener("TabClose", unregisterGlobalTab);
  let win = gTab.ownerGlobal;
  win.removeEventListener("unload", unregisterGlobalTab);
  win.gBrowser.removeTabsProgressListener(gTabsProgressListener);

  gTab.removeAttribute("customizemode");

  gTab = null;
}

function CustomizeMode(aWindow) {
  this.window = aWindow;
  this.document = aWindow.document;
  this.browser = aWindow.gBrowser;
  this.areas = new Set();

  // There are two palettes - there's the palette that can be overlayed with
  // toolbar items in browser.xul. This is invisible, and never seen by the
  // user. Then there's the visible palette, which gets populated and displayed
  // to the user when in customizing mode.
  this.visiblePalette = this.document.getElementById(kPaletteId);
  this.pongArena = this.document.getElementById("customization-pong-arena");
  if (Services.prefs.getCharPref("general.skins.selectedSkin") != "classic/1.0") {
    let lwthemeButton = this.document.getElementById("customization-lwtheme-button");
    lwthemeButton.setAttribute("hidden", "true");
  }
  if (AppConstants.CAN_DRAW_IN_TITLEBAR) {
    this._updateTitlebarCheckbox();
    this._updateDragSpaceCheckbox();
    Services.prefs.addObserver(kDrawInTitlebarPref, this);
    Services.prefs.addObserver(kExtraDragSpacePref, this);
  }
  this.window.addEventListener("unload", this);
}

CustomizeMode.prototype = {
  _changed: false,
  _transitioning: false,
  window: null,
  document: null,
  // areas is used to cache the customizable areas when in customization mode.
  areas: null,
  // When in customizing mode, we swap out the reference to the invisible
  // palette in gNavToolbox.palette for our visiblePalette. This way, for the
  // customizing browser window, when widgets are removed from customizable
  // areas and added to the palette, they're added to the visible palette.
  // _stowedPalette is a reference to the old invisible palette so we can
  // restore gNavToolbox.palette to its original state after exiting
  // customization mode.
  _stowedPalette: null,
  _dragOverItem: null,
  _customizing: false,
  _skipSourceNodeCheck: null,
  _mainViewContext: null,

  get _handler() {
    return this.window.CustomizationHandler;
  },

  uninit() {
    if (AppConstants.CAN_DRAW_IN_TITLEBAR) {
      Services.prefs.removeObserver(kDrawInTitlebarPref, this);
      Services.prefs.removeObserver(kExtraDragSpacePref, this);
    }
  },

  toggle() {
    if (this._handler.isEnteringCustomizeMode || this._handler.isExitingCustomizeMode) {
      this._wantToBeInCustomizeMode = !this._wantToBeInCustomizeMode;
      return;
    }
    if (this._customizing) {
      this.exit();
    } else {
      this.enter();
    }
  },

  _updateLWThemeButtonIcon() {
    let lwthemeButton = this.document.getElementById("customization-lwtheme-button");
    let lwthemeIcon = this.document.getAnonymousElementByAttribute(lwthemeButton,
                        "class", "button-icon");
    lwthemeIcon.style.backgroundImage = LightweightThemeManager.currentTheme ?
      "url(" + LightweightThemeManager.currentTheme.iconURL + ")" : "";
  },

  setTab(aTab) {
    if (gTab == aTab) {
      return;
    }

    if (gTab) {
      closeGlobalTab();
    }

    gTab = aTab;

    gTab.setAttribute("customizemode", "true");
    SessionStore.persistTabAttribute("customizemode");

    gTab.linkedBrowser.stop();

    let win = gTab.ownerGlobal;

    win.gBrowser.setTabTitle(gTab);
    win.gBrowser.setIcon(gTab,
                         "chrome://browser/skin/customize.svg");

    gTab.addEventListener("TabClose", unregisterGlobalTab);

    win.gBrowser.addTabsProgressListener(gTabsProgressListener);

    win.addEventListener("unload", unregisterGlobalTab);

    if (gTab.selected) {
      win.gCustomizeMode.enter();
    }
  },

  enter() {
    if (!this.window.toolbar.visible) {
      let w = this.window.getTopWin(true);
      if (w) {
        w.gCustomizeMode.enter();
        return;
      }
      let obs = () => {
        Services.obs.removeObserver(obs, "browser-delayed-startup-finished");
        w = this.window.getTopWin(true);
        w.gCustomizeMode.enter();
      };
      Services.obs.addObserver(obs, "browser-delayed-startup-finished");
      this.window.openUILinkIn("about:newtab", "window");
      return;
    }
    this._wantToBeInCustomizeMode = true;

    if (this._customizing || this._handler.isEnteringCustomizeMode) {
      return;
    }

    // Exiting; want to re-enter once we've done that.
    if (this._handler.isExitingCustomizeMode) {
      log.debug("Attempted to enter while we're in the middle of exiting. " +
                "We'll exit after we've entered");
      return;
    }

    if (!gTab) {
      this.setTab(this.browser.loadOneTab("about:blank", {
        inBackground: false,
        forceNotRemote: true,
        skipAnimation: true,
        triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
      }));
      return;
    }
    if (!gTab.selected) {
      // This will force another .enter() to be called via the
      // onlocationchange handler of the tabbrowser, so we return early.
      gTab.ownerGlobal.gBrowser.selectedTab = gTab;
      return;
    }
    gTab.ownerGlobal.focus();
    if (gTab.ownerDocument != this.document) {
      return;
    }

    let window = this.window;
    let document = this.document;

    this._handler.isEnteringCustomizeMode = true;

    // Always disable the reset button at the start of customize mode, it'll be re-enabled
    // if necessary when we finish entering:
    let resetButton = this.document.getElementById("customization-reset-button");
    resetButton.setAttribute("disabled", "true");

    (async () => {
      // We shouldn't start customize mode until after browser-delayed-startup has finished:
      if (!this.window.gBrowserInit.delayedStartupFinished) {
        await new Promise(resolve => {
          let delayedStartupObserver = aSubject => {
            if (aSubject == this.window) {
              Services.obs.removeObserver(delayedStartupObserver, "browser-delayed-startup-finished");
              resolve();
            }
          };

          Services.obs.addObserver(delayedStartupObserver, "browser-delayed-startup-finished");
        });
      }

      CustomizableUI.dispatchToolboxEvent("beforecustomization", {}, window);
      CustomizableUI.notifyStartCustomizing(this.window);

      // Add a keypress listener to the document so that we can quickly exit
      // customization mode when pressing ESC.
      document.addEventListener("keypress", this);

      // Same goes for the menu button - if we're customizing, a click on the
      // menu button means a quick exit from customization mode.
      window.PanelUI.hide();

      let panelHolder = document.getElementById("customization-panelHolder");
      let panelContextMenu = document.getElementById(kPanelItemContextMenu);
      this._previousPanelContextMenuParent = panelContextMenu.parentNode;
      document.getElementById("mainPopupSet").appendChild(panelContextMenu);
      panelHolder.appendChild(window.PanelUI.overflowFixedList);

      window.PanelUI.overflowFixedList.setAttribute("customizing", true);
      window.PanelUI.menuButton.disabled = true;
      document.getElementById("nav-bar-overflow-button").disabled = true;

      this._transitioning = true;

      let customizer = document.getElementById("customization-container");
      customizer.parentNode.selectedPanel = customizer;
      customizer.hidden = false;

      this._wrapToolbarItemSync(CustomizableUI.AREA_TABSTRIP);

      let customizableToolbars = document.querySelectorAll("toolbar[customizable=true]:not([autohide=true]):not([collapsed=true])");
      for (let toolbar of customizableToolbars)
        toolbar.setAttribute("customizing", true);

      await this._doTransition(true);

      // Let everybody in this window know that we're about to customize.
      CustomizableUI.dispatchToolboxEvent("customizationstarting", {}, window);

      await this._wrapToolbarItems();
      this.populatePalette();

      this._setupPaletteDragging();

      window.gNavToolbox.addEventListener("toolbarvisibilitychange", this);

      this._updateResetButton();
      this._updateUndoResetButton();

      this._skipSourceNodeCheck = Services.prefs.getPrefType(kSkipSourceNodePref) == Ci.nsIPrefBranch.PREF_BOOL &&
                                  Services.prefs.getBoolPref(kSkipSourceNodePref);

      CustomizableUI.addListener(this);
      this._customizing = true;
      this._transitioning = false;

      // Show the palette now that the transition has finished.
      this.visiblePalette.hidden = false;
      window.setTimeout(() => {
        // Force layout reflow to ensure the animation runs,
        // and make it async so it doesn't affect the timing.
        this.visiblePalette.clientTop;
        this.visiblePalette.setAttribute("showing", "true");
      }, 0);
      this._updateEmptyPaletteNotice();

      this._updateLWThemeButtonIcon();

      this._setupDownloadAutoHideToggle();

      this._handler.isEnteringCustomizeMode = false;

      CustomizableUI.dispatchToolboxEvent("customizationready", {}, window);

      if (!this._wantToBeInCustomizeMode) {
        this.exit();
      }
    })().catch(e => {
      log.error("Error entering customize mode", e);
      this._handler.isEnteringCustomizeMode = false;
      // Exit customize mode to ensure proper clean-up when entering failed.
      this.exit();
    });
  },

  exit() {
    this._wantToBeInCustomizeMode = false;

    if (!this._customizing || this._handler.isExitingCustomizeMode) {
      return;
    }

    // Entering; want to exit once we've done that.
    if (this._handler.isEnteringCustomizeMode) {
      log.debug("Attempted to exit while we're in the middle of entering. " +
                "We'll exit after we've entered");
      return;
    }

    if (this.resetting) {
      log.debug("Attempted to exit while we're resetting. " +
                "We'll exit after resetting has finished.");
      return;
    }

    this._handler.isExitingCustomizeMode = true;

    this._teardownDownloadAutoHideToggle();

    CustomizableUI.removeListener(this);

    this.document.removeEventListener("keypress", this);

    let window = this.window;
    let document = this.document;

    this.togglePong(false);

    // Disable the reset and undo reset buttons while transitioning:
    let resetButton = this.document.getElementById("customization-reset-button");
    let undoResetButton = this.document.getElementById("customization-undo-reset-button");
    undoResetButton.hidden = resetButton.disabled = true;

    this._transitioning = true;

    (async () => {
      await this.depopulatePalette();

      await this._doTransition(false);

      if (this.browser.selectedTab == gTab) {
        if (gTab.linkedBrowser.currentURI.spec == "about:blank") {
          closeGlobalTab();
        } else {
          unregisterGlobalTab();
        }
      }
      let browser = document.getElementById("browser");
      browser.parentNode.selectedPanel = browser;
      let customizer = document.getElementById("customization-container");
      customizer.hidden = true;

      window.gNavToolbox.removeEventListener("toolbarvisibilitychange", this);

      this._teardownPaletteDragging();

      await this._unwrapToolbarItems();

      if (this._changed) {
        // XXXmconley: At first, it seems strange to also persist the old way with
        //             currentset - but this might actually be useful for switching
        //             to old builds. We might want to keep this around for a little
        //             bit.
        this.persistCurrentSets();
      }

      // And drop all area references.
      this.areas.clear();

      // Let everybody in this window know that we're starting to
      // exit customization mode.
      CustomizableUI.dispatchToolboxEvent("customizationending", {}, window);

      window.PanelUI.menuButton.disabled = false;
      let overflowContainer = document.getElementById("widget-overflow-mainView").firstChild;
      overflowContainer.appendChild(window.PanelUI.overflowFixedList);
      document.getElementById("nav-bar-overflow-button").disabled = false;
      let panelContextMenu = document.getElementById(kPanelItemContextMenu);
      this._previousPanelContextMenuParent.appendChild(panelContextMenu);

      // We need to set this._customizing to false before removing the tab
      // or the TabSelect event handler will think that we are exiting
      // customization mode for a second time.
      this._customizing = false;

      let customizableToolbars = document.querySelectorAll("toolbar[customizable=true]:not([autohide=true])");
      for (let toolbar of customizableToolbars)
        toolbar.removeAttribute("customizing");

      this._maybeMoveDownloadsButtonToNavBar();

      delete this._lastLightweightTheme;
      this._changed = false;
      this._transitioning = false;
      this._handler.isExitingCustomizeMode = false;
      CustomizableUI.dispatchToolboxEvent("aftercustomization", {}, window);
      CustomizableUI.notifyEndCustomizing(window);

      if (this._wantToBeInCustomizeMode) {
        this.enter();
      }
    })().catch(e => {
      log.error("Error exiting customize mode", e);
      this._handler.isExitingCustomizeMode = false;
    });
  },

  /**
   * The customize mode transition has 4 phases when entering:
   * 1) Pre-customization mode
   *    This is the starting phase of the browser.
   * 2) LWT swapping
   *    This is where we swap some of the lightweight theme styles in order
   *    to make them work in customize mode. We set/unset a customization-
   *    lwtheme attribute iff we're using a lightweight theme.
   * 3) customize-entering
   *    This phase is a transition, optimized for smoothness.
   * 4) customize-entered
   *    After the transition completes, this phase draws all of the
   *    expensive detail that isn't necessary during the second phase.
   *
   * Exiting customization mode has a similar set of phases, but in reverse
   * order - customize-entered, customize-exiting, remove LWT swapping,
   * pre-customization mode.
   *
   * When in the customize-entering, customize-entered, or customize-exiting
   * phases, there is a "customizing" attribute set on the main-window to simplify
   * excluding certain styles while in any phase of customize mode.
   */
  _doTransition(aEntering) {
    let docEl = this.document.documentElement;
    if (aEntering) {
      docEl.setAttribute("customizing", true);
      docEl.setAttribute("customize-entered", true);
    } else {
      docEl.removeAttribute("customizing");
      docEl.removeAttribute("customize-entered");
    }
    return Promise.resolve();
  },

  _getCustomizableChildForNode(aNode) {
    // NB: adjusted from _getCustomizableParent to keep that method fast
    // (it's used during drags), and avoid multiple DOM loops
    let areas = CustomizableUI.areas;
    // Caching this length is important because otherwise we'll also iterate
    // over items we add to the end from within the loop.
    let numberOfAreas = areas.length;
    for (let i = 0; i < numberOfAreas; i++) {
      let area = areas[i];
      let areaNode = aNode.ownerDocument.getElementById(area);
      let customizationTarget = areaNode && areaNode.customizationTarget;
      if (customizationTarget && customizationTarget != areaNode) {
        areas.push(customizationTarget.id);
      }
      let overflowTarget = areaNode && areaNode.getAttribute("overflowtarget");
      if (overflowTarget) {
        areas.push(overflowTarget);
      }
    }
    areas.push(kPaletteId);

    while (aNode && aNode.parentNode) {
      let parent = aNode.parentNode;
      if (areas.includes(parent.id)) {
        return aNode;
      }
      aNode = parent;
    }
    return null;
  },

  _promiseWidgetAnimationOut(aNode) {
    if (!gCosmeticAnimationsEnabled ||
        aNode.getAttribute("cui-anchorid") == "nav-bar-overflow-button" ||
        (aNode.tagName != "toolbaritem" && aNode.tagName != "toolbarbutton") ||
        (aNode.id == "downloads-button" && aNode.hidden)) {
      return null;
    }

    let animationNode;
    if (aNode.parentNode && aNode.parentNode.id.startsWith("wrapper-")) {
      animationNode = aNode.parentNode;
    } else {
      animationNode = aNode;
    }
    return new Promise(resolve => {
      function cleanupCustomizationExit() {
        resolveAnimationPromise();
      }

      function cleanupWidgetAnimationEnd(e) {
        if (e.animationName == "widget-animate-out" && e.target.id == animationNode.id) {
          resolveAnimationPromise();
        }
      }

      function resolveAnimationPromise() {
        animationNode.removeEventListener("animationend", cleanupWidgetAnimationEnd);
        animationNode.removeEventListener("customizationending", cleanupCustomizationExit);
        resolve();
      }

      // Wait until the next frame before setting the class to ensure
      // we do start the animation.
      this.window.requestAnimationFrame(() => {
        this.window.requestAnimationFrame(() => {
          animationNode.classList.add("animate-out");
          animationNode.ownerGlobal.gNavToolbox.addEventListener("customizationending", cleanupCustomizationExit);
          animationNode.addEventListener("animationend", cleanupWidgetAnimationEnd);
        });
      });
    });
  },

  async addToToolbar(aNode) {
    aNode = this._getCustomizableChildForNode(aNode);
    if (aNode.localName == "toolbarpaletteitem" && aNode.firstChild) {
      aNode = aNode.firstChild;
    }
    let widgetAnimationPromise = this._promiseWidgetAnimationOut(aNode);
    if (widgetAnimationPromise) {
      await widgetAnimationPromise;
    }

    let widgetToAdd = aNode.id;
    if (CustomizableUI.isSpecialWidget(widgetToAdd) && aNode.closest("#customization-palette")) {
      widgetToAdd = widgetToAdd.match(/^customizableui-special-(spring|spacer|separator)/)[1];
    }

    CustomizableUI.addWidgetToArea(widgetToAdd, CustomizableUI.AREA_NAVBAR);
    if (!this._customizing) {
      CustomizableUI.dispatchToolboxEvent("customizationchange");
    }

    // If the user explicitly moves this item, turn off autohide.
    if (aNode.id == "downloads-button") {
      Services.prefs.setBoolPref(kDownloadAutoHidePref, false);
      if (this._customizing) {
        this._showDownloadsAutoHidePanel();
      }
    }

    if (widgetAnimationPromise) {
      if (aNode.parentNode && aNode.parentNode.id.startsWith("wrapper-")) {
        aNode.parentNode.classList.remove("animate-out");
      } else {
        aNode.classList.remove("animate-out");
      }
    }
  },

  async addToPanel(aNode) {
    aNode = this._getCustomizableChildForNode(aNode);
    if (aNode.localName == "toolbarpaletteitem" && aNode.firstChild) {
      aNode = aNode.firstChild;
    }
    let widgetAnimationPromise = this._promiseWidgetAnimationOut(aNode);
    if (widgetAnimationPromise) {
      await widgetAnimationPromise;
    }

    let panel = CustomizableUI.AREA_FIXED_OVERFLOW_PANEL;
    CustomizableUI.addWidgetToArea(aNode.id, panel);
    if (!this._customizing) {
      CustomizableUI.dispatchToolboxEvent("customizationchange");
    }

    // If the user explicitly moves this item, turn off autohide.
    if (aNode.id == "downloads-button") {
      Services.prefs.setBoolPref(kDownloadAutoHidePref, false);
      if (this._customizing) {
        this._showDownloadsAutoHidePanel();
      }
    }

    if (widgetAnimationPromise) {
      if (aNode.parentNode && aNode.parentNode.id.startsWith("wrapper-")) {
        aNode.parentNode.classList.remove("animate-out");
      } else {
        aNode.classList.remove("animate-out");
      }
    }
    if (gCosmeticAnimationsEnabled) {
      let overflowButton = this.document.getElementById("nav-bar-overflow-button");
      BrowserUtils.setToolbarButtonHeightProperty(overflowButton).then(() => {
        overflowButton.setAttribute("animate", "true");
        overflowButton.addEventListener("animationend", function onAnimationEnd(event) {
          if (event.animationName.startsWith("overflow-animation")) {
            this.setAttribute("fade", "true");
          } else if (event.animationName == "overflow-fade") {
            this.removeEventListener("animationend", onAnimationEnd);
            this.removeAttribute("animate");
            this.removeAttribute("fade");
          }
        });
      });
    }
  },

  async removeFromArea(aNode) {
    aNode = this._getCustomizableChildForNode(aNode);
    if (aNode.localName == "toolbarpaletteitem" && aNode.firstChild) {
      aNode = aNode.firstChild;
    }
    let widgetAnimationPromise = this._promiseWidgetAnimationOut(aNode);
    if (widgetAnimationPromise) {
      await widgetAnimationPromise;
    }

    CustomizableUI.removeWidgetFromArea(aNode.id);
    if (!this._customizing) {
      CustomizableUI.dispatchToolboxEvent("customizationchange");
    }

    // If the user explicitly removes this item, turn off autohide.
    if (aNode.id == "downloads-button") {
      Services.prefs.setBoolPref(kDownloadAutoHidePref, false);
      if (this._customizing) {
        this._showDownloadsAutoHidePanel();
      }
    }
    if (widgetAnimationPromise) {
      if (aNode.parentNode && aNode.parentNode.id.startsWith("wrapper-")) {
        aNode.parentNode.classList.remove("animate-out");
      } else {
        aNode.classList.remove("animate-out");
      }
    }
  },

  populatePalette() {
    let fragment = this.document.createDocumentFragment();
    let toolboxPalette = this.window.gNavToolbox.palette;

    try {
      let unusedWidgets = CustomizableUI.getUnusedWidgets(toolboxPalette);
      for (let widget of unusedWidgets) {
        let paletteItem = this.makePaletteItem(widget, "palette");
        if (!paletteItem) {
          continue;
        }
        fragment.appendChild(paletteItem);
      }

      let flexSpace = CustomizableUI.createSpecialWidget("spring", this.document);
      fragment.appendChild(this.wrapToolbarItem(flexSpace, "palette"));

      this.visiblePalette.appendChild(fragment);
      this._stowedPalette = this.window.gNavToolbox.palette;
      this.window.gNavToolbox.palette = this.visiblePalette;
    } catch (ex) {
      log.error(ex);
    }
  },

  // XXXunf Maybe this should use -moz-element instead of wrapping the node?
  //       Would ensure no weird interactions/event handling from original node,
  //       and makes it possible to put this in a lazy-loaded iframe/real tab
  //       while still getting rid of the need for overlays.
  makePaletteItem(aWidget, aPlace) {
    let widgetNode = aWidget.forWindow(this.window).node;
    if (!widgetNode) {
      log.error("Widget with id " + aWidget.id + " does not return a valid node");
      return null;
    }
    // Do not build a palette item for hidden widgets; there's not much to show.
    if (widgetNode.hidden) {
      return null;
    }

    let wrapper = this.createOrUpdateWrapper(widgetNode, aPlace);
    wrapper.appendChild(widgetNode);
    return wrapper;
  },

  depopulatePalette() {
    return (async () => {
      this.visiblePalette.hidden = true;
      let paletteChild = this.visiblePalette.firstChild;
      let nextChild;
      while (paletteChild) {
        nextChild = paletteChild.nextElementSibling;
        let itemId = paletteChild.firstChild.id;
        if (CustomizableUI.isSpecialWidget(itemId)) {
          this.visiblePalette.removeChild(paletteChild);
        } else {
          // XXXunf Currently this doesn't destroy the (now unused) node in the
          //       API provider case. It would be good to do so, but we need to
          //       keep strong refs to it in CustomizableUI (can't iterate of
          //       WeakMaps), and there's the question of what behavior
          //       wrappers should have if consumers keep hold of them.
          let unwrappedPaletteItem =
            await this.deferredUnwrapToolbarItem(paletteChild);
          this._stowedPalette.appendChild(unwrappedPaletteItem);
        }

        paletteChild = nextChild;
      }
      this.visiblePalette.hidden = false;
      this.window.gNavToolbox.palette = this._stowedPalette;
    })().catch(log.error);
  },

  isCustomizableItem(aNode) {
    return aNode.localName == "toolbarbutton" ||
           aNode.localName == "toolbaritem" ||
           aNode.localName == "toolbarseparator" ||
           aNode.localName == "toolbarspring" ||
           aNode.localName == "toolbarspacer";
  },

  isWrappedToolbarItem(aNode) {
    return aNode.localName == "toolbarpaletteitem";
  },

  deferredWrapToolbarItem(aNode, aPlace) {
    return new Promise(resolve => {
      dispatchFunction(() => {
        let wrapper = this.wrapToolbarItem(aNode, aPlace);
        resolve(wrapper);
      });
    });
  },

  wrapToolbarItem(aNode, aPlace) {
    if (!this.isCustomizableItem(aNode)) {
      return aNode;
    }
    let wrapper = this.createOrUpdateWrapper(aNode, aPlace);

    // It's possible that this toolbar node is "mid-flight" and doesn't have
    // a parent, in which case we skip replacing it. This can happen if a
    // toolbar item has been dragged into the palette. In that case, we tell
    // CustomizableUI to remove the widget from its area before putting the
    // widget in the palette - so the node will have no parent.
    if (aNode.parentNode) {
      aNode = aNode.parentNode.replaceChild(wrapper, aNode);
    }
    wrapper.appendChild(aNode);
    return wrapper;
  },

  createOrUpdateWrapper(aNode, aPlace, aIsUpdate) {
    let wrapper;
    if (aIsUpdate && aNode.parentNode && aNode.parentNode.localName == "toolbarpaletteitem") {
      wrapper = aNode.parentNode;
      aPlace = wrapper.getAttribute("place");
    } else {
      wrapper = this.document.createElement("toolbarpaletteitem");
      // "place" is used by toolkit to add the toolbarpaletteitem-palette
      // binding to a toolbarpaletteitem, which gives it a label node for when
      // it's sitting in the palette.
      wrapper.setAttribute("place", aPlace);
    }


    // Ensure the wrapped item doesn't look like it's in any special state, and
    // can't be interactved with when in the customization palette.
    // Note that some buttons opt out of this with the
    // keepbroadcastattributeswhencustomizing attribute.
    if (aNode.hasAttribute("command") &&
        aNode.getAttribute(kKeepBroadcastAttributes) != "true") {
      wrapper.setAttribute("itemcommand", aNode.getAttribute("command"));
      aNode.removeAttribute("command");
    }

    if (aNode.hasAttribute("observes") &&
        aNode.getAttribute(kKeepBroadcastAttributes) != "true") {
      wrapper.setAttribute("itemobserves", aNode.getAttribute("observes"));
      aNode.removeAttribute("observes");
    }

    if (aNode.getAttribute("checked") == "true") {
      wrapper.setAttribute("itemchecked", "true");
      aNode.removeAttribute("checked");
    }

    if (aNode.hasAttribute("id")) {
      wrapper.setAttribute("id", "wrapper-" + aNode.getAttribute("id"));
    }

    if (aNode.hasAttribute("label")) {
      wrapper.setAttribute("title", aNode.getAttribute("label"));
      wrapper.setAttribute("tooltiptext", aNode.getAttribute("label"));
    } else if (aNode.hasAttribute("title")) {
      wrapper.setAttribute("title", aNode.getAttribute("title"));
      wrapper.setAttribute("tooltiptext", aNode.getAttribute("title"));
    }

    if (aNode.hasAttribute("flex")) {
      wrapper.setAttribute("flex", aNode.getAttribute("flex"));
    }

    let removable = aPlace == "palette" || CustomizableUI.isWidgetRemovable(aNode);
    wrapper.setAttribute("removable", removable);

    // Allow touch events to initiate dragging in customize mode.
    // This is only supported on Windows for now.
    wrapper.setAttribute("touchdownstartsdrag", "true");

    let contextMenuAttrName = "";
    if (aNode.getAttribute("context")) {
      contextMenuAttrName = "context";
    } else if (aNode.getAttribute("contextmenu")) {
      contextMenuAttrName = "contextmenu";
    }
    let currentContextMenu = aNode.getAttribute(contextMenuAttrName);
    let contextMenuForPlace = aPlace == "menu-panel" ?
                                kPanelItemContextMenu :
                                kPaletteItemContextMenu;
    if (aPlace != "toolbar") {
      wrapper.setAttribute("context", contextMenuForPlace);
    }
    // Only keep track of the menu if it is non-default.
    if (currentContextMenu &&
        currentContextMenu != contextMenuForPlace) {
      aNode.setAttribute("wrapped-context", currentContextMenu);
      aNode.setAttribute("wrapped-contextAttrName", contextMenuAttrName);
      aNode.removeAttribute(contextMenuAttrName);
    } else if (currentContextMenu == contextMenuForPlace) {
      aNode.removeAttribute(contextMenuAttrName);
    }

    // Only add listeners for newly created wrappers:
    if (!aIsUpdate) {
      wrapper.addEventListener("mousedown", this);
      wrapper.addEventListener("mouseup", this);
    }

    if (CustomizableUI.isSpecialWidget(aNode.id)) {
      wrapper.setAttribute("title", gWidgetsBundle.GetStringFromName(aNode.nodeName + ".label"));
    }

    return wrapper;
  },

  deferredUnwrapToolbarItem(aWrapper) {
    return new Promise(resolve => {
      dispatchFunction(() => {
        let item = null;
        try {
          item = this.unwrapToolbarItem(aWrapper);
        } catch (ex) {
          Cu.reportError(ex);
        }
        resolve(item);
      });
    });
  },

  unwrapToolbarItem(aWrapper) {
    if (aWrapper.nodeName != "toolbarpaletteitem") {
      return aWrapper;
    }
    aWrapper.removeEventListener("mousedown", this);
    aWrapper.removeEventListener("mouseup", this);

    let place = aWrapper.getAttribute("place");

    let toolbarItem = aWrapper.firstChild;
    if (!toolbarItem) {
      log.error("no toolbarItem child for " + aWrapper.tagName + "#" + aWrapper.id);
      aWrapper.remove();
      return null;
    }

    if (aWrapper.hasAttribute("itemobserves")) {
      toolbarItem.setAttribute("observes", aWrapper.getAttribute("itemobserves"));
    }

    if (aWrapper.hasAttribute("itemchecked")) {
      toolbarItem.checked = true;
    }

    if (aWrapper.hasAttribute("itemcommand")) {
      let commandID = aWrapper.getAttribute("itemcommand");
      toolbarItem.setAttribute("command", commandID);

      // XXX Bug 309953 - toolbarbuttons aren't in sync with their commands after customizing
      let command = this.document.getElementById(commandID);
      if (command && command.hasAttribute("disabled")) {
        toolbarItem.setAttribute("disabled", command.getAttribute("disabled"));
      }
    }

    let wrappedContext = toolbarItem.getAttribute("wrapped-context");
    if (wrappedContext) {
      let contextAttrName = toolbarItem.getAttribute("wrapped-contextAttrName");
      toolbarItem.setAttribute(contextAttrName, wrappedContext);
      toolbarItem.removeAttribute("wrapped-contextAttrName");
      toolbarItem.removeAttribute("wrapped-context");
    } else if (place == "menu-panel") {
      toolbarItem.setAttribute("context", kPanelItemContextMenu);
    }

    if (aWrapper.parentNode) {
      aWrapper.parentNode.replaceChild(toolbarItem, aWrapper);
    }
    return toolbarItem;
  },

  async _wrapToolbarItem(aArea) {
    let target = CustomizableUI.getCustomizeTargetForArea(aArea, this.window);
    if (!target || this.areas.has(target)) {
      return null;
    }

    this._addDragHandlers(target);
    for (let child of target.children) {
      if (this.isCustomizableItem(child) && !this.isWrappedToolbarItem(child)) {
        await this.deferredWrapToolbarItem(child, CustomizableUI.getPlaceForItem(child)).catch(log.error);
      }
    }
    this.areas.add(target);
    return target;
  },

  _wrapToolbarItemSync(aArea) {
    let target = CustomizableUI.getCustomizeTargetForArea(aArea, this.window);
    if (!target || this.areas.has(target)) {
      return null;
    }

    this._addDragHandlers(target);
    try {
      for (let child of target.children) {
        if (this.isCustomizableItem(child) && !this.isWrappedToolbarItem(child)) {
          this.wrapToolbarItem(child, CustomizableUI.getPlaceForItem(child));
        }
      }
    } catch (ex) {
      log.error(ex, ex.stack);
    }

    this.areas.add(target);
    return target;
  },

  async _wrapToolbarItems() {
    for (let area of CustomizableUI.areas) {
      await this._wrapToolbarItem(area);
    }
  },

  _addDragHandlers(aTarget) {
    // Allow dropping on the padding of the arrow panel.
    if (aTarget.id == CustomizableUI.AREA_FIXED_OVERFLOW_PANEL) {
      aTarget = this.document.getElementById("customization-panelHolder");
    }
    aTarget.addEventListener("dragstart", this, true);
    aTarget.addEventListener("dragover", this, true);
    aTarget.addEventListener("dragexit", this, true);
    aTarget.addEventListener("drop", this, true);
    aTarget.addEventListener("dragend", this, true);
  },

  _wrapItemsInArea(target) {
    for (let child of target.children) {
      if (this.isCustomizableItem(child)) {
        this.wrapToolbarItem(child, CustomizableUI.getPlaceForItem(child));
      }
    }
  },

  _removeDragHandlers(aTarget) {
    // Remove handler from different target if it was added to
    // allow dropping on the padding of the arrow panel.
    if (aTarget.id == CustomizableUI.AREA_FIXED_OVERFLOW_PANEL) {
      aTarget = this.document.getElementById("customization-panelHolder");
    }
    aTarget.removeEventListener("dragstart", this, true);
    aTarget.removeEventListener("dragover", this, true);
    aTarget.removeEventListener("dragexit", this, true);
    aTarget.removeEventListener("drop", this, true);
    aTarget.removeEventListener("dragend", this, true);
  },

  _unwrapItemsInArea(target) {
    for (let toolbarItem of target.children) {
      if (this.isWrappedToolbarItem(toolbarItem)) {
        this.unwrapToolbarItem(toolbarItem);
      }
    }
  },

  _unwrapToolbarItems() {
    return (async () => {
      for (let target of this.areas) {
        for (let toolbarItem of target.children) {
          if (this.isWrappedToolbarItem(toolbarItem)) {
            await this.deferredUnwrapToolbarItem(toolbarItem);
          }
        }
        this._removeDragHandlers(target);
      }
      this.areas.clear();
    })().catch(log.error);
  },

  persistCurrentSets(aSetBeforePersisting) {
    let document = this.document;
    let toolbars = document.querySelectorAll("toolbar[customizable='true'][currentset]");
    for (let toolbar of toolbars) {
      if (aSetBeforePersisting) {
        let set = toolbar.currentSet;
        toolbar.setAttribute("currentset", set);
      }
      // Persist the currentset attribute directly on hardcoded toolbars.
      document.persist(toolbar.id, "currentset");
    }
  },

  reset() {
    this.resetting = true;
    // Disable the reset button temporarily while resetting:
    let btn = this.document.getElementById("customization-reset-button");
    BrowserUITelemetry.countCustomizationEvent("reset");
    btn.disabled = true;
    return (async () => {
      await this.depopulatePalette();
      await this._unwrapToolbarItems();

      CustomizableUI.reset();

      this._updateLWThemeButtonIcon();

      await this._wrapToolbarItems();
      this.populatePalette();

      this.persistCurrentSets(true);

      this._updateResetButton();
      this._updateUndoResetButton();
      this._updateEmptyPaletteNotice();
      this._moveDownloadsButtonToNavBar = false;
      this.resetting = false;
      if (!this._wantToBeInCustomizeMode) {
        this.exit();
      }
    })().catch(log.error);
  },

  undoReset() {
    this.resetting = true;

    return (async () => {
      await this.depopulatePalette();
      await this._unwrapToolbarItems();

      CustomizableUI.undoReset();

      this._updateLWThemeButtonIcon();

      await this._wrapToolbarItems();
      this.populatePalette();

      this.persistCurrentSets(true);

      this._updateResetButton();
      this._updateUndoResetButton();
      this._updateEmptyPaletteNotice();
      this._moveDownloadsButtonToNavBar = false;
      this.resetting = false;
    })().catch(log.error);
  },

  _onToolbarVisibilityChange(aEvent) {
    let toolbar = aEvent.target;
    if (aEvent.detail.visible && toolbar.getAttribute("customizable") == "true") {
      toolbar.setAttribute("customizing", "true");
    } else {
      toolbar.removeAttribute("customizing");
    }
    this._onUIChange();
  },

  onWidgetMoved(aWidgetId, aArea, aOldPosition, aNewPosition) {
    this._onUIChange();
  },

  onWidgetAdded(aWidgetId, aArea, aPosition) {
    this._onUIChange();
  },

  onWidgetRemoved(aWidgetId, aArea) {
    this._onUIChange();
  },

  onWidgetBeforeDOMChange(aNodeToChange, aSecondaryNode, aContainer) {
    if (aContainer.ownerGlobal != this.window || this.resetting) {
      return;
    }
    // If we get called for widgets that aren't in the window yet, they might not have
    // a parentNode at all.
    if (aNodeToChange.parentNode) {
      this.unwrapToolbarItem(aNodeToChange.parentNode);
    }
    if (aSecondaryNode) {
      this.unwrapToolbarItem(aSecondaryNode.parentNode);
    }
  },

  onWidgetAfterDOMChange(aNodeToChange, aSecondaryNode, aContainer) {
    if (aContainer.ownerGlobal != this.window || this.resetting) {
      return;
    }
    // If the node is still attached to the container, wrap it again:
    if (aNodeToChange.parentNode) {
      let place = CustomizableUI.getPlaceForItem(aNodeToChange);
      this.wrapToolbarItem(aNodeToChange, place);
      if (aSecondaryNode) {
        this.wrapToolbarItem(aSecondaryNode, place);
      }
    } else {
      // If not, it got removed.

      // If an API-based widget is removed while customizing, append it to the palette.
      // The _applyDrop code itself will take care of positioning it correctly, if
      // applicable. We need the code to be here so removing widgets using CustomizableUI's
      // API also does the right thing (and adds it to the palette)
      let widgetId = aNodeToChange.id;
      let widget = CustomizableUI.getWidget(widgetId);
      if (widget.provider == CustomizableUI.PROVIDER_API) {
        let paletteItem = this.makePaletteItem(widget, "palette");
        this.visiblePalette.appendChild(paletteItem);
      }
    }
  },

  onWidgetDestroyed(aWidgetId) {
    let wrapper = this.document.getElementById("wrapper-" + aWidgetId);
    if (wrapper) {
      wrapper.remove();
    }
  },

  onWidgetAfterCreation(aWidgetId, aArea) {
    // If the node was added to an area, we would have gotten an onWidgetAdded notification,
    // plus associated DOM change notifications, so only do stuff for the palette:
    if (!aArea) {
      let widgetNode = this.document.getElementById(aWidgetId);
      if (widgetNode) {
        this.wrapToolbarItem(widgetNode, "palette");
      } else {
        let widget = CustomizableUI.getWidget(aWidgetId);
        this.visiblePalette.appendChild(this.makePaletteItem(widget, "palette"));
      }
    }
  },

  onAreaNodeRegistered(aArea, aContainer) {
    if (aContainer.ownerDocument == this.document) {
      this._wrapItemsInArea(aContainer);
      this._addDragHandlers(aContainer);
      this.areas.add(aContainer);
    }
  },

  onAreaNodeUnregistered(aArea, aContainer, aReason) {
    if (aContainer.ownerDocument == this.document && aReason == CustomizableUI.REASON_AREA_UNREGISTERED) {
      this._unwrapItemsInArea(aContainer);
      this._removeDragHandlers(aContainer);
      this.areas.delete(aContainer);
    }
  },

  openAddonsManagerThemes(aEvent) {
    aEvent.target.parentNode.parentNode.hidePopup();
    this.window.BrowserOpenAddonsMgr("addons://list/theme");
  },

  getMoreThemes(aEvent) {
    aEvent.target.parentNode.parentNode.hidePopup();
    let getMoreURL = Services.urlFormatter.formatURLPref("lightweightThemes.getMoreURL");
    this.window.openUILinkIn(getMoreURL, "tab");
  },

  updateUIDensity(mode) {
    this.window.gUIDensity.update(mode);
  },

  setUIDensity(mode) {
    let win = this.window;
    let gUIDensity = win.gUIDensity;
    let currentDensity = gUIDensity.getCurrentDensity();
    let panel = win.document.getElementById("customization-uidensity-menu");

    Services.prefs.setIntPref(gUIDensity.uiDensityPref, mode);

    // If the user is choosing a different UI density mode while
    // the mode is overriden to Touch, remove the override.
    if (currentDensity.overridden) {
      Services.prefs.setBoolPref(gUIDensity.autoTouchModePref, false);
    }

    this._onUIChange();
    panel.hidePopup();
  },

  resetUIDensity() {
    this.window.gUIDensity.update();
  },

  onUIDensityMenuShowing() {
    let win = this.window;
    let doc = win.document;
    let gUIDensity = win.gUIDensity;
    let currentDensity = gUIDensity.getCurrentDensity();

    let normalItem = doc.getElementById("customization-uidensity-menuitem-normal");
    normalItem.mode = gUIDensity.MODE_NORMAL;

    let compactItem = doc.getElementById("customization-uidensity-menuitem-compact");
    compactItem.mode = gUIDensity.MODE_COMPACT;

    let items = [normalItem, compactItem];

    let touchItem = doc.getElementById("customization-uidensity-menuitem-touch");
    // Touch mode can not be enabled in OSX right now.
    if (touchItem) {
      touchItem.mode = gUIDensity.MODE_TOUCH;
      items.push(touchItem);
    }

    // Mark the active mode menuitem.
    for (let item of items) {
      if (item.mode == currentDensity.mode) {
        item.setAttribute("aria-checked", "true");
        item.setAttribute("active", "true");
      } else {
        item.removeAttribute("aria-checked");
        item.removeAttribute("active");
      }
    }

    // Add menu items for automatically switching to Touch mode in Windows Tablet Mode,
    // which is only available in Windows 10.
    if (AppConstants.isPlatformAndVersionAtLeast("win", "10")) {
      let spacer = doc.getElementById("customization-uidensity-touch-spacer");
      let checkbox = doc.getElementById("customization-uidensity-autotouchmode-checkbox");
      spacer.removeAttribute("hidden");
      checkbox.removeAttribute("hidden");

      // Show a hint that the UI density was overridden automatically.
      if (currentDensity.overridden) {
        let sb = Services.strings.createBundle("chrome://browser/locale/uiDensity.properties");
        touchItem.setAttribute("acceltext",
                                 sb.GetStringFromName("uiDensity.menuitem-touch.acceltext"));
      } else {
        touchItem.removeAttribute("acceltext");
      }

      let autoTouchMode = Services.prefs.getBoolPref(win.gUIDensity.autoTouchModePref);
      if (autoTouchMode) {
        checkbox.setAttribute("checked", "true");
      } else {
        checkbox.removeAttribute("checked");
      }
    }
  },

  updateAutoTouchMode(checked) {
    Services.prefs.setBoolPref("browser.touchmode.auto", checked);
    // Re-render the menu items since the active mode might have
    // change because of this.
    this.onUIDensityMenuShowing();
    this._onUIChange();
  },

  onLWThemesMenuShowing(aEvent) {
    const DEFAULT_THEME_ID = "{972ce4c6-7e08-4474-a285-3208198ce6fd}";
    const LIGHT_THEME_ID = "[email protected]";
    const DARK_THEME_ID = "[email protected]";
    const MAX_THEME_COUNT = 6;

    this._clearLWThemesMenu(aEvent.target);

    function previewTheme(aPreviewThemeEvent) {
      LightweightThemeManager.previewTheme(
        aPreviewThemeEvent.target.theme.id != DEFAULT_THEME_ID ?
        aPreviewThemeEvent.target.theme : null);
    }

    function resetPreview() {
      LightweightThemeManager.resetPreview();
    }

    let onThemeSelected = panel => {
      this._updateLWThemeButtonIcon();
      this._onUIChange();
      panel.hidePopup();
    };

    AddonManager.getAddonByID(DEFAULT_THEME_ID, aDefaultTheme => {
      let doc = this.window.document;

      function buildToolbarButton(aTheme) {
        let tbb = doc.createElement("toolbarbutton");
        tbb.theme = aTheme;
        tbb.setAttribute("label", aTheme.name);
        if (aDefaultTheme == aTheme) {
          // The actual icon is set up so it looks nice in about:addons, but
          // we'd like the version that's correct for the OS we're on, so we set
          // an attribute that our styling will then use to display the icon.
          tbb.setAttribute("defaulttheme", "true");
        } else {
          tbb.setAttribute("image", aTheme.iconURL);
        }
        if (aTheme.description)
          tbb.setAttribute("tooltiptext", aTheme.description);
        tbb.setAttribute("tabindex", "0");
        tbb.classList.add("customization-lwtheme-menu-theme");
        let isActive = activeThemeID == aTheme.id;
        tbb.setAttribute("aria-checked", isActive);
        tbb.setAttribute("role", "menuitemradio");
        if (isActive) {
          tbb.setAttribute("active", "true");
        }
        tbb.addEventListener("focus", previewTheme);
        tbb.addEventListener("mouseover", previewTheme);
        tbb.addEventListener("blur", resetPreview);
        tbb.addEventListener("mouseout", resetPreview);

        return tbb;
      }

      let themes = [aDefaultTheme];
      let lwts = LightweightThemeManager.usedThemes;
      let currentLwt = LightweightThemeManager.currentTheme;

      let activeThemeID = currentLwt ? currentLwt.id : DEFAULT_THEME_ID;

      // Move the current theme (if any) and the light/dark themes to the start:
      let importantThemes = [LIGHT_THEME_ID, DARK_THEME_ID];
      if (currentLwt && !importantThemes.includes(currentLwt.id)) {
        importantThemes.push(currentLwt.id);
      }
      for (let importantTheme of importantThemes) {
        let themeIndex = lwts.findIndex(theme => theme.id == importantTheme);
        if (themeIndex > -1) {
          themes.push(...lwts.splice(themeIndex, 1));
        }
      }
      themes = themes.concat(lwts);
      if (themes.length > MAX_THEME_COUNT)
        themes.length = MAX_THEME_COUNT;

      let footer = doc.getElementById("customization-lwtheme-menu-footer");
      let panel = footer.parentNode;
      let recommendedLabel = doc.getElementById("customization-lwtheme-menu-recommended");
      for (let theme of themes) {
        let button = buildToolbarButton(theme);
        button.addEventListener("command", () => {
          if ("userDisabled" in button.theme)
            button.theme.userDisabled = false;
          else
            LightweightThemeManager.currentTheme = button.theme;
          onThemeSelected(panel);
        });
        panel.insertBefore(button, recommendedLabel);
      }

      let lwthemePrefs = Services.prefs.getBranch("lightweightThemes.");
      let recommendedThemes = lwthemePrefs.getStringPref("recommendedThemes");
      recommendedThemes = JSON.parse(recommendedThemes);
      let sb = Services.strings.createBundle("chrome://browser/locale/lightweightThemes.properties");
      for (let theme of recommendedThemes) {
        try {
          theme.name = sb.GetStringFromName("lightweightThemes." + theme.id + ".name");
          theme.description = sb.GetStringFromName("lightweightThemes." + theme.id + ".description");
        } catch (ex) {
          // If finding strings for this failed, just don't build it. This can
          // happen for users with 'older' recommended themes lists, some of which
          // have since been removed from Firefox.
          continue;
        }
        let button = buildToolbarButton(theme);
        button.addEventListener("command", () => {
          LightweightThemeManager.setLocalTheme(button.theme);
          recommendedThemes = recommendedThemes.filter((aTheme) => { return aTheme.id != button.theme.id; });
          lwthemePrefs.setStringPref("recommendedThemes",
                                     JSON.stringify(recommendedThemes));
          onThemeSelected(panel);
        });
        panel.insertBefore(button, footer);
      }
      let hideRecommendedLabel = (footer.previousSibling == recommendedLabel);
      recommendedLabel.hidden = hideRecommendedLabel;
    });
  },

  _clearLWThemesMenu(panel) {
    let footer = this.document.getElementById("customization-lwtheme-menu-footer");
    let recommendedLabel = this.document.getElementById("customization-lwtheme-menu-recommended");
    for (let element of [footer, recommendedLabel]) {
      while (element.previousSibling &&
             element.previousSibling.localName == "toolbarbutton") {
        element.previousSibling.remove();
      }
    }

    // Workaround for bug 1059934
    panel.removeAttribute("height");
  },

  _onUIChange() {
    this._changed = true;
    if (!this.resetting) {
      this._updateResetButton();
      this._updateUndoResetButton();
      this._updateEmptyPaletteNotice();
    }
    CustomizableUI.dispatchToolboxEvent("customizationchange");
  },

  _updateEmptyPaletteNotice() {
    let paletteItems = this.visiblePalette.getElementsByTagName("toolbarpaletteitem");
    let whimsyButton = this.document.getElementById("whimsy-button");

    if (paletteItems.length == 1 &&
        paletteItems[0].id.includes("wrapper-customizableui-special-spring")) {
      whimsyButton.hidden = false;
    } else {
      this.togglePong(false);
      whimsyButton.hidden = true;
    }
  },

  _updateResetButton() {
    let btn = this.document.getElementById("customization-reset-button");
    btn.disabled = CustomizableUI.inDefaultState;
  },

  _updateUndoResetButton() {
    let undoResetButton =  this.document.getElementById("customization-undo-reset-button");
    undoResetButton.hidden = !CustomizableUI.canUndoReset;
  },

  handleEvent(aEvent) {
    switch (aEvent.type) {
      case "toolbarvisibilitychange":
        this._onToolbarVisibilityChange(aEvent);
        break;
      case "dragstart":
        this._onDragStart(aEvent);
        break;
      case "dragover":
        this._onDragOver(aEvent);
        break;
      case "drop":
        this._onDragDrop(aEvent);
        break;
      case "dragexit":
        this._onDragExit(aEvent);
        break;
      case "dragend":
        this._onDragEnd(aEvent);
        break;
      case "mousedown":
        this._onMouseDown(aEvent);
        break;
      case "mouseup":
        this._onMouseUp(aEvent);
        break;
      case "keypress":
        if (aEvent.keyCode == aEvent.DOM_VK_ESCAPE) {
          this.exit();
        }
        break;
      case "unload":
        this.uninit();
        break;
    }
  },

  /**
   * We handle dragover/drop on the outer palette separately
   * to avoid overlap with other drag/drop handlers.
   */
  _setupPaletteDragging() {
    this._addDragHandlers(this.visiblePalette);

    this.paletteDragHandler = (aEvent) => {
      let originalTarget = aEvent.originalTarget;
      if (this._isUnwantedDragDrop(aEvent) ||
          this.visiblePalette.contains(originalTarget) ||
          this.document.getElementById("customization-panelHolder").contains(originalTarget)) {
        return;
      }
      // We have a dragover/drop on the palette.
      if (aEvent.type == "dragover") {
        this._onDragOver(aEvent, this.visiblePalette);
      } else {
        this._onDragDrop(aEvent, this.visiblePalette);
      }
    };
    let contentContainer = this.document.getElementById("customization-content-container");
    contentContainer.addEventListener("dragover", this.paletteDragHandler, true);
    contentContainer.addEventListener("drop", this.paletteDragHandler, true);
  },

  _teardownPaletteDragging() {
    DragPositionManager.stop();
    this._removeDragHandlers(this.visiblePalette);

    let contentContainer = this.document.getElementById("customization-content-container");
    contentContainer.removeEventListener("dragover", this.paletteDragHandler, true);
    contentContainer.removeEventListener("drop", this.paletteDragHandler, true);
    delete this.paletteDragHandler;
  },

  observe(aSubject, aTopic, aData) {
    switch (aTopic) {
      case "nsPref:changed":
        this._updateResetButton();
        this._updateUndoResetButton();
        if (AppConstants.CAN_DRAW_IN_TITLEBAR) {
          this._updateTitlebarCheckbox();
          this._updateDragSpaceCheckbox();
        }
        break;
    }
  },

  _updateTitlebarCheckbox() {
    if (!AppConstants.CAN_DRAW_IN_TITLEBAR) {
      return;
    }
    let drawInTitlebar = Services.prefs.getBoolPref(kDrawInTitlebarPref, true);
    let checkbox = this.document.getElementById("customization-titlebar-visibility-checkbox");
    // Drawing in the titlebar means 'hiding' the titlebar.
    // We use the attribute rather than a property because if we're not in
    // customize mode the button is hidden and properties don't work.
    if (drawInTitlebar) {
      checkbox.removeAttribute("checked");
    } else {
      checkbox.setAttribute("checked", "true");
    }
  },

  _updateDragSpaceCheckbox() {
    if (!AppConstants.CAN_DRAW_IN_TITLEBAR) {
      return;
    }

    let extraDragSpace = Services.prefs.getBoolPref(kExtraDragSpacePref);
    let drawInTitlebar = Services.prefs.getBoolPref(kDrawInTitlebarPref, true);
    let menuBar = this.document.getElementById("toolbar-menubar");
    let menuBarEnabled = menuBar
      && AppConstants.platform != "macosx"
      && menuBar.getAttribute("autohide") != "true";

    let checkbox = this.document.getElementById("customization-extra-drag-space-checkbox");
    if (extraDragSpace) {
      checkbox.setAttribute("checked", "true");
    } else {
      checkbox.removeAttribute("checked");
    }

    if (!drawInTitlebar || menuBarEnabled) {
      checkbox.setAttribute("disabled", "true");
    } else {
      checkbox.removeAttribute("disabled");
    }
  },

  toggleTitlebar(aShouldShowTitlebar) {
    if (!AppConstants.CAN_DRAW_IN_TITLEBAR) {
      return;
    }
    // Drawing in the titlebar means not showing the titlebar, hence the negation:
    Services.prefs.setBoolPref(kDrawInTitlebarPref, !aShouldShowTitlebar);
    this._updateDragSpaceCheckbox();
  },

  toggleDragSpace(aShouldShowDragSpace) {
    if (!AppConstants.CAN_DRAW_IN_TITLEBAR) {
      return;
    }

    Services.prefs.setBoolPref(kExtraDragSpacePref, aShouldShowDragSpace);
  },

  get _dwu() {
    if (!this.__dwu) {
      this.__dwu = this.window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
    }
    return this.__dwu;
  },

  get _dir() {
    if (!this.__dir) {
      this.__dir = this.window.getComputedStyle(this.document.documentElement).direction;
    }
    return this.__dir;
  },

  _onDragStart(aEvent) {
    __dumpDragData(aEvent);
    let item = aEvent.target;
    while (item && item.localName != "toolbarpaletteitem") {
      if (item.localName == "toolbar" || item.id == kPaletteId ||
          item.id == "customization-panelHolder") {
        return;
      }
      item = item.parentNode;
    }

    let draggedItem = item.firstChild;
    let placeForItem = CustomizableUI.getPlaceForItem(item);

    let dt = aEvent.dataTransfer;
    let documentId = aEvent.target.ownerDocument.documentElement.id;

    dt.mozSetDataAt(kDragDataTypePrefix + documentId, draggedItem.id, 0);
    dt.effectAllowed = "move";

    let itemRect = this._dwu.getBoundsWithoutFlushing(draggedItem);
    let itemCenter = {x: itemRect.left + itemRect.width / 2,
                      y: itemRect.top + itemRect.height / 2};
    this._dragOffset = {x: aEvent.clientX - itemCenter.x,
                        y: aEvent.clientY - itemCenter.y};

    let toolbarParent = draggedItem.closest("toolbar");
    if (toolbarParent) {
      let toolbarRect = this._dwu.getBoundsWithoutFlushing(toolbarParent);
      toolbarParent.style.minHeight = toolbarRect.height + "px";
    }

    gDraggingInToolbars = new Set();

    // Hack needed so that the dragimage will still show the
    // item as it appeared before it was hidden.
    this._initializeDragAfterMove = () => {
      // For automated tests, we sometimes start exiting customization mode
      // before this fires, which leaves us with placeholders inserted after
      // we've exited. So we need to check that we are indeed customizing.
      if (this._customizing && !this._transitioning) {
        item.hidden = true;
        DragPositionManager.start(this.window);
        let canUsePrevSibling = placeForItem == "toolbar" || placeForItem == "menu-panel";
        if (item.nextSibling) {
          this._setDragActive(item.nextSibling, "before", draggedItem.id, placeForItem);
          this._dragOverItem = item.nextSibling;
        } else if (canUsePrevSibling && item.previousSibling) {
          this._setDragActive(item.previousSibling, "after", draggedItem.id, placeForItem);
          this._dragOverItem = item.previousSibling;
        }
        let currentArea = this._getCustomizableParent(item);
        currentArea.setAttribute("draggingover", "true");
      }
      this._initializeDragAfterMove = null;
      this.window.clearTimeout(this._dragInitializeTimeout);
    };
    this._dragInitializeTimeout = this.window.setTimeout(this._initializeDragAfterMove, 0);
  },

  _onDragOver(aEvent, aOverrideTarget) {
    if (this._isUnwantedDragDrop(aEvent)) {
      return;
    }
    if (this._initializeDragAfterMove) {
      this._initializeDragAfterMove();
    }

    __dumpDragData(aEvent);

    let document = aEvent.target.ownerDocument;
    let documentId = document.documentElement.id;
    if (!aEvent.dataTransfer.mozTypesAt(0)) {
      return;
    }

    let draggedItemId =
      aEvent.dataTransfer.mozGetDataAt(kDragDataTypePrefix + documentId, 0);
    let draggedWrapper = document.getElementById("wrapper-" + draggedItemId);
    let targetArea = this._getCustomizableParent(aOverrideTarget || aEvent.currentTarget);
    let originArea = this._getCustomizableParent(draggedWrapper);

    // Do nothing if the target or origin are not customizable.
    if (!targetArea || !originArea) {
      return;
    }

    // Do nothing if the widget is not allowed to be removed.
    if (targetArea.id == kPaletteId &&
       !CustomizableUI.isWidgetRemovable(draggedItemId)) {
      return;
    }

    // Do nothing if the widget is not allowed to move to the target area.
    if (targetArea.id != kPaletteId &&
        !CustomizableUI.canWidgetMoveToArea(draggedItemId, targetArea.id)) {
      return;
    }

    let targetAreaType = CustomizableUI.getPlaceForItem(targetArea);
    let targetNode = this._getDragOverNode(aEvent, targetArea, targetAreaType, draggedItemId);

    // We need to determine the place that the widget is being dropped in
    // the target.
    let dragOverItem, dragValue;
    if (targetNode == targetArea.customizationTarget) {
      // We'll assume if the user is dragging directly over the target, that
      // they're attempting to append a child to that target.
      dragOverItem = (targetAreaType == "toolbar"
                        ? this._findVisiblePreviousSiblingNode(targetNode.lastChild)
                        : targetNode.lastChild) ||
                     targetNode;
      dragValue = "after";
    } else {
      let targetParent = targetNode.parentNode;
      let position = Array.indexOf(targetParent.children, targetNode);
      if (position == -1) {
        dragOverItem = (targetAreaType == "toolbar"
                          ? this._findVisiblePreviousSiblingNode(targetNode.lastChild)
                          : targetNode.lastChild);
        dragValue = "after";
      } else {
        dragOverItem = targetParent.children[position];
        if (targetAreaType == "toolbar") {
          // Check if the aDraggedItem is hovered past the first half of dragOverItem
          let itemRect = this._dwu.getBoundsWithoutFlushing(dragOverItem);
          let dropTargetCenter = itemRect.left + (itemRect.width / 2);
          let existingDir = dragOverItem.getAttribute("dragover");
          let dirFactor = this._dir == "ltr" ? 1 : -1;
          if (existingDir == "before") {
            dropTargetCenter += (parseInt(dragOverItem.style.borderInlineStartWidth) || 0) / 2 * dirFactor;
          } else {
            dropTargetCenter -= (parseInt(dragOverItem.style.borderInlineEndWidth) || 0) / 2 * dirFactor;
          }
          let before = this._dir == "ltr" ? aEvent.clientX < dropTargetCenter : aEvent.clientX > dropTargetCenter;
          dragValue = before ? "before" : "after";
        } else if (targetAreaType == "menu-panel") {
          let itemRect = this._dwu.getBoundsWithoutFlushing(dragOverItem);
          let dropTargetCenter = itemRect.top + (itemRect.height / 2);
          let existingDir = dragOverItem.getAttribute("dragover");
          if (existingDir == "before") {
            dropTargetCenter += (parseInt(dragOverItem.style.borderBlockStartWidth) || 0) / 2;
          } else {
            dropTargetCenter -= (parseInt(dragOverItem.style.borderBlockEndWidth) || 0) / 2;
          }
          dragValue = aEvent.clientY < dropTargetCenter ? "before" : "after";
        } else {
          dragValue = "before";
        }
      }
    }

    if (this._dragOverItem && dragOverItem != this._dragOverItem) {
      this._cancelDragActive(this._dragOverItem, dragOverItem);
    }

    if (dragOverItem != this._dragOverItem || dragValue != dragOverItem.getAttribute("dragover")) {
      if (dragOverItem != targetArea.customizationTarget) {
        this._setDragActive(dragOverItem, dragValue, draggedItemId, targetAreaType);
      }
      this._dragOverItem = dragOverItem;
      targetArea.setAttribute("draggingover", "true");
    }

    aEvent.preventDefault();
    aEvent.stopPropagation();
  },

  _onDragDrop(aEvent, aOverrideTarget) {
    if (this._isUnwantedDragDrop(aEvent)) {
      return;
    }

    __dumpDragData(aEvent);
    this._initializeDragAfterMove = null;
    this.window.clearTimeout(this._dragInitializeTimeout);

    let targetArea = this._getCustomizableParent(aOverrideTarget || aEvent.currentTarget);
    let document = aEvent.target.ownerDocument;
    let documentId = document.documentElement.id;
    let draggedItemId =
      aEvent.dataTransfer.mozGetDataAt(kDragDataTypePrefix + documentId, 0);
    let draggedWrapper = document.getElementById("wrapper-" + draggedItemId);
    let originArea = this._getCustomizableParent(draggedWrapper);
    if (this._dragSizeMap) {
      this._dragSizeMap = new WeakMap();
    }
    // Do nothing if the target area or origin area are not customizable.
    if (!targetArea || !originArea) {
      return;
    }
    let targetNode = this._dragOverItem;
    let dropDir = targetNode.getAttribute("dragover");
    // Need to insert *after* this node if we promised the user that:
    if (targetNode != targetArea && dropDir == "after") {
      if (targetNode.nextSibling) {
        targetNode = targetNode.nextSibling;
      } else {
        targetNode = targetArea;
      }
    }
    if (targetNode.tagName == "toolbarpaletteitem") {
      targetNode = targetNode.firstChild;
    }

    this._cancelDragActive(this._dragOverItem, null, true);

    try {
      this._applyDrop(aEvent, targetArea, originArea, draggedItemId, targetNode);
    } catch (ex) {
      log.error(ex, ex.stack);
    }

    // If the user explicitly moves this item, turn off autohide.
    if (draggedItemId == "downloads-button") {
      Services.prefs.setBoolPref(kDownloadAutoHidePref, false);
      this._showDownloadsAutoHidePanel();
    }
  },

  _applyDrop(aEvent, aTargetArea, aOriginArea, aDraggedItemId, aTargetNode) {
    let document = aEvent.target.ownerDocument;
    let draggedItem = document.getElementById(aDraggedItemId);
    draggedItem.hidden = false;
    draggedItem.removeAttribute("mousedown");

    let toolbarParent = draggedItem.closest("toolbar");
    if (toolbarParent) {
      toolbarParent.style.removeProperty("min-height");
    }

    // Do nothing if the target was dropped onto itself (ie, no change in area
    // or position).
    if (draggedItem == aTargetNode) {
      return;
    }

    // Is the target area the customization palette?
    if (aTargetArea.id == kPaletteId) {
      // Did we drag from outside the palette?
      if (aOriginArea.id !== kPaletteId) {
        if (!CustomizableUI.isWidgetRemovable(aDraggedItemId)) {
          return;
        }

        CustomizableUI.removeWidgetFromArea(aDraggedItemId);
        BrowserUITelemetry.countCustomizationEvent("remove");
        // Special widgets are removed outright, we can return here:
        if (CustomizableUI.isSpecialWidget(aDraggedItemId)) {
          return;
        }
      }
      draggedItem = draggedItem.parentNode;

      // If the target node is the palette itself, just append
      if (aTargetNode == this.visiblePalette) {
        this.visiblePalette.appendChild(draggedItem);
      } else {
        // The items in the palette are wrapped, so we need the target node's parent here:
        this.visiblePalette.insertBefore(draggedItem, aTargetNode.parentNode);
      }
      if (aOriginArea.id !== kPaletteId) {
        // The dragend event already fires when the item moves within the palette.
        this._onDragEnd(aEvent);
      }
      return;
    }

    if (!CustomizableUI.canWidgetMoveToArea(aDraggedItemId, aTargetArea.id)) {
      return;
    }

    // Skipintoolbarset items won't really be moved:
    if (draggedItem.getAttribute("skipintoolbarset") == "true") {
      // These items should never leave their area:
      if (aTargetArea != aOriginArea) {
        return;
      }
      let place = draggedItem.parentNode.getAttribute("place");
      this.unwrapToolbarItem(draggedItem.parentNode);
      if (aTargetNode == aTargetArea.customizationTarget) {
        aTargetArea.customizationTarget.appendChild(draggedItem);
      } else {
        this.unwrapToolbarItem(aTargetNode.parentNode);
        aTargetArea.customizationTarget.insertBefore(draggedItem, aTargetNode);
        this.wrapToolbarItem(aTargetNode, place);
      }
      this.wrapToolbarItem(draggedItem, place);
      BrowserUITelemetry.countCustomizationEvent("move");
      return;
    }

    // Is the target the customization area itself? If so, we just add the
    // widget to the end of the area.
    if (aTargetNode == aTargetArea.customizationTarget) {
      CustomizableUI.addWidgetToArea(aDraggedItemId, aTargetArea.id);
      // For the purposes of BrowserUITelemetry, we consider both moving a widget
      // within the same area, and adding a widget from one area to another area
      // as a "move". An "add" is only when we move an item from the palette into
      // an area.
      let custEventType = aOriginArea.id == kPaletteId ? "add" : "move";
      BrowserUITelemetry.countCustomizationEvent(custEventType);
      this._onDragEnd(aEvent);
      return;
    }

    // We need to determine the place that the widget is being dropped in
    // the target.
    let placement;
    let itemForPlacement = aTargetNode;
    // Skip the skipintoolbarset items when determining the place of the item:
    while (itemForPlacement && itemForPlacement.getAttribute("skipintoolbarset") == "true" &&
           itemForPlacement.parentNode &&
           itemForPlacement.parentNode.nodeName == "toolbarpaletteitem") {
      itemForPlacement = itemForPlacement.parentNode.nextSibling;
      if (itemForPlacement && itemForPlacement.nodeName == "toolbarpaletteitem") {
        itemForPlacement = itemForPlacement.firstChild;
      }
    }
    if (itemForPlacement) {
      let targetNodeId = (itemForPlacement.nodeName == "toolbarpaletteitem") ?
                            itemForPlacement.firstChild && itemForPlacement.firstChild.id :
                            itemForPlacement.id;
      placement = CustomizableUI.getPlacementOfWidget(targetNodeId);
    }
    if (!placement) {
      log.debug("Could not get a position for " + aTargetNode.nodeName + "#" + aTargetNode.id + "." + aTargetNode.className);
    }
    let position = placement ? placement.position : null;

    // Force creating a new spacer/spring/separator if dragging from the palette
    if (CustomizableUI.isSpecialWidget(aDraggedItemId) && aOriginArea.id == kPaletteId) {
      aDraggedItemId = aDraggedItemId.match(/^customizableui-special-(spring|spacer|separator)/)[1];
    }

    // Is the target area the same as the origin? Since we've already handled
    // the possibility that the target is the customization palette, we know
    // that the widget is moving within a customizable area.
    if (aTargetArea == aOriginArea) {
      CustomizableUI.moveWidgetWithinArea(aDraggedItemId, position);
    } else {
      CustomizableUI.addWidgetToArea(aDraggedItemId, aTargetArea.id, position);
    }

    this._onDragEnd(aEvent);

    // For BrowserUITelemetry, an "add" is only when we move an item from the palette
    // into an area. Otherwise, it's a move.
    let custEventType = aOriginArea.id == kPaletteId ? "add" : "move";
    BrowserUITelemetry.countCustomizationEvent(custEventType);

    // If we dropped onto a skipintoolbarset item, manually correct the drop location:
    if (aTargetNode != itemForPlacement) {
      let draggedWrapper = draggedItem.parentNode;
      let container = draggedWrapper.parentNode;
      container.insertBefore(draggedWrapper, aTargetNode.parentNode);
    }
  },

  _onDragExit(aEvent) {
    if (this._isUnwantedDragDrop(aEvent)) {
      return;
    }

    __dumpDragData(aEvent);

    // When leaving customization areas, cancel the drag on the last dragover item
    // We've attached the listener to areas, so aEvent.currentTarget will be the area.
    // We don't care about dragexit events fired on descendants of the area,
    // so we check that the event's target is the same as the area to which the listener
    // was attached.
    if (this._dragOverItem && aEvent.target == aEvent.currentTarget) {
      this._cancelDragActive(this._dragOverItem);
      this._dragOverItem = null;
    }
  },

  /**
   * To workaround bug 460801 we manually forward the drop event here when dragend wouldn't be fired.
   */
  _onDragEnd(aEvent) {
    if (this._isUnwantedDragDrop(aEvent)) {
      return;
    }
    this._initializeDragAfterMove = null;
    this.window.clearTimeout(this._dragInitializeTimeout);
    __dumpDragData(aEvent, "_onDragEnd");

    let document = aEvent.target.ownerDocument;
    document.documentElement.removeAttribute("customizing-movingItem");

    let documentId = document.documentElement.id;
    if (!aEvent.dataTransfer.mozTypesAt(0)) {
      return;
    }

    let draggedItemId =
      aEvent.dataTransfer.mozGetDataAt(kDragDataTypePrefix + documentId, 0);

    let draggedWrapper = document.getElementById("wrapper-" + draggedItemId);

    // DraggedWrapper might no longer available if a widget node is
    // destroyed after starting (but before stopping) a drag.
    if (draggedWrapper) {
      draggedWrapper.hidden = false;
      draggedWrapper.removeAttribute("mousedown");

      let toolbarParent = draggedWrapper.closest("toolbar");
      if (toolbarParent) {
        toolbarParent.style.removeProperty("min-height");
      }
    }

    if (this._dragOverItem) {
      this._cancelDragActive(this._dragOverItem);
      this._dragOverItem = null;
    }
    DragPositionManager.stop();
  },

  _isUnwantedDragDrop(aEvent) {
    // The simulated events generated by synthesizeDragStart/synthesizeDrop in
    // mochitests are used only for testing whether the right data is being put
    // into the dataTransfer. Neither cause a real drop to occur, so they don't
    // set the source node. There isn't a means of testing real drag and drops,
    // so this pref skips the check but it should only be set by test code.
    if (this._skipSourceNodeCheck) {
      return false;
    }

    /* Discard drag events that originated from a separate window to
       prevent content->chrome privilege escalations. */
    let mozSourceNode = aEvent.dataTransfer.mozSourceNode;
    // mozSourceNode is null in the dragStart event handler or if
    // the drag event originated in an external application.
    return !mozSourceNode ||
           mozSourceNode.ownerGlobal != this.window;
  },

  _setDragActive(aItem, aValue, aDraggedItemId, aAreaType) {
    if (!aItem) {
      return;
    }

    if (aItem.getAttribute("dragover") != aValue) {
      aItem.setAttribute("dragover", aValue);

      let window = aItem.ownerGlobal;
      let draggedItem = window.document.getElementById(aDraggedItemId);
      if (aAreaType == "palette") {
        this._setGridDragActive(aItem, draggedItem, aValue);
      } else {
        let targetArea = this._getCustomizableParent(aItem);
        let makeSpaceImmediately = false;
        if (!gDraggingInToolbars.has(targetArea.id)) {
          gDraggingInToolbars.add(targetArea.id);
          let draggedWrapper = this.document.getElementById("wrapper-" + aDraggedItemId);
          let originArea = this._getCustomizableParent(draggedWrapper);
          makeSpaceImmediately = originArea == targetArea;
        }
        let propertyToMeasure = aAreaType == "toolbar" ? "width" : "height";
        // Calculate width/height of the item when it'd be dropped in this position.
        let borderWidth = this._getDragItemSize(aItem, draggedItem)[propertyToMeasure];
        let layoutSide = aAreaType == "toolbar" ? "Inline" : "Block";
        let prop, otherProp;
        if (aValue == "before") {
          prop = "border" + layoutSide + "StartWidth";
          otherProp = "border-" + layoutSide.toLowerCase() + "-end-width";
        } else {
          prop = "border" + layoutSide + "EndWidth";
          otherProp = "border-" + layoutSide.toLowerCase() + "-start-width";
        }
        if (makeSpaceImmediately) {
          aItem.setAttribute("notransition", "true");
        }
        aItem.style[prop] = borderWidth + "px";
        aItem.style.removeProperty(otherProp);
        if (makeSpaceImmediately) {
          // Force a layout flush:
          aItem.getBoundingClientRect();
          aItem.removeAttribute("notransition");
        }
      }
    }
  },
  _cancelDragActive(aItem, aNextItem, aNoTransition) {
    let currentArea = this._getCustomizableParent(aItem);
    if (!currentArea) {
      return;
    }
    let nextArea = aNextItem ? this._getCustomizableParent(aNextItem) : null;
    if (currentArea != nextArea) {
      currentArea.removeAttribute("draggingover");
    }
    let areaType = CustomizableUI.getAreaType(currentArea.id);
    if (areaType) {
      if (aNoTransition) {
        aItem.setAttribute("notransition", "true");
      }
      aItem.removeAttribute("dragover");
      // Remove all property values in the case that the end padding
      // had been set.
      aItem.style.removeProperty("border-inline-start-width");
      aItem.style.removeProperty("border-inline-end-width");
      aItem.style.removeProperty("border-block-start-width");
      aItem.style.removeProperty("border-block-end-width");
      if (aNoTransition) {
        // Force a layout flush:
        aItem.getBoundingClientRect();
        aItem.removeAttribute("notransition");
      }
    } else {
      aItem.removeAttribute("dragover");
      if (aNextItem) {
        if (nextArea == currentArea) {
          // No need to do anything if we're still dragging in this area:
          return;
        }
      }
      // Otherwise, clear everything out:
      let positionManager = DragPositionManager.getManagerForArea(currentArea);
      positionManager.clearPlaceholders(currentArea, aNoTransition);
    }
  },

  _setGridDragActive(aDragOverNode, aDraggedItem, aValue) {
    let targetArea = this._getCustomizableParent(aDragOverNode);
    let draggedWrapper = this.document.getElementById("wrapper-" + aDraggedItem.id);
    let originArea = this._getCustomizableParent(draggedWrapper);
    let positionManager = DragPositionManager.getManagerForArea(targetArea);
    let draggedSize = this._getDragItemSize(aDragOverNode, aDraggedItem);
    positionManager.insertPlaceholder(targetArea, aDragOverNode, draggedSize,
                                      originArea == targetArea);
  },

  _getDragItemSize(aDragOverNode, aDraggedItem) {
    // Cache it good, cache it real good.
    if (!this._dragSizeMap)
      this._dragSizeMap = new WeakMap();
    if (!this._dragSizeMap.has(aDraggedItem))
      this._dragSizeMap.set(aDraggedItem, new WeakMap());
    let itemMap = this._dragSizeMap.get(aDraggedItem);
    let targetArea = this._getCustomizableParent(aDragOverNode);
    let currentArea = this._getCustomizableParent(aDraggedItem);
    // Return the size for this target from cache, if it exists.
    let size = itemMap.get(targetArea);
    if (size)
      return size;

    // Calculate size of the item when it'd be dropped in this position.
    let currentParent = aDraggedItem.parentNode;
    let currentSibling = aDraggedItem.nextSibling;
    const kAreaType = "cui-areatype";
    let areaType, currentType;

    if (targetArea != currentArea) {
      // Move the widget temporarily next to the placeholder.
      aDragOverNode.parentNode.insertBefore(aDraggedItem, aDragOverNode);
      // Update the node's areaType.
      areaType = CustomizableUI.getAreaType(targetArea.id);
      currentType = aDraggedItem.hasAttribute(kAreaType) &&
                    aDraggedItem.getAttribute(kAreaType);
      if (areaType)
        aDraggedItem.setAttribute(kAreaType, areaType);
      this.wrapToolbarItem(aDraggedItem, areaType || "palette");
      CustomizableUI.onWidgetDrag(aDraggedItem.id, targetArea.id);
    } else {
      aDraggedItem.parentNode.hidden = false;
    }

    // Fetch the new size.
    let rect = aDraggedItem.parentNode.getBoundingClientRect();
    size = {width: rect.width, height: rect.height};
    // Cache the found value of size for this target.
    itemMap.set(targetArea, size);

    if (targetArea != currentArea) {
      this.unwrapToolbarItem(aDraggedItem.parentNode);
      // Put the item back into its previous position.
      currentParent.insertBefore(aDraggedItem, currentSibling);
      // restore the areaType
      if (areaType) {
        if (currentType === false)
          aDraggedItem.removeAttribute(kAreaType);
        else
          aDraggedItem.setAttribute(kAreaType, currentType);
      }
      this.createOrUpdateWrapper(aDraggedItem, null, true);
      CustomizableUI.onWidgetDrag(aDraggedItem.id);
    } else {
      aDraggedItem.parentNode.hidden = true;
    }
    return size;
  },

  _getCustomizableParent(aElement) {
    if (aElement) {
      // Deal with drag/drop on the padding of the panel.
      let containingPanelHolder = aElement.closest("#customization-panelHolder");
      if (containingPanelHolder) {
        return containingPanelHolder.querySelector("#widget-overflow-fixed-list");
      }
    }

    let areas = CustomizableUI.areas;
    areas.push(kPaletteId);
    return aElement.closest(areas.map(a => "#" + CSS.escape(a)).join(","));
  },

  _getDragOverNode(aEvent, aAreaElement, aAreaType, aDraggedItemId) {
    let expectedParent = aAreaElement.customizationTarget || aAreaElement;
    if (!expectedParent.contains(aEvent.target)) {
      return expectedParent;
    }
    // Offset the drag event's position with the offset to the center of
    // the thing we're dragging
    let dragX = aEvent.clientX - this._dragOffset.x;
    let dragY = aEvent.clientY - this._dragOffset.y;

    // Ensure this is within the container
    let boundsContainer = expectedParent;
    let bounds = this._dwu.getBoundsWithoutFlushing(boundsContainer);
    dragX = Math.min(bounds.right, Math.max(dragX, bounds.left));
    dragY = Math.min(bounds.bottom, Math.max(dragY, bounds.top));

    let targetNode;
    if (aAreaType == "toolbar" || aAreaType == "menu-panel") {
      targetNode = aAreaElement.ownerDocument.elementFromPoint(dragX, dragY);
      while (targetNode && targetNode.parentNode != expectedParent) {
        targetNode = targetNode.parentNode;
      }
    } else {
      let positionManager = DragPositionManager.getManagerForArea(aAreaElement);
      // Make it relative to the container:
      dragX -= bounds.left;
      dragY -= bounds.top;
      // Find the closest node:
      targetNode = positionManager.find(aAreaElement, dragX, dragY);
    }
    return targetNode || aEvent.target;
  },

  _onMouseDown(aEvent) {
    log.debug("_onMouseDown");
    if (aEvent.button != 0) {
      return;
    }
    let doc = aEvent.target.ownerDocument;
    doc.documentElement.setAttribute("customizing-movingItem", true);
    let item = this._getWrapper(aEvent.target);
    if (item) {
      item.setAttribute("mousedown", "true");
    }
  },

  _onMouseUp(aEvent) {
    log.debug("_onMouseUp");
    if (aEvent.button != 0) {
      return;
    }
    let doc = aEvent.target.ownerDocument;
    doc.documentElement.removeAttribute("customizing-movingItem");
    let item = this._getWrapper(aEvent.target);
    if (item) {
      item.removeAttribute("mousedown");
    }
  },

  _getWrapper(aElement) {
    while (aElement && aElement.localName != "toolbarpaletteitem") {
      if (aElement.localName == "toolbar")
        return null;
      aElement = aElement.parentNode;
    }
    return aElement;
  },

  _findVisiblePreviousSiblingNode(aReferenceNode) {
    while (aReferenceNode &&
           aReferenceNode.localName == "toolbarpaletteitem" &&
           aReferenceNode.firstChild.hidden) {
      aReferenceNode = aReferenceNode.previousSibling;
    }
    return aReferenceNode;
  },

  onPaletteContextMenuShowing(event) {
   let isFlexibleSpace = event.target.triggerNode.id.includes("wrapper-customizableui-special-spring");
   event.target.querySelector(".customize-context-addToPanel").disabled = isFlexibleSpace;
 },

  onPanelContextMenuShowing(event) {
    let inPermanentArea = !!event.target.triggerNode.closest("#widget-overflow-fixed-list");
    let doc = event.target.ownerDocument;
    doc.getElementById("customizationPanelItemContextMenuUnpin").hidden = !inPermanentArea;
    doc.getElementById("customizationPanelItemContextMenuPin").hidden = inPermanentArea;
  },

  _checkForDownloadsClick(event) {
    if (event.target.closest("#wrapper-downloads-button") && event.button == 0) {
      event.view.gCustomizeMode._showDownloadsAutoHidePanel();
    }
  },

  _setupDownloadAutoHideToggle() {
    this.document.getElementById(kDownloadAutohidePanelId).removeAttribute("hidden");
    this.window.addEventListener("click", this._checkForDownloadsClick, true);
  },

  _teardownDownloadAutoHideToggle() {
    this.window.removeEventListener("click", this._checkForDownloadsClick, true);
    this.document.getElementById(kDownloadAutohidePanelId).hidePopup();
  },

  _maybeMoveDownloadsButtonToNavBar() {
    // If the user toggled the autohide checkbox while the item was in the
    // palette, and hasn't moved it since, move the item to the default
    // location in the navbar for them.
    if (!CustomizableUI.getPlacementOfWidget("downloads-button") &&
        this._moveDownloadsButtonToNavBar &&
        this.window.DownloadsButton.autoHideDownloadsButton) {
      let navbarPlacements = CustomizableUI.getWidgetIdsInArea("nav-bar");
      let insertionPoint = navbarPlacements.indexOf("urlbar-container");
      while (++insertionPoint < navbarPlacements.length) {
        let widget = navbarPlacements[insertionPoint];
        // If we find a non-searchbar, non-spacer node, break out of the loop:
        if (widget != "search-container" &&
             !(CustomizableUI.isSpecialWidget(widget) && widget.includes("spring"))) {
          break;
        }
      }
      CustomizableUI.addWidgetToArea("downloads-button", "nav-bar", insertionPoint);
    }
  },

  async _showDownloadsAutoHidePanel() {
    let doc = this.document;
    let panel = doc.getElementById(kDownloadAutohidePanelId);
    panel.hidePopup();
    let button = doc.getElementById("downloads-button");
    // We don't show the tooltip if the button is in the panel.
    if (button.closest("#widget-overflow-fixed-list")) {
      return;
    }

    let offsetX = 0, offsetY = 0;
    let panelOnTheLeft = false;
    let toolbarContainer = button.closest("toolbar");
    if (toolbarContainer && toolbarContainer.id == "nav-bar") {
      let navbarWidgets = CustomizableUI.getWidgetIdsInArea("nav-bar");
      if (navbarWidgets.indexOf("urlbar-container") <= navbarWidgets.indexOf("downloads-button")) {
        panelOnTheLeft = true;
      }
    } else {
      await this.window.promiseDocumentFlushed(() => {});

      if (!this._customizing || !this._wantToBeInCustomizeMode) {
        return;
      }
      let buttonBounds = this._dwu.getBoundsWithoutFlushing(button);
      let windowBounds = this._dwu.getBoundsWithoutFlushing(doc.documentElement);
      panelOnTheLeft = (buttonBounds.left + buttonBounds.width / 2) > windowBounds.width / 2;
    }
    let position;
    if (panelOnTheLeft) {
      // Tested in RTL, these get inverted automatically, so this does the
      // right thing without taking RTL into account explicitly.
      position = "leftcenter topright";
      if (toolbarContainer) {
        offsetX = 8;
      }
    } else {
      position = "rightcenter topleft";
      if (toolbarContainer) {
        offsetX = -8;
      }
    }

    let checkbox = doc.getElementById(kDownloadAutohideCheckboxId);
    if (this.window.DownloadsButton.autoHideDownloadsButton) {
      checkbox.setAttribute("checked", "true");
    } else {
      checkbox.removeAttribute("checked");
    }

    // We don't use the icon to anchor because it might be resizing because of
    // the animations for drag/drop. Hence the use of offsets.
    panel.openPopup(button, position, offsetX, offsetY);
  },

  onDownloadsAutoHideChange(event) {
    let checkbox = event.target.ownerDocument.getElementById(kDownloadAutohideCheckboxId);
    Services.prefs.setBoolPref(kDownloadAutoHidePref, checkbox.checked);
    // Ensure we move the button (back) after the user leaves customize mode.
    event.view.gCustomizeMode._moveDownloadsButtonToNavBar = checkbox.checked;
  },

  togglePong(enabled) {
    // It's possible we're toggling for a reason other than hitting
    // the button (we might be exiting, for example), so make sure that
    // the state and checkbox are in sync.
    let whimsyButton = this.document.getElementById("whimsy-button");
    whimsyButton.checked = enabled;

    if (enabled) {
      this.visiblePalette.setAttribute("whimsypong", "true");
      this.pongArena.hidden = false;
      if (!this.uninitWhimsy) {
        this.uninitWhimsy = this.whimsypong();
      }
    } else {
      this.visiblePalette.removeAttribute("whimsypong");
      if (this.uninitWhimsy) {
        this.uninitWhimsy();
        this.uninitWhimsy = null;
      }
      this.pongArena.hidden = true;
    }
  },

  whimsypong() {
    function update() {
      updateBall();
      updatePlayers();
    }

    function updateBall() {
      if (ball[1] <= 0 || ball[1] >= gameSide) {
        if ((ball[1] <= 0 && (ball[0] < p1 || ball[0] > p1 + paddleWidth)) ||
            (ball[1] >= gameSide && (ball[0] < p2 || ball[0] > p2 + paddleWidth))) {
          updateScore(ball[1] <= 0 ? 0 : 1);
        } else {
          if ((ball[1] <= 0 && (ball[0] - p1 < paddleEdge || p1 + paddleWidth - ball[0] < paddleEdge)) ||
              (ball[1] >= gameSide && (ball[0] - p2 < paddleEdge || p2 + paddleWidth - ball[0] < paddleEdge))) {
            ballDxDy[0] *= Math.random() + 1.3;
            ballDxDy[0] = Math.max(Math.min(ballDxDy[0], 6), -6);
            if (Math.abs(ballDxDy[0]) == 6) {
              ballDxDy[0] += Math.sign(ballDxDy[0]) * Math.random();
            }
          } else {
            ballDxDy[0] /= 1.1;
          }
          ballDxDy[1] *= -1;
          ball[1] = ball[1] <= 0 ? 0 : gameSide;
        }
      }
      ball = [Math.max(Math.min(ball[0] + ballDxDy[0], gameSide), 0),
              Math.max(Math.min(ball[1] + ballDxDy[1], gameSide), 0)];
      if (ball[0] <= 0 || ball[0] >= gameSide) {
        ballDxDy[0] *= -1;
      }
    }

    function updatePlayers() {
      if (keydown) {
        let p1Adj = 1;
        if ((keydown == 37 && !isRTL) ||
            (keydown == 39 && isRTL)) {
          p1Adj = -1;
        }
        p1 += p1Adj * 10 * keydownAdj;
      }

      let sign = Math.sign(ballDxDy[0]);
      if ((sign > 0 && ball[0] > p2 + paddleWidth / 2) ||
          (sign < 0 && ball[0] < p2 + paddleWidth / 2)) {
        p2 += sign * 3;
      } else if ((sign > 0 && ball[0] > p2 + paddleWidth / 1.1) ||
                 (sign < 0 && ball[0] < p2 + paddleWidth / 1.1)) {
        p2 += sign * 9;
      }

      if (score >= winScore) {
        p1 = ball[0];
        p2 = ball[0];
      }
      p1 = Math.max(Math.min(p1, gameSide - paddleWidth), 0);
      p2 = Math.max(Math.min(p2, gameSide - paddleWidth), 0);
    }

    function updateScore(adj) {
      if (adj) {
        score += adj;
      } else if (--lives == 0) {
        quit = true;
      }
      ball = ballDef.slice();
      ballDxDy = ballDxDyDef.slice();
      ballDxDy[1] *= score / winScore + 1;
    }

    function draw() {
      let xAdj = isRTL ? -1 : 1;
      elements["wp-player1"].style.transform = "translate(" + (xAdj * p1) + "px, -37px)";
      elements["wp-player2"].style.transform = "translate(" + (xAdj * p2) + "px, " + gameSide + "px)";
      elements["wp-ball"].style.transform = "translate(" + (xAdj * ball[0]) + "px, " + ball[1] + "px)";
      elements["wp-score"].textContent = score;
      elements["wp-lives"].setAttribute("lives", lives);
      if (score >= winScore) {
        let arena = elements.arena;
        let image = "url(chrome://browser/skin/customizableui/whimsy.png)";
        let position = `${(isRTL ? gameSide : 0) + (xAdj * ball[0]) - 10}px ${ball[1] - 10}px`;
        let repeat = "no-repeat";
        let size = "20px";
        if (arena.style.backgroundImage) {
          if (arena.style.backgroundImage.split(",").length >= 160) {
            quit = true;
          }

          image += ", " + arena.style.backgroundImage;
          position += ", " + arena.style.backgroundPosition;
          repeat += ", " + arena.style.backgroundRepeat;
          size += ", " + arena.style.backgroundSize;
        }
        arena.style.backgroundImage = image;
        arena.style.backgroundPosition = position;
        arena.style.backgroundRepeat = repeat;
        arena.style.backgroundSize = size;
      }
    }

    function onkeydown(event) {
      keys.push(event.which);
      if (keys.length > 10) {
        keys.shift();
        let codeEntered = true;
        for (let i = 0; i < keys.length; i++) {
          if (keys[i] != keysCode[i]) {
            codeEntered = false;
            break;
          }
        }
        if (codeEntered) {
          elements.arena.setAttribute("kcode", "true");
          let spacer = document.querySelector("#customization-palette > toolbarpaletteitem");
          spacer.setAttribute("kcode", "true");
        }
      }
      if (event.which == 37 /* left */ ||
          event.which == 39 /* right */) {
        keydown = event.which;
        keydownAdj *= 1.05;
      }
    }

    function onkeyup(event) {
      if (event.which == 37 || event.which == 39) {
        keydownAdj = 1;
        keydown = 0;
      }
    }

    function uninit() {
      document.removeEventListener("keydown", onkeydown);
      document.removeEventListener("keyup", onkeyup);
      if (rAFHandle) {
        window.cancelAnimationFrame(rAFHandle);
      }
      let arena = elements.arena;
      while (arena.firstChild) {
        arena.firstChild.remove();
      }
      arena.removeAttribute("score");
      arena.removeAttribute("lives");
      arena.removeAttribute("kcode");
      arena.style.removeProperty("background-image");
      arena.style.removeProperty("background-position");
      arena.style.removeProperty("background-repeat");
      arena.style.removeProperty("background-size");
      let spacer = document.querySelector("#customization-palette > toolbarpaletteitem");
      spacer.removeAttribute("kcode");
      elements = null;
      document = null;
      quit = true;
    }

    if (this.uninitWhimsy) {
      return this.uninitWhimsy;
    }

    let ballDef = [10, 10];
    let ball = [10, 10];
    let ballDxDyDef = [2, 2];
    let ballDxDy = [2, 2];
    let score = 0;
    let p1 = 0;
    let p2 = 10;
    let gameSide = 300;
    let paddleEdge = 30;
    let paddleWidth = 84;
    let keydownAdj = 1;
    let keydown = 0;
    let keys = [];
    let keysCode = [38, 38, 40, 40, 37, 39, 37, 39, 66, 65];
    let lives = 5;
    let winScore = 11;
    let quit = false;
    let document = this.document;
    let rAFHandle = 0;
    let elements = {
      arena: document.getElementById("customization-pong-arena")
    };
    let isRTL = document.documentElement.matches(":-moz-locale-dir(rtl)");

    document.addEventListener("keydown", onkeydown);
    document.addEventListener("keyup", onkeyup);

    for (let id of ["player1", "player2", "ball", "score", "lives"]) {
      let el = document.createElement("box");
      el.id = "wp-" + id;
      elements[el.id] = elements.arena.appendChild(el);
    }

    let spacer = this.visiblePalette.querySelector("toolbarpaletteitem");
    for (let player of ["#wp-player1", "#wp-player2"]) {
      let val = "-moz-element(#" + spacer.id + ") no-repeat";
      elements.arena.querySelector(player).style.background = val;
    }

    let window = this.window;
    rAFHandle = window.requestAnimationFrame(function animate() {
      update();
      draw();
      if (quit) {
        elements["wp-score"].textContent = score;
        elements["wp-lives"] && elements["wp-lives"].setAttribute("lives", lives);
        elements.arena.setAttribute("score", score);
        elements.arena.setAttribute("lives", lives);
      } else {
        rAFHandle = window.requestAnimationFrame(animate);
      }
    });

    return uninit;
  },
};

function __dumpDragData(aEvent, caller) {
  if (!gDebug) {
    return;
  }
  let str = "Dumping drag data (" + (caller ? caller + " in " : "") + "CustomizeMode.jsm) {\n";
  str += "  type: " + aEvent.type + "\n";
  for (let el of ["target", "currentTarget", "relatedTarget"]) {
    if (aEvent[el]) {
      str += "  " + el + ": " + aEvent[el] + "(localName=" + aEvent[el].localName + "; id=" + aEvent[el].id + ")\n";
    }
  }
  for (let prop in aEvent.dataTransfer) {
    if (typeof aEvent.dataTransfer[prop] != "function") {
      str += "  dataTransfer[" + prop + "]: " + aEvent.dataTransfer[prop] + "\n";
    }
  }
  str += "}";
  log.debug(str);
}

function dispatchFunction(aFunc) {
  Services.tm.dispatchToMainThread(aFunc);
}
PK
!<ou}~�~�modules/DownloadsCommon.jsm/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = [
  "DownloadsCommon",
];

/**
 * Handles the Downloads panel shared methods and data access.
 *
 * This file includes the following constructors and global objects:
 *
 * DownloadsCommon
 * This object is exposed directly to the consumers of this JavaScript module,
 * and provides shared methods for all the instances of the user interface.
 *
 * DownloadsData
 * Retrieves the list of past and completed downloads from the underlying
 * Downloads API data, and provides asynchronous notifications allowing
 * to build a consistent view of the available data.
 *
 * DownloadsIndicatorData
 * This object registers itself with DownloadsData as a view, and transforms the
 * notifications it receives into overall status data, that is then broadcast to
 * the registered download status indicators.
 */

// Globals

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");

XPCOMUtils.defineLazyModuleGetters(this, {
  NetUtil: "resource://gre/modules/NetUtil.jsm",
  PluralForm: "resource://gre/modules/PluralForm.jsm",
  AppConstants: "resource://gre/modules/AppConstants.jsm",
  DownloadHistory: "resource://gre/modules/DownloadHistory.jsm",
  Downloads: "resource://gre/modules/Downloads.jsm",
  DownloadUIHelper: "resource://gre/modules/DownloadUIHelper.jsm",
  DownloadUtils: "resource://gre/modules/DownloadUtils.jsm",
  FileUtils: "resource://gre/modules/FileUtils.jsm",
  OS: "resource://gre/modules/osfile.jsm",
  PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
  RecentWindow: "resource:///modules/RecentWindow.jsm",
});

XPCOMUtils.defineLazyGetter(this, "DownloadsLogger", () => {
  let { ConsoleAPI } = ChromeUtils.import("resource://gre/modules/Console.jsm", {});
  let consoleOptions = {
    maxLogLevelPref: "browser.download.loglevel",
    prefix: "Downloads"
  };
  return new ConsoleAPI(consoleOptions);
});

const kDownloadsStringBundleUrl =
  "chrome://browser/locale/downloads/downloads.properties";

const kDownloadsStringsRequiringFormatting = {
  sizeWithUnits: true,
  statusSeparator: true,
  statusSeparatorBeforeNumber: true,
  fileExecutableSecurityWarning: true
};

const kDownloadsStringsRequiringPluralForm = {
  otherDownloads3: true
};

const kMaxHistoryResultsForLimitedView = 42;

const kPrefBranch = Services.prefs.getBranch("browser.download.");

var PrefObserver = {
  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                         Ci.nsISupportsWeakReference]),
  getPref(name) {
    try {
      switch (typeof this.prefs[name]) {
        case "boolean":
          return kPrefBranch.getBoolPref(name);
      }
    } catch (ex) { }
    return this.prefs[name];
  },
  observe(aSubject, aTopic, aData) {
    if (this.prefs.hasOwnProperty(aData)) {
      delete this[aData];
      this[aData] = this.getPref(aData);
    }
  },
  register(prefs) {
    this.prefs = prefs;
    kPrefBranch.addObserver("", this, true);
    for (let key in prefs) {
      let name = key;
      XPCOMUtils.defineLazyGetter(this, name, function() {
        return PrefObserver.getPref(name);
      });
    }
  },
};

PrefObserver.register({
  // prefName: defaultValue
  animateNotifications: true,
});


// DownloadsCommon

/**
 * This object is exposed directly to the consumers of this JavaScript module,
 * and provides shared methods for all the instances of the user interface.
 */
var DownloadsCommon = {
  // The following legacy constants are still returned by stateOfDownload, but
  // individual properties of the Download object should normally be used.
  DOWNLOAD_NOTSTARTED: -1,
  DOWNLOAD_DOWNLOADING: 0,
  DOWNLOAD_FINISHED: 1,
  DOWNLOAD_FAILED: 2,
  DOWNLOAD_CANCELED: 3,
  DOWNLOAD_PAUSED: 4,
  DOWNLOAD_BLOCKED_PARENTAL: 6,
  DOWNLOAD_DIRTY: 8,
  DOWNLOAD_BLOCKED_POLICY: 9,

  // The following are the possible values of the "attention" property.
  ATTENTION_NONE: "",
  ATTENTION_SUCCESS: "success",
  ATTENTION_WARNING: "warning",
  ATTENTION_SEVERE: "severe",

  /**
   * Returns an object whose keys are the string names from the downloads string
   * bundle, and whose values are either the translated strings or functions
   * returning formatted strings.
   */
  get strings() {
    let strings = {};
    let sb = Services.strings.createBundle(kDownloadsStringBundleUrl);
    let enumerator = sb.getSimpleEnumeration();
    while (enumerator.hasMoreElements()) {
      let string = enumerator.getNext().QueryInterface(Ci.nsIPropertyElement);
      let stringName = string.key;
      if (stringName in kDownloadsStringsRequiringFormatting) {
        strings[stringName] = function() {
          // Convert "arguments" to a real array before calling into XPCOM.
          return sb.formatStringFromName(stringName,
                                         Array.slice(arguments, 0),
                                         arguments.length);
        };
      } else if (stringName in kDownloadsStringsRequiringPluralForm) {
        strings[stringName] = function(aCount) {
          // Convert "arguments" to a real array before calling into XPCOM.
          let formattedString = sb.formatStringFromName(stringName,
                                         Array.slice(arguments, 0),
                                         arguments.length);
          return PluralForm.get(aCount, formattedString);
        };
      } else {
        strings[stringName] = string.value;
      }
    }
    delete this.strings;
    return this.strings = strings;
  },

  /**
   * Indicates whether we should show visual notification on the indicator
   * when a download event is triggered.
   */
  get animateNotifications() {
    return PrefObserver.animateNotifications;
  },

  /**
   * Get access to one of the DownloadsData, PrivateDownloadsData, or
   * HistoryDownloadsData objects, depending on the privacy status of the
   * specified window and on whether history downloads should be included.
   *
   * @param window
   *        The browser window which owns the download button.
   * @param [optional] history
   *        True to include history downloads when the window is public.
   * @param [optional] privateAll
   *        Whether to force the public downloads data to be returned together
   *        with the private downloads data for a private window.
   * @param [optional] limited
   *        True to limit the amount of downloads returned to
   *        `kMaxHistoryResultsForLimitedView`.
   */
  getData(window, history = false, privateAll = false, limited = false) {
    let isPrivate = PrivateBrowsingUtils.isContentWindowPrivate(window);
    if (isPrivate && !privateAll) {
      return PrivateDownloadsData;
    }
    if (history) {
      if (isPrivate && privateAll)
        return LimitedPrivateHistoryDownloadData;
      return limited ? LimitedHistoryDownloadsData : HistoryDownloadsData;
    }
    return DownloadsData;
  },

  /**
   * Initializes the Downloads back-end and starts receiving events for both the
   * private and non-private downloads data objects.
   */
  initializeAllDataLinks() {
    DownloadsData.initializeDataLink();
    PrivateDownloadsData.initializeDataLink();
  },

  /**
   * Get access to one of the DownloadsIndicatorData or
   * PrivateDownloadsIndicatorData objects, depending on the privacy status of
   * the window in question.
   */
  getIndicatorData(aWindow) {
    if (PrivateBrowsingUtils.isContentWindowPrivate(aWindow)) {
      return PrivateDownloadsIndicatorData;
    }
    return DownloadsIndicatorData;
  },

  /**
   * Returns a reference to the DownloadsSummaryData singleton - creating one
   * in the process if one hasn't been instantiated yet.
   *
   * @param aWindow
   *        The browser window which owns the download button.
   * @param aNumToExclude
   *        The number of items on the top of the downloads list to exclude
   *        from the summary.
   */
  getSummary(aWindow, aNumToExclude) {
    if (PrivateBrowsingUtils.isContentWindowPrivate(aWindow)) {
      if (this._privateSummary) {
        return this._privateSummary;
      }
      return this._privateSummary = new DownloadsSummaryData(true, aNumToExclude);
    }
    if (this._summary) {
      return this._summary;
    }
    return this._summary = new DownloadsSummaryData(false, aNumToExclude);
  },
  _summary: null,
  _privateSummary: null,

  /**
   * Returns the legacy state integer value for the provided Download object.
   */
  stateOfDownload(download) {
    // Collapse state using the correct priority.
    if (!download.stopped) {
      return DownloadsCommon.DOWNLOAD_DOWNLOADING;
    }
    if (download.succeeded) {
      return DownloadsCommon.DOWNLOAD_FINISHED;
    }
    if (download.error) {
      if (download.error.becauseBlockedByParentalControls) {
        return DownloadsCommon.DOWNLOAD_BLOCKED_PARENTAL;
      }
      if (download.error.becauseBlockedByReputationCheck) {
        return DownloadsCommon.DOWNLOAD_DIRTY;
      }
      return DownloadsCommon.DOWNLOAD_FAILED;
    }
    if (download.canceled) {
      if (download.hasPartialData) {
        return DownloadsCommon.DOWNLOAD_PAUSED;
      }
      return DownloadsCommon.DOWNLOAD_CANCELED;
    }
    return DownloadsCommon.DOWNLOAD_NOTSTARTED;
  },

  /**
   * Given an iterable collection of Download objects, generates and returns
   * statistics about that collection.
   *
   * @param downloads An iterable collection of Download objects.
   *
   * @return Object whose properties are the generated statistics. Currently,
   *         we return the following properties:
   *
   *         numActive       : The total number of downloads.
   *         numPaused       : The total number of paused downloads.
   *         numDownloading  : The total number of downloads being downloaded.
   *         totalSize       : The total size of all downloads once completed.
   *         totalTransferred: The total amount of transferred data for these
   *                           downloads.
   *         slowestSpeed    : The slowest download rate.
   *         rawTimeLeft     : The estimated time left for the downloads to
   *                           complete.
   *         percentComplete : The percentage of bytes successfully downloaded.
   */
  summarizeDownloads(downloads) {
    let summary = {
      numActive: 0,
      numPaused: 0,
      numDownloading: 0,
      totalSize: 0,
      totalTransferred: 0,
      // slowestSpeed is Infinity so that we can use Math.min to
      // find the slowest speed. We'll set this to 0 afterwards if
      // it's still at Infinity by the time we're done iterating all
      // download.
      slowestSpeed: Infinity,
      rawTimeLeft: -1,
      percentComplete: -1
    };

    for (let download of downloads) {
      summary.numActive++;

      if (!download.stopped) {
        summary.numDownloading++;
        if (download.hasProgress && download.speed > 0) {
          let sizeLeft = download.totalBytes - download.currentBytes;
          summary.rawTimeLeft = Math.max(summary.rawTimeLeft,
                                         sizeLeft / download.speed);
          summary.slowestSpeed = Math.min(summary.slowestSpeed,
                                          download.speed);
        }
      } else if (download.canceled && download.hasPartialData) {
        summary.numPaused++;
      }

      // Only add to total values if we actually know the download size.
      if (download.succeeded) {
        summary.totalSize += download.target.size;
        summary.totalTransferred += download.target.size;
      } else if (download.hasProgress) {
        summary.totalSize += download.totalBytes;
        summary.totalTransferred += download.currentBytes;
      }
    }

    if (summary.totalSize != 0) {
      summary.percentComplete = (summary.totalTransferred /
                                 summary.totalSize) * 100;
    }

    if (summary.slowestSpeed == Infinity) {
      summary.slowestSpeed = 0;
    }

    return summary;
  },

  /**
   * If necessary, smooths the estimated number of seconds remaining for one
   * or more downloads to complete.
   *
   * @param aSeconds
   *        Current raw estimate on number of seconds left for one or more
   *        downloads. This is a floating point value to help get sub-second
   *        accuracy for current and future estimates.
   */
  smoothSeconds(aSeconds, aLastSeconds) {
    // We apply an algorithm similar to the DownloadUtils.getTimeLeft function,
    // though tailored to a single time estimation for all downloads.  We never
    // apply something if the new value is less than half the previous value.
    let shouldApplySmoothing = aLastSeconds >= 0 &&
                               aSeconds > aLastSeconds / 2;
    if (shouldApplySmoothing) {
      // Apply hysteresis to favor downward over upward swings.  Trust only 30%
      // of the new value if lower, and 10% if higher (exponential smoothing).
      let diff = aSeconds - aLastSeconds;
      aSeconds = aLastSeconds + (diff < 0 ? .3 : .1) * diff;

      // If the new time is similar, reuse something close to the last time
      // left, but subtract a little to provide forward progress.
      diff = aSeconds - aLastSeconds;
      let diffPercent = diff / aLastSeconds * 100;
      if (Math.abs(diff) < 5 || Math.abs(diffPercent) < 5) {
        aSeconds = aLastSeconds - (diff < 0 ? .4 : .2);
      }
    }

    // In the last few seconds of downloading, we are always subtracting and
    // never adding to the time left.  Ensure that we never fall below one
    // second left until all downloads are actually finished.
    return aLastSeconds = Math.max(aSeconds, 1);
  },

  /**
   * Opens a downloaded file.
   *
   * @param aFile
   *        the downloaded file to be opened.
   * @param aMimeInfo
   *        the mime type info object.  May be null.
   * @param aOwnerWindow
   *        the window with which this action is associated.
   */
  openDownloadedFile(aFile, aMimeInfo, aOwnerWindow) {
    if (!(aFile instanceof Ci.nsIFile)) {
      throw new Error("aFile must be a nsIFile object");
    }
    if (aMimeInfo && !(aMimeInfo instanceof Ci.nsIMIMEInfo)) {
      throw new Error("Invalid value passed for aMimeInfo");
    }
    if (!(aOwnerWindow instanceof Ci.nsIDOMWindow)) {
      throw new Error("aOwnerWindow must be a dom-window object");
    }

    let isWindowsExe = AppConstants.platform == "win" &&
      aFile.leafName.toLowerCase().endsWith(".exe");

    let promiseShouldLaunch;
    // Don't prompt on Windows for .exe since there will be a native prompt.
    if (aFile.isExecutable() && !isWindowsExe) {
      // We get a prompter for the provided window here, even though anchoring
      // to the most recently active window should work as well.
      promiseShouldLaunch =
        DownloadUIHelper.getPrompter(aOwnerWindow)
                        .confirmLaunchExecutable(aFile.path);
    } else {
      promiseShouldLaunch = Promise.resolve(true);
    }

    promiseShouldLaunch.then(shouldLaunch => {
      if (!shouldLaunch) {
        return;
      }

      // Actually open the file.
      try {
        if (aMimeInfo && aMimeInfo.preferredAction == aMimeInfo.useHelperApp) {
          aMimeInfo.launchWithFile(aFile);
          return;
        }
      } catch (ex) { }

      // If either we don't have the mime info, or the preferred action failed,
      // attempt to launch the file directly.
      try {
        aFile.launch();
      } catch (ex) {
        // If launch fails, try sending it through the system's external "file:"
        // URL handler.
        Cc["@mozilla.org/uriloader/external-protocol-service;1"]
          .getService(Ci.nsIExternalProtocolService)
          .loadURI(NetUtil.newURI(aFile));
      }
    }).catch(Cu.reportError);
  },

  /**
   * Show a downloaded file in the system file manager.
   *
   * @param aFile
   *        a downloaded file.
   */
  showDownloadedFile(aFile) {
    if (!(aFile instanceof Ci.nsIFile)) {
      throw new Error("aFile must be a nsIFile object");
    }
    try {
      // Show the directory containing the file and select the file.
      aFile.reveal();
    } catch (ex) {
      // If reveal fails for some reason (e.g., it's not implemented on unix
      // or the file doesn't exist), try using the parent if we have it.
      let parent = aFile.parent;
      if (parent) {
        this.showDirectory(parent);
      }
    }
  },

  /**
   * Show the specified folder in the system file manager.
   *
   * @param aDirectory
   *        a directory to be opened with system file manager.
   */
  showDirectory(aDirectory) {
    if (!(aDirectory instanceof Ci.nsIFile)) {
      throw new Error("aDirectory must be a nsIFile object");
    }
    try {
      aDirectory.launch();
    } catch (ex) {
      // If launch fails (probably because it's not implemented), let
      // the OS handler try to open the directory.
      Cc["@mozilla.org/uriloader/external-protocol-service;1"]
        .getService(Ci.nsIExternalProtocolService)
        .loadURI(NetUtil.newURI(aDirectory));
    }
  },

  /**
   * Displays an alert message box which asks the user if they want to
   * unblock the downloaded file or not.
   *
   * @param options
   *        An object with the following properties:
   *        {
   *          verdict:
   *            The detailed reason why the download was blocked, according to
   *            the "Downloads.Error.BLOCK_VERDICT_" constants. If an unknown
   *            reason is specified, "Downloads.Error.BLOCK_VERDICT_MALWARE" is
   *            assumed.
   *          window:
   *            The window with which this action is associated.
   *          dialogType:
   *            String that determines which actions are available:
   *             - "unblock" to offer just "unblock".
   *             - "chooseUnblock" to offer "unblock" and "confirmBlock".
   *             - "chooseOpen" to offer "open" and "confirmBlock".
   *        }
   *
   * @return {Promise}
   * @resolves String representing the action that should be executed:
   *            - "open" to allow the download and open the file.
   *            - "unblock" to allow the download without opening the file.
   *            - "confirmBlock" to delete the blocked data permanently.
   *            - "cancel" to do nothing and cancel the operation.
   */
  async confirmUnblockDownload({ verdict, window,
                                                  dialogType }) {
    let s = DownloadsCommon.strings;

    // All the dialogs have an action button and a cancel button, while only
    // some of them have an additonal button to remove the file. The cancel
    // button must always be the one at BUTTON_POS_1 because this is the value
    // returned by confirmEx when using ESC or closing the dialog (bug 345067).
    let title = s.unblockHeaderUnblock;
    let firstButtonText = s.unblockButtonUnblock;
    let firstButtonAction = "unblock";
    let buttonFlags =
        (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_0) +
        (Ci.nsIPrompt.BUTTON_TITLE_CANCEL * Ci.nsIPrompt.BUTTON_POS_1);

    switch (dialogType) {
      case "unblock":
        // Use only the unblock action. The default is to cancel.
        buttonFlags += Ci.nsIPrompt.BUTTON_POS_1_DEFAULT;
        break;
      case "chooseUnblock":
        // Use the unblock and remove file actions. The default is remove file.
        buttonFlags +=
          (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_2) +
          Ci.nsIPrompt.BUTTON_POS_2_DEFAULT;
        break;
      case "chooseOpen":
        // Use the unblock and open file actions. The default is open file.
        title = s.unblockHeaderOpen;
        firstButtonText = s.unblockButtonOpen;
        firstButtonAction = "open";
        buttonFlags +=
          (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_2) +
          Ci.nsIPrompt.BUTTON_POS_0_DEFAULT;
        break;
      default:
        Cu.reportError("Unexpected dialog type: " + dialogType);
        return "cancel";
    }

    let message;
    switch (verdict) {
      case Downloads.Error.BLOCK_VERDICT_UNCOMMON:
        message = s.unblockTypeUncommon2;
        break;
      case Downloads.Error.BLOCK_VERDICT_POTENTIALLY_UNWANTED:
        message = s.unblockTypePotentiallyUnwanted2;
        break;
      default: // Assume Downloads.Error.BLOCK_VERDICT_MALWARE
        message = s.unblockTypeMalware;
        break;
    }
    message += "\n\n" + s.unblockTip2;

    Services.ww.registerNotification(function onOpen(subj, topic) {
      if (topic == "domwindowopened" && subj instanceof Ci.nsIDOMWindow) {
        // Make sure to listen for "DOMContentLoaded" because it is fired
        // before the "load" event.
        subj.addEventListener("DOMContentLoaded", function() {
          if (subj.document.documentURI ==
              "chrome://global/content/commonDialog.xul") {
            Services.ww.unregisterNotification(onOpen);
            let dialog = subj.document.getElementById("commonDialog");
            if (dialog) {
              // Change the dialog to use a warning icon.
              dialog.classList.add("alert-dialog");
            }
          }
        }, {once: true});
      }
    });

    let rv = Services.prompt.confirmEx(window, title, message, buttonFlags,
                                       firstButtonText, null,
                                       s.unblockButtonConfirmBlock, null, {});
    return [firstButtonAction, "cancel", "confirmBlock"][rv];
  },
};

XPCOMUtils.defineLazyGetter(this.DownloadsCommon, "log", () => {
  return DownloadsLogger.log.bind(DownloadsLogger);
});
XPCOMUtils.defineLazyGetter(this.DownloadsCommon, "error", () => {
  return DownloadsLogger.error.bind(DownloadsLogger);
});

/**
 * Returns true if we are executing on Windows Vista or a later version.
 */
XPCOMUtils.defineLazyGetter(DownloadsCommon, "isWinVistaOrHigher", function() {
  let os = Services.appinfo.OS;
  if (os != "WINNT") {
    return false;
  }
  return parseFloat(Services.sysinfo.getProperty("version")) >= 6;
});

// DownloadsData

/**
 * Retrieves the list of past and completed downloads from the underlying
 * Downloads API data, and provides asynchronous notifications allowing to
 * build a consistent view of the available data.
 *
 * Note that using this object does not automatically initialize the list of
 * downloads. This is useful to display a neutral progress indicator in
 * the main browser window until the autostart timeout elapses.
 *
 * This powers the DownloadsData, PrivateDownloadsData, and HistoryDownloadsData
 * singleton objects.
 */
function DownloadsDataCtor({ isPrivate, isHistory, maxHistoryResults } = {}) {
  this._isPrivate = !!isPrivate;

  // Contains all the available Download objects and their integer state.
  this.oldDownloadStates = new Map();

  // For the history downloads list we don't need to register this as a view,
  // but we have to ensure that the DownloadsData object is initialized before
  // we register more views. This ensures that the view methods of DownloadsData
  // are invoked before those of views registered on HistoryDownloadsData,
  // allowing the endTime property to be set correctly.
  if (isHistory) {
    if (isPrivate) {
      PrivateDownloadsData.initializeDataLink();
    }
    DownloadsData.initializeDataLink();
    this._promiseList = DownloadsData._promiseList.then(() => {
      // For history downloads in Private Browsing mode, we'll fetch the combined
      // list of public and private downloads.
      return DownloadHistory.getList({
        type: isPrivate ? Downloads.ALL : Downloads.PUBLIC,
        maxHistoryResults
      });
    });
    return;
  }

  // This defines "initializeDataLink" and "_promiseList" synchronously, then
  // continues execution only when "initializeDataLink" is called, allowing the
  // underlying data to be loaded only when actually needed.
  this._promiseList = (async () => {
    await new Promise(resolve => this.initializeDataLink = resolve);
    let list = await Downloads.getList(isPrivate ? Downloads.PRIVATE
                                                 : Downloads.PUBLIC);
    await list.addView(this);
    return list;
  })();
}

DownloadsDataCtor.prototype = {
  /**
   * Starts receiving events for current downloads.
   */
  initializeDataLink() {},

  /**
   * Promise resolved with the underlying DownloadList object once we started
   * receiving events for current downloads.
   */
  _promiseList: null,

  /**
   * Iterator for all the available Download objects. This is empty until the
   * data has been loaded using the JavaScript API for downloads.
   */
  get downloads() {
    return this.oldDownloadStates.keys();
  },

  /**
   * True if there are finished downloads that can be removed from the list.
   */
  get canRemoveFinished() {
    for (let download of this.downloads) {
      // Stopped, paused, and failed downloads with partial data are removed.
      if (download.stopped && !(download.canceled && download.hasPartialData)) {
        return true;
      }
    }
    return false;
  },

  /**
   * Asks the back-end to remove finished downloads from the list. This method
   * is only called after the data link has been initialized.
   */
  removeFinished() {
    Downloads.getList(this._isPrivate ? Downloads.PRIVATE : Downloads.PUBLIC)
             .then(list => list.removeFinished())
             .catch(Cu.reportError);
    let indicatorData = this._isPrivate ? PrivateDownloadsIndicatorData
                                        : DownloadsIndicatorData;
    indicatorData.attention = DownloadsCommon.ATTENTION_NONE;
  },

  // Integration with the asynchronous Downloads back-end

  onDownloadAdded(download) {
    // Download objects do not store the end time of downloads, as the Downloads
    // API does not need to persist this information for all platforms. Once a
    // download terminates on a Desktop browser, it becomes a history download,
    // for which the end time is stored differently, as a Places annotation.
    download.endTime = Date.now();

    this.oldDownloadStates.set(download,
                               DownloadsCommon.stateOfDownload(download));
  },

  onDownloadChanged(download) {
    let oldState = this.oldDownloadStates.get(download);
    let newState = DownloadsCommon.stateOfDownload(download);
    this.oldDownloadStates.set(download, newState);

    if (oldState != newState) {
      if (download.succeeded ||
          (download.canceled && !download.hasPartialData) ||
          download.error) {
        // Store the end time that may be displayed by the views.
        download.endTime = Date.now();

        // This state transition code should actually be located in a Downloads
        // API module (bug 941009).
        DownloadHistory.updateMetaData(download);
      }

      if (download.succeeded ||
          (download.error && download.error.becauseBlocked)) {
        this._notifyDownloadEvent("finish");
      }
    }

    if (!download.newDownloadNotified) {
      download.newDownloadNotified = true;
      this._notifyDownloadEvent("start");
    }
  },

  onDownloadRemoved(download) {
    this.oldDownloadStates.delete(download);
  },

  // Registration of views

  /**
   * Adds an object to be notified when the available download data changes.
   * The specified object is initialized with the currently available downloads.
   *
   * @param aView
   *        DownloadsView object to be added.  This reference must be passed to
   *        removeView before termination.
   */
  addView(aView) {
    this._promiseList.then(list => list.addView(aView))
                     .catch(Cu.reportError);
  },

  /**
   * Removes an object previously added using addView.
   *
   * @param aView
   *        DownloadsView object to be removed.
   */
  removeView(aView) {
    this._promiseList.then(list => list.removeView(aView))
                     .catch(Cu.reportError);
  },

  // Notifications sent to the most recent browser window only

  /**
   * Set to true after the first download causes the downloads panel to be
   * displayed.
   */
  get panelHasShownBefore() {
    try {
      return Services.prefs.getBoolPref("browser.download.panel.shown");
    } catch (ex) { }
    return false;
  },

  set panelHasShownBefore(aValue) {
    Services.prefs.setBoolPref("browser.download.panel.shown", aValue);
    return aValue;
  },

  /**
   * Displays a new or finished download notification in the most recent browser
   * window, if one is currently available with the required privacy type.
   *
   * @param aType
   *        Set to "start" for new downloads, "finish" for completed downloads.
   */
  _notifyDownloadEvent(aType) {
    DownloadsCommon.log("Attempting to notify that a new download has started or finished.");

    // Show the panel in the most recent browser window, if present.
    let browserWin = RecentWindow.getMostRecentBrowserWindow({ private: this._isPrivate });
    if (!browserWin) {
      return;
    }

    if (this.panelHasShownBefore) {
      // For new downloads after the first one, don't show the panel
      // automatically, but provide a visible notification in the topmost
      // browser window, if the status indicator is already visible.
      DownloadsCommon.log("Showing new download notification.");
      browserWin.DownloadsIndicatorView.showEventNotification(aType);
      return;
    }
    this.panelHasShownBefore = true;
    browserWin.DownloadsPanel.showPanel();
  }
};

XPCOMUtils.defineLazyGetter(this, "HistoryDownloadsData", function() {
  return new DownloadsDataCtor({ isHistory: true });
});

XPCOMUtils.defineLazyGetter(this, "LimitedHistoryDownloadsData", function() {
  return new DownloadsDataCtor({ isHistory: true, maxHistoryResults: kMaxHistoryResultsForLimitedView });
});

XPCOMUtils.defineLazyGetter(this, "LimitedPrivateHistoryDownloadData", function() {
  return new DownloadsDataCtor({ isPrivate: true, isHistory: true,
    maxHistoryResults: kMaxHistoryResultsForLimitedView });
});

XPCOMUtils.defineLazyGetter(this, "PrivateDownloadsData", function() {
  return new DownloadsDataCtor({ isPrivate: true });
});

XPCOMUtils.defineLazyGetter(this, "DownloadsData", function() {
  return new DownloadsDataCtor();
});

// DownloadsViewPrototype

/**
 * A prototype for an object that registers itself with DownloadsData as soon
 * as a view is registered with it.
 */
const DownloadsViewPrototype = {
  /**
   * Contains all the available Download objects and their current state value.
   *
   * SUBCLASSES MUST OVERRIDE THIS PROPERTY.
   */
  _oldDownloadStates: null,

  // Registration of views

  /**
   * Array of view objects that should be notified when the available status
   * data changes.
   *
   * SUBCLASSES MUST OVERRIDE THIS PROPERTY.
   */
  _views: null,

  /**
   * Determines whether this view object is over the private or non-private
   * downloads.
   *
   * SUBCLASSES MUST OVERRIDE THIS PROPERTY.
   */
  _isPrivate: false,

  /**
   * Adds an object to be notified when the available status data changes.
   * The specified object is initialized with the currently available status.
   *
   * @param aView
   *        View object to be added.  This reference must be
   *        passed to removeView before termination.
   */
  addView(aView) {
    // Start receiving events when the first of our views is registered.
    if (this._views.length == 0) {
      if (this._isPrivate) {
        PrivateDownloadsData.addView(this);
      } else {
        DownloadsData.addView(this);
      }
    }

    this._views.push(aView);
    this.refreshView(aView);
  },

  /**
   * Updates the properties of an object previously added using addView.
   *
   * @param aView
   *        View object to be updated.
   */
  refreshView(aView) {
    // Update immediately even if we are still loading data asynchronously.
    // Subclasses must provide these two functions!
    this._refreshProperties();
    this._updateView(aView);
  },

  /**
   * Removes an object previously added using addView.
   *
   * @param aView
   *        View object to be removed.
   */
  removeView(aView) {
    let index = this._views.indexOf(aView);
    if (index != -1) {
      this._views.splice(index, 1);
    }

    // Stop receiving events when the last of our views is unregistered.
    if (this._views.length == 0) {
      if (this._isPrivate) {
        PrivateDownloadsData.removeView(this);
      } else {
        DownloadsData.removeView(this);
      }
    }
  },

  // Callback functions from DownloadList

  /**
   * Indicates whether we are still loading downloads data asynchronously.
   */
  _loading: false,

  /**
   * Called before multiple downloads are about to be loaded.
   */
  onDownloadBatchStarting() {
    this._loading = true;
  },

  /**
   * Called after data loading finished.
   */
  onDownloadBatchEnded() {
    this._loading = false;
    this._updateViews();
  },

  /**
   * Called when a new download data item is available, either during the
   * asynchronous data load or when a new download is started.
   *
   * @param download
   *        Download object that was just added.
   *
   * @note Subclasses should override this and still call the base method.
   */
  onDownloadAdded(download) {
    this._oldDownloadStates.set(download,
                                DownloadsCommon.stateOfDownload(download));
  },

  /**
   * Called when the overall state of a Download has changed. In particular,
   * this is called only once when the download succeeds or is blocked
   * permanently, and is never called if only the current progress changed.
   *
   * The onDownloadChanged notification will always be sent afterwards.
   *
   * @note Subclasses should override this.
   */
  onDownloadStateChanged(download) {
    throw Cr.NS_ERROR_NOT_IMPLEMENTED;
  },

  /**
   * Called every time any state property of a Download may have changed,
   * including progress properties.
   *
   * Note that progress notification changes are throttled at the Downloads.jsm
   * API level, and there is no throttling mechanism in the front-end.
   *
   * @note Subclasses should override this and still call the base method.
   */
  onDownloadChanged(download) {
    let oldState = this._oldDownloadStates.get(download);
    let newState = DownloadsCommon.stateOfDownload(download);
    this._oldDownloadStates.set(download, newState);

    if (oldState != newState) {
      this.onDownloadStateChanged(download);
    }
  },

  /**
   * Called when a data item is removed, ensures that the widget associated with
   * the view item is removed from the user interface.
   *
   * @param download
   *        Download object that is being removed.
   *
   * @note Subclasses should override this.
   */
  onDownloadRemoved(download) {
    throw Cr.NS_ERROR_NOT_IMPLEMENTED;
  },

  /**
   * Private function used to refresh the internal properties being sent to
   * each registered view.
   *
   * @note Subclasses should override this.
   */
  _refreshProperties() {
    throw Cr.NS_ERROR_NOT_IMPLEMENTED;
  },

  /**
   * Private function used to refresh an individual view.
   *
   * @note Subclasses should override this.
   */
  _updateView() {
    throw Cr.NS_ERROR_NOT_IMPLEMENTED;
  },

  /**
   * Computes aggregate values and propagates the changes to our views.
   */
  _updateViews() {
    // Do not update the status indicators during batch loads of download items.
    if (this._loading) {
      return;
    }

    this._refreshProperties();
    this._views.forEach(this._updateView, this);
  },
};

// DownloadsIndicatorData

/**
 * This object registers itself with DownloadsData as a view, and transforms the
 * notifications it receives into overall status data, that is then broadcast to
 * the registered download status indicators.
 *
 * Note that using this object does not automatically start the Download Manager
 * service.  Consumers will see an empty list of downloads until the service is
 * actually started.  This is useful to display a neutral progress indicator in
 * the main browser window until the autostart timeout elapses.
 */
function DownloadsIndicatorDataCtor(aPrivate) {
  this._oldDownloadStates = new WeakMap();
  this._isPrivate = aPrivate;
  this._views = [];
}
DownloadsIndicatorDataCtor.prototype = {
  __proto__: DownloadsViewPrototype,

  /**
   * Removes an object previously added using addView.
   *
   * @param aView
   *        DownloadsIndicatorView object to be removed.
   */
  removeView(aView) {
    DownloadsViewPrototype.removeView.call(this, aView);

    if (this._views.length == 0) {
      this._itemCount = 0;
    }
  },

  onDownloadAdded(download) {
    DownloadsViewPrototype.onDownloadAdded.call(this, download);
    this._itemCount++;
    this._updateViews();
  },

  onDownloadStateChanged(download) {
    if (!download.succeeded && download.error && download.error.reputationCheckVerdict) {
      switch (download.error.reputationCheckVerdict) {
        case Downloads.Error.BLOCK_VERDICT_UNCOMMON: // fall-through
        case Downloads.Error.BLOCK_VERDICT_POTENTIALLY_UNWANTED:
          // Existing higher level attention indication trumps ATTENTION_WARNING.
          if (this._attention != DownloadsCommon.ATTENTION_SEVERE) {
            this.attention = DownloadsCommon.ATTENTION_WARNING;
          }
          break;
        case Downloads.Error.BLOCK_VERDICT_MALWARE:
          this.attention = DownloadsCommon.ATTENTION_SEVERE;
          break;
        default:
          this.attention = DownloadsCommon.ATTENTION_SEVERE;
          Cu.reportError("Unknown reputation verdict: " +
                         download.error.reputationCheckVerdict);
      }
    } else if (download.succeeded) {
      // Existing higher level attention indication trumps ATTENTION_SUCCESS.
      if (this._attention != DownloadsCommon.ATTENTION_SEVERE &&
          this._attention != DownloadsCommon.ATTENTION_WARNING) {
        this.attention = DownloadsCommon.ATTENTION_SUCCESS;
      }
    } else if (download.error) {
      // Existing higher level attention indication trumps ATTENTION_WARNING.
      if (this._attention != DownloadsCommon.ATTENTION_SEVERE) {
        this.attention = DownloadsCommon.ATTENTION_WARNING;
      }
    }
  },

  onDownloadChanged(download) {
    DownloadsViewPrototype.onDownloadChanged.call(this, download);
    this._updateViews();
  },

  onDownloadRemoved(download) {
    this._itemCount--;
    this._updateViews();
  },

  // Propagation of properties to our views

  // The following properties are updated by _refreshProperties and are then
  // propagated to the views.  See _refreshProperties for details.
  _hasDownloads: false,
  _percentComplete: -1,

  /**
   * Indicates whether the download indicators should be highlighted.
   */
  set attention(aValue) {
    this._attention = aValue;
    this._updateViews();
    return aValue;
  },
  _attention: DownloadsCommon.ATTENTION_NONE,

  /**
   * Indicates whether the user is interacting with downloads, thus the
   * attention indication should not be shown even if requested.
   */
  set attentionSuppressed(aValue) {
    this._attentionSuppressed = aValue;
    this._attention = DownloadsCommon.ATTENTION_NONE;
    this._updateViews();
    return aValue;
  },
  _attentionSuppressed: false,

  /**
   * Updates the specified view with the current aggregate values.
   *
   * @param aView
   *        DownloadsIndicatorView object to be updated.
   */
  _updateView(aView) {
    aView.hasDownloads = this._hasDownloads;
    aView.percentComplete = this._percentComplete;
    aView.attention = this._attentionSuppressed ? DownloadsCommon.ATTENTION_NONE
                                                : this._attention;
  },

  // Property updating based on current download status

  /**
   * Number of download items that are available to be displayed.
   */
  _itemCount: 0,

  /**
   * A generator function for the Download objects this summary is currently
   * interested in. This generator is passed off to summarizeDownloads in order
   * to generate statistics about the downloads we care about - in this case,
   * it's all active downloads.
   */
  * _activeDownloads() {
    let downloads = this._isPrivate ? PrivateDownloadsData.downloads
                                    : DownloadsData.downloads;
    for (let download of downloads) {
      if (!download.stopped || (download.canceled && download.hasPartialData)) {
        yield download;
      }
    }
  },

  /**
   * Computes aggregate values based on the current state of downloads.
   */
  _refreshProperties() {
    let summary =
      DownloadsCommon.summarizeDownloads(this._activeDownloads());

    // Determine if the indicator should be shown or get attention.
    this._hasDownloads = (this._itemCount > 0);

    // Always show a progress bar if there are downloads in progress.
    if (summary.percentComplete >= 0) {
      this._percentComplete = summary.percentComplete;
    } else if (summary.numDownloading > 0) {
      this._percentComplete = 0;
    } else {
      this._percentComplete = -1;
    }
  }
};

XPCOMUtils.defineLazyGetter(this, "PrivateDownloadsIndicatorData", function() {
  return new DownloadsIndicatorDataCtor(true);
});

XPCOMUtils.defineLazyGetter(this, "DownloadsIndicatorData", function() {
  return new DownloadsIndicatorDataCtor(false);
});

// DownloadsSummaryData

/**
 * DownloadsSummaryData is a view for DownloadsData that produces a summary
 * of all downloads after a certain exclusion point aNumToExclude. For example,
 * if there were 5 downloads in progress, and a DownloadsSummaryData was
 * constructed with aNumToExclude equal to 3, then that DownloadsSummaryData
 * would produce a summary of the last 2 downloads.
 *
 * @param aIsPrivate
 *        True if the browser window which owns the download button is a private
 *        window.
 * @param aNumToExclude
 *        The number of items to exclude from the summary, starting from the
 *        top of the list.
 */
function DownloadsSummaryData(aIsPrivate, aNumToExclude) {
  this._numToExclude = aNumToExclude;
  // Since we can have multiple instances of DownloadsSummaryData, we
  // override these values from the prototype so that each instance can be
  // completely separated from one another.
  this._loading = false;

  this._downloads = [];

  // Floating point value indicating the last number of seconds estimated until
  // the longest download will finish.  We need to store this value so that we
  // don't continuously apply smoothing if the actual download state has not
  // changed.  This is set to -1 if the previous value is unknown.
  this._lastRawTimeLeft = -1;

  // Last number of seconds estimated until all in-progress downloads with a
  // known size and speed will finish.  This value is stored to allow smoothing
  // in case of small variations.  This is set to -1 if the previous value is
  // unknown.
  this._lastTimeLeft = -1;

  // The following properties are updated by _refreshProperties and are then
  // propagated to the views.
  this._showingProgress = false;
  this._details = "";
  this._description = "";
  this._numActive = 0;
  this._percentComplete = -1;

  this._oldDownloadStates = new WeakMap();
  this._isPrivate = aIsPrivate;
  this._views = [];
}

DownloadsSummaryData.prototype = {
  __proto__: DownloadsViewPrototype,

  /**
   * Removes an object previously added using addView.
   *
   * @param aView
   *        DownloadsSummary view to be removed.
   */
  removeView(aView) {
    DownloadsViewPrototype.removeView.call(this, aView);

    if (this._views.length == 0) {
      // Clear out our collection of Download objects. If we ever have
      // another view registered with us, this will get re-populated.
      this._downloads = [];
    }
  },

  onDownloadAdded(download) {
    DownloadsViewPrototype.onDownloadAdded.call(this, download);
    this._downloads.unshift(download);
    this._updateViews();
  },

  onDownloadStateChanged() {
    // Since the state of a download changed, reset the estimated time left.
    this._lastRawTimeLeft = -1;
    this._lastTimeLeft = -1;
  },

  onDownloadChanged(download) {
    DownloadsViewPrototype.onDownloadChanged.call(this, download);
    this._updateViews();
  },

  onDownloadRemoved(download) {
    let itemIndex = this._downloads.indexOf(download);
    this._downloads.splice(itemIndex, 1);
    this._updateViews();
  },

  // Propagation of properties to our views

  /**
   * Updates the specified view with the current aggregate values.
   *
   * @param aView
   *        DownloadsIndicatorView object to be updated.
   */
  _updateView(aView) {
    aView.showingProgress = this._showingProgress;
    aView.percentComplete = this._percentComplete;
    aView.description = this._description;
    aView.details = this._details;
  },

  // Property updating based on current download status

  /**
   * A generator function for the Download objects this summary is currently
   * interested in. This generator is passed off to summarizeDownloads in order
   * to generate statistics about the downloads we care about - in this case,
   * it's the downloads in this._downloads after the first few to exclude,
   * which was set when constructing this DownloadsSummaryData instance.
   */
  * _downloadsForSummary() {
    if (this._downloads.length > 0) {
      for (let i = this._numToExclude; i < this._downloads.length; ++i) {
        yield this._downloads[i];
      }
    }
  },

  /**
   * Computes aggregate values based on the current state of downloads.
   */
  _refreshProperties() {
    // Pre-load summary with default values.
    let summary =
      DownloadsCommon.summarizeDownloads(this._downloadsForSummary());

    this._description = DownloadsCommon.strings
                                       .otherDownloads3(summary.numDownloading);
    this._percentComplete = summary.percentComplete;

    // Only show the downloading items.
    this._showingProgress = summary.numDownloading > 0;

    // Display the estimated time left, if present.
    if (summary.rawTimeLeft == -1) {
      // There are no downloads with a known time left.
      this._lastRawTimeLeft = -1;
      this._lastTimeLeft = -1;
      this._details = "";
    } else {
      // Compute the new time left only if state actually changed.
      if (this._lastRawTimeLeft != summary.rawTimeLeft) {
        this._lastRawTimeLeft = summary.rawTimeLeft;
        this._lastTimeLeft = DownloadsCommon.smoothSeconds(summary.rawTimeLeft,
                                                           this._lastTimeLeft);
      }
      [this._details] = DownloadUtils.getDownloadStatusNoRate(
        summary.totalTransferred, summary.totalSize, summary.slowestSpeed,
        this._lastTimeLeft);
    }
  },
};
PK
!<f�ثp=p=modules/DownloadsSubview.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = [
  "DownloadsSubview",
];

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");

ChromeUtils.defineModuleGetter(this, "AppConstants",
                               "resource://gre/modules/AppConstants.jsm");
ChromeUtils.defineModuleGetter(this, "Downloads",
                               "resource://gre/modules/Downloads.jsm");
ChromeUtils.defineModuleGetter(this, "DownloadsCommon",
                               "resource:///modules/DownloadsCommon.jsm");
ChromeUtils.defineModuleGetter(this, "DownloadsViewUI",
                               "resource:///modules/DownloadsViewUI.jsm");
ChromeUtils.defineModuleGetter(this, "FileUtils",
                               "resource://gre/modules/FileUtils.jsm");

let gPanelViewInstances = new WeakMap();
const kRefreshBatchSize = 10;
const kMaxWaitForIdleMs = 200;
XPCOMUtils.defineLazyGetter(this, "kButtonLabels", () => {
  return {
    show: DownloadsCommon.strings[AppConstants.platform == "macosx" ? "showMacLabel" : "showLabel"],
    open: DownloadsCommon.strings.openFileLabel,
    retry: DownloadsCommon.strings.retryLabel,
  };
});

class DownloadsSubview extends DownloadsViewUI.BaseView {
  constructor(panelview) {
    super();
    this.document = panelview.ownerDocument;
    this.window = panelview.ownerGlobal;

    this.context = "panelDownloadsContextMenu";

    this.panelview = panelview;
    this.container = this.document.getElementById("panelMenu_downloadsMenu");
    while (this.container.lastChild) {
      this.container.lastChild.remove();
    }
    this.panelview.addEventListener("click", DownloadsSubview.onClick);
    this.panelview.addEventListener("ViewHiding", DownloadsSubview.onViewHiding);

    this._viewItemsForDownloads = new WeakMap();

    let contextMenu = this.document.getElementById(this.context);
    if (!contextMenu) {
      contextMenu = this.document.getElementById("downloadsContextMenu").cloneNode(true);
      contextMenu.setAttribute("closemenu", "none");
      contextMenu.setAttribute("id", this.context);
      contextMenu.removeAttribute("onpopupshown");
      contextMenu.setAttribute("onpopupshowing",
        "DownloadsSubview.updateContextMenu(document.popupNode, this);");
      contextMenu.setAttribute("onpopuphidden", "DownloadsSubview.onContextMenuHidden(this);");
      let clearButton = contextMenu.querySelector("menuitem[command='downloadsCmd_clearDownloads']");
      clearButton.hidden = false;
      clearButton.previousSibling.hidden = true;
      contextMenu.querySelector("menuitem[command='cmd_delete']")
        .setAttribute("command", "downloadsCmd_delete");
    }
    this.panelview.appendChild(contextMenu);
    this.container.setAttribute("context", this.context);

    this._downloadsData = DownloadsCommon.getData(this.window, true, true, true);
    this._downloadsData.addView(this);
  }

  destructor(event) {
    this.panelview.removeEventListener("click", DownloadsSubview.onClick);
    this.panelview.removeEventListener("ViewHiding", DownloadsSubview.onViewHiding);
    this._downloadsData.removeView(this);
    gPanelViewInstances.delete(this);
    this.destroyed = true;
  }

  /**
   * DataView handler; invoked when a batch of downloads is being passed in -
   * usually when this instance is added as a view in the constructor.
   */
  onDownloadBatchStarting() {
    this.batchFragment = this.document.createDocumentFragment();
    this.window.clearTimeout(this._batchTimeout);
  }

  /**
   * DataView handler; invoked when the view stopped feeding its current list of
   * downloads.
   */
  onDownloadBatchEnded() {
    let {window} = this;
    window.clearTimeout(this._batchTimeout);
    let waitForMs = 200;
    if (this.batchFragment.childElementCount) {
      // Prepend the batch fragment.
      this.container.insertBefore(this.batchFragment, this.container.firstChild || null);
      waitForMs = 0;
    }
    // Wait a wee bit to dispatch the event, because another batch may start
    // right away.
    this._batchTimeout = window.setTimeout(() => {
      this._updateStatsFromDisk();
      this.panelview.dispatchEvent(new window.CustomEvent("DownloadsLoaded"));
    }, waitForMs);
    this.batchFragment = null;
  }

  /**
   * DataView handler; invoked when a new download is added to the list.
   *
   * @param {Download} download
   * @param {DOMNode}  [options.insertBefore]
   */
  onDownloadAdded(download, { insertBefore } = {}) {
    let shell = new DownloadsSubview.Button(download, this.document);
    this._viewItemsForDownloads.set(download, shell);
    // Triggger the code that update all attributes to match the downloads'
    // current state.
    shell.onChanged();

    // Since newest downloads are displayed at the top, either prepend the new
    // element or insert it after the one indicated by the insertBefore option.
    if (insertBefore) {
      this._viewItemsForDownloads.get(insertBefore)
          .element.insertAdjacentElement("afterend", shell.element);
    } else {
      (this.batchFragment || this.container).prepend(shell.element);
    }
  }

  /**
   * DataView Handler; invoked when the state of a download changed.
   *
   * @param {Download} download
   */
  onDownloadChanged(download) {
    this._viewItemsForDownloads.get(download).onChanged();
  }

  /**
   * DataView handler; invoked when a download is removed.
   *
   * @param {Download} download
   */
  onDownloadRemoved(download) {
    this._viewItemsForDownloads.get(download).element.remove();
  }

  /**
   * Schedule a refresh of the downloads that were added, which is mainly about
   * checking whether the target file still exists.
   * We're doing this during idle time and in chunks.
   */
  async _updateStatsFromDisk() {
    if (this._updatingStats)
      return;

    this._updatingStats = true;

    try {
      let idleOptions = { timeout: kMaxWaitForIdleMs };
      // Start with getting an idle moment to (maybe) refresh the list of downloads.
      await new Promise(resolve => this.window.requestIdleCallback(resolve), idleOptions);
      // In the meantime, this instance could have been destroyed, so take note.
      if (this.destroyed)
        return;

      let count = 0;
      for (let button of this.container.childNodes) {
        if (this.destroyed)
          return;
        if (!button._shell)
          continue;

        await button._shell.refresh();

        // Make sure to request a new idle moment every `kRefreshBatchSize` buttons.
        if (++count % kRefreshBatchSize === 0) {
          await new Promise(resolve => this.window.requestIdleCallback(resolve, idleOptions));
        }
      }
    } catch (ex) {
      Cu.reportError(ex);
    } finally {
      this._updatingStats = false;
    }
  }

  // ----- Static methods. -----

  /**
   * Show the Downloads subview panel and listen for events that will trigger
   * building the dynamic part of the view.
   *
   * @param {DOMNode} anchor The button that was commanded to trigger this function.
   */
  static show(anchor) {
    let document = anchor.ownerDocument;
    let window = anchor.ownerGlobal;

    let panelview = document.getElementById("PanelUI-downloads");
    anchor.setAttribute("closemenu", "none");
    gPanelViewInstances.set(panelview, new DownloadsSubview(panelview));

    // Since the DownloadsLists are propagated asynchronously, we need to wait a
    // little to get the view propagated.
    panelview.addEventListener("ViewShowing", event => {
      event.detail.addBlocker(new Promise(resolve => {
        panelview.addEventListener("DownloadsLoaded", resolve, { once: true });
      }));
    }, { once: true });

    window.PanelUI.showSubView("PanelUI-downloads", anchor);
  }

  /**
   * Handler method; reveal the users' download directory using the OS specific
   * method.
   */
  static async onShowDownloads() {
    // Retrieve the user's default download directory.
    let preferredDir = await Downloads.getPreferredDownloadsDirectory();
    DownloadsCommon.showDirectory(new FileUtils.File(preferredDir));
  }

  /**
   * Handler method; clear the list downloads finished and old(er) downloads,
   * just like in the Library.
   *
   * @param {DOMNode} button Button that was clicked to call this method.
   */
  static onClearDownloads(button) {
    let instance = gPanelViewInstances.get(button.closest("panelview"));
    if (!instance)
      return;
    instance._downloadsData.removeFinished();
    Cc["@mozilla.org/browser/download-history;1"]
      .getService(Ci.nsIDownloadHistory)
      .removeAllDownloads();
  }

  /**
   * Just before showing the context menu, anchored to a download item, we need
   * to set the right properties to make sure the right menu-items are visible.
   *
   * @param {DOMNode} button The Button the context menu will be anchored to.
   * @param {DOMNode} menu   The context menu.
   */
  static updateContextMenu(button, menu) {
    while (!button._shell) {
      button = button.parentNode;
    }
    menu.setAttribute("state", button.getAttribute("state"));
    if (button.hasAttribute("exists"))
      menu.setAttribute("exists", button.getAttribute("exists"));
    else
      menu.removeAttribute("exists");
    menu.classList.toggle("temporary-block", button.classList.contains("temporary-block"));
    for (let menuitem of menu.getElementsByTagName("menuitem")) {
      let command = menuitem.getAttribute("command");
      if (!command)
        continue;
      if (command == "downloadsCmd_clearDownloads") {
        menuitem.disabled = !DownloadsSubview.canClearDownloads(button);
      } else {
        menuitem.disabled = !button._shell.isCommandEnabled(command);
      }
    }

    // The menu anchorNode property is not available long enough to be used elsewhere,
    // so tack it another property name.
    menu._anchorNode = button;
  }

  /**
   * Right after the context menu was hidden, perform a bit of cleanup.
   *
   * @param {DOMNode} menu The context menu.
   */
  static onContextMenuHidden(menu) {
    delete menu._anchorNode;
  }

  /**
   * Static version of DownloadsSubview#canClearDownloads().
   *
   * @param {DOMNode} button Button that we'll use to find the right
   *                         DownloadsSubview instance.
   */
  static canClearDownloads(button) {
    let instance = gPanelViewInstances.get(button.closest("panelview"));
    if (!instance)
      return false;
    return instance.canClearDownloads(instance.container);
  }

  /**
   * Handler method; invoked when the Downloads panel is hidden and should be
   * torn down & cleaned up.
   *
   * @param {DOMEvent} event
   */
  static onViewHiding(event) {
    let instance = gPanelViewInstances.get(event.target);
    if (!instance)
      return;
    instance.destructor(event);
  }

  /**
   * Handler method; invoked when anything is clicked inside the Downloads panel.
   * Depending on the context, it will find the appropriate command to invoke.
   *
   * We don't have a command dispatcher registered for this view, so we don't go
   * through the goDoCommand path like we do for the other views.
   *
   * @param {DOMMouseEvent} event
   */
  static onClick(event) {
    // Middle clicks fall through and are regarded as left clicks.
    if (event.button > 1)
      return;

    let button = event.originalTarget;
    if (!button.hasAttribute || button.classList.contains("subviewbutton-back"))
      return;

    let command = "downloadsCmd_open";
    if (button.classList.contains("action-button")) {
      button = button.parentNode;
      command = button.hasAttribute("showLabel") ? "downloadsCmd_show" : "downloadsCmd_retry";
    } else if (button.localName == "menuitem") {
      command = button.getAttribute("command");
      button = button.parentNode._anchorNode;
    }
    while (button && !button._shell && button != this.panelview &&
           (!button.hasAttribute || !button.hasAttribute("oncommand"))) {
      button = button.parentNode;
    }

    // We don't need to do anything when no button was clicked, like a separator
    // or a blank panel area. Also, when 'oncommand' is set, the button will invoke
    // its own, custom command handler.
    if (!button || button == this.panelview || button.hasAttribute("oncommand"))
      return;

    if (command == "downloadsCmd_clearDownloads") {
      DownloadsSubview.onClearDownloads(button);
    } else if (button._shell.isCommandEnabled(command)) {
      button._shell[command]();
    }
  }
}

DownloadsSubview.Button = class extends DownloadsViewUI.DownloadElementShell {
  constructor(download, document) {
    super();
    this.download = download;

    this.element = document.createElement("toolbarbutton");
    this.element._shell = this;

    this.element.classList.add("subviewbutton", "subviewbutton-iconic", "download",
      "download-state");
  }

  get browserWindow() {
    return this.element.ownerGlobal;
  }

  async refresh() {
    if (this._targetFileChecked)
      return;

    try {
      await this.download.refresh();
    } catch (ex) {
      Cu.reportError(ex);
    } finally {
      this._targetFileChecked = true;
    }
  }

  /**
   * Handle state changes of a download.
   */
  onStateChanged() {
    // Since the state changed, we may need to check the target file again.
    this._targetFileChecked = false;

    this._updateState();
  }

  /**
   * Handler method; invoked when any state attribute of a download changed.
   */
  onChanged() {
    let newState = DownloadsCommon.stateOfDownload(this.download);
    if (this._downloadState !== newState) {
      this._downloadState = newState;
      this.onStateChanged();
    } else {
      this._updateState();
    }

    // This cannot be placed within onStateChanged because when a download goes
    // from hasBlockedData to !hasBlockedData it will still remain in the same state.
    this.element.classList.toggle("temporary-block",
                                  !!this.download.hasBlockedData);
  }

  /**
   * Update the DOM representation of this download to match the current, recently
   * updated, state.
   */
  _updateState() {
    super._updateState();
    this.element.setAttribute("label", this.element.getAttribute("displayName"));
    this.element.setAttribute("tooltiptext", this.element.getAttribute("fullStatus"));

    if (this.isCommandEnabled("downloadsCmd_show")) {
      this.element.setAttribute("openLabel", kButtonLabels.open);
      this.element.setAttribute("showLabel", kButtonLabels.show);
      this.element.removeAttribute("retryLabel");
    } else if (this.isCommandEnabled("downloadsCmd_retry")) {
      this.element.setAttribute("retryLabel", kButtonLabels.retry);
      this.element.removeAttribute("openLabel");
      this.element.removeAttribute("showLabel");
    } else {
      this.element.removeAttribute("openLabel");
      this.element.removeAttribute("retryLabel");
      this.element.removeAttribute("showLabel");
    }

    this._updateVisibility();
  }

  _updateVisibility() {
    let state = this.element.getAttribute("state");
    // This view only show completed and failed downloads.
    this.element.hidden = !(state == DownloadsCommon.DOWNLOAD_FINISHED ||
      state == DownloadsCommon.DOWNLOAD_FAILED);
  }

  /**
   * Command handler; copy the download URL to the OS general clipboard.
   */
  downloadsCmd_copyLocation() {
    let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"]
                      .getService(Ci.nsIClipboardHelper);
    clipboard.copyString(this.download.source.url);
  }
};
PK
!<�r�H�Hmodules/DownloadsViewUI.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 module is imported by code that uses the "download.xml" binding, and
 * provides prototypes for objects that handle input and display information.
 */

"use strict";

var EXPORTED_SYMBOLS = [
  "DownloadsViewUI",
];

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");

ChromeUtils.defineModuleGetter(this, "Downloads",
                               "resource://gre/modules/Downloads.jsm");
ChromeUtils.defineModuleGetter(this, "DownloadUtils",
                               "resource://gre/modules/DownloadUtils.jsm");
ChromeUtils.defineModuleGetter(this, "DownloadsCommon",
                               "resource:///modules/DownloadsCommon.jsm");
ChromeUtils.defineModuleGetter(this, "FileUtils",
                               "resource://gre/modules/FileUtils.jsm");
ChromeUtils.defineModuleGetter(this, "OS",
                               "resource://gre/modules/osfile.jsm");
ChromeUtils.defineModuleGetter(this, "PlacesUtils",
                               "resource://gre/modules/PlacesUtils.jsm");
ChromeUtils.defineModuleGetter(this, "RecentWindow",
                               "resource:///modules/RecentWindow.jsm");

var DownloadsViewUI = {
  /**
   * Returns true if the given string is the name of a command that can be
   * handled by the Downloads user interface, including standard commands.
   */
  isCommandName(name) {
    return name.startsWith("cmd_") || name.startsWith("downloadsCmd_");
  },
};

this.DownloadsViewUI.BaseView = class {
  canClearDownloads(nodeContainer) {
    // Downloads can be cleared if there's at least one removable download in
    // the list (either a history download or a completed session download).
    // Because history downloads are always removable and are listed after the
    // session downloads, check from bottom to top.
    for (let elt = nodeContainer.lastChild; elt; elt = elt.previousSibling) {
      // Stopped, paused, and failed downloads with partial data are removed.
      let download = elt._shell.download;
      if (download.stopped && !(download.canceled && download.hasPartialData)) {
        return true;
      }
    }
    return false;
  }
};

/**
 * A download element shell is responsible for handling the commands and the
 * displayed data for a single element that uses the "download.xml" binding.
 *
 * The information to display is obtained through the associated Download object
 * from the JavaScript API for downloads, and commands are executed using a
 * combination of Download methods and DownloadsCommon.jsm helper functions.
 *
 * Specialized versions of this shell must be defined, and they are required to
 * implement the "download" property or getter. Currently these objects are the
 * HistoryDownloadElementShell and the DownloadsViewItem for the panel. The
 * history view may use a HistoryDownload object in place of a Download object.
 */
this.DownloadsViewUI.DownloadElementShell = function() {};

this.DownloadsViewUI.DownloadElementShell.prototype = {
  /**
   * The richlistitem for the download, initialized by the derived object.
   */
  element: null,

  /**
   * URI string for the file type icon displayed in the download element.
   */
  get image() {
    if (!this.download.target.path) {
      // Old history downloads may not have a target path.
      return "moz-icon://.unknown?size=32";
    }

    // When a download that was previously in progress finishes successfully, it
    // means that the target file now exists and we can extract its specific
    // icon, for example from a Windows executable. To ensure that the icon is
    // reloaded, however, we must change the URI used by the XUL image element,
    // for example by adding a query parameter. This only works if we add one of
    // the parameters explicitly supported by the nsIMozIconURI interface.
    return "moz-icon://" + this.download.target.path + "?size=32" +
           (this.download.succeeded ? "&state=normal" : "");
  },

  /**
   * The user-facing label for the download. This is normally the leaf name of
   * the download target file. In case this is a very old history download for
   * which the target file is unknown, the download source URI is displayed.
   */
  get displayName() {
    if (!this.download.target.path) {
      return this.download.source.url;
    }
    return OS.Path.basename(this.download.target.path);
  },

  get browserWindow() {
    return RecentWindow.getMostRecentBrowserWindow();
  },

  /**
   * The progress element for the download, or undefined in case the XBL binding
   * has not been applied yet.
   */
  get _progressElement() {
    if (!this.__progressElement) {
      // If the element is not available now, we will try again the next time.
      this.__progressElement =
           this.element.ownerDocument.getAnonymousElementByAttribute(
                                         this.element, "anonid",
                                         "progressmeter");
    }
    return this.__progressElement;
  },

  /**
   * Processes a major state change in the user interface, then proceeds with
   * the normal progress update. This function is not called for every progress
   * update in order to improve performance.
   */
  _updateState() {
    this.element.setAttribute("displayName", this.displayName);
    this.element.setAttribute("image", this.image);
    this.element.setAttribute("state",
                              DownloadsCommon.stateOfDownload(this.download));

    if (!this.download.succeeded && this.download.error &&
        this.download.error.becauseBlockedByReputationCheck) {
      this.element.setAttribute("verdict",
                                this.download.error.reputationCheckVerdict);
    } else {
      this.element.removeAttribute("verdict");
    }

    // Since state changed, reset the time left estimation.
    this.lastEstimatedSecondsLeft = Infinity;

    this._updateProgress();
  },

  /**
   * Updates the elements that change regularly for in-progress downloads,
   * namely the progress bar and the status line.
   */
  _updateProgress() {
    if (this.download.succeeded) {
      // We only need to add or remove this attribute for succeeded downloads.
      if (this.download.target.exists) {
        this.element.setAttribute("exists", "true");
      } else {
        this.element.removeAttribute("exists");
      }
    }

    // When a block is confirmed, the removal of blocked data will not trigger a
    // state change for the download, so this class must be updated here.
    this.element.classList.toggle("temporary-block",
                                  !!this.download.hasBlockedData);

    // The progress bar is only displayed for in-progress downloads.
    if (this.download.hasProgress) {
      this.element.setAttribute("progressmode", "normal");
      this.element.setAttribute("progress", this.download.progress);
    } else {
      this.element.setAttribute("progressmode", "undetermined");
    }

    if (this.download.stopped && this.download.canceled &&
        this.download.hasPartialData) {
      this.element.setAttribute("progresspaused", "true");
    } else {
      this.element.removeAttribute("progresspaused");
    }

    // Dispatch the ValueChange event for accessibility, if possible.
    if (this._progressElement) {
      let event = this.element.ownerDocument.createEvent("Events");
      event.initEvent("ValueChange", true, true);
      this._progressElement.dispatchEvent(event);
    }

    let labels = this.statusLabels;
    this.element.setAttribute("status", labels.status);
    this.element.setAttribute("hoverStatus", labels.hoverStatus);
    this.element.setAttribute("fullStatus", labels.fullStatus);
  },

  lastEstimatedSecondsLeft: Infinity,

  /**
   * Returns the labels for the status of normal, full, and hovering cases. These
   * are returned by a single property because they are computed together.
   */
  get statusLabels() {
    let s = DownloadsCommon.strings;

    let status = "";
    let hoverStatus = "";
    let fullStatus = "";

    if (!this.download.stopped) {
      let totalBytes = this.download.hasProgress ? this.download.totalBytes
                                                 : -1;
      let newEstimatedSecondsLeft;
      [status, newEstimatedSecondsLeft] = DownloadUtils.getDownloadStatus(
                                          this.download.currentBytes,
                                          totalBytes,
                                          this.download.speed,
                                          this.lastEstimatedSecondsLeft);
      this.lastEstimatedSecondsLeft = newEstimatedSecondsLeft;
      hoverStatus = status;
    } else if (this.download.canceled && this.download.hasPartialData) {
      let totalBytes = this.download.hasProgress ? this.download.totalBytes
                                                 : -1;
      let transfer = DownloadUtils.getTransferTotal(this.download.currentBytes,
                                                    totalBytes);

      // We use the same XUL label to display both the state and the amount
      // transferred, for example "Paused -  1.1 MB".
      status = s.statusSeparatorBeforeNumber(s.statePaused, transfer);
      hoverStatus = status;
    } else if (!this.download.succeeded && !this.download.canceled &&
               !this.download.error) {
      status = s.stateStarting;
      hoverStatus = status;
    } else {
      let stateLabel;

      if (this.download.succeeded && !this.download.target.exists) {
        stateLabel = s.fileMovedOrMissing;
        hoverStatus = stateLabel;
      } else if (this.download.succeeded) {
        // For completed downloads, show the file size (e.g. "1.5 MB").
        if (this.download.target.size !== undefined) {
          let [size, unit] =
            DownloadUtils.convertByteUnits(this.download.target.size);
          stateLabel = s.sizeWithUnits(size, unit);
          status = s.statusSeparator(s.stateCompleted, stateLabel);
        } else {
          // History downloads may not have a size defined.
          stateLabel = s.sizeUnknown;
          status = s.stateCompleted;
        }
        hoverStatus = status;
      } else if (this.download.canceled) {
        stateLabel = s.stateCanceled;
      } else if (this.download.error.becauseBlockedByParentalControls) {
        stateLabel = s.stateBlockedParentalControls;
      } else if (this.download.error.becauseBlockedByReputationCheck) {
        stateLabel = this.rawBlockedTitleAndDetails[0];
      } else {
        stateLabel = s.stateFailed;
      }

      let referrer = this.download.source.referrer || this.download.source.url;
      let [displayHost /* ,fullHost */] = DownloadUtils.getURIHost(referrer);

      let date = new Date(this.download.endTime);
      let [displayDate /* ,fullDate */] = DownloadUtils.getReadableDates(date);

      let firstPart = s.statusSeparator(stateLabel, displayHost);
      fullStatus = s.statusSeparator(firstPart, displayDate);
      status = status || stateLabel;
    }

    return {
      status,
      hoverStatus: hoverStatus || fullStatus,
      fullStatus: fullStatus || status,
    };
  },

  /**
   * Returns [title, [details1, details2]] for blocked downloads.
   */
  get rawBlockedTitleAndDetails() {
    let s = DownloadsCommon.strings;
    if (!this.download.error ||
        !this.download.error.becauseBlockedByReputationCheck) {
      return [null, null];
    }
    switch (this.download.error.reputationCheckVerdict) {
      case Downloads.Error.BLOCK_VERDICT_UNCOMMON:
        return [s.blockedUncommon2, [s.unblockTypeUncommon2, s.unblockTip2]];
      case Downloads.Error.BLOCK_VERDICT_POTENTIALLY_UNWANTED:
        return [s.blockedPotentiallyUnwanted,
                [s.unblockTypePotentiallyUnwanted2, s.unblockTip2]];
      case Downloads.Error.BLOCK_VERDICT_MALWARE:
        return [s.blockedMalware, [s.unblockTypeMalware, s.unblockTip2]];
    }
    throw new Error("Unexpected reputationCheckVerdict: " +
                    this.download.error.reputationCheckVerdict);
  },

  /**
   * Shows the appropriate unblock dialog based on the verdict, and executes the
   * action selected by the user in the dialog, which may involve unblocking,
   * opening or removing the file.
   *
   * @param window
   *        The window to which the dialog should be anchored.
   * @param dialogType
   *        Can be "unblock", "chooseUnblock", or "chooseOpen".
   */
  confirmUnblock(window, dialogType) {
    DownloadsCommon.confirmUnblockDownload({
      verdict: this.download.error.reputationCheckVerdict,
      window,
      dialogType,
    }).then(action => {
      if (action == "open") {
        return this.unblockAndOpenDownload();
      } else if (action == "unblock") {
        return this.download.unblock();
      } else if (action == "confirmBlock") {
        return this.download.confirmBlock();
      }
      return Promise.resolve();
    }).catch(Cu.reportError);
  },

  /**
   * Unblocks the downloaded file and opens it.
   *
   * @return A promise that's resolved after the file has been opened.
   */
  unblockAndOpenDownload() {
    return this.download.unblock().then(() => this.downloadsCmd_open());
  },

  /**
   * Returns the name of the default command to use for the current state of the
   * download, when there is a double click or another default interaction. If
   * there is no default command for the current state, returns an empty string.
   * The commands are implemented as functions on this object or derived ones.
   */
  get currentDefaultCommandName() {
    switch (DownloadsCommon.stateOfDownload(this.download)) {
      case DownloadsCommon.DOWNLOAD_NOTSTARTED:
        return "downloadsCmd_cancel";
      case DownloadsCommon.DOWNLOAD_FAILED:
      case DownloadsCommon.DOWNLOAD_CANCELED:
        return "downloadsCmd_retry";
      case DownloadsCommon.DOWNLOAD_PAUSED:
        return "downloadsCmd_pauseResume";
      case DownloadsCommon.DOWNLOAD_FINISHED:
        return "downloadsCmd_open";
      case DownloadsCommon.DOWNLOAD_BLOCKED_PARENTAL:
        return "downloadsCmd_openReferrer";
      case DownloadsCommon.DOWNLOAD_DIRTY:
        return "downloadsCmd_showBlockedInfo";
    }
    return "";
  },

  /**
   * Returns true if the specified command can be invoked on the current item.
   * The commands are implemented as functions on this object or derived ones.
   *
   * @param aCommand
   *        Name of the command to check, for example "downloadsCmd_retry".
   */
  isCommandEnabled(aCommand) {
    switch (aCommand) {
      case "downloadsCmd_retry":
        return this.download.canceled || this.download.error;
      case "downloadsCmd_pauseResume":
        return this.download.hasPartialData && !this.download.error;
      case "downloadsCmd_openReferrer":
        return !!this.download.source.referrer;
      case "downloadsCmd_confirmBlock":
      case "downloadsCmd_chooseUnblock":
      case "downloadsCmd_chooseOpen":
      case "downloadsCmd_unblock":
      case "downloadsCmd_unblockAndOpen":
        return this.download.hasBlockedData;
      case "downloadsCmd_cancel":
        return this.download.hasPartialData || !this.download.stopped;
      case "downloadsCmd_open":
        // This property is false if the download did not succeed.
        return this.download.target.exists;
      case "downloadsCmd_show":
        // TODO: Bug 827010 - Handle part-file asynchronously.
        if (this.download.target.partFilePath) {
          let partFile = new FileUtils.File(this.download.target.partFilePath);
          if (partFile.exists()) {
            return true;
          }
        }

        // This property is false if the download did not succeed.
        return this.download.target.exists;
      case "downloadsCmd_delete":
      case "cmd_delete":
        // We don't want in-progress downloads to be removed accidentally.
        return this.download.stopped;
    }
    return DownloadsViewUI.isCommandName(aCommand) && !!this[aCommand];
  },

  doCommand(aCommand) {
    if (DownloadsViewUI.isCommandName(aCommand)) {
      this[aCommand]();
    }
  },

  downloadsCmd_cancel() {
    // This is the correct way to avoid race conditions when cancelling.
    this.download.cancel().catch(() => {});
    this.download.removePartialData().catch(Cu.reportError);
  },

  downloadsCmd_confirmBlock() {
    this.download.confirmBlock().catch(Cu.reportError);
  },

  downloadsCmd_open() {
    let file = new FileUtils.File(this.download.target.path);
    DownloadsCommon.openDownloadedFile(file, null, this.element.ownerGlobal);
  },

  downloadsCmd_openReferrer() {
    this.element.ownerGlobal.openURL(this.download.source.referrer);
  },

  downloadsCmd_pauseResume() {
    if (this.download.stopped) {
      this.download.start();
    } else {
      this.download.cancel();
    }
  },

  downloadsCmd_show() {
    let file = new FileUtils.File(this.download.target.path);
    DownloadsCommon.showDownloadedFile(file);
  },

  downloadsCmd_retry() {
    if (this.download.start) {
      // Errors when retrying are already reported as download failures.
      this.download.start().catch(() => {});
      return;
    }

    let window = this.browserWindow || this.element.ownerGlobal;
    let document = window.document;

    // Do not suggest a file name if we don't know the original target.
    let targetPath = this.download.target.path ?
                     OS.Path.basename(this.download.target.path) : null;
    window.DownloadURL(this.download.source.url, targetPath, document);
  },

  downloadsCmd_delete() {
    // Alias for the 'cmd_delete' command, because it may clash with another
    // controller which causes unexpected behavior as different codepaths claim
    // ownership.
    this.cmd_delete();
  },

  cmd_delete() {
    (async () => {
      // Remove the associated history element first, if any, so that the views
      // that combine history and session downloads won't resurrect the history
      // download into the view just before it is deleted permanently.
      try {
        await PlacesUtils.history.remove(this.download.source.url);
      } catch (ex) {
        Cu.reportError(ex);
      }
      let list = await Downloads.getList(Downloads.ALL);
      await list.remove(this.download);
      await this.download.finalize(true);
    })().catch(Cu.reportError);
  },
};
PK
!<�A׊n-n-modules/DragPositionManager.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

ChromeUtils.import("resource:///modules/CustomizableUI.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");

var gManagers = new WeakMap();

const kPaletteId = "customization-palette";

var EXPORTED_SYMBOLS = ["DragPositionManager"];

function AreaPositionManager(aContainer) {
  // Caching the direction and bounds of the container for quick access later:
  let window = aContainer.ownerGlobal;
  this._dir = window.getComputedStyle(aContainer).direction;
  let containerRect = aContainer.getBoundingClientRect();
  this._containerInfo = {
    left: containerRect.left,
    right: containerRect.right,
    top: containerRect.top,
    width: containerRect.width
  };
  this._horizontalDistance = null;
  this.update(aContainer);
}

AreaPositionManager.prototype = {
  _nodePositionStore: null,

  update(aContainer) {
    this._nodePositionStore = new WeakMap();
    let last = null;
    let singleItemHeight;
    for (let child of aContainer.children) {
      if (child.hidden) {
        continue;
      }
      let coordinates = this._lazyStoreGet(child);
      // We keep a baseline horizontal distance between nodes around
      // for use when we can't compare with previous/next nodes
      if (!this._horizontalDistance && last) {
        this._horizontalDistance = coordinates.left - last.left;
      }
      // We also keep the basic height of items for use below:
      if (!singleItemHeight) {
        singleItemHeight = coordinates.height;
      }
      last = coordinates;
    }
    this._heightToWidthFactor = this._containerInfo.width / singleItemHeight;
  },

  /**
   * Find the closest node in the container given the coordinates.
   * "Closest" is defined in a somewhat strange manner: we prefer nodes
   * which are in the same row over nodes that are in a different row.
   * In order to implement this, we use a weighted cartesian distance
   * where dy is more heavily weighted by a factor corresponding to the
   * ratio between the container's width and the height of its elements.
   */
  find(aContainer, aX, aY) {
    let closest = null;
    let minCartesian = Number.MAX_VALUE;
    let containerX = this._containerInfo.left;
    let containerY = this._containerInfo.top;
    for (let node of aContainer.children) {
      let coordinates = this._lazyStoreGet(node);
      let offsetX = coordinates.x - containerX;
      let offsetY = coordinates.y - containerY;
      let hDiff = offsetX - aX;
      let vDiff = offsetY - aY;
      // Then compensate for the height/width ratio so that we prefer items
      // which are in the same row:
      hDiff /= this._heightToWidthFactor;

      let cartesianDiff = hDiff * hDiff + vDiff * vDiff;
      if (cartesianDiff < minCartesian) {
        minCartesian = cartesianDiff;
        closest = node;
      }
    }

    // Now correct this node based on what we're dragging
    if (closest) {
      let targetBounds = this._lazyStoreGet(closest);
      let farSide = this._dir == "ltr" ? "right" : "left";
      let outsideX = targetBounds[farSide];
      // Check if we're closer to the next target than to this one:
      // Only move if we're not targeting a node in a different row:
      if (aY > targetBounds.top && aY < targetBounds.bottom) {
        if ((this._dir == "ltr" && aX > outsideX) ||
            (this._dir == "rtl" && aX < outsideX)) {
          return closest.nextSibling || aContainer;
        }
      }
    }
    return closest;
  },

  /**
   * "Insert" a "placeholder" by shifting the subsequent children out of the
   * way. We go through all the children, and shift them based on the position
   * they would have if we had inserted something before aBefore. We use CSS
   * transforms for this, which are CSS transitioned.
   */
  insertPlaceholder(aContainer, aBefore, aSize, aIsFromThisArea) {
    let isShifted = false;
    for (let child of aContainer.children) {
      // Don't need to shift hidden nodes:
      if (child.getAttribute("hidden") == "true") {
        continue;
      }
      // If this is the node before which we're inserting, start shifting
      // everything that comes after. One exception is inserting at the end
      // of the menupanel, in which case we do not shift the placeholders:
      if (child == aBefore) {
        isShifted = true;
      }
      if (isShifted) {
        if (aIsFromThisArea && !this._lastPlaceholderInsertion) {
          child.setAttribute("notransition", "true");
        }
        // Determine the CSS transform based on the next node:
        child.style.transform = this._diffWithNext(child, aSize);
      } else {
        // If we're not shifting this node, reset the transform
        child.style.transform = "";
      }
    }
    if (aContainer.lastChild && aIsFromThisArea &&
        !this._lastPlaceholderInsertion) {
      // Flush layout:
      aContainer.lastChild.getBoundingClientRect();
      // then remove all the [notransition]
      for (let child of aContainer.children) {
        child.removeAttribute("notransition");
      }
    }
    this._lastPlaceholderInsertion = aBefore;
  },

  /**
   * Reset all the transforms in this container, optionally without
   * transitioning them.
   * @param aContainer    the container in which to reset transforms
   * @param aNoTransition if truthy, adds a notransition attribute to the node
   *                      while resetting the transform.
   */
  clearPlaceholders(aContainer, aNoTransition) {
    for (let child of aContainer.children) {
      if (aNoTransition) {
        child.setAttribute("notransition", true);
      }
      child.style.transform = "";
      if (aNoTransition) {
        // Need to force a reflow otherwise this won't work.
        child.getBoundingClientRect();
        child.removeAttribute("notransition");
      }
    }
    // We snapped back, so we can assume there's no more
    // "last" placeholder insertion point to keep track of.
    if (aNoTransition) {
      this._lastPlaceholderInsertion = null;
    }
  },

  _diffWithNext(aNode, aSize) {
    let xDiff;
    let yDiff = null;
    let nodeBounds = this._lazyStoreGet(aNode);
    let side = this._dir == "ltr" ? "left" : "right";
    let next = this._getVisibleSiblingForDirection(aNode, "next");
    // First we determine the transform along the x axis.
    // Usually, there will be a next node to base this on:
    if (next) {
      let otherBounds = this._lazyStoreGet(next);
      xDiff = otherBounds[side] - nodeBounds[side];
      // We set this explicitly because otherwise some strange difference
      // between the height and the actual difference between line creeps in
      // and messes with alignments
      yDiff = otherBounds.top - nodeBounds.top;
    } else {
      // We don't have a sibling whose position we can use. First, let's see
      // if we're also the first item (which complicates things):
      let firstNode = this._firstInRow(aNode);
      if (aNode == firstNode) {
        // Maybe we stored the horizontal distance between nodes,
        // if not, we'll use the width of the incoming node as a proxy:
        xDiff = this._horizontalDistance || (this._dir == "ltr" ? 1 : -1) * aSize.width;
      } else {
        // If not, we should be able to get the distance to the previous node
        // and use the inverse, unless there's no room for another node (ie we
        // are the last node and there's no room for another one)
        xDiff = this._moveNextBasedOnPrevious(aNode, nodeBounds, firstNode);
      }
    }

    // If we've not determined the vertical difference yet, check it here
    if (yDiff === null) {
      // If the next node is behind rather than in front, we must have moved
      // vertically:
      if ((xDiff > 0 && this._dir == "rtl") || (xDiff < 0 && this._dir == "ltr")) {
        yDiff = aSize.height;
      } else {
        // Otherwise, we haven't
        yDiff = 0;
      }
    }
    return "translate(" + xDiff + "px, " + yDiff + "px)";
  },

  /**
   * Helper function to find the transform a node if there isn't a next node
   * to base that on.
   * @param aNode           the node to transform
   * @param aNodeBounds     the bounding rect info of this node
   * @param aFirstNodeInRow the first node in aNode's row
   */
  _moveNextBasedOnPrevious(aNode, aNodeBounds, aFirstNodeInRow) {
    let next = this._getVisibleSiblingForDirection(aNode, "previous");
    let otherBounds = this._lazyStoreGet(next);
    let side = this._dir == "ltr" ? "left" : "right";
    let xDiff = aNodeBounds[side] - otherBounds[side];
    // If, however, this means we move outside the container's box
    // (i.e. the row in which this item is placed is full)
    // we should move it to align with the first item in the next row instead
    let bound = this._containerInfo[this._dir == "ltr" ? "right" : "left"];
    if ((this._dir == "ltr" && xDiff + aNodeBounds.right > bound) ||
        (this._dir == "rtl" && xDiff + aNodeBounds.left < bound)) {
      xDiff = this._lazyStoreGet(aFirstNodeInRow)[side] - aNodeBounds[side];
    }
    return xDiff;
  },

  /**
   * Get position details from our cache. If the node is not yet cached, get its position
   * information and cache it now.
   * @param aNode  the node whose position info we want
   * @return the position info
   */
  _lazyStoreGet(aNode) {
    let rect = this._nodePositionStore.get(aNode);
    if (!rect) {
      // getBoundingClientRect() returns a DOMRect that is live, meaning that
      // as the element moves around, the rects values change. We don't want
      // that - we want a snapshot of what the rect values are right at this
      // moment, and nothing else. So we have to clone the values.
      let clientRect = aNode.getBoundingClientRect();
      rect = {
        left: clientRect.left,
        right: clientRect.right,
        width: clientRect.width,
        height: clientRect.height,
        top: clientRect.top,
        bottom: clientRect.bottom,
      };
      rect.x = rect.left + rect.width / 2;
      rect.y = rect.top + rect.height / 2;
      Object.freeze(rect);
      this._nodePositionStore.set(aNode, rect);
    }
    return rect;
  },

  _firstInRow(aNode) {
    // XXXmconley: I'm not entirely sure why we need to take the floor of these
    // values - it looks like, periodically, we're getting fractional pixels back
    // from lazyStoreGet. I've filed bug 994247 to investigate.
    let bound = Math.floor(this._lazyStoreGet(aNode).top);
    let rv = aNode;
    let prev;
    while (rv && (prev = this._getVisibleSiblingForDirection(rv, "previous"))) {
      if (Math.floor(this._lazyStoreGet(prev).bottom) <= bound) {
        return rv;
      }
      rv = prev;
    }
    return rv;
  },

  _getVisibleSiblingForDirection(aNode, aDirection) {
    let rv = aNode;
    do {
      rv = rv[aDirection + "Sibling"];
    } while (rv && rv.getAttribute("hidden") == "true");
    return rv;
  }
};

var DragPositionManager = {
  start(aWindow) {
    let areas = [aWindow.document.getElementById(kPaletteId)];
    for (let areaNode of areas) {
      let positionManager = gManagers.get(areaNode);
      if (positionManager) {
        positionManager.update(areaNode);
      } else {
        gManagers.set(areaNode, new AreaPositionManager(areaNode));
      }
    }
  },

  stop() {
    gManagers = new WeakMap();
  },

  getManagerForArea(aArea) {
    return gManagers.get(aArea);
  }
};

Object.freeze(DragPositionManager);
PK
!<��7!<D<Dmodules/ExtensionPopups.jsm/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";

/* exported PanelPopup, ViewPopup */

var EXPORTED_SYMBOLS = ["BasePopup", "PanelPopup", "ViewPopup"];

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");

ChromeUtils.defineModuleGetter(this, "CustomizableUI",
                               "resource:///modules/CustomizableUI.jsm");
ChromeUtils.defineModuleGetter(this, "E10SUtils",
                               "resource://gre/modules/E10SUtils.jsm");
ChromeUtils.defineModuleGetter(this, "ExtensionParent",
                               "resource://gre/modules/ExtensionParent.jsm");
ChromeUtils.defineModuleGetter(this, "setTimeout",
                               "resource://gre/modules/Timer.jsm");

ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
ChromeUtils.import("resource://gre/modules/ExtensionUtils.jsm");

var {
  DefaultWeakMap,
  promiseEvent,
} = ExtensionUtils;


const POPUP_LOAD_TIMEOUT_MS = 200;

const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";

function makeWidgetId(id) {
  id = id.toLowerCase();
  // FIXME: This allows for collisions.
  return id.replace(/[^a-z0-9_-]/g, "_");
}

function promisePopupShown(popup) {
  return new Promise(resolve => {
    if (popup.state == "open") {
      resolve();
    } else {
      popup.addEventListener("popupshown", function(event) {
        resolve();
      }, {once: true});
    }
  });
}

XPCOMUtils.defineLazyGetter(this, "standaloneStylesheets", () => {
  let stylesheets = [];

  if (AppConstants.platform === "macosx") {
    stylesheets.push("chrome://browser/content/extension-mac-panel.css");
  }
  if (AppConstants.platform === "win") {
    stylesheets.push("chrome://browser/content/extension-win-panel.css");
  }
  return stylesheets;
});

class BasePopup {
  constructor(extension, viewNode, popupURL, browserStyle, fixedWidth = false, blockParser = false) {
    this.extension = extension;
    this.popupURL = popupURL;
    this.viewNode = viewNode;
    this.browserStyle = browserStyle;
    this.window = viewNode.ownerGlobal;
    this.destroyed = false;
    this.fixedWidth = fixedWidth;
    this.blockParser = blockParser;

    extension.callOnClose(this);

    this.contentReady = new Promise(resolve => {
      this._resolveContentReady = resolve;
    });

    this.viewNode.addEventListener(this.DESTROY_EVENT, this);
    this.panel.addEventListener("popuppositioned", this, {once: true, capture: true});

    this.browser = null;
    this.browserLoaded = new Promise((resolve, reject) => {
      this.browserLoadedDeferred = {resolve, reject};
    });
    this.browserReady = this.createBrowser(viewNode, popupURL);

    BasePopup.instances.get(this.window).set(extension, this);
  }

  static for(extension, window) {
    return BasePopup.instances.get(window).get(extension);
  }

  close() {
    this.closePopup();
  }

  destroy() {
    this.extension.forgetOnClose(this);

    this.destroyed = true;
    this.browserLoadedDeferred.reject(new Error("Popup destroyed"));
    // Ignore unhandled rejections if the "attach" method is not called.
    this.browserLoaded.catch(() => {});

    BasePopup.instances.get(this.window).delete(this.extension);

    return this.browserReady.then(() => {
      if (this.browser) {
        this.destroyBrowser(this.browser, true);
        this.browser.remove();
      }
      if (this.stack) {
        this.stack.remove();
      }

      if (this.viewNode) {
        this.viewNode.removeEventListener(this.DESTROY_EVENT, this);
        delete this.viewNode.customRectGetter;
      }

      let {panel} = this;
      if (panel) {
        panel.style.removeProperty("--arrowpanel-background");
        panel.removeAttribute("remote");
      }

      this.browser = null;
      this.stack = null;
      this.viewNode = null;
    });
  }

  destroyBrowser(browser, finalize = false) {
    let mm = browser.messageManager;
    // If the browser has already been removed from the document, because the
    // popup was closed externally, there will be no message manager here, so
    // just replace our receiveMessage method with a stub.
    if (mm) {
      mm.removeMessageListener("DOMTitleChanged", this);
      mm.removeMessageListener("Extension:BrowserBackgroundChanged", this);
      mm.removeMessageListener("Extension:BrowserContentLoaded", this);
      mm.removeMessageListener("Extension:BrowserResized", this);
      mm.removeMessageListener("Extension:DOMWindowClose", this);
    } else if (finalize) {
      this.receiveMessage = () => {};
    }
  }

  // Returns the name of the event fired on `viewNode` when the popup is being
  // destroyed. This must be implemented by every subclass.
  get DESTROY_EVENT() {
    throw new Error("Not implemented");
  }

  get STYLESHEETS() {
    let sheets = [];

    if (this.browserStyle) {
      sheets.push(...ExtensionParent.extensionStylesheets);
    }
    if (!this.fixedWidth) {
      sheets.push(...standaloneStylesheets);
    }

    return sheets;
  }

  get panel() {
    let panel = this.viewNode;
    while (panel && panel.localName != "panel") {
      panel = panel.parentNode;
    }
    return panel;
  }

  receiveMessage({name, data}) {
    switch (name) {
      case "DOMTitleChanged":
        this.viewNode.setAttribute("aria-label", this.browser.contentTitle);
        break;

      case "Extension:BrowserBackgroundChanged":
        this.setBackground(data.background);
        break;

      case "Extension:BrowserContentLoaded":
        this.browserLoadedDeferred.resolve();
        break;

      case "Extension:BrowserResized":
        this._resolveContentReady();
        if (this.ignoreResizes) {
          this.dimensions = data;
        } else {
          this.resizeBrowser(data);
        }
        break;

      case "Extension:DOMWindowClose":
        this.closePopup();
        break;
    }
  }

  handleEvent(event) {
    switch (event.type) {
      case this.DESTROY_EVENT:
        if (!this.destroyed) {
          this.destroy();
        }
        break;
      case "popuppositioned":
        if (!this.destroyed) {
          this.browserLoaded.then(() => {
            if (this.destroyed) {
              return;
            }
            this.browser.messageManager.sendAsyncMessage("Extension:GrabFocus", {});
          }).catch(() => {
            // If the panel closes too fast an exception is raised here and tests will fail.
          });
        }
        break;
    }
  }

  createBrowser(viewNode, popupURL = null) {
    let document = viewNode.ownerDocument;

    let stack = document.createElementNS(XUL_NS, "stack");
    stack.setAttribute("class", "webextension-popup-stack");

    let browser = document.createElementNS(XUL_NS, "browser");
    browser.setAttribute("type", "content");
    browser.setAttribute("disableglobalhistory", "true");
    browser.setAttribute("transparent", "true");
    browser.setAttribute("class", "webextension-popup-browser");
    browser.setAttribute("webextension-view-type", "popup");
    browser.setAttribute("tooltip", "aHTMLTooltip");
    browser.setAttribute("contextmenu", "contentAreaContextMenu");
    browser.setAttribute("autocompletepopup", "PopupAutoComplete");
    browser.setAttribute("selectmenulist", "ContentSelectDropdown");
    browser.setAttribute("selectmenuconstrained", "false");
    browser.sameProcessAsFrameLoader = this.extension.groupFrameLoader;

    if (this.extension.remote) {
      browser.setAttribute("remote", "true");
      browser.setAttribute("remoteType", E10SUtils.EXTENSION_REMOTE_TYPE);
    }

    // We only need flex sizing for the sake of the slide-in sub-views of the
    // main menu panel, so that the browser occupies the full width of the view,
    // and also takes up any extra height that's available to it.
    browser.setAttribute("flex", "1");
    stack.setAttribute("flex", "1");

    // Note: When using noautohide panels, the popup manager will add width and
    // height attributes to the panel, breaking our resize code, if the browser
    // starts out smaller than 30px by 10px. This isn't an issue now, but it
    // will be if and when we popup debugging.

    this.browser = browser;
    this.stack = stack;

    let readyPromise;
    if (this.extension.remote) {
      readyPromise = promiseEvent(browser, "XULFrameLoaderCreated");
    } else {
      readyPromise = promiseEvent(browser, "load");
    }

    stack.appendChild(browser);
    viewNode.appendChild(stack);

    ExtensionParent.apiManager.emit("extension-browser-inserted", browser);

    let setupBrowser = browser => {
      let mm = browser.messageManager;
      mm.addMessageListener("DOMTitleChanged", this);
      mm.addMessageListener("Extension:BrowserBackgroundChanged", this);
      mm.addMessageListener("Extension:BrowserContentLoaded", this);
      mm.addMessageListener("Extension:BrowserResized", this);
      mm.addMessageListener("Extension:DOMWindowClose", this, true);
      return browser;
    };

    if (!popupURL) {
      // For remote browsers, we can't do any setup until the frame loader is
      // created. Non-remote browsers get a message manager immediately, so
      // there's no need to wait for the load event.
      if (this.extension.remote) {
        return readyPromise.then(() => setupBrowser(browser));
      }
      return setupBrowser(browser);
    }

    return readyPromise.then(() => {
      setupBrowser(browser);
      let mm = browser.messageManager;

      // Sets the context information for context menus.
      mm.loadFrameScript("chrome://browser/content/content.js", true);

      mm.loadFrameScript(
        "chrome://extensions/content/ext-browser-content.js", false);

      mm.sendAsyncMessage("Extension:InitBrowser", {
        allowScriptsToClose: true,
        blockParser: this.blockParser,
        fixedWidth: this.fixedWidth,
        maxWidth: 800,
        maxHeight: 600,
        stylesheets: this.STYLESHEETS,
      });

      browser.loadURIWithFlags(popupURL, {triggeringPrincipal: this.extension.principal});
    });
  }

  unblockParser() {
    this.browserReady.then(browser => {
      this.browser.messageManager.sendAsyncMessage("Extension:UnblockParser");
    });
  }

  resizeBrowser({width, height, detail}) {
    if (this.fixedWidth) {
      // Figure out how much extra space we have on the side of the panel
      // opposite the arrow.
      let side = this.panel.getAttribute("side") == "top" ? "bottom" : "top";
      let maxHeight = this.viewHeight + this.extraHeight[side];

      height = Math.min(height, maxHeight);
      this.browser.style.height = `${height}px`;

      // Used by the panelmultiview code to figure out sizing without reparenting
      // (which would destroy the browser and break us).
      this.lastCalculatedInViewHeight = Math.max(height, this.viewHeight);
    } else {
      this.browser.style.width = `${width}px`;
      this.browser.style.minWidth = `${width}px`;
      this.browser.style.height = `${height}px`;
      this.browser.style.minHeight = `${height}px`;
    }

    let event = new this.window.CustomEvent("WebExtPopupResized", {detail});
    this.browser.dispatchEvent(event);
  }

  setBackground(background = "") {
    if (background) {
      this.panel.style.setProperty("--arrowpanel-background", background);
    }
    this.background = background;
  }
}

/**
 * A map of active popups for a given browser window.
 *
 * WeakMap[window -> WeakMap[Extension -> BasePopup]]
 */
BasePopup.instances = new DefaultWeakMap(() => new WeakMap());

class PanelPopup extends BasePopup {
  constructor(extension, document, popupURL, browserStyle) {
    let panel = document.createElement("panel");
    panel.setAttribute("id", makeWidgetId(extension.id) + "-panel");
    panel.setAttribute("class", "browser-extension-panel");
    panel.setAttribute("tabspecific", "true");
    panel.setAttribute("type", "arrow");
    panel.setAttribute("role", "group");
    if (extension.remote) {
      panel.setAttribute("remote", "true");
    }

    document.getElementById("mainPopupSet").appendChild(panel);

    panel.addEventListener("popupshowing", () => {
      let event = new this.window.CustomEvent("WebExtPopupLoaded", {
        bubbles: true,
        detail: {extension},
      });
      this.browser.dispatchEvent(event);
    }, {once: true});

    super(extension, panel, popupURL, browserStyle);
  }

  get DESTROY_EVENT() {
    return "popuphidden";
  }

  destroy() {
    super.destroy();
    this.viewNode.remove();
    this.viewNode = null;
  }

  closePopup() {
    promisePopupShown(this.viewNode).then(() => {
      // Make sure we're not already destroyed, or removed from the DOM.
      if (this.viewNode && this.viewNode.hidePopup) {
        this.viewNode.hidePopup();
      }
    });
  }
}

class ViewPopup extends BasePopup {
  constructor(extension, window, popupURL, browserStyle, fixedWidth, blockParser) {
    let document = window.document;

    // Create a temporary panel to hold the browser while it pre-loads its
    // content. This panel will never be shown, but the browser's docShell will
    // be swapped with the browser in the real panel when it's ready.
    let panel = document.createElement("panel");
    panel.setAttribute("type", "arrow");
    if (extension.remote) {
      panel.setAttribute("remote", "true");
    }
    document.getElementById("mainPopupSet").appendChild(panel);

    super(extension, panel, popupURL, browserStyle, fixedWidth, blockParser);

    this.ignoreResizes = true;

    this.attached = false;
    this.shown = false;
    this.tempPanel = panel;

    this.browser.classList.add("webextension-preload-browser");
  }

  /**
   * Attaches the pre-loaded browser to the given view node, and reserves a
   * promise which resolves when the browser is ready.
   *
   * @param {Element} viewNode
   *        The node to attach the browser to.
   * @returns {Promise<boolean>}
   *        Resolves when the browser is ready. Resolves to `false` if the
   *        browser was destroyed before it was fully loaded, and the popup
   *        should be closed, or `true` otherwise.
   */
  async attach(viewNode) {
    this.viewNode = viewNode;
    this.viewNode.addEventListener(this.DESTROY_EVENT, this);
    this.viewNode.setAttribute("closemenu", "none");

    this.panel.addEventListener("popuppositioned", this, {once: true, capture: true});
    if (this.extension.remote) {
      this.panel.setAttribute("remote", "true");
    }

    // Wait until the browser element is fully initialized, and give it at least
    // a short grace period to finish loading its initial content, if necessary.
    //
    // In practice, the browser that was created by the mousdown handler should
    // nearly always be ready by this point.
    await Promise.all([
      this.browserReady,
      Promise.race([
        // This promise may be rejected if the popup calls window.close()
        // before it has fully loaded.
        this.browserLoaded.catch(() => {}),
        new Promise(resolve => setTimeout(resolve, POPUP_LOAD_TIMEOUT_MS)),
      ]),
    ]);

    if (!this.destroyed && !this.panel) {
      this.destroy();
    }

    if (this.destroyed) {
      CustomizableUI.hidePanelForNode(viewNode);
      return false;
    }

    this.attached = true;


    // Store the initial height of the view, so that we never resize menu panel
    // sub-views smaller than the initial height of the menu.
    this.viewHeight = this.viewNode.boxObject.height;

    // Calculate the extra height available on the screen above and below the
    // menu panel. Use that to calculate the how much the sub-view may grow.
    let popupRect = this.panel.getBoundingClientRect();

    this.setBackground(this.background);

    let win = this.window;
    let popupBottom = win.mozInnerScreenY + popupRect.bottom;
    let popupTop = win.mozInnerScreenY + popupRect.top;

    let screenBottom = win.screen.availTop + win.screen.availHeight;
    this.extraHeight = {
      bottom: Math.max(0, screenBottom - popupBottom),
      top:  Math.max(0, popupTop - win.screen.availTop),
    };

    // Create a new browser in the real popup.
    let browser = this.browser;
    await this.createBrowser(this.viewNode);

    this.ignoreResizes = false;

    this.browser.swapDocShells(browser);
    this.destroyBrowser(browser);

    if (this.dimensions) {
      if (this.fixedWidth) {
        delete this.dimensions.width;
      }
      this.resizeBrowser(this.dimensions);
    }

    this.viewNode.customRectGetter = () => {
      return {height: this.lastCalculatedInViewHeight || this.viewHeight};
    };

    this.tempPanel.remove();
    this.tempPanel = null;

    this.shown = true;

    if (this.destroyed) {
      this.closePopup();
      this.destroy();
      return false;
    }

    let event = new this.window.CustomEvent("WebExtPopupLoaded", {
      bubbles: true,
      detail: {extension: this.extension},
    });
    this.browser.dispatchEvent(event);

    return true;
  }

  destroy() {
    return super.destroy().then(() => {
      if (this.tempPanel) {
        this.tempPanel.remove();
        this.tempPanel = null;
      }
    });
  }

  get DESTROY_EVENT() {
    return "ViewHiding";
  }

  closePopup() {
    if (this.shown) {
      CustomizableUI.hidePanelForNode(this.viewNode);
    } else if (this.attached) {
      this.destroyed = true;
    } else {
      this.destroy();
    }
  }
}
PK
!<�	Ty=y=modules/ExtensionsUI.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";

var EXPORTED_SYMBOLS = ["ExtensionsUI"];

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/EventEmitter.jsm");

ChromeUtils.defineModuleGetter(this, "AddonManager",
                               "resource://gre/modules/AddonManager.jsm");
ChromeUtils.defineModuleGetter(this, "AddonManagerPrivate",
                               "resource://gre/modules/AddonManager.jsm");
ChromeUtils.defineModuleGetter(this, "AppMenuNotifications",
                               "resource://gre/modules/AppMenuNotifications.jsm");
ChromeUtils.defineModuleGetter(this, "ExtensionData",
                               "resource://gre/modules/Extension.jsm");
ChromeUtils.defineModuleGetter(this, "RecentWindow",
                               "resource:///modules/RecentWindow.jsm");
ChromeUtils.defineModuleGetter(this, "Services",
                               "resource://gre/modules/Services.jsm");

XPCOMUtils.defineLazyPreferenceGetter(this, "WEBEXT_PERMISSION_PROMPTS",
                                      "extensions.webextPermissionPrompts", false);

const DEFAULT_EXTENSION_ICON = "chrome://mozapps/skin/extensions/extensionGeneric.svg";

const BROWSER_PROPERTIES = "chrome://browser/locale/browser.properties";
const BRAND_PROPERTIES = "chrome://branding/locale/brand.properties";

const HTML_NS = "http://www.w3.org/1999/xhtml";

var ExtensionsUI = {
  sideloaded: new Set(),
  updates: new Set(),
  sideloadListener: null,
  histogram: null,

  async init() {
    this.histogram = Services.telemetry.getHistogramById("EXTENSION_INSTALL_PROMPT_RESULT");

    Services.obs.addObserver(this, "webextension-permission-prompt");
    Services.obs.addObserver(this, "webextension-update-permissions");
    Services.obs.addObserver(this, "webextension-install-notify");
    Services.obs.addObserver(this, "webextension-optional-permission-prompt");
    Services.obs.addObserver(this, "webextension-defaultsearch-prompt");

    await Services.wm.getMostRecentWindow("navigator:browser").delayedStartupPromise;

    this._checkForSideloaded();
    this._checkNewDistroAddons();
  },

  async _checkForSideloaded() {
    let sideloaded = await AddonManagerPrivate.getNewSideloads();

    if (!sideloaded.length) {
      // No new side-loads. We're done.
      return;
    }

    // The ordering shouldn't matter, but tests depend on notifications
    // happening in a specific order.
    sideloaded.sort((a, b) => a.id.localeCompare(b.id));

    if (WEBEXT_PERMISSION_PROMPTS) {
      if (!this.sideloadListener) {
        this.sideloadListener = {
          onEnabled: addon => {
            if (!this.sideloaded.has(addon)) {
              return;
            }

            this.sideloaded.delete(addon);
              this._updateNotifications();

            if (this.sideloaded.size == 0) {
              AddonManager.removeAddonListener(this.sideloadListener);
              this.sideloadListener = null;
            }
          },
        };
        AddonManager.addAddonListener(this.sideloadListener);
      }

      for (let addon of sideloaded) {
        this.sideloaded.add(addon);
      }
        this._updateNotifications();
    } else {
      // This and all the accompanying about:newaddon code can eventually
      // be removed.  See bug 1331521.
      let win = RecentWindow.getMostRecentBrowserWindow();
      for (let addon of sideloaded) {
        win.openUILinkIn(`about:newaddon?id=${addon.id}`, "tab");
      }
    }
  },

  async _checkNewDistroAddons() {
    let newDistroAddons = AddonManagerPrivate.getNewDistroAddons();
    if (!newDistroAddons) {
      return;
    }

    for (let id of newDistroAddons) {
      let addon = await AddonManager.getAddonByID(id);

      let win = Services.wm.getMostRecentWindow("navigator:browser");
      if (!win) {
        return;
      }

      let {gBrowser} = win;
      let browser = gBrowser.selectedBrowser;

      // The common case here is that we enter this code right after startup
      // in a brand new profile so we haven't yet loaded a page.  That state is
      // surprisingly difficult to detect but wait until we've actually loaded
      // a page.
      if (browser.currentURI.spec == "about:blank" ||
          browser.webProgress.isLoadingDocument) {
        await new Promise(resolve => {
          let listener = {
            onLocationChange(browser_, webProgress, ...ignored) {
              if (webProgress.isTopLevel && browser_ == browser) {
                gBrowser.removeTabsProgressListener(listener);
                resolve();
              }
            },
          };
          gBrowser.addTabsProgressListener(listener);
        });
      }

      // If we're at about:newtab and the url bar gets focus, that will
      // prevent a doorhanger from displaying.
      // Our elegant solution is to ... take focus away from the url bar.
      win.gURLBar.blur();

      let strings = this._buildStrings({
        addon,
        permissions: addon.userPermissions,
      });
      let accepted = await this.showPermissionsPrompt(browser, strings,
                                                      addon.iconURL);
      if (accepted) {
        addon.userDisabled = false;
      }
    }
  },


  _updateNotifications() {
    if (this.sideloaded.size + this.updates.size == 0) {
      AppMenuNotifications.removeNotification("addon-alert");
    } else {
      AppMenuNotifications.showBadgeOnlyNotification("addon-alert");
    }
    this.emit("change");
  },

  showAddonsManager(browser, strings, icon, histkey) {
    let global = browser.selectedBrowser.ownerGlobal;
    return global.BrowserOpenAddonsMgr("addons://list/extension").then(aomWin => {
      let aomBrowser = aomWin.QueryInterface(Ci.nsIInterfaceRequestor)
                             .getInterface(Ci.nsIDocShell)
                             .chromeEventHandler;
      return this.showPermissionsPrompt(aomBrowser, strings, icon, histkey);
    });
  },

  showSideloaded(browser, addon) {
    addon.markAsSeen();
    this.sideloaded.delete(addon);
    this._updateNotifications();

    let strings = this._buildStrings({
      addon,
      permissions: addon.userPermissions,
      type: "sideload",
    });
    this.showAddonsManager(browser, strings, addon.iconURL, "sideload")
        .then(answer => {
          addon.userDisabled = !answer;
        });
  },

  showUpdate(browser, info) {
    this.showAddonsManager(browser, info.strings, info.addon.iconURL, "update")
        .then(answer => {
          if (answer) {
            info.resolve();
          } else {
            info.reject();
          }
          // At the moment, this prompt will re-appear next time we do an update
          // check.  See bug 1332360 for proposal to avoid this.
          this.updates.delete(info);
          this._updateNotifications();
        });
  },

  observe(subject, topic, data) {
    if (topic == "webextension-permission-prompt") {
      let {target, info} = subject.wrappedJSObject;

      // Dismiss the progress notification.  Note that this is bad if
      // there are multiple simultaneous installs happening, see
      // bug 1329884 for a longer explanation.
      let progressNotification = target.ownerGlobal.PopupNotifications.getNotification("addon-progress", target);
      if (progressNotification) {
        progressNotification.remove();
      }

      info.unsigned = info.addon.signedState <= AddonManager.SIGNEDSTATE_MISSING;
      if (info.unsigned && Cu.isInAutomation &&
          Services.prefs.getBoolPref("extensions.ui.ignoreUnsigned", false)) {
        info.unsigned = false;
      }

      let strings = this._buildStrings(info);

      // If this is an update with no promptable permissions, just apply it
      if (info.type == "update" && strings.msgs.length == 0) {
        info.resolve();
        return;
      }

      let icon = info.unsigned ? "chrome://browser/skin/warning.svg" : info.icon;

      let histkey;
      if (info.type == "sideload") {
        histkey = "sideload";
      } else if (info.type == "update") {
        histkey = "update";
      } else if (info.source == "AMO") {
        histkey = "installAmo";
      } else if (info.source == "local") {
        histkey = "installLocal";
      } else {
        histkey = "installWeb";
      }

      this.showPermissionsPrompt(target, strings, icon, histkey)
          .then(answer => {
            if (answer) {
              info.resolve();
            } else {
              info.reject();
            }
          });
    } else if (topic == "webextension-update-permissions") {
      let info = subject.wrappedJSObject;
      info.type = "update";
      let strings = this._buildStrings(info);

      // If we don't prompt for any new permissions, just apply it
      if (strings.msgs.length == 0) {
        info.resolve();
        return;
      }

      let update = {
        strings,
        addon: info.addon,
        resolve: info.resolve,
        reject: info.reject,
      };

      this.updates.add(update);
      this._updateNotifications();
    } else if (topic == "webextension-install-notify") {
      let {target, addon, callback} = subject.wrappedJSObject;
      this.showInstallNotification(target, addon).then(() => {
        if (callback) {
          callback();
        }
      });
    } else if (topic == "webextension-optional-permission-prompt") {
      let {browser, name, icon, permissions, resolve} = subject.wrappedJSObject;
      let strings = this._buildStrings({
        type: "optional",
        addon: {name},
        permissions,
      });

      // If we don't have any promptable permissions, just proceed
      if (strings.msgs.length == 0) {
        resolve(true);
        return;
      }
      resolve(this.showPermissionsPrompt(browser, strings, icon));
    } else if (topic == "webextension-defaultsearch-prompt") {
      let {browser, name, icon, resolve, currentEngine, newEngine} = subject.wrappedJSObject;

      let bundle = Services.strings.createBundle(BROWSER_PROPERTIES);

      let strings = {};
      strings.acceptText = bundle.GetStringFromName("webext.defaultSearchYes.label");
      strings.acceptKey = bundle.GetStringFromName("webext.defaultSearchYes.accessKey");
      strings.cancelText = bundle.GetStringFromName("webext.defaultSearchNo.label");
      strings.cancelKey = bundle.GetStringFromName("webext.defaultSearchNo.accessKey");
      strings.addonName = name;
      strings.text = bundle.formatStringFromName("webext.defaultSearch.description",
                                                 ["<>", currentEngine, newEngine], 3);
      resolve(this.showDefaultSearchPrompt(browser, strings, icon));
    }
  },

  // Create a set of formatted strings for a permission prompt
  _buildStrings(info) {
    let bundle = Services.strings.createBundle(BROWSER_PROPERTIES);
    let brandBundle = Services.strings.createBundle(BRAND_PROPERTIES);
    let appName = brandBundle.GetStringFromName("brandShortName");
    let info2 = Object.assign({appName}, info);

    let strings = ExtensionData.formatPermissionStrings(info2, bundle);
    strings.addonName = info.addon.name;
    return strings;
  },

  showPermissionsPrompt(browser, strings, icon, histkey) {
    function eventCallback(topic) {
      let doc = this.browser.ownerDocument;
      if (topic == "showing") {
        let textEl = doc.getElementById("addon-webext-perm-text");
        textEl.textContent = strings.text;
        textEl.hidden = !strings.text;

        let listIntroEl = doc.getElementById("addon-webext-perm-intro");
        listIntroEl.textContent = strings.listIntro;
        listIntroEl.hidden = (strings.msgs.length == 0);

        let list = doc.getElementById("addon-webext-perm-list");
        while (list.firstChild) {
          list.firstChild.remove();
        }

        for (let msg of strings.msgs) {
          let item = doc.createElementNS(HTML_NS, "li");
          item.textContent = msg;
          list.appendChild(item);
        }
      } else if (topic == "swapping") {
        return true;
      }
      return false;
    }

    let popupOptions = {
      hideClose: true,
      popupIconURL: icon || DEFAULT_EXTENSION_ICON,
      persistent: true,
      eventCallback,
      name: strings.addonName,
    };

    let win = browser.ownerGlobal;
    return new Promise(resolve => {
      let action = {
        label: strings.acceptText,
        accessKey: strings.acceptKey,
        callback: () => {
          if (histkey) {
            this.histogram.add(histkey + "Accepted");
          }
          resolve(true);
        },
      };
      let secondaryActions = [
        {
          label: strings.cancelText,
          accessKey: strings.cancelKey,
          callback: () => {
            if (histkey) {
              this.histogram.add(histkey + "Rejected");
            }
            resolve(false);
          },
        },
      ];

      win.PopupNotifications.show(browser, "addon-webext-permissions", strings.header,
                                  "addons-notification-icon", action,
                                  secondaryActions, popupOptions);
    });
  },

  showDefaultSearchPrompt(browser, strings, icon) {
    return new Promise(resolve => {
      let popupOptions = {
        hideClose: true,
        popupIconURL: icon || DEFAULT_EXTENSION_ICON,
        persistent: false,
        removeOnDismissal: true,
        eventCallback(topic) {
          if (topic == "removed") {
            resolve(false);
          }
        },
        name: strings.addonName,
      };

      let action = {
        label: strings.acceptText,
        accessKey: strings.acceptKey,
        disableHighlight: true,
        callback: () => {
          resolve(true);
        },
      };
      let secondaryActions = [
        {
          label: strings.cancelText,
          accessKey: strings.cancelKey,
          callback: () => {
            resolve(false);
          },
        },
      ];

      let win = browser.ownerGlobal;
      win.PopupNotifications.show(browser, "addon-webext-defaultsearch", strings.text,
                                  "addons-notification-icon", action,
                                  secondaryActions, popupOptions);
    });
  },

  showInstallNotification(target, addon) {
    let win = target.ownerGlobal;
    let popups = win.PopupNotifications;

    let brandBundle = win.document.getElementById("bundle_brand");
    let appName = brandBundle.getString("brandShortName");
    let bundle = win.gNavigatorBundle;

    let message = bundle.getFormattedString("addonPostInstall.message1",
                                            ["<>", appName]);
    return new Promise(resolve => {
      let action = {
        label: bundle.getString("addonPostInstall.okay.label"),
        accessKey: bundle.getString("addonPostInstall.okay.key"),
        callback: resolve,
      };

      let icon = addon.isWebExtension ?
                 addon.iconURL || DEFAULT_EXTENSION_ICON :
                 "chrome://browser/skin/addons/addon-install-installed.svg";
      let options = {
        hideClose: true,
        timeout: Date.now() + 30000,
        popupIconURL: icon,
        eventCallback(topic) {
          if (topic == "dismissed") {
            resolve();
          }
        },
        name: addon.name,
      };

      popups.show(target, "addon-installed", message, "addons-notification-icon",
                  action, null, options);
    });
  },
};

EventEmitter.decorate(ExtensionsUI);
PK
!<hF`��modules/Feeds.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = [ "Feeds" ];

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");

ChromeUtils.defineModuleGetter(this, "BrowserUtils",
                               "resource://gre/modules/BrowserUtils.jsm");
ChromeUtils.defineModuleGetter(this, "RecentWindow",
                               "resource:///modules/RecentWindow.jsm");

var Feeds = {
  // Listeners are added in nsBrowserGlue.js
  receiveMessage(aMessage) {
    let data = aMessage.data;
    switch (aMessage.name) {
      case "WCCR:registerProtocolHandler": {
        let registrar = Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"].
                          getService(Ci.nsIWebContentHandlerRegistrar);
        registrar.registerProtocolHandler(data.protocol, data.uri, data.title,
                                          aMessage.target);
        break;
      }

      case "WCCR:registerContentHandler": {
        let registrar = Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"].
                          getService(Ci.nsIWebContentHandlerRegistrar);
        registrar.registerContentHandler(data.contentType, data.uri, data.title,
                                         aMessage.target);
        break;
      }

      case "WCCR:setAutoHandler": {
        let registrar = Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"].
                          getService(Ci.nsIWebContentConverterService);
        registrar.setAutoHandler(data.contentType, data.handler);
        break;
      }

      case "FeedConverter:addLiveBookmark": {
        let topWindow = RecentWindow.getMostRecentBrowserWindow();
        topWindow.PlacesCommandHook.addLiveBookmark(data.spec, data.title, data.subtitle)
                                   .catch(Cu.reportError);
        break;
      }
    }
  },

  /**
   * isValidFeed: checks whether the given data represents a valid feed.
   *
   * @param  aLink
   *         An object representing a feed with title, href and type.
   * @param  aPrincipal
   *         The principal of the document, used for security check.
   * @param  aIsFeed
   *         Whether this is already a known feed or not, if true only a security
   *         check will be performed.
   */
  isValidFeed(aLink, aPrincipal, aIsFeed) {
    if (!aLink || !aPrincipal)
      return false;

    var type = aLink.type.toLowerCase().replace(/^\s+|\s*(?:;.*)?$/g, "");
    if (!aIsFeed) {
      aIsFeed = (type == "application/rss+xml" ||
                 type == "application/atom+xml");
    }

    if (aIsFeed) {
      try {
        let href = BrowserUtils.makeURI(aLink.href, aLink.ownerDocument.characterSet);
        BrowserUtils.urlSecurityCheck(href, aPrincipal,
                                      Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL);
        return type || "application/rss+xml";
      } catch (ex) {
      }
    }

    return null;
  },

};
PK
!<Ɯ8P//modules/FormSubmitObserver.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/*
 * Handles the validation callback from nsIFormFillController and
 * the display of the help panel on invalid elements.
 */

"use strict";

var EXPORTED_SYMBOLS = [ "FormSubmitObserver" ];

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/BrowserUtils.jsm");

function FormSubmitObserver(aWindow, aTabChildGlobal) {
  this.init(aWindow, aTabChildGlobal);
}

FormSubmitObserver.prototype =
{
  _validationMessage: "",
  _content: null,
  _element: null,

  /*
   * Public apis
   */

  init(aWindow, aTabChildGlobal) {
    this._content = aWindow;
    this._tab = aTabChildGlobal;
    this._mm =
      this._content.QueryInterface(Ci.nsIInterfaceRequestor)
                   .getInterface(Ci.nsIDocShell)
                   .sameTypeRootTreeItem
                   .QueryInterface(Ci.nsIDocShell)
                   .QueryInterface(Ci.nsIInterfaceRequestor)
                   .getInterface(Ci.nsIContentFrameMessageManager);

    // nsIFormSubmitObserver callback about invalid forms. See HTMLFormElement
    // for details.
    Services.obs.addObserver(this, "invalidformsubmit");
    this._tab.addEventListener("pageshow", this);
    this._tab.addEventListener("unload", this);
  },

  uninit() {
    Services.obs.removeObserver(this, "invalidformsubmit");
    this._content.removeEventListener("pageshow", this);
    this._content.removeEventListener("unload", this);
    this._mm = null;
    this._element = null;
    this._content = null;
    this._tab = null;
  },

  /*
   * Events
   */

  handleEvent(aEvent) {
    switch (aEvent.type) {
      case "pageshow":
        if (this._isRootDocumentEvent(aEvent)) {
          this._hidePopup();
        }
        break;
      case "unload":
        this.uninit();
        break;
      case "input":
        this._onInput(aEvent);
        break;
      case "blur":
        this._onBlur(aEvent);
        break;
    }
  },

  /*
   * nsIFormSubmitObserver
   */

  notifyInvalidSubmit(aFormElement, aInvalidElements) {
    // We are going to handle invalid form submission attempt by focusing the
    // first invalid element and show the corresponding validation message in a
    // panel attached to the element.
    if (!aInvalidElements.length) {
      return;
    }

    // Show a validation message on the first focusable element.
    for (let i = 0; i < aInvalidElements.length; i++) {
      // Insure that this is the FormSubmitObserver associated with the
      // element / window this notification is about.
      let element = aInvalidElements.queryElementAt(i, Ci.nsISupports);
      if (this._content != element.ownerGlobal.top.document.defaultView) {
        return;
      }

      if (!(ChromeUtils.getClassName(element) === "HTMLInputElement" ||
            ChromeUtils.getClassName(element) === "HTMLTextAreaElement" ||
            ChromeUtils.getClassName(element) === "HTMLSelectElement" ||
            ChromeUtils.getClassName(element) === "HTMLButtonElement")) {
        continue;
      }

      if (!Services.focus.elementIsFocusable(element, 0)) {
        continue;
      }

      // Update validation message before showing notification
      this._validationMessage = element.validationMessage;

      // Don't connect up to the same element more than once.
      if (this._element == element) {
        this._showPopup(element);
        break;
      }
      this._element = element;

      element.focus();

      // Watch for input changes which may change the validation message.
      element.addEventListener("input", this);

      // Watch for focus changes so we can disconnect our listeners and
      // hide the popup.
      element.addEventListener("blur", this);

      this._showPopup(element);
      break;
    }
  },

  /*
   * Internal
   */

  /*
   * Handles input changes on the form element we've associated a popup
   * with. Updates the validation message or closes the popup if form data
   * becomes valid.
   */
  _onInput(aEvent) {
    let element = aEvent.originalTarget;

    // If the form input is now valid, hide the popup.
    if (element.validity.valid) {
      this._hidePopup();
      return;
    }

    // If the element is still invalid for a new reason, we should update
    // the popup error message.
    if (this._validationMessage != element.validationMessage) {
      this._validationMessage = element.validationMessage;
      this._showPopup(element);
    }
  },

  /*
   * Blur event handler in which we disconnect from the form element and
   * hide the popup.
   */
  _onBlur(aEvent) {
    aEvent.originalTarget.removeEventListener("input", this);
    aEvent.originalTarget.removeEventListener("blur", this);
    this._element = null;
    this._hidePopup();
  },

  /*
   * Send the show popup message to chrome with appropriate position
   * information. Can be called repetitively to update the currently
   * displayed popup position and text.
   */
  _showPopup(aElement) {
    // Collect positional information and show the popup
    let panelData = {};

    panelData.message = this._validationMessage;

    // Note, this is relative to the browser and needs to be translated
    // in chrome.
    panelData.contentRect = BrowserUtils.getElementBoundingRect(aElement);

    // We want to show the popup at the middle of checkbox and radio buttons
    // and where the content begin for the other elements.
    let offset = 0;

    if (aElement.tagName == "INPUT" &&
        (aElement.type == "radio" || aElement.type == "checkbox")) {
      panelData.position = "bottomcenter topleft";
    } else {
      let win = aElement.ownerGlobal;
      let style = win.getComputedStyle(aElement);
      if (style.direction == "rtl") {
        offset = parseInt(style.paddingRight) + parseInt(style.borderRightWidth);
      } else {
        offset = parseInt(style.paddingLeft) + parseInt(style.borderLeftWidth);
      }
      let zoomFactor = this._getWindowUtils().fullZoom;
      panelData.offset = Math.round(offset * zoomFactor);
      panelData.position = "after_start";
    }
    this._mm.sendAsyncMessage("FormValidation:ShowPopup", panelData);
  },

  _hidePopup() {
    this._mm.sendAsyncMessage("FormValidation:HidePopup", {});
  },

  _getWindowUtils() {
    return this._content.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
  },

  _isRootDocumentEvent(aEvent) {
    if (this._content == null) {
      return true;
    }
    let target = aEvent.originalTarget;
    return (target == this._content.document ||
            (target.ownerDocument && target.ownerDocument == this._content.document));
  },

  QueryInterface: XPCOMUtils.generateQI([Ci.nsIFormSubmitObserver])
};
PK
!<N�YYmodules/HeadlessShell.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = ["HeadlessShell"];

ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/osfile.jsm");

// Refrences to the progress listeners to keep them from being gc'ed
// before they are called.
const progressListeners = new Map();

function loadContentWindow(webNavigation, uri) {
  return new Promise((resolve, reject) => {
    webNavigation.loadURI(uri, Ci.nsIWebNavigation.LOAD_FLAGS_NONE, null, null, null);
    let docShell = webNavigation.QueryInterface(Ci.nsIInterfaceRequestor)
                                .getInterface(Ci.nsIDocShell);
    let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                              .getInterface(Ci.nsIWebProgress);
    let progressListener = {
      onLocationChange(progress, request, location, flags) {
        // Ignore inner-frame events
        if (progress != webProgress) {
          return;
        }
        // Ignore events that don't change the document
        if (flags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT) {
          return;
        }
        let contentWindow = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                                    .getInterface(Ci.nsIDOMWindow);
        progressListeners.delete(progressListener);
        webProgress.removeProgressListener(progressListener);
        contentWindow.addEventListener("load", (event) => {
          resolve(contentWindow);
        }, { once: true });
      },
      QueryInterface: XPCOMUtils.generateQI(["nsIWebProgressListener",
                                             "nsISupportsWeakReference"])
    };
    progressListeners.set(progressListener, progressListener);
    webProgress.addProgressListener(progressListener,
                                    Ci.nsIWebProgress.NOTIFY_LOCATION);
  });
}

async function takeScreenshot(fullWidth, fullHeight, contentWidth, contentHeight, path, url) {
  try {
    let windowlessBrowser = Services.appShell.createWindowlessBrowser(false);
    var webNavigation = windowlessBrowser.QueryInterface(Ci.nsIWebNavigation);
    let contentWindow = await loadContentWindow(webNavigation, url);
    contentWindow.resizeTo(contentWidth, contentHeight);

    let canvas = contentWindow.document.createElementNS("http://www.w3.org/1999/xhtml", "html:canvas");
    let context = canvas.getContext("2d");
    let width = fullWidth ? contentWindow.innerWidth + contentWindow.scrollMaxX - contentWindow.scrollMinX
                          : contentWindow.innerWidth;
    let height = fullHeight ? contentWindow.innerHeight + contentWindow.scrollMaxY - contentWindow.scrollMinY
                            : contentWindow.innerHeight;
    canvas.width = width;
    canvas.height = height;
    context.drawWindow(contentWindow, 0, 0, width, height, "rgb(255, 255, 255)");

    function getBlob() {
      return new Promise(resolve => canvas.toBlob(resolve));
    }

    function readBlob(blob) {
      return new Promise(resolve => {
        let reader = new FileReader();
        reader.onloadend = () => resolve(reader);
        reader.readAsArrayBuffer(blob);
      });
    }

    let blob = await getBlob();
    let reader = await readBlob(blob);
    await OS.File.writeAtomic(path, new Uint8Array(reader.result), {flush: true});
    dump("Screenshot saved to: " + path + "\n");
  } catch (e) {
    dump("Failure taking screenshot: " + e + "\n");
  } finally {
    if (webNavigation) {
      webNavigation.close();
    }
  }
}

let HeadlessShell = {
  async handleCmdLineArgs(cmdLine, URLlist) {
    try {
      // Don't quit even though we don't create a window
      Services.startup.enterLastWindowClosingSurvivalArea();

      // Default options
      let fullWidth = true;
      let fullHeight = true;
      // Most common screen resolution of Firefox users
      let contentWidth = 1366;
      let contentHeight = 768;

      // Parse `window-size`
      try {
        var dimensionsStr = cmdLine.handleFlagWithParam("window-size", true);
      } catch (e) {
        dump("expected format: --window-size width[,height]\n");
        return;
      }
      if (dimensionsStr) {
        let success;
        let dimensions = dimensionsStr.split(",", 2);
        if (dimensions.length == 1) {
          success = dimensions[0] > 0;
          if (success) {
            fullWidth = false;
            fullHeight = true;
            contentWidth = dimensions[0];
          }
        } else {
          success = dimensions[0] > 0 && dimensions[1] > 0;
          if (success) {
            fullWidth = false;
            fullHeight = false;
            contentWidth = dimensions[0];
            contentHeight = dimensions[1];
          }
        }

        if (!success) {
          dump("expected format: --window-size width[,height]\n");
          return;
        }
      }

      // Only command line argument left should be `screenshot`
      // There could still be URLs however
      try {
        var path = cmdLine.handleFlagWithParam("screenshot", true);
        if (!cmdLine.length && !URLlist.length) {
          URLlist.push(path); // Assume the user wanted to specify a URL
          path = OS.Path.join(cmdLine.workingDirectory.path, "screenshot.png");
        }
      } catch (e) {
        path = OS.Path.join(cmdLine.workingDirectory.path, "screenshot.png");
        cmdLine.handleFlag("screenshot", true); // Remove `screenshot`
      }

      for (let i = 0; i < cmdLine.length; ++i) {
        URLlist.push(cmdLine.getArgument(i)); // Assume that all remaining arguments are URLs
      }

      if (URLlist.length == 1) {
        await takeScreenshot(fullWidth, fullHeight, contentWidth, contentHeight, path, URLlist[0]);
      } else {
        dump("expected exactly one URL when using `screenshot`\n");
      }
    } finally {
      Services.startup.exitLastWindowClosingSurvivalArea();
      Services.startup.quit(Ci.nsIAppStartup.eForceQuit);
    }
  }
};
PK
!<EZ44modules/LaterRun.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = ["LaterRun"];

ChromeUtils.import("resource://gre/modules/Services.jsm");

const kEnabledPref = "browser.laterrun.enabled";
const kPagePrefRoot = "browser.laterrun.pages.";
// Number of sessions we've been active in
const kSessionCountPref = "browser.laterrun.bookkeeping.sessionCount";
// Time the profile was created at:
const kProfileCreationTime = "browser.laterrun.bookkeeping.profileCreationTime";

// After 50 sessions or 1 month since install, assume we will no longer be
// interested in showing anything to "new" users
const kSelfDestructSessionLimit = 50;
const kSelfDestructHoursLimit = 31 * 24;

class Page {
  constructor({pref, minimumHoursSinceInstall, minimumSessionCount, requireBoth, url}) {
    this.pref = pref;
    this.minimumHoursSinceInstall = minimumHoursSinceInstall || 0;
    this.minimumSessionCount = minimumSessionCount || 1;
    this.requireBoth = requireBoth || false;
    this.url = url;
  }

  get hasRun() {
    return Services.prefs.getBoolPref(this.pref + "hasRun", false);
  }

  applies(sessionInfo) {
    if (this.hasRun) {
      return false;
    }
    if (this.requireBoth) {
      return sessionInfo.sessionCount >= this.minimumSessionCount &&
             sessionInfo.hoursSinceInstall >= this.minimumHoursSinceInstall;
    }
    return sessionInfo.sessionCount >= this.minimumSessionCount ||
           sessionInfo.hoursSinceInstall >= this.minimumHoursSinceInstall;
  }
}

let LaterRun = {
  init() {
    if (!this.enabled) {
      return;
    }
    // If this is the first run, set the time we were installed
    if (Services.prefs.getPrefType(kProfileCreationTime) == Ci.nsIPrefBranch.PREF_INVALID) {
      // We need to store seconds in order to fit within int prefs.
      Services.prefs.setIntPref(kProfileCreationTime, Math.floor(Date.now() / 1000));
    }
    this.sessionCount++;

    if (this.hoursSinceInstall > kSelfDestructHoursLimit ||
        this.sessionCount > kSelfDestructSessionLimit) {
      this.selfDestruct();
    }
  },

  // The enabled, hoursSinceInstall and sessionCount properties mirror the
  // preferences system, and are here for convenience.
  get enabled() {
    return Services.prefs.getBoolPref(kEnabledPref, false);
  },

  set enabled(val) {
    let wasEnabled = this.enabled;
    Services.prefs.setBoolPref(kEnabledPref, val);
    if (val && !wasEnabled) {
      this.init();
    }
  },

  get hoursSinceInstall() {
    let installStamp = Services.prefs.getIntPref(kProfileCreationTime, Date.now() / 1000);
    return Math.floor((Date.now() / 1000 - installStamp) / 3600);
  },

  get sessionCount() {
    if (this._sessionCount) {
      return this._sessionCount;
    }
    return this._sessionCount = Services.prefs.getIntPref(kSessionCountPref, 0);
  },

  set sessionCount(val) {
    this._sessionCount = val;
    Services.prefs.setIntPref(kSessionCountPref, val);
  },

  // Because we don't want to keep incrementing this indefinitely for no reason,
  // we will turn ourselves off after a set amount of time/sessions (see top of
  // file).
  selfDestruct() {
    Services.prefs.setBoolPref(kEnabledPref, false);
  },

  // Create an array of Page objects based on the currently set prefs
  readPages() {
    // Enumerate all the pages.
    let allPrefsForPages = Services.prefs.getChildList(kPagePrefRoot);
    let pageDataStore = new Map();
    for (let pref of allPrefsForPages) {
      let [slug, prop] = pref.substring(kPagePrefRoot.length).split(".");
      if (!pageDataStore.has(slug)) {
        pageDataStore.set(slug, {pref: pref.substring(0, pref.length - prop.length)});
      }
      if (prop == "requireBoth" || prop == "hasRun") {
        pageDataStore.get(slug)[prop] = Services.prefs.getBoolPref(pref, false);
      } else if (prop == "url") {
        pageDataStore.get(slug)[prop] = Services.prefs.getStringPref(pref, "");
      } else {
        pageDataStore.get(slug)[prop] = Services.prefs.getIntPref(pref, 0);
      }
    }
    let rv = [];
    for (let [, pageData] of pageDataStore) {
      if (pageData.url) {
        let uri = null;
        try {
          let urlString = Services.urlFormatter.formatURL(pageData.url.trim());
          uri = Services.io.newURI(urlString);
        } catch (ex) {
          Cu.reportError("Invalid LaterRun page URL " + pageData.url + " ignored.");
          continue;
        }
        if (!uri.schemeIs("https")) {
          Cu.reportError("Insecure LaterRun page URL " + uri.spec + " ignored.");
        } else {
          pageData.url = uri.spec;
          rv.push(new Page(pageData));
        }
      }
    }
    return rv;
  },

  // Return a URL for display as a 'later run' page if its criteria are matched,
  // or null otherwise.
  // NB: will only return one page at a time; if multiple pages match, it's up
  // to the preference service which one gets shown first, and the next one
  // will be shown next startup instead.
  getURL() {
    if (!this.enabled) {
      return null;
    }
    let pages = this.readPages();
    let page = pages.find(p => p.applies(this));
    if (page) {
      Services.prefs.setBoolPref(page.pref + "hasRun", true);
      return page.url;
    }
    return null;
  },
};

LaterRun.init();
PK
!<kC�����modules/MigrationUtils.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = ["MigrationUtils", "MigratorPrototype"];

const TOPIC_WILL_IMPORT_BOOKMARKS = "initial-migration-will-import-default-bookmarks";
const TOPIC_DID_IMPORT_BOOKMARKS = "initial-migration-did-import-default-bookmarks";
const TOPIC_PLACES_DEFAULTS_FINISHED = "places-browser-init-complete";

ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");

Cu.importGlobalProperties(["URL"]);

ChromeUtils.defineModuleGetter(this, "AutoMigrate",
                               "resource:///modules/AutoMigrate.jsm");
ChromeUtils.defineModuleGetter(this, "BookmarkHTMLUtils",
                               "resource://gre/modules/BookmarkHTMLUtils.jsm");
ChromeUtils.defineModuleGetter(this, "LoginHelper",
                               "resource://gre/modules/LoginHelper.jsm");
ChromeUtils.defineModuleGetter(this, "PlacesUtils",
                               "resource://gre/modules/PlacesUtils.jsm");
ChromeUtils.defineModuleGetter(this, "PromiseUtils",
                               "resource://gre/modules/PromiseUtils.jsm");
ChromeUtils.defineModuleGetter(this, "ResponsivenessMonitor",
                               "resource://gre/modules/ResponsivenessMonitor.jsm");
ChromeUtils.defineModuleGetter(this, "Sqlite",
                               "resource://gre/modules/Sqlite.jsm");
ChromeUtils.defineModuleGetter(this, "TelemetryStopwatch",
                               "resource://gre/modules/TelemetryStopwatch.jsm");
ChromeUtils.defineModuleGetter(this, "WindowsRegistry",
                               "resource://gre/modules/WindowsRegistry.jsm");
ChromeUtils.defineModuleGetter(this, "setTimeout",
                               "resource://gre/modules/Timer.jsm");

var gMigrators = null;
var gProfileStartup = null;
var gMigrationBundle = null;
var gPreviousDefaultBrowserKey = "";

let gForceExitSpinResolve = false;
let gKeepUndoData = false;
let gUndoData = null;

XPCOMUtils.defineLazyGetter(this, "gAvailableMigratorKeys", function() {
  if (AppConstants.platform == "win") {
    return [
      "firefox", "edge", "ie", "chrome", "chromium", "360se",
      "canary",
    ];
  }
  if (AppConstants.platform == "macosx") {
    return ["firefox", "safari", "chrome", "chromium", "canary"];
  }
  if (AppConstants.XP_UNIX) {
    return ["firefox", "chrome", "chrome-beta", "chrome-dev", "chromium"];
  }
  return [];
});

function getMigrationBundle() {
  if (!gMigrationBundle) {
    gMigrationBundle = Services.strings.createBundle(
     "chrome://browser/locale/migration/migration.properties");
  }
  return gMigrationBundle;
}

/**
 * Shared prototype for migrators, implementing nsIBrowserProfileMigrator.
 *
 * To implement a migrator:
 * 1. Import this module.
 * 2. Create the prototype for the migrator, extending MigratorPrototype.
 *    Namely: MosaicMigrator.prototype = Object.create(MigratorPrototype);
 * 3. Set classDescription, contractID and classID for your migrator, and set
 *    NSGetFactory appropriately.
 * 4. If the migrator supports multiple profiles, override the sourceProfiles
 *    Here we default for single-profile migrator.
 * 5. Implement getResources(aProfile) (see below).
 * 6. For startup-only migrators, override |startupOnlyMigrator|.
 */
var MigratorPrototype = {
  QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserProfileMigrator]),

  /**
   * OVERRIDE IF AND ONLY IF the source supports multiple profiles.
   *
   * Returns array of profile objects from which data may be imported. The object
   * should have the following keys:
   *   id - a unique string identifier for the profile
   *   name - a pretty name to display to the user in the UI
   *
   * Only profiles from which data can be imported should be listed.  Otherwise
   * the behavior of the migration wizard isn't well-defined.
   *
   * For a single-profile source (e.g. safari, ie), this returns null,
   * and not an empty array.  That is the default implementation.
   */
  getSourceProfiles() {
    return null;
  },

  /**
   * MUST BE OVERRIDDEN.
   *
   * Returns an array of "migration resources" objects for the given profile,
   * or for the "default" profile, if the migrator does not support multiple
   * profiles.
   *
   * Each migration resource should provide:
   * - a |type| getter, returning any of the migration types (see
   *   nsIBrowserProfileMigrator).
   *
   * - a |migrate| method, taking a single argument, aCallback(bool success),
   *   for migrating the data for this resource.  It may do its job
   *   synchronously or asynchronously.  Either way, it must call
   *   aCallback(bool aSuccess) when it's done.  In the case of an exception
   *   thrown from |migrate|, it's taken as if aCallback(false) is called.
   *
   *   Note: In the case of a simple asynchronous implementation, you may find
   *   MigrationUtils.wrapMigrateFunction handy for handling aCallback easily.
   *
   * For each migration type listed in nsIBrowserProfileMigrator, multiple
   * migration resources may be provided.  This practice is useful when the
   * data for a certain migration type is independently stored in few
   * locations.  For example, the mac version of Safari stores its "reading list"
   * bookmarks in a separate property list.
   *
   * Note that the importation of a particular migration type is reported as
   * successful if _any_ of its resources succeeded to import (that is, called,
   * |aCallback(true)|).  However, completion-status for a particular migration
   * type is reported to the UI only once all of its migrators have called
   * aCallback.
   *
   * @note  The returned array should only include resources from which data
   *        can be imported.  So, for example, before adding a resource for the
   *        BOOKMARKS migration type, you should check if you should check that the
   *        bookmarks file exists.
   *
   * @param aProfile
   *        The profile from which data may be imported, or an empty string
   *        in the case of a single-profile migrator.
   *        In the case of multiple-profiles migrator, it is guaranteed that
   *        aProfile is a value returned by the sourceProfiles getter (see
   *        above).
   */
  getResources: function MP_getResources(/* aProfile */) {
    throw new Error("getResources must be overridden");
  },

  /**
   * OVERRIDE in order to provide an estimate of when the last time was
   * that somebody used the browser. It is OK that this is somewhat fuzzy -
   * history may not be available (or be wiped or not present due to e.g.
   * incognito mode).
   *
   * @return a Promise that resolves to the last used date.
   *
   * @note If not overridden, the promise will resolve to the unix epoch.
   */
  getLastUsedDate() {
    return Promise.resolve(new Date(0));
  },

  /**
   * OVERRIDE IF AND ONLY IF the migrator is a startup-only migrator (For now,
   * that is just the Firefox migrator, see bug 737381).  Default: false.
   *
   * Startup-only migrators are different in two ways:
   * - they may only be used during startup.
   * - the user-profile is half baked during migration.  The folder exists,
   *   but it's only accessible through MigrationUtils.profileStartup.
   *   The migrator can call MigrationUtils.profileStartup.doStartup
   *   at any point in order to initialize the profile.
   */
  get startupOnlyMigrator() {
    return false;
  },

  /**
   * Override if the data to migrate is locked/in-use and the user should
   * probably shutdown the source browser.
   */
  get sourceLocked() {
    return false;
  },

  /**
   * DO NOT OVERRIDE - After deCOMing migration, the UI will just call
   * getResources.
   *
   * @see nsIBrowserProfileMigrator
   */
  getMigrateData: async function MP_getMigrateData(aProfile) {
    let resources = await this._getMaybeCachedResources(aProfile);
    if (!resources) {
      return [];
    }
    let types = resources.map(r => r.type);
    return types.reduce((a, b) => { a |= b; return a; }, 0);
  },

  getBrowserKey: function MP_getBrowserKey() {
    return this.contractID.match(/\=([^\=]+)$/)[1];
  },

  /**
   * DO NOT OVERRIDE - After deCOMing migration, the UI will just call
   * migrate for each resource.
   *
   * @see nsIBrowserProfileMigrator
   */
  migrate: async function MP_migrate(aItems, aStartup, aProfile) {
    let resources = await this._getMaybeCachedResources(aProfile);
    if (resources.length == 0)
      throw new Error("migrate called for a non-existent source");

    if (aItems != Ci.nsIBrowserProfileMigrator.ALL)
      resources = resources.filter(r => aItems & r.type);

    // Used to periodically give back control to the main-thread loop.
    let unblockMainThread = function() {
      return new Promise(resolve => {
        Services.tm.dispatchToMainThread(resolve);
      });
    };

    let getHistogramIdForResourceType = (resourceType, template) => {
      if (resourceType == MigrationUtils.resourceTypes.HISTORY) {
        return template.replace("*", "HISTORY");
      }
      if (resourceType == MigrationUtils.resourceTypes.BOOKMARKS) {
        return template.replace("*", "BOOKMARKS");
      }
      if (resourceType == MigrationUtils.resourceTypes.PASSWORDS) {
        return template.replace("*", "LOGINS");
      }
      return null;
    };

    let browserKey = this.getBrowserKey();

    let maybeStartTelemetryStopwatch = resourceType => {
      let histogramId = getHistogramIdForResourceType(resourceType, "FX_MIGRATION_*_IMPORT_MS");
      if (histogramId) {
        TelemetryStopwatch.startKeyed(histogramId, browserKey);
      }
      return histogramId;
    };

    let maybeStartResponsivenessMonitor = resourceType => {
      let responsivenessMonitor;
      let responsivenessHistogramId =
        getHistogramIdForResourceType(resourceType, "FX_MIGRATION_*_JANK_MS");
      if (responsivenessHistogramId) {
        responsivenessMonitor = new ResponsivenessMonitor();
      }
      return {responsivenessMonitor, responsivenessHistogramId};
    };

    let maybeFinishResponsivenessMonitor = (responsivenessMonitor, histogramId) => {
      if (responsivenessMonitor) {
        let accumulatedDelay = responsivenessMonitor.finish();
        if (histogramId) {
          try {
            Services.telemetry.getKeyedHistogramById(histogramId)
                    .add(browserKey, accumulatedDelay);
          } catch (ex) {
            Cu.reportError(histogramId + ": " + ex);
          }
        }
      }
    };

    let collectQuantityTelemetry = () => {
      for (let resourceType of Object.keys(MigrationUtils._importQuantities)) {
        let histogramId =
          "FX_MIGRATION_" + resourceType.toUpperCase() + "_QUANTITY";
        try {
          Services.telemetry.getKeyedHistogramById(histogramId)
                  .add(browserKey, MigrationUtils._importQuantities[resourceType]);
        } catch (ex) {
          Cu.reportError(histogramId + ": " + ex);
        }
      }
    };

    // Called either directly or through the bookmarks import callback.
    let doMigrate = async function() {
      let resourcesGroupedByItems = new Map();
      resources.forEach(function(resource) {
        if (!resourcesGroupedByItems.has(resource.type)) {
          resourcesGroupedByItems.set(resource.type, new Set());
        }
        resourcesGroupedByItems.get(resource.type).add(resource);
      });

      if (resourcesGroupedByItems.size == 0)
        throw new Error("No items to import");

      let notify = function(aMsg, aItemType) {
        Services.obs.notifyObservers(null, aMsg, aItemType);
      };

      for (let resourceType of Object.keys(MigrationUtils._importQuantities)) {
        MigrationUtils._importQuantities[resourceType] = 0;
      }
      notify("Migration:Started");
      for (let [migrationType, itemResources] of resourcesGroupedByItems) {
        notify("Migration:ItemBeforeMigrate", migrationType);

        let stopwatchHistogramId = maybeStartTelemetryStopwatch(migrationType);

        let {responsivenessMonitor, responsivenessHistogramId} =
          maybeStartResponsivenessMonitor(migrationType);

        let itemSuccess = false;
        for (let res of itemResources) {
          let completeDeferred = PromiseUtils.defer();
          let resourceDone = function(aSuccess) {
            itemResources.delete(res);
            itemSuccess |= aSuccess;
            if (itemResources.size == 0) {
              notify(itemSuccess ?
                     "Migration:ItemAfterMigrate" : "Migration:ItemError",
                     migrationType);
              resourcesGroupedByItems.delete(migrationType);

              if (stopwatchHistogramId) {
                TelemetryStopwatch.finishKeyed(stopwatchHistogramId, browserKey);
              }

              maybeFinishResponsivenessMonitor(responsivenessMonitor, responsivenessHistogramId);

              if (resourcesGroupedByItems.size == 0) {
                collectQuantityTelemetry();
                notify("Migration:Ended");
              }
            }
            completeDeferred.resolve();
          };

          // If migrate throws, an error occurred, and the callback
          // (itemMayBeDone) might haven't been called.
          try {
            res.migrate(resourceDone);
          } catch (ex) {
            Cu.reportError(ex);
            resourceDone(false);
          }

          await completeDeferred.promise;
          await unblockMainThread();
        }
      }
    };

    if (MigrationUtils.isStartupMigration && !this.startupOnlyMigrator &&
        Services.policies.isAllowed("defaultBookmarks")) {
      MigrationUtils.profileStartup.doStartup();
      // First import the default bookmarks.
      // Note: We do not need to do so for the Firefox migrator
      // (=startupOnlyMigrator), as it just copies over the places database
      // from another profile.
      (async function() {
        // Tell nsBrowserGlue we're importing default bookmarks.
        let browserGlue = Cc["@mozilla.org/browser/browserglue;1"].
                          getService(Ci.nsIObserver);
        browserGlue.observe(null, TOPIC_WILL_IMPORT_BOOKMARKS, "");

        // Import the default bookmarks. We ignore whether or not we succeed.
        await BookmarkHTMLUtils.importFromURL(
          "chrome://browser/locale/bookmarks.html", true).catch(r => r);

        // We'll tell nsBrowserGlue we've imported bookmarks, but before that
        // we need to make sure we're going to know when it's finished
        // initializing places:
        let placesInitedPromise = new Promise(resolve => {
          let onPlacesInited = function() {
            Services.obs.removeObserver(onPlacesInited, TOPIC_PLACES_DEFAULTS_FINISHED);
            resolve();
          };
          Services.obs.addObserver(onPlacesInited, TOPIC_PLACES_DEFAULTS_FINISHED);
        });
        browserGlue.observe(null, TOPIC_DID_IMPORT_BOOKMARKS, "");
        await placesInitedPromise;
        doMigrate();
      })();
      return;
    }
    doMigrate();
  },

  /**
   * DO NOT OVERRIDE - After deCOMing migration, this code
   * won't be part of the migrator itself.
   *
   * @see nsIBrowserProfileMigrator
   */
  async isSourceAvailable() {
    if (this.startupOnlyMigrator && !MigrationUtils.isStartupMigration)
      return false;

    // For a single-profile source, check if any data is available.
    // For multiple-profiles source, make sure that at least one
    // profile is available.
    let exists = false;
    try {
      let profiles = await this.getSourceProfiles();
      if (!profiles) {
        let resources = await this._getMaybeCachedResources("");
        if (resources && resources.length > 0)
          exists = true;
      } else {
        exists = profiles.length > 0;
      }
    } catch (ex) {
      Cu.reportError(ex);
    }
    return exists;
  },

  /** * PRIVATE STUFF - DO NOT OVERRIDE ***/
  _getMaybeCachedResources: async function PMB__getMaybeCachedResources(aProfile) {
    let profileKey = aProfile ? aProfile.id : "";
    if (this._resourcesByProfile) {
      if (profileKey in this._resourcesByProfile)
        return this._resourcesByProfile[profileKey];
    } else {
      this._resourcesByProfile = { };
    }
    this._resourcesByProfile[profileKey] = await this.getResources(aProfile);
    return this._resourcesByProfile[profileKey];
  },
};

var MigrationUtils = Object.freeze({
  resourceTypes: {
    SETTINGS:   Ci.nsIBrowserProfileMigrator.SETTINGS,
    COOKIES:    Ci.nsIBrowserProfileMigrator.COOKIES,
    HISTORY:    Ci.nsIBrowserProfileMigrator.HISTORY,
    FORMDATA:   Ci.nsIBrowserProfileMigrator.FORMDATA,
    PASSWORDS:  Ci.nsIBrowserProfileMigrator.PASSWORDS,
    BOOKMARKS:  Ci.nsIBrowserProfileMigrator.BOOKMARKS,
    OTHERDATA:  Ci.nsIBrowserProfileMigrator.OTHERDATA,
    SESSION:    Ci.nsIBrowserProfileMigrator.SESSION,
  },

  /**
   * Helper for implementing simple asynchronous cases of migration resources'
   * |migrate(aCallback)| (see MigratorPrototype).  If your |migrate| method
   * just waits for some file to be read, for example, and then migrates
   * everything right away, you can wrap the async-function with this helper
   * and not worry about notifying the callback.
   *
   * For example, instead of writing:
   * setTimeout(function() {
   *   try {
   *     ....
   *     aCallback(true);
   *   }
   *   catch() {
   *     aCallback(false);
   *   }
   * }, 0);
   *
   * You may write:
   * setTimeout(MigrationUtils.wrapMigrateFunction(function() {
   *   if (importingFromMosaic)
   *     throw Cr.NS_ERROR_UNEXPECTED;
   * }, aCallback), 0);
   *
   * ... and aCallback will be called with aSuccess=false when importing
   * from Mosaic, or with aSuccess=true otherwise.
   *
   * @param aFunction
   *        the function that will be called sometime later.  If aFunction
   *        throws when it's called, aCallback(false) is called, otherwise
   *        aCallback(true) is called.
   * @param aCallback
   *        the callback function passed to |migrate|.
   * @return the wrapped function.
   */
  wrapMigrateFunction: function MU_wrapMigrateFunction(aFunction, aCallback) {
    return function() {
      let success = false;
      try {
        aFunction.apply(null, arguments);
        success = true;
      } catch (ex) {
        Cu.reportError(ex);
      }
      // Do not change this to call aCallback directly in try try & catch
      // blocks, because if aCallback throws, we may end up calling aCallback
      // twice.
      aCallback(success);
    };
  },

  /**
   * Gets a string from the migration bundle.  Shorthand for
   * nsIStringBundle.GetStringFromName, if aReplacements isn't passed, or for
   * nsIStringBundle.formatStringFromName if it is.
   *
   * This method also takes care of "bumped" keys (See bug 737381 comment 8 for
   * details).
   *
   * @param aKey
   *        The key of the string to retrieve.
   * @param aReplacements
   *        [optioanl] Array of replacements to run on the retrieved string.
   * @return the retrieved string.
   *
   * @see nsIStringBundle
   */
  getLocalizedString: function MU_getLocalizedString(aKey, aReplacements) {
    aKey = aKey.replace(/_(canary|chromium|chrome-beta|chrome-dev)$/, "_chrome");

    const OVERRIDES = {
      "4_firefox": "4_firefox_history_and_bookmarks",
      "64_firefox": "64_firefox_other",
    };
    aKey = OVERRIDES[aKey] || aKey;

    if (aReplacements === undefined)
      return getMigrationBundle().GetStringFromName(aKey);
    return getMigrationBundle().formatStringFromName(
      aKey, aReplacements, aReplacements.length);
  },

  _getLocalePropertyForBrowser(browserId) {
    switch (browserId) {
      case "edge":
        return "sourceNameEdge";
      case "ie":
        return "sourceNameIE";
      case "safari":
        return "sourceNameSafari";
      case "canary":
        return "sourceNameCanary";
      case "chrome":
        return "sourceNameChrome";
      case "chrome-beta":
        return "sourceNameChromeBeta";
      case "chrome-dev":
        return "sourceNameChromeDev";
      case "chromium":
        return "sourceNameChromium";
      case "firefox":
        return "sourceNameFirefox";
      case "360se":
        return "sourceName360se";
    }
    return null;
  },

  getBrowserName(browserId) {
    let prop = this._getLocalePropertyForBrowser(browserId);
    if (prop) {
      return this.getLocalizedString(prop);
    }
    return null;
  },

  /**
   * Helper for creating a folder for imported bookmarks from a particular
   * migration source.  The folder is created at the end of the given folder.
   *
   * @param sourceNameStr
   *        the source name (first letter capitalized).  This is used
   *        for reading the localized source name from the migration
   *        bundle (e.g. if aSourceNameStr is Mosaic, this will try to read
   *        sourceNameMosaic from the migration bundle).
   * @param parentGuid
   *        the GUID of the folder in which the new folder should be created.
   * @return the GUID of the new folder.
   */
  async createImportedBookmarksFolder(sourceNameStr, parentGuid) {
    let source = this.getLocalizedString("sourceName" + sourceNameStr);
    let title = this.getLocalizedString("importedBookmarksFolder", [source]);
    return (await PlacesUtils.bookmarks.insert({
      type: PlacesUtils.bookmarks.TYPE_FOLDER, parentGuid, title,
    })).guid;
  },

  /**
   * Get all the rows corresponding to a select query from a database, without
   * requiring a lock on the database. If fetching data fails (because someone
   * else tried to write to the DB at the same time, for example), we will
   * retry the fetch after a 100ms timeout, up to 10 times.
   *
   * @param path
   *        the file path to the database we want to open.
   * @param description
   *        a developer-readable string identifying what kind of database we're
   *        trying to open.
   * @param selectQuery
   *        the SELECT query to use to fetch the rows.
   *
   * @return a promise that resolves to an array of rows. The promise will be
   *         rejected if the read/fetch failed even after retrying.
   */
  getRowsFromDBWithoutLocks(path, description, selectQuery) {
    let dbOptions = {
      readOnly: true,
      ignoreLockingMode: true,
      path,
    };

    const RETRYLIMIT = 10;
    const RETRYINTERVAL = 100;
    return (async function innerGetRows() {
      let rows = null;
      for (let retryCount = RETRYLIMIT; retryCount && !rows; retryCount--) {
        // Attempt to get the rows. If this succeeds, we will bail out of the loop,
        // close the database in a failsafe way, and pass the rows back.
        // If fetching the rows throws, we will wait RETRYINTERVAL ms
        // and try again. This will repeat a maximum of RETRYLIMIT times.
        let db;
        let didOpen = false;
        let exceptionSeen;
        try {
          db = await Sqlite.openConnection(dbOptions);
          didOpen = true;
          rows = await db.execute(selectQuery);
        } catch (ex) {
          if (!exceptionSeen) {
            Cu.reportError(ex);
          }
          exceptionSeen = ex;
        } finally {
          try {
            if (didOpen) {
              await db.close();
            }
          } catch (ex) {}
        }
        if (exceptionSeen) {
          await new Promise(resolve => setTimeout(resolve, RETRYINTERVAL));
        }
      }
      if (!rows) {
        throw new Error("Couldn't get rows from the " + description + " database.");
      }
      return rows;
    })();
  },

  get _migrators() {
    if (!gMigrators) {
      gMigrators = new Map();
    }
    return gMigrators;
  },

  forceExitSpinResolve: function MU_forceExitSpinResolve() {
    gForceExitSpinResolve = true;
  },

  spinResolve: function MU_spinResolve(promise) {
    if (!(promise instanceof Promise)) {
      return promise;
    }
    let done = false;
    let result = null;
    let error = null;
    gForceExitSpinResolve = false;
    promise.catch(e => {
      error = e;
    }).then(r => {
      result = r;
      done = true;
    });

    Services.tm.spinEventLoopUntil(() => done || gForceExitSpinResolve);
    if (!done) {
      throw new Error("Forcefully exited event loop.");
    } else if (error) {
      throw error;
    } else {
      return result;
    }
  },

  /*
   * Returns the migrator for the given source, if any data is available
   * for this source, or null otherwise.
   *
   * @param aKey internal name of the migration source.
   *             Supported values: ie (windows),
   *                               edge (windows),
   *                               safari (mac),
   *                               canary (mac/windows),
   *                               chrome (mac/windows/linux),
   *                               chromium (mac/windows/linux),
   *                               360se (windows),
   *                               firefox.
   *
   * If null is returned,  either no data can be imported
   * for the given migrator, or aMigratorKey is invalid  (e.g. ie on mac,
   * or mosaic everywhere).  This method should be used rather than direct
   * getService for future compatibility (see bug 718280).
   *
   * @return profile migrator implementing nsIBrowserProfileMigrator, if it can
   *         import any data, null otherwise.
   */
  getMigrator: async function MU_getMigrator(aKey) {
    let migrator = null;
    if (this._migrators.has(aKey)) {
      migrator = this._migrators.get(aKey);
    } else {
      try {
        migrator = Cc["@mozilla.org/profile/migrator;1?app=browser&type=" +
                      aKey].createInstance(Ci.nsIBrowserProfileMigrator);
      } catch (ex) { Cu.reportError(ex); }
      this._migrators.set(aKey, migrator);
    }

    try {
      return migrator && (await migrator.isSourceAvailable()) ? migrator : null;
    } catch (ex) { Cu.reportError(ex); return null; }
  },

  /**
   * Figure out what is the default browser, and if there is a migrator
   * for it, return that migrator's internal name.
   * For the time being, the "internal name" of a migrator is its contract-id
   * trailer (e.g. ie for @mozilla.org/profile/migrator;1?app=browser&type=ie),
   * but it will soon be exposed properly.
   */
  getMigratorKeyForDefaultBrowser() {
    // Canary uses the same description as Chrome so we can't distinguish them.
    const APP_DESC_TO_KEY = {
      "Internet Explorer":                 "ie",
      "Microsoft Edge":                    "edge",
      "Safari":                            "safari",
      "Firefox":                           "firefox",
      "Nightly":                           "firefox",
      "Google Chrome":                     "chrome",  // Windows, Linux
      "Chrome":                            "chrome",  // OS X
      "Chromium":                          "chromium", // Windows, OS X
      "Chromium Web Browser":              "chromium", // Linux
      "360\u5b89\u5168\u6d4f\u89c8\u5668": "360se",
    };

    let key = "";
    try {
      let browserDesc =
        Cc["@mozilla.org/uriloader/external-protocol-service;1"]
          .getService(Ci.nsIExternalProtocolService)
          .getApplicationDescription("http");
      key = APP_DESC_TO_KEY[browserDesc] || "";
      // Handle devedition, as well as "FirefoxNightly" on OS X.
      if (!key && browserDesc.startsWith("Firefox")) {
        key = "firefox";
      }
    } catch (ex) {
      Cu.reportError("Could not detect default browser: " + ex);
    }

    // "firefox" is the least useful entry here, and might just be because we've set
    // ourselves as the default (on Windows 7 and below). In that case, check if we
    // have a registry key that tells us where to go:
    if (key == "firefox" && AppConstants.isPlatformAndVersionAtMost("win", "6.2")) {
      // Because we remove the registry key, reading the registry key only works once.
      // We save the value for subsequent calls to avoid hard-to-trace bugs when multiple
      // consumers ask for this key.
      if (gPreviousDefaultBrowserKey) {
        key = gPreviousDefaultBrowserKey;
      } else {
        // We didn't have a saved value, so check the registry.
        const kRegPath = "Software\\Mozilla\\Firefox";
        let oldDefault = WindowsRegistry.readRegKey(
            Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, kRegPath, "OldDefaultBrowserCommand");
        if (oldDefault) {
          // Remove the key:
          WindowsRegistry.removeRegKey(
            Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, kRegPath, "OldDefaultBrowserCommand");
          try {
            let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFileWin);
            file.initWithCommandLine(oldDefault);
            key = APP_DESC_TO_KEY[file.getVersionInfoField("FileDescription")] || key;
            // Save the value for future callers.
            gPreviousDefaultBrowserKey = key;
          } catch (ex) {
            Cu.reportError("Could not convert old default browser value to description.");
          }
        }
      }
    }
    return key;
  },

  // Whether or not we're in the process of startup migration
  get isStartupMigration() {
    return gProfileStartup != null;
  },

  /**
   * In the case of startup migration, this is set to the nsIProfileStartup
   * instance passed to ProfileMigrator's migrate.
   *
   * @see showMigrationWizard
   */
  get profileStartup() {
    return gProfileStartup;
  },

  /**
   * Show the migration wizard.  On mac, this may just focus the wizard if it's
   * already running, in which case aOpener and aParams are ignored.
   *
   * @param {Window} [aOpener]
   *        optional; the window that asks to open the wizard.
   * @param {Array} [aParams]
   *        optional arguments for the migration wizard, in the form of an array
   *        This is passed as-is for the params argument of
   *        nsIWindowWatcher.openWindow. The array elements we expect are, in
   *        order:
   *        - {Number} migration entry point constant (see below)
   *        - {String} source browser identifier
   *        - {nsIBrowserProfileMigrator} actual migrator object
   *        - {Boolean} whether this is a startup migration
   *        - {Boolean} whether to skip the 'source' page
   *        - {String} an identifier for the profile to use when migrating
   *        NB: If you add new consumers, please add a migration entry point
   *        constant below, and specify at least the first element of the array
   *        (the migration entry point for purposes of telemetry).
   */
  showMigrationWizard:
  function MU_showMigrationWizard(aOpener, aParams) {
    let features = "chrome,dialog,modal,centerscreen,titlebar,resizable=no";
    if (AppConstants.platform == "macosx" && !this.isStartupMigration) {
      let win = Services.wm.getMostRecentWindow("Browser:MigrationWizard");
      if (win) {
        win.focus();
        return;
      }
      // On mac, the migration wiazrd should only be modal in the case of
      // startup-migration.
      features = "centerscreen,chrome,resizable=no";
    }

    // nsIWindowWatcher doesn't deal with raw arrays, so we convert the input
    let params;
    if (Array.isArray(aParams)) {
      params = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
      for (let item of aParams) {
        let comtaminatedVal;
        if (item && item instanceof Ci.nsISupports) {
          comtaminatedVal = item;
        } else {
          switch (typeof item) {
            case "boolean":
              comtaminatedVal = Cc["@mozilla.org/supports-PRBool;1"].
                                createInstance(Ci.nsISupportsPRBool);
              comtaminatedVal.data = item;
              break;
            case "number":
              comtaminatedVal = Cc["@mozilla.org/supports-PRUint32;1"].
                                createInstance(Ci.nsISupportsPRUint32);
              comtaminatedVal.data = item;
              break;
            case "string":
              comtaminatedVal = Cc["@mozilla.org/supports-cstring;1"].
                                createInstance(Ci.nsISupportsCString);
              comtaminatedVal.data = item;
              break;

            case "undefined":
            case "object":
              if (!item) {
                comtaminatedVal = null;
                break;
              }
              /* intentionally falling through to error out here for
                 non-null/undefined things: */
            default:
              throw new Error("Unexpected parameter type " + (typeof item) + ": " + item);
          }
        }
        params.appendElement(comtaminatedVal);
      }
    } else {
      params = aParams;
    }

    Services.ww.openWindow(aOpener,
                           "chrome://browser/content/migration/migration.xul",
                           "_blank",
                           features,
                           params);
  },

  /**
   * Show the migration wizard for startup-migration.  This should only be
   * called by ProfileMigrator (see ProfileMigrator.js), which implements
   * nsIProfileMigrator. This runs asynchronously if we are running an
   * automigration.
   *
   * @param aProfileStartup
   *        the nsIProfileStartup instance provided to ProfileMigrator.migrate.
   * @param [optional] aMigratorKey
   *        If set, the migration wizard will import from the corresponding
   *        migrator, bypassing the source-selection page.  Otherwise, the
   *        source-selection page will be displayed, either with the default
   *        browser selected, if it could be detected and if there is a
   *        migrator for it, or with the first option selected as a fallback
   *        (The first option is hardcoded to be the most common browser for
   *         the OS we run on.  See migration.xul).
   * @param [optional] aProfileToMigrate
   *        If set, the migration wizard will import from the profile indicated.
   * @throws if aMigratorKey is invalid or if it points to a non-existent
   *         source.
   */
  startupMigration:
  function MU_startupMigrator(aProfileStartup, aMigratorKey, aProfileToMigrate) {
    if (Services.prefs.getBoolPref("browser.migrate.automigrate.enabled", false)) {
      this.asyncStartupMigration(aProfileStartup,
                                 aMigratorKey,
                                 aProfileToMigrate);
    } else {
      this.spinResolve(this.asyncStartupMigration(aProfileStartup,
                                                  aMigratorKey,
                                                  aProfileToMigrate));
    }
  },

  asyncStartupMigration:
  async function MU_asyncStartupMigrator(aProfileStartup, aMigratorKey, aProfileToMigrate) {
    if (!aProfileStartup) {
      throw new Error("an profile-startup instance is required for startup-migration");
    }
    gProfileStartup = aProfileStartup;

    let skipSourcePage = false, migrator = null, migratorKey = "";
    if (aMigratorKey) {
      migrator = await this.getMigrator(aMigratorKey);
      if (!migrator) {
        // aMigratorKey must point to a valid source, so, if it doesn't
        // cleanup and throw.
        this.finishMigration();
        throw new Error("startMigration was asked to open auto-migrate from " +
                        "a non-existent source: " + aMigratorKey);
      }
      migratorKey = aMigratorKey;
      skipSourcePage = true;
    } else {
      let defaultBrowserKey = this.getMigratorKeyForDefaultBrowser();
      if (defaultBrowserKey) {
        migrator = await this.getMigrator(defaultBrowserKey);
        if (migrator)
          migratorKey = defaultBrowserKey;
      }
    }

    if (!migrator) {
      let migrators = await Promise.all(gAvailableMigratorKeys.map(key => this.getMigrator(key)));
      // If there's no migrator set so far, ensure that there is at least one
      // migrator available before opening the wizard.
      // Note that we don't need to check the default browser first, because
      // if that one existed we would have used it in the block above this one.
      if (!migrators.some(m => m)) {
        // None of the keys produced a usable migrator, so finish up here:
        this.finishMigration();
        return;
      }
    }

    let isRefresh = migrator && skipSourcePage &&
                    migratorKey == AppConstants.MOZ_APP_NAME;

    if (!isRefresh && AutoMigrate.enabled) {
      try {
        await AutoMigrate.migrate(aProfileStartup, migratorKey, aProfileToMigrate);
        return;
      } catch (ex) {
        // If automigration failed, continue and show the dialog.
        Cu.reportError(ex);
      }
    }

    let migrationEntryPoint = this.MIGRATION_ENTRYPOINT_FIRSTRUN;
    if (isRefresh) {
      migrationEntryPoint = this.MIGRATION_ENTRYPOINT_FXREFRESH;
    }

    let params = [
      migrationEntryPoint,
      migratorKey,
      migrator,
      aProfileStartup,
      skipSourcePage,
      aProfileToMigrate,
    ];
    this.showMigrationWizard(null, params);
  },

  _importQuantities: {
    bookmarks: 0,
    logins: 0,
    history: 0,
  },

  insertBookmarkWrapper(bookmark) {
    this._importQuantities.bookmarks++;
    let insertionPromise = PlacesUtils.bookmarks.insert(bookmark);
    if (!gKeepUndoData) {
      return insertionPromise;
    }
    // If we keep undo data, add a promise handler that stores the undo data once
    // the bookmark has been inserted in the DB, and then returns the bookmark.
    let {parentGuid} = bookmark;
    return insertionPromise.then(bm => {
      let {guid, lastModified, type} = bm;
      gUndoData.get("bookmarks").push({
        parentGuid, guid, lastModified, type,
      });
      return bm;
    });
  },

  insertManyBookmarksWrapper(bookmarks, parent) {
    let insertionPromise = PlacesUtils.bookmarks.insertTree({guid: parent, children: bookmarks});
    return insertionPromise.then(insertedItems => {
      this._importQuantities.bookmarks += insertedItems.length;
      if (gKeepUndoData) {
        let bmData = gUndoData.get("bookmarks");
        for (let bm of insertedItems) {
          let {parentGuid, guid, lastModified, type} = bm;
          bmData.push({parentGuid, guid, lastModified, type});
        }
      }
    }, ex => Cu.reportError(ex));
  },

  insertVisitsWrapper(places, options) {
    this._importQuantities.history += places.length;
    if (gKeepUndoData) {
      this._updateHistoryUndo(places);
    }
    return PlacesUtils.asyncHistory.updatePlaces(places, options, true);
  },

  async insertLoginsWrapper(logins) {
    this._importQuantities.logins += logins.length;
    let inserted = await LoginHelper.maybeImportLogins(logins);
    // Note that this means that if we import a login that has a newer password
    // than we know about, we will update the login, and an undo of the import
    // will not revert this. This seems preferable over removing the login
    // outright or storing the old password in the undo file.
    if (gKeepUndoData) {
      for (let {guid, timePasswordChanged} of inserted) {
        gUndoData.get("logins").push({guid, timePasswordChanged});
      }
    }
  },

  initializeUndoData() {
    gKeepUndoData = true;
    gUndoData = new Map([["bookmarks", []], ["visits", []], ["logins", []]]);
  },

  async _postProcessUndoData(state) {
    if (!state) {
      return state;
    }
    let bookmarkFolders = state.get("bookmarks").filter(b => b.type == PlacesUtils.bookmarks.TYPE_FOLDER);

    let bookmarkFolderData = [];
    let bmPromises = bookmarkFolders.map(({guid}) => {
      // Ignore bookmarks where the promise doesn't resolve (ie that are missing)
      // Also check that the bookmark fetch returns isn't null before adding it.
      return PlacesUtils.bookmarks.fetch(guid).then(bm => bm && bookmarkFolderData.push(bm), () => {});
    });

    await Promise.all(bmPromises);
    let folderLMMap = new Map(bookmarkFolderData.map(b => [b.guid, b.lastModified]));
    for (let bookmark of bookmarkFolders) {
      let lastModified = folderLMMap.get(bookmark.guid);
      // If the bookmark was deleted, the map will be returning null, so check:
      if (lastModified) {
        bookmark.lastModified = lastModified;
      }
    }
    return state;
  },

  stopAndRetrieveUndoData() {
    let undoData = gUndoData;
    gUndoData = null;
    gKeepUndoData = false;
    return this._postProcessUndoData(undoData);
  },

  _updateHistoryUndo(places) {
    let visits = gUndoData.get("visits");
    let visitMap = new Map(visits.map(v => [v.url, v]));
    for (let place of places) {
      let visitCount = place.visits.length;
      let first, last;
      if (visitCount > 1) {
        let visitDates = place.visits.map(v => v.visitDate);
        first = Math.min.apply(Math, visitDates);
        last = Math.max.apply(Math, visitDates);
      } else {
        first = last = place.visits[0].visitDate;
      }
      let url = place.uri.spec;
      try {
        new URL(url);
      } catch (ex) {
        // This won't save and we won't need to 'undo' it, so ignore this URL.
        continue;
      }
      if (!visitMap.has(url)) {
        visitMap.set(url, {url, visitCount, first, last});
      } else {
        let currentData = visitMap.get(url);
        currentData.visitCount += visitCount;
        currentData.first = Math.min(currentData.first, first);
        currentData.last = Math.max(currentData.last, last);
      }
    }
    gUndoData.set("visits", Array.from(visitMap.values()));
  },

  /**
   * Cleans up references to migrators and nsIProfileInstance instances.
   */
  finishMigration: function MU_finishMigration() {
    gMigrators = null;
    gProfileStartup = null;
    gMigrationBundle = null;
  },

  gAvailableMigratorKeys,

  MIGRATION_ENTRYPOINT_UNKNOWN: 0,
  MIGRATION_ENTRYPOINT_FIRSTRUN: 1,
  MIGRATION_ENTRYPOINT_FXREFRESH: 2,
  MIGRATION_ENTRYPOINT_PLACES: 3,
  MIGRATION_ENTRYPOINT_PASSWORDS: 4,
  MIGRATION_ENTRYPOINT_NEWTAB: 5,

  _sourceNameToIdMapping: {
    "nothing":    1,
    "firefox":    2,
    "edge":       3,
    "ie":         4,
    "chrome":     5,
    "chromium":   6,
    "canary":     7,
    "safari":     8,
    "360se":      9,
  },
  getSourceIdForTelemetry(sourceName) {
    return this._sourceNameToIdMapping[sourceName] || 0;
  },
});
PK
!<��H�
�
modules/OpenInTabsUtils.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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.EXPORTED_SYMBOLS = ["OpenInTabsUtils"];

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");

XPCOMUtils.defineLazyGetter(this, "bundle", function() {
  return Services.strings.createBundle("chrome://browser/locale/tabbrowser.properties");
});

/**
 * Utility functions that can be used when opening multiple tabs, that can be
 * called without any tabbrowser instance.
 */
this.OpenInTabsUtils = {
  getString(key) {
    return bundle.GetStringFromName(key);
  },

  getFormattedString(key, params) {
    return bundle.formatStringFromName(key, params, params.length);
  },

  /**
   * Gives the user a chance to cancel loading lots of tabs at once.
   */
  confirmOpenInTabs(numTabsToOpen, aWindow) {
    const WARN_ON_OPEN_PREF = "browser.tabs.warnOnOpen";
    const MAX_OPNE_PREF = "browser.tabs.maxOpenBeforeWarn";
    if (!Services.prefs.getBoolPref(WARN_ON_OPEN_PREF)) {
      return true;
    }
    if (numTabsToOpen < Services.prefs.getIntPref(MAX_OPNE_PREF)) {
      return true;
    }

    // default to true: if it were false, we wouldn't get this far
    let warnOnOpen = { value: true };

    const messageKey = "tabs.openWarningMultipleBranded";
    const openKey = "tabs.openButtonMultiple";
    const BRANDING_BUNDLE_URI = "chrome://branding/locale/brand.properties";
    let brandShortName = Services.strings.createBundle(BRANDING_BUNDLE_URI).
                         GetStringFromName("brandShortName");

    let buttonPressed = Services.prompt.confirmEx(
      aWindow,
      this.getString("tabs.openWarningTitle"),
      this.getFormattedString(messageKey, [numTabsToOpen, brandShortName]),
      (Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0) +
        (Services.prompt.BUTTON_TITLE_CANCEL * Services.prompt.BUTTON_POS_1),
      this.getString(openKey), null, null,
      this.getFormattedString("tabs.openWarningPromptMeBranded",
                              [brandShortName]),
      warnOnOpen
    );

    let reallyOpen = (buttonPressed == 0);
    // don't set the pref unless they press OK and it's false
    if (reallyOpen && !warnOnOpen.value) {
      Services.prefs.setBoolPref(WARN_ON_OPEN_PREF, false);
    }

    return reallyOpen;
  },

  /*
   * Async version of confirmOpenInTabs.
   */
  promiseConfirmOpenInTabs(numTabsToOpen, aWindow) {
    return new Promise(resolve => {
      Services.tm.dispatchToMainThread(() => {
        resolve(this.confirmOpenInTabs(numTabsToOpen, aWindow));
      });
    });
  }
};
PK
!<�XQ�w�w�modules/PageActions.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = [
  "PageActions",
  // PageActions.Action
  // PageActions.Button
  // PageActions.Subview
  // PageActions.ACTION_ID_BOOKMARK
  // PageActions.ACTION_ID_BOOKMARK_SEPARATOR
  // PageActions.ACTION_ID_BUILT_IN_SEPARATOR
];

ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");

ChromeUtils.defineModuleGetter(this, "AppConstants",
  "resource://gre/modules/AppConstants.jsm");
ChromeUtils.defineModuleGetter(this, "AsyncShutdown",
  "resource://gre/modules/AsyncShutdown.jsm");
ChromeUtils.defineModuleGetter(this, "BinarySearch",
  "resource://gre/modules/BinarySearch.jsm");


const ACTION_ID_BOOKMARK = "bookmark";
const ACTION_ID_BOOKMARK_SEPARATOR = "bookmarkSeparator";
const ACTION_ID_BUILT_IN_SEPARATOR = "builtInSeparator";

const PREF_PERSISTED_ACTIONS = "browser.pageActions.persistedActions";
const PERSISTED_ACTIONS_CURRENT_VERSION = 1;


var PageActions = {
  /**
   * Inits.  Call to init.
   */
  init() {
    let callbacks = this._deferredAddActionCalls;
    delete this._deferredAddActionCalls;

    this._loadPersistedActions();

    // Register the built-in actions, which are defined below in this file.
    for (let options of gBuiltInActions) {
      if (!this.actionForID(options.id)) {
        this._registerAction(new Action(options));
      }
    }

    // Now place them all in each window.  Instead of splitting the register and
    // place steps, we could simply call addAction, which does both, but doing
    // it this way means that all windows initially place their actions the same
    // way -- placeAllActions -- regardless of whether they're open when this
    // method is called or opened later.
    for (let bpa of allBrowserPageActions()) {
      bpa.placeAllActions();
    }

    // These callbacks are deferred until init happens and all built-in actions
    // are added.
    while (callbacks && callbacks.length) {
      callbacks.shift()();
    }

    // Purge removed actions from persisted state on shutdown.  The point is not
    // to do it on Action.remove().  That way actions that are removed and
    // re-added while the app is running will have their urlbar placement and
    // other state remembered and restored.  This happens for upgraded and
    // downgraded extensions, for example.
    AsyncShutdown.profileBeforeChange.addBlocker(
      "PageActions: purging unregistered actions from cache",
      () => this._purgeUnregisteredPersistedActions(),
    );
  },

  _deferredAddActionCalls: [],

  /**
   * The list of Action objects, sorted in the order in which they should be
   * placed in the page action panel.  If there are both built-in and non-built-
   * in actions, then the list will include the separator between the two.  The
   * list is not live.  (array of Action objects)
   */
  get actions() {
    let actions = this.builtInActions;
    if (this.nonBuiltInActions.length) {
      // There are non-built-in actions, so include them too.
      if (actions.length) {
        // There are both built-in and non-built-in actions.  Add a separator
        // between the two groups so that the returned array looks like:
        // [...built-ins, separator, ...non-built-ins]
        actions.push(new Action({
          id: ACTION_ID_BUILT_IN_SEPARATOR,
          _isSeparator: true,
        }));
      }
      actions.push(...this.nonBuiltInActions);
    }
    return actions;
  },

  /**
   * The list of built-in actions.  Not live.  (array of Action objects)
   */
  get builtInActions() {
    return this._builtInActions.slice();
  },

  /**
   * The list of non-built-in actions.  Not live.  (array of Action objects)
   */
  get nonBuiltInActions() {
    return this._nonBuiltInActions.slice();
  },

  /**
   * The list of actions currently in the urlbar, sorted in the order in which
   * they appear.  Not live.
   *
   * @param  browserWindow (DOM window, required)
   *         This window's actions will be returned.
   * @return (array of PageAction.Action objects) The actions currently in the
   *         given window's urlbar.
   */
  actionsInUrlbar(browserWindow) {
    // Remember that IDs in idsInUrlbar may belong to actions that aren't
    // currently registered.
    return this._persistedActions.idsInUrlbar.reduce((actions, id) => {
      let action = this.actionForID(id);
      if (action && action.shouldShowInUrlbar(browserWindow)) {
        actions.push(action);
      }
      return actions;
    }, []);
  },

  /**
   * Gets an action.
   *
   * @param  id (string, required)
   *         The ID of the action to get.
   * @return The Action object, or null if none.
   */
  actionForID(id) {
    return this._actionsByID.get(id);
  },

  /**
   * Registers an action.
   *
   * Actions are registered by their IDs.  An error is thrown if an action with
   * the given ID has already been added.  Use actionForID() before calling this
   * method if necessary.
   *
   * Be sure to call remove() on the action if the lifetime of the code that
   * owns it is shorter than the browser's -- if it lives in an extension, for
   * example.
   *
   * @param  action (Action, required)
   *         The Action object to register.
   * @return The given Action.
   */
  addAction(action) {
    if (this._deferredAddActionCalls) {
      // init() hasn't been called yet.  Defer all additions until it's called,
      // at which time _deferredAddActionCalls will be deleted.
      this._deferredAddActionCalls.push(() => this.addAction(action));
      return action;
    }

    let hadSep = this.actions.some(a => a.id == ACTION_ID_BUILT_IN_SEPARATOR);

    this._registerAction(action);

    let sep = null;
    if (!hadSep) {
      sep = this.actions.find(a => a.id == ACTION_ID_BUILT_IN_SEPARATOR);
    }

    for (let bpa of allBrowserPageActions()) {
      if (sep) {
        // There are now both built-in and non-built-in actions, so place the
        // separator between the two groups.
        bpa.placeAction(sep);
      }
      bpa.placeAction(action);
    }

    return action;
  },

  _registerAction(action) {
    if (this.actionForID(action.id)) {
      throw new Error(`Action with ID '${action.id}' already added`);
    }
    this._actionsByID.set(action.id, action);

    // Insert the action into the appropriate list, either _builtInActions or
    // _nonBuiltInActions.

    // Keep in mind that _insertBeforeActionID may be present but null, which
    // means the action should be appended to the built-ins.
    if ("__insertBeforeActionID" in action) {
      // A "semi-built-in" action, probably an action from an extension
      // bundled with the browser.  Right now we simply assume that no other
      // consumers will use _insertBeforeActionID.
      let index =
        !action.__insertBeforeActionID ? -1 :
        this._builtInActions.findIndex(a => {
          return a.id == action.__insertBeforeActionID;
        });
      if (index < 0) {
        // Append the action.
        index = this._builtInActions.length;
      }
      this._builtInActions.splice(index, 0, action);
    } else if (gBuiltInActions.find(a => a.id == action.id)) {
      // A built-in action.  These are always added on init before all other
      // actions, one after the other, so just push onto the array.
      this._builtInActions.push(action);
    } else {
      // A non-built-in action, like a non-bundled extension potentially.
      // Keep this list sorted by title.
      let index = BinarySearch.insertionIndexOf((a1, a2) => {
        return a1.getTitle().localeCompare(a2.getTitle());
      }, this._nonBuiltInActions, action);
      this._nonBuiltInActions.splice(index, 0, action);
    }

    if (this._persistedActions.ids.includes(action.id)) {
      // The action has been seen before.  Override its pinnedToUrlbar value
      // with the persisted value.  Set the private version of that property
      // so that onActionToggledPinnedToUrlbar isn't called, which happens when
      // the public version is set.
      action._pinnedToUrlbar =
        this._persistedActions.idsInUrlbar.includes(action.id);
    } else {
      // The action is new.  Store it in the persisted actions.
      this._persistedActions.ids.push(action.id);
      this._updateIDsPinnedToUrlbarForAction(action);
    }
  },

  _updateIDsPinnedToUrlbarForAction(action) {
    let index = this._persistedActions.idsInUrlbar.indexOf(action.id);
    if (action.pinnedToUrlbar) {
      if (index < 0) {
        index = action.id == ACTION_ID_BOOKMARK ? -1 :
                this._persistedActions.idsInUrlbar.indexOf(ACTION_ID_BOOKMARK);
        if (index < 0) {
          index = this._persistedActions.idsInUrlbar.length;
        }
        this._persistedActions.idsInUrlbar.splice(index, 0, action.id);
      }
    } else if (index >= 0) {
      this._persistedActions.idsInUrlbar.splice(index, 1);
    }
    this._storePersistedActions();
  },

  // These keep track of currently registered actions.
  _builtInActions: [],
  _nonBuiltInActions: [],
  _actionsByID: new Map(),

  /**
   * Returns the ID of the action before which the given action should be
   * inserted in the urlbar.
   *
   * @param  action (Action object, required)
   *         The action you're inserting.
   * @return The ID of the reference action, or null if your action should be
   *         appended.
   */
  nextActionIDInUrlbar(browserWindow, action) {
    // Actions in the urlbar are always inserted before the bookmark action,
    // which always comes last if it's present.
    if (action.id == ACTION_ID_BOOKMARK) {
      return null;
    }
    let id = this._nextActionID(action, this.actionsInUrlbar(browserWindow));
    return id || ACTION_ID_BOOKMARK;
  },

  /**
   * Returns the ID of the action before which the given action should be
   * inserted in the panel.
   *
   * @param  action (Action object, required)
   *         The action you're inserting.
   * @return The ID of the reference action, or null if your action should be
   *         appended.
   */
  nextActionIDInPanel(action) {
    return this._nextActionID(action, this.actions);
  },

  /**
   * The DOM nodes of actions should be ordered properly, both in the panel and
   * the urlbar.  This method returns the ID of the action that comes after the
   * given action in the given array.  You can use the returned ID to get a DOM
   * node ID to pass to node.insertBefore().
   *
   * Pass PageActions.actions to get the ID of the next action in the panel.
   * Pass PageActions.actionsInUrlbar to get the ID of the next action in the
   * urlbar.
   *
   * @param  action
   *         The action whose node you want to insert into your DOM.
   * @param  actionArray
   *         The relevant array of actions, either PageActions.actions or
   *         actionsInUrlbar.
   * @return The ID of the action before which the given action should be
   *         inserted.  If the given action should be inserted last, returns
   *         null.
   */
  _nextActionID(action, actionArray) {
    let index = actionArray.findIndex(a => a.id == action.id);
    if (index < 0) {
      return null;
    }
    let nextAction = actionArray[index + 1];
    if (!nextAction) {
      return null;
    }
    return nextAction.id;
  },

  /**
   * Call this when an action is removed.
   *
   * @param  action (Action object, required)
   *         The action that was removed.
   */
  onActionRemoved(action) {
    if (!this.actionForID(action.id)) {
      // The action isn't registered (yet).  Not an error.
      return;
    }

    this._actionsByID.delete(action.id);
    for (let list of [this._nonBuiltInActions, this._builtInActions]) {
      let index = list.findIndex(a => a.id == action.id);
      if (index >= 0) {
        list.splice(index, 1);
        break;
      }
    }

    for (let bpa of allBrowserPageActions()) {
      bpa.removeAction(action);
    }
  },

  /**
   * Call this when an action's pinnedToUrlbar property changes.
   *
   * @param  action (Action object, required)
   *         The action whose pinnedToUrlbar property changed.
   */
  onActionToggledPinnedToUrlbar(action) {
    if (!this.actionForID(action.id)) {
      // This may be called before the action has been added.
      return;
    }
    this._updateIDsPinnedToUrlbarForAction(action);
    for (let bpa of allBrowserPageActions()) {
      bpa.placeActionInUrlbar(action);
    }
  },

  logTelemetry(type, action, node = null) {
    if (type == "used") {
      type =
        node && node.closest("#urlbar-container") ? "urlbar_used" :
        "panel_used";
    }
    let histogramID = "FX_PAGE_ACTION_" + type.toUpperCase();
    try {
      let histogram = Services.telemetry.getHistogramById(histogramID);
      if (action._isBuiltIn) {
        histogram.add(action.labelForHistogram);
      } else {
        histogram.add("other");
      }
    } catch (ex) {
      Cu.reportError(ex);
    }
  },

  // For tests.  See Bug 1413692.
  _reset() {
    PageActions._purgeUnregisteredPersistedActions();
    PageActions._builtInActions = [];
    PageActions._nonBuiltInActions = [];
    PageActions._actionsByID = new Map();
  },

  _storePersistedActions() {
    let json = JSON.stringify(this._persistedActions);
    Services.prefs.setStringPref(PREF_PERSISTED_ACTIONS, json);
  },

  _loadPersistedActions() {
    try {
      let json = Services.prefs.getStringPref(PREF_PERSISTED_ACTIONS);
      this._persistedActions = this._migratePersistedActions(JSON.parse(json));
    } catch (ex) {}
  },

  _purgeUnregisteredPersistedActions() {
    // Remove all action IDs from persisted state that do not correspond to
    // currently registered actions.
    for (let name of ["ids", "idsInUrlbar"]) {
      this._persistedActions[name] = this._persistedActions[name].filter(id => {
        return this.actionForID(id);
      });
    }
    this._storePersistedActions();
  },

  _migratePersistedActions(actions) {
    // Start with actions.version and migrate one version at a time, all the way
    // up to the current version.
    for (let version = actions.version || 0;
         version < PERSISTED_ACTIONS_CURRENT_VERSION;
         version++) {
      let methodName = `_migratePersistedActionsTo${version + 1}`;
      actions = this[methodName](actions);
      actions.version = version + 1;
    }
    return actions;
  },

  _migratePersistedActionsTo1(actions) {
    // The `ids` object is a mapping: action ID => true.  Convert it to an array
    // to save space in the prefs.
    let ids = [];
    for (let id in actions.ids) {
      ids.push(id);
    }
    // Move the bookmark ID to the end of idsInUrlbar.  The bookmark action
    // should always remain at the end of the urlbar, if present.
    let bookmarkIndex = actions.idsInUrlbar.indexOf(ACTION_ID_BOOKMARK);
    if (bookmarkIndex >= 0) {
      actions.idsInUrlbar.splice(bookmarkIndex, 1);
      actions.idsInUrlbar.push(ACTION_ID_BOOKMARK);
    }
    return {
      ids,
      idsInUrlbar: actions.idsInUrlbar,
    };
  },

  // This keeps track of all actions, even those that are not currently
  // registered because they have been removed, so long as
  // _purgeUnregisteredPersistedActions has not been called.
  _persistedActions: {
    version: PERSISTED_ACTIONS_CURRENT_VERSION,
    // action IDs that have ever been seen and not removed, order not important
    ids: [],
    // action IDs ordered by position in urlbar
    idsInUrlbar: [],
  },
};

/**
 * A single page action.
 *
 * Each action can have both per-browser-window state and global state.
 * Per-window state takes precedence over global state.  This is reflected in
 * the title, tooltip, disabled, and icon properties.  Each of these properties
 * has a getter method and setter method that takes a browser window.  Pass null
 * to get the action's global state.  Pass a browser window to get the per-
 * window state.  However, if you pass a window and the action has no state for
 * that window, then the global state will be returned.
 *
 * `options` is a required object with the following properties.  Regarding the
 * properties discussed in the previous paragraph, the values in `options` set
 * global state.
 *
 * @param id (string, required)
 *        The action's ID.  Treat this like the ID of a DOM node.
 * @param title (string, required)
 *        The action's title.
 * @param anchorIDOverride (string, optional)
 *        Pass a string to override the node to which the action's activated-
 *        action panel is anchored.
 * @param disabled (bool, optional)
 *        Pass true to cause the action to be disabled initially in all browser
 *        windows.  False by default.
 * @param extensionID (string, optional)
 *        If the action lives in an extension, pass its ID.
 * @param iconURL (string or object, optional)
 *        The URL string of the action's icon.  Usually you want to specify an
 *        icon in CSS, but this option is useful if that would be a pain for
 *        some reason.  You can also pass an object that maps pixel sizes to
 *        URLs, like { 16: url16, 32: url32 }.  The best size for the user's
 *        screen will be used.
 * @param nodeAttributes (object, optional)
 *        An object of name-value pairs.  Each pair will be added as an
 *        attribute to DOM nodes created for this action.
 * @param onBeforePlacedInWindow (function, optional)
 *        Called before the action is placed in the window:
 *        onBeforePlacedInWindow(window)
 *        * window: The window that the action will be placed in.
 * @param onCommand (function, optional)
 *        Called when the action is clicked, but only if it has neither a
 *        subview nor an iframe:
 *        onCommand(event, buttonNode)
 *        * event: The triggering event.
 *        * buttonNode: The button node that was clicked.
 * @param onIframeHiding (function, optional)
 *        Called when the action's iframe is hiding:
 *        onIframeHiding(iframeNode, parentPanelNode)
 *        * iframeNode: The iframe.
 *        * parentPanelNode: The panel node in which the iframe is shown.
 * @param onIframeHidden (function, optional)
 *        Called when the action's iframe is hidden:
 *        onIframeHidden(iframeNode, parentPanelNode)
 *        * iframeNode: The iframe.
 *        * parentPanelNode: The panel node in which the iframe is shown.
 * @param onIframeShowing (function, optional)
 *        Called when the action's iframe is showing to the user:
 *        onIframeShowing(iframeNode, parentPanelNode)
 *        * iframeNode: The iframe.
 *        * parentPanelNode: The panel node in which the iframe is shown.
 * @param onLocationChange (function, optional)
 *        Called after tab switch or when the current <browser>'s location
 *        changes:
 *        onLocationChange(browserWindow)
 *        * browserWindow: The browser window containing the tab switch or
 *          changed <browser>.
 * @param onPlacedInPanel (function, optional)
 *        Called when the action is added to the page action panel in a browser
 *        window:
 *        onPlacedInPanel(buttonNode)
 *        * buttonNode: The action's node in the page action panel.
 * @param onPlacedInUrlbar (function, optional)
 *        Called when the action is added to the urlbar in a browser window:
 *        onPlacedInUrlbar(buttonNode)
 *        * buttonNode: The action's node in the urlbar.
 * @param onRemovedFromWindow (function, optional)
 *        Called after the action is removed from a browser window:
 *        onRemovedFromWindow(browserWindow)
 *        * browserWindow: The browser window that the action was removed from.
 * @param onShowingInPanel (function, optional)
 *        Called when a browser window's page action panel is showing:
 *        onShowingInPanel(buttonNode)
 *        * buttonNode: The action's node in the page action panel.
 * @param pinnedToUrlbar (bool, optional)
 *        Pass true to pin the action to the urlbar.  An action is shown in the
 *        urlbar if it's pinned and not disabled.  False by default.
 * @param subview (object, optional)
 *        An options object suitable for passing to the Subview constructor, if
 *        you'd like the action to have a subview.  See the subview constructor
 *        for info on this object's properties.
 * @param tooltip (string, optional)
 *        The action's button tooltip text.
 * @param urlbarIDOverride (string, optional)
 *        Usually the ID of the action's button in the urlbar will be generated
 *        automatically.  Pass a string for this property to override that with
 *        your own ID.
 * @param wantsIframe (bool, optional)
 *        Pass true to make an action that shows an iframe in a panel when
 *        clicked.
 */
function Action(options) {
  setProperties(this, options, {
    id: true,
    title: !options._isSeparator,
    anchorIDOverride: false,
    disabled: false,
    extensionID: false,
    iconURL: false,
    labelForHistogram: false,
    nodeAttributes: false,
    onBeforePlacedInWindow: false,
    onCommand: false,
    onIframeHiding: false,
    onIframeHidden: false,
    onIframeShowing: false,
    onLocationChange: false,
    onPlacedInPanel: false,
    onPlacedInUrlbar: false,
    onRemovedFromWindow: false,
    onShowingInPanel: false,
    pinnedToUrlbar: false,
    subview: false,
    tooltip: false,
    urlbarIDOverride: false,
    wantsIframe: false,

    // private

    // (string, optional)
    // The ID of another action before which to insert this new action in the
    // panel.
    _insertBeforeActionID: false,

    // (bool, optional)
    // True if this isn't really an action but a separator to be shown in the
    // page action panel.
    _isSeparator: false,

    // (bool, optional)
    // True if the action's urlbar button is defined in markup.  In that case, a
    // node with the action's urlbar node ID should already exist in the DOM
    // (either the auto-generated ID or urlbarIDOverride).  That node will be
    // shown when the action is added to the urlbar and hidden when the action
    // is removed from the urlbar.
    _urlbarNodeInMarkup: false,
  });
  if (this._subview) {
    this._subview = new Subview(options.subview);
  }
}

Action.prototype = {
  /**
   * The ID of the action's parent extension (string, nullable)
   */
  get extensionID() {
    return this._extensionID;
  },

  /**
   * The action's ID (string, nonnull)
   */
  get id() {
    return this._id;
  },

  /**
   * Attribute name => value mapping to set on nodes created for this action
   * (object, nullable)
   */
  get nodeAttributes() {
    return this._nodeAttributes;
  },

  /**
   * True if the action is pinned to the urlbar.  The action is shown in the
   * urlbar if it's pinned and not disabled.  (bool, nonnull)
   */
  get pinnedToUrlbar() {
    return this._pinnedToUrlbar || false;
  },
  set pinnedToUrlbar(shown) {
    if (this.pinnedToUrlbar != shown) {
      this._pinnedToUrlbar = shown;
      PageActions.onActionToggledPinnedToUrlbar(this);
    }
    return this.pinnedToUrlbar;
  },

  /**
   * The action's disabled state (bool, nonnull)
   */
  getDisabled(browserWindow = null) {
    return !!this._getProperty("disabled", browserWindow);
  },
  setDisabled(value, browserWindow = null) {
    return this._setProperty("disabled", !!value, browserWindow);
  },

  /**
   * The action's icon URL string, or an object mapping sizes to URL strings
   * (string or object, nullable)
   */
  getIconURL(browserWindow = null) {
    return this._getProperty("iconURL", browserWindow);
  },
  setIconURL(value, browserWindow = null) {
    return this._setProperty("iconURL", value, browserWindow);
  },

  /**
   * The action's title (string, nonnull)
   */
  getTitle(browserWindow = null) {
    return this._getProperty("title", browserWindow);
  },
  setTitle(value, browserWindow = null) {
    return this._setProperty("title", value, browserWindow);
  },

  /**
   * The action's tooltip (string, nullable)
   */
  getTooltip(browserWindow = null) {
    return this._getProperty("tooltip", browserWindow);
  },
  setTooltip(value, browserWindow = null) {
    return this._setProperty("tooltip", value, browserWindow);
  },

  /**
   * Sets a property, optionally for a particular browser window.
   *
   * @param  name (string, required)
   *         The (non-underscored) name of the property.
   * @param  value
   *         The value.
   * @param  browserWindow (DOM window, optional)
   *         If given, then the property will be set in this window's state, not
   *         globally.
   */
  _setProperty(name, value, browserWindow) {
    if (!browserWindow) {
      // Set the global state.
      this[`_${name}`] = value;
    } else {
      // Set the per-window state.
      let props = this._propertiesByBrowserWindow.get(browserWindow);
      if (!props) {
        props = {};
        this._propertiesByBrowserWindow.set(browserWindow, props);
      }
      props[name] = value;
    }
    // This may be called before the action has been added.
    if (PageActions.actionForID(this.id)) {
      for (let bpa of allBrowserPageActions(browserWindow)) {
        bpa.updateAction(this, name);
      }
    }
    return value;
  },

  /**
   * Gets a property, optionally for a particular browser window.
   *
   * @param  name (string, required)
   *         The (non-underscored) name of the property.
   * @param  browserWindow (DOM window, optional)
   *         If given, then the property will be fetched from this window's
   *         state.  If the property does not exist in the window's state, or if
   *         no window is given, then the global value is returned.
   * @return The property value.
   */
  _getProperty(name, browserWindow) {
    if (browserWindow) {
      // Try the per-window state.
      let props = this._propertiesByBrowserWindow.get(browserWindow);
      if (props && name in props) {
        return props[name];
      }
    }
    // Fall back to the global state.
    return this[`_${name}`];
  },

  // maps browser windows => object with properties for that window
  get _propertiesByBrowserWindow() {
    if (!this.__propertiesByBrowserWindow) {
      this.__propertiesByBrowserWindow = new WeakMap();
    }
    return this.__propertiesByBrowserWindow;
  },

  /**
   * Override for the ID of the action's activated-action panel anchor (string,
   * nullable)
   */
  get anchorIDOverride() {
    return this._anchorIDOverride;
  },

  /**
   * Override for the ID of the action's urlbar node (string, nullable)
   */
  get urlbarIDOverride() {
    return this._urlbarIDOverride;
  },

  /**
   * True if the action is shown in an iframe (bool, nonnull)
   */
  get wantsIframe() {
    return this._wantsIframe || false;
  },

  /**
   * A Subview object if the action wants a subview (Subview, nullable)
   */
  get subview() {
    return this._subview;
  },

  get labelForHistogram() {
    return this._labelForHistogram || this._id;
  },

  /**
   * Returns the URL of the best icon to use given a preferred size.  The best
   * icon is the one with the smallest size that's equal to or bigger than the
   * preferred size.  Returns null if the action has no icon URL.
   *
   * @param  peferredSize (number, required)
   *         The icon size you prefer.
   * @return The URL of the best icon, or null.
   */
  iconURLForSize(preferredSize, browserWindow) {
    let iconURL = this.getIconURL(browserWindow);
    if (!iconURL) {
      return null;
    }
    if (typeof(iconURL) == "string") {
      return iconURL;
    }
    if (typeof(iconURL) == "object") {
      // This case is copied from ExtensionParent.jsm so that our image logic is
      // the same, so that WebExtensions page action tests that deal with icons
      // pass.
      let bestSize = null;
      if (iconURL[preferredSize]) {
        bestSize = preferredSize;
      } else if (iconURL[2 * preferredSize]) {
        bestSize = 2 * preferredSize;
      } else {
        let sizes = Object.keys(iconURL)
                          .map(key => parseInt(key, 10))
                          .sort((a, b) => a - b);
        bestSize = sizes.find(candidate => candidate > preferredSize) || sizes.pop();
      }
      return iconURL[bestSize];
    }
    return null;
  },

  /**
   * Performs the command for an action.  If the action has an onCommand
   * handler, then it's called.  If the action has a subview or iframe, then a
   * panel is opened, displaying the subview or iframe.
   *
   * @param  browserWindow (DOM window, required)
   *         The browser window in which to perform the action.
   */
  doCommand(browserWindow) {
    browserPageActions(browserWindow).doCommandForAction(this);
  },

  /**
   * Call this when before placing the action in the window.
   *
   * @param  browserWindow (DOM window, required)
   *         The browser window the action will be placed in.
   */
  onBeforePlacedInWindow(browserWindow) {
    if (this._onBeforePlacedInWindow) {
      this._onBeforePlacedInWindow(browserWindow);
    }
  },

  /**
   * Call this when the user activates the action.
   *
   * @param  event (DOM event, required)
   *         The triggering event.
   * @param  buttonNode (DOM node, required)
   *         The action's panel or urlbar button node that was clicked.
   */
  onCommand(event, buttonNode) {
    if (this._onCommand) {
      this._onCommand(event, buttonNode);
    }
  },

  /**
   * Call this when the action's iframe is hiding.
   *
   * @param  iframeNode (DOM node, required)
   *         The iframe that's hiding.
   * @param  parentPanelNode (DOM node, required)
   *         The panel in which the iframe is hiding.
   */
  onIframeHiding(iframeNode, parentPanelNode) {
    if (this._onIframeHiding) {
      this._onIframeHiding(iframeNode, parentPanelNode);
    }
  },

  /**
   * Call this when the action's iframe is hidden.
   *
   * @param  iframeNode (DOM node, required)
   *         The iframe that's being hidden.
   * @param  parentPanelNode (DOM node, required)
   *         The panel in which the iframe is hidden.
   */
  onIframeHidden(iframeNode, parentPanelNode) {
    if (this._onIframeHidden) {
      this._onIframeHidden(iframeNode, parentPanelNode);
    }
  },

  /**
   * Call this when the action's iframe is showing.
   *
   * @param  iframeNode (DOM node, required)
   *         The iframe that's being shown.
   * @param  parentPanelNode (DOM node, required)
   *         The panel in which the iframe is shown.
   */
  onIframeShowing(iframeNode, parentPanelNode) {
    if (this._onIframeShowing) {
      this._onIframeShowing(iframeNode, parentPanelNode);
    }
  },

  /**
   * Call this on tab switch or when the current <browser>'s location changes.
   *
   * @param  browserWindow (DOM window, required)
   *         The browser window containing the tab switch or changed <browser>.
   */
  onLocationChange(browserWindow) {
    if (this._onLocationChange) {
      this._onLocationChange(browserWindow);
    }
  },

  /**
   * Call this when a DOM node for the action is added to the page action panel.
   *
   * @param  buttonNode (DOM node, required)
   *         The action's panel button node.
   */
  onPlacedInPanel(buttonNode) {
    if (this._onPlacedInPanel) {
      this._onPlacedInPanel(buttonNode);
    }
  },

  /**
   * Call this when a DOM node for the action is added to the urlbar.
   *
   * @param  buttonNode (DOM node, required)
   *         The action's urlbar button node.
   */
  onPlacedInUrlbar(buttonNode) {
    if (this._onPlacedInUrlbar) {
      this._onPlacedInUrlbar(buttonNode);
    }
  },

  /**
   * Call this when the DOM nodes for the action are removed from a browser
   * window.
   *
   * @param  browserWindow (DOM window, required)
   *         The browser window the action was removed from.
   */
  onRemovedFromWindow(browserWindow) {
    if (this._onRemovedFromWindow) {
      this._onRemovedFromWindow(browserWindow);
    }
  },

  /**
   * Call this when the action's button is shown in the page action panel.
   *
   * @param  buttonNode (DOM node, required)
   *         The action's panel button node.
   */
  onShowingInPanel(buttonNode) {
    if (this._onShowingInPanel) {
      this._onShowingInPanel(buttonNode);
    }
  },

  /**
   * Removes the action's DOM nodes from all browser windows.
   *
   * PageActions will remember the action's urlbar placement, if any, after this
   * method is called until app shutdown.  If the action is not added again
   * before shutdown, then PageActions will discard the placement, and the next
   * time the action is added, its placement will be reset.
   */
  remove() {
    PageActions.onActionRemoved(this);
  },

  /**
   * Returns whether the action should be shown in a given window's urlbar.
   *
   * @param  browserWindow (DOM window, required)
   *         The window.
   * @return True if the action should be shown and false otherwise.  The action
   *         should be shown if it's both pinned and not disabled.
   */
  shouldShowInUrlbar(browserWindow) {
    return this.pinnedToUrlbar && !this.getDisabled(browserWindow);
  },

  get _isBuiltIn() {
    let builtInIDs = [
      "pocket",
      "screenshots",
      "webcompat-reporter-button",
    ].concat(gBuiltInActions.filter(a => !a.__isSeparator).map(a => a.id));
    return builtInIDs.includes(this.id);
  },
};

this.PageActions.Action = Action;


/**
 * A Subview represents a PanelUI panelview that your actions can show.
 * `options` is a required object with the following properties.
 *
 * @param buttons (array, optional)
 *        An array of buttons to show in the subview.  Each item in the array
 *        must be an options object suitable for passing to the Button
 *        constructor.  See the Button constructor for information on these
 *        objects' properties.
 * @param onPlaced (function, optional)
 *        Called when the subview is added to its parent panel in a browser
 *        window:
 *        onPlaced(panelViewNode)
 *        * panelViewNode: The panelview node represented by this Subview.
 * @param onShowing (function, optional)
 *        Called when the subview is showing in a browser window:
 *        onShowing(panelViewNode)
 *        * panelViewNode: The panelview node represented by this Subview.
 */
function Subview(options) {
  setProperties(this, options, {
    buttons: false,
    onPlaced: false,
    onShowing: false,
  });
  this._buttons = (this._buttons || []).map(buttonOptions => {
    return new Button(buttonOptions);
  });
}

Subview.prototype = {
  /**
   * The subview's buttons (array of Button objects, nonnull)
   */
  get buttons() {
    return this._buttons;
  },

  /**
   * Call this when a DOM node for the subview is added to the DOM.
   *
   * @param  panelViewNode (DOM node, required)
   *         The subview's panelview node.
   */
  onPlaced(panelViewNode) {
    if (this._onPlaced) {
      this._onPlaced(panelViewNode);
    }
  },

  /**
   * Call this when a DOM node for the subview is showing.
   *
   * @param  panelViewNode (DOM node, required)
   *         The subview's panelview node.
   */
  onShowing(panelViewNode) {
    if (this._onShowing) {
      this._onShowing(panelViewNode);
    }
  }
};

this.PageActions.Subview = Subview;


/**
 * A button that can be shown in a subview.  `options` is a required object with
 * the following properties.
 *
 * @param id (string, required)
 *        The button's ID.  This will not become the ID of a DOM node by itself,
 *        but it will be used to generate DOM node IDs.  But in terms of spaces
 *        and weird characters and such, do treat this like a DOM node ID.
 * @param title (string, required)
 *        The button's title.
 * @param disabled (bool, required)
 *        Pass true to disable the button.
 * @param onCommand (function, optional)
 *        Called when the button is clicked:
 *        onCommand(event, buttonNode)
 *        * event: The triggering event.
 *        * buttonNode: The node that was clicked.
 * @param shortcut (string, optional)
 *        The button's shortcut text.
 */
function Button(options) {
  setProperties(this, options, {
    id: true,
    title: true,
    disabled: false,
    onCommand: false,
    shortcut: false,
  });
}

Button.prototype = {
  /**
   * True if the button is disabled (bool, nonnull)
   */
  get disabled() {
    return this._disabled || false;
  },

  /**
   * The button's ID (string, nonnull)
   */
  get id() {
    return this._id;
  },

  /**
   * The button's shortcut (string, nullable)
   */
  get shortcut() {
    return this._shortcut;
  },

  /**
   * The button's title (string, nonnull)
   */
  get title() {
    return this._title;
  },

  /**
   * Call this when the user clicks the button.
   *
   * @param  event (DOM event, required)
   *         The triggering event.
   * @param  buttonNode (DOM node, required)
   *         The button's DOM node that was clicked.
   */
  onCommand(event, buttonNode) {
    if (this._onCommand) {
      this._onCommand(event, buttonNode);
    }
  }
};

this.PageActions.Button = Button;


// These are only necessary so that Pocket and the test can use them.
this.PageActions.ACTION_ID_BOOKMARK = ACTION_ID_BOOKMARK;
this.PageActions.ACTION_ID_BOOKMARK_SEPARATOR = ACTION_ID_BOOKMARK_SEPARATOR;
this.PageActions.PREF_PERSISTED_ACTIONS = PREF_PERSISTED_ACTIONS;

// This is only necessary so that the test can access it.
this.PageActions.ACTION_ID_BUILT_IN_SEPARATOR = ACTION_ID_BUILT_IN_SEPARATOR;


// Sorted in the order in which they should appear in the page action panel.
// Does not include the page actions of extensions bundled with the browser.
// They're added by the relevant extension code.
// NOTE: If you add items to this list (or system add-on actions that we
// want to keep track of), make sure to also update Histograms.json for the
// new actions.
var gBuiltInActions = [

  // bookmark
  {
    id: ACTION_ID_BOOKMARK,
    urlbarIDOverride: "star-button-box",
    _urlbarNodeInMarkup: true,
    // The title is set in browser-pageActions.js by calling
    // BookmarkingUI.updateBookmarkPageMenuItem().
    title: "",
    pinnedToUrlbar: true,
    nodeAttributes: {
      observes: "bookmarkThisPageBroadcaster",
    },
    onShowingInPanel(buttonNode) {
      browserPageActions(buttonNode).bookmark.onShowingInPanel(buttonNode);
    },
    onCommand(event, buttonNode) {
      browserPageActions(buttonNode).bookmark.onCommand(event, buttonNode);
    },
  },

  // separator
  {
    id: ACTION_ID_BOOKMARK_SEPARATOR,
    _isSeparator: true,
  },

  // copy URL
  {
    id: "copyURL",
    title: "copyURL-title",
    onPlacedInPanel(buttonNode) {
      browserPageActions(buttonNode).copyURL.onPlacedInPanel(buttonNode);
    },
    onCommand(event, buttonNode) {
      browserPageActions(buttonNode).copyURL.onCommand(event, buttonNode);
    },
  },

  // email link
  {
    id: "emailLink",
    title: "emailLink-title",
    onPlacedInPanel(buttonNode) {
      browserPageActions(buttonNode).emailLink.onPlacedInPanel(buttonNode);
    },
    onCommand(event, buttonNode) {
      browserPageActions(buttonNode).emailLink.onCommand(event, buttonNode);
    },
  }
];

if (Services.prefs.getBoolPref("identity.fxaccounts.enabled")) {
  gBuiltInActions.push(
  // send to device
  {
    id: "sendToDevice",
    title: "sendToDevice-title",
    onPlacedInPanel(buttonNode) {
      browserPageActions(buttonNode).sendToDevice.onPlacedInPanel(buttonNode);
    },
    onLocationChange(browserWindow) {
      browserPageActions(browserWindow).sendToDevice.onLocationChange();
    },
    subview: {
      buttons: [
        {
          id: "notReady",
          title: "sendToDevice-notReadyTitle",
          disabled: true,
        },
      ],
      onPlaced(panelViewNode) {
        browserPageActions(panelViewNode).sendToDevice
          .onSubviewPlaced(panelViewNode);
      },
      onShowing(panelViewNode) {
        browserPageActions(panelViewNode).sendToDevice
          .onShowingSubview(panelViewNode);
      },
    },
  });
}


/**
 * Gets a BrowserPageActions object in a browser window.
 *
 * @param  obj
 *         Either a DOM node or a browser window.
 * @return The BrowserPageActions object in the browser window related to the
 *         given object.
 */
function browserPageActions(obj) {
  if (obj.BrowserPageActions) {
    return obj.BrowserPageActions;
  }
  return obj.ownerGlobal.BrowserPageActions;
}

/**
 * A generator function for all open browser windows.
 *
 * @param browserWindow (DOM window, optional)
 *        If given, then only this window will be yielded.  That may sound
 *        pointless, but it can make callers nicer to write since they don't
 *        need two separate cases, one where a window is given and another where
 *        it isn't.
 */
function* allBrowserWindows(browserWindow = null) {
  if (browserWindow) {
    yield browserWindow;
    return;
  }
  let windows = Services.wm.getEnumerator("navigator:browser");
  while (windows.hasMoreElements()) {
    yield windows.getNext();
  }
}

/**
 * A generator function for BrowserPageActions objects in all open windows.
 *
 * @param browserWindow (DOM window, optional)
 *        If given, then the BrowserPageActions for only this window will be
 *        yielded.
 */
function* allBrowserPageActions(browserWindow = null) {
  for (let win of allBrowserWindows(browserWindow)) {
    yield browserPageActions(win);
  }
}

/**
 * A simple function that sets properties on a given object while doing basic
 * required-properties checking.  If a required property isn't specified in the
 * given options object, or if the options object has properties that aren't in
 * the given schema, then an error is thrown.
 *
 * @param  obj
 *         The object to set properties on.
 * @param  options
 *         An options object supplied by the consumer.
 * @param  schema
 *         An object a property for each required and optional property.  The
 *         keys are property names; the value of a key is a bool that is true if
 *         the property is required.
 */
function setProperties(obj, options, schema) {
  for (let name in schema) {
    let required = schema[name];
    if (required && !(name in options)) {
      throw new Error(`'${name}' must be specified`);
    }
    let nameInObj = "_" + name;
    if (name[0] == "_") {
      // The property is "private".  If it's defined in the options, then define
      // it on obj exactly as it's defined on options.
      if (name in options) {
        obj[nameInObj] = options[name];
      }
    } else {
      // The property is "public".  Make sure the property is defined on obj.
      obj[nameInObj] = options[name] || null;
    }
  }
  for (let name in options) {
    if (!(name in schema)) {
      throw new Error(`Unrecognized option '${name}'`);
    }
  }
}
PK
!<+?_���&modules/ParseBreakpadSymbols-worker.js/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
/* eslint-env worker */

"use strict";

importScripts("resource://gre/modules/osfile.jsm");
importScripts("resource:///modules/ParseSymbols.jsm");

async function fetchSymbolFile(url) {
  const response = await fetch(url);

  if (!response.ok) {
    throw new Error(`got error status ${response.status}`);
  }

  return response.text();
}

function parse(text) {
  const syms = new Map();

  // Lines look like this:
  //
  // PUBLIC 3fc74 0 test_public_symbol
  //
  // FUNC 40330 8e 0 test_func_symbol
  const symbolRegex = /\nPUBLIC ([0-9a-f]+) [0-9a-f]+ (.*)|\nFUNC ([0-9a-f]+) [0-9a-f]+ [0-9a-f]+ (.*)/g;

  let match;
  let approximateLength = 0;
  while ((match = symbolRegex.exec(text))) {
    const [address0, symbol0, address1, symbol1] = match.slice(1);
    const address = parseInt(address0 || address1, 16);
    const sym = (symbol0 || symbol1).trimRight();
    syms.set(address, sym);
    approximateLength += sym.length;
  }

  return ParseSymbols.convertSymsMapToExpectedSymFormat(syms, approximateLength);
}

onmessage = async e => {
  try {
    let text;
    if (e.data.filepath) {
      text = await OS.File.read(e.data.filepath, {encoding: "utf-8"});
    } else if (e.data.url) {
      text = await fetchSymbolFile(e.data.url);
    }

    const result = parse(text);
    postMessage({result}, result.map(r => r.buffer));
  } catch (error) {
    postMessage({error: error.toString()});
  }
  close();
};
PK
!<�r0ff%modules/ParseCppFiltSymbols-worker.js/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
/* eslint-env worker */

"use strict";

importScripts("resource:///modules/ParseSymbols.jsm");

class WorkerCppFiltParser {
  constructor(length) {
    this._decoder = new TextDecoder();
    this._index = 0;
    this._approximateLength = 0;
    this._results = new Array(length);
  }

  consume(arrayBuffer) {
    const data = this._decoder.decode(arrayBuffer, {stream: true});
    const lineRegex = /.*\n?/g;
    const buffer = this._currentLine + data;

    let match;
    while ((match = lineRegex.exec(buffer))) {
      let [line] = match;
      if (line[line.length - 1] === "\n") {
        this._processLine(line);
      } else {
        this._currentLine = line;
        break;
      }
    }
  }

  finish() {
    this._processLine(this._currentLine);
    return {symsArray: this._results, approximateLength: this._approximateLength};
  }

  _processLine(line) {
    const trimmed = line.trimRight();
    this._approximateLength += trimmed.length;
    this._results[this._index++] = trimmed;
  }
}

let cppFiltParser;
onmessage = async e => {
  try {
    if (!cppFiltParser) {
      cppFiltParser = new WorkerCppFiltParser();
    }
    if (e.data.finish) {
      const {symsArray, approximateLength} = cppFiltParser.finish();
      const result = ParseSymbols.convertSymsArrayToExpectedSymFormat(symsArray, approximateLength);

      postMessage({result}, result.map(r => r.buffer));
      close();
    } else {
      cppFiltParser.consume(e.data.buffer);
    }
  } catch (error) {
    postMessage({error: error.toString()});
  }
};
PK
!<X9t�
�
 modules/ParseNMSymbols-worker.js/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
/* eslint-env worker */

"use strict";

importScripts("resource:///modules/ParseSymbols.jsm");

class WorkerNMParser {
  constructor() {
    this._decoder = new TextDecoder();
    this._addrToSymMap = new Map();
    this._approximateLength = 0;
  }

  consume(arrayBuffer) {
    const data = this._decoder.decode(arrayBuffer, {stream: true});
    const lineRegex = /.*\n?/g;
    const buffer = this._currentLine + data;

    let match;
    while ((match = lineRegex.exec(buffer))) {
      let [line] = match;
      if (line[line.length - 1] === "\n") {
        this._processLine(line);
      } else {
        this._currentLine = line;
        break;
      }
    }
  }

  finish() {
    this._processLine(this._currentLine);
    return {syms: this._addrToSymMap, approximateLength: this._approximateLength};
  }

  _processLine(line) {
    // Example lines:
    // 00000000028c9888 t GrFragmentProcessor::MulOutputByInputUnpremulColor(sk_sp<GrFragmentProcessor>)::PremulFragmentProcessor::onCreateGLSLInstance() const::GLFP::~GLFP()
    // 00000000028c9874 t GrFragmentProcessor::MulOutputByInputUnpremulColor(sk_sp<GrFragmentProcessor>)::PremulFragmentProcessor::onCreateGLSLInstance() const::GLFP::~GLFP()
    // 00000000028c9874 t GrFragmentProcessor::MulOutputByInputUnpremulColor(sk_sp<GrFragmentProcessor>)::PremulFragmentProcessor::onCreateGLSLInstance() const::GLFP::~GLFP()
    // 0000000003a33730 r mozilla::OggDemuxer::~OggDemuxer()::{lambda()#1}::operator()() const::__func__
    // 0000000003a33930 r mozilla::VPXDecoder::Drain()::{lambda()#1}::operator()() const::__func__
    //
    // Some lines have the form
    // <address> ' ' <letter> ' ' <symbol>
    // and some have the form
    // <address> ' ' <symbol>
    // The letter has a meaning, but we ignore it.

    const regex = /([^ ]+) (?:. )?(.*)/;
    let match = regex.exec(line);
    if (match) {
      const [, address, symbol] = match;
      this._addrToSymMap.set(parseInt(address, 16), symbol);
      this._approximateLength += symbol.length;
    }
  }
}

let nmParser;
onmessage = async e => {
  try {
    if (!nmParser) {
      nmParser = new WorkerNMParser();
    }
    if (e.data.finish) {
      const {syms, approximateLength} = nmParser.finish();
      let result;
      if (e.data.isDarwin) {
        result = ParseSymbols.convertSymsMapToDemanglerFormat(syms);
      } else {
        result = ParseSymbols.convertSymsMapToExpectedSymFormat(syms, approximateLength);
      }

      postMessage({result}, result.map(r => r.buffer));
      close();
    } else {
      nmParser.consume(e.data.buffer);
    }
  } catch (error) {
    postMessage({error: error.toString()});
  }
};
PK
!<����modules/ParseSymbols.jsm/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";

/* exported ParseSymbols */

var EXPORTED_SYMBOLS = ["ParseSymbols"];

function convertStringArrayToUint8BufferWithIndex(array, approximateLength) {
  const index = new Uint32Array(array.length + 1);

  const textEncoder = new TextEncoder();
  let buffer = new Uint8Array(approximateLength);
  let pos = 0;

  for (let i = 0; i < array.length; i++) {
    const encodedString = textEncoder.encode(array[i]);

    let size = pos + buffer.length;
    if (size < buffer.length) {
      size = 2 << Math.log(size) / Math.log(2);
      let newBuffer = new Uint8Array(size);
      newBuffer.set(buffer);
      buffer = newBuffer;
    }

    buffer.set(encodedString, pos);
    index[i] = pos;
    pos += encodedString.length;
  }
  index[array.length] = pos;

  return {index, buffer};
}

function convertSymsMapToExpectedSymFormat(syms, approximateSymLength) {
  const addresses = Array.from(syms.keys());
  addresses.sort((a, b) => a - b);

  const symsArray = addresses.map(addr => syms.get(addr));
  const {index, buffer} =
    convertStringArrayToUint8BufferWithIndex(symsArray, approximateSymLength);

  return [new Uint32Array(addresses), index, buffer];
}

function convertSymsArrayToExpectedSymFormat(symsArray, approximateSymLength) {
  const {index, buffer} =
    convertStringArrayToUint8BufferWithIndex(symsArray, approximateSymLength);
  return [index, buffer];
}

function convertSymsMapToDemanglerFormat(syms) {
  const addresses = Array.from(syms.keys());
  addresses.sort((a, b) => a - b);

  const symsArray = addresses.map(addr => syms.get(addr));
  const textEncoder = new TextEncoder();
  const buffer = textEncoder.encode(symsArray.join("\n"));

  return [new Uint32Array(addresses), buffer];
}

var ParseSymbols = {
  convertSymsMapToExpectedSymFormat,
  convertSymsArrayToExpectedSymFormat,
  convertSymsMapToDemanglerFormat,
};
PK
!<$?�-]-]modules/PermissionUI.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = [
  "PermissionUI",
];

/**
 * PermissionUI is responsible for exposing both a prototype
 * PermissionPrompt that can be used by arbitrary browser
 * components and add-ons, but also hosts the implementations of
 * built-in permission prompts.
 *
 * If you're developing a feature that requires web content to ask
 * for special permissions from the user, this module is for you.
 *
 * Suppose a system add-on wants to add a new prompt for a new request
 * for getting more low-level access to the user's sound card, and the
 * permission request is coming up from content by way of the
 * nsContentPermissionHelper. The system add-on could then do the following:
 *
 * Cu.import("resource://gre/modules/Integration.jsm");
 * Cu.import("resource:///modules/PermissionUI.jsm");
 *
 * const SoundCardIntegration = (base) => ({
 *   __proto__: base,
 *   createPermissionPrompt(type, request) {
 *     if (type != "sound-api") {
 *       return super.createPermissionPrompt(...arguments);
 *     }
 *
 *     return {
 *       __proto__: PermissionUI.PermissionPromptForRequestPrototype,
 *       get permissionKey() {
 *         return "sound-permission";
 *       }
 *       // etc - see the documentation for PermissionPrompt for
 *       // a better idea of what things one can and should override.
 *     }
 *   },
 * });
 *
 * // Add-on startup:
 * Integration.contentPermission.register(SoundCardIntegration);
 * // ...
 * // Add-on shutdown:
 * Integration.contentPermission.unregister(SoundCardIntegration);
 *
 * Note that PermissionPromptForRequestPrototype must be used as the
 * prototype, since the prompt is wrapping an nsIContentPermissionRequest,
 * and going through nsIContentPermissionPrompt.
 *
 * It is, however, possible to take advantage of PermissionPrompt without
 * having to go through nsIContentPermissionPrompt or with a
 * nsIContentPermissionRequest. The PermissionPromptPrototype can be
 * imported, subclassed, and have prompt() called directly, without
 * the caller having called into createPermissionPrompt.
 */
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");

ChromeUtils.defineModuleGetter(this, "Services",
  "resource://gre/modules/Services.jsm");
ChromeUtils.defineModuleGetter(this, "SitePermissions",
  "resource:///modules/SitePermissions.jsm");
ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
  "resource://gre/modules/PrivateBrowsingUtils.jsm");

XPCOMUtils.defineLazyGetter(this, "gBrandBundle", function() {
  return Services.strings
                 .createBundle("chrome://branding/locale/brand.properties");
});

XPCOMUtils.defineLazyGetter(this, "gBrowserBundle", function() {
  return Services.strings
                 .createBundle("chrome://browser/locale/browser.properties");
});

var PermissionUI = {};

/**
 * PermissionPromptPrototype should be subclassed by callers that
 * want to display prompts to the user. See each method and property
 * below for guidance on what to override.
 *
 * Note that if you're creating a prompt for an
 * nsIContentPermissionRequest, you'll want to subclass
 * PermissionPromptForRequestPrototype instead.
 */
var PermissionPromptPrototype = {
  /**
   * Returns the associated <xul:browser> for the request. This should
   * work for the e10s and non-e10s case.
   *
   * Subclasses must override this.
   *
   * @return {<xul:browser>}
   */
  get browser() {
    throw new Error("Not implemented.");
  },

  /**
   * Returns the nsIPrincipal associated with the request.
   *
   * Subclasses must override this.
   *
   * @return {nsIPrincipal}
   */
  get principal() {
    throw new Error("Not implemented.");
  },

  /**
   * If the nsIPermissionManager is being queried and written
   * to for this permission request, set this to the key to be
   * used. If this is undefined, user permissions will not be
   * read from or written to.
   *
   * Note that if a permission is set, in any follow-up
   * prompting within the expiry window of that permission,
   * the prompt will be skipped and the allow or deny choice
   * will be selected automatically.
   */
  get permissionKey() {
    return undefined;
  },

  /**
   * These are the options that will be passed to the
   * PopupNotification when it is shown. See the documentation
   * for PopupNotification for more details.
   *
   * Note that prompt() will automatically set displayURI to
   * be the URI of the requesting pricipal, unless the displayURI is exactly
   * set to false.
   */
  get popupOptions() {
    return {};
  },

  /**
   * PopupNotification requires a unique ID to open the notification.
   * You must return a unique ID string here, for which PopupNotification
   * will then create a <xul:popupnotification> node with the ID
   * "<notificationID>-notification".
   *
   * If there's a custom <xul:popupnotification> you're hoping to show,
   * then you need to make sure its ID has the "-notification" suffix,
   * and then return the prefix here.
   *
   * See PopupNotification.jsm for more details.
   *
   * @return {string}
   *         The unique ID that will be used to as the
   *         "<unique ID>-notification" ID for the <xul:popupnotification>
   *         to use or create.
   */
  get notificationID() {
    throw new Error("Not implemented.");
  },

  /**
   * The ID of the element to anchor the PopupNotification to.
   *
   * @return {string}
   */
  get anchorID() {
    return "default-notification-icon";
  },

  /**
   * The message to show to the user in the PopupNotification. A string
   * with "<>" as a placeholder that is usually replaced by an addon name
   * or a host name, formatted in bold, to describe the permission that is being requested.
   *
   * Subclasses must override this.
   *
   * @return {string}
   */
  get message() {
    throw new Error("Not implemented.");
  },

  /**
   * This will be called if the request is to be cancelled.
   *
   * Subclasses only need to override this if they provide a
   * permissionKey.
   */
  cancel() {
    throw new Error("Not implemented.");
  },

  /**
   * This will be called if the request is to be allowed.
   *
   * Subclasses only need to override this if they provide a
   * permissionKey.
   */
  allow() {
    throw new Error("Not implemented.");
  },

  /**
   * The actions that will be displayed in the PopupNotification
   * via a dropdown menu. The first item in this array will be
   * the default selection. Each action is an Object with the
   * following properties:
   *
   *  label (string):
   *    The label that will be displayed for this choice.
   *  accessKey (string):
   *    The access key character that will be used for this choice.
   *  action (SitePermissions state)
   *    The action that will be associated with this choice.
   *    This should be either SitePermissions.ALLOW or SitePermissions.BLOCK.
   *
   *  callback (function, optional)
   *    A callback function that will fire if the user makes this choice, with
   *    a single parameter, state. State is an Object that contains the property
   *    checkboxChecked, which identifies whether the checkbox to remember this
   *    decision was checked.
   */
  get promptActions() {
    return [];
  },

  /**
   * If the prompt will be shown to the user, this callback will
   * be called just before. Subclasses may want to override this
   * in order to, for example, bump a counter Telemetry probe for
   * how often a particular permission request is seen.
   */
  onBeforeShow() {},

  /**
   * Will determine if a prompt should be shown to the user, and if so,
   * will show it.
   *
   * If a permissionKey is defined prompt() might automatically
   * allow or cancel itself based on the user's current
   * permission settings without displaying the prompt.
   *
   * If the permission is not already set and the <xul:browser> that the request
   * is associated with does not belong to a browser window with the
   * PopupNotifications global set, the prompt request is ignored.
   */
  prompt() {
    // We ignore requests from non-nsIStandardURLs
    let requestingURI = this.principal.URI;
    if (!(requestingURI instanceof Ci.nsIStandardURL)) {
      return;
    }

    if (this.permissionKey) {
      // If we're reading and setting permissions, then we need
      // to check to see if we already have a permission setting
      // for this particular principal.
      let {state} = SitePermissions.get(requestingURI,
                                        this.permissionKey,
                                        this.browser);

      if (state == SitePermissions.BLOCK) {
        this.cancel();
        return;
      }

      if (state == SitePermissions.ALLOW) {
        this.allow();
        return;
      }

      // Tell the browser to refresh the identity block display in case there
      // are expired permission states.
      this.browser.dispatchEvent(new this.browser.ownerGlobal
                                         .CustomEvent("PermissionStateChange"));
    }

    let chromeWin = this.browser.ownerGlobal;
    if (!chromeWin.PopupNotifications) {
      this.cancel();
      return;
    }

    // Transform the PermissionPrompt actions into PopupNotification actions.
    let popupNotificationActions = [];
    for (let promptAction of this.promptActions) {
      let action = {
        label: promptAction.label,
        accessKey: promptAction.accessKey,
        callback: state => {
          if (promptAction.callback) {
            promptAction.callback();
          }

          if (this.permissionKey) {

            // Permanently store permission.
            if ((state && state.checkboxChecked) ||
                promptAction.scope == SitePermissions.SCOPE_PERSISTENT) {
              let scope = SitePermissions.SCOPE_PERSISTENT;
              // Only remember permission for session if in PB mode.
              if (PrivateBrowsingUtils.isBrowserPrivate(this.browser)) {
                scope = SitePermissions.SCOPE_SESSION;
              }
              SitePermissions.set(this.principal.URI,
                                  this.permissionKey,
                                  promptAction.action,
                                  scope);
            } else if (promptAction.action == SitePermissions.BLOCK) {
              // Temporarily store BLOCK permissions only.
              // SitePermissions does not consider subframes when storing temporary
              // permissions on a tab, thus storing ALLOW could be exploited.
              SitePermissions.set(this.principal.URI,
                                  this.permissionKey,
                                  promptAction.action,
                                  SitePermissions.SCOPE_TEMPORARY,
                                  this.browser);
            }

            // Grant permission if action is ALLOW.
            if (promptAction.action == SitePermissions.ALLOW) {
              this.allow();
            } else {
              this.cancel();
            }
          }
        },
      };
      if (promptAction.dismiss) {
        action.dismiss = promptAction.dismiss;
      }

      popupNotificationActions.push(action);
    }

    let mainAction = popupNotificationActions.length ?
                     popupNotificationActions[0] : null;
    let secondaryActions = popupNotificationActions.splice(1);

    let options = this.popupOptions;

    if (!options.hasOwnProperty("displayURI") || options.displayURI) {
      options.displayURI = this.principal.URI;
    }
    // Permission prompts are always persistent; the close button is controlled by a pref.
    options.persistent = true;
    options.hideClose = !Services.prefs.getBoolPref("privacy.permissionPrompts.showCloseButton");
    // When the docshell of the browser is aboout to be swapped to another one,
    // the "swapping" event is called. Returning true causes the notification
    // to be moved to the new browser.
    options.eventCallback = topic => topic == "swapping";

    this.onBeforeShow();
    chromeWin.PopupNotifications.show(this.browser,
                                      this.notificationID,
                                      this.message,
                                      this.anchorID,
                                      mainAction,
                                      secondaryActions,
                                      options);
  },
};

PermissionUI.PermissionPromptPrototype = PermissionPromptPrototype;

/**
 * A subclass of PermissionPromptPrototype that assumes
 * that this.request is an nsIContentPermissionRequest
 * and fills in some of the required properties on the
 * PermissionPrompt. For callers that are wrapping an
 * nsIContentPermissionRequest, this should be subclassed
 * rather than PermissionPromptPrototype.
 */
var PermissionPromptForRequestPrototype = {
  __proto__: PermissionPromptPrototype,

  get browser() {
    // In the e10s-case, the <xul:browser> will be at request.element.
    // In the single-process case, we have to use some XPCOM incantations
    // to resolve to the <xul:browser>.
    if (this.request.element) {
      return this.request.element;
    }
    return this.request
               .window
               .QueryInterface(Ci.nsIInterfaceRequestor)
               .getInterface(Ci.nsIWebNavigation)
               .QueryInterface(Ci.nsIDocShell)
               .chromeEventHandler;
  },

  get principal() {
    return this.request.principal;
  },

  cancel() {
    this.request.cancel();
  },

  allow() {
    this.request.allow();
  },
};

PermissionUI.PermissionPromptForRequestPrototype =
  PermissionPromptForRequestPrototype;

/**
 * Creates a PermissionPrompt for a nsIContentPermissionRequest for
 * the GeoLocation API.
 *
 * @param request (nsIContentPermissionRequest)
 *        The request for a permission from content.
 */
function GeolocationPermissionPrompt(request) {
  this.request = request;
}

GeolocationPermissionPrompt.prototype = {
  __proto__: PermissionPromptForRequestPrototype,

  get permissionKey() {
    return "geo";
  },

  get popupOptions() {
    let pref = "browser.geolocation.warning.infoURL";
    let options = {
      learnMoreURL: Services.urlFormatter.formatURLPref(pref),
      displayURI: false,
      name: this.principal.URI.hostPort,
    };

    if (this.principal.URI.schemeIs("file")) {
      options.checkbox = { show: false };
    } else {
      // Don't offer "always remember" action in PB mode
      options.checkbox = {
        show: !PrivateBrowsingUtils.isWindowPrivate(this.browser.ownerGlobal)
      };
    }

    if (options.checkbox.show) {
      options.checkbox.label = gBrowserBundle.GetStringFromName("geolocation.remember");
    }

    return options;
  },

  get notificationID() {
    return "geolocation";
  },

  get anchorID() {
    return "geo-notification-icon";
  },

  get message() {
    if (this.principal.URI.schemeIs("file")) {
      return gBrowserBundle.GetStringFromName("geolocation.shareWithFile3");
    }

    return gBrowserBundle.formatStringFromName("geolocation.shareWithSite3",
                                                  ["<>"], 1);
  },

  get promptActions() {
    // We collect Telemetry data on Geolocation prompts and how users
    // respond to them. The probe keys are a bit verbose, so let's alias them.
    const SHARE_LOCATION =
      Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST_SHARE_LOCATION;
    const ALWAYS_SHARE =
      Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST_ALWAYS_SHARE;
    const NEVER_SHARE =
      Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST_NEVER_SHARE;

    let secHistogram = Services.telemetry.getHistogramById("SECURITY_UI");

    return [{
      label: gBrowserBundle.GetStringFromName("geolocation.allowLocation"),
      accessKey:
        gBrowserBundle.GetStringFromName("geolocation.allowLocation.accesskey"),
      action: SitePermissions.ALLOW,
      callback(state) {
        if (state && state.checkboxChecked) {
          secHistogram.add(ALWAYS_SHARE);
        } else {
          secHistogram.add(SHARE_LOCATION);
        }
      },
    }, {
      label: gBrowserBundle.GetStringFromName("geolocation.dontAllowLocation"),
      accessKey:
        gBrowserBundle.GetStringFromName("geolocation.dontAllowLocation.accesskey"),
      action: SitePermissions.BLOCK,
      callback(state) {
        if (state && state.checkboxChecked) {
          secHistogram.add(NEVER_SHARE);
        }
      },
    }];
  },

  onBeforeShow() {
    let secHistogram = Services.telemetry.getHistogramById("SECURITY_UI");
    const SHOW_REQUEST = Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST;
    secHistogram.add(SHOW_REQUEST);
  },
};

PermissionUI.GeolocationPermissionPrompt = GeolocationPermissionPrompt;

/**
 * Creates a PermissionPrompt for a nsIContentPermissionRequest for
 * the Desktop Notification API.
 *
 * @param request (nsIContentPermissionRequest)
 *        The request for a permission from content.
 * @return {PermissionPrompt} (see documentation in header)
 */
function DesktopNotificationPermissionPrompt(request) {
  this.request = request;
}

DesktopNotificationPermissionPrompt.prototype = {
  __proto__: PermissionPromptForRequestPrototype,

  get permissionKey() {
    return "desktop-notification";
  },

  get popupOptions() {
    let learnMoreURL =
      Services.urlFormatter.formatURLPref("app.support.baseURL") + "push";

    return {
      learnMoreURL,
      displayURI: false,
      name: this.principal.URI.hostPort,
    };
  },

  get notificationID() {
    return "web-notifications";
  },

  get anchorID() {
    return "web-notifications-notification-icon";
  },

  get message() {
    return gBrowserBundle.formatStringFromName("webNotifications.receiveFromSite2",
                                                    ["<>"], 1);
  },

  get promptActions() {
    let actions = [
      {
        label: gBrowserBundle.GetStringFromName("webNotifications.allow"),
        accessKey:
          gBrowserBundle.GetStringFromName("webNotifications.allow.accesskey"),
        action: SitePermissions.ALLOW,
        scope: SitePermissions.SCOPE_PERSISTENT,
      },
      {
        label: gBrowserBundle.GetStringFromName("webNotifications.notNow"),
        accessKey:
          gBrowserBundle.GetStringFromName("webNotifications.notNow.accesskey"),
        action: SitePermissions.BLOCK,
      },
    ];
    if (!PrivateBrowsingUtils.isBrowserPrivate(this.browser)) {
      actions.push({
        label: gBrowserBundle.GetStringFromName("webNotifications.never"),
        accessKey:
          gBrowserBundle.GetStringFromName("webNotifications.never.accesskey"),
        action: SitePermissions.BLOCK,
        scope: SitePermissions.SCOPE_PERSISTENT,
      });
    }
    return actions;
  },
};

PermissionUI.DesktopNotificationPermissionPrompt =
  DesktopNotificationPermissionPrompt;

/**
 * Creates a PermissionPrompt for a nsIContentPermissionRequest for
 * the persistent-storage API.
 *
 * @param request (nsIContentPermissionRequest)
 *        The request for a permission from content.
 */
function PersistentStoragePermissionPrompt(request) {
  this.request = request;
}

PersistentStoragePermissionPrompt.prototype = {
  __proto__: PermissionPromptForRequestPrototype,

  get permissionKey() {
    return "persistent-storage";
  },

  get popupOptions() {
    let checkbox = {
      // In PB mode, we don't want the "always remember" checkbox
      show: !PrivateBrowsingUtils.isWindowPrivate(this.browser.ownerGlobal)
    };
    if (checkbox.show) {
      checkbox.checked = true;
      checkbox.label = gBrowserBundle.GetStringFromName("persistentStorage.remember");
    }
    let learnMoreURL =
      Services.urlFormatter.formatURLPref("app.support.baseURL") + "storage-permissions";
    return {
      checkbox,
      learnMoreURL,
      displayURI: false,
      name: this.principal.URI.hostPort,
    };
  },

  get notificationID() {
    return "persistent-storage";
  },

  get anchorID() {
    return "persistent-storage-notification-icon";
  },

  get message() {
    return gBrowserBundle.formatStringFromName("persistentStorage.allowWithSite",
                                                    ["<>"], 1);
  },

  get promptActions() {
    return [
      {
        label: gBrowserBundle.GetStringFromName("persistentStorage.allow"),
        accessKey:
          gBrowserBundle.GetStringFromName("persistentStorage.allow.accesskey"),
        action: Ci.nsIPermissionManager.ALLOW_ACTION
      },
      {
        label: gBrowserBundle.GetStringFromName("persistentStorage.dontAllow"),
        accessKey:
          gBrowserBundle.GetStringFromName("persistentStorage.dontAllow.accesskey"),
        action: Ci.nsIPermissionManager.DENY_ACTION
      }
    ];
  }
};

PermissionUI.PersistentStoragePermissionPrompt = PersistentStoragePermissionPrompt;

/**
 * Creates a PermissionPrompt for a nsIContentPermissionRequest for
 * the WebMIDI API.
 *
 * @param request (nsIContentPermissionRequest)
 *        The request for a permission from content.
 */
function MIDIPermissionPrompt(request) {
  this.request = request;
  let types = request.types.QueryInterface(Ci.nsIArray);
  let perm = types.queryElementAt(0, Ci.nsIContentPermissionType);
  this.isSysexPerm = (perm.options.length > 0 &&
                      perm.options.queryElementAt(0, Ci.nsISupportsString) == "sysex");
  this.permName = "midi";
  if (this.isSysexPerm) {
    this.permName = "midi-sysex";
  }
}

MIDIPermissionPrompt.prototype = {
  __proto__: PermissionPromptForRequestPrototype,

  get permissionKey() {
    return this.permName;
  },

  get popupOptions() {
    // TODO (bug 1433235) We need a security/permissions explanation URL for this
    let options = {
      displayURI: false,
      name: this.principal.URI.hostPort,
    };

    if (this.principal.URI.schemeIs("file")) {
      options.checkbox = { show: false };
    } else {
      // Don't offer "always remember" action in PB mode
      options.checkbox = {
        show: !PrivateBrowsingUtils.isWindowPrivate(this.browser.ownerGlobal)
      };
    }

    if (options.checkbox.show) {
      options.checkbox.label = gBrowserBundle.GetStringFromName("midi.remember");
    }

    return options;
  },

  get notificationID() {
    return "midi";
  },

  get anchorID() {
    return "midi-notification-icon";
  },

  get message() {
    let message;
    if (this.principal.URI.schemeIs("file")) {
      if (this.isSysexPerm) {
        message = gBrowserBundle.formatStringFromName("midi.shareSysexWithFile.message");
      } else {
        message = gBrowserBundle.formatStringFromName("midi.shareWithFile.message");
      }
    } else if (this.isSysexPerm) {
      message = gBrowserBundle.formatStringFromName("midi.shareSysexWithSite.message",
                                                    ["<>"], 1);
    } else {
      message = gBrowserBundle.formatStringFromName("midi.shareWithSite.message",
                                                    ["<>"], 1);
    }
    return message;
  },

  get promptActions() {
    return [{
        label: gBrowserBundle.GetStringFromName("midi.Allow.label"),
        accessKey: gBrowserBundle.GetStringFromName("midi.Allow.accesskey"),
        action: Ci.nsIPermissionManager.ALLOW_ACTION
      },
      {
        label: gBrowserBundle.GetStringFromName("midi.DontAllow.label"),
        accessKey: gBrowserBundle.GetStringFromName("midi.DontAllow.accesskey"),
        action: Ci.nsIPermissionManager.DENY_ACTION
    }];
  },

  onBeforeShow() {
  },
};

PermissionUI.MIDIPermissionPrompt = MIDIPermissionPrompt;
PK
!<�''h^^modules/PingCentre.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.importGlobalProperties(["fetch"]);

ChromeUtils.defineModuleGetter(this, "AppConstants",
  "resource://gre/modules/AppConstants.jsm");
ChromeUtils.defineModuleGetter(this, "ClientID",
  "resource://gre/modules/ClientID.jsm");
ChromeUtils.defineModuleGetter(this, "TelemetryEnvironment",
  "resource://gre/modules/TelemetryEnvironment.jsm");

const PREF_BRANCH = "browser.ping-centre.";

const TELEMETRY_PREF = `${PREF_BRANCH}telemetry`;
const LOGGING_PREF = `${PREF_BRANCH}log`;
const PRODUCTION_ENDPOINT_PREF = `${PREF_BRANCH}production.endpoint`;

const FHR_UPLOAD_ENABLED_PREF = "datareporting.healthreport.uploadEnabled";
const BROWSER_SEARCH_REGION_PREF = "browser.search.region";

// Only report region for following regions, to ensure that users in countries
// with small user population (less than 10000) cannot be uniquely identified.
// See bug 1421422 for more details.
const REGION_WHITELIST = new Set([
  "AE", "AF", "AL", "AM", "AR", "AT", "AU", "AZ", "BA", "BD", "BE", "BF",
  "BG", "BJ", "BO", "BR", "BY", "CA", "CH", "CI", "CL", "CM", "CN", "CO",
  "CR", "CU", "CY", "CZ", "DE", "DK", "DO", "DZ", "EC", "EE", "EG", "ES",
  "ET", "FI", "FR", "GB", "GE", "GH", "GP", "GR", "GT", "HK", "HN", "HR",
  "HU", "ID", "IE", "IL", "IN", "IQ", "IR", "IS", "IT", "JM", "JO", "JP",
  "KE", "KH", "KR", "KW", "KZ", "LB", "LK", "LT", "LU", "LV", "LY", "MA",
  "MD", "ME", "MG", "MK", "ML", "MM", "MN", "MQ", "MT", "MU", "MX", "MY",
  "MZ", "NC", "NG", "NI", "NL", "NO", "NP", "NZ", "OM", "PA", "PE", "PH",
  "PK", "PL", "PR", "PS", "PT", "PY", "QA", "RE", "RO", "RS", "RU", "RW",
  "SA", "SD", "SE", "SG", "SI", "SK", "SN", "SV", "SY", "TG", "TH", "TN",
  "TR", "TT", "TW", "TZ", "UA", "UG", "US", "UY", "UZ", "VE", "VN", "ZA",
  "ZM", "ZW"
]);

/**
 * Observe various notifications and send them to a telemetry endpoint.
 *
 * @param {Object} options
 * @param {string} options.topic - a unique ID for users of PingCentre to distinguish
 *                  their data on the server side.
 * @param {string} options.overrideEndpointPref - optional pref for URL where the POST is sent.
 */
class PingCentre {
  constructor(options) {
    if (!options.topic) {
      throw new Error("Must specify topic.");
    }

    this._topic = options.topic;
    this._prefs = Services.prefs.getBranch("");

    this._setPingEndpoint(options.topic, options.overrideEndpointPref);

    this._enabled = this._prefs.getBoolPref(TELEMETRY_PREF);
    this._onTelemetryPrefChange = this._onTelemetryPrefChange.bind(this);
    this._prefs.addObserver(TELEMETRY_PREF, this._onTelemetryPrefChange);

    this._fhrEnabled = this._prefs.getBoolPref(FHR_UPLOAD_ENABLED_PREF);
    this._onFhrPrefChange = this._onFhrPrefChange.bind(this);
    this._prefs.addObserver(FHR_UPLOAD_ENABLED_PREF, this._onFhrPrefChange);

    this.logging = this._prefs.getBoolPref(LOGGING_PREF);
    this._onLoggingPrefChange = this._onLoggingPrefChange.bind(this);
    this._prefs.addObserver(LOGGING_PREF, this._onLoggingPrefChange);
  }

  /**
   * Lazily get the Telemetry id promise
   */
  get telemetryClientId() {
    Object.defineProperty(this, "telemetryClientId", {value: ClientID.getClientID()});
    return this.telemetryClientId;
  }

  get enabled() {
    return this._enabled && this._fhrEnabled;
  }

  _setPingEndpoint(topic, overrideEndpointPref) {
    const overrideValue = overrideEndpointPref &&
      this._prefs.getStringPref(overrideEndpointPref);
    if (overrideValue) {
      this._pingEndpoint = overrideValue;
    } else {
      this._pingEndpoint = this._prefs.getStringPref(PRODUCTION_ENDPOINT_PREF);
    }
  }

  _onLoggingPrefChange(aSubject, aTopic, prefKey) {
    this.logging = this._prefs.getBoolPref(prefKey);
  }

  _onTelemetryPrefChange(aSubject, aTopic, prefKey) {
    this._enabled = this._prefs.getBoolPref(prefKey);
  }

  _onFhrPrefChange(aSubject, aTopic, prefKey) {
    this._fhrEnabled = this._prefs.getBoolPref(prefKey);
  }

  _createExperimentsString(activeExperiments, filter) {
    let experimentsString = "";
    for (let experimentID in activeExperiments) {
      if (!activeExperiments[experimentID] ||
          !activeExperiments[experimentID].branch ||
          (filter && !experimentID.includes(filter))) {
        continue;
      }
      let expString = `${experimentID}:${activeExperiments[experimentID].branch}`;
      experimentsString = experimentsString.concat(`${expString};`);
    }
    return experimentsString;
  }

  _getRegion() {
    let region = "UNSET";

    if (Services.prefs.prefHasUserValue(BROWSER_SEARCH_REGION_PREF)) {
      region = Services.prefs.getStringPref(BROWSER_SEARCH_REGION_PREF);
      if (region === "") {
        region = "EMPTY";
      } else if (!REGION_WHITELIST.has(region)) {
        region = "OTHER";
      }
    }
    return region;
  }

  async sendPing(data, options) {
    let filter = options && options.filter;
    let experiments = TelemetryEnvironment.getActiveExperiments();
    let experimentsString = this._createExperimentsString(experiments, filter);
    if (!this.enabled) {
      return Promise.resolve();
    }

    let clientID = data.client_id || await this.telemetryClientId;
    let locale = data.locale || Services.locale.getAppLocalesAsLangTags().pop();
    let profileCreationDate = TelemetryEnvironment.currentEnvironment.profile.resetDate ||
      TelemetryEnvironment.currentEnvironment.profile.creationDate;
    const payload = Object.assign({
      locale,
      topic: this._topic,
      client_id: clientID,
      version: AppConstants.MOZ_APP_VERSION,
      release_channel: AppConstants.MOZ_UPDATE_CHANNEL
    }, data);
    if (experimentsString) {
      payload.shield_id = experimentsString;
    }
    if (profileCreationDate) {
      payload.profile_creation_date = profileCreationDate;
    }
    payload.region = this._getRegion();

    if (this.logging) {
      // performance related pings cause a lot of logging, so we mute them
      if (data.action !== "activity_stream_performance") {
        Services.console.logStringMessage(`TELEMETRY PING: ${JSON.stringify(payload)}\n`);
      }
    }

    return fetch(this._pingEndpoint, {method: "POST", body: JSON.stringify(payload)}).then(response => {
      if (!response.ok) {
        Cu.reportError(`Ping failure with HTTP response code: ${response.status}`);
      }
    }).catch(e => {
      Cu.reportError(`Ping failure with error: ${e}`);
    });
  }

  uninit() {
    try {
      this._prefs.removeObserver(TELEMETRY_PREF, this._onTelemetryPrefChange);
      this._prefs.removeObserver(LOGGING_PREF, this._onLoggingPrefChange);
      this._prefs.removeObserver(FHR_UPLOAD_ENABLED_PREF, this._onFhrPrefChange);
    } catch (e) {
      Cu.reportError(e);
    }
  }
}

this.PingCentre = PingCentre;
this.PingCentreConstants = {
  PRODUCTION_ENDPOINT_PREF,
  FHR_UPLOAD_ENABLED_PREF,
  TELEMETRY_PREF,
  LOGGING_PREF
};
const EXPORTED_SYMBOLS = ["PingCentre", "PingCentreConstants"];
PK
!<�M����modules/PlacesUIUtils.jsm/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

var EXPORTED_SYMBOLS = ["PlacesUIUtils"];

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/Timer.jsm");

XPCOMUtils.defineLazyModuleGetters(this, {
  OpenInTabsUtils: "resource:///modules/OpenInTabsUtils.jsm",
  PlacesUtils: "resource://gre/modules/PlacesUtils.jsm",
  PluralForm: "resource://gre/modules/PluralForm.jsm",
  PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
  RecentWindow: "resource:///modules/RecentWindow.jsm",
  PromiseUtils: "resource://gre/modules/PromiseUtils.jsm",
  PlacesTransactions: "resource://gre/modules/PlacesTransactions.jsm",
  Weave: "resource://services-sync/main.js",
});

XPCOMUtils.defineLazyGetter(this, "bundle", function() {
  return Services.strings.createBundle("chrome://browser/locale/places/places.properties");
});

const gInContentProcess = Services.appinfo.processType == Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT;
const FAVICON_REQUEST_TIMEOUT = 60 * 1000;
// Map from windows to arrays of data about pending favicon loads.
let gFaviconLoadDataMap = new Map();

const ITEM_CHANGED_BATCH_NOTIFICATION_THRESHOLD = 10;

// copied from utilityOverlay.js
const TAB_DROP_TYPE = "application/x-moz-tabbrowser-tab";
const PREF_LOAD_BOOKMARKS_IN_BACKGROUND = "browser.tabs.loadBookmarksInBackground";
const PREF_LOAD_BOOKMARKS_IN_TABS = "browser.tabs.loadBookmarksInTabs";

let InternalFaviconLoader = {
  /**
   * This gets called for every inner window that is destroyed.
   * In the parent process, we process the destruction ourselves. In the child process,
   * we notify the parent which will then process it based on that message.
   */
  observe(subject, topic, data) {
    let innerWindowID = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
    this.removeRequestsForInner(innerWindowID);
  },

  /**
   * Actually cancel the request, and clear the timeout for cancelling it.
   */
  _cancelRequest({uri, innerWindowID, timerID, callback}, reason) {
    // Break cycle
    let request = callback.request;
    delete callback.request;
    // Ensure we don't time out.
    clearTimeout(timerID);
    try {
      request.cancel();
    } catch (ex) {
      Cu.reportError("When cancelling a request for " + uri.spec + " because " + reason + ", it was already canceled!");
    }
  },

  /**
   * Called for every inner that gets destroyed, only in the parent process.
   */
  removeRequestsForInner(innerID) {
    for (let [window, loadDataForWindow] of gFaviconLoadDataMap) {
      let newLoadDataForWindow = loadDataForWindow.filter(loadData => {
        let innerWasDestroyed = loadData.innerWindowID == innerID;
        if (innerWasDestroyed) {
          this._cancelRequest(loadData, "the inner window was destroyed or a new favicon was loaded for it");
        }
        // Keep the items whose inner is still alive.
        return !innerWasDestroyed;
      });
      // Map iteration with for...of is safe against modification, so
      // now just replace the old value:
      gFaviconLoadDataMap.set(window, newLoadDataForWindow);
    }
  },

  /**
   * Called when a toplevel chrome window unloads. We use this to tidy up after ourselves,
   * avoid leaks, and cancel any remaining requests. The last part should in theory be
   * handled by the inner-window-destroyed handlers. We clean up just to be on the safe side.
   */
  onUnload(win) {
    let loadDataForWindow = gFaviconLoadDataMap.get(win);
    if (loadDataForWindow) {
      for (let loadData of loadDataForWindow) {
        this._cancelRequest(loadData, "the chrome window went away");
      }
    }
    gFaviconLoadDataMap.delete(win);
  },

  /**
   * Remove a particular favicon load's loading data from our map tracking
   * load data per chrome window.
   *
   * @param win
   *        the chrome window in which we should look for this load
   * @param filterData ({innerWindowID, uri, callback})
   *        the data we should use to find this particular load to remove.
   *
   * @return the loadData object we removed, or null if we didn't find any.
   */
  _removeLoadDataFromWindowMap(win, {innerWindowID, uri, callback}) {
    let loadDataForWindow = gFaviconLoadDataMap.get(win);
    if (loadDataForWindow) {
      let itemIndex = loadDataForWindow.findIndex(loadData => {
        return loadData.innerWindowID == innerWindowID &&
               loadData.uri.equals(uri) &&
               loadData.callback.request == callback.request;
      });
      if (itemIndex != -1) {
        let loadData = loadDataForWindow[itemIndex];
        loadDataForWindow.splice(itemIndex, 1);
        return loadData;
      }
    }
    return null;
  },

  /**
   * Create a function to use as a nsIFaviconDataCallback, so we can remove cancelling
   * information when the request succeeds. Note that right now there are some edge-cases,
   * such as about: URIs with chrome:// favicons where the success callback is not invoked.
   * This is OK: we will 'cancel' the request after the timeout (or when the window goes
   * away) but that will be a no-op in such cases.
   */
  _makeCompletionCallback(win, id) {
    return {
      onComplete(uri) {
        let loadData = InternalFaviconLoader._removeLoadDataFromWindowMap(win, {
          uri,
          innerWindowID: id,
          callback: this,
        });
        if (loadData) {
          clearTimeout(loadData.timerID);
        }
        delete this.request;
      },
    };
  },

  ensureInitialized() {
    if (this._initialized) {
      return;
    }
    this._initialized = true;

    Services.obs.addObserver(this, "inner-window-destroyed");
    Services.ppmm.addMessageListener("Toolkit:inner-window-destroyed", msg => {
      this.removeRequestsForInner(msg.data);
    });
  },

  loadFavicon(browser, principal, uri, requestContextID) {
    this.ensureInitialized();
    let win = browser.ownerGlobal;
    if (!gFaviconLoadDataMap.has(win)) {
      gFaviconLoadDataMap.set(win, []);
      let unloadHandler = event => {
        let doc = event.target;
        let eventWin = doc.defaultView;
        if (eventWin == win) {
          win.removeEventListener("unload", unloadHandler);
          this.onUnload(win);
        }
      };
      win.addEventListener("unload", unloadHandler, true);
    }

    let {innerWindowID, currentURI} = browser;

    // First we do the actual setAndFetch call:
    let loadType = PrivateBrowsingUtils.isWindowPrivate(win)
      ? PlacesUtils.favicons.FAVICON_LOAD_PRIVATE
      : PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE;
    let callback = this._makeCompletionCallback(win, innerWindowID);
    let request = PlacesUtils.favicons.setAndFetchFaviconForPage(currentURI, uri, false,
                                                                 loadType, callback, principal,
                                                                 requestContextID);

    // Now register the result so we can cancel it if/when necessary.
    if (!request) {
      // The favicon service can return with success but no-op (and leave request
      // as null) if the icon is the same as the page (e.g. for images) or if it is
      // the favicon for an error page. In this case, we do not need to do anything else.
      return;
    }
    callback.request = request;
    let loadData = {innerWindowID, uri, callback};
    loadData.timerID = setTimeout(() => {
      this._cancelRequest(loadData, "it timed out");
      this._removeLoadDataFromWindowMap(win, loadData);
    }, FAVICON_REQUEST_TIMEOUT);
    let loadDataForWindow = gFaviconLoadDataMap.get(win);
    loadDataForWindow.push(loadData);
  },
};

var PlacesUIUtils = {
  ORGANIZER_LEFTPANE_VERSION: 8,
  ORGANIZER_FOLDER_ANNO: "PlacesOrganizer/OrganizerFolder",
  ORGANIZER_QUERY_ANNO: "PlacesOrganizer/OrganizerQuery",

  LOAD_IN_SIDEBAR_ANNO: "bookmarkProperties/loadInSidebar",
  DESCRIPTION_ANNO: "bookmarkProperties/description",

  /**
   * Makes a URI from a spec, and do fixup
   * @param   aSpec
   *          The string spec of the URI
   * @return A URI object for the spec.
   */
  createFixedURI: function PUIU_createFixedURI(aSpec) {
    return Services.uriFixup.createFixupURI(aSpec, Ci.nsIURIFixup.FIXUP_FLAG_NONE);
  },

  getFormattedString: function PUIU_getFormattedString(key, params) {
    return bundle.formatStringFromName(key, params, params.length);
  },

  /**
   * Get a localized plural string for the specified key name and numeric value
   * substituting parameters.
   *
   * @param   aKey
   *          String, key for looking up the localized string in the bundle
   * @param   aNumber
   *          Number based on which the final localized form is looked up
   * @param   aParams
   *          Array whose items will substitute #1, #2,... #n parameters
   *          in the string.
   *
   * @see https://developer.mozilla.org/en/Localization_and_Plurals
   * @return The localized plural string.
   */
  getPluralString: function PUIU_getPluralString(aKey, aNumber, aParams) {
    let str = PluralForm.get(aNumber, bundle.GetStringFromName(aKey));

    // Replace #1 with aParams[0], #2 with aParams[1], and so on.
    return str.replace(/\#(\d+)/g, function(matchedId, matchedNumber) {
      let param = aParams[parseInt(matchedNumber, 10) - 1];
      return param !== undefined ? param : matchedId;
    });
  },

  getString: function PUIU_getString(key) {
    return bundle.GetStringFromName(key);
  },

  /**
   * Shows the bookmark dialog corresponding to the specified info.
   *
   * @param aInfo
   *        Describes the item to be edited/added in the dialog.
   *        See documentation at the top of bookmarkProperties.js
   * @param aWindow
   *        Owner window for the new dialog.
   *
   * @see documentation at the top of bookmarkProperties.js
   * @return true if any transaction has been performed, false otherwise.
   */
  showBookmarkDialog(aInfo, aParentWindow) {
    // Preserve size attributes differently based on the fact the dialog has
    // a folder picker or not, since it needs more horizontal space than the
    // other controls.
    let hasFolderPicker = !("hiddenRows" in aInfo) ||
                          !aInfo.hiddenRows.includes("folderPicker");
    // Use a different chrome url to persist different sizes.
    let dialogURL = hasFolderPicker ?
                    "chrome://browser/content/places/bookmarkProperties2.xul" :
                    "chrome://browser/content/places/bookmarkProperties.xul";

    let features = "centerscreen,chrome,modal,resizable=yes";

    let topUndoEntry;
    let batchBlockingDeferred;

    // Set the transaction manager into batching mode.
    topUndoEntry = PlacesTransactions.topUndoEntry;
    batchBlockingDeferred = PromiseUtils.defer();
    PlacesTransactions.batch(async () => {
      await batchBlockingDeferred.promise;
    });

    aParentWindow.openDialog(dialogURL, "", features, aInfo);

    let performed = ("performed" in aInfo && aInfo.performed);

    batchBlockingDeferred.resolve();

    if (!performed &&
        topUndoEntry != PlacesTransactions.topUndoEntry) {
      PlacesTransactions.undo().catch(Cu.reportError);
    }

    return performed;
  },

  /**
   * set and fetch a favicon. Can only be used from the parent process.
   * @param browser   {Browser}   The XUL browser element for which we're fetching a favicon.
   * @param principal {Principal} The loading principal to use for the fetch.
   * @param uri       {URI}       The URI to fetch.
   */
  loadFavicon(browser, principal, uri, requestContextID) {
    if (gInContentProcess) {
      throw new Error("Can't track loads from within the child process!");
    }
    InternalFaviconLoader.loadFavicon(browser, principal, uri, requestContextID);
  },

  /**
   * Returns the closet ancestor places view for the given DOM node
   * @param aNode
   *        a DOM node
   * @return the closet ancestor places view if exists, null otherwsie.
   */
  getViewForNode: function PUIU_getViewForNode(aNode) {
    let node = aNode;

    if (node.localName == "panelview" && node._placesView) {
      return node._placesView;
    }

    // The view for a <menu> of which its associated menupopup is a places
    // view, is the menupopup.
    if (node.localName == "menu" && !node._placesNode &&
        node.lastChild._placesView)
      return node.lastChild._placesView;

    while (node instanceof Ci.nsIDOMElement) {
      if (node._placesView)
        return node._placesView;
      if (node.localName == "tree" && node.getAttribute("type") == "places")
        return node;

      node = node.parentNode;
    }

    return null;
  },

  /**
   * Returns the active PlacesController for a given command.
   *
   * @param win The window containing the affected view
   * @param command The command
   * @return a PlacesController
   */
  getControllerForCommand(win, command) {
    // A context menu may be built for non-focusable views.  Thus, we first try
    // to look for a view associated with document.popupNode
    let popupNode;
    try {
      popupNode = win.document.popupNode;
    } catch (e) {
      // The document went away (bug 797307).
      return null;
    }
    if (popupNode) {
      let view = this.getViewForNode(popupNode);
      if (view && view._contextMenuShown)
        return view.controllers.getControllerForCommand(command);
    }

    // When we're not building a context menu, only focusable views
    // are possible.  Thus, we can safely use the command dispatcher.
    let controller = win.top.document.commandDispatcher
                        .getControllerForCommand(command);
    return controller || null;
  },

  /**
   * Update all the Places commands for the given window.
   *
   * @param win The window to update.
   */
  updateCommands(win) {
    // Get the controller for one of the places commands.
    let controller = this.getControllerForCommand(win, "placesCmd_open");
    for (let command of [
      "placesCmd_open",
      "placesCmd_open:window",
      "placesCmd_open:privatewindow",
      "placesCmd_open:tab",
      "placesCmd_new:folder",
      "placesCmd_new:bookmark",
      "placesCmd_new:separator",
      "placesCmd_show:info",
      "placesCmd_reload",
      "placesCmd_sortBy:name",
      "placesCmd_cut",
      "placesCmd_copy",
      "placesCmd_paste",
      "placesCmd_delete",
    ]) {
      win.goSetCommandEnabled(command,
                              controller && controller.isCommandEnabled(command));
    }
  },

  /**
   * Executes the given command on the currently active controller.
   *
   * @param win The window containing the affected view
   * @param command The command to execute
   */
  doCommand(win, command) {
    let controller = this.getControllerForCommand(win, command);
    if (controller && controller.isCommandEnabled(command))
      controller.doCommand(command);
  },

  /**
   * By calling this before visiting an URL, the visit will be associated to a
   * TRANSITION_TYPED transition (if there is no a referrer).
   * This is used when visiting pages from the history menu, history sidebar,
   * url bar, url autocomplete results, and history searches from the places
   * organizer.  If this is not called visits will be marked as
   * TRANSITION_LINK.
   */
  markPageAsTyped: function PUIU_markPageAsTyped(aURL) {
    PlacesUtils.history.markPageAsTyped(this.createFixedURI(aURL));
  },

  /**
   * By calling this before visiting an URL, the visit will be associated to a
   * TRANSITION_BOOKMARK transition.
   * This is used when visiting pages from the bookmarks menu,
   * personal toolbar, and bookmarks from within the places organizer.
   * If this is not called visits will be marked as TRANSITION_LINK.
   */
  markPageAsFollowedBookmark: function PUIU_markPageAsFollowedBookmark(aURL) {
    PlacesUtils.history.markPageAsFollowedBookmark(this.createFixedURI(aURL));
  },

  /**
   * By calling this before visiting an URL, any visit in frames will be
   * associated to a TRANSITION_FRAMED_LINK transition.
   * This is actually used to distinguish user-initiated visits in frames
   * so automatic visits can be correctly ignored.
   */
  markPageAsFollowedLink: function PUIU_markPageAsFollowedLink(aURL) {
    PlacesUtils.history.markPageAsFollowedLink(this.createFixedURI(aURL));
  },

  /**
   * Allows opening of javascript/data URI only if the given node is
   * bookmarked (see bug 224521).
   * @param aURINode
   *        a URI node
   * @param aWindow
   *        a window on which a potential error alert is shown on.
   * @return true if it's safe to open the node in the browser, false otherwise.
   *
   */
  checkURLSecurity: function PUIU_checkURLSecurity(aURINode, aWindow) {
    if (PlacesUtils.nodeIsBookmark(aURINode))
      return true;

    var uri = Services.io.newURI(aURINode.uri);
    if (uri.schemeIs("javascript") || uri.schemeIs("data")) {
      const BRANDING_BUNDLE_URI = "chrome://branding/locale/brand.properties";
      var brandShortName = Services.strings.
                           createBundle(BRANDING_BUNDLE_URI).
                           GetStringFromName("brandShortName");

      var errorStr = this.getString("load-js-data-url-error");
      Services.prompt.alert(aWindow, brandShortName, errorStr);
      return false;
    }
    return true;
  },

  /**
   * Get the description associated with a document, as specified in a <META>
   * element.
   * @param   doc
   *          A DOM Document to get a description for
   * @return A description string if a META element was discovered with a
   *         "description" or "httpequiv" attribute, empty string otherwise.
   */
  getDescriptionFromDocument: function PUIU_getDescriptionFromDocument(doc) {
    var metaElements = doc.getElementsByTagName("META");
    for (var i = 0; i < metaElements.length; ++i) {
      if (metaElements[i].name.toLowerCase() == "description" ||
          metaElements[i].httpEquiv.toLowerCase() == "description") {
        return metaElements[i].content;
      }
    }
    return "";
  },

  /**
   * Retrieve the description of an item
   * @param aItemId
   *        item identifier
   * @return the description of the given item, or an empty string if it is
   * not set.
   */
  getItemDescription: function PUIU_getItemDescription(aItemId) {
    if (PlacesUtils.annotations.itemHasAnnotation(aItemId, this.DESCRIPTION_ANNO))
      return PlacesUtils.annotations.getItemAnnotation(aItemId, this.DESCRIPTION_ANNO);
    return "";
  },

  /**
   * Check whether or not the given node represents a removable entry (either in
   * history or in bookmarks).
   *
   * @param aNode
   *        a node, except the root node of a query.
   * @param aView
   *        The view originating the request.
   * @return true if the aNode represents a removable entry, false otherwise.
   */
  canUserRemove(aNode, aView) {
    let parentNode = aNode.parent;
    if (!parentNode) {
      // canUserRemove doesn't accept root nodes.
      return false;
    }

    // Is it a query pointing to one of the special root folders?
    if (PlacesUtils.nodeIsQuery(parentNode) && PlacesUtils.nodeIsFolder(aNode)) {
      let guid = PlacesUtils.getConcreteItemGuid(aNode);
      // If the parent folder is not a folder, it must be a query, and so this node
      // cannot be removed.
      if (PlacesUtils.isRootItem(guid)) {
        return false;
      }
    }

    // If it's not a bookmark, we can remove it unless it's a child of a
    // livemark.
    if (aNode.itemId == -1) {
      // Rather than executing a db query, checking the existence of the feedURI
      // annotation, detect livemark children by the fact that they are the only
      // direct non-bookmark children of bookmark folders.
      return !PlacesUtils.nodeIsFolder(parentNode);
    }

    // Generally it's always possible to remove children of a query.
    if (PlacesUtils.nodeIsQuery(parentNode))
      return true;

    // Otherwise it has to be a child of an editable folder.
    return !this.isFolderReadOnly(parentNode, aView);
  },

  /**
   * DO NOT USE THIS API IN ADDONS. IT IS VERY LIKELY TO CHANGE WHEN THE SWITCH
   * TO GUIDS IS COMPLETE (BUG 1071511).
   *
   * Check whether or not the given Places node points to a folder which
   * should not be modified by the user (i.e. its children should be unremovable
   * and unmovable, new children should be disallowed, etc).
   * These semantics are not inherited, meaning that read-only folder may
   * contain editable items (for instance, the places root is read-only, but all
   * of its direct children aren't).
   *
   * You should only pass folder nodes.
   *
   * @param placesNode
   *        any folder result node.
   * @param view
   *        The view originating the request.
   * @throws if placesNode is not a folder result node or views is invalid.
   * @note livemark "folders" are considered read-only (but see bug 1072833).
   * @return true if placesNode is a read-only folder, false otherwise.
   */
  isFolderReadOnly(placesNode, view) {
    if (typeof placesNode != "object" || !PlacesUtils.nodeIsFolder(placesNode)) {
      throw new Error("invalid value for placesNode");
    }
    if (!view || typeof view != "object") {
      throw new Error("invalid value for aView");
    }
    let itemId = PlacesUtils.getConcreteItemId(placesNode);
    if (itemId == PlacesUtils.placesRootId ||
        view.controller.hasCachedLivemarkInfo(placesNode))
      return true;

    // leftPaneFolderId is a lazy getter
    // performing at least a synchronous DB query (and on its very first call
    // in a fresh profile, it also creates the entire structure).
    // Therefore we don't want to this function, which is called very often by
    // isCommandEnabled, to ever be the one that invokes it first, especially
    // because isCommandEnabled may be called way before the left pane folder is
    // even created (for example, if the user only uses the bookmarks menu or
    // toolbar for managing bookmarks).  To do so, we avoid comparing to those
    // special folder if the lazy getter is still in place.  This is safe merely
    // because the only way to access the left pane contents goes through
    // "resolving" the leftPaneFolderId getter.
    if (typeof Object.getOwnPropertyDescriptor(this, "leftPaneFolderId").get == "function") {
      return false;
    }
    return itemId == this.leftPaneFolderId;
  },

  /** aItemsToOpen needs to be an array of objects of the form:
    * {uri: string, isBookmark: boolean}
    */
  _openTabset: function PUIU__openTabset(aItemsToOpen, aEvent, aWindow) {
    if (!aItemsToOpen.length)
      return;

    // Prefer the caller window if it's a browser window, otherwise use
    // the top browser window.
    var browserWindow = null;
    browserWindow =
      aWindow && aWindow.document.documentElement.getAttribute("windowtype") == "navigator:browser" ?
      aWindow : RecentWindow.getMostRecentBrowserWindow();

    var urls = [];
    let skipMarking = browserWindow && PrivateBrowsingUtils.isWindowPrivate(browserWindow);
    for (let item of aItemsToOpen) {
      urls.push(item.uri);
      if (skipMarking) {
        continue;
      }

      if (item.isBookmark)
        this.markPageAsFollowedBookmark(item.uri);
      else
        this.markPageAsTyped(item.uri);
    }

    // whereToOpenLink doesn't return "window" when there's no browser window
    // open (Bug 630255).
    var where = browserWindow ?
                browserWindow.whereToOpenLink(aEvent, false, true) : "window";
    if (where == "window") {
      // There is no browser window open, thus open a new one.
      var uriList = PlacesUtils.toISupportsString(urls.join("|"));
      var args = Cc["@mozilla.org/array;1"].
                  createInstance(Ci.nsIMutableArray);
      args.appendElement(uriList);
      browserWindow = Services.ww.openWindow(aWindow,
                                             "chrome://browser/content/browser.xul",
                                             null, "chrome,dialog=no,all", args);
      return;
    }

    var loadInBackground = where == "tabshifted";
    // For consistency, we want all the bookmarks to open in new tabs, instead
    // of having one of them replace the currently focused tab.  Hence we call
    // loadTabs with aReplace set to false.
    browserWindow.gBrowser.loadTabs(urls, {
      inBackground: loadInBackground,
      replace: false,
      triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
    });
  },

  openLiveMarkNodesInTabs:
  function PUIU_openLiveMarkNodesInTabs(aNode, aEvent, aView) {
    let window = aView.ownerWindow;

    PlacesUtils.livemarks.getLivemark({id: aNode.itemId})
      .then(aLivemark => {
        let urlsToOpen = [];

        let nodes = aLivemark.getNodesForContainer(aNode);
        for (let node of nodes) {
          urlsToOpen.push({uri: node.uri, isBookmark: false});
        }

        if (OpenInTabsUtils.confirmOpenInTabs(urlsToOpen.length, window)) {
          this._openTabset(urlsToOpen, aEvent, window);
        }
      }, Cu.reportError);
  },

  openContainerNodeInTabs:
  function PUIU_openContainerInTabs(aNode, aEvent, aView) {
    let window = aView.ownerWindow;

    let urlsToOpen = PlacesUtils.getURLsForContainerNode(aNode);
    if (OpenInTabsUtils.confirmOpenInTabs(urlsToOpen.length, window)) {
      this._openTabset(urlsToOpen, aEvent, window);
    }
  },

  openURINodesInTabs: function PUIU_openURINodesInTabs(aNodes, aEvent, aView) {
    let window = aView.ownerWindow;

    let urlsToOpen = [];
    for (var i = 0; i < aNodes.length; i++) {
      // Skip over separators and folders.
      if (PlacesUtils.nodeIsURI(aNodes[i]))
        urlsToOpen.push({uri: aNodes[i].uri, isBookmark: PlacesUtils.nodeIsBookmark(aNodes[i])});
    }
    this._openTabset(urlsToOpen, aEvent, window);
  },

  /**
   * Loads the node's URL in the appropriate tab or window or as a web
   * panel given the user's preference specified by modifier keys tracked by a
   * DOM mouse/key event.
   * @param   aNode
   *          An uri result node.
   * @param   aEvent
   *          The DOM mouse/key event with modifier keys set that track the
   *          user's preferred destination window or tab.
   */
  openNodeWithEvent:
  function PUIU_openNodeWithEvent(aNode, aEvent) {
    let window = aEvent.target.ownerGlobal;

    let where = window.whereToOpenLink(aEvent, false, true);
    if (where == "current" && this.loadBookmarksInTabs &&
        PlacesUtils.nodeIsBookmark(aNode) && !aNode.uri.startsWith("javascript:")) {
      where = "tab";
    }

    this._openNodeIn(aNode, where, window);
    let view = this.getViewForNode(aEvent.target);
    if (view && view.controller.hasCachedLivemarkInfo(aNode.parent)) {
      Services.telemetry.scalarAdd("browser.feeds.livebookmark_item_opened", 1);
    }
  },

  /**
   * Loads the node's URL in the appropriate tab or window or as a
   * web panel.
   * see also openUILinkIn
   */
  openNodeIn: function PUIU_openNodeIn(aNode, aWhere, aView, aPrivate) {
    let window = aView.ownerWindow;
    this._openNodeIn(aNode, aWhere, window, aPrivate);
  },

  _openNodeIn: function PUIU__openNodeIn(aNode, aWhere, aWindow, aPrivate = false) {
    if (aNode && PlacesUtils.nodeIsURI(aNode) &&
        this.checkURLSecurity(aNode, aWindow)) {
      let isBookmark = PlacesUtils.nodeIsBookmark(aNode);

      if (!PrivateBrowsingUtils.isWindowPrivate(aWindow)) {
        if (isBookmark)
          this.markPageAsFollowedBookmark(aNode.uri);
        else
          this.markPageAsTyped(aNode.uri);
      }

      // Check whether the node is a bookmark which should be opened as
      // a web panel
      if (aWhere == "current" && isBookmark) {
        if (PlacesUtils.annotations
                       .itemHasAnnotation(aNode.itemId, this.LOAD_IN_SIDEBAR_ANNO)) {
          let browserWin = RecentWindow.getMostRecentBrowserWindow();
          if (browserWin) {
            browserWin.openWebPanel(aNode.title, aNode.uri);
            return;
          }
        }
      }

      aWindow.openUILinkIn(aNode.uri, aWhere, {
        allowPopups: aNode.uri.startsWith("javascript:"),
        inBackground: this.loadBookmarksInBackground,
        private: aPrivate,
      });
    }
  },

  /**
   * Helper for guessing scheme from an url string.
   * Used to avoid nsIURI overhead in frequently called UI functions.
   *
   * @param aUrlString the url to guess the scheme from.
   *
   * @return guessed scheme for this url string.
   *
   * @note this is not supposed be perfect, so use it only for UI purposes.
   */
  guessUrlSchemeForUI: function PUIU_guessUrlSchemeForUI(aUrlString) {
    return aUrlString.substr(0, aUrlString.indexOf(":"));
  },

  getBestTitle: function PUIU_getBestTitle(aNode, aDoNotCutTitle) {
    var title;
    if (!aNode.title && PlacesUtils.nodeIsURI(aNode)) {
      // if node title is empty, try to set the label using host and filename
      // Services.io.newURI will throw if aNode.uri is not a valid URI
      try {
        var uri = Services.io.newURI(aNode.uri);
        var host = uri.host;
        var fileName = uri.QueryInterface(Ci.nsIURL).fileName;
        // if fileName is empty, use path to distinguish labels
        if (aDoNotCutTitle) {
          title = host + uri.pathQueryRef;
        } else {
          title = host + (fileName ?
                           (host ? "/" + this.ellipsis + "/" : "") + fileName :
                           uri.pathQueryRef);
        }
      } catch (e) {
        // Use (no title) for non-standard URIs (data:, javascript:, ...)
        title = "";
      }
    } else
      title = aNode.title;

    return title || this.getString("noTitle");
  },

  get leftPaneQueries() {
    // build the map
    this.leftPaneFolderId;
    return this.leftPaneQueries;
  },

  get leftPaneFolderId() {
    delete this.leftPaneFolderId;
    return this.leftPaneFolderId = this.maybeRebuildLeftPane();
  },

  // Get the folder id for the organizer left-pane folder.
  maybeRebuildLeftPane() {
    let leftPaneRoot = -1;

    // Shortcuts to services.
    let bs = PlacesUtils.bookmarks;
    let as = PlacesUtils.annotations;

    // This is the list of the left pane queries.
    let queries = {
      "PlacesRoot": { title: "" },
      "History": { title: this.getString("OrganizerQueryHistory") },
      "Downloads": { title: this.getString("OrganizerQueryDownloads") },
      "Tags": { title: this.getString("OrganizerQueryTags") },
      "AllBookmarks": { title: this.getString("OrganizerQueryAllBookmarks") },
    };
    // All queries but PlacesRoot.
    const EXPECTED_QUERY_COUNT = 4;

    // Removes an item and associated annotations, ignoring eventual errors.
    function safeRemoveItem(aItemId) {
      try {
        if (as.itemHasAnnotation(aItemId, PlacesUIUtils.ORGANIZER_QUERY_ANNO) &&
            !(as.getItemAnnotation(aItemId, PlacesUIUtils.ORGANIZER_QUERY_ANNO) in queries)) {
          // Some extension annotated their roots with our query annotation,
          // so we should not delete them.
          return;
        }
        // removeItemAnnotation does not check if item exists, nor the anno,
        // so this is safe to do.
        as.removeItemAnnotation(aItemId, PlacesUIUtils.ORGANIZER_FOLDER_ANNO);
        as.removeItemAnnotation(aItemId, PlacesUIUtils.ORGANIZER_QUERY_ANNO);
        // This will throw if the annotation is an orphan.
        bs.removeItem(aItemId);
      } catch (e) { /* orphan anno */ }
    }

    // Returns true if item really exists, false otherwise.
    function itemExists(aItemId) {
      try {
        bs.getFolderIdForItem(aItemId);
        return true;
      } catch (e) {
        return false;
      }
    }

    // Get all items marked as being the left pane folder.
    let items = as.getItemsWithAnnotation(this.ORGANIZER_FOLDER_ANNO);
    if (items.length > 1) {
      // Something went wrong, we cannot have more than one left pane folder,
      // remove all left pane folders and continue.  We will create a new one.
      items.forEach(safeRemoveItem);
    } else if (items.length == 1 && items[0] != -1) {
      leftPaneRoot = items[0];
      // Check that organizer left pane root is valid.
      let version = as.getItemAnnotation(leftPaneRoot, this.ORGANIZER_FOLDER_ANNO);
      if (version != this.ORGANIZER_LEFTPANE_VERSION ||
          !itemExists(leftPaneRoot)) {
        // Invalid root, we must rebuild the left pane.
        safeRemoveItem(leftPaneRoot);
        leftPaneRoot = -1;
      }
    }

    if (leftPaneRoot != -1) {
      // A valid left pane folder has been found.
      // Build the leftPaneQueries Map.  This is used to quickly access them,
      // associating a mnemonic name to the real item ids.
      delete this.leftPaneQueries;
      this.leftPaneQueries = {};

      let queryItems = as.getItemsWithAnnotation(this.ORGANIZER_QUERY_ANNO);
      // While looping through queries we will also check for their validity.
      let queriesCount = 0;
      let corrupt = false;
      for (let i = 0; i < queryItems.length; i++) {
        let queryName = as.getItemAnnotation(queryItems[i], this.ORGANIZER_QUERY_ANNO);

        // Some extension did use our annotation to decorate their items
        // with icons, so we should check only our elements, to avoid dataloss.
        if (!(queryName in queries))
          continue;

        let query = queries[queryName];
        query.itemId = queryItems[i];

        if (!itemExists(query.itemId)) {
          // Orphan annotation, bail out and create a new left pane root.
          corrupt = true;
          break;
        }

        // Check that all queries have valid parents.
        let parentId = bs.getFolderIdForItem(query.itemId);
        if (!queryItems.includes(parentId) && parentId != leftPaneRoot) {
          // The parent is not part of the left pane, bail out and create a new
          // left pane root.
          corrupt = true;
          break;
        }

        // Titles could have been corrupted or the user could have changed his
        // locale.  Check title and eventually fix it.
        if (bs.getItemTitle(query.itemId) != query.title)
          bs.setItemTitle(query.itemId, query.title);
        if ("concreteId" in query) {
          if (bs.getItemTitle(query.concreteId) != query.concreteTitle)
            bs.setItemTitle(query.concreteId, query.concreteTitle);
        }

        // Add the query to our cache.
        this.leftPaneQueries[queryName] = query.itemId;
        queriesCount++;
      }

      // Note: it's not enough to just check for queriesCount, since we may
      // find an invalid query just after accounting for a sufficient number of
      // valid ones.  As well as we can't just rely on corrupt since we may find
      // less valid queries than expected.
      if (corrupt || queriesCount != EXPECTED_QUERY_COUNT) {
        // Queries number is wrong, so the left pane must be corrupt.
        // Note: we can't just remove the leftPaneRoot, because some query could
        // have a bad parent, so we have to remove all items one by one.
        queryItems.forEach(safeRemoveItem);
        safeRemoveItem(leftPaneRoot);
      } else {
        // Everything is fine, return the current left pane folder.
        return leftPaneRoot;
      }
    }

    // Create a new left pane folder.
    var callback = {
      // Helper to create an organizer special query.
      create_query: function CB_create_query(aQueryName, aParentId, aQueryUrl) {
        let itemId = bs.insertBookmark(aParentId,
                                       Services.io.newURI(aQueryUrl),
                                       bs.DEFAULT_INDEX,
                                       queries[aQueryName].title);
        // Mark as special organizer query.
        as.setItemAnnotation(itemId, PlacesUIUtils.ORGANIZER_QUERY_ANNO, aQueryName,
                             0, as.EXPIRE_NEVER);
        // We should never backup this, since it changes between profiles.
        as.setItemAnnotation(itemId, PlacesUtils.EXCLUDE_FROM_BACKUP_ANNO, 1,
                             0, as.EXPIRE_NEVER);
        // Add to the queries map.
        PlacesUIUtils.leftPaneQueries[aQueryName] = itemId;
        return itemId;
      },

      // Helper to create an organizer special folder.
      create_folder: function CB_create_folder(aFolderName, aParentId, aIsRoot) {
              // Left Pane Root Folder.
        let folderId = bs.createFolder(aParentId,
                                       queries[aFolderName].title,
                                       bs.DEFAULT_INDEX);
        // We should never backup this, since it changes between profiles.
        as.setItemAnnotation(folderId, PlacesUtils.EXCLUDE_FROM_BACKUP_ANNO, 1,
                             0, as.EXPIRE_NEVER);

        if (aIsRoot) {
          // Mark as special left pane root.
          as.setItemAnnotation(folderId, PlacesUIUtils.ORGANIZER_FOLDER_ANNO,
                               PlacesUIUtils.ORGANIZER_LEFTPANE_VERSION,
                               0, as.EXPIRE_NEVER);
        } else {
          // Mark as special organizer folder.
          as.setItemAnnotation(folderId, PlacesUIUtils.ORGANIZER_QUERY_ANNO, aFolderName,
                           0, as.EXPIRE_NEVER);
          PlacesUIUtils.leftPaneQueries[aFolderName] = folderId;
        }
        return folderId;
      },

      runBatched: function CB_runBatched(aUserData) {
        delete PlacesUIUtils.leftPaneQueries;
        PlacesUIUtils.leftPaneQueries = { };

        // Left Pane Root Folder.
        leftPaneRoot = this.create_folder("PlacesRoot", bs.placesRoot, true);

        // History Query.
        this.create_query("History", leftPaneRoot,
                          "place:type=" +
                          Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_QUERY +
                          "&sort=" +
                          Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING);

        // Downloads.
        this.create_query("Downloads", leftPaneRoot,
                          "place:transition=" +
                          Ci.nsINavHistoryService.TRANSITION_DOWNLOAD +
                          "&sort=" +
                          Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING);

        // Tags Query.
        this.create_query("Tags", leftPaneRoot,
                          "place:type=" +
                          Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_QUERY +
                          "&sort=" +
                          Ci.nsINavHistoryQueryOptions.SORT_BY_TITLE_ASCENDING);

        // All Bookmarks Folder.
        this.create_query("AllBookmarks", leftPaneRoot,
                          "place:type=" +
                          Ci.nsINavHistoryQueryOptions.RESULTS_AS_ROOTS_QUERY);
      }
    };
    bs.runInBatchMode(callback, null);

    return leftPaneRoot;
  },

  /**
   * If an item is a left-pane query, returns the name of the query
   * or an empty string if not.
   *
   * @param aItemId id of a container
   * @return the name of the query, or empty string if not a left-pane query
   */
  getLeftPaneQueryNameFromId: function PUIU_getLeftPaneQueryNameFromId(aItemId) {
    var queryName = "";
    // If the let pane hasn't been built, use the annotation service
    // directly, to avoid building the left pane too early.
    if (Object.getOwnPropertyDescriptor(this, "leftPaneFolderId").value === undefined) {
      try {
        queryName = PlacesUtils.annotations.
                                getItemAnnotation(aItemId, this.ORGANIZER_QUERY_ANNO);
      } catch (ex) {
        // doesn't have the annotation
        queryName = "";
      }
    } else {
      // If the left pane has already been built, use the name->id map
      // cached in PlacesUIUtils.
      for (let [name, id] of Object.entries(this.leftPaneQueries)) {
        if (aItemId == id)
          queryName = name;
      }
    }
    return queryName;
  },

  shouldShowTabsFromOtherComputersMenuitem() {
    let weaveOK = Weave.Status.checkSetup() != Weave.CLIENT_NOT_CONFIGURED &&
                  Weave.Svc.Prefs.get("firstSync", "") != "notReady";
    return weaveOK;
  },

  /**
   * WARNING TO ADDON AUTHORS: DO NOT USE THIS METHOD. IT'S LIKELY TO BE REMOVED IN A
   * FUTURE RELEASE.
   *
   * Checks if a place: href represents a folder shortcut.
   *
   * @param queryString
   *        the query string to check (a place: href)
   * @return whether or not queryString represents a folder shortcut.
   * @throws if queryString is malformed.
   */
  isFolderShortcutQueryString(queryString) {
    // Based on GetSimpleBookmarksQueryFolder in nsNavHistory.cpp.

    let queriesParam = { }, optionsParam = { };
    PlacesUtils.history.queryStringToQueries(queryString,
                                             queriesParam,
                                             { },
                                             optionsParam);
    let queries = queries.value;
    if (queries.length == 0)
      throw new Error(`Invalid place: uri: ${queryString}`);
    return queries.length == 1 &&
           queries[0].folderCount == 1 &&
           !queries[0].hasBeginTime &&
           !queries[0].hasEndTime &&
           !queries[0].hasDomain &&
           !queries[0].hasURI &&
           !queries[0].hasSearchTerms &&
           !queries[0].tags.length == 0 &&
           optionsParam.value.maxResults == 0;
  },

  /**
   * Helpers for consumers of editBookmarkOverlay which don't have a node as their input.
   *
   * Given a bookmark object for either a url bookmark or a folder, returned by
   * Bookmarks.fetch (see Bookmark.jsm), this creates a node-like object suitable for
   * initialising the edit overlay with it.
   *
   * @param aFetchInfo
   *        a bookmark object returned by Bookmarks.fetch.
   * @return a node-like object suitable for initialising editBookmarkOverlay.
   * @throws if aFetchInfo is representing a separator.
   */
  async promiseNodeLikeFromFetchInfo(aFetchInfo) {
    if (aFetchInfo.itemType == PlacesUtils.bookmarks.TYPE_SEPARATOR)
      throw new Error("promiseNodeLike doesn't support separators");

    let parent = {
      itemId: await PlacesUtils.promiseItemId(aFetchInfo.parentGuid),
      bookmarkGuid: aFetchInfo.parentGuid,
      type: Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER
    };

    return Object.freeze({
      itemId: await PlacesUtils.promiseItemId(aFetchInfo.guid),
      bookmarkGuid: aFetchInfo.guid,
      title: aFetchInfo.title,
      uri: aFetchInfo.url !== undefined ? aFetchInfo.url.href : "",

      get type() {
        if (aFetchInfo.itemType == PlacesUtils.bookmarks.TYPE_FOLDER)
          return Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER;

        if (this.uri.length == 0)
          throw new Error("Unexpected item type");

        if (/^place:/.test(this.uri)) {
          if (this.isFolderShortcutQueryString(this.uri))
            return Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT;

          return Ci.nsINavHistoryResultNode.RESULT_TYPE_QUERY;
        }

        return Ci.nsINavHistoryResultNode.RESULT_TYPE_URI;
      },

      get parent() {
        return parent;
      }
    });
  },

  /**
   * This function wraps potentially large places transaction operations
   * with batch notifications to the result node, hence switching the views
   * to batch mode.
   *
   * @param {nsINavHistoryResult} resultNode The result node to turn on batching.
   * @note If resultNode is not supplied, the function will pass-through to
   *       functionToWrap.
   * @param {Integer} itemsBeingChanged The count of items being changed. If the
   *                                    count is lower than a threshold, then
   *                                    batching won't be set.
   * @param {Function} functionToWrap The function to
   */
  async batchUpdatesForNode(resultNode, itemsBeingChanged, functionToWrap) {
    if (!resultNode) {
      await functionToWrap();
      return;
    }

    resultNode = resultNode.QueryInterface(Ci.nsINavBookmarkObserver);

    if (itemsBeingChanged > ITEM_CHANGED_BATCH_NOTIFICATION_THRESHOLD) {
      resultNode.onBeginUpdateBatch();
    }

    try {
      await functionToWrap();
    } finally {
      if (itemsBeingChanged > ITEM_CHANGED_BATCH_NOTIFICATION_THRESHOLD) {
        resultNode.onEndUpdateBatch();
      }
    }
  },

  /**
   * Constructs a Places Transaction for the drop or paste of a blob of data
   * into a container.
   *
   * @param   aData
   *          The unwrapped data blob of dropped or pasted data.
   * @param   aNewParentGuid
   *          GUID of the container the data was dropped or pasted into.
   * @param   aIndex
   *          The index within the container the item was dropped or pasted at.
   * @param   aCopy
   *          The drag action was copy, so don't move folders or links.
   *
   * @return  a Places Transaction that can be transacted for performing the
   *          move/insert command.
   */
  getTransactionForData(aData, aNewParentGuid, aIndex, aCopy) {
    if (!this.SUPPORTED_FLAVORS.includes(aData.type))
      throw new Error(`Unsupported '${aData.type}' data type`);

    if ("itemGuid" in aData && "instanceId" in aData &&
        aData.instanceId == PlacesUtils.instanceId) {
      if (!this.PLACES_FLAVORS.includes(aData.type))
        throw new Error(`itemGuid unexpectedly set on ${aData.type} data`);

      let info = { guid: aData.itemGuid,
                   newParentGuid: aNewParentGuid,
                   newIndex: aIndex };
      if (aCopy) {
        info.excludingAnnotation = "Places/SmartBookmark";
        return PlacesTransactions.Copy(info);
      }
      return PlacesTransactions.Move(info);
    }

    // Since it's cheap and harmless, we allow the paste of separators and
    // bookmarks from builds that use legacy transactions (i.e. when itemGuid
    // was not set on PLACES_FLAVORS data). Containers are a different story,
    // and thus disallowed.
    if (aData.type == PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER)
      throw new Error("Can't copy a container from a legacy-transactions build");

    if (aData.type == PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR) {
      return PlacesTransactions.NewSeparator({ parentGuid: aNewParentGuid,
                                               index: aIndex });
    }

    let title = aData.type != PlacesUtils.TYPE_UNICODE ? aData.title
                                                       : aData.uri;
    return PlacesTransactions.NewBookmark({ url: Services.io.newURI(aData.uri),
                                            title,
                                            parentGuid: aNewParentGuid,
                                            index: aIndex });
  },

  /**
   * Processes a set of transfer items that have been dropped or pasted.
   * Batching will be applied where necessary.
   *
   * @param {Array} items A list of unwrapped nodes to process.
   * @param {Object} insertionPoint The requested point for insertion.
   * @param {Boolean} doCopy Set to true to copy the items, false will move them
   *                         if possible.
   * @paramt {Object} view The view that should be used for batching.
   * @return {Array} Returns an empty array when the insertion point is a tag, else
   *                 returns an array of copied or moved guids.
   */
  async handleTransferItems(items, insertionPoint, doCopy, view) {
    let transactions;
    let itemsCount;
    if (insertionPoint.isTag) {
      let urls = items.filter(item => "uri" in item).map(item => item.uri);
      itemsCount = urls.length;
      transactions = [PlacesTransactions.Tag({ urls, tag: insertionPoint.tagName })];
    } else {
      let insertionIndex = await insertionPoint.getIndex();
      itemsCount = items.length;
      transactions = await getTransactionsForTransferItems(
        items, insertionIndex, insertionPoint.guid, doCopy);
    }

    // Check if we actually have something to add, if we don't it probably wasn't
    // valid, or it was moving to the same location, so just ignore it.
    if (!transactions.length) {
      return [];
    }

    let guidsToSelect = [];
    let resultForBatching = getResultForBatching(view);

    // If we're inserting into a tag, we don't get the guid, so we'll just
    // pass the transactions direct to the batch function.
    let batchingItem = transactions;
    if (!insertionPoint.isTag) {
      // If we're not a tag, then we need to get the ids of the items to select.
      batchingItem = async () => {
        for (let transaction of transactions) {
          let guid = await transaction.transact();
          if (guid) {
            guidsToSelect.push(guid);
          }
        }
      };
    }

    await this.batchUpdatesForNode(resultForBatching, itemsCount, async () => {
      await PlacesTransactions.batch(batchingItem);
    });

    return guidsToSelect;
  },
};

// These are lazy getters to avoid importing PlacesUtils immediately.
XPCOMUtils.defineLazyGetter(PlacesUIUtils, "PLACES_FLAVORS", () => {
  return [PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER,
          PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR,
          PlacesUtils.TYPE_X_MOZ_PLACE];
});
XPCOMUtils.defineLazyGetter(PlacesUIUtils, "URI_FLAVORS", () => {
  return [PlacesUtils.TYPE_X_MOZ_URL,
          TAB_DROP_TYPE,
          PlacesUtils.TYPE_UNICODE];
});
XPCOMUtils.defineLazyGetter(PlacesUIUtils, "SUPPORTED_FLAVORS", () => {
  return [...PlacesUIUtils.PLACES_FLAVORS,
          ...PlacesUIUtils.URI_FLAVORS];
});

XPCOMUtils.defineLazyGetter(PlacesUIUtils, "ellipsis", function() {
  return Services.prefs.getComplexValue("intl.ellipsis",
                                        Ci.nsIPrefLocalizedString).data;
});

XPCOMUtils.defineLazyPreferenceGetter(PlacesUIUtils, "loadBookmarksInBackground",
                                      PREF_LOAD_BOOKMARKS_IN_BACKGROUND, false);
XPCOMUtils.defineLazyPreferenceGetter(PlacesUIUtils, "loadBookmarksInTabs",
                                      PREF_LOAD_BOOKMARKS_IN_TABS, false);
XPCOMUtils.defineLazyPreferenceGetter(PlacesUIUtils, "openInTabClosesMenu",
  "browser.bookmarks.openInTabClosesMenu", false);

/**
 * Determines if an unwrapped node can be moved.
 *
 * @param unwrappedNode
 *        A node unwrapped by PlacesUtils.unwrapNodes().
 * @return True if the node can be moved, false otherwise.
 */
function canMoveUnwrappedNode(unwrappedNode) {
  if ((unwrappedNode.concreteGuid && PlacesUtils.isRootItem(unwrappedNode.concreteGuid)) ||
      unwrappedNode.id <= 0 || PlacesUtils.isRootItem(unwrappedNode.id)) {
    return false;
  }

  let parentGuid = unwrappedNode.parentGuid;
  // If there's no parent Guid, this was likely a virtual query that returns
  // bookmarks, such as a tags query.
  if (!parentGuid ||
      parentGuid == PlacesUtils.bookmarks.rootGuid) {
    return false;
  }
  // leftPaneFolderId and allBookmarksFolderId are lazy getters running
  // at least a synchronous DB query. Therefore we don't want to invoke
  // them first, especially because isCommandEnabled may be called way
  // before the left pane folder is even necessary.
  if (typeof Object.getOwnPropertyDescriptor(PlacesUIUtils, "leftPaneFolderId").get != "function" &&
      (unwrappedNode.parent == PlacesUIUtils.leftPaneFolderId)) {
    return false;
  }
  return true;
}

/**
 * This gets the most appropriate item for using for batching. In the case of multiple
 * views being related, the method returns the most expensive result to batch.
 * For example, if it detects the left-hand library pane, then it will look for
 * and return the reference to the right-hand pane.
 *
 * @param {Object} viewOrElement The item to check.
 * @return {Object} Will return the best result node to batch, or null
 *                  if one could not be found.
 */
function getResultForBatching(viewOrElement) {
  if (viewOrElement && viewOrElement instanceof Ci.nsIDOMElement &&
      viewOrElement.id === "placesList") {
    // Note: fall back to the existing item if we can't find the right-hane pane.
    viewOrElement = viewOrElement.ownerDocument.getElementById("placeContent") || viewOrElement;
  }

  if (viewOrElement && viewOrElement.result) {
    return viewOrElement.result;
  }

  return null;
}

/**
 * Processes a set of transfer items and returns transactions to insert or
 * move them.
 *
 * @param {Array} items A list of unwrapped nodes to get transactions for.
 * @param {Integer} insertionIndex The requested index for insertion.
 * @param {String} insertionParentGuid The guid of the parent folder to insert
 *                                     or move the items to.
 * @param {Boolean} doCopy Set to true to copy the items, false will move them
 *                         if possible.
 * @return {Array} Returns an array of created PlacesTransactions.
 */
async function getTransactionsForTransferItems(items, insertionIndex,
                                               insertionParentGuid, doCopy) {
  let transactions = [];
  let index = insertionIndex;

  for (let item of items) {
    if (index != -1 && item.itemGuid) {
      // Note: we use the parent from the existing bookmark as the sidebar
      // gives us an unwrapped.parent that is actually a query and not the real
      // parent.
      let existingBookmark = await PlacesUtils.bookmarks.fetch(item.itemGuid);

      // If we're dropping on the same folder, then we may need to adjust
      // the index to insert at the correct place.
      if (existingBookmark && insertionParentGuid == existingBookmark.parentGuid) {
        if (index > existingBookmark.index) {
          // If we're dragging down, we need to go one lower to insert at
          // the real point as moving the element changes the index of
          // everything below by 1.
          index--;
        } else if (index == existingBookmark.index) {
          // This isn't moving so we skip it.
          continue;
        }
      }
    }

    // If this is not a copy, check for safety that we can move the
    // source, otherwise report an error and fallback to a copy.
    if (!doCopy && !canMoveUnwrappedNode(item)) {
      Cu.reportError("Tried to move an unmovable Places " +
                     "node, reverting to a copy operation.");
      doCopy = true;
    }
    transactions.push(
      PlacesUIUtils.getTransactionForData(item,
                                          insertionParentGuid,
                                          index,
                                          doCopy));

    if (index != -1 && item.itemGuid) {
      index++;
    }
  }
  return transactions;
}
PK
!<=�h�|�|�modules/PluginContent.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = [ "PluginContent" ];

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/Timer.jsm");
ChromeUtils.import("resource://gre/modules/BrowserUtils.jsm");

Cu.importGlobalProperties(["InspectorUtils"]);

XPCOMUtils.defineLazyGetter(this, "gNavigatorBundle", function() {
  const url = "chrome://browser/locale/browser.properties";
  return Services.strings.createBundle(url);
});

ChromeUtils.defineModuleGetter(this, "AppConstants",
  "resource://gre/modules/AppConstants.jsm");

var PluginContent = function(global) {
  this.init(global);
};

const FLASH_MIME_TYPE = "application/x-shockwave-flash";
const REPLACEMENT_STYLE_SHEET = Services.io.newURI("chrome://pluginproblem/content/pluginReplaceBinding.css");

const OVERLAY_DISPLAY = {
  HIDDEN: 0, // The overlay will be transparent
  BLANK: 1, // The overlay will be just a grey box
  TINY: 2, // The overlay with a 16x16 plugin icon
  REDUCED: 3, // The overlay with a 32x32 plugin icon
  NOTEXT: 4, // The overlay with a 48x48 plugin icon and the close button
  FULL: 5, // The full overlay: 48x48 plugin icon, close button and label
};

PluginContent.prototype = {
  init(global) {
    this.global = global;
    // Need to hold onto the content window or else it'll get destroyed
    this.content = this.global.content;
    // Cache of plugin actions for the current page.
    this.pluginData = new Map();
    // Cache of plugin crash information sent from the parent
    this.pluginCrashData = new Map();

    // Note that the XBL binding is untrusted
    global.addEventListener("PluginBindingAttached", this, true, true);
    global.addEventListener("PluginPlaceholderReplaced", this, true, true);
    global.addEventListener("PluginCrashed", this, true);
    global.addEventListener("PluginOutdated", this, true);
    global.addEventListener("PluginInstantiated", this, true);
    global.addEventListener("PluginRemoved", this, true);
    global.addEventListener("pagehide", this, true);
    global.addEventListener("pageshow", this, true);
    global.addEventListener("unload", this);
    global.addEventListener("HiddenPlugin", this, true);

    global.addMessageListener("BrowserPlugins:ActivatePlugins", this);
    global.addMessageListener("BrowserPlugins:NotificationShown", this);
    global.addMessageListener("BrowserPlugins:ContextMenuCommand", this);
    global.addMessageListener("BrowserPlugins:NPAPIPluginProcessCrashed", this);
    global.addMessageListener("BrowserPlugins:CrashReportSubmitted", this);
    global.addMessageListener("BrowserPlugins:Test:ClearCrashData", this);

    Services.obs.addObserver(this, "decoder-doctor-notification");
  },

  uninit() {
    let global = this.global;

    global.removeEventListener("PluginBindingAttached", this, true);
    global.removeEventListener("PluginPlaceholderReplaced", this, true, true);
    global.removeEventListener("PluginCrashed", this, true);
    global.removeEventListener("PluginOutdated", this, true);
    global.removeEventListener("PluginInstantiated", this, true);
    global.removeEventListener("PluginRemoved", this, true);
    global.removeEventListener("pagehide", this, true);
    global.removeEventListener("pageshow", this, true);
    global.removeEventListener("unload", this);
    global.removeEventListener("HiddenPlugin", this, true);

    global.removeMessageListener("BrowserPlugins:ActivatePlugins", this);
    global.removeMessageListener("BrowserPlugins:NotificationShown", this);
    global.removeMessageListener("BrowserPlugins:ContextMenuCommand", this);
    global.removeMessageListener("BrowserPlugins:NPAPIPluginProcessCrashed", this);
    global.removeMessageListener("BrowserPlugins:CrashReportSubmitted", this);
    global.removeMessageListener("BrowserPlugins:Test:ClearCrashData", this);

    Services.obs.removeObserver(this, "decoder-doctor-notification");

    delete this.global;
    delete this.content;
  },

  receiveMessage(msg) {
    switch (msg.name) {
      case "BrowserPlugins:ActivatePlugins":
        this.activatePlugins(msg.data.pluginInfo, msg.data.newState);
        break;
      case "BrowserPlugins:NotificationShown":
        setTimeout(() => this.updateNotificationUI(), 0);
        break;
      case "BrowserPlugins:ContextMenuCommand":
        let contextMenu = this.global.contextMenu;

        switch (msg.data.command) {
          case "play":
            this._showClickToPlayNotification(contextMenu.getTarget(msg, "plugin"), true);
            break;
          case "hide":
            this.hideClickToPlayOverlay(contextMenu.getTarget(msg, "plugin"));
            break;
        }
        break;
      case "BrowserPlugins:NPAPIPluginProcessCrashed":
        this.NPAPIPluginProcessCrashed({
          pluginName: msg.data.pluginName,
          runID: msg.data.runID,
          state: msg.data.state,
        });
        break;
      case "BrowserPlugins:CrashReportSubmitted":
        this.NPAPIPluginCrashReportSubmitted({
          runID: msg.data.runID,
          state: msg.data.state,
        });
        break;
      case "BrowserPlugins:Test:ClearCrashData":
        // This message should ONLY ever be sent by automated tests.
        if (Services.prefs.getBoolPref("plugins.testmode")) {
          this.pluginCrashData.clear();
        }
    }
  },

  observe: function observe(aSubject, aTopic, aData) {
    switch (aTopic) {
      case "decoder-doctor-notification":
        let data = JSON.parse(aData);
        let type = data.type.toLowerCase();
        if (type == "cannot-play" &&
            this.haveShownNotification &&
            aSubject.top.document == this.content.document &&
            data.formats.toLowerCase().includes("application/x-mpegurl", 0)) {
          this.global.content.pluginRequiresReload = true;
          this.updateNotificationUI(this.content.document);
        }
    }
  },

  onPageShow(event) {
    // Ignore events that aren't from the main document.
    if (!this.content || event.target != this.content.document) {
      return;
    }

    // The PluginClickToPlay events are not fired when navigating using the
    // BF cache. |persisted| is true when the page is loaded from the
    // BF cache, so this code reshows the notification if necessary.
    if (event.persisted) {
      this.reshowClickToPlayNotification();
    }
  },

  onPageHide(event) {
    // Ignore events that aren't from the main document.
    if (!this.content || event.target != this.content.document) {
      return;
    }

    this.clearPluginCaches();
    this.haveShownNotification = false;
  },

  getPluginUI(plugin, anonid) {
    return plugin.ownerDocument.
           getAnonymousElementByAttribute(plugin, "anonid", anonid);
  },

  _getPluginInfo(pluginElement) {
    if (ChromeUtils.getClassName(pluginElement) === "HTMLAnchorElement") {
      // Anchor elements are our place holders, and we only have them for Flash
      let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
      return {
        pluginName: "Shockwave Flash",
        mimetype: FLASH_MIME_TYPE,
        permissionString: pluginHost.getPermissionStringForType(FLASH_MIME_TYPE)
      };
    }
    let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
    pluginElement.QueryInterface(Ci.nsIObjectLoadingContent);

    let tagMimetype;
    let pluginName = gNavigatorBundle.GetStringFromName("pluginInfo.unknownPlugin");
    let pluginTag = null;
    let permissionString = null;
    let fallbackType = null;
    let blocklistState = null;

    tagMimetype = pluginElement.actualType;
    if (tagMimetype == "") {
      tagMimetype = pluginElement.type;
    }

    if (this.isKnownPlugin(pluginElement)) {
      pluginTag = pluginHost.getPluginTagForType(pluginElement.actualType);
      pluginName = BrowserUtils.makeNicePluginName(pluginTag.name);

      // Convert this from nsIPluginTag so it can be serialized.
      let properties = ["name", "description", "filename", "version", "enabledState", "niceName"];
      let pluginTagCopy = {};
      for (let prop of properties) {
        pluginTagCopy[prop] = pluginTag[prop];
      }
      pluginTag = pluginTagCopy;

      permissionString = pluginHost.getPermissionStringForType(pluginElement.actualType);
      fallbackType = pluginElement.defaultFallbackType;
      blocklistState = pluginHost.getBlocklistStateForType(pluginElement.actualType);
      // Make state-softblocked == state-notblocked for our purposes,
      // they have the same UI. STATE_OUTDATED should not exist for plugin
      // items, but let's alias it anyway, just in case.
      if (blocklistState == Ci.nsIBlocklistService.STATE_SOFTBLOCKED ||
          blocklistState == Ci.nsIBlocklistService.STATE_OUTDATED) {
        blocklistState = Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
      }
    }

    return { mimetype: tagMimetype,
             pluginName,
             pluginTag,
             permissionString,
             fallbackType,
             blocklistState,
           };
  },

  /**
   * _getPluginInfoForTag is called when iterating the plugins for a document,
   * and what we get from nsIDOMWindowUtils is an nsIPluginTag, and not an
   * nsIObjectLoadingContent. This only should happen if the plugin is
   * click-to-play (see bug 1186948).
   */
  _getPluginInfoForTag(pluginTag, tagMimetype) {
    let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);

    let pluginName = gNavigatorBundle.GetStringFromName("pluginInfo.unknownPlugin");
    let permissionString = null;
    let blocklistState = null;

    if (pluginTag) {
      pluginName = BrowserUtils.makeNicePluginName(pluginTag.name);

      permissionString = pluginHost.getPermissionStringForTag(pluginTag);
      blocklistState = pluginTag.blocklistState;

      // Convert this from nsIPluginTag so it can be serialized.
      let properties = ["name", "description", "filename", "version", "enabledState", "niceName"];
      let pluginTagCopy = {};
      for (let prop of properties) {
        pluginTagCopy[prop] = pluginTag[prop];
      }
      pluginTag = pluginTagCopy;

      // Make state-softblocked == state-notblocked for our purposes,
      // they have the same UI. STATE_OUTDATED should not exist for plugin
      // items, but let's alias it anyway, just in case.
      if (blocklistState == Ci.nsIBlocklistService.STATE_SOFTBLOCKED ||
          blocklistState == Ci.nsIBlocklistService.STATE_OUTDATED) {
        blocklistState = Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
      }
    }

    return { mimetype: tagMimetype,
             pluginName,
             pluginTag,
             permissionString,
             // Since we should only have entered _getPluginInfoForTag when
             // examining a click-to-play plugin, we can safely hard-code
             // this fallback type, since we don't actually have an
             // nsIObjectLoadingContent to check.
             fallbackType: Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
             blocklistState,
           };
  },

  /**
   * Update the visibility of the plugin overlay.
   */
  setVisibility(plugin, overlay, overlayDisplayState) {
    overlay.classList.toggle("visible", overlayDisplayState != OVERLAY_DISPLAY.HIDDEN);
    if (overlayDisplayState != OVERLAY_DISPLAY.HIDDEN) {
      overlay.removeAttribute("dismissed");
    }
  },

  /**
   * Adjust the style in which the overlay will be displayed. It might be adjusted
   * based on its size, or if there's some other element covering all corners of
   * the overlay.
   *
   * This function will handle adjusting the style of the overlay, but will
   * not handle hiding it. That is done by setVisibility with the return value
   * from this function.
   *
   * @returns A value from OVERLAY_DISPLAY.
   */
  computeAndAdjustOverlayDisplay(plugin, overlay) {
    let fallbackType = plugin.pluginFallbackType;
    if (plugin.pluginFallbackTypeOverride !== undefined) {
      fallbackType = plugin.pluginFallbackTypeOverride;
    }
    if (fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY_QUIET) {
      return OVERLAY_DISPLAY.HIDDEN;
    }

    // If the overlay size is 0, we haven't done layout yet. Presume that
    // plugins are visible until we know otherwise.
    if (overlay.scrollWidth == 0) {
      return OVERLAY_DISPLAY.FULL;
    }

    let overlayDisplay = OVERLAY_DISPLAY.FULL;

    // Is the <object>'s size too small to hold what we want to show?
    let pluginRect = plugin.getBoundingClientRect();
    let pluginWidth = Math.ceil(pluginRect.width);
    let pluginHeight = Math.ceil(pluginRect.height);

    // We must set the attributes while here inside this function in order
    // for a possible re-style to occur, which will make the scrollWidth/Height
    // checks below correct. Otherwise, we would be requesting e.g. a TINY
    // overlay here, but the default styling would be used, and that would make
    // it overflow, causing it to change to BLANK instead of remaining as TINY.

    if (pluginWidth <= 32 || pluginHeight <= 32) {
      overlay.setAttribute("sizing", "blank");
      overlayDisplay = OVERLAY_DISPLAY.BLANK;
    } else if (pluginWidth <= 80 || pluginHeight <= 60) {
      overlayDisplay = OVERLAY_DISPLAY.TINY;
      overlay.setAttribute("sizing", "tiny");
      overlay.setAttribute("notext", "notext");
    } else if (pluginWidth <= 120 || pluginHeight <= 80) {
      overlayDisplay = OVERLAY_DISPLAY.REDUCED;
      overlay.setAttribute("sizing", "reduced");
      overlay.setAttribute("notext", "notext");
    } else if (pluginWidth <= 240 || pluginHeight <= 160) {
      overlayDisplay = OVERLAY_DISPLAY.NOTEXT;
      overlay.removeAttribute("sizing");
      overlay.setAttribute("notext", "notext");
    } else {
      overlayDisplay = OVERLAY_DISPLAY.FULL;
      overlay.removeAttribute("sizing");
      overlay.removeAttribute("notext");
    }


    // XXX bug 446693. The text-shadow on the submitted-report text at
    //     the bottom causes scrollHeight to be larger than it should be.
    let overflows = (overlay.scrollWidth > pluginWidth) ||
                    (overlay.scrollHeight - 5 > pluginHeight);
    if (overflows) {
      overlay.setAttribute("sizing", "blank");
      return OVERLAY_DISPLAY.BLANK;
    }

    // Is the plugin covered up by other content so that it is not clickable?
    // Floating point can confuse .elementFromPoint, so inset just a bit
    let left = pluginRect.left + 2;
    let right = pluginRect.right - 2;
    let top = pluginRect.top + 2;
    let bottom = pluginRect.bottom - 2;
    let centerX = left + (right - left) / 2;
    let centerY = top + (bottom - top) / 2;
    let points = [[left, top],
                   [left, bottom],
                   [right, top],
                   [right, bottom],
                   [centerX, centerY]];

    let contentWindow = plugin.ownerGlobal;
    let cwu = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                           .getInterface(Ci.nsIDOMWindowUtils);

    for (let [x, y] of points) {
      if (x < 0 || y < 0) {
        continue;
      }
      let el = cwu.elementFromPoint(x, y, true, true);
      if (el === plugin) {
        return overlayDisplay;
      }
    }

    overlay.setAttribute("sizing", "blank");
    return OVERLAY_DISPLAY.BLANK;
  },

  addLinkClickCallback(linkNode, callbackName /* callbackArgs...*/) {
    // XXX just doing (callback)(arg) was giving a same-origin error. bug?
    let self = this;
    let callbackArgs = Array.prototype.slice.call(arguments).slice(2);
    linkNode.addEventListener("click",
                              function(evt) {
                                if (!evt.isTrusted)
                                  return;
                                evt.preventDefault();
                                if (callbackArgs.length == 0)
                                  callbackArgs = [ evt ];
                                (self[callbackName]).apply(self, callbackArgs);
                              },
                              true);

    linkNode.addEventListener("keydown",
                              function(evt) {
                                if (!evt.isTrusted)
                                  return;
                                if (evt.keyCode == evt.DOM_VK_RETURN) {
                                  evt.preventDefault();
                                  if (callbackArgs.length == 0)
                                    callbackArgs = [ evt ];
                                  evt.preventDefault();
                                  (self[callbackName]).apply(self, callbackArgs);
                                }
                              },
                              true);
  },

  // Helper to get the binding handler type from a plugin object
  _getBindingType(plugin) {
    if (!(plugin instanceof Ci.nsIObjectLoadingContent))
      return null;

    switch (plugin.pluginFallbackType) {
      case Ci.nsIObjectLoadingContent.PLUGIN_UNSUPPORTED:
        return "PluginNotFound";
      case Ci.nsIObjectLoadingContent.PLUGIN_DISABLED:
        return "PluginDisabled";
      case Ci.nsIObjectLoadingContent.PLUGIN_BLOCKLISTED:
        return "PluginBlocklisted";
      case Ci.nsIObjectLoadingContent.PLUGIN_OUTDATED:
        return "PluginOutdated";
      case Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY:
      case Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY_QUIET:
        return "PluginClickToPlay";
      case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE:
        return "PluginVulnerableUpdatable";
      case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE:
        return "PluginVulnerableNoUpdate";
      default:
        // Not all states map to a handler
        return null;
    }
  },

  handleEvent(event) {
    let eventType = event.type;

    if (eventType == "unload") {
      this.uninit();
      return;
    }

    if (eventType == "pagehide") {
      this.onPageHide(event);
      return;
    }

    if (eventType == "pageshow") {
      this.onPageShow(event);
      return;
    }

    if (eventType == "PluginRemoved") {
      this.updateNotificationUI(event.target);
      return;
    }

    if (eventType == "click") {
      this.onOverlayClick(event);
      return;
    }

    if (eventType == "PluginCrashed" &&
        !(event.target instanceof Ci.nsIObjectLoadingContent)) {
      // If the event target is not a plugin object (i.e., an <object> or
      // <embed> element), this call is for a window-global plugin.
      this.onPluginCrashed(event.target, event);
      return;
    }

    if (eventType == "HiddenPlugin") {
      let win = event.target.defaultView;
      if (!win.mozHiddenPluginTouched) {
        let pluginTag = event.tag.QueryInterface(Ci.nsIPluginTag);
        if (win.top.document != this.content.document) {
          return;
        }
        this._showClickToPlayNotification(pluginTag, false);
        let winUtils = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
        try {
          winUtils.loadSheet(REPLACEMENT_STYLE_SHEET, win.AGENT_SHEET);
          win.mozHiddenPluginTouched = true;
        } catch (e) {
          Cu.reportError("Error adding plugin replacement style sheet: " + e);
        }
      }
    }

    let plugin = event.target;

    if (eventType == "PluginPlaceholderReplaced") {
      plugin.removeAttribute("href");
      let overlay = this.getPluginUI(plugin, "main");
      this.setVisibility(plugin, overlay, OVERLAY_DISPLAY.FULL);
      // Add pseudo class so our styling will take effect
      InspectorUtils.addPseudoClassLock(plugin, "-moz-handler-clicktoplay");
      overlay.addEventListener("click", this, true);
      return;
    }

    if (!(plugin instanceof Ci.nsIObjectLoadingContent))
      return;

    if (eventType == "PluginBindingAttached") {
      // The plugin binding fires this event when it is created.
      // As an untrusted event, ensure that this object actually has a binding
      // and make sure we don't handle it twice
      let overlay = this.getPluginUI(plugin, "main");
      if (!overlay || overlay._bindingHandled) {
        return;
      }
      overlay._bindingHandled = true;

      // Lookup the handler for this binding
      eventType = this._getBindingType(plugin);
      if (!eventType) {
        // Not all bindings have handlers
        return;
      }
    }

    let shouldShowNotification = false;
    switch (eventType) {
      case "PluginCrashed":
        this.onPluginCrashed(plugin, event);
        break;

      case "PluginNotFound": {
        /* NOP */
        break;
      }

      case "PluginBlocklisted":
      case "PluginOutdated":
        shouldShowNotification = true;
        break;

      case "PluginVulnerableUpdatable":
        let updateLink = this.getPluginUI(plugin, "checkForUpdatesLink");
        let { pluginTag } = this._getPluginInfo(plugin);
        this.addLinkClickCallback(updateLink, "forwardCallback",
                                  "openPluginUpdatePage", pluginTag);
        /* FALLTHRU */

      case "PluginVulnerableNoUpdate":
      case "PluginClickToPlay":
        this._handleClickToPlayEvent(plugin);
        let pluginName = this._getPluginInfo(plugin).pluginName;
        let messageString = gNavigatorBundle.formatStringFromName("PluginClickToActivate2", [pluginName], 1);
        let overlayText = this.getPluginUI(plugin, "clickToPlay");
        overlayText.textContent = messageString;
        if (eventType == "PluginVulnerableUpdatable" ||
            eventType == "PluginVulnerableNoUpdate") {
          let vulnerabilityString = gNavigatorBundle.GetStringFromName(eventType);
          let vulnerabilityText = this.getPluginUI(plugin, "vulnerabilityStatus");
          vulnerabilityText.textContent = vulnerabilityString;
        }
        shouldShowNotification = true;
        break;

      case "PluginDisabled":
        let manageLink = this.getPluginUI(plugin, "managePluginsLink");
        this.addLinkClickCallback(manageLink, "forwardCallback", "managePlugins");
        shouldShowNotification = true;
        break;

      case "PluginInstantiated":
        shouldShowNotification = true;
        break;
    }

    // Show the in-content UI if it's not too big. The crashed plugin handler already did this.
    let overlay = this.getPluginUI(plugin, "main");
    if (eventType != "PluginCrashed") {
      if (overlay != null) {
        this.setVisibility(plugin, overlay,
                           this.computeAndAdjustOverlayDisplay(plugin, overlay));
        let resizeListener = () => {
          this.setVisibility(plugin, overlay,
            this.computeAndAdjustOverlayDisplay(plugin, overlay));
          this.updateNotificationUI();
        };
        plugin.addEventListener("overflow", resizeListener);
        plugin.addEventListener("underflow", resizeListener);
      }
    }

    let closeIcon = this.getPluginUI(plugin, "closeIcon");
    if (closeIcon) {
      closeIcon.addEventListener("click", clickEvent => {
        if (clickEvent.button == 0 && clickEvent.isTrusted) {
          this.hideClickToPlayOverlay(plugin);
          overlay.setAttribute("dismissed", "true");
        }
      }, true);
    }

    if (shouldShowNotification) {
      this._showClickToPlayNotification(plugin, false);
    }
  },

  isKnownPlugin(objLoadingContent) {
    return (objLoadingContent.getContentTypeForMIMEType(objLoadingContent.actualType) ==
            Ci.nsIObjectLoadingContent.TYPE_PLUGIN);
  },

  canActivatePlugin(objLoadingContent) {
    // if this isn't a known plugin, we can't activate it
    // (this also guards pluginHost.getPermissionStringForType against
    // unexpected input)
    if (!this.isKnownPlugin(objLoadingContent))
      return false;

    let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
    let permissionString = pluginHost.getPermissionStringForType(objLoadingContent.actualType);
    let principal = objLoadingContent.ownerGlobal.top.document.nodePrincipal;
    let pluginPermission = Services.perms.testPermissionFromPrincipal(principal, permissionString);

    let isFallbackTypeValid =
      objLoadingContent.pluginFallbackType >= Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY &&
      objLoadingContent.pluginFallbackType <= Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY_QUIET;

    return !objLoadingContent.activated &&
           pluginPermission != Ci.nsIPermissionManager.DENY_ACTION &&
           isFallbackTypeValid;
  },

  hideClickToPlayOverlay(plugin) {
    let overlay = this.getPluginUI(plugin, "main");
    if (overlay) {
      overlay.classList.remove("visible");
    }
  },

  // Forward a link click callback to the chrome process.
  forwardCallback(name, pluginTag) {
    this.global.sendAsyncMessage("PluginContent:LinkClickCallback",
      { name, pluginTag });
  },

  submitReport: function submitReport(plugin) {
    if (!AppConstants.MOZ_CRASHREPORTER) {
      return;
    }
    if (!plugin) {
      Cu.reportError("Attempted to submit crash report without an associated plugin.");
      return;
    }
    if (!(plugin instanceof Ci.nsIObjectLoadingContent)) {
      Cu.reportError("Attempted to submit crash report on plugin that does not" +
                     "implement nsIObjectLoadingContent.");
      return;
    }

    let runID = plugin.runID;
    let submitURLOptIn = this.getPluginUI(plugin, "submitURLOptIn").checked;
    let keyVals = {};
    let userComment = this.getPluginUI(plugin, "submitComment").value.trim();
    if (userComment)
      keyVals.PluginUserComment = userComment;
    if (submitURLOptIn)
      keyVals.PluginContentURL = plugin.ownerDocument.URL;

    this.global.sendAsyncMessage("PluginContent:SubmitReport",
                                 { runID, keyVals, submitURLOptIn });
  },

  reloadPage() {
    this.global.content.location.reload();
  },

  // Event listener for click-to-play plugins.
  _handleClickToPlayEvent(plugin) {
    let doc = plugin.ownerDocument;
    let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
    let permissionString;
    if (ChromeUtils.getClassName(plugin) === "HTMLAnchorElement") {
      // We only have replacement content for Flash installs
      permissionString = pluginHost.getPermissionStringForType(FLASH_MIME_TYPE);
    } else {
      let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
      // guard against giving pluginHost.getPermissionStringForType a type
      // not associated with any known plugin
      if (!this.isKnownPlugin(objLoadingContent))
        return;
      permissionString = pluginHost.getPermissionStringForType(objLoadingContent.actualType);
    }

    let principal = doc.defaultView.top.document.nodePrincipal;
    let pluginPermission = Services.perms.testPermissionFromPrincipal(principal, permissionString);

    let overlay = this.getPluginUI(plugin, "main");

    if (pluginPermission == Ci.nsIPermissionManager.DENY_ACTION ||
        pluginPermission == Ci.nsIObjectLoadingContent.PLUGIN_PERMISSION_PROMPT_ACTION_QUIET) {
      if (overlay) {
        overlay.classList.remove("visible");
      }
      return;
    }

    if (overlay) {
      overlay.addEventListener("click", this, true);
    }
  },

  onOverlayClick(event) {
    let document = event.target.ownerDocument;
    let plugin = document.getBindingParent(event.target);
    let overlay = this.getPluginUI(plugin, "main");
    // Have to check that the target is not the link to update the plugin
    if (!(ChromeUtils.getClassName(event.originalTarget) === "HTMLAnchorElement") &&
        (event.originalTarget.getAttribute("anonid") != "closeIcon") &&
        !overlay.hasAttribute("dismissed") &&
        event.button == 0 &&
        event.isTrusted) {
      this._showClickToPlayNotification(plugin, true);
    event.stopPropagation();
    event.preventDefault();
    }
  },

  reshowClickToPlayNotification() {
    let contentWindow = this.global.content;
    let cwu = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                           .getInterface(Ci.nsIDOMWindowUtils);
    let plugins = cwu.plugins;
    for (let plugin of plugins) {
      let overlay = this.getPluginUI(plugin, "main");
      if (overlay)
        overlay.removeEventListener("click", this, true);
      let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
      if (this.canActivatePlugin(objLoadingContent))
        this._handleClickToPlayEvent(plugin);
    }
    this._showClickToPlayNotification(null, false);
  },

  /**
   * Activate the plugins that the user has specified.
   */
  activatePlugins(pluginInfo, newState) {
    let contentWindow = this.global.content;
    let cwu = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                           .getInterface(Ci.nsIDOMWindowUtils);
    let plugins = cwu.plugins;
    let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);

    let pluginFound = false;
    let placeHolderFound = false;
    for (let plugin of plugins) {
      plugin.QueryInterface(Ci.nsIObjectLoadingContent);
      if (!this.isKnownPlugin(plugin)) {
        continue;
      }
      if (pluginInfo.permissionString == pluginHost.getPermissionStringForType(plugin.actualType)) {
        let overlay = this.getPluginUI(plugin, "main");
        if (ChromeUtils.getClassName(plugin) === "HTMLAnchorElement") {
          placeHolderFound = true;
        } else {
          pluginFound = true;
        }
        if (newState == "block" || newState == "blockalways" || newState == "continueblocking") {
          if (overlay) {
            overlay.addEventListener("click", this, true);
          }
          plugin.pluginFallbackTypeOverride = pluginInfo.fallbackType;
          plugin.reload(true);
        } else if (this.canActivatePlugin(plugin)) {
          if (overlay) {
            overlay.removeEventListener("click", this, true);
          }
          plugin.playPlugin();
        }
      }
    }

    // If there are no instances of the plugin on the page any more, what the
    // user probably needs is for us to allow and then refresh. Additionally, if
    // this is content that requires HLS or we replaced the placeholder the page
    // needs to be refreshed for it to insert its plugins
    if (newState != "block" && newState != "blockalways" && newState != "continueblocking" &&
       (!pluginFound || placeHolderFound || contentWindow.pluginRequiresReload)) {
      this.reloadPage();
    }
    this.updateNotificationUI();
  },

  _showClickToPlayNotification(plugin, showNow) {
    let plugins = [];

    // If plugin is null, that means the user has navigated back to a page with
    // plugins, and we need to collect all the plugins.
    if (plugin === null) {
      let contentWindow = this.content;
      let cwu = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                             .getInterface(Ci.nsIDOMWindowUtils);
      // cwu.plugins may contain non-plugin <object>s, filter them out
      plugins = cwu.plugins.filter((p) =>
        p.getContentTypeForMIMEType(p.actualType) == Ci.nsIObjectLoadingContent.TYPE_PLUGIN);

      if (plugins.length == 0) {
        this.removeNotification("click-to-play-plugins");
        return;
      }
    } else {
      plugins = [plugin];
    }

    let pluginData = this.pluginData;

    let principal = this.content.document.nodePrincipal;
    let location = this.content.document.location.href;

    for (let p of plugins) {
      let pluginInfo;
      if (p instanceof Ci.nsIPluginTag) {
        let mimeType = p.getMimeTypes() > 0 ? p.getMimeTypes()[0] : null;
        pluginInfo = this._getPluginInfoForTag(p, mimeType);
      } else {
        pluginInfo = this._getPluginInfo(p);
      }
      if (pluginInfo.permissionString === null) {
        Cu.reportError("No permission string for active plugin.");
        continue;
      }
      if (pluginData.has(pluginInfo.permissionString)) {
        continue;
      }

      let permissionObj = Services.perms.
        getPermissionObject(principal, pluginInfo.permissionString, false);
      if (permissionObj) {
        pluginInfo.pluginPermissionPrePath = permissionObj.principal.originNoSuffix;
        pluginInfo.pluginPermissionType = permissionObj.expireType;
      } else {
        pluginInfo.pluginPermissionPrePath = principal.originNoSuffix;
        pluginInfo.pluginPermissionType = undefined;
      }

      this.pluginData.set(pluginInfo.permissionString, pluginInfo);
    }

    this.haveShownNotification = true;

    this.global.sendAsyncMessage("PluginContent:ShowClickToPlayNotification", {
      plugins: [...this.pluginData.values()],
      showNow,
      location,
    }, null, principal);
  },

  /**
   * Updates the "hidden plugin" notification bar UI.
   *
   * @param document (optional)
   *        Specify the document that is causing the update.
   *        This is useful when the document is possibly no longer
   *        the current loaded document (for example, if we're
   *        responding to a PluginRemoved event for an unloading
   *        document). If this parameter is omitted, it defaults
   *        to the current top-level document.
   */
  updateNotificationUI(document) {
    document = document || this.content.document;

    // We're only interested in the top-level document, since that's
    // the one that provides the Principal that we send back to the
    // parent.
    let principal = document.defaultView.top.document.nodePrincipal;
    let location = document.location.href;

    // Make a copy of the actions from the last popup notification.
    let haveInsecure = false;
    let actions = new Map();
    for (let action of this.pluginData.values()) {
      switch (action.fallbackType) {
        // haveInsecure will trigger the red flashing icon and the infobar
        // styling below
        case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE:
        case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE:
          haveInsecure = true;
          // fall through

        case Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY_QUIET:
        case Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY:
          actions.set(action.permissionString, action);
          continue;
      }
    }

    // Remove plugins that are already active, or large enough to show an overlay.
    let cwu = this.content.QueryInterface(Ci.nsIInterfaceRequestor)
                          .getInterface(Ci.nsIDOMWindowUtils);
    for (let plugin of cwu.plugins) {
      let info = this._getPluginInfo(plugin);
      if (!actions.has(info.permissionString)) {
        continue;
      }
      let fallbackType = info.fallbackType;
      if (fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE) {
        actions.delete(info.permissionString);
        if (actions.size == 0) {
          break;
        }
        continue;
      }
      if (fallbackType != Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY &&
          fallbackType != Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY_QUIET &&
          fallbackType != Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE &&
          fallbackType != Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE) {
        continue;
      }
      let overlay = this.getPluginUI(plugin, "main");
      if (!overlay) {
        continue;
      }
      let overlayDisplayState = this.computeAndAdjustOverlayDisplay(plugin, overlay);
      this.setVisibility(plugin, overlay, overlayDisplayState);
      if (overlayDisplayState > OVERLAY_DISPLAY.BLANK) {
        actions.delete(info.permissionString);
        if (actions.size == 0) {
          break;
        }
      }
    }

    // If there are any items remaining in `actions` now, they are hidden
    // plugins that need a notification bar.
    this.global.sendAsyncMessage("PluginContent:UpdateHiddenPluginUI", {
      haveInsecure,
      actions: [...actions.values()],
      location,
    }, null, principal);
  },

  removeNotification(name) {
    this.global.sendAsyncMessage("PluginContent:RemoveNotification", { name });
  },

  clearPluginCaches() {
    this.pluginData.clear();
    this.pluginCrashData.clear();
  },

  hideNotificationBar(name) {
    this.global.sendAsyncMessage("PluginContent:HideNotificationBar", { name });
  },

  /**
   * Determines whether or not the crashed plugin is contained within current
   * full screen DOM element.
   * @param fullScreenElement (DOM element)
   *   The DOM element that is currently full screen, or null.
   * @param domElement
   *   The DOM element which contains the crashed plugin, or the crashed plugin
   *   itself.
   * @returns bool
   *   True if the plugin is a descendant of the full screen DOM element, false otherwise.
   **/
  isWithinFullScreenElement(fullScreenElement, domElement) {

    /**
     * Traverses down iframes until it find a non-iframe full screen DOM element.
     * @param fullScreenIframe
     *  Target iframe to begin searching from.
     * @returns DOM element
     *  The full screen DOM element contained within the iframe (could be inner iframe), or the original iframe if no inner DOM element is found.
     **/
    let getTrueFullScreenElement = fullScreenIframe => {
      if (typeof fullScreenIframe.contentDocument !== "undefined" && fullScreenIframe.contentDocument.mozFullScreenElement) {
        return getTrueFullScreenElement(fullScreenIframe.contentDocument.mozFullScreenElement);
      }
      return fullScreenIframe;
    };

    if (fullScreenElement.tagName === "IFRAME") {
      fullScreenElement = getTrueFullScreenElement(fullScreenElement);
    }

    if (fullScreenElement.contains(domElement)) {
      return true;
    }
    let parentIframe = domElement.ownerGlobal.frameElement;
    if (parentIframe) {
      return this.isWithinFullScreenElement(fullScreenElement, parentIframe);
    }
    return false;
  },

  /**
   * The PluginCrashed event handler. Note that the PluginCrashed event is
   * fired for both NPAPI and Gecko Media plugins. In the latter case, the
   * target of the event is the document that the GMP is being used in.
   */
  onPluginCrashed(target, aEvent) {
    if (!(aEvent instanceof this.content.PluginCrashedEvent))
      return;

    let fullScreenElement = this.content.document.mozFullScreenElement;
    if (fullScreenElement) {
      if (this.isWithinFullScreenElement(fullScreenElement, target)) {
        this.content.document.mozCancelFullScreen();
      }
    }

    if (aEvent.gmpPlugin) {
      this.GMPCrashed(aEvent);
      return;
    }

    if (!(target instanceof Ci.nsIObjectLoadingContent))
      return;

    let crashData = this.pluginCrashData.get(target.runID);
    if (!crashData) {
      // We haven't received information from the parent yet about
      // this crash, so we should hold off showing the crash report
      // UI.
      return;
    }

    crashData.instances.delete(target);
    if (crashData.instances.length == 0) {
      this.pluginCrashData.delete(target.runID);
    }

    this.setCrashedNPAPIPluginState({
      plugin: target,
      state: crashData.state,
      message: crashData.message,
    });
  },

  NPAPIPluginProcessCrashed({pluginName, runID, state}) {
    let message =
      gNavigatorBundle.formatStringFromName("crashedpluginsMessage.title",
                                            [pluginName], 1);

    let contentWindow = this.global.content;
    let cwu = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                           .getInterface(Ci.nsIDOMWindowUtils);
    let plugins = cwu.plugins;

    for (let plugin of plugins) {
      if (plugin instanceof Ci.nsIObjectLoadingContent &&
          plugin.runID == runID) {
        // The parent has told us that the plugin process has died.
        // It's possible that this content process hasn't yet noticed,
        // in which case we need to stash this data around until the
        // PluginCrashed events get sent up.
        if (plugin.pluginFallbackType == Ci.nsIObjectLoadingContent.PLUGIN_CRASHED) {
          // This plugin has already been put into the crashed state by the
          // content process, so we can tweak its crash UI without delay.
          this.setCrashedNPAPIPluginState({plugin, state, message});
        } else {
          // The content process hasn't yet determined that the plugin has crashed.
          // Stash the data in our map, and throw the plugin into a WeakSet. When
          // the PluginCrashed event fires on the <object>/<embed>, we'll retrieve
          // the information we need from the Map and remove the instance from the
          // WeakSet. Once the WeakSet is empty, we can clear the map.
          if (!this.pluginCrashData.has(runID)) {
            this.pluginCrashData.set(runID, {
              state,
              message,
              instances: new WeakSet(),
            });
          }
          let crashData = this.pluginCrashData.get(runID);
          crashData.instances.add(plugin);
        }
      }
    }
  },

  setCrashedNPAPIPluginState({plugin, state, message}) {
    // Force a layout flush so the binding is attached.
    plugin.clientTop;
    let overlay = this.getPluginUI(plugin, "main");
    let statusDiv = this.getPluginUI(plugin, "submitStatus");
    let optInCB = this.getPluginUI(plugin, "submitURLOptIn");

    this.getPluginUI(plugin, "submitButton")
        .addEventListener("click", (event) => {
          if (event.button != 0 || !event.isTrusted)
            return;
          this.submitReport(plugin);
        });

    let pref = Services.prefs.getBranch("dom.ipc.plugins.reportCrashURL");
    optInCB.checked = pref.getBoolPref("");

    statusDiv.setAttribute("status", state);

    let helpIcon = this.getPluginUI(plugin, "helpIcon");
    this.addLinkClickCallback(helpIcon, "openHelpPage");

    let crashText = this.getPluginUI(plugin, "crashedText");
    crashText.textContent = message;

    let link = this.getPluginUI(plugin, "reloadLink");
    this.addLinkClickCallback(link, "reloadPage");

    let overlayDisplayState = this.computeAndAdjustOverlayDisplay(plugin, overlay);

    // Is the <object>'s size too small to hold what we want to show?
    if (overlayDisplayState != OVERLAY_DISPLAY.FULL) {
      // First try hiding the crash report submission UI.
      statusDiv.removeAttribute("status");

      overlayDisplayState = this.computeAndAdjustOverlayDisplay(plugin, overlay);
    }
    this.setVisibility(plugin, overlay, overlayDisplayState);

    let doc = plugin.ownerDocument;
    let runID = plugin.runID;

    if (overlayDisplayState == OVERLAY_DISPLAY.FULL) {
      // If a previous plugin on the page was too small and resulted in adding a
      // notification bar, then remove it because this plugin instance it big
      // enough to serve as in-content notification.
      this.hideNotificationBar("plugin-crashed");
      doc.mozNoPluginCrashedNotification = true;

      // Notify others that the crash reporter UI is now ready.
      // Currently, this event is only used by tests.
      let winUtils = this.content.QueryInterface(Ci.nsIInterfaceRequestor)
                                 .getInterface(Ci.nsIDOMWindowUtils);
      let event = new this.content.CustomEvent("PluginCrashReporterDisplayed", {bubbles: true});
      winUtils.dispatchEventToChromeOnly(plugin, event);
    } else if (!doc.mozNoPluginCrashedNotification) {
      // If another plugin on the page was large enough to show our UI, we don't
      // want to show a notification bar.
      this.global.sendAsyncMessage("PluginContent:ShowPluginCrashedNotification",
                                   { messageString: message, pluginID: runID });
      // Remove the notification when the page is reloaded.
      doc.defaultView.top.addEventListener("unload", event => {
        this.hideNotificationBar("plugin-crashed");
      });
    }
  },

  NPAPIPluginCrashReportSubmitted({ runID, state }) {
    this.pluginCrashData.delete(runID);
    let contentWindow = this.global.content;
    let cwu = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                           .getInterface(Ci.nsIDOMWindowUtils);
    let plugins = cwu.plugins;

    for (let plugin of plugins) {
      if (plugin instanceof Ci.nsIObjectLoadingContent &&
          plugin.runID == runID) {
        let statusDiv = this.getPluginUI(plugin, "submitStatus");
        statusDiv.setAttribute("status", state);
      }
    }
  },

  GMPCrashed(aEvent) {
    let target          = aEvent.target;
    let pluginName      = aEvent.pluginName;
    let gmpPlugin       = aEvent.gmpPlugin;
    let pluginID        = aEvent.pluginID;
    let doc             = target.document;

    if (!gmpPlugin || !doc) {
      // TODO: Throw exception? How did we get here?
      return;
    }

    let messageString =
      gNavigatorBundle.formatStringFromName("crashedpluginsMessage.title",
                                            [pluginName], 1);

    this.global.sendAsyncMessage("PluginContent:ShowPluginCrashedNotification",
                                 { messageString, pluginID });

    // Remove the notification when the page is reloaded.
    doc.defaultView.top.addEventListener("unload", event => {
      this.hideNotificationBar("plugin-crashed");
    });
  },
};
PK
!<io���B�Bmodules/ProcessHangMonitor.jsm/* -*- mode: js; indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = ["ProcessHangMonitor"];

ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");

/**
 * This JSM is responsible for observing content process hang reports
 * and asking the user what to do about them. See nsIHangReport for
 * the platform interface.
 */

var ProcessHangMonitor = {
  /**
   * This timeout is the wait period applied after a user selects "Wait" in
   * an existing notification.
   */
  get WAIT_EXPIRATION_TIME() {
    try {
      return Services.prefs.getIntPref("browser.hangNotification.waitPeriod");
    } catch (ex) {
      return 10000;
    }
  },

  /**
   * Should only be set to true once the quit-application-granted notification
   * has been fired.
   */
  _shuttingDown: false,

  /**
   * Collection of hang reports that haven't expired or been dismissed
   * by the user. These are nsIHangReports.
   */
  _activeReports: new Set(),

  /**
   * Collection of hang reports that have been suppressed for a short
   * period of time. Value is an nsITimer for when the wait time
   * expires.
   */
  _pausedReports: new Map(),

  /**
   * Initialize hang reporting. Called once in the parent process.
   */
  init() {
    Services.obs.addObserver(this, "process-hang-report");
    Services.obs.addObserver(this, "clear-hang-report");
    Services.obs.addObserver(this, "quit-application-granted");
    Services.obs.addObserver(this, "xpcom-shutdown");
    Services.ww.registerNotification(this);
  },

  /**
   * Terminate JavaScript associated with the hang being reported for
   * the selected browser in |win|.
   */
  terminateScript(win) {
    this.handleUserInput(win, report => report.terminateScript());
  },

  /**
   * Terminate Sandbox globals associated with the hang being reported
   * for the selected browser in |win|.
   */
  terminateGlobal(win) {
    this.handleUserInput(win, report => report.terminateGlobal());
  },

  /**
   * Start devtools debugger for JavaScript associated with the hang
   * being reported for the selected browser in |win|.
   */
  debugScript(win) {
    this.handleUserInput(win, report => {
      function callback() {
        report.endStartingDebugger();
      }

      report.beginStartingDebugger();

      let svc = Cc["@mozilla.org/dom/slow-script-debug;1"].getService(Ci.nsISlowScriptDebug);
      let handler = svc.remoteActivationHandler;
      handler.handleSlowScriptDebug(report.scriptBrowser, callback);
    });
  },

  /**
   * Terminate the plugin process associated with a hang being reported
   * for the selected browser in |win|. Will attempt to generate a combined
   * crash report for all processes.
   */
  terminatePlugin(win) {
    this.handleUserInput(win, report => report.terminatePlugin());
  },

  /**
   * Dismiss the browser notification and invoke an appropriate action based on
   * the hang type.
   */
  stopIt(win) {
    let report = this.findActiveReport(win.gBrowser.selectedBrowser);
    if (!report) {
      return;
    }

    switch (report.hangType) {
      case report.SLOW_SCRIPT:
        this.terminateScript(win);
        break;
      case report.PLUGIN_HANG:
        this.terminatePlugin(win);
        break;
    }
  },

  /**
   * Stop all scripts from running in the Sandbox global attached to
   * this window.
   */
  stopGlobal(win) {
    let report = this.findActiveReport(win.gBrowser.selectedBrowser);
    if (!report) {
      return;
    }

    switch (report.hangType) {
      case report.SLOW_SCRIPT:
        this.terminateGlobal(win);
        break;
    }
  },

  /**
   * Terminate whatever is causing this report, be it an add-on, page script,
   * or plug-in. This is done without updating any report notifications.
   */
  stopHang(report) {
    switch (report.hangType) {
      case report.SLOW_SCRIPT: {
        if (report.addonId) {
          report.terminateGlobal();
        } else {
          report.terminateScript();
        }
        break;
      }
      case report.PLUGIN_HANG: {
        report.terminatePlugin();
        break;
      }
    }
  },

  /**
   * Dismiss the notification, clear the report from the active list and set up
   * a new timer to track a wait period during which we won't notify.
   */
  waitLonger(win) {
    let report = this.findActiveReport(win.gBrowser.selectedBrowser);
    if (!report) {
      return;
    }
    // Remove the report from the active list.
    this.removeActiveReport(report);

    // NOTE, we didn't call userCanceled on nsIHangReport here. This insures
    // we don't repeatedly generate and cache crash report data for this hang
    // in the process hang reporter. It already has one report for the browser
    // process we want it hold onto.

    // Create a new wait timer with notify callback
    let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
    timer.initWithCallback(() => {
      for (let [stashedReport, otherTimer] of this._pausedReports) {
        if (otherTimer === timer) {
          this.removePausedReport(stashedReport);

          // We're still hung, so move the report back to the active
          // list and update the UI.
          this._activeReports.add(report);
          this.updateWindows();
          break;
        }
      }
    }, this.WAIT_EXPIRATION_TIME, timer.TYPE_ONE_SHOT);

    this._pausedReports.set(report, timer);

    // remove the browser notification associated with this hang
    this.updateWindows();
  },

  /**
   * If there is a hang report associated with the selected browser in
   * |win|, invoke |func| on that report and stop notifying the user
   * about it.
   */
  handleUserInput(win, func) {
    let report = this.findActiveReport(win.gBrowser.selectedBrowser);
    if (!report) {
      return null;
    }
    this.removeActiveReport(report);

    return func(report);
  },

  observe(subject, topic, data) {
    switch (topic) {
      case "xpcom-shutdown": {
        Services.obs.removeObserver(this, "xpcom-shutdown");
        Services.obs.removeObserver(this, "process-hang-report");
        Services.obs.removeObserver(this, "clear-hang-report");
        Services.obs.removeObserver(this, "quit-application-granted");
        Services.ww.unregisterNotification(this);
        break;
      }

      case "quit-application-granted": {
        this.onQuitApplicationGranted();
        break;
      }

      case "process-hang-report": {
        this.reportHang(subject.QueryInterface(Ci.nsIHangReport));
        break;
      }

      case "clear-hang-report": {
        this.clearHang(subject.QueryInterface(Ci.nsIHangReport));
        break;
      }

      case "domwindowopened": {
        // Install event listeners on the new window in case one of
        // its tabs is already hung.
        let win = subject.QueryInterface(Ci.nsIDOMWindow);
        let listener = (ev) => {
          win.removeEventListener("load", listener, true);
          this.updateWindows();
        };
        win.addEventListener("load", listener, true);
        break;
      }

      case "domwindowclosed": {
        let win = subject.QueryInterface(Ci.nsIDOMWindow);
        this.onWindowClosed(win);
        break;
      }
    }
  },

  /**
   * Called early on in the shutdown sequence. We take this opportunity to
   * take any pre-existing hang reports, and terminate them. We also put
   * ourselves in a state so that if any more hang reports show up while
   * we're shutting down, we terminate them immediately.
   */
  onQuitApplicationGranted() {
    this._shuttingDown = true;
    this.stopAllHangs();
    this.updateWindows();
  },

  onWindowClosed(win) {
    let maybeStopHang = (report) => {
      if (report.hangType == report.SLOW_SCRIPT) {
        let hungBrowserWindow = null;
        try {
          hungBrowserWindow = report.scriptBrowser.ownerGlobal;
        } catch (e) {
          // Ignore failures to get the script browser - we'll be
          // conservative, and assume that if we cannot access the
          // window that belongs to this report that we should stop
          // the hang.
        }
        if (!hungBrowserWindow || hungBrowserWindow == win) {
          this.stopHang(report);
          return true;
        }
      } else if (report.hangType == report.PLUGIN_HANG) {
        // If any window has closed during a plug-in hang, we'll
        // do the conservative thing and terminate the plug-in.
        this.stopHang(report);
        return true;
      }
      return false;
    };

    // If there are any script hangs for browsers that are in this window
    // that is closing, we can stop them now.
    for (let report of this._activeReports) {
      if (maybeStopHang(report)) {
        this._activeReports.delete(report);
      }
    }

    for (let [pausedReport, ] of this._pausedReports) {
      if (maybeStopHang(pausedReport)) {
        this.removePausedReport(pausedReport);
      }
    }

    this.updateWindows();
  },

  stopAllHangs() {
    for (let report of this._activeReports) {
      this.stopHang(report);
    }

    this._activeReports = new Set();

    for (let [pausedReport, ] of this._pausedReports) {
      this.stopHang(pausedReport);
      this.removePausedReport(pausedReport);
    }
  },

  /**
   * Find a active hang report for the given <browser> element.
   */
  findActiveReport(browser) {
    let frameLoader = browser.frameLoader;
    for (let report of this._activeReports) {
      if (report.isReportForBrowser(frameLoader)) {
        return report;
      }
    }
    return null;
  },

  /**
   * Find a paused hang report for the given <browser> element.
   */
  findPausedReport(browser) {
    let frameLoader = browser.frameLoader;
    for (let [report, ] of this._pausedReports) {
      if (report.isReportForBrowser(frameLoader)) {
        return report;
      }
    }
    return null;
  },

  /**
   * Remove an active hang report from the active list and cancel the timer
   * associated with it.
   */
  removeActiveReport(report) {
    this._activeReports.delete(report);
    this.updateWindows();
  },

  /**
   * Remove a paused hang report from the paused list and cancel the timer
   * associated with it.
   */
  removePausedReport(report) {
    let timer = this._pausedReports.get(report);
    if (timer) {
      timer.cancel();
    }
    this._pausedReports.delete(report);
  },

  /**
   * Iterate over all XUL windows and ensure that the proper hang
   * reports are shown for each one. Also install event handlers in
   * each window to watch for events that would cause a different hang
   * report to be displayed.
   */
  updateWindows() {
    let e = Services.wm.getEnumerator("navigator:browser");

    // If it turns out we have no windows (this can happen on macOS),
    // we have no opportunity to ask the user whether or not they want
    // to stop the hang or wait, so we'll opt for stopping the hang.
    if (!e.hasMoreElements()) {
      this.stopAllHangs();
      return;
    }

    while (e.hasMoreElements()) {
      let win = e.getNext();

      this.updateWindow(win);

      // Only listen for these events if there are active hang reports.
      if (this._activeReports.size) {
        this.trackWindow(win);
      } else {
        this.untrackWindow(win);
      }
    }
  },

  /**
   * If there is a hang report for the current tab in |win|, display it.
   */
  updateWindow(win) {
    let report = this.findActiveReport(win.gBrowser.selectedBrowser);

    if (report) {
      this.showNotification(win, report);
    } else {
      this.hideNotification(win);
    }
  },

  /**
   * Show the notification for a hang.
   */
  showNotification(win, report) {
    let nb = win.document.getElementById("high-priority-global-notificationbox");
    let notification = nb.getNotificationWithValue("process-hang");
    if (notification) {
      return;
    }

    let bundle = win.gNavigatorBundle;

    let buttons = [{
        label: bundle.getString("processHang.button_stop.label"),
        accessKey: bundle.getString("processHang.button_stop.accessKey"),
        callback() {
          ProcessHangMonitor.stopIt(win);
        }
      },
      {
        label: bundle.getString("processHang.button_wait.label"),
        accessKey: bundle.getString("processHang.button_wait.accessKey"),
        callback() {
          ProcessHangMonitor.waitLonger(win);
        }
      }];

    let message = bundle.getString("processHang.label");
    if (report.addonId) {
      let aps = Cc["@mozilla.org/addons/policy-service;1"].getService(Ci.nsIAddonPolicyService);

      let doc = win.document;
      let brandBundle = doc.getElementById("bundle_brand");

      let addonName = aps.getExtensionName(report.addonId);

      let label = bundle.getFormattedString("processHang.add-on.label",
                                            [addonName, brandBundle.getString("brandShortName")]);

      let linkText = bundle.getString("processHang.add-on.learn-more.text");
      let linkURL = "https://support.mozilla.org/kb/warning-unresponsive-script#w_other-causes";

      let link = doc.createElement("label");
      link.setAttribute("class", "text-link");
      link.setAttribute("role", "link");
      link.setAttribute("onclick", `openUILinkIn(${JSON.stringify(linkURL)}, "tab")`);
      link.setAttribute("value", linkText);

      message = doc.createDocumentFragment();
      message.appendChild(doc.createTextNode(label + " "));
      message.appendChild(link);

      buttons.unshift({
        label: bundle.getString("processHang.button_stop_sandbox.label"),
        accessKey: bundle.getString("processHang.button_stop_sandbox.accessKey"),
        callback() {
          ProcessHangMonitor.stopGlobal(win);
        }
      });
    }

    if (AppConstants.MOZ_DEV_EDITION && report.hangType == report.SLOW_SCRIPT) {
      buttons.push({
        label: bundle.getString("processHang.button_debug.label"),
        accessKey: bundle.getString("processHang.button_debug.accessKey"),
        callback() {
          ProcessHangMonitor.debugScript(win);
        }
      });
    }

    nb.appendNotification(message, "process-hang",
                          "chrome://browser/content/aboutRobots-icon.png",
                          nb.PRIORITY_WARNING_HIGH, buttons);
  },

  /**
   * Ensure that no hang notifications are visible in |win|.
   */
  hideNotification(win) {
    let nb = win.document.getElementById("high-priority-global-notificationbox");
    let notification = nb.getNotificationWithValue("process-hang");
    if (notification) {
      nb.removeNotification(notification);
    }
  },

  /**
   * Install event handlers on |win| to watch for events that would
   * cause a different hang report to be displayed.
   */
  trackWindow(win) {
    win.gBrowser.tabContainer.addEventListener("TabSelect", this, true);
    win.gBrowser.tabContainer.addEventListener("TabRemotenessChange", this, true);
  },

  untrackWindow(win) {
    win.gBrowser.tabContainer.removeEventListener("TabSelect", this, true);
    win.gBrowser.tabContainer.removeEventListener("TabRemotenessChange", this, true);
  },

  handleEvent(event) {
    let win = event.target.ownerGlobal;

    // If a new tab is selected or if a tab changes remoteness, then
    // we may need to show or hide a hang notification.

    if (event.type == "TabSelect" || event.type == "TabRemotenessChange") {
      this.updateWindow(win);
    }
  },

  /**
   * Handle a potentially new hang report. If it hasn't been seen
   * before, show a notification for it in all open XUL windows.
   */
  reportHang(report) {
    if (this._shuttingDown) {
      this.stopHang(report);
      return;
    }

    // If this hang was already reported reset the timer for it.
    if (this._activeReports.has(report)) {
      // if this report is in active but doesn't have a notification associated
      // with it, display a notification.
      this.updateWindows();
      return;
    }

    // If this hang was already reported and paused by the user ignore it.
    if (this._pausedReports.has(report)) {
      return;
    }

    // On e10s this counts slow-script/hanged-plugin notice only once.
    // This code is not reached on non-e10s.
    if (report.hangType == report.SLOW_SCRIPT) {
      // On non-e10s, SLOW_SCRIPT_NOTICE_COUNT is probed at nsGlobalWindow.cpp
      Services.telemetry.getHistogramById("SLOW_SCRIPT_NOTICE_COUNT").add();
    } else if (report.hangType == report.PLUGIN_HANG) {
      // On non-e10s we have sufficient plugin telemetry probes,
      // so PLUGIN_HANG_NOTICE_COUNT is only probed on e10s.
      Services.telemetry.getHistogramById("PLUGIN_HANG_NOTICE_COUNT").add();
    }

    this._activeReports.add(report);
    this.updateWindows();
  },

  clearHang(report) {
    this.removeActiveReport(report);
    this.removePausedReport(report);
    report.userCanceled();
  },
};
PK
!<��Ah]	]	modules/RecentWindow.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = ["RecentWindow"];

ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");

var RecentWindow = {
  /*
   * Get the most recent browser window.
   *
   * @param aOptions an object accepting the arguments for the search.
   *        * private: true to restrict the search to private windows
   *            only, false to restrict the search to non-private only.
   *            Omit the property to search in both groups.
   *        * allowPopups: true if popup windows are permissable.
   */
  getMostRecentBrowserWindow: function RW_getMostRecentBrowserWindow(aOptions) {
    let checkPrivacy = typeof aOptions == "object" &&
                       "private" in aOptions;

    let allowPopups = typeof aOptions == "object" && !!aOptions.allowPopups;

    function isSuitableBrowserWindow(win) {
      return (!win.closed &&
              (allowPopups || win.toolbar.visible) &&
              (!checkPrivacy ||
               PrivateBrowsingUtils.permanentPrivateBrowsing ||
               PrivateBrowsingUtils.isWindowPrivate(win) == aOptions.private));
    }

    let broken_wm_z_order =
      AppConstants.platform != "macosx" && AppConstants.platform != "win";

    if (broken_wm_z_order) {
      let win = Services.wm.getMostRecentWindow("navigator:browser");

      // if we're lucky, this isn't a popup, and we can just return this
      if (win && !isSuitableBrowserWindow(win)) {
        win = null;
        let windowList = Services.wm.getEnumerator("navigator:browser");
        // this is oldest to newest, so this gets a bit ugly
        while (windowList.hasMoreElements()) {
          let nextWin = windowList.getNext();
          if (isSuitableBrowserWindow(nextWin))
            win = nextWin;
        }
      }
      return win;
    }
    let windowList = Services.wm.getZOrderDOMWindowEnumerator("navigator:browser", true);
    while (windowList.hasMoreElements()) {
      let win = windowList.getNext();
      if (isSuitableBrowserWindow(win))
        return win;
    }
    return null;
  }
};

PK
!<h��_55modules/RemotePrompt.jsm/* vim: set ts=2 sw=2 et tw=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";

var EXPORTED_SYMBOLS = [ "RemotePrompt" ];

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");

ChromeUtils.defineModuleGetter(this, "PlacesUIUtils",
                               "resource:///modules/PlacesUIUtils.jsm");
ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
                               "resource://gre/modules/PrivateBrowsingUtils.jsm");
ChromeUtils.defineModuleGetter(this, "PromptUtils",
                               "resource://gre/modules/SharedPromptUtils.jsm");
ChromeUtils.defineModuleGetter(this, "Services",
                               "resource://gre/modules/Services.jsm");

var RemotePrompt = {
  // Listeners are added in nsBrowserGlue.js
  receiveMessage(message) {
    switch (message.name) {
      case "Prompt:Open":
        if (message.data.uri) {
          this.openModalWindow(message.data, message.target);
        } else {
          this.openTabPrompt(message.data, message.target);
        }
        break;
    }
  },

  openTabPrompt(args, browser) {
    let window = browser.ownerGlobal;
    let tabPrompt = window.gBrowser.getTabModalPromptBox(browser);
    let newPrompt;
    let needRemove = false;
    let promptId = args._remoteId;

    function onPromptClose(forceCleanup) {
      // It's possible that we removed the prompt during the
      // appendPrompt call below. In that case, newPrompt will be
      // undefined. We set the needRemove flag to remember to remove
      // it right after we've finished adding it.
      if (newPrompt)
        tabPrompt.removePrompt(newPrompt);
      else
        needRemove = true;

      PromptUtils.fireDialogEvent(window, "DOMModalDialogClosed", browser);
      browser.messageManager.sendAsyncMessage("Prompt:Close", args);
    }

    browser.messageManager.addMessageListener("Prompt:ForceClose", function listener(message) {
      // If this was for another prompt in the same tab, ignore it.
      if (message.data._remoteId !== promptId) {
        return;
      }

      browser.messageManager.removeMessageListener("Prompt:ForceClose", listener);

      if (newPrompt) {
        newPrompt.abortPrompt();
      }
    });

    try {
      let eventDetail = {
        tabPrompt: true,
        promptPrincipal: args.promptPrincipal,
        inPermitUnload: args.inPermitUnload,
      };
      PromptUtils.fireDialogEvent(window, "DOMWillOpenModalDialog", browser, eventDetail);

      args.promptActive = true;

      newPrompt = tabPrompt.appendPrompt(args, onPromptClose);

      if (needRemove) {
        tabPrompt.removePrompt(newPrompt);
      }

      // TODO since we don't actually open a window, need to check if
      // there's other stuff in nsWindowWatcher::OpenWindowInternal
      // that we might need to do here as well.
    } catch (ex) {
      onPromptClose(true);
    }
  },

  openModalWindow(args, browser) {
    let window = browser.ownerGlobal;
    try {
      PromptUtils.fireDialogEvent(window, "DOMWillOpenModalDialog", browser);
      let bag = PromptUtils.objectToPropBag(args);

      Services.ww.openWindow(window, args.uri, "_blank",
                             "centerscreen,chrome,modal,titlebar", bag);

      PromptUtils.propBagToObject(bag, args);
    } finally {
      PromptUtils.fireDialogEvent(window, "DOMModalDialogClosed", browser);
      browser.messageManager.sendAsyncMessage("Prompt:Close", args);
    }
  }
};
PK
!<�Tn���modules/ScrollbarSampler.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = ["ScrollbarSampler"];

ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");

var gSystemScrollbarWidth = null;

var ScrollbarSampler = {
  getSystemScrollbarWidth() {
    if (gSystemScrollbarWidth !== null) {
      return Promise.resolve(gSystemScrollbarWidth);
    }

    return new Promise(resolve => {
      this._sampleSystemScrollbarWidth().then(function(systemScrollbarWidth) {
        gSystemScrollbarWidth = systemScrollbarWidth;
        resolve(gSystemScrollbarWidth);
      });
    });
  },

  resetSystemScrollbarWidth() {
    gSystemScrollbarWidth = null;
  },

  _sampleSystemScrollbarWidth() {
    let hwin = Services.appShell.hiddenDOMWindow;
    let hdoc = hwin.document.documentElement;
    let iframe = hwin.document.createElementNS("http://www.w3.org/1999/xhtml",
                                               "html:iframe");
    iframe.setAttribute("srcdoc", '<body style="overflow-y: scroll"></body>');
    hdoc.appendChild(iframe);

    let cwindow = iframe.contentWindow;
    let utils = cwindow.QueryInterface(Ci.nsIInterfaceRequestor)
                       .getInterface(Ci.nsIDOMWindowUtils);

    return new Promise(resolve => {
      cwindow.addEventListener("load", function(aEvent) {
        let sbWidth = {};
        try {
          utils.getScrollbarSize(true, sbWidth, {});
        } catch (e) {
          Cu.reportError("Could not sample scrollbar size: " + e + " -- " +
                         e.stack);
          sbWidth.value = 0;
        }
        // Minimum width of 10 so that we have enough padding:
        sbWidth.value = Math.max(sbWidth.value, 10);
        resolve(sbWidth.value);
        iframe.remove();
      }, {once: true});
    });
  }
};
Object.freeze(this.ScrollbarSampler);
PK
!<��h�\
\
modules/SearchWidgetTracker.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/*
 * Keeps the "browser.search.widget.inNavBar" preference synchronized.
 */

"use strict";

var EXPORTED_SYMBOLS = ["SearchWidgetTracker"];

ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");

ChromeUtils.defineModuleGetter(this, "CustomizableUI",
                               "resource:///modules/CustomizableUI.jsm");

const WIDGET_ID = "search-container";
const PREF_NAME = "browser.search.widget.inNavBar";

const SearchWidgetTracker = {
  init() {
    this.onWidgetAdded = this.onWidgetRemoved = (widgetId, area) => {
      if (widgetId == WIDGET_ID && area == CustomizableUI.AREA_NAVBAR) {
        this.syncPreferenceWithWidget();
      }
    };
    this.onWidgetReset = this.onWidgetUndoMove = node => {
      if (node.id == WIDGET_ID) {
        this.syncPreferenceWithWidget();
      }
    };
    CustomizableUI.addListener(this);
    Services.prefs.addObserver(PREF_NAME,
                               () => this.syncWidgetWithPreference());
  },

  onAreaNodeRegistered(aArea) {
    // The placement of the widget always takes priority, and the preference
    // should always match the actual placement when the browser starts up - i.e.
    // once the navigation bar has been registered.
    if (aArea == CustomizableUI.AREA_NAVBAR) {
      this.syncPreferenceWithWidget();
    }
  },

  onCustomizeEnd() {
    // onWidgetUndoMove does not fire when the search container is moved back to
    // the customization palette as a result of an undo, so we sync again here.
    this.syncPreferenceWithWidget();
  },

  syncPreferenceWithWidget() {
    Services.prefs.setBoolPref(PREF_NAME, this.widgetIsInNavBar);
  },

  syncWidgetWithPreference() {
    let newValue = Services.prefs.getBoolPref(PREF_NAME);
    if (newValue == this.widgetIsInNavBar) {
      return;
    }

    if (newValue) {
      // The URL bar widget is always present in the navigation toolbar, so we
      // can simply read its position to place the search bar right after it.
      CustomizableUI.addWidgetToArea(WIDGET_ID, CustomizableUI.AREA_NAVBAR,
        CustomizableUI.getPlacementOfWidget("urlbar-container").position + 1);
    } else {
      CustomizableUI.removeWidgetFromArea(WIDGET_ID);
    }
  },

  get widgetIsInNavBar() {
    let placement = CustomizableUI.getPlacementOfWidget(WIDGET_ID);
    return placement ? placement.area == CustomizableUI.AREA_NAVBAR : false;
  },
};
PK
!<y�-9modules/ShellService.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = ["ShellService"];

ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.defineModuleGetter(this, "WindowsRegistry",
                               "resource://gre/modules/WindowsRegistry.jsm");

/**
 * Internal functionality to save and restore the docShell.allow* properties.
 */
let ShellServiceInternal = {
  /**
   * Used to determine whether or not to offer "Set as desktop background"
   * functionality. Even if shell service is available it is not
   * guaranteed that it is able to set the background for every desktop
   * which is especially true for Linux with its many different desktop
   * environments.
   */
  get canSetDesktopBackground() {
    if (AppConstants.platform == "win" ||
        AppConstants.platform == "macosx") {
      return true;
    }

    if (AppConstants.platform == "linux") {
      if (this.shellService) {
        let linuxShellService = this.shellService
                                    .QueryInterface(Ci.nsIGNOMEShellService);
        return linuxShellService.canSetDesktopBackground;
      }
    }

    return false;
  },

  /**
   * Used to determine whether or not to show a "Set Default Browser"
   * query dialog. This attribute is true if the application is starting
   * up and "browser.shell.checkDefaultBrowser" is true, otherwise it
   * is false.
   */
  _checkedThisSession: false,
  get shouldCheckDefaultBrowser() {
    // If we've already checked, the browser has been started and this is a
    // new window open, and we don't want to check again.
    if (this._checkedThisSession) {
      return false;
    }

    if (!Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser")) {
      return false;
    }

    if (AppConstants.platform == "win") {
      let optOutValue = WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
                                                   "Software\\Mozilla\\Firefox",
                                                   "DefaultBrowserOptOut");
      WindowsRegistry.removeRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
                                   "Software\\Mozilla\\Firefox",
                                   "DefaultBrowserOptOut");
      if (optOutValue == "True") {
        Services.prefs.setBoolPref("browser.shell.checkDefaultBrowser", false);
        return false;
      }
    }

    return true;
  },

  set shouldCheckDefaultBrowser(shouldCheck) {
    Services.prefs.setBoolPref("browser.shell.checkDefaultBrowser", !!shouldCheck);
  },

  isDefaultBrowser(startupCheck, forAllTypes) {
    // If this is the first browser window, maintain internal state that we've
    // checked this session (so that subsequent window opens don't show the
    // default browser dialog).
    if (startupCheck) {
      this._checkedThisSession = true;
    }
    if (this.shellService) {
      return this.shellService.isDefaultBrowser(startupCheck, forAllTypes);
    }
    return false;
  }
};

XPCOMUtils.defineLazyServiceGetter(ShellServiceInternal, "shellService",
  "@mozilla.org/browser/shell-service;1", Ci.nsIShellService);

/**
 * The external API exported by this module.
 */
var ShellService = new Proxy(ShellServiceInternal, {
  get(target, name) {
    if (name in target) {
      return target[name];
    }
    if (target.shellService) {
      return target.shellService[name];
    }
    Services.console.logStringMessage(`${name} not found in ShellService: ${target.shellService}`);
    return undefined;
  }
});
PK
!<_��H�9�9modules/SiteDataManager.jsm"use strict";

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");

ChromeUtils.defineModuleGetter(this, "OfflineAppCacheHelper",
                               "resource:///modules/offlineAppCache.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "serviceWorkerManager",
                                   "@mozilla.org/serviceworkers/manager;1",
                                   "nsIServiceWorkerManager");

var EXPORTED_SYMBOLS = [
  "SiteDataManager"
];

XPCOMUtils.defineLazyGetter(this, "gStringBundle", function() {
  return Services.strings.createBundle("chrome://browser/locale/siteData.properties");
});

XPCOMUtils.defineLazyGetter(this, "gBrandBundle", function() {
  return Services.strings.createBundle("chrome://branding/locale/brand.properties");
});

var SiteDataManager = {

  _qms: Services.qms,

  _appCache: Cc["@mozilla.org/network/application-cache-service;1"].getService(Ci.nsIApplicationCacheService),

  // A Map of sites and their disk usage according to Quota Manager and appcache
  // Key is host (group sites based on host across scheme, port, origin atttributes).
  // Value is one object holding:
  //   - principals: instances of nsIPrincipal.
  //   - persisted: the persistent-storage status.
  //   - quotaUsage: the usage of indexedDB and localStorage.
  //   - appCacheList: an array of app cache; instances of nsIApplicationCache
  _sites: new Map(),

  _getCacheSizeObserver: null,

  _getCacheSizePromise: null,

  _getQuotaUsagePromise: null,

  _quotaUsageRequest: null,

  async updateSites() {
    Services.obs.notifyObservers(null, "sitedatamanager:updating-sites");
    // Clear old data and requests first
    this._sites.clear();
    this._getAllCookies();
    await this._getQuotaUsage();
    this._updateAppCache();
    Services.obs.notifyObservers(null, "sitedatamanager:sites-updated");
  },

  _getBaseDomainFromHost(host) {
    let result = host;
    try {
      result = Services.eTLD.getBaseDomainFromHost(host);
    } catch (e) {
      if (e.result == Cr.NS_ERROR_HOST_IS_IP_ADDRESS ||
          e.result == Cr.NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
        // For these 2 expected errors, just take the host as the result.
        // - NS_ERROR_HOST_IS_IP_ADDRESS: the host is in ipv4/ipv6.
        // - NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS: not enough domain parts to extract.
        result = host;
      } else {
        throw e;
      }
    }
    return result;
  },

  _getOrInsertSite(host) {
    let site = this._sites.get(host);
    if (!site) {
      site = {
        baseDomain: this._getBaseDomainFromHost(host),
        cookies: [],
        persisted: false,
        quotaUsage: 0,
        lastAccessed: 0,
        principals: [],
        appCacheList: [],
      };
      this._sites.set(host, site);
    }
    return site;
  },

  /**
   * Retrieves the amount of space currently used by disk cache.
   *
   * You can use DownloadUtils.convertByteUnits to convert this to
   * a user-understandable size/unit combination.
   *
   * @returns a Promise that resolves with the cache size on disk in bytes.
   */
  getCacheSize() {
    if (this._getCacheSizePromise) {
      return this._getCacheSizePromise;
    }

    this._getCacheSizePromise = new Promise((resolve, reject) => {
      // Needs to root the observer since cache service keeps only a weak reference.
      this._getCacheSizeObserver = {
        onNetworkCacheDiskConsumption: consumption => {
          resolve(consumption);
          this._getCacheSizePromise = null;
          this._getCacheSizeObserver = null;
        },

        QueryInterface: XPCOMUtils.generateQI([
          Ci.nsICacheStorageConsumptionObserver,
          Ci.nsISupportsWeakReference
        ])
      };

      try {
        Services.cache2.asyncGetDiskConsumption(this._getCacheSizeObserver);
      } catch (e) {
        reject(e);
        this._getCacheSizePromise = null;
        this._getCacheSizeObserver = null;
      }
    });

    return this._getCacheSizePromise;
  },

  _getQuotaUsage() {
    this._cancelGetQuotaUsage();
    this._getQuotaUsagePromise = new Promise(resolve => {
      let onUsageResult = request => {
        if (request.resultCode == Cr.NS_OK) {
          let items = request.result;
          for (let item of items) {
            if (!item.persisted && item.usage <= 0) {
              // An non-persistent-storage site with 0 byte quota usage is redundant for us so skip it.
              continue;
            }
            let principal =
              Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(item.origin);
            let uri = principal.URI;
            if (uri.scheme == "http" || uri.scheme == "https") {
              let site = this._getOrInsertSite(uri.host);
              // Assume 3 sites:
              //   - Site A (not persisted): https://www.foo.com
              //   - Site B (not persisted): https://www.foo.com^userContextId=2
              //   - Site C (persisted):     https://www.foo.com:1234
              // Although only C is persisted, grouping by host, as a result,
              // we still mark as persisted here under this host group.
              if (item.persisted) {
                site.persisted = true;
              }
              if (site.lastAccessed < item.lastAccessed) {
                site.lastAccessed = item.lastAccessed;
              }
              site.principals.push(principal);
              site.quotaUsage += item.usage;
            }
          }
        }
        resolve();
      };
      // XXX: The work of integrating localStorage into Quota Manager is in progress.
      //      After the bug 742822 and 1286798 landed, localStorage usage will be included.
      //      So currently only get indexedDB usage.
      this._quotaUsageRequest = this._qms.getUsage(onUsageResult);
    });
    return this._getQuotaUsagePromise;
  },

  _getAllCookies() {
    let cookiesEnum = Services.cookies.enumerator;
    while (cookiesEnum.hasMoreElements()) {
      let cookie = cookiesEnum.getNext().QueryInterface(Ci.nsICookie2);
      let site = this._getOrInsertSite(cookie.rawHost);
      site.cookies.push(cookie);
      if (site.lastAccessed < cookie.lastAccessed) {
        site.lastAccessed = cookie.lastAccessed;
      }
    }
  },

  _cancelGetQuotaUsage() {
    if (this._quotaUsageRequest) {
      this._quotaUsageRequest.cancel();
      this._quotaUsageRequest = null;
    }
  },

  _updateAppCache() {
    let groups = this._appCache.getGroups();
    for (let group of groups) {
      let cache = this._appCache.getActiveCache(group);
      if (cache.usage <= 0) {
        // A site with 0 byte appcache usage is redundant for us so skip it.
        continue;
      }
      let principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(group);
      let uri = principal.URI;
      let site = this._getOrInsertSite(uri.host);
      if (!site.principals.some(p => p.origin == principal.origin)) {
        site.principals.push(principal);
      }
      site.appCacheList.push(cache);
    }
  },

  getTotalUsage() {
    return this._getQuotaUsagePromise.then(() => {
      let usage = 0;
      for (let site of this._sites.values()) {
        for (let cache of site.appCacheList) {
          usage += cache.usage;
        }
        usage += site.quotaUsage;
      }
      return usage;
    });
  },

  getSites() {
    return this._getQuotaUsagePromise.then(() => {
      let list = [];
      for (let [host, site] of this._sites) {
        let usage = site.quotaUsage;
        for (let cache of site.appCacheList) {
          usage += cache.usage;
        }
        list.push({
          baseDomain: site.baseDomain,
          cookies: site.cookies,
          host,
          usage,
          persisted: site.persisted,
          lastAccessed: new Date(site.lastAccessed / 1000),
        });
      }
      return list;
    });
  },

  _removePermission(site) {
    let removals = new Set();
    for (let principal of site.principals) {
      let { originNoSuffix } = principal;
      if (removals.has(originNoSuffix)) {
        // In case of encountering
        //   - https://www.foo.com
        //   - https://www.foo.com^userContextId=2
        // because setting/removing permission is across OAs already so skip the same origin without suffix
        continue;
      }
      removals.add(originNoSuffix);
      Services.perms.removeFromPrincipal(principal, "persistent-storage");
    }
  },

  _removeQuotaUsage(site) {
    let promises = [];
    let removals = new Set();
    for (let principal of site.principals) {
      let { originNoSuffix } = principal;
      if (removals.has(originNoSuffix)) {
        // In case of encountering
        //   - https://www.foo.com
        //   - https://www.foo.com^userContextId=2
        // below we have already removed across OAs so skip the same origin without suffix
        continue;
      }
      removals.add(originNoSuffix);
      promises.push(new Promise(resolve => {
        // We are clearing *All* across OAs so need to ensure a principal without suffix here,
        // or the call of `clearStoragesForPrincipal` would fail.
        principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(originNoSuffix);
        let request = this._qms.clearStoragesForPrincipal(principal, null, true);
        request.callback = resolve;
      }));
    }
    return Promise.all(promises);
  },

  _removeAppCache(site) {
    for (let cache of site.appCacheList) {
      cache.discard();
    }
  },

  _removeCookies(site) {
    for (let cookie of site.cookies) {
      Services.cookies.remove(
        cookie.host, cookie.name, cookie.path, false, cookie.originAttributes);
    }
    site.cookies = [];
  },

  _unregisterServiceWorker(serviceWorker) {
    return new Promise(resolve => {
      let unregisterCallback = {
        unregisterSucceeded: resolve,
        unregisterFailed: resolve, // We don't care about failures.
        QueryInterface: XPCOMUtils.generateQI([Ci.nsIServiceWorkerUnregisterCallback])
      };
      serviceWorkerManager.propagateUnregister(serviceWorker.principal, unregisterCallback, serviceWorker.scope);
    });
  },

  _removeServiceWorkersForSites(sites) {
    let promises = [];
    let serviceWorkers = serviceWorkerManager.getAllRegistrations();
    for (let i = 0; i < serviceWorkers.length; i++) {
      let sw = serviceWorkers.queryElementAt(i, Ci.nsIServiceWorkerRegistrationInfo);
      // Sites are grouped and removed by host so we unregister service workers by the same host as well
      if (sites.has(sw.principal.URI.host)) {
        promises.push(this._unregisterServiceWorker(sw));
      }
    }
    return Promise.all(promises);
  },

  remove(hosts) {
    let unknownHost = "";
    let targetSites = new Map();
    for (let host of hosts) {
      let site = this._sites.get(host);
      if (site) {
        this._removePermission(site);
        this._removeAppCache(site);
        this._removeCookies(site);
        Services.obs.notifyObservers(null, "browser:purge-domain-data", host);
        targetSites.set(host, site);
      } else {
        unknownHost = host;
        break;
      }
    }

    if (targetSites.size > 0) {
      this._removeServiceWorkersForSites(targetSites)
          .then(() => {
            let promises = [];
            for (let [, site] of targetSites) {
              promises.push(this._removeQuotaUsage(site));
            }
            return Promise.all(promises);
          })
          .then(() => this.updateSites());
    }
    if (unknownHost) {
      throw `SiteDataManager: removing unknown site of ${unknownHost}`;
    }
  },

  /**
   * In the specified window, shows a prompt for removing
   * all site data, warning the user that this may log them
   * out of websites.
   *
   * @param {mozIDOMWindowProxy} a parent DOM window to host the dialog.
   * @returns a boolean whether the user confirmed the prompt.
   */
  promptSiteDataRemoval(win) {
    let brandName = gBrandBundle.GetStringFromName("brandShortName");
    let flags =
      Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0 +
      Services.prompt.BUTTON_TITLE_CANCEL * Services.prompt.BUTTON_POS_1 +
      Services.prompt.BUTTON_POS_0_DEFAULT;
    let title = gStringBundle.GetStringFromName("clearSiteDataPromptTitle");
    let text = gStringBundle.formatStringFromName("clearSiteDataPromptText", [brandName], 1);
    let btn0Label = gStringBundle.GetStringFromName("clearSiteDataNow");

    let result = Services.prompt.confirmEx(
      win, title, text, flags, btn0Label, null, null, null, {});
    return result == 0;
  },

  /**
   * Clears all site data and cache
   *
   * @returns a Promise that resolves when the data is cleared.
   */
  async removeAll() {
    this.removeCache();
    return this.removeSiteData();
  },

  /**
   * Clears the entire network cache.
   */
  removeCache() {
    Services.cache2.clear();
  },

  /**
   * Clears all site data, which currently means
   *   - Cookies
   *   - AppCache
   *   - ServiceWorkers
   *   - Quota Managed Storage
   *   - persistent-storage permissions
   *
   * @returns a Promise that resolves with the cache size on disk in bytes
   */
  async removeSiteData() {
    Services.cookies.removeAll();
    OfflineAppCacheHelper.clear();

    // Iterate through the service workers and remove them.
    let promises = [];
    let serviceWorkers = serviceWorkerManager.getAllRegistrations();
    for (let i = 0; i < serviceWorkers.length; i++) {
      let sw = serviceWorkers.queryElementAt(i, Ci.nsIServiceWorkerRegistrationInfo);
      promises.push(this._unregisterServiceWorker(sw));
    }
    await Promise.all(promises);

    // Refresh sites using quota usage again.
    // This is for the case:
    //   1. User goes to the about:preferences Site Data section.
    //   2. With the about:preferences opened, user visits another website.
    //   3. The website saves to quota usage, like indexedDB.
    //   4. User goes back to the Site Data section and commands to clear all site data.
    // For this case, we should refresh the site list so not to miss the website in the step 3.
    // We don't do "Clear All" on the quota manager like the cookie, appcache, http cache above
    // because that would clear browser data as well too,
    // see https://bugzilla.mozilla.org/show_bug.cgi?id=1312361#c9
    this._sites.clear();
    await this._getQuotaUsage();
    promises = [];
    for (let site of this._sites.values()) {
      this._removePermission(site);
      promises.push(this._removeQuotaUsage(site));
    }
    return Promise.all(promises).then(() => this.updateSites());
  },
};
PK
!<+�e��modules/ThemeVariableMap.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

var EXPORTED_SYMBOLS = ["ThemeVariableMap"];

const ThemeVariableMap = [
  ["--lwt-accent-color-inactive", "accentcolorInactive"],
  ["--lwt-background-alignment", "backgroundsAlignment"],
  ["--lwt-background-tiling", "backgroundsTiling"],
  ["--tab-loading-fill", "tab_loading", "tabbrowser-tabs"],
  ["--lwt-tab-text", "tab_text"],
  ["--tab-line-color", "tab_line", "tabbrowser-tabs"],
  ["--toolbar-bgcolor", "toolbarColor"],
  ["--toolbar-color", "toolbar_text"],
  ["--url-and-searchbar-background-color", "toolbar_field"],
  ["--url-and-searchbar-color", "toolbar_field_text"],
  ["--lwt-toolbar-field-border-color", "toolbar_field_border"],
  ["--urlbar-separator-color", "toolbar_field_separator"],
  ["--tabs-border-color", "toolbar_top_separator", "navigator-toolbox"],
  ["--lwt-toolbar-vertical-separator", "toolbar_vertical_separator"],
  ["--toolbox-border-bottom-color", "toolbar_bottom_separator"],
  ["--lwt-toolbarbutton-icon-fill", "icon_color"],
  ["--lwt-toolbarbutton-icon-fill-attention", "icon_attention_color"],
  ["--lwt-toolbarbutton-hover-background", "button_background_hover"],
  ["--lwt-toolbarbutton-active-background", "button_background_active"],
  ["--lwt-selected-tab-background-color", "tab_selected"],
];
PK
!<)ou:��modules/TransientPrefs.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = ["TransientPrefs"];

ChromeUtils.import("resource://gre/modules/Preferences.jsm");

var prefVisibility = new Map;

/* Use for preferences that should only be visible when they've been modified.
   When reset to their default state, they remain visible until restarting the
   application. */

var TransientPrefs = {
  prefShouldBeVisible(prefName) {
    if (Preferences.isSet(prefName))
      prefVisibility.set(prefName, true);

    return !!prefVisibility.get(prefName);
  }
};
PK
!<�66modules/UITour.jsm// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

"use strict";

var EXPORTED_SYMBOLS = ["UITour"];

ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/TelemetryController.jsm");
ChromeUtils.import("resource://gre/modules/Timer.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");

Cu.importGlobalProperties(["URL"]);

ChromeUtils.defineModuleGetter(this, "BrowserUITelemetry",
  "resource:///modules/BrowserUITelemetry.jsm");
ChromeUtils.defineModuleGetter(this, "CustomizableUI",
  "resource:///modules/CustomizableUI.jsm");
ChromeUtils.defineModuleGetter(this, "FxAccounts",
  "resource://gre/modules/FxAccounts.jsm");
ChromeUtils.defineModuleGetter(this, "LightweightThemeManager",
  "resource://gre/modules/LightweightThemeManager.jsm");
ChromeUtils.defineModuleGetter(this, "PageActions",
  "resource:///modules/PageActions.jsm");
ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
  "resource://gre/modules/PrivateBrowsingUtils.jsm");
ChromeUtils.defineModuleGetter(this, "ProfileAge",
  "resource://gre/modules/ProfileAge.jsm");
ChromeUtils.defineModuleGetter(this, "ReaderParent",
  "resource:///modules/ReaderParent.jsm");
ChromeUtils.defineModuleGetter(this, "ResetProfile",
  "resource://gre/modules/ResetProfile.jsm");
ChromeUtils.defineModuleGetter(this, "UITelemetry",
  "resource://gre/modules/UITelemetry.jsm");
ChromeUtils.defineModuleGetter(this, "UpdateUtils",
  "resource://gre/modules/UpdateUtils.jsm");

// See LOG_LEVELS in Console.jsm. Common examples: "All", "Info", "Warn", & "Error".
const PREF_LOG_LEVEL      = "browser.uitour.loglevel";
const PREF_SEENPAGEIDS    = "browser.uitour.seenPageIDs";

const BACKGROUND_PAGE_ACTIONS_ALLOWED = new Set([
  "forceShowReaderIcon",
  "getConfiguration",
  "getTreatmentTag",
  "hideHighlight",
  "hideInfo",
  "hideMenu",
  "ping",
  "registerPageID",
  "setConfiguration",
  "setTreatmentTag",
]);
const MAX_BUTTONS         = 4;

const BUCKET_NAME         = "UITour";
const BUCKET_TIMESTEPS    = [
  1 * 60 * 1000, // Until 1 minute after tab is closed/inactive.
  3 * 60 * 1000, // Until 3 minutes after tab is closed/inactive.
  10 * 60 * 1000, // Until 10 minutes after tab is closed/inactive.
  60 * 60 * 1000, // Until 1 hour after tab is closed/inactive.
];

// Time after which seen Page IDs expire.
const SEENPAGEID_EXPIRY  = 8 * 7 * 24 * 60 * 60 * 1000; // 8 weeks.

// Prefix for any target matching a search engine.
const TARGET_SEARCHENGINE_PREFIX = "searchEngine-";

// Create a new instance of the ConsoleAPI so we can control the maxLogLevel with a pref.
XPCOMUtils.defineLazyGetter(this, "log", () => {
  let ConsoleAPI = ChromeUtils.import("resource://gre/modules/Console.jsm", {}).ConsoleAPI;
  let consoleOptions = {
    maxLogLevelPref: PREF_LOG_LEVEL,
    prefix: "UITour",
  };
  return new ConsoleAPI(consoleOptions);
});

var UITour = {
  url: null,
  seenPageIDs: null,
  // This map is not persisted and is used for
  // building the content source of a potential tour.
  pageIDsForSession: new Map(),
  pageIDSourceBrowsers: new WeakMap(),
  /* Map from browser chrome windows to a Set of <browser>s in which a tour is open (both visible and hidden) */
  tourBrowsersByWindow: new WeakMap(),
  // Menus opened by api users explictly through `Mozilla.UITour.showMenu` call
  noautohideMenus: new Set(),
  availableTargetsCache: new WeakMap(),
  clearAvailableTargetsCache() {
    this.availableTargetsCache = new WeakMap();
  },

  _annotationPanelMutationObservers: new WeakMap(),

  highlightEffects: ["random", "wobble", "zoom", "color"],
  targets: new Map([
    ["accountStatus", {
      query: (aDocument) => {
        // If the user is logged in, use the avatar element.
        let fxAFooter = aDocument.getElementById("appMenu-fxa-container");
        if (fxAFooter.getAttribute("fxastatus")) {
          return aDocument.getElementById("appMenu-fxa-avatar");
        }

        // Otherwise use the sync setup icon.
        let statusButton = aDocument.getElementById("appMenu-fxa-label");
        return aDocument.getAnonymousElementByAttribute(statusButton,
                                                        "class",
                                                        "toolbarbutton-icon");
      },
      // This is a fake widgetName starting with the "appMenu-" prefix so we know
      // to automatically open the appMenu when annotating this target.
      widgetName: "appMenu-fxa-label",
    }],
    ["addons",      {query: "#appMenu-addons-button"}],
    ["appMenu",     {
      addTargetListener: (aDocument, aCallback) => {
        let panelPopup = aDocument.defaultView.PanelUI.panel;
        panelPopup.addEventListener("popupshown", aCallback);
      },
      query: "#PanelUI-button",
      removeTargetListener: (aDocument, aCallback) => {
        let panelPopup = aDocument.defaultView.PanelUI.panel;
        panelPopup.removeEventListener("popupshown", aCallback);
      },
    }],
    ["backForward", {query: "#back-button"}],
    ["bookmarks",   {query: "#bookmarks-menu-button"}],
    ["controlCenter-trackingUnblock", controlCenterTrackingToggleTarget(true)],
    ["controlCenter-trackingBlock", controlCenterTrackingToggleTarget(false)],
    ["customize",   {
      query: "#appMenu-customize-button",
      widgetName: "appMenu-customize-button",
    }],
    ["devtools",    {
      query: "#appMenu-developer-button",
      widgetName: "appMenu-developer-button",
    }],
    ["forget", {
      allowAdd: true,
      query: "#panic-button",
      widgetName: "panic-button",
    }],
    ["help",        {query: "#appMenu-help-button"}],
    ["home",        {query: "#home-button"}],
    ["library",     {query: "#appMenu-library-button"}],
    ["pocket", {
      allowAdd: true,
      query: (aDocument) => {
        // The pocket's urlbar page action button is pre-defined in the DOM.
        // It would be hidden if toggled off from the urlbar.
        let node = aDocument.getElementById("pocket-button-box");
        if (node && !node.hidden) {
          return node;
        }
        return aDocument.getElementById("pageAction-panel-pocket");
      },
    }],
    ["privateWindow", {query: "#appMenu-private-window-button"}],
    ["quit",        {query: "#appMenu-quit-button"}],
    ["readerMode-urlBar", {query: "#reader-mode-button"}],
    ["search",      {
      infoPanelOffsetX: 18,
      infoPanelPosition: "after_start",
      query: "#searchbar",
      widgetName: "search-container",
    }],
    ["searchIcon", {
      query: (aDocument) => {
        let searchbar = aDocument.getElementById("searchbar");
        return aDocument.getAnonymousElementByAttribute(searchbar,
                                                        "anonid",
                                                        "searchbar-search-button");
      },
      widgetName: "search-container",
    }],
    ["searchPrefsLink", {
      query: (aDocument) => {
        let element = null;
        let popup = aDocument.getElementById("PopupSearchAutoComplete");
        if (popup.state != "open")
          return null;
        element = aDocument.getAnonymousElementByAttribute(popup,
                                                           "anonid",
                                                           "search-settings");
        if (!element || !UITour.isElementVisible(element)) {
          return null;
        }
        return element;
      },
    }],
    ["selectedTabIcon", {
      query: (aDocument) => {
        let selectedtab = aDocument.defaultView.gBrowser.selectedTab;
        let element = aDocument.getAnonymousElementByAttribute(selectedtab,
                                                               "anonid",
                                                               "tab-icon-image");
        if (!element || !UITour.isElementVisible(element)) {
          return null;
        }
        return element;
      },
    }],
    ["trackingProtection", {
      query: "#tracking-protection-icon",
    }],
    ["urlbar",      {
      query: "#urlbar",
      widgetName: "urlbar-container",
    }],
    ["pageActionButton", {
      query: "#pageActionButton"
    }],
    ["pageAction-bookmark", {
      query: (aDocument) => {
        // The bookmark's urlbar page action button is pre-defined in the DOM.
        // It would be hidden if toggled off from the urlbar.
        let node = aDocument.getElementById("star-button-box");
        if (node && !node.hidden) {
          return node;
        }
        return aDocument.getElementById("pageAction-panel-bookmark");
      },
    }],
    ["pageAction-copyURL", {
      query: (aDocument) => {
        return aDocument.getElementById("pageAction-urlbar-copyURL") ||
               aDocument.getElementById("pageAction-panel-copyURL");
      },
    }],
    ["pageAction-emailLink", {
      query: (aDocument) => {
        return aDocument.getElementById("pageAction-urlbar-emailLink") ||
               aDocument.getElementById("pageAction-panel-emailLink");
      },
    }],
    ["pageAction-sendToDevice", {
      query: (aDocument) => {
        return aDocument.getElementById("pageAction-urlbar-sendToDevice") ||
               aDocument.getElementById("pageAction-panel-sendToDevice");
      },
    }],
    ["screenshots", {
      query: (aDocument) => {
        return aDocument.getElementById("pageAction-urlbar-screenshots") ||
               aDocument.getElementById("pageAction-panel-screenshots");
      },
    }]
  ]),

  init() {
    log.debug("Initializing UITour");
    // Lazy getter is initialized here so it can be replicated any time
    // in a test.
    delete this.seenPageIDs;
    Object.defineProperty(this, "seenPageIDs", {
      get: this.restoreSeenPageIDs.bind(this),
      configurable: true,
    });

    delete this.url;
    XPCOMUtils.defineLazyGetter(this, "url", function() {
      return Services.urlFormatter.formatURLPref("browser.uitour.url");
    });

    // Clear the availableTargetsCache on widget changes.
    let listenerMethods = [
      "onWidgetAdded",
      "onWidgetMoved",
      "onWidgetRemoved",
      "onWidgetReset",
      "onAreaReset",
    ];
    CustomizableUI.addListener(listenerMethods.reduce((listener, method) => {
      listener[method] = () => this.clearAvailableTargetsCache();
      return listener;
    }, {}));
  },

  restoreSeenPageIDs() {
    delete this.seenPageIDs;

    if (UITelemetry.enabled) {
      let dateThreshold = Date.now() - SEENPAGEID_EXPIRY;

      try {
        let data = Services.prefs.getCharPref(PREF_SEENPAGEIDS);
        data = new Map(JSON.parse(data));

        for (let [pageID, details] of data) {

          if (typeof pageID != "string" ||
              typeof details != "object" ||
              typeof details.lastSeen != "number" ||
              details.lastSeen < dateThreshold) {

            data.delete(pageID);
          }
        }

        this.seenPageIDs = data;
      } catch (e) {}
    }

    if (!this.seenPageIDs)
      this.seenPageIDs = new Map();

    this.persistSeenIDs();

    return this.seenPageIDs;
  },

  addSeenPageID(aPageID) {
    if (!UITelemetry.enabled)
      return;

    this.seenPageIDs.set(aPageID, {
      lastSeen: Date.now(),
    });

    this.persistSeenIDs();
  },

  persistSeenIDs() {
    if (this.seenPageIDs.size === 0) {
      Services.prefs.clearUserPref(PREF_SEENPAGEIDS);
      return;
    }

    Services.prefs.setCharPref(PREF_SEENPAGEIDS,
                               JSON.stringify([...this.seenPageIDs]));
  },

  onPageEvent(aMessage, aEvent) {
    let browser = aMessage.target;
    let window = browser.ownerGlobal;

    // Does the window have tabs? We need to make sure since windowless browsers do
    // not have tabs.
    if (!window.gBrowser) {
      // When using windowless browsers we don't have a valid |window|. If that's the case,
      // use the most recent window as a target for UITour functions (see Bug 1111022).
      window = Services.wm.getMostRecentWindow("navigator:browser");
    }

    let messageManager = browser.messageManager;

    log.debug("onPageEvent:", aEvent.detail, aMessage);

    if (typeof aEvent.detail != "object") {
      log.warn("Malformed event - detail not an object");
      return false;
    }

    let action = aEvent.detail.action;
    if (typeof action != "string" || !action) {
      log.warn("Action not defined");
      return false;
    }

    let data = aEvent.detail.data;
    if (typeof data != "object") {
      log.warn("Malformed event - data not an object");
      return false;
    }

    if ((aEvent.pageVisibilityState == "hidden" ||
         aEvent.pageVisibilityState == "unloaded") &&
        !BACKGROUND_PAGE_ACTIONS_ALLOWED.has(action)) {
      log.warn("Ignoring disallowed action from a hidden page:", action);
      return false;
    }

    switch (action) {
      case "registerPageID": {
        if (typeof data.pageID != "string") {
          log.warn("registerPageID: pageID must be a string");
          break;
        }

        this.pageIDsForSession.set(data.pageID, {lastSeen: Date.now()});

        // The rest is only relevant if Telemetry is enabled.
        if (!UITelemetry.enabled) {
          log.debug("registerPageID: Telemetry disabled, not doing anything");
          break;
        }

        // We don't want to allow BrowserUITelemetry.BUCKET_SEPARATOR in the
        // pageID, as it could make parsing the telemetry bucket name difficult.
        if (data.pageID.includes(BrowserUITelemetry.BUCKET_SEPARATOR)) {
          log.warn("registerPageID: Invalid page ID specified");
          break;
        }

        this.addSeenPageID(data.pageID);
        this.pageIDSourceBrowsers.set(browser, data.pageID);
        this.setTelemetryBucket(data.pageID);

        break;
      }

      case "showHighlight": {
        let targetPromise = this.getTarget(window, data.target);
        targetPromise.then(target => {
          if (!target.node) {
            log.error("UITour: Target could not be resolved: " + data.target);
            return;
          }
          let effect = undefined;
          if (this.highlightEffects.includes(data.effect)) {
            effect = data.effect;
          }
          this.showHighlight(window, target, effect);
        }).catch(log.error);
        break;
      }

      case "hideHighlight": {
        this.hideHighlight(window);
        break;
      }

      case "showInfo": {
        let targetPromise = this.getTarget(window, data.target, true);
        targetPromise.then(target => {
          if (!target.node) {
            log.error("UITour: Target could not be resolved: " + data.target);
            return;
          }

          let iconURL = null;
          if (typeof data.icon == "string")
            iconURL = this.resolveURL(browser, data.icon);

          let buttons = [];
          if (Array.isArray(data.buttons) && data.buttons.length > 0) {
            for (let buttonData of data.buttons) {
              if (typeof buttonData == "object" &&
                  typeof buttonData.label == "string" &&
                  typeof buttonData.callbackID == "string") {
                let callback = buttonData.callbackID;
                let button = {
                  label: buttonData.label,
                  callback: event => {
                    this.sendPageCallback(messageManager, callback);
                  },
                };

                if (typeof buttonData.icon == "string")
                  button.iconURL = this.resolveURL(browser, buttonData.icon);

                if (typeof buttonData.style == "string")
                  button.style = buttonData.style;

                buttons.push(button);

                if (buttons.length == MAX_BUTTONS) {
                  log.warn("showInfo: Reached limit of allowed number of buttons");
                  break;
                }
              }
            }
          }

          let infoOptions = {};
          if (typeof data.closeButtonCallbackID == "string") {
            infoOptions.closeButtonCallback = () => {
              this.sendPageCallback(messageManager, data.closeButtonCallbackID);
            };
          }
          if (typeof data.targetCallbackID == "string") {
            infoOptions.targetCallback = details => {
              this.sendPageCallback(messageManager, data.targetCallbackID, details);
            };
          }

          this.showInfo(window, target, data.title, data.text, iconURL, buttons, infoOptions);
        }).catch(log.error);
        break;
      }

      case "hideInfo": {
        this.hideInfo(window);
        break;
      }

      case "previewTheme": {
        this.previewTheme(data.theme);
        break;
      }

      case "resetTheme": {
        this.resetTheme();
        break;
      }

      case "showMenu": {
        this.noautohideMenus.add(data.name);
        this.showMenu(window, data.name, () => {
          if (typeof data.showCallbackID == "string")
            this.sendPageCallback(messageManager, data.showCallbackID);
        });
        break;
      }

      case "hideMenu": {
        this.noautohideMenus.delete(data.name);
        this.hideMenu(window, data.name);
        break;
      }

      case "showNewTab": {
        this.showNewTab(window, browser);
        break;
      }

      case "getConfiguration": {
        if (typeof data.configuration != "string") {
          log.warn("getConfiguration: No configuration option specified");
          return false;
        }

        this.getConfiguration(messageManager, window, data.configuration, data.callbackID);
        break;
      }

      case "setConfiguration": {
        if (typeof data.configuration != "string") {
          log.warn("setConfiguration: No configuration option specified");
          return false;
        }

        this.setConfiguration(window, data.configuration, data.value);
        break;
      }

      case "openPreferences": {
        if (typeof data.pane != "string" && typeof data.pane != "undefined") {
          log.warn("openPreferences: Invalid pane specified");
          return false;
        }
        window.openPreferences(data.pane, { origin: "UITour" });
        break;
      }

      case "showFirefoxAccounts": {
        Promise.resolve().then(() => {
          return data.email ? FxAccounts.config.promiseEmailURI(data.email, "uitour") :
                              FxAccounts.config.promiseSignUpURI("uitour");
        }).then(uri => {
          const url = new URL(uri);
          // Call our helper to validate extraURLCampaignParams and populate URLSearchParams
          if (!this._populateCampaignParams(url, data.extraURLCampaignParams)) {
            log.warn("showFirefoxAccounts: invalid campaign args specified");
            return;
          }

          // We want to replace the current tab.
          browser.loadURI(url.href);
        });
        break;
      }

      case "showConnectAnotherDevice": {
        FxAccounts.config.promiseConnectDeviceURI("uitour").then(uri => {
          const url = new URL(uri);
          // Call our helper to validate extraURLCampaignParams and populate URLSearchParams
          if (!this._populateCampaignParams(url, data.extraURLCampaignParams)) {
            log.warn("showConnectAnotherDevice: invalid campaign args specified");
            return;
          }

          // We want to replace the current tab.
          browser.loadURI(url.href);
        });
        break;
      }

      case "resetFirefox": {
        // Open a reset profile dialog window.
        if (ResetProfile.resetSupported()) {
          ResetProfile.openConfirmationDialog(window);
        }
        break;
      }

      case "addNavBarWidget": {
        // Add a widget to the toolbar
        let targetPromise = this.getTarget(window, data.name);
        targetPromise.then(target => {
          this.addNavBarWidget(target, messageManager, data.callbackID);
        }).catch(log.error);
        break;
      }

      case "setDefaultSearchEngine": {
        let enginePromise = this.selectSearchEngine(data.identifier);
        enginePromise.catch(Cu.reportError);
        break;
      }

      case "setTreatmentTag": {
        let name = data.name;
        let value = data.value;
        Services.prefs.setStringPref("browser.uitour.treatment." + name, value);
        // The notification is only meant to be used in tests.
        UITourHealthReport.recordTreatmentTag(name, value)
                          .then(() => this.notify("TreatmentTag:TelemetrySent"));
        break;
      }

      case "getTreatmentTag": {
        let name = data.name;
        let value;
        try {
          value = Services.prefs.getStringPref("browser.uitour.treatment." + name);
        } catch (ex) {}
        this.sendPageCallback(messageManager, data.callbackID, { value });
        break;
      }

      case "setSearchTerm": {
        let targetPromise = this.getTarget(window, "search");
        targetPromise.then(target => {
          let searchbar = target.node;
          searchbar.value = data.term;
          searchbar.updateGoButtonVisibility();
        });
        break;
      }

      case "openSearchPanel": {
        let targetPromise = this.getTarget(window, "search");
        targetPromise.then(target => {
          let searchbar = target.node;

          if (searchbar.textbox.open) {
            this.sendPageCallback(messageManager, data.callbackID);
          } else {
            let onPopupShown = () => {
              searchbar.textbox.popup.removeEventListener("popupshown", onPopupShown);
              this.sendPageCallback(messageManager, data.callbackID);
            };

            searchbar.textbox.popup.addEventListener("popupshown", onPopupShown);
            searchbar.openSuggestionsPanel();
          }
        }).catch(Cu.reportError);
        break;
      }

      case "ping": {
        if (typeof data.callbackID == "string")
          this.sendPageCallback(messageManager, data.callbackID);
        break;
      }

      case "forceShowReaderIcon": {
        ReaderParent.forceShowReaderIcon(browser);
        break;
      }

      case "toggleReaderMode": {
        let targetPromise = this.getTarget(window, "readerMode-urlBar");
        targetPromise.then(target => {
          ReaderParent.toggleReaderMode({target: target.node});
        });
        break;
      }

      case "closeTab": {
        // Find the <tabbrowser> element of the <browser> for which the event
        // was generated originally. If the browser where the UI tour is loaded
        // is windowless, just ignore the request to close the tab. The request
        // is also ignored if this is the only tab in the window.
        let tabBrowser = browser.ownerGlobal.gBrowser;
        if (tabBrowser && tabBrowser.browsers.length > 1) {
          tabBrowser.removeTab(tabBrowser.getTabForBrowser(browser));
        }
        break;
      }
    }

    // For performance reasons, only call initForBrowser if we did something
    // that will require a teardownTourForBrowser call later.
    // getConfiguration (called from about:home) doesn't require any future
    // uninitialization.
    if (action != "getConfiguration")
      this.initForBrowser(browser, window);

    return true;
  },

  initForBrowser(aBrowser, window) {
    let gBrowser = window.gBrowser;

    if (gBrowser) {
        gBrowser.tabContainer.addEventListener("TabSelect", this);
    }

    if (!this.tourBrowsersByWindow.has(window)) {
      this.tourBrowsersByWindow.set(window, new Set());
    }
    this.tourBrowsersByWindow.get(window).add(aBrowser);

    Services.obs.addObserver(this, "message-manager-close");

    window.addEventListener("SSWindowClosing", this);
  },

  handleEvent(aEvent) {
    log.debug("handleEvent: type =", aEvent.type, "event =", aEvent);
    switch (aEvent.type) {
      case "TabSelect": {
        let window = aEvent.target.ownerGlobal;

        // Teardown the browser of the tab we just switched away from.
        if (aEvent.detail && aEvent.detail.previousTab) {
          let previousTab = aEvent.detail.previousTab;
          let openTourWindows = this.tourBrowsersByWindow.get(window);
          if (openTourWindows.has(previousTab.linkedBrowser)) {
            this.teardownTourForBrowser(window, previousTab.linkedBrowser, false);
          }
        }

        break;
      }

      case "SSWindowClosing": {
        let window = aEvent.target;
        this.teardownTourForWindow(window);
        break;
      }
    }
  },

  observe(aSubject, aTopic, aData) {
    log.debug("observe: aTopic =", aTopic);
    switch (aTopic) {
      // The browser message manager is disconnected when the <browser> is
      // destroyed and we want to teardown at that point.
      case "message-manager-close": {
        let winEnum = Services.wm.getEnumerator("navigator:browser");
        while (winEnum.hasMoreElements()) {
          let window = winEnum.getNext();
          if (window.closed)
            continue;

          let tourBrowsers = this.tourBrowsersByWindow.get(window);
          if (!tourBrowsers)
            continue;

          for (let browser of tourBrowsers) {
            let messageManager = browser.messageManager;
            if (aSubject != messageManager) {
              continue;
            }

            this.teardownTourForBrowser(window, browser, true);
            return;
          }
        }
        break;
      }
    }
  },

  // Given a string that is a JSONified represenation of an object with
  // additional utm_* URL params that should be appended, validate and append
  // them to the passed URL object. Returns true if the params
  // were validated and appended, and false if the request should be ignored.
  _populateCampaignParams(url, extraURLCampaignParams) {
    // We are extra paranoid about what params we allow to be appended.
    if (typeof extraURLCampaignParams == "undefined") {
      // no params, so it's all good.
      return true;
    }
    if (typeof extraURLCampaignParams != "string") {
      log.warn("_populateCampaignParams: extraURLCampaignParams is not a string");
      return false;
    }
    let campaignParams;
    try {
      if (extraURLCampaignParams) {
        campaignParams = JSON.parse(extraURLCampaignParams);
        if (typeof campaignParams != "object") {
          log.warn("_populateCampaignParams: extraURLCampaignParams is not a stringified object");
          return false;
        }
      }
    } catch (ex) {
      log.warn("_populateCampaignParams: extraURLCampaignParams is not a JSON object");
      return false;
    }
    if (campaignParams) {
      // The regex that the name of each param must match - there's no
      // character restriction on the value - they will be escaped as necessary.
      let reSimpleString = /^[-_a-zA-Z0-9]*$/;
      for (let name in campaignParams) {
        let value = campaignParams[name];
        if (typeof name != "string" || typeof value != "string" ||
            !name.startsWith("utm_") ||
            value.length == 0 ||
            !reSimpleString.test(name)) {
          log.warn("_populateCampaignParams: invalid campaign param specified");
          return false;
        }
        url.searchParams.append(name, value);
      }
    }
    return true;
  },

  setTelemetryBucket(aPageID) {
    let bucket = BUCKET_NAME + BrowserUITelemetry.BUCKET_SEPARATOR + aPageID;
    BrowserUITelemetry.setBucket(bucket);
  },

  setExpiringTelemetryBucket(aPageID, aType) {
    let bucket = BUCKET_NAME + BrowserUITelemetry.BUCKET_SEPARATOR + aPageID +
                 BrowserUITelemetry.BUCKET_SEPARATOR + aType;

    BrowserUITelemetry.setExpiringBucket(bucket,
                                         BUCKET_TIMESTEPS);
  },

  // This is registered with UITelemetry by BrowserUITelemetry, so that UITour
  // can remain lazy-loaded on-demand.
  getTelemetry() {
    return {
      seenPageIDs: [...this.seenPageIDs.keys()],
    };
  },

  /**
   * Tear down a tour from a tab e.g. upon switching/closing tabs.
   */
  async teardownTourForBrowser(aWindow, aBrowser, aTourPageClosing = false) {
    log.debug("teardownTourForBrowser: aBrowser = ", aBrowser, aTourPageClosing);

    if (this.pageIDSourceBrowsers.has(aBrowser)) {
      let pageID = this.pageIDSourceBrowsers.get(aBrowser);
      this.setExpiringTelemetryBucket(pageID, aTourPageClosing ? "closed" : "inactive");
    }

    let openTourBrowsers = this.tourBrowsersByWindow.get(aWindow);
    if (aTourPageClosing && openTourBrowsers) {
      openTourBrowsers.delete(aBrowser);
    }

    this.hideHighlight(aWindow);
    this.hideInfo(aWindow);

    let panels = [
      {
        name: "appMenu",
        node: aWindow.PanelUI.panel,
        events: [
          [ "popuphidden", this.onPanelHidden ],
          [ "popuphiding", this.onAppMenuHiding ],
          [ "ViewShowing", this.onAppMenuSubviewShowing ]
        ]
      },
      {
        name: "pageActionPanel",
        node: aWindow.BrowserPageActions.panelNode,
        events: [
          [ "popuphidden", this.onPanelHidden ],
          [ "popuphiding", this.onPageActionPanelHiding ],
          [ "ViewShowing", this.onPageActionPanelSubviewShowing ]
        ]
      },
      {
        name: "controlCenter",
        node: aWindow.gIdentityHandler._identityPopup,
        events: [
          [ "popuphidden", this.onPanelHidden ],
          [ "popuphiding", this.onControlCenterHiding ]
        ]
      },
    ];
    for (let panel of panels) {
      // Ensure the menu panel is hidden and clean up panel listeners after calling hideMenu.
      if (panel.node.state != "closed") {
        await new Promise(resolve => {
          panel.node.addEventListener("popuphidden", resolve, { once: true });
          this.hideMenu(aWindow, panel.name);
        });
      }
      for (let [ name, listener ] of panel.events) {
        panel.node.removeEventListener(name, listener);
      }
    }

    this.noautohideMenus.clear();
    this.resetTheme();

    // If there are no more tour tabs left in the window, teardown the tour for the whole window.
    if (!openTourBrowsers || openTourBrowsers.size == 0) {
      this.teardownTourForWindow(aWindow);
    }
  },

  /**
   * Tear down all tours for a ChromeWindow.
   */
  teardownTourForWindow(aWindow) {
    log.debug("teardownTourForWindow");
    aWindow.gBrowser.tabContainer.removeEventListener("TabSelect", this);
    aWindow.removeEventListener("SSWindowClosing", this);

    let openTourBrowsers = this.tourBrowsersByWindow.get(aWindow);
    if (openTourBrowsers) {
      for (let browser of openTourBrowsers) {
        if (this.pageIDSourceBrowsers.has(browser)) {
          let pageID = this.pageIDSourceBrowsers.get(browser);
          this.setExpiringTelemetryBucket(pageID, "closed");
        }
      }
    }

    this.tourBrowsersByWindow.delete(aWindow);
  },

  // This function is copied to UITourListener.
  isSafeScheme(aURI) {
    let allowedSchemes = new Set(["https", "about"]);
    if (!Services.prefs.getBoolPref("browser.uitour.requireSecure"))
      allowedSchemes.add("http");

    if (!allowedSchemes.has(aURI.scheme)) {
      log.error("Unsafe scheme:", aURI.scheme);
      return false;
    }

    return true;
  },

  resolveURL(aBrowser, aURL) {
    try {
      let uri = Services.io.newURI(aURL, null, aBrowser.currentURI);

      if (!this.isSafeScheme(uri))
        return null;

      return uri.spec;
    } catch (e) {}

    return null;
  },

  sendPageCallback(aMessageManager, aCallbackID, aData = {}) {
    let detail = {data: aData, callbackID: aCallbackID};
    log.debug("sendPageCallback", detail);
    aMessageManager.sendAsyncMessage("UITour:SendPageCallback", detail);
  },

  isElementVisible(aElement) {
    let targetStyle = aElement.ownerGlobal.getComputedStyle(aElement);
    return !aElement.ownerDocument.hidden &&
             targetStyle.display != "none" &&
             targetStyle.visibility == "visible";
  },

  getTarget(aWindow, aTargetName, aSticky = false) {
    log.debug("getTarget:", aTargetName);
    if (typeof aTargetName != "string" || !aTargetName) {
      log.warn("getTarget: Invalid target name specified");
      return Promise.reject("Invalid target name specified");
    }

    let targetObject = this.targets.get(aTargetName);
    if (!targetObject) {
      log.warn("getTarget: The specified target name is not in the allowed set");
      return Promise.reject("The specified target name is not in the allowed set");
    }

    return new Promise(resolve => {
      let targetQuery = targetObject.query;
      aWindow.PanelUI.ensureReady().then(() => {
        let node;
        if (typeof targetQuery == "function") {
          try {
            node = targetQuery(aWindow.document);
          } catch (ex) {
            log.warn("getTarget: Error running target query:", ex);
            node = null;
          }
        } else {
          node = aWindow.document.querySelector(targetQuery);
        }

        resolve({
          addTargetListener: targetObject.addTargetListener,
          infoPanelOffsetX: targetObject.infoPanelOffsetX,
          infoPanelOffsetY: targetObject.infoPanelOffsetY,
          infoPanelPosition: targetObject.infoPanelPosition,
          node,
          removeTargetListener: targetObject.removeTargetListener,
          targetName: aTargetName,
          widgetName: targetObject.widgetName,
          allowAdd: targetObject.allowAdd,
        });
      }).catch(log.error);
    });
  },

  targetIsInAppMenu(aTarget) {
    let targetElement = aTarget.node;
    // Use the widget for filtering if it exists since the target may be the icon inside.
    if (aTarget.widgetName) {
      targetElement = aTarget.node.ownerDocument.getElementById(aTarget.widgetName);
    }

    return targetElement.id.startsWith("appMenu-");
  },

  targetIsInPageActionPanel(aTarget) {
    return aTarget.node.id.startsWith("pageAction-panel-");
  },

  /**
   * Called before opening or after closing a highlight or an info tooltip to see if
   * we need to open or close the menu to see the annotation's anchor.
   *
   * @param {ChromeWindow} aWindow the chrome window
   * @param {bool} aShouldOpen true means we should open the menu, otherwise false
   * @param {String} aMenuName "appMenu" or "pageActionPanel"
   */
  _setMenuStateForAnnotation(aWindow, aShouldOpen, aMenuName) {
    log.debug("_setMenuStateForAnnotation: Menu is ", aMenuName);
    log.debug("_setMenuStateForAnnotation: Menu is expected to be:", aShouldOpen ? "open" : "closed");
    let menu = aMenuName == "appMenu" ? aWindow.PanelUI.panel : aWindow.BrowserPageActions.panelNode;

    // If the panel is in the desired state, we're done.
    let panelIsOpen = menu.state != "closed";
    if (aShouldOpen == panelIsOpen) {
      log.debug("_setMenuStateForAnnotation: Menu already in expected state");
      return Promise.resolve();
    }

    // Actually show or hide the menu
    let promise = null;
    if (aShouldOpen) {
      log.debug("_setMenuStateForAnnotation: Opening the menu");
      promise = new Promise(resolve => {
        this.showMenu(aWindow, aMenuName, resolve);
      });
    } else if (!this.noautohideMenus.has(aMenuName)) {
      // If the menu was opened explictly by api user through `Mozilla.UITour.showMenu`,
      // it should be closed explictly by api user through `Mozilla.UITour.hideMenu`.
      // So we shouldn't get to here to close it for the highlight/info annotation.
      log.debug("_setMenuStateForAnnotation: Closing the menu");
      promise = new Promise(resolve => {
        menu.addEventListener("popuphidden", resolve, { once: true });
        this.hideMenu(aWindow, aMenuName);
      });
    }
    return promise;
  },

  /**
   * Ensure the target's visibility and the open/close states of menus for the target.
   *
   * @param {ChromeWindow} aChromeWindow The chrome window
   * @param {Object} aTarget The target on which we show highlight or show info.
   */
  async _ensureTarget(aChromeWindow, aTarget) {
    let shouldOpenAppMenu = false;
    let shouldOpenPageActionPanel = false;
    if (this.targetIsInAppMenu(aTarget)) {
      shouldOpenAppMenu = true;
    } else if (this.targetIsInPageActionPanel(aTarget)) {
      shouldOpenPageActionPanel = true;
      // Ensure the panel visibility so as to ensure the visibility of the target
      // element inside the panel otherwise we would be rejected in the below
      // `isElementVisible` checking.
      aChromeWindow.BrowserPageActions.panelNode.hidden = false;
    }

    // Prevent showing a panel at an undefined position, but when it's tucked
    // away inside a panel, we skip this check.
    if (!aTarget.node.closest("panelview") && !this.isElementVisible(aTarget.node)) {
      return Promise.reject(`_ensureTarget: Reject the ${aTarget.name || aTarget.targetName} target since it isn't visible.`);
    }

    let menuToOpen = null;
    let menuClosePromises = [];
    if (shouldOpenAppMenu) {
      menuToOpen = "appMenu";
      menuClosePromises.push(this._setMenuStateForAnnotation(aChromeWindow, false, "pageActionPanel"));
    } else if (shouldOpenPageActionPanel) {
      menuToOpen = "pageActionPanel";
      menuClosePromises.push(this._setMenuStateForAnnotation(aChromeWindow, false, "appMenu"));
    } else {
      menuClosePromises.push(this._setMenuStateForAnnotation(aChromeWindow, false, "appMenu"));
      menuClosePromises.push(this._setMenuStateForAnnotation(aChromeWindow, false, "pageActionPanel"));
    }

    let promise = Promise.all(menuClosePromises);
    await promise;
    if (menuToOpen) {
      promise = this._setMenuStateForAnnotation(aChromeWindow, true, menuToOpen);
    }
    return promise;
  },

  previewTheme(aTheme) {
    let origin = Services.prefs.getCharPref("browser.uitour.themeOrigin");
    let data = LightweightThemeManager.parseTheme(aTheme, origin);
    if (data)
      LightweightThemeManager.previewTheme(data);
  },

  resetTheme() {
    LightweightThemeManager.resetPreview();
  },

  /**
   * The node to which a highlight or notification(-popup) is anchored is sometimes
   * obscured because it may be inside an overflow menu. This function should figure
   * that out and offer the overflow chevron as an alternative.
   *
   * @param {ChromeWindow} aChromeWindow The chrome window
   * @param {Object} aTarget The target object whose node is supposed to be the anchor
   * @type {Node}
   */
  async _correctAnchor(aChromeWindow, aTarget) {
    // PanelMultiView's like the AppMenu might shuffle the DOM, which might result
    // in our anchor being invalidated if it was anonymous content (since the XBL
    // binding it belonged to got destroyed). We work around this by re-querying for
    // the node and stuffing it into the old anchor structure.
    let refreshedTarget = await this.getTarget(aChromeWindow, aTarget.targetName);
    let node = aTarget.node = refreshedTarget.node;
    // If the target is in the overflow panel, just return the overflow button.
    if (node.closest("#widget-overflow-mainView")) {
      return CustomizableUI.getWidget(node.id).forWindow(aChromeWindow).anchor;
    }
    return node;
  },

  /**
   * @param aChromeWindow The chrome window that the highlight is in. Necessary since some targets
   *                      are in a sub-frame so the defaultView is not the same as the chrome
   *                      window.
   * @param aTarget    The element to highlight.
   * @param aEffect    (optional) The effect to use from UITour.highlightEffects or "none".
   * @see UITour.highlightEffects
   */
  async showHighlight(aChromeWindow, aTarget, aEffect = "none") {
    let showHighlightElement = (aAnchorEl) => {
      let highlighter = aChromeWindow.document.getElementById("UITourHighlight");

      let effect = aEffect;
      if (effect == "random") {
        // Exclude "random" from the randomly selected effects.
        let randomEffect = 1 + Math.floor(Math.random() * (this.highlightEffects.length - 1));
        if (randomEffect == this.highlightEffects.length)
          randomEffect--; // On the order of 1 in 2^62 chance of this happening.
        effect = this.highlightEffects[randomEffect];
      }
      // Toggle the effect attribute to "none" and flush layout before setting it so the effect plays.
      highlighter.setAttribute("active", "none");
      aChromeWindow.getComputedStyle(highlighter).animationName;
      highlighter.setAttribute("active", effect);
      highlighter.parentElement.setAttribute("targetName", aTarget.targetName);
      highlighter.parentElement.hidden = false;

      let highlightAnchor = aAnchorEl;
      let targetRect = highlightAnchor.getBoundingClientRect();
      let highlightHeight = targetRect.height;
      let highlightWidth = targetRect.width;

      if (this.targetIsInAppMenu(aTarget) || this.targetIsInPageActionPanel(aTarget)) {
        highlighter.classList.remove("rounded-highlight");
      } else {
        highlighter.classList.add("rounded-highlight");
      }
      if (highlightAnchor.classList.contains("toolbarbutton-1") &&
          highlightAnchor.getAttribute("cui-areatype") === "toolbar" &&
          highlightAnchor.getAttribute("overflowedItem") !== "true") {
        // A toolbar button in navbar has its clickable area an
        // inner-contained square while the button component itself is a tall
        // rectangle. We adjust the highlight area to a square as well.
        highlightHeight = highlightWidth;
      }

      highlighter.style.height = highlightHeight + "px";
      highlighter.style.width = highlightWidth + "px";

      // Close a previous highlight so we can relocate the panel.
      if (highlighter.parentElement.state == "showing" || highlighter.parentElement.state == "open") {
        log.debug("showHighlight: Closing previous highlight first");
        highlighter.parentElement.hidePopup();
      }
      /* The "overlap" position anchors from the top-left but we want to centre highlights at their
         minimum size. */
      let highlightWindow = aChromeWindow;
      let highlightStyle = highlightWindow.getComputedStyle(highlighter);
      let highlightHeightWithMin = Math.max(highlightHeight, parseFloat(highlightStyle.minHeight));
      let highlightWidthWithMin = Math.max(highlightWidth, parseFloat(highlightStyle.minWidth));
      let offsetX = (targetRect.width - highlightWidthWithMin) / 2;
      let offsetY = (targetRect.height - highlightHeightWithMin) / 2;
      this._addAnnotationPanelMutationObserver(highlighter.parentElement);
      highlighter.parentElement.openPopup(highlightAnchor, "overlap", offsetX, offsetY);
    };

    try {
      await this._ensureTarget(aChromeWindow, aTarget);
      let anchorEl = await this._correctAnchor(aChromeWindow, aTarget);
      showHighlightElement(anchorEl);
    } catch (e) {
      log.warn(e);
    }
  },

  _hideHighlightElement(aWindow) {
    let highlighter = aWindow.document.getElementById("UITourHighlight");
    this._removeAnnotationPanelMutationObserver(highlighter.parentElement);
    highlighter.parentElement.hidePopup();
    highlighter.removeAttribute("active");
  },

  hideHighlight(aWindow) {
    this._hideHighlightElement(aWindow);
    this._setMenuStateForAnnotation(aWindow, false, "appMenu");
    this._setMenuStateForAnnotation(aWindow, false, "pageActionPanel");
  },

  /**
   * Show an info panel.
   *
   * @param {ChromeWindow} aChromeWindow
   * @param {Node}     aAnchor
   * @param {String}   [aTitle=""]
   * @param {String}   [aDescription=""]
   * @param {String}   [aIconURL=""]
   * @param {Object[]} [aButtons=[]]
   * @param {Object}   [aOptions={}]
   * @param {String}   [aOptions.closeButtonCallback]
   * @param {String}   [aOptions.targetCallback]
   */
  async showInfo(aChromeWindow, aAnchor, aTitle = "", aDescription = "",
           aIconURL = "", aButtons = [], aOptions = {}) {
    let showInfoElement = (aAnchorEl) => {
      aAnchorEl.focus();

      let document = aChromeWindow.document;
      let tooltip = document.getElementById("UITourTooltip");
      let tooltipTitle = document.getElementById("UITourTooltipTitle");
      let tooltipDesc = document.getElementById("UITourTooltipDescription");
      let tooltipIcon = document.getElementById("UITourTooltipIcon");
      let tooltipButtons = document.getElementById("UITourTooltipButtons");

      if (tooltip.state == "showing" || tooltip.state == "open") {
        tooltip.hidePopup();
      }

      tooltipTitle.textContent = aTitle || "";
      tooltipDesc.textContent = aDescription || "";
      tooltipIcon.src = aIconURL || "";
      tooltipIcon.hidden = !aIconURL;

      while (tooltipButtons.firstChild)
        tooltipButtons.firstChild.remove();

      for (let button of aButtons) {
        let isButton = button.style != "text";
        let el = document.createElement(isButton ? "button" : "label");
        el.setAttribute(isButton ? "label" : "value", button.label);

        if (isButton) {
          if (button.iconURL)
            el.setAttribute("image", button.iconURL);

          if (button.style == "link")
            el.setAttribute("class", "button-link");

          if (button.style == "primary")
            el.setAttribute("class", "button-primary");

          // Don't close the popup or call the callback for style=text as they
          // aren't links/buttons.
          let callback = button.callback;
          el.addEventListener("command", event => {
            tooltip.hidePopup();
            callback(event);
          });
        }

        tooltipButtons.appendChild(el);
      }

      tooltipButtons.hidden = !aButtons.length;

      let tooltipClose = document.getElementById("UITourTooltipClose");
      let closeButtonCallback = (event) => {
        this.hideInfo(document.defaultView);
        if (aOptions && aOptions.closeButtonCallback) {
          aOptions.closeButtonCallback();
        }
      };
      tooltipClose.addEventListener("command", closeButtonCallback);

      let targetCallback = (event) => {
        let details = {
          target: aAnchor.targetName,
          type: event.type,
        };
        aOptions.targetCallback(details);
      };
      if (aOptions.targetCallback && aAnchor.addTargetListener) {
        aAnchor.addTargetListener(document, targetCallback);
      }

      tooltip.addEventListener("popuphiding", function(event) {
        tooltipClose.removeEventListener("command", closeButtonCallback);
        if (aOptions.targetCallback && aAnchor.removeTargetListener) {
          aAnchor.removeTargetListener(document, targetCallback);
        }
      }, {once: true});

      tooltip.setAttribute("targetName", aAnchor.targetName);
      tooltip.hidden = false;
      let alignment = "bottomcenter topright";
      if (aAnchor.infoPanelPosition) {
        alignment = aAnchor.infoPanelPosition;
      }

      let { infoPanelOffsetX: xOffset, infoPanelOffsetY: yOffset } = aAnchor;

      this._addAnnotationPanelMutationObserver(tooltip);
      tooltip.openPopup(aAnchorEl, alignment, xOffset || 0, yOffset || 0);
      if (tooltip.state == "closed") {
        document.defaultView.addEventListener("endmodalstate", function() {
          tooltip.openPopup(aAnchorEl, alignment);
        }, {once: true});
      }
    };

    try {
      await this._ensureTarget(aChromeWindow, aAnchor);
      let anchorEl = await this._correctAnchor(aChromeWindow, aAnchor);
      showInfoElement(anchorEl);
    } catch (e) {
      log.warn(e);
    }
  },

  isInfoOnTarget(aChromeWindow, aTargetName) {
    let document = aChromeWindow.document;
    let tooltip = document.getElementById("UITourTooltip");
    return tooltip.getAttribute("targetName") == aTargetName && tooltip.state != "closed";
  },

  _hideInfoElement(aWindow) {
    let document = aWindow.document;
    let tooltip = document.getElementById("UITourTooltip");
    this._removeAnnotationPanelMutationObserver(tooltip);
    tooltip.hidePopup();
    let tooltipButtons = document.getElementById("UITourTooltipButtons");
    while (tooltipButtons.firstChild)
      tooltipButtons.firstChild.remove();
  },

  hideInfo(aWindow) {
    this._hideInfoElement(aWindow);
    this._setMenuStateForAnnotation(aWindow, false, "appMenu");
    this._setMenuStateForAnnotation(aWindow, false, "pageActionPanel");
  },

  showMenu(aWindow, aMenuName, aOpenCallback = null) {
    log.debug("showMenu:", aMenuName);
    function openMenuButton(aMenuBtn) {
      if (!aMenuBtn || !aMenuBtn.boxObject || aMenuBtn.open) {
        if (aOpenCallback)
          aOpenCallback();
        return;
      }
      if (aOpenCallback)
        aMenuBtn.addEventListener("popupshown", aOpenCallback, { once: true });
      aMenuBtn.boxObject.openMenu(true);
    }

    if (aMenuName == "appMenu" || aMenuName == "pageActionPanel") {
      let menu = {
        onPanelHidden: this.onPanelHidden
      };
      if (aMenuName == "appMenu") {
        menu.node = aWindow.PanelUI.panel;
        menu.onPopupHiding = this.onAppMenuHiding;
        menu.onViewShowing = this.onAppMenuSubviewShowing;
        menu.show = () => aWindow.PanelUI.show();
      } else {
        menu.node = aWindow.BrowserPageActions.panelNode;
        menu.onPopupHiding = this.onPageActionPanelHiding;
        menu.onViewShowing = this.onPageActionPanelSubviewShowing;
        menu.show = () => aWindow.BrowserPageActions.showPanel();
      }

      menu.node.setAttribute("noautohide", "true");
      // If the popup is already opened, don't recreate the widget as it may cause a flicker.
      if (menu.node.state != "open") {
        this.recreatePopup(menu.node);
      }
      if (aOpenCallback) {
        menu.node.addEventListener("popupshown", aOpenCallback, { once: true });
      }
      menu.node.addEventListener("popuphidden", menu.onPanelHidden);
      menu.node.addEventListener("popuphiding", menu.onPopupHiding);
      menu.node.addEventListener("ViewShowing", menu.onViewShowing);
      menu.show();
    } else if (aMenuName == "bookmarks") {
      let menuBtn = aWindow.document.getElementById("bookmarks-menu-button");
      openMenuButton(menuBtn);
    } else if (aMenuName == "controlCenter") {
      let popup = aWindow.gIdentityHandler._identityPopup;

      // Add the listener even if the panel is already open since it will still
      // only get registered once even if it was UITour that opened it.
      popup.addEventListener("popuphiding", this.onControlCenterHiding);
      popup.addEventListener("popuphidden", this.onPanelHidden);

      popup.setAttribute("noautohide", "true");
      this.clearAvailableTargetsCache();

      if (popup.state == "open") {
        if (aOpenCallback) {
          aOpenCallback();
        }
        return;
      }

      this.recreatePopup(popup);

      // Open the control center
      if (aOpenCallback) {
        popup.addEventListener("popupshown", aOpenCallback, { once: true });
      }
      aWindow.document.getElementById("identity-box").click();
    } else if (aMenuName == "pocket") {
      let pageAction = PageActions.actionForID("pocket");
      if (!pageAction) {
        log.error("Can't open the pocket menu without a page action");
        return;
      }
      pageAction.doCommand(aWindow);
    } else if (aMenuName == "urlbar") {
      this.getTarget(aWindow, "urlbar").then(target => {
        let urlbar = target.node;
        if (aOpenCallback) {
          urlbar.popup.addEventListener("popupshown", aOpenCallback, { once: true });
        }
        urlbar.focus();
        // To demonstrate the ability of searching, we type "Firefox" in advance
        // for URLBar's dropdown. To limit the search results on browser-related
        // items, we use "Firefox" hard-coded rather than l10n brandShortName
        // entity to avoid unrelated or unpredicted results for, like, Nightly
        // or translated entites.
        const SEARCH_STRING = "Firefox";
        urlbar.value = SEARCH_STRING;
        urlbar.select();
        urlbar.controller.startSearch(SEARCH_STRING);
      }).catch(Cu.reportError);
    }
  },

  hideMenu(aWindow, aMenuName) {
    log.debug("hideMenu:", aMenuName);
    function closeMenuButton(aMenuBtn) {
      if (aMenuBtn && aMenuBtn.boxObject)
        aMenuBtn.boxObject.openMenu(false);
    }

    if (aMenuName == "appMenu") {
      aWindow.PanelUI.hide();
    } else if (aMenuName == "bookmarks") {
      let menuBtn = aWindow.document.getElementById("bookmarks-menu-button");
      closeMenuButton(menuBtn);
    } else if (aMenuName == "controlCenter") {
      let panel = aWindow.gIdentityHandler._identityPopup;
      panel.hidePopup();
    } else if (aMenuName == "urlbar") {
      aWindow.gURLBar.closePopup();
    } else if (aMenuName == "pageActionPanel") {
      aWindow.BrowserPageActions.panelNode.hidePopup();
    }
  },

  showNewTab(aWindow, aBrowser) {
    aWindow.openLinkIn("about:newtab", "current", {targetBrowser: aBrowser});
    aWindow.gURLBar.focus();
  },

  _hideAnnotationsForPanel(aEvent, aShouldClosePanel, aTargetPositionCallback) {
    let win = aEvent.target.ownerGlobal;
    let hideHighlightMethod = null;
    let hideInfoMethod = null;
    if (aShouldClosePanel) {
      hideHighlightMethod = aWin => this.hideHighlight(aWin);
      hideInfoMethod = aWin => this.hideInfo(aWin);
    } else {
      // Don't have to close panel, let's only hide annotation elements
      hideHighlightMethod = aWin => this._hideHighlightElement(aWin);
      hideInfoMethod = aWin => this._hideInfoElement(aWin);
    }
    let annotationElements = new Map([
      // [annotationElement (panel), method to hide the annotation]
      [win.document.getElementById("UITourHighlightContainer"), hideHighlightMethod],
      [win.document.getElementById("UITourTooltip"), hideInfoMethod],
    ]);
    annotationElements.forEach((hideMethod, annotationElement) => {
      if (annotationElement.state != "closed") {
        let targetName = annotationElement.getAttribute("targetName");
        UITour.getTarget(win, targetName).then((aTarget) => {
          // Since getTarget is async, we need to make sure that the target hasn't
          // changed since it may have just moved to somewhere outside of the app menu.
          if (annotationElement.getAttribute("targetName") != aTarget.targetName ||
              annotationElement.state == "closed" ||
              !aTargetPositionCallback(aTarget)) {
            return;
          }
          hideMethod(win);
        }).catch(log.error);
      }
    });
  },

  onAppMenuHiding(aEvent) {
    UITour._hideAnnotationsForPanel(aEvent, true, UITour.targetIsInAppMenu);
  },

  onAppMenuSubviewShowing(aEvent) {
    UITour._hideAnnotationsForPanel(aEvent, false, UITour.targetIsInAppMenu);
  },

  onPageActionPanelHiding(aEvent) {
    UITour._hideAnnotationsForPanel(aEvent, true, UITour.targetIsInPageActionPanel);
  },

  onPageActionPanelSubviewShowing(aEvent) {
    UITour._hideAnnotationsForPanel(aEvent, false, UITour.targetIsInPageActionPanel);
  },

  onControlCenterHiding(aEvent) {
    UITour._hideAnnotationsForPanel(aEvent, true, (aTarget) => {
      return aTarget.targetName.startsWith("controlCenter-");
    });
  },

  onPanelHidden(aEvent) {
    aEvent.target.removeAttribute("noautohide");
    UITour.recreatePopup(aEvent.target);
    UITour.clearAvailableTargetsCache();
  },

  recreatePopup(aPanel) {
    // After changing popup attributes that relate to how the native widget is created
    // (e.g. @noautohide) we need to re-create the frame/widget for it to take effect.
    if (aPanel.hidden) {
      // If the panel is already hidden, we don't need to recreate it but flush
      // in case someone just hid it.
      aPanel.clientWidth; // flush
      return;
    }
    aPanel.hidden = true;
    aPanel.clientWidth; // flush
    aPanel.hidden = false;
  },

  getConfiguration(aMessageManager, aWindow, aConfiguration, aCallbackID) {
    switch (aConfiguration) {
      case "appinfo":
        this.getAppInfo(aMessageManager, aWindow, aCallbackID);
        break;
      case "availableTargets":
        this.getAvailableTargets(aMessageManager, aWindow, aCallbackID);
        break;
      case "search":
      case "selectedSearchEngine":
        Services.search.init(rv => {
          let data;
          if (Components.isSuccessCode(rv)) {
            let engines = Services.search.getVisibleEngines();
            data = {
              searchEngineIdentifier: Services.search.defaultEngine.identifier,
              engines: engines.filter((engine) => engine.identifier)
                              .map((engine) => TARGET_SEARCHENGINE_PREFIX + engine.identifier)
            };
          } else {
            data = {engines: [], searchEngineIdentifier: ""};
          }
          this.sendPageCallback(aMessageManager, aCallbackID, data);
        });
        break;
      case "sync":
        this.sendPageCallback(aMessageManager, aCallbackID, {
          setup: Services.prefs.prefHasUserValue("services.sync.username"),
          desktopDevices: Services.prefs.getIntPref("services.sync.clients.devices.desktop", 0),
          mobileDevices: Services.prefs.getIntPref("services.sync.clients.devices.mobile", 0),
          totalDevices: Services.prefs.getIntPref("services.sync.numClients", 0),
        });
        break;
      case "canReset":
        this.sendPageCallback(aMessageManager, aCallbackID, ResetProfile.resetSupported());
        break;
      default:
        log.error("getConfiguration: Unknown configuration requested: " + aConfiguration);
        break;
    }
  },

  setConfiguration(aWindow, aConfiguration, aValue) {
    switch (aConfiguration) {
      case "defaultBrowser":
        // Ignore aValue in this case because the default browser can only
        // be set, not unset.
        try {
          let shell = aWindow.getShellService();
          if (shell) {
            shell.setDefaultBrowser(true, false);
          }
        } catch (e) {}
        break;
      default:
        log.error("setConfiguration: Unknown configuration requested: " + aConfiguration);
        break;
    }
  },

  getAppInfo(aMessageManager, aWindow, aCallbackID) {
    (async () => {
      let appinfo = {version: Services.appinfo.version};

      // Identifier of the partner repack, as stored in preference "distribution.id"
      // and included in Firefox and other update pings. Note this is not the same as
      // Services.appinfo.distributionID (value of MOZ_DISTRIBUTION_ID is set at build time).
      let distribution =
          Services.prefs.getDefaultBranch("distribution.").getCharPref("id", "default");
      appinfo.distribution = distribution;

      // Update channel, in a way that preserves 'beta' for RC beta builds:
      appinfo.defaultUpdateChannel = UpdateUtils.getUpdateChannel(false /* no partner ID */);

      let isDefaultBrowser = null;
      try {
        let shell = aWindow.getShellService();
        if (shell) {
          isDefaultBrowser = shell.isDefaultBrowser(false);
        }
      } catch (e) {}
      appinfo.defaultBrowser = isDefaultBrowser;

      let canSetDefaultBrowserInBackground = true;
      if (AppConstants.isPlatformAndVersionAtLeast("win", "6.2") ||
          AppConstants.isPlatformAndVersionAtLeast("macosx", "10.10")) {
        canSetDefaultBrowserInBackground = false;
      } else if (AppConstants.platform == "linux") {
        // The ShellService may not exist on some versions of Linux.
        try {
          aWindow.getShellService();
        } catch (e) {
          canSetDefaultBrowserInBackground = null;
        }
      }

      appinfo.canSetDefaultBrowserInBackground =
        canSetDefaultBrowserInBackground;

      // Expose Profile creation and last reset dates in weeks.
      const ONE_WEEK = 7 * 24 * 60 * 60 * 1000;
      let profileAge = new ProfileAge(null, null);
      let createdDate = await profileAge.created;
      let resetDate = await profileAge.reset;
      let createdWeeksAgo = Math.floor((Date.now() - createdDate) / ONE_WEEK);
      let resetWeeksAgo = null;
      if (resetDate) {
        resetWeeksAgo = Math.floor((Date.now() - resetDate) / ONE_WEEK);
      }
      appinfo.profileCreatedWeeksAgo = createdWeeksAgo;
      appinfo.profileResetWeeksAgo = resetWeeksAgo;

      this.sendPageCallback(aMessageManager, aCallbackID, appinfo);
    })().catch(err => {
      log.error(err);
      this.sendPageCallback(aMessageManager, aCallbackID, {});
    });
  },

  getAvailableTargets(aMessageManager, aChromeWindow, aCallbackID) {
    (async () => {
      let window = aChromeWindow;
      let data = this.availableTargetsCache.get(window);
      if (data) {
        log.debug("getAvailableTargets: Using cached targets list", data.targets.join(","));
        this.sendPageCallback(aMessageManager, aCallbackID, data);
        return;
      }

      let promises = [];
      for (let targetName of this.targets.keys()) {
        promises.push(this.getTarget(window, targetName));
      }
      let targetObjects = await Promise.all(promises);

      let targetNames = [];
      for (let targetObject of targetObjects) {
        if (targetObject.node)
          targetNames.push(targetObject.targetName);
      }

      data = {
        targets: targetNames,
      };
      this.availableTargetsCache.set(window, data);
      this.sendPageCallback(aMessageManager, aCallbackID, data);
    })().catch(err => {
      log.error(err);
      this.sendPageCallback(aMessageManager, aCallbackID, {
        targets: [],
      });
    });
  },

  addNavBarWidget(aTarget, aMessageManager, aCallbackID) {
    if (aTarget.node) {
      log.error("addNavBarWidget: can't add a widget already present:", aTarget);
      return;
    }
    if (!aTarget.allowAdd) {
      log.error("addNavBarWidget: not allowed to add this widget:", aTarget);
      return;
    }
    if (!aTarget.widgetName) {
      log.error("addNavBarWidget: can't add a widget without a widgetName property:", aTarget);
      return;
    }

    CustomizableUI.addWidgetToArea(aTarget.widgetName, CustomizableUI.AREA_NAVBAR);
    this.sendPageCallback(aMessageManager, aCallbackID);
  },

  _addAnnotationPanelMutationObserver(aPanelEl) {
    if (AppConstants.platform == "linux") {
      let observer = this._annotationPanelMutationObservers.get(aPanelEl);
      if (observer) {
        return;
      }
      let win = aPanelEl.ownerGlobal;
      observer = new win.MutationObserver(this._annotationMutationCallback);
      this._annotationPanelMutationObservers.set(aPanelEl, observer);
      let observerOptions = {
        attributeFilter: ["height", "width"],
        attributes: true,
      };
      observer.observe(aPanelEl, observerOptions);
    }
  },

  _removeAnnotationPanelMutationObserver(aPanelEl) {
    if (AppConstants.platform == "linux") {
      let observer = this._annotationPanelMutationObservers.get(aPanelEl);
      if (observer) {
        observer.disconnect();
        this._annotationPanelMutationObservers.delete(aPanelEl);
      }
    }
  },

/**
 * Workaround for Ubuntu panel craziness in bug 970788 where incorrect sizes get passed to
 * nsXULPopupManager::PopupResized and lead to incorrect width and height attributes getting
 * set on the panel.
 */
  _annotationMutationCallback(aMutations) {
    for (let mutation of aMutations) {
      // Remove both attributes at once and ignore remaining mutations to be proccessed.
      mutation.target.removeAttribute("width");
      mutation.target.removeAttribute("height");
      return;
    }
  },

  selectSearchEngine(aID) {
    return new Promise((resolve, reject) => {
      Services.search.init((rv) => {
        if (!Components.isSuccessCode(rv)) {
          reject("selectSearchEngine: search service init failed: " + rv);
          return;
        }

        let engines = Services.search.getVisibleEngines();
        for (let engine of engines) {
          if (engine.identifier == aID) {
            Services.search.defaultEngine = engine;
            resolve();
            return;
          }
        }
        reject("selectSearchEngine could not find engine with given ID");
      });
    });
  },

  notify(eventName, params) {
    let winEnum = Services.wm.getEnumerator("navigator:browser");
    while (winEnum.hasMoreElements()) {
      let window = winEnum.getNext();
      if (window.closed)
        continue;

      let openTourBrowsers = this.tourBrowsersByWindow.get(window);
      if (!openTourBrowsers)
        continue;

      for (let browser of openTourBrowsers) {
        let messageManager = browser.messageManager;
        if (!messageManager) {
          log.error("notify: Trying to notify a browser without a messageManager", browser);
          continue;
        }
        let detail = {
          event: eventName,
          params,
        };
        messageManager.sendAsyncMessage("UITour:SendPageNotification", detail);
      }
    }
  },
};

function controlCenterTrackingToggleTarget(aUnblock) {
  return {
    infoPanelPosition: "rightcenter topleft",
    query(aDocument) {
      let popup = aDocument.defaultView.gIdentityHandler._identityPopup;
      if (popup.state != "open") {
        return null;
      }
      let buttonId = null;
      if (aUnblock) {
        if (PrivateBrowsingUtils.isWindowPrivate(aDocument.defaultView)) {
          buttonId = "tracking-action-unblock-private";
        } else {
          buttonId = "tracking-action-unblock";
        }
      } else {
        buttonId = "tracking-action-block";
      }
      let element = aDocument.getElementById(buttonId);
      return UITour.isElementVisible(element) ? element : null;
    },
  };
}

this.UITour.init();

/**
 * UITour Health Report
 */
/**
 * Public API to be called by the UITour code
 */
const UITourHealthReport = {
  recordTreatmentTag(tag, value) {
    return TelemetryController.submitExternalPing("uitour-tag",
      {
        version: 1,
        tagName: tag,
        tagValue: value,
      },
      {
        addClientId: true,
        addEnvironment: true,
      });
  }
};
PK
!<��UU/modules/UpdateTopLevelContentWindowIDHelper.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 module tracks each browser window and informs network module
 * the current selected tab's content outer window ID.
 */

var EXPORTED_SYMBOLS = ["trackBrowserWindow"];

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");

// Lazy getters
XPCOMUtils.defineLazyServiceGetter(this, "_focusManager",
                                   "@mozilla.org/focus-manager;1",
                                   "nsIFocusManager");

// Constants
const TAB_EVENTS = ["TabBrowserInserted", "TabSelect"];
const WINDOW_EVENTS = ["activate", "unload"];
const DEBUG = false;

// Variables
var _lastFocusedWindow = null;
var _lastTopLevelWindowID = 0;

// Exported symbol
function trackBrowserWindow(aWindow) {
  WindowHelper.addWindow(aWindow);
}

// Global methods
function debug(s) {
  if (DEBUG) {
    dump("-*- UpdateTopLevelContentWindowIDHelper: " + s + "\n");
  }
}

function _updateCurrentContentOuterWindowID(aBrowser) {
  if (!aBrowser.outerWindowID ||
      aBrowser.outerWindowID === _lastTopLevelWindowID) {
    return;
  }

  debug("Current window uri=" + aBrowser.currentURI.spec +
        " id=" + aBrowser.outerWindowID);

  _lastTopLevelWindowID = aBrowser.outerWindowID;
  let windowIDWrapper = Cc["@mozilla.org/supports-PRUint64;1"]
                          .createInstance(Ci.nsISupportsPRUint64);
  windowIDWrapper.data = _lastTopLevelWindowID;
  Services.obs.notifyObservers(windowIDWrapper,
                               "net:current-toplevel-outer-content-windowid");
}

function _handleEvent(aEvent) {
  switch (aEvent.type) {
    case "TabBrowserInserted":
      if (aEvent.target.ownerGlobal.gBrowser.selectedBrowser === aEvent.target.linkedBrowser) {
        _updateCurrentContentOuterWindowID(aEvent.target.linkedBrowser);
      }
      break;
    case "TabSelect":
      _updateCurrentContentOuterWindowID(aEvent.target.linkedBrowser);
      break;
    case "activate":
      WindowHelper.onActivate(aEvent.target);
      break;
    case "unload":
      WindowHelper.removeWindow(aEvent.currentTarget);
      break;
  }
}

function _handleMessage(aMessage) {
  let browser = aMessage.target;
  if (aMessage.name === "Browser:Init" &&
      browser === browser.ownerGlobal.gBrowser.selectedBrowser) {
    _updateCurrentContentOuterWindowID(browser);
  }
}

// Methods that impact a window. Put into single object for organization.
var WindowHelper = {
  addWindow: function NP_WH_addWindow(aWindow) {
    // Add event listeners
    TAB_EVENTS.forEach(function(event) {
      aWindow.gBrowser.tabContainer.addEventListener(event, _handleEvent);
    });
    WINDOW_EVENTS.forEach(function(event) {
      aWindow.addEventListener(event, _handleEvent);
    });

    let messageManager = aWindow.getGroupMessageManager("browsers");
    messageManager.addMessageListener("Browser:Init", _handleMessage);

    // This gets called AFTER activate event, so if this is the focused window
    // we want to activate it.
    if (aWindow == _focusManager.activeWindow)
      this.handleFocusedWindow(aWindow);

    // Update the selected tab's content outer window ID.
    _updateCurrentContentOuterWindowID(aWindow.gBrowser.selectedBrowser);
  },

  removeWindow: function NP_WH_removeWindow(aWindow) {
    if (aWindow == _lastFocusedWindow)
      _lastFocusedWindow = null;

    // Remove the event listeners
    TAB_EVENTS.forEach(function(event) {
      aWindow.gBrowser.tabContainer.removeEventListener(event, _handleEvent);
    });
    WINDOW_EVENTS.forEach(function(event) {
      aWindow.removeEventListener(event, _handleEvent);
    });

    let messageManager = aWindow.getGroupMessageManager("browsers");
    messageManager.removeMessageListener("Browser:Init", _handleMessage);
  },

  onActivate: function NP_WH_onActivate(aWindow, aHasFocus) {
    // If this window was the last focused window, we don't need to do anything
    if (aWindow == _lastFocusedWindow)
      return;

    this.handleFocusedWindow(aWindow);

    _updateCurrentContentOuterWindowID(aWindow.gBrowser.selectedBrowser);
  },

  handleFocusedWindow: function NP_WH_handleFocusedWindow(aWindow) {
    // aWindow is now focused
    _lastFocusedWindow = aWindow;
  },
};
PK
!<� 7��modules/ZoomUI.jsm// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = [ "ZoomUI" ];

ChromeUtils.import("resource://gre/modules/Services.jsm");

var ZoomUI = {
  init(aWindow) {
    aWindow.addEventListener("EndSwapDocShells", onEndSwapDocShells, true);
    aWindow.addEventListener("FullZoomChange", onZoomChange);
    aWindow.addEventListener("TextZoomChange", onZoomChange);
    aWindow.addEventListener("unload", () => {
      aWindow.removeEventListener("EndSwapDocShells", onEndSwapDocShells, true);
      aWindow.removeEventListener("FullZoomChange", onZoomChange);
      aWindow.removeEventListener("TextZoomChange", onZoomChange);
    }, {once: true});
  },
};

function fullZoomLocationChangeObserver(aSubject, aTopic) {
  // If the tab was the last one in its window and has been dragged to another
  // window, the original browser's window will be unavailable here. Since that
  // window is closing, we can just ignore this notification.
  if (!aSubject.ownerGlobal) {
    return;
  }
  updateZoomUI(aSubject, false);
}
Services.obs.addObserver(fullZoomLocationChangeObserver, "browser-fullZoom:location-change");

function onEndSwapDocShells(event) {
  updateZoomUI(event.originalTarget);
}

function onZoomChange(event) {
  let browser;
  if (event.target.nodeType == event.target.DOCUMENT_NODE) {
    // In non-e10s, the event is dispatched on the contentDocument
    // so we need to jump through some hoops to get to the <xul:browser>.
    let gBrowser = event.currentTarget.gBrowser;
    let topDoc = event.target.defaultView.top.document;
    if (!topDoc.documentElement) {
      // In some events, such as loading synthetic documents, the
      // documentElement will be null and getBrowserForDocument will
      // return null.
      return;
    }
    browser = gBrowser.getBrowserForDocument(topDoc);
  } else {
    browser = event.originalTarget;
  }
  updateZoomUI(browser, true);
}

/**
 * Updates zoom controls.
 *
 * @param {object} aBrowser The browser that the zoomed content resides in.
 * @param {boolean} aAnimate Should be True for all cases unless the zoom
 *   change is related to tab switching. Optional
 */
function updateZoomUI(aBrowser, aAnimate = false) {
  let win = aBrowser.ownerGlobal;
  if (aBrowser != win.gBrowser.selectedBrowser) {
    return;
  }

  let appMenuZoomReset = win.document.getElementById("appMenu-zoomReset-button");
  let customizableZoomControls = win.document.getElementById("zoom-controls");
  let customizableZoomReset = win.document.getElementById("zoom-reset-button");
  let urlbarZoomButton = win.document.getElementById("urlbar-zoom-button");
  let zoomFactor = Math.round(win.ZoomManager.zoom * 100);

  // Hide urlbar zoom button if zoom is at 100% or the customizable control is
  // in the toolbar.
  urlbarZoomButton.hidden =
    (zoomFactor == 100 ||
     (customizableZoomControls &&
      customizableZoomControls.getAttribute("cui-areatype") == "toolbar"));

  let label = win.gNavigatorBundle.getFormattedString("zoom-button.label", [zoomFactor]);
  if (appMenuZoomReset) {
    appMenuZoomReset.setAttribute("label", label);
  }
  if (customizableZoomReset) {
    customizableZoomReset.setAttribute("label", label);
  }
  if (!urlbarZoomButton.hidden) {
    if (aAnimate) {
      urlbarZoomButton.setAttribute("animate", "true");
    } else {
      urlbarZoomButton.removeAttribute("animate");
    }
    urlbarZoomButton.setAttribute("label", label);
  }
}

ChromeUtils.import("resource:///modules/CustomizableUI.jsm");
let customizationListener = {};
customizationListener.onWidgetAdded =
customizationListener.onWidgetRemoved =
customizationListener.onWidgetMoved = function(aWidgetId) {
  if (aWidgetId == "zoom-controls") {
    for (let window of CustomizableUI.windows) {
      updateZoomUI(window.gBrowser.selectedBrowser);
    }
  }
};
customizationListener.onWidgetReset =
customizationListener.onWidgetUndoMove = function(aWidgetNode) {
  if (aWidgetNode.id == "zoom-controls") {
    updateZoomUI(aWidgetNode.ownerGlobal.gBrowser.selectedBrowser);
  }
};
CustomizableUI.addListener(customizationListener);

PK
!<˧�αB�Bmodules/distribution.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

var EXPORTED_SYMBOLS = [ "DistributionCustomizer" ];

const DISTRIBUTION_CUSTOMIZATION_COMPLETE_TOPIC =
  "distribution-customization-complete";

const PREF_CACHED_FILE_EXISTENCE  = "distribution.iniFile.exists.value";
const PREF_CACHED_FILE_APPVERSION = "distribution.iniFile.exists.appversion";

ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.defineModuleGetter(this, "Preferences",
                               "resource://gre/modules/Preferences.jsm");
ChromeUtils.defineModuleGetter(this, "PlacesUtils",
                               "resource://gre/modules/PlacesUtils.jsm");

function DistributionCustomizer() {
}

DistributionCustomizer.prototype = {
  get _iniFile() {
    // For parallel xpcshell testing purposes allow loading the distribution.ini
    // file from the profile folder through an hidden pref.
    let loadFromProfile = Services.prefs.getBoolPref("distribution.testing.loadFromProfile", false);

    let iniFile;
    try {
      iniFile = loadFromProfile ? Services.dirsvc.get("ProfD", Ci.nsIFile)
                                : Services.dirsvc.get("XREAppDist", Ci.nsIFile);
      if (loadFromProfile) {
        iniFile.leafName = "distribution";
      }
      iniFile.append("distribution.ini");
    } catch (ex) {}

    this.__defineGetter__("_iniFile", () => iniFile);
    return iniFile;
  },

  get _hasDistributionIni() {
    if (Services.prefs.prefHasUserValue(PREF_CACHED_FILE_EXISTENCE)) {
      let knownForVersion = Services.prefs.getStringPref(PREF_CACHED_FILE_APPVERSION, "unknown");
      if (knownForVersion == AppConstants.MOZ_APP_VERSION) {
        return Services.prefs.getBoolPref(PREF_CACHED_FILE_EXISTENCE);
      }
    }

    let fileExists = this._iniFile.exists();
    Services.prefs.setBoolPref(PREF_CACHED_FILE_EXISTENCE, fileExists);
    Services.prefs.setStringPref(PREF_CACHED_FILE_APPVERSION, AppConstants.MOZ_APP_VERSION);

    this.__defineGetter__("_hasDistributionIni", () => fileExists);
    return fileExists;
  },

  get _ini() {
    let ini = null;
    try {
      if (this._hasDistributionIni) {
        ini = Cc["@mozilla.org/xpcom/ini-parser-factory;1"].
              getService(Ci.nsIINIParserFactory).
              createINIParser(this._iniFile);
      }
    } catch (e) {
      if (e.result == Cr.NS_ERROR_FILE_NOT_FOUND) {
        // We probably had cached the file existence as true,
        // but it no longer exists. We could set the new cache
        // value here, but let's just invalidate the cache and
        // let it be cached by a single code path on the next check.
        Services.prefs.clearUserPref(PREF_CACHED_FILE_EXISTENCE);
      } else {
        // Unable to parse INI.
        Cu.reportError("Unable to parse distribution.ini");
      }
    }
    this.__defineGetter__("_ini", () => ini);
    return this._ini;
  },

  get _locale() {
    const locale = Services.locale.getRequestedLocale() || "en-US";
    this.__defineGetter__("_locale", () => locale);
    return this._locale;
  },

  get _language() {
    let language = this._locale.split("-")[0];
    this.__defineGetter__("_language", () => language);
    return this._language;
  },

  async _parseBookmarksSection(parentGuid, section) {
    let keys = Array.from(enumerate(this._ini.getKeys(section))).sort();
    let re = /^item\.(\d+)\.(\w+)\.?(\w*)/;
    let items = {};
    let defaultIndex = -1;
    let maxIndex = -1;

    for (let key of keys) {
      let m = re.exec(key);
      if (m) {
        let [, itemIndex, iprop, ilocale] = m;
        itemIndex = parseInt(itemIndex);

        if (ilocale)
          continue;

        if (keys.includes(key + "." + this._locale)) {
          key += "." + this._locale;
        } else if (keys.includes(key + "." + this._language)) {
          key += "." + this._language;
        }

        if (!items[itemIndex])
          items[itemIndex] = {};
        items[itemIndex][iprop] = this._ini.getString(section, key);

        if (iprop == "type" && items[itemIndex].type == "default")
          defaultIndex = itemIndex;

        if (maxIndex < itemIndex)
          maxIndex = itemIndex;
      } else {
        dump(`Key did not match: ${key}\n`);
      }
    }

    let prependIndex = 0;
    for (let itemIndex = 0; itemIndex <= maxIndex; itemIndex++) {
      if (!items[itemIndex])
        continue;

      let index = PlacesUtils.bookmarks.DEFAULT_INDEX;
      let item = items[itemIndex];

      switch (item.type) {
      case "default":
        break;

      case "folder":
        if (itemIndex < defaultIndex)
          index = prependIndex++;

        let folder = await PlacesUtils.bookmarks.insert({
          type: PlacesUtils.bookmarks.TYPE_FOLDER,
          parentGuid, index, title: item.title
        });

        await this._parseBookmarksSection(folder.guid,
                                          "BookmarksFolder-" + item.folderId);

        if (item.description) {
          let folderId = await PlacesUtils.promiseItemId(folder.guid);
          PlacesUtils.annotations.setItemAnnotation(folderId,
                                                    "bookmarkProperties/description",
                                                    item.description, 0,
                                                    PlacesUtils.annotations.EXPIRE_NEVER);
        }

        break;

      case "separator":
        if (itemIndex < defaultIndex)
          index = prependIndex++;

        await PlacesUtils.bookmarks.insert({
          type: PlacesUtils.bookmarks.TYPE_SEPARATOR,
          parentGuid, index
        });
        break;

      case "livemark":
        if (itemIndex < defaultIndex)
          index = prependIndex++;

        // Don't bother updating the livemark contents on creation.
        let parentId = await PlacesUtils.promiseItemId(parentGuid);
        await PlacesUtils.livemarks.addLivemark({
          feedURI: Services.io.newURI(item.feedLink),
          siteURI: Services.io.newURI(item.siteLink),
          parentId, index, title: item.title
        });
        break;

      case "bookmark":
      default:
        if (itemIndex < defaultIndex)
          index = prependIndex++;

        let bm = await PlacesUtils.bookmarks.insert({
          parentGuid, index, title: item.title, url: item.link
        });

        if (item.description) {
          let bmId = await PlacesUtils.promiseItemId(bm.guid);
          PlacesUtils.annotations.setItemAnnotation(bmId,
                                                    "bookmarkProperties/description",
                                                    item.description, 0,
                                                    PlacesUtils.annotations.EXPIRE_NEVER);
        }

        if (item.icon && item.iconData) {
          try {
            let faviconURI = Services.io.newURI(item.icon);
            PlacesUtils.favicons.replaceFaviconDataFromDataURL(
              faviconURI, item.iconData, 0,
              Services.scriptSecurityManager.getSystemPrincipal());

            PlacesUtils.favicons.setAndFetchFaviconForPage(
              Services.io.newURI(item.link), faviconURI, false,
              PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, null,
              Services.scriptSecurityManager.getSystemPrincipal());
          } catch (e) {
            Cu.reportError(e);
          }
        }

        if (item.keyword) {
          try {
            await PlacesUtils.keywords.insert({ keyword: item.keyword,
                                                url: item.link });
          } catch (e) {
            Cu.reportError(e);
          }
        }

        break;
      }
    }
  },

  _newProfile: false,
  _customizationsApplied: false,
  applyCustomizations: function DIST_applyCustomizations() {
    this._customizationsApplied = true;

    if (!Services.prefs.prefHasUserValue("browser.migration.version"))
      this._newProfile = true;

    if (!this._ini)
      return this._checkCustomizationComplete();

    // nsPrefService loads very early.  Reload prefs so we can set
    // distribution defaults during the prefservice:after-app-defaults
    // notification (see applyPrefDefaults below)
    Services.prefs.QueryInterface(Ci.nsIObserver)
      .observe(null, "reload-default-prefs", null);
  },

  _bookmarksApplied: false,
  async applyBookmarks() {
    await this._doApplyBookmarks();
    this._bookmarksApplied = true;
    this._checkCustomizationComplete();
  },

  async _doApplyBookmarks() {
    if (!this._ini)
      return;

    let sections = enumToObject(this._ini.getSections());

    // The global section, and several of its fields, is required
    // (we also check here to be consistent with applyPrefDefaults below)
    if (!sections.Global)
      return;

    let globalPrefs = enumToObject(this._ini.getKeys("Global"));
    if (!(globalPrefs.id && globalPrefs.version && globalPrefs.about))
      return;

    let bmProcessedPref;
    try {
      bmProcessedPref = this._ini.getString("Global",
                                            "bookmarks.initialized.pref");
    } catch (e) {
      bmProcessedPref = "distribution." +
        this._ini.getString("Global", "id") + ".bookmarksProcessed";
    }

    let bmProcessed = Services.prefs.getBoolPref(bmProcessedPref, false);

    if (!bmProcessed) {
      if (sections.BookmarksMenu)
        await this._parseBookmarksSection(PlacesUtils.bookmarks.menuGuid,
                                          "BookmarksMenu");
      if (sections.BookmarksToolbar)
        await this._parseBookmarksSection(PlacesUtils.bookmarks.toolbarGuid,
                                          "BookmarksToolbar");
      Services.prefs.setBoolPref(bmProcessedPref, true);
    }
  },

  _prefDefaultsApplied: false,
  applyPrefDefaults: function DIST_applyPrefDefaults() {
    this._prefDefaultsApplied = true;
    if (!this._ini)
      return this._checkCustomizationComplete();

    let sections = enumToObject(this._ini.getSections());

    // The global section, and several of its fields, is required
    if (!sections.Global)
      return this._checkCustomizationComplete();
    let globalPrefs = enumToObject(this._ini.getKeys("Global"));
    if (!(globalPrefs.id && globalPrefs.version && globalPrefs.about))
      return this._checkCustomizationComplete();

    let defaults = new Preferences({defaultBranch: true});

    // Global really contains info we set as prefs.  They're only
    // separate because they are "special" (read: required)

    defaults.set("distribution.id", this._ini.getString("Global", "id"));
    defaults.set("distribution.version", this._ini.getString("Global", "version"));

    let partnerAbout;
    try {
      if (globalPrefs["about." + this._locale]) {
        partnerAbout = this._ini.getString("Global", "about." + this._locale);
      } else if (globalPrefs["about." + this._language]) {
        partnerAbout = this._ini.getString("Global", "about." + this._language);
      } else {
        partnerAbout = this._ini.getString("Global", "about");
      }
      defaults.set("distribution.about", partnerAbout);
    } catch (e) {
      /* ignore bad prefs due to bug 895473 and move on */
      Cu.reportError(e);
    }

    var usedPreferences = [];

    if (sections["Preferences-" + this._locale]) {
      for (let key of enumerate(this._ini.getKeys("Preferences-" + this._locale))) {
        try {
          let value = this._ini.getString("Preferences-" + this._locale, key);
          if (value) {
            defaults.set(key, parseValue(value));
          }
          usedPreferences.push(key);
        } catch (e) { /* ignore bad prefs and move on */ }
      }
    }

    if (sections["Preferences-" + this._language]) {
      for (let key of enumerate(this._ini.getKeys("Preferences-" + this._language))) {
        if (usedPreferences.indexOf(key) > -1) {
          continue;
        }
        try {
          let value = this._ini.getString("Preferences-" + this._language, key);
          if (value) {
            defaults.set(key, parseValue(value));
          }
          usedPreferences.push(key);
        } catch (e) { /* ignore bad prefs and move on */ }
      }
    }

    if (sections.Preferences) {
      for (let key of enumerate(this._ini.getKeys("Preferences"))) {
        if (usedPreferences.indexOf(key) > -1) {
          continue;
        }
        try {
          let value = this._ini.getString("Preferences", key);
          if (value) {
            value = value.replace(/%LOCALE%/g, this._locale);
            value = value.replace(/%LANGUAGE%/g, this._language);
            if (key == "general.useragent.locale") {
              defaults.set("intl.locale.requested", parseValue(value));
            } else {
              defaults.set(key, parseValue(value));
            }
          }
        } catch (e) { /* ignore bad prefs and move on */ }
      }
    }

    let localizedStr = Cc["@mozilla.org/pref-localizedstring;1"].
      createInstance(Ci.nsIPrefLocalizedString);

    var usedLocalizablePreferences = [];

    if (sections["LocalizablePreferences-" + this._locale]) {
      for (let key of enumerate(this._ini.getKeys("LocalizablePreferences-" + this._locale))) {
        try {
          let value = this._ini.getString("LocalizablePreferences-" + this._locale, key);
          if (value) {
            value = parseValue(value);
            localizedStr.data = "data:text/plain," + key + "=" + value;
            defaults._prefBranch.setComplexValue(key, Ci.nsIPrefLocalizedString, localizedStr);
          }
          usedLocalizablePreferences.push(key);
        } catch (e) { /* ignore bad prefs and move on */ }
      }
    }

    if (sections["LocalizablePreferences-" + this._language]) {
      for (let key of enumerate(this._ini.getKeys("LocalizablePreferences-" + this._language))) {
        if (usedLocalizablePreferences.indexOf(key) > -1) {
          continue;
        }
        try {
          let value = this._ini.getString("LocalizablePreferences-" + this._language, key);
          if (value) {
            value = parseValue(value);
            localizedStr.data = "data:text/plain," + key + "=" + value;
            defaults._prefBranch.setComplexValue(key, Ci.nsIPrefLocalizedString, localizedStr);
          }
          usedLocalizablePreferences.push(key);
        } catch (e) { /* ignore bad prefs and move on */ }
      }
    }

    if (sections.LocalizablePreferences) {
      for (let key of enumerate(this._ini.getKeys("LocalizablePreferences"))) {
        if (usedLocalizablePreferences.indexOf(key) > -1) {
          continue;
        }
        try {
          let value = this._ini.getString("LocalizablePreferences", key);
          if (value) {
            value = parseValue(value);
            value = value.replace(/%LOCALE%/g, this._locale);
            value = value.replace(/%LANGUAGE%/g, this._language);
            localizedStr.data = "data:text/plain," + key + "=" + value;
            defaults._prefBranch.setComplexValue(key, Ci.nsIPrefLocalizedString, localizedStr);
          }
        } catch (e) { /* ignore bad prefs and move on */ }
      }
    }

    return this._checkCustomizationComplete();
  },

  _checkCustomizationComplete: function DIST__checkCustomizationComplete() {
    const BROWSER_DOCURL = "chrome://browser/content/browser.xul";

    if (this._newProfile) {
      let xulStore = Cc["@mozilla.org/xul/xulstore;1"].getService(Ci.nsIXULStore);

      try {
        var showPersonalToolbar = Services.prefs.getBoolPref("browser.showPersonalToolbar");
        if (showPersonalToolbar) {
          xulStore.setValue(BROWSER_DOCURL, "PersonalToolbar", "collapsed", "false");
        }
      } catch (e) {}
      try {
        var showMenubar = Services.prefs.getBoolPref("browser.showMenubar");
        if (showMenubar) {
          xulStore.setValue(BROWSER_DOCURL, "toolbar-menubar", "autohide", "false");
        }
      } catch (e) {}
    }

    let prefDefaultsApplied = this._prefDefaultsApplied || !this._ini;
    if (this._customizationsApplied && this._bookmarksApplied &&
        prefDefaultsApplied) {
      Services.obs.notifyObservers(null, DISTRIBUTION_CUSTOMIZATION_COMPLETE_TOPIC);
    }
  }
};

function parseValue(value) {
  try {
    value = JSON.parse(value);
  } catch (e) {
    // JSON.parse catches numbers and booleans.
    // Anything else, we assume is a string.
    // Remove the quotes that aren't needed anymore.
    value = value.replace(/^"/, "");
    value = value.replace(/"$/, "");
  }
  return value;
}

function* enumerate(UTF8Enumerator) {
  while (UTF8Enumerator.hasMore())
    yield UTF8Enumerator.getNext();
}

function enumToObject(UTF8Enumerator) {
  let ret = {};
  for (let i of enumerate(UTF8Enumerator))
    ret[i] = 1;
  return ret;
}
PK
!<��B
B
#modules/experiments/Experiments.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = [
  "Experiments",
];

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/osfile.jsm");
ChromeUtils.import("resource://gre/modules/Log.jsm");
ChromeUtils.import("resource://gre/modules/AsyncShutdown.jsm");

Cu.importGlobalProperties(["XMLHttpRequest"]);

ChromeUtils.defineModuleGetter(this, "UpdateUtils",
                               "resource://gre/modules/UpdateUtils.jsm");
ChromeUtils.defineModuleGetter(this, "AddonManager",
                               "resource://gre/modules/AddonManager.jsm");
ChromeUtils.defineModuleGetter(this, "AddonManagerPrivate",
                               "resource://gre/modules/AddonManager.jsm");
ChromeUtils.defineModuleGetter(this, "TelemetryEnvironment",
                               "resource://gre/modules/TelemetryEnvironment.jsm");
ChromeUtils.defineModuleGetter(this, "TelemetryLog",
                               "resource://gre/modules/TelemetryLog.jsm");
ChromeUtils.defineModuleGetter(this, "CommonUtils",
                               "resource://services-common/utils.js");

XPCOMUtils.defineLazyServiceGetter(this, "gCrashReporter",
                                   "@mozilla.org/xre/app-info;1",
                                   "nsICrashReporter");

const FILE_CACHE                = "experiments.json";
const EXPERIMENTS_CHANGED_TOPIC = "experiments-changed";
const PREF_CHANGED_TOPIC        = "nsPref:changed";
const MANIFEST_VERSION          = 1;
const CACHE_VERSION             = 1;

const KEEP_HISTORY_N_DAYS       = 180;

const PREF_BRANCH               = "experiments.";
const PREF_ENABLED              = "enabled"; // experiments.enabled
const PREF_ACTIVE_EXPERIMENT    = "activeExperiment"; // whether we have an active experiment
const PREF_LOGGING              = "logging";
const PREF_LOGGING_LEVEL        = PREF_LOGGING + ".level"; // experiments.logging.level
const PREF_LOGGING_DUMP         = PREF_LOGGING + ".dump"; // experiments.logging.dump
const PREF_MANIFEST_URI         = "manifest.uri"; // experiments.logging.manifest.uri
const PREF_FORCE_SAMPLE         = "force-sample-value"; // experiments.force-sample-value

const URI_EXTENSION_STRINGS     = "chrome://mozapps/locale/extensions/extensions.properties";

const CACHE_WRITE_RETRY_DELAY_SEC = 60 * 3;
const MANIFEST_FETCH_TIMEOUT_MSEC = 60 * 3 * 1000; // 3 minutes

const TELEMETRY_LOG = {
  // log(key, [kind, experimentId, details])
  ACTIVATION_KEY: "EXPERIMENT_ACTIVATION",
  ACTIVATION: {
    // Successfully activated.
    ACTIVATED: "ACTIVATED",
    // Failed to install the add-on.
    INSTALL_FAILURE: "INSTALL_FAILURE",
    // Experiment does not meet activation requirements. Details will
    // be provided.
    REJECTED: "REJECTED",
  },

  // log(key, [kind, experimentId, optionalDetails...])
  TERMINATION_KEY: "EXPERIMENT_TERMINATION",
  TERMINATION: {
    // The Experiments service was disabled.
    SERVICE_DISABLED: "SERVICE_DISABLED",
    // Add-on uninstalled.
    ADDON_UNINSTALLED: "ADDON_UNINSTALLED",
    // The experiment disabled itself.
    FROM_API: "FROM_API",
    // The experiment expired (e.g. by exceeding the end date).
    EXPIRED: "EXPIRED",
    // Disabled after re-evaluating conditions. If this is specified,
    // details will be provided.
    RECHECK: "RECHECK",
  },
};
XPCOMUtils.defineConstant(this, "TELEMETRY_LOG", TELEMETRY_LOG);

const gPrefs = Services.prefs.getBranch(PREF_BRANCH);
var gExperimentsEnabled = false;
var gAddonProvider = null;
var gExperiments = null;
var gLogAppenderDump = null;
var gPolicyCounter = 0;
var gExperimentsCounter = 0;
var gExperimentEntryCounter = 0;
var gPreviousProviderCounter = 0;

// Tracks active AddonInstall we know about so we can deny external
// installs.
var gActiveInstallURLs = new Set();

// Tracks add-on IDs that are being uninstalled by us. This allows us
// to differentiate between expected uninstalled and user-driven uninstalls.
var gActiveUninstallAddonIDs = new Set();

var gLogger;
var gLogDumping = false;

function configureLogging() {
  if (!gLogger) {
    gLogger = Log.repository.getLogger("Browser.Experiments");
    gLogger.addAppender(new Log.ConsoleAppender(new Log.BasicFormatter()));
  }
  gLogger.level = gPrefs.getIntPref(PREF_LOGGING_LEVEL, Log.Level.Warn);

  let logDumping = gPrefs.getBoolPref(PREF_LOGGING_DUMP, false);
  if (logDumping != gLogDumping) {
    if (logDumping) {
      gLogAppenderDump = new Log.DumpAppender(new Log.BasicFormatter());
      gLogger.addAppender(gLogAppenderDump);
    } else {
      gLogger.removeAppender(gLogAppenderDump);
      gLogAppenderDump = null;
    }
    gLogDumping = logDumping;
  }
}

// Loads a JSON file using OS.file. file is a string representing the path
// of the file to be read, options contains additional options to pass to
// OS.File.read.
// Returns a Promise resolved with the json payload or rejected with
// OS.File.Error or JSON.parse() errors.
function loadJSONAsync(file, options) {
  return (async function() {
    let rawData = await OS.File.read(file, options);
    // Read json file into a string
    let data;
    try {
      // Obtain a converter to read from a UTF-8 encoded input stream.
      let converter = new TextDecoder();
      data = JSON.parse(converter.decode(rawData));
    } catch (ex) {
      gLogger.error("Experiments: Could not parse JSON: " + file + " " + ex);
      throw ex;
    }
    return data;
  })();
}

// Returns a promise that is resolved with the AddonInstall for that URL.
function addonInstallForURL(url, hash) {
  return AddonManager.getInstallForURL(url, null, "application/x-xpinstall", hash);
}

// Returns a promise that is resolved with an Array<Addon> of the installed
// experiment addons.
function installedExperimentAddons() {
  return AddonManager.getActiveAddons(["experiment"]).then(({addons}) => {
    return addons.filter(a => !a.appDisabled);
  });
}

// Takes an Array<Addon> and returns a promise that is resolved when the
// addons are uninstalled.
async function uninstallAddons(addons) {
  if (!AddonManagerPrivate.isDBLoaded()) {
    await new Promise(resolve => {
      Services.obs.addObserver({
        observe(subject, topic, data) {
          Services.obs.removeObserver(this, "xpi-database-loaded");
          resolve();
        },
      }, "xpi-database-loaded");
    });

    // This function was called during startup so the addons that were
    // passed in were partial addon objects.  Now that the full addons
    // database is loaded, get proper Addon objects.
    addons = await AddonManager.getAddonsByIDs(addons.map(a => a.id));
  }

  let ids = new Set(addons.map(addon => addon.id));
  return new Promise(resolve => {

    let listener = {};
    listener.onUninstalled = addon => {
      if (!ids.has(addon.id)) {
        return;
      }

      ids.delete(addon.id);
      if (ids.size == 0) {
        AddonManager.removeAddonListener(listener);
        resolve();
      }
    };

    AddonManager.addAddonListener(listener);

    for (let addon of addons) {
      addon.uninstall();
    }

  });
}

/**
 * The experiments module.
 */

var Experiments = {
  /**
   * Provides access to the global `Experiments.Experiments` instance.
   */
  instance() {
    if (!gExperiments) {
      gExperiments = new Experiments.Experiments();
    }

    return gExperiments;
  },
};

/*
 * The policy object allows us to inject fake enviroment data from the
 * outside by monkey-patching.
 */

Experiments.Policy = function() {
  this._log = Log.repository.getLoggerWithMessagePrefix(
    "Browser.Experiments.Policy",
    "Policy #" + gPolicyCounter++ + "::");

  // Set to true to ignore hash verification on downloaded XPIs. This should
  // not be used outside of testing.
  this.ignoreHashes = false;
};

Experiments.Policy.prototype = {
  now() {
    return new Date();
  },

  random() {
    let pref = gPrefs.getStringPref(PREF_FORCE_SAMPLE, undefined);
    if (pref !== undefined) {
      let val = Number.parseFloat(pref);
      this._log.debug("random sample forced: " + val);
      if (isNaN(val) || val < 0) {
        return 0;
      }
      if (val > 1) {
        return 1;
      }
      return val;
    }
    return Math.random();
  },

  futureDate(offset) {
    return new Date(this.now().getTime() + offset);
  },

  oneshotTimer(callback, timeout, thisObj, name) {
    return CommonUtils.namedTimer(callback, timeout, thisObj, name);
  },

  updatechannel() {
    return UpdateUtils.UpdateChannel;
  },

  locale() {
    return Services.locale.getAppLocaleAsLangTag();
  },

  /**
   * For testing a race condition, one of the tests delays the callback of
   * writing the cache by replacing this policy function.
   */
  delayCacheWrite(promise) {
    return promise;
  },
};

function AlreadyShutdownError(message = "already shut down") {
  Error.call(this, message);
  let error = new Error();
  this.name = "AlreadyShutdownError";
  this.message = message;
  this.stack = error.stack;
}
AlreadyShutdownError.prototype = Object.create(Error.prototype);
AlreadyShutdownError.prototype.constructor = AlreadyShutdownError;

function CacheWriteError(message = "Error writing cache file") {
  Error.call(this, message);
  let error = new Error();
  this.name = "CacheWriteError";
  this.message = message;
  this.stack = error.stack;
}
CacheWriteError.prototype = Object.create(Error.prototype);
CacheWriteError.prototype.constructor = CacheWriteError;

/**
 * Manages the experiments and provides an interface to control them.
 */

Experiments.Experiments = function(policy = new Experiments.Policy()) {
  let log = Log.repository.getLoggerWithMessagePrefix(
      "Browser.Experiments.Experiments",
      "Experiments #" + gExperimentsCounter++ + "::");

  // At the time of this writing, Experiments.jsm has severe
  // crashes. For forensics purposes, keep the last few log
  // messages in memory and upload them in case of crash.
  this._forensicsLogs = [];
  this._forensicsLogs.length = 30;
  this._log = Object.create(log);
  this._log.log = (level, string, params) => {
    this._addToForensicsLog("Experiments", string);
    log.log(level, string, params);
  };

  this._log.trace("constructor");

  // Capture the latest error, for forensics purposes.
  this._latestError = null;


  this._policy = policy;

  // This is a Map of (string -> ExperimentEntry), keyed with the experiment id.
  // It holds both the current experiments and history.
  // Map() preserves insertion order, which means we preserve the manifest order.
  // This is null until we've successfully completed loading the cache from
  // disk the first time.
  this._experiments = null;
  this._refresh = false;
  this._terminateReason = null; // or TELEMETRY_LOG.TERMINATION....
  this._dirty = false;

  // Loading the cache happens once asynchronously on startup
  this._loadTask = null;

  // The _main task handles all other actions:
  // * refreshing the manifest off the network (if _refresh)
  // * disabling/enabling experiments
  // * saving the cache (if _dirty)
  this._mainTask = null;

  // Timer for re-evaluating experiment status.
  this._timer = null;

  this._shutdown = false;
  this._networkRequest = null;

  // We need to tell when we first evaluated the experiments to fire an
  // experiments-changed notification when we only loaded completed experiments.
  this._firstEvaluate = true;

  this.init();
};

Experiments.Experiments.prototype = {
  QueryInterface: XPCOMUtils.generateQI([Ci.nsITimerCallback, Ci.nsIObserver, Ci.nsISupportsWeakReference]),

  /**
   * `true` if the experiments manager is currently setup (has been fully initialized
   * and not uninitialized yet).
   */
  get isReady() {
    return !this._shutdown;
  },

  observe(subject, topic, data) {
    switch (topic) {
      case PREF_CHANGED_TOPIC:
        if (data == PREF_BRANCH + PREF_MANIFEST_URI) {
          this.updateManifest();
        } else if (data == PREF_BRANCH + PREF_ENABLED) {
          this._toggleExperimentsEnabled(gPrefs.getBoolPref(PREF_ENABLED, false));
        }
        break;
    }
  },

  init() {
    this._shutdown = false;
    configureLogging();

    gExperimentsEnabled = gPrefs.getBoolPref(PREF_ENABLED, false) && Services.telemetry.canRecordExtended;
    this._log.trace("enabled=" + gExperimentsEnabled + ", " + this.enabled);

    Services.prefs.addObserver(PREF_BRANCH + PREF_LOGGING, configureLogging);
    Services.prefs.addObserver(PREF_BRANCH + PREF_MANIFEST_URI, this, true);
    Services.prefs.addObserver(PREF_BRANCH + PREF_ENABLED, this, true);

    AddonManager.shutdown.addBlocker("Experiments.jsm shutdown",
      this.uninit.bind(this),
      this._getState.bind(this)
    );

    this._registerWithAddonManager();

    this._loadTask = this._loadFromCache();

    return this._loadTask.then(
      () => {
        this._log.trace("_loadTask finished ok");
        this._loadTask = null;
        return this._run();
      },
      (e) => {
        this._log.error("_loadFromCache caught error: " + e);
        this._latestError = e;
        throw e;
      }
    );
  },

  /**
   * Uninitialize this instance.
   *
   * This function is susceptible to race conditions. If it is called multiple
   * times before the previous uninit() has completed or if it is called while
   * an init() operation is being performed, the object may get in bad state
   * and/or deadlock could occur.
   *
   * @return Promise<>
   *         The promise is fulfilled when all pending tasks are finished.
   */
  async uninit() {
    this._log.trace("uninit: started");
    await this._loadTask;
    this._log.trace("uninit: finished with _loadTask");

    if (!this._shutdown) {
      this._log.trace("uninit: no previous shutdown");
      this._unregisterWithAddonManager();

      Services.prefs.removeObserver(PREF_BRANCH + PREF_LOGGING, configureLogging);
      Services.prefs.removeObserver(PREF_BRANCH + PREF_MANIFEST_URI, this);
      Services.prefs.removeObserver(PREF_BRANCH + PREF_ENABLED, this);

      if (this._timer) {
        this._timer.clear();
      }
    }

    this._shutdown = true;
    if (this._mainTask) {
      if (this._networkRequest) {
        try {
          this._log.trace("Aborting pending network request: " + this._networkRequest);
          this._networkRequest.abort();
        } catch (e) {
          // pass
        }
      }
      try {
        this._log.trace("uninit: waiting on _mainTask");
        await this._mainTask;
      } catch (e) {
        // We error out of tasks after shutdown via this exception.
        this._log.trace(`uninit: caught error - ${e}`);
        if (!(e instanceof AlreadyShutdownError)) {
          this._latestError = e;
          throw e;
        }
      }
    }

    this._log.info("Completed uninitialization.");
  },

  // Return state information, for debugging purposes.
  _getState() {
    let activeExperiment = this._getActiveExperiment();
    let state = {
      isShutdown: this._shutdown,
      isEnabled: gExperimentsEnabled,
      isRefresh: this._refresh,
      isDirty: this._dirty,
      isFirstEvaluate: this._firstEvaluate,
      hasLoadTask: !!this._loadTask,
      hasMainTask: !!this._mainTask,
      hasTimer: !!this._hasTimer,
      hasAddonProvider: !!gAddonProvider,
      latestLogs: this._forensicsLogs,
      experiments: this._experiments ? [...this._experiments.keys()] : null,
      terminateReason: this._terminateReason,
      activeExperiment: activeExperiment ? activeExperiment.id : null,
    };
    if (this._latestError) {
      if (typeof this._latestError == "object") {
        state.latestError = {
          message: this._latestError.message,
          stack: this._latestError.stack
        };
      } else {
        state.latestError = "" + this._latestError;
      }
    }
    return state;
  },

  _addToForensicsLog(what, string) {
    this._forensicsLogs.shift();
    let timeInSec = Math.floor(Services.telemetry.msSinceProcessStart() / 1000);
    this._forensicsLogs.push(`${timeInSec}: ${what} - ${string}`);
  },

  _registerWithAddonManager(previousExperimentsProvider) {
    this._log.trace("Registering instance with Addon Manager.");

    AddonManager.addAddonListener(this);
    AddonManager.addInstallListener(this);

    if (!gAddonProvider) {
      // The properties of this AddonType should be kept in sync with the
      // experiment AddonType registered in XPIProvider.
      this._log.trace("Registering previous experiment add-on provider.");
      gAddonProvider = previousExperimentsProvider || new Experiments.PreviousExperimentProvider(this);
      AddonManagerPrivate.registerProvider(gAddonProvider, [
          new AddonManagerPrivate.AddonType("experiment",
                                            URI_EXTENSION_STRINGS,
                                            "type.experiment.name",
                                            AddonManager.VIEW_TYPE_LIST,
                                            11000,
                                            AddonManager.TYPE_UI_HIDE_EMPTY),
      ]);
    }

  },

  _unregisterWithAddonManager() {
    this._log.trace("Unregistering instance with Addon Manager.");

    this._log.trace("Removing install listener from add-on manager.");
    AddonManager.removeInstallListener(this);
    this._log.trace("Removing addon listener from add-on manager.");
    AddonManager.removeAddonListener(this);
    this._log.trace("Finished unregistering with addon manager.");

    if (gAddonProvider) {
      this._log.trace("Unregistering previous experiment add-on provider.");
      AddonManagerPrivate.unregisterProvider(gAddonProvider);
      gAddonProvider = null;
    }
  },

  /*
   * Change the PreviousExperimentsProvider that this instance uses.
   * For testing only.
   */
  _setPreviousExperimentsProvider(provider) {
    this._unregisterWithAddonManager();
    this._registerWithAddonManager(provider);
  },

  /**
   * Throws an exception if we've already shut down.
   */
  _checkForShutdown() {
    if (this._shutdown) {
      throw new AlreadyShutdownError("uninit() already called");
    }
  },

  /**
   * Whether the experiments feature is enabled.
   */
  get enabled() {
    return gExperimentsEnabled;
  },

  /**
   * Toggle whether the experiments feature is enabled or not.
   */
  set enabled(enabled) {
    this._log.trace("set enabled(" + enabled + ")");
    gPrefs.setBoolPref(PREF_ENABLED, enabled);
  },

  async _toggleExperimentsEnabled(enabled) {
    this._log.trace("_toggleExperimentsEnabled(" + enabled + ")");
    let wasEnabled = gExperimentsEnabled;
    gExperimentsEnabled = enabled && Services.telemetry.canRecordExtended;

    if (wasEnabled == gExperimentsEnabled) {
      return;
    }

    if (gExperimentsEnabled) {
      await this.updateManifest();
    } else {
      await this.disableExperiment(TELEMETRY_LOG.TERMINATION.SERVICE_DISABLED);
      if (this._timer) {
        this._timer.clear();
      }
    }
  },

  /**
   * Returns a promise that is resolved with an array of `ExperimentInfo` objects,
   * which provide info on the currently and recently active experiments.
   * The array is in chronological order.
   *
   * The experiment info is of the form:
   * {
   *   id: <string>,
   *   name: <string>,
   *   description: <string>,
   *   active: <boolean>,
   *   endDate: <integer>, // epoch ms
   *   detailURL: <string>,
   *   ... // possibly extended later
   * }
   *
   * @return Promise<Array<ExperimentInfo>> Array of experiment info objects.
   */
  getExperiments() {
    return (async () => {
      await this._loadTask;
      let list = [];

      for (let [id, experiment] of this._experiments) {
        if (!experiment.startDate) {
          // We only collect experiments that are or were active.
          continue;
        }

        list.push({
          id,
          name: experiment._name,
          description: experiment._description,
          active: experiment.enabled,
          endDate: experiment.endDate.getTime(),
          detailURL: experiment._homepageURL,
          branch: experiment.branch,
        });
      }

      // Sort chronologically, descending.
      list.sort((a, b) => b.endDate - a.endDate);
      return list;
    })();
  },

  /**
   * Returns the ExperimentInfo for the active experiment, or null
   * if there is none.
   */
  getActiveExperiment() {
    let experiment = this._getActiveExperiment();
    if (!experiment) {
      return null;
    }

    let info = {
      id: experiment.id,
      name: experiment._name,
      description: experiment._description,
      active: experiment.enabled,
      endDate: experiment.endDate.getTime(),
      detailURL: experiment._homepageURL,
    };

    return info;
  },

  /**
   * Experiment "branch" support. If an experiment has multiple branches, it
   * can record the branch with the experiment system and it will
   * automatically be included in data reporting (FHR/telemetry payloads).
   */

  /**
   * Set the experiment branch for the specified experiment ID.
   * @returns Promise<>
   */
  async setExperimentBranch(id, branchstr) {
    await this._loadTask;
    let e = this._experiments.get(id);
    if (!e) {
      throw new Error("Experiment not found");
    }
    e.branch = String(branchstr);
    this._log.trace("setExperimentBranch(" + id + ", " + e.branch + ") _dirty=" + this._dirty);
    this._dirty = true;
    Services.obs.notifyObservers(null, EXPERIMENTS_CHANGED_TOPIC);
    await this._run();
  },
  /**
   * Get the branch of the specified experiment. If the experiment is unknown,
   * throws an error.
   *
   * @param id The ID of the experiment. Pass null for the currently running
   *           experiment.
   * @returns Promise<string|null>
   * @throws Error if the specified experiment ID is unknown, or if there is no
   *         current experiment.
   */
  async getExperimentBranch(id = null) {
    await this._loadTask;
    let e;
    if (id) {
      e = this._experiments.get(id);
      if (!e) {
        throw new Error("Experiment not found");
      }
    } else {
      e = this._getActiveExperiment();
      if (e === null) {
        throw new Error("No active experiment");
      }
    }
    return e.branch;
  },

  /**
   * Determine whether another date has the same UTC day as now().
   */
  _dateIsTodayUTC(d) {
    let now = this._policy.now();

    return stripDateToMidnight(now).getTime() == stripDateToMidnight(d).getTime();
  },

  /**
   * Obtain the entry of the most recent active experiment that was active
   * today.
   *
   * If no experiment was active today, this resolves to nothing.
   *
   * Assumption: Only a single experiment can be active at a time.
   *
   * @return Promise<object>
   */
  lastActiveToday() {
    return (async () => {
      let experiments = await this.getExperiments();

      // Assumption: Ordered chronologically, descending, with active always
      // first.
      for (let experiment of experiments) {
        if (experiment.active) {
          return experiment;
        }

        if (experiment.endDate && this._dateIsTodayUTC(experiment.endDate)) {
          return experiment;
        }
      }
      return null;
    })();
  },

  _run() {
    this._log.trace("_run");
    this._checkForShutdown();
    if (!this._mainTask) {
      this._mainTask = (async () => {
        try {
          await this._main();
        } catch (e) {
          // In the CacheWriteError case we want to reschedule
          if (!(e instanceof CacheWriteError)) {
            this._log.error("_main caught error: " + e);
            return;
          }
        } finally {
          this._mainTask = null;
        }
        this._log.trace("_main finished, scheduling next run");
        try {
          await this._scheduleNextRun();
        } catch (ex) {
          // We error out of tasks after shutdown via this exception.
          if (!(ex instanceof AlreadyShutdownError)) {
            throw ex;
          }
        }
      })();
    }
    return this._mainTask;
  },

  async _main() {
    do {
      this._log.trace("_main iteration");
      await this._loadTask;
      if (!gExperimentsEnabled) {
        this._refresh = false;
      }

      if (this._refresh) {
        await this._loadManifest();
      }
      await this._evaluateExperiments();
      if (this._dirty) {
        await this._saveToCache();
      }
      // If somebody called .updateManifest() or disableExperiment()
      // while we were running, go again right now.
    }
    while (this._refresh || this._terminateReason || this._dirty);
  },

  async _loadManifest() {
    this._log.trace("_loadManifest");
    let uri = Services.urlFormatter.formatURLPref(PREF_BRANCH + PREF_MANIFEST_URI);

    this._checkForShutdown();

    this._refresh = false;
    try {
      let responseText = await this._httpGetRequest(uri);
      this._log.trace("_loadManifest() - responseText=\"" + responseText + "\"");

      if (this._shutdown) {
        return;
      }

      let data = JSON.parse(responseText);
      this._updateExperiments(data);
    } catch (e) {
      this._log.error("_loadManifest - failure to fetch/parse manifest (continuing anyway): " + e);
    }
  },

  /**
   * Fetch an updated list of experiments and trigger experiment updates.
   * Do only use when experiments are enabled.
   *
   * @return Promise<>
   *         The promise is resolved when the manifest and experiment list is updated.
   */
  updateManifest() {
    this._log.trace("updateManifest()");

    if (!gExperimentsEnabled) {
      return Promise.reject(new Error("experiments are disabled"));
    }

    if (this._shutdown) {
      return Promise.reject(Error("uninit() alrady called"));
    }

    this._refresh = true;
    return this._run();
  },

  notify(timer) {
    this._log.trace("notify()");
    this._checkForShutdown();
    return this._run();
  },

  // START OF ADD-ON LISTENERS

  onUninstalled(addon) {
    this._log.trace("onUninstalled() - addon id: " + addon.id);
    if (gActiveUninstallAddonIDs.has(addon.id)) {
      this._log.trace("matches pending uninstall");
      return;
    }
    let activeExperiment = this._getActiveExperiment();
    if (!activeExperiment || activeExperiment._addonId != addon.id) {
      return;
    }

    this.disableExperiment(TELEMETRY_LOG.TERMINATION.ADDON_UNINSTALLED);
  },

  /**
   * @returns {Boolean} returns false when we cancel the install.
   */
  onInstallStarted(install) {
    if (install.addon.type != "experiment") {
      return true;
    }

    this._log.trace("onInstallStarted() - " + install.addon.id);
    if (install.addon.appDisabled) {
      // This is a PreviousExperiment
      return true;
    }

    // We want to be in control of all experiment add-ons: reject installs
    // for add-ons that we don't know about.

    // We have a race condition of sorts to worry about here. We have 2
    // onInstallStarted listeners. This one (the global one) and the one
    // created as part of ExperimentEntry._installAddon. Because of the order
    // they are registered in, this one likely executes first. Unfortunately,
    // this means that the add-on ID is not yet set on the ExperimentEntry.
    // So, we can't just look at this._trackedAddonIds because the new experiment
    // will have its add-on ID set to null. We work around this by storing a
    // identifying field - the source URL of the install - in a module-level
    // variable (so multiple Experiments instances doesn't cancel each other
    // out).

    if (this._trackedAddonIds.has(install.addon.id)) {
      this._log.info("onInstallStarted allowing install because add-on ID " +
                     "tracked by us.");
      return true;
    }

    if (gActiveInstallURLs.has(install.sourceURI.spec)) {
      this._log.info("onInstallStarted allowing install because install " +
                     "tracked by us.");
      return true;
    }

    this._log.warn("onInstallStarted cancelling install of unknown " +
                   "experiment add-on: " + install.addon.id);
    return false;
  },

  // END OF ADD-ON LISTENERS.

  _getExperimentByAddonId(addonId) {
    for (let [, entry] of this._experiments) {
      if (entry._addonId === addonId) {
        return entry;
      }
    }

    return null;
  },

  /*
   * Helper function to make HTTP GET requests. Returns a promise that is resolved with
   * the responseText when the request is complete.
   */
  _httpGetRequest(url) {
    this._log.trace("httpGetRequest(" + url + ")");
    let xhr = new XMLHttpRequest();

    this._networkRequest = xhr;
    return new Promise((resolve, reject) => {

      let log = this._log;
      let errorhandler = (evt) => {
        log.error("httpGetRequest::onError() - Error making request to " + url + ": " + evt.type);
        reject(new Error("Experiments - XHR error for " + url + " - " + evt.type));
        this._networkRequest = null;
      };
      xhr.onerror = errorhandler;
      xhr.ontimeout = errorhandler;
      xhr.onabort = errorhandler;

      xhr.onload = (event) => {
        if (xhr.status !== 200 && xhr.state !== 0) {
          log.error("httpGetRequest::onLoad() - Request to " + url + " returned status " + xhr.status);
          reject(new Error("Experiments - XHR status for " + url + " is " + xhr.status));
          this._networkRequest = null;
          return;
        }

        resolve(xhr.responseText);
        this._networkRequest = null;
      };

      try {
        xhr.open("GET", url);

        if (xhr.channel instanceof Ci.nsISupportsPriority) {
          xhr.channel.priority = Ci.nsISupportsPriority.PRIORITY_LOWEST;
        }

        xhr.timeout = MANIFEST_FETCH_TIMEOUT_MSEC;
        xhr.send(null);
      } catch (e) {
        this._log.error("httpGetRequest() - Error opening request to " + url + ": " + e);
        reject(new Error("Experiments - Error opening XHR for " + url));
      }
    });
  },

  /*
   * Path of the cache file we use in the profile.
   */
  get _cacheFilePath() {
    return OS.Path.join(OS.Constants.Path.profileDir, FILE_CACHE);
  },

  /*
   * Part of the main task to save the cache to disk, called from _main.
   */
  async _saveToCache() {
    this._log.trace("_saveToCache");
    let path = this._cacheFilePath;
    this._dirty = false;
    try {
      let textData = JSON.stringify({
        version: CACHE_VERSION,
        data: [...this._experiments.values()].map(e => e.toJSON()),
      });

      let encoder = new TextEncoder();
      let data = encoder.encode(textData);
      let options = { tmpPath: path + ".tmp", compression: "lz4" };
      await this._policy.delayCacheWrite(OS.File.writeAtomic(path, data, options));
    } catch (e) {
      // We failed to write the cache, it's still dirty.
      this._dirty = true;
      this._log.error("_saveToCache failed and caught error: " + e);
      throw new CacheWriteError();
    }

    this._log.debug("_saveToCache saved to " + path);
  },

  /*
   * Task function, load the cached experiments manifest file from disk.
   */
  async _loadFromCache() {
    this._log.trace("_loadFromCache");
    let path = this._cacheFilePath;
    try {
      let result = await loadJSONAsync(path, { compression: "lz4" });
      this._populateFromCache(result);
    } catch (e) {
      this._experiments = new Map();
      if (e instanceof OS.File.Error && e.becauseNoSuchFile) {
        // No cached manifest yet.
        this._log.trace("_loadFromCache - no cached manifest yet");
      } else {
        this._log.error("_loadFromCache - caught error", e);
      }
    }
  },

  _populateFromCache(data) {
    this._log.trace("populateFromCache() - data: " + JSON.stringify(data));

    // If the user has a newer cache version than we can understand, we fail
    // hard; no experiments should be active in this older client.
    if (CACHE_VERSION !== data.version) {
      throw new Error("Experiments::_populateFromCache() - invalid cache version");
    }

    let experiments = new Map();
    for (let item of data.data) {
      let entry = new Experiments.ExperimentEntry(this._policy);
      if (!entry.initFromCacheData(item)) {
        continue;
      }

      // Discard old experiments if they ended more than 180 days ago.
      if (entry.shouldDiscard()) {
        // We discarded an experiment, the cache needs to be updated.
        this._dirty = true;
        continue;
      }

      experiments.set(entry.id, entry);
    }

    this._experiments = experiments;
  },

  /*
   * Update the experiment entries from the experiments
   * array in the manifest
   */
  _updateExperiments(manifestObject) {
    this._log.trace("_updateExperiments() - experiments: " + JSON.stringify(manifestObject));

    if (manifestObject.version !== MANIFEST_VERSION) {
      this._log.warning("updateExperiments() - unsupported version " + manifestObject.version);
    }

    let experiments = new Map(); // The new experiments map

    // Collect new and updated experiments.
    for (let data of manifestObject.experiments) {
      let entry = this._experiments.get(data.id);

      if (entry) {
        if (!entry.updateFromManifestData(data)) {
          this._log.error("updateExperiments() - Invalid manifest data for " + data.id);
          continue;
        }
      } else {
        entry = new Experiments.ExperimentEntry(this._policy);
        if (!entry.initFromManifestData(data)) {
          continue;
        }
      }

      if (entry.shouldDiscard()) {
        continue;
      }

      experiments.set(entry.id, entry);
    }

    // Make sure we keep experiments that are or were running.
    // We remove them after KEEP_HISTORY_N_DAYS.
    for (let [id, entry] of this._experiments) {
      if (experiments.has(id)) {
        continue;
      }

      if (!entry.startDate || entry.shouldDiscard()) {
        this._log.trace("updateExperiments() - discarding entry for " + id);
        continue;
      }

      experiments.set(id, entry);
    }

    this._experiments = experiments;
    this._dirty = true;
  },

  getActiveExperimentID() {
    if (!this._experiments) {
      return null;
    }
    let e = this._getActiveExperiment();
    if (!e) {
      return null;
    }
    return e.id;
  },

  getActiveExperimentBranch() {
    if (!this._experiments) {
      return null;
    }
    let e = this._getActiveExperiment();
    if (!e) {
      return null;
    }
    return e.branch;
  },

  _getActiveExperiment() {
    let enabled = [...this._experiments.values()].filter(experiment => experiment._enabled);

    if (enabled.length == 1) {
      return enabled[0];
    }

    if (enabled.length > 1) {
      this._log.error("getActiveExperimentId() - should not have more than 1 active experiment");
      throw new Error("have more than 1 active experiment");
    }

    return null;
  },

  /**
   * Disables all active experiments.
   *
   * @return Promise<> Promise that will get resolved once the task is done or failed.
   */
  disableExperiment(reason) {
    if (!reason) {
      throw new Error("Must specify a termination reason.");
    }

    this._log.trace("disableExperiment()");
    this._terminateReason = reason;
    return this._run();
  },

  /**
   * The Set of add-on IDs that we know about from manifests.
   */
  get _trackedAddonIds() {
    if (!this._experiments) {
      return new Set();
    }

    return new Set([...this._experiments.values()].map(e => e._addonId));
  },

  /*
   * Task function to check applicability of experiments, disable the active
   * experiment if needed and activate the first applicable candidate.
   */
  async _evaluateExperiments() {
    this._log.trace("_evaluateExperiments");

    this._checkForShutdown();

    // The first thing we do is reconcile our state against what's in the
    // Addon Manager. It's possible that the Addon Manager knows of experiment
    // add-ons that we don't. This could happen if an experiment gets installed
    // when we're not listening or if there is a bug in our synchronization
    // code.
    //
    // We have a few options of what to do with unknown experiment add-ons
    // coming from the Addon Manager. Ideally, we'd convert these to
    // ExperimentEntry instances and stuff them inside this._experiments.
    // However, since ExperimentEntry contain lots of metadata from the
    // manifest and trying to make up data could be error prone, it's safer
    // to not try. Furthermore, if an experiment really did come from us, we
    // should have some record of it. In the end, we decide to discard all
    // knowledge for these unknown experiment add-ons.
    let installedExperiments = await installedExperimentAddons();
    let expectedAddonIds = this._trackedAddonIds;
    let unknownAddons = installedExperiments.filter(a => !expectedAddonIds.has(a.id));
    if (unknownAddons.length) {
      this._log.warn("_evaluateExperiments() - unknown add-ons in AddonManager: " +
                     unknownAddons.map(a => a.id).join(", "));

      await uninstallAddons(unknownAddons);
    }

    let activeExperiment = this._getActiveExperiment();
    let activeChanged = false;

    if (!activeExperiment) {
      // Avoid this pref staying out of sync if there were e.g. crashes.
      gPrefs.setBoolPref(PREF_ACTIVE_EXPERIMENT, false);
    }

    // Ensure the active experiment is in the proper state. This may install,
    // uninstall, upgrade, or enable the experiment add-on. What exactly is
    // abstracted away from us by design.
    if (activeExperiment) {
      let changes;
      let shouldStopResult = await activeExperiment.shouldStop();
      if (shouldStopResult.shouldStop) {
        let expireReasons = ["endTime", "maxActiveSeconds"];
        let kind, reason;

        if (expireReasons.includes(shouldStopResult.reason[0])) {
          kind = TELEMETRY_LOG.TERMINATION.EXPIRED;
          reason = null;
        } else {
          kind = TELEMETRY_LOG.TERMINATION.RECHECK;
          reason = shouldStopResult.reason;
        }
        changes = await activeExperiment.stop(kind, reason);
      } else if (this._terminateReason) {
        changes = await activeExperiment.stop(this._terminateReason);
      } else {
        changes = await activeExperiment.reconcileAddonState();
      }

      if (changes) {
        this._dirty = true;
        activeChanged = true;
      }

      if (!activeExperiment._enabled) {
        activeExperiment = null;
        activeChanged = true;
      }
    }

    this._terminateReason = null;

    if (!activeExperiment && gExperimentsEnabled) {
      for (let [id, experiment] of this._experiments) {
        let applicable;
        let reason = null;
        try {
          applicable = await experiment.isApplicable();
        } catch (e) {
          applicable = false;
          reason = e;
        }

        if (!applicable && reason && reason[0] != "was-active") {
          // Report this from here to avoid over-reporting.
          let data = [TELEMETRY_LOG.ACTIVATION.REJECTED, id];
          data = data.concat(reason);
          const key = TELEMETRY_LOG.ACTIVATION_KEY;
          TelemetryLog.log(key, data);
          this._log.trace("evaluateExperiments() - added " + key + " to TelemetryLog: " + JSON.stringify(data));
        }

        if (!applicable) {
          continue;
        }

        this._log.debug("evaluateExperiments() - activating experiment " + id);
        try {
          await experiment.start();
          activeChanged = true;
          activeExperiment = experiment;
          this._dirty = true;
          break;
        } catch (e) {
          // On failure, clean up the best we can and try the next experiment.
          this._log.error("evaluateExperiments() - Unable to start experiment: " + e.message);
          experiment._enabled = false;
          await experiment.reconcileAddonState();
        }
      }
    }

    gPrefs.setBoolPref(PREF_ACTIVE_EXPERIMENT, activeExperiment != null);

    if (activeChanged || this._firstEvaluate) {
      Services.obs.notifyObservers(null, EXPERIMENTS_CHANGED_TOPIC);
      this._firstEvaluate = false;
    }

    if ("@mozilla.org/toolkit/crash-reporter;1" in Cc && activeExperiment) {
      try {
        gCrashReporter.annotateCrashReport("ActiveExperiment", activeExperiment.id);
        gCrashReporter.annotateCrashReport("ActiveExperimentBranch", activeExperiment.branch);
      } catch (e) {
        // It's ok if crash reporting is disabled.
      }
    }
  },

  /*
   * Schedule the soonest re-check of experiment applicability that is needed.
   */
  _scheduleNextRun() {
    this._checkForShutdown();

    if (this._timer) {
      this._timer.clear();
    }

    if (!gExperimentsEnabled || this._experiments.length == 0) {
      return;
    }

    let time = null;
    let now = this._policy.now().getTime();
    if (this._dirty) {
      // If we failed to write the cache, we should try again periodically
      time = now + 1000 * CACHE_WRITE_RETRY_DELAY_SEC;
    }

    for (let [, experiment] of this._experiments) {
      let scheduleTime = experiment.getScheduleTime();
      if (scheduleTime > now) {
        if (time !== null) {
          time = Math.min(time, scheduleTime);
        } else {
          time = scheduleTime;
        }
      }
    }

    if (time === null) {
      // No schedule time found.
      return;
    }

    this._log.trace("scheduleExperimentEvaluation() - scheduling for " + time + ", now: " + now);
    this._policy.oneshotTimer(this.notify, time - now, this, "_timer");
  },
};


/*
 * Represents a single experiment.
 */

Experiments.ExperimentEntry = function(policy) {
  this._policy = policy || new Experiments.Policy();
  let log = Log.repository.getLoggerWithMessagePrefix(
    "Browser.Experiments.Experiments",
    "ExperimentEntry #" + gExperimentEntryCounter++ + "::");
  this._log = Object.create(log);
  this._log.log = (level, string, params) => {
    if (gExperiments) {
      gExperiments._addToForensicsLog("ExperimentEntry", string);
    }
    log.log(level, string, params);
  };

  // Is the experiment supposed to be running.
  this._enabled = false;
  // When this experiment was started, if ever.
  this._startDate = null;
  // When this experiment was ended, if ever.
  this._endDate = null;
  // The condition data from the manifest.
  this._manifestData = null;
  // For an active experiment, signifies whether we need to update the xpi.
  this._needsUpdate = false;
  // A random sample value for comparison against the manifest conditions.
  this._randomValue = null;
  // When this entry was last changed for respecting history retention duration.
  this._lastChangedDate = null;
  // Has this experiment failed to activate before?
  this._failedStart = false;
  // The experiment branch
  this._branch = null;

  // We grab these from the addon after download.
  this._name = null;
  this._description = null;
  this._homepageURL = null;
  this._addonId = null;
};

Experiments.ExperimentEntry.prototype = {
  MANIFEST_REQUIRED_FIELDS: new Set([
    "id",
    "xpiURL",
    "xpiHash",
    "startTime",
    "endTime",
    "maxActiveSeconds",
    "appName",
    "channel",
  ]),

  MANIFEST_OPTIONAL_FIELDS: new Set([
    "maxStartTime",
    "minVersion",
    "maxVersion",
    "version",
    "minBuildID",
    "maxBuildID",
    "buildIDs",
    "os",
    "locale",
    "sample",
    "disabled",
    "frozen",
    "jsfilter",
  ]),

  SERIALIZE_KEYS: new Set([
    "_enabled",
    "_manifestData",
    "_needsUpdate",
    "_randomValue",
    "_failedStart",
    "_name",
    "_description",
    "_homepageURL",
    "_addonId",
    "_startDate",
    "_endDate",
    "_branch",
  ]),

  DATE_KEYS: new Set([
    "_startDate",
    "_endDate",
  ]),

  UPGRADE_KEYS: new Map([
    ["_branch", null],
  ]),

  ADDON_CHANGE_NONE: 0,
  ADDON_CHANGE_INSTALL: 1,
  ADDON_CHANGE_UNINSTALL: 2,
  ADDON_CHANGE_ENABLE: 4,

  /*
   * Initialize entry from the manifest.
   * @param data The experiment data from the manifest.
   * @return boolean Whether initialization succeeded.
   */
  initFromManifestData(data) {
    if (!this._isManifestDataValid(data)) {
      return false;
    }

    this._manifestData = data;

    this._randomValue = this._policy.random();
    this._lastChangedDate = this._policy.now();

    return true;
  },

  get enabled() {
    return this._enabled;
  },

  get id() {
    return this._manifestData.id;
  },

  get branch() {
    return this._branch;
  },

  set branch(v) {
    this._branch = v;
  },

  get startDate() {
    return this._startDate;
  },

  get endDate() {
    if (!this._startDate) {
      return null;
    }

    let endTime = 0;

    if (!this._enabled) {
      return this._endDate;
    }

    let maxActiveMs = 1000 * this._manifestData.maxActiveSeconds;
    endTime = Math.min(1000 * this._manifestData.endTime,
                       this._startDate.getTime() + maxActiveMs);

    return new Date(endTime);
  },

  get needsUpdate() {
    return this._needsUpdate;
  },

  /*
   * Initialize entry from the cache.
   * @param data The entry data from the cache.
   * @return boolean Whether initialization succeeded.
   */
  initFromCacheData(data) {
    for (let [key, dval] of this.UPGRADE_KEYS) {
      if (!(key in data)) {
        data[key] = dval;
      }
    }

    for (let key of this.SERIALIZE_KEYS) {
      if (!(key in data) && !this.DATE_KEYS.has(key)) {
        this._log.error("initFromCacheData() - missing required key " + key);
        return false;
      }
    }

    if (!this._isManifestDataValid(data._manifestData)) {
      return false;
    }

    // Dates are restored separately from epoch ms, everything else is just
    // copied in.

    this.SERIALIZE_KEYS.forEach(key => {
      if (!this.DATE_KEYS.has(key)) {
        this[key] = data[key];
      }
    });

    this.DATE_KEYS.forEach(key => {
      if (key in data) {
        let date = new Date();
        date.setTime(data[key]);
        this[key] = date;
      }
    });

    // In order for the experiment's data expiration mechanism to work, use the experiment's
    // |_endData| as the |_lastChangedDate| (if available).
    this._lastChangedDate = this._endDate ? this._endDate : this._policy.now();

    return true;
  },

  /*
   * Returns a JSON representation of this object.
   */
  toJSON() {
    let obj = {};

    // Dates are serialized separately as epoch ms.

    this.SERIALIZE_KEYS.forEach(key => {
      if (!this.DATE_KEYS.has(key)) {
        obj[key] = this[key];
      }
    });

    this.DATE_KEYS.forEach(key => {
      if (this[key]) {
        obj[key] = this[key].getTime();
      }
    });

    return obj;
  },

  /*
   * Update from the experiment data from the manifest.
   * @param data The experiment data from the manifest.
   * @return boolean Whether updating succeeded.
   */
  updateFromManifestData(data) {
    let old = this._manifestData;

    if (!this._isManifestDataValid(data)) {
      return false;
    }

    if (this._enabled) {
      if (old.xpiHash !== data.xpiHash) {
        // A changed hash means we need to update active experiments.
        this._needsUpdate = true;
      }
    } else if (this._failedStart &&
               (old.xpiHash !== data.xpiHash) ||
               (old.xpiURL !== data.xpiURL)) {
      // Retry installation of previously invalid experiments
      // if hash or url changed.
      this._failedStart = false;
    }

    this._manifestData = data;
    this._lastChangedDate = this._policy.now();

    return true;
  },

  /*
   * Is this experiment applicable?
   * @return Promise<> Resolved if the experiment is applicable.
   *                   If it is not applicable it is rejected with
   *                   a Promise<string> which contains the reason.
   */
  isApplicable() {
    let locale = this._policy.locale();
    let channel = this._policy.updatechannel();
    let data = this._manifestData;

    let now = this._policy.now() / 1000; // The manifest times are in seconds.
    let maxActive = data.maxActiveSeconds || 0;
    let startSec = (this.startDate || 0) / 1000;

    this._log.trace("isApplicable() - now=" + now
                    + ", randomValue=" + this._randomValue);

    // Not applicable if it already ran.

    if (!this.enabled && this._endDate) {
      return Promise.reject(["was-active"]);
    }

    // Define and run the condition checks.

    let simpleChecks = [
      { name: "failedStart",
        condition: () => !this._failedStart },
      { name: "disabled",
        condition: () => !data.disabled },
      { name: "frozen",
        condition: () => !data.frozen || this._enabled },
      { name: "startTime",
        condition: () => now >= data.startTime },
      { name: "endTime",
        condition: () => now < data.endTime },
      { name: "maxStartTime",
        condition: () => this._startDate || !data.maxStartTime || now <= data.maxStartTime },
      { name: "maxActiveSeconds",
        condition: () => !this._startDate || now <= (startSec + maxActive) },
      { name: "appName",
        condition: () => !data.appName || data.appName.includes(Services.appinfo.name) },
      { name: "minBuildID",
        condition: () => !data.minBuildID || Services.appinfo.platformBuildID >= data.minBuildID },
      { name: "maxBuildID",
        condition: () => !data.maxBuildID || Services.appinfo.platformBuildID <= data.maxBuildID },
      { name: "buildIDs",
        condition: () => !data.buildIDs || data.buildIDs.includes(Services.appinfo.platformBuildID) },
      { name: "os",
        condition: () => !data.os || data.os.includes(Services.appinfo.OS) },
      { name: "channel",
        condition: () => !data.channel || data.channel.includes(channel) },
      { name: "locale",
        condition: () => !data.locale || data.locale.includes(locale) },
      { name: "sample",
        condition: () => data.sample === undefined || this._randomValue <= data.sample },
      { name: "version",
        condition: () => !data.version || data.version.includes(Services.appinfo.version) },
      { name: "minVersion",
        condition: () => !data.minVersion || Services.vc.compare(Services.appinfo.version, data.minVersion) >= 0 },
      { name: "maxVersion",
        condition: () => !data.maxVersion || Services.vc.compare(Services.appinfo.version, data.maxVersion) <= 0 },
    ];

    for (let check of simpleChecks) {
      let result = check.condition();
      if (!result) {
        this._log.debug("isApplicable() - id="
                        + data.id + " - test '" + check.name + "' failed");
        return Promise.reject([check.name]);
      }
    }

    if (data.jsfilter) {
      return this._runFilterFunction(data.jsfilter);
    }

    return Promise.resolve(true);
  },

  /*
   * Run the jsfilter function from the manifest in a sandbox and return the
   * result (forced to boolean).
   */
  async _runFilterFunction(jsfilter) {
    this._log.trace("runFilterFunction() - filter: " + jsfilter);

    let ssm = Services.scriptSecurityManager;
    const nullPrincipal = ssm.createNullPrincipal({});
    let options = {
      sandboxName: "telemetry experiments jsfilter sandbox",
      wantComponents: false,
    };

    let sandbox = Cu.Sandbox(nullPrincipal, options);
    try {
      Cu.evalInSandbox(jsfilter, sandbox);
    } catch (e) {
      this._log.error("runFilterFunction() - failed to eval jsfilter: " + e.message);
      throw ["jsfilter-evalfailed"];
    }

    let currentEnvironment = await TelemetryEnvironment.onInitialized();

    Object.defineProperty(sandbox, "_e",
      { get: () => Cu.cloneInto(currentEnvironment, sandbox) });

    let result = false;
    try {
      result = !!Cu.evalInSandbox("filter({get telemetryEnvironment() { return _e; } })", sandbox);
    } catch (e) {
      this._log.debug("runFilterFunction() - filter function failed: "
                      + e.message + ", " + e.stack);
      throw ["jsfilter-threw", e.message];
    } finally {
      Cu.nukeSandbox(sandbox);
    }

    if (!result) {
      throw ["jsfilter-false"];
    }

    return true;
  },

  /*
   * Start running the experiment.
   *
   * @return Promise<> Resolved when the operation is complete.
   */
  start() {
    this._log.trace("start() for " + this.id);

    this._enabled = true;
    return this.reconcileAddonState();
  },

  // Async install of the addon for this experiment, part of the start task above.
  async _installAddon() {
    let hash = this._policy.ignoreHashes ? null : this._manifestData.xpiHash;

    let install = await addonInstallForURL(this._manifestData.xpiURL, hash);
    gActiveInstallURLs.add(install.sourceURI.spec);

    return new Promise((resolve, reject) => {
      let failureHandler = (failureInstall, handler) => {
        let message = "AddonInstall " + handler + " for " + this.id + ", state=" +
                     (failureInstall.state || "?") + ", error=" + failureInstall.error;
        this._log.error("_installAddon() - " + message);
        this._failedStart = true;
        gActiveInstallURLs.delete(failureInstall.sourceURI.spec);

        TelemetryLog.log(TELEMETRY_LOG.ACTIVATION_KEY,
                        [TELEMETRY_LOG.ACTIVATION.INSTALL_FAILURE, this.id]);

        reject(new Error(message));
      };

      let listener = {
        _expectedID: null,

        onDownloadEnded: downloadEndedInstall => {
          this._log.trace("_installAddon() - onDownloadEnded for " + this.id);

          if (downloadEndedInstall.existingAddon) {
            this._log.warn("_installAddon() - onDownloadEnded, addon already installed");
          }

          if (downloadEndedInstall.addon.type !== "experiment") {
            this._log.error("_installAddon() - onDownloadEnded, wrong addon type");
            downloadEndedInstall.cancel();
          }
        },

        onInstallStarted: installStartedInstall => {
          this._log.trace("_installAddon() - onInstallStarted for " + this.id);

          if (installStartedInstall.existingAddon) {
            this._log.warn("_installAddon() - onInstallStarted, addon already installed");
          }

          if (installStartedInstall.addon.type !== "experiment") {
            this._log.error("_installAddon() - onInstallStarted, wrong addon type");
            return false;
          }
          return undefined;
        },

        onInstallEnded: installEndedInstall => {
          this._log.trace("_installAddon() - install ended for " + this.id);
          gActiveInstallURLs.delete(installEndedInstall.sourceURI.spec);

          this._lastChangedDate = this._policy.now();
          this._startDate = this._policy.now();
          this._enabled = true;

          TelemetryLog.log(TELEMETRY_LOG.ACTIVATION_KEY,
                         [TELEMETRY_LOG.ACTIVATION.ACTIVATED, this.id]);

          let addon = installEndedInstall.addon;
          this._name = addon.name;
          this._addonId = addon.id;
          this._description = addon.description || "";
          this._homepageURL = addon.homepageURL || "";

          // Experiment add-ons default to userDisabled=true. Enable if needed.
          if (addon.userDisabled) {
            this._log.trace("Add-on is disabled. Enabling.");
            listener._expectedID = addon.id;
            AddonManager.addAddonListener(listener);
            addon.userDisabled = false;
          } else {
            this._log.trace("Add-on is enabled. start() completed.");
            resolve();
          }
        },

        onEnabled: addon => {
          this._log.info("onEnabled() for " + addon.id);

          if (addon.id != listener._expectedID) {
            return;
          }

          AddonManager.removeAddonListener(listener);
          resolve();
        },
      };

      ["onDownloadCancelled", "onDownloadFailed", "onInstallCancelled", "onInstallFailed"]
        .forEach(what => {
          listener[what] = eventInstall => failureHandler(eventInstall, what);
        });

      install.addListener(listener);
      install.install();
    });
  },

  /**
   * Stop running the experiment if it is active.
   *
   * @param terminationKind (optional)
   *        The termination kind, e.g. ADDON_UNINSTALLED or EXPIRED.
   * @param terminationReason (optional)
   *        The termination reason details for termination kind RECHECK.
   * @return Promise<> Resolved when the operation is complete.
   */
  async stop(terminationKind, terminationReason) {
    this._log.trace("stop() - id=" + this.id + ", terminationKind=" + terminationKind);
    if (!this._enabled) {
      throw new Error("Must not call stop() on an inactive experiment.");
    }

    this._enabled = false;
    let now = this._policy.now();
    this._lastChangedDate = now;
    this._endDate = now;

    let changes = await this.reconcileAddonState();
    this._logTermination(terminationKind, terminationReason);

    if (terminationKind == TELEMETRY_LOG.TERMINATION.ADDON_UNINSTALLED) {
      changes |= this.ADDON_CHANGE_UNINSTALL;
    }

    return changes;
  },

  /**
   * Reconcile the state of the add-on against what it's supposed to be.
   *
   * If we are active, ensure the add-on is enabled and up to date.
   *
   * If we are inactive, ensure the add-on is not installed.
   */
  async reconcileAddonState() {
    this._log.trace("reconcileAddonState()");

    if (!this._enabled) {
      if (!this._addonId) {
        this._log.trace("reconcileAddonState() - Experiment is not enabled and " +
                        "has no add-on. Doing nothing.");
        return this.ADDON_CHANGE_NONE;
      }

      let addon = await this._getAddon();
      if (!addon) {
        this._log.trace("reconcileAddonState() - Inactive experiment has no " +
                        "add-on. Doing nothing.");
        return this.ADDON_CHANGE_NONE;
      }

      this._log.info("reconcileAddonState() - Uninstalling add-on for inactive " +
                     "experiment: " + addon.id);
      gActiveUninstallAddonIDs.add(addon.id);
      await uninstallAddons([addon]);
      gActiveUninstallAddonIDs.delete(addon.id);
      return this.ADDON_CHANGE_UNINSTALL;
    }

    // If we get here, we're supposed to be active.

    let changes = 0;

    // That requires an add-on.
    let currentAddon = await this._getAddon();

    // If we have an add-on but it isn't up to date, uninstall it
    // (to prepare for reinstall).
    if (currentAddon && this._needsUpdate) {
      this._log.info("reconcileAddonState() - Uninstalling add-on because update " +
                     "needed: " + currentAddon.id);
      gActiveUninstallAddonIDs.add(currentAddon.id);
      await uninstallAddons([currentAddon]);
      gActiveUninstallAddonIDs.delete(currentAddon.id);
      changes |= this.ADDON_CHANGE_UNINSTALL;
    }

    if (!currentAddon || this._needsUpdate) {
      this._log.info("reconcileAddonState() - Installing add-on.");
      await this._installAddon();
      changes |= this.ADDON_CHANGE_INSTALL;
    }

    let addon = await this._getAddon();
    if (!addon) {
      throw new Error("Could not obtain add-on for experiment that should be " +
                      "enabled.");
    }

    // If we have the add-on and it is enabled, we are done.
    if (!addon.userDisabled) {
      return changes;
    }

    // Check permissions to see if we can enable the addon.
    if (!(addon.permissions & AddonManager.PERM_CAN_ENABLE)) {
      throw new Error("Don't have permission to enable addon " + addon.id + ", perm=" + addon.permission);
    }

    // Experiment addons should not require a restart.
    if (addon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_ENABLE) {
      throw new Error("Experiment addon requires a restart: " + addon.id);
    }

    await new Promise((resolve, reject) => {

      // Else we need to enable it.
      let listener = {
        onEnabled: enabledAddon => {
          if (enabledAddon.id != addon.id) {
            return;
          }

          AddonManager.removeAddonListener(listener);
          resolve();
        },
      };

      for (let handler of ["onDisabled", "onOperationCancelled", "onUninstalled"]) {
        listener[handler] = (evtAddon) => {
          if (evtAddon.id != addon.id) {
            return;
          }

          AddonManager.removeAddonListener(listener);
          reject("Failed to enable addon " + addon.id + " due to: " + handler);
        };
      }

      this._log.info("reconcileAddonState() - Activating add-on: " + addon.id);
      AddonManager.addAddonListener(listener);
      addon.userDisabled = false;
    });
    changes |= this.ADDON_CHANGE_ENABLE;

    this._log.info("reconcileAddonState() - Add-on has been enabled: " + addon.id);
    return changes;
   },

  /**
   * Obtain the underlying Addon from the Addon Manager.
   *
   * @return Promise<Addon|null>
   */
  _getAddon() {
    if (!this._addonId) {
      return Promise.resolve(null);
    }

    return AddonManager.getAddonByID(this._addonId).then(addon => {
      if (addon && addon.appDisabled) {
        // Don't return PreviousExperiments.
        return null;
      }

      return addon;
    });
  },

  _logTermination(terminationKind, terminationReason) {
    if (terminationKind === undefined) {
      return;
    }

    if (!(terminationKind in TELEMETRY_LOG.TERMINATION)) {
      this._log.warn("stop() - unknown terminationKind " + terminationKind);
      return;
    }

    let data = [terminationKind, this.id];
    if (terminationReason) {
      data = data.concat(terminationReason);
    }

    TelemetryLog.log(TELEMETRY_LOG.TERMINATION_KEY, data);
  },

  /**
   * Determine whether an active experiment should be stopped.
   */
  shouldStop() {
    if (!this._enabled) {
      throw new Error("shouldStop must not be called on disabled experiments.");
    }

    return new Promise(resolve => {
      this.isApplicable().then(
        () => resolve({shouldStop: false}),
        reason => resolve({shouldStop: true, reason})
      );

    });
  },

  /*
   * Should this be discarded from the cache due to age?
   */
  shouldDiscard() {
    let limit = this._policy.now();
    limit.setDate(limit.getDate() - KEEP_HISTORY_N_DAYS);
    return (this._lastChangedDate < limit);
  },

  /*
   * Get next date (in epoch-ms) to schedule a re-evaluation for this.
   * Returns 0 if it doesn't need one.
   */
  getScheduleTime() {
    if (this._enabled) {
      let startTime = this._startDate.getTime();
      let maxActiveTime = startTime + 1000 * this._manifestData.maxActiveSeconds;
      return Math.min(1000 * this._manifestData.endTime, maxActiveTime);
    }

    if (this._endDate) {
      return this._endDate.getTime();
    }

    return 1000 * this._manifestData.startTime;
  },

  /*
   * Perform sanity checks on the experiment data.
   */
  _isManifestDataValid(data) {
    this._log.trace("isManifestDataValid() - data: " + JSON.stringify(data));

    for (let key of this.MANIFEST_REQUIRED_FIELDS) {
      if (!(key in data)) {
        this._log.error("isManifestDataValid() - missing required key: " + key);
        return false;
      }
    }

    for (let key in data) {
      if (!this.MANIFEST_OPTIONAL_FIELDS.has(key) &&
          !this.MANIFEST_REQUIRED_FIELDS.has(key)) {
        this._log.error("isManifestDataValid() - unknown key: " + key);
        return false;
      }
    }

    return true;
  },
};

/**
 * Strip a Date down to its UTC midnight.
 *
 * This will return a cloned Date object. The original is unchanged.
 */
var stripDateToMidnight = function(d) {
  let m = new Date(d);
  m.setUTCHours(0, 0, 0, 0);

  return m;
};

/**
 * An Add-ons Manager provider that knows about old experiments.
 *
 * This provider exposes read-only add-ons corresponding to previously-active
 * experiments. The existence of this provider (and the add-ons it knows about)
 * facilitates the display of old experiments in the Add-ons Manager UI with
 * very little custom code in that component.
 */
this.Experiments.PreviousExperimentProvider = function(experiments) {
  this._experiments = experiments;
  this._experimentList = [];
  this._log = Log.repository.getLoggerWithMessagePrefix(
    "Browser.Experiments.Experiments",
    "PreviousExperimentProvider #" + gPreviousProviderCounter++ + "::");
};

this.Experiments.PreviousExperimentProvider.prototype = Object.freeze({
  name: "PreviousExperimentProvider",

  startup() {
    this._log.trace("startup()");
    Services.obs.addObserver(this, EXPERIMENTS_CHANGED_TOPIC);
  },

  shutdown() {
    this._log.trace("shutdown()");
    try {
      Services.obs.removeObserver(this, EXPERIMENTS_CHANGED_TOPIC);
    } catch (e) {
      // Prevent crash in mochitest-browser3 on Mulet
    }
  },

  observe(subject, topic, data) {
    switch (topic) {
      case EXPERIMENTS_CHANGED_TOPIC:
        this._updateExperimentList();
        break;
    }
  },

  getAddonByID(id, cb) {
    for (let experiment of this._experimentList) {
      if (experiment.id == id) {
        cb(new PreviousExperimentAddon(experiment));
        return;
      }
    }

    cb(null);
  },

  getAddonsByTypes(types, cb) {
    if (types && types.length > 0 && !types.includes("experiment")) {
      cb([]);
      return;
    }

    cb(this._experimentList.map(e => new PreviousExperimentAddon(e)));
  },

  _updateExperimentList() {
    return this._experiments.getExperiments().then((experiments) => {
      let list = experiments.filter(e => !e.active);

      let newMap = new Map(list.map(e => [e.id, e]));
      let oldMap = new Map(this._experimentList.map(e => [e.id, e]));

      let added = [...newMap.keys()].filter(id => !oldMap.has(id));
      let removed = [...oldMap.keys()].filter(id => !newMap.has(id));

      for (let id of added) {
        this._log.trace("updateExperimentList() - adding " + id);
        let wrapper = new PreviousExperimentAddon(newMap.get(id));
        AddonManagerPrivate.callInstallListeners("onExternalInstall", null, wrapper, null, false);
        AddonManagerPrivate.callAddonListeners("onInstalling", wrapper, false);
      }

      for (let id of removed) {
        this._log.trace("updateExperimentList() - removing " + id);
        let wrapper = new PreviousExperimentAddon(oldMap.get(id));
        AddonManagerPrivate.callAddonListeners("onUninstalling", wrapper, false);
      }

      this._experimentList = list;

      for (let id of added) {
        let wrapper = new PreviousExperimentAddon(newMap.get(id));
        AddonManagerPrivate.callAddonListeners("onInstalled", wrapper);
      }

      for (let id of removed) {
        let wrapper = new PreviousExperimentAddon(oldMap.get(id));
        AddonManagerPrivate.callAddonListeners("onUninstalled", wrapper);
      }

      return this._experimentList;
    });
  },
});

/**
 * An add-on that represents a previously-installed experiment.
 */
function PreviousExperimentAddon(experiment) {
  this._id = experiment.id;
  this._name = experiment.name;
  this._endDate = experiment.endDate;
  this._description = experiment.description;
}

PreviousExperimentAddon.prototype = Object.freeze({
  // BEGIN REQUIRED ADDON PROPERTIES

  get appDisabled() {
    return true;
  },

  get blocklistState() {
    return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
  },

  get creator() {
    return new AddonManagerPrivate.AddonAuthor("");
  },

  get foreignInstall() {
    return false;
  },

  get id() {
    return this._id;
  },

  get isActive() {
    return false;
  },

  get isCompatible() {
    return true;
  },

  get isPlatformCompatible() {
    return true;
  },

  get name() {
    return this._name;
  },

  get pendingOperations() {
    return AddonManager.PENDING_NONE;
  },

  get permissions() {
    return 0;
  },

  get providesUpdatesSecurely() {
    return true;
  },

  get scope() {
    return AddonManager.SCOPE_PROFILE;
  },

  get type() {
    return "experiment";
  },

  get userDisabled() {
    return true;
  },

  get version() {
    return null;
  },

  // END REQUIRED PROPERTIES

  // BEGIN OPTIONAL PROPERTIES

  get description() {
    return this._description;
  },

  get updateDate() {
    return new Date(this._endDate);
  },

  // END OPTIONAL PROPERTIES

  // BEGIN REQUIRED METHODS

  isCompatibleWith(appVersion, platformVersion) {
    return true;
  },

  findUpdates(listener, reason, appVersion, platformVersion) {
    AddonManagerPrivate.callNoUpdateListeners(this, listener, reason,
                                              appVersion, platformVersion);
  },

  // END REQUIRED METHODS

  /**
   * The end-date of the experiment, required for the Addon Manager UI.
   */

   get endDate() {
     return this._endDate;
   },

});
PK
!<��Umodules/offlineAppCache.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

var EXPORTED_SYMBOLS = ["OfflineAppCacheHelper"];

ChromeUtils.import("resource://gre/modules/Services.jsm");

var OfflineAppCacheHelper = {
  clear() {
    var appCacheStorage = Services.cache2.appCacheStorage(Services.loadContextInfo.default, null);
    try {
      appCacheStorage.asyncEvictStorage(null);
    } catch (er) {}
  }
};
PK
!<bF{!&&&modules/policies/BookmarksPolicies.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Bookmark object received through the policy engine will be an
 * object with the following properties:
 *
 * - URL (nsIURI)
 *   (required) The URL for this bookmark
 *
 * - Title (string)
 *   (required) The title for this bookmark
 *
 * - Placement (string)
 *   (optional) Either "toolbar" or "menu". If missing or invalid,
 *   "toolbar" will be used
 *
 * - Folder (string)
 *   (optional) The name of the folder to put this bookmark into.
 *   If present, a folder with this name will be created in the
 *   chosen placement above, and the bookmark will be created there.
 *   If missing, the bookmark will be created directly into the
 *   chosen placement.
 *
 * - Favicon (nsIURI)
 *   (optional) An http:, https: or data: URL with the favicon.
 *   If possible, we recommend against using this property, in order
 *   to keep the json file small.
 *   If a favicon is not provided through the policy, it will be loaded
 *   naturally after the user first visits the bookmark.
 *
 *
 * Note: The Policy Engine automatically converts the strings given to
 * the URL and favicon properties into a nsIURI object.
 *
 * The schema for this object is defined in policies-schema.json.
 */

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");

ChromeUtils.defineModuleGetter(this, "PlacesUtils",
  "resource://gre/modules/PlacesUtils.jsm");

const PREF_LOGLEVEL = "browser.policies.loglevel";

XPCOMUtils.defineLazyGetter(this, "log", () => {
  let { ConsoleAPI } = ChromeUtils.import("resource://gre/modules/Console.jsm", {});
  return new ConsoleAPI({
    prefix: "BookmarksPolicies.jsm",
    // tip: set maxLogLevel to "debug" and use log.debug() to create detailed
    // messages during development. See LOG_LEVELS in Console.jsm for details.
    maxLogLevel: "error",
    maxLogLevelPref: PREF_LOGLEVEL,
  });
});

this.EXPORTED_SYMBOLS = [ "BookmarksPolicies" ];

this.BookmarksPolicies = {
  // These prefixes must only contain characters
  // allowed by PlacesUtils.isValidGuid
  BOOKMARK_GUID_PREFIX: "PolB-",
  FOLDER_GUID_PREFIX: "PolF-",

  /*
   * Process the bookmarks specified by the policy engine.
   *
   * @param param
   *        This will be an array of bookmarks objects, as
   *        described on the top of this file.
   */
  processBookmarks(param) {
    calculateLists(param).then(async function addRemoveBookmarks(results) {
      for (let bookmark of results.add.values()) {
        await insertBookmark(bookmark).catch(log.error);
      }
      for (let bookmark of results.remove.values()) {
        await PlacesUtils.bookmarks.remove(bookmark).catch(log.error);
      }
      for (let bookmark of results.emptyFolders.values()) {
        await PlacesUtils.bookmarks.remove(bookmark).catch(log.error);
      }

      gFoldersMapPromise.then(map => map.clear());
    });
  }
};

/*
 * This function calculates the differences between the existing bookmarks
 * that are managed by the policy engine (which are known through a guid
 * prefix) and the specified bookmarks in the policy file.
 * They can differ if the policy file has changed.
 *
 * @param specifiedBookmarks
 *        This will be an array of bookmarks objects, as
 *        described on the top of this file.
 */
async function calculateLists(specifiedBookmarks) {
  // --------- STEP 1 ---------
  // Build two Maps (one with the existing bookmarks, another with
  // the specified bookmarks), to make iteration quicker.

  // LIST A
  // MAP of url (string) -> bookmarks objects from the Policy Engine
  let specifiedBookmarksMap = new Map();
  for (let bookmark of specifiedBookmarks) {
    specifiedBookmarksMap.set(bookmark.URL.spec, bookmark);
  }

  // LIST B
  // MAP of url (string) -> bookmarks objects from Places
  let existingBookmarksMap = new Map();
  await PlacesUtils.bookmarks.fetch(
    { guidPrefix: BookmarksPolicies.BOOKMARK_GUID_PREFIX },
    (bookmark) => existingBookmarksMap.set(bookmark.url.href, bookmark)
  );

  // --------- STEP 2 ---------
  //
  //     /=====/====\=====\
  //    /     /      \     \
  //    |     |      |     |
  //    |  A  |  {}  |  B  |
  //    |     |      |     |
  //    \     \      /     /
  //     \=====\====/=====/
  //
  // Find the intersection of the two lists. Items in the intersection
  // are removed from the original lists.
  //
  // The items remaining in list A are new bookmarks to be added.
  // The items remaining in list B are old bookmarks to be removed.
  //
  // There's nothing to do with items in the intersection, so there's no
  // need to keep track of them.
  //
  // BONUS: It's necessary to keep track of the folder names that were
  // seen, to make sure we remove the ones that were left empty.

  let foldersSeen = new Set();

  for (let [url, item] of specifiedBookmarksMap) {
    foldersSeen.add(item.Folder);

    if (existingBookmarksMap.has(url)) {
      log.debug(`Bookmark intersection: ${url}`);
      // If this specified bookmark exists in the existing bookmarks list,
      // we can remove it from both lists as it's in the intersection.
      specifiedBookmarksMap.delete(url);
      existingBookmarksMap.delete(url);
    }
  }

  for (let url of specifiedBookmarksMap.keys()) {
    log.debug(`Bookmark to add: ${url}`);
  }

  for (let url of existingBookmarksMap.keys()) {
    log.debug(`Bookmark to remove: ${url}`);
  }

  // SET of folders to be deleted (bookmarks object from Places)
  let foldersToRemove = new Set();

  // If no bookmarks will be deleted, then no folder will
  // need to be deleted either, so this next section can be skipped.
  if (existingBookmarksMap.size > 0) {
    await PlacesUtils.bookmarks.fetch(
      { guidPrefix: BookmarksPolicies.FOLDER_GUID_PREFIX },
      (folder) => {
        if (!foldersSeen.has(folder.title)) {
          log.debug(`Folder to remove: ${folder.title}`);
          foldersToRemove.add(folder);
        }
      }
    );
  }

  return {
    add: specifiedBookmarksMap,
    remove: existingBookmarksMap,
    emptyFolders: foldersToRemove
  };
}

async function insertBookmark(bookmark) {
  let parentGuid = await getParentGuid(bookmark.Placement,
                                       bookmark.Folder);

  await PlacesUtils.bookmarks.insert({
    url: bookmark.URL,
    title: bookmark.Title,
    guid: generateGuidWithPrefix(BookmarksPolicies.BOOKMARK_GUID_PREFIX),
    parentGuid,
  });

  if (bookmark.Favicon) {
    await setFaviconForBookmark(bookmark).catch(
      () => log.error(`Error setting favicon for ${bookmark.Title}`));
  }
}

async function setFaviconForBookmark(bookmark) {
  let faviconURI;
  let nullPrincipal = Services.scriptSecurityManager.createNullPrincipal({});

  switch (bookmark.Favicon.scheme) {
    case "data":
      // data urls must first call replaceFaviconDataFromDataURL, using a
      // fake URL. Later, it's needed to call setAndFetchFaviconForPage
      // with the same URL.
      faviconURI = Services.io.newURI("fake-favicon-uri:" + bookmark.URL.spec);

      PlacesUtils.favicons.replaceFaviconDataFromDataURL(
        faviconURI,
        bookmark.Favicon.spec,
        0, /* max expiration length */
        nullPrincipal
      );
      break;

    case "http":
    case "https":
      faviconURI = bookmark.Favicon;
      break;

    default:
      log.error(`Bad URL given for favicon on bookmark "${bookmark.Title}"`);
      return Promise.resolve();
  }

  return new Promise(resolve => {
    PlacesUtils.favicons.setAndFetchFaviconForPage(
      bookmark.URL,
      faviconURI,
      false, /* forceReload */
      PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
      resolve,
      nullPrincipal
    );
  });
}

function generateGuidWithPrefix(prefix) {
  // Generates a random GUID and replace its beginning with the given
  // prefix. We do this instead of just prepending the prefix to keep
  // the correct character length.
  return prefix + PlacesUtils.history.makeGuid().substring(prefix.length);
}

// Cache of folder names to guids to be used by the getParentGuid
// function. The name consists in the parentGuid (which should always
// be the menuGuid or the toolbarGuid) + the folder title. This is to
// support having the same folder name in both the toolbar and menu.
XPCOMUtils.defineLazyGetter(this, "gFoldersMapPromise", () => {
  return new Promise(resolve => {
    let foldersMap = new Map();
    return PlacesUtils.bookmarks.fetch(
      {
        guidPrefix: BookmarksPolicies.FOLDER_GUID_PREFIX
      },
      (result) => {
        foldersMap.set(`${result.parentGuid}|${result.title}`, result.guid);
      }
    ).then(() => resolve(foldersMap));
  });
});

async function getParentGuid(placement, folderTitle) {
  // Defaults to toolbar if no placement was given.
  let parentGuid = (placement == "menu") ?
                   PlacesUtils.bookmarks.menuGuid :
                   PlacesUtils.bookmarks.toolbarGuid;

  if (!folderTitle) {
    // If no folderTitle is given, this bookmark is to be placed directly
    // into the toolbar or menu.
    return parentGuid;
  }

  let foldersMap = await gFoldersMapPromise;
  let folderName = `${parentGuid}|${folderTitle}`;

  if (foldersMap.has(folderName)) {
    return foldersMap.get(folderName);
  }

  let guid = generateGuidWithPrefix(BookmarksPolicies.FOLDER_GUID_PREFIX);
  await PlacesUtils.bookmarks.insert({
    type: PlacesUtils.bookmarks.TYPE_FOLDER,
    title: folderTitle,
    guid,
    parentGuid
  });

  foldersMap.set(folderName, guid);
  return guid;
}
PK
!<��N�p�pmodules/policies/Policies.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "gXulStore",
                                   "@mozilla.org/xul/xulstore;1",
                                   "nsIXULStore");

XPCOMUtils.defineLazyModuleGetters(this, {
  AddonManager: "resource://gre/modules/AddonManager.jsm",
  BookmarksPolicies: "resource:///modules/policies/BookmarksPolicies.jsm",
  CustomizableUI: "resource:///modules/CustomizableUI.jsm",
  ProxyPolicies: "resource:///modules/policies/ProxyPolicies.jsm",
  WebsiteFilter: "resource:///modules/policies/WebsiteFilter.jsm",
});

const PREF_LOGLEVEL           = "browser.policies.loglevel";
const BROWSER_DOCUMENT_URL    = "chrome://browser/content/browser.xul";

XPCOMUtils.defineLazyGetter(this, "log", () => {
  let { ConsoleAPI } = ChromeUtils.import("resource://gre/modules/Console.jsm", {});
  return new ConsoleAPI({
    prefix: "Policies.jsm",
    // tip: set maxLogLevel to "debug" and use log.debug() to create detailed
    // messages during development. See LOG_LEVELS in Console.jsm for details.
    maxLogLevel: "error",
    maxLogLevelPref: PREF_LOGLEVEL,
  });
});

var EXPORTED_SYMBOLS = ["Policies"];

/*
 * ============================
 * = POLICIES IMPLEMENTATIONS =
 * ============================
 *
 * The Policies object below is where the implementation for each policy
 * happens. An object for each policy should be defined, containing
 * callback functions that will be called by the engine.
 *
 * See the _callbacks object in EnterprisePolicies.js for the list of
 * possible callbacks and an explanation of each.
 *
 * Each callback will be called with two parameters:
 * - manager
 *   This is the EnterprisePoliciesManager singleton object from
 *   EnterprisePolicies.js
 *
 * - param
 *   The parameter defined for this policy in policies-schema.json.
 *   It will be different for each policy. It could be a boolean,
 *   a string, an array or a complex object. All parameters have
 *   been validated according to the schema, and no unknown
 *   properties will be present on them.
 *
 * The callbacks will be bound to their parent policy object.
 */
var Policies = {
  "Authentication": {
    onBeforeAddons(manager, param) {
      if ("SPNEGO" in param) {
        setAndLockPref("network.negotiate-auth.trusted-uris", param.SPNEGO.join(", "));
      }
      if ("Delegated" in param) {
        setAndLockPref("network.negotiate-auth.delegation-uris", param.Delegated.join(", "));
      }
      if ("NTLM" in param) {
        setAndLockPref("network.automatic-ntlm-auth.trusted-uris", param.NTLM.join(", "));
      }
    }
  },

  "BlockAboutAddons": {
    onBeforeUIStartup(manager, param) {
      if (param) {
        blockAboutPage(manager, "about:addons", true);
      }
    }
  },

  "BlockAboutConfig": {
    onBeforeUIStartup(manager, param) {
      if (param) {
        blockAboutPage(manager, "about:config", true);
        setAndLockPref("devtools.chrome.enabled", false);
      }
    }
  },

  "BlockAboutProfiles": {
    onBeforeUIStartup(manager, param) {
      if (param) {
        blockAboutPage(manager, "about:profiles", true);
      }
    }
  },

  "BlockAboutSupport": {
    onBeforeUIStartup(manager, param) {
      if (param) {
        blockAboutPage(manager, "about:support", true);
      }
    }
  },

  "Bookmarks": {
    onAllWindowsRestored(manager, param) {
      BookmarksPolicies.processBookmarks(param);
    }
  },

  "Certificates": {
    onBeforeAddons(manager, param) {
      if ("ImportEnterpriseRoots" in param) {
        setAndLockPref("security.enterprise_roots.enabled", true);
      }
    }
  },

  "Cookies": {
    onBeforeUIStartup(manager, param) {
      addAllowDenyPermissions("cookie", param.Allow, param.Block);

      if (param.Block) {
        const hosts = param.Block.map(uri => uri.host).sort().join("\n");
        runOncePerModification("clearCookiesForBlockedHosts", hosts, () => {
          for (let blocked of param.Block) {
            Services.cookies.removeCookiesWithOriginAttributes("{}", blocked.host);
          }
        });
      }

      if (param.Default !== undefined ||
          param.AcceptThirdParty !== undefined ||
          param.Locked) {
        const ACCEPT_COOKIES = 0;
        const REJECT_THIRD_PARTY_COOKIES = 1;
        const REJECT_ALL_COOKIES = 2;
        const REJECT_UNVISITED_THIRD_PARTY = 3;

        let newCookieBehavior = ACCEPT_COOKIES;
        if (param.Default !== undefined && !param.Default) {
          newCookieBehavior = REJECT_ALL_COOKIES;
        } else if (param.AcceptThirdParty) {
          if (param.AcceptThirdParty == "never") {
            newCookieBehavior = REJECT_THIRD_PARTY_COOKIES;
          } else if (param.AcceptThirdParty == "from-visited") {
            newCookieBehavior = REJECT_UNVISITED_THIRD_PARTY;
          }
        }

        if (param.Locked) {
          setAndLockPref("network.cookie.cookieBehavior", newCookieBehavior);
        } else {
          setDefaultPref("network.cookie.cookieBehavior", newCookieBehavior);
        }
      }

      const KEEP_COOKIES_UNTIL_EXPIRATION = 0;
      const KEEP_COOKIES_UNTIL_END_OF_SESSION = 2;

      if (param.ExpireAtSessionEnd !== undefined || param.Locked) {
        let newLifetimePolicy = KEEP_COOKIES_UNTIL_EXPIRATION;
        if (param.ExpireAtSessionEnd) {
          newLifetimePolicy = KEEP_COOKIES_UNTIL_END_OF_SESSION;
        }

        if (param.Locked) {
          setAndLockPref("network.cookie.lifetimePolicy", newLifetimePolicy);
        } else {
          setDefaultPref("network.cookie.lifetimePolicy", newLifetimePolicy);
        }
      }
    }
  },

  "DisableAppUpdate": {
    onBeforeAddons(manager, param) {
      if (param) {
        manager.disallowFeature("appUpdate");
      }
    }
  },

  "DisableBuiltinPDFViewer": {
    onBeforeUIStartup(manager, param) {
      if (param) {
        manager.disallowFeature("PDF.js");
      }
    }
  },

  "DisableDeveloperTools": {
    onBeforeAddons(manager, param) {
      if (param) {
        setAndLockPref("devtools.policy.disabled", true);
        setAndLockPref("devtools.chrome.enabled", false);

        manager.disallowFeature("devtools");
        blockAboutPage(manager, "about:devtools");
        blockAboutPage(manager, "about:debugging");
        blockAboutPage(manager, "about:devtools-toolbox");
      }
    }
  },

  "DisableFeedbackCommands": {
    onBeforeUIStartup(manager, param) {
      if (param) {
        manager.disallowFeature("feedbackCommands");
      }
    }
  },

  "DisableFirefoxAccounts": {
    onBeforeAddons(manager, param) {
      if (param) {
        setAndLockPref("identity.fxaccounts.enabled", false);
      }
    }
  },

  "DisableFirefoxScreenshots": {
    onBeforeAddons(manager, param) {
      if (param) {
        setAndLockPref("extensions.screenshots.disabled", true);
      }
    }
  },

  "DisableFirefoxStudies": {
    onBeforeAddons(manager, param) {
      if (param) {
        manager.disallowFeature("Shield");
      }
    }
  },

  "DisableForgetButton": {
    onProfileAfterChange(manager, param) {
      if (param) {
        setAndLockPref("privacy.panicButton.enabled", false);
      }
    }
  },

  "DisableFormHistory": {
    onBeforeUIStartup(manager, param) {
      if (param) {
        setAndLockPref("browser.formfill.enable", false);
      }
    }
  },

  "DisableMasterPasswordCreation": {
    onBeforeUIStartup(manager, param) {
      if (param) {
        manager.disallowFeature("createMasterPassword");
      }
    }
  },

  "DisablePocket": {
    onBeforeAddons(manager, param) {
      if (param) {
        setAndLockPref("extensions.pocket.enabled", false);
      }
    }
  },

  "DisablePrivateBrowsing": {
    onBeforeAddons(manager, param) {
      if (param) {
        manager.disallowFeature("privatebrowsing");
        blockAboutPage(manager, "about:privatebrowsing", true);
        setAndLockPref("browser.privatebrowsing.autostart", false);
      }
    }
  },

  "DisableProfileImport": {
    onBeforeUIStartup(manager, param) {
      if (param) {
        manager.disallowFeature("profileImport");
        setAndLockPref("browser.newtabpage.activity-stream.migrationExpired", true);
      }
    }
  },

  "DisableProfileRefresh": {
    onBeforeUIStartup(manager, param) {
      if (param) {
        manager.disallowFeature("profileRefresh");
        setAndLockPref("browser.disableResetPrompt", true);
      }
    }
  },

  "DisableSafeMode": {
    onBeforeUIStartup(manager, param) {
      if (param) {
        manager.disallowFeature("safeMode");
      }
    }
  },

  "DisableSecurityBypass": {
    onBeforeUIStartup(manager, param) {
      if ("InvalidCertificate" in param) {
        setAndLockPref("security.certerror.hideAddException", param.InvalidCertificate);
      }

      if ("SafeBrowsing" in param) {
        setAndLockPref("browser.safebrowsing.allowOverride", !param.SafeBrowsing);
      }
    }
  },

  "DisableSetDesktopBackground": {
    onBeforeUIStartup(manager, param) {
      if (param) {
        manager.disallowFeature("setDesktopBackground", true);
      }
    }
  },

  "DisableSystemAddonUpdate": {
    onBeforeAddons(manager, param) {
      if (param) {
        manager.disallowFeature("SysAddonUpdate");
      }
    }
  },

  "DisableTelemetry": {
    onBeforeAddons(manager, param) {
      if (param) {
        setAndLockPref("datareporting.healthreport.uploadEnabled", false);
        setAndLockPref("datareporting.policy.dataSubmissionEnabled", false);
        blockAboutPage(manager, "about:telemetry");
      }
    }
  },

  "DisplayBookmarksToolbar": {
    onBeforeUIStartup(manager, param) {
      let value = (!param).toString();
      // This policy is meant to change the default behavior, not to force it.
      // If this policy was alreay applied and the user chose to re-hide the
      // bookmarks toolbar, do not show it again.
      runOncePerModification("displayBookmarksToolbar", value, () => {
        gXulStore.setValue(BROWSER_DOCUMENT_URL, "PersonalToolbar", "collapsed", value);
      });
    }
  },

  "DisplayMenuBar": {
    onBeforeUIStartup(manager, param) {
      let value = (!param).toString();
        // This policy is meant to change the default behavior, not to force it.
        // If this policy was alreay applied and the user chose to re-hide the
        // menu bar, do not show it again.
      runOncePerModification("displayMenuBar", value, () => {
        gXulStore.setValue(BROWSER_DOCUMENT_URL, "toolbar-menubar", "autohide", value);
      });
    }
  },

  "DontCheckDefaultBrowser": {
    onBeforeUIStartup(manager, param) {
      setAndLockPref("browser.shell.checkDefaultBrowser", false);
    }
  },

  "EnableTrackingProtection": {
    onBeforeUIStartup(manager, param) {
      if (param.Value) {
        if (param.Locked) {
          setAndLockPref("privacy.trackingprotection.enabled", true);
          setAndLockPref("privacy.trackingprotection.pbmode.enabled", true);
        } else {
          setDefaultPref("privacy.trackingprotection.enabled", true);
          setDefaultPref("privacy.trackingprotection.pbmode.enabled", true);
        }
      } else {
        setAndLockPref("privacy.trackingprotection.enabled", false);
        setAndLockPref("privacy.trackingprotection.pbmode.enabled", false);
      }
    }
  },

  "Extensions": {
    onBeforeUIStartup(manager, param) {
      if ("Install" in param) {
        runOncePerModification("extensionsInstall", JSON.stringify(param.Install), () => {
          for (let location of param.Install) {
            let url;
            if (location.includes("://")) {
              // Assume location is an URI
              url = location;
            } else {
              // Assume location is a file path
              let xpiFile = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
              try {
                xpiFile.initWithPath(location);
              } catch (e) {
                log.error(`Invalid extension path location - ${location}`);
                continue;
              }
              url = Services.io.newFileURI(xpiFile).spec;
            }
            AddonManager.getInstallForURL(url, null, "application/x-xpinstall").then(install => {
              if (install.addon && install.addon.appDisabled) {
                log.error(`Incompatible add-on - ${location}`);
                install.cancel();
                return;
              }
              let listener = {
              /* eslint-disable-next-line no-shadow */
                onDownloadEnded: (install) => {
                  if (install.addon && install.addon.appDisabled) {
                    log.error(`Incompatible add-on - ${location}`);
                    install.removeListener(listener);
                    install.cancel();
                  }
                },
                onDownloadFailed: () => {
                  install.removeListener(listener);
                  log.error(`Download failed - ${location}`);
                },
                onInstallFailed: () => {
                  install.removeListener(listener);
                  log.error(`Installation failed - ${location}`);
                },
                onInstallEnded: () => {
                  install.removeListener(listener);
                  log.debug(`Installation succeeded - ${location}`);
                }
              };
              install.addListener(listener);
              install.install();
            });
          }
        });
      }
      if ("Uninstall" in param) {
        runOncePerModification("extensionsUninstall", JSON.stringify(param.Uninstall), () => {
          AddonManager.getAddonsByIDs(param.Uninstall, (addons) => {
            for (let addon of addons) {
              if (addon) {
                try {
                  addon.uninstall();
                } catch (e) {
                  // This can fail for add-ons that can't be uninstalled.
                  // Just ignore.
                }
              }
            }
          });
        });
      }
      if ("Locked" in param) {
        for (let ID of param.Locked) {
          manager.disallowFeature(`modify-extension:${ID}`);
        }
      }
    }
  },

  "FlashPlugin": {
    onBeforeUIStartup(manager, param) {
      addAllowDenyPermissions("plugin:flash", param.Allow, param.Block);

      const FLASH_NEVER_ACTIVATE = 0;
      const FLASH_ASK_TO_ACTIVATE = 1;
      const FLASH_ALWAYS_ACTIVATE = 2;

      let flashPrefVal;
      if (param.Default === undefined) {
        flashPrefVal = FLASH_ASK_TO_ACTIVATE;
      } else if (param.Default) {
        flashPrefVal = FLASH_ALWAYS_ACTIVATE;
      } else {
        flashPrefVal = FLASH_NEVER_ACTIVATE;
      }
      if (param.Locked) {
        setAndLockPref("plugin.state.flash", flashPrefVal);
      } else if (param.Default !== undefined) {
        setDefaultPref("plugin.state.flash", flashPrefVal);
      }
    }
  },

  "Homepage": {
    onBeforeUIStartup(manager, param) {
      // |homepages| will be a string containing a pipe-separated ('|') list of
      // URLs because that is what the "Home page" section of about:preferences
      // (and therefore what the pref |browser.startup.homepage|) accepts.
      let homepages = param.URL.spec;
      if (param.Additional && param.Additional.length > 0) {
        homepages += "|" + param.Additional.map(url => url.spec).join("|");
      }
      if (param.Locked) {
        setAndLockPref("browser.startup.homepage", homepages);
        setAndLockPref("browser.startup.page", 1);
        setAndLockPref("pref.browser.homepage.disable_button.current_page", true);
        setAndLockPref("pref.browser.homepage.disable_button.bookmark_page", true);
        setAndLockPref("pref.browser.homepage.disable_button.restore_default", true);
      } else {
        // The default pref for homepage is actually a complex pref. We need to
        // set it in a special way such that it works properly
        let homepagePrefVal = "data:text/plain,browser.startup.homepage=" +
                               homepages;
        setDefaultPref("browser.startup.homepage", homepagePrefVal);
        setDefaultPref("browser.startup.page", 1);
        runOncePerModification("setHomepage", homepages, () => {
          Services.prefs.clearUserPref("browser.startup.homepage");
          Services.prefs.clearUserPref("browser.startup.page");
        });
      }
    }
  },

  "InstallAddonsPermission": {
    onBeforeUIStartup(manager, param) {
      if ("Allow" in param) {
        addAllowDenyPermissions("install", param.Allow, null);
      }
      if ("Default" in param) {
        setAndLockPref("xpinstall.enabled", param.Default);
        if (!param.Default) {
          blockAboutPage(manager, "about:debugging");
        }
      }
    }
  },

  "NoDefaultBookmarks": {
    onProfileAfterChange(manager, param) {
      if (param) {
        manager.disallowFeature("defaultBookmarks");
      }
    }
  },

  "OfferToSaveLogins": {
    onBeforeUIStartup(manager, param) {
      setAndLockPref("signon.rememberSignons", param);
    }
  },

  "OverrideFirstRunPage": {
    onProfileAfterChange(manager, param) {
      let url = param ? param.spec : "";
      setAndLockPref("startup.homepage_welcome_url", url);
    }
  },

  "OverridePostUpdatePage": {
    onProfileAfterChange(manager, param) {
      let url = param ? param.spec : "";
      setAndLockPref("startup.homepage_override_url", url);
      // The pref startup.homepage_override_url is only used
      // as a fallback when the update.xml file hasn't provided
      // a specific post-update URL.
      manager.disallowFeature("postUpdateCustomPage");
    }
  },

  "PopupBlocking": {
    onBeforeUIStartup(manager, param) {
      addAllowDenyPermissions("popup", param.Allow, null);

      if (param.Locked) {
        let blockValue = true;
        if (param.Default !== undefined && !param.Default) {
          blockValue = false;
        }
        setAndLockPref("dom.disable_open_during_load", blockValue);
      } else if (param.Default !== undefined) {
        setDefaultPref("dom.disable_open_during_load", !!param.Default);
      }
    }
  },

  "Proxy": {
    onBeforeAddons(manager, param) {
      if (param.Locked) {
        manager.disallowFeature("changeProxySettings");
        ProxyPolicies.configureProxySettings(param, setAndLockPref);
      } else {
        ProxyPolicies.configureProxySettings(param, setDefaultPref);
      }
    }
  },

  "SanitizeOnShutdown": {
    onBeforeUIStartup(manager, param) {
      setAndLockPref("privacy.sanitize.sanitizeOnShutdown", param);
      if (param) {
        setAndLockPref("privacy.clearOnShutdown.cache", true);
        setAndLockPref("privacy.clearOnShutdown.cookies", true);
        setAndLockPref("privacy.clearOnShutdown.downloads", true);
        setAndLockPref("privacy.clearOnShutdown.formdata", true);
        setAndLockPref("privacy.clearOnShutdown.history", true);
        setAndLockPref("privacy.clearOnShutdown.sessions", true);
        setAndLockPref("privacy.clearOnShutdown.siteSettings", true);
        setAndLockPref("privacy.clearOnShutdown.offlineApps", true);
      }
    }
  },

  "SearchBar": {
    onAllWindowsRestored(manager, param) {
      // This policy is meant to change the default behavior, not to force it.
      // If this policy was already applied and the user chose move the search
      // bar, don't move it again.
      runOncePerModification("searchInNavBar", param, () => {
        if (param == "separate") {
          CustomizableUI.addWidgetToArea("search-container", CustomizableUI.AREA_NAVBAR,
          CustomizableUI.getPlacementOfWidget("urlbar-container").position + 1);
        } else if (param == "unified") {
          CustomizableUI.removeWidgetFromArea("search-container");
        }
      });
    }
  },

  "SearchEngines": {
    onBeforeUIStartup(manager, param) {
      if (param.PreventInstalls) {
        manager.disallowFeature("installSearchEngine", true);
      }
    },
    onAllWindowsRestored(manager, param) {
      Services.search.init(() => {
        if (param.Add) {
          // Only rerun if the list of engine names has changed.
          let engineNameList = param.Add.map(engine => engine.Name);
          runOncePerModification("addSearchEngines",
                                 JSON.stringify(engineNameList),
                                 () => {
            for (let newEngine of param.Add) {
              let newEngineParameters = {
                template:    newEngine.URLTemplate,
                iconURL:     newEngine.IconURL,
                alias:       newEngine.Alias,
                description: newEngine.Description,
                method:      newEngine.Method,
                suggestURL:  newEngine.SuggestURLTemplate,
                extensionID: "set-via-policy"
              };
              try {
                Services.search.addEngineWithDetails(newEngine.Name,
                                                     newEngineParameters);
              } catch (ex) {
                log.error("Unable to add search engine", ex);
              }
            }
          });
        }
        if (param.Default) {
          runOnce("setDefaultSearchEngine", () => {
            let defaultEngine;
            try {
              defaultEngine = Services.search.getEngineByName(param.Default);
              if (!defaultEngine) {
                throw "No engine by that name could be found";
              }
            } catch (ex) {
              log.error(`Search engine lookup failed when attempting to set ` +
                        `the default engine. Requested engine was ` +
                        `"${param.Default}".`, ex);
            }
            if (defaultEngine) {
              try {
                Services.search.currentEngine = defaultEngine;
              } catch (ex) {
                log.error("Unable to set the default search engine", ex);
              }
            }
          });
        }
      });
    }
  },

  "WebsiteFilter": {
    onBeforeUIStartup(manager, param) {
      this.filter = new WebsiteFilter(param.Block || [], param.Exceptions || []);
    }
  },

};

/*
 * ====================
 * = HELPER FUNCTIONS =
 * ====================
 *
 * The functions below are helpers to be used by several policies.
 */

/**
 * setAndLockPref
 *
 * Sets the _default_ value of a pref, and locks it (meaning that
 * the default value will always be returned, independent from what
 * is stored as the user value).
 * The value is only changed in memory, and not stored to disk.
 *
 * @param {string} prefName
 *        The pref to be changed
 * @param {boolean,number,string} prefValue
 *        The value to set and lock
 */
function setAndLockPref(prefName, prefValue) {
  if (Services.prefs.prefIsLocked(prefName)) {
    Services.prefs.unlockPref(prefName);
  }

  setDefaultPref(prefName, prefValue);

  Services.prefs.lockPref(prefName);
}

/**
 * setDefaultPref
 *
 * Sets the _default_ value of a pref.
 * The value is only changed in memory, and not stored to disk.
 *
 * @param {string} prefName
 *        The pref to be changed
 * @param {boolean,number,string} prefValue
 *        The value to set
 */
function setDefaultPref(prefName, prefValue) {
  let defaults = Services.prefs.getDefaultBranch("");

  switch (typeof(prefValue)) {
    case "boolean":
      defaults.setBoolPref(prefName, prefValue);
      break;

    case "number":
      if (!Number.isInteger(prefValue)) {
        throw new Error(`Non-integer value for ${prefName}`);
      }

      defaults.setIntPref(prefName, prefValue);
      break;

    case "string":
      defaults.setStringPref(prefName, prefValue);
      break;
  }
}

/**
 * addAllowDenyPermissions
 *
 * Helper function to call the permissions manager (Services.perms.add)
 * for two arrays of URLs.
 *
 * @param {string} permissionName
 *        The name of the permission to change
 * @param {array} allowList
 *        The list of URLs to be set as ALLOW_ACTION for the chosen permission.
 * @param {array} blockList
 *        The list of URLs to be set as DENY_ACTION for the chosen permission.
 */
function addAllowDenyPermissions(permissionName, allowList, blockList) {
  allowList = allowList || [];
  blockList = blockList || [];

  for (let origin of allowList) {
    Services.perms.add(origin,
                       permissionName,
                       Ci.nsIPermissionManager.ALLOW_ACTION,
                       Ci.nsIPermissionManager.EXPIRE_POLICY);
  }

  for (let origin of blockList) {
    Services.perms.add(origin,
                       permissionName,
                       Ci.nsIPermissionManager.DENY_ACTION,
                       Ci.nsIPermissionManager.EXPIRE_POLICY);
  }
}

/**
 * runOnce
 *
 * Helper function to run a callback only once per policy.
 *
 * @param {string} actionName
 *        A given name which will be used to track if this callback has run.
 * @param {Functon} callback
 *        The callback to run only once.
 */
 // eslint-disable-next-line no-unused-vars
function runOnce(actionName, callback) {
  let prefName = `browser.policies.runonce.${actionName}`;
  if (Services.prefs.getBoolPref(prefName, false)) {
    log.debug(`Not running action ${actionName} again because it has already run.`);
    return;
  }
  Services.prefs.setBoolPref(prefName, true);
  callback();
}

/**
 * runOncePerModification
 *
 * Helper function similar to runOnce. The difference is that runOnce runs the
 * callback once when the policy is set, then never again.
 * runOncePerModification runs the callback once each time the policy value
 * changes from its previous value.
 *
 * @param {string} actionName
 *        A given name which will be used to track if this callback has run.
 *        This string will be part of a pref name.
 * @param {string} policyValue
 *        The current value of the policy. This will be compared to previous
 *        values given to this function to determine if the policy value has
 *        changed. Regardless of the data type of the policy, this must be a
 *        string.
 * @param {Function} callback
 *        The callback to be run when the pref value changes
 */
function runOncePerModification(actionName, policyValue, callback) {
  let prefName = `browser.policies.runOncePerModification.${actionName}`;
  let oldPolicyValue = Services.prefs.getStringPref(prefName, undefined);
  if (policyValue === oldPolicyValue) {
    log.debug(`Not running action ${actionName} again because the policy's value is unchanged`);
    return;
  }
  Services.prefs.setStringPref(prefName, policyValue);
  callback();
}

let gChromeURLSBlocked = false;

// If any about page is blocked, we block the loading of all
// chrome:// URLs in the browser window.
function blockAboutPage(manager, feature, neededOnContentProcess = false) {
  manager.disallowFeature(feature, neededOnContentProcess);
  if (!gChromeURLSBlocked) {
    blockAllChromeURLs();
    gChromeURLSBlocked = true;
  }
}

let ChromeURLBlockPolicy = {
  shouldLoad(aContentType, aContentLocation, aRequestOrigin, aContext, aMimeTypeGuess, aExtra) {
    if (aContentLocation.scheme == "chrome" &&
        aContentType == Ci.nsIContentPolicy.TYPE_DOCUMENT &&
        aRequestOrigin &&
        aRequestOrigin.spec == "chrome://browser/content/browser.xul" &&
        aContentLocation.host != "mochitests") {
      return Ci.nsIContentPolicy.REJECT_REQUEST;
    }
    return Ci.nsIContentPolicy.ACCEPT;
  },
  shouldProcess(aContentType, aContentLocation, aRequestOrigin, aContext, aMimeTypeGuess, aExtra) {
    return Ci.nsIContentPolicy.ACCEPT;
  },
  classDescription: "Policy Engine Content Policy",
  contractID: "@mozilla-org/policy-engine-content-policy-service;1",
  classID: Components.ID("{ba7b9118-cabc-4845-8b26-4215d2a59ed7}"),
  QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPolicy]),
  createInstance(outer, iid) {
    return this.QueryInterface(iid);
  },
};


function blockAllChromeURLs() {
  let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
  registrar.registerFactory(ChromeURLBlockPolicy.classID,
                            ChromeURLBlockPolicy.classDescription,
                            ChromeURLBlockPolicy.contractID,
                            ChromeURLBlockPolicy);

  let cm = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
  cm.addCategoryEntry("content-policy", ChromeURLBlockPolicy.contractID, ChromeURLBlockPolicy.contractID, false, true);
}
PK
!<$���&modules/policies/PoliciesValidator.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");

const PREF_LOGLEVEL           = "browser.policies.loglevel";

XPCOMUtils.defineLazyGetter(this, "log", () => {
  let { ConsoleAPI } = ChromeUtils.import("resource://gre/modules/Console.jsm", {});
  return new ConsoleAPI({
    prefix: "PoliciesValidator.jsm",
    // tip: set maxLogLevel to "debug" and use log.debug() to create detailed
    // messages during development. See LOG_LEVELS in Console.jsm for details.
    maxLogLevel: "error",
    maxLogLevelPref: PREF_LOGLEVEL,
  });
});

var EXPORTED_SYMBOLS = ["PoliciesValidator"];

var PoliciesValidator = {
  validateAndParseParameters(param, properties) {
    return validateAndParseParamRecursive(param, properties);
  }
};

function validateAndParseParamRecursive(param, properties) {
  if (properties.enum) {
    if (properties.enum.includes(param)) {
      return [true, param];
    }
    return [false, null];
  }

  log.debug(`checking @${param}@ for type ${properties.type}`);
  switch (properties.type) {
    case "boolean":
    case "number":
    case "integer":
    case "string":
    case "URL":
    case "URLorEmpty":
    case "origin":
      return validateAndParseSimpleParam(param, properties.type);

    case "array":
      if (!Array.isArray(param)) {
        log.error("Array expected but not received");
        return [false, null];
      }

      let parsedArray = [];
      for (let item of param) {
        log.debug(`in array, checking @${item}@ for type ${properties.items.type}`);
        let [valid, parsedValue] = validateAndParseParamRecursive(item, properties.items);
        if (!valid) {
          return [false, null];
        }

        parsedArray.push(parsedValue);
      }

      return [true, parsedArray];

    case "object": {
      if (typeof(param) != "object") {
        log.error("Object expected but not received");
        return [false, null];
      }

      let parsedObj = {};
      for (let property of Object.keys(properties.properties)) {
        log.debug(`in object, checking\n    property: ${property}\n    value: ${param[property]}\n    expected type: ${properties.properties[property].type}`);

        if (!param.hasOwnProperty(property)) {
          if (properties.required && properties.required.includes(property)) {
            log.error(`Object is missing required property ${property}`);
            return [false, null];
          }
          continue;
        }

        let [valid, parsedValue] = validateAndParseParamRecursive(param[property], properties.properties[property]);

        if (!valid) {
          return [false, null];
        }

        parsedObj[property] = parsedValue;
      }

      return [true, parsedObj];
    }
  }

  return [false, null];
}

function validateAndParseSimpleParam(param, type) {
  let valid = false;
  let parsedParam = param;

  switch (type) {
    case "boolean":
      if (typeof(param) == "boolean") {
        valid = true;
      } else if (typeof(param) == "number" &&
                 (param == 0 || param == 1)) {
        valid = true;
        parsedParam = !!param;
      }
      break;

    case "number":
    case "string":
      valid = (typeof(param) == type);
      break;

    // integer is an alias to "number" that some JSON schema tools use
    case "integer":
      valid = (typeof(param) == "number");
      break;

    case "origin":
      if (typeof(param) != "string") {
        break;
      }

      try {
        parsedParam = Services.io.newURI(param);

        let pathQueryRef = parsedParam.pathQueryRef;
        // Make sure that "origin" types won't accept full URLs.
        if (pathQueryRef != "/" && pathQueryRef != "") {
          valid = false;
        } else {
          valid = true;
        }
      } catch (ex) {
        valid = false;
      }
      break;

    case "URL":
    case "URLorEmpty":
      if (typeof(param) != "string") {
        break;
      }

      if (type == "URLorEmpty" && param === "") {
        valid = true;
        break;
      }

      try {
        parsedParam = Services.io.newURI(param);
        valid = true;
      } catch (ex) {
        valid = false;
      }
      break;
  }

  return [valid, parsedParam];
}
PK
!<�bF�
�
"modules/policies/ProxyPolicies.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");

Cu.importGlobalProperties(["URL"]);

const PREF_LOGLEVEL = "browser.policies.loglevel";

XPCOMUtils.defineLazyGetter(this, "log", () => {
  let { ConsoleAPI } = ChromeUtils.import("resource://gre/modules/Console.jsm", {});
  return new ConsoleAPI({
    prefix: "ProxyPolicies.jsm",
    // tip: set maxLogLevel to "debug" and use log.debug() to create detailed
    // messages during development. See LOG_LEVELS in Console.jsm for details.
    maxLogLevel: "error",
    maxLogLevelPref: PREF_LOGLEVEL,
  });
});

// Don't use const here because this is acessed by
// tests through the BackstagePass object.
var PROXY_TYPES_MAP = new Map([
  ["none", Ci.nsIProtocolProxyService.PROXYCONFIG_DIRECT],
  ["system", Ci.nsIProtocolProxyService.PROXYCONFIG_SYSTEM],
  ["manual", Ci.nsIProtocolProxyService.PROXYCONFIG_MANUAL],
  ["autoDetect", Ci.nsIProtocolProxyService.PROXYCONFIG_WPAD],
  ["autoConfig", Ci.nsIProtocolProxyService.PROXYCONFIG_PAC],
]);

var EXPORTED_SYMBOLS = [ "ProxyPolicies" ];

var ProxyPolicies = {
  configureProxySettings(param, setPref) {
    if (param.Mode) {
      setPref("network.proxy.type", PROXY_TYPES_MAP.get(param.Mode));
    }

    if (param.AutoConfigURL) {
      setPref("network.proxy.autoconfig_url", param.AutoConfigURL.spec);
    }

    if (param.UseProxyForDNS !== undefined) {
      setPref("network.proxy.socks_remote_dns", param.UseProxyForDNS);
    }

    if (param.AutoLogin !== undefined) {
      setPref("signon.autologin.proxy", param.AutoLogin);
    }

    if (param.SOCKSVersion !== undefined) {
      if (param.SOCKSVersion != 4 && param.SOCKSVersion != 5) {
        log.error("Invalid SOCKS version");
      } else {
        setPref("network.proxy.socks_version", param.SOCKSVersion);
      }
    }

    if (param.Passthrough !== undefined) {
      setPref("network.proxy.no_proxies_on", param.Passthrough);
    }

    if (param.UseHTTPProxyForAllProtocols !== undefined) {
      setPref("network.proxy.share_proxy_settings", param.UseHTTPProxyForAllProtocols);
    }

    function setProxyHostAndPort(type, address) {
      let url;
      try {
        // Prepend https just so we can use the URL parser
        // instead of parsing manually.
        url = new URL(`https://${address}`);
      } catch (e) {
        log.error(`Invalid address for ${type} proxy: ${address}`);
        return;
      }

      setPref(`network.proxy.${type}`, url.hostname);
      if (url.port) {
        setPref(`network.proxy.${type}_port`, Number(url.port));
      }
    }

    if (param.HTTPProxy) {
      setProxyHostAndPort("http", param.HTTPProxy);

      // network.proxy.share_proxy_settings is a UI feature, not handled by the
      // network code. That pref only controls if the checkbox is checked, and
      // then we must manually set the other values.
      if (param.UseHTTPProxyForAllProtocols) {
        param.FTPProxy = param.SSLProxy = param.SOCKSProxy = param.HTTPProxy;
      }
    }

    if (param.FTPProxy) {
      setProxyHostAndPort("ftp", param.FTPProxy);
    }

    if (param.SSLProxy) {
      setProxyHostAndPort("ssl", param.SSLProxy);
    }

    if (param.SOCKSProxy) {
      setProxyHostAndPort("socks", param.SOCKSProxy);
    }
  }
};
PK
!<��u"modules/policies/WebsiteFilter.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 implements the policy to block websites from being visited,
 * or to only allow certain websites to be visited.
 *
 * The blocklist takes as input an array of MatchPattern strings, as documented
 * at https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Match_patterns.
 *
 * The exceptions list takes the same as input. This list opens up
 * exceptions for rules on the blocklist that might be too strict.
 *
 * In addition to that, this allows the user to create a whitelist approach,
 * by using the special "<all_urls>" pattern for the blocklist, and then
 * adding all whitelisted websites on the exceptions list.
 *
 * Note that this module only blocks top-level website navigations. It doesn't
 * block any other accesses to these urls: image tags, scripts, XHR, etc.,
 * because that could cause unexpected breakage. This is a policy to block
 * users from visiting certain websites, and not from blocking any network
 * connections to those websites. If the admin is looking for that, the recommended
 * way is to configure that with extensions or through a company firewall.
 */

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");

const LIST_LENGTH_LIMIT = 1000;

const PREF_LOGLEVEL = "browser.policies.loglevel";

XPCOMUtils.defineLazyGetter(this, "log", () => {
  let { ConsoleAPI } = ChromeUtils.import("resource://gre/modules/Console.jsm", {});
  return new ConsoleAPI({
    prefix: "WebsiteFilter Policy",
    // tip: set maxLogLevel to "debug" and use log.debug() to create detailed
    // messages during development. See LOG_LEVELS in Console.jsm for details.
    maxLogLevel: "error",
    maxLogLevelPref: PREF_LOGLEVEL,
  });
});

var EXPORTED_SYMBOLS = [ "WebsiteFilter" ];

function WebsiteFilter(blocklist, exceptionlist) {
  let blockArray = [], exceptionArray = [];

  for (let i = 0; i < blocklist.length && i < LIST_LENGTH_LIMIT; i++) {
    try {
      let pattern = new MatchPattern(blocklist[i]);
      blockArray.push(pattern);
      log.debug(`Pattern added to WebsiteFilter.Block list: ${blocklist[i]}`);
    } catch (e) {
      log.error(`Invalid pattern on WebsiteFilter.Block: ${blocklist[i]}`);
    }
  }

  this._blockPatterns = new MatchPatternSet(blockArray);

  for (let i = 0; i < exceptionlist.length && i < LIST_LENGTH_LIMIT; i++) {
    try {
      let pattern = new MatchPattern(exceptionlist[i]);
      exceptionArray.push(pattern);
      log.debug(`Pattern added to WebsiteFilter.Exceptions list: ${exceptionlist[i]}`);
    } catch (e) {
      log.error(`Invalid pattern on WebsiteFilter.Exceptions: ${exceptionlist[i]}`);
    }
  }

  if (exceptionArray.length) {
    this._exceptionsPatterns = new MatchPatternSet(exceptionArray);
  }

  Services.obs.addObserver(this, "http-on-modify-request", true);
}

WebsiteFilter.prototype = {
  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                         Ci.nsISupportsWeakReference]),

  observe(subject, topic, data) {
    let channel, isDocument = false;
    try {
      channel = subject.QueryInterface(Ci.nsIHttpChannel);
      isDocument = channel.isDocument;
    } catch (e) {
      return;
    }

    // Only filter document accesses
    if (!isDocument) {
      return;
    }

    if (this._blockPatterns.matches(channel.URI)) {
      if (!this._exceptionsPatterns ||
          !this._exceptionsPatterns.matches(channel.URI)) {
        // NS_ERROR_BLOCKED_BY_POLICY displays the error message
        // designed for policy-related blocks.
        channel.cancel(Cr.NS_ERROR_BLOCKED_BY_POLICY);
      }
    }
  }
};
PK
!<T�Rp9p9modules/policies/schema.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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.EXPORTED_SYMBOLS = ["schema"];

this.schema =
{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "type": "object",
  "properties": {
    "Authentication": {
      "description": "Sites that support integrated authentication. See https://developer.mozilla.org/en-US/docs/Mozilla/Integrated_authentication",
      "first_available": "60.0",
      "enterprise_only": true,

      "type": "object",
      "properties": {
        "SPNEGO" : {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "Delegated" : {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "NTLM" : {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      }
    },

    "BlockAboutAddons": {
      "description": "Block access to the Add-ons Mananger (about:addons).",
      "first_available": "60.0",

      "type": "boolean"
    },

    "BlockAboutConfig": {
      "description": "Blocks access to the about:config page.",
      "first_available": "60.0",

      "type": "boolean"
    },

    "BlockAboutProfiles": {
      "description": "Blocks access to the about:profiles page.",
      "first_available": "60.0",

      "type": "boolean"
    },

    "BlockAboutSupport": {
      "description": "Blocks access to the about:support page.",
      "first_available": "60.0",

      "type": "boolean"
    },

    "Bookmarks": {
      "description": "Allows the creation of bookmarks in the Bookmarks bar, Bookmarks menu, or a specified folder inside them.",
      "first_available": "60.0",

      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "Title": {
            "type": "string"
          },

          "URL": {
            "type": "URL"
          },

          "Favicon": {
            "type": "URLorEmpty"
          },

          "Placement": {
            "type": "string",
            "enum": ["toolbar", "menu"]
          },

          "Folder": {
            "type": "string"
          }
        },
        "required": ["Title", "URL"]
      }
    },

    "Certificates": {
      "description": "Whether or not to use built in certs. This policy is Windows only at this time.",
      "first_available": "60.0",

      "type": "object",
      "properties": {
        "ImportEnterpriseRoots": {
          "type": "boolean"
        }
      }
    },

    "Cookies": {
      "description": "Allow or deny websites to set cookies.",
      "first_available": "60.0",

      "type": "object",
      "properties": {
        "Allow": {
          "type": "array",
          "items": {
            "type": "origin"
          }
        },

        "Block": {
          "type": "array",
          "items": {
            "type": "origin"
          }
        },

        "Default": {
          "type": "boolean"
        },

        "AcceptThirdParty": {
          "type": "string",
          "enum": ["always", "never", "from-visited"]
        },

        "ExpireAtSessionEnd": {
          "type": "boolean"
        },

        "Locked": {
          "type": "boolean"
        }
      }
    },

    "DisableAppUpdate": {
      "description": "Prevent the browser from updating.",
      "first_available": "60.0",
      "enterprise_only": true,

      "type": "boolean"
    },

    "DisableBuiltinPDFViewer": {
      "description": "Disables PDF.js, which displays PDFs within Firefox.",
      "first_available": "60.0",

      "type": "boolean"
    },

    "DisableDeveloperTools": {
      "description": "Prevents access to developer tools.",
      "first_available": "60.0",

      "type": "boolean"
    },

    "DisableFeedbackCommands": {
      "description": "Prevents ability to send feedback from the help menu (\"Submit Feedback\" and \"Report Deceptive Site\").",
      "first_available": "60.0",

      "type": "boolean"
    },

    "DisableFirefoxAccounts": {
      "description": "Disables Firefox Account based services, including Sync.",
      "first_available": "60.0",

      "type": "boolean"
    },

    "DisableFirefoxScreenshots": {
      "description": "Prevents usage of the Firefox Screenshots feature.",
      "first_available": "60.0",

      "type": "boolean"
    },

    "DisableFirefoxStudies": {
      "description": "Prevents Firefox from running studies.",
      "first_available": "60.0",

      "type": "boolean"
    },

    "DisableForgetButton": {
      "description": "Prevents access to the \"Forget\" button.",
      "first_available": "60.0",

      "type": "boolean"
    },

    "DisableFormHistory": {
      "description": "Don't remember search and form history.",
      "first_available": "60.0",

      "type": "boolean"
    },

    "DisableMasterPasswordCreation": {
      "description": "If true, removes access to create a master password.",
      "first_available": "60.0",

      "type": "boolean"
    },

    "DisablePocket": {
      "description": "Prevents ability to save webpages to Pocket.",
      "first_available": "60.0",

      "type": "boolean"
    },

    "DisablePrivateBrowsing": {
      "description": "Disables private browsing.",
      "first_available": "60.0",

      "type": "boolean"
    },

    "DisableProfileImport": {
      "description": "Disables the Firefox \"Import data from another browser\" button",
      "first_available": "60.0",

      "type": "boolean"
    },

    "DisableProfileRefresh": {
      "description": "Disables the \"Refresh Firefox\" button in about:support",
      "first_available": "60.0",

      "type": "boolean"
    },

    "DisableSafeMode": {
      "description": "Prevents ability to restart in safe mode. You can only prevent the Shift key by using GPO on Windows.",
      "first_available": "60.0",

      "type": "boolean"
    },

    "DisableSecurityBypass": {
      "description": "Prevents the user from bypassing certain security warnings.",
      "first_available": "60.0",

      "type": "object",
      "properties": {
        "InvalidCertificate": {
          "type": "boolean"
        },

        "SafeBrowsing": {
          "type": "boolean"
        }
      }
    },

    "DisableSetDesktopBackground": {
      "description": "Prevents usage of the \"Set Image as Desktop Background\" feature.",
      "first_available": "60.0",

      "type": "boolean"
    },

    "DisableSystemAddonUpdate": {
      "description": "Prevent the browser from installing and updating system addons.",
      "first_available": "60.0",
      "enterprise_only": true,

      "type": "boolean"
    },

    "DisableTelemetry": {
      "description": "Turns off telemetry.",
      "first_available": "60.0",
      "enterprise_only": true,

      "type": "boolean"
    },

    "DisplayBookmarksToolbar": {
      "description": "Causes the bookmarks toolbar to be displayed by default.",
      "first_available": "60.0",

      "type": "boolean"
    },

    "DisplayMenuBar": {
      "description": "Causes the menu bar to be displayed by default.",
      "first_available": "60.0",

      "type": "boolean"
    },

    "DontCheckDefaultBrowser": {
      "description": "Don't check for the default browser on startup.",
      "first_available": "60.0",

      "type": "boolean"
    },

    "EnableTrackingProtection": {
      "description": "Enables or disables tracking protection and optionally locks it.",
      "first_available": "60.0",

      "type": "object",
      "properties": {
        "Value": {
          "type": "boolean"
        },
        "Locked": {
          "type": "boolean"
        }
      },
      "required": ["Value"]
    },

    "Extensions": {
      "description": "Install, uninstall or lock extensions. The Install option takes URLs or paths as parameters. The Uninstall and Locked options take extension IDs.",
      "first_available": "60.0",
      "enterprise_only": true,

      "type": "object",
      "properties": {
        "Install" : {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "Uninstall" : {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "Locked" : {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      }
    },

    "FlashPlugin": {
      "description": "Allow or deny flash plugin usage.",
      "first_available": "60.0",

      "type": "object",
      "properties": {
        "Allow": {
          "type": "array",
          "items": {
            "type": "origin"
          }
        },

        "Block": {
          "type": "array",
          "items": {
            "type": "origin"
          }
        },

        "Default": {
          "type": "boolean"
        },

        "Locked": {
          "type": "boolean"
        }
      }
    },

    "Homepage": {
      "description": "Set and optionally lock the homepage.",
      "first_available": "60.0",
      "enterprise_only": true,

      "type": "object",
      "properties": {
        "URL": {
          "type": "URL"
        },
        "Locked": {
          "type": "boolean"
        },
        "Additional": {
          "type": "array",
          "items": {
            "type": "URL"
          }
        }
      },
      "required": ["URL"]
    },

    "InstallAddonsPermission": {
      "description": "Allow webites to install add-ons.",
      "first_available": "60.0",

      "type": "object",
      "properties": {
        "Allow": {
          "type": "array",
          "items": {
            "type": "origin"
          }
        },
        "Default": {
          "type": "boolean"
        }
      }
    },

    "NoDefaultBookmarks": {
      "description": "Don't create the default bookmarks bundled with Firefox, nor the Smart Bookmarks (Most Visited, Recent Tags). Note: this policy is only effective if used before the first run of the profile.",
      "first_available": "60.0",

      "type": "boolean"
    },

    "OfferToSaveLogins": {
      "description": "Enforces the setting to allow Firefox to offer to remember saved logins and passwords. Both true and false values are accepted.",
      "first_available": "60.0",

      "type": "boolean"
    },

    "OverrideFirstRunPage": {
      "description": "Override the first run page. Set this policy to blank if you want to disable the first run page.",
      "first_available": "60.0",
      "enterprise_only": true,

      "type": "URLorEmpty"
    },

    "OverridePostUpdatePage": {
      "description": "Override the post-update \"What's New\" page. Set this policy to blank if you want to disable the post-update page.",
      "first_available": "60.0",
      "enterprise_only": true,

      "type": "URLorEmpty"
    },

    "PopupBlocking": {
      "description": "Allow or deny popup usage.",
      "first_available": "60.0",

      "type": "object",
      "properties": {
        "Allow": {
          "type": "array",
          "items": {
            "type": "origin"
          }
        },

        "Default": {
          "type": "boolean"
        },

        "Locked": {
          "type": "boolean"
        }
      }
    },

    "Proxy": {
      "description": "Configure Proxy settings.",
      "first_available": "60.0",

      "type": "object",
      "properties": {
        "Mode": {
          "type": "string",
          "enum": ["none", "system", "manual", "autoDetect", "autoConfig"]
        },

        "Locked": {
          "type": "boolean"
        },

        "AutoConfigURL": {
          "type": "URLorEmpty"
        },

        "FTPProxy": {
          "type": "string"
        },

        "HTTPProxy": {
          "type": "string"
        },

        "SSLProxy": {
          "type": "string"
        },

        "SOCKSProxy": {
          "type": "string"
        },

        "SOCKSVersion": {
          "type": "number",
          "enum": [4, 5]
        },

        "UseHTTPProxyForAllProtocols": {
          "type": "boolean"
        },

        "Passthrough": {
          "type": "string"
        },

        "UseProxyForDNS": {
          "type": "boolean"
        },

        "AutoLogin": {
          "type": "boolean"
        }
      }
    },

    "SanitizeOnShutdown": {
      "description": "Clears ALL browser data on shutdown.",
      "first_available": "60.0",

      "type": "boolean"
    },

    "SearchBar": {
      "description": "Sets the default location of the search bar. Only applies on firtrun, but can be changed.",
      "first_available": "60.0",
      "enterprise_only": true,

      "type": "string",
      "enum": ["unified", "separate"]
    },

    "SearchEngines": {
      "description": "Modifies the list of search engines built into Firefox",
      "first_available": "60.0",
      "enterprise_only": true,

      "type": "object",
      "properties": {
        "Add": {
          "type": "array",
          "items": {
            "type": "object",
            "required": ["Name", "URLTemplate"],

            "properties": {
              "Name": {
                "type": "string"
              },
              "IconURL": {
                "type": "URLorEmpty"
              },
              "Alias": {
                "type": "string"
              },
              "Description": {
                "type": "string"
              },
              "Method": {
                "type": "string",
                "enum": ["GET", "POST"]
              },
              "URLTemplate": {
                "type": "string"
              },
              "SuggestURLTemplate": {
                "type": "string"
              }
            }
          }
        },
        "Default": {
          "type": "string"
        },
        "PreventInstalls": {
          "type": "boolean"
        }
      }
    },

    "WebsiteFilter": {
      "description": "Blocks websites from being visited. The parameters take an array of Match Patterns, as documented in https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Match_patterns. Only http/https accesses are supported at the moment. The arrays are limited to 1000 entries each.",
      "first_available": "60.0",
      "enterprise_only": "true",

      "type": "object",
      "properties": {
        "Block": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },

        "Exceptions": {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      }
    }
  }
}
PK
!<j��E=F=F'modules/sessionstore/ContentRestore.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = ["ContentRestore"];

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", this);
ChromeUtils.import("resource://gre/modules/Services.jsm", this);

ChromeUtils.defineModuleGetter(this, "DocShellCapabilities",
  "resource:///modules/sessionstore/DocShellCapabilities.jsm");
ChromeUtils.defineModuleGetter(this, "FormData",
  "resource://gre/modules/FormData.jsm");
ChromeUtils.defineModuleGetter(this, "ScrollPosition",
  "resource://gre/modules/ScrollPosition.jsm");
ChromeUtils.defineModuleGetter(this, "SessionHistory",
  "resource://gre/modules/sessionstore/SessionHistory.jsm");
ChromeUtils.defineModuleGetter(this, "SessionStorage",
  "resource:///modules/sessionstore/SessionStorage.jsm");
ChromeUtils.defineModuleGetter(this, "Utils",
  "resource://gre/modules/sessionstore/Utils.jsm");

const ssu = Cc["@mozilla.org/browser/sessionstore/utils;1"]
              .getService(Ci.nsISessionStoreUtils);

/**
 * Restores frame tree |data|, starting at the given root |frame|. As the
 * function recurses into descendant frames it will call cb(frame, data) for
 * each frame it encounters, starting with the given root.
 */
function restoreFrameTreeData(frame, data, cb) {
  // Restore data for the root frame.
  // The callback can abort by returning false.
  if (cb(frame, data) === false) {
    return;
  }

  if (!data.hasOwnProperty("children")) {
    return;
  }

  // Recurse into child frames.
  ssu.forEachNonDynamicChildFrame(frame, (subframe, index) => {
    if (data.children[index]) {
      restoreFrameTreeData(subframe, data.children[index], cb);
    }
  });
}

/**
 * This module implements the content side of session restoration. The chrome
 * side is handled by SessionStore.jsm. The functions in this module are called
 * by content-sessionStore.js based on messages received from SessionStore.jsm
 * (or, in one case, based on a "load" event). Each tab has its own
 * ContentRestore instance, constructed by content-sessionStore.js.
 *
 * In a typical restore, content-sessionStore.js will call the following based
 * on messages and events it receives:
 *
 *   restoreHistory(tabData, loadArguments, callbacks)
 *     Restores the tab's history and session cookies.
 *   restoreTabContent(loadArguments, finishCallback)
 *     Starts loading the data for the current page to restore.
 *   restoreDocument()
 *     Restore form and scroll data.
 *
 * When the page has been loaded from the network, we call finishCallback. It
 * should send a message to SessionStore.jsm, which may cause other tabs to be
 * restored.
 *
 * When the page has finished loading, a "load" event will trigger in
 * content-sessionStore.js, which will call restoreDocument. At that point,
 * form data is restored and the restore is complete.
 *
 * At any time, SessionStore.jsm can cancel the ongoing restore by sending a
 * reset message, which causes resetRestore to be called. At that point it's
 * legal to begin another restore.
 */
function ContentRestore(chromeGlobal) {
  let internal = new ContentRestoreInternal(chromeGlobal);
  let external = {};

  let EXPORTED_METHODS = ["restoreHistory",
                          "restoreTabContent",
                          "restoreDocument",
                          "resetRestore"
                         ];

  for (let method of EXPORTED_METHODS) {
    external[method] = internal[method].bind(internal);
  }

  return Object.freeze(external);
}

function ContentRestoreInternal(chromeGlobal) {
  this.chromeGlobal = chromeGlobal;

  // The following fields are only valid during certain phases of the restore
  // process.

  // The tabData for the restore. Set in restoreHistory and removed in
  // restoreTabContent.
  this._tabData = null;

  // Contains {entry, scrollPositions, formdata}, where entry is a
  // single entry from the tabData.entries array. Set in
  // restoreTabContent and removed in restoreDocument.
  this._restoringDocument = null;

  // This listener is used to detect reloads on restoring tabs. Set in
  // restoreHistory and removed in restoreTabContent.
  this._historyListener = null;

  // This listener detects when a pending tab starts loading (when not
  // initiated by sessionstore) and when a restoring tab has finished loading
  // data from the network. Set in restoreHistory() and restoreTabContent(),
  // removed in resetRestore().
  this._progressListener = null;
}

/**
 * The API for the ContentRestore module. Methods listed in EXPORTED_METHODS are
 * public.
 */
ContentRestoreInternal.prototype = {

  get docShell() {
    return this.chromeGlobal.docShell;
  },

  /**
   * Starts the process of restoring a tab. The tabData to be restored is passed
   * in here and used throughout the restoration. The epoch (which must be
   * non-zero) is passed through to all the callbacks. If a load in the tab
   * is started while it is pending, the appropriate callbacks are called.
   */
  restoreHistory(tabData, loadArguments, callbacks) {
    this._tabData = tabData;

    // In case about:blank isn't done yet.
    let webNavigation = this.docShell.QueryInterface(Ci.nsIWebNavigation);
    webNavigation.stop(Ci.nsIWebNavigation.STOP_ALL);

    // Make sure currentURI is set so that switch-to-tab works before the tab is
    // restored. We'll reset this to about:blank when we try to restore the tab
    // to ensure that docshell doeesn't get confused. Don't bother doing this if
    // we're restoring immediately due to a process switch. It just causes the
    // URL bar to be temporarily blank.
    let activeIndex = tabData.index - 1;
    let activePageData = tabData.entries[activeIndex] || {};
    let uri = activePageData.url || null;
    if (uri && !loadArguments) {
      webNavigation.setCurrentURI(Utils.makeURI(uri));
    }

    SessionHistory.restore(this.docShell, tabData);

    // Add a listener to watch for reloads.
    let listener = new HistoryListener(this.docShell, () => {
      // On reload, restore tab contents.
      this.restoreTabContent(null, false, callbacks.onLoadFinished);
    });

    webNavigation.sessionHistory.addSHistoryListener(listener);
    this._historyListener = listener;

    // Make sure to reset the capabilities and attributes in case this tab gets
    // reused.
    let disallow = new Set(tabData.disallow && tabData.disallow.split(","));
    DocShellCapabilities.restore(this.docShell, disallow);

    if (tabData.storage && this.docShell instanceof Ci.nsIDocShell) {
      SessionStorage.restore(this.docShell, tabData.storage);
      delete tabData.storage;
    }

    // Add a progress listener to correctly handle browser.loadURI()
    // calls from foreign code.
    this._progressListener = new ProgressListener(this.docShell, {
      onStartRequest: () => {
        // Some code called browser.loadURI() on a pending tab. It's safe to
        // assume we don't care about restoring scroll or form data.
        this._tabData = null;

        // Listen for the tab to finish loading.
        this.restoreTabContentStarted(callbacks.onLoadFinished);

        // Notify the parent.
        callbacks.onLoadStarted();
      }
    });
  },

  /**
   * Start loading the current page. When the data has finished loading from the
   * network, finishCallback is called. Returns true if the load was successful.
   */
  restoreTabContent(loadArguments, isRemotenessUpdate, finishCallback) {
    let tabData = this._tabData;
    this._tabData = null;

    let webNavigation = this.docShell.QueryInterface(Ci.nsIWebNavigation);
    let history = webNavigation.sessionHistory;

    // Listen for the tab to finish loading.
    this.restoreTabContentStarted(finishCallback);

    // Reset the current URI to about:blank. We changed it above for
    // switch-to-tab, but now it must go back to the correct value before the
    // load happens. Don't bother doing this if we're restoring immediately
    // due to a process switch.
    if (!isRemotenessUpdate) {
      webNavigation.setCurrentURI(Utils.makeURI("about:blank"));
    }

    try {
      if (loadArguments) {
        // A load has been redirected to a new process so get history into the
        // same state it was before the load started then trigger the load.
        let referrer = loadArguments.referrer ?
                       Utils.makeURI(loadArguments.referrer) : null;
        let referrerPolicy = ("referrerPolicy" in loadArguments
            ? loadArguments.referrerPolicy
            : Ci.nsIHttpChannel.REFERRER_POLICY_UNSET);
        let postData = loadArguments.postData ?
                       Utils.makeInputStream(loadArguments.postData) : null;
        let triggeringPrincipal = loadArguments.triggeringPrincipal
                                  ? Utils.deserializePrincipal(loadArguments.triggeringPrincipal)
                                  : null;

        if (loadArguments.userContextId) {
          webNavigation.setOriginAttributesBeforeLoading({ userContextId: loadArguments.userContextId });
        }

        webNavigation.loadURIWithOptions(loadArguments.uri, loadArguments.flags,
                                         referrer, referrerPolicy, postData,
                                         null, null, triggeringPrincipal);
      } else if (tabData.userTypedValue && tabData.userTypedClear) {
        // If the user typed a URL into the URL bar and hit enter right before
        // we crashed, we want to start loading that page again. A non-zero
        // userTypedClear value means that the load had started.
        // Load userTypedValue and fix up the URL if it's partial/broken.
        webNavigation.loadURI(tabData.userTypedValue,
                              Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP,
                              null, null, null,
                              Services.scriptSecurityManager.getSystemPrincipal());
      } else if (tabData.entries.length) {
        // Stash away the data we need for restoreDocument.
        let activeIndex = tabData.index - 1;
        this._restoringDocument = {entry: tabData.entries[activeIndex] || {},
                                   formdata: tabData.formdata || {},
                                   scrollPositions: tabData.scroll || {}};

        // In order to work around certain issues in session history, we need to
        // force session history to update its internal index and call reload
        // instead of gotoIndex. See bug 597315.
        history.reloadCurrentEntry();
      } else {
        // If there's nothing to restore, we should still blank the page.
        webNavigation.loadURI("about:blank",
                              Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_HISTORY,
                              null, null, null,
                              Services.scriptSecurityManager.getSystemPrincipal());
      }

      return true;
    } catch (ex) {
      if (ex instanceof Ci.nsIException) {
        // Ignore page load errors, but return false to signal that the load never
        // happened.
        return false;
      }
    }
    return null;
  },

  /**
   * To be called after restoreHistory(). Removes all listeners needed for
   * pending tabs and makes sure to notify when the tab finished loading.
   */
  restoreTabContentStarted(finishCallback) {
    // The reload listener is no longer needed.
    this._historyListener.uninstall();
    this._historyListener = null;

    // Remove the old progress listener.
    this._progressListener.uninstall();

    // We're about to start a load. This listener will be called when the load
    // has finished getting everything from the network.
    this._progressListener = new ProgressListener(this.docShell, {
      onStopRequest: () => {
        // Call resetRestore() to reset the state back to normal. The data
        // needed for restoreDocument() (which hasn't happened yet) will
        // remain in _restoringDocument.
        this.resetRestore();

        finishCallback();
      }
    });
  },

  /**
   * Finish restoring the tab by filling in form data and setting the scroll
   * position. The restore is complete when this function exits. It should be
   * called when the "load" event fires for the restoring tab.
   */
  restoreDocument() {
    if (!this._restoringDocument) {
      return;
    }
    let {formdata, scrollPositions} = this._restoringDocument;
    this._restoringDocument = null;

    let window = this.docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                               .getInterface(Ci.nsIDOMWindow);

    // Restore form data.
    restoreFrameTreeData(window, formdata, (frame, data) => {
      // restore() will return false, and thus abort restoration for the
      // current |frame| and its descendants, if |data.url| is given but
      // doesn't match the loaded document's URL.
      return FormData.restore(frame, data);
    });

    // Restore scroll data.
    restoreFrameTreeData(window, scrollPositions, (frame, data) => {
      if (data.scroll) {
        ScrollPosition.restore(frame, data.scroll);
      }
    });
  },

  /**
   * Cancel an ongoing restore. This function can be called any time between
   * restoreHistory and restoreDocument.
   *
   * This function is called externally (if a restore is canceled) and
   * internally (when the loads for a restore have finished). In the latter
   * case, it's called before restoreDocument, so it cannot clear
   * _restoringDocument.
   */
  resetRestore() {
    this._tabData = null;

    if (this._historyListener) {
      this._historyListener.uninstall();
    }
    this._historyListener = null;

    if (this._progressListener) {
      this._progressListener.uninstall();
    }
    this._progressListener = null;
  }
};

/*
 * This listener detects when a page being restored is reloaded. It triggers a
 * callback and cancels the reload. The callback will send a message to
 * SessionStore.jsm so that it can restore the content immediately.
 */
function HistoryListener(docShell, callback) {
  let webNavigation = docShell.QueryInterface(Ci.nsIWebNavigation);
  webNavigation.sessionHistory.addSHistoryListener(this);

  this.webNavigation = webNavigation;
  this.callback = callback;
}
HistoryListener.prototype = {
  QueryInterface: XPCOMUtils.generateQI([
    Ci.nsISHistoryListener,
    Ci.nsISupportsWeakReference
  ]),

  uninstall() {
    let shistory = this.webNavigation.sessionHistory;
    if (shistory) {
      shistory.removeSHistoryListener(this);
    }
  },

  OnHistoryGoBack(backURI) { return true; },
  OnHistoryGoForward(forwardURI) { return true; },
  OnHistoryGotoIndex(index, gotoURI) { return true; },
  OnHistoryPurge(numEntries) { return true; },
  OnHistoryReplaceEntry(index) {},

  // This will be called for a pending tab when loadURI(uri) is called where
  // the given |uri| only differs in the fragment.
  OnHistoryNewEntry(newURI) {
    let currentURI = this.webNavigation.currentURI;

    // Ignore new SHistory entries with the same URI as those do not indicate
    // a navigation inside a document by changing the #hash part of the URL.
    // We usually hit this when purging session history for browsers.
    if (currentURI && (currentURI.spec == newURI.spec)) {
      return;
    }

    // Reset the tab's URL to what it's actually showing. Without this loadURI()
    // would use the current document and change the displayed URL only.
    this.webNavigation.setCurrentURI(Utils.makeURI("about:blank"));

    // Kick off a new load so that we navigate away from about:blank to the
    // new URL that was passed to loadURI(). The new load will cause a
    // STATE_START notification to be sent and the ProgressListener will then
    // notify the parent and do the rest.
    let flags = Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
    this.webNavigation.loadURI(newURI.spec, flags,
                               null, null, null,
                               Services.scriptSecurityManager.getSystemPrincipal());
  },

  OnHistoryReload(reloadURI, reloadFlags) {
    this.callback();

    // Cancel the load.
    return false;
  },

  OnLengthChanged(aCount) {
    // Ignore, the method is implemented so that XPConnect doesn't throw!
  },

  OnIndexChanged(aIndex) {
    // Ignore, the method is implemented so that XPConnect doesn't throw!
  },
};

/**
 * This class informs SessionStore.jsm whenever the network requests for a
 * restoring page have completely finished. We only restore three tabs
 * simultaneously, so this is the signal for SessionStore.jsm to kick off
 * another restore (if there are more to do).
 *
 * The progress listener is also used to be notified when a load not initiated
 * by sessionstore starts. Pending tabs will then need to be marked as no
 * longer pending.
 */
function ProgressListener(docShell, callbacks) {
  let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                            .getInterface(Ci.nsIWebProgress);
  webProgress.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_STATE_WINDOW);

  this.webProgress = webProgress;
  this.callbacks = callbacks;
}

ProgressListener.prototype = {
  QueryInterface: XPCOMUtils.generateQI([
    Ci.nsIWebProgressListener,
    Ci.nsISupportsWeakReference
  ]),

  uninstall() {
    this.webProgress.removeProgressListener(this);
  },

  onStateChange(webProgress, request, stateFlags, status) {
    let {STATE_IS_WINDOW, STATE_STOP, STATE_START} = Ci.nsIWebProgressListener;
    if (!webProgress.isTopLevel || !(stateFlags & STATE_IS_WINDOW)) {
      return;
    }

    if (stateFlags & STATE_START && this.callbacks.onStartRequest) {
      this.callbacks.onStartRequest();
    }

    if (stateFlags & STATE_STOP && this.callbacks.onStopRequest) {
      this.callbacks.onStopRequest();
    }
  },
};
PK
!<�+�e-modules/sessionstore/DocShellCapabilities.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = ["DocShellCapabilities"];

/**
 * The external API exported by this module.
 */
var DocShellCapabilities = Object.freeze({
  collect(docShell) {
    return DocShellCapabilitiesInternal.collect(docShell);
  },

  restore(docShell, disallow) {
    return DocShellCapabilitiesInternal.restore(docShell, disallow);
  },
});

const CAPABILITIES_TO_IGNORE = new Set(["Javascript"]);

/**
 * Internal functionality to save and restore the docShell.allow* properties.
 */
var DocShellCapabilitiesInternal = {
  // List of docShell capabilities to (re)store. These are automatically
  // retrieved from a given docShell if not already collected before.
  // This is made so they're automatically in sync with all nsIDocShell.allow*
  // properties.
  caps: null,

  allCapabilities(docShell) {
    if (!this.caps) {
      let keys = Object.keys(docShell);
      this.caps = keys.filter(k => k.startsWith("allow")).map(k => k.slice(5));
    }
    return this.caps;
  },

  collect(docShell) {
    let caps = this.allCapabilities(docShell);
    return caps.filter(cap => !docShell["allow" + cap]
                              && !CAPABILITIES_TO_IGNORE.has(cap));
  },

  restore(docShell, disallow) {
    let caps = this.allCapabilities(docShell);
    for (let cap of caps)
      docShell["allow" + cap] = !disallow.has(cap);
  },
};
PK
!<6��ZZ$modules/sessionstore/GlobalState.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = ["GlobalState"];

const EXPORTED_METHODS = ["getState", "clear", "get", "set", "delete", "setFromState"];
/**
 * Module that contains global session data.
 */
function GlobalState() {
  let internal = new GlobalStateInternal();
  let external = {};
  for (let method of EXPORTED_METHODS) {
    external[method] = internal[method].bind(internal);
  }
  return Object.freeze(external);
}

function GlobalStateInternal() {
  // Storage for global state.
  this.state = {};
}

GlobalStateInternal.prototype = {
  /**
   * Get all value from the global state.
   */
  getState() {
    return this.state;
  },

  /**
   * Clear all currently stored global state.
   */
  clear() {
    this.state = {};
  },

  /**
   * Retrieve a value from the global state.
   *
   * @param aKey
   *        A key the value is stored under.
   * @return The value stored at aKey, or an empty string if no value is set.
   */
  get(aKey) {
    return this.state[aKey] || "";
  },

  /**
   * Set a global value.
   *
   * @param aKey
   *        A key to store the value under.
   */
  set(aKey, aStringValue) {
    this.state[aKey] = aStringValue;
  },

  /**
   * Delete a global value.
   *
   * @param aKey
   *        A key to delete the value for.
   */
  delete(aKey) {
    delete this.state[aKey];
  },

  /**
   * Set the current global state from a state object. Any previous global
   * state will be removed, even if the new state does not contain a matching
   * key.
   *
   * @param aState
   *        A state object to extract global state from to be set.
   */
  setFromState(aState) {
    this.state = (aState && aState.global) || {};
  }
};
PK
!<����M"M">modules/sessionstore/RecentlyClosedTabsAndWindowsMenuUtils.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

var EXPORTED_SYMBOLS = ["RecentlyClosedTabsAndWindowsMenuUtils"];

const kNSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/PlacesUtils.jsm");

ChromeUtils.defineModuleGetter(this, "PluralForm",
                               "resource://gre/modules/PluralForm.jsm");
ChromeUtils.defineModuleGetter(this, "SessionStore",
                               "resource:///modules/sessionstore/SessionStore.jsm");

var navigatorBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");

var RecentlyClosedTabsAndWindowsMenuUtils = {

  /**
  * Builds up a document fragment of UI items for the recently closed tabs.
  * @param   aWindow
  *          The window that the tabs were closed in.
  * @param   aTagName
  *          The tag name that will be used when creating the UI items.
  * @param   aPrefixRestoreAll (defaults to false)
  *          Whether the 'restore all tabs' item is suffixed or prefixed to the list.
  *          If suffixed (the default) a separator will be inserted before it.
  * @param   aRestoreAllLabel (defaults to "menuRestoreAllTabs.label")
  *          Which localizable string to use for the 'restore all tabs' item.
  * @returns A document fragment with UI items for each recently closed tab.
  */
  getTabsFragment(aWindow, aTagName, aPrefixRestoreAll = false,
                  aRestoreAllLabel = "menuRestoreAllTabs.label") {
    let doc = aWindow.document;
    let fragment = doc.createDocumentFragment();
    if (SessionStore.getClosedTabCount(aWindow) != 0) {
      let closedTabs = SessionStore.getClosedTabData(aWindow, false);
      for (let i = 0; i < closedTabs.length; i++) {
        createEntry(aTagName, false, i, closedTabs[i], doc,
                    closedTabs[i].title, fragment);
      }

    createRestoreAllEntry(doc, fragment, aPrefixRestoreAll, false,
                          aRestoreAllLabel, closedTabs.length, aTagName);
    }
    return fragment;
  },

  /**
  * Builds up a document fragment of UI items for the recently closed windows.
  * @param   aWindow
  *          A window that can be used to create the elements and document fragment.
  * @param   aTagName
  *          The tag name that will be used when creating the UI items.
  * @param   aPrefixRestoreAll (defaults to false)
  *          Whether the 'restore all windows' item is suffixed or prefixed to the list.
  *          If suffixed (the default) a separator will be inserted before it.
  * @param   aRestoreAllLabel (defaults to "menuRestoreAllWindows.label")
  *          Which localizable string to use for the 'restore all windows' item.
  * @returns A document fragment with UI items for each recently closed window.
  */
  getWindowsFragment(aWindow, aTagName, aPrefixRestoreAll = false,
                     aRestoreAllLabel = "menuRestoreAllWindows.label") {
    let closedWindowData = SessionStore.getClosedWindowData(false);
    let doc = aWindow.document;
    let fragment = doc.createDocumentFragment();
    if (closedWindowData.length != 0) {
      let menuLabelString = navigatorBundle.GetStringFromName("menuUndoCloseWindowLabel");
      let menuLabelStringSingleTab =
        navigatorBundle.GetStringFromName("menuUndoCloseWindowSingleTabLabel");

      for (let i = 0; i < closedWindowData.length; i++) {
        let undoItem = closedWindowData[i];
        let otherTabsCount = undoItem.tabs.length - 1;
        let label = (otherTabsCount == 0) ? menuLabelStringSingleTab
                                          : PluralForm.get(otherTabsCount, menuLabelString);
        let menuLabel = label.replace("#1", undoItem.title)
                             .replace("#2", otherTabsCount);
        let selectedTab = undoItem.tabs[undoItem.selected - 1];

        createEntry(aTagName, true, i, selectedTab, doc, menuLabel,
                    fragment);
      }

      createRestoreAllEntry(doc, fragment, aPrefixRestoreAll, true,
                            aRestoreAllLabel, closedWindowData.length,
                            aTagName);
    }
    return fragment;
  },


  /**
    * Re-open a closed tab and put it to the end of the tab strip.
    * Used for a middle click.
    * @param aEvent
    *        The event when the user clicks the menu item
    */
  _undoCloseMiddleClick(aEvent) {
    if (aEvent.button != 1)
      return;

    aEvent.view.undoCloseTab(aEvent.originalTarget.getAttribute("value"));
    aEvent.view.gBrowser.moveTabToEnd();
    let ancestorPanel = aEvent.target.closest("panel");
    if (ancestorPanel) {
      ancestorPanel.hidePopup();
    }
  },
};

function setImage(aItem, aElement) {
  let iconURL = aItem.image;
  // don't initiate a connection just to fetch a favicon (see bug 467828)
  if (/^https?:/.test(iconURL))
    iconURL = "moz-anno:favicon:" + iconURL;

  aElement.setAttribute("image", iconURL);
}

/**
 * Create a UI entry for a recently closed tab or window.
 * @param aTagName
 *        the tag name that will be used when creating the UI entry
 * @param aIsWindowsFragment
 *        whether or not this entry will represent a closed window
 * @param aIndex
 *        the index of the closed tab
 * @param aClosedTab
 *        the closed tab
 * @param aDocument
 *        a document that can be used to create the entry
 * @param aMenuLabel
 *        the label the created entry will have
 * @param aFragment
 *        the fragment the created entry will be in
 */
function createEntry(aTagName, aIsWindowsFragment, aIndex, aClosedTab,
                     aDocument, aMenuLabel, aFragment) {
  let element = aDocument.createElementNS(kNSXUL, aTagName);

  element.setAttribute("label", aMenuLabel);
  if (aClosedTab.image) {
    setImage(aClosedTab, element);
  }
  if (!aIsWindowsFragment) {
    element.setAttribute("value", aIndex);
  }

  if (aTagName == "menuitem") {
    element.setAttribute("class", "menuitem-iconic bookmark-item menuitem-with-favicon");
  }

  element.setAttribute("oncommand", "undoClose" + (aIsWindowsFragment ? "Window" : "Tab") +
                       "(" + aIndex + ");");

  // Set the targetURI attribute so it will be shown in tooltip.
  // SessionStore uses one-based indexes, so we need to normalize them.
  let tabData;
  tabData = aIsWindowsFragment ? aClosedTab
                     : aClosedTab.state;
  let activeIndex = (tabData.index || tabData.entries.length) - 1;
  if (activeIndex >= 0 && tabData.entries[activeIndex]) {
    element.setAttribute("targetURI", tabData.entries[activeIndex].url);
  }

  if (!aIsWindowsFragment) {
    element.addEventListener("click", RecentlyClosedTabsAndWindowsMenuUtils._undoCloseMiddleClick);
  }
  if (aIndex == 0) {
    element.setAttribute("key", "key_undoClose" + (aIsWindowsFragment ? "Window" : "Tab"));
  }

  aFragment.appendChild(element);
}

/**
 * Create an entry to restore all closed windows or tabs.
 * @param aDocument
 *        a document that can be used to create the entry
 * @param aFragment
 *        the fragment the created entry will be in
 * @param aPrefixRestoreAll
 *        whether the 'restore all windows' item is suffixed or prefixed to the list
 *        If suffixed a separator will be inserted before it.
 * @param aIsWindowsFragment
 *        whether or not this entry will represent a closed window
 * @param aRestoreAllLabel
 *        which localizable string to use for the entry
 * @param aEntryCount
 *        the number of elements to be restored by this entry
 * @param aTagName
 *        the tag name that will be used when creating the UI entry
 */
function createRestoreAllEntry(aDocument, aFragment, aPrefixRestoreAll,
                                aIsWindowsFragment, aRestoreAllLabel,
                                aEntryCount, aTagName) {
  let restoreAllElements = aDocument.createElementNS(kNSXUL, aTagName);
  restoreAllElements.classList.add("restoreallitem");
  restoreAllElements.setAttribute("label", navigatorBundle.GetStringFromName(aRestoreAllLabel));
  restoreAllElements.setAttribute("oncommand",
                                  "for (var i = 0; i < " + aEntryCount + "; i++) undoClose" +
                                    (aIsWindowsFragment ? "Window" : "Tab") + "();");
  if (aPrefixRestoreAll) {
    aFragment.insertBefore(restoreAllElements, aFragment.firstChild);
  } else {
    aFragment.appendChild(aDocument.createElementNS(kNSXUL, "menuseparator"));
    aFragment.appendChild(restoreAllElements);
  }
}
PK
!<��Z^;
;
!modules/sessionstore/RunState.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = ["RunState"];

const STATE_STOPPED = 0;
const STATE_RUNNING = 1;
const STATE_QUITTING = 2;
const STATE_CLOSING = 3;
const STATE_CLOSED = 4;

// We're initially stopped.
var state = STATE_STOPPED;

/**
 * This module keeps track of SessionStore's current run state. We will
 * always start out at STATE_STOPPED. After the session was read from disk and
 * the initial browser window has loaded we switch to STATE_RUNNING. On the
 * first notice that a browser shutdown was granted we switch to STATE_QUITTING.
 */
var RunState = Object.freeze({
  // If we're stopped then SessionStore hasn't been initialized yet. As soon
  // as the session is read from disk and the initial browser window has loaded
  // the run state will change to STATE_RUNNING.
  get isStopped() {
    return state == STATE_STOPPED;
  },

  // STATE_RUNNING is our default mode of operation that we'll spend most of
  // the time in. After the session was read from disk and the first browser
  // window has loaded we remain running until the browser quits.
  get isRunning() {
    return state == STATE_RUNNING;
  },

  // We will enter STATE_QUITTING as soon as we receive notice that a browser
  // shutdown was granted. SessionStore will use this information to prevent
  // us from collecting partial information while the browser is shutting down
  // as well as to allow a last single write to disk and block all writes after
  // that.
  get isQuitting() {
    return state >= STATE_QUITTING;
  },

  // We will enter STATE_CLOSING as soon as SessionStore is uninitialized.
  // The SessionFile module will know that a last write will happen in this
  // state and it can do some necessary cleanup.
  get isClosing() {
    return state == STATE_CLOSING;
  },

  // We will enter STATE_CLOSED as soon as SessionFile has written to disk for
  // the last time before shutdown and will not accept any further writes.
  get isClosed() {
    return state == STATE_CLOSED;
  },

  // Switch the run state to STATE_RUNNING. This must be called after the
  // session was read from, the initial browser window has loaded and we're
  // now ready to restore session data.
  setRunning() {
    if (this.isStopped) {
      state = STATE_RUNNING;
    }
  },

  // Switch the run state to STATE_CLOSING. This must be called *before* the
  // last SessionFile.write() call so that SessionFile knows we're closing and
  // can do some last cleanups and write a proper sessionstore.js file.
  setClosing() {
    if (this.isQuitting) {
      state = STATE_CLOSING;
    }
  },

  // Switch the run state to STATE_CLOSED. This must be called by SessionFile
  // after the last write to disk was accepted and no further writes will be
  // allowed. Any writes after this stage will cause exceptions.
  setClosed() {
    if (this.isClosing) {
      state = STATE_CLOSED;
    }
  },

  // Switch the run state to STATE_QUITTING. This should be called once we're
  // certain that the browser is going away and before we start collecting the
  // final window states to save in the session file.
  setQuitting() {
    if (this.isRunning) {
      state = STATE_QUITTING;
    }
  },
});
PK
!<��;RBRB$modules/sessionstore/SessionFile.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = ["SessionFile"];

/**
 * Implementation of all the disk I/O required by the session store.
 * This is a private API, meant to be used only by the session store.
 * It will change. Do not use it for any other purpose.
 *
 * Note that this module implicitly depends on one of two things:
 * 1. either the asynchronous file I/O system enqueues its requests
 *   and never attempts to simultaneously execute two I/O requests on
 *   the files used by this module from two distinct threads; or
 * 2. the clients of this API are well-behaved and do not place
 *   concurrent requests to the files used by this module.
 *
 * Otherwise, we could encounter bugs, especially under Windows,
 *   e.g. if a request attempts to write sessionstore.js while
 *   another attempts to copy that file.
 *
 * This implementation uses OS.File, which guarantees property 1.
 */

ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/osfile.jsm");
ChromeUtils.import("resource://gre/modules/AsyncShutdown.jsm");

ChromeUtils.defineModuleGetter(this, "RunState",
  "resource:///modules/sessionstore/RunState.jsm");
ChromeUtils.defineModuleGetter(this, "TelemetryStopwatch",
  "resource://gre/modules/TelemetryStopwatch.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "Telemetry",
  "@mozilla.org/base/telemetry;1", "nsITelemetry");
XPCOMUtils.defineLazyServiceGetter(this, "sessionStartup",
  "@mozilla.org/browser/sessionstartup;1", "nsISessionStartup");
ChromeUtils.defineModuleGetter(this, "SessionWorker",
  "resource:///modules/sessionstore/SessionWorker.jsm");
ChromeUtils.defineModuleGetter(this, "SessionStore",
  "resource:///modules/sessionstore/SessionStore.jsm");

const PREF_UPGRADE_BACKUP = "browser.sessionstore.upgradeBackup.latestBuildID";
const PREF_MAX_UPGRADE_BACKUPS = "browser.sessionstore.upgradeBackup.maxUpgradeBackups";

const PREF_MAX_SERIALIZE_BACK = "browser.sessionstore.max_serialize_back";
const PREF_MAX_SERIALIZE_FWD = "browser.sessionstore.max_serialize_forward";

XPCOMUtils.defineLazyPreferenceGetter(this, "kMaxWriteFailures",
  "browser.sessionstore.max_write_failures", 5);

var SessionFile = {
  /**
   * Read the contents of the session file, asynchronously.
   */
  read() {
    return SessionFileInternal.read();
  },
  /**
   * Write the contents of the session file, asynchronously.
   */
  write(aData) {
    return SessionFileInternal.write(aData);
  },
  /**
   * Wipe the contents of the session file, asynchronously.
   */
  wipe() {
    return SessionFileInternal.wipe();
  },

  /**
   * Return the paths to the files used to store, backup, etc.
   * the state of the file.
   */
  get Paths() {
    return SessionFileInternal.Paths;
  },

  get MaxWriteFailures() {
    return kMaxWriteFailures;
  }
};

Object.freeze(SessionFile);

var Path = OS.Path;
var profileDir = OS.Constants.Path.profileDir;

var SessionFileInternal = {
  Paths: Object.freeze({
    // The path to the latest version of sessionstore written during a clean
    // shutdown. After startup, it is renamed `cleanBackup`.
    clean: Path.join(profileDir, "sessionstore.jsonlz4"),

    // The path at which we store the previous version of `clean`. Updated
    // whenever we successfully load from `clean`.
    cleanBackup: Path.join(profileDir, "sessionstore-backups", "previous.jsonlz4"),

    // The directory containing all sessionstore backups.
    backups: Path.join(profileDir, "sessionstore-backups"),

    // The path to the latest version of the sessionstore written
    // during runtime. Generally, this file contains more
    // privacy-sensitive information than |clean|, and this file is
    // therefore removed during clean shutdown. This file is designed to protect
    // against crashes / sudden shutdown.
    recovery: Path.join(profileDir, "sessionstore-backups", "recovery.jsonlz4"),

    // The path to the previous version of the sessionstore written
    // during runtime (e.g. 15 seconds before recovery). In case of a
    // clean shutdown, this file is removed.  Generally, this file
    // contains more privacy-sensitive information than |clean|, and
    // this file is therefore removed during clean shutdown.  This
    // file is designed to protect against crashes that are nasty
    // enough to corrupt |recovery|.
    recoveryBackup: Path.join(profileDir, "sessionstore-backups", "recovery.baklz4"),

    // The path to a backup created during an upgrade of Firefox.
    // Having this backup protects the user essentially from bugs in
    // Firefox or add-ons, especially for users of Nightly. This file
    // does not contain any information more sensitive than |clean|.
    upgradeBackupPrefix: Path.join(profileDir, "sessionstore-backups", "upgrade.jsonlz4-"),

    // The path to the backup of the version of the session store used
    // during the latest upgrade of Firefox. During load/recovery,
    // this file should be used if both |path|, |backupPath| and
    // |latestStartPath| are absent/incorrect.  May be "" if no
    // upgrade backup has ever been performed. This file does not
    // contain any information more sensitive than |clean|.
    get upgradeBackup() {
      let latestBackupID = SessionFileInternal.latestUpgradeBackupID;
      if (!latestBackupID) {
        return "";
      }
      return this.upgradeBackupPrefix + latestBackupID;
    },

    // The path to a backup created during an upgrade of Firefox.
    // Having this backup protects the user essentially from bugs in
    // Firefox, especially for users of Nightly.
    get nextUpgradeBackup() {
      return this.upgradeBackupPrefix + Services.appinfo.platformBuildID;
    },

    /**
     * The order in which to search for a valid sessionstore file.
     */
    get loadOrder() {
      // If `clean` exists and has been written without corruption during
      // the latest shutdown, we need to use it.
      //
      // Otherwise, `recovery` and `recoveryBackup` represent the most
      // recent state of the session store.
      //
      // Finally, if nothing works, fall back to the last known state
      // that can be loaded (`cleanBackup`) or, if available, to the
      // backup performed during the latest upgrade.
      let order = ["clean",
                   "recovery",
                   "recoveryBackup",
                   "cleanBackup"];
      if (SessionFileInternal.latestUpgradeBackupID) {
        // We have an upgradeBackup
        order.push("upgradeBackup");
      }
      return order;
    },
  }),

  // Number of attempted calls to `write`.
  // Note that we may have _attempts > _successes + _failures,
  // if attempts never complete.
  // Used for error reporting.
  _attempts: 0,

  // Number of successful calls to `write`.
  // Used for error reporting.
  _successes: 0,

  // Number of failed calls to `write`.
  // Used for error reporting.
  _failures: 0,

  // Object that keeps statistics that should help us make informed decisions
  // about the current status of the worker.
  _workerHealth: {
    failures: 0
  },

  // `true` once we have started initialization of the worker.
  _initializationStarted: false,

  // A string that will be set to the session file name part that was read from
  // disk. It will be available _after_ a session file read() is done.
  _readOrigin: null,

  // `true` if the old, uncompressed, file format was used to read from disk, as
  // a fallback mechanism.
  _usingOldExtension: false,

  // The ID of the latest version of Gecko for which we have an upgrade backup
  // or |undefined| if no upgrade backup was ever written.
  get latestUpgradeBackupID() {
    try {
      return Services.prefs.getCharPref(PREF_UPGRADE_BACKUP);
    } catch (ex) {
      return undefined;
    }
  },

  async _readInternal(useOldExtension) {
    let result;
    let noFilesFound = true;
    this._usingOldExtension = useOldExtension;

    // Attempt to load by order of priority from the various backups
    for (let key of this.Paths.loadOrder) {
      let corrupted = false;
      let exists = true;
      try {
        let path;
        let startMs = Date.now();

        let options = {encoding: "utf-8"};
        if (useOldExtension) {
          path = this.Paths[key]
                     .replace("jsonlz4", "js")
                     .replace("baklz4", "bak");
        } else {
          path = this.Paths[key];
          options.compression = "lz4";
        }
        let source = await OS.File.read(path, options);
        let parsed = JSON.parse(source);

        if (!SessionStore.isFormatVersionCompatible(parsed.version || ["sessionrestore", 0] /* fallback for old versions*/)) {
          // Skip sessionstore files that we don't understand.
          Cu.reportError("Cannot extract data from Session Restore file " + path +
            ". Wrong format/version: " + JSON.stringify(parsed.version) + ".");
          continue;
        }
        result = {
          origin: key,
          source,
          parsed,
          useOldExtension
        };
        Telemetry.getHistogramById("FX_SESSION_RESTORE_CORRUPT_FILE").
          add(false);
        Telemetry.getHistogramById("FX_SESSION_RESTORE_READ_FILE_MS").
          add(Date.now() - startMs);
        break;
      } catch (ex) {
          if (ex instanceof OS.File.Error && ex.becauseNoSuchFile) {
            exists = false;
          } else if (ex instanceof OS.File.Error) {
            // The file might be inaccessible due to wrong permissions
            // or similar failures. We'll just count it as "corrupted".
            console.error("Could not read session file ", ex, ex.stack);
            corrupted = true;
          } else if (ex instanceof SyntaxError) {
            console.error("Corrupt session file (invalid JSON found) ", ex, ex.stack);
            // File is corrupted, try next file
            corrupted = true;
          }
      } finally {
        if (exists) {
          noFilesFound = false;
          Telemetry.getHistogramById("FX_SESSION_RESTORE_CORRUPT_FILE").
            add(corrupted);
        }
      }
    }
    return {result, noFilesFound};
  },

  // Find the correct session file, read it and setup the worker.
  async read() {
    // Load session files with lz4 compression.
    let {result, noFilesFound} = await this._readInternal(false);
    if (!result) {
      // No result? Probably because of migration, let's
      // load uncompressed session files.
      let r = await this._readInternal(true);
      result = r.result;
    }

    // All files are corrupted if files found but none could deliver a result.
    let allCorrupt = !noFilesFound && !result;
    Telemetry.getHistogramById("FX_SESSION_RESTORE_ALL_FILES_CORRUPT").
      add(allCorrupt);

    if (!result) {
      // If everything fails, start with an empty session.
      result = {
        origin: "empty",
        source: "",
        parsed: null,
        useOldExtension: false
      };
    }
    this._readOrigin = result.origin;

    result.noFilesFound = noFilesFound;

    // Initialize the worker (in the background) to let it handle backups and also
    // as a workaround for bug 964531.
    this._initWorker();

    return result;
  },

  // Initialize the worker in the background.
  // Since this called _before_ any other messages are posted to the worker (see
  // `_postToWorker()`), we know that this initialization process will be completed
  // on time.
  // Thus, effectively, this blocks callees on its completion.
  // In case of a worker crash/ shutdown during its initialization phase,
  // `_checkWorkerHealth()` will detect it and flip the `_initializationStarted`
  // property back to `false`. This means that we'll respawn the worker upon the
  // next request, followed by the initialization sequence here. In other words;
  // exactly the same procedure as when the worker crashed/ shut down 'regularly'.
  //
  // This will never throw an error.
  _initWorker() {
    return new Promise(resolve => {
      if (this._initializationStarted) {
        resolve();
        return;
      }

      if (!this._readOrigin) {
        throw new Error("_initWorker called too early! Please read the session file from disk first.");
      }

      this._initializationStarted = true;
      SessionWorker.post("init", [this._readOrigin, this._usingOldExtension, this.Paths, {
        maxUpgradeBackups: Services.prefs.getIntPref(PREF_MAX_UPGRADE_BACKUPS, 3),
        maxSerializeBack: Services.prefs.getIntPref(PREF_MAX_SERIALIZE_BACK, 10),
        maxSerializeForward: Services.prefs.getIntPref(PREF_MAX_SERIALIZE_FWD, -1)
      }]).catch(err => {
        // Ensure that we report errors but that they do not stop us.
        Promise.reject(err);
      }).then(resolve);
    });
  },

  // Post a message to the worker, making sure that it has been initialized first.
  async _postToWorker(...args) {
    await this._initWorker();
    return SessionWorker.post(...args);
  },

  /**
   * For good measure, terminate the worker when we've had over `kMaxWriteFailures`
   * amount of failures to deal with. This will spawn a fresh worker upon the next
   * write.
   * This also resets the `_workerHealth` stats.
   */
  _checkWorkerHealth() {
    if (this._workerHealth.failures >= kMaxWriteFailures) {
      SessionWorker.terminate();
      // Flag as not-initialized, to ensure that the worker state init is performed
      // upon the next request.
      this._initializationStarted = false;
      // Reset the counter and report to telemetry.
      this._workerHealth.failures = 0;
      Telemetry.scalarAdd("browser.session.restore.worker_restart_count", 1);
    }
  },

  write(aData) {
    if (RunState.isClosed) {
      return Promise.reject(new Error("SessionFile is closed"));
    }

    let isFinalWrite = false;
    if (RunState.isClosing) {
      // If shutdown has started, we will want to stop receiving
      // write instructions.
      isFinalWrite = true;
      RunState.setClosed();
    }

    let performShutdownCleanup = isFinalWrite &&
      !sessionStartup.isAutomaticRestoreEnabled();

    this._attempts++;
    let options = {isFinalWrite, performShutdownCleanup};
    let promise = this._postToWorker("write", [aData, options]);

    // Wait until the write is done.
    promise = promise.then(msg => {
      // Record how long the write took.
      this._recordTelemetry(msg.telemetry);
      this._successes++;
      if (msg.result.upgradeBackup) {
        // We have just completed a backup-on-upgrade, store the information
        // in preferences.
        Services.prefs.setCharPref(PREF_UPGRADE_BACKUP,
          Services.appinfo.platformBuildID);
      }
    }, err => {
      // Catch and report any errors.
      console.error("Could not write session state file ", err, err.stack);
      this._failures++;
      this._workerHealth.failures++;
      // By not doing anything special here we ensure that |promise| cannot
      // be rejected anymore. The shutdown/cleanup code at the end of the
      // function will thus always be executed.
    });

    // Ensure that we can write sessionstore.js cleanly before the profile
    // becomes unaccessible.
    AsyncShutdown.profileBeforeChange.addBlocker(
      "SessionFile: Finish writing Session Restore data",
      promise,
      {
        fetchState: () => ({
          options,
          attempts: this._attempts,
          successes: this._successes,
          failures: this._failures,
        })
      });

    // This code will always be executed because |promise| can't fail anymore.
    // We ensured that by having a reject handler that reports the failure but
    // doesn't forward the rejection.
    return promise.then(() => {
      // Remove the blocker, no matter if writing failed or not.
      AsyncShutdown.profileBeforeChange.removeBlocker(promise);

      if (isFinalWrite) {
        Services.obs.notifyObservers(null, "sessionstore-final-state-write-complete");
      } else {
        this._checkWorkerHealth();
      }
    });
  },

  wipe() {
    return this._postToWorker("wipe").then(() => {
      // After a wipe, we need to make sure to re-initialize upon the next read(),
      // because the state variables as sent to the worker have changed.
      this._initializationStarted = false;
    });
  },

  _recordTelemetry(telemetry) {
    for (let id of Object.keys(telemetry)) {
      let value = telemetry[id];
      let samples = [];
      if (Array.isArray(value)) {
        samples.push(...value);
      } else {
        samples.push(value);
      }
      let histogram = Telemetry.getHistogramById(id);
      for (let sample of samples) {
        histogram.add(sample);
      }
    }
  }
};
PK
!<���
�
)modules/sessionstore/SessionMigration.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = ["SessionMigration"];

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", this);
ChromeUtils.import("resource://gre/modules/osfile.jsm", this);

ChromeUtils.defineModuleGetter(this, "Utils",
  "resource://gre/modules/sessionstore/Utils.jsm");

// An encoder to UTF-8.
XPCOMUtils.defineLazyGetter(this, "gEncoder", function() {
  return new TextEncoder();
});

// A decoder.
XPCOMUtils.defineLazyGetter(this, "gDecoder", function() {
  return new TextDecoder();
});

var SessionMigrationInternal = {
  /**
   * Convert the original session restore state into a minimal state. It will
   * only contain:
   * - open windows
   *   - with tabs
   *     - with history entries with only title, url, triggeringPrincipal
   *     - with pinned state
   *     - with tab group info (hidden + group id)
   *     - with selected tab info
   *   - with selected window info
   *
   * The complete state is then wrapped into the "about:welcomeback" page as
   * form field info to be restored when restoring the state.
   */
  convertState(aStateObj) {
    let state = {
      selectedWindow: aStateObj.selectedWindow,
      _closedWindows: []
    };
    state.windows = aStateObj.windows.map(function(oldWin) {
      var win = {extData: {}};
      win.tabs = oldWin.tabs.map(function(oldTab) {
        var tab = {};
        // Keep only titles, urls and triggeringPrincipals for history entries
        tab.entries = oldTab.entries.map(function(entry) {
          return { url: entry.url,
                   triggeringPrincipal_base64: entry.triggeringPrincipal_base64,
                   title: entry.title };
        });
        tab.index = oldTab.index;
        tab.hidden = oldTab.hidden;
        tab.pinned = oldTab.pinned;
        return tab;
      });
      win.selected = oldWin.selected;
      win._closedTabs = [];
      return win;
    });
    let url = "about:welcomeback";
    let formdata = {id: {sessionData: state}, url};
    let entry = { url, triggeringPrincipal_base64: Utils.SERIALIZED_SYSTEMPRINCIPAL };
    return { windows: [{ tabs: [{ entries: [ entry ], formdata}]}]};
  },
  /**
   * Asynchronously read session restore state (JSON) from a path
   */
  readState(aPath) {
    return (async function() {
      let bytes = await OS.File.read(aPath, {compression: "lz4"});
      let text = gDecoder.decode(bytes);
      let state = JSON.parse(text);
      return state;
    })();
  },
  /**
   * Asynchronously write session restore state as JSON to a path
   */
  writeState(aPath, aState) {
    let bytes = gEncoder.encode(JSON.stringify(aState));
    return OS.File.writeAtomic(aPath, bytes, {tmpPath: aPath + ".tmp", compression: "lz4"});
  }
};

var SessionMigration = {
  /**
   * Migrate a limited set of session data from one path to another.
   */
  migrate(aFromPath, aToPath) {
    return (async function() {
      let inState = await SessionMigrationInternal.readState(aFromPath);
      let outState = SessionMigrationInternal.convertState(inState);
      // Unfortunately, we can't use SessionStore's own SessionFile to
      // write out the data because it has a dependency on the profile dir
      // being known. When the migration runs, there is no guarantee that
      // that's true.
      await SessionMigrationInternal.writeState(aToPath, outState);
    })();
  }
};
PK
!<I)�2�2%modules/sessionstore/SessionSaver.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = ["SessionSaver"];

ChromeUtils.import("resource://gre/modules/Timer.jsm", this);
ChromeUtils.import("resource://gre/modules/Services.jsm", this);
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", this);
ChromeUtils.import("resource://gre/modules/TelemetryStopwatch.jsm", this);

XPCOMUtils.defineLazyModuleGetters(this, {
  AppConstants: "resource://gre/modules/AppConstants.jsm",
  PrivacyFilter: "resource:///modules/sessionstore/PrivacyFilter.jsm",
  RunState: "resource:///modules/sessionstore/RunState.jsm",
  SessionStore: "resource:///modules/sessionstore/SessionStore.jsm",
  SessionFile: "resource:///modules/sessionstore/SessionFile.jsm",
  PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
});

/*
 * Minimal interval between two save operations (in milliseconds).
 *
 * To save system resources, we generally do not save changes immediately when
 * a change is detected. Rather, we wait a little to see if this change is
 * followed by other changes, in which case only the last write is necessary.
 * This delay is defined by "browser.sessionstore.interval".
 *
 * Furthermore, when the user is not actively using the computer, webpages
 * may still perform changes that require (re)writing to sessionstore, e.g.
 * updating Session Cookies or DOM Session Storage, or refreshing, etc. We
 * expect that these changes are much less critical to the user and do not
 * need to be saved as often. In such cases, we increase the delay to
 *  "browser.sessionstore.interval.idle".
 *
 * When the user returns to the computer, if a save is pending, we reschedule
 * it to happen soon, with "browser.sessionstore.interval".
 */
const PREF_INTERVAL_ACTIVE = "browser.sessionstore.interval";
const PREF_INTERVAL_IDLE = "browser.sessionstore.interval.idle";
const PREF_IDLE_DELAY = "browser.sessionstore.idleDelay";

// Notify observers about a given topic with a given subject.
function notify(subject, topic) {
  Services.obs.notifyObservers(subject, topic);
}

// TelemetryStopwatch helper functions.
function stopWatch(method) {
  return function(...histograms) {
    for (let hist of histograms) {
      TelemetryStopwatch[method]("FX_SESSION_RESTORE_" + hist);
    }
  };
}

var stopWatchStart = stopWatch("start");
var stopWatchFinish = stopWatch("finish");

/**
 * The external API implemented by the SessionSaver module.
 */
var SessionSaver = Object.freeze({
  /**
   * Immediately saves the current session to disk.
   */
  run() {
    return SessionSaverInternal.run();
  },

  /**
   * Saves the current session to disk delayed by a given amount of time. Should
   * another delayed run be scheduled already, we will ignore the given delay
   * and state saving may occur a little earlier.
   */
  runDelayed() {
    SessionSaverInternal.runDelayed();
  },

  /**
   * Sets the last save time to the current time. This will cause us to wait for
   * at least the configured interval when runDelayed() is called next.
   */
  updateLastSaveTime() {
    SessionSaverInternal.updateLastSaveTime();
  },

  /**
   * Cancels all pending session saves.
   */
  cancel() {
    SessionSaverInternal.cancel();
  }
});

/**
 * The internal API.
 */
var SessionSaverInternal = {
  /**
   * The timeout ID referencing an active timer for a delayed save. When no
   * save is pending, this is null.
   */
  _timeoutID: null,

  /**
   * The idle callback ID referencing an active idle callback. When no idle
   * callback is pending, this is null.
   * */
  _idleCallbackID: null,

  /**
   * A timestamp that keeps track of when we saved the session last. We will
   * this to determine the correct interval between delayed saves to not deceed
   * the configured session write interval.
   */
  _lastSaveTime: 0,

  /**
   * `true` if the user has been idle for at least
   * `SessionSaverInternal._intervalWhileIdle` ms. Idleness is computed
   * with `nsIIdleService`.
   */
  _isIdle: false,

  /**
   * `true` if the user was idle when we last scheduled a delayed save.
   * See `_isIdle` for details on idleness.
   */
  _wasIdle: false,

  /**
   * Minimal interval between two save operations (in ms), while the user
   * is active.
   */
  _intervalWhileActive: null,

  /**
   * Minimal interval between two save operations (in ms), while the user
   * is idle.
   */
  _intervalWhileIdle: null,

  /**
   * How long before we assume that the user is idle (ms).
   */
  _idleDelay: null,

  /**
   * Immediately saves the current session to disk.
   */
  run() {
    return this._saveState(true /* force-update all windows */);
  },

  /**
   * Saves the current session to disk delayed by a given amount of time. Should
   * another delayed run be scheduled already, we will ignore the given delay
   * and state saving may occur a little earlier.
   *
   * @param delay (optional)
   *        The minimum delay in milliseconds to wait for until we collect and
   *        save the current session.
   */
  runDelayed(delay = 2000) {
    // Bail out if there's a pending run.
    if (this._timeoutID) {
      return;
    }

    // Interval until the next disk operation is allowed.
    let interval = this._isIdle ? this._intervalWhileIdle : this._intervalWhileActive;
    delay = Math.max(this._lastSaveTime + interval - Date.now(), delay, 0);

    // Schedule a state save.
    this._wasIdle = this._isIdle;
    this._timeoutID = setTimeout(() => {
      let hiddenDOMWindow = Services.appShell.hiddenDOMWindow;

      // Execute _saveStateAsync when we have enough idle time. Otherwise,
      // another idle request is made to schedule _saveStateAsync again.
      let saveStateAsyncWhenIdle = (deadline) => {
        // When looking at the telemetry data, the time it takes to execute
        // _saveStateAsync is around 5.9ms (median). Therefore,
        // we'll not execute the function when the idle time is less than 5ms.
        if (deadline.timeRemaining() < 5) {
          this._idleCallbackID = hiddenDOMWindow.requestIdleCallback(saveStateAsyncWhenIdle);
          return;
        }
        this._saveStateAsync();
      };

      this._idleCallbackID = hiddenDOMWindow.requestIdleCallback(saveStateAsyncWhenIdle);
    }, delay);
  },

  /**
   * Sets the last save time to the current time. This will cause us to wait for
   * at least the configured interval when runDelayed() is called next.
   */
  updateLastSaveTime() {
    this._lastSaveTime = Date.now();
  },

  /**
   * Cancels all pending session saves.
   */
  cancel() {
    clearTimeout(this._timeoutID);
    this._timeoutID = null;
    Services.appShell.hiddenDOMWindow.cancelIdleCallback(this._idleCallbackID);
    this._idleCallbackID = null;
  },

  /**
   * Observe idle/ active notifications.
   */
  observe(subject, topic, data) {
    switch (topic) {
      case "idle":
        this._isIdle = true;
        break;
      case "active":
        this._isIdle = false;
        if (this._timeoutID && this._wasIdle) {
          // A state save has been scheduled while we were idle.
          // Replace it by an active save.
          clearTimeout(this._timeoutID);
          this._timeoutID = null;
          this.runDelayed();
        }
        break;
      default:
        throw new Error(`Unexpected change value ${topic}`);
    }
  },

  /**
   * Saves the current session state. Collects data and writes to disk.
   *
   * @param forceUpdateAllWindows (optional)
   *        Forces us to recollect data for all windows and will bypass and
   *        update the corresponding caches.
   */
  _saveState(forceUpdateAllWindows = false) {
    // Cancel any pending timeouts.
    this.cancel();

    if (PrivateBrowsingUtils.permanentPrivateBrowsing) {
      // Don't save (or even collect) anything in permanent private
      // browsing mode

      this.updateLastSaveTime();
      return Promise.resolve();
    }

    stopWatchStart("COLLECT_DATA_MS");
    let state = SessionStore.getCurrentState(forceUpdateAllWindows);
    PrivacyFilter.filterPrivateWindowsAndTabs(state);

    // Make sure we only write worth saving tabs to disk.
    SessionStore.keepOnlyWorthSavingTabs(state);

    // Make sure that we keep the previous session if we started with a single
    // private window and no non-private windows have been opened, yet.
    if (state.deferredInitialState) {
      state.windows = state.deferredInitialState.windows || [];
      delete state.deferredInitialState;
    }

    if (AppConstants.platform != "macosx") {
      // We want to restore closed windows that are marked with _shouldRestore.
      // We're doing this here because we want to control this only when saving
      // the file.
      while (state._closedWindows.length) {
        let i = state._closedWindows.length - 1;

        if (!state._closedWindows[i]._shouldRestore) {
          // We only need to go until _shouldRestore
          // is falsy since we're going in reverse.
          break;
        }

        delete state._closedWindows[i]._shouldRestore;
        state.windows.unshift(state._closedWindows.pop());
      }
    }

    // Clear cookies and storage on clean shutdown.
    this._maybeClearCookiesAndStorage(state);

    stopWatchFinish("COLLECT_DATA_MS");
    return this._writeState(state);
  },

  /**
   * Purges cookies and DOMSessionStorage data from the session on clean
   * shutdown, only if requested by the user's preferences.
   */
  _maybeClearCookiesAndStorage(state) {
    // Only do this on shutdown.
    if (!RunState.isClosing) {
      return;
    }

    // Don't clear when restarting.
    if (Services.prefs.getBoolPref("browser.sessionstore.resume_session_once")) {
      return;
    }

    let expireCookies = Services.prefs.getIntPref("network.cookie.lifetimePolicy") ==
                        Services.cookies.QueryInterface(Ci.nsICookieService).ACCEPT_SESSION;
    let sanitizeCookies = Services.prefs.getBoolPref("privacy.sanitize.sanitizeOnShutdown") &&
                          Services.prefs.getBoolPref("privacy.clearOnShutdown.cookies");

    if (expireCookies || sanitizeCookies) {
      // Remove cookies.
      delete state.cookies;

      // Remove DOMSessionStorage data.
      for (let window of state.windows) {
        for (let tab of window.tabs) {
          delete tab.storage;
        }
      }
    }
  },

  /**
   * Saves the current session state. Collects data asynchronously and calls
   * _saveState() to collect data again (with a cache hit rate of hopefully
   * 100%) and write to disk afterwards.
   */
  _saveStateAsync() {
    // Allow scheduling delayed saves again.
    this._timeoutID = null;

    // Write to disk.
    this._saveState();
  },

  /**
   * Write the given state object to disk.
   */
  _writeState(state) {
    // We update the time stamp before writing so that we don't write again
    // too soon, if saving is requested before the write completes. Without
    // this update we may save repeatedly if actions cause a runDelayed
    // before writing has completed. See Bug 902280
    this.updateLastSaveTime();

    // Write (atomically) to a session file, using a tmp file. Once the session
    // file is successfully updated, save the time stamp of the last save and
    // notify the observers.
    return SessionFile.write(state).then(() => {
      this.updateLastSaveTime();
      notify(null, "sessionstore-state-write-complete");
    }, console.error);
  },
};

XPCOMUtils.defineLazyPreferenceGetter(SessionSaverInternal, "_intervalWhileActive", PREF_INTERVAL_ACTIVE,
  15000 /* 15 seconds */, () => {
  // Cancel any pending runs and call runDelayed() with
  // zero to apply the newly configured interval.
  SessionSaverInternal.cancel();
  SessionSaverInternal.runDelayed(0);
});

XPCOMUtils.defineLazyPreferenceGetter(SessionSaverInternal, "_intervalWhileIdle", PREF_INTERVAL_IDLE,
  3600000 /* 1 h */);

XPCOMUtils.defineLazyPreferenceGetter(SessionSaverInternal, "_idleDelay", PREF_IDLE_DELAY,
  180000 /* 3 minutes */, (key, previous, latest) => {
  // Update the idle observer for the new `PREF_IDLE_DELAY` value. Here we need
  // to re-fetch the service instead of the original one in use; This is for a
  // case that the Mock service in the unit test needs to be fetched to
  // replace the original one.
  var idleService = Cc["@mozilla.org/widget/idleservice;1"].getService(Ci.nsIIdleService);
  if (previous != undefined) {
    idleService.removeIdleObserver(SessionSaverInternal, previous);
  }
  if (latest != undefined) {
    idleService.addIdleObserver(SessionSaverInternal, latest);
  }
});

var idleService = Cc["@mozilla.org/widget/idleservice;1"].getService(Ci.nsIIdleService);
idleService.addIdleObserver(SessionSaverInternal, SessionSaverInternal._idleDelay);
PK
!<g�s�s�%modules/sessionstore/SessionStore.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = ["SessionStore"];

// Current version of the format used by Session Restore.
const FORMAT_VERSION = 1;

const TAB_STATE_NEEDS_RESTORE = 1;
const TAB_STATE_RESTORING = 2;
const TAB_STATE_WILL_RESTORE = 3;

// A new window has just been restored. At this stage, tabs are generally
// not restored.
const NOTIFY_SINGLE_WINDOW_RESTORED = "sessionstore-single-window-restored";
const NOTIFY_WINDOWS_RESTORED = "sessionstore-windows-restored";
const NOTIFY_BROWSER_STATE_RESTORED = "sessionstore-browser-state-restored";
const NOTIFY_LAST_SESSION_CLEARED = "sessionstore-last-session-cleared";
const NOTIFY_RESTORING_ON_STARTUP = "sessionstore-restoring-on-startup";
const NOTIFY_INITIATING_MANUAL_RESTORE = "sessionstore-initiating-manual-restore";
const NOTIFY_CLOSED_OBJECTS_CHANGED = "sessionstore-closed-objects-changed";

const NOTIFY_TAB_RESTORED = "sessionstore-debug-tab-restored"; // WARNING: debug-only
const NOTIFY_DOMWINDOWCLOSED_HANDLED = "sessionstore-debug-domwindowclosed-handled"; // WARNING: debug-only

// Maximum number of tabs to restore simultaneously. Previously controlled by
// the browser.sessionstore.max_concurrent_tabs pref.
const MAX_CONCURRENT_TAB_RESTORES = 3;

// Amount (in CSS px) by which we allow window edges to be off-screen
// when restoring a window, before we override the saved position to
// pull the window back within the available screen area.
const SCREEN_EDGE_SLOP = 8;

// global notifications observed
const OBSERVING = [
  "browser-window-before-show", "domwindowclosed",
  "quit-application-granted", "browser-lastwindow-close-granted",
  "quit-application", "browser:purge-session-history",
  "browser:purge-domain-data",
  "idle-daily", "clear-origin-attributes-data"
];

// XUL Window properties to (re)store
// Restored in restoreDimensions()
const WINDOW_ATTRIBUTES = ["width", "height", "screenX", "screenY", "sizemode"];

// Hideable window features to (re)store
// Restored in restoreWindowFeatures()
const WINDOW_HIDEABLE_FEATURES = [
  "menubar", "toolbar", "locationbar", "personalbar", "statusbar", "scrollbars"
];

// Messages that will be received via the Frame Message Manager.
const MESSAGES = [
  // The content script sends us data that has been invalidated and needs to
  // be saved to disk.
  "SessionStore:update",

  // The restoreHistory code has run. This is a good time to run SSTabRestoring.
  "SessionStore:restoreHistoryComplete",

  // The load for the restoring tab has begun. We update the URL bar at this
  // time; if we did it before, the load would overwrite it.
  "SessionStore:restoreTabContentStarted",

  // All network loads for a restoring tab are done, so we should
  // consider restoring another tab in the queue. The document has
  // been restored, and forms have been filled. We trigger
  // SSTabRestored at this time.
  "SessionStore:restoreTabContentComplete",

  // A crashed tab was revived by navigating to a different page. Remove its
  // browser from the list of crashed browsers to stop ignoring its messages.
  "SessionStore:crashedTabRevived",

  // The content script encountered an error.
  "SessionStore:error",
];

// The list of messages we accept from <xul:browser>s that have no tab
// assigned, or whose windows have gone away. Those are for example the
// ones that preload about:newtab pages, or from browsers where the window
// has just been closed.
const NOTAB_MESSAGES = new Set([
  // For a description see above.
  "SessionStore:crashedTabRevived",

  // For a description see above.
  "SessionStore:update",

  // For a description see above.
  "SessionStore:error",
]);

// The list of messages we accept without an "epoch" parameter.
// See getCurrentEpoch() and friends to find out what an "epoch" is.
const NOEPOCH_MESSAGES = new Set([
  // For a description see above.
  "SessionStore:crashedTabRevived",

  // For a description see above.
  "SessionStore:error",
]);

// The list of messages we want to receive even during the short period after a
// frame has been removed from the DOM and before its frame script has finished
// unloading.
const CLOSED_MESSAGES = new Set([
  // For a description see above.
  "SessionStore:crashedTabRevived",

  // For a description see above.
  "SessionStore:update",

  // For a description see above.
  "SessionStore:error",
]);

// These are tab events that we listen to.
const TAB_EVENTS = [
  "TabOpen", "TabBrowserInserted", "TabClose", "TabSelect", "TabShow", "TabHide", "TabPinned",
  "TabUnpinned"
];

const NS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";

/**
 * When calling restoreTabContent, we can supply a reason why
 * the content is being restored. These are those reasons.
 */
const RESTORE_TAB_CONTENT_REASON = {
  /**
   * SET_STATE:
   * We're restoring this tab's content because we're setting
   * state inside this browser tab, probably because the user
   * has asked us to restore a tab (or window, or entire session).
   */
  SET_STATE: 0,
  /**
   * NAVIGATE_AND_RESTORE:
   * We're restoring this tab's content because a navigation caused
   * us to do a remoteness-flip.
   */
  NAVIGATE_AND_RESTORE: 1,
};

ChromeUtils.import("resource://gre/modules/PrivateBrowsingUtils.jsm", this);
ChromeUtils.import("resource://gre/modules/Services.jsm", this);
ChromeUtils.import("resource://gre/modules/TelemetryStopwatch.jsm", this);
ChromeUtils.import("resource://gre/modules/TelemetryTimestamps.jsm", this);
ChromeUtils.import("resource://gre/modules/Timer.jsm", this);
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", this);
ChromeUtils.import("resource://gre/modules/osfile.jsm", this);

XPCOMUtils.defineLazyServiceGetters(this, {
  gSessionStartup: ["@mozilla.org/browser/sessionstartup;1", "nsISessionStartup"],
  gScreenManager: ["@mozilla.org/gfx/screenmanager;1", "nsIScreenManager"],
  Telemetry: ["@mozilla.org/base/telemetry;1", "nsITelemetry"],
});

XPCOMUtils.defineLazyModuleGetters(this, {
  AppConstants: "resource://gre/modules/AppConstants.jsm",
  AsyncShutdown: "resource://gre/modules/AsyncShutdown.jsm",
  DevToolsShim: "chrome://devtools-shim/content/DevToolsShim.jsm",
  GlobalState: "resource:///modules/sessionstore/GlobalState.jsm",
  PrivacyFilter: "resource:///modules/sessionstore/PrivacyFilter.jsm",
  PromiseUtils: "resource://gre/modules/PromiseUtils.jsm",
  RecentWindow: "resource:///modules/RecentWindow.jsm",
  RunState: "resource:///modules/sessionstore/RunState.jsm",
  SessionCookies: "resource:///modules/sessionstore/SessionCookies.jsm",
  SessionFile: "resource:///modules/sessionstore/SessionFile.jsm",
  SessionSaver: "resource:///modules/sessionstore/SessionSaver.jsm",
  TabAttributes: "resource:///modules/sessionstore/TabAttributes.jsm",
  TabCrashHandler: "resource:///modules/ContentCrashHandlers.jsm",
  TabState: "resource:///modules/sessionstore/TabState.jsm",
  TabStateCache: "resource:///modules/sessionstore/TabStateCache.jsm",
  TabStateFlusher: "resource:///modules/sessionstore/TabStateFlusher.jsm",
  Utils: "resource://gre/modules/sessionstore/Utils.jsm",
  ViewSourceBrowser: "resource://gre/modules/ViewSourceBrowser.jsm",
  setTimeout: "resource://gre/modules/Timer.jsm",
});

/**
 * |true| if we are in debug mode, |false| otherwise.
 * Debug mode is controlled by preference browser.sessionstore.debug
 */
var gDebuggingEnabled = false;
function debug(aMsg) {
  if (gDebuggingEnabled) {
    aMsg = ("SessionStore: " + aMsg).replace(/\S{80}/g, "$&\n");
    Services.console.logStringMessage(aMsg);
  }
}

/**
 * A global value to tell that fingerprinting resistance is enabled or not.
 * If it's enabled, the session restore won't restore the window's size and
 * size mode.
 * This value is controlled by preference privacy.resistFingerprinting.
 */
var gResistFingerprintingEnabled = false;

var SessionStore = {
  get promiseInitialized() {
    return SessionStoreInternal.promiseInitialized;
  },

  get promiseAllWindowsRestored() {
    return SessionStoreInternal.promiseAllWindowsRestored;
  },

  get canRestoreLastSession() {
    return SessionStoreInternal.canRestoreLastSession;
  },

  set canRestoreLastSession(val) {
    SessionStoreInternal.canRestoreLastSession = val;
  },

  get lastClosedObjectType() {
    return SessionStoreInternal.lastClosedObjectType;
  },

  init: function ss_init() {
    SessionStoreInternal.init();
  },

  getBrowserState: function ss_getBrowserState() {
    return SessionStoreInternal.getBrowserState();
  },

  setBrowserState: function ss_setBrowserState(aState) {
    SessionStoreInternal.setBrowserState(aState);
  },

  getWindowState: function ss_getWindowState(aWindow) {
    return SessionStoreInternal.getWindowState(aWindow);
  },

  setWindowState: function ss_setWindowState(aWindow, aState, aOverwrite) {
    SessionStoreInternal.setWindowState(aWindow, aState, aOverwrite);
  },

  getTabState: function ss_getTabState(aTab) {
    return SessionStoreInternal.getTabState(aTab);
  },

  setTabState: function ss_setTabState(aTab, aState) {
    SessionStoreInternal.setTabState(aTab, aState);
  },

  duplicateTab: function ss_duplicateTab(aWindow, aTab, aDelta = 0, aRestoreImmediately = true) {
    return SessionStoreInternal.duplicateTab(aWindow, aTab, aDelta, aRestoreImmediately);
  },

  getClosedTabCount: function ss_getClosedTabCount(aWindow) {
    return SessionStoreInternal.getClosedTabCount(aWindow);
  },

  getClosedTabData: function ss_getClosedTabData(aWindow, aAsString = true) {
    return SessionStoreInternal.getClosedTabData(aWindow, aAsString);
  },

  undoCloseTab: function ss_undoCloseTab(aWindow, aIndex) {
    return SessionStoreInternal.undoCloseTab(aWindow, aIndex);
  },

  forgetClosedTab: function ss_forgetClosedTab(aWindow, aIndex) {
    return SessionStoreInternal.forgetClosedTab(aWindow, aIndex);
  },

  getClosedWindowCount: function ss_getClosedWindowCount() {
    return SessionStoreInternal.getClosedWindowCount();
  },

  getClosedWindowData: function ss_getClosedWindowData(aAsString = true) {
    return SessionStoreInternal.getClosedWindowData(aAsString);
  },

  undoCloseWindow: function ss_undoCloseWindow(aIndex) {
    return SessionStoreInternal.undoCloseWindow(aIndex);
  },

  forgetClosedWindow: function ss_forgetClosedWindow(aIndex) {
    return SessionStoreInternal.forgetClosedWindow(aIndex);
  },

  getWindowValue: function ss_getWindowValue(aWindow, aKey) {
    return SessionStoreInternal.getWindowValue(aWindow, aKey);
  },

  setWindowValue: function ss_setWindowValue(aWindow, aKey, aStringValue) {
    SessionStoreInternal.setWindowValue(aWindow, aKey, aStringValue);
  },

  deleteWindowValue: function ss_deleteWindowValue(aWindow, aKey) {
    SessionStoreInternal.deleteWindowValue(aWindow, aKey);
  },

  getTabValue: function ss_getTabValue(aTab, aKey) {
    return SessionStoreInternal.getTabValue(aTab, aKey);
  },

  setTabValue: function ss_setTabValue(aTab, aKey, aStringValue) {
    SessionStoreInternal.setTabValue(aTab, aKey, aStringValue);
  },

  deleteTabValue: function ss_deleteTabValue(aTab, aKey) {
    SessionStoreInternal.deleteTabValue(aTab, aKey);
  },

  getLazyTabValue(aTab, aKey) {
    return SessionStoreInternal.getLazyTabValue(aTab, aKey);
  },

  getGlobalValue: function ss_getGlobalValue(aKey) {
    return SessionStoreInternal.getGlobalValue(aKey);
  },

  setGlobalValue: function ss_setGlobalValue(aKey, aStringValue) {
    SessionStoreInternal.setGlobalValue(aKey, aStringValue);
  },

  deleteGlobalValue: function ss_deleteGlobalValue(aKey) {
    SessionStoreInternal.deleteGlobalValue(aKey);
  },

  persistTabAttribute: function ss_persistTabAttribute(aName) {
    SessionStoreInternal.persistTabAttribute(aName);
  },

  restoreLastSession: function ss_restoreLastSession() {
    SessionStoreInternal.restoreLastSession();
  },

  speculativeConnectOnTabHover(tab) {
    SessionStoreInternal.speculativeConnectOnTabHover(tab);
  },

  getCurrentState(aUpdateAll) {
    return SessionStoreInternal.getCurrentState(aUpdateAll);
  },

  reviveCrashedTab(aTab) {
    return SessionStoreInternal.reviveCrashedTab(aTab);
  },

  reviveAllCrashedTabs() {
    return SessionStoreInternal.reviveAllCrashedTabs();
  },

  navigateAndRestore(tab, loadArguments, historyIndex) {
    return SessionStoreInternal.navigateAndRestore(tab, loadArguments, historyIndex);
  },

  getSessionHistory(tab, updatedCallback) {
    return SessionStoreInternal.getSessionHistory(tab, updatedCallback);
  },

  undoCloseById(aClosedId) {
    return SessionStoreInternal.undoCloseById(aClosedId);
  },

  resetBrowserToLazyState(tab) {
    return SessionStoreInternal.resetBrowserToLazyState(tab);
  },

  /**
   * Determines whether the passed version number is compatible with
   * the current version number of the SessionStore.
   *
   * @param version The format and version of the file, as an array, e.g.
   * ["sessionrestore", 1]
   */
  isFormatVersionCompatible(version) {
    if (!version) {
      return false;
    }
    if (!Array.isArray(version)) {
      // Improper format.
      return false;
    }
    if (version[0] != "sessionrestore") {
      // Not a Session Restore file.
      return false;
    }
    let number = Number.parseFloat(version[1]);
    if (Number.isNaN(number)) {
      return false;
    }
    return number <= FORMAT_VERSION;
  },

  /**
   * Filters out not worth-saving tabs from a given browser state object.
   *
   * @param aState (object)
   *        The browser state for which we remove worth-saving tabs.
   *        The given object will be modified.
   */
  keepOnlyWorthSavingTabs(aState) {
    for (let i = aState.windows.length - 1; i >= 0; i--) {
      let win = aState.windows[i];
      for (let j = win.tabs.length - 1; j >= 0; j--) {
        let tab = win.tabs[j];
        if (!SessionStoreInternal._shouldSaveTab(tab)) {
          win.tabs.splice(j, 1);
          if (win.selected > j) {
            win.selected--;
          }
        }
      }
      if (!win.tabs.length) {
        aState.windows.splice(i, 1);
        if (aState.selectedWindow > i) {
          aState.selectedWindow--;
        }
      }
    }
  },
};

// Freeze the SessionStore object. We don't want anyone to modify it.
Object.freeze(SessionStore);

var SessionStoreInternal = {
  QueryInterface: XPCOMUtils.generateQI([
    Ci.nsIDOMEventListener,
    Ci.nsIObserver,
    Ci.nsISupportsWeakReference
  ]),

  _globalState: new GlobalState(),

  // A counter to be used to generate a unique ID for each closed tab or window.
  _nextClosedId: 0,

  // During the initial restore and setBrowserState calls tracks the number of
  // windows yet to be restored
  _restoreCount: -1,

  // For each <browser> element, records the current epoch.
  _browserEpochs: new WeakMap(),

  // Any browsers that fires the oop-browser-crashed event gets stored in
  // here - that way we know which browsers to ignore messages from (until
  // they get restored).
  _crashedBrowsers: new WeakSet(),

  // A map (xul:browser -> nsIFrameLoader) that maps a browser to the last
  // associated frameLoader we heard about.
  _lastKnownFrameLoader: new WeakMap(),

  // A map (xul:browser -> object) that maps a browser associated with a
  // recently closed tab to all its necessary state information we need to
  // properly handle final update message.
  _closedTabs: new WeakMap(),

  // A map (xul:browser -> object) that maps a browser associated with a
  // recently closed tab due to a window closure to the tab state information
  // that is being stored in _closedWindows for that tab.
  _closedWindowTabs: new WeakMap(),

  // A set of window data that has the potential to be saved in the _closedWindows
  // array for the session. We will remove window data from this set whenever
  // forgetClosedWindow is called for the window, or when session history is
  // purged, so that we don't accidentally save that data after the flush has
  // completed. Closed tabs use a more complicated mechanism for this particular
  // problem. When forgetClosedTab is called, the browser is removed from the
  // _closedTabs map, so its data is not recorded. In the purge history case,
  // the closedTabs array per window is overwritten so that once the flush is
  // complete, the tab would only ever add itself to an array that SessionStore
  // no longer cares about. Bug 1230636 has been filed to make the tab case
  // work more like the window case, which is more explicit, and easier to
  // reason about.
  _saveableClosedWindowData: new WeakSet(),

  // A map (xul:browser -> object) that maps a browser that is switching
  // remoteness via navigateAndRestore, to the loadArguments that were
  // most recently passed when calling navigateAndRestore.
  _remotenessChangingBrowsers: new WeakMap(),

  // whether a setBrowserState call is in progress
  _browserSetState: false,

  // time in milliseconds when the session was started (saved across sessions),
  // defaults to now if no session was restored or timestamp doesn't exist
  _sessionStartTime: Date.now(),

  // states for all currently opened windows
  _windows: {},

  // counter for creating unique window IDs
  _nextWindowID: 0,

  // states for all recently closed windows
  _closedWindows: [],

  // collection of session states yet to be restored
  _statesToRestore: {},

  // counts the number of crashes since the last clean start
  _recentCrashes: 0,

  // whether the last window was closed and should be restored
  _restoreLastWindow: false,

  // number of tabs currently restoring
  _tabsRestoringCount: 0,

  // When starting Firefox with a single private window, this is the place
  // where we keep the session we actually wanted to restore in case the user
  // decides to later open a non-private window as well.
  _deferredInitialState: null,

  // Keeps track of whether a notification needs to be sent that closed objects have changed.
  _closedObjectsChanged: false,

  // A promise resolved once initialization is complete
  _deferredInitialized: (function() {
    let deferred = {};

    deferred.promise = new Promise((resolve, reject) => {
      deferred.resolve = resolve;
      deferred.reject = reject;
    });

    return deferred;
  })(),

  // Whether session has been initialized
  _sessionInitialized: false,

  // A promise resolved once all windows are restored.
  _deferredAllWindowsRestored: (function() {
    let deferred = {};

    deferred.promise = new Promise((resolve, reject) => {
      deferred.resolve = resolve;
      deferred.reject = reject;
    });

    return deferred;
  })(),

  get promiseAllWindowsRestored() {
    return this._deferredAllWindowsRestored.promise;
  },

  // Promise that is resolved when we're ready to initialize
  // and restore the session.
  _promiseReadyForInitialization: null,

  // Keep busy state counters per window.
  _windowBusyStates: new WeakMap(),

  /**
   * A promise fulfilled once initialization is complete.
   */
  get promiseInitialized() {
    return this._deferredInitialized.promise;
  },

  get canRestoreLastSession() {
    return LastSession.canRestore;
  },

  set canRestoreLastSession(val) {
    // Cheat a bit; only allow false.
    if (!val) {
      LastSession.clear();
    }
  },

  /**
   * Returns a string describing the last closed object, either "tab" or "window".
   *
   * This was added to support the sessions.restore WebExtensions API.
   */
  get lastClosedObjectType() {
    if (this._closedWindows.length) {
      // Since there are closed windows, we need to check if there's a closed tab
      // in one of the currently open windows that was closed after the
      // last-closed window.
      let tabTimestamps = [];
      let windowsEnum = Services.wm.getEnumerator("navigator:browser");
      while (windowsEnum.hasMoreElements()) {
        let window = windowsEnum.getNext();
        let windowState = this._windows[window.__SSi];
        if (windowState && windowState._closedTabs[0]) {
          tabTimestamps.push(windowState._closedTabs[0].closedAt);
        }
      }
      if (!tabTimestamps.length ||
          (tabTimestamps.sort((a, b) => b - a)[0] < this._closedWindows[0].closedAt)) {
        return "window";
      }
    }
    return "tab";
  },

  /**
   * Initialize the sessionstore service.
   */
  init() {
    if (this._initialized) {
      throw new Error("SessionStore.init() must only be called once!");
    }

    TelemetryTimestamps.add("sessionRestoreInitialized");
    OBSERVING.forEach(function(aTopic) {
      Services.obs.addObserver(this, aTopic, true);
    }, this);

    this._initPrefs();
    this._initialized = true;

    Telemetry.getHistogramById("FX_SESSION_RESTORE_PRIVACY_LEVEL").add(
      Services.prefs.getIntPref("browser.sessionstore.privacy_level"));
  },

  /**
   * Initialize the session using the state provided by SessionStartup
   */
  initSession() {
    TelemetryStopwatch.start("FX_SESSION_RESTORE_STARTUP_INIT_SESSION_MS");
    let state;
    let ss = gSessionStartup;

    if (ss.doRestore() ||
        ss.sessionType == Ci.nsISessionStartup.DEFER_SESSION) {
      state = ss.state;
    }

    if (state) {
      try {
        // If we're doing a DEFERRED session, then we want to pull pinned tabs
        // out so they can be restored.
        if (ss.sessionType == Ci.nsISessionStartup.DEFER_SESSION) {
          let [iniState, remainingState] = this._prepDataForDeferredRestore(state);
          // If we have a iniState with windows, that means that we have windows
          // with app tabs to restore.
          if (iniState.windows.length) {
            // Move cookies over from the remaining state so that they're
            // restored right away, and pinned tabs will load correctly.
            iniState.cookies = remainingState.cookies;
            delete remainingState.cookies;
            state = iniState;
          } else {
            state = null;
          }

          if (remainingState.windows.length) {
            LastSession.setState(remainingState);
          }
        } else {
          // Get the last deferred session in case the user still wants to
          // restore it
          LastSession.setState(state.lastSessionState);

          if (ss.previousSessionCrashed) {
            this._recentCrashes = (state.session &&
                                   state.session.recentCrashes || 0) + 1;

            if (this._needsRestorePage(state, this._recentCrashes)) {
              // replace the crashed session with a restore-page-only session
              let url = "about:sessionrestore";
              let formdata = {id: {sessionData: state}, url};
              let entry = {url, triggeringPrincipal_base64: Utils.SERIALIZED_SYSTEMPRINCIPAL };
              state = { windows: [{ tabs: [{ entries: [entry], formdata }] }] };
            } else if (this._hasSingleTabWithURL(state.windows,
                                                 "about:welcomeback")) {
              // On a single about:welcomeback URL that crashed, replace about:welcomeback
              // with about:sessionrestore, to make clear to the user that we crashed.
              state.windows[0].tabs[0].entries[0].url = "about:sessionrestore";
              state.windows[0].tabs[0].entries[0].triggeringPrincipal_base64 = Utils.SERIALIZED_SYSTEMPRINCIPAL;
            }
          }

          // Update the session start time using the restored session state.
          this._updateSessionStartTime(state);

          // make sure that at least the first window doesn't have anything hidden
          delete state.windows[0].hidden;
          // Since nothing is hidden in the first window, it cannot be a popup
          delete state.windows[0].isPopup;
          // We don't want to minimize and then open a window at startup.
          if (state.windows[0].sizemode == "minimized")
            state.windows[0].sizemode = "normal";
          // clear any lastSessionWindowID attributes since those don't matter
          // during normal restore
          state.windows.forEach(function(aWindow) {
            delete aWindow.__lastSessionWindowID;
          });
        }
      } catch (ex) { debug("The session file is invalid: " + ex); }
    }

    // at this point, we've as good as resumed the session, so we can
    // clear the resume_session_once flag, if it's set
    if (!RunState.isQuitting &&
        this._prefBranch.getBoolPref("sessionstore.resume_session_once"))
      this._prefBranch.setBoolPref("sessionstore.resume_session_once", false);

    TelemetryStopwatch.finish("FX_SESSION_RESTORE_STARTUP_INIT_SESSION_MS");
    return state;
  },

  _initPrefs() {
    this._prefBranch = Services.prefs.getBranch("browser.");

    gDebuggingEnabled = this._prefBranch.getBoolPref("sessionstore.debug");

    Services.prefs.addObserver("browser.sessionstore.debug", () => {
      gDebuggingEnabled = this._prefBranch.getBoolPref("sessionstore.debug");
    });

    this._max_tabs_undo = this._prefBranch.getIntPref("sessionstore.max_tabs_undo");
    this._prefBranch.addObserver("sessionstore.max_tabs_undo", this, true);

    this._max_windows_undo = this._prefBranch.getIntPref("sessionstore.max_windows_undo");
    this._prefBranch.addObserver("sessionstore.max_windows_undo", this, true);

    this._restore_on_demand = this._prefBranch.getBoolPref("sessionstore.restore_on_demand");
    this._prefBranch.addObserver("sessionstore.restore_on_demand", this, true);

    gResistFingerprintingEnabled = Services.prefs.getBoolPref("privacy.resistFingerprinting");
    Services.prefs.addObserver("privacy.resistFingerprinting", this);
  },

  /**
   * Called on application shutdown, after notifications:
   * quit-application-granted, quit-application
   */
  _uninit: function ssi_uninit() {
    if (!this._initialized) {
      throw new Error("SessionStore is not initialized.");
    }

    // Prepare to close the session file and write the last state.
    RunState.setClosing();

    // save all data for session resuming
    if (this._sessionInitialized) {
      SessionSaver.run();
    }

    // clear out priority queue in case it's still holding refs
    TabRestoreQueue.reset();

    // Make sure to cancel pending saves.
    SessionSaver.cancel();
  },

  /**
   * Handle notifications
   */
  observe: function ssi_observe(aSubject, aTopic, aData) {
    switch (aTopic) {
      case "browser-window-before-show": // catch new windows
        this.onBeforeBrowserWindowShown(aSubject);
        break;
      case "domwindowclosed": // catch closed windows
        this.onClose(aSubject).then(() => {
          this._notifyOfClosedObjectsChange();
        });
        if (gDebuggingEnabled) {
          Services.obs.notifyObservers(null, NOTIFY_DOMWINDOWCLOSED_HANDLED);
        }
        break;
      case "quit-application-granted":
        let syncShutdown = aData == "syncShutdown";
        this.onQuitApplicationGranted(syncShutdown);
        break;
      case "browser-lastwindow-close-granted":
        this.onLastWindowCloseGranted();
        break;
      case "quit-application":
        this.onQuitApplication(aData);
        break;
      case "browser:purge-session-history": // catch sanitization
        this.onPurgeSessionHistory();
        this._notifyOfClosedObjectsChange();
        break;
      case "browser:purge-domain-data":
        this.onPurgeDomainData(aData);
        this._notifyOfClosedObjectsChange();
        break;
      case "nsPref:changed": // catch pref changes
        this.onPrefChange(aData);
        this._notifyOfClosedObjectsChange();
        break;
      case "idle-daily":
        this.onIdleDaily();
        this._notifyOfClosedObjectsChange();
        break;
      case "clear-origin-attributes-data":
        let userContextId = 0;
        try {
          userContextId = JSON.parse(aData).userContextId;
        } catch (e) {}
        if (userContextId)
          this._forgetTabsWithUserContextId(userContextId);
    }
  },

  /**
   * This method handles incoming messages sent by the session store content
   * script via the Frame Message Manager or Parent Process Message Manager,
   * and thus enables communication with OOP tabs.
   */
  receiveMessage(aMessage) {
    // If we got here, that means we're dealing with a frame message
    // manager message, so the target will be a <xul:browser>.
    var browser = aMessage.target;
    let win = browser.ownerGlobal;
    let tab = win ? win.gBrowser.getTabForBrowser(browser) : null;

    // Ensure we receive only specific messages from <xul:browser>s that
    // have no tab or window assigned, e.g. the ones that preload
    // about:newtab pages, or windows that have closed.
    if (!tab && !NOTAB_MESSAGES.has(aMessage.name)) {
      throw new Error(`received unexpected message '${aMessage.name}' ` +
                      `from a browser that has no tab or window`);
    }

    let data = aMessage.data || {};
    let hasEpoch = data.hasOwnProperty("epoch");

    // Most messages sent by frame scripts require to pass an epoch.
    if (!hasEpoch && !NOEPOCH_MESSAGES.has(aMessage.name)) {
      throw new Error(`received message '${aMessage.name}' without an epoch`);
    }

    // Ignore messages from previous epochs.
    if (hasEpoch && !this.isCurrentEpoch(browser, data.epoch)) {
      return;
    }

    switch (aMessage.name) {
      case "SessionStore:update":
        // |browser.frameLoader| might be empty if the browser was already
        // destroyed and its tab removed. In that case we still have the last
        // frameLoader we know about to compare.
        let frameLoader = browser.frameLoader ||
                          this._lastKnownFrameLoader.get(browser.permanentKey);

        // If the message isn't targeting the latest frameLoader discard it.
        if (frameLoader != aMessage.targetFrameLoader) {
          return;
        }

        if (aMessage.data.isFinal) {
          // If this the final message we need to resolve all pending flush
          // requests for the given browser as they might have been sent too
          // late and will never respond. If they have been sent shortly after
          // switching a browser's remoteness there isn't too much data to skip.
          TabStateFlusher.resolveAll(browser);
        } else if (aMessage.data.flushID) {
          // This is an update kicked off by an async flush request. Notify the
          // TabStateFlusher so that it can finish the request and notify its
          // consumer that's waiting for the flush to be done.
          TabStateFlusher.resolve(browser, aMessage.data.flushID);
        }

        // Ignore messages from <browser> elements that have crashed
        // and not yet been revived.
        if (this._crashedBrowsers.has(browser.permanentKey)) {
          return;
        }

        // Update the tab's cached state.
        // Mark the window as dirty and trigger a delayed write.
        TabState.update(browser, aMessage.data);
        this.saveStateDelayed(win);

        // Handle any updates sent by the child after the tab was closed. This
        // might be the final update as sent by the "unload" handler but also
        // any async update message that was sent before the child unloaded.
        if (this._closedTabs.has(browser.permanentKey)) {
          let {closedTabs, tabData} = this._closedTabs.get(browser.permanentKey);

          // Update the closed tab's state. This will be reflected in its
          // window's list of closed tabs as that refers to the same object.
          TabState.copyFromCache(browser, tabData.state);

          // Is this the tab's final message?
          if (aMessage.data.isFinal) {
            // We expect no further updates.
            this._closedTabs.delete(browser.permanentKey);
            // The tab state no longer needs this reference.
            delete tabData.permanentKey;

            // Determine whether the tab state is worth saving.
            let shouldSave = this._shouldSaveTabState(tabData.state);
            let index = closedTabs.indexOf(tabData);

            if (shouldSave && index == -1) {
              // If the tab state is worth saving and we didn't push it onto
              // the list of closed tabs when it was closed (because we deemed
              // the state not worth saving) then add it to the window's list
              // of closed tabs now.
              this.saveClosedTabData(closedTabs, tabData);
            } else if (!shouldSave && index > -1) {
              // Remove from the list of closed tabs. The update messages sent
              // after the tab was closed changed enough state so that we no
              // longer consider its data interesting enough to keep around.
              this.removeClosedTabData(closedTabs, index);
            }
          }
        }
        break;
      case "SessionStore:restoreHistoryComplete": {
        // Notify the tabbrowser that the tab chrome has been restored.
        let tabData = TabState.collect(tab);

        // wall-paper fix for bug 439675: make sure that the URL to be loaded
        // is always visible in the address bar if no other value is present
        let activePageData = tabData.entries[tabData.index - 1] || null;
        let uri = activePageData ? activePageData.url || null : null;
        // NB: we won't set initial URIs (about:home, about:newtab, etc.) here
        // because their load will not normally trigger a location bar clearing
        // when they finish loading (to avoid race conditions where we then
        // clear user input instead), so we shouldn't set them here either.
        // They also don't fall under the issues in bug 439675 where user input
        // needs to be preserved if the load doesn't succeed.
        // We also don't do this for remoteness updates, where it should not
        // be necessary.
        if (!browser.userTypedValue && uri && !data.isRemotenessUpdate &&
            !win.gInitialPages.includes(uri)) {
          browser.userTypedValue = uri;
        }

        // Update tab label and icon again after the tab history was updated.
        this.updateTabLabelAndIcon(tab, tabData);

        let event = win.document.createEvent("Events");
        event.initEvent("SSTabRestoring", true, false);
        tab.dispatchEvent(event);
        break;
      }
      case "SessionStore:restoreTabContentStarted":
        if (browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE) {
          // If a load not initiated by sessionstore was started in a
          // previously pending tab. Mark the tab as no longer pending.
          this.markTabAsRestoring(tab);
        } else if (data.reason != RESTORE_TAB_CONTENT_REASON.NAVIGATE_AND_RESTORE) {
          // If the user was typing into the URL bar when we crashed, but hadn't hit
          // enter yet, then we just need to write that value to the URL bar without
          // loading anything. This must happen after the load, as the load will clear
          // userTypedValue.
          //
          // Note that we only want to do that if we're restoring state for reasons
          // _other_ than a navigateAndRestore remoteness-flip, as such a flip
          // implies that the user was navigating.
          let tabData = TabState.collect(tab);
          if (tabData.userTypedValue && !tabData.userTypedClear && !browser.userTypedValue) {
            browser.userTypedValue = tabData.userTypedValue;
            win.URLBarSetURI();
          }

          // Remove state we don't need any longer.
          TabStateCache.update(browser, {
            userTypedValue: null, userTypedClear: null
          });
        }
        break;
      case "SessionStore:restoreTabContentComplete":
        // This callback is used exclusively by tests that want to
        // monitor the progress of network loads.
        if (gDebuggingEnabled) {
          Services.obs.notifyObservers(browser, NOTIFY_TAB_RESTORED);
        }

        SessionStoreInternal._resetLocalTabRestoringState(tab);
        SessionStoreInternal.restoreNextTab();

        this._sendTabRestoredNotification(tab, data.isRemotenessUpdate);

        Services.obs.notifyObservers(null, "sessionstore-one-or-no-tab-restored");
        break;
      case "SessionStore:crashedTabRevived":
        // The browser was revived by navigating to a different page
        // manually, so we remove it from the ignored browser set.
        this._crashedBrowsers.delete(browser.permanentKey);
        break;
      case "SessionStore:error":
        TabStateFlusher.resolveAll(browser, false, "Received error from the content process");
        break;
      default:
        throw new Error(`received unknown message '${aMessage.name}'`);
    }
  },

  /* ........ Window Event Handlers .............. */

  /**
   * Implement nsIDOMEventListener for handling various window and tab events
   */
  handleEvent: function ssi_handleEvent(aEvent) {
    let win = aEvent.currentTarget.ownerGlobal;
    let target = aEvent.originalTarget;
    switch (aEvent.type) {
      case "TabOpen":
        this.onTabAdd(win);
        break;
      case "TabBrowserInserted":
        this.onTabBrowserInserted(win, target);
        break;
      case "TabClose":
        // `adoptedBy` will be set if the tab was closed because it is being
        // moved to a new window.
        if (!aEvent.detail.adoptedBy)
          this.onTabClose(win, target);
        this.onTabRemove(win, target);
        this._notifyOfClosedObjectsChange();
        break;
      case "TabSelect":
        this.onTabSelect(win);
        break;
      case "TabShow":
        this.onTabShow(win, target);
        break;
      case "TabHide":
        this.onTabHide(win, target);
        break;
      case "TabPinned":
      case "TabUnpinned":
      case "SwapDocShells":
        this.saveStateDelayed(win);
        break;
      case "oop-browser-crashed":
        this.onBrowserCrashed(target);
        break;
      case "XULFrameLoaderCreated":
        if (target.namespaceURI == NS_XUL &&
            target.localName == "browser" &&
            target.frameLoader &&
            target.permanentKey) {
          this._lastKnownFrameLoader.set(target.permanentKey, target.frameLoader);
          this.resetEpoch(target);
        }
        break;
      case "BrowserWillChangeProcess":
        let promise = TabStateFlusher.flush(target);
        target.frameLoader.addProcessChangeBlockingPromise(promise);
        break;
      case "BrowserChangedProcess":
        let newEpoch = 1 + Math.max(this.getCurrentEpoch(target),
                                    this.getCurrentEpoch(aEvent.otherBrowser));
        this.setCurrentEpoch(target, newEpoch);
        target.messageManager.sendAsyncMessage("SessionStore:becomeActiveProcess", {
          epoch: newEpoch
        });
        break;
      default:
        throw new Error(`unhandled event ${aEvent.type}?`);
    }
    this._clearRestoringWindows();
  },

  /**
   * Generate a unique window identifier
   * @return string
   *         A unique string to identify a window
   */
  _generateWindowID: function ssi_generateWindowID() {
    return "window" + (this._nextWindowID++);
  },

  /**
   * Registers and tracks a given window.
   *
   * @param aWindow
   *        Window reference
   */
  onLoad(aWindow) {
    // return if window has already been initialized
    if (aWindow && aWindow.__SSi && this._windows[aWindow.__SSi])
      return;

    // ignore windows opened while shutting down
    if (RunState.isQuitting)
      return;

    // Assign the window a unique identifier we can use to reference
    // internal data about the window.
    aWindow.__SSi = this._generateWindowID();

    let mm = aWindow.getGroupMessageManager("browsers");
    MESSAGES.forEach(msg => {
      let listenWhenClosed = CLOSED_MESSAGES.has(msg);
      mm.addMessageListener(msg, this, listenWhenClosed);
    });

    // Load the frame script after registering listeners.
    mm.loadFrameScript("chrome://browser/content/content-sessionStore.js", true);

    // and create its data object
    this._windows[aWindow.__SSi] = { tabs: [], selected: 0, _closedTabs: [], busy: false };

    if (PrivateBrowsingUtils.isWindowPrivate(aWindow))
      this._windows[aWindow.__SSi].isPrivate = true;
    if (!this._isWindowLoaded(aWindow))
      this._windows[aWindow.__SSi]._restoring = true;
    if (!aWindow.toolbar.visible)
      this._windows[aWindow.__SSi].isPopup = true;

    let tabbrowser = aWindow.gBrowser;

    // add tab change listeners to all already existing tabs
    for (let i = 0; i < tabbrowser.tabs.length; i++) {
      this.onTabBrowserInserted(aWindow, tabbrowser.tabs[i]);
    }
    // notification of tab add/remove/selection/show/hide
    TAB_EVENTS.forEach(function(aEvent) {
      tabbrowser.tabContainer.addEventListener(aEvent, this, true);
    }, this);

    // Keep track of a browser's latest frameLoader.
    aWindow.gBrowser.addEventListener("XULFrameLoaderCreated", this);
    aWindow.gBrowser.addEventListener("BrowserChangedProcess", this);
    aWindow.gBrowser.addEventListener("BrowserWillChangeProcess", this);
  },

  /**
   * Initializes a given window.
   *
   * Windows are registered as soon as they are created but we need to wait for
   * the session file to load, and the initial window's delayed startup to
   * finish before initializing a window, i.e. restoring data into it.
   *
   * @param aWindow
   *        Window reference
   * @param aInitialState
   *        The initial state to be loaded after startup (optional)
   */
  initializeWindow(aWindow, aInitialState = null) {
    let isPrivateWindow = PrivateBrowsingUtils.isWindowPrivate(aWindow);

    // perform additional initialization when the first window is loading
    if (RunState.isStopped) {
      RunState.setRunning();

      // restore a crashed session resp. resume the last session if requested
      if (aInitialState) {
        // Don't write to disk right after startup. Set the last time we wrote
        // to disk to NOW() to enforce a full interval before the next write.
        SessionSaver.updateLastSaveTime();

        if (isPrivateWindow) {
          // We're starting with a single private window. Save the state we
          // actually wanted to restore so that we can do it later in case
          // the user opens another, non-private window.
          this._deferredInitialState = gSessionStartup.state;

          // Nothing to restore now, notify observers things are complete.
          Services.obs.notifyObservers(null, NOTIFY_WINDOWS_RESTORED);
          Services.obs.notifyObservers(null, "sessionstore-one-or-no-tab-restored");
          this._deferredAllWindowsRestored.resolve();
        } else {
          TelemetryTimestamps.add("sessionRestoreRestoring");
          this._restoreCount = aInitialState.windows ? aInitialState.windows.length : 0;

          // global data must be restored before restoreWindow is called so that
          // it happens before observers are notified
          this._globalState.setFromState(aInitialState);

          // Restore session cookies before loading any tabs.
          SessionCookies.restore(aInitialState.cookies || []);

          let overwrite = this._isCmdLineEmpty(aWindow, aInitialState);
          let options = {firstWindow: true, overwriteTabs: overwrite};
          this.restoreWindows(aWindow, aInitialState, options);
        }
      } else {
        // Nothing to restore, notify observers things are complete.
        Services.obs.notifyObservers(null, NOTIFY_WINDOWS_RESTORED);
        Services.obs.notifyObservers(null, "sessionstore-one-or-no-tab-restored");
        this._deferredAllWindowsRestored.resolve();
      }
    // this window was opened by _openWindowWithState
    } else if (!this._isWindowLoaded(aWindow)) {
      let state = this._statesToRestore[aWindow.__SS_restoreID];
      let options = {overwriteTabs: true, isFollowUp: state.windows.length == 1};
      this.restoreWindow(aWindow, state.windows[0], options);
    // The user opened another, non-private window after starting up with
    // a single private one. Let's restore the session we actually wanted to
    // restore at startup.
    } else if (this._deferredInitialState && !isPrivateWindow &&
             aWindow.toolbar.visible) {

      // global data must be restored before restoreWindow is called so that
      // it happens before observers are notified
      this._globalState.setFromState(this._deferredInitialState);

      this._restoreCount = this._deferredInitialState.windows ?
        this._deferredInitialState.windows.length : 0;
      this.restoreWindows(aWindow, this._deferredInitialState, {firstWindow: true});
      this._deferredInitialState = null;
    } else if (this._restoreLastWindow && aWindow.toolbar.visible &&
             this._closedWindows.length && !isPrivateWindow) {

      // default to the most-recently closed window
      // don't use popup windows
      let closedWindowState = null;
      let closedWindowIndex;
      for (let i = 0; i < this._closedWindows.length; i++) {
        // Take the first non-popup, point our object at it, and break out.
        if (!this._closedWindows[i].isPopup) {
          closedWindowState = this._closedWindows[i];
          closedWindowIndex = i;
          break;
        }
      }

      if (closedWindowState) {
        let newWindowState;
        if (AppConstants.platform == "macosx" || !this._doResumeSession()) {
          // We want to split the window up into pinned tabs and unpinned tabs.
          // Pinned tabs should be restored. If there are any remaining tabs,
          // they should be added back to _closedWindows.
          // We'll cheat a little bit and reuse _prepDataForDeferredRestore
          // even though it wasn't built exactly for this.
          let [appTabsState, normalTabsState] =
            this._prepDataForDeferredRestore({ windows: [closedWindowState] });

          // These are our pinned tabs, which we should restore
          if (appTabsState.windows.length) {
            newWindowState = appTabsState.windows[0];
            delete newWindowState.__lastSessionWindowID;
          }

          // In case there were no unpinned tabs, remove the window from _closedWindows
          if (!normalTabsState.windows.length) {
            this._removeClosedWindow(closedWindowIndex);
          // Or update _closedWindows with the modified state
          } else {
            delete normalTabsState.windows[0].__lastSessionWindowID;
            this._closedWindows[closedWindowIndex] = normalTabsState.windows[0];
          }
        } else {
          // If we're just restoring the window, make sure it gets removed from
          // _closedWindows.
          this._removeClosedWindow(closedWindowIndex);
          newWindowState = closedWindowState;
          delete newWindowState.hidden;
        }

        if (newWindowState) {
          // Ensure that the window state isn't hidden
          this._restoreCount = 1;
          let state = { windows: [newWindowState] };
          let options = {overwriteTabs: this._isCmdLineEmpty(aWindow, state)};
          this.restoreWindow(aWindow, newWindowState, options);
        }
      }
      // we actually restored the session just now.
      this._prefBranch.setBoolPref("sessionstore.resume_session_once", false);
    }
    if (this._restoreLastWindow && aWindow.toolbar.visible) {
      // always reset (if not a popup window)
      // we don't want to restore a window directly after, for example,
      // undoCloseWindow was executed.
      this._restoreLastWindow = false;
    }
  },

  /**
   * Called right before a new browser window is shown.
   * @param aWindow
   *        Window reference
   */
  onBeforeBrowserWindowShown(aWindow) {
    // Register the window.
    this.onLoad(aWindow);

    // Just call initializeWindow() directly if we're initialized already.
    if (this._sessionInitialized) {
      this.initializeWindow(aWindow);
      return;
    }

    // The very first window that is opened creates a promise that is then
    // re-used by all subsequent windows. The promise will be used to tell
    // when we're ready for initialization.
    if (!this._promiseReadyForInitialization) {
      // Wait for the given window's delayed startup to be finished.
      let promise = new Promise(resolve => {
        Services.obs.addObserver(function obs(subject, topic) {
          if (aWindow == subject) {
            Services.obs.removeObserver(obs, topic);
            resolve();
          }
        }, "browser-delayed-startup-finished");
      });

      // We are ready for initialization as soon as the session file has been
      // read from disk and the initial window's delayed startup has finished.
      this._promiseReadyForInitialization =
        Promise.all([promise, gSessionStartup.onceInitialized]);
    }

    // We can't call this.onLoad since initialization
    // hasn't completed, so we'll wait until it is done.
    // Even if additional windows are opened and wait
    // for initialization as well, the first opened
    // window should execute first, and this.onLoad
    // will be called with the initialState.
    this._promiseReadyForInitialization.then(() => {
      if (aWindow.closed) {
        return;
      }

      if (this._sessionInitialized) {
        this.initializeWindow(aWindow);
      } else {
        let initialState = this.initSession();
        this._sessionInitialized = true;

        if (initialState) {
          Services.obs.notifyObservers(null, NOTIFY_RESTORING_ON_STARTUP);
        }
        TelemetryStopwatch.start("FX_SESSION_RESTORE_STARTUP_ONLOAD_INITIAL_WINDOW_MS");
        this.initializeWindow(aWindow, initialState);
        TelemetryStopwatch.finish("FX_SESSION_RESTORE_STARTUP_ONLOAD_INITIAL_WINDOW_MS");

        // Let everyone know we're done.
        this._deferredInitialized.resolve();
      }
    }).catch(console.error);
  },

  /**
   * On window close...
   * - remove event listeners from tabs
   * - save all window data
   * @param aWindow
   *        Window reference
   *
   * @returns a Promise
   */
  onClose: function ssi_onClose(aWindow) {
    let completionPromise = Promise.resolve();
    // this window was about to be restored - conserve its original data, if any
    let isFullyLoaded = this._isWindowLoaded(aWindow);
    if (!isFullyLoaded) {
      if (!aWindow.__SSi) {
        aWindow.__SSi = this._generateWindowID();
      }

      this._windows[aWindow.__SSi] = this._statesToRestore[aWindow.__SS_restoreID];
      delete this._statesToRestore[aWindow.__SS_restoreID];
      delete aWindow.__SS_restoreID;
    }

    // ignore windows not tracked by SessionStore
    if (!aWindow.__SSi || !this._windows[aWindow.__SSi]) {
      return completionPromise;
    }

    // notify that the session store will stop tracking this window so that
    // extensions can store any data about this window in session store before
    // that's not possible anymore
    let event = aWindow.document.createEvent("Events");
    event.initEvent("SSWindowClosing", true, false);
    aWindow.dispatchEvent(event);

    if (this.windowToFocus && this.windowToFocus == aWindow) {
      delete this.windowToFocus;
    }

    var tabbrowser = aWindow.gBrowser;

    let browsers = Array.from(tabbrowser.browsers);

    TAB_EVENTS.forEach(function(aEvent) {
      tabbrowser.tabContainer.removeEventListener(aEvent, this, true);
    }, this);

    aWindow.gBrowser.removeEventListener("XULFrameLoaderCreated", this);
    aWindow.gBrowser.removeEventListener("BrowserChangedProcess", this);
    aWindow.gBrowser.removeEventListener("BrowserWillChangeProcess", this);

    let winData = this._windows[aWindow.__SSi];

    // Collect window data only when *not* closed during shutdown.
    if (RunState.isRunning) {
      // Grab the most recent window data. The tab data will be updated
      // once we finish flushing all of the messages from the tabs.
      let tabMap = this._collectWindowData(aWindow);

      for (let [tab, tabData] of tabMap) {
        let permanentKey = tab.linkedBrowser.permanentKey;
        this._closedWindowTabs.set(permanentKey, tabData);
      }

      if (isFullyLoaded) {
        winData.title = tabbrowser.selectedBrowser.contentTitle || tabbrowser.selectedTab.label;
      }

      if (AppConstants.platform != "macosx") {
        // Until we decide otherwise elsewhere, this window is part of a series
        // of closing windows to quit.
        winData._shouldRestore = true;
      }

      // Store the window's close date to figure out when each individual tab
      // was closed. This timestamp should allow re-arranging data based on how
      // recently something was closed.
      winData.closedAt = Date.now();

      // we don't want to save the busy state
      delete winData.busy;

      // When closing windows one after the other until Firefox quits, we
      // will move those closed in series back to the "open windows" bucket
      // before writing to disk. If however there is only a single window
      // with tabs we deem not worth saving then we might end up with a
      // random closed or even a pop-up window re-opened. To prevent that
      // we explicitly allow saving an "empty" window state.
      let isLastWindow =
        Object.keys(this._windows).length == 1 &&
        !this._closedWindows.some(win => win._shouldRestore || false);

      // clear this window from the list, since it has definitely been closed.
      delete this._windows[aWindow.__SSi];

      // This window has the potential to be saved in the _closedWindows
      // array (maybeSaveClosedWindows gets the final call on that).
      this._saveableClosedWindowData.add(winData);

      // Now we have to figure out if this window is worth saving in the _closedWindows
      // Object.
      //
      // We're about to flush the tabs from this window, but it's possible that we
      // might never hear back from the content process(es) in time before the user
      // chooses to restore the closed window. So we do the following:
      //
      // 1) Use the tab state cache to determine synchronously if the window is
      //    worth stashing in _closedWindows.
      // 2) Flush the window.
      // 3) When the flush is complete, revisit our decision to store the window
      //    in _closedWindows, and add/remove as necessary.
      if (!winData.isPrivate) {
        // Remove any open private tabs the window may contain.
        PrivacyFilter.filterPrivateTabs(winData);
        this.maybeSaveClosedWindow(winData, isLastWindow);
      }

      completionPromise = TabStateFlusher.flushWindow(aWindow).then(() => {
        // At this point, aWindow is closed! You should probably not try to
        // access any DOM elements from aWindow within this callback unless
        // you're holding on to them in the closure.

        for (let browser of browsers) {
          if (this._closedWindowTabs.has(browser.permanentKey)) {
            let tabData = this._closedWindowTabs.get(browser.permanentKey);
            TabState.copyFromCache(browser, tabData);
            this._closedWindowTabs.delete(browser.permanentKey);
          }
        }

        // Save non-private windows if they have at
        // least one saveable tab or are the last window.
        if (!winData.isPrivate) {
          // It's possible that a tab switched its privacy state at some point
          // before our flush, so we need to filter again.
          PrivacyFilter.filterPrivateTabs(winData);
          this.maybeSaveClosedWindow(winData, isLastWindow);
        }

        // Update the tabs data now that we've got the most
        // recent information.
        this.cleanUpWindow(aWindow, winData, browsers);

        // save the state without this window to disk
        this.saveStateDelayed();
      });
    } else {
      this.cleanUpWindow(aWindow, winData, browsers);
    }

    for (let i = 0; i < tabbrowser.tabs.length; i++) {
      this.onTabRemove(aWindow, tabbrowser.tabs[i], true);
    }

    return completionPromise;
  },

  /**
   * Clean up the message listeners on a window that has finally
   * gone away. Call this once you're sure you don't want to hear
   * from any of this windows tabs from here forward.
   *
   * @param aWindow
   *        The browser window we're cleaning up.
   * @param winData
   *        The data for the window that we should hold in the
   *        DyingWindowCache in case anybody is still holding a
   *        reference to it.
   */
  cleanUpWindow(aWindow, winData, browsers) {
    // Any leftover TabStateFlusher Promises need to be resolved now,
    // since we're about to remove the message listeners.
    for (let browser of browsers) {
      TabStateFlusher.resolveAll(browser);
    }

    // Cache the window state until it is completely gone.
    DyingWindowCache.set(aWindow, winData);

    let mm = aWindow.getGroupMessageManager("browsers");
    MESSAGES.forEach(msg => mm.removeMessageListener(msg, this));

    this._saveableClosedWindowData.delete(winData);
    delete aWindow.__SSi;
  },

  /**
   * Decides whether or not a closed window should be put into the
   * _closedWindows Object. This might be called multiple times per
   * window, and will do the right thing of moving the window data
   * in or out of _closedWindows if the winData indicates that our
   * need for saving it has changed.
   *
   * @param winData
   *        The data for the closed window that we might save.
   * @param isLastWindow
   *        Whether or not the window being closed is the last
   *        browser window. Callers of this function should pass
   *        in the value of SessionStoreInternal.atLastWindow for
   *        this argument, and pass in the same value if they happen
   *        to call this method again asynchronously (for example, after
   *        a window flush).
   */
  maybeSaveClosedWindow(winData, isLastWindow) {
    // Make sure SessionStore is still running, and make sure that we
    // haven't chosen to forget this window.
    if (RunState.isRunning && this._saveableClosedWindowData.has(winData)) {
      // Determine whether the window has any tabs worth saving.
      let hasSaveableTabs = winData.tabs.some(this._shouldSaveTabState);

      // Note that we might already have this window stored in
      // _closedWindows from a previous call to this function.
      let winIndex = this._closedWindows.indexOf(winData);
      let alreadyStored = (winIndex != -1);
      let shouldStore = (hasSaveableTabs || isLastWindow);

      if (shouldStore && !alreadyStored) {
        let index = this._closedWindows.findIndex(win => {
          return win.closedAt < winData.closedAt;
        });

        // If we found no tab closed before our
        // tab then just append it to the list.
        if (index == -1) {
          index = this._closedWindows.length;
        }

        // About to save the closed window, add a unique ID.
        winData.closedId = this._nextClosedId++;

        // Insert tabData at the right position.
        this._closedWindows.splice(index, 0, winData);
        this._capClosedWindows();
        this._closedObjectsChanged = true;
      } else if (!shouldStore && alreadyStored) {
        this._removeClosedWindow(winIndex);
      }
    }
  },

  /**
   * On quit application granted
   */
  onQuitApplicationGranted: function ssi_onQuitApplicationGranted(syncShutdown = false) {
    // Collect an initial snapshot of window data before we do the flush
    this._forEachBrowserWindow((win) => {
      this._collectWindowData(win);
    });

    // Now add an AsyncShutdown blocker that'll spin the event loop
    // until the windows have all been flushed.

    // This progress object will track the state of async window flushing
    // and will help us debug things that go wrong with our AsyncShutdown
    // blocker.
    let progress = { total: -1, current: -1 };

    // We're going down! Switch state so that we treat closing windows and
    // tabs correctly.
    RunState.setQuitting();

    if (!syncShutdown) {
      // We've got some time to shut down, so let's do this properly that there
      // will be a complete session available upon next startup.
      // To prevent a blocker from taking longer than the DELAY_CRASH_MS limit
      // (which will cause a crash) of AsyncShutdown whilst flushing all windows,
      // we resolve the Promise blocker once:
      // 1. the flush duration exceeds 10 seconds before DELAY_CRASH_MS, or
      // 2. 'oop-frameloader-crashed', or
      // 3. 'ipc:content-shutdown' is observed.
      AsyncShutdown.quitApplicationGranted.addBlocker(
        "SessionStore: flushing all windows",
        () => {
          // Set up the list of promises that will signal a complete sessionstore
          // shutdown: either all data is saved, or we crashed or the message IPC
          // channel went away in the meantime.
          let promises = [this.flushAllWindowsAsync(progress)];

          const observeTopic = topic => {
            let deferred = PromiseUtils.defer();
            const cleanup = () => Services.obs.removeObserver(deferred.resolve, topic);
            Services.obs.addObserver(deferred.resolve, topic);
            deferred.promise.then(cleanup, cleanup);
            return deferred;
          };

          // Build a list of deferred executions that require cleanup once the
          // Promise race is won.
          // Ensure that the timer fires earlier than the AsyncShutdown crash timer.
          let waitTimeMaxMs = Math.max(0, AsyncShutdown.DELAY_CRASH_MS - 10000);
          let defers = [this.looseTimer(waitTimeMaxMs),
            observeTopic("oop-frameloader-crashed"), observeTopic("ipc:content-shutdown")];
          // Add these monitors to the list of Promises to start the race.
          promises.push(...defers.map(deferred => deferred.promise));

          return Promise.race(promises)
            .then(() => {
              // When a Promise won the race, make sure we clean up the running
              // monitors.
              defers.forEach(deferred => deferred.reject());
            });
        },
        () => progress);
    } else {
      // We have to shut down NOW, which means we only get to save whatever
      // we already had cached.
    }
  },

  /**
   * An async Task that iterates all open browser windows and flushes
   * any outstanding messages from their tabs. This will also close
   * all of the currently open windows while we wait for the flushes
   * to complete.
   *
   * @param progress (Object)
   *        Optional progress object that will be updated as async
   *        window flushing progresses. flushAllWindowsSync will
   *        write to the following properties:
   *
   *        total (int):
   *          The total number of windows to be flushed.
   *        current (int):
   *          The current window that we're waiting for a flush on.
   *
   * @return Promise
   */
  async flushAllWindowsAsync(progress = {}) {
    let windowPromises = new Map();
    // We collect flush promises and close each window immediately so that
    // the user can't start changing any window state while we're waiting
    // for the flushes to finish.
    this._forEachBrowserWindow((win) => {
      windowPromises.set(win, TabStateFlusher.flushWindow(win));

      // We have to wait for these messages to come up from
      // each window and each browser. In the meantime, hide
      // the windows to improve perceived shutdown speed.
      let baseWin = win.QueryInterface(Ci.nsIInterfaceRequestor)
                       .getInterface(Ci.nsIDocShell)
                       .QueryInterface(Ci.nsIDocShellTreeItem)
                       .treeOwner
                       .QueryInterface(Ci.nsIBaseWindow);
      baseWin.visibility = false;
    });

    progress.total = windowPromises.size;
    progress.current = 0;

    // We'll iterate through the Promise array, yielding each one, so as to
    // provide useful progress information to AsyncShutdown.
    for (let [win, promise] of windowPromises) {
      await promise;
      this._collectWindowData(win);
      progress.current++;
    }

    // We must cache this because _getMostRecentBrowserWindow will always
    // return null by the time quit-application occurs.
    var activeWindow = this._getMostRecentBrowserWindow();
    if (activeWindow)
      this.activeWindowSSiCache = activeWindow.__SSi || "";
    DirtyWindows.clear();
  },

  /**
   * On last browser window close
   */
  onLastWindowCloseGranted: function ssi_onLastWindowCloseGranted() {
    // last browser window is quitting.
    // remember to restore the last window when another browser window is opened
    // do not account for pref(resume_session_once) at this point, as it might be
    // set by another observer getting this notice after us
    this._restoreLastWindow = true;
  },

  /**
   * On quitting application
   * @param aData
   *        String type of quitting
   */
  onQuitApplication: function ssi_onQuitApplication(aData) {
    if (aData == "restart") {
      if (!PrivateBrowsingUtils.permanentPrivateBrowsing) {
        this._prefBranch.setBoolPref("sessionstore.resume_session_once", true);
      }

      // The browser:purge-session-history notification fires after the
      // quit-application notification so unregister the
      // browser:purge-session-history notification to prevent clearing
      // session data on disk on a restart.  It is also unnecessary to
      // perform any other sanitization processing on a restart as the
      // browser is about to exit anyway.
      Services.obs.removeObserver(this, "browser:purge-session-history");
    }

    if (aData != "restart") {
      // Throw away the previous session on shutdown without notification
      LastSession.clear(true);
    }

    this._uninit();
  },

  /**
   * On purge of session history
   */
  onPurgeSessionHistory: function ssi_onPurgeSessionHistory() {
    SessionFile.wipe();
    // If the browser is shutting down, simply return after clearing the
    // session data on disk as this notification fires after the
    // quit-application notification so the browser is about to exit.
    if (RunState.isQuitting)
      return;
    LastSession.clear();

    let openWindows = {};
    // Collect open windows.
    this._forEachBrowserWindow(({__SSi: id}) => openWindows[id] = true);

    // also clear all data about closed tabs and windows
    for (let ix in this._windows) {
      if (ix in openWindows) {
        if (this._windows[ix]._closedTabs.length) {
          this._windows[ix]._closedTabs = [];
          this._closedObjectsChanged = true;
        }
      } else {
        delete this._windows[ix];
      }
    }
    // also clear all data about closed windows
    if (this._closedWindows.length) {
      this._closedWindows = [];
      this._closedObjectsChanged = true;
    }
    // give the tabbrowsers a chance to clear their histories first
    var win = this._getMostRecentBrowserWindow();
    if (win) {
      win.setTimeout(() => SessionSaver.run(), 0);
    } else if (RunState.isRunning) {
      SessionSaver.run();
    }

    this._clearRestoringWindows();
    this._saveableClosedWindowData = new WeakSet();
  },

  /**
   * On purge of domain data
   * @param aData
   *        String domain data
   */
  onPurgeDomainData: function ssi_onPurgeDomainData(aData) {
    // does a session history entry contain a url for the given domain?
    function containsDomain(aEntry) {
      if (Utils.hasRootDomain(aEntry.url, aData)) {
        return true;
      }
      return aEntry.children && aEntry.children.some(containsDomain, this);
    }
    // remove all closed tabs containing a reference to the given domain
    for (let ix in this._windows) {
      let closedTabs = this._windows[ix]._closedTabs;
      for (let i = closedTabs.length - 1; i >= 0; i--) {
        if (closedTabs[i].state.entries.some(containsDomain, this)) {
          closedTabs.splice(i, 1);
          this._closedObjectsChanged = true;
        }
      }
    }
    // remove all open & closed tabs containing a reference to the given
    // domain in closed windows
    for (let ix = this._closedWindows.length - 1; ix >= 0; ix--) {
      let closedTabs = this._closedWindows[ix]._closedTabs;
      let openTabs = this._closedWindows[ix].tabs;
      let openTabCount = openTabs.length;
      for (let i = closedTabs.length - 1; i >= 0; i--)
        if (closedTabs[i].state.entries.some(containsDomain, this))
          closedTabs.splice(i, 1);
      for (let j = openTabs.length - 1; j >= 0; j--) {
        if (openTabs[j].entries.some(containsDomain, this)) {
          openTabs.splice(j, 1);
          if (this._closedWindows[ix].selected > j)
            this._closedWindows[ix].selected--;
        }
      }
      if (openTabs.length == 0) {
        this._closedWindows.splice(ix, 1);
      } else if (openTabs.length != openTabCount) {
        // Adjust the window's title if we removed an open tab
        let selectedTab = openTabs[this._closedWindows[ix].selected - 1];
        // some duplication from restoreHistory - make sure we get the correct title
        let activeIndex = (selectedTab.index || selectedTab.entries.length) - 1;
        if (activeIndex >= selectedTab.entries.length)
          activeIndex = selectedTab.entries.length - 1;
        this._closedWindows[ix].title = selectedTab.entries[activeIndex].title;
      }
    }

    if (RunState.isRunning) {
      SessionSaver.run();
    }

    this._clearRestoringWindows();
  },

  /**
   * On preference change
   * @param aData
   *        String preference changed
   */
  onPrefChange: function ssi_onPrefChange(aData) {
    switch (aData) {
      // if the user decreases the max number of closed tabs they want
      // preserved update our internal states to match that max
      case "sessionstore.max_tabs_undo":
        this._max_tabs_undo = this._prefBranch.getIntPref("sessionstore.max_tabs_undo");
        for (let ix in this._windows) {
          if (this._windows[ix]._closedTabs.length > this._max_tabs_undo) {
            this._windows[ix]._closedTabs.splice(this._max_tabs_undo, this._windows[ix]._closedTabs.length);
            this._closedObjectsChanged = true;
          }
        }
        break;
      case "sessionstore.max_windows_undo":
        this._max_windows_undo = this._prefBranch.getIntPref("sessionstore.max_windows_undo");
        this._capClosedWindows();
        break;
      case "privacy.resistFingerprinting":
        gResistFingerprintingEnabled = Services.prefs.getBoolPref("privacy.resistFingerprinting");
        break;
      case "sessionstore.restore_on_demand":
        this._restore_on_demand = this._prefBranch.getBoolPref("sessionstore.restore_on_demand");
        break;
    }
  },

  /**
   * save state when new tab is added
   * @param aWindow
   *        Window reference
   */
  onTabAdd: function ssi_onTabAdd(aWindow) {
    this.saveStateDelayed(aWindow);
  },

  /**
   * set up listeners for a new tab
   * @param aWindow
   *        Window reference
   * @param aTab
   *        Tab reference
   */
  onTabBrowserInserted: function ssi_onTabBrowserInserted(aWindow, aTab) {
    let browser = aTab.linkedBrowser;
    browser.addEventListener("SwapDocShells", this);
    browser.addEventListener("oop-browser-crashed", this);

    if (browser.frameLoader) {
      this._lastKnownFrameLoader.set(browser.permanentKey, browser.frameLoader);
    }

    // Only restore if browser has been lazy.
    if (aTab.__SS_lazyData && !browser.__SS_restoreState && TabStateCache.get(browser)) {
      let tabState = TabState.clone(aTab);
      this.restoreTab(aTab, tabState);
    }

    // The browser has been inserted now, so lazy data is no longer relevant.
    delete aTab.__SS_lazyData;
  },

  /**
   * remove listeners for a tab
   * @param aWindow
   *        Window reference
   * @param aTab
   *        Tab reference
   * @param aNoNotification
   *        bool Do not save state if we're updating an existing tab
   */
  onTabRemove: function ssi_onTabRemove(aWindow, aTab, aNoNotification) {
    this.cleanUpRemovedBrowser(aTab);

    if (!aNoNotification) {
      this.saveStateDelayed(aWindow);
    }
  },

  /**
   * When a tab closes, collect its properties
   * @param aWindow
   *        Window reference
   * @param aTab
   *        Tab reference
   */
  onTabClose: function ssi_onTabClose(aWindow, aTab) {
    // notify the tabbrowser that the tab state will be retrieved for the last time
    // (so that extension authors can easily set data on soon-to-be-closed tabs)
    var event = aWindow.document.createEvent("Events");
    event.initEvent("SSTabClosing", true, false);
    aTab.dispatchEvent(event);

    // don't update our internal state if we don't have to
    if (this._max_tabs_undo == 0) {
      return;
    }

    // Get the latest data for this tab (generally, from the cache)
    let tabState = TabState.collect(aTab);

    // Don't save private tabs
    let isPrivateWindow = PrivateBrowsingUtils.isWindowPrivate(aWindow);
    if (!isPrivateWindow && tabState.isPrivate) {
      return;
    }

    // Store closed-tab data for undo.
    let tabbrowser = aWindow.gBrowser;
    let tabTitle = aTab.label;
    let {permanentKey} = aTab.linkedBrowser;

    let tabData = {
      permanentKey,
      state: tabState,
      title: tabTitle,
      image: tabbrowser.getIcon(aTab),
      iconLoadingPrincipal: Utils.serializePrincipal(aTab.linkedBrowser.contentPrincipal),
      pos: aTab._tPos,
      closedAt: Date.now()
    };

    let closedTabs = this._windows[aWindow.__SSi]._closedTabs;

    // Determine whether the tab contains any information worth saving. Note
    // that there might be pending state changes queued in the child that
    // didn't reach the parent yet. If a tab is emptied before closing then we
    // might still remove it from the list of closed tabs later.
    if (this._shouldSaveTabState(tabState)) {
      // Save the tab state, for now. We might push a valid tab out
      // of the list but those cases should be extremely rare and
      // do probably never occur when using the browser normally.
      // (Tests or add-ons might do weird things though.)
      this.saveClosedTabData(closedTabs, tabData);
    }

    // Remember the closed tab to properly handle any last updates included in
    // the final "update" message sent by the frame script's unload handler.
    this._closedTabs.set(permanentKey, {closedTabs, tabData});
  },

  /**
   * Remove listeners which were added when browser was inserted and reset restoring state.
   * Also re-instate lazy data and basically revert tab to its lazy browser state.
   * @param aTab
   *        Tab reference
   */
  resetBrowserToLazyState(aTab) {
    let browser = aTab.linkedBrowser;
    // Browser is already lazy so don't do anything.
    if (!browser.isConnected) {
      return;
    }

    this.cleanUpRemovedBrowser(aTab);

    aTab.setAttribute("pending", "true");

    this._lastKnownFrameLoader.delete(browser.permanentKey);
    this._crashedBrowsers.delete(browser.permanentKey);
    aTab.removeAttribute("crashed");

    aTab.__SS_lazyData = {
      url: browser.currentURI.spec,
      title: aTab.label,
      userTypedValue: browser.userTypedValue || "",
      userTypedClear: browser.userTypedClear || 0
    };
  },

  /**
   * When a tab is removed or suspended, remove listeners and reset restoring state.
   * @param aBrowser
   *        Browser reference
   */
  cleanUpRemovedBrowser(aTab) {
    let browser = aTab.linkedBrowser;

    browser.removeEventListener("SwapDocShells", this);
    browser.removeEventListener("oop-browser-crashed", this);

    // If this tab was in the middle of restoring or still needs to be restored,
    // we need to reset that state. If the tab was restoring, we will attempt to
    // restore the next tab.
    let previousState = browser.__SS_restoreState;
    if (previousState) {
      this._resetTabRestoringState(aTab);
      if (previousState == TAB_STATE_RESTORING)
        this.restoreNextTab();
    }
  },

  /**
   * Insert a given |tabData| object into the list of |closedTabs|. We will
   * determine the right insertion point based on the .closedAt properties of
   * all tabs already in the list. The list will be truncated to contain a
   * maximum of |this._max_tabs_undo| entries.
   *
   * @param closedTabs (array)
   *        The list of closed tabs for a window.
   * @param tabData (object)
   *        The tabData to be inserted.
   */
  saveClosedTabData(closedTabs, tabData) {
    // Find the index of the first tab in the list
    // of closed tabs that was closed before our tab.
    let index = closedTabs.findIndex(tab => {
      return tab.closedAt < tabData.closedAt;
    });

    // If we found no tab closed before our
    // tab then just append it to the list.
    if (index == -1) {
      index = closedTabs.length;
    }

    // About to save the closed tab, add a unique ID.
    tabData.closedId = this._nextClosedId++;

    // Insert tabData at the right position.
    closedTabs.splice(index, 0, tabData);
    this._closedObjectsChanged = true;

    // Truncate the list of closed tabs, if needed.
    if (closedTabs.length > this._max_tabs_undo) {
      closedTabs.splice(this._max_tabs_undo, closedTabs.length);
    }
  },

  /**
   * Remove the closed tab data at |index| from the list of |closedTabs|. If
   * the tab's final message is still pending we will simply discard it when
   * it arrives so that the tab doesn't reappear in the list.
   *
   * @param closedTabs (array)
   *        The list of closed tabs for a window.
   * @param index (uint)
   *        The index of the tab to remove.
   */
  removeClosedTabData(closedTabs, index) {
    // Remove the given index from the list.
    let [closedTab] = closedTabs.splice(index, 1);
    this._closedObjectsChanged = true;

    // If the closed tab's state still has a .permanentKey property then we
    // haven't seen its final update message yet. Remove it from the map of
    // closed tabs so that we will simply discard its last messages and will
    // not add it back to the list of closed tabs again.
    if (closedTab.permanentKey) {
      this._closedTabs.delete(closedTab.permanentKey);
      this._closedWindowTabs.delete(closedTab.permanentKey);
      delete closedTab.permanentKey;
    }

    return closedTab;
  },

  /**
   * When a tab is selected, save session data
   * @param aWindow
   *        Window reference
   */
  onTabSelect: function ssi_onTabSelect(aWindow) {
    if (RunState.isRunning) {
      this._windows[aWindow.__SSi].selected = aWindow.gBrowser.tabContainer.selectedIndex;

      let tab = aWindow.gBrowser.selectedTab;
      let browser = tab.linkedBrowser;

      if (browser.__SS_restoreState &&
          browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE) {
        // If __SS_restoreState is still on the browser and it is
        // TAB_STATE_NEEDS_RESTORE, then then we haven't restored
        // this tab yet.
        //
        // It's possible that this tab was recently revived, and that
        // we've deferred showing the tab crashed page for it (if the
        // tab crashed in the background). If so, we need to re-enter
        // the crashed state, since we'll be showing the tab crashed
        // page.
        if (TabCrashHandler.willShowCrashedTab(browser)) {
          this.enterCrashedState(browser);
        } else {
          this.restoreTabContent(tab);
        }
      }
    }
  },

  onTabShow: function ssi_onTabShow(aWindow, aTab) {
    // If the tab hasn't been restored yet, move it into the right bucket
    if (aTab.linkedBrowser.__SS_restoreState &&
        aTab.linkedBrowser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE) {
      TabRestoreQueue.hiddenToVisible(aTab);

      // let's kick off tab restoration again to ensure this tab gets restored
      // with "restore_hidden_tabs" == false (now that it has become visible)
      this.restoreNextTab();
    }

    // Default delay of 2 seconds gives enough time to catch multiple TabShow
    // events. This used to be due to changing groups in 'tab groups'. We
    // might be able to get rid of this now?
    this.saveStateDelayed(aWindow);
  },

  onTabHide: function ssi_onTabHide(aWindow, aTab) {
    // If the tab hasn't been restored yet, move it into the right bucket
    if (aTab.linkedBrowser.__SS_restoreState &&
        aTab.linkedBrowser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE) {
      TabRestoreQueue.visibleToHidden(aTab);
    }

    // Default delay of 2 seconds gives enough time to catch multiple TabHide
    // events. This used to be due to changing groups in 'tab groups'. We
    // might be able to get rid of this now?
    this.saveStateDelayed(aWindow);
  },

  /**
   * Handler for the event that is fired when a <xul:browser> crashes.
   *
   * @param aWindow
   *        The window that the crashed browser belongs to.
   * @param aBrowser
   *        The <xul:browser> that is now in the crashed state.
   */
  onBrowserCrashed(aBrowser) {
    this.enterCrashedState(aBrowser);
    // The browser crashed so we might never receive flush responses.
    // Resolve all pending flush requests for the crashed browser.
    TabStateFlusher.resolveAll(aBrowser);
  },

  /**
   * Called when a browser is showing or is about to show the tab
   * crashed page. This method causes SessionStore to ignore the
   * tab until it's restored.
   *
   * @param browser
   *        The <xul:browser> that is about to show the crashed page.
   */
  enterCrashedState(browser) {
    this._crashedBrowsers.add(browser.permanentKey);

    let win = browser.ownerGlobal;

    // If we hadn't yet restored, or were still in the midst of
    // restoring this browser at the time of the crash, we need
    // to reset its state so that we can try to restore it again
    // when the user revives the tab from the crash.
    if (browser.__SS_restoreState) {
      let tab = win.gBrowser.getTabForBrowser(browser);
      this._resetLocalTabRestoringState(tab);
    }
  },

  // Clean up data that has been closed a long time ago.
  // Do not reschedule a save. This will wait for the next regular
  // save.
  onIdleDaily() {
    // Remove old closed windows
    this._cleanupOldData([this._closedWindows]);

    // Remove closed tabs of closed windows
    this._cleanupOldData(this._closedWindows.map((winData) => winData._closedTabs));

    // Remove closed tabs of open windows
    this._cleanupOldData(Object.keys(this._windows).map((key) => this._windows[key]._closedTabs));

    this._notifyOfClosedObjectsChange();
  },

  // Remove "old" data from an array
  _cleanupOldData(targets) {
    const TIME_TO_LIVE = this._prefBranch.getIntPref("sessionstore.cleanup.forget_closed_after");
    const now = Date.now();

    for (let array of targets) {
      for (let i = array.length - 1; i >= 0; --i) {
        let data = array[i];
        // Make sure that we have a timestamp to tell us when the target
        // has been closed. If we don't have a timestamp, default to a
        // safe timestamp: just now.
        data.closedAt = data.closedAt || now;
        if (now - data.closedAt > TIME_TO_LIVE) {
          array.splice(i, 1);
          this._closedObjectsChanged = true;
        }
      }
    }
  },

  /* ........ nsISessionStore API .............. */

  getBrowserState: function ssi_getBrowserState() {
    let state = this.getCurrentState();

    // Don't include the last session state in getBrowserState().
    delete state.lastSessionState;

    // Don't include any deferred initial state.
    delete state.deferredInitialState;

    return JSON.stringify(state);
  },

  setBrowserState: function ssi_setBrowserState(aState) {
    this._handleClosedWindows();

    try {
      var state = JSON.parse(aState);
    } catch (ex) { /* invalid state object - don't restore anything */ }
    if (!state) {
      throw Components.Exception("Invalid state string: not JSON", Cr.NS_ERROR_INVALID_ARG);
    }
    if (!state.windows) {
      throw Components.Exception("No windows", Cr.NS_ERROR_INVALID_ARG);
    }

    this._browserSetState = true;

    // Make sure the priority queue is emptied out
    this._resetRestoringState();

    var window = this._getMostRecentBrowserWindow();
    if (!window) {
      this._restoreCount = 1;
      this._openWindowWithState(state);
      return;
    }

    // close all other browser windows
    this._forEachBrowserWindow(function(aWindow) {
      if (aWindow != window) {
        aWindow.close();
        this.onClose(aWindow);
      }
    });

    // make sure closed window data isn't kept
    if (this._closedWindows.length) {
      this._closedWindows = [];
      this._closedObjectsChanged = true;
    }

    // determine how many windows are meant to be restored
    this._restoreCount = state.windows ? state.windows.length : 0;

    // global data must be restored before restoreWindow is called so that
    // it happens before observers are notified
    this._globalState.setFromState(state);

    // Restore session cookies.
    SessionCookies.restore(state.cookies || []);

    // restore to the given state
    this.restoreWindows(window, state, {overwriteTabs: true});

    // Notify of changes to closed objects.
    this._notifyOfClosedObjectsChange();
  },

  getWindowState: function ssi_getWindowState(aWindow) {
    if ("__SSi" in aWindow) {
      return JSON.stringify(this._getWindowState(aWindow));
    }

    if (DyingWindowCache.has(aWindow)) {
      let data = DyingWindowCache.get(aWindow);
      return JSON.stringify({ windows: [data] });
    }

    throw Components.Exception("Window is not tracked", Cr.NS_ERROR_INVALID_ARG);
  },

  setWindowState: function ssi_setWindowState(aWindow, aState, aOverwrite) {
    if (!aWindow.__SSi) {
      throw Components.Exception("Window is not tracked", Cr.NS_ERROR_INVALID_ARG);
    }

    this.restoreWindows(aWindow, aState, {overwriteTabs: aOverwrite});

    // Notify of changes to closed objects.
    this._notifyOfClosedObjectsChange();
  },

  getTabState: function ssi_getTabState(aTab) {
    if (!aTab.ownerGlobal.__SSi) {
      throw Components.Exception("Default view is not tracked", Cr.NS_ERROR_INVALID_ARG);
    }

    let tabState = TabState.collect(aTab);

    return JSON.stringify(tabState);
  },

  setTabState(aTab, aState) {
    // Remove the tab state from the cache.
    // Note that we cannot simply replace the contents of the cache
    // as |aState| can be an incomplete state that will be completed
    // by |restoreTabs|.
    let tabState = JSON.parse(aState);
    if (!tabState) {
      throw Components.Exception("Invalid state string: not JSON", Cr.NS_ERROR_INVALID_ARG);
    }
    if (typeof tabState != "object") {
      throw Components.Exception("Not an object", Cr.NS_ERROR_INVALID_ARG);
    }
    if (!("entries" in tabState)) {
      throw Components.Exception("Invalid state object: no entries", Cr.NS_ERROR_INVALID_ARG);
    }

    let window = aTab.ownerGlobal;
    if (!("__SSi" in window)) {
      throw Components.Exception("Window is not tracked", Cr.NS_ERROR_INVALID_ARG);
    }

    if (aTab.linkedBrowser.__SS_restoreState) {
      this._resetTabRestoringState(aTab);
    }

    this.restoreTab(aTab, tabState);

    // Notify of changes to closed objects.
    this._notifyOfClosedObjectsChange();
  },

  duplicateTab: function ssi_duplicateTab(aWindow, aTab, aDelta = 0, aRestoreImmediately = true) {
    if (!aTab.ownerGlobal.__SSi) {
      throw Components.Exception("Default view is not tracked", Cr.NS_ERROR_INVALID_ARG);
    }
    if (!aWindow.gBrowser) {
      throw Components.Exception("Invalid window object: no gBrowser", Cr.NS_ERROR_INVALID_ARG);
    }

    // Create a new tab.
    let userContextId = aTab.getAttribute("usercontextid");
    let newTab = aTab == aWindow.gBrowser.selectedTab ?
      aWindow.gBrowser.addTab(null, {relatedToCurrent: true, ownerTab: aTab, userContextId}) :
      aWindow.gBrowser.addTab(null, {userContextId});

    // Start the throbber to pretend we're doing something while actually
    // waiting for data from the frame script.
    newTab.setAttribute("busy", "true");

    // Collect state before flushing.
    let tabState = TabState.clone(aTab);

    // Flush to get the latest tab state to duplicate.
    let browser = aTab.linkedBrowser;
    TabStateFlusher.flush(browser).then(() => {
      // The new tab might have been closed in the meantime.
      if (newTab.closing || !newTab.linkedBrowser) {
        return;
      }

      let window = newTab.ownerGlobal;

      // The tab or its window might be gone.
      if (!window || !window.__SSi) {
        return;
      }

      // Update state with flushed data. We can't use TabState.clone() here as
      // the tab to duplicate may have already been closed. In that case we
      // only have access to the <xul:browser>.
      let options = {includePrivateData: true};
      TabState.copyFromCache(browser, tabState, options);

      tabState.index += aDelta;
      tabState.index = Math.max(1, Math.min(tabState.index, tabState.entries.length));
      tabState.pinned = false;

      // Restore the state into the new tab.
      this.restoreTab(newTab, tabState, {
        restoreImmediately: aRestoreImmediately
      });
    });

    return newTab;
  },

  getClosedTabCount: function ssi_getClosedTabCount(aWindow) {
    if ("__SSi" in aWindow) {
      return this._windows[aWindow.__SSi]._closedTabs.length;
    }

    if (!DyingWindowCache.has(aWindow)) {
      throw Components.Exception("Window is not tracked", Cr.NS_ERROR_INVALID_ARG);
    }

    return DyingWindowCache.get(aWindow)._closedTabs.length;
  },

  getClosedTabData: function ssi_getClosedTabData(aWindow, aAsString = true) {
    if ("__SSi" in aWindow) {
      return aAsString ?
        JSON.stringify(this._windows[aWindow.__SSi]._closedTabs) :
        Cu.cloneInto(this._windows[aWindow.__SSi]._closedTabs, {});
    }

    if (!DyingWindowCache.has(aWindow)) {
      throw Components.Exception("Window is not tracked", Cr.NS_ERROR_INVALID_ARG);
    }

    let data = DyingWindowCache.get(aWindow);
    return aAsString ? JSON.stringify(data._closedTabs) : Cu.cloneInto(data._closedTabs, {});
  },

  undoCloseTab: function ssi_undoCloseTab(aWindow, aIndex) {
    if (!aWindow.__SSi) {
      throw Components.Exception("Window is not tracked", Cr.NS_ERROR_INVALID_ARG);
    }

    var closedTabs = this._windows[aWindow.__SSi]._closedTabs;

    // default to the most-recently closed tab
    aIndex = aIndex || 0;
    if (!(aIndex in closedTabs)) {
      throw Components.Exception("Invalid index: not in the closed tabs", Cr.NS_ERROR_INVALID_ARG);
    }

    // fetch the data of closed tab, while removing it from the array
    let {state, pos} = this.removeClosedTabData(closedTabs, aIndex);

    // create a new tab
    let tabbrowser = aWindow.gBrowser;
    let tab = tabbrowser.selectedTab = tabbrowser.addTab(null, state);

    // restore tab content
    this.restoreTab(tab, state);

    // restore the tab's position
    tabbrowser.moveTabTo(tab, pos);

    // focus the tab's content area (bug 342432)
    tab.linkedBrowser.focus();

    // Notify of changes to closed objects.
    this._notifyOfClosedObjectsChange();

    return tab;
  },

  forgetClosedTab: function ssi_forgetClosedTab(aWindow, aIndex) {
    if (!aWindow.__SSi) {
      throw Components.Exception("Window is not tracked", Cr.NS_ERROR_INVALID_ARG);
    }

    var closedTabs = this._windows[aWindow.__SSi]._closedTabs;

    // default to the most-recently closed tab
    aIndex = aIndex || 0;
    if (!(aIndex in closedTabs)) {
      throw Components.Exception("Invalid index: not in the closed tabs", Cr.NS_ERROR_INVALID_ARG);
    }

    // remove closed tab from the array
    this.removeClosedTabData(closedTabs, aIndex);

    // Notify of changes to closed objects.
    this._notifyOfClosedObjectsChange();
  },

  getClosedWindowCount: function ssi_getClosedWindowCount() {
    return this._closedWindows.length;
  },

  getClosedWindowData: function ssi_getClosedWindowData(aAsString = true) {
    return aAsString ? JSON.stringify(this._closedWindows) : Cu.cloneInto(this._closedWindows, {});
  },

  undoCloseWindow: function ssi_undoCloseWindow(aIndex) {
    if (!(aIndex in this._closedWindows)) {
      throw Components.Exception("Invalid index: not in the closed windows", Cr.NS_ERROR_INVALID_ARG);
    }

    // reopen the window
    let state = { windows: this._removeClosedWindow(aIndex) };
    delete state.windows[0].closedAt; // Window is now open.

    let window = this._openWindowWithState(state);
    this.windowToFocus = window;

    // Notify of changes to closed objects.
    this._notifyOfClosedObjectsChange();

    return window;
  },

  forgetClosedWindow: function ssi_forgetClosedWindow(aIndex) {
    // default to the most-recently closed window
    aIndex = aIndex || 0;
    if (!(aIndex in this._closedWindows)) {
      throw Components.Exception("Invalid index: not in the closed windows", Cr.NS_ERROR_INVALID_ARG);
    }

    // remove closed window from the array
    let winData = this._closedWindows[aIndex];
    this._removeClosedWindow(aIndex);
    this._saveableClosedWindowData.delete(winData);

    // Notify of changes to closed objects.
    this._notifyOfClosedObjectsChange();
  },

  getWindowValue: function ssi_getWindowValue(aWindow, aKey) {
    if ("__SSi" in aWindow) {
      let data = this._windows[aWindow.__SSi].extData || {};
      return data[aKey] || "";
    }

    if (DyingWindowCache.has(aWindow)) {
      let data = DyingWindowCache.get(aWindow).extData || {};
      return data[aKey] || "";
    }

    throw Components.Exception("Window is not tracked", Cr.NS_ERROR_INVALID_ARG);
  },

  setWindowValue: function ssi_setWindowValue(aWindow, aKey, aStringValue) {
    if (typeof aStringValue != "string") {
      throw new TypeError("setWindowValue only accepts string values");
    }

    if (!("__SSi" in aWindow)) {
      throw Components.Exception("Window is not tracked", Cr.NS_ERROR_INVALID_ARG);
    }
    if (!this._windows[aWindow.__SSi].extData) {
      this._windows[aWindow.__SSi].extData = {};
    }
    this._windows[aWindow.__SSi].extData[aKey] = aStringValue;
    this.saveStateDelayed(aWindow);
  },

  deleteWindowValue: function ssi_deleteWindowValue(aWindow, aKey) {
    if (aWindow.__SSi && this._windows[aWindow.__SSi].extData &&
        this._windows[aWindow.__SSi].extData[aKey])
      delete this._windows[aWindow.__SSi].extData[aKey];
    this.saveStateDelayed(aWindow);
  },

  getTabValue: function ssi_getTabValue(aTab, aKey) {
    return (aTab.__SS_extdata || {})[aKey] || "";
  },

  setTabValue: function ssi_setTabValue(aTab, aKey, aStringValue) {
    if (typeof aStringValue != "string") {
      throw new TypeError("setTabValue only accepts string values");
    }

    // If the tab hasn't been restored, then set the data there, otherwise we
    // could lose newly added data.
    if (!aTab.__SS_extdata) {
      aTab.__SS_extdata = {};
    }

    aTab.__SS_extdata[aKey] = aStringValue;
    this.saveStateDelayed(aTab.ownerGlobal);
  },

  deleteTabValue: function ssi_deleteTabValue(aTab, aKey) {
    if (aTab.__SS_extdata && aKey in aTab.__SS_extdata) {
      delete aTab.__SS_extdata[aKey];
      this.saveStateDelayed(aTab.ownerGlobal);
    }
  },

  /**
   * Retrieves data specific to lazy-browser tabs.  If tab is not lazy,
   * will return undefined.
   *
   * @param aTab (xul:tab)
   *        The tabbrowser-tab the data is for.
   * @param aKey (string)
   *        The key which maps to the desired data.
   */
  getLazyTabValue(aTab, aKey) {
    return (aTab.__SS_lazyData || {})[aKey];
  },

  getGlobalValue: function ssi_getGlobalValue(aKey) {
    return this._globalState.get(aKey);
  },

  setGlobalValue: function ssi_setGlobalValue(aKey, aStringValue) {
    if (typeof aStringValue != "string") {
      throw new TypeError("setGlobalValue only accepts string values");
    }

    this._globalState.set(aKey, aStringValue);
    this.saveStateDelayed();
  },

  deleteGlobalValue: function ssi_deleteGlobalValue(aKey) {
    this._globalState.delete(aKey);
    this.saveStateDelayed();
  },

  persistTabAttribute: function ssi_persistTabAttribute(aName) {
    if (TabAttributes.persist(aName)) {
      this.saveStateDelayed();
    }
  },


  /**
   * Undoes the closing of a tab or window which corresponds
   * to the closedId passed in.
   *
   * @param aClosedId
   *        The closedId of the tab or window
   *
   * @returns a tab or window object
   */
  undoCloseById(aClosedId) {
    // Check for a window first.
    for (let i = 0, l = this._closedWindows.length; i < l; i++) {
      if (this._closedWindows[i].closedId == aClosedId) {
        return this.undoCloseWindow(i);
      }
    }

    // Check for a tab.
    let windowsEnum = Services.wm.getEnumerator("navigator:browser");
    while (windowsEnum.hasMoreElements()) {
      let window = windowsEnum.getNext();
      let windowState = this._windows[window.__SSi];
      if (windowState) {
        for (let j = 0, l = windowState._closedTabs.length; j < l; j++) {
          if (windowState._closedTabs[j].closedId == aClosedId) {
            return this.undoCloseTab(window, j);
          }
        }
      }
    }

    // Neither a tab nor a window was found, return undefined and let the caller decide what to do about it.
    return undefined;
  },

  /**
   * Updates the label and icon for a <xul:tab> using the data from
   * tabData.
   *
   * @param tab
   *        The <xul:tab> to update.
   * @param tabData (optional)
   *        The tabData to use to update the tab. If the argument is
   *        not supplied, the data will be retrieved from the cache.
   */
  updateTabLabelAndIcon(tab, tabData = null) {
    if (tab.hasAttribute("customizemode")) {
      return;
    }

    let browser = tab.linkedBrowser;
    let win = browser.ownerGlobal;

    if (!tabData) {
      tabData = TabState.collect(tab);
      if (!tabData) {
        throw new Error("tabData not found for given tab");
      }
    }

    let activePageData = tabData.entries[tabData.index - 1] || null;

    // If the page has a title, set it.
    if (activePageData) {
      if (activePageData.title &&
          activePageData.title != activePageData.url) {
        win.gBrowser.setInitialTabTitle(tab, activePageData.title, { isContentTitle: true });
      } else if (activePageData.url != "about:blank") {
        win.gBrowser.setInitialTabTitle(tab, activePageData.url);
      }
    }

    // Restore the tab icon.
    if ("image" in tabData) {
      // Use the serialized contentPrincipal with the new icon load.
      let loadingPrincipal = Utils.deserializePrincipal(tabData.iconLoadingPrincipal);
      win.gBrowser.setIcon(tab, tabData.image, loadingPrincipal);
      TabStateCache.update(browser, { image: null, iconLoadingPrincipal: null });
    }
  },

  // This method deletes all the closedTabs matching userContextId.
  _forgetTabsWithUserContextId(userContextId) {
    let windowsEnum = Services.wm.getEnumerator("navigator:browser");
    while (windowsEnum.hasMoreElements()) {
      let window = windowsEnum.getNext();
      let windowState = this._windows[window.__SSi];
      if (windowState) {
        // In order to remove the tabs in the correct order, we store the
        // indexes, into an array, then we revert the array and remove closed
        // data from the last one going backward.
        let indexes = [];
        windowState._closedTabs.forEach((closedTab, index) => {
          if (closedTab.state.userContextId == userContextId) {
            indexes.push(index);
          }
        });

        for (let index of indexes.reverse()) {
          this.removeClosedTabData(windowState._closedTabs, index);
        }
      }
    }

    // Notify of changes to closed objects.
    this._notifyOfClosedObjectsChange();
  },

  /**
   * Restores the session state stored in LastSession. This will attempt
   * to merge data into the current session. If a window was opened at startup
   * with pinned tab(s), then the remaining data from the previous session for
   * that window will be opened into that window. Otherwise new windows will
   * be opened.
   */
  restoreLastSession: function ssi_restoreLastSession() {
    // Use the public getter since it also checks PB mode
    if (!this.canRestoreLastSession) {
      throw Components.Exception("Last session can not be restored");
    }

    Services.obs.notifyObservers(null, NOTIFY_INITIATING_MANUAL_RESTORE);

    // First collect each window with its id...
    let windows = {};
    this._forEachBrowserWindow(function(aWindow) {
      if (aWindow.__SS_lastSessionWindowID)
        windows[aWindow.__SS_lastSessionWindowID] = aWindow;
    });

    let lastSessionState = LastSession.getState();

    // This shouldn't ever be the case...
    if (!lastSessionState.windows.length) {
      throw Components.Exception("lastSessionState has no windows", Cr.NS_ERROR_UNEXPECTED);
    }

    // We're technically doing a restore, so set things up so we send the
    // notification when we're done. We want to send "sessionstore-browser-state-restored".
    this._restoreCount = lastSessionState.windows.length;
    this._browserSetState = true;

    // We want to re-use the last opened window instead of opening a new one in
    // the case where it's "empty" and not associated with a window in the session.
    // We will do more processing via _prepWindowToRestoreInto if we need to use
    // the lastWindow.
    let lastWindow = this._getMostRecentBrowserWindow();
    let canUseLastWindow = lastWindow &&
                           !lastWindow.__SS_lastSessionWindowID;

    // global data must be restored before restoreWindow is called so that
    // it happens before observers are notified
    this._globalState.setFromState(lastSessionState);

    // Restore session cookies.
    SessionCookies.restore(lastSessionState.cookies || []);

    // Restore into windows or open new ones as needed.
    for (let i = 0; i < lastSessionState.windows.length; i++) {
      let winState = lastSessionState.windows[i];
      let lastSessionWindowID = winState.__lastSessionWindowID;
      // delete lastSessionWindowID so we don't add that to the window again
      delete winState.__lastSessionWindowID;

      // See if we can use an open window. First try one that is associated with
      // the state we're trying to restore and then fallback to the last selected
      // window.
      let windowToUse = windows[lastSessionWindowID];
      if (!windowToUse && canUseLastWindow) {
        windowToUse = lastWindow;
        canUseLastWindow = false;
      }

      let [canUseWindow, canOverwriteTabs] = this._prepWindowToRestoreInto(windowToUse);

      // If there's a window already open that we can restore into, use that
      if (canUseWindow) {
        // Since we're not overwriting existing tabs, we want to merge _closedTabs,
        // putting existing ones first. Then make sure we're respecting the max pref.
        if (winState._closedTabs && winState._closedTabs.length) {
          let curWinState = this._windows[windowToUse.__SSi];
          curWinState._closedTabs = curWinState._closedTabs.concat(winState._closedTabs);
          curWinState._closedTabs.splice(this._max_tabs_undo, curWinState._closedTabs.length);
        }

        // Restore into that window - pretend it's a followup since we'll already
        // have a focused window.
        // XXXzpao This is going to merge extData together (taking what was in
        //        winState over what is in the window already.
        let options = {overwriteTabs: canOverwriteTabs, isFollowUp: true};
        this.restoreWindow(windowToUse, winState, options);
      } else {
        this._openWindowWithState({ windows: [winState] });
      }
    }

    // Merge closed windows from this session with ones from last session
    if (lastSessionState._closedWindows) {
      this._closedWindows = this._closedWindows.concat(lastSessionState._closedWindows);
      this._capClosedWindows();
      this._closedObjectsChanged = true;
    }

    DevToolsShim.restoreDevToolsSession(lastSessionState);

    // Set data that persists between sessions
    this._recentCrashes = lastSessionState.session &&
                          lastSessionState.session.recentCrashes || 0;

    // Update the session start time using the restored session state.
    this._updateSessionStartTime(lastSessionState);

    LastSession.clear();

    // Notify of changes to closed objects.
    this._notifyOfClosedObjectsChange();
  },

  /**
   * Revive a crashed tab and restore its state from before it crashed.
   *
   * @param aTab
   *        A <xul:tab> linked to a crashed browser. This is a no-op if the
   *        browser hasn't actually crashed, or is not associated with a tab.
   *        This function will also throw if the browser happens to be remote.
   */
  reviveCrashedTab(aTab) {
    if (!aTab) {
      throw new Error("SessionStore.reviveCrashedTab expected a tab, but got null.");
    }

    let browser = aTab.linkedBrowser;
    if (!this._crashedBrowsers.has(browser.permanentKey)) {
      return;
    }

    // Sanity check - the browser to be revived should not be remote
    // at this point.
    if (browser.isRemoteBrowser) {
      throw new Error("SessionStore.reviveCrashedTab: " +
                      "Somehow a crashed browser is still remote.");
    }

    // We put the browser at about:blank in case the user is
    // restoring tabs on demand. This way, the user won't see
    // a flash of the about:tabcrashed page after selecting
    // the revived tab.
    aTab.removeAttribute("crashed");
    browser.loadURI("about:blank", null, null);

    let data = TabState.collect(aTab);
    this.restoreTab(aTab, data, {
      forceOnDemand: true,
    });
  },

  /**
   * Revive all crashed tabs and reset the crashed tabs count to 0.
   */
  reviveAllCrashedTabs() {
    let windowsEnum = Services.wm.getEnumerator("navigator:browser");
    while (windowsEnum.hasMoreElements()) {
      let window = windowsEnum.getNext();
      for (let tab of window.gBrowser.tabs) {
        this.reviveCrashedTab(tab);
      }
    }
  },

  /**
   * Navigate the given |tab| by first collecting its current state and then
   * either changing only the index of the currently shown history entry,
   * or restoring the exact same state again and passing the new URL to load
   * in |loadArguments|. Use this method to seamlessly switch between pages
   * loaded in the parent and pages loaded in the child process.
   *
   * This method might be called multiple times before it has finished
   * flushing the browser tab. If that occurs, the loadArguments from
   * the most recent call to navigateAndRestore will be used once the
   * flush has finished.
   */
  navigateAndRestore(tab, loadArguments, historyIndex) {
    let window = tab.ownerGlobal;

    if (!window.__SSi) {
      Cu.reportError("Tab's window must be tracked.");
      return;
    }

    let browser = tab.linkedBrowser;

    // Were we already waiting for a flush from a previous call to
    // navigateAndRestore on this tab?
    let alreadyRestoring =
      this._remotenessChangingBrowsers.has(browser.permanentKey);

    // Stash the most recent loadArguments in this WeakMap so that
    // we know to use it when the TabStateFlusher.flush resolves.
    this._remotenessChangingBrowsers.set(browser.permanentKey, loadArguments);

    if (alreadyRestoring) {
      // This tab was already being restored to run in the
      // correct process. We're done here.
      return;
    }

    // Start the throbber to pretend we're doing something while actually
    // waiting for data from the frame script.
    tab.setAttribute("busy", "true");

    // Flush to get the latest tab state.
    TabStateFlusher.flush(browser).then(() => {
      // loadArguments might have been overwritten by multiple calls
      // to navigateAndRestore while we waited for the tab to flush,
      // so we use the most recently stored one.
      let recentLoadArguments =
        this._remotenessChangingBrowsers.get(browser.permanentKey);
      this._remotenessChangingBrowsers.delete(browser.permanentKey);

      // The tab might have been closed/gone in the meantime.
      if (tab.closing || !tab.linkedBrowser) {
        return;
      }

      let refreshedWindow = tab.ownerGlobal;

      // The tab or its window might be gone.
      if (!refreshedWindow || !refreshedWindow.__SSi || refreshedWindow.closed) {
        return;
      }

      let tabState = TabState.clone(tab);
      let options = {
        restoreImmediately: true,
        // We want to make sure that this information is passed to restoreTab
        // whether or not a historyIndex is passed in. Thus, we extract it from
        // the loadArguments.
        newFrameloader: recentLoadArguments.newFrameloader,
        remoteType: recentLoadArguments.remoteType,
        // Make sure that SessionStore knows that this restoration is due
        // to a navigation, as opposed to us restoring a closed window or tab.
        restoreContentReason: RESTORE_TAB_CONTENT_REASON.NAVIGATE_AND_RESTORE,
      };

      if (historyIndex >= 0) {
        tabState.index = historyIndex + 1;
        tabState.index = Math.max(1, Math.min(tabState.index, tabState.entries.length));
      } else {
        options.loadArguments = recentLoadArguments;
      }

      // Need to reset restoring tabs.
      if (tab.linkedBrowser.__SS_restoreState) {
        this._resetLocalTabRestoringState(tab);
      }

      // Restore the state into the tab.
      this.restoreTab(tab, tabState, options);
    });

    tab.linkedBrowser.__SS_restoreState = TAB_STATE_WILL_RESTORE;

    // Notify of changes to closed objects.
    this._notifyOfClosedObjectsChange();
  },

  /**
   * Retrieves the latest session history information for a tab. The cached data
   * is returned immediately, but a callback may be provided that supplies
   * up-to-date data when or if it is available. The callback is passed a single
   * argument with data in the same format as the return value.
   *
   * @param tab tab to retrieve the session history for
   * @param updatedCallback function to call with updated data as the single argument
   * @returns a object containing 'index' specifying the current index, and an
   * array 'entries' containing an object for each history item.
   */
  getSessionHistory(tab, updatedCallback) {
    if (updatedCallback) {
      TabStateFlusher.flush(tab.linkedBrowser).then(() => {
        let sessionHistory = this.getSessionHistory(tab);
        if (sessionHistory) {
          updatedCallback(sessionHistory);
        }
      });
    }

    // Don't continue if the tab was closed before TabStateFlusher.flush resolves.
    if (tab.linkedBrowser) {
      let tabState = TabState.collect(tab);
      return { index: tabState.index - 1, entries: tabState.entries };
    }
    return null;
  },

  /**
   * See if aWindow is usable for use when restoring a previous session via
   * restoreLastSession. If usable, prepare it for use.
   *
   * @param aWindow
   *        the window to inspect & prepare
   * @returns [canUseWindow, canOverwriteTabs]
   *          canUseWindow: can the window be used to restore into
   *          canOverwriteTabs: all of the current tabs are home pages and we
   *                            can overwrite them
   */
  _prepWindowToRestoreInto: function ssi_prepWindowToRestoreInto(aWindow) {
    if (!aWindow)
      return [false, false];

    // We might be able to overwrite the existing tabs instead of just adding
    // the previous session's tabs to the end. This will be set if possible.
    let canOverwriteTabs = false;

    // Look at the open tabs in comparison to home pages. If all the tabs are
    // home pages then we'll end up overwriting all of them. Otherwise we'll
    // just close the tabs that match home pages. Tabs with the about:blank
    // URI will always be overwritten.
    let homePages = ["about:blank"];
    let removableTabs = [];
    let tabbrowser = aWindow.gBrowser;
    let startupPref = this._prefBranch.getIntPref("startup.page");
    if (startupPref == 1)
      homePages = homePages.concat(aWindow.gHomeButton.getHomePage().split("|"));

    for (let i = tabbrowser._numPinnedTabs; i < tabbrowser.tabs.length; i++) {
      let tab = tabbrowser.tabs[i];
      if (homePages.includes(tab.linkedBrowser.currentURI.spec)) {
        removableTabs.push(tab);
      }
    }

    if (tabbrowser.tabs.length == removableTabs.length) {
      canOverwriteTabs = true;
    } else {
      // If we're not overwriting all of the tabs, then close the home tabs.
      for (let i = removableTabs.length - 1; i >= 0; i--) {
        tabbrowser.removeTab(removableTabs.pop(), { animate: false });
      }
    }

    return [true, canOverwriteTabs];
  },

  /* ........ Saving Functionality .............. */

  /**
   * Store window dimensions, visibility, sidebar
   * @param aWindow
   *        Window reference
   */
  _updateWindowFeatures: function ssi_updateWindowFeatures(aWindow) {
    var winData = this._windows[aWindow.__SSi];

    WINDOW_ATTRIBUTES.forEach(function(aAttr) {
      winData[aAttr] = this._getWindowDimension(aWindow, aAttr);
    }, this);

    var hidden = WINDOW_HIDEABLE_FEATURES.filter(function(aItem) {
      return aWindow[aItem] && !aWindow[aItem].visible;
    });
    if (hidden.length != 0)
      winData.hidden = hidden.join(",");
    else if (winData.hidden)
      delete winData.hidden;

    let sidebarBox = aWindow.document.getElementById("sidebar-box");
    let sidebar = sidebarBox.getAttribute("sidebarcommand");
    if (sidebar && sidebarBox.getAttribute("checked") == "true")
      winData.sidebar = sidebar;
    else if (winData.sidebar)
      delete winData.sidebar;
  },

  /**
   * gather session data as object
   * @param aUpdateAll
   *        Bool update all windows
   * @returns object
   */
  getCurrentState(aUpdateAll) {
    this._handleClosedWindows().then(() => {
      this._notifyOfClosedObjectsChange();
    });

    var activeWindow = this._getMostRecentBrowserWindow();

    TelemetryStopwatch.start("FX_SESSION_RESTORE_COLLECT_ALL_WINDOWS_DATA_MS");
    if (RunState.isRunning) {
      // update the data for all windows with activities since the last save operation
      this._forEachBrowserWindow(function(aWindow) {
        if (!this._isWindowLoaded(aWindow)) // window data is still in _statesToRestore
          return;
        if (aUpdateAll || DirtyWindows.has(aWindow) || aWindow == activeWindow) {
          this._collectWindowData(aWindow);
        } else { // always update the window features (whose change alone never triggers a save operation)
          this._updateWindowFeatures(aWindow);
        }
      });
      DirtyWindows.clear();
    }
    TelemetryStopwatch.finish("FX_SESSION_RESTORE_COLLECT_ALL_WINDOWS_DATA_MS");

    // An array that at the end will hold all current window data.
    var total = [];
    // The ids of all windows contained in 'total' in the same order.
    var ids = [];
    // The number of window that are _not_ popups.
    var nonPopupCount = 0;
    var ix;

    // collect the data for all windows
    for (ix in this._windows) {
      if (this._windows[ix]._restoring) // window data is still in _statesToRestore
        continue;
      total.push(this._windows[ix]);
      ids.push(ix);
      if (!this._windows[ix].isPopup)
        nonPopupCount++;
    }

    // collect the data for all windows yet to be restored
    for (ix in this._statesToRestore) {
      for (let winData of this._statesToRestore[ix].windows) {
        total.push(winData);
        if (!winData.isPopup)
          nonPopupCount++;
      }
    }

    // shallow copy this._closedWindows to preserve current state
    let lastClosedWindowsCopy = this._closedWindows.slice();

    if (AppConstants.platform != "macosx") {
      // If no non-popup browser window remains open, return the state of the last
      // closed window(s). We only want to do this when we're actually "ending"
      // the session.
      // XXXzpao We should do this for _restoreLastWindow == true, but that has
      //        its own check for popups. c.f. bug 597619
      if (nonPopupCount == 0 && lastClosedWindowsCopy.length > 0 &&
          RunState.isQuitting) {
        // prepend the last non-popup browser window, so that if the user loads more tabs
        // at startup we don't accidentally add them to a popup window
        do {
          total.unshift(lastClosedWindowsCopy.shift());
        } while (total[0].isPopup && lastClosedWindowsCopy.length > 0);
      }
    }

    if (activeWindow) {
      this.activeWindowSSiCache = activeWindow.__SSi || "";
    }
    ix = ids.indexOf(this.activeWindowSSiCache);
    // We don't want to restore focus to a minimized window or a window which had all its
    // tabs stripped out (doesn't exist).
    if (ix != -1 && total[ix] && total[ix].sizemode == "minimized")
      ix = -1;

    let session = {
      lastUpdate: Date.now(),
      startTime: this._sessionStartTime,
      recentCrashes: this._recentCrashes
    };

    let state = {
      version: ["sessionrestore", FORMAT_VERSION],
      windows: total,
      selectedWindow: ix + 1,
      _closedWindows: lastClosedWindowsCopy,
      session,
      global: this._globalState.getState()
    };

    // Collect and store session cookies.
    state.cookies = SessionCookies.collect();

    DevToolsShim.saveDevToolsSession(state);

    // Persist the last session if we deferred restoring it
    if (LastSession.canRestore) {
      state.lastSessionState = LastSession.getState();
    }

    // If we were called by the SessionSaver and started with only a private
    // window we want to pass the deferred initial state to not lose the
    // previous session.
    if (this._deferredInitialState) {
      state.deferredInitialState = this._deferredInitialState;
    }

    return state;
  },

  /**
   * serialize session data for a window
   * @param aWindow
   *        Window reference
   * @returns string
   */
  _getWindowState: function ssi_getWindowState(aWindow) {
    if (!this._isWindowLoaded(aWindow))
      return this._statesToRestore[aWindow.__SS_restoreID];

    if (RunState.isRunning) {
      this._collectWindowData(aWindow);
    }

    return { windows: [this._windows[aWindow.__SSi]] };
  },

  /**
   * Gathers data about a window and its tabs, and updates its
   * entry in this._windows.
   *
   * @param aWindow
   *        Window references.
   * @returns a Map mapping the browser tabs from aWindow to the tab
   *          entry that was put into the window data in this._windows.
   */
  _collectWindowData: function ssi_collectWindowData(aWindow) {
    let tabMap = new Map();

    if (!this._isWindowLoaded(aWindow))
      return tabMap;

    let tabbrowser = aWindow.gBrowser;
    let tabs = tabbrowser.tabs;
    let winData = this._windows[aWindow.__SSi];
    let tabsData = winData.tabs = [];

    // update the internal state data for this window
    for (let tab of tabs) {
      let tabData = TabState.collect(tab);
      tabMap.set(tab, tabData);
      tabsData.push(tabData);
    }
    winData.selected = tabbrowser.tabbox.selectedIndex + 1;

    this._updateWindowFeatures(aWindow);

    // Make sure we keep __SS_lastSessionWindowID around for cases like entering
    // or leaving PB mode.
    if (aWindow.__SS_lastSessionWindowID)
      this._windows[aWindow.__SSi].__lastSessionWindowID =
        aWindow.__SS_lastSessionWindowID;

    DirtyWindows.remove(aWindow);
    return tabMap;
  },

  /* ........ Restoring Functionality .............. */

  /**
   * restore features to a single window
   * @param aWindow
   *        Window reference to the window to use for restoration
   * @param winData
   *        JS object
   * @param aOptions
   *        {overwriteTabs: true} to overwrite existing tabs w/ new ones
   *        {isFollowUp: true} if this is not the restoration of the 1st window
   *        {firstWindow: true} if this is the first non-private window we're
   *                            restoring in this session, that might open an
   *                            external link as well
   */
  restoreWindow: function ssi_restoreWindow(aWindow, winData, aOptions = {}) {
    let overwriteTabs = aOptions && aOptions.overwriteTabs;
    let isFollowUp = aOptions && aOptions.isFollowUp;
    let firstWindow = aOptions && aOptions.firstWindow;

    if (isFollowUp) {
      this.windowToFocus = aWindow;
    }

    // initialize window if necessary
    if (aWindow && (!aWindow.__SSi || !this._windows[aWindow.__SSi]))
      this.onLoad(aWindow);

    TelemetryStopwatch.start("FX_SESSION_RESTORE_RESTORE_WINDOW_MS");

    // We're not returning from this before we end up calling restoreTabs
    // for this window, so make sure we send the SSWindowStateBusy event.
    this._setWindowStateBusy(aWindow);

    if (!winData.tabs) {
      winData.tabs = [];
    // don't restore a single blank tab when we've had an external
    // URL passed in for loading at startup (cf. bug 357419)
    } else if (firstWindow && !overwriteTabs && winData.tabs.length == 1 &&
             (!winData.tabs[0].entries || winData.tabs[0].entries.length == 0)) {
      winData.tabs = [];
    }

    // See SessionStoreInternal.restoreTabs for a description of what
    // selectTab represents.
    let selectTab = 0;
    if (overwriteTabs) {
      selectTab = parseInt(winData.selected || 1, 10);
      selectTab = Math.max(selectTab, 1);
      selectTab = Math.min(selectTab, winData.tabs.length);
    }

    let tabbrowser = aWindow.gBrowser;
    let newTabCount = winData.tabs.length;
    var tabs = [];

    // disable smooth scrolling while adding, moving, removing and selecting tabs
    let arrowScrollbox = tabbrowser.tabContainer.arrowScrollbox;
    let smoothScroll = arrowScrollbox.smoothScroll;
    arrowScrollbox.smoothScroll = false;

    // We need to keep track of the initially open tabs so that they
    // can be moved to the end of the restored tabs.
    let initialTabs;
    if (!overwriteTabs && firstWindow) {
      initialTabs = Array.slice(tabbrowser.tabs);
    }

    // Get rid of tabs that aren't needed anymore.
    if (overwriteTabs) {
      for (let i = tabbrowser.browsers.length - 1; i >= 0; i--) {
        if (!tabbrowser.tabs[i].selected) {
          tabbrowser.removeTab(tabbrowser.tabs[i]);
        }
      }
    }

    let restoreTabsLazily = this._prefBranch.getBoolPref("sessionstore.restore_tabs_lazily") && this._restore_on_demand;

    for (var t = 0; t < newTabCount; t++) {
      let tabData = winData.tabs[t];

      let userContextId = tabData.userContextId;
      let select = t == selectTab - 1;
      let tab;

      // Re-use existing selected tab if possible to avoid the overhead of
      // selecting a new tab.
      if (select &&
          tabbrowser.selectedTab.userContextId == userContextId) {
        tab = tabbrowser.selectedTab;
        if (!tabData.pinned) {
          tabbrowser.unpinTab(tab);
        }
        tabbrowser.moveTabToEnd();
        if (aWindow.gMultiProcessBrowser && !tab.linkedBrowser.isRemoteBrowser) {
          tabbrowser.updateBrowserRemoteness(tab.linkedBrowser, true);
        }
      }

      // Add a new tab if needed.
      if (!tab) {
        let createLazyBrowser = restoreTabsLazily && !select && !tabData.pinned;

        let url = "about:blank";
        if (createLazyBrowser && tabData.entries && tabData.entries.length) {
          // Let tabbrowser know the future URI because progress listeners won't
          // get onLocationChange notification before the browser is inserted.
          let activeIndex = (tabData.index || tabData.entries.length) - 1;
          // Ensure the index is in bounds.
          activeIndex = Math.min(activeIndex, tabData.entries.length - 1);
          activeIndex = Math.max(activeIndex, 0);
          url = tabData.entries[activeIndex].url;
        }

        // Setting noInitialLabel is a perf optimization. Rendering tab labels
        // would make resizing the tabs more expensive as we're adding them.
        // Each tab will get its initial label set in restoreTab.
        tab = tabbrowser.addTab(url,
                                { createLazyBrowser,
                                  skipAnimation: true,
                                  noInitialLabel: true,
                                  userContextId,
                                  skipBackgroundNotify: true });

        if (select) {
          let leftoverTab = tabbrowser.selectedTab;
          tabbrowser.selectedTab = tab;
          tabbrowser.removeTab(leftoverTab);
        }
      }

      tabs.push(tab);

      if (tabData.hidden) {
        tabbrowser.hideTab(tab, tabData.extData && tabData.extData.hiddenBy);
      }

      if (tabData.pinned) {
        tabbrowser.pinTab(tab);
      }
    }

    // Move the originally open tabs to the end.
    if (initialTabs) {
      let endPosition = tabbrowser.tabs.length - 1;
      for (let i = 0; i < initialTabs.length; i++) {
        tabbrowser.unpinTab(initialTabs[i]);
        tabbrowser.moveTabTo(initialTabs[i], endPosition);
      }
    }

    // We want to correlate the window with data from the last session, so
    // assign another id if we have one. Otherwise clear so we don't do
    // anything with it.
    delete aWindow.__SS_lastSessionWindowID;
    if (winData.__lastSessionWindowID)
      aWindow.__SS_lastSessionWindowID = winData.__lastSessionWindowID;

    if (overwriteTabs) {
      this.restoreWindowFeatures(aWindow, winData);
      delete this._windows[aWindow.__SSi].extData;
    }

    // Restore cookies from legacy sessions, i.e. before bug 912717.
    SessionCookies.restore(winData.cookies || []);

    if (winData.extData) {
      if (!this._windows[aWindow.__SSi].extData) {
        this._windows[aWindow.__SSi].extData = {};
      }
      for (var key in winData.extData) {
        this._windows[aWindow.__SSi].extData[key] = winData.extData[key];
      }
    }

    let newClosedTabsData = winData._closedTabs || [];

    if (overwriteTabs || firstWindow) {
      // Overwrite existing closed tabs data when overwriteTabs=true
      // or we're the first window to be restored.
      this._windows[aWindow.__SSi]._closedTabs = newClosedTabsData;
    } else if (this._max_tabs_undo > 0) {
      // If we merge tabs, we also want to merge closed tabs data. We'll assume
      // the restored tabs were closed more recently and append the current list
      // of closed tabs to the new one...
      newClosedTabsData =
        newClosedTabsData.concat(this._windows[aWindow.__SSi]._closedTabs);

      // ... and make sure that we don't exceed the max number of closed tabs
      // we can restore.
      this._windows[aWindow.__SSi]._closedTabs =
        newClosedTabsData.slice(0, this._max_tabs_undo);
    }

    // Restore tabs, if any.
    if (winData.tabs.length) {
      this.restoreTabs(aWindow, tabs, winData.tabs, selectTab);
    }

    // set smoothScroll back to the original value
    arrowScrollbox.smoothScroll = smoothScroll;

    TelemetryStopwatch.finish("FX_SESSION_RESTORE_RESTORE_WINDOW_MS");
    if (Services.prefs.getIntPref("browser.tabs.restorebutton") != 0 ) {
      Services.telemetry.scalarAdd("browser.session.restore.number_of_tabs", winData.tabs.length);
      Services.telemetry.scalarAdd("browser.session.restore.number_of_win", 1);
    }

    this._setWindowStateReady(aWindow);

    this._sendWindowRestoredNotification(aWindow);

    Services.obs.notifyObservers(aWindow, NOTIFY_SINGLE_WINDOW_RESTORED);

    this._sendRestoreCompletedNotifications();
  },

  /**
   * Prepare connection to host beforehand.
   *
   * @param url
   *        URL of a host.
   * @returns a flag indicates whether a connection has been made
   */
  prepareConnectionToHost(url) {
    if (!url.startsWith("about:")) {
      let sc = Services.io.QueryInterface(Ci.nsISpeculativeConnect);
      let uri = Services.io.newURI(url);
      sc.speculativeConnect(uri, null, null);
      return true;
    }
    return false;
  },

  /**
   * Make a connection to a host when users hover mouse on a tab.
   * This will also set a flag in the tab to prevent us from speculatively
   * connecting a second time.
   *
   * @param tab
   *        a tab to speculatively connect on mouse hover.
   */
  speculativeConnectOnTabHover(tab) {
    if (tab.__SS_lazyData && !tab.__SS_connectionPrepared) {
      let url = this.getLazyTabValue(tab, "url");
      let prepared = this.prepareConnectionToHost(url);
      // This is used to test if a connection has been made beforehand.
      if (gDebuggingEnabled) {
        tab.__test_connection_prepared = prepared;
        tab.__test_connection_url = url;
      }
      // A flag indicate that we've prepared a connection for this tab and
      // if is called again, we shouldn't prepare another connection.
      tab.__SS_connectionPrepared = true;
    }
  },

  /**
   * Restore multiple windows using the provided state.
   * @param aWindow
   *        Window reference to the first window to use for restoration.
   *        Additionally required windows will be opened.
   * @param aState
   *        JS object or JSON string
   * @param aOptions
   *        {overwriteTabs: true} to overwrite existing tabs w/ new ones
   *        {isFollowUp: true} if this is not the restoration of the 1st window
   *        {firstWindow: true} if this is the first non-private window we're
   *                            restoring in this session, that might open an
   *                            external link as well
   */
  restoreWindows: function ssi_restoreWindows(aWindow, aState, aOptions = {}) {
    let isFollowUp = aOptions && aOptions.isFollowUp;

    if (isFollowUp) {
      this.windowToFocus = aWindow;
    }

    // initialize window if necessary
    if (aWindow && (!aWindow.__SSi || !this._windows[aWindow.__SSi]))
      this.onLoad(aWindow);

    let root;
    try {
      root = (typeof aState == "string") ? JSON.parse(aState) : aState;
    } catch (ex) { // invalid state object - don't restore anything
      debug(ex);
      this._sendRestoreCompletedNotifications();
      return;
    }

    // Restore closed windows if any.
    if (root._closedWindows) {
      this._closedWindows = root._closedWindows;
      this._closedObjectsChanged = true;
    }

    // We're done here if there are no windows.
    if (!root.windows || !root.windows.length) {
      this._sendRestoreCompletedNotifications();
      return;
    }

    if (!root.selectedWindow || root.selectedWindow > root.windows.length) {
      root.selectedWindow = 0;
    }

    // open new windows for all further window entries of a multi-window session
    // (unless they don't contain any tab data)
    let winData;
    for (var w = 1; w < root.windows.length; w++) {
      winData = root.windows[w];
      if (winData && winData.tabs && winData.tabs[0]) {
        var window = this._openWindowWithState({ windows: [winData] });
        if (w == root.selectedWindow - 1) {
          this.windowToFocus = window;
        }
      }
    }

    this.restoreWindow(aWindow, root.windows[0], aOptions);

    DevToolsShim.restoreDevToolsSession(aState);
  },

  /**
   * Manage history restoration for a window
   * @param aWindow
   *        Window to restore the tabs into
   * @param aTabs
   *        Array of tab references
   * @param aTabData
   *        Array of tab data
   * @param aSelectTab
   *        Index of the tab to select. This is a 1-based index where "1"
   *        indicates the first tab should be selected, and "0" indicates that
   *        the currently selected tab will not be changed.
   */
  restoreTabs(aWindow, aTabs, aTabData, aSelectTab) {
    var tabbrowser = aWindow.gBrowser;

    if (!this._isWindowLoaded(aWindow)) {
      // from now on, the data will come from the actual window
      delete this._statesToRestore[aWindow.__SS_restoreID];
      delete aWindow.__SS_restoreID;
      delete this._windows[aWindow.__SSi]._restoring;
    }

    let numTabsToRestore = aTabs.length;
    let numTabsInWindow = tabbrowser.tabs.length;
    let tabsDataArray = this._windows[aWindow.__SSi].tabs;

    // Update the window state in case we shut down without being notified.
    // Individual tab states will be taken care of by restoreTab() below.
    if (numTabsInWindow == numTabsToRestore) {
      // Remove all previous tab data.
      tabsDataArray.length = 0;
    } else {
      // Remove all previous tab data except tabs that should not be overriden.
      tabsDataArray.splice(numTabsInWindow - numTabsToRestore);
    }

    // Let the tab data array have the right number of slots.
    tabsDataArray.length = numTabsInWindow;

    if (aSelectTab > 0 && aSelectTab <= aTabs.length) {
      // Update the window state in case we shut down without being notified.
      this._windows[aWindow.__SSi].selected = aSelectTab;
    }

    // If we restore the selected tab, make sure it goes first.
    let selectedIndex = aTabs.indexOf(tabbrowser.selectedTab);
    if (selectedIndex > -1) {
      this.restoreTab(tabbrowser.selectedTab, aTabData[selectedIndex]);
    }

    // Restore all tabs.
    for (let t = 0; t < aTabs.length; t++) {
      if (t != selectedIndex) {
        this.restoreTab(aTabs[t], aTabData[t]);
      }
    }
  },

  // Restores the given tab state for a given tab.
  restoreTab(tab, tabData, options = {}) {
    let browser = tab.linkedBrowser;

    if (browser.__SS_restoreState) {
      Cu.reportError("Must reset tab before calling restoreTab.");
      return;
    }

    let loadArguments = options.loadArguments;
    let window = tab.ownerGlobal;
    let tabbrowser = window.gBrowser;
    let forceOnDemand = options.forceOnDemand;

    let willRestoreImmediately = options.restoreImmediately ||
                                 tabbrowser.selectedBrowser == browser;

    let isBrowserInserted = browser.isConnected;

    // Increase the busy state counter before modifying the tab.
    this._setWindowStateBusy(window);

    // It's important to set the window state to dirty so that
    // we collect their data for the first time when saving state.
    DirtyWindows.add(window);

    // In case we didn't collect/receive data for any tabs yet we'll have to
    // fill the array with at least empty tabData objects until |_tPos| or
    // we'll end up with |null| entries.
    for (let otherTab of Array.slice(tabbrowser.tabs, 0, tab._tPos)) {
      let emptyState = {entries: [], lastAccessed: otherTab.lastAccessed};
      this._windows[window.__SSi].tabs.push(emptyState);
    }

    // Update the tab state in case we shut down without being notified.
    this._windows[window.__SSi].tabs[tab._tPos] = tabData;

    // Prepare the tab so that it can be properly restored. We'll pin/unpin
    // and show/hide tabs as necessary. We'll also attach a copy of the tab's
    // data in case we close it before it's been restored.
    if (tabData.pinned) {
      tabbrowser.pinTab(tab);
    } else {
      tabbrowser.unpinTab(tab);
    }

    if (tabData.hidden) {
      tabbrowser.hideTab(tab);
    } else {
      tabbrowser.showTab(tab);
    }

    if (!!tabData.muted != browser.audioMuted) {
      tab.toggleMuteAudio(tabData.muteReason);
    }

    if (!tabData.mediaBlocked) {
      browser.resumeMedia();
    }

    if (tabData.lastAccessed) {
      tab.updateLastAccessed(tabData.lastAccessed);
    }

    if ("attributes" in tabData) {
      // Ensure that we persist tab attributes restored from previous sessions.
      Object.keys(tabData.attributes).forEach(a => TabAttributes.persist(a));
    }

    if (!tabData.entries) {
      tabData.entries = [];
    }
    if (tabData.extData) {
      tab.__SS_extdata = Cu.cloneInto(tabData.extData, {});
    } else {
      delete tab.__SS_extdata;
    }

    // Tab is now open.
    delete tabData.closedAt;

    // Ensure the index is in bounds.
    let activeIndex = (tabData.index || tabData.entries.length) - 1;
    activeIndex = Math.min(activeIndex, tabData.entries.length - 1);
    activeIndex = Math.max(activeIndex, 0);

    // Save the index in case we updated it above.
    tabData.index = activeIndex + 1;

    tab.setAttribute("pending", "true");

    // If we're restoring this tab, it certainly shouldn't be in
    // the ignored set anymore.
    this._crashedBrowsers.delete(browser.permanentKey);

    // Update the persistent tab state cache with |tabData| information.
    TabStateCache.update(browser, {
      // NOTE: Copy the entries array shallowly, so as to not screw with the
      // original tabData's history when getting history updates.
      history: {entries: [...tabData.entries], index: tabData.index},
      scroll: tabData.scroll || null,
      storage: tabData.storage || null,
      formdata: tabData.formdata || null,
      disallow: tabData.disallow || null,
      userContextId: tabData.userContextId || 0,

      // This information is only needed until the tab has finished restoring.
      // When that's done it will be removed from the cache and we always
      // collect it in TabState._collectBaseTabData().
      image: tabData.image || "",
      iconLoadingPrincipal: tabData.iconLoadingPrincipal || null,
      userTypedValue: tabData.userTypedValue || "",
      userTypedClear: tabData.userTypedClear || 0
    });

    // Restore tab attributes.
    if ("attributes" in tabData) {
      TabAttributes.set(tab, tabData.attributes);
    }

    if (isBrowserInserted) {
      // Start a new epoch to discard all frame script messages relating to a
      // previous epoch. All async messages that are still on their way to chrome
      // will be ignored and don't override any tab data set when restoring.
      let epoch = this.startNextEpoch(browser);

      // Ensure that the tab will get properly restored in the event the tab
      // crashes while restoring.  But don't set this on lazy browsers as
      // restoreTab will get called again when the browser is instantiated.
      browser.__SS_restoreState = TAB_STATE_NEEDS_RESTORE;

      this._sendRestoreHistory(browser, {tabData, epoch, loadArguments});

      // This could cause us to ignore MAX_CONCURRENT_TAB_RESTORES a bit, but
      // it ensures each window will have its selected tab loaded.
      if (willRestoreImmediately) {
        this.restoreTabContent(tab, options);
      } else if (!forceOnDemand) {
        TabRestoreQueue.add(tab);
        // Check if a tab is in queue and will be restored
        // after the currently loading tabs. If so, prepare
        // a connection to host to speed up page loading.
        if (TabRestoreQueue.willRestoreSoon(tab)) {
          if (activeIndex in tabData.entries) {
            let url = tabData.entries[activeIndex].url;
            let prepared = this.prepareConnectionToHost(url);
            if (gDebuggingEnabled) {
              tab.__test_connection_prepared = prepared;
              tab.__test_connection_url = url;
            }
          }
        }
        this.restoreNextTab();
      }
    } else {
      // __SS_lazyData holds data for lazy-browser tabs to proxy for
      // data unobtainable from the unbound browser.  This only applies to lazy
      // browsers and will be removed once the browser is inserted in the document.
      // This must preceed `updateTabLabelAndIcon` call for required data to be present.
      let url = "about:blank";
      let title = "";

      if (activeIndex in tabData.entries) {
        url = tabData.entries[activeIndex].url;
        title = tabData.entries[activeIndex].title || url;
      }
      tab.__SS_lazyData = {
        url,
        title,
        userTypedValue: tabData.userTypedValue || "",
        userTypedClear: tabData.userTypedClear || 0
      };
    }

    if (tab.hasAttribute("customizemode")) {
      window.gCustomizeMode.setTab(tab);
    }

    // Update tab label and icon to show something
    // while we wait for the messages to be processed.
    this.updateTabLabelAndIcon(tab, tabData);

    // Decrease the busy state counter after we're done.
    this._setWindowStateReady(window);
  },

  /**
   * Kicks off restoring the given tab.
   *
   * @param aTab
   *        the tab to restore
   * @param aOptions
   *        optional arguments used when performing process switch during load
   */
  restoreTabContent(aTab, aOptions = {}) {
    let loadArguments = aOptions.loadArguments;
    if (aTab.hasAttribute("customizemode") && !loadArguments) {
      return;
    }

    let browser = aTab.linkedBrowser;
    let window = aTab.ownerGlobal;
    let tabbrowser = window.gBrowser;
    let tabData = TabState.clone(aTab);
    let activeIndex = tabData.index - 1;
    let activePageData = tabData.entries[activeIndex] || null;
    let uri = activePageData ? activePageData.url || null : null;
    if (loadArguments) {
      uri = loadArguments.uri;
      if (loadArguments.userContextId) {
        browser.setAttribute("usercontextid", loadArguments.userContextId);
      }
    }

    this.markTabAsRestoring(aTab);

    let newFrameloader = aOptions.newFrameloader;

    let isRemotenessUpdate;
    if (aOptions.remoteType !== undefined) {
      // We already have a selected remote type so we update to that.
      isRemotenessUpdate =
        tabbrowser.updateBrowserRemoteness(browser, !!aOptions.remoteType,
                                           { remoteType: aOptions.remoteType,
                                             newFrameloader });
    } else {
      isRemotenessUpdate =
        tabbrowser.updateBrowserRemotenessByURL(browser, uri, {
          newFrameloader,
        });
    }

    if (isRemotenessUpdate) {
      // We updated the remoteness, so we need to send the history down again.
      //
      // Start a new epoch to discard all frame script messages relating to a
      // previous epoch. All async messages that are still on their way to chrome
      // will be ignored and don't override any tab data set when restoring.
      let epoch = this.startNextEpoch(browser);

      this._sendRestoreHistory(browser, {
        tabData,
        epoch,
        loadArguments,
        isRemotenessUpdate,
      });
    }

    // If the restored browser wants to show view source content, start up a
    // view source browser that will load the required frame script.
    if (uri && ViewSourceBrowser.isViewSource(uri)) {
      new ViewSourceBrowser(browser);
    }

    browser.messageManager.sendAsyncMessage("SessionStore:restoreTabContent",
      {loadArguments, isRemotenessUpdate,
       reason: aOptions.restoreContentReason ||
               RESTORE_TAB_CONTENT_REASON.SET_STATE,
       requestTime: Services.telemetry.msSystemNow()});
  },

  /**
   * Marks a given pending tab as restoring.
   *
   * @param aTab
   *        the pending tab to mark as restoring
   */
  markTabAsRestoring(aTab) {
    let browser = aTab.linkedBrowser;
    if (browser.__SS_restoreState != TAB_STATE_NEEDS_RESTORE) {
      throw new Error("Given tab is not pending.");
    }

    // Make sure that this tab is removed from the priority queue.
    TabRestoreQueue.remove(aTab);

    // Increase our internal count.
    this._tabsRestoringCount++;

    // Set this tab's state to restoring
    browser.__SS_restoreState = TAB_STATE_RESTORING;
    aTab.removeAttribute("pending");
  },

  /**
   * This _attempts_ to restore the next available tab. If the restore fails,
   * then we will attempt the next one.
   * There are conditions where this won't do anything:
   *   if we're in the process of quitting
   *   if there are no tabs to restore
   *   if we have already reached the limit for number of tabs to restore
   */
  restoreNextTab: function ssi_restoreNextTab() {
    // If we call in here while quitting, we don't actually want to do anything
    if (RunState.isQuitting)
      return;

    // Don't exceed the maximum number of concurrent tab restores.
    if (this._tabsRestoringCount >= MAX_CONCURRENT_TAB_RESTORES)
      return;

    let tab = TabRestoreQueue.shift();
    if (tab) {
      this.restoreTabContent(tab);
    }
  },

  /**
   * Restore visibility and dimension features to a window
   * @param aWindow
   *        Window reference
   * @param aWinData
   *        Object containing session data for the window
   */
  restoreWindowFeatures: function ssi_restoreWindowFeatures(aWindow, aWinData) {
    var hidden = (aWinData.hidden) ? aWinData.hidden.split(",") : [];
    WINDOW_HIDEABLE_FEATURES.forEach(function(aItem) {
      aWindow[aItem].visible = !hidden.includes(aItem);
    });

    if (aWinData.isPopup) {
      this._windows[aWindow.__SSi].isPopup = true;
      if (aWindow.gURLBar) {
        aWindow.gURLBar.readOnly = true;
      }
    } else {
      delete this._windows[aWindow.__SSi].isPopup;
      if (aWindow.gURLBar) {
        aWindow.gURLBar.readOnly = false;
      }
    }

    aWindow.setTimeout(() => {
      this.restoreDimensions(aWindow,
        +(aWinData.width || 0),
        +(aWinData.height || 0),
        "screenX" in aWinData ? +aWinData.screenX : NaN,
        "screenY" in aWinData ? +aWinData.screenY : NaN,
        aWinData.sizemode || "", aWinData.sidebar || "");
    }, 0);
  },

  /**
   * Restore a window's dimensions
   * @param aWidth
   *        Window width
   * @param aHeight
   *        Window height
   * @param aLeft
   *        Window left
   * @param aTop
   *        Window top
   * @param aSizeMode
   *        Window size mode (eg: maximized)
   * @param aSidebar
   *        Sidebar command
   */
  restoreDimensions: function ssi_restoreDimensions(aWindow, aWidth, aHeight, aLeft, aTop, aSizeMode, aSidebar) {
    var win = aWindow;
    var _this = this;
    function win_(aName) { return _this._getWindowDimension(win, aName); }

    const dwu = win.QueryInterface(Ci.nsIInterfaceRequestor)
                   .getInterface(Ci.nsIDOMWindowUtils);
    // find available space on the screen where this window is being placed
    let screen = gScreenManager.screenForRect(aLeft, aTop, aWidth, aHeight);
    if (screen) {
      let screenLeft = {}, screenTop = {}, screenWidth = {}, screenHeight = {};
      screen.GetAvailRectDisplayPix(screenLeft, screenTop, screenWidth, screenHeight);
      // screenX/Y are based on the origin of the screen's desktop-pixel coordinate space
      let screenLeftCss = screenLeft.value;
      let screenTopCss = screenTop.value;
      // convert screen's device pixel dimensions to CSS px dimensions
      screen.GetAvailRect(screenLeft, screenTop, screenWidth, screenHeight);
      let cssToDevScale = screen.defaultCSSScaleFactor;
      let screenRightCss = screenLeftCss + screenWidth.value / cssToDevScale;
      let screenBottomCss = screenTopCss + screenHeight.value / cssToDevScale;

      // Pull the window within the screen's bounds (allowing a little slop
      // for windows that may be deliberately placed with their border off-screen
      // as when Win10 "snaps" a window to the left/right edge -- bug 1276516).
      // First, ensure the left edge is large enough...
      if (aLeft < screenLeftCss - SCREEN_EDGE_SLOP) {
        aLeft = screenLeftCss;
      }
      // Then check the resulting right edge, and reduce it if necessary.
      let right = aLeft + aWidth;
      if (right > screenRightCss + SCREEN_EDGE_SLOP) {
        right = screenRightCss;
        // See if we can move the left edge leftwards to maintain width.
        if (aLeft > screenLeftCss) {
          aLeft = Math.max(right - aWidth, screenLeftCss);
        }
      }
      // Finally, update aWidth to account for the adjusted left and right edges.
      aWidth = right - aLeft;

      // And do the same in the vertical dimension.
      if (aTop < screenTopCss - SCREEN_EDGE_SLOP) {
        aTop = screenTopCss;
      }
      let bottom = aTop + aHeight;
      if (bottom > screenBottomCss + SCREEN_EDGE_SLOP) {
        bottom = screenBottomCss;
        if (aTop > screenTopCss) {
          aTop = Math.max(bottom - aHeight, screenTopCss);
        }
      }
      aHeight = bottom - aTop;
    }

    // Suppress animations.
    dwu.suppressAnimation(true);

    // We want to make sure users will get their animations back in case an exception is thrown.
    try {
      // only modify those aspects which aren't correct yet
      if (!isNaN(aLeft) && !isNaN(aTop) && (aLeft != win_("screenX") || aTop != win_("screenY"))) {
        aWindow.moveTo(aLeft, aTop);
      }
      if (aWidth && aHeight && (aWidth != win_("width") || aHeight != win_("height")) && !gResistFingerprintingEnabled) {
        // Don't resize the window if it's currently maximized and we would
        // maximize it again shortly after.
        if (aSizeMode != "maximized" || win_("sizemode") != "maximized") {
          aWindow.resizeTo(aWidth, aHeight);
        }
      }
      if (aSizeMode && win_("sizemode") != aSizeMode && !gResistFingerprintingEnabled) {
        switch (aSizeMode) {
        case "maximized":
          aWindow.maximize();
          break;
        case "minimized":
          aWindow.minimize();
          break;
        case "normal":
          aWindow.restore();
          break;
        }
      }
      let sidebarBox = aWindow.document.getElementById("sidebar-box");
      if (aSidebar && (sidebarBox.getAttribute("sidebarcommand") != aSidebar ||
                       !sidebarBox.getAttribute("checked"))) {
        aWindow.SidebarUI.showInitially(aSidebar);
      }
      // since resizing/moving a window brings it to the foreground,
      // we might want to re-focus the last focused window
      if (this.windowToFocus) {
        this.windowToFocus.focus();
      }
    } finally {
      // Enable animations.
      dwu.suppressAnimation(false);
    }
  },

  /* ........ Disk Access .............. */

  /**
   * Save the current session state to disk, after a delay.
   *
   * @param aWindow (optional)
   *        Will mark the given window as dirty so that we will recollect its
   *        data before we start writing.
   */
  saveStateDelayed(aWindow = null) {
    if (aWindow) {
      DirtyWindows.add(aWindow);
    }

    SessionSaver.runDelayed();
  },

  /* ........ Auxiliary Functions .............. */

  /**
   * Remove a closed window from the list of closed windows and indicate that
   * the change should be notified.
   *
   * @param index
   *        The index of the window in this._closedWindows.
   *
   * @returns Array of closed windows.
   */
  _removeClosedWindow(index) {
    let windows = this._closedWindows.splice(index, 1);
    this._closedObjectsChanged = true;
    return windows;
  },

  /**
   * Notifies observers that the list of closed tabs and/or windows has changed.
   * Waits a tick to allow SessionStorage a chance to register the change.
   */
  _notifyOfClosedObjectsChange() {
    if (!this._closedObjectsChanged) {
      return;
    }
    this._closedObjectsChanged = false;
    setTimeout(() => {
      Services.obs.notifyObservers(null, NOTIFY_CLOSED_OBJECTS_CHANGED);
    }, 0);
  },

  /**
   * Update the session start time and send a telemetry measurement
   * for the number of days elapsed since the session was started.
   *
   * @param state
   *        The session state.
   */
  _updateSessionStartTime: function ssi_updateSessionStartTime(state) {
    // Attempt to load the session start time from the session state
    if (state.session && state.session.startTime) {
      this._sessionStartTime = state.session.startTime;
    }
  },

  /**
   * call a callback for all currently opened browser windows
   * (might miss the most recent one)
   * @param aFunc
   *        Callback each window is passed to
   */
  _forEachBrowserWindow: function ssi_forEachBrowserWindow(aFunc) {
    var windowsEnum = Services.wm.getEnumerator("navigator:browser");

    while (windowsEnum.hasMoreElements()) {
      var window = windowsEnum.getNext();
      if (window.__SSi && !window.closed) {
        aFunc.call(this, window);
      }
    }
  },

  /**
   * Returns most recent window
   * @returns Window reference
   */
  _getMostRecentBrowserWindow: function ssi_getMostRecentBrowserWindow() {
    return RecentWindow.getMostRecentBrowserWindow({ allowPopups: true });
  },

  /**
   * Calls onClose for windows that are determined to be closed but aren't
   * destroyed yet, which would otherwise cause getBrowserState and
   * setBrowserState to treat them as open windows.
   */
  _handleClosedWindows: function ssi_handleClosedWindows() {
    var windowsEnum = Services.wm.getEnumerator("navigator:browser");

    let promises = [];
    while (windowsEnum.hasMoreElements()) {
      var window = windowsEnum.getNext();
      if (window.closed) {
        promises.push(this.onClose(window));
      }
    }
    return Promise.all(promises);
  },

  /**
   * open a new browser window for a given session state
   * called when restoring a multi-window session
   * @param aState
   *        Object containing session data
   */
  _openWindowWithState: function ssi_openWindowWithState(aState) {
    var argString = Cc["@mozilla.org/supports-string;1"].
                    createInstance(Ci.nsISupportsString);
    argString.data = "";

    // Build feature string
    let features = "chrome,dialog=no,suppressanimation,all";
    let winState = aState.windows[0];
    WINDOW_ATTRIBUTES.forEach(function(aFeature) {
      // Use !isNaN as an easy way to ignore sizemode and check for numbers
      if (aFeature in winState && !isNaN(winState[aFeature]))
        features += "," + aFeature + "=" + winState[aFeature];
    });

    if (winState.isPrivate) {
      features += ",private";
    }

    var window =
      Services.ww.openWindow(null, this._prefBranch.getCharPref("chromeURL"),
                             "_blank", features, argString);

    do {
      var ID = "window" + Math.random();
    } while (ID in this._statesToRestore);
    this._statesToRestore[(window.__SS_restoreID = ID)] = aState;

    return window;
  },

  /**
   * Whether or not to resume session, if not recovering from a crash.
   * @returns bool
   */
  _doResumeSession: function ssi_doResumeSession() {
    return this._prefBranch.getIntPref("startup.page") == 3 ||
           this._prefBranch.getBoolPref("sessionstore.resume_session_once");
  },

  /**
   * whether the user wants to load any other page at startup
   * (except the homepage) - needed for determining whether to overwrite the current tabs
   * C.f.: nsBrowserContentHandler's defaultArgs implementation.
   * @returns bool
   */
  _isCmdLineEmpty: function ssi_isCmdLineEmpty(aWindow, aState) {
    var pinnedOnly = aState.windows &&
                     aState.windows.every(win =>
                       win.tabs.every(tab => tab.pinned));

    let hasFirstArgument = aWindow.arguments && aWindow.arguments[0];
    if (!pinnedOnly) {
      let defaultArgs = Cc["@mozilla.org/browser/clh;1"].
                        getService(Ci.nsIBrowserHandler).defaultArgs;
      if (aWindow.arguments &&
          aWindow.arguments[0] &&
          aWindow.arguments[0] == defaultArgs)
        hasFirstArgument = false;
    }

    return !hasFirstArgument;
  },

  /**
   * on popup windows, the XULWindow's attributes seem not to be set correctly
   * we use thus JSDOMWindow attributes for sizemode and normal window attributes
   * (and hope for reasonable values when maximized/minimized - since then
   * outerWidth/outerHeight aren't the dimensions of the restored window)
   * @param aWindow
   *        Window reference
   * @param aAttribute
   *        String sizemode | width | height | other window attribute
   * @returns string
   */
  _getWindowDimension: function ssi_getWindowDimension(aWindow, aAttribute) {
    if (aAttribute == "sizemode") {
      switch (aWindow.windowState) {
      case aWindow.STATE_FULLSCREEN:
      case aWindow.STATE_MAXIMIZED:
        return "maximized";
      case aWindow.STATE_MINIMIZED:
        return "minimized";
      default:
        return "normal";
      }
    }

    var dimension;
    switch (aAttribute) {
    case "width":
      dimension = aWindow.outerWidth;
      break;
    case "height":
      dimension = aWindow.outerHeight;
      break;
    default:
      dimension = aAttribute in aWindow ? aWindow[aAttribute] : "";
      break;
    }

    if (aWindow.windowState == aWindow.STATE_NORMAL) {
      return dimension;
    }
    return aWindow.document.documentElement.getAttribute(aAttribute) || dimension;
  },

  /**
   * @param aState is a session state
   * @param aRecentCrashes is the number of consecutive crashes
   * @returns whether a restore page will be needed for the session state
   */
  _needsRestorePage: function ssi_needsRestorePage(aState, aRecentCrashes) {
    const SIX_HOURS_IN_MS = 6 * 60 * 60 * 1000;

    // don't display the page when there's nothing to restore
    let winData = aState.windows || null;
    if (!winData || winData.length == 0)
      return false;

    // don't wrap a single about:sessionrestore page
    if (this._hasSingleTabWithURL(winData, "about:sessionrestore") ||
        this._hasSingleTabWithURL(winData, "about:welcomeback")) {
      return false;
    }

    // don't automatically restore in Safe Mode
    if (Services.appinfo.inSafeMode)
      return true;

    let max_resumed_crashes =
      this._prefBranch.getIntPref("sessionstore.max_resumed_crashes");
    let sessionAge = aState.session && aState.session.lastUpdate &&
                     (Date.now() - aState.session.lastUpdate);

    return max_resumed_crashes != -1 &&
           (aRecentCrashes > max_resumed_crashes ||
            sessionAge && sessionAge >= SIX_HOURS_IN_MS);
  },

  /**
   * @param aWinData is the set of windows in session state
   * @param aURL is the single URL we're looking for
   * @returns whether the window data contains only the single URL passed
   */
  _hasSingleTabWithURL(aWinData, aURL) {
    if (aWinData &&
        aWinData.length == 1 &&
        aWinData[0].tabs &&
        aWinData[0].tabs.length == 1 &&
        aWinData[0].tabs[0].entries &&
        aWinData[0].tabs[0].entries.length == 1) {
      return aURL == aWinData[0].tabs[0].entries[0].url;
    }
    return false;
  },

  /**
   * Determine if the tab state we're passed is something we should save. This
   * is used when closing a tab or closing a window with a single tab
   *
   * @param aTabState
   *        The current tab state
   * @returns boolean
   */
  _shouldSaveTabState: function ssi_shouldSaveTabState(aTabState) {
    // If the tab has only a transient about: history entry, no other
    // session history, and no userTypedValue, then we don't actually want to
    // store this tab's data.
    return aTabState.entries.length &&
           !(aTabState.entries.length == 1 &&
                (aTabState.entries[0].url == "about:blank" ||
                 aTabState.entries[0].url == "about:newtab" ||
                 aTabState.entries[0].url == "about:printpreview" ||
                 aTabState.entries[0].url == "about:privatebrowsing") &&
                 !aTabState.userTypedValue);
  },

  /**
   * Determine if the tab state we're passed is something we should keep to be
   * reopened at session restore. This is used when we are saving the current
   * session state to disk. This method is very similar to _shouldSaveTabState,
   * however, "about:blank" and "about:newtab" tabs will still be saved to disk.
   *
   * @param aTabState
   *        The current tab state
   * @returns boolean
   */
  _shouldSaveTab: function ssi_shouldSaveTab(aTabState) {
    // If the tab has one of the following transient about: history entry, no
    // userTypedValue, and no customizemode attribute, then we don't actually
    // want to write this tab's data to disk.
    return aTabState.userTypedValue ||
           (aTabState.attributes &&
            aTabState.attributes.customizemode == "true") ||
           (aTabState.entries.length &&
            !(aTabState.entries[0].url == "about:printpreview" ||
              aTabState.entries[0].url == "about:privatebrowsing"));
  },

  /**
   * This is going to take a state as provided at startup (via
   * nsISessionStartup.state) and split it into 2 parts. The first part
   * (defaultState) will be a state that should still be restored at startup,
   * while the second part (state) is a state that should be saved for later.
   * defaultState will be comprised of windows with only pinned tabs, extracted
   * from state. It will also contain window position information.
   *
   * defaultState will be restored at startup. state will be passed into
   * LastSession and will be kept in case the user explicitly wants
   * to restore the previous session (publicly exposed as restoreLastSession).
   *
   * @param state
   *        The state, presumably from nsISessionStartup.state
   * @returns [defaultState, state]
   */
  _prepDataForDeferredRestore: function ssi_prepDataForDeferredRestore(state) {
    // Make sure that we don't modify the global state as provided by
    // nsSessionStartup.state.
    state = Cu.cloneInto(state, {});

    let defaultState = { windows: [], selectedWindow: 1 };

    state.selectedWindow = state.selectedWindow || 1;

    // Look at each window, remove pinned tabs, adjust selectedindex,
    // remove window if necessary.
    for (let wIndex = 0; wIndex < state.windows.length;) {
      let window = state.windows[wIndex];
      window.selected = window.selected || 1;
      // We're going to put the state of the window into this object
      let pinnedWindowState = { tabs: [] };
      for (let tIndex = 0; tIndex < window.tabs.length;) {
        if (window.tabs[tIndex].pinned) {
          // Adjust window.selected
          if (tIndex + 1 < window.selected)
            window.selected -= 1;
          else if (tIndex + 1 == window.selected)
            pinnedWindowState.selected = pinnedWindowState.tabs.length + 1;
            // + 1 because the tab isn't actually in the array yet

          // Now add the pinned tab to our window
          pinnedWindowState.tabs =
            pinnedWindowState.tabs.concat(window.tabs.splice(tIndex, 1));
          // We don't want to increment tIndex here.
          continue;
        }
        tIndex++;
      }

      // At this point the window in the state object has been modified (or not)
      // We want to build the rest of this new window object if we have pinnedTabs.
      if (pinnedWindowState.tabs.length) {
        // First get the other attributes off the window
        WINDOW_ATTRIBUTES.forEach(function(attr) {
          if (attr in window) {
            pinnedWindowState[attr] = window[attr];
            delete window[attr];
          }
        });
        // We're just copying position data into the pinned window.
        // Not copying over:
        // - _closedTabs
        // - extData
        // - isPopup
        // - hidden

        // Assign a unique ID to correlate the window to be opened with the
        // remaining data
        window.__lastSessionWindowID = pinnedWindowState.__lastSessionWindowID
                                     = "" + Date.now() + Math.random();

        // Actually add this window to our defaultState
        defaultState.windows.push(pinnedWindowState);
        // Remove the window from the state if it doesn't have any tabs
        if (!window.tabs.length) {
          if (wIndex + 1 <= state.selectedWindow)
            state.selectedWindow -= 1;
          else if (wIndex + 1 == state.selectedWindow)
            defaultState.selectedIndex = defaultState.windows.length + 1;

          state.windows.splice(wIndex, 1);
          // We don't want to increment wIndex here.
          continue;
        }


      }
      wIndex++;
    }

    return [defaultState, state];
  },

  _sendRestoreCompletedNotifications: function ssi_sendRestoreCompletedNotifications() {
    // not all windows restored, yet
    if (this._restoreCount > 1) {
      this._restoreCount--;
      return;
    }

    // observers were already notified
    if (this._restoreCount == -1)
      return;

    // This was the last window restored at startup, notify observers.
    if (!this._browserSetState) {
      Services.obs.notifyObservers(null, NOTIFY_WINDOWS_RESTORED);
      this._deferredAllWindowsRestored.resolve();
    } else {
      // _browserSetState is used only by tests, and it uses an alternate
      // notification in order not to retrigger startup observers that
      // are listening for NOTIFY_WINDOWS_RESTORED.
      Services.obs.notifyObservers(null, NOTIFY_BROWSER_STATE_RESTORED);
    }

    this._browserSetState = false;
    this._restoreCount = -1;
  },

   /**
   * Set the given window's busy state
   * @param aWindow the window
   * @param aValue the window's busy state
   */
  _setWindowStateBusyValue:
    function ssi_changeWindowStateBusyValue(aWindow, aValue) {

    this._windows[aWindow.__SSi].busy = aValue;

    // Keep the to-be-restored state in sync because that is returned by
    // getWindowState() as long as the window isn't loaded, yet.
    if (!this._isWindowLoaded(aWindow)) {
      let stateToRestore = this._statesToRestore[aWindow.__SS_restoreID].windows[0];
      stateToRestore.busy = aValue;
    }
  },

  /**
   * Set the given window's state to 'not busy'.
   * @param aWindow the window
   */
  _setWindowStateReady: function ssi_setWindowStateReady(aWindow) {
    let newCount = (this._windowBusyStates.get(aWindow) || 0) - 1;
    if (newCount < 0) {
      throw new Error("Invalid window busy state (less than zero).");
    }
    this._windowBusyStates.set(aWindow, newCount);

    if (newCount == 0) {
      this._setWindowStateBusyValue(aWindow, false);
      this._sendWindowStateEvent(aWindow, "Ready");
    }
  },

  /**
   * Set the given window's state to 'busy'.
   * @param aWindow the window
   */
  _setWindowStateBusy: function ssi_setWindowStateBusy(aWindow) {
    let newCount = (this._windowBusyStates.get(aWindow) || 0) + 1;
    this._windowBusyStates.set(aWindow, newCount);

    if (newCount == 1) {
      this._setWindowStateBusyValue(aWindow, true);
      this._sendWindowStateEvent(aWindow, "Busy");
    }
  },

  /**
   * Dispatch an SSWindowState_____ event for the given window.
   * @param aWindow the window
   * @param aType the type of event, SSWindowState will be prepended to this string
   */
  _sendWindowStateEvent: function ssi_sendWindowStateEvent(aWindow, aType) {
    let event = aWindow.document.createEvent("Events");
    event.initEvent("SSWindowState" + aType, true, false);
    aWindow.dispatchEvent(event);
  },

  /**
   * Dispatch the SSWindowRestored event for the given window.
   * @param aWindow
   *        The window which has been restored
   */
  _sendWindowRestoredNotification(aWindow) {
    let event = aWindow.document.createEvent("Events");
    event.initEvent("SSWindowRestored", true, false);
    aWindow.dispatchEvent(event);
  },

  /**
   * Dispatch the SSTabRestored event for the given tab.
   * @param aTab
   *        The tab which has been restored
   * @param aIsRemotenessUpdate
   *        True if this tab was restored due to flip from running from
   *        out-of-main-process to in-main-process or vice-versa.
   */
  _sendTabRestoredNotification(aTab, aIsRemotenessUpdate) {
    let event = aTab.ownerDocument.createEvent("CustomEvent");
    event.initCustomEvent("SSTabRestored", true, false, {
      isRemotenessUpdate: aIsRemotenessUpdate,
    });
    aTab.dispatchEvent(event);
  },

  /**
   * @param aWindow
   *        Window reference
   * @returns whether this window's data is still cached in _statesToRestore
   *          because it's not fully loaded yet
   */
  _isWindowLoaded: function ssi_isWindowLoaded(aWindow) {
    return !aWindow.__SS_restoreID;
  },

  /**
   * Resize this._closedWindows to the value of the pref, except in the case
   * where we don't have any non-popup windows on Windows and Linux. Then we must
   * resize such that we have at least one non-popup window.
   */
  _capClosedWindows: function ssi_capClosedWindows() {
    if (this._closedWindows.length <= this._max_windows_undo)
      return;
    let spliceTo = this._max_windows_undo;
    if (AppConstants.platform != "macosx") {
      let normalWindowIndex = 0;
      // try to find a non-popup window in this._closedWindows
      while (normalWindowIndex < this._closedWindows.length &&
             !!this._closedWindows[normalWindowIndex].isPopup)
        normalWindowIndex++;
      if (normalWindowIndex >= this._max_windows_undo)
        spliceTo = normalWindowIndex + 1;
    }
    if (spliceTo < this._closedWindows.length) {
      this._closedWindows.splice(spliceTo, this._closedWindows.length);
      this._closedObjectsChanged = true;
    }
  },

  /**
   * Clears the set of windows that are "resurrected" before writing to disk to
   * make closing windows one after the other until shutdown work as expected.
   *
   * This function should only be called when we are sure that there has been
   * a user action that indicates the browser is actively being used and all
   * windows that have been closed before are not part of a series of closing
   * windows.
   */
  _clearRestoringWindows: function ssi_clearRestoringWindows() {
    for (let i = 0; i < this._closedWindows.length; i++) {
      delete this._closedWindows[i]._shouldRestore;
    }
  },

  /**
   * Reset state to prepare for a new session state to be restored.
   */
  _resetRestoringState: function ssi_initRestoringState() {
    TabRestoreQueue.reset();
    this._tabsRestoringCount = 0;
  },

  /**
   * Reset the restoring state for a particular tab. This will be called when
   * removing a tab or when a tab needs to be reset (it's being overwritten).
   *
   * @param aTab
   *        The tab that will be "reset"
   */
  _resetLocalTabRestoringState(aTab) {
    let browser = aTab.linkedBrowser;

    // Keep the tab's previous state for later in this method
    let previousState = browser.__SS_restoreState;

    if (!previousState) {
      Cu.reportError("Given tab is not restoring.");
      return;
    }

    // The browser is no longer in any sort of restoring state.
    delete browser.__SS_restoreState;

    aTab.removeAttribute("pending");

    if (previousState == TAB_STATE_RESTORING) {
      if (this._tabsRestoringCount)
        this._tabsRestoringCount--;
    } else if (previousState == TAB_STATE_NEEDS_RESTORE) {
      // Make sure that the tab is removed from the list of tabs to restore.
      // Again, this is normally done in restoreTabContent, but that isn't being called
      // for this tab.
      TabRestoreQueue.remove(aTab);
    }
  },

  _resetTabRestoringState(tab) {
    let browser = tab.linkedBrowser;

    if (!browser.__SS_restoreState) {
      Cu.reportError("Given tab is not restoring.");
      return;
    }

    browser.messageManager.sendAsyncMessage("SessionStore:resetRestore", {});
    this._resetLocalTabRestoringState(tab);
  },

  /**
   * Each fresh tab starts out with epoch=0. This function can be used to
   * start a next epoch by incrementing the current value. It will enables us
   * to ignore stale messages sent from previous epochs. The function returns
   * the new epoch ID for the given |browser|.
   */
  startNextEpoch(browser) {
    let next = this.getCurrentEpoch(browser) + 1;
    this._browserEpochs.set(browser.permanentKey, next);
    return next;
  },

  /**
   * Manually set the epoch to a given value.
   */
  setCurrentEpoch(aBrowser, aEpoch) {
    this._browserEpochs.set(aBrowser.permanentKey, aEpoch);
    return aEpoch;
  },

  /**
   * Returns the current epoch for the given <browser>. If we haven't assigned
   * a new epoch this will default to zero for new tabs.
   */
  getCurrentEpoch(browser) {
    return this._browserEpochs.get(browser.permanentKey) || 0;
  },

  /**
   * Each time a <browser> element is restored, we increment its "epoch". To
   * check if a message from content-sessionStore.js is out of date, we can
   * compare the epoch received with the message to the <browser> element's
   * epoch. This function does that, and returns true if |epoch| is up-to-date
   * with respect to |browser|.
   */
  isCurrentEpoch(browser, epoch) {
    return this.getCurrentEpoch(browser) == epoch;
  },

  /**
   * Resets the epoch for a given <browser>. We need to this every time we
   * receive a hint that a new docShell has been loaded into the browser as
   * the frame script starts out with epoch=0.
   */
  resetEpoch(browser) {
    this._browserEpochs.delete(browser.permanentKey);
  },

  /**
   * Countdown for a given duration, skipping beats if the computer is too busy,
   * sleeping or otherwise unavailable.
   *
   * @param {number} delay An approximate delay to wait in milliseconds (rounded
   * up to the closest second).
   *
   * @return Promise
   */
  looseTimer(delay) {
    let DELAY_BEAT = 1000;
    let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
    let beats = Math.ceil(delay / DELAY_BEAT);
    let deferred = PromiseUtils.defer();
    timer.initWithCallback(function() {
      if (beats <= 0) {
        deferred.resolve();
      }
      --beats;
    }, DELAY_BEAT, Ci.nsITimer.TYPE_REPEATING_PRECISE_CAN_SKIP);
    // Ensure that the timer is both canceled once we are done with it
    // and not garbage-collected until then.
    deferred.promise.then(() => timer.cancel(), () => timer.cancel());
    return deferred;
  },

  /**
   * Send the "SessionStore:restoreHistory" message to content, triggering a
   * content restore. This method is intended to be used internally by
   * SessionStore, as it also ensures that permissions are avaliable in the
   * content process before triggering the history restore in the content
   * process.
   *
   * @param browser The browser to transmit the permissions for
   * @param options The options data to send to content.
   */
  _sendRestoreHistory(browser, options) {
    // If the tabData which we're sending down has any sessionStorage associated
    // with it, we need to send down permissions for the domains, as this
    // information will be needed to correctly restore the session.
    if (options.tabData.storage) {
      for (let origin of Object.getOwnPropertyNames(options.tabData.storage)) {
        try {
          let {frameLoader} = browser;
          if (frameLoader.tabParent) {
            let attrs = browser.contentPrincipal.originAttributes;
            let dataPrincipal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(origin);
            let principal = Services.scriptSecurityManager.createCodebasePrincipal(dataPrincipal.URI, attrs);
            frameLoader.tabParent.transmitPermissionsForPrincipal(principal);
          }
        } catch (e) {
          console.error(e);
        }
      }
    }

    browser.messageManager.sendAsyncMessage("SessionStore:restoreHistory", options);
  },
};

/**
 * Priority queue that keeps track of a list of tabs to restore and returns
 * the tab we should restore next, based on priority rules. We decide between
 * pinned, visible and hidden tabs in that and FIFO order. Hidden tabs are only
 * restored with restore_hidden_tabs=true.
 */
var TabRestoreQueue = {
  // The separate buckets used to store tabs.
  tabs: {priority: [], visible: [], hidden: []},

  // Preferences used by the TabRestoreQueue to determine which tabs
  // are restored automatically and which tabs will be on-demand.
  prefs: {
    // Lazy getter that returns whether tabs are restored on demand.
    get restoreOnDemand() {
      let updateValue = () => {
        let value = Services.prefs.getBoolPref(PREF);
        let definition = {value, configurable: true};
        Object.defineProperty(this, "restoreOnDemand", definition);
        return value;
      };

      const PREF = "browser.sessionstore.restore_on_demand";
      Services.prefs.addObserver(PREF, updateValue);
      return updateValue();
    },

    // Lazy getter that returns whether pinned tabs are restored on demand.
    get restorePinnedTabsOnDemand() {
      let updateValue = () => {
        let value = Services.prefs.getBoolPref(PREF);
        let definition = {value, configurable: true};
        Object.defineProperty(this, "restorePinnedTabsOnDemand", definition);
        return value;
      };

      const PREF = "browser.sessionstore.restore_pinned_tabs_on_demand";
      Services.prefs.addObserver(PREF, updateValue);
      return updateValue();
    },

    // Lazy getter that returns whether we should restore hidden tabs.
    get restoreHiddenTabs() {
      let updateValue = () => {
        let value = Services.prefs.getBoolPref(PREF);
        let definition = {value, configurable: true};
        Object.defineProperty(this, "restoreHiddenTabs", definition);
        return value;
      };

      const PREF = "browser.sessionstore.restore_hidden_tabs";
      Services.prefs.addObserver(PREF, updateValue);
      return updateValue();
    }
  },

  // Resets the queue and removes all tabs.
  reset() {
    this.tabs = {priority: [], visible: [], hidden: []};
  },

  // Adds a tab to the queue and determines its priority bucket.
  add(tab) {
    let {priority, hidden, visible} = this.tabs;

    if (tab.pinned) {
      priority.push(tab);
    } else if (tab.hidden) {
      hidden.push(tab);
    } else {
      visible.push(tab);
    }
  },

  // Removes a given tab from the queue, if it's in there.
  remove(tab) {
    let {priority, hidden, visible} = this.tabs;

    // We'll always check priority first since we don't
    // have an indicator if a tab will be there or not.
    let set = priority;
    let index = set.indexOf(tab);

    if (index == -1) {
      set = tab.hidden ? hidden : visible;
      index = set.indexOf(tab);
    }

    if (index > -1) {
      set.splice(index, 1);
    }
  },

  // Returns and removes the tab with the highest priority.
  shift() {
    let set;
    let {priority, hidden, visible} = this.tabs;

    let {restoreOnDemand, restorePinnedTabsOnDemand} = this.prefs;
    let restorePinned = !(restoreOnDemand && restorePinnedTabsOnDemand);
    if (restorePinned && priority.length) {
      set = priority;
    } else if (!restoreOnDemand) {
      if (visible.length) {
        set = visible;
      } else if (this.prefs.restoreHiddenTabs && hidden.length) {
        set = hidden;
      }
    }

    return set && set.shift();
  },

  // Moves a given tab from the 'hidden' to the 'visible' bucket.
  hiddenToVisible(tab) {
    let {hidden, visible} = this.tabs;
    let index = hidden.indexOf(tab);

    if (index > -1) {
      hidden.splice(index, 1);
      visible.push(tab);
    }
  },

  // Moves a given tab from the 'visible' to the 'hidden' bucket.
  visibleToHidden(tab) {
    let {visible, hidden} = this.tabs;
    let index = visible.indexOf(tab);

    if (index > -1) {
      visible.splice(index, 1);
      hidden.push(tab);
    }
  },

  /**
   * Returns true if the passed tab is in one of the sets that we're
   * restoring content in automatically.
   *
   * @param tab (<xul:tab>)
   *        The tab to check
   * @returns bool
   */
  willRestoreSoon(tab) {
    let { priority, hidden, visible } = this.tabs;
    let { restoreOnDemand, restorePinnedTabsOnDemand,
          restoreHiddenTabs } = this.prefs;
    let restorePinned = !(restoreOnDemand && restorePinnedTabsOnDemand);
    let candidateSet = [];

    if (restorePinned && priority.length)
      candidateSet.push(...priority);

    if (!restoreOnDemand) {
      if (visible.length)
        candidateSet.push(...visible);

      if (restoreHiddenTabs && hidden.length)
        candidateSet.push(...hidden);
    }

    return candidateSet.indexOf(tab) > -1;
  },
};

// A map storing a closed window's state data until it goes aways (is GC'ed).
// This ensures that API clients can still read (but not write) states of
// windows they still hold a reference to but we don't.
var DyingWindowCache = {
  _data: new WeakMap(),

  has(window) {
    return this._data.has(window);
  },

  get(window) {
    return this._data.get(window);
  },

  set(window, data) {
    this._data.set(window, data);
  },

  remove(window) {
    this._data.delete(window);
  }
};

// A weak set of dirty windows. We use it to determine which windows we need to
// recollect data for when getCurrentState() is called.
var DirtyWindows = {
  _data: new WeakMap(),

  has(window) {
    return this._data.has(window);
  },

  add(window) {
    return this._data.set(window, true);
  },

  remove(window) {
    this._data.delete(window);
  },

  clear(window) {
    this._data = new WeakMap();
  }
};

// The state from the previous session (after restoring pinned tabs). This
// state is persisted and passed through to the next session during an app
// restart to make the third party add-on warning not trash the deferred
// session
var LastSession = {
  _state: null,

  get canRestore() {
    return !!this._state;
  },

  getState() {
    return this._state;
  },

  setState(state) {
    this._state = state;
  },

  clear(silent = false) {
    if (this._state) {
      this._state = null;
      if (!silent)
        Services.obs.notifyObservers(null, NOTIFY_LAST_SESSION_CLEARED);
    }
  }
};
PK
!<s���&modules/sessionstore/SessionWorker.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

/**
 * Interface to a dedicated thread handling I/O
 */

ChromeUtils.import("resource://gre/modules/PromiseWorker.jsm", this);
ChromeUtils.import("resource://gre/modules/osfile.jsm", this);

var EXPORTED_SYMBOLS = ["SessionWorker"];

var SessionWorker = new BasePromiseWorker("resource:///modules/sessionstore/SessionWorker.js");
// As the Session Worker performs I/O, we can receive instances of
// OS.File.Error, so we need to install a decoder.
this.SessionWorker.ExceptionHandlers["OS.File.Error"] = OS.File.Error.fromMsg;

PK
!<v��ߴ!�!+modules/sessionstore/StartupPerformance.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = ["StartupPerformance"];

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", this);

ChromeUtils.defineModuleGetter(this, "Services",
  "resource://gre/modules/Services.jsm");
ChromeUtils.defineModuleGetter(this, "setTimeout",
  "resource://gre/modules/Timer.jsm");
ChromeUtils.defineModuleGetter(this, "clearTimeout",
  "resource://gre/modules/Timer.jsm");

const COLLECT_RESULTS_AFTER_MS = 10000;

const OBSERVED_TOPICS = ["sessionstore-restoring-on-startup", "sessionstore-initiating-manual-restore"];

var StartupPerformance = {
  /**
   * Once we have finished restoring initial tabs, we broadcast on this topic.
   */
  RESTORED_TOPIC: "sessionstore-finished-restoring-initial-tabs",

  // Instant at which we have started restoration (notification "sessionstore-restoring-on-startup")
  _startTimeStamp: null,

  // Latest instant at which we have finished restoring a tab (DOM event "SSTabRestored")
  _latestRestoredTimeStamp: null,

  // A promise resolved once we have finished restoring all the startup tabs.
  _promiseFinished: null,

  // Function `resolve()` for `_promiseFinished`.
  _resolveFinished: null,

  // A timer
  _deadlineTimer: null,

  // `true` once the timer has fired
  _hasFired: false,

  // `true` once we are restored
  _isRestored: false,

  // Statistics on the session we need to restore.
  _totalNumberOfEagerTabs: 0,
  _totalNumberOfTabs: 0,
  _totalNumberOfWindows: 0,

  init() {
    for (let topic of OBSERVED_TOPICS) {
      Services.obs.addObserver(this, topic);
    }
  },

  /**
   * Return the timestamp at which we finished restoring the latest tab.
   *
   * This information is not really interesting until we have finished restoring
   * tabs.
   */
  get latestRestoredTimeStamp() {
    return this._latestRestoredTimeStamp;
  },

  /**
   * `true` once we have finished restoring startup tabs.
   */
  get isRestored() {
    return this._isRestored;
  },

  // Called when restoration starts.
  // Record the start timestamp, setup the timer and `this._promiseFinished`.
  // Behavior is unspecified if there was already an ongoing measure.
  _onRestorationStarts(isAutoRestore) {
    this._latestRestoredTimeStamp = this._startTimeStamp = Date.now();
    this._totalNumberOfEagerTabs = 0;
    this._totalNumberOfTabs = 0;
    this._totalNumberOfWindows = 0;

    // While we may restore several sessions in a single run of the browser,
    // that's a very unusual case, and not really worth measuring, so let's
    // stop listening for further restorations.

    for (let topic of OBSERVED_TOPICS) {
      Services.obs.removeObserver(this, topic);
    }

    Services.obs.addObserver(this, "sessionstore-single-window-restored");
    this._promiseFinished = new Promise(resolve => {
      this._resolveFinished = resolve;
    });
    this._promiseFinished.then(() => {
      try {
        this._isRestored = true;
        Services.obs.notifyObservers(null, this.RESTORED_TOPIC);

        if (this._latestRestoredTimeStamp == this._startTimeStamp) {
          // Apparently, we haven't restored any tab.
          return;
        }

        // Once we are done restoring tabs, update Telemetry.
        let histogramName = isAutoRestore ?
          "FX_SESSION_RESTORE_AUTO_RESTORE_DURATION_UNTIL_EAGER_TABS_RESTORED_MS" :
          "FX_SESSION_RESTORE_MANUAL_RESTORE_DURATION_UNTIL_EAGER_TABS_RESTORED_MS";
        let histogram = Services.telemetry.getHistogramById(histogramName);
        let delta = this._latestRestoredTimeStamp - this._startTimeStamp;
        histogram.add(delta);

        Services.telemetry.getHistogramById("FX_SESSION_RESTORE_NUMBER_OF_EAGER_TABS_RESTORED").add(this._totalNumberOfEagerTabs);
        Services.telemetry.getHistogramById("FX_SESSION_RESTORE_NUMBER_OF_TABS_RESTORED").add(this._totalNumberOfTabs);
        Services.telemetry.getHistogramById("FX_SESSION_RESTORE_NUMBER_OF_WINDOWS_RESTORED").add(this._totalNumberOfWindows);

        // Reset
        this._startTimeStamp = null;
     } catch (ex) {
        console.error("StartupPerformance: error after resolving promise", ex);
      }
    });
  },

  _startTimer() {
    if (this._hasFired) {
      return;
    }
    if (this._deadlineTimer) {
      clearTimeout(this._deadlineTimer);
    }
    this._deadlineTimer = setTimeout(() => {
      try {
        this._resolveFinished();
      } catch (ex) {
        console.error("StartupPerformance: Error in timeout handler", ex);
      } finally {
        // Clean up.
        this._deadlineTimer = null;
        this._hasFired = true;
        this._resolveFinished = null;
        Services.obs.removeObserver(this, "sessionstore-single-window-restored");
      }
    }, COLLECT_RESULTS_AFTER_MS);
  },

  observe(subject, topic, details) {
    try {
      switch (topic) {
        case "sessionstore-restoring-on-startup":
          this._onRestorationStarts(true);
          break;
        case "sessionstore-initiating-manual-restore":
          this._onRestorationStarts(false);
          break;
        case "sessionstore-single-window-restored": {
          // Session Restore has just opened a window with (initially empty) tabs.
          // Some of these tabs will be restored eagerly, while others will be
          // restored on demand. The process becomes usable only when all windows
          // have finished restored their eager tabs.
          //
          // While it would be possible to track the restoration of each tab
          // from within SessionRestore to determine exactly when the process
          // becomes usable, experience shows that this is too invasive. Rather,
          // we employ the following heuristic:
          // - we maintain a timer of `COLLECT_RESULTS_AFTER_MS` that we expect
          //   will be triggered only once all tabs have been restored;
          // - whenever we restore a new window (hence a bunch of eager tabs),
          //   we postpone the timer to ensure that the new eager tabs have
          //   `COLLECT_RESULTS_AFTER_MS` to be restored;
          // - whenever a tab is restored, we update
          //   `this._latestRestoredTimeStamp`;
          // - after `COLLECT_RESULTS_AFTER_MS`, we collect the final version
          //   of `this._latestRestoredTimeStamp`, and use it to determine the
          //   entire duration of the collection.
          //
          // Note that this heuristic may be inaccurate if a user clicks
          // immediately on a restore-on-demand tab before the end of
          // `COLLECT_RESULTS_AFTER_MS`. We assume that this will not
          // affect too much the results.
          //
          // Reset the delay, to give the tabs a little (more) time to restore.
          this._startTimer();

          this._totalNumberOfWindows += 1;

          // Observe the restoration of all tabs. We assume that all tabs of this
          // window will have been restored before `COLLECT_RESULTS_AFTER_MS`.
          // The last call to `observer` will let us determine how long it took
          // to reach that point.
          let win = subject;

          let observer = (event) => {
            // We don't care about tab restorations that are due to
            // a browser flipping from out-of-main-process to in-main-process
            // or vice-versa. We only care about restorations that are due
            // to the user switching to a lazily restored tab, or for tabs
            // that are restoring eagerly.
            if (!event.detail.isRemotenessUpdate) {
              this._latestRestoredTimeStamp = Date.now();
              this._totalNumberOfEagerTabs += 1;
            }
          };
          win.gBrowser.tabContainer.addEventListener("SSTabRestored", observer);
          this._totalNumberOfTabs += win.gBrowser.tabContainer.itemCount;

          // Once we have finished collecting the results, clean up the observers.
          this._promiseFinished.then(() => {
            if (!win.gBrowser.tabContainer) {
              // May be undefined during shutdown and/or some tests.
              return;
            }
            win.gBrowser.tabContainer.removeEventListener("SSTabRestored", observer);
          });
        }
        break;
        default:
          throw new Error(`Unexpected topic ${topic}`);
      }
    } catch (ex) {
      console.error("StartupPerformance error", ex, ex.stack);
      throw ex;
    }
  }
};
PK
!<5n�z		&modules/sessionstore/TabAttributes.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = ["TabAttributes"];

// We never want to directly read or write these attributes.
// 'image' should not be accessed directly but handled by using the
//         gBrowser.getIcon()/setIcon() methods.
// 'muted' should not be accessed directly but handled by using the
//         tab.linkedBrowser.audioMuted/toggleMuteAudio methods.
// 'pending' is used internal by sessionstore and managed accordingly.
// 'iconloadingprincipal' is same as 'image' that it should be handled by
//                        using the gBrowser.getIcon()/setIcon() methods.
// 'activemedia-blocked' should not be accessed directly but handled by using
//                       tab's toggleMuteAudio() or linkedBrowser's methods
//                       activeMediaBlockStarted()/activeMediaBlockBlockStopped().
const ATTRIBUTES_TO_SKIP = new Set(["image", "muted", "pending", "iconloadingprincipal",
                                    "skipbackgroundnotify", "activemedia-blocked"]);

// A set of tab attributes to persist. We will read a given list of tab
// attributes when collecting tab data and will re-set those attributes when
// the given tab data is restored to a new tab.
var TabAttributes = Object.freeze({
  persist(name) {
    return TabAttributesInternal.persist(name);
  },

  get(tab) {
    return TabAttributesInternal.get(tab);
  },

  set(tab, data = {}) {
    TabAttributesInternal.set(tab, data);
  }
});

var TabAttributesInternal = {
  _attrs: new Set(),

  persist(name) {
    if (this._attrs.has(name) || ATTRIBUTES_TO_SKIP.has(name)) {
      return false;
    }

    this._attrs.add(name);
    return true;
  },

  get(tab) {
    let data = {};

    for (let name of this._attrs) {
      if (tab.hasAttribute(name)) {
        data[name] = tab.getAttribute(name);
      }
    }

    return data;
  },

  set(tab, data = {}) {
    // Clear attributes.
    for (let name of this._attrs) {
      tab.removeAttribute(name);
    }

    // Set attributes.
    for (let name in data) {
      if (!ATTRIBUTES_TO_SKIP.has(name)) {
        tab.setAttribute(name, data[name]);
      }
    }
  }
};

PK
!<�����!modules/sessionstore/TabState.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = ["TabState"];

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", this);

ChromeUtils.defineModuleGetter(this, "PrivacyFilter",
  "resource:///modules/sessionstore/PrivacyFilter.jsm");
ChromeUtils.defineModuleGetter(this, "TabStateCache",
  "resource:///modules/sessionstore/TabStateCache.jsm");
ChromeUtils.defineModuleGetter(this, "TabAttributes",
  "resource:///modules/sessionstore/TabAttributes.jsm");
ChromeUtils.defineModuleGetter(this, "Utils",
  "resource://gre/modules/sessionstore/Utils.jsm");

/**
 * Module that contains tab state collection methods.
 */
var TabState = Object.freeze({
  update(browser, data) {
    TabStateInternal.update(browser, data);
  },

  collect(tab) {
    return TabStateInternal.collect(tab);
  },

  clone(tab) {
    return TabStateInternal.clone(tab);
  },

  copyFromCache(browser, tabData, options) {
    TabStateInternal.copyFromCache(browser, tabData, options);
  },
});

var TabStateInternal = {
  /**
   * Processes a data update sent by the content script.
   */
  update(browser, {data}) {
    TabStateCache.update(browser, data);
  },

  /**
   * Collect data related to a single tab, synchronously.
   *
   * @param tab
   *        tabbrowser tab
   *
   * @returns {TabData} An object with the data for this tab.  If the
   * tab has not been invalidated since the last call to
   * collect(aTab), the same object is returned.
   */
  collect(tab) {
    return this._collectBaseTabData(tab);
  },

  /**
   * Collect data related to a single tab, including private data.
   * Use with caution.
   *
   * @param tab
   *        tabbrowser tab
   *
   * @returns {object} An object with the data for this tab. This data is never
   *                   cached, it will always be read from the tab and thus be
   *                   up-to-date.
   */
  clone(tab) {
    return this._collectBaseTabData(tab, {includePrivateData: true});
  },

  /**
   * Collects basic tab data for a given tab.
   *
   * @param tab
   *        tabbrowser tab
   * @param options (object)
   *        {includePrivateData: true} to always include private data
   *
   * @returns {object} An object with the basic data for this tab.
   */
  _collectBaseTabData(tab, options) {
    let tabData = { entries: [], lastAccessed: tab.lastAccessed };
    let browser = tab.linkedBrowser;

    if (tab.pinned) {
      tabData.pinned = true;
    }

    tabData.hidden = tab.hidden;

    if (browser.audioMuted) {
      tabData.muted = true;
      tabData.muteReason = tab.muteReason;
    }

    tabData.mediaBlocked = browser.mediaBlocked;

    // Save tab attributes.
    tabData.attributes = TabAttributes.get(tab);

    if (tab.__SS_extdata) {
      tabData.extData = tab.__SS_extdata;
    }

    // Copy data from the tab state cache only if the tab has fully finished
    // restoring. We don't want to overwrite data contained in __SS_data.
    this.copyFromCache(browser, tabData, options);

    // After copyFromCache() was called we check for properties that are kept
    // in the cache only while the tab is pending or restoring. Once that
    // happened those properties will be removed from the cache and will
    // be read from the tab/browser every time we collect data.

    // Store the tab icon.
    if (!("image" in tabData)) {
      let tabbrowser = tab.ownerGlobal.gBrowser;
      tabData.image = tabbrowser.getIcon(tab);
    }

    // Store the serialized contentPrincipal of this tab to use for the icon.
    if (!("iconLoadingPrincipal" in tabData)) {
      tabData.iconLoadingPrincipal = Utils.serializePrincipal(browser.mIconLoadingPrincipal);
    }

    // If there is a userTypedValue set, then either the user has typed something
    // in the URL bar, or a new tab was opened with a URI to load.
    // If so, we also track whether we were still in the process of loading something.
    if (!("userTypedValue" in tabData) && browser.userTypedValue) {
      tabData.userTypedValue = browser.userTypedValue;
      // We always used to keep track of the loading state as an integer, where
      // '0' indicated the user had typed since the last load (or no load was
      // ongoing), and any positive value indicated we had started a load since
      // the last time the user typed in the URL bar. Mimic this to keep the
      // session store representation in sync, even though we now represent this
      // more explicitly:
      tabData.userTypedClear = browser.didStartLoadSinceLastUserTyping() ? 1 : 0;
    }

    return tabData;
  },

  /**
   * Copy data for the given |browser| from the cache to |tabData|.
   *
   * @param browser (xul:browser)
   *        The browser belonging to the given |tabData| object.
   * @param tabData (object)
   *        The tab data belonging to the given |tab|.
   * @param options (object)
   *        {includePrivateData: true} to always include private data
   */
  copyFromCache(browser, tabData, options = {}) {
    let data = TabStateCache.get(browser);
    if (!data) {
      return;
    }

    // The caller may explicitly request to omit privacy checks.
    let includePrivateData = options && options.includePrivateData;

    for (let key of Object.keys(data)) {
      let value = data[key];

      // Filter sensitive data according to the current privacy level.
      if (!includePrivateData) {
        if (key === "storage") {
          value = PrivacyFilter.filterSessionStorageData(value);
        } else if (key === "formdata") {
          value = PrivacyFilter.filterFormData(value);
        }
      }

      if (key === "history") {
        // Make a shallow copy of the entries array. We (currently) don't update
        // entries in place, so we don't have to worry about performing a deep
        // copy.
        tabData.entries = [...value.entries];

        if (value.hasOwnProperty("userContextId")) {
          tabData.userContextId = value.userContextId;
        }

        if (value.hasOwnProperty("index")) {
          tabData.index = value.index;
        }
      } else {
        tabData[key] = value;
      }
    }
  }
};
PK
!<X��jj&modules/sessionstore/TabStateCache.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = ["TabStateCache"];

/**
 * A cache for tabs data.
 *
 * This cache implements a weak map from tabs (as XUL elements)
 * to tab data (as objects).
 *
 * Note that we should never cache private data, as:
 * - that data is used very seldom by SessionStore;
 * - caching private data in addition to public data is memory consuming.
 */
var TabStateCache = Object.freeze({
  /**
   * Retrieves cached data for a given |tab| or associated |browser|.
   *
   * @param browserOrTab (xul:tab or xul:browser)
   *        The tab or browser to retrieve cached data for.
   * @return (object)
   *         The cached data stored for the given |tab|
   *         or associated |browser|.
   */
  get(browserOrTab) {
    return TabStateCacheInternal.get(browserOrTab);
  },

  /**
   * Updates cached data for a given |tab| or associated |browser|.
   *
   * @param browserOrTab (xul:tab or xul:browser)
   *        The tab or browser belonging to the given tab data.
   * @param newData (object)
   *        The new data to be stored for the given |tab|
   *        or associated |browser|.
   */
  update(browserOrTab, newData) {
    TabStateCacheInternal.update(browserOrTab, newData);
  }
});

var TabStateCacheInternal = {
  _data: new WeakMap(),

  /**
   * Retrieves cached data for a given |tab| or associated |browser|.
   *
   * @param browserOrTab (xul:tab or xul:browser)
   *        The tab or browser to retrieve cached data for.
   * @return (object)
   *         The cached data stored for the given |tab|
   *         or associated |browser|.
   */
  get(browserOrTab) {
    return this._data.get(browserOrTab.permanentKey);
  },

  /**
   * Helper function used by update (see below). For message size
   * optimization sometimes we don't update the whole session storage
   * only the values that have been changed.
   *
   * @param data (object)
   *        The cached data where we want to update the changes.
   * @param change (object)
   *        The actual changed values per domain.
   */
  updatePartialStorageChange(data, change) {
    if (!data.storage) {
      data.storage = {};
    }

    let storage = data.storage;
    for (let domain of Object.keys(change)) {
      for (let key of Object.keys(change[domain])) {
        let value = change[domain][key];
        if (value === null) {
          if (storage[domain] && storage[domain][key]) {
            delete storage[domain][key];
          }
        } else {
          if (!storage[domain]) {
            storage[domain] = {};
          }
          storage[domain][key] = value;
        }
      }
    }
  },

  /**
   * Helper function used by update (see below). For message size
   * optimization sometimes we don't update the whole browser history
   * only the current index and the tail of the history from a certain
   * index (specified by change.fromIdx)
   *
   * @param data (object)
   *        The cached data where we want to update the changes.
   * @param change (object)
   *        Object containing the tail of the history array, and
   *        some additional metadata.
   */
  updatePartialHistoryChange(data, change) {
    const kLastIndex = Number.MAX_SAFE_INTEGER - 1;

    if (!data.history) {
      data.history = { entries: [] };
    }

    let history = data.history;
    let toIdx = history.entries.length;
    if ("toIdx" in change) {
      toIdx = Math.min(toIdx, change.toIdx + 1);
    }

    for (let key of Object.keys(change)) {
      if (key == "entries") {
        if (change.fromIdx != kLastIndex) {
          let start = change.fromIdx + 1;
          history.entries.splice.apply(
            history.entries, [start, toIdx - start].concat(change.entries));
        }
      } else if (key != "fromIdx" && key != "toIdx") {
        history[key] = change[key];
      }
    }
  },

  /**
   * Updates cached data for a given |tab| or associated |browser|.
   *
   * @param browserOrTab (xul:tab or xul:browser)
   *        The tab or browser belonging to the given tab data.
   * @param newData (object)
   *        The new data to be stored for the given |tab|
   *        or associated |browser|.
   */
  update(browserOrTab, newData) {
    let data = this._data.get(browserOrTab.permanentKey) || {};

    for (let key of Object.keys(newData)) {
      if (key == "storagechange") {
        this.updatePartialStorageChange(data, newData.storagechange);
        continue;
      }

      if (key == "historychange") {
        this.updatePartialHistoryChange(data, newData.historychange);
        continue;
      }

      let value = newData[key];
      if (value === null) {
        delete data[key];
      } else {
        data[key] = value;
      }
    }

    this._data.set(browserOrTab.permanentKey, data);
  }
};
PK
!<�`x��(modules/sessionstore/TabStateFlusher.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = ["TabStateFlusher"];

/**
 * A module that enables async flushes. Updates from frame scripts are
 * throttled to be sent only once per second. If an action wants a tab's latest
 * state without waiting for a second then it can request an async flush and
 * wait until the frame scripts reported back. At this point the parent has the
 * latest data and the action can continue.
 */
var TabStateFlusher = Object.freeze({
  /**
   * Requests an async flush for the given browser. Returns a promise that will
   * resolve when we heard back from the content process and the parent has
   * all the latest data.
   */
  flush(browser) {
    return TabStateFlusherInternal.flush(browser);
  },

  /**
   * Requests an async flush for all browsers of a given window. Returns a Promise
   * that will resolve when we've heard back from all browsers.
   */
  flushWindow(window) {
    return TabStateFlusherInternal.flushWindow(window);
  },

  /**
   * Resolves the flush request with the given flush ID.
   *
   * @param browser (<xul:browser>)
   *        The browser for which the flush is being resolved.
   * @param flushID (int)
   *        The ID of the flush that was sent to the browser.
   * @param success (bool, optional)
   *        Whether or not the flush succeeded.
   * @param message (string, optional)
   *        An error message that will be sent to the Console in the
   *        event that a flush failed.
   */
  resolve(browser, flushID, success = true, message = "") {
    TabStateFlusherInternal.resolve(browser, flushID, success, message);
  },

  /**
   * Resolves all active flush requests for a given browser. This should be
   * used when the content process crashed or the final update message was
   * seen. In those cases we can't guarantee to ever hear back from the frame
   * script so we just resolve all requests instead of discarding them.
   *
   * @param browser (<xul:browser>)
   *        The browser for which all flushes are being resolved.
   * @param success (bool, optional)
   *        Whether or not the flushes succeeded.
   * @param message (string, optional)
   *        An error message that will be sent to the Console in the
   *        event that the flushes failed.
   */
  resolveAll(browser, success = true, message = "") {
    TabStateFlusherInternal.resolveAll(browser, success, message);
  }
});

var TabStateFlusherInternal = {
  // Stores the last request ID.
  _lastRequestID: 0,

  // A map storing all active requests per browser.
  _requests: new WeakMap(),

  /**
   * Requests an async flush for the given browser. Returns a promise that will
   * resolve when we heard back from the content process and the parent has
   * all the latest data.
   */
  flush(browser) {
    let id = ++this._lastRequestID;
    let mm = browser.messageManager;
    mm.sendAsyncMessage("SessionStore:flush", {id});

    // Retrieve active requests for given browser.
    let permanentKey = browser.permanentKey;
    let perBrowserRequests = this._requests.get(permanentKey) || new Map();

    return new Promise(resolve => {
      // Store resolve() so that we can resolve the promise later.
      perBrowserRequests.set(id, resolve);

      // Update the flush requests stored per browser.
      this._requests.set(permanentKey, perBrowserRequests);
    });
  },

  /**
   * Requests an async flush for all non-lazy browsers of a given window.
   * Returns a Promise that will resolve when we've heard back from all browsers.
   */
  flushWindow(window) {
    let promises = [];
    for (let browser of window.gBrowser.browsers) {
      if (window.gBrowser.getTabForBrowser(browser).linkedPanel) {
        promises.push(this.flush(browser));
      }
    }
    return Promise.all(promises);
  },

  /**
   * Resolves the flush request with the given flush ID.
   *
   * @param browser (<xul:browser>)
   *        The browser for which the flush is being resolved.
   * @param flushID (int)
   *        The ID of the flush that was sent to the browser.
   * @param success (bool, optional)
   *        Whether or not the flush succeeded.
   * @param message (string, optional)
   *        An error message that will be sent to the Console in the
   *        event that a flush failed.
   */
  resolve(browser, flushID, success = true, message = "") {
    // Nothing to do if there are no pending flushes for the given browser.
    if (!this._requests.has(browser.permanentKey)) {
      return;
    }

    // Retrieve active requests for given browser.
    let perBrowserRequests = this._requests.get(browser.permanentKey);
    if (!perBrowserRequests.has(flushID)) {
      return;
    }

    if (!success) {
      Cu.reportError("Failed to flush browser: " + message);
    }

    // Resolve the request with the given id.
    let resolve = perBrowserRequests.get(flushID);
    perBrowserRequests.delete(flushID);
    resolve(success);
  },

  /**
   * Resolves all active flush requests for a given browser. This should be
   * used when the content process crashed or the final update message was
   * seen. In those cases we can't guarantee to ever hear back from the frame
   * script so we just resolve all requests instead of discarding them.
   *
   * @param browser (<xul:browser>)
   *        The browser for which all flushes are being resolved.
   * @param success (bool, optional)
   *        Whether or not the flushes succeeded.
   * @param message (string, optional)
   *        An error message that will be sent to the Console in the
   *        event that the flushes failed.
   */
  resolveAll(browser, success = true, message = "") {
    // Nothing to do if there are no pending flushes for the given browser.
    if (!this._requests.has(browser.permanentKey)) {
      return;
    }

    // Retrieve active requests for given browser.
    let perBrowserRequests = this._requests.get(browser.permanentKey);

    if (!success) {
      Cu.reportError("Failed to flush browser: " + message);
    }

    // Resolve all requests.
    for (let resolve of perBrowserRequests.values()) {
      resolve(success);
    }

    // Clear active requests.
    perBrowserRequests.clear();
  }
};
PK
!<�V�H��-modules/syncedtabs/SyncedTabsDeckComponent.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");

ChromeUtils.import("resource:///modules/syncedtabs/SyncedTabsDeckStore.js");
ChromeUtils.import("resource:///modules/syncedtabs/SyncedTabsDeckView.js");
ChromeUtils.import("resource:///modules/syncedtabs/SyncedTabsListStore.js");
ChromeUtils.import("resource:///modules/syncedtabs/TabListComponent.js");
ChromeUtils.import("resource:///modules/syncedtabs/TabListView.js");
let { getChromeWindow } = ChromeUtils.import("resource:///modules/syncedtabs/util.js", {});

XPCOMUtils.defineLazyGetter(this, "FxAccountsCommon", function() {
  return ChromeUtils.import("resource://gre/modules/FxAccountsCommon.js", {});
});

let log = ChromeUtils.import("resource://gre/modules/Log.jsm", {})
            .Log.repository.getLogger("Sync.RemoteTabs");

var EXPORTED_SYMBOLS = [
  "SyncedTabsDeckComponent"
];

/* SyncedTabsDeckComponent
 * This component instantiates views and storage objects as well as defines
 * behaviors that will be passed down to the views. This helps keep the views
 * isolated and easier to test.
 */

function SyncedTabsDeckComponent({
  window, SyncedTabs, fxAccounts, deckStore, listStore, listComponent, DeckView, getChromeWindowMock,
}) {
  this._window = window;
  this._SyncedTabs = SyncedTabs;
  this._fxAccounts = fxAccounts;
  this._DeckView = DeckView || SyncedTabsDeckView;
  // used to stub during tests
  this._getChromeWindow = getChromeWindowMock || getChromeWindow;

  this._deckStore = deckStore || new SyncedTabsDeckStore();
  this._syncedTabsListStore = listStore || new SyncedTabsListStore(SyncedTabs);
  this.tabListComponent = listComponent || new TabListComponent({
    window: this._window,
    store: this._syncedTabsListStore,
    View: TabListView,
    SyncedTabs,
    clipboardHelper: Cc["@mozilla.org/widget/clipboardhelper;1"]
                       .getService(Ci.nsIClipboardHelper),
    getChromeWindow: this._getChromeWindow,
  });
}

SyncedTabsDeckComponent.prototype = {
  PANELS: {
    TABS_CONTAINER: "tabs-container",
    TABS_FETCHING: "tabs-fetching",
    LOGIN_FAILED: "reauth",
    NOT_AUTHED_INFO: "notAuthedInfo",
    SINGLE_DEVICE_INFO: "singleDeviceInfo",
    TABS_DISABLED: "tabs-disabled",
    UNVERIFIED: "unverified"
  },

  get container() {
    return this._deckView ? this._deckView.container : null;
  },

  init() {
    Services.obs.addObserver(this, this._SyncedTabs.TOPIC_TABS_CHANGED);
    Services.obs.addObserver(this, FxAccountsCommon.ONLOGIN_NOTIFICATION);
    Services.obs.addObserver(this, "weave:service:login:change");
    // If the Sync service is not ready, in init() > updatePanel() we will
    // show a blank screen. If tab syncing is disabled, we will not get any other
    // ui-refreshing notifications! We listen to :ready in order to check again
    // if this engine is disabled and refresh the UI one last time.
    Services.obs.addObserver(this, "weave:service:ready");

    // Go ahead and trigger sync
    this._SyncedTabs.syncTabs()
                    .catch(Cu.reportError);

    this._deckView = new this._DeckView(this._window, this.tabListComponent, {
      onConnectDeviceClick: event => this.openConnectDevice(event),
      onSyncPrefClick: event => this.openSyncPrefs(event)
    });

    this._deckStore.on("change", state => this._deckView.render(state));
    // Trigger the initial rendering of the deck view
    // Object.values only in nightly
    this._deckStore.setPanels(Object.keys(this.PANELS).map(k => this.PANELS[k]));
    // Set the initial panel to display
    this.updatePanel();
  },

  uninit() {
    Services.obs.removeObserver(this, this._SyncedTabs.TOPIC_TABS_CHANGED);
    Services.obs.removeObserver(this, FxAccountsCommon.ONLOGIN_NOTIFICATION);
    Services.obs.removeObserver(this, "weave:service:login:change");
    Services.obs.removeObserver(this, "weave:service:ready");
    this._deckView.destroy();
  },

  observe(subject, topic, data) {
    switch (topic) {
      case this._SyncedTabs.TOPIC_TABS_CHANGED:
        this._syncedTabsListStore.getData();
        this.updatePanel();
        break;
      case "weave:service:ready":
        Services.obs.removeObserver(this, "weave:service:ready");
        // Intended fallthrough.
      case FxAccountsCommon.ONLOGIN_NOTIFICATION:
      case "weave:service:login:change":
        this.updatePanel();
        break;
      default:
        break;
    }
  },

  // There's no good way to mock fxAccounts in browser tests where it's already
  // been instantiated, so we have this method for stubbing.
  _getSignedInUser() {
    return this._fxAccounts.getSignedInUser();
  },

  getPanelStatus() {
    return this._getSignedInUser().then(user => {
      if (!user) {
        return this.PANELS.NOT_AUTHED_INFO;
      }
      if (this._SyncedTabs.loginFailed) {
        return this.PANELS.LOGIN_FAILED;
      }
      if (!user.verified) {
        return this.PANELS.UNVERIFIED;
      }
      if (!this._SyncedTabs.isConfiguredToSyncTabs) {
        return this.PANELS.TABS_DISABLED;
      }
      if (!this._SyncedTabs.hasSyncedThisSession) {
        return this.PANELS.TABS_FETCHING;
      }
      return this._SyncedTabs.getTabClients().then(clients => {
        if (clients.length) {
          return this.PANELS.TABS_CONTAINER;
        }
        return this.PANELS.SINGLE_DEVICE_INFO;
      });
    })
    .catch(err => {
      Cu.reportError(err);
      return this.PANELS.NOT_AUTHED_INFO;
    });
  },

  updatePanel() {
    // return promise for tests
    return this.getPanelStatus()
      .then(panelId => this._deckStore.selectPanel(panelId))
      .catch(Cu.reportError);
  },

  openSyncPrefs() {
    this._getChromeWindow(this._window).gSync.openPrefs("tabs-sidebar");
  },

  openConnectDevice() {
    this._getChromeWindow(this._window).gSync.openConnectAnotherDevice("tabs-sidebar");
  },
};

PK
!<�ͩ{��)modules/syncedtabs/SyncedTabsDeckStore.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

let { EventEmitter } = ChromeUtils.import("resource:///modules/syncedtabs/EventEmitter.jsm", {});

var EXPORTED_SYMBOLS = [
  "SyncedTabsDeckStore"
];

/**
 * SyncedTabsDeckStore
 *
 * This store keeps track of the deck view state, including the panels and which
 * one is selected. The view listens for change events on the store, which are
 * triggered whenever the state changes. If it's a small change, the state
 * will have `isUpdatable` set to true so the view can skip rerendering the whole
 * DOM.
 */
function SyncedTabsDeckStore() {
  EventEmitter.call(this);
  this._panels = [];
}

Object.assign(SyncedTabsDeckStore.prototype, EventEmitter.prototype, {
  _change(isUpdatable = false) {
    let panels = this._panels.map(panel => {
      return {id: panel, selected: panel === this._selectedPanel};
    });
    this.emit("change", {panels, isUpdatable});
  },

  /**
   * Sets the selected panelId and triggers a change event.
   * @param {String} panelId - ID of the panel to select.
   */
  selectPanel(panelId) {
    if (!this._panels.includes(panelId) || this._selectedPanel === panelId) {
      return;
    }
    this._selectedPanel = panelId;
    this._change(true);
  },

  /**
   * Update the set of panels in the deck and trigger a change event.
   * @param {Array} panels - an array of IDs for each panel in the deck.
   */
  setPanels(panels) {
    if (panels === this._panels) {
      return;
    }
    this._panels = panels || [];
    this._change();
  }
});
PK
!<}O���
�
(modules/syncedtabs/SyncedTabsDeckView.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

let log = ChromeUtils.import("resource://gre/modules/Log.jsm", {})
            .Log.repository.getLogger("Sync.RemoteTabs");

var EXPORTED_SYMBOLS = [
  "SyncedTabsDeckView"
];

/**
 * SyncedTabsDeckView
 *
 * Instances of SyncedTabsDeckView render DOM nodes from a given state.
 * No state is kept internaly and the DOM will completely
 * rerender unless the state flags `isUpdatable`, which helps
 * make small changes without the overhead of a full rerender.
 */
const SyncedTabsDeckView = function(window, tabListComponent, props) {
  this.props = props;

  this._window = window;
  this._doc = window.document;

  this._tabListComponent = tabListComponent;
  this._deckTemplate = this._doc.getElementById("deck-template");
  this.container = this._doc.createElement("div");
};

SyncedTabsDeckView.prototype = {
  render(state) {
    if (state.isUpdatable) {
      this.update(state);
    } else {
      this.create(state);
    }
  },

  create(state) {
    let deck = this._doc.importNode(this._deckTemplate.content, true).firstElementChild;
    this._clearChilden();

    let tabListWrapper = this._doc.createElement("div");
    tabListWrapper.className = "tabs-container sync-state";
    this._tabListComponent.init();
    tabListWrapper.appendChild(this._tabListComponent.container);
    deck.appendChild(tabListWrapper);
    this.container.appendChild(deck);

    this._attachListeners();
    this.update(state);
  },

  destroy() {
    this._tabListComponent.uninit();
    this.container.remove();
  },

  update(state) {
    // Note that we may also want to update elements that are outside of the
    // deck, so use the document to find the class names rather than our
    // container.
    for (let panel of state.panels) {
      if (panel.selected) {
        Array.prototype.map.call(this._doc.getElementsByClassName(panel.id),
                                 item => item.classList.add("selected"));
      } else {
        Array.prototype.map.call(this._doc.getElementsByClassName(panel.id),
                                 item => item.classList.remove("selected"));
      }
    }
  },

  _clearChilden() {
    while (this.container.firstChild) {
      this.container.firstChild.remove();
    }
  },

  _attachListeners() {
    let syncPrefLinks = this.container.querySelectorAll(".sync-prefs");
    for (let link of syncPrefLinks) {
      link.addEventListener("click", this.props.onSyncPrefClick);
    }
    this.container.querySelector(".connect-device").addEventListener("click", this.props.onConnectDeviceClick);
  },
};

PK
!<���p66)modules/syncedtabs/SyncedTabsListStore.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

let { EventEmitter } = ChromeUtils.import("resource:///modules/syncedtabs/EventEmitter.jsm", {});

var EXPORTED_SYMBOLS = [
  "SyncedTabsListStore"
];

/**
 * SyncedTabsListStore
 *
 * Instances of this store encapsulate all of the state associated with a synced tabs list view.
 * The state includes the clients, their tabs, the row that is currently selected,
 * and the filtered query.
 */
function SyncedTabsListStore(SyncedTabs) {
  EventEmitter.call(this);
  this._SyncedTabs = SyncedTabs;
  this.data = [];
  this._closedClients = {};
  this._selectedRow = [-1, -1];
  this.filter = "";
  this.inputFocused = false;
}

Object.assign(SyncedTabsListStore.prototype, EventEmitter.prototype, {
  // This internal method triggers the "change" event that views
  // listen for. It denormalizes the state so that it's easier for
  // the view to deal with. updateType hints to the view what
  // actually needs to be rerendered or just updated, and can be
  // empty (to (re)render everything), "searchbox" (to rerender just the tab list),
  // or "all" (to skip rendering and just update all attributes of existing nodes).
  _change(updateType) {
    let selectedParent = this._selectedRow[0];
    let selectedChild = this._selectedRow[1];
    let rowSelected = false;
    // clone the data so that consumers can't mutate internal storage
    let data = Cu.cloneInto(this.data, {});
    let tabCount = 0;

    data.forEach((client, index) => {
      client.closed = !!this._closedClients[client.id];

      if (rowSelected || selectedParent < 0) {
        return;
      }
      if (this.filter) {
        if (selectedParent < tabCount + client.tabs.length) {
          client.tabs[selectedParent - tabCount].selected = true;
          client.tabs[selectedParent - tabCount].focused = !this.inputFocused;
          rowSelected = true;
        } else {
          tabCount += client.tabs.length;
        }
        return;
      }
      if (selectedParent === index && selectedChild === -1) {
        client.selected = true;
        client.focused = !this.inputFocused;
        rowSelected = true;
      } else if (selectedParent === index) {
        client.tabs[selectedChild].selected = true;
        client.tabs[selectedChild].focused = !this.inputFocused;
        rowSelected = true;
      }
    });

    // If this were React the view would be smart enough
    // to not re-render the whole list unless necessary. But it's
    // not, so updateType is a hint to the view of what actually
    // needs to be rerendered.
    this.emit("change", {
      clients: data,
      canUpdateAll: updateType === "all",
      canUpdateInput: updateType === "searchbox",
      filter: this.filter,
      inputFocused: this.inputFocused
    });
  },

  /**
   * Moves the row selection from a child to its parent,
   * which occurs when the parent of a selected row closes.
   */
  _selectParentRow() {
    this._selectedRow[1] = -1;
  },

  _toggleBranch(id, closed) {
    this._closedClients[id] = closed;
    if (this._closedClients[id]) {
      this._selectParentRow();
    }
    this._change("all");
  },

  _isOpen(client) {
    return !this._closedClients[client.id];
  },

  moveSelectionDown() {
    let branchRow = this._selectedRow[0];
    let childRow = this._selectedRow[1];
    let branch = this.data[branchRow];

    if (this.filter) {
      this.selectRow(branchRow + 1);
      return;
    }

    if (branchRow < 0) {
      this.selectRow(0, -1);
    } else if ((!branch.tabs.length || childRow >= branch.tabs.length - 1 || !this._isOpen(branch)) && branchRow < this.data.length) {
      this.selectRow(branchRow + 1, -1);
    } else if (childRow < branch.tabs.length) {
      this.selectRow(branchRow, childRow + 1);
    }
  },

  moveSelectionUp() {
    let branchRow = this._selectedRow[0];
    let childRow = this._selectedRow[1];

    if (this.filter) {
      this.selectRow(branchRow - 1);
      return;
    }

    if (branchRow < 0) {
      this.selectRow(0, -1);
    } else if (childRow < 0 && branchRow > 0) {
      let prevBranch = this.data[branchRow - 1];
      let newChildRow = this._isOpen(prevBranch) ? prevBranch.tabs.length - 1 : -1;
      this.selectRow(branchRow - 1, newChildRow);
    } else if (childRow >= 0) {
      this.selectRow(branchRow, childRow - 1);
    }
  },

  // Selects a row and makes sure the selection is within bounds
  selectRow(parent, child) {
    let maxParentRow = this.filter ? this._tabCount() : this.data.length;
    let parentRow = parent;
    if (parent <= -1) {
      parentRow = 0;
    } else if (parent >= maxParentRow) {
      return;
    }

    let childRow = child;
    if (parentRow === -1 || this.filter || typeof child === "undefined" || child < -1) {
      childRow = -1;
    } else if (child >= this.data[parentRow].tabs.length) {
      childRow = this.data[parentRow].tabs.length - 1;
    }

    if (this._selectedRow[0] === parentRow && this._selectedRow[1] === childRow) {
      return;
    }

    this._selectedRow = [parentRow, childRow];
    this.inputFocused = false;
    this._change("all");
  },

  _tabCount() {
    return this.data.reduce((prev, curr) => curr.tabs.length + prev, 0);
  },

  toggleBranch(id) {
    this._toggleBranch(id, !this._closedClients[id]);
  },

  closeBranch(id) {
    this._toggleBranch(id, true);
  },

  openBranch(id) {
    this._toggleBranch(id, false);
  },

  focusInput() {
    this.inputFocused = true;
    // A change type of "all" updates rather than rebuilds, which is what we
    // want here - only the selection/focus has changed.
    this._change("all");
  },

  blurInput() {
    this.inputFocused = false;
    // A change type of "all" updates rather than rebuilds, which is what we
    // want here - only the selection/focus has changed.
    this._change("all");
  },

  clearFilter() {
    this.filter = "";
    this._selectedRow = [-1, -1];
    return this.getData();
  },

  // Fetches data from the SyncedTabs module and triggers
  // and update
  getData(filter) {
    let updateType;
    let hasFilter = typeof filter !== "undefined";
    if (hasFilter) {
      this.filter = filter;
      this._selectedRow = [-1, -1];

      // When a filter is specified we tell the view that only the list
      // needs to be rerendered so that it doesn't disrupt the input
      // field's focus.
      updateType = "searchbox";
    }

    // return promise for tests
    return this._SyncedTabs.getTabClients(this.filter)
      .then(result => {
        if (!hasFilter) {
          // Only sort clients and tabs if we're rendering the whole list.
          this._SyncedTabs.sortTabClientsByLastUsed(result);
        }
        this.data = result;
        this._change(updateType);
      })
      .catch(Cu.reportError);
  }
});
PK
!<�����&modules/syncedtabs/TabListComponent.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");

let log = ChromeUtils.import("resource://gre/modules/Log.jsm", {})
            .Log.repository.getLogger("Sync.RemoteTabs");

XPCOMUtils.defineLazyModuleGetters(this, {
  BrowserUITelemetry: "resource:///modules/BrowserUITelemetry.jsm",
  OpenInTabsUtils: "resource:///modules/OpenInTabsUtils.jsm",
});

var EXPORTED_SYMBOLS = [
  "TabListComponent"
];

/**
 * TabListComponent
 *
 * The purpose of this component is to compose the view, state, and actions.
 * It defines high level actions that act on the state and passes them to the
 * view for it to trigger during user interaction. It also subscribes the view
 * to state changes so it can rerender.
 */

function TabListComponent({window, store, View, SyncedTabs, clipboardHelper,
                           getChromeWindow}) {
  this._window = window;
  this._store = store;
  this._View = View;
  this._clipboardHelper = clipboardHelper;
  this._getChromeWindow = getChromeWindow;
  // used to trigger Sync from context menu
  this._SyncedTabs = SyncedTabs;
}

TabListComponent.prototype = {
  get container() {
    return this._view.container;
  },

  init() {
    log.debug("Initializing TabListComponent");

    this._view = new this._View(this._window, {
      onSelectRow: (...args) => this.onSelectRow(...args),
      onOpenTab: (...args) => this.onOpenTab(...args),
      onOpenTabs: (...args) => this.onOpenTabs(...args),
      onMoveSelectionDown: (...args) => this.onMoveSelectionDown(...args),
      onMoveSelectionUp: (...args) => this.onMoveSelectionUp(...args),
      onToggleBranch: (...args) => this.onToggleBranch(...args),
      onBookmarkTab: (...args) => this.onBookmarkTab(...args),
      onCopyTabLocation: (...args) => this.onCopyTabLocation(...args),
      onSyncRefresh: (...args) => this.onSyncRefresh(...args),
      onFilter: (...args) => this.onFilter(...args),
      onClearFilter: (...args) => this.onClearFilter(...args),
      onFilterFocus: (...args) => this.onFilterFocus(...args),
      onFilterBlur: (...args) => this.onFilterBlur(...args)
    });

    this._store.on("change", state => this._view.render(state));
    this._view.render({clients: []});
    // get what's already available...
    this._store.getData();
    this._store.focusInput();
  },

  uninit() {
    this._view.destroy();
  },

  onFilter(query) {
    this._store.getData(query);
  },

  onClearFilter() {
    this._store.clearFilter();
  },

  onFilterFocus() {
    this._store.focusInput();
  },

  onFilterBlur() {
    this._store.blurInput();
  },

  onSelectRow(position) {
    this._store.selectRow(position[0], position[1]);
  },

  onMoveSelectionDown() {
    this._store.moveSelectionDown();
  },

  onMoveSelectionUp() {
    this._store.moveSelectionUp();
  },

  onToggleBranch(id) {
    this._store.toggleBranch(id);
  },

  onBookmarkTab(uri, title) {
    this._window.top.PlacesCommandHook
      .bookmarkLink(this._window.top.PlacesUtils.bookmarksMenuFolderId, uri, title)
      .catch(Cu.reportError);
  },

  onOpenTab(url, where, params) {
    this._window.openUILinkIn(url, where, params);
    BrowserUITelemetry.countSyncedTabEvent("open", "sidebar");
  },

  onOpenTabs(urls, where) {
    if (!OpenInTabsUtils.confirmOpenInTabs(urls.length, this._window)) {
      return;
    }
    if (where == "window") {
      this._window.openDialog(this._window.getBrowserURL(), "_blank",
                              "chrome,dialog=no,all", urls.join("|"));
    } else {
      let loadInBackground = where == "tabshifted";
      this._getChromeWindow(this._window).gBrowser.loadTabs(urls, {
        inBackground: loadInBackground,
        replace: false,
        triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
      });
    }
    BrowserUITelemetry.countSyncedTabEvent("openmultiple", "sidebar");
  },

  onCopyTabLocation(url) {
    this._clipboardHelper.copyString(url);
  },

  onSyncRefresh() {
    this._SyncedTabs.syncTabs(true);
  }
};
PK
!<r���E�E!modules/syncedtabs/TabListView.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

ChromeUtils.import("resource://gre/modules/Services.jsm");

ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
                               "resource://gre/modules/PrivateBrowsingUtils.jsm");

let { getChromeWindow } = ChromeUtils.import("resource:///modules/syncedtabs/util.js", {});

let log = ChromeUtils.import("resource://gre/modules/Log.jsm", {})
            .Log.repository.getLogger("Sync.RemoteTabs");

var EXPORTED_SYMBOLS = [
  "TabListView"
];

function getContextMenu(window) {
  return getChromeWindow(window).document.getElementById("SyncedTabsSidebarContext");
}

function getTabsFilterContextMenu(window) {
  return getChromeWindow(window).document.getElementById("SyncedTabsSidebarTabsFilterContext");
}

/*
 * TabListView
 *
 * Given a state, this object will render the corresponding DOM.
 * It maintains no state of it's own. It listens for DOM events
 * and triggers actions that may cause the state to change and
 * ultimately the view to rerender.
 */
function TabListView(window, props) {
  this.props = props;

  this._window = window;
  this._doc = this._window.document;

  this._tabsContainerTemplate = this._doc.getElementById("tabs-container-template");
  this._clientTemplate = this._doc.getElementById("client-template");
  this._emptyClientTemplate = this._doc.getElementById("empty-client-template");
  this._tabTemplate = this._doc.getElementById("tab-template");
  this.tabsFilter = this._doc.querySelector(".tabsFilter");
  this.clearFilter = this._doc.querySelector(".textbox-search-clear");
  this.searchBox = this._doc.querySelector(".search-box");

  this.container = this._doc.createElement("div");

  this._attachFixedListeners();

  this._setupContextMenu();
}

TabListView.prototype = {
  render(state) {
    // Don't rerender anything; just update attributes, e.g. selection
    if (state.canUpdateAll) {
      this._update(state);
      return;
    }
    // Rerender the tab list
    if (state.canUpdateInput) {
      this._updateSearchBox(state);
      this._createList(state);
      return;
    }
    // Create the world anew
    this._create(state);
  },

  // Create the initial DOM from templates
  _create(state) {
    let wrapper = this._doc.importNode(this._tabsContainerTemplate.content, true).firstElementChild;
    this._clearChilden();
    this.container.appendChild(wrapper);

    this.list = this.container.querySelector(".list");

    this._createList(state);
    this._updateSearchBox(state);

    this._attachListListeners();
  },

  _createList(state) {
    this._clearChilden(this.list);
    for (let client of state.clients) {
      if (state.filter) {
        this._renderFilteredClient(client);
      } else {
        this._renderClient(client);
      }
    }
    if (this.list.firstChild) {
      const firstTab = this.list.firstChild.querySelector(".item.tab:first-child .item-title");
      if (firstTab) {
        firstTab.setAttribute("tabindex", 2);
      }
    }
  },

  destroy() {
    this._teardownContextMenu();
    this.container.remove();
  },

  _update(state) {
    this._updateSearchBox(state);
    for (let client of state.clients) {
      let clientNode = this._doc.getElementById("item-" + client.id);
      if (clientNode) {
        this._updateClient(client, clientNode);
      }

      client.tabs.forEach((tab, index) => {
        let tabNode = this._doc.getElementById("tab-" + client.id + "-" + index);
        this._updateTab(tab, tabNode, index);
      });
    }
  },

  // Client rows are hidden when the list is filtered
  _renderFilteredClient(client, filter) {
    client.tabs.forEach((tab, index) => {
      let node = this._renderTab(client, tab, index);
      this.list.appendChild(node);
    });
  },

  _renderClient(client) {
    let itemNode = client.tabs.length ?
                    this._createClient(client) :
                    this._createEmptyClient(client);

    this._updateClient(client, itemNode);

    let tabsList = itemNode.querySelector(".item-tabs-list");
    client.tabs.forEach((tab, index) => {
      let node = this._renderTab(client, tab, index);
      tabsList.appendChild(node);
    });

    this.list.appendChild(itemNode);
    return itemNode;
  },

  _renderTab(client, tab, index) {
    let itemNode = this._createTab(tab);
    this._updateTab(tab, itemNode, index);
    return itemNode;
  },

  _createClient(item) {
    return this._doc.importNode(this._clientTemplate.content, true).firstElementChild;
  },

  _createEmptyClient(item) {
    return this._doc.importNode(this._emptyClientTemplate.content, true).firstElementChild;
  },

  _createTab(item) {
    return this._doc.importNode(this._tabTemplate.content, true).firstElementChild;
  },

  _clearChilden(node) {
    let parent = node || this.container;
    while (parent.firstChild) {
      parent.firstChild.remove();
    }
  },

  // These listeners are attached only once, when we initialize the view
  _attachFixedListeners() {
    this.tabsFilter.addEventListener("input", this.onFilter.bind(this));
    this.tabsFilter.addEventListener("focus", this.onFilterFocus.bind(this));
    this.tabsFilter.addEventListener("blur", this.onFilterBlur.bind(this));
    this.clearFilter.addEventListener("click", this.onClearFilter.bind(this));
  },

  // These listeners have to be re-created every time since we re-create the list
  _attachListListeners() {
    this.list.addEventListener("click", this.onClick.bind(this));
    this.list.addEventListener("mouseup", this.onMouseUp.bind(this));
    this.list.addEventListener("keydown", this.onKeyDown.bind(this));
  },

  _updateSearchBox(state) {
    if (state.filter) {
      this.searchBox.classList.add("filtered");
    } else {
      this.searchBox.classList.remove("filtered");
    }
    this.tabsFilter.value = state.filter;
    if (state.inputFocused) {
      this.searchBox.setAttribute("focused", true);
      this.tabsFilter.focus();
    } else {
      this.searchBox.removeAttribute("focused");
    }
  },

  /**
   * Update the element representing an item, ensuring it's in sync with the
   * underlying data.
   * @param {client} item - Item to use as a source.
   * @param {Element} itemNode - Element to update.
   */
  _updateClient(item, itemNode) {
    itemNode.setAttribute("id", "item-" + item.id);
    let lastSync = new Date(item.lastModified);
    let lastSyncTitle = getChromeWindow(this._window).gSync.formatLastSyncDate(lastSync);
    itemNode.setAttribute("title", lastSyncTitle);
    if (item.closed) {
      itemNode.classList.add("closed");
    } else {
      itemNode.classList.remove("closed");
    }
    if (item.selected) {
      itemNode.classList.add("selected");
    } else {
      itemNode.classList.remove("selected");
    }
    if (item.isMobile) {
      itemNode.classList.add("device-image-mobile");
    } else {
      itemNode.classList.add("device-image-desktop");
    }
    if (item.focused) {
      itemNode.focus();
    }
    itemNode.dataset.id = item.id;
    itemNode.querySelector(".item-title").textContent = item.name;
  },

  /**
   * Update the element representing a tab, ensuring it's in sync with the
   * underlying data.
   * @param {tab} item - Item to use as a source.
   * @param {Element} itemNode - Element to update.
   */
  _updateTab(item, itemNode, index) {
    itemNode.setAttribute("title", `${item.title}\n${item.url}`);
    itemNode.setAttribute("id", "tab-" + item.client + "-" + index);
    if (item.selected) {
      itemNode.classList.add("selected");
    } else {
      itemNode.classList.remove("selected");
    }
    if (item.focused) {
      itemNode.focus();
    }
    itemNode.dataset.url = item.url;

    itemNode.querySelector(".item-title").textContent = item.title;

    if (item.icon) {
      let icon = itemNode.querySelector(".item-icon-container");
      icon.style.backgroundImage = "url(" + item.icon + ")";
    }
  },

  onMouseUp(event) {
    if (event.which == 2) { // Middle click
      this.onClick(event);
    }
  },

  onClick(event) {
    let itemNode = this._findParentItemNode(event.target);
    if (!itemNode) {
      return;
    }

    if (itemNode.classList.contains("tab")) {
      let url = itemNode.dataset.url;
      if (url) {
        this.onOpenSelected(url, event);
      }
    }

    // Middle click on a client
    if (itemNode.classList.contains("client")) {
      let where = getChromeWindow(this._window).whereToOpenLink(event);
      if (where != "current") {
        this._openAllClientTabs(itemNode, where);
      }
    }

    if (event.target.classList.contains("item-twisty-container")
        && event.which != 2) {
      this.props.onToggleBranch(itemNode.dataset.id);
      return;
    }

    let position = this._getSelectionPosition(itemNode);
    this.props.onSelectRow(position);
  },

  /**
   * Handle a keydown event on the list box.
   * @param {Event} event - Triggering event.
   */
  onKeyDown(event) {
    if (event.keyCode == this._window.KeyEvent.DOM_VK_DOWN) {
      event.preventDefault();
      this.props.onMoveSelectionDown();
    } else if (event.keyCode == this._window.KeyEvent.DOM_VK_UP) {
      event.preventDefault();
      this.props.onMoveSelectionUp();
    } else if (event.keyCode == this._window.KeyEvent.DOM_VK_RETURN) {
      let selectedNode = this.container.querySelector(".item.selected");
      if (selectedNode.dataset.url) {
        this.onOpenSelected(selectedNode.dataset.url, event);
      } else if (selectedNode) {
        this.props.onToggleBranch(selectedNode.dataset.id);
      }
    }
  },

  onBookmarkTab() {
    let item = this._getSelectedTabNode();
    if (item) {
      let title = item.querySelector(".item-title").textContent;
      this.props.onBookmarkTab(item.dataset.url, title);
    }
  },

  onCopyTabLocation() {
    let item = this._getSelectedTabNode();
    if (item) {
      this.props.onCopyTabLocation(item.dataset.url);
    }
  },

  onOpenSelected(url, event) {
    let where = getChromeWindow(this._window).whereToOpenLink(event);
    this.props.onOpenTab(url, where, {});
  },

  onOpenSelectedFromContextMenu(event) {
    let item = this._getSelectedTabNode();
    if (item) {
      let where = event.target.getAttribute("where");
      let params = {
        private: event.target.hasAttribute("private"),
      };
      this.props.onOpenTab(item.dataset.url, where, params);
    }
  },

  onOpenAllInTabs() {
    let item = this._getSelectedClientNode();
    if (item) {
      this._openAllClientTabs(item, "tab");
    }
  },

  onFilter(event) {
    let query = event.target.value;
    if (query) {
      this.props.onFilter(query);
    } else {
      this.props.onClearFilter();
    }
  },

  onClearFilter() {
    this.props.onClearFilter();
  },

  onFilterFocus() {
    this.props.onFilterFocus();
  },
  onFilterBlur() {
    this.props.onFilterBlur();
  },

  _getSelectedTabNode() {
    let item = this.container.querySelector(".item.selected");
    if (this._isTab(item) && item.dataset.url) {
      return item;
    }
    return null;
  },

  _getSelectedClientNode() {
    let item = this.container.querySelector(".item.selected");
    if (this._isClient(item)) {
      return item;
    }
    return null;
  },

  // Set up the custom context menu
  _setupContextMenu() {
    Services.els.addSystemEventListener(this._window, "contextmenu", this, false);
    for (let getMenu of [getContextMenu, getTabsFilterContextMenu]) {
      let menu = getMenu(this._window);
      menu.addEventListener("popupshowing", this, true);
      menu.addEventListener("command", this, true);
    }
  },

  _teardownContextMenu() {
    // Tear down context menu
    Services.els.removeSystemEventListener(this._window, "contextmenu", this, false);
    for (let getMenu of [getContextMenu, getTabsFilterContextMenu]) {
      let menu = getMenu(this._window);
      menu.removeEventListener("popupshowing", this, true);
      menu.removeEventListener("command", this, true);
    }
  },

  handleEvent(event) {
    switch (event.type) {
      case "contextmenu":
        this.handleContextMenu(event);
        break;

      case "popupshowing": {
        if (event.target.getAttribute("id") == "SyncedTabsSidebarTabsFilterContext") {
          this.handleTabsFilterContextMenuShown(event);
        }
        break;
      }

      case "command": {
        let menu = event.target.closest("menupopup");
        switch (menu.getAttribute("id")) {
          case "SyncedTabsSidebarContext":
            this.handleContentContextMenuCommand(event);
            break;

          case "SyncedTabsSidebarTabsFilterContext":
            this.handleTabsFilterContextMenuCommand(event);
            break;
        }
        break;
      }
    }
  },

  handleTabsFilterContextMenuShown(event) {
    let document = event.target.ownerDocument;
    let focusedElement = document.commandDispatcher.focusedElement;
    if (focusedElement != this.tabsFilter) {
      this.tabsFilter.focus();
    }
    for (let item of event.target.children) {
      if (!item.hasAttribute("cmd")) {
        continue;
      }
      let command = item.getAttribute("cmd");
      let controller = document.commandDispatcher.getControllerForCommand(command);
      if (controller.isCommandEnabled(command)) {
        item.removeAttribute("disabled");
      } else {
        item.setAttribute("disabled", "true");
      }
    }
  },

  handleContentContextMenuCommand(event) {
    let id = event.target.getAttribute("id");
    switch (id) {
      case "syncedTabsOpenSelected":
      case "syncedTabsOpenSelectedInTab":
      case "syncedTabsOpenSelectedInWindow":
      case "syncedTabsOpenSelectedInPrivateWindow":
        this.onOpenSelectedFromContextMenu(event);
        break;
      case "syncedTabsOpenAllInTabs":
        this.onOpenAllInTabs();
        break;
      case "syncedTabsBookmarkSelected":
        this.onBookmarkTab();
        break;
      case "syncedTabsCopySelected":
        this.onCopyTabLocation();
        break;
      case "syncedTabsRefresh":
      case "syncedTabsRefreshFilter":
        this.props.onSyncRefresh();
        break;
    }
  },

  handleTabsFilterContextMenuCommand(event) {
    let command = event.target.getAttribute("cmd");
    let dispatcher = getChromeWindow(this._window).document.commandDispatcher;
    let controller = dispatcher.focusedElement.controllers.getControllerForCommand(command);
    controller.doCommand(command);
  },

  handleContextMenu(event) {
    let menu;

    if (event.target == this.tabsFilter) {
      menu = getTabsFilterContextMenu(this._window);
    } else {
      let itemNode = this._findParentItemNode(event.target);
      if (itemNode) {
        let position = this._getSelectionPosition(itemNode);
        this.props.onSelectRow(position);
      }
      menu = getContextMenu(this._window);
      this.adjustContextMenu(menu);
    }

    menu.openPopupAtScreen(event.screenX, event.screenY, true, event);
  },

  adjustContextMenu(menu) {
    let item = this.container.querySelector(".item.selected");
    let showTabOptions = this._isTab(item);

    let el = menu.firstChild;

    while (el) {
      let show = false;
      if (showTabOptions) {
        if (el.getAttribute("id") == "syncedTabsOpenSelectedInPrivateWindow") {
          show = PrivateBrowsingUtils.enabled;
        } else if (el.getAttribute("id") != "syncedTabsOpenAllInTabs" &&
                   el.getAttribute("id") != "syncedTabsManageDevices") {
          show = true;
        }
      } else if (el.getAttribute("id") == "syncedTabsOpenAllInTabs") {
        const tabs = item.querySelectorAll(".item-tabs-list > .item.tab");
        show = tabs.length > 0;
      } else if (el.getAttribute("id") == "syncedTabsRefresh") {
        show = true;
      } else if (el.getAttribute("id") == "syncedTabsManageDevices") {
        show = true;
      }
      el.hidden = !show;

      el = el.nextSibling;
    }
  },

  /**
   * Find the parent item element, from a given child element.
   * @param {Element} node - Child element.
   * @return {Element} Element for the item, or null if not found.
   */
  _findParentItemNode(node) {
    while (node && node !== this.list && node !== this._doc.documentElement &&
           !node.classList.contains("item")) {
      node = node.parentNode;
    }

    if (node !== this.list && node !== this._doc.documentElement) {
      return node;
    }

    return null;
  },

  _findParentBranchNode(node) {
    while (node && !node.classList.contains("list") && node !== this._doc.documentElement &&
           !node.parentNode.classList.contains("list")) {
      node = node.parentNode;
    }

    if (node !== this.list && node !== this._doc.documentElement) {
      return node;
    }

    return null;
  },

  _getSelectionPosition(itemNode) {
    let parent = this._findParentBranchNode(itemNode);
    let parentPosition = this._indexOfNode(parent.parentNode, parent);
    let childPosition = -1;
    // if the node is not a client, find its position within the parent
    if (parent !== itemNode) {
      childPosition = this._indexOfNode(itemNode.parentNode, itemNode);
    }
    return [parentPosition, childPosition];
  },

  _indexOfNode(parent, child) {
    return Array.prototype.indexOf.call(parent.childNodes, child);
  },

  _isTab(item) {
    return item && item.classList.contains("tab");
  },

  _isClient(item) {
    return item && item.classList.contains("client");
  },

  _openAllClientTabs(clientNode, where) {
    const tabs = clientNode.querySelector(".item-tabs-list").childNodes;
    const urls = [...tabs].map(tab => tab.dataset.url);
    this.props.onOpenTabs(urls, where);
  }
};
PK
!<!��modules/syncedtabs/util.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = [
  "getChromeWindow"
];

// Get the chrome (ie, browser) window hosting this content.
function getChromeWindow(window) {
  return window
         .QueryInterface(Ci.nsIInterfaceRequestor)
         .getInterface(Ci.nsIWebNavigation)
         .QueryInterface(Ci.nsIDocShellTreeItem)
         .rootTreeItem
         .QueryInterface(Ci.nsIInterfaceRequestor)
         .getInterface(Ci.nsIDOMWindow)
         .wrappedJSObject;
}
PK
!<h�ˎu:u:&modules/translation/BingTranslator.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = [ "BingTranslator" ];

ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/Log.jsm");
ChromeUtils.import("resource://gre/modules/PromiseUtils.jsm");
ChromeUtils.import("resource://services-common/async.js");
ChromeUtils.import("resource://gre/modules/Http.jsm");

Cu.importGlobalProperties(["XMLHttpRequest"]);

// The maximum amount of net data allowed per request on Bing's API.
const MAX_REQUEST_DATA = 5000; // Documentation says 10000 but anywhere
                               // close to that is refused by the service.

// The maximum number of chunks allowed to be translated in a single
// request.
const MAX_REQUEST_CHUNKS = 1000; // Documentation says 2000.

// Self-imposed limit of 15 requests. This means that a page that would need
// to be broken in more than 15 requests won't be fully translated.
// The maximum amount of data that we will translate for a single page
// is MAX_REQUESTS * MAX_REQUEST_DATA.
const MAX_REQUESTS = 15;

/**
 * Translates a webpage using Bing's Translation API.
 *
 * @param translationDocument  The TranslationDocument object that represents
 *                             the webpage to be translated
 * @param sourceLanguage       The source language of the document
 * @param targetLanguage       The target language for the translation
 *
 * @returns {Promise}          A promise that will resolve when the translation
 *                             task is finished.
 */
var BingTranslator = function(translationDocument, sourceLanguage, targetLanguage) {
  this.translationDocument = translationDocument;
  this.sourceLanguage = sourceLanguage;
  this.targetLanguage = targetLanguage;
  this._pendingRequests = 0;
  this._partialSuccess = false;
  this._serviceUnavailable = false;
  this._translatedCharacterCount = 0;
};

this.BingTranslator.prototype = {
  /**
   * Performs the translation, splitting the document into several chunks
   * respecting the data limits of the API.
   *
   * @returns {Promise}          A promise that will resolve when the translation
   *                             task is finished.
   */
  translate() {
    return (async () => {
      let currentIndex = 0;
      this._onFinishedDeferred = PromiseUtils.defer();

      // Let's split the document into various requests to be sent to
      // Bing's Translation API.
      for (let requestCount = 0; requestCount < MAX_REQUESTS; requestCount++) {
        // Generating the text for each request can be expensive, so
        // let's take the opportunity of the chunkification process to
        // allow for the event loop to attend other pending events
        // before we continue.
        await Async.promiseYield();

        // Determine the data for the next request.
        let request = this._generateNextTranslationRequest(currentIndex);

        // Create a real request to the server, and put it on the
        // pending requests list.
        let bingRequest = new BingRequest(request.data,
                                          this.sourceLanguage,
                                          this.targetLanguage);
        this._pendingRequests++;
        bingRequest.fireRequest().then(this._chunkCompleted.bind(this),
                                       this._chunkFailed.bind(this));

        currentIndex = request.lastIndex;
        if (request.finished) {
          break;
        }
      }

      return this._onFinishedDeferred.promise;
    })();
  },

  /**
   * Resets the expiration time of the current token, in order to
   * force the token manager to ask for a new token during the next request.
   */
  _resetToken() {
    // Force the token manager to get update token
    BingTokenManager._currentExpiryTime = 0;
  },

  /**
   * Function called when a request sent to the server completed successfully.
   * This function handles calling the function to parse the result and the
   * function to resolve the promise returned by the public `translate()`
   * method when there's no pending request left.
   *
   * @param   request   The BingRequest sent to the server.
   */
  _chunkCompleted(bingRequest) {
    if (this._parseChunkResult(bingRequest)) {
      this._partialSuccess = true;
      // Count the number of characters successfully translated.
      this._translatedCharacterCount += bingRequest.characterCount;
    }

    this._checkIfFinished();
  },

  /**
   * Function called when a request sent to the server has failed.
   * This function handles deciding if the error is transient or means the
   * service is unavailable (zero balance on the key or request credentials are
   * not in an active state) and calling the function to resolve the promise
   * returned by the public `translate()` method when there's no pending.
   * request left.
   *
   * @param   aError   [optional] The XHR object of the request that failed.
   */
  _chunkFailed(aError) {
    if (aError instanceof XMLHttpRequest &&
        [400, 401].includes(aError.status)) {
      let body = aError.responseText;
      if (body && body.includes("TranslateApiException") &&
          (body.includes("balance") || body.includes("active state")))
        this._serviceUnavailable = true;
    }

    this._checkIfFinished();
  },

  /**
   * Function called when a request sent to the server has completed.
   * This function handles resolving the promise
   * returned by the public `translate()` method when all chunks are completed.
   */
  _checkIfFinished() {
    // Check if all pending requests have been
    // completed and then resolves the promise.
    // If at least one chunk was successful, the
    // promise will be resolved positively which will
    // display the "Success" state for the infobar. Otherwise,
    // the "Error" state will appear.
    if (--this._pendingRequests == 0) {
      if (this._partialSuccess) {
        this._onFinishedDeferred.resolve({
          characterCount: this._translatedCharacterCount
        });
      } else {
        let error = this._serviceUnavailable ? "unavailable" : "failure";
        this._onFinishedDeferred.reject(error);
      }
    }
  },

  /**
   * This function parses the result returned by Bing's Http.svc API,
   * which is a XML file that contains a number of elements. To our
   * particular interest, the only part of the response that matters
   * are the <TranslatedText> nodes, which contains the resulting
   * items that were sent to be translated.
   *
   * @param   request      The request sent to the server.
   * @returns boolean      True if parsing of this chunk was successful.
   */
  _parseChunkResult(bingRequest) {
    let results;
    try {
      let doc = bingRequest.networkRequest.responseXML;
      results = doc.querySelectorAll("TranslatedText");
    } catch (e) {
      return false;
    }

    let len = results.length;
    if (len != bingRequest.translationData.length) {
      // This should never happen, but if the service returns a different number
      // of items (from the number of items submitted), we can't use this chunk
      // because all items would be paired incorrectly.
      return false;
    }

    let error = false;
    for (let i = 0; i < len; i++) {
      try {
        let result = results[i].firstChild.nodeValue;
        let root = bingRequest.translationData[i][0];

        if (root.isSimpleRoot) {
          // Workaround for Bing's service problem in which "&" chars in
          // plain-text TranslationItems are double-escaped.
          result = result.replace(/&amp;/g, "&");
        }

        root.parseResult(result);
      } catch (e) { error = true; }
    }

    return !error;
  },

  /**
   * This function will determine what is the data to be used for
   * the Nth request we are generating, based on the input params.
   *
   * @param startIndex What is the index, in the roots list, that the
   *                   chunk should start.
   */
  _generateNextTranslationRequest(startIndex) {
    let currentDataSize = 0;
    let currentChunks = 0;
    let output = [];
    let rootsList = this.translationDocument.roots;

    for (let i = startIndex; i < rootsList.length; i++) {
      let root = rootsList[i];
      let text = this.translationDocument.generateTextForItem(root);
      if (!text) {
        continue;
      }

      text = escapeXML(text);
      let newCurSize = currentDataSize + text.length;
      let newChunks = currentChunks + 1;

      if (newCurSize > MAX_REQUEST_DATA ||
          newChunks > MAX_REQUEST_CHUNKS) {

        // If we've reached the API limits, let's stop accumulating data
        // for this request and return. We return information useful for
        // the caller to pass back on the next call, so that the function
        // can keep working from where it stopped.
        return {
          data: output,
          finished: false,
          lastIndex: i
        };
      }

      currentDataSize = newCurSize;
      currentChunks = newChunks;
      output.push([root, text]);
    }

    return {
      data: output,
      finished: true,
      lastIndex: 0
    };
  }
};

/**
 * Represents a request (for 1 chunk) sent off to Bing's service.
 *
 * @params translationData  The data to be used for this translation,
 *                          generated by the generateNextTranslationRequest...
 *                          function.
 * @param sourceLanguage    The source language of the document.
 * @param targetLanguage    The target language for the translation.
 *
 */
function BingRequest(translationData, sourceLanguage, targetLanguage) {
  this.translationData = translationData;
  this.sourceLanguage = sourceLanguage;
  this.targetLanguage = targetLanguage;
  this.characterCount = 0;
}

BingRequest.prototype = {
  /**
   * Initiates the request
   */
  fireRequest() {
    return (async () => {
      // Prepare authentication.
      let token = await BingTokenManager.getToken();
      let auth = "Bearer " + token;

      // Prepare URL.
      let url = getUrlParam("https://api.microsofttranslator.com/v2/Http.svc/TranslateArray",
                            "browser.translation.bing.translateArrayURL");

      // Prepare request headers.
      let headers = [["Content-type", "text/xml"], ["Authorization", auth]];

      // Prepare the request body.
      let requestString =
        "<TranslateArrayRequest>" +
          "<AppId/>" +
          "<From>" + this.sourceLanguage + "</From>" +
          "<Options>" +
            '<ContentType xmlns="http://schemas.datacontract.org/2004/07/Microsoft.MT.Web.Service.V2">text/html</ContentType>' +
            '<ReservedFlags xmlns="http://schemas.datacontract.org/2004/07/Microsoft.MT.Web.Service.V2" />' +
          "</Options>" +
          '<Texts xmlns:s="http://schemas.microsoft.com/2003/10/Serialization/Arrays">';

      for (let [, text] of this.translationData) {
        requestString += "<s:string>" + text + "</s:string>";
        this.characterCount += text.length;
      }

      requestString += "</Texts>" +
          "<To>" + this.targetLanguage + "</To>" +
        "</TranslateArrayRequest>";

      // Set up request options.
      return new Promise((resolve, reject) => {
        let options = {
          onLoad: (responseText, xhr) => {
            resolve(this);
          },
          onError(e, responseText, xhr) {
            reject(xhr);
          },
          postData: requestString,
          headers
        };

        // Fire the request.
        let request = httpRequest(url, options);

        // Override the response MIME type.
        request.overrideMimeType("text/xml");
        this.networkRequest = request;
      });
    })();
  }
};

/**
 * Authentication Token manager for the API
 */
var BingTokenManager = {
  _currentToken: null,
  _currentExpiryTime: 0,
  _pendingRequest: null,

  /**
   * Get a valid, non-expired token to be used for the API calls.
   *
   * @returns {Promise}  A promise that resolves with the token
   *                     string once it is obtained. The token returned
   *                     can be the same one used in the past if it is still
   *                     valid.
   */
  getToken() {
    if (this._pendingRequest) {
      return this._pendingRequest;
    }

    let remainingMs = this._currentExpiryTime - new Date();
    // Our existing token is still good for more than a minute, let's use it.
    if (remainingMs > 60 * 1000) {
      return Promise.resolve(this._currentToken);
    }

    return this._getNewToken();
  },

  /**
   * Generates a new token from the server.
   *
   * @returns {Promise}  A promise that resolves with the token
   *                     string once it is obtained.
   */
  _getNewToken() {
    let url = getUrlParam("https://datamarket.accesscontrol.windows.net/v2/OAuth2-13",
                          "browser.translation.bing.authURL");
    let params = [
      ["grant_type", "client_credentials"],
      ["scope", "http://api.microsofttranslator.com"],
      ["client_id",
      getUrlParam("%BING_API_CLIENTID%", "browser.translation.bing.clientIdOverride")],
      ["client_secret",
      getUrlParam("%BING_API_KEY%", "browser.translation.bing.apiKeyOverride")]
    ];

    this._pendingRequest = new Promise((resolve, reject) => {
      let options = {
        onLoad(responseText, xhr) {
          BingTokenManager._pendingRequest = null;
          try {
            let json = JSON.parse(responseText);

            if (json.error) {
              reject(json.error);
              return;
            }

            let token = json.access_token;
            let expires_in = json.expires_in;
            BingTokenManager._currentToken = token;
            BingTokenManager._currentExpiryTime = new Date(Date.now() + expires_in * 1000);
            resolve(token);
          } catch (e) {
            reject(e);
          }
        },
        onError(e, responseText, xhr) {
          BingTokenManager._pendingRequest = null;
          reject(e);
        },
        postData: params
      };

      httpRequest(url, options);
    });
    return this._pendingRequest;
  }
};

/**
 * Escape a string to be valid XML content.
 */
function escapeXML(aStr) {
  return aStr.toString()
             .replace(/&/g, "&amp;")
             .replace(/\"/g, "&quot;")
             .replace(/\'/g, "&apos;")
             .replace(/</g, "&lt;")
             .replace(/>/g, "&gt;");
}

/**
 * Fetch an auth token (clientID or client secret), which may be overridden by
 * a pref if it's set.
 */
function getUrlParam(paramValue, prefName) {
  if (Services.prefs.getPrefType(prefName))
    paramValue = Services.prefs.getCharPref(prefName);
  paramValue = Services.urlFormatter.formatURL(paramValue);
  return paramValue;
}
PK
!<��VV(modules/translation/LanguageDetector.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = ["LanguageDetector"];

ChromeUtils.import("resource://gre/modules/Timer.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");

// Since Emscripten can handle heap growth, but not heap shrinkage, we
// need to refresh the worker after we've processed a particularly large
// string in order to prevent unnecessary resident memory growth.
//
// These values define the cut-off string length and the idle timeout
// (in milliseconds) before destroying a worker. Once a string of the
// maximum size has been processed, the worker is marked for
// destruction, and is terminated as soon as it has been idle for the
// given timeout.
//
// 1.5MB. This is the approximate string length that forces heap growth
// for a 2MB heap.
var LARGE_STRING = 1.5 * 1024 * 1024;
var IDLE_TIMEOUT = 10 * 1000;

const WORKER_URL = "resource:///modules/translation/cld-worker.js";

var workerManager = {
  detectionQueue: [],

  detectLanguage(aParams) {
    return this.workerReady.then(worker => {
      return new Promise(resolve => {
        this.detectionQueue.push({resolve});
        worker.postMessage(aParams);
      });
    }).then(result => {
      // We have our asynchronous result from the worker.
      //
      // Determine if our input was large enough to trigger heap growth,
      // or if we're already waiting to destroy the worker when it's
      // idle. If so, schedule termination after the idle timeout.
      if (aParams.text.length >= LARGE_STRING || this._idleTimeout != null)
        this.flushWorker();

      return result;
    });
  },

  _worker: null,
  _workerReadyPromise: null,

  get workerReady() {
    if (!this._workerReadyPromise)
      this._workerReadyPromise = new Promise(resolve => {
        let worker = new Worker(WORKER_URL);
        worker.onmessage = (aMsg) => {
          if (aMsg.data == "ready")
            resolve(worker);
          else
            this.detectionQueue.shift().resolve(aMsg.data);
        };
        this._worker = worker;
      });

    return this._workerReadyPromise;
  },

  // Holds the ID of the current pending idle cleanup setTimeout.
  _idleTimeout: null,

  // Schedule the current worker to be terminated after the idle timeout.
  flushWorker() {
    if (this._idleTimeout != null)
      clearTimeout(this._idleTimeout);

    this._idleTimeout = setTimeout(this._flushWorker.bind(this), IDLE_TIMEOUT);
  },

  // Immediately terminate the worker, as long as there no pending
  // results. Otherwise, reschedule termination until after the next
  // idle timeout.
  _flushWorker() {
    if (this.detectionQueue.length)
      this.flushWorker();
    else {
      if (this._worker)
        this._worker.terminate();

      this._worker = null;
      this._workerReadyPromise = null;
      this._idleTimeout = null;
    }
  },
};

var LanguageDetector = {
  /**
   * Detect the language of a given string.
   *
   * The argument may be either a string containing the text to analyze,
   * or an object with the following properties:
   *
   *  - 'text' The text to analyze.
   *
   *  - 'isHTML' (optional) A boolean, indicating whether the text
   *      should be analyzed as HTML rather than plain text.
   *
   *  - 'language' (optional) A string indicating the expected language.
   *      For text extracted from HTTP documents, this is expected to
   *      come from the Content-Language header.
   *
   *  - 'tld' (optional) A string indicating the top-level domain of the
   *      document the text was extracted from.
   *
   *  - 'encoding' (optional) A string describing the encoding of the
   *      document the string was extracted from. Note that, regardless
   *      of the value of this property, the 'text' property must be a
   *      UTF-16 JavaScript string.
   *
   * @returns {Promise<Object>}
   * @resolves When detection is finished, with a object containing
   * these fields:
   *  - 'language' (string with a language code)
   *  - 'confident' (boolean) Whether the detector is confident of the
   *      result.
   *  - 'languages' (array) An array of up to three elements, containing
   *      the most prevalent languages detected. It contains a
   *      'languageCode' property, containing the ISO language code of
   *      the language, and a 'percent' property, describing the
   *      approximate percentage of the input which is in that language.
   *      For text of an unknown language, the result may contain an
   *      entry with the languge code 'un', indicating the percent of
   *      the text which is unknown.
   */
  detectLanguage(aParams) {
    if (typeof aParams == "string")
      aParams = { text: aParams };

    return workerManager.detectLanguage(aParams);
  },
};
PK
!<�uN�h7h7#modules/translation/Translation.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = [
  "Translation",
  "TranslationTelemetry",
];

const TRANSLATION_PREF_SHOWUI = "browser.translation.ui.show";
const TRANSLATION_PREF_DETECT_LANG = "browser.translation.detectLanguage";

ChromeUtils.import("resource://gre/modules/Services.jsm");

var Translation = {
  STATE_OFFER: 0,
  STATE_TRANSLATING: 1,
  STATE_TRANSLATED: 2,
  STATE_ERROR: 3,
  STATE_UNAVAILABLE: 4,

  serviceUnavailable: false,

  supportedSourceLanguages: ["bg", "cs", "de", "en", "es", "fr", "ja", "ko", "nl", "no", "pl", "pt", "ru", "tr", "vi", "zh"],
  supportedTargetLanguages: ["bg", "cs", "de", "en", "es", "fr", "ja", "ko", "nl", "no", "pl", "pt", "ru", "tr", "vi", "zh"],

  _defaultTargetLanguage: "",
  get defaultTargetLanguage() {
    if (!this._defaultTargetLanguage) {
      this._defaultTargetLanguage = Services.locale.getAppLocaleAsLangTag()
                                      .split("-")[0];
    }
    return this._defaultTargetLanguage;
  },

  documentStateReceived(aBrowser, aData) {
    if (aData.state == this.STATE_OFFER) {
      if (aData.detectedLanguage == this.defaultTargetLanguage) {
        // Detected language is the same as the user's locale.
        return;
      }

      if (!this.supportedSourceLanguages.includes(aData.detectedLanguage)) {
        // Detected language is not part of the supported languages.
        TranslationTelemetry.recordMissedTranslationOpportunity(aData.detectedLanguage);
        return;
      }

      TranslationTelemetry.recordTranslationOpportunity(aData.detectedLanguage);
    }

    if (!Services.prefs.getBoolPref(TRANSLATION_PREF_SHOWUI))
      return;

    if (!aBrowser.translationUI)
      aBrowser.translationUI = new TranslationUI(aBrowser);
    let trUI = aBrowser.translationUI;

    // Set all values before showing a new translation infobar.
    trUI._state = Translation.serviceUnavailable ? Translation.STATE_UNAVAILABLE
                                                 : aData.state;
    trUI.detectedLanguage = aData.detectedLanguage;
    trUI.translatedFrom = aData.translatedFrom;
    trUI.translatedTo = aData.translatedTo;
    trUI.originalShown = aData.originalShown;

    trUI.showURLBarIcon();

    if (trUI.shouldShowInfoBar(aBrowser.currentURI))
      trUI.showTranslationInfoBar();
  },

  openProviderAttribution() {
    let attribution = this.supportedEngines[this.translationEngine];
    ChromeUtils.import("resource:///modules/RecentWindow.jsm");
    RecentWindow.getMostRecentBrowserWindow().openUILinkIn(attribution, "tab");
  },

  /**
   * The list of translation engines and their attributions.
   */
  supportedEngines: {
    "bing": "http://aka.ms/MicrosoftTranslatorAttribution",
    "yandex": "http://translate.yandex.com/"
  },

  /**
   * Fallback engine (currently Bing Translator) if the preferences seem
   * confusing.
   */
  get defaultEngine() {
    return this.supportedEngines.keys[0];
  },

  /**
   * Returns the name of the preferred translation engine.
   */
  get translationEngine() {
    let engine = Services.prefs.getCharPref("browser.translation.engine");
    return !Object.keys(this.supportedEngines).includes(engine) ? this.defaultEngine : engine;
  },
};

/* TranslationUI objects keep the information related to translation for
 * a specific browser.  This object is passed to the translation
 * infobar so that it can initialize itself.  The properties exposed to
 * the infobar are:
 * - detectedLanguage, code of the language detected on the web page.
 * - state, the state in which the infobar should be displayed
 * - translatedFrom, if already translated, source language code.
 * - translatedTo, if already translated, target language code.
 * - translate, method starting the translation of the current page.
 * - showOriginalContent, method showing the original page content.
 * - showTranslatedContent, method showing the translation for an
 *   already translated page whose original content is shown.
 * - originalShown, boolean indicating if the original or translated
 *   version of the page is shown.
 */
function TranslationUI(aBrowser) {
  this.browser = aBrowser;
}

TranslationUI.prototype = {
  get browser() {
    return this._browser;
  },
  set browser(aBrowser) {
    if (this._browser)
      this._browser.messageManager.removeMessageListener("Translation:Finished", this);
    aBrowser.messageManager.addMessageListener("Translation:Finished", this);
    this._browser = aBrowser;
  },
  translate(aFrom, aTo) {
    if (aFrom == aTo ||
        (this.state == Translation.STATE_TRANSLATED &&
         this.translatedFrom == aFrom && this.translatedTo == aTo)) {
      // Nothing to do.
      return;
    }

    if (this.state == Translation.STATE_OFFER) {
      if (this.detectedLanguage != aFrom)
        TranslationTelemetry.recordDetectedLanguageChange(true);
    } else {
      if (this.translatedFrom != aFrom)
        TranslationTelemetry.recordDetectedLanguageChange(false);
      if (this.translatedTo != aTo)
        TranslationTelemetry.recordTargetLanguageChange();
    }

    this.state = Translation.STATE_TRANSLATING;
    this.translatedFrom = aFrom;
    this.translatedTo = aTo;

    this.browser.messageManager.sendAsyncMessage(
      "Translation:TranslateDocument",
      { from: aFrom, to: aTo }
    );
  },

  showURLBarIcon() {
    let chromeWin = this.browser.ownerGlobal;
    let PopupNotifications = chromeWin.PopupNotifications;
    let removeId = this.originalShown ? "translated" : "translate";
    let notification =
      PopupNotifications.getNotification(removeId, this.browser);
    if (notification)
      PopupNotifications.remove(notification);

    let callback = (aTopic, aNewBrowser) => {
      if (aTopic == "swapping") {
        let infoBarVisible =
          this.notificationBox.getNotificationWithValue("translation");
        aNewBrowser.translationUI = this;
        this.browser = aNewBrowser;
        if (infoBarVisible)
          this.showTranslationInfoBar();
        return true;
      }

      if (aTopic != "showing")
        return false;
      let translationNotification = this.notificationBox.getNotificationWithValue("translation");
      if (translationNotification)
        translationNotification.close();
      else
        this.showTranslationInfoBar();
      return true;
    };

    let addId = this.originalShown ? "translate" : "translated";
    PopupNotifications.show(this.browser, addId, null,
                            addId + "-notification-icon", null, null,
                            {dismissed: true, eventCallback: callback});
  },

  _state: 0,
  get state() {
    return this._state;
  },
  set state(val) {
    let notif = this.notificationBox.getNotificationWithValue("translation");
    if (notif)
      notif.state = val;
    this._state = val;
  },

  originalShown: true,
  showOriginalContent() {
    this.originalShown = true;
    this.showURLBarIcon();
    this.browser.messageManager.sendAsyncMessage("Translation:ShowOriginal");
    TranslationTelemetry.recordShowOriginalContent();
  },

  showTranslatedContent() {
    this.originalShown = false;
    this.showURLBarIcon();
    this.browser.messageManager.sendAsyncMessage("Translation:ShowTranslation");
  },

  get notificationBox() {
    return this.browser.ownerGlobal.gBrowser.getNotificationBox(this.browser);
  },

  showTranslationInfoBar() {
    let notificationBox = this.notificationBox;
    let notif = notificationBox.appendNotification("", "translation", null,
                                                   notificationBox.PRIORITY_INFO_HIGH);
    notif.init(this);
    return notif;
  },

  shouldShowInfoBar(aURI) {
    // Never show the infobar automatically while the translation
    // service is temporarily unavailable.
    if (Translation.serviceUnavailable)
      return false;

    // Check if we should never show the infobar for this language.
    let neverForLangs =
      Services.prefs.getCharPref("browser.translation.neverForLanguages");
    if (neverForLangs.split(",").includes(this.detectedLanguage)) {
      TranslationTelemetry.recordAutoRejectedTranslationOffer();
      return false;
    }

    // or if we should never show the infobar for this domain.
    let perms = Services.perms;
    if (perms.testExactPermission(aURI, "translate") == perms.DENY_ACTION) {
      TranslationTelemetry.recordAutoRejectedTranslationOffer();
      return false;
    }

    return true;
  },

  receiveMessage(msg) {
    switch (msg.name) {
      case "Translation:Finished":
        if (msg.data.success) {
          this.originalShown = false;
          this.state = Translation.STATE_TRANSLATED;
          this.showURLBarIcon();

          // Record the number of characters translated.
          TranslationTelemetry.recordTranslation(msg.data.from, msg.data.to,
                                                    msg.data.characterCount);
        } else if (msg.data.unavailable) {
          Translation.serviceUnavailable = true;
          this.state = Translation.STATE_UNAVAILABLE;
        } else {
          this.state = Translation.STATE_ERROR;
        }
        break;
    }
  },

  infobarClosed() {
    if (this.state == Translation.STATE_OFFER)
      TranslationTelemetry.recordDeniedTranslationOffer();
  }
};

/**
 * Uses telemetry histograms for collecting statistics on the usage of the
 * translation component.
 *
 * NOTE: Metrics are only recorded if the user enabled the telemetry option.
 */
var TranslationTelemetry = {

  init() {
    // Constructing histograms.
    const plain = (id) => Services.telemetry.getHistogramById(id);
    const keyed = (id) => Services.telemetry.getKeyedHistogramById(id);
    this.HISTOGRAMS = {
      OPPORTUNITIES: () => plain("TRANSLATION_OPPORTUNITIES"),
      OPPORTUNITIES_BY_LANG: () => keyed("TRANSLATION_OPPORTUNITIES_BY_LANGUAGE"),
      PAGES: () => plain("TRANSLATED_PAGES"),
      PAGES_BY_LANG: () => keyed("TRANSLATED_PAGES_BY_LANGUAGE"),
      CHARACTERS: () => plain("TRANSLATED_CHARACTERS"),
      DENIED: () => plain("DENIED_TRANSLATION_OFFERS"),
      AUTO_REJECTED: () => plain("AUTO_REJECTED_TRANSLATION_OFFERS"),
      SHOW_ORIGINAL: () => plain("REQUESTS_OF_ORIGINAL_CONTENT"),
      TARGET_CHANGES: () => plain("CHANGES_OF_TARGET_LANGUAGE"),
      DETECTION_CHANGES: () => plain("CHANGES_OF_DETECTED_LANGUAGE"),
      SHOW_UI: () => plain("SHOULD_TRANSLATION_UI_APPEAR"),
      DETECT_LANG: () => plain("SHOULD_AUTO_DETECT_LANGUAGE"),
    };

    // Capturing the values of flags at the startup.
    this.recordPreferences();
  },

  /**
   * Record a translation opportunity in the health report.
   * @param language
   *        The language of the page.
   */
  recordTranslationOpportunity(language) {
    return this._recordOpportunity(language, true);
  },

  /**
   * Record a missed translation opportunity in the health report.
   * A missed opportunity is when the language detected is not part
   * of the supported languages.
   * @param language
   *        The language of the page.
   */
  recordMissedTranslationOpportunity(language) {
    return this._recordOpportunity(language, false);
  },

  /**
   * Record an automatically rejected translation offer in the health
   * report. A translation offer is automatically rejected when a user
   * has previously clicked "Never translate this language" or "Never
   * translate this site", which results in the infobar not being shown for
   * the translation opportunity.
   *
   * These translation opportunities should still be recorded in addition to
   * recording the automatic rejection of the offer.
   */
  recordAutoRejectedTranslationOffer() {
    this.HISTOGRAMS.AUTO_REJECTED().add();
  },

   /**
   * Record a translation in the health report.
   * @param langFrom
   *        The language of the page.
   * @param langTo
   *        The language translated to
   * @param numCharacters
   *        The number of characters that were translated
   */
  recordTranslation(langFrom, langTo, numCharacters) {
    this.HISTOGRAMS.PAGES().add();
    this.HISTOGRAMS.PAGES_BY_LANG().add(langFrom + " -> " + langTo);
    this.HISTOGRAMS.CHARACTERS().add(numCharacters);
  },

  /**
   * Record a change of the detected language in the health report. This should
   * only be called when actually executing a translation, not every time the
   * user changes in the language in the UI.
   *
   * @param beforeFirstTranslation
   *        A boolean indicating if we are recording a change of detected
   *        language before translating the page for the first time. If we
   *        have already translated the page from the detected language and
   *        the user has manually adjusted the detected language false should
   *        be passed.
   */
  recordDetectedLanguageChange(beforeFirstTranslation) {
    this.HISTOGRAMS.DETECTION_CHANGES().add(beforeFirstTranslation);
  },

  /**
   * Record a change of the target language in the health report. This should
   * only be called when actually executing a translation, not every time the
   * user changes in the language in the UI.
   */
  recordTargetLanguageChange() {
    this.HISTOGRAMS.TARGET_CHANGES().add();
  },

  /**
   * Record a denied translation offer.
   */
  recordDeniedTranslationOffer() {
    this.HISTOGRAMS.DENIED().add();
  },

  /**
   * Record a "Show Original" command use.
   */
  recordShowOriginalContent() {
    this.HISTOGRAMS.SHOW_ORIGINAL().add();
  },

  /**
   * Record the state of translation preferences.
   */
  recordPreferences() {
    if (Services.prefs.getBoolPref(TRANSLATION_PREF_SHOWUI)) {
      this.HISTOGRAMS.SHOW_UI().add(1);
    }
    if (Services.prefs.getBoolPref(TRANSLATION_PREF_DETECT_LANG)) {
      this.HISTOGRAMS.DETECT_LANG().add(1);
    }
  },

  _recordOpportunity(language, success) {
    this.HISTOGRAMS.OPPORTUNITIES().add(success);
    this.HISTOGRAMS.OPPORTUNITIES_BY_LANG().add(language, success);
  },
};

this.TranslationTelemetry.init();
PK
!<���1modules/translation/TranslationContentHandler.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = [ "TranslationContentHandler" ];

ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.defineModuleGetter(this, "LanguageDetector",
  "resource:///modules/translation/LanguageDetector.jsm");

const STATE_OFFER = 0;
const STATE_TRANSLATED = 2;
const STATE_ERROR = 3;

var TranslationContentHandler = function(global, docShell) {
  let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                            .getInterface(Ci.nsIWebProgress);
  webProgress.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT);

  global.addEventListener("pageshow", this);

  global.addMessageListener("Translation:TranslateDocument", this);
  global.addMessageListener("Translation:ShowTranslation", this);
  global.addMessageListener("Translation:ShowOriginal", this);
  this.global = global;
};

TranslationContentHandler.prototype = {
  handleEvent(aEvent) {
    // We are only listening to pageshow events.
    let target = aEvent.target;

    // Only handle top-level frames.
    let win = target.defaultView;
    if (win.parent !== win)
      return;

    let content = this.global.content;
    if (!content.detectedLanguage)
      return;

    let data = {};
    let trDoc = content.translationDocument;
    if (trDoc) {
      data.state = trDoc.translationError ? STATE_ERROR : STATE_TRANSLATED;
      data.translatedFrom = trDoc.translatedFrom;
      data.translatedTo = trDoc.translatedTo;
      data.originalShown = trDoc.originalShown;
    } else {
      data.state = STATE_OFFER;
      data.originalShown = true;
    }
    data.detectedLanguage = content.detectedLanguage;

    this.global.sendAsyncMessage("Translation:DocumentState", data);
  },

  /* nsIWebProgressListener implementation */
  onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
    if (!aWebProgress.isTopLevel ||
        !(aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) ||
        !this.global.content)
      return;

    let url = aRequest.name;
    if (!url.startsWith("http://") && !url.startsWith("https://"))
      return;

    let content = this.global.content;
    if (content.detectedLanguage)
      return;

    // Grab a 60k sample of text from the page.
    let encoder = Cc["@mozilla.org/layout/documentEncoder;1?type=text/plain"]
                    .createInstance(Ci.nsIDocumentEncoder);
    encoder.init(content.document, "text/plain", encoder.SkipInvisibleContent);
    let string = encoder.encodeToStringWithMaxLength(60 * 1024);

    // Language detection isn't reliable on very short strings.
    if (string.length < 100)
      return;

    LanguageDetector.detectLanguage(string).then(result => {
      // Bail if we're not confident.
      if (!result.confident) {
        return;
      }

      // The window might be gone by now.
      if (Cu.isDeadWrapper(content)) {
        return;
      }

      content.detectedLanguage = result.language;

      let data = {
        state: STATE_OFFER,
        originalShown: true,
        detectedLanguage: result.language
      };
      this.global.sendAsyncMessage("Translation:DocumentState", data);
    });
  },

  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
                                         Ci.nsISupportsWeakReference]),

  receiveMessage(msg) {
    switch (msg.name) {
      case "Translation:TranslateDocument":
      {
        ChromeUtils.import("resource:///modules/translation/TranslationDocument.jsm");

        // If a TranslationDocument already exists for this document, it should
        // be used instead of creating a new one so that we can use the original
        // content of the page for the new translation instead of the newly
        // translated text.
        let translationDocument = this.global.content.translationDocument ||
                                  new TranslationDocument(this.global.content.document);

        let preferredEngine = Services.prefs.getCharPref("browser.translation.engine");
        let translator = null;
        if (preferredEngine == "yandex") {
          ChromeUtils.import("resource:///modules/translation/YandexTranslator.jsm");
          translator = new YandexTranslator(translationDocument,
                                            msg.data.from,
                                            msg.data.to);
        } else {
          ChromeUtils.import("resource:///modules/translation/BingTranslator.jsm");
          translator = new BingTranslator(translationDocument,
                                          msg.data.from,
                                          msg.data.to);
        }

        this.global.content.translationDocument = translationDocument;
        translationDocument.translatedFrom = msg.data.from;
        translationDocument.translatedTo = msg.data.to;
        translationDocument.translationError = false;

        translator.translate().then(
          result => {
            this.global.sendAsyncMessage("Translation:Finished", {
              characterCount: result.characterCount,
              from: msg.data.from,
              to: msg.data.to,
              success: true
            });
            translationDocument.showTranslation();
          },
          error => {
            translationDocument.translationError = true;
            let data = {success: false};
            if (error == "unavailable")
              data.unavailable = true;
            this.global.sendAsyncMessage("Translation:Finished", data);
          }
        );
        break;
      }

      case "Translation:ShowOriginal":
        this.global.content.translationDocument.showOriginal();
        break;

      case "Translation:ShowTranslation":
        this.global.content.translationDocument.showTranslation();
        break;
    }
  }
};
PK
!<�ȴ��Z�Z+modules/translation/TranslationDocument.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = [ "TranslationDocument" ];

const TEXT_NODE = Ci.nsIDOMNode.TEXT_NODE;

ChromeUtils.import("resource://services-common/async.js");

/**
 * This class represents a document that is being translated,
 * and it is responsible for parsing the document,
 * generating the data structures translation (the list of
 * translation items and roots), and managing the original
 * and translated texts on the translation items.
 *
 * @param document  The document to be translated
 */
var TranslationDocument = function(document) {
  this.itemsMap = new Map();
  this.roots = [];
  this._init(document);
};

this.TranslationDocument.prototype = {
  translatedFrom: "",
  translatedTo: "",
  translationError: false,
  originalShown: true,

  /**
   * Initializes the object and populates
   * the roots lists.
   *
   * @param document  The document to be translated
   */
  _init(document) {
    let window = document.defaultView;
    let winUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsIDOMWindowUtils);

    // Get all the translation nodes in the document's body:
    // a translation node is a node from the document which
    // contains useful content for translation, and therefore
    // must be included in the translation process.
    let nodeList = winUtils.getTranslationNodes(document.body);

    let length = nodeList.length;

    for (let i = 0; i < length; i++) {
      let node = nodeList.item(i);
      let isRoot = nodeList.isTranslationRootAtIndex(i);

      // Create a TranslationItem object for this node.
      // This function will also add it to the this.roots array.
      this._createItemForNode(node, i, isRoot);
    }

    // At first all roots are stored in the roots list, and only after
    // the process has finished we're able to determine which roots are
    // simple, and which ones are not.

    // A simple root is defined by a root with no children items, which
    // basically represents an element from a page with only text content
    // inside.

    // This distinction is useful for optimization purposes: we treat a
    // simple root as plain-text in the translation process and with that
    // we are able to reduce their data payload sent to the translation service.

    for (let root of this.roots) {
      if (root.children.length == 0 &&
          root.nodeRef.childElementCount == 0) {
        root.isSimpleRoot = true;
      }
    }
  },

  /**
   * Creates a TranslationItem object, which should be called
   * for each node returned by getTranslationNodes.
   *
   * @param node        The DOM node for this item.
   * @param id          A unique, numeric id for this item.
   * @parem isRoot      A boolean saying whether this item is a root.
   *
   * @returns           A TranslationItem object.
   */
  _createItemForNode(node, id, isRoot) {
    if (this.itemsMap.has(node)) {
      return this.itemsMap.get(node);
    }

    let item = new TranslationItem(node, id, isRoot);

    if (isRoot) {
      // Root items do not have a parent item.
      this.roots.push(item);
    } else {
      let parentItem = this.itemsMap.get(node.parentNode);
      if (parentItem) {
        parentItem.children.push(item);
      }
    }

    this.itemsMap.set(node, item);
    return item;
  },

  /**
   * Generate the text string that represents a TranslationItem object.
   * Besides generating the string, it's also stored in the "original"
   * field of the TranslationItem object, which needs to be stored for
   * later to be used in the "Show Original" functionality.
   * If this function had already been called for the given item (determined
   * by the presence of the "original" array in the item), the text will
   * be regenerated from the "original" data instead of from the related
   * DOM nodes (because the nodes might contain translated data).
   *
   * @param item     A TranslationItem object
   *
   * @returns        A string representation of the TranslationItem.
   */
  generateTextForItem(item) {
    if (item.original) {
      return regenerateTextFromOriginalHelper(item);
    }

    if (item.isSimpleRoot) {
      let text = item.nodeRef.firstChild.nodeValue.trim();
      item.original = [text];
      return text;
    }

    let str = "";
    item.original = [];
    let wasLastItemPlaceholder = false;

    for (let child of item.nodeRef.childNodes) {
      if (child.nodeType == TEXT_NODE) {
        let x = child.nodeValue.trim();
        if (x != "") {
          item.original.push(x);
          str += x;
          wasLastItemPlaceholder = false;
        }
        continue;
      }

      let objInMap = this.itemsMap.get(child);
      if (objInMap && !objInMap.isRoot) {
        // If this childNode is present in the itemsMap, it means
        // it's a translation node: it has useful content for translation.
        // In this case, we need to stringify this node.
        // However, if this item is a root, we should skip it here in this
        // object's child list (and just add a placeholder for it), because
        // it will be stringfied separately for being a root.
        item.original.push(objInMap);
        str += this.generateTextForItem(objInMap);
        wasLastItemPlaceholder = false;
      } else if (!wasLastItemPlaceholder) {
        // Otherwise, if this node doesn't contain any useful content,
        // or if it is a root itself, we can replace it with a placeholder node.
        // We can't simply eliminate this node from our string representation
        // because that could change the HTML structure (e.g., it would
        // probably merge two separate text nodes).
        // It's not necessary to add more than one placeholder in sequence;
        // we can optimize them away.
        item.original.push(TranslationItem_NodePlaceholder);
        str += "<br>";
        wasLastItemPlaceholder = true;
      }
    }

    return generateTranslationHtmlForItem(item, str);
  },

  /**
   * Changes the document to display its translated
   * content.
   */
  showTranslation() {
    this.originalShown = false;
    this._swapDocumentContent("translation");
  },

  /**
   * Changes the document to display its original
   * content.
   */
  showOriginal() {
    this.originalShown = true;
    this._swapDocumentContent("original");
  },

  /**
   * Swap the document with the resulting translation,
   * or back with the original content.
   *
   * @param target   A string that is either "translation"
   *                 or "original".
   */
  _swapDocumentContent(target) {
    (async () => {
      // Let the event loop breath on every 100 nodes
      // that are replaced.
      const YIELD_INTERVAL = 100;
      let maybeYield = Async.jankYielder(YIELD_INTERVAL);
      for (let root of this.roots) {
        root.swapText(target);
        await maybeYield();
      }
    })();
  }
};

/**
 * This class represents an item for translation. It's basically our
 * wrapper class around a node returned by getTranslationNode, with
 * more data and structural information on it.
 *
 * At the end of the translation process, besides the properties below,
 * a TranslationItem will contain two other properties: one called "original"
 * and one called "translation". They are twin objects, one which reflect
 * the structure of that node in its original state, and the other in its
 * translated state.
 *
 * The "original" array is generated in the generateTextForItem function,
 * and the "translation" array is generated when the translation results
 * are parsed.
 *
 * They are both arrays, which contain a mix of strings and references to
 * child TranslationItems. The references in both arrays point to the * same *
 * TranslationItem object, but they might appear in different orders between the
 * "original" and "translation" arrays.
 *
 * An example:
 *
 *  English: <div id="n1">Welcome to <b id="n2">Mozilla's</b> website</div>
 *  Portuguese: <div id="n1">Bem vindo a pagina <b id="n2">da Mozilla</b></div>
 *
 *  TranslationItem n1 = {
 *    id: 1,
 *    original: ["Welcome to", ptr to n2, "website"]
 *    translation: ["Bem vindo a pagina", ptr to n2]
 *  }
 *
 *  TranslationItem n2 = {
 *    id: 2,
 *    original: ["Mozilla's"],
 *    translation: ["da Mozilla"]
 *  }
 */
function TranslationItem(node, id, isRoot) {
  this.nodeRef = node;
  this.id = id;
  this.isRoot = isRoot;
  this.children = [];
}

TranslationItem.prototype = {
  isRoot: false,
  isSimpleRoot: false,

  toString() {
    let rootType = "";
    if (this.isRoot) {
      if (this.isSimpleRoot) {
        rootType = " (simple root)";
      } else {
        rootType = " (non simple root)";
      }
    }
    return "[object TranslationItem: <" + this.nodeRef.localName + ">"
           + rootType + "]";
  },

  /**
   * This function will parse the result of the translation of one translation
   * item. If this item was a simple root, all we sent was a plain-text version
   * of it, so the result is also straightforward text.
   *
   * For non-simple roots, we sent a simplified HTML representation of that
   * node, and we'll first parse that into an HTML doc and then call the
   * parseResultNode helper function to parse it.
   *
   * While parsing, the result is stored in the "translation" field of the
   * TranslationItem, which will be used to display the final translation when
   * all items are finished. It remains stored too to allow back-and-forth
   * switching between the "Show Original" and "Show Translation" functions.
   *
   * @param result    A string with the textual result received from the server,
   *                  which can be plain-text or a serialized HTML doc.
   */
  parseResult(result) {
    if (this.isSimpleRoot) {
      this.translation = [result];
      return;
    }

    let domParser = Cc["@mozilla.org/xmlextras/domparser;1"]
                      .createInstance(Ci.nsIDOMParser);

    let doc = domParser.parseFromString(result, "text/html");
    parseResultNode(this, doc.body.firstChild);
  },

  /**
   * This function finds a child TranslationItem
   * with the given id.
   * @param id        The id to look for, in the format "n#"
   * @returns         A TranslationItem with the given id, or null if
   *                  it was not found.
   */
  getChildById(id) {
    for (let child of this.children) {
      if (("n" + child.id) == id) {
        return child;
      }
    }
    return null;
  },

  /**
   * Swap the text of this TranslationItem between
   * its original and translated states.
   *
   * @param target   A string that is either "translation"
   *                 or "original".
   */
  swapText(target) {
    swapTextForItem(this, target);
  }
};

/**
 * This object represents a placeholder item for translation. It's similar to
 * the TranslationItem class, but it represents nodes that have no meaningful
 * content for translation. These nodes will be replaced by "<br>" in a
 * translation request. It's necessary to keep them to use it as a mark
 * for correct positioning and spliting of text nodes.
 */
const TranslationItem_NodePlaceholder = {
  toString() {
    return "[object TranslationItem_NodePlaceholder]";
  }
};

/**
 * Generate the outer HTML representation for a given item.
 *
 * @param   item       A TranslationItem object.
 * param    content    The inner content for this item.
 * @returns string     The outer HTML needed for translation
 *                     of this item.
 */
function generateTranslationHtmlForItem(item, content) {
  let localName = item.isRoot ? "div" : "b";
  return "<" + localName + " id=n" + item.id + ">" +
         content +
         "</" + localName + ">";
}

 /**
 * Regenerate the text string that represents a TranslationItem object,
 * with data from its "original" array. The array must have already
 * been created by TranslationDocument.generateTextForItem().
 *
 * @param item     A TranslationItem object
 *
 * @returns        A string representation of the TranslationItem.
 */
function regenerateTextFromOriginalHelper(item) {
  if (item.isSimpleRoot) {
    return item.original[0];
  }

  let str = "";
  for (let child of item.original) {
    if (child instanceof TranslationItem) {
      str += regenerateTextFromOriginalHelper(child);
    } else if (child === TranslationItem_NodePlaceholder) {
      str += "<br>";
    } else {
      str += child;
    }
  }

  return generateTranslationHtmlForItem(item, str);
}

/**
 * Helper function to parse a HTML doc result.
 * How it works:
 *
 * An example result string is:
 *
 * <div id="n1">Hello <b id="n2">World</b> of Mozilla.</div>
 *
 * For an element node, we look at its id and find the corresponding
 * TranslationItem that was associated with this node, and then we
 * walk down it repeating the process.
 *
 * For text nodes we simply add it as a string.
 */
function parseResultNode(item, node) {
  item.translation = [];
  for (let child of node.childNodes) {
    if (child.nodeType == TEXT_NODE) {
      item.translation.push(child.nodeValue);
    } else if (child.localName == "br") {
      item.translation.push(TranslationItem_NodePlaceholder);
    } else {
      let translationItemChild = item.getChildById(child.id);

      if (translationItemChild) {
        item.translation.push(translationItemChild);
        parseResultNode(translationItemChild, child);
      }
    }
  }
}

/**
 * Helper function to swap the text of a TranslationItem
 * between its original and translated states.
 * How it works:
 *
 * The function iterates through the target array (either the `original` or
 * `translation` array from the TranslationItem), while also keeping a pointer
 * to a current position in the child nodes from the actual DOM node that we
 * are modifying. This pointer is moved forward after each item of the array
 * is translated. If, at any given time, the pointer doesn't match the expected
 * node that was supposed to be seen, it means that the original and translated
 * contents have a different ordering, and thus we need to adjust that.
 *
 * A full example of the reordering process, swapping from Original to
 * Translation:
 *
 *    Original (en): <div>I <em>miss</em> <b>you</b></div>
 *
 * Translation (fr): <div><b>Tu</b> me <em>manques</em></div>
 *
 * Step 1:
 *   pointer points to firstChild of the DOM node, textnode "I "
 *   first item in item.translation is [object TranslationItem <b>]
 *
 *   pointer does not match the expected element, <b>. So let's move <b> to the
 *   pointer position.
 *
 *   Current state of the DOM:
 *     <div><b>you</b>I <em>miss</em> </div>
 *
 * Step 2:
 *   pointer moves forward to nextSibling, textnode "I " again.
 *   second item in item.translation is the string " me "
 *
 *   pointer points to a text node, and we were expecting a text node. Match!
 *   just replace the text content.
 *
 *   Current state of the DOM:
 *     <div><b>you</b> me <em>miss</em> </div>
 *
 * Step 3:
 *   pointer moves forward to nextSibling, <em>miss</em>
 *   third item in item.translation is [object TranslationItem <em>]
 *
 *   pointer points to the expected node. Match! Nothing to do.
 *
 * Step 4:
 *   all items in this item.translation were transformed. The remaining
 *   text nodes are cleared to "", and domNode.normalize() removes them.
 *
 *   Current state of the DOM:
 *     <div><b>you</b> me <em>miss</em></div>
 *
 * Further steps:
 *   After that, the function will visit the child items (from the visitStack),
 *   and the text inside the <b> and <em> nodes will be swapped as well,
 *   yielding the final result:
 *
 *     <div><b>Tu</b> me <em>manques</em></div>
 *
 *
 * @param item     A TranslationItem object
 * @param target   A string that is either "translation"
 *                 or "original".
 */
function swapTextForItem(item, target) {
  // visitStack is the stack of items that we still need to visit.
  // Let's start the process by adding the root item.
  let visitStack = [ item ];

  while (visitStack.length > 0) {
    let curItem = visitStack.shift();

    let domNode = curItem.nodeRef;
    if (!domNode) {
      // Skipping this item due to a missing node.
      continue;
    }

    if (!curItem[target]) {
      // Translation not found for this item. This could be due to
      // an error in the server response. For example, if a translation
      // was broken in various chunks, and one of the chunks failed,
      // the items from that chunk will be missing its "translation"
      // field.
      continue;
    }

    domNode.normalize();

    // curNode points to the child nodes of the DOM node that we are
    // modifying. During most of the process, while the target array is
    // being iterated (in the for loop below), it should walk together with
    // the array and be pointing to the correct node that needs to modified.
    // If it's not pointing to it, that means some sort of node reordering
    // will be necessary to produce the correct translation.
    // Note that text nodes don't need to be reordered, as we can just replace
    // the content of one text node with another.
    //
    // curNode starts in the firstChild...
    let curNode = domNode.firstChild;

    // ... actually, let's make curNode start at the first useful node (either
    // a non-blank text node or something else). This is not strictly necessary,
    // as the reordering algorithm would correctly handle this case. However,
    // this better aligns the resulting translation with the DOM content of the
    // page, avoiding cases that would need to be unecessarily reordered.
    //
    // An example of how this helps:
    //
    // ---- Original: <div>                <b>Hello </b> world.</div>
    //                       ^textnode 1      ^item 1      ^textnode 2
    //
    // - Translation: <div><b>Hallo </b> Welt.</div>
    //
    // Transformation process without this optimization:
    //   1 - start pointer at textnode 1
    //   2 - move item 1 to first position inside the <div>
    //
    //       Node now looks like: <div><b>Hello </b>[         ][ world.]</div>
    //                                         textnode 1^       ^textnode 2
    //
    //   3 - replace textnode 1 with " Welt."
    //   4 - clear remaining text nodes (in this case, textnode 2)
    //
    // Transformation process with this optimization:
    //   1 - start pointer at item 1
    //   2 - item 1 is already in position
    //   3 - replace textnode 2 with " Welt."
    //
    // which completely avoids any node reordering, and requires only one
    // text change instead of two (while also leaving the page closer to
    // its original state).
    while (curNode &&
           curNode.nodeType == TEXT_NODE &&
           curNode.nodeValue.trim() == "") {
      curNode = curNode.nextSibling;
    }

    // Now let's walk through all items in the `target` array of the
    // TranslationItem. This means either the TranslationItem.original or
    // TranslationItem.translation array.
    for (let targetItem of curItem[target]) {

      if (targetItem instanceof TranslationItem) {
        // If the array element is another TranslationItem object, let's
        // add it to the stack to be visited.
        visitStack.push(targetItem);

        let targetNode = targetItem.nodeRef;

            // If the node is not in the expected position, let's reorder
            // it into position...
        if (curNode != targetNode &&
            // ...unless the page has reparented this node under a totally
            // different node (or removed it). In this case, all bets are off
            // on being able to do anything correctly, so it's better not to
            // bring back the node to this parent.
            targetNode.parentNode == domNode) {

          // We don't need to null-check curNode because insertBefore(..., null)
          // does what we need in that case: reorder this node to the end
          // of child nodes.
          domNode.insertBefore(targetNode, curNode);
          curNode = targetNode;
        }

        // Move pointer forward. Since we do not add empty text nodes to the
        // list of translation items, we must skip them here too while
        // traversing the DOM in order to get better alignment between the
        // text nodes and the translation items.
        if (curNode) {
          curNode = getNextSiblingSkippingEmptyTextNodes(curNode);
        }

      } else if (targetItem === TranslationItem_NodePlaceholder) {
        // If the current item is a placeholder node, we need to move
        // our pointer "past" it, jumping from one side of a block of
        // elements + empty text nodes to the other side. Even if
        // non-placeholder elements exists inside the jumped block,
        // they will be pulled correctly later in the process when the
        // targetItem for those nodes are handled.

        while (curNode &&
               (curNode.nodeType != TEXT_NODE ||
                curNode.nodeValue.trim() == "")) {
          curNode = curNode.nextSibling;
        }

      } else {
        // Finally, if it's a text item, we just need to find the next
        // text node to use. Text nodes don't need to be reordered, so
        // the first one found can be used.
        while (curNode && curNode.nodeType != TEXT_NODE) {
          curNode = curNode.nextSibling;
        }

        // If none was found and we reached the end of the child nodes,
        // let's create a new one.
        if (!curNode) {
          // We don't know if the original content had a space or not,
          // so the best bet is to create the text node with " " which
          // will add one space at the beginning and one at the end.
          curNode = domNode.appendChild(domNode.ownerDocument.createTextNode(" "));
        }

        // A trailing and a leading space must be preserved because
        // they are meaningful in HTML.
        let preSpace = /^\s/.test(curNode.nodeValue) ? " " : "";
        let endSpace = /\s$/.test(curNode.nodeValue) ? " " : "";

        curNode.nodeValue = preSpace + targetItem + endSpace;
        curNode = getNextSiblingSkippingEmptyTextNodes(curNode);
      }
    }

    // The translated version of a node might have less text nodes than its
    // original version. If that's the case, let's clear the remaining nodes.
    if (curNode) {
      clearRemainingNonEmptyTextNodesFromElement(curNode);
    }

    // And remove any garbage "" nodes left after clearing.
    domNode.normalize();
  }
}

function getNextSiblingSkippingEmptyTextNodes(startSibling) {
  let item = startSibling.nextSibling;
  while (item &&
         item.nodeType == TEXT_NODE &&
         item.nodeValue.trim() == "") {
    item = item.nextSibling;
  }
  return item;
}

function clearRemainingNonEmptyTextNodesFromElement(startSibling) {
  let item = startSibling;
  while (item) {
    if (item.nodeType == TEXT_NODE &&
        item.nodeValue != "") {
      item.nodeValue = "";
    }
    item = item.nextSibling;
  }
}
PK
!<�<�....(modules/translation/YandexTranslator.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = [ "YandexTranslator" ];

ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/Log.jsm");
ChromeUtils.import("resource://gre/modules/PromiseUtils.jsm");
ChromeUtils.import("resource://services-common/async.js");
ChromeUtils.import("resource://gre/modules/Http.jsm");

Cu.importGlobalProperties(["XMLHttpRequest"]);

// The maximum amount of net data allowed per request on Bing's API.
const MAX_REQUEST_DATA = 5000; // Documentation says 10000 but anywhere
                               // close to that is refused by the service.

// The maximum number of chunks allowed to be translated in a single
// request.
const MAX_REQUEST_CHUNKS = 1000; // Documentation says 2000.

// Self-imposed limit of 15 requests. This means that a page that would need
// to be broken in more than 15 requests won't be fully translated.
// The maximum amount of data that we will translate for a single page
// is MAX_REQUESTS * MAX_REQUEST_DATA.
const MAX_REQUESTS = 15;

const YANDEX_ERR_KEY_INVALID               = 401; // Invalid API key
const YANDEX_ERR_KEY_BLOCKED               = 402; // This API key has been blocked
const YANDEX_ERR_DAILY_REQ_LIMIT_EXCEEDED  = 403; // Daily limit for requests reached
const YANDEX_ERR_DAILY_CHAR_LIMIT_EXCEEDED = 404; // Daily limit of chars reached
// const YANDEX_ERR_TEXT_TOO_LONG             = 413; // The text size exceeds the maximum
// const YANDEX_ERR_UNPROCESSABLE_TEXT        = 422; // The text could not be translated
// const YANDEX_ERR_LANG_NOT_SUPPORTED        = 501; // The specified translation direction is not supported

// Errors that should activate the service unavailable handling
const YANDEX_PERMANENT_ERRORS = [
  YANDEX_ERR_KEY_INVALID,
  YANDEX_ERR_KEY_BLOCKED,
  YANDEX_ERR_DAILY_REQ_LIMIT_EXCEEDED,
  YANDEX_ERR_DAILY_CHAR_LIMIT_EXCEEDED,
];

/**
 * Translates a webpage using Yandex's Translation API.
 *
 * @param translationDocument  The TranslationDocument object that represents
 *                             the webpage to be translated
 * @param sourceLanguage       The source language of the document
 * @param targetLanguage       The target language for the translation
 *
 * @returns {Promise}          A promise that will resolve when the translation
 *                             task is finished.
 */
var YandexTranslator = function(translationDocument, sourceLanguage, targetLanguage) {
  this.translationDocument = translationDocument;
  this.sourceLanguage = sourceLanguage;
  this.targetLanguage = targetLanguage;
  this._pendingRequests = 0;
  this._partialSuccess = false;
  this._serviceUnavailable = false;
  this._translatedCharacterCount = 0;
};

this.YandexTranslator.prototype = {
  /**
   * Performs the translation, splitting the document into several chunks
   * respecting the data limits of the API.
   *
   * @returns {Promise}          A promise that will resolve when the translation
   *                             task is finished.
   */
  translate() {
    return (async () => {
      let currentIndex = 0;
      this._onFinishedDeferred = PromiseUtils.defer();

      // Let's split the document into various requests to be sent to
      // Yandex's Translation API.
      for (let requestCount = 0; requestCount < MAX_REQUESTS; requestCount++) {
        // Generating the text for each request can be expensive, so
        // let's take the opportunity of the chunkification process to
        // allow for the event loop to attend other pending events
        // before we continue.
        await Async.promiseYield();

        // Determine the data for the next request.
        let request = this._generateNextTranslationRequest(currentIndex);

        // Create a real request to the server, and put it on the
        // pending requests list.
        let yandexRequest = new YandexRequest(request.data,
                                          this.sourceLanguage,
                                          this.targetLanguage);
        this._pendingRequests++;
        yandexRequest.fireRequest().then(this._chunkCompleted.bind(this),
                                       this._chunkFailed.bind(this));

        currentIndex = request.lastIndex;
        if (request.finished) {
          break;
        }
      }

      return this._onFinishedDeferred.promise;
    })();
  },

  /**
   * Function called when a request sent to the server completed successfully.
   * This function handles calling the function to parse the result and the
   * function to resolve the promise returned by the public `translate()`
   * method when there are no pending requests left.
   *
   * @param   request   The YandexRequest sent to the server
   */
  _chunkCompleted(yandexRequest) {
    if (this._parseChunkResult(yandexRequest)) {
      this._partialSuccess = true;
      // Count the number of characters successfully translated.
      this._translatedCharacterCount += yandexRequest.characterCount;
    }

    this._checkIfFinished();
  },

  /**
   * Function called when a request sent to the server has failed.
   * This function handles deciding if the error is transient or means the
   * service is unavailable (zero balance on the key or request credentials are
   * not in an active state) and calling the function to resolve the promise
   * returned by the public `translate()` method when there are no pending
   * requests left.
   *
   * @param   aError   [optional] The XHR object of the request that failed.
   */
  _chunkFailed(aError) {
    if (aError instanceof XMLHttpRequest) {
      let body = aError.responseText;
      let json = { code: 0 };
      try {
        json = JSON.parse(body);
      } catch (e) {}

      if (json.code && YANDEX_PERMANENT_ERRORS.includes(json.code))
        this._serviceUnavailable = true;
    }

    this._checkIfFinished();
  },

  /**
   * Function called when a request sent to the server has completed.
   * This function handles resolving the promise
   * returned by the public `translate()` method when all chunks are completed.
   */
  _checkIfFinished() {
    // Check if all pending requests have been
    // completed and then resolves the promise.
    // If at least one chunk was successful, the
    // promise will be resolved positively which will
    // display the "Success" state for the infobar. Otherwise,
    // the "Error" state will appear.
    if (--this._pendingRequests == 0) {
      if (this._partialSuccess) {
        this._onFinishedDeferred.resolve({
          characterCount: this._translatedCharacterCount
        });
      } else {
        let error = this._serviceUnavailable ? "unavailable" : "failure";
        this._onFinishedDeferred.reject(error);
      }
    }
  },

  /**
   * This function parses the result returned by Yandex's Translation API,
   * which returns a JSON result that contains a number of elements. The
   * API is documented here:
   * http://api.yandex.com/translate/doc/dg/reference/translate.xml
   *
   * @param   request      The request sent to the server.
   * @returns boolean      True if parsing of this chunk was successful.
   */
  _parseChunkResult(yandexRequest) {
    let results;
    try {
      let result = JSON.parse(yandexRequest.networkRequest.responseText);
      if (result.code != 200) {
        Services.console.logStringMessage("YandexTranslator: Result is " + result.code);
        return false;
      }
      results = result.text;
    } catch (e) {
      return false;
    }

    let len = results.length;
    if (len != yandexRequest.translationData.length) {
      // This should never happen, but if the service returns a different number
      // of items (from the number of items submitted), we can't use this chunk
      // because all items would be paired incorrectly.
      return false;
    }

    let error = false;
    for (let i = 0; i < len; i++) {
      try {
        let result = results[i];
        let root = yandexRequest.translationData[i][0];
        root.parseResult(result);
      } catch (e) { error = true; }
    }

    return !error;
  },

  /**
   * This function will determine what is the data to be used for
   * the Nth request we are generating, based on the input params.
   *
   * @param startIndex What is the index, in the roots list, that the
   *                   chunk should start.
   */
  _generateNextTranslationRequest(startIndex) {
    let currentDataSize = 0;
    let currentChunks = 0;
    let output = [];
    let rootsList = this.translationDocument.roots;

    for (let i = startIndex; i < rootsList.length; i++) {
      let root = rootsList[i];
      let text = this.translationDocument.generateTextForItem(root);
      if (!text) {
        continue;
      }

      let newCurSize = currentDataSize + text.length;
      let newChunks = currentChunks + 1;

      if (newCurSize > MAX_REQUEST_DATA ||
          newChunks > MAX_REQUEST_CHUNKS) {

        // If we've reached the API limits, let's stop accumulating data
        // for this request and return. We return information useful for
        // the caller to pass back on the next call, so that the function
        // can keep working from where it stopped.
        return {
          data: output,
          finished: false,
          lastIndex: i
        };
      }

      currentDataSize = newCurSize;
      currentChunks = newChunks;
      output.push([root, text]);
    }

    return {
      data: output,
      finished: true,
      lastIndex: 0
    };
  }
};

/**
 * Represents a request (for 1 chunk) sent off to Yandex's service.
 *
 * @params translationData  The data to be used for this translation,
 *                          generated by the generateNextTranslationRequest...
 *                          function.
 * @param sourceLanguage    The source language of the document.
 * @param targetLanguage    The target language for the translation.
 *
 */
function YandexRequest(translationData, sourceLanguage, targetLanguage) {
  this.translationData = translationData;
  this.sourceLanguage = sourceLanguage;
  this.targetLanguage = targetLanguage;
  this.characterCount = 0;
}

YandexRequest.prototype = {
  /**
   * Initiates the request
   */
  fireRequest() {
    return (async () => {
      // Prepare URL.
      let url = getUrlParam("https://translate.yandex.net/api/v1.5/tr.json/translate",
                            "browser.translation.yandex.translateURLOverride");

      // Prepare the request body.
      let apiKey = getUrlParam("%YANDEX_API_KEY%", "browser.translation.yandex.apiKeyOverride");
      let params = [
        ["key", apiKey],
        ["format", "html"],
        ["lang", this.sourceLanguage + "-" + this.targetLanguage],
      ];

      for (let [, text] of this.translationData) {
        params.push(["text", text]);
        this.characterCount += text.length;
      }

      // Set up request options.
      return new Promise((resolve, reject) => {
        let options = {
          onLoad: (responseText, xhr) => {
            resolve(this);
          },
          onError(e, responseText, xhr) {
            reject(xhr);
          },
          postData: params
        };

        // Fire the request.
        this.networkRequest = httpRequest(url, options);

      });
    })();
  }
};

/**
 * Fetch an auth token (clientID or client secret), which may be overridden by
 * a pref if it's set.
 */
function getUrlParam(paramValue, prefName) {
  if (Services.prefs.getPrefType(prefName))
    paramValue = Services.prefs.getCharPref(prefName);
  paramValue = Services.urlFormatter.formatURL(paramValue);
  return paramValue;
}
PK
!<
�=A�_�_!modules/translation/cld-worker.js'use strict';var c;c||(c=eval("(function() { try { return Module || {} } catch(e) { return {} } })()"));var aa={},g;for(g in c)c.hasOwnProperty(g)&&(aa[g]=c[g]);var ba=!1,k=!1,m=!1,ca=!1;
if(c.ENVIRONMENT)if("WEB"===c.ENVIRONMENT)ba=!0;else if("WORKER"===c.ENVIRONMENT)k=!0;else if("NODE"===c.ENVIRONMENT)m=!0;else if("SHELL"===c.ENVIRONMENT)ca=!0;else throw Error("The provided Module['ENVIRONMENT'] value is not valid. It must be one of: WEB|WORKER|NODE|SHELL.");else ba="object"===typeof window,k="function"===typeof importScripts,m="object"===typeof process&&"function"===typeof require&&!ba&&!k,ca=!ba&&!m&&!k;
if(m){c.print||(c.print=console.log);c.printErr||(c.printErr=console.warn);var da,ea;c.read=function(a,b){da||(da=require("fs"));ea||(ea=require("path"));a=ea.normalize(a);var d=da.readFileSync(a);d||a==ea.resolve(a)||(a=path.join(__dirname,"..","src",a),d=da.readFileSync(a));d&&!b&&(d=d.toString());return d};c.readBinary=function(a){a=c.read(a,!0);a.buffer||(a=new Uint8Array(a));assert(a.buffer);return a};c.load=function(a){fa(read(a))};c.thisProgram||(c.thisProgram=1<process.argv.length?process.argv[1].replace(/\\/g,
"/"):"unknown-program");c.arguments=process.argv.slice(2);"undefined"!==typeof module&&(module.exports=c);process.on("uncaughtException",function(a){if(!(a instanceof n))throw a;});c.inspect=function(){return"[Emscripten Module object]"}}else if(ca)c.print||(c.print=print),"undefined"!=typeof printErr&&(c.printErr=printErr),c.read="undefined"!=typeof read?read:function(){throw"no read() available (jsc?)";},c.readBinary=function(a){if("function"===typeof readbuffer)return new Uint8Array(readbuffer(a));
a=read(a,"binary");assert("object"===typeof a);return a},"undefined"!=typeof scriptArgs?c.arguments=scriptArgs:"undefined"!=typeof arguments&&(c.arguments=arguments),eval("if (typeof gc === 'function' && gc.toString().indexOf('[native code]') > 0) var gc = undefined");else if(ba||k)c.read=function(a){var b=new XMLHttpRequest;b.open("GET",a,!1);b.send(null);return b.responseText},c.readAsync=function(a,b,d){var e=new XMLHttpRequest;e.open("GET",a,!0);e.responseType="arraybuffer";e.onload=function(){200==
e.status||0==e.status&&e.response?b(e.response):d()};e.onerror=d;e.send(null)},"undefined"!=typeof arguments&&(c.arguments=arguments),"undefined"!==typeof console?(c.print||(c.print=function(a){console.log(a)}),c.printErr||(c.printErr=function(a){console.warn(a)})):c.print||(c.print=function(){}),k&&(c.load=importScripts),"undefined"===typeof c.setWindowTitle&&(c.setWindowTitle=function(a){document.title=a});else throw"Unknown runtime environment. Where are we?";function fa(a){eval.call(null,a)}
!c.load&&c.read&&(c.load=function(a){fa(c.read(a))});c.print||(c.print=function(){});c.printErr||(c.printErr=c.print);c.arguments||(c.arguments=[]);c.thisProgram||(c.thisProgram="./this.program");c.print=c.print;c.u=c.printErr;c.preRun=[];c.postRun=[];for(g in aa)aa.hasOwnProperty(g)&&(c[g]=aa[g]);
var aa=void 0,t={V:function(a){tempRet0=a},R:function(){return tempRet0},w:function(){return p},o:function(a){p=a},H:function(a){switch(a){case "i1":case "i8":return 1;case "i16":return 2;case "i32":return 4;case "i64":return 8;case "float":return 4;case "double":return 8;default:return"*"===a[a.length-1]?t.q:"i"===a[0]?(a=parseInt(a.substr(1)),assert(0===a%8),a/8):0}},O:function(a){return Math.max(t.H(a),t.q)},W:16,la:function(a,b){"double"===b||"i64"===b?a&7&&(assert(4===(a&7)),a+=4):assert(0===
(a&3));return a},ea:function(a,b,d){return d||"i64"!=a&&"double"!=a?a?Math.min(b||(a?t.O(a):0),t.q):Math.min(b,8):8},h:function(a,b,d){return d&&d.length?(d.splice||(d=Array.prototype.slice.call(d)),d.splice(0,0,b),c["dynCall_"+a].apply(null,d)):c["dynCall_"+a].call(null,b)},l:[],K:function(a){for(var b=0;b<t.l.length;b++)if(!t.l[b])return t.l[b]=a,2*(1+b);throw"Finished up all reserved function pointers. Use a higher value for RESERVED_FUNCTION_POINTERS.";},U:function(a){t.l[(a-2)/2]=null},k:function(a){t.k.v||
(t.k.v={});t.k.v[a]||(t.k.v[a]=1,c.u(a))},s:{},ga:function(a,b){assert(b);t.s[b]||(t.s[b]={});var d=t.s[b];d[a]||(d[a]=function(){return t.h(b,a,arguments)});return d[a]},fa:function(){throw"You must build with -s RETAIN_COMPILER_SETTINGS=1 for Runtime.getCompilerSetting or emscripten_get_compiler_setting to work";},n:function(a){var b=p;p=p+a|0;p=p+15&-16;return b},A:function(a){var b=u;u=u+a|0;u=u+15&-16;return b},d:function(a){var b=v;v=v+a|0;v=v+15&-16;return v>=w&&!ga()?(v=b,0):b},F:function(a,
b){return Math.ceil(a/(b?b:16))*(b?b:16)},ka:function(a,b,d){return d?+(a>>>0)+4294967296*+(b>>>0):+(a>>>0)+4294967296*+(b|0)},C:8,q:4,X:0};c.Runtime=t;t.addFunction=t.K;t.removeFunction=t.U;var ia=!1;function assert(a,b){a||y("Assertion failed: "+b)}function ja(a){var b=c["_"+a];if(!b)try{b=eval("_"+a)}catch(d){}assert(b,"Cannot call unknown function "+a+" (perhaps LLVM optimizations or closure removed it?)");return b}var ka,la;
(function(){function a(a){a=a.toString().match(f).slice(1);return{arguments:a[0],body:a[1],returnValue:a[2]}}function b(){if(!l){l={};for(var b in d)d.hasOwnProperty(b)&&(l[b]=a(d[b]))}}var d={stackSave:function(){t.w()},stackRestore:function(){t.o()},arrayToC:function(a){var b=t.n(a.length);ma(a,b);return b},stringToC:function(a){var b=0;null!==a&&void 0!==a&&0!==a&&(b=t.n((a.length<<2)+1),na(a,b));return b}},e={string:d.stringToC,array:d.arrayToC};la=function(a,b,d,f,l){a=ja(a);var O=[],P=0;if(f)for(var x=
0;x<f.length;x++){var ha=e[d[x]];ha?(0===P&&(P=t.w()),O[x]=ha(f[x])):O[x]=f[x]}d=a.apply(null,O);"string"===b&&(d=z(d));if(0!==P){if(l&&l.async){EmterpreterAsync.Y.push(function(){t.o(P)});return}t.o(P)}return d};var f=/^function\s*[a-zA-Z$_0-9]*\s*\(([^)]*)\)\s*{\s*([^*]*?)[\s;]*(?:return\s*(.*?)[;\s]*)?}$/,l=null;ka=function(d,e,f){f=f||[];var A=ja(d);d=f.every(function(a){return"number"===a});var X="string"!==e;if(X&&d)return A;var O=f.map(function(a,b){return"$"+b});e="(function("+O.join(",")+
") {";var P=f.length;if(!d){b();e+="var stack = "+l.stackSave.body+";";for(var x=0;x<P;x++){var ha=O[x],Y=f[x];"number"!==Y&&(Y=l[Y+"ToC"],e+="var "+Y.arguments+" = "+ha+";",e+=Y.body+";",e+=ha+"=("+Y.returnValue+");")}}f=a(function(){return A}).returnValue;e+="var ret = "+f+"("+O.join(",")+");";X||(f=a(function(){return z}).returnValue,e+="ret = "+f+"(ret);");d||(b(),e+=l.stackRestore.body.replace("()","(stack)")+";");return eval(e+"return ret})")}})();c.ccall=la;c.cwrap=ka;
function oa(a,b,d){d=d||"i8";"*"===d.charAt(d.length-1)&&(d="i32");switch(d){case "i1":B[a>>0]=b;break;case "i8":B[a>>0]=b;break;case "i16":pa[a>>1]=b;break;case "i32":C[a>>2]=b;break;case "i64":tempI64=[b>>>0,(tempDouble=b,1<=+qa(tempDouble)?0<tempDouble?(ra(+sa(tempDouble/4294967296),4294967295)|0)>>>0:~~+ta((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)];C[a>>2]=tempI64[0];C[a+4>>2]=tempI64[1];break;case "float":ua[a>>2]=b;break;case "double":va[a>>3]=b;break;default:y("invalid type for setValue: "+
d)}}c.setValue=oa;function wa(a,b){b=b||"i8";"*"===b.charAt(b.length-1)&&(b="i32");switch(b){case "i1":return B[a>>0];case "i8":return B[a>>0];case "i16":return pa[a>>1];case "i32":return C[a>>2];case "i64":return C[a>>2];case "float":return ua[a>>2];case "double":return va[a>>3];default:y("invalid type for setValue: "+b)}return null}c.getValue=wa;c.ALLOC_NORMAL=0;c.ALLOC_STACK=1;c.ALLOC_STATIC=2;c.ALLOC_DYNAMIC=3;c.ALLOC_NONE=4;
function xa(a,b,d,e){var f,l;"number"===typeof a?(f=!0,l=a):(f=!1,l=a.length);var h="string"===typeof b?b:null;d=4==d?e:["function"===typeof D?D:t.A,t.n,t.A,t.d][void 0===d?2:d](Math.max(l,h?1:b.length));if(f){e=d;assert(0==(d&3));for(a=d+(l&-4);e<a;e+=4)C[e>>2]=0;for(a=d+l;e<a;)B[e++>>0]=0;return d}if("i8"===h)return a.subarray||a.slice?E.set(a,d):E.set(new Uint8Array(a),d),d;e=0;for(var q,r;e<l;){var A=a[e];"function"===typeof A&&(A=t.ha(A));f=h||b[e];0===f?e++:("i64"==f&&(f="i32"),oa(d+e,A,f),
r!==f&&(q=t.H(f),r=f),e+=q)}return d}c.allocate=xa;c.getMemory=function(a){return ya?"undefined"!==typeof F&&!F.b||!za?t.d(a):D(a):t.A(a)};function z(a,b){if(0===b||!a)return"";for(var d=0,e,f=0;;){e=E[a+f>>0];d|=e;if(0==e&&!b)break;f++;if(b&&f==b)break}b||(b=f);e="";if(128>d){for(;0<b;)d=String.fromCharCode.apply(String,E.subarray(a,a+Math.min(b,1024))),e=e?e+d:d,a+=1024,b-=1024;return e}return c.UTF8ToString(a)}c.Pointer_stringify=z;
c.AsciiToString=function(a){for(var b="";;){var d=B[a++>>0];if(!d)return b;b+=String.fromCharCode(d)}};c.stringToAscii=function(a,b){return Aa(a,b,!1)};
function Ba(a,b){for(var d,e,f,l,h,q,r="";;){d=a[b++];if(!d)return r;d&128?(e=a[b++]&63,192==(d&224)?r+=String.fromCharCode((d&31)<<6|e):(f=a[b++]&63,224==(d&240)?d=(d&15)<<12|e<<6|f:(l=a[b++]&63,240==(d&248)?d=(d&7)<<18|e<<12|f<<6|l:(h=a[b++]&63,248==(d&252)?d=(d&3)<<24|e<<18|f<<12|l<<6|h:(q=a[b++]&63,d=(d&1)<<30|e<<24|f<<18|l<<12|h<<6|q))),65536>d?r+=String.fromCharCode(d):(d-=65536,r+=String.fromCharCode(55296|d>>10,56320|d&1023)))):r+=String.fromCharCode(d)}}c.UTF8ArrayToString=Ba;
c.UTF8ToString=function(a){return Ba(E,a)};
function Ca(a,b,d,e){if(!(0<e))return 0;var f=d;e=d+e-1;for(var l=0;l<a.length;++l){var h=a.charCodeAt(l);55296<=h&&57343>=h&&(h=65536+((h&1023)<<10)|a.charCodeAt(++l)&1023);if(127>=h){if(d>=e)break;b[d++]=h}else{if(2047>=h){if(d+1>=e)break;b[d++]=192|h>>6}else{if(65535>=h){if(d+2>=e)break;b[d++]=224|h>>12}else{if(2097151>=h){if(d+3>=e)break;b[d++]=240|h>>18}else{if(67108863>=h){if(d+4>=e)break;b[d++]=248|h>>24}else{if(d+5>=e)break;b[d++]=252|h>>30;b[d++]=128|h>>24&63}b[d++]=128|h>>18&63}b[d++]=128|
h>>12&63}b[d++]=128|h>>6&63}b[d++]=128|h&63}}b[d]=0;return d-f}c.stringToUTF8Array=Ca;c.stringToUTF8=function(a,b,d){return Ca(a,E,b,d)};function Da(a){for(var b=0,d=0;d<a.length;++d){var e=a.charCodeAt(d);55296<=e&&57343>=e&&(e=65536+((e&1023)<<10)|a.charCodeAt(++d)&1023);127>=e?++b:b=2047>=e?b+2:65535>=e?b+3:2097151>=e?b+4:67108863>=e?b+5:b+6}return b}c.lengthBytesUTF8=Da;
function Ea(){return Fa().replace(/__Z[\w\d_]+/g,function(a){var b;a:{if(c.___cxa_demangle)try{var d=D(a.length);na(a.substr(1),d);var e=D(4),f=c.___cxa_demangle(d,0,0,e);if(0===wa(e,"i32")&&f){b=z(f);break a}}catch(l){b=a;break a}finally{d&&Ga(d),e&&Ga(e),f&&Ga(f)}t.k("warning: build with  -s DEMANGLE_SUPPORT=1  to link in libcxxabi demangling");b=a}return a===b?a:a+" ["+b+"]"})}
function Fa(){var a=Error();if(!a.stack){try{throw Error(0);}catch(b){a=b}if(!a.stack)return"(no stack trace available)"}return a.stack.toString()}c.stackTrace=function(){return Ea()};function Ha(a){0<a%4096&&(a+=4096-a%4096);return a}var buffer,B,E,pa,Ia,C,Ja,ua,va;
function Ka(){c.HEAP8=B=new Int8Array(buffer);c.HEAP16=pa=new Int16Array(buffer);c.HEAP32=C=new Int32Array(buffer);c.HEAPU8=E=new Uint8Array(buffer);c.HEAPU16=Ia=new Uint16Array(buffer);c.HEAPU32=Ja=new Uint32Array(buffer);c.HEAPF32=ua=new Float32Array(buffer);c.HEAPF64=va=new Float64Array(buffer)}var La=0,u=0,ya=!1,Ma=0,p=0,Na=0,v=0;
c.reallocBuffer||(c.reallocBuffer=function(a){var b;try{if(ArrayBuffer.b)b=ArrayBuffer.b(buffer,a);else{var d=B;b=new ArrayBuffer(a);(new Int8Array(b)).set(d)}}catch(e){return!1}return Oa(b)?b:!1});function ga(){var a=Math.pow(2,31);if(v>=a)return!1;for(;w<=v;)if(w<a/2)w=Ha(2*w);else{var b=w;w=Ha((3*w+a)/4);if(w<=b)return!1}w=Math.max(w,16777216);if(w>=a)return!1;a=c.reallocBuffer(w);if(!a)return!1;c.buffer=buffer=a;Ka();return!0}var Pa;
try{Pa=Function.prototype.call.bind(Object.getOwnPropertyDescriptor(ArrayBuffer.prototype,"byteLength").get),Pa(new ArrayBuffer(4))}catch(Qa){Pa=function(a){return a.byteLength}}for(var Ra=c.TOTAL_STACK||8192,w=c.TOTAL_MEMORY||2097152,G=65536;G<w||G<2*Ra;)G=16777216>G?2*G:G+16777216;G=Math.max(G,16777216);G!==w&&(w=G);c.buffer?buffer=c.buffer:buffer=new ArrayBuffer(w);Ka();C[0]=255;if(255!==E[0]||0!==E[3])throw"Typed arrays 2 must be run on a little-endian system";c.HEAP=void 0;c.buffer=buffer;
c.HEAP8=B;c.HEAP16=pa;c.HEAP32=C;c.HEAPU8=E;c.HEAPU16=Ia;c.HEAPU32=Ja;c.HEAPF32=ua;c.HEAPF64=va;function H(a){for(;0<a.length;){var b=a.shift();if("function"==typeof b)b();else{var d=b.da;"number"===typeof d?void 0===b.r?t.h("v",d):t.h("vi",d,[b.r]):d(void 0===b.r?null:b.r)}}}var Sa=[],Ta=[],Ua=[],I=[],Va=[],za=!1;function Wa(a){Sa.unshift(a)}c.addOnPreRun=Wa;c.addOnInit=function(a){Ta.unshift(a)};function Xa(a){Ua.unshift(a)}c.addOnPreMain=Xa;c.addOnExit=function(a){I.unshift(a)};
function Ya(a){Va.unshift(a)}c.addOnPostRun=Ya;function Za(a,b,d){d=Array(0<d?d:Da(a)+1);a=Ca(a,d,0,d.length);b&&(d.length=a);return d}c.intArrayFromString=Za;c.intArrayToString=function(a){for(var b=[],d=0;d<a.length;d++){var e=a[d];255<e&&(e&=255);b.push(String.fromCharCode(e))}return b.join("")};function na(a,b,d){a=Za(a,d);for(d=0;d<a.length;)B[b+d>>0]=a[d],d+=1}c.writeStringToMemory=na;function ma(a,b){for(var d=0;d<a.length;d++)B[b++>>0]=a[d]}c.writeArrayToMemory=ma;
function Aa(a,b,d){for(var e=0;e<a.length;++e)B[b++>>0]=a.charCodeAt(e);d||(B[b>>0]=0)}c.writeAsciiToMemory=Aa;Math.imul&&-5===Math.imul(4294967295,5)||(Math.imul=function(a,b){var d=a&65535,e=b&65535;return d*e+((a>>>16)*e+d*(b>>>16)<<16)|0});Math.ia=Math.imul;Math.clz32||(Math.clz32=function(a){a=a>>>0;for(var b=0;32>b;b++)if(a&1<<31-b)return b;return 32});Math.$=Math.clz32;var qa=Math.abs,ta=Math.ceil,sa=Math.floor,ra=Math.min,J=0,$a=null,ab=null;
function bb(){J++;c.monitorRunDependencies&&c.monitorRunDependencies(J)}c.addRunDependency=bb;function cb(){J--;c.monitorRunDependencies&&c.monitorRunDependencies(J);if(0==J&&(null!==$a&&(clearInterval($a),$a=null),ab)){var a=ab;ab=null;a()}}c.removeRunDependency=cb;c.preloadedImages={};c.preloadedAudios={};var K=null,db=[function(a,b){throw"Array index "+a+" out of bounds: [0,"+b+")";}],La=8,u=La+1097872;Ta.push();var K="cld-worker.js.mem",eb=u,u=u+16;c._i64Add=fb;c._i64Subtract=gb;
function hb(a){c.___errno_location&&(C[c.___errno_location()>>2]=a);return a}function ib(){return!!ib.b}var jb=0,kb=[],L={};function lb(a){if(!a||L[a])return a;for(var b in L)if(L[b].D===a)return b;return a}
function mb(){var a=jb;if(!a)return(M.setTempRet0(0),0)|0;var b=L[a],d=b.type;if(!d)return(M.setTempRet0(0),a)|0;var e=Array.prototype.slice.call(arguments);c.___cxa_is_pointer_type(d);mb.buffer||(mb.buffer=D(4));C[mb.buffer>>2]=a;for(var a=mb.buffer,f=0;f<e.length;f++)if(e[f]&&c.___cxa_can_catch(e[f],d,a))return a=C[a>>2],b.D=a,(M.setTempRet0(e[f]),a)|0;a=C[a>>2];return(M.setTempRet0(d),a)|0}c._memset=nb;function ob(a,b){I.push(function(){t.h("vi",a,[b])});ob.level=I.length}c._bitshift64Lshr=pb;
c._bitshift64Shl=qb;function rb(a,b){rb.b||(rb.b={});a in rb.b||(t.h("v",b),rb.b[a]=1)}c._memcpy=sb;var tb=0;function N(){tb+=4;return C[tb-4>>2]}var ub={},vb={};function F(a){F.b||(v=Ha(v),F.b=!0,assert(t.d),F.f=t.d,t.d=function(){y("cannot dynamically allocate, sbrk now has control")});var b=v;return 0==a||F.f(a)?b:4294967295}c._memmove=wb;var xb=1;
function Q(a,b){tb=b;try{var d=N(),e=N(),f=N(),l=0;Q.buffer||(Q.b=[null,[],[]],Q.g=function(a,b){var d=Q.b[a];assert(d);0===b||10===b?((1===a?c.print:c.printErr)(Ba(d,0)),d.length=0):d.push(b)});for(var h=0;h<f;h++){for(var q=C[e+8*h>>2],r=C[e+(8*h+4)>>2],A=0;A<r;A++)Q.g(d,E[q+A]);l+=r}return l}catch(X){return"undefined"!==typeof FS&&X instanceof FS.B||y(X),-X.G}}function D(a){return t.d(a+8)+8&4294967288}c._malloc=D;
I.push(function(){var a=c._fflush;a&&a(0);if(a=Q.g){var b=Q.b;b[1].length&&a(1,10);b[2].length&&a(2,10)}});
var Ma=p=t.F(u),ya=!0,Na=Ma+Ra,v=t.F(Na),yb=xa([8,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,7,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,0,1,
0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0],"i8",3);c.L={Math:Math,Int8Array:Int8Array,Int16Array:Int16Array,Int32Array:Int32Array,Uint8Array:Uint8Array,Uint16Array:Uint16Array,Uint32Array:Uint32Array,Float32Array:Float32Array,Float64Array:Float64Array,NaN:NaN,Infinity:Infinity,byteLength:Pa};
c.M={abort:y,assert:assert,invoke_iiii:function(a,b,d,e){try{return c.dynCall_iiii(a,b,d,e)}catch(f){if("number"!==typeof f&&"longjmp"!==f)throw f;M.setThrew(1,0)}},invoke_viiiii:function(a,b,d,e,f,l){try{c.dynCall_viiiii(a,b,d,e,f,l)}catch(h){if("number"!==typeof h&&"longjmp"!==h)throw h;M.setThrew(1,0)}},invoke_vi:function(a,b){try{c.dynCall_vi(a,b)}catch(d){if("number"!==typeof d&&"longjmp"!==d)throw d;M.setThrew(1,0)}},invoke_ii:function(a,b){try{return c.dynCall_ii(a,b)}catch(d){if("number"!==
typeof d&&"longjmp"!==d)throw d;M.setThrew(1,0)}},invoke_v:function(a){try{c.dynCall_v(a)}catch(b){if("number"!==typeof b&&"longjmp"!==b)throw b;M.setThrew(1,0)}},invoke_viiiiii:function(a,b,d,e,f,l,h){try{c.dynCall_viiiiii(a,b,d,e,f,l,h)}catch(q){if("number"!==typeof q&&"longjmp"!==q)throw q;M.setThrew(1,0)}},invoke_viiii:function(a,b,d,e,f){try{c.dynCall_viiii(a,b,d,e,f)}catch(l){if("number"!==typeof l&&"longjmp"!==l)throw l;M.setThrew(1,0)}},_pthread_cleanup_pop:function(){assert(ob.level==I.length,
"cannot pop if something else added meanwhile!");I.pop();ob.level=I.length},___syscall6:function(a,b){tb=b;try{var d=ub.Q();FS.close(d);return 0}catch(e){return"undefined"!==typeof FS&&e instanceof FS.B||y(e),-e.G}},___gxx_personality_v0:function(){},___assert_fail:function(a,b,d,e){ia=!0;throw"Assertion failed: "+z(a)+", at: "+[b?z(b):"unknown filename",d,e?z(e):"unknown function"]+" at "+Ea();},___cxa_allocate_exception:function(a){return D(a)},___cxa_find_matching_catch:mb,___setErrNo:hb,_sbrk:F,
___cxa_begin_catch:function(a){ib.b--;kb.push(a);var b=lb(a);b&&L[b].I++;return a},_emscripten_memcpy_big:function(a,b,d){E.set(E.subarray(b,b+d),a);return a},___resumeException:function(a){jb||(jb=a);var b=lb(a);b&&(L[b].I=0);throw a+" - Exception catching is disabled, this exception cannot be caught. Compile with -s DISABLE_EXCEPTION_CATCHING=0 or DISABLE_EXCEPTION_CATCHING=2 to catch.";},__ZSt18uncaught_exceptionv:ib,_sysconf:function(a){switch(a){case 30:return 4096;case 85:return G/4096;case 132:case 133:case 12:case 137:case 138:case 15:case 235:case 16:case 17:case 18:case 19:case 20:case 149:case 13:case 10:case 236:case 153:case 9:case 21:case 22:case 159:case 154:case 14:case 77:case 78:case 139:case 80:case 81:case 82:case 68:case 67:case 164:case 11:case 29:case 47:case 48:case 95:case 52:case 51:case 46:return 200809;
case 79:return 0;case 27:case 246:case 127:case 128:case 23:case 24:case 160:case 161:case 181:case 182:case 242:case 183:case 184:case 243:case 244:case 245:case 165:case 178:case 179:case 49:case 50:case 168:case 169:case 175:case 170:case 171:case 172:case 97:case 76:case 32:case 173:case 35:return-1;case 176:case 177:case 7:case 155:case 8:case 157:case 125:case 126:case 92:case 93:case 129:case 130:case 131:case 94:case 91:return 1;case 74:case 60:case 69:case 70:case 4:return 1024;case 31:case 42:case 72:return 32;
case 87:case 26:case 33:return 2147483647;case 34:case 1:return 47839;case 38:case 36:return 99;case 43:case 37:return 2048;case 0:return 2097152;case 3:return 65536;case 28:return 32768;case 44:return 32767;case 75:return 16384;case 39:return 1E3;case 89:return 700;case 71:return 256;case 40:return 255;case 2:return 100;case 180:return 64;case 25:return 20;case 5:return 16;case 6:return 6;case 73:return 4;case 84:return"object"===typeof navigator?navigator.hardwareConcurrency||1:1}hb(22);return-1},
_pthread_getspecific:function(a){return vb[a]||0},_pthread_self:function(){return 0},_pthread_once:rb,_pthread_key_create:function(a){if(0==a)return 22;C[a>>2]=xb;vb[xb]=0;xb++;return 0},_emscripten_asm_const_iii:function(a,b,d){return db[a](b,d)},_pthread_setspecific:function(a,b){if(!(a in vb))return 22;vb[a]=b;return 0},___cxa_throw:function(a,b,d){L[a]={a:a,D:a,type:b,aa:d,I:0};jb=a;"uncaught_exception"in ib?ib.b++:ib.b=1;throw a+" - Exception catching is disabled, this exception cannot be caught. Compile with -s DISABLE_EXCEPTION_CATCHING=0 or DISABLE_EXCEPTION_CATCHING=2 to catch.";
},_abort:function(){c.abort()},_pthread_cleanup_push:ob,_time:function(a){var b=Date.now()/1E3|0;a&&(C[a>>2]=b);return b},___syscall140:function(a,b){tb=b;try{var d=ub.Q(),e=N(),f=N(),l=N(),h=N();assert(0===e);FS.ja(d,f,h);C[l>>2]=d.position;d.T&&0===f&&0===h&&(d.T=null);return 0}catch(q){return"undefined"!==typeof FS&&q instanceof FS.B||y(q),-q.G}},___syscall146:Q,STACKTOP:p,STACK_MAX:Na,tempDoublePtr:eb,ABORT:ia,cttz_i8:yb};// EMSCRIPTEN_START_ASM

var M=(function(global,env,buffer) {
"almost asm";var a=global.Int8Array;var b=global.Int16Array;var c=global.Int32Array;var d=global.Uint8Array;var e=global.Uint16Array;var f=global.Uint32Array;var g=global.Float32Array;var h=global.Float64Array;var i=new a(buffer);var j=new b(buffer);var k=new c(buffer);var l=new d(buffer);var m=new e(buffer);var n=new f(buffer);var o=new g(buffer);var p=new h(buffer);var q=global.byteLength;var r=env.STACKTOP|0;var s=env.STACK_MAX|0;var t=env.tempDoublePtr|0;var u=env.ABORT|0;var v=env.cttz_i8|0;var w=0;var x=0;var y=0;var z=0;var A=global.NaN,B=global.Infinity;var C=0,D=0,E=0,F=0,G=0.0,H=0,I=0,J=0,K=0.0;var L=0;var M=0;var N=0;var O=0;var P=0;var Q=0;var R=0;var S=0;var T=0;var U=0;var V=global.Math.floor;var W=global.Math.abs;var X=global.Math.sqrt;var Y=global.Math.pow;var Z=global.Math.cos;var _=global.Math.sin;var $=global.Math.tan;var aa=global.Math.acos;var ba=global.Math.asin;var ca=global.Math.atan;var da=global.Math.atan2;var ea=global.Math.exp;var fa=global.Math.log;var ga=global.Math.ceil;var ha=global.Math.imul;var ia=global.Math.min;var ja=global.Math.clz32;var ka=env.abort;var la=env.assert;var ma=env.invoke_iiii;var na=env.invoke_viiiii;var oa=env.invoke_vi;var pa=env.invoke_ii;var qa=env.invoke_v;var ra=env.invoke_viiiiii;var sa=env.invoke_viiii;var ta=env._pthread_cleanup_pop;var ua=env.___syscall6;var va=env.___gxx_personality_v0;var wa=env.___assert_fail;var xa=env.___cxa_allocate_exception;var ya=env.___cxa_find_matching_catch;var za=env.___setErrNo;var Aa=env._sbrk;var Ba=env.___cxa_begin_catch;var Ca=env._emscripten_memcpy_big;var Da=env.___resumeException;var Ea=env.__ZSt18uncaught_exceptionv;var Fa=env._sysconf;var Ga=env._pthread_getspecific;var Ha=env._pthread_self;var Ia=env._pthread_once;var Ja=env._pthread_key_create;var Ka=env._emscripten_asm_const_iii;var La=env._pthread_setspecific;var Ma=env.___cxa_throw;var Na=env._abort;var Oa=env._pthread_cleanup_push;var Pa=env._time;var Qa=env.___syscall140;var Ra=env.___syscall146;var Sa=0.0;function Ta(newBuffer){if(q(newBuffer)&16777215||q(newBuffer)<=16777215||q(newBuffer)>2147483648)return false;i=new a(newBuffer);j=new b(newBuffer);k=new c(newBuffer);l=new d(newBuffer);m=new e(newBuffer);n=new f(newBuffer);o=new g(newBuffer);p=new h(newBuffer);buffer=newBuffer;return true}
// EMSCRIPTEN_START_FUNCS
function $a(a){a=a|0;var b=0;b=r;r=r+a|0;r=r+15&-16;return b|0}function ab(){return r|0}function bb(a){a=a|0;r=a}function cb(a,b){a=a|0;b=b|0;r=a;s=b}function db(a,b){a=a|0;b=b|0;if(!w){w=a;x=b}}function eb(a){a=a|0;i[t>>0]=i[a>>0];i[t+1>>0]=i[a+1>>0];i[t+2>>0]=i[a+2>>0];i[t+3>>0]=i[a+3>>0]}function fb(a){a=a|0;i[t>>0]=i[a>>0];i[t+1>>0]=i[a+1>>0];i[t+2>>0]=i[a+2>>0];i[t+3>>0]=i[a+3>>0];i[t+4>>0]=i[a+4>>0];i[t+5>>0]=i[a+5>>0];i[t+6>>0]=i[a+6>>0];i[t+7>>0]=i[a+7>>0]}function gb(a){a=a|0;L=a}function hb(){return L|0}function ib(a,b){a=a|0;b=b|0;var c=0,d=0;d=980497+(a<<3&2040)|0;c=a>>>8;if(c&255|0)nd(b,c&255,l[d+5>>0]|0);c=a>>>16;if(c&255|0)nd(b,c&255,l[d+6>>0]|0);c=a>>>24;if(c|0)nd(b,c&255,l[d+7>>0]|0);return}function jb(a,b,c,d,e){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,m=0,n=0,o=0;o=r;r=r+16|0;n=o+4|0;m=o;f=a+b|0;g=a+c|0;j=e+8|0;h=k[e+4>>2]|0;b=k[j>>2]|0;c=(i[f>>0]|0)==32?f+1|0:f;do{if(c>>>0>=g>>>0)break;k[n>>2]=c;f=l[1009576+(l[c>>0]|0)>>0]|0;k[m>>2]=f;c=c+f|0;f=ud(d,n,m)|0;if(f<<24>>24){k[e+32+(b<<3)>>2]=c-a;k[e+32+(b<<3)+4>>2]=f&255;b=b+1|0}}while((b|0)<(h|0));k[j>>2]=b;n=c-a|0;k[e+32+(b<<3)>>2]=n;k[e+32+(k[j>>2]<<3)+4>>2]=0;r=o;return n|0}function kb(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,i=0,j=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0;q=a+c|0;z=f+12|0;u=k[f+4>>2]|0;A=f+16|0;v=u+-1|0;w=d+16|0;r=d+12|0;y=a;s=e+16|0;t=e+12|0;h=k[z>>2]|0;g=k[A>>2]|0;p=a+b|0;while(1){if(p>>>0>=q>>>0){c=p;break}b=l[1009576+(l[p>>0]|0)>>0]|0;c=p+b|0;b=(l[1009576+(l[c>>0]|0)>>0]|0)+b|0;do if(b>>>0>5){o=qb(p,b)|0;a=k[d>>2]|0;m=k[w>>2]|0;n=(o>>>12)+o|0;i=(k[r>>2]|0)+-1&n;j=m&o;b=k[a+(i<<4)>>2]|0;if((b^j)&m){b=k[a+(i<<4)+4>>2]|0;if((b^j)&m){b=k[a+(i<<4)+8>>2]|0;if((b^j)&m){b=k[a+(i<<4)+12>>2]|0;if(!((b^j)&m))x=8}else x=8}else x=8}else x=8;if((x|0)==8){x=0;if(b){k[f+8040+(h<<3)>>2]=p-y;k[f+8040+(h<<3)+4>>2]=b&~m;h=h+1|0}}j=k[e>>2]|0;m=k[s>>2]|0;a=(k[t>>2]|0)+-1&n;i=m&o;b=k[j+(a<<4)>>2]|0;if((b^i)&m){b=k[j+(a<<4)+4>>2]|0;if((b^i)&m){b=k[j+(a<<4)+8>>2]|0;if((b^i)&m){b=k[j+(a<<4)+12>>2]|0;if((b^i)&m)break}}}if(b){k[f+16048+(g<<3)>>2]=p-y;k[f+16048+(g<<3)+4>>2]=b&~m;g=g+1|0}}while(0);if((g|0)<(v|0)&(h|0)<(u|0))p=c;else break}k[z>>2]=h;k[A>>2]=g;z=c-y|0;k[f+8040+(h<<3)>>2]=z;k[f+8040+(h<<3)+4>>2]=0;k[f+16048+(k[A>>2]<<3)>>2]=z;k[f+16048+(k[A>>2]<<3)+4>>2]=0;return}function lb(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,m=0,n=0,o=0,p=0,q=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0;D=r;r=r+16|0;C=D;t=a+b|0;u=a+c|0;B=f+8|0;b=k[B>>2]|0;A=k[f+4>>2]|0;v=C;k[v>>2]=0;k[v+4>>2]=0;v=C+4|0;w=d+16|0;x=d+12|0;y=e+12|0;z=e+16|0;g=0;c=(i[t>>0]|0)==32?t+1|0:t;do{if(c>>>0>=u>>>0)break;s=c+(l[979972+(l[c>>0]|0)>>0]|0)|0;s=s+(l[979972+(l[s>>0]|0)>>0]|0)|0;t=s+(l[979972+(l[s>>0]|0)>>0]|0)|0;t=t+(l[979972+(l[t>>0]|0)>>0]|0)|0;q=c;p=sb(c,t-q|0)|0;do if(!((p|0)==(k[C>>2]|0)?1:(p|0)==(k[v>>2]|0))){j=k[d>>2]|0;h=k[w>>2]|0;o=(p>>>12)+p|0;m=(k[x>>2]|0)+-1&o;n=h&p;c=k[j+(m<<4)>>2]|0;if((c^n)&h){c=k[j+(m<<4)+4>>2]|0;if((c^n)&h){c=k[j+(m<<4)+8>>2]|0;if((c^n)&h){c=k[j+(m<<4)+12>>2]|0;if(!((c^n)&h))m=8;else m=9}else m=8}else m=8}else m=8;if((m|0)==8){m=0;if(!c)m=9;else j=0}if((m|0)==9){c=k[y>>2]|0;if(!c)break;n=k[e>>2]|0;h=k[z>>2]|0;j=c+-1&o;m=h&p;c=k[n+(j<<4)>>2]|0;if((c^m)&h){c=k[n+(j<<4)+4>>2]|0;if((c^m)&h){c=k[n+(j<<4)+8>>2]|0;if((c^m)&h){c=k[n+(j<<4)+12>>2]|0;if((c^m)&h)break}}}if(!c)break;else j=-2147483648}k[C+(g<<2)>>2]=p;k[f+32+(b<<3)>>2]=q-a;k[f+32+(b<<3)+4>>2]=c&~h|j;b=b+1|0;g=g&1^1}while(0);c=(i[t>>0]|0)==32?t:s;if(c>>>0<u>>>0)c=c+(l[980228+(l[c>>0]|0)>>0]|0)|0;else c=u}while((b|0)<(A|0));k[B>>2]=b;e=c-a|0;k[f+32+(b<<3)>>2]=e;k[f+32+(k[B>>2]<<3)+4>>2]=0;r=D;return e|0}function mb(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,m=0,n=0,o=0,p=0,q=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0;J=r;r=r+16|0;F=J;u=a+b|0;A=a+(c+1)|0;H=f+12|0;g=k[H>>2]|0;B=k[f+4>>2]|0;I=f+16|0;b=k[I>>2]|0;C=B+-1|0;k[F>>2]=0;k[F+4>>2]=0;k[F+8>>2]=0;k[F+12>>2]=0;u=(i[u>>0]|0)==32?u+1|0:u;x=F+8|0;y=e+16|0;z=e+12|0;E=a;v=d+16|0;w=d+12|0;n=0;a=g;g=0;q=u;D=u;j=u;while(1){if(D>>>0>=A>>>0){c=D;break}c=i[D>>0]|0;if(c<<24>>24==32){t=u;p=ub(u,j-t|0)|0;s=L;n=F;o=x;do if(!(((p|0)==(k[n>>2]|0)?(s|0)==(k[n+4>>2]|0):0)|((p|0)==(k[o>>2]|0)?(s|0)==(k[o+4>>2]|0):0))){h=F+(g<<3)|0;k[h>>2]=p;k[h+4>>2]=s;g=1-g|0;h=F+(g<<3)|0;c=k[h>>2]|0;h=k[h+4>>2]|0;do if(!((c|0)==0&(h|0)==0|(c|0)==(p|0)&(h|0)==(s|0))){c=vb(c,h,p,s)|0;j=L;m=k[e>>2]|0;o=k[y>>2]|0;n=k[z>>2]|0;h=rf(c|0,j|0,12)|0;h=of(h|0,L|0,c|0,j|0)|0;h=n+-1&h;j=rf(c|0,j|0,4)|0;j=o&j;c=k[m+(h<<4)>>2]|0;if((c^j)&o){c=k[m+(h<<4)+4>>2]|0;if((c^j)&o){c=k[m+(h<<4)+8>>2]|0;if((c^j)&o){c=k[m+(h<<4)+12>>2]|0;if((c^j)&o){c=n;break}}}}if(!c)c=n;else{k[f+16048+(b<<3)>>2]=q-E;k[f+16048+(b<<3)+4>>2]=c&~o;c=n;b=b+1|0}}else{c=k[z>>2]|0;o=k[y>>2]|0;m=k[e>>2]|0}while(0);q=rf(p|0,s|0,12)|0;q=of(q|0,L|0,p|0,s|0)|0;j=c+-1&q;p=rf(p|0,s|0,4)|0;h=o&p;c=k[m+(j<<4)>>2]|0;if((c^h)&o){c=k[m+(j<<4)+4>>2]|0;if((c^h)&o){c=k[m+(j<<4)+8>>2]|0;if((c^h)&o){c=k[m+(j<<4)+12>>2]|0;if(!((c^h)&o))G=17}else G=17}else G=17}else G=17;if((G|0)==17){G=0;if(c){k[f+16048+(b<<3)>>2]=t-E;k[f+16048+(b<<3)+4>>2]=c&~o;b=b+1|0}}m=k[d>>2]|0;n=k[v>>2]|0;h=(k[w>>2]|0)+-1&q;j=n&p;c=k[m+(h<<4)>>2]|0;if((c^j)&n){c=k[m+(h<<4)+4>>2]|0;if((c^j)&n){c=k[m+(h<<4)+8>>2]|0;if((c^j)&n){c=k[m+(h<<4)+12>>2]|0;if((c^j)&n)break}}}if(c){k[f+8040+(a<<3)>>2]=t-E;k[f+8040+(a<<3)+4>>2]=c&~n;a=a+1|0}}while(0);m=D+1|0;c=i[D>>0]|0;o=0;h=u;j=m}else{o=n+1|0;h=q;m=u}c=D+(l[1009576+(c&255)>>0]|0)|0;if((b|0)<(C|0)&(a|0)<(B|0)){n=o;q=h;D=c;j=(o|0)<9?c:j;u=m}else break}k[H>>2]=a;k[I>>2]=b;H=c-E|0;k[f+8040+(a<<3)>>2]=H;k[f+8040+(a<<3)+4>>2]=0;k[f+16048+(k[I>>2]<<3)>>2]=H;k[f+16048+(k[I>>2]<<3)+4>>2]=0;r=J;return}function nb(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0;d=(c|0)<8?c*12|0:100;e=c*5>>3;e=(e|0)<3?3:(e|0)>16?16:e;c=a-b|0;if((c|0)<(e|0))if((c|0)<1)d=0;else{b=(c*100|0)/(e|0)|0;d=(d|0)<(b|0)?d:b}return d|0}function ob(a,b){a=a|0;b=b|0;var c=0.0;if(b)if(a){if((b|0)>(a|0))c=+(b|0)/+(a|0);else c=+(a|0)/+(b|0);if(!(c<=1.5))if(c>4.0)a=0;else a=~~((4.0-c)*100.0/2.5);else a=100}else a=0;else a=100;return a|0}function pb(a,b){a=a|0;b=b|0;a=((Hc(a)|0)&255)<<8;return a|(l[980484+b>>0]|0)|0}function qb(a,b){a=a|0;b=b|0;var c=0;do if(b){c=l[a>>0]|l[a+1>>0]<<8|l[a+2>>0]<<16|l[a+3>>0]<<24;if((b|0)<5){c=k[120+((b&3)<<2)>>2]&c;c=c>>>3^c;break}else{a=a+4|0;a=k[120+((b&3)<<2)>>2]&(l[a>>0]|l[a+1>>0]<<8|l[a+2>>0]<<16|l[a+3>>0]<<24);c=(a<<18^a)+(c>>>3^c)|0;break}}else c=0;while(0);return c|0}function rb(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0;do if((b|0)>=5){d=l[a>>0]|l[a+1>>0]<<8|l[a+2>>0]<<16|l[a+3>>0]<<24;d=d>>>3^d;e=a+4|0;e=l[e>>0]|l[e+1>>0]<<8|l[e+2>>0]<<16|l[e+3>>0]<<24;if((b|0)<9){a=k[120+((b&3)<<2)>>2]&e;d=(a<<4^a)+(d^c)|0;break}else{a=a+8|0;a=k[120+((b&3)<<2)>>2]&(l[a>>0]|l[a+1>>0]<<8|l[a+2>>0]<<16|l[a+3>>0]<<24);d=(d^c)+(e<<4^e)+(a<<2^a)|0;break}}else{d=k[120+((b&3)<<2)>>2]&(l[a>>0]|l[a+1>>0]<<8|l[a+2>>0]<<16|l[a+3>>0]<<24);d=d^c^d>>>3}while(0);return d|0}function sb(a,b){a=a|0;b=b|0;var c=0;if(!b)b=0;else{c=(i[a+-1>>0]|0)==32?17476:0;b=rb(a,b,(i[a+b>>0]|0)==32?c|1145307136:c)|0}return b|0}function tb(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,m=0;f=(i[a+-1>>0]|0)==32;e=f?c|17476:c;f=f?d:d;d=(i[a+b>>0]|0)==32;e=d?e|1145307136:e;f=d?f:f;switch(b+-1>>2|0){case 0:{b=k[120+((b&3)<<2)>>2]&(l[a>>0]|l[a+1>>0]<<8|l[a+2>>0]<<16|l[a+3>>0]<<24);c=b;d=0;b=(rf(b|0,0,3)|0)^b;a=L;break}case 1:{h=l[a>>0]|l[a+1>>0]<<8|l[a+2>>0]<<16|l[a+3>>0]<<24;j=rf(h|0,0,3)|0;g=L;c=a+4|0;b=k[120+((b&3)<<2)>>2]&(l[c>>0]|l[c+1>>0]<<8|l[c+2>>0]<<16|l[c+3>>0]<<24);c=of(b|0,0,h|0,0)|0;d=L;b=of((sf(b|0,0,4)|0)^b|0,L|0,j^h|0,g|0)|0;a=L;break}case 2:{j=l[a>>0]|l[a+1>>0]<<8|l[a+2>>0]<<16|l[a+3>>0]<<24;g=rf(j|0,0,3)|0;h=L;m=a+4|0;m=l[m>>0]|l[m+1>>0]<<8|l[m+2>>0]<<16|l[m+3>>0]<<24;d=of(m|0,0,j|0,0)|0;c=L;h=of((sf(m|0,0,4)|0)^m|0,L|0,g^j|0,h|0)|0;j=L;a=a+8|0;b=k[120+((b&3)<<2)>>2]&(l[a>>0]|l[a+1>>0]<<8|l[a+2>>0]<<16|l[a+3>>0]<<24);c=of(d|0,c|0,b|0,0)|0;d=L;b=of(h|0,j|0,(sf(b|0,0,2)|0)^b|0,L|0)|0;a=L;break}case 3:{m=l[a>>0]|l[a+1>>0]<<8|l[a+2>>0]<<16|l[a+3>>0]<<24;j=rf(m|0,0,3)|0;h=L;g=a+4|0;g=l[g>>0]|l[g+1>>0]<<8|l[g+2>>0]<<16|l[g+3>>0]<<24;c=of(g|0,0,m|0,0)|0;d=L;h=of((sf(g|0,0,4)|0)^g|0,L|0,j^m|0,h|0)|0;m=L;j=a+8|0;j=l[j>>0]|l[j+1>>0]<<8|l[j+2>>0]<<16|l[j+3>>0]<<24;d=of(c|0,d|0,j|0,0)|0;c=L;j=of(h|0,m|0,(sf(j|0,0,2)|0)^j|0,L|0)|0;m=L;a=a+12|0;b=k[120+((b&3)<<2)>>2]&(l[a>>0]|l[a+1>>0]<<8|l[a+2>>0]<<16|l[a+3>>0]<<24);c=of(d|0,c|0,b|0,0)|0;d=L;b=of(j|0,m|0,(rf(b|0,0,8)|0)^b|0,L|0)|0;a=L;break}case 4:{m=l[a>>0]|l[a+1>>0]<<8|l[a+2>>0]<<16|l[a+3>>0]<<24;h=rf(m|0,0,3)|0;j=L;g=a+4|0;g=l[g>>0]|l[g+1>>0]<<8|l[g+2>>0]<<16|l[g+3>>0]<<24;d=of(g|0,0,m|0,0)|0;c=L;j=of((sf(g|0,0,4)|0)^g|0,L|0,h^m|0,j|0)|0;m=L;h=a+8|0;h=l[h>>0]|l[h+1>>0]<<8|l[h+2>>0]<<16|l[h+3>>0]<<24;c=of(d|0,c|0,h|0,0)|0;d=L;h=of(j|0,m|0,(sf(h|0,0,2)|0)^h|0,L|0)|0;m=L;j=a+12|0;j=l[j>>0]|l[j+1>>0]<<8|l[j+2>>0]<<16|l[j+3>>0]<<24;d=of(c|0,d|0,j|0,0)|0;c=L;j=of(h|0,m|0,(rf(j|0,0,8)|0)^j|0,L|0)|0;m=L;a=a+16|0;b=k[120+((b&3)<<2)>>2]&(l[a>>0]|l[a+1>>0]<<8|l[a+2>>0]<<16|l[a+3>>0]<<24);c=of(d|0,c|0,b|0,0)|0;d=L;b=of(j|0,m|0,(rf(b|0,0,4)|0)^b|0,L|0)|0;a=L;break}default:{m=l[a>>0]|l[a+1>>0]<<8|l[a+2>>0]<<16|l[a+3>>0]<<24;j=rf(m|0,0,3)|0;h=L;g=a+4|0;g=l[g>>0]|l[g+1>>0]<<8|l[g+2>>0]<<16|l[g+3>>0]<<24;c=of(g|0,0,m|0,0)|0;d=L;h=of((sf(g|0,0,4)|0)^g|0,L|0,j^m|0,h|0)|0;m=L;j=a+8|0;j=l[j>>0]|l[j+1>>0]<<8|l[j+2>>0]<<16|l[j+3>>0]<<24;d=of(c|0,d|0,j|0,0)|0;c=L;j=of(h|0,m|0,(sf(j|0,0,2)|0)^j|0,L|0)|0;m=L;h=a+12|0;h=l[h>>0]|l[h+1>>0]<<8|l[h+2>>0]<<16|l[h+3>>0]<<24;c=of(d|0,c|0,h|0,0)|0;d=L;h=of(j|0,m|0,(rf(h|0,0,8)|0)^h|0,L|0)|0;m=L;j=a+16|0;j=l[j>>0]|l[j+1>>0]<<8|l[j+2>>0]<<16|l[j+3>>0]<<24;d=of(c|0,d|0,j|0,0)|0;c=L;j=of(h|0,m|0,(rf(j|0,0,4)|0)^j|0,L|0)|0;m=L;a=a+20|0;b=k[120+((b&3)<<2)>>2]&(l[a>>0]|l[a+1>>0]<<8|l[a+2>>0]<<16|l[a+3>>0]<<24);c=of(d|0,c|0,b|0,0)|0;d=L;b=of(j|0,m|0,(rf(b|0,0,6)|0)^b|0,L|0)|0;a=L}}j=rf(c|0,d|0,17)|0;j=of(j|0,L|0,c|0,d|0)|0;m=L;h=rf(j|0,m|0,9)|0;m=of(h|0,L|0,j|0,m|0)|0;m=of(0,m&255|0,b^e|0,a^f|0)|0;return m|0}function ub(a,b){a=a|0;b=b|0;var c=0,d=0,e=0;if(!b){b=0;a=0}else{c=(i[a+-1>>0]|0)==32;e=c?17476:0;c=c?0:0;d=(i[a+b>>0]|0)==32;a=tb(a,b,d?e|1145307136:e,d?c:c)|0;b=L}L=b;return a|0}function vb(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0;f=rf(a|0,b|0,13)|0;e=L;b=sf(a|0,b|0,51)|0;d=of(f|b|0,e|L|0,c|0,d|0)|0;return d|0}function wb(a,b,c,d,e,f,g){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;g=g|0;var h=0,i=0;h=r;r=r+48|0;i=h+24|0;k[i>>2]=0;k[i+4>>2]=1097857;k[i+8>>2]=23;k[i+12>>2]=26;f=kc(a,b,c,i,0,d,e,h,f,g)|0;r=h;return ((f|0)==26?0:f)|0}function xb(a,b,c,d,e,f,g,h,i){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;i=i|0;return kc(a,b,c,d,0,e,f,g,h,i)|0}function yb(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,g=0;a:do if(a<<16>>16){d=a&1023;e=k[b>>2]|0;do if((e|0)>0){c=0;while(1){f=b+4+(c<<1)|0;g=j[f>>1]|0;c=c+1|0;if((g&1023|0)==(d|0)){c=5;break}if((c|0)>=(e|0)){c=6;break}}if((c|0)==5){b=g<<16>>16>>10;a=a<<16>>16>>10;j[f>>1]=((b|0)>=(a|0)?b:a)<<10|d;break a}else if((c|0)==6)if((e|0)>13)break a;else break}while(0);k[b>>2]=e+1;j[b+4+(e<<1)>>1]=a}while(0);return}function zb(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,g=0;a:do if(a<<16>>16){d=a&1023;e=k[b>>2]|0;do if((e|0)>0){c=0;while(1){f=b+4+(c<<1)|0;g=m[f>>1]|0;c=c+1|0;if((g&1023|0)==(d|0)){c=5;break}if((c|0)>=(e|0)){c=6;break}}if((c|0)==5){j[f>>1]=g+2048&64512|d;break a}else if((c|0)==6)if((e|0)>13)break a;else break}while(0);k[b>>2]=e+1;j[b+4+(e<<1)>>1]=a}while(0);return}function Ab(a){a=a|0;var b=0,c=0,d=0,e=0,f=0,g=0,h=0;if((k[a>>2]|0)>4){g=0;do{f=j[a+4+(g<<1)>>1]|0;d=f<<16>>16>>10;d=(d|0)>-1?d:0-d|0;a:do if((g|0)>0){e=g;while(1){b=e+-1|0;c=j[a+4+(b<<1)>>1]|0;h=c<<16>>16>>10;if((((h|0)>-1?h:0-h|0)|0)>=(d|0)){b=e;break a}j[a+4+(e<<1)>>1]=c;if((e|0)>1)e=b;else break}}else b=g;while(0);j[a+4+(b<<1)>>1]=f;g=g+1|0}while((g|0)<(k[a>>2]|0));k[a>>2]=4}return}function Bb(a){a=a|0;var b=0,c=0,d=0;d=i[a>>0]|0;b=(d&1)==0;d=b?(d&255)>>>1:k[a+4>>2]|0;if((d|0)>0){a=b?a+1|0:k[a+8>>2]|0;b=0;c=0;do{b=((i[a+c>>0]|0)==44&1)+b|0;c=c+1|0}while((c|0)!=(d|0))}else b=0;return b|0}function Cb(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0;g=c;c=0;a:while(1){if((c|0)<(g|0))f=c;else{c=0;break}while(1){d=f+g>>1;c=b+(d*12|0)|0;e=_d(k[c>>2]|0,a)|0;if((e|0)>=0)break;c=d+1|0;if((c|0)<(g|0))f=c;else{c=0;break a}}if((e|0)>0){g=d;c=f}else break}return c|0}function Db(a){a=a|0;var b=0,c=0,d=0,e=0,f=0;f=181;b=0;a:while(1){if((b|0)<(f|0))e=b;else{b=0;break}while(1){c=e+f>>1;b=5776+(c<<3)|0;d=_d(k[b>>2]|0,a)|0;if((d|0)>=0)break;b=c+1|0;if((b|0)<(f|0))e=b;else{b=0;break a}}if((d|0)>0){f=c;b=e}else break}return b|0}function Eb(a){a=a|0;Ba(a|0)|0;cf()}function Fb(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0;d=c+-3|0;a:do if((d|0)>(b|0))do{e=a+b|0;e=l[e>>0]|l[e+1>>0]<<8|l[e+2>>0]<<16|l[e+3>>0]<<24;if((e^1010580540)+-16843009&(e&-2139062144^-2139062144)|0)break a;b=b+4|0}while((b|0)<(d|0));while(0);b:do if((b|0)<(c|0))while(1){if((i[a+b>>0]|0)==60)break b;b=b+1|0;if((b|0)>=(c|0)){b=-1;break}}else b=-1;while(0);return b|0}function Gb(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0;a:do if((b|0)<(c|0))while(1){b:do switch(i[a+b>>0]|0){case 61:break a;case 34:{e=b+1|0;if((e|0)<(c|0)){d=b;b=e;while(1){switch(i[a+b>>0]|0){case 34:break b;case 92:{d=d+2|0;break}default:d=b}b=d+1|0;if((b|0)>=(c|0))break b}}else b=e;break}case 39:{e=b+1|0;if((e|0)<(c|0)){d=b;b=e;while(1){switch(i[a+b>>0]|0){case 39:break b;case 92:{d=d+2|0;break}default:d=b}b=d+1|0;if((b|0)>=(c|0))break b}}else b=e;break}default:{}}while(0);b=b+1|0;if((b|0)>=(c|0)){b=-1;break a}}else b=-1;while(0);return b|0}function Hb(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0;g=Vd(d)|0;a:do if((c-b|0)>=(g|0)){f=g+b|0;while(1){if((c|0)<=(f|0))break;e=c+-1|0;if((i[a+e>>0]|0)==32)c=e;else break}c=c-g|0;if((c|0)>=(b|0)){c=a+c|0;if((g|0)>0){e=0;while(1){if((i[c+e>>0]|32|0)!=(i[d+e>>0]|0)){c=0;break a}e=e+1|0;if((e|0)>=(g|0)){c=1;break}}}else c=1}else c=0}else c=0;while(0);return c|0}function Ib(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0;e=Vd(d)|0;a:do if((c-b|0)>=(e|0)){c=c-e|0;b:do if((c|0)>(b|0))while(1){switch(i[a+b>>0]|0){case 39:case 34:case 32:break;default:break b}b=b+1|0;if((b|0)>=(c|0))break b}while(0);b=a+b|0;if((e|0)>0){c=0;while(1){if((i[b+c>>0]|32|0)!=(i[d+c>>0]|0)){b=0;break a}c=c+1|0;if((c|0)>=(e|0)){b=1;break}}}else b=1}else b=0;while(0);return b|0}function Jb(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0;k[a>>2]=0;k[a+4>>2]=0;k[a+8>>2]=0;if((c|0)<(d|0)){f=1;do{e=l[b+c>>0]|0;g=(l[984554+e>>0]|0)>>>(f*3|0);f=g&3;do if(g&4|0)if(!f){Ce(a,1,i[984810+e>>0]|0);break}else{Ce(a,1,44);break}while(0);c=c+1|0}while((c|0)!=(d|0));if(!f)Ce(a,1,44)}return}function Kb(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0;a:do if((c|0)<(d|0)){b:while(1){switch(i[b+c>>0]|0){case 39:case 34:break b;case 32:break;default:{f=5;break a}}c=c+1|0;if((c|0)>=(d|0)){f=5;break a}}if((c|0)>=0){e=c+1|0;c:do if((e|0)<(d|0)){c=e;d:while(1){switch(i[b+c>>0]|0){case 39:case 34:break d;case 62:{f=8;break d}case 61:{f=9;break d}case 60:{f=10;break d}case 38:{f=11;break d}default:{}}c=c+1|0;if((c|0)>=(d|0))break c}if((f|0)==8)c=c+-1|0;else if((f|0)==9)c=c+-1|0;else if((f|0)==10)c=c+-1|0;else if((f|0)==11)c=c+-1|0;if((c|0)>=0){Jb(a,b,e,c);break a}}while(0);ze(a,1097857,0)}else f=5}else f=5;while(0);if((f|0)==5)ze(a,1097857,0);return}function Lb(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,g=0,h=0,l=0,m=0,n=0,o=0;o=r;r=r+32|0;n=o;d=i[a>>0]|0;l=a+4|0;e=k[l>>2]|0;c=(d&1)==0?(d&255)>>>1:e;if((c|0?(Bb(a)|0)<=4:0)?(c|0)>0:0){g=a+8|0;h=a+1|0;f=0;do{c=Ie(a,f)|0;if((c|0)==-1){if(!(d&1))e=(d&255)>>>1}else e=c;c=e-f|0;do if((c|0)<17){tf(n|0,((d&1)==0?h:k[g>>2]|0)+f|0,c|0)|0;i[n+c>>0]=0;c=Cb(n,136,213)|0;if(c|0){yb(j[c+8>>1]|0,b);yb(j[c+10>>1]|0,b);break}c=me(n,45)|0;if(c|0)i[c>>0]=0;if((Vd(n)|0)<4?(m=Cb(n,2692,257)|0,m|0):0){yb(j[m+8>>1]|0,b);yb(j[m+10>>1]|0,b)}}while(0);f=e+1|0;d=i[a>>0]|0;e=k[l>>2]|0}while((f|0)<(((d&1)==0?(d&255)>>>1:e)|0))}r=o;return}function Mb(a,b){a=a|0;b=b|0;var c=0,d=0;c=r;r=r+16|0;d=c;Jb(d,a,0,Vd(a)|0);Lb(d,b);Ae(d);r=c;return}function Nb(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0;e=r;r=r+16|0;d=e;c=Vd(a)|0;if((c|0)<=3){ne(d,a);i[d+3>>0]=0;if((c|0)>0){a=0;do{f=d+a|0;i[f>>0]=l[f>>0]|0|32;a=a+1|0}while((a|0)!=(c|0))}a=Db(d)|0;if(a|0){zb(j[a+4>>1]|0,b);zb(j[a+6>>1]|0,b)}}r=e;return}function Ob(a,b){a=a|0;b=b|0;switch(a|0){case 62:case 48:case 46:case 45:case 14:{zb(4112,b);break}case 47:case 20:case 13:{zb(4165,b);break}case 12:case 21:case 11:case 10:{zb(4104,b);break}case 44:case 16:{zb(4105,b);break}default:{}}return}function Pb(a,b){a=a|0;b=b|0;zb(a+8192&65535,b);return}function Qb(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,g=0,h=0;f=r;r=r+80|0;e=f;d=f+8|0;k[a>>2]=0;k[a+4>>2]=0;k[a+8>>2]=0;if((k[b>>2]|0)>0){c=0;do{g=j[b+4+(c<<1)>>1]|0;h=Ec(g&1023)|0;k[e>>2]=h;k[e+4>>2]=g<<16>>16>>10;ue(d,985273,e);Fe(a,d);c=c+1|0}while((c|0)<(k[b>>2]|0))}r=f;return}function Rb(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0,j=0,l=0,m=0,n=0,o=0,p=0,q=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0;D=r;r=r+16|0;C=D;k[a>>2]=0;k[a+4>>2]=0;k[a+8>>2]=0;y=(c|0)<8192?c:8192;a:do if((y|0)>0){s=C+4|0;t=a+1|0;u=C+8|0;v=C+1|0;w=a+8|0;x=a+4|0;c=0;do{c=Fb(b,c,y)|0;if((c|0)<0)break a;d=c+1|0;if((d|0)<(y|0))c=d;else break a;b:while(1){switch(i[b+c>>0]|0){case 62:break b;case 60:{z=6;break b}case 38:{z=7;break b}default:{}}c=c+1|0;if((c|0)>=(y|0))break a}if((z|0)==6){z=0;c=c+-1|0}else if((z|0)==7){z=0;c=c+-1|0}if((c|0)<0)break a;if((((((!(Ib(b,d,c,985280)|0)?!(Ib(b,d,c,985284)|0):0)?!(Ib(b,d,c,985290)|0):0)?!(Ib(b,d,c,985298)|0):0)?!(Ib(b,d,c,985304)|0):0)?!(Ib(b,d,c,985309)|0):0)?(A=Ib(b,d,c,985312)|0,B=Gb(b,d,c)|0,(B|0)>-1):0){g=B;f=0;e=d;while(1){do if(A){if(Hb(b,e,g,985318)|0?Ib(b,g+1|0,c,985330)|0:0){z=25;break}if(Hb(b,e,g,985348)|0){d=g+1|0;if(!(Ib(b,d,c,985354)|0)?!(f|(Ib(b,d,c,985367)|0)):0){d=0;z=26}else z=25}else z=24}else z=24;while(0);if((z|0)==24)if(f)z=25;else{d=0;z=26}if((z|0)==25)if(Hb(b,e,g,985377)|0){d=1;z=29}else{d=1;z=26}if((z|0)==26){z=0;if(!(Hb(b,e,g,985386)|0)?!(Hb(b,e,g,985392)|0):0)e=g+1|0;else z=29}if((z|0)==29){z=0;p=g+1|0;Kb(C,b,p,c);q=i[C>>0]|0;f=(q&1)==0;q=f?(q&255)>>>1:k[s>>2]|0;c:do if(q|0){e=i[a>>0]|0;if(!(e&1)){e=(e&255)>>>1;g=t}else{e=k[x>>2]|0;g=k[w>>2]|0}l=f?v:k[u>>2]|0;d:do if(e>>>0>=q>>>0){m=g+e|0;n=l+q|0;o=g;if((e|0)<(q|0))break;h=m+(1-q)|0;if((h|0)==(g|0))break;j=i[l>>0]|0;e=g;e:while(1){if((i[e>>0]|0)==j<<24>>24){f=e;g=l;do{g=g+1|0;if((g|0)==(n|0))break e;f=f+1|0}while((i[f>>0]|0)==(i[g>>0]|0))}e=e+1|0;if((e|0)==(h|0))break d}if(!((e|0)==(m|0)|(e-o|0)==-1))break c}while(0);Ee(a,l,q)}while(0);Ae(C);e=p}g=Gb(b,e,c)|0;if((g|0)<=-1)break;else f=d}}c=c+1|0}while((c|0)<(y|0))}while(0);c=i[a>>0]|0;d=(c&1)==0;if(d)e=(c&255)>>>1;else e=k[a+4>>2]|0;if(e>>>0>1){if(d)c=(c&255)>>>1;else c=k[a+4>>2]|0;He(a,c+-1|0)}r=D;return}function Sb(a,b){a=a|0;b=b|0;var c=0;c=(b|0)<32?b:32;a:do if((c|0)>0){b=0;while(1){if((i[a+~b>>0]|0)==32)break a;b=b+1|0;if((b|0)>=(c|0)){b=0;break}}while(1){if((i[a+(0-b)>>0]&-64)<<24>>24!=-128)break a;b=b+1|0;if((b|0)>=(c|0)){b=0;break}}}else b=0;while(0);return b|0}function Tb(a,b){a=a|0;b=b|0;var c=0,d=0,e=0;d=(b|0)<32?b:32;c=0;while(1){if((c|0)>=(d|0)){e=3;break}b=c+1|0;if((i[a+c>>0]|0)==32)break;else c=b}a:do if((e|0)==3)if((d|0)>0){b=0;while(1){if((i[a+b>>0]&-64)<<24>>24!=-128)break a;b=b+1|0;if((b|0)>=(d|0)){b=0;break}}}else b=0;while(0);return b|0}function Ub(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,m=0;j=a+b|0;e=k[c>>2]|0;if((b|0)>0){b=0;h=a;do{f=i[h>>0]|0;g=f&255;do if((f&255)>=192){if((g&224|0)==192){g=l[h+1>>0]|0|g<<8;a=2;break}a=i[h+1>>0]|0;f=i[h+2>>0]|0;if((g&240|0)==224){g=(a&255)<<8|g<<16|f&255;a=3;break}else{g=(a&255)<<16|g<<24|(f&255)<<8|(l[h+3>>0]|0);a=4;break}}else a=1;while(0);h=h+a|0;m=d+(e<<2)|0;f=k[m>>2]|0;k[m>>2]=g;b=((g|0)==(f|0)?a:0)+b|0;e=(g^e<<4)&4095}while(h>>>0<j>>>0)}else b=0;k[c>>2]=e;return b|0}function Vb(a,b){a=a|0;b=b|0;var c=0,d=0;c=b&-4;if((c|0)>0){d=0;b=0;do{b=((i[a+d>>0]|0)==32&1)+b+((i[a+(d|1)>>0]|0)==32&1)+((i[a+(d|2)>>0]|0)==32&1)+((i[a+(d|3)>>0]|0)==32&1)|0;d=d+4|0}while((d|0)<(c|0))}else b=0;return b|0}function Wb(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0;s=a+b|0;e=k[c>>2]|0;if((b|0)>0){q=a;p=0;r=a;m=a;n=0;while(1){g=i[r>>0]|0;j=g&255;f=q+1|0;i[q>>0]=g;do if(g<<24>>24!=32)if((g&255)>=192){if((j&224|0)==192){h=r+1|0;i[f>>0]=i[h>>0]|0;j=l[h>>0]|0|j<<8;f=q+2|0;h=p;o=2;break}h=r+1|0;i[f>>0]=i[h>>0]|0;g=r+2|0;f=q+3|0;i[q+2>>0]=i[g>>0]|0;if((j&240|0)==224){j=(l[h>>0]|0)<<8|j<<16|(l[g>>0]|0);h=p;o=3;break}else{o=r+3|0;i[f>>0]=i[o>>0]|0;j=(l[h>>0]|0)<<16|j<<24|(l[g>>0]|0)<<8|(l[o>>0]|0);f=q+4|0;h=p;o=4;break}}else{h=p;o=1}else{m=(p<<1|0)>(n|0)?m:f;f=m;h=0;o=1;n=0}while(0);r=r+o|0;q=d+(e<<2)|0;g=k[q>>2]|0;k[q>>2]=j;e=(j^e<<4)&4095;if(r>>>0>=s>>>0)break;else{q=f;p=((j|0)==(g|0)?o:0)+h|0;n=o+n|0}}}else f=a;k[c>>2]=e;e=f-a|0;if((e|0)>=(b+-3|0)){if((e|0)<(b|0))i[f>>0]=32}else{i[f>>0]=32;i[f+1>>0]=32;i[f+2>>0]=32;i[f+3>>0]=0}return e|0}function Xb(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,g=0,h=0,j=0,l=0,m=0,n=0,o=0,p=0;o=r;r=r+16|0;m=o;h=a+b|0;k[m>>2]=0;n=jf(16384)|0;qf(n|0,0,16384)|0;if((b|0)>0){j=h;c=a;l=a+1|0;d=a;e=0;f=a;do{g=j-f|0;g=(g|0)>48?48:g;while(1)if((i[f+g>>0]&-64)<<24>>24==-128)g=g+1|0;else break;p=Vb(f,g)|0;if((p|0)<12&(Ub(f,g,m,n)|0)<19){if(e){p=Tb(f,g)|0;e=g-p|0;f=f+p|0}else e=g;if((e|0)>0){uf(d|0,f|0,e|0)|0;d=d+e|0;g=e;e=0}else{g=e;e=0}}else if(!e){d=d+(0-(Sb(d,d-c|0)|0))|0;if((d|0)==(a|0)){i[a>>0]=32;d=l;e=1}else e=1}else e=1;f=f+g|0}while(f>>>0<h>>>0)}else{c=a;d=a}c=d-c|0;if((c|0)>=(b+-3|0)){if((c|0)<(b|0))i[d>>0]=32}else{i[d>>0]=32;i[d+1>>0]=32;i[d+2>>0]=32;i[d+3>>0]=0}kf(n);r=o;return c|0}function Yb(a,b){a=a|0;b=b|0;var c=0,d=0,e=0;e=r;r=r+16|0;d=e;if((b|0)<256)b=0;else{k[d>>2]=0;c=jf(16384)|0;qf(c|0,0,16384)|0;if((Vb(a,256)|0)<64?(Ub(a,256,d,c)|0)<171:0)b=0;else b=1;kf(c)}r=e;return b|0}function Zb(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0,i=0,l=0,m=0,n=0,o=0,p=0,q=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0;y=r;r=r+48|0;x=y+32|0;t=y+16|0;s=y;q=0;do{d=j[a+568+(q<<1)>>1]|0;n=d&65535;do if(((((d<<16>>16!=-1?(o=k[a+616+(q<<2)>>2]|0,o|0):0)?(p=(k[a+808+(q<<2)>>2]|0)/(o|0)|0,(d&65535)<165&(p|0)<41):0)?(i=k[7224+(n<<2)>>2]|0,(i|0)!=26):0)?(l=rd(a,i&65535)|0,(l|0)>=0):0)?(m=k[a+616+(l<<2)>>2]|0,m|0):0){d=(k[a+808+(l<<2)>>2]|0)/(m|0)|0;e=(d|0)<(p|0);if(!e?!((n|0)<(i|0)&(d|0)==(p|0)):0){f=q;g=0;h=l}else{f=l;g=1;h=q}z=e?p:d;e=m+o|0;z=ha((z|0)>41?z:41,e)|0;j[a+568+(f<<1)>>1]=-1;k[a+712+(f<<2)>>2]=0;k[a+808+(f<<2)>>2]=0;k[a+712+(h<<2)>>2]=e;k[a+808+(h<<2)>>2]=z;if(!(c|(e|0)>9&b^1))if(g){h=Ec(i)|0;z=Ec(n)|0;k[s>>2]=h;k[s+4>>2]=d;k[s+8>>2]=m;k[s+12>>2]=z;pe(941016,985398,s);break}else{n=Ec(n)|0;z=Ec(i)|0;k[t>>2]=n;k[t+4>>2]=p;k[t+8>>2]=o;k[t+12>>2]=z;pe(941016,985398,t);break}}while(0);q=q+1|0}while((q|0)!=24);f=0;do{d=a+568+(f<<1)|0;z=j[d>>1]|0;e=z&65535;if(((z<<16>>16!=-1?(u=k[a+616+(f<<2)>>2]|0,v=a+808+(f<<2)|0,u|0):0)?(w=(k[v>>2]|0)/(u|0)|0,(w|0)<=40):0)?(j[d>>1]=-1,k[a+712+(f<<2)>>2]=0,k[v>>2]=0,!(c|(u|0)>9&b^1)):0){z=Ec(e)|0;k[x>>2]=z;k[x+4>>2]=w;k[x+8>>2]=u;pe(941016,985425,x)}f=f+1|0}while((f|0)!=24);r=y;return}function _b(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0;e=c+616+(b<<2)|0;k[e>>2]=(k[c+616+(a<<2)>>2]|0)+(k[e>>2]|0);e=c+712+(b<<2)|0;d=c+712+(a<<2)|0;k[e>>2]=(k[d>>2]|0)+(k[e>>2]|0);e=c+808+(b<<2)|0;b=c+808+(a<<2)|0;k[e>>2]=(k[b>>2]|0)+(k[e>>2]|0);j[c+568+(a<<1)>>1]=-1;k[d>>2]=0;k[b>>2]=0;return}function $b(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0,i=0,j=0,l=0,n=0,o=0;j=r;r=r+16|0;i=j;f=b^1|c;g=0;do{d=m[a+568+(g<<1)>>1]|0;b=Fc(d)|0;a:do if(b|0){h=g;do{h=h+1|0;if((h|0)>=24)break a;e=m[a+568+(h<<1)>>1]|0}while((Fc(e)|0)!=(b|0));b=(k[a+616+(g<<2)>>2]|0)<(k[a+616+(h<<2)>>2]|0);c=b?g:h;if(!f){l=k[a+616+(c<<2)>>2]|0;n=(k[a+808+(c<<2)>>2]|0)/((l|0?l:1)|0)|0;o=Ec(b?d:e)|0;e=Ec(b?e:d)|0;k[i>>2]=o;k[i+4>>2]=n;k[i+8>>2]=l;k[i+12>>2]=e;pe(941016,985446,i)}_b(c,b?h:g,a)}while(0);g=g+1|0}while((g|0)!=24);r=j;return}function ac(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,l=0,m=0;l=r;r=r+48|0;h=l+32|0;j=l+24|0;g=l+16|0;f=l;se(985487,34,1,941016);e=k[a>>2]|0;if((e|0)!=26){m=Dc(e)|0;e=i[d>>0]|0?1097857:985485;d=k[b>>2]|0;k[f>>2]=m;k[f+4>>2]=e;k[f+8>>2]=d;pe(941016,985522,f)}e=k[a+4>>2]|0;if((e|0)!=26){f=Dc(e)|0;m=k[b+4>>2]|0;k[g>>2]=f;k[g+4>>2]=m;pe(941016,985535,g)}e=k[a+8>>2]|0;if((e|0)!=26){g=Dc(e)|0;m=k[b+8>>2]|0;k[j>>2]=g;k[j+4>>2]=m;pe(941016,985535,j)}k[h>>2]=k[c>>2];pe(941016,985546,h);se(1017206,5,1,941016);r=l;return}function bc(a,b,c,d,e,f,g,h){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;var l=0.0,m=0,n=0,o=0,q=0,r=0,s=0,t=0,u=0,v=0;k[c>>2]=0;n=c+4|0;k[n>>2]=0;r=c+8|0;k[r>>2]=0;k[d>>2]=26;m=d+4|0;k[m>>2]=26;q=d+8|0;k[q>>2]=26;k[e>>2]=0;u=e+4|0;k[u>>2]=0;v=e+8|0;k[v>>2]=0;o=f+8|0;s=f+16|0;k[f>>2]=0;k[f+4>>2]=0;k[f+8>>2]=0;k[f+12>>2]=0;k[f+16>>2]=0;k[f+20>>2]=0;k[g>>2]=b;i[h>>0]=0;t=j[a+568>>1]|0;switch(t<<16>>16){case 26:case -1:{f=0;break}default:{k[d>>2]=t&65535;d=k[a+616>>2]|0;k[c>>2]=(k[a+808>>2]|0)/((d|0?d:1)|0)|0;if((d|0)<1)l=0.0;else l=+((k[a+712>>2]<<10|0)/(d|0)|0|0);p[f>>3]=l;f=d}}d=j[a+570>>1]|0;switch(d<<16>>16){case 26:case -1:{c=0;break}default:{k[m>>2]=d&65535;d=k[a+620>>2]|0;k[n>>2]=(k[a+812>>2]|0)/((d|0?d:1)|0)|0;if((d|0)<1)l=0.0;else l=+((k[a+716>>2]<<10|0)/(d|0)|0|0);p[o>>3]=l;c=d}}d=j[a+572>>1]|0;switch(d<<16>>16){case 26:case -1:{d=0;break}default:{k[q>>2]=d&65535;d=k[a+624>>2]|0;k[r>>2]=(k[a+816>>2]|0)/((d|0?d:1)|0)|0;if((d|0)<1)l=0.0;else l=+((k[a+720>>2]<<10|0)/(d|0)|0|0);p[s>>3]=l}}c=c+f|0;d=d+c|0;if((d|0)>(b|0)){k[g>>2]=d;b=d}r=(b|0)<1?1:b;f=(f*100|0)/(r|0)|0;k[e>>2]=f;s=(c*100|0)/(r|0)|0;c=((d*100|0)/(r|0)|0)-s|0;k[v>>2]=c;d=s-f|0;k[u>>2]=d;if((d|0)<(c|0)){d=d+1|0;k[u>>2]=d;k[v>>2]=c+-1}if((f|0)<(d|0)){k[e>>2]=f+1;k[u>>2]=d+-1}k[g>>2]=b;switch(t<<16>>16){case 26:case -1:{d=0;break}default:{d=k[a+616>>2]|0;d=((k[a+808>>2]|0)/((d|0?d:1)|0)|0|0)>40&1}}i[h>>0]=d;i[h>>0]=(100-(k[e>>2]|0)-(k[u>>2]|0)-(k[v>>2]|0)|0)>20?0:d;return}function cc(a,b,c,d,e,f,g){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;g=g|0;var h=0,j=0,l=0,m=0,n=0,o=0,p=0,q=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0;y=r;r=r+32|0;w=y+8|0;v=y;q=y+12|0;k[q>>2]=k[1971];k[q+4>>2]=k[1972];k[q+8>>2]=k[1973];h=k[c>>2]|0;k[d>>2]=k[b>>2];i[e>>0]=(h|0)>1&1;j=0;m=0;l=0;p=3;while(1){if((k[b+(m<<2)>>2]|0)==25){o=(k[c+(m<<2)>>2]|0)+l|0;n=m+1|0;if((n|0)<3){h=m;j=n;while(1){k[q+(h<<2)>>2]=k[q+(j<<2)>>2];h=j+1|0;if((h|0)==3)break;else{u=j;j=h;h=u}}j=k[q>>2]|0}l=p+-1|0;h=((k[c>>2]|0)*100|0)/(101-o|0)|0;k[d>>2]=k[b+(j<<2)>>2];if((k[c+(j<<2)>>2]|0)<2){i[e>>0]=0;m=n;s=j;t=o;u=l}else{m=n;s=j;t=o;u=l}}else{m=m+1|0;s=j;t=l;u=p}if((m|0)==3)break;else{j=s;l=t;p=u}}o=k[q+4>>2]|0;p=k[c+(o<<2)>>2]|0;n=ha(p,a)|0;o=b+(o<<2)|0;l=k[b+(s<<2)>>2]|0;m=(l|0)==0;a:do if(m){j=k[o>>2]|0;switch(j|0){case 26:case 0:{x=24;break a}default:{}}if((n|0)>1499&(p|0)>16){h=(p*100|0)/(101-t-(k[c+(s<<2)>>2]|0)|0)|0;k[d>>2]=j;if((p|0)<2)i[e>>0]=0}else x=23}else{b=l+-4|0;if(b>>>0<11?(1035>>>(b&2047)&1)!=0:0){j=k[o>>2]|0;if(j>>>0<15){if(16561>>>(j&32767)&1){x=23;break}}else if((j|0)==26){x=23;break}if((n|0)>1499&(p|0)>19){h=(p*100|0)/(101-t-(k[c+(s<<2)>>2]|0)|0)|0;k[d>>2]=j;if((p|0)<2)i[e>>0]=0}else x=23}else x=23}while(0);if((x|0)==23){j=k[o>>2]|0;x=24}do if((x|0)==24){if(!j){if(m)break;h=((k[c+(s<<2)>>2]|0)*100|0)/(101-t-p|0)|0;break}x=j+-4|0;if(x>>>0<11?(1035>>>(x&2047)&1)!=0:0){if(l>>>0<15?16561>>>(l&32767)&1:0)break;h=((k[c+(s<<2)>>2]|0)*100|0)/(101-t-p|0)|0}}while(0);if((h|0)<26){if(!(f^1|g)){x=Ec(k[d>>2]|0)|0;k[v>>2]=x;k[v+4>>2]=h;pe(941016,985557,v)}k[d>>2]=26;i[e>>0]=0}if((h|0)<51)i[e>>0]=0;if((100-(k[c>>2]|0)-(k[c+4>>2]|0)-(k[c+8>>2]|0)|0)>20)i[e>>0]=0;if(!u){if(!(f^1|g)){x=Ec(k[d>>2]|0)|0;k[w>>2]=x;pe(941016,985593,w)}k[d>>2]=26;i[e>>0]=0}r=y;return}function dc(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0;if(Jc(a)|0){d=c+16|0;e=k[d>>2]|0;k[c+20+(e<<2)>>2]=b;k[d>>2]=e+1&3}if(Kc(a)|0){e=c+36|0;d=k[e>>2]|0;k[c+40+(d<<2)>>2]=b;k[e>>2]=d+1&3}return}function ec(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0;d=pb(b,1)|0;if(Jc(a)|0?Jc(b)|0:0){e=c+56|0;f=k[e>>2]|0;k[c+60+(f<<2)>>2]=d;k[e>>2]=f+1&3}if(Kc(a)|0?Kc(b)|0:0){f=c+76|0;e=k[f>>2]|0;k[c+80+(e<<2)>>2]=d;k[f>>2]=e+1&3}return}function fc(a,b){a=a|0;b=b|0;var c=0,d=0;switch(a|0){case 16:{ec(16,69,b);break}case 69:{ec(69,16,b);break}default:{c=Fc(a)|0;if(c|0){d=0;do{if(!((d|0)==(a|0)|(c|0)!=(Fc(d)|0)))ec(a,d,b);d=d+1|0}while((d|0)!=512)}}}return}function gc(a,b,c,d,e){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;var f=0,g=0,h=0,l=0,n=0,o=0,p=0;p=r;r=r+64|0;h=p+8|0;g=p;n=p+32|0;o=p+16|0;l=p+12|0;k[n>>2]=0;if(!c){Rb(o,a,b);Lb(o,n);if(i[e+5>>0]|0?(c=i[o>>0]|0,f=(c&1)==0,(f?(c&255)>>>1:k[o+4>>2]|0)|0):0){c=k[e>>2]|0;k[g>>2]=f?o+1|0:k[o+8>>2]|0;pe(c,985624,g)}Ae(o)}if(d|0){f=k[d>>2]|0;if(f|0?i[f>>0]|0:0)Mb(f,n);f=k[d+4>>2]|0;if(f|0?i[f>>0]|0:0)Nb(f,n);f=k[d+8>>2]|0;if((f|0)!=23)Ob(f,n);f=k[d+12>>2]|0;if((f|0)!=26)Pb(f,n)}Ab(n);if(i[e+5>>0]|0){Qb(o,n);d=i[o>>0]|0;f=(d&1)==0;if((f?(d&255)>>>1:k[o+4>>2]|0)|0){d=k[e>>2]|0;k[h>>2]=f?o+1|0:k[o+8>>2]|0;pe(d,985648,h)}Ae(o)}d=k[n>>2]|0;c=(d|0)>0;if(c){b=0;do{a=j[n+4+(b<<1)>>1]|0;f=a&1023;a=a<<16>>16>>10;if((a|0)>0)dc(f,pb(f,a)|0,e);b=b+1|0}while((b|0)<(d|0))}k[l>>2]=0;hc(o,l);g=k[o>>2]|0;if(c){b=g+40|0;f=0;while(1){l=m[n+4+(f<<1)>>1]&1023;h=g+((Fc(l)|0)<<2)|0;k[h>>2]=(k[h>>2]|0)+1;switch(l|0){case 16:{k[b>>2]=(k[b>>2]|0)+1;break}case 69:{k[b>>2]=(k[b>>2]|0)+1;break}default:{}}f=f+1|0;if((f|0)==(d|0)){a=0;break}}do{l=j[n+4+(a<<1)>>1]|0;f=l&1023;a:do if((l<<16>>16>>10|0)>0){l=Fc(f)|0;if((l|0)>0?(k[g+(l<<2)>>2]|0)==1:0)fc(f,e);switch(f|0){case 16:case 69:break;default:break a}if((k[b>>2]|0)==1)fc(f,e)}while(0);a=a+1|0}while((a|0)!=(d|0))}b=g;if(g|0){f=o+4|0;a=k[f>>2]|0;if((a|0)!=(g|0))k[f>>2]=a+(~((a+-4-b|0)>>>2)<<2);Qe(g)}r=p;return}function hc(a,b){a=a|0;b=b|0;var c=0,d=0,e=0;k[a>>2]=0;e=a+4|0;k[e>>2]=0;k[a+8>>2]=0;ic(a);c=k[e>>2]|0;a=11;d=c;while(1){k[d>>2]=k[b>>2];a=a+-1|0;if(!a)break;else d=d+4|0}k[e>>2]=c+44;return}function ic(a){a=a|0;var b=0;b=gf(44)|0;k[a+4>>2]=b;k[a>>2]=b;k[a+8>>2]=b+44;return}function jc(a){a=a|0;var b=0,c=0,d=0;c=k[a>>2]|0;d=c;if(c|0){a=a+4|0;b=k[a>>2]|0;if((b|0)!=(c|0))k[a>>2]=b+(~((b+-4-d|0)>>>2)<<2);Qe(c)}return}function kc(a,b,c,d,e,f,g,h,j,l){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;j=j|0;l=l|0;var m=0,n=0,o=0,p=0,q=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0;U=r;r=r+3488|0;S=U+3112|0;O=U+3104|0;F=U+3096|0;E=U+3088|0;R=U+3080|0;L=U+3072|0;I=U+3064|0;H=U+3056|0;J=U+3040|0;C=U+3032|0;p=U+3024|0;o=U+3016|0;D=U+2112|0;m=U+3468|0;n=U+3308|0;z=U+3320|0;q=U;T=U+3168|0;A=U+3144|0;y=U+3136|0;G=U+3124|0;Q=U+3120|0;k[f>>2]=26;K=f+4|0;k[K>>2]=26;M=f+8|0;k[M>>2]=26;k[g>>2]=0;N=g+4|0;k[N>>2]=0;P=g+8|0;k[P>>2]=0;k[h>>2]=0;k[h+4>>2]=0;k[h+8>>2]=0;k[h+12>>2]=0;k[h+16>>2]=0;k[h+20>>2]=0;k[j>>2]=0;i[l>>0]=0;if(e&8192|0){ze(D,a,b);if(!(e&512)){lc(n);B=(i[n>>0]&1)==0?n+1|0:k[n+8>>2]|0;k[p>>2]=b;k[p+4>>2]=B;pe(941016,985693,p);Ae(n)}else{mc(m);B=(i[m>>0]&1)==0?m+1|0:k[m+8>>2]|0;k[o>>2]=b;k[o+4>>2]=B;pe(941016,985674,o);Ae(m)}Ae(D)}if(!b)m=26;else{pd(D);k[z>>2]=941016;i[z+4>>0]=e>>>8&1;w=e>>>9&1;i[z+5>>0]=w;x=z+6|0;i[x>>0]=e>>>10&1;i[z+7>>0]=e>>>11&1;k[z+12>>2]=26;t=z+8|0;k[t>>2]=0;k[z+140>>2]=7896;o=z+144|0;k[o>>2]=0;m=z+16|0;n=m+120|0;do{k[m>>2]=0;m=m+4|0}while((m|0)<(n|0));u=e>>>12;gc(a,b,c,d,z);kd(q);kd(q+528|0);kd(q+1056|0);kd(q+1584|0);uc(T,a,b,c);k[o>>2]=T;n=A+4|0;o=A+12|0;k[A>>2]=0;k[A+4>>2]=0;k[A+8>>2]=0;k[A+12>>2]=0;k[A+16>>2]=26;k[y>>2]=0;s=jf(16384)|0;p=(e&4|0)!=0;if(p)qf(s|0,0,16384)|0;q=(e&2|0)==0;v=(e&1|0)!=0;B=0;while(1){if(!(Ac(T,A)|0)){o=21;break}m=k[n>>2]|0;if(q){if(!(v|(m|0)<2049)?Yb(k[A>>2]|0,m)|0:0){o=15;break}}else{m=Xb(k[A>>2]|0,m)|0;k[n>>2]=m}if(p){m=Wb(k[A>>2]|0,m,y,s)|0;k[n>>2]=m}k[t>>2]=k[o>>2];jd(A,z,D);B=m+B|0}if((o|0)==15){if(w|0){k[C>>2]=B;pe(941016,985708,C)}kf(s);m=kc(a,b,c,d,e|2,f,g,h,j,l)|0}else if((o|0)==21){kf(s);n=(w|0)!=0;m=u&1;if(n&(m|0)==0){if(!(i[x>>0]|0))se(1017206,5,1,941016);td(D)}p=(m|0)!=0;$b(D,n,p);sd(D);bc(D,B,G,f,g,h,j,l);do if(!(v|(B|0)<257)){if(i[l>>0]|0){m=k[g>>2]|0;if((m|0)>69){o=29;break}if(((k[N>>2]|0)+m|0)>92){o=29;break}}if(!(p|n^1))ac(f,g,j,l);if((B|0)<256){if(n){k[E>>2]=B;pe(941016,985862,E)}m=kc(a,b,c,d,e|93,f,g,h,j,l)|0;break}else{if(n){k[F>>2]=B;pe(941016,985937,F)}m=kc(a,b,c,d,e|13,f,g,h,j,l)|0;break}}else o=29;while(0);if((o|0)==29){Zb(D,n,p);sd(D);bc(D,B,G,f,g,h,j,l);cc(B,f,g,Q,l,n,p);m=n^1;if(!(p|m)){o=0;do{n=k[f+(o<<2)>>2]|0;if((n|0)!=26){j=Ec(n)|0;E=k[G+(o<<2)>>2]|0;F=k[g+(o<<2)>>2]|0;k[J>>2]=j;k[J+4>>2]=E;k[J+8>>2]=F;pe(941016,985771,J)}o=o+1|0}while((o|0)!=3);k[H>>2]=B;pe(941016,985761,H);H=Dc(k[Q>>2]|0)|0;J=i[l>>0]|0?32:42;k[I>>2]=H;k[I+4>>2]=J;pe(941016,985785,I);se(985793,9,1,941016)}if(m|p^1)m=k[Q>>2]|0;else{se(985803,37,1,941016);m=k[f>>2]|0;if((m|0)!=26){J=Ec(m)|0;g=k[g>>2]|0;k[L>>2]=J;k[L+4>>2]=g;pe(941016,985841,L)}m=k[K>>2]|0;if((m|0)!=26){L=Ec(m)|0;N=k[N>>2]|0;k[O>>2]=L;k[O+4>>2]=N;pe(941016,985841,O)}m=k[M>>2]|0;if((m|0)!=26){O=Ec(m)|0;P=k[P>>2]|0;k[S>>2]=O;k[S+4>>2]=P;pe(941016,985841,S)}m=k[Q>>2]|0;Q=Dc(m)|0;S=i[l>>0]|0?32:42;k[R>>2]=Q;k[R+4>>2]=S;pe(941016,985785,R);se(1017206,5,1,941016)}}}vc(T)}r=U;return m|0}function lc(a){a=a|0;ze(a,1097857,0);return}function mc(a){a=a|0;ze(a,1097857,0);return}function nc(a){a=a|0;a:do if(a>>>0>=256){if(a>>>0>=55296){switch(a&-16|0){case 64992:case 64976:{a=65533;break a}default:{}}if((a&65534|0)==65534)a=65533;else a=(a+-57344|0)>>>0<1056768?a:65533}}else a=k[7932+(a<<2)>>2]|0;while(0);return a|0}function oc(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,g=0;a:do if((b|0)>0){g=0;c=0;d=0;while(1){f=i[a+g>>0]|0;if((f&-64)<<24>>24!=-128){e=(d|0)>7|((c|0)>24?1:((l[993817+((f&255)>>>4)>>0]|0)+g|0)>(b|0));if(e)break a;else d=(e&1^1)+d|0}switch(f<<24>>24){case 60:{f=1097824+c|0;i[f>>0]=38;i[f+1>>0]=108;i[f+2>>0]=116;i[f+3>>0]=59;c=c+4|0;break}case 62:{f=1097824+c|0;i[f>>0]=38;i[f+1>>0]=103;i[f+2>>0]=116;i[f+3>>0]=59;c=c+4|0;break}case 38:{f=1097824+c|0;i[f>>0]=i[993833]|0;i[f+1>>0]=i[993834]|0;i[f+2>>0]=i[993835]|0;i[f+3>>0]=i[993836]|0;i[f+4>>0]=i[993837]|0;c=c+5|0;break}case 39:{f=1097824+c|0;i[f>>0]=i[993839]|0;i[f+1>>0]=i[993840]|0;i[f+2>>0]=i[993841]|0;i[f+3>>0]=i[993842]|0;i[f+4>>0]=i[993843]|0;i[f+5>>0]=i[993844]|0;c=c+6|0;break}case 34:{f=1097824+c|0;i[f>>0]=i[993846]|0;i[f+1>>0]=i[993847]|0;i[f+2>>0]=i[993848]|0;i[f+3>>0]=i[993849]|0;i[f+4>>0]=i[993850]|0;i[f+5>>0]=i[993851]|0;c=c+6|0;break}default:{i[1097824+c>>0]=f;c=c+1|0}}g=g+1|0;if((g|0)>=(b|0))break a}}else c=0;while(0);i[1097824+c>>0]=0;return}function pc(a,b){a=a|0;b=b|0;do if(b>>>0>=128){if(b>>>0<2048){i[a>>0]=b>>>6|192;i[a+1>>0]=b&63|128;b=2;break}b=b>>>0>1114111?65533:b;if(b>>>0<65536){i[a>>0]=b>>>12|224;i[a+1>>0]=b>>>6&63|128;i[a+2>>0]=b&63|128;b=3;break}else{i[a>>0]=b>>>18|240;i[a+1>>0]=b>>>12&63|128;i[a+2>>0]=b>>>6&63|128;i[a+3>>0]=b&63|128;b=4;break}}else{i[a>>0]=b;b=1}while(0);return b|0}function qc(a,b){a=a|0;b=b|0;var c=0,d=0;d=r;r=r+16|0;c=d;if((b|0)>15)b=-1;else{tf(c|0,a|0,b|0)|0;i[c+b>>0]=0;b=Lc(c)|0;if((b|0)>-1)b=k[8956+(b<<3)+4>>2]|0;else b=-1}r=d;return b|0}function rc(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0,j=0,l=0;j=a+b|0;a:do if((b|0)!=0?(i[a>>0]|0)==38:0){k[c>>2]=1;f=a+1|0;d=i[f>>0]|0;if(d<<24>>24==35){if((b|0)<4){d=-1;break}d=a+2|0;b:do switch(i[d>>0]|0){case 88:case 120:{d=a+3|0;do{if((i[d>>0]|0)!=48)break;d=d+1|0}while(d>>>0<j>>>0);if((d|0)==(j|0)){d=-1;break a}f=i[d>>0]|0;if((f+-48&255)>=10)switch(f<<24>>24){case 65:case 66:case 67:case 68:case 69:case 70:case 97:case 98:case 99:case 100:case 101:case 102:break;default:{d=-1;break a}}c:do if(d>>>0<j>>>0){e=f;b=d;while(1){if((e+-48&255)>=10)switch(e<<24>>24){case 65:case 66:case 67:case 68:case 69:case 70:case 97:case 98:case 99:case 100:case 101:case 102:break;default:{h=b;break c}}b=b+1|0;if(b>>>0>=j>>>0){h=b;break c}e=i[b>>0]|0}}else h=d;while(0);b=h;g=b-d|0;if((g|0)>=8?!((g|0)==8&f<<24>>24<56):0){d=65533;break b}d:do if(d>>>0<h>>>0){g=f;e=0;while(1){f=e<<4;e=g<<24>>24;do if((g+-48&255)>=10)if((g+-97&255)<6){e=e+-87|0;break}else{e=(g+-65&255)<6?e+-55|0:0;break}else e=e+-48|0;while(0);f=e+f|0;e=d+1|0;if((e|0)==(h|0)){d=f;break d}d=e;g=i[e>>0]|0;e=f}}else d=0;while(0);d=nc(d)|0;break}case 48:{while(1){d=d+1|0;if(d>>>0>=j>>>0){l=32;break b}if((i[d>>0]|0)!=48){l=32;break}}break}default:l=32}while(0);do if((l|0)==32){if((d|0)==(j|0)){d=-1;break a}f=i[d>>0]|0;if((f+-48&255)>=10){d=-1;break a}e:do if(d>>>0<j>>>0){b=d;while(1){b=b+1|0;if(b>>>0>=j>>>0){g=b;break e}if(((i[b>>0]|0)+-48&255)>=10){g=b;break}}}else g=d;while(0);b=g;e=b-d|0;if((e|0)>=9){if((e|0)!=10){d=65533;break}if((Yd(d,993853,10)|0)>=1){d=65533;break}}if(d>>>0<g>>>0){e=(f<<24>>24)+-48|0;d=d+1|0;if((d|0)==(g|0))d=e;else{f=d;d=e;do{d=(d*10|0)+-48+(i[f>>0]|0)|0;f=f+1|0}while((f|0)!=(g|0))}}else d=0;d=nc(d)|0}while(0);if((d|0)==-1|b>>>0>j>>>0){d=-1;break}}else{g=f;if((b|0)>1?(d+-48&255)<10|((d&-33)+-65&255)<26:0){d=f;do{d=d+1|0;if(d>>>0>=j>>>0)break;h=i[d>>0]|0}while((h+-48&255)<10|((h&-33)+-65&255)<26);b=d;e=d}else{b=g;e=f}d=qc(f,b-g|0)|0;if((d|0)<0){d=-1;break}if((d|0)>255){if(e>>>0>=j>>>0){d=-1;break}if((i[e>>0]|0)!=59){d=-1;break}}}e=b;if(e>>>0<j>>>0?(i[e>>0]|0)==59:0)b=e+1|0;k[c>>2]=b-a}else l=3;while(0);if((l|0)==3){k[c>>2]=0;d=-1}return d|0}function sc(a,b,c,d,e){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;b=rc(a,b,d)|0;if((b|0)>0)b=pc(c,b)|0;else{k[d>>2]=1;b=0}k[e>>2]=b;return}function tc(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0;f=a+b|0;a:do if((b|0)>0){d=a;e=1008264;while(1){e=l[e+(l[1009064+(l[d>>0]|0)>>0]|0)>>0]|0;if((e|0)<=(c|0))break;d=d+1|0;if(d>>>0<f>>>0)e=1008264+(e*20|0)|0;else break a}switch(e|0){case 0:case 2:{b=d-a|0;break a}default:{}}b=d-a|0;while(1){d=b+-1|0;if((b|0)<=1)break a;if((i[a+d>>0]|0)==60)break;else b=d}}while(0);return b|0}function uc(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;k[a>>2]=b;k[a+4>>2]=b;k[a+8>>2]=b+c;k[a+12>>2]=c;i[a+16>>0]=d&1;i[a+28>>0]=1;i[a+29>>0]=1;k[a+32>>2]=1;c=a+36|0;Nc(c);d=a+88|0;Nc(d);b=jf(40960)|0;k[a+20>>2]=b;b=jf(61440)|0;k[a+24>>2]=b;Oc(c);Oc(d);return}function vc(a){a=a|0;var b=0;b=k[a+20>>2]|0;if(b|0)kf(b);b=k[a+24>>2]|0;if(b|0)kf(b);Pc(a+88|0);Pc(a+36|0);return}function wc(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,m=0,n=0,o=0,p=0,q=0,s=0,t=0,u=0;u=r;r=r+32|0;o=u+12|0;s=u+8|0;q=u+4|0;n=u;p=u+16|0;k[q>>2]=0;a:do if((c|0)>0){m=a+16|0;j=a+32|0;a=0;e=0;while(1){wd(b+e|0,c-e|0,o);e=(k[o>>2]|0)+e|0;if((e|0)>=(c|0)){e=c;a=0;break a}g=b+e|0;h=i[g>>0]|0;f=h&255;b:do if(((h&-32)<<24>>24==32?(i[1009320+f>>0]|0)!=0:0)?(i[m>>0]|0)==0:0)switch(h<<24>>24){case 60:{a=tc(g,c-e|0,k[j>>2]|0)|0;k[q>>2]=a;break b}case 62:{k[q>>2]=1;a=1;break b}case 38:{sc(g,c-e|0,p,q,n);k[o>>2]=l[1009576+(l[p>>0]|0)>>0];k[s>>2]=p;a=(vd(s,o)|0)&255;t=12;break b}default:break b}else t=11;while(0);if((t|0)==11){a=l[1009576+f>>0]|0;k[q>>2]=a;k[o>>2]=a;k[s>>2]=g;a=(vd(s,o)|0)&255;t=12}if((t|0)==12){t=0;if(a|0)break a;a=k[q>>2]|0}e=a+e|0;if((e|0)>=(c|0)){a=0;break}}}else{e=0;a=0}while(0);k[d>>2]=a;r=u;return e|0}function xc(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,g=0,h=0,j=0,l=0,m=0,n=0,o=0,p=0,q=0,s=0,t=0,u=0,v=0,w=0;w=r;r=r+16|0;p=w+4|0;o=w;t=a+20|0;m=k[t>>2]|0;k[b>>2]=m;u=b+4|0;k[u>>2]=0;v=a+4|0;k[b+8>>2]=(k[v>>2]|0)-(k[a>>2]|0);k[b+12>>2]=0;k[b+16>>2]=26;l=b+20|0;i[l>>0]=0;s=a+12|0;n=k[s>>2]|0;n=(n+-40928|0)>>>0<40928?(n|0)/2|0:40896;i[m>>0]=32;i[(k[t>>2]|0)+1>>0]=0;b=k[s>>2]|0;if((b|0)<1)b=0;else{m=a+16|0;j=a+32|0;c=0;g=b;h=0;a=1;b=0;while(1){d=k[v>>2]|0;e=d+b|0;f=i[e>>0]|0;f=f<<24>>24==13?10:f;if(((f&-32)<<24>>24==32?(i[1009320+(f&255)>>0]|0)!=0:0)?(i[m>>0]|0)==0:0){a:do switch(f<<24>>24){case 60:{b:do if((b|0)<(g+-3|0))switch(i[d+(b+1)>>0]|32|0){case 112:{d=(i[d+(b+2)>>0]|0)<64?10:32;break b}case 98:{if((i[d+(b+2)>>0]|32|0)!=114){d=32;break b}d=(i[d+(b+3)>>0]|0)<64?10:32;break b}case 116:{if((i[d+(b+2)>>0]|32|0)!=114){d=32;break b}d=(i[d+(b+3)>>0]|0)<64?10:32;break b}default:{d=32;break b}}else d=32;while(0);c=(tc(e,g-b|0,k[j>>2]|0)|0)+1|0;k[p>>2]=c;if(h)switch(d<<24>>24){case 10:case 32:{e=d;d=1;break a}default:{}}i[(k[t>>2]|0)+a>>0]=d;e=d;d=d<<24>>24==32|d<<24>>24==10;a=a+1|0;break}case 62:{k[p>>2]=1;i[(k[t>>2]|0)+a>>0]=62;c=1;e=62;d=h;a=a+1|0;break}case 38:{sc(e,g-b|0,(k[t>>2]|0)+a|0,p,o);c=k[p>>2]|0;e=38;d=h;a=(k[o>>2]|0)+a|0;break}default:{e=f;d=h}}while(0);h=c;b=c+b|0}else{if(h)switch(f<<24>>24){case 10:case 32:{d=1;break}default:q=22}else q=22;if((q|0)==22){q=0;i[(k[t>>2]|0)+a>>0]=f;d=f<<24>>24==32|f<<24>>24==10;a=a+1|0}h=c;e=f;b=b+1|0}if(!((a|0)<(n|0)|e<<24>>24!=10&e<<24>>24!=32)){q=25;break}if((a|0)>40927){q=28;break}g=k[s>>2]|0;if((g|0)<=(b|0))break;else{c=h;h=d}}if((q|0)==25)i[l>>0]=1;else if((q|0)==28)i[l>>0]=1;d=k[v>>2]|0;c:do if((b|0)>0){c=b;while(1){if((i[d+c>>0]&-64)<<24>>24!=-128){b=c;break c}b=c+-1|0;a=a+-1|0;if((c|0)>1)c=b;else break}}while(0);k[v>>2]=d+b;k[s>>2]=(k[s>>2]|0)-b;i[(k[t>>2]|0)+a>>0]=32;i[(k[t>>2]|0)+(a+1)>>0]=32;i[(k[t>>2]|0)+(a+2)>>0]=32;i[(k[t>>2]|0)+(a+3)>>0]=0;k[u>>2]=a;b=1}r=w;return b|0}function yc(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,g=0,h=0,j=0,m=0,n=0,o=0,p=0,q=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0;C=r;r=r+32|0;s=C+16|0;v=C+12|0;c=C+8|0;u=C+4|0;t=C;if(!(i[a+28>>0]|0))b=xc(a,b)|0;else{z=a+20|0;y=k[z>>2]|0;k[b>>2]=y;A=b+4|0;k[A>>2]=0;B=a+4|0;o=b+8|0;k[o>>2]=(k[B>>2]|0)-(k[a>>2]|0);d=b+12|0;k[d>>2]=0;k[b+16>>2]=26;p=b+20|0;i[p>>0]=0;x=a+12|0;q=k[x>>2]|0;q=(q+-40928|0)>>>0<40928?(q|0)/2|0:40896;k[u>>2]=0;k[t>>2]=0;i[y>>0]=32;i[(k[z>>2]|0)+1>>0]=0;y=a+36|0;Oc(y);Vc(y,k[o>>2]|0);b=wc(a,k[B>>2]|0,k[x>>2]|0,c)|0;k[B>>2]=(k[B>>2]|0)+b;k[x>>2]=(k[x>>2]|0)-b;if((b|0)==1)Tc(y,1);else{Vc(y,b);Uc(y,1)}b=k[x>>2]|0;if((b|0)<1){Rc(y);b=0}else{o=k[c>>2]|0;k[d>>2]=o;m=a+16|0;n=a+29|0;j=a+32|0;d=b;c=1;a=0;b=0;do{a:do if((d|0)>(b|0)){g=a;while(1){f=(k[B>>2]|0)+b|0;a=i[f>>0]|0;e=a&255;b:do if(((a&-32)<<24>>24==32?(i[1009320+e>>0]|0)!=0:0)?(i[m>>0]|0)==0:0){switch(a<<24>>24){case 62:case 60:{h=c;c=0;break a}case 38:break;default:break b}sc(f,d-b|0,(k[z>>2]|0)+c|0,u,t);g=(k[z>>2]|0)+c|0;k[s>>2]=l[1009576+(l[g>>0]|0)>>0];k[v>>2]=g;g=(vd(v,s)|0)&255}else w=15;while(0);if((w|0)==15){w=0;a=l[1009576+e>>0]|0;k[t>>2]=a;k[u>>2]=a;e=(k[z>>2]|0)+c|0;if((b|0)<(d+-3|0)){h=l[f>>0]|l[f+1>>0]<<8|l[f+2>>0]<<16|l[f+3>>0]<<24;i[e>>0]=h;i[e+1>>0]=h>>8;i[e+2>>0]=h>>16;i[e+3>>0]=h>>24}else tf(e|0,f|0,a|0)|0;g=(k[B>>2]|0)+b|0;k[s>>2]=l[1009576+(l[g>>0]|0)>>0];k[v>>2]=g;g=(vd(v,s)|0)&255}if((g|0)!=40&(g|0)!=(o|0)){if(!g){h=c;c=0;break a}d=k[u>>2]|0;h=(k[B>>2]|0)+b+d|0;k[s>>2]=l[1009576+(l[h>>0]|0)>>0];k[v>>2]=h;h=vd(v,s)|0;if(!(h<<24>>24==0|(h&255|0)==(o|0))?(i[n>>0]|0)!=0:0){h=c;c=g;break a}}else d=k[u>>2]|0;b=d+b|0;a=k[t>>2]|0;c=a+c|0;do if((d|0)!=(a|0))if((d|0)<(a|0)){Tc(y,d);Uc(y,a-d|0);break}else{Tc(y,a);Vc(y,d-a|0);break}else Tc(y,d);while(0);if((c|0)>40927)break;d=k[x>>2]|0;if((d|0)<=(b|0)){h=c;c=g;break a}}i[p>>0]=1;h=c;c=g}else{h=c;c=a}while(0);d=k[x>>2]|0;c:do if((d|0)>(b|0))while(1){wd((k[B>>2]|0)+b|0,d-b|0,s);d=k[s>>2]|0;k[u>>2]=d;b=d+b|0;Vc(y,d);d=k[x>>2]|0;if((d|0)<=(b|0)){a=c;break c}a=(k[B>>2]|0)+b|0;e=i[a>>0]|0;f=e&255;d:do if(((e&-32)<<24>>24==32?(i[1009320+f>>0]|0)!=0:0)?(i[m>>0]|0)==0:0)switch(e<<24>>24){case 60:{c=tc(a,d-b|0,k[j>>2]|0)|0;k[u>>2]=c;break d}case 62:{k[u>>2]=1;c=1;break d}case 38:{sc(a,d-b|0,(k[z>>2]|0)+h|0,u,t);c=(k[z>>2]|0)+h|0;k[s>>2]=l[1009576+(l[c>>0]|0)>>0];k[v>>2]=c;c=(vd(v,s)|0)&255;w=42;break d}default:{w=42;break d}}else w=41;while(0);if((w|0)==41){c=l[1009576+f>>0]|0;k[u>>2]=c;k[s>>2]=c;k[v>>2]=a;c=(vd(v,s)|0)&255;w=42}if((w|0)==42){w=0;if(c|0){a=c;break c}c=k[u>>2]|0}b=c+b|0;Vc(y,c);d=k[x>>2]|0;if((d|0)<=(b|0)){a=0;break}else c=0}else a=c;while(0);c=h+1|0;i[(k[z>>2]|0)+h>>0]=32;Uc(y,1);if((a|0)!=40&(a|0)!=(o|0))break;if((c|0)>=(q|0)){w=48;break}d=k[x>>2]|0}while((b|0)<(d|0));if((w|0)==48)i[p>>0]=1;e=k[x>>2]|0;e:do if((b|0)>0){a=(b|0)<(e|0);d=b;while(1){if(!a){b=d;break e}if((i[(k[B>>2]|0)+d>>0]&-64)<<24>>24!=-128){b=d;break e}b=d+-1|0;c=c+-1|0;if((d|0)>1)d=b;else break}}while(0);k[B>>2]=(k[B>>2]|0)+b;k[x>>2]=e-b;i[(k[z>>2]|0)+c>>0]=32;i[(k[z>>2]|0)+(c+1)>>0]=32;i[(k[z>>2]|0)+(c+2)>>0]=32;i[(k[z>>2]|0)+(c+3)>>0]=0;Uc(y,4);Rc(y);k[A>>2]=c;b=1}}r=C;return b|0}function zc(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,g=0;c=r;r=r+16|0;g=c+4|0;d=a+88|0;Oc(d);e=b+4|0;f=a+24|0;xd(k[b>>2]|0,(k[e>>2]|0)+3|0,k[f>>2]|0,61440,(i[a+16>>0]|0)!=0,c+8|0,g,c,d);a=k[g>>2]|0;i[(k[f>>2]|0)+a>>0]=0;k[b>>2]=k[f>>2];k[e>>2]=a+-3;Rc(d);r=c;return}function Ac(a,b){a=a|0;b=b|0;var c=0;c=yc(a,b)|0;zc(a,b);return c|0}function Bc(a){a=a|0;a=(a|0)<0?0:a;return k[15988+(((a|0)>101?0:a)<<2)>>2]|0}function Cc(a){a=a|0;a=(a|0)<0?0:a;return k[16396+(((a|0)>101?0:a)<<2)>>2]|0}function Dc(a){a=a|0;a=(a|0)<0?26:a;return k[11076+(((a|0)>613?26:a)<<2)>>2]|0}function Ec(a){a=a|0;a=(a|0)<0?26:a;return k[13532+(((a|0)>613?26:a)<<2)>>2]|0}function Fc(a){a=a|0;do switch(a|0){case 40:case 38:{a=1;break}case 105:{a=2;break}case 135:{a=2;break}case 17:{a=3;break}case 68:{a=3;break}case 84:{a=4;break}case 83:{a=4;break}case 78:{a=5;break}case 28:{a=5;break}case 29:{a=5;break}case 160:{a=5;break}case 35:{a=6;break}case 64:{a=6;break}case 51:{a=6;break}case 43:{a=6;break}case 10:{a=7;break}case 80:{a=7;break}case 1:{a=7;break}case 31:{a=8;break}case 14:{a=8;break}case 12:{a=8;break}case 143:{a=9;break}case 147:{a=9;break}default:a=0}while(0);return a|0}function Gc(a){a=a|0;if(a>>>0>101)a=26;else a=k[16804+(a<<2)>>2]|0;return a|0}function Hc(a){a=a|0;if((a|0)<512)a=i[992810+a>>0]|0;else a=0;return a|0}function Ic(a,b){a=a|0;b=b|0;do if(a>>>0<=101){if((k[16396+(a<<2)>>2]|0)>>>0<2){b=k[16804+(a<<2)>>2]|0;break}b=b&255;if((a|0)==1){b=m[941344+(b<<1)>>1]|0;break}else{b=m[941856+(b<<1)>>1]|0;break}}else b=26;while(0);return b|0}function Jc(a){a=a|0;if((a|0)<512)a=(m[941344+((l[992810+a>>0]|0)<<1)>>1]|0|0)==(a|0);else a=0;return a|0}function Kc(a){a=a|0;if((a|0)<512)a=(m[941856+((l[992810+a>>0]|0)<<1)>>1]|0|0)==(a|0);else a=0;return a|0}function Lc(a){a=a|0;var b=0,c=0,d=0,e=0;c=0;e=265;a:while(1)while(1){if((c|0)>=(e|0)){b=-1;break a}b=c+e>>1;d=_d(a,k[8956+(b<<3)>>2]|0)|0;if((d|0)<0){e=b;continue a}if((d|0)>0)c=b+1|0;else break a}return b|0}function Mc(a){a=a|0;switch(a|0){case 1:{a=0;break}case 3:{a=1;break}case 6:{a=2;break}default:a=3}return a|0}function Nc(a){a=a|0;var b=0;k[a>>2]=0;k[a+4>>2]=0;k[a+8>>2]=0;k[a+12>>2]=1;a=a+16|0;b=a+36|0;do{k[a>>2]=0;a=a+4|0}while((a|0)<(b|0));return}function Oc(a){a=a|0;var b=0;if(!(i[a>>0]&1)){i[a+1>>0]=0;i[a>>0]=0}else{i[k[a+8>>2]>>0]=0;k[a+4>>2]=0}k[a+12>>2]=1;a=a+16|0;b=a+36|0;do{k[a>>2]=0;a=a+4|0}while((a|0)<(b|0));return}function Pc(a){a=a|0;Ae(a);return}function Qc(a){a=a|0;var b=0,c=0,d=0,e=0,f=0,g=0,h=0,j=0;j=a+16|0;g=k[j>>2]|0;do if(g|0){h=a+12|0;b=k[h>>2]|0;if((b|0)==1){e=i[a>>0]|0;f=(e&1)==0;if(f)c=(e&255)>>>1;else c=k[a+4>>2]|0;if(c|0){if(f){c=(e&255)>>>1;d=a+1|0}else{c=k[a+4>>2]|0;d=k[a+8>>2]|0}d=i[d+(c+-1)>>0]|0;if((d&-64)<<24>>24==64?(g+(d&63)|0)>>>0<64:0){if(f){c=(e&255)>>>1;b=a+1|0}else{c=k[a+4>>2]|0;b=k[a+8>>2]|0}a=b+(c+-1)|0;i[a>>0]=(l[a>>0]|0)+g;k[j>>2]=0;break}}}if(g>>>0>63){c=g;d=0;e=30;while(1){b=c>>>e&63;if(d|(b|0)!=0){Ge(a,b&255);b=1}else b=0;if((e|0)<=6)break;c=k[j>>2]|0;d=b;e=e+-6|0}c=k[j>>2]|0;b=k[h>>2]|0}else c=g;Ge(a,(c&63|b<<6)&255);k[j>>2]=0}while(0);return}function Rc(a){a=a|0;Sc(a);a=a+20|0;k[a>>2]=0;k[a+4>>2]=0;k[a+8>>2]=0;k[a+12>>2]=0;k[a+16>>2]=0;k[a+20>>2]=0;return}function Sc(a){a=a|0;var b=0,c=0;if(!(k[a+16>>2]|0)){b=i[a>>0]|0;if(!(b&1))b=(b&255)>>>1;else b=k[a+4>>2]|0;if(!b)c=6}else c=6;if((c|0)==6){Tc(a,1);Qc(a)}return}function Tc(a,b){a=a|0;b=b|0;var c=0;do if(b|0){c=a+44|0;k[c>>2]=(k[c>>2]|0)+b;c=a+48|0;k[c>>2]=(k[c>>2]|0)+b;c=a+12|0;if((k[c>>2]|0)==1){a=a+16|0;k[a>>2]=(k[a>>2]|0)+b;break}else{Qc(a);k[c>>2]=1;k[a+16>>2]=b;break}}while(0);return}function Uc(a,b){a=a|0;b=b|0;var c=0,d=0,e=0;do if(b|0){d=a+48|0;k[d>>2]=(k[d>>2]|0)+b;d=a+12|0;e=k[d>>2]|0;if((e|0)==2){a=a+16|0;k[a>>2]=(k[a>>2]|0)+b;break}c=a+16|0;if((b|0)==1&(e|0)==3?(k[c>>2]|0)==1:0){k[d>>2]=1;break}Qc(a);k[d>>2]=2;k[c>>2]=b}while(0);return}function Vc(a,b){a=a|0;b=b|0;var c=0,d=0,e=0;do if(b|0){d=a+44|0;k[d>>2]=(k[d>>2]|0)+b;d=a+12|0;e=k[d>>2]|0;if((e|0)==3){a=a+16|0;k[a>>2]=(k[a>>2]|0)+b;break}c=a+16|0;if((b|0)==1&(e|0)==2?(k[c>>2]|0)==1:0){k[d>>2]=1;break}Qc(a);k[d>>2]=3;k[c>>2]=b}while(0);return}function Wc(a,b,c,d,e,f,g){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;g=g|0;var h=0,l=0,n=0,o=0,p=0,q=0,s=0,t=0;t=r;r=r+16|0;l=t;od(f,l);h=k[l>>2]|0;o=Ic(a,h&255)|0;l=k[l+4>>2]|0;q=Ic(a,l&255)|0;n=f+16|0;if((d|0)>0)s=(m[n+(h<<1)>>1]<<10|0)/(d|0)|0;else s=0;p=(Mc(a)|0)+(o<<2)|0;p=j[(k[(k[e+140>>2]|0)+32>>2]|0)+(p<<1)>>1]|0;j[g>>1]=c;j[g+2>>1]=b;j[g+4>>1]=o;j[g+6>>1]=q;c=j[n+(h<<1)>>1]|0;j[g+8>>1]=c;h=j[n+(l<<1)>>1]|0;j[g+10>>1]=h;j[g+12>>1]=d;e=k[f+12>>2]|0;j[g+14>>1]=e;j[g+16>>1]=a;e=(nb(c&65535,h&65535,e&65535)|0)&255;h=g+18|0;i[h>>0]=e;e=Fc(o)|0;if(e|0?(e|0)==(Fc(q)|0):0)i[h>>0]=100;s=(ob(s,p)|0)&255;i[g+19>>0]=s;r=t;return}function Xc(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0;e=(k[a+8>>2]|0)==1;f=e?a+96|0:a+116|0;d=e?a+16|0:a+36|0;c=k[d+4>>2]|0;if(c|0)ib(c,b);c=k[d+8>>2]|0;if(c|0)ib(c,b);c=k[d+12>>2]|0;if(c|0)ib(c,b);c=k[d+16>>2]|0;if(c|0)ib(c,b);e=e?a+56|0:a+76|0;c=k[f+4>>2]|0;if(c|0)ib(c,b);c=k[f+8>>2]|0;if(c|0)ib(c,b);c=k[f+12>>2]|0;if(c|0)ib(c,b);c=k[f+16>>2]|0;if(c|0)ib(c,b);d=b+16|0;c=k[e+4>>2]|0;if(c|0)j[d+((c>>>8&255)<<1)>>1]=0;c=k[e+8>>2]|0;if(c|0)j[d+((c>>>8&255)<<1)>>1]=0;c=k[e+12>>2]|0;if(c|0)j[d+((c>>>8&255)<<1)>>1]=0;c=k[e+16>>2]|0;if(c|0)j[d+((c>>>8&255)<<1)>>1]=0;return}function Yc(a,b,c,d,e,f,g){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;g=g|0;var h=0,l=0,n=0,o=0,p=0,q=0,s=0,t=0;t=r;r=r+16|0;h=t;s=k[b+56064+(c<<2)>>2]|0;q=k[b+56064+(c+1<<2)>>2]|0;ld(f);k[e+16>>2]=0;k[e+20>>2]=0;if(i[d+7>>0]|0){p=k[d>>2]|0;k[h>>2]=s;k[h+4>>2]=q;pe(p,1017069,h)}k[e>>2]=s;k[e+12>>2]=q-s;if((q|0)>(s|0)){h=d+96|0;l=d+8|0;n=d+116|0;p=s;do{o=k[b+24056+(p<<3)+4>>2]|0;ib(o,f);e=b+24056+(p<<3)+2|0;c=j[e>>1]|0;if((c&65535)<2){md(f);c=j[e>>1]|0}if(c<<16>>16==3){e=(k[l>>2]|0)==1?h:n;c=k[e>>2]|0;k[e+4+(c<<2)>>2]=o;k[e>>2]=c+1&3}p=p+1|0}while((p|0)!=(q|0))}Xc(d,f);p=m[b+24056+(s<<3)>>1]|0;Wc(a,s,p,(m[b+24056+(q<<3)>>1]|0)-p|0,d,f,g);k[d+12>>2]=m[g+4>>1];r=t;return}function Zc(a,b,c,d,e){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;var f=0,g=0,h=0,i=0,l=0,m=0,n=0,o=0,p=0,q=0,s=0,t=0,u=0,v=0,w=0,x=0;x=r;r=r+608|0;w=x+552|0;s=x+528|0;p=x;q=x+576|0;k[w>>2]=0;k[w+4>>2]=0;k[w+8>>2]=0;k[w+12>>2]=0;k[w+16>>2]=0;k[w+20>>2]=0;k[s>>2]=0;k[s+4>>2]=0;k[s+8>>2]=0;k[s+12>>2]=0;k[s+16>>2]=0;k[s+20>>2]=0;g=b+24|0;if((k[g>>2]|0)>0){i=s+12|0;l=s+16|0;m=s+4|0;n=s+20|0;o=s+8|0;t=0;do{kd(p);Yc(a,b,t,c,s,p,q);h=k[d>>2]|0;if((h|0)<50){u=d+4+(h*20|0)|0;f=q;v=u+20|0;do{j[u>>1]=j[f>>1]|0;u=u+2|0;f=f+2|0}while((u|0)<(v|0));h=(k[d>>2]|0)+1|0;k[d>>2]=h};k[w>>2]=k[s>>2];k[w+4>>2]=k[s+4>>2];k[w+8>>2]=k[s+8>>2];k[w+12>>2]=k[s+12>>2];k[w+16>>2]=k[s+16>>2];k[w+20>>2]=k[s+20>>2];k[s>>2]=(k[s>>2]|0)+(k[i>>2]|0);k[m>>2]=(k[m>>2]|0)+(k[l>>2]|0);k[o>>2]=(k[o>>2]|0)+(k[n>>2]|0);t=t+1|0}while((t|0)<(k[g>>2]|0))}else h=k[d>>2]|0;i=k[b+20>>2]|0;f=j[b+24056+(i<<3)>>1]|0;g=d+4+(h*20|0)|0;u=g;v=u+20|0;do{j[u>>1]=0;u=u+2|0}while((u|0)<(v|0));j[g>>1]=f;j[d+4+(h*20|0)+2>>1]=i;k[e>>2]=k[w>>2];k[e+4>>2]=k[w+4>>2];k[e+8>>2]=k[w+8>>2];k[e+12>>2]=k[w+12>>2];k[e+16>>2]=k[w+16>>2];k[e+20>>2]=k[w+20>>2];r=x;return}function _c(a,b){a=a|0;b=b|0;var c=0,d=0,e=0;if((k[a>>2]|0)>0){c=0;do{e=l[a+4+(c*20|0)+18>>0]|0;d=l[a+4+(c*20|0)+19>>0]|0;qd(b,j[a+4+(c*20|0)+4>>1]|0,m[a+4+(c*20|0)+12>>1]|0,m[a+4+(c*20|0)+8>>1]|0,e>>>0<d>>>0?e:d);c=c+1|0}while((c|0)<(k[a>>2]|0))}return}function $c(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0,i=0,j=0,l=0,m=0,n=0,o=0,p=0,q=0,s=0,t=0;p=r;r=r+112|0;o=p+96|0;n=p+80|0;l=p+64|0;h=p+48|0;g=p+32|0;i=p+16|0;e=p;t=Bc(k[c>>2]|0)|0;d=c+8|0;s=k[d>>2]|0;j=c+12|0;q=k[j>>2]|0;m=c+16|0;f=k[m>>2]|0;k[e>>2]=t;k[e+4>>2]=s;k[e+8>>2]=q;k[e+12>>2]=f;pe(a,1017096,e);e=c+4|0;a:do if((k[e>>2]|0)>0){f=0;do{if((f|0)<(k[d>>2]|0)){s=k[c+32+(f<<3)>>2]|0;t=k[c+32+(f<<3)+4>>2]|0;oc(b+s|0,6);k[i>>2]=f;k[i+4>>2]=s;k[i+8>>2]=(t|0)<0?(t&2147483647)+2e9|0:t;k[i+12>>2]=1097824;pe(a,1017160,i)}if((f|0)<(k[j>>2]|0)){s=k[c+8040+(f<<3)>>2]|0;t=k[c+8040+(f<<3)+4>>2]|0;oc(b+s|0,12);k[g>>2]=f;k[g+4>>2]=s;k[g+8>>2]=t;k[g+12>>2]=1097824;pe(a,1017175,g)}if((f|0)<(k[m>>2]|0)){s=k[c+16048+(f<<3)>>2]|0;t=k[c+16048+(f<<3)+4>>2]|0;oc(b+s|0,12);k[h>>2]=f;k[h+4>>2]=s;k[h+8>>2]=t;k[h+12>>2]=1097824;pe(a,1017191,h)}if((f|0)<(k[d>>2]|0))se(1017206,5,1,a);if((f|0)>50)break a;f=f+1|0}while((f|0)<(k[e>>2]|0))}while(0);d=k[d>>2]|0;if((d|0)>50){s=k[c+32+(d<<3)>>2]|0;t=k[c+32+(d<<3)+4>>2]|0;oc(b+s|0,6);k[l>>2]=d;k[l+4>>2]=s;k[l+8>>2]=(t|0)<0?(t&2147483647)+2e9|0:t;k[l+12>>2]=1097824;pe(a,1017160,l)}d=k[j>>2]|0;if((d|0)>50){s=k[c+8040+(d<<3)>>2]|0;t=k[c+8040+(d<<3)+4>>2]|0;oc(b+s|0,12);k[n>>2]=d;k[n+4>>2]=s;k[n+8>>2]=t;k[n+12>>2]=1097824;pe(a,1017175,n)}d=k[m>>2]|0;if((d|0)>50){s=k[c+16048+(d<<3)>>2]|0;t=k[c+16048+(d<<3)+4>>2]|0;oc(b+s|0,12);k[o>>2]=d;k[o+4>>2]=s;k[o+8>>2]=t;k[o+12>>2]=1097824;pe(a,1017191,o)}se(1017206,5,1,a);r=p;return}function ad(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0,j=0,l=0,n=0,o=0;l=r;r=r+48|0;j=l+40|0;g=l+16|0;h=l+8|0;d=l;f=c+20|0;k[d>>2]=k[f>>2];pe(a,1017212,d);d=k[f>>2]|0;if((d|0)>=0){e=0;while(1){if(!((e|0)>50&(e|0)<(d+-1|0))){o=m[c+24056+(e<<3)>>1]|0;n=i[1017266+(m[c+24056+(e<<3)+2>>1]|0)>>0]|0;d=k[c+24056+(e<<3)+4>>2]|0;oc(b+o|0,6);k[g>>2]=e;k[g+4>>2]=o;k[g+8>>2]=n;k[g+12>>2]=d;k[g+16>>2]=1097824;pe(a,1017271,g);d=k[f>>2]|0}if((e|0)<(d|0))e=e+1|0;else break}}se(1017206,5,1,a);e=c+24|0;k[h>>2]=k[e>>2];pe(a,1017242,h);if((k[e>>2]|0)>=0){d=0;while(1){o=k[c+56064+(d<<2)>>2]|0;k[j>>2]=d;k[j+4>>2]=o;pe(a,1017294,j);if((d|0)<(k[e>>2]|0))d=d+1|0;else break}}se(1017206,5,1,a);r=l;return}function bd(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,g=0,h=0,i=0,l=0,n=0,o=0,p=0;c=r;r=r+48|0;d=c;p=m[b>>1]|0;o=m[b+2>>1]|0;n=Ec(m[b+4>>1]|0)|0;l=m[b+8>>1]|0;i=Ec(m[b+6>>1]|0)|0;h=m[b+10>>1]|0;g=m[b+12>>1]|0;f=m[b+14>>1]|0;e=Bc(m[b+16>>1]|0)|0;b=j[b+18>>1]|0;k[d>>2]=p;k[d+4>>2]=o;k[d+8>>2]=n;k[d+12>>2]=l;k[d+16>>2]=i;k[d+20>>2]=h;k[d+24>>2]=g;k[d+28>>2]=f;k[d+32>>2]=e;k[d+36>>2]=b&255;k[d+40>>2]=(b&65535)>>>8&65535;pe(a,1017302,d);r=c;return}function cd(a,b){a=a|0;b=b|0;var c=0,d=0,e=0;e=r;r=r+16|0;d=e+8|0;c=e;k[c>>2]=k[b>>2];pe(a,1017351,c);se(1017382,101,1,a);if((k[b>>2]|0)>=0){c=0;while(1){k[d>>2]=c;pe(a,1017484,d);bd(a,b+4+(c*20|0)|0);if((c|0)<(k[b>>2]|0))c=c+1|0;else break}}se(1017206,5,1,a);r=e;return}function dd(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0,i=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0;d=k[a+140>>2]|0;if(b){t=k[d+4>>2]|0;r=0;s=t;b=d+8|0;d=d+12|0}else{r=1;s=k[d+16>>2]|0;t=k[d+20>>2]|0;b=d+24|0;d=d+28|0}e=k[b>>2]|0;b=k[d>>2]|0;o=c+8|0;d=k[o>>2]|0;p=k[c+12>>2]|0;q=k[c+16>>2]|0;j[c+24056>>1]=k[c+28>>2];j[c+24058>>1]=r;n=pb(Gc(k[a+8>>2]|0)|0,1)|0;k[c+24060>>2]=n;if((q|0)>0|((p|0)>0|(d|0)>0)){n=b+4|0;m=e+4|0;g=0;i=0;l=0;b=1;while(1){h=k[c+32+(g<<3)>>2]|0;f=k[c+8040+(i<<3)>>2]|0;a=k[c+16048+(l<<3)>>2]|0;do if((i|0)>=(p|0)|(f|0)>(h|0)|(f|0)>(a|0)){if(!((a|0)>(f|0)|((l|0)>=(q|0)|(a|0)>(h|0)))){f=l+1|0;e=k[(k[n>>2]|0)+(k[c+16048+(l<<3)+4>>2]<<2)>>2]|0;if(!e){a=i;e=f;break}j[c+24056+(b<<3)>>1]=a;j[c+24056+(b<<3)+2>>1]=3;k[c+24056+(b<<3)+4>>2]=e;a=i;e=f;b=b+1|0;break}f=k[c+32+(g<<3)+4>>2]|0;a=f&2147483647;f=(f|0)<0?t:s;g=g+1|0;e=k[f+8>>2]|0;if((a|0)<(e|0)){e=k[(k[f+4>>2]|0)+(a<<2)>>2]|0;if(!e){a=i;e=l;break}j[c+24056+(b<<3)>>1]=h;j[c+24056+(b<<3)+2>>1]=r;k[c+24056+(b<<3)+4>>2]=e;a=i;e=l;b=b+1|0;break}a=a-e+a|0;f=k[f+4>>2]|0;e=k[f+(a<<2)>>2]|0;a=k[f+(a+1<<2)>>2]|0;if(e){j[c+24056+(b<<3)>>1]=h;j[c+24056+(b<<3)+2>>1]=r;k[c+24056+(b<<3)+4>>2]=e;b=b+1|0}if(!a){a=i;e=l}else{j[c+24056+(b<<3)>>1]=h;j[c+24056+(b<<3)+2>>1]=r;k[c+24056+(b<<3)+4>>2]=a;a=i;e=l;b=b+1|0}}else{a=i+1|0;e=k[(k[m>>2]|0)+(k[c+8040+(i<<3)+4>>2]<<2)>>2]|0;if(!e)e=l;else{j[c+24056+(b<<3)>>1]=f;j[c+24056+(b<<3)+2>>1]=2;k[c+24056+(b<<3)+4>>2]=e;e=l;b=b+1|0}}while(0);if((e|0)<(q|0)|((a|0)<(p|0)|(g|0)<(d|0))){i=a;l=e}else break}d=k[o>>2]|0}else b=1;k[c+20>>2]=b;j[c+24056+(b<<3)>>1]=k[c+32+(d<<3)>>2];k[c+24056+(b<<3)+4>>2]=0;return}function ed(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0,i=0,j=0,l=0,n=0,o=0;o=b&1^1;i=b?50:20;j=c+20|0;e=k[j>>2]|0;b=k[c+8>>2]|0;if((b|0)>0){l=(i>>>1)+i|0;n=i<<1;h=b;f=0;d=0;b=a;do{if((h|0)>=(l|0))if((h|0)<(n|0))g=h+1>>1;else g=i;else g=h;k[c+56064+(d<<2)>>2]=f;k[c+56268+(d<<2)>>2]=b;d=d+1|0;if((f|0)<(e|0)&(g|0)>0){a=0;b=f;do{a=((m[c+24056+(b<<3)+2>>1]|0|0)==(o|0)&1)+a|0;b=b+1|0}while((b|0)<(e|0)&(a|0)<(g|0));f=b}b=m[c+24056+(f<<3)>>1]|0;h=h-g|0}while((h|0)>0);e=k[j>>2]|0}else{k[c+56064>>2]=0;k[c+56268>>2]=m[c+24056>>1];d=1;b=a}k[c+24>>2]=d;k[c+56064+(d<<2)>>2]=e;k[c+56268+(d<<2)>>2]=b;return}function fd(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0;j=r;r=r+1056|0;h=j+24|0;g=c+7|0;if(i[g>>0]|0){se(1017490,12,1,k[c>>2]|0);$c(k[c>>2]|0,k[a>>2]|0,f)}dd(c,e,f);ed(b,e,f);if(i[g>>0]|0){se(1017503,9,1,k[c>>2]|0);ad(k[c>>2]|0,k[a>>2]|0,f)}k[h>>2]=0;Zc(k[a+12>>2]|0,f,c,h,j);if(i[g>>0]|0)cd(k[c>>2]|0,h);_c(h,d);r=j;return}function gd(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;qd(d,(Gc(b)|0)&65535,a,a,100);k[c+12>>2]=26;return}function hd(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0,l=0,m=0,n=0,o=0,p=0,q=0,s=0;p=r;r=r+16|0;o=p;d=gf(56472)|0;k[d+4>>2]=1e3;l=d+8|0;k[d+8040>>2]=0;k[d+8044>>2]=0;k[d+16048>>2]=0;k[d+16052>>2]=0;j[d+24056>>1]=0;k[d+24060>>2]=0;k[d+56064>>2]=0;k[d+56268>>2]=0;k[l>>2]=0;k[l+4>>2]=0;k[l+8>>2]=0;k[l+12>>2]=0;k[l+16>>2]=0;k[l+20>>2]=0;k[l+24>>2]=0;k[l+28>>2]=0;k[d>>2]=k[a+12>>2];e=b+12|0;k[e>>2]=26;k[b+136>>2]=0;f=d+28|0;k[f>>2]=1;g=k[a+4>>2]|0;if((g|0)>1){h=b+7|0;n=b+140|0;m=1;do{if(i[h>>0]|0){q=k[b>>2]|0;k[o>>2]=m;k[o+4>>2]=g;pe(q,1017513,o)}q=m;m=jb(k[a>>2]|0,m,g,k[k[n>>2]>>2]|0,d)|0;s=k[n>>2]|0;kb(k[a>>2]|0,q,m,k[s+8>>2]|0,k[s+12>>2]|0,d);fd(a,q,b,c,1,d);k[l>>2]=0;k[l+4>>2]=0;k[l+8>>2]=0;k[l+12>>2]=0;k[l+16>>2]=0;k[f>>2]=m}while((m|0)<(g|0))}Qe(d);k[e>>2]=26;r=p;return}function id(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0,i=0,l=0,m=0;d=gf(56472)|0;k[d+4>>2]=1e3;g=d+8|0;k[d+8040>>2]=0;k[d+8044>>2]=0;k[d+16048>>2]=0;k[d+16052>>2]=0;j[d+24056>>1]=0;k[d+24060>>2]=0;k[d+56064>>2]=0;k[d+56268>>2]=0;k[g>>2]=0;k[g+4>>2]=0;k[g+8>>2]=0;k[g+12>>2]=0;k[g+16>>2]=0;k[g+20>>2]=0;k[g+24>>2]=0;k[g+28>>2]=0;k[d>>2]=k[a+12>>2];k[b+12>>2]=26;k[b+136>>2]=0;e=d+28|0;k[e>>2]=1;f=k[a+4>>2]|0;if((f|0)>1){i=b+140|0;h=1;do{m=k[i>>2]|0;l=h;h=lb(k[a>>2]|0,h,f,k[m+16>>2]|0,k[m+20>>2]|0,d)|0;m=k[i>>2]|0;mb(k[a>>2]|0,l,h,k[m+24>>2]|0,k[m+28>>2]|0,d);fd(a,l,b,c,0,d);k[g>>2]=0;k[g+4>>2]=0;k[g+8>>2]=0;k[g+12>>2]=0;k[g+16>>2]=0;k[e>>2]=h}while((h|0)<(f|0))}Qe(d);return}function jd(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0,j=0,l=0,m=0,n=0;g=r;r=r+32|0;f=g+16|0;e=g;d=g+20|0;if(!(i[b+7>>0]|0))d=a+12|0;else{l=k[b>>2]|0;h=a+12|0;n=Bc(k[h>>2]|0)|0;j=a+4|0;m=k[j>>2]|0;k[e>>2]=n;k[e+4>>2]=m;pe(l,1017545,e);ze(e,k[a>>2]|0,k[j>>2]|0);j=k[b>>2]|0;mc(d);k[f>>2]=(i[d>>0]&1)==0?d+1|0:k[d+8>>2]|0;pe(j,1017576,f);Ae(d);se(1017206,5,1,k[b>>2]|0);Ae(e);d=h}k[b+12>>2]=26;k[b+136>>2]=0;n=Cc(k[d>>2]|0)|0;switch(((n|0)!=3&(i[b+4>>0]|0)!=0?2:n)|0){case 1:case 0:{gd(k[a+4>>2]|0,k[a+12>>2]|0,b,c);break}case 3:{hd(a,b,c);break}case 2:{id(a,b,c);break}default:{}}r=g;return}function kd(a){a=a|0;k[a>>2]=0;k[a+4>>2]=0;k[a+8>>2]=0;k[a+12>>2]=0;return}function ld(a){a=a|0;k[a>>2]=0;k[a+4>>2]=0;k[a+8>>2]=0;k[a+12>>2]=0;return}function md(a){a=a|0;a=a+12|0;k[a>>2]=(k[a>>2]|0)+1;return}function nd(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0;b=b&255;d=b>>>2;e=sf(1,0,d|0)|0;f=L;h=a;g=k[h>>2]|0;h=k[h+4>>2]|0;if((g&e|0)==0&(h&f|0)==0){d=a+16+(d<<3)|0;k[d>>2]=0;k[d+4>>2]=0;d=a;k[d>>2]=g|e;k[d+4>>2]=h|f}a=a+16+(b<<1)|0;j[a>>1]=(m[a>>1]|0)+c;return}function od(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,l=0,n=0,o=0,p=0,q=0;q=r;r=r+16|0;p=q;k[b>>2]=-1;n=b+4|0;k[n>>2]=-1;o=b+8|0;k[o>>2]=-1;k[p>>2]=-1;k[p+4>>2]=-1;k[p+8>>2]=-1;d=a;c=k[d>>2]|0;d=k[d+4>>2]|0;if(!((c|0)==0&(d|0)==0)){i=a+16|0;j=p+8|0;l=p+4|0;g=0;while(1){if(!((c&1|0)==0&0==0)){h=0;do{e=h+g|0;f=m[i+(e<<1)>>1]|0;if((f|0)>(k[j>>2]|0)){a=k[l>>2]|0;if((f|0)>(a|0)){k[j>>2]=a;k[o>>2]=k[n>>2];a=k[p>>2]|0;if((f|0)>(a|0)){k[l>>2]=a;k[n>>2]=k[b>>2];a=0}else a=1}else a=2;k[p+(a<<2)>>2]=f;k[b+(a<<2)>>2]=e}h=h+1|0}while((h|0)!=4)}c=rf(c|0,d|0,1)|0;d=L;if((c|0)==0&(d|0)==0)break;else g=g+4|0}}r=q;return}function pd(a){a=a|0;var b=0;b=a+536|0;k[b>>2]=0;k[b+4>>2]=0;k[b+8>>2]=0;k[b+12>>2]=0;k[b+16>>2]=0;k[b+20>>2]=0;k[b+24>>2]=0;k[b+28>>2]=0;k[a>>2]=0;k[a+4>>2]=0;k[a+8>>2]=0;k[a+12>>2]=0;k[a+16>>2]=0;k[a+20>>2]=0;a=a+568|0;b=a+48|0;do{k[a>>2]=-1;a=a+4|0}while((a|0)<(b|0));return}function qd(a,b,c,d,e){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;var f=0,g=0,h=0,i=0,l=0,m=0;k[a>>2]=(k[a>>2]|0)+1;g=b&65535;l=g&15;m=j[a+568+(l<<1)>>1]|0;do if(m<<16>>16==b<<16>>16){b=a+616+(l<<2)|0;k[b>>2]=(k[b>>2]|0)+c;b=a+712+(l<<2)|0;k[b>>2]=(k[b>>2]|0)+d;d=ha(e,c)|0;a=a+808+(l<<2)|0;k[a>>2]=(k[a>>2]|0)+d}else{f=l^8;i=j[a+568+(f<<1)>>1]|0;if(i<<16>>16==b<<16>>16){b=a+616+(f<<2)|0;k[b>>2]=(k[b>>2]|0)+c;b=a+712+(f<<2)|0;k[b>>2]=(k[b>>2]|0)+d;d=ha(e,c)|0;a=a+808+(f<<2)|0;k[a>>2]=(k[a>>2]|0)+d;break}g=g&7|16;h=j[a+568+(g<<1)>>1]|0;if(h<<16>>16==b<<16>>16){b=a+616+(g<<2)|0;k[b>>2]=(k[b>>2]|0)+c;b=a+712+(g<<2)|0;k[b>>2]=(k[b>>2]|0)+d;d=ha(e,c)|0;a=a+808+(g<<2)|0;k[a>>2]=(k[a>>2]|0)+d;break}if(m<<16>>16!=-1){if(i<<16>>16!=-1)if(h<<16>>16==-1)f=g;else{f=(k[a+616+(f<<2)>>2]|0)<(k[a+616+(l<<2)>>2]|0)?f:l;f=(k[a+616+(g<<2)>>2]|0)<(k[a+616+(f<<2)>>2]|0)?g:f}}else f=l;j[a+568+(f<<1)>>1]=b;k[a+616+(f<<2)>>2]=c;k[a+712+(f<<2)>>2]=d;d=ha(e,c)|0;k[a+808+(f<<2)>>2]=d}while(0);return}function rd(a,b){a=a|0;b=b|0;var c=0,d=0;a:do if(!(k[a+4>>2]|0)){d=b&65535;c=d&15;if((j[a+568+(c<<1)>>1]|0)!=b<<16>>16){c=c^8;if((j[a+568+(c<<1)>>1]|0)!=b<<16>>16){c=d&7|16;c=(j[a+568+(c<<1)>>1]|0)==b<<16>>16?c:-1}}}else{c=0;while(1){if((j[a+568+(c<<1)>>1]|0)==b<<16>>16)break a;c=c+1|0;if((c|0)>=24){c=-1;break}}}while(0);return c|0}function sd(a){a=a|0;var b=0,c=0,d=0,e=0,f=0,g=0,h=0,i=0,l=0;l=0;while(1){g=a+568+(l<<1)|0;if((j[g>>1]|0)==-1)k[a+616+(l<<2)>>2]=-1;b=l;l=l+1|0;if((l|0)>=24)continue;h=a+616+(b<<2)|0;i=a+712+(b<<2)|0;e=a+808+(b<<2)|0;f=l;do{b=a+568+(f<<1)|0;c=a+616+(f<<2)|0;if((j[b>>1]|0)==-1){k[c>>2]=-1;d=-1}else d=k[c>>2]|0;if((k[h>>2]|0)<(d|0)){d=j[g>>1]|0;j[g>>1]=j[b>>1]|0;j[b>>1]=d;d=k[h>>2]|0;k[h>>2]=k[c>>2];k[c>>2]=d;d=k[i>>2]|0;c=a+712+(f<<2)|0;k[i>>2]=k[c>>2];k[c>>2]=d;c=k[e>>2]|0;d=a+808+(f<<2)|0;k[e>>2]=k[d>>2];k[d>>2]=c}f=f+1|0}while((f|0)!=24);if((l|0)==3)break}k[a+4>>2]=1;return}function td(a){a=a|0;var b=0,c=0,d=0,e=0,f=0,g=0,h=0,i=0;f=r;r=r+32|0;e=f+8|0;d=f;se(1017581,14,1,941016);c=0;do{b=j[a+568+(c<<1)>>1]|0;if(b<<16>>16!=-1){i=Ec(b&65535)|0;h=k[a+616+(c<<2)>>2]|0;g=k[a+712+(c<<2)>>2]|0;b=k[a+808+(c<<2)>>2]|0;k[e>>2]=c;k[e+4>>2]=i;k[e+8>>2]=h;k[e+12>>2]=g;k[e+16>>2]=b;pe(941016,1017620,e)}c=c+1|0}while((c|0)!=24);k[d>>2]=k[a>>2];pe(941016,1017596,d);r=f;return}function ud(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0;e=k[c>>2]|0;do if((e|0)>=1){f=k[b>>2]|0;g=(k[a+32>>2]|0)+(k[a>>2]|0)|0;d=k[a+16>>2]|0;h=i[f>>0]|0;a=h&255;if(h<<24>>24>-1){a=i[g+a>>0]|0;k[b>>2]=f+1;k[c>>2]=e+-1;break}if((e|0)>1&(a&224|0)==192){a=i[g+(l[g+a>>0]<<d)+(l[f+1>>0]|0)>>0]|0;k[b>>2]=f+2;k[c>>2]=e+-2;break}if((e|0)>2&(a&240|0)==224){a=g+(l[g+a>>0]<<d+4)|0;a=i[a+(i[a+(l[f+1>>0]|0)>>0]<<d)+(l[f+2>>0]|0)>>0]|0;k[b>>2]=f+3;k[c>>2]=e+-3;break}if((a&248|0)==240&(e|0)>3){a=g+(l[g+(l[g+a>>0]<<d)+(l[f+1>>0]|0)>>0]<<d+4)|0;a=i[a+(i[a+(l[f+2>>0]|0)>>0]<<d)+(l[f+3>>0]|0)>>0]|0;k[b>>2]=f+4;k[c>>2]=e+-4;break}else{k[b>>2]=f+1;k[c>>2]=e+-1;a=0;break}}else a=0;while(0);return a|0}function vd(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0;c=k[b>>2]|0;do if((c|0)>=1){d=k[a>>2]|0;f=i[d>>0]|0;e=f&255;if(f<<24>>24>-1){f=j[942368+(e<<1)>>1]|0;k[a>>2]=d+1;k[b>>2]=c+-1;c=f&255;break}if((c|0)>1&(e&224|0)==192){f=j[942368+((m[942368+(e<<1)>>1]|0)<<6<<1)+((l[d+1>>0]|0)<<1)>>1]|0;k[a>>2]=d+2;k[b>>2]=c+-2;c=f&255;break}if((c|0)>2&(e&240|0)==224){f=j[942368+((m[942368+((m[942368+(e<<1)>>1]|0)<<6<<1)+((l[d+1>>0]|0)<<1)>>1]|0)<<6<<1)+((l[d+2>>0]|0)<<1)>>1]|0;k[a>>2]=d+3;k[b>>2]=c+-3;c=f&255;break}if((e&248|0)==240&(c|0)>3){f=j[942368+((m[942368+((m[942368+((m[942368+(e<<1)>>1]|0)<<6<<1)+((l[d+1>>0]|0)<<1)>>1]|0)<<6<<1)+((l[d+2>>0]|0)<<1)>>1]|0)<<6<<1)+((l[d+3>>0]|0)<<1)>>1]|0;k[a>>2]=d+4;k[b>>2]=c+-4;c=f&255;break}else{k[a>>2]=d+1;k[b>>2]=c+-1;c=0;break}}else c=0;while(0);return c|0}function wd(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0,j=0,m=0;h=a+b|0;j=h+-7|0;k[c>>2]=0;if(b|0){b=a;a:do{b:do if(b>>>0<j>>>0){g=b;while(1){d=k[g>>2]|0;b=g+4|0;e=k[b>>2]|0;f=g+8|0;if((d+-656877351|d+1145324612|e+-656877351|e+1145324612)&-2139062144|0){if((i[1008008+(l[g+1>>0]|0)>>0]|i[1008008+(d&255)>>0]|i[1008008+(l[g+2>>0]|0)>>0]|i[1008008+(l[g+3>>0]|0)>>0])<<24>>24){b=g;break b}if((i[1008008+(l[g+5>>0]|0)>>0]|i[1008008+(e&255)>>0]|i[1008008+(l[g+6>>0]|0)>>0]|i[1008008+(l[g+7>>0]|0)>>0])<<24>>24)break b}if(f>>>0<j>>>0)g=f;else{b=f;break}}}while(0);if(b>>>0<h>>>0)d=993864;else{d=993864;m=13;break}while(1){f=i[d+(l[b>>0]|0)>>0]|0;e=b+1|0;if((f&255)>239)break;b=993864+((f&255)<<6)|0;if(e>>>0<h>>>0){d=b;b=e}else{d=b;b=e;m=13;break a}}c:do if((d-993864|0)>>>0>=64)do{b=b+-1|0;if(b>>>0<=a>>>0)break c}while((i[b>>0]&-64)<<24>>24==-128);while(0)}while(f<<24>>24==-3);d:do if((m|0)==13)if((d-993864|0)>>>0>=64)do{b=b+-1|0;if(b>>>0<=a>>>0)break d}while((i[b>>0]&-64)<<24>>24==-128);while(0);k[c>>2]=b-a}return}function xd(a,b,c,d,e,f,g,h,j){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;j=j|0;var n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0;H=(j|0)==0;I=(j|0)!=0;E=0;F=0;G=0;while(1){C=a;x=a+b|0;D=c;y=c+d|0;z=x;if((d|0)<(b|0))if(H){r=239;q=0;o=0;n=0}else{Tc(j,0);r=239;q=0;o=0;n=0}else{A=1009832;u=0;B=a;p=D;r=0;o=C;n=0;a:while(1){s=o;if(s>>>0<x>>>0){q=i[s>>0]|0;w=i[A+(q&255)>>0]|0;r=w&255;i[p>>0]=q;o=s+1|0;p=p+1|0;if((w&255)>239)w=q;else{w=B;A=1009832+(r<<6)|0;u=q;B=w;continue}}else w=u;if((r|0)<=239){J=43;break}b:do switch(r|0){case 247:{if(H)q=B;else{Tc(j,-2-B+o|0);Vc(j,2);q=o}i[p+-3>>0]=i[A+((w&255)+64)>>0]|0;A=1009832;u=0;B=q;p=p+-2|0;r=0;n=n+1|0;continue a}case 248:{if(H)q=B;else{Tc(j,o+~B|0);Vc(j,1);q=o}u=w&255;i[p+-3>>0]=i[A+(u+128)>>0]|0;i[p+-2>>0]=i[A+(u+64)>>0]|0;A=1009832;u=0;B=q;p=p+-1|0;r=0;n=n+1|0;continue a}case 246:{if(H)q=B;else{Tc(j,o+~B|0);Vc(j,1);q=o}i[p+-2>>0]=i[A+((w&255)+64)>>0]|0;A=1009832;u=0;B=q;p=p+-1|0;r=0;n=n+1|0;continue a}case 245:{q=w&255;r=p;i[r+-3>>0]=i[A+(q+192)>>0]|0;J=23;break}case 244:{q=w&255;r=p;J=23;break}case 243:{q=w&255;r=p;J=24;break}case 251:{i[p+-1>>0]=i[A+(w&255|256)>>0]|0;w=B;A=1009832;u=0;r=0;n=n+1|0;B=w;continue a}case 250:{q=A;r=w&255;if((q-1009832|0)>>>0<320){r=l[A+(r|512)>>0]<<8;break b}else{r=l[A+(r+128)>>0]<<8;J=29;break b}}case 249:case 252:{r=0;J=29;break}default:{J=10;break a}}while(0);if((J|0)==23){i[r+-2>>0]=i[A+(q+128)>>0]|0;J=24}else if((J|0)==29){J=0;q=A}if((J|0)==24){J=0;i[r+-1>>0]=i[A+(q+64)>>0]|0;w=B;A=1009832;u=0;r=0;n=n+1|0;B=w;continue}s=w&255;r=l[A+((q-1009832|0)>>>0<320?s|256:s+64|0)>>0]|r;s=974880+(r<<2)|0;v=l[s>>0]&127;q=l[974880+(r<<2)+1>>0]|0;if(!((q&128|0)==0|e)){s=r+1|0;q=l[974880+(s<<2)+1>>0]|0;s=974880+(s<<2)|0}u=q&127;r=m[s+2>>1]|0;q=p+(0-v)|0;t=q+u|0;if((y-t|0)<(z-o|0)){r=239;J=39;break}tf(q|0,1016936+r|0,u|0)|0;n=n+1|0;do if(!H){if(u>>>0>v>>>0){Tc(j,o-B|0);Uc(j,u-v|0);p=o;break}if(u>>>0<v>>>0){Tc(j,o-B-v+u|0);Vc(j,v-u|0);p=o}else p=B}else p=B;while(0);if((i[s>>0]|0)>=0){A=1009832;u=0;B=p;p=t;r=0;continue}r=l[1016936+(r+u)>>0]|0;A=1009832+(r<<6)|0;u=w;B=p;p=t}c:do if((J|0)==10)J=39;else if((J|0)==43){J=0;if((A-1009832|0)>>>0<320)r=241;else while(1){q=o+-1|0;o=q;p=p+-1|0;if(q>>>0<=a>>>0){r=240;break c}if((i[q>>0]&-64)<<24>>24!=-128){r=240;break}}}while(0);d:do if((J|0)==39){J=0;o=o+-1|0;p=p+-1|0;if((A-1009832|0)>>>0>=320)do{q=o+-1|0;o=q;p=p+-1|0;if(q>>>0<=a>>>0)break d}while((i[q>>0]&-64)<<24>>24==-128)}while(0);if(I&o>>>0>B>>>0)Tc(j,o-B|0);q=o-C|0;o=p-D|0}F=q+F|0;G=o+G|0;E=n+E|0;if((r|0)!=253)break;else{a=a+q|0;b=b-q|0;c=c+o|0;d=d-o|0}}k[f>>2]=F;k[g>>2]=G;k[h>>2]=E;return}function yd(a){a=a|0;if(a|0)Qe(a);return}function zd(a){a=a|0;return Ec(k[a>>2]|0)|0}function Ad(a){a=a|0;return i[a+4>>0]|0}function Bd(a){a=a|0;if(a|0){Cd(a);Qe(a)}return}function Cd(a){a=a|0;var b=0;b=k[a+4>>2]|0;if(b|0)Qe(b);b=k[a+8>>2]|0;if(b|0)Qe(b);b=k[a+12>>2]|0;if(b|0)Qe(b);return}function Dd(a,b,c){a=a|0;b=b|0;c=c|0;return Ed(b,c)|0}function Ed(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0;c=r;r=r+32|0;e=c+16|0;d=c+4|0;f=c+28|0;k[e>>2]=0;k[e+4>>2]=0;k[e+8>>2]=0;k[d>>2]=0;k[d+4>>2]=0;k[d+8>>2]=0;i[f>>0]=0;a=wb(a,Vd(a)|0,b,e,d,c,f)|0;b=gf(20)|0;Fd(b,(i[f>>0]|0)!=0,a,e,d);r=c;return b|0}function Fd(a,b,c,d,e){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;k[a>>2]=c;i[a+16>>0]=b&1;b=gf(8)|0;c=k[e>>2]&255;k[b>>2]=k[d>>2];i[b+4>>0]=c;k[a+4>>2]=b;b=gf(8)|0;c=k[e+4>>2]&255;k[b>>2]=k[d+4>>2];i[b+4>>0]=c;k[a+8>>2]=b;b=gf(8)|0;e=k[e+8>>2]&255;k[b>>2]=k[d+8>>2];i[b+4>>0]=e;k[a+12>>2]=b;return}function Gd(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;return Hd(b,c,d,e,f)|0}function Hd(a,b,c,d,e){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,l=0;f=r;r=r+80|0;l=f+56|0;h=f+40|0;g=f+28|0;j=f+72|0;k[l>>2]=e;k[l+4>>2]=c;k[l+8>>2]=d;k[l+12>>2]=26;k[h>>2]=0;k[h+4>>2]=0;k[h+8>>2]=0;k[g>>2]=0;k[g+4>>2]=0;k[g+8>>2]=0;i[j>>0]=0;e=xb(a,Vd(a)|0,b,l,h,g,f,f+24|0,j)|0;c=gf(20)|0;Fd(c,(i[j>>0]|0)!=0,e,h,g);r=f;return c|0}function Id(a){a=a|0;return (i[a+16>>0]|0)!=0|0}function Jd(a){a=a|0;return Ec(k[a>>2]|0)|0}function Kd(a,b){a=a|0;b=b|0;if(b>>>0>=3)Ka(0,b|0,3)|0;return k[a+4+(b<<2)>>2]|0}function Ld(a){a=a|0;if(a|0)Qe(a);return}function Md(a){a=a|0;return Ec(k[a>>2]|0)|0}function Nd(a){a=a|0;if(a|0)Qe(a);return}function Od(a){a=a|0;var b=0,c=0;b=r;r=r+16|0;c=b;k[c>>2]=k[a+60>>2];a=Pd(ua(6,c|0)|0)|0;r=b;return a|0}function Pd(a){a=a|0;var b=0;if(a>>>0>4294963200){b=Qd()|0;k[b>>2]=0-a;a=-1}return a|0}function Qd(){var a=0;if(!0)a=1097308;else{a=(Ha()|0)+64|0;a=k[a>>2]|0}return a|0}function Rd(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0,i=0,j=0,l=0,m=0,n=0,o=0,p=0;p=r;r=r+48|0;m=p+16|0;l=p;i=p+32|0;n=a+28|0;g=k[n>>2]|0;k[i>>2]=g;o=a+20|0;g=(k[o>>2]|0)-g|0;k[i+4>>2]=g;k[i+8>>2]=b;k[i+12>>2]=c;h=a+60|0;j=a+44|0;e=2;b=g+c|0;while(1){if(!0){k[m>>2]=k[h>>2];k[m+4>>2]=i;k[m+8>>2]=e;f=Pd(Ra(146,m|0)|0)|0}else{Oa(8,a|0);k[l>>2]=k[h>>2];k[l+4>>2]=i;k[l+8>>2]=e;f=Pd(Ra(146,l|0)|0)|0;ta(0)}if((b|0)==(f|0)){b=6;break}if((f|0)<0){b=8;break}b=b-f|0;d=k[i+4>>2]|0;if(f>>>0<=d>>>0)if((e|0)==2){k[n>>2]=(k[n>>2]|0)+f;g=d;d=i;e=2}else{g=d;d=i}else{g=k[j>>2]|0;k[n>>2]=g;k[o>>2]=g;g=k[i+12>>2]|0;f=f-d|0;d=i+8|0;e=e+-1|0}k[d>>2]=(k[d>>2]|0)+f;k[d+4>>2]=g-f;i=d}if((b|0)==6){m=k[j>>2]|0;k[a+16>>2]=m+(k[a+48>>2]|0);a=m;k[n>>2]=a;k[o>>2]=a}else if((b|0)==8){k[a+16>>2]=0;k[n>>2]=0;k[o>>2]=0;k[a>>2]=k[a>>2]|32;if((e|0)==2)c=0;else c=c-(k[i+4>>2]|0)|0}r=p;return c|0}function Sd(a){a=a|0;return}function Td(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0;e=r;r=r+32|0;f=e;d=e+20|0;k[f>>2]=k[a+60>>2];k[f+4>>2]=0;k[f+8>>2]=b;k[f+12>>2]=d;k[f+16>>2]=c;if((Pd(Qa(140,f|0)|0)|0)<0){k[d>>2]=-1;a=-1}else a=k[d>>2]|0;r=e;return a|0}function Ud(a,b){a=a|0;b=b|0;var c=0,d=0,e=0;d=b&255;a:do if(!d)a=a+(Vd(a)|0)|0;else{if(a&3){c=b&255;do{e=i[a>>0]|0;if(e<<24>>24==0?1:e<<24>>24==c<<24>>24)break a;a=a+1|0}while((a&3|0)!=0)}d=ha(d,16843009)|0;c=k[a>>2]|0;b:do if(!((c&-2139062144^-2139062144)&c+-16843009))do{e=c^d;if((e&-2139062144^-2139062144)&e+-16843009|0)break b;a=a+4|0;c=k[a>>2]|0}while(!((c&-2139062144^-2139062144)&c+-16843009|0));while(0);c=b&255;while(1){e=i[a>>0]|0;if(e<<24>>24==0?1:e<<24>>24==c<<24>>24)break;else a=a+1|0}}while(0);return a|0}function Vd(a){a=a|0;var b=0,c=0,d=0;d=a;a:do if(!(d&3))c=4;else{b=a;a=d;while(1){if(!(i[b>>0]|0))break a;b=b+1|0;a=b;if(!(a&3)){a=b;c=4;break}}}while(0);if((c|0)==4){while(1){b=k[a>>2]|0;if(!((b&-2139062144^-2139062144)&b+-16843009))a=a+4|0;else break}if((b&255)<<24>>24)do a=a+1|0;while((i[a>>0]|0)!=0)}return a-d|0}function Wd(a,b){a=+a;b=b|0;var c=0,d=0,e=0;p[t>>3]=a;c=k[t>>2]|0;d=k[t+4>>2]|0;e=rf(c|0,d|0,52)|0;e=e&2047;switch(e|0){case 0:{if(a!=0.0){a=+Wd(a*18446744073709551616.0,b);c=(k[b>>2]|0)+-64|0}else c=0;k[b>>2]=c;break}case 2047:break;default:{k[b>>2]=e+-1022;k[t>>2]=c;k[t+4>>2]=d&-2146435073|1071644672;a=+p[t>>3]}}return +a}function Xd(a,b){a=+a;b=b|0;return +(+Wd(a,b))}function Yd(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0;a:do if(!c)c=0;else{while(1){d=i[a>>0]|0;e=i[b>>0]|0;if(d<<24>>24!=e<<24>>24)break;c=c+-1|0;if(!c){c=0;break a}else{a=a+1|0;b=b+1|0}}c=(d&255)-(e&255)|0}while(0);return c|0}function Zd(a){a=a|0;var b=0,c=0;b=0;while(1){if((l[1094098+b>>0]|0)==(a|0)){c=2;break}b=b+1|0;if((b|0)==87){b=87;a=1094186;c=5;break}}if((c|0)==2)if(!b)b=1094186;else{a=1094186;c=5}if((c|0)==5)while(1){do{c=a;a=a+1|0}while((i[c>>0]|0)!=0);b=b+-1|0;if(!b){b=a;break}else c=5}return b|0}function _d(a,b){a=a|0;b=b|0;var c=0,d=0;d=i[a>>0]|0;c=i[b>>0]|0;if(d<<24>>24==0?1:d<<24>>24!=c<<24>>24)b=d;else{do{a=a+1|0;b=b+1|0;d=i[a>>0]|0;c=i[b>>0]|0}while(!(d<<24>>24==0?1:d<<24>>24!=c<<24>>24));b=d}return (b&255)-(c&255)|0}function $d(a,b){a=a|0;b=b|0;if(!a)a=0;else a=ae(a,b)|0;return a|0}function ae(a,b){a=a|0;b=b|0;do if(a){if(b>>>0<128){i[a>>0]=b;a=1;break}if(b>>>0<2048){i[a>>0]=b>>>6|192;i[a+1>>0]=b&63|128;a=2;break}if(b>>>0<55296|(b&-8192|0)==57344){i[a>>0]=b>>>12|224;i[a+1>>0]=b>>>6&63|128;i[a+2>>0]=b&63|128;a=3;break}if((b+-65536|0)>>>0<1048576){i[a>>0]=b>>>18|240;i[a+1>>0]=b>>>12&63|128;i[a+2>>0]=b>>>6&63|128;i[a+3>>0]=b&63|128;a=4;break}else{a=Qd()|0;k[a>>2]=84;a=-1;break}}else a=1;while(0);return a|0}function be(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,l=0,m=0,n=0,o=0,p=0,q=0;o=r;r=r+128|0;j=o+112|0;l=o;m=l;p=941128;q=m+112|0;do{k[m>>2]=k[p>>2];m=m+4|0;p=p+4|0}while((m|0)<(q|0));if((b+-1|0)>>>0>2147483646)if(!b){e=j;f=1;n=4}else{q=Qd()|0;k[q>>2]=75}else{e=a;f=b;n=4}if((n|0)==4?(q=-2-e|0,q=f>>>0>q>>>0?q:f,k[l+48>>2]=q,h=l+20|0,k[h>>2]=e,k[l+44>>2]=e,p=e+q|0,g=l+16|0,k[g>>2]=p,k[l+28>>2]=p,de(l,c,d)|0,q|0):0){q=k[h>>2]|0;i[q+(((q|0)==(k[g>>2]|0))<<31>>31)>>0]=0}r=o;return}function ce(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0;d=a+20|0;e=k[d>>2]|0;a=(k[a+16>>2]|0)-e|0;a=a>>>0>c>>>0?c:a;tf(e|0,b|0,a|0)|0;k[d>>2]=(k[d>>2]|0)+a;return c|0}function de(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0,j=0,l=0,m=0,n=0,o=0,p=0,q=0;q=r;r=r+224|0;m=q+120|0;p=q+80|0;o=q;n=q+136|0;d=p;e=d+40|0;do{k[d>>2]=0;d=d+4|0}while((d|0)<(e|0));k[m>>2]=k[c>>2];if((ee(0,b,m,o,p)|0)<0)c=-1;else{c=k[a>>2]|0;l=c&32;if((i[a+74>>0]|0)<1)k[a>>2]=c&-33;j=a+48|0;if(!(k[j>>2]|0)){d=a+44|0;e=k[d>>2]|0;k[d>>2]=n;f=a+28|0;k[f>>2]=n;g=a+20|0;k[g>>2]=n;k[j>>2]=80;h=a+16|0;k[h>>2]=n+80;c=ee(a,b,m,o,p)|0;if(e){Ua[k[a+36>>2]&7](a,0,0)|0;c=(k[g>>2]|0)==0?-1:c;k[d>>2]=e;k[j>>2]=0;k[h>>2]=0;k[f>>2]=0;k[g>>2]=0}}else c=ee(a,b,m,o,p)|0;p=k[a>>2]|0;k[a>>2]=p|l;c=(p&32|0)==0?c:-1}r=q;return c|0}function ee(a,b,c,d,e){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;var f=0,g=0,h=0,m=0.0,n=0,o=0,q=0,s=0,u=0,v=0,w=0.0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,aa=0,ba=0,ca=0,da=0,ea=0,fa=0,ga=0,ia=0;ia=r;r=r+624|0;ca=ia+24|0;ea=ia+16|0;da=ia+588|0;$=ia+576|0;ba=ia;W=ia+536|0;ga=ia+8|0;fa=ia+528|0;M=(a|0)!=0;N=W+40|0;V=N;W=W+39|0;X=ga+4|0;Y=da;Z=0-Y|0;_=$+12|0;$=$+11|0;aa=_;O=aa-Y|0;P=-2-Y|0;Q=aa+2|0;R=ca+288|0;S=da+9|0;T=S;U=da+8|0;f=0;g=0;o=0;x=b;a:while(1){do if((f|0)>-1)if((g|0)>(2147483647-f|0)){f=Qd()|0;k[f>>2]=75;f=-1;break}else{f=g+f|0;break}while(0);b=i[x>>0]|0;if(!(b<<24>>24)){K=244;break}else g=x;b:while(1){switch(b<<24>>24){case 37:{b=g;K=9;break b}case 0:{b=g;break b}default:{}}J=g+1|0;b=i[J>>0]|0;g=J}c:do if((K|0)==9)while(1){K=0;if((i[b+1>>0]|0)!=37)break c;g=g+1|0;b=b+2|0;if((i[b>>0]|0)==37)K=9;else break}while(0);v=g-x|0;if(M?(k[a>>2]&32|0)==0:0)fe(x,v,a)|0;if((g|0)!=(x|0)){g=v;x=b;continue}n=b+1|0;g=i[n>>0]|0;h=(g<<24>>24)+-48|0;if(h>>>0<10){J=(i[b+2>>0]|0)==36;n=J?b+3|0:n;g=i[n>>0]|0;s=J?h:-1;o=J?1:o}else s=-1;b=g<<24>>24;d:do if((b&-32|0)==32){h=0;do{if(!(1<<b+-32&75913))break d;h=1<<(g<<24>>24)+-32|h;n=n+1|0;g=i[n>>0]|0;b=g<<24>>24}while((b&-32|0)==32)}else h=0;while(0);do if(g<<24>>24==42){g=n+1|0;b=(i[g>>0]|0)+-48|0;if(b>>>0<10?(i[n+2>>0]|0)==36:0){k[e+(b<<2)>>2]=10;b=1;n=n+3|0;g=k[d+((i[g>>0]|0)+-48<<3)>>2]|0}else{if(o|0){f=-1;break a}if(!M){u=h;J=0;n=g;I=0;break}b=(k[c>>2]|0)+(4-1)&~(4-1);J=k[b>>2]|0;k[c>>2]=b+4;b=0;n=g;g=J}if((g|0)<0){u=h|8192;J=b;I=0-g|0}else{u=h;J=b;I=g}}else{b=(g<<24>>24)+-48|0;if(b>>>0<10){g=0;do{g=(g*10|0)+b|0;n=n+1|0;b=(i[n>>0]|0)+-48|0}while(b>>>0<10);if((g|0)<0){f=-1;break a}else{u=h;J=o;I=g}}else{u=h;J=o;I=0}}while(0);e:do if((i[n>>0]|0)==46){b=n+1|0;g=i[b>>0]|0;if(g<<24>>24!=42){h=(g<<24>>24)+-48|0;if(h>>>0<10)g=0;else{o=0;break}while(1){g=(g*10|0)+h|0;b=b+1|0;h=(i[b>>0]|0)+-48|0;if(h>>>0>=10){o=g;break e}}}b=n+2|0;g=(i[b>>0]|0)+-48|0;if(g>>>0<10?(i[n+3>>0]|0)==36:0){k[e+(g<<2)>>2]=10;o=k[d+((i[b>>0]|0)+-48<<3)>>2]|0;b=n+4|0;break}if(J|0){f=-1;break a}if(M){H=(k[c>>2]|0)+(4-1)&~(4-1);o=k[H>>2]|0;k[c>>2]=H+4}else o=0}else{o=-1;b=n}while(0);q=0;while(1){g=(i[b>>0]|0)+-65|0;if(g>>>0>57){f=-1;break a}H=b+1|0;g=i[1095990+(q*58|0)+g>>0]|0;h=g&255;if((h+-1|0)>>>0<8){b=H;q=h}else break}if(!(g<<24>>24)){f=-1;break}n=(s|0)>-1;do if(g<<24>>24==19)if(n){f=-1;break a}else K=52;else{if(n){k[e+(s<<2)>>2]=h;F=d+(s<<3)|0;G=k[F+4>>2]|0;K=ba;k[K>>2]=k[F>>2];k[K+4>>2]=G;K=52;break}if(!M){f=0;break a}he(ba,h,c)}while(0);if((K|0)==52?(K=0,!M):0){g=v;o=J;x=H;continue}s=i[b>>0]|0;s=(q|0)!=0&(s&15|0)==3?s&-33:s;h=u&-65537;G=(u&8192|0)==0?u:h;f:do switch(s|0){case 110:switch(q|0){case 0:{k[k[ba>>2]>>2]=f;g=v;o=J;x=H;continue a}case 1:{k[k[ba>>2]>>2]=f;g=v;o=J;x=H;continue a}case 2:{g=k[ba>>2]|0;k[g>>2]=f;k[g+4>>2]=((f|0)<0)<<31>>31;g=v;o=J;x=H;continue a}case 3:{j[k[ba>>2]>>1]=f;g=v;o=J;x=H;continue a}case 4:{i[k[ba>>2]>>0]=f;g=v;o=J;x=H;continue a}case 6:{k[k[ba>>2]>>2]=f;g=v;o=J;x=H;continue a}case 7:{g=k[ba>>2]|0;k[g>>2]=f;k[g+4>>2]=((f|0)<0)<<31>>31;g=v;o=J;x=H;continue a}default:{g=v;o=J;x=H;continue a}}case 112:{q=G|8;o=o>>>0>8?o:8;s=120;K=64;break}case 88:case 120:{q=G;K=64;break}case 111:{h=ba;g=k[h>>2]|0;h=k[h+4>>2]|0;if((g|0)==0&(h|0)==0)b=N;else{b=N;do{b=b+-1|0;i[b>>0]=g&7|48;g=rf(g|0,h|0,3)|0;h=L}while(!((g|0)==0&(h|0)==0))}if(!(G&8)){g=G;q=0;n=1096470;K=77}else{q=V-b|0;g=G;o=(o|0)>(q|0)?o:q+1|0;q=0;n=1096470;K=77}break}case 105:case 100:{g=ba;b=k[g>>2]|0;g=k[g+4>>2]|0;if((g|0)<0){b=pf(0,0,b|0,g|0)|0;g=L;h=ba;k[h>>2]=b;k[h+4>>2]=g;h=1;n=1096470;K=76;break f}if(!(G&2048)){n=G&1;h=n;n=(n|0)==0?1096470:1096472;K=76}else{h=1;n=1096471;K=76}break}case 117:{g=ba;b=k[g>>2]|0;g=k[g+4>>2]|0;h=0;n=1096470;K=76;break}case 99:{i[W>>0]=k[ba>>2];b=W;s=1;v=0;u=1096470;g=N;break}case 109:{g=Qd()|0;g=Zd(k[g>>2]|0)|0;K=82;break}case 115:{g=k[ba>>2]|0;g=g|0?g:1096480;K=82;break}case 67:{k[ga>>2]=k[ba>>2];k[X>>2]=0;k[ba>>2]=ga;b=ga;o=-1;K=86;break}case 83:{b=k[ba>>2]|0;if(!o){ke(a,32,I,0,G);b=0;K=97}else K=86;break}case 65:case 71:case 70:case 69:case 97:case 103:case 102:case 101:{m=+p[ba>>3];k[ea>>2]=0;p[t>>3]=m;if((k[t+4>>2]|0)>=0)if(!(G&2048)){F=G&1;E=F;F=(F|0)==0?1096488:1096493}else{E=1;F=1096490}else{m=-m;E=1;F=1096487}p[t>>3]=m;D=k[t+4>>2]&2146435072;do if(D>>>0<2146435072|(D|0)==2146435072&0<0){w=+Xd(m,ea)*2.0;g=w!=0.0;if(g)k[ea>>2]=(k[ea>>2]|0)+-1;B=s|32;if((B|0)==97){u=s&32;x=(u|0)==0?F:F+9|0;v=E|2;b=12-o|0;do if(!(o>>>0>11|(b|0)==0)){m=8.0;do{b=b+-1|0;m=m*16.0}while((b|0)!=0);if((i[x>>0]|0)==45){m=-(m+(-w-m));break}else{m=w+m-m;break}}else m=w;while(0);g=k[ea>>2]|0;b=(g|0)<0?0-g|0:g;b=ie(b,((b|0)<0)<<31>>31,_)|0;if((b|0)==(_|0)){i[$>>0]=48;b=$}i[b+-1>>0]=(g>>31&2)+43;q=b+-2|0;i[q>>0]=s+15;n=(o|0)<1;h=(G&8|0)==0;g=da;while(1){F=~~m;b=g+1|0;i[g>>0]=l[1096454+F>>0]|u;m=(m-+(F|0))*16.0;do if((b-Y|0)==1){if(h&(n&m==0.0))break;i[b>>0]=46;b=g+2|0}while(0);if(!(m!=0.0))break;else g=b}h=q;o=(o|0)!=0&(P+b|0)<(o|0)?Q+o-h|0:O-h+b|0;n=o+v|0;ke(a,32,I,n,G);if(!(k[a>>2]&32))fe(x,v,a)|0;ke(a,48,I,n,G^65536);g=b-Y|0;if(!(k[a>>2]&32))fe(da,g,a)|0;b=aa-h|0;ke(a,48,o-(g+b)|0,0,0);if(!(k[a>>2]&32))fe(q,b,a)|0;ke(a,32,I,n,G^8192);b=(n|0)<(I|0)?I:n;break}b=(o|0)<0?6:o;if(g){g=(k[ea>>2]|0)+-28|0;k[ea>>2]=g;m=w*268435456.0}else{m=w;g=k[ea>>2]|0}D=(g|0)<0?ca:R;C=D;h=D;do{A=~~m>>>0;k[h>>2]=A;h=h+4|0;m=(m-+(A>>>0))*1.0e9}while(m!=0.0);g=k[ea>>2]|0;if((g|0)>0){n=D;o=h;while(1){q=(g|0)>29?29:g;g=o+-4|0;do if(g>>>0>=n>>>0){h=0;do{z=sf(k[g>>2]|0,0,q|0)|0;z=of(z|0,L|0,h|0,0)|0;A=L;y=Cf(z|0,A|0,1e9,0)|0;k[g>>2]=y;h=Bf(z|0,A|0,1e9,0)|0;g=g+-4|0}while(g>>>0>=n>>>0);if(!h)break;n=n+-4|0;k[n>>2]=h}while(0);h=o;while(1){if(h>>>0<=n>>>0)break;g=h+-4|0;if(!(k[g>>2]|0))h=g;else break}g=(k[ea>>2]|0)-q|0;k[ea>>2]=g;if((g|0)>0)o=h;else break}}else n=D;if((g|0)<0){x=((b+25|0)/9|0)+1|0;y=(B|0)==102;do{v=0-g|0;v=(v|0)>9?9:v;do if(n>>>0<h>>>0){g=(1<<v)+-1|0;o=1e9>>>v;u=0;q=n;do{A=k[q>>2]|0;k[q>>2]=(A>>>v)+u;u=ha(A&g,o)|0;q=q+4|0}while(q>>>0<h>>>0);g=(k[n>>2]|0)==0?n+4|0:n;if(!u){n=g;g=h;break}k[h>>2]=u;n=g;g=h+4|0}else{n=(k[n>>2]|0)==0?n+4|0:n;g=h}while(0);h=y?D:n;h=(g-h>>2|0)>(x|0)?h+(x<<2)|0:g;g=(k[ea>>2]|0)+v|0;k[ea>>2]=g}while((g|0)<0);x=n;y=h}else{x=n;y=h}do if(x>>>0<y>>>0){g=(C-x>>2)*9|0;n=k[x>>2]|0;if(n>>>0<10)break;else h=10;do{h=h*10|0;g=g+1|0}while(n>>>0>=h>>>0)}else g=0;while(0);z=(B|0)==103;A=(b|0)!=0;h=b-((B|0)!=102?g:0)+((A&z)<<31>>31)|0;if((h|0)<(((y-C>>2)*9|0)+-9|0)){o=h+9216|0;h=D+4+(((o|0)/9|0)+-1024<<2)|0;o=((o|0)%9|0)+1|0;if((o|0)<9){n=10;do{n=n*10|0;o=o+1|0}while((o|0)!=9)}else n=10;u=k[h>>2]|0;v=(u>>>0)%(n>>>0)|0;o=(h+4|0)==(y|0);do if(o&(v|0)==0)n=x;else{w=(((u>>>0)/(n>>>0)|0)&1|0)==0?9007199254740992.0:9007199254740994.0;q=(n|0)/2|0;if(v>>>0<q>>>0)m=.5;else m=o&(v|0)==(q|0)?1.0:1.5;do if(E){if((i[F>>0]|0)!=45)break;w=-w;m=-m}while(0);o=u-v|0;k[h>>2]=o;if(!(w+m!=w)){n=x;break}B=o+n|0;k[h>>2]=B;if(B>>>0>999999999){g=x;while(1){n=h+-4|0;k[h>>2]=0;if(n>>>0<g>>>0){g=g+-4|0;k[g>>2]=0}B=(k[n>>2]|0)+1|0;k[n>>2]=B;if(B>>>0>999999999)h=n;else{q=g;h=n;break}}}else q=x;g=(C-q>>2)*9|0;o=k[q>>2]|0;if(o>>>0<10){n=q;break}else n=10;do{n=n*10|0;g=g+1|0}while(o>>>0>=n>>>0);n=q}while(0);h=h+4|0;x=n;h=y>>>0>h>>>0?h:y}else h=y;v=0-g|0;B=h;while(1){if(B>>>0<=x>>>0){y=0;break}h=B+-4|0;if(!(k[h>>2]|0))B=h;else{y=1;break}}do if(z){b=(A&1^1)+b|0;if((b|0)>(g|0)&(g|0)>-5){s=s+-1|0;b=b+-1-g|0}else{s=s+-2|0;b=b+-1|0}h=G&8;if(h|0)break;do if(y){h=k[B+-4>>2]|0;if(!h){n=9;break}if(!((h>>>0)%10|0)){o=10;n=0}else{n=0;break}do{o=o*10|0;n=n+1|0}while(!((h>>>0)%(o>>>0)|0|0))}else n=9;while(0);h=((B-C>>2)*9|0)+-9|0;if((s|32|0)==102){h=h-n|0;h=(h|0)<0?0:h;b=(b|0)<(h|0)?b:h;h=0;break}else{h=h+g-n|0;h=(h|0)<0?0:h;b=(b|0)<(h|0)?b:h;h=0;break}}else h=G&8;while(0);u=b|h;o=(u|0)!=0&1;q=(s|32|0)==102;if(q){g=(g|0)>0?g:0;s=0}else{n=(g|0)<0?v:g;n=ie(n,((n|0)<0)<<31>>31,_)|0;if((aa-n|0)<2)do{n=n+-1|0;i[n>>0]=48}while((aa-n|0)<2);i[n+-1>>0]=(g>>31&2)+43;C=n+-2|0;i[C>>0]=s;g=aa-C|0;s=C}v=E+1+b+o+g|0;ke(a,32,I,v,G);if(!(k[a>>2]&32))fe(F,E,a)|0;ke(a,48,I,v,G^65536);do if(q){n=x>>>0>D>>>0?D:x;h=n;do{g=ie(k[h>>2]|0,0,S)|0;do if((h|0)==(n|0)){if((g|0)!=(S|0))break;i[U>>0]=48;g=U}else{if(g>>>0<=da>>>0)break;qf(da|0,48,g-Y|0)|0;do g=g+-1|0;while(g>>>0>da>>>0)}while(0);if(!(k[a>>2]&32))fe(g,T-g|0,a)|0;h=h+4|0}while(h>>>0<=D>>>0);do if(u|0){if(k[a>>2]&32|0)break;fe(1096522,1,a)|0}while(0);if((b|0)>0&h>>>0<B>>>0)while(1){g=ie(k[h>>2]|0,0,S)|0;if(g>>>0>da>>>0){qf(da|0,48,g-Y|0)|0;do g=g+-1|0;while(g>>>0>da>>>0)}if(!(k[a>>2]&32))fe(g,(b|0)>9?9:b,a)|0;h=h+4|0;g=b+-9|0;if(!((b|0)>9&h>>>0<B>>>0)){b=g;break}else b=g}ke(a,48,b+9|0,9,0)}else{q=y?B:x+4|0;if((b|0)>-1){o=(h|0)==0;n=x;do{g=ie(k[n>>2]|0,0,S)|0;if((g|0)==(S|0)){i[U>>0]=48;g=U}do if((n|0)==(x|0)){h=g+1|0;if(!(k[a>>2]&32))fe(g,1,a)|0;if(o&(b|0)<1){g=h;break}if(k[a>>2]&32|0){g=h;break}fe(1096522,1,a)|0;g=h}else{if(g>>>0<=da>>>0)break;qf(da|0,48,g+Z|0)|0;do g=g+-1|0;while(g>>>0>da>>>0)}while(0);h=T-g|0;if(!(k[a>>2]&32))fe(g,(b|0)>(h|0)?h:b,a)|0;b=b-h|0;n=n+4|0}while(n>>>0<q>>>0&(b|0)>-1)}ke(a,48,b+18|0,18,0);if(k[a>>2]&32|0)break;fe(s,aa-s|0,a)|0}while(0);ke(a,32,I,v,G^8192);b=(v|0)<(I|0)?I:v}else{q=(s&32|0)!=0;o=m!=m|0.0!=0.0;g=o?0:E;n=g+3|0;ke(a,32,I,n,h);b=k[a>>2]|0;if(!(b&32)){fe(F,g,a)|0;b=k[a>>2]|0}if(!(b&32))fe(o?(q?1096514:1096518):q?1096506:1096510,3,a)|0;ke(a,32,I,n,G^8192);b=(n|0)<(I|0)?I:n}while(0);g=b;o=J;x=H;continue a}default:{b=x;h=G;s=o;v=0;u=1096470;g=N}}while(0);g:do if((K|0)==64){h=ba;g=k[h>>2]|0;h=k[h+4>>2]|0;n=s&32;if(!((g|0)==0&(h|0)==0)){b=N;do{b=b+-1|0;i[b>>0]=l[1096454+(g&15)>>0]|n;g=rf(g|0,h|0,4)|0;h=L}while(!((g|0)==0&(h|0)==0));K=ba;if((q&8|0)==0|(k[K>>2]|0)==0&(k[K+4>>2]|0)==0){g=q;q=0;n=1096470;K=77}else{g=q;q=2;n=1096470+(s>>4)|0;K=77}}else{b=N;g=q;q=0;n=1096470;K=77}}else if((K|0)==76){b=ie(b,g,N)|0;g=G;q=h;K=77}else if((K|0)==82){K=0;G=je(g,0,o)|0;F=(G|0)==0;b=g;s=F?o:G-g|0;v=0;u=1096470;g=F?g+o|0:G}else if((K|0)==86){K=0;h=0;g=0;q=b;while(1){n=k[q>>2]|0;if(!n)break;g=$d(fa,n)|0;if((g|0)<0|g>>>0>(o-h|0)>>>0)break;h=g+h|0;if(o>>>0>h>>>0)q=q+4|0;else break}if((g|0)<0){f=-1;break a}ke(a,32,I,h,G);if(!h){b=0;K=97}else{n=0;while(1){g=k[b>>2]|0;if(!g){b=h;K=97;break g}g=$d(fa,g)|0;n=g+n|0;if((n|0)>(h|0)){b=h;K=97;break g}if(!(k[a>>2]&32))fe(fa,g,a)|0;if(n>>>0>=h>>>0){b=h;K=97;break}else b=b+4|0}}}while(0);if((K|0)==97){K=0;ke(a,32,I,b,G^8192);g=(I|0)>(b|0)?I:b;o=J;x=H;continue}if((K|0)==77){K=0;h=(o|0)>-1?g&-65537:g;g=ba;g=(k[g>>2]|0)!=0|(k[g+4>>2]|0)!=0;if((o|0)!=0|g){s=(g&1^1)+(V-b)|0;s=(o|0)>(s|0)?o:s;v=q;u=n;g=N}else{b=N;s=0;v=q;u=n;g=N}}q=g-b|0;n=(s|0)<(q|0)?q:s;o=v+n|0;g=(I|0)<(o|0)?o:I;ke(a,32,g,o,h);if(!(k[a>>2]&32))fe(u,v,a)|0;ke(a,48,g,o,h^65536);ke(a,48,n,q,0);if(!(k[a>>2]&32))fe(b,q,a)|0;ke(a,32,g,o,h^8192);o=J;x=H}h:do if((K|0)==244)if(!a)if(!o)f=0;else{f=1;while(1){b=k[e+(f<<2)>>2]|0;if(!b){b=0;break}he(d+(f<<3)|0,b,c);f=f+1|0;if((f|0)>=10){f=1;break h}}while(1){f=f+1|0;if(b|0){f=-1;break h}if((f|0)>=10){f=1;break h}b=k[e+(f<<2)>>2]|0}}while(0);r=ia;return f|0}function fe(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0;d=c+16|0;e=k[d>>2]|0;if(!e)if(!(ge(c)|0)){e=k[d>>2]|0;f=5}else d=0;else f=5;a:do if((f|0)==5){g=c+20|0;d=k[g>>2]|0;f=d;if((e-d|0)>>>0<b>>>0){d=Ua[k[c+36>>2]&7](c,a,b)|0;break}b:do if((i[c+75>>0]|0)>-1){d=b;while(1){if(!d){e=f;d=0;break b}e=d+-1|0;if((i[a+e>>0]|0)==10)break;else d=e}if((Ua[k[c+36>>2]&7](c,a,d)|0)>>>0<d>>>0)break a;b=b-d|0;a=a+d|0;e=k[g>>2]|0}else{e=f;d=0}while(0);tf(e|0,a|0,b|0)|0;k[g>>2]=(k[g>>2]|0)+b;d=d+b|0}while(0);return d|0}function ge(a){a=a|0;var b=0,c=0;b=a+74|0;c=i[b>>0]|0;i[b>>0]=c+255|c;b=k[a>>2]|0;if(!(b&8)){k[a+8>>2]=0;k[a+4>>2]=0;b=k[a+44>>2]|0;k[a+28>>2]=b;k[a+20>>2]=b;k[a+16>>2]=b+(k[a+48>>2]|0);b=0}else{k[a>>2]=b|32;b=-1}return b|0}function he(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0.0;a:do if(b>>>0<=20)do switch(b|0){case 9:{d=(k[c>>2]|0)+(4-1)&~(4-1);b=k[d>>2]|0;k[c>>2]=d+4;k[a>>2]=b;break a}case 10:{d=(k[c>>2]|0)+(4-1)&~(4-1);b=k[d>>2]|0;k[c>>2]=d+4;d=a;k[d>>2]=b;k[d+4>>2]=((b|0)<0)<<31>>31;break a}case 11:{d=(k[c>>2]|0)+(4-1)&~(4-1);b=k[d>>2]|0;k[c>>2]=d+4;d=a;k[d>>2]=b;k[d+4>>2]=0;break a}case 12:{d=(k[c>>2]|0)+(8-1)&~(8-1);b=d;e=k[b>>2]|0;b=k[b+4>>2]|0;k[c>>2]=d+8;d=a;k[d>>2]=e;k[d+4>>2]=b;break a}case 13:{e=(k[c>>2]|0)+(4-1)&~(4-1);d=k[e>>2]|0;k[c>>2]=e+4;d=(d&65535)<<16>>16;e=a;k[e>>2]=d;k[e+4>>2]=((d|0)<0)<<31>>31;break a}case 14:{e=(k[c>>2]|0)+(4-1)&~(4-1);d=k[e>>2]|0;k[c>>2]=e+4;e=a;k[e>>2]=d&65535;k[e+4>>2]=0;break a}case 15:{e=(k[c>>2]|0)+(4-1)&~(4-1);d=k[e>>2]|0;k[c>>2]=e+4;d=(d&255)<<24>>24;e=a;k[e>>2]=d;k[e+4>>2]=((d|0)<0)<<31>>31;break a}case 16:{e=(k[c>>2]|0)+(4-1)&~(4-1);d=k[e>>2]|0;k[c>>2]=e+4;e=a;k[e>>2]=d&255;k[e+4>>2]=0;break a}case 17:{e=(k[c>>2]|0)+(8-1)&~(8-1);f=+p[e>>3];k[c>>2]=e+8;p[a>>3]=f;break a}case 18:{e=(k[c>>2]|0)+(8-1)&~(8-1);f=+p[e>>3];k[c>>2]=e+8;p[a>>3]=f;break a}default:break a}while(0);while(0);return}function ie(a,b,c){a=a|0;b=b|0;c=c|0;var d=0;if(b>>>0>0|(b|0)==0&a>>>0>4294967295)while(1){d=Cf(a|0,b|0,10,0)|0;c=c+-1|0;i[c>>0]=d|48;d=a;a=Bf(a|0,b|0,10,0)|0;if(!(b>>>0>9|(b|0)==9&d>>>0>4294967295))break;else b=L}if(a)while(1){c=c+-1|0;i[c>>0]=(a>>>0)%10|0|48;if(a>>>0<10)break;else a=(a>>>0)/10|0}return c|0}function je(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0;f=b&255;d=(c|0)!=0;a:do if(d&(a&3|0)!=0){e=b&255;while(1){if((i[a>>0]|0)==e<<24>>24)break a;a=a+1|0;c=c+-1|0;d=(c|0)!=0;if(!(d&(a&3|0)!=0)){g=5;break}}}else g=5;while(0);b:do if((g|0)==5)if(d){e=b&255;if((i[a>>0]|0)!=e<<24>>24){d=ha(f,16843009)|0;c:do if(c>>>0>3)while(1){f=k[a>>2]^d;if((f&-2139062144^-2139062144)&f+-16843009|0)break;a=a+4|0;c=c+-4|0;if(c>>>0<=3){g=11;break c}}else g=11;while(0);if((g|0)==11)if(!c){c=0;break}while(1){if((i[a>>0]|0)==e<<24>>24)break b;a=a+1|0;c=c+-1|0;if(!c){c=0;break}}}}else c=0;while(0);return (c|0?a:0)|0}function ke(a,b,c,d,e){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;var f=0,g=0;g=r;r=r+256|0;f=g;do if((c|0)>(d|0)&(e&73728|0)==0){e=c-d|0;qf(f|0,b|0,(e>>>0>256?256:e)|0)|0;d=k[a>>2]|0;c=(d&32|0)==0;if(e>>>0>255){b=e;do{if(c){fe(f,256,a)|0;d=k[a>>2]|0}b=b+-256|0;c=(d&32|0)==0}while(b>>>0>255);if(c)e=e&255;else break}else if(!c)break;fe(f,e,a)|0}while(0);r=g;return}function le(){return 0}function me(a,b){a=a|0;b=b|0;a=Ud(a,b)|0;return ((i[a>>0]|0)==(b&255)<<24>>24?a:0)|0}function ne(a,b){a=a|0;b=b|0;oe(a,b,4);return}function oe(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0;e=b;a:do if(!((e^a)&3)){d=(c|0)!=0;if(d&(e&3|0)!=0)do{e=i[b>>0]|0;i[a>>0]=e;if(!(e<<24>>24))break a;c=c+-1|0;b=b+1|0;a=a+1|0;d=(c|0)!=0}while(d&(b&3|0)!=0);if(d){if(i[b>>0]|0){b:do if(c>>>0>3)do{d=k[b>>2]|0;if((d&-2139062144^-2139062144)&d+-16843009|0)break b;k[a>>2]=d;c=c+-4|0;b=b+4|0;a=a+4|0}while(c>>>0>3);while(0);f=11}}else c=0}else f=11;while(0);c:do if((f|0)==11)if(!c)c=0;else while(1){f=i[b>>0]|0;i[a>>0]=f;if(!(f<<24>>24))break c;c=c+-1|0;a=a+1|0;if(!c){c=0;break}else b=b+1|0}while(0);qf(a|0,0,c|0)|0;return}function pe(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0;d=r;r=r+16|0;e=d;k[e>>2]=c;de(a,b,e)|0;r=d;return}function qe(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,g=0,h=0,j=0;j=r;r=r+16|0;h=j;g=b&255;i[h>>0]=g;d=a+16|0;e=k[d>>2]|0;if(!e)if(!(ge(a)|0)){e=k[d>>2]|0;f=4}else c=-1;else f=4;do if((f|0)==4){d=a+20|0;f=k[d>>2]|0;if(f>>>0<e>>>0?(c=b&255,(c|0)!=(i[a+75>>0]|0)):0){k[d>>2]=f+1;i[f>>0]=g;break}if((Ua[k[a+36>>2]&7](a,h,1)|0)==1)c=l[h>>0]|0;else c=-1}while(0);r=j;return c|0}function re(){var a=0,b=0,c=0;do if((k[235273]|0)>=0?(le()|0)!=0:0){if((i[941091]|0)!=10?(a=k[235259]|0,a>>>0<(k[235258]|0)>>>0):0){k[235259]=a+1;i[a>>0]=10;break}qe(941016,10)|0}else c=3;while(0);do if((c|0)==3){if((i[941091]|0)!=10?(b=k[235259]|0,b>>>0<(k[235258]|0)>>>0):0){k[235259]=b+1;i[b>>0]=10;break}qe(941016,10)|0}while(0);return}function se(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;fe(a,ha(c,b)|0,d)|0;return}function te(a,b,c){a=a|0;b=b|0;c=c|0;be(a,2147483647,b,c);return}function ue(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0;d=r;r=r+16|0;e=d;k[e>>2]=c;te(a,b,e);r=d;return}function ve(a){a=a|0;var b=0,c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0;do if(a>>>0<245){o=a>>>0<11?16:a+11&-8;a=o>>>3;i=k[274328]|0;b=i>>>a;if(b&3|0){b=(b&1^1)+a|0;c=1097352+(b<<1<<2)|0;d=c+8|0;e=k[d>>2]|0;f=e+8|0;g=k[f>>2]|0;do if((c|0)!=(g|0)){if(g>>>0<(k[274332]|0)>>>0)Na();a=g+12|0;if((k[a>>2]|0)==(e|0)){k[a>>2]=c;k[d>>2]=g;break}else Na()}else k[274328]=i&~(1<<b);while(0);G=b<<3;k[e+4>>2]=G|3;G=e+G+4|0;k[G>>2]=k[G>>2]|1;G=f;return G|0}g=k[274330]|0;if(o>>>0>g>>>0){if(b|0){c=2<<a;c=b<<a&(c|0-c);c=(c&0-c)+-1|0;h=c>>>12&16;c=c>>>h;e=c>>>5&8;c=c>>>e;f=c>>>2&4;c=c>>>f;d=c>>>1&2;c=c>>>d;b=c>>>1&1;b=(e|h|f|d|b)+(c>>>b)|0;c=1097352+(b<<1<<2)|0;d=c+8|0;f=k[d>>2]|0;h=f+8|0;e=k[h>>2]|0;do if((c|0)!=(e|0)){if(e>>>0<(k[274332]|0)>>>0)Na();a=e+12|0;if((k[a>>2]|0)==(f|0)){k[a>>2]=c;k[d>>2]=e;j=k[274330]|0;break}else Na()}else{k[274328]=i&~(1<<b);j=g}while(0);g=(b<<3)-o|0;k[f+4>>2]=o|3;d=f+o|0;k[d+4>>2]=g|1;k[d+g>>2]=g;if(j|0){e=k[274333]|0;b=j>>>3;c=1097352+(b<<1<<2)|0;a=k[274328]|0;b=1<<b;if(a&b){a=c+8|0;b=k[a>>2]|0;if(b>>>0<(k[274332]|0)>>>0)Na();else{l=a;m=b}}else{k[274328]=a|b;l=c+8|0;m=c}k[l>>2]=e;k[m+12>>2]=e;k[e+8>>2]=m;k[e+12>>2]=c}k[274330]=g;k[274333]=d;G=h;return G|0}a=k[274329]|0;if(a){h=(a&0-a)+-1|0;F=h>>>12&16;h=h>>>F;E=h>>>5&8;h=h>>>E;G=h>>>2&4;h=h>>>G;b=h>>>1&2;h=h>>>b;i=h>>>1&1;i=k[1097616+((E|F|G|b|i)+(h>>>i)<<2)>>2]|0;h=(k[i+4>>2]&-8)-o|0;b=i;while(1){a=k[b+16>>2]|0;if(!a){a=k[b+20>>2]|0;if(!a)break}b=(k[a+4>>2]&-8)-o|0;G=b>>>0<h>>>0;h=G?b:h;b=a;i=G?a:i}e=k[274332]|0;if(i>>>0<e>>>0)Na();g=i+o|0;if(i>>>0>=g>>>0)Na();f=k[i+24>>2]|0;c=k[i+12>>2]|0;do if((c|0)==(i|0)){b=i+20|0;a=k[b>>2]|0;if(!a){b=i+16|0;a=k[b>>2]|0;if(!a){n=0;break}}while(1){c=a+20|0;d=k[c>>2]|0;if(d|0){a=d;b=c;continue}c=a+16|0;d=k[c>>2]|0;if(!d)break;else{a=d;b=c}}if(b>>>0<e>>>0)Na();else{k[b>>2]=0;n=a;break}}else{d=k[i+8>>2]|0;if(d>>>0<e>>>0)Na();a=d+12|0;if((k[a>>2]|0)!=(i|0))Na();b=c+8|0;if((k[b>>2]|0)==(i|0)){k[a>>2]=c;k[b>>2]=d;n=c;break}else Na()}while(0);do if(f|0){a=k[i+28>>2]|0;b=1097616+(a<<2)|0;if((i|0)==(k[b>>2]|0)){k[b>>2]=n;if(!n){k[274329]=k[274329]&~(1<<a);break}}else{if(f>>>0<(k[274332]|0)>>>0)Na();a=f+16|0;if((k[a>>2]|0)==(i|0))k[a>>2]=n;else k[f+20>>2]=n;if(!n)break}b=k[274332]|0;if(n>>>0<b>>>0)Na();k[n+24>>2]=f;a=k[i+16>>2]|0;do if(a|0)if(a>>>0<b>>>0)Na();else{k[n+16>>2]=a;k[a+24>>2]=n;break}while(0);a=k[i+20>>2]|0;if(a|0)if(a>>>0<(k[274332]|0)>>>0)Na();else{k[n+20>>2]=a;k[a+24>>2]=n;break}}while(0);if(h>>>0<16){G=h+o|0;k[i+4>>2]=G|3;G=i+G+4|0;k[G>>2]=k[G>>2]|1}else{k[i+4>>2]=o|3;k[g+4>>2]=h|1;k[g+h>>2]=h;a=k[274330]|0;if(a|0){d=k[274333]|0;b=a>>>3;c=1097352+(b<<1<<2)|0;a=k[274328]|0;b=1<<b;if(a&b){a=c+8|0;b=k[a>>2]|0;if(b>>>0<(k[274332]|0)>>>0)Na();else{p=a;q=b}}else{k[274328]=a|b;p=c+8|0;q=c}k[p>>2]=d;k[q+12>>2]=d;k[d+8>>2]=q;k[d+12>>2]=c}k[274330]=h;k[274333]=g}G=i+8|0;return G|0}}}else if(a>>>0<=4294967231){a=a+11|0;o=a&-8;j=k[274329]|0;if(j){c=0-o|0;a=a>>>8;if(a)if(o>>>0>16777215)i=31;else{q=(a+1048320|0)>>>16&8;z=a<<q;p=(z+520192|0)>>>16&4;z=z<<p;i=(z+245760|0)>>>16&2;i=14-(p|q|i)+(z<<i>>>15)|0;i=o>>>(i+7|0)&1|i<<1}else i=0;b=k[1097616+(i<<2)>>2]|0;a:do if(!b){a=0;b=0;z=86}else{e=c;a=0;g=o<<((i|0)==31?0:25-(i>>>1)|0);h=b;b=0;while(1){d=k[h+4>>2]&-8;c=d-o|0;if(c>>>0<e>>>0)if((d|0)==(o|0)){a=h;b=h;z=90;break a}else b=h;else c=e;d=k[h+20>>2]|0;h=k[h+16+(g>>>31<<2)>>2]|0;a=(d|0)==0|(d|0)==(h|0)?a:d;d=(h|0)==0;if(d){z=86;break}else{e=c;g=g<<(d&1^1)}}}while(0);if((z|0)==86){if((a|0)==0&(b|0)==0){a=2<<i;a=j&(a|0-a);if(!a)break;q=(a&0-a)+-1|0;m=q>>>12&16;q=q>>>m;l=q>>>5&8;q=q>>>l;n=q>>>2&4;q=q>>>n;p=q>>>1&2;q=q>>>p;a=q>>>1&1;a=k[1097616+((l|m|n|p|a)+(q>>>a)<<2)>>2]|0}if(!a){h=c;i=b}else z=90}if((z|0)==90)while(1){z=0;q=(k[a+4>>2]&-8)-o|0;d=q>>>0<c>>>0;c=d?q:c;b=d?a:b;d=k[a+16>>2]|0;if(d|0){a=d;z=90;continue}a=k[a+20>>2]|0;if(!a){h=c;i=b;break}else z=90}if((i|0)!=0?h>>>0<((k[274330]|0)-o|0)>>>0:0){e=k[274332]|0;if(i>>>0<e>>>0)Na();g=i+o|0;if(i>>>0>=g>>>0)Na();f=k[i+24>>2]|0;c=k[i+12>>2]|0;do if((c|0)==(i|0)){b=i+20|0;a=k[b>>2]|0;if(!a){b=i+16|0;a=k[b>>2]|0;if(!a){s=0;break}}while(1){c=a+20|0;d=k[c>>2]|0;if(d|0){a=d;b=c;continue}c=a+16|0;d=k[c>>2]|0;if(!d)break;else{a=d;b=c}}if(b>>>0<e>>>0)Na();else{k[b>>2]=0;s=a;break}}else{d=k[i+8>>2]|0;if(d>>>0<e>>>0)Na();a=d+12|0;if((k[a>>2]|0)!=(i|0))Na();b=c+8|0;if((k[b>>2]|0)==(i|0)){k[a>>2]=c;k[b>>2]=d;s=c;break}else Na()}while(0);do if(f|0){a=k[i+28>>2]|0;b=1097616+(a<<2)|0;if((i|0)==(k[b>>2]|0)){k[b>>2]=s;if(!s){k[274329]=k[274329]&~(1<<a);break}}else{if(f>>>0<(k[274332]|0)>>>0)Na();a=f+16|0;if((k[a>>2]|0)==(i|0))k[a>>2]=s;else k[f+20>>2]=s;if(!s)break}b=k[274332]|0;if(s>>>0<b>>>0)Na();k[s+24>>2]=f;a=k[i+16>>2]|0;do if(a|0)if(a>>>0<b>>>0)Na();else{k[s+16>>2]=a;k[a+24>>2]=s;break}while(0);a=k[i+20>>2]|0;if(a|0)if(a>>>0<(k[274332]|0)>>>0)Na();else{k[s+20>>2]=a;k[a+24>>2]=s;break}}while(0);do if(h>>>0>=16){k[i+4>>2]=o|3;k[g+4>>2]=h|1;k[g+h>>2]=h;a=h>>>3;if(h>>>0<256){c=1097352+(a<<1<<2)|0;b=k[274328]|0;a=1<<a;if(b&a){a=c+8|0;b=k[a>>2]|0;if(b>>>0<(k[274332]|0)>>>0)Na();else{t=a;v=b}}else{k[274328]=b|a;t=c+8|0;v=c}k[t>>2]=g;k[v+12>>2]=g;k[g+8>>2]=v;k[g+12>>2]=c;break}a=h>>>8;if(a)if(h>>>0>16777215)c=31;else{F=(a+1048320|0)>>>16&8;G=a<<F;E=(G+520192|0)>>>16&4;G=G<<E;c=(G+245760|0)>>>16&2;c=14-(E|F|c)+(G<<c>>>15)|0;c=h>>>(c+7|0)&1|c<<1}else c=0;d=1097616+(c<<2)|0;k[g+28>>2]=c;a=g+16|0;k[a+4>>2]=0;k[a>>2]=0;a=k[274329]|0;b=1<<c;if(!(a&b)){k[274329]=a|b;k[d>>2]=g;k[g+24>>2]=d;k[g+12>>2]=g;k[g+8>>2]=g;break}c=h<<((c|0)==31?0:25-(c>>>1)|0);d=k[d>>2]|0;while(1){if((k[d+4>>2]&-8|0)==(h|0)){z=148;break}b=d+16+(c>>>31<<2)|0;a=k[b>>2]|0;if(!a){z=145;break}else{c=c<<1;d=a}}if((z|0)==145)if(b>>>0<(k[274332]|0)>>>0)Na();else{k[b>>2]=g;k[g+24>>2]=d;k[g+12>>2]=g;k[g+8>>2]=g;break}else if((z|0)==148){a=d+8|0;b=k[a>>2]|0;G=k[274332]|0;if(b>>>0>=G>>>0&d>>>0>=G>>>0){k[b+12>>2]=g;k[a>>2]=g;k[g+8>>2]=b;k[g+12>>2]=d;k[g+24>>2]=0;break}else Na()}}else{G=h+o|0;k[i+4>>2]=G|3;G=i+G+4|0;k[G>>2]=k[G>>2]|1}while(0);G=i+8|0;return G|0}}}else o=-1;while(0);c=k[274330]|0;if(c>>>0>=o>>>0){a=c-o|0;b=k[274333]|0;if(a>>>0>15){G=b+o|0;k[274333]=G;k[274330]=a;k[G+4>>2]=a|1;k[G+a>>2]=a;k[b+4>>2]=o|3}else{k[274330]=0;k[274333]=0;k[b+4>>2]=c|3;G=b+c+4|0;k[G>>2]=k[G>>2]|1}G=b+8|0;return G|0}a=k[274331]|0;if(a>>>0>o>>>0){E=a-o|0;k[274331]=E;G=k[274334]|0;F=G+o|0;k[274334]=F;k[F+4>>2]=E|1;k[G+4>>2]=o|3;G=G+8|0;return G|0}do if(!(k[274446]|0)){a=Fa(30)|0;if(!(a+-1&a)){k[274448]=a;k[274447]=a;k[274449]=-1;k[274450]=-1;k[274451]=0;k[274439]=0;v=(Pa(0)|0)&-16^1431655768;k[274446]=v;break}else Na()}while(0);g=o+48|0;d=k[274448]|0;h=o+47|0;c=d+h|0;d=0-d|0;i=c&d;if(i>>>0<=o>>>0){G=0;return G|0}a=k[274438]|0;if(a|0?(t=k[274436]|0,v=t+i|0,v>>>0<=t>>>0|v>>>0>a>>>0):0){G=0;return G|0}b:do if(!(k[274439]&4)){b=k[274334]|0;c:do if(b){e=1097760;while(1){a=k[e>>2]|0;if(a>>>0<=b>>>0?(r=e+4|0,(a+(k[r>>2]|0)|0)>>>0>b>>>0):0)break;a=k[e+8>>2]|0;if(!a){z=173;break c}else e=a}a=c-(k[274331]|0)&d;if(a>>>0<2147483647){b=Aa(a|0)|0;if((b|0)==((k[e>>2]|0)+(k[r>>2]|0)|0)){if((b|0)!=(-1|0)){g=b;f=a;z=193;break b}}else z=183}}else z=173;while(0);do if((z|0)==173?(u=Aa(0)|0,(u|0)!=(-1|0)):0){a=u;b=k[274447]|0;c=b+-1|0;if(!(c&a))a=i;else a=i-a+(c+a&0-b)|0;b=k[274436]|0;c=b+a|0;if(a>>>0>o>>>0&a>>>0<2147483647){v=k[274438]|0;if(v|0?c>>>0<=b>>>0|c>>>0>v>>>0:0)break;b=Aa(a|0)|0;if((b|0)==(u|0)){g=u;f=a;z=193;break b}else z=183}}while(0);d:do if((z|0)==183){c=0-a|0;do if(g>>>0>a>>>0&(a>>>0<2147483647&(b|0)!=(-1|0))?(w=k[274448]|0,w=h-a+w&0-w,w>>>0<2147483647):0)if((Aa(w|0)|0)==(-1|0)){Aa(c|0)|0;break d}else{a=w+a|0;break}while(0);if((b|0)!=(-1|0)){g=b;f=a;z=193;break b}}while(0);k[274439]=k[274439]|4;z=190}else z=190;while(0);if((((z|0)==190?i>>>0<2147483647:0)?(x=Aa(i|0)|0,y=Aa(0)|0,x>>>0<y>>>0&((x|0)!=(-1|0)&(y|0)!=(-1|0))):0)?(f=y-x|0,f>>>0>(o+40|0)>>>0):0){g=x;z=193}if((z|0)==193){a=(k[274436]|0)+f|0;k[274436]=a;if(a>>>0>(k[274437]|0)>>>0)k[274437]=a;j=k[274334]|0;do if(j){e=1097760;while(1){a=k[e>>2]|0;b=e+4|0;c=k[b>>2]|0;if((g|0)==(a+c|0)){z=203;break}d=k[e+8>>2]|0;if(!d)break;else e=d}if(((z|0)==203?(k[e+12>>2]&8|0)==0:0)?j>>>0<g>>>0&j>>>0>=a>>>0:0){k[b>>2]=c+f;G=j+8|0;G=(G&7|0)==0?0:0-G&7;F=j+G|0;G=f-G+(k[274331]|0)|0;k[274334]=F;k[274331]=G;k[F+4>>2]=G|1;k[F+G+4>>2]=40;k[274335]=k[274450];break}a=k[274332]|0;if(g>>>0<a>>>0){k[274332]=g;h=g}else h=a;b=g+f|0;a=1097760;while(1){if((k[a>>2]|0)==(b|0)){z=211;break}a=k[a+8>>2]|0;if(!a){b=1097760;break}}if((z|0)==211)if(!(k[a+12>>2]&8)){k[a>>2]=g;m=a+4|0;k[m>>2]=(k[m>>2]|0)+f;m=g+8|0;m=g+((m&7|0)==0?0:0-m&7)|0;a=b+8|0;a=b+((a&7|0)==0?0:0-a&7)|0;l=m+o|0;i=a-m-o|0;k[m+4>>2]=o|3;do if((a|0)!=(j|0)){if((a|0)==(k[274333]|0)){G=(k[274330]|0)+i|0;k[274330]=G;k[274333]=l;k[l+4>>2]=G|1;k[l+G>>2]=G;break}b=k[a+4>>2]|0;if((b&3|0)==1){g=b&-8;e=b>>>3;e:do if(b>>>0>=256){f=k[a+24>>2]|0;d=k[a+12>>2]|0;do if((d|0)==(a|0)){d=a+16|0;c=d+4|0;b=k[c>>2]|0;if(!b){b=k[d>>2]|0;if(!b){E=0;break}else c=d}while(1){d=b+20|0;e=k[d>>2]|0;if(e|0){b=e;c=d;continue}d=b+16|0;e=k[d>>2]|0;if(!e)break;else{b=e;c=d}}if(c>>>0<h>>>0)Na();else{k[c>>2]=0;E=b;break}}else{e=k[a+8>>2]|0;if(e>>>0<h>>>0)Na();b=e+12|0;if((k[b>>2]|0)!=(a|0))Na();c=d+8|0;if((k[c>>2]|0)==(a|0)){k[b>>2]=d;k[c>>2]=e;E=d;break}else Na()}while(0);if(!f)break;b=k[a+28>>2]|0;c=1097616+(b<<2)|0;do if((a|0)!=(k[c>>2]|0)){if(f>>>0<(k[274332]|0)>>>0)Na();b=f+16|0;if((k[b>>2]|0)==(a|0))k[b>>2]=E;else k[f+20>>2]=E;if(!E)break e}else{k[c>>2]=E;if(E|0)break;k[274329]=k[274329]&~(1<<b);break e}while(0);d=k[274332]|0;if(E>>>0<d>>>0)Na();k[E+24>>2]=f;b=a+16|0;c=k[b>>2]|0;do if(c|0)if(c>>>0<d>>>0)Na();else{k[E+16>>2]=c;k[c+24>>2]=E;break}while(0);b=k[b+4>>2]|0;if(!b)break;if(b>>>0<(k[274332]|0)>>>0)Na();else{k[E+20>>2]=b;k[b+24>>2]=E;break}}else{c=k[a+8>>2]|0;d=k[a+12>>2]|0;b=1097352+(e<<1<<2)|0;do if((c|0)!=(b|0)){if(c>>>0<h>>>0)Na();if((k[c+12>>2]|0)==(a|0))break;Na()}while(0);if((d|0)==(c|0)){k[274328]=k[274328]&~(1<<e);break}do if((d|0)==(b|0))B=d+8|0;else{if(d>>>0<h>>>0)Na();b=d+8|0;if((k[b>>2]|0)==(a|0)){B=b;break}Na()}while(0);k[c+12>>2]=d;k[B>>2]=c}while(0);a=a+g|0;e=g+i|0}else e=i;a=a+4|0;k[a>>2]=k[a>>2]&-2;k[l+4>>2]=e|1;k[l+e>>2]=e;a=e>>>3;if(e>>>0<256){c=1097352+(a<<1<<2)|0;b=k[274328]|0;a=1<<a;do if(!(b&a)){k[274328]=b|a;F=c+8|0;G=c}else{a=c+8|0;b=k[a>>2]|0;if(b>>>0>=(k[274332]|0)>>>0){F=a;G=b;break}Na()}while(0);k[F>>2]=l;k[G+12>>2]=l;k[l+8>>2]=G;k[l+12>>2]=c;break}a=e>>>8;do if(!a)c=0;else{if(e>>>0>16777215){c=31;break}F=(a+1048320|0)>>>16&8;G=a<<F;E=(G+520192|0)>>>16&4;G=G<<E;c=(G+245760|0)>>>16&2;c=14-(E|F|c)+(G<<c>>>15)|0;c=e>>>(c+7|0)&1|c<<1}while(0);d=1097616+(c<<2)|0;k[l+28>>2]=c;a=l+16|0;k[a+4>>2]=0;k[a>>2]=0;a=k[274329]|0;b=1<<c;if(!(a&b)){k[274329]=a|b;k[d>>2]=l;k[l+24>>2]=d;k[l+12>>2]=l;k[l+8>>2]=l;break}c=e<<((c|0)==31?0:25-(c>>>1)|0);d=k[d>>2]|0;while(1){if((k[d+4>>2]&-8|0)==(e|0)){z=281;break}b=d+16+(c>>>31<<2)|0;a=k[b>>2]|0;if(!a){z=278;break}else{c=c<<1;d=a}}if((z|0)==278)if(b>>>0<(k[274332]|0)>>>0)Na();else{k[b>>2]=l;k[l+24>>2]=d;k[l+12>>2]=l;k[l+8>>2]=l;break}else if((z|0)==281){a=d+8|0;b=k[a>>2]|0;G=k[274332]|0;if(b>>>0>=G>>>0&d>>>0>=G>>>0){k[b+12>>2]=l;k[a>>2]=l;k[l+8>>2]=b;k[l+12>>2]=d;k[l+24>>2]=0;break}else Na()}}else{G=(k[274331]|0)+i|0;k[274331]=G;k[274334]=l;k[l+4>>2]=G|1}while(0);G=m+8|0;return G|0}else b=1097760;while(1){a=k[b>>2]|0;if(a>>>0<=j>>>0?(A=a+(k[b+4>>2]|0)|0,A>>>0>j>>>0):0)break;b=k[b+8>>2]|0}e=A+-47|0;b=e+8|0;b=e+((b&7|0)==0?0:0-b&7)|0;e=j+16|0;b=b>>>0<e>>>0?j:b;a=b+8|0;c=g+8|0;c=(c&7|0)==0?0:0-c&7;G=g+c|0;c=f+-40-c|0;k[274334]=G;k[274331]=c;k[G+4>>2]=c|1;k[G+c+4>>2]=40;k[274335]=k[274450];c=b+4|0;k[c>>2]=27;k[a>>2]=k[274440];k[a+4>>2]=k[274441];k[a+8>>2]=k[274442];k[a+12>>2]=k[274443];k[274440]=g;k[274441]=f;k[274443]=0;k[274442]=a;a=b+24|0;do{a=a+4|0;k[a>>2]=7}while((a+4|0)>>>0<A>>>0);if((b|0)!=(j|0)){f=b-j|0;k[c>>2]=k[c>>2]&-2;k[j+4>>2]=f|1;k[b>>2]=f;a=f>>>3;if(f>>>0<256){c=1097352+(a<<1<<2)|0;b=k[274328]|0;a=1<<a;if(b&a){a=c+8|0;b=k[a>>2]|0;if(b>>>0<(k[274332]|0)>>>0)Na();else{C=a;D=b}}else{k[274328]=b|a;C=c+8|0;D=c}k[C>>2]=j;k[D+12>>2]=j;k[j+8>>2]=D;k[j+12>>2]=c;break}a=f>>>8;if(a)if(f>>>0>16777215)c=31;else{F=(a+1048320|0)>>>16&8;G=a<<F;E=(G+520192|0)>>>16&4;G=G<<E;c=(G+245760|0)>>>16&2;c=14-(E|F|c)+(G<<c>>>15)|0;c=f>>>(c+7|0)&1|c<<1}else c=0;d=1097616+(c<<2)|0;k[j+28>>2]=c;k[j+20>>2]=0;k[e>>2]=0;a=k[274329]|0;b=1<<c;if(!(a&b)){k[274329]=a|b;k[d>>2]=j;k[j+24>>2]=d;k[j+12>>2]=j;k[j+8>>2]=j;break}c=f<<((c|0)==31?0:25-(c>>>1)|0);d=k[d>>2]|0;while(1){if((k[d+4>>2]&-8|0)==(f|0)){z=307;break}b=d+16+(c>>>31<<2)|0;a=k[b>>2]|0;if(!a){z=304;break}else{c=c<<1;d=a}}if((z|0)==304)if(b>>>0<(k[274332]|0)>>>0)Na();else{k[b>>2]=j;k[j+24>>2]=d;k[j+12>>2]=j;k[j+8>>2]=j;break}else if((z|0)==307){a=d+8|0;b=k[a>>2]|0;G=k[274332]|0;if(b>>>0>=G>>>0&d>>>0>=G>>>0){k[b+12>>2]=j;k[a>>2]=j;k[j+8>>2]=b;k[j+12>>2]=d;k[j+24>>2]=0;break}else Na()}}}else{G=k[274332]|0;if((G|0)==0|g>>>0<G>>>0)k[274332]=g;k[274440]=g;k[274441]=f;k[274443]=0;k[274337]=k[274446];k[274336]=-1;a=0;do{G=1097352+(a<<1<<2)|0;k[G+12>>2]=G;k[G+8>>2]=G;a=a+1|0}while((a|0)!=32);G=g+8|0;G=(G&7|0)==0?0:0-G&7;F=g+G|0;G=f+-40-G|0;k[274334]=F;k[274331]=G;k[F+4>>2]=G|1;k[F+G+4>>2]=40;k[274335]=k[274450]}while(0);a=k[274331]|0;if(a>>>0>o>>>0){E=a-o|0;k[274331]=E;G=k[274334]|0;F=G+o|0;k[274334]=F;k[F+4>>2]=E|1;k[G+4>>2]=o|3;G=G+8|0;return G|0}}G=Qd()|0;k[G>>2]=12;G=0;return G|0}
function we(a){a=a|0;var b=0,c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,l=0,m=0,n=0,o=0,p=0,q=0;if(!a)return;c=a+-8|0;g=k[274332]|0;if(c>>>0<g>>>0)Na();a=k[a+-4>>2]|0;b=a&3;if((b|0)==1)Na();d=a&-8;m=c+d|0;do if(!(a&1)){a=k[c>>2]|0;if(!b)return;j=c+(0-a)|0;i=a+d|0;if(j>>>0<g>>>0)Na();if((j|0)==(k[274333]|0)){a=m+4|0;b=k[a>>2]|0;if((b&3|0)!=3){q=j;e=i;break}k[274330]=i;k[a>>2]=b&-2;k[j+4>>2]=i|1;k[j+i>>2]=i;return}d=a>>>3;if(a>>>0<256){b=k[j+8>>2]|0;c=k[j+12>>2]|0;a=1097352+(d<<1<<2)|0;if((b|0)!=(a|0)){if(b>>>0<g>>>0)Na();if((k[b+12>>2]|0)!=(j|0))Na()}if((c|0)==(b|0)){k[274328]=k[274328]&~(1<<d);q=j;e=i;break}if((c|0)!=(a|0)){if(c>>>0<g>>>0)Na();a=c+8|0;if((k[a>>2]|0)==(j|0))f=a;else Na()}else f=c+8|0;k[b+12>>2]=c;k[f>>2]=b;q=j;e=i;break}f=k[j+24>>2]|0;c=k[j+12>>2]|0;do if((c|0)==(j|0)){c=j+16|0;b=c+4|0;a=k[b>>2]|0;if(!a){a=k[c>>2]|0;if(!a){h=0;break}else b=c}while(1){c=a+20|0;d=k[c>>2]|0;if(d|0){a=d;b=c;continue}c=a+16|0;d=k[c>>2]|0;if(!d)break;else{a=d;b=c}}if(b>>>0<g>>>0)Na();else{k[b>>2]=0;h=a;break}}else{d=k[j+8>>2]|0;if(d>>>0<g>>>0)Na();a=d+12|0;if((k[a>>2]|0)!=(j|0))Na();b=c+8|0;if((k[b>>2]|0)==(j|0)){k[a>>2]=c;k[b>>2]=d;h=c;break}else Na()}while(0);if(f){a=k[j+28>>2]|0;b=1097616+(a<<2)|0;if((j|0)==(k[b>>2]|0)){k[b>>2]=h;if(!h){k[274329]=k[274329]&~(1<<a);q=j;e=i;break}}else{if(f>>>0<(k[274332]|0)>>>0)Na();a=f+16|0;if((k[a>>2]|0)==(j|0))k[a>>2]=h;else k[f+20>>2]=h;if(!h){q=j;e=i;break}}c=k[274332]|0;if(h>>>0<c>>>0)Na();k[h+24>>2]=f;a=j+16|0;b=k[a>>2]|0;do if(b|0)if(b>>>0<c>>>0)Na();else{k[h+16>>2]=b;k[b+24>>2]=h;break}while(0);a=k[a+4>>2]|0;if(a)if(a>>>0<(k[274332]|0)>>>0)Na();else{k[h+20>>2]=a;k[a+24>>2]=h;q=j;e=i;break}else{q=j;e=i}}else{q=j;e=i}}else{q=c;e=d}while(0);if(q>>>0>=m>>>0)Na();a=m+4|0;b=k[a>>2]|0;if(!(b&1))Na();if(!(b&2)){if((m|0)==(k[274334]|0)){p=(k[274331]|0)+e|0;k[274331]=p;k[274334]=q;k[q+4>>2]=p|1;if((q|0)!=(k[274333]|0))return;k[274333]=0;k[274330]=0;return}if((m|0)==(k[274333]|0)){p=(k[274330]|0)+e|0;k[274330]=p;k[274333]=q;k[q+4>>2]=p|1;k[q+p>>2]=p;return}e=(b&-8)+e|0;d=b>>>3;do if(b>>>0>=256){f=k[m+24>>2]|0;a=k[m+12>>2]|0;do if((a|0)==(m|0)){c=m+16|0;b=c+4|0;a=k[b>>2]|0;if(!a){a=k[c>>2]|0;if(!a){n=0;break}else b=c}while(1){c=a+20|0;d=k[c>>2]|0;if(d|0){a=d;b=c;continue}c=a+16|0;d=k[c>>2]|0;if(!d)break;else{a=d;b=c}}if(b>>>0<(k[274332]|0)>>>0)Na();else{k[b>>2]=0;n=a;break}}else{b=k[m+8>>2]|0;if(b>>>0<(k[274332]|0)>>>0)Na();c=b+12|0;if((k[c>>2]|0)!=(m|0))Na();d=a+8|0;if((k[d>>2]|0)==(m|0)){k[c>>2]=a;k[d>>2]=b;n=a;break}else Na()}while(0);if(f|0){a=k[m+28>>2]|0;b=1097616+(a<<2)|0;if((m|0)==(k[b>>2]|0)){k[b>>2]=n;if(!n){k[274329]=k[274329]&~(1<<a);break}}else{if(f>>>0<(k[274332]|0)>>>0)Na();a=f+16|0;if((k[a>>2]|0)==(m|0))k[a>>2]=n;else k[f+20>>2]=n;if(!n)break}c=k[274332]|0;if(n>>>0<c>>>0)Na();k[n+24>>2]=f;a=m+16|0;b=k[a>>2]|0;do if(b|0)if(b>>>0<c>>>0)Na();else{k[n+16>>2]=b;k[b+24>>2]=n;break}while(0);a=k[a+4>>2]|0;if(a|0)if(a>>>0<(k[274332]|0)>>>0)Na();else{k[n+20>>2]=a;k[a+24>>2]=n;break}}}else{b=k[m+8>>2]|0;c=k[m+12>>2]|0;a=1097352+(d<<1<<2)|0;if((b|0)!=(a|0)){if(b>>>0<(k[274332]|0)>>>0)Na();if((k[b+12>>2]|0)!=(m|0))Na()}if((c|0)==(b|0)){k[274328]=k[274328]&~(1<<d);break}if((c|0)!=(a|0)){if(c>>>0<(k[274332]|0)>>>0)Na();a=c+8|0;if((k[a>>2]|0)==(m|0))l=a;else Na()}else l=c+8|0;k[b+12>>2]=c;k[l>>2]=b}while(0);k[q+4>>2]=e|1;k[q+e>>2]=e;if((q|0)==(k[274333]|0)){k[274330]=e;return}}else{k[a>>2]=b&-2;k[q+4>>2]=e|1;k[q+e>>2]=e}a=e>>>3;if(e>>>0<256){c=1097352+(a<<1<<2)|0;b=k[274328]|0;a=1<<a;if(b&a){a=c+8|0;b=k[a>>2]|0;if(b>>>0<(k[274332]|0)>>>0)Na();else{o=a;p=b}}else{k[274328]=b|a;o=c+8|0;p=c}k[o>>2]=q;k[p+12>>2]=q;k[q+8>>2]=p;k[q+12>>2]=c;return}a=e>>>8;if(a)if(e>>>0>16777215)c=31;else{o=(a+1048320|0)>>>16&8;p=a<<o;n=(p+520192|0)>>>16&4;p=p<<n;c=(p+245760|0)>>>16&2;c=14-(n|o|c)+(p<<c>>>15)|0;c=e>>>(c+7|0)&1|c<<1}else c=0;d=1097616+(c<<2)|0;k[q+28>>2]=c;k[q+20>>2]=0;k[q+16>>2]=0;a=k[274329]|0;b=1<<c;do if(a&b){c=e<<((c|0)==31?0:25-(c>>>1)|0);d=k[d>>2]|0;while(1){if((k[d+4>>2]&-8|0)==(e|0)){a=130;break}b=d+16+(c>>>31<<2)|0;a=k[b>>2]|0;if(!a){a=127;break}else{c=c<<1;d=a}}if((a|0)==127)if(b>>>0<(k[274332]|0)>>>0)Na();else{k[b>>2]=q;k[q+24>>2]=d;k[q+12>>2]=q;k[q+8>>2]=q;break}else if((a|0)==130){a=d+8|0;b=k[a>>2]|0;p=k[274332]|0;if(b>>>0>=p>>>0&d>>>0>=p>>>0){k[b+12>>2]=q;k[a>>2]=q;k[q+8>>2]=b;k[q+12>>2]=d;k[q+24>>2]=0;break}else Na()}}else{k[274329]=a|b;k[d>>2]=q;k[q+24>>2]=d;k[q+12>>2]=q;k[q+8>>2]=q}while(0);q=(k[274336]|0)+-1|0;k[274336]=q;if(!q)a=1097768;else return;while(1){a=k[a>>2]|0;if(!a)break;else a=a+8|0}k[274336]=-1;return}function xe(){wa(1096524,1096553,1164,1096636)}function ye(){wa(1096657,1096553,1175,1096686)}function ze(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0;if(c>>>0>4294967279)xe();if(c>>>0<11){i[a>>0]=c<<1;a=a+1|0}else{e=c+16&-16;d=gf(e)|0;k[a+8>>2]=d;k[a>>2]=e|1;k[a+4>>2]=c;a=d}tf(a|0,b|0,c|0)|0;i[a+c>>0]=0;return}function Ae(a){a=a|0;if(i[a>>0]&1)Qe(k[a+8>>2]|0);return}function Be(a,b,c,d,e,f,g,h){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;var j=0,l=0,m=0;if((-18-b|0)>>>0<c>>>0)xe();if(!(i[a>>0]&1))m=a+1|0;else m=k[a+8>>2]|0;if(b>>>0<2147483623){j=c+b|0;l=b<<1;j=j>>>0<l>>>0?l:j;j=j>>>0<11?11:j+16&-16}else j=-17;l=gf(j)|0;if(e|0)tf(l|0,m|0,e|0)|0;if(g|0)tf(l+e|0,h|0,g|0)|0;c=d-f|0;if((c|0)!=(e|0))tf(l+e+g|0,m+e+f|0,c-e|0)|0;if((b|0)!=10)Qe(m);k[a+8>>2]=l;k[a>>2]=j|1;b=c+g|0;k[a+4>>2]=b;i[l+b>>0]=0;return}function Ce(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0;if(b|0){d=i[a>>0]|0;if(!(d&1))e=10;else{d=k[a>>2]|0;e=(d&-2)+-1|0;d=d&255}if(!(d&1))f=(d&255)>>>1;else f=k[a+4>>2]|0;if((e-f|0)>>>0<b>>>0){De(a,e,b-e+f|0,f,f);d=i[a>>0]|0}if(!(d&1))e=a+1|0;else e=k[a+8>>2]|0;qf(e+f|0,c|0,b|0)|0;d=f+b|0;if(!(i[a>>0]&1))i[a>>0]=d<<1;else k[a+4>>2]=d;i[e+d>>0]=0}return}function De(a,b,c,d,e){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;var f=0,g=0;if((-17-b|0)>>>0<c>>>0)xe();if(!(i[a>>0]&1))g=a+1|0;else g=k[a+8>>2]|0;if(b>>>0<2147483623){c=c+b|0;f=b<<1;c=c>>>0<f>>>0?f:c;c=c>>>0<11?11:c+16&-16}else c=-17;f=gf(c)|0;if(e|0)tf(f|0,g|0,e|0)|0;if((d|0)!=(e|0))tf(f+e|0,g+e|0,d-e|0)|0;if((b|0)!=10)Qe(g);k[a+8>>2]=f;k[a>>2]=c|1;return}function Ee(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0;d=i[a>>0]|0;if(!(d&1))f=10;else{d=k[a>>2]|0;f=(d&-2)+-1|0;d=d&255}e=(d&1)==0;if(e)d=(d&255)>>>1;else d=k[a+4>>2]|0;if((f-d|0)>>>0>=c>>>0){if(c|0){if(e)e=a+1|0;else e=k[a+8>>2]|0;tf(e+d|0,b|0,c|0)|0;d=d+c|0;if(!(i[a>>0]&1))i[a>>0]=d<<1;else k[a+4>>2]=d;i[e+d>>0]=0}}else Be(a,f,c-f+d|0,d,d,0,c,b);return}function Fe(a,b){a=a|0;b=b|0;Ee(a,b,Vd(b)|0);return}function Ge(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0;c=i[a>>0]|0;d=(c&1)!=0;if(d){e=(k[a>>2]&-2)+-1|0;f=k[a+4>>2]|0}else{e=10;f=(c&255)>>>1}if((f|0)==(e|0)){De(a,e,1,e,e);if(!(i[a>>0]&1))d=7;else d=8}else if(d)d=8;else d=7;if((d|0)==7){i[a>>0]=(f<<1)+2;c=a+1|0}else if((d|0)==8){c=k[a+8>>2]|0;k[a+4>>2]=f+1}a=c+f|0;i[a>>0]=b;i[a+1>>0]=0;return}function He(a,b){a=a|0;b=b|0;var c=0,d=0;c=i[a>>0]|0;d=(c&1)==0;if(d)c=(c&255)>>>1;else c=k[a+4>>2]|0;if(c>>>0<b>>>0)ye();if(d){i[a>>0]=b<<1;c=a+1|0}else{c=k[a+8>>2]|0;k[a+4>>2]=b}i[c+b>>0]=0;return}function Ie(a,b){a=a|0;b=b|0;var c=0,d=0;c=i[a>>0]|0;if(!(c&1)){d=(c&255)>>>1;c=a+1|0}else{d=k[a+4>>2]|0;c=k[a+8>>2]|0}if(d>>>0>b>>>0){b=je(c+b|0,44,d-b|0)|0;c=(b|0)==0?-1:b-c|0}else c=-1;return c|0}function Je(){var a=0,b=0,c=0,d=0,e=0,f=0,g=0,h=0;e=r;r=r+48|0;g=e+32|0;c=e+24|0;h=e+16|0;f=e;e=e+36|0;a=Ke()|0;if(a|0?(d=k[a>>2]|0,d|0):0){a=d+48|0;b=k[a>>2]|0;a=k[a+4>>2]|0;if(!((b&-256|0)==1126902528&(a|0)==1129074247)){k[c>>2]=1096989;Ne(1097084,c)}if((b|0)==1126902529&(a|0)==1129074247)a=k[d+44>>2]|0;else a=d+80|0;k[e>>2]=a;d=k[d>>2]|0;a=k[d+4>>2]|0;if(Te(8,d,e)|0){h=k[e>>2]|0;h=Xa[k[(k[h>>2]|0)+8>>2]&3](h)|0;k[f>>2]=1096989;k[f+4>>2]=a;k[f+8>>2]=h;Ne(1096998,f)}else{k[h>>2]=1096989;k[h+4>>2]=a;Ne(1097043,h)}}Ne(1097122,g)}function Ke(){var a=0,b=0;a=r;r=r+16|0;if(!(Ia(1097808,2)|0)){b=Ga(k[274453]|0)|0;r=a;return b|0}else Ne(1096810,a);return 0}function Le(){var a=0;a=r;r=r+16|0;if(!(Ja(1097812,9)|0)){r=a;return}else Ne(1096760,a)}function Me(a){a=a|0;var b=0;b=r;r=r+16|0;we(a);if(!(La(k[274453]|0,0)|0)){r=b;return}else Ne(1096707,b)}function Ne(a,b){a=a|0;b=b|0;var c=0;c=r;r=r+16|0;k[c>>2]=b;de(941016,a,c)|0;re();Na()}function Oe(a){a=a|0;return}function Pe(a){a=a|0;Qe(a);return}function Qe(a){a=a|0;we(a);return}function Re(a){a=a|0;return}function Se(a){a=a|0;return}function Te(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0;g=r;r=r+64|0;f=g;if((a|0)!=(b|0))if((b|0)!=0?(e=Ue(b,16)|0,(e|0)!=0):0){b=f;d=b+56|0;do{k[b>>2]=0;b=b+4|0}while((b|0)<(d|0));k[f>>2]=e;k[f+8>>2]=a;k[f+12>>2]=-1;k[f+48>>2]=1;_a[k[(k[e>>2]|0)+28>>2]&3](e,f,k[c>>2]|0,1);if((k[f+24>>2]|0)==1){k[c>>2]=k[f+16>>2];b=1}else b=0}else b=0;else b=1;r=g;return b|0}function Ue(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,g=0,h=0,l=0,m=0,n=0,o=0,p=0,q=0,s=0;s=r;r=r+64|0;q=s;p=k[a>>2]|0;o=a+(k[p+-8>>2]|0)|0;p=k[p+-4>>2]|0;k[q>>2]=b;k[q+4>>2]=a;k[q+8>>2]=48;h=q+12|0;l=q+16|0;a=q+20|0;c=q+24|0;d=q+28|0;e=q+32|0;f=q+40|0;g=(p|0)==(b|0);m=h;n=m+40|0;do{k[m>>2]=0;m=m+4|0}while((m|0)<(n|0));j[h+40>>1]=0;i[h+42>>0]=0;a:do if(g){k[q+48>>2]=1;Za[k[(k[b>>2]|0)+20>>2]&3](b,q,o,o,1,0);a=(k[c>>2]|0)==1?o:0}else{Va[k[(k[p>>2]|0)+24>>2]&3](p,q,o,1,0);switch(k[q+36>>2]|0){case 0:{a=(k[f>>2]|0)==1&(k[d>>2]|0)==1&(k[e>>2]|0)==1?k[a>>2]|0:0;break a}case 1:break;default:{a=0;break a}}if((k[c>>2]|0)!=1?!((k[f>>2]|0)==0&(k[d>>2]|0)==1&(k[e>>2]|0)==1):0){a=0;break}a=k[l>>2]|0}while(0);r=s;return a|0}function Ve(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;if((a|0)==(k[b+8>>2]|0))We(b,c,d,e);else{a=k[a+8>>2]|0;Za[k[(k[a>>2]|0)+20>>2]&3](a,b,c,d,e,f)}return}function We(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0;i[a+53>>0]=1;do if((k[a+4>>2]|0)==(c|0)){i[a+52>>0]=1;c=a+16|0;e=k[c>>2]|0;if(!e){k[c>>2]=b;k[a+24>>2]=d;k[a+36>>2]=1;if(!((d|0)==1?(k[a+48>>2]|0)==1:0))break;i[a+54>>0]=1;break}if((e|0)!=(b|0)){d=a+36|0;k[d>>2]=(k[d>>2]|0)+1;i[a+54>>0]=1;break}e=a+24|0;c=k[e>>2]|0;if((c|0)==2){k[e>>2]=d;c=d}if((c|0)==1?(k[a+48>>2]|0)==1:0)i[a+54>>0]=1}while(0);return}function Xe(a,b,c,d,e){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;var f=0,g=0,h=0;do if((a|0)==(k[b+8>>2]|0)){if((k[b+4>>2]|0)==(c|0)?(f=b+28|0,(k[f>>2]|0)!=1):0)k[f>>2]=d}else{if((a|0)!=(k[b>>2]|0)){h=k[a+8>>2]|0;Va[k[(k[h>>2]|0)+24>>2]&3](h,b,c,d,e);break}if((k[b+16>>2]|0)!=(c|0)?(h=b+20|0,(k[h>>2]|0)!=(c|0)):0){k[b+32>>2]=d;g=b+44|0;if((k[g>>2]|0)==4)break;f=b+52|0;i[f>>0]=0;d=b+53|0;i[d>>0]=0;a=k[a+8>>2]|0;Za[k[(k[a>>2]|0)+20>>2]&3](a,b,c,c,1,e);if(i[d>>0]|0)if(!(i[f>>0]|0)){f=1;d=13}else d=17;else{f=0;d=13}do if((d|0)==13){k[h>>2]=c;c=b+40|0;k[c>>2]=(k[c>>2]|0)+1;if((k[b+36>>2]|0)==1?(k[b+24>>2]|0)==2:0){i[b+54>>0]=1;if(f){d=17;break}else{f=4;break}}if(f)d=17;else f=4}while(0);if((d|0)==17)f=3;k[g>>2]=f;break}if((d|0)==1)k[b+32>>2]=1}while(0);return}function Ye(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;if((a|0)==(k[b+8>>2]|0))Ze(b,c,d);else{a=k[a+8>>2]|0;_a[k[(k[a>>2]|0)+28>>2]&3](a,b,c,d)}return}function Ze(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0;d=a+16|0;e=k[d>>2]|0;do if(e){if((e|0)!=(b|0)){c=a+36|0;k[c>>2]=(k[c>>2]|0)+1;k[a+24>>2]=2;i[a+54>>0]=1;break}d=a+24|0;if((k[d>>2]|0)==2)k[d>>2]=c}else{k[d>>2]=b;k[a+24>>2]=c;k[a+36>>2]=1}while(0);return}function _e(a){a=a|0;Qe(a);return}function $e(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;if((a|0)==(k[b+8>>2]|0))We(b,c,d,e);return}function af(a,b,c,d,e){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;var f=0,g=0;do if((a|0)==(k[b+8>>2]|0)){if((k[b+4>>2]|0)==(c|0)?(g=b+28|0,(k[g>>2]|0)!=1):0)k[g>>2]=d}else if((a|0)==(k[b>>2]|0)){if((k[b+16>>2]|0)!=(c|0)?(f=b+20|0,(k[f>>2]|0)!=(c|0)):0){k[b+32>>2]=d;k[f>>2]=c;e=b+40|0;k[e>>2]=(k[e>>2]|0)+1;if((k[b+36>>2]|0)==1?(k[b+24>>2]|0)==2:0)i[b+54>>0]=1;k[b+44>>2]=4;break}if((d|0)==1)k[b+32>>2]=1}while(0);return}function bf(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;if((a|0)==(k[b+8>>2]|0))Ze(b,c,d);return}function cf(){var a=0,b=0,c=0,d=0;c=r;r=r+16|0;d=c+8|0;a=Ke()|0;if((a|0?(b=k[a>>2]|0,b|0):0)?(a=b+48|0,(k[a>>2]&-256|0)==1126902528?(k[a+4>>2]|0)==1129074247:0):0){Ya[k[b+12>>2]&3]();Ne(1097134,c)}c=k[235310]|0;k[235310]=c+0;Ya[c&3]();Ne(1097134,d)}function df(a){a=a|0;return}function ef(a){a=a|0;Qe(a);return}function ff(a){a=a|0;return 1097187}function gf(a){a=a|0;var b=0,c=0;b=(a|0)==0?1:a;while(1){c=ve(b)|0;if(c|0){a=6;break}a=hf()|0;if(!a){a=5;break}Ya[a&3]()}if((a|0)==5){c=xa(4)|0;k[c>>2]=941332;Ma(c|0,72,6)}else if((a|0)==6)return c|0;return 0}function hf(){var a=0;a=k[274454]|0;k[274454]=a+0;return a|0}function jf(a){a=a|0;return gf(a)|0}function kf(a){a=a|0;Qe(a);return}function lf(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0;e=r;r=r+16|0;d=e;k[d>>2]=k[c>>2];a=Ua[k[(k[a>>2]|0)+16>>2]&7](a,b,d)|0;if(a)k[c>>2]=k[d>>2];r=e;return a&1|0}function mf(a){a=a|0;if(!a)a=0;else a=(Ue(a,104)|0)!=0;return a&1|0}function nf(){}function of(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;c=a+c>>>0;return (L=b+d+(c>>>0<a>>>0|0)>>>0,c|0)|0}function pf(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;d=b-d-(c>>>0>a>>>0|0)>>>0;return (L=d,a-c>>>0|0)|0}function qf(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0;d=a+c|0;if((c|0)>=20){b=b&255;f=a&3;g=b|b<<8|b<<16|b<<24;e=d&~3;if(f){f=a+4-f|0;while((a|0)<(f|0)){i[a>>0]=b;a=a+1|0}}while((a|0)<(e|0)){k[a>>2]=g;a=a+4|0}}while((a|0)<(d|0)){i[a>>0]=b;a=a+1|0}return a-c|0}function rf(a,b,c){a=a|0;b=b|0;c=c|0;if((c|0)<32){L=b>>>c;return a>>>c|(b&(1<<c)-1)<<32-c}L=0;return b>>>c-32|0}function sf(a,b,c){a=a|0;b=b|0;c=c|0;if((c|0)<32){L=b<<c|(a&(1<<c)-1<<32-c)>>>32-c;return a<<c}L=a<<c-32;return 0}function tf(a,b,c){a=a|0;b=b|0;c=c|0;var d=0;if((c|0)>=4096)return Ca(a|0,b|0,c|0)|0;d=a|0;if((a&3)==(b&3)){while(a&3){if(!c)return d|0;i[a>>0]=i[b>>0]|0;a=a+1|0;b=b+1|0;c=c-1|0}while((c|0)>=4){k[a>>2]=k[b>>2];a=a+4|0;b=b+4|0;c=c-4|0}}while((c|0)>0){i[a>>0]=i[b>>0]|0;a=a+1|0;b=b+1|0;c=c-1|0}return d|0}function uf(a,b,c){a=a|0;b=b|0;c=c|0;var d=0;if((b|0)<(a|0)&(a|0)<(b+c|0)){d=a;b=b+c|0;a=a+c|0;while((c|0)>0){a=a-1|0;b=b-1|0;c=c-1|0;i[a>>0]=i[b>>0]|0}a=d}else tf(a,b,c)|0;return a|0}function vf(a,b,c){a=a|0;b=b|0;c=c|0;if((c|0)<32){L=b>>c;return a>>>c|(b&(1<<c)-1)<<32-c}L=(b|0)<0?-1:0;return b>>c-32|0}function wf(a){a=a|0;var b=0;b=i[v+(a&255)>>0]|0;if((b|0)<8)return b|0;b=i[v+(a>>8&255)>>0]|0;if((b|0)<8)return b+8|0;b=i[v+(a>>16&255)>>0]|0;if((b|0)<8)return b+16|0;return (i[v+(a>>>24)>>0]|0)+24|0}function xf(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0;f=a&65535;e=b&65535;c=ha(e,f)|0;d=a>>>16;a=(c>>>16)+(ha(e,d)|0)|0;e=b>>>16;b=ha(e,f)|0;return (L=(a>>>16)+(ha(e,d)|0)+(((a&65535)+b|0)>>>16)|0,a+b<<16|c&65535|0)|0}function yf(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0;j=b>>31|((b|0)<0?-1:0)<<1;i=((b|0)<0?-1:0)>>31|((b|0)<0?-1:0)<<1;f=d>>31|((d|0)<0?-1:0)<<1;e=((d|0)<0?-1:0)>>31|((d|0)<0?-1:0)<<1;h=pf(j^a|0,i^b|0,j|0,i|0)|0;g=L;a=f^j;b=e^i;return pf((Df(h,g,pf(f^c|0,e^d|0,f|0,e|0)|0,L,0)|0)^a|0,L^b|0,a|0,b|0)|0}function zf(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0;e=r;r=r+16|0;h=e|0;g=b>>31|((b|0)<0?-1:0)<<1;f=((b|0)<0?-1:0)>>31|((b|0)<0?-1:0)<<1;j=d>>31|((d|0)<0?-1:0)<<1;i=((d|0)<0?-1:0)>>31|((d|0)<0?-1:0)<<1;a=pf(g^a|0,f^b|0,g|0,f|0)|0;b=L;Df(a,b,pf(j^c|0,i^d|0,j|0,i|0)|0,L,h)|0;d=pf(k[h>>2]^g|0,k[h+4>>2]^f|0,g|0,f|0)|0;c=L;r=e;return (L=c,d)|0}function Af(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0;e=a;f=c;c=xf(e,f)|0;a=L;return (L=(ha(b,f)|0)+(ha(d,e)|0)+a|a&0,c|0|0)|0}function Bf(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;return Df(a,b,c,d,0)|0}function Cf(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0;f=r;r=r+16|0;e=f|0;Df(a,b,c,d,e)|0;r=f;return (L=k[e+4>>2]|0,k[e>>2]|0)|0}function Df(a,b,c,d,e){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;var f=0,g=0,h=0,i=0,j=0,l=0,m=0,n=0,o=0,p=0;l=a;i=b;j=i;g=c;n=d;h=n;if(!j){f=(e|0)!=0;if(!h){if(f){k[e>>2]=(l>>>0)%(g>>>0);k[e+4>>2]=0}n=0;e=(l>>>0)/(g>>>0)>>>0;return (L=n,e)|0}else{if(!f){n=0;e=0;return (L=n,e)|0}k[e>>2]=a|0;k[e+4>>2]=b&0;n=0;e=0;return (L=n,e)|0}}f=(h|0)==0;do if(g){if(!f){f=(ja(h|0)|0)-(ja(j|0)|0)|0;if(f>>>0<=31){m=f+1|0;h=31-f|0;b=f-31>>31;g=m;a=l>>>(m>>>0)&b|j<<h;b=j>>>(m>>>0)&b;f=0;h=l<<h;break}if(!e){n=0;e=0;return (L=n,e)|0}k[e>>2]=a|0;k[e+4>>2]=i|b&0;n=0;e=0;return (L=n,e)|0}f=g-1|0;if(f&g|0){h=(ja(g|0)|0)+33-(ja(j|0)|0)|0;p=64-h|0;m=32-h|0;i=m>>31;o=h-32|0;b=o>>31;g=h;a=m-1>>31&j>>>(o>>>0)|(j<<m|l>>>(h>>>0))&b;b=b&j>>>(h>>>0);f=l<<p&i;h=(j<<p|l>>>(o>>>0))&i|l<<m&h-33>>31;break}if(e|0){k[e>>2]=f&l;k[e+4>>2]=0}if((g|0)==1){o=i|b&0;p=a|0|0;return (L=o,p)|0}else{p=wf(g|0)|0;o=j>>>(p>>>0)|0;p=j<<32-p|l>>>(p>>>0)|0;return (L=o,p)|0}}else{if(f){if(e|0){k[e>>2]=(j>>>0)%(g>>>0);k[e+4>>2]=0}o=0;p=(j>>>0)/(g>>>0)>>>0;return (L=o,p)|0}if(!l){if(e|0){k[e>>2]=0;k[e+4>>2]=(j>>>0)%(h>>>0)}o=0;p=(j>>>0)/(h>>>0)>>>0;return (L=o,p)|0}f=h-1|0;if(!(f&h)){if(e|0){k[e>>2]=a|0;k[e+4>>2]=f&j|b&0}o=0;p=j>>>((wf(h|0)|0)>>>0);return (L=o,p)|0}f=(ja(h|0)|0)-(ja(j|0)|0)|0;if(f>>>0<=30){b=f+1|0;h=31-f|0;g=b;a=j<<h|l>>>(b>>>0);b=j>>>(b>>>0);f=0;h=l<<h;break}if(!e){o=0;p=0;return (L=o,p)|0}k[e>>2]=a|0;k[e+4>>2]=i|b&0;o=0;p=0;return (L=o,p)|0}while(0);if(!g){j=h;i=0;h=0}else{m=c|0|0;l=n|d&0;j=of(m|0,l|0,-1,-1)|0;c=L;i=h;h=0;do{d=i;i=f>>>31|i<<1;f=h|f<<1;d=a<<1|d>>>31|0;n=a>>>31|b<<1|0;pf(j|0,c|0,d|0,n|0)|0;p=L;o=p>>31|((p|0)<0?-1:0)<<1;h=o&1;a=pf(d|0,n|0,o&m|0,(((p|0)<0?-1:0)>>31|((p|0)<0?-1:0)<<1)&l|0)|0;b=L;g=g-1|0}while((g|0)!=0);j=i;i=0}g=0;if(e|0){k[e>>2]=a;k[e+4>>2]=b}o=(f|0)>>>31|(j|g)<<1|(g<<1|f>>>31)&0|i;p=(f<<1|0>>>31)&-2|h;return (L=o,p)|0}function Ef(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;return Ua[a&7](b|0,c|0,d|0)|0}function Ff(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;Va[a&3](b|0,c|0,d|0,e|0,f|0)}function Gf(a,b){a=a|0;b=b|0;Wa[a&15](b|0)}function Hf(a,b){a=a|0;b=b|0;return Xa[a&3](b|0)|0}function If(a){a=a|0;Ya[a&3]()}function Jf(a,b,c,d,e,f,g){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;g=g|0;Za[a&3](b|0,c|0,d|0,e|0,f|0,g|0)}function Kf(a,b,c,d,e){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;_a[a&3](b|0,c|0,d|0,e|0)}function Lf(a,b,c){a=a|0;b=b|0;c=c|0;ka(0);return 0}function Mf(a,b,c,d,e){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;ka(1)}function Nf(a){a=a|0;ka(2)}function Of(a){a=a|0;ka(3);return 0}function Pf(){ka(4)}function Qf(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;ka(5)}function Rf(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;ka(6)}

// EMSCRIPTEN_END_FUNCS
var Ua=[Lf,Rd,Td,ce,Te,Lf,Lf,Lf];var Va=[Mf,af,Xe,Mf];var Wa=[Nf,Oe,_e,Re,Se,Pe,df,ef,Sd,Me,Nf,Nf,Nf,Nf,Nf,Nf];var Xa=[Of,Od,ff,Of];var Ya=[Pf,Je,Le,Pf];var Za=[Qf,$e,Ve,Qf];var _a=[Rf,bf,Ye,Rf];return{_emscripten_bind_LanguageInfo_getLanguageCode_0:Jd,_bitshift64Lshr:rf,_bitshift64Shl:sf,_malloc:ve,___cxa_is_pointer_type:mf,_emscripten_bind_LanguageGuess_getPercent_0:Ad,_emscripten_bind_VoidPtr___destroy___0:Nd,_memset:qf,_memcpy:tf,_emscripten_bind_LanguageInfo_getIsReliable_0:Id,_i64Subtract:pf,_emscripten_bind_LanguageInfo___destroy___0:Bd,_i64Add:of,_emscripten_bind_LanguageInfo_get_languages_1:Kd,_emscripten_bind_Language_getLanguageCode_0:Md,_emscripten_bind_LanguageGuess___destroy___0:yd,_emscripten_bind_Language___destroy___0:Ld,___cxa_can_catch:lf,_free:we,_emscripten_bind_LanguageInfo_detectLanguage_5:Gd,_memmove:uf,_emscripten_bind_LanguageInfo_detectLanguage_2:Dd,_emscripten_bind_LanguageGuess_getLanguageCode_0:zd,runPostSets:nf,_emscripten_replace_memory:Ta,stackAlloc:$a,stackSave:ab,stackRestore:bb,establishStackSpace:cb,setThrew:db,setTempRet0:gb,getTempRet0:hb,dynCall_iiii:Ef,dynCall_viiiii:Ff,dynCall_vi:Gf,dynCall_ii:Hf,dynCall_v:If,dynCall_viiiiii:Jf,dynCall_viiii:Kf}})


// EMSCRIPTEN_END_ASM
(c.L,c.M,buffer),zb=c._emscripten_bind_LanguageInfo_getLanguageCode_0=M._emscripten_bind_LanguageInfo_getLanguageCode_0,pb=c._bitshift64Lshr=M._bitshift64Lshr,qb=c._bitshift64Shl=M._bitshift64Shl,Ab=c._emscripten_bind_LanguageGuess_getLanguageCode_0=M._emscripten_bind_LanguageGuess_getLanguageCode_0;c.___cxa_is_pointer_type=M.___cxa_is_pointer_type;
var Bb=c._emscripten_bind_LanguageGuess_getPercent_0=M._emscripten_bind_LanguageGuess_getPercent_0,Cb=c._emscripten_bind_VoidPtr___destroy___0=M._emscripten_bind_VoidPtr___destroy___0,nb=c._memset=M._memset,sb=c._memcpy=M._memcpy,Db=c._emscripten_bind_LanguageInfo_getIsReliable_0=M._emscripten_bind_LanguageInfo_getIsReliable_0,gb=c._i64Subtract=M._i64Subtract,Eb=c._emscripten_bind_LanguageInfo___destroy___0=M._emscripten_bind_LanguageInfo___destroy___0,fb=c._i64Add=M._i64Add,Fb=c._emscripten_bind_LanguageInfo_get_languages_1=
M._emscripten_bind_LanguageInfo_get_languages_1,Gb=c._emscripten_bind_Language_getLanguageCode_0=M._emscripten_bind_Language_getLanguageCode_0,Hb=c._emscripten_bind_LanguageGuess___destroy___0=M._emscripten_bind_LanguageGuess___destroy___0,Ib=c._emscripten_bind_Language___destroy___0=M._emscripten_bind_Language___destroy___0;c.___cxa_can_catch=M.___cxa_can_catch;var Ga=c._free=M._free;c.runPostSets=M.runPostSets;
var Jb=c._emscripten_bind_LanguageInfo_detectLanguage_5=M._emscripten_bind_LanguageInfo_detectLanguage_5,wb=c._memmove=M._memmove,Kb=c._emscripten_bind_LanguageInfo_detectLanguage_2=M._emscripten_bind_LanguageInfo_detectLanguage_2,D=c._malloc=M._malloc,Oa=c._emscripten_replace_memory=M._emscripten_replace_memory;c.dynCall_iiii=M.dynCall_iiii;c.dynCall_viiiii=M.dynCall_viiiii;c.dynCall_vi=M.dynCall_vi;c.dynCall_ii=M.dynCall_ii;c.dynCall_v=M.dynCall_v;c.dynCall_viiiiii=M.dynCall_viiiiii;
c.dynCall_viiii=M.dynCall_viiii;t.n=M.stackAlloc;t.w=M.stackSave;t.o=M.stackRestore;t.ba=M.establishStackSpace;t.V=M.setTempRet0;t.R=M.getTempRet0;
if(K)if("function"===typeof c.locateFile?K=c.locateFile(K):c.memoryInitializerPrefixURL&&(K=c.memoryInitializerPrefixURL+K),m||ca){var Lb=c.readBinary(K);E.set(Lb,t.C)}else{var Nb=function(){c.readAsync(K,Mb,function(){throw"could not load memory initializer "+K;})};bb();var Mb=function(a){a.byteLength&&(a=new Uint8Array(a));E.set(a,t.C);c.memoryInitializerRequest&&delete c.memoryInitializerRequest.response;cb()};if(c.memoryInitializerRequest){var Ob=function(){var a=c.memoryInitializerRequest;200!==
a.status&&0!==a.status?(console.warn("a problem seems to have happened with Module.memoryInitializerRequest, status: "+a.status+", retrying "+K),Nb()):Mb(a.response)};c.memoryInitializerRequest.response?setTimeout(Ob,0):c.memoryInitializerRequest.addEventListener("load",Ob)}else Nb()}function n(a){this.name="ExitStatus";this.message="Program terminated with exit("+a+")";this.status=a}n.prototype=Error();n.prototype.constructor=n;var Pb=null,ab=function Qb(){c.calledRun||Rb();c.calledRun||(ab=Qb)};
c.callMain=c.Z=function(a){function b(){for(var a=0;3>a;a++)e.push(0)}a=a||[];za||(za=!0,H(Ta));var d=a.length+1,e=[xa(Za(c.thisProgram),"i8",0)];b();for(var f=0;f<d-1;f+=1)e.push(xa(Za(a[f]),"i8",0)),b();e.push(0);e=xa(e,"i32",0);try{var l=c._main(d,e,0);Sb(l,!0)}catch(h){if(!(h instanceof n))if("SimulateInfiniteLoop"==h)c.noExitRuntime=!0;else throw h&&"object"===typeof h&&h.stack&&c.u("exception thrown: "+[h,h.stack]),h;}finally{}};
function Rb(a){function b(){if(!c.calledRun&&(c.calledRun=!0,!ia)){za||(za=!0,H(Ta));H(Ua);if(c.onRuntimeInitialized)c.onRuntimeInitialized();c._main&&Tb&&c.callMain(a);if(c.postRun)for("function"==typeof c.postRun&&(c.postRun=[c.postRun]);c.postRun.length;)Ya(c.postRun.shift());H(Va)}}a=a||c.arguments;null===Pb&&(Pb=Date.now());if(!(0<J)){if(c.preRun)for("function"==typeof c.preRun&&(c.preRun=[c.preRun]);c.preRun.length;)Wa(c.preRun.shift());H(Sa);0<J||c.calledRun||(c.setStatus?(c.setStatus("Running..."),
setTimeout(function(){setTimeout(function(){c.setStatus("")},1);b()},1)):b())}}c.run=c.run=Rb;function Sb(a,b){if(!b||!c.noExitRuntime){if(!c.noExitRuntime&&(ia=!0,p=void 0,H(I),c.onExit))c.onExit(a);m?process.exit(a):ca&&"function"===typeof quit&&quit(a);throw new n(a);}}c.exit=c.exit=Sb;var Ub=[];
function y(a){void 0!==a?(c.print(a),c.u(a),a=JSON.stringify(a)):a="";ia=!0;var b="abort("+a+") at "+Ea()+"\nIf this abort() is unexpected, build with -s ASSERTIONS=1 which can give more information.";Ub&&Ub.forEach(function(d){b=d(b,a)});throw b;}c.abort=c.abort=y;if(c.preInit)for("function"==typeof c.preInit&&(c.preInit=[c.preInit]);0<c.preInit.length;)c.preInit.pop()();var Tb=!1;c.noInitialRun&&(Tb=!1);c.noExitRuntime=!0;Rb();function R(){}R.prototype=Object.create(R.prototype);
R.prototype.constructor=R;R.prototype.c=R;R.e={};c.WrapperObject=R;function Vb(a){return(a||R).e}c.getCache=Vb;function S(a,b){var d=Vb(b),e=d[a];if(e)return e;e=Object.create((b||R).prototype);e.a=a;return d[a]=e}c.wrapPointer=S;c.castObject=function(a,b){return S(a.a,b)};c.NULL=S(0);c.destroy=function(a){if(!a.__destroy__)throw"Error: Cannot destroy object. (Did you create it yourself?)";a.__destroy__();delete Vb(a.c)[a.a]};c.compare=function(a,b){return a.a===b.a};c.getPointer=function(a){return a.a};
c.getClass=function(a){return a.c};
var T={buffer:0,size:0,j:0,p:[],i:0,t:function(){if(this.i){for(var a=0;a<this.p.length;a++)c._free(this.p[a]);this.p.length=0;c._free(this.buffer);this.buffer=0;this.size+=this.i;this.i=0}this.buffer||(this.size+=128,this.buffer=c._malloc(this.size),assert(this.buffer));this.j=0},f:function(a,b){assert(this.buffer);var d=b.BYTES_PER_ELEMENT,e=a.length*d,e=e+7&-8,f;this.j+e>=this.size?(assert(0<e),this.i+=e,f=c._malloc(e),this.p.push(f)):(f=this.buffer+this.j,this.j+=e);e=f;switch(d){case 2:e>>=1;
break;case 4:e>>=2;break;case 8:e>>=3}for(d=0;d<a.length;d++)b[e+d]=a[d];return f}};function Wb(a){return"string"===typeof a?T.f(Za(a),B):a}function U(){throw"cannot construct a Language, no constructor in IDL";}U.prototype=Object.create(R.prototype);U.prototype.constructor=U;U.prototype.c=U;U.e={};c.Language=U;U.prototype.getLanguageCode=U.prototype.m=function(){return z(Gb(this.a))};U.prototype.__destroy__=function(){Ib(this.a)};
function V(){throw"cannot construct a LanguageGuess, no constructor in IDL";}V.prototype=Object.create(U.prototype);V.prototype.constructor=V;V.prototype.c=V;V.e={};c.LanguageGuess=V;V.prototype.getPercent=V.prototype.P=function(){return Bb(this.a)};V.prototype.getLanguageCode=V.prototype.m=function(){return z(Ab(this.a))};V.prototype.__destroy__=function(){Hb(this.a)};function W(){throw"cannot construct a LanguageInfo, no constructor in IDL";}W.prototype=Object.create(U.prototype);
W.prototype.constructor=W;W.prototype.c=W;W.e={};c.LanguageInfo=W;
W.prototype.detectLanguage=W.prototype.b=function(a,b,d,e,f){var l=this.a;T.t();a&&"object"===typeof a?a=a.a:a=Wb(a);b&&"object"===typeof b&&(b=b.a);d&&"object"===typeof d?d=d.a:d=Wb(d);e&&"object"===typeof e&&(e=e.a);f&&"object"===typeof f?f=f.a:f=Wb(f);return void 0===d?S(Kb(l,a,b),W):void 0===e?S(_emscripten_bind_LanguageInfo_detectLanguage_3(l,a,b,d),W):void 0===f?S(_emscripten_bind_LanguageInfo_detectLanguage_4(l,a,b,d,e),W):S(Jb(l,a,b,d,e,f),W)};W.prototype.getIsReliable=W.prototype.N=function(){return!!Db(this.a)};
W.prototype.getLanguageCode=W.prototype.m=function(){return z(zb(this.a))};W.prototype.get_languages=W.prototype.S=function(a){var b=this.a;a&&"object"===typeof a&&(a=a.a);return S(Fb(b,a),V)};W.prototype.__destroy__=function(){Eb(this.a)};function Z(){throw"cannot construct a VoidPtr, no constructor in IDL";}Z.prototype=Object.create(R.prototype);Z.prototype.constructor=Z;Z.prototype.c=Z;Z.e={};c.VoidPtr=Z;Z.prototype.__destroy__=function(){Cb(this.a)};(function(){function a(){}c.calledRun||Xa(a)})();
W.g=W.prototype.b;T.f=T.f.bind(T);T.t=T.t.bind(T);
for(var Xb={ISO_8859_1:0,ISO_8859_2:1,ISO_8859_3:2,ISO_8859_4:3,ISO_8859_5:4,ISO_8859_6:5,ISO_8859_7:6,ISO_8859_8:7,ISO_8859_9:8,ISO_8859_10:9,JAPANESE_EUC_JP:10,EUC_JP:10,JAPANESE_SHIFT_JIS:11,SHIFT_JIS:11,JAPANESE_JIS:12,JIS:12,CHINESE_BIG5:13,BIG5:13,CHINESE_GB:14,CHINESE_EUC_CN:15,EUC_CN:15,KOREAN_EUC_KR:16,EUC_KR:16,UNICODE_UNUSED:17,CHINESE_EUC_DEC:18,EUC_DEC:18,CHINESE_CNS:19,CNS:19,CHINESE_BIG5_CP950:20,BIG5_CP950:20,JAPANESE_CP932:21,CP932:21,UTF8:22,UNKNOWN_ENCODING:23,ASCII_7BIT:24,RUSSIAN_KOI8_R:25,
KOI8_R:25,RUSSIAN_CP1251:26,CP1251:26,MSFT_CP1252:27,CP1252:27,RUSSIAN_KOI8_RU:28,KOI8_RU:28,MSFT_CP1250:29,CP1250:29,ISO_8859_15:30,MSFT_CP1254:31,CP1254:31,MSFT_CP1257:32,CP1257:32,ISO_8859_11:33,MSFT_CP874:34,CP874:34,MSFT_CP1256:35,CP1256:35,MSFT_CP1255:36,CP1255:36,ISO_8859_8_I:37,HEBREW_VISUAL:38,CZECH_CP852:39,CP852:39,CZECH_CSN_369103:40,CSN_369103:40,MSFT_CP1253:41,CP1253:41,RUSSIAN_CP866:42,CP866:42,ISO_8859_13:43,ISO_2022_KR:44,GBK:45,GB18030:46,BIG5_HKSCS:47,ISO_2022_CN:48,TSCII:49,TAMIL_MONO:50,
TAMIL_BI:51,JAGRAN:52,MACINTOSH_ROMAN:53,UTF7:54,BHASKAR:55,HTCHANAKYA:56,UTF16BE:57,UTF16LE:58,UTF32BE:59,UTF32LE:60,BINARYENC:61,HZ_GB_2312:62,UTF8UTF8:63,TAM_ELANGO:64,TAM_LTTMBARANI:65,TAM_SHREE:66,TAM_TBOOMIS:67,TAM_TMNEWS:68,TAM_WEBTAMIL:69,KDDI_SHIFT_JIS:70,DOCOMO_SHIFT_JIS:71,SOFTBANK_SHIFT_JIS:72,KDDI_ISO_2022_JP:73,ISO_2022_JP:73,SOFTBANK_ISO_2022_JP:74},Yb=function(a){if(a.J)return a.J();if(!(a instanceof Array)&&"string"!=typeof a)throw Error();var b=0;return{next:function(){return b==
a.length?{done:!0}:{done:!1,value:a[b++]}}}}(Object.keys(Xb)),Zb=Yb.next();!Zb.done;Zb=Yb.next()){var $b=Zb.value;$b.includes("_")&&(Xb[$b.replace(/_/g,"")]=Xb[$b])}
Xa(function(){onmessage=function(a){a=a.data;var b=void 0;if(void 0==a.tld&&void 0==a.encoding&&void 0==a.language)b=W.g(a.text,!a.isHTML);else var d=String(a.encoding).toUpperCase().replace(/[_-]/g,""),e=void 0,e=Xb.hasOwnProperty(d)?Xb[d]:Xb.UNKNOWN_ENCODING,b=W.g(a.text,!a.isHTML,a.tld||null,e,a.language||null);postMessage({language:b.m(),confident:b.N(),languages:Array(3).fill(0).map(function(a,d){var e=b.S(d);return{languageCode:e.m(),percent:e.P()}}).filter(function(a){return"un"!=a.languageCode||
0<a.percent})});c.destroy(b)};postMessage("ready")});

PK
!<`ɖ/�/�%modules/translation/cld-worker.js.mem�\��\�0�\���\��@�\��\ֽ�\�0�\�X������������/"�(��2"�(���!e(���"�(��@!-(���!a(���!I(�[!6(�� (��!a(�"o(�5"�(��!J(��(!%(�8"�(�d!9(�F!/(�(!%(%�R!3(#�,�;"�(4��!N(<�� (B��!Y(H��!Y(O�
!(Y��!g(a�^!7(i��!k(r��"�({�� (����E(����E(��"p(��J"�(��
!(��� (D�� (
�� (��!j(�� (�>"�(i��� (�� �a!8(��!(�� (	�� 	(��!F(��A"�(�� ('�� (,�� (5�� (<��!C(D�!(J�!(S��"�(Y��!K(b�� (i�� (o�D"�({��!U(��U!4(��J"�(��G"�(���"�(��� (��"!#(@�"!#(@�� (�� (F���"�(�+!&((��g!:(�"q(���(�(	�(�(!(!(#� (+� (2� (9� (BI!0(Kj!;(SS"�(\"r(c}"�(i�!h(oV"�({�"�)�� 	(��!_(��!X(��!b(�.!'(�� (��m(�"s(�� (��!f(�%!$(�Y"�(�1!((&�4!)(�|!A(�"�()"�(y!@(#��"�(+5(8�!`(BN�(T�!g(\\"�(b�"�)j:!+(q� 
(P�x� 
(P�� 
(P�� 
(P��!P(
��!P(
�� 
(P��!P(
��"�(�L!1(�"n(�_"�(��!M(�"u(��"�(��!M(�� (�� (�� (� (	� (m!<("v($�!d(3� (<b"�(B� 
(Je"�(Qh"�(Wk"�(`�"�(�fp!=(s!({�"�(��!V(�� (�� (�"w(��!O(�v!?(�v!?(��!c(��!O(�n"�(��m(��!D(��v!?(��!](�� (NN�(��!G(� (s!>(� (�!l(! (%"x(+C!.(1 "y(7�!i(��=��E(F=!,(MX!5(R�!i(��Z�!L(c#"z(iq"�(pt"�(w "y(}!!(�!!(��!W(��!^(�!"(�O!2(��!H(��"�(�!B(�!B(�w"�(�7!*(�,"�(��!S(T���![(�&"{(���E(�� (� (
��E(��E(��E(&��E(,��E(2� (9z"�(@�!T(S�2"2"�(/"/"�(�!�!e(�"�"�(E@!-(�!HI(a([![!6(N[!6(R�!a(V[!6(""o(Z� (]� (5"5"�(�!�!J(`�!J(8"d�(N(F!F!/(jF!/(
!
!(R!R!3(#�;";"�(n��E(r1!((&�(!(!%(�!�!i(���!uY( �!�!N(^!^!7({^!7(�((�� (�!�!k(��!_(��E""p(�
!(�"�"�(� � (D�^!7(7!7!*(�7!*(�� (D� � (
�� (
�� � (�� (��!j(�� (
�� (�!�!j(>">"�(i�"!(�[!6(� � (� � �� a!a!8(�O!2(� � (�� (�!(!!(d!d!9(�!�!M(��!M(� � (�! (A"A"�(�!�!F(�� � (�� (�� (�!�!C(!�((��=((�!(�� (�� (p!p!=(u�!K(��!K(�� (!!(�!�!U(� (U!U!4(�"�"�(G"G"�(J"�(�"�"�(� (� � (
� ("!"!#(@���E(
!
!(J"J"�(� � (� (�!�!a(g!g!:(� (F�+!+!&((�+!&((�""q(�"�"�(M"�(�(+!&((� +!&((�$�(�(� � (F�� � ((� (P"�(�(,� (� � (/� (2� (6I!0(I!I!0(�!�!K(9"v(<�!X(?�!h(}"}"�(""r(D"D"�(�!�!h(j!j!;(� � 	(B� 	(F� 	(S"S"�(I� 	(�!�!_(�!�!X(M"r(.!.!'(P�!b(�!�!f(�"�"�(T� (""s(�!�!b(� � (X� (� � (�"�"�(Y"Y"�()")"�(%!%!$(4!4!)(�!�!`(55(\�!`(y!y!@(#�1!1!((&�|!|!A(`� (�!cg(((\"\"�(i� 
(P�:!:!+(� � (�!�!P(
�� � 
(P��"�"�)�"�"�(�"�"�(L!L!1(_"_"�(""n(m!l<(u(r�!M(v! (yO!2(� � (|m!<(�� (�� (""u(� � (�� (�� (""v(�!�!d(b"b"�(� � (�!(� � 
(�� 
(V"V"�(k"k"�(�"�"�(��!�!c(�� (h"h"�(�!�O(?(�!�!D(�v!v!?(�v!?(e"e"�(""w(�!�!](�� (@!@!-(!!(�!(�!(�!(n"n"�(�!�!V(�!�!G(� � (�� (s!s!>(�� (��!l(�!�!l(C!C!.(=!=!,(""x(X!X!5(�!�L(i(�"x(�!�!W(!! (�"�"�)t"t"�(#"#"z(!!!(q"q"�( " "y(�!��(E(��"�(�!"(�!�!^(!!"(O!O!2(�!�!H(�^!7(�^!7(�"�"�(!!B(�!B(�!B(w"w"�(,","�(�!�!S(T���!S(T��!�![(&"&"{(z"z"�(� � (���E(�!�!T(S���7�6�!uM�E-�!a��[!Z]���!J8"N �%F!
!R!6;"�r(!(&��!��!�(i��/^!{7 #&�"),/7!!�D�� 2�
�58>"6;�"�6��� !I�� A"��!F�� !p!uK>!��!xEA
! N�J"�� +!&(�"DG�J6MM� F�� P6/<X?h�!S	F	V6Mr.!b�!6Y\O_V� bf� e6hknq� �Y"%!$4!tg�!`5Ewy!6|!Azj`�!(&�}\"�:!�U� � 
P�+�"�n��_"6m!�v�v y2� �"6� �U�6�� � ��� 
V"�k"6���!6��!?�!D�e""n"6�!
� �6�"X!5�x�!t"6�!!�!E�>��"�!�!���!H�'�"�B��6z"e�
e[PDF
=J
@(&M#M#!2
TS�?�i��@V<ClCK�����L
����
��         	
 
                   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ �   � &   ! �0 `9 R }      "   �"!a: S ~x���������������������������������������������������������������������������������������������������&����������������������! ����������	���>�#�*�0�7�<�A�G<J�Q�T�[�^Rd�k�q�x�~������������3 ���"�X���`����������������������
x
�
�
�
�
�%
�,
5!4
�:
&>
'"B
 "F
'K
�Q
H"W
�^
�c
 i
�n
�u
" z
)"~

�
��
��
��
��
��
c&�
E"�
��
�!�
*"�
��
�!�
  �
�!�
��
��
f&�
��
��
�
��
� "   "�*a"0�4�8�=� B"H�M"T�[�b�iD o�ue"x>{�!��!�e&�& ���������!�"�+"�����"������!���)#����!�#� d"
#"�"�% 9  � <#�( .�4�;"A�D"J�O :!`"U"X�\	"b�"g�n�q�x�~S���> ������""("����������"�����"�0 ��"�����������2 �""�"�!"*# �&�!+Y2	#8 >!C�G#N�R V: ] c iap�"u�z�~����<"�`&��"��"�"��"��������"�����4"�����	 �������"!��!���!����"�*�/!6�9�@�D�I�N
 R W_flt{����������������")1;DLR[cku{����������������!(4<DQYcks~���������������#*15=@GNV`iry��������������� &,29=AEIOU_dks{���������������")/5;@LQVgpx|��������������	
!%)-159=AEIMQUY]aeimquy}��������������������������������	
!%)-159=AEIMQUY]aeimquy}��������������������������������	
!%)-159=AEIMQUY]aeimquy}��������������������������������	
!%)-159=AEIMQUY]aeimquy}��������������������������������	
!%)-159=AEIMQUY]aeimquy}��������������������������������	
.:CMZcks~�������������	 *4?HS^t|�������������(3>HR\fqz�����������%2=FOXahs~��������� , 9 B J S ] f y � � � � � � � � � � � � � � � � � � � � � � � � � � � � !!!
!
!!!!!!!"!%!(!+!.!1!4!7!:!=!@!C!F!I!L!O!R!U!X![!^!a!d!g!j!m!p!s!v!y!|!!�!�!�!�!�!�!�!�!�!�!�!�!�!�!�����!�!�!�!�!�!�!�!�!���!�!�!�!�!�!�!�!�!�!�!�!�!�!�!�!�!""""""��"""" "#"&"��������)","/"2"5"8";">"A"D"G"J"M"P"S"V"Y"\"_"b"e"h"k"n"q"t"w"z"}"�"�"�"�"�"�"�"�"�"�"�"�"�"�"�"�"�"�"�"�"�"�"�"�"�"�"�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������"�"�"�"�"�"�"#
###"#*#2#:#B#J#R#Z#b#j#r#z#�#�#�#�#�#�#�#�#�#�#�#�#�#�#�#�#$
$$$"$*$2$:$B$J$R$Z$b$j$r$z$�$�$�$�$�$�$�$�$�$�$�$�$�$�$�$�$%
%%%"%*%2%:%B%J%R%Z%b%j%r%z%�%�%�%�%�%�%�%�%�%�%�%�%�%�%�%�%&
&&&"&>(C(H(M(R(W(\(a(f(k(p(u(z((�(�(�(�(�(*(�(�(�(�(/(�(�(�(�(�(�(�(�����(���(�(�(�(�(�(�(�()))))))$))).)3)8)=)B)G)L)Q)V)[)`)e)j)o)t)y)~)�)�)�)�)�)�)4(�)�)�)�)�)9(�)�)�)�)�)�)�)�)�)�)�)�)�)�)**
***
a6lj#%<4n.,;)O5bigKIk�h`"$%&'( *+,m./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcde@%����/�\���8��C���P)3/�BaB-BBBB-�aa�a����---�--�---�------�a�aa�a�a�a�a�aaa�a�aa��a�a�aa�a���a�a��aa�a��a�a���a�a�a�aa��a���-�-�-�-�-U-U-U-�-�-U-U-	-��UaUUa	aUU��U		a��aUa�a	a	aUa�a�a��aUaUaU	a	a	Ua�a���a	UUa�	U	-	-		-		-	-----K�"�6+3`��E�;��)�s���@5����&��\���L�@M�����w�`%�|�@@�	�K���
�#��D�����r�	�7�
��@A�
�,� =��(�
0��p"�
p~�
�<��X��3���P���-�
�)��@�pO���P�0n��'�
P�����p��;��7�
 f����po��~� 8�9���`E�`*�`X�A�
��@L��<��8��n��v�@�`R����\����p��9��]�P �@G��3��-��I�@q�p����
�?� C� /�
���
�f��L��^������	@��
�x��b���P:��^�@��0f��� -�����p#�
�a�0����H�0Z��
`y���F�
 �� ���
�K�0j�
�9�����B����p\�
�%� k�~�	�G��`��&����G����
`��@��B�-��|� �@G�0������`%�P#��E��d�
��M�
����;�`��,�	�]��v����g�@v��g�`n�
�� y�	0��	���W�`8��\��B���P��f�
�?�D���C�`'�
��@�X��=�
 �@��(�И�Њ�@:� &�	�P���E�
���
�� 8�@!�
��?�к�PJ�P
�PE�pD������ ����j�@[��T� �`C� q��D��?�
��`�	P�r��09�
P��S������pG�
P�� ,�
p}�������`;��
��`3�e��w����pn����pI��J� �
b�$� H�І��Q�
�$�@z�pd���p\�������f�?�
�^��E��$�
 .�`��@!��D��g��8��c��D�@o��>�
 }������� 9� ��H� 8�@6� �pE�	��@�p2��A���=�`f���0b�p���5�@�0��e�0��B�G�PG��b��9�	�y������C�@�`�
���@0���`��l�@?��%���
��
��|����5�`j��E�
@��
`n��U�@`�PQ��7��R��
pm�
�M�_��"�P@��<�
�[�0e�P]��+��k�2�
�+����@��v��p�Px�p� ��
0�`~��C�����
��B�k��.��l�p2�}��h�pp�0�����X� '���`I��)��7� _����
P=��A� �
�@�� �*��t��#� F��D��`7�@����)��k��A��c�г�p�������x����	�t��h�
���B�0^�0f�
�B�P��0H�p,�p�02�`��t��^� V���@�����E���@�n����ж�
P��c�x�����W�`�Ј��^�Pf��5�`������C��<��W��W��W����Y����@l�Pt�G����_�@��P����P�����0!�	@�� �� {�
C�P��	�)�
P�@s�0_� ?��
���p;��(���PA�
P�-���`R��#�pS�Ј� ��q� ��`���02��S��A��>�	P�o������P����#�@U��;� D�
� ���F��:�`W�m��E��8��R�P���j�	�8�	��P�X��f� ���7� ~�
�E����:��\�
 Z��*�@��
>�@�0q�P8�p���!��#�
u�
��p���p}��
�PF�/�p����	�[��*�@#�0/��[� �
00�`u��D�
`A�����@���7��u�L���Pw�P��@�0:��R��0�
0&��!���
�~����p(�{�P�pI���	0x��:��6��V��;���`i� ^����
���`5����`2��	���`q����$�
p������ 9��{�@E��:�p=�p ��!�P�����
��� D��?��^�0�	@�@?�0��
@��������6�05�@��
���`|�P���f��4����`�P*�� #���p%��$��:�����l�P��
�!�\����0��`e��1����%�@]��\�;��b���
����P��
pe�
����8�`;����
0�`��v�@%� ��6�`�	p,����P��<� ��@��`���`��p��P��
?�Ђ�`<�
P.�`�0d��O�H�p����`�PT�$��z�P_�	�z����?��"�
05�����6��L��M�P`�@���}�	����3��`+�
��
�@�@E�@^�0M���	��P�`O���@5�~��&�
�%��������	������J�PF����#�c��"�6�p����!�Pd�@����P���'��~��W��}�
�I��S�0&�9�	�T����\�
p'��� 7�
�%�@����%�
@��pn��Q���@s��/��'����pY��7��h�@S��B�|�@��^�L���p%�pR�P�p6� $�����`D���`��p�p��
�_��&�`�
�<�pn��b�p#��{���p��6�J�`R�$����C�
����n�0(��?�0��0v��)�.�
(��U�	�� H�P6�
�G�К�
@.�pv�����~�@��|�@F�+����b��:�0��D��^��Y���� 9�����Z���p��m��G�p���������	�������G���p��w� A�`d�
pE�	q�01����
G��q�
p�0%��Q� ��0��m� g�����?�
0B�@3��a�
`W��}�`����P��P��N�`��
�(��*��f��n� I����w���������_����v�p��0D��`��p$�F��c�0Z�`>�
�D��$���@\�@��`��L�
�1���*��Z����@�&������
�0�
07��3��C�ps�Pg�01��������`b��>���`�`��
�#�����I�
�	P@����p"�`f��f��)�pG�
�$�`Z�
F�
�����p����	���p��s�
 H�0t�0h� .�&�0�@��y��;��&� ��D��e�Pi�p[��<��#��K�� !���v� ���V�`���0�@��D����
���	�f� )�p+�`��:�0B�o����Є�0�
�U��c���
0���l�@���@��	�pj����d�
`�pA�
�c��W�'�@��U��`�X�
�_��5��5��f� +��e�`]���
��$�0M��,�6�P�����4�	`h�������`8�`U��6�p%��T������p�����a�p)�`��`����V�0����X��R��^��D��n����:����o�p:�`���Q�
���	K�
#�@�	���������
@�6�	�C�@��r��G��$�������*��}�п�`e�@a����
P"� p�@n��%�@|�PA�PD�
�`��P2��O��G�	`��p,���@�P��v�
��X��/�`G�0�pF����W�
@A��,�@�@�`o��A��O�@M��F��'�pQ�0�p� z�Pf�$��+�0%�
p��S�`C����`]��E��\�`��PK��8��� e�@}�@0�@�p���x�P9�
� ����$�p�PJ�01�`3�PJ�	���f��<�`��`#�@0�
�s�@T��C���Pd�������7�
`��!��� �������p���6�2�0v�0<�
��p�� t���0@��F��^�`,�����@�O�2�0<�p��@��`,� �
���c����P=�P��p(�@����n�P\��S���Ps�
��pW��
�\�v��V�	�l�P��0l�@(��d��r��/�0�pI��?��6�
PK�@>�����y��e��c�PO��G�����:�Py�
�
�P���J������
�$�������P�p���w��w��v� =��i��&���	*��b���f��h�Ps�P�@%��
� G���E�PD��7��<�f�	�=�
 ��
�]�`:�`�	 ]�����F�p;�@����8� X�P����PT��� &����X�
`��P��P��v��o��"��}�
P=� Y�
P!�0$���
������>��{�p�����0��0����0��C�
P��
H���p<��B�
�`��K����@M�
�}���`���5���#�����`�����
p��u�`3�L�&��/��/��x��f��Q�
�!�0�C��*�@t�p~��V�`<�p��`���H�`s��,�px�4�
0%� Q�0t� K�P�.����0)�0G���Y��E�
�`�����H�`(�������B�p� *�
 �@f������� ���C��y��.� �����+��)�`�PM�� � 
�Б��E���0Q�
�v��0���pP�
`�
���\�`�P!���01�@�� p�0y�pH�p��p,��7��6�
`F�
P���)�0�����|����� g�?��������pM�`�� c�Њ�8��X��-�`u�0P��08�u��K�p�:�
W�L�
������� V�� �
0)�
 ��0f��+��F���`%�p$��c�`"� d��6����H��c�P3�`���� $�	�X��/� �P���^��<� ������j�ps�0/��� D��,�@O�+��7��7�6�
�]�`Y��'�P[�pv��_�`*�PF��r����<� Z����� \��]����0_��8�
�D�]� 2�@�0������s��,�
P_�`-�P���x�P��H������� ��Z�p}� w�
�>�
P%�0D��v�pp�B�
�$��N��X�@)�
��P	��R��-�p!�@:��1�@P��A���  � M��%�@p�PF�
��)���P�^����=�V��f�`&�`�� �P��p6��|�0\��E�
P��0|��^�����9�0��
�<�
�f�`A��0"�
�� ��
����E�
�K� _�pk��j�0��2�@O��9� 
��/�p��
��p���W���� D�p��"�`���e�
�o��������`O��I�
0��0Y����$�`K�������:��[�	�B�����m���	@v��C��$��H�
 =������
� �
p������
����"�pi�5��_���0��
?���S�����B� !��"���N�����+�
`�`f��H��&��������m�@U�Px��`���P�������P� ���p2����0��@g��^����������c��>�0$� ����p��`=�`q�
�'���%�`3� K�`S��%�
�^�������	���Z��� 
��V��`n�
`�p��E�
�2��#�pA����+��q���9�=��`�����c�
9���p��@<��v����
��)�
�p.�P�0�`3�
@ �
08� T�@~�	�R��|��p����C�
�	�Р��g��{��j������л�P�� L��E�?�@g�� F�0b�P�����U������ <�`f��P��� F�`9�	�P���<��}��a�
 A��S��B�0!��_�	0#����
���$�B�P��0$�&�
�
@���Y����d�P�@����@k��p�r��Z�Pv�@��n�0\�@h��q�V��^� S�0��6��G�����v�@O��d�
`����&��5���
�4�P"�
�M�P-��E��
�� ��s�����P��1���� ��4��}�
�b������[��.���� D� ��� �
@�� �08�C�@�<��_�pc��(�
������`��{�@F��,���0?��4�
F��	���S��?�`����	V� f��r�
�6�
P`�`���
`��
�<�
P�p��
 ���0/����?�`���z�
�R����	�-�p���D�0F�Z�0W�����
�
�$����P#� g�
�Z�)��	�@F�0D�`e� ��
�%� m�#����d�@��
��@v�	�Y��'���0
�����
@��PD�
��	@	��)�`5��;��m�ph�<�@j�0��k�	@}��*���X� ���@\�
P/�P�P0�
�g�
P�
�q�Ph�P��`"�`=���-�p �� ��'�	�	�0B�p��`&�
���@E��V��n�p������.��j�
P��/�
 >� ����&��a�	���
`�������
�\������`�p)�@��U��Q��)�
���(�
�D�@�����e�pC����g�@N��Y����+�p� `�p~����`o�����`��o�
p<��b�pF������.��$������p]�@#�
P��g��k�`�8����Y����p!�����Z�
p��P{�pU�
P6��`�	 �
Ѐ�p`�p%��+�`�g�`F�
@��`��@�����	p�	�p	��B�1���<�`#������;�@%�
Po�PY��p�
�^� �z��d�@A��'��!��m���P��P���E�@E��3��F��E�
pd�0���%����P`��d��E�@Q�������n�-�
�|�0���*�`8� .�P����@���P�@#�	 ��t��}�0|�5��"�Pv�
`��	�A�	�%����@����n�p
�Ph�
�7�
�X�PA��o�P~��:�PU�!P�
�*�
�t�i�@j�`��p;�P��	�4��u��E��� D��k�G�`��0X���0��'�	�q�
����:�����3��c��f��Y�
�!�Ph��>����?�����0i�	�-����P�� i�
p�p���Z��*�@.�������
n�@
��\�@O��B����0��� C��I����0x�pV�0K�
��pd����0� 8�PA�
�]���
�\�����6��E��T�pz��b�
p����U��E�@+�p��A����
@�;�
0��0������`��J� p5�
�������-��(��H��?�P*�@>��e�P��5�@�
�!��a�	 W�	����)�
�\�P ��$�`��@u����u�p&�N�
Pc�@%���0�����p �@d��U�`&���`���X�^�0k��
��F�
 -�`R��H��<����0��T�0���(�
����%��B��j�2��7�0e��5�����	 m��8���@y����0m� 8�	 F��/��	�
0/��E��a� �P������a��f�@A��M���f��'�0��H�І�0}�0r��(�	@�pS������w�P��
@��*��e��y�@`�
�b�P%�0Y� ��@���{�\�����PF�t��������j�p)�@1�	 }�@��d�
`I�p���`F�r�pw���@n��J��$�P���1��U����
�
�&�P[��p�@��`J������0.����h���
�_�@S����@$���
�d�pw�!�>�
���*��;��X�	�-��d�`X��f���`4�*��_��W��
�
0N��Y�p��
�@�pQ���p������������P���P��o��y� ��0��� N��m��V��0�
�q���`8�0h�
�J�`^����`(� }��u��
�0���*�P?��
��h��G�BB$$B7-
$-7-H�X�����5)3��܋܋7p����cP3������.|� � �Cd۽	�#
���ce��R
���CdI��c� � ��>��'��c�ce�Cd�o��Cd��>�Q$� ��c���c �o!�g6"�ce#��$��/%��/&�Cd	^d'� (�+)�(u*��c+�j,��c-�N.��c/R"0�i1^d2�í3��k4$n5�(u6��+7� 8�(u9�7�$���,!:��/;�$<�,!=��;��k%��/>��z?��<�,!.��c@�CdA� B��kC�iD�ceE��'F�,!G�iH�I���1D5�(uJ��cK�+gL��'M�CdNt�O��&��cP�ce$��/Q��'R��/S�ceT��U� V���W��cX�CdY�)<�,!Z��c[��c\� ]��c^ף_f`�t�a1Db�f*�o2�C�c�fd���em~f�)gg��/h�,!i�)gj�k�l�dm�nm~o�i>?��
&p�)g:��/q�
*�?:r��(s��t��F7q1u�
�:��/vت)�(uw�{~���C�����%��/x�f	��ey�)z�ij�?:{�|W�\��/}�`8y�f~�D��RY�D怀�y���S�@"��,!��*���o���8��j���	 51D<�,!�@"��)g�
ی�,!��f�*!���v�J��,!��X��1D1D�@"y�f��Jm���ic�y(��薀�i���z��g�@"{
ۚ�y1D���ݜm~���ic/g���i�Ad����C���8�Ad�Gd�
 ���5/g���i��f��c��{~��c���i���9X֬�f�Udc/gR
 ���6���밀�i��{~��f������{~���i���I�����Gd��;��w����c�����i���������Y���c���i������Ā��ŀ��ƀ�{��c��	��c�����;��c̀�{̀�/�:|�����'�
 �)|�@"Ԁ"��O+=X+����j��؀�{ـ�h��c��ۀ�i�ף��'ހ�ia��ic/g�
 ��c�Ad…�Jm��z� ��c��{����&�逊)��c�������iO��z�b*��'� � ۚ/g�)��c��i���'�� c/g��P���i���{�� ���'��c��c;��'��c��&� b*UdR�&�������c	��{����
Gd-g
f
�c��{9�c���&��{���{�|��f.���4Zd���{����I"� ��'�����{j�6�Bv���I"��h�f�c� =�D� �c!��{"
f#��{$r�%AmI"&�)n'�ʣ(���))�&w��*�zB$�"g+)|,�c-��'.��i/y�0��i1�	o2���3�L�J�4�냀�i5�)n6� 7�cQ��*{�8�c9j:�;�f<�=��i>I"9�-?��i�"g��{@���ANmB�&C\mD�BvE��iF�)nG��'H� I�i�d~��eJ�Bv(�íK��{=�D�L�F�M��{N��i�NmO�iP��{Q-g5
)���R�7S���T�o��
fU��iV\mW�iX���Y��rZ��iw�D�[����Շ���+)|\��I"]
f^
)_Nm`��a���b���cJd�\d�JdI"�x�)�>e���f�fI"g��iz�Cdhwe2
)i��ij�{�8�ݢ�{�k��l��m�XdA� hwen�Cdo��	p�{q�"�dr��i\ms�ft�N�u�{v��#wAmx��y��iz�UT{��|F�}��i~�N�:|�����e���\m���i��Ő��zt�e��i��\m��{��=����2��Kv�Ad�Վ�/�$|��2�C�����2��e����ij��&�������F� ���ݘ�e��o�5���i��Xd�:D�
)��Jd�
٠��:D�8D�ڢ�(��Xd����� ���k	o��j��"n�����8D��g���&��ګ����e�����g��6s���3��磰�y��{�� ���X)��۴�f��{y�fD�X���9Ѷ��&���b��go��S��{����ݺ�y��Q����f*�1Ѿ5u�	 �����:D�5u��e�-nJ�5�ā�YŁ�b�-n��y�5u�eD�X��C��Yvǁ�bȁ�>��d�
oʁX�ˁ�j����5u���ρ��g���ɀ�b��k:|�����0�O �X9�-n� ��i��$�7�؁�b���ځ�kہ���5u�"|ށm#��w����i��k���どc	 ��k�ck�����y݀�k��a�5u�m#ꁩc끩c��y��k��� 큩c�Nd��a��a<��c�������k�����c)���g��l1� ���a��f*��g�8|�ne�\d�Nd����Am���k�Nd�۽�-n�У�5u��-���Zd��c����ze�ze��-��a����aw�/�<�h��-	���5u
�k�Ԥ�ze-n
��>��%�Z$��-5uv1!�ze�
���-�>�Nd�i\d!D �a�i��-����>9�� ��{!���"��-�Hd#��a$�ze%���&�ի'��-(�w�x�)�ze*��a��<�+�ݩ�����d�,�m#���{w
�-\d��݀�k.��-/��k0I"1��a.��-2��a3!D4�ze5\d6��7�Ea8Nd�$�9!Dm�I":����;OvЁ|,L�2�:�Am<&=Nd"�>��?�$�?��@�zeA��-B�i~C��zD�Am8���E��-FI"G��-T��H��iI���Z$��/!DJ:|K��mL��j��bM��-.��-N�b]�zeO�����7�P�e�Q�g�R��-��-��ze�`S�b971TYUI"a��V��W��i�
�X���Y���Z¢��b[���\]v���ێOv]��^��€֣�؁+�b��Jda}_�ל`�'���b��<Da�{w�I"b��i�Zdေ#�)c�b��d�Le�cf��ig"uhI"i��ij	%Ё�?kĭ��$lY+mUm=���n�'o�ap��i��%q�cr��s�	۝��s�>Dj�eO�i~��t�'u��iW�cv�AmX��w�U�x��yAdz�c{�a|��i}�&���i~��ᘀ
 ��݀�'��c��}#���{b����F���_"��c���{Q�R���c��b��_"����c쁻j��c,�}#$n>|���܏�|��Vp���i��c��:}#g���m�>Db��V���m��>D����=(��Jd�~e����f�2ݛ~e���{hD���i���i�����}#�$nw���4~e���'�;���c��&���ݦ��k���'��{��z�Ad��븩����x4~e���'�-t��c�y*���c�g��c���'��Kv��%���c��g��Zmt�e���'�8��c���c�&��+���D���'��c������c�۽���'��av��ٺ��l��k4~e���ܼ���kB��):���c�)oB����'���D��E�a�_"}�&���c‚�{�Em�m~;������{ł{~k���m~ǂ��Ȃ�cɂ_"�Em����~e���c�%|��i��%D���+ς�c��D�)т�'҂�c=�tA��]�����&��e4~eԂ{~��?��j���ׂ%D؂�'�`*�F���b.�{~܂:�݂�'��gނ*�5<u߂���˶��'Ⴂc�Ad�r�����'�@"���y�%D炢c�@"�{~��D����{�g�<u~�Ad� �z�� ؂�c��e� �c�<uv���� Q�w�@"��e�˕���S�Em�%D��Xm�
�og�<u� <��Nj�%D��i����b~�5GB�b~ )�/�-|��ܠ =gEm ��Mg�{~���%D	Ud>��a
Ud�{.D�t�-�i�-��{
�Q �s�=gUd�M"��{~�{k2 ���j��'�M"Ud�?��-:�
`
H�{Ud��%�<u�M"�i� �i!m��%!�J��-"��cb�/#�&A$��'%��'�J�&�-'��{�? (��9W)F�* +�\d, -Ud.@"�l/��'0Ud]��c1DD2��'3��4�{5��c6DD7 ��8�k9 :��';��?<�b~Ȁ�1=��bwDD>��ck�D�?�&:@�-ADDA�g�����0�BDD9�6C�/D$g=�
��=gl�-E��cF��c�:u9��G��aHDD�=gI�k��-J�:uKUd<�LDDM�{N�cO�-P2QDDR�:u�UdS��T�Hm��{U6n��g��:u)�>!V��kWDD:��X��cY�j���'R��%Z��d[�Hm\��k]���^��%_���5�a,DD`�za��+b��kcDDd�e]��c쁥%��a,e�HmJ��'�b%`f�g��%2}h��ci��bjmwJ��>kR�l�km�n�;go��b����d�fp�Cdq�Ear�cs��it�E���gu��vAmw�c�����m�x�CdyDD)�gz�k�?3(�_�{�e|��yЁ��}��~DD�k������)��܂��yh�e��\m��:u�۽v��%�d>�@+�Ad(�8'��"g��ka��lj�:皀6&20h��)n���f���ލ�����y���y�m8��Z�t���<�Hm�\mO��?6��#���?��>!�\m���Z�ˢ���?�?3��.D��ޙ�Xd���"���y���i�\m�)�����[���ੁ!`3(;\m���?���3���)���v�+� ���ip�Cd��Xd���z��e��)n������?XzԤ�����,��?3��z���?9�S��Cd�� �ۅ��X� �?3��!��5����i��Cd���y�9:�Cd�\m���y�?3�Cd���?t�e���i�����-|�\m���y������-h��*� ���-�ˢ��ʁ�����-ڹ\m���-�����dh�ԂF"�&B�R"���"�ae��'�Fn����\mÃ�?�@+�f����-����Fv����b�U ���Ƀ�?ʃ�n����Fv̓�?��΃Xd��c{ۦЃ�-ȀЀ��i҃�?b���Ӄ�i��cՃXdf�փ�-׃ 2�}��k�Fvڃ�i�'6}��iۃ&܃�i�����	�(`��x߃�-��i�:DK��Fv�zkB�k�}�,DoB���k�}��b��'��c���ރw5�>�����kƁ,D�-n��μ��냩�<�샅-�?g5�����i�Ud-~�o%e�F��Hd�,D�o%Q��Ud�����h�-~�	��b���-���-���-n�^9��p����-�i*�-n������<��'�[���Cm��q�\���-n��e��i*��{��ۦ:Db���c�yFv�e(<'%���-nFv	��i
�Cm��k�o
��L��Y����c�c�Cm4��i�c�c�Cm|e�b���{\d���|e���?�Cm��ݫ�L��y�i�|e��,Db��c\d�,D��i��Md��c��� �Cm!��k"|e#\d$���%�R&\d��,D'��iD|eQW9� n(��y)��c*� nŁ�b⁚��&���+��,� nD��&X������-\d.��i/�g0� n����1�Cm2i*�,�"|3|e4� n5-n6�e7\d8�Cm9]v�I+���i*:�Cm;]v<�CmBi*ɓ���Q6�=�e>� n?�Cm@��cA��)�M�B!DC�Cm�!DDI"��)gE�Cm��eF]v,��&G�CmH�\dI��kJ� nK\dL�Cmb�M�M��kN|eO����AmP�$gQ��R�AmSI"T|eU�AmV|e�i>W��X�AmY���Z���[��\�Am]���^f_�Amv�Ԣ`\da]v���bI"cB�d�Ame\d����!D�� n��e5��ݒ�!fˢg��h�F�i�f�����E9���5���j��孂Amk�{���l�Ame��m����&����AmT Qn�����o�Amp��f�jq��de�ʵr�E9���z�W�s�?{�t�!|B����Amu]vv����B�'}w!Dx�Amy�E9z�fK�{!D̀~1|�b}�����{~�{�{�I"����e��f�K���>D�Q҂�f�!D��د�e��b�Gm(	 �I"\�Am�Gm�	 ��{��f��k����i�Gm��)��܌�>D��c�[���Ѐ��Jd(�����iSGm��Am��Am��E9(����c�Gm(��'(��y������E9�Gm��/�����E9(���	 ��k��c��kQ������L��ΚGm��{)�ce�����ݛ��k�>D���c���c��k��v���k�GmlQ9�#K%Gm���c|[������c)��ܥ��c��f���kwSpO$n��{���c��bd�f��>D�����i���Ԯ�c����k������b�,���c���cr��	��a��Q�	 ���k
���z�foҴ�	 ���-�Gm���c�8Ѹ��-�~1���k���c�Gm���-�$n���ɾ��-9�w��դ��c�GmR�c������k�w_„�-y��kO	 Ä�-�Gm[�cń��k���c��hd���'D��>�S���iɄ�-�3D����Yv�Em˄�k���N̄�k�EmQ��΄�{�4�Є�c��z҄�kӄ�{Ԅ�-Մ����zׄ�{���?؄�c
��{ل��ڄ�c!���G��-Z��_���-�F���z݄gs�ބ:ĉ��z��%D߄%D�Em��z��i�ň�%D��+g��{�M"]��y�%D��y���z��-�%D]��y�zo���%DDEm�Em��z��?y>�-����������-Em�M"�-]��y�;�z��{�%D��Վ�{~��%D��&[��-���{�����%D��?���S���-h��ĂM"a�!�Z��!`��%D��y��)������%D��yEm�JmNd{ۦEm�%D\d��y�%D���Jm�ۦEm	�%D
���>���	�z
ϣ��n$�JmtEm�\d��{[�zZd.���a��c�z��-��׀����1Zd�-�z�%D�Jm�Ԣ�����%D�-N����Jm�c~��٢��y�c��%D��������i�k�Jmv�F+ �-!�%D"�'���i#:�$�-%�c��f&�]��y'��(�c)�c����*�++��,�?-��i.Nd��{.���/\d��0F�9�R���{1��2�c3�Jm4f5Nd6��G97��2���8�����9�d:�c;��<��i�Jm�Dv=�n>��?���@��#A�JmBDD��G9f�C�kȀ��')�ۦD��EDDF��iG�Jm���DD ��'H�G9I�-�DDJ�kK�cL�sIM��bNDvO�G9��XP��i؂G9Q��'�HmRDDS��c��HmT��%�HmUDDV�{W�G9X�HmY�-Z�[�c%�(|\�Hm]DDU��b^�(|_)���yqc�	o`G9*�G9a�L9bDDc�cd��c��ke��'� �����cf�(|��igDDh�Y�i��cUDDj��'k
_lDDm�Hm%ۦ=�tn�G9o�ipi#q��r���s��$,��0�R�kt�Hm{ۦu��cv���iw�(|��}xDDz�G9��}yWmz�4{�I|�i���}DDd�)n~�!n���(�DD�k��ۀ�����i�DD�i���i��Ղ)n0���DD����i��k�DD��?�Nm�)�Nm��{8�x���h�!n�����@+A�?�Nm��_"��?�����+��Hmo���L9��i�Nm��j���Hm�Nm��HmY�}��L9��i�Nm���i�΀-g���d��?kB��"D�٪��i��_"��i�Zd�J9��?�Nm���m"D���0�Zd���a��i��Z���i�"D���a����Nm�"DR�i��"|]Nm�"|�
o���a""D��/�f�Am����)n��<"D��{��{���av�!n��{��b�����i����F"�"D��"|��#��{�Nm8����!n�����"|.�r>����w���?…�阀"|��/���
o!�"|Ņ��NmDž�-�Nm��{o����{�"D̅�-*ۿ���Nm��{��b��bW�{���х�-��?҅�-����Nm���Յ�'���'�f��Nm�"D_��-��b��i�����{o���J9݅"|ޅo�߅�-�a�iᅇ-�:D�"|��i�"|�o.�P��x����h��'D��兇-8�����i��/��ah� ��{��	��l�/�(w�%�9�2����{\Zd��,D��iՀ����c���r�:D� ��{���ib�,D��{�5Z�y�9:��i��{��,D��{��-W�{��,D��{������-,-n������ ��Zm�b~׀�-�-n��p���'_��-�:|�,D���-n�����-H8�́�'��}��[��,D��-��i�����-	�Hd4�y��,D
��l�iˁ�+�Cm��,D�i*b-n
��i{���Iv�b�������{��bL����ic�y\Zd��a���d��Iv��a�)g��<�i��a�:D7'���7�>Y:DAm�j�r�b~�;n��U��.�,D �-!�-"�b~#��a$���ȀƂi*1��{%�-&��'��a(�F�Ӄ )��b
�ƴ*�}+�-�Y�,��a-��c.��a��3`��/��0�,D1��aj�Zm/��2��3�-4�,DG�a5��a6��y7@"8��q9�ad��:��+;��aeUd<�&D=�->�&Dv�>o�H����'�j�>?�-@-nA�&D��aB&|C�-D�-��aE�&DF��G&|H�i��%ii*I��aJոK�&D��⚀�o9��L��cM����&�=��N��kO���%�&DP��kQ��+��R��/R��_S�&DT�?y��kU�&DV���	�`��a��&���c=�UUW��a	�}��&DX��Y��aZ�&D[��\�fm�}���=��*�o]�&^&|_��a��-jv>��-s�;�o`�>�a��b�ac�+�;d&|e��<�����b��/f&|g�a���h��//��i�-g��/j�
O��&D��=H�jۉ��/�&D�?��}����k��Rl��mm�i��'n��ϖ�x�o)p��q�&Dr�)j��s�':>|t�Bv��)u�&D��l�?���e�>Dv��rw�b�)X�DX�i����?x�4y�>Dz�'f��}�����b��x�f�}{�i�����ݜ�}c�o|Rm��}��k~�>D��/��Xdr&Z��<��>D�Gm�Mv�Mv��i��Xd�}��>D����o�:s���k�Um��cGMv:��&��Xd���j{�c�Um�-g����a��>D����)���j����-g��(n��c���Mvk��j�UmQ��d$��k��c�Cdw��
����]Um��4��{� �Um+��k���B���۝�c���k���z��c��'�Um������{� %
ۣ D�{�;gW�}k���sq���{��-��H�`j�,��-��I���{=������c��Xd�[�� � ��-������ް��Xd���k񂱌�Y��Um�`��Xd� ���'�}��-�k��W��Mv���_΀�ݸUm���k���'&��kX��ϻ��&�Um��c�}�ɻ��+�Mv�Ume��&��c��_����ZmĆ�'`ņ�'��k�Um�uI�`������c��k��Sɇ�oˆ	o��a͆�']�a	�`(�YvΆ��φ8��}~�V�ρ��+D��{҆���Zd��'Ԇ�Ն�kֆ�'*��%׆Cd�3D��}ن�{�3Dfi*ۆCd{��?���܆�{݆f�ã�+D� �+Dv����r�4�f�3D� 	�?�}~憅�+D�}��'������`�'־��'솏?�+D�f�+D�3Dw�:�.�C�ۃ&����+|��'���j��[�'�+|9��-J���+D���'<�����Ad� ���'�Cه+D�Ca������'���-��	� D��{������Ad��{�a�`t��<��y�f����/�y�{	 D���L
�Xm}~ D K�e����)
 D���Xm�+Do� D��a�/���\d�Xm��ܚݣ�g��y�Xm	 �I+%�Xm D�y����-��H�8<=�F��-�f D�k�:Zd<�5e�F�(�yZã�Xm��{>ue�f5P���"�k>�>|�f �{!��z�f"�/#�k$�-% DA	 �8<&��a'�+|( Dd�{@���)j6j��*�uR��{�
o+ D,]v-��a.��c/��@��-0\dH�1 D2�k3
o4��c5�{6 D�kk�2���7�Xm`XZ�Ad���ݠ�{8�Xm���c�*9�k:DD;��a<´=�y>�Xm?DD@ãA�y�'B����}����C�XmDDD��a�Z9%��}E�-���5	oFDDÄ^m
´���
GDD�{�c]v�dwH	 I��(J��c��&o�	K�-L� M]vLDDN��a���O�-�}>��Ȁ�P�'#Q�-R�kS�k��?�T�-gUDDV�-�&WDDXU�Y�{Zk�[��-\�{�DD]�k�k��>d��^��c�Ѵ_�>�f`Eda��-$b)�D�QX�cI"d��m.��-e���f��-g��#h���������?���-iI"���Yj��㄀�ר�.:k/|��o4Ed��}↕|9��lfmI"Q���/|r�!-nEd!��o��-��o��>���{Ё�����p´�>���q��i��o���-���r�/DsDDtEdu�`vDDw�/Dx�һy��zDD{;g|��i�ٷ��)���}�Am$�S~$|��i�z��뀇M+�\m���i��/D��/D�`o��o�+�����/D9��������/D��m*�\m�<<�\mQ�{N6Cd��/D;f���i�´�71�Wmv��A��>���
��ޒ\m���ii$|�\m��/D/|��>i�/DV��Wm)�o�$|��>�5����i�\m����i�U�%����x���o�X9�8�I"�Ed��C�X9��F"��o{�`�AmkB�`������/D���)�o��b��'����H�F"���i���i,�/D��'��m*���i���J��6��M+���ސ�'��%9��iv�.��/D�����i�\mQ�ͱ`��i�\m���i9��Sy��{���i�'���2�`��b&Fk����%���{���Y+��}�@+����B6	����\m�`*l��-�`���X9��b�o����%‡ ���ć���$Ň�ib� �:D�b{�k.���{LJF"	�b*��)ȇ
�g:D��k;���z��c��b��{��{��'��J��;���}҇R����=�f���b��i��{m�ZmՇZm��c*]�Ѵ�'/��)؇:|���ه��ڇZmۇ^9���i܇^9݇Zm��'�:|.��c��:|� �;��:|��+�^9��cw��Ξ5S�:D��b��a�;�=����^9��b�:|�ZmZ��%�51�Zm��{��ܛ�{���b�^9��:|��i��i�:|�Zm:�i����HdR R�}Ä�)�^9m�}��)��i��c�^d�Em���i��Zm" ��^9��Zm���o���}��^9��{��c��{���c��d�}Հh��cÄ�)t�}�c������}��M"��{�c��>�i�i�b�{����-	�bX�Q
���	�����N��c
`�-�:|���>��b~�^9e������?�Դ�-����f2���Zm�^9�-��-g����O��c�-�^9���c����^9��������-�- �^9�� �-b��b��a!��i�Q�w�"�i�Z�#"D$�i%��&���'�-(�i)��*�"|+�-"D,���-�?v���.�-o�i/�Iv0��>1�2��i��L9��o�22�"|
'3��'�e$Y���|��4�"|"��k*�<5)D��-]�?6��j7�"|8��'���9���5���5�-q�"�9�-:)D�"|;�M".�-<�"|=)D*�/>�-?)D����@��%A)Dk�"|8�}��}B�-C�)|s"DD��'E�-F)DGFJH�)|I�-JAmK���`#L)D�;�M�)|N��^mO�)|�;mP�vc���Q�)|)���&)R^mS��������T^mU�c9�"|V��%���%W�"|X�c��2Y^mZ�{[�)|����\�c]�2^��%6�_��d% �� g`���a)D@�b�)|yfc�"|d)De�-f�>D�^m��
跆
��>|g^mh�>D����K"i)D.>|����j��k)Dl�>D��}��c�5Zm�>Dn`b�iL�>D�^mo�)|p�-(�)|����)D6�O���q�Hm���`r�p�s�>D�m뀼�L�>Dt`u�P�v��kw�c"��%����x�py�}�>Dz`{��%|� }�Id~��6�>D�)|��>D_��9���i��ce��{o�݃Mv��}��vc��^��ce�^m���{�>|���y��}���c���Q��cv��1f���{�Mv�z��;g�^m��ce:��{��}�^m���bʁ;g���{�k���c>��ٓ�[v��Y���c��>D�� �>Dm�}=�eי�`��}���c��>D聂k��}Ex*��*`���i�dמ��}���{�)���{���=����>D9�z��}�`��-��}!�W�t�>D���-A��j�`��@v��&D���b��}o���-���}��@v��{��&D�� ��+�4߁�{���j��+��C$�֣>�ce5�N�Mv���-��&D���kt�����}�۴��-���c�۶�&Dk�ݹ�ce% �-|��ced�}��&DQ9D�)耨�9D���{F�&D��ce��&D�-|�9D�}~�9D���f��ce�i*�9DĈ�?�`ƈ�bLj�b�-|Ɉ�{h��r�F9D�Ad��aeˈ-D̈Ձ͈Ad�<!ΈAd�&|9D��?�ae�9DȀ�T���y�-DшAd�-|��b�%)�9DԈ�&Bֈ-D�9D�-|��9DڈAdۈ-D��a݈-Dd�}��?=S���-߈&D�-D^$�9D�Ad�-D��'�Ad�-ل����'�Ad�&Di9D�ie�ae��i�9|h��-Q�&D��'���-D�Add���_��3��'�}~��y-|�-D�9D���Ad�գ�9D�z�Ad��'�9D�}~	����Adi9Dd�~�u��i�9D������I�9D���i���tY���f��i3����'�9D�m~b�:n�-����ɀ�/��:nL��/��i�Ad�� �}�-D�ae��0�Ad�{�Ad�B"��i	�b_�	�B��ae�{�AdÇ5�		�c
	�	�c�-D��=����y
�:n8��x	�c	�i��!��?e��z+m~��i��v���	�i	c���+	�c��{��c��o	�i	�{Ԁ����z�
���c�{�	�c	
�Z�=���	
	DD 	�i!��cl�x"	DD����dw#�7�$	DDdw��$%��z&	DD���c'�	o���b(	DDZ�=���)��c*	DD��$��a��z+	DD����1�z�:n,	DD-	`{q.��i/��cv�����~s��0	DD1	dw��c��P�2	DD3��c&DvƁ��4	DD5�֣6	dw�ze��$�`7	�k��c8	DD9	�c:��'�b;	DD`��<	`��b�DD���=��b>	Ք?���@	eeA��bB��cC	DDD��c뀀b
`_�8�E��z��T�F��cG	DDH��ctEd5 I	EdJ��'K��z,�?;��zL��cԀ�#M	dw��yN��zO	dwP��{Q�UTR��cS�Am��zT	DDU��܎��cP����EdV	7�W	�>t��bX	DDY	Edl b=|�DDZ	�[	dw�ee\	DD���]	������^�Am_	DD�r��&`	DDZ�Ama��c)���b	DD)���c��'�Edd�m*e�Am�UKf���g	 Dh	\m�;�i	2Dj	2Dk	Ca���9��l���m	 D��n��c{ Do��p	2Dq	>nr�
s�����$U D��*t��cu��bՀ�C�>hv��b9��:'���?w	EdhY�ㄲ�x	EdhY�+��y	 Dz	�/52D{	Ed|	 D�Y����}���~	 D��a뀼�ۘ\m��܀	�>���	 Db\m���ahY��	 D�գ����j�Zô�	 D;g�Y��	ه�e��������u?"��}��=D���Ȁ�^�	<F�z��J$�	�{��E"�F"�	\m�	�z�	�c�����EJ:�Amw�uC����	 DQ��	 D���#Ԁ�S���"L�b�	\m�����"�	\m���a�	 D���-�	 D�	 =���	 D�	�d:�b�	\m��5�ۭF��/�	 D��孤	2D�	f��	 D�	���cЁ�h�	��	�	�����h�	 D�	�{�J$�	d~�	�c����	Fa���?����Xz]��)g�	�c���S���˶�)g�Y�e�۷	�c��z�Y���u��:�oR�c��	Y��2>�	�z�	Y���g1���aJ�"���a��hek�f���a��Hd�	���	�4ÉHdĉheʼn�$�-u�	�c�	5@9�W1�	ôȀ�ɉHd�	�,�	�z�nd�{��M��^�	 �	�{�	�"́��	�c(��o�f�	EmWd��c�	���t_
�o��+���׀heY�u�	 P��Ӊ�{�	 Չ�-�����	Em׉t�=�En���?�g1.�he�	�i�	 ډl1�	Em���>w�he�	�i�3'�Wd�	d~ �	Em�	�J�������Wd	Em5�X���	Y��c�Hd�Av���Hd�	 剉ko�f�iw���}�	�c����	�ia���	�����	�E��>�he쉭>�/D
`�Hd�	neD�Iv
��k;�Hd�k	��i�Avه�-��Hd5 �Y��	'���	\d�/D���i>�	new�hec��o�u��l1�`��Z�s�Hd�AvD��i��Hd9\d�	 ne���i@̣�aĂ/D�	 �	�-��$D����$Hm�������k�/�	�i	$|�	\dt�h�	�a����
�i
�'���
\d�$D
-g�	۴�iwX<F
$|
�i
���u�Av��	��F+݀$D
5b�#�	
�('s�/D��'

2
�'y�u�Bd
��+����B�ọ���_�:�
C"��k*�Bd��k
\d��u����'

`���
�>
�ف�Bdm0zj�
�J������Q
\d9�6�
�'�-g4ne�`#j��Ё�.
�'
ne
\dp�N�
��`# 
�>X�6S!
N$9S�"
\d�s2#
$|�C"5�N�3�N�tۘ0zރ��$
�{%���&�X-�����ݡIb'�����x��(�ʜ��7���>)�
�W�h*�G9��}+�bw,�N�-
f.���/
f0��d��h܈bw �'��1
�{9S�2�Zd3�1h4�bw�$g9�s�5
f��ә(�c���g��-6
f3�N�ه��7
K"�C"�k�8��i9
�'	�>3�N�:�;
-g<��i=
�*>��i?�Zd�f@
�{9��i��X�A
��B
��C��+D�Hm�C"�YE�Zdv���j�h�d�Ev�0z*���MvF
�iX�E���^~���G�s�	��nH
0z���I
����iJ�Zd5	̣K��i5�i~L�����$M��i�Um��IlN
fO
Mv5� P��>Q��iR��cЁ��t���5�N�S��cT�i~��_"U��iW��܅���D�V�Q$W
�iX��i�fWfY
MvZ�[v3�i~/��i]��[��cs#B\�Q$���]��c^
�b_
 ��{�&`��ca��b��c�c
Fd����d��h�����Q$�Xd��w$p�i~y��c�ue�Q$��Ӧ�Fdf
 g�g6h�4Zp�1hi
Fd<��h��bj
 Ȁ߀=�F�k��cl
Fd?h�����{m�M�m�"|n��'o��i�Fd*�{Jp
�b.XUq
Fd�U,r
 5Ums
Fd)?hg��i<"D����d��^t��,�/��u�� �bv
Fde��w�"|��x
Fdy��>z�2{
)Do�Q$⁧NK�|
)D}
 ~�"|y��c
�X�
)D���c����u�Q$���i�
)D�
0z�)D_��>���'���c�
)Dd�뀼���"|�
 ��f���*
�/?�����?�
)D�K��
)Dy�)|�
 ���b��f�
Fd�
�d�
 ���'�
Fd�
��
C�ه)|��ۗ
�&�-��A�	옊f�ۙ
Fd���fi��'�
)D�
Fd��)|��f�
�b�
)D����)�����'��)|��G"��f���'�̣�
 D��{��)|�ܧ�����'��
�i��)|�
 D���ɭ
�4���Ԁ�j�
)D�^d��)|��
�i�)D �)|��G"��c��Hʳ
�i��"|��C��
)D��{�?����iK�������b�
)D�۹�Hv��f�
 De)D�
ۼ�f���c��ܾ
5Z�
�i�
)Dه)|h���f�
�i�
 D���'ĊZm�
�Ŋf�)D�
�i�f�
�cȊi6�
�cm��iʊY�huey��y�)|t���
�{̊f�
�c?���
M��iy�)|�=ϊ��
�cъ)|mۚ���)|s���
q1��h��
 D�
%n��`Պ�4֊�{�
�c؊�c�Gmي�c���ڊ�@hue�
����
 D݊2ފ�cX��ć�{��H�;�i��b(��
 D(�����
 D9�
�i�
�c. D����
�c��H�"�Hv���b�
�i���?5������{���b~"��b�j?�~��
�{���{�Ģ����Hv ��c��ꊹc܄ |k�2��{�� |슱�� ��h트+�c�.�c9�ל��� ���&D��-��	�
�����i�
�{�
�c��q�[��
˴�ue*6hf���&D�
�c/
Ib~�u����
�c���fۯ��"�
�c��x��u�Ђ�݆f��&D���
-|�
9D�i#��+!�&D��m���-|�����4�
'�2-|	����b����
f��c	�&D-|c=
-|
�&Dw�y}-|��b��#�R�
��{�#nW-|�-�?D-|�-D�&�Kdj����/�>�-D�Kd-|9D�-�-D-|�?D�-D�?D���#n�Kdw���-D �?D!-|��?D"�'#�-D$�?D%�'&�
O'�-D(�-D)�?D*�#n����+�-D��&D,$|-�?D.�i��Kd�9D���/�hd�'0���1�Kd2�Kd��3��-@��x4�-D{-|5�Kd��6�-D7�&D8-|9��x�&D9�-D:�Kd�f;̣/$|<�i��J=�$DX�w�>�Kd?9DJ-|
f�Kd}	�+@�iA�����B-|��k�T;��@dC�$DD��/E-|F�-DG�-D�-|_�!6h�-|H�$DI۸J��k��-DK�#nL�bҀ�M�8hN��k��h�iO�-DP�?D	�Q�Kd:�$DR�-DS�E_T��bU�-DV�cW�Kd���X�-DY�?DZ��b[���\�-Dw�wr�'��?D]�$DI����i^-g_����$|��'`}~��ha�{�\db۾$|�yc��zd��e�cf\mgIbƁ��h��ziuCĉ�j$|k�{h�:<6Kvl$|m���w�/n��:<>�x%��'o��ap�cquJr�$D�:<s�*~t�c��iụ>�$Dv�������'�ϣw
�l�@dw� �:<U��h)���h�:<x�@dy�tIЁ8hh�:<z�X${d~|�$D(��}H~�$D��h��Y��c�{k�c���^�Ib���c�}~k6h��cdځ�6h�5@���Y�̣�u�6h���+� �uI���݉��݊�c���z�����Zm;'� �����i�&l+Dk��6:|����)��{�Od�wH���z� jۏOd��U,���z[�&ڊ�s���� �Wd��"Ԁ�_�Od�%ᔋ�z�Y����z� w�~8��c�Od�:<��Cv� ��g��c2��7�g2�:<��E�)Od�+Dg+Di�=D��Cv���a:��>� ���J��a��c��&��V9���i��a�Wd�{f���a���|e��&`<θ�f��=DT����/o�ݨ�F�af��2D��- ��/݀=D���5Ģ� ����+��%9���x1�2D�xI�À�a׉,�2D���a�Z�o\$���a97:hwe��/��*v��6h� �g��/�Wd�Od����e��+�+D
��j���R�Od����bk	U;�&Ɓ�м����|e�QҫĢ�����+�8hOdS��>�2D�Od,�/�+D�Ģ���@�b�)D�=ho�o�=z�)D��zډCv����Y"(?�ọ��Cv�)DNj�_ȋ�$ɋ*ʋ���?�ˋ�b�\d�)D� �	 ϋ;g�)D��zы�iS��b|��?��)D)���/Ӌ	��)D���Ջ�a�?���z׋�i��@?�,�/�)D)D��<(?�j�Q2D��Ӛ��h	dwQ�2�
\$�2D�VQ�2D��e��U,�)DY�e���#�)|�Y"�x15�;g��z��fw֙��!��c��b(�	�ւ2��b���Dዣ�⋣�㋀b䋆b��j�b�	 kCm�f)D���`�	 �)|� ��")D��z��j9�7��71=�6s׀ze�����cꋛ2��dw����)Dw���(?��ze�Zd�ze�~1�	 �)D�Zd��b������Zd�)D��z냨�B���+)���x1���-��~1��ze��5GQ�S��ze��)|��j��Zd+��%���c��<��i.�Zd�̌�)|(���i6��{��ze���{o�o��ze	��
�i� X����ze���F�i�Zd	0z0z� �{w��&����?h�D��i	 �~1���y�c�U,
��{�i��h��iR�iX�{
�b��������i�U,��i���i�b튶�	��ze؅�i��io���ze��&��&�Zd�{w��{�b�6|��c��{��c�_������4z�ze��i*��& �b!��i���/"�i8��J	#�Zd$�J�ބ�>%��id�Zd��a=�TB&�b'��/�~1(��+)��>*��
��/z����+���,�b-��+.� /{�0�Bd(�'���	1�$�?hO��kD��2��+3��44�'Ԁ�J/��i%�x��'��5�i���&���6�i~�Av��i7�'v��v�+8� 9-|9�{:-|���&;����<0z/�Bd=��i��j<0z=��i>��W?�Bd@���A�BdB��cs��>C
�D��cE-|,�?	��t��i�Bd-|F�״G��iH���I�BdJ-|K�'L�IdM�$N�Id�U,A
GWc�c$��.���yO�35�Όe�b�IdP-|Q�Z��+R�D��bS�&�?T��y^��+��
 U�b݃�#L����� V�?W�a�~e�0zo�_��xX��Y�-DZ^d[�{A�Bd\�4�]�{^�Bd_��>�
�7)X�{`~e*��a�{b�?�
�À�d��{c~e��ꢊX9d�Bde�?��Zmf�_g��h�?9��-|i���-|j��yk��cl��m��	��a��{n-|o�{��i닡ip^dq�{o
,r��isUJ-|t�{uU2v-|
�8hw��ix���y�{z�-{-|D��a��-ʇ�'|�-�6h}�o~�cɀ�iIb��-��{��c�6h��{T�״�Xd��-D���놌�i���+��c��-D��i��Id���y���c��Id�x*�-D��E9��?����a�^d�:�~ew�i���{���c��c��-|懽+��i��-��?��d��{�W�k��b���{�ɪ�C�$g��i���j���{%)|��{��i��&�j*�״@v3��{���coh<�ᣦ��iH�Am��k^�k��i���{���c%��+9ت�{~���{%�i��k��-�Cټ�{~��'ϋ���-u���'��kY_v���$o۲�-��i�"D��`~��h�����c��k���h	9ط�"|��}�-|���i��`~��-��`~��i{�c��-(��ꉂ�%��i��c��`~��y���L(�����/Ì"|��c�km��a��i��c��i�fl9���kɌ`~ʌ״���Md͌"|��/�+z�����'���cn�����ܣ��j�?���-ւ�uЌ��ь�{��NҌ"|;��ӌ����Ռ�c;�������֌Am׌"|��/ٌ��a�`H�y��/��-����e�j$9�m��>��'܌�b�����k+)|ތ�'Ҁ�����
�;|���Yd�X"b�I����k�Md�'�Md9{}�>^m匦N�	Md挦��Yd� D؁�'�2D�
�aތ�'�2D������� D��i�C�Ca� D��+�2D�`~��z����Yd��a������2D� D� D�+z�2DM	�a��-�^m�2D��;|���k�-��i�2D� D��.m��kJ��% D�ؤ��Zv
f
�i
 D9�;I
2D�a�5Z
i#��'
 D��'��k	
�a

 D
2D
�a���iB DW�i
��i����S���j[�p����wt��D)�����i��k{���'
�D
X����
X�G�%��$�:|�;|S�Yd
 D��bw�Yd���3>��
�{���4�;|�[��kх�-��
Em	�Hv
�%b��-
 D����{5��i<n�2Dv��Y��{���-�k 
 D!
f>"
�a#��-t��ke��$��{���"^m��z%��-	d~>2D&�����'��{
�Y(
d~��Հ�w)�Zv!��*
�c+��-��a��8h,��{j-��iT�.
�c/
�%����0��i1�4Z����g2��{3��-4
=h׀��Ɓ2��8h���b�5��a6��?7��{��c�X�8
�D9�&Dz��i�����{(�1'+�����ψ&D��\m:� ;��{<��-c��{=
�jȉ�+>
 ?��-@
�6F��A
 B
�D}�&{�C�&DD��{E
d!F
d�G��-?���H
 ���?�zKQ�SI��-���jJ�&�K�&D����L
 M��-N
 O
]d��+P��{Q
]do��+R��?S
 T��-ւ&DU���V
9D����W�&D5nX��{� Y
�c>�=zZ
]d[��-ރ�b�\�?D=�N�]
�P�c^�?D_
�d`��-#��a��ib�?Dc��-��V^d��ie��-�f�
��f
]dg
��h�?Di
Z�j��ik
]dl
9Dm��?n
]do�?Dp
�4i�'q��ir
�+��-s
]dt��i��ףu�?Dv
`n��-w��i+����$|x��?y��iz��{{��-|
�i}
=h~
�-3
�h
�i�� ��k���i���i���{�
�'�i�
�i��?D��݀�{QW1R��-��&D$$|��T�����&�
]d�
 �
�-�
]d�
�i�
9D��]��
��9���i�
u3��k�
R�
�e�
�?�
�-�iju��
T;��?D�
�i��-��?D�
9D���i���-=t��
�?���-���f���ޟ��i���+���i�
�i�� ���-���b��q~�
�{�
�iv�֧��b�
�?���i���-��?DoI���|��?DZ��+��Nҭ�?D_�Eٮ�Ea���i�
 ��ܱ
�{���x�����$���	ރ���D�0z�
7f!�
�-�
�{��D�
�i8��<���D�
�-���1�
���
�i����%����i�
��
�-���D�
�a��D�
f�
�i�
�{����
f�����=hb�{J��A��N�()5'���)Ǎ�mj���{Wf��>�Hm���]�ȍ��ɍ�ml�����Db`8b��|ʍf��
v~��j̍�݇Y�T�ף2���r���͍��Q��Ƕ�iYf��?�#'�
f�
�{��+Ѝ�@�����T��
�{$�<��Sҍ�A�-g�
g��
�{�y	X$�
ft�=�*�
�{�
�{=�5���a���D��Ӛb�{���N��꣚����
�k�
Zd_�8��D�
�k�����
�io���
�p����
�k�
�i��S���D��3`�}b��eA��Dߍ�Ä�z��&�
�?�
f�
�kk��b�
�i�
f�CvO�i~�
�i	�a~�
fƪ荩c�����`�?��a�
��덩c�a퍩c�
Od��x�
�k�8�
f�c�c����
�il�����=D�ףm\9��Am ��&�
�k	 ����b�a~��<��
�i��$��D3�=D�
Dd��Am���8���cJ���k)|c�h�Am���c\�����DJ����\d�
`��١��s�����?�����,j�&⁦�::�����B9��]����>f�?'�����kk�D��a�k"�D��i�2�iĂ�'by��=z��i	�kh��
�2G�D��2�K	
� �k�iR�i��a~9��e�)D��8�i~;D���	 O� �������cҀ���_
�e��f"��k�)��a~�)#���>;���)DN��c��8��) )Ds�=Dk�2Ԇ���>(�մ!��,"�;|#��,	 $��8(�8'%�2&�D��j8�
Z�')D(�)|{��))D�6h�������o��6��܄�,*��k+)D,��k����-�D.)Dc���d�c��;|Ӄ;|.F��2/��-0 D^�1�;|Ё�b����2 D3�+{�4��kه�%@�5��y6 DЂ�%7�;|8��- ��$ဇ����k5�j9 D:)DG��;��,<)Dv���y D�`= D>)D*���l�)|b	 D?)D�dw@<n��kA��-B)D`^�C��i��HD DE DF��iG)DH�z�)D+;D9�Ik�Ҁ-|J��i���X�{RK DL)DM�-�6h+�-N DO��iR���P��kQ�cR��-S��?T��iU�cV��,���ףW�;|X��iY��-Z�cT���[��%\��,]��,^�-_��-W��k D;��`��-a��kbEm� Dc���B�D�� Dd�i��ݒ<n��ce Df�cj۷���g�-〻cy Dh Dw�f�i�j�c?���kc�l<nm�an Dom~��/����p��q Dr�-���%s D#�8t��-u�{~vۥ���m���5�i����@�fw��-x9�y�iz�k�
;|{��-�ۧ�|��cۊ��m�}��if�2~�b��i���-��ᣧ
�mނ���o��j�
m~��c5ȴ��j��Z��bj�L!��j��x�m~F��ۢ���z_�j�mފ�c(mދ�i9���j��k+�|���-��j�˕��.�8���8mސ� *Ȱ$�{~��k�)z�|~��q,�)��j��i,۽�8�-|�2`���mm�k�q,�8\��$݀/D��'s�V�-|�۫�m��g���c�-|���{����'��>�Bd�������c��{-|�
 �.��������m�Ԉ�S-|��?D���⣎Id��
 �-|��'!�-D�����>�C���?D�-|]�Id��|���-D���(m�)�Id��j��?D(�+��k��?DX�-D����Id��-D�ۢ��y��?Dց-D��?D��:�h� �ۢN�Id)�#nd���?Dń�t>����-Dg�?D<�h��Id��at>���?�Ӛ��-D�$|Ɓ�N,�Id��?D�����-D���%��?D���?Ž�b�2`Ď@d��a��+Ŏ�[Ǝ@d��a����2`I
��?D�$|��3b��'�0�ˎ�i���i腶�̎�i��amwΎ?Dώ-DЎ�iю@dҎ-D��@dӎ�i�$|%�$DՎ�'W��iH�?D֎-D׎?Dʇ@���aL��{c�-D��a���|ڎ?D֎-D`ێ$D�-D��
 ܎�a��aݎ-DW�Idގ�cߎ�i��?D�-D���ᎂ��
&��&`�i8��i�@d��z掶���a��z��{�$|��i��i�ף����6�-��{��
����i	:��{���$|����`���|�$D���$|y;|��$|��$D��a��$DQ�ap������{�����`��@d��Hm��$D|��h��z��|⁦�ۊf�/|T�|���Hm����������$D:�����@d��i�
i8)�@d��8��z]���D��
)��d��zu,X��h5����{��z	��8�i8v��a
�׌"D.Fd;�$�{
��{�����Y��`�!��k��ko��+���{�{��5�+�!���|��_�"|��k	��a��bI�� %�bMd��j.
�y5b$�|�G��*tPb�4ښ���Md��{Md�������i��&��{�iϋ�hÎ�p���^Md��/�"|\�|��CvMd��{ �|��Zdv��'!�i"���#��'$���%���&"D'Z�(�"|L
���y)"D���*��'+Md\�z,��i-��k.��/:� '�0�Cv1Md2��il��k�W3�Cv4Mdv��'5�b6�-7Md���bJ��j8�Cv92Dd�7j*:��j	2D;�-<�Cv�a=2D>�i����?2D@�YdA�-B�iC�iD�-q��QE�CvF�iG�-H2Do��+I�iv��%��-G��'��'J2DK�-†�>L2D�	�4��-M��cN�iO�����-P��b@m;�� Q�eR��S�CvT2DU4�VMdW�-XMdȀՁY�CvZ�-c2D5��[�i\�ic&H�$z�|�]��c^�?=����_)D`P��p���a�ib��c��rbc�{d�{T��kk��'���%e)D�-f�ig��jh�{i�ij�Cv�ak)Dl�m8m��j�an)Do�-c2Dp��iq�{r)DT�s��jt�f>2D\�zu����2DG�y>��a���;�-�av�?w�ix)Dy�iz�-ه�D{�y|�-j	2D��y>}�)|~)D2D�:a��i��m8��a��)|d�:��Zv�ܸG�y>��i���9�S��m8��8�Fa��?��m8��)|��m8�����{��)|�y>��y�a��f{
�z��R7�7�)D��������f��)|�&|p�A��Y��)|���m8��f��&D��{�@m�˕�)DŁ&D��{�1'�)D��{��?�{1Ŵ���y�)D�`��Y�ۯ��{��{�)D��)|_�m8��{�&D�{q��ǩ)D���}�� ��y��.��k�m8��)|9�S��kc�)|�ao�iN�f��)|�9D����y>��f��a��E"��)|o�+�������y��)|+�� ���^tŴ��co��+��>j�	۵�&D������j(�.��kc�V���y��AA��������j���b��;��F��.9D+��>w���k�
ۯ�gԵ&������֣��z��'�&�棐�'�kk
�Ï�/�&|i��/9{������/��'u��r�+��'���zj�z��D⁗�����ʏ&Dˏ��̏����'�ɣ�|��
���
�A��|o�Ł�/hx8Ђ	���'��D�-Џ 
��я�-;|+�b��'S��y����ӏ,ԏ�-Տ��ɣ��j���i�9D€�8��a׏ ��j��%�����kڏ|�����/�����jp�ژ���� ���/b)܏�-.�jݏ�.�bi��/ޏ �-|�Zm2ĭ;�|�����Ꮬ-��AŁ�/� ㏛k��
a{����-98���:<�ɣ�Zm叛k�?⁛k��(u��'-|=�N��6|샘y�ɣ�`��5�d�'����-|� �-D������&v�Dc�&��'�y��`�-D�-�-|����Y��8�c�6|�D�.�-D���S����� ��-D?����F����8��-D��-��h�.n��-D���-��>̀|����k�.nЂ�8���'���-��ܾwU���l�-D��8��-�Zm���m�f8T�4��--|�-D�`8Ђ�8�-D�X�	f_��+�-D��(	`89W9
-|�)�y7.n�
ۊ.n�L考�8
-|��-�T�A`8��8S��ik�-b`8-|e����
��	X$�-D�o-|[�����8[�BR��Y<h�U��|���(�Id�-DY��8ň�ܫ���-D\��z�����8�-D�����Y�*�i��|��R��>���v�(��-Y���+D�-DŅ�8	�Ɉy`8�-D�����aЁ�������Z���Y��8�r���8����Ɓڷ��c�_��z �>9���!�3��":|#��c$�>���&%��i&��a�|�'���܁(��X��c�F�)_�*�&`h�|�+��z�+|8
�>�|�,��a-��.��f8^�ت.N�Am���l@���m��z��/Ð��֣0��{1�iɀ�a��/2��c3����������4��aa��z5��ac�2g��z&�y6��a��c7�.��2<�h8��'9�i��a��摻��:��&���	w�D�;��c�2��a�t�p5ɀ"��'���'�����g����<�c�<�y�̣��i=�>>��?��c@��cB'!A�it�>��zB�iC�i(�`��k:���D�yE�i��L�9z�y�f5�rbЂ�z�	v�2��F��'=���G��at�>��H��cI��a����b��z��.J�f=�>K�%�L��zM��a���=���@��N��cG��"=�7TO�P��aQ��R7�SɣT�2��`>U��YVZ�W��'=({��8
�yX�;|����Y�kc��<Z�y�e�{��W�j[�k\�	v�]�y*�k��^�b���_�y`�ya2Db�yf�fc��'	��&����d����'��9y��&(��bJɣe��f D���
2
��'g Do�+L�g�o�Y���t�>���}펽ih D�i��>i D�>��)�2Dj Dk��-l�k(�y�Ιm��-�	+j�k��-z��at�>7��$n Do��ip�>qt\r�>su:t��xu
2y D���̣5�ܻ��awv�y�
2w Dx��i��y��Ř���y��iz��z��-R��-{��i��{���-����� ��]�>?	��a�a|��}��-��a~�z���;��ۀ��i� D��z���z��a� D� D��z��a��2D� D�Nv��z��a z��Y��� D�2Dy�/D�
2m z���h9�r� D���� D��/D��.� D���h`���|�f����-� D��%���i�@�{@m;���a��ȣ��c���Ǚ��?|��? z��L��-���i���(u���"���i���-��k���-���i��tB�W���a����c6��0ѣ�c��)���i(��o�z��c���z��c���֩�k���x����z���iS
�.g�a���ۡ��.�co��i[�a��� �����ar��+��.�Em��'��۲�"��c{�z��X�1Ѵ C��<��'��c��aN�5��'� ox��'j�h��Bd��%cۯ���|���>��'t��-s:|{���c1�����c$��	 ���VR5n��'d��	S�
 Q�͚�����Yh�@+���c��'��:���c
�Ð[dރ��Đ?D��"Ọ̋_
�Ɛ?D��'W��kW��}Ȑ�4��kk�z��Aː�i��k�ɀ�����-��cϐ���i���=�?D��zd��ѐ?D́yȅ��|����Ґ?DӐ�iԐ�jJ�ȣ��iՐ?DqC2o�_j��Y֐�f�{ko��%א�ܿ���3�>��'��'ِ�iڐ?D�П�$|=�?D��'f�X9ݐ[d��i9�5D�-d�'��i���|d����̣��'N��I�c�Adᐢc��i��b�����$|䐢c��a���񎀢c���|��a搢c�ݻ�?D��i�?D�$|d��Gc��$|��a�?Dn:�?D����$DT�;��'c����i��Y�+�?D5�Zm�z~�[d�..� �{�i8}��k�c�[d��V��?D�[d���j��N���i��E�j��%�:���h���i3���it&~��}��i(��?����b���a�W����$|���c�C���ci8��`s;�e��$|��$$|�
��o�h�)�i�;��{�!nl�{����zY"Dl��c������>c�Zm��	F�	uJ
��cɣ��>����a�{����
�Zm.#|�Y��ޗ�z�����
a�z~D��c����oN��>ʈ�QS�@��(5���zt���t���"|��a���	�n"D��pc�rb�i8�{����ј�{5��*�AdX$�۷��C��A�"| �"Dw����>Ȁ���"|��@âUv�zx�z�̣9�S�:<��"|�i�Q�k�j����"|d́ 
� ��0��/ �i!�+"2��-��9
(�-#� $�"|6;�%���_���i����0&��'��Cv����'��%(��')��ct���*��'9��*+�"|,��c��'Ȁ�
��-�Cv.�k/��a0���1��c2f�3��݀Cvj&h4�U���%
���zx5�z���a��
aA 6��a7�k8��9��':��aj���;Vd<~~=��i�~~��>��a?��a@��%A�bB�cC�aD�b�	��g�CvE�aF��a�"|ɣ��bG��a��aH��ݾ��[I�a	Dd
��cJVd	�K�aL�&�W�6;�����s���M�'N��a�~~Oâ9���D��iP�bQ�c�&hR�be��`.V75ZY���ZvS�CvT�a߁�'��2U�kVգ��W�iX�jY��aZ�Zv[�c��rb\��i]�a���x�b^)D_�p�`�Yd���aa�b	��k\� b��a�)Dc�bQ��%d��ae��a6�b��9��%s��c��`f��jg�&h��]�Vd����i)Dj��ak�uC��&{��`l)Dm��`9��n���o�)|p��Qq�&����rZ�	�dv��s�&t)D:�t	 \�bu~~vVd��rb�^m:�b;��Am9��w)D9�Uz�ax�)|y)D��m8z�b����{ D|��f��b}�m8b�a��@��m8@�|�~�&����)C�Y� ��)|G��j��;|�x����4Z�
ۃ Db�$zی8vx$� D�<n4�)|������)|
;��)D� D���"��&DȀ�	� D� D+)Di�&Dȃ۪�)D��&Dښ�rb9�{���	�[9�{q�)D�f��s�)��2�)D� D��&DJ��i�x�jXO� Dvo<Σ���&ё�&D��c\���o�9D��cs��mƁ)|����Σz�&D��)|�)��c��iC��&D����;|��)|G�&DX9D��&D� D��)|��c��c��&D(�����`��&D��u\�����{ D��cރ��J�(c D�����}���Q� D��4�_��c��co��a��>���{� D��鲑�z�gԴ]d���z��'�
ۧ���c��:Z� D���z��cg���m)���bp�Ӛ��a��&DY�`�BЅ��|d]d��&�B�����)^����jxÀ|���Y���	pj��iه��;��@m��#��i��c+�&Dv�i��c‘&D���C������i����#��)��cj��z���D=�A,
�C�jx5�-o�%��&h��%Ƒ̣��S�g{�;���cs���c��|���Ӛb�i��co&h�k��9D��i�g|y�c��-|@Σ�
�`�|(���-|��i�51Б	�q�	$jۯ�f[�z�-|`9�������{b�� ��zґE9}	����ԑ�zՑi��-|בi!��{(��諉�=}��ّ� �-|h�-D�AmƁ��ۑ?D�;�s�-Dܑ�b�����'��zߑ�b�?D�-|�Ids�-D�	���c�Id�{�䑫c@�-D�Id��b��z�-|�-D�?DI
��v0ꑃb&h�-D�?D(��D�w������t$|�����%� z�)�?DI��	�-D;��?D�c�-À�b5�c�f8:��b�)���Q����?D�iV$|��?D���c��)���b����Q��LJ�f8���b�\v�-|���b�@d-|���a�$|�n�4��i��b��g�f8-|��ܧ���b-|��a�@d�?D�@d-|N����$D(��	�?D
�@d*��%��c�f
�?D��c���c��a��b�z�-D�?D�@d��b�a�@d�?¢�?D%��c��b��-D�-D�?D�$|H�a�-D��c�a�@d �@dV�z.��!��b"\v#��b��$*����&hΣ$�z���5�2%��z&$|Z�_'\v�
��A�{($|)Fnp�o*�z$|QW+�@d"��/,�@dY��.����z-+D$D.�z.��a��a/�@d0�f8݃�����1�c�$D��v�2���3�=D4��a��z5��`6��a���27�c8�a9�jd���':�����?����_�����d<�f9���c�z��5;��a<�2;��zs�=����	+Dd�fƁ{K=��ӏ���-z�`� ���a>�c?�zQ��+D@T;�ޣA�{΁�5��$)���{�f�|B��O('!6;�C�&D������.��5*�ޣ���E����f#j�&)�#/��=DF+�GMdHMd5��<'�ۀ�;���b������7�D�I�f;����.JMd�Mdt��zK�&L	 M�Cv��=��N�c:���O�CvP�YQ
���bR z��Ы��	��׀&�S�bad>\!��T�cU z��'V�bWMdh`X�CvX�zKY�bZg�[Md\�Cv��bg!]�Cv��b!^Md���_��(��	2�
;�b4�Cv�{���b`���a�Yb�bE�c��8 z�g�dg!��������b�F�m ze�Cv����f zg�Cv�!h	 v��o��$���-ed>
��iڊFS�@+it����+��cj2Dk��h�&���lNvlm#i�cm�bnMdo�Cvp�bq��ar	 s��t�Cvu��aH�%��avMd���w��a���x��d4�Cvy�bz2D_',�!��b{)D���(|�Cv=���})D�p�k�2vxʉCv~)Dvx>�$z�)D�
ף��Cvh!�)DD�ޣ��Cv���ao�������J�>J¢���a�Fh�)D�j���D��Ɓ���vxw��¨�/Do���$�'��)D�vx��-�R��ۣ)D��)|��)Do��k��D�.<��)|���(�)ڋ�D�)D8��CJĆ$z�)D[�)|��L���>�)D��)|�o5�v&h��v��_�����ܫۀ���y�c_��=�ף��/Dv�o���ay�c��)|g��D��4S�Σ�f�)D��-��c��c)D�ܟo�5@���/��)|�)D��c���aHmy�ck��D�)Dmo�vx���Q���d�ϥ�-J�/Ѧ)D�o��ַ�>�)D���x�ף����vxP�Qj�"�)|Ԇ�xv��*����c�Hd���>��i�Gv�	��j���y�t��µ���b*�/D��)|.�/D=e�ґ	���_��8���bm��b���V���>��ٕ��)|Y����2�Bd]���T���/D���-�o��c���k��#����o�F����S2��b:��γ��c�L,D�$��5�8�̍�xj����*`��QX��!���crgԩ�xx��cH�;8
ף<�"�ܶ�}���c������(��d��cO��&y�cƁR���co��xkxc��b�q���cЁxx5�����b�Fٽ۾�a��a�����-z������a䣚��&��ꗣ&h萺x��a��aĒ�>Œ�������ΣȒ�#�ף�Zmɒo��8��&h˒�bω�x̒o�
դ8 gQ�w��Σa2�8�iovxk�1<%TTΒ���-��a�u3d���	�5��a#��c=K9��a��۰vx���'e����9�a���(�Zm�vx�-|��xx��t�Ւ�&��xx֒�{ג��ؒi�ْc~5��&�
 }vxڒxx���9����-|Z�d�#�V
vxܒ��ݒ�c�i8�-|�+��xxN�m����5�	,k
��呼��(���	�-D�-D����i8ރv��-D�6|h�!n��	�-D�-|�C�C��-D�����$�6|���������;�-D��>��a����-D�-|́�j��a��!n��a9���-D<�-D��!n����"D���j�"D^����-D�"D���%��{��-D׉ʦ��b�4Z�ףp�բԀ�+�������"D;�)����b�Jv_"D��wU�	Σ��	��������"|vx��f8��	"D
-|vx��	"D��&�b%"D
�"|�b[�-|�-D�b�"|Zd"D�Jv�-D�"|ބ���!n�r�!n��D�Id�'I
�	I�����}q����D�-D"Dܣ��%�c�-D�kj+�) ��x!�c"�"|#�-D��D$�EJ%�'
	�i��"|Ȁ#������{�۫���D����r�݀)G���&"D'��c��:�(�c)�b*�Ba��9�ډ+�c,��c-�	o.�b
���f8mAm.��/�c���c�zЁxx0������A����e�51�	o
��.��	o���'Z��2�Jvv�i3�"|4�	o�}I��5�Jv6�"|7�f88Vd9�	oc��'o��i:�"|րw��'�����"|p ;��'<�4Z�ףV
vx=�c�����D6�{>a�x>�c?��c�� 2�c @ ��DS䣑G�ރ{:G�A�'=Up��
)B�c�{�I��c��C�9�D���cD&;�	o���'E�c��h�ZvF��Rx~���G
)܂��9�HMdI��c���	o���'�	o(��DJ��c�	oKMd9X��b뇘�L��cM�m8��	o_��l���b_���N���DףȀג�&
^�O�fP��D�����ǪQ�i���RVdS��D�iT�m	�f�{U��D��d�-��+V�;|W�iXMdY��b;
)�ߣ��c�
��s��Z�&��磀	�[��DX��1o���}x��8\��Dd�:�]Md��&>2D^�;|_�/�2D`��ca�@����fb��D���c2D(��Dd��be�iL�Z�&f�fg�f2Z��A�����f��������D��h��o2Dm��i D%nV
2Dj DX�{D��e|k�zl D���c��fm���nMdo2Dp��Y� Dp��&|q�&DrMd��/Ł&De��D��&D� D+�m8sMd>2Dtx�o
ou}8
 Dv2Dw
ox��D�9D�f���N����DT�&�r��J��+c��c���w�p�y�fz��D{��Z�m8|�f}��Do���ִ���'~�;|>2D D��/.�>��U��
o\&�jd����D���i�2D^��(��D� D�9D��z��[À�d�2D�&DR��'��<�կpj��[���{� D>2D�&B�;�9DV
2D� D���j��z��>2Dp�Kd��ovx5	�#��zj��i�[�D�ǪN-b�&D��磒��'��ԓ�&D�/gL���g��<��-z��&D>�h��c��&D8�i��tjȀҁ��'_� ���&�� ��cX{J�{™
o�� ��cl�(n��a���{���xJ����c�� ���a��ݖ��l�c��e�
o{
2�
o�� ���{�
o���D́�B��*���{�� ��)v�ۇ	�1���{��&�� h�5����{�� d��ު��{��)�cv�F�ۯ��f8���ܫfp� �X�pj�f��b��9D���i�I�c�v<�(n��b펫+��bw�76��c��i!��bh�`���{xףl�$h�&���{�f8 ��kvx��?Dc����b�=���i����`9���]��i��b�Ǫ�
�J��c�c�ck�b���i��b���{��c$�Cm��?D�� Ҁ�{��8�2���y��{Q�“�i��{��b(��Ó�i4� ��bœ�iƓ�{Ǔ�.r�	��$|��boѰL��ɓ�{ʓ�ia�c�l*˓�i̓�{9�;��b�{��?D���dՊ��͓�Ċ	W1�f��{}��c�����n4GГ�i%��8*��ӏP��dwѓW���&ovx��&��1��b��]�������$݀�`��`���J֓�a������lJ�c6}x38|ؓ�i��`e�b=�p�ٓ&ړ�a��ۓ�i��p����a�+Dkvxݓ?D9´��&��`QR��+D��{h�!��i�+D��+D�?D䓡8�ߣ�?D-�Ϣr��d�=�iQ��"+D �@d[ףG��۫�	4��i�����z�)��}j]�=D$�+D⁃��&�!n����z"�+|�)����+|�)��z4oX6�+D���R�&�����>�a�Y��aj�����z݃�A8
�v��(耚-Oo����aXW^
!�I�{o��	>+D�݃���ߣ��h��>h�@d������/����a�
"��o߂=D��h���a9����>[�6[��i�2���'��>���kv�2��:h���+�+D���$�?z��>�i��R��f��}��b6�?z�	 	��$v���z�ӗ�ߣa��Ϣ��zX5�Hd�f��.��k	 ��bJͣ	�+|	 ��f�Gm	���
�i�������$��X��X�
���b��=D��z�f�i�>��z��	%�a�)%2zo�xxk�`#݀Cv��i�	 Uv��d���I
�	/?���cY��uW�{2z�i�	�>�����*��R����(�e��?�W��uܣ�F��i�ߣ.��x�&>;���o�A��c �g�!�7�"Vd#�il�,$Vd��io� %��c&��bi��$'��d�n�Cv(VdA�c=����{��Gm(��93�)��c�	 ���*Uv��]�+��c��f#,���-Ӏ��ל�DdVd.g�J�a/�a�
2�Fa	 !�o��_�i0�a�	�`R��UvbDd1��t
�i�u:���/2��ogd=�3�a42z���e5Vd]2z
��D6�i��Y��7
2݀Cv8�a9�8��z�	 :�ze�	�%�)D��x;���Cv��z<Xm=�d�>��?��$f2z@)DA��cB�z�jC)DDj�h�zEjF)Dه�-�	/|G��{H���I)DJ�)|?����v��)|K�;|ڋ/DL�M�zN)DO)DP�/DQ��i���ӃFa,�/D��z���R��i�DdS)DTCJU)DV�;|z�/D��-8
)D
��DW�/DH���X�zY�e���]�J��:��	Ҁ�d��D����iw=v�5���Z��@Ł/Ds��[�b\�)|]�b�����ze���?^��i_�b`)D�iea��ib�zc)Dd�/Du�
2�Xv9s���7�e)Df��i{������/�/Dg/|h�bioj�/Dk��il�Ը��mn�zZ=o�bp�bq�z�[T�4r)Ds�z���iZ�t�zu)D���v�b)D)�bw�zx��iy�/D�)|�!Dz�c{�bd�BdXm���>��i�)||�/D}�Bd~�c��i��/D��	���i��)|��b��/D���i��%B��Bd��	o<�Ϣ߂�VJ��䀉}j���i��Bdp�˸Ё�_���cπ裊��i���i�آ
�񌔻c���b�����)���{�o݁	o�<n���c��b'��S� 	�b��c���	p�%g)=��6Zd��c���i/�a��b���h���ib��,-�Ϣ�$[RҘ��i5�Bd��裚��i��{��	o ���c���h��Xv��U8_��n瀨8 ��{<��,���ݡ��'��c>!袔�,���nA��'���i��{B��dw�٥�c��˸ƁW�
� �*��S�)
��%���h*ۥ�{��c D�c��\��)
��(O��cU2�
 ��ab��c���o��x���ߨ��d��c���c��{k����do
&�)Ԃ�a߂�e���c��� ��Y�98΅�{)��Q� ���h"�z���=���c��(������E³�i�i8����c��i��i���i!�-|��z��i�:�V �~8�݁�-|� ��{��?D��Id�i)�zmw�-D{-|”IdÔf��Dj�e�i�Zd�{ +�h�#�d`Ŕ-D��{ǔ?D�-D��i��Id]�;�?D�Idɔ-Dʔ�'˔-D̔Id�Fd)�?D$�i�"Dϔf��D��z@��IdєIdҔ-DӔ�b�?DԔf���֔f2�\vؔ-Dٔ?D�-|�"D���ܔ?DS�-D�IdݔId8�-Dޔf8߇?D�"|�"��-|�˕��zȀ���Fd���0��ir�)��D'
������i�i8�f��z�-|���をI�?D�����-D�-D��i��Id��>��>��z�f8�f�f�?D(��D�-DX2��>a�V���/�-D��>��Id��X��آ�-D��f��?D��Id��T壞�f��-D��Id��آ5����
�/��-D�
o��g���>�@dh�-DÎ�f��D�?h��t�ߌ�Z�Nm���"|���P:�%o���f8�z���G"�h'�'#��5�D��x��>	F
��x��(���z �آ�,�a�2���
�f8@����x�n��oA f��Yd[2ѝ�o�H�̣�c��o�蓉���%{�@d��f7�/���t�@d�?h���f ��][�SJ��(����
o,=g)'!6�8<�2ރ{l�m��g�w�%�25@��d�
 �& .��ko��d5Z5���;
o��im���k��' ��i� k��i�f!��e�"��i�:uJ�#Mdɀ�i���־H�p�$Md닳[%�0	f�&�2��V��k'��i��b(��k��i鑦R%�2)��i�)*���+ Ă�k,_��m8s�i-Md.��idۢ�Cv��Yd�/Md��{k�2��Cv0�Cv1��i���ރ��2��ip�-��{�
�m#Md|��3�b�v���݀Cv�BЄKv4��{���5��i���%#Md��`�����j6Md7����n*��8&|9�/5�cĉm8�Dd<�m8:��j��x;��i��:偓�Ă�i<Dd=���>��i?��i@|~��fA�f��>!��zB�Cv�_d���{C&|D7n�8:EMd냺iF��j�MdG��{��ޫ�	H�&DyDdI�&D)�?J&|K�FLMdMfN��{O�4>��P��h��&D�܌Q��R��hSMd	f�S&|��	9�eT�8�	�z8:U��hЁ���&e�&D��gV_dW��{X]dY��h׀�Z�&D9D[��h\��,]9D^��{_�Cv��j`9D\�8��Cv�&Da9D�O�b�e�"��'c9Dd�&D?�U~.��he��,f��{g��>h��i.��D��p�����i�	}i�Kd.��D���/j��/k9Dl��h��b��Dm��z�&Dn�x&D���/o9Dp�[q9D;�x���yDd���rDd�9|A��'Z!����s�z��꣚��t9DWۯ����u�{v��/�����9D��Kd��&Dw*AZ��9D���D��i)��x9D��hy�i��iz��z4�z{��f
��d��i|��xԀ�%}�i��D<�(n��z~�&D�i���hʃ_%�i�9D���ha��z�	����i���Z�fb�钅9Dt�zo��o��i��z�^v�i�9D��i��D�����9D�	Ym� ��i�9D�G9��z���D�]d���J,��/t��>�$3��/�f��i��D��b�:n��{o��x�f��9|��D��> m�.��Do��Jo���{����E9���-���D��$��i��zی�zp"���%��{*����Cm��U�����y������	�!ۗ����{kr������i���.��l�c~��x$�dl�x��{��z��$?����۟���{��-=*�X�61��$��}��i��	�F9��7��ib��e���ܒ�iZ��׀�-�5iFێ2
�%���d��At����ŧ��%��Pҩ�&֪�Cm��/��Cm�Ș��2
���*���}����آ�����׭�{% �`8���>��{��8��$7Um��2������q���(��{��Cmj��/z
2�2��$��'
��o�ã���$[�i��?��a���/b�dYV�(�۲�裬�2�Q$��z��F�Zd��z������ɸ�c~
���@۹������+D��c~���.=��%"+D�)�*�J��z���
f��	�?��z��fr�Ě���Z���z��+|��IdQ�=D•�x����+D/��ĕ�b
���&.�zƕ�bǕ�x�=|�;[��er���,���9�=DՊӘ�{��&ȕIdm�aɕ+|�$gQ�=D����	�z˕�x����{��z��%Ε+|ϕ�b��{@�ѕ�)H=|R�Idҕ��ӕ�x��{�����z���hՕ�b��'��{��Fi���ی�zؕ2ψ�D�$gl�f���B��b���D�$gd�f�$g��'+Dψ�Dݕf��%=|S�Xm�'�$g��z����$g�D╪j)��x�x��D���j���)��xg�z��D�Od���d��z�D畁bg�f	۰��D�=D镁bTԙw�O�ꕁbc�����de���땁b���z��f8�d��Ide���h�f� �z�Id��i� �z��j�$g€ã�$gV
 ńң8�d��b�j�>nz���$g���$���c����k���z��{��Q�R�]�'5�5��{����d�g�C��{*��z�k���	�f97�<�ã��$�Fi��bJ���h��D��$�����i�f8ψ�D ��z��h��b��ׁ�f8��z� ψ�D��ji��C�)ψ�DY�f�Gm���d)���v��^���>��`�"g	��$���db���
���z���K���d[�6s>����z3�9��R� ȃ��
� R�O�O����>� "��>p�b�Wd��=u�bu:��-�rf:�"g��h���z��x� v�ң��f��-��%�i{�i�/|� ��d���������kdMd)����b�X��0��i�b9r�e�i��$T�	o������zX�z? �i!��c��	Md"�iۃ�+?	��#�iX��z���l�/D$���%�xƁ:d&�i��'/|�Md��<�-(Z�j�ۆ�;|)Md*�/D+�x,�i���(�3��,xVd
�/D��b-�,.�/D/�i0��O1���d�/D����Md�2 D3�b4���5�/D6�i��� 7�"g�/�/D��� ��x8�&3f9��&���De�Av: D;�/D�x�Av<��xMd= D>���?����-=�t`�{M@ D3��x������A���B D(���C D��
��+Q�BdD��iU�/DdMdS��xE��i΀�xF DG��Md؂BdH����HMd?�bI DJ�}'!K�i� DL9�BdM�iN��i��b��{>���-O��P� Q�zR�%�J�N���S��i���T�Av;�� Հ�n_�	U�{V�;|T�(��iW�Bd;��$X�zډ'!m�Y D
�d[�݋��'Z�/DSjϓ� [�)g\��i� Dl�z���l9��i���5��脢x]���� D��m^ D+;_�)g`�	+a D��["�bfbv�+c�	lC"��d��,�)e Df��xg�)gh��ii D���d��+Q�bo��xj��iψ)g��k��il)m��i��%�
ۯ
2n��iT�)go�Jmp��$q�)gr��i����s���t��8u�ov�cw�T2֣x
o�)g����y��i��d��ãq�zz��i�����	���z�@��-{�i{g��%r�z�$|�z�	 }�o~��i�{�
o��d���i��b���
�5�)����"��ii8�f���i��)g���b��zX5S�)����t��Hތd�������i.�����z���b��i�偫H���댖)g�U��(ݍ��iS�)g
D���b'i8,��/���i���i������f
V �V���=��!n��!n���bo�A��?D؂�if��̶��[dc�=���{ȃ�Ƅ�?D��[d���i���x)���W��{�C���:�
�8�=��dt�?D�?��$
�I5�=��[d���{��=<�!n��o��?D����[d�����"D�$|��?D�Fd��o�	"D�Fd���{"D�$|�Fd�����"D[��5�!nFdL3���i��!n���n��?D��=��x���"D��?D��[d��i>Ev���b���i��i��=���z�"|��ܭ�i
��d�s�f�i8�$|Ё�`,��/��"|�Fd��'�"D��?D�t{p!��\v�$|�����-g9R��"DR�"|\�d��!n��?D��i��9�5�=��c�=��!n2ü�?D�"D��Y�2�c�=��!n��{��"|��?Dc�=–!n��[d�����'�e�Ė?DŖ�i��&Ɩ?DǖE��-gg��`��i���"D��{����Fd�"Dϖ���-g����"DZ����Fd��i��i�"|�7$|����$| ����"DS�G"�$|l�I��i��m�Fd�F��$|�H���{�������a\$ݖ�N��⌀"|�*A��z��$����
��z��{�S����n��z��i��o��z�G"��Q$b ��$D��z\�i�G"��&��{��i�-g��z҇��Hv2S��Ggo ဉ$��{B�Hv�-g��{5ȣ3�+�Hv�-gd�ց�iQ� g��i� ��z��{� �f��i��{_�i��{	��5�
}�Zv� ��x��z��,{��'��i��{��i��{��Q��,����Y�{��i߁��i�{��E�|}v�+�{�i���v�i�{�i��	�i T,����}�X$��{�V1	 
�Q$Ё�d�)��i�y�
%n�i,�hd��an �m8�{�z ��a���$�ij�{�{o�-��{#���'�x�z�Hv���{�i��a�{�z��am�8����i��i{�x��a�	�{ ��  �{!&|�2j�f���{2í8�f"��a� g# $ %��a&��z'��( ���`)%nz�i*�x+&|��z�����+,��a- �{���{.�&D/��a�Ҁ:�&D�� ��{0 �&|��������%nI����&D1�i���2�&D%��'3�i4�m8��i5�{�)���6�i7�&D��Y�8��a9)D:9D;�i<��a=9D�&|'��_>��a�)D(�&D��t:|?)D�&D@��a�8�[��aA��'Ђ	�)�� B)D�������%C�~D9DE)D��&F��aG��aH)Dw�n���/�)DI��aJ���뀽�K9DL)D.�&D�	�m.�&D���z&|��'M)D[��&���^��&D�N�KdO��P�)|Q9DR&DS]dO&|T)DUfR�'V&|W�)|X��z�)DY��>R�'(�&D��/���Z�&D[��'\�)|�'��ڹ��.���]ˣ^��_$g`�Kd��':�����`a��zb�&D.�&D\��$��"���dc�&DQ�	d)D��ԣ)D���e��z�&��'Ts��&��(n�9Dffg)Dh��c�)Di�'�9D��oj��o{�h�zk)D��:no��9��?�l)Dm�(n�'ntvo�)|���pfj
�q)Dr9D%\$s�zy�(n�"aUmt�£���嚀�·�>`u��me��cv�aЂ:���,w�%"��'x��iy�az�)|{�'��|f}�'E�)|~��c�E9�f���z�B���Kd?�V�fe�0�5�(n�f>D��':4�a����d���,���zv�@�a��y����z��F���?����{���z�B�hE'�`8���z���,oB����f��z�`�P��(n��Ύ��'� [���(�t��,��a���z���z>�À|�r�S�z�(n���z������zЁ�%�+��i��(n��wň�?���z���'��#ۘ`8��>�`8儤������g��$���'��a���%�$��d���r�<Q�;����	�?���㾀��"�2���'��3���ԚUm��2��a 7)d��	�����z��$�g��Y�Տ���ߢ�as�R�$���)ϋ�ݣ���o�2�-|���X�-|���z�+D�Od�Od
��d��d��1���$���z��c���Od��B=B����,�t����z��$�-|��2�	+D�-|d��	+D�-|���z5��&�-|�-DX���o��?��5i#rԣa����
?��+D��+|Od
-|�+D��Ĺ�+|��9S�ˆ�b��$d�-D��+|���`+DI��'��-D�-|��-D�`8ʎ��a~��-D����� D×�'� Dŗ-D��)�\v�-D�Ca��<V���jȗ�@ɗ�x}+D<�h���&����y��f8ʗ+|� D�-|� DΗ�&��ݫ��^Q����f85�h׀+|� D�XmԀ	&��2З+|8��.��'ї�az
Od�$җ�a
5@��
�i�-|� D�R՗f8�OdJmח-D���â���xl	p�� D'!�]mٗ�aȀ�� ��$� Dۗ-D����ܗ�a���ݗ-D	�j��&�`jޗ+|o�	&ߗ�a�-D-��b��a�)��&�-D��`��z�fx�-D.���sfx�)g��a�+|��&�)��Ds��� D3�o� D{fx��D��{$�)� Dw��8��D�\v��|܄�z�\vfx���)>?���z�g�j�d� D��Dw���k��D��b� D���$�F����z��D��j��	��zg�x~[���,�)��Xmd��f8���,���a��D��zā	&��{��&>��h�u0��a!��A��G)�9t��p�]��=�	&u��it�,��z?���z��>X�;T��z��a���&Ɓ��5�Im�`j��>s��Jm�	��ތ�>	�$
�z��,u:�x�u������z�~Ȁ�^
�{�zw�uf����)_',�z�,h����E��&Y�b��D��	v��{��>QX�=�X��z��������z��D$�o��	�d��)g�b)��|��z~/|j��a���j����Ȁ%�)��| �x!���"�,#��z$�}%�/D
�a�	�>&�#n'�/D�(Ƽ�]�(�}��/D�
c�5���)�&o���b��*�#n���+�/Dm�~ӏ��,��?<�ai�}-&�.�/D���>/�},�`�$|0�a1�#n2��-3$|4���5�#n+/|�G96��>������k$|7�/DG��z82D�{��'9�#n����5�ʀ_}5��:��-Q7:>2D;�/D�u�<�/D�$|=��id>$|?�$D��z@�b��âA�/Dk��x�2D$|B�Xk��xJդC�$D0a��xD$|E��xF�$DGMd5��H�$DI�bJˣK�#nL�$D<�aM$|N�bO$|���`P�'����Q�$DR�#nS�/DqG+�BdT�/DU�}O-gV�/Dg��x{��`��5@W�$DX�}��|�>!Y�/DZ�#n[�}�:u\2D]�/D���`w�}c�m#R�/D^�$D)_�$DO$|��/D�\$H$|`�tCAm(\m��a\mb��c��id$|e��x����f�'g�'h�)�	�?i�j�)k$|�}8Ё�dl$|��	�7��
 m}�Z�n���'o�$D��;gp�%:�q�$Dr��xs�)"�{t��x�*Au�)uԣv��`�	&"�{�Úw�)ЁM��Ymwۯd�7�x�4Sy�;gz��f{�$D8
=�o|�)}�)~��ns�$D97:j��lg�d�^�Sɀ�����'>�&���@�)���c�ۯ(���)��[����c�(	����ի��l?!
U2O-g���b��	&���b��ۀ���}���i8���cG��bd�	���bՀp��'n���'_����)��$���a���{���"i8��;g�
�i8_�i��$���V��c��Y�4�)��$��)�����`��)���bĆ�c�i8j&`�&ؖ��c�i8��)�i8�:�݀�'��)e��c{�5b3��ޛ�)���a���b��$G
�݁z��g,�f��`��i��i��$�g�����)���Oۯ��x��%���O��8���|���Ǘ�ܤ�iO��br�	��A�Fd$�<��bd�r_�3G$��kr�ܣ���bt&fx5��h�i�6|���b���j;��<���bbi8c�k>^�$ĀZ�y�$���z���$��+���b�Fd��F�~���c��o���b��`f��bd����^l�bX"�Fd��ױ�d��`k�`kXi8NKѳ�`��o��$��?���'
X"��
�Fd*��J�3�����G��>��~�󸾀S�7�o��.�)D��9绘o��'��)Dw��'d�O��ʈ�0��I"�	;��/�)D��o �/5��h���N����X"���(�)D���'���)D��E���`���.��&9����)Dy�`�)D��`Ƙ��ǘ�'��ބV�Ș�'��:�Q��Κ���)D��Dʘ)|���&�)D̘o��.
 f�->��&͘�b���&Θ)|t��i;f#Ϙ�'И)|��5���i�X"�)D����Ԙ�b`����X"֘)|��k
���À�&T��ט)|ؘ�b٘�&ژYdۘ�'ܘ)|��bg$g��Ydޘ�&��a���)D☁bd��%ĉ�&V�)|�Hv�)D��a����b�)|��'�Ӹ%`j��b�ZvQ���阁b��al� �)D�Zv� ��b
ԣ����O���)D8
��Z&�X$��2�Zv"�a��
}�)D���	�0"��&w	,�)|d��^�K��)|��a��,�g��,����Yd��)|8�Yd��Zv��)|���'�f8��a��2����)|�����b(��2��)|������2�,n��b�,�ݦ�)|�Am� ������((��2�,k�yb��^��$�|�<u�d���	��$��m
��$��b�N腤�D�dg�<��>�D���|/���m8
�be���,� (�� �a=��{��$@��$�Am�b�f8�f8�,@����{�m8�K�X��q�j�#��8�
}l��`ʍC��Am��'����&��� �x��'[����$����K���� (��2���'$��}�� �"|�ʀ�,�) �, ,Vd>��~!�e�k�yb�"D�� ��7n"�)��,Տ�#�)�{�)���$��$����yb%�"|o�yb&�8���|+���'��_t�_�"D(�%�S��$)-|m��8*9D+���+-|�]d)9DS��$耢��y,]d--|.�b��8,9D/��$����Cm09D�-|#�x1�82�)3�Am4��`�9D��`���z�mj)�� 5��ⴅ�`6�-D7-|8�-D�fx9��`:"D;9Dm�o]d���'<9D=�-D>��`?-|@9DA]d�9|݀KdB�-D���'��j���C�-D.�&D9DE-|p]dF��`G-|fxH^vI�-D�8:J DK�KdL��'M��'5�-DN9DO�z��iP���Q�-Di9D
�;|R�zS�9|Ё�݆ DT��`U�)�	 DV�Kd����3�id��W���X�)Y�zZ�"|[�-D�i\]d��&!��D�b$@+a.nd��]9Dd��^ D_-|`9Da�i��i�<��b-|c��`��|d9De��`f-|g�-Dh9Di��`j�-Dd]d�9Dk�b��i��l�f�moظn��o�-D�Jm�op��`q��`r�-D�:nl��x���~� Ă:ns�����+_��6Q�-DK&to��E9u�-D��v�-Dw�&x Dw�U5w��y�Kd�.n*��`o���z.n���iƁzT��'Q��,�Em{����|�z�.n��mx�do��}�&~D D�c�}�Em���,��,Ă�c��zv�+��<n�ꣅ�z���ކ��f��$���z��D(��,ց�bĉ:n��$+��,��ҷЁ� ��Dv��f��:n��$�`8ŋ�cfG9X��c���z��Ʈ����m��c��D�`8}���֎�&9��,`8���,5`8o���o��&D����
�$`8�o�o<u,]㷒��&��|��+�o9H����X�`8� ��&D�	�Ȝ���b��z��zwg���ߘ�&D��`��)gu,��X$��&D��`��`���5��&o���&D�=|��{�����be&9·�n��I9��`��$߁�&���@��)������1|���b�`8��&D���z���z�<��xv�`��`���&D���|�`8��a~��`��$��b��?D��=D��&��=D��?D����`��`�8�D�=|j�K�=D�&B�;�G��x9�=D��D�	R��8���&���&%�`8H��xk�?D��?D���&�2D��a~Q7���b��a~��?D���a�`*�&���=D��i�=D�2D�$|��bę=Dř�&*�C���`��Yݐa~Q3��$|�{�	�)��?DQ�7�)��|H�`ș�b�2D�=|��$��$DX�;���$ိ~���5��$i�`��a�$|͙�aΙHmy2Dϙ@d��$���k�P�s�)��|љ�a��ѻҙ?D�G9ԙ?D�=|�a~��&���a�2D�OdؙHmٙ9�ڙ?Dۙ�a�	���e���&ܙ�aݙY�5�=Dޙ�aߙa~�?D�t>���ӏ��<����?D�?���)㙧��a��$��+�2D噧>�=D����d�y2D�?D5�����a�٣�)Ꙥ�X�zK�2D��z� y2D���Dc���ܣ�[�ϋ;g����&�2D�$|��z�>o��'���{����Ј�Ir 3t&���F��F��	���'���a+�Hm�C�*)���⣈�)���a��$D���Լ�&݀�a@�)��g���I�I�x���g�,�<��f���|��	��"g���a>���W��Hm�"g���|k�x,���a�"g��b������a?��"gQ������b�(h��u,��"gj�g�t��J�u:���h�"g)�Cm8t>���
�+9������aU X�W1�� 'h�"g�E����o��� '-�,>��x�:�o�v����>+D��(j��΀�zO)�J)��|��>��J��z
�?���8�x���z	��a
��z$jj�9��3����Av)A��$i�	;
�Av=�r��x뀍$L��a�[�+D����ի�����$�"gm[�Jm����WЁ��xo�o��$�AvQ7���$&K+��dA��$�����o)��$�P�Av�Ĵ�"g�����(�"g�Av&�J9��$VDd6|h�"g�������|�y>a�e��e�93�=�e �x��!�o*���Î�$X��"��$�Avd�#�a$�o%Dd��_go&�Bd�
+D�
)'�搋6|���(�a��8o���Dd��b)�Bd��^�)���*�a+M,��`])ho�	 -+D�[�)@m
����.+D��</��$����Av����e������`5��$)��${@�0�Av1�Cvڂ	2)D/�Bd��&<�Bde�
)/��$3�z)��$�	O	 脼8S���h�Bd�Ddo���4�uC5�)I�Bd���)D���6���]�)|�p>���k�z��&X�z�a@�����7t��&�oi
��8�&f�z{6|9)D��!�J��c!�A�!�:$'뀗d;��Ȁ�^�)|��a�)D�:�<�6����p�)|��Bd���=�o��)|>�)|?$'��|��a@�a=�x,��aՂ�`A�`�?zB$'C�aD�`�	�a�jd��^E)D�7:4�BdF֌G�*���&�)|+$g׉پ��������})4��b��%���cH�h~I)DJ�EK�`L��վ�q�M)DN�aO)DPZm�u:Q)D���Nane��h@��R�`si8�)DS�zz�aT�`���c6�fU�aVEv��W�a��`X�dv�y>X�Ư�)|X�`���Y��b�2(�a��e�Z�)|J�a_��_Σ5�a��`�2��bĄ�'Հ����"���h~�� 	$'���bi8���Ȁ�&�1[$'l�O+Evw�]���IL��\��]�a}�/D^�a݀Ami8U�a_ �g�`�`a �Amb�`k�q,c����ad�)e��e ��8@��f�dg�a�h�Ami�`�/Dj �)���{V��k�bA c��lFd( ���,9�9� z�Amm�knFdoFdpj*d[�q��'?�+(��w��$m��rFdsEv8�/D5�ct�`
�k��bh�$u)Ёq,vFd�"DwFdx jGv���b�EvyFd��0zFd�?�N{EvS)�Am|�x&�`}�"|)=	%j"D~�bR�aʇi8����-|b��d���,��i6��/D�;D� ��Y�4�� �)��݇�-|x��;D��
 � 3������݌�`(��N ���ݍ ��f������< �`���'W�}��� t���Id�FdR�-D�馓�;|ہ�Δ Xm�j*��Id��k��-D�-|��f�Fdk?����d�Fd��;|��$]��b��f�Fd��;|�	o�)�}��G"6�;|��|���:��	o+��� D��b���&��f��-DH�i��;|����:�<n���'��;|� D��G"t�z��Fϯ�f��Id��G"8�f8��;|��G"��Zv��}�ش���f8��i���?�<n��Hv����b�d
���=���Hv�}��Hû DQ&��f��i��>��f�;D��C���'<�Hv�h��H�*o�)3��0��HEmšfÚZv��=�������ĚHv��H�(�h�b�Yd��Hvه�|Ś�{��;|��?ǑE�7�E�H�ƚ�aǚ-D(�H�[�	oȚ;|��)'�HvɚE�y�f����	�#�|��Em�m8�<n���7o˚f�EmY Do�4},�|��m8�ش��8Κf��z�W�;��i%��$�<n�C�Em��HË��$���EmԚ8� D���X�8֚Hvךm8��|g��
�Hvؚ_w��ٚ)'I��e�6aښfV�f���|*��?ۚ���H��D��,{��${��$��TT��&��m[�ޚf9����Hvߚ��ʋ�|,��D8��-�[��D��+(�H��D
��oh���&D�[�����t�m�f�o��*��zl�.XU��&��z��ܜo8[��Em߀)���dl�a�m8L���Em�d	��z݁)�	;�Em=���&D
o	�fO�&�TT��z�E�	�f�)�f�)'�Md�f�X��2�)�f
o�)�t>���z{��$�2�[�)Md92Ä��zH��`�[��]dO��`�t>��2��f��&��&��?D��n��ˀ��m��?D��)��Kd`���.�)��Kd�
o��?D�D���J"��Kd�D��k�?D]d�Kd)Md�{�5����D��|�߂�h�Kd��`{��c[��?D��h�	��`�
�{���z$|
�)�KdȀޒȀ�Ł��`�$|���8�)��?D
��z�&D[���G9��z��)�':��h� �?D��h�8]��z�)�?D� c��k�4��)Ђ@d�@d��'p�)w�Tٸt>�f�Kd��{�@d�t>�� $|�N�t�MdT5Z��`�?D ��!�@d"��`���`#9�{��+>�?D" 3$]d%�Kd&�?D'� O��`(�Kd\�|�����k� )�Kd*�?D+��`,��x-�@dX��hZ�i��-3.�?D��,/��h��0�?D1��h2�Kd3� 4�Kde[�_���5��kw�E�ܒU����hr-g9�֜���6��h7Wm8��h9��xȃ�S��\m�|����x���d:��t[��!!��ݗ-gwt� m[�;��x���<�@d=$|l��>�@d��zz�	$|���w���{����@d?�
׌�@��x��+�|�	�R�	A�}Î�iه@d�
ۯl� g*:���dk��x)��:�[�
Xm	���)��:�;TB�z��Ɔ��"gC�@dS��'D�@d��<���d������d�ցJ�E��D�"ge���F��&����G�ԁ_�H[�I��bJ�z��DK�lL��D\�zM��&
u���D�8:H��&�*:Ӄ�D���&�YN�zO�`P�|������!!X��Q��&4�=�e�R��bS�ލ�$J�X�L��DTOdU��bV�$���&W X��D�oYOd%�4�Z�$[��D; ���&\�|�]�k^Od��h�`���e_�Cv�f�`9DOda9Db�`c+Dg�`#d�`�$�k1��e9D���xe�Cv5 �f+Dg�|h�#ui��π=j9DkOdl�$9DmOdn�Cv��+D��ᴊ#u��a�+���&bOd�7����&o�`��bp��b���xq��&r��Ds��bt�#u��bu�Cv�"gvXmw��b��2/g�!�6|O��&�Odx���y��bz�Cv{��&L�2|�`e[��3�vأ���-]�`Y��h}��bA�fƁ��r��_��~��+�>q�OOd�o(�Zm���`>�ꣻ$��`� \�Zm��`1�2�=�Odm�"���$o�ʣ��Zm��x���xh��b���x������'�Od��Cv�2���`�+D�)D�Zm��L��x�#u��R݀Zm9�{\�Zm��Cv�)D���$�)D��Cv��h���x��)��Zm��#u��\m��t�l�Zm��#ua�G%��E�!洔�>�
Я�>.���b	 r�����.�!)D��B��)� �)Db	 ���$���艂)��E"+~-�)D���'�[�{�#۞����>���z��$���4[��j��=��)|z�f9��ډ�z�f�S�)|��
]��`��h8[���F��D(e���Zm������:��'m[��z]��)|��b��Zm��f�Dw���=�EĬ
2��f���2'9w3��9�)D��2'�Zm{�D��2*)D�|�6��b��b��2�|�N�3�)D���b��$���V5@�x��b��Sz�)Dw3Κ�ͩ���)D�f�
2�u:���ft�.��{[��ͷ
2�|��)|�P���ze߂��	�z��,�
2���b��E����8�À|�o� ���{��dÎE"
����,g�`#>�)|z�-�[�>�� �� ���zm�)|��x�Fh�[��F�›�bfÛAmt� 
��aL�a��?L�L�	;�� �/|���5�?�

���dǛ�zț VÐ�|��fʛ�$� ˛�z�=��*�͛�zΛAmϛ|���f��bi�Am��=D֑�z��b��i��/D�ˢӛ�z��&ԛ�$՛�z[�v⁤cS��ݼ��֛ כ=D��/D��iٛ\m��hۛ�zĀj
���ܛj�9��Y��z��S��4҇�ݛ�z+�|���h�B+��h(�F�ߛ�z�$�����h��b"��e�d�}��z�^m����-���f�)�����z���'��z��-��}4S�^mv���d蛟$�
�N�u��ܼ��7��,�����'�
ˢ�f� ��it�d:�m���/D9�{� ��z�/D.� �=�%�"g��'5��iЂ=����i��h���$���Z�'�$-|��h�cb��d�-|��=�r
�.��b����z���⩀�D=^m�hO�-D��'���D��;|���D��,����h�-|��;|J��v+������D��-D�� ��,�|��-|�C9�� ���D0���m�f9{���"gl�|���y�쁡j���	��'9�-D��D$��D��b�b�"g�C���D�;|�
Ca��D�-Dn�Ѐ�a��=��v���j8��|����D	�-D
-|��D��-|�	o��*�=��t�d"�;|��+�����|��M��h
���.���e)9�;����h~
-|T]��8т���-|b��d耏v�i8Ȁ�	-|�����l��4����8��b��D��|�倢xЁ�ۮ)��-����D��D�8���a[��-D�->i8����D����-D��x��D��j�;|k��&��DEm
fi8o��o�i8�Em���?	�ƫ%��$g�v�Em��,��8��>��,����Ɓˀ�ݬ
���z �f
��s�i&�h!��,*��x"�`#�z����4Em���,$�{[�%�hX��h��{�c�J�&�i*��z&�+gЁ��:�z~'��x	�vk;(�8�)&��
�k��n���a�%$�h��`*��,+X"��b�=�k8[�,�b&�*u��o-�`.i88�T2/�)���9X"�o0�8��`��-1��:B�8�t�'���d2�`\�+�-��|����z3�zZ�h4Xm5�byWv����-�O?k�b��~
	�>��݄�-�
o��/4Em6X"7f�k�i8��y��=�8
o�Y��z9�$a?�%�U�ɃBd:�Y�	E��¢;��~�� ���?<�D����=�$���>��'�
�D?��,9��,@��,AMdB���Dc��h:�i�p������ւ�"
ot��+���C��hHf�
oX�9RD�?DE��hf���I��hFF�YdG�DH��h��c�`�Am�YdIX"2?���DJ�x���'Kdw��DL��h��姆�5~~��YdM�x+���"~~�Xޘ�T�X"2 ��'K�?DN~~�рO�Rt�d.~~/dwP~~����Q2DR�YdU�����aSNv|��-��ht��`k T���$�o��G9T�x��Yd��Zv�����'��DU�ZvfVdw��D��6c�Yd���W�����fX~~Adw��9v��?Y�ZvZ��h���`��Cdw"�Hm[�Zv\��h{�f]��h�=g���z"G9� o��&^���~~_�Yd�#`��ha�Zvb��hc�Yd��]�d��zd��he�Yd9X�f�?D
�Q-g�$gj��'h�Ei�[d����j�?D��o�Yd��	k�Zvl�?DI�Ɂ# ������>Ӄ8v
~m��>�d_�Ԥ�р��vn��-s��o��>p��`q�xU��ir�-sNvt\vu�)��	vNvw\v�~~X��c<u{�t�dx��ݺ�m8y��w��B�m8z��>{�������.�8|�Zv�Zv�d�i}�8~��>�o���d��`q�����d/����ƀ"Do�̴(��i��Vp��:�"D
�=��� �c���>�)|�"D:�8݊Hm%��c��4�8� ⁤c�m8��&���z��"|j�f���DgX9��\m��o��DY"DlX9o=g���D�;'geedр$��D� ����&� �U~b�h���D��8o�f�h���>t�d�
f���"���`֜�� ���>���D�]d���,� ��Dw������>;�f#���D� �,�]dӐf�9D��i��D. �9D>�"|. ��D�����h�]d� ��h�� ه�{g�E2��hU�,0Z�����$��)��h�,��h� �j��h�,��d���k�f��]d,��/��$��h��`���`�]d�"D���`�[����`���D�]d�[����D�uCw��`�=�$��D�J�]d큍$ 	]d� O^v}o��D���`�^vw�66� Z�
�� ���D� h���� �o��h���㄀c2��Zm���h���`�o<���Z�´<��,��h� ��hهc%�]d�]d��h�o� ���i�X$�(u�]d�]d��,��hœ)��`�]dĜ�`v�)�]dƜZm�UAd��?�)�^v5��<f�i?	5�ȜheɜZm�@+˜)��z�����h��$����)���,��%�)=��7���͜)��Zm�he0���Μ�ꢊ�x���z���d>�ϜE����ќ�&���%nҜ�&��)����Ӝ)Ԝ���^v՜��Zme��&9��S�[���&r�_���h��ݘ�w֜�bO^vop
��ל�&$�#��&�@+��bDd�@+倃b�:�X{��@+>�i��FP�eܜ�&��������D$�Dޜ�b�f���h��D
{��J����Df�2�`8=�I�o�¢$���2㜃b��&�f_�;��D�Zm蜃b�&|�)�`8���f�&D�f��D�`8��D�f��B?��'(�B?�&D�ֈ�����D�m1�0�����&��`t��i�X$�g��f���	;��f��&Dc����&Äiw��̣��&D��5@���&�5u�2S�`���Q�8�UvI
�	��1������i`����&D7�p��k��&fו �`�&DȀ`8o{�A��i=��f�ne�D�� ��b	�DJ��-
�Cm<ۯ��b[�� ��c~
�a~�f� ��|f<`8� �D�g���� &��D&��5ur�+I�ɁQ2�B?5um��=D4�&�B� �Vd��Cmt��dd�\�$�a~��� ��B��,��%�iVd�&D���)�iml�&D��W�&�`^m��2��<��,�&D�
$gl� X�wU�&D{�f�b:$g݁Qm �)�$�$g�i���$�5u遆$��Y!�2H�ݻ5uه=�"�`#�k$^m%�&T���i^	�c&�f'� h��$g�
�+(� 5o���.���$)�(nq��O���:d� ����#��>Ҁf*�Cmy�=D+�a~��x�}m� ���bڋ��,�Cm��"g:�b-�Cm.�a~�N%/�%���$��	o.��N$gU��?$�g-i��$t��d��m�b2�}W�f0�2�mw1�-2�i!3�Id4�i�>?���d5�&�����$6�;|7��$8�&,�/$g9�b:mwq�R;�b���d<Vd=�>�f�	o�?�b��P@�f� ��"gA DĄf9��$)���gc*B��$�k�\mĀ��C��$D�f��iE D������JsF�-gG�h���a���d���H�hI�f8J�;|8�� K Dm��i_�Z䂂�$L DM�hNmwg
)O DM��i�a)g櫀ɁU$O�Xv5��P DQh���bb�R D/mw��bS� |�b�=���5�{T�=�U��zdV�hW DX�	oY� |='�����d��=�Z�hv�o״�D�[�b�́\�z���)�g$X��]GmՀW����d�zd�^+Dܒ�_�Id`�;|����a�xb�Idc�;|d D�́e D��hfOde���� |g D��)� D��zo�ɴ�r�h D���Y�����
 i D`�|���d�+D��+���$j�zt��k�zl Dv��Ym�zn D� |K��+o�z�
�h�+D���p��+q�zr� O�hs�it�zJ�h_&g+�-g�UBu�hv�c�6Tw�zx�c���d��2m�,�}?�F����/�zy����W�k��}z�c{�t:��_���v�̣*5@|�譫�A�
)}�hb�{~� �h�+D��c��=���c��zg+D�Gm�����T��qo�x״%_�Gm��h�Gm���b��"Ɖ磊
oD������b������xj+D�� �+D�ݬ�i��,�Md��E�J��_��z���b�� *�Bd�MdXm#p��$p�}���$�Md���d�e��^�]�v�
o�ɀ��2��	��h�H�� ��c�f��#n�����Bd�}��o=���Md���㘝�b��d�v���z9������,�MdJ��v5,��?D��cʁ)g�� �ƞ�#n�}j�m#��$��,_��kL�dԊ?D�ց��Š\v�<u5�8�Md+$|�� �
I+��)g�$|�Bd��?D�X$���&{�o=����$|���b*dw���+�蜦�#n���)��d�$|�\v��d��7�7:�$|��`=s諝�xO�o��[d���+�$|�����#��b��$DT״_��v!��Y�dw��ˢ�� i��<��$D��@d^$|D�����Md��i�\v��$D�i8���(�^���$D�)g��?D��Y���:i8�$|�ee�i8��#nd�v��ીɁ��Ea���Jm�����$D5�ݽ�[d�i8u��x��j��V
���a��N%p���?D�j�m#ց����e�t�j�m#�����!n���<����|�i8��/D
L��$|o��xt��,��D��|�$|���i�����ƣi8o��
��#�\v�$|�\v�6���&�$|D��+jf�CJȝ$D��/Dɝoރ���ۯ	�v��8ʝ$D�t��,˝�xEv��}����"D��`.���耦�m��x��!n#�Jmj��<ۅ/D��ʴ����@d��7���b�3��$D��bН!nV
���i8o��z��<�	�&�"D~��ݣi8����aӝ�z��`�i8>����"|gee>��(��"|�ۯQ���"Dם�b��"|��b.�>!
�|*ʒ/|5���"Dڝ�b��b��b6��dd��*�+R�!n��ʴ�|�4���!nޝ}��Oߝi6��b�Y��"D�/Dp�jt��,LX��!n��$��b坚b�!n�!n�"|��.&�IЅ��=)�{�8�`������"|�)ȀÒ��al�.�"|O���l-g�&ƈ�eړ�Cv�"D�����f����j*"D���'Wۯ��"|��o�X9흩c�{�����"D��h큩cJۯ��'�"Db��h�)���h�h=��w�{����d����큩c���da�-�c��"|���'���n���2�Fh���d��"|��a���h���h��zm�iՏ�i��z5��=�⻣�z���`��[��-g���]��-�o���`��>!�Zm8��cA�z�����Y�o���|��tq߂�?���\o�i�����z��>�-g��/���d��${�F�VeG"�Zm��h)v�!����-����iȀ���`�z)DQ:��Zm	��h*�p�
)D��d�J��h
� ߁	 �{�)D��c� }���ݙ�u{3��������c)D�EEe��)D��cS�&5����	:�)D� ��=�)D̸�)|9o��-Z�%o��f�m8��	���)D��m�8�)D�z{���-d�	��8�z�)|���d f!� X�m8")D� �)|#�z$�m8ʎ�%��O��&�m8'�)|(�z��z�� )��?��x*���e�*��d�&|+���m8,�)|r�޴d��@���d-&|��&��TA.�x.�Y/�b0�x�)DĂ)|%nb	)D:&|3�&��1)D⁚���ੁ��2)D3� 4m8��&D5)D6�&D	�x�N%��&D���d��&7� 8)D9&|:� ���;)D<�&D��'%��|=)D>�h���db�dF�&D�&|<g
���l�f���?�h��,Հ�=I�j�cL
��@�&DA�c��*�A��aB�hC�'D�&DE�cF�&D����)|����8��`���O�h�	� 
�xG�'ʁ&DH�c~B�-g8��I�&DJ�CmȀ��i�c�$m����c9�:�ރ��K�CmL��`��h�� Q��`��M�hN�&Drg�';A�CmO��P�)Q�<m��`���$R�CmS�)���$T�YdU��>V�)W�&��V1X�)����ch��`Y�&D�v����`Z�Yd��CmÃ)=�x}[�*n\�&e�f�]��`^�,_�&D��d`�)����a�)xb^m�':c�&d�&De�c��'��?�f��z"�Kdg��kh^m��da��'i�&j�}�eЎ)W�)k�(nl�ho�<Ȁm�cl�}�|n�&o�Cmp�,q�xr�&ە�`s�)�CmY��"�/���t��$e�Cmu�xv��h���e�w-|x�c~y��zz-|{�Cm|�}t�"9\6���,/��`z��$}��X~-|��`��&Ã)��Cm��s����$��Cm���$�-|t)Li���&��	o�o��&�-|��Yd��)��-Dʁ	o�-|�)��-D��IdD�	o|��nj�&�mwQ��񍞃b�-|��&��Id�����	��,�-D�����-De����,'���-|�{���E����l�.n��{wy-|� D���>:�}��-D�	)͚.n��	o��bY�}�Ca���d�@+���b��[�.n� DX���>!k��x�ze�
)���x��٥.n���ԣ-|�
)��-D� Dk}.��b"��,�-| D��+_��4-D�)�-|��L��:�-|���l� D��&���ԭ-|ao��x�.n�-|h�}6���P��
)���z�	o� D�
)�Gm
k�?�� |�Gm�Um�9D��-D�+Dq)�9D�Um+D�
)zGm�.n����߇�xUm��-D�
)��}�+D���+D�Gm��'�������9DF�}?��%�9D�+D�Gm<��,���x�.n��`�x�o��x5��.n���x_�?��&� Dj��,�
)Ȟ�x�
)ʞ+|�	�>� D	]dOd��x"��x̞f8�x-��ݴ
)�@z�7��xQ����}f��a͞}�
){��	{Ϟ+|���xО�'ў��+�a�+DӞ�,�x)��|���

2���Hb#՞}�)��`U��a{�מ)�)��+|�{�؞)����ٞ�$��)��)�ց���&�Gm�+D�o����+D�����aS�)���b�[Umy+D$��b\��$�c
��xk�)��)���xR��$�)�ى��&�q���)�9D�Od䞅��"|����+|w7���|���51��b�
ol��x鞭,�Md�|���x�)
o<��,���dp��a	���Q��,잜d힘bb�i��i!DI+*	 4
o��˒�)g>��a��)��~��-�Cm�
oj���}�Y��������֥�����d��b��j�v_���|�	ckb��|*�iAm�Mdb��ϋCm����`8�O��f���d��b��>�/�2D�$|���f��Cm��B�2D� 3k���)��b���I�2D[��&$|���d��b}# 3'�Cm2D��,}��`v�3��냰�������H}���$���u$|2Dz��b(\v��O�	$|��
�)�2D��[��Jm���
2D���|<״$|�-}.�����V
�x�)�JmMd2D$|�����|�2D�&*8��Jm!d;耯~�����x��CmS�&��b��`�$gW-��)9R���`�ۊ�)g���W-�߁=�U�2D��W-�&߁�2D�&�xc��2D�״�x�a2D }z�&!2D���`"�x#}$2D����%�w}&�/D���'�<��a(�/DQW�)$|*2D+2D�
�&$|B},2D[j�-}���/Dy2D냭S./|Ă�>/�i_��z��z�R��/D0�Xm1��`2�/D�ۯ�V 	�=D��ci�/D3�)z�&4�c5�/D6�b��7�h8�b�X9�;)�"gZ��m9�bS�&��b�é:�?5�<��z�&5�;/|<�b�/D�	״=�)>��S�&�o?��&��@��k�U�z�&A�b���|"|z�&B�'b��ݾ-gC�h
aD�b/|Ex*F��k�,��'G�8H�/Dc�cI�b�׸ތ/DQV�J�b��݁�&.��ݨ� K-gL��b������ĂL9m�'�hM��kN�/Dt�/D�����)���.O�/DP��k{ۦ�c*Q��k���-'��>���Ȁ�P���{Q��S�/
�'fۯ��bЁ��3��2-gg�hR�bS�c��q��T�'A-g��ؚ�cU)	����n���y��ܾW��f�g�`m�,V��zWNmX��zY�bZNm[[�\��&���|g|~���i�f#)����]��z^�_\m��"gG9�	)`Nm����a��hbNm��"�L9��?����\m�&��y���97(��cNmގ�zd�����(	��e��zy��hf\m��8g\m���h�Eh	5n��ޅ�z~h)���h\)i\mj�,\�z~�HdkNml�,m6|n��o��?� g���y��h׀�a��zp)D��LJ���6dq)D���W��{r����is)DЂy�t��hw���u)D1��L����
�v)D�ޣwNmxNmy)Dz\m�M+{��&U��a|�`)D})D~��ze���\m�����-����?�E��\m�)D���&�W���ay��h"�}�)D�Nm��}���&���&�Nm��)|(�}8�#��)D���h��)|�)D��
�ޣ�Kd��d�z~�)D5%��ߓ��h��)|���ݖ��&��hea{�j��<Q�θ��<��b�{��)|犣�g�}哾����&���-f�n������&93Ü)D��)|��)|�)D��-�5@��0y)D��F���j#�-��X�)D��Bd���A���.�`�)D�XU�	{��&)��[��b��b�)Du���	'�����Jm�)D���~ݬHm���&m�Ы�䁮�)|�;g���&���щ�xt�>��f�}ÄBd#�}���M��)|���&�}���<�?��~�s�t{�d�����&m��h�聴�	g�����<���<�gkk�U$R{����'����$���_}��=�z���L�|*v��'ʈ�a�����z���c��'��'�yȅ�X	{��`D��'���,���c���c��/���'w��k{��
)���h��q�5z;g��mv�'���ם�%���h���~~L����8n�~~���$��h�~~=	{���u!�����$��&�Vd�ޣş�c��&.���~~܂4Z=�e�ȟ&�ڏ�ݑ$gm��#����ze�~~9��,ʟ�c��\��
�~~��$��{K�~~ȁ���)�~~���8�a�)a���Vde�8ϟ�ad�6�5ZҀ����&��&G�/��̫��h,�ze�i8F�e��z��q��~~����gk�-|�;D��iF�*�;Dl�):�gk�e}�X^0�5@؟�h…Eٟ�c�;Dl��h��?ڟ�,۟�`�-|��xNݟ�,ޟ�Rߟ�c�c�$g�-|�~~�A%����~~�Ev�-D�~~�����&�;|�-D�	{��kꋲ�.�?Ł�<B<�&�-|�-D��?�-D��?��$�#�`#�;|��k�bp<nr��j�-|�;|�;D�-D��$�<n�}�-D���o��|� D�}<�k���݅��@r	Ca��V8C� D�-D�8V����}�%D��=�)�)$��b��.FAm�<n��-D��  D�f8 )y-|9�w� )�"|t} �Nc D҆X� <n } )	�"|
 }_�x }D"D -|Q5�
 -| ;D �h
��� -|�-D	} ) )Dآ�"|  D  D��i8��| Um��<Q) 9Dh�Am�"| 9D ).��| 9D8�  �c )�y��-D���<҆Y�"| UmO��`��i 9D �;|!�-D��M+�"|9D" �c#��b$ 9D% �c&��>��c��8� � D' <n( Z��c)��$* Um��>}d�xV<n+ 9D��%,  D��V�- 9D.  DLu~��d/  Ds�?0 9D1 )Q��2 9D�)=ŏ�ؒ3 9D؇9|eK�8�f84 �c��ʀ���|5�"|6��`�)w)�����B)�}#Ȁ��)+��<i9D��߄���O��7 Ume�a%��h=�f�p)���8 )�Hv���|()��c��"|��9 )�}m��3�): 9D;�}�� < Um�9D	;= )h��|> 9D? �c@�}�UmA 9D���0�Hv{B��-C �cD 9D�� E 9DF �cG�}[ؗ�Hv�� ȃ[Q��C)��E���c9��-e�a%��k��}u
֣��>H �bI��,��YJ�%�e�a%K��`L�%M��,l	{{��$�)N �b�)_��O )���P�}_��Կ�+Q��,�)h��?Ł}#Q�?D��t��+E�CmR�?DS�[dT�)gU��kV )W �-��bX�m8Y�?Du�b��?Z�[dE
�B��Z[��k\��bJ�m8]�?DY�,���|^�?D_ �ܮ�d`�)ga�jb�?Dc�j�d�}e�?Df��'g @ah��?i 2D�)a����'H��hj 2D�?D��)g���k $|�?D���\l��?d�z�m��b��}p��b���xn�}l�[do�?D���p �iy�`���xq�Xmr 2Dp�&Ds�@dt 2D�Xmu��b���<T��+A���v У�	�����S�@dw 2Dx ����j9�����bb��|���y�Xmz�){�@d|�Xm�$D?	��[�@d
�)g%���z]���?} �&ܚXm~�@d�?D�)�d{�&D��Cm��?D��?D� �`��[d��}��@dd��b��Cm���?U�?D��Cm� =|��8� �`��?D���x���b� 2D���b��?D��}���b��)S�$D��}��	}e��i���b>2D��	}���x� �&����o2D� ޘ �m�=D%���	}	&��a� 2D��&� ��Am� 2D�
�	� {�� $|��	}��@dD��x�|L�=D�/џ�@d��`��@d��䀿,��	}��Xm�'�� F���3��}8�xڥ�@d��V ��Xm��=D���x�ۉ��@dd�7穠Xmܚ)L3ê�@d���y�Xm��}���.9�=D�Up��=D�����8�@d.�&��f�-)�������.(�{���k��ݰ �&��=D��5���j� �&� �`�� ���jx�+J�	У[	{� -g���jS�Hm��-���j��	}� �-���,Um���b����$l	{���b�����=D�k� �$S-gc�����x_��X� i#�_d� -g{�����
�� i#?�p���	}
������!��% 	}àCvĠ�kk��A� -gƠ�bBUm� �bȠCv��$}oUm,���ɠCvH�bʠ�j�
�$� -g���	�$�i#̠Cv����8��{͠Cvn�R�
gˬ�$� �bϠCv�W1�cРCv� |~Ҡ;g��^� \m{��j��b��;gԠCv�\mU��bՠ�� Z9נCv�<� g� \m� ��۠�j������.ܠ�jH��.� |~��Cv���~��`�z�;��<w�PҠ�Cvޠ�`��(��b� Dd� |~� \m�i#� g���㠪j�oD?�� �`\m� |~���b�
�h� �b]��`�~���Cv� Gm� �����?� �$�Cv��-�
�-� \m� i#���D{�� i#�Cv� \m� �k�Cv� �+� �����Cv� 
}��� �b� 
}� )D� �ݮ��i��Cv뀄?�)D� 
}��Cv���`O�����Cv�|� 
}׀U7_��>d�!\mk���~�.�.�����`Od�AvX��
鴟��~!)Dp��諀�B!\mǑ���	!\mУ�Av!)DJ�ӣ�����
)D!+D!
}v�x3�hЂZ$	!VB��k
!\m!)D}�H[�]��`&'@+DF�!)D���pt�~
!�{�z� ���at�|j��`!&'�+|�
����!�b!&'���za�!u��h>�)|����!�c�E"i!)D(����Avk�h�|$m�!)D>{��U��)|ЁL%�����[���h3�h�)D7�o���m�o!
}!)D�-!!)D.�����!�b!)DY
}.����bZУ�{� !�h��Jm��^9!�Av"!+D���#�Avt��_��Bd$��b����)&'r��+%!�~&�!u� o�ie�S�������Bd'�Jm�^9(����먁Bd���<o{���&�����$�	))!�h��bn�~"�^9*��b(&'����-{�+�Bd�	)�[�0��j$
�y:w>{���^9��y��b,!C���&� �	��-��$o{�&'3	)��Fk{�'
,i�m#	�c��<��c�Hme�&�$g�W��fɚ�y��&���8��JmZm|��r���5��#��&���$��Jmk�Xm.!�鋀�b��q�|�|��&j�f���bm,\� 9���&����/�^9��$���i{O�0�Bd1!�$��2!�&3!5�4���(��h��5l�&,�&��h5!�&9��6!�k�Hz����,�� ׀�QS� .��-ʃ֣>��h��7!�&8!u:��&l�?9!4Z:!�x���h��h���8��{;!�a��x���h��k����
�x���Ё��}�-|�	4b��c~<��h=!��>�E�[�?!-|�}k���@!}A��$H[�B!�&s��$��{C�m#���'�)���qFPjD!-|����E!�&��%4���w��F!�&G!-|H�-DI�;|J!}���5��dK��b�&1}L�-DM!�wN��bO!}��hP�;|Q!�`s��Q�2�R��b�{��!
�-D��-S!}�%T!�dU!��V!-|W!}X�-DY��bZ�;|[!Nm\��V]��b�|^!\m_��b`!Fd�"D��b5�;|a�/Db!Fdc!�bu�(d!�b<��e��bc�tIf��bg�-D��&h!�`�-Di!�b�Amj�-Dk�f8�|l!"D����p�	�m�"|n!�x8
-D}s�bo!"D�Fd��`p!-|p5@q!�`r!Fd�"D��?s!}t!\mu!�bv!Nm�}w�-Dl�b��x!}�{�y!�bz�-D{!��|�"|}��bh�8I�-D��~�����i6>�"|ۄU�!�b��i8��b��-D��V�!�I+�bod��5G��-Db��b��-D$�0無Id��-D��;|�!�`��"|���b����ѐ-D3���I��b��cej�+'�!�{�!Fd����h��h�!Fd��A)��S�"|���h�!�b�̸���c�!����b%�ܒ��'���c���c߁�&�!�b�y�Fd���h�F�!Fd�
"Ds��c���'��|߁
)���&�!�b�!�b�|΂�h��4���h�����"|��b/�ce�㑶�	�� ���&o�_����!
2�����c4��c.�)��s��~e�!)�ٞ�G"���&h򭠡�d ��&���'��_R��t��i�'݀(u�����Hv��G"y)I����բ�(uk g�(u_[��!�{t��%c�(u5�+,�S��-���h���h��i��ע�+'���$����*��c�q���*
��������+�Md���-�(u��ce�!Md�{��� ���h�����Wx��(u%��h��ce��G"�!�`��B3�H���{7�oÄ�c΂�,��ce�!�b���c@�m8��f��ȕ�
:�*z)��!g9z9V��갡?D����"Em��?D��b���k�EmȀ���
���-<�b��p��?D��!g
&�����k��x��?D	�?DT�bB�-����!~e#�x��I��HveNv+�?D��Hv��(u�!}��(ut��_�UK�!}t
�f
����X���&��!g�!�-���l�!}Q2D�!�`O贡�`¡�&���G��`�!}_�f���9ġ?D�����iš�c�!`#�!}�f�!�&ɡ&D%���o{��}�}К��X�`���9;Zmʡ�`�{�ˡ�z���̡�`�����!}<�-p��`�!Nv�!Md�!}ѡ?D���z�!U2�!}ԡ&Dա?D��j֡&D���`�*!o{�סfӤ�)go�?Dء�z��$١�`ҍ��b�<ڡ�zۡ!g�!�hĂ&Dݡ�k��9ޡ�`ߡŠ�?D�{D�&D
��k,�&D8�f�?D�{����!}쁉k�{��!�.|Rɼ��}�F��!Nv�!�hh���2D�zO�Ea�!}��`�Kd�}�!Nv�!�xNv��P[�J/'�!}>{�	��fښ2D�<���`�!}��YdQ��`
>{���zԀ�o�S���9��+�����`���o{���`�!�f��+m[��z��`�!'Aŋ�`{���Kde��z���*��v���Kd�|��Ydt�<��0'���z���Kdt
�j��&D���-h��HZmt�i��<�$�) (�0'
���+��0'���b;��ȫ��.��bt��p"�c"�h"�`{�h�h)�0'��b"�c�$!���-o?�h-g�{����YO��0'���b��&��-y�Hm	�E�'��#
�0'�KdQ��b�-g��b
�kh-g+��-
��`��`��b�)"�b"�k��Kdh��|�����>"�b��b�Kd"�bP[��i#��k"�b8[�"�h"�$���o�ӣ��b	Zm�,垈b�-g>�-�mq��݀Cv��Q��_r����,"�b�-g��;g_�Q�G	f"�?j�b�0'��i����9�>�bw�j� ��z!"�k"��z'?�#"Dd��ce���b�d�$"�b�@+��(%��k�UQ{̰B��-&�$!��b'�0'��?�("�b��f(���)��ze�ȶ*"�z9�;;Zm��-+"�h,"�kp��-��h.��,5�]�/��z�����-g0��b�b���h-g1"Um2"�$�%g�����>3"Um4"i#5"�ko�eUm6"�b7��h*���8"�k9"�`5���b���x�݀;geDda�}<���L��:��.�x���bb^c�Odd;"�`<�}
��%="�zgOdT�P�k��h��`.��h��.��8���>"Od?��z@���D]dA��xt�i��fC[�B��xC��zt9DD"��_���E��z
��F��x{�m�fG�	}H��xI"�`��	}J"OdQ��=Od	�f���K��x��z*��hL"[�M�	}�:�[��9DL$N�	}b�;'dd4'�-��cO"Od���X���x�	}��`샻xւ�-׀���q�4'P��hR����9Dm4'Q�	}r�
=u�&R��x��{�j�r���f��iS��-)�`T"�`U�C�V�}|��(�9|��
�1[�W�ԢX�	}���+�I"�x�X���Y"OdQ�XmZ"Od�2["9D\��c]��c^�(g_�Xm���'�``��b��n.��ɸ�fa��b"[w��c[QҖ	gc��x�jd��xZ��%e�}X9D!��bf��c!���	}g"�jʐ����譡Zm�j���+a�1z�M����h��cȒ��i�	}.�;'�Q��)!(��jj�	}���e�!	 ��%��ꇉE9��٪�}k��b�)!l"�im�}@���n��br�Cmo��cY)!���p�Cml��$m4'Հ��)���W~��q��,��$���$������&�h`8r")s"�j����i��$t")u"�hv"�������(g��J�w"�jj�%&��&��Xm���fx"ney"z"�j{"Vd|�Xm�)}"Am~�(ge)�����$��b��Xm�"Vd��(g�")���b�����}]Vd4ne�����/���	 �")�)!B��cb`8�")5�<o�	������b��$�`8���c��a���>�")��7:���⎀�>���>�"Vdt�㭘[��"u:e�}΁��Y�f��i���c���>�z%�"Vd4!��	��"�z$�q����,��$S
�i�"
}���>Ё�W�"�#�")8�f��a���%Zm�z%�"���>�"
} ��/b��kw��>��f��?�����5;
}���>�"�x���$�")�"j���=<
}��H��o����i#�)������+��Id��o�������Id�"Vd�")�?����2��Id��=D��;|"�o�"Vd�"���i+��b�"VdCJ��i��Am�"Vd���>V�F����>���p>-����o�"}���>�"}�����ZmR�/DZ�\m�"�h��{�b�"}�I"�"\m�"�b��g˖�&O�>a���`\�b�"�b���. �bw���d}���Ԝ�b���=�w�"\m���$�"}0������*�+u�Xv���`�"�h;�/_�֨��"
}���`��x�"i#J��J�"�b��2{�"
}���i�"
}t��t���"�b�"
}	}Ào�oŢ�`�"�� 
}���H��oՀ5�Ǣ�`k�&�"�b���`��&F��iQ�γ�&�u�r��`ɢ�`�	)�F9߂;|ʢId�;|ˢ�W���'��<�"�h���"	)΢�bp�٪���YϢAm���b�F��� ���oТekZ�-{�{��yKѢ�$&���Ł�$���z�"��	}遝$p�i%��$�"�h�ƗY�-!:g�Ԣ�`բ#���eگ��$�Zm9���"�bע�`آ�,�"}���$�����"�b!�#��b$D5��i��iۢ˸t�,	
;�zNܢ�`��W��,��ݢ�z$����"�,i��$`�v�ߢF��"�c��f���iR��io��c	_��$�"�&9�Ҡ��|e�9������$��ie��i
�c������?�����<w�֙ބ�S�D	�Xi��$j��)��� ���+�	)V
�&A�^9�"�c� UJ�[��f��<�9'wFm�f�"Zm	�5��"Em5�#�^9\�"Md,��$�ô�[�)�^9�"Md��݂��$��d���1��[���$Ȁ�Md��`8��^9W��$"Em}c/u�Md��#�"Em遝$,Em9v*���%���h��c5/uЁ�%�"Em�t��"�&�?D��c} ��h.�S-�7FP�"���"F=��EmJ�G�"Emo��ht�,�"�U6��p��ց�<�"\v^	�-�"Mdm��x�'�"�d)��9���O��?D€��"��o�?�"\v��f]��e��m#���j��iEm�)*�9	��^ne�"\v��C��"\v�X"�	���x��yȃ{�ҴQ7:�\v#X"$od�`���^9�i��w�I9#�`0��#���(,[��x~#�?	��z�������
��x���#�`#MdAne
#\v聢x�n���Md#��]Em�� ��(���<�ǣ��b}�'�='��?D,����-#�`v��J���*�k��m##�-��X��?D��[d9��<��f���o�� h�`��h�?D=����?D�m##�t���<��f#��#�`��9S�m#�)���:i	�`⁥�o��f#�? ��x��)�7��`�Yd��h��?�����&�[�uҴ/��c\v��-#X"��h�YdQ�� ��xm��h�"'!#���c�X""�)�Yd#��,_��8��&$�	}%�ꖵuR���5��À� &�F+�Cd'#�k��Yd���c5����(�����h�P��Yd&
�j�c)#�c*#�k�Jm+��-& ,#�k-�	}B��-.�Yd/#�k��.��߅�ah�Zv��"'�ށ��٫�3���
��-0�Zv��kʁi
�-1��.杈b��:u2#�k3#�-Ai84#�>5��h��6#)7�Zv�8��9R�:u9#�k:��&�h~i�)���9��fJ�ZvI��-���&���5u�2;�Yd�Zv<��&=�)�]�>#�b?�E��_d��@��-��d����A��B#�)��9��C#i8D#��)E�	}��c��z)�yi��c[����h׀�7��cy�;gF#i8G��c�H��cu�bI#Z���mJ��c�<���j€���i�
�f���jK#�k��xL��c�i8���j
�(��x�<�M�ZvN��jv�D�x��xO�Zv�cy�kP��cQ#�x{�]d��j5R�� ��G�S#g����Y�fk��j��J��/T�I9�������۩��U��c�i8��j��	�z3��H��?���c���z�)7�xV��?W#�N.��c��5`�
��X#8Y��?m�ft�x�)�{	�XZ#�b����$i8[��j;��(v�=�_d{��~��#l�${�)��*�bX9��%�f#\#�y]#�n^#�b.
},
�h_#]d�w�`#]d���m�
}h�`�F�a#]d샣j��h�
}������.��fb#]dc��?d��j瀩ce#�hf#]dg#9Dh�!g܄�`���N���f��>x!i#]d 9Dj�"u��vk#�x��V�l��>m#9Dn�)uo��`p#�hq��`D�,r#]ds#�x�]d�Ft��` �}�-u#Dd�����`v��-]dfio}w��`�-x��`ٖC�y#]dz#
}��X7�{#^v*��?|�Em}#]d
�~��ސ��-�f#��?]]d���>:��`.)���?T��:aJ��>&oĉ�f���>?��#�h���m��n��݄����t�7i����f#��	�#]d�ͣ�9Dp���#]d���c�#��e)�	xV�#
}v^v���_y�h��z�����	ٍ#]d.
}������`�#MS��c]]d�#]d���c�#�hy��`�#�<���c���b�#�h�Jm��ܖ��c�Zm���b��e�#�,�#�c���>������c@��`���-��`�#�c�fg�)��)u�#�c���-m��ȟE9�+D���M�fN��m���c��)4�c�@9Ă�,�#�,�i�$'���#�cD�,���k���bJ�m8+�)>o��k�h�m8ń�.D�������j'��Y9����m8.��[����cv� �*�����z�������ͣ��a,|e`8��j�ی���b��m8�3G�#`8��bQ��v� 諣)�|e�#�$��>~�܈��b��
٭��b���������˶��2{t`8o��h���b��e���k��f�ޣ��c�Iu`8���'���c�`8Y�����|�>�#�c_4��:��'ò�)�#�ce��m��&�#"D&�,Ą�h�)�����&�'�W�c���#�c��k��)��c~��c��i���/�c~���&��j����#-ue-u3��&��)�������&��E���h��=L��hЂ�'�"-u�/��c~i��$;���#`8��"|��_�#`8a��£�k(���p���ã"|:��:��&뀸�ģa~?�,��"�ţAm	-uV��hƣ}ǣ�xȣn�ɣAm�#`8�	}�#"ḌAm�#�d�
����vV����h$$:�����Σ�bϣ}��a~У}Q[�e�ٸ�Am���`8ѣ�&ң=DӣAmm�ԣ�&�}գAm֣�'ף�nj�Kd���h���@�#�S��&�#�bڣAm����|ݐ�&pĢۣAmܣ)4�"|ݣAm�`m�ޣAm5�"|J��\ߣ}��s�	fm��} �"|k^m�Am����#�bm�f�fr�:gH�ݎ���#�e�#Ģ-u�#�W���j裃Q�#-u꣪jf/�q�룪j�#�b�P�*��x�6(�}�~1,�f��n�Am����Am珥�~1t�|��Ame������g~Y��J����k�}���b��$
�:g�}�a~e��jb�k�Am�j��}e�g���}��tm�AmA
���c��#�kp��b9�V�W�f�#�bƁ֜����#�k��e���j�#7�#;!�c���X�<���#����j��,$C�����k$g�$����$�2��$�b���ȃ�Ģ2��:g׀
������,�:gt�,>��Q��	$�,9��c
��j]�c�_や�� D��,���a�Q��F+�F�$�c
$�,$)!�
�-j�c$�`���$�cʋ���j$�c$�k��6ͦ��j�� D��,5Cd$)��&D$G-$�k$�c��ik$�k�	 *�,Ɓ7����z�I9$�c��:a$�k(s$o@z��$�k$�,��z���$�q�p�&D.��b�,9zt��x $Em�!$Em"$=u#$o$��>%�&DEm=u�
�)&$Md����
	�`b�(�=���'��,)���($Md;��c��)$�b�/*$�cu��]+�&D�Ģ���*�xy)#�c,�[dzD��`w׀��-$Em� ��ՙb���.$�t�,/$�c���⫀�00$Em���x1$�h2$�,3$Em4$&5$Ģ�,N�c���j6$�?.�O9�Emm�-*{}�Ut����
`7$}�Ģ8$Em9$�?.���:$\v�f�fq4�4�
};$�?<$�h0�t3�}@�&D=��y>$}�
�j�??$w�����x@$}A$�hQ}�{R�"\v�oB$�'y��3C$Em��+p�&D9EmD$�?E$�`F$Em��hG$}��c�YH$=u��xI��zJ��z	�?K$UTL$Eme=uM$Md����� �N$MdO��z�
2tzee=u�}���eN'�P��z��j� �&!�>��b�-����Q$��u����zKւ�`u&�	�-R��z��$�-p�	O	)B��g�
}��-��մS���T�[d��:U�BdV��z��?W$Aȃ��X$}��8��G<Y$�?���j耕���I9}}Z�}[$F�:�?\��i�&��z]$}^$�?��m_$\v`$�?������&��|��&b}�H#��G9a$�-�
VB�G9��eɚfP�{�9{~9��b�������
Ģc��z��'/�G9d$$D��-!e��xF�Bd	�f���H�g5g�G9ȕ�z���h�=i$C�p��c��ew�vfj��zo�ִ��F+�����k��i:�Jl��z��G9$�&m�on��z>�-�I9ÄG9w�T���e��7����ao�op�Hm�����<����h��i��)'h�>q�Hmr$�>�zes$��i����>*�&Y�?!xĢt$)u$�h���ݨ��o�o��ˀr�Àv$�hՠմWm�(�i8w��-�Z9kHmx�Hm���i8Q��y$)z�HmFĢ��z{�Hm|$�$������}�Hm~$@9��e$@9Ev�����ʀ�Hm�$j��Wm�$j9��̓$i8�$)杈b�$��t@9�Wmh���%�+|���ch�Hm����]��c�$@9�$i8J�����9(��$�kK�մ��9�$�`��⌤�c��3i�&� >�{���Qi8���c�׾�ִ�\m���c��Hm��HmQ5A���"Fdo��<���9�$$u��oG\m`�o��$)��oe��9�"DR�T;���.���:���YFd�i���Hm[�tI)i8���,�b�"D���������#"��z���<�$\m%�z�$$u��6l�$�_������9�$�`l��ݜ$)�� \m���c���$"D���O�|e�X�b�b�$�$�s-�$$u�$s-��?�$"D�$$u	�f�<���'�%�i8~Ǜ�?�e��9��"|��I�X9��z�[�V�U���9祤BmX�[�P�Y��c��Bm���|���h[�cŋ�i���h��c��c;�`���9Ȁԁ
��h>���h]��c#�UT���-g��>z\m���>e�"|	�$}��+|m[�"D�$\m���h��}���>j�7���	}*�
)�$X9#Z䳤�>�j*���>���$$u�x*�$\m���>;�cj��h�$�z롧>LF����-���OΎ�?�������-߁}i	}����j�����z���>���h���h�C���H�w�����͢��Ծ�}��׀Pҿ��>�D95�礁
�P�&!���>�����	}���-¤X9=��nä}Ĥ}����ŤZmʃ�nbw�$od��^��"|(�P?$op�1ak��c�
�Յ�>�?��K+Ǥ�>
�}�$���c�G� ɤ�>Q�Cʤ	}��Zmˤ�hB�c���̤�>ͤ}5��Τ�>Ƈ�c5Uv��D9ϤZm��	}�Ƙ�T�U�cX:dФ�-�$�,Ҥ}��>�$�cԤ�cդ�>֤�h�$�,ؤ�-S�S�٤}ڤ)�Zm�$�,��c)��ܤ	}�$�cޤm8ߤ}ढc�$�cH��
y�m8Y�u#�m8�����$�cb��|�m8�$�c��2'���$���o�(��)��	�,��zjoz�m8�?��)�$�c�$�-�$)@���xm�kv{�Ԁ�[��9��n�)�$�c��/��>Q�L)���$�-d��k�$)�$Fm����$�k�$)�$�-�$Fm�)��Uଋ���n�Fm�$)�$Fm�$�kj�J�$Fm�m8���a���$�$)�$�c��kd�ZmL
X�c�c�/��k)?a��i8"۴��2'��&D�$Fm�	£@����&X��$Fm�)�$Fme�&D:���-�-�FmQ|���m8x�c"��n>�|��)��h�$�c%Fm�)��q���9����h<��n�&D�a؁m8��m�)�!FmJ�p�m�h�&D$��%
}M�&D%)�a��$��&D	%���a;�!g
%�h%�|
}%)��hG��`��o
%�h��al
}_�%)U��a%Fm%�%�-%�h%)�)u����%C�%)
1v2�%�-{
}��`%�&%��%���&D�Fm%�k&|
}AFm�o�Kd��%�k��a�`#�����WO���` �o��)u��Kd!%}S�"u�)u���	�z"%�#��@$�)um�)��&D%%�b&�f8��o*ۡ��{�h:^m��G+y}'�^m"�h(%}J�|Ȁ�)�&Do�|��c��h�}*%^m^��Y���+%�h,%�?��i-%-D(�H����.%
}/��90�o1%}��0zg�2��9��(n3��`4%
}�J���?5%�56�o7%}8��99%Ģ�}b�����OD6aΎ�?��|:%I9瀘9�I9����6XmS�k;�o<��{=��`>���?%4b@��`iI9A��9oĢB��{k6a
�)ux£��)u�I9�ҪC%I9D��9J�G+��)�)̄�{>��E%I9F%I9G��{��G+�!}e��o6ay��,H��9��I%�d⇘9J%}V
�|A��-K%)���,L%}M%6aN��k�����}�,O��,�I9�I9�|P���Q%}�£w�SpR��?/�e��9S��-T%�x�w<U�)瀘9��{d�|ݝ��V��,���+!���y��,��Fab��|��(���W%I9X��iY��9ޤ�{���Z%�xG��
[%I98[�H�����9�£ʋd�\��{2��9]%I9��Ϝ�^��9ҕC�O��&J5��I9_%�>�b`%u~a%�k��dցo�|��`.�>b��&l�}��x��xc��&d%�k���,�[���)e�}4���x�T��y�e��'��xO�`f��&g�}(���h%-u3��&i��0�����4b��8j��ka��|���	d�ȁk��$l��,����m��in��ko%�`p��x}#-uq��?+�#gr��kD�`s�#gb�`ѣԂ�im�:瀻x����t%+D��)u�Kmv%�`w�#gx��?x�Km*��'y%-u�Kmz��?��{��||�Km}��i~%&�+D�KmoJ9���?*��k��⁥�&��#g�%-u���&�k⁆��
g�Z������&9W��%$u ��k��#g9�W+�{���'��Km�%���{"�9�%�j���kւ9u>��&��Km���k��j��1a\��x�%�`���x!â�%�9�%�j���h��Km�%�{ʁ�|�������9��{���S��Km�%�=J��(�%�e_X^�%�{��)���	���Q�%�`h��ɜ%�9	�@m�%�j����fXm��)���x(���%6a�%�j�3΄G"׀fӠ%���d����?�?�T�f�H��p�%�j��Km:�Km�%�j�x~��S�6a�%�9���ܦ�Km���ko�&Ȥ���i��Km��#gρ%d�쪥�k�����a��[d���<�M9��Km��+|g��k���i%��4�-��	}�M9����}$u߁)'�%�i ���"�$X�9RƁ6���&B�-��	}�%�9��M9@����BZ��J���|l�-������c�ԣ���,�%�j*��'��t�|�"�9,��,��j���P�ܸ��M9���޷�	})��-$��Հ�Y?�M9��	}_����i��2ѹ�)�?���	}��b��N/�)'�P�q����|m��a���6a��	}W$9���n�%�(��-.�=�耾>��	}c��x��꾥�>�%��.�=�ݝ�C�6a��%6a.�=��Xmȃ[J��i�b��Ɓ�����u:��u��}6a(���׆	}d��>%`8Q�3�oɢO��(�)��|�؀��/�M9���	}�6a�i#¥	}ށ�,)۴å	}�����E�Om��+oɢyWm �|К�n���>;��<��/�Omĥ	}���|K�UBe)e�|��k�|(����a���o�Fಒg�x6�6aW��im?��}��|�%�/��zXOm�%�d��-�%�ko��o�͢U�%Om����6a�VBc�̌�h*Wm�{��Om�6a��/D۠�>ɥ�z>��$�>僾>+��>O���ʥi8�Jh˥�z�|.)���ߎ�/D*��nt�|�	Om/)��z��>��bF�+>6ḁ�`耾>o����`�%X9�h���`�%6aϥ�z�w~׉V�g����hХ�`UOmC
�@�|��h(����%Om�%�k$Wm�
�忎�[虶�%[�%��/ԥ�`�u�t��#�Vr�ˢa��|�/��%�k���`���%Om�%X9�%Om.��`�%۴:��kJ��`ڥ�`�%X9�
}!��|��f#���`���%X9ݥ/D5��`ޥ�z߁�k		)�ZdH
}a6a� ����ߥ�z��i۴���WȀ��%
}6a���k�%�h{|~��z�`�_���z��&�>g	�h�%|~l�ekL��i�z��`�	�a�%=a��$����%ۢ!Fat��jo=*Y+�|~�%DmQ��`e��|o����`R�?�%Dmw�-!��|�%
}
�y�@93��`0���2'�+�%�?($D��L������%�c�<�����{£壟k�`m�h���<g�&%�?��$��z�s�:�\�?'�J�]?t��'�%�cH�P���{��hQ7:�%�cB��k��;F�Zm��{�F9x���Zm6a��9��(?�{��Zm,��<lY+��Zm��&���;�(�%�c���<�%�&���b�%
}g�v�F9��|r����Zmob���58�%+!?��b۴��Zm�he�%�c�}�Zm��93��|&+!%�x��Zm&�-�+!��?�Zm��-�+!&+!{����)�)uj�)~���mۢ�x&F��Zm�H�i�z&�c:�?	�)=�����?�	:���|?�뻣�[
&�?�)��|:@9w����{
�)&�cg�?�jL&+!�)&�ct�n
&�c��-���&�x>��|
��[&�`���Zmp��c&�c����F9&�`�Zm���F9�Zm&�$&�`&�-�)&�k���|��=D��> �\m!�)"&�>#&�k����r��$�Zm6a%&�-�+!;ۢ	�*�&��|'&�>�`{=a6a��i�;�(&�`B�k���|)&�k��)
	�-��>Ȏ)�Bm*&�z+&�-��$�X$��z,�)c �|-&�i.�)��>6�
�-��(/&�z�����k0&5m1��Rʃ�y�k*�|2�)3&�?4&�iy�k5�)��Bm6��|ȆY��?.�)��+b�Bme��|����k6a��{7&�-m��-	��>���H�6a8&�`��{9&=a:&�>�"|;&�`l��{<&�>��>���|=&�kZ��&>��≙�h?&�>��@&�-n��&A&�`]��&B��bC&�`���WD&�>jf�E�}_�>R��|^�$�"�-A�Im��-F&�kG��&��ImH��h��hL��D�/��|��|I&~~	��{~~J��&���h��hK&^m��b��m‰��L&CCM��bN&C���iO��hP&~~���|Q�}R&^mS��&�6a�f8T��b$���U��&V�BmW���vm8S.gc��?H��d��&X&�?Y���Z&^ml����~~��?[&۴���<��b`^mc�(o���\&�?�~~<��.�Zd]�)U"T]U��h� ������?�h�|4�y���{^��h‹��_��h����&���`&6aa&~~��+���h�~~���7�����+g�����ho��hD�|v��{b�2'l��ݘ�V��iЂ�h��Imb��&튡�c����S�d&i8��<+�Im�8e��{f��z��|g��bh&^m|�� �i&^mj�8���8��iӃ8���<��+b��nk��cl&�d9��|7�%��$m&V n&�|f��nB۴�����o&~~p�8a��ư���-q��c����r��c>����-��|s��c���id��t�8����"��nu�f8Їf8{6a�j�v��k.��c�����9����zw�8	!gx&�,y��c���z6��|�)��z��x�i6��|b۴Ԃa�(a����m�az��cb۴���l{&�b|��i}��c-��|����~�&D]ң&�/�Fm�4��;�b۴h��>�&X+�b��&ܖ8a��|�X+�|���-n�{i�(g1$��c�8���-�h�&�xE��c��q���	�&MmX+ ]d>�h��F҅&Mm�&�h6a�&�b��8a���߂&DLX։&�b�Mm6a��48���`l�"u���$��b(��㌦�|l�Bd�&9D2��&D���|L�������|
�hc�:�h�&�b
�YmFm��ae�&)��Yms�ae��Ym��8a�k8��ae�&Mm�&�kX�Ym&D���|
(a����`#�(�Ym9W�o�;u�&)���^W(a��YmyMmj��`�Fm����:�&�b�w���=��幄Ym�
�����
C���+��Ym��-��Ym���`��W���S�
�s��k{���*�h����)ʃ�#��ň��4}9�����x�&}���kv��f�W�&}�&�b�>Y9Db�Ym=���{�&Mm"]dD)�&}������#9�;��&Mm�6a���`#UT���-�&Mms��|���`��{�I9�9D�&}�&}�&�b�9D��8��Ym��ael��-�|��c��Ym�����|��P���`ki�ae�&X����|�&�i���k�Ym��?Ђ�-��hԱ�kF��۲�Ym	ң���k�&)�&�c�&I9$�io�۷&I9��ݸ&�c�����R�)u�i:��x���,5�i�&}�&�-��m8��(��,o��b>�z2ܽ�&�cLңĉ�b,�z�&}c ���b��m8Y��b�&}�&�c��|�&�c{���5�i��o����&�{����&�iŦ�b��գ!�3GƦ�|�$�o�x���.��'8��|��ʀ�O?��w��bw������������&`8��|����R$iȦ�&����cz�cj�ɦ)ʦ�&˦�&�&iͦ}�8�Φ�bn���&����C�Ц�bѦ�&o��[�3���&�&$i~#�����&)�&�cʉ�c����iզ�b�&)�&�cئ}v��٦�|��)�&�cۦ�c�&�`�&�`��˸.��&���|ަ�bc�i;�֣�&�$ݐ�&�&)�&�|��i<��#Q�}�)��$�k���b�&)�|e]mY(a��&����&�`ဃb�$)N��h]m���|e)���v��<�dwF۴b$��禈b�
��]m\�}A]m�)�&�`Ҁ�x�)�&]meeϋo�(aW&�]m[�����3��x�&ee���<�&]mI�-��`J����
=a�&]m������|R�o�b�&���&$u����bj)������(a.}l�Kmџ�|�b�Ԁ�����b?�)�	�`_)��$��y�(�=D�&)��c�&)i��X"�(a�&�k���k{�`���b�&)�&�`�&�h�(aD]m4)�&�`�&�`���*���&�i�(a���i��/�&�k�&]m'[��@m'�(T]m�գ��+'�kN�k']m'�&���'�&X��`���(��'�oТ�寙)!'�&�ңl�&�Kk
�net��
DZB��k�g-	��k:�-@�b
��iw�p�'�b{�(�
�&��k�)!G��
'�b;��i��M9'�&;��i:��k���i'�bʋT�g)!)���<e��|w�����<�b׌�-�,ԂQ-7��!��%���'&��	}�r�'W�'�&'��}��|�$u�g1�}Y�_9����
�A'=a~!�S���<7o�	}��<>�_9n��g���'�b'�c��|'�c�}�:�.� DG��k����}7�_9'o���|�܋�@m��|Ё�-�	o��/��c�}l��<����	} �}!'�c�Ŵ��W1�C�D��|"�he#'�c�he7�g1$'�Fc�&%'�b&'�&���Q''�&(�he��?���|�c�X-)'�&��b9<��-nQ�8g�|��u۴*�8g��he+��|<�֣��%�b۴��c>�_95��,��<,�he9�8gI����գ-�M9.'��;��h9!/'�,��(0'���Q��)���<1�}2�	}��) ��v�p�=�s��3�	}�we4�heO�)�9!d	������j�۾��d֟5	���he5�)6�Kd7��z��)8��|�PkOf�9��|m�,�)��	�(a��h�)[�c���|)۴��,:'�co�(���O�)ߌ���(a;�)*
۴<�he	)��1���Dž(a='Dm���M���R�i8Dm>�8g?�)픘S��t���i��z��-΂8g�he��/Db#Dm@'�xA'�hd�7����w���Dm����C�c�-��}	�(DmB�)C�գD'�i����zoڌE�)F'�-�Dm"$�-&�be�:�	)G'�iH'�6��z4!g2��-I'g�J'Dm�)�(ab}t�	��	�
(aa��bK'�lL'�i���bM�)����e
}N�)[�t蘆�$O����ο�(u�(W(a��/��j���bP'�hv����	�(ai.�%BP�hdƐQ'
}��`DmQ��R'�{�(aS'�i?�G<.�)u�PkT'
}��+��&�֣���bU��b.�{c����.�h�g�V'
}W��by��.�C�X'�$J��)h'�ȩ���#��Y'<g߂fPZ'
}��&p
}�۾��1���|�	Σ�_v���[�)u	}��&ȗ��k��\'��]'�i���磔Le�a^��>_'}�@9��`'�cj�/a�F9�F9(��&O	)�@9b���c'�&d'}ۯ�F9e'�&���a�ze�dȨ�	��*�f��bŁ�<+��b@9g�-|h'
}Q�{��F9R��|�� ��&��F9i'��j'
}j�Ok�F9�
�k'
}�ze���& �F9Q��
$iSi�}mPkl�F9m'}��UH�zen��&��`���f<��b�Od�F9���+��&Z���F9o��{R�F9e6az<g�Pk	Evp��b(R9q��&�i8�	��;ur��|�;u�}���{z<g\��/��i��c��s'<gJ�״r�J���i�No�?9��{��zem8t'}6�q�d{�q%	�cu'<g8��a�*��/v'}��w'}x'�'?�ܣy'�`z'�c�$F�{'�`�u,"��/|��c}'�ch�z~�F9�����	��.���i'�&�z��)�'�b���{�q%��F9�'9�9�q%���c�'�c���c��b%��|c����'�c�'�bȃ{��'�c{Fdk�&�'�'���c�)�'�'�'�`�;�
��c��_��|��b�'�''�6���'�':�-	�)v �Gʎ'�c�b����'�cc�գ	X$�'�b�'�&�b��b&�Bm�'2�'fe�Fd�5���%
��n�*���_6����q%6a2]�b��)k�Bm�
g�
�i���05 D��`���X�	�b��c��&��+���/���h��X؂�h���c{�&|��h���h�׀��+�'�`n�'g�&� �'�c�"!���cn�[m4��띧Im�#g�)]feoݞ'�c�Od��'�b�'�'��Im�'-u��Imk	f�ń�Y9Sɣ�
)�'�c��6�����h�'�c+��h�j�,�[m��b��ǂ����'�b���h�)�6L����)�)���h�'cbf��h��zoݪ��%�'�z���h�譧#g��Ch5�Nd��*�R�#g��h�����W䰧Im9��6���z#�j�2۴���w���ւ@m��z�'�-�@m�zÀ�ܼ�u��֣ʁ۩�{o�Ud��@m�
gPPk��f�'-us
ۇ�zU;��@m���h��)�
۶�@m���h�'�z�����-b��o��繧58%�#g;�i�hmu%�'�c�'�z��3���h��#g��c��轧@m�u%��#g����~��g��Im��[m�hw��m8	�§Im�˻�zo�ImçHv}ħ@mŧ@m��	�n����uv�գ�'i1ף����	}W�c\�-#�	}��iǧ�c�/�'�-
Z��'�z����r��i5��\��c�'�c,�d��'�T�(�'�zY��c�ͧ�c��zΧ@mϧ	}.�&�Hv�'�zѧ�a�'���FmD�c�'�z�'�kէ@m֧m8䀌-�'�`曚-���ا�c���c���u9�ŧ���٧@mڧ�c	���6�
�n-<�Ok�'
��'�&�'Fm�ۧ
�ާʕߧ@m���i�
����vٸ
�䣯���_�ST�'Fm�'�&��@��
�ȕ�'B9k�&A)�'�c�'X+�B9S���'�i���m�kd
�`��/eFmu%���槚y$��cg'g�#�`X�&!��/�'�-;�ɸ�'�c��i��88Mm|��m�9Dl'g%4So����'�hn�	}ꧻc��h<�m8j�`	��c�	}���i��e=a�	}g��c�'�h�c�'Mm.�?,�-���`��&��'�&�'�-9����$�&m/O��`��Ji�N�������c�keƁ��'MmO�&���c��ke�&9�ke�f��'�-��ke=a��h�'Mmv�+���c�KdU��`���cD����ke�')��-|�Ch����w������</�ke�C���cƒ�(�+��Ym�')��`(Dm�f�o��c�ke���k��Ym�h��.)��{�-|��cl'g�&(�蚀�y	�&�
+il'g��`Dm�����ke&�h�)
(~m���(�(Mmo���l'g��i��h(UTU�(Mm	��`<Mmi������"
(Mm(�&��`��c
(�&(���0&��Q�keX�9%���i�{�O��`(�&��g���`R����(
}(�&��k�I9(�&�E��c��id�����c�ke�x%(
}�ʢ2���&��i8다i��Ud��aX�����S(
}���i(�bi!�&�)u}e������d(���;
}(�|~�)u��
(�b��n (Dm	
}c�!��"(Dm#(�b�OҘ��������ې���J��%�1�ң$�m1%(۫�j&(�b'�o�F��W�%�]d��o9��!���v�2�((m8O�xی)u��(�[)�o��*�F9+(�x,�x%����#ƪ�ހw�P�-(
}�ێ��?�����&��<���.��&��W�}9��߂�&���/(�b?�{��}0(�&���1��b����2��&�b3�&�$3�F9J+!4(�'5(
}�����6(���'�K�7��b�+!2d28�o9��&Q��J����5��|:(oe;��f�Įc+!<��&��o=(oeu+!s�>(�c?(�'@(oe)+!���&��ٞA(oeN��&	�	8�%B(�cC�)uo��i9�C	oeD(+!��g���u���&��c���$��^,�}E(oeF��k��'G(�cW&�H(�'loeI(�c�+!J��i��}K(oeL(�'�#��M(&N(]m�oeO(��P��`��c�������`j{�Q��b��+R��&S(�T��&֘}�{�j�	�U(oe���i2í��=� oeV(��H�����}w��b���&m�=W��D��&,�/X��b5��iY��h��d�[Z(x1[�)|%��\(��]��h�'^�)��Fd_(+!�/`(oeH;�a(oe�+!z�ib(�i.oe��-��5
��i/��)�
u\*?�c(�cn)!d(oe��hc���oe��Ye(�c
oe��c�Od���f�)g(�cY�y���hh��'���hi(-uf�]kj��bȀ́Q�{
-uk��cc���q�k�+i�8�l�)m(-u��%���bn���e��}��0o��i�p�p��ke���%;|��_9q��kr��b΁ң��=D{��bP�y�犎���Cs(-ut�}ޤ�bo���u�}v��hQ���=��c��{=�e���1Ca'
�+w��bx�)y��h�W��	�z({�{�)|��b�M9���1���XV�{Ä	�}(-u~�_9���u�a׉����h��}����;�)�( Dj�Iv�%��c 
�i%Amґ���(�b��}���u��jg D�}����'
�w�����(�b�(-u���b��`q�ʜ	��|��������b����c\�q%�(�b��n���b�( D��ze��;T4��ʈ���( DJ��P�(-u�()!��
z'��/SX-�(�`�-u��ze�(�b��?�K9��$#��_9�( Dĉze��cЂDv���a���{��+Q�zek۴�(�k9�P#�~��1?�E�(�k(��}�(�i���u>K9���1���1��?p�}�ze�<i�(�`��}�( D��?�({���ze`��_�n��i��)���1��������/3�k�/!����(�ik�<iH'j6{��xa�	۝��z Dx-�����z��b�f@}��xŁ�$�(�{5 De D�(�{�(�h�(��(�k�(�&y�{w�)�(�,��ze�]yK��'�(�&��L��z�(�c���')�b���յ��zF�h��)����z#ʢ�(�b��ݬ(�&���׀=
ۭ��z�(�&�(�b�(�'��\dt�,=�e|�(�hD�ִ�(�&X[������ף��bM�&e�Q��
�Դ(�k��'�(�h�(�h���z��%�(�c���(�c�(�&���z��h�����(�k������/!3�&]�)L�ʢ���(�,�(�i�(�b=��^�/!���/�)�(�b�(�i��/!è�z�nĨDm��a;��zŨ�'�(�b_��Ԥ�&.�b�(�&Ȩ�'�(�b�(�h,�&�(�b�
ۤ�&�(�h�(�c�(�&ϛ-|߁D��(�c�(�&�,�(�h�(�b���z�(�bo�%N���(�&�(�b�
ۯ"�h�(�c�(�b�(�&�洘��g�:�a#�(�c�(�,mPk���j�(�b��,'�c���(�c����(�b��(�(�b:��Jk�b�be]~eߨ�E�bਁ9��mۇ$|�(����H�ִ�(}��C��($|4�����Ca�\dH&�ܠ��	��@��y�X�be�u���W-���i��b�dP��ۅ��u��j��9�($|>��j?����x�f�W��D�L�;�}jE�Ɓ;*�(�bgQ+���<ao�-!�P��b�$D?	���(�bX����i�(�b�(���$|'�ִ�
��d16����_��(�>�
�@dl�d1
�	ww�
�9���(�z
�x�=�je�wz�z�ie�O9���9�ie����&F��(�z�(}��\dL��(�z�(���9���������׀��	���i6���(���R�
�����ct��,h)a�}��S-8�<���{�(}���cB��ϣscD��<��Ϫ�
��<�(�z{
���{�<ց<i��/��������ڇ�Dmy��c�Ϫ�Fd��}��`l����is
�%�z��c,����}��z�Ʈ��<is�`��Mv��c��Y)5�Y)�d�ց��&��})�'6�‚�{B��x�����i;��)
���&f��Q����z	)�>�}
)�z�ʢ���)�>))	X$)�z#�`
)�` ��&�����y�)�z��&�Q	u�^��%)�`��a%)�c��$y��c���)�zrme�+��/��b)�`)�j��bb@���{)me��c)�>�41��/meÀBm��))me��&��{���h�	me�����hLϪ��J��{lme);|�^9��:e��j�[m�Fd��c�	����΀��co��D�$
��.
�cq��<i��c�[maKk )f�R�>!��c"��h>�)�%�d�ګ�́#��&I
’���$��c��O[�%��&���&�[mۗ�cv�
)��X�'��6��QW�:�xOCC1��l��bg��&�
��F�L�Yd��&���`U !��Қ��
z���<�X"(�[m�5g���u)��`��(���55g*)�`����+)�-�Yd����[�k	�,)�`��$�S�@m-�W�.��z/�@mȀ́���0)�$1��z��2�@mt���3)me4)�$��	ۘ��k9�3Æ�$3��5)$u6��z�R���A+�q%���^ȉ=�#g)D7�@m�7�ca}����ցf��zw8��c9))Dh�#g��
zg�[mˉzw+��%���:))D�Zv���zیzwU��z;��><��c��{��>!��c��a)&'ٓ��	}=))D��>��>?�	}@))D.��A)}B))Du�Ҁ�$Z�C���ۃ=C�	}��uW�&'}D�<iE��>�$u뀼�)DF��>G�}H�	}��n�&'I))DJ){�B&'c)D���
�+K��>��Ϛ��&'n� ��Nd>��z��܄�zL�	}Ɏ}��ϪM��>N��j�@mǗ�O��>P��Q�n-=�d�L3�هiQ))DԪ��xR��?S))D��{A���qd�%��?5�)|T��{H�삄?"�v�_��{K.��?��{�U))D�$�{V��>�ƪdX-W))DX��>[������k�'��=�Y)�{Z��>[))D\)�h���]��{�)Dm�շ�9!^��{_)�h�	}`)�,a)�hy�,���9g���b��>c)}ed�Y��Ae��yf��>�a�&'g)}eh�	}g��`��G&}ei)�cj)�xk)]dl)�h�)|X��������>m)�c��=�n)}e�o��{p�Ymq)�hr��-s)�ct��$��9�u)�'��뀻cv��zv�(ކϪ��h��9�w��iȀ��x)�c���/y��z�){���ց��+R��cz��i(��"]d{)}ep�Ym�)ď�i��9�|��z})�c0Dm~�Ym��i�Vm���c���%�
}e�){����i���{�)Dm��9�)�hd��ce�
م)Dm�)�h���{�))�)Vm���i�))w��݋��{��hiDm��,�)�cQ�ל�)}e�	�h�)�)�i���i��-�Dm���i/�<i�)�U��)}e�)Vm��4�)kc��,��'��+D@���a8�9�"�]k���c�)Dm�)]d��z�)�i��tAh��W�+5]d�)�i8�9����i���-�)
}Ȁ�Ŝ��-'����9���(
�+�)-|�)ۯ��>��y"食)-|)��ܠ��z��9����i\�����c{��-Dm�����9��ʢU��i���c��a���z�����S�)Dm��Dm���i�r�9i8�)Dm���(���z��-D�)�z������C
}�)�i�)�-�)Dmx毣�zl�j��a%��-D��	�?�IVml
}Ӄm11�-D�ۊ�)�)�z��-D��u�=���k��-Dk?�B
��d���	2��i6a�;|���b8��)u�<g��F9(�訁�n��=�b��}5f���y �	ۊ�-D�-|.�)u��F9(��c��Dm8�-D��}���c���o��
¢ȃ�{k�í���9���&�췩�(�)-|��o���F9.�)�)Y9�&�{���g�)
}��F9kC�À���-D��n�)-|���y��F9�_�׀F��]d{&a΀�]m��<i���f��F9���3��ދ�zJ����h-��?��-D7�iL��z�iv=�R��-D���`�-DS�}©�x�){�[�-D���]]�)uz<g�)&a�h-����ʁ	�z�Y�߂�$<�=�)]mQ��S�&aƩ�$�	��pc�
'�=dᔐ�j�+��L9���ִ��yz<g�bc����'��j�]m/pc��Ć�'ǩY�@آ�)ۂ��x�)
+�F9j��
�3�)�{�)�&�}�)de �F9�=����)�-�{��)�'w�&q=���	��wpcz�}<ˣآ�)�-m���bck�9{_���j���'q���#�Y#آ$�}�pc��Ӣ�����)Y9�)�&w�tC��J�-
���	ˣ�G�����-R��>�pcA�-ҩ�j�)�-ԩ�x����	�����)����-Ɓ�v��<,��x�	������)�iԀ�i���j�\h�q�ש	����bc�آ������-ة�j�)!X���٩�x���Xک�`mpc9<�w��쁊y�_9۩�j�pc	ۇbc����I�$�izܩ�j�)pc��b�pcbc}oީIm�)�-���੮�|���Ԁ��2���⩃k�_9�ImJ:�L�Imh��k뀵9䩋kwde�)oh�Im)��9橃k穋k��Im�r�pcA��9詃k(���驃k�)-u/��i��f�/pcl�M96���i멃k����)��9�M9A��9�)pc D�m8��Imk뀵9mpc�) D�k\��%���s��7)!�z��
ˣ뀵9}Ϫ�X�S�(M9F��k(���k*�{�� Dl)!�k}�2D���9���])!]-u뀵9�)&a��@m���k�)!'
�J+��-���k D&a��@m��-t���k��@m���i� |4-u�@m�	ۢ� |� Dc2D��@m=���-���-�)�x��h��>y�@m���iN��k��Im��k$�_9뀵9*&a��kj��>\@�)��9���I��'I�Im�Md��k���*2D��io�p����뀵9��?���9���?^\��-�}��i��kI
���7�\��x,�i�ڀ��?	*2D��x
*$uԀ�*;�z!2D�@m
*B9��?�0'*��c2D*�h߁�?N��*B9*�h�@m��h$ˣ��x��z��-��0'���(*��@m�	�}���SN��ۇ��i��,(�0'��{J��zo�cc�Y*̪�@m�
�y�����@my�ht�x�o��hÀ�)�0'�[�t��(�0'��i�0'��{D�	���i��h�xt�{sդ7�{d��^��h�t��#Tl)���QX����1��y*�`D�o�����y@�Ϣc�i���?d߁*�h$����{p�}�MmH��o�����)7����%��}*MmL��� *Mml��'��r���G!��?ڋ�ho�n�"*	)�ـ��0'#*�h��$*B9$��%����E%��h����
kc&*B9��i6��{(�0'y�h'*Mm_�ie��-!��Jϋ�pc(*Mm��0't���y|g�!C��;��k�
�c�-!���Sv׬�-!o�.�r�)*�c�
���{�W-r�S��i*�o�I��Oع�-!w�����-Ԃ�t�fk��π+���m�kz�ܼ��?煔c�,*$|X���r���%&a�@9�Ck���J��ie-*�(�Ϣ(����-!��O�.�&���o��{�`ՠ�(�?����9��$D��oc��.�$D��V/*Mm$��0��z�����1�$D=�N2*Mm���z�ۨ�`e3��/j��-	��i�-!5��if��)d��iԀ�<�$|��轇�v'�4��h�pc�$D}5��z���}��b���Ԃ-!�C^�b\��/d}���6*}��}��-@�=Ԁ�����?��z7�ie�j����pc�i8}=�n�z�ܟ�$D*�'���$D���׀}���)u���y_��8��{w��o�=Fa���y9*} ��d�^:*}��ߣ�{;*�h��},��<�#VBc�+'`}(�<�?����i<��{/��/��Y5�hc�F�6ԇ?�=��iS�G��$D��Wc�$D>*&a?*�h@*�c�&a�}A*�hB*��C�F94'D*�c������`e��F9E*�hF�u0�3�
YD��+G��H�F9I��i���>�tcJ*}��b��{��bF�h�fe���ЁL%K*}�F9L�}~������Y��IJ�b��F9����
�؂�ih(
ۀ��yN��o�آM� ���N�F9O*�hP�F95��i5�bQ��yz�,Q��i�	�d�-��,��i6D{�e�)u)�U�l�}[�Y�ݐF9���{R*�hS*�,T*��U�)uɀ�iV*�-;�-W��{X*�>��i��{�߁Y��h��'�Fa��.���"�tc���iomeZ* [��h��-��'��\�F9Zԣ��c]*�-[$Y���+.�)^��hÄF9��<�{�_��c��F9`*�c�o9�_����F9a*�c���hA�-b*�c��>)��y���c�F9d��h�)e*�-�F9f��cW����-\�F9!�A�h�
�zg*�z�)h��hF�>h(�)i��c��j*�>k��h�z�cw5�����'�)ɀ�i:|/����z��W��dv��|���l*�)�����ϸ��cm*�>����Ӏ��)dٸ��)o��s��n*�zo*�>�>u\Wme�)p*�zā��3�iq*���)%�-���r��c4��Y|�zZ��#$�۸���?s*�iB��ct*�z�)u*)D�:T�)v��c5��tv �w*�zx��{��-u��U���{������y��c���z�m8{��{|��c}*)D~*�>��c���*)D.��{��y��墧��Y��!gx)D���<��}��Imރ:�����آ��)���<�*�z�*δ�)3����)|��;|�*)D�>Q�W���<�}��zq�z��m8�r�C����)�*�z�*)Di��<�*�z�Fm��)B�e#�آ
ی*�)�x]��c��z΀!g���=�����*�`i��<oրΟ)|���<	�!gk������|���*�`�e#�����n��^UAx')D���<-�)|g�!g���c�*-u���ifm8����9��{���}O)D4�+)Dȃ�� ��j�*)D���y���-u��"uv)D*ے*K9�
[ғ*)D�*K9-uo2���	X��;|)	 ʃ���*K95�;|(���2�e����<�<H�����DK9S�e#���<��{���a	 ��)|��;|����e�u@ˣ���6�*K9�S���g���)|�*K9����냹�*#�{�pc�)"K9�*�{qv�ĉ9'��e#���羚��d����*�{�*	 �V)���c�י�!�(����'���cւ�-���c�)�*�{�)���-���`�*)���hԀآ��*�����ݪ�*�{��棧��;*�d��ޚ��y��v���Њ�-7�Щ*)���kc�yVm���-(��*Vm'꣬*)����$�a���c&��-,
)Ԁ��y)k
2���+�*Vm�*)l�"uj�8k��ݰ*)���_c[�Q�;屪 �)�
�(�*)�*K9�	 �*�hw�hJ��<�*)�*�{(	 )x�%I9����5��<��D4'�*Vm���`�����䁼ˣ_ݢ��c�)6�9'��}c'�$!������-���'�$!���-�I9M��{,��-�€���,�*�xj��-Z&:
�(��h'��X�*-|Ϝ4@߂�⿪�,_��}�*I9��*)ªآ(���	)�*-|�pc���<ց�m����)���l,��-|�I9Ī-D��o�PmŪ-D(��ƪoY)�Mm�*���*)���ɪ-De-|ʪ?D�z�dݢ	:��$|r��_����w�����h�-D�)-|
Mm˪-D̪�i��آ�	%W��)��-D=}�?Dͪ-D��)�����	ݢ@ԣ����*}Q��G�-D��{Ϫ-D�*}k��xѪo+��	}k��x������iR�Ҫ-D�*�x>��xנ-D��W��;u�*}d%g�gȱgP�*Zm�*�{B
��ƁW�����*Mm=�؀=w��*�-S�ܧI9�*}
!��$�*Mm(ԣ�*Mm��p�*-|ݪ}�-|��}c�-D� �L�0'���&ު-D��{W)�h����Tg���e�?z�6�ߪ-DeP�w�p��*�h�o�xG�-D�?D~�Y���0'�*�c��`Z��J�*���?Dv�š�*�h�-D�-D�W-��cF_�	��*�c��i�*�>���A"�}cԀ�<��Km��i꪿,���*�]m(�0'I}瀻x{�m못xS�/��
�
��k��̟�}cA�ck��x
��`ؠ�
�{p �Km�*���cT�H�}x
��*
�
 �x�
 ��`�`8��`��
 �}�xZ�ܣ%IdlM?m��'�
 >�tc��{��@dI
���*�{��t+�@d��)��y"�}c��Km��)�聫���jÃ)��	(��)��@d�*�j���,pc��)�}c�*�jH��&|
�V�蟀}c�"��*�\u������'��h<�jmpc�*�c���*�ǚq�*�ck	��8
�j���x�)Ppc+�z'�)+�a�@d��)Ȁ�.�	��%.�
 ����iv�9']��'%�(g�)���	�).�
 $�.+�k��ۃ�'�b��NÃ)˧EJ+�kx�kFx��i�+�i�)	�#g���}D)!
�Km����  �d$+�i��im�_9r&l)R�k+�i
+@k���}�����[m��+r���)��B+�.���+�yb$�{"�tc$��+ݢ+�jZ�Jg�(g=g#�i���'(�(g:�)�)� �jĆ_9Cpc�)+}�)+}��'n�(g+}����'+$u+}+�i}�eDd��<9�;��¾" Dc�Cv�$u��X��&����O��`��>�2D +?�dݢ��/��	�y��=�})��- D!��z��$"��-#��>$��>a��i
ɴL״�i%+�`&��i�	�-'��z��i���++�M9z�&(�@m)+�j��z*+�i+��zz�&,+�-��J���z.+�i�۴/��i0��-1+�&R��z�i2��i�wA3��z���-4��i5+�`�<�܁�z���6�8�[m7+�`8��z��o�
�&6��-S�&9��i>�dw��@mm4'�C`:�~~A}� Dm@k;��z<��zm��=+Omz�&>+}?+�-��-@�	} D� ��2D���+ �&��}}�s	�	}��_a=uO��`A+}_��X����>j��-X��>B�	}5��<{��-C��iD��z󌣏E��i����uCÇe�F��z��bL�	}��G��z����H�	}��II��z��gJ��>K+�cL��`v�Hݝr�M�@mN��z���`O�	}S�&Ȁ��P��i+��`�
K�Q+�R��zS+�bT��`U��iV��`�ָ��܅��W+�&��-���X��`��5�]�Y+�ץ���Z��c�9!��&!c2[+�h��	�b�b��x�F[�	�	}>�آ�be\��`b):�z]��`���ou�&��ݪȀܬ�m^��`���X�=_+�-`��`a��ib+�(��	}c+�-��Gd�	}�Zm��%B��&e+�-���T��x��J	����9Df+�-g+�hV	j�j�h��-��&����i+fw�W�>�:aj+�-�ԣT�p?�:�k+�-:��bl+�h��m}�-��Jm+�-Ȁ��n��{o��`�@9��?���X�6S��ڎ��`����p+��=�vY5��{q+Vmr��{�c=�P�s+Vm;�bt+�臀�u��{.c�Q��v��`���w+Vmx+�`y+Dm�����	�`z+�i���{+Vm(�|+@9s�?}+Vm�Dm~��M+�h��f��+Vm�������h�+VmT�3T�����wU�+�-�ƹv
���R�-���c,
��+Vm���c�+Vm�Dm�ܴ�,���c�+�&�`eg����c��_}+:�+Vm���c�g�Ɓ�ʋ+Dm�ݢ�VmG�}c��|Z��c��ދ
}g���¾�+
}��h<���{y�*�dw���c�+
}m4'���<��-4'@k���c�+Vm���{���c"�xa j!�j�Fْ��{���&�z�+���+VmL���X����+
}Z������&�)u� �A����+VmDm {��+�e(�蛫�&�+
}�*ܴE��&��}.�z��Id�Dm�+VmW�`�&|Xٶ�`���}��>��҄}�+�z���y��
����&w��y���/�jo��J=����e�-���c뀀$��}*�=�Id��kv�Dk��}������F�+�k;��s�F9���c
}���+o��S�
}��}��F9w��ݦ��c�!���F�*��*s��	���i��3S�+�kƁ��c/u_դ��~�+�/oʹ�+�`Ԁ�����L
�edX$�+
}���A��&�
}��|���y���&�+�k'��.Y挀�i���&��w�}���&Ҁ}��i8��íi8�+�k9<���}%��x�}�����������F9Z����ŗ���뵫�&���i��
)R�Id#��J�+�`�;u��}��R���+��Id=��� ��&�+{���:.���io�ߴ��́�%Ё��\��&�	 ���&�+�`�	��h�	�-9��,�����a���aQS�I�{E
��ѡ}�+)
I���h���a֔������$�+):��i	�ʟ�+�&)�kī�&�+~~�+�-��aT��&�+�&�;d% !ȫ�&�F9g�cI
"��)ɫ�a.�4�ʫfx˫�&��&̫}ͫ�%z��io�=Ϋ�&ϫ�a�+�c�w�+�&���&�?a��}U)ҫ�?��6T��cӫ�&�+�&��?酔���&��])ի�at��?��-A	 �+�{�+)�+	 
�\.$�6/U;�+���+�{"�>�Zm�+�{�+�y�
�-���}��&�0��ǘݫ�%�q���&���%�	г )뀞?ޫ�a����R@k���?��Y�߫�&QXֆ�ߴ�?aP��a���v�ܣ��E���&��a.)w��?�+)D�+)g&i�B|�de='��yǑu\��`�+)�+�yЊIm�+�&��a�)�+�c�Md='�+�&�+�c��?1
V �+�c��&���YB�o7�;|D)�x��)|�o����<����m�o9�n���-���Z�^��?hۂ�Im='�+}���+}�&9��i�Y���?Ԁ�o��]d��j���Im���	}�+X+�?�o����+�yv}��yQ��y��<���Im�}@k6�y��Im,�y���{a�V���X��"u�)D�2�+Fm��@mÀ<��X�:�x���yU��i����Q����+}�Md�+)D�+�����+��k�i�+Fm[�x�+)DÄo;�?B�8�+}����b�Im9��@m�>&�x���k�@��?�F�2��8�"uQ�@mL�Im�o?�Y�
��`�+g#�)|]�a�o='���h��<�,�c{��j�Im��`�<�,}���	��`��Iml�"u��<��⣁�3�Ӄ)Dr�t�
,}��t���c���,v�9�}��h,}�
�x���
��`�Ɛ5��h�X+ 
�?��cs&D��,)}=O�,F�,)��`!�:����,Q�Ɯ)��cb��h�����:���k���YmĠ�h��c,)���`�
 ��΂��,)%��c�F���`��c�@m��a��),���h,)�@m�):�?�$|�����)���`�����s�)>�}c>��'��?€�d��.a���t,,)��+��D,)��R��x ��'(O��H���&��? ,�ckI9!��`"��'#,I9$,�V.��'%,)&,�a8��,'��c(,�9��),�cb�j����*,�p.��`jI9�O�+��`���',,I9-��,:��c��+.��h/,Mm0,)���o��i���,�)O�1,Mm2��c3��c�)��|4,I9��xx�Ym5,I9 �$|6,Mm�)�M"݄)#7,I98,�m9�ie��<�:��c��;,Z�<,)���c=�ie>,Mm�Cm?��c@�?D��-D
w��oA��c΁&��$|��d��?Dm��cB,�-C��cD,MmE�?D��F��b�
�Ղ�c���[�T���N��PcFy��F�˧Fa_��b�DmG��cH,}�ޤ�cR��I�?Dǂ���cj_mJ��cQ�T�Ä-DK,I9��b�Dm%��c��bJ��Ă�,��cO���j���aL��c��b������M��&N,_mNMmifO,�P,I9Հ��Q��c%s�R,MmH_mS,�bT,Mmg��&��Λ��+��`D��l�����/e��ݹ_mU,�&V��`���Հ�W,]mu�+X,�&5�`Y��&4��aZ��c�2�9�iey��c�
�`j�&ˉ-D[��c\�ie���)�9;���)�9],��Ä-D^,�m_��c`,Dme�h��;��Z�ma��c��`b�Km��`Sf����&��	}c,]md��a�#Fa��e4F�e��`f,�`g�	}9�:*��h,�&i,Dm�)(�/@�	�	}��x����j�Kmf#k�	}l,�z	%�Î��ɗ�9m��&&�- 
��������&�mn��`��Km���:��xo��&��@dp�	}q�	}���?r�@d���[���os,�{t,�`�+�&�)u,�`=�$���`é�b93�t�`��{�F9
���&m�{v��aȃW1w,�`x,�&i�׀�<���-���y��PQ���z,�&t���X-{@k�����//�&�(g}	ð{@kg)ۃ���9)���9<���5��baG"��(g����F9{,]m���R��$(��)|,�m���}�	}��ہ��i~�	}��Y�,)�:g�,me}�c���%����Z��iZ��+��M9�,)��V��鄬	}��EJ��	}��cゐwЁۧ�i���m@k-�.��V}f�ۆ��i�
me��M9{�eb��cw�,�c��������M9*#me����c���5���.�y
�c,��$��c.�M9W�{v�	ۊ�_9��ی��i<Rkyme�F9*�_9�� �me��F9��c{@k�cwg�M9�����F9��ې,"D��S��M9��	����i��c7�-ʋ
����eDd9��%�,�yq�4Z5��%5��b�@k�,)�me�)!D�Fo��x��y�G-�׸.Cm�,)o�Y�)��+�c���^���ٔ��'�,)��=v�壻D���c�,me��c���D�Do�,me�"DD��(�棤��'�9���!�D���c>��&��"|v�Dk�,
}�'����x���^��s�o���}�,
}3"=u���'�cwʈw���Cv�,
}�,����"|�y��Cva��������,
}�@k�9���iS��&	
}��"|.���9�6���i5���*�"|�,�-���i)�۹Om?�:��}{@k�e#�@Е
}��e�}�	�O�y&�}�
&<Rkw��I���b۴U
"D2��'���>۴[s-�,
}���'e�a3��Y���Q��,>g��}���'�,�{X��q�$|���'D��9��r8�fk$�xc���偗�'�i��v0��}R��k� D�D��, D��c�<��, D�,�p�/�}: De�9D��?ao�S���Ăbe��e#��}� D�-|���o5=u�,�>�,
}���io�|�+��<�����i*
۴� D��o��}���i�	 AdЧ�i�, D�,
}���i�,�?���a��kǒ	���}�,�i��tA"�beˆ�-ӆ��l��ܽ��k�����ھ��i��&�d�5~8� |���i�ÁƋIm��)�&���i¬)וbe@��+�#�/o�(�)��)ì)�Á[$jY9���=H��-Ĭ�aŬ)_�Ƭ�-��?��)��^�&(��bǬ)�, D���7�%g�X����)�m�-!	��{b@9H�{��&�[�ɬ�-��ʬ)��&��o*���Ȁ��@9]�f1z�-!�,�mϒ�i
����&�#g�og�i�uR��)9�on�⧉�8q���j�u�2���̬�it��ih@9!�_�,�&�@9.�7�ά�i�,�&���i��&h�-|����,@9V��iu���,dP�,�&Ӭ&Di�<�Ԭ�b�@9�,�?p����&֬�-׬&D聭c�%nج)�,�&��'*�&>R��&�a���-�\mڬ)��۬�-ܬ&D	5儀�-d�Ϧ�,��ެ&D߬)����e�)�Mm�� ��w��{ᬄ-��Ҁ���

�
Mm�,}㬄-)}�&D�,@9����,}��{@�ʖ$���&D�,@9�,MmhF��,}(�&D9��-�,Mm���&D�,�&�,@9��<�����ie��Y�,}
!��,�&�
��,}))e�9�,Mm�.=v_�,�%À�i�Vm��F�����$|�,}���)�Mm��
ۋ�@9�c�,�y�,�mo���,�aw��yՀ�wB$|������y���<��_T��)���ٓ&D�,�i7�t5�h�,$|�,�i��j��A��*�����߁��
Yd5ᱦ��-�iv��%�&D��i��cS�&D�h�Fdv�'-}��+-�i�)|-}e�ف
Mm���;��b-��#�i+}�MmȀ��:��z͟�i-}-Mm��iep�(n-}���^	-�i��c�����壐�<�Y��Z�m��(nb�i3
��XmԀ��י4�yȀ۸�U�
�ie��T���Eu��)��ܮ�B��܄ie��(n��{���-�`
�ieH�uJ��)���^�)���ۑ�p�A
&`]�ôO�)�ma�$/�����-��۝	)-}�vY)��"�m�$|��	,��'-	)W&���+f�{}Ȁ�^��h3*�i� !ńô?�����9�	�����(۴?��O�Cmk�b-�b-�cゔw4��'��$|-�i-�-,�{�㑡��%h�.g�A��'R�-|���9��4������`Ҁ<������T;�D9���5k1^�諀�D�)|S�):�{��&�+��g��Ҵ��z-��d�'�� ��&��%��ބ�9��SҀ�b��D9--:�gH���-�y��
��?k	���{��&��he����&o���y�D9{H������Y5�me��,'�ƀ�H�-me -oz�m�{�jRk!�)�E���y!�ô߁��"-me�i� !z��&�+D@�#-�y��&$-+D�����9l+Do%�!g��R�
H�&��{'-me���L(�!g��)- ��i��!g*�[mہ��(Z�{�+- ��[m��&g�zƁ„�d�ve,�;|����<�D9-�+|m[�.�!gÄ&�+DH�[m���&?��6�?Հق�[m����y}�۫��^X��Ȁ��Fm/-}0-Fm�+|��`1�D9���/2-$u3-Fmw��oO$u4-Fm���>h�!g5-Fm��iʎ�9����`�a�`\��'m?�6�[m��������X7-�`�X�8-�?D�&������w<9-�&�o�me�9���"G�@my}��Jj��mw�i��:-me�me�%{�D�Mk;�[m���9Md���}e�9(�[m\�m��"uŘ!gv�&Ҁ@m����-|<-)D��"�/:�!g�i]�R�#g=��`����>-)D�X�͓��?-�{@�!g���'A-)DB�[mိ�C��`�����'D)D݀�`:)D��V���[m�}D-Fm
�+E���S�&F-FmG�)|s��������"o�?�FaH-}���ѧ)|�F�8[���I-}��V���{J��hh$u��K�)|�)|L-)D�"蚀��3��`M�)|��b=���N-ִO�)|7��P��h/��Q��`���㚀��ތ��
	�:�bd���B)|R-)S�)|��`T�)|�[���T���f��ִ��v?"ܣ��J�ӃG+��U-�y�,A���<����"��&!V� ���"3�2��i�&�m��)�$!�;|oִ��PҮ��W�2�{���]9�n� X-)��LY-����Z-)[��`�o��{_�9\-)D�{�h���h��]��I��(��%t%gI9Ʌ�^�)|=��lI9_-�%e��`� 
�)|�o1��%a-)b-%g��c-)����)|�~ed-�c��%e-%g��	}f-)HI9g-�%D)/I9h-)i�)|B�j��B���cH�)D�B�k-�m�+��/Dw��l��z�c�ִ(��)m��'G
�������U
#�b�/D5�
�ckB����ĉ�n��z���-��ئo-�cw�Ң�zb���p��)J�-�{�)q� ��z��/D��or-)�Dmj�
�
��|ʉ/D�Y�s-)���
�ct��'s�/Du-"Dv��z؂/Dk3�w-)�Vm��;Fm��&ǔ�zS��hx��z��&���$y-`cӃ�'z�"|w�p�{��z|-)�9��c��,}��&d�����]���?~-�h��h2�����'����
���%�-Vm#�+��`����$���h�-�coB���"|ꃭ�&*���-
}>B��--|��A��&���z���&<�ߣj
�h�
}.�����'B�Z������z�$����Y`c�-�h���Y��|��'��"|]m�p���z��/D��)*۹�/D���z���닭}�ߌ�Km�-D���a[�(���ێ-
}�-|��)u��Km.��x����_�m`c�--|,��/��Kmi����ߣ���c��&�-&u�Ȕ�}:�-D�-`c���&2���w�З-"D�����-D���'���` D)�}��᙭-Doe���'�-o�`c)�Km��}��㌒�&�� D��m�)���h���a��Km�'}��-D�}s�}��"|��-Dl��h��*�)s�.�"|� D�`c�-o�- D���a�(g+({�/$
}Ђ�}�-��/�Km���-�h���
}�-o�{��-oω(g{��f8��-D���/�-�?��+Z���/��c�{��
��l�9�e�ݑ-D��Km��%��Km��-D�{���p�>�\��-�z��谭-D>oП%��۱�Km��-D�)��}.�)|�EJ����b�- D��c��-D�Km"�/oR���)>�%���%��M9��D����z� D��)���cD#�iv��Y��)��R�H��{��͢$�����$c����)�+��_9(�%�D DfRk�-�&>���- D��(g�-)�F��9S�(g��M9)�-�-*�(gc�-�i* g­_9/��í�V��c��_9ĭ)߁�$Ā4�R�
�!���|��c[���-�-�4Z�;|�-�-m��۸}�-�(�{���?���%��-&|
��c�-T���{��"8�ɭ&D:�a��-�{������$����Y�-�-�de9���˭&D��&D��&��zÀ�>�Zm	��5�-7��cʋq�̤_9[$٤��-d���&D���`��-^�&Dv�鸺��d�z��M9Om$�)!f��̭&D�)�-OmjRk�--u���i[�tk�w����Qւ�o�	_����H$�-��y�Rk��ϭ&D@�ʖ	��6��hA��'m�)��J�m`���ՀN�Э%�&��>����{J�>�[m�-Omc�-q�ҭ&D��z��+ӭ[m�-�&QS��xխ�i�-�-׭�z���i'"�'�-deOmS�m����٭�zڭ�i�-�al��?ܭ�i�F�ݭ�\&|�-�{��h�-}��z��i�Q��'��+u�-}�h��9�-Omc��z�&�c�$|��'��i��':z�&Da���-�h��i��z��n�&D�$|��z'!�b��zL�X��h ��`��z��c��b5�R�-���x��h�f1��z���y�-}oY���c�-Tl�--u �b/��z�-�b�-}��h�?��be��'��x�lOm�$|�B�%�x���z��b�B���be;�x�{`c��I�-`c>B�;�x�<U-V
�<U-H�f,�$D���z��[m���iQ�?��x�����im�);�?X2���-�h���i�	)=�tkB�$*�hx�x�����ߣ�-2D��h��x�-�-�-�h�9���+5y1�Ԗ�-�c�-$|
�-��mo�	�+�?��b�-$|�-2D��	&�ש��. .$|�@d�bx�x��jx���.�c. ����5�b��(����f1��;�xӔck�{J���.�{}�?Z���x�:�x�L��.�{��D�.Ԗ. � �{.@9:�x�	.�{���
.�?��.(���oB�w�����$�?.�{��ioB��y1(�(�`c��Xcy15��+�?.�{��?oB�
. c�{=�?�u3d�̀.�{�B�r�.3]��J���&�b۴ց��o4�+�'9��..�58�{�
;�EP�`c.MmԀհ�)�+…����{�V����.+D.Mm(����R��{��e���.��i�k�.�i��܄�h�"j!^�#g:j������iS�#g�4��+D�f(���@�%���.�{��f. �#gȀ�.�{� V��y��{�#gm�Fd.@9�#gÄId�i���h.9Dj���f�% .9D!.�zR�i�#g".�{���%�#.9D�Jj$�)%.�3��`8�/u&�#g�%l��x&��i���%��-|6Y��?�!���'.�z(��i+D)�#g*.�+.�J;�#g,��i"�9|e�i�Y-��i����..gP�����^,/��iϸ�����
���h0��i��?��	1.fe2.�z5/u���
��I
ā3��iҀ#g�+D4.�i5.Mm�a���<�46��i7.9D��
�8�#g�_9��ib�Bm�������:.9DD�*���8�z;.9Dn�#gx%)D��/ĂBm��f��#g�&)D��/߁�[�#g<�f���=.��>.)D��B�hǬ#g��i?�ie@.)D5��A.)Dw�5�b�5�B��hC.)D���hx�h�D.�?��84��E��h��/��Y�)D��PF��hG.)DN�)|��ۇ�yȧ��9��hH.)DI.�?J.)DK�)|L��ik*$uM��i����
)��	}LWϊ��v�
�)D)D:��N�)|o8&��{O��i��	}m`cf��iP.F�Q�)|&��-R��i���S��h
cky��hT�)|����fxu�f�U�)|���)�hh�<n�&V�	}W.)D��k���h����X.)D�)|��/o�4��/�����ʑ)Di�/���Y��i(���.��z�)D��{KZ.)D[.�/J�F�k\��h]�].��^.)D_.)D:��z`��i����a.)D�� �	}��j�b.)Dr��ic��?d.)D΁&���躘/|e�9煀�z���Ea�	}R�mc��)|���B���s�f�)|�Y�g�	}b��i�)|Տ�%�)|h�	}i.�mk�2:������-��s�&	�$j�)|��s�&k.me{�l��k9�����x�
۱��{m.�>n�=Do��{��&A��{E��{�me��-p��{q��{5�d�r.�d��ɪ��3�=D�`c��/D��&s��{6�-΀��s�&�	F�����J't��.u.Fmv�=D�	"Dw��{r�)�&D��{x��.��&���TT��{y��{�"Dz��h{�g�:�����{��{�۴h-���{|.Fm�x}��{��&{��{��o����?@d+'g~��i��`%��?5Fm6�"|��{���i�."D��meB��?Ri1�."D�.mec*{���&��x*�mc�.me�.�'����.Fmg�h�
��s�&��'���h�+�'i�ڢ��a����E��"DJ�Ӹ�&��߇.
}h&-|��"|D��Y���{w�����&�.
}���{��/D��&v�棌��{�.-|5��Y��&k�1��m����.-|�.�'���{.-|=���?���h���{���w�t��I
��-D�.-|�.}e��Ym��-D�
�h}��ܕ��`�
}�ۖ��?��-D�.-|XX+.�YmQ;��)�.
}�."D��&������-D�[���-D��Ym�?���h���x�-D�Fh���hv���9PY !���}��,��-D��Ym[��?��k��i��x}��-D�.�iQ�h��YmE�i�.�h
���
��'q�R��ϣ�-D�.-|��-D=�"|���y��i��"|�.�i��FC
��k	�Ǩ�Yma"��.�i����h� !��i�.��+&-|>�z�
}�.���.
}� �i�X-�.+!�.�i���=�.-|�.�i��-D� �.�5�.?����k�.+!�.�i��p���Ym�.  ��Ym�.�{���.�%k�p��£�.�i�-Dנ-D{�a���yD�{��-D���kB +!���ᾮ-D�&�{�.�{�.�?��-D���)�{3�d�7D����Y��{� %�?���kD�d��{jrc�.������{d���. î�-�:��?�.�{��!g%I9��	jrcĆm1��{"rc�9�.�kզ<n�.�z3�4�J�
���;����{��b]+!�.�&���=�]m�#�i`c�. *�ɮ�-.�&؀�dc�Vm������.+!zeZ�pӥ n��nt�
�3�\�?+!�.�%�#��jrc`c5���[�?�.+!�.`c�8�ή&DϮ�-m`c������)+!�	 `c�I9s&Ю�-�.�{��6�&�`�B�2��%Ү&D��m1Ӯ B�Ԯ�-�.�{�dc	�J�I9֮&Dۂ�&D.-u��mc�.�`.-u�.����-���yψ&D(��iٮ�m�i8�`c�.�`.-uۮ�-�.]mݮBd�֢��&D�Bd� �`ޮ&D���&D�€��mc߮�-�Bd�.]mV�u\�-u�Kmϖ��(���n����#��'@���<�?Do]m��i*�`�?D�.]m�;u�W�R��.]m�?Di��/Q����`�.�%�'�.�y�Km�]m��	�?D쮄-:�?D�a~�.�yq�4@��iﮡc�-�Km)���&D��`��ڊ�哖?D��'>B������5)Y�Kmjrc:�p�.�yn������-c�s\.-u��'G��~*���a�j*��2��BdB��&�g"�f8�-u��&D���c�]m2Df*��iW&���y�.-u�Gd�.�`�%��r�)j4�-u���{k��.]m��:g.-u���`O��{]mp-u)��i���a���e�����'�	۴��£x�9�����)��%݀Km��/�y݀Km�Y��c(�a���钚��c������!��㯂ie�EJ�?D� �ie��?� ��j҇�w	��?��Kml�/!t��i���c
�t�/�yt����%p)�y=������%V��y�z��j����
�_9��{52D*���=s���?��aL2Dt� ���ª�1���?ބ2Ë�t��zȀ��'�9�ZJ� �_9u�M9��&/�-�M9{��?�����-�(n`��{��j>�+/�-��{`1)��$��?��--����GҀ�?�Fd��-u�=���'9�j�$�n�-���/Fd
��%��y�}��-��'�*���)�u%��'��y/Fd��mcƁ��_�3�Fd�9���i�ȕ/�
��Dd��[�_9���q%@Fd;��?/�)/Fd��}�}lFd7��i � ��_9!��?mu%�Om=u���������_9"/9D�)=u#� :�I"��M9	��?$�����
��Q%/�ie���.��%k��%&/9D'/�i�u�9D�
����8
=u�9D(/�i�"�x)/�i
ê�	�-�	�c*/Om��w@��ܑ����£+/�cYme,/9Dg�[mu%meo�&-/+D./9Db&//��qF�0/Om��h1/+D��vc2�Fa˟FپG��-3/9D*�-4/>g���Z�h�aw��[m�Fd�'í��T!��o=u5��'���	����+|6/ D��{�9Dh�[m7/ DZ�£U�{��i8/ D.�?>g��i�
 Dʋ�݊	{k��vcv�F�9/�{��}���:/ D;/�h���ib </9D?�{D�
�{��x=/ Dd��z���\�)>/ D?/�{�"�a��c@/me�+D�
 Da�{�9D��YA/�{���4B/5ZC/ D�z�9DD��zT>gE/ D)���c��hF/ Dv�X|�)�Gm�f�ۄE���{G��z�<���{��Àbe��?��{�[m�c#��{��(H��zᎠ%9)!��%BI���J/ D�IK��EL/ Dg�?!��hs�i���z.�?��g�)݄f˔�����h���z.�?��{����$�! DM/�{N/�?��q%�;� D�c��1C DB��@91�aO� v��JP/@9ɀ�iT���Q/�aj�mcR/@9�@9��{#��iS/�aT/@9��vcU��i���o����V��yc��֠�T]W/@9�Cd���X��z�@m���h'
��R9��f�be�f{Y��z�9�Z/@9�Id��z[/@9΀-g\/@9]��z^/dȇ�vcQ���r��zb��>
�׾@95��_��cց��Q�[���i)!�
�+��T`/�h���z܈�i_�BT;a��i��(���H�?!�iR9�)b��cz��ic/�hQZ�ρ��d��i��EP���iX:�X:ܾ:T��y��id�6X�e/�{�������@9O��y6��i� �s�f��c���Ng/@9h/@9�mcyj�kF��@9�Ł�$z�mc=��ci/@9ڋ)���<�+ڊ4S�/D*ww�@9j��i�
��&����/D�	��k/{���i&��{D��I���@&��FO��y�Fnz /��i��q%�I��/DΠۨ���l�A=��{S�hf��/�ADm{�ӑ�ym�Kd=��Ah��e���-��%���c������{en/$|o��i	��i�fe� �eO�fep/�h$]���/D�!5�	%�h��?��Z�/D��zUDm;��cq/�zr/�h�A�`��?s/Dm���A��t/fe��Z�f��u/�Vk���$|m�� �e`cf��'H�%PDmv/$|�QҨ5z��w��hl+$|�
�yx/�z�'��$D8���y/�z��-z��{���o���/D׀�{|�Bm}�	$~����hp�/D���u��B�Aj�Y�/�h�{e�[dÀKd�˚ ����݀�h: ���hi�=DՀ��*�/D�=Do���ht�}u�t��h�|%
?�y�܁/�'��-!� �/$|��)ui�)�/F��/f���a*f3*Dmmu%N׷���<����T����h�?�5Cd�g�'��h���h��h���?��6g��h��}�/�i#�;|�/��k�݌��?b�z���?��}��z׀vq�/�z��f��3�;|�/�iXW��$D��F9���ē/�y�����F9�/�i��F9�,�zv !�����F9��}���/�i�%�8g��}�����YU,�]d۩�D9��}�/�z
�z�/�i�/�'���/�i��z�/ ��8g� ��z��(�/�iR�?�Y�ϋ}�/�i���f�/ o�ʣ�/�z�F9�9!�/�i9|�/�{Ȓp��/�i�/�{�/ �/�z��;|�/ �/�{��}�/�i�/�{��Id�/ {�q%?	˳�+��Id�z��)q��3�����
۳/ �/�{���ܫ��^�d�t�mc��{����GZ�ۿ�/'��z�/ <rc�/�{�/�z�/)�/�i��{�/)\���/'���/�z��}*�{ǂ��zo �z�
���i��z�/�-Ҁ-gz��%�/�i�/Fm�/)�/�-�/Fm�/)Y�/'�/���/Fm�	)�/�-�~FFm�/�{k�fԀȴ�%Fm}�Ch<�/��/ �/)�/�-�9!�/�i�/Fm�/ �Jdh�i�/�i�/�-��ih9!�/ mFm!2���/)	���/�{Od�/�-�Fm�/ ��-��/Fm(�~�\J���9|���z�#'��/�{e2�/Fm���h�/�-*�&D�sc��zɀ�%�	ٴ���ٯ&D�/)D�������/Md�/Fm�/)�2��9��/)D9X֫�^&t�ň����-{�/'�"Md)Du*)�/�i���)�/)�/)D��/'�/�b�)DY�/'�r���,�/)�/Fme�2�	;�/Fm�/)D�o�Im�/Fm�/)�/�-�ɢ�ImH��`)D�/W��/Fm�/)D�/�-<�Im�/Fm�/�-+�)|1�Im�/Fm�/)D�)|�<g]�'�=�/)D.�?D�)D��'�/}����/}��Ym��o�/<g��)|���'�ã��ke�/}��)|�x%O�;ug<g�)|�ke�G+��ov}��-�G+0}d�ke)���ޣ5���k�`m0f0}0)D��,))�'���0�'���	0<gA���٤�g%�y
0}�ke&��)$�b0<g
0)D�o��{'-f0-u��1�ã�)|0}0Md��'0)D�o3���Ev0I9�ket
�0)D��ke��ܒRp)��Հ��Im���E�oc��y@�)|�E�)|_��'�H�y��=�Im��a҆��f�-0<g��{$��h�I9�Ym��ߒ 0���	�'!0<gۣ_`��'����Ă &��)��{�r�"0}#�o�Z��'���a�<g瀟-$0}�<g(�H�>��%0}]�`&0����>�*��2�9�N��r�J�z0��X�oՏ�5&Ղ ʋ�8'��=��߸�`�H����%�S(�o)��{*0�`��f�+�T]/�`.��,0I9d�灎Fd6��$�%��S3�b�`-��{��%�H����L��J��jT�	����ބS�a�q%p�ã��`S��%.0I9/0I9�`00Fd��j10����%�|%c�>��.{�h��wۯ����20 ��9��30Fd���4�2���t�2�~�h���'5��-6�o]m&FdӃ�{-|Roe�I9s�T��rc��a70Mm��}�o7�I80oe� .�}ͬo�oe90-|B�$5 g�������jB+-|��i:0oe;�}�0�7��-�Mmh-|5�͌d<�Km�$=0]m>�ie?�Km@0oeA0-|B0 C0oeD�Km4��E09D��`��KmF0�%G0-|H0]m��a<���;oI�-Dz��\�?�MmJ�Km�?K0oeyMm���iL0-|����"��iM0 D؎-DN�}ۛKmO0 DP�Km��-Do�&Q0$uR0�{S�Kmu�{eT�KmU�-D��aV0 Dψ�-��g���a� g D����W�-D��c�X�-DY�KmZ0-|�U���-D[0oe\0 DO�)]�@m^�Km_0 D�oe�9D_�5��o�ӁKm`0-|+�-g��@m�a0 DqUTb0Mmc0�{��	��-�@mRoed�-De0�{Ł�if�@m�-|A]mg�@mh0 Di�Kmj�-Dk0�{>��i��}o�-Dl0�{���m�Kmn��i=����jp��|�&o0�{�o��{p�-D8�������q�Km�	 �)!r�Km���5��ies�@m��t0)d�Վd���M9�h�-D�d|`��tX��dS��<k��a����u0 v0�?���%�	}R D��S��<�$u+�?t&��Dd�w0B9x�)y0 D?���p�@mz0 D���u\;g�B9{0 D>�@m|0 D�@m��O�)�oV�}0}tB9~�@m0}�b�vc��&x	�jt�W����݀�@m�0B9w��ѡ�Md��@m���m�0�ye�$���Nb��t�Ϊ׀����ob��%���B9@����(����ꇀvc���d\�ã�����0}��{[kC�*
��o�y���k�,}��3���$�0)�}h�i��_9��>{�ۉ�M9J�֊0)X�M*G5J�51z��<���ց����i�Lj���>�0�i�0)�0Om��9x'���%)��_9�-�0Om��<����
*�'��Om���0���`B9b�i��I5�w��$�i+�-g�0)��}{�Nv[�ek{}v��eB9	�Yd,��$�0)��-��0}�0Om�0)�iv��}�Om������?U��{w�Kd)Om�	VBX}���Om�0=|�0Om�>n3�`1��625��`�0Dm7��i��/Ȯ�$��-���`�0Om������t�����-o�)��f�:)�02D=����f{�02D���a��=D�0�h��-:����?�02D�	2D	��`���?���{��{�0DmЁ��ۥ0Om(�覰�a�0$|$)�0Omဥ��#�)��i���&�02D��$D>�beg�{6��i]�y���%�0Om9����`�Om��$D�0�'��$D�0�-]�y���yc�Zv��$D��'���{����$D���-�0
}���o��mc�0�'4�y���o�0
}%�$D"��$��$D���`��㹰�`�t��t_3)
}���a�0
}!��"��$��&f�'�0j�02D�0
}��$D�
}�02De�RU%'��0�'�)°)u[�����Qð���Id�0
}Ű)u�0$|�0�c��ɀ�igԯ�Id�2D�-�-!�	&O�02DȰId�$|�0
}ʰ$D
�)u�02D&o�0$|Ͱ��谏%:� ��0 D�Id4�y��R�H�ϰ$D�0 D�0 DҰ�? �)u����0 D��Id԰F9d��հ$D˗f��0 D��
}�0 D=��5��}�0�'���yʥF9��$D�0�'҇3T9R9ΜC��y�0
}BX>&o�0 D�0
}"��c�z�0�'S��c3�����0 D �߰ۻ�S�0
}΂��X>�0[��&��u��F9z��'i�A�����Y���W�i��i�ⰺcTգD��	�6��f΂��v��<����0�%�)u�0�%_��L�Id�F9�)��k��y�)u�)�Id�+]d�0R9�R9d����Id�0�`�)�0 D&t��ۗwws�i�)����%�0 DT�`�f���^�%{��0]d���)�0 Dt�ã�-J���0 De�}��)����0 D"�Av��+ԀТc��y�0Od�0 D����0 DF_/Z�Ϝ�+�F9���y�0�a���y���y��ߺ�F9���y{��c�F9�)ף,
�a�0Vm�0�%�))���.��aL
�`�6|;�%b�a���c*�����cA
Y�f2��c*�
�`���oZ�z��c�I�>���)z�`*�����)$��3i{�#ۇ��H1�?���n�%1�`��*[�B]db��i�Y� 
���)��`)�%	 ���+y�%��@`�)	1Od��}��Fd�')D
�)�Cӧ�c1)Dg�Bd�k����-
1-uՀ�皀�<�Avg��ߝ�א�B	��h3)�ay��-�ܡ�}�)DX|Kk�Avn�a1)D���$j�Od1)D���-��'ǖ���)D�)|y�a-u@"c��)|��c��h�-u��h���O)D1)D��'`�)|:g������c��?! ��h�"6|c�$�)D��$v��1)D���'.�
)!�ˣ^$}(����c
��&��'����)|��c���h�ckt�����)|����1}��)|�1}���Ȁ�^W@9d��Р�c���'1)D���a>�Y�܄)|�cD��B��c��)|�
dȘ�4S��aD)$|1)D�f8B��{��{9�;C�zw1)D1$|"} 1tU�	�	Y��?!1$|"1-uL
T;#1)D��y^�$D$1)D$���?!%1$|��?���&1)D��}'��'�ۘ)D(1��y��zw���?��=Ȭ�c��ap�)|)1�y�)|΀�5��c�=*��c�2*@96��i+1�y,�)|��y����l�)|S��c���ϋ���)|m�~~.������--1�y.��c>�=����B�g�a/1$|��7㯨�a}���J01�aR�1��%o�21}�"��z�c��oւ�%$|�8���}��)�ց�h(�=�$|31�=&�5�
/������{{��{���o����av�E�41��X+51�i�a����4�����-�)3�)�� ��~6��71�y(�i��81X+���Ȁ���yo�2O���9���l��hϋ�<��*X�o
�Ot:1�;1X+���G��=��R<�E�	�i
�<"�=�W,�X�c3}=1�=2շĆE����m>1�if��*
ۧ��?1}�����@12A��}�\d��ho��hB1X+� *�`C1}eD1-|)���9DE1-|ތ9�	�+F1}eG1�h��cH1}e��mI1}eJ�48K1-|�Y�L1�i���M��a��cN��aoӢ���`��%O1}eP1-|��ϣ���`Q��a��R�ke���A��aS1-|T�;|h�U�YmV1}eW�-D��cX�[mY��aZ��{����[1X+��Ym\1-|��j]�Ym^�)������-_��af��'`1�>y�&�a��ab�Ymc1}em{�!���P��O݈�a�}d1}e�ke
��f�-Dg�Ymh1}e���9��ai1}j�Ym	��a����p�-D��;|��<k��`o�	l���m��a���R�
 ��s�}n1-|o�Ym���r�g����;|p1�iq��{y}.��{r1}es�Ymn ��t�Ymo���	}ey}�"|u��k�b�	�cv��av��w1Mm]'-|x��ay1.nf{�z��ay}�-D���ce�!h��a���'{��a|�Ym}1-|��)~��a��Ym����-D�1)D���a��Ym��@m܁;|�1)Dڊe�����1)D��2䆱�{��[mk)D��Ym��-D��O9I
�	��Ym���{��m1�1)D���{�ۏ�ke��-Dy)�ߎ�1)D�1)D��p{}��)|�1)D��ew�m3}��	}�6��1.n����1)D:���}@�(Z#.n�1}�1�J�֌�1)D��)|΂��L�	}��)�}��~ct��o��	}�1kFma�G�Ȁ�^��)|5�����	}��j��)|���	}��	}��l߂)|t��ow�E���)|��Q� w�f���3à1)D���y��	}��)|�1m1S�o )D3�"u2շ���m*��1)D9�{T�15��1Fm�ʸ�)Do��+J�+o��J�{�1e~��ܨ1۩1)}�o����:��� �N_U���ܪ1)D�o��"u�1�a�1)D�1)+;g�1�`>�£���]m���o�)��"u��$���-�1]m`��m�1)����1�a��{��)|:�Y�1]m�1�`Ҁoy���)|�1]m!�+
�I�1�a���o�1�`�%�1]m�1)m۴a�`�&�a��)D��E���94k�t�@oe���c��?D�1]m��ʢ�)�1�a!�[�1�ai��i�W�?D�]m���+�1�iñ?D�1�i���ű�i�1]mDZ?D� ��5�0�%�1]m
Vf�Kd]���ɱ?D�ʢ3���]mj��1�i4+|�1]m�1)�븾;�΀��]m�#�iͱo�a�� m��cαo��@�1�-�1�%�1�a��a�1)��`ӱ?DԱ�bz��O�{���1)ֱ�b�1�a&2D�/]m�/)�`��o�1]m����1Dm'!�i`XU�1]m�	X-��$�1[�1]m�1�{��{�1]m�1�%d�{�1�$�1]m�?D��|;Q�A]m��=�1����X6e��9��?D����1�a
��ۧ���Y���i��ad���?D��%O��
�1-|�?D�����-|�1�i�_9ѬEJ�?D��%.ˢ.�i�_9��/{I��1-|bI9��/�1�iU��z�-���}Ł_9҄�/o�-D��fI
�����i��|
��/�1
}�-D��/�-|+�m�1Dm��-D���k���/����,o���k��-D���/��F��-D��}�1o���a��_9.-|��-D�-D���/�1 D�y��Ղ�_9i'���Gm�-D� D2o?��k���W��-D��k��/2 D���F!�����/��k�-D�@d2o	�-D
2 DZ�F�Q�}+o���2 D|��k2o-|ȃ���k
�"|��{�Aa2`1. D2�D�9ބ{R҄�/2
}_����k2-|2o�_9냿��_9{ D$�+a�-D�-D>��/2 D"��2 D<��/�vU��_9�-D2]d�}�����/�"��ɦ�"|��k9�V��/ 2ob�-D!��k�"��/%�-D#�)�
`1��"|o�ʢ�-D$2o{�]�-D%�)&2 D2
�'2o9�Z�� D�`1��)��k��k��Cv(�)� ]dS�"|;(�-�$ Dm�m)2o�J+e�{*2 D+�)΂�$7/ D,��Ԁݣ��a-2]d.��%�/��`0�)g�f8ѐ�_�����S12 D���%22Vmg��`=��8���@��?؂�%[�)���?3�"|42de52�-ȗCل���X��62DdW�Cv#��a�[\��i���?�)4����d���%Vm�"|7��%Dd8��{�9�أY��?��-��
9�)��l�,:2�V�C��)��;2Vm�Dd<2�it��m��_n)!��M�^v���=�)>�o?��%��)@2Vm=�֨�r��jO�)�U�A2DdB�)�p����mC��%D�)Y�Km���%&���m)[���84bH��f�	 ��}E2c#F�Cv���G��%H��`��撕�%B�)!�{�I��%�рJ��%���	K2VmL��m�Dd�u�5��]_�M2Vmb��,deN��i�)!d��t�Im]��Yw�w���-�ѸdDdy�;uO2
���)�֒P2Vm�gԂ$�;�0�2Q��A2Dd݀?!w���R2Dd�����i�oS��iT2Dd}w�V�z����)�`82D4$|U2}�����ݙ�AV��i6��ʁ�i��Y�W2}�02DX2$|j۫�è��mw��wY2$|H2DՈj�z�F�Z2	 k2D[���2D$|�3�*���֒�ʢ�L�$|΀&D���i�X�S��	��i\�&D���e	�aZ)!]2$|\2D6(�+Ĺ@��o���x^2Y9>��	e��i쁺c_2Y9o��`22D��F���i%�&D��a2Y9J#}X�ib2ˢ_������1}9�#c��i�����a9���أ
�ʢ����憅L���@<gN}?���y���)Հ�����)ɀ�y�
}:�&D��y����'}fGv׀����9����Kvӏ��,�ۧ���d22DȁY���m��p�}��)�ˢe2��f2Y9g2$|h2}i2}j2`8����+&}k22D	�&���l2��Q�NQ�m2$|n2�a?	��v���}�ۺ��)A�a\�Y���&���o2����)~���ʇ&D�fep2}��9�*q��-��&D��%�::�	"!	��ӏD��O�]feo�����Y9�
�%A�a5���r2�as2fe��&DƁ{�t2}���u�&Dv2Y9��mw2}x2Y9�fe�}]fe�����)y2�a���>�۩���no	Tl��)k�i#}��i���������)z2}�����{2o��%,�it��|�̣��"�Mm�x#��-]fe}2Mm���Λfed��{�k~2;DcMm瀜m݀�y2�-׀��&���8��?Fۯ�2�a*�-��ia�}�2�R�2�am�~~Y�Y탲#g�2�-�2�i�ۆ2feC'�������ʋ5���#g����Q�a�2�a+�-�2MmB�%��#g,�)��ie���h��Id��#g�2�-I#ƿ虸a�2feΆ�ٹ�-!S�g#ڛIdq�5�2fe����60�-�2::��Id �&d�ܗ��Oȗ���	:"�1.�Bm��ie�L�D<n��N�`��E��vq�N��Id�h��C��)�2�i�D9:�#g�����{T�2$u�2�i��97ӎ����r8��;|l$u;D9�����?�-������aw���[���+���?T����E�8�g#
������{�6i>�-o��B
u3��#g������T��"�)�,<n��Ԫ���7�-?��Y'%������#g%�#g�;Dk��?�Id��q���ݣq��f��(��#g��’��I����Id���@m��-!�)Du��g#P���2)D��Id�-y�;|
F�(�N�9�)D$��?����Ȇ�V��Id]�ϪT)D�.��%ݕf5��裲	}�2)D-)Dd�f��)|o��fk�����7��m��)|�2)D��dx�����=�!gȀ�2�h�2)D�5�5�	}Ҁ	}�2)D���񫲌?��2)Do��!g���m��)|S�!g��}��m���a
����!gu�ʢ��}���{��fXFm��)|!
��ڷ��S�j��m�����i>���)|h��}�7S�2)DҀ!g����	}X�wU>��У��f*�{�o��j)D���\o���g��h���k�2)D�����Ԃ�	X$;�A�2+D����im�#Dj#€�2)D�Ԣ��>:7)�2)D���ɸ��Q��)|��4�2}e��f�2)D�f�_m��)|8{��2Md��!g����)|� _m��)|�2'g�2MdIJ!g�2Md�S�M����fY��%�	}�('g�2MdDz)|��-��4T����2Mdɲ�`ʲ�aH_ml�,�me�E��ke�2Md��-t�i̲?D�Md"Fm��E�=��Ͳ�-���2meϲ�`��keв?Dj���NѲke.}e��"��#/Md��j0�>Ҳ?DӲ�aԲke��Z����Ȁ����C� ��a�����2Dmj�K$y�?D�2Nvv�Ydb��af���2Dmز�aٲ�-ڲ�a��۲keܲ?D��L�"#Dm'��t��_��g�"u�2�F޲keL
�y� �р��ap�ke�C�4��2me{�oˢ�ke�@d� �@d(������m� �Dm���dMd@��ᔜm�@dYmeMd�2Md�_m�a��.�@d��a�2Md��`�me�@d�� ��љ��?D)�@d��|�ke�@d˃��2
}� �kei�g���a��(Q�NH�?D,I9+���@doأ��ߋ�`{�?D��K$Ȓ��b
}��1i8�ke;��-��*�p��@d�?D��}:��\�@d8-|��-D�-|��wj�I9��-D(Z�e����i8ȃ���-D)a��i��-DX�i�wp*�;|��-D���m����/D��@d��-D9�w�� ��-D�Z��
}����<��d�-D9�%��i���8�-D��ҷ�@d���z�{ゔI��m1�@d���m��b�}��&[j�&��-D�����@d�f��F9���	�-D)-|�
ۯh��}
�F9��h��}m��.�;|5�}9����-D�nqf�2���ݸ
3"D�-|�I9h��}˃�c3-|n"D��'�i8�
}���i���&3+!�F9 ��c샀y -|O�a���D�m13+!�}�&-|�-D)�I9|�i�i�[�a�+!3oe3�%�6�3oe�+!WO$��"|�-DB��b5+!��+3+!�)3oe��-D3oe����p�}�a3�a
�&���J���3oe�-D 3+!�/�?!�Cv��a����%"��b#3oeȏ��@��c5	��]d$3+!�F9��}%3�a� =��%&3oe'�Cv;F��F��}�
oe(�Cv�d��2)3oe��4ʉCv*3oeT�%�F9����F9��}=��&�-foe)����F9+�YmčCv&�����Vy�Vm�]d,3�%-��cc���m�/��cwΥ�.3�a)�i8��&/3+!�3Á�Cv	��z%�}��"|��)ڋ��03+!��)1�(g���3+!23oe33�-��Odf�a4�(gDoe��)}�(gO�a�'oe&���53�/0oee	�%"��a63oe��(g�^�f�%73oe��h� &	 )oe�Cv��)���,€�@�آV�9���h)�Zx8�Cv���)w���p����)U�Y�*�cw���ʉCv93-u��/��m�Cv��h������Cv+�Y� ��)�EJ�����):3�k�uՠ�Ym�	 l�(g٫�5�N��	 ��/ރ��;�?D�	 <�Im9���x=3�f>��?��k}�Im�)��M9+��/�Im���i
�y@3	 A3�%B�?D��h]deÄ�)C�?D��I��(g�]��Fa�	Sp[d�D�V l�ke�㈀(gE�?D���)g�e#����2DF�?DDR����
��)g�?D�����G��H32DI�?D��)J3
2�Im3��);�y�٣b��ݼ��	 K�?DL3M9h��}Ղ�%�?DM3	 ����k�j��}��&D�2D&��3�^��	�=���O��iҋ�{=1��	 ���v��n��}l��щ���jg�h7�&D���N��io�<O��{��݄�Y�&DP�Y���{��&DQ3��R��i��{�܌.�\dS3������5T�&D����{@��M�&D����U�M9h�ImV�?DQ�m8��&D�?DW�Im)�/!��V
]m�hX�f��Y�?D7�f�e#m��v٣Z�&D�e#��b[3]m�Om���%考kmR�\3
�.�h�*��2D���.��%�`8���$�/!�)]�+u^32D��"!b��%��a�Z���=��z�f������_���x�.��%O��{���{���%��&D`��-���ؚ���	�Y�T$f����x��,a��%��-��܍��/���b��{'�䣼��&D5�h��z��Ɔ���l���c��Q�&D.��%�)x�&D^��{=��%�=�tC�'k�h{���>��z�٣�Y�ĉ2��&�Q�Gv��_�d3�h���me3�i���	�qӝ�5zp��n�ۼ�!d$����ݎ�k���$���g"	)9��ۺMmI
Á��-9�l��"7&�-f3�ig��%��g�Mm����m��h3	)�e�Á.��%������[�	)�����&i��ij��$���%W�˕.� k3�,h�i#jl��i5��S�[mm3Mm���Ɓ���	���-��-n� ���YY�Cv?��o� p3Mm��-�o�m�j��Cv�Mmq�ie͉_9r�ˣr3�-X�G\�����s��{���mT��ݫ�ڀJ�/�Z���t3$uu��{�&v� �	۟�Id�٣۲ (�&w3 D�#�{x����f��w�Idy� ���mz3 Dd���`1���m{3f����F���m�Dd|��3�{���m��ӃId}��{~�ۘ�{��Y"��贁 t��m�&Mm��j����efp����J�3f٣�!�S� D��%.t3�c*��{��5�ۀ3 DC
�����i�Dd�ہ��{.� ��
 ���<+ww���i=#ۃ��f��޷��{ŏ0�39D��[m��i�� >�_9��Idı�{��85/u�)�/u��&���yt���#F�n��{E
Fa D��^���y���{�3 D�3f.��{���m}�Id�f��i�ww���y�3�f{f
�z냱���i+��=�Dd�3�g�{[v_$��o٣�39D�Dd�%f�r��Á�3f86|��yq�d�3Ddl)��m��i��y�m*߂ۑ3uC�z�xӒ��y�+|�ۣv��yL0�k�t\�e�3�k�3�a���i��kJ뾋�&!�a��f�X�}���z����=����3�kA�ke�e���a�x#��k����be������k��,�3�a��o��iN����=���В۩��œ	���<�3�kҀ�ܵ��ւ�y���ip9D���k�beJۯ7�ia���w+D�5gO�4��k��-�b~
��y���i���MdX#5g�2Md��J��J����3�kZ��ȝ3�{�ܞ�9D�3Md��=�
)
+�aĉ�=�$�i�3�{e�%p���}�-�Mdk�G�5�Y"Ȓ�mÀ}�	2{�ƥ�j�3�{�3�-�3Md��ۤ��-��[d��Y$Ҡ�-�	z>�eI�{e	2�-�g�kCk۴׀�h�3�i��-\�?!�j��k����F����h�3�i���-��h�[dHMd�Nv�3�k�	 *�
)�3�i�	 ���b�� �
g�'�ڃ��Vʫ��(���k�ƭ��i���b�3$|���-��a���a�\v�ۯy!@9;��b�3�i%��������$�\v��i�3 ���?���Md���?���3@9���"z$|���a�3�i��aep��3Md���?������+.�<��ܺ3�yB2�3�%�{e/Md�3i8�%Md�U29�3�b����z�3�'�r$�3�{��z��zw��ye)���P�q��3�i���3�i³uó�a�����o�?�ij�a��h�3�i�g*Ƴzwdz{e>�	<�[d�3�y��^�(�iɳyeʳ[d˳�/����g��b*���3\vͳ)u����$|�3�i�3�i݀;|гId��FdD#�fȀ,
!��:�ѳ�/��}�fބ����/~&���� �<�ҳ�/ӳIdԳ;|N�ճ�/%�$ֳId׳�aسId��>��/D;�lȀ"����a��/DٳV ڳIdm{�۳�/ܳ�aݳ<���7޳�a߳f�Id���tU��/D��/��Id�m��zX�/D��/:.�i�>ni8
Y5��8�#�Id���g�<�O� ���X�,"n���S��&h�i8�"|���3�N��� �"|�3�yЁ ���/Q��/��j��ihC��!�i��h:����d�^���/f�&;�uY��g��{�"|�����/s� ��{���$�3}e>�?���&,�/D�3}e�"|�	��`��{}e�;|,�/D��$��/�3}e��a��)u�Id�3}e��;|�3�i�"|�Id��۴�hL��$�/D+�Id�Fdw�?���{[�E���-�9��}e4��{���%�3}e�
"D��i���a���%�3}e��i�9��3}e{��	�hd�́nUs�F�	^����a4}e��%	���'�>��a4}e��>kG"T����9�g"D&"ۯ��-5��ahC�:� 4}e��8gl�ad�^�4Vm���{���ʈJ���a�	4�-�C�
4)�����-��{H��Y�hhC����.
��$��-��%�}e��{49!���s�}e5f*Ɓ�.)4}eD�D9��׀e��*Vm4�a4}e��G�4}e��4?����)��.��a��9�4}e*+}e�u:�� 	Y�4}eb%nr��WHA>�9�4-u}��ՙ�ԣT
)D��Y"���a�i?��9�t��%����t���E�Md4)D�����)D��iMd��9���i��9����[4)D
Md��'G���a��%ŋ�- ��i3)D!4f�%n���b�'��Md��)|����"�?DV���
7�o��	F��c#��c$4Md�?�%4�$&��cs�?D��-'4Mdr�?D/��ܺ��ء�c(��i�<g��<��=)�)|�,<g���c����=f�Md*&���*4<g+�)|,��c-4f.��i��V)€�.�
ۯ:�x�/�?D�㑥&c�&04<g1��ca&24<g
����c���yV
Fm3��y44<g5��c;&|64)D���c7��y84f9��c��c�v��:�&D;��c�MdyFm&|9��ú�@db�z���<�&D=��c>4Md삣c��a?��i�Md@4MdA4)DB�&D��iC4)DD��i��y��m8E�&DF�?D���G�@dH�&DȀ"p9��O�a�G�I�@dJ��b��K�&D��?L�BdJ��(A��M��c��)|��<�N��i:1��G��'O��cP�?DQ�)|d���R4�aS�&DT�?DU4<g���c�&V�?D.��c�a>W4<gR��b\�<�X�?z&X��c͍&#O�&��&f���K<gk��e;�<�Y4<g���H��Z4�m[4f�?D�&D�ʣH(&\��iD�n*Ä��L����ւ�yݝ:�
V�&���fFm0��]��y^4`�[Q_�Y�o��	���2��%�&D��-_��'��e��@d��`4�%��ʣQ�-m�w���[�&D\����a�z������w�����%Ɓ��%�A�->��a4Vdb�&D�
ۯ	�:gL�\��{��4bk�-T��Cc��%��:gd4�a���{΂:g���+�{e4�ag�?>�!�d��-�&��:g���i&�fu�!�������;�x�y��%�L�jVdf��=(�<���:gk�!V
�!p*-|.�-}��g4�-� h4�i�����%i��iI�%�Ɓ�%1oj4-|k�G�s��W��EJl4I9m��a+�xӮ�_9H'��n4�c��io��aM.�iDI9b�zv��	.�z� R��dp4�-ϜD��c�7�q4�-r�-Ds�CvĂ2�
�(t�[mm`u�iev��kdÐ)��G�-D���k��w�-D�zx��k��mA�yQ�-��j�y�-Dz��{��aB�c
�y��}�ʣ|��k}4�i��fǁ�%����Q�����؁�k��!~4�y���a�Dd݀CvՂ-D4�i���kw�Sp��f�4Dd���%=�t��-D��y>�!�Dd��jԄ��k��@m��f8*��݇��k��@m�4�i���ȴ����4�i�4 V	����@m���k׀G���c���k:��a��i�c�4-|��@m���k��’�@m@z�		%��eF�ʣXz��Y�����@m9�����a9�����E>�6�*���49Dy�51[�b��i��@mc=ucy1��f���k��-D��f8P�[m��i=u��[m���u�oe��ie�49D��ݪ�@m��-D���k	��{���kj9D���?�4+D��	fp��a?�ye=u�4�y�49D���Ϗ��?�y1o��tB9A$u\�%��@myDd�4��4 D�4+D=uҀ���ۧ�f8+�b���%���X��@mj�
٩��?�4 D��Km��f8��f\��f�@m�4�{�+|�Y��̀�4��9ۚlf��@m$���f�Km��@m����Od���?��!Y�{�y!��������%��@m������*.� D��	&)� :����������$�4B9ۊ��!.�@9��"p9DL
��e=u,����k�����j�+Dd�9De=u��aG"�)�i�4�{��j���?�4�ip��s��?�4�-#3meo�4�����������!��	f{�c���?�4�-h��u�+�b��i)��ҀM9��	fR����c�Uv���㾴ۿ4����=B9)<�Km���Dڊ����CY|�Ҁ�݃ʣЁ�!�4�-B9��?(��c��M9���o��̓�$���ĉM9,�h3�pg�%��)�܉�Fd��<���k�4�-��M9:��w�8e��J�?!ô m���Ĵ��\vc'@9���{�4�kp�M9Y@9�4Dm	�M9Ǵ��e�
٘UZ�����۟��ws�ۼ3Dmϋ<�Ă�b��j�5���m��;\v�j
ȴ�{DmgDmҀ<�׀�ob�*Er����Q"H�$ɴ�i�4�i�@9%d��4Dm�Uv̴�{>�`ʹ�Ԁ�Ԁ��Dm+!@9)���4Dm��<�a�i�4me���ۃ&�	���-�\v���r��	m�w�3�4DmЁ�$�����4��>�G��|9v������h�M9�!
f�4
f�����Ӵ�e׉q���C���{�F��4
fG"մM9������o�$�m`��y�p�@9k���ʣX��e��ִ��m��%��<%���)J����*|w��!n�DmDm״)ul�)��˴�g$��شId�4@9�4
fÀ<�ѧ!n�i8۴Id�4Dm�Vk�4\v	%+|�DmH�&*�Idw��� Dm޴�i�=DR�d~�fP|��|�Fਆ�i��$2�*�* ��aߴIdӂ����iQ�S�4"D�ݣ��KdՀ�8�J��uz���i"D���+�c8�ʣ.�aʉ/DU��z
Y������.��鯂IdוF9���,��<"D�4�a���� ��ŃOmz�FdƁ��i�4"D�4
f#��	�"|h�a�F9紊k�4
f�"D��j�F9�i�"D��"|�k�����4
fS�)>բ
�L>���(l��$͓"|�4��*���AI
�-�4��	��i���<�"D�4�-8`�4�%��-��/D�Id��"|��B��-.�Bm�4�a�Idx���l�b	�	���	25
���"|�/D��bm oJ���Fd��p��-"D�C+��a�C��4"D�4 A�-���l�-�4�c�4 ��-f�3�bџ)$"D��-5�5 ��hQW�XW�4�-�����%�-f�k,�� 
Cd.�-��F9G�`��Bm�4�-J�-d�"|�4����-���ǡ�Ţ�4de�W^�-]��h��)��BmÎ�}	�"|� !p��-�
	2��-N������������a	�"|��ٶi��$W�-|�4�-��?�)�a?$˸A�-���}f��k��(	2 ��?5�-R�i��h����	2!u35�%f$����a��5��l�$A�-��-5�y��h��-��a��A��?���c�̀�
偄Q��?��ۅ�i%����
�p�g���2)D	5f
5f5Md5-u&��>v����z���
5Md��?Z�	��iݎk,�
F�lju՗Fawj����|y�y5Md3������B�;u5�y��?��i����R/f̄�k5��5f{�>���a���O��i�%�?��Ұ�[d5�i�)|���^�>��5�i_f�Md=��P56|ܡ�?)��$�$ ����{5Md��i�Fm)�����a5Fmkf�4Fmv�&5�io���TT�[dl�'&�
��٢��<���{ ��k:���I
�zFmw���!5�i��E���yH�w�"5Fm:�[dO�F�K�F#�[d$� ���t;��%5�i����&��i'5Fm.�&D(5��d��{)5Fm�&D*5&|+5�i�(f=U$,��b	X$-5Md�)D.5f���/5Md05f15Fm25)D��YD��{35����>R� ��&Dۊ��4��{55Fm>��e6� ��b��toګ�
�Fm7�&D��� �\"d��bR�&D�4�	��ʈs�85�i9��{\�!g:5�i���b_��a�	)���b;�[d<��k=�X"%Fm<�i᫊b�bFm>��{���?��{@��a��iA5&|B��-V
�񬁵e9���C��a	�^�o��or���À�D��-E��a�u����FmT��a�����&�F� ��aG��%�Z���aH� V�%}�x�A��Հ
��$|p�&D��&D�{KI��{c�Bd�-\��a���J�&DK��-L� ��g�Ϥ�{]��b=��M5��b� v���w���S���A��-��&DN��{m��(A��m�(n����ۨ��u>��O��-�����$P5K9��ia��{��í������Q��-��3���i����щ(n �
`R��ai�=��`��i; ���n����S5�i���b3��۹��->��-Q���
&T5�i^I9��}�
IU��-L�
2V5�iW5-|	��Y]��a{�@9�����{�i���&��c]�(nI9���%� :X��-Y5�i)��}Z5�c	�cS�x�[5�i��-\5�'�I9]5�{��5�^�;|��x�_5�i��6�XZ���0�mh��u��$`�ie��xӨ�㚀�a����x���{a��{���e��x��b5�c����۩���>*�b���'�hX�%�(nL��(�
�c5f
/J�d5f����3�ye5�{�)�i9��l��݆�{8�cf5f���i��㿃��fg5�i��{…T2����h��{h5�ii5fK��վ����]d=�{���j��ik5�{l5fI9 �
`m5�i���{�W�n5�b��o5�{���9��{���p5f����}\ �UT���a'�k�{�#�a������Z�cq�8Ɓ3���{8�����I9r�"ns5�b	-|t5�b��'u59DB�{��H�5��� �yv5�aw�ie��?�#D�Y�C0����}���@��x�"ny�iez5�y�ӣ{5�a|5#D�p�#D}5�y�oe~�;|i#D�"n�Cd�5�a/�y�59Dl��i,#D&:�َ�E��5�a���i��Km�5f�5�a�f�>�Q"���#D�5f)�{$�xӇ5fQ��?��Km���i�5f�5�{���?"9D�5�{��d.�	9S��5#Dj�"n��x���i���i��x��Y�����`X��݁x�
�b#D;�x��Km��T]��Km,#D��#|e�Ym��Ym[��?+�x��ar�Km�3�B�x�	��i*�#|l�"n��xd�����\CdބSɌ��))+�x�������#D��+|����#D"+D�+�{)��}�#D�
�a��g~�5#D���i	3zC����i)#D_�9D#D�5�a����+D�5#D����9D���i\�Km�)9���G����?�X�ȥKm��Y�;�xӘ5mee��H)!��Km��T 	��G���?g�x���c��Kml��񫀽���{�N��k:����G"$�x�Y �ܣ���d<�M9+�xӛ�M9�?D?�uC\���?D{`m�aރ2ã����%��M9o�i�k胨.v�?D���Ӄ��8�`��$:S���To��M9��Ǟ5�-'�j���i�5�?�Dm��M9)�Chk��Dm�5�?�&n4Vd�$:>��cZ�	��F٢�M9��oj�$:�5�?��i�5VdՂ?Dd�Hr�4Zs�fo��y���a��9�;��yΟ�`,�M9���a��%��ܧ��`y���P�a���+.���Ԁ�o�5�?���a��@d���5�ݫ��aI
㸬5Dm5Uv��i�W��M9	�i'�۰�n�ף
�`5Uv�5Dm��f�M9��k�5����a��M9��'��M9�X1��`����߄M9��fp��m���Om���@�i�5�bS���5'|��Y��`8=�T��X"��y�	o�5Omb
�i��?Db�W>n/|?+���4���i��?D�5�i�5Om�戹�f=��Ę�i��	f��iA�fЁ�w}�	r��4Omw��+��/D��'D�5Om{a~��	f�+Dm�����/D�'D��Vd}��/D�\�/|�&����'D��	f��\���f>�\�}���k�]d��=Dµ'Dõ	fx����5��զ��\����4�������ŵ'D��'|�fƵ'D@�/Do�\�8�>
�c˃\��F9��أ�5��o�\�=��/�5�iɵ'D��\����<��F� Omʵ�)��=D*��gOm˵'D�C�����5�ջ/|�۹�\�͵'D�۾�)���)�5�cx�'D�5Om��b�/(�'D��`o�\���=�f8�5�c����j]m�n�	)΀��Q:��cѵ�y�	fy�-��9���׀�ҵ'D�5�-݁�'Ե/Dյ'D����́�ܼ��ֵ	f��f́��o�\�����5�c�5�-@���b		)ٵ'D���+ڵ	f�/Do�\�G����G�۵'D�
u0@�.��D>�\�)�'D���j���%T*��a"�
����F9>�\��5�-�-!��Z�ãU�=>�\���}+����5�a?��_9�ܣ��cՠ�f�5�?U�a��bރ��5�c�����5Vm_�'� �-�5�->���5Dd�ުI�?h��5�aQ���5Vm3*D����5*D�5�as�g��u��5Vm�):l*DC�F��Vm�5�xJ�W���,�5*D�4Vm�5�xh�z>>�}�5Vm�z>���5*D$���J����5�x�-W):�,�-�z>�Vm��S2s�F�p�xXg��*D�z>o��5*D�z>�rfr�*|�*D����5Vm�z>�5 �5�y�5Vm��}�5
f��$��y��}�5
f7�a����5*D�
�����4h�z>���݂*D���L�5
f���L��a,*D@�ȋ�*|���v�Y�]
f �5Vm��*|{��'j�ʋ��$
f���'^5�?ߝ��5
f��e�5�a�
 6+Vm��F��ww�5Vm��F���֣]d�*D"
fF��'���y�	���*D�5Vm{��'�5Vmi*Dcܣ6ۀ6Vm�*D��yN��`*DXW^�+n�Vm�*D]���?�r��Im��'�z>��+n�����al���6������ψz>6�k�*|��+=�����̴6�k��x�	6�a
��i������'�Imʋ��:���ã�3�k6�
�i8�+n'۶���-@�x�6�a���[���L��;�'���6
f_�\�x�닦+�<��gY�
f���6�b	X$��k��x�6Y9?�6�k��yp��iցbe��$���щ�����'���i�*?�T�i�6�k��x�j��b6����'��iv��+���}��'��,$�����i�Imh�G��?��k��i�Im΀����$�
�9H�)!D�&B>N��j��}���η������oҀx�=���-:6�f��-��-�C��fƁV�R�x���-���Y�g���[���6�k ��i+�x���<��^�h���i!6�kR��c?	��"6�k!D#��-�/�-@�Tz�-:���ׯ�ok��x2��-$��i%�Bd&6�$'�̴����/�-o\"��֣'6�%%���(6�$��F��.|)6.|��2��$i��R�?�F�*6�-���?+6�$74.|�Q�,��-.|}�?6��?-6�$l@9.��?��}/6Y9;��b������-�Y9����.|뀞?p��́.D�(U;�!D>.|�G�0��?16.|��-:���G�%����������}2�G�΀x�g��?9#Mm݁x�C�G�)��36.|̑q��Ev���Ek��?+�x�)�-:�MmЁ��4�.DA�eD5�����a�Y�6��?���-��.D���76�-Ÿ�b�Mm�x�a`k$�x�8�G�9��x:��a>�\�� ��[m�2Mm;� +�x�<�[mm���Z�>�\�*6�-=�G�%��{Χ ��`8ᬩx>��{?� e�i�/�-@� ��i�\�A6�-�\�"��)��?B6�$���?��Q"&�j���[mC��xD6.|��`8k�����x�\�`6&E��aF�.D)�٘�v���?G��iv����iL��?��č {<.o�\����H� ���}���I6��J��{���;�x��Fd��[m��<��V��K�+L6�}(�x�׀G����b���G#�in�yM6Mm��x��i��ͳ�G�J��MmXJ�N� # Mm�����io�<�Fd4��i�Mm?�=����}k�\�*�i�����-O��aP���Q��a�\����x(��j�9DR� �[m�S��{T��xU69DV� W��{X��a���Y69Dj9D��Z��{��]9�9D[�Id\61D�!�->9D2��Ł�<]�Idi9D3��a{���.��Sֳ�{r�Ym���a���$^69D��_�Ym>��}���%�1D���`69D<1Da�Ymb69DX+c69Dd��a��Ym=���e69Ds1Dm�o��Gf6&g6
 ϋ�)�����Ö��Ȁ�<h��a�uCl��)i69D��Ym���Ԃ�/�9D��Ym��o3��}�Ym�9Dk��)��os�X�j61Dv��$��	f��Q�2
 ���k�Ym���{���`8l�:nm۴�{���c�	�����cd�?b��m69D�[�t�HP�F�1Dn69D>�
ۂ�]9���)�q�2i9D�	f��o
 o69D=�o�1DP5�=9Dj9D"�}�o[��?�}9Dp6_m�˜
MdʉYm��o"�:n<��/V�z��Ymq6Md��עr��a�E	��Ys6Md��G"t�	f��`8w����ڊeҰ�	fu6�bb�Ym�'�v6�xm�ow6�{���$(�bm
 M9b�bk�����4x�)#�x����y6
 z6�b{�?D3�a|��cR�ie��}v��}��c�bH�~6�{��c�6��>�&�c���c��? ��p6_m���c�c��À�)I
������)��2�{���cPDm�6f�6�?�ۆ6�b�
�k��*-DmR��&�B9��c���c��I"�6Dm�6�?<��i��?h��c��}�6�a���h�i
)�6�x��Q���c�6Dm���c��}E3�?o�ã��Σ�6�?��`e��}c4�����6f�_m>��}�6�x퐣cG�@$���c����6�x�6�?�6_m1Mdۘ�W�6Md��z�an5ZG�b�Dm����6���]m5�a�6�b�	
f�6]m����k��}���c�6]mD6=|۞��c�6�?�*]m�����6]m���c�մ��"nӃ�}�6�a��?D�(]m���c��?D(����?���c�6]m<ۨ6�?�6=|Y
f���c��)n������+֟�i���cg]m�=|B#��3���6f�&F�5|@��)�6]m�ۭ6�?�=|�6Dmd��i���d
f��i9���#����6]m���5��"j#Q7�m�-�)n��մi��V���n�����@$�&ϱ�"nG��i��`s���ʐ�!Y=|��~�a�(���ܳ�`eT��F��`e9�a	����"nց\�׀���F9=��&�}p&�R�F9��}��Ě^5]mw�W�X�7ܵ6���6]m:=|�J��6
f�4�a�#D9��B닫�6I9�6
f���{��{��?/�n��F9���ݳ�ܪ�6i8�X�5�̑1ѽ6�a�i8��#>������������$+�%�ٴO��{��=De�$�G���Fd���VA'�:4S�6�%A��%�6�$���{��}���,�cQ�2o���6I9�6�%k��ݙ��>�_9+�cwA��%Q�!gk��>��}¶_9{����6�c_�$��}�$���7�i8t���Ķ 9�˧�`��&;M"⁦9�cw��⁦9Ŷ!g�
ۯ�6�x�*瀅_9)���}?�b�_9���,�Ƕ_9c�_��c��g���%�6�c�3�ܙ�,(���m���i�$�aS�jt�68Dʶ_9˶ ̶�a��
oJ�����o[�v_�!3��a��}�i8�68D9w3ބ�Ǎ3�_��a�68D���ݸ�}ʋ��9�,�8D	8D��}϶�aж"u�8]��a���6�c���6�-�68D�$�68D<��a��_9�ٴ�_9ڏ�?ն�}o�8��68D�6�$&��>�_9ض8|�6 �68D�f]g{:�c��i8"�_99|_8Dl�8|���HÄcw۶_9��&u��ܶ�a��,Dr�������  ��8�j`�6`1��}9��,���?k��X!oe�NҜ �}5��o�}h�f�Od,�i�Avk��}	de�&�6 �f߶Imn��`��'D��f����68D��}����Av��N�x �6de?����$���?���f�Wt&8D��}���'D�68D䶅?����j*�8D_��aĆ�嶜?�f�Av�,Dց��%�o��}趖i鶜?�f�)g��?R��i��	�ڴ�6�{��ۇ�{����Àe#o��>�8|ΟAv�6�{�,D춂y>\"
�6a4�{# �e#=���&�y8
ɸ�Od��,M.�y� ��>z����}/�n���c�6 /�4le#�6�}�6�{m��>):%��c����BdM�{96����Ђ�)����be���{X���z9���Av���i�f��iz��b{��c��?��e#��b�_,�G�X�ξ��}�6�{k�\�g�f]�e#R��o�\���Im[�e����
�m8o�\��
{��&&뀘��6):|
j�e#�'Dk�\��e#k��Z�����\��)o�ʹN���k�\����$���Q�m8o�\����6�c�g���$�X��"���6�}P���$S��-��m8�6�ft�ʹ���}>������$@���c��諀��6�{$�����$��be!�\����o9�m8�$��<�v�_�&u�%�*DQ�ŏ�Bd]���):82�<|�
f7�%�!�驂�
5Z��7���<��
@9}��7m8o�j�$k�\�7�$7<|����׫ˈm��@�<������S2o�\���%b<|c����<D7<|(`8>�\�7<|"��o�*D	�<D� k��}��$����S�<D�&݁<�
��}o�\�U���T���<Dʁ�}�=n�\�7=n���N����Ev>��}e��$\��&���
7=n��41�{���%�}� ��$m{��}\�����ܒ�d7=n&�N+Ă�ag�[m�<D��%>��o<�<D�)�l*�u��f7�%7=n7��r	�d��<D��}7<|Y)h�ף7�%7޴���}�?���%7�$��
��	Ev7<|ރ�����|�W��{v�ʹ7f`�ٹ<|7f��b���c������c	�im�۽fe��<D7�t\7f�=n��iŁ<D<��
`�z��Fdc�Od7�a?��e0f �<D!7Fdd�iE�F	��5�$ Ł<D�Fd9�V\"��bۊ4S#7}�<$��b%7��&7Fd�5�R �U]P}�f`{ӫ=n���=U�'��i�Fd(7Mm3!k��
f��ۂ�c�<�)�
��Bm�+�_*7�b���y��`��`�Fd*�b�=+��i��I$�[mgFdD��XS�
fhX�,��x�A�,��c-�[m[���.��x/��b��07f�fD��O��۹)!�	;1��c�fԀ�ه�`
�}2��c肁b�=cf�/Fdo�}���ck1{.�	�feD۬��}�&�
)��]f����+�Ҁ��hf+Fd��b h37ffe��F�_{�47 57Fd�H���-���O��bwEg67Q�
�77 �H���c�gF��}�e���ȟ�� ����3��}{���@dD�G"8�_(�H�� \	f
��`(۴97 �����ʦ��}:��Ժ۞��҇�;�Hv��T$��)��=
���#z����ۂ5b�4۲_��+<���,x]<�g# �">!D��@���}��?:=7 =�"��$�����)D)�-:���)"2�O�~�����}���ܷ�k]$��q�>7me�����HvY�?:��)!��H�?7�a(�H�@7�ai�yA�Odg�.D��bB7 t@"ނ�S��cC7me��}:j��j!�x�D7mes��yme{�.D6��o����Ё�cE��yƁ�����}9��D�㴎�HvF��yG7 
)gb������:�H��}*�Ģ�۽��d��{H���۽ݑ�I7���hr�e{�N��T�iJ7�ib6�e���	�>��i���{K7ۚ�iZ$n��L7�bM7�+N��"z�>D��x��}O��{P�TT��a��>�ag������:��aQ�t���$�O����8�aR7�$�DS7�iT7�U��{V��a��j����5O�	��ց-h��pme���dѸW��a\�{G�
۫�X7$n��k�G�Y7�xZ7�c�me��{��b����[7�b	%�v��������g�{���\� ]��xv��a��{�ێ�>�|����	f��U~a��"�ܷ��ۀ����}���d娑"�y�)��^7� �_��a`��a7�i��i���"b7�w���p�)����}�[dȀ�^c7��@���'d7�ie�E�nw���"���[�>*��af��aV��a���%ۆCg7��h�	fx�Kd	�ii�kee{�W$n��KdX�e�j�Kdk��a$�%DVF��f5O���&���fl7�$��	fm��a�Bvo�\��
��5��a�	f���-n�Cd�Kd����ߡo��ke_C�o�Kdp�Cdq�%Dr�Cds�ke��ᣲKdӃ�}�Kdt�Cd����u�f���a��>ω v�Kd�Cddつ��+����h�CdA������w�Cdx7 :Q��=J�#y�&:�
k5z��c)zX�Kd{�Cd���b	�4a�}���%����|��af�`�5�:� ��b���z߂�}}��{��i8��f~�%D������ =�}��c+� ���KdQ���Kd��f��keD�+�%��cH�Kd��Cd$� �7�$5�2��ke��Cd��%5�2��m1���{��c�i8��c��KdE��=5�2�ۂ�Cdc�7�%�i8���b��\��g*�񘆢�R��b���$� ##�{�7�%���Γ	�ܚ�2��$}�d��P�f8�2R� �i8����!
�<o2���c�F�3�����?	�%ԗ��7�c��
ۢ!�c[t��Ͽ�&:�.Fv=�O��ێFv���;��{;� w�f��ی��b�G����}[����c9�[��5ak2��j��bl��5���´�h���{s�/�#z���?�7��a����8
/u����A�%��R$;�c����|de�j�7�a�7�<��g/X�����7�%	%g���hY����c�%i�$�?zX��b��"oe��$��2�z�ո�7oes�{
���7oe���X����`�Od�7
f� �7Gd����7Gd��W�ܜ7oe>+�a�7 �7Gdo2Gd"����&uoe?�G�]dӃ�i��	%�
f�]d���i���v
���i��܎
]d�7oe�7X����i�7
f�7Gdoe��
�
 �Od�7oe�7Gd�7OdĞE��X��de�Ym���oe�7GdT�Km��g�Od���y�7ft�#z=��֩7Gd�f��Kmm[����h�Od�7oe��R$�#zY��y�{��Ȅ���7�{������X���a������Km����� :��ݻ��㡁���R$m�{Y�f|�X��7oe��`�d�
f'X��7oef��g���ܲ7oe�ң>�u{��� �� n�7Gd�7X��7oe'
fH�%��i�7�{eq��oe�OdI�{�� n�7Gd���iv�{)�z>�ۇۏ�i���i�7]d��)n@��c5�{"���v�H����&���ֽ7!Dx� n�!D��"�{�7�{'�)n!�۹)!���vң��5r�+%��cW)!o۰� n���ae��{u�KmC
�p�!D	��b�9�f�v̡���7)!8�W!D_�7f4�m�
�{���%���"I"�7�{�0ft����vX�����%�{�7f:����m8v��+������`N���\�rSb��#�X!Dz��%"�E�)�z>�7!D�
ve'���R8D��cŷ n��b�z>f��ca�2�7�-)�z>Z��$�I"�,e��"Q�{���2���7�`��f8Dȷw�=����7�`k�� �������	!D��`���h���,38DR�>D�7�`�&n(�%�>|�,D��`a��˷Jd
�`O�i�S��76zo2Gh7��b�7����,D�78DƁ��7!D��>DзJd\"�%<�Jd�!D+�Y�E��7!DҷJd�fQ���-nӷ�%���7-n����+3���ČQ-nʁ�b��Jd%���7�i]-n��$ZңR7�$5��7�`�
��,D|�$b���7�$^�Jdط�$��!|N� ��ieٷ8z�Jd�7*�������4-n�� �7-n��ܷie�� I
��7�%(�$��޷ ��d��+:�9�'D*Y��7����$��[m�ie��Xpң� �>D+��� R8D�ie���	`�Jd��գ�)�c�'D�Jd��j�Sh��a�Aʉ'Do�"�f8Q�,Do��+�7�c�7�c��>D��"e�Kv�$���aЁ�2����c��k��,DƁ�����'D�i��aZ��+���|�7\G��.�$��Qj��ak�"�7-n� �7�c,��{y1�7�$��x��7-n���O�"�7$nဩx�x�P�2�������x�@3��6�9<Ё�a�9�oߴ@3���� A�Y�c�xӤQ�� :��&.�腀�めݏ� e����Q���8z�,):k%|� I���V
B�M"���):3��Q��n��u�G"�'Dk�"�	fׅ	f���%��M"����%D)�����$NW�A�	f%�"w���%�������a@�c�u�U�%D�7�"���a�!�i���7�c9�`��Q��%D�ң�7
 5�x��%D8�cЁz&��%�
´#�,�%D�f8�.�,��%D�f8Ȁ�	8�xӿ
 m[�kң��}8Nd8
 �%Dɀ&:�%D�
 �!Nd��f�d�4Sg��ŭ	f���X�򅀸㚀x��%D�y�=ރ{}	�%D���
8Nd��V��&:�o�=�8���+�'a´s*D�A����8
 ��
8Nd82�	f��8Nd��_�5�52�%D����i�me�%D��(�,�����w�8me��Q�8�M"�	fZҳ96�8mec�����$�%D�%D��%�v싀� ����	f��$��`��$���ҡ��4j!�%D�[����$���ϫ�i�ww�z[�������$O��`�%D��} �[d�F� 8
 ���!8Nd���_U��6z��G�Jң���$X{�"��{_�,�{#8�,~��6z��V��`��z��fPm[���'u�F�ADvt��$8Dv%8�$�6z&8�'8DD�Dv(��`,(=zƊ�`D����f�)8�a
��+��8zl�j*8DD+8DD���i�۹��b$DD:�x���x�{K��a�&����,��$脢x���-��i�´.8DD�$´/��.�C���i)�a'��Q��08DDI����U]��ң1��xl�x6۽�c�1me�	�o��8z���`��a�[d���i28��38DDf�����o�i�(|�6z[���]d�$ң*���o�u
�{e4��i5�8z68
f7��$8��i�����2���9�(|��`t!�a:��i;��$
f<�[d=8�aj6�>��i}6zs�[$|j�að?8
f��a�)n9�V�@8�z��v=z��(|��
g���"_䨁�R>��xjDDA�YdB8
fه�"o��x�zӑb�DDϒ�iC8DvD8�$"��b	})��E8�$F8DDy��迚�iG�ye��i�TH�YdI������iJ��x�*���bK��xdž�i���v���Z��L��x��"
�YdҀ�b{�a�8zĉYdM8
fՂ�,&��'N�Yd����O���m��bX�;O8@"��F"� =��%�@"�A�a��şZv���{���&.�褃գ+.̣ȃ{k�{K���.�Zv��Ё�u���ĕ
fP8��[��-:��`�AQ8
fR8��۽S8
f3�i1T8�`瀞?���۟F"�� U��&��zw��R��&V�ZvW��b��&X�)nY8�{g��	�9��	}��
�#�%o�cri8n����գv��yw�{ه�"�����RZ8�`������$��([�Yd��u\8�`�6zo6z	%m1�6����b]��b�i�^��b�z�&.��{_8�`��e`8�`��{��fa��y���b8�ic��{F
�d�id8�8e8�{f��yΥ�uD��{Z�+v��գg��{�ih8�i��y9�-�8zi��>j�8z��bk��{��I���)�8l8�i��X��9%�+܈.D��im8��գo�b�6z(���G�i���`��oP�i�i8����n8�8o��ap8�`�6zm�8����=zst.�iq��{k�E��$�c"������am�r�,DSX-*�3�k��(����)���۔�is�,D(���y��{t8 �"�G�`,u8�{%��$݃�[�& v�,Dw�,D�b~n�i?k���d}e*v�x8 ��,D^}e(�L��34S]d?']dy8}e���{e�v�z��{Ud�,D{8}e6z|�,D��{�B}8�i~8Ud��{�8]d�8�8-��ar�=��4'�]d�,D���{��Z�Iv�8Ud�8=z�
����a�8}e�]dH$)�
ه8]d[��E�f�8Ud�8}e%�
ۊ8۫F����k6�-6z:���8Ud�8�b�8z.�Ym��,Ḍ���a<��a�,D=R퐸�k�8�b(����	&��YmV	C�%��$C�����Q��(�,D�Ud���8��,D�8}�)���y�m~=����-n݀Bv�Hdh!@9�8 R��$��,Dk�8z��a�8}e���k?+gP�8 �8]d���8}e �)]d-ࣜ8}e�£���c�8Ud�8]d�դ���b�}e��'��a��b�UdjE�9�x�8Ud%�?ݐ�bo��+ŋ�/�8�b�8]d�UdȤ�Hd]dB��c���a���c���bk6z�,�8�,z��a���kW��/���b��?���a[�f;�?���k�8�,�Iv%�?	�c���y�z>��?��{���bo=zn�{�8�,���{����ȣ���~£�<Ҁ�c���*���-(���j��g�ѣ��y��
:�_
VQl	`%�?�@��j��8�,%�?�8�`�8}"۱8۸��	�*�{���r+�5��3���l,۲�8z�z0�$���{�`�8�{�`8�86zπȣ�8۶��%���c}۸8�`��?�8�%ʋ��".�{�8�`�7�գ��tB�8ے[�%�?t�Yr��{Ղ�b���b��?
�Q��'6`8%�?��"n��7��b<��g���-�Y%�?�8�`�8�{8�R�?��:|��{M�8�,¸XdR�?ø��{�8�,md,Ÿ�-o�Y?��Z��oƸ/:
�,ǸXdw&���-�{�8�{�X� ]m��%ZMv�8�$���2����%w��I"�E����LXϣ	��ʸ�-�Mv3��-΂Y��8�$̸�%d��>M��i6�"n���[z����a5�=�Mv�8�a��i���o��%���z�a~���i>�E�θ8z�md,�;nk)n����{��+&��8�`�#D�â���H���8�yI�$�,�`�a_��&Ѹ�-�zҸ;ns����X��+(����8�y�_*�I���-��H�k���'�8��5��)����)o�u�2�Mո�-= �-ָXd��cX;C��׸�-�8�a�8��8�y���޸��a۸۹�Xd��]d�8�b�Y%�-��,
O�3����8£�f�Y��a<��+�'�b޸�6�/�y�Mv�Y�lG��۽�����{\��{c�����v�8Mv�
U]���އ�{a���΀�.��{:�$�Mv8[��8fḩ8������{�������i€IJ��(���{�9�#�:g}�訁�8~��)�yr=]��4S���Y��Y�;n��{���>��u�8�y�;n���{>����8�y9�I"{��渇?����8�-+�y����T�ce����5��=�8�-�83D�8�-�8f[�U��k���%�����C��,�83D��{kY�_9�8fX�8�f���k�8 �?Y�_9�8�b�8 ��<���c�8�b���%X��,(��~!F�
��?(�������ʰ�,�8�6�\d�8 �>|���%�8 ���k�88Do��iZ�i�8\d�8 �-8D���k�  <���*��%:3��8z�8\d?��{���kQ��,�8 J��{���k��.����>��fQ��Fm6=z�>D9\d$��?���9\dB "�sN��kt33����F���%�>D�f��?�	}��>D9\d�
����?��4i;��?���4�	9\d��f[���/
��ke�8zD��k��+���k9 ^5mey�E����k��b�*]vu�7�)�f
��`�\d��,�f��,��k�	}��k�7 ��?�>D?��%9 ;�{ey��kX����^��`�]v9 ��{e���{��?9\d9 ��`�̣
�'& 9]v��$�h ��Fm9\d ��`���� �=z!9\d"9�?#��$$9\d�{%��?&98D'��?���gf=z\d(��`)��{���?*�fl�'D+9�{(�?ယb†�݀�/+�Y�8zN��{� ,�>D-9�{�1�{�7�l�Y�d΁.9�{:��_.�fr���?��{聢x�]v�i/9�{0������j	21�����9��x�d�29�{��+3��x.�?�&�*�{ŋ�io�Y���&���`T]c=u-���z��cg��?j�beo&4��{���?��ԣ��z�Y":�͸������£.��{�i,��i	��?59�{6� ��k7��{I
΁X��8��{
����>9��y���⁇^�� f��$� ����{�j�)��{�I�cI9:9�?�
}t�+��+�f��h���;9�{9��S<9�?#:�=9�{O�?k��u����ܠg�M�{�Z�%L�>9�{?9�O	�%*��@�AdH�?!A�f3�{�1�d��B� ���������_*ۘ��z��M"�&�����Ĩ)��B"%�	�Ad����e���C�毘�j�v��u���Q�4���P�f�i�������]�	f|�X��X�O�	fS�Ad�����S3��	��z(���{ k�L�׀3��9TE��y=���	h�D9*D�(z*��À<D�g�I9E9*D� F9�`�*D�΁h�M"�����2},��Hi8G�ע>Ndw��~�ǘ���B*D���H9i8I9�%O�`�h~�i8J�	f���"����K9�%����[� o����i8�����uL9��M�	fN� T���O9�`�"|��Zޣ��`h�{P�Ad�!�{��N���Q��yR�Km���C��5S��yT9�`���>����$[�%U��`V9i8OfM�(�a���{=z�W9�i��%���X9�{�,��
��`(z��Y	�a��Y9=z��J�Z� <�-[������z��`\9Dv�*=z��i�,��]9�i�� j:D^9DD_��`v£�t\e�i�ی`��`�j*a9�ap�z�b9DD:D�DDc9Dvd9�aD��e9�iЭ�d�DD��f9:D�A$�c=DD
n>g9DD���=n���h9�ii9Dv�գj9DDk9�`2�:|l9�i�	2o��om9�{���?n9g�o9DD�g{p�(|q9Dvt�,Ȓ��zg#.��ݫ̀(z8��DD�[�r9�{���?s9�ig�:|��ct��$�Ay�-��~	2v���=F�u9Edv�:|��w��`�ix9	2y9�iz9�a{��$|9�i}��/o��_��&��-~��/"��>Ԁ��$�;n�:D�)=
f9�i���/��x�)jDD��ہ9�i�Dve=���/�9�-�
f��-Dv���/�.DD�9DDw��/Ԃ���Aq�B�9:D���/�-c�գ5���9
f���b~X�(|o���f��+��b~���/��9�
�I�'#_
���o�7e�z����6��c�գ̈́�?9S��y@"v��>��F"&��`�*	2y���J�:�k,f��:|�`����F"��:|҇��9�-�
XҎ� n)E9�";ؾ�ִ��(��F"
fl��x�� n��F"�ۋ��R�-�9
fi1���xx
f�� n�9ۍ�i�9�?E� nS��]
f\�?��ܕ��/�d*���/o��xjۇ��q���D�ڣi��{���/$!D�F"��c���>���/%���$�Y�Ä�bm��/˝)n��.D������>
�9ܛ��{�9�{���� n}0f!D9���5۫���9!D����q>%���!D��Y�:����{��&����=��4��w��y�q>��m8Ɓ����q>�9f�9�{&�m8�9f�{ͳ n��i��?
F�4�8�� n)r#���?�i��{�!D�)�i��84� n�9�-Z!D���%��q>ތY����{��q>�o�q>���x���%K�i�� n	�{D�ִ[�Ӂ n�0�y�9�-�Fm�9�i`�9�`��&�9�i�ޣQ�8e[�c*�����{�9�-��\	��=�s�l>|�ST%�Zd���i���B
��/�>D�!D�9�-�f~�8B:����Y�*� ��>D���,D���i�9�-�9!D.�{�A��!D냤�� �9f��>D��s�m8�7��f�)�>D�!D�Bv��>D�9-n!)W���-n#�Hd'�,D���ρ��b{�,D��>D��5L��>Dة�i�9�iDz�i���a�ׁ{۶�i¹�iv��c)	��a� �9UQ-n������Ĺ>D���a��i}>|gC�	C�9�-�x#�9-nǹ�i��$�9>|�9�-ʹ�i�%�`9��-˹�a�9�-���͹�a2�Iv��
2ւ�b	��a��i�kι>DϹ>D{�,D��Hdй�a7�/��9$n�ke�x#Ԇ>D{�ҹ�aӹ�-Q��:�����>Թ,Dչ>D@
}���dֹ�-Q�,D���aT�Y��Z�8�>DT��׹,Dd�'ع>D���-@� 	}�����������$nl�6�۩���ٹ�-�Հڹ�i�?hs��9�3�Y��۹@vߋ�{���aܹ�iݹ�{UT޹�i߹�a�a,i*��i�aii*�@v��{{ۚ�@v���*��a��i�}$��a�i*���x��{���a�Iv_��i� Ȁո��a,i*��n�9u>�����{,I9�Iv.��b@�7���mu>�Iv��{4���,i*m��z5���y$n?��+��{+�Y�vi*��{˝	f"I95�=n��{�$n7i*��d�d}�Ad�:�̚�{)�%Dk�ܠ��{�d8
}<��%D�9�hZ����8�9��@�h��%Dբ����9�h·Ad�I9�9�iA���%De��*���Ad�Ad�9�im�T磆��n���{l�hԀ��Ad�����Q%�9�i��Ad�i*Y`�9�h����i*��{��93�^�%D���{��"n�9�h�=�[���%D��Ad�� ��n4��4�h�i*�Ā�9�h����J�E��D���b�b:�aS�hy�i�!�i�`���:�aj��At�:�i�Ad�����b:�b���r�%D�AdH'Uc�%D�%D:�,;��b	��%
:�a�%Dc�b�%D$��x
:Mv���b��o�#Dt�;g#:�b��i��b偹�Mv@�h��b:�h�%Dt
Ģ����%D:Mv�f:B"3��`��hX�:�a�i:g���bJ����Xd:Mv�3���,u�݉f�U���a��{�Km��b����?��i�#D�<���i����:�,��i�g�b����=�e���#DC��%�e�c��H��i���YЂ=�:DD:�{����:�?
�&��? :�{!��b":DD��b�	�{�{#:DD�"n"�`�܁$:DD%:�{&:�?"�k~':DD(�x>):�i���*:DDe�x>���b+:�i,:DD-:�_ʦ(|J���"DD\��<��{.:�a�DD���h/:�{YMv��{�`0��i1:�,2:Mv����x>�t�Q�3�)�h�[�`3:DD4:��5:�{i��<�`�7�{���Ђ=�۱g�ی��8��z�
4SfEdo�{m`����)6:�{7:
f$�k�
I"��{Ԁ��8:�?9��{::
f=��ݹEd=�?��y������<��Xb  Ed�(|;:DD8�q>X�V1<�)nV
ۀ�m=:�?�x>>:DD?:�{�
f��+#DDZ�	}����
Z�	�x� #DD��)n��{��摫ׁt��,@:DDA:�{N�G�"��,Q��B:�{o���C��i
���o���:DDؚg�v�_�XbD�(|�\d���<���������(|
��'E�(|F: �r$��/��
a�3
fA��c�����#�+�G:�bʈ�TH:��8%��:�^������I:\dJ:��?�+�ݾ��&t�K:��*�{k��n�����f����L:�b����EdM:
f�\dj4����kN�fO:\d�h�P:EdQ:�b�������f3�` &����iĂ�&5\dR���/�`/\dS:`T��yU�f��b��q�V�)n�7��Xb<�zW�f>��X�)n��k���Y��`˩�hQ��,Z�)n9�k����`[��,Vf񂶪�����E����)���\��-<��k�&n��f8��D��im�f�*ַ
�xp�
�<�#7�]��$���^:�b*\d��8_�F"��i�[Q���`���`��h��x���h���m[�Y��$��h�fa�F"	`�θd梸��,ɰf=3��k��h�����+���h"�-��(|>b��h���k����$�v�_���c��`��f�!�-"��k9w3ĉ�ad��a��+΁�je��,��aەq>��cf��kd��a��'D���k��-g�Hdh:��_
��i��j�,D��$5���k:�`l�,D���?��a��Jm�Hdn�,D���?x�+o:��p��%��aq��h�� �Hd��-o�ir�,De��g�s��ht:-n��9�>�,Du��$i9
}l��av:�-X��al��$J��h��Xb8
��w:�-x:
}��9�:�Hd06-ny��$z:�-I���{��$Ȁځ9�|:�-}�,D?�(� ������~� Y�'D���:
}l��kj�'D���h� �:�-�ݨ��?�-t�,�ہ:�-��*�:�-���ap��a����%���c
���,��*�`*���`�:-n5�=u'�Hd�� ��,D۫�ځ]��`bf��,D2���f�:*D���`�� ��k��,D	�?���Ջ���[��8o����� ��g*D���d
f��,D��Hd`���ʕe�,D.�f%f�`*<���*D�:�y���$�:�-�:
}�:�?�磔:�-��{�@��Y�:f�*D�:
}�������4�b�:�-�:*DS�&�:K"l-n�:�-�:
}�:
}��h��Xb�� ��	�UA�:f���:��F���	���&���*��*����h�Xb	[��:f��f��f*?	��:�h݄�Ģ:�{��+ ��&���`*���Z��f���*�`*:�h\�x	[�����`*���*�����ݸ`xk�+n
�z~P[��:[����y�:f�:�h~�ި:*D�:�fi`*�:�h��x��g@9wf�SCd�:�?���$�"Vk�:f�.�a�t���a�YuF���h�����Xbb��?��ܭ:�%������-߷�>��J����X٬��v��t�d�	7��&%��x�:�%w�Fa�:�k�����Xbt�kz�z��%��>�:�k#�j�L�as���:�%�:�h��h�:�a� �+�[�%+�6��f&�a��˴:�>�:�k*�*|��	�ݶ:�%�O�u�>�:�a�%��{�:�k8�/�h_[������:�k��:|�a��#�%ףk��:�k��%��:|��k���i�.�k9��G]m��:�:�k3��ј�f_�������b~���:|�k����ֿ��i�Cm&t�R�����i!�%Kl�q>�k�做 �[�摸[�b,6z��n�:Fa��%#�a�:�a��i~=��|��`��bú״�:�a�����N��%��,<1۽���iź;n�:�%Ǻ�i���-�:�>���-��ܢ�%��Xbo�k9{�w��Q9��N��iɺ�cb���	2g�kȀ�=������8�b~�:��[&��f����9��.|���i˺���D���-�:f���+��c��S(~~d[����"���Ё�+Ȁ߁�4`1_��=���g���N�%*���$u�%��:[�����?!
6zA�6��zκ�cϺ�?к�{g��ݭ��bѺ�{u��?�f���Һ�i[�5��*
`eӺ�{���i�Upr�x���.D
��4�����k6z��{䆡c�*��Ժ����7��߁�~~9�����xպ�i��-���cֺ�{	[���i�`�:3D���&5��cS��&“i~�^�8`x.��-غ�{`��d~��8���VF�d��ٺ�c��?ӏ�܊��~~(��c.��?:Cd{��"z�8�:~~��+Z�Yy�B��:3D5�Aܺ�%>3D�
+$u���/��n�d�8�f�:�h�!D]6~~!��+���x��xh�E��4!D+�x�d偸�8Ӑx�O��{h�E���޺8���;��R��+ߺ�{��xӶ�j;�x:�<�.��?���%��{(��㮄�GP`�:�i��c�Tޣ���%Q��xӘY"
�8:�x���{I�.D���� ���ޣD�� �be.3D��� h�EB�����v� ��Y�Zd���:�i�:3D�>D�X-�>D�:�i� �gȤ�>D�:3D(��t�8��>D� !D�:2��b� ��x6z��
t�Q�d6�Bv�:�c�:�i��z9�*�>D`��g�c�&�)�+o�E����a��,$�x��:�c�:�i�>D���ag�i��a�
�+�>D�:�i�� R�<�.��a�U��	�c�������c���a�	�c9���:�i��x�:�i�: ���a��E�
��.+�x����:�c���aه�o�:�c���{;�?L�#D�+|��mUdL��+���'���>D;�c�Ym���k;�id[�:�4�΀�{eC��$�iP��֥�[v<��a��+�ң�>Dc�Y;�i�)��+�
2;�i9���	�>D
;�i�����`Dz>DX��[v( :v�+�
�c;$n��	;�{	[�m`
;�i��lz8��!��c;�i."$n�cg6z���	�����{��'m~m~5�+�0�,D��QX�;�c;�,��a;$n�
�c�����y����\�&;`�Z��{X5�+��ՀW�Ȁ��8�[v�a#�Ym��;`;f*�b́�o5��vZ���DZ�i*�$f׀�$�$n$�)#��)��{���o쌕ej�z����bi*;���&�i*a��y��,���n;����;$n�1��c��h��{�f�Ad��b;�{;�i ��b��x�!�Ad���I
�Ԁ& ��cd[�"��cB��b�{3����㫀�#��i��͒��	$��c�Ad	[��)Ҁ<�%;�i�����x�\��+�Ad��fI��%�x�Z�p�$&��c�����+'��b���c��b8�Dm��s(;�h���ɟ�b,i*_[�)��c�
����*��c+;f	���iY�"n	��c�i*���b$��x9�w3��6�bZ��+,;��Ё�삣c-;�a"�"n.;�ݎ��'���/;�af���y�a0��c9�3�1;^d2;�a3;�%��I9��	f;�x�*�ң2�<���� ��c��<�2ĭ�&z4;�a	$�%�ۿϣo��i5;}~6�f�b�:�{7��b8�	f(�%9��c_���:;�a������a���c�h;;�a<;�%���j&z=��b��h��x���6'g$8�a��[����+X��ƣ�%8��>;f냷����d��0.��i��0�8e�ac��<���%�a?;�%��i��a�[�z��c�-R�Z�@�E�Jϣؕ"n���7�&:�;��9DD���c���h[#�A;:D/DD� �����DDy�aQ�+t�=B;DDj3:DC;�aD��`E;DD��%�&F;�a�3���n��G��c�	}~H;:D
���I;�aJ��CK;DD�:D��(�%�aL�:|�_���^Ȓ�����nM�~8N��a�%)}~�&z�:|����:D��%q*�E�[���%뀼x�:|#�<�A>��	O;DDЁG���&׀E�.-
f�A>P;��z��%Q��R�:|S����&��Y��Wd^��%92ò�%T�~8U��hs���V����,XI"n
f��I���m*
��i�uե�&�i�" .���DDW;�b:D�(|X�:|�c�j�@DD����t�(��:�Y;DD0���OdZ;Ed��b[;:D�	}���\;DD*Wd�&����];DD��c9�g�_9J[����e�}�,�.ā&^��a�.DD�	��:D_�V ���%��dZ� !~��`+�<��)nc�۠�o�������cM9��ڇ�F�`�(|����~a��a��b�-ub��b�8#=��kÄ���݄�:|��l$����| ��b��o��%�;��yĵ�o`��z�cc�f*�{K�#�0���bW�\mm[�o�E��mxd��be�)nf��bg;�b�F"ဂ-9��i��+3�H�y�b��m*�\d*Wdh��b��&���r���	i;&z�f����H�*���ցi1Ȁ͸���m��bʥ�&H�)nH���&zń�+Μ�@w��D���hj;!D����+hk;�h�ۅ��n>�����l�&m�)	V�=���]v^��,��<׀�¾����f *�����\d>����
�!Dn;�a���o;8Dp��b���i��'Dq;�a���Jr�'D�?�@;Omˋ�%���h�ˀs;�a��Yғ�%զ�b��%���t;\du;�%v�G�w;8D^��bY���a��	�a
d~��%k��	�&��'Dx;�a���������߂F�a�$�y�۾[\z;&z��+s����]o�&�{[9���{;!DP�f|;��Ŏ��};��2�!�y�y�>D��o9�P��;T~�Zd(��;�{�;�a�h���+g�<��):�;'D��Zd��<�����Hd):������h��>D(��	��;d~�>D��Zd+6z��be]��b��>D%��c��Zd���>D��Zd��be;6z���Hd۬�h��'D���%��Hd��d��>D��������c��i�U��c���'�ۗ��i���'��`�Hd���㒻Zd�;�aC'����,Dc�a��Zd���'c�ң���&��M"�;�a뀵"��ݙ��'��Iv\��>뀵"�d~�Ӂ�#�%|��'�
��	��Ć�'�-nNO�뀵"�;-n��>Dw���.��;�>��Hd��Zd���(��>D�dl�<�݀M"%�ZdY�ܠ��&��ۂ�ܡ�>DЁ�o
�b��>D%��b��Zd�ۘ�j(�����xӔ��Gy�>D��+뀑~��ه�-��>D4�Z�x�;�>DQ�{ڳ�^����*���@v뀵"h�>D&O�V���`ŋ�i��+>��`\�x�ւ�':�x�.���;-nϋ�`٧�`*-n���&v,�YΨ&��Iv���&��L���ˀ���	&�	Œ뀵"��"�*D����Ө&���<�M"��[�x�M��/�	��a>�;�D���'��Iv���䥆�����"��&���#ݣ���g��D�+A��"�m,@��&�^����Hfrxւ`���"}�xӚ��<�&Ёy���AdV
�D��<��̑V;&���:���l��:�<����R&��xӬ;�D�;)�;c��;$n�;�a\�x�3��ba��9��z���:�dЁy���Ad� R�xӲ;�&��xӝ�%DO�Ad�[���Ad܅��x��%D���`+h$�&�;)��Ad�۷;�b�!��#6�b�;�&*�I9!�/hӃOd�;�a���F�(�[���z0;^d��b�;�bTg!��+f6)�;���;�bi^d3����bo���bQ&[��;)Ԁ⭩�E>}�I��
�b�	2e��9��1�b�;�D��&f�/h���A��&�bO�/h:�b<�/h
�&:��ic����;�D��Ҁx�U%D��#��G��6zD���	2��Ad_�/hU�Ad�D�o�ۿ;Mv[=��Ё(�4�7�a�;	2サ�o�	��a�[����-:\�Ado)%t%ەx�»Ad8�G�݁x�^d)�E>��Ū��&��i~�0h���+*�&��%")�;Mv�;�b��{B	�b;�b�8@"��+=��L���Dm�Mv�;^d>�G��+�c\�i~����;�c��c���_��+���$<��ǻb~8�b��\'�+:/DDȻ�aw�b�;�cʻ�&<��$��d˻i~/���$u��T����;DD�����G���x�B����;DD�Mvk���:���λb~2��a��!�G�ϻ`~{�i3�H�c��a�`~�oeĉ`��_;���.
)o��+�;DD
�A
�\"	2 :DD(��x	u\��x�R���>��xoL�o:�����d�G��;DDm0h	%�һ}
/�]΀�����S�yS��ao
	f��H�df�����3Edӻi~Ի}�;Ed�y	f�;f�i~y�������Ő��ZĢ׻}�;Ed�}�ۨ��ٻi~�;DD%����{��;Edܻۈ�f_���;Ed���&�z��)n��̕CDD(�������.�7DD��&\����;X�b%�a�fY�f�z�+DD��y�;Dv�M9?	�Oۃ��X�`�fB�����<��	5�jsDD��G:(�a�;EdԚf���:��%�;DD%�a}��x׀`~j�
�À�x���o	}r��_��TT����	��iU�f9�̡����}� t�o�ۊY�?	�Z�}�r�m� ŋ.D�;f�EdL��dF�a����a���4f<��٣� *�'2��� ^�x�;Ed�Nj���8[�Ԫ ���i�a���;Ed��@���aۅ-�a��a7� ���w�fӧ�%�;m~�;��w�f��;m~*��9�(pTl|V��;T���զ�$��`� J��Yy�
2�G�"J���
�
�G��;��o�)n��a0h�a�����o�f���� �ac3�%X�:Fv)Ɓ���a�xj�;�i��%��������;�a�Cd�a��G�$����m~>�G�J�
�xj�;1D`�����d�Z�v��+���a����k�2� 0���[�d����� ���a߁
�l���_������k���a�C�M� �"C���
�G���Cd���a.��5�)X�a.� k�I�(���� ���J�+����;�_����a���'X��)��a�`o�E������xj_�7.
�G�(�6��Ԁ�i�;�%\���,D��ij�-���a5�CdJT��%:��>��#
�G�8��a�m~�;�%L�W���a���h��a�G�t�Hdo6n݀�a<����<-n<��f����b<-n�����a�Hd�ˁ�i6:���$��Ǿ�ƹ�Cd������z�&z��K�HdH�v�<�bb�b��+<-n)���L�����݁��	<�b�6�
�Hd�	}"#�bT����	C�L��<&z�)ud������{�>g�f;-n�ˁ�!g�)u�b
�;u�&z��c˜f4�&�j�xFn��I<*�J��a�Hd8-�x[���z���(����Hdk��<}��t�ܸ��<�bk��}�ף���)���9���Hd�	�i��/�ˁ�}<�<�i����f*���bs�x���;�V/+����̢ϓ���Hdl�f*D���>�G��Hd<��>�G����b<�il��������"n#�	���c����3�i���R�"n�������<�hg��<-n<K"�ˁ<}��⫀�	�����"nm�8դ<+!T��!�G���< ��c <�C=�,	�&A!�"no�Y|��3*�,"�"n�	�+!�#Dj#D)�h场c��:#D��<#<+!��Ġ�$#D$<�h%�"n&�E��#D��"n�&֢9����v�L��aW��cm!g��̀�}:����#D'��a�G���o��ߣ�#|(<}��<vjc+!F)<F�oe��b*��a���i�#D+<{�,��h-��%9��+:�'���	"��,(�����c.��bo�G�!�o/��aR�"n	��>G#D;��c�+!�X��{�%��'Z
,ʉ�c*��/���cj���o��A��'0�#|1<�a����<8
+!?��%��d����2<�,q��:���
+!3<K"4��c"���K�#<+!
oe��&����#D ��_X�����#D�6z�	�,����#D�5������coeډ�a..�()#D<��:�Y�@#D�oe�Y�6��b��c�,7<#D��<c
 8<���X�耸c�݁9< ���&@��b�-u���:�#|^����X�g��&�݁���i��h���ތ�ƭ ��:|�vj��&��*�����e�;��i�����ˁ����I =�����fo�<�:|YI"݁�����`7�=<Z�k�"��,��*>���[�z{��
z�{���쁘b��όB�~�?�G�d�	������
�@<[����	��m[�����?���D�ף��i[�z���\!�d_�3A<f���%������,߀�hx�I�θB<fi�`����c���b��#�80h���C<I"�8DD��`ݝ�n����܄�`��}Q���݀�:|�3�w�cE��`��e�O,ʣ��I��`��%j��	�cF�G����l���t�f�+I"�* �Y��_
 G<�a�8DH��{#��`{��D���g��b{�3��1�\dX��b,�*B�fI���;'�av�J%�{Ą���Ʋ��������	�n�9�	�5b��;nV�9F$�7�����z��{o��%g�;n��i~z���$���J<�aK�;n��i~���r:�`��.�a�'|���<�̢]�;nh'|�{���Od�N�	�G���9nP���L<�a���M�	}R���������N�'D:�'��xO<�P<fQ�'DR<�cS<&���ij�'D���*����'DT�
)M�@m��U�
)�'D=��V��`q���@�
)W��X�� X�'D���KvH)!*���qY<�&l�����'D�0�'Z��%�
)[�'D\<�b_���G�']�'D^<�b%��D_<�&��b��+Q�'D��`<�&a<�bb<�d��cc�'D��2d<�'���'DL�S:�'�{��5���Kv�be<�&:�&f�'Dg<�a΀��h�'DJ�-?8�c��#���H��i�Zdj<�ck<�cr{�l<�&m�	}����.�
)_��n<�b6���6������o<�c��b.�
)��{+���p<�bq�>DցG�ʇ��r�'Dŋ/h�>Ds<�c�'*��{h�_"L�>D�)t<�i�'DC�'D=�"���c��u� /��v�Zd�?�Y� w�'Dx<�'���Ջ>Dy<�&Z���'����z<7���{
�cg��{׀ ��b��&��cW)���{<�'|��{x�&}�M"~� ����� �b9��w�c��b�o�h�vr�<�c�� ��Zd�<f�� ���{��g�� �ԁD�Y�����Z"�<*D��d~�<�b�Dm�<*D���{՝/h�� ��Zd��{�<�d�*D�<�b��F�bψ>D鑆�<*D�<�b�<�b��X^��{��^�cDm;�'Q�Sɏ<*D<�bU	m~���<�<fQ�w3����bӢI�}j.m~���i*D.�b��_"�<�x�<�c'f�<�b��c�<�镼 �NdR;�,��<����<D�<
}�vj���f�<f;�}���{�� .f#� ���-�*D�<�x���{"��y�*D���Л<�b�*|A�bi*D��7�[����{o�o���{R���o
��^��Nힼ�{���4��G�@�<f��	f�<���	�b	:��f�<*DDm�Ndt����<*D�<f5��$n�<Dm�<f4�Ad��	f�	}�<f�<�+�����}�H���բ��΂*D�ˁe�	f� ���v8Ndz��`�X�#�rӂ�Ad����`��*|�ԁ��$k�+no�������i���iW�/h�)T�a���i@�h!)�ۮ��i���xW�/hҀ�Ǔ��i��F9[���oe��Adۅ��ir�Y*s�Av=�w�g2%��'���io���o�ڢv�=�<�a����i�<	2�<�NЂ	f:��`���i��)�<�<X5��<�a��j���i�֭"�)v&	2Xu���������f����i��{��	f{�� [��i�<�{�<{�"	2��Adk�ۻ�)��'���iA�{��ɨ�a���i��+n8��5�!�X���a�xj��4h�<�{.��-���x�<o9������0h]�{~���?:�<�a�<�{����a���{~`���Ə�i� �za�k�*�i�<�{��4�{~���i'���^d�5g�F��<�i��Qo�J�Vvx�ϣ�<�{�{��<�i�VvǼ�&ȼF9YDD�ˁ��iX��Vvh�?:ɼ{~�<�i�W��<�ie��@�<.|BDD�[�o�G��@"w��?[��<@"����ae�����d�Ӣ�<Vv�<Dv�<DD��a�(DD��,�<.|��y�'DDҀ��{��<�x�<DD�.D
�բԼ��<�{`1u\�<�%!�G�w���<DD�<�%`��a��<�{���&{���{�<DD���aۼ.D#DD�<fg��&�<�{�<DD�ˁ�<{��<�i;"����^
o�G��<
f�.|�Wd�;�`�{~�����&@"Vv(�.D�<�i��/e���<�i��
���/�DD>��>��8�<�i�	��Vvg��/ :Vv�<�i��Em16Wd��V��<Dv"�f����ү�/m{�,
sI�<DD� W��<.|҄f�<fU��/��{{��\��{X��/��<	H��<DD��f��b��aV��/�G��<�i��/�*�iƥ�{�{�� D��-�f��8�%�'�b��i��.D|��/���h��{_�����.D��/��{Ђ�́ �`���ja��� �<}��5��Z�}j��{�� �� ���j�Wd��x��E���{� �<�i�m�� a��{���{�����/dX-U��b�!�� �Bv���b�<Wd�����/��b��/pY"Xf瀞bĚ�/Q�M� ���{{�f�<̢t��{��BvU��/V�f���{�� c2�»�/�Im�� ��{���/�"Y"ʉ)n%�����{��i� ��x��{pY"Հ5�pY"��Bv{����h��{[�p���{HU�(���*��l�
�f�&�����a�"Y"�(n��>��%Am%�
�j3 :=�<1D=}���a>�f�}e%� v�Y��z%�����f �a���/3'Ud	��z�1D�X���:�d���*�Se��>(��o飼���X�
���td~Қ4�[�g� ��{����
=Ԁ�l��� ��apY"Z������d~X�Hه�J���pY"s��{���>pY"�������	۽�ZdȀ܁pY"���zp��
�>D�,D�	)xUTv"�@飽U���瘀�a�{���Zd�&1D�>D= �Y�= s�\�'�Zdm�%[�b������>�>D�Zd� �>|�?��Zd����/Q��=-nݗ>D��܁�Zd= ��{�EΌ��i=�{*ە�g?:�i˺��83�{)�b��if�5�{�Zd�
�bgX�r�p��>D��i��z@3X� =f!��i���8�b��b�fo��z"=X�P[���z���d �iw�}&�b@3X�D�{�[�#=X�$=f"���X��f8��8$�Y�;��Zd%�@9&�>D�!f'�Zdg��&��<�罣��i���8��8�&(��iv�}"��8c�Ca��Y�)�Zd���b��b�-�i�X�*�>Dc��=eMm��>D+�Zd���bl��.S�Zd�X��[�F��hٮ�,��i�[���i�
�bX�-��i.��Dg�{}����[����f!
�X�{Ƣ�D�
Mm/��i���80��i1=��Ѡ�b��8Y���ۀ5����b�
����$��ԁ2�"n3��D4=�{�ۢ�
��b�Gd���A���8"�=h웠j"�}%��d����y^�Iv���05�Iv6=���ad��x�{��X��#D7��x��f��a;�Xd���x֑�%8=f9��a���x"��%:=}~��;=f;Ӣ3�	}((��ه�%�
)�Y�Gd
��x<=f5����5|=��a���3����b>=�:��%9���	}���]�	}?={���zk��Z�@�Ad3��x�xA��x\GdA��aB��C��bD=^dm�Ad���܁�D���z�o耴D"���ۨ
�z�hE��D�aF=^d����Am3��D9{T3�?z(�7��H��mG=^d	�{�H�AdI=�4�i���5b!5|*kF�
X�ʇ�{��L[Gd��h3�i85|=�|���{�i��MJ��jK=^d���(�騃�xL��%v���{�P�8dM��aҀ�{��x���x����
���O��%I
ԁ����:�ۀN=�i��9�O=MvP�	}�#�hv�4�5��xQ��{�7�����R=I"S�	}D�&T� U�AdV=�W=�i�� ���AdX�	}R�{~s�t���բF��{]� Y=I"�i~Z=���+�%��C	�8[={�\� ]�i~^=^d_� m�>�Z�`=�b&�*&��€��v��T�m�A�{~a=MvQ^d��~8���>��o輁���9^d����b� 	����d�Dه�'c=DD��d=8De�4��摯�d�D���f� ���g=DD���T���i�DD�8DU��k��(I�܁����8D�{��8D�`~h=DDd�D�DDi��j	�Dj�}z�ik=Mv��kl�8�X�,��`~m=DDf�8�8D�����%���U]n=8DZ��dQ'Mv՚�h�"8D�$��o�@m"��z�� �DDX�3�p�8|:8D�,q� ���hr=8D�@mI���6��s�9�+� t=
}(�)D�ܰ�� T�(u=8DR���v=
}l����� 뀼�w=Dv��(��8|��Yx=
}y�}z=
}$������`Y�`~�o��i׀g�)�'�k	�D���`��j֔f�{�'DWF�����]�D�'|�o3{�|=Dv
}���(+�9n}=DD�Dv�)��i~=8D�&���@mt�}oDD�d����\d=8D��'D=���)],\d
��h��i���i��}o��@��{��'DI
⁃�8|B�@mH�8|c�{:���ʐ�H��`~����՚@m��_�=)ވ`~��`՚@mW�'D�=�{��x��<����T�}z�8|Ɓ7.�=�{��������j�3TƁ������=
}������k��'�Y�=�{Y):��ܖ���⁊=
 ��	f+)u�EdD�=b():o����ۿ�ܣ���%���Q��=m~h.�{���=�tA��	fV�	f(���=�{�
):���{�%m~��{
�_e���	f
����h��	f5��^��E��	m~^����>� !(��٨{�)):DX��=m~�
�D��>��h�����k��ϋ!gI�偓=���=�{'�M"g�{��,��z+�!gt����{�=�b��+T�B�_"@�!g����8��(� �e���,�=�{����,k�ۚ=�{l����{���Od:�{��M"
��Y�F��8��M"�{��=Nd�� D��md~�5*D�8�<|Dm��8آ
�O�b���,�!go�<���!gU��`>�^���8���,5��`���,J�����Nd�8�=<|�=�b��"u����_�$���o�"uNd��<D�d~����<D���w�f_��`�=<|W���oj�_"߈<D�M"�=<|��`�=�`��<DNd�=*D9;<|��"=n�!gR�!gկf�=<|i!�`8�I9S��~���j�`J��,��C����ϋ"u��"u3�`���,���׭=
f�1�j�<D�=� ���,"�<DB!�`f�<��#D��+?�����.N�"uv��+n���jZ��$�&��:e���D*��X�f�:粽f#{�S�bi�<DV(�/&��`�fQ�x�<D�W9�{~�=<|�Dmϋ�����j�
��B�z�f��+o飵=*D���D���jם$u����k�+n���O���<D_��	���y��"u��<D�={���fŁ<D��n����T��	v����/�`3�a ��<Dz�ݡ��ޫϻ�����IvB�<ܽ�"u����<D΁&�F9�= 2I�*{�����0�E����=
fƁ�m��ݝ�c4�`�0A��w51�{�R���z�ݎ��y���c:�����(+���
��^��D�	)щ8؝&�Y�t�= ��+�k�&��	)v�b~��C쁩j�8���y���D��L�,�hZ��J�������`r=���½�D��8�a�&)�{X ܒ���?:��&� ý�D#j�Y-j!�$!{��d�X�7��D��f��V�?:�[��8��?�����yȀҀ.�� BK" �{��
�(�=�����
�>���@"\���ւ?:Îz>��?:�� �={������o{D$v�ݙ����6ue۫���=�>\��ǐ����=.|��l� V	��DX���^���^Ƚۓ�uR�=��o��X�|�[Q���5�=R"À<�@�(�{��=�6�����́j{�.|�᥂8�φSTX���m8c�D��>�X�e2~��[�=.|B�����5�h��u\�=�z�=����{*TT�bԀ�O�j9�>�= �_�S�<�1�yϽ�&������*�j���fD$�=Mv{��'.���������v����'�Xv�.D'���y��d*ѽ.D�fҽ�i��>�v��=�{�=�j݀�%�X���L���.��i��)���y2��=Mv�=�D�i~�$ *�Ȁ���{���-J�i�=fؽ�ie�;n����=�aH�>�=�b�X��=�b���	��=�>��#'�;n�=f��Б޽�$�f	�b߽�i�7�ܛj	X����=�i�=�b�=�{H�[m���T��i3��=�b�f��/�`X#�y��TT. ���=�`��Lm��D��=�`Y�y罔C�Y��� �) ҇R��=�b� �=f�= �a
�a�=f�� y�Bv��{�=f\� �= $� F�Bv�i����= �=Mv��Ё��� �i�=�D�=fg�{i��i6=�D������{�:h)!(}���{�� �=��(g������F���(gڂ9� ���{��bʊ������{���g����{~�6�=�i������;n� �=�b;!D�'!D�)!>�b����� r��<��Bvb��,>�b��{>1D������{{X���%��yl�����>�bT�E���%���>Ud�Cd�Cd� f	>f�h#)!
>$u��>?W�ۇ�a�
 :v�Cdg1D� ;� L!Do�Cd��&�|� %�x�E��{�(g%ې��~�Cd���j
��{%�ل���
)>?����{�v~��4���{ʋ٤���V���(�oeZ�Bv�	2�CdȀ��Q��	o�����{���<��x�>g���{����Cd΀x�R�'��Cd��X���΁؀���	h:����'����'�T��>D���>�i��Ǭ�me>�i��W����i*���g�xө����!D��>�i���������Jc���y�Zd���>�ie�4�e��z{���>�i�
Mm�� ��Cd���>�i�� �z
Mm�Cdc������%��%��x���y>�b��׬ : �Cd!>m~">I��Cd#>�ixVB$�x�$>ף%�/o�+&�Zd#)��{.��
 :l�x��F�����'��{(�[v��i�Ī�$��݁x�)>�mЂ�:��x�"{�d�ճ1�<����<�h�
Mm���������Ȁ��5�����{*��*���Ђ�:�$n�ݙFv+��{,>�i->�{.>$nm��]d9��/>}�Fv��i���{;�{
ִ���@����0>�i1>}��h耘S�Zd}�zj�c�2>�{w�mCdyMm�����Jc3>�i��h_s���o4>�i�Fv!
�ܚ���^}�$n5>Fv�
�h��&$��h�m~���6>�{yMm�Mm*{��*$n������	:��7� $ɣ�b���Xd8>}������:2���=��x��|�����תD{�5�pӃ[$9>#D��"n:�/;>#D%Gd��b@�<��b��!�	=���`�h=���d�dɚ	;>>#D?>Gd��{����-�{
� �k��"87��@>#D�X�]�"n�	f��⥛�aA>��S���R��i��{=��a�'$n[Gdu%�{�B��b�YvC>#DD��aE>Gdw$�"�#|O(���:�y c��z� !��zkF>#D��dÄ�iG>�{�"nH> I��a.)�����	fJ��h��i�#DK>�{L> {�����{M�#|�x��#D:  ���J4i#Dg�{Њ"n	�dj��a)������6۽�)uN�)��d҆4����O> P>�{�#|���4Gd��jQ>kF��R>#D�Gd	�dS>�{T>�{U> V>#D��b:���W> X>�{�����#D��b��`Y>�{�"�az���{����x%#Dd�>Z>�{[>�aN�{]�>z�����D|�{������iA�j|�9�ʇ��ѐ��\��a�{
=�h��X� e�)u�'�>]>�{Y��^�#|��>�&�i�y���!g_> `> �
�>+�x��#|g�����@9Ԃ*y �dZ��+��{a> X�uC\�{3 ��>���b>�{�:��c> �6fd>��g#���ie>�{��+�����hd�d�{��=^d�?���{*���&:׀��'�{h���߲*
&n��df�)���Q�cg>DDՀf�h�4�i>�d�DD ���z?�j>&nk>DDl>�>�����j�DD�8Dѐ�����m>�j9�;n> o��a:����DDp��>���R�C������ ��{������p>&n����q>DDr��z�
�j��_s�Jdt��z@2�a��bu>�`w�[��(|�b=��zv>�{�Jd��f�DDw>��w�{x>�'�T�`������ڪ�z��p�y��z����z�JdV�����i@����b{>'||>�aڡ�z�*}�Jd���h=�j>Q0�`\�a~�Jd�3�j���>�`o�!g��(*�b��`5��-���h�-u�>
f�����>�ae�Kv��)n߀�z�>'|,
�-u��(Y�b�4�`|�'D�DD��f'����'D�&'|��Jd	�DD��'D�>��n
f��'D�>&n��	}�=�ዾ'D����	}����	)�'D���㍾�a��'D�\d���ߡ��h��c��a�����`޺'D�(�'O�f���z��'D:�b���l���L�'D���zm��a��>��'D�KvY��h��a���Ǔ��`܁�z��'D��'D+�'[�蕾�`��'D���`��ߣ�>�b��Kv��'r�+���{�>�a��'DO��aԀ���'D���`��`�
�n:� --u��i��L8
�����hQ��q��դ�):�	}j��>���'|�����̍��G����X���x��'DW�>�>�&Б�.͍%mK��{��-�>�&��hŐ�o�>�>	�y�m���>�>�� ��M"��'Di
���	���ۥ�'D.��{���`|2���>��)���γ'D���>+�'D�>�&b�>�>�>>��J:7��Ђ�f��	�>���橁z>�>�a:�3k��ۆ��>�>�a���`���>(�ߣ<�M"Ȁ�	5������`J�,�>��F�g�M"e[d���>�� Ђ`w��� ����$��ւ*D�>�*�� �|�4��>*D���`������cNd��a�>*D�M"��TT����m��,T/*Dh��>�F�4d~����{V$�� _�[���+�>�Q�	d~�>�&i*D_�>��F���Jc�����a�_)?D���x�a�;):9��C�9*D�>�>�w��,D�+*D��Zds�F����*Dг,D��<D�-�*|m��ۤ*D��`�=*D�=n���&���ONd��ȁ���>*D�>
}���d�۾>-n��F�U�,D���>���>�>*DȀ���<D��	¾�>i*D��a��o�"�a�>Ov��`ľ�'�&�>
}���y�N�s�F��>�ay-n��'<
}m�8���!Nd�>*Ds�F�^�fk�*D� �>NdS��'��@Я*D��+n�Nd�*DW�a�Nd��+n(�,�>*DQ;۽ɾ�'ʾ�j��+n�>�z�`�*De�,DY���̾�'���;�'�>*D\Nd|��ՏɸϾ*|W���=����>f�>�a�,Dg�x�Ҿ�'���G�֣���x��&��'�>�k<��oU�,DH�h0
} �x�X�yoԾ�b�>
}�>�a�
�`�׾ nΟ n@"�Xؾʢh�]dپb~�>�a�&9��۾ n �` 
}�>�a	�mD9ݾ}���>
}@ݻ�� ng�a߾�'���*�F9��}� n��'���b�Sb�}e� n��'�o�+n��#����b
 ��+n��'Y!D����!D��')��u����'
5�a뀹�"�?:�>!D��`��-�a�+n{���`6+)!�5!D�5��}����>�a�]d\!D��)����!D��&�>�a�>�at�㑹)!À������&�>$utxq��	����B@"� n�>)�>!D�
$u��}/� n��x��}����>�&���,�>)a��bw!D{�c��<�{��>�>ه�f�� n���&+*�)�>.|
��
��&�>)˛ n�Y�Jc�>.|��Jc��~�z>|�ٳ� n��}B.|�>�>�>.|���>)!��$��nl���.!D��{fl@"3����6��Fmm���_9m�.D��+�>�>	'!D��.D6��c�<�)!��� :!D�.D�]"�� <�5Z�+�G�?!D��i?!D���`���'�Hd?.|?!D+�x������F�.Do��?��&m�q�Ǎi6o��{()�9� .|���!|��?meC�����!�k�	��&%�U\X�x�
�G��)�j�h'��aҳo?�j?)
�G��o>������?)�.D���a� #�o!��.'�f�>)?@"%��X��zO)Ł[m?.|{�>��i�����o?�>��+>�b�G����[m���md,@=�bϋo?.|���{�}���[m�=}_�s����d,?}��S5�ʢ��a�	ˢh��b0�+��aC��}��[m����?�b��.D�G�=��(���o}w�G�Q3�?}�i~�.DV�G�Ϛ.Do
}�a�bxN�w?}|�%Z� �G�
�����ymd,��!?�_z}�Mv"�G���x�"�G��ee�#��a
�G��[�me��	$?}����l���H���c�<� À{~S�oa��[m��&��5ڜ��a���݀{~%?1D��~�Im&�o~�6�f1DT�*�~�
1Dd�e<d�e�삙b'?Ud��a(�[m�� :Ud8����bx)��`*��a9�R� ��aŒH���e��6u+?Ud
�%D,�Cd-��b���.�	f��E��}ރ���b/?1D�)uU$nsUd0?1Dk�"v�����a<Ud	�)5���1�%Dy��a�  k�"2�%D��`<��a3?1D)�Y҈F�Հ
�4��8��5?�&6��`���%DW��a���bl�	f7�%D�
Ud�1D!�f�����8��a9?�&Ɓ�裂�aՀ�"۴��C��
g2:�Cd� !�iF;�E�<�%D��� =�%DA�&���'�5.�!1D��'p�&���>?Ud??�&꺗��5UT�1D��&Ђ���i*@��a�1D}�atUd���e�P+!A�%D=��`B?�a��F��b���`u91D���S�-!���`���C��bD�%D��+�kx<�и��%D9|�M�ĉe�E��`F�%D�aڵ	f��	f�i*+!�)u��-G�%D�i*S5m~H�%De=u�����G�j���I?�&�m~(+!{�Cd��E�J?�&�i*����� <��r���i*5���������c���z��G�K��c$�!g�#�g>T��I���k��2�L?m~��M��c�֣J��+۴A���ց��6n��&:�
)�P�m�
�B�3�6nl�cl"g�����Κ����YҮ�
�o�1
�		����cii*U��cN?�a�� O��a���+!k��c��ʢP��&���'�a�݀h��&+!��aU��cQ��az<4�R?)���&׀��ٳXd� �`��G�<��aS�Xd�3m~��Xd�-�W�aT?m~U�XdV?��Ȁ%�W?m~B�� X�(|X�XdN��a���(�� ���o�G�(�� ��a9�UV��&���a���&���Y?-uZ?
f[?)��&��i\�(|o@�]��c���T�(|��&W��`���h��a��b^?5|_�Xd`?-u��?!����
�aa�(|���-u��� .��`b�	}��:ec?5|(�b;��ii��d?�aWGd��a�Xd28��v��+�����`h�	}e?d~f?@9��i�>g9��mKm9��g��`h?@9i?Gd?���Yv�5|�?��;F�������&l��a�� j�(|ԟ"u������� �݀�@9耣�k�Xd�*�@9��h���&M$}j�D�l��be�ag�`/�悀(|m?}*�I���%�2�F���	}�����n?�bo?}~�ad��<�
�p��%��`(��b�?Dq?-u�}�5|v���I"r?
f�;���bs?)�%۴���HǪ�}t?)u?-uv?}��}���hw�)n����o�G�x?)y?�>X��2��#���ۃ�[�����Yv��w��۴�	}z�\d.��`h�cɑ)n{?)�I"�	}|�m*�px}��ne�*��>�	}�"@9 �	}��8{}��	}a�>Ђ�f���I
݀~?I"…�cm��>
�ո���{�����8l$���X�(��2w�����8�&d���G�,梘7�>�۴i����z>���A!
���}���>�G�� ?��t��bL�����b۽vȣ�?8D���=�G��?\d�?8D�?\dŐã̈4:\d-%8D���d��G�H)\d���bq����?�j�?\d�zӶ8D��G�?�Ϫ�j�f)��2�?�>�ʢ����>|\d�,��?8D
���X�'�,D��%�8؊�,D��8|�?�>���`�7�g�,D�8DЂ8|���b1���?\d�>�?8D�?\d��m*��8|��#��,D�?�h8��%`�?\d���&�Dm>�q>��,D�?8D
}L�,D�=
}���یBm:8DwH�o�E�\dk\d���ݘ?8D�
�8�Q�?]vW�j�?\d��8|�8�h��Bmr
�	�
}�JdЁۜ?�he�Kvo#D��Bm�n>��`�+��,D/����#|��}]v�?�h��8|+��(��~�?8D�?8Dπ�?�b�?\d��b���i	�}�\d�?8D���?���?\d�����?�b��,D8Dٖg�ʐg!�\d�C��?�b�����)�?�bQ�,D �9n�?�{:�8|X[���Bm��,DL�,D�?�b�?�����-ɀ�j����C���Kv��,D�?�b�z3�h4�,D��y�?�b���+9�,Dm��>-�{��{:]v�Y�=�t杢j����4S�b�
ɴ?�h����?�{�
}#-n�����J��������{�?-n$��hk��"�	��� 
}5�{a�kc�)!���"_Z�7
}d�Ɂ�?�b$�8d��ѧ�z"� ��b����۹?�bY�#|¢}�
)!~�6�X#�D��&��i~��eD$�+���X��)�"��?�b�}Q����~��_"�? ;���?�b���>��d�D�)��>�&O�)���>c�D��&����?�&�?�{���z@�&��)Q���Ŀ	}7�&ſ�>�?۴ǿ�>�i	�M"ȿ�>t0�&�	}���xe�)�DmI��Q�����Z�ɿ�jʿ)˿�x�$�z�ã̿	}���
<|�?Nd���ο�>I��Ͽ	}Ӂ)gFm�?<|}�	}5�)���<|�?�bҿ_"�Fm�3<|;����Q���><|��	}�?Fm��f�=�S�JԢ3m1��QH�X��&֌��*�3T�&��<D�Y�Կ>D׽<D�<�?Nd�݀Ł<D� �')!ֿ>D
6<|x�)׿<D�?<|,�<Dٿq>z�_"ڿ<D�
<|ۿf����?<|ݿ�>����޿<DA�<D�<|߿)�>D�	�(�Fm�)�?=n�>D���>�?m~s�M"��>�?=n[������>i�<D俭>�q>���dŁ<DŏFP$m~�?�'U�	}X�z?�?=n���X�62���52�>D�?�@“<D����$*�|���A<|=��a�&m~�?<|��'D���j�?�&�
&Ł<DK/���<D��O��'<|��ox����?<|��L��=n뿥,	Fm���������?<|sCd̀'D���j\9�a�'D��q>��`�?DmhX�>(��Jv����z�<D���W�t\�<D�Dm�Ca���)���>��<D�>Dv�_�hmm~� �:g��f)����<D�Dm�,s�Tl��<D9�;m�G��?�������´��h��!�X���H�Nk�""۴����
&+4=nmpx�?m~�:g=n�=no�矀�d�?��?I9�=n��´�ae��a	�:g��������f!���3
}�ԣԀ�o���!“�,�X��.I9��e؅b~���,%��z�´�)
}�X�Ҁ�>W�%K̛�����LR�h���?I9�?
}I
݀�+��(��b�?I9���?X�� �ampx0�fԸ0I9��bQ����� &I9E��b	�b~�����c���&@
}��)|I9����/���&o�DX����+��*�
}��o�L����b��AdJ�m��D.
}�.@"��)@ ���&��7��4@��b��&�)�X�����@"��Ad,��&զ�&���ե��af��c����f����(�´g@"mu>�F9�	R"Ȁ������I9����pxj�6͹@"*�F9y���ǃ4S���'@��X��u03DЁ�@@"I9ݎ�'�-�r,��ݹ�a5��@m5���Î �a)��@3Dg��b�	�80'�	@
}!Y���T�}��I9,��<��&��
��&��b�´������´�ao�&@X���9��	 ��/۴��&
@me+����b�}@�j��q>���Ad{��i�[�@X�?	i��!'@o�3Zb��!'��	��i,)���\d۴�
me��7/�!'聉b�@"j�@*Dy�a�Ъ�!'�*D�,�a@W��!'���bS���5���@)�!'6��v&meX�!'�kx���$����XNjj��δme@��!'�Dvy)�Km�F9��!'@DD��";��>@Dv���2$7$u@�-�DD@�&r���
�e��) �!'!�Kmv�!'���;� ��Dm"@DD�����mw�s*�a��A�#@Dv8
�-�i��A�DDe DDQ��������$@DD��_jme����!'o�G�
DD[$X-%@DD��܀yme{�߁!'u�Ez�����W&@DD΀�z�me���aX�����DDԂ����!'7���O�&��b'@Ud�	2�4�b4�{~��!'y)�Ed�b
��d��!'(@��O�y�)@Ed�!'*��a?�a�	 ��bDvU�h+)��u���`+@�b�DD�px,����&-��`��&j�8�px3��a���.�ImJDD/@Ud0@$u�	:�1��a2�CdV�G���ãDv3�x>��b$����<DD>g4@$u��ah�?!5@DD�$u6@Ud7�Cd���8��a9�M9���aߑCd:��a���=�
�&�M9v�%���������;@�b<��a)@9=@}��CdT�&ӏ
�>�Cd?@}@�F"�j�߇�݈�b�@9�\�A@}�w���
B@!�C@�bg�&!��f	M9�%�bD�Cdy�f��H'�6 �b���E��DF��D��G@.|�f׉ٸ}#˕��bH��D���a��`�fH��D��h��q>I@Ed>�E�ʉf�
.|۴J@i*ҥ�a��DK�f�����hj�8�|>��q���Cd��,L��aL�M9M��D%���N@)O�CdP��D���y��,���Dz�b3�bǃ��Y��$�q>�"�����Cd���Q��D�@9L��D��.R�Cd
��?<��h�&�{���B�#��8���o�\�j@9S@){n>T@}@3)k�\�U@W���DV@}C�=I��ݙW�E���+k۴@��z�������X@��k2��/�Om>X��۴$��D{�85��y��UY��D����"X�v/FvZ��D[@:Dk2�d~j)�ɣ\@FvC��
�b�!:DJ��z�"U�Y�Q���D� ���D]@:D"X�.Fv�7:D^�.D�[�5�A_@)�:D9�;��FvĄ����Xd(��:�OmQ�������/)���&dX-`��&z��� :Fv=:Da�Hd"X�>��b@ "X��Hd3��aZ�.�Omo�cc�:|��i��X���:|bOmd��&�X�e@:Df@ n�`*�Bmr�ɦ�#D\X�NGdg@ �,DGd�Mmh@ m��ak�:|i@ j@Gd�
�k@Gdd	);�l@�hm�Xdn@Gdj���o@X���UFvT��c*X��Gd���"X�p@ v��Jq@�h��ɣ�)u>
X�v#Dr�Ivs@ �7:Dt@ I�)u�u��ib}~�>X�v@:Dw@Fv�Ʈ�X��
X�*):D4���o�ݙe�Hd�0�B8fx�Ivy@f/�)����Gds�)z@�{����{�:|. �f�{fd�V��^�|@Gd�:o���-�^�}@X�
�x>��&��*��+f~�Hdr�ɣ�o�}bO <���:|. c*X���ݵX��oe=�{X��@Gd���&�* ��N�.�{�Gd�@f�X������hGdQV15�H�h7Mmc*X�gf�&X�s������ʴ��^�7�~8
	Mm�I"Qƻ��P���@�&,�}�	C+k�^�(�^��z��8�\����&��H�_�	�}(�H��:� �*Э���)u�*��j��)#!Dr�}@�ɣ9���ܻS�&�R9��%�H�����
f�����fDш�m*k���?��Ǽ0�jŋ	}��+Ԡ)�\��3h$��{�@�&�@f��닯��@fզWd	�	}�I"g�݌�^��@f��<���	��	}:f#�!Da��bv�5����\��@Fm�
;���b��( �d�@\dh�>���&��>��)�U���]��b�I"/7�>=����H��I"3U����c��i((�z����I��@FmȀ�	��	}��H�
I"�����c6=�&�@�i[��>ĉ�c5���>D�@Fm�&�&��`��Jd ��)�gb��Jd�&�>|o�Ӄ�cQ!D"�}��d�Fm�ER�£���A��>Doͣ��Jd��)��>D�]v
�y���Eٛ�>D%�I"��£��)�>D���-u��)�@Mv���m�Jdt�	}y%-u��>D��Jd�)3�>D��`3*�j������ w�y_ه<������ �-u�>|��	}��>D�Mv��Jdk۴�� e�Kv�i~��b��Kv��fG)�'纶Jd�Mvh�{~��i~�Kv�� �@>|�@�>���zy�Jd�@�>�q�
)���ze6>|��h�Kv���z��>D�h��>D�� ���t>Fm�@>D=�h=���fp�>D��Jd���z��Kv�@$n��>D����{~;�zy�>D� _��H���z���`}ף��>D��>DX����zNj��t�>D�h	X�G�Jd��h��>D��Jd��a�@���v�T��>D�ֹ@Mv�)&��kZ�4�@-u9���#$n�� N�h�
�QI��@-u��Ȁ�@�c�� ��z�@-u��h�� �y�o�=�re�� �@v5��fd�w�����@
}���f� ��a�u>�'D��zHu>e����%���z�Kv��{~�x�!�h�):��Kv�@I9�@�h����#���,�hA� ����@
}� ��xb��dk�q���zd��>� ��b�@I9'��x�ɣ�� �Ad��M"���D4� ��D�磸
}@�b�Ad��Do�b��@v���D�M"�@�%<�	f������Adh3
 ��`��D� ��x��@
};�z�Ad�/
 ����Ad����� ���	��D�Nd��&��&Xf�Ad���Ρ@v�Ad�@f
��'�b�@Nd����8D�`*�NdR� �b�/fN��D�֣�@8D�^�_����Ad@
 �M"X�{D�@f=�}��!�&�.8D�):��b/Nd���@ff8D�@
}�����"o2�@Nd5���	�b@� �Adf:�5b(8D�^��%D� �Ad���D����
%Dtm~��D� z����}��Ad�<y� �_"Q��ĢNd�o��&�����z�>;�}dfگfb�&�@
 ��DR��_���ȶ#g�1'6�AdY�#g"7
 =���h�Ym�@f$�t�g�#g�@
 ݃ô���@Nd.�b�@
 �~�Nd9���@�a�)�%�&8Nd_cD.�#g��&tfzNd�"��X�6S������#g%�Ym�@Nd�oeO�F��j�=&��*Dl8D;Ov�Km�Ym@�<�a��+%��f�$�a��}�@2����DDĂ{ed��7g��z��"�s��@DD��Ym�@ �ADD���
��H�}���d���>gd��ȃ;e�@�a���i3�$'T�b~j���'�5���@DD��^��!�Ɓ�3�@�b*$uG?DDy m~M�9���@�b���j���z���i�@g�b~�@DDQ�7K�#g��i�@DD�
f�1'�:F��#g�@
f��l	�m�@�b^%��OEd脁?w����~F=Ed�@
f^%��%�x>��������L�t��ô=���-:~�y_��	�ۘDD���h��b��YmADD��Ymք�h��)n;:DD��y΋�h��)n�y4�b*��D7����A$uD;nDz)nb��j���zi�
)��dA�zADD��z��D9w��H2��z����j6���io�!,�;���hj$u7�)nA����ô!��iADDA�b�CÄM9e��h����8���@9|��D	��h�@9�S�m��{��h
��b�f*��D^��-A�b��D��(|��b���%
�Im�b~G�}x8��hA��e
fA�b9� y��h	���bބ7\̍<D�Ed�k�R��b���a�+��%�⭞�bA
f���%����hz%
fՏϣA
fAEd�{���
��h!ɪ����z��h��z��h��jѕ�F������h$֣�����h��)n��>�>	�4b€��W��h�)n���z&�1ы��]m#�)n2�jA�>%��A�>A@9.��7AOm%��A]mX�.D/�	���>��>��.DU��b��@9z@9(�p���b5@9A]m��A�>n]m��j~+2�\@9e�f��<�'��n�>@֣3�!��f0�z	]m��d*�Om�OmF����
�zd�	������b�ׂ A�>�
�z!��b"A�>����� �����<D]��d�z��4X�6�����#A�x@�-�.D��o�o�}��j�����{$A�z%A�i(�����	�.Dd������>&A�z��x{��*��P7�Bm'��z>�?_X�h� #�Hd(�Hd��>���)�,D*A�>��{�,D5�ʘ+�Hd�,D�]m5�(,A -�,Dj��,�>���.A�h�	]m�jH�h�Om/A]m0A�Y%�Hd���� �� ��{� 1�Hd2AMm3A-n4A 5AUdm� m? 	�z3��>��ad�z�,D6A "��h7AUd�	)s�_9��i8AMm9AUd�,Da�_9:A�z�h��Iv;�Hd<AUd��Hd2AMm�; �-na�*#j6��z��a΂�a=A����h�
Ud�=�h�Mm>A ?AUdQ71��j���>���ZdX�_99{0:�%�� �:`1@�=@AUdAAfB� y�Iv��a߁HdCAUdDAf�CC�� �}-��he� ʋ
ن$ 4UdهHd����_9EA}��#gO��j5o|�Ɇ�h�,DѪHdFA G�HdHA IA$u�[�j/ J�Hd,�z��h
�!���k۴׀��UdKA �����j
���LAUd�Mm��hMAUdNA-n�
gȜ˕�!Mmii*OA-n6�}x���dUd�bPAMm
(UdQA$u��zy�Ivd�ۘ$uYMm�4UdI
��a�&%�IvRAK"��9瘁�#�Iv�
�z��aT�_9���l�de��_9s�E���Ivk�\�SAf,��;��[mk+!s��z��x��&5�by�IvTA`14�b����)ud�b���>=���5��>�b����f��5��#�߁h��>U��>
��>VA$uWA+!2f.�mW$z�XA��Y��>$u>��Q��>���샿>�$u�Ó�f�e��ie��>=����<�ߣb��.Z��>D����2f�}x����[��>;���b��D5��>��>5�	>|\�25֣�	��@��ݞ�t3��&!��f�	��n���>9���]��>5�d���^�2z����F�����_��h0K"�
�X�wU`AfaA+!��=�Fm�+!ofĂ2b�>Dc��hU�Xd���fw��d�Xdj��-΀Xd�����\�&�f���>e�>Df�XdG�Xd<f耘�k��id)4�i��������g��h�>Dd)��h���x��beh��>iA�a�>D��hǔXdo�>Dd���j��#��>���k�XdlA�a����m�Xdd)���z��S��`�}~��-��oen��z�����O��ao��zpA�a��
-ud��+a�˴
���&q��zH�Xd
�9���}~r�;us��i��ٽ���j��Xd��&t��zX�8��&�Ăk~)��h��zuA}~z��,	��&�
)�G��-u��Y�k~�
f.��a��O��zv�Xd��ܱXdل������fw�Xd��
����z�)Q��H��O�7%k۴^֣x�>D��Xd��a�:'t�����-y�Xdz��,�:'wQҾ��{A}~v������� :����:'�كgnv�A|A-u}A�am�í���:'���~��z��z�:'��
-u���z�:'}~Ă�iQ�g�l8��xh�:'���z!����:'��ʐ�������&k�����z�:'m�k�I9���L��,q�X���x˃�-���d�I9&����˴�3D낅L&�D��AI9�-} :	'�j���'��`�݄A���1��X�v���	fރ�~�A��.�:�E�j��&V7�}l2ʸk�?%}��a�!������D3�ށ�{��fyf��<����W}�A ��\���k�}�A �}�:'�o��B�*��{)/�ψ:'uߣՀ
��C�3!�d|
�ܣ�.�)u�:'�A /�k��	f�A\dI�Ad�:'f�}�ߣ�1 ˆ)	�>3���)u۽<��_�A\dJ��&=��/�3�Ȁ䁎��b5�"n��)u�A\d��N*5����b9�ל�A�x���l6�����b�A\do������&�A\d�A �A�5�A8D�A\d��}��&�AU��\dƔ�&Q��b��z�>��"n	�z*��`�A\d���be�z}���\d�A�>]v‰�.����Aߣ=�����}��)u�A\d�}�A A��u> ]v��Y7\dd�	��)u5�"n���&d�>j��b��AdG]v��&�A�z›Ad�z�A�>�E���n�>�Ym�\�{~[��ݧA �\d�z�A]v�A\d�A�z���k��I
���f�!f�Y]vݐ�b�A\d��9;�Ym(&�A�>O��&�&K������>b�Km���ܮA\di�g���Xk���8D���)��A6n��zc�}��Km� �% !�;�5DD+!�A�z���&e�6�������i��&k+!��
��ADD�A�z�Km��i�"n������d�X��'�z{��A�>�2DD��=����8|��ֵA]vm��ze�@m:��{�6n��a�X^�
DD��@mT��5�����>{�x�A��9]v�A�>�����uk=rDD���)���z�9
f�I"�DD�	��_
f���>��_h�`�����-u��zk=8
i1
f����
-u(�+!�A�`5�m�a��>)��f=�5���j% !��-u���+V�	
�?�R摻A-u��Ym։�o��@m��f-u���z��_"���D-uz�	}�����D=�$��i1���u�#-u�A��j裂�M9�ADD�`*��f��D�ADD)��f��D�A
f�-uQ���F��f��(��f-u�DD%��܂`*:�~��bt���7rӨ�� �A�S����A`*쁙b���D�<�����@9k�f��D!gY��u���v����b�i1ĵ�W� g�8��fb�b�f�`*$��h�`��N����A�b�A��i`*��b뀘b��`�A�5ه<�`*�i1�� �˕(-u}��D�'-u%Up���z�Ag�� ��j9V���jҀ"u�`*��5b��ٙD%��2���3���z�a8�Ȉk�f�A]m����߄�D�峮��T��A�����:� �f���jD�<D)�f�`*��D�"u�	}��	�f�`*�	}��i`*Ɓ�����w�����>��"u��>���:2:�)�@9*���H`*�A]mڳ�>뀘bZ� l��j��kH�'DŢ���9{�O��j�<�냫��A]m9��i� �F��>���72Ђ<�=�Cr��>"Cd��'DB��.3��j���n��ADm��X�o	�Y�ݝ ����Ad~�ADm�'D�%��^ ��´d���ADm��>�ADm6����,�	�Y\Dm�>�'D����Ad~�A���DmD�<D5�27��!  Ăb~"�%6��+t���a8S�:|��Hd��{,Dm�	�2���:|���>'
�qӣ��i����5�2����b~J�"�j��+�$���+j��� .�i�O	)��8��>7
}p���AWd�AWdԊ'D���j�b~�yc �A
}���Wd�>Ȥ%K_\�Dm��g?_+�����h�A �
}g�m1@"�9a�j
	
}��M"�h��h
�d��b����G�k�d|C�ɪ��۾;n�ADm�3vRb�m1 �
}��݇��d��?:k�v���a��M"�ADmo��+�A
}�᷍m1� k�{�ADm�����tD�o�����b~��*�w4��G��$�A'�v�Y�rNd�A�{�	f��&��Aiel����7$u��~��"5 �A�z�A@"��`	��b�f*D�`1����b�����F9�6$u��F9�� n�Nd���nN� n2�F9���j�=���X7
}�����ANd3
}�F99�3�/Wd�zO� n0�� nlNd�
��|T���h��zk��&�d*�A
}��hz���� n�f��i�G9!X�i!����A�z������&!D�f�A�z�f*�!D� n����9!�Af��F9��_9���-Y!DZ�#��j��zB!D�� nJ��-��B�B!DBf#� n�9!!DJ�X�Ё�B��$�� z���!�ܫ	E�!D(�fBf�F9*��f�"$u*D�!DB�zQ;���*D�!D��z�� n��!|˛ n6����z��b�f6��d��7�N%B!Dz��"� n�bT"!D��
� n���	شE� n!�)�z�F9���+耦=�=f~� n�裛Af~B���/�zϋ�av�zD�2� nF�z����5b	B�zB!Dȕ Oش^5!D��[
ӣ����z� -!耴�X��
B!D��O�f!D���Ε>D� �!D�f~m���fu�Zd�(g�>D�oe�!D���%fB�jw�f�o��������ݴ7!DAoe� ��"��h�>DU!DD��
�Zd8� B���be�>D��"���-��h���)��bD�!|�-:B�z�>Dl�s{� �����׀����`ʋ���>Ds�⨁�d�Zd���d��E��)|9�z�ZdBf~Ԃ�")�Im�aB�zBf~(���aѧi~7&�z��>�o��t�u$�b�a�ش\���>��9{0� �Zd�<g� A���>D���H��Zd$��;��-:��Im� �
)x��6v�2�����B$n��v �Zde��=�F���c�q�-��+!�>D@��a"�>D)��֜$nl���[�Zd� :�$n��be�(.|̑�eީIm*��䕯�	)��ii��+H�c����k۽��γ����@v�����$n#B)=J���
}�-i*������f8�av��_qme��5Z�Im$B$ng�8(��jv���i~����=��I3���+}�N��
i*���
��V��F%B%|��9�w�Im�i*���j�a���&�i~H�6�'™-t�Imj��x1�.D(�ImyjzF��+b�"��)�Ime�Q�%|�۽⁕4*�.D-��+�’�oP�.D�%|+B$nV :��a���{pش,�	fd��>���R%|+!$n�W��i-�Ad�.B$n���/B S���7$n�i*0�%Dt
�.膙��1�%DW5}������&��l2¢jj.|���.D��Y[���耦�R��>3�o4B^d��a5�%Dj���=��6B�&i*7�%DX��8B^d�i*_�49B�ad��:��n*�%D;B^d�
ӣ<B�&W�%D���J�*gf�%D^d�-|=B�&>B^dt�~�!��?B�a���?��ބ�{�%|%�f�΢�X-~�}!��jx�-�
�l��Ƕ+!r�+�}@B�&A�%DÎ��)���}/+!��b���hB�oCBm~_�%D�����+��#DBMm���F�m~EB#D+1D�MmFBMmR�Ad!�"n�[��#D{���ʐ�w�
�G�%DHB#D���(IB��JB#Dc^d.���Ad:Ud��Ӂa��z��$gKB#DLB}~�%D�����&�"n��&J����#|'Ud�1D�	��"*Mm��k~����;�&���z���MB�&�}~�#D���{Mm�^dT���l��z�z�k~&/Mm��z��|��"n�#D��r�S�-!���d�}��}���kĆYmNBDvZ�OBDDP�zL��QBDD�Ϣ��d�}���hR�̴�e�A��ޤ DD����~8s�)��{	�}l��X���4�S�)T�l8
DDUB+!V�)��%��@m���˸	�)WBDDX�(|�`~	MmY�@m�%��m~i#DDDW*MmZ�`~��z�Mm[�@m$�P"#D\BDD��@m�Mm]�`~����^B#Db�(|_�@m����(|`�@m��kV�k~b+��XI"$ۿ
f��@m��#|a�@m���M*-uԀ�A#Edn��4��*�I"/�#|w�u�b�k~Ed�i�c�@mĉ`~� ��f���d�#|���{(�
��e�(|9�����-o�?���&ܸ�DDݎ�kf�f���+%���gBDDi�M9O�j�DD7�`~hBf�i�)?���,�`~�*ֶ��FDD��@m�J	��l8���x=v_{��8��m&Kj�l8����.�<�j���֮B9<�$:��4H�4�۟fm�kk�`~$��b��"l�mel�@m!�`~��c�mB�jY�.n�@m�f��o�F"��(|p�@m7�;u��b_[��;uq�(|@�om*rB�j��)��b���sB}:��tB}��
}J������uB
fvB�zo����&nw€bxB
f--u"�h>9�zEdyB-uD���fzB�|>�������i!X�i���z��?�9��|!��a�>���{B�z�"uP���*�zM�;u8�U��6}�fo~��X1�Gxy�;u,����f��������a���#��z��C��;utB9=��%|B�z�
&n��C9���O�B��'|}B}F��~~B}�]m_ *֫$��'Dv�ꣀB�>��I
�ˁ�'D&���
�'Dn���}u��%9�'Dcӣ��B&n�'D�B�>�B}ރV[j'|]mA�X��'D#�����a�B�d��݇��aH��%�DmЁ������B�>��'D:��⛌���;�ǁ��t�.�z:�'D���z�W�'DXy��B�z��f��'D)����ًB�kcӣx�'D2��7��z#�'D���,�zt磈�e|�B�z��,D�����'DX����B'|��,Dw�'D�B�>�x�����,D�'D�B�>�����aj'|"��>��Hd��,De�ݔB�>*��B����)�zwbDm��tAe�,D����-n��,De�'D$����)��'D8
ӣ�-nի�>�
�ݗ���,D�����,D��>h�'D���!���MȀ&�<��>��'D):�-n�;|��,D�������Ɓ'D��}��,D������|��+>)a��1��xބ�S��Iv�&B�&���_9�)�>�Bd~�B�b\-�>k��8��a��_9j�-%-n��b[�,D�� ��cy-n"�,D�&Dm��Z���b���z�6��z�S��(ݬ,D
��z�B8D�BԴ�B*D��,D�� ��f��,D�B*D��}�<|\���5M��*D���&��,D���B8D�����,D�B*Dy-n���&\d��X�����ځ�<*D�BM"�-ni�,Dw�IҀ��z%-n�۹�b�#G9�Y�b���8�*D98�g�8�R�F9<��8��̣%-n�B*D.�_9����b\d�B*D��*|耫8%���S�ݰ�F9�+>�m�|[�@m2��%�@m���|��&e!D�*D�oF!DT��j��֢�
!DՀh��Ivߝ4@�!D,*D��ie�B�a�4f�/!D��@m��oyr*�2�f��8;�����| ��>̑�}��.�����*|@��|"��8��}�Boe��|�B*D{澥�>��}j*D��}�+n���}��+nm��ji*D��}���m.�|	j�r<*D�©j���/!D�*D�Cd�­>�8D������}m�|�f*?	��)�}������e���}M��>W�`v�}��arVma�`��};�&4������Y�}��}"$�a����5�o��}��+���>b��?�}+�-g�&
�a��}Տ���|Hs�]��	o��ကx�����}n����|b�i\�a�Vm��}+*�_�­>�B{���ޫ�ǒ�
!D;������o�����<:oe��i�B!D��ޯ�$uoe�$���<9���}߁�n��be�Bme���$�­>(	2�BMv��}���j�­>��}�>�Bme���,�­>�	2��+n�!< �}��az�`����}me�Aj���))�����}��I@�#����Mv�	2dy�4����)��,�`Ɓ�S1��kރr���`�����
�҄)D�j�{�j���`�8�B�$9�0�Vm@�)$���闁-:��e$n�B�$�#U�)J$n�̣�"�|I��6[��89W���)�{��$n�Dm�B.|��l��ImX�)ϖCaW�)�BDm����оW��BDmЁ����|P�)5���R/Dmo��9�{���
�kDm��i?���,���5 �BDmj'qЁ{����<D;�܅�V%�.D�$n,.|���BMv��\�B.|���Bmee���Bme 
�ܹ.|�����)��8��.DW?A�)Dm&��cW?t.|o-�jS��&��.DZٴ��BW?qԣ�B3D͟<D�B.|��1��)Ҁ���.D��|�B�jͷiL�$n��<D�bS�<Dn��`HF2���`
}�%|�����>�|+�Imm��>��'�)�����A��`�f.|(��y3Dt�&�����&�.}�8�B.|�W���`'�*o�3D(Dm�B���	۽�|�}��|�P}�BDm�����|�y��&l{e�3D<���	��*����`�
��6�<DԂ8���=��.D<�k"��b �<D��c+���-g�B�z�B�jĄ|�@��`��~�.D�|���r�4�
�zԀ��l�������&t�%Ԃ�k�F9�B�z�����8�S�|�Ԁ���$�}���&l�-Q����!3D�3D��/ު�`l"g+.ٴo�?��z۲�&"��8�B3D��&$�F9��&'���-o��3�F9�-������B�|=�ԙ�	g{���Ṅ�`������x��[�F9��9��q��B�>���&�1D�Y�?:������z�������&�B�>��¹	;��|�BMmP���Fa$�E����bm~~�x������&܈{~l2ٴZ����~��F"�\d�B�>o�~��)���ϡ��Հ�a��)-�44�#81D��.��&�	{�31D��)���a�F9��[m(��K
����&R���s1D��9���F9�-g" �B���g��))�B�-��%m�8h�[mv&6n7�-���&ǔ�z�7�-��[m;@"�z8ɰ�z����
8�y-��&p���R��z�)��s���yI��dX���T�X$v)�y��&���km�.
��H(Cd.�y�m~�B1D�Mm�c7��<Z�%�b�1D��ܮ�&�B�>/�Mm�1Da6n	�Y����a��a�B1D��>���B1DC�b�Jd8�8[�q�˺E�h����[m��jb����y~׀������b�[mr�ބ���|��{~�p+���C�bb��'r�˴P�[m��b9�[ !@��h�[m�A)Q���Y�)n"�\dƁ:j8�|r�����.��dCf�毹)g� ĉ�~��f��hGC�C)��|��Im�z�l{eL�ImQ71D�գ�7)	�Imƪt�Jm�8s'#ϋ�ܕ�g
d��ː(��z1Cu��Im
��h9���o���H���̽�{��}*�{T\�|���Im�۷C�c�����n� D��	%�����&!N��h��8ج5�
�ImS�|�m�|:����B��.%���aQ�Im����)������)�"uh���)v�޴.�Ϙ4@�/���h.�b\���������h�&���j��C�aO�_��h��;��j�Imɀ�h)>d{��nC�a��h��8Е)<���`�Im��)�>�aցG�%74S*���)�����h��|=�p��y�Ӄ�h����C�a�)nկ)���h�|����8���zC�a��"u聆-C}~S�|�h�)C��V�=�8�|����x�|����>�����C%{���z�+ C�ao�|�!C}~t�~���z��a��
"C}~���#C�|��z'ڋ�8
@9ᓭ>���z$�)����ϒ)%�z_�庇��a�)���J�3Z��&��z+��>�}���i��ye�}	�
;'C:D������֝�������E2���)(C:D��n�&[�x)C@9��a����b���֜�>�}ց�~ۗ�c�����a}*�"u"��kQ�W��:Di��&�:D9��oݸ�3�i+C�Nb},�:|�{�g}~.��.:D���&-C:D%{�}~.C�%q�%$q��z�:|�?�/áx��0�:|1C-n�!ױ�x2�:|�:D\�:|���3C-n4áx8�|�9��5�zl�8�5����Eu�o^�|��6Mmm�8Wo|��>o�~̑ԙ��*��8����,�*�@96Co�#MmS:�|Ё����,�*�i��Y�7�;n�Mm��*H�)u��t��:D�#oyMmc�ݸ-n8Co�z�:D9Co8Mm��_9���|����F�5�%�(:D���y:Co;C-nj3:D��c,�*Mm<�@92<o��y"�*�j)o���y��=���>CC�]�:|҇
�?C8D��޴)o�i8�
8D����^�@Àb:8DAÀb���bŁ*:o�8DB�:|CÀb��⏀b�<8D� DC8D��ݨo�o�8D�=�Mmo�o�8ث�׀�EÀb��^�&MmFC8D�-8DG�@m���b�
-n��8|HCMmIC8D�\dJC8D��ݯ�*�5Mm��n�Mm����\dKC8D���o��~��8�LC8DM�"n��-��|Ł*N�BmO�Fm���/L!D8DPÀb=�wA�8DS�|��@mz�8|QC�zRC!Db�j̛�gSC!DT�8|6(ף���b9���bA�)u��U�8|��iԙ��k5��ɀ8|��|VC�z.��L��+�x>��~WC�-XC\dYC8D�!Gd���bZC8D���[Àb�ˣ
��ȃ��8�|�#D�8Dҫ�b�Q�8�R8D.����Y�\C!D]C8Db��b^Àb\;ge��.�>����@�R-�8�_Cdee��� `�@m��8|���b�Ƈ���a�@m�de�� bC�zS�|�o��8[�f�J��3��$Q�cCVm��8dCVm�&>|����.!D�
����8|�)de�|eC�)�)��Vm��>D������� �8e !DQҢ��8fC�z�6!DT�i�x�j��8���?`�ZdgC!DhC�yiC!D��>D���d���j�>D�!Dep��-k�>D�>|l�g�3]m�>DҀ�?o��<In�>D3�"��)�#��ʈ��I"o�|�o�Zd�ԙ�)!i�y��-p�>D_���� J��|qC�y�I"��|����	�rC�y�:deY�>Dȗ�奂-g��E���>D��$��sC�yK����k�?tC>|u�[v���,�����>|ه�'vC�ywC)xC�b��3y��'��b�=�b#��,>��~�dezC>|A�>De��~��b{C>|k�b��>D/��襙��>D5�_��b��9|�>D�6Dmp�b��I�}C<|�ף�)�"��|~�>Dd<|��|�V�'A����>D�C�b҆T;�C<|�Dm���|�C�b�C�y�-gj�k�C<|��>Do����=Q҅�>D瀼~��8���|��	I"z�|�
Fms��C�y�B�Vo��~"��|�C<|��<D:����~�C<|#*�-��D�}S�|�o�~�Y9��<D?	z�Xz�|�z��C<|?	��+�k��$��<D�<|��c��?�ç,��5���x	�9�(}<|��@v�(%|�6�C�b��|]}�<D�C}�j�C<|�}��,7�b4�oDm	�<D���,b�o*�63��4��<D��^�L>�o~���>���Ad�C}��ߘ�Ǟ<|�C})}Dm�C<|�CDm�C=n�$n�ûj���|H6$nl�<D\�8�=n�Dm�ÿ>J�|���Adm��
���<�Ad�<|O
)��&��Ad��i�C�&��̒E��j9��ՠˣz���c�Um��<DՔ�iY��>��Ad,�<D�@v�%��Ad:�&>^d9��1i�<D��a���joާZ� n�Ady�&��Ad�C�&��<D�C=n⁣d�
���
Y9�C=n�C}�=n�C�O?�-*��"�&���a�'g�=À8����C%|���C'D9����?vy#Ȁ�	}��-
$}�C%|��Ad�C�&��Ad�oJ�V�4i��b~��Ad2&������#��fm�����jt�+jI9m~��'D��&��M"*�	o��%D]�&�8��?:o���
;t��h�I9��a�1��@Ѐ	j!O�)��h_�R��'D��{~X�V1l�8ج�AdF��j�&���~����)	�a&�-!��a��M"���C�&c�Fm��ie?	W1G��$�C�&H�|�G��$�@"��-!S������ !T�ԣ��8�z9��-b�)�3�i��b~�&:�$@"� !��c�F9�$���	 !�CDDp���ā	�/��fxDD샇$��Om�CDDӃ�~b�	9DD��y�3@��e��CDD@<�c6+ !
�[m@DDG��$��[���׉�ri)O���f��Š��(�/v�.DD�i8�C*D&Dv�CDDI
�	�&I9�6�ۃ��������Yց|����p���Ş�U2�C*D��}�CDD�|Rbo^�*|�V^�
o�*D�C
o���瀼~k����/Ed��f瀼~�C�|��*|���Ř�)��)݁}$�ԣF�u��C@",Ed��iezB
o��41�����ie�C
o9��(Ed������j[f�N�?�:FfEdt�ԣ�DDe�ۘ��s��1���Im�CEd���@"{Cdi��5�-.�!g��Im��!g	��~ DvЊIm�C���CDDҀ�&�$|\��&�����
Fn�CDD���o{�;|�CEdo{�Հ���eڔ���CDD���Imo{��fo��~�)��!g΀�&�C}p�o��Im��㑨�0,�f)J��@�zՂF"���o{����1�
o�{�b�Im��4��&�l�!g1CEd)'�Im��~5	�&p��*�+!]6Ed�C
o�C)۽�z=2Ed��|Ɓ��u>Edg;|ց����"u�C�z!
{�5

oee��������Kv$���CEdy)���Yk��d�!g�;Ed�%�A����#��C�>9֝;�z�Cme��&���䛾��~T�$��C�>����`�ν�9��)n���&��Im+��&��z�	�z�����Img�>��?�)!
{���>]_m:�FmX���� �3me�{����%%�z�� ��F��~��5��X���o{��2�� ��Cd���ao{�"*me��Cdk{����,�f��M9��a	%�~�?!��`Ӄ�~��Cd5��yu������~�� k�>���yk�Y���Cd��&!���aQ��~����?!���-�Cd~ل��k��{��Cd���z��-a��aj�����B�Cd���z�C}9�4��CdO����zX��O��A�M9�M9�V�m�z҇���C�y��a�C�&��Hd��%B
����.D��Cd�}=4��C.|u��zJ�Cd�C�>3����hme��Cd(S>����,mem�8|�5Z����C�b��z��+Q}Q�.D���i�����Hdc4|���ܘ��z�=��Hd��̅����C-n�i*��>��|��Hd�MmY�5@�o��Hd���`�oh�CdXjz��Cdw��|l�hc�yR�Cd�%Mm��h0�Cd�\d��Hdĉ,Do��Hd�CMm��Cd��C��,}��nӢ��c�%)�
;|MmꋲΩW�w}P6�����Co�"����}��������z�[m�-n
�����K��z�C}��-�Hd���z�����,{�HdܱHd�ל)���"�,D��Hd��.D�;n��y_���[m��,Dv۽�����&u��t3ӏ	:��Hd禵~ȒZOd��_��Hd��)yMml0Fv��)�o��Hd�ԣ�Co@��)��HdN�)�Fv	4@$�?5�')���)�*MmOX-N���*')��DMm3-n���|=5DMmܒQ��4o
���"n�o�)9��8�i�]�iq�Ehg�E�U/L�*'�-k��~F-o%�)큂b�Bm�Hʮ1D�[m��-ƴ�z�)	DGd�¢]�[m
Gd3�)�
DGd��\d��c��zg�-�Gd��`z��
�����zQ�&��z���zV(��x��jĭ>�z�޸�)7�-�	�]���ծ�>���F
F�
Do%�J��z$��ĭ>��8�y�����-�c�)o�+Ŀ>Ć�x��'���z΂�>
.�?cUdD�`��j��z�)r�<�de��z���z��%��?�>�)o�+��J�Ħ�G��ԇ�)I3�'��&P�`o%7�&�+��ĺc�Ám���J��VD�`� j�4�y����&D�`o%	;|Ȁ�0�'#��'�DGdo%��$:J֢�a�GdD�`T���D#Dz��z��zo%D�`��>D�^�g��hDGd�)!����B)Ё�f��z��>�GdDMv`���Udk{�DMvψ�z�>s���)!�z%MvI��z���yv�n ĭ>!Ŀ>� 3"ĭ>yMv�	!!�&)�y���3��/Mv�)!k{�j����y��Y�#DMv$D�`�>D׉5l��-���%�zv�5[i
��K/��&D�`RF�,�m*h�^���+O�m�)���7�`��0=��z8��'Ě�(ěy���) �;g�ܴMv)ěy�(g!���*Do���+Doˆ(gk{�,D�b���h���5���8H6o<��_4�bv�+�os�\d�,�b-Dol)|e�(g�oY)!�j.Doϋe#/DMv0�>DN�b�:)!�!!����1Do�)�[���z�Mv9��2Do��f3Do�	�bz%Mv�)!1Do4D�y��6�))���G&�y�;Mv�Mvb��u�f�-|�����f>Mv5ěy��&V����
MvZ�Xd\�f6�%��%`!�YG�#���7D�b	�9�8�f��i~�����.'�Xd9�f��b:D�b�Jdw�i~ဃ�;D�z<�|��(�=�;u�b;��>Do���dD�"g?ą$2�Jd�Om��n�*�Jd/*Ah�z8
��@�Jd�
jꏶRRoA�Ad���h�����z�:�9��#u�Coe�Kv#j]B�Ade�KvW�'D���AdCDo��h��aXF�m�����D�#u�$��z�"g��h݇|�E�'D�t%FD�h��KvG�'D:�|�8�Kv��"g��f���5�+u����9�h�,veN���ɚ���c�
��~H�#up�,��'Dg�|�ID�zJ�,I
��R�#uȀ���
܁Ad���mt%���-"�XdYC�,��+ÀJdJD�֨
�z�g��5�
�'D8t%�	}~KD�,��8o�d�LD�z��h���?�'DA��MD�-p������o&��%a�n��>�AdU��?ND�,��Kv_���‰�9�U
�+uOD�>ǃ"`��~��?PDm~d���#uv�AdQ�>��h�U�Ύ�~��3�䅼~�G+Q�AdX�AdRD�z����hSD�zT�)U�|��-!݀'D�&V�M"��#ug�B�*��emt%��1'�W?�WD�>�1'?���XD�z_%m~8��c�6fYD�h��1'��8��b�m~Ԃ|�}�1'ZD�>[D�b����{��/9�,���D�-!��O�\���\DDv��:g�1'�
�z� �1�b���֣Dv�1'��]mRDD
;|S�x�*D]DNd^D�z�DD_DVm`��zaDDD$�=��DDb�1'��bVm�Nd���cDVm��b1��d>�bO���9��X�vdDNdeD*D냾�Q�>S��bfDDD	��xgDDD��b�*D�de"$ۡ=m~���&g�
f�#5���f�)�!Dv@��a�b5l1jDDh�1'o�f�	\diDDD��1'jD*D�|�]Nd9�M"k�1'k�����~���*�
i�Ed���>k��zlD*D�xmD�bT�'x�'�nċbo�1'���b7�bpębv�ʹqD�b�Dv
	'urċb[����P�c�	o�
�:�N�s�ot��z���bu�	oOfvDݣwD�b�f��bx��hT/DD\�|�a��byDDv
'u�OvzDNd�DD{DVm��	op��j:�|�ͱ	oOv�Vm|Df2۴���b"Vm}��h ��x~��zDNd���h��z��	o��o���b������,�}*g��b��&��jցme���h��F"�D�$���?Ԉڢ���hL��x+�|��	o�}+�|��$�-|�$��F"Q�@m��+���ĵGk۽:�|��f��v{�+!ݣJ��(Ov��F"���hD
)5���@�ʻ��⣇��h	>�
�F"F��j���h.��b�?�\���AEd��	o5�<:
'u���h��
�o$�|��ębd�ʻ9�ʌD�&���<�{t�]m���h�me�Ļj��q����h�?��5'�|����h&�	op5'���:�|�:�B�X���?!"���5'�	2튬(���U�m��c�ܑ�|��棼�P�	�?!΀|���{e�?!Q�֜�{eo���v�M9Q��{��f<�_"�"��{e�;|G�y%��)�D�zc�_"n�3�������s�?!p�$�D�i��?!��&|�{e$�|�腬($�|���'AVvݐ�&Հ
�����y%��V$�|�3%�5���	���5@9P������,@9�������?���{!�b�� �D`*�� ͓��:�|��@9AVv���6��~5'�(m5'�Vvk�f���$p�.`*�}��bZ	��?��@9��C
!�V(���*u�ܫ���j3��."Dm�D�b�5G�|��&�)���7&Vv��&`���y%,{�-)�|�Y�{�/
oԀ��G�y%�D),�|��	��|���j��b5
o��y%ˆ_"�D
of�.DĄ|��?!�D)l�z(4
o݀{e�D�j���{e�Vv�D�zY���cO-g=nl�Hd�
o��}���iнIvc�<DH�zC)�.DV	Ca'�f�R"Dm��Ԋ;n�D
o�d$j��D�zW�R��Vv�+g)
o�ȣ�W1�D
o�ĉy��[mh�8��Fa�Vv��A�
Dm׉��[m��z��Hd�DCA#C�1�ie�|��f��l����� �'��%��
	�h��ie�Ӏ�D�bh�+g��F9k���D�z�Âb�)[����Ԟ�F9�)���~��h'��R
o�D)��h���L�D�z��F9���[���Ӑ�O��ǩD)��hO�jރ���D)h�,!�
oJ��+��"��$��$�&�D�t���DWd�!
o��8'N�F��i��D]m)�,!�,!~�����F9O�xګ�㒮�,!���i��8'���8'.��f�f���
�i8�=Ud��[m��;n�8'��%-��(r�;nň,{*oD�|�֣��в�[m��8'�DUd�Fٵ�ie_j!Ř�`���>����}�DUd��8'�f9�Z��V�}e�
�f�D�-��f�a�B�;�z�DUd�Do��%"�=����w��nt����-Z����-3�Ud�ȣĂF9�D1D��-e������W���c�DUd�z0�&���b~�D�&Z����F9���>��>��8':��%4Ud�D�-�����~�D�&��`�D�&�����|�I�|���#�5ٌ����T���9��W�&�<ܥ�|�ֱ�{���O�Bm��:�`1J���S3@"�DUd��&\�|�S�<��DUd��&�<�<ݣk�&#U��DUd?�>�|�儸+��>D��5Zd�Y��D�-g`1$�|�4Ud=���,��`�D�-�D�b��P�DUda�ô��ZdՀW�n��>7%Doݣ���`+�ZdȀ
��|�mf��e��Zd)�Ac��vi�yfy��;͓�����z�f��)�DMvp6�b��)�ή�>Dk�p�mf�me�D�&���̇Zdތ�ʟ)��Im��|�$�8�<���&�����)�ěy+Mv�D�>���z�Dme��Im�D�- �|���)��ô����T�&(�-Ȁ�=%�|�&me��Zd�D�-��&i3��棹$g"<';�|��)��Zd��Zd���J��|�ޝZd%<'�8���)���e8*��%'�)<'g$gԂ<���Zd'Mv�ĵP�ִ��Zd:Fm�7$g	�b�[�c��yo�,��Zd��Zd�m8K6֣f�m8*�%*%Fm)�Xd�Dme���y��-�Xde	Mv��)]�y�G��@�����Xdy$gg�Xd��)���Dme�fSg#��)��i���z��}/��j�i��Xdo�f���zk�Bv��)���j5��y���zd� ��)���f_�w���z��Xd�8'�ěyA��+�Ġj��f$�%S��zd�����L���]m�z���j��Imk�EEP��d5��z��h��zj����XdT��jV��f�	�.�3{�ۖg��>�|���z;�Xd/�
$g��������z���z$g�Ġj7��zރ8����/U�Yv��<�|��D
)����뀡x�Fm���y���zvգc��y+�&��Xd�D^d9��Ăy��Xd�D^d+�&��&���|���z��Xd��}8��xh^d
)�����~8�����$I���:��?w^d{�Cd�Yv/'^dg
)� a��p��d�8'e
)l}~q�6�:^d��:g��8'
�����+�8'���Ec�`E�bh�:gG^d�+�zE
)�iG�CE�>�5�5���x��z��H�*�	od��x�%^d[�hЁ��)fՀ�����Mm
�����.���xE�%��z;��z�9�E
)��z���.šx��%��e3��.� i*�ogI9e�,{�f?����	����I��AdE
)���m�>�����&���nЁ�����n�
 a	E�>��|
�{~���E
)O^d_f�nu%Mm��ή�jC'գ�!ۭ4{E�� �>
E�9%�j��fX
^dH�J	ȗJ��:g�^d���v)�9��j+����n���_���
����DDg�:���EDvEDD��uQ�c�\d
���DDDEDDE\d߁I9�:gEDDE8D���{� ���Ede�+�j���u��f��`~%��d)��nE\d��|���|b�i&D�|�j�4@����`~>��fg���E\dE\dE�%EdeEDDY\d�5�%� ���+8Mm�nE8D"�kEDDS���	MmADvY�')f E�z!E�x"E\dm8D[����G]v#EEd�"g[C�$EEd%E\d�nM�`~Y\d&E]v*̀5uV��f>��	5u'E�j��|(EEd)��n+�xӫ������n�zz��X�o������)�)g�z�5u��Y���*E�j+E=u,��a+��?=<:�vЪ�;�8D:���-E8Dޣv]vWde.EDDlEd[��?�CDD/E5u/
o�3\dL�׉�xKDDD$�ܐ��`B\dl��0�`~�Ed��ܝ�m��1�`~z����a�Kmp�Ӹ,�o��5�5u��`~2�j���j��"`%�z^�`~3�oe��ִ����x��`~g7{�|�f�������X���v���m�|2��+g#@�=_D]v���b����x��|	�����YQ�����|4E�z���T�e�Ed� �x��l��䀦>��5��b��z�5u5EEd6E=u]�z7EEd�l8[��b8E�z9EEd5u{�f�0�z����������|a$�}z���$r���[��?�@�&�)�|:Ň?g)Б��1�;E�z<E�&��)gܒp�=Ei1*�շ��QiȎ)��%z����>�Km��|�_",�z�� �v۽�%�{�j��,l�M9!��J���i�M9~#	>ŧ,?E�.��>v�b���f
$@9<��,i
&O��f@ŧ,y�&������-A�Ca�	�X��@9+���BE@9��8�:+`*(�8�k��k�C�M9���,#d~ی_",�j5�%���,b�� ��f�|��,�)e��8DE�|!>d~���)z�:������d~���eC�Q2��)��,.��E�"nFE@9�GE�&HE�&$���+�����>,���큧,�V��bޤ�xIE�>��8�ĉ�zm��.���x�ۭe��h�u:�B�&��M9m�|JE)���z��bK�Jd�-g��|L����h�Z�N)���fJa8��|R��,�_"���L8�L�JdM��z�))��큧,��o%g#NE�bOŭ%PE�|�@9Qŧ,RŻjգ�@9`+�b�Jd�f_�դ�@9Sʼn9l-gm�bTE)�������0�b靉9UEOm�d��9�b�@9$�o��b�d~��)���VE�%�DmW�o�<�X��z��8�YEd~��!'ƒ�_��e�&�ی�i���A�oZ��z[E���j������z�Cd�6z
Cd���z��b,)�� ��A��#b���Q�VJČͽ�9$��{"�b�-go��\���)m�o]E�bRa8$���e��bt����������5)��%��JdL��R���	;�(��^E�b_E-g`E a�
���b� a��BmaE-g��h����a8���杻j���jbE-g%�������P�Q����[�b~;�������8�cE aX�oaOm5��^dE�-��&e�A���fz��9y�b~R��f����R�&Z�ief�4Sĉ�f���V�	b�b~5�x��o�Č�ie����4
;��-1�ie��8ؙ�c\����b~9�w�0�xNg�&��|���>��B�gE@m�ᣳ
�hE�-i��&ս�b��>X�r����&��b~��&��?��>��f/���{����>|{1��m1C�d9w���-m��h<��>g�)Ԃ�&Cd���bU��>ĉ�f��M"9S�m�|jE�>�b~m�|k��&=t�聾�
�l<u*
�>>��fW�Fٸ�€{��&lE�>�X�e�p�@"��>����mE<u��&{ߴn��&T��P����8�D9e�!��}~�m18�D9Oe��K��b��	)�8�*���#`Ä�&���oE�|����!�4�pE<uqE��€�	��b���y��j:���$)��>k��fn�-��jr�
��{Z�rE�jlNd�zf%�f9܉[E���|e�|u��& a�G���sE)f�ctE�j���>�me��yuE=g��>t�^d��fv��h���U�W��-wE�je��y�j���yv�Zd���i��[vȒ�mof��>C��)S�{e�$�>��f
���+�ޫ���kU~xE�-�����+yE)zE�z{E�>�f�"�)��?m�|��O+�me�<u�.�b{�Uy�j�me�H����l�'DFE�z?o�H��{�j8$���o��$|E�z}Eo~EFm�)'�ɣ٫���'D�,oEFm�0�z�Eo�EMv���=)�E�zZFmp)�H���R�Q o(�H���Րb��|�EfH6o�E�j;��(�E�z|���f�o�EFmme�
)��ܜMv�E)�
Fm��Od�EFm� MvWme�Ef�EFm�����V��n$��f�|�����5�<�>!������	��j��&(�\�fӗ�?AFm�?��k��kFm���-s�E�rj8���`���=��Q��?�ˁ����e�:�?��x_�%��(��R�?	�N΀�cFًE�b��Aa��x�,Fmcow����*A�+�F(�H���?$vR	����L���)���G7���|{.f�::��oFm�ů>��%R�?9{0l���>!�E)X���*�>!
�h��-3�€Ҁ�&�E�|$�ܐţxt��j�z0������V���J��~:�>!�E)��h����%�?�+)~�h�{�
��c$7)m�(��{d�&��h���f=��$������g�?�Eg2	۽�������}��?��&�E�hm :�������C�.�5S:�/3�;d	�-��?�E�z�E)R�?�ń-�E)��|$�?��V
{��"�i��|,I9�{�EI9��-B����ń-5��xʫ��l��&���>iI99X1�7*�z�EI9���:�xH�E�z��xg���l���,�zI9H	�%�ń-(��Pw/m~;���R
�z�)��|�Em~vף�	I9|I�"��.{�#I9*��-��h����:Cd�[m�EI9��%�E�z�E)b��,Ȁ�^���p��H�=c�
��{�+�����p��w������E7�Q;I{��>k
�"�|Y�[m��)�ť,�ť,\����-$gQ����E�z��|���=j�}�I"*D��4���y��ΦE�z�)�ń-�E$g9��쁥,��[m�EI9�E�z-��-t��,�裫EI9i*���I9S$g��yB�-,I9�$g��oe�Cm~�E�z�I9�	�iA�zCd��}��"n�EI9��|�s?�窉�����)�	�|=��z��qu\�G�����E$g�E�-���(`$m~.�-��)��c����ݲ�"n{�����}v�״{��{����n�꾥,���|�`��w����,i�y�h��ه�z��z�ť,L����[m���@$g�	���,���z����	
)��b��۹��?r��z��|>�}
��?��n��Ym���zhGd���zv�ݬ�H{��z�E$g�Cm���	ox�Km��Cm$g��Km��΂$go�Km7�-��Cm��Km)�Cm�$g���i����ܚ�Pҿ�Cm�Cm�!��o��Km���z��Cm����-:B�Ym��Km��z҇
�X�Km�E.|��Cm�Gd��Km��)��Cm���䧄Km��Cm��&3����R�Ym��N.��Km��}��.W�Cm�.|6�oݝI���x�Km��N.�Cm�E��Ă$:�.|��)#kF��Km(�#|��)�Cmp�ɣ�Kme a:�����xK4R���ŋ�zf2aA�oL��%�����I"[H��I"���&5��yg�l8�o����z��y��Km�E�>�:F�����Cm��q�耢�k�!'��Cm���x��z]I"��	ow�Cmg�)���;ߣgI"��o��)��Cm:�o��Odi�m*/�&��Km��Cm�!!�Cm��m*��Ym�<�Ӑ�ܾ�y�،m*���
�̣�����E�&�|��Q?i�m*����q�
��m/��%��!'�#�	�jQ��_��E9<2a���^4�h�n�m*	�>��?5��y3�j��}�%�E�?�M9��
�{���W��?i�m*c..|�����)9��m}���y�)��E9@�o��g!�E�?����v��a�!'��|���aŁm*|����m*���i���f��k���a��}�����&�E�&9�W}9�;�<�����s}s�&*�!'���Q���ap������dP��E�z��&o�n�E�>,�m*6�M9��m*�E�>bt�€���M9��`]�b،m*�!'N9
o��Q?��M9��̣
�����M93�YG��m*�����}���%gGm<�Xd��ι1D.Om�EGm�	Ѣw�}�E�zy�j�#�|yOm�EOm�|׍�-����E�zD]m߁!'��}\�`#v蚀������z�EGm����)grC�L]m(Ca�E�z�EOm�E
o���ÿ�B)Q�U�'D�E�?l>�E�z�EOm3
o:��EGmq'}�Om3�'D��;g���`�E}v��o�EGm��}�'D�o��.�KvւYҤ�)gx�ָ�b��'D��ֈOmz�)g��?F����|�EE��Yd�
oe�Kv�)��,�'D<�n��|+�,�'DM-9)�<�?���+�z���΋ۦ��'D7&OmF�n:Gm(��=�z��'D;Om�EGm��,�Ŭ,��,��΍	���gP�E
o&�<�-�,��,���FGm� g5�;g#C�|�R��,�7GmQZ�X�i��i!|Z�Ƌy񂾣F�,�2�>���F�>t�)gk���F�|ه<�"۴)���F�|!�g�<�|.��`��g��NdF���
y*�9	���|�)gY��zh����*ƭ>5���ބ2�	�'Dy���ڪ�>j�|d�������n���
F�>d��>>�nՂM"Ҡ�&��|��_9��bFo����bo��&
F�>P�fn��>���F�iF�>F�`���>F�,&gm�|\���Fde�
�|j�n3�)��|���%���	<�|Q����a9�S���&A�U?��Ҁ�?ϋ��?	�U�M��ۮ�v~Q����,�Nd������,{ۦFR9����4I+�.�>�
\d�51>�!uS
�>k��,:��b$���`��x�,�>F\dQ�iFNd�Jm�Jm�de%}	Nd	�ZdF)Ǝ?�Jm{ۦO�Jm�1�>��|O*�jXy���*||z�\d��+D# �j(�*|F-g��>�Jm���刭>⁧����o�a:��xW�|.�Zd;B-gg�`#�JmF�|���x��-�4�ƺxl��{U�*|_���R��j�Jm F�i!�)�)"F��@�Jm���9k��-���$�i���j�-�Jm�*|s�a9���ׁ#�)$F)����8-g%�)&F�`'�{e��zȀׁ���|��%�	de:�)(F-g��Od5���f�-g��V�-b��x�Yd)�)m�|*F�(A���+F\d,F������}��j��&����(�գ=��-Ǝ?���9U�)s��9��?��?��y���o��j5N��Jm=Q�L��i���z������|��Jm���i��i��j.�L9��zX�wU���z�-}�H���y{ۦ��̣*��|{	fj�L9/F�-;���0F-g����)u���)*�-l�}�|,�գ�Q�}��n������z1�)��}�֪�J��G����2Ʀ>(�+n:�4i���3��j��X?�be~�?3�)Q8�G��"�X?���{f2ad���M+�6�4�L9O�)��|e.��t� ���a��DŽ�ho�I"���h�be5��h��	o6��&����	ol��hg���5)7��h��$D�ۦ�?��	o�n�+%ۦ=�{8FѢ$���5��i9��z:F	iO��Y?	�8*��Lآ%��;F
)���i���|r�<w(_�Z���+<��h���i=��&b�)��>��z>�]mƩ}�Am?FNm���-g	f@��=3Nm�آ��}=��X����_�v�AFNmB��hC�	o��}DF
)EFNmFFNm��<���ʢG��hH�}Y��h�&���<�%�����]��hh�O�d��IF��.|Nm����?��`cBNmq�ݴ�-�	8Dq:�-a
)JFNm5<u�Nm���h�U2H�U�KF��#6�-Nm�e<��hLF�%��M+M�.DNFNm��	o���h{ۦc��h)�	oȀ��O�F	
)	����DNm���v��h��v��YPƿ>�	o���Nmց�ܽ��)�	ot�Am�^��ɶQ��i�	iƁZRF
)���i��g�6Wg�{~S�E�݁$g���,{��aTƩjUF�7Î/(����	ބ�S�D�&W�V�[mI6E����i>	i��zL:�%�{�WF�%,�{~_����.|����	�z�����zc&D���A�.|/�z�
o�գ������'�Ơ��Ё��XFoYF�zZ�{~7��i[F�-\��ik�J��PՑ�D�z\?CE�-��z���]F\?“8D|���h�z^Ʀm��-�
o��-k�i#ȃ�Nb��ۦ+�j��z���$�8|�+���!'��zآ����w�|��Sc��-Ёʢ���b�g��7��j�o��6a��ֹ��-_��i%ۦ��(u��N��
آ�8��d�h`F�|ɀ�iaF
oo�崘�h@�o
����h��{~bF
o���-^Em98)�^kj��nJN���N֜!o�EmcF�zj�[m	id�[m@9�z$�iee�`3�N����H��efF�zg�`́?3	�Ⳛ�`_�fԚo�������h�`>��ni�YmI��	�Ym��N��-����7���
����-j�Ym���kFV\lF�-��j���]�Ym�
omFUd?�-���:�&�@+n�Ym��J�
�97�k�noF�&�`��n��hN�YmpF�&q�Ym���o���&���-��Ym(���r�Ym_��-���`sF
o���c@�|��(uh�z��|tF�&��;��Ym���yyM-��b��&�Xo�բ���.��&��<nu�Ym��&3�xȃ{��)���Ё��!��(,�&�		i��&8
oiEm�܉S��.vFEm�M-�4
owF�&9��SAEm���d�������H��.�j�ox�YmyFme���`zF�&��`.�&{�Ym�<�Ck�e��b��Fb�
}|�`��JLآ)me���;}F�jA��[��$g�(sme��<D	�i~�Ym���J^�c��ie�׫�&�L�a��nF�&�/g�M-��i=�G�7�>a�|�F�iR�z��jaY�d%�`#�F�iB�&�m.�&���b�:w�آo��n�n//��k�f�F�?���I�[�5��y���X�?��c��)Q�Ś�r�<�|	��n��)�2�J����ݪ+��x$��'.����)���&/:�?�'�|����8
���F�?e��i{��&�Fd~l��<+���;*�?�F�i_�Ƀ�n���z=�e���Z�o��n.���4A�?Ä�n$�<DS
�ij7�?ʵ<DՂ)����i�(�i�F�֫��7�آ��-���a&i1�F�?�Ƅ-�me=��@�ͣ��i�#)�Fme�����b�c뀄-A}~���hԆ�-���F)9�����h��|�! ��iO]m���h���-�C]m�F}~�k�F�?�9)�/Um����F]m%��zG}~S)�)ʔF]m�Ƅ-ڪ�z��p�$]mj�v�`%��z�F)�|��}<�|�F�?�F)�FUm>��zH��y�F]m����if=3�ޛF�?�f�FUm'	}~�@)�F�`�F�?��ݟF]m��zo��Fo��z��h�z�}~�ƚ���h�F]m�F]mh-Um��c	��z�B 
�{Um�}~��zR]m�Х+�|B�$gy�
Um���Ą�i�Ff��<y]m���z��Cm��ac��}��!����`%����#s���})[��ݥ�`#���|�/)C]m�����t��{�.)�]m�FUm(�:DY]m�頹p)V����}~�%Um'U]��z�E���z�-g��z���̼��F}~ZUmŐͣ���]m�����z����F]m�F�%=p���D�'�����z� �%ĉ�z���|�&���$�%��b�F�%��|}z��~8��e�vi*ւ�|��b*��f6�n��E�����_9��RЂx�́�v}��d���j����_����À��$�|������-l_"fD�n
	�%+���߄_9��(��6�̌?��Ffؕ�n�F�i���e��x��ih-`1�F�xԢQ�|o�{��z�~8�`1���`1�/�F�i��?+ck�F�dl��>:�x�8�>�C�	��u�F8D����/�F�x ��b���n.�C��	i��xՀ�8�Ъ���|���`�F8D��Z���%j��e��^�F\d*�sU+�-g+\d
ޣm�%�4@�F	i�����Xm�F�%��>)��՟��|ɀ�`�F�|c4��B8D삜?�F�zX�=杀b��"g)���8D��Xm�"8D���y����z��i�`1��}Ă�b�F�z_$8D���`ϊ�nlGm�M-��nz��b��Xmńͣ��`l��y��Xm�F�-a�j��b�ƭ>��Xm�ekw��|�)��x���nz��N�Xm��N.�F�x	Gm�Xm֙t��+�#�����d���n��Xm2}e���oe��d������Cdw���F�z��b�>D�ƀb�F8D���b�F�bo��.�/W��b���n+��`��d��w�("U#Z�?c���bĉ�`�FZ9G�&���O���F�b׀f��C��ƀb>D���2�����Xm������i�Xm�Cd��|b�zB��ի恫�����*�n�z��Xmr�բ��jȀ��o�x���j�F�b�o��Xm�����:�-<Z9��ͣ�$j�|n�z�7�bxآl���)y2o9�;dބ;ܖ��6�e�=���U-'�k��5⣩�/	�-:)������n΁����Fo���Yl��?AI+�ۡ�-�F�b(�9nQ%���Z9�F�b<�|ĉ�n���=��%�|��%�I+!��竀ۧ)�F�&�^kh�;g��b��U٠���F�b�
ݪ�+��>�Cd��F�FI+�F�&��N�95�b'�(�ƅ$�F�bx��|����N���o*�r:竢�%쁿>��|s��{��	fh��F�{Ghڏ$g�o6�������F)����&�F�n��iV)o���"��v�J%����F�b����F))�	fȀ��L��iI����|҇�8m���F\m�F)(��y(�i� )ow����(�F\mR�-gR)�FCd�F)�ƿ>|���,�iH)�F\m��be@
 Q�3��e���%��i�F)|����&�0�X��oz�&[�3T�%
\m��n��i�F)��/{�i#�F�i�.)��o~A�z�\m>آj1\m���+hM-�F�z�&�F\m۽�F\m*D�����{o\m%<|�ڥ��o�o=n<|�F��	f�ƭ%�F)gm~�JmM)a��%y\m��	f�.���%�)�ƭ%�	f�m~&�)	]��o�+)����]��[���m~�@�bX�6R�o	ԢX��F)�F]�&`�F\m�F�������n���F)�
���i%�F]���&\m��&�F7�G\m�X��)�ie+\m�m~b��|ރ6\G\ma�%�{Bǧ�:$g
������֣I����o(!��́Gm�Jm�$g]feb��b���_���J�&�O�<D���8�Afe�)l$g����^i�o��L%$g�W�A���/�]�X������D������������^9K�m~�}�}Yx�=��o�+:���m~�-����}Gfe>�^9���?G$g���G���R$g�b~	�}{��-�ݷ����|s��z��i�b~��^9V?�lp��
�}�^9h����~�uI���i��-��{�$
fw��	Njb
�}���_M��-G{`�t؎�E�)�

;y��`Njb�j!�i%�����n%$g��{���C��&�69G
f%$g-�r��b��&��	o��&��,k��n��)+$g��ͣ��m%$g>´RB���Am#��$�Am7�l��8�)���b�}ǚ��^9�o��}‹�(մ�?����j�ډ���&R�θR${���&NjbK����)�Am9W���&_���}G��~�b��`��b�	��}ŀ���&G
f �Am!�)"�}#��&��{$��&2��|�W�
´%)%�)���|�´ʈ�����&j�;�'
f��/�%Nm���`��y�I"&�)X!�a'G
f���n���
f(��`t�&�5�����2n���)�Am*��`+��&��ǩӫ�b:i17�)�(|џ�&<�&,Njbo�آ�>(�4S���>���
d�� -�)��2�0�ǘӃ۾ڥ��jD�	oW�c��+.��&�=gӐ�&�i��.D��Am/Gۼ��j��aւ�$9�v��=g�g��D�ͣ����i���j��)����0G�>�<u9{������*
��1��&2ǻj��zOʈ���g�c���&#�%����%��iց��	�zR���/�F�ǝFalj�W�c��&p��j�)��)��z��&���m�֣f����芈�i3��&��$4��i5�)H6�j6��&�}�����
�%��&7�ۧ�ͣ8G)�ib�zx�J9G}:ǟ-D���-�z;G�%�-\'�F�(9��$��Bvj#���i��>�����Bv�C�{���$��ݺ�D�g�g� �c>�:u)��i�DPK�<GZd���i_����cϛ-g
o�	�cW@���{C�(P�q���c�
o=�bw��Bv�!�b���7>GEm���-W��ia��j?G)@�)֠��ݱ�Bv��&AG�z�:u1�۔���%5���B�Bv��{a��kCG�b;�%�$
oj�:M-k2���nD�)g'�o2E�keCټ�ek�9�zp
oS�keF�)gG�ce�(�$��7 :C�%H�ce�Cd�
��I�ce�%}�DJ�ke����ceKG
oL�)g�h*�bwEmؕ(u�!�b[$
	��g�M"����M"?��=�(�NdM�ke��c����N�ceQ��b(�Cd��<�֣>�F+��2O�keE�ce�Cd_�)gPG�bQ�ce��)g�Cd�:uz
�bl}R�:uS�ce%NdĚce�$�bTǂb[
oUǂb� VG�`:-Dπ���1D�F)� .Emc�Nd��)g�(|G��bW�cez��c杂b,����)Xǂb�)gYG���}��)gZǨcJ�(�[�[�ke�|������iZ��\�)g���o=���
�kev��c�F+]�Cde�)g��ce��d��)g��^�ke�
@+_�ce�ob�bTF��Cd`�
}y�i���U��%�cec�Cdaǭ>,��cbG�%Ɓ�Mho���<cǂbh�����>}�b�
���o��m1Q����ydGo�Âb�
`���9S�邉yeG�__��Ȁ��H�%���yA��⬊F�fǭ>gǂb\#hG;giG�-��ij��{ݢ�b�F+��d�j�,��>3��=�
�i�&�������4���T]Ƀm1_���+��cd�H��
��5S6��nf��h
��ck�h��a�	Fv)������d��5	ߣ���w�À�a�
�ǟ
�OB��ބ�l�HmmG�%���5Cd�4����i���~���A
�M*�d��)���h�(������>�0�%��n�)�o��ԣ����"-g�%G�Ё��E�W�������|>�ո����oGoe�
�`pGoe@�Zmߝy�9����Q̿�>+��aq��z oeS�i���zk�#/rGoesG�iG��z�	��t�Hm.o	��zuGoe�}e��`��hJ��J�f���D��£��zOg��
g�vGoew�z8�i��z#$oe€�+�	C���`z
oe��z/�-5������z��Cm��b&��X��+b��x�zA�
Ny�zeB����<{����`z��z������{�z���* 3����|�Cmթ�z}��z��W�<7f[�hȉ�����4�-gG��z���a��~�z��+��`€�H~����c8=���~���>��nz�-gG���{�jՀ��oeЂ�㘁�aGoe�����bA
��!���ۣk�c�ge��֣27-g��z�GU]��z�Ĉ��/Xۃ�zp��V�Goe�I"�Goe5��ֹ�?�%d��{z��z���/�i#{������φ��K��z�G�&��E�D���i#�&���z�G�e��-�G�&���-'��+d���'ۊG�&�&�����8�y�I"v�(�i#5�&x%�y����W��d	۶���<�&��3�oe�GI"�G�&�G$g5��y3u>��Amo��h��Ёy��
��4�m���������Am
�����	��}�&�6f�f��%��Amz��y$g����O�AmS�a�G�h���m�Qks�Am̐�ck�m*��Am⁦�@���b��|��a��-n������ܤ��%ĬAm{���g^m��Am�����k
�y���f
��9I"���� �G�aõ�`�Gە�Am�Gi#%�i9�3�ܵAm:£��H�6�.�&�i#{�a4�������p��,�a.�"g�G�zs��䳅�o�+���Ǩcd�ցV�ۗ>�&{�z���k��Am��f���5����G�z#�����6��%�M���8D���`��Amw�9熁Jd����ǭ%���ܝGUd��Am��Am�	o���`�	 �G�z�'z��%
�%Ҁ�c�Jd��%	�Xm��Amu�t�%��h��Am�ǭ%Z���c�G}~��AmO�����ݜg!xg��Kv�;g�G�a_��i3�.|��d�.�a���i��k�Gf:�z #v%����G��(ߣm}m�Qk��
���`y���O�*X���۾w�J�Β1�Xk���,�"g{�����ւ�-:�Xm�,ߣ�����cɚF2b���8��`}�'!��j���:��D���a(�����a�G��;���.�"g��&���{~?�����a���iƁ��;�Xm��%|���ǔ4(���Ms5@��G�zȃ۷�GGmc�(u�7'��-xd�JU~	�X���ai��+��(u���iN��ݩ�
z}�'!�G�%+�<{�Qk�o���ݱG���(�A���G
o��&r���2�ír�q��Em�GUd�G))EmQ+£��۵G
o݀(uw��b��\d��{�Ǚb�(|��Xkrsc	�&
o���d��^��z�3��&�Ǚbk�/J�N�ӣ�GEm�)g�GEm��>�)gg�(uȀ�7	*����&�Em�)g��m#���&���&��%C�	f)���d7�7�bB�ǣ��yb��%@<�ϋ-no�)g8{�'
�����&(�䣼G
o�GEm��&񟯿%5���a]�&���4Em��=���aCߣ�G\m�G�&���a磊u5�(u�����&G�����4���m�j��&G��Y����8��ab��%�����vf��	f�G).��%�,\m�EmX����j�G�`Ɓ7����&�Em���y	X��Xd���+Ane�GVk�G
o�GEm�)^\mc��\m��)g�G)�b�Ǚb"NdȤu:\���y�o�)Emne��`��b/\m���A\m���&����Gne�q�/�ۮ�͸���bЁ���	f�f���G�b��z��)g����.��%�G�b��f,ne��%c�f���^A��&��%��G9(f�m{�*�ւ�߀Qѻ����G9��&	�fိ%��o��}�G_���O�p��G\m���a��}\�x�
&��V��G�d�G�&t����G93��a⁚ⶐ}t�F�������	�f��}cNd$\m��G9j�
����39\m�ne��%Q����d��߽
�5��a�C������>������a֗���G9���ܼ��zܛ��d́�%/5ߣڵ�in��z�D�%�������}���i���Î:'(��)���zr-7�}e�=u��Xd	��i�G�a��Hm	�%£��}�G�b�G9��z�G�{�D�����G9��0���a����G99�ל����bȀ�:��+�)���G�$-g��G9�G�b���aB
f�����G9��Hm����*
f
�uI��}�8��(PD
f�
ߣ��}�G�b:�Hm�{�
f���z���E����
f��)裟!�-���Z����Hm���h��{��}S�a��h��ʌ<��	�)o����)�b����]k�
fl�`&�$|f��z��.�G�%���z�)]kR��z��t�J@�����Is��_+���hb�)����Y��i��C�Ђ����z �Hm~��i��)�"�b�7���i��Hm�
o��Xk���h��Hm���b���hԀ�(?�b��(�Hm!7�b5ʌ:�X$���+��#�`#�
f�b��)$�Hm��c�-g��;F�b��G�Ȫ�G�b���J�_SNm�=
fa]kƁ7�"@\dp�虆f#�n${��h��G�a�G
fi�)Ԁʌ�
f�o>��a%��U2�G��߁)<�(:7�|��י�@d7��h��i�	�i!���h{���)�����h…�
�G�{�zw���h :�{�v�\dK����ljy���G�bH�aH�{�zw��@9�aȒ��e�P�!D[�xNĽ�dȗ��b�a�gՇ�i�B����{���j��jH�aH�aS��j�-|d�Ё�%�aH�z.��
!DH�zJ�'k��j�}H�z��z�&��o�z�th��Ђ�H�ze�i��jȂbcF�z	H�>
W�
Ȃb��@]k-:�n���H�zH�>,�8�f���{��<��J{�Oʰ��a҇�8�!�9�z
H�z%�⫀����w�e�n$H�a��zH�z�&�>{��*�A(�{��♂�%;�*u�ɻS}���o�ۢr�aH�z�������"i���k�H�a�����zH�{���Z�zH�>����}�"�j�!DX)a5}�#	�h�	�{��:;g�)iB�hdג嚕�H�h����}eH}e=����:u��<{�Fd��8��܀H�zH�z���Xt nH�h8�m�8�/�zh��&ue�Ƹ�5�b"��/Ӑ8�H�>�1!��+��]dJ��e��>��H�z*�/�!DB@�>{)}e˜;gD�iw��
K�>����HC�H�hH}eH�>���Vk�!<H�>��9{� H}e��!ȁ+耾c?	��"H}e�;g����x�(oUdЁ�-8?��C�ڻ)��+'��H���9;T�g�}e{�CC�,$n�
}e�C����G)��+8�Qk�$n�����+>�ir��J#H�=��`��O��j��j$Ho��%�G������a
�u�g
=��+�B��%H�:?D���c&�
}+�hF���������jvg5���'H�yi��헆��#(H}e�Vkh��|#o��k��c� g����W��c	�������)H���T h���b݀�c����b��j�y*ȁ+����+�iwB�6�=l���h��_�e<T-<7�yh��P�'W���y)��+,H�=$'=�Āh��b���h��c(��+"��>h�۹��c���.�y$'ܣ-ȿ>s���
,{�"
�g�8�$�������.H�a/��<�osfoI��
)���煛y0H�`we|v��1H�����k��5�ۧ�+�����&}b���׉�8Ҁ8�.��y�%D2H�`�"�a=���tib�`���u��<�3H)�]kc�/Ԋ�c���]�4H�a��̚�y5��h\�)��yY��c��`w�'�%��c6��h��/�B��y��f�ۻ��h7H�a8H;����c!�&9�Zm��ʌ(�:H�yd�Xd�Ǝ��h݀)m)e��h�Zm�F�=�
u�ؾ�v�`ۃ���h��b�)Ȏ�h;�)�7�?����A؈)t�?��8���8��x��'�<H�%=�Zme��x�h닱Y>�z?�)u������@�)[��hk��z���z��`���N�?ʁ�)AȡxF�Zm��aBH�`��C��h���)DH�`���z�)��?ρE����x�Fg��i툄|EH�޾�V�FH�`A�-|�!�&������i���z��yރz}��X�8G��h���ik��jHHUmI��z�"aʈg���{���z�Zm���h�$g�����,������o�J���x6��hw�f�J��z�Yv�)ՀJ���xp��,�%<KH�-"��>m{�LH}�0���q�L
������`k�XdMH 3��?쁥,�;n�D���c����	}�9A�g���� 3�XkW�cNH�-��aOH�%g�-l�%5���#U]Ԁ�.y�-�}P��zQH�%�i#R��z�x=��x
$g��)�%��ݘdf��_B/����ii�xЁ��Ё��S��i��(|T��z��c��q��f%@��&2ĭ��uIg��b��v�ԙ<�u�U�/Ђ�a7��&g��%�E�j�쀃��Yv�oBi#j��a����j��C:�-$��o��	o��d���-(�Q7��o
�-VHoA
"fWH$gX�m�o{�Xȃk�$g��cQ��Y�AmZȃk��h&)|ձ�k[HAd�co�|���	o�����ƒk��a�o�!�j��<{M!�P��j\ȃk]H޴^H^m�/�,C��/���#�T]���>���k(��m�f���>��|�_ȃk�]k`�	o��k�
�aa����y�obH\dcH�ja��}dHo�y�/��a�s�x�(eȃk�i#�
�^���b�'> m|eA�y��z�"\dfH�z�"g��k(����o{'�gH�z�k&�$|fW?���'�'���zhH�>��	o�E�i�\d���iH�z<����	o	���l�۸O=d]vjȃkkH�zە�lH�jڋ���ʌ������km��a΂	o�)ne��e�zn�"g���oH�z���m�az�Xkp�|� ��?5��kz�>qȃk��%rȥd��ߒ_���%sH�>g��?t�{D���u��a���?v�{wH U]vxH\d�޴*�1�$�z���X�6�y�|�z�{���i{ȭ%|H�>�9\d@]k}�{��ә�|e]vL��{�\da��?���~�{�	G�=�
���a���?��{ޟ�Ɯ��`���%�5Ӛ�����z&1�z���ր��{��{�H�zԀ���ȅ?��v��#��z�~1$�z����…?S
�>���{���z�Xm�H�z�H�z��{*(�>��:u�:u���=i�	]v�H�>�H�{�ۤ��z�:u]�>���׈�b�]v֏�a�z�Fg����:u��>"D:����{�H
o�:u��:
o����a�*z�:u���6]kb��{'=Wm�.
o��CӍȭ%	�׎H-n5��+a��{��{�ȭ%�:u�Em��0v�#���
�̢���h��{�
o�D)��{Q����
o��:uX�v����h�HEm��c�+�壥��ho	�����h	?'�	�ú�o��f\��)��c��3��&��ha����h@�����_"�ȧ,�:u�(�
o�Hx�+�*'8�,?'�+X9���h�Z�5��$���C�9���>D�|�z\ml�8��X9��`��:u�HX9�`*�X9y�8ؚCdcX9��:u=
-n5���:u����ڋ:���He��x���^\m�HX9;��x��)�*'*��o�*'s�NЁת��J���h�*'�H\m*�{l���h���*)�Œ�H)�H�a����H-n���h�HEmXWm��m#���ho��(���Y+����r�a��;����o�H�a����A�9�ȧ,�c�?����a�H�{9��,{��e+a@��h��{S�|����jȀ���H��9��
�H�a�ȧ,�HX9Y�f��o��av��y�A,e�a��8��{$�|����j��|���{��H�
�|���y����}�H�����9��*'>�|��zoʣb�/���%�*'���#]�i�"�i�ȩjn��%��a��a*
��雨x]kΔ|���(�H�>�z���%�(k�|�V\m���߇�����d���j��j��mꏫ���}�ƾ;�+��Ϛ�y+�����s���ܷH�i�?�zЁ�b��>Ă�>��	��?�����ȿ%Q�3�b��%���W�٢���2�?g��L�;
�	}dw���Pp���a�z��?�g���H�a���.�{��Hm��%���%)����Hm�����a��Zm�H�{�ȁy��Zm��8؏�{��y!d�-g�h��B
�e�H�z5&���Zm��������HmM��
�|�R��k��-{�Hm������Zm��|���E���ž��_Ą|��qc�H�-��Hmw��/��i��-��Zm���/i�-�]k:�-�����?ބ�����/Z��<�H)3l�?:�I���]-Km��("�h������/���T��/Y��a* �-?�8�x����/���H�-�H-g4���|����Zm����%�Hml�M+�o�2�k����/v�-�������Zmր*���h�H$g=�4K��Zm���9s���Zm=��0a��Hm��8�h�f#��Zm�H$g��Hm����T�Q3��x��ZmH)$g��Zm���?��L�m���Ȍ?y�Zm�����-v��(���i�qcP)lNm��}o�+�H�-�H-g<��{��/5�̀.-g��-(�����x�Ā��>��-�Q���}ם��k���l$gɀ�i���/���{h�M+���/q��?ʵ�m�}m�qc�����/À����/���{Ҡ�?�����/�k�-r=���/��6�����țyA��?S�y���{�F	�	x��&ؾ�_�/aR�Ұ8�mՔӢ���nQ�z�w�p�o��o�~����H$ǵ�{��Y���Am��/a%��h<u�H$g�|�F��[�Cd��Am��a�ǻjscMXqY�����=���(�AmS�m�����a�H)ޝAm0a�3�]D�%+a	)���'�	HĠ���u	=.�������ꖨ��i0a��}r�J�Ȍ?��o[>�j���{���?脼c��{��m�H�ar�/����Ё|�b�,!5�a�c9�3���T�	���k+^mÀ��o%*^m(�H���oƪ���������yz�d>���xh�^��2Q��;����|�j�/a�H'D �%���y�"��C9
��5�������Am�H^m����!D��Am��]�C90a��h�>!� n}�9���f���(o��o��Am��1'v��?J΀J�	�z��z���id�
2�a�)�H�zo����z�G)����䅋-��Amd���i���iň�a@�/a��8�Am>�����3gƁ�-���1'9��t����6��-��mq�
�ȯ>=�ȫ��(=vA�H^m�0aK6�_��|����i����+��FJ���Xvco��<�H^mˆ-3������D�{��9o�|�b�=摏-(���vc���-���-��-�$n� �w�|��ȇ-S�|�\��-�@+���-�H�i�������C��-c=�}�ڀS��-(�1'�}m�f�}��>��f���-�HC9������i����|�vc���ȇ-X���-:2���7}F��+�����������jvcA�-|��U2�vc�'�f�}���+���z�����iI}Z��p��i�Em�f�=��i�Em)�q�ɇ-�i�i*��xL���r��yIoJ��n@�(IEmɇ-�F���kt�i��e_�f���ݨvc�N�ɉk	ɇ-
IEmɉk�*A��+L��.D#�޺vc�f�
I�����)gIoɉk:�$��m
�z�Emy}o��vc���R}?�� Io݀�y���(?���ɉkyD�"}���k%vc��k�fJ��A�%D)����P� ���-�D�z>�&��� �=��(9'��j�-f~e�%DI�z5�煀]dÃ)����a��c�)l�����Ϻ�)�����Em�$���ae)1�-��z�)v�������)�o09vcg�bxEm�xc:�)?Em�5'j��k �z�vcI�zɉk]Ę�BEmɉk��cR�)YEm���k)��I�i�z�)뀉kz�i�掀)�����k���+ه�b_��cI=�)(���	���W�)gI�i��� �z;��i\�?!I��5'�z"�z��za��z#�G9⁧���=$�z�ia�/߂ae<�f%I�=&�z��G9$g��i�g'�xc;�Y�V�ƾ���(I�ie:�=)I�-:7�3$�?���a*�G9+I��TG~e���i,�z�%��-I�>��)�mw���zD���.�)��F+���Z4���d2��ĉ�z�/C �zw�)�z/I�z0�z:����G9g�)��������z^�v!������i��G9I�z��<� �	 3��i1ɺc��e���F	�z���a�0aԂ|���i�5'a�Uܴ�{R�Hm��^��z2I�i��Hm��>vc3�G9ԜҰ>"�>4�zy�?�HmݢG95�z�8vca��a@��z6Ivcd����7�Hm�����8�G9�֢��k9I��a���	�u0:�z*�;�z<�G9ȏE�:��=�G9��k��=���F�Hmʎ琎!!#�����6�
[>Ivcv��O�k?I�k@I-g7� ��y%AI�k*�-g���:��+�ѪI6^��HmR�o�����9��tBə9�=���i�=��m��i��k�����)n���ƫ��5���p���5�������܀��Y�j�o5���
�,C�Hmd:�>���Xׂ�{�D�YE�Hm���9{�j�fѠ�zl����Hmʋ
�^f��˸Fə9����"�,1��?f�ku�4�݃�F4=�k	 i#-��+i�V �Hm��@����G�Hm�׀���+���'e�:|H�Hme�r�酹�$����fnvc"�h�b������	��"gy�"g�0a�	}c� ����k�0a��yII-gJI-gQ|b0ap�E)&�L�'[��S�KɎ-�xc��at���י���Km��zZ��,S�4o��3}c������`p�����%��Xmz}c��%ϯ	&��Y�ل��=3������a��g��E��xc��vLI�a��p�4��9���`�fkm�;']��z/0a����~6�{�0a���ʉ�%i��d�j�	׀gj���z����fo�}�)vcw�}.��%vcf��?h5f��>�{M��Ŗ��<X�6�NIft��%�@}cOI�x%��y��kP�"gQI�c2�#@;}ct�"g��xg�g1n���R�a&��?r�SI�>m0a#�'�b�4a��Ά�f��?v��=@���Ԃ��T��aUI+aVI�aȀ�ˎ�>g�m*�a�'S��a��=W��ad0a������-X�#uy�z'ۃ�<�}c���������D�#un0aY�he��?m�\�,DZI}cI<�xn�%[I�x5������\�>DTI8�f�oeo�1'vc�ia�%Lz��)���h��>��c]I)��G��
�><6)��98��-�>Ȁ�o�xc%Y���^I-n�>s��ar���U�,D_I�h.��%`�,D��#��<aI�-���䔿%�vcr������:�xbI)c�he�}d�:u����)vc)eI�>�C�f�m#���a}cr���(�xd0a��;�-b)�>gI�>��-�
)�	vAv��hI�>��iiI�,ڡ�a�iF�,��#��-,0aa>|�0a�oj�{�%��8��k��a�.>D
���ko6)k�xc���?lI0aU�,D�+a���J�>D��,D��i��mI)$�h�5뀺x=)n�,D�>DH��jܒvfֱ>D��eOoI�i�>E-�io�p��-W#4@p��<��qI)rI�-sI-ntI�-uI\mt)vI�>��iwI�zxɺx��4�o9�>=�n/�iY-nyI�>�o��-���)�Q҂�JmzI�z���L�� �|R{I+a^��{����|I�z<�	��ir��ʇ�>ÃJm�8�=l��x}�;g
� u�ˣ
��(��z�0a@�	�oA�iY�U����d��	L��~�{+�?
�(���+/��{ɿ>���J�{��I�?�
�z���{<'���-��	f�梂�}j6}cbo��z��}�ɟ-_&���i<��+p�?))ޞ�-��}��
2ĉ8'5��>��}���x��%�*�
2��N9���t=܈}���i�����}��&���eo%='
�֢޺}�g��Ad��}����&��Q+�+�#�zz� �I�k��}쁿>r��f��o��kj���	�;D��-��Z�0&{��*}c���z��Jm��}J���Ԁ�A�1'>�(�i�/Q�;L��zh�/�1'��}�I�Ԏ�Zmo�=��Zm!��%��c����_�����Zm�I�kdݒ�%�ݒI�k��e�I QXm�����5��}~��Zm�I�?��^�vc��k�Tɕ�}���j�vc�
0a���I�{��}〿>�(r��0�׀��Ad���i�Ivc�}��ę��i��-��-��Zm��)Z�Ad��)��}& ��{��)@�}�1'���)]�)5%���i�I�&y�)gf���i��{��&�Ӛ�c�
c��I�d���i��&�I�i�I�iX�8���������#�)Ёxc���)߄�-�vc�f�I�k�
o���)x�m�INm�ʴ�)��`e|�摨I$g��)��μܰ�	� �vc��)��Zm�=a�I�i��/a�IDD'��<L���*�8��IDD5�kt��f����kkNm���n7������+�ݒ��ft�%�dȡ�cd'�mw� NmDD���{���?�{�IDD(�?a�iM�{��+�f��kc�%��^��b�p�3vc�HNm�f���i��)R��?DD���{�Ivc�Nm���?��)�DD%!
f���{r�d��)_���9�H3D 
�m �)=��o�ߣ��kK�״N��{%�i��ԣH}k}ct%���`�}��	������n広/aϋ�?�
�%[
�i���{W�	o�I=g��Am��k�
o�%㯳�+g�	o��b�I�i��	;��c�IoN���
DD��xc]�Amt%X��	�h�IF��qDD.)�hc�<��n�IDD2�F��Am�hc���?�INm�hc
�״J���0a++�Am�hc���{��m�I�hd���I�-�I�h���`L�Am���{�ɞ?t%�I�h�I�-��8D�;
f�I�-݁D|�):�+%y���{�hcI�Am��Am���{$y����&�ˣ�hcx�O%<'���&#�-i��`k�9:y�7�hoܣ�����I�i6
f�I�-o<'�I�-�����I�ih50a���&�I��k�Am�����&�I�i����Ivcm"Ѿ��I�>���"�i���`�	o�I�>S5�i��k�z�I�z;��)�?)�hD�iǃ����q����j���&l�<��%�?
��`X��&�C�h��`�i��Q��� n~�1�{��hc��?��`6�-e�(�^m����8�h�
hc�i*���3�=
hc�	���Y���&vIX�6���-�y��I�>XW �If�&DR��-�� S�\cS�yX��?�I�z��ō�&1��&�F"�?3,��?/�zt��`7�b	.u���&��C��ۈ,$n��ˣ��`���z�I�z�@+��b��{��ze�i���z��	�����I�z�
�{�7��y�I�z�I�i_�Ԥ�I�{p�b�I���I�i���ɚy@�&��
}�I�i�I�>a7�?/��
}w���~C�>a�!�G�4�?�I�`��	�]�և��z��`��$[�ɚyr<om�9������
}���i�&g��<��˃�`5�H
���'���C�͢xȍd{	����`��Hd8�)��+-D
o���cj��IoM��iҍѨ��i��l��<Phc�I�����y��.	�]�I��#�	�o�ۚ����B���i��<��o���)g�ۇ���܋��chD���c��Z���I�{��|X�@�Dw���ɉk�ɚy��g�;g�I�{�Ȁ�oܒe�`�sI�?�{�Em��'k�z��k�z�,�}#���k~e��`���y����\y��f��%��<��y�z��y��V ��)��c�}ck����)(a��ɣc�����
}\yӎ��c���m�B/g�ɣce�����)�ɣc��)(a�z�xc�"����}E
o�i*�I
o_�§�ih�)��)�
����z���cO25@�)	��i�hc��A��)�oW��-�)j�Y�I}c��f���i���ė�`��aYUm�I�`����ly�Z�4S��a�܀����) ��c%}c�)gA�)�^���`���Ɉ�o�BJ�a�xc=�T�ʣc85'�G?;�<�ܒp��)���{����	�9��-ʣcJy�3�}�
g7�Cm:)>�zJfŋ<|�)�`�}co	�U� 3�5'wf�	�dd������o�H	J^�;�)۷���J�}��)��}
J�y2FkV0�d{�)��}f}c��-F��}�)��)>9}	�4G׀����%�f�J�&l�
f�}ƫ)�xc@�T]�^�m��$	:D5���
�Cm�$��w�)���`���-\�}��aܒ���}�<���a<'A��?A�Hm<�Zm��a�}��k���=U]G�=a��ʀ��n؅Zmy}��z�<'b��ag�Zmr�����c�Zmm{���R�}�Zm�}���6�Ő� ��&��Zm�)p�}�{�
�G���1玀�%�ĭ�}o{��Zm�}Jo)��?�Zm��HmJoy}p�}J�kN�i�i��}@�k����e-g�p�� -g��ke���Zm��Hm\y�F$)S�c!���y�J}Jo��km<' J}#7�{K��*�{�?��}=�!J:D�o�JlB�*��b(�U"�Zm#�E9Z���}$J}c�
��Zm%�ZmRo�o*�ϴZ�Zm�$W�ĉ*����&�Zm����͓�a��E9'�ZmU�k}�f#��H%�f#R�i(J�k�i#<'�Zm偵�)J)*ʂk;��c+�Zmr�i<+o�F),J�ik��Q��Ջ��[-Jo�!�i�o��k��Xm[��`Ҁf#=�c.J)���%�f#/J-g�!!�{�tAd�o7Mv0ʠc1Jo2J�i��ko{�l�f#}c�棑{��hc��b�Xm��c����+3ʀb4ʭ*���3�+�f#Ղ�`5u��f#���k�}5�z{��m�IGm��c�o6�"g"�E97J�i�5u#���Gm�"g߁�c�ɣ��z8ʧ=���>W�����t�X�zN+�f#9J5u=�"g}c:�z�o���;J�i�Gm�kkj��'Jo%�f#<�	f��=�AmȀ�L�}c�5u:�f#�hc���c5ݥ9��>�	f ��`�hc?	���B�&%�f#?ʂk;�f#@�zAJ5uBʸ��6lCJ^mDʠc��Am�^m��b}7�a�;T��a�b�j��G?���>EJ�aۃ���Ӛd�����0o�G����X��FJ^m�
�+,�<�GJ^m��nHJ'D����I�AmX�{JJ^mク�KJ^m����5uL�zeMJ�<�Gm��~N�zeX�;Gm}���|5��zeY��>��b
ǘO�>D��&=��8)�)��X���šAm�AmPJ5u�I+��zew�{��+GmA�Y��Am����,f���mx��z
��Y)��&��6�Qʿ>�&�a$y��f��&����R�G�I+���p���$nSJ�aoɣT�Am�)���"�>DU�)
�1&�VJ��WJ�a�#ZGXJ:�
fW�iwY�)�G�Z�)��ʀ�)[ʅ-�ft�c���^m�2�c{<'r��-"@^mQ�dDM�1\J�c]J&g^���ze	�Ca��c5��+��ڪ�洇������V� D_J�c֔4�\�<��U�۹�b3��`Jvcr���a�>D.I+b��֥���cJI+��腺t����3�>Dd�ze����y�K@�cw����&e�>D<�f���c�c�ִ9�W�f�}b	)
�<g�)�f%y�Y,)h�)i��:y��)��}j� ��a(�@vs��J5���"�}h-\mG�̣��ʀRyӲ� �iwj��ݲ���+�� @�}>�)��%m�m#4EmȀ��k��'�W?��l�)Ԋ +�iw>��-�
�c���-mJEm��=a/��-���n�ە�f߁�-��t�o��i4
fp�}qJ�c�CJ��i{��%���2�@���r�ae-#`C9#�5=X�:̎
�sʩjte8r7�\m2������\yӇ�ae����avcYW1I���Adc�}tJ
)^�m� nuJ�&&Em��	o��� ��a%��vJ�&
\mw�ae
�(��ք}xJ�&T��ayJ~e�dh�㑪�8uw�����=E
)��t,ؚ�sz�}d�@v�&��i�"~eބ�ǩKmt�L9�z��c1�fщ��Jm`3�?3��s���?��S}Ɓ�4{J~e|J)��5&�&�4�"ê�#�JT��"#Em"$�����E
fD
)��&&
f.3Em�<�����}JEmg����������4~evcL���?�~eЁ�+��ae7����?a��{��9�
)W��i�2�P����o���y�~��aʿ%]�&��z�J�c.��w�ae��&�~e9�o�<�)+y�����i~׀��o��Y�J�&�� JB"�1�&�J
)�J)v���`c��&���{@�cv�Ѡ�k$	�&w :]�&{�>X��a.��:�J~eoɣ��{:��&8�'���r�L9Iu���aЁ�+��k\���5�����{u��Q�C����&€���Y�0DD…�*�H��c<u���j_�����>9�WJ���i�C�E�kfDD���i�<���ʋ�[�l&:À�&uDD�Q��3þ�{RV
DD��"n�<u��kȀӀk�"nc�'
�	�+h�_GX�$p�yDD R�Ё���0�}��Hm�0DD�JDD�ɣ�y�	�6Q�0�JNm�G��5�J<u���&m�}�JDDLDDd
o��aԀ�Aŋ�b|DDB��a�=gL
oӍ9��"n
��j����h���j�"ni��Y1�J�)�

o8��&́�c���b�hcV�|���J�y�ׁL
o-��c���i�;ܐʩc�ʉb��o���{c�"n�	��
o�JFkV
DD�ʩcrG
o[R�8
DD� +�JX��i����<�`ւ�����i�"ەʩc~�ݖJDD�DD���c.)j��)��cj	DDr�����\DD{���ʩcs�R�-#��i���cS��i�J)��G��ۙ�}@�<��b�ʂkЁ�R�J)��k��c��+g_��ԝ�Hm�F"��o�"n��o���c��)�)�}��"n�F"@ᣡJ)�)&��k=g�

o*������hm��-���k���{#
o�
5b���ʩcL
o��b��N�#D�ʩcQ�ל�A���-���{���iT����ۧʩc%��e�F"Q�>!��Ψ��{T��J�&�ʩc��9�ه�i������-.��a��{��F"���������Ϋ��{e}c����{�Fk�%X����{��g1J�2)����ʌ-�J�aq?)�� ����Ρ
 nM��?;��8�Gm,!yHރz]�a�긬�'�棗諀�����J)�����Ed���c����C��ʂk�
�fR�>!����
F�5������L��A���ma}c6�?3�ʂk@�{���{�����ܿ���95������-����)��δJ}׽�{��hc��i�o����M��{���������-2����������{���}(()}�X�����
�p{��m�a�J}���{��M"���9�7�i'�4��he��>��?3��&����
h�J}��>��+}c�J}�J)��l��,u��+ $)}�
�ԼJ}�J�cU���Hd�J)�J}Q�եƘ׎�+Q���Ba���?���J�cm�)D0)�)k��+��i�ʛk�Jo)���<�ʀ
��(��c��c�J�i���?�Jo9����J�c�ʄ?��c%��m��.��he0)5�����fJ�N���J�c�ʅ-�J�f�5�c5���-�ʀ'U/L}�J)��zބ�β.����'D�}��k)or�i7��-o����J}4��-}�c���k��;g犊)��Fa�J}�{�z�����kɅ-
	�,�G��ʅ-�J�z�� �ۮ.�z^)׎�+�pȀӀ�J�c"(�z�ʅ-�U��{�&��he�)>��+�")��)T�����Ժ�)�J)>��+e�Zd�ʅ-y���n\(��)��)�J�i�J���/�
�{��)���g$)D�ۡ��Y��c�k��)⁢c�o���9t��Jo�):w��Qґ��):��)���j�ɣc���)���c��i�d��k���-��)��U/6��[Fk�ʅ-5�����)=���'D��A�e��)���։��=�(5fs���J):�褀)�4r��%L�N���)j��&��'D��d��'D5��h��͑Cm(����'D݀�{T�$n[��>�Cm
��p�JfFk��Cm�oj$n�Jo�J�{o�T��J�`����:�)�J�`�J�{��
��p���2.�c��ʢc�J�&{�`��?���l�޸����7� 
�&Ã)ЊCm������)��Cm�J�`�J�&��Cm�T]�J�`k�+�J�`9�4� 3�J�i&�+�
h�Cm
=�J�`��Cm���L����Cm���mK"k�+$���o�+2f�oe-%-D��Cm�s��C����a�i#�J*D��`
�ɢ�J�`���z:D��ܫْ��
2�����&�*D�!!�C��J*D
�ZmЂ�>S�kJ���o�܀�+5�J%D�*D��i?�J�&���
!!{�%|���z���J�`�݀�JX�ߓ�>�F�k��Cm�x������@��Cm�J�`K�`�4!!���c�Cm���^��*|����Zmk2�W,�C�K�&o2�!!��W,�E9��S����.�Cd$*D�+sC�i#K�&;�aa�6��8
�ao�+���⅀H�K:�+����C��7$g!!k�+d�����E9�o��d���<I�򣝁�b��=�=���*�&@������f
f���ρ�׀�P������r$g&�E9�i-/�a	f�C��Zml$g���u��c�&+��N���ރ�S�ْ�>|�#`J�i�-�&w���J���&�"g���D���"�kKMv�p��fb�����Q�+���aKMv� ���Q����͇�a��E9m�"f��YI��Le�Xm�W,ڊEa��`�-n�W,o�+��E9T������d��^	�Xm
KGmm���)� �\�i�ʀ��`���k0�i嫀Ӏ���`)ʘ�aKGm
KfKGm��"g|�Xm��k��L���t�K$g��*A:��
:��(�.ǖS�#ud��Kfx��z��#up�a[Gm|:��#u��� :�Ð5={��#u��&!)KF�K)�#u��`	�������-:gGmŒj��zP��amېGmK)�	oH�:���`K)ѐ|��#u���K)�#u��c�-:�#uZ�p��j�x���K�c/�#u� :� ��c��%����|�
��Ă2@�.��z�B)��#uX���5=|�ּ��
� �6��'.�#u ˌ-!KGm"K)#K.|Ã#u�����������-�׉�[$�|��)=��LP�-:H��-:7��Gm@��-_:S��k$ˋk�^m\'����c%K�c�4״��k�	���н�hc&K)'�#u���(Ԋm#(K))K�c��|��@)P�	o*K)+�#u��z���̾�z1,K)��-K)'��(.�#uR�Х.D����9�'��zS�#u��.D/K)� �V/���v��&�c0K)�X�1K�c��.+�|����&�!�ch� ��b��$3��ޣ��?׀R҇�aR��-	���2K��k��8�a?�c��`z��ac�����3K��4�2;��-Ȁ�U"�d���H�4�l�.DId}�c��ޣ9�;��݀��y���|��a5K�c߂.D6�|���c�����7K�c8K)O��z������c���a��`׫"nH�c߂.D
+Jmw�=���9�}��.D�
�c�ٕ:K\mrޢy"�YȀ���z;�Jm(��4״oɴ<K)���;�Ɓל=�۾�}��'h����c>�}9�S?K
o@�"n��}��o�&gʇ�)%�z�����(R
o98����>9� � AK�c��a��jBK�z��}�ɢC�JmD��hEK�c���FK�i���h��aGK��ց
۸�}��'9����z�%2<�c�JmA״Q�cH�	f�&gr�zsE{~I��aJ�)g€ ����݀K�	fх�hL��h�1D�#D��M��h<@&g"
oZ��a:�|�N�)gb����e<ޣO��h(ۈ��aP��hb"�m�o���zg�|�������1D;�|��
��w�JmQK�a��(ۭ�	fo�|����z�~e��z�{R�)S�}T�i��͓:�g��iǔ).�}X���U�i.�Jm���^V�iW�)��ۅ��X�i#�|�2��J�Y��zo״�#Dk��Z��h��i�ۈ���)Z����e:-n'u[�)����	f���h��i\�^9�=]��h*�ۘ����U����
�%+:|���#��zi���۹#D��i
�I�*X�*���@��h���a^�i׿)g_�	f`�)��{aK�ab�	fV��6X���|� ��hc�	f̘�o���sE{~ɿ�h�"c��a�|��ۯ��h�㭕�|�z�z��h
����(s��" Ҁ)��a:�|�*���dK�zJ�,4�j7�&e��zf�i��f?��g�)o�|�J������*�ᚤ)h��hz�ێ�^9��k��o5��a��hiK�d���jK�z �)��i+DDkKNm��Yl�)��|�mKDDn��ho��h ��Y���pKNm�(�z�?Nm�`#qKDD/%DD�,�a$�_r��h���ބ3���5�?�}W�+sKNm5�ktK}j�j���Ġ��_uK�z��|+DDe,Nm�DDd�ည��+v�ZmwKDD)�kxK�ao�|�*���_'���
��+�h��hW)	�X"�X���ᩁ�h�Z�)yKNmHH�z(��W��hV	
fJ�zcNm9�9�zK-g(��%��'���
Ո�3{��hh�t�>�|����AW�zO�ʀ� �aO��c�|�h�|���,�,��5��b��-��cfc(��c/'�zW)|Kۗ=g��f~%��}KNm��˃�+f
f(�����~KNm#
f�+g��{���cKDD��{ �z�DDR�a�K
fǒ!'Z��{�KDD@��c�K}�K�z�j����c�˩c�K�z��i~Ё�+�'D�C��*����{l�-�˻c�Ko@��Y���Qs����o�|���&i���{�˩c,��k���sJ�z�Zm,�h���⁀?��zo����{��>�zl�>|���+��<W�f
��((��k�K�z���{��O�,!v��-o�=<��a��-�fc\��k�K�a���c[:|�˩cR�z~��cЊ�-��k�A��+J
f/�z��N(��쁞-��cY�.��`�u!Äbw���@�.B��?���e	�h��*�,!	�h���{܀��,!�Äۓ��{��y�K�z��bw���{�K�.RH�z���{
;a�+gQ� gO���vc׀�%�zl�h���U?�i�6�z���{ɍ�j�v�ߴ�z�'DfcQ��SԀ�;nԞ�&yF"�Kg6+��kJ�
�F�	$nF��fc
��
��@��k�,!ā�@�%��ۛ�>|�2�{[�'����-΂�k*���!uO�41��� c����'W� gl*DV�T�������T@��(3��l���V���v��ϔ!u�K��ĵ��;�|��X�m��,\��㣨x�=�K*D	5�D�Z�{��*D5��b;�|��K8Dȃ��X*D���ze^�he�K*D��ze�
��*|I�|���h�K�c�*D�uU��|�Svc�g��ze#�,o�|��K�c&.u�F��  �K*De��K�c�ǁ�*D��ze�ze��|��K�c��Zdg��[�K�c��ze�X1(�c�{)#�a(�ze��|��KL��K�{@��>�{��ߴ�U���c!j���d(���{�6�c�Ef����K�{�K�c���m�K�{��ze��)��ze�ck2�"θ�
f(��V��kC3�c�K@+ݥ�z�˛k9�S���z�����{�#�c3Z��K�{�K.u+�|�cִ%&���]�ze>X��^mNJ�a��-!X�4�zeo�|��K*D�a��� ��;�|��_k���h)8Dy��z��,9�5�8ѹ�|��ct�zeݝ�p��|�w�����{b��!�zeƷ�{�8D倢c���h��ze��|�Ad��Zd��{qx�+��'��z4��c�K�czm~N��h���c�K�cc��m�(�{Q�P�|���h�z����ˢc��hH�{ ��ke��hl��<efc,��k���{�\%�|�=�A�Ģcm���Y�K�{�{X�]���{��c����jfck��c��z��
hz��{������CU������k��h&�)��9��b�{�eM��-:�Um��>|���i�\�k���X?7��i{Am�)�K�`U����i�fc5��?���+��D��z�Um��Cmw�t�$ ���ۦ	of��i����K���%:�i��)ny��k��{�f�\�!�ޣ_��h���c��տ�-:�z8��i3������h���z��|�Um�ˢc����KUmT�����i���{#��i�,4~eL�CmF��i�K�-���z�����,Um��h�W�p��?]~e��h��׉
�Ȓ�����U�Cm���i�K��M~ee���Y�Z^N�g���66��i)���K�����{�KUm���۾�[*��+9%�(i?D��.D��)��g���zvD����<DK��{���z��<Dk�n;Um��iP��z3��md%���K�z@��{���+�	Um�K.|��z���z�k�K~e=
��R8���i���P�q�U�?��'���#����K�zX�.Dŋ\d�K�kd��+�y刃.D�! :��<DJ��
J����� ��D�
���=�� ���4~e�o�K�j�R�> �� O)���{�K�hp�?�ޣ'ͣJ� %��'��� <7�w� �Ko�� �K^d��<D�� �K~el�'!���z�����ڌ	:�p>E�J	c7�$�'�J�ӑ���f�� ��zB
�d���ܸ o��;���{��{b�)��x�y\��'Ȋ��f.� �
�|{�� �?�Ki#5�j�:k��Kf
�j�<D�ӁHd�K�y�i#�K�z���{*�>| �i(��լ�(uNNҀ�<D���+�'�/�i_�W�E� �f���(�?��Xm�� �g���}��Xm�z�i%��I�����Xm7��z�� ��i�EaV�E��K�z��Xm�� :�)A����/��w���KGmo��+����K
o[�CӨ�"nc�2�*�k�Gm_�p��z�#D���aЁ�Ύ�E5uƥXm�#D���ad�i��z�Kf�K�y�#D��w9�ìXm�5u{'�K��amd%Q�zw%�ig1D4Gm=�o���'�z�K5u	�i߁�wa��&9;��5uYf��-.���0f���i��݁J@
Gm���>�i���z�K 
���K
 ���e%5uiGm�f���a�8 2�Xm�:�	�ؕ�5uҨXm�ˆj��?:�{U��a�K�z� �{�˂k���{8'Hd)�(u{fc)���_
�C)���Lf뀸�L@"���'ŸXmL�{l*!
f��	ێ��5'
�L�z�5u��6�<�{�'ԧ�-b��k��&03
o�5u�Gm�@"̞-�#DO��/j�ۢ��ǐ-�
 �%�{���+��#L�{�K5u
���G5u9Vϫ�	큌-���-󞥘o�����'E,�{A��-̌-R�f#׋\m�4SI
�	�6�ň\�a̞-�訁d�$��b��/.��y���a}��㳇�-d�		L�Ȳ��y�ʈp�9��+k��-����
̞-1 *
ۛ�L�m#LI+L��
Lf�)�@"L�{L�cYB�{:�')���n�YLf���	�]��f��$:�f#���ܻI̧,.�aS��-��m#h�f#��c���-�{;�f#��k�:|���L�{׿�-Bce��4���Ɩ/\mO��-�2d~̞-5�L�c��ϣ���"@\m��{��޴�����i�cL\m��iL����i"�c��a���̄?L�cL\m��i)�XZ׸��i�Jm��i�8uJ�e��]�2\m���i�磣�>D.��? ��ib۽���ڋm#!̄?"�8u��h��&ۨ\mS��a#L\m���i��*���$L\m�)�a��8um�"g��i���a����Bv%�Jm&��i��'�Bvm��i���,�c��Bv(��i��hՀ��=\m�	��)L�a$���¢��{*�Jm�#���ܾ��{J����K+�Bv,��{��� a�h-̅-�].�az��a���{!��.̅-/L\m0L\m\�fT��1L�a<��2̅-�\mƋfv��j3L\m.����˪4��i5̗�]��-��;g6L\m���-7��i��W��.��a8��a��a9̅-�}:L>Dў��;̦�<�Bv�>|���5B��?�R�J��i=̅->̄?ɑ�c��-��'DR�Cd���L�+��l�hϛ"g݀'D삢ca�CdW��-)�'D*fc��f*?̅-@̢cA�JmO�JIB��:�'��^9;鸯�%���cBL�a۽>��{���`��cb��ܟ� C��{��;g��Q��c�a�f�'DD̅-E̢cԂ v���#��{���a����F�^9�y�?/G̅-;�ݴ� �˕'_8[�fjfc���h� [���H̅-�G)I�M+�eMJ�fw�4���EK̅-ċf���=L̅-*�Vz�Cd�����꾀z?h;K��������M�CdN�'D�<�$����cO�Cdv�N �W�# ;�$|PL�k(��0&c����c��mv��[���Q̢cm�n
_��ǀ���^9���g׀�&��^9R̢cSL�/{d%TL�U̢cV�	f�{�"�^9��cWLNmX�	f���YL*D�1<u��e�^9�<uZ�	f�8D+@�k)��������&�<u﫲j�	f��u!u08D)�#�	f��e�QȀǀ�-��Z�[L�k�<u�8Dk��	����8�\L��.����j]LNm��$�
fc+��'X��h�:|���;�	�5i)��d��X�����{���j^LNm� o��	݀%!JE �B�?_L<uւ8�`L�?R�8|a��b�oP�۸T�i! jv%�9��?�<u���ۺ=g���	c̩cd�	f��oy$�{jv%̑��Tx��{��c�
���{���6_=g��{Ȇf��.�?� �۽�GdJ���_L e̩c��j�
�?+��cfL7���c<Cd�?<uϠi~g
 +� G�ag̩c�I?	q^��chL�?�<ui̻c�{hL�?⁚⌢�%���j̻cJ<ukL�d���c�=glL�zx ��c��bB�c���h����|mL�alL�z��ˢ�DD���SƗ��DDcӣ9��q�{nL�av�J�)|� �joLDD���{3�����cQ�3Æ��8��%D�i���{�?��Qb�av1!��bw�ӣpLDD�2�c� �%�{qL�a�DD(��rL�?v"��s̻ct̩cuL�?v�>!A�=������?�<I"Ԁ�%CXm�DD���wL�᧫��I"x̆?��>!N��0��%��>!��Rp��9���c��9��c��eo�"
fyL!D��z�>!�Ew��k(�'u��yR.�a�5GYI"��zL!Df/I"=���
f3��{̥%,��'I�zF¢g�	�����i|��i|��}L
fm�{,74!D�@h��i�I"e !D�DD-¢P�}�#w幁]���y~LDDL�hiDD�*37I"��wY�h�LDD؇�iȀ����-���i���L)6�?3��>!K�g���:�:�?3�g!�L�h5�����5�]��L�h�)�;�'@�[�h��ce��f]-#��,���"�<耦ŋ�-�����L�h����R=�66�L�htI���L!D�<.|��h8F�+o�Ҵ��zeKh��̌-�g��)}(�6Q%�z��{��ވL�c�L�z���{��܊̌-��e*5�e�c�#ȕ�z�-!DX�W�_���9V�̋k	��{ލ�-; �z��<DX:F��]��f�L�c�?3�Dm~s�ce���	�#�z_��i�L�z�L^��̉k�̌-…U�p��-�L�c��ze	F���d��Am�6j�X�z$�=�V��e1��Jd��-���5��k��-��c�̍?5�zeF@+=���=Ԃf�Z�ށe1�L�h�	�C��	�L�zl2���n�̛kR�.D#=2<��k��X�؅&�j�S��������yЁ��|#2/�j���k��e5��k�-3|���k�Kvs�zLʌ-~�fZ�.D�̌-5��{�	۽��zt��-0�ze#�c�L{���z��eq��.DL��ޙ̌-� g�$ncC$n+��-��L>��?�̌-����'=�o%���{��z�3�z��<D��}v耉.D6�8��-�
�c���D�m~e2�a���Dm$n�g��}t��i���k���D�5�G�ˢ�
D��)���������+�'3@�8�-���+��n���<���6۽�LUm���)Ă,!����DV�yQ�	�̉k;��c��-3酹�� �EmR��z7��co2��a���z���i6۽�L%|�a�\mv�M">2��yރ:���z���z�2+�)g��.(Em��z�L�y�㯥�Cm��o�L$n��F�#f�)Fa�� ��i��Cm�L$nn��l�?��M"�?:�F��z�*���Cm;�g��L �L Dz�z�%|�Em��Cm��i��D�۽~e�L ���)	��a��Cm�i��Dl�`8g) �%�{�Lfh�M"�L�{�L c����%�CmXۜ�L�{�7@"Ȁ�ŵL �L�{9�;���CmX�{yEmY��N��Cm�L%|5'�L �s��L �����iQ�i5 2��߂&:�L�k��Cm��R=f��{��Cm�L ��	 �LEm���z�̫c�L�{)�#�(%|��M"�LNdl��cv��i��e���c�I�{��Nҍ8%|��Cm������c���aĂ?:"2���Cm�L �L�{���.���
�K�kd�Iv���.z�Cm�=Ɓr��L l�Cm���L ����Cm�A �����4�L �����L�kΈCm�����E9n�\d����?f0? �f��2
NdQ��L �@"B���L @�k*�{�L�{��e,�{��k�6�{�&����{
��b��}�L�{g;|�F�{"�{|�4@��#��}� M!�d��&:���Ȁ�^��'#щ����L�����ol��,��E9�L�kՀ��%�C��{�:�{����#��0�{9�wt�}h��)�}c��*��*`Q�W^�X�w�߁7|��hm��,��ۚ�Xm�c9�W��L�k�:uL�c�U�*���/�L�a�����e�L�/�L)5
�kk4۽hc�L���)��k�=�E�e��-S���*�Ea	�g��۽�����¢��}�:u��E9 �a�L):�iG�Xm��)�
Gmѣ��E95�f<LGm�L)�L�{D����LGmK�aZ�o~%���XmGGm"�i���a�	��:u�L�i�3��;Gm���()��i�	�+��i���G�f?#��)�#��CdT��a3Gm��)n�)�FFu���z%)�LGm���_�S�GmZ9o��j��f������Cd��)n�o�Hmć�	�	�bX��;�Cd�Gms��z��a���z��)nB>Do�6Gm���b���:u��)n�Gmzf+�f#�!�b�o���(�)�o��Hm�:uR�:uR�f#.��	�i�)��a�L�iA��\
f[�Xm���a��-�LGm��i�۫�Ѐ�LGm��l1�L�67��izGmTI�a�L-g�iֳ�-e b�$3���LGm�̞-T�[<�y�Gm�$�y�Lo���-A��4�	Gm���o-£��4��%`����Zd(!�������}�!�%魞-l�)nV�f��/q�6�r���ݝQ�Q���f��l1k�II)��)n��_�
�>���I+	��b�/R"%�YU�J)J��Y�I+)�#�0�y.�	f����Y&V/F
��H$�b�j��fjR"���́3aFoQ�S�L�c�L�Fo�F��3a��	f2��kAo�	f���a.�#9S��c$�%.�c2}���k
J�in����*`���&��������L�i���-L�4�������/M�c�
�y��
���/�5���r8D{!�c��7��-����v۽��,8D���i�8D����)���i�H) ��3a�o���ek��8��iH8��>�y�Jm�*`��c�;\mh�Jm)���M8D�Jm�8D��iAne.�JmM}��bMFvdg?��3a�Jm8D��i
�3a��E���>��i�Jm�=�3aX��i��酧[d���;�p	��iSM��	f_��
�oŅ3a��i�	f�zE$�c}�Jm�)��d��3a���
�)r	����i�d��i�JmW�3a�CJ�ze�Jm��)At��' ���(�zɗ)
�)�Jm�!m�}�)�}�UdM8D�):Ud��}ͅ-�}��)^��i�*`��i��y��{
�Y<�!m��a����iA�OdL�*M�a��%��i��&������{V��xM$g����i�} �)�����	���i.�zi�Jm�DD�5[�o��ze)���!͢c"�Jm�DD�$�a���6�a{�Jm�}?����}eF�a�}s;���L9'��o)���C�}#M�z�a��=v�n$�)�� n���H�}%�)'�˻N� n���a��A���)� nZ�=o�&��)y� n9G����2Gd�
f&� n����'�)(�}98)��{��)d�T/� n$��>�!Dٶ)�� n
�\d*�eM��a��L9��4��!D�}�ǣ�)��)+M
ff�}耤OI",M�a���-��-�!Dٶ)-M
f��a.�Am%ۦ/�)W!D0�Am�
�a�!Dw��J$gP6��(
f!>�k��)1M����[�
Dv�DD!D2M
)�
fv����$
)���	9P3�Am4͢c�%�i�/��!D5�)�f6�)g�Am�!D7M
)8MNm<�磑�/9W�9MNm:�Am�!
)��};M�k��PNm&<MNm=M
)�� n=�E�6Nm�}>�f��/�Am|�/��k2�))3�k:�i�I"X�i���L�"��Zd�/�o��k%�_09�l�Xd=!D���
)�#Nm�!D�.51��j?MNm�)��{�!D@�Amh�Xd�G���)�!D��{4�AmAMNm��-ΎAmBM!D�f���C�)@��DMNm��9��)EM
)��AmFMNmG�)H�XdIMNmJM
)o��KM�k̈�0LM�i_��c��{��.���{M�Am5��Nͩc#�/OM�{3�{,7o
}��{@��cPͻcQM�{���RM�/SMNmTͩco
)��cx�z��/UM
)�F�8Nm���c�������cVM�-NmWM}XM�/V���Nm΂�cG�->��zg}YM�-��TB<��c��-����y��dq�fPqg��YvZͩcvJ�z��y�C9��,�{�y����[M�{?E�{�Yvhc������p�.ր�@,�{���cԂXd'��9�3ß�{\�bw,�y��z�yp�?��{]M}��{U�?��{((m8�ϸƁ������k�?h���6�y��c>�k���{���xHUph��c���hc��-7)�kH6$nÀ�ܾ��cy
o���N�:P�]��bw���?��k8��c��?^��{x�´6+Em_��{���c`ͩc��yj�-3��?a���tQ��tA(�'�EEm��a
; ��a�����a��Ѐ=��?
�J ��=�h�a��a��y#Zdb��ac��a�X�8��c|��܎��a���dd�%D���a���B�yn�%Dn�
e͹c���]Em2۽f�%Dg�%D����'�v�M"h��a�
on����a�%DӞ�a��a	� ���a�%D���ay�Iv�li��a�3�%D��񚀹cyNd<?\dj��a=��5��´�;|k��a	�%D��alNd��l�%Dw@"��a��){��m�)���`n��a�k���oMEmP�%D\dy��a�NdV
ue�%D�0
ogNdp͞-Q�S��'q�)`2\dwuUrMEm�
o������-�M�sMEm���a��X���m��at͞-�Em��S���-u�%Dc��+��Iv���a���a��-��%DS9v�%D4��}��ix�洓4Sw�%D�'�cx��ax��-��iŜ%Dy�%D���-����[p!�a��zM��n�%Do�1<��G9|i�{�%DSĞ-{M�cRB�9Ék|�%D��G9��Ѐ�]v삛k���}͉k���B�)3��k�������o��z�@"~͉kkgNd��&�G99!�f���-��);�G96��*�Î���)L��(�G9�_��G9;<u���� ��$�\d�ڋ��͞-��F������)�����}���!Ё�܃�G9	fJ�co��+�䣄M�c{�
}��)�͍?脭x�
)V�F�Y��l�
}9���=u��)��'J�k����Hm��߮��#��i�I��)��(|�!D���?�G9��ZdV	��R�c��Hm�͉k		2��G9���k�)*�Ր�a�)�B�it�Hm��)��HmW�a��^d%�Hm�߈�)�����Bv��)�G94Mvx��k�MUm��G9�0)�MUm�M)�G9	����a��i����hB��MUm���'	2)A�a�	2�)J�	��ag�Y��il�i�MUm�ad�)n�()	�a)	2���z�����z���zؠ�	�a��is��o�M�a�)�M`*�MUmhcl�i��9��a��Cd�MUm<�&e�)�_"eMv�9oO�T��Hm��
;<�_"��Cd^ Um{���MC���Hmw�
ّ�r���i�MUm)��(|�DUm��)��Hm,�xe�͢c��aĉ);�ʋo��7�ao��[��͢c�0)�3Mv�cK*�a�M�c�Hm#Ăb��c�"۽v`*���&�M)�MUm�͢cv��'�#�i<��MMv$s����c�MUm�M)�-gUmg}j����&��k$�^d��€��a��i�,Umj_�}$n�M$n{ Um���&��/�٫M�/>�k�E�%i#���cBZd�M�k��_"�d~��k5��i����5o
�\d �;g�M�k�o��)n��/hc	��'	Ja�=u���b�kJ���{|�)nԂ�z���$n�͢c>5o�;���'G�P�Mo0��%㑜��c� �Mo�)�Mo��&`*�*�{�i#�Mo���cO��b�ck���_�M�/{D$nk2}��'�}��A������%��o��/�or�kHm����M��<ãp�����8[���i"�%D[�z�������b�k�D�k��f�S�{{�#�M�kJ�F�)Ç�0jk
P������#Fv��r�|��a��Z��k�o���}5���{+��a�Mo��jeÎ>��b~��Xm:�k��,D��,D��Xmv�J��/�/=Hʼ�Zm��6��,D�M�/O�Xma�8<�
��b��ab�a�)_|e:Zd���aB#����,D݇Xm�}�D���^�,D��a��b~�M�z_Ԥ��,D@����Xmu��j-ne�Ӫ��Xm�M�z�}wJ����Xmb�%D��,Dz�)�M�i�͎-�Gd��Sxv����Xm��b~4�kw��,D��Xm[ Ԃ%D�	����%�Dg�g�L�Xmg�?��F�O�{������;n�	�c��Xm�	�c�ͥ%')	=�}��&�|e��9���a�����ao�c2�;ndco{��%��q���"�d������,Dƒڰ��,D�M�c5����tZ9k����Xm��Xm���a���偔Vx�,D���@ބr�!�b���F��o3���M�c��,D����M����c��Xm��b~^2��,D?���$������,D��?���9�[�j���
�M�z�M-n�~1��(f�F�A
��:I"�M���L�?aUd�o:�����&SZd:ã��!����(4 �����;n�%��/��Ea{�%��c��}�#ã��b�ͥ%�M)��ݝq�s�E�D��m΁
�\�c��%��U��;n�)�e�QK�b\�c<�;n� �MI"\�b�MZ9�.�@f�	����N��c�ͥ%_*AH��/�2�c��}o�	o�f=R�D!!���/j�
��fb�����i ;�c\:�ɸrf_��i����Am�Mf���ik���t��i��	��ǔf���i�M�'b����/�M)�����/b��9�ң.�m�\mFne��'���6偃X��f�4\m�1)#�)n���i�M�c�M)���iJ�����Y�'�B\m�M>|�M\m/)�I"֟)n�M�'�M)��o��"g�M)j
�c��f���}��(瀫%�)������i�\mp���c�M)_Zd��o���ic)��c�	f�\{\mZ���
��n] B�f���i���\m�\m���i�!��J�+J\mܞ)n�����f���/=�
	>|q�)n/:)h���M\m�M)��/�M]��)n�\m��c�M�'�,)Q�AmB��Q5��D)�8���]��M)�Gm]��i�E�ρV�ƁR�׀�!O���M)�M��G��)>|�M)��+�MGma�c[�{���)ft���M\mX��E<<v��{N\mc��XW1*]ψ�iN\m�&D4�{N\m���y��{�ei�����N\mW�>DN�{�kN�?�>Dd�?�� ��𘁃-�����6	����6���9W�΁yN; :z�Ԕd~}���	�n��Զ	�?e	�}�f�]��^9$��2��Ɓ[6���	ΰj
N]a}�C�}%��)Ё���^9���#����aN�?؁,D
�}S��{��{��2���"]���}���Ԁ��{Mi*�
o�}�����yλ}� �{���׀��@�{z��y�}M
o��-����N3D9
o%�-����,D��9�}N
o��_+s�E��Ew�t_��i���fbN�-N-n�I+�	�?ԁ}�,D�o�o$�i���
��H����N
o��.�{�*ã��:̋�Ad+3D:�-�3D�Ad.�$���D�i.��O҂�AdN�?�M"A
o�}Y�%!�^9����^99'3D�`#�Ad�Ad,`#�3D����}M�M"{I�}aۘ��`#�}t�}���� �}�U���iV�Ad����j��q+
o��}�,D	ܖ)!�)�}�`#��o"N�kdB�
��H��i�	f#N-n�c�����k�%��?�i���b�-n0��$�)r
ң�
�i%Ϋc=\d�{&N-n1
o�3Da���M"'Ϋc�k� (N-n=��,`#03DG��lNd����텫cdB�Q��'Ђ��@�}��|�o��l��{���[9�(�hc)�o���"Ё��t��*�)+N�{�	f,�oҢ�0-N�k�� 5�4�i��Ad���<!D���ce���f���c.λc/N#D0Ωc��1Ωc)���!D��n�)�������N����@"��ץ�c�. Ї)�{b=�)��&�۽.�
Ȁ�� ����2Ωcx	)3λc���d���4Ωc��U�o��9���"n=�J�f�_DD	�C��)��?<�գO=Dv=�j��{�8��c5N�{��{j9DDt
��
�.���ik��vr<DD9���5��i6N}7NDD8N��_"}��*9NDD��9N!D:��i�,DDcD)��
o��%���=��9����;��iv�̣�1�c����Y��ݣ<NDD%�cRʩc�?DD
I������}�cg�-���)EDDX:���́�`��i��)=NDD>��i>NDD������c���c#Wm���`��ckɩc:��{Ed�)?N
f@��{A���_"�WmBNEd	��y�d~�"	28
�<�aCNEdl�k~t��a����6EdD�c�0EdY�_"<DDR*
fQ��a��ct��{DNWm�;gDD�&ueE�cef
fF��{,�m*GNDDHNDDINDvj٫�ceJNDDUGX9KNDv�Z��X9L�)�ԣ���o�}�`*o��V
���X9��*��cM��i2��{�DD���ϑ}ɀce�&9X9NN}s�ce���ag�cc��--��Ho��+X9Q�,��c'X9ON}>��)����f#�o���y���P·-%%}?JX9��ce��c,�b��cS��4�$�7���l� ga�F"�'
f�Ed�}QN�c.Y+C��YRN
f!?}�����yٖuCSNWm}T�a�$}UNEdVNWm.Y+�}WN�'�EdXN�cYN�'mݣZN}9��[N�c~�Wx�Yv*��\N�']NX9r�m*���a�(^N��R��- �cem?�c=��ݞ�'_No>��d�ce;�'� ��'`No�ۿX9aN�'D�̣�5X9�a8bΞ-cN�cdN��9��dD&o����F"�Y+eNX9�7�'w��<��/p*o��YS9$nx��A�}kS�cfN�'�}Ȁ
o�<oã6o�I��}gN��\}�f#j�����y!
��w6G�o��i���kR�4�-��8
�<��hN��iN�i����8�X�jΞ-kN:D;�cd�O���'J��TA�';�i�Y+5��ψ�-��i�)���.Y+��'l�)�'�b~<��#.Y+�)�)^���
�̣
��m�)e�Zm�:D
�c��֬�L��Eo�3�'��ZmB�'�"�i��{�'D��'�:D�}n�ZmR7�'^$�0��Zm
g�+"n���/*�R�?�:DoN�-ޤ�k��ZmT��p�)qN�iК	��8���� �)�b~A�-%�)��գ\L���'D��Sge��i��om~rΜ-[�?��as�Zm��e��	�aK�^�t�ZmuN:DvNUdwN�i~g�	����?xN�$�i�㯩 ơ��`��iFUd97�o��=W"�7�3�Fy�)W#��5z�Zm{N:D|�)+�>��)}�Zm~N�-k�P�$�Zm��z��;n�$g�)��Zm@�٣�N�3�)��Zm> 37�&:�N�l�D+.�F��΢cr�d*���`��ܹ� ��ZmQ7u��S�(�{��iJ��n��Hd���g��Zm��Hd��{��aB�:|*N�{��l$g�Cm���cބW��N���H��J��'��m~�[��M=��c,ge���'����Ҫ���T�,ge���'��(|o���c���*�D+�ֈ΢c�N�kЁ��)�N���
}�棎�D+l��+=�Eh��'� �*D��Њ�z@�e�k�+��̣o�ָo������!!醅Lʍ8�b��'?	r�	�?" 3�;n[�q��N�'��׀'ό��'��2�f���Q��4�)n  '��t��ݍ���!!ւ^���'��	o�*D�!!�)K/:��)nU��܏�AmR��'�6n����i#j���΢c��:':�$9��k���N�i���d$Ef�)ne��'�΢cA��'ݠ�'z�)n�΢c����z��ܔN�/�N^maA�i��%�>>|�N�/i!�z��g�N^m��Am.�{S�{e������2�����'  a)��g ��'w���N��NC J�(|T^m�g�ۚN>|�	�����ۺ��a]�>DX�{��>|t&��>D��f�Nۼ9�>D�
^m	�fm���>D����	�f9�je��{���
���)n��>DA�i!֔i���a��>D+�i#�
8؟N^m�����>D�Umƍ�'-7�?m�9�Nmw��Am�*Mvˆ	o*�Xd�N�>\�)n����Ԗ�0Ղ�a��>D����)n������9��Amo�	Ҁm~�բh�>�^m@����Jx�9��g���>Dr�"gt��?g��d��>��'!sg��NMv:��?X^m��9-�#u����/���8ݫ؁"�/�I���� ��?�#u��x:�>Dy0>|��TBD�"gU|ed�f���D|�F���?�=ģ��a��>D���-�a��o��>D�����?vY�d�f���ab��{k���N�>��g5�m~��׫�>D�
l�l1�#u��>D˺�����?j�jeЁ�{��#u탏?!5��Ώ�.�Y.�@v�)�>�+۸�NMv��#uv���.|qU]Ё�{&!�>Պ&��kC8Mvx@Mv���5)�9��keQ�D�L� �&���k����N
o)�(u"Jd����vʭ�#u�e
�,D^(-ng�#uEm�N�i�N�-�����N�i��-x��JEm�ιc~��E�@-n��-�Hx���� ��#u0�.�.)��)���iX��S��o�� c�|�B�E��j4� d��is�ae���*۳N
o�� ��o����N�c;-n�� �N�ifQo�c7��{�2f�\�AdH�&׀ ��ܔ�*����i�.|�= ��w�ul�%!��Ad�N�'t�AdV� d���3�Ad�N�cf�N�'�fc�(u…4��g�c��+(�
oN$ˆ(u�H�c����4�ae	%�uN$�<f��(u�� )neR��$�iz&g}	��kN$�inf�N�c��Ad�N\dX���b
o|�Ad�N�iX�{��i*��-��}q?�ģ�N�-2\dAi*�
8��N-n�neUž-�N-n��-��-�NEmp��?g���a��j� ��<�����Ad�
ne�� 犖���o健������ae���ܒ������k�;n3�u��#De�oV�Ad���z� )���,#D�N�c�ae��a��z9q�������Ad�N]vw��i#D��ae�c�Ad�-X����:F�&f��գ��/ne�N$�N�c�Nne�N v�(� �N�'���	{coN$�����{XW1��i#D�Nf������N$
=�<<neO��-����'u(��,#D�;�Dv.������X��=����NDD�dwG�"n�B��NDD����Σ�m���NDDi#Dr�=���>��T��-�ģ�c1�̣�NDD����g��i"dw���o�y~�
z8j\k���k�����x�֣ރ5d�����i"dw�#D9��49��<�NDD���i�N�cT>|A�Hm�8DD���{��>D{DDl1UT��i,#DL�>D0��C�cS�i�NDDR�>D���M()'u���v��jX	29��i98�}��&"�HmD�c��h������>D���zR�cҀ�>���ͥ�iۊ�QV�iLZ�i�?Ed�-gf*#w����N�c=��R2-g��>D{�Y��NDD*���NDD9�i"	2�Ed8
�<	dw��c�hȀ́Ȁ���B�o�Q$�NDD�dwk�Q$�6��۪���g��i��k�Ed��zp�i���g����i~Ղf2�]�2GDD3�HmՂ)s�V���>D l�c}�Q$�w$$��i��e ��Q$P�����iꢦ�4��i@�գ,`*���ݣ�k<��ho��z����N�ck�>D���h�N\m�΢c�@��N ��ix�c(	2��i[9�Q$�k� �5�c��Q$�ȢcW#���h�N�c�N\me�ڪ� i�i�\m�g
ۭ�z�;Ed3)-g��	�-g��w$b\m�_���c�N\m�NEd�g=����Q$ʎ��.Ed���	B��&Ed��g٣��1��)�΢c�9��Θ-
\m��Q$o�⣨���Ωc�������g�)o�Q$��41�Ωc�z���c����?������X���S�@"�N "�yl���Ωc8
x��Ωc⁥d����c���!��c�N ��c�k�!բ��	�F���+gz�U�'D��f� ۸+g�f�λc��fQ�
����Ωc��f/�Ad�N�i��e�΢c���Ad�'D�΢c��f9�'D�\m��+g^��bXq�񓪿�N�-���N�i+�>x�i=%�-��z��Adc�����i���ݙ�N�-Z3�z�f��Q�f��-A���,!�f8%ۄ�Ad���~���� �����f� ��heo�����i��Hd��g1�f�he/:�id�O�{��Y��ٹa82���5�-O�-γ�c����35��׀he����+g�)}~V�-)�he��-�he%�+g��i��b~�fD#Tl:�?�iϻc��-	�fʋ��
O�ia�S�heތ[���?[��A��he(���ue�fo۰i�f�B�{��
2K�?�f�1!
��{�HdF� ����c�iwa��g|�O�iχ-o����?9�:L�9�:�sE�i�m~5٣`{�*{��g{�?�-��99����1� ��{��a�
2�� �<�-���b5��,(���{O$g����Hd�b~ՠ�,>�{�*D��'��i�� \�{T/*Dg��5�he-� ��Hdj9DD�Cd���۰h�heo���\CDDχ-�Hd�h=C�Q�DD�Hd��?O�?3Dv��ݡ��b���ȃwU�DD��v�_�s��1!πbO�c�8D��{O*D��c�WdODD�m~O�cue]%*D9�� ψy�	%�ց2��a��'ҡ[�N�!O�cP�'"O�cc��#ODD�*Dk�2$��{%O�'x�搃@"��'e��yf'Jm����&O�''O�c%$g(O�'z8د*D)O�'v��i�=g
f��?�*O�c9zTV��d��iH�_݀�k+O�c)��,ODv5��k��cb,f~!��$g�Cm��-��i.ODv/��i�7DD0O$g�*D,'�	�'�
DD��n�DD1O�'=j��*D2ODD�Dv��'P5)@��{���k���y�"�'�*D=��{�Cd���kdޣ3Of@�̴=��f�	^m��'4��i5��p�j�����j�F�'��hk>�
	�i���i5O�',�'�����6�i=��6��a����a+�'�/g��^��c_�Ԥ7O!DF�c8Of$�.e�'߁��f9��i�+�c	��/:Of;�X$q�5x��i<Ϥ>���-h��k5�܀f��'d�Zd��aD�̴=Ϝ-�f �)��ZdZ� dfg���
�ZdU��h�X$b��ie��{>=��9�td�� R��i��h���o�����V�Zd� 
����h��/>��ij�ۖ��d��xe?�ir���Z����@O�a$�A�Zd��'ˁ�r�<���B� COCm���,�i��X$��{%��-� �������z�Mv��{�$�
�/�{��'k��?߁�-��(XW1_�Fe��.��zDO�{��h�F"���AE�X$ۗ�h�S8�F��zG�Zd�	�9�*#�0�{:�*����zHϜ-�%�b�X$��F"�{z��-�[v��hI�X$5���f��,�
@+JO�{KO�bLO :��w$�b;�X$MO�{N�Zd��bO��'��Zd��ioD"v���J�xe���PO�b��(4�{��/QO�b�w������RO�by�k���x�HdS�:|> ����h�i��F��zmw�i��zo�Sƨ:|Q6���(����<�%�iT�:|3��'��HdU�:|*���zv۽�:|%|a��d$�. VO�{�i*�+�ic��
WO�k� T��ih#�i3�:|��5@�Hd s�XO�b���az
�{Y�'��Hd���*��'�) �j��i>��@3 X�) �;nZO�b�o[�%D.�j:�i� \��'A�bR��':�i��u97�<��'݀�']��av�%D^O�a_��'>�=<��`�:|���c;,C�a��'b��'��a��:|�SA���.ˆ
f5���c��'\�Hd5��'��)gD��e���'�;ndO|ek\$%�k5�:|]~eS�HdV
\$��ieϠck����գw���A��$�:�ʋr�
�c�z�s�Ϡcf�:|��{8�-n:�i���bo\$မ�o\$<|e����c�)Q�i��b��a��)g��h2��ЁB�T |egϠc,��aQ��b�'h�"n��8zĂIv쑠c�
|e?�E�w��'(
��yHk\$a�B
�dž�ae��'\$��a
쩀��i�)"�)g��z�#�>j��'k�;nl��'D�o��Iv�5um�;n�>�z���:	�:��U�>�zy�ܮ�)��z�
)���[�U�nO�>�֙oOgpOCd���RJg�\$"�\d��k�����n|eqO�>|���\$��c��>���:�Ȁ��������c���e>JdrO\$���u\$#Dn
|e酔�<��b���4��?x�����b=���s�"nt�Cm� >|uO�?�5u���3*>|*��$:��6vϏ?�zrfw����z��רCm�b��>a�5�,�/��~1��e��ܹ�$:"��/��zxO���>|��p�g�����ȃ5yO�>z�>D�>|�:u{OCd�5uy
&|��i����:u}O#D~�>D�CdX�>D��i
�Y�>�>D��:u�����c�:u�O-g��-�l8Jd��i!��>D�%`��>D%��:u��ϥ���Ӟ>Dq�6�OMvS����^���'���{(�3'��>D��>DN*�NMv��#+�����{��մm��-��-��#�����*Mv>��{I��i�# 
Cdo��R3���dw�;g���iЁ�xb�?�%�?�O@">��w#>|�F�8dw*�Nd�ωb�O�c=����]>|�OMv�:u�O�c��c�ωb�O�c	�O�>D�*�c*�w� >|�d�ù_��{�OC�R�I"��#��4��O�'Ӏ:u���s�3b(�>D��͓���>DǑU��:u⁗ݔ�>D�� �<�c��>D��:u�� "�>D�:uY�vƖO�cX�;F&+��W����c�cb��9�c	Cd+!\m��WD��k��,�ۗ�#����uq�OMvh��-.�,��,��#��oV;�w@–�\m�	Mv�πk�	�Y�;g~�`���m��,U�8u�O�'w��{�;g,�#���x�O�'��b��,�c�cz�������;g�O�c��,��z�O�'>�c�c�'
�?+��7�w_*��>�\dGm&
�c/��y�'9���&Gm���o��/	�cD���>z��ݟO�'G��y��'DL�AdړC$B��>��'D�	���πk��ԧ�{�O�'��C$��Ad�����d�O�i(
�����'D��yb��C$Îe����	G�Ad�C$֟Cd%�仁�X�A�?d~��C$S��'9��AdU��,�O�?G�գ�O�&��?�'D�'��y��"n��S܄Ad>�iu�v_�O�?:"�a��,��y��Ad;�CdO<˝��e߂�=��	�'�O�?�O�'Œ&D? |��Y��}~v�]�c�
hn�?����d~��ZmtN�{�i�O۽�'D���L��j�O���O�{�-�ρy=d�~���{�C${��i���<>�͢�O�{��C$��?z-n7��ᒁ�/.�b~��Zm�O�i#�-�I���O�iD-n�φb��Ad�-��C$�'DS��c):O�i��b��C$�O ��-|r����)��b�C$��Ad뀸cQ�Ad�bZ.�-�O):;�{h?g�O��<��v��%�O�i{ۦ*�r��O�b�O�-z��t�knWd����&V��d~��k�
�dm�,*�w�jˋCm�~/�k��-�O�{	�m#�υ-l�)���b۽ODD��v��C<u�O�k�ODv�	�{�O
�k��У�c��x��?�*DG�k�):�O<uu�=�ODv�ˢc�O�k�O�b�D�{�OŪ�O�k�I��ODDR�)��{"�,D�HDD�
�ke=���O�-��b��	��bh-�-�ODD@<u8D{ۦ�ODDL
[�J$g%�ۅ
DD�<u��bé�Xz Xz�L�-�z0�ODD�
�y���*Dv���ODD�O	 Ć8��ODD+��b@f9��;״f���c�Of̏�.Ȼc҇K��Of�k��k�O�{��ct�k�%u���	 Ą�-�OEd���c�Dv�N�Z�y���c#Ed���c)|�ϻc�Dv��Am�ϻc�O�k��c��Am�ODv�DD�?	 �ODDf	7�N7*D<�Am�ϻc�ODv�H8D�ODDyf�O<uDv����Am<��f�ODD�O<uL��'��c�Ϣcc�Am:G�a���c��Am�-�ODDk����'�����zt^m@�h�z����'�a�O^m)�o��a�����zĉ8D��=���{�y2�]�Ɓ�xo�����AmZ����J��Y�� �)�OEdCL���Of�9�z3�>!Bf��ze�� �>!��w�Zd�%�OEdk�����4�ze4Ed��w)����Zd��c�ϻc��Am]�ze�Of�O�{��Am�>!�φbϛ ���c�OCds�Zdg�)���'�Am9� z��c��Am��Zd�� �	I"��Am���'<�g8��?������'��Zd��ze�Zd	�z�K�t�ۗ�AmӃ?3Հ���`�Am�O�a�?3��Am����D�2������ZdA�i~O:�f>=F���z�}�χ-�%��͢U�?Z���'p�b�χ-.��ˆ-:��۾�?3�f>�&�7� Ӄ?3��zel�%�>!'� Fˇ-�*� :���*��*#6�?3�Zd$�o���ze"��S��"n�χ-
�����{�χ-��Zdց�e����	�b�?3��ze��Zd���{	%�4�ze%Nmބ3��}��jB��y���-�}�۷��y�)���{� @��Ї-�����{
��y
�(��<D��c�w��cŁ<D-��{��?3h�`����-���y��c�7�-��i��c�`��?3���P�i{@�5���P��xCEmy��y��DZ�o���PEm���{ᾚy  Ї-��ʞ�>��w����Em/��w���iڋ��o���PEm�ι��-����EmP�c������-	P ��<D�i��־
PEm��}B
�  $�)gϋf��
 b��PEm�IvO�bD�`ô�k�5�i,�<D�	cM*�
��{��<D��i��������y�;uEm<��{� D��� Em�(H��{��iP�'�!�ŇXda�o��P�-P�'C1^d�{�^d��f^��i��i(���P~e��b�a8
��%ۦ�ց�O�-)���ЂbǃU $Em/&�'���bG ���'�% ���z3�`��{�/�PEm�~e/ɵwPEm #�'P^dP�- ���(Jр��; �xk��PEm�-^�"n)����b��5bĂ)��B߄�bPEmA��i����-���O���Pm~.%Dl��iP�?�����?��-���Ԁ�Pm~aH1D��
 O�&�#D���P� �Iv���ir�'!P#D��G9"�G9#P#D�}ǿ�-X��#D8�}k�G.$�G9��>��k��e�g�[C��#D<��-%�CmH��?�?��Cm&P�'>2'�G9�����Cm��(�G9��Cm"�G9)P#D��zb�#D*P~e�#|��G9���Ҫd�M"7��b���?>��zj~e�d%C�+Ю%P7C���ij�
���w�-�b,�Cm-Pf$����G9.�Cm.�h/Pf*�wO�Cm��#0�$:g�M"Q���	�b��$:R��i$����#|����]d1�Cm��#|�r�	fM�`~��?Y�/�����2P#D��Zd֋G9G�G9܄�i�
z8��,Q<m~�G93P�k��Cm-�G9
m~"*�km��zh��5�ͣ�	m~S�G94�Hm5�G9>����`~���i�5#D���k�G9�	�ji#6P�-7�Cm֋G9�!f�i#�M"��ᫍ?��Hm��}Z���5�ݾ���i$�ݧ��e�kk��N8�Cm"��5�b^�Cm4�.�=�Hm����{���'�-�������9P�k��tF�P��� ��i��kfp��Mv���iP%�Y!���.�k��ܾf:��'�$:��)Rf]�O�Hm��j��Hm;��z�;g�;�>)|��{5��lNd��E9\ۢ
&n3i�<��i��`=�Hm���W>�Hmrڴ�ku��+?P�{����k@P�kA�`~A�k�&nBP�{�MvC�`~*��b��ZdD�Hm���Y����{EP�kǯHmz�ZdĂ�k̑���kQ��M����i�Mv����9���ᬎ-Z꣰͂k9Og�Eg!e�Bv�۸�Ӏ.�-���b#���"gv�2�E94��E9֔i!|�i�O���F�2�R���U���U�c���$�Bv��E9G�[v����e���)�je�Gm�;gV��ޑ���Gm� ��{�}~�HЃy�"���{IPGmJP�i�<}~��y?��e��u����
	�b!����Gmy�Y�KЏ?��z"&n�}~@��Yg��Um��'D7�/�'DLPGmR��y��-M�'D� D?	�9�̏?NPGml��kOP D`�&P�'DQP�i���?k DRP D��i������y�T�5��ySP�iQ�'D���%Dz'DĂ�?e DTP�i<:D����U�'D��iY��?j�z1џ�w��'DVP Dm�i�'D��/� D>��wW�'D�:爀"g���'=�iX�'D�X�
��/YP�i/
^d9�'D"�/F�'D D���.ǍW��GmT��?��iZP D�GmQ�[P�i���a.��?��'D\P�{:
''Du� D]Є?� D..$���^� |t��?aGm���?g��y+���%6I+Ղ�/��a��/_P�{���):)��ܹ� |��9�'D+��&%�-d�� Dg�'DOP D*�i���i��-5��À�?���i)�'D�
�{`P�i�g�D D2�'D�):aP�ib�'D/��wcPj�2 Dŷ�-o���L�idP DeЅ-�Fm��,��m#�&��M.�-X;�fP�c8��-��-��?J����+�c��-���$��FgЅ-��Ǵ-n����hЅ-d~�Ҫz��aiЅ-j��/����kD"kЅ-l�a���?�а;�ct�4�})�.�c�
�{9��B��a�{��*DmP�cN��Ă�{J��&?�7)ʋo��	):B��/����{
�Y�)nP*DoЅ-��]
��X���pЅ-�m#o���,*Dj��qP8D����X�;T	�{��bk�)rP*Ds�Jm��-�{�o�*|t�Jm�d��ʷ������9�*|���buP*DvЅ-w��i=����…-��|��1�'l���|z�Ё��5�|���8|xЅ-���=����'yP�'���`��-R�XmzЅ-��'{�Jm�*||�Jm��;n	����y}�Jm~�;nЅ-��{�Ѕ-��\d��Xm��	��l��{'	�PCJ\����E�.��8��Jm��Am�[�΀8�5)�P�c�=*D(�'o)|X��>��*|�Am�8D�*Dr$|߁JmOG�'�	$|�*D��#n��{o
-�b�a�Z9��ǫ$|L�Am[
�'�*DX���J�P�a��g����{��a9�Jm�Am��+n�P�'̔�{Z�*|�P�a�Pv~��$D��'����ѧAm՜��8�'��i�Cy$|:����Jm��#nCt>�g��$D��Am�P$|��$D��Xmw��5��:�8|ʇ�a�v~�P�'6�$D[��ǗZ9d��ٴ!�'-�>D�Pv~d܀��z��{�P�')+�i���{�r�Ё��/�'Ł$D9�p9�`9�x��$Dc�Ӏ�����6���+nQ�vOI"�?��$D��i�g�>D���{��?����>D�`��\d�a�r	�$D`����5$|�����$|�$D�PI"�Am��Ami�$D��+n�Z9��t�P�?��a*�)]t>��[�sŹ�d�'��U�P�a߁�w�Pt>�нJI��	m"v~��J�P�'��))�v���$DH-�'?t>��o�磣�$D��*��*v~��.|��?�P�{�
�'�$D�\m��V���>D
�)[��g�b��}�P\m���5�ېNm����L9�"g�Nm��_���>D�P�i�\d��ݨP.|{��`��>D�6gI"aS�Nm��i�Pd~.|�P�?d��_����r���:�uNm��ݫ�>DZ�<��?�)�����i\m�>Nm�
Nm��<D0�?��}� ��?$.|3NmG�{�.|�P�?(\m#.|����pu,c�i�Pd~�
ۗA��F�<DTNm�P	 $�����Xd�8Em�.|{ۦ��<Dq:\mG�9�FEmKNmC�.D��Xd����kc������񯒋k�k%6�b5
�{G��c���a��.�p���}G��c��n巍�c(�8'G��'��<D��{���k��'��'��|�ǩc����\Nm���'ߤ�c3L�{��'q"���a��(L?�������'�Pd~���a��Ad��\m(���Q��c��'
�y�	����Nm���v��'À�c���'{��a����P.|)�ܽлco��׷���g����.D;��'n�<D�P�-{�}����H�.DY�}.�C�����P�-I
�P�{�D�-ʇ�a�P�-��<DϖƮ�	ob��P��-	�����$�f|��{��큋k�Ќ-�'n�i���&�Ќ-�P�-_�i��=��݀�c��-���}�)D�Ќ-=��9�-��}�P)D�Em
�_�g�͟�-��&{����P)Do����Ћk��-�P)D�P�-�-{��@G��c�P)D�P�?�Ќ-0�Ad�P)D���'���s
�?p��.U��'��'a�)|��8����Ќ-�P)D��)|�y����P)D
�Ad�P�?|
��9)D�1D�Ќ-��Ad?�����-G�y>�^9_����?���y>)D�P�-��)|��5��<�}G�y>�P�k�P)D@"����_+p�-��M""�?:��)|��)|���P�-��{~�P�-:�������)|�����˹�?�PDv�2�>����?��)|�����&���
��-���_���p�f��-�P)D����k�?�К��)D�Ќ-G@"�PDDv�ڇ�Ќ-[����f�Q�+�?�Ќ-��j����#�k{��}�P@"�P�?�LDD�P)D��Zd��_�P)Do�&��-\d��Zd<�c��?��)|	���y�)|����/�?:�m~����P�k�)|���)|��+'�)�Zd�����)|���P1D��)|z�f��Zdi���PEdw�#�w�Zd��y��Zd�1`S��?w�cG�y>��{~耛k���k��B
jˑ@"]G�j�ܣ�P�y5�|��/��Ȓ�n��<�ۍ���A�c}�
 �'��PDD���7�-n>���l��>���o�
�c<�x�DD���5�о�d���P�F�i�6DD�Лk�@���
 �X9@�����y�@�w�������k샚y#JX9�DD��Zd��i~�\d��U���8�}�(f�,�P�'��o��i~�&���{K��Zd��F"�Zdo�� ���{v��_�ɁZ��=���ʐF�v
S9���{	��h�� l�kb��}��7�X9���<%P�c�� ~%�=٘Ęs��М-��c�� �&腶����-QWm���c�]�⁛k���-Q-|O-|Q�e�Wm���k�܀ќ-�-|�{� Q-|~�)ц?ќ-74�'ћk��9縄�	�Bv-|
�i~��̾:|��ۈ�4Z��
 �Umǂ�Q�-D���o����'�/�'U�)QUm�-|�'�-D|F-|
Q�aQ�'��-D��o�f�Um�aќ-s��i��iQ�'����	f�-D8
ܸ�/�'�-|�-Um��B�ף��{%���Q-|�+QUmQUm��'��V�<� w��-�>|ō�,��-D�i�8�o��-DQUm��iЂ�8�i�-D�{"�y��	>���-Dќ->�o�-|�A.n!�"|�i�-DQ-|�� ��o��-DQ^d��- Q.n!�i������r-|��Cd���"��'%��i����#Q�'9���<��i:�$$Q-|���%Q^d�i��i�/�'�U]:���3�-D&�-D'�Zm�4�'	��>����(Q�'=��b)�Zmq��*�i����۱�⋱Zm+�i,�-D�-D-��'.n.�"|˕-D/�i{�}�m~9�-…e��(�_"��Zm=m~���x�Od���
�����-䀛b��ozOm~��g+�k0ѩj9v�ՂZm1��}��Zm2щkh�{v�.�Zm%��d��jە��Y���3�"|)Ð�i��>Q�2
��n聩j�
�M+11m~X�2\����-����덉k���j+�8؀��>)��Zm(����q��Z#�23m~~2.n4Q^d�)� ��j9��:7�d;ƣ5�Zm
�6��aU�Zm���a������{7�Zm8D8�2���9Q8D���c���aS��b���Կ�Zm:Ѡc���b;Q8D<�Zm������c�Zm�8D���c�t:x���L8D=�Zmm~J8DX�Xm~)>Qm~��c?Ѡcp��%e8D��Q�=�t�Xm@Qm~(�'��cA�2<8D���k�2�"m~h�Xms�-��%[���c��|Z���BQ�i��fY�β�8|@�y�Ɂ8Dc�2���k�oCѩj،�b���ŢcJ8D� �y{��}��	��XهXmS�f�+�iD�XmQ)��EѠc(���iw��ւ8�ǑN�FQ�iū�'���
�Xm��ir���y�Y�<�}Pj?��'��ۇ���j��GQ8D��8؍8D. D�
��FuH��'U��c���|i��bB De�}��I��'	��'�az

2Z�f���'���c,��c���JQ8D��aKѢc��'�8D��XmLQ^m���bP^me��' D�2D��R�XmŶ8|���|MQ^mNQ DOQ�a��'c�]���6P��'
2QQ D͐�'^mR�&D�^mC%2DS�XmTQ D��jUQ�a3 D9�i�)�Xw�F�g^m��/( DZ�&D�
�6���/)��V��/A�ioΣWQ�ia^m���'=uq���+Si#X5����m���Am���di��/XQ�kY��'{!^m�y~Z�&D[QMvZ>_�\J/��'���d�
�\Q�>i#�I"m z���_]��'O���ل6�R��/s
 D^��'('��k�OI"_�Am( D`��'/*�� D�*#��j�6�i���u>^m<��3Q�j<�" z�&D Da�&DbQ
2�ud7��#^m��k� D[FncQ�c��as��dQ�cSi#f�C`eQ�cfQ^m	�>��������cm���gQ�-hQ^miQ�cu��	-�bř&DG�jQ�cc�})\ms{�e��	g�\m���<ִ��b�\mkQd~c�]����o&<|��k���t\lQ�c�\mގ)��fw�
�cӃ�{\m��imQ�c<|���onQHd���o���o��i�۷����pQ�b&�hqQ\mrQ�k3�o(�c+\m�<|�DI"Ł<DsQ�cj�<DtQ<|u��i�\mQ<|��q�'���c���p��i�<|	_��<Dv��i�w}5��)jp�9x���۾�w��i��<D��x�8
�b�Em��ax��iy��i,�<DzQ]?Em�5\m{QEm���o|��i:	�<�ȋ@+}Q�c)
4Em�����"����~Q�cP����}��}Q�c5n_�}6�"�Q�c�=n�}�\mA�c�d~���\m/����i�+$|,�<D���}���Ԁ��QSɼ/\mXW1�Qd~8��dEm���i���{�Em	��{�Q�'Ё�of�'2O<|�Y��$|��ae՚Kv�$|��_��߄Q�'G��y�Q�-d��id�'��$D���/$|���iD)�'i�<D
�^9�<D5Y���=��^9���}���b��݉�ae�QEmy$|{8�'�Q�-���ae��9�Q]��i��^9��'�-9��4H6$|�
�'!Em{@�񂶣5��i�QEm	
��$D���2Em��A$��'gEm	��� z��9��Y
��Q=n�Jd.�-���큌-��^9̓aes���7�-n��ae۟f*���-��}�>ɩ�ae"�^9z�m#���}�Q$|g���h�'
c1��ae~��9h�`��o�aen�x0��-���e�����ae��e�u(���+�A$���/�3�'�3$|��/��m#���o�Q+Dm�Da��&ִ��$D�Q�'��/�Q�'�'��$D��{�:�=�-��$D�Q�y�oәQ�y�Q�-���������v���y93Ò�F���jS�k��>)���3P@"=��k?�jFD��>>dw��ή.�y7�F�4�Ȁ�}ل��%��k��9w�Q�uI+j0�i�@"k���DD��[9�Q�e�5<u􍫣����c� m���4�jd���7��L���_=+ܜQ�io�oANd(?�DD�Q�y\d��k���i�'G9�ydw��a�{�Qdw���6Ad�Q�kp�۳=�DD���ņ����o�����'��_�!DD�@+J&�8/����yϤJm��c���b�ݠQ�{�Hd�6�-�ЩcS��>���cW�y��p�[�S���Jm������c�Q�y�ѩc���E���+t��c�$�{D��%��+��y��ۥѻc
��<�}�'�
��Q�y
IקQ�?�'�����}��]��4�)�����o{���d�^���+��o���c�Cee�QDDŐ��yt��+̩c�����EdJ�Y3��{�f�Nd;�˹�c-��>D ���i��ag�%���Ֆh�Q�}���|<�}�ѩc{��q��������g���cJ�y�тk��]�a�+��Q�k����p���%�Jm�
۩��Մ�ݮў-la1m��ˁ�ں�-
�a����ee�Q)D(���ȸ5�o\�&�ѩcT9�d�(����o�ѩcH6�-L��ў-�)D ��o�����')D�c����=)D�ѩc�8K���$��-�)D�ѩc-��bӃ�ot+)Dt�i~�ѻc��o�ў-�)|ȏ�޷Q)D��i~f��ob�;|�)D� 뀦>S�)|�Q)D�::�;D�?�Q)D�ў-&�Bv���>��-���b�;|���/��)|���z��p���Cdp)D� W�cev:գ4�Cd�g6{zd�g_��/��)|�;|�Cd��o?+����)|t۾��o��cee�Cd�i���oꯞ-��ceғ)|�����-��Cdf�,�����u0�;|��'���y�)|:�����ce��}��CdL)D��Cd��;|���/��Q)D9{}?<n��Cde��@��ce*�=ɣ�Ѐ$)D(�j�Q)D3��9�3��۔;)D�he��Cd��b=�he���?p��o��׿<n�!�j��)|��CdKF���)|s?0�i�<n�&�c+��atۚ�-��c��he<n��;|=���Ё�� Q�)|�Q�cW}\��oL�)|��=��.o&��ce�Q�c�y���Cd.�he��9��Qm~��Cd[�heG�$z@+�o�Q�c��ceoߣm��f9��y�cJ�[�Q�b��ce�Q�cy�he*��� r�Cd��o�Q�{j����H�'��c�Q�c���a�D�i���m~�z���b��%=X��х-����%�s;�����;�c������Ё�����r��D���a��Zmam~��{~�&�R�t<n	 D��a���:|m�al��݋I���he�{~v�#��{*
�b���|�Q�c��!	��5��cX{MT�heݝf|X5S�� �
�c֔F�׀he"�-H�c�Fv5�c���|/�4��V�a�c��o��oJ��-�����ieFv��1-|��5o��o���k��--|oo�t��Q-|�Qm~���|��C�Q�-�Q-|�Ѣc)�'6L-|����u�)��'���z����-|z�X"2�b~�Q�'�� ���$H�>��-D��x�6��o����J��Gd)��g��?D�	-|��?D��i�	��2-|Gd*�?D4�\m��N�m(����<-|������:��/s�-D�;�� zI-|=���Q-|A�'$��/�)�f	f��/ge�c�u��|����?D��>�FQ{�cv��-D�Q�'����at�-D��?D���c���1��'�Ѣcei�
IW�?D�g���J�e��Q�'�
���-D��?D���(Y�aӂ�A�Q�'��"|�Q-|7�CَGd�
���Q�'[���e	�aY�u)��gǡ@d�
�@dg�'�Q�a~�	)�=����'gf�'!H���l�-|��@d�Q-|�B�'��Zd�!2��?D�;������oQ�-D�Q�'eE�a�����?D3���Ȁ(��L$Gd,�-D��?D��o@�������QX��QGdYI"���'��-DÄ�x_�l)I"�a12��@d�Q�?s����a��Y���q�2�S?�'.�@d��w?�>o2=�@d�׀
3�>)I"a�d��@d���x�Q�a=��I"��Cm��oו[v����	�'�Q�am��%���k�ѥ%��>�	�a�z�Q�'e�b
�{�2�c��%��@d��k�"�b��@d���Q�b��{���%���(�ѻj��>�"|r�@dR�b{�����'��z��k�j�J��%/'�b��"|B��'QW�R�b��aI�@d(���ҥ%D��i(��j�ť%R�k���xa��-	��Կ�{�
�%����I"k�bI7�{A
$ҥ%�;�l���\�У	�W-ҥ%9�w�9�7��c��"g�?|��I���c�"g�p��(�>�j����4��Jdc���Jd�g�;Ƣ'!)�I�b�g�je�f*�i���|��Jd'�Jd�*�Q�f��:
����\	��Q>��%ҥ%�Jd�$�b�f	 a��k��I�s�2@	�Xd��%����I�Y�{+�f=G�i�	�c
ҥ%�	 ��fؕ����%R�b�{R�i�����%
R�c�+ڂ�Gm�'l�Jdh
Ca\P�b�C٩���o��>m��'��{�iR DZ�Cv�&����)z��o�Cv�&|&�Jd�CvX�z�)�u$���`�Cv+�f݀�'s����'��'���7�,�Cv�u$H��'j��	A�u$R D����o���R D��'�u$Q�Jd�Σ���	\�&DJ�Cv�&De D�,2D�I���o��Cvo���`�&DC��'L�&D.��'�I�o D�&D�f6��b�&Dw�e��4 D�u$_�Xd5��)���
��b���5��&D��{lf1�&D���R	 ���-$51���'�P���&D%��/R9Do�2C zރ7���bq�X���9V��`���b�i*��f�#�&D$�f!�:�&D���b�Kv��'�Cv���u�9D ҂b!�Cv"��'mĭh��'Ã�'���')�u$9Dh D��m#�-zi*��58�I+�u$��p�CvŁm#{�g��u$���ί�m#92D�V�#�&DV���v1� D�m#$҂b{C���:�&DQ��>�7�a,��c����@�fւ8���cS���ۉ�$�a���b�W "�&D��M"~�&D�P�c��fB�M"��4�&D�ʂb���bx��%�f#��b&R9D.Nd�Ƃb�f7�j'RNd��aؚ4�ĉ�coNd׉���f&g(��i��m#�ܾ��o<��Nd��a+f)R9D(���*RNd+R��,R9Dd��i��fE":��-RNd.R�Y9���xΣ(���uD�a��":��i/R�5����H'ٕ0��i�Nd��m#�q1�Nd��%h��܎��c,�m#t\d�E",�>C��iZΣ1��i2��z=�F�9��i=���:Nd
I�|3�f��i��}~��Xm�V=��e0Nd4�fGv5Бd��L��c5RNd���b��ܾHd6ҋb���jY��7�f���h�
Σ剙bQ�V|��b���h8R�cf�"Rʢ�r���Y&C2�Σ肋b5��������Z
OvރD]�	��Nd 
m89R\d���:RNd��z��avv~�Gv.$|�;Rne<ҫc
م�܄�X=Rne��S$N�f�&Nd��zo�o�Ƙ<��h>R$|��c?R\d���@ҋbo��o�_A��i$Cm��%7?|?��i0$|���o��{B�Hd��$DCR�b���5�$D�I���¢Ԃ��<� D�$DER�kv�=F� ���V$|F�j.G�&r����Gvv���-G�� �f#�'����&j��Hҫc�� ���n݋۽�¢I� d�Ɓr��{JY�Jҋbk� ���쁙bg�'ŋ�c��؁�E�)�'��c��{KҋbU!TT���c��'LR�ea6�'��h� Xf�@�'܄�h&Σ�
 \�'��6�(�NmM�+|NR�'Ԁ�dh� O��Q)�f(��5�]�`���PR�'€��d�����t�g��+|v;f�W�'����fi�!n�'� $|�5�b���QR"DRR�cr���f�SR"D� �'�AΣT� ��'݀$D��IdH�_"�����'�"D
	��	�۬�#[U� ǂ#�f"D�D�i�`*v��V� �NmWR�&��cXR�i��C��>&�?Y� ZR"D��"|�bw����E�i�� [R�c��foZ�Jh��-.Nm\R"D��"|]��+����I�������\m��+<�"|�G�i2<��\�'^R"D_�"|z�i`"D`RNm��bڋI+\�'�S�Yf�d~��'aRK�b�Zm>�{��'��_"X�wU�"|M.\m�f��"|z�ic�IddR�b\m�#f�;�{"D��z��腽p���[��"|�����c�Nm)�+D��"||�9�<�bc��"Y�_"�Zd�"D�G�beһcfR"D��c{�l�{���c�2gR�{D��hR"DM��c{���!iR"D�2��&jһckR"D\�d��!��D�ڸ�c�
�c
\mQ�SX�r�<�"|k�2�"|�cMlR7�o�2��2����m�"|�&��{5x1�{~�'��"|nһcu�2���~�"|8�{~�Σ����9S�J���"|7��b��a8��g���cj�΀NmoR�{ғ{~s�2pҢc���cf6�{m��0/�{qR�c��̴�Kb�`���Շ�b������m�a^	�cj�%h��b�cj2z��܋B�{*�a��g���ΣrR�{*�닶|)DcJ���,./|��,k��� >�2��۟�c���s�b~+�d��v)D��So�2]3	 ���&�
	 ,�Nm��tRMd/�b~Ռ�cu�2J䣎
	 vR)Dw�{~���&(J��)|:ף�2xR)Dʈ�����y$UdO��Z�1�e;ue��{~hΣh)D�/Dhue���a��m�����yR)D	��a���'�	(J��3Md-Udz�)|{��'P	 �)||R�-Q	:�}R&|~��'�'Ud���aUdk���;|���'��9	ue��g��'�
ף��$]3	 ˁ�X�Ɓ�;|_��	�-N��a��'5�)|Ȁ	�RUdII&|V��a����=�R�.�kۄR�>���a�Σ{�̴)DZ�W���a�R�k�
�.��f�=�#�J�f�NNv���'�R�>�b��ף���a	 �R��:�/$�Nv��4�zej���&DƁ[�=ݸ��&D��zeWUd��&D�	 (J���3%��x�	 \�;|��)|��8##�.o���J����a��&D��ze� ��ze�/�c<��a��>��4Udn� ��-��&D7��a���'��ze.�;|4��?Dz�a-��'���'e��玧ze�ҩc��ze������a��gt�!*ۇ�6�7)�/��גr�]����d��	=��c]�ze	�c���Y��ze���닡ok��c�]��Fah-&|�*A��&D��/k	f�
�>�&�/( Z�R�>�&D�R�>���c?	{–	ף��>��;O��{�-z�����4�ze�{���&D���lNv��ze��&D�ǩc����fZd��D��!��&D	%ۗ��OD�gG�ze�P�z*���i~��zen���
��5��&��{���{Ȭ�x2��ז{}�Ңj��{
�l�i8��ze<�{���j��ze��-zŜ�{w�-z�zev��D��d�z<����ףc��{�t��%J�Xd�R-|e��炂xe��Xd���{݀�cÀ
o�	Ӂ�{t����	�XdD�%��d����{�R�a)J����xe(J�s�Xd
դȀג�R-|�a��{�9�	��C��{p�U2��-D��?D�һj�`�R-|2�(n���{��i~(�?D.�Xd���{,�����i(J���Xd�飥�-D�R�a���-|<��i{�
 Ł}#��iH�-D5�?D���|��/��-D�
 ��i���d�V�
���8��-D���{��i5�?D�W1|��*�Xd��b�RUm��?D)�z���jJ���x!���{�
 ӑ-D�ҢjG�'�W�EҢj���io����{e�и*�tI)�']~ed�@d��i��Xd�:ף��iw���9��"���bZ�����Vpy��b�����i �'�R�a��
 ��a��xe	���b$�Xdk��b�����'�	�J�"�7D�h�'�R-|���'�R�aV
HmuR+D�R�c�
�6��?Dw��Gж�-D��+����-DG�?D��bj��i�����i�R�c(����҂bEm~���i��x�Ԁ����?D.�
 V��i�k�e�-D��c&��W����+|��iû�'�D�c{�
 ��i�����HʎF�R[���')��8�2m~?��T��+ތ.�Мk�2��|6�^d�R�c��'���
�Hd��'i*׀e���ߵ8�b���k��մ��c~ף�m~\�+|:z埯@d,�{8�
a�gp��.�������@F�l�j���+��o����M���.�{���'K6�+ϡ�b�҉kԀ�Ȁ�����'��#��/.�N%�R|e����{͓��+Dy6\d�����R\dk��P���'~�F�;�&���'�ף���R\d�?zr�]�����+�R+Do�#m|e��gd�؁�\d��{��+|�=�5�|�j_����R�c��%h��`>�|e�R\dd���O��nG�+|��ܾ��5�+�`>�!�ij,m~�$5@9�ks���R\d�R�i腼��|eӂi!�R�i�R�i�R\d'
�����aB$)��u�R\d�R�i�R�i��u����]vM:\db+�A%�	ւ`>�R\dI�-�]vȃ2��R\d9)�6�^dYr�,�i��u���Z
�i�u�]v����Y���z'|�g!�
g���i���1�r��Ҽ�O�i�
�\d���&�ߣ��5�R\d,
 D%�b��a,Ņ�1�2<]|e>��׆��oQs���a,��c���&5_}�bB��aU|e D �R�&�R�bj���R D\dl�bi}w�R D�f��R ݀Cv%�b���N����);�bՀr�R D΁ڣr	�C��a,h���RVd	Ddi�"g�҂k�Vd	�b�Dd�RVd . D��Y"�oV2De�iZ����&;��Q�7u������-Vd*�i�Ree�R�k�۠9D���-����~1��Wd�:Uv�����'<2zjӣ
��D� �R9D��ߣ[����-�9D��u�:�>���ce��Ղ�/Ё�D��/�	��R�k�R9D�R�>��ڣ���p9D�RDd��6�O �RVdo�҃y&�@9h!Vd\�/D+�b
�/�R�>��.���>p���Ԁ�o�UB�/D�(}R�:�/D��٫�b�RVd;�b�&�/�&� �d7��/DȀ�5��y�Ro� �R ���)���.��l��b$�ϣ%�/D����]�ue|��>������&f��-o���҃yW��ʻ�/D�R��`�m�R��׀/D,�*#�[���*���+p��yi��I�+9D��g����C��U��/|��5��kl��is�_"b�c�>>�ߣȀ��Q�s�x��e���?���iՀ�w\mjXL4�(�����/D��Xv�R�>�҄?>��i;�/D�/���i�5�c�R�>���i+.ӣ�R�b�>b��?���ba�b��i�R�>	�R�`y��?$Dd>�Kd�J���/DJТJ�9�⽃U m��(���i��`#��������c��9D�����{��c~����iS�b>Zd���k���?�|���}J�K���i���l�b��Xv���iƁ�SރPòߝ�mJ�&���L&`���	�c�
&�:�CmS\mS�cӅ--$|r��+�+�U8{��-�7�ӄ?��+S\mt��!'��G�&��iӅ-����i9��>;��;\m(J����-2��Ӆ-���i���?sC	S$|B�?y��if_�o$�c
ӄ?*�$��o�J��i��H?��?S�?�)c�)S�cY��?
Ӣc:�?S�b�h��dA�cj�B�cӅ-S�cp$|�}\�?��}m��,S�ch$|U�b~9�;e��<�ڣb�nӢc&Od���cJ�M� =|f�}h�q�eٷ��A'��Ӣc��,Ӆ-���0Ӆ--��
��i��{������?$�ߣ���@�S�?�������&����Ӆ-Ӂy�)p�U2S�-偂�HmS6|z�-��8��ߣS�-y��Z���NS�-	�����\x<���f'�'�B�?��Id��#��F�D�'���-j�CmS��b���A
ZP+��/S�'���s)�_+ ӭ>��-"�O���?��kd`���>Ԁ�%L�Id��&!S�?�k"�Id#ӭ>����kd�Id���羂�gz�`#$S�k�3���g��h�Id��%S�>��[�Id��>	�F��\v(�ߣ��>'ܣ�4�$��%r�'Պڣh� �6�k=���9��9�e{�&�"|���מG\v2�Q���V��k'�����>��"|�4Z
\v�F�'�X���D�����o�#���(�s�o�Id;�'(S�>h�58�Id��k�@��?�:�ߣ�
)o�+
�#)�"|�?�>" ���>���b�>h�-\x�}�ڶ��'\�Id��։J���b���>9%���+�D&�y\�d*{��u����?�摭>���A*S�k:�{+ӭ>��X�	�k{�%T!��l��>��L�ze��k
��,ӫ�~���n���^X��:��d���c?��k�i>-S�>
\v�?�����f|��.S�/r�]�����/S�>D�����cp	�>H�d*��ߣ�޴�f��\v}ڣ��(0S�Y�ˋ"|�i>����݁��u��>fe�
[��gӣoд͚ߣ��i>q���*��-}�i>�&�
�2���f�t�oȀ�:�i#��{l����	U�À��Ɓ�{�֣j�׀�݂�7�Q�3å��k?����j��::���r/	d��ٖ�{�Ţ���ݑ)D����3'J�b(�!'���1SMd��b� '
���z����)D���hMdW�f2S�b3S)DZ=������@�i>�:)D�֣�@�i4�f5S)D3��{O�9��f��
o=b��O�5�$�1)Dŋ)|�X��Y�
Md?�ߨE�L(�bϟ�i%��?6Ӌk\CMd7�)|�KMd}(���;|�*AI)D��i��!`dMd��&�)|聇-8�)|�޸9��iXMd:��i޴;Ӈ-��F<�;| ���Aw壷)|+��-�#���9=�)|���wŋky��'���a>��i���g��'�裣 eӣ?�;|���x��@�)|9���fY�D� @��aZ�)|���Q��SAӇ-�ϣ1��-d�Bd(JMd�9�?	�-9 MdX��B�&DC�&DĀ�()	 ��ؒ{�&D� ��WTD�f�ES�bF�&D����9 Md��Gӌ-HS	 Z�&DIӌ-�
o��i?�&D��`JS�cN�Kӌ-:��	 ���iLӋk�i*y > MS9DN�&DO��i5��-�)|Pӌ-�����9DQS9D�Ќ-�i*_���ᇌ-i9Dψ)|R��i�)9D��&DS�;|��)|��9�'���c��ǩ���-�-���'o��yTS9DUӇ-t��k���'����h��'/:�&2���i*���kVӻc� �?�:d�I�i*���]WS9DX�&D@��cYS 9D0��k2<n9���� .�{p�9|��b�&�{��&D���=������wX�(���R�x��&D.��Dt�{���-���k8��g��,,i*ZS9D�٢V
2<3�8
�z��[S9D�"�d�+9D	��Ox+fO���\ӌ-��{~�-���c7'�����zp*9Dn��D���3�-�z]ӌ-O�{��Σ������^ӌ-,i*�H�z��-�n�+_SY��a��{�f�i*�����>`S�{%`t��k���y�$-|rHg���k	�aS�-�b8{-|bS�iHH�>F�i	�l)����>a�gxցNmcӉkC!�{�
`dS�i���v��+����	�g��c������,n��De�?Df�-Dg�?�����-|gS�i�$�i5
-|w��S�-D��R�?.��Do�+P�{~KA�{�i�0-|��yS5�{Q���ڣhS�ii�?Dj�-Df"�{{-|+�-D�2�c���>�,�c3�?e���Ɯ�?Dk�-D�����8��l�c~��e$�t��f2�����mS�>��-n�-D����=s�o�-Dp�?D{�Cm����C�o�#���A�A腣����;Z�-D���߬���B��k	�ԙCm3�(n��c~�٢�Cm�Cm	�!�tdqS�i�I�r�
 r�i���~�G�sS�i����d��r �c~tS�iuS)��Cm���cX-|�5S��vS�i%�V�
�me}��pnk5��@d��A
�+��kX�k'�-��'�"�9=|���k>Xd€�*v:�kԂi#wS+DW�>�k~kXmkR$k��&
�>k�c~���G����n�?D�����k'�㴑�c~:�裒��	�6�x�=D������ ����FkR$5��c~��>��Cm���8y�.皀Cm����k��=DoR$��:����k�cQ�kb��8z�=D���k_�'9��Lk��������ƒ�o™=D�:'m��k�ie�I�FP=���k��}g�>{Ӣjj�h��'|S�{�Ȃk�?��&��?}S"D~S�{���-��'̣���S"D�Ӣj�S"D�����=D�O�k�S"D;̣�M�!nه�ݞ�?m�{J��D���a;"D�.�{��k�S"D[�����a�S�{��?���1��i���i�d�%��R$L�k��a�+|��=D�S"D�,���>[8�{|
4Gb�E{���f8��=D�	壊ӎ-kR$���iW�"|9�����z��"|D�J���a��2"�>m����գ	*�P5"Dv��=D���i�S"D���	*�"|E�i��"|��ݑS�-��{.�r>�"D��!nh3�{��-w��I���{��{���i�f8݀�?��{��!n=�%��"D:��$���-"D��"|��{�j��"D�Cل�i�}~�
����{a{�	ەS D4��i1�i̜�i߁�-G��-�S D
��i���-� D��a4�j�g����{��i�ӏ?q�"|R[�D D�-��"|��ň��-k>'� D���i��f8���i��iu���m D9��<_�k�ۢ��?� |�S D�ӂb�*�<7DdȀ��T��Ԁң2�����-�����i���io��'�S Dzm#�?��}~�S Dd*�ٳ�4V) D��ޣ�g~g��{Z��#g)z3��&�*�s�-���{	*��g~���{€ݢ�SGm��݊��{n�-4�y��
}��-�Gm뀄bG&�y�4���bw��šo���{����J��,�Mb[��%��'
�Ȧ�f��	 *
���ՂCv8�5ZDY��e�0�yB>'g���,'Ɏ/D��/D�S�-�ӏ?́me<ۯ{�/DL�������*���/D���� �S]d�R��/D���/���>a�Av��/�	`~Q���S D*���Av���	7炂*�����Y��z���Fn��-co�������S�i����2�i�/Dw/�+�Av�����/��/D��i��i|�J2��b�,�iБ�Ă/D�S�z�S�i����(�ie��a�&Ϻ#�i��'��&D��裵�Av�;�L�/��zm"�ijUT���85���J��,�3�z�S�iZ
�y5
�i��&D�S�z�S�i���iҫ&Du�a�5�z�S�/
��i9�i#��g�	 Ѫ&D���'(�zT�#��i�_��/D�	 :�-��&D)�*��p���$�&Dd�z��'��i��?Y�/D���}��iX�ƽS�>ˆ/D�uka��b^��i�'��&D|����?>�>���i$7�{��4SG#�i�z��/���h��8U�Jm&|�բ��Avk>�>��� �a�*���i�S�i
��{�ӄ���&DY=����`,&|B�iQ�zo)�H�i��in�&D(����S�aX����S$|
ff$7��&DaY�#�b�S�>��&Dg$|���i�S���S�><v>o��iG��i���.$|�S`8��8��&DТX��`8Q����ib��{b5��Ǔ��i9��m}g������i��	��fb�S$|��8o��_��'`�&v�����CQ��z���v��ٛ�j��$D�Y�kXmb�{�$|V$|G8�{m$�>��j�Ѵ���+�$D�/��A�>u�=��(n[9U��S�>�����z�S�>����Ng���(L	�b�S�>�q�Ղ�{�S�>��-�@v�!�b��Y������&�l*щ(nĂ�/(�ga�td���@�9X1��b%(��wێ�T/�`����z�)$|(���o2�/�b���%��ۈ�b�S`8%�bG�&�A���nZ��+)�[d9���e��;��/�Y���$�����Q�R�����/��㎀�'"��/��bE�b�6���	o�S�cD�`�SFd��[d���{��S�k[�[do�`��&#�[d�����S�iЊ[d�N$R��i��� �S�'O�i��䣳��S�i�$W��SFd�S�'��̸�S�iy�F��&�Fdډ�'��`�S�c�)���S�-��`X����(n5�=�	����Fd#�b�(n�S�'w���4��-�!�b���b���/r�i�S Ł�/ރ{}���l*�`Ps�r���X��S �S+D�ˣ�S+D�Id��[d��`�\��ܜ �o�+d�B	�i��{o��S�&%�����h���/3+D��{D) &0�|��[dv ��lŁ�/H�{'+D���/�(��S+D)���������+|:ҿ>���z>�N$��i*Fd'	+D�,�b�S�i�
F�{F�+DH�{�S t��cu2L�2��S ���>��N$�S�i��-k�\$"�+|�S�c��iN��l�Nm�S�'�g������-gd�f-�P�&�������z�S�-�S ���Nm%��zm}�Y$`j��>���`@��z:�[�\$��>W�d^͌���zo�v�C���-�+D���z�S�-]�f��{���>��Y���r�j�['�-�S�-�+Dɀfȃ{�A�-�S�>k�\$�ӭ>"��>��f�y`�S+D�S�-��?�S�y�S+D�Xm��>�Hv�4]�fT�-�� ��-;�y]�f�+|��z�3'.�-�ݸ��zT�iT�-�\$T�ia"�%U��>���7,
)D<��MC�-�>;�-(��+���@o�\$�2)D.����
�?�K�8T�?T�-5)DT�iT�)|�`���zT)DT�-ۚ�	TMd�<�-�$��
��a�fDz�a�!�?]�f��k��a��)|��yԅ�
T)DMd���'g�fT�-��)|��T�y���aT�-C�>*�2T�-T�i�
:�T�kw6�3�T�>�J@��	�U)D�-T�>T�-'ˣ����T�>�;|T�-�`j���܁�-��a�)|&�'34�?m��@��aȗZ��6l��ah�;|��h��b;ˣ�iq��}_dLMd�ˣԘ-�Hv.����i���y9�i��a*���%�3�iT)D&��a�\m�ܘ�����a��kc��� T�i9��]���-!T)D��"T�i��� #T9D��id��$T9Dؕ)|��|��y��-�69Dd"n�y�%T9D6�-���a*/|�ˣȀ�Q��y&T9D'Ԟ-S��a�N�z��� w���Z��a�Ǵ샚y�ˣ!�)|���k��� (��a��Q�/D�ݣ)�/D������k@�*T�z%��c�Fm�ˣ+T�z<�/D!��׎��k�[�:7F�),Ԟ--Ԟ-�z����k���G�/Dݥ�cp��_.ԩc�k��z��`/�/D;(*V�Kdk{�lf!V~�#��0�/D�#@1ԛk2Ԟ-3�/D�[��âjǞ-�� �j�c<��z��k4Ԣj�f:
ˣ��/D���k5Ԟ-���c6�Kdg�/D��`���-�`*�Aj9D��p+9Do��c�� 9{}�����z��i7Ԟ-�J9D����Y$�ux��iY�6G�9D�/D8�Kd��l��>�����i9�/D{`���il��.:�/Dc��-oˣ;T�zZ
-|<Tf���i=�/D>T�c=�/�/D*��i�-|OG�b?Ԝ-%`��>z�`��/D�i8]�a@T-|���i��/Dl��-����/D������AT-|BTi8<��cۃӸ�ٗC�-D5�?DDTi8倜-ET-|>�a��F��i���-Ă-D��?DGTu~L)ʈi8�c���H�-DIT}~4�a��^"�h~��3`��a�����p�J�-D��?DKT}~�}~:7�LTu~F��}~=C�|DO��iݬ�M�-Db�?DB�-|���N�-D;�OT�aPT;�9��i7�R�Ȁ��O� �����+,�-D!�dQ�?D;����i���-RT$gM��{*�����i��Ќ`���ƫ�^���c�=�x�	[�j`SԢc�i8ˣT�-D�G�L��c��%UT�a�����as��-V�`Jɨ��'O&���cWT}~	&XԢc��y��"=YT�aZT-|[Ԣc��%�Od����\T�c�&�8�-D����d�?DS�b�P���?D���G=|]TOds�h�q�^Ԣc_T�'��?D����K��-�'� �ݣ�
Ԕ�# l��$��'2�c`�-D9��\+�&aT �����ybS
�cF�'#ˣ��t{��F�$g[T�b� ��?���b�=D
B���n�.�bT�'<8�l$g��b*�cԢcdT <8;�>��k�#�'c�o�Odh��R yŢceT;���m��k��i]�z��c���ˣ���c�Fa�"�i� k�B�L�i���'f��4�Ƴ��x(��+�� 3��kO�zm��cj�Cd�/�i���'gT�id�ָhT�/iT�zjT�i��oo�`ȗ�kT�i��"|��
f��`ʋ�Q�E���'lT�'���amԀbo��c-�"|*���nԀb:ˣT�a5"�ih: w2Od���c7H�zoԀb��'v��a<�pT�c��"|qT�irT�'
?�0 �?����asT�i(���&p�i�)tT�'uT?���'vT�h �%�2wԠc���bc����
����&.�ixT�>���y��azT�i޼f8�?��a�?�{T�i��"z���l��ip�ij��Z&
��%|T�i�`}T�id���`��̽�m8~�`d�⁀�Xm!��d��m8T�>�"D
?��ԥ%�ˣ�>.�i+S�i��{t�z�T�i�T�cGB�"�>��c<��'-�c��Q�9��D��>��m8��g��T�c��Ă�b����遾wJۯ��"|�3ÄT�{�c�, D>�2_C�(��k����	�c=��2�?��c���%ڕF�L����%Gd���~��C�?�� D`��vc|T�i��ck�Xm�ԥ%
?��T;�S	�i�T DX�?����z
G�����?�<8;�! D��`��c�/�6x�+��i�TdP�@��T�i+ D69]d{��cI��	���T�>d Dԣ��i���>��L�+�]d��Y��h�۾�����c��%�Bm���.��`V�u�Î(uu�u��m8�ԥ%oY$�T�c(>'�ԣ2�k��m8G D��c�T�%��E�O��fkY$���=7 D���ㄅ�'�ԥ%Y��7��'��c������i�A�c�-����>z��'Y: D�Hxr����'i7���&���>]?�c��Cd	��'�� �T�c-`h���ۯ��'�	�z�Ҵr��i�
ԣG��'��cɃ�{���&3�'N9�cȀۀ�}Ȇ4@����z�T�c���k���'*�`�|~냮Ȩ
\m�)�&D]��|��UK.�`�T�c�T�'o�����&DG�a	�Bd�T�zP�c��Y��'q�4oY$္>&� .�{�&D�T�c�!X�=)��&D���Q�c�T�a�T9Dv
�b��&D��&ه�'���/��&DkY$���'�9D�O��|~�T9DX��ġ�&D&\m���
�YQ��i� �T9D%�xA�i!p�i��>9D��U �&���i��&D�T9D�����'���'��i<)�c�ۯ	�cܜ�&�'ۯ���:�c�����&�T`���-�=ۨ���Q��*N�b�T�'��+�T�c&9Dh)h&|P9D�(�c���>�T).��z��%�Xo���:)D&�T$|g|~	��>	�&D��Î>h��&DLˣQ��z��3'��{]��>
\m�i��&D�\m[r���Bdt�&D�T9D�ԣR`8���z�:9D�&D%�`O��OQ����y"��kw�t�$Dg횭>�T9D�$D���hv6|��	�9W֙�Ɨۊ��>�`�Z"UN$|ޓf�9Dˣ��+J����T9D�̢2{,Ɓs�w�U�jY���׀qf��_�K�f�:n8�Fm~�Պ�\$�Y��T5�<�$DJۯ�i#�ݣ�`8��$D~Y�g�*�$D�i��z	�-�$D����B�d��φ�Z�@̣!��z�T�i��+h�$D��:nJۯ<�͸��>��ik���G�5S�` ��x|�E����c���>���?�$|������>���a�,$|�p�F��a���`0�ڀ��+W��`�� 9�P�!-g�+z~����a���(��:Fy���Fm:��c�ף!>$|���a���cʉo���a��k��[d�����cĂ�c(�� ���a����� ����ˋ�c)FdA��`�"ˣ	������-g���al�k��� ��[d耦��$D�ԫc���H�����h��c��$D5���,`#����a(�_�� �TX"�T�k	�b�$��b��:n΁����	o��`8��j��nȀ�����'�N �ˣ[
Fd�T�k{��2�i���&݀�'ه�a��0��Ɓ{]�s���aˋ�cyٕ���`��[d��G vԣ��"%��c���I8ˣ�`�� �	�z���a��:����Hx�ԫc@��`���&D�ۀ	&�ԫc���a�`���T�z
��`��{l'nˣ���&���'��Yd��=Dj��-=��`��0���cXN�zy���;��c�Yd�ԁbIFe�z3Щc{��r��ԩc&��br�z��n���z5�YdW#f�T\v�ԁb�Ś�ԩc��Yd����Tf#g���b���{3���5ԣ�ԩc��`��:���z�Կ>���by��c5��ԁbh��z���c!�\m�Z�Cm=�����YȀ���b��z5�Yd��zd��i�����ԩc���z���b���߂��ԁb.�{���&�ԩc����R�ϣ��O���%�� 
�&��i���b��վ�;̳=|>�"|�T[���jC(�&��Yd��;F��"|�	f����A{,�T�zp�&�::�T)D�T�z�"|I5Z�)D���cګJd��	
�&��Ydm�=D���zR#��z����k{�c�Yd���i��=�$��@���c��!�V8�Yd�T)DȺ�b��Ӝ9�m�"|�)D*h��;|h��z��)|�ԁb�T)D���� Md=���T)D뇕_V����)|���{�;D9	�j�t_8
)Dg�iցyb�)|��̢�6)DȀ�^�Zv��;|��)|�"D�vq�6�x!
?��T�Yj�`��2�����x8�>��%��&�)DÃ;|�� Ʃ"|��)|���L����&
�f�*
o��<���|���€����d�"|��)|����)|�ԏb��ۖ�*h��߅���"�������i���c��c����;���
)D�T���i�T)Ḍ�cee�F�ք�-����oK��djo?���{D�T)D�+z|)D�4�>`�?�)D�ce�Ԣc��i��w�46�T)D�ܣd�6�T]dz�-�G���T]d@�`�:���2�Y�c��i��ډ�T]d9ZQ���c��ۘ�����)|�A�'9�c�T�'�T�b]d��ex�	̣�"]d��b{��$u)�'v���;|)�bϊ��9�Իc=���T�bT�	�F�'��f�T�ct
���Y���L�c��Kd���V)�'�'5���I�c@�+.��p���z���u���Av�b�лc@0�c�T �T�'�T�co�+\����oU ��bA��cբc����c��'՛b	 ��U�iU�'5 �B����b��A�Av��$
�Hx*�iه�i�>��K�d�M]d-�-v��%���n_�}�
ۯ �Z��+)���_ۯ�&D5�c�'�J��j�;]d�$�c,
�-v�e�x@�m��i�'�B�i��	��&D�^vJ�&D<�ܣ�0z�̣�	�iU�z�&Dw+ܑ�-v�-��n�&D%K�b	U-|Oۯ
�&D��'āϣo*h�-|h5@�5�ԣ�C"T�Av��Kd��{9i8����X$+�d�&DP��ݟ-|_ۯ�
-|D�	��#�i8Q���&D�����-DC".�~�6�?D�i8�-Dn0z~� 
Ui8�8�z@�ɢ���i8x��J�)���Ui8�۫	)-|�-D��&D$�h7�Bd�-Dṿ�0zV�?D�����?D�i8�;��:�z�C�	�Bd,�-D�?D����-|�&DUi8ތ-Dù���
�����&Dp1h���%_���_�-D��-D
�� e�?D7`8�&��5_ۯ��e��Я�h�-D<}����Ufg��x���_{i8�� �-D-|�8i8�D�-D`�J��a�C"9��iUi8Ոb�i8�
�f�� j1h}b�Sx�
i8
C"Ոbr�@d.-|-�o��lՈbUi8
�xU�k*h��@dm�?D��	i�$v�$��-D Ui8�r,i8��ݾ|D
��ܔԣܒ��!Ոb�f�=�$"Uf�5�#�@d{@�$ՈbȀB�I"��?D��$%U�k���b̈%�%@�N��>&Ui#��'U�k�Od��x:�	�T�j2�շW(j���b���΂�$e�j��.��`8Д�'(U�$)��'� ބ2�A
uJ�毗�P����B*��'+Uf��$��a~,UFd��{�+z6`8#1h-��'4�ze��kY�a����.��'���A{��b�Fd���Qf<�/��'V��Yo��GLFdԀ��0U�a�*�a1UFd���ܷ�̢ٶ�bv���)�>h	Q�'f��'RFd)��ł�$Տ&“u,��uCʁa~@�ap̣�Fd2�oy��'3�o�Fd��y��'��}4�a��ܶ�$��a�a~b��kr,<��+��$5�ao
�8
	���cH�$6UFd=�+�����̢����7U�'�����k�����'�h��#hd�����	8��'@+D��>h���a-��'Ё2�Y�9UFd����2>�D�:��'�U &;UFdI&b nQ&0zFd"��'��\-Fd<�a=��'W�f8S��&��&>UFd�<�?�a~80z�F�m�a@�a��aA�m8��&'�	�D�XmBUFd��Ģ��f��z<�d��m8#j�Og�,هm8��p	��zrg�?��ǾgCU�`��m8V��DU Dg&�E�m8���F�a^�8. D*�g�X����5�GU Dʓm8Ցc�HU D^���m8�uxLDd&�I�m8OCnJU D�m8k���C�Cv��	���a�̣$�x9�Ψ D��5#"Dd��m8�2D�����)�DdKU D�Cm�����@��')� D� D�V,m�շ5� |� Dg2D�EmLU)��Υ�&K D�⣷��AȀ�	6"zr�����W�m8��8�4M��z�m8��-1=��<�Ģ�ۨЮ0z�y>�+|N�m80z��m8��'��<�)�����	O�m8P�f!�y>ՠ۸��G"i��Q�m8RU DS�m8�U;��Cv<��iT�m8UU D� |c)�v�շ� Dn�m8��"V��A+��$Q���WU D	����'q��Uh�Y��b���'� D�1hXU D_�}����Ȁ��dr��Dd���i/�)gY�'���cf}Q3��y>��$���ϋ |�<�c� ��iʇ D�&�bĆ�"��#�aZՍ$��'���"��&�;g��.[U�c���B�Hvy \� |v�:��&��a���v�$�.��4�M�Hvb7Y�d����9���5��o����*�c?�j���ㄩ�o��bN����� Ȁ��b=�&��+�bs5i�K�,�a)���]�&vD⣹�b6�y>�KgP�
�c<�b{�=��$��&7�z1/�a.��-��cX�.
��Z��p�z������&��> ��&^U�.���cဍ$��>y0�e���$���뀍$_U9D"	;`U�>ҕŮI�z䅭c��+%�c�#٣e�&�a�Kdb�Kd�����	}�g�Y���j"@�zD�Y�4��cU�id�Kdq�c��a�8h��cf��b��͢x�Kdy�cy�'�$j� &`<�ie�Kdc21#9D��=Ԁ�f�#ngU�jhU]d�����?7���	���5iU�.��Kd?$|����7]٣��Kd�zjU�.$|��i��z��SkU$|�Ƙی#n�4zl�Kd�4zm�z��&H��X�$|e�θJ���p���ʫ�����n�$Do�$D(�&#$|d��p�$DqU�z�$|9�
���r�$DsU$|��ft��z/$|3��&€Σu�$DZ��i'����vU$|ӁKdw�$Dk�5hx�$DyU�>$|�E_��<���zU�>{U�z|U�c�051�>}U�i'�$D�����&�x���>��>8h�΢~�$DȀ��Ȁ�	��8h�Kd*�D�m��D%�c��Kd=�$DN��p�+������&���zR�c��`j���5hhM$|
��&�6�:nv��/i�$D�$|B��&L)ʋ��>���׉�`G�#n�$|0�Y�2�:nd��<�ڢ��	�
$|�	�c�Id&�'%��^"��d���Ɓ���$D>���q0zҒo��$D_�Id
��n�U�&��$D���R�&|�i�����it���z=����$D�	٣�<�����x���z. _�Ŝ 6�c	����i�Wm� 聏bD�c���6*�;���A9P� ��c��c�1j*��S�U�cϚ�i*�ۆU�$�բc9�I�U o�h~"�cP�e�䀦>���cl�)�U�c��4�7 ��(t��%b,��ϋ)�U ���"��%ԟ�i���c�U Ss���)80z���o .�_J�t��r��Y��i+��iw��Q��i�~8��9�>�+[�)��)�'�U �UOd�٣�Od� f�	�+���cȀ�	��'�9���D��%D�)�U�cM�{{⦐U�$:	ˑU�$W��c�UaӃ�c�Od!�Y�Yd�G�♒�!��g��A>��	��U .��c�U)d�U�$�)�'��f<�g��Y8�f�&~l��co>�/�H�f��cN �U�io�5hp��c�U ��i��f�
�Q{q��	o��8ؙբc��f��b}��$��<hc�UOd
��cW�U�j;� !ٳ�>ϡf��c������i1'�"|�+���i#�f�Odz� ��f��_��}OdZ�	��"|׀���
f��fN�Vg}�&���-Od~$�i8�U�ic&?z���K�k��&��`�U�-D8h���Z�'"K���"|)D���a���&�ջc�թj)D� �Md�U�{�U)D��"|(Q)�U)D\�x*��b�
1h">�ΦU)D�V,7o聏b��f�Md��c�U)D���Yv�fm�	o��f���~@�i��O�9�_Md��<h��>k)D��)|��fQ�;|�)DJ��>[�f��)|��fQ)D�U�i��R��<h+�8ا
�=��$��;|O��(�>\���m0z݁8���UMd����}�:�$�/��;|�U�/�����>�)|*�9����qj�ĉm8{������֯�)|�"|��L"��2d��v���͢Y��8�1h\'I��at�`Nv+��a�����F���֢�ػ�΢���&�=z�)�_��-9��{�{v��%�?�@�m8���%��/��{�kIb	Md���(3)D��z�Nv��&��o�΢���}���U)D
�3!a�=Md\�͢K��GNvQ�)|6~8��{��9X�)Ă�$��j��;|>��׳�)|{�ʇ)D���b�8h�U]d<�)|�⣺��%ךm8	 o
���b���$��o%�<�)|%�.�	 ˁ�w�0z����h���.t0z	���{����U0z�U�b� ;fs��iB
o�0z���i\f
��oU	 *�4zl�&���&���'�(f	 P0z�թc�&�每�
o�r��	Em��b�՛k�0z���i��,9�U ���im��cf?	b��a�ۯ�)�%��a� �b�U⣛��i���$Ib�#�Y#��i�U�'���'*B
2d��'v��&c,:��c1 �'�,f��ao���Bd#�a�`%�i8m�&Do8hi�a�U�a��{.�C��N�j��{o�5h	�Bdì�j���{��G�U�a�F]d��{+�`{۫��Ÿ0z�ۗ�qg�m.0zu$�'��{�
o��aK�&Dm)�j��a��,��]��U�`���iY����թc��{g9D���i�{o��	���i�����{Z磾���Q8��<h� ע��ه�'mt>9��.��Bd
9D,
����{��?D���'���kK�؁-D�U9D�{c�����-D��?D�U�z�-|��i��)��8ؼ�o��+��-Db-|3��?��i�-D9�V��U�`-|U��i_��=��?D��i���ތ-D�U9D�)�{�5h�U�'��\��{��{��-��i���{z�-Do�?D��Bd���o��	��&Dqj���h~�&Dʉ?Dj�w��?D�=��աx�՝?^=�{��{C�?D��Bd�;`8��h냤�5�kY�@de��i�U�{��	��i�.nҀ?D]	-|�?.n���x�-|{`8J�a��{��u��m�*��b�U9DV�*#9D9��".n�՚bۃᣡR�k�8�{�,{`8��N�՚b8!���ch�Cd��c�=|���bD�?D3.n��@dw.n��S�?�i��-D�
�iw��s��b)����b
(�c���	����U�iY�
f�{��@d��͢��m?�{�U�ct�� 	��b��i�`�՚b��i���@�{�U�c��
f0z�U�idF�=�{���
R ���%t��*p��?�.�`��v`8����*��Q�2��U�{.����U��(�{�U �	΢��c��'�
 �U %`8��|���U R�i�8h� ��O���'X�i� �՚b�՚bk��'߁�i[�w�U.n�e|�
 6�jE�3SK��o�	 =v%�}��Xd��Ø�͚b���'�����Hmv����՚bK���\�&~�uC��c_��bb�
 �Q�$�U�i���Ւ�cv��;�_r��oa�&D��A�
f��F�s@�
�}�d��U r�]���i�U�$�U r�=��n�U�i��c���cȀ́��	B��D�"�U=|�R�z��=z��AB��D��<�4r�U X�����+z��N�W�iR�5b;�A>�XdB��D�/&|e�U8h�U�i���D¤�'�դc��	�U�i����D��
 ��'����)��ʫ��7��z���A�U�iv�%���n���DJ�f8L	����v�����+J�i�U�/聏b{8Fd�8\}�J�o�>!��i���DqCٯo=U+�}���i� D�&3��Yd�Vd�2D��i=C�I}��2���z�5 D�!�c��g���Fo�2n�����Ȁ�L)�`���ע�U�5������--�f��zD�b�2�Cf���{�8k DV�f��25��D5�{b�U DB��D��Y���D57�i D�8V�i;�ۢ���i�2DV D�f2D��DV D��i����(��D �bЂ�aa�i�Zv��Zvo΢��z3�W���V�i����/��Y�Zv��z��=D�Ydm�-�f�hƚ�f	�ZvDd��bo��4	�>��Yd�f��PV����Ydo�5h����Zv*
���c�
�DՂ�>����b��8:��z����k��{̑�A�9�V2Dd�6���{/�g�0�D	V�b@��$"��*
��hV�b���{D���h�2D�f���{��D
� ���
�D��h�?V  DV
�DB
�DV]d���{d7���{f��,��h��h� >�D�Ƨ,$}���Dt��h9ۉV
�Dψ�,V��
!�f��i���c���k
.)��h��zo�ȫ�	\� ��h]��{\��>�.�cOע��c��d}�RǦ>�����	�c�}�_�h�8� B$��i��c�Zv��id�Zv� �)��i����w�8��x��i� ��c{E"�]d���l���"���{���i}�� ��D��D����֨x���iV]d%�i80�Dj��h��{��k � Ł�$!V]dB
�D"��h����#��{�dw$��hy�8�%V�'&��{[��xA�i!�"]dg� '֍$Yf��'Wf���,���(� Ɗ�,)V�'*րk���i+��hb�'���,�]d,� ���cރzڳ��JR��j-��i��i��t��fЂ�iQ��{\}����岃�{�<h���d�Ca��j�
C�s)�'�c}]d��{�A$|.��D>�+����'/V�z6|��h���D��D�z���iG�i!,��8ر��io��D���&�g��}�0V]d�f1��i*$|2� 
أ���/��D�\m.��i{N�i��iM� ���j3� 4V$|��BdHZ"5V]d����X{f���׍����=}�6V�.���7V]d��:fۯ8V$|9V�i�ۢo��D(�'B]d�	5n8X�B��ct�%����.:V�i���4@;V5n3�i� �{h�k<V�'�5@�Y���$D�#f�&Oˡjh��=V�{��yi8�U��D��l�5nw�����A��D>�Cd��ۢ���{V��D?V�iWi8"�z_��j@Vi8偔���	&��D���뀃?o��.�Id�}�L*i8A�2���xBV�i"�2�zCV�iզ<h_�B��&��Id��i8*�/_��SDV�iE�Id�k5��<FV�i�-i8�	�)��D��ۢ�C�i��Dh-�$y�$D��	��[d<_�)��DG�Id�o���!
���L�$�Id�$Dd�Bd��$DԂ�8�?]�ۢ)��D��Id��$A��DW�Id��F�{_�x�	H�Id��D��$����{�2D�jF��m8��<h��$d�Id67�{6�<h�	i8w��{k}�Z���kQ����%+�z~��aa��$�{��o��$���ע�c\v���$3�o��8�IVi8q��_��iဆ$v�Y�)���z����bJ�IdKVi8n��ԥ��$��i���<��'De��DT��z{�)��IdLV�jS
o+�]�(�Id<�+�)l��?��v@�2!=���z��DM֩c�vN��z�}�)��De��D�
�$L�[�$�{OVfz��i뀹>��D����{PVfj����DO��c��}.��'q�⏗�>�c����
fQ֩c�� *��'��8ڮ�c���c���b�Am+��>���'��-y��{	Fd�`83&`Rֻjo!,y
o��'	Fd������c���>Y�z��::��{SV�&���c��'��8i#�o�+���j�4@V��'TV~~�)���8D�5�]���{���hw�%���)�<~~UV�{J��_�O��]�9��AVV~~��D�WV 3X�f���}�x�a~��bƁ������z'���9�����&SfY�Amx�o�ش0h�=�ZV)D�
�+�	�[VMd�.
owUB�MdG��)Md�;D	�XZ}�����R)D��&�	��c?f����\V)D���X��8~~D;D(Md�}�]VFdo�%��'p)D^֩ct_��'
��'"��`�a~��a�-)D%+Md;��baֿ>G�;|�Md�
��
$� 3�	�1	�;|�4�&G�;|dFdb�Am��xcV)Ds���d�m8&2��m8��)|o�#2eVMd����")D}ע�!:��::�~~.�-:)D��/f�)|��m8gV~~\�x)DG*�%	�;|��8��&�!~~H�;|h�m8=�/��)8��-��C����?8�m8���e�ն�;|b��/h���B�F�Ǣm8��;|?<n:��>@�c��ih)Dզ�>sOMd��:}�iV<n�Md���$j�m8z!!YÊbkV<n���>
��̧���}�lV<n���jt�),j�/Ũj�����?��0mV)Dn�D��b%�%<n���De6�c���6�>f�;|E�)|����o�D)��ܡJ�i��;|��)Dv�%��u3t��d@<n
��D��8. �% ��{p�m8q��zrV s�8��m8��I+%�"gtV�cu֧>m�84�i��vֻc��8� ��8��cwV�i_�8��cx֧>z�f���I �>�,��c��cyV�iz�"g{�D|V�c@�i=. c}�Dz��R' 6T}V<n��bh�,� ~։kȃR���8V搢�c�	ـV<n��8��F2��a��'�V :7�-����;�����i�����D=�f����c��%�2�։kd�&��'���D��iq:�c����U�����d�f�V�%��DvԢ�,C
�(H��ރw���J��M���DԀ���-��:�9��D��c��o�8���D�V ���c��� ��i��gE�cM�8G'��a�#������V m9D�	;������a �Yp�ֻc<�z�V ��zЁ���;�/��})?�S-|R�>ʋ�݋V�/_����}��V-|��e���c�-|l"�z��/A�I9��-Do�!ŽV�z�?D��J�G�?DZ�+�V�.��z��?D��-DU�zh����?D*B�zo�-D���?DR��b,��o��&��-D�3�z3��8�?|��?Dl�-D>�/�����V�.X�������Y��-D���b���k���?D!��d&�/	�yw!�F�	���=|�?D�-D��oӗ�?D�`X�r�g���&d��Ę�?D�j�&c���0љVf�V�D��ݛ�?D�Vf�V�D���b���-|�փb�?Dv���f��@d:�?D�!�D]����i{?D_�zʇ�87�:���D�d{��@d+�zI�z�!�a
�D\�,��z{R�zʇ�8�V�D���i��.�V�/�f$��c��?D��?D�V}�'��b��kh�?D����-D�A�D��=��փb.�k�f�Vf�?D����@d�q���?DF
i8d����?D�V�DY��d��-Df�@d����Hm���b�����i�=�k{�g����{���i-��is���kՏ��@d����kd�ЀP�=������D���f�Zm�
�j��A��dd��	3�b��P���	���Vfw� ��DI��iϷ )��[�}�V
�Dx��+��@d�7S�Bfq*�-���,���iW �D�b�Vf��<�Vfʇ�8�V�D�D � �f��D���:�@d�	��	�	��dÄہ�@d����Vd�����ɨ��a�o���ji8x�Zq�2�$��_�V ��ݛu\��Hm偗�Ě ����1i8��ZmQ�n�N��݀)���$g����$gk{���d``��d��k��=+��,b��Xi8���Vi8k�c��н� �{��֭,@��{�/��7Od�=G��V�i�� ���{9ڷ=���M��{B� �b�ֆ$�U �ţ��iT��g}���5�妽V�be�k�as������`��t{��۸J��
����Hg)gaj��a��� 8�i8�������0.�� ���5����+M���.�� �V)�V�i�FhA$g$�zl�x���Cv�!�b2D)3�V D�$g�2DDd�V D�CvT$g���$Μ&��iȀ�	�V D�V D(��	�+eG�Y�V D�V�iJ�^��.�V2D�&��脀��L�Yd�V�8ú�d��?.�������Cv.�� Q2D;�"|�8֟Cv� D��i�� |�V�i� D
��̀�zD&�V D��i(,�bl�bo��^�&� |r;�V D=��i�V�b�V DS� |JT�
�i"�{�"|�!�b��W�V�i� D�}	�,�V D��%��&��`�V D~�hb�=����L�i/� |Ł�&4�ґ����'l"D��F=�{9X*��i��V�i��Cv���..�� �V DQ�>H�Cv�V�i�V D�V2D�V�>�V]d��� Do f�gB���V�ik8�{]d�V D:�m8��E"��$�m8� D|Fa�
a�V D�V�i�V2DFM�{[�め�D��m8s�iJ��D_
��11�izX����� |��8��m8Y��D(�����%�Vo��I��$y� |$'أ�"|�M�>“m8���1Ȁ�	�6�M��D��"|��m8b+أ�c�3��k���D�Ҍ
c���zZ��+偣d�5o��d��Yb�{����,����%)��i��{��m8���{Io���i��m8��i��z�	�zu���)U7�{��i���z�E�{W��D7o�X���i�o�o�V�,l��x��W%�ٕՂ�,=�)���i�u:�$
"���bڋ
}�o<�qӾ��<
�%���i��i��m8�)�oه�DՂ)�W%�
a.�m8�E"U��iX)g�!�ݝ�֭�Ů���{r�)��m8���i%@�]��iY��D����m8Z����Ņ�	�ʐ	Զ�D�#�i���iS��j��D��iބ�ރ�������Y7��&�أ[F������'&�D��&x�z�Vo�V]d��'���#n�{�	�v��)g]7�R���{B�#n��i��A=�#n�!24��i���{J2[�Q6$|��C����{���,�V�'��Ƙ�V$|=�i��zy��i7��a
��A�#n_��i����b���i��#n����h]dw�$D�'��xf�Bd��$D��#n�V�'�E$|k�&D?O$|��+��x�6�$D�&D�E�'�5$|��$D	�Bd��.��&D�$|��&D5�UkQk�$D�$|��&D��'����&D���V2Ԁ�i`�U�V$|���/��$D���|$�����j'�'}�!!� ?���,��v�:_:�=����vd��f��#n��&D5��v���/A��|��$D�	|9��#n�q���#n{��V$|��$D�)g	$|�'��$Do?�W�'��Idi�$D�
��Y��#n;=$|<�Id<��Y�_��+$|W�'�&D�V �$|�X�&D)$|����&DƮW�'$�&DW2�Sf�y�$DW�'=d��$DW�'	W�b�Id�[d
�&DLH�i�x]�Bd��io�'4`8b�[d������&h�i�$Dt
��W�iF��e�¤Id��fPo�	����.�b��	~�)�z
W�i���5�dg`8��bb���d�5	�jq�g59��r�	��iX`8�!!=�نA�iW�b߁��$�=�iHX������P��f���Ȁ��s���Q�Id��[d�i���fF`8�[d-�Id�[���iW`8��i�\v��{��
��ϗ�j��{�� W-gˋ)z;-gJ�f��"g׀��W�{��
a'3�b�)x�b�)@�&j�d��fš�z@�i�4�b���aW�b=���W�{\�iP��a!��z�{W�i��}	�%ۃ=�6-gR�f�Jv��aW`8׻c�'W�▹�'V�b׻ce����	o���aE�i@C�b���cW`8*�	o$�ⵉ�zX)gt��� ��aF�JW�!W�hJ��j���z�g����d�㵭��c���}"��a=�#�f�v$7�
 �������Jv� � U?�{9����A��)��?���d�Fd$W�{�{�
�N��8�[��h�������::܂f�ͷ*֫�����fA�l��o�Ɓ�����8�����h�i%ס8�'n� 
��'��i���aO<�Y��
a��>��^��z���'&W)Dq��zI
�'W)D(��aX�Q%)D݀�a��-)D�:�z
�'��-)W)D*W�x���jMd���at�zW���	oJ��a+W)D;�&,׻c-��a	�;|o�.�)|/�	o0W�z���迎	o1W)DՀ�9���+|9	��Yd�=2��a���`4�;|$Md3�`4��a����9��fX��aX���ȃ��5W)Dp)D{�8����%!] ��?6W������!��a7W�c8W)D��x9W)D:�)|���_ۉ�)|���^+�>��d�)|��a…���d��-��a��`S�c���a���.�&���x�&�'f��
;�)|;W)D��zѪ�a2��9�")D��>��b��a<W)Dŋ�-=WNv�S�&��z{���3��a
A�c��&�
+D>��a�)b	)DQ%�z��c�Nv
)D��z?W�c@׺j���u�)D��&h_d�.�xg�&��t�d�;|{��8����|KA��a��dBW�zC�)|�
aD��a��EW�z�`��)Df�Yd3�+|F�`��)D�Nvh��a4
o��D��&���4�a�/�&��)
ov�ȸG��a���|H�8��,�b�_�J!Ѿ�Y�e�c��6B+��iO�U��V��������z�#ƁV��,IW�&l�,t�dc������&{�@���	遝$� ��&�NNv��k��c��+d{x��z�%c��?B
j�$�z
o��6�JW�&�	
o�ƛkR�5���
o���khB��������W$g�$g��e�ɗ�k�`>QS�K׏bf���T��dw���ʐ&�����$go)i�zt��+���j�ZvX5NLW�z��aރy�MW$gu��+ރy�)��NW�aq��ƁW8�7�C'�%
	$gY�����OW�a���ze� PW
o��a{��v5S��%�i�z�7$gj&
o
-|��,:�݁���{k�_�h�o���:-|QW]dRW
o��o[
�'��
al�oÎ�|��ݍ'-|ob,�,:Q��1&�$]dĉ>:SW�'Q�̉�I9TW-|��?D�bЁ.���UW�'{�-D����e�R�VW�'WW-|X�i<�/YW�'�-DZW�x�
-|e�-DK@�'�-|I
Ձj*A!:/|[�-D��y�\�?DqUɿ�ʌy-|]W�'^W-|��y�_W$g�:�`W"D���ia�?D$gT��i&�����i�5�'ݴ-DbW"DcW�'d�-Dj�䟇-D<�hmCm����e�?D�
�iڊT����fW"D.�!n	]d��}gW"D��ʌ�}��bXW �gԨ��}h�@d�-|i�!nԑ�
�'jW�'���kW-|�b��'"D�-|	]d�&-|�T�'�x���-|��'..n4��E�/�ԡ�'lW-|z�@dy"D�� � �Op6�a���bm�"|�.n�Zmn�-D�+m_�G�@dD=�'�.n����{y�'o�"|Ԃ�bpW�zZ#.n���#�z~�i8�i8�&qW"Dw�-D�)Q?"Dr�-DsW)p�?DtWi8��]��|��y�d��u�!n�®�6�vשcwW)�"D�"D_�[����mq��5d>�ox�!n5��vyW"Dz�!n�i�i8�"D��&��2{�Zm|W"D	�ܹ�i��}W.n9��~Wi8��|w
�&���'�������}r��+�	�EW)�
i8�&�����	����zX���� t=�.n�`~��{��"|g�"|޸�c���5�Xd���a�o�+g�שc����@d�W�d��_^�z~���D���v��cZ�z��Zmr�{D��'
ȒJ���$	_��b�Zm��$��)�� o=�z���>��Zm��a
��H[${�#��a<i8C�+��)��|)�`8[݇�b�_9�[��Wo�i8p��a+Ode�שc)�����f8��$��o�b�׆$Od�(�&g�db��j����f8�W	 ���^X���%��\���$b 	 ��j���l,��Lk��b�שc=�I�5�i�b˂e!�i�Uv%�b)�Ȓ5���z)8ݧ��u�Ia��D{��8׀�/���'�d��$���'��+c�Cv�j�")��d�Wи�2��ib��|�<��$�)`8�Dd�W2D�Oo�`8,�"@2D���[�W�a�	 r��6Vd:G�a耙�<�ɣL2D���b�W�'2VdX:R 3��d5�b���a��dnW�w�t��Cv�52Df��Ǝ�f3l}5�b��N%��&Do�xD�a��;��W�x0���WVdt%2D��&D��Cv���|��b�K2D��bk�xi�����z�a���`��i.�&D�W2D���|��/��5�b��&Df0�&D�5b�Uv�b��c��Q2D�3�Ȁ灹�'�W�x��&D�����$�W2D>���꣫�=J��~��/g�)&|��c�"�ܛ�&D^mb��|ٳ�'��&
�aR�c���`y��/���`���'&�me@��$ˁʌ�W2D�&�W�&�W�c���|��$��)����l�':���֚^m}�a�W2D��m8Q2D����&D�W�a��x2DP�)���׍$�&D��&;�&D�g�1
����)u�)+.��P�D���x��&D��&��)�?�5F���)|�b*�&�#�&��m8�
��ک�&D@�)�-,o�)u���`���ݪW�bm��>�W�cl�&�2�c�W�b�����)�צ,�W�bd赧��vl�j����`�"�cH$�c~�m8�o��c��,��	�@�b
�c��w
��Y��23�KĊb2�X��(n�׊b
!���"g� 
�Cf�bB��2�W�b��Xv�)���(n,�&���$v�}��i��_o�i���Ȁ灘�)��aX<L=�'��ץ>)��2΂(n�����2�U�L�m8��N��4�2�"g��b���br��%D���)��m8Z
�b3���g���zg�m8�W5nQ	�C�b� �܇�dB��z�b��i-g�)�b�Rp)�� �����p�p����z/�
C��H�b���m�>!>jx�W?��� 9�b�W D�W�b�� ��`>�6|���88pjooco��&�5nʃ&=��!�j�e��z��c�W D��	�6|�f��W D3��bMoI�"g�� . D�(nh��d�W�iy�'l@+U�>!����o4
)}F
)�W Dy�'^o6|:�(n(�� �W D;� +��`j�}Ԃ(n�"?�ނ���W��)����W DƁ��6|v&
)��H���7oh)�6|-g�5n�W��!���4̨�>�7+DX-g�39D��{���zg
)�5n��-����O9D�W+D�W6|�W9D�o�e�Y�l��x�W+DʐB���DG+D<���|�eЁ&=�w��@9Dϋ;g���z��R�
)��
 @�z�9D�4 D�� �+|w���W?�	�y��)�	+D��[d�Wo)�Q��� ��	o��%���xZ

))|�2��W�`+D�=�Id�Wo�* D�שx���&��j���&h�����&�׃b���&�2o���&�W�'�׃b/��&��[dxWo���&��+|	�2�!�P���xf�[d�˓&��-��g��+D<��x��8�ex�+|{���D9D���Ĩ)R?\vkb,��&��	�#�a�&�ׂ�bb��|a�T]ĉ�&���&�שx���zb`~{��'�W\vs@֣*?��W9D���&b/+D'�ʌ���&��a�X�L��e���,Xm(��jas�qٞe��&�׸��$���i��
�vָ��{z��b��Id��*B\v~����=�*�ۧ�%��
��p�?��i��+|(��p��'Ai8��8n1i8w�ܜ?�z�׃b�.
o�ʌ2��&���i���&�i81 �{-
o�שx΁&*��'q�g����W�{.��c�(�z���x��!
o.�묁i>e��c�6�z�>�Wi8�#/���c��t�X�yB�
oS��zK/��!�bp
�Hn&�>jxi8�שcւ�ݳҩc�$|�W
o���wv���ہբw��{dB���z�$|�qx%���i8�f�qxg��z�
of�)@K$|Y��z���R�::��w���W$|��Ƙ�B����a���zH�c��)�-�&�שc
��u*$|���౦�;eۖ���o?�=�o�'�&�B��|o��$
N�����Cm8�~V$|Ă$D�W�&r
�+y�䇀r>{��A�)T�.@K�z��&����WMd�/|�i8�W�&�Wi8ɯ)$|��&��$D�;Dj�b����W�b�=|��b��$D����W�'&�+
o�W�x��b��&O��c՜�@���$cQMdI'�Ve�'�`~��of=|
�/D��+ˌs�[X�/D��;|#Mde���e�':&�>�xŜ;|�/D�;|�ݎ2��;|��z��9�Rd>;R��b��Cm��ބ‹�x���x!�:�/D�'��;|��>
�/D�W�b:��E���bl�Am��4��`�W�'o�_�;|��&�
A%/���	���;|��;|�W�&�)3,��	o�{[�	�Ṁ$DB���W<n��$D��;|R�/D��;|�b�Wce��)�W<n������`XMd����;|�����j�.�	o����a�'��	o��ʎ��3�p	�	oX�b��c����c�+�����d>X�b�/De�'�m#(
�bX�'I<nl��aq��U�/D 
�,OF�r�xÃ;|X�c�'�;<n�"|��;|��Vѧ"|X?�A%<nX<n��/D)��N�/D';�c&�;|�9�z�;|L)	�"|9D�c>�� Ɍ	o�ف����b
X�ch���X)Z=p�z�#)��	oX�cx�	o�ف���`�
g	�Am
X)
F"��c�״!�z��t
�ՀEE^)�qx��iS(FmU�c�*)���aX<n��8p*�i|RGm��m8��"|g
)Z&�)A�c
�z�m8M�FX<n��ux����<n�m8���>X<nR�"|*�^
�w�"|V�:�Q�m8k?��&o����"|}����զdc�cX�z��a��D�js�����p�z��l`��,�7X�zX�cJ�+؊bئ, �m8ۚ���)�;?�[�/�zeaAm��,X�cX�z^)p�zX�c��,�K�)؊b	�z����&�� �I)D��5J����2j��b��|?��·��|ˆ�</
�i�q�ئ,�k�#.)D��)�|D�
?|�E��a�Gmd��X)D��`�>:9������
o��$����U2�)|�?Dw����?D�m8�F)D*��bt�?D�D�zX)D
o;�?D��d�j�=>�ܳ�?D��)| ��'m��&!�?D"��'��z�N@)D#�)|����.2�{%�(�?)D��?D�<
o$�c~R��'���sxx��)|�@a.�7�`e��')
o�̦,��c~`�I2:�a	�F%�?D&�?Dm�{��,B�)|'X�a��?DR��'h4�'�0�(؊bW��?D��')�?D[�t��T A<�a�@d6�'*�&D+X�a�����?DJ�)|,��'-�?D.X�a
���&D{�@d��?D�kt�*d{X�q쳥@a� �a��(�D?D
oo}��`+?|@<u/X
oi�@d��Jm0X
o�+)Dj�?D1X��2X�a��3�?DÄ)|�"�am_��?D^�@dF
o���"
oX6)o�o��?D4�@d5�?D%ۦ���6�?D� �Bd��j�?De��'���%�e7�@d8X�&9X�a:��';��'������c~<�Jm��@d=X�$o�ux��`v��A�&7���>X�&)�E%(���/g�&?�Jm{�@�(��?�g�z~O��c��(�	�"�ao�ux
kj���@X�&:�aAX���'��DBX�`."�{T�+� �aȃ��{v>��'C�@d�ߣ��c����b���[I�&���x����(h��8"@�&��fL��c�����,DX$gE�@d!�	�f��.d���`8m_�vT�b3�b���B��?:���eb��D����F���$gX����bF$g�}����j���GX�b�$��+0�<�f83�&���_�k��T�Y�[S�&R��eA>�CHX�&IX$g�v�����bJ�f82ĪjЁ��7��,o�uxo��2W$��3t86��m�nK�f8Ղe*���b��&4�DI1-|��k�b���`Lتj�-|������2k�ux��yH��f8��	��f8��	O���aM�f8@�-D���c�b�!-|ĉ�jN��zOXEv��b���zb-|t�-��f8.�i�u��o���(PX�b����-Dk�ux��6�{>���''�b��Cv`KC�Q�-DR�CvSX$g��OTX�bU�Cv� D��b�fV�-D�{>,Vd��CvH)$gW�Cv>�uxJ�!�v�+�)X�aCag }ϔ�j	))��a,�-D��Cv��a%q��io�)ۅ|x<ִ��-D�Dd�a�f8Y�Cv< �#)�R)�*VdY��`@�5Z�Cv�/�Y�[X)��J�b�CvvY���Cv�ux���9���Ӄ|x\�Cv[X)V"Vd]��`^X)9�{�v�����F)�>-|�ux�����/_XNm�Cv��{>`X)�KE��i�C)a��z�g~8�'b�M+���oÎ����	�NmQU9Do�G9D��i��'c��`}Nm9|³��'��ddX9D��`U�Cv�.����`R�Cv��z��…�E]�/e�uxh�|x��f�CvRFg�'DH9D�)"�Cvh�E"ʉCv9�����9�ڷ��Ђ�`d��j���f�"��t�\m��k9De���� DZ
)U��'�'iXNmW�`�)Ro��f��Dj��D�Mo�ۉ~�`���D���`�Nm���8$��D
�fkX�alX9D́ D	�c��`m��DnX9D/�f��E"!�CK�C��qxoX�a	9Dg�dp�f��&o��D�Fa�f���q��D��p�۰��'r��D�+9D����sX�ot�f�$�aV�vd�����DuX�a�d�	+D#(�a�9DZ�y�ץ	�v��}����59D�ؾv�}k�z4�t�a������z҇u��}���b��8���i�L%��a_�oj=����})��w�E"x�}�*�z��f��`y�f
��>�}���9Dr��D���i9X1z��D{��`���{D��|��D�8��d�,�
��}X�a���~X�z�i��f�X�a���z˺E��f$��D��a$��D��}"� ��h�X�z���-��)g��b�� �c.��{��%���a��)g瀂%�	��S�x���7|x���j�m�w���o�ux��:n��豺�i�Hv5��c�A�3���;$@���{c�(�p��z�X�&����{����}[��?��&S����@�bQ2D��}(�$Dy�&�X�z#�	��ū���v�}���z]�)����$Dm�����)���zV2D:��x�X$|��}��z�2D��L|�7�πaxx)���d�R�&Y=|S��zO�z�� t���)5�/u��`�%��&���� ��$D��&5o(� �X�`���J����
�&Z�`�
�e��$D�f��$D5�c��͓�@�f�X�'g�)�)��[d�Zm(��X�=D���m2DC�)g3� ��)gڊgP��)�� ��=Dm=|��)g���hfщE9Ԁ�.��ݖ��h�����	f�V 2�&-7f���Fo��`�X�&m�$�	o�摘X�&��=D�� v��h
��W�3zo� �� L�$��~,�X�'��Id:
"D����C�&�fl��&�� �� �)f���hУo�ux�� H�F�Id�X 8�)8�D+�� �X@�П��}<��h��bdT�'�X�$��'L��h�Am��"|Yf��bb�i��I�j)Yf>G=|���V7#�{��=D��d�w�{v>�X�`��dG&�b�f�& �X�`h4�`��"|�У�f3�'j��Id�$�s�gf�ӣ���Wf��"|���j��%��b��)���h#�E��f�X-gZMi8ی"|iB-g��=D��	o�����e�i8�	o�|x�X-g{v>k?�a�fܸ�h�ػx�ػc�X-gy��b4���䐻c@�2N����clUm�Xi8X��Č�(ۛ�Nmv�<�Um���c���	(��+���� 
9Y��cd�f���.�C<鸎��bGm�ػc��2�Um��f���/��c��c
�f��2%Umd�t�2�z��f��O�bh��8�X}�X�zQ�dz�f��NI��	������0䅩c���b��Н����q#���_��_�梶�f%��x��f	�a��xx��f��o�w�o���z~��z�b�؆$�}��f�X�d�X�zÀ^m��zt�&�bF�f�Xi8���fb�z��pd-g��f��f�X)Dv=�/i8e��%���K�y�f�Mdk�2=���C��)Dc�2Z�&�_�f�Md@�2?��	�0)D���&:�nX��b	 ��V�2��`��f�X)D��`o�f���$�X�`��f�����2*��b)D��zh�f�X)DB�)|��f�X�zȀ�	
)D+
o�f�9�φf5����f����+�z!��ȃ�����co��2�d���'�D�zB��&�=�`3�^m�F��XУ��`r�)|��b��`�
���؆$΂ gA
o�V)�X&|��Av�%���f#��bf����X�h�؆$v���U��)|��o��Av��dԂ�x��f�/Nv�ؘbؚ?z�!u�XNv��&DP�'k����&D��Avʉ^"��BdN�do��y�f�XMd�&D���iR�o���'�X�$��f��&D$�o;Nv�`f&�$��hn��d�()D
���`�o��y�Jm �Xm�/Nv�Bd��`��&D����X
o�)|�L�`�v>�X�b�)�&D�Bdb
o5�o������)D��v��&D��)D>�ux�ze��Av�x�߂ �"/'��o>��i��i�Av
�Xm�/'R����X�b��o�������X�xg�Xm	�,d�탺8��/u(S�b\2=g�o�Xm�У�m8��v#�m8�&D��G�f�����b�=�F٘��̳m8�f9�V}8
<u�/'��8���H��)�Nv�Z�{�8��Bd��m8���X�6��X�b��m8NX�Bd��m8���XNv��&D�)��Bd�W��(����&��{��d�D�bo��iʁO�l��d/�f���b�ze�wh�Xm&�b�)87����ד�b*��iL��i��v:�bX�{�9���Zm��h,�b�	Z9��Xm���h�W�b����Xm�$�3?�����
/'���h�c�	=g�X-|��h�f��5���'�b�Ɩ�8��,��Ђb�aZm����񍽾��٥����{�Ǵ��dk��y�)����{���񍽾���h��<ŝ,::�bǡ�hk�<n�����8��{;�;|��}j}����t��-|�X�b�*�b���ho�ӣ�C;D���z
���d�X�b���z�X�b��;|ҳ�'�(Zm�*A�|xЁ�&��;|	��z�������hi�-D�b��'>�}j�
:���D��z�XFd�XFd��Y�j]�%���{��ڣd�	'��٭&�.HFd� Dj���z�}�W D��z�X�`F3�;| ��]���h��z���z�A\m�X�`�XNm��i�����i��f�)-|��0'���zH
�`ʋ/�X��	_$9Zm�2�il�`Ёd�p-|���'s��zƁ��
�d���ڣ5-D�X\m�	u~=FdtI�+��)�����`�
�d�Ǵe)(\m!0�n�n�9D)p���{5)�9DDu~���z#�$���z��'��z����X\mt��z@�ӣ�KNm�XFd���'��d��	���h��-D����b��Z��x�;|o��.o��iQ��z��h��$|� fQ�$ԗ6���'�;'@+)��hY�$À
fc��-<�+|9����Zm���<Fd���-Y�$T��a=Fd��x���8�{��$]Y -��x��SS�G"�
�`Y ��᫡�&d_$���&�&�J��f�
�7Y�`��&���x���b��"���g�'�jQ��,��ib���j�Y4'� Ё�����'\m�4'l�
fYNmx�t��{�[����&�
�耪�ڋ
fs�{	كbu��֮���1!�i]4'
Y�$X���J�څ�fӃ�x��㬐��% {��	4'�9Dd4'�Y��ʪ�;�z�Hv��1��|�?��Hv��$� �zk�xڬ J��fL
�AЁʪ�Hv���,��xk�í��.���Q�z��ģコ�
Y�zF��j��^9ĚHv���)��I
�	ʋ���z?���z����2���b�zY�z���b���,�?D>29��x��`>Uvm��zq��xց�n��i���Ȩ�d*��&�E�z�5M?��d��?��zY�?DY�jj��iY4'����5Uv���z�?DEj�2�j��f8J�O	6g6L�z@"��j2D���?D5Č��zm%4'���z�*Vd�4'��?ډ?D��f)���_+�*Vd��d�4 co�?DVd��Cm?�IC�z�
C�[�
�h4 j�?D�?��Vd�2S�x~Y$g�	�bن$)f�F9��$gg�z�8VdΟ�k>�h.�f8��bq�z��z��|x�Hv���k$g��ߡ�����49�;��?�
#)�	2Y�b�z$��$��>Y$g���+���9���L�YVd��� �?D�zYVd��-���6$go?���� �~1*>�-�aŁ�/���>��`�_+��i�hA�xZ��پ>�U�x�
Zmx�?D Y�x�$!�_+"YVd��f�f��d9�Kdm��`#�Yd��� ����$g���$Ç��Yd$�Kd����ʍ.�?#��%�)g��8��`��	��$g
`#@-&YVd������d�v�U �)'��`����7爉�$(� �R$g)� b��$[ *��`+٣j��x,Y$ge��>>?�(��D-�Kd���`.ن$/��Dӏ�K0��`k?�5�2Dn
�ԊKd��g�� 1��D���`_�d� !`�T�e��>m[�[� {���h�f�2��`)��DF$�x3پ>[����o��� �%@�Հ���`S�x4��i��>"��/���D5�`���	�؁ŋ�z5�/D��v
X�#�X��`ރW1m"�b6��i��KdÀ^m�ӳwH#z�Kd��<7ٍ$8�KdQ��`(��D�|~��j�|~=�u�*�bw�� 9٣j�
|~�Ydߜ �@��Kd:�Xv���`�� ʐ�4U£jd�E@�ވ�$��<��`;��D��<�Xvx�9��q�{�̶�㵐 ւXv�@���x��<̤�`*�bwP�&��E�,�<o�&j���=�9'+)���	ރz?��x�CaA
F:C�>�٩Zd�'�,u/�>Y D$�<?�;g��Cv;�x��x�׷���>?	��"�e�iDY��;g@��i��<�^m�|~u��>m�&}�ڣR����}A�9'Bم-	%o� DCY@�D�`z�bwE��zFY D��Xv�Xv�ce��Cv���`G��iH�`��<
��i;)�;��<9����.����Dd�X�fd�j6���>��CvYg�х-IY Dr������bQ���@��<������<�
-g��}�Ι��im�f{Gm5�k��i:Od�;��6 D�&��	�} Dx�}��;gJY]d.�+ҡ���]d��i�:	 KY�`y�E�L٢xz�}a�}�Odn���5f��
�EȀ�B	�;gW�DMY�`�IOd��(u���?Lj�>�-��`2ĊbN�9'��;gOY D�����?s�d����)pf�PY�`�D)Q٥>_��ރ2ñ��h9�˘�f��d��x��o[�Ave��h�M D&�[d���h����R��h$}�oQ�`���Av]
��S�)gTY�`�o���x��jƇ�hXo�02n���5�$���,b�z�cOd�`UY2#fVY��}j�Avk��۾W}h&\v���Q����WY\v$�)gĉ�hXYOd=��:l�(u��`_�7�m[�YYOd��`�U$���O&�RҀ�x�52�$ZY]d[�}o��\Y\vƁ��]�z�351�0�`�a5��c��ٶ,5�**��5����Jm\Zm&\v	 =<��.�`wQ��U���y�\v�"�`,�)gv���{"�`��^�o|�LԂ)ҡ$���[�Xm@�ӻg�)gj�JmȀˁ���>��_�o`YfI�)g9t��ňf�AvaY\v��JmR�o�)g�؂��dk2�Av����46��bb�)g��o�*�cY2dفb���<耤O���be٩xY���ff��&Q%�`gفbhY)iY2jفb���c���&$|��kY\v��d��&���b�� �ǣ�>\v)<����`o��Z	V ���4��&�{1&`8���&\vl�Bd���b�3�mY`8ޓ;nفbo�o��&���,&`8oX��$|5e��bۃˣ�`8p��&QW��qY$|���P`8��$D��{���d
��*::��#�brY`8��&�isفb?������&$|���.R�Xm�f/(�tYf;Giu�f"Z9쁪j�ô���C��+���_d�%��9���v�f;LU]�΁b=��`8��DW�f��:wفb��f���	��[ ���
ô�谁�Do��+x�ft��D��u�ȩxyY_d��u_���.���czفb{��&|Y-g}٩x��Рfz
 
`8~��&o-g��^m�`8��&��Y{�i#g�f�Id��&��w_�*A;��	o��d��;|[�Y�b�`8r�(.$|3��'��{�Y`8�-gG:�"壁�Id��$DՏ�+�
F����dQ{� �AmMHZ9ǑF�J
��_�Fy_d��fz	}���9#�h��oӂ�Am��h��;|o�[���d��j��f���k`�F�Z\m��I"ψ�D���օ ��f%�&�Y�<���z;D��D��f� {��$
ô�f��i���fa�Am���"\m��Dބ�i#t���+�
�0�Ҭ>����\�f#	���hY���\m��f�����HUB-g_9D�	oD9D��+Q%-g�Y9D�Y-gz�f�V1��i�ek����Y�b�Y�b� �x`��k. �嵍Y�b���
ôQ ���l�Y�bπ���Y 
ô��'Y�	oh��'] �Y�x��t�y)�����ݒ�Am�9D��d������Am���d���&����d9���+ރz��b� q�@����8���`��k�*�g�� ြd����Y�h���€�7 ,�<��d��bu�2?����)��b�U�x�8k�2a��i���d�n�\m5�mT*��Z�_��2��ˁ����(�8u��.���*����ߕYD���� g������-l��i�{�o�bC��i���>���"g��<��>�{���b�9DWf� �Y ��݇��e4
��	f�Y�x	�f� I�����Ya*���<��b�8 O��Md���j5l�f��C� D))�D�Y{�UB��>t{�#�D/��X:��Md��Wfh�
f���d�)D<Md�)D'��`�Y
o�,��������z{�B?��fm�)|ƒմ��`�Y)D�YMd��rGEm��Ƣ�YEm	��zg�?DEm�Y)Dl��zO�u��Y)DkJ�\C�bQ��z���z�Y)D��dY� I�ˁ\�?D�F��՗)|���,�?D
	
o�:�$�gf��c~��)|�?Dk�c~3EmZ)Dgf�X"�Y��c~6��I�i�:�Em���	f�)|��)|{��YEm[�m$gP[���MEm�TfȀ����c~*'��]"$g�(f��o���)D%�B?�٢cl��	<�)|⁼d�ǀ��bS	
oǸΫ�J��٢c�$gK����?D��bl$g(Em�?D�وb<��-ډ�hR�H���b�)D耹�ĉ�&�f��c���d���h�Y
o)��b��{���co��h���z�Em��ݩ�m#�GEm�մ�z?	y���d�f	�?D6�$��N��`�Yf���&���bG�c~���h���h�%�`��d��cc��c��c~�� 
��b���&��Yd��5߁iw�?X"�'���h�jԡX"��b��d=|T�.�c�c~_��h�YX"������hm��,��hT�@M��c ��~��[x&�[���!n#$���C"D�
f��YdLX"�"D�$gT��h��ݱY$gWӈb����Y�`�Y"Dtf��Yd�وb�f��h����!nU"D�٢cy��b��h�Y"Dֱ�cc 	�f��%��c:�Z���U�"D��b>��h� f�!n2�"|���{t��Y��{��&����h�Y"D��-��"|9��ѹ�!nj��-�!���ɠ{��#����.�"D-Y"D��1ĩ��"|�Y"Dh��d��2r��P�	��Y-|�,�`Ԍ)*
��ۗ�,��e��ZvR�:u��Ydz��d��Hm��ZvUBf�5�����:u&Di8��2�
y8e0"D9���i8m�fYB"DL-|�mw���h�Yi8�Y-|���,�����d��4��g~ǝ4S�Y"Df"-|k��#�2�Y"D��-D��Hm�:u�-D	 g~"D�{��"D~@i8-|�Zv�	g~�Y�~�{��Y-|��-D�ԣr{��g������-D�{����%��I�g~� D���d��g�-|V	 D��-D�"|�����h��b� D�i��2Qң�"|��b��-D��&�Y D��-Dh�f8H�"|��
��/��Hm� D� D�-D���o��Zv=��F�R��$
�-D磖i�:u�Y D��o�8��-{/ Dh�Zv��-DԀ�o� D�������?� D�6}�܀}	x^����: DU��՝He��ݫ�ˁ�
f�0 D�Yi8�Y]d��zϋ�$�v��i
 D�{��i8�g~k����(u�Y]d�k�+�*b���Y�htQ��b�g~v�i��)�G-�i!� |�Y]d���%n��-D�1)�Y]d�"�h�Y Dv ��Y�h�)(5 Dd�d9� |ʐ�E�F�
)�Y�h��9��׀'���d�2j���.��Y D(������ٟ$���`J�d���hb���Y D���+b����]d�)��hך�$��)g4&|�鬥��$�m���=&|��$���`����ٟ$� i��d������f�a)���)��|G��㘁���
}�
%nQ]d��f��Y]d�U@��k̦d���`�ˣ���b�T&|>��Z�Y0�̴����r�&DA=]d���{��.�!	}�Y]dZ��_��6
��zJ�&D���/�Bd��w�=�x�<)�%n���z��{�a��T��<��z�6�h���|%�&D��z�&|���{;�&D��=jc4�;�����z��?��$ʋ�ꊉ��i�<�	 %�&Dv��+,�x1��|��z���i[�i`�o�Ƙ��z���i��XmK�&D��z�
}�2M��&D%��z�	ã��$�$�Y��`��oMه�zʐ�����`��7����S��M�)g�d��I"��}�z��<;�&�������~o�m8Ȁ&ЄAd���B���%�&m���&D�*Ă':��#n3��;�&D
��[�G�#n�[���x?�&D(�m8�}2D݀#n��-��zR�m8�}���;�&D�$|G�C�U�C�%�&D(��Ŋm8�`8��i;�&D��z��oތ�Ŋ�z����_��Q����z����b�}ǘ�z��}�����`�Y$|Q�΢�#n��$DȀ�Q��yծ$DW��5KD$|��$D��}V����$Dt��(j�#n��xȀԁl$|J��[���$D��̴����
2D��s���d?��裆�M��M��㩀$Dp
ĩ��m8�YM��$D�����k�$|+�$D�#��J�m8to��܂�S�Y-g�����'8-g���y���de��,��#n�m8�٢x�m8I
-gL�h�Y$|�[�p�$D$|2Ev��9��$D�i#�Y$|B�$D��%:���5�#n��)h�b�}����$�)�+s-ד}�h:�)�2$|� �b��ޝAm�F��),�bg�)�7����h�$D��Id�f�y�a~i�$D�AmHv!���Fd1);��d�-g��$D,�)��Am�Am���x�Am�)��X��$D��$}�)T�ԣ��4Gv
Nm��IdZ�`��|�i�)�(n��$��n�)�Am^�)��Am���z�$ڢx=����i#�ãZ�`�a~�	�h�Am�\m��z	Z�$Si#qX
Zo��5Z�n�i#Z�`[0y�
Z\m�Nm��hrã�U2��IdЁ���+U�Z�h�1-gZ�`��"ggȘgHt���+����AmĬ)b�iˈ����)����y!+D�Am_�
(�	���uY+D���8��'h��d�Am��X�r�%:\ms�Am
���	��͜�)����8��b���<�	�z�R+D8�Am�4��y+Dg�z��Amz�`b������'n�+|)�	���~�
�	�`��{5oa��b��Ђf.
f��g��\mZ�`G��blo��ak�-8)Q$fo��ib����:	}]���
\m��V��<倻xr��b�x+D8�~�(=wr8c�"g�؁HNm=�H�c�Z���C���n��"g;+D�	f��n$�q���}/X+D*��bd���Z+D�}o�ic�"gv��ƚ�xH�^9���o �6|����+|�y���d�@da�DE����awQ�Q�����{�}d�()�	�Ԃ
}�
Md��})D.��s�O���<?����|b$�ނ��<��}��9݊�<��+|.)D�ݣ����L)DW>5n��i�)D���<Ԁ�ib�����ZEm.���o�&�Em-��x�m�&��)|ZMd���xYBEm�)|r8c��x)D�.Ĵ;�)|�>DZ)DQ�)|#��aZEmd��a��)|ZLEm�)D��)Dj��a"^9.Em~�)|(���>
�O�2�W���m"r8�[d����m{�GT)Dó)|ZEm��i��_+��F�^9��0�[dM���D��at�)|kD� Em.���Z)k�}(��)��a�Em�){�)|��&OĴ Z)DYU)!ZMd*�*~��a)O�Mdt)Dܩ�a%�)|���y�)|���������"Z)DK�&��9���_#Z)M#Em$Z)De��
��Q�	)1�0�2(M�m�%�+���>x��Yp)��|�����PEm%�)|<��*����b.���K)�{����&Z)'ھ>5�'�)|j��nj��*�{��)|(��a��xz�m#c���Ңc�)�)|"�xs�Y(�ԣD�	`�������
}*�/DI���J��>+��a)�	���,�����������Kd,�/Dq�@+-ڙ"��.�.Z)O�&��|`�����`H��m�X)U!�@/Z)>�/DƁ�}�0��`펶1�o2�G9��`�b2Z)�)�Lf�o��A�~))57}�0�x��`�w#u�x3��`��&��ԣ���`y}�&e�o��Kda}�/|o͢Z�G9x��ds�4@#��|�)3��`{}4�oՀ$�r�f�tIw�"|�}��G9��o��>�{�5�ok�)>�
}/�"|����k�./|=�o�6�Am{-�,�/|냼�7ڣ��,8�)��/D�(��Kd��&�)ՇHm����XY�9�Hm*�b9�"|��ݙ�`��/D�b:�/D;�Hm]-|8�֔�"|��մy}��`9�/D��߹�Hmm��|i8���~<�Hm=�/D�݁>�Hm��/D?�o�Ei8_�)@�o��G9A��`f�;|��}Ii8d��BZ}���`C�-D��)q}DZ-|-|̈́-DE�Am�Z�F�-D-|ʋ��=}�)j��b-|Ɓ-D�y�Q�+�dd:p<-|��-D�i8#�մs	:#��xG�"|��;|{kHZ�x�`Q���C-|9��Z�jI�-Dv�ִJZi8�/�-D�)K�"|o��m��<L�HmM�;|��?���<�X9�3�N�`�.n�[�O�-D�%����P�`qD�@}QZ-|R�Hmd��`Ҁ)�
-|SڢcT�-D᳈b���ճ��|_�Hm��HmКbUZ}Vڭ~���'W.nWZDXښb	�0i8���&�
u~>�aR�j��%tÚbQ<L(�-D���b@ٴo�-D�i8��j��cYZi8-.n��-D���'���bZZD[��&Ł-D�.n\��&m[�꒙�.�-Dڊ�Lٚb�?�ǔ-D���'��]ښb,<�`^��&Ȁ��覢c_��&`ژ-aZ�`a����$���b��hoyF�F��o3���oĶ�b���k��8m�~܄�hc��&dښb���eb��h���eZ�$�恅(�9�	�5ej*,4�`
� [,o�5ogie�X9�c:�$:�)
��h�=
СW��&�TL&`fZ.n<��h���.��`��A(��h�����[����,�CmJ%�`,}��cަ�bvށo\��h�����t���|�&D���b.��&o����`g�&D���~��`h��h���&iZ�`jZ�`½&Dٴ���&�{�O��&��&��x�%��r��E���&;��h)
�12o�&DkZ�`�X-lZ�$u��a�1�`m�&D��X-�,<£�nZ�`b�&Dိ~ӃX-�5@�	�zo��hd�Zmp�&D��?���(�)6�'6�X-��n��a��ó&D�£��&Dq��h�y8r�Zm�X-��*5���	�i��h:�?D�h<��A����s�	}��~-��Zmԑ	}���5�X-��l�|��-|�����?Dt�	}�Uv;�Zmg�m8ɀm8CЩx�%�|$�&DǿZm�X-s���62�`��u�	}�z±�x��`���F+�Vd>��ݔ�m8vZ�`��?D�m8�52D���x��mp�kG��&g�?o���w�	}x�m8����F��2DQ3�y�&D��Zm��	}Ԁ�+�Fb�	�m8��X-o£z�	}���&���?��h^��h͓����a��X-{Z�b����m8j/|Z�kӃX-��a0�Zm��ӃX-c���}��`~Z�b���Z�bք�-6�X-I�⁀Z�b�@d�Z�k;�k
�ڴ���ak�F.
�	}�0�k�s��d���?D��zk���-u��	}�y8_��`ϔ:uD�k'�|a��8���	}����U�h��m8z�m8��	}�)�i#�	}Z��&z�m8�Z�h��?D�����ևZ)a�m8�Z)�Z�h
2D;)��m8������8��h*��&���v��$�bO��&���|��h���7�bŋ�-��Hm9�2���a���|�2DZ=V �Z�h�)��lkU ���z=��3)�Z�b�Z)`&����Z�h��?	��$�`8�� �Z)"��z�$gH)$g��h��;g4����zo�+��ےZ�h���z�ڍ$	)�7�)��g���*
)�Z|~߁�|�Z�hr��d�	�lj�d;����ܳ�B2<��z��hC8�h�Hm�Z�h2��*��$*|~��hЁ�|X���p
X�倂k��I��h�)o���Z)~�y��
��4b�ױ8�Ud@��k�
o�Z9D�Z�h��W)��m8�hm£6+
})�Z�h
��.
}|~)9D���s+D�x�Z9Dc
}����q��zPB9D���~��T�x�9D�&�����-*%9D���z�Z5nC|~
�b�妞��z���	��c��Ǽ��=����d	�<�:�x<�U`�Z9D�Z
}o޴N��8*���ǝE���8k��`%�8�:���`��f8�Z9Dj��f=9�9D��8 
}�\-�5n9���fT DlL6|��Cv�}8bVB�
���5n(��`,
}�Z^m'��j&6|�}8q|~M���l�8 D�Z9D_�8�ڂk� D��"g��L��8_+ D
޴��g�m��"��?������-l
}��ݧZ9D4J D�K
}���?�&�;��-�}8�����`ԗ�թچ?a[�*-9D�ڇ-+���6|ʈ����xC��<��=��
}>�@+|��ƫZ�|��|��:n�9D��}5�(
�zad恟QҭZ�x��|��:�z�
�-[5nk�Ȁ́�a�8>��Rɽ�`�
�-�V�o�|e��ݿ����8��%i��ktٻ�`t��Y���퀡j��R����7�EϮZ D���`l�:nm�8ȍeM���`m[�Z6|=��k�b����ٱ��`7�X8�6|��kԂ�- �:nZ)|��Cv��)����j�Cv%'�Ă).��(6|d�����a�)/ݶ����<+.������|'�[d�f�c��'8��c��|,�$�ډk�)�,Ĵ풉kH�*#��$�w��ډk��$�F���xŁ�<��݂�$��|��k��)a�:n�[d�z��|ݎ�c6�)��-|��|lie���JW�)��'d�o�|,(�|W�)<�$��)XwU�|tI��$9���'���0'ѻ3�)6�|&��aރW��z���jo�M��)��|�-)��)U?�i��}�ϸk�zџ�&��k�=|�	Mz�h��d�ܪ$ۯ�@�=�,�$Ё��7��&�Z�|��)�%!y@�`��)���&)
���&E�)�#'*�|(�`��k��I9o&�ډk��=D�)���=D(��e�|���~�)��`i��<��$��Yd{��b�Z�|H�=D���&��`�Z�$���h��&��=D���&��h���b��=D�Z�`�Z�$��|(
�ځb��=D�Z$|��-X��z�ݸ�8$ǁbU��h�������`�{���Am���&H�=D+'�|�ځb�'D��h&"Dv�|/��&��Am=��$g��bA�=D ��&��b/$|�����Am��i#���h��&E)�ځb��Amu�Ame�|BS�`�{���&�)||��|�;|]��b}�"|���z,�@+�����&@3�`e�8n�D+�ځb/|�$|;�=D�Z�`�3)�$|)���|�dw�Z�&�`��{�?�<��*�Zv?�q���Z�`��=D%$|�;D��<?��Z�k���܀㴍����ځbz��h.�� ?�Е=D�Zi8��"|�"D��=D,��<��V���"|�i8�恋;|��Id�Ёb��?���;|׉4�|�9��Am��k)�|��;|���m�)�Q�k�Z"D�?�,�;|��9��Amm��-�)Ӄ;|��k��Am�Y��kZi8`K�d��;|���߁"|=N�k�:�ˆ�>���.�� �Id�)��|*dw��;|�Z>n�ZdwBϧ>A�a��;|�Z�/�-g��"|��|��;|R�$u��"|���|�Wm�Z�h�$D�;|�9�|��z�<n>�F�o������!���d��b���$ʒ�~��>��f#�&�|i��>?���i ��>�Z<n���$��ڒ�-g���>?�Z�+�X9��k?������Z<n��)�چ$�?���+#�|j�B� '���<n�Zi8�紏�;|d�;|�� 'ه'a<n��b�*<n��;|��o�]d;}���$~��j��b�;|9���{��Z}�+<nm�c�	)X���"�bH�;|蘧>ㆧ><n.Em��8� '�Em�ڧ>�Z<n
�bՀ���Z�|�&�ZEm�1�|�ڧ>�þ�D����|]-�|	�|�Z�b�h� '�ZX92���'Em��+�b�� '��Av���c*o�Z<n[>Em�Z�|�6<n�Z}�oDo_�o�Z�|�e"L{¼/<n��h�D�<n���b�Zo\<n�چ$h��jf?}�}��?m�f�F�k8�oE��`NU�:�x���9�Mh�f#��&D9�,��(�	�|��&D.}�}5��V�bc��ih���&D�Z}�&Da�b��Bd�&�hR�xw���+}j�JvA�Bd�Zo�[�o��)�?�hH�b��)9�۝����|�Z)D��)��� ao4Em��|�	)��@o�-|�?�%nՀ���	�|���{_�ZEmm�-ǔZm��hs�|���(��&D)�e�Zm������Av�Z�|��&D,�|��)��Zm�Z�>^�Nm�����i[)DN}��?D[Md�[��Zmp�?D�
�Ӄ?D��-T�tA��?�?D���G��.�� »Zm[)D��&D"%�fz�|BY}H��?[)DL�?D������&D�*�����'C�����0�k$�-́�a�~%9�29��&D� ��)r�&D��-�m81�?D{;|��Bd�);�)3�)|A�&Do��+��?D��|�-�)|:�k��R�T`8m$'���O7�)$'��iwg�@d��Zm�����)D��b[�km$'��?D�q�3�z~ڋ�?���@e�Zm��@dۈb�Nv	�@d����
ۈb[��b[�k(�)7)D����@d���ϋI+G_�
�?D�Hm��)|��V�?DW$'���b똸��?DޤHmmZmh�@dۚb�@dm��b�iw��b��f	�?D��ۈb��Hm)��$ۈb�?D[)5���
$�$���bK�@d��&�m8,i#%Јb��u�d���@d�m8��|e[���iw��@d[���')B/go���@dy�[���,Ђ�h��+�2Ñ����
)4���/)�R�m$'�=��|m[�L2���o)tFd[`8��'�DFd�)�!!D��,��@dH��bl��,“�h��F�R��[Fd[)ۈb��|�i۸� [FdjNv�Evk�X�i��b![Fd"�@dGK�fۘ�,���b�Nm#[~~$[^mv���q���%[Fd��$��Hm��Y��&���R��h!��a����Q��h��|k{�5�'�)^�`&[�^�~~�)ȃ������b�t�m����Fd�)���g^m���YEvA�`'[)���?���(�����y	��m[��!!)[�|*[Ev+[){����+w��)	-|,�hӏEƒ��S��,�^m-[Fdw���S:[�]FdĆ�?@^m��z>����+m�)����nmw�-D�Fd�[m-|.[Fd/�hρ�0[Fd�}7�I8��>�-|��4�L�G"��`p�-Da��x�	}��81�82[Fd_�H���>3�-De:9D4[Fd5[�`Ą�x�-D�=�6�87�Cv�$�/� D� �`8[�`!>^m���9�-DX�Cv��M+���x:[�b�^mT�CvA~~�#uo��+X�}�C^m���;�-D<[�b�	 D=�f8�����	�"g�-D��}��-D�a�M+O�Cv(�������>[o?[�|m�8@[-|k{�A�Cv�|*��Ȁ��k�mw9��׀e�B[9D���`��?wKz1܁#u�CvC[�k��&%;|�#�4���`�)|j�k�-|��Cv��b��}?�Cv�	}��G"D[mw�p�2o��HvE[mwF��`�8G[ob�=�d����7��8H[�|!��B-�8��}j�����Xm��i:x�ҥ8Q�	}“�`���oI[�|�#u������CvJ�Zm˩xK�8.����'a0�8Lۉk�FT��x2{�6�8E�Cv)O?���$�|8�$ ���c�\v��Zm�|���`�|�Dd\�Zm�)'\��-��E"�k��)��)'l�&��)'�O?M[H�o��m�����bN�)'���kH�-��Z�O�)'P[�|�J�o�_|�j�H�8��h�[�j�-��`�U)󑔒dƈ��)'Qۉk�����g���|Ȁ䒐�Zm�C�hR�)=�
��h&���)'�&g��tU	�E"Y�Hv��aeO)_��$�)''�HvI�Zm
�|v���ҀZm��8[�]�$��fO�)��Y$�z��)�hJ4b�)*UTg58�S�=�S[
}���T[�-�0�h�V)������$rM.3
}JW)Uۉkc��c V[)���e�-������fW�)'S�)�	
}"=�&a��)'�y8o��cZ�x���$�E"s��kX�Kd�gj)�)o�	Y�Kd�I���l�?Z[
}��d�0�x{�h�b�)'m[���ae�T
}J�.�!�E���x�Kd[��`\ۃbރ�][F��f�&g^�Kd��a�֒	��`_ۃb`�Kd�2Dx��b�=$|�7�(�L�����`΀�b��&9[JV"�&a�Kd�:�|�&gb[�&C��`��� �f��gԵ��bm{�n�Tل��'c�Kd��Um*�F�d[�iSS$|�Ŵ�-|���ɍ����Neۃb�'u�
}9+$|���l� u��z9�a�|�/د�}ۃ�����v��f���ff��Й�J��I�ϣ}g[�`��Kd>dw�����Kdh[
}i�}j[�&�}~�|k[
}l[�xmۃb!
Xm����k�M�*��b"�Kd��ce� ��bo�Kd"dwn[?�[��˃b5�Kdoۃb�z0�Gv���xp�KdA
��q[�k]��`r�Kdo�i�$DL*i8sۃb�����k(�L�t��`n��`��hs�eMu��`������xv[�kw��`7��`J���Y����G�����"%�fh��f��-��x'j!�
j�x�\md[�w�5�qV y[�k%�q%�i8���5$|�
�ǫ���;T����|��E9^m��Rg�}��q%zۛk�θኛk��*������V�{�}��C��+2{�|ۛk;�&�}9'�{�|!�Y�3��k�?��$Dj��k�Id��4G���}�`��w0~ۛk�	�f��=����ϋ�$�-|Y�O�	�E9׀o�z��x[�|D�{À�$�iv�f_����8�`w߁Nm[��=���z��&@ ϋ#i6��U�Jvo�i��Xk�2�p��))Xm�Yi8��k���i8�n�7"!�[�b3!�h��w���\��k����7��&�[�`׀���Xv��i���[�b�[�`���k���&�
�J��2[[�`a�q%�|��Π��k��(��b���&Р;g� �Q�l�Fm�+�44�`�ɀ�->5Od+�`#�)|���&���&*�[��;�`ܜ�&��́���h[�p��-� �i]Ԃi-Od
f3��`��#g����#gh��{JFv�+؂�h�����*{�v��h��-{R�[�`u�#gk��ͱ�-lUm��&�*�`�$�Od�[�z�[�`�[$u��/|��?�[OdJW�[�`�JvoROd"�>
�>���&��#gm[�R�#g�Od��z��ݹOdw$�>��#g��`��&��Bd5�b��&>Xm���&k�#g�	$ue�G%�#g�[�z��r�+�`�Od�6|�[�&��b=��4�[5G�>�[)D�&�`�[�z�%�`�[�>�>��@���aA�>��h]�`HMd�	)D3��k�����L"5)DΈhe������9�w��{�5����he?	���|ʘ�#g��	 �[)D�u%	�#g�ˣ��#gZ���Av��4�[)D�G%��l$u�&�|��m�)D	[�e�G%1#�z�
ۖ[)D�[�z��#gp�Ң����H)D8�m8��u���Bd/e}	�`)D��m8?�����)|�ݙ[�`m{�d�m8�$�>��)|��[d��d'�`�we_�:*
����z:�'��>��6���m8)�Bd*�����,!�m8�2��he,we��F�	�[�$��-|��m8�ۧ>)�����MdO��6{�	{���O��*i�?���hb��>Q�μ��Y�[�����(�ۺj�ۘb���ɻ�he���{��f5��F��X��\vc<u�[)Nvo�%A
�Q)�2'Ɓ{�c)D���k{��dw�s���"Nv�K�v� �2'*
����jNv��m8��2'.9Z��\v��_fm8B�h���9��6��z��bwNvϋ�c���&�h��(��`#.�բ[�hN�2'��`#
��_��Xm��2'��m8<�h�2'��u�N�h�[�,��z�,�h��k�m8���z������(��i�
�
~���z�2'�}�'g�2�[���-Nv�ɰ�ہb�&۩��z�[dw݁�b�Nv���z�Ьہb�<u�F������z�[�h���z��Am�~~���j��-|7��i����Ȁ����zgm��b���zQ�Am�[�h�:��2'���b5<uւ�{G؁b��z_��bЁ*i�[)|��*i���b�[��/}�2'�,�*��Am%��'n�b[��[}
�*i�2'�[�f��h=�������hV}��b�Am��v�Um*�Ah��uE�h���zo	dP���*�|���z�-|���<��e� �[�hy��zւ�fI������z�x%=��㷆��{��b"%�f�'9D@�Am���z��`�[9D�%� ��il9Dk�+�Am5Fd8[��[�xx��z�[-|�/����*iH�;|���i��Am�[-|o��&=})�)u	
�
�ND}�����U���&y�Am��)u�R�1=�����x��`��Am��8GT-|,�-D��;|����,�&c�)u��-D���`U���W&��;n�G�c������;|�-|���`{�c��)u!([�3�bW�co�f#ȒR��&��)u��j��c�[�b��-D��c[[�J�
��n$T�b�;|�[�b�[�c9��ʞx%�����}�[���g��}�[-|\m��b�[�cs@+�Nm�դ#�b�ۚb����a����ۚb;9D�X�݁�ݿ��i�b�9D`��V�3�c�%:�ۚb	�0<��bҀ�i��Zm�[-|�%�cv�3�.�)�*���ۚbm�fr�;|��)u���h_4�cejlb�u��i���i��bx���Ԃ�`��)u�[\m{�呀�Zm��h���b��}��o�"+!	��h<�-D4Em.��b�+!R+!��`��Zm��8#�Zm�,�,��
����c�ۿ,�?�b��c�{�[+!a�����Zm3��be�eu"�b�=c���*
+!2��f('�B�b$'#��Zm��	 �&&���f6'�[+!�;n��R���
)U+!eɸhD���i�CX"+��k�+!��
)�\m��	�Σ�j��ۚb(�Zmo<io��+��Zm+!���P%Cn�
�fl��k�Em�a.��b#��,���i�
)��{l��Zm�$��i"�F+O�)�p��+_�����Zm�ۢc9W��i#���2��Ӛ뀘?�[+!�)$ !C	+!�Zm��%��'���z�5�1+�Zm9��`��c����|%:F�h��&�FEm�ۀ+!�|%g� Ё�,�
)��)Z���k�WvȀ����[�`�_��)��S��*iA��c�����[�?��)磙I��	�E=��I��y��[�`A�)��(�Œ��#!�/��Yd��	}
��Y�[�`��Yd*-u{��\�}��`�
)��u�Q)��~~λ)�H5uQ�`�5uu[X"s���[����h��1�� ���YdH�����(���?D;�`��Yd"��h+�	}7��-(�9�Q�S��h�W}�}X{�Uv�[Vd"�b�1|%�}I�b��Yd�2�b(���G��Y�:�Q-:�!g3�	}1��c��Q-������)
J�b�b<�Yd.���j^m��)�Cm*-u�����)����".g�[�`���h�-uH��X�2D��h��y��`�W�b�Vd:Y-u��i8��>�	2Dc�]�C@�b�&^m��Zv�"�H�����`�[Vdi�-|�}��Zv4��b���z#k�bo���`ML)|[S�kW�y��Yd⁞��
�=�^m�[?�3�kVd׀�c�Yd��?
��� 8��h��b���z�Ydn��h��}?����1ҀQ-(�����k��3��h�ƪ�Vd���`Y��c���k{�fŁ�$���h]��$	}R��`�-\�bT����Q-��k�u�G�Zv���$n���Q�/!��۳��c.V \Vd"�Cmm��r2�ܟ$΀`#�mw�G�bT"�&���l$7��
^m��,�+�?�&ܩc�;'[=�?a��$Zͩci��$b��Ё*i�;'ܟ$���z���c��`ܩc�f�ߔ�����z(���݀)�/!(�;'$�i#�hܩc��iw�
��Zv��C�遟$l��$���*�/!c��k���Zv�� v�Zv,��$Ќ�W��Zv=��")��f8�聨?�	\�-
�HmU�h�"��X���&
\mw�;'�4@�+�p]d�
}�<i�*i��+Ł�$ :�,9�S\�b���${�f׀�c\]d
�i	�Xv
\�b>P�h�&�{��W
}PD]d\�h�Xv�!�b]d�� ʋ�I�
}\�h�[�b\]d��k�g�\��c�����f8b
}\�,�hl�b��&��h��ܫ��	�;'��jÄۀ��9U�h�[��,Άף9��5
�b�Id�]d\]dܡjc��"j�!�ף�hs��`������\�b�Gm�	��*if۩��h�_��-�
���]��`w��9+(�hv�'״�k��Z���ң�=�\�i\]d��x���>��9\]dM‚k��`�o��x\�'���܂k�&�.�؞����b�]d 
<i\�hƢ�9\]dҡO�	�b���i��h޿Bd��`�b�-]d�h�#�x�)�h�����'6|�h
�*i$�36����c�|��l1]dJ��2\�h\�b5
GmS��x \�c�H�h�и���'�h�5@��i!ܡj"\�c���9����Iv<�-$���c%�z�z�i#���9��c��c%4��`雡j�դk��\�c;I+��9��i	 �>��j�I+o������`��f��eM��{]��`]��?#\�'PFI+���
������>هc%���k�x��`� �cȒq�b�[d���x$\�ci;|L�	��e�%\�c��&y��&\�'#��>�F/RƮ=��&_ۯ'\�'��&��2o�co���f(\^vo��a����[d����<iv��?���~v!`8cDN�Bd��)`F�f��Y�-�[d�� cH`8t��݄�Ga�����'��b�� dw`8)\<u*��&{�f�W�c���_���OGI+?�c+ܦ,�`8�_�*`8���&�5a����j�f'���=���$�*i���&�$�>(�c,\�c-\�>W`8�\v�㸿��PL�c.\\vh��bc�|�q�݅M�ǀ	 �>�D`8/\\v�=� G`8jQ�")��[d�	�jTA\v0\��Q:<��b�����)!ĉ[d��j�<u���|!��&���&��1\=g��sq��&`��b�<u3Ev<�f2ܘb��QҎG)!�`8x���dY�=���ne	`85Ad�I�J��i�*i�_�>��&?�h��f�frG`8���Ɓ�v��3\\v{��h����$4\`85\\v������,6\?�_Z DA`87�a~:�ۀ���W*���r�ף�=��Fd;��b)�a~��,��b=�:�`̏��9<ub��b%�x8ܘb��b�i%�`,
 D���d��4@�� Zјb9\ DJ�ףD�a~�g�5�� |:\ D��>9��;\�xb�����H��`�*$u��`��k��fu�5a�a~k��'�m#�$u=g<\ D@�x$���9���h�`��ze�����j��>!��zeD5gU>)!��h�����3?�(Ev��&��J����f�״f=\eeܛ��4�ze6o�ze*{������O��&��e���a}%i#���&b�>!��1��?>�a~$u��;|d��h����	���{�"F?��
_dA�>!�i#[ʴ��?݁g--�a~n��Ղa~��xl�g-΁���$�4�ze��`�K�&�*i^�;|��x?�8@�;|\<io�M+r�Х�>��&�-�`�4�c�)��;|��Id:�x��	ۈ<i�&�G�&�۰��`A\�bj������x�?�C�AEZ�s�Id�c��x����IdB��`���xM.�cC�8�X+��ze/�����`?�������kT�b)� 
�*i���fQw�y��xH��X�<n��ze	�c3?�p��jD\�be�c耧>�X�Eܧ>�́D��c��=�p#�c9�{���#+�i#6����R�%�+�)Nm�\m��jb�&!���@m)�;|�UJ5�b� �?��-;�,eШ�� �X$&*�g樁� �����v�9!F\�,��f�A9��=�e~{�ʈ��Gܧ>Q8:���j�	e~")�b~��xЁ*i�9!�ʴ$.�b[>9!3�b�����c<�ף�8gH\�bQ��>g�cI\�bJܧ>���6K\�b��,w���9!z<i����	���*hHe~7�c+_�L�8g����%�bQ�cM\9!��腳�8,��<j�i�����iЂ=�k��/�� 60)W(:���cQ;�])P��	�N\�h�@�h�&!G��y���|e�nO\�<S
�S$|��>�{�%)�R�(�,3$|)(�P\�,Q\}}��c<�<�� �)R\�hS\�bj����&��&c)T\9!�)Uܢc��)c)�~eq$%�%a�/b8
��Yk)�ˢc�)�)���-��b=Qҡ�$D�L�b�)}})1)V\�h�e~큢cW\<ivۚ�h�I+	���	��o8ʫ\T���G)�x���l��,�*i'�	�{�z<i�8�x׀r@<i��'J"�'��?D�����Yd3!}�	ێ�?D���
|
���<��iw\�i#�FC�Iv96�X\�hY�.�������&��xه=��,�h���b���?�h�))Z�?D[�?D���^��`��c~�����Cm.�-\�Cm*{ӏ�h�{[��`^�ۜ��d]��z̈́)u^ܢcg��`d�Cm�����1#�G�c~<�f_ܢc3�	�A�Cm�<iq�c�`ܢcB;|�c��$D؆)u�<g��ha�Cmb���}�Cm�fl@d��c~i�Cm9{��DmA�Cmc�}^�c��|ه�iz?}���Vi�x/
���gd\�`���h��ie��hj{�=�}��|�'�� )�cf\�xS�Cm
ߴ�?g�Cm��Idj�?D4<iۃ�(��`h\<gI�zeWf{�%a��}h��߬��l)�i8�i8��'�.�=�{���P��i��}���hl�$��?Dw�F�߇}<i8/mw��Cm\��`i��`��Cm��q�j܍?���i8��&���h�6�-��h�i8�c��$k�ze�����Ki8l�Cm/IdZ�}�CC{�	g�e*��}m�CmK��R�L�D<g�U ��}i��c�f���}��>�f�[k�i87̩c���k��¢��������y�\��b�L%a��c�tfQfЁac��hd�7(��c�}��E9�e����X
��j
��.���߇��l���x�$9��S����Fa����n܂kyi8p����	۫����ze��Vp�i8��h~��-��zi8�Ć�2z�1o\i8���1D��r��>WU�r��j���ki8�́��&�f8`7k�ʗf8jE�i8�����i8"@-uk'qp\�-ϋ�c
��v��<�=�`����e�o�2�E��%aqܙ���`���Ԫ�n����߈,)DT��P"��,��}�'Gmrܩc)���s\Gm��gYt�)|�=����(u\)D����28�lʇi8v\Gmw\-u��%a�	}v�4�x��h*�%a��`g~Δ	}��h�}�j��N)D�%ao��hZ>-uj*����fw��'�'��
��y\ Dz�}h-Gm�)|*{‚��j{ۦ��z��n� D&�����'c�)|��hD����f{\�x4�}|ܣj���ܺ�f8}�)|���d��hQS�5 D���h{! D��%aGm~\�i�}��`�"uDd��@m+�%a�\ D�"u�@m���h2G-u=��h���T����>T��`�܄?P��n=��5�	���] DF��?"�`��Zmm�@m�\Gm� D�,�`�.Gm<G�z�	}:4�c	Gm9�S��Ɇ�)|��	}
�_�@m^��?ތ�_�E��@mA��?��@me� |���V�Eم��`�܄?ߝi�΂	}��)D��@m�W���']�}>�'�@m…��U� |�܄?��hm��()��9��+w��')&D�\�c�ٙ� D�܄?��zvA+5�}��|���p"�z*I+
F�i��c��m8��Y�\�cX���ه�i%�z�|���c��&��܌�@m(&D0ͅ-5�z��@m���>�)�i�W|�\��\��c��@m��"u�� {#�
�2�P����>�܅-9��
&���-p�)�B9o�0ёܻc���>��g��܄?�ֻc�\
���Ε�cB9_��>i�{�܄?�ܥ>	�ƶ��Sg.�cn`8�ۅ-��n*�Zm<B9��c�\�N(`8���>g��ݸ��((��-���bҡX޼O����w��-����	�1�\�c�ۘ\�?��|��i�
}aܲJm
�Jm�%�<�\-|o�&|��|�GB9�Oع`8�Jm��og�j�$�?��?b�{Ӛ�}�I�h;�Xm�\�h.�N���� \�xڕ�o���(݊�c�\�h՜���o��9D��Xm�ť>�ܻc�ۅ-�Jm*�|�
}�
�<��yp��>�\-|e��>9���Jml�<�飚-|+(�И�-�Gv�-|��{b�o����-D-|��Jm[�h��-D�&��
}�-D5��n��Jm8/'��%a���c����ܰ�[�h�B9��-DJ*A��h�&PF�?�Iv�=��r�5�-DH�x�Dm5�����i��p�-Dj��J��	^m�\�&9S�h�Jm\�Xm_+�'Z
$|�N��*#(�I���C���֣	)�5�<�߁�)-|�\�&�I�����cIDm��c#�|��Jm���%�\�&N�JmA�l�L9��h_[��XmDm����
}[�r��jg�hj#�&]/}ws��\�h�*}:�թ\�&5�&�H%RG۪\�hI*}Y��㎍��5�$D^Z�&�	���k���{�m{ۦ�.�&C
��}������&��3�&��C֗�k����m�}�-D%�%am[���L9�h�Dm튳�$|��L9=�e��,i9��I�1�'��b�ݦ��DZ9F$Dm���e��x��$�4'�2�&��o9�2�{���c���-�&*N}��%a�\�&{ۯ\�h�)�\�c�h�|‰�A;�b��25�'��$DXW1dF����I�<�Ǫr�%ao������,�bC鴘7�3��$O����L9�I��|��$D���&��I�$�S@�NmܙE+���<�Nm���<��}o@+�cQ�+x-]}|%Nm���<�z��}k�G��\Nm�\�b��Z\mQ�{����V�����"%:B�F9�x-�#�X��%a��j��<��}_��<!��J�F9���\Nm(�����*-Nm(�R?��{!�Ք��.����������)!�\mo���{ۦ�)!��9{}�I���ǪyU\mELNm.�o�{g)!m%�����84'��F9(�R?�\NmY)!�`W)!���<,��<F�F9�)!H\m�4Ȁ�z�o�\��=��Y9��L��or�¢a�	���}@�fՀq��Zm|CC
����)!g	{�\NmÄ}t��Rn)!�;NmŸ�<b�0'eOҡ7��)!ehl���a-��<“�<�Um4'?��%�\n~L��-s�S-E�i�v_���k냮����<���ETx-���j�R?�
f�[�>��W�܋k����Q�iX۷ډF9�>���o��j@$u뀼���>9��l !a�|���nt���h�>���8
2D%�)�C�
��-��)���e$uu�(�9g)!5�����&�	)!����%a�\�>-7)!�p��܌-��&�\)DW)!��)���&;��-��a)!���<%�)�	Q�V)D�����)̀�{��܌-���&W��A9��x��e/�`�)D�܌-b�	ێ)D.�
)�܌-�Md�\)D���aI�
)�v�…eM\CMd��&�\$u�\)D�):�)|�Md%�)�)D儽	���x)D�MdC{��9�x>��-��#g���h����((���!g��`���ho���΂!g�ϣ��)|	�>	���r���)�8}��!g���x_�)|�>���h���<�aI�x��)|�={�;�)酔�O�&��܌��`��O�D�	��!gȀ��!gI7痁_+w�r�9���>g�\�{e�)|�\}�Zm��&Ĥ�-i!�y�)D.�"u�\)D�\�{�\`#|7}�9�܌-T��+5���fU�$�����9�)D�5|e=�����&��9���(%
�`�
�Qc�����aԀ�(�{�߁�9�aj)D��9a��h��)|�{�c����Im���9Xy0h��9�y�b�i�i8Ȁ�5��[���3��j�����\}���x�9�"u���l���h�\i8����bЁa8��h(}�����ӻx�܃?���h�\i8ʈ�
��9�}�h��9�{=�E�C������`g�!g"��?��.=�ގ�-!�n-��H)[�k�*A�{�&M�Uǩc�\�?8�k�\�/���b:|e	��
CaV�"udE�R�/��� ��c�i8��F������9�z|���(��b�)�����\@9�����9�)�@9�\�h�P}�0)�ܩc��J�#)��9R8�R7��c���
���˧C�W)�)�\}h��9�1�)��{d���\i8n)��n��k��zeg)���{�\i8J��,۟�/�;)�'�k)DZK9�i8��=��}�)���/9���vТ�	58�\i8�f8(J�)z��-�{�W)��i뷩c/'`&�|)6�9'".-|�	�ί��/偄�6�9'��h9������o��
�xX�����n����o�a/'�Mm�\-|�\�'J�A-|��c!"Um��-D��/3���_�-Dð�])�\-|�
�x�\Um�-|
�'�����
�Um��.�_�-D�
�)�\�'���z�)�J���-D��-Dm�a*�|"۴�)�	u~��-D�\�'�)d�M[PUmc&���)��-DX�J����-D��h��;u��-���z~�y�X���-D���-�x��hl��k7�B�ja/'���q�F�Ć-D��>��}���.kUmV�-D5���Mm�N�\�h�-|�-|"Mm=����Zd��'/'�\�?�9'J��\�x.��+�Ҟ-%�x)�����|5���Q�3�l,�|�\�x�u~Um���h�\�'f�9'�\�ha��h���b-|:�?���\�h%
 (J�G�UT�\�'��-Dɀ�h�	�G�-D��|m��iϜ��Zm���zy$�c*6Um��-D3�N���z���hj����,	��?�-Dl��������h���-4�-Dr��`��,w�}f��c��&p٢���h]�c	��?��{:�}��hIF���a݌-��_۠�,w�p�Pɿ,��m8�|���]�c>�}7��h�Uɚ%fx�O9�[?޸�m8ĂO9]���O95���~e�;T�m8�	�ݻc���I��d�����{������{��,��F���h��{"��?���h?���z~������h[�	��{��t��A`8��-
��{��_��o��h���,��؞J��(��D�*����m8t��h97f�j�j���o���?O��c8�xګ�ؒ=��ٻ�b��ǁv�9߂�c�W�e�˶��X!�|�[?ݢc.�֣l��&rV�c=��{���w9-u�Xm��(i��,��-���
�m8��,��}�|�Xmݢc#$�z��(g(�Ӹ��d�z��{)J�*�-]-u9�Ύ�m8�XmŜ�c��{
��nݍ?n��cD�	}��n�O9[��?�Xm�(g�@-u��iw��i��-��XmJ���i����Xmk�Rp��(g9�V�ހ	}�}�;|��:T�$cb��'��]ބ2���cJ��}3�;|��d���'�;|`8��'�	}ݷ}�#V~V��;|m4'>�?�:F:#g�͢cÄ�n]Zm��,��'��a�h�	}���'�Ad]�`L�Z�����W�(g��;|]�`���܀��dݢce��'��Xm�xڝ�b�~��ƥ�o��k��?$�(g]�k��n2��gG��'���U�(g9�W��~~���k)J�v��eVdR�k��k �Xm��k�-u����?~~ -u��N>b�a5�y+�i��;����#�!�@m"�	}�{��-g�@m���݅�N>3�^,���`�*�{#]�-ު�'���ڋ��$]~~z�	}��(o~~�Z9�mwj��R�jT�����e%�	}���'��'�@z����{���{�i#�
&��'������E'��'�{(]�&9�ƴ�{�Bz	�Ё�-��({������{)]�{��i6��N>*]�a��{e���+]i#��-m�&�`�)i D��&��klie����$
�W�ih�@mw�j�(�Z��ݢt��W�&�b�`ߝj�)��F�=����~~��c,]�b�N>Jr8j�/��iςk˪b�-]\m��7�ۉv�8?��(�\m����cl���.� S��`� �,B|~�$���`�[�_��N>��0��|���$�zj��8Q\m
�-m��n^\m	�b"��zk��}�u��X�7�&h�c�
�{�|~�|.]\m���o�D\m |~�
��9�S/]\m�|~��+0�{��|��튼׼�b�1]I95
}��:2]���\mx;I9��f8��I93]\m4݊-5]
}o�i6]I9���(rKk/�9D�]m�-�I9�
}7]\m8�8p�?D�	\m��=����،�����C�bԂ�&
}g�?D6�b���ރv���GL
}lie�M%�b����Ɓz����Zm�.]9�?D��bc\m����mVm��b��|C3\mwF�7Vm�|~��f�:]\mIJa%k�o�\m= Vm;]I9�|~�5Vm�\mADm<])=]\m�CVm�n��?5�?Db�ܘ�'>])m\Vm�)?]�h_�`��i�$|��}8��ּDmf)@�@dA]VmI9B])��/C]DmrFVmf�)D]Vm7)E])F]Dm`���_��f���(��`�|�Vm�)�DmG])H]I9�DmI]
}9%���`�ա�*�^9�BVmJ]I9�ɻ����):�@d
8VmK]}L])�)�}D]����o���}}�'�D�>စ-6�^9���x��ĩ6�-=�W����n�a%��l��^9Z��(A�?D��a%M]Vm��)N]VmȀ�	O]DmP])a"�W
:Vm�Dm]��i׉В5)�=Vm�f�耚-k�V���ց�nQ݃bʞa%�=�R]VmjDmR��$)Vm9�2�>�����b���x���&S]).��bt��&�m#T])�$�$U])V]�hW��&X݃bY]�`�3)Y��`��-Z]�j�yg��be��`��iЁR���j���|���'"�)�
 ��h뀼���e�C�ޣ�ȴJ��+[��h{�j��&��}��b��}�/u�yR�$9����u�o���\�}"�)F�.�|]��&��'5/u(J����&�F�/(J���F9�/u]�}��j^]�i�m~5W�67�x>��&��}�ۖ������_݃b�K�k:��k���bm4'��}��&\s��+�i)J���O)J��?��j~�i8�z�݀)"�F9o�}`]�k*�F9�'Y9f:i8a��&(��ǡ��b���bb��&(��ǂ�F9�3�iS�>����c]Y93�>�Zm���+��&
�Y��d�:gJ�d�M9c��e݁b��&��M9��|��b�V�y�Y9��}"�M9fݦ(���
g݁b�?Y9��yh]�i����ji]�y(�����i�
Dd7)�/j�}�Y9�M9)J��ަ��bo���i��(}؁b����G���ځb��}�����1k݁b���l�}m�F96�^m���<B�M9)�ަ=�o���r�t�}n]�&ӻ�b���	�������c�29���ݢ,�{o]�&���p]�kqݧ(e�/�l	{Ё�%b��(�&H:Oت$u*��@kĂ}*��iЁ�1rݾ>s]�&tݞ-Ɓ;�[p�uݞ-w�a�&�&��im�aY9 !�%�?�7�i��Jv݁b��Ԥ��\w]�bR��b��`ž>)���x]�`t��-��`X�i8���>�`³�	y�M9zݞ-��`�&�&1Ɓb{�M9�q�|ݾ>]�^��M9}]�`~]�bݞ-�b�����.��;|)ì�Im�)��;|ЊIm�]�`΂�h*��&��h��;�`Q٢�C�&��^��@k�]�&7X�`l��`F	 �#�b4��`�r��]�&$ !���`{�뀼�<�Im?��s�ce����e��>���`׼Ima�&`���d�Im
NՑ��t��i֗C�j��m"�x�ce��)ЊImk�g��Imh��h"��-�ݾ>e��>%��`���ʃ��́�$������-��"u��ce���h�����i%���ceh����ݾ>v�_��x��ce�]�b�ݞ-��h�X���R��>J���
�0J��[�]�b�])Dg��k���`��"u�	پ*�i��X�ce�]	 �9ߑ�`��;|��Im;�b��`��`6�^mk�vh�&!*�(�)D΂�h3X��]�b�����)Dd�f��`�1�bん<Y�ݕ]�b�ZmP�EP�)|���i=��{���h�9!�]�&���h��b��ce��7�c�����`���*��ʈ���ŗ�	�i�]�b
��_����
c���c����*��i9�����,뀼���m8��,���ݻc�Cٚ]}g�k�ݍ?a�b�]�x{Kf�Cm���c��m8��<k���6*ȣ5��cwZ�(�b���^�&F$@9�}Ä�i��&d}r�v�/d�^��)s{��Cv��W텔�`���}	���i�}w�OҞ�`���}H@9�]�bt���9���m8�@9���,.�i��>��&�b�g}�]}e���ܤ�b���}���c�]}��]�&��o�ݢc���&�]�b�]�&�?v&Mm�8@9�]Mm�!}�ݢcx��c��i�����]�&z)�
�&�U@9���Y��j�
_m�ݢc:�h����]_m���-���o���}t~��h�ݢc���}���m8}�`m/��;u�Mm���Q~��ɧ�m8�]}�

�

����&`���]Mm��?D��i�Mm�&	)�����_m�Mm������9+�ܤ?�磥��_m@�;u�)um�a�����}���]@9J�������;u�]@9����]_m�O@9��?D���}4F�P)��p	)l�ݠ�k
��_c������}�+�`�])Z
�kb�a��c�Ey��IdĂ�,�|��d�`%�B?��?DԀ�(�)ף����`yMm��Bm����	)�ٶ��>���b�$�`��i��Z!�k|ɈbÀoD�h-D�|AÁ���b�]�k����3�`�(�ky�H-|��Bm�݈b��|�]�h�XDX�s��]}���i��;u�	)@��(l���B-i#�?D�]}ii#	)��&�s��i���b�����i#ji#��hjw��}&7}�i#a��݁�i��;u�����&�]}��?D�ݞ-�Ȉb���iM��by��);��]}9�&W�$$��-X��b��{%�UA�V�&��8�ݞ-�i#�ȴ�� �5i#�]�&�)��&��Bm<$}�{]��&�i#�$}��kti�])o�<�]�`���]��&��<;������� �]�b���%塈b�j�4��&3�M�]��&ti{��i#���&�|�]�b풥%c�@d���bt�~~.��{I�t!�b��x�t�B�b|
}��}{@k�]}�i#�cF�����^� ���i�]}��}9�Ů�j�,i#���&�]}ii#��Vp�]}���&�7�b�ݥ%"��%��$*�&��}�i#�]�{
��"�:g�����&Y-u��b�}��/]/�b��8��|+�/8�m~��}���	�04��&��}�	�&~�eb��z�-u�f8V��I��i���;��c�]����x��|cȴ;
�b����蝢�&��x��}k�	}�Ҁ��ܨ��x�)!8����	}<Rk���Y�cA����])!Ⱥ�%�ҀĄ�&�"J�sJ7P�c��'��	}h��%�]�b]�cV�}��
F�#g�\��o�Eyi%���b,��	iob��i��	}���'�]Fm�]o{����'<�Ԣ$�g�o�F�Y�ݟ�Dk�Fm�`��i��	}�`��i{�}���Z���J[�
�w���$���@�����`�
�9S���d��^�{�])!}��(%%Fm�:g@k�{�Cv��
)����5��]�z(��龀[��F�*�cw�{�]Fm��j�ЂC���Y�2�ݢc�)!������J��i�-u�)!_[�=��c�p�)!��cw��{?禞b�-�-���c)���]�.@��c.��b��h�>a���c��5��iЁ����Ey��x܈@m-(�e��
)�ݢc��	}�
Fm��l��cw�]�xک}fRk����b�[��cw	del��ܼ��k��	��&�{���$`1F�5��=u���k��e#d����'��uC	deŁ�$�c����=���`��!g���/��e#.Z��$o��`�o,�x��|'
��p��k�]�`6��c��`� !���_����k���$��b����<�a���$���ݍ$y������i�!gv�)n�����\�!gŁ�$3��ki��Q̝�c��f�9�����$�!g��hS�W퉙�/��"u��܂��$O��
"��z遍$B�i����Ł�$2��v��)��"u%�!g�]=u�]ne-��f	��zk{�o�Jsd{��1s�-���zȃ��腭���G�jRk��z�(48��x��{"��$>
}���z���ki�<�	�(9��,��$z	�xҀ"uAne4�i���Յ��z~\I9*
ne߂�$w�I뀸��]I9���㪙�zi��$�]I9Q�"u�:|���,���z���z��`�!g����m�-h9�i���/�]I9ɰ�`��ܪ5�d�I9.��/��i@��cϔ�zd��i'�+���ik{�΁�+�����]I95���*Fah�|�ݫcd��^�]I9�]
}��ifi�|�]�i/fk|eROI9D������+�
}��z�]I9;�ݓ=j�]�&܁�z(�
����i<�"u����_��15��(�]@9��"uO���bne��h-�c���z��.@9,Dm"��	�b5�ָ�]�&	��z���5�]�&j��z�ͣ�]
}�Θ-��&��e�뀸՜�&"Vmb�m~)���)"Vm�I9�|��T䳔eDm��i���&�@9����
�n@+�&���D�i�EI9�� ���+��$���OI9*��&	TU&�&4�i}��`�@9�֣�ݫc�]�k���9�dP�]�&�d�i��/.�b<	)^�`���`��/��Ea���`��k�ݫc��`&�&�	)`�5��b�]�&��}�Km^�`Vm^Dmd	)�L�b[�.��3�k�(�(ӑ�`P���)���ʇB?d�ie�.�	
;���`꫆$�h����
���h��Ao��`^�&<�a�ie5���#�����&���`�)^�bm�{�@9���k����^�`��r开���Id����^�&���	�ie���$뀚97��h_�Id`�)�� ^�''�f����ܻ�g����`9�%�������&	��`���9��$!����ie�!�,��zo���_U�kX��&��{��&h�|���,&��`^�b	g��h����	^�'��ܮ�Bm�{		),��`b4b'��(�X�k
^�b�)/�b�&	)��k6����ie	I�`��ׁy�KmR��k��Id^�c�5�`d�H,�F9�	��Jv�
�ƕ�N偤�o��Tp��5�+�ie�����4�o�i�ieQ�����'�%S��`�� ~�����`D��iԀ踈��U���߂F9�|���`J���m�)J��m/���h
^�zR�c^�z��G-��M9�M9������_9o2.g�
	�c(�Jvv��%���-�ݚ����M9�Y9�z�$u�26��:�_9��<����;��cm�����
&�ަd�5�m�#�cti{/5�ݩ)H�M9߁�i���3�F9��?�G-b�{.��cQ��l�_9���<�1��f�o���j��$%�F�M9^$u�}6�'脘b��M99��;��֋�M9���b���O�&`)��=�1i����}R�cs�E�ޘb�;$up��9�WW��(g�Vd��&�D9$u:74i-��&��G-*۸d���)���OmW?�V�?Pt���{�D9��G-q�X�M9~me��D9?	��)!�����J�)!ޘb��G-ti۲�'Ȁ��^�`	�_9ҩ�'^)!�����G-�	 ^�`��G-��'���&n)!^�`hue�Q�B/fM������[m��c�Md2�D9��'�)!�#ue����F���?�����[m.��h��V��Im6+)!<vq^�`)��$Ome��݇��b ^Fm�I�F�'T��!^FmxC�k�_m��[mЊ[m\7i�	 Y)!�[m�{�7Fm�"ޘb(� D9�[m�&[#^Fmׂ�c�f�r���V��b$^)��(g%^Fm���&])9��A�[m����/�5&ޘb'^)��/n)!��[m�hMdAd9��(��i��.)��'��/��`�!����&Q�P���*^�`�)!�NFm����)!�Nvw�/���-h!Fmr�p+^�/�)!�) ��i,�[mg{��`���-yO�`r)!K�u\-ޞ-��9.�y����
�9�X�+��i8�8.^�?���-�|kTIFmF�9/�[m9��NTFm��-��b�1ѫ��0ޞ-���賀��
)��9�b��&�́1^Fm���-����,�y�9�����G+�����)2ޞ-�ې9Fm3^)��i���&4^Fm|�@5^)�
���P�/��8�nw�cȀ�|7i��������9%�6^�/��<m:�/�)�[�.��{	}O��bPf2Ϳ7�be��<&��'�(l���8^}�2Σ��o��=<�5� �塁o19^}/�Z�e��;:ށb;^�bI����9L��-��b�����{��i��{��o<^}�Y}��9i�<6��{Ӄo܃�-�}=��{��n�
�9X���3�b=G}w}DK9���9���	Cu�^}��9��%b1Mm�cq*�$7�b��xe�9��_�	)���ߣ��>^�&�������xe<��+������=5�ҡ$�偖��}	� =�uv��?�ӥ��%�bz�o�
��{��)uۅ�{Ŏ⣝�o�0Mm�}�}�C�b?^�b��<�'}��]9�	�@��zA^�b����"}���s��zj��f�cDi��$��y���ó�zcF}tP�b��2{�Zd��b�{���?D�v�Pvff�,R9�_F�B�)u���i�I9��[d���i�Fa�����c33�iI9����h�|C��z��
þ��{5�)ue	�`���z��z��{��J�����zD^�c��0�j�a���a�
 �������E^�i�Mm_����۷F^�cGޢc��)��$#s-I9탚b���zHޚb��C���I^�h�����b �cX�7�{�cd�ۀƁ3�J��h�b�!U�h)�+�h�ۚ/u�I9j{�)C��b�/u��cw��
��n=��K^�cc/u�́��UTp��j���bL��z.���.�����iMޚb��S�z1N��zO^�{��V�ˢcۢ��w3���O�;u����Km���i��`���b���kP^�,�K�X�΂)�/u�{&�Km�/u[�Qޚb��.�`c��'�Y-o�,��c���R�Km��{���nc#{��'㑦�c΁�b��c咉k��hS�Km�7�%��[�d��b�`eo���F9T^�i߉Kms�z�8
Σ(��h��c/��b׀�kÀX"ۄF����`c��GU޻���7(�eXʚbh��c.��kA��cY�O�|�F��"DVޚb�����J=��Pd���&�6��5/u{fف��Ȑ��Cm��nǬ}W^|e~�'������w��f�j�{������x��*gy�1�
�{��9n|e��.��Km+�.ؕ�<-u�Km��܀�}��'��6�"|�(�>��WX�Kmm|e��:g)���ף�{���oo6�Km�z
H�i&�OȀ�7A?i1�:g|eY^�i��^g��4��z�z
i1뀥9��:g:�c��i+��`6ljk����ϖ�Z^-u��i[�}�����i�{�+��Q�:gf�i\�	}'�+w=�z��i
���L<-u]�M9�,i>�}��{�"|׀p��F,-u6ze5CdjCh�C���v݀}�-u^�M9Ђ=�_�	}���d���� �c���C�}R�-�"|�����b���,�c��}`�"|E
��	|ea�"|}�M9fRk^	�be��X��$�&���{�@m�"D���a�%�baކ$b^de&�h㆘-(� '���x�cކ$��M9��hX�)��a��9�Eded��`��&���5dee^�b�&�b��e��h'
_�.g�P�i9�;�f^-u��z�?�i�[���	}GOm(� 't�b�i1�	}8,i�o�=u�}��)]�n��b��	;���+`cW#�뀭c�Om��	}��`��	}g^�/����)�,Σk��-�}�)ۃո��cUB�/h^�k�9�4)	���dew�&�P��ji^OmGm��`j^�b{��c��k
�ĢV��v��$�W�b��cM��h摣j����R�k�!�hk^�`�
����h���$���c�&>�/�������l��'(� 'I�Ym=66��m^�h��nކ$W��jo^)��$f��h���k��'���$=���p��h���`��Ym5��)�ĩc�́��'�̆$(��ά�c�,B�xq^�'��^>�\-!��d�"�&Drޤcd��'�
5h�k�&D���`4fĆ)���ks�&D
�z�,)k�v� '�))������;)o��T��	>�XԀբH�/���Ȁ�?}/4@�)M�`��c�-S��zt�&D>u����c����hu^�a5�����zv^
}<����w��z3F
}����f1b���8�����R��$������Ym
}Ҁ�'[��?�x�q�Tl8��aQ��h� ���`��&x^�,8�)Z�:�Ģy��{�'���j|���䣀�!gP;�-��co���{Ң��(����^A�m1!�&DG��>>�Ij!�	�czޥ>Ђm1.��yᎭcHu�ȀՁquY(�����o{^�'��z�N6�-�-��y��[٥>,�<��&Dr��]@9oɀ���>|ޡj��&DQ�=�}ށyašj.�?���~^Vm��?c�W,m����'@9��F���Fo�c��ܹ�?S3Dm�Gm>��,�@9<�o�t���;a��j�$'DR9u�Xvه=�t�բ�ͥ>�
}g�c�P@9t#a�VmW�c��?���MF@9��?9�[69�[`c�ݴ�	�??�?1�; ^]m���&9<��ۯ@9�����Հޡjo�j��&8�ȀՁ=��&��I�ہ��&S��<k�u��{��>���j��-��{�F�hoۇ�c쁥>���>�����ޡj��V@9��ieF�h��{e��c��hx�飬4���>�-�58���x�Y�h���&�ޥ>"��hL=�h���&Ě�h�oe'�c�^�?��Ģ���p�?�Dma�h����Ě�h!.�>+��/����T@9��?�vfѧ�h]"�cÄId��-
�U-
�?f��h��)>�kI���5�A�d���^�i��ie�^�`��k*��&����A�k���6��o/�`��#���&}��鲼�h���&����OG�/?����ch�$����O*�c�uUӏ��8�h�ݴ���i�kE
�C��&w�/�t����ɪ��-)�k+��Y���h�~��Ҫ"ǘ5���ty;���^�`߁)�%�h�i}�ꭰ;�/��Ģ>�cA�ie�^�h��F9��kr��<
	�iQ��6Y-�^�/rףh����c�Y-�	�{e���!�V<��h|��Y-��h���v�f8|���ݴʈ։7�k����{��n����<�k"�f8u�	�%�)���I%�d���hπ�%/"�k�ܟ����ܒ^@���"�i�x�
�y���oo2�Y-Ɓ2�*�_9.�iA�k�^�/d��F�^�`UB�io2����&��>a��:ik6u�&�b�|w\$u6Y-�_�ϗ�b�)�[�X-����b��Bm5��w���[�,�|��_9�_�7��[���A�&ѕ9Ҁ��k���`��5�@������h�=�b��4G���h�4$uk�f8����W��b���{v��\��b��_99w�h�f8��Ё�eY�&�^$u���j�Y-�<�����x��4����w��j�`1�Y-.�Q�D5G�Y-3��jme�)���6Y-m�8��_9��?��a&meH�f�t{��^�&����A��j��c[��Zd�)!���A���j��I�[E/��t�_9@�բ�+�cף���|�}�hˁ͢�iv��.�
�g��b�)!��.��R���`G$u�(F���z��i��gi`1X���mew)!���(L�fǣB�
)����c
�͢�ImDC��_���z#�b��[m���z�ҧ>���i-އ-Z�[m���i�mer9��-�Gm8K)!#6�c��i"�dc���G���z�^�h�)�*����zz�hQ�t�c�� D�����iq��ףFm]?�h��.��F�h�:g|ԧ>8�/r�h�^me:�c.��-���i�:g��m�^�h��:g��c(��j�{�j��>U�i�͢���פާ>��c���<�Ҁ��h�)'��j��c�Md�����t��۟�z�[��Hd��i$��z��[m9�w0�)!�^{����z�"�hZ�ɣM��zR !:g=u���zQ�7�-@�s��i�%�Պ̌��,��ҍSFmD��4�����i�ާ>U7�x���nj�i��YmA��-	�'�އ-I��i�)��Ym���3�,�)�'i�ye=uc�Ym�^��[�,Ȁ�k)�h��f��[��`���n6]�'���kc�Ym���k�
 !j,�h���n���b��'7�Ymc�Ym�ɣ��dc�	{�����R�{=��՘�k�^�{o�$��Ym�}��Ym�[����.��]9y��k��`�^}k��#Ȁ�j�G�ߝ��D�\-�YmҀ�$�}�@�{�>�-�{ ��k�
��9�,�ެ>~�&�^�{e�"u��,]�-�ށk��͢�@��[/��'���K�"u?'�{�n�{�>gf�Cm=u�^�/��Ym�^}�[���-�7�x��Ym&�'��{D)Gm #�a�}�4gPd�x�g��mc	.�x��'ʁ�kMO�-9�Ym�n���爃�k��.�^�z�[�`S�-c�Ym��i�^I9��-c�Ym:���o*a:�n��i������^��{�8I9}�?z
�{�i���`�ށko�ik�z���`Ԁ�<�<5��,����^�?�^I9h	�i?Z�^�{�+}���c��ۼ^��m�x�S�)u5��Y��A���펫���ijrce�;u��+��?��)u3�id��	e��x�	�?v�-�	�/�!�iā��JZI9�O�/e�"uk{��^I9�)u>�dc��Z��ܒ[���dcŏ�L��������)u @�A2@9��a���>�dcD@����@@9��)u��W1�H��C��s������x<�ߣ�MmC�^I9��z���{�-t#a_��
�iނ�o��$��I9��@9��zk�WOه/|>�dc�����;��^�z{%:�^�?y�i�ޞ-w+�t��-�8I9��zq�v�X�i�^]m���-$��&�?��Ģ��)u�)'^�=����-��u\�o�^@9������{�����<:�o�?�k�^�`��ތ]m�6){�)'"�k�0�`a�;ufrc5�<�=�w��7]mor%�2�$����ޛk��mc���[���5˘Ă�c�Ωc/)�^��h�`��cW�=��;#F��h��c!��y��h���iX����	@9j��c�Km�ޑ9�)f�`Q��Uc���)�^@9�hy��h���{"�dcr�KmV��99���	%)�"D���{U��c��kjrc5��h�=���c�"D��Km����ޞ-���k`���L��-��͢s��d^"D���3�y���h�]m	ͩc�Ը���iꋛS��y˘Bm�����]mo��9��"|��`�[���k�BmӪ�z�"|�Zd�=�=�o��]m��kb�y������/)"��iƁ�֣1a�^�y���>���z�)�^�ybT �^-u��M�Ί�i.��3��G��'�ީc����ޛk��9��>���9{�Bm/�md��h��)$ۅ��>���ij��zk��9#�)�"D�6��ӷ�hu��9.�Bm��+!d����'���hm��i�����'Q�}�^-u�)
-u"��{9�2���}�*)û�}���?�^"D��M9|��{��Ù}��}��Bm�^"DQ�_9 �M9������{3!"Dq	-u��M9	�}y��?��g���M9�����{�����?�
���-��}��?ό�?Ĥ}��S��_9����v�Bm���{��}šM9&�M9��?�	}��Bm���z���Ԋ"|��{���i��M9q��-���iM�@m3o���?���iR-u��?�M9z�	}�o���|�_9�Zd���,�M9{��?��
�+�$���h[$g{(�2'p1a��m�G��.��@m�z� �ވk�&�):-u��vc�D9�"�Qe	o����ɚ�M9�^o�k�Y�@m�}/�m.��h/o�@�cm��bg f��{��hW���.),�_L�c��hȀ�&_��h��}�����M9��)D�^)!���?��h��M9_�M9P�g����׹)!��'&�h��_9t�M9��c )!��D9��_9�c�ޏ?�^�h��g�'xo�ik��?��e#�2'/$�c��:d����G�c���Y)!��)"��h�o��h.�vc�^�co�)b�h���c��$��2'��9��-?	�5
�c݃ɪ�����5�h��i��mB,�i�^o�iʫ���$Fmt��c1adE�^�i�4Fm��D9i�-�Fm��@m״�c�^�?m)Fm4�iT�i��D9�F�c��E��&D�?٪&Ds�E������U�cI
�:Fm��z�&D��&D�H�{�9��'3Er%+��z3��z�iHI�h��Ԁ�JT�1�&D��i�	)!.�{�)!\�-u<�c���z�^�h��&D�)!X�����i��i�)!=�g��������{�F�{��'��&Dh��z�!FmΆѻ�^�{��h�&Dy�b��z�����&D{�%��	`���c��z��&D<��i���Ct8
}/��z
Fme���L
�dY&Z�e��nĉGa�,=�N҇�vc�����1~�z��x	�5ad��^�i���]�m1�^�?��r	��ރ��8�i���x�7�?��X���2G�{��T��&D��&Dt#a9����i���z3����be���{Ӂ&D?��zK�̴�^�/|���kCة��z%u0��&D+��z�%�F�vc���z�&Dt��<{C���z
�&D��^��vc����yk��+��vc�1a�
}�ע���!�� u2{��
}a��-��ħ��wJ�d�V%������j�]��`�� u̒�-�{
�p;-D<6'�#�_�{���9_�/j� uO
}�Ҫ��_�jS��`cDm_oeJ��&�,>���`r�+f��9ƁS�����l��`k�5a���y���8%��{�5a�d��vc)��A�i���i!�Ea�h��vck������lo�5a�끩xK�hoe�:I9m��`M��x�=�c��E��
�� D�ߪ2{�&�bk�5a���f�)���%I9��:�tգoe��ݨ�ۢ���lI9o�i I9�hd���	�mvд&�'�{}v��/_�c����fe�{e_I9�����5a_�k+@I9�{�=����]�h���wv��&s{���z�ɁdF���@��nW4�'Q��(����Ap:)����g� Z�=5�c������4G���k���Jj��9��"	_�c��I)���
_I9Z*�%I��*�'I9ÄF9���
�ۢv�ɣ��F9�kЁ5a��-p�c�_��_�'�)���a��I9.�z?��>_I9oܪa`c�<r+I9
_�h��F9����g`�k����t�h��z�0�x���aI�nv֣��&"�)w�����+�{e�k1��yX�%�&���d�^�1aY�Km^��a��U/��d���<3�EW2{�����d�Q����o��b,��<������Km�)4�.�
W킂�<��<�裤��-�<�Km9�2�_����Km�ע�	)��F�_�z�Km�#g*�zg�c�1a_�'�ۈ�`��
�H�$�֟#gX�L�����#g
�´���c���,���<�8a
�i��10�ƩԀ�Z�OҺ�Kmj�
�À��?�=$u���2��2�#g<�����ir
��#gO����u%�)�-e�>;�#g��j��/=�}�}_�-���/�j_�i�#g�	�i��KmE�}��6]m>�5a���H׫Kmރ{�$R-$uh�#g�}�T�>_�-,��<hT�i�R�>�5a_�-,��<�з��+Ł�<˶}����} _�-Ł�<����S�Km?�!_�>F�Km"_�>R-s�=�-#�Kmh�},��<��#g����o�}c���(���NJIm�-̈́[m�?�mc$��/�r%��>��7n�#g�$uL)�;�iv�<���%�M9{�jO�?@�)G���:g��S�z��+�C���i� �%:��9&_�>ҍ��N��F'_=9�-~�Im��?l�`e&�i�����M9֣(_�>:�a�>)�M9w٥ D��n}�M9N���)_����h�}*�}+�M9�)'4�5a� !����5�h|),_)>=e�Tta�z��5ae���ʑ�-e�M9S�F�*��%�������:g()��-��-9�{}��eq{�	۲��y2)��c��<a-_)��?�ۢ��-g�i�֣�y�)._�a�Zd��v�cQ8��>��M9/�Im���,��?o�ۣj)��>X�u\� �=V����2���>|�M90_)�����U��M91�M9k)���z*=��T]2�Im����'g9�M9>� Ă2����3�M9s��c״ke+��m�Ym�;�-^��y�c)�)m�q�ke4ߛk���5߾>[��o��J�)	
;O��c7��>���~8)	�h�6��`7�oD��c{8a8�ox�z !9_)j�U):��i�!g;_)�	�乀�`2�j��{y��kOj>۴<_}��k�%��i������y�Om��`=ߩc��{>��h�<a?_}���+�/D��&�o��:��)kr%�)@_Om�o�5aOmA_}B߾>�<a�'}���>�%)�oC_Om��5aD�`M�Bd��ЁIЁ�HB�<avU}w�5�ke%8a`V}hעA�o
�n?	r�E_Mm��o��c�}�
1a{�f��iF��iGߜ-y��k7�/D%��/��/DD�5a��>X5H�ke��5a���`s�c���x̜-�|%b-MmI��iB�o���r%Q��iJ_�&�5a��oKߩc���y��+L_}�1aM_Mm��o��<a@褙i6���Z�N�oO�`�5�xP�oQ_}ބR�_.�&e�<a� ��R_MmS_}3
�x���aRҥ��%�_A}h��?T_�x*��N������2{���xg��X�Q�{��Jt�n�vcߌ��{
����� �&e�5aU_�{*�����y*��i?��5a%��/���`��
�� ��0ѧ�c�1aI�{Q��`Ԃ�zܚ�-�V%V�`��c��fk�Mm�,:�$��c"MmR��/�{¾�{J ��l�{��x����À�݅�;Wߜ-��f������vc��ꁴ�&1a�EMm4@9
�&J�ͣJ�coe�ٴk{����X_�ceNҫ��	�)ux�& Y_]mF]mZ_oe��܎@9;���.)SR-���i	��h�c�ٴ�+![_oe�����i�)u�V\_�{	)�c�`�#�c��i݀�y]_�'(4+!��93���^_�c��5z5�i�)J+!+��b__�'`_�i��h�/�'a�O9$=�Xo )����������poeb_@9-?��oe����c_�'*
)R+!w��>�'
���@9�<ao=���bg�'�F��#֣	��h6�iY-�z���&�'d_�'o��e_�`!�Y�9oeD>�z7��h����l8oe>�bf߻c�V%�?�=Q�`*��J����]m�B�i�o�<�<a�=�`�FdF�`�h*8a���ٴ%�<ao��+�Ǡcg_�z� R-h_oe���i�O�c�i��z��i_�ij_oe�Cxړ�`@�ik_�?@;8a���?l_�h��'�+!m_�'c�i(��+!n_�{��{�O�zo_�'A)Ё5a��'p_�{	�����'�������c����&�y%���_�դ���s���3�zd�����{q�Bmr_�z��i����h��
 s_�{{�5at_�`'�z���	}���[�zP�z�1a=����n-uu_�z��<v_�i��9�V�.��f�>�`�-�z�vcw�}�%�zx�_9y_�{��&�z��5a��iT��(��f8z_�`���%��cȃ6�D�`{_�?<���v��&>@���_9|��&��	}��?U�5a�1�}��&
I���(��5�<k�5a�w�l�}��_9�R-�[�	��b�@m-uN�j�]�4G�de聩x����QW�	}��ֺ1a]���o�<r����z~��&X�W)h�ۊ��km�۲6�{��o����@mR��k�+h
	���_�{Q����y�gF�ң���i,����@m�5a�֣���M��x��	}�r1�	`1��`��5az�b��/���&.��i�@��.g+R-�i��@m�)!�)Y�i����x��vc�)���&=�h�_)!���3�_)��_9=��ˏ9���ݺ�ۢ��x��
ܒԙ+��i��{�����&�_)!j�Rp�)!������v�՘��_uC�
)J��'g)���ĉ�i�
)߁�`ƀ�'8��{��'n)�.gyV`1b��'o��_B��$5��o�_�&@��2)���'���'�_�c�ߚk��m��i���'���^�)�_�c&��`�#�'��)� DH��-��'�ah��'���i��i*N)���`x��S�S�Ȱ�'
��kj��w=��wM��iӃۍ_�'���'>儙5a�'�+!�c�_�'�7���'��&D�E�c}�&D�c+()�	Fm9��O�g��5a�+��co��z�bR_�hd4@��'�
)!���g,
}�_9D�̦R8�BZ
}�+9D;ߣ��'I��'��be��'�R-�
}�>9D���'�_�&�C
}���z܈�{MW�y���+�y�_8a���{HC
}���	,�<dF�x?�
)!�Ym��˸��'b�Ym�_9Dǭ����|%�
}	�j�5a�ߜ?)�YmV�c��5.Fm!�c�)JV�$ 9D�Fm��Ym����a��Yml�5a��şC2��'�_�c�+�

}���VmDm�_)���a�<a��`�4�'T��+F
}�)�^Dm��Ym���y
}{�5a�&DX9�iBO�'�@�	�&D�o��F'_��Ym�ߜ?^Dmby&8
=�_�i	��?��O���;���݂[9D�;'�WDm����ip��LDm�!��_9D����9D^Dm뀈b��<e�5a��y�
}Q�i�_9D�H�y�0
}�Ym4_
})?De�<a�_9D�]Dm��xk��b������+i�}�_�x[�x��$��Ym���b���x��Ym9�S������<a�18a߂c��Ym�I���,�{sI9��Ym�Ģx���/w��ǒ�+��-P�%ikI9��9|X���J֣I���$&�{eG��?�Q��_Dm���-�ww�_DmU�g2�fM<��#8,Z�]/ l��/��i��^,�ie�-!�I9��)�D�`�����<Y��a��)��y~��c�]m����>�i�I9v�io7�)��iϒ)���ܢ��a�i���&S�`%�i8�I9�_I95��-��)b��-��}���Y���"|���&΀`8�hd�ƌ�)����8�<a>�����-��?!k{��_@9�֊s�)d��h�C�_�ao�fS��&L�ધ�)���bh-�_33:��x���&��z�_�%�9�a�"�kr%�<a��)��F9��)j��ݐ�&fI9�ǘ�_�{��&��<�{��_@9]mX����<a�I��_]m��&��<aB Z=#�)���+l�ie��&��<a��)�_@9��&��)S�)���(�`۱��&9��ݐ�&Z�<���E��a��ieKkj��#Ї)4� �_]m�%���&�����_�`g���-!��c�0]m�)�I]m.��c��`�	 ?�L�d�.��`��tQ���Հ����&y�%P]m��%d�^}2@9�C]m�߁b
��y�`�$u	}�_@95g��+�����"F2	�W�a?�Z�7)�'���c��&�_}���� ��c��&F�{�Fk��g���c���&��'ݐ�&�
]m��)���h��+'=���c�}��&Ą)�'��)@�`w��:o�&(��&�_]m|{�v��+��&�_�'Ä۬�&��@����@�`w�:p��&�A]m�_�'�&�`�_]m:�il�~#��o���r�&}6���_�`��X���,��U7�`Z+�KkÀY���c3�&�Kmhm���c���c�_�'=�����c��%H��c=���>��A!�=��z���뎊�c���h��g�&=���ʩc���b��^迆�w��ho����q�}$u���ho����+N�'������m��+Q)�o��h��/�<a��_9�	me2�f��_9�_}��M9�}��,��%��J��i�_}'��+�:�'�_�&o����!
����`�y�_�'�Im��%�Z+����_9t{�탍$ӶImk�ffņLo���2'�C�M9A�Im��&�Bml��$9�	�<�@�-����Imi
��I�Im��M9��@h	�����ۣ��`����me	���'�5���-�i��+��Ν`1Հ��M9��_9��n�����B�i����զဥ��+f��#~K��3���<a�meb�hr����.
X�Zme!
���9}e��,d'�_}e�
�>���4�Im��Imo���?E�c��c"�_9�	����io����_9H�X�	�'k��$�9!	�_9��'Z��+.�)D�+5�[m5&v���_9��'��h�9}e��8g�_9�_�,�ic�'�ke0*�b&���cף�_�'��ke�i �c�֟�'��1�-�'�_�c{�&���-�PkU�bo��<4}eZ��%>Z�hX��U�`80/�c�ش��
 0'��_ D���ܣ}	��X}�߻c�b�Av:���	�'�[Fm��蒾7)	Fm��D�m{���`�ke��{���c<����4}e��o�_ D����M�c�8g. De��D�o�1�i��c'DQW��_ D�_}e�_�cn1�'�_�'z3 DoC��c� �c�_}eN D�@����)�i�_ D	FmD$ D�	�D�+T����_�'>�b
X�_�i�Mm�i�_ D����_�'���+�_�h΂����� ��c�&MmX1�'_%�'����be���yw����F����&J��?��mPk��	�k{��	��`A�`w���f�Ok�����::���4�<g��$5��cF���=}�_Mm<��<�Ok���[�)uMm����j� DZش@�Mm�Pk	Fm�;u�Kk9�����;u�܉y�����Mm&��&�����m1D���Mma�a�_ D����_�`&�}��)u ��lMm��`��;u��+Pk$,��c��%����/�D�;u���"�2o=�@�k��b�m1O��y�@�3��U�ۧ+���0��m1y<g���"�Ok�<g|��6b��a�Mm��h���<�&K	$MmS<g��y��j��f����i�����M��c���i�Mm���{���&)�<g��iy��cń�-DK/;Ѱ��i�MMm��^NٸPkv���+��;u��)u��;u���o=B��
�;u�}�_�%��+v��#m.Pk�oe.��{�}�_oeb)�����c���.)�):�i#.� 98K�I9=�V�)�x-�_)��-,�$����_)soe���k��'��$8��+�"f�՚�b�����c��-��*���:"�f�I9�,�+!9�:�c���K)�)�۫��ی�'�5ak��lm8�������&�\�%���']�����_fe�8�-�)�_Fd⃨c�5I9�_I9<%:�_)9�A7�`f�1}�ל{��i;/�`ی�'���)9}���ߨc�1)%�z���c,��i���c���'�Y�%S3�-̈��ѽ$D}=�0�Y�$ӣ�W$|5�M�
	�)���Ԃ�܈$D�)5�M���F������)�}a�a+�y_��_�-	��$|�����$D����F�e=��'ٸ�I9�z�_I9�)�}*{�])�	�&h�?��'�#}4�Y��$DNh�w}S-uY�t�F��S�M�yՑB�]fes�Bp��{S�iB
�V�Pk���'R*}�����-u�[�-�_�'z���A�'��}e��'/-u��'��k��}Z<feD�%
Fd��}��d��_�-���'��}��y�_�-t�}���k�_�'� �'�y��۱
�-:}�߃k4&�_�'g��GgsCh���k���k:����y0�P�}8Ѓk���_�'����$DJ��i����}���?��iD��m��k����Ӆ'#�O���i.g뀼�:���.�}N��`�_oЊ@mH��k�����%�}����#!deg��k�����i!դ�ж����'�g|o��@m� ���7�6�R@��6��@m5�+��%���:�'��@m��@m�_�D�Pk��k>o�E�Ӂ�k��yd[`�'G�}@�"|��>+�g#v��kJ�@m�@ma��h�kH)�i`o�����+��Ok3o���h`�{	�@mZӣ�ħ=B�@m���j�@m�k`oi1
���k�g#��TkQ�9����@ml���v�هB��
F�r�{�
�iD�mH�������-\�����߁�'{�)�����i�)�) 7�{=��� ��T�)|�)_�M9��@m	�'=�)Ł�$
�@m<�Ok�)����@ƭ%H��l� W�M9]�"|�(o瀭%ˆ@mĀ��������?�%�+#�"|.)�@m���+
�A'b,B9
�?e�ێG !������%` ����L�)h-$��ƭ%f��?`�&d���s{��!5	@dQ�{����)�A+����{�o���gQ��wj�Ok�耗�_�1���t�)��C� ��iԂi8�R�`�i�[������i1)����
z��+�%Y2��i�%X���`�i� `)D� �}��)w��%.�&��i�)�٥,����ۭ%=�w�`)DÀx-`)D*��m�'k�	`d��?��ke��M9��L�ic=u��M9�)|�")D
�Kdy�'f,�{G)D?���!n���?ӏp��	}����Z�i`)Dޭ�`����V�ke��)_��řH)Di��&)D�֒A
Ɛ�Fp�ke}e@)`)D` .�	}�C��'R��'` <�&�
g�
)D��&�{�o���J�{�	R�tKk `�'!��'j�	}J��ieVmv��'��'ӑ)|	�"�Ym���&�#`�'����$��'����2�'aVm��&�H)D��:�'j��'�%`)Dd�u���ʢ���iw�C���i��&D�Pk&�ke�)DN�&DH��h������ܹ}Gg�'`)D:�&D�CDmv;�]s��(`�`�#�&�P�a��&����+g�x-)�Ym*`�i�&Drӣvˢ��'�Dm+`�',`�&��)D���/8Q�'	�g��Ym�/�&T��D�i!5�i�%A�%��?	�Ʈ��'-�	}���ie��' ��`.`�&���'�&{� u33��"��{�����VV	�'��'�'V���ylD
�p�ie�C�fo��+��a9�m1�
Ex�&��&D��/`��0`�'1`�&�12`�&4Ca�'A$&DV�'	�L��y��ie�7�'�G�&=�����֎&Do�۾�������ln*�u0��m�� u9����-���mJڣ�m1� 6�_�t�m�{eZ�y,أ�@9R�%o�d3``8����%�ۈ��ՀV��[����V
G�4�m1�`8�eӣՀ�X5`�%��y�F9j�f�`8��Ԗ�定�&6�F9��m7��&�%�
z%�F9i�F98`oe��&� 
}9`oe���&��-:`�&���m;`�&&"!`-��<`oe����4�-=��z��&>`
}?`�����oe��N�ީ(uI��9
}@`oe�F9A`-|B�F9�Xoe��W�_>��w���C�F9�(
}ӑ-DD��&�	�Ȁ��E`oe��En,
��%
�w׻cF`oe�����&���f3��t��c���&��cxt
}�I�/]m� Z��)�-D��h*oe���<��#gG�-D
��*��H` D��#g�$ueX�M�#g:�-Db'���g�#g	�-DS�b	�I�F9,�-D���%LT$u߄F9�-DJ`oe[�`K�-D���&�F9_�x-L` Dϊ�l�-D[��&M�#g�洷�-D�oe�oe�h��#g97N��&O`oe=�#g���P��&+�`r���Q�BmR`oeS��&���m�ުT`oeU��hh-|��mV` DN($uik�W�?I�p8�=>-|��L;me�-|X�#g�,�'��Ng��hY�-D$u���c���{��-D�%gZ�-D�Č?Ą�&��mv@���������mG�-D"me��zw��ge�}*�V=mem�&|��-D*|bh�`����k�`[�-DT�#gʃ[m���meX�`�[m\�#g]` DI��&��[m^�}�g27�`�&_�#g�$u\�`z���
 D�̌�me���>��d{�i�`�=�m� DI<�b�g!�g��ش D����F�r�w�`���e��\�`9�zm�_9�ʜ/�[mo��L�`���`�_9��[m	�Bm�)����g�[mL���I��?�<��ǫ��Ό�[m�ۚ��?��Im���h��ʴ���6a�?ŋ�>����8�#��-t��瘁�֚�
zb�_9����8�#���nc`�`��5���v�-o� �g�/m����-|�`���n*ۇ4b�G�9�{��me�ڣ�ո�`����+0�W<�3}eˁ�dI�[mi�[m�Zm3�����Ok>��ց��u\���id'EJ��+8Pk)5��:���	 <��oނ��Ԭ_9�T"�)!�K9i'gϜ��	 ��_9��{�
?�Ą�-_?��-��_9�4�{���zd�_9e`'g�	 uˢ��a*��&f`)!��&b�TkÀ
f)��H)!���^^�-��m~�����3�g��z��)o�-j��ah`�b�Ii`'g�й��zj`$|Fm$�x->2D�'g�W[�QK9�2D3�#nT�+D�ʴ9�hr��z���|��lk`�h����$|�j�}l`�a��o��{�,��()��h���-m��z�������sL�hw�V����n`�a�{o`$|9�61�{4��{~���&2Dp`�a��{�hoڣ��C�%�o�Q��(���{��$D�2D�P$||�hM��{q��zr`�h���)s`}t��z_[��2Du`	 R+}��b����$ 	 Q����v`�h[�{��.'goӣw`'gx�$D׀u�w��{�����W�'s)�'�Aj{�y`}Q�ҷ��4d�$D}�'m\�'�}���{(�ʢ7U�'mPk	�	�z�Ymu�{a2}���m���{`�'���ze&j��R��z��d�MmA�Ym��a�Tk�.�h��-9�h���i|�Ym� }:��`����@d�h���i��'A�Ym}`�h~�Ym�]md����{Q۪�=�'��$D�G�y�'���{�����=����s{�\'��i��be�`}ょ�p��z��)u��iV���{[�ك�is"D���zf6}�$DY�$|�}��"|���z�`}\�&�����m��i��}:��c��'�
z�
��(寂�i�/�'���y$[���@oV�`&}�1��h��9(�"|�<g&���w��c�/DmPkh��9��}�=�'�Nʵ�-�VН�z��ΨH�
�Ym���zÄ/D�
z @�:��c�Ym�VЊ�"|1��b���`�z��i���z�4�@;i8�����z�4@�*���n_(+!�`i8X�"|�z��i�%���c��zw�9S�ʢI����yi+! �`�%Àx-��
8'n��Odĉ�cL�����z��i8$�zh��+!	��'��
 E@+!h��9��%*��hw���
 EHF��5�zX���8D�6+!9�{�'+!3{�ji8ˋ�c#+!:��cg�"|	��'o�ݣ���e��a|�����g$@����a���bϖ�+!��c����#�	�����T-��b����s����m��)���c�}��[҆3GI���'i8�Wȃ{kl���Z�{��m*�M�H�z�`+!��{���'a�i�`+!�Z�{Vx�2{���1�T-�`i8̞?�Gfed��6+!�I9h	�zb�l�`)D���Ii8�p���:�:s�`+!ÀY�`]mc��mDi8Ɓ{���	�R�Y�,-u�zd�	B��iJ�d��=������iS�)��=)DS�zAA�i��y��	}��i��ze��ܗ�)|+�Y��
 ��z�`)D]�
 �O9�
�Kb%�c�\�
 3���=�S���.���`���c/uЎ�'���`�`�i)D��'��!g��]k��i��fG]m\�����'�vC:g�����	}��'��Yd�F2?�v'��)|��gĂ�ht��'�
F�B���	}��^�)|�{M뀼�v�4S%�!g���'���k��!g��)|8
=pf����h�`�c:��m-��πˣ��h�涄�@m�
�fb%�"u�de��I��>��fl���)�i���=��3��']m�������ɫ�&Dd�i��`)D���=	�`��Yd��i����)DZ&���z(��)�`)Dz�`�`�z�!@���&D4ڣl�*n^@�i���z#�W������S���`|ډ��)|o�YdȀ���E��Xp)�Yd�@m��x��`���g)������'x�&Dl�	}��)|k����g�
����V��'*�9��'��fʈq�z��'l��>��,h�!g腩�P��'D��Q�����!g��f�'Eo����M9�`�h崛��_�	@ڣ�����-o�b�_9��M93*�ac�b��m8N�?F�_9U�b���o�m8v�௩M9t�_9��m*&vB��c�&Dd�Z���a��M9
���O��m8��&D��`��,��_9gfׯ�z���%t�F�@mg�4�M9�W�W�瀝� یzwa��98�� ʁM9��VÀY��zwD{�5��`*ۀ۫��i���Ȁ݀�Էi��i��i� ����'��i=� ���z�	+���i���2C�Ʃ�z�`}e�㴯`�-a� �`�-���iC1-|@(}e�� 9]dރ�ϙ����-�� 4}e��p�A� �&�h�=����N6}e�&�huɵ�� �8}eʕM9�
}ј�ճ�m8�B�h�
}�`	) b%�-�}e�M9��M9q�_9�`
}���ie�m8�`-|�`�h��M9�}eX�zz�`]d	 ��}��{��h��i����fGKm��z��8}e�{@�}	��i�-D��;| f�`�h�i��-D�	�mDm�Dm���{�Y�@���-D�Vm9��f���i{�۽`Vm������i�Vm��i�@�Ykc� f�}e�` D�}eh! D��z��ތ&��" D�`Vm��-�������ف��9}e�� X�;Te��{�`}e����	 Dd��ig��`�i�-"�]kI1}e-WVm[�C��`
}�`}e�`�h�`]d*�i D}e�i9�{��-|�}eB DtkcѸ�&�h�`-|y�zwf��-D�`�h�ـA�ke��bek��c��-D�-Dj�&�U�9�@��w���Q��{��j" D-�-D&��1���}$��'�ˁ�kc�N>i�-Dک}���0�'Q�-D��;|��}��i�BVm�-��i4�p���`�i)���{��'@ـz�i�&��i�Vm�33J�5�=3uC2�����yی�i�ˣ��ieEVm���< D����z�Dm�`�i��F=���������Y�$|��߁�y.� �m1�Ad=��Ȁ��ږ:��h��͓���be��y���˜�G�be��i���l����ኃ).�j�g���8&!�k���m�`�k�7`8��%b%Zkc&�����%��莃�y�:���
����i6�m1��0ј_�������`�kd�c���L��yD�F9������-���L4���~�4R�f������&��kt״�oe�	�Ʊ���(g��i�k��&���Z��yծie_�t���_�S���{���ikc���+�(gk�F9o�(g�<:�)�:��W�f��{�z=�{e
�k�`�?��2��9D�����iڏ���<��?D��y�m1�)pco�?D1�m��k���-g�?��i��{�`�k	��j7�{�
�BpcH��i$�&1�-�)pcW�{��-$��
F9�G�^�?D��)�`�-���-��?D�$	 �`�-��`�{gO$|��?De��{n`8�f8i��<�Y���	 c�)���`�h���<��'Äۚ����hʇ?D/����-l�Y�pckc�`�h�?Dpc�?�iT�`�h4��)�`�i՘@d���]�iQ�۪��-��(gɄF9�pcc� ,^�@d��Ҙ8pcR��Z+YU�i���=���Y9�Y9Tg��2�$|�?�	 l�$D��&T�����iL0^]�?i��<���?��)�"�i؁�{�	 ��@d@<pcr�?D��@d�E�&脞?��/؝-�V��	$w��|�`me���-��B��me��<���5��-ŋoc��?DR��`�yѵ?DR�-dpc��Im�me�'�y=����e��/D�me���� 52D6�[m���r�<�O��
�.�#�-
�憀$|�`�i֔i6����3�iȀ�o�-��z;����k��
mpc�%8h(��26���)n��i��k�#�h$mej��k���/�pc�$D����aR�i�<� �'w�O�mpc��k߁�i��k���/����-|:��?B��.{�C\۱48��C펀�/G\
�$D+��&��„����k��ˆ�/	�i|���ڴ�/�֞?	�V���{����t����z���{�@�{�n�:�-+����/À���V����=�������{��k�i��/D@�y��F�p�,�/D�
�ۤImm�:g���LJbcd����k�2�?�*���
�F��k�դv�Х�����{�\,�8g(�/D��Î��n�����{L�N�_9!���/���kӃ&o����{х�k+�Y�o
���kc��ԣ���{�����\��/>�z�� |���F"@�����/��9�A��-ΆǢb��/��z��)���B��?���s���A΂8g������?�����\�`$ui�ˣ�zq�-!��n��i1*���0��{��i��i��Dd���a��{�g��9�;/�h�	
ٟ@�(���;/�h�6|�@����{F����?��8g=��d������Y]��g��{@���&>A$u@�Y�j��{�6|�Dd~���$u+���?�����`�h��?y�h�i����kc�Yۯ�;$u��e_��@�=S����z�X� Dd�`�h�)jWMm	Dd&i1�� �`)D)���2��D�i@ۯh���h9!�Mme=uҀ�?�����-�`)D7����̪����o��Ёl�l�]9���d��-��<Mm�8S��
 eMmo��o/u�W)D��&��tcJ�գ�`Mm6�)|o� ��;u�)Dv�9I�����	 �)D9�V�`Dd���h��
 ��.�S�;u��c��)|s�Cv�`)Dx��iX�ҫVȃ˭��cҀ<��DdFU��cH�/��i��A��i�c
�hJۯ6�)|�܁�c�	 ��i�;u�#�Y���|�Y��;u׀����h5��c�/ug�<����-��;u��i��)���=<g�;u0��h��ٽ�%�� <gB�aq��4��i;�;u���c�X)D�/u��ij��>��i���yz��i�))�Bd[$����a�XĆ��{ǥ/��iքocQ��_a�ܫ��^��k蘣ca	 y��i+*�	�c��
a�i��%��hpco�e	 <pc]��
<gp�O#�`e��c��[�pc�pc�ch�`e��i_��i	�
�-,�Y��c���iai8�&(���L٣c�>�V	����h��`��i�Wi8Z&��%�����h�T/Ȓ99����%�Z헥�Q����0J�kϊOd:�<��	�܋ǥ����=���=��P*A�ۯ-���pc+�<�&Q%<g�?!ei8���)���/�`eV�`eO�oc	%&���a<g��F�����iX�Bd�&�3i8��a<g�-ߤBd�@9�Bda�{a@9j&�ԣx��`e���z�����d��n����C+�"�tc_�%�G}a�e97D�J&�{��?!��?��d���P�{�pct��i+@9P-}8� E��%X�%W�{KF��%�5�pc��+�?5!��e]	�.@��@B�{{��a-|���&�?!ai8c�m���,ni8��cQZJ��v
�{��cL�x{��=G�hN��y�'=����c��)���� a�-���y����!a-|�%ؚ���>|��-"a�c#�#&�-D/��y��;|h�cǭ {�Id�4�-h�c�"�m�
��w�ې�{��}��hD�}�4�i#�-D��c �'fۯ:�)u��<��$���5�$�-��h��{Ch
�i�-D��c���|���@9%a�h&�-D'a�z����;|(�IdQ��/�cwxFa)�Id*a�i8��%L�h]�Id+�
 ,a�i-a�-��
 .�-D�tc/�Id�Id$�z��y�
 ��Id0a�zo���]�-Yh�1�@m/�cw2a�-3�@m���-4a�zmpcϷ@m �z ��h5�Bm!�i!�@m8�i=�-6�@m��7a�zEE�i��.�cWR�i��C8�@m
�)�cm�id�ޟ/�c�"|;B�'�F�z9a�h��}�%�i$� ��@m)���`�':�Id#�';�)��NjE��ْ<a9De�t�)�
 �:����:���"|��=�@m>��'>�@m?a�zY�y@�'}�"|��hA�z�������1�@m*��E�
 (�@m����A�'��ݸ����)y�@mB�
 Ca�zo
_a�eDKd�=���?2��*�)o�z����/�	��ԣ��)!���Da�i��i��E�m8���u&�zl*�)t�_9Fa�iG�@m�pc^g�m8(�"|1�@m熿%r6���e#�j��X$<���W�@m��j��Ɏm8H�@mY��?B9I��'eCz�����a�pc�ޒ��a8pc��+�e����܌� S�tJ�?���h���*�tc���zV�ơK�?���_�9D�9�A{����L��iq��)��zaCzM�z(���yS��}�ْB��iҀ���	�?�ԣ���i�Y��s�yތ�zMd�	�y"�-u/�X��yN��i��OaMd�B9^.�?f?�-���i9��zP�e#��h�zƓ�i��f���|��z��pc������+!���iQaMd���?p��P�	}�gw�}e끏-��?D��i�)-uZ��iVMd���>�-u�pcg��-��z\��)��i´�z��ĉ}c��--u|��(��zRaMd���'�	}]C�jMd��?DS�	}�-u��-�f�)Vms�B#�	}��iVm��G���zā!T�?D��CL�zU�{eCzDmV��iDmW�z+EkcX��i_ۯ~��	�W)c��'�#~Y�?Dz�&���zZa)��	}��-[�C\�z������]aVm2�&D��i���#�&D^aMd�}e�	Vm`�&D�KMdq�be_aDm`�&D��F�)b�� �&D�Md޹�-�#}e��&D�)!(Mdl#a�&DMdDmba)����G���-8�&DA�&DL��-��'�I����-�i�kcq�˕X:���܇���{"��-Nv��&D��aɈ�-���g��%���cc�&D&�?D�#�d�@d��?De�	}y�&Dܛ��&Df�-B��i�����'DmpcE\�h_�T/R��)�Vmܷ�'9������
��Ɓ�z���i�Dm�
�杁�-� 輎�i2���Dm�˪��}c�&DD�ԣVm��ਜ਼&DW��i�h�&Dw�8ga�hg�oha�h
����i��&D=7)	�{e��i��&D�Z�b�!m�tc��&D�=�i��o��zi�E+S�oja�i���X{���Ԃoka�i�&D��z��
�h�{em��ml�om�}�cw��C��%ց�<���ii��i���bna�ir��Q����<o�}@��U>�h������i�
��:�F9��oc�X�'pa��hA�i����
ۢ�:g��-���1��̣�
4@׀F9���R��ie,���	�ܹ���{��
���#�h@�o�f�z��=�`��	�:gN��8�fb�!Q�Y�fk�e��o�f
}qaI9��`
}�}�pc��b�0����i���{T
}耉7j�:g���i��mS�z8Ѥc7��:	�(�{�4Z�҇4��*I9�{��:g��c
}raI9W�f*�{Z�f��{w�oc'#s�)�	�{f�octa�{��<>ۈ��J�o:����+uaDd��oca���}��oc>���k��mۦY9tݣy�{y��v��<׀F9���<�Y�h��Cv&�3Sw�F9pcz�:g��P����pcA=U> !Q���ݣ�:g��tc�}mpc��fЁL>�pc5�F9��CvjFDdx�f��}bZ+x�:g��)��2ya�hz�)n�f���}(�H��pc 	Dd�p�w !��{b�/d��>I9����${�)��H�=u|aI9��)�H��pc�h��)p�>�v*ރ��.�H�9ɷ���|���Y2{��)D��bo�+D�mb�H�뀞?��}��)[��O�)D%��e����Ӆe����`��Ā[�Km}a6|�̣9��0A��:�)���Q)Èmet��a�+|��ocJ�~��a�i	��h҆����a�����i�o��a���XCa����7}΋�a�'�hϔ}�D}��)��[m�pc��)���aW	 m{�!����*���iH2�I���a(�Im,���4S}�=u��Hء�H�W�-��`�⁆��a9W�5���P}@6�b���d���i=u��a0�)e�"u$�bm]��a���>@�s���%)?�"u]��{���h!��{=u��a�y��-b��iA��{7@���a���{)�m�)Wf���9����a}��ib4���=���{vs�]3	 ��mm?�,�=�����fထm�a�yk��{��u\��+|���aߚE��E�!��{��{S�m�^}�����{����j����aБU Qഉ��a���'�I���{r�q�8!}7��aY��{�Uv��me}�˥>&������#H���='��{���8��A}�a)��M9���ȍ�M9�a)l�'�6}�(�yh�{a��i�
2ńӪ�p�<��o���M9	�۳ �y&I}���K)��o�a)L
}��?!���k��M9J�
ۤ}>�=��{�
�.�M9Q��/@9`6}Q�f���{���y5Uv�ˋ�)��M9��S2���{��5$��_����C���x�"�M9�@9֖o�}w�f�*%�m��o��Q���}��Fa� }�@9��mJ�&AK9��o��xө��}:���d�4�z;�-}��y�3}m�=À�ܖ�oe�-�a_m�!U���8
Uv�a}��-�M�	%@9�a�-�m�$�5[�- @9�y���m'�բ�M9�
�h���[X�-�|D]�a�a�i��i��o{�	�a��o��M9�'�a��tʽ�o2I}�a�-��?s�i�a}g�xӹ_mw�q�7�i�1�-`7}K�i���x���cЁ�}�Q@9�a}b�/D���
㭯}f?�i���h�a}�a�i��偢a�-ۅId�a�-�D�i&��q���!n9@9C�
 �"D��i��}�a�h��;|��{�@9�{K�i	����
���n3"D@I�{@��hȻ�k��f��l�;|�'�-�"D"۴"�/D��tc
kc��{�
"D�f�a"D�N�s���`e�a�-�"D�?�a�{��"|����a�-�a�c;DP�a�-"D��{�Y�-�a�i3�{5��h�'�-I"�i��	s�{
���{����}c�;D������Y�.�a�i�a"D��Bm ���o�x�a�{��R	N�&��P�O5�����+�ܝ��#Gq���'��)�7�"||�)#:��y���+�?����3"D��/DD�%,��Eg	"D���'t�
 ���I��w|d�Idw��h��}��l�%V�YGj�9�f�]6�{R�
 �"DR�`8f�}b�
 V# �!nr&��f���h"D�G�j{�}�"D7������ї"D�x��O9����+��Q�U�[�{��}��o��"|�N	��w�ݥ��od���?�6<�+&�a�?��TlU�F��Կ�j�\�xӶʦ�W�*��[�ۀ��m�荸}	�&�LR9�m1kyc��f���O9A��,�"|97����xӈ�%�,��,����bP�j���c���	��h����p��(�}������,��yJ�̣���as�E���܁x�щ��)��}݇�c3`�?���aᎸc!eA�k�6�&�}��aMd��^����}��ߊo=E��%��ٶރ�>��������}�$�x�z��c��.-u]��y�}���y���)!†�x��e��5��-��bS��yV��c�t#�-�t����Y�	}T
�O9��[d�?Dr�	}����Θ	}��+��9�;u���	}��?DƁ�����[����%N��-�!�i�	}o�-�7)!΁���g��de��?D��;u&K�aFm�F�>deE�	}3�@���̔[d��y�	}o��)ԟ�c��㱋�a��[d�aFm=����?���<V��c��	}�?D%Fm��	}s�E������[d+��cj��a�S�R�i��E��-uɎ�a�	}m<�ܖX�il�a��?DDve=�
)@�x�;�&D�a�iK�'3&|��-��xӘ�	�i=Fm��?��`������?��i�aMd��@d�Md�a}���a��&DF�&DQޞ?��	}52?J�i�)5��-�@�n(�֣}T�&D	�e��`��&Do��x���-ZFm�	}\'���-�О?��-�)o�5	�7�&Dک;u��yg������ah�&D�R⎞?)�izIFm:����'�+Fm��sJ�y��'$��i��y�+/:Fm�ǘ�Ş?
��$&|>�G+2��?	)5�&D�a}�)�Fm���z��:)%���a)�8�m8(�ȴ�)�a�i�)��j����
�i)j�k)��&D��:.��c�*0$�<���'���k� ��&D�a�co���Z���ch�<�/��m��?��'��c*�D�>��&DҀ<�׀
���?|�4G��`���=����",�c��?��<����݁<���}k�ۼ��˜�a)r
���f'�c���=b�y����xӨ�<�g[�'�'"Dm���"D��?��p��=�c��'H��^)%�x�|@�y��<�:�+���x��(n2��m���R��=�Ѹ��'������m��fd_I9�C��Ό\C
}{�i�xӖ�}������c�ۥ���"�m8�=׀}cϋ<�g�}�I9B�c3��g�(n:"������h�m�}c;�<��a
}��$|������F
}�aI9g�{w��'҇Q�o�ۉ�<�:�}�a�c	!gJ��i݁<�Zf1k��I
}������Ԛ�'��:Vmh���m�
���}�a
}�4:�Շ�'=��݃$I9�'���c耙��aVm�a))���ÀxӇVm�a)	�}����a>�۝���m�+)
��}ĉ}c8��{��a9
}�����I�PD)$���r
��1Vm:�{S3DdCG��)yVm�Q�ae�׷\9Vm�o�)��(n��}�C����z#�
}��@mh 4I9�a)((�%��^s�1�aI9h��x�a�a���{QI9$T9D�+D�YVm�a)OSVm�9D�I9�F)��}Ԃ!�=Vm99D�a+D	��ch}e�9Do�i��B��a9D*�mZU+D8���)9D��Y��'��e�lj�4g��'W�Km�+D�a)Շ�'�a+D�Ymٳ�c�a)�%|T9D��c��Km$�$|��dVm�a�an�.�+|Q��iāͣ��Kmā��<��j3)�CVm��(��c��Km��i���i>�Q9D��Km��c�a)��x��cԚie���'��c��i��'���'��)�>��Km�o���cǴ�ja��nj&�t�)G��i�o��Km�WҜ����9R�)Հ
�m۝����)	�J'Ԏ�c��'���'\�a�=�g�/�/���i)���x5���&D%��'�i8�$9D$ ��'�t\��ak�&D�a9D׀&�����a9D����(�&D��
}�x�k����c|�����bh|w����8.��iK�{e���-e�Kmc��i~ԣcR�&DD�KmH��c��&D5��i���i��)�|w��i��)g�UB!�Km�9|Eңc�&DY�Km�)���'5�<S�ie@�<���i� .������c\ ��)���i�Y9��)���'������1ѧ��i>
����Sj�M9�x���'���
�3��封M9���9~�m�f,C2���?�M9���y�'��&DJY9?���pf�����s�&D9;���i���}�Y9��i��aC�&D�Y9Q���i��&D#��i���}����i����h�G�<��M9�3Å��`�Yҫ@9��pZ+:���X���9����Nؒ��i@Ӧ%�FDm�fԆ�i�Vd��M9lDmL��>�M9>��%L������o(�aY9��o�Sj��%�i��-t�}'�
�;��bh��L
���Z+�-me��%�Om��+��}%�icOm,]m���Zv[p��</|<�!�
]m�OOm�.�c8
Uv�k���/�����a�qA�c��M9悦%���iU��A�Om��c�	)��%����>Oma���.��e(�M�bme[�xӤ��f�G�k�c�[m��o%����{3�۹�<���[mX��9�i[���[m�x���{�\�c�2��/D��}4Om�`8�%:��Id�4���an�	���m8�`8������ۮ��z���#�㌣�[���6�-���I��{��%*�Id�[m#�ܘ�ѧf���{��^	��a����>�/D
b���i7��/�OmҀ�aʋ4�6��kme�؁��x�b�-
�t2\bOm���a҇�
�fb�c��a
� Δ�m���=�v_V�����8����-t�$z4/|�/DE�c��{[��mbme�������=Y��{��qX�/�U;��$b\Z�������Id����Ё���{���m�/D?��;�$|w��b?�f�/D��a�����/DH����a���
�۹��/k��a��f8Ё���a��{�J�H(���->�hЁۤo��f8����aR��<�W�pˁk���xL�q���R�-�E���s���_9��9瀋�m��]9��k5+D�iG�_9"�y5�}oNm�&\��'��tf87��y}�7��̸�	�8�o&�y��+|��{y\����Pv��_9�\�}��k�$|xWR9=�&��k瀜-z�&r�=��+�G`1�]9����k�]9
ģr�6[����9W�B�<�>$u�J������c?��5J�f8����Ҁ`8�Fmv���s'[����^b$u DՀ��b?�&?�
����o�  D�X�}�oUB�ob�y��-Oe.���ol�<�7Md"�}̸�{����zNdW��kb�{�)����d�_m��X�]9�	 �]9;��j*%�{ۘ�k׽]9ɀ�/�uR��3�����]9倅�+�?���]`�_9 b	 �og D�ہ�T % D!b_m"b�{�(�?nU�aS�b����h}e�Im�
)*	},R9(���ў�f��o�A�a�_m}�bMdj?�!2ђ�-J&`�vfe�}�? K�{Ԁ݀*�*�Im.�;u�<�_�Z�:_m�4:�#�Av+�<�b�Im�ak��9$b�a%�c�Im��?�����ִ�}7�a�G+�}��P�	�?D&b�a݀�ca�}2��9o�<����}�)��aE��-���c'b_m��`e<�㣬)U��-(b�a+�`e��aB�-|�be�Bd$*�{+��-r.���9)[������-m�$KF�:������y)�-m�݀��=��<�Q%}��,�o*b�{�[3�o�Q���h��&!�o+�-@�<��)̈́-d'}|V�>�Q��}�`e����+,b}ߤ
�׀&!�������������8��}��x��;uw���-�Bd�`7L}e�}U )Y�%��os� a�}.b}�����/b)0b}1�/D9�ZQ�ǥ�)|�}��9&��y�K9���i�I��2b)���(�i28C��}(��}�<����6)�)��L3�`eQ{�Q��-�)p"D9���"���#.Q[J&۫�c�	)
"Dw�g�4b�bЁl�5�Bd���<�a}:��-�Bd6b}7���_�Z��}oA�`e�]}k�bԂo�	�V�Q�8b}��}��-|m�;�Y��ca�}L�$D9�}:b�b�yG)���o��<4}$|h)���}D;)�髀��r};�aX��&#�"|3�y�$D������U2�����o@�"|��U#����ЁG�o��p�Ϝ�<E�Ӵ/D܄"|�:����ao��a5b~	���	B��s���[[i8q��e��݀
���<b"D=�}[�.>�)u�i8�"|+��mMmϔ}�ƀ%$|����Ҁ�a����<�"D��$ރ��?b$D�Ev(�b��}@b�i"D�b2���N�y�I9lo�}\�$|Ab��Y�c`M}Bb�E��?MRi8���ۯ[�-Cbde�)ud���
X+��$Dm�.o�ۻ�$D��-�6�]��2>de�VFdD�}E�"|F�}��)u�deR'�*��{�Fd���ood���؁���nҍ��ڣ���Fn��hY�}w��9���?��o�Fa�)de��Nbder����um84de�g{I��n���a`��Ev>�cw��H�:��mބ��h�G��a��`��eރ��+Hb���)oL�}��aeo�	E���]�}Ib9D��u�i82�>Jb=uEv���ܐh���mo��
=*�}���N��-K��aO�)>��o6�o;��i	��h�N��cwh��m����U9D���L�% deMb9DH�9�7��a���%
�Y���Nb9D7X+O�YmP��aցQ�a��ddeΕ�a��H�Q��a[D$O�)���8
Fd;�!gʋp��9D���a=3��oރ��o`�YmP !�g��%�Z&|�R��a��Ym
ٹ !S��aL�}Tb�{�b~`�Ym�&|�������U�kV��aԊ�%��4S��{��!g	�G�W��aX�Yms�)7�Ym��F���H��}ځ)��
�l��d�_9D3*&|����Yb9DZ��a5��?�$|��?�͢k�K�E$&|��!gk���i[� %���&D�ô��\b9D�����z �a]�&D'�
��K&|	͢(�)���-u
)D9�L��{t��)Do�?����&D���Ҁ!g)D�?�^�):��_�:n��&DB�YmX�)���mV)D}-u���&D`��a%)D
��ʥ}k��o���$|<?�a�&Dх�a����b�)|�&|�-)Dc�)|��!g��R�!gY�6a	�G���'&�N�ao�o��4���L8���o��(��(���7C�k��}�m8J�d�)|��&D���\�?\�y/f?)D�Iիk�	}����a���_��)|W�m8e�&D�z倉�o��)|��&D��=�A��Z��)|����r�	}���h�&DQ�)|��M9>��o,Dm��S2gm8J���
f��)|ြm&��y���m��}5��8�9��)|l�i8>��or�ˣ[��V�:n�Dm��J<�.�
��x��P)�$!�z#	Dm���i����ց����$!e� u�,)D;���Σ�1]m��ݴ)D>�۔���9�)|��)|z�c����fb]mg��ho�۾�_�YU�a�'���}k��hb�a�Q]m����ێ(]m҇���ib�a/]mY�jb�a��-Ӄm8��)|֢B]mkbDm-� u��-l�ie$�}����m�m8dցI����}g�ie��9	�e�����a�=|j��}h��inb����f��]m���VJv!���oob]m��a
`^�=D:�&pb@9RJ�?qb�c�c	��-Ă{e�=Dl� u�=D݊�-���=�&�w�]��a�}� ��iea�}rbS�X���f�����i���Q�fhX�:֢�sb v���I$Z��&
��N���tb]mv�ǣۆ�'u� 뀟�m��=�Tpvb�aq'�c�Bm*�z"�)w��il �B�-Omރ���&�b��'J���-	%ۿ��|T�sfxb ބ�&O��?�O �	%� 
}|�i�J�}��) � �-|I��yb-|D�Ov9D��F9X-|����zb+D� �E
}€넦-|'���{��|�=D�Bm{�Kd|�
f��ie{�
ۀ��9D���
�Kd��)5+D}b9Da�}c���Q��~b
}�CE9D� -|s�)v�mժ��{'���-e{��+|�b?��
�_`�3��_9��Jj�b
}<�%(�7&-|C�%�-Df�m1���}�R �bVm�=-|�m1a��k D��ܿ�#g��f\�#g� D�b$u��_9��f�X D���V�#g��_9���}��φ�Bm�-D��F9���mkC���f�_9r�&TZVm���}^($u뀜-��0����T��O_+ DW��ip&�)��f�������b-|�֣��݇�e��l#��f��%�9D�9D��Ow�}.�f8R�� DQ�	VVm��u��o7�f��}2�-|�b-|�9Z

}:C?��b�ju&�f$�S-g�(g��fg��c:�-D��ߐ�+|��֑��}��(gȃS���+|�bme)!�_9s�@J��k��c�W)!���b�a��#gΑ_9��RBz��E��#gs� ��%�m1�b$u��#g��fŴ(g��f�b)!��_9�me� D��_9��#g������-k�9:��5��i��ymeH)!�б���{Q�a��fs��Q�2y��3 D�/��
)�Av���Z�ڢ|�W�`1��Jjn�ImM�a���f$f3�&�)��i[�-��%XFmk��m���i+��i ��cc�-����b�i��Av�vC�j
���c�!����%�[dh�48���{�ImǍ4�3��9����܀Ă2.��i��W8�bme�b��g�(gE$�i����A�-9�Ρb�i��(g����i�b�{Ȓ�Xo���b\v�Fm"�if���b�a�)!sżm��b�iJ��/9�Ӏ��e���!�`HMd���?�ô�Im�A���io��m��[d�gb���{�aY��m�S�m�%�k�ť��z"��o���o�7��iv֣�	��۪����� �y��	�V��4�� =�����
)��x5�i)����/D��Av���m׹���޹'g���XW��i��u(gf��i ��G�-��ic�iD��m�+��x�i<֢%*A��i�/|��y�^�-���b�i�}��o1f$g!)�i������a��:�c�'g���c.�iX�V��$|v��k��,�X�}ʁO��bĀZҪb�c$|���9�� ʈg��'���g�c�� Z�ڢ3/|Z澾� �b�c�*�b�� ڊ4Gl��{�b}Ł$D�+�x낆�4�&!������ (�	�i�$D�ڢ�ô�lD��Z�Z�	��i�/|�$D9��9��y��}���{9��N>�xD�O��9�c\�y$|"@dR�x*\�cÀY�kX���A���'��ۨ�ބ����{���=��<{�e��f��$D�=���?�9؜�ZI9��uR%Pi8ߝk5��d��bI9,�$Di�cҀ�&�i8"��y5�b*䢡Mm���l�-|�?���G�h��ܖ�c��9	˂�$D�Mm��b�)�$D>?��b�c�i8��b��*�<��J�h���g�a	��l1j��bI9ꞙף���$I��O�iFd݀[m	f�"Fd�&�◁�='�`��$D�����?a�}(�bFd�O�a����b�ai�$D�l*(�)?�n�2���{p�{Հ�SZ�a�=FdٮH��.Fdp�{���{�bFd�b�a��"�5�{��m��l !O���i9��3�{�?����l !'
�.EFda�}Ɓ�X��7��h|���W�	�sI9��a�bFd5�{I9o�ڢ�/uj��#��)u�i8m{�݀[m�I9�b�a�+i8l !�X�c/u=�+�&0���i=�8gp+!��'o��z�܀���+!�=fe�RFd8��h�$fe��SR (��	�h��G��
f�۪E�4S�>�%� ��Jm�. ���h�Q���BUFd��ւt �?���G"�	�, ��'���)��0Fd�����/u���i�b�a +!R��i6��b��57FdW"+!�  ��� 9 !��i�/ �ʦ��G"� g !��Z��ڢ���im��<�{�����E�y�f����D�{��	��8ۘ�������T�ڢ�F���&��é���i�܀B�"uL�ڢ��@m���im�x�5/u 
�?�b��zi��i��k?�3�y}�	�w�o�Wf	��ik{�)���+!����\ � ��Km�
x����-0���w D�}����e	�?a�G"J��j:
֢�)D�b�{�[�
-u��i}�ڢ[��i�b)D�gֹ�i��H��Bdi��i�)D�?�G)D9���G"��?�i�\)D��?����k7)D�b�{�΢a�G"y�{1�� 3)D���i��)|��3����i�!g��{��0z���i�)|/)D����{dfdZ)D����s)D�q���M97⣬:�;uo֢��mT)Dwv}��	}G)D����,�)|�nyf3�(�S�	}XW�(�	���i����%�?�m8��$��	���)|��i�^ve…��T�?ӧM9
��Bd�)|k���S�i5�)|��'��؉քM9��	���m8�\�h-uP�)|3�C�{��)D;�Hv���^,�E�)D<��k�����)D)��k��z�b�$���a��>�$3�Q�;)�u4���)D��
�,
����O����	s�H.)D���aH�$o�ܣ�b)D�f�b�$���ʃ���)|+��a��)|��ۨ46� p����%�bOme��a�I#��iaCv����b�`e�	����ak��b���M9��m8	��a����Z�ic�`#��M9�	}݃���M9�m8���P��9�o�f����y���{9�Kd|F�X3�-թE��ke�m8��9�e��a��i8�Yd��a���a���{S
�h(��J��	���%���a���{� ��Kd�� ��*�	D�ԁb���� �h[$V~���{a� p���Kd(�s�&��Ydv��J)�>���a��bs��a�� ��j6����&�fi�y��Kd�� ���0�� ���aS� a
���Z���� ����<��k���a�� ��"|��&�4���a�� ��&�O��� ��"|"��a=��� o�	{��3�R�i3ۮx�k�N��&�	%�� \��{�� ��ke�b
}���{�"|�ș�i.�ke���+3�ke���ao�� �b-|9��?9D��Kd��"|>�b"
}�b9D:��? � 5-|Vg�(�ȑ�f8��ag��,�� �$
}���+�9D�&cy1�� '��aN�-D�b
}0��a	��a�� ��	fX��?�
}�{�s�&ւ ��-D䕧��� G�-D=�V^(�	f�-|s�&�-D��f6��{�8��-D��	f�-D$��?��hVg��b-|Gg�de�b D[\��}���F-|� D€�4��	f��"|�-D��	f=���c����	fj�-D�-Dm�7�"|t
֣��-D�f8����i�b D��0�G�ޜ/9DC D�ࣂ�-D��@m�	f�`1�
 �-|�b DD -|n��������{=�sqm����6�iƁ�I$�f8�b9Dr8	�a@��?:�@m�b D=����Ɂ�.
}�-|�;D�
 �boe�}�b-|r8��	f	����@m
 �	f��'�{�b]d�b�{�b}e��oe�)!�b]dL��j�b�ay
 �E�a�b�{���d��V"aOd$��	ft]d�A���a�Od��-DBOd�5]d�9D��a�ׁ�{e=����boe<',�b�)!-��-��{e�]d�ׁ]�a9�:�<'挢��E�h�-��{ez]d�K�a��9D�b�b�ac
 �)oe�b D-Ods&�< DSZ-
<'� �a��r8� ��_)!s�%�}r�)O(�q[��
 
 ��ws��-�coe��a8��i��[d5Od�{�a�}�
�(���9w_��hcoe�B9c]dף�m�۪���+Odcoe�5���>|w��&D$�i#m������Fhcoec����&Dvt\�&Dc]dcoe��Voec�OvP)!nOdQ����{5�&Dۃ�a	�{e�{6��-�<'CvH)!�,c
f	_�{��ۡ)!��y��9���a�	}.
f
�&D��y��Du�<'À�`}
�r���{ej��ha�u	�&D>�u���y8���w(
���v�	`
c�{�|w�����?Do��S�����mJ
f��Kd�{�
fa��c�
f��=Dc�a���������1d���ߣ�i�F�(
���l��c%�?D
� 
f�1��B/��yf3�uP$|�|w3Vk��dD�ê�JM�����Z+8��5b��cy8)
�
c|wc�i��&DO��Lg�fU2D*���ޏ��cH�'��?D��Kd��&Dm��$|�Dm���c$|�f�f��$D�CDm�	
f&�fxL$|��)�&�k2Dd)�E`8��{�����$D��=�s�(Dm
�2D6�ir�F�$|��	_�O5bZ�2D�`8	�uT�	��{�z*� 
I��히b��{�R�?D�
��
�H`8���cJ�$�:g�c���b��"9:IB��b��B�?Do��{���曡cQX[�Ꚁ�c���b� �Hg���bi�$��i���i��h�w�?�e�Lr�Ȁ,Ä�uX��{c�$N�3i2D��$D�� =��Ѩ��u�����q�{���c�$��$�ie%۶�8`8W�$���H&1H�t��p�-7�����[mk�2�r�c`8݀�/ˆf��c`8=%���ud̸c�i�"'z)n��fȀ$�[m�ׁ�l*_��-��{,
�-耖�;�i#�����i]��{�$D�:g�vs�b��A[�-N�F���i��-<��$e�$��:g���X2� ���/)����ʣH�-�&:v�-�R�-�DX�΀���j�1����6��:��$��N�9>�֣Տ��=Y@��==u�/�9��-�u�
�?��F9�I�>��$���*�a���̸&�2��X-��b�8g߁�u�F9
�(����h9!�]=u;�I" �i��cz��.>�xY�Nj�
Ȁ�?{��c �))�����f3 "��k��Ym�ubD��y��k�i?�i��ۧǰt��km��"��Ё"'�k��-b�-�9!
 �����PH�i�W ��i !D+D돁k��a���o�i*`{k ��{/f�$ c K~&�]9���a� ���X�悁k G����-鑆�$uo��}7��uO
�{��a(3�y����;ɣd��<��1
�B�/%ցk���{�	�ck����⇉��#��	�Bd��A`��ǰ�]=uH�le}S	+D��i�X�Y�;u =u��`58R�cg���j��#��'�i��U�ix')D���xG��a;a"𸅀'����o��JO !c d[�zI��@�g� ���a�����'�b7�"u"�k��a�n�7X���'#c)D�)�{7��k$c)D����E��1�{��)|)�z>�@me� %c)D�;uo�٫�֣��)|�?��s
�&c�{��E�K�Av!��Ќ���Cv���$@�i'c)D����i1D� g�)|��<`�;u(3�{�ʢg�?(�)|@� ]��?��F�$��u<��i)�)|'��k���?��?!)Dщ)|�
�x�
$ɍ�܅�'�'��"um��۽�Bd�$�c�P��?:�$�	@9@��$=��ݜ<gz��$��(m`9 @9*cf+�-l��8#@9��$,�--c<g���ce�i�'�
@9�'�)D	�)|BP@9?<g���c����m��c�;�	�$��.�-/cM"��-� me�<g0c)D�fa��+rs���Y��`e�+�sEfڋ�$]<g>τ-X�)|Q��~�	@9�zB�d�dP�o�֣1c	2�о|D���$S��a�f��k��c>�u	29��-2�$	�$�˜�g��fy]m3�-�&@9ܛŮ$���%k�ێ��?4�b���i��V
�$7@9R\�k�`+�Yd�K��+d,Ղ 5c@9�`Ɓ����:��$�<g�� >	26c<gH�$��a��Uں�-7@9��y	27cFd8c<gm����S��c�'�	2`U<g�S�t
Ԣ�Yd9cFd���`4�`
�y^��cn��%�۾W�Q��Ԃ�a5��c�'�`:c�y���^d��%;cfk�x�C`���7	2�fl�y<cf� �y��վ���
ۯ���=cFd]f>c�`8��A)�?c�a@c�yl�L+�x�(R�e�zw��Zvv�(Ac�y�=	25WMm�-|j��<	2�C�aG�O�p�3�-uae�P���Yd{��%Bc�y��i��͌���C��Dci879�y��Zv���9r�Ec�y���F���,:+��iFcFd�-|G��i�}@���͢-Df�GFd<8i8ᨁb�$-|�Fd��:Fdw�E釀��?4i8#H���Fd*�m� Hc�y��9��_9Ici8�Fd�i8�u�Jc-|�FdUi8K�_9L�};�`g��i82g��}�W�!�y�3�i��-DM�?Nc DOc�y�, D%6�2��?��-D��u�y6 D�z�	�(� DP�)uQ�-DW= D��-D�e�R�i�`1� D���?��u���S�@mߌ0y�-n. T�@m�i���X�@m.2 ���9WU�@mV�%�֣Wc \�@m� D���D9���cɣ��@ms�E���E���󥀲���x�i#�i8Xc D�D92� |Yc s�E��+�_�A]dZci8� ���[�Id�  %�U]#�@m\c�b]ci8$�?��D9��@m{�D9�]dt��.�{�7}e��E�!:.]d��8�c)�íN�@m�}e�R �+]d���s�E�=}eP (e����?��uP�`�M D�.��r�!"�b. D*2^�@m��D9_c D��E�:��`c]d����
��a D���f%�a�
]dg�⟁p�T
 �>]dY�scac Dg1Fm��b�@m*�O9wy*}{�sc]��a��E��@mz �Fa�
�� .�)��íz��%
�sc��E�bc B��cc�i�Fm�íd�@mhFm�D9S}eFm��E����{	��۹Fmec}e��a��D9�E�1��{]dm�=fc}e_���`?Fm9�7Ӻ6Fm��̣��E�>�beC8�iO��z�j�sc���/`���}gc}ehc]d�
x�+}e}](}e�6�i9���:F�a��?���z�)!���%��b��
2�-u`��z��i4�b�B9�i�sc�/�i�>�%��sc�gȢ�}icFmp��z����4���O�bڅ��hj�a�>�y%=�$����Y�sc��k�a�ѣ)FmR�i	�f����sc�FmL4Md�Z��sc:�i#y�i��?DYFm��Ȱ?�y{�o��i9�yl�m8�fFmt�?�CC��`mc�?��ya�uu�Kd?�m8d��{nc�i���52Do�m8pc�i���y�m8qc��Rc�i�Mdr�m8T�	f���z���y�#gsc�?��f9�m8��̣u£.`8(��`۱��^�?D"`8���y��m8�Œ��"�f��m8m�j�1`88F£H��a	��z
��,����2D�����Md�$D.�#n���y�$|�m8h$|��$DDm�$D�E`8t����(E��$DX��!$%��i.� ��*�1$|t�$D��q��{�� ��	f����iu��W��}	£�D��v��``8j�m�Kdd�m8n�$Dw�m8��3���s�$D�I9�í��`��i6�$Dx�	f��d�o�m8;�$�����`Ӄm8�$D�#nvEDm߄�y��9�y�{���`J�$D�9�$
����+�aX���	�q�d2�㮜 $|<�%:�I9z�$DZ�Rp{�E�ۊf��?�?�����$��ieҀ �#fyI9�$||c����}
`j22D'
ܸ��$DQ5}c�-d�$D~��?�cI9.�-Yfe3��l�}\��{SD�a[�a~�2�'�a�c�-i�����i���i�c�-�� E��y�c�a���H�-���{<��i�$Dt�a���v:��S���v�c�a��w0[�t� �� Ą���k�a6�%�ܘ��߆� h���t\yI9�&I94�-9��Se�a~z����$�c�a���Q6g��?����$>���69D�`�cI9+#g�£�7749D��]d��F9�'�)Տ���d%������a~|�UB"9D4Od��-:���Ŏ�}�í���'�F9�����vl]d��KmX�y��F9.��?��Km�c�-�V
fh	�a�c�-j2
f�c�a[]d��Km���k.
f3��X�.��-�c
f��a~��k�Km�`��-@�Km���+�9�-�]d��$�{��k��a���k5$uy�y*��xۃ��k�V��_�{��v��Kmq�&�m�Km%�Z�	Dd��%y��ih�#ga�F9�c�i��k�c�{�
�5��x�Km.�޹z#'n��KmO��?]$u���i��۔�k�r8k��-�]dT�a!"$uԁ�k.� n�6|�c�{���7�� n��f��iƁ۪c�����Cv�� n�c
flJ$u��Km]�f�Km���0�'�l� n����c9D�� nU��k��k��f�
�{*��5�Ɲc�`��KmL����Km8
���� n���k�V)DŃk���me��Km�c)D|� n���$�c)D��Km�!D����Km��g
Q|[V���o��kԈ�+�� nj�{)D��k��?R��i���i���$۱
meQ6|���)|�c)D)��?�c)D�c!D�!D�U)D���$��?!�c)D��)|�q�!D>��{e�6)D�c!Dm��vZ�{��)|��W)DȀ�� nh�q>��*[!DJ�{e�#f�R�?�r8S7�-�`:#,)DJѢe�)|+�?��)|w�؉�f��)|���?�T�-� n��ie�q>3��?��� �)|�c�-x�487��/���S�C����/D��%�c)D��/|����-�)DQ�7� :`8�q>�V)DQ��Z!Dm�o)D��?^��v��)D��q>5��ۃ���c!D��i���5�?�c)DX�;F��-�Q`8���{:!D��<Om�1Om�/5��j�?)R�o��)|΂��6�-�cOm��)|���{���)�)|����Im���{����S��$}Om�=����/Om�0�j��-e�q>OmԞ)|����cOm�*n�M�-��!|׉���q>�)�c�-���k��Y��(wꅰ�q>:Om���i.х?c
}N�q>����݀�/v����`8&�9�^1�-w�ݹ��a�>
}BOK9^Om��U>�K9(�=x�� �j:���U>R��a���b�+���	0�)�U>遁b��a��a£`��Ԁ���~~��iy��a}��a5��dʐZһc�i�b��U>YOmȀ����o���a�c�i���az����8n�c}ҀI"9¡��b.��a�	ݾ{���i�2�i�c}�cOm��Bmb$n��ak�}�
$nL��a5�i
}���`
��a��iY9��ke��J.�c�i��}:��a����^"�/<�
}�e�E�i�'�%��o�!����a��{=-|
��a�MmЁ�h�c�i�{����`���b�-|X5��i�
;D�Gvhi8�c-|�ci8�c-|T����mu>��vz���a��a����)u�u>68�i��;|؆ ٻ�a��b��aR�����ii8��-Dm��>r�%D%=-|d�-D�i8q����iډ%D�'-|hi8d�-D�I��o���k��́%D��;|��)uE
#���i�-|��-�%|/�iY��aԊ[m��}��Bm��;|9�;
��a�%D2i8�`7����%D�
�{�j*���a���i��-D��>�Bm\�%D{��$��{́�a �-D�؉F�%D���U�Ido�-Du�%D�}��i��0ه�a픪�i8-|��Id���/%|���և��c#�,8�%Dlæ�,�b� -|X����d��%D�HS��c�%|�b?�&-|�i8�-|5��cg_"�)�c�-�{8`��-D�i8��-�)B+i8z�;|̑Y��x���-dg!�c�ic*�-��a��S�&i8ـ�c��-D�x��<	2u>_�%D�M�&.n��؁%D�c�a���<��c��%D��-�7���;|��`��%D�E�b�-��u�,�b�	2H�uY���,7�E���{���$����c	2���۬f9R9�0�^"2����b��c'��G�a�P	2��,e���pf�c�b��b@�Y�	.nv�w\M��:{~@	2��=A����z��cN&0���HFmz��c�S��c�a��!۫��vM�a-��=��4�o�������-_�é���h�a�ۘ�-3�`o�?d�����C��cf�	0���Bdy�a�c�a��Bd����x���r���r��++(�-�J}e��ah�a�
����-�#f�ـ�4S���?�c�a{�{�x>��ێf�)h��?	2��*�T-u:�Y�m˴{��c	2BZ�a�H�?J�b���y䅩%.�a�cMd�ۯ|�(|��?k�Y��O9ӆ�?��(|��O9��y� ��x>��yJ��E���� -u�Y�m`Z���%`��Y��ۮ�?Dg�(|9-u���o��+��	}�x>�	��A Nj�X
�?DMd?�V1�-��up��?DR�)n
��i���y�#�a�m8Ё�x�	},�?D�c�?��)n�&ii�?D.�aMd՝	}�L�ac�`9{�-u��?D(����.�a���Bd��?��)n�c����L���c�? 2D���˭	}��?D�Bd?�g�p�?Dݷ(|%�}���ia�x>"��y�m8��c��?D��?�x>q���Ё�b���3ʌ?�cMd��y���i��x92D����7�x>c˴o��a�x>��&��{2D��oj��ɻ��B2DV�f���(�@d���kƁ��f�Zv�)Md��)ni��Ӄ/|��)n��i���b���aS��Dd�?D�˼�$s79%�A��b��$*���Ϝ�����cI9���Zv�@d��k�m8���Zvf�m8Y-�V���y���i��$��?Db��%s��9�摏-х�i�F�2D��$�~~�c2D�"i8B���΅m8(2D3�
2XI9�i8qI9l��7�_��-��bd��c.��%(2D���g�x���c�c�r1���-D"�F��%��{�cFdI9��z�샿%쁚kQ���N�F���`I�F��=t�v��%r�,i
C���Zv�ǭ%bX+S�7샿%��f���kx��^X+΀ ��
2��&���.��g���&],�x���&�Zvj����Zv��,D@�Fa�%4�%"�2�}��Zv�Y�3�,D���.��NVVi8���o��,D���
|>���Yf�,D<8i8�ϣ��2:�x��?H*I9��!�ci8ϊ�o��%�,D��%��-��i�2�N-n�ci8*{��?C�,D	]dO�P��-y�,D��-�c�y��i/�69�,D�yE
}�,��,DW�`a-n��D��
2��,D��Ym��Ym5�
2��,D*��9��zU�S]dg
}��YmfF��-n�c�y�{X�y@���ad����a6�Ym��íh�87�5ŜYm7���� ���YmC��a튤��Ym�g�ʉ�`�}',f� N��{��a�	Vm��a=�%���m�,Dl�f�YmS� ����{�8�+Vm!毚�ޣ�	fp��{�,Dr��a�í]6f)��?deā�����a��,DL�����d-n�� X{���������	��aVm
�8��aS���^��`hI]d�{@��t��ۘ� N�Ymn]d�a�kf�y��E��Ym9�7�p���f���)�&VmR5n����U�ʃδ]�� ��a߆f
�b�أ��Ym3�y�{*��$�"n�8؉ ���{0�Ym�-j� \�Ym����a5n� ЕYml)��fݧ8̈��O�{���`��"�(��a�{�� �8��a�í{�%�8Ņ+a� ��a�Ģ����9������)�#K8|˟ Y^f1�M9_�����b�xF9���5�Cv�VVm�F�H�?!�m8����k�k3����E:�dDdw6���{e+�%k�<��m8��Z�d�%�$O�����m8���2��b�%f�m8�[�a���-�f�/`8W�m8��v�<-�O<N�@9��m8�3�a��b�ùs��d`8������N�`8����=΁�5�m8��,_7�m8��<���))�%d�%��<��F"f�m8"�%�����^(@9�X��^�t��)3�%�`8v��ܦ�<���Y�JE
f'�zd�,��5z�Y�ks�8��j�
�S	��$���o�0�5):#�$]m����m8L��d�����{��a4�6�m8=w���x�
]m;�5z;�������:�{�í9��Q0�`d]mݖ9�|�M9�m8�3
fd�a	`.�6�')Z��a�]m:�x��ce�`ЁY�d�a�c��"��{�6CC��``$
f߂�i�2â��:�ݲۑ��Y�A*��G�a��:F�9Y9��l��d@9�`8䀡c��f��nd`8o��1�aЂc>:�b�%��Z�a��"w�w���x^�Y���i4��%�W]m�bw�� d]m!d�i��ߝ�m�	0���f�2�i���&>�أ(,]m�ik}c"d�aR�b�
f{��"?�f#�^��y��o��7�(�+���i��{Y]m��c��b:�5z$d]m1Y9���i����2�a����5�i��&� �5z�7�iz�`���%d�bo��j�5&d�{'d�a��g�`�֎
5�G$�a��>=6D�nh;D(d3DC���i_�׾y�����	0�e��"Y�c>��x;�i�ze>�A<����n��bm�Kd~��I���< ��b��q�)�c���i��e��3D�e���`�[m�;|���F"�=�����*�;||;3DŘ;|+�_9�3D����_9[
�ig�;|5�����3DO��/2��/;Do�[m,�_9y�{���/$��R�W��b���/a�_9��ie�;n-��/.�Id��5���ib�;|���/~�_9“�,.��,h�ۄ�_9a�}K3D9�ൂ;|�	�fY�����)�E��!|�_9��w��F"+�<���ߣ�;|/�,<n><n��/���׊��q������/cD�,'!��_9>Z<n0d3D1��{��{2��/r�or��	%?���/��a냳�8�f8�i�� ���o���%b��/$�D9U��%��-���/���3d<nx*}��{��/��;|]�Id������,q��,4d<n�8���/��c� �_9����.��{z�;|R��{5��/6d<n7d�c遟$��;|9��/i��$8d�-9��/o���,��״Ġ ���@�xE�-�r����/��_9���/OS�-!��3���*�b.��a:d�c��ĹF�����y�k��>�c�A�-�yȉĦ%Ɓ�t��a��ۙ��݈�L��\������_�]9��+��F��{a{�<nXFaW��a@�]9���+� 1D�c;��{Qs�X7)��xJ>�i�M<n.�-�&;� �р��)	�]9(��m��?��-�aJ#�-|� �)B�h�񨑦�v&$n<d�-<�=d�-݃�DR�c���&m�gf�c��i>d�-�$n�рPb�i?d�-��?Ӵ�@�x#*):Ad�-��ʃ�$nR4Z����{��b�L�c��z~��z0f�30����+��aBd):��H�>:C�aTp����x����״���
��a~��+��磍f����{����al.fFm"�^+�me��aP`� ��a�MdW��+5�;uDd$n��y��(&Edme.�?D�
�it��	}F�?D��G�8��>v�[d:�?DG�%D;`�g�����?D���i�Mdp�
H��iId�i=����Y�'
�J�;u}���i��k��>z ��$���iKd{��iLd�km�g��i���)��iy�?D3�H��������$�C����۵�;u���:�M��i��]d���F{����xN��iOd�kցۙ�5z�[d�?��`��Dm_���)�z'MdP�@d��x�meQ�@d��{%��i�,�&:(?D~�|
���^�@d ��cg��iRdf��@dS��i+0�J��'�Ұ�.met��Td�,�����@d���7|�p���%�?DH�kЁ״\.�%��%DU�?DJ�@d� �iV�?D�%D=�@ds��Wd�%y��cR��zl�@dX��iZi8y�?D��Y������Y�@d�)5�?Dk�I9����Z��iYCi8si8[���[�-�I9��%�@do�L�큊b?��c_W��-�T�X�,R���g��xZ�%߁���{��X�r��	�kƁڷ�����Y���\d�%�#g]d�k��4]fe5fo��i��@d3$u��%�5fe萨c(F$IB:�o���}@ۯ��@d��^d:D��Ԁ!��a_�b^�&:`d:D������Z�I��-�بc�!Nv��@d�c�a>�����Z#~~���b�
fe*�+�:D�I9�fad�%�#$u�~~���Ybd.|��:|�@m@�x��a�陡f��h*���s��C3:Dpf���(�%���?S���c�:|dd�aedi8:��I9e��Į� fdi8��:|X%f+@I9;p��YCi8���-��agdi8�:|�9:!�����d���-j�ۙ��?R�i4fe䋙��:|��Ӛ��?j��iq��e���MmP�:|m$<���7�YmwEMm��i��:|����fe[�t��� D\":D�}=��ւ��h�:|�fe����+�`����&fe���.i�k���]�a)��N;�aρ��F$��W�;nj��i�g�kd�i��}G���Cv_ۯld�aT�?��:|��a��&��	}A�)nE�Cv݁<��?m��_���Շ;n�<�%�ndde��85��W\��?&�bD�	}��<�o�;n��:|
1�y��{?��d;��?p�:|��0������:|Ҁg#����x��R�<�	 �bg�Cv$�<�Ԟ?������mf�$�b��Cv��� q�fr�ۅ��Vs�Cv��@mT����)�fĂ@mƁ7��i�4b� ��Cv˸��	��@���s�t�g#݀@m�	�bG ����}ee�ۄz��b��%z��u��a�5z̏��!���M�g#⁄�v��	�8��������l�g#w�g#<��i���f�CvJ�8��hg�Cv��<�� m����&��բ�&x�ۀ�-tv�y�E"[�<������e#�����M9�dez�m8���p�m8;�<��{���ᘐM9"�`� ��\�<���b�aʦ#&��H���%y�<�������5$˸��z塁<�?	{�U��%քm8��Ӛ<�m8{d>|��H�v�e#<�m8|���H�}�M9o��.���)~�m8w���U��%	�����
��.J�iߝ�����t��������3l���1 :�M9��FaƁ>D)�H��H�� :@�>D����3�4��>|_����%;�>D�>|Vx��-���	�?��zv��*>|�8�,�+-nF��+�>D�>|5�
��f1�d-n�!���]m�Om��>Dq�m8��e���
��)d�Ԋ,D��ek����z��z�J��Ǔ>D��g�4���<w�m8��%�>DJ��@�����h���~�P��m8��w��,{��IBZ��ʣc�d>|닸���b��c�>D#-n�	f`���`-n肃b�	fZ��Ў�a5�ke�f�j�RGv��o��c�
-nd�e���a!��c7��`��b �<�d�eI'V/��c���bQ{�Q{[몣c���a ��b-�b��Ă�{�����c��a���a��	f��>��)u�h>��>D��b{��o���<������	fH�>D~��Ǩ��O���:�>D�d-n�ODm�$�b`��	.��b�-n���a{��Q���a=R-n�bDm��b�GS���>~���@v]�"nDm���&-n��%�Gg���^��+�h��w���1����c��|,gw��ke��=θېd�h�zp�ke��p�	�˕_�6��c �&���Y�ܒ�keQ�	f뀵"��ke�&��J��+���a‘ke���cC�	f��c���bX��]�f;�<���b���aҡ� ���al����Y�&	:��}���a=��8۟Ad���=�.��)hF�Ad���I���`���Q����[��܏�Adڋ<�*�Id5�Hث�*�Ad/�����h��y��Ad���$"۴����Ad´@v���/��Dm�"n*��5�<��&��Ad�y�a�߮�Ad�}΂<��6�C�Ad�b��ta��iB��Ă@v��ݝ�����Ad\�b��O�T�������Ad�����
�Ad
�iek�H���j�H�9P�C� .�Ad���(��9�8
���j�Adg��$�F9��Ad��}	%��a�O�Ȁ��ň����5zW B"���*ƁWҗ�<��doeЁz�եs�O�
oe�-uz�Id���X<T��F9�d�{_B"�(oe�d�c�B"���$��a�Id\�5z��}��Ad�L�a#oe�����܋�Ad��<�a��yX�a�Ad��e�d	2�doek��yQ��,��_9�[�t
f.��y�<����F]�a��c�+-u�oeQ���<���H�Q:�aH>m0h�d
f���޲0`1b��f����doe�Z�T�Dǁ��%�d�a�
f�;	2�&���o�0h ցDmO�d�b�	2��c�dDD��a�g~>&Fa�dDDl��$���[�8DD�S�i%����dDDݑ�,�:�a��H��&�[�0h�oe�dDD�.g��~���ʉbe[���ah�c� DDᨸǪdoe9�H[���P$���\Coe���id�/�$u&Dv�r�k��$u��#gdoe\�kT"�bo�۫dDDB�P$�doe�d�a�dDDyO$�/�c��b�dDDg.�a�d�/�d	2�dDD9N�i�d�a�c	2�d�aXme4Md��/
�)�DDZEd"�x'DD#��i����dEd�DD�d��me9:IȀ��Ȁ�LGEd�MdD$�y�[�
�/@�_LMd��H�a��Jۯ����DvҀ����}f+DD� �a������)Md�d ݝ��=���DDhMd���@Ed�Md9��i2���Z"�DDWEdN;�a9*��=DD<Md����)�dDD�d�aX��ʒ%z�Ed�dDD��)��������v�)�����)���i[��,b�[d�Ed�  2��d"�{e�`8j�f�^�3�{e��o���ioͣi�F��^�3��-�":���iG�^���<v&Nv���io�ش�f3meIMd��4G��*/MdL����{s��i{��c��<����sMd���{!D�;IGNv��i{��c���{�� ���bof�Md+'���4��^���y�',�dEd�>@m'D���{b� o�G��d ��[d���c���{�Nv�% ���,_�8�
�L>���i��bt��i��������z��^���i� �(u$��$�����`� =����k���Ă[d����� 2[�;��i�d):!���V����i���w��-� �����Dm."):���Qs7��3���g��q��fC=C�5�
2���Ӄ^�n=Nv���;�x�Z�עO\v�$n������W�f��^�Ɔ�`8D�Od^NӃ^�X�*E��{���:�>�����hD�ش̈́f��-ĀZ��
2%*���{�d\v�$C����bZ��-xטb5�����b���{�d�h�Ċb�$n8��{Ҁ<�ǥ(�H�m$<��ݯ-�hX!6n�Dm��*|lf#� o��?�Hd�d�V$nL�
ߨ��ׇ�z��Hd2��`����p��-�݇�`\�<��ƌ:�<��`��zo��E):忆$;�_���D�L��d{�d����Hd���`0h���{+�Hdҁ���z`�iԮ;�ƴ�Hd�d�ik��(�HأMm�d�i�~8�d�i���`h3Mm����9�Hd�d�i%�%z������`z%Mm��Hd�d$nH��f���)*ֵ��`:F�i�� �xӣ%|��Iv���a
�Œ��艂<��幀IvQ=n�� ?�����Y����a�7�i{�>�/�iJ��W*���f�{�냷_D�aR��i���a�d�i���
f�d�c[D�b�2�zt� �d�b�d�{��Hdi��i��� ��$�Hd�df|�b���i�C�	�{�d�b9�~��Hd`������ʈ�l���iه ��"n]�b�!�f��b/��a�d�b��bƁ3�k� ���7{�%zx��nY�i�[���cD��\����ft��۫�	�����<[�Ԁ�Yy�"n��Ivj�^�J��A�Ġn%|$p�k��%#D;�cm[��W�4�v�)u�z�+��%D�<�(���{�*g:#Dn��e��֪�c���aj���4�=���� �d#D3��
�c:#D���a�Dm#HĀ:#DR��a���czf�� XfN���c��=.�zc����+fP��c��z��b�dfa�bS��=�
'##'����=�6����b
+!��b��#|Z#D�	fI3�{��%�[�%���d�bh۸K"%��c_ȵ�"n�K"h��c�.|Ё��a��ca�o!+�@";�&:�
@"Ё�܊۔��W��c��z�Qflj���߻�4��5#D��˩�c���ܑ�Fef0�Ђ��d#D.�z��,k�í���sog�&:��$ϔz~�\���o�������@
f�:D�d#DX�M�^���Ҫ%�Y�^�f�#D��<�m$#Da]m���^�.K"��<�	�%��h��(+!�!p��
+!_�X�[���:|
�i� �a]ۥ��[���Yb�z�3	2L�$M�^�����O�6
��=�d�ax�x����_�#|��z4�;n�d�a��u7��y����f݃��oMv�����d�aN
-u�Uv�Y$y�a��$:ϑ��Mve'�a5�-��^��d�a��-B��r�ye����۩��>f�a$OMvj��i3�)ng��h�[��d@"��>�7ܸs��{�g��;n��߁}��Hب����:|h�a�dVd��Z�Vd����;na�cg��ca��m��iaVd?�^��d�`U"7琡�%�$��;n׀v��Y$���h�^����&��;F�vR��&
�	��Y�Z�^��%;��&v
�)�dVd��^���
�	O$s��Q7�;U����a�%Tl���3�������_�a�ۙ�8gӃۧ���zw��E�;n�J�W�G�d�a��;nڰ�a���>�ye����`�X[�o�cF��aیzwH�p��j�����ye���f
�2x�ebg�?��h���a�i��;nk�%V��	��Y$���a�^�o��a���H�T �f��%�����Y��f%�$׉�V��a[�U2��l�fi��x1��a��'D��z�6� ��'Dʒ���튡i�
*���%���
*#��'D%�`;��$y��a������#B��f�'D��Cd��8�d�`��%;�ߚ%�q���i��m���.����8�ˣ��b���a���$��'D���a�����b�	�f��������f��t����%���0˜�$��'D���b�%�� `�'D���W������{��� ���a!���3���(�����,D�>Dރ�iӏ������&À,D���z€���
��a� t�'D�3��>D�*�{��'D�d}e�-n�р=M}eOg���f8��(`�'DS�c��fooe@�cv��Y.� �%}en���̀>D1�C���,D�Z}e� �f���#6}e��h�>De���'D���}ey�he�haY}e�	f�'D;�;uey1�-ny�'Dx}e0�8���y�{ת�{>|Äۯ9}e5�	fN�f� ���a���.��{W� e�焦�ne}eޝ�{Z��+vĀ���{��f�bKCaz�c��z��bȀ��{�	fr	V/�4�e*D�a�W[Δxӹ�e*D� }ך��{��a�by�h	�	f���{)}e:�xӂ�f8�� �Z}e/����*D
�a`�ce)��xl�,D��b�<-n�@}e�?*Db��{M}e_�8_� ��En���{���a��a��`}e��iʋ�v!�-%�c�}e�Vm�[���o�4�
e�c=���]$�/-n뀹�J}e>fk�|�����io�Շ�f���
���ve)�;�h��AV
f*��{w�F���e�x��{j�ۑ�&ј��1Df�$�|T���{]��a��{�۞�{����O�	fkc��*�*Dof�{L�+nk��a��ނ	`��9�>f��5b[�>f#��XЁE���b�e*D��ԃb�[�w�m8�&�aց��Ё@+��`�@f�,��Ɓ��xeVm)���2��e�iG��';
`(,�i��%�)��d�e����B��'��[d��^��m8���<�Ad���%�.FP�fo�be��f2�Ad�?������+I�Ad:�x��\����H�t���')���of��Av�J'����ִ^[\vV
f%�x�[B��'T\v���'���=���V
fw���B��'�Ѧ,>f,��=�cŀy<��c�=of72:�x� ��,����8[�\���G��'����b�z�c=��y�\v��,�I��e�o̢e�{R��:�bA�Ad��m8��{��L��?
f����eI"*��&�	Y9t���/�{"�ۡ��e
f����Ќ�@09�e�?%�cl��b��[��D
f�3�{�?+�m8�
��m8�KZ�_)��1���-�I"��m8l�?��{��� ��'e��o�� e
f8�3� �{ؠ�49�z��k��	ʵ�ݨ��c?��,�-�\v�?�����-�?��`�{�:DDZ��A������Y!e?�<��"e�{j����O�{���c�ִ�񎊘b#e�i�X�-�8DSĀ�JDD_�i��-$e�i8���RDDw�d���Vk��i�1.|A�@m�
fv��o��
��t�{�i��e%eDD>ہ@�{4�ª&eDD�X�{.|Vv�DDR.|*��A'��`�iFDD���Q�;�(eDDB�i��$�����{7R�?\:DDo��
���ԣ�۸��`)e�i���/[���6f>�?(���Wd�DD܍�d�#�bf*eDD
�i+e�-o�=(�>�ۚ��$�x�ԣ�&.|,��#fH��4-�-�G���-:.�-_�b�Dv��Ԁ�J.e�-�*�i�=�
f�Wdx�����`/��i0e.|O�-Q%�i���k�3�i1e�-�f2e�i3e�-ͽ�$,��ü�k4��i�_d.|쮩x砃k���/۬�a:�&[��kݷ[m�遆�/���k5��i6�x"��a[����)�c<��/�.D�-��	���k7��/�)�c���$)��i�^�c����e��a;�c��.D���a����ƅ�k8�@m�c���w�c,��i9e�b:�k;��{)��a�������r��a�b��/<��{���y�f��cԊ�{���?��/=eWdo��bo����棣Wd[����@��k���/>e�c��b���(-�`?�[m��`���� *�b@e1DA�kB��{2��{Ce1D鯃k���{݀�{���a���aW��{B�F"De1D���ke���Q|k���e1Ds�E��/�� Ɏ�k���i|��a҄�/������{�1DF�x�h?��a��,��Y�`��k%��{��o�*��Qʃ�kG��/��{o��/�M"\�bHe��Ie�ct�-Ё�?	��g��?���c�M":��cJe�c��Ke�b[��?��1Dކ��Y"9�S�f)�I�8�9�L��{��ʜĆ\�M�kN�?�� g�>Ҁ�?���k��f�I�����O�?3��ko�G�t�=Pe1DQeۣP�h���Z����7���۽���{Re$n���S� �ˣw1DoߣԌ)R�c:�Zdk���l�Zd�ԅ?Te1D�W$n8
Dm�$nT�ZdUe��V�ZdS�Y�l�/Ɓ�v���WeNd���(�"u%��mAh9!X�ZdӐ�c@=n΀��^��Y"��a�^�h����k����m�.��z)�i��Zdʃ=큘b̑tYe۫��?��Zd��Zd���Ze�x��k��?��ᨁZd���A�M"R�x���k[�Zdw�U$��?>�Zd �[vT�i@��$n�xDm�7�a�xm&\�Zd	�Hd��a2�h�xKD�i]��aTrӫ��i���i� :���fk�G��a��a�%#���>%�Y��"��>݃�YR��iȀ�	ٳ�a��@��
Zߣ��a.��$�Zd��բ�����c�a��a�Zd?��ܕ����Q�܄�b�����yQ7��^�ZdW��iW$�f_��ag����b7��a9��i��"n��j��8����-`�a��^���%ε�i\��ameQˣa��i��a�
�`���i��b�-ԗ��c�i*$�d��i� ͓��(��虶^���i]�x%��c���9�We���\�Q����W˸����W<ge�IvX���D�֣�p�,5|R�i���~��i��&:����io�����jf��ige۾�0��� �P��i�F9h��a��iv���m[�"�=hi��aQ�U �
�(j��ik�a�,�I��뀉$l��i��a�i2�g�iȃ��m� ЂI�I
́D��-ߣ2����@��?:�{����`K'���>��`n�m1�ԣk�2����.f<F�^�֣o�iɖ�-�H9:� ��Ư�i�^dd́j��iwv�R��x���.^dpe@"��b̤�iҀ�f5@"[:D�V�Wfz:D��Z���i�5@"�] A[�y��zw�@"G���5�yƁ�-1�i��x� @"3�yQ7qe@"���i��񘾰�r�%� �b9Wse@"\^d��%�btef�:D^@"`�^dX��?ue@",fv�
�@�owe@"a��RH�߀:0 �-xe^d��Fdye�-�
۾p�ze@"{eMv�
�ȃ7�����y�6�$�6�&p��������c
'�iQ��/ �(�a|e�-�?�iလm��_}e�a����R?�i��̛ŗ���86��Mv�,f�@"c���8
f�*޴A�E�.��	%�i~e^d��{�e�-�;ny	2+�y�J�Y�8t��{oe�e�y�;nG��h^d�g~B
&i�>�8�e�i>0�b�!��5@"xde����@"G�`y	2h���0�i�f)F2�e8D�F�a�	2��
���%���
ٌ�i�-���{@�k,R� D���?��k�ۥ^8D����v���i�8D�
��W� �-��ެB�aWMv��`~����-��O��`~�<����`~5��8��z��`~E��a �-�Ca�18D�e�i�飈�8|�e�i��8�LE�-m�����.��? ���?��֣�ef
�飆��?�	2��)n
�8��;n(������S���񂼣�f� �Ʒ�e�֧�=ȀՁS���z�8�*�	_��8|z%�y���#�Vۄ�������;n��
)D���٣-��{�?��e8D	��>���%��cҀ��e8D��6��?"7�yd��D�f��Cd$����e8DC����I8D$����'D��-����v���2C�i�'D��	�$8D�e�L�2�%�c"fW��>88D
��l���e�i��Si~���%��i�#�j��>֑`~=��n��m���"���`%�8|g�xk���2{�ӥ�>�efo�@m 	�`~��@m%��w�P��'D>��0��t���t&�8ؔef�
��?;���:���f�O"bCfY'D���P�(���z�8�,�բ�	o�efĚ�,������f��?����>D>��=� ��ۖ�f
� R�&a��h�>z��>D\�?�.���'D�3g�t�&�?��g?�!�?l-n�;{�>?�L"�zwǩYA�{+�,D��i�� Oƛ���΀ �>�?�����Y�́�'D���}���@v�2��H�	f� ��f��{���)�C9摏-���,��eՠ֣o��$۽g��&:Z�o���6�eۚ�	fr�7�ף�g�� "�耏-6۽�	f��4C�_c�
�p�	fa):�8�
	�+��-�́j����f�B9���ރ���v��� �	f+� �?��C<|�T�a��f��$���?����b�`(�fUB�8��8c�=(��襆��>D=� �<|���-:� �Dm�e<|��8V
�{�-�,-n��J�<D�e<|�8��ۣ	���<D�� �xj�]6�{h�ۡe<|��<D;�a�e�<|���-@+�{΂��J�����8=�Τe
 �X�{Ÿ<D�<|+���g����
����a8�<D��f��-*��J�<D��	fv�F���8
�$��&ѧ�- �
`�N�'��y��-y$�`n'Gd���'ւ],ɗ�`o=n��f$��+����<|	d~��<D��NҖGd��<D.��y��l*�]�b�򸅁9�-7�$8�&����۽�`Y^�b�e<|X�y�o�_5���ô8�z��
`SGd��c����	�a��<Dv
?�	�x��$ ��x�&{~�e<|��
�i�<D�́��ԣ(��_�<D�)��xt���� �+����}Ÿ<DC�噁���c�y���Ŀ%$���c����<D���`[�XdS
�x��<D|��0z���	Gd���ao�<D+���[�>��{��n��ܷ��wπ��oh�J���a��"nr�j5�`���a,n~�G5D��xa=n��xo�+ĂF9a�b�X�#�z5z��`m�zAG�<zGd�e�`�
4i�bH4�V
f�{�<p�	%��`9%�+{Am�ۘ�Y(��b�x�e#D��Ad!�x�_
f���[� t��ҁ���#D
kΒۀ:
f������F9i#Dg��,�{���^���ρ� ׀�ܳe
fI"��h����
���m`V
f)��`l��?ź8d�&��c�I"k>#D!��+�[��#
f�#DBX�����iC���?�����x���Z��<k�2���\|~�A#Do��+�
#D����`O�}�DD���Rj!�=I";�x�����棵�{R�飶eDDȀ�	?�"n/?��eDD�}�ުj�e{��+@|~�Qg�c4DDꛪj,#D���?%����ݪjMņ-}��eDD�!I"�GDv���(���T�/�����=|�eDD�?�l7�/y����e#D��z3?��	I"/I����?��h3?��eDD,#D�?��'t&DDl���y�{�壯�D���?��eI"�?�	%{����h�����#+��)"�`���h��R��x��bZ���/��	�����eDD����[�/:�)D��#|�B�ت+��h	����mex� ���a��x�!D,Ed����T�a�meQ	&�e��h����?�3DD�Ed/��)�aʺ����x��{e���h�i~���i�?����VBc*�����i��H���皀i~⁻x]��i���x��
��me	}��i��{e9DD���'�9Β贯�{���i�jX	2B�{ɀi~��/S7�-j��.�{
&n뀚"��b��h���i/Ҧ�o�c�� ��W��e�{N��b�e�{�
`i�$ゲ��%͡�ay�Bv9��L��4�|��S����	!D���{����h���i»�{���h�e�a�e{�W��{j�%��{���i���{ʋ���4Sb��i���{�ʀ?�E�y]�i~�"��y	2�9���`�	2�.��!D��i~�O.�-�e�i����xa��{�G�Y��{B�?k�b����m��{�ݙw��i6���ʋq�
�bO�i~��Bv����G���'|��{��i���{��)���xΌ��"��{���x���i��'D6�Cd��xy����Cdd}�'D���a�Cd_W�Њ'D)��Q��'D��Cd�����e{�N�{S���.����x��{��Cd�{�+�z��'D?���''A��Cd�Cd��'D�Vm�{���xy�Bv�{�€Ъ�?�H�_"���nѧ�x��'Dt�����Bv�w�Sb�x���׀H�c��<��M"��Z�J?�.��{h�'DX3�y�Bv��'D��߄�:���Cds�'DQ����M"��+!ȣ�Zd� h�M"F��xj?�(��球xө��x��f9����-���1�X�o��<���Ԝ�+�#�{�:-DV�\��4Z΀����Cd��'D�e�-�UU�mX���'D6'D�eMm��+��'D�Mm�gb�`*��Cd��'D�*.Mm?�+y�'DU?�ag��b€���e�-��D��'D��'D�e�i��Cd��cv�'D��Zdϖ����e�i{�'D�M"q3MmS5�i��'Dd�}��M"�MmBO�a(�[vm�c��i�Am%|�{��Am����Z�����X�%D&�a�{��MmLJ�i����Ă�/6Sb��Q�����a�1m~T�W�FvfSb�Mm
g2����a��Iv�o,ʐ�Uj�,�	�{B�[v�e*D�e�i�e�{�P�{�&���{��Ӏ�e*D�e�i��-�F�2@�{��ho2�g�i%J�-��i%O�{�e*D,M�h�e*D��H=��gm~�*D�����s�tG�-
�cZ�o�+*D$�������{��?�e�-Z��P�.��i��h�e�{*�-S���g):V�-W�{ބ�h-�iC'�Yh���5��+�e*D�G�i����$)����`?�i�):��{񂮣F$�i.�v���	���{��<����eGd�D�yX�%D�8V���z�Gd��<�����)��[v|��=��b*�
�*DGd����+!% r*D��<�eGd�e*D�{��Gdނ	`'�9�WGd]6�{,�b��%� m�+n��`��{�2�e�b,GFvD)�x��ג�XGdڋx�e*D~������{�Z*D�f�K"k�ݙ� �i#(�<R�,��գ���l�h�eGd��I
ˁV�k��#m��y�e�?�q8��h��݁��,��xu�Y��f�u���ɚ��:���*|��(.$��\�k��?Ԁ�J犆���kOGd��g�d��.�`��bf&�?QWx���{Am�{��
�<���G{Am����eGd5��%�k>f�K"���x�G�k���x�)���@F�o�ݙ�e�x�R� $�<v�kg�y�Gd�:D\�k��ږ��>k����?�_�e�b�X�:���m�}v�&���	�&j�Àx�I"o�x�{�k��y��_�\��	�`X�Î>zA���*�(|���I"l�)n�
`o��+g�{`��C�+Mv����(��-�I"e��df슶.Q	�^d=�k�����0�f�:D5�)n����������-r�?���-f�k�I"Z�?B����kI"�	VQ� f�k�?���<�)n��-:�?�0N�?ly8*�[m����E>.|ۊ��X���&[�8����i��;n%��R��i�if�a�;nJ��d��,[�oڌ�O�af�i37������[�َ.|���yȤ4Gh_vI"����f�`��`�JdP�.D9W���c�k��`f�a	f�{���J���^�=��Nx��?
���`�k�	4S��?h�`�Jd
f�`i/SbI"��a�?H�$��aI"��z;��.D_+�i<��?ȃ��{�j��?��?e4̹�Jd��x
��+�����(�Jd��aX8�ִ��?%�� Z�)nA��aP���%�� u��p�Jd��$)����%�"՞?�Kv5��aȀ�	h:�҇�N������a��i��<^����V��?�
���{��?N��i%ۈTj��i�����`ef� �
�逎 j�!��f�i�V/0�����z�Kv�� ��ib��%I� f�`p��<�۞?$�<�J��d^�����h$�<���p{��{��Jd��ax��i��k1��0��\��?���k�?e���O�Jd?���ʇ� ٿ $��ڋ<��?Ȁ�	*{����?@1|~{�g�<�9��?�˜��<��6���I""�h*��<��{�,D�
�y9��_��Kvh� �%+�<�*��m�zm�@dv�,D%�j��G� Ё�{Y1D���{����{�o��?k{�,��<&�'�>D���7���ɴ�61D��{^��h���i��N⁕e~�`�|~ʱ ����1D�{�f1D.��i��2{�V�%��
-nM.�yɀ	f�jlσff1Dǘ	f�7�M�	f�i*J�>�z1$�<��-1Do�ߣ� 
1D��	f�;-n�fck��ܚ�ͣ��<��x��f%FR�<���y�"|~�?���ͣ����Ύ
`�Vm�	f��M"+�<���,DȲ��H�%*Nd f{�e ����!
�i�M"!f|~����at�f�`�e�a#�f�))��ݬ��-"f1D�Nd���e�Cv#f�a�S1D��	f�%+Nd���M"�#NdVm"v~;�ͣ?��Q�{�9���$f1Dw�ũ
��,��c��)��h�d~Y#G�nM1D��%�ۨ��&�1D�]-n@�aރ;:�a%f
 �1D2[��M"&�	f'��(N
 4{��
�,e�>37
 b9 :��a�5]J�Q�bmۨ��:
 ��a�ͣ�a�Cv<�	f�ao�b'��h�`B���=n,� ��hm h(�M")�%�1D3=n*�M"�U��c�Nd݃ͣ�e%�.�Q��52v��m�Y�+��a7��c�
δp��a,f�a���3��`JH�i��aY��cʇ�'R��--fNd.��J��'��cє�a�
`���`�{�.�c	��a/�cR��Yv��'0��agk���`oȣ"��ac��<1f
 2�c��{���T��at�޴(�Y���a{�~*��%(��3f�{�b1/֣c4�c5��'��0����;�I"��c#C h��{J��'U�{ՀtȀ��:�x�m��� 0J�%���`��í�A{� ��'� =�/�0�6��'���j�E��၂�f*7�cw��ixW�%@��h��{>?�Ȁ�0���H>?�=��a��{�8�8��a�B�hbu�5S��z>?�[��cD��a9�c��,� hA��$���Z�ek�
f� ����:��'o?��{9�c���`,�f*V��'�δu?�a��)%��`;f�$����<f�{v�iه��S��'��$Q���'(���OS�{ψG����'=f�{R�<�ψG�R�%�ہb_�3���x�?�>f�%?f�{8{�e��b���y`7���z#F9YAm9;n�D�W@�x.de��{�
�(4�"nAfDD큽,��%m�Hy*oR{�Bf@"뀸�9{�k��7@"x�"n"��bk?��#�+���b��"n��c֔�
��bt��c���X�?��Z�������׹Dv04@"&��bb�g8>?��H���)�e�3DDC2�{g�F9r�_?`��)��DDo?��#z@de3!�o?�{V$��<��Ł�DD�&��Ɓ[�JX�C� ��\�
�&J��>�
	�.�{g)�fB����zS@"��$�c+D�bJ�8�֔��ǁbEf�a���c����JҁbX6)T� �I��VP3D�!D_�`��x5	2���cm"ہ���C�.�u\��`F�b�@"��xC�`e�c�DD���D�aGf�`N!D��aH�G�If�a�!Dg�x��ۆ�2c@"�c�)!��)��F�g3D�C���aI��aa�ۥDDJ��a�[m��`�#�Kf@"�&��+M"q�6�	@"=e=5��oW8D|��a
�!|m h��`8D\�{~k�G�Lf8D�ڧ�:�ib���A����X�M��a�4b=���&,��a����]��Nf8DO��aȀ�H���Pf�a��%V8DQf8DI�ׁ��a���a��8|�!D=��a�� q���ڋ�go�۬G�V
G�W8D��a������+$�8���}	�#G/�`Rf�i[8D��cSf8D���`����Tf�a8D�����+�f+�`UfUd=qӫ��.�`+�`4�iMf��V�`9��������a��i�w��W�`҄�a�{~+��{���$Xf8DYf�b������>8D<`'3Ud(��Zf8D��cL�۞��a���E��a41�5x�m[f8D6�ۨ���\�`�08D偄Qg�x��8�]fUd-$�b���� ^f8D��Cd��$��N8DZ���)�i��5'$nĆ\��9Ud�,�x_��Ԛ_"5
�i*��$`�_"af ��	��� Cd��V��R��>zb�Bvx��d�X��$n[Q�&cf`*d捙e�8ث�ff$n����a�CdρR��z^��a�X�;Fl��h?$n���`ʣej'��a���`��<�g�Zd5�8ب�߂������$n�	�b3*fd��{�8�vP$n�br���
��b%?DބP=p��oe��<T�<�hf�xifUd���Udj�Cdkf Ѩ lf�b���'m�_"��*N�x�{���<�z��Hdބ���Q������+Ҁ_"Mm~��{{��'���=,�Cd�ۘ�I���Id�D3����8K��a7�nf�ax��-3��@�E�o���5m~	��f����C�����9{���N�$m~�3�ej4������gY=m~b�����{���h_mރvU�����[vo�%D>�&��Ě��ι�/b�ak����က-����%Dt#zk��2|,���
9�;�a��iL�%D˃\�
):��+pf�a��$���
۶�&r	��<|g�ie��ieC݀%D���5��z��%DG��xk[�	�a;�<�Ă�-k�G�k��*qf�aȀՒ�� �<�	�a�*D���{��	i�<D��b~}����������@rf�aH�`݁Xds�b)����״���ϓ�c���a�(m~t�<D��cu�b��Xd�,�`��<DԼ�{G��=n�Xd�b~���;Gd��$����s?�$G�F�۬����Ђ�z�`ev�cw�b���@��i�$��$`� ����v�آ���	4�[vz��[�Xd��Yv��$�+�Xd����C�%DGd���b�i�b�&Ȁ��j��X���{�xf�a��$\7�$�ׯ�ah�$�Gd&��h��S�Ȁꁾ��Nyf�a�C�ҀXd���iz��&���{f�aG�b~|S��<Ds6�a�_�`��	��ȨcҀXd��|�<D}�c�*E�����`��'����b~��<�`��&9לy��i��h��<D�򋷍�c~ff������L�<D�P_�;�Xd�� ����S��c�bi�$/��'Efo2��P_�f�$�ffi�$sY�������u$=nҒ�'SGd=�Ӹ�ff���&i�$'!}8
/u\��&x�ͣf*�b��$}GdI���Dn�0�c��:|̏�EIme�,���r�+\�ۚ�Yv'{��kO�����{>"J���hY�	�Q��c��YvaI"I"��<���'��o*`��Amۄf�+Am��(|eI"��<���i���$I"���&�)n�MvB�]"���heI"�)n�fmen�X�`:D�f���c���[)I"9�cl��.o��J��&�ݪYme��-%�c�j6�;n�me:D{��Y�?:I"
>@d[��{��{�)n��b��i�f:D��F��vQ�����f�����"\d=���|��B�"n(��n�G�>G���)n��b��|�ܣ$Ǵ
�`��ۭ &\dvˢr�<����5��K�`��I"~�Є�`e�4G6\d�f �I"٫�� ��F��bde(�a
deT� ���f 
��t#z3�4�I"�$���Jdg�<���ox�@"���*��ݍf �]v#�i�I"���bI"S��l��h����f &�,��e���iM]v&�,�͚b���n��d �C�ݑ�or>Ă2z��h�Y�h�f]v�?:��;n�f�i�
)5��h�ܣ�J\d���b�f %(��f\da{����)�������%[r�_�f]v�f�h]��+�\d�����%����Z�_or>Y�����  m]v�\d�f\dZ��f ){>(\ Q��.ւ��a@"�\d�����a�f�`��`LJ����a�7��m hꙏݴ��%�%���aT���Q'�h�f�{)��Z����{���h4�f�0�b���a�� _�é��Č�˭%�f�b3��`o����$�`{� ��,��fk㕣�b��a+�>D���f�`v��a�#>|���(��ۧ\Ї���<��h	�b�?�`���~Z���F�b�`�f�b��}�&�?k{�U�b�����f�`9�,D�X�D.��םf�h�N{�9-nզej�f�b�E���f�b5�?�t׽��%���$�,D~��a���a�:i*2[��oJ���f-n���a��%Ȁ�	��p��>̐,D
I���aH�k��ո%�������c8
-n
]i* ��fI���Cd	�fn�'DiB�b8��`������Cd;�z��Cd�f�b�M"‰ߣq��f�b߃	f��{�i*�M"����Cd�C�.�+�Ca��Cd��	f&��+�f�`��Cde�M"q3i*�f�bȀ�.O���<����z	�
ۿ�����a�M"�Cd�
�
���݀'D�۽5
Nd�d~�e��L���&�����}��Cd)���'D����Ғ�;�b��?�)��>�-nb��ר��\�&	���fNdMR)���&�ۅ�1�Ђ����G�"۽�1za��i�91z����/)o���{�Cd�)����V>P�Cd��&X��a�>�'����f�M"�f�'��Cd��fĄ�aD� ��c�)�
�+��Cd��Cd�����)Ӌۉ� ��6m h��@vݵCd�M"n�'D	�f��Cd�'D��	fց��>�cv����Cd��%$d~��a��c��Cd�F��۽�j6g� �Z��nO�`)���Z�aW��ah	�c��-Z�	���a?��`f2hv€��&�摷��`���a�
):��+�Z�R�c�I�`���+j2h;�%UFv��+<4l���{��`%}���+�f}��ң��2�
)�	�ùf�'��a���&k��O}Ђ��7��ao�+7� 7�x'�ߣ�f}����8F�(5�����)���a͇�`>)�ɁH��&.����b~���(��a,�o��,+��&��aÀQm���
t#zoZd	�ck�N��Ԫ�}5����r>U7}I�c����,(�&�@֣�'}�f�`�f
f9\�������a�f):m h�*�`Y��-֣ Gd8���fGd���G�>z�
f�V>��`<2ha
f/GdY��2��>�f%
fc��
��+��aԀ�E��`>�$h��ae�+!��4���a���`��*|(Gd��Jx��4o�,)Fv��G�'Ҁ��0���X��X��"���f�%#7��f
fĂ�b�Gd�)�+i��b��������f)��b���2�:��{X���I)Gd������%��b�X�S()�LDDe���۽
��o�۸��xa�۾�G��b~#z�۽�C�Z2���bY&���-hF���b��w~DD���`����	�@�⣓aDD����b����� nW
f�����DvGd�a1zj��`�DD��)� nȒ��ňݨQ�px�܌#�	ۋb�fDva��"�NGd��ܰ� nRDD�ADD�� n	��	�y�fDD@�ҳ��ң��}S��cҀ}�%GS���,;��cm��X�UB��d�j���)�fDDh-!D�� n�3D;��`@��'B!DI"Z��v� n���)!ٙ���I"%K�a�T!D���P)!�Rme���`�� nl��`��ʢI��DD�	mep��I"o�a�\�d��ܚ
�kDD|��7�����5z���w������
DD�f!D�6�a	�`�I"}��c��I"$!Dv��n� n�3Dm���fDDi$:��f!D��蠁�
���I"�� n]�Z�w��6�{~�}�f!D���(���{~��i~��l�}j� nܶ�xȀ��ґ4So�‪H�)�S��f�i(�48o�&>�!|ú�%m_�I"����Ă!|�����3�!D���)!�5z�,�C!D�D!Dk����x��4c崼F�ah�\��������D�aZ�(
��V4S���!DÀ�"���{�I"�h�!D3�i~Z��%���SI"S���)!��́�?I"��a5��݀�!|}���c�����!|Y)!N�Jd��be��	�`��Jd����t�`����`Ł[m��&�&(�U ���&�֣�f= ��Jd=��㧄KvZ��+�f�`��+���i�	}��́����'�{i�`��L����������<�i�'�bҀ�&��O�[�a�)���L��`��Jd���ބ2�>���f�b�G�����_�hu�JdoG�����f�bXJ摾�6�	�	֣O ٳf�'O��%ϤJdl��z��	}� �c�����&o�|>��KvԀ�����=
�>�b8��7�b:"�M֣��Ц��d��f��ca��i���D�)�cډZdr��D���D��`��f��f�J$n��b��@c�bo��D��cc��ݿ�x��ϋ�x5���'6n���!€O�
5���g�����_�c�QrX��&�`��G��	}ѿ�DZ�wp���	}L�5z���`��L��<z��f(�����D�`/�f��&l��"����	}=%�����ܣo*z����t�a{�k�|>�	�c��Iv��	}��D?m~��fJ��`��+���x>�|>$no��o�9�c�%D�۫����%D�-h����Q�M"��S��M"� ��fm~�f�c��	� C��=%|��M"��E���%D�*zs����w�O�.z%�f��&�π���Z���X���ZNd��DK��
o��D����m��%D(����o�)�%D��h_�o~!��R��`��%D��hȀ����D�fNdo�_6�Md�4Nde�TU\�����m~�ң�Ndk2�Nd[���h���w��Ҁ�`t�h�0��%D_�*A.��~��`Ԡ�b<�&:=����@9��&:���#��Nz����ٺcb���5z�fNd2z���k��b
Nd��b��M"�f
}�
�+�%Do����><�%���{�X�=�Q흗%D��h��%�M"L9X�}��c��bH�,�M"s���f�߂&:#�%D�( �f
}�fNd��M"�f L�&:�X�Y�M"��%D �D
}. ��bNd�
}U�Yv��ht�)S����ań�+
�8K
}o��i��W��a�fNd
}+�&-��Nd˿�at��i�f�h�X���5z8zЁ%`oX�AI �� b@9]f�۽ŋ�c2����۽����a�[��ffc*X��;=f���bX�H�f�{c/u 
�?�f:D�<ze��a����)f���(3�`/#�{��2kr>>�2���{o��Y��Q{�v���4f(,
}8
/uc����o��<j��f��{�e
�<�۾�<z�
}�(|X:D遂b�f (5
}%��o��<3����:|���*�{��`��i)�(|���b��8����i�f�b��:|w�i�"n�V>g �~%��m�(|��C���b�(|JӖ�����h�(|��ik�\����i��"n���<9��b?���֟�iy��bo��aّ�awfՇ�h�6Mv�fC�i(M�w�#��(|� [�b��b�*f�۽S���9���[�h�
�E�r�+C�D�fV�{�۽����{o?�Z�`Ԁ��`(��fۮo>r>Ȁ��6�;n֣,E�`R�(|ψ��X�I�i�(|6��
�j��`k��u�"ng�brF)��C��&	��h����Ł�b{�xo��<N��u�@"���<�[�ݣ��bg�&�V>_�4G����g\d���i�iŁ�b�)���_9W��{k�)i�����)=�ox�crݣ��b��&��d{67�bp�-��cgMv�= ރ;�	\d�m#�2r֣ o�πÀ���c�)nZ���k��H
�&I
Ӹ�@"����)��ݱ������J���	��aH>��
g�a��܅�<u�)nÀ���_�M9��a��;nҡ�oݣ�Ԥ�b��$�5�܀��fv,��{�p���v�q�� {���
��{��&�R	2�5z�)g�a(�$�I�a�]vP7��[��p���Y&�l��Z���{���%� 	%۲� ��h��x�'۟8z�<zy	2Y�� XD��I��`ه�h�,	%ۣ	�%�=n\��{e��hg�,�� ��{,ͩ,9_�]�����6�8^}o��+	�� ���{��3�>�%d�5zӃ<z� ~�C�m�8g	2��ao�,D3�y	2���:�,D�
�,p1z��h� ��,y	2��b�,Dg}�9�a(���,D��8D6>|��� ��&�Bv׉澂�%�?�a���GdUdv��$@��,��,g�8o�πy	2���{��8U�,Dǔ�{�-nc�xӃ��c.�����?��+�,D��{(�+�� ՝�h�Ud�>DU�,D�Ud�۷�f���D��h��,D��Ƅ�h gUd�8�� s� n�+̏��Ђ���{!� "gUd#�,D��bm�8\ې"UdcFmc
�ߧ�{��`;�{'}e&�݀�bc�ig��$��D%gUdIUd&�f���`d�{j�Cd�	f$����g�5z'g�(�,D��`���{=�P�6�bf6�������ȉ��(�f^��{Ȁ選�%)�,DԀ�oA��b���V5Udj
 0��
*�{����{o��c*-nS��dʎ֊n��`�WÀ��+g
 ���|[/,�{-gUdY��j�����o�.g
 Bd~E�ۮ��Ud8[���8d>]fUdݣo��&l��-���&��ƒ�]6�b�}~QҬ��D���g��/��&0g�{$�ܸ�ۦ�{�
�{β�`!��x���{��A�Cdm\
 ��.1�b��D�h��&���DS�����&2��`��f3�Cd%I9��d���{�ۈ�9�"��`��h��{4��`%�{��E�5g�h�:Z�Y�ho�{e6g�7g�|���5��8��&9��&X�y���y0r�	�
 DI9�
��{!���
 :g�hz۩;h�ie��Adm��;g�h:C8z�b��-<�%%8zm)�{e 'I9`�:���uՀs�:��=g
 ��h	���� ��a���s\Y�}>�xw�5zT�偖��I�a���&��{��"R��-�b~�8z\��d���&�ۡ��~7�E�%�{?�磔�+�H�	��9s�_$}��G�F�o��E2��&�5z@�b~��&^�Xd}O�{Agۼ��+��Bg�h���.�{��*ۧ��h]�Xd�):>!�I9���A=nCg
f(@��I5S�{e!��	����,k㮾�&�Dg
f��Xkډ�(
fʞ5z"�f*���i�=�b�8zz�Y�Eg�{Fg (
f{�5zj#�b�]�bλ�h���=ƒ�L�Yv	�{H8zGg�{=���e
fr∛5zV4�`Hg}�� ���h��{��a��	}.7�ԭ�`Ig�b�}��`[���U-g�h�{/R�Ap}��XdV
�b��.Dvf�`\�b~Jg
fk�ۨ�{�bKgDDL�	}M�Xd�5z㕦d	�j]�[��b�DDI�避�{a�5zNgDD����i����Og�i��DvrգP�Xdx�4Z���ODDQgDDm[�*��R,
fRgDDSgDv�
f{��h�Dv,�*������{Tg�i�{m�ې��{�TDD!��`Ug�b�9<���{�&
fVgDv�^�Wg�i�.�bl�C���XgDD]'�Y� ��xe��/DD^��{Zg�i[gDD��`?��b�� d�i:F�b����t�i� \g�`���1�	�	�8[�]��{�i���{�\�� ���Wdv��$�)e��X���J�n���<z���h}��{v������)��`1� ���܂i?3��i%� !DF��z�Ed���`^gDDC�i0�d93D��i~_g�i����F
F��'�i�{~(VB`��zV
?��
C�l
DvA� agDDb"��b��/w��aDD��>��z
�i>�����i@K\d���z~^\de�2Δi~j�B��{k�υ3D6�^��\dw��/�!g�M98��zT��(�hm[�8z.K\de��\� @K\d������hcg\dd��z��<z���$eg\df��/y�g���-ه<������`d��g��{?\d�3D�&�Z\d?�9���g��/�I"o����F"{�1�\dϗ�{��>kr>����)\d:]vWI"o�{~�/hg]v[X\d�&߁�z��F"��ށc�2G]v^C�h&
}v���i��{N�83�hj��z˂���F"9�Z�%����j���,k��z��{~�>`��/n��>�2t e��&l�>m��{ԊF"t�>e�M9n�F"og :���l8	)��{��}9�,Yc\d�t�V���ו�/��a����[�I���=3� j

}�%]v���(��aS\d�&�hp� qg\d�C rg$n���.fJZ�h@"�#�zs�}k��`8�at��{ug�{��؀v�<z]��{�� ��Jd���a΂ tF�{*]vr>wg$n�=ux��aB
�fz&9��aX�`�{��{cD]vY��{y�}���k�� ]�{)�'d��C*{�G]v���(��۹�,�����?����`]vǂ�*�]vS��&z�^�{� #Q]v��УWf��L���b��*O	)����Q�58}F�{ރb��j�}.��d����;�B�}k���X�'� �Hd�$ ����]� ;�z^@m~l��aP��B�y����&�� r��a�����J�Μ)�����Ȁ	��� 9�Hd|��a3��-�"�{k��	 �z���a6nef�Ȁށ���a��Cd1�a�'=�%D��Cdo�z%��B�{@fi����E���Cd��-�'f�磳L�{W��i
�I�_$n% �{Ԁ̢���i���i-$�{+�<�}g�b��a+��i��!gނ���-�b����~��iX��^���
M��[Q{�݀Bvo,��<���IvF�+<�ی�%DS
�h-�Iv�){�Iv�[����&��%D�Z���i
�+{�f�Cd
��7�)v�)Q��h��>~�Ab�����i�e� #�牣x�����j�҇W"�e�
#�z"�a���i������&:�
[�a����i�g�z���BD��
��x��7t�%�B�z���o�za�<z+�<�D��{��Iv���	�d�?��{X�9���脙-}۳��i/�Q��%D.��ʉ�i�g�a�g}~À�x��Cd�΁ܒs���M"��[v:�Cd���_}~%�<z�_"��:ܢ���i*�%]�%���i	��,�M"�� ��>��c���5�)�g�a���x��g�ϋY�g�%��ᾀyԎ��x����h�'#�Ղk~?��"7�a|���9�VW�~+���������e�a��o��>#�hgK^d�?:DXW�@�����<z99:a��-��i���,�<:D�ۊg�i���.}���:D��jq#`�����g}��h�3�i):Dc�c2��偆X��h�]d��b~��h3�c��g�h�<z,F������"^d���dL:�h�i��Bm“�z�g�iܛ�Q��,��b~[>�iX�>�������a~���Ȁށ�:|�g�as4b�g�a�g�h�(|���>��:|7�7
�a.0�$�i�+D��h)�+!��(|m��()�:|��򣇱*�`�&���"��b��:|`�׀�t��i����!Mvj��,�(|�b~y�iv��/�at��,Ӵ<z�:|D�	�gMv������$�iO}A�;n�g:D,�io���O+JX:D�U J��h��;n倛bX�iS}��;n��b/��c}�
�d\��e�z�HЙg�iw�m*�
:D:}
�x�e�MJ��\��ݚg�iD=^d}�;n��b]]�iͷ�<�У�g�iY�(|���m��y�r�<����埇�b����b~%��:|����bj�.g ��7!��A��<�<����y��?���b�3 ˋ(|�4G�ۚ'Mv��+f��&;�F��g ��g�)!�۽�:�
܀b׀���%���b j��J�(�� �;�i���:7��Mv4���
 i ��i��;n��<�� 
�;n��&� ���b�T�>�ָ�[��5�j�i�
�+2΀b� ���<���E �� w�>�g��o�'+C���;nxd�h�����>��b� x 
�u\	�^���b���R�k��	�'��b����g�"�g�{r�R�Ă�$:77�9��au� {��2�շ�&�h�� 5o��&�8��<�£�G �����2�Ҁ�,����7��b
 m
f���&�.�h\FmQ���f~�g E
 �Fm��<��$u���<�:� �#g�	>|��h��b�g����(�>|i�`���+<��o�h�̀b����W҅�߂+�i��Ƙ��h��fP��fȷ�����b!fҀ#g�g�>�,Dй �g�i��b���: !M� 9�>D-�>DT� ��8�\1"�bX�W1����$>|��>D{&>|�C�{���7�il��M�>Dh���`U�,D>f��lL�>Dv�(�gfeJf�����h�g�i��,D�"fof�g�8ތX�g�h��,D{�8�g"��,Dlf~8Efϋ��5o���+�>D9�,D1��h7��xNf����>DNfb�,D�g-n��f��h��	}���R�f3��{۶g>|��T/��#g�f%��i��
v�>D=��{�f��1�y��i8�i_�9��'�8�۝��զ�&��f���i�g�bp6�ij���p/�z>|����F�g�i@�8��	}��h��>D���ݼ�>D�g�8��ݸ��`կf�>D�������h���9[�k���	}]�>DR���,D���hܩ�h9��i&������gf<��oG��m���,D�-n$!gN�Tp�f���@v���{����&“�h���)\f�,���=����{��a��&Ђ�`���׉��d~oԣ��5lm,a� ����Q���{�� A�f΀ �f{��Y�"u��	}��a;�fX�����`G� ���{ʱ�a��2�fÀ"u9�@v���h���a�)"��{���`�4�ڊB��f���`��&!v�@v��޺�i;� I9L��p���?���i*� Z��h+��`߂ ĉ&!X)7�Ad-QI9��&!+��>0��(�Xb���IȀ�f�Ad��>m����d)�	�1)r�<���>UB�Ad��{9�׫�{���-e���Ad��i�� &��%�)�����@v�M" ���&�Ad�ۍ�Ad'�fʐF�v���M"�a@)�gNd���{�	=")�疖��>�wH�Ad����')���j(����u
��>�])���d�����)�Y�� �B",i*���Nd���`���v����� ��f*N�Ad��Xd>)��V���J�z��-��8Q�z)�����&���Rg�?
X�խ>b1
}�i*>��&ʹ�>��Ad���>�g)x�+��Ad�	�Q�
}���.)��>sXb���&ȀÀ��h,!g]�Ad�{�g�h���&��E�ZM�{��iA�h�4�h��Ad���6��&��}��k~̛���ou�h��qӤ*
}j���h��1��)u�Yvm$�hރ:F���R�d������)[�݁)u
�k~�����	f�g۱�k~
�ѻG/)�g�hg-!r(�hF�})��显��`���i�g)�ۘDD��}�3�{��)uk�)u���{9����<RRDD��je�h
�ѻy��&b�$��<=
��gDD�>�f����)u�gDD;��<�Yv�I�{%eDD�g�{��)uz�z,DDV�	f�����{�
}��{P�hY�a�DD�� n��o����gDD�� n��}?D�{Ŝ n9DD�g�{��|LDD9�;�����'��o�� n�FDD���	ƁWBDDL>�{�g
}��iDD�)u���ׇ�{偧N�&��� nm�h�gDD�@�h�"n}� n�gDD�� n�����V DDt!DR!�>�)u4=!D�gEd������i_� n�!D����9x���i~~�����iZ
�{o!D��i�gۊ���gDD�������A� ndDD�[����`� �{�g!D5�i~��i!NEd&�"n�!D�{~ĉ�i�gDD�!De�}�(�{�gDD6�{���9�2��gDDP� nj�M9�>���i�/�O�i~�:�M� neh�Kj�	��S�}G�)ܐ"n�+!�%���"no!D��{~wVp(�}�o�
Fh��i٣�}���o��i��<��W�������P���i
��oj��+.��ױ��>D�>!���c� N��:b�>]��i,!D�Ed��)���{��g��[��g!D��z##\d�":e�G����{7�e���F"7�x�g!D���{�x���+�g!D�gEd#41�Xbw\d±�{�F"��&�\b������8�-ur>�-uD�^���_9���{��E�OS
f���"���{�-u���i9j8���{c��Dh�Y�9�F"����-uO�i~h�i��+!���{4[�`��m*���{�(
ff6�`���ip���+�g-u'���"�ň�h^�J�YI�À73\d/��7o��+���+��a��ȃ��h�iM�>�g-u�\dͳ�a��<5
f��Ih-uh\d
�=f�`l�xӴ	2��r}��+0�$n���{[��{�Q������{����{"$nǐF"�)H�h�:$n���o����Z���{� �h
�F":�x��aa$n��ih�h���{��ݩ��"�F"
��1�)��aQ8h$n�٣���h-u��Hd4$nr��oW��a$n<�Hd'�����HdՀI���-��G�r�Hd�*�hk����9�[��˕S)!d�Ph��h�ih�ho�Hd�+�g���$��	䇫������9�P��i�Zd�Hd	h�`�.gZ&�%|���(R%|��Hd�Ud��+�i%�Hdw������Xb
�j��Hd��a�+U>�i?�$n:�Hd�%|N*$n�dm~$����i�����%D��Cd�%D�>�i�
�;�xӣ%|�%Dȃ�J
hۂ����B���x|�%D�7�i�[v�VB]���xh�i��iR%|h�i΂�ݡ.g�ش
�h�M�i_y9$�hh�i+��a9�;ǬHdw5�i�%|��%D\�{:X�'�_"���a	�%D��{�j.�Hd�%D�[v��{*]�h	 h�{��"X�%�٨�a(�%D��Hd�%D��ZdO��az����٫��	)'!��j)�%Dg%|���a�_"h�iȀ�	�%D�%|��i��<� �%Dh�{��i��x	�b��%|ʵ������m~h�i?$�k��fF$Ad�	�i��{�/ĉ&:����ȃS��"�i�%D�`*h}~o�ϣw��;�Q���<J����_��%D�K"h ��%D�	)3*K"8�Cdʺ�VB):�����
	)u�%D�'Db��|ǖŮk[��`*��o		)h�{v�<��U���{�5`*e��t��,�Xb�5�{��%h�-e`xt��,R��h,@9>��AՏ���,;�ţ���	��3�{Ɓ�X�{ �h���T � `*	�B9�x�{٣`x�ێ
Ŷ!h Ɛ'�, �6�z'�a�֒�	:D"h�i����=#h�a{�h�`�脀]dtXb��aݏ�0�
����i��+�`�#g���,$h�z��ax�Kf%h�a��:|��`o��:D&h�zϒ(|E�b~��fP��Fh�K"���h�(|��q�'�*|*�Bm�O�z�L�a���(|]	)��a`x�� ��=�ϣif(�:|Tt\
	)���م`�z]	)�
tQ֋X5����()�:|��a*h�h��(|�Nd��j���+�(|������x�=f~�`Àx��(|�:|�b~(�ف�e:�x���Y���Y9Xb�aw��� ��9�e�����hv����Df��)n���?,h:D-h�ap
7����À��F�z@�����b���D�a�����h��=��i<�+.h�h/h�a�	����j=��h���0h�z�a@��+��1��i�����:�(|�����i��m�b~�)nNi�j����h
��iǑC�R�(|�����ɐXd�3�O��T�4Z��
�&�(|��Հ��tf���…4G;�j�a�f��)nI�߁��2hf��E��f(���{�g�x��n�3hf€ �+��x�V���'f��&!��aO��d��`���k-��4� 5��`��fց���,���`����#��b6� ��a7�;n.U;�7�>� ���`��v٣�	�\�u�8� ��`�4Z9���7+:;I"΂ ��>��)n6����v�&!:��+��_�>:��h���a��i7��`b� ��N�g��h��t���`���iw�q�
�&���h�� \�&��'�5�>��<?���;� d�&!�&��^<� ��d�h@
�o
H'�	J��9�2�߁�agm,Ё�����9�
)��'����
�S:[�\���)Z��$�>|=h\d>� 56uB�<����'��a� j�-?��a�Y\d� O)]��`�� D�
)�EC���<�6u	�x���@� w���m��b'��0���'҇%���=L�,Dk�í�����>N�,D���`{{��>|2�,DA� ���H�L�>��ȃIB�,D:�>*�z� �w���<�,D�Y>|56u���C�>D70��-#����>D����X�D�.D�-n��+�
)�.D��,D��>D�]v�)�E-nDz,D3����v��,DȀ߁�>D΁&����D����h�����OL\d{���xEh�h�,D��j�b>v�
)F�,D<�����v�
)-n��0�)l�j��
)z�����h���	G��iH�Ym5W-ny�h���i��,D���hR�	f�P\d.�9�Ih.|\d �x�-n�	��$�j��hI]d~�c8���{J��a��� �)�7d~���V;{��h��y�h@��{�{�%)K�,DL�h���$��{�F�a����3'M�,D�&	�(,ǡ,D�T2z�a9��aN�,D)�a$���s ���ޠId~�-n�}ۢ˸Y�`��.-ny-n��n{��iO�h��"�U��b��$�-n4d~�b>s'[�aQ��0j�o�W-n<J�`�"�h� �}Ph�aOu�1�{� �a�h��	ld~,:f,Qh�h�����Ym�d~ZG#D��ao�<����&�
�h
�X&��h�!#DXy�ҡ��B��i3X�5�Ym��h�Q�`#D���,���hc?#DR��aҀ}�I9�!#D��zShI9(��逥�)j6#DTh�b�Z��� �1D��$��C��	��Ɓ��Uh�az�M",�o���8��F=��`x(���*�{�v+[d~����#D�voo�b���V��&f#D���'���'�vWh�a��#|��Z���'��Ad���>τ4弁�>e��&�����#|Q�W-�vX�}�ˀ$��f���Y��&�X#DG��h	�W����&���H�)�#|� i*�������2Zh#DR��'���P�M"8
#D�o�5i*�Xb�#D"�f*�2�$��v�)Łf*2{���%!��dX�o#D�)��(?#D9�����z��#D�}~��>o�i*��n���@�,��)H��ҡ��[�)/:}~9��Xb\�a)�)��M"�-�QoH�a���\�)]h}~�)!��~8�#|��'��⁔V^�#|��ak�)_h���}~��f�#D��;ue���`h�ax��+�-�aj�4T)!J��'-7)!k����zk:�x��;�a��:h�G��	2�ӣ�(�b�G�a�f(�x�3}~�"Y�b	�a��Ӑ;uƁVW%�x��/�F9�	��d})tXbb�fchDDwE�a��)��Ӂ)dhDD�����i�>DD(���[6��}(�)أ'3}~r�f�{�,�f*���e�;u���`!�)fh)5��:DD�Z�3�&W�vUDDgh}���.W�v��Y�j��a���hhDD��)k��]m�4}~�ϣ�蹀�$��&�Ԓ����^أ9)!Ɓ{�)�����++
�jih}�(��r�a��b~RcDDf6)J%�ajhDD@�iւG���a]])��7.�����a/F)�;ukhDD�/��Áj.g�G�ˁ�je=�Xk��3Wd3��t
�ց��_���;�x�<�fC�=�&D3D�;ul�{~��+w���mh�j����Ȁ��i~m&����]}��<�J�e�/nhDD��'D73D���fg�9�}�WdDDc��{��`)Vvo�'Dph)q�b�����aڋ�`�d�&���)��a�3D"�M9�j�l��%�������)�	FaP�'D���`��'D��b~�
����{�'D�&�f
�|
��r��a��&s�%��b~Њ�`!��AW�k,v����'D���aj��?j�k,�'DӐ�`o�i~��}z�8���`���{۠}���`���>���bth3D\�xӎ�>u�b���`���5C�}�'v�E�S�8�wh'|�"!Ă�>��'D۲�&���Go&x�'D�������*#�>y�'Dz�b�'D �i~�'D2��&���ż���w�'D��ݣr������a���Ă�&��{�'D|��a��F"Ԃ�%\��a		)��`}�}�\m`x~��`"�M9��'D�
�x�����E�φ���'Dh�>3��bf��8��mx�E�䅀b4��`�`1���d�ހd��	b�<����Q�r�:1�j{b>΂����		Y"�EC����B��2y�I�E�7�>�z��d���€�皀�D攀b���&�6nه��6n$Ё�-!�)*Do���a%�C����d�h�a$>�€b�h8D�"*DX��>��&զ�bͦ>���&́X��5*Do����F�$uX��b���a�]*DΎ@mЮ�b�*D�Zd���J��=6n*D_�*|X�Zd́ a���]�Zd�8Du(�a�/��'9���k�e��]*D�$u=W�ˆHd���@m��Zd(��a�a���d�̣|�Hd8D��Zd��a�h��8/��aȀ���@m��*|��Hd݀@mb�Zd��+%��lj�Zd/��b��ߊ�*|k��jp������Zd"�be������w�w�!D�*D�Zde/!D~����
2em~������h��`�/u�͢�$uw�a�6n�Zd�h
	�`�h*D"� �,m~��8���j
N��`߂Zd$T�aWV*Dg�d�6n�h*D��L��`"�}�	�a?��1�+n���UB�a�&�hv�Iv������i��[v���h�8���

�G���Zd0����������(�j���i���i�@{#��j��Zd��[vɎbeԂ8���2��PҶ�C�T�*|��@m�Zd��*D�@d��Hd�;m~�#U��S�8�a��d��[vv�J����!D��*�tA�ց}����c��&!p��&���_��Հ�� ΁=�&b�����j�!D�Gm~k�������a!Dt��Ђ��t!D+0�7Ć[v�h�a'��j������E�#��%��{�^d"�_��+n�i*���Փh�&��}��&Tӣ׀u�G�[v+��$���Ђ�����Ci*�9:�&߁�&D�G��9:kJ�4�&'��j�iЁ�n� ti*��%� �D�h�&z�8�{b>#�uIg�djrx�
�����7�ᅊ-:��{)B7�ੁ��(�&1�ી�	�=''A)�ՠ$n�&�hDm�#�����D�&4��-�-:k��)���������h:D��+��4�h:D`x	�-:�h:D�h�a�h^d� =�@99:D�M�&�^dO����h:D�h^d�hDm�h@9�Dm��<�L�]:D�h.|�^dՏ_Dm�h�a����偔��.Dه�cz�ݡh@9e�/�Dm��:|�@9�A:DN.|���b�4^d*�;"#:D��+&�C��:|�hDm[�.Dl�&�@9�&Mv^aDmA#��:|Ti*�<.|u(=n�h�ah��-:��.D�֢�#=n��x�:D��.D"@:DF��d�=n��:|�h�j��bO�&���Mv(�:|����E�}���`��%1m�8��:|��-:�����)r
Mv��:|��j؍;n��%�h&Mv8Dmd^dJC}m���aG:D�h^d��t�!*��*#C�i��h��c(���a9���/`:D���i����h@9�h:D�h^d{Dm��i�:D�h��a��a�����J�ւ@9P�:|^��h��L�V�;n���"�mx���F֢�8���hx��ag�%D�.D:�:|h&ɗ%D��.D���a��c2O�4Zp�(0�:|��`~߁L����ނ�`~���<>�L��ˁ&���b�u�����
�d���	\�������@9�=n��}��ه�݈�`~Q��g)��� 
�$���G����Mv��=7 �`~���+[Y��	�>s[����a��;n�8�/g���/�h1D��)u�h�~�1D����f��8��F9'B�~o���,�F9�h1D�8g&�����O.�Bm��;n�F9;���B@1D�Am���a��%D
B1D���i�F9ه�aU�F9|��F����h1Dl��h���n��jR�����F96��8���θ�F9�U1D�����>��j�h1D���&6��8�
�Zt���L����Fmh! �"�dt���s�)�hF9����&�R�d��`~�)�h1D?	D��7\����)���e#����N>|J��[����)�G�l 耣�D�d�$>|��o��)k�~8
1D)
>|PV@"�hf*��_�é�1D��d�fC�>��ܸ�¦>�1D�,�أ�>Dg�>Dt�F9���8�h1Dc�>DD"\z������Gf��>D��>�>D��G>|=Ռ�1Dc�>D���ݧ5Zz1D��>DY�(|g� 瀹8���7-uv��%�5G�)�hck��h>|΁ʣ	�O�cr?n���+�>D�>|��ekoأ� Ɓ>D��>D�T�a�O"τ\�)S-u݃��frxل�輁��Ղ)n��)
��&�>D��)�d�)�d�a�?>|��=:m���h>|jrx$_�(�a�h>|��a���8ȃz1>�>Dy�f{�E��$ck�xj���h�c���a�h�a�!>|���,��,�h֢��->|��)n����h� !wE�a���>��Im��׼Im��Fu=���>Dt�Im�Y7�b�#��Im.�)n�����>D���+�W���>��>D��Im��b�;́>Dx����Im����h�&	)ɧ,��adž@v���
֢e�"up���d)��aQ�&�!)"��a����D�&H�&��~h�8����)��s��z�8ؘ��)����x�,Ɓw'9�޿S���h�'9����T��bd�D��Q6�<�b��,��Im�d�&R�����Im��,$��h)��,ւ@v%���.�Im����'Z��أ���,�h�&9���Im�@vu�
)��aM5��瀧,ѧImk�U /�
)���m�����a{�����=�E��Cd�)�Z����h���=�E�5��?5�{b�(�G��Cd��Ad�K9�4����mx�hd~�_"��Ad,%)�� �h)��&�����@v��&��Ad��&9T�bm�Cd�>���'�����CdX���mx`x�
�d���>�E��"u��Ad�h�&D�uj��Ad�&�w3�&��n,5}����%���fأ�	Z�w����e|_�}�AdY�
)r4}��&�}b�&�����&_��b��I
Ւ�	&*����Ad,�f*�Ad����E��h�%Łf*��Vʓ_��CdQ�}�4Z�ڏ����mx�R-n��f*�a'D��f*��>���������Ad�hoe��:'׽f*Mmg�,D�ˁ�Ad��ˣb�Y�(oe��a�h�h֪,D��AdD�Ad��f*��Cd�	���Ad6���Łf*�hMm€�+��	f��h���ด��d-n�>I9�&��f*�1	}k�	f�h}�Mm��f*P��c$���h��X�:vW�k,����Mm��}����j-n���T!C��RҰJMm5�	fzMm�h-n@}�BMm�%A�h�y*�������hJ����n�6DD��!�ˁ�B"G?DD��aq�*�ރ����f*�hDv��n�	f�hDD�h8D3�	f����hDD�h8D��}�C�yI8D:�a9�	���?8D��ڢ�hDD����@m;�b~�h8D�7���۷.}~ADv�:g�DD�Mm��6I9\ZDD='8Dl
 ń��hDD�8D�Mm�hoe&Dv�A��Mm��8��hDD��@mY�8��#Mm�SDD��!��a݇��8WDDR�8|�9�h�aWA!Ds�@mj���NU#�Qm�h�GA�l#�*DD�Ed���s�	f�hDDu�a��8�EdVEd��@m ��)?b8D��`�!Dk{�g!D8DZ���ێ��&�h8D�hEd�O!D�8D�hDDv�!�:�{~,DD#!D�(8D�A�!Ed�? J�W= v��<�h8D< �hEd�DDg"8De*�a�����hDD���a�
��hDD+Ed��a�_�ajDD�����V�{~��%����5�Y
�}8c�a�Ed	��
�{>B[���H�*Ed̝8|݀�%9��E�b~{�@����^S�8ؑ�:�{~��`�T�7��&�@m]�y��&��-ʇ8D���Dܒ�l���/�IEd��	P�8� ��{�7Ed���֒�!�h!D<+A��GEd�h�a�h c=u��j���&�)iEdo=
�$Q�!��` ��`�cEd��i�$ w���AEdeP�� g��&
�a*#�>��Dv��M�a�{~�%Y�O
 ���
YϜE�T�E�Fm*{���{~a�~iI"�����i~�^��4�o�������i
f����{~I���(��)!��}� 
f��}�*8����[�������b��h��{�
)!#I"�8����D�:�E�W�	����i�}*�t��zK�	n")!��M9��AQ�2��d�	}�3)![�ه�cŁ�<���j#Dm��c������ܠ	}������~Ӵ^�<| # Ё��(j8�ˣ\�dժ^�ւ���M9s<|8�8�*Dm�bo�d~C��^�ER<|�bDm<��1)|�<|��&ؠڢ����|jh��b2<|~��ciDm�8��	}�ۘ�Hd��<D�.D�x{�Hd"�Zd	i<|��c�Hd���
�.Dt�<D_Ԥ	�F�b9�7����+���Z�2i
fi<|
�<D���~��Zd{>�<DS}�X<|g�	}M)!Q%}�H����X2��jHK=n�Hd
��� m~��������݀Zd�<D�%����Hd����f$<|2�E�.m~�}X!%|�����HdA��Hd(�H��b}z�<D�O�<݃ˣ�����M9		)i)5�2wS%|��z�{�6[�i}������a�Hd����� ~VB�,<|��%D��a���z�Dm�	<|��<Dif�7V?9�� �`f�@92��Q����f�r���z�Hd�� ]f��Zd-�<D�� v�J�Hdi�bq� ܟ�z�D�<D� �
�܍HdQ�C�<Di�b�"f�Hdkf����o&� ����(�b�2�̴�\�b�f��a�%|��"n�#}�	)�D � �;�b!i3D)� �f*����z�[f"i},
�9���א���O}�찄�f��8~��hx6�Q�2.%|	��h�X��
}@�b�	)#i�&)�D$� �)�b��F9�^���!�� ��*'��D�Iv��>/#DDK"c�ˣ�Iv�{��f%� =�b����6�|jiBf� z��؅�a��~���a���h�a�b� ���'�F9&i�&�����1Y�D.� �9����b��(�Dj� �#DX�$6K"��b$4�b'i
}(�M"b�D\������)i�b�X��)*��Ȁܒ*i�b4f��b�M9+�?:\	}{xj�|j	\Nd��k B�"n��<��c,ifWf��6ONd��&:�7����a�`8��������`�Vmjxj-i�`��$��c3��d��hЁ��.i�cR
}�%/iVm��&�^dso�������4@���:�F9ބ��遂b=�T��f��T�;�����i(�&�Vm���(-D�{�,3^d遂b����|j��X�#}~T�ɢ�Mv�K"u%Mv���b�i���b|zK*
�`k�Y�ތX�AMv+(X�	�z0i�`s]Mv
��fz��b�zgX�z�M"�ǂb;�i#m� B�z��fL/f��?:{�� ��)n����Im���y9��x���wEMvƁ�b5��yo��oHMv����ރ��m��a�%f�{�@"��a��;u1i}~�z�x8w����f���0���{��}~2�!g;=�`���,@"���h�a�3@".MvtJ����:��a3�Im�M��4i�aD�ʕVj���߇�j�������cȀ����b�㯩��t������b�!g;�Imd�!gT���aρ9 
��x����� ���b��:�����4G�&Mve��a7Mvj_���/��{	�摈�a��E��z҇��Om��%�Mv���g��z��y,Q	��{��[�
���
_�b�jJ�_��H��a2����>�
���a�g����h'��a��/@�;u2������ab�U]��\c� �jV��a�|M���m��q�iC��\�d�UB}�Iml��?��=�<�҇'�	�Imr�8ի	�y��]��%-�
)�f��T 
����Cd	��[Z�Cd5�!g�,��fo���l3��6�Cd��b�'D%�!g[9��ȃ��r��k��Cdo�c��b'�o����Cd� �<g]�Cd)��� �`~7�Cd�����,���b8i}�{���m*8�,m�CdԂ|���B�N�t}9� ��ׇ�,���-�|���8�:i����{;�W��*��ݓ��0<�Cd=� �fP>� =���Ă=!|�,�Oml����� +}Ё8�)�,D�>*aw{xj�{��� ?i:DȀ˸ݴ ���h�C:D@� H:D���f��&A� !�CdB� Td2� � �,����:|j� BiMmC�|j��>R}D�Cd,�<�Y����G� ��<��c̘Cd���� \�ԣEi�>.Mm9� ��0F�Cd��&��b��>�� �{>h�>�D):���b��;nq'�>��<�tg�-|�t���?��Mm�	�%�� �:D�I9�|�Ɓ���� G�	fi�<_� ;�8�*�V�H�	f�G*�ᒢ�9�R������;nY�O):�1'�	fI�1'Jid~��IŁ[m���Ã;n�Fvd���K�	f�\d~8�1'�N壟��j�<d�bZ�!��1'��[m8� ,�<5�;n��L+"FvE��b�3�� md~��P� L�1'���W�>M� f)N�[m~�����zg������ ��︍�1'��<� i�<��*|���w�3�ם��i�<v�@m�}e9���1'�� Oi}e)��~�1'c��PIMm+��x�����F�fT����	f�):Pif���z|�{)��h_+
 �!Ud*�@m�I9t�F�GdH%gԂ�ݽ�&�Q�1'��bl�d��1'�bUd�#�R�	fo��xo�ԣ[�	f����1':�[m��SC��zg��>w�+����oS�1'̈yH�Ud[��x)��*���9�@F�T�1'UiX��f��zV�1'>�-�D�í>��1'��zvA+�Fvj��h�X�Df�~��cW��h�d~� 	 !�2j��h�⁎ ���>��(X��h߂O9���h�/摧�<ց #��h1�b�C
 �|j'��zl��>Y�]95���Z��h��|� F��>y� D솺xo�Ƣ5��z[��hȀ��� D��b_�C�����H磦\�f.x8�8D6�|j��D\��z$�!g�x8]��h�UdՀPҷ�ɣ��a���]��h�%ݣ^��Dw��B�>DI��hB��D�Ud�BGd�	�b_��h`�>D��(gUI�D��Dai�b��(gbi�b�
fx8ci�b��'�	
f��(gk��D}[��D��f��|�8�s���>)�c��D��"uI
��Qz1d��DI"D�݀�> ��h�)!���D�_m|�!gI"���j���D�5'�R �+�!ge�jh��h�b�_mf��hgi�{|�b�	}���hk?�t�dI"����hI
f)��D�|��c�b[�!g�	}�=�Y��(g�VB9{��dބ��I"j�	}�C���f_ g!�	g���>H��D�������󸾄�Dˆ>DhiVvg)!b��D�����iix8�>D{��x�	}q��DX��b
�>�X)��D���:�0'_��fj��Do?��>D���`�N�z���Dm���b�#g�����P"
f
f�Vv��fo=ki�z�)l�XdI"��(g*���Vvl��xQ�۹�da�Jdmi�z��;I"��>ni�z����	)!�Jd�Vv+�iI"��&o��.i�Jd�)!�i�� �
)!/�?=]m���5'�$�b��?�R9R�?À|�)��:piWd��׼b�q�Jd��.����6�|j����,8�Ń��ri�j���	}���X3D�Vv��.�7Vv	�Yvs�JdZ�t�{~�DmB� �i~X)YVv����Q��ui�zk�-�>+��a(�B�(��+vi�z�Vv�5�Wd�أ5�Ad�Kvk�XdJg�w�Jd��z�>��h�Yv�%��
�&�Ŝ0�z�
׀A�z����x�)x�Ad̤{~�{�O�j��db�Ad%�Jd[�t	�D��<�ody��>��k� �����h��4Z��.@�y*{�P�D$e�>��|��}�{~ݙ}���<���J�F�t�Űk��Wd��,�,��}́ 3D�T�D��}��.=���
�h��,*Wd��}/�8'���Wda2y�}�{~��j��^�o�е�{~.����{~ΟKv��8'j�/%$
}X)��51D�hЕ}=����y�d�
}N�8'
�+un�O��{~�8'�����N���� �8'l 2J���Sc���G ��>+�Add��߄F9zi f�=>L
}��(ش��] %�� ��_���>یM"3�8'H�z�M"u2 z�8'b'g�(�}�9!Y�}�^�{i�D��ףV�U
}�&�Dv�Y��AVmTX�&a�bjDD)�|�}6���DD:�}��`��I��dY�背8'����%#g��)���}i�&~i�&)����}kDDiNd�de.��f�i�&�iDDI�&�iNd:�Z���8'pNd<1Dv~����<Vm�i�V����i�|��iDD��8'��Zd.���ԠZdg���ЁQ"�<Y"J��`��X���Zd�Scg�{�Zdc$[��e�γZdde)X��f
}�im~l�<ܚ�R+a�~��Zd�ىiNd�Ve�i �m~v��Jj��E=X�j]v�Sz�^�[S	2�����߾v��Zd)�}ךM"�i)!!"�&�`��M"����^�l�}΀$u�iNde��n$��L�M"Nd�im~ւM"�	)!+!DZ��`�Nd�	2O�ʹ�ide��Zd�REd�()!*��P)�����&U)!�i�a2<DD���dl�|��ƪ�"\d>�&"��z�DDe��zx��,�iNdr��a���,�	)!��ŔiDD��R9���z�&��@�ZdI�:��Im:�&9���h�~x�Zd��>F��襧+�X����|���S��Zd��� ����z���il�(g9���Zd��Zdg<'���z��,��+=ۧ,&��,۳<'�	2�)!��|��i!Ds����)2�otӢj�W8�^��m~��:3<'��|�����!ݣ"�^��<'DEd%�f1�)!�<'�|�=�F"��*���^���fr!D�}��[v��,#���)!��:#g��,�)!{X����z��EE`��,�����ʹ���j�K<'���z�)D��,X|Dj��i
�)D+�f1��,��d"��z����τJ��-	�	���i��U ��D�8|��D����	����D�����i�<'5��%J�ރV^���jH��Da��.��l���D����`^#<'��_"�<'��b�%���'A#��.��yW�?!�<'��E�}�ҩjXW� ^d&��=�
��o�D�d<'�P^d��_")�c9�Bv��_"k�8؍��j½�DR��^d�i^d�i@9� ⁔C#��D�}f �6$n�i@9��Ʒ�]"��6̺_^dQW1$n9W9�_}=�f��@9d�)@9r�A57^d��h o������6:D2^d�$nބ71��fg >^do}�?����hX�5�g�:DI�k�Hd�@9�uI%��$��D� JX^dQ��D9�P9�#
�h�h^dQ��D��Hdʵ8ا����6=�ܛY�		)� ���c�����D��jt�?9��Q��D倅ʷ�%�7�%1Ȁ��%f3��j�L+(��i��(�iĂieW}�iMm��U2��9�?^dX�zk�����x��-��O�Ivӏ��	���G*����*���A�c���Ǥ�Iv� �i@9��%D^d�(`� [}뀺*��z�i@9*�;n�&`=O�i@99{T�F2�A� ���b@9_�f��h����v'�zd�	�ie��z+��ǩ��*g���}( j�H#�f���i�z{�d����6�ie`�zd��^
�: ���+�i�z��`~8���QPW �Mm��-� !8D�Fa�`~뀺*:�⣹�&:�-D$�8��Jc�I D#����KI�z������`~��z�	���8ثif�Y ��Q�>�۩�Bm�zՂ`~B+ ���m�|݀[m�F�oD�D֎ievcUd��`~�%DP5 �a�DL�D�SUdW �Df�i�D����p�`~͓4�[m��`~݁��������Dp�Y �7 ]��-7�>����ǒ㴽��ӽƮ����*
�fH�`~+���t��$�iUd�)A5��ݯi�Do�ܰiUd��8J�f��Jch��i�ze�f"@ {�#'�Z��`~�D��邁#'g !�������`~�#'bf ʉ*g��Q3�����{���&�g�c��{E մx����@� ړ#'&1�b<�p�⣓�BmJ�#'�iUd�i�b��C��-fH�bO1Udm<�b�Tf��D�{��D�ifo��f��>DfUd.�f}H�|�4Ud+���/���`;#g�Dgf������ �$Ud�i�b���z�� ��>��Dv��z�	Ud��z��>DS:�|�/�y�i�&R]me��偔��#� 3��z��yQG�>�i �me?p�K� ��#'X:*X:���#'Z�����Ϥ�zӜ��o��݅�#'�3�bȀ�ϻ�zhme�ic8�>D�������3�)n�� ��>)�}��#'8
���ime�f,E$u�#'Ome}���%8�b.���{�#'��|�� ��b�me��#']�b���)n����;����i��@�)nȨfJ��><Z�Ca�C��)nm�|�id~���i�bli1U
d~�f�6���z1�{����;d~棧�f�Jc	�)nnFm�id~Ҁ��&�	}�	�F3PFm��݀�	^� 2e :�7d~�>D�Dm7�&-,Fm��z�Ĵ���Cv~Z�׉���X�<�	��z�	me ��ǥ�����VP� ���������o����Xdg����)gFm����B��ǿ��z�8me�� �iMv8��>g���A��zA�Xd�meq�Xd��|����"x���	}S��>b�`er�+UB=nM� +���ϋ��L��>�
&�!�Ao=��Xd���>΀�����z�f��h.�d���z��zg� 9�;�Jd>
ۆ�	}ѧXd���ՀN�	�yHW�Xd����g� �id~0�J���za��zS���D��+8,Z�T����񩁐#TTFm���+��>=�fk��z��h��g :�id~����h5Ԁ@��z
�h�Xd9��gf�/���>�ۿ>_��z�h���z���_��W������z��߈��z��h�O$Z� M��d�zv�ִl������z�(�/B� ��Xd�� �� ���z�΁.�zL��>�B":�&��+)"g���-��zJ!�z�,D�5%� ���z��?g��x�� h�>�]mD�,D"����&��:�i�h��g��,D��z{��f���b�hƁ��h�bS��܄�QҀ���]�h	���.[
}�Э>r�ִ]�d�#Dd�YX���Q�,D��P0�z����F9)�����b�#Di*���&I9:�&I9:-n��Ad�#D�,Di*�
}�-#D�iI9���z���j���&�i-n�#D,
����%�"��Q�W�O$�&I9��#|+�����&�ࣿ� 	��+�z��d��,Dm�|��b��Ά�86�m1H������b���Ԋ#|ܐ?:���b.��&�OI9��%#D��	f��4dem)\d����KI9��&�i\d���b���&a:���'I9��dA�"n���#|��"n䃋b��b��)u���i\dJ#D'��b�����cه���b�#|�i#D�\de�f�� n<YDvT�)u� n�8#D(+
}e DDc����b�� n�� n>#D݃�A}���ʉ n����z�$:o#DZUDD����l�����Ye:DD%ʋb�I9�
#D>����#|J���i\d��{=�p��"�{�Edv��q�$:h�� nh�܉6I9����H�#|t�d�A!D�� n��;B!D��d9�o��b�i!D�i]v+�|m�!D�Eœ� n�Ed��m1X��b��{~�Ed 
��s!D�i\d��z�S]vy\dh���TEd?����i\d�e��e=u�i�zbX!D~DD2DDڴ7EdF&�DDo!DZ��@���Ed�i!D���(�iDD�� n�'����YJ_&n�� nqO&n�i�z��{~� n%�}��ol���t�d�)!Ă)��׳��iւ!|�
�o�i��Ȥf�]vӑ{~���4�� ne��a����i~�i)ÄI-��}����[�Jc�	&n3�$:�i)��i=�>���:��Y	'!Dk)!�����iEd�ov=!D����i]v�a�z�be���w�J�=u���/$�z�]v�����z��u�)�)!�)=u�$uU@�z�i!D��`c� �-)��.h��&�)�	2���*��v�|�ÄI-�i�j���8�Km뀺*R�E��ij8r
f96{��"V��d��4���{ÄI-B���	��d�(��p�Km�������P�|N�3��DԂ�����[���:����i
f�-uҀ��
&n��	����Y9�+���Cd��E�pc`�Ea�Y9��Jc�m*��M9-u�-)b�M9ƴ'Dr&n��b�ݜ�>��M9��'D�M9�'A)��*\�o�i�j��8($n`�d�}��M9n$n��o}�M9= )��'D
}���{�Bv1}�[$n��'D�Cd��-�oR
}���ae�h��M9��'DƘ���'DU�M9-5@9L�:ؘ�'D��8���{=�����8	�o��F�u�+m���V�o��vR	%$n�M9/�Ƙd]}��o���	��b�}����i�b�i}��o뀺*��'D�4Z~}^�he�>@;}uI�i@9�>��'D����o�&��'D�2m~��j����[������ۉ/m~��M9�'D�m~�"�Q�Om�����'D	��jF%|��_"j�>����1�'D�M9⁔V^��6��o`��n�M9"۴�o�=�'D9���M9�[-�~p�it�>�%D�M9"۽�%D�^}Q����F�i:�%D^a�ze�>��9VB�%|9�R�5�aj}�ҳ	j�h
�%D�0�z�Om��>i�[m!�%DDh���;�z>���:�$�z��j�@9j�z�*D��_����h|;@9~d�i
�>�%Dj*D��zm�%Dj��9��q\���Z*D���"۴9�ٵ�%Dd��nj*D���$.<6*Dnm~a�txܒF2����j*D�m~��*|��Y�b~t�����z�����+!(���j*Dj�-�*|Ԃ:gj�>��{D�Wm~ރ�p�����x��z�a8�0*DsŹ�L���)�zV���z��x�*D?��j�zY�i�):�$�;�����z�g��J�I
���%D��������_Gdz��x*�����Dz�%DS��x9�ԛ=�fO�%D���։�����ܒG�m�8a*D��[m��+n��8�%D�%�
*D�Ʈ��)���_�L��*D
�+n@d**D�+����Ԃ�x>��k��I��a���e���&:�+n�fj*D�L�k�{3c�*D�+nS:�|��y���Y��>F��j*D��	����6n�J�������.�$u7���%J�������*|oE�|�/$u�8��l�g���8���x��%���4f9�t���R9_���q��L�x)�����ȒX�����;��ܷ��x6�o�yx���ܜ��|��&��oz�d*���bIɺx����(|\����f,��b�Gd �(|��L��4S:���s�E�m���l,���!�Zd�����b�	�+�9�&\���oр"�+ne��bd���)�L�)�D9��cv ��"�Zd��(|A�D9�xl����włb�Zd�8�o��E���&u����+n#���K" �&o�o+(�&�D9��(|Ԁ�_]�(|��g��j$�b،)nO�	}c
me��&��}��	}��t�����#��-���%�[v�����	}
����E��h��:���{�8&j�&��}rI"�	}�#�/F�& �	}�Ad�F����|Fm�}x�[v�LR9p*�i��)nmKmPR9�B.|$���R'�?!�	}��}(jFm��zV�{e��z���)j.|Fm��&�o�>�Fm����k*�D9�4Fm�}$�ݍ�z��"Fm�(�z�@9܀��� �.|+�.D\��t1Fm,�)nd���=�����-jFmK�.D.j@9�.|/j�zXq8�?D�I�&�V0jFm�!��=�.D�me��.Dh�ߣo�)n��Q
���.Dr<�z�	�1j�z2�	}3j@9��	}�@9;��k�{e��	})�}��$�)n��.w���X�)n��	}4��ai�)nĉ&!��>�)�	)�Fmک[v�	��}t�壠�{e��>�3��=���	��)�I�>�G)B�>��Kv�)��'�)(�	�[�w��j�)�'.�w?���?���Fm5j�zv� 5�o{M@9u$.|��.DA')A)OK�z�.D�'	>�)?z��'�ִ6j�zh)2�í7j�jX�Z/\��"��|�7�b�� 8j)Ԃ :����T@��
�j9j:Db���kI�#�Jd�4Z:j:D�.Dh� �!�����"n��fP�����a��>��'o�J����6s8{���Bm� :D2�í��	�8�,D;j:D�	��bo<j�b���,D��g��,D�o��Bm�'�)�|��>=�Bm��� ��܁�,D��Bm�3���b>j�j�%1D���܏-n{�j�
}����o��A��Y(�'Ȓg���%�3)L-nX5�?j1D�H��	I9 ��|A�"n�����&�61D����@�,D��w�1D��w�
}Z�Pc��f��&�,D�4�+v�%e�قI9:D��)utO
}YI9:D"@1D�f��zw�ִ�)u9�q�1D-n�Y�|4�)u�����	f*
}��r�+X���]�&'I9���ƅ�b�
�j�c�
��`Q�ܸ���O�}�f�A�,DB�b�<�&��Bm����miv~{��́,Dz�	f�6�&�Bm�,Dj7�&s��&	�}X��`��b	��	�m�%��
�Cj�&��>䕄�eGִ<�:|���>W
}�de��&9Wg
}p��D�b�	�vU
}E�`�\ds�-��-���o\dFj1Dm< h�&D
}RI9t�o��|&�f1D��-��%l�;nf���|��)u�	���&G��zW�uH�>�I9I�)uJ�`��P��	�P��`K��&Lj�&l��,�{���z���b��0��/�C�a�#�&M�;n�)u�'\d.�)u[�}ſ>������KmN�b��`��Km�	������-O�`�K�k�)%g�&P�Ym��zO�Km��zl���"��>��z%�Km��&	��>{�)Q�)�Km�\dj��z��)R�)l�hO�.�)S�Km(�%� !��)���G��>�ʴ9�����Ym��Km�
=ȉ�#���T�>o��z�Fm���z��c���>U�)���z����>ߤ�zҲ)�����)(>|�j(l��.Vj��"�&��c���z��JcW�)(����)���X�)(��9FmY��zZ�)v��ӑ���䋃�g��)��ܕ�>D�	Fm�����ƀKm��KmЁ}Fm���z�x8�.Hd���U
X<��>�)��
uk[��z���z*­>2�a=-u+�Ym�ۧCd݁��́�z+!7
f��Km݁��)5�b�n� �bR���\��z��Km�.
f����Ղy��`��+X��z]�z��+!^j
f/�&��X?_��z:���m?-uD�'D��$�b\�!g�)��y`�be�V���
9��y�	o��N������X?s�>|s�b���h�뜡	o���l�&�2ÔCab"���Cَ�'Do�|�a��h(�+i�m*QWT���b|W�^�f�"�}8��h�b������bjd~��c�	ol��b�;e��f'��4�	oj�}k�*ֺ6�b���y3d~D��a
)w�}5��hT����?
)���b��h���ߣ��-����	i�=-u�
ve��K#
),�m*��zd�%���8e��h�n��b�
)	Sd~�-u@�b��V���"���b�
)fj-ug�-�$	i��b
)\-u98}~��	o��nq[��\X
)���h�	o���n��m*l��&��Rb]mn):�]}~p��ij�zP3�\�ܚ�	ov�s���Q�]mjj3D��~kj�z<}~ROm%��j����'DgI9l�{~mjOmZB�z��{~e�n�Ad"�^d��8S��hnjI9���bZ�M9o�{~}�$g>?�ޑ{~��nl���ojI9Y&��n�n�:C£I9��b�{~~&�pjI9i�[mqjOm�e���1��7�^3Drj8D%��v��c�cr+I9sj�z���耩v]�z�%
)���6�{~9�U	�zd���J�ST��>
�����8�8D�w��?�tj�zZ������&����5�cuj8D���	�zz�{~����3-�z97����{�:g��8|�53Dd�z :8D��>t�+{�:gv�d��4@�I9'jOm	�X�o	i��%�9�dI9ROm+�8�
�(
�,�z�~�`x��.
���{~l���Q�d g�ܹ&�z���`��z���N*�����<v��ܒ&ѝ�8�*�j��|8�
o���&��,ׂ{~�4����i����vj�j*��na�nwj8D)�/m�8�Q8D��>�n�C�z'������aÄ�ns
8D)�����ރ9�fF�>xj�zyj
o����b,�}*Ҁ8�o#8D,J�>��}*�
oׂ_9�-8Dcg]"	i�
o�����YC8D�
�8I
�H��b��G,�}*zjDD�	���Km�_9\�8�G�_9Ł}*DD`W���d{����]9�裑�zӺ�8b�jm�8j�8�}*"�9n.Dv��fX����8|��*�Z�
o�}*ɀ�.ú�+�DD_^�.!�f*8+
o;�8�$��?{jDD�΀��Œ��wDD�
o,	i���ޤ�j�Dv���{�w�e��g2�n|�j}jDD�$nɶ��}*�
58Ҁ�?�K�����~j�&ւ �=��j�I�?�@mjEd2����&�o�DD��_9��d���}*�)!�i�?�o��&ڂ���}*���j�Ed�?�X�8��E��������%$ /���Ѩ_9:���"�eD�_9�$u��}*`1��&�Edy6n��fo�Y��nY����%�-<�|@��z"!D�j	i�jDD��M9���&��F��>g�M9�&:sDv/�M9D�=#;�?!s�M9�jDD�'��Y҄j<|k�:�X�{e�j�i��
<|�G���3���j��.=��ÎYү�M9�<|�@9���xȀM9d\�����5C��&u)<|p�f��j@9�<|)�&��b�j@9���x�j<|@����g@9��>d%<|�8X	i��M9����?�aG@9�j�&��M9<|:�)�<|3�Xd��<D��- �&Z�̣����	@9Ł<D��-G�`%��h%�&:�<D���o�<|�ג=e+0�7��<DB�aa�>i�S�"�@9��M9*��n���_�<D{��+	��>��M9j�|"۴��n�?���|��Y��V)��M9�C<|�k�Y):�<D��M9�j)><|��M9�j)����j�&�j)�j)�2����:�M9>=n�n&@9x��<|���m)�j�n��x+)���ܹ<|�Om�)"�$T<|�3)G�`%�n:��<|}�ɣ������+<|Ă�,��:����j@9G�`%$�<D�/��
7�j:D��:�q�<D)
:D�)�	)�R:Di4)��be�b)����$g��beK�>T#:D��h�be��z��:����Z�.���Dz�,@�&��ɣց����Hd��h|�8���o�"n������j��hU�bh�:|9:?��:��ܗj:D���hk�f�:D̑&��j�j)�)
����$'�:D!Y�
Yo':D��Hdی�z��b�����9:	�e�k?a�g����b=��5��Hd��#)+�:|���d��8g]�Yv�	���a��́���,���z��V��h�%���Hd7�"nf�&ɎIv���&H	i���z%��zk�:|���&��%�Dm�����z�9碡:|٢}=��&�i*e���S:Dv��!��;nB�}6l���&�-!��%,�%D�'��Md4]@mH:m~z��&�;n���&��}��}*��9�Iv��%�?:p��jԖ涐:|���-ҍ��J㣝�Bm��}��}��:| ��-�"n��v���Bm�E\d��"n�&���BmT��,�:|��ʴ����9@"��j��%Ds�}��i
+d%��&�jo(�zd�ʒ��;n>�ִ�b@"@\d�j�-d_o��}��j!_�j��)��*��-�d%T\d��&'���^o��(���rH����zcHm�  *�-ց��J�;n%��&� ���U���ʢ$8o�����>O��&��x(�/Q@"JoXzK����̓�&��Ζ;ع�y|}�j�z�=��a��&S��zg)98\�	iq�Eº�c	;��{	���?:��g����զWd��Ymv�%D���w�q�����Ym��)Ղy~�Y����$��Ym��$�j\d;���
���.Ɓ¡��́)`�Ym���#��)Z̢��۬:�9[>�o� �jo�ʴ� Fm��Ym9ov�oFm)>|J��!'�zj��A�Ym�Fm�j>|
�۹�Ym>�ִF��j�b�jFm��,�j�b݀�$��i]�bU�Ym�DFm=1�j�T�b�J�iH��X�j�ܬjFm�
�b����)�!'�A��>D)o��=�>D\�b�ͣU?o>��w��P
�>Dw���@�i�o��+!��!'�>DÃ)YFmA�b��>D��Ymȃ�υ�A���?]Mv�	;�ͣ]>|Dz>D������&��Ym�j�b��i���YmQ�(|��$�	i��>DNE�3��$��>D��)��YmO-u��>D��Ym� �byMvɀ>D�Fm+��{3Mv�!'��>D5���ji1��>De�ٲjMv�
o��I�f��Cd< -u��Cd�>D<>|�/Fm������b\�@+z��b�jFmLo��Cd��b>|wFm�bG-u.��&Fm��b��i��Cd*���$gń� ;�zb�iL>�b_�Z�Cd �b��>D��Im�A�R�>DX���h�-��Cd����m��ݧ"܎�b�N>��8�:"�>D=�����
���>A) �_"0�Z�Y)��>D2�Wd_��
٣)*�@�4G=����W9W��W ��V)��>�}
��-���n����Î/�)d~���H'�6t����,ӏ�8(���o��4�	_��@v
}~�4'�+zT-u�*]m~'DDbi1��Cd�Cd�)$	i����Cd���ς}~�j�kx�Cd.]m�j)C�n�j)e������jI9^�ɣ]�Cd[S]m��,��Yq9):�)>E}~@}~������,J�n�"��	)�jI9qQd~Xve_���Ad��
�8#I9��Ad�}~�
7�؁�>��Ad�j]m��$]��z�V1k�Ad�-g�Ad����j��J���c���)�J���zG]m�a).V ��j�AdB�����z]f`)��z�8D���)hX�S� u�>��Ad��>	��S�&��Ad�B"R}~Ց��Ȁ���j�&���z�)Fv�B"�z x
]m��c�W��0�fP�W���Ad�4�ֿ�)�j�x'!}~W�#b�&S�b~;]m^�}���zvB"DĢ��}�Adk�F'��&��}m�*�fw�o���~8�c]m��}i*]m��}�jI9��,D�Zdh����8�B"��}�
(���{d��[[I9B"��y�jGdj,Gd���=�F�B"&�Ad��}z��>��	�,DE�4Gdh�Ad���a�}k�-g%��>��Ad��,D1�b��ƍAd���zo�}\�@+#��z7�}y��@��<��ׁBG�b��o�jFv%�8�Ym{�<���5bn����
 �
�Fv�jFv�j�b[�_9��bC�C��b�m1���b�:��խ�}R��b�jGd���i��m1;��z��b�����#g�$un�#g��}J��o��	o$��jl�-g��}��}��o��Km��dĂ�b�h$u����E��jDD[ċb��B�#g�jDD���+f��������b�+DD�Z�
�}��l��zb����D��j&��/�jGd��#g��b�jDD���&:DD�jGd��m1��b�jDD+��b݀b�$u�WGd�ͣi�V��b\DDὀb�jGd����jDD���]�& $u�jDD
�&l�	o偀b�ˀb=��-�ǥX�s���&��)M�Km�jDDkBEd�I"��Y�%4@DDa��b��_9��bg�X��	o��܅�Q4��jEd�m1e���ʟ)h�&k�a�m1�b
��iQDD��bA�u�ݐ�b�:Z�ݐ�b��σ!D�jDD$ujEd���DD��#gv�����)�eDDe����M9S��zLDDG�&�M9�f�jDD�j�>�M9�jEd�jDD��i~�Ed�:��3�>Ղ{ebI"���@�����M9Ä�bH��b�j�z��>�}SD�za���4S*i���b>�'>��	o�M9�
�z.}…��
�<���<���&�� �g�zrh�z�I"�� �j�>��z�Ed��Jd�)gEd��/�oes�Fa�!D�%�j�&�j�z�4@���_��Jd�joe*��X��`[��w��F"w��w��:5Z@��Y�I"R�Jd�!D�� Gg.!Do�a�j�z�Wd5�Jd�� �-g��f�T���	]m܄M94���Om5	���)!��Jd�F��_�8�b�]m���`�)B]m�j
o��M9���(�^d�'g��M9�Kv9�M9��-:�
o�M9�}
o�	'gk
o�։D��k}k
oQ��l��z����Kv�´.�	fe��`�[]m,(��I
okOmk
o�2���>��j}��b��-��z��|c��.'g &��Q[\k�b#´���9�S��b5Dm<�Jdc�	f���z�Jd�5�{0�z��F"=�HĀC��	f���i����'g��Jdge����b�.$n���`2���Jd��c�]m��oj�f���z5~bJ��	k
ok��?���
�Hd뀫�Ɓ�{w$�bs���k�4�ڋX�ѐHd4)�b5OmƁ��
o�0�b�j�����Hd������o�DmT�Ql�-g�B �ג�Hd����9�M�3!i�ba�	f���^+$����b
k�f.�Hd3��z�KvV�k5���g�B��Hd��B���g�f��3��
�z�oЂ��ی	f,��:��{~K���,��o~b�Hd��&���`z�f��%i��4���zr��,{�f���&&�[k�{������)u3�{~K�Hd��Iv�f�9�{��ڥ\����{~?��x�����3D{F����Թ%|�V�JWDm�;F&��&k�zmfe"۴��l��x-�HdkNd�:�~^NdՇHd��aQ���\;g�Hdk@"<��okNdp�{~=�oZ�Hd���	o�	ۣ�o]fe�%D�Iv�-�kNd�A�)u�LR9�$m~Հ
�@�'
آ�
�-lJNd��zڇ���
I&�Ψ����Ac� ��&:׀����&	��:7�l�`1��&>�x9���i*j$�	m~4fe�{����%|��-ڢd�ϣ2Ndh
f5OvJU�ʒ�{5��܈)u�@"R?OvƁ�����?��_9Ř�`1!�%���a%�Iv�f��`RK"'��>���ԚOv9�%DkNd���g�{��k�%D�Nd=����d~��q�Q+!I����1D{Nd��	Ѝ�����-Nd
#Ov�_9À�?6<Nd5��z��a�
���z�
f5��.k�fC	+!�%���6�>
f�z�&`1�+!�BNd���kNd���z�@+`1kfz
+!�z�+!�)�zb�ה��(i��o��.���o��z˧������zN����z��ekOv��g�0�g� �z0\�!�)���zl�8gR�b�j�Z�Ov���h:��?��
��&�{�e
f��z��z������	�)�<��"�BvOv9�S��$g��+���z��a�����o���W#k+!�
4S$kK"�iz�%���yЁ��*�i%k
f�Zd}-��0�ϝ��z��z�Mvl}_��z�79�{���yH�Zd-���ߣ!�)h��ܣ?�9�����ir������X|Dk��yO��z@���d���=����緃�߅�ۣ�����9!&�)'��z��Im��Ɉ�e����s���f��z_����-uJ��j�
�?����Pk��~o�>��?!���@9�:����i(��h�[��&�)nk�ݙ�#�)�)n*k�a>�(���+k�y+�;u��,�[v��)n
f����hq�;u����-uh��f5��h�iG��e�y9��h|�
��
���&@9z�z��--�bev�o5��ya�����h8L���x��f.�bel.g�Cd
��z/�-��0��&l��z=��o�费�%I�&�g}+�k�%�v����h�ԑ
�h���ȃ7�?�i1k�,9�{�9�{�[�B����Ym#=a���i1��D����@9��T}~��y��fb�)n�/��f2�;uЁ�A�4�������e��h��z?�p�o�	=��f�/��+V�9�Y93�Cd{�� ���ݨ�P� �zI��V\��$��Im\��a���'�tߣ���b"�>Y	;(�z{��h��>��ke�'@9���24��h�@9U
7���h���{��fa��z�ߣ:���5k�z]��+v�(��6k��6��&� �z\@9�'!��f�'!��	@��3.g;���O
�zDߣ9�>(�'!7k:D��h8�/!(�'!�g��3'hf@99k�z���G}V =�z�'!o��kY�$ {���o?9#��kk��:3�xb�'!�^dn�x�'!:k�i:�z;k�z}7:D/:o��� �'!��,D<k�z�ơS�%̀(u��f�	�z=�b�� �x��f׀o���ai��5	ߣ�	;��%:�Y���|^��+��>>kd2?k�z��+p��@ߣ(�'!�7'�(u"��b+?Ud�}��x�Mm
���PAMm��:|��(u_�x�
$�@�,D"-n�IUdYܙ�z��b��
x'�ʣ)�'!.Mm�'!�-nM-o)����6Ud��h>/�jo�M-���-n�jUdjFMm�'!��	fm��b��za��a��c�)g(�'!�YmG����Mm⁽��dol��+3��b��j#��7:D|N�xA�b���a��)g|��Bk�d@�ܘUd�Ud�Y���b����
Mm��(uk�f�4�	fS��b�A��� ��H������,D;�����e��$��;nv�	fB��	��z*�,C�b��(u��(u�6Ud��P�͇�bS��bDkUd��L��Ym^£��������cߣ�Ud�MmE�,�Ud��V���b���FkUd���z+)eo>��zBM-���bo��ܡ'Ud��b��7��{S��ܢgUd�)G�)�IUdc��Hk)Ђ�i���b����(�)gIk}eQ6�χ	fۈ@m�J�Ym9��)�&ȸd�\���)g]}eJoe����_��b��YmD��K�Ymo��	AٙbL�@mG&oeဦ�����M�)⁛b	��6N�>�oe�ѣ>oe���>z�%j6,�Kd���>���Okoe8,Z���ިr���m1P�>5��񙂿>��}|��q(��zQki1Z��ゲ�R�>w@m�2òRoe[*!}e�oe�;���J����}���>wC�'�}'��>��@mIߣ���!i1��S�>:�)|�}샿>�o��}�&���z0\CP�ۿ>�裢~nhoeee�?��s�tI%��>��w
��@m?$ߣ�x�P��0�Rߔ������Xd9WTkoe՝>DЮXd��F����Ukoe�� �>D��r��(��Xd�EoeVk}eރW1��hRoeWkFmXk}epoe�'�c�x�Y��h�#4ZB��>�JI"��}H�Xd8
f��}��>Z�}���>S-g���2�B��Շ�$�@
fmĭ�'�b�<����<[k]mQz�\k]m]�Xd�b^�>s�}oH�b��ht:�bȀށ&�}Y7-g_�}#6
f�$�b3��5��.{�bȎ�h�&�QҖa
f���(��&L�2�t��(��ke
f-g�e��zw��h{��z9�ZQa
fԂ�j5!�b
�����)���)`��z'���
fa�Xd�?��mb��z�x��1���ic��zdk�b�Q?/G�bek��f�)T�>Dg�)���z���if۽�X�b�/��bD6ue��:�䣚����/�be��ׂXdd~���z)�Q?�Dm`��m�b<d~o���{酀Xd ��z�¾��"��h��hw��z�]m��P;-gh�)ik]md�b˖)O
f��b��"g����ߣ�Dm��b=��c�?]mo�k%����UH k(���c:���4�b��z�	�_jk�b�5�Ђ+z6��=��߃)k��i���z��e�)ȒxN���d���z*i/�E�R������z�:'`VI9I����+n<Wd��i2�)������P���i����:'j��i�1���zg!Dm��%Ҁ�cT��((�)���-a$�%s�M"�� ��f7�%X���9���:1� l�)W&VB�
��k��_9��+z][F��%FJ/�?:�}����֋�`�8����?��{��a~I9d�ˁ���mk�>��J��_9�ʪ�����?:n�"g��M"���
�����`��f�+�>H�m1f���:�"`1�4\d�	���@"S:�/U�
�W`1��fyըc\:\d���`O��(�����Wd�@"A��&97�%:��(~`1�>���%�#��b�m1o��`#\d}���&ւ�`��>Z��Ԁ��}�5Zs�fk����\dcD]v%�z`K�|���S�D~]v�(I9�>���&�'D.'��kF���&�*]v\�a�I9�E�_��m�_9��Zо�m1���|���"�Ad�]vp�F9qk
oUIY"Q�zrk\d9!t�_91U\dY� ��?:����H�w�m1�M
o�C\d��_9u��fi\d��>]v���sk`15��+�
}��z:�i����=�-:��.t��`��"t��Juk�-��z��y�B\dvk�zX�:wk`1�]U#��ʃ�"�F9
`1(Q\d��z���&LDDD����
�z�����vf�z(����;g]��,��c�
9!r�<��S�G]v
��>Vm¤�%�]vk���u����&<$�joej�F9�gDD!�Z��:]v��f�j�zわ�oez�zF ]v#���H��`CDD��Zde���h��`:]v�Zd��z��O�	���q9]v9�{�5�z�%]va/+J�z���xkDD{�Zd<��a��O��jm�y��^>'v�Jyk�z��<�P���c�f�L�z��-�7�b����Zd����t��je�Zdt�++�k5��z��p�I[��$zk��/���k�	ۙ�������J�Km��fه�i���༁i~{�f\��|�Zd��e�Im�
��iАѸ�;u��`׀4��*ւ�Zd<�i���;ul�z|�M9��;u׀q��Z�B�b<1��2DDҀ
}��%��<�щ_"t
��[�b���2DD}�x:�&~��h�M9x�:�+j�y)�f�+�`��&g�Zdm�/��x��Zd���e�e"�H�)�4S��Im5�i~h�be��9@9ӏ���Zd
��x��$l�
}�%��Zd���x�<gt,Se#i`*�'<g2��FJ��f*�)�%߂��Ŧ>m�W�o��a�W���-���Ɓ3Ê�ژ��>�/Ѿ�q�b���H'[�ǒ��f�����v�)q9zu���uI��`���x�ݍ}~�	�7���P�Y`*��[v���Q�Im���Aw}~T��Y��SJ����)	�d��<����/���<g�"Az
`]�)�k<g9XTL�ǁ�x9����Im�M9T��{L�����Y9��ސ���€ �	E�������x��&������M9݇�aÃ)j��۽�����l��z�a8�)o�A�F_�pF9cF�k��2,�zj��>��%�a8�{�D#��“�>���z�<g���z�^db���k:DQ�)�����Y�^d>��zv��O֦>����^d���-��ƒ�_;�)S��-��g�m":D�0^d:��z��)钣��k�a<��,O*}t� �I
��a7^d������.�ش	�c?��"�:||�ֆ�)O�b~@��z{�)큂b�}~f�i��7)h�$�k^d���#(^d�X��b~\U�j�8���`"�h:O:DJ΢m~���<"��i�8Mmo�}[^d��jj�۝�J���o�����*�b~������do�d���OMm3���q��zw�zԀ��a�ix���,����"a�km~;Υ,ȃ��Ԁ�� �;n��A?�$g�of۽��ie�k^d�=Mm>��z��a=^d�
:Dy�g?N�x��i'�i���<�.�;nH�x�7�S��y�	m~v��E��7}�o�<{�kfv�*����<���y8�j��+���?�Ivϋ��k�j��<�@�(
��j���	*�^�a�m~ʋ�݀��j�������v�شж;nJ�3���b~׉g�[�wĂb~��G���j�D)w�$��jA9˝�[m���&~�<��;n��A�;��{�3Mm�`~o��h�8��@mU�`~��ո���%\�`~l�@m��f���@m;�`~oեzw<�=|�@m
ɻ��x��<�d�`~��@m�>��@m��&���̍���	}eL�>��`~� ���[}e��`~Pi@m�D���@m��`~��P�����@m�gP��>�}e # 
$�>��@m<���j���N6}e5�֒�@m]}e��`~	2�=�>ջ@m%�z���j��}��`~G}e	��G��bʊU2��@m��y��灿�@mlI"
9}ed���y�@m}e���X�g]��-3��&Ɓ�	����1�i�8��*#��>�k}e�<����/�2ù�`~�
F�������`~��@m�kf~�W}��@m;�`~">|�}��@m���J�(�4hս@m1�zd���h�f%��['I����������/��@mm�k}e����@mYf~=���L��f�k�>��W�>��ca����>D��:'"�`��z�:u��	]6}e�$nw�s龀��k�zn���?.}e%%�>l$n��.��>.�%$n9����A�=�:u�O}esI"T5Z�
$na0��+)O9�v.o����z�$nk��*�>D�:uG?�z��r�>Du��mP���E�:u:x�2���#�>D�>tã���:uj�>D�B9��>Dc�4�>�:u����I)�y*�>D�`#vãD��$k��*�Yv��f��)g>��)ܒ��:u|�y4a(�>�j�����	�����j�f.Mvo�-��z���9�_w��i�o�0�⍤�a�&D�z�����{}/:�zh������U�z9w�>�C�ݯ�:us?�ySDmރ�ϸ���X�;g��:u��a`d~e
�H�^+7�b��	f��>D���a$����k��g��xh����:u�C��mw����ƫj�:u��)g�>Dh����C_
5G߁C_��I��,��C��6s�d~l)���e��Dm���o=��@�)���y���^�����b����b��)jc8|`�y#�o�	�j�S4S��)�k�y;'ĉf���z�k�y��f\�u���a*p�!�����Ϭ� �%D�դ������k5���tۢ��jɩ����>��zg�����a��������o�aЁ��E�?Ԃ8�0Jd~n��a$���ؕV����z��%��Ad��;��0�)���a��aF���{�?=�.��8�Ԃ8�׀rq�|2���	�;��u���(\d~�UB<����?+a����y&���m1=�Ad�M�>�k�&D�cT,i*2����\d�k�&ã�k\d�Ad��&����&X��>�k�-3!CP��?)J��x���"��-�\d���z=��y9�_ѕ7��AdX���9���:��-A�>��&e��d`�-��x�!;��k\d3�&�ɯ�%|���z�����Q�+a��xh���vãg������k�&v�8+a�$�DU2��ݠUJ�+�-Z#\dM����F9�#�P�x�Ԁ
'dIoe��
I'���oe� 3�5��t�}O��5�k�?�koe��(|p
�)��|��.���:Xoe��a�����oe��%ܢi\d�^oeh�E *�ah����C�a����)�j/���*=oe��q��Adg����m15-g��&pN-g\d��k~t�-���k��>H��zڋ��
�-`��z.W�
�(���k
fS5�-�G��z�i�-�\d�oeZ��kg���fx��N����k�"V ,VmhGGDD��b⁃k]��z퉃k�Z��N��de�	o���z��|���i���k��	ol�)nT�����k�EDDv�F9���zM��z�`+a���S�(|�DD���L�Z��E+a�kDDY��k����C�k�}���k�I�a(��z
DD9`oe��zP��z�-DD%�T�+�i#ބ��6oe9DD	�
�>��9D&oe�kDD����"g$DDX�Q�;�ay��k�������	o@�&�	o^��k��z�k;�m��+��k~�kEdsEDD��|h��z���k\DD���k��	og�����X5���/m�}���z���I�k~������4�o[����i��`?��z��zk����΃kEd����4��zP��i
�Aml}�:Z���Am�g���bM�Am�KmX�_"��$9�6:��{	%��5��%�Am	��b�_+��ŋ/a�kDD��Amqf�qQDDe��?���یAm�kDD��Am�津���#�k�i~@�}�����Amw�/a����J��+ޝAm��i~о�?{��|?�Am�(����Am��}Ё��q��%��}<��Ȁ��>�h̟Am���D��&'�}�Am���9�k)��?׉����?=5�0aR����Am�[�>��9�o��1H!D��/'D=HdƒԢ8�Am��+�	�`��/ؒ/�V�Y�~�|w�n�`mæ>�k�`ϔi~��(C)��i~�G��k'D��Am��z�p��Am�k�|��&��`����?E�z+���X�zսAmǒ�#t�M�Am�\�.
o�Ū��Amo̢�	;���%��Amg�Y�Y�_"�y*4����>��.k�_"��z�
��
ʦ]�%�}E��Kڧ,�r����zg
ol�?N�b��?S-n:��d�CyC������k-n#!�z�����,	�
����|��%
Ч,냶jS����b|K9�h}_*#_*��F2���偤�m��h}_{��kZ+�6�Dz�,�z�_}R�zЧ�,��Ū�0aR��ه�$�zه�$� $np�j�3�g�i#y��m�9�zh���z}5 e-n"�Hd�F"��z��y��F�ĂF"�$n�}�'�
$nƁ�j[}9���`��߂Y9�k�z.�>������ch�8�d�%����z�k�z\o����-n�3�z�����}J:����k�z��,�m~o�kEm���,D����|o��c��|ݎ�|#]Em6#��#m~Ι���EmH������,��B5.Em�km~�k)M��,"����}���ED
ˣ\���o��kEm��z�zi}k��c�
m~�YoJ}���%�|�kEmLW�c�����x���Em�Mm�Eme}����
:��4}5�Ӛ�C��o�Emf>�z��[ma+a�X�zY	Em0�{~9���[m��m�-(��	����Faŋ�|:�7�$�8��k)���8�
�%D��[mԂ8�"+a�6ܫ.�L�8�2���|��|h�[m��PҮ�%���kow_�_��`B�>�&�o��%&EmB�8�g����kEm݁o�Mm�|�	Emo����+a�)��._�/ъ�{17�o�)���P�	Em5�)V
%|��b����k�-ք�|a$��i����)<��|���<9�>��[m7&)%�-d�^��)h���<�>��o%����m�|.�>�٢>ʩjЂ�i��������)"�&:&�)]��"�)m0a��>��jJ�n�n8��|\x� $�&�ݚ�-�5'�|i
��D�>���|��8�뀢���DI��%�-���m�t����X��|��)z��C�.C��j��|��ё�k�-��}�㙤�} F9�6Ejɰ%�&��G9��O9	�I��|�o��	"�&��|���o&���c�G9��o��)��}i�]9ŋ
��}Ӑo��ؕ)t+a5�D�>W�'���|d[�|	�)�k�`���|�}�T-�Q�ՠB�-h�be��G9���ܔ�Vd�ZdʇG9�)�Hm9��	�G9N�Hm*��I�>D�(w1�-.$g��Hm*�Hḿ����|J�(�	+aʉHm��}��i�:�`
���$�����}���$'��y�Hm�Σ��� �`:��?k���W�}��Hm<me=�%�:�����Hm������|k�Hm��O9�k�`Y��?���o�um�5'�MvʇZd�kMv���i��e�y1��}��i5��ȟ������Hm\$g���x��G9���?q����G9��h�����Ӛo��iL�2�kF�ց���nIƙ9���z��?��9	��k���me���z$���k�`k���9"�`�)n냽���)�"#�}	�&Ԁ��8�`H��z�/ј�HmϒHm��)n9{k���z�C�`9�Hm��)��I:x���������9��Hm5i�ڊԣ���=���i$�o�(|g���R�HmH�*����n�9<��iQ�����"g��)<��(偣�=���՝/a��?���i<��z��9��)̈���9\��?c��?,�"g�i�����o�G���̀��5me����|2�G'w�����I��R����z��%�Om:}���-�0ah��l}�����9��{���8�|&�-|�v� 
}ri*��)�s���z׀t�'���aj�c�9��Qc���y��|��z��&�0aQǯ>�ř9P0a+��z;��-��l�%��{O��Ɣ#u�ә9b��|��):����״0a�ڦJ��I�$|8�|
���D*#h�%�*���j�|>�4ab[��:#ug}z�|�}[��|��������	���#ui*���-)��-��i��O"Z����W��kz�|�.��&�y��#ul\d[��ioԂ|�7��y|��f2'9Pj�/.��f��	��x̛fPeob��HJ��0��#u	0al}3�nd��:�|�l�x�o�}�0aU�,Dw���\}�|G4�x��>lo��+�K+����Ɓ7Gd{u��Y	0aTd2��x��,Dh�i#D\dg�{�|���>�0-n�-�i"��|1�|��m�|o�z�������<�xń��2'�i�-	l�z�,D΁������9!�g
�#u��'!l�z��i0�,D{��$��zN���_Z-n��bѐ|���oc��k��c6nl�z?9���
ׯ�%|� -n��c>��|҆���x���z�'�����|�ЁC�
l-n΀|�5a�-n�:�\d��|��z\-�xP��a�
uJ�
�o���CJ��z��Eց��Q�t{v~�@�z�X�x
��Vm��zP�vf��x�g��{®u��z��/�*AOde?�g��|�itT��m��|�Vm���Afd:�ie��ac��3�,D��B��,DH��ݎ��/Z���|��z�-n�
�|��i���c�"Vm�&�-aVm��|��{zm0al�i��IVml�z��f�6del�jm�|���@��{o��l�zlVm��G�m�|l�z��{Ҧ����)g�ݸ�)|�G����ap�<���D)��o��{�z���{
�.v���ab��{Z�n���+T�>Dz�zւ�ag��"��a7����i��a���z�Ո�>b�U ��>&�U���=)��>�7�iDVm�Z�-�y[$F�KVm;�i#��>o���+�Y�*�n��N9l���Ђ1�0F���1�FAVm���h)�iπ����h	
�1��&10��o�̢��&�9'��h�
�[k�i�>���F���>����&g�&w6��B�!v(���k��h���g�Ӄ�>��|o����z��������"x8�|���h:���&�|�\c��|�
�&���z��)��h��|��z䝺cm�|׀o��Om�x8�oZ���Y�:De��hń�+�)l�&�&���́ؾ�aE�)��h��|��>:�����LY��>��CmE�)Py��&��y"��%��>�	���(�c�y��)��}~T�Q瀿>���(�:Db�|�,����a���Cզ)���h��	;��f1���hT��Vl}~T����aT�	o��)m�K"RY9��)��ף0-g+�|����l�&��h��|��
}~�=aR�)��V�S�|���|զ)���i�Y�g��h��o��6�ף� F�v��j4�|�lY9��|	�|aF�5�;n)��I'V �ףy�|�>�-m�|�]��z�W��y*��)���n!�C��)���
�ؾ���)���&��)��Y�5�	o��i;�)o�G���;nY���u��؇)�"2'r�)�b}~ϋY�hX�|:�-s}~md~��<]��3���S��i����Z��z��)�f�z���<�몁բw�6�À�i��)5�bQ�y*k�|�����<�-g]��{M�aOS
)'!Y9Z
-g�|��o�-��a��|lo΀Y� l�a-Ī�������Y��|�\c7o���|��À|�|\�|ȀՒJ����%2'7o��|�Z��	�z]��z<�-�!D�b~[2�|A���c�)DY��z:Cot
̣�+g~������z����4�3�AmԊ{~�,�-	�|ґT/D�[m���GZ���-�|�i���-��d��*[�����i4�Am���(��-Z�j��̣��Am2��6�����`���Ζ�`ŭ>��*X�m.�|^��	G��`G��`��Amm�'!l�|�������`S:0a4�Am"�Am��f�b~v��z���҆�C��Fb�����`m�|��ie4�Am����x���p��`��,h�Am���*��-�Ѐ6�8D#��`��&���w�t$��&
	�,%�|��`$��&ئ%�b��J`-o�L�,�����&l$o�0a8���b��bc�G��`&��&�/�h��>��g��yv��Jx�>��?�-R�ie'�bHtt�-_$�>����(�b��-�>D����	�e���i�ED��|B�|�)��:��*)��`t�>*l�-t
̣��>*�d0ae�>D���%�ܱ��`+l
o�h�-�E���L���,�_9?	�𺂀b�|��iꆀb�,9��-��`t�-y�
�
ok��"��{ƒϣ���������@����&�|ރz�w�/a9�2�
����&����{d�I�y�|�~��Jx���敀b��i;��b�2'	�:�Լ�b:#�2'��i�����z�Y\Q��L���?ݐ�b:��&�
#`e�f�,�zV����z��%I�?w��|ĉ�|����3
ot�-J��|׀���>.�b�o/l��-�D�2'��e�� �>��z0�Jm��`e�>��i*�-��He�>�$nI"�-����e��x��z�,
o��-�-D�/gj��|a2'��Q���k�|�Z
me1lo��*2'l��	l�iv.o2�ZdQ'_moRo�B�|�8�N�I>9_m3�?9�k��:P
Wmlf#=M_m��hvۀ�&�`�.u]�bQ�E��/me��[v��e"��$Ԃ'4lD�o;��{t�|Z
me5�&�`5	̣�)|5�Ad���|�<DbEm���+��`|>�2'�L�z;me�D���Ђ��6�[vlB_m4]2'>Wm��z	 �`�2'��%D�a�zŶAd7l�z8l�?��t�Y=�n�>�`�|� �:�S��za��x��b㄰Ս�Ad9l�ka6�?)��|:�Jm���x��+��|�������|� ��`�2�|���zz�|��B"��~�-|�k8�|�;lme��xz�|���8��|��`e�K�y��k���=��wf�`e8�|����i�|�X#/g/�y\4|�Р�k,��zg�`��ϣ���'Ϲ�j�me*�`e>�)�g{�$'�̣����q�)<�Ek
$'��o#�{m�|j��|
�zV�`e�$'��`�x��ã=l}����|�����ٙ�v��Jb��\�oʭF+9X���%DR�����l}2)����.���$' �Ek�:��y��Ad�>�)t{�$'�	�`�|�����Z�{��ۚΰ��%��.t����|
��3�b܀&t{�$'���'�X9H��y��@��FD�Eky�|����P�k���h�|���|>�o��&�����<�ۣ�)9�|�?lX9���-#\�i��gc@�o�����oDD€�RH:$g
	��Q��r��n(�&揷�gZd(f%�.Dv��o�բ�X9����}�ۢ��|KDv�ʀ�$'�����t���AlDD�Y+�:|a:DY$'(��$g�Y+�!DDB�ZmJ$'ہ�:|9�;��ۗ4S8DD�ʀ�U2�X��{��Zm��hW!=g��?��-�ip�<�Zm���>�ϣ5��-"�Zm���d=��?���C�ZmV��?\��&�����Zm���b��΁&v�������o���Zm��-튢�v��yD�Zm���y��.є��+��?	��y�	�-���RE�Zm@;�z�h��;n(��F��zGlDD�	EdH��zN��?bXDD[����
W�ݬ�(�Nm3�-��8u��z�+�aĂ�/(����z��}`7cZIl:DJlDD4�)NC�a�ADD�Ȑ�z���$K�Zm,�}L�I���z%�Zmȁ�z�
o�)fEF��Fh��3�degǟ	���i���M�ZmH�zM�j���c�2D��Zm5�:|H�7�<�ϣ.�Zm}�|���zX��Nl9'��iQ<�-���?�=���}��"g$=�ǂ��deO�?�=�P�?�cw5�-Q��ם���n8��|5��ƁW��}��,�� 
=�ے	�Ee�����=h�Ik9'Q��Շ;n�5u���$$�l.5u��\9	�{|��=T��z���8�4�=b5u��Uk�ϣns�!̣�F"i�>R��z��$J���|׀f���z�|����S�z7��z��?�cw(�ov�<T��zo�ϴ�de��)(�oR��$b��|!���Ul)��j哷Y��?3���ɘ���5u�de(�y��jB�>\���F�i�e#i1��	����j-5u�+�&H9'nh�iOVQ�o��jD��(	��Z�+k���&�=��̣rE^m���c�*od��`�o$��Àe#v��`��b^m��}Vl^m�&���|Wl)���q��X��`�&���W�.^mXl�bY�`o��Zlo�����B9R�bx�&.)^m[�`��t{\l)}4o�oe]l^m�<'�^m�$5u�b�&�d{��A��>D�	�b��'�o��TJZ�@��oA^m^�>D+��p���n]�b��/�z��(_���_�>D�`SY�zMk�z��&�>D:)�+���`7�&����A��&���ȕ�`���T��bW�1x�բo��h���Ek`l�&?^m��(a�`�\^m�JMi�z��c�M K^mw���b�cY�b�C�&���=�N3��cl^m���c}�գ@+o>|�)g������a���aQ2�b���c��գD#���c���dl�b��Л�c�f��}�U��w����U ��p{��b��c���ʀ'&e�cD�)gs������if�c���R����)gt{�Y�a�=��8(�>D�M��ʋ���cҀHd���i�P5Ggl}Ё����<'��8��A�;=�9���h��z!/}rհ�Z}׀�"�12'����il�z.
h�!Z)�	��c���jl�z}�o*��klo3}l��{�)|]7�z�9���8�g���mloQ6�n�c�&���Ё�����cƣ�cݲ�+9��z����)g.��6�BЁ����+_��ce��{	���oloЁ���ʀ�}	�	��`p��zq�c��h{Ro���"d~�CmȀƁ�ow�
��-4���_U/݃ϣ��~�$��J��z�os�ae�U}Q��h����Adr�j}���ʃ�'���sl} ��J�d�&�&w:4�洨*2'��&L	�c��Ad{�t{=��N��i�Y�&�1�&h���t�aeJd+�Ad72ѿ<�&z�h%=�|��S�2�Adul�&�Adv��h��tIs�ae\���{����2�ڢ:=���oe���ݦCo>�2��a��|۷�J�&��ie3�I-ǂ��ݩ�z���|z��
�w�o�mloʃ�<�hwlo��$ȃ��Տ�n��&���k��	�)G�Pցo*�����)xl)���a�&VK�l-ga�&:�hy�)��f*����}҄)������aey�)]#-g��!���i=����=��ae���Q��i��l�o�=�I�)L�Ad��ae〩j��izl�&���쁩j�{���i��&=�g:� 0�i�)�i[�)�7����Ԃ +=���b)�)@c�&��&�F�B��b�]�i;�(#*���{z�e��b�;����o%���bڢ�?�
�飈.�i߁I-i��{{l�i][��DD��c���bt&DD�oX��{(��|lDD}�	o�)}�ot&DD
�&X��b���DD�î��E9̛���)c<u�‹b���a;-g����!EDD�Ad�DDՇ)��������)�Ջbi'ڢ
Dv~lDDl<u��)=���lDD��b�K"�8xDDȃ{�s�Dv�lDDF
)|��S��cwEDD���R5?���]v��DD�	oo�+چ	oZGm�DDpӸ�����i{Gm�+���b\#���{��ыbm�Ё��5���ؕEk³��meoI�"{�\<u��j��oGm�+g,=g��p%����j2ee���B\c�Am�l<u�Ed�l�`�\DDXme�<u��)�lDD��呆l<uk�{o�-�{eƁ��_�z���ރz�dz�z�c/Rck<��c�
c�i~-%Gm<u���[��n�lDD�V >��>�i~�lDD{�x�&!O��6c��`��Am���H���$�bO�&$��J�Ԥ�ִ�6�`ڋ)@=gz��i�����z�Ed��Am���y��|>�b�m{�`���Mۣ���"g��&���Ղ)��f�"5��l=gN��᛭�`���O����"gZ��v�բ��fZM=g���.m�)_���89��XiGm9���=g%Gm�lme,�[�fk��4�5�+�l�&3�ִ�J�&�+��L �l
o��b�`�Am��&+I��!! ǭ���\�v0�?_�8�Q
o����&J
o�bk�Am�	�&cAd�	��6�.��Y�)��۶�Ame9'ތHc���
o{
osl�b�&�%���
o��b�#ugI+�
o��>!��{
:%؎�-3��b.�	�SCW$C��l
o9�b�l
o%�&o��%��#u���G�b�g1�l�b܀���fU�bւ|��jz��b	�Ek���巃�����b�"ga9'��?3���zӞ�"g�_���z<��i��F"Z6�h����l�h݀�c�d@+�*?�hk_�&������#u�l�hES
o����\m=�hI
oo��?��e|�Jm�he��&R��ی�b��h=>�&"�F"o�hN�b��he����l
oR��>��b[$���he�=���Hd�J�5�$�hg=�[�he��Hd�=�iI+��{��he�p�'D�F�ho˴���a��hef�b��%9;�h:=�Έhe�zB8�z�qx�he�g�-��#u���T�����a�:m~���%'�z�#u�l�h������aO�#u�l�z��h�Em�lm~��׸��z�&����-h:�z��m��iw�lEm��heO�{~��zr����t%��he�l�z9�������<r��]�z��z]Em�l�h�ۖuJ���X�h�<�
�FN�8�
)��O�ٌ�l�z��Iv4f�l�z��h��[$j!"o��c���E��+�!�z�&�bÄ�%Y���f�쫣&�,�یg���n.�bܒ�Ё���heF�7�:��	�>�̒��aj�{�?�J�b��-�4@���a������o�{�l�z��b��3èl�-8�z��O�9�>��cB�c���������ᣩl�b�X9��h:�z���Ja_��
�0k�<'�7d���z{�(j,�z	��V�l�bMB�z�l�b��p��j1���N��*ۊ��������4|d��z�ܹ�iw��&?�
��-o��+S�{~�δ��=������d~/��&4�	`ێ�>�
:��-t{��=c��&���ބ���&����9�2�k�<'=�������)����U�b/�bt{�f3&gоCm.��-�̅?�a)�Cmh=�nj�&�l�b�Cm�l�b���.�K"����>�bK"��?lcNmF
��*:D���c�Z����Cm�i�����Cm�&��Uk���%l�a4�`5�Y���&���a��a�Nm��Cm~�f��o�Ek��Cm�a5���r��p��e�Q�5������<)f�`
'u��o��:|��o$�oj�Ek
	'u���&o��%�#$�oF�+!�'u��&��W�	Nm�ۯ��&�Hm������am����/���&%$g�+�Mvc=���&�l�`�l�a��Hm�+��Cm��y��Cm;��&���ݷ������t{���&�G?�l�_��oԂyQ����������y+�Cm��CmZ
Mv�l�`s�z�$gy	2y�Cm��)�%ب��&̛%`T���磼���
{Ă)�@d��$��a����i.��z5���b��X��Nmy	2g�a��z�$g�(�U���l�`���;8���pi�;�I��a$)�`�������=���
��i�Zm��:�D�[�}Ӑ�z>�y%���}�E93�y%�l�j5i����i�l�a;�;n��8�)(�jB�y%l�}ߌ4b��7���9��A��Vdl=����Z��5G��^��X���{���!���l�a/mwN��?5��+�ބ�a:b�a���i����=W�a9�{���i��+���-ڌ�ir�����i��?�����it��{{	2n���=�y%��}3��LGm��}���-�מ?���՛�}%ۯ��l�Q���?�Gm�)��c
�
����I�}>�E9�)��}k�= ��i%)��z���ݢ�+g�۹�}ǔ�zK&)�Gm�l�*�{�)N&F�|�E9�f��K7�DGm���u��iD��+>��+���.�8|���3�J*�d�^�+gJ�%��b�K�jL;���d(g�C&��������
)h�o#�|�h)Î��䃛b?��Q�S�<uR^4�a�8�������%5oi�8ؚ��)*�Ԥmod�<�(�W�����+U��1��8��{};�W�Q+�(w��݉�|�9��iߝf��lGm�lo]��{Ϥ�i�(���?�lGm�(o�Gmh-�.�oo�8؄Gm8��z��MdJ���lGm1Y��l)��l)S�8�o� Wu:��|�]6Gm��,!Ɓ����l��xw�>|�'�8؇ �I9�)�gcl}k[�*��5�Zd���P;|��(u9�i���bl=���)��>D2�>���n98\�>D5��+�8�v��<̈́)gӐ|���,!{�)�ɣ�o�4ɢ�oO�1�b�f5� �^mh�,!��iS2'���߇8ؘ�x��}X�y0��-3��|�_���o�8�w2�iS�)g��b��;gG��b�C��RH�Ą8�Aۙb;��a���{p�;gy�
2O�)g�HdK*�ii�8�9�V�>�آ:�{�����ߍ��c+(۠�
���
	�)g�l�z�ia�)gB��bt��bߌ��y�
2(��a=���7��RԂ@+���dȀ܀샙b��$!��l�i�����l�zq��2Њ,!��-3��Jmt��b�D�i��f���a��_A�z�l�{����܀���_�����ct��z	��3�'�'�iǒ���8���(�>�{���z���&�CЎ�)g�/g{��)o��{�f� �i	�z:�� �i�Z�kocȣf��F��D�iܒp�=���*!��b��%&�C���z��8�`�S�k�Y�	�h�	�8�W��>���e>��zB�(���fǗ�w3��$��a�i/g���a��z5��z��y����{ex�᣷�iՑ���J�g�|�"�>�f?��z�d��aR�|�X��I��h�o���)(�	���hZ	���)����#��h0�)�=�跸8�h��h�)��(\�{�l�i�ɣ�)�l^dA�Jm�*!h��.������he�b^��zvD�(��&��aɗ�z�5��������h��|���:���)�8إ�boʀ���h%��)��)�&���h��)t��z�[ofi�8�5�8фʀ�|����hՀ6�^d(�2�bmfA�)��O�<�}+�8�k���M��h��8���jւ�9���h�)S
���)�4�b���h�l�&����C����<���aQ�{e�l�bŋ}]��cs�|���p�b4-g��)�i#$�8ؼ��y�}�մ͌F���ܒ�~Y�b���S2'��b��a~Kۥ�8�o#�b�))��+ս�hq�}g�|�+�8�ߔo�|ww��aÃ�hO(:炀�<�F�9���:�8���)�^d�)����ȴ��o�l�b��8ؙ����&
�&.���0�b��&��(:��a���%���6-g'2�b[��x>ۀ	�b7�o�J��`�	o�DD��b�ܡx��?p�o�}ՠ�_���P.	�>nj�D��x^�
۫���	�}���=������{ a��f�;��(Nm�ʇ�z���c�-g^�|����x�
)�lNmSe�&Ղ�%��A��?�z{�)�� �0�b�lNm���|�lNm��`� �&�l�b�l�b�G�z�a�&��)
����z��;h��`n�z�G�&/Nm��bT
)79�b��`�i#�l�D�ۊ�(�Y��%o��a���e�os���ec��	���>Ԃ8�rwe�\�z�lDD�|�L �g���aZk�h��{'3Nm���c���S��x a[�E�o�����ۧ�6����&F��T�>Ԁ�+=�ĩ a�"'��)���	:Nm�l�z��>�lDD��h���z�T�z�l�a$7Nm*
)("'��z�Nm���z9�R�������ix�&W�z9�2Ó�z("'��>��h���(�l�zd��i��<���z���i�+?:�[�Ȁ��D�h_�ڥ���z��{�>Ȁ�	S2'���%j��c��-����i`Fۅ��l�c���iS�"g��j�l�c�ۈ�&ه<��� ������`/�l�c9}y��iه<���i�=g4��aOV�a��&�l�co�c:�c�Ƣ,�>�i��a����a5���44b�Ȁ�l�h"'R�>��z�EdC�?R�a�@5u��iP��	5u�P��#
oϏ�%g�.��h:�5u�	�.o�f#��{��Ǒ�O��xb�uCd��ii�f#�5uS�"gk��z��(k���G��&R�>��z���&�l�j�5u���i��uc�l
oD��i�(a a�P���JW�>ț�k�
���>�B+�����k��j�Ɂk��
h_���9��C���.��beC���m�+�jKk5u5u�l�c�#d~���@��€���(��t;��(���?�&#�sS:�����Z��{:�az��Sm۫��	�
C�Ȁ��Ca�Y
 
�|��@+���(�d~����	I@+r��+z#@+�|�o�o_��k�8D_U
o+5ui�f#��<��x�)���	Z#5u��־�7��ze��ec\�@+k�Zd8Y"�@5uÄ�kj��g�
�g��|�Kk5uj��nu:d{A�)��
h�zeo�ߴЁ��J��R��{m�a�	�C��xʈp�L�ze�	͌>��;cdv�ZdI�ze�k�
�z	'�k=�ze�ze�ێ�z���ׁkG���ʹ=�ze��ze���a�Zd؈�bh�؂y�_�N0�?>�?=����zj��,耕�4�ze��[v3��-=��SD@+-�)����)�z���,�����ۮ'�d���k	��	��ŋ�x��+��z�,���z�����!���z�#1b��,��z�?f�i�ze��?l��%f��z�|��
�(��ze��iw6T�g��ze}F��,mFaS�a�%��h�|�:7��CW�{k���Q�ze�Zd"�J��x��7�9�8خ�f�t
��8��:؅�M��a�����-m�a�������xD�8؇�e����a��ޣ9�)`�{�+��xO��-�;�	�i
�-�8ؗ��Fv�(R8��<��>��8��|�.��-��`%�z;��{K>�z�����ۆ�z�v�;��d�i����8���c�|�˫�-l��<��{�8��|���E����z
�i��z߁�-	41瀣x��{,��-Uml�8���h��,{�x ai��z��	`�ဘ�A+
)\�T��)�>����N��Q��i/�a'�Cm��i��-5�8�}�|��"'݁8�m�&є�,��h���9�|����(�����;�g};�8�"$gRF��fb(�(+��ܲ��h���-�|�m$g1��i�F�3��h�Cm��iÀ�x$g;�8��66�����8�Ӥ�iڋ|�Ԃ��U�z��8�Y+g�T��z͑Cm��x��[^.�y�y��j3Um���]��,`��i�W$g3��h$g��Zm���jB�8�TI�h�èx�8��|���b�$gxWo
�^9�!$g��}~��Ϋ��%Um�@)ƒ꣝��:�)Q��W�R�y�b��I)���w�Zm��Hm��h�)�t�"~e�)�r
Тg)=eQ�ŚZm��W�ʇ��K�f�8��ec�ٵ[y�h�	2'߻�bz��,��h��9E a����+|��,��h��}��c)�z	����|��$gՀy}��l3�> ��h
�P��{���99��S.��%��t{��{ymwn�����Y
�x���ҡDJ�V�b�h6#&�ym�z���r�_�h��{��g���	5^dȶ�?'�|���hh �ѦN�+�V�xg��?���{Ά�6,z{���ǝU��&�h��!&I�z��i~�ec��{ؚ�fo#{�&�B�$�m�z��?��o�|��#�i���{��ꬂ��?;�|��{�-�zm�z߁bw���a�)^�z��Ɓ;�٥$a�"�{$�8�_Ոk�e�O,€ a�C�-��+|�S�zȀ���zU���Xm���kl�i�z �iB�Xm+�8؆
=g��I+��Xm~��{��>)+�z:Gm�i~��>�k�H�zo��+���	�j��i�Xm�`~�
�>�ec[��{q��=��ց�b8d%�
�-���{	�h�&�j]�[�XmD��{ɚ�e���=@��{��Xm��?��z�Gm���l=g����z��-ރ:d���?m�z9�ˑ��y�j>�-���@�'�zg�\9� a�Z�z��a{*�����m�i%%�jnZ�m�z���a\Gmd�Ձm�z$Gmx�-�
�%O�z��a�條^m��a&�x��ڌ�)	�iS�|��!a8d%)�@��i%o���mo\��c�fp^m��Xm�GmI �XmA�J^�#�(� D��?--o�"g5�Od8
��Gm����္���
p^m�˕y�j=���J=g4��a�
۫Œ=�90�=g��?����	�"gO�j��4@�Gm��[��i������?��jQ	��@mA��iE�>!Ԁ���4��av�$a�� 8�����?
X�{�as�����{]6�{
'�� �$��磕��a�ٙ�
�X%*�>!� �*}	�e���k m�{���a�����a!m�{�o�i������i"��a����k�I+À��w�g���}J�� ��Sl��k��~��+Z��H��y���)g���⯂�y�+�?!f�e��j��c��y��?׷)g��j䐃y��
�O!�a�\mv��݃�f����,�í��"g��)g����k�*A���z3�����kf[��#�)gD\m���(�>!���yh�{�:�im����e�E>\m� [�����܎��(�(��>!9��� �쁃y&��?��{9���$m\m��=F\m�%�{�E)���a%��h��&m%DƁ�8�i�{'��h[�a�{y�<�U��,(m).�����%�G\m	��hygȆ�)�)gAI)T��*��aw�f�r�p����F�1	)��}�2��Т�ec�)lů>+m�a��+���aʹec�	o�}~�x��\mu�a�ecbQ�h6�{�!�he��h���>51�,v��)\m�%*$�a�U�a�\m���h��#��h,�-t��z{�ec�R�a���>��zj�ec9���-m\m��i���$J\m�ecq�&�.�-�\mx�&2e)���a��?5��&�W�
)И�z���/m)J��,0�-k��h�ecl�{�>1��z�ժ�ec(��+��h��Jm��OdY��aB��+.��?1�Jm/��-����ˡ)_���-��-{�h��v2��hm}�7:9���[�E�"�}3m�iV��zh��4�-R;�i5��h�)9�3�-�}��h6��aΌ�_�o��:�i���e�}g�c(��+�/�a��ii`�a�
�k�ecOV�i뀕#7m�k8�)
�ec��{9m�aq���hY)�&�a��i���-$��{�ec��+��f*Z�ϣe�"n�}
��>_��:��z���.��-��z���$��ۡ��i[�s�9�L9��N�����L�}	� ��+.Q-gЊ�-��z% a;m-g��o��i����������&����oL7� ai�y��EhH[�iC�o�)���<s��Ti#<m�if�Cmri#g�i��ao��c��y��f*����S��i+J�iB-g��N�F�o=mi#d�B�(��it�}o��o�]���};����e�����Cm>��{�Rݰ�	o�����}?�^98ENm��w@��{��f;�A�Cm:��{B�>*�c�����(��>{ aj�}�j�^'-g��^9����CmNm�2aܛ&:Dm�a���zf2a�-gdfsG�H�N�aۓ�Sc�a��Ӽ�N�䀦>"��h�L�G
)��w��z�uI�)l�ec׵CmZ���=E�o�֣k�o=���F�Cmh��hJ�A˔)G�Cm(��ߨ��o|F�z_��
z��h���z"2aH�>M�o|��Im�-��=& J�	o��Cm��Т a4'x�c<2a�)z�c��)[�?��Am�1�z�ێee����o�z{ۦ�4'׀(��z|�bf2a���NmWd��i��uˆ�>��ec��)5	�#��o����bdo��b��%�lV ��>k����|��	��8���'���?� Kmfu��>Lm}B����i��zƁ�==fMm�-Nm�&Om�{�&�?b��>.�&�)Î�hPmf>�2g�{vk�?Q�E9Rm�&�c�{��Z������
�9���Yj7f&�-���Ŷx�m��8���ۃ�Zӣ��E9��i�f�)S�E9����Ȁ܁f�ec�4@Tmf5�	+&\��+���	ǘ�?�$�XUm�{�H�z�
)3o
ot�؀VmGmȃ��9<�i�f#g�Amb�����w�p�Wm
o��~��iT�E���Q9����|�E9���[��c�
oZI5Ad��2S]��Q�����e)�)D��E9ďAm�f
o�7��z���i}}���E9}��X�'DY��i��5i���I9���Zm}'�m;�`#��{@���<�b~�'D[�k�}�:�{+��k�&�	�b\mGmng�_��R�{���kcYd�}��'D��i5׌f�,����'D����W��奦�ic��uRA
u�v���	�,.��i�'Dd�e=C�UCa(�'D�ˋkS�m#	��Q���K������}:�c���%��U�Gc�h�=
on��%�'D��8DUK�h%��c�ȋk(���hi�h�
Gm#��i��ע���b��f���-]�-�ʋk��Ca��̥a�i%^�-d� ���-��h�_Gm�ne_m�h_;u:=�>�'�'�D��'D��=܄f(Q�h`mEmo	�Q��bam�>Ӄ�-b�h<f�'D뛌-z�-d� g]�'D�$�h���k��z����bm�z���-'��k��'D���i��>瀘+m.�>� ):r(�z94��hb��iT��-��b�ދk��{w��^cm�,�Em-��i�!uL*�>gM-#����Ԏm~e�zHՍ?@�x�n$�f<Y�h��>(3m~@3�h��z��[v6�,}'�hӃ^�|j�z܀��:��c7�h�����'�zdm�h���;��.�x@�_��%9��-�?���-e�?��z!���C]�zf�%|�!u�FaȀ����ܦ%�!uc��<*�z��gC�����dZ�_8)�>ܛ��À �7‹ۦ��������|g� W�>��Jm�{	�m��ygm�>�#�z��
۫̈h�Jm����
&�����KZ�>��<-���Jm���X��b�����zL�JmimI+b����I+O�Jm���)��?�W ꫭc��Jm�):�/g��!u��zr�Т?	����݀Jm��!uU�i0�g�Jm$��x���?���b���)[�	���<�
�y�Jm�eg� ^�Jm
�y[�`#��E9jm)�	`�:�Ǒ����ô�.k�	f����]�=bUm�$�?j)��c+�mcYdN�i����쁻j�.)=������`j�=�ėUm�Jmk]��^�c���F�\��s��9������xl�j~�
��(쁻jm�	f��fI��ޯ�.|W�	f<�kЁ�"j��>V�Jmn�	f��kom݀�����Jm	�ma�$�p�	fv�k]�Jm��<1��$�?����Z�	f��?��kh�~c���qm�?�Ҵf�Hmr�o>��	b�o��摨�Ȓ�Q�o3��+��)9�����i���XT�=sm�?ރ{�ރ{9v����i�L9g���k�D^d��Hmt�ih��cum�?�.���ii�?�g�ivm)@�c׿	f[�op�Hm�i�f��d���ޟ��i<��!�8�ۢ�ô(�í��}#ے��j�	fP�Hm�-a�X�쁻j�i��y��_����#���'��o�-a(��l�I@i�}#��"��}����
:�Zڣ���Rv��o�M+4�����wm�{F�L9��ax�})��Ԙ� ��M+p�?o����+;
 <2aym�?�;e�3�?
�ôA2�k�bNm�ϋo������:�z�}��{�}�灩��#��?"�Ҵ@@NmP<�?���i�i|�}��J�7��O��
Nm��7�{�zq�*���L9}�i��i�.Nmtӣ���i�7��|���?�L9��^�����a&
=����?�e�⁆Q�g��~c�#�a~m�{
\Nm
	J9�ӣ"��k��H��z���À|8m�{���8^Nm��{k�%f
fo{�@�+)�'w�*���
��d�a�m
f4�i�m�a�ôv�_��,��8�}I
É��x��M+5�Xm;�}����e���=�����8 $)�\NmǘXm��}݀�y�Nma�-a��8w�W�i��Oߖ�G�)mm
f��8WD�%k.|S;�a��{�mNm9�2���_�%�>m�?h��8�)U��zD�,��i�mNm�m
f����� 
f���0E�a
�Iz;)��-��z��^k��8��)d)m{��
fִa@"��i�Úk�Hd6�i�����8z;)�mob���H�i#�.Df2a��?��z'�i��Xmvo:� D��.D#�xґ�1ل����n1?E)�m�xl�.b��kCE�i��8�Ѐ��Z���%{ �m�i��(uY�"g���{�(uI�Bv	ؕ���{�6�i�i#)�:�m
f�)(�i�o���O�)���샠1��(u�m�x�m
f�!M� ܢ���{��H�x�c�(ut)L��(�	�_m�s��)�݀h�"g�:u΁���_L��d�)�x��k���d5�(un3}S�|�5��ko��m}>��(���k�
��ւ�1`��䚓�zT�JZoc�(u�ˢȺ�z��^k|��>���k΂�cR�i��=Ą){v%G���gf}3am��k��y�2ִw���l� ��k��y�}�o��`�g?�F�y���"�i*�j
W�xU�&e�#�I���m�&�i�m�j�$�xc�(u�݀B�yK�&Y���k|���#A�x�(ue��`���N9\m��ke�:���{���;g��`;��=�
��2�m�&<֣�m�&�í=�v%����`Ā��\mc�Cd�}[��k����A}��{l��K�&3a"}�B�j)4'���4'��[�m�&���d~j�y{��Ɓ۷�m1DR_}>�&Ԁ�i�Gо�;C�m}��;g��)g8��_�Ut
!g��/���8"�;g7�p�m��ʫu+�m���(��x�)�/C<�{o�%�m�&z��z��؀���転��`e�&$�z�	;�����*|z��&��&�z��k@�&��A�J�%���>y�&���e�p�B���N҅�`� �O��m�&?��`���h�zg��%���a�m�&���>�m�&F*!��z��d��)(�z�ݰ��%Ɍ�a��)��-�m �mf��)�`#
O�7�)Q5X%����)� ��%�Hg�{s�&��z��%��)m�
�Of\��z��{���aЁ�ilj8�ɑ)5��i��c�����)�1�{`�v���ab�>�ۇ��
*��-%�))1f��}�
�{�āVT�{����%.��a�ۍ�%9��������&�{�
��ݨm�{�XfeY���)5�%�?�{7�8ѩ�)�}p�z��
�m�{B�8ѷ�}��zӃ�>�%��^9���a����}���aZ��i�FҾ��>�m�k��1{x8�m�k��������)K��6��`	��zl�)V��aO�}��^9��)���a\Um��ڨ����`�}�{���aЊCm��)O�޳��a��)Q�^9�m�{��)�7�R���&��{�	�`�]Um��%��1n�`��kЁ�bZ�(�O�{�8f�"'�/�-�mf�D�d�4��?�{%UmԀیW��|��Cm��61��}r=d��i�����xdρ9�j�	ofv%Ђ�|��k���^�Cm5ԣy��vc	��i܈�i@
)i�-���dUmѬf=�PҩCUm�0�@�Cm���=
)��fk����8�ʈ�k�`#�m
)B�8���2�-���Ck'�3�^�Fh݁`#� 
)�m�-r
,���5��)&0�y_UmUZ`#*��k��%|�

)X�km��FhR�>�oy�)�*`I	�V �?-���m
)"��i5˲��|4�	h�	�	��ݘ�"�#�a�
)��?�8�����<����	o����(�?S�i#Ҁo���3{��|	��m
)���i���+jJ�a�c>�Am9!��F�K�a��a���6�-��{~Q�6���?��i�m�a5�����偢}�:Ja
)G�a�m�=$
)m�۾m�a)����-��2�m���`��<�
)���m#_���K�@��`t
)��`ף�
)��»1
)��)I�����&��b��ft�z9���Am�z��{~bл�Am=��Ձ��{74�z�������5,���8D��>&�y&��,��Z���`��?���A���&��
)�m8D���`�� ��>����e�&�`
ݴF� ��!�a|���V �?�M-���`���m�a����&I"fE�CV/�y
���f��a%�Xm�Xw��`wtf�b���&���aR��&:�f#�b�a�=g�m
o,��a��y_�"��Xm7�kÀf#�m
oG��`�)KvD��`:�)D�Y)B��`��k+�Xm�<�k(�i���`%�Xmj�Am=�E���X���%^�h}��n�ekw�����)����Q��U8D���`��:�)�m8D�m<�c�bTCxC��m>D%�Xm�f��>7�k��2��8D��ۀ����Xmd�Ո7��&�m�z.�<K�Xm(�	f5�����j;vR���i'��Yj�:�z��&��{��bT������'k��XmS��&��{�6��ffiZ9�J G�4Slj�
�
o��f�ڛXmא�a�D�R
o�m�{�{�~c��X�����,D���S��.��{��f[M-t�<�m�k����y#5=�m '
�)�Hzo��'��-���-ʇ)eC��zm#��)n��x����&���j�&��)a�Em�����m)rWm�h;'��-�)��lZ
=D)���9�ի����z�	f�����-��-��	f�;�߂?a}I+sEm2����)�E�������i텞-ւ)y�i�h�g��)�i:�}Em�G�a��u:�Em�)��}�����}w�4������`���X�y0��y Y2��`�mEm�
ף�
 V��Z9�;�����{���;��{w��}�����-�CZ9�摄_\mr��-O�(��}3�N�6��?#�?ax�}��`aKvl@+�͞-Ȁ���X�K���m\mQ���)�`�m\mk�}��-�Օ\m�}���-�@ףw��
�a��(�θgNd�\m������)�m�&��M"�Em����}e�$\mؚ���&����<D�����&:-D��}�m�`m��O�\m)?D�j�aD�<D�F+E�}�c
f�i#��}��-h�i��貄}��fӒB�-J�۸�%�&�`��a��ވ��Jm��%o�Ѵ`�i�z�F+h����X�'	2N
f��i8��	یM"�J\mJ)��}�
f���]��E9��fw�JmO:�"�M"�m)�'��;\m7	2�	�%�&�����%�t����	�a�<|�X�k	2�m\m.�)eC�3	2�)+��>nƮ�Z�E)_C	2��-�-a��)F
���)��{&�>��a:	2&)��f�o4�)ƀ!#�a��k��)T��c��)N)@�(����m�h�%&`���z�f��kH�f*����z����<r�)��Q7:��)��c�Xy;�-��l�[�k�]�^�kG�?��	��9p&�%g�)�]�kE�)�

f�ހ���c��c,�{ބ;��&)�
f�k��-O��c�Hm��c&
f�Zm�HG9���c�!`2	2u���	2��k�
�L��Hm.�)������<$G�Ł�b�)o�)Ȁ��ŏp����@}��)]�^9M��z����ހA��錀�c��f��c���ɠ�f��II��$g��&��^9�ˣ��)�
_�����)n��&>*|����#�kT���a�x#4�X�:�i#g�zB�^9|��~��k��d��&_��,��c�i#s������k���&Ł�boˣ��z��Cmv��K'	c�}%�kw��Y�����4~�橀����N��c׀��o����G+k����mNmv�����&*�F�7��&@"Q�^93	}Ђ�<"|ew�4Z.��cW�^9��oR\Nmg�?�����2j���?Ϣc�Nm�	8!]���b�_+$� *�ߣ��&3��W��
|eȃ��i#oմ7@"ی�&�m}k��&b���͆?'�gؕ�'���&�K����&ki#5}��4�Nm��D��7��&@Ӹ�	8!��/���J�XGJ�X��ˣ��{���&����+�Y�X�z����@�.�	����&/�CmHK+��&PD�z"��?����?Dˣ��U]b���_+�>�.���������]�H�m|e;��2�j?Ɓ�#4�-�|e��~��z���m�z���a{�&��{LF���f���f����́�%��#O6�z�e��>���z�ek����z�F�L
�����a�R"gf1$����a��%)�"D��-�ST�����a������5��%O1f���a8��i��� �&`w���氇-���%V	���m^mCd"���Q;۳��%��c�z�^m�Z��mf+����Bv�
^m�m�i�o=��a���a��Bvh�_�b/.�aщ���:ub�z�3�mf@�����zt�<��>�b
B�z�{����VE�c:$�z��H���i������D�`�a���.��s�������%mĭ���%���aՂBv�M���a9�z�a2��k%ԥ%���:u���;d��%	�%���(u	M���%?GGm��p�!D��k-��Hی:u�
����s�cem�Nk����ce9P�	^m���a��m#!���|��舙m#���kP�a��ce�
�m$��bU�净��W��k(���ce��m#����ce�oY^m�^m��i!
�b��cec�bs�ce�
����k:�a�ሀ���Cd��m#�}��k��`���kq�ce*���Cd׹�k���i8Cd*!
�'��R��A�W��k�m)%Gm;�ce���&�m)��$�'��ce,)N̉k��m#:GM�m).���e�ط���k.��׊��b)��VbZ)�����`w��k8����"�[�mGmf��!D��ce+�)��k%B)��`5�CdT�c)F�)o!D;�ce�m)��ce���$����)k�ۄ���m)s�ce��m#�"�O)�)�a��CdR�;g��ceHˉk��m#�5)w�ceL�8mQk��m#�m)�ʉk��ceU��k������)����k*-�a���4&D&�"��;��a��)m�>���	��%3�)���|�a��7)��aoˣt���ƭ>-��a'�£�m)���a��?�o�W^�m�?�)���f&)���>&��%�$n�){�`9��%e�:��yg�ar����Y�m�iJ��>(�?�)��P�����fB��1��-
�Qk\�)R��an�i�tI�0�.�/o9�	��̴�a%�_��$n�}�
�>�����?�}ȆFar�)��F��ȃ��=��6�-Ã}��.$n쁅ȅ;����n�&���x�=n$nc��n-g-o��}�o€£A�a�#p��	�-��i��Xds�-�6�[o�}�T�k�t_�Xds�-�}��k��ۻ�}���.��變�L	'�k�a;��oe�s��^�-�}�����-�������a܎Jmc�i����o�o�"f�΁y�@�k���a
x	n�k�ԣ
�}��LM+�?n�-��iUB�k*���gk�.o8�	f��kƇoX����.��L��i>-g?	�%�c�Ջ�i��o6�}�����}|���Q���i��ka�}��a9�Z����%D���aЪ�c;�	fvo�׫c�j?��}�o��o]�[
�}no��C�.���W��c5�5z�k,�}-on�-���a�c��k����T-a��i�os�-8��a���z���Eds�!���-� �k$�{d����)���-g�.�-xuIV�k��)n �=�k�o�B�)�)+5l��k	��z�0�)�{���a�)���¬����%D[��� ��c��zN��*��
H����D�&���c3����a�������?�I"M�c@�)��(l��aВ)��?��zV��i
��c�&:��)�i��a���b.�&
��	��	�U7�)��AmY�`��?n�{Qk�_|���n�&��&�.|��Am��_�4T�`@ԣ��{�Qk�Am�)m��.�HТ�@��F.�`3��)n�'�Am�Am��4#���)��<ˣ�����z�?<�J)�\����iI����}�Am!���e: 37�Am���:�j1��&=�Am
}
Nm>��<�´Q71�7�ւ)Ӄ&:ه�`��Am��K�&����k�)�n$�{��t�Xmf_�Άn�&����'��ͨ�`v���p"���5Z#(i#��ۚ%h��(|)
f�i#À�ܣ	ˣ;ˌj�ۢ��93
f���5�JdӧAm���(|u����8
f��	���Am݀�`���ꑓ�����`���6��E���	
fی{~+�D_�
fNm��c�cʈ��)k�XmQk
f �-� �c:�)n�j?�j�{��CX�:H��
!�-���i����
f��	o����#�d����@�)nl-:۵�-���X"]���R-��2�粇-**|)���8"�-ǔf��F��U6�ax�)n ���H�f�ʇ-��)n|T�c����-�����€´���lIg�j��۠�`��Xm#�Jdpˣ	���d�c���-��Jd�����f�/�cS� g��tq	�fP��`$n�c&�6뫁ʌ)���%�"g&n�a���`'�f��ܹ�f�Xke	�aÀ g;��a���z�UkQk�C9΂ g;�c(�-m�c�ˣ�
f$��-*)�
�a-@�c6ǘl��`/)%�aM	�cO1�{��)n}��a)���ӻ�-Em�S7�p�����ZEm��)n����k�*j;Em��)�E��-*�)n?���+�f˟�-,�-Y�l1-��&4�f�� g�Em	��i���Ȁ��z��i)��܃iV/�I���	�cX!Em���.�!u��z�F�%��/nEm���k>��at�c�M"�!uwVg��m#3������k���)����M"4��&J��{I������c�m#�
\m� g����I��4ne�f�Em�Em�ԣqu�=BNd��!u���
z0�f�c���f�1��a����;c�T@+
��`���)��ܗ�!u�M"�>�-�I���&�Nd�
)�����a�#2n�-�M"3�,D���/Ё)��#��,D�"n��]��g�\m
����&��`��db���F��O:?D�����c�,D�
\m:� ���?���\mc�m#“,Dq-n�\'D���c�-n"nV��ݙ�!u,`*7��<��,D؂�k%#'�l��cqE�gI�4�,D���cV�Xz;Xk5ۀ�?�_"k\m��x�߁�mc��$�ne,�=���Q0
o��	`�Ov'����m#V�,D��=+J-n5-n��^�
��$���\m�
 �ne��%5n-nB
Nd���/�Y
o�-n�J�#neH��6�zT�Bc�Uk��
F���xȄF9��@����O$g�
o����{�D��<�<D�<=�%*���#�$g��,D�4噆�c7�,D
�a��Uk8n�k�\k�� ��9��#��ʌ9n�a�$g^�zƠG9�*�`�Xd<?�a��`97:�Uk:�,D	�Zm��aB�G9��_���-n�$g;n�a<�Hm��G9	�g$g=n�`�j�������ꮢc(��)>n\?n�k%
o?�ܣ@��zy4S+�`�mwf��c'�aA�Hm�`���zńʌw�`=�?��c���QD
���!DL�\kBn�`J�Hm=h��_C�Hm�e���`*��]�Hm���c��zD�cEn!D����ٳE�瑢c������&�ȕQ�^9��`>��z���ƨ��c��z}>��h!$g�*|�Hm �Y��k��	�� Ɓ�Sk��r��iF�^9�\kG�cg��h9�:����c7�b~e��h�
C�t��c��x?H�c��ހЁ�������I��h��d[٩cJ�cV�`2��&�$�HmӒ�c6�-Kn�a��/��hL�c���c#�cҥ�c�c��ո��cM�}���zЁZmȀހ�{���4Z�)<�ܣ�Hm���zd��h�ũc���z?�Zmލ�h ��ź�i�����ހ��8��)Ł�bN�c�}��h��£(��Ca܅�hX�W^)���Um��CJ��*Xkބ{���iЁ��c�$o�ܣ�%u�k�͒��������0]ٴ���� �p�裯�`�����h'��=�⹁�{�c��k��b��68!� �Q�� Щc���ht�ףٴ��cb��_;�[	2��h��?���N���Wj��Ç��w�O��hP�c��k�U$ލ�h��g���":�Q��htٴ��ϙ�%����dR�aSn�c��?���]�Ѫ��|�Q�����	r��:�/�����y	2�K$nT�\k��#Q�f�L}��\kg�|����	2���a��+�=�)c�|�Un�z�\k���d�z�|����_������?������S_�z�K�=�|����������%�'!�W$n_3�z�'!m�K"6�\kVn�z(�'!%�&�9�ל��(ul�.@	2��Wn�zr�f�Xn}d��	�>ٴ���À�܋�Xm�Q�zYn$nl�c�٣�����y"CdL�z(�����:ukF��(u��Q�o�	2<�n9֜y	2Zn�z[no�%|���:u9��c�c�#}��Q����d����\#+�h�����7h)�'!TA�c��:u�aҀY�M�-�
�cǴʌH�(u���a)\nٴ��|���a]n�c��Do8�m�o��)g��#Ȁހ
�z4})�?^��a�ɛk7�{_n�z�d�z���a���{=�F�`�{��c��(����aan�z���{*���bn�zeh�c	��{۪�a�
��Ȁ�^c�{d�(u���F����a��(u�:�e�%DR��aÎ(uf�{��aւ�k��O(��y��{�ћkg�Xm���{
��gg�Xm4�g6���abXo��0�hn�c��e���{d���tin�c�ܣj��a��oٴk�otcc���((o�o�-q$��Qk.o� ��co�N9ܖ���Uk�#*�Ɓ�@��- ��`a��e�.��#��ln�ctT-6�\kDF�5�)���'W��aؖ)���{�X^߂�LZJQ�)$I+��)g>�ܣ���f��l�|���aRI+3��b��zd���m�)��n�ko�{�ۇ�gѧ�ip�k�ʌkr���%Dǔ�aw�x2��*7�i	�)���LJ�����X�V^ �)S������P6����iϋ|����h��-3*��z�|���h�
��Ё��=Qk�V �=9��h��~/���&�`8k�镼)��Y�o�|�" �)" 3y�-3�f!��h����t��z> 3;�)�偶���(�
.�)� 3q��h��)�|��&J�^��h����).���n��hQW9	_�)�F����h��
2b2�(,���Xd� 3��&:k��0��h��/ыWբ��o���h����0�O�� 3r��hs��h�I��\k��hhL6n,#D�\mɑ�ha�{�Xd$�-�����Ɓ���F)h�����t��h~�F��	��au��h��{��	�	-g1Xk������h�L>�{��X�
2��{ǒ���h$S�cvn�a��{��hR�-,�ce��hy��hg �
��2�|�i��h�5!!��F����<��h�4�>��z�`���zy��hw��hxn�{yn\m��c� 3��I-�)nU��z�bzn�{;�a� 3������ӱ�	o�a���)��۫��^{n�a��{p�-�ay-g္m뀅#<��U��z����h�3 |n���	��::����P����(�}��)?�i�c�}�->�:����~��h:�uՀ0�j�&�*dw��0b��h��i��,�������p%��hn�{�
�bo_��n�i����=�{�dw���`-g�iD)�-�=�z�Odw�e�a��-4�i�f%uI��"g�"�c\�-V
�b�a�W�i0�!�c�b�_�c����5�08��<�X�a��|���Ukݲ=��	� 5�v-��)n/��)���'�|�/��)	�g�b��W�l�|�����0i#�I�x��g���zT��4�)nƀ�z�
ۀ�)���PY�����h��)�
g��)R�#upL�ht��ִE�m�N�-��a���h �#u��>
�i{��b�itcc��>��#u��nc��8P�Am�<�k�ہn�z5��/�{���z��i�C�-܁#uY&�Q����Amw��/	�i�f=B\d�:DЂ��2�8�#u��f+`:D�)���/���h�ʦ�f{����6_�5ƪt�Am�#u�n�-���%k��?.�����/�\k���?(��/�(�-,�\k97(��/���?�i#������{Q�-���/�"|x�-�n:D��f#�;\d��{��:�ĉ�/��#uH:D���{Ɓ����?�����x��#uv�崄n�-�+-n���/��#u�y�(�/�'Dr�p�%�?��#u�{��#uR2�z�,D�6�+��{�n�z" �Zd�#uy��{9��k��?S�z���_���{��#u��#uU-n��:|d��^a�Am���v��/H�;n#��{o�;n_.-n�X1d2�ۜ�2���/?�-ه��|e�:DQ��Ήn\d��\kQ��a�n�-)��-�t�\d�n�-�� ŋk%J%�zo��?�áO� 7D�|��:f��oJ��A%� ?�z�R
f��z�n�z�,D`��{��-?	��"�cwH����{��,D�x������	=j��H�;nK/�cތ&�`$f=��	��Q��;n'-f���{�~c��P��{�R�c'���Q�c��1�� {�c�g���AИYf���c�cV+�zu�6z���� ��zg�|����� ����Ɓ{1r�ܣ�n�c��{�c��~1�+-n[�	=U�+�|�S�|���z�'�,�Y�/�|�hI�cgEm7L�z/��-w��δ�z��ze�� =���!D�ٚy�����~ciԁ˺�#�iL���z�\k�!D��o����a�o4�ze��;no���L�)*�iw��>k� R��iVȀ�iQ�����J��`��
�)+Em�n�c���?�n�c���w�	��f���k��:�cz
f���h�2�cm�\kV
!DZ
�ԫ�i9���e��z�{���i���A��i)��z���?"�ۚ��i���>tz�|�t�:�#��@��ii�Y틎�k*����iv�!|�毛��{K�iw�;F��~1l��i���Ӟiw�X9��iQ��i�݉k���i��J��n��nX9S�Uh�\kڋ|�]�ze���i@��k��j�z�\kC��i;��-�X9ۅ�k��>D<�>��i�K"\}�X9�!D�!i']{ӯ��iҀ|�&��d�V��i��>D�":�|���`�nX9G
�O��>D%�����{e�����-�!DQeX93���e�a�\k��f]�񷍉k���{���z��'f�⣴��i�|�5��k@��k���i۫́���i��?b�f��i��،��4霉kۆ>D*��i���)��k�:����
��Շ_"���i�ш8�K"��uR{��it����|�b���<�¤�.ބVҋ��_�������p"Y+$��-�nX9Ԁ��":�n�?���S�|��������J��Y�_����-��ܽ#شF�d��f���屋�yցX�L6X9���	�4�6gI�f��{���	�#���ޗ��fmB�{?�?��f8����j���>D����f����n�z�n�{1�sI���ܲ�>D#�?'���Ӛ6�\k��)��Zm���i��c�Fa\�ȃ��d�Zm	�{����)��Zm���{#Y�qc>�:�&s�{X�*�qc5�Hm.��c
�Zm�8��cc�o�.�-�n�{��{�Ĺ��HmY	�{׀fәOm��Zm�3Db�k�ŀ��d���c��ZmV��ܑ�Zmه�&"|�@v�n�k݄E°n�-�����-��i�1�i:G�kpqc��?��-���ܥ�-�qc��i��i�i�����i:��'Hqc��i�n�{�n�-��i�n�{�����?��ܫ�dk*�i��c4)�i�n�k�+g?�-Q�ip:�z�n�{�i�ҩc��i]~e�n�i�����c<D�۶뭸�Zm��i��+��Ad��%D��c�;�k�b~�Ad���=��cg� ւb~
��4Z��ctZmj�AdN�%D� r�n��Hm$�{�8!���X;�-��c��|�*mwv�m脢c��I"������-�e�� ��z8
qcI�z|���p�`�i����X�q�pqc��-�i�� �4�i�˜d��c��zo��.�E���cD�i��c�i;� T���/]��� �
�(�߰�i�� N�s�d�i9�8�z�f�#�/h�,!��c�iCߖ���-ҀAdi�/�y�UB��c����5��.�����ʎy}��crϣ��>+0:��c��/�3:��f����|���+�n�/	������ccԂ {�-3����nƁ���j?o��ȀÒ�	��kjc�\dt�k����$g4)����A���,�/Dv,�/�ߪ'��cb���bDD	�	2�{�h07^m����4YI$g<ODD�%�a�o%7��&$g_D^m��i��cv��1Q�w�9DD8t%e���ۨ�Km�n@":DDQ{��� ဠc�͠cX_DvD�̴���|��Ø s=�Nb����nDD�:ut�=ٶ �>DDЁ�#'!@"�������w����=�4S�Cd���
X��DD�Dvf�a���i��(|3	i#b�����|�_DD^��ktcc:+f��ݿ���]f�=g��:u�D�c3f*�(|e DD#�c��i�Ģր��5	2�n�c��i��?��߂E�f�+�c��ۥfB��'��c�;gjM�$gݬ�-��f�EdH'��&!�c�7DDf:^m�Dv�#DD<��-���z��i]�DD���zs�;g���I%�d �ay�h��;g;$g��-�	2�?^m���{�Ed{	2�nDD�?�c��-��z}GDD�-�n�a���a6�j�*�c���{��	�KY��+�cX�uc���z'�c���{h��-o�̴���io����)�� D"����ܘ�cY��{X��aӏ�u�n�c��;g�qc��tq�n�c��uc	�cj�{.��z���{	�uc��c����P�_H�uc�=g;f���a�n�c1��X�:�c*f(��_��cBLjy��uc�M$׀i��-D)��]�_��*����$�1	� ��z�wd� �c���a݇-��p����{��p���cU��, ��(��z�y���-��z����a��-��,��a��F""��zÇ-e�h�����,ۯ�a@�hV��{>��{3��ap��a��7� j�N���w��_Q�aer��yxj�cmqc��k�X92�[4*#:*��n�c��aeQ�����i�։k)Y����d�c��ho%�,�c]����kKvc��k����`w����n�-j������kU��,��ae�o*ۧ�ae�f����� �, :���yJ�P�
��@�n�i ��?��F"b�iS�tIs�ae��t�$o��.kێ>ucģ�n�i��k��}@أB�:|��m#�� �T��a�˸����oQ�{ڋ�,DO��n:D��hƁۀ�n>u������hv��ց!�8�i>���
����P�xc�-:�?����i!?:D��
=5��(�?���hh��>t=�:�i�2xc��h��i9�zu��kl�Cd{jc��?g��kL�ae��,D:�?�z��Cd<�:|g�?�Hd�n�{��z��m#�;�{�jc�-nO�ݸ��uc}��k�Iv�n:D�����?%�m���h�37�a�\k���ad��z(��{��>�-���i,M�i�nxc7��z�,o:�i�1�z���i��X�{,�i�`��zԼo��)g��zV N�z��Xd%�Iv9������p(��zI?�i��z�@�(�IvC�,D+�?E�Iv�{.��z������;nl�m�� ��Iv��z��aɐIv�ucv2m`~��zN��z�HdԂ �:|��h��z��Hdg�a8Dc��i�	dw�����:�?���i��{��?�ap�)�aမ�8DW	�{9�w���<���S;�@��{�n�z�8�{uI{�uc�f�z��X��n�/���{��i[�F��n5u=Dn�g5u��Iv5��h{�uc���c'�k|V����
)UE5u�8��z� �i��(ree�K�k�5u3 ��������uc�eed�߁���i��k���a��
2!��i������Am�g ��Am���a�{�O �Amo��335u� :�Am��Cmʏ�c�=v�݅��io��)���Am���ilftڵ��i���z��=Cd�c9[����i�=:��ܟ�cԀ����i:>|��Amz�i5
fXG���c*��a�����'��z��ҕ�Am��"n���o��*���_)�Amܕ�h���H��%��/Ç�ĉ|c��AmZ
�أ���i�ߣj�,���c��iC�>D���c��(�ee�5u��Am�	�Jㄶ
O��{��Aml��-��f#�#5u��{.��z_ %��t%@�f#��{
$>|��k335u,�f#g��{��f#��cR�>D�i"�Am���{�aAI �xc�
�ĉ|c�-gI�|c� a�x���{0j�a��Amh��i�Am�Cm��{���m�Am��f#���i����>D��Am��cu���n-g���-��?������%�DMv"��{ ���i5����|c�MMv<�י2Mv�*�a�n���Mv�F���),�f#�=�5C��	o[�/:��?����
ϋf>��:��'��2�f�ao�	o���a�_������z�T�m��D_�;�x����{�-�h"C9
���Łf#�	o���{��_�c�	o��V�ZG��+�f���fo�h߁ucH6�z�c'��s����|co�۰��{�z5�	o�|cŁf#o�an�>Dt%�����heo�h�3Mv�he��a3�|cD����h��he�
�L
���G�>��{��he�xc<�oMvY�heh	 � �h��|c�uI��)o�h���܎�y�K�o�-��h]�5Go�a��hI+�a�Lo`-�z�.Em/�|c��he)�zo�h	o�-�XGm��z�Em�)�#�z��i
oEm]�zo)ݐۂ�h]5�i3Ӛ���:Em>��a=�he�0)�����io�i��i
o�zk�ۀ�c��-�A)��(��{�'Em��-#�iS�|co�z�)��i�EmY�z�A�c�WmT
�io�z�dEm�2�z��-�,�z7�z@�+�Ad��z�Y�iĉhei�<D6�h"(EmF�h�(�i��hoEmK�i��AdQ��{Q�z��it%��{w�M�{�XdĂ�{��z���c�C�|���b�t%Q3�B�{��g�F�'Dqcu�v�Q��{o�-O�coEm�b�����)o)
=no�-@��͢[X�io�{y)��-���oGmo�c��-(,�i�$�{���zn2Em�͢��uc�Xd='��'�z.�)o)������E^�b?4�io�x���'l�o@㣚����	a�i����F)��V��6�)4@� (����B�	�)oI+�w����W):��{��+5�H�'D뀼���	`�

o��}\���Ǚ�Ad�I"��4T�Q�{"ݎ�S
��f��.���M#�{��v��ݷ�����n���.3�*��:�xӇ�G9���.��G9g�o��'�}o o�b1�uc���}��G9g�ohƃ:DD��G9�Ӛ� :�m�}g�	f��oU�}��G9o<u9%㣙$g����2�uc�xc;Dv o<ug�xӾ{[ADD���EA<u*

o!oDD�}Àx�b�nc��G9
�ZmD&<uNDv��Hm	� ����B~e:DD�
����<u��Jm��G9"�c�qc>�E(������Hm#�Hm�8Xdc(DD�}$oDDN�}l��.v��i�x��*|��.��H�:��c��i_-f��}��i%�}X��WEd�HmXyB��偕�&�Hm.	2�-�{�
��}�1�&�ի��id�G9~�Zm'�}%Of(o	2<�})�Hm�H�*�Hm+oEd����,of�-��g{�Ed-o=gˡ}�=g����(�£d�i�<u
nEd.oDD/��iֶ�c�{0oDDWDD�d���h�)�)ŋx8�C��<uUZDD�2�Ed���c1�)ϣ�cy��'hEd�DD�m��ih�ǵ��ޥ�Hm� �p��f��Hm2�c�}r�a��)3�Hm�
Зأq��g��c4�Hm���i��Hm}�f2P��i��4Z�o����5�}���{6�)�i�
�e�}���i7�cщ�&8�Sf.G	2Ю"gaȹ�g��i��-ϔ}y�kv���	fȃ��o��d��a�\d���{7��A�c���a8of��-Ԫ �ӚY��9�O�Ed=;
f�<	2>��
1Edv�J#�9�)ŋ}i
f�)��i~��a�)Z� g� _����i#�)�����F"ӏ�����c:
f�)���h� !��c���z9�}h�)〻c���z�9::� ��)o}���� �>!�i#�&ƫ'�ܤ>!Si#�����;� ��)�!
f6�?3\*�6� �9O��&6�?3h���o#cP��/�!��W$C�4�k!��C��c)<o)Q�-=oxcT$^m( �>o�-d
Nm�|c���%t%?�"gh)@o�-Aoo%�uc���a�#֢�P��Bo.|d��ah&�-C� ����AA�i^�-�/Do:D:D���a��i`�E�%
fq�c�E�>!�FS1^m	��a�F�?'=�-�-�i�o6��k�T�f"�i�F�-���)Goo˂4@j��k�)cQ�i,3^m�g�-v��(76�i[P�-�B�iǓ:|�łka>�-�)�羄�kB:Dv�J�&�?�p���Ho�{�?3�!��"��>k��d}���Io�i���k?�{���xc7�z�%�iJo�zb�-�m*�����a�{�5�-��?3�;�{�GoK�kF:	;�����L�k
�-��-��{�uc���MooNoWd���zOo�zL���X����?���JPo�-
0oh���{t �5�;nQ�z8t%Ro�i��{S�
2�I�^ma�zz��-��z%T:D��?��Iv��z*����w������ �c��?��iTo�cr�����Uo�����V�k��Iv���Wo�c|�RXwh�����蜅�?�uc�*�����D�,Da��z8��-��c�W�yO�z	_�;��k<��c����#�c��{��?��|�I
	*���[5�{��c�F�{�Xd;��z{�c4�cv���Ӛ�{Xo�z}q1��{k����zYo�{{�Zo���$�{	�1[o�cl�zR��d��zl��
	;�F���1D���-Ǖ�z݀N9�'n��?\o1D���g��y��zؕ�cƒ�i��a1D�֣���z�և-?���]o#D>�uc	f��z)G�co���Yf!��-� ��Cm7��>O�5b^o�cm�9��Y�1D:�Cm���2��9�1Dc#D���6f�m�a�Cm�)��k�Cm_�)�-�c��mo�c,:���_���a���i�)�?�c%xc��-`�)v�&�R�a܀�+��
}��=:����W^5>{a�)�f����"��:7��g�]�'ㆅ-;�o;�x��Cm΁���)�f��Cm��c�>|b�'B���А�+�)@��<X��'S��z����>Db>|"*�'��)(ov�繀>D�"#D5ob�)c�}	�	ۨ��%��)d�Cmf#D�'eo#D4�)P�Cm*�|cʥZd��|cfoxc��}o�>{Տȣ*xcg�)�Cm�|c���0"�}Y��̑p��:�����h�-���+����A�)Q�)Q�Zdi�)W�>D,-g�Ij�)��Cm�Zd���i�t�P�t��'�	o��i�Y�f��m'�︫��L4�)>�ib���).�)5Ӛ� Y��a�^"k�	o8��'m������e�;g��|c��v쒜-��|c��x�^����a�I"�f}�;g�j�x��E9��c�>|b��o2xc�)*�>D�;��j�d�)W��-a�E9�>D������-����0���-l�E9�&nD�Zd��}Ҫ)O�ya�)����m�E9� |�f!ؚf���0�ˆ>D��*Ç�ZdD$�-no
)� :��R�)9���1d)�=�[ :)ȃ�ƕ�)��I&��?�TۢB/���Mv�	��4�r&�
)>��}� :eoi�?�	o8Cooo
)�G
)�]����{9�&���;�xӖXGm�wed�p�"��}y��m��aO�Y�poUm%��98Ι��T��%2��o��Т�[v)�i4�}%+Gm�.o=�%i�Xd����Gmq�a��-m٣+o|�E9W�?r�'Dsoot�E9"�?M��cu�E9��p��?�#Gmv�cw�cf�|c��'D�0���c."&n�cWo�&�k�4��`�'DxoGmy��azo�{Y�'D���a��Ad�&`���a{��a��%��:�|o�kc��݅�'D���cဩc�
)ĉ|c��ڢ}�'DC���f|%��a\�'D
�i���ۅ�cW��c,�/T�`8��o9�~oo�B"�B"|��
��/cL�'Dooe�Jl��/y̻c���a���aǍ�X�׋ކ֢�Gm�	�JӠc��<So��&��+g�oGm�n��o�-h��c`Gm������'��'D!�l��Q�
Gm��"n��'Dꫩc�o�k4��c��}\�f#of��'D>�c��/H�OQ��ݴ'D߁�c��c��'D�
o�����a���a�ͧ���a3P���'DF
oS�Ad���a%��&����	��oځ'D��aဩc*�|c���a�|c=�Y|�}m�	f�':瑉d{�
o)
o�"7�Ԃw��'��j8��	f�����^d���)���߁|c/
o{��m���o
o�f���p�	f	�k�o�k.�o��c�^d"��}9{uk����	f,����t*DN$��DDɀ�/8D�o\dÀ����o�o��"n�oDD�@8D*D��	f�\d9DDg� ւ	fb���PH*DE
o���k��?fDDL
o
��XV*D��he�DD���ݑo*D��?�*D��Jm�|c#DDt
��w�ze��T��Jm���ʉ8D�ze��i�<
o(!*D���i��iT�ze:�Jm���$�&��)g �I+��)�):L�ze�*D���{{
�Y):��5G��=�he��-��)w�FV�c.���X��Њze.�)�oDD߁�3�Wmv�_�J�c����o�c������?���i@�JmWm*�c��w�yWm[Ed{�ۗo*D�Am����.�a��c>DD���i��ucb�k�+Wm��u������+Ӄ�{_*D=�����{Y)����a��Xm�o*DQ\db~�o8D�&*DQ�2���a
�&�oDD\�鷆��
DD�ze瀍?o��?��Am.��ݜ�ze��Jmr�c��Jmk��?W��i������{͆?�"�c��{>�Xm��v�l���A�)�cN�uc��Am��ze��Jm���~�J��	�c��f��c*�*D���i����>D�c�uc�Y3��{�X9�Wm��c�0�c�X9�Wm�X9���֞�{�o�c��cl���5��i%eWm��i��c��{�oWmЁ����a�����c��1�f�o�c��1w�p��'��c<
f��o�'��'��ۂ�ad�ai!
f��+n���oX9@

f�����oĂ+n�X���{j��
)���{�ɵc*����&[�Am�1)����,<�,��cB^)�5�'�Y+ ��c��������%��)�D�'���=e�x)L�uc��'D5����T ����C��'��i4�'D?��v���
X)�
�cSkX9o�cP��i�o�c�)"]�d�aX9�QNm��{�i�/\m�Td~����i(����d�{�������oNm�Nd~Jm)��	Fhڊ�S�B.|�oNm��ݳ���l)�����'Dڋ��k��oO��|9Nm��i�5�'%�{�У����L9��Zmsd~�����`���۲�ck��<B~e��ii��c�ZmuE�{��Zm��<g��� :�'����£L�[M.)�o)�0
f���	��cO�.D"��c��{Àf��c:).|�'D�Zm��i�it�!�Y7�{b��i`�Y�	;���d���ܒ�ZmӐ����Zmu��i{�{�?�T��i��E³o�{Q�'D��i�����GEm!\m��Zm��ky����c8=�{#�<D����B��i>d~mEm���m�9x�Zm���i{���E\m�o�z�:Dl���>�J��oNm��d\m�3=n�	�{�� ���z�9�	o��jX�˘��
�L5��<)�<D��z�� xW�{�8Ȁ��o�{&�{��4U�ZmY�.D��c&;u�
�_�Zm��c��<�Zm���~�q���a�Zm���|��.D(�4ZޝZmn�.D�*�4�<DS�i��7μ�<D9��݀.Dʈ%��}ܢb~�3D�B��}�������as˕�����y�}�=n���8�9��{=��I�o):r��oR���_�i�cJX):��{o�%i�}>�[p"�{o��k��_f��ɢҀ}��\9�9D)b~�1D���V��-�>�-��f*�fЂ�z�ƪ��˻�i���<1D=����-���b��i��/����i8���[����}����ij�z��ipf���'1:�́�'���_o��<��f*�
1D���'��9��c����F�Ea�o�iH��'��i��+�ʉb�k�1Dƒ�A@����#�	��"�kD����c�o<�9��b"�c)Gz�L1D?Ef���'���c3or�k��c]^m+����VK�o^mCީc���'=��W�FL�i��k��cR�i�o^mԞ�c�$g�o^m@��':ۢl�oc@"9�a�g8GT1D�o^m�)��n���'�f[��ꐁc��$gP�ZdU��c��i� f�^m䐉bO(�N�\93��k��/v��1D�^mDefo�o�o)�[1Dܦ�k�X�^�aӃ�'�i��i��Hm���k_�Zd߄�'�W�i<'�aA�i聛b��i4�Hm߁�c|zT��c�^m���'�)8� c��c�	2�c�a(��'��kuĩc�Gz��_���;�_!D\� �����	2_��IL��c{�����i~������|c	��o@"�f�^m��i~�
�ޥ��oa)��-e���L��΁�����	2d^m$�ag)8,���k^m)�i~�o^m%W����%)	��o@"L;F��j��)o����i~s���|c\)��*~���ö�]9�i�MvDB��i~_
��뀺#:�&�d� ��i~k�	����o}�)'��z�� �o :�o)�i~���æd�Q_(k}(��y��?�)��Zd���� "Q)�	2�'_�o}��m�۩��# )|�㑨ۘ���W�Q��_�w_���	�[��ݢƁ{��m8ZMv� �Xoj)oP��i�8�Ԃ�i�7UmB	2.��o�q�k�i~�|c� Um�ooXz�c��m>���o�c~I)��c�w��HUmI
�I
����@Ԗ�Z�c냱a3}�o}"o"MUm��|��)�o�c��8��opף�cB��-�oo�o}]2�cv�����ae��-V.)���i�*�cI�ݸ3��i奚yBUm��cMÅ-�D*#�*��o)�}��s��c�0&��o)�oUm���?�}�Cd���YX���W)ݲΪ�#��Cd�
)2��-�E)�BvL��Y.�ae������Cd��#�Cd	)߂ae���-��?s�aeq��i[��o�UmĚ�k-��	��ae9��'7m�kZUm�oo��)�o�c�^d^	Um���i����+��k���iBUUm��}�oo��A���m���cl����'L���Q�c��)=�}���)���'p
R(3Um�"�cUm�o)5�(u5h�c��Cd�om~v���)?�c�f́����)�R)!�Ё��x��(����ܾ:���Cd��	�ǔ������)c����}Њ�/Ĉ�-n�aelc1�o����)Ƀ�{…-�)��	f��k���wd$n�Ёɢo�3:��	m~��5l�%D�)���Dm~�o۽�m~}�)�۽��o����:�)gЁ�_���F"m~o�Ϊ
��	f�%DU-��m~�&\�?2�)g�$۽�U�^d��}��%Db�o�8D��)��}��"n��)x�8Da�}�oDD���'���y> �o8D_�o_U\d��}:��$�DD��)�}h-8D��)e�}w�PҐh8D#m~�DD���Y
	&C�)dw$�Xmdw�)�om~��o��)�aDD��5�8D�o8D�Xm�5˕g�
�o8D���}dw3%D��+w-D��i��Á��ܳ<����)�Xmc8D
�ݢ��5*��ϙXm��)e�ʣ��+�oee��)���t��	_���
��Gd��+֘Xmy�Y�98��o�.DD�Gd��a��+�)�!!|&�a/�	fXY�`B�Xm�^8D�)e�E9��o��Xm��oiEd�dw��Am�o8D����Cee5��m�o���ow8D"dw�8D(��+�oDD�5���a�DD��]>5Yd�o�8D)�a9�P�o8DGDD���c~�a�o8D���c�������8D�Xm2R�9�����z�Am,ף���c|n�o��Xm���3è�����/{�a�Jm���cĄXmȀÁ�!�	�)9�Xo�H�Xm����Xmմ)=������c=�
�i}j�P�����h��))�8؜�-��HO�"g�۽��)*���}��4��
}��%l�j�o���X�T�(׀�8Z����a�Gd��a��oq4��-g���o�
f�a��{��oX�������J�!!�o��)��c��cԸ�t��(|'
�Y�o
f5����i#$+I"Ȁ!��o*2��f�^
fpi#瀘+����pI"〻c9\
f�&�o�[-g̐�/Yq8,S��#�#u,m
f߁�����}�&��#u3�f���̈Ea��A��Y�#u>��a��c��*���K
f�3�C�V:�Z���#u���k��#u�Om��k��h�kpd~+f#@��t��o��fw�#u9{u�G<|��k��f׀ƞ/	o�#u�����Z^V�#u���mFL@+€�a��>y��k�C�p\m�l@+���%g\mک#u�?<|*�o��#u*{�<|��071���	�
	pd~j�hee<|
�k샆?���JfX\mx��j�heB<\mR�g1<�f<��(�he��4d~�-m��i$+I"� gO�#u�*\m�ze�E���\m�I"��p���<D��#upI"��i�֞-S����i�#uZ�#uߌ-�he��	d��{۵�-���k���m�#u.Em��o״�k]��ac\m�"�#u
�helJ<|ʈ5>"�#u_��-pEm�hep\m;��a�$\m[��"�z��o8'EmW\mo��+��oG=n����,�Ea>��{a�!uf��op\m����p]�:Emp\mZഖJEm���hA���!a�ރZ�=��p<|U� gЁ!u��?�he����К����ޣ;&g�:�ik��-w�hel�i��Ϊp�c����JdK�he������-�o��ϰI�he��<Dp�c������Jdd����he3�

����Sz�� ��p�c�7��S�o2zEm���葭*|���{�P���Q�c��*|�G]��݄����{��41}��oX���^�^9=�3#�^9c��o�3|D):d�'��o�8D�EmpEm�^9���8�,D���o��p):p�cE�cЁf*S�*|�ۛ�^9?�,D>�X���mӃ^9" ���ݙ�,Dp�^9��*|�X�'��f�!�X���w�'�!u���5� '�$���^9��o����.
o��^9��x:�%��ݘ���	�'݀%!h�cD��o���_�'(�YƩ^9��Cmo2&��^9�&�cv8D���9�Cm<��%p�M" �k�o�M"�	*Da��GF���@!p�c����c	�i���	�ܨ~e��M"3��f��"�M"m�,Ds�4�o
$g�Cm����#��ݍ�*|��^9U�Ƌ�O |f ��o�>�h$�k�,D�''`:�f��^9��'n�sI���'��oQ!$g��^9���k�
�i�f*�-f%�i��^9�(���S
�^9'�
ov��l�{f��aO$g�K�'�ԛG;�� g#!g�-n	���Y�a��h������<�Zd
�aX 3�o���95��M"�y�=�%�]�M"�'@"���^����ʈp�s��&y�"Ņ��&�c
�Cm'p�a��Cm Ђ�&(�k:��$�V�M"�'��ᗁ�c)�m#*p$g+p$g���c,�M"Y!D-�M".�k��Eh���i�fj�)a�M"��o5��{!Dv�t��
�+ު5f�F�9!D�!$gb�d')��)~eNd<�x8[E�9/p�aP�-:L6Nd���w�$f��@";�e.��<�i~˭[v>����o5��{Ё/�0p�i�.|wAm���R���a�i~��{��a�	��,�o=��w݇�-1�i~)�+��4Z���i��o�磇�&�o��92�cȃ[�+��ymwp��{
�?݀�c��a���m��<.|�
�I�ߩ
ۍ�"{��a��)�mw��+Ɓ��(�+�G{Ӵ!.|9�������ܨ��LR�{;�bw���R��o�^�Ţ���ւ��5]_D)�ݎ�[vރW1r��/�-�۽~��0
��i6'��
�ݙ�*u)�Y�hGm@"�Q�-z�o9���+��i�k*�i�Ġa��i��il�$|��`8~��Il#�iĔ�I3�+gD�kX�zݧ
���'��ce9��l�-np��{Ё/�P�Cd9��S4p�-���cH.|�����d5�ce2*��9X�Cd� *#��V��c6�c���	$
�k5R�n#)-?�
	
(	(�R!' !��d(%
+Rd!$!
	22h5�!+Rd	�
?	Z-	�JSU!	
-22#'!
-�!�5dR�2�k%+S$'2� *$!!(�2
5	�1;%	-1�k(5R25	
�!�%!	"nJ�2"'n�!5%
n
!�%;)
$�21
*'1$1�1nd'2'J(*1��)5�dRS;*	
22S#!
�
�2!UR�	
5%�'
�*$%�
�

(?� �
UU-!(5�
	"5�
	?$��!('%	
�
%;-?�
�k?2�2"	J S(2"% 		!!�?�(S(% �	�(dS*-5	
%
R�15 )%k$((d52�kR,52+	%
$+	
�
		U(5�kU!
*(�dU$J"�
�
2$d)%1hU%2�1�Udk?�!5d�		
+$	215'�5!5)h�
hJ5�2

dhU5%	k%52k*-+1`'hdS ;-
U(	%
!%*U$-$5!
	"hJ?J"�-

5( �)-Z?5% 51
-
;?�
(kU
	S
$#1�

 !2!+1hkJ�
	




d	
UU

-*

*!(�-h(d!!	

1(d
!	-
�	?#�
#;	�	�?�*'
h�!*d!n"*"*5d
	n;�)2� 5�
	1$*	

�'?�	"dUU5 +
-5+?�!+$#*�2n;
k�%?�'	#$n�?
Ud�  !?d
� 5 (5
!
!	%dU�#S	*)-1�!$S�;
#�*	?�'5Ud�	
 �
*5'� 
'#
-��5S kU?�'�
	 �

'�*
n�*
(�!?!� �#�'5�
*�
(d�U		"U(
#'S
S
k1S(U!SS5-)#

	SU
-!
Z2�
-


-)	�2$	
$J#	%`
-�)2! %!J2�RJ
-�5d5�
Rh#5! 
�)5
+
-	J�d	n+
?-!	2�!�
k d
-

1�

2
)5?
-
1$5�?	�-U�;?�
# ;�
	2
*
�k;�
�	�?k�+R)-�;`	
?;�;k"1�?d-1�
$;"�;
-J5U`JJh?U?*'(�5+"2dn#�;d�

)S!?�1
?h
�;;+R2!k;#k	k!d71d;		�U*!�;-US$	%�1�?S2U�
R2 *(;!	
*d$d(U�%#2 �; %�!��?	%( U !5d
`

�

?�
	R
-J�S
)R	
!%�U!'-3'�!; R!�U*-)
�
Jkh#�)$2n?J5(+�6;R$%?+�( #+))	$'hknRd(	#2!)	'%#d

�5U12
5 $�?�
	
)�
*	
	�2�-%?UR!
-�#?1#
*U##S!!'
d
#

?hk�2
2#'�)
�!�5*R'5�)Rd`	
(R�
Z

k1R(!
-?�
-�2;dS
		J#$

�	?1J!
-U?$Rk#�-$!
-�-?
�
�	�U5!�5!�
 	�?�;	�	
$%5�!(((�
! 	
!U(
		
S
 d��)2 
--
�
S?d-(J5
d$!	�S
-?
#Ud" 
-	S
	5	?-	S�`5�k!	%�!5�
n!*�
-?R;kU "!�?�5d(
)�5U
�'
#
k�;�?
U 	U '(!U(!U(�
-�!d�R�*!U(�U*R1;�U�Uk�'�k`	
)�Uk?!dkh%�)U�;15�?
-2$�;-?�!�1
!#1!!!,
!�U1k�?k�!�`	
�


;!	)
J

#?#�k
)5#*
!%(
k
%?�!�*2
%kS!*�	-1?!!5+�#	
-! # )$#+(`2)d?!S
!2�RU"k�k !�*!)k2R
-

'�
Ud
 �?
?;-)*R�R5'"
J!!!�!�)�
	�%#
;h(�S�Z�+k?d
�(")*	%�$$?%�!)	
-2-* �?		%5-2

!�J)2?#'�
+
-!;-)�U(�!�;22�5?R�(U2 
;	
)7	�1(5
-
	�
	
J�(5R)5U
	
55*	�U'U5U �*k!S
�
-U!

-dR2*�
--5�%;�

#!�d(5)2��$�* )	 %!$	
	�(d
dU'dU(�?U	Ud*R
J h*)�
R(
	U��knS%dU(R#U
##�(R�!-)
-J�1"d�* d?�*n�	
-k	n�;*2S
	
!?�5d�	�$U;�
	;)n*;#Z2�nU($!U5
dk	'#�k5�?!
!	
�
(h!�dU)	?J(!�
�!
%!'!2R��
2Z*!1�
		U;*?
'2Uk?	1	-#n)
-n#
-'�	'�#U%-
�'	�
S	h1U!1�d55
dkU#�!!S1dR�1U
#' -5U
k!�;51�?# 
�# *! ?Z!
	5)?5	
�#U)?d#n#�
;	2
;(�2?(nZ	
�		(!�!*�+n(�%5*
�?!d5h!-	
��!5)-'
!1�kUd
	#'#5J1%	1?!!
-�
`? 1hU
�k+ �)��
h5;k'USd5k2)�	
'(5JJ n+�!�SU
5�1;�%1		�	
d?
d
�
U2	%2
-J+�k1;!!
�S$!;k!�2�2

	
�*%
	`
	��;1R�
	
?�RU12?
(dU?+*+R5S1-kRR�*R5�!5�5
-	'�nkd5%U
-)	
�25dU�
2kR(-�5Ud�
%%J kS		?kSR%�-)S
	�dU5�d%�)S(!�S	
d(5)	�h(d(U	-1#�
	SR%)ZS!'%�
'!		
!�+ 2

k1 	*?	
?%
(�U%	
	-�!$)	�
'+� %S

2#-�#�	
�#%�*1
'!�!
�US
*SJ+U-
�	
Z?
R;�';!+�-)�1� )-�
-)S�*5
�1�U	;2�d	;�)(5hU-1�R*(�%U	d5�n�#'�5(d 1	k5�;
	-
-)
*�*	
�?
- '-5#k�dU(�
'-
-5	�RU;�-*Sn	R
�U	-	k#*(d?
-UU�Ud�'?�2+#U
�d! ?)-5'
U5d(!!(�
�	
?%U2
U5 hk	
�(!S	;5("
-+?n

,�)
-#�;*h$J5%-' 1n
,)R�!�#"$#U(�
%�Ukn)�
#�k)RU#5(!�
?)2
�*R�d(
? �S
)-�)
� n'k �	
-%U,�#
�#R�
-�;�!
!?d1+(!k	2	
k1$?k�5R�!	#
�1!
-?1�
	*	##
)*
-2#
,
#;�	#
%+�
��#�1
	
-
#

�#�-	)?;;?�S%#d5#

2(
1?%
R�2
)
d
"1!#'!#h 
!$#
h�	#%
#!hS
)#	
d) U))
1#Uh
2!'2�)$hU?Z `
-	�U
'1S	)2�'(S)
?'(
�h)**d�U�2d%hk
?�hS#1U' ? 
1 4�	
!U'+�'
2!�d+
?k	!

	�	�#

-U�1;k�R1�	
�d��	 !
-)h�?!
	-

�+U	)
� 
dU(�5�!	#
? R�5! 5�+!�%
J5RJ
	�-)!+R�#
g?	
#� %J�)U- d5UJ	Ud*n+!U2U
	U( 2!) ?+h
+*Uh#n�#)�
h4+'�"S%�
kS*ZnS*J	1�
d!
(!$ �*J�dk?n
	
 ?
)*!- �!n�ndh�d-;;+U
-!�
#-�d-2
 *'n(#R1#	�k�1;	�!�) 2-�	
�'�%	? 
�2?	
�'	
)2)
;	
�h
�kJ2))#(�	;(�


� +%	k)n*
;dU2 J#`dU'%J�S
�"(#
1h)	7�	�#
�
#�' h�-k*
$$�nR		)-)
#2+n�k*
R(;S%##
�?
n;k	1J�
-
�+kU�
�#
+

)JR�(�
-;*11)�+J�	
-7!)S+�

	)%
�UR%)J	*�) 1	�)2#�d
S)�'#-")�S$'+
)�*;'

�
	12
	1	
'?�	?1�1)!
;	*1*�
�?
1		
-
2!�	

R1;*�? 
U5�
	
�-)	5d
 ** � (�*
)

	"R5!'�	�	%#512�hk55�S5(�*("!d!h�h*- �k
'h�
	!�11	d(J?

-%
�!k ('5�?k;	
�!!5!J��
	�?
-"2?
U#2
+#�	-+,1)
�?
�

?	�!�1�''5���2-
�*�#2�U5*�2	
' -h)
��5?�1*��%U("k	
U�2
�#
!*"d5

k1*
�!$ d

�-kk1
Z?
?h(#
#
-S-%2�S
U(;-
U%;�Sd		!)�;(	�;%?U�+
-1(
R((*	;d%�(dU UJRRU(-R
�
1h-dkU?
	
-	�(�R(%U%J!�dh?1-!?�5
-JU*)
	*5
-#(U#$�!
!J(#�!(	
��R'(?R(1
-��# R�%R- % ;# 
	
	-�21�#%)
	
%U5	
�1)-
-�;5� ;5RU(5
�

- +�J;�?
(�'h��5�dh'5 �
"'�%�*! %	%U5'k+(#%�
 +� � %$�'�')	#!
!5!			5d#	
	
*`5U(
�hR(	

	�5 �'�	
)+''(�
�UJh+1kn-
��1
	

,1+�%R%Z%J)�5R
S%*1�%S`U(d�1!- ZJ%kRd�S%�k
5'!1!
-*(�)�
;5�*�%�?d-*%?$R�
k� !'�2)�k1�#k"	
?	-U(%d(Z
1%-*)1%%2�-�
(d�SJ	
J!�!�d(�!

#
%
U%�
�-Ud
!
�kd�'2U#
��%

�%"
-�
	`!�R�k?�-+�J�-)�*�2��

		!
	);�!�S(R;U%
#US dS `#'
2�

+2R	R��5
J#
��*n'�	
-�1k1
n;�
		#

	!?�
2)
	%(%%�
�)-+!�!+JR2�%(�%(-U%(S�%(
�?;%(U;U�5	�
�?�;?!1R;1!$	(%	d%U5'
%�d�k!�%(�R;k 	�#%?5�#
-U�;?S	!?�U5!U%U�#;!R#�kU5
)
�;?U�? 5!�
SU2?�SU�?
	�
5�;? �2-)U2
)	;� 1k��!`!'		d	J?�)


	; �2�%#*U% %�!!�-	�'�'�?%�%
	R5(!'%%+?
%
�

(5�$d#U?S
(	(Uk!d'
�5+?	(kU25*'�2 R�
!�	5d

$
Ud52	R!�?�-R!%U?
	-2�S(U%S
	R5(S	52
-�d!U�(?��
RJ5!U'd�#" �!(	�5(
RUS�!
�%�
(*2
(52�
'k�Rhh5?!'(R
���S)*'	U5!dUSU�!�;-S�S�!)SUR#!�!�
)-U
%	#'
	�
-#J#	
�;!	�?�!2�
!?n;
)-?
%*)'$
-!('
!�
n
�
U	)kZ !
�R�k!
	�!k� 1)#�?�*	��dS%S*�d!(;?�S'�#�)*+#k�S?
h;�2	
)	1?S�!
		*U#UR!k�d+
#' *%;�	;2%k(*S�%?5	U#�(!U2�2*
�R5	�
-�
-�d
$	J!!+Ud5	R*(!nU
	(U	J!U�


	5R5(!R*!* J(!�-2�5d(d�U5*h!(RR!	UJ�	?J(?*U	
Z*JS�5hS5(J!*Z
�%J

5'�!�%
*5�
	Uk	5(5*
#�	�()d�

;!-2;!
-�R��5�R JUd(*
	h-(!5R!�5d-�	

;�!(5U12h%
	�+5	�JS!�
J�

`5
	
�%;?
2! R
-
-2��J�!��*
JU��
-)�U5#�;RU5d		
 
?;5dU(U�"

�

--Z
��;2)!!	�
#�;U!5k�SU;J�(
+k;
%dk; n;U;?'
UdU
5	-;d-�?*	!
;�%?�;?	�??
Z	
d(
-	"d;
�
-�#!�;d	
�-
�!(kd( �	
`�-*1
?
�'!;-*�#�-
-
S+*2�!d;�
S)�
);RS�(
�(	�	
`S	(�k);�
	)�;	�h(`;*�
)�	*;	
�;	�R%#
�k1(�
)"UdZ-;"?	U*Z2;S5J5�	��52%!?�n�
`)
;
!
	#	
	�;2�	
?�hJ(^S
2 ;)*�!�Sk�J	`	$;Z!Sk
	�?	)Ud"�S*�	
;	
`
-�
�(5%�
�'#� %?#kRU-S	'%!��
-Sd
-�'

*
?-�
-S�- 	-S
-S
	
�S-R	
	
	;�;�?
-		)-
	-
S?
R
	�R5d;
k���
�-	2�5�;?-�5
d;J�%-)"-
	�%		S
�
 SS�5#-2)%U)?�*
�	�S?�d


	�!dR	
)!;d�	1 �d�1nn# d5Uk	-
`2 �% �
S
	? 2U
-�	
dRU�
	
	�

�J�'	'dU!k�?;�
	5*�Uk�dR	
-)�
	#-S�!-)	%
'�?�%2#	'#
!S'�'5�	
-	��5	
%�	*	U;	R%U!ZUUd(!;J�
-
!(;-	
�#-
-	R#	)2#";h�
	S	�
�'U'�hd(��;?R! !�2R�(%!$	%R#!5�R U�R�	-(�	
-
�k�U;R(n(�-?�*#(!k2"(�'S*�;)-�2R��%#	%*�S5S�%		?�

?�	%(U?


R	�?	*` 
Ud�	5	�
%�
�%!Z	�
		�!Jk(�;
�	Jh'!�-( �h
-S	dd' 	d-!	R5d;	!U?#�??��	Z%+-+Ud(�
#
+

"J
%k�k5!�!+
�
(�
	

'	
�R )'! -	*`h*�	1�2�R
� (��SJ	%
%S�	
�	
'�	
�%#-?2-'
5-		) �%	J	
?hd*-? )2)
 2)�
? ) -%-�?�	h-+-	
'	k?%�
	�%	"1	)�-+	!1
-�
�1)?	-
	?#	!	%5�

k)J�
+JS	
�*d��1�R;+
R1� ;�;`�	R%
�d
US�?J	
k��)-#�	
�J+	�	
U
-		"kU
�!�	?)
-(';?*S?�

"kR�5;!2 �(!;! J�
 S*h�)
1S
�
-	�h(	%(h
 )#Sh
#�? Rh;(1n S121dU	-
75��R1�)(R5Z
"S	�#*2�#�
	�1?d�S?hSJ(
	�'%%�-J R?5
'%J
 1	-n	?;�!S��('�!;* n?;;�%�
;?
	"kR(��J1
	
�JR?J
	?
S�#%�
	
�*;
2
)1��;�;k
!+!#%h+(

U(
;'h
S
!?�'"?

( (R�*$( U"#

)�
Uk�
-�?!U	JR�R�#
#�-)
S	
U*!%	2
*�!!�' 

-	2%#!%+()�?�d5#
(

*
�5*5"	U 
�!�;d(J()�2k(5#�(dU2)	
�?Ud�			
��R	S
?)�k�
)*!h	*	2�k�#
5U(*!�5�	

(! �R#

%%U�k%(�('
*�JS5'*5%
h(dk�	1`';�
�
h

)��� n5	(�Jh;	)	*5'*�*!�(Ud	J	;;d
;�	�knZd;?�'+U# Jh
%	)�(U;?d�JS!?;d
)	+! dJ+;d(�	Rh5
�;d	)(5d;	#5-)-�	
-Z
	 ;dhk#)`;d(1�
	
	;d
d;�k�h!'
?�
�

k
hJ?	
�!�#�##
5�	-*h('�'U''!-5�5k	! '

!�- J'h�#;
#�
�-		 	*!��kSk;k�(�!'�5�(; � U-# 1�5#U 15#2U dS5 #h�S�k2�
 +
 

RU5-#5�?
 !Uk	k	�k+�**�k�R)U*U? 5(d155	 �1	

-)5'd
?		
�
)!%�kJ�?*	
	


�
'	
-��%R5;	;5
		
U?��2�'U*S�!'U(�n'
		

		
UdU5;	(��
#�"5h)U
-%hS5�2�'	
� )	;U"
;d

-�;
-5UdSU
-
2�d


	d

"d
�!hd
�
!
S-
2�?
%	U
��%;*�

d�;R*�
)-�

-
�J ?2(*%!1;S!�*

�'%�
?�!	�	
!#�
 ?#		
;#�-
�1(;US�`�S�

-�R�!�)��	
�
()	)?%d%�5�k!

d?  �	
�?	��;%)-'S+?"!�2)�kR!!d(
-5?k
S;	
?;�-
+
	?�1-
JR�?;Ud
�%-)�
J
SS?�	)�
 -)!dZ	�R+Sd
-�;�;�S
R!)%�d?	2�
�kJ(k-5U�dk�h�SZ?#US?�?#d	,?SU
k;�J+5 h($k�		�?*��J�R5(U%R�%�;�J2nJ
R�k�d5;)2�J!Z
 (�* �
#+-
�d!Z	2�;!�R*d*2�!
-h�!
-	��
�

-S�#J	##U 	*;U
?,
-U*)#*-
d;(Z)*1�
-#
	
�%?n�#
'
��	
-*5U5kR
	
#	5((5U�U'
�kU5�U5�d�5JUR#�kU!*)	k(;#�2`Ud(
-)�dk�dhU(Ud!5)�5�dU?�%��)%%R
Ud?U;U%
!5"�)kUk�
�1Z%	5-
 15dh�+h�5 #5+�
-	�;(5�-		5+5(?�+'!?
*`2�%#�
�S?1	
)-
S#-5(�)�#?
�)h5Sk�%
	!	??2)�
#5;�'+	
�
 S	;(U	?
%-
	S	
4$�;U 
	U;		�)
-	
!;U;U 1;d	*�;1
!S�

U
2��2Ud;

R
U?# ��	#
 
� �
4d*?
*)
2%
�% )!S( 
�*)1�?�#!�U(U! %d�	)�J;S	* %�%		;�)**�'�k!J7n;#�	�*JdR�* 	%�� *�"n#;#%1n'�'5!#n?#2k2!	
'�#;#�JUn5�)2�
'�)S
!	;!#;,	#h�5??(�% J�k�;U#d5 +�
	2!5(�?�
#�5
?1
!#!;�
-�J
�*? *)�d5U5�
;	�?
	
?� d(5
Sd2 �2)'?�1S
2?�? �d(
��dU �d*;
-
�n	
*!�!R
	)�!�%5�#?	 )� 	S`!d5

-)
'� +�?'�
�	5(	?	 ?U	 )�h"
)k	
	


1U5-�UU(
	
		
	J
	
-	
��

�1dk
		
�	,'�n�-
?SdU	
�R1
�RS�#)'��R�*?US'
*?�
*S#;	
�2 *?�?)S!+"!?Uh%�n!�*
	,'�-U��d	�d*�d�15�	*��
�d�?n
!�2 n?``n"d);d *�n?#
d5%Z
;	#J!�?n
n1�;5#�2�h1#`k!R'd	�?n
?%d!�#�!	�	 Z)�n�J%*�?!+	
1

5
!	#)�#
%#('�#+#d-�	
 !!�S	�#2?�(	�kS�(1�
�J�
Jn'	
Z!�
��?5#	!� 
%5�%S?# -dk5��*
dk

k!d!R%!-2*	
2`12	%S� 	
2�R`+!2kS;�125J	S��%?%Z%�-)dJR2
*`!k	
`

2
�#%d(%R	)2��S#�#%	%	�?%;

U%	�
-,h%5U
-;#�%
#
2Z#
�
�5 ��51
'!k
+%!*k+	
�
�
S�U 6k �
2( �+-
;1
)
-+�1;�;*�!�;	Z;"

5�	U-
`!!

�*�?'1	+� 
*Ud%	%	

1

1*�%U
*;1dU(�#;*

-�!(?�?!�%7 R

�	%!��*k
-R���%�)- Z
-)!*S`)�*	?)�
!�
-*U?? 
J
?	*2
'1%	R�?%S!�;`*)$�'�%U-	
#?�%!%	R�k�k'�SJh*S�;�!�%*)%S�
*;)�hR �'�*)�*RS;	*)#*)�
�!)%�!)�#

�		!S
U!*
 
!��(?�
R!1%)	
"%"

#�
`�
S*25S�5!!�R; 	k�- 
%	h
5 )�#!?dknRUJ?k
�;#�'(#?%	#Ud�;
�+'�!5!

-	
'�	�2 *� d%  R
U��#	?h	R ?+

	RJ�!d)5��5�h!Un
-	#�U!(+;#�('+(R�
-)
+`
(+%
1�1
#;dU1URh �;*4?k1�R1#�* �"1-;kk	%�#?)#	*()(!dk)+

)�?	
		S�
d+
*+;1�;?dU�	;*�5U%�',��'1k-�?�1;*
#
	�	�5�
51��k!2!�d�?%�%)2!U� !2U!5)Ud�	kU1 +�k!5)�5 �
	�%;�'-�R	-�-)	*	#��'�� R�k5�+
k`	�SR(
-�
-;d
'?
	?�? 
 '�
�)�?�(
	S5'% U?
k;-�
�-
k	k1
-1S
Uk
�?J�J*��'kZ
��d;R+k�R?U? kU*
k;���k��
	 `*)kJ
-1*k;�J;!
-�#��

-)�)22U##*��
�kU�'!�	
#�JdS
*
�2(5*�%


2Sk'%�nk�1kR
n;

knR� 
'	U�n(;�
-?���n

)	)	n 

�5
-#
 nk�
%
)
`
'
�S�
�%�;n�?Rd5k�
!RJSnU?Rd�
	-`?
#

-
� 	�UZ
�
-1

5RdZ
d
`#
�?h�
)

U5�
)	(d�*k	
 �
)5hd�d%5!
-�$	#�(%?�	S	�d%	R
-�
-
-)
U
�5d!�d5	)R1d5'�hd%#'d5d*+"
�
�
�	1U5�-
d	
�*;	 -
�
	Jhk;	�

�d5S(%�#U(%+�%�	�Sh�%(%�%
#��?�?%��(%
-"J;?�;?d �hk*?�d�)%J�; �	k+?�d;!	�!�!
%(
5	U�hS2-	�S5`%(	4%d	*#;
-?
S#)�
�	�d?	
-%?+Z;�%�d�)-
-d�*%Z
)
�#

�hJ	�?2)
;)(%-!
d$%!U)JRU;!U*)�
 -J"
	'%�#n�%#2%!k*)#�nJ�#R�(;�%'h(*2�2�n#?	?%'�
-Unh;h(Sn'�'%		k;h5	
JR�n1�	�
!?;S!*;�	
�5�!!(;? �%'�n;*%?*�'�(n*!;�k5!
�h?
	k�	;?#�h�J�5Rd�
)
k*�R	kR1
�(?
	-	?		k�'
	JRS�k?	�k'hJhJ�(*JR!(Sh	Ud-)
�n(R-	#hJ	�
%)	�
�%;n;
'k+-
)S;�

d;;JR�?#;*S	;
n
�2�52�
-n-*�1

�1
	�J;1!�;5;%Z

*-�1)�hk�*?

-�1*
��*	�n**;	

!S;�*#�
	;S�S%J�S*
#!

�#
� 1%#� !2 S+�
	%J�U5d"S*%(;S�k'R*$�!	�+d#		d� 

�
		�1	�?);d'!�-+'�!�1R2('-1��J
S%k�2)�!
d;�	
!;*??2R!�
		
S*
)
S)

*�**�1)�!�%�;-!
-�R(�*
k7$*R5�"U1?!(RU5#	%�dU�k�(1kZkdR;; 	)(�RUZ%(R!;	%R;
-
#�d
5	R�

'%	
+R!�Ud	 S) �R(hJR(�;J
5R)!�?�!	'
#S 5�(R;�;*�	 !�)!?S�2�5'�;?dk�+J?�;2�?	
!U)
S	S!Z
-!S-�dS	#�* �
d	R		SkS�R?#
U(nS�U52�	
?*��%k
U5!Sk?J�	1(
!
UU#((U5#
(dR-	J-

-"?U*
-"(d?�n
-�
S(S-�#
�#
U#k�!U#U#
d"U��#
#!�(dk�UU(�
-!k+�1
# �'!�Uk
	 !Ud	5
d�
�'% (R%U5(
�1

#S�kR	#2 -
�#�
�#
�!(�U5!�
-�;	U1;� R))
-	5!� d�

-)Z�(�5!J'+!%�kR�J+�kk�;R�
-JUR(d�(
!R
'+n#2�
5!+J	
-U#�Ud
�-�(�d;( h�*Zd!;�(U 1? dSkJ�U5!(Ud5`-2�
)	
�*�h5
('�	
�;
�h�( n*`5(U1
1hdU�k#SU;?�dU#U5U%ZU*+;";-)
"!�?�;"$�#�J% 
?)�(k2Z)
-;	�;-	
	(�)��kU�`;U
R!
S
-�(;(�% �?�'5�J-
U
5;k;�
�d!U�!*R�-2(2(%�k%*!%�?);
#!h;?
	 ;!�?+�%

5%	�1 	R%#!!1+
;kJ%��

�
;*�J
';�J
-Jd�5!� !+�*ZJ-
�J�*%"J�+�%%%?JJ


+�-
�*�;
5#R1
"�U
 �
-S *

-%
	
�-U�% )!-(U--
J!	+d%k�	%1�+	%R5U�-�

	,�)-�?(S

	U	*(�?�!,	�)?2%Z	)*?Z	
-?	UU�SdS-�*?	*- 		?SJ�!	-dU"	*!;�*1!	*2��?*dU*#�?�#�(d�J-U�?*�U?
#	
	!R	#
d;R
!�
'	�# -
%U+J�h-�##�J+#�%�!J
*J�5�?!�1#�
	*'(h
k+
"h�J!(-U5SRk!'(!J)dJ�	-2
#J
�(JUU
*;�5 
!(!	5 �)*;
1!2
(;R* R!*!S" (�!'�k2h�#
�
�!	!"*
�
d!(�)#
k#	+Jd5�J
ZJ'
5;5J	2%	)	

�RhR;
�1�J+%	
)�J+)
�U52Jh-(h
�d!�S		UdkR;1*%h`
1�?!�J#h�
-�(kUS�hkZ!	+
7J�)k*n'�k
h)d 
�d(U*%'
h�	
-U(�k�#JU�2�(U(!!��
-
Z-	

?�
�!)J
Un
U!R	!
-

�51�U*S
	R�n?;	n? 
R; ?�d�,�
-�R(�R(`(
#1�R�1;�d2U*)
�
U5U
�k�R�(�	-)n%�R�%"%�R

R?U	#  
k1�5(�
�
	�	�'(�(';Ud
1	-�?*	
**'�RJ�	R(	
#�S
�	�Uh?�R;(2-�S�
(`
-�(5�%J(;�k?UR	';	J)�'R�JR;'(SR�%!	?S�'65��+1;
-'#6?�1�
-!-S
-	#!-
�?SU�'
�n1
;
-�
+1? !*3$�*;!2
-nRJ
�!2!��	�k	2
�h'�
)2)	?	?2R?'�?�!
5�$-�k� �	-kU�R)-!�d'5S'�)
�#�R5#!�	#'
(�##;�
	�*+�
�;k%�(
	!(;�	(��
	d!(J
-;R
-
�
;hJ	
%;!!*
S!�(
�!?*!(h;J


;(+";%S �	h;(+"J�;R�'('+*+*�1 �(U5�1Sdk�d5*R(+�%�(�*�R2d	+�RU! 6'U�?"Uh ��hkRk�1?�hJ�d
'U�h(!�2R �'*(# hUJ!%1k;(1?U;( ?�R���?5(U%)!-;	�hJ!U?;(
;?J'J(*;hU(
�+?�;?!d

5Ud(!*d*	�%
#�R*;S+Ud5-
S�*R�
!J	?��h�#�;?`*Rd�;�d!�!(1 )

Rd;d�?R�U	#�5(`
-�S�(%(�*d�!*�#�*Rd
�U2
-k�;)Shk'	dU�?%
#+	'?UR(R)k( 
U�-
�(!-)+


`
`5 *	'R

)-5d	'�
�
k	#-!d�k;	dU�?#�#�;k�%2�
-2*	5;U(�*?1";d'�R(
S;kSd(�5(d!'%;k�d;*�)dU#J S	)U)
#-k1S�;k	
5)2,%;#;�kS*�?S�!(2R(	Ud�;�;�R(!(2d R	(�)�
%�JR�'�	R�*�J#
Z?	%�(d5!'�?n�%)#
R�5 U�?Ud!**RdZZ


+k5;	d(5R1%Z
# kU�';Ud5Sk%�
R�d(
;*��'Jd;?�;JS;kU%U!*;�1+S*R�)	�**d�
�
�JhZU�k�*kUdkd
d;�;*d�
�
;#;?)
kJ�)(%�)
d
?	;�
	�
-
)%�S%
S�
)	


)k	U;k)�)
(5(+	5*dJ!�-'+!**�
"-) 1�kd�k*dU	;(�?
d�
�
 �	)-�(U#*1;+"%	k%?!Rk�k�2U?�**?d5(�?)-k%�2
J?"S;h�#
1
�	1RR�2*?)R
2R'%R(*
�;�
	)U�'J*R�Jk�R1 ??
*1*k?)1J�2
R	�R�d5U#�#1�n�JdkU%'�U`#%!)#!U?
kd
*U4n�R	
#-	�
!#n?�U
*	�h5U5%�R;h�n'?%�U%dRJ�n 1dS1U�)-?US?!n�Sk�R?�U5*;!(�?;�*!?d(RUR�R�#S(*
n+�-J��#�U�(RJ
�RU-1 	J#�
�knJ)#� d5U1)2dU-(1
-�	
?Ud
(R`5()d
dJh?!�R5)'kU
'#?�kh5U1
	-�?
�%�!(?�hk`�(Rd�?-	
#�;5(�)-�
'�*2�R1�d� k*1UJUdk�5(5?�J(U*�*?�?	R5	!?�S*2d�
S�*�dh5�(Ud�
#US;�dd�*�*S;5Ud-�U	!J�U!-(	
�n*#Ud�
	kU�
�*S�%(5�*?�%	 Ud5*	�	
�hk�U�1 dU�

h	U
	�
UU-
1)2�U`1Sd	(#kU�-	)�!R"
U!) �Shkk�
�!;-�*U+�?�*
+#+S�)?;	11	
'd1�)1;S�-#RS�S1	'�k;S*��SU'��
�
#'	
dU%
	d)U��?�
-
�
�
�##
d( 	�	2;Ud�S�
)	%� 5
R
�J%�RUhR
(�
-*
�
�'k
-	?!%�?	Z2'?-)
!2;U�dU!5?5�-!(UdR*?�%�?5�?�)'%U5'�
)�*;2	J!�U %	�
;% *	!�
##?5?�h(%n-S�Sd5R!*
-1J!5U5k	d�J!5U()(	h'-	�;	-��)
-+2		%2
k�?J;-	S!
		25(*
%ZdU�*Rh�R(!	(�222 S�#%-)�5!*dR
-2R;�k%�%k�*'Ud?;
-
hJ-
	U5!��d*��dU
-!�*
-�		
Ud!*;d�!�Ud;1Uhd?
?�

	�(#J+!h''+JU;�)'%1#(5##1!+'
	%�
'+%U!U2(d;U�U�%)S?5d;	(
'�
�5- kJ+�	
�%R
U(dSR;d�	S�?+J�kU5�*1?�U(	�	k%�#)'!	n �Ud�U?5
	�J-(k�
?S��SSk�S
5R+SJ��%*"SS!)dJ
	�*� J�2�*�?�?�	
S
#S	?J%Z5Uk�* k?
?k*;�Ud�-!�1� k5�		d5�Z2*	RS�S!U(2
-�
*(5�%d(` *Ud*d5R��d5U)*J�U%S`�#(Un?*(d�	*
+%�dU�(%
SUR�`�5Rd�5US%S	
J5�k*R��?;'?U5d?)S
�?
�SJ#�;
�*#�Uh�kZ2
�?	n�
)��#U((U!7	kn "#�(US?
+S-5�'R!5'(�U)	�
5k?!
�S	R'�
U-)
1k�-)
U�!S1��?		�-
)'(-#
�
-���;U	!
�	
	)�)-
S-* �	U5?#
#	#+�!k
���
-J�
??"S1?k)*��
	#;dZ!!S+ �#
�-�

R%	?


�U!SJ�S	��-�)
SU*	
*�S 2-�%

-

d	5U

52)-5h��?# 5Jd? ��?;)-�
!?!?�!-
5
�
-#-
U!!55Uh�%�
�!
*%J
�#5d "dh- 5U+�d5)U
-?
-)5hd
��(	�	

U(dU%k�'Ud!R 

-d%Z5(*!Ud
?22��?�2?*d�JR


�'R1%h%�kR?!(	d!�
�!?+5��J�hUdU
!�d�R%`�kk%#R(R'�d�?�%R2
4	


*%��*(�!kn!�!�k�1!%�)
- �d	
�1d�

'�%)�*�#)?*
*#?�R-	
!d�5?U;	
��?	�5?*;%�
�?
(
�()k	+
5RU;
5	dh�?(kU
*� Uk2('?
�J�2%�-(!k5Ud�R(5(
(d *	�U1d?�		R*+'�
�*
* 
?%�% �;!U-
�
		"5dd	)
-*+�	�
*�	
-d-RU(;* #%#kk#(Ud�R'2%�	'#+#R;
'
#(5'�'�?k*5	5�5(*'U�RJ�d�-)U2n)
-
5
d�#
	�
	2�?�%2#�
�% �h� !�2	�(U2?%�**1!
!hJ5
S#??
*�#	�
1 	2	�(�h	*! �5'�S#5d
J(�U1�'%!
R	�'�!#?SS�d5hd(Z?�5S;	Rd	R!dU )�!*(�dU(�#k(k';�(R�S�(Udd	�(*�(1'5R*5(5	;U#R
U%�RR	�5!�;	R(dn�R	Uk!��%
-
�J"('�(U!U
#!�(%� 	
(*S�S�
%�
-)�
2�!�Rd;�U ?�
`
-J�;dd!�	Z%$)�!#5
;"�1S�n	�
	h;	n1SR�5�?#n
!
S#d;�5(;d�kh�;JR

?
�-	�U d�d�;�-�)2,Un
n
�%) %
%J�!%'*S%k�
d� *�?;Z
*�(h5n(2�*2S k5� Ud(*%d;�Ud�?
%1*��!S!2%(2dSk
U%d�
�
?')

-2#�#
*5�
	UUh
%	*U(5%�#�+R1�R(J!%URR*U
#�(S	
#� 
�#
%�
!�?�-	?�*2�	?-#J "#��?h?#(R??		��
-
-		?�?�	
S%U!5k	�-
� ;?*5�d*	#%�!U!#Z1
#'�?;	#)#'?
!	
U
 RdU�
-��
	��-�(-2 2U
((U�RdU?kn
R�kS(U*�
-?+�kUd-�	-�;h

!U1Z;U�-?2;U�U(-	;d
`
- RU
Rk!%�'h
'n�'U;h
�;�		(�U 
?�(1'; U;S�**
-�`	�;d%
d;U�
-U!!;�
��(�	"R"��;?5(�2
-�5+
(Z
#?�d
;5;knJ�1
%-'+�d!1S?!
�;`;R5
5SU!�!
'#�
�
-#
?U-? #k;'?`?)
-
%%�'�%;4!;?+5U;	
-2;��kU-
	?�!�5k�*U; 1�k;(d�

#�?	�
"J(k5;R;2 �#�!(;�k;U?�k	?�;?!!'5��-kRRJd5�+!2;%J!�#
n'*'S2 kJ!Zk"5U
U #�5'k;R
-� �


!5%+R S2
�J	�?
�5;hZ25
d�k	
5R�J#
-S!5 ;�;(d�
!(;5dS
#
�+�2;5�
�R!;�dJ+�
d;;*�(kU%;	
#5!�hR*5;�d�;� dd�?�(d;�?;d-%	��)UJU
)
!�!*?))J �
�%!
J)k�
 (!�	?�'('R�	�
%1� (;'#�	
	2�)!
)�!J))2
�!;�5 �k�	�	5U!;(5)R2"'	
!�'
(d!	!;�2)�(5d5(d
%?UdR5SJ�%
�
�%�(d(�
'5;-)�!;!'*'hk�? )�*)'5�ShR	
% �*%	
n�;*Z' Z
	�5U(�n�!�)2S?
!`*) U�) S)*RJ�k	�; %
�%?�*

�J�'
2;�;J%�)�;J�5�5
�
-2R 
1�S%Sk1�
4'k?
)-%�'%%
%-
dS
�5d�
�)
-�;%#?S	d2��'	)*%�2U
SdR)S
`(% 	
�S *R%'� )�%'!U?"#�!�JS�%#�# (
*; !5(?�?�	
(�5U12dd'k�
1�*(!*	1*!�k��k�* S'5Z
+#*�1(	� 
2-
�k(;?5 �Sk;'k#
)-?d#S#�%�h%��?�1*�
?;�?!U
"(5JS�
(R5(U�R!�?
%!�1;h-5
�-�
	5�% �%S��%� U

2`?	
�dU
S�Ud`	?�R1(�S�
	�k%�SU (h
	!(S;'
((�
-)�'	�'(dd	-JS#�*	�#U
1�
UJd�!
J(
�5(� �2d�
-R5 '��R5?(�%'�'#S!`%# ?RU�-%`%�%�%(5SJ-	(UR�#!'�!(1�k(�
�
Z(% k(Z?*-	�dh�-%
!J5U5�
d(Z!!5dU55U*d

'	! ?�;�#?�#�`k�d%"'�'!	d�#(Sd�1�%;Ud�k!'�dU	�!S('"'SR%*Z'kJURdJ�!'S?k?	%?�5?S-�+#JRU%S�';'�1
h?�Ud)S�n
-ZdU
	UR(d�1?'?!�	!�
25S �Ud(� 
-)-�S
U)2�1	
-	-!#�	
	* *U)� 	-�kn !�)�;d1�;d-?�';'d;�)�!�
�)
* � n�
n 
)�%**!?kd( ;
-�%�?d�5�	�k'n �5U�#?!)5`!�S
;R�?�;
1;R?�d(d%�#�� -	#�	-'d ()##*R*�
##� SU#�
#

�dd�1+%#S�d(h�S###	hJkd"(`;(;�#? (R
J+#U( �;�
�	*(;�( RU1h5�;J5(;�)	
"%'*5;�UU R�;Rh�d5JdUU;�%;�1�k	(Uh5kJ1�d1�;U R�)�5d#* R�1(dUn	-?	
hd%RJ!Z� ?%d�%�!�
-!	�
;�)*#�n; 	-2?�U(d�2SU!(5
!%#*k*d	`%#�d�U!Jn?
!
%�J*�*�(R%J�+�
?h �?kSU �
?�n!U5R �
-k5
)2d�+#*R(dR�*�*!5�k5'%
dUJ	d!��%d-
�*;5(�5dU
;SdJZ-
�*!��5d�!J%�(?�-!2;
-*J�%-)
�5U2?!;�k�(?!
�
1�Sk"
(;5%�	;+;;d!Jk;hk�,	
�?5�;��?2U	
	dU�-�?n�	-Z;??2�)*#?1�?�#kUh?
-hh*�)-�%k�1R'��
Z#�
�*


-�
	%#;?;+U�
-(R	
�U2!R�

+%�?�k�%?#%k�*%	+�'?#'
�!S* 1	;ZUd(kd%�R%#?*RS#?ZU!Sd�d2?�)'"%!
�h�U	�kd!�	!dUR
;ZdU5%(;�	U
�d!#�RU##kd�%
�#�#�U%-?(!%�#�nd5 
U#%'Ud%!�%
hd#k*
�
	�d#-k�k�U;(��'�
	
USUd�Sk�)1-�k55hU*RU�dS?�'�Sk5Sd'�;J(
 *	!k�J	k;??RU;�
R1SU�d5S-!�S!
h+�*	d5U�RSd"S �h!k�
-

?
-d!h2�
5+�(Rh�(%�+ kS�!)$-�R5 1?R*
�RJk?R
� (5!�)2�5� 1 
U !#!

	�?�+%5(�?k*�)� 
� k*U
2J	)S�J��!R ;Z!�R d
h 
J)�* �%UU!�!	1R!U%Ud"�J! %d�k1��?*
k�15	* ;�*
-�%		
	*	)2�1 h?)J;*
�1 R1RJ*�*JS?#��
�
'���'J !�nk; %J�Uk;�*Z;1R
!;R!+h)2*�R1-
k!#�R;�!R!S�
;R5dJ;R1J�+(�R;11!R-(+R;J!+�#�S�1R5?+k �;1�R	U	Z	1SU;�1;Sh*
U(h+UR+�+(
�;?Z1)�!�	(d�(

 #�R*'7

�U	R(�k�R�#
-%�J
�;
5ZU!�5�
)�k!k5!)�1R�(+�
 )�U#;!(
kh*Ud#nU5�%!R
n�#
 R�#
�U%)U";!dUk" 2);")d	�2
 )�	�
	�J5
n�2%�R�
-#%)#!�
*%!#*;)U5%#5;�h(2�d	5(��(;�!�d(�? !d(U?J5!hUd
-
J;�RJSR
;*1kS?
�RJ-�?	1R�	)�	5U2Rn;� �'�'�;(
-�k�RhJ��hk�dJ!UdUJ*R
#?�'dh!d�
k�+U�Sn�d1U
1�
Zd?
* ;(5	kJ�*5(
kZ2-
#?��kd'�Rk(U-55'?*U(%dkS5J)?d�J;?	�k'k
k�(�	�(U�5�
#d"d!S�;J!dhdU151JSS%�2�
- (d�#
2�'	
�kU%J�'
J5'
k�#U
�k??�
-d
! *
dRk�' �nk�
1S!	S%1	'�J%??�-)#?)(�?�(5U;)*)2?U)�#
*;�;U!
�URhkJ��	)�%")U;)UJ-hn)USS �*U	�'?J
�dRd*;4
SU-?	d�R;U#k?*2-R�5	�
1?1��'�R�*
*?;	*?*-�1?*2�khJ#"	n*?�
�%
#R) �	
#
)UJ(�)-'�!(� '�k(d(�;�S�)		)*;R%J�Z*5"%�kJR2(R�2dU2S� R*#U5��dn�*?� n'�)�-;�;J!�d��	
�)*�#n
#?�?�
 %(dUk)d'��*+2 �;�#2�!�R(	(
�! �?-##)2�? + �Skd)S�1R%� #�%�1UJh!�h()-�(
k
dn;%J�
RU5�RSh%�(!;1�5d 
��
U
-?�1�J!�2%#��2
-	�?U1;�1 �k!S*�k?!�5(
-�#Z -`h-%;2d)-)S5U	� 2�R(!��(!RR(d!R�!
k(*!!�	�!�R	?�?	�)	!�	SJ5�!5%*h�!
�	%��%	
� ;U
�dU�*k1
k(��5 !
R
5U�1#d?�;?d	
	%5
�( dJ
k�   *�h�S�!*1(!�
-k�!d�R(')*
!k�
(d("	kd2�U dU�5d)U ��2(?d*#
5(R
35*RU(5%5)R�5 ?�;Rd �*
2dR(�U%) U* h;�d5#"�2�(U%(SR(;S	S(�
Z'2*)�Ud �	�US5U( �(R?�5UZ;Jh
	h�J;d
*	dSU�
�Udh�hS5dU�(#�
?�%�?k?!5(U�	5USU*n?�d(UdR�5(	5((S	5�
�?*�?�?*1k?!;R(Uk# #k*�Ud�*�5;J ��URdhRJ	J?k!�R	k!!�+�2?k
U(;�d5%;#� *5+!k�
h!k�kU*R( �5?d+5!U2�h5U*R	Z'dS�5US5	?dUh;
Z(;	!'�d;(		*dU-)U	d
d�S#	�
dUR#;+5UZ++�1%�
*#
!RR%?!%U!;*�U?d2R
�;
?+?2	;�
5?�
	!J(  !(S	+  )#J!
(+k
J5!U k?#'�J*� -Z#
5UR�*Z?(U
�
d#�(dk!5+�2+
 5(	?	
 �5k?	d(R� �'(;%U;(d#�* �	
2d(5h�
*�!U�-�	R�k!�hUd*#U(5d�2#!S5�2 UdS�5(	�
hU�%Sk��S?!-*�%
J2�UJ
2;�5U
)�%
Z1!-�SU';�

#�!
 #!d�*�d% )-;�h* J(Rk
-�;*#
*�d*��%"5*(d)
nR?kn�%?	Ud*n�RZ*'1"n�n�1 5�-)
'5
d'	

nU

�;1�%?�)J�k?�'1�-
5
dk�!#-
	!�%*+�
�
-- *�%SJ)-�dZ(US-"(
�(dR�dk�!�U(
U�U2(5�R U(2 �%*�R %;`(R%;5(k5%�?!J�2 ��RJ#�5	�U(*�%!(!5�d
%"hR�k?U;�U�RUU(;U"R�Rd��!RUJ;15(R!U!U5k �;R(U�dU!Zh!!R%Z(;h(�n5!R;R(`Ud5UdS5�(dU
R*(!�R!Z(�U(�US�%
�;�U;(d	U5ZR		�;*R*'�h(R	 )d?%�'?�h�#��;�(
1�'%?)?�';*�RR�U(d*
	
%'�)�!�R!n�* ;+R2?%)�
#S*U 	'+
)#!55d)
	?;�(;?�k;
n!�S

	-  �?�
�%
'�1kU;S?
Uk�'�%;!;!2�#
- �d 'SZk!S	'��
SUU5d�%2;!�;�?#�R!1	(�R	�S(�#�;?�Ud�%%	
)#R1d

n
Ud%SUd(
5?	
-khnU*(	U#
�?SSh�	k5(d		!212
!;2 5�;k�;!;
5U;U;�!!nU(5hnJ`;U(�(#Z�2

-	
�kn�JR
d(
�()

%!;*�#!�
-;!5�+�	 ?�2 	h;dJ� +-;d*
�(
-5�;dU
	d;1U
- '
�;dZ(5�2d;�k1�
-U- *	kS�
hS
(; ++#;dJ	*d;+U*Z
#;d�)
#k		) �d�k�?U5R	U�-1-�S-� !��U;�+(U%#(;�-)	*(
J	(Sd�U;-)	)�U5%Ud;*5U�UdU!dS�U
%nU(5�(%#(%)*�hJd!5#(%�)h(J%)*S(�% ((n
#%%())R�%( %!%+-	J5)*�JR�
	n�)�)�1�kk �	
	-
-?!	)#	
k(USJ*(��
�RU% ��?R `k! � 5h (U�
��?	�	(5  %�	��% � R(� ?U��% -	5R#�#%	k!JR-	
�-k(�R;;!�k?��(+ ;;!d
	 %ZkR�'?k�h5`	(R"Rd
-#??Rd	+R�
 )	�
*-!S�%
1% (5 #R
-!�
 *
�%d*d�
�;kU� h�	?�#d�??5S�d�k
%?
�R2) -�1�h-((dR
-
*Rk�d!%'�U!�%%	�d5U
Rk�dUU!*	Udk
-'kd�%%dJ%R;)" RUd*2U �UdR!
�J5�%R5�%d?�("	�*;	
�J (�R;	1�R�
!`
-%2�#
 �J%	-"n
%�nk1k%JkJ;k?�k!R�-�UR�2 k!�!��'(!UdJ!+!+�'�	�-#!� 
-�2�%1� 1�UR%R��!�n!� ;n1;+�
S!*�J#
R)�'(�k5R�#!R�
;	�R(	d(R(;�2)d*hRh�(R*;1�(;S
-�kR?;	5?�	R(�!5;%('(!+�	J5d
;R	!�-R((R�1�#�5U!�-?U	-�;d5S�5�dS1-;5U 12d�d1	2(R**Sk*;
*	
�(�?
SS�k�Uk#	5kh�	*
;Z	
d(	k'5kh�#
5�*�#��'*�2	)?1*U(;R	
�	�))!�
'5!�d;)5 !5d?)5
*5;
;
	%d);%`dJU�)�;
d	#	�-'U1'k5)*1�	!'?) *2 �k;k-	RU'�
?U;R?*11'�)�R*�'"'"�-d+	(U	�+;)k`(U!;J'h#(�Skh�?�'
R*-k(;
'2h*d�J!d5 �'
!�5k�	5�'5(�';UR (d
'*kh5(
�?S�		2'!#�?S 	?%�?(d�(R�k�'? !#J	 *�R%�'	S'	"h5?;�

*hS�U�U(d�SS	'*�
-� ?'	#5�-
('	*''55d�Sh;S%?�;�	'�'	!h*�*2Z('�Jhd4*�' !#�R%*	
�*�*Jd ?	+�d	U�*h�#+h	(%#J	?JJ!,Jd-	J'2�**SdS;�
�	
kU2�15�(d5kd�(?	Rd�';?!*�*);?nS�k%�(5d�! �))*�R*S�R-R-!��5

!;(�#�?S5
�?S
�*Z�5�;kU*5S���#d!	 k(�
( �(UR�
�?'�)

-5S�*�%5%h	)52h	! %
h?d;5
#RJ52d U5U�
�%1	�(J*%% S(*(% 5	SU�;Jd
�5%#;;�(d�k*(%d5(S�!�R�'�!?"d??d�2�d
+�!#UJ�US;--!(S)�k!%�U ()R(#
UkU!�(U;�J*	
R ;�-	�	%U UR�%d?�;k
 
%R;�?U'�;UR$-S�U5()k�
�(d�d�R%�'+
2 ;k� h!; ?h ��(U
 ((5U�
�J'�
(S�-�R'�2�S*'k�'h?!*)?	�U;*)d"* d(U�) *�	k�S-d-	 "d
'%	'1(
)�	�;�n5U�1 h?�Jh(�R�dR�Jh*)!* U)�;(�	;?2�S5dh+
(;�+,(RR%(%S!S!!n-2�)R?�S(	
��J4!5`
!h#�!55	k�2 ;k+'
�'�U�! ��U5!	
�5R(5(d5 U(k#
�R�;)�n	5S(S#�n	(d!� �Rd+�RnU5�1 ;��
-�%��%1* k� (U5J�'U
-
+1;5d5!��'S'�?�?(`?
�
J�2d�!�%`	
�'n
�'�%

'-%;�%
!�-J!!�2
�'?!%!�'*�2R1?�R#	�R2�*)	
+�!+(�)	*
!R+�
5kR2�
*)	
-#kd(;!?S*
�'5 	�(;d��;(R!;k'Z(% *	*-
'1(�?��
�	U*'�)U�(dU(#(	5!2� d2'?	S
)� 1*�J
-�d%�h2h*�%U-?5U-
�#�'+�d5-!R
! Sd5 �**S�h!RJ!`	
dd)�
-
5U5+S�5d(1*?'-	
5Z#*	!�?*�2	U!�1?�	?*%!!U55	%!#J?k?JU#S�%!*
?!'U?%`h(d("S�h(d*�h� *!k' hd�? ?1?�*dJ�*?S�!J�!hn!�2;dn �?�n?�)+ n	5Z#�d!5?n�S!�!Jd�n?*?;�(h(U'%k%'�5J7hn�2;
n�5Rd��	J!U%h n?*'
;1(2
$�'	�%#
Sh'�+�R *hU+JS�?;�'%!;)5�S?+�Z1h �#
	1�?	d;�n1k;�SU
Sn�)�k;nJk�n1;1S�?k;!(�R*)�!
;d�'k*!(�;R	R�	
k!'�'��;�S2"%1R
#kh
)�U
)��d�'R;
	�'#'*�%
�'�JUU �
? �;U5(S'� #�*# ;RUS !?� n!n	;� !�#?1' 	RU�!
-�kR( �!+! (7*�SU2?�5J
�1

?U5(?�
� (1� 
-U d�)-)!1
�S*2*n�!RJ�'n?S�%
#�5%h�n	!?2 �nn
5;'�-5(%%!+�U#J�
�;(Un**
R�
-J�*;
-S'*d
*'+d;1�)�
�#�

)-	RR'�Sd)U	-5*5;1�'�*1(�' 	R;2)
 �'!�d5�*1�R?	S�dU	2(�d��S*�!?S+'1'+�)2%	R5�!
2J!�
#	-	S 2�5(�
%*� ;55-�* *!'5%5�*?'51
-#�'�
(�'#
�
-�5U�'�
d!*Sk
-(?dkd(�51?kU(!	5dU 
-�5d)2+n
-�k?�5d�+�	

#�
"!`	
d))-�'?��h?*'n1� *�;n?�n1*?	
J�1;�n#'#!�'	'?5)	)	25d
-	��(15!Snd!d��
�;!�S
-	5k�)	�)-	
-)2	J(

 k
(d�;h(�)S2�
-SJ�; 	;Zh!d!d	�RU�
-�!	S5*Z	



�1RS

d
5*dd�k	n(�)*k *
�J5!!�#�SZ	%7#?"h"U%dJ )*	5UUJR'�#U
h
R�S
?#
�kRS )?
55- U�!;kSS
 h? 1*2Ud�'
�'2)�
-)(1R+��k(�	�'hUkS	'��h1#dR*(�	(Uh(;h�5#
d?%J(S
UhSd�J!S	d!k5S+�
�d
�SZS5U!

	 (U( SS�U�1+J
!�d

d�+!5dU;U%`�Udh`(R

-U	!
5J�!�*5(�;?�J�(h�!2 �#
��k�%
#()`�S1(	U U5;
-�;!�5)!;
1d5��'R
�)n+2	
Z1(	511 5+;--*�	�!-
5;)-R1�kS	�#hkd(	JU�-
kJU	*#�U(1�R1�	%	�-S;	?�k�S*#-?h�RU

�5�;1*�(J#�5�5	;n�!S*�%d*Un!S�#)
(� *
#n*�2 �n� 
�%	�
+!�-)
 
#'(Uh	1k�;(�%;1 #*((U?�	
-
�2- 
%h!;S	
!%
	
 !%�d	�
-'UU5"U!	
!?
-�%dh*hUk�kJ�%##Ukh(�� *!R�k`k�(1 '
)k1U( �k! ;�k"�S(d?�#d51k5dUZd5�5U d
%1d
 
-�%?S1
%�hdk
k*5(�
1�UR �%�U%15  h�)
-15
 S� k(d2)-( ��*
5�*%kk�d�Z5; �JJd�
-?)�hd
dU5�-
U� 5-�5���(	��U-�)�	;!U+
##nR�h?d5-��5(h-S�;Uh1#U5 72R1h(��(Rh5(�h(d�5))2�kdS!�()5!�%� !
(U �h1�J�*	h k��(R '!	h(	5!�;hSZ(dh� d!(1�U'�1U'Jhd'!	-)�1d�(h15*(; *)(!*	+U*(�h(d!	dRhdR�5-)�k�#	
-S�U�d(�	?�;'J�	h5)�U�!';%*kd

)-;JZ
-�5%-	R '5	k(*R
�(UR!1;
�'(�225UU
	 S;�5'!#5U '�h# �?Jd5
J'�5(d��
*
�1;5*'" SS1!1S�d??*�*�5?S1�?R5!U?�
'Sk�U(�5*�UU%�(d�*�U?S!?�!5d�
-�(U!R2!5Z'h�?�!(h�!��-	�2(d�?
;U(
	�h2U!!%-`?#?(	U�)2"+� J+% dU?�dU5�k
-�JRdU?+
)**?�Ud
U;5�R5�d
Sn�
-�(;U�UR;k'�U(J�)!)
��hR#S�5?#�USRdUZSJU;RSSk� S�S)�d(;S
R*	 (�(Udk5Udd�?
S`dU(S`dU5
S5�Ud5U;12"d�R(5�-)�*;'�#! *!�-	
	!5�J(U5
S'#
5
�(��5S�
*�
2S5(2*2*�51S)�;*�!1n�?-2)
S�S);5�*5'S�k;�*�;

1�5d�(UR�k�(	
�U
-%
%
-�%Ud+1�)�)5-

(5	(U��1
-5U#!�R�U(R�d-d�
;S
Rd5J!
5d	!R!U?#�*;�hJ- �#-
%R!?'�J%�('!�
	*
kk-kU�?*U(#*k+J�?)�k�5*�#!*#
	��!2'	?�-k

%!-)�)?!%�!�J1(�S 
�R)ZS!5S�	-!*��-;1�;	*1� d?	 S'�-2RU!5�JSS�!Jd?S�S2+k U?SUS 
		dd!5!1%(5U?`?*R
'
d' Z'!?*'!'�*�*
'�'�' 2S!�?(?(�
�U	
%1'5?#�)-;��
(	)"!�( k?�2
5Z%�-#�(�);*`!5#`?;� 	 �U( *J
(d�*1?#`()��#	k����k!	J5)k S
?(S�	 �kk*���d	�	
�hkn�5d((; !�U%�!
�#�h'5�#
(5'!5d? k�!U5U;�Z?Zd1!�-	UdS��#2;;
-
-�;(;�?d�SR k�2	�dJ;;h�;?`kd
�;�#�'S;d(�'�?�
	-
k?�
�-*U1
�12
#�
-�21(5
)�#!'!S�`

k�(!	 	2kJ#!R�;(

	5(U!�)hS�	1�5() !(;##� 2!�-S%U#U 
*�
h%k5�(*?�#Z�SS5%7'kS�kS;�h
 (S�# �d�!S S�% �'
-RS*;(2�

%
(R1
�(	h%� -d5�k;� 54	
)

-dR;k;?k!;U 	�-
�*)
5(#255?%?�?+?!'#;k-1�R;
((;�J
�(#;d�;U 1;1U  );R;�
`�!�
-#

�(%
��	
1hk*5
#");% U	-2� #
?(�#�(;5SJ�;�#%�5;�5�5(U�R�'
	(d55U
5-(51Uh2*S�15��#h3
	 ;nJk+��dn*	-;n�	-J!#�k+�
-�)h5%�#
-
d12�?;h5�#k1;k�*()-2Un�#!*1�5
U2#�1%�Ud	�5(	S#�k5k+)
�dh5UU5;5%d+	�!?k�?�%�-�SU�!S#kRU �J)JUU-
S'#*JSU5(7S'!�
U�) �%�R ' J�(RU;1?*�	
�(��%!�dU�!d;R�!k�n'5USn�d�h
-)�Skn'k�n)�d?	�U(U5# 	nRd�?
�#5S%k�R?�%

)#n�??d�#	
�
-
%?*�-n;
�U�
)
# �


?#- U`U;hJk5;�%�UkU(5!%UUk�!5�
k2�-
%!U�%?�1RJ;!(;5
)2*n'�

k�#�'UJ
;??%�k'�-%�%�(!!%!�5�'�-	%Sh!� '�'U
k�!� �(k�(*!�()#R;%�'d1%k

�UU	R-J�-
##
-!	�k
�
%
R�J(h
�(U1�S( ��d5;UJ2�J
-	
!

�(U!�U�
�U5-�
-��;
SUU
UdJ#dZU�J5J#(�5;(J h!S+�Jd5n*�h5d-n?"#k�(U!1d�
;(�R
;�k;�dU�5`k?	�5)?
��k%�#
 5(k5J(�
5S(U))k;%
"5J�?
**U55()2�?%�
#)!()?k% !���%hd5`1R?+)	)�'S�)�'k�R!
-*�!5
5d(�-�
	?d5dS)

#(
#�hd dRh!
�*1;"#�%�nn#
�#%(5R	nS?�%
!23
)	"SJ�U%5?�d?%n#n+

-��%	(JSZJS

� ?�-
S5()1;2�#!%h�hJR;1Jn!(	J15k�d5 R;!5;�
�!�5
% �(UUh�k!�
5%	(U!%�k*�S�U%dh;SJ!?k
%RU%SS! 
%RS1
*�d	J)U	S	�#�-h#k�-?	
*�*!�5�Z%(%
%�)-�)-25%5((52	2#(dS�(4$�5	�-��?+�)-�5(5d#�!!k�5�
+k!;!(n2	)n�	d%
"nSS
)n-d(
n�n Ud-��*RU(Z'%d!
# )�),Sn�U-J*�-)R+2�dh;
U)�U� #J%)`�)S � 1*�(R(%,n	d�R(*��(UJ*d*�5�2  !dJ#�d(n-!d-)
%J�SJ �!S%� '+�S'R�+RR*�*%'R��Rd

	;k-%�R%S�RJ	�-�JR)-U )'`?SR� �k�J?U 1�-)2�2

;nUk
n)
UU)
2�	k*�R%5d�	# n�? h`'	*�n`	#�nS'�n�S!#�S�5�S'(R�?d	')
	
�)?�'�5k%'�S�d!S�2�S�kJ�S'S!!)�kJ5?5!kR!JU	
�U5!!;
U-R 5�;UdUS� �Rd(k�*S!)�)�
)!U*;!k(Ukd�!
-�(!		Rd
-!!RUU
 �!�!R5!�2!-
!�?2?-�U�
SU�*!-
!-�!h%n;#;5'	d	�5?(�S	1Z!!!
S`-�J(�
1	*�(d�J
-*-;%
2R�?1�'#dJ	�
#;�?
h��RS!Uk h(��#%S)!;	)�(5-51�)5*d
!5d�;1d �-2!5(?Z -'1'JdJUS�d?
-(5d(�d�1 S*UnR�d5�)d%R #�
2-n)!)�-
'�%��%!?
-�(d1R#�J
-)*'1)��
?*�

(?�) 1	h*R-J	*2�;'Z;RJnk;#�%�SdU��!dJ*%;6%	-2�(`

?�-%	%hd%khkS�*% �
-d	%	 Z%Ud*S
hk��J�) �%�U%
*%-!;	%** 
1*	�##+*S?5��*%�'

%*�JSk�S-"#;Jnh d�*;�1?�dd;!��*
-
*%R;� `#'#�n2nS
��n%n+
?�
�S?)
	�! k� !
;S!	)
�U;�Ud#U 2�*k	�
�5k	#!;R(�d(5�%�2
�#	)�#
%;'?1U-(�)J;S�;R1�;?�';U1Z
-!;*
-)(;�d�*d#`k(
J�*%U'd5!�%5 )
#�*#U%?
R+'4JS�JS+�; !Sh�;�(#`SJ+d5�k'+"JS�	 �5UU
k 	#! JS)J	d!?(�!
?()� !�d#(-RJ(((5)R(S)�d#
S#
(RJSd5U�	�hU(�
R!!d-d;
(Uk�-R(`
d5
Rn)hn(�
*Rd-!`% Sk
? �);*;?J!�	+	%;(R �Rd� � RUd�5k� �U5d�(R�!RR	5!U�d�(R5�
*U�
-
#�dU# +�RJ5*	!d5	!?'!�� 1*dR!*%'	2�%*�(k5	U(	k�R�)!
;"!!�
"'?�!�;�J5!J�5)JZ�2)J+

�
?	�R(�
	-�?R�%;�5�5(�-J)1�JZ);
*�5k5�('U�?%
	%5
?1�%�dh#�
?'!�h5d!dd!h�%U%)Z?�k# 51�5dU
�!
;
�;?d-U
#5 *��hk�k 2(� ��
-�	?�;#k�?!��J'!�;�#*S�5 d� (%�'�	�*?d;?Z5U%�h(U	(hd5 
(dhd(�1%�U%521�1
�Ud(S
(� �	(	)#5S�* SJ%U 15R%�?dU1�	

S%
�-		2�* '+k�1k+�d1
dR;�-�?d1;�5!h 1d'S`*�-S5	%*	!nh?�)ZS	�(5�#* %;�?-*
+-k
5d
!RU#�*%�(-(JS!
!Z!�51�5!J�U%;(R�
U!h!�R;-)
#	)�-RUR%�?�'	 #	#%%!�k+�;*1�1
�' U%?�-#kS�Sn##�?k� 
	%*k(ZU#�;�R�?+?Rd�	
�n!
+) Jd#�

R2d(*

+�#�*�*?�R#d
�*Uk�
�k��#%*	� �2*J�+5��'+��!;�	%-'%
 
2		�d;�!(�*;�;�;%�n�Jkn!n�-%�?#!�5?Ud)'J"S'?))J
% � U(��!S�
!?�d5� 5�?�! dd#�J1%()(( '( %1�	`#
'�	J
-�%
'�'5k�'(*��!kU?	)�!k 5-!�	2�U
�5h!d)�%Z?2
?�S�n?
n-n12,RU�'
k'�nk?	R(d
12	RUd�n?�5k5'n�-n	��5k�
k�k�dS�!R��
*d�;(dJd�!
5
�(#�5S �kU
'%�d*%�!((-�? �*�?n2 �
-��
5%#�+5
' ;)* ?;?�k
�;k�?R	;kk%;!hU5k;!�)
�
	5k	?�n�;U-�n#Z
*	nnSUd�%1�';�n��kURZ(*RSJ	 !�*UkR?`kR5d "
'
JkR(! 	5kd�U`U! ??-��;�U!(
k� S1;-�	*S�JSR1!#
�)
-�-�(Jd+d%
k;�;*� J)*�d
(J5 %)-��U)�n +�
�	!1	('�%( %*?U5?�R#S�'+Sd)2��;#515dRJ�
	 dR-d�Jk%?5!�)25Jd�)-?-)
-
)�J 5 d51?%
-'hJ�*�U(!kS*� )	US �k;
	5+!'*R`##Rd


�)k	5)U�?"k
��S	?;+#"J
�1?d(%Sh�'	ZS1SJR
## #%'�)
`
�k(�'S�%kd5�U(%'!J��
� 
?
;	#SJU	!* �Ud+�*	#
U1� 

�%�#*�#�d %*	%1*	*4%?n	
?
dR#*1
n;k#� !%
�J R;d(�nd�k�k	RhUn%
�*?�J'#(R�2Sd5�
*51;�;UdU(;
5d%%(d�;
k5%
(Rd�*�;%51?	'U;'
�(
d5
U1k;*5�2#'�	1%�*)�k� *#�
#�?� 5�
!!5
%�	�!2!*??;J!k2%?*(�;R?�;
�;�(;;
?;�?�;�	J5 +-1
-kU#hRd
+'?d% � 
-R
#�J�Jn� 4d512  (-!2 �(�J2
�-	*R1S�1)� 2k;d;dJ� )  �d(;`1*'�1JR�?!5n'�%S
');d��
-�"nR*
	%�J5�dSU�) �!S#%;#(�?)	#�R1d%�

* 6
-
�5S
)�*�)
'S%�
#**Ud;UR!�-)	kS(%�S?'	J+
Sd;?)'n*J+*�-�'�1U�(!J+*�!�%!)%		d�U(1R;k1�% d!;	 51*5�!51UR1d1�?(?�1 
1U�S

15(!)2�?%'��S!#��U(J5U'+ZU5'#J(
�k�S d5- )�;12;�S+!%n�J5  -�5d5'S	�UJ!�+��U)5JS "5�-
"51d2-!2

?#!#)R�5k �J	5 ?;`!%+�-�)*
)-U5d
%�(1 ;5SU
*
�!?k2-
?Z	#%5	()�?	5R�?�h�#�(%��5d;�h#(�	?�k-#5�*!� 
-Z)(5
#R(� �d!*�J2!�R;�#SR'
'k�2	��S
*�-;h)��'(5U% 1S�R�'*"(*�
	�;
**
�
�
-�5(�n	�+S*-'?�
2U(R`Z?
?)5'(�;
!`?5
(#�(?!5k#�(�) ?�	)!�2�#1
�
!S	R'2;)�12�%S%*();d*#d;?�2�(5�'�d2'	
(5dU'U%??;(� ?�R)2R; -
S%!U-�#1��U5#1�Ud!�n!2
S
�Udk�
n�%	;�U%;	*�� S�
#d(n
�*S	
'�
U�n
�	;*?�!UU�hUJ
?�?)*U#%)2k(�#
(	k(k;	*
(�)!#'�1'%UZ? %55h	U'�'*�*!�!R�
	
�

-(+�!U	(!�1�
-	 2�#S�
�RJd#d);Jh!;'k+#"n)`(S
!)1

R*�d			*�
-;2 #?J!J
()�"'�()�hU�) 1#�)Z?? ;'5J�?1*#	?�?(RhJ(1d�'�(d2R;(�(Ud1�?
;�-�d-Z� (%kUU� R
	5	
(J�� 	5k!	kZ?%
Uk%�	
�5?;)-Z;dR%U�-�k;#	*;R	!	�d?;�US(�;5�%(S#�;-�#?!k)�#(
%	5�#
	-
(?hk�+J�
�
?-	-S�' #�("R��(;d;(
?S RU�R(2�
	dh!;R	-(	�kUR?RJ�R(!!5�(5S!	k'5dU!(R	%#((2Rd53U(!�%hJ#"n2!�UU#
�!*h(URdUU'd5	�2*�hU(R�1R%�Uh(U ;!!h%�U'R�d5*;R
-Ud �dU!�U1U?!�*;##
	%-*( +#*;�)2�2�*�+�J�h1�-!�)U2d�#!�1#+�d2�;�-�%!�5U	
�5�SUd 52U Ud#-%!+�-;U?1RZUd�
#

UdJ�RUR;R	*%d2! �%dUd%	dU)S�?
RJ+Ud	
?,dUkhJUJS�U)-�U
 ;
	U%U�;!*2!5 �5U R,	USU5�2�R%5
�2)�RJS
!;U%5
�dh#�;UU d!'�)5d5�d)5�-� d�	�S1hR�
-�)�;k

-?�U25)U )U)(##d(k
%-�*d5�;	U�-)
�(�-*k+�*2�
+('?�(	�*)-hk+*k'�-k!%U�?�) -+*�J?)
-	hd
)-
 d�
+�`
�;+�	
�k?;�
)�?	?+'�1#%!(�(
-5�'�S'h�

5d	S?�)�')+�
-�U	
�S

h�(	`kR�%5-
	RU

5JkS%�k%;)�5	!U!*;�
-;
nS( ��%5'(#1)*d	�%1 ((h!%Z*#?�

"(5*�?�S�-
�;	d	-k5d#
5kk5�5UJ�-	
�
!(5Ud1U�+2U#�)2R;	k
	kR�5!J'5�S!;�#	+��)'S*�
k
-#2)�?-))d	5�'�*?#�#� �#�5?!U%#
�1 �k%5((S	5S
#��
"%�	-%RdU�-#�?	5+
JU!+?%
"�S(�%�;k	���kU'�%R�S)"%%+% ;(5 �'�h*� 
�
2;UU; 1!k! R+	*U `	�)2	U?�(!k�R( Z+#*# 5%U��)-*(!5,;�R;d5#�' 3!(2�
J�Rd1
 �S �
RU22k
U �
!"*�

�#),5Rk)!d	�k+�' !�#?�''kU!R;?%�h�"�R�d�5JU;d)2Zk#
�;Zd�J5
�R�
)k�!?
5k�!�5!!k(%dd�J�!d(;d�dh%1?k�51nU%�	 1
R%(%�
 Rd*%#2`%
�Ud-J�%*� �kd?h(*� kU
-!Rd� �#?�U��
'
-dJ�  �!?(5%Rh(U?5�%U	k1h%)k%k(�k%!!k!+� J�-S	(%2*�k*SS�k%
* �%�S(#!R�1�S2��)�!!!5�!	U�5;�S�;R�*�J%!
	�Jk�-
-+5;�2
-�;	!+�1hkSZ�%1
�(J�
-J�%kR?U(�)	J%��knk#*�U!�*J�#	#
S�SU�h�%k%R��*%#	 %*		n
%#
k(�*�
R
*?+�R�*#)$?���
(R�(R#J�R()h
R SJ�S5
�U51JS	
)-)�%*	
��n�d5� (5* !�
� U(
2)S�1U!;�)#�?-5*�(Z?	�*Z+�1dS;JU*��hd!�1!d
-!��!1�R
S�
-
 �S`5Rd3;'�';	!;(R#RS(5`d5(�-*!%;*;!!S�*%(d
!?-))�R�U*�*Ud
'h;)-*%�UJdU	(U�
-�15*�1�5'-)�2�
+	5(�?�dSR%�J?�%? ;! 	�;)�U�R`S5)�;)
�
#1R# �U2*#*#�	U))	�	�5%( �h�
-`%d�*	dR# S�S2U	d�(S1%J� 
-
h�S*�J(R�J�%�+�(2'R?'� 1R'�R # ;5d	R�2`?UJ(SJ
-?1�!!(Uk�)�R?	�#U�
'�%R�!'#Z#;R;� Z% #!;�!	U#%R'�
#J%?(*�
(�)'�*R�	*-Jkh5	)*	?
�d)d)?*	h	1?d??�h5J�*�
��!S��k(J+�(!
5)	5?"d
*%(
�
�%h(�%;*�#*k?5�	?5�-	
S;
%�5R1#�'
%�+;S?dZ?`(*

5�;
-5;5
*
`5;(�5� �1;�5Rd U;?5%%2�#�1?
-� %R	*;k	2�5;�?
	(ZS??�d;! �1�Sh	d2
UU+�	U�%�*?-?�%?��1
d�;?	�5
�R� ('?5'2;�U5';'	U()�;�k	�kU 	
'�2-'dJd�		;!U'
(�S)'d'()(�U(�RU?�#�	R! # 
SJd"R�
�(2�JR+-!*R*#��
�#!	%�;�-
�
�R�#'SR  1*
- �R!�-
�R!d
 %�R( ;!U�;U(U�(-(%1�
?	) *�5*RU 

%d
h%�ZU%(%dURUd� R1)
J�R?U%d#d?*�
%U�*S�h;(�)%#h
(�dJU�(UR
Ud�	*

	
;-U;k�2
h('U(R5�h	 !�%5dU��*�	'	55'(
�5#
�5?�R
)�dU�
-�5)Uk�-!*�*(U !U1d�)�#�dU�;!R%�%��)�!#
5%Z
� %)*	%�S-*�?5
! 	*5�;R`(�#'
-�*#%!�*�SZ !
'2!(!k�5)#

1�Rk(�!;2	
5� R*�(U`k� ?;�dk#�
#-k
'%55Z5(�*2UU5+�'(�)k
�kd1�2J(!d(;(!d
!U!5�5#�dUhU!dn*�'1?�)
)!!5J-"+k5!'*�(#�h5�'J;�	%
 '?d#!#;#	� kh�hd*k#
d?�5(#	ZJd*((!2�(1�Ud�2  *5�k�Sn	?Udh(�(Sd�k!�'�( 5*5	Uk?�d
()��h	5
�#!*�%(�?S	d�#�S?R�?�hU�	
�

�
	%S!RU�U1�%*"�)'?J%SJ
�%(#d("d%%h*;�;#
1n 1d(1)	
%2JR'%�1�?	(d�*!U5�5�%�-
	ZdR5`;,'(	�52	
�!(�d5(5�dSU+�5S�;d#5�R%�5J2
S(
)%U`;	`
;k��%*;%d*!�5kd �	�S1�%+�2 ��#
UJ%"Sh
�U5Z+'� ';1��J1�n* R'� ?!�
J#S
 k,#('%d;!R�%R%�#(5UJUk
�%d;5J2
 Jh(�J�UJ('1+ 1U*�hU	;�%Ud*Rk�U5�(%5 2*U�!;�d*
5R ;1!�!	%!�U *)�*5
U(UR �*RU;!S;	;�UR(�R 	
�;1(U;! !1) *!R
k�(U�h%%�*k`*5
d# "1;�!hU�))2?#�5� ?k	`S#)?#!)d#
�)#�' R(S#%;�d`J
U;R12dh5dd
-	;�!�	*R#�'d�J!��)!2hS�'�h!5+�
U�;-
;
Ud�'�# %?
5#dhU%�R)'R�UR(U
(U?
)( 5

?
h�!'	 (UR�U	*UUkdR(U5�U�!;R�;1�5
U�?U�!�#
�5!�*)5!�5%�R-�)�%)d(Ud�5d((k
;5��R;%5	R;U!-
�%5)�U	Ud5-
;;

�*�52(
n!
5d
1�(;5
Jk�)2?kJ 5
!
�
?�#%(51(�*;�;*�	5�'#1 'RR�5k(
hS�R5�khhU�%;? �*�

S!'#%
U?*

U	!5(#*2
R�?S
*!�;� U
25UR()R�'#�2( �R5-)�US5U-
1!�
(		'	!;(%`%(SU(2(	)
�Ud%�!%�Jd7�*�)�
n	+� 1�1;�k�* 4kk
k!5d;�U)51
Ud	1#�5(! k�(d�Rd�1U#Uk5k� h(
kk�U�JS*
!#5Z
�		��*k�!�R*!
RJ#	�#�Rk�*#!(RRJ�U!%	
Jh(�U�J(1URUJ�## 1h!�U%R�-�h!R*!+;1	�;U!RU'( �5J?1k(27 ##*
; �U�%�R5U*1�5' S�RR�*(2

UU% RU5�%)#'
U!R%;�!##
(h�**h)%	 �*h��2!�-�1k�'�%�	J�!+�';�+�J%
*�1;�
Zk�5�!?�
?�U(Rd; '�R!hd�S1�!d*S�
�#)�
-kSh'%5?#';
5U' � JRUS)	h%R)ShUS-S%!k
-��US
�% 	!*� d(J2 #
	!hd�(+�?1! 2
J??k
U)�
�!4k'�5'�5(5+`?�'�!�k+ �*5
#�

"! #	�1
*# k��
*)�';�**U�d+5�(U
U
U �d5	�

-5dU1S5d �d
5	�5 ;�d%5�R�d�5�
-S��#*�'�d)
S
�)-�2%5)�
�S�
(5�'5 ?d(5d�-

+#�-)�
-?k��
5h	R!5	U� -�)1� !�S5!U�-)2
5S5*(U*5�!
-�Rk!
!(�#�S)" #!d* �% �	S
!";--UR�RJ��5
-�d !U 
" J!U5h�U	U��*R%+�U(�	(?(S�R2)-! !(%1�(R�)
k�?n�2S(5!�#�1k+%�%!R�k�';�%-'15!S1�%S!%*�; '��J�d%	


�+5 �'�;`*# �

-kS�S

 J�%5Ud�R �
'
�-)�(*h;�'�'#
)�!Z	)�-R	5?5
�S!	*�#��2�dS�
�J
-�-'�
!5!�J	JR� �( U)
�J1�kk #52�J*% !h1�S-
?#S��%n;Rk%!+k%%
h%�%!k"!%)*k??#SU1dR;(R51!%k+kS�
-�R`�5U%5U-!�S	S)51�'#�UR)5U�khh'�1 
� 11#	2	
��)2S�?#nR�#RS(;!#1d5�'�(UR1+n�
51n�
-
**�	�*�k!	dk5� 5(; `k�5`dU;k-2�1%	('�1d�)-J
d�
�#%?�(RS	 Uh1�5(RUU�%U
+?R��)��
n !+
Jh;(R�SU�?' ;)%RS!�5kd+��!(�5d�U!�k5(U!�n#S#S�;
 SR*RS?(�Shk2n5k�?+?�R�);
*?!nkRS(�!5J;�)�
�;R#( d?#'�
k�;?d+R�+�U�S�R
�R2�?5Z)#;J�'RdJ�U%�'U�;'
J'+d%	';k�Ud�k`d)-;
(d�UJJR
;Jh5(5h�55?	2`)154n	55n�	Rn
-5%5S�R'
� S"1?�5���!#�5!*%1�1�?	J;�2k!
n`?;!n�J;k!k�n?'�R
S;!?k1!�
;�n
+��h15%*!?�+Z#�R (
*�17,�2
!�S*%�J1S%�n!S�RSU?!J`n!J h)?JU�+ (Ud?�JnR)%R�JU(n�%
U�Jh+"	*nR2�h?;%	?!Un	*	�?R*R�1kn�h#?)S�%1�)
+n
���	�	 		�)�n5h?k�d	
*��?;�!*�S;#(�S	%h�(JS�'?hS2�'5%�%(�%S�
�)
	
�d)
-d*!�k!!h!�*�SJ�	k�hk��	h�( ;�k�#
	)-kSJ�
#`-(U; �1;`� )
5'*h;1 *!�(*%'
� U�
S�n5(�U
 
1	%+`%*�(!�;�n�*�+�JS;*'n!%n;d*(�#-kU#�%�?1%�;�-	
'%�%(�kk��kS1+%R�
-	�(U�	% 
��?
#
�'�; 
�(JR�'�?S�
;��)	*-
"
k	2J!
k'
S�#
R;
#�R1+R k
-UddJ�1�#%
Ud
#%!U"(
#1h�	?�-

1*�	)(RUJ�%�)
�kJ�!%��S(?�(? �hU;`('+!' %�'*#hZhS�5
!
5R-%�+nR('((( (;�;(*hk�U(2d;�+;%?�+12�?(!��!;R(*#S*�(k+	+!�(+S
h(-�'S�J2,R
h++�%�# �;R�R�1Z
	�
�(%U�#' �;R)(!B�k�11#R-k�d%#�#U�( 5S"*##k� �!U�R
J !;J	nU`

�)�5
-k
-�;�Sk� �k�?5�h#J
�k; ;�)*;�1	U;#?	d)
U`�
�
k�))1U
�;h)-#�5
-n5�hd)h5U"#�!
�d5 -� )	�kd�Uh;�R#
-;�-kUJ-� )�)  % U	-!!RJ%!	
;,hJ+
�%?�
%(-�

)-�R�d#�?#
(�-	
#hU�hRkJd%J	S	 1S!Uh� ;'%d	5
 #'�;;h %Jh�;? ;hk	U5!5n�n;
�# *?;(5�	kR!5'5U%h�(5R#%5h2Rd)h5�
5
Uh(h5;S(5!	*R5		
5�S	dh! 
�
	%55!(U�+�U5 �! Uk;?'+'5
15%�
	�hJ%?�UU�Sk�kR5�� 	U �k5! ;�;R!(U;)�U1�%;
	�)h hJRS!RdU;(S1nJJ�hUd*
#*;h�5SdU� Sd��J1Ud�
�
S
�kn*d(�
?#*�dU
#*5�k#?R;'RU�'525
Ud!;�5hdZdJ(�%	)-(5d
-)�!�%)S5;R!hd5;?�	*2JJ ) S!* � �R)'
 	'�?��)-#?�-%	
"�'#`?�	n�#(�2d �d
2 ) !%�U%�! U	� �hJ!#U?�	�;)��);��J+n
-k5?�d�-);!d5?
n%#�!U(� (!-;;�
k
�-U-5Rh)(UUd?�2*!JhJUURUhd�J�JU�!S 2hJ�Jh5
�))	(
5;5	;�kdR!R!
5Sh
%+5%�+;�!n;	�R�#;	1S	�%
%�k%	� �Sk#�!#!�%�%	�%*U?�J�U!Jh
#dU#J�
+�U

'k`�
?n�5!J)�5h+k
�5#�* 5�%� 
 �Rk%dh,%�1)Uh ��� 
 	*��d!R#
#  #
S#1
hU;U! 1 (%(%#�R	 S�%R5S�';�R�	)R�#�')*�'�#k!)RRJ	5(�R%S�%S%�;%�
	hJ,h *S�#+�%RU�+S5
"h�
(J5""5;h�(SR
�R�J2�Jh#URUShJ	#h)5JU ?�+h� �h hJ-�R% 
;1�d%	5
5�U
�+�)�#R'U-'2�2�`hU �?�-%�
-� �%h)
nnU	n?S

*h()h(�R(�h)�
�!Uh`%S�Uh�h%%�S5((5�#
h�)�k�;5!J�?'�(S5�R5(J�JSRU2%1k�h%
2-;(
1�
	U%��5S
U�2�1�5J�d%5	(�
dU�1#k1�
J1
#	1
UUh�
�
-'%#)
!2%	#�
�S(#*�!��#�- ��'?	#%S�;!!�hknk(
�
U
#J� (2�5+nR�h!5!�n !�1� !'	k�2d
d%��5;�U1�%	?		%;�;
?5(J5)�%-�;d�UU!;�hd�
?Jk	�;(dR5Z#
*Rk	R(k"5�	
1'�JUJ�h%!*?S 
d!�%�
	 ?

#?S?�?��U2! ?+'%R
J5�k%5	'

k�
-�%!?�#%k

(�'kSk	)%d�S5%hd;-%d�n5
	1h
J�hdJ�-1�d'Z;-	
�
1(S�;(!


1#h;51�hS%d*!h�!5hnS!h;n�;�;%�%	(;	%?";%21(U	�-
	��U15J;;%dR
;�USd� *;S(%�
-�
(d2k+(�#+++�+#*
?+�1+�
�+�+R+#;�
�SS#�;�;2�!#?Uk%S%k�U
S�S
U;d5'+?nn?
 `'��n'�'�?#n*k
�(5k)� ;!		-dUh+*"��k"n+!�R 5R)�R%�1R�;(�	*+n�RSJ�
n5ZR5	)%?�UU5�	
U*;
	S#�!�?+�!�h+�Sh%k-S+JU#'n�
�(+!*�S'5kn	 �!U5? *d�*)�	
%-!2	5U�(�J*h�R�?!?-	;���hJ�h!*
R
+h�hRdUkU%
?
**(�-
R�
n�(	1�! R5(hR	�kU
�R �R *S!�**J)R
�-*k?!R#
)`%	�
�	
�h5;?

h
-�(*	)-�J)1S	�RJ?�k(# SU
d1�#1!�
�%
U�U%("+h�5 S	�d ��
�?U*5Ud�d5�U�d5;Un #!1U�	)2
J!15-� �( �#dk�S?�?S%55Uh	-'k�U	 U%

-�5(dU
5%
�U5�'�#
�(	U
*%d)hd(�'
*�
-	�R(kR-(
UU5�k	)�?'�,
��	2?)�%#!

!-�k?"( !5kUJ5);#
%!J
-5d'%!h"!J�Jd
�
-! %)
Un(kJ %U-'�!5?S`�	2�S?#!S+S�?#S
S#S!R
-?SR
-'�SU�)�J	
-�k5U?S4S?	
dS+R?S!
4 �5Jd�5R;#�;R	�
�JR 	Z	�!%*		U!	�?	#?�%#5+,R?%�R�+J�%�h
-
�(U�*1%�Rn�-
%+
nR(�n#+
%R U�Rn�?
-� ?
�)+�%R�h�;�-k%
�*#	?#�5��
-S�'!J'�R+S� n�;%%�
)
;d�-R+�U(�R;Sh�'+RnS
R'%�?
-'!nS�2d

nh!	 !U2�	�??�
2%d�))*)
#�!?1'Jd�!5R;(1�1 2�
-;J;h1�h; +Jk�;(!�
k;Jh
*; �;J�	
45UhR;dJ;�k�k�5h%*##�#
�!�J(

U)2 ?1�	
!;;	k!;h!U)2?
h1�;?�;hJ�!*
h2�%
;�J5h�2�;	Z(;J!�5	?
%�+�) 
**%5(�
?)�Rd'*�U)�%�5dU �#!##-%U5
U!�
+;�UR *�
�d
(2
-5(*d�S'(2
-�+n�+�'!'+;U1( U�+1�)!h)!R;%�-UdJ5U(�� U��-hU��%1h*	)(�d*(�JUh)(! UJ�	(�S2d�%hJd!S	?
)-;1?)*d�1`
�1;�dd	!d)(;(�
-�dn!2�S2�*(U)�#%1	5d#�5Jd)d%UJ�(J�R?�)R! ##%R�R*#R#R#!�?#%R!'�*�*�k;�kJ;�*�
�-	�n)�!U�U5RJU�5
�%k�R*Jh�
S��;?S?(!�;+'+dJhJ%%nR*�d'() �%n+�h#+S%+#�1U?1%R#"Rn1	;��1+n;n#!k"(1kRh
)SU	hU5� 			*35S 2?!?k�hJU%�-hR�*R5%5U�5JU
RS?�h	R#UR�nhk�!
�;%�UR2-U �
-S2+J�!-Z
2,+S�);7+J
	�;%�)!�
*�%5�)
J�J
?�U�)�!5%�%�#R	#*k
�(5 �-?+5U	)

(�d�R'%�)-
�!JS*! �*S1	�S5�Rk��	
-J!(
	#�
-�1�	*S
1-�!;*Z(�15(*R!U�k�5*
�5 d`
*+�2	#'�#*;	�52;	2S2��nU�
S�	!R	`#*!2*2?1�
�hU)%�??+)�	)k	;'	*k

%"*	
�)
-)�ddk	�d?;dRUU1
);
�%U	�S%! U�hd	R�(55
	�%2� *�k;*
	%%'*?%�%
5	S�'h�
	
	
S2*1R(*	;!k�k
n	�S	(R�h�?�h	d�R#)h2 Zhd��-?#d	�U55�'R5d Ud*�dU+#�;SR�U#
�dR �#!?1�2!%�?)	
+�	-*
+
d�	�?�%#�-(
+U!S!Z1Jh�	-
!S5#�?�+;�hdU�?� h?15!1#!S#S-5�#	*-1#�kn	!R 
)'�U	!
�	2!%
;U)-
(		k-	*�
-�k'(!�?	�)1	)JJhR1�h5%	)	�!Z)	1	%5�J-!)�?1
(�1 	
B ?!-�
 	
2�S	1nS	1	11#� #Rd+�'4�
'!+�%"dJ #%S�h%n5�(RU	'* )�k%S�Jh	?�?�#;*(%�)?d	%	%! %(%(+!
-�1J(	�?
��) !U�!%�%�hUU1�Jd�
`JSh�1'(��!5;R�!h 2#

;;4*#� #� 5�k 
�?J
2�
�
)-(;�2*'�+Snn S?
;*n
(	S�-
n�n1+')R!)
�d5�h
,
)�;SR�5;�2�;))�	�;kS;);�1%(�;)k1�1(S(5ZU(�'�?�k
J�U!�k%�2'�"R'd?kU

 ?d?�k'*!S�R! )�S�)!R�1�'1
+h�h?�d#�kZ5R?!!�	?�;h*��+h�'�dh%)!?h��'	
1dnn	�-#?1(!'�;#�
R-�RJd�%�Uh`�
(*h)kSU��?�%; �;(�	U+1?);5Jh�?
%�-	 	-d4??;�);�kU;RJ!
�
)-�d#�--	*';h!U+#��
�;	�5 k�-
�
�';#
-?5	)`)!
�%�%�kd�1#
5! �R%(	n�-) !�2		k!�?S%

-�
)�R(-`?�?	�Uk
#�U%U%
dU;�-�RU	*k�S*5-
nkn;�#-5�JU�;?�
-#%(-�(d�h	*k?�
#*k'�
-?!
%�kn*'�*�!�!%k?S+';(R�#k	�
-U;RU;5��Sd
k !�1�
-�
�?-	
;�(�1#h�J51�
��;S�
?	U �dh(�
#5dk?+
!Ud!!*5*	�5h5!#%S#
;#!
##SdR �5-J1�#(%UZ!S	R(U	Rk�1�#�%
�-)(k�21 1%U�(UJ��(R�?S�`hJ	`k%5 k�(;�U (S5J�%S*h5?S-hS���;k
J�
-	J;?�*J

+J	+�-
�5?�hk�U% 
*d�#J+�d�'*
-�Uk�
�;(JUd
�
5-!! '�)
�hkU(-U)5J-��('
2�-�;U5kJ-�5%JU�?2�(!
5

k�dUZ(h	�15	d�R15
U *%�	 ;h�%	#;(!�kd ,h�U%S
!d %U)'	
�?d+;�*+k;�;
JS� !	5�*1�-)
�;R�';
;S�R;%�;
�k
-15�';
1#? �!U Uh
�(-�?�k5h5!dURd`% �hJ�S
�Ud)�';%U(�d!�(U�%U2
k	�%J-�;-!?�R(�?S		
-�'!+%�
	;SdU�
-�()�*
5h �k!	S())5U-	
�?"	n�1;	S*	!�
-;hn� !�d	d1
 Un4;�
�%(1(?)5#'#k( �!+JUd�SU5�
-1U15d�
?*%-
? (5-
	ZJ1U)�*�%J�S#	#% 	
-�+S2;
Sk�'#
S	J*?hUU5'S)hd'%*�!( ��2! 	;#S�hU	d?�#*�	 %2
 k
(?hS?k�S!(;
S �!S'5
)�21�
dh�5+d�5(Sd	?!R�	
-# �	!1S�#
k�(R
;�?;!U#5h(	5
h%U�SUh
R;�5(Uh5%d��%� �R5d*d!� 2#!(d4%(dUS(-	%5-;Sd�S5�dJ-(
#�)�#�R!#�%SU	�
*
J
'�U!%1UU-;�S	--�
�51U�R'h#!
h)+;5�!5 5R)
-
�!J%1J%+(-)
	
d?1-U��R
n)�)	�%�Ukn)-�dk�2(JRk�U2�R5
)!(
 JUR�
�h!�2�
�S��n  hU? '	)2�'#�*
#5�;#�k*)?R-�%RS�RUUdR!S#"dSk%Zk%	�d(��!1	;k%�%?�S1kS?�
�?;!
(	 ?
!5�
*��#'�'!'�#' kU�Ud 	S`(#'
#d�S"SR%
!;h#� 
� *d�J!R;(5SRS�)-%
#	S�?	'R'�%�+'%;R
5S
h�!kh1�1; �;RU�R'U�R1J;2R;!Rd5�;!(�!'�1d5'�(5�(+�;1!�1;R�')	Ud1�
-!U�RUk�15(hd
�k�-2�n!�
?2
-1%;U


'-U
	�%!5URU5)"5�'5U;5;k
�J;�S5U�# %�# �*'�)( `dR;R5)kk?h*!�)- 

� )h

	#n%�R(�
h%-U-k?(kS�#�n�J*�?-R5� 

-Ukh*�(%-U-n2%+�Uk15;� hUS%;�h(�� h5?2
%(�%
kd�
**2�?1
*
%?�5U(%�'�%5�Ud(*�5
)	5-�' U**nR!
JU)
# �U� #�J5�(';�	)!?	k�51�5(�;
 *
(%	5�'�
�
k�5hk�*�!*%(�!;R
;-
-�k�dh5;k�R;J� �J2�?
R5) �U- �
5
'
#�
S?U?#`'??�n�J�!-�)12�S	!U5*
-�kU� R!��)�'+�!k#?' R?	'n(?�(%�!(hJ? '5
U	�
)-�?	
U(%��)
%�##�!)�;*(!-)1�R
�?' Sk(%
-�� R#)	 �RS	
-�dUU%�?

5?�% #-�hU�-2+5!U)5'�U(h�#S*�- 2�
J*)2
5(	�12h	!�k'h?R�2�d	d�'!?
*'n*
!5�n
)�?5R)
� k?'#?'�k�#�?k� �%
-?kd%5S�	%�
�5)�5	
�%#�(R	#	?#5!#
 `
�! �	d�	R5�k'(	5�?�(J;S!" 	(	5 2�
)�`!�d(k��n*(2� 
#?�%d�S);�;

hkU�
-�;h�?%-RU?h %k?S'�d(!�
?	�#�
?UR�S	�%(d�;!

�))S?S!2
dR

R;��?
'	Skdkk%�*;�R�*?2�k!�Uk�?dn5n1�21J-`h�k%!�n
1R�
	'�(
n;%U2�+(	k2 %!S	#
�? 
-(R�(?'k!?(kU?�
)#?n�#?"hZRS#d�R(S�h%(#?�;(`�(5�R�
!-�R	
(%���U!	(�*R�;hd�
U �'�*�d;hR!5;�#
-�d;�'RdUUJkU;5�	(	; 
USdR�21�'n
;�'R�hd!*'#�h
-
R;%';�!�k*((1�d�%�U�5d�!�
5)�5)2k(1)*!!d�*� (�# �''��%�?!
�S?
�?'�#
d'?*??�� #!(J
'-�n*1J-�'(�*;'5dR
!
n
R)2	
J;R�;k+k1k*1 1
2	)�d;� R1�?! `k!�J
d R�d�R
�; (!2"'%	'+'SRhURZJd-#JU%(kh�!�k�U (��%	 
%'J;	U 2SS�
S
S�'R�h�%#?5?�R2
JU)-Un`J'��%)?�#�kJ)#5�2!';S2�'5R'#�'#dh�#d�R	(RU	�JR!�;R(- �k5�1;�(
k�h!*2�?-?�5'
!(kU!RUU !�?	 S;	�*#;	R*�?	�S#+
 h�+�-�5�5 *h-5�kRS+�R!k�R�	
n?;U(5�(#'�S� Ud�
(	h� nSn?�);�(�k'nJ�hUn?5�%JhJhS�!2k
�';*�%#'(
�('k1�hJ�h%JUJdJ 5-Z?)h?2�d'�J
�!
*�kn�!5!*d�R�;�!d*d*h
'�1�5kd%SR1S 	'�!US��'(*d+��R!R*#('
?h�%d
;�!d�;
�U+�%?d*5�	h�)-�d1�hkR�S'#
'S+�+!#%�R!SS�*U#�J-�U(4
) �h�
)?�Sk`)!%
-U;!'�)*U�(U �55?#d�?h�(k!5'�RJk;�S! �;hdSJ5	'�5h�1S� 
k?-))� (5;(�	�52(;�;d+	?S(;�#*�h�R k5#�;(	Jd"h �?S#Uk`hRdh?%*?�
%� 2� (;�+ �Rd �U�(d�5	U?!5! !(�U;(�#?d�U*�J!kR!�U25
(;
R-kS;�J(�d(?�)d
#�d��SJ�SRJ(�;?��'-)2dS�
d�U�	�k1�#�#(�*#S;Z#
dU�`'

-
?;J%R;R)%	' -)�h*(�#;d'�JS#R�'#*?!*�
)�%SU%J%!�%R	�R�%%kR�5k';�)(�n#%hRZk�%R�RR�?!%*	#�-
k	
R*;R*�(d�h	)�%'+%� !(!h	R5�%d2J�%5!h�'
#�-
-k�%-�;d1h
#1
k�()-�#�!�%�(
'	(#;h;d
-#(�5(R(nU�R*	5(�#(UJ+"!�!( !	( ? !U�?�!�!)� -�(?2#5*5�U�# �
�'?,	 dU5 �;''#�!!!(k-� 
)*�!%-"%k�k!�)2Jk�kS�
-% �5h5;?J#'*-' 5	�*	%Rn
;�J?(�!%
;+�hk-(h�
-�%h(%!''�k�U-
kh
Rh;�(;#S�2	R;'!(!�U�h1U1R	d��*k�!
J*!%;?Udh�J
-3*�
-`'�)#�?#d!�#��d�J
))*'
';-�5U;
�S	U(�!�`%�1 k�
- %R�
Sd	�!?�(k�;#n!J#	 �k")
n;%*
#�'S!R%(�%
5;J?�1SJkS*5;U1


(?RU*R	d� 	'	U�(RSh+�(U�)
�	1h

	�J!��R(�%
�?
�5!�`�%5�)RS
�?'!U
?*�?�5-;�	2��'�R5?�?R!5!�#
-!JR��+�ZUkS5  51�(d1	*+?JS#��-	$UJ�
�#-
�	1
R��#�
�U
��#US"Un
2
#
%2R�
#
-R�JUR
-
�#
-�%�
#2;�n)�
-��JR�n;##�	�d1k	;(!*	h	k1?	�-
+d�	)�Ud
-�);'?Uk(��1 U5515U-1�??+J� %7(�
-nh U
�dU #
	J
�RhU� k?+n5?
#�kUk"k�Rh(�d�%*�JR+�(1
?�S�%ShRS+Ukd5R?S!�S Ud�S�*�dS5-		h	Uh#d%hJ?Jh4JU(J�(UR	�%( 
%n�5(2�*	h�%#�SJ5�'1�h�%!J�%5�J((�?5��%
�*;*� � R�?	1S�-5�#
2�n(U�#%!
-	U	S#�
S!;(1Jd�Uhn
 h�k(%7%U*k%UURU R �) !

)-h(;S�
!))R�*;�	*�	))?;Rh(hhS?	
-
	5UR�h
hUdU#
-`R U5d	;%!5�%-� !U%�R#R(S5#�?hk)�* dh
	1d
U
 !#+�#�%+�;h�;1�	 +5 U5 (15� �;��*	;UU;�!�)( n
�2d�� %5'1n1+*

5)�
?;S5�5JU��)1�
+!-);�!(?�%)2%d;!�!%5
(!�J(�;'U�5d!%2�(*(!(U%d�
-�5d �	dRU%( *+ �kR!�!(;k?(�+
n
')�%�d5(
n
2�S
n%
1!hk%?1
`R?�S
�	%		?!U*S51!+%?!1	�+� �?%U�((S�hnk
*)
h 5��';k
5-�(d��1	`;R5�k*!�RdSR*))�?�	SJ�S(Uk�
)(�	;*;*�+(S�U
�#
�%UR�d(((dR�S*(?d(#k	�)*k�(	�S*nk�S'*
-�h?*		1d�1
�	**)�d-�U	�	(�k�!�')-U'!�n�-%#�*!RS%'dR�#
*J�?
+*);��"+� 2)�h)	#�;5;?!+R�1 �?�	-2?Ud 

h�5�52J?�J !

-h;;UJ(;�-�!?*(Uh
�??�U�%�#*k�	�dU'�;h;??� (?5�;S2�?S�?	k'kRR�#
+!;k
 5;�`%k	
-�hd5)S�Rk-
�
1
?#2�;('� *�d*�k �	5�h S�%;(Rd+�1d?�(h-�k
�5(�?(�'k�RS;hk'h�*�	) 1 
�;Sk�'�S*!	(h�'	;J�#*�hR*nS(��h	�5J**?;�hk5U5
J�(!�JS2+!?
?
 h;2
"R k(�k?�#?d! h;Z;h5(�2d�#%(?d�?�kJ�	S�
-�5%�)�d?k�J
d�'*(	�n (2`�1 n-�- 
R#!SZh
	d�-
d�5Ud
*�!
�d5B�
#%1#
%)*�kS �k 
�5RUS�
	1#�(h'�(hJ�UR;n
�R)h"(k(-�(*	h	)2d
�S*��#�!�
�
�)
)25*%) 2R%5�U
-U�J7;'2+UJ
�R5�n +�% �
�?�!("h	�!?�!)	%d?h?)�!+ 1!UU;	kR�%'�% )d(�R! *S
% 
+! �+R�J  !R�d(�d	*%?�J(R(�% 
�1(d�('
 #!�#U R(U2� (� �d%�dS
dU
(;1�!� 22#	?�;�12� 
U??1�!15%�?1-?�kU�
S �;!
R
5��
)U5*	
U�!k!� h!	?�
�1 
!	�
�?)%-�%�Rd�2�(dh�)	�UZ%*�#%Z
	-)11�)-1
-� U*(-1)�-n1%�-)1�
-)1�*Ud	)n	RJ�*)�?�
�(�+
U	
4S-� )*(�!+)1�*+	d	5�U5�U�(U	J#d(5�(*�	*�
 
R #1RR %-J�5!�R%
R�RJ�5S!%	
-�R1J;#1�J;Z1
S	'�!d(!1U!�%)Zd5�1*;�U 1SZ)5
1n1S�*R1(Sn1;�51!"
�2(	
-5
1��(21UUS�2R�*1U �
#?(�k�%	�SJJ+
�%dR
US;� �#�hh(d%(U*%Rd%? %S�%	dJ�-#� �%R!?�S�2?!J5
%??%�5!�
-�R�R�J��U�R'�S'�'hk�d?	RS'
?nh(U�U
U)	�R(h��?#	!#d;�k�-#�	)�1�?*�1�%�2R1
2U(�1;�;!�%5�1##J�!h(? R
-)-
dS�%5�h(�k';#`'?�J5()Sn;
�
)-
	-�Z5J;%S�
	J5!�S1J5�5d
�UR
-��

- %*%J� JSdJ+%;�*5k�			�J	�kJ(d� 1!�R d;!J*!Ukh;?ZdJ;�?;
S�1
5�Sk5�-! �U *R2*S2+
S-�#1�S
-�	!�1�%5�SdUd*J;*(�(?�*%(�
-)5�(�R!�U+	!d5�5�	)��)�)S
!�(
!(U!'d+�2(##-!U*
!�
-�(�(%�
-)U
�-?52�)	�-!')�#�dU	?�U%�;dd� kUd�!RUZS(d51*�hk!*�J2-�; (,#�?(J#%*dJR5�!#�;%�US
�Uk� R!R!; �;kh
*5�nS7�
�U�SU�% J;% n1!-J%Udh5S-�*) 
3k�%52d �Uk5�J+U ;*�dRUU;S�J2
)�*dJ)!5�?	J'd	*	�5�J�5U	1**)(R)�U*�51S�?`##U;��R�	?��+ !�Jh(�R5�1*�R��S�?5�UR�;%#*??Z* US�5)h�
(U�!*kd�J
? 
#�-2��*�1R	;U
-�hS1;�-!;S�*�-	k;d�-	�;(U�k'
;�'
")Jd�J(d�')�?)�1UR�*!%*�S�n)�)
-%5�)�*;
-�

--�(;)h�U1;7`#
�
	US	hJ(J �*)2�	2�#U5R�h S1 �d�
-7�U5U�? S�5 �#U
�!5�+-�(�'
5�
S��- UJ�S*JRUk%�)UJ�(R!UR�(R�-R3SU�;R k	h(5
J'��-+S�JUd�J�# h)`!!*
�h
+-�R S-?�%U
R%1)*�5k 	*S)d�

�S5�;�;SdJJ!� U;� *;5(S
� )�?*S�'�2�U k�)! d S�% %�?!
S	-'�? �1%*%�%)(�h
�5�J 
2�5(1� 2��+*!�;S' (�	d(�)?2)`*		S )�
#( %�#
�	�*d!%)R
!-R5(('�#�
-h�d525Jh�+-nd�
?,�'nB5J		 %-���;�5#J	2 �SR#)k1#%�#J�J;�5?5(�1
� ;��)��-
#J 
�*n*?!
R
#Rn�!%
 +S*� k;�)?S1SJ?%#�J�k;?Sh��k;�dZR�S-5-S
S1+
?
�J�*U-5�%)�
%�#`%�5!U 
 dZ%%k�	)�
?�! �;R�kJ		
S?�J�')2�-�d�;	k
��'5S�	
R
Rn�n�5!�
�;d?;
)hJ'	
 n;�R+`�h*R?�(�1; ��+�
	�% 2�
n#k%R
+kJ?1(JU6*R�'U!1k17�%
!#
U!�!5!
��%�k�%!U%hn
)
%;R%!?�d;-;5
`R*�1RU (U�-k%R�
U*�(2dR�+!S52)�d	*(+n�#�*;!�(5k�%1�U
kR1� ?�+R�'� --)`*U5
 -�
+�)- S)?!1�S)d;��)�2
-hJh��d�

S#(� '�kn -
�
?	�-JJ4
*Jn�!U%�
'�'
��+J#U5U1*- �(k'JR�nR'�J'+-*R%k�%;�d"
S� �� US�kd�n-2 
S��5�5U	JU%h �J%n)(�

dS�(�J�(#S`
52U
�!�?%	�(h�-)2�-%2�2!n#?� *U(�?;�
+`?!??�(k	'�	JS5�SS�!	�J+5*J+�d5
5	k%J�	k(�!-d�	!
��*?�2* �U!U
-
(h+�2J
(U5*%h'J�!)�5S�?21�h
�hR(�	#)

?�S1nJ�;R !��h!%?d?h(	�#��!5R(ZR��Sk	�)�5J ?�( ;� �-?�!'*�'#';1%ZJ%J)�-)
�##S?';#SZ?�%+�#)1S

-S;S�	�?�?Sk+*)�' 
kJ1;�J�;n);2#�;kd)d(U�-���?�;nU1;d!�S�*#�d5U)!�k�(*�2
�2S�1h(�%S;?R; 	5�%(J�JR	!�-%	�J5Zk!S(S1Z	;%
-�5;�(n15*U�( k2

#!�#U
-�R#)��Uhd�
�kU5�h -#!�1�SUd��	 �*1�(�#S#
'!
R;�#�*RR"R���*!

k;%�;)
(';�n?2+*'	% #�J
�%)�SJ�U(dR(�)S
-n�d  k1*
�%5�-)?%�
�?�?5%;d#
	

	2�dR�##+!UR;))d%�)2�?�
)�*J*'�%-	1�;J		-%�?�-R	�'�R5	�S'�S � !�1%;1�#Rk#
�;�U'�k'5�*k
;)%�;�R?1
	�(�k1RUR)�U�;h*
(d*d�Uk!U#Uh5�RS n�J�S+!�*(
SR�(SJ�dJ�ddJShk%J1	5J��# S?�k�*J2�*#)� k�;U�-
�k�h5U+��;�S(+;'h��(#R�Sd�+�R1?
7�d5)��5�!US�(U%)U�*�J5
 �S
-�S	U�?	#1!S(US�5U# �)5!�-
dU � 
� �;��
-U)	�1!-
�5*'�d�#(d	1+*+	dS!�51d'S�%SU'U-5n	?(5d*1	*�+�h(?%
-	S!
h5(��n
 d; )5S
-d15	%�S	�
5)�d%*-d	Rdd!51� �J*S!
�h2%R1�)-�!�%1�2�-JSU15!S	
� RS#(
	J	d5#�)U)hUS!%S�d?�(?R5�!'�;!'S�5-�%R
5U 
�	!�1
; !	S%�%
-	Rk�*#k);!R	-%�U�
	5�?k
�;
-d	! �!4d#�
2#�k'* 2�5!��'	;5!7kJ
-�dRh�5h%�
�1
-?�+J�J*��R!hk%d5+5 )5
 Jk�
*UU%2*U)2)25 -2�U*h-J�R
R�
'�;�;S5�k�'+5Rk%`#%U(
-�S#�%!#Skd+*1
-#S!?#S!-2�?S#!!%�?��h�5kd�J�%�dU	5*�RS-Z�%�n!�%%1'�1 �?hd2�#�R'	kS1'
-�(S#!k5k�
SUUS#J�nU
�#(?
?;�
- ;�)+5�
-(� ?
-�J
1��'41S(

�-kUU( 2�
��-
�(2U�1d�d�	!U�
-(%hZ� 2*2)�(dSd��(2+�%U-d�() �)?JdR	1�5U#	)��%-d	�)!5!�5�!
	U5(S'-�U!�S5� +d �	26;�*(�
d* *�6?�	#
+ �+kS5 5(
+�'%;	##S
'-
?(;*`('?�U
�(�'n�U�5!'�*J5;�
)	
�
��kR5Uk�;�Ud�kU	5�	U`	Ud	!��!dSdS	!)U#
��1'�)U*##	?�� �#�	!Z2�# �Rh%��)�U!�5R?)%!d#5d	�R
J#'
�U1�*
�5-d)
# 	? �
);	�
;	U�2US`-
%#�%2� -k
?�*;-#��n2d	Rd
�! �'5S*%k�� �#55%���-�5�*��S!�
-?�-
�5;�?	
��h- 
U(5(�5 *�d�
-d�(J*J	�d;R(SU%(J#�1%;�d -�(Sd(?5;	�d'�*'n
d(�#�(US(;�?+�?�
S;!1#'�5US dJ1dJ J#� 5�2(#S�?� %?�
d�!�hS(�	 �*U;k	+U�(U
*S(�-#*! 
UJ%� 2Z	*h*!5((*1 #�h%J ; 1 #SJ2
;	�)�%dU�U(%	��U!#%"%?�
-!U�(�S�%d	(Ud�-dk%d*	* �%dU�
+%d%d%)
R(�R�kR;R+5(
-�#?�UR?�5U%Uk,Rh%U
 %%�
�	(�
�
?
(U(-

d;5 �;5UR5�;?U1�#R*�h;;S�+�- �?%	) � �R
� 2
�hUU

)	

-)
R�)d�('%�5�+(
-S;d�(J
(	-�d?!%S� k �h%	�
`-	*�Rh;�*
2-	nJ!h5�*�JhR	�
R+RJ;�;

�5 *�?)*�R�R%;(Jd*�R	!)UR5U) R

�S�d�
*!-#�h%5�
%�
+k	#� U�S!	� R(�
	�-Rd�*
 �
-h)�'�U1�*k#
-�h�)�)R�21�U�k!(5;S' kSR�%%'1�!2'�2�1
-%�dk%
(d;�
-RRR
k 5%(
�!?R(	d1%h1�!U	Sdk1�	1�(+5��h*2?!�#�(S�;5%;*�dR;
�(UR? � U;d�-+�d;(1U+�
�U Sk� �5d*
( 
 �!  U�d!�R;�;�U	U
	�k�%'%12 �1�!d�k�U
1 *
?	�*	)2�** ?%*
-�
k-
�J 	�k	dU2U �U) (5k(�2	)�#*
-�
)1�
2	�#?�')�	�#)�?d�-
� !+
-!	�2%�1J�h(*�	�d
( �?5-?*(�*;U!�	*�1*U*5
��5R�%R� �dSk�;��d(#
)%;!�d1(%�%dJ;
d(!-�?�%	%! US#�
dS,;�;(2d5?�
�%
-�
*;U
S�;#;U�SUh*!�(�;(
!�- ZSRSR	2
nU)
-S�#�R	hZ
;2S�k'R'*�;�5J?%	+�!%51;1�d�!1
���U( (�S#-Zd �U�#�(�)�5'
?;kd��
#
#1�kS	U�+!�!Sk�	
S	!UZ
�
-%S	 Z!�5 #;
)S%
n	)�;(
5)*U;
R+�##?J
#n�21R�??�-)�
(�J5 ;�1(hJ�R�U-�RJ�2
JRk�Z;%�5��%��h?J;!)#J*-d(	d1!;1!�
	+n��5?�15	5��5
;!!R?�!2`-;;�Uh�!dk(	#Rk !J�
(
U%�)%d5?U�*�#5�#)	� 2U+�(S�k��)�%k�)	2��+�dk;)�!��h?)R'�SUR�
-%2
R(�
	�R1�k�5	�R!		;�S
-�-�(�2�5*S#h�-	
	(���S	�JU "-�UR!#�)dU�1+ `1?
dR!� !*�#n 
�*�-�
# !	 �)k S?S�*
5(��2�%!5(�?�%S!!;Z;(hn%+#

Ud!%
%
 (k
*���'�5�2(5�#�5')dU(*	�-) �?�d�+)	-	%�
% *�1Rd*5hS 
 -
�5�k(�L
�L� �cP3���ce�Cd��o �-��c��i�	 Ov
�z�6�@�%
�Ym��}$n`���2 ��@����c��w�Q$@dw ����&���`�|
��,�f|}�t@
���� � !@ף"`#D#��'$`2%�$D&�л'�c& �n(��)��Y*� +/|,�-$.�(u/��0@��1��"2�?�3��S4��{5}@�%6+!7@�c8� 9�ce:��z;�)<`��=@�k>`�i?�Cd�	�@��A��gB��iC�Q
D��}E��{F ��G �H`#DI���J��gK`��L`'gM�Y�"�<I���N@-gO�I�7	 P`>Q�,!R��?S X10�ZmT��iU��iV���W@�bX@�{T��?Y`EdZ��i[ 1D\?�A�u$N��?]�.^@�Y_�$D` ��W�Kma@��b�c��id 1De��hf�|�g��h� ]`q1i@�S�v�j�>DA ��k�e#l@:�m`Edn�m*o@��p �cM@�Fq	2r��is@detfeu@��vwDw�
�x�)uq�m8y �{z`:�{��)|��W@�m}�?:W`�~�V~}�	fA��� z�I ����`�%�`C��`>D��kd�| ����h@ ������@�^��j�@��A��g� �i� �݊`
 R�X"�`8�ˍ@�M�Fd��>DIQ�1`��A���>`���@��.��'�Ţ@�g� a�7��`
 ��=�@�[��q� �i� �-;���@ !�`�����c�@P�`���`
 �@4Zg�	�I�@d��
f��)gM௘�`��R@G���?D��k������Fҥ��z�`q1��%D�`��|�7�`�cf@�e�@��|�ڀ��?D��)J@飬�ۭ@�/H�m*f`
2o��g�	���Km�` �@�a]`\m�Fd� Gd�`8�@Om��&Do@+!��Q$�`X���g����i�`�J@�9���%�fe� ����c�������c� j���f7��/I��v�R���yW����i���(���%ƀ�c��'DX�f� Gd� �{	�}�` �@}~�`X�̀z����{a��-Ϡ��� ��� ��@ � z�` ����O+ր�mנ9�ؠzـ����hۀ���  �~�+g���@�>€�T���/gM`}��※c�@�j��Ad���{��u����'���{�z�@+!�֢.��젼��'� �{e��g�Bd�@+!I�8�� ��y�7�b~�@�c�Bv��zd ��f��?�� �� |��``��he�@+!�� �g#�@ ��Bd�@�E�`X����'��Hd��$�@�9�]��iad{AMm����9��g1���%!��C@n��iH`WdA�?	��i
f�Bd3 ۣ���v�'
��z�)n�
2�Ad���Hd��{�f�Ad��i��h���&aۣ!�7
f�)|f�m8!����� �i!a�?"�M9X�Cv#!�{$)D�	�%�B�&���'��{(�i)!Ad*ᥘ+A�,0)D,�)|-���@!� F_�)nq@@�7�-!�ˣ.��� /!�F@�-0��'1� 2A�-3���4a��5A6|��iI��m6��kA$u7�Cv8AfA���*�{9�á%��i�:�3l;a*D<�{=��i��yN �e��he>A$u?ADdA���@A$gAADvW�cN �eBaDDW�wC�$DAfE��iF�iAi1GaDDH��+I�cJA�K���v<L�I@�M�CdNAMmOA�zK��	P��{QaDDRA$ge`8DS�`e��^�T��iU�m8V!�x࠮�v@��W�8�;`��H`*D�x�X���Ya�ԉ��Z� [��bf 8�P���\�)|f`V����]� ^��b_�ڣ �"`*D`�Kda�-b!��	�c��y��m8g� dADvea�i� �!f�Kd��N�ga�i�്ha��iaDDJ�,j�?k!�elA�@�h��Kvm`#nA�'o��/ �p��{;@6nT@DvqafrADvs� t��ag��jua�{vA
�@۴
ADdw��kx� yAfzA�)�&{"D|��i�@f}��i~��a�`8D��i%A�������ァg��!ۄ� o�@"��Kd7�/!���b�aۈ��{�&(&���{���a�ax1�!�oa�y���a���a���z�!�-�� C�x�	`�iO�lv��y������J��ݓ�ƥ7K"���{7@�'��c]`�`��F"��fR �z!ww���yR@X$��Zd2�Kd��xӚa���!u\&�X$�A5S��kg�ȝA��f`�h�a!D�A���aՀX��A%& ����ۣ�be���b�A��-n���§�Kd�
}VA�a�ΪFd�!۬�q�H�<���{P��
o� 5u��&�!۰�j��ݲ-|�e:����v�d@y1�!۴��|`ـ�!:Di@�#�
}�a�'w@�<�a�{�A�y�!�c]"D�A�'�A��fd�A���b2Vm��[v�@+�`���
)�!��`��`�{��F�I`�u�A��� �e&�ڀ&�ڢv P��[v"|zA�aH�<�a���y��{� DR�Zv�@r8ǁ*A�!\v� D����ʁ�{g�{��&,�!������Ar8΁����b�X�v@�z��a�a�a�!����c��-n:�i��!\d̴֡P��8�!5u�!��!�ea�!!\dI�ف�aI�|�!\d]��{ۡzwO��-��:nݡ��!���m�A$|�!�=�Ame�ayq�a �-D&�u���]v��:n�A�"�� n�a�'C�
f�`�� :��A����v@�S졞?��}��id�	fl�f�qa�i�!��a5�W�d���-Da�h�oe��<D��Hd�A)!�A�a��:|��8��L]2!���*i8��{e�!�{Ġ�,W�!�y�!ne��:|��)g*�n��{e��:|��{W@oBoBZ���{�Id���Ϛ \vC�&`<'i��"�{b<n�i~�Id��a	���
B$|��z��H�/D@�-]��'�ze|��	
B�$����a�@���zwC�;|i�ۮ� (@�5�IvbX+�i�Id�M�d@��
��$B"DR@�a�;|]@"D�`��������"8!��'e��z��+N@�a�VBB�a�]di�IvJ��gJ��u�Idt��b� �4�b�o��a& Bfe� �/!"4S"]d C�VA�#�/DW`�bV!�w�a|e:Ap���$B �
f%� SaX9&"�L.@X9'��(�>D)"�{*��+��/P�E� C�T`|wR�{e,�"|V!��`�-��.��zH@�//�"n0��n1†$2B?�3�P�s�耠c4��'5�;|6�h7�o8†$9�}�� :���$�BmX�'P�U�;�;|��fC��ޖ���<�b=�<�>b�a�A�+?B�cJ`\$@��ig�&A�-(�+N@�aBBFa��ڷC��uD��`|w7@":%���E©jF�zGbf
�H�>|7�'DI{�W`��6SJ"��o�9��6�KB��L� M�6�N��{O��aP��Q���&@ˢy��aR��cS��{T��D� S�Ub��V��a�`��WB�- C�X� Y�8gZ��'[�V�\�f]�5^B�c��JC�#_�'`Bˢa��9!€�d zq��iN_�VA�.B�0 Z9��b1��D%@VmbB�c9!dB�ie�:uf"q1<$ng�f�`5uh"�e@���)g
�i�>Djb#Dkb>|A��`lm~m�$n�:go"�VpB{�<�vd��cqb�h�A�brb�av�W��>Di#s?���<t"�aW�$:���/!�i���ۀk�u��{`�?� �v�E�R �b	��1�H�;�Fdw��x��{y�eD#�#z�:u7��b{�$|*#}B�(~��i���
�1�崀�A�g�:u��8'w���i��;uI �߃�>D���iR@�m������{�A&���[mV@&����{���i�Bf��)|�B%�J�Ad��)|��v;��m8��c�BMdA����*�b��~1�b��;���	�{~�"ʢ�`<.J�����f��<����/���x�B�i����b��A��b@�w���/��)|R����?D���i� ��Y�]�#���k��?D��xӠ�c��c��M"�?y`����1�b�܉`�ݥb\m�B�#!M9��Bd�"&���{�B��"��	@{��Bf���yC�UB�B�կB[�B-u�=ui Am���{;���"&���i�Bˢ�BMd��)|�Bf`�-��g���y"�#&�Hi���b �B�'���"�-�"g�A�i���Au8v ����i��)|Ѡ�'�"<g@9!��ie��@d��b�)|��;u�@����&DN`�g���a�� ���+]�-��,�=g@9!�W�	=u�B�{��Odɂ��A���B¢�"�d���¢��B��΂��")��h�B@"W`�i�"�-�@(=f�g�"Nd�B�'�B����'Ԣ�%�"Fy �n`�+;@�{R`J
�`���‹bׂb~:�:��@��`oآʕق�'ڢ�%�B�?܂�i�b\mP��8:���o��|��7ޢ�k&��|�θ�b�W��w@��"�{�" �"�)�C�@{����%|����Dd��*����†Qт@"qA�k��%V!�x�BdJ��w��o ���B�kW`���"�{�=g��
��o��u�b��B\��/b �!���me�@d��i�b9��B�'�`��]�'Dg@7'�a5��b �B�x�B?z�)D���i�B{���b���!��z��Ymz�E��B-n
 �}�a>��f���'a�y��f�‚kJ�����!c�c�i��a��kc�-��
#�i\�E�A��$$u��/���c��1`���N_�}*	�b���eb�?3
�CdF�f�E>�@�%�)�C-|
C�?��k{;!C-|�kC*E��V�bC-|��aV`�
���y:�$|3��c�Cj?aµeC�?C���:g�B�-�E>I ���@-|�f�[m�a@z��{�&��y˂9�-D����i øDŽ���!c*D"��#�jF`�-$��co��?%c<u&�!f`�b� �e<�''�yA�E>(#ʢ�me)cf*��?+��-,�Xd-cDD`�(;�`_9� Z����{1�e.�f8/�{Oa*Dz�-n0cf1C�_V!�-��e2C D3Ci1|���AUTI��4�k}�fi��{_��$n`�-5CDdI����6cf���$ zC@�7c�b�@��8���7�CvvX�9#�y:�x�;�f<��b=�w� �b>�w�#B9D?�9�@��{y�A�CvB�)nC�Q�DC=gX �y:�1�W��w��i���EC D���F�T@DdFd,@�bH@@9�yBGCfH��{]�/:HcfIcDDJ�kK�{LCfѢ�� @oe{ ��MC&��ǚ@fM`*DNc<uOC��PC=gQC@+RB9ScfT��`EdU
�V��cW��'XC@9Y#��*A	 ZCDv[�̀��{W��b\��iC!�)]#f^�@mR�h_��`ef`��?��8ac�b
B9b���_@9D`��#B9D��ocC+DFB9��lq��c ���:'d#�a;@	 e��' �x�>`�{v����`�b���NfC��gC=uh#\)� iC=g.���]�1�x�j�zekcEd1�BhJ�,�@�`l�F"mC	2n��-oC	 pC9Dq�Zvr� s��'&��ct��c?gw`״�`%uu�{. �'v�51@+DwcEdH@9Dx�Zdy�zezcEd{C�?o�G�d�2��b|� N�9�}�>!��?������/*!��~C�?�����?
B9�c�k��9D
��i��2� ��*�%\��<�Ci8��o%�Cme��;|%A�W��a#z� ��ze���]�M+�ÉkqA�c��zeI-nJ �,f� ��c���C�`�Éy��-D��?�C���c�A��-D�c-:��ۓ�-�A�?��{;��ᕃId�;|�����,D�Ci8�C��"@i*	 �ᚣ,DI��ߛ� �`�k�`�{�C2D��ܝ���J�J.�c#���)u�#��:�0Ѡ�ba ��-|b�ۢ�ze<�i~��Zv��}S@�����]`Nme�zw��y� £g�í.���� |R��H�{!f�zw���O� D�� n�c � UdB��a\�۩� |7@)�C9��'f)bNm��>N���N@�(�Ci8A`.|�}e�#ʢ���i"`�c��D9�#\d|�䁳۴}e
���zA�)��@m�c�y��8�H�<D�COm
�@m�c ͡�y�����v�@m���{*���#ɽ�-�`�ξ�Hd��_9�]d�C2�u�£�kU �i�C�	:�`e�� |ă@mŃ�%��c�#\d�c�.���`�� �b!��a��)z���#uCN�	�) ���C���c ͣsc����}e�}e��>҃�%����`V9�#!D.`�<&@� ���c��K@�y@���CFپ��%փ |ף�yO�Fh:�@9y@��؃jB�t��a�C�i�#�bۣ�ܣ�{ݣ���E��CB9��ߣsc�c.|��a�C�i�C"D]�/D����.D�F9o��A@
f� ۯ�C������c;�Hd�#�{�c^d�cEmT@�i=����c�?��cw@��J����F.�sc���|`�^��<D�#�{�f����C�i��<�b�]d�B��-gdc�c����Om�C
f��a7@�b�CI"y�.;	2���ρ�%|��?��f��5�
B�$eB"D�C�kW�o1���k���y�C�%��!n�@[�I����>D�&DDm~D"D� ��n`�c!�]�$�)u]��$G�t�ʠ�i��"|��?DT	2�!n$��N�	������f	2�@_dO��>	�G+
��L��A�i
�G+�!�#I���$��m8��D�i$C�&�!:��D�'��`Dm~��id#DċyD�'d^d��#|�рM";� ��F�!���-|ĩc��$ ���YvĻc�beDm $|f�	fq2D!d��"����)#�&�@�h$$Gm%ċkO!�&ċy'$Gm(�f�b#D%��?_`#D��#|>�Wdg	 "`#DUA�k��-)$Gm*ĩc��D9+d�h�Xg2D���F��J��{��o�B�aq��,��--��.���_�@v/�Cm0Dm~��<�`f��/1�L�;�4	 �H`#D��imA�&� ��2D�c3df4�`~�A�g���hˠ�ca۽��'5Dy86DI97��j���b8��-9�	:df���-;��-<Ļc*a��=��?>DI9?d�-@�?D�`�?A��iB�G9C�`~D��{
��k��E$EmT@I9Fj!G��+� HDI9Ir�V!`F��� ��Y�`~�`�<�k�a�-J�#nW��-�Ea`��K��i:����LDvCMf�o ��< �}���N�{���R��'��7�ie�!�rB`8O��iP��iQD�z=��I�[dd���>��?Rd��<�[dS��a�@`8T��U���V�[d*ᾘW���X�4tY$�Z��{[D�a`�f\�T����]DFm^$�ܱ��y_d�y��Ad`�n8a�tBb�k��e�c��?dD�ae�Jm	 ��R��f$�g�f1h��{i$Nvq`�jD&|K��o%�) ��T�ykD�zl�4S;��m� V�{n$�io�İp��q��:�+:��@��rD-usd�t$g�
"fu�Jm ��M �?{`��v�&DJ�z:�7�w��	x$��yDFmzD)D{��i%���|d��}��{��F�~��id�{��y���k��%D��ko��ii@ۃ$�-��yi�	ۅ$����n:a)��[d���a�@�$W�F+��-��F9���Ռ�[dy`��]`'n��M"����I��g��ay���C��aK@�	�ď-S!�i��#g���(	=u"éx��	��iW�F9�d�y�a�	�dF�ď?�d���d��d'|y����$����'D$"nv w��v���{��{���b"�}*���i�@�$���{���<�d
���
ۡ��ߢ� ��xӤ��a| ���D�k|��3�ӣW`��")�$�'{��-����Y"�G9S��o�
f�Ćwt
f���0�D�`���ޭ����x�d�Bd��_�$�-��摱D�k�Vm��i]��'��{|���|`��dGm�۳�Km�
f��'e�L9A�� �&B$��'���aP�qf�D�i`���d�>:!�@i�f\`)|H��i�@�x��f. ^"�I9"`G��!(�+��bf��'b3hJ �ܻ��a�)D\����iN��I�)D�Ą-��a��{e��'D��_f��R�x����P�c~I��hO�(n�@��d�?��-��Q*I9�):�Ą?I��4�$�{`����i��;n��#g] )�p�[�5��D���A�T�'D�$�z7 D
�\d	�q>"�*��;nz!!D�D��:!�]�$�����d^d�`e~���i��o΄�$�����yϤ(n�d:Di �k��q>��-���䀹���q>�d'#��i� 3���c�$�����`W��`�i�D"D��ie�d�%�D"D܄��;��b�@���Ą-�Dfߤ*����)|N�����i�d*D]�-��c���i��{�t�d�+�D�+��c}`8DT@M��l�Df�d�c@�}��wca�o�D-|} !Dfī��$���m!�x��=�D"D�OI`�+��-�$��㠾+��>��{}\`\"�^m:a�|����Xm���z|`ڀ��o��-����{ �k�ۢ���)|�d*D��"|�$�-�Ĺ�D�{:�f�$Nm���'y�������i�O9��/�$Nm�����	��iׁ�'j!Ťx]�keC����-n���<���aE}e��{��'|ۀ$O��	�?�")
�Jmj�x�Ef�O9.A��BjT�@`8���i�@@d
�}y����x�}�#|e�0e�E�{� :DI`�z`)A����o`�$P`�{5��'E�'��iM��zF࿣�`*:�g!�`1A�U>� �#|�aJ��?�a���j���"|�
���iE�'��$D%:D����a���e�a�,Dz�:| ��N@t>!E�'�A�k*a��!�i"�}#E+D$�#nT$|%E�'€�I �&E�{q@+:l��'E�'W�o!Am(��-&�ƴ�>D{aUd>a3h)�k*��?:a��+E�{|�,I"W��a-�>D�[��@6�.���"��b/���c��a0�a�` �1�+|o �o2)!ߣ�3��x@�-4�#n5��'6�}*a��7�Am
���W��%���I8e����oB �a�*�T@�'5�#nc�^�9-|�`�V�,:���|���;���<����@��i��x7-n=�c>$|?%�@E�iI��AEmeB-|)��[y���C�%D��-DR��bD}E��i`}�@�i*�1F��$G%�{H��{ �,�IvI�M9K��K�_"`�{J����`K�
�L�$Dg��+@��[�Y��i ��K`�+M��$*�8XJ�S2�@6gN�����a4Mm�!�{O��メ�Pe.nw�`A ��Q�:|R%Zd#�M9M9�kSŏݢ@i8Tţd�@i81�fU�Ve\m@i8J`�dI����{�v�{�W�{K�@:�X�,� �ݐ�bY�`� UdZEd�[ʣ�A�f[�{�M+Ca���@��]��$\�%DJ`�&�0�]E�(^�%D@��_eu>`��aq��}(��aE	2�@�'�$�ib�{
��*Jad����Jad��!8)c�-D�@	2=�-d��
�-De��f�'��@4ÏbEm�{I��g�֢|��O�7���.D;@	 h��k��XdieEmN��gj�k��'N�{=B�hl�&D�`�am��a�B��N@}��in�۞��o�AdpEd~�a\m!�bb'n}`Nmqe)C /�r��cR`Fs�+��	ftEd~�^"�#�n�@�ue��R��� dEmv��k#�/D��fw�'Dx�ͣy�z��c�&:I�x}}�=D&`�d.aF���'0��c� �{e�-&|�<D)��$�?��}�{&��~��'o��c�/р��cv`�c�@	 
A�}S����@
۰�F9�!\S�b�%���@�� @�?|���e�}�%�Qh0�����E�}�%�'�%�{#�/D�E{�]�f*��ۉE�ai`X��eH�	)���c�eEm�	fJ��kn��ƣ�E	 ��(|q Úf8�E۴���aJ��ݑE�k��-�B�;��&��>D��x>���a���k	`hk�Em~��a: X�:A?�}��$&`Y�%�>@��z�Nd�)D�@�Ɨ�	f�f���_�>D�e1D��x,�e���'Dz���7�Cv��)|�d�-+�Yd�E�a����Mv���i��Av�A?�*��>�E�->��ܤE:�<��遣����{�A���m8W@�-�%���1f�\�I�۾{D2D`�?��fW��o�y>�@ۦ��-�%=
�=\��ݫ�Yd7�M"��)|�Z$`�@�c�Ţx�2D;`)h�
�A �?�@�$yO�i�I9�eDD��>�řy
 �y��y �?�eDD��)|WaDDA�>D�%Um�a���jH)D��m8z�)|:���E�,���yNX�wB�,< �oo  �E�-�`۹��'�eۘ��w"�>�-u�E�e���U�)|�E�?�E�$f`2e �<�V�Sa I�`e��Zv��@d���e�e��Å	}ąYdť����Zv�।�eDDI@I����y��2ɅYd{ ���řy�%����.v 4�J�����y>;���e`�}��k�% o~����Ń�ҥ�y#b*D)�(<`0��j!��x��;����%�-‏?/�Yօ�-�%�?؅�yf2W��-��j�eDD���q@���%���?�E
f�}~=����%3D�"�1�;gx��P-!|~I ��E�>
����*|�%J�@6|5�f�:��@ۍ��kn`��ׂF������'ㅛy��a�Av�eC�
�f! �&���
A�}{@?�ad�v`�S&���C�v�g��?�@�� ��
��e|>� �`祣q�E��酂y€�y�+�Xm�e�kf��x�+|�Ei8o����F�v�׋��k�%�A�% f �-��}w�`�e�y�% ����}%��xӥ��{��-�����f��,Dn�$�AF9�Y9:!�h:E۸�
}�� ��
2
��H��2w У�!��w����O!��e�k��&� ����i��+|&�-f�y���P`���+|���f��af�kƦ>���|��	�����}�B�y	�}���}
fUm��+���-D�[d��
���� j�
}�z��Y�Ʀ>��G�&��F�yu �椬��?��{3��)|@�	� ��oXb�yd����dFg&X9f�;��i��{�@�'}�Cv�V���2��@�}�-D�*��x�d����%X� � !���"ff�$�y#f��x���`F@�%� ��@��$�j?Uet%�Ym�Dd�ࢢ&��+:�0�\���'��FO���(F�')f.n�@��*�T]+�@
}���M �$,�"|��=:�.-��a.F��"`.n/F�k0�-�B�jv��&�
o1
}2�}3F�7T�Km��W�ݠG�4�.Do���]@�$�dUm5f.n6f)\�?�7�Ymt!�a8��jz�<�f�,�`�9��zA�%A@�x€ߣi@_��"|:&�IF����-;��;���<FMm=�.D>�E��@�.�իlCI"�%Wm?�	f@��``.n뀕�A��b�VmH�,}�?n�Y��E��Bf.|��}CDd*�,D��`���lE�.Df�\�qAMm���bF�4�|`��G�M9i Ev��Iv�!F�H�0��i]�̣o �-o`��I�/DJFMmOCF���'q@?�K&�L�\�<�MF7�´N�Y�OF�$P��yQ� YC?�R�[mS��x1�/DT��7@�zU�i|�́;� �-Ń <@$|V�f
!�}1������0�@���:A�}WƊ)���X�.DY��j�[�Z�'�����V��fR��[�iS�{\fX9]��N�7�^ƀb& ´R���{O�}_F/| WmV�XJk�`I��}����`�i�!Ѵa&��b&��cFFdd�2�@�'����e��bJ��af&\P��*f��� �ˮ�
�� g�XmV!Evv��ah&�yW��xi&�ij��ck&�yn�fg
fM���I�E���o|��� ��� �%J@�olF�`C�$m��bn��aof��C���!��&�\�p� \�\��@��< �o6��ad@�<�iq�[mr��{s��|�ց:�}�'t&��uF�bvF�w�i����b@�`x�E��#�y#�]9y��%�fU��z��a{Fj!|&�o}F`8~F*w�ʣF`*["��/ \T��ba :ʀ
2��	f�)�ƿ%�F`*�&�a� �1��	fO��S€]�."��K��F9D�F�܇f1D���a�d��M�8�c2DW@Y9�ʣ� D�c�&Z9�&�y��Cm��eO�����i&��c����0�]D�@�8�@�j
@�xi DF �i�2D��b~݀M�X �i�F�c��&�$�y�F��F"��h���/��{��f^m����@�{7�'`)z��&D!�%�&:D�`�&��_#
 ��yࠁ"�`A9_��/�&�a� � ��;`��R��i ��A�g��c���'�&Z9W@�}��i&��.f���f��}�^9�Fã �ܟ��&�Y����g� ;�%`�-D_m��x�F_��Zm�������{�Ƣcw"�o|@���Ym��dh��'] :��dA�b)��I���)�e$�M+�ۧ&:D�f���M"��)�W
�F����&�a�F:��x�#*A��{e�Ff̈́[�F_m]��aC�<�'�?DW@&��汆�!�}�F�b�_n�x�`��Zm��b~��5��A;��`*A�>za�%��۶��/��+y��-���iP@�¸��/��0�&�-%Ģc�@�j��Zm��f�P�
��@Aa�{�A�ż��/��׽�j"@`#@�8h �;D`\��j��b��jK����AMv�7��I9&��+�5h�FFm/�
�CA����>|7@`1��2q�`���B����`e�����I+:AY*��i'b\I��"�Xv�f\�� _ )
�5z�f<n4�"|�I���<DɆ;|ʆ�$��-��`e��-��&ͦE���-7c<ng��+���Ϧ���-�L���a�f5nӆ�>�f\m�ę���_9���$֦�
�f8@]��c!ɣ�F=n�`W@;���	`)h�@$>����b|��7!᫘ �>} �/I�I��<�q�`	�"1ę��fEm�f\m�㥘�&)� �-� ���F���' �>I@�u%@=nf@�������{
��<�A��O%��f\�fִ��{�f\m�C���`v��$P@�P�t�bAd>��'o�)|�&>|�?.�\��F]m�f�a�)|� W}�F�i���ۀ��C�t(`��W@�&J?��&^m���n�F�c�&���M+���8R�S���}���q��=΀be�cg�f�a7�^9�F]{����f���}7 �>]�f8�F=nz��`�.{��f�iS���!^m���i�� ���F]m�@���yW��iVa�g���{���W�F�'	@Hu�~f��}q�$:(��%�F�{�������%����v@�v���i���� GmM��x}��'�¢jF@�x� �V0�c��F9J`���a�o���P������W��}z�'q@�}�‚k���f�_mw"�}P��8�i~� MvG�$��Kd�
۰��9�	q��{qa�bǛkW`Md{_m�;uG�%�_9���R ���f8G�c	�ceI@?�
�״��o��jg��I`��zM`�?��<�G�'��{f�>��y%�`
�%D�C�
�G�$B`ۯS�%Dg@���cw�m1�"X��r���cG�-q��}��i�&J�4Z�ce>+D솪�T`8D����
oǩcB`ۮ�&gDD��i��?Oaۯ�Q+��c��{��a���C��>�"�y��O��i�@"'4@�x%AL;y@L�`�c�i8Sanez!)� ��"`�cG�% �yU��o!G�$"G�x#ǩc�Dde�B���E�xy ��� X�$G�$%��a&��c�A��'G-n(GD$)g8D�!�aq��bR�fl��j%���o F�*��a�`)ߥ�oI��n+�ce �{~,��--�&D`)@K+.��{/��-0G-|1��`J��2��i3K�4��z5'�y6��7�@d��,���|8G�J@���G�g�' �dF �i�!Hd9'�`��:'�;�Ad<��c=G�>>��aA-n��'?g��@�AdA�-D�A�����/ =|��&B� �8e@{�"@I9C�-DG9DEfe"@i*e�i~FGDv��/G�ae���-¤��K� �Y�Z�Hg�pd��i��'A ��I��-7�i�f;�&�$u@�`	�2���T�Ad����g��J��%ᦑK�&DL���MG�a����% N���fez�:n�K�I�JmO�.D�@�zg@��P�:|QG-|h@i8� ��@�)�fe��%zf�q�{R��}S��c0�8�@��1f�$��T':DU)D<��|��oP���Vg�%&��4W�ۜA�ߟᬘ���,���oX���ހ�Y;DZ��% �� :D�����Y�i`�/b�fe[�9�\�'D" gwg):]�Imf���{A�$^�Bd5�Av���W�.D_�	}`�'N���a�Ad0��bj�cg�d�g1��@9o@ۘ` `A>W��e'�?�@�}��c�N�;@:p%�/Dՠ�f'c�*A��q!�-I�`��g�Cvh�g#iGC�!A�aW@�i�C�ajg~~;@hkG�cN��i�}ߥ�}$�>|I��'l�Cdm'�in��k���iog�%p�)|qG�%�@޴1��vל��ۉ�ۉ�\�r�c2\ ^"��i�c	 �=�g#s�@mt�F�u�cwv��'& ѻ���}wG�cx��%�`{�d���&��'����y)Dq��o| �`���@?�zG�ig@��{'�i� \|�j}ǀbOA�cf�_�~'�-/DK��_�a ��-|���o�f*D@�$��c�g�-��)ng@��gT;���z��)|�`���aͥ�}\�\�C@e=�݆�	;۠���he�G��g<n���?�a��'���ɋ�;|��ceb{�"�<D� Ev�ǭ%€���ǭ%de��`I�*|���G���i��ze��-�dw`�i���h��U2z�Ě�Gi#ޡ`3��{f�����'�i��CdR�if�媌�ۖ�Cv(��g*D:����>�G�ܚG���)bC��g<|"�<DWAۜG�k ��y�G�< )a# ���i���܇A���Ej�b F_��y�>z�G�>�G�$��b��I$��ܤ`���'�Afe����I@���a�cR`�o����GFd�# w"���t����"`�c��	���@':��}zগ��<D�'�-Y`{��GFv�dž���b�G2D�(u���}��i��F@޴|`�����q@+:��a��SIB}:��}� ee�'\mn��@U;J���_fƢ�;��`C� ��)!�G2D�@��Y���fd_;@�C�!�+b�g���c�GFd#����bz����Am] �yS@zԳ�+v@z���NҵG�h�G�"A�z
�m*�`�}g�J$�@�{F�M9�	f��m8��f��a�g����!�>D�Y��gge��	�Cx��Y��ke�'>|]�-:�F����ag�I�1��-|��a�G�?F ����bW�?D
A�g�'g��H��G D�GFvo`�xćke�gf��o�gUd���`LJf�'��	f>@�h�G�'ʇf{�'�a̧�iU�?Dg��a��g#:!�a���/1 ��� ��·-D�'�a���Ч�b:@�h!A��J�E�%]m:���b~ѧ�i�'�a⠣c�<��g�'P�v�ԧ�b�"X�Շ@d�-���ׇ@v�G'�`��B�z��W�b~�g���Y��' P�he�d�+A��g�'2�d~���o݇"|ާheA��g @�'%�>�A���`f�fߧ�-d`��:��<`��+aGd�g���	fᧃb`�:!4�J�h`�@d�G�'�"|�he�I"qb�|�`Gd姺j��a���xg�j<@Mv�G�>���?aUd���q��'ЀhN�g#��o|`�^��*��a��Hd�G�c�Hd���{́�'���`��j*��a
�#z��>���a� ��k�}��0��Hؾ�<��}���+g�������2�'�f�U��'��"��-	�"��_9��@d�'�a�&2�#�����F' �'���ۻ ne�ge�P@6�� \vJAZ��=�Y�����ljy1@��)��%�&ne�'�-�g�A|@р&@̣\�f�Av�g��D$n��k��i�ljy�g�{�@Fm�[v�xU���Hv��D�R��1��i���a%AO�|`��VzA)|H�$Ḥ����aHdh��"HFm��I��a*!X��i�C�`Z�q@��_�j	��
��a��
�$D��a�Bvd1�bE���a5@$n
�AdK��+�@�a�
fe �>H�F �`1@��!!�I@��(�b!�b;@z

fH	2=�<�oeȉk!����I�h�oeq���_9�H�H�`�Ad;�Xv��9�f�Q"H�a!۞
�ŀb�i�
�#�"���JdI�46f��|����p��n�W �i�Y�e��{�E���	f�Kd�ke-u�Cd�6T����ʦ� :�%Dy�N�I��/ � i`�!�Cd<�b"HF�*A��G`�^���#��d%��a��'&`�+@��g�ަ�@�wK ��$��$uJ�'��N�*�毉�\�%�3S��Fm&Ț����`Gm��Xd'�f��>��G�
�<�9��3��o�Kv��CvA�u$(H�i%@	2 ��R�@+	  z�@�)��)�b���/���G*�G�:�P�UC۽+(�6,�Cv-(�c�"�c�Ak�} :DF�a.�b���{/(4So@�hW �i0-uf�21�-��'h2�?�!Ed%�H�3�-� UmI�ۆG�a���m��H�_��>4���5�Y��$6�b��ke� Ed�B�7�Cd&���^�8�Cv�ù��q�9(EdA�u${@�ȃ��a�-!����:2D;��(<h ;��螧�=h�iJ�'#B�$]�M"���L�-���>��qa�{?h��@�YdA(W�B(DDCH� �,D(*A�`�u�A �^�E��`Fh�/GH�ye��gH(EdA`�c�¹�f2I�{e���i���JJH�bK�&DLH�%	�MȨd�bN@Fa�{NHIb����Ede7�%V��!��O�3��`�ifY��^�Ph�Q(�J���h@Fdd Um���/R�&d UmS��>WmwC�HmfNvT�-F�%_�m#UfV�^��->mw7 EvW��X�c�A��Y(EdZ��i%�cM`��V X��{0	@�-bb\v]��{[��i\��i٤�b]�&F�x���V�ۋ@Fv^h�s�i8_h�c`�J7 !D$ X����`( �R$d�2H�m#a�����"@`*��,b��{c� �@"��E{�dȻjeHFdǂ�'*��>f���_�m#@۩�Gm:�Ź7`#DgHOv�@۴h��iiHoe�[v|@�RH��i�R$�Q$
a�hjhNdk(�l�a�í>>H4'm(X�n���N���o(��FFdpḤ�D�2�3K�q�an�
fA༁�irH)D&CPs�f!�ڻ
�)f��tH��uh�x`��Ē�v�)|wḤ���^xh7�yH]dX�/�!X�W@OdVA_�ڧ�	]��'�@s����w[�zH D{�Ym��e#|hj�}��'� �~��-|���O��z��i���}��ۀ�)n��Qa�>� �C�<�d�/u��b~�f��
 �H D�Hʄ�a!A
f@ D �x���W�bbۅ�G"@���ۇh�iJ�ۈ�Hd��3'Z9��ۉ�aA�z>S��$f`�b��c�H
f C�&��Zm�D�+}`Gd��b~Az���i�)!0��i!�)|�aSb�HIb7@
f�H D��?]$|I�1Ѥ�p���Y�Ḅd@�zi������(0�ܔ��� nK��+�Ȧ,�`�ݖ�C����ioH��h�i&D�{�E™�Hv�a!D��ec��o2<�H D�h����J��}�)g�H�h������`;����H D�ȭ%7 ue��d�9�z��c��� �ܠH�id`����C(=�`�%����C��{z�$g����iƻcw��Ģ�.]��+�h!D�`f�!f�H�h���ܟ�<�i`�{��Ym͡*|�H�iI�oӦ�c�hf��<��A���f|F[����$W�۪�IdA@"gf D"�`���f2��Hd�H�i��c��У7��iP�j��H�&��L�����1�-��!n& �c�ȹ��@�}=)!�f��"I��.2<\�2�țb�D�{�!�b
�c�+D�hNmW��q��&���
 m8Va0h] �iTMm�Hs�|@ϙ��"|�!�i�@��A�,W�Id!�i%�P��h�o��b�(�b�鴽H�{� z�f�c]@�'�@gk3a8D�h�ig����{�D����<� \���C��%)���}��iI����Jd�ȍ$���c��,D��%�`�u&`�ܟf�Ē";��b:A��`we��>�(#Dƈ�5'¹c�(�i|��� �c�HK"{�O����7��c�Kd�H-n�%h�H-|�H]m: �c*�z̈8h1#W������`��Kd��[mz`��Ј8zE�H�$)`���H[�
`�d�H-n��=�H��] :D�A�$��Kd�hg��ȣ	@�z��,D���� \m��&�'�Ɖb7��/��Kd7$����Kv�h�{��&D�H ����bNJ��H��8z��JdJ�� x1��?Dߨ7��'���	��,D�ȯ�����a�ȉb�Ȇj���7�>g���H-|W�8h��H�V�'@��X$���(X-:A$|�hIb��<����A�a�Hd�g�i�!Y&�>�澘��i8�(�5g`'D`�գ㾘�H$|���h��?D툢c1@��h�i㾘�� 7�+�x�;a�J@�P@h��-D�@��):gy1��ϣ�h�X=b�d�.D��(ף�TJ��I
&��u}`ue��Kd��'�Ȃk�H	 ;@	 ��8z����;|,�O9�-uʂ�(���{�hUdma�-��;|V�5S�5�0eue��]���xӕ�S� ��;|��$���%D
a�cw��%����%W6zGu�� �I�a�)|7��'�GC� �P(�f`Ud���aɟ$i�c>C���;|���J��.�\@�BI�/�!�a᡺���/��a<�	f�]d]�M"	i�+
	�yV�f8*�T]I�,@�%����d�x�:��f��!��`:�j��-u��:���`��
�&D
A�,&@�u�Ad)�Q��kI��WMdwb��-h�iI��0��Y�`�?I���^�I������H�/}`*:�huUF`��G��U�&:�M"� Q�o`<n��'��;|�a�DW��UA=��b�@�VJA��	Σ)�
�Y�����6z:�ňd�	f��%Uew��%Dz!ww��(�<�'iNdU���J��j��Y��4��<D���EAzX@mea��x�@Ov#�>�'D>��| ��%!	F�")�c3��?	`�#)
 $�N�%��`�&�Ea\d~'���()Wd)�Cd*��c+�&D,�'D-Ime.�if6z� �/��a�`+zCA+D;`��{0o�"'��{e0�^d߂�x1��`P`w#�c��Sp���>���� �- �
f2��>3��d4	ף5)|eN@Dd}��>�=zq�[da���`5Ӻ`�6ɦlR@f7�<D��fx8���O��i9�8zA&���S	`ۋ�[$:iX"���?;����`7�<	6z
aң=��i>I�$?��i@IOdf��A	�aW`�=hB	6hCɂbDɀbE�(|�ȶF)'n� \$���x���G��i���i@�g�#� ��;��bHig�&@��Ii��aDD
��@/uJ��%�@E�KIi8L��iM	�-<��f�=zx��NI
f	��b"�S�('"�aO	h�f8-nP�Cd� �b|�Á�&���!�-���Q��a� �cq�m1W��R�  dEdP`�d�zF�{SI
fTiۜd�Ɣ&��U	jV�Y�d�)N`0z�Xd:Vp���&c�@ۘ� x���:���	f
 �b�?Dͥ�}�Q��b�%�b�(@��;`�[|�́!���d ��*AG�WiDD� W�X�Yd|��tYi�&�{р�*Z��x��f=�x�%@�cJ�8hN��>���| ߒ[��{\��{ta~e� ���`3�g���{{Ɣ_�@B�]�b@^��{�>D_	}eP���b�6l`I�(a	de�B�x���b)*DR�l_C�h��*|cI�b>@�d� e��iga
 n�(nf��g��g��b@@�ih��{iiңj	}ekIѴlI�i>��	�`�{c�D:A[J �a���ym	+Dnɹ�o)��<`	`J��+}@�j@?�p�ZdK`��q�tC��r��8� P����?��b"�}#�ze�+I�q�%���أBad���)D�`�zs	�`_�)t��Ou�
 ��+�+Hi*o@�iN��v�R���,]��>g���P��8+D�`��A༁w	JV��it�ze����Ydx�Zdg@��"�m*w@�A��P�GӉ6z��G�Ã'yɛk�#�%zi0z{I�j2�{����=�Nm7i*Ca�a� &`<@�c|�ۼ!F�J �x�`�VV!��_}I�b~� �B�g<�N%`�zezA`J�fci
 ���y�	�i) ���	�Y�� �	�+ �]�/D��c@�bff7ĦY����u���&�	f�@3�| ��i�'�	+D)�–!a|e& �A� �Ň	�i���i&	2*�zC��8{��c���a����	 ���b��>���F�b����a�i�`,`Xm%AO��	�`d�͌���{��x[bp�f��< �J �b|�􌉵5i �aA��p�+�y%A���a�j�i\d��i��`>���a�@]d�f#	 �{I`):��n��8��,DT`\v�II"eB-| %���&���am$ ��i~��Bv�������)�i���`���aX��/�) AI"��/D�p��)2z���{�	6z��ZE�) ��Ym�G��ɹ�!g��Od�i۠	�i�@�-�E�c�I���	���k!�IvP�G�:A�����!�i; Fai ��)�ܤɀy����	�bVd:a�.�`1D�I�b��y�I�(f\dX��`g�a>�	Vd���>d`\vN6g��o�I[����&��c�ɀywB[�����c�`>���ܰ	�-�Ba�a��g�P���@������.�Vv�ɹ��Hd���a¹�۪0Dv��.D�i�-�)�iq�j��&��;@�������,D�) N �(P ٹ��{�	
f㠹���.D�)�{�xӽII"�).|�-
��a{A�{�	m~�"�&��@mUc�!AI"�i����c�I�{|��	Va�z���%$`Udw��:a���I�/�	_d� Ga� p����`�)�b�G��	�-f�C���	�b��y.Ĺ���`�i�{�dZ��i�̉m8W`��cy�(�.R%.|��k@�|@��U�.��k�	9D��*RO"Wdd�݃!?��)?����i7 !D�ɻc���/���-&(���)��w"D$)@��Bo���a,$DD\�ە���I�i�I�i�I�a�c��׉8h�)�c;@��٩��m8�)�b��x�`�?��_"�)Wd���i
���
��j9)Wd@��݉[m��j

@":ofީ�K ߣ7�c��iR������IĀ���/�)�b�ɩxT��i}@"DW�8hbBd>���/䩽��)Wd������@���c�)�-X��k��Ǫ)�����`�@"D�/D��iW���\9I@�k�)4S��f� �b:��i�{g���@���f�B��	m~�I�/�Ʉ?���������Ʉ-��Y���b�i\m �����c�	�/�i�i Am�����lz�E�J�E�	[���/D���{�o�IMd���i�ɩx+a�k��f����)�cA���)1D�驉I@Mdy �-	``e"f��"|���&���/��`� 
�c*�c"@�$�5�a@۽
�aA��k*1D
 I�����6�Yd߃�'X@"D}��{��+
�c*�b*ˣW �{a�kn�M"i&�-
��Y]G�s‰b ���E�$f�E����i��{	�&�	6zb[�A[�`�y�@U��A�$
ʄ?O��	
�cf@Y""ӌjC����ʇ�
j�b
����H�J�
��&:�CmB�-�Yv�B����c�$|����
�+�c���;|��i�b���V[�q��ba�{��o� �ݮ�����]������/I@��D�`�_���9����Zv�`�a� ۾��{�a#DJ�LJ�-JMvJ�$%DXd��b�"�%CA�?�@i#��:���Zd���b*EmJ�{ �Zv�i!J�'"j�aS�Hd�@�j� tR��M�!�M"i �c#��b1���$�a~o�
f%j�?&J�'H�$�\�Ʉ['�Zv ��@�{(�+�)��{F��*���F�?��2�+�V!D$,��b-��{{�k�R�輻-.J�{Q��
�	}/ʫ*0�� NvK��1
$n2��?3j�a7@fw"[�!��%4jZ���^�5*[�6�kC�-T�IvB��87�Zd:A�a���aC�������ق[d8�m~9��-@�x;�x��"D:�k;JFm��y�%�{��@99&ۘ��'<J�`=�Zd��[v€��q«8�Ø?>J�ce��>���)��nL�Zd?���@����ƥ>��/@ʭ,A�`�@��� �i���&f`�A*ߣd�	}B��?:a\d8���C�f�a3DDj\dfH�ޡ�o �6I����`E��-F�kGJ51H
�`I
d~��i8Jj\vK��-L�{��[d́"|M�i4�Se��	��N�{t"5nma�`O�&D�H]d�C�XP��a�@�Q��{m����yRJ]dS���I��T�aU�Y�B��8Vj\dWʭ,��	}:�JdX
=zYj\dZ��?| �?[��a\J]vq«8]�8z�r8^J�>n@)|I�}K�_�$uf�$g,�b7 �>z�]�`��⢅?a�)�d �obJ]dc*�idJ�/ej\d]�'D��?Df
 g
�ah
�`5d8Dij\djJ�ck�"nl��-m�?Dnj
�0�9��Zo
�>�ַC� �,Ǡx�6S�#��p��-��>D|΁{���qJ]d�f��a0zr
 �_�aU)s�?DtJ$|7 ::F���uʦW]�2e`�I��-C��`+��x:��2*���v��-��yw*�-y �� ���x�?DK��%y*:Dzj�i]��`�!^v{��{
�a��P��/�@d|*^d�%�b}*?�� s.ۯ~���ʂb�F]d��D�J���Jme�d£��&�
�+�
)D�a����=z_d$oe��G��J]vv���`�q`�ʍ$��f�_v�J�a��?DU�=h�J	2n��$
����9�| �=<@�*B�8�J	2�g%!�������?���`d�b���J�{Bf��')��A�J@"�*^d�
Mv�
��:��
"$@�&e�k,�J	 ��'�"F���	f�@�i��F��_"��
ۗJ�i��z�J�x���'�
fӣ�
�-�*:D�j�
�iѣ�J���j���{��;n��'��'��g�j A��ܣ�?����J�ܦ�f}�f�ۧ�)|��M"�*8D́�%�`�%@�i5��k��*�
ȣ�J�,��'����J�i�`â�
Md!aie3��o��?� �jh�-�*8D�@���
}��-��f<"8:��ƴ��f���yHMd��?�$8D�G�$�bd��<=B�`�
�-��f��n�"�@�綊&D��� ۷�-��s�d�G���kobg!�Sɹ�&D�j�-<@ m��:a�J�i݅@m��`~Hi*d�-2��Va[����k���`"i*W��&���k�� :����8��ۈ��'�j��Hi*�4�*ەB�n��-Ū�i��-��-Ȋ��2���a��`ʪ�7�c`�ݻ�	f]��z��U2��?a���}��,W`V!8:�J�k�!���ΪAm�
�Y���~�J�i�@��*!��}�Cv�Cfmaf�ʟ$q��`q��g�D�V!��ʟ$�J�P@X�%@ �*�IՊ�k�#�i� �	n�-|"i*|@́f�?֪Cd���Am��8� �Dd��A%���j���ʩcيi#ڊBd�b����-��t�wb::ݪ 1�{�jDD��)q  ��o�J��*��|�J�,g ��
�{���ו�ۉ��-���?�*a8�i**��D]@�,����D
���*�{z�%"i*����
fY#?��>D�jDDꪎ?$��a�a%�L����&
A��D�Cdݡ��J�k�
}~�J۽�jg!@�>��(|t!�a��N����� �����:n}�a~( �5o�x�_G;D�$�i�jDD�j�-���
�{|��
�+��Bm��+�
-|![�P-n\�Cd���*��|��>D
�>@\�
-n�� ��%�
�
�F�㴕`���  d�)��8n�)K):���;	��;� o@?�q@�� �-:�fk�?��-I�݀D�?a壮�
 ;� kf@۽eFDd�J�>�-|K�>���+��-�!?��A
f	��hfE�?:���>�C�-� �W�]�f�>D`��*A�-q�O�`���	f�`�	�2
��>��K�i��?J���
��/K)f@^"�c~��/q <|F@��-|�9��;|,�2NK�
2�&�kJ ���-��-���?�Z�W���VA�8"$�c��`����-X�?+�;���?zf�l8}~C���7`
 �@):VA�8��/��5��-��xk{�|΁ �-�!��7�yK{�q�a K)D�b�!�<D"��?@v�[#�+�¹�:��(#���BOm$˙�J��kB�)|��%��&�)|'�C���*�;|��k(�)|���)�{~��;|*k�iI��+Dm�:�&��@��U�.�N@�xM��񕠺���Aj�����#��fT��a3éx,K�c�A��&�-+�f�BOm.�Y�|ీ/�h0kf����@[�%�$�'�-�Ad1kCJ2K�cQbF3��%%��&4K��@��f��b��8 �h�$�5�=Dc�6K�-�� 7K�k8��k���!n3��j]�d*9�F"��X:K�F�ܣ��ˀm8;K�-<�N�=�����d���:�v�>�i?�"n c�k�`�&g��{<��f@�ף���'��.A[�Bk�#C�!|ѠE�D�iE��io ��� C��I��F+�?W�;�B��G��iH�81@�����Ik�iVB�/J�'���@G9���{���+ɂ��O��KK�c��Lk�{�Av�e�8%@�k��q>ଣi@��|`��$`>|MkkFJ�q>V!��cb!��N+�i��}��	��m8 t�q�c�`�O��aP˭%Q��{2��V��a\�ݙR�N�S+#DTKI"h@�-U��ae�HdV�,DB�8R`�z�Id|�Ё% �iW��aXK�-YC��Y+ P�5m��%Cai@-|Z�{[˱�:��\�#n2&�j�'٣]Dv
`�`f@)|^K�b�IdI�-|
�}_K�b7�Cv|@��
 �@�b|��`ˏ-���' ��a+�{!�/�`GmN �"`n~�6uK���bK}c�"|�c��aC�dk�aW@��e�Ʒ��{f��{���ag�{>�Eoo��h
f?��DS@W1i�Idjk�a3���K`��
��m���	�!.|U�ik�Ή�cl��b�ju%]��i� �{@��m
fn� 
@M9oK-|z��DF `p Dq��{r��J �s�)t�	�@��uK-|`��� ��!�IvI�-|b�K��;��{v�@dw��bx+DD��=9ɏ?_��&d`Gm�@2�W�?D[C˴V!�)��:���y�Id����z�@dW��$N���{+!D���h��G9U�5_��c|K$|}��z~��iV�$D�$���8��Id͡Hm��$D��dV"!D:�u،�����b���i8>!DD��i��|��#u>�˻x�+.|�*!D���hY#?�
 6|���{�K�a�ⶣ���{�`��C�|��˿%�k��&�A���h�»x�k�/���a��*d�i~>��ݎ˻x_i*���?���{-@��q��|`ϳ�@�� �b��zC�IN�4S�$���d�9�kC٩a�����i���i��Hm�A"D���{���>|��q�E�>���ASb��@d���iI�Xb�BFdJ{���h�+�ifd磛�-�+�{�@����{b۞R$� ��f��x:@����aH����K���+€�+�i���nN@4@:At{������i�A�/���"��/Fʹ�k�?`	��g���A����8`!-%�Bv�`%|�F����i���8|`���KMv��"nc&|��/��A��{C�|��=���i�G�-��Ad�B�,���N�˿%Kଣ>��&`�+�KMvN�	��h��-;�z��$f@�,���b�k�b���{}��{���{�+�{;����+�{���O(�3�}��%D��JmkHv>��x�K�>�K�?Ĉ�{��i�+�{�k��$n�K�>�%;��΁�KMv�+�� �KMv�K"D�k��a�գq��y��'D{��a���K�>=�ŃBd5`Um��"|��b���jla�cv s�ŋ�A�k�b.�i���4�J����Yv�ˉkɋXb�K2D˫��*�� j�aV�z�$:̋�{m�&�@[���eA�Hd�&i�'���z�@��A ���'�%��b�4@�� @�{W��þ��x�W :3q�G����&�"��}H�,M`���+�?ݠ^�j�&�KU]���%7`�a��(n�KMd�=�FK2D��-	 hkԫCvo@�,&f˸�k�iU��Ά‚b�Yv֫��<���@ ��b�� X^��?&��nkۯ����k�����ڋ�'a���R�5�> ?��E����I2D�ˢc�+�-�������ت��M"t�-J�f]�?`��N`[��˥R�Zd�m8�+�ݮ@	)�`f ?��@���K�����K2D��)nw�壦���LI�i�˿,�k�k�K�{��}SJ̸R [�F��(���)n��	��{l��A�+�+� ��͡%D��~~w�ӌq���cC�i�x>�Fd��k��Id> ?�H��/��-�K�z��(n\�N$��T@�{I��k_m~B�E"�i�i�@{�@�2 
)��@R@�'�MmXM�K)Dg��镠��w�Դ�ˢcC��n������c���i�k�?�K�iR�vձ�?��D�`��>���|��y��k��^���b��z@w�n��i*f�7F  }`8Dd �o��`�+*Dрy>�`<�a�K�z�a�֟$�{@)D��f: �-��f<�aJ@�>c�m8:�XbX��=|�)�Հ��=R@B����L�kH�;g]��y��%D�aNmFd��9�R��z�C�'I@���o�}���/:�1�L�i��G"f�N$f�>�]m�Y�@�hf��>V!}���/̛k���6��A�iJ�^�eǟ$�A��`��@���@�;��,�{,�bi�'l&,*Do�壘����`ѠG"	,�y�@��
̭>���ܻ�f�ĭ>L��|Āf�\$@������h �y+D˫�0آ�:��*a�����ܰ���
��W&�`L�?`��(,�y�Ym��,|>�'D,�F��������{9h �y�F�L�x�D�,��k��θ�@�-с�ab"`�~xc��bo����?��$:ᄻ,Gd7 :D�S�f����J��,GdC�����-;�ux`�ϱ�y�ڀ�'H)D3c�,v <�LR",�{�E�x|��A�??��$� �Hv L�,��2:�Ĺ!�+|q�`"��zK#��h���`$lx*�`�-�`�-%�f� 8)&�,Dh �y'L
o!a�-(�f)L-|P���W`�-o�\$=­," �&@�Y&�\$h �yg@��*��-%@�,+��ܩ��{:�T9:��R,�'-��a*���.� |́S�c����/��a<�8�f���0�;|�@
}J6n)�+<��||��1�>v�h�Aʀͣ��2L-|�`8�dZ����-3,8Dq@
o>A�?i�I�A ��f��z�*~@�`��)n�A[��A{�A�ܕ`�����|4̆$5,�-	��R�[&�f�$������@��6)D7��a3`�;@A>�`�K�Aɣ8�Zm���
9쩪:�f��!kxV!`x�A�-%`*C�zJ��;�.D�K�a�Y9	ve��y<�3S`A�=L-n>�-.AU�DgP*���f{�?,.nR`�S@Lfߦ��A�hBl�-��kCL�aDLf�a�?EL���L蠨c0K"�2�@۽w`�+��*|F��-G̶�;�<�HL�b� �-C�(:���o@
`
fI��}�_"J�	K��aLl�M�'"`fN��ߣ����a�
�k��%�{��kO,`x��&�t��� P�{~Q�KdѠ�yR,8Dw��AFG�`�륩
�-S�/DCaf	 `xwb}��kT�z1@��RGd���(�9DI@��U�JdV�2	��hWL�b|f[�� �A���׀I��b(����`
�ǴX��kY��FZ,^m�a�v[l*D�@Av|������@�,V�7�\��x]��c^l\m3�Kd_9D`��jal�+*���bl�ycL�k�t�
ɴ|�ρ0`�dl|~e� f�j�h�a{�W�%f��a$��zgl͢+>|0� �a�6sW�.���z���.hl�`i/|I���R��'<`��j�Kdq�,:��>D
�z�@۽|��	� �{kl{����a�B�*5@�`Q�� �>lL�b�e�Q|��Lml�yn�Kd) �Y���iĀ\$�`5`�{�`�%A`�|C�|8F�>oLi8aE�y���[�i[����C���[p�,D| ���z��hq��.K��+�Fmr��{Հ-:$Ҁ�`��{s��ht��h�D�Y��E�a��.`�E;�	��zuL�yv�>AMd�#ۺV��B?�� j�aE�%)-:��wL�yv��`CdI@v�� 7�xL��y�&�AC�T@�kv�U&��+z�M"{�-]E��-]@�$|LAd
Aɣ�A��}-|���iW@i8~��{.A���	fC�;nS�}� ��|��?��i�D�x��i���+ {����'��&��a;��,ۃ�����C���&o Nvi��cq`Q�C�B|��?�,1D�L�a �a�l�o� 0��-q@��i``�x|@��%^�!?��L���L$g�,Nd%��A��LMd΀�i�LMvn`Cd���-�C��f�|��!Nd�,�a�l�%Ń	f��f���i���,> ?��=nρ�iU%��L D�@}~�����zFݣ�`��?VAf���i<����A�i���{���n���c���'�`Ԥ���zق�ca�aV!d>��?DwE�'K���t�S���Aș��$���d~" Em)�ȣ�j��eM���a��-�E2DS`����{���>��N�����{R _���`��x��RB�l
 �,Nv���$�Dm�,��ۣ�c>@��@W�b z� gk��c0��'R@c�n��,F��:�B�t(F�{!f���-�,�{C�Ad�A�
�%������?���ie`�.�>o��ܸ��ii�ᦺ �{� )�����W�Va hc�E�S��'��.D��?��"|���֬L�'4�c�:�`A�X9�l zoH�z���|�(&�� %�/D�L�'��2��c�L�'�����i۱���f׸�E���%]�f8@ ���i�@���ˣ!`��E� ��<��a�'g��"|�L�'�ˣ�ex��{�6z��Zd�۸l�a�`��I`۹�8G�"|�Lm~���a��z�a>�� G�fӽLm~|Ӂ��CAy8i�{��i&?�;6zwb�>�����L�>��<���=f �{���c���c͡ ��{q��b	��ig���A����7
f�,�{�,�i�B����x�Ŭ�bi���, N�})`ϣR ǬE��V~A��cV!`f?��L�	�@�A�:<*AuR�@G�:%��g# D�%�m8N ��Ɍ�b�A�k��"|S�2 �Z�̩x�K`�oˬ�a��m8��%DI�i&?�e�}o�m8��%�(���$�cό�c�lCP& 
�"�f*��a| ԁѬ�bҬ�c��iB�Fg@��Ń�i�)[�� :լ�b�ݣ��{DI�i��x�l�c،�i"�{&`?�4��b��S D	�%�ٌ}~���6zՠ�b��{J��"�de� :�`1D8'��L�,&��%���`Ud����]v�l�N�,{��L�>_i*� !D:�����ji�_��`�C���i8����f�L�a!��}�L)!@6��ң�L�aI��fY$���c4i8"�{���|0������̉y�G�
 �c7@)!R@�a]@)!��i$��k��E���}�A�`��8&�,3D����FG@"��t#�@��G��L�`�i8�D�a��{>�>W`�<@�� `IAf�A����&� 72D�{~�L$n��c@��M`�%W@�T��a�L�a���a�l�{<��n��b�,�af)��Ad&��"*�{~/j�%�L�'d�rb��b����'�̢c�D�z�,)�`�dA�>z��-���<�{~���lq� ��c�� �	�bf`�b��ۓh���I�L�''B)3_i*�`�!ȣ�S����F���̿>���<} �&&`��F"���#��h��>�>z��%D�&XUbC���{�,A�灀k��8�L�'W���E#!D7@	2f�\$€I9U�D��c��.�H�amC���{~�#�-{�[m����BdM�(�&:�Nm��aM}~�8�%��T@�%-Ud�%a� �L�xP��I��&Di��c��&� |1���2��x�:��4N�}!�[����f��'�c�#���"|M]mB�%	�"n
M}~%A���&͛k#��
�Cv�cA^9P��4�Y��`OB]m3`�py�.���(�
�a���im�i:�n��y��z�&%A��f�&M�zM}~y@����hm���`	}; x�P`�$J�&D�_"��J�L>
�&��i��%��Yd�`�A�b��&�"|���ag!-^mQh:::A���"D#�FW|`�<�
aU,Ud�Yd&���
�&* `W�`W2V![��$|�̦d m:D!mDD"��a]��h#M�%���DDd$�C�%
�a�@}~@A>���&m:D����@A>� �@�C'��`(
�&)mDD;@A>d Uvg��k*M�%Gt�+-5G,M�j-M}~C���Z��`g��`�_.͂y/��a���0M)D��%<@;�1�`<�$D2M;D3͂k���H4��%&�@)D5ͩc:���6
�&��bƅ
�G�I��7��z8M-g9�_"��926|��9����,:�%Dg`^�o�B�;M)D�Yd<m�cC�%C���ޱL�z)��Y=�8�@-u>-�m ��\��b?M�c@M;DAm�a2 ^mI"g�&��jB
�fCm�c|`��Ci�Dm�a7�m8R@?<z�>@`8�E;D�`�x����#�E��I���FmDDG
Fd��9�p�	��$DH��aIM��;��`q�%DJ�~8�a{� �h�c۷P�
�i��c�eޣK-[�Lm8D��
9)֣M�F� �`n��>@A>��$;� o`{�N
m~O�<D�X�*�;|7��+R�Zۭ��>|`��[�J:��^`� F&|!<|�@�(PM DDB�+��_QM;D�@�`R-�'�I"g@��:���̣�s�S
=n�۔��zJ��>T�`栈be���@-g�A��P ����@)DU�b~�����;|V��h��n�`�P���WA���=||��J�G�G9D(� =O��W��&���Xm�a7@]d-!Gd€YdY��4&#L�Z�f�iB��i[M D<��|�@��f��h��iS�$€Yv'�z����u0P���\-<|	��i]��'�ke^��z_-'���hW�%€YvN@�`"�$`��h��%��<�a�ad�Yv��(�f
;�@�=aM :b�N���h��`K�� :D��F�
@��#�$
A#zc��d��"�$em\vf�b~g��bh����o� ^dڈb~�ZҤ¿>P����"Gdi��&j
�%:���<�'��N�7 :D*�`5@�c�#�k-�b
�%v��� Gdd �-l
��m
�q�'DS-�bn�C���>a��o
&z�)�b���ip���Od�;�]":q
�arMd~_�h�aQ���DsmR|hH`�Mv7`�zt�HduM�vM���
)D'�+|0���b�H���X`�`7`�zw��c�@�n�!�ܨ�G�JE��€Yv@��J�L,q�k�"Dq��i;���xM-n:aH���hy��z���''I��B�oxz�)n��B�{�Zvz��,>�[���g\?�I���|M-n}��&~�>D��'f?����`Q���&�
Mv�M�{�I"x��JT�'�
j���d��F�K��%��bJf��͋b���̅M�%Ѡ�')`�x��׆M�&7�ܣ�d�UJ�b�-\v���"`�x�DjԉM-|�� _��&�m>|���݌��'��Iv�Âb���'C�m#���'W@ۏ�L�I��i-���G����)�-\vN�b�͋bW��'K �o�@ !R`�b��Hd���hS�>D�A-nC�T���-DGd�Y��X?��
/�Ǖ�B&b���} �$|ඁ�
�Q7�$D�e*DO![��M�ݦ�	�M-n�<���S	���m*D���f@��< +h�-�S*&�-����k�-D`*D�mg�A��@`����%���� Z�D�z �h���a���-�bW��|�`y~͡�&
�f�M�z�M ��bIL�D�
�ce�)|����M�{N��q�<D����.De��| ္��ca��b�
�>�
�,��?D6�e�\�-|��>�@���
�ޥ-�b���a��KdJ�(�*���ej��z1|��^:Aj�����M ��	���aN�>N@�`��i�M$n���j)@âf�L��M&�F��S�N%` � �ͮ�B���`�����ŌKd�-�&OB"�
�a:A&�`*D�A�������
�z|����0��@d��f��	��b�?��٣�
�a� �(|�遶�&D`�Ӹ
j"�f�m^vb�v{��$��Bd�
)�-�i���iYۮ�ۻ-�b���0)�-�b�
��| ȁ��AvǠ,D�B�'S�Ģ�.ϿM�'�
�c�$�x�_o@*zfr>��4��ej�
	 Cgו��w"0h�
�&I�~y��I HmB�zN@ �F�`�M�h��/z�!R�>ct�8it�M�`��>DW�
���Ad��b~�B�b��bȭ>Dɍ&D��E�g��˥��ʍBdo�G�&`�ma�c��Av��aՠ^�� �i���a7�f��%KR 0h͍-D��Av [�ύ?D`b�+\�G�����
j��-+h=b!� 0h{@�B�/hD&�&N 
;{�'D�-�i�&D�Ji8i��ԭCd;�'DJ�	-nxHg!{�'D,d~�M�b�H�>�ݰ�Mi8�M��c�f�!�&A�>��i~ٍ-D�Mi8[‡�o���M@��
̣"`�cʀf�`�,���hݍ	f���yZ���&D�`�b*a���mf�M)�M`8���;�ck2A$g�cH��&gd~f��]�>Df ۺ` n�lf�&�����tx;��d�!�'V��`���	� �c�8�O!g�R�}.�n>0@���Li83@��G�I���E>DB�NI�����f���:�G��@�X�-0z
�?D���Bi8ak5g 'D���&OEQҦ����`�'r��`zipxW�-h:A���-D��'D�a�bW�k歈bV�f筙�J@B��$�i�!Ei]�$��w�	��-Dat>]��$����})����M"�o��n���iN��F��N��M�`@��뭸��Gf�Õ4� i���.��M)D�'���}W&h���&W����aT`Ed來��&��XF���€�b{��a�� ��/D�vxf�o�i� /�w���
�k���^�@������b�,����+ �J����a~۠�D�j�T@�cw��g����!����)|a��x��X��a~~��j�mW�
̣�
Fd����P�
ل��`���&S�.hC��M�a���&1��,
��D�
u:��$�`7:�
�$����mEd������ {`ۨƦo|��cK�.D�B���
Fdn����i�C��vx���&@˻N�c�e�f@�o@B�'��d.�aC!��OhX��a�b&h�&5S�!����hnEdv ��0&h�dۈ@f�F 0h��ad`�i	�Bv
N����)w�ң�ҳ:�B�C`��@m~�`��~e�a|��	��>N��`��<�w�+|J�`
�Ad�`����&�e:����`o�G�
B�&�@�%�GFd�!���b�������b۔�b~��`�%��.Y#�a|���N����a�C�'�D�&�c~�'@����`ރ�ݻ��i�G"W�	�(�g��!�	���i�m8�$�a�(����aW��z���B��Oa�m{ ?���a��Ģ
A#z|���!�a��ܾņQg��%!���K�� |�O����m�m8I��Nm~��a
!֣VA1hV�u,�۷$GdP�C��o�R�c��>�bX�J�� .�C���!�aq��c".0h`X�w�̌���F��5#
f$.Gdq�'q��cd@
=F���A���%n1D&��'��&��&���} D��8�5h��� �&(�bq��a���������'JI"o�G�7 3D��bL��ΡG�2�G�|�����<�շ)� n:����$�:���_��&�)*�i~+�8��o,.!D�LՖŀ�5-nX�.΢x��b&	���\ �b��#�H)!/�!n0N-|]�ke& �D�me<�@ЧH)!e�'1.W�&��i��iW��2.DD ù�3'3D�E1zy���	��%?�q@d~��)3�-D��b4���e�F�ң5���o �e�h�@��}
��	��ARx�|b��_m��,6��aM���y���+.|��7�f�C���vx���i	&h8N�'9�;:�)󧦏%��y��|>ȟ�;N-|<N"D�E�=�&+me:��}�ǹ��IdDI}�f8>Κ��Űٯ�t?�)@N-|A��$��f*A.�b��z 4S�(�o�G�"��b�%+hB.�&C�Jd\��DN�a���i:�iE�c�@Fd�	)���6z�H�B��X�hq@}f�G�T��bC�a�@��F���b{�|����"�ۘ���G�"|W�Id\vx&�!nH�	`*�-�E�'�$I��a&�9�&� J�'B�f��EN�	:��KN�c��xx�	�x\�G�`{�L�b�Kv_&UdM��NN�O��Pέ%Q�]dR�c@8hSN�E�`w"�z_�}*> �bTn y�����KdU�bVΤjW R`��f�|>XNoem��bYN�<��#|Zλc�&[N"Do��A��ct�	\��]N}���F^�f8_λc`�����U�
�a�	}��ib�"nf��c��&��'�F��d�He�Kd_`#Df�#n�@�&{a�$؂��g�f���f+!��hN�{g��|����J��bi��&z���Ir>�'�cd���A����E��j�Kd���+'�ck���&�l�]d����m�ag��D �Dˀ�n�fom~��pN�b�E�`�`�q�$Dr�Kdp�%�^�g@��	 �|���`#Ds�fV!(gf��� �|gm~]f��tN�2�|>g��DW��,g@��]`�z{A�$m��b$@�bu. �'_�&XϤHMvvέ%Ѡ^�wN-gx�Id���0_�$DyNMda�[dF��iz�c�r,􇻣�e�b:���f��+� %n{NMd]��+���Sp(�S|.�a��dQ�
���
ᕟ�* V![���.z;g�{}N�zy q��DSF�%�A�� c�i�@�ѩ%D���D�� �`�%_;AMv~�{e�$:g��D�n{�od� ���
A¢���'C���W��DOe6>Oe6l���D�ۃ�f��c��(|���M"�n�>��;n��%DŨ%D�@I�o�L�F +z�%:��&���Sjʣ��
)W@��[�݉.+z���`�B�<X`�c�N�z:�eb���b�Nf�`�dP�H#��+Q�Xd.�h_��c:AwH�N�h��%7��b�`8D�y1c�&:-�xx-I�z�b��{@��n8Dy���=m�c���D&���{@�|B���N D��&D��f��u3�Od�.}S��κ@)���%@������M"o��$��;|�N9D���Ιb��ib&=����퇀R���;|%
ot�C|���D�&d/u�A[��n'n��&� �5�U�%�W�
}_��{tb {;a�c�+�AV>P �wb[�>��f 
ۦaʀ�B¢��>�.NvZN w@m�N7�|B�A���`�B�z���&N`J�{f&��&r�SJ�@���N�z�F D�
oW`X���Z��Dހ�*��Am���b��'D���c̈́�c;��`�n b۽�N�c@�:�nX���8�
�4I��֪��i��:]�Am
�iN��%)���	}��
)Tf�c�.�
B9�.�'�Qүn�c��h`�o�� |��	7)W�2W@��&`�_���f��x:`�&d/u�.�{��D�.�C�)�.�+��
�h tA�c`�oBۯP K�
}�n�b���z�DK��.�&�.�i
�����3a�bN`�V��"|��b�+I� �8ǧ�N�$Ĉ �@������E�5���K`�+������ay@�#�@��F��%`�`7�
;�vx߁ ��辆Y���)d@�����b!0z{��i��)|۽W�ۻ� (i�b|�j� I��έ,�L�c��)|'�����iV�>D�`�`��E��m8���X�)Î;n�'�c�N�c��&1C#�	 K��.�'�`�`i@��
@��*FF�b��υ2?�b�ۅ�;ntB����i|��N�c��&�;|n�(n��b�bC�_"o �&C���R��z� \d��{��i|�	&��ˎY�z�?DΔC��Zd�n�bU�E�����(�#��|���	�`*�n*D��-o���)D�]d����NDm��Y�`W9�nNd� h!{�H`*D*[���}y����@���!Z��@�b�N�ce{��Nf��b��̀��a:Aw��Kf�N�c�ߣ��ԪvX{@��3�*|�&���£>cs��۽��{�N���NfMb��:�@d����]d���+d�)|mA����*|K��i{@
o� �N
}�lj ��{�M	2R�<DY��nf�CmN��@	 �"�*�[dVa�hF��d;��`�B
}�nEm��b���~�n�x�`)�&�Nf��i� �1�n��N����*|i��{W@8hP@@"|@�7�O"�n�뎛yW�� �8خ��/��'��
��/�$�+;@6|�?z�}&,�2���z � OB	2|@��n�&�� �F)=|s����c�N�i�}�� �I�8AV>�a;��&!`�6� ��<h��a�2A�\�(����]d����d����ۇ`׻f����oڈJ�aa#>n�, �.�`�)��6	�� ��>D��&D���j��V 
&�Y�]*��>D��% �b�b�$�N۽�� 1 ��) �q�/h3����.�b߃��VA�8ՠ�i�N	2o�dO
}g��Z-|�bO+D�W1W!Uv�bf�-|L�>DV�5��!�탲%w��o-n�a��<z���E3����d,�+i��'wbt>� ���f �aq@�$��Q;��&|�Y�:���%�,��Cd@���xo�{P���I�X9� �f)�:������{��>	O�i	 >D
ϙbI@�^@��/�/�a�!#�B@ۯ
O�`OC�\����Z�Of�O@���j�kV!0z�60�f�&D���-��vf ��P���}�>:���&�)g@���o�{W�-D߂Bde`�{dl1I�>DP]d>fc��d,S`{?�-D�Cd��Ɵ�Y��,D�}�C��<@Vv�Ý?���xŠ�*���O�>oDD�ہ����`�io�o�{�"�>0d~:���d,I@?�o�&��D /h���Fa�9De�|��W �kf�G��){�V!0h!��aT�-:"og��6z#�-D�Ƣx$o
 %��{R��x&�?De�)�F�>�5zgl	�|H�Q'�	��E۽W��(O۽��t�)O
f*/�{���/|��H+�?D��-D,�?D_c
 d)|`�EJ ����e���`M�T]�F4��KVd�A/|��U-j���kk�B��/]��b.�ѻF��`|`�	�@i~zA$g΀�?:`�-$D)D:�=z&�G��u!C�Ea//��0��1�;n���2�)n
 M$\�G����d?g�3OVd4ODv5oDDy��?�� ٥W�iw,�i �MY#�6}N�Z���x��j��8س�ebW@	}d@��(`���-��\�7�G��Df8O}G`vq�&\��9�;|��o��J�,,DS���W:�f;O
f<�xj=�x��/�)|A}x>Ϗ�h�I9?o_�%@/�{`�bA�i�E��B�.DC�xg)��D}v���E��{f��� S�CAգ	�8gFO�aG��iH��FH/4S6Y"�aWd�J
fC����i�j��I��{;� Jox��EZdm��&W`{�@ZdW@�K��{%`Wd${�Lo��5zM�y�`�i3� N��iO�/DP�2 {�@�Q��{R�Bd���n{�F"N	)���S�;n���&ca1DTo�bU�'V�)n7� ��f�	)WO}�A����8���/�@}��Yv�VQX�iz����{Y��{���%]@y*ZO�`�N�b�&C�C!Cهʢ���[O}wb�`Bgx*\/�i]O�'́Yv�Dd�*+-���b��^o�_����(d�ư�a��DF�`��{ �P� Dɤz�a/�bb/�i@�{!�i�R�*�Bd%��c�Xvc��{��d��`�A����D�F��g!|�$O�f�w"_�eDd���df��zj}�g�-h��hSDdi/�bj DC�qk��D��ab�DF�;��zk/�b
�%]@y*lO�b��3�!{�9!mo1Df`�DN@�6n/�iWLd~;��z~��ioO��p� q/�bw"[�]��=0� |a۱��a˥�+�bˊ9r��'so1DR*�/!�t�ZvJ�����z����N��&@
�`Ud������%��Ļc"�xjU��џc��u��a`�{vo�D���`�w�GS��(W
۪@ݢ�@��x&`eۯ��Ջp&��>DP�V�_&X�yV\z�-D��Zv{/~eac�d���|�f D�
ע��y���y];��ze��*z�<��� }OCh~OC�?C�r
�c��#n�c���(�j�A灍$=��/��/e ۀo	;��O���z���hYc� ��$�o�i|��n�ݘ�m~d�-Dd`,B��8�oF����{e���� @�k���`���!׌9��Q�`�>|�5zN�8#��,� �z}�ۢ�+�ig@��n�8�f��	��<'�¢c��:�Qb��Hdh� _ EdM����F"�A"D`i�_�O�h} )7�b�o�{�Of4a� {�糒�O)J�\����/ Nl��/\v�� 
��{A�<h��t�=u<�AЧDY$�o�����O"D�� �ۢ�@���d���Ad;"��.�]dvo�Dߨi��E���ԗ��{���՘�f8�
}�`��T��i�]d�/\d�]vǠ�{;����{>��EJ���y���`g &D��?f�2��}�/\dJ� f@8h�"�:�t���Ԫ:�t��Y�m!)��q��ۮ&���a���b�}�V�nP����!{��`#D��o@ �!蜤f�O�a��	\d*#82�'���� �/�i�b��/�{�� {@Y�_�,��&�f�!|��f� A��o ��}2G�b���b�]d�a�`�d���| �	���b�/�i`;��؀�A�.�f�O�b@�'�]d<`�=}���':���6n��Zd��x�Add@�>_����,�aQب�^�=/�{%��{MVaD$��vLkΩ�+`KdR�Bd*�=��$n� �8�A۽V@�ݔ�ZdW {��@�.�/Z��@Y�y@�C�j���aT�jf��JA5�a�����J�^���{���I����)��f�z���{d@1ݷ��i��oI���We¹�)� �z��{���&| �������j[�3�/�ig����f���yI�H����%�);@a$tAMv��jN��(���a�o^d���(�OMv��B�e�ۮ!@d)@����{~���D���i��Cv3��)��+*����$|����i*�:A�u���DC�8�
�Bdŏm8Ə8>��i�A�b�d�%b�+�����i���+ȯ%D����yH�k�I��{~�� �ODv��i���i�`���J��Ϣx:�|M��`ߣ�'w¢>��m8�Of�o8D&�27`\d�eDDĦ2R`�Џ�iW {�_CDvR@DdS-|~��k��i��d��{~���/�I"���`�+fVbN��� Z��)Rf�7�Jd-�@mρ�iqa�,ԏ�hV�8|Տ�zΡ2��8|��&D�`��`�����!{�O(U2�OMd7�{W��@)د2;�@m�o\d<��iʇ5Z����aDDP`�-��%:�&�g�&�O
}ۯ���$�?���(��xߦ�I�}<@��*�Id�`�';`fM�����k���{�b� �m���y��j�O9D{@�����hf��|���`�`���@m|`�	ʠ�>��jw@ݢ��Di�%":�Va�:�F�}��>	��8�/)��j g!|`�	��<g"����/�b:��8T@`8|`�^�\d��bB�:�O9D�@���<z� ;�Q%::(��I��>*��D�f���i@�
![���{菆$ׂ|�Ѐ��`~��zW��q�fJϸTI��z�/
 ��h�)����/:Db���5��h��.�`�{_��/7�}"��/Po�Dv��U�O�`�/^v%@�a�9|��:n����B�<�́��i �v������:|F(:D
A�h������]@�j�b!g�OX����5�o�ע�=zd���i@@�!a�v�����^����ݪ��`���3��<v ֜q��,�����>��	f@�iq`�w �o����Ń�/ͬ�>�<zo ��a/��<;��'��z�����g��՟$}���,��;n�O�i��;|��z�GIWMv���ǟ�}S��z�(��}��Md��m8�ȸ�>�dK���W�:|�O�hA��O�z��P�iP�i0�c�;|W@��:!{�� (��`�C ��e`�i͡Cm�a��b�j��z_��*���>7�)|�@�h�m*���^S�>I��OA�U�-�A�*�a�-�8�F",̦>��!g�@"!g ���p�+�}�i��i'�9�!g%�,����`�_��a<|���+�`~������m8��;|���	���;nz�<�I {�
�cTh<n��bR �X��+���6�g�.}!#�i��lCm~p-D
��x� �����<q ۘ@)g]F�ޅh�a|`ʁ��d*� �̀bZ��<C@��b�j �iqa�x=���V!�"0�{�VbB�m8P+D����P��W��W�;nR`�dp*D�;|�c��>�"g�D"�,�*|4g�eJ��d��%�!�bR����,�!g��b
!��A�Df@ie9��y�c��`��Yvʦ8�b�8P��eB
f�a���k�c��D�fR�Idf�Q$⎻c��8�h�C=n�IdW��dM v~P=nP�z��h ���D���J���b0�i�@2 P ]��$|��	F��h!�ۨB+DR��`"��.#P�i$p A r#�� %��/ $gW {���f0 ���`K �&�''$ut�f(��d����S�{&��W��f�'�{܎�c)P�i���H�<Du�#g�n�*0�{qB�8+�,D`�ã͈f8a% ,�f*$u3@�J��;�f9I=|�`��f8�,�{-�+|.P�i/���00�i1P�{V�
ۦa�C���2-n�|t�|d �+=���3�f�a$[�-|�a$���m��ݙ"�"��K@�<�(4�w"Hd�L͢	f�	���x�#�h ��ݠ�D�a��*AƓ�@գ;���4-n顣����o��iI��5�f�@�2��#g:��d6�JdW��7	)�a��7�D8P�i9�b:j?;�>D:��d<��{=П$|v7��`��A�-��#gR �>0ck/�go >|�J�i?f@��bA�=za�>�KC�AP�{d��#B0>nC0�dS�?Do���h�J�{Cw�<@�d���i>�jD��H�^Ep�����FP�>G��iF@�,I�طa�vJ���v[�˥��͡ �ف�.菅-DM�C������8H�f��D�jV�z���"I�%���D�!�c6�X9P�E�J�f��*KP�.;@��L��<M��`Nf�@vC��`F`�_i�dOp�F@�,b#��PP�&:�����@��@�<&�̻Q�@dA�>ma��R��b
�$gM�}}��SP�.��>ckF��jT� Oh���GQ�W`�bC"gZa.|U�m| ց��V��i��b��iWpfW� X��z�LI9Y�@dQ��b��|�ҀZPMd^�-:���[��P���<�'�# ��ãf`�b��{\Ѝ$�j�P�y{��]Ѝ$^Pf\@��,c�b�e�_Pi8@۱�k@o�!X��MI9`��{a��,:/|<�
ab� *A
�|`�=*�=�&��p���b���i��[mc0{�@W�dpfbZ�e��f��g��>.F��*F�gp�{X� ���iwˢh��b �j���;nN�i~
A��i0Nv>cR�
��&�s��"gjP DB��>�
�`kp�il�am����`�+��T2%��x��j(@ݢn��->@�o�Hmp0)�ᔛ��i$�xx씡�`3�|��j�/Dq��$\`�bA %ݮ��
)$ X�f��<	�F�<�'zAf�%�� դa��rp�{�!X�rG$e�I$`�bW@�a�i�����i+�6e�}�d{�#5S�b�bd�Q�Ճ�W�
oJ`\�
A��sP֣t]m3���A)CA$gu�i�CA�j˫	�D� ��i~bT��;nJ`Aze�[mvP�z�)!���>A�nie!D��i~z��m	2H`�j %�*����i��u���eB)q�BdP��8j���k~X� H,�bg ��Fb;���]@f~��i��mi X�A %�:@�zj �ݑ�$g��iw0�xp�byP�z7`�$A %�z0�-C}{�Cd|P D�ۚ}P�zW�o;�������~��{b1D/�b] :`)�)u��Bdp�`�����Bv]�)W �{ ��e��|��I`)��4Z��Km�h��
�i�%q�Gb<�
a��>�梐Dd�p�> rb�hף��Cv*a�/�0�b�� |P��&����Ć�&2D_��'�� |I�d��{��$���z�P�h��}~w�<nK��R [�el
 M�x�S�{�C�"n|�E�b)�P�z �&;�����Cd<�'�x`�>� �I@Xb��YdC�x�3��zb"T$1@�#ˢ�%EdI�d�i87�b��{A (�e"fA ���`Œ0)��i6�0�{��)u�P�>(`&��{#��K��}��i�"Ƙc��'���k�a�*}2D��L���$" )�?z��"_�}#�� �V�[d��h���h	�~~]���0�e.�m*�0����i�M9�� �0Ed��{@����iX2D@���E��3`�}��i.�h�0�i�(��w��@�.��z�)a�>��zy�?D��P��"���D@�:Ar��`!�|��	<��
�"n�� n��o��bd�=%�!n��zd�b-��$�	�hf �%&\d�P`#⊣�R [�������?D��Jc�P}�J���F�P$|�!E����b�`{�} Ev�PMm�P$|�0Ede"f���h@ˣ���D��{��z�&�Pm~��/�`�~W�>@@"!��j���bA (ݮ }	 fg@���m8�0u\S�"|€�D|G"DL�K�k�ݗ�K��FFd�p�R�"n���P@Up��h9�9�.�m*�P"D�M�{q@��"��/���${��{0�"|*�Y���	!��%#�#��'���i@��|��	�"|��[dd�=f ݳP$|���i�b,��E�6	�d�@�}�@}}��`a���{�P�(R��=B�`3`ڶ�'��9骊�i�PFv�0���/�@�{���1A`1�P}���| ��;@Fd��x�T�h� !�p�-�$|�b��!!ݨ�)7��{|��	�&|<�Jc.A����#n:��ʾp�ܿ��i���i�p*����)uX�F"M�"|�Yv|�ρ&�&�$|�P�'�g&��#|�&D��tIŐ-!���(`���#n����a&�h9 !�Yv	��ǐBd�@�i���Ȱf<����������P�h<�'w@�	���a$n�E����^�҅�b9A��0�<�.٣��'&`Gd���1`�o����i_ �?I`�L�{�dGd�  ̐�i�0�{�� ��p�i|ف��ib �ϵ�p��a܀�@��"`#D& �����b'"%n� IB�x�Єb2��xA�~xT�&D��#|�еA� ��a�0 3!���P�,>`�{�E�W&�i!���`�z
������b�{!���7��j
�#n>���02:AXbU�[d��ݪ��J ����΀@���p�iiF�jm������%���Q$g)�p�j�p�xx��g�y�g���0���){��+�i(`� �{�``���it�p�{]� u<@ ����` ��zN`�d��)h��I`Z|�p���P�i<@�d|�ۀ2!�bߐ$DT�&DH��x���a(��]di�m~� ����� |��렍�;�E��fO"��kf��ƪ�v��v+
�z�2�����~]#���@��`�"u �{F��>�P�L��8|��jAH�^F��av�Uv���P�,�A��)�(�`��@_d%d8D�ШB��aU4�K�,����`�z��/�p\dT��{�B��@]d]`�h�����`�zg�`m�
f�P-u��z��a��%:��`������N���&`����-ꐻc��JvK`�o�%D&�&D���+O��>X�'��U5J��dC���0�b�`��� �wb�a&C����|��P&nN��>��`@��g��:�����_�Kd�`8D��aJB���C�" �iNH-gd@�<<B&ntD��a�$g�$u��' W���)|���W���E��e �d��Kd%@	)<@�`�	}zAw>�����!ݮ�':r>��Kd5��iJe��?eÐ���	]fw�p����)n��	}������0�z7� E'I'fI�ye��&��'���=��y�)!|��� ��a��F"��	}�g��X��Jd��z�z>aC-u���� ��Q��E'�$uP`'| d�,��ʆ�<"�ס��%!��A���A	;�a��1:D��,� ��B�a|��=):
�(|*�>`g�
 m,):�a'n��a| �	тbѡ8R��hR��zS��>�bi�+	�U 
�)|� �������	oP��+g��ѡ8
�'D��	`"�;n}�)n�h����|@�%@ qB�-q"�h���)D'&)D<�
 E'F��۪�)|<``x�@�a�(|�
���;|�d��I��
Aˣ|��B�VA������e�`F_�$uߣ`R@�ۣ���v�Ϯ�F�� �w� �&g� q�b��h��z�aL�M"��%E>�;nF�޴����gԮ�F����q�5�zg� ��%��`��Yd�� �@�����`q�z� >��������z���`ѭ><�z��`�c��7��,���Q�iR%�&�a*D���@&u q�az��a7@�$	  !q�z"(�)J!�"Q�&
���#q*D$�*|v�7K�Dˣ:�YK�g
�޴� �?����M"B�>!��>�a�z�fNvJ�*|F�)%�uCI�dEg�aS�AH��� �a�a���	|�Ӏ���ǘ�<D&Q-n?g��'Q+D���'PB�(�<D����)�z�B̈́��Q���N�ט��"*�  ��d+Q+D�z>D
}g� ��F�����+�<D"@-n��,D'�*|��`,�<Dw��-�~S�V1.1�/�a�<D\$��&㯏�,V!�8
@��/kd>eBDm0��a�f�rM
}|@Ȁ	
F���'�?icD�gz1��	m���2�a�ef}`�hP���Y�CAOd3��B�Zd��j���.4�a����!��5Qf6q��`*a��7��x��� \d�[v���ji@I�8�~��,��	�=D9��a:��a���`)`,q�-D;�+n�${��!{��@$u�� n�*D{�
):!w�I�e�	�F9���`+Q+DŌ�&džF9<Q�]d�e V!�%9� |�Ӏ}�&�N
`�x=�`>Q)?Q$u@��j{f{�`�dA)!T�-D�N�Bq�'kٞCQ+D�ǴA�@��w��_ۀ��:@?��F�]Y99A��T-n�K�hyŹ���K��D��b�G�A�z>E�%{A�$;��z��[d��|8����\b@�hF�[d��u/�a�-DG�Ym菹���<J@<�%-n3�5�f�E���y�2��'|`�H��aIQ�zA�?�@?���b~��p��#j��@$g�a�J�-DK�]`�>)��L1�a!�-o�z�,VB{!)�'�+]��h
-nMQ$g�"���
-nN1��7�?D�$��%@�zy&{k���� Wv~OQ�bE��C�r�u� n=d!D8�븁�x��>DdB�`Pq.n]cAmQ�TTR1�b��{S-|�@W�<�'�L�`�B	�|\1�?T"D��6] �a�ȟ$Uq.nt0�`��!V���:��|)`���}�`)W�"|K��%�i��@�iX1�b<�Jc€Ym^a!DYQ��`b�+Z��z��)��Jc��.D��[1�i\q)]�?D��^Q�>]e&��-D�yȦ��d�Ym;��D��5G��oN )e")_q)w�Eaj,���_P�G�w��+� X2��V�Eh<�'`q�+a)bQMv��>��DU��'��%��d�;��i?m{�c��ad�G�NF�����3
��⥩E/|�ͧTeq�a:A��d�v�����E��@;g���]��><�'&����fQ�`���g�z;�{~h�#ni�jz!�?j��a`����e")��j%`.|
B�`Va_�k�#n_f.n
 |~l1F�_F��m�}_��an�h8I��`�/��Jce��a��s\|`��o�zq���{�"|/�f2Wer�@�+;�v�<@�
���A\$`��O��2%�}�>��8gA$n1@��pQ2DV��V�z~q�zr�}�e}z��"�[m�zd�m#\��xM����x�X�z�`=%@i8s��`N���$,���M� ��lNt��`e`�au�#nv�ʢw��aA�ax���oy}~�``~䣩8&`az1�{M�#n{�z	���w@=�� I`D�N@4Sڥ�#|3�&�i˕o��~J��	��|Q\$ĥ#n}���I�d~�E�-I	2U�bH�/D�_"�`o;�9�ͩ#|y�M"@$|�]m��f*�q1D�h���'�1�$]  �b�v�Q�a��`��)�`%|�	�b�!�v��)u�q1D:A�ʇQ�aF�&l!g�'�h���a!�,�醂)� �b<@���磉��b.B`*�Q2D�`%|����Q)��'� �b���a����.�����;uN�Cd�Vd�@7�d�$DT��`��ؤ�1
 �@�����'� ۷b���Q�xo���Q�'���a��;u��Fm��
�\��ޔDv��b~�`�&
@�a��)]@)!��Cپ`���2D@۽Z	g�g��Q�a�B�(1�D�v�7.�Q)Dv�Q�adoe�&|v�{D�E�� :ݜQ)C@�8�]�
��c����1�a#dU��;F���bR`�m1@�gA	 &��:�יV`���VdW�b~e�k,7����k>���a��%D�+!I@Xb�@۴�Q�&�x�v5L�	f�Ѣc���a;�����` Wd�ff��b~�@��}��+ rbW`}I�TD�x���` �cN��	&n�2D�����bѣk,� Ev�q�H@�&| �	 t�z��˕�ǶI ��A��P��Vd� �b��c�@�pq��`�b�{�Qm~�Q�'UD�{@�&��,]��$���`�q^m$+�ĮQ�&S��d�ֻ@m~mڥ���btIF٦� b�(|`�|���)���cq��$���`��_9n�5ti�� � OaZ�i��'*� e`ce`�{��b�o�6����`�����>���a���`���ba�>�13D
�������a{�>��$��c��]Z�F"f��n&I[��b��� �A�ϵ�z��(|D�&�Qm~%�Xd��D�-3D.��c��F������c��ޣ��m*�a���@�f�Q$;�a���&��`R�)��`��2���w@�owBKm��'q��`
a=���ժ��+���a!�����ĩ%I@����(|�௘�`۽�{~�`�(������ay���0`X���M9<kJ���=S��a����q@��3G۽.@����by���C��q��$��aT)Ñ�bq�Xd��c>�%�]� | �	�a����	}���_��a��bA�Fd�d�Ǒ�i�1)���z��c3G۴bb`j;&��ɱ�&i��/
/�Q)D�� �Q}��?!�	}��*:���~)
)��4!@�T@�'\�?!̑�a�bGd���b�1
)�b��r���٫�N�j?l DV!pxf�E�i@����݁�b��x��,)�Q�aĠ� T@}o )�1
)C$g�!��@��A)DБf8;��q�	ol�4SJ�4��GddB)DJ��̃�8|�&�i��&)�nёf8@9� �z�Hdf��Ld5ZNf�:A��m~C� ���'��)|�1
)yबI`�Q��)|�	}|��7n��b�����ϪH� VA�>@V�JK��q�a
���߅D�e�b��b&K��I"�@���2�Q�aڱHv���i��&{��a�A�,�A���Q�a���ݦ�eM����*8�$gޱk5' D7�m*�)f�yb| �߱Hd���{�q�a�����`�i?�f8��O��PZ��1�OV1��%@�,�1
)�@+t�o�Q�,���2�Q���$g�q�`(�f΀be���&�~��:�q��iz��h
��ݤ�HdA`�&|��{I"g�� �&�C����Ū���X�Am$� �Q"D�)�b�	�+Ӄ[d�}\�4��E�ʪox��&b�ZvT@de0��A��Q��i�I"�Am�
��Am�%�q\v�!�b  2���>��`�@ۦ��z��D1u,�)o C�P s�>051�@��'��b�m~3)q��&V!�8�1�b�q8Dy���&@"Dac�`�m8Dn@�������� �%C��Q����"n��zo C���f�"��,D���q8D6�)��8|XA��o C��1 �)�q�d��+De��m��h��Id�Q9DP�1���,D&f�+f@�����y�q�b�F2��"|N��`?�����'Ci8]`#DVAi8��1�"n��"|�
'�,D�i~R�>�"|; j�q�i~]@
}�&%���<@�&��8
�R]dr\dR�>R]v	R
}�a�-N��`
R9Dr�b(@̣}@"Dk�Kdo C�A��r8D"�9n�f�a=�;n.�8؈`��
��`/F���)v%.n} R�_�R D��&�$nr�i
!��R�a_��bEJ 2^d����	���U�P�eB-|$n�;|��Cm`��e}�%<``x�!:D��&�Xd��c��J��+W��~d��i�$n�:|� �d`
�Y�8�:n�"�<!��r�{�D�b�f�>r�; �v�����i7)!^M]d)@��@�o��cR-|]��>qo�`{�%@�at�-D��`o�:n��|'f�τ$D }��t�M �iMo��i8���{1e�$�&�o!�;n7G�&"K����X#�o$R�x��Cm% �#�b��cH Em7)!�@R�&�}IMdb�r��:|'�;n�!Q�z �'*A(�;|€�$	`d>�'��)R Dw"t,*o��a+2.|��.D,2.n.$}��|HckV��.  �0f���	.�L�-R�hؤ:|{`8.Ҩ�/���N�x�/DT;DD��j�	o.�	}0�;nA�����]�(gF�/D1�/DVaNv�@Fmf��"��<��)��Am2r<|�%D3�	o$�.D4�))��%n 
)��4G5�;n�@��| �� �'�d��{k��o�& �Y�C�&��B��8�X�����@u>
�&�B��6�B�Md7RFmo@K���+7`*�@Fm8MdI`Am�BFmA��)g<|9R����/D��:R�+�agխ���;�/D��[<�z��i=�c>rNd3����8��� ɓq�ux�����b?Rm~&B���hۀ�>@�)0O�Y�z�b<n]@=|ArGm��8%a�����|��-A�Y҈�f�h��b�ַBROd@�$3�p�0ߣ ��C�	oDRm��i*��|�����F+E�c�F�� ��F�"|( �GR�|N�)�8mA�&H�ߣ0�c��yjI21D7@=nJ�&KFaf�L�`�f�`��dc�:a�]��F�F�\d��G2�LR�$C�zM�b
��,N�<�b��!�b��)OR2DJ�q,�`r�P��h���%3G۽�o��Q���:a�o9�m8
�h�t���`m�t:��.b�'T@2DR��`S�+
A�b�$�+T��4�)��>�������SF)U�j���)f Q�F kx��`�B����8V�@dWR=|�=D�A�܇��Μb�`X�=DY�@vZR=n͊�,� �nmMU;y�[�Hm\R�$O`x�l!ٞ]�m8� f��b���^��`{��$_R)f$& �d`�a~����f�a �Hmހf�W� v�=Da�>�	�b҉b������`C��4C�������V�c�Hmo�+3�[dd�Ime�;u)��fd��f-g��>N�5��Fa�ަ�j�_�9gg2�zh2�a������I+�a{�'D$@Dm�#���&�)DiR
o���;�Dm>@��|��^�m��A?�q��&jR@"R��k�a��8��
)l�Adm��`n2�ao��2�A��"X�S���ap��>נ�l�$C�g���qR�`r�>Da��,�g�Ams�
)	�����n�8�M���	��'tR)���s���au���@�iS�>DF`�+v-uwR)*�
) ����~=�BvJŦO��x��&��zW�{�f�ە��Ny��b
�$zR
}I�7�q@?�W�:{�"g��8*��|�Q�c3�o8)��(@����fI4�|�@d��'3b�OaT~}�&D��'~R�d�"g{`�b@U~���b����Ad��(`�`R�ـ��a��ār�& �x�R�&wf���I��am����B�/�ux���,�2X��2�a3!���B۴:�o��'�R�&��a|`��v���`�&�r{�I2���Ad�`v>��8�����Y��R���_"D��|%I����'7�Cva�oD��|�Km��~��(����R��M 5n�R�'	�i>�c�۽�éj�gV/<A)�2j��2�b3g�*a
�)��Ad.�8�^Q)�_"i��>� ����&D�2�bd��;�k�@d��d�&DI�C_��a&��&�o�v��Rf~A���o��0���J��a��j��Km`{R��j�J��IP��՘)� kj�R$u��z�!���}_�G�)@��Km��C���b@�?� �&�2�&��a��a`�&v#�֞)	ODv��$��hI}~�AXk�Cd�a�Q��Bd��%@DvI`��`� �a�b.�k,�r)��Bd�@}�a��J�-n'b)<@�a���`[�q�k,@�c}�,D�RI9�_"���li@{���a� �b7�Cv5��a*���H�<��Av9�&B�h�D�oe���ieEdd)�Mw@�+"0�& cC"�R)DC��ަrUdV��a��'o�G���oS��G�����.��@f~�2C�V1:�N��z�-|H�f*�}~O(�Mff��D�Θ`Ud�R�?�-|�`î�R�-�-|�rA�����&���эjK��F  �J`��\�\�����U�M+�Dd�-:�s�=H�f*A��f}��j���a���ְ�Cd��f*����j�Bo��M+`),*����-|�@W�rNmI�o��oe��f*a@����f8�ƒ�Dv�oe�#8Dc��'���k�@+S���|j`I���ĩ>��!�aCd@9�R}�F"F���-n�GI+I@��9!+��<�@o!�_�z�)I`3�k�Cd��f�)��Ɨ@�%�2EdC�����)%D�a��&�P`�@�av@Z��*Gdţ8|�y,3���d��h
����Fm:a�a߂�`�-D�@Om��M+!A�af�d"�@OmVb�a��|j�)A��f�r ���f��^���9�Dd�R�`7LoT(8:q`{�|��^�)] Ev���i�í,���`C��R`�8�2EdŒ{~�` �9D��fO�S���&�QOm9A���@�:��`_�Am�R�a�R�`�r�`�r o@�xy��{� ����G�W�{~i�\m��~�P��}��`*��8ͲE"I�A��f����|@Ձ��)��^�ڥg��r:D��f��8�`���9D�`w
*���R�v�R"D��H9D���{g��:��f*��8��,7��,
���g�f	���A���8�f��^��r�a�i8!A�aY�Am������^�M`�h1��D�+���)*��8��-S�%�Q���i~͡ �R��
`:D���`�Hp�ڒi~*��8�i{��r�a��oS�i~e�Bm`{��`sj�Ҁb�`�ca:D��i�w`��X�f�"n�RFv�e�th4m�AMmV�ax|&pjM��$��£��Xv��!�W� ��E" X���>���Mߒ	}I�q�A�8��d��f��}��Hd�)%��hq`C9��i]��)��;|�`��� ��}��ĮqA-n��`@|,�RFd��}
a�9��G��>@�k��}2D�%X�`Gv]�
 �r)��a&:�%a��c�G"�R�$1l::�PZ���}U/o{@���`��9���q��DN��dC�>��i��}��i�@ DVA[���|��8&`Gd�i��R�D�}*�Ao%�)�E�h�rGv� 4Ef�$|��Hd��\m�A-|R��R�D�C�� X�;�����>�Rf*�BmC�G"��Cm�r�i�R DS����:D��H�� oK�ʴW��RMm�R9�2 �!<ny�H�3a1D�) C�|�i�q>VA�DM��xZd��<D����wb�8��	o�`{��@ D��.D���`sGdW�$:����H��K�(Ԁf���c���8O���Hv��҇ye��b3@���Zd�)Em�[v��	}|�ׁ�ʣ:���;|�Уs��)I�)'y࣬F�m8� %n�f=n.brjJ��x��a�N�m8	3�b
�	}.��8��$��*�Cf�).�2q� �fS DXD�ux�M��g�e�
}e�-�)S�'�ii��$����:�W�l8��*S}}�D9J �D!A�b�B�b!��X�Zd/ʢx�Hd�	oS�>f��z�.D|�] �{�HdF�f7}�	}(  �e�?���� �ZdΠHdS }h��z{@����&"f����5E�bS�{�g��i���z
 �dǪ�1�^��g>|b[�����O!4�<I"P �w��b�w�S�>&`�K`�w�a�O��A RH�x�!& 
o���N��z>�&D!�=Dj�N:���|��"S�>Rf�<�m8�N]d
A�d#�)�b�`,�9�v@��m8C�f<�$���a�)( �٩)��9�N�A/a�����ݖ�	}& �� P-u$s %ӻj&f'� (
}�F-g)S-g�3h��c| �&@-gf�\�*S <�d�@]v��a+�Jd��~)*
�J%+&�V��cT#�+WKqj�DMd|�L�f"H�,sUmah�+J �D��Kv�|�d@Fm-�{~.S �`3@/s}��8E�9��b:� 0��'n �1��`�av� ��2��`q�) �J �S�?D|��73�'D��h4�}� �$�@��5�'D&�d6Sxxz��$3�'D�d���`D�&`bn��b7��x� �D-�F��`VAu>8���A��9�ݲ��h��he��z:�8�:ӂb������5 #D �D�o�jwBu>g�aE[��@��;_d]@ !�FMd<��`� ��=��h�h�`U`� vʼnk���{�L"e�΁�x[����h%��`%�@rV!t>$E	2>S�a?��	��n f@s��y���b�5� ^��{~
I9�(Q�A� �C�:�U�������k�fB��
{�-!C�?DD� !��||��E
��z��D�2TMv��a���'��2m��<��3�)|����%MdT㫣��F�$G�aH�Im�MFa3�;uC`��O$n|����D��F� I�%D�-FJ�Md5�a*�AdJ�E�I`v�%a��$��Ѐ��I@z΁)|��bG�Av�dKS�&zB"L� ���$M�@v��& ��N�b-��&C��u��[O�AdP3
 ) ��Q��D]�������R��D�X�SsNd|��	KI�zo��Du�uRT�}UI9.'�:�BdVS�`i����F����" W�bI�Bdf���ay�%A)�pZm��{�XS�V�@�W7�Xmq!f���]�
fTH	 Y�`Z  ��R �Q��)��*|(�f��<M C"� [�ɣ\s��.��a`�z%��<�"g�@oN���@�kyF6�]sNv^S+D_���|�I@��� �A�zW��&m��x��\�b�`�h�L�`��&a��DbsNd�`����q�3G+D��=�ˣc3
 �Omd�}]�!u�DH�!�ɣ�Af%�n��aW �d�΢��|���j��xH`�z( θe�Cv7�2f g�}N N��`�b� ��hSf��|�{u|���iSOvj�Cvk�2mpF��61olS�z�gNd*��df����Tޡ m�Y�%a���}ڥ�W�1' ������n�#gW Z9�og�b�����\c��f�U�
�7��%7�Cv6%�>���`o pS�k�I$u`W�q �{���ze���<1�"�R�$��j"�-r2a@b8s��z���a�C�C�t��`W����"Gm��ˣ7@K9A�kg`�u�Cdv��`|�ɀ�}w�Cd\�8�]�,Dx3�,q��Dysg��gg�d#�&ͩCd{�~��O;��Ń�io �'�cf��bg� 7�)zs��{ӆ$31ף�f����ʙ�`2Ò��t��j;��z|� }�Cv́�ig�ˣ�8~��b��a� � ��E�C����_b�P�Q
f�j�&����
��
��-D��;n_�<���iW��D�� ( ������m��|~�#;|��>D�⃳�i�S6|��z��)|eKg!�3&B)@!~�B��|��
��b��,Dv wTF�[mU� B �k��[m�_bpEd"��z�@����9�Fd�Im�����h( ��y�	f��1'7@9o�L���;|�@+� 3��W�Om�`
 �z3����,D��|�W��{�>��2�`�:�v~%@
f����Fd �z�S)DR����z����@M��S
f@�h��*|�@9�S
fgl����fC�XM����@m���z:a�_� 
b�b�3������E�z&��3��g!Gd���hf`�{���f�
�Ji�♳�hw�i!�S�>w�i�}@ ���{���&�3Gd�SOmy �蚡q��Fd�᳷X Ud�`��
�D��;n�3Gd���be��z���h��){��'3��,qv~�h�3��hR��ݡS�,�[�m��z�3Udqa�.��x�өx�»x�����$�өx�Ӎ$��b)f&���1�}��z���z�R������P��<@��o I�I`��f�E"'g{@���������O�!��]��$�� \`
2��|�%�h�� ���d%��i���!}��hS�/D%�Wk-!^m)�ԣVs�b��F��s�����z��/D*Ud�S�>���>n�xI��׮������z��Fa�@�����&�3�j ���ϣ��$��'��W] >n+��ii��:�2W@ BC�,Vax8f?�`�i��!gP"�Oz	[��S�b\?�W�ٰ�F�ϣ ^m���hf?���'���z%	)za�n��|�0�x��dy���� ���� T@�,y	)��Hd��c; �$/!f?�AI"?
?�S�ii�]m��(gV�hzAMq�B� ���8�. ���>:�Amzi�f��Hd�ӻxGNMm��ϣ�� ��f�yȽ
f�@��{�1�LMm��jK�W�Hdf��i����⣬d oX�'�F-|�A?�T�Iv7@)3
"�z4'cI����	}��?D�S4'|�ш�SMm�3��%X�"�_9y	)|�!�A��f�@6�N`Ðؤ � {�� {��sUdl� P`��l�{�Vv�3X� @�&�3\m��,D��"u;e�Dm�?�&\m5�j�lC��afk�Sf�S$u��Hd�@2�3 ́	}�@�hC�Ԯ�i���Ӌb�S�zf?�@�/�tͳJd��>o��f��+%@-|�O��Vd\?����`J 2DI}|�j���F@�9�Vvd(u<@��;T�S Va5'�a۴�@Ę]`1ѓ�$}�?D;iy�`�=na����VvO�eR�Y�C�	} Vv6Y":a�A��f���`��==BM-w��i|߁m�d�G���KfmA�>ӓ�$I�� ��?F���]��Sʷ�%��a�=|����{�D9ճJd<��k��-Dz!��f?�o }�Vv�S �>���ד{~�� qa�hr:��ӿ>M������`��>�!Wdܓ{~椢i��>�	��$W��� �Vv �b�Sgf@����?ߓKd\?��Hָ/R�"`�zg��ya�8��S}�ӭ>]�<DF �d�Jd?��
UE�r������A�{e�� I������a͡�k`{�҅�k]@B"�KvF �d_��f?����`�ţ0�
��2S@{�I�� �c^m��i~��tI������a��`�{~n
[�CQҧ̪�g��D��hw@g�oe$@�&�������h��S;� �%s�g��Dqa�zcC�xI�33��+qi*��Zd�A�]@B"a�{�`3�I@��i�>D��h3��+^
}H�<��i!��`R��x|р'b)qa�h,��j��`�ŭ>�A��]�)Ы��ӿ>.�Bv�A6�����&��z��F9���`遟���&gCa}$@-uR`�d�3f�
o���`��`�bef�����i!�8'ȱ �@=AB[����"�c�
}��\m�'�h[��+�k~*� T�Yd��b@�$�dN"��'d�oA�{)�+�[vS�r��a	��S��g��{g�?D��"q�M"g� e@��@�yf@�{�G9����{'Nv������&�3\db��L���E@�J����Dv�3Nd��Yda �`�b'N}��?DJ����|�|@�	\���N@}��M"�S}z��h��r����aJ��#DD��?D�>DU�n��"��&� 	}�3\d����g���%�D�LJ�H�<_��i�S��4Nd:���|ŁTo0A}~�Zvdw�&$��&!gca ] �z�D��:�d�H}����@�[d4DDI� � �`o�lZm; �&�"!D_F@���%4DDq!�j��Hm=gI� (@�o��j%a���j�&i�dm~J �����`�K	2	�9'O�դym~W`Zm
4�&���d۠�e"Nd�_97�G9�[d���i
�&T)!h�[v�'~~�"!D��,�Od4Nd�ZdD	2�a<�Zd_�[v�
�jʠ�`�=gĠ\m4���kN�|�4!Dh�[v
@ieUI��tߣ��`ݠ:'A^"|@�BJ�m*q!�j�H)!*a�P@&�A���ZdT)!��
S�zk!`����Ϥ�`� Xdj���9�jF�b_�i`X��8�Im?g��N@�>��|�T���t8D�Đ��F��|����i&���0�z�:'I��I� 5��-T�`��z��é:%�.��=�4 $��� T��t<'��&��f�m/g =g�"�j!4�&"4�Ի��x#��a��`&�8�
 �8laٞ�b^d.��a$T	2%t<'< �<�Jc�a��N �v:� �c�i�D�+����Jc&�"|K�̴P ��'T�>_�f#I���( ���+�Į��z��zM@9D)ԥ��c�A��zd�y� ]@9Dl\d*�	f]�}]�\+�	f���/��}�࣬!�=�7�i,T='-��`VA_�
��'��h�@��&�^m蠟��E�n��"��"n���D.T]dM�|��'ܸ/T�>�C"D�@K+|@�H��x�@꣮!}A�}0ԛy1��D&���2f<����@]d3ԍ$���&*A��I`�b�ۯA��f` {44�b�L�_�Fࣺ����{�)�b
A`*�@�(0�o8f����+€f%$n`o��'�L]dO!�k:�hi[�9��D�5��&���6�hza�h�)��aS`wTV�c�%o�a���ʚ�� 7T9D�$'�J!K��f#8T�,9��b�����AieGDd� ���z:2;�)<tDDg�Zm�[�@9D��{�@�%=�:|o�i�A�,{!:D>Mm�D`*N`� 9k�`RŦ>� z)�A����Y�|@Ӏ�D6��-^d�D��^"c��Do�meR �d�Іĕ@J��&e�$D?4�z�%�z�Ņ��ޣ@ԅLA�fHi#:��l
����C-g�D�˩x�͍$B�)]��CtA9D�E�V"%|E��dFT@90�|�G�d��I��%_d�ڣ�@��HT-gf X�f X�{�L+��*"u�I��.JT
f���F-uKTDd�S�OT��h�^m�A6nF �du�Ivv`8� {�J��&L_dMT-uf`�{wb�k>�f����7�b���vN�D����O�)P`8�k�iqۯPӻ¬%D|@ؒw� �fQT ��ZmQ��/�(�D]@}���&��AmC�8�RT�h�����bSԏ�,d��ݠheT�)�!�*��ڢ����f�2U�b�̲�>��V�BmF@�h.bNm�D-uW_d�A��XT����<D:���Y��&|р �`Z4-DB#\k�H�z1`J�$�b~*�ףg�)�a�D[t 1 �D\4Ud]�`~�&��d{�&���a�.R@	)�#�cA��y�,�!)��AmI��/A���`7�^�f�dh\�`_��&����� �`�)� �EaT�y��[mb4Udc�
 ^D�zd4UdX�)Bۯe�`~��f�C�7d��f�g4f�!5uA��|рh�a~7�=D����fĹ�b��xiԭ,�<u�j�i�F�d�`��o`$�D�duJ��e�j4Ud�D2|`�	<�JcJ!Udo@{���~kԭ>I �Dq��ilԭ>&��A��b~@�� ۴����#'�D;v�f���Ȳje��F�´mTVdnt\m4f&`�>ȇ����C�KҴs�Bm���h:��dR������f��f+f|�	4�>@[AA��{o$up4�&��
}<�=�RM�$�b��]@D*(�&R�'qô	�frf���#'e 7�fI@���M��ܻ Ud�y}s�"gt�fuT�xI@��oy ���=�v4�&w45uK�k�@�b�$u`)x�����(|`)v�>D�\�lMvN��z�Ǣ*A���`y�izԭ>b`�+��(d�å <T���d{)DX@I"
!�d|tEm��.I��d�B?I�����.�$�d4�	}�Mv%_m���z})D"�<"�]9q��,�`�%�Md�%۴C!>|I�>�x�%۴@����<~��+_�<z�$g�"Z��
) ��Uf��&f�	}�4�b�`Em��z��,�)D�TD�T�b`�n�hZ��2�|�tEm{� � ���TD �-|��>D��?D���z�$R���nH$gd�/u��#g�j���2�t��I�0�Ca'AiFm]@"��d��;nN`�f�)D<"X"G�m#� ��W`{��T$umeAD��2%d~P���d~|�ʒ��Xv� \m��|!�i���(��@d(��&����vKখw"�|��4��nAI"�ĐH`Emv�S -!:A�Vbne� �""��$�tfN ��߂�z�tEm;f��tf��ݾ��,
�
fy�8g('�|%��z� }�PDS3QƔ� nR����"O���[�&
�x��{�T�z�`���M��x�w�"�_9}�M"JP+D�d�h����g���5� �a�hN����d�
!�d��_�j�����Yd�e*q@�������+z�?�������B����d(��+�t	}�T��K��	��fI �՘��h_`#A`�z���o�9��Ҝdy�8g� �N`�f�s�����O$A�ze"DW��|'��>���c!�k��bU�Yve"DO�f8zA$gzA�{�tNd���>`�he��h|�ԁT�Yd:�e�qa�z|���T۽�`��Ŧ[W@�?S@{�k�"|��=�B�'d`����z��_]������z��h�t�YW@��;���I��	���@f�Y"��z��a��|�a��)����be;�'�4fGդ� �}{ ��O��vOAd2��z|�è	 a�zq�>D	Ţ�@�>�Tf�4�����z����6!�z]-n�I�n�ݙ��5I� ��&ш�ga@۽O���z�e*J�fz!�z"��b��f��!n�����"| ��;@I9�-n����&`�n
!�da`X�V!�|Wh��A�)I�d@�>���h#�mK���!n��}"@i*&�I.�me������X��4#D�T���&g�4�z����� w"�|�TI9�!g�N�C�X@�,��#n��I�JI9ND��4Ʈ!fb&K�Im�@�>e�7�P���aCe�
)� ڴ�Tɣ)�)�Ŏd��i~&�y���i~�����T$n�@3�<P$n���a��Zd��[d���&��f��	f�`㯸�?DF �d���.ʠ,D����&`�+I�Im��G���?DS@{�D� |�`7���}�ԋbN��;@I9��}��Imgo�k�.\dN �d�b	}7 z*��}��||'\v���b'��+�g]@y*J!��� n��.�"��m���<�z~�TI9�
}C����h~�SMdHn4Z�TI+�-���&�dĴ#n֋58�]v&�����aƔ
�Ǵh~|@�a�K�j<2݋� n&����E	2�]vt���f�Q�ȴ#n���ɴ#n� ����#|�+�N��4\dP@)!˔)u*���|�
�m1�T�h�ҋb��i~C��$�A��dwg�;u��.D�Z��}��i& 
�Q�I-��6��b'���i*�$���ҜdCA�d[�t����:��d���Ad��Ad�e�@7�J�m���iє}*��`<�Av� |� 2d@ � )�T�d"i*JE���bq`�i�ۯ�Կ>�T�&Ք n�>֔�xW{�
�,��/D"��<��+g�T"D_F�&��%D��Ad"�zqi8߈�d �"GԜdP�(!aa��o-�Ւ�ԣr��+`7���$D�ۯ�=gP��w�
i8�T�jH@	 0 �<�]vh�`m�~q��i	@���e�AC)�0�&�T�h]dO��v1��i�GXEC)<�JcC�%�4Ed&f�n�i~��`�&D�!؁�@�h%��pD�L�T"D!��z&@¢I��d��%D��/D�T���T�$�''n+!�!nN`G��"nA�<u)��o�T�b�a	o� |9� |�| ׁ�m#D{@������A�&]�.�����)uN@Mm2�Ad�&|���`J�o!a	f���{g@���'Df��%�)�s^d
@�&�4Z���h�-g�Cd�H�b��7�"|
 �8�m*c_�Am~��o
`1D�t#D��m8a�k~�`;|`��z�T"DX�#|��&{�m8� ��1��xT@Mm��
}�jN@���}��iVA[�ZL�a���i��fP��e�4�-�T@9(�<���?�#�>�E�b���ܜ���@����k~����"`#D��Ǭ)�|X�.!`���)���T@9�)��M9U����}��&���t���+Nm�+!�C�j��*R ��Vd�$���HdA��$�(;�#n
�ʣ�t#D�TM�)��z��Cd́�h;��dd����z�m~�`~��z�&�_$�C}���(|�S�C�b�M9CaDD�T��U�)B`�i�8���xf�W�$:�CdM���C���U�i	�o
U�zi&%|$n��#|L�:gP���  � %|$nC��'
}w"�|_ )V��-Om�u#<�Jc53D��:g�(nW��|�m8���Uve��f>@���@�d5�-.W�fa�dfUg|��)|��x&{��@Dv7�(fZ�'f�U�hba�@#g�a�u�z� �& ۴n;g� �I� @�aOA�<Sׂ(�&��oU�`z�j�0��j"��$C�Am<��d�(n����){!�i��):��^��&�M@&n���8U)DJ�F�
�b~��T;d���!3D�oگ���b�)|R���U�`�>�Ӣ�f���`�>�#�N��x�Am| ݁�#�i�"�禁�� 5�&� %|!U���)"5%|&��e �- �&�� & �#U�zPT&ac��I`�R�x/��|& ��]�[mӾ� ۴��h����Xv�z��fC�&����Õd$5֢���"g,�a~W�Id%�;g&�) c5nB�-<]�H gw'�
 j��R�@Gԕd��&(�hM�j���`)�-3]��<f�}W��dVA_�! �o*U�>��+�hI��d,՟$�\-�c~|%���Lf`��i�z�F]m��d]`5|.U�,�s&�/5�z0Ĵ�G�;%!�&�Ɵ$0`���`"!A&|D��[P@�15�&2�Cmz!^m3$giE�,A�ܥc~7Q_� =/!�54��`�c~e���Ȧ�ڥ��fP�+Dah-$��xg �5��b6՛k�A�,7���8��z9��h:5�&�f�a$r;զd!��+�$g†X�$u0�'���"&�&��o��㑉@veA�D9<��||@#���)�M��w"|xLłbq@?�Va�8g�&�6ne��'Ja5Z���f�� ۴=U�c��(n<�}N`x�>U�c7	)?$gq@?�f����~@��z��C��+nO�8�d �+!�fo��+|�&�д��;�Aթj3�=�(n��s߆MdB��`�Å
�C���Cu�b�ze��$*���@����k4E�>Du�bEU-n��|��}��kF�&�MdR@R9��)nI@��� �jG�}7`e~Hu�bI�o0�9؅�o�#�	E0��$���-D:Md$gq@��%�~J5�&��	o����mPDm=/)�g>gkDr8���V!�|]):�`ȯ��|Va�kI`%�K�}�b#����V/e�-|��M"V� z0%�,DL�}I`��MU DN�)|3�	}��fPOU�z&�_P5.|��ʕQ�	}��{eR�D9�4GSU DF�����{�X�b� .n&`��]��TU@9�d�|��� c8DU�j�ʔC�*.nY��A2.n:��`R�FN[�o`�bVU�z�A�'h@)V�[D�=WU�z�Fm��&2�`�J-n@�/������X��>:�}����A��Y��xI�Q� k��|	`�ڋ	o�F
}�%۴Va�|��2ZU}eq�&R�9�f��x�o[u51 �Y�������*�w?�̙�\�*|*�w?-�_:�����.D �]�Id^U D�f��Ȩ�d��a_5.n`�G9e �b���ka�}b5.|
@�z(&cտ>@��/!�_�#��7�a/��|dU}e#feU+D;a8Df�/D���jg5�b!!�~e"Um:�}hU)@�%i��x&��`�B)H�aeju*D�dj��F��w}n����LK�#:D1����o�@)����:��d(��%����Nok�*n���d�}:�e�T�/Ds�4��@"��Am�+|���&�֣�0aT�}Q�@m�/:��.D���G9l5�`R}T}��,DW��`��2����@�gK9D���x� �`������jm�/D���<nU�xoU)p�"|q�)}r�o�
�`��)su)A�^%NA�xt5�Qi'��u-|�#���B'�v�).���@�x�
o��o �&��bw-|{!:D@�
]`!Dx-|&��yUMd}B�%��=�bwz��`�)"`Wm1i�&u-|��x{@I9��p�P�w_q"�|��ޫ��/�)m���:�H���be>
�f{51Do���Jj!|�)� Ad��o��;|�Li8sp�	U��|�9R�(,D}��:� �~�;|:Ai8����;|Ā-|&�Ǵv�:m} :D>21D�
}g�	�{M��	fg�	�L�i~��,D��"|K�)uP@�5���&
@	i;��i�)\d���`��,D���.%���A$|q"�|�uu~�)��`��)u�”Vq�h~�51D�df�u�`ebu~�#1D�-nd��b��F�B��|A{%A�w�Ui8%A����� \v���b:��e�&��h~�51D�` ���>��h���{`��a�|%%�Z��c� ~��a�$OI} ��B�-�51D
a�M�Ui8:A�u.n��x�mC2D5@$|�	�|��&f�&5@$n*���OdebWm%`.n���ݑ�-D�#�iM�x�U�`;�Gd�A�h��b�U�`0�$7`}���c
�x&��I`%|���|�͒���`��i~f���8g���z���&�v~C!"|�G�w��cA�<�[vc�|�UDmc�.DM@�ݢ@o�%۴��)8�.硪
��.�&%�̘u3D��x
��,�`.n�@�a�u.n%����-�UD�5�&�Uf��zb}�F�`| ݁�۽}�`~���h��,%��6F !W��c��ݪ�j�U�<�/�J�Ū8�j.|bB�-��Jm(��+y��y���`Em���cyeo�dx8��Km�A�?�ux8��Xm|@�R� S�ڴ��)���&��8%�$]Fm�dx8���$D�Fmt��OVa�~� ����|����'$��&�I�`�@�j�U/|c�|��h����eۯb'�Ā-| �)�5W?��F9*k����n�����Zm���+��zx(u����&��|��Km�A�`|Ò]@fe�~qa}fH���b��}_�f#"��k[�k��b���������h�f��)�Ղyn��%٢}�`�c���s��b��&O�[�
���K)/��)��&�%X�?:�8�7��aV/�u�~Va�|� >n�u1D3 ��P�$�)�����g�fO����zS�z1�[m>�n�˴q�-%�&D<�[m��|�|�ځA )��zC@$���`�n�Ug!څF9�۹��[mI?�d@�c]�g#�S�-e����&�U�>ߦxȣD�-�[m�~�@��h)�F+��X-C��w���<�bae
)R �b�j*#!��
�F��ng�F+��>D7�`�u)��k7�k�@{�B��&	2D*����2DR�Zm�`���5\m���|�Ղy!�K)/f@�~1@��]�d*s�+%2D|Ò��k����2D�`ie�S`8a�' � g>�nǕ�h{fUT;�#�:���'=���Y�Om�!o��m8e�[m�]m�U�b��'D���~c#�`�.>�bv@6��@��&IMɱ�	˕�嘠|*g@��b�:g��h�U�z����Om��M9��:g�b
)��Hm#i#�թx��$��M9)`�%�թx=�tl��h9�y��Hm��M9���|�"3D�]m�TI+��&��h�U�hmOm�UI+�5Nm��Im���hzKVB�/�	������y:J�3`���5\m] !�uCm�@����k:!��ߵ�&@��<`�Ƙ�`����h�Z�q�{~�D���Ad��1���P@H�1{�a�z~�U)��-顰*�$ug���)|��z�58D��)n�)>@}7���;�Tܺ��-M@m�g�i�:gVa�|
�"]m�5bf?�7@�&�5��]m� 8D�`ɱ��}~I�I��]m{��n1]m߅}�+�z3�f8�]m7��&�5�W�U�hU2�uI�U�z`j\?]��z�{~�U)� �c�58:��n�8��5yq9ұ8����{w"�%�C����zP��C!"|�U�bwB�8�<p|~����o�B]mW�c �(���.W��?��~�U�x�Ն?|��H�|��i`hd�U}~A@��W�|��S�
B�--�8�?ʦ� 8:]� ��=B�x�u5nVa�|�a���J�&� ]�8��E�5)e`�8����H�9n� �7�Cv�u^m˥ɣ���`
�*f$&�̭>�u|~��_9�U�zP@�8N�_9�58DR@�z&�ݴv|~�6|��|(�6~~��'d��6�&�C6|ՃC����b���ށA�$"@`#� ����j*a�5yDvM�
C� D)ݣ:����`0�8��ov�h	�_9B�c>Dd
A�~��|
�Zd��}*$u
}{�o
�n���j��`c�e��ᆚ1��â(�n]���$DD%a����ˣ� D`�-651�)vCm@�0h�`Y�R@�ݐ`�z�jK4��h��|�|��	�me6��A��D�@�xJ�ã�a��1@/��Km%a��]�D"]`�{@:vwb�k��dA"�8���&��zĀ-|f�|u؞�)֢j�_9���X��:|;��"��Fm�)������e��J-|&��:�K�����?@;Dd@-|�@���_9]@;D�޸U)G;DF�ɣV!�8�d棓AKd.��n�%7�,��O��|V-|�J&K@д6�&�@�6~~.$�ݛb@z@,I�j	�� =WM9V`#N0�-�M9VB};�ʕ�@�	�}|��8V"D�_� ��ֱ*�} ��k!�-D���y:���$`\d"V@9#�M9��<�H��<��)$�
�W��d%V D&�|��Է�-D��즒�'V"D�`	{(�M9A@��)�j�Co�B@+V�b*V D+�be,��b�x-�@mʠ8ش!�l��?�E;D�!��>�|o�1J�5Z� ���beaCo��*.v)݀beAJ���*/�<D0V D���h1V;DL�8|��-Du��b��82�@m<b8D_�*��z!A�{I��|! �J3V�c4�<D�}o�<�d��'"��<C`"j5�z��|��I+ROmT��b�;|�d!Dg���6v)5��&g�)g}`)*�"|7V}g �"��<o� 
�M�/D"�$������`�b�2A�c[�j8��b9��j7@Fd�Y�/V D���|:v8DjD�`�-g�'<n1� �lC�c6j)�bj�›k���`vc7��`;"D<��aW@��:!�]� :D-
)|��n`}�@{�3)U%�/�#�k�8���H=V9D=��a>���?VVm@v>|AV9D:�����`�
�>��l�|@��*=|kQ�/G=nB)�@{u�*)�ᦽ�~��-CV�cDv�,:aCm�O�,�OmC���E��b{=|A�aa�|�Fv��	fO�4��զ,	��3G��=�&W*��|G��&OH��@�>N@4@(��o�A��6��>H�k*��|����@{�A�z%I��b|��	���|qЦ,�UVm���|�P�cJ6)��������@{�K�����|��i&��.L��|!�S \\��Am���`M�}H��/��-&����hzɻxՀm8��;nNֻxjm~�L-gs�i!���,O)& ��] )`�$xk>|���߂}��$n�a>|i���a��€����i(��j��Id�
)�|P�y0�>7`>nI?�I�Id@��]@�>Q�)g��|��ǴR�b0E�>�༓w[�;�L>�br�
�ɣS��,0�b�TZ�n�8�¦,TV2D����f��W�-N@�zn@�z1�BU��&V֦>%��-� ����;|< W�8حҊ����X��>����@$|3a<nY6X9Z�;|��zM��9�k���>��W )C����)!aw~��b��f#�����[�bq��i\��$"@-gI�3͜@�]6X9� 'R@�L��@v��b �8ج�f#w��J�RDm�Ae^V-g_�8�2�@vd@����ڤɣ�j@a߂}@y� z1��F9`�}a��$��'bV	)c�@v�X�w�=�@r�q�@d��oC����i�@ 3�Jd��#d�oi��>Too� e6�&��-e�b��~z���1D2D~�@d��-!g��P��� �bfvEm!x*v��g6�-�A$|����b�`)u����O�h�	oi��j
��x�@��S�j�Ad�!\di@{�k�o�ˮB��C�l�<DY�=D{ �&m�	}�	on��ji�!'o}7��$U��|э��`<|pV}�`p�0��$�AoI��3|��$>|qV�-�@v��@vr�oi;|J�) ��::�fb�og���A�)���ނ�|���)@_�<D����B��|��@v�Ï-��'DHa�."�<D��)`þs�)To���?W�!'<�A�^!>|t}u� �@���Ymd ��<@	;v6�b��p}]`)wV	)xvEmQ�Bd<� y��b���_ �-?-u�ay�%@�& ozv%|��3�{V�b�`%nb����Âk�|V�+렔N֠��`	iM � W��|SMdq o}�	}�a�
B��|ߢ��w@��f-u߃�'~��j��`�v�b�V?�����Zm�}�c���)�V�i�b��/�Kd��R+Рt���Av�Fm<�AЕ�>D�6>|�։b��9�_���?D��iw�V�&�_�%�Cd�_"̈́-|;`��{�-n�J��:AeڊVCa�6>|��M"�b�?�V�&��GRs���Mm�љbq)D	���;uAz��H��""N���.�VDv�iwn�{
�Zm��m8(�m8q�$|.«j�`#�wB[�Р�8
�Zm��&���j��Cd��?y��d ��o�~@��� ��…�+�`W2�� �c{�X@DvI��"��� ���y3��c� !C� I�����c��� f�|4Gm;@�H�'a���/�-�Vj�)DK&��Hm�6Gm�p�����`��=��D"�v)���b� i⠊b�)|��$Ai1�v)w�ɣK@�oVa�f� X"��$:�I9�D)1��hI���|���b��Nm0�@v�&� Az�\�i5dEv��iwg��ט��-��Jm
�΀ u���b�]mV����เ�ֵ~_�oz���A
}�%�.o���bW��~I��U��J����� ��!gN u0��ImG�)|�!�9	 !� �a#"g��k�A����֭��Av\�|�g��{a*D��@d^Fv�V
o�lc��t��o@)Dq�]9�6\mH�$���,�A�S�\P��D���z
$װ�6)�����`Ed~jNd�V)�~��z�v���| �iC�)
�AdM��hC�o���b��$��o���*�=���o�|���k��n��࠺I�V
}�����2�Hm�Fdg&'�b�b$���B��'�\dJ����Bd(
f����f��'�e^m��լFd@�h��&��Xm�@H|��	f�&�o�e���&>��?of���a���� �V)D �-��XmJ�L%|��H1�Jm�Ť����y���	_c�c�t�f�b���F@�,y&��K�ٸ,�Im���	��Im5��Y)��#i�6Gd���)@ɰ&{���}tF"���a@�`��zR�	`�6Gd�@�bDFme���g@�� ��KmW��b����V�A�m^m��|�6Gd�Km@��Fv@��;Uj���Cv��Rɺ��&D�b�$|�Ʋ�V�#u"OFa&Y�!�<���c���jTT�8��G"�V�`��8�6�b��)�֍$��8g����v~~m!� J�L%�T�$�cb<��Ⱦ�Ym:a�m�����$u�ĩxd�k� D�$g��Jm;�f��}!�<�%�����1�&��#u�8�voX D�Dd"��i��8a �b���#gs��Y��0��CvW`X�� D��`cc$g�V�>�Hv�.%��@۽G�Hv�,D�Ar8Lme<ϼ~�B�b8	ٷVa�f��S�F��Ɩ	},�8�6�hq}D�.D%a����|�@�b%�H��ǥ�֋b����O�8�$u��Km!౴�`e�W��Cv� ����x@��f@�a3�#g'΅jR�_9� �z�|�m1<�=����`<`��Y�#u;�tR��_9�$u��m1<�H��.D�COm��fW�)YG����A`�~�$g�)�
�k8�}0@)��)y��ζ�z��?X	 D�`
)�֧�6�&�� MF����+�Vd~�րbCݕ;��)���h�:4��y�|w�Fh��-D@�+��jo�ݙg�`
�;nW��`�B"DuSFd��M+e��<�)]��<I���U�h�b�&�@"D|��7d o�րb!\m��|�e��v�b́f8�&NmV[�3�&�� ��	���'a�n�b�;||@�+��)]@&u� � �.D%�̆�4b��&��� �Y{ ����&�(Ed>��|,�Jd�v�b�V�z���YR`�`W`�?]FX�BX�w"�%X@)!�V-|��;|W%q�OmR�`
o�VFd
a�i� �OVK"߶)<�J�V
oߡJd���n��kJ�U����`UdJa��w�ԣ	��0t.�Rq��$L�&�*'A`�~��� N� �No%�'A�hv�[�ae��`O��h|�;|�8R�ٕ�[m�����vUd��>{GFvf�<-/Z�#Nm�e�Y�b��B�+'�6\m�A�?1@��	�`�
}��G"��R�'oFˆ@��6Nm�Jd�֩c�V�h�h%`�b<�Jd%�5i�}l�&~�:���'�ֻc_@�{&��ݘ�f8�����
)b�
�O��`f`Cة �?�'���'��
).��h�ֿ>�@�~sB��7�
)�Ơ%�"�<q@��}�3b�vY��s�?$n%!S�!�.~�c!Ҥ`mW ��8ѣ��`��������vݣu�Hvo� �ք$�`{�
}S�{K�7��V�xcaGv�o@�0	�|���b�"��.�/ �(gf{��Kd�A�-*�� a#�{�:!���B��U��a'!q��xv ����� 	�|݀�hP`�����W�$:��(g(�մ��(u�C�6�ݕ X֯�-|�G"t��hVA[�ӡ�-�v��ĩ(l�^9���~�@�?��&7 !M�(u�ֿ,V�,M`��	��_��(g��`9DŽ|��� ��Hd[�p���^9�v�bP�n��'#��Ӣ7�?:� �i�57@�hP�q�O�d>`��7`eey�{~`�`��j�@|��?�༓ ��&f�I ���Ci8�Id ������|�C��n����*#�~`�٣B��
�e#�lJ��`w��X�m8�`�a5�ɤ�[�d@������{�6�i~W}<�9�@��q�C�7�`W ����>D�{��{��W�e���`�`\c1�HS`�1{��!K�: Zm���hw�f�|	�)
W}J@�8�6��W}�w�bA)!
�f
�k�b�vٞ)@ѸN�c7`|~D��Y�B���#������7`ee- 4m�@e���9��z�"�F��!g�-g�hW@��w�b���h��&��ŐOdCa��<�9f$ߣ	��>w�_�R+!�K�0��f��k�*uq�-�[�4�"uy�%D@���
���&��Ѡ�$�?@��C!N"R�;g��i�XdM�e#
*#N@[�\�4��A}~���~/'��|�$΁���`�C���Kv�#�xf�Xd�Kd�)�@&�#Cj��#g�)I`e|���z<�9��`{��%f��)�zW`17�'D�Kv���.`�?.B�-€�ݑ��`��$��˿%})֒�`J{�I��h���hh�hN�g�Ҥ2i� 
a�,@�-R�v07�hW�`�b;| �Wd��������Vm	�Kv�Kd��� ��z<�'D)|���(+�h ����p���%Ăk�$g/%�C8G�(!7��CaXm"�he�A�b���h��#g#7�b���k$W�<%�#g,�z�I9��|�[�be@�?SA�� ��<	�-`�{9��{&-gq�j
ˣ&C?�IMd���(eV�k"@i#��>I�>`��V!|8�+g"@i#'�f�
Md"`we(W�$)�f}�'D���f�Kv���h
��i*�Kd��h�
I9|��B�>+�Av(Md���!遱�#g�-gŅ�Yi "�Cv��|�,):q�M"<� �AZP� Umq$��H`we��UĠ1'&����N�>T@�.W�bfHf����ܐ���% �z��&�
��� ��g�龂M"��;g-�be(W�$qB��.�j�  g�>�[va;nza�>��k���w@؀/�z._�t����;u�
�b0�1'1��?< �f2��zW@��3�ԣ���h�-g4��h8�4G�C��bNd/��jk��x�Ef5��z�a8D@&g�G#䀧�=�@+:ũ(��C��)DS��b`\d���b��{lV)���  fd�M�-g]@f�@ؖWH�+%A��6W�$7w\d8�`9wNd�T)U���[�f�):w�b���;w\d"�.���z�Ek�{AVm<W����h��~�qNd@2ÏeDDX�.��W%�m8R[�=��zJad�>�)|�Y?�m8���hRH�z€�P�'�R�/@��xe@_���t\3k'g,U�z� <i�bEdA� �b/
�v����t'gB��i��>�s/��iC�b�!s89N]dM&��FU"7�
�`�AVmDw8DEWde
A�iF��b�
}a��{�a8Dd`�<'	�m����GW@9aNd��{�k��bn�-n$�ke�JOd :D~���X@Vmm��bHwNvq���A)���d���bC��j�D�0�Ym��(���xI78DK��#�`#I����@]"]� &��iy%>gJ�F"ZA)O�z�KW�zLW�h4E)A�o��ƿM7�bH�Xmg�}@;��5�A9D;�x��$�|W ���`�<N�f1OW+D!�E�"�.O!�gC�f#b"�fPW�h��f���bQ��b�@�	��
�(g��)R�Xmq@+DSWOdT�	}UW�hn��x9	 7)!e@]d]}V�	}])!W�}�M-X�keUeW��AmH��-�XmD������ f �f#pĵY7Ud@�&>�p��	2ˠ�{�Mm_�bf�Z�	o[��h\WmeS�YmF ߣ����3��5MmS`��K��_!!�a��+�'�+F`)���+� Uv]�)uC!Bmn�o^}V�-;F�8_�Ym- #�7�?)��`�	f+hF��D�z�R�ha�-Db��`�@�&�fcaR"J���|�{� ̴��fJ��ݷ�	f��� 6��cWVdǁ�m�o�x�AVvd�z��{e�`# ���W�-D]�)u�o��SB�@+����z�QDmC²��he�o}�,Df-n����R�����(< ���bW�{<� �#���c*!!������hgWVdq�+!���/i�d�e��b�l*!�
�z��}	/@�%Mmf�%|��.D�e*!hw\mN�|i��I"�-W�-:��b]@me�AVviw�-S�S�jw)�eif�)��M9�ŰY�"�hkWI"l�Ym{��,]�@mm�)uJ�b�n7�h,FI"���+o)Ja���<�
�depWI"z��%U�q��bP�����br׍$�Bi#�y%:���s�cѥ���j��u:P����Am���z��b� �btW
}z!�-�*�bu�
گ�ڣ]@i# ���܇A)�鿴�#����/�<u��Ivv�}���C!�zw�h~RҴ�Bx7)F@
}yWDm"��/VEDmU��,��aEmn@�%ԈF��I������k:���F£�rEm���%g`���a}&���_��/	 �R��Zi�£z�ZmR`�i�v#C��W7��	f ��{��b4�F�q��>@&|W
}d����U
}�
)}W
o~�
)>�iwq��a���w�	�b�%)����qoe'�����Y����#$d��W�<]&ĩ��"�I�je|��H�7����o|���:� �A\�Xv�Ui1��h�KI"I`En���i���`Pd�nR`'DW�&���@�cq`���I�����b���m�AmC`s�р�>�
f���>�Bm��'���恅�>DP���1_ḿ�,"�F9=s�bg�Xv?��݆W�`F࿴w�j0��%�7����Bm�wUd�&KC&�Fa��`W��%�c5g��� �W}р9'���d 2��YdI��b�GZ�@�c� ck�s�b�
fa{�7\m���j�e�b��(�xHg!��i���i�w�bH�`�B�<���T�
2y`1D��Xd(�
�>:�}m�X"A`ݑ�Xv��-!��}��)�Vd��?D�w5u�w�b�w5u��*�S��z���`��4Z�W�&W��(��4���vfH��$݂����FP�@3�n�z�8'v@��T��i�
fN��3/�.��9'|�>a.g{A�,4��0 �-�5��7�b€Yd"��$n�Q--4S:G��"-g'����Fm@�`�a5u�M�/�2D?cU��
f�`}%d5u;�<ܙ�}��h��%�`������!��%
���'"�܁�%�@�=�H��7�be��`�����i�i�K�i!�Wme��k�)wb�/��o�����?D��}�'3D��)ac�&���`SI9f��ݟ�Zd�D�.�@meР�<�k~m�-��Q-�Vd���z�# �̱ՠWmeq�-���c�7�b�`lP�&℟$�w^m��i���Im`.g��h��z���� �b�[v'"3D�@�b4��O7�*F'A���b���.C �㤷e8>@���e۴���y�WmeC@X��bg�< �I����D�$�fhC��c�N��m1�w^m{�?���c	`�Ր��$�=�N@�zIBۜ��$��)��$ ���A�h͡Adf��۩��%K �+n��cH�`��jw֣;�� @�z�����m#���!Em7��j)�����b�ף�F�fp�\m7�bt�|�#E9��
o�|��7<'aEMv�F9���$;�5aR�Xv�M�< \ЭwG��@=.O!�����z|	����f�@ �a�?���$< \�n�%g��&"�F9�AdZ���7�a��'���X���_��$|�p ����Xq]]v�m1|��� 4�|�䁯]v{�F9d@�ܕ@�q��>N�����Im�7�-��/!��Xv�7\v
��]�,0��2�W��|1<'����7
}�.�+�׿,��Yd�	}�@���@�h�ۘ���z7��c�	=�Q*ͺ7<'€�y��f #<i�7\d�!�+ <'��Ad<]�M��.�W�h ��wX"��� ��)�a��'�:ib�c�� {x���`��h�7
)�a�l߅�9< \��"'�Yv	���i �`�C�> �V��z��`�W�y1@��f �{�C�>n��z@��Dd��Bv���F+
)O��R � W���o�.f <iǚ���F97�Cv�W�b�A�j	6n÷:g]��.��O�l�D�`�3�g�
}�Vmg��xw��+��D/�7
)&���Ʒ:uc!�y���g�8'J��@��H�)Ƿ�&�l���i����&��Ƣx1�H���`@zlw����`/ȷ�&q@M�W�>8�ɟˢ��� �� i8.��<�����f�,f`�cI�� ţ�x.���"I9���?lKMm���O���A�e�7��-�`۴�Ad�Y�Z	���a�kw�
� 
)�`{i`Xd��ݐ�b�I+�� �W�`�aweV �i�$��-*�g�8'$�͗�%Η[d��i~�`��&'�`ʤc�W�x�U�y�f���qdjY?�зZdїIm�!�g��A&��`����;u)�ʴzA�|�W`8w ���%�-i�]v��j��W}�שcA;��a�bOi\d�Ƹ�!�����2��9�Wde[��)d��fP�
�ח�`J�t�O!�$��&e��-�A����Hmٗ�`�w\dI9۷�b&���)��jHA��G��H����WFd��j]`<u�7�Q�@Dv3�$�wDDW��cC�Fm@����&��I9>�<��`�b�WMm�`�c��[d� �f���ى@�f  �'F��<��!g�թc�D5���$@���@9D�a���y1�9�F�=�a�"gPh˕��x�	�9�aX9�wd{1@ĭf�մ�W]dCA9:��x�)����a��,�-G�zA��n>U=u�W=u�w5uC�����f�@K+a@۽�rEd"@`*3G`8�z	�j	`�}{A]v
����lEd��9�a8D	�g$##`�wDD�h棤EF�.�''BY+�ʴ�w�|��Am�p5g������I@�oi`�|5D]v��z*�+v ;����b/wEd��`~�w\d�F=g�`<g���z�`<u���Y�aX+m!y�����`��Ym<`�S��b
̣�G�`)€hӁ5�<� I�f�W�oo@{����t��:�ɠP�����"g��z���ŀ ��f�ܒ������fJ`���@
o���oA]va :�F=u<� V�cq5u:A7�F"f�|���)�G=uw ��W�~*L��`dM?�;`5GV�h��`��*if��-�)Oa�l�{����1�z�bcd�@�`q�z!c87MmO�D�)��n��>!�9|w@"�� �We�����b�ՐU��`�W�,C����_��c�ܭ[��D�,�B�&q��>�c�|0@۽�]����e�q��a~�_ :D|���V!�f-�zHA������6���� �a��Y�i`�fg���שx�GF��Ī�W�c�$g�s�nZ�_"��;n�w�&�7�Y� D@�`�A�-_dm�f#@	}��k�W�&���A�)q�b��>�A�9���?࠯�)����$�C� |w�;|��	��>��zeqb�i����w>�;|���f�ωk�`ۦc�>�
��W@�c'�c~�׉k�%���z�bW 
�࣠$oV`8�k�W�%M�����z{�m8��C���&7 �>l�C@?�X�,��)	��:�� |�E���[�`8o���I"���ff@"D��+W{�	�>��&!T�?3:a�J?�ˀId�U��+��eg��v�PՀM9DP�?��`��`~�t&i`Xd��<DX-gI�q��a\m@9!��z�"n�{&`��]@]m7�?!@}���X��3���@9��<��!ߣ���F��>�`��%�,��>���	�Am@?��`���`A)R[�m�@����d�o> |w(��
�`,�bS���
`)�`�cCA-g��!�6h�a&�C�	W@?��$n��۬#�b�$|���h��&
�=D<�@J��h@"DD���'��H�������{]��x�@�c;@a���gC)�"n
�Am��d��jF`�aC��q�}��v蕀���A���`~eS���a�bN�j&�aR�j�8�h�$|�� W���(@��IdK@�@�zI��Mn <i]�A;�A$|T�,X9!7�$D��:g��`f �b/H'�"�| D�hX�z� �bX{�Rt�	Q˕�$|�d�XB9V�>DL��c&��&�`�f���|���rG�Q��b�S��"���F��x�:gJ���X�yTI�ki�b�o��Am�:g�!��؆$��a~W�ۦ�ԁ�Am�AmЀ�G�)g��&J����g�X)�+�YI��"�A�'��%X)|�!�*j�M <iV_�|�"@�y��`c�'3w�e`�f�ן$d@)X)q@�����E�Md��b���Cm�A�{A�hX)�ō?'fF��I��:42R F9
@��:A��>@����:u߁f�c~�?D�@Z8��dc�cЅg� )���R`�>7 �?o��܇���R�Bm;� D2�ã)Ⰸ�:gF��+I@����`F � !�oz�:gI�|"ثx� р܅�.I{�#)$ثj%me��a�a{(�o�)ą�.<P��7�d*��?&�}"�o1��f��x'X�-(�}@�`R ��&��0�|�v�7���ib"$'o��,�m<g)�c(=I`��П$��x�	�`�)g*X�x>�C+ح>C�m1d���B�c;�`��{�$,x<g-��&���`�x�5aJ���v�\�?���{�?D�#4�.�Cm�d�>/�c~\��<0�c~K�렇m11Xf2��`&�Y�U���8j�&��UA7@Md3Xmey���v�ZE�����(�Ç�a�-�c~4X���A�S�z����hW�|GKmw� �i`�|58�%�j6��c#D�m���%���<"��-*!�k�Ad7Xme@Md����'��>v[�8X�,9X�cJ�hy ):�{e���)F � I@��T�j�fĀ%�Zd��y܅�.>A 3��{;x)�#)�@g#<Xf=�-eGۦ>x�&��
��z?8j8@��x���zޡ�c ��xR̀kVA[����D Co\@me�Y�w�8D��?�	�(��F`N	}&�Bd��&A�-����%@i*B��kC�zJ�>CFm�@�cDx�&EX};��F�&�H����GXmeC Upa+a8Em��
� �Ao����
��c�l�|Hx�b*���� �?�����Bd��>!zAI9g�F+J���x��iC���蠹��  a-gc+a��[d��?�-uaUmf��co�Dm���qoeg�F+(�y�F+��zI�k~J-u+
��ޫ¢K�{e��Ym�oei�&aI�0�R���@�akoeR���X��H'_Od6%�bL��j/A�%MXf~n���N�>O�zf��fe��>
��&?Sj]�`�_�oeaDdp��'!�c�mV~:a:�PXo� {z���h��}&����A�E��QG�.Q��j&��@�a�	}] �$%�-!���b�)|�i�/��') 	_�@�kR8��S�	}����
@�a�A@d%�@m��ce�l%ő"g�$�k��a+a���h��g8�f8Va�|��oT��i�F9"��i�b�7 �yU�@moe
AڣV؄-r���`=����d�Od\5�
��<P�S��"�zW �|] �y.��:��&�*in��bW8�zX�}y�	}:aEEi@ۦN�Y��gs�7��.Y8
�eGm�@�xZ��`*�=�҄}[��j�sg~g�c{�%a�ƭ�\�������]��x^�hb`F�_�	}I`J��=�`�c��h 
���cJ��a��h���b�@m�%aJjz��'��@m�j�o �|��5GJ���mc؄?B�m8d�)C!Ed:{���G+�v}e�z�$�A��U��*�䀍$w`ڢ��I�b"��k�K��P@��fX�j�E�<P`��W^�"�hR`�| �Aa��f@��`���訅�O�@mHI9� �|�)�I9<@�Μ�d�=!!�=�ZmϏ@m�g!ב"ugx�bO$g��<�ڤɣB��chؤ>iةcj�oq@�-i@ۦ:�b���,kX�-&�%Z5S��9�`�<�ĩcl��h瓡i�-ad�Odq�,J�L%��Am`�z�>m��?@an��Q�%����E$goX�%(��%pX$g��}��F��,��kK`�	{�"nq�}r�	��<��s�iI@��;`��g`�/s�}{F�7��zn���d�o����zw`�(��@aİy�}
{��`#���(��<bV	)��tةc�>�����c���-nt�?uةcX�#���QW�"|�Y�JA�bC���v�U2P�4��)w��-x�BmW�Jm`J?��	}܂�z,fC9���y
oz��-<@������z���??�>|m�+|{X�-|X�?�6}��-��N.~��?�Ȧ.��?���-/F$uϡ�,]`�h �8��}�X�-�`Gd��Ym�b���@��R�X^0�	o���>�(ovJ9���Ŀ����-���"|F@[�j���T@�'� �j�$|i@{��X�hA�z�Jm��k���c�a���Dm�`S�P��ֈ$|��#|���p��}R@�9J`F�T��&ޡ{��C�����ۊ�||��kq@�b��2'�a
ۋ��M��,f`�?i]m% 
)��$DT@	)lQ�=�۲�}�m[��}o�
}9	MmW,Em� �	@���d�(��&ѾbC9�tɪOE�I)��_)EmI���P �������g�H�B��i���h���z�-aB�-�Km���?$�4Sߣ�?����@�&
����-G�m8�)V�1ay@��f@}�����f��zC!z8�E�|c�)��KmT�N.��z!3!
!�|C@���y��ةj��{~W  �+�m~*�2'&�+&������V@�;�U�吘M9���R`#����C�/�@�|KM�i�T�cˠ%|&�p��%aDFّ8Em}�_9�4�&u,�-t�&!�@ۦ;��|��f�V_�m�c�8�-1��a�,Zi��P?�)�x�t�8�-�g?��/]���I�S�`
�B�1a]@&|�Hvѣ�|�xNm�ƦP�j�fN�%�m8& �n`�&�L%n`x-A3a|��7"ؖ�!�&�`}��}�&D&���L�k� Uv�)�P�c��/H`�y��W�F�,�iٞ�F+�`�9���m8�eĢ��"u{�%a��}��_��&D�GFmA�z�m)!�?�f�9A[�@�keB]m:�FҼ�h�z.�!g��"u�)�a��*�o�XOm ��ܠ�{~���{5��xg�+D�+�b�D�Ρ)�R?�X
o�I�" �=l��a������c�ܣX
ob0�&\{�d�z�B]m�XFm�%�-�i��r���h<@��-h-C�i�G9��>� �-��
)=mGm�Xz��c�$uiF+n��'D�nGm�2D H)!4E]m�@&|�p����P�Kd��G+�g8N}ެX$ac�%��)g��Yd�&D��F9��}e 6'R)W �iZ��<� ���x�,р�`�X�|���q�,���i|�����ef�6T:���:�6d��g����)uq�5'��-Va�|��c�]@)!
A�|1�&D�@�zUa�`��?��-a)��{T@}� �ݠ$��+�J��{%!��F�z�5@z�?]�8ثcXmg���9	I9���`�!�
��y ��S��`��\0��K`̀@{�<@��.B}~�C]m��`����zi0a�(|W��jN`Ę�T�j�<����3Ǧ%��(|�o������?��Hm��;ge�)g ����x��� �eX��i��'D���&�@��
�-J����V�P��!�#��O9P��
 	imᆙ�B�k��4a]�_+C��ݏE�`�!�mqb�>i)DI�A ^9���iߥ�iF��x�me��'���h��y�#f<���ح>q@�z:a�&����)���W�۶�)HI9|@ʀ�`��P�c�CA�>_)DP�$�*��?�H�>��ݰí>� g$�x���W��,+�C�|������x��>�ō?���-;��&2'���?R���MdC��if6a�@g#T�>_��&HI9���>� )���H���i�����Ά�$�H�/�j5�x�@�>*�͌�	�i��h����xo�c�Md-�aр�'M��{�x+ahmeH�j"�yC��>K�.��zeJa�_S�`e`{ ���Jm�`�bW��{�c�i�X-u��X�k��9Ь���&!J�����`�c[��?ȅ�'T�*�`��f�R��x�|�ح>��{�@ۦ���?��,��a1ȭ>n�o��J ����K��-Tr*&@��S`���}h'}!!�|"@�?J���}g��c�؉kI�
o�B�|W��|�����+��"�?b�Jm"�y&���{F'A�}�X�?��9;����gc���գ�O7��-!��� {�x��|��9'w@�gg!e�)Co^��?	���e�)ǘ�'zA"|%�}@@9��f�)S͉k�a���d���ˠ�-ׁ)1�Fݠ�9t��x��#g�P-u���n��Km��Km� Zm	���4E$u���>��[dv`��W��Kf`Xm]��c@K9� :!|`�H�`3!�{��; �5��Km�QX,�0o�8}�Ad�zaW?��KmJ�'�]�a��Ca)�X-u�|�x�jI`�9�6a��xZ�=���X-�`�$�@dP��>}�n�6aϘ�x��и#gј�?�L��*�=�-�X-͡,D�X�'J����x�&2'	��M��&G�����-��q��`�j'�,DP�Km����H)`!�-�-n��f͡,D�tUm�-D�D)<`��X�h�
6a-n��z�`�|����
��,%@�h�-|9�)uCA�h�}��9'�·����ؿ>-���-|N��?/T@+�x�i[)�-wB�|��{��	}N��?"
��v� �@�bUmX@)"Q�'7��a���fi�X?P�{۸,D�Vm�X�igš��)g�D�-ݸ�heGۦ��iP�H�)��("��<�vq��|@�-
a0aA�?��-eB�z*�=�}�&!�4@>��n���Y�`#7��(�ئ,��a��&!�#�X��b�x.| �j:ᗬ�dNm�8
)!�?��Q�M;Ѿ�W~
��wU	6apH£��=����z�8
)gI�e��>��c���h�X�'�-D�+YM5S �b� (f�`��V@�C�D��I�-��yȨ�
�R`%g"��<��[�7��iqB�ko����>���%&آI`)�W`�-��zC��υ��C���.�bv��z�����6ag��褢�>��,q�m0J�����	}�X�k{�%3>�	�b"�|-�g�{��>� �|o���sС�@�-� @zM�O9�8*�> Wm�W�kO��|^3
)��_"�bNm@�-��|����|�J�y{W{��b�@�{��{~�J�bNmC�cR�z���V�f{wt�&�¶+��F���i*i@ۦ����!|~�XDm��he�Bmi�
}��&J��:�x+aGʦ��h�&č?��z:��߸�z~�@��xx8�"մq�_��͢�`x8.�'>�&�ϸ�iF[�����d@o�.D3�z~��.D�ǁ��K�(|~��/��C�A"�|xŶ���`SaX9��h�x�b� �$ȣ�`�/�}~���c�lx8�UkJ���cP@���}~M�U$<���;Fe����7��c�|�a�(g�aX+^��c�@:��Xmg-ú�a��)�}{�]m:a�}��(g$��`���`�f0a�`x8��+%���S����&]`x8��Z�^B��|��|�d@Dm�ADmW��`f�!'V!�||@��
@�a��i 2a�!�xW��n�8�&��	o��}*�-a��a �o��z�F�
�h\@��X
}swx*R@�KU��@�x�g����W��f��#:A����ۨ`��S�6�%�b�z�v W*�bEm�����/MF�S|`�	�6a��?!��`��` dEm�@����b�|`恕 ��J���Y�c��_��(un�:||�ؒy%�i*A �N�`����E�+`aF@�:.�	}�oڨ�)`��}~�;��b~��S@9Y
odb
)	Y�1�bQ�Fm| �ւ
XUv���X"�&Y
}Y
oI@)�€�y�<DI��|�a�: �z���`
`�-���ng�)u+!�fxN7�
;��ʀ	٤%?E
}�떮�$
��A{?�A�aJ���
)<�N>��?"�}*��N>[��pS|K:`Xm� �jA��{:�y��e���eo�Ym�`L�"`�z����<
�;|N�	�:�P�N�	|��:a�`�@9H�k�<'<�+`�z�z&@�+I�cm8��Zm�6a��[m7 �c��i)z ��H��<ni�oa0a9�+g�:F~o����Nme�K��ٻc؀���`�R ݀ie���%9�>y�h9�-�|�)� ;L� DA�@9S�8R����|Y�k9�>�@�iw~��� 
A���ͣ��N>����M�{~�� �ih�Zm�#�b��&;����榗����` ��}�n�o.�?`���F0Oʣ9�>oA�� +!{�"|!+!C�� �n��A"9�-&��1�g���l��%M�>D.��z#
}���1$Y�k��+`1���h�%�bG`p�T�[�{~�N�l%�?&�Am>�`'y�{o�gc(�Am
�-y
o��5�|��)
}V�aX]m��?���W��J@�|N��f�&�|P���R�e����ݕ@4�:�����ie*��i��H��=W@{�+9�,L��`,�-&؀w�i�=D-9�- ����`.6a�+�,	��|�&g
��z/Y�-�-g���2'0��h� �>�A�{��&�=a19�-2Y�)@�3Y]m��/49�?
!�i5��n@I9�A�#Dm6Vm�ý��!٢҂�z���Oڇ�7��hn�'�ʀb�&� >D8��z9��&�3~~gAf:��%P�o�C�|�;�`%�`6<9�as!^mJ�|��+)��V =y���a�|6�&�;���:��VmW�Zd��ʵ�Bm�C��>�Im���c?�f8�"|@��3�:pF ��
@�h��?>�Ume,~~@y�-�H�69iU��a%A�~8��SByUmI �q��|W@��e��n�!	i���zv��W2'%$~~�`[Ҳ��z�`�?�@p��me`�yC9��P)��Q�:���I�o�nD9^mJ�Q-E6ae��y�E��F�ImG6a���&Hy�z}Ѣ�I9Wm6a�AJm�/uJDmz_q��%g�yP�H�0@����U��+e��I��W2'K9EmHDmi�?3'Wm��������Z!Wm�ɼ��@�`bb0aWoLY�xMY)�&�m� ��e$|1�|�.��|v)W�*a�>�bƮ�t�FN���)�oO9^mPY�h�J��1��k�|����J`�1���k�b�-Qy�>]me��J	�@�,R�J�S��$�עx@��達"T��?U`1)��(`bۢ(ȩc����P��nG'�x�� ���al�T]V�_9�G�|P�t3D�2'W�^9X�`ev`���a��Y�o�oZ9�y[9Wm��8a�@[�O蔢VA�|\9�j���]9�x^�� �yv�WW�/g�A��(�֢ %f�%X�b"�|�`8D_�XmW��i�`�?��>
aɀ��>`�)��`eg���a�kD0�kS��be�z�`X+!o��8�/,�80�8�bYY9c��h�"ƘI���f)S�F+��x;���d��ke�kB`�aL�:g��xJ����8afYY9_�x<`�m���%@Mm��h�X۴W�$!�&��<E�j��"g4�j<BMm2'�jSA۴��y�8a)���gy�bSMMmd���(amA)uhy�bt	�iy\dq"�j�󾣡
�("M�,��+j�Ym�AMm���I�b�t�bkY�b�c�I�jX9lYY+m��`��'D �nn��+*A��R�;u.b�i�S�f�aeo��-���fpY}�sCqY�|W@?�o{�r9DD9	�=� }v �Ͼ��R�&C��i�Z�s$gt�)�����:u�e�zP�O�46a�)uy�bg���T@MmvY�{@w�aex�|y�xzA?D}��+z�oe���Dvg�'DAIz�W�iW��i��{YMm�8`�|YY9P@�m~w"�|�`�>A༾��>�v'�`Тk&�{W��``��.������. Z9�!)�&��܊����顢}y�&I�-~�-��-�(�� )|��HmS �򘀢x��n�Y�&0%�b�Y	 �y�&����9�b�w�I9"��@�܅��-��֌VI+@�`��w�#����Âb��z��be%I+f`�x|@�'��.��Zm7W�i�)��k�9Z9�������.&�ᣴA��$u��ce����۴���܊Y�`T`�&v���`�y!D
�{F
W`�
��h��ce���zV!�agde���d;�[m�y�&���-B`�||��	��&�b&`��q@�&�g�sW��|��L�'�f�:|���>*A}!)|M=�R������;�"g���?]��`T	�k�U]ׁ�$�B.��&���-&��`y��hZ�bW��i��{��o��)P��f�oSA�_I9z+X�f@�z��8��d��Rg!���x�Y7�V�|ieJ+W��{�9�b���&=a<@Cd�Y�`�(a��`L��|�����F��(	��|]i+(a�Y]md`�i`�%�Y]mw��W��|W�}y��h��`R`�OA༾���'�y\m�z���b���&��&i�B?��|jh\m�@m~���c��b���
����|TF=n�Z���`���c)"a8:`�&��?3B@�x��Hz �m�!5g:��3:�i%M#'֞9�bg�v���遺@���`��i༾�9j���&���DF�k>#6̡ٛk�a摦��\�G�y̸>9'�S� ��C���&"@�yI�/��@����"u�G��l��c��)�=a�Y]m3��<h��&���i:����=D�ٛk:@{�́o<@�����(�țk��
�<@�g��cq�?��`�i8ߥ�n&��Y`�&�}�=DW��i��3H�������dKk|`�Ƅ?�z�'���YaKk�o��)S�_FuBg@���Au�FG]�Q�$"�=D�Gq�W5�o�Y$u'H�y(�)��.aG�%Mm<��}�9^m�`� t�� t��呮�}`ek��^+O%��y)����Km�|�u�J�;<�j%�@�/��W@�n}�^9��)���bg��cF`[�`�c@X�&��(.���׳�`��E����(�o9��3Ejˇ��(�Y�h�Y�z���c<_��&��V��M9J�g�P��
�;g��aQ��&A@$g��;u�f�@&�ϡ�`d >|�H�!f{dF�9��s�)g�;g�g���9oM")�`Y��`)���i>@�����&"`�`J༈II��@7����`v�������j� !>����|��}o�Hd� ��-@)Ca��f{K`ۢ(}��]�>DzA�z�2'l�B`�0��o�`�`"��<�8gD���g#��o�i��� ɣG�J!gP��@d��`eC��&���b��w��Y�h��||`��b�b[�s_��`0 �,�_9S�he�y�v¹8g��W�V�`ev�W�&��_;I51�y��� Up���h�������ە�W�N`��ę)u��|3�$�o����$���b�䩳&`�n#c�CŹ�`�۫�oe��Ϫ�]�b�AmX`�cy��j�y�&��	}X�he�@�b��<qA�c�y�z�9}����#8|���z@�n%��y�9}w"�aC�Ǐ�@�8�٩x ��(��>�{w̙�xA@}�m#��?Ff�93�Y�c�@f<;������i�n�&�YOm�+�b�&��j�c&Hi#{�8�(�����iHi#@�/q��x��+�YDm�éx鈣�W�Ek7@b1��/f�@v���h��h�٩j1@��v`���`ef��{레b�D�y�YDmg���J�����&K�%����֙}�9�bn`�i��&��M��Φ(ʃ��-C��z����M���А@��}b�X�w��!`���&�	��}b"�z��cW,�bg%٢�zJ���]�c��{�B۴�y�i�@�ݙ�%.�/ޙ�%�@oy`��a,�T�%'+��y�&��<&&�+��ie�Y
}��Q�I`
�S@��&�O��&J��>�/D�0?�>�I-����ae�Y
o��&d�-��]�|����%䨔4VAۡ
�<�5�b*��&��ld�2@�{v`�Ǣ�&�z�`c-uc#�z��;u��ɴ�ADm��z��''��%Ca<��9���&�YDm�-!@�iJ༈3��I��/�o.�}�Cv@{��-u�F
o�Z_��i�|�)f����Cv$ �i�����)	��|�Cv&��nU�ce�ًb�`������eb�{�	o��ke�Ð <gD�	}�Dv��Ӏ�'��b��&S��b� ���ce��}`���Y9�:u�Dd�bn�aw ����ke�Dd��>1�h���@���b@�z�jۯ��+;�CvC!�i�M�6Ju3�@�>��F+�F�{%��b�@�?q`���baC�i�Ey�A}J �&�0?��me%�x����'g=u �ͳ!ze�Y�iI@o���b��p%a���S|����kۮ��b
A{~�ke�����%�C��`!!€�@�%��	�W�;u�E����)�
)렸����aC�h��|q@��C�-|P�#�V�'�+g�@�{3�m1J �i��`q@������	�x�d©cI���B�ިB����B�J@^�I@�Y��٭,��}|�΀�9DDk��/�@m-@�c��z€� �{1@���	%��c�������I�ne�����kF��%�F+ތsI�@��Yfe6N�c�b	6a��T��`���hw���ŭ,��g�)"��>����YfeH�z7`j*WiZfe�ane*���2@{��{
 �{���e�b�@fe=���Amy�	�]��`�
�`oi夿��)��"u3�xӕ�v�A�Z�xK@ң��	%$�&��|��v;Afe��Bm�"u���|��
��cm0uBZ�xA�ՑFU����?�ene��Ի �{�ӣ�h ��Am�OtI�F�)]m�`�{F�i|��M&U}e���`

}f2'�F"|�H�T-u��״���kf��<)���z�-�)½�3��̈́��P�sf�d��bʦF���{��hI���q��cq�g��!��:�&Z-gD�cR�#�	�[m
Z-uJଘ��ۋ@�$��hZ�b�2�&a
�
��h��zgeM`L"K����h5�#g�[m���h� 6��Y�%�#uD)\m�?3���Od�f)���7��/����c��Hm�����@�&�(�ݣ�FmuXoe	@f��
;�f� �9W��c"�[m��Zţ�1�nd���;���%A�{��#uV�)L���&V�
�b�Hm%@I+zߣ.�����B?��r%;�h'<@� 3M )Q�
�J��I�$! �#��e�f9',��%�|�w ��g�	 zeQ�$!d ?D�#un��kz�&��k�I"o��%�Im� EmG���@I+[C��}��`�2����y�d�%�#u����Z�`	�1=B�&7�,s��z��k��	 �g#W��hڰB���a;�Hd́�hy�d���4�k�?3J�5�@�y��-] !�"δy&�&iCA)|K��dZ�cy��-��p�
���@�`��&��`���%�d�ڻc�a�n���k���0�<��u��+AdZ�`�Dm�̴�J�O�k?e�
���9ɿ, �m8���$!z�hd �o ))���$$@j�"Vk�ĭ%6�he#�`$:�&DŽ/����M`Fa%��zVA{�F���&��%��>'�heU��/��`v`��(��aC��>-��R��\�D�'��Iv�@۴"]m��kv@�):�b*ڻcr�$+�iS��&�AFmM@)|qm8�a��N�&,��`k����f��'g��`���&澴3��5-Z�&e�y�bߣ�I"�"�-�h�l&����r1�`����cbV	)e Rk`��
!ţF���{��-���'.�#/ڻcI@��S�Jd	�`0Z�$� �&1�h�"g��	}�t�V!/R`4��Dk�`4�2�z� 'g�h��z3ZMmI��a€�&*ʽ�@o4�"u���J �-5ZMm��a�K�b6ڭ%�`ڣ)f���7�Jd�E�N�}� 'g���`%@Mm8�keHf6Ωc9��`&ћk��Jd|G��	����M9d��&� �c3G9DdNf�aUd�A�&::�hVA{�;Z�k�ϩc����ke`��SMm1��i�U�kA��܎���ށG-�ؽ1<��`�aUd=:Nm.�X�i��W`�b=�Ym�@�'b�hԠ�&��<'0@`1M��<�!��7�ؕW`̛ۖyq`�1ĐFPI��3�Y���K��~�L"�_9v�keW�`	)J�&�$uW`����D9G
4��)%᤬>�c����-�bm0�n�D��Cx$@�a|H4G���
?��bK��+�� x-@z�i��q%}�_9�(�YZ�CmV!�a�Cm:��ct!�-o��x>)A��&f@}VAK"B�}�.)�DV�@?D �����i��x^�-|m��;��-�keCڂk��<i
��>D�M9Ġۉ��b��G9E�_9W@{�FڛkGZDmH�M9I){A)!(��+�`��9N`1Jz�bK�o�)�$*�tJgɥ�̥mf )c(���["V���&����	fD��9I�>��E9���%�L�`� @��˷S�`�-�m��e`/��}�����}�@c��&� �����aC�/R�
)O���ͅy%z���Lz�`�x%
����z�劗����&V�ۘ�f*Aoi�[m�S�/&�+���MFmWA�`���`�b���FNZ)q�x%�e��&��Y��J���}�����k��
)��O�(uPڢx:�J�>��A�I-;�DmJ`�1�`Nd�Q�&���Y�`�=V���+'QZ
}QE)��{b��{��q%1�L�R�}�u�X )��-��[m���+iE��
�{���v�W���i_�[mJ�C��bne#�[mI���{A)!}J��t��9���%Z�)u�+!#��d�]������S��bj�A>@���i�&���+�@oe4�;|T:Gm#�{U�E9|ހR�	��-�c۔�&%��BC{����|��jf���}�F�pFa�Vԕ�֜��/Vz�J we��bՠ�&�[�ZE��_�]9:�o���x�D����G9:tNd�lne�D�a&�WZg!�t)!!�K�} Gm0�o��+�C��	 �`XZ�&@��;`sIo�%���?A ��F�i
�{Y��bi�-_�<� �Z�A ���l�m�(`mp��2��{0��%[��a�.�-Gmgeƪ�b��@��\�{����]��i^��hP����`W��h_+!`�ZdaZ��O���%��k"�<C�T� �bM�|���-�����@�(���F{�b��zWVkF�hdiyJ |%<�,!R�bxFqӏE���s�i��\dA@�g����(����ȉ)"�<�q%>`�&{��z�
]k��}�`�"@�&�Hm�Vk��JcZ-gg��)(�I�d�P)!ll�QR`��R�;��z� 8(ذG9I ze3�ң&+i��(u�������b�ɢcH��$�F9�@-gd:�`�}���h��yE�i}��zٮ)e)3:��]�Vk	�/f�HmK �pK��+&�)uw�Ŵ"��$���1�II9�`��g�;u`ۇA����c ��."V?�F/1{�h��h�E��i�}jZ�%"`#;IF�P ��Ǚb<@�%�g@í"��z'��i�`�$%�;uI@���CaCp_�A�<C`��`Nm�A�+kZ D �j��DvB�i| ߀l�bm�)#���nZ}@?�o��z�@opZ�i�>Dr��q�}r�Hms�Jm<�oH!�tZ-g*a����2uZ-u���?Di )I s�G*��W!�`vZo�)3F��z���$eG���-nDX�Oa��Je��o
Σ���iϤze�`)K�ʀg�_+ 5�-nu�-!`���`��w�Xk񃹣���hq��&|`�^x��by�zw"��$���'	�#ۊ�ze��og�_+���nz�)u{�)gJ�����a�cz�J|z�i
a�[Mp�inG�k&���
`1�b}"`#i��i��h:�/!��	���'��g z1'ZP{�	�}z�b��D�a�<i~ڍ?���aYL��"`#]@o�Km���'�ڻj@�JmE�D��Jm^K�k�Z�`n�8�b�'I��kS��>@�zʉ�k���z�DZ�;��(��9���w�
&�`ʢ�eO-�໣��?�]k��h�D���/!�Z�cX�ze��fq�	ێ)�:�&d`�J�N�,��)�(�&���'�n���a��v@��U��8I�&��Z�>3�	�*�?�R�'@�hii�t�!�%Drʌ���h���/\��c]��`(�%<@���I9d`��Z�c̒Km�e�i&+i:A�����/���@;|�b�aJA���:g�:�&�c�a|�́�ڟ$|���Z�k�:u���*�o|�^]�Ad^��'P�4���	�H`}&�ٻ��-��˜��-��:@ې:�{��٘@7i�:�&�+!��be"��/IB{��ӟ$I���q�o�-gѭ�D�v�)��)1�&H �{�����9�Z�bf@�f ��7�R�!@}e �)&��_�Z�>�F�c]�be]�U`�bW��j�&I�cNz�I?��h�b?C;"�ide�����?!��-!_J;|Lg��!G��AVm�h�Ad|wۇ	}V�
��}���Y�-�jۖ�o&��j��C@9	{�F@���	�"nF`�R�a.gN���;����C�z<��9�C;�&�Ģ���$.@�:�Z�Hˠj�क़��9�Y?*�9���H���z
)�`���XmP����xG�Xm�z a���x*���Uwp�������aW@{��"�z9��$�(
=�C}ev���`Uv�dw� M"��<{�a
�.��$��k���a�	}��;g`ۡ��.�aٙ)"~eK����c
�]��2Ѕ1 a*��X��`7������yI��qVkF@�<��x #�-��ң-��9f`�'���$�XmqVk�M�a:��Т�{~�˙-A�/;���i�$|���9q	j���j�F���k|��R����0'��a��xa��zՀYmTɿ,Dr�(e@�/I�kg����
��>:�X��i�Om��{~<�
h1�
�>a�h���zC�d1�ecN��z�hr��:Nm�pZ�
`�h#$Fn����&F����C�Am@�a�Z�z���`:a�f�ڗw
�,���z&�~1�Om���(!{&ڭm�.�<i���z�cʌ���hb��'�k�ڻc��ց��a�@��d��O!�`V��x"=�`�45��i��)<��{�a��|ྒ�˻c"��j��)��a���z���h�8�`ڀ�j��b~i��[bF��.Aդ��=�݀�s�)��x�í�'g�V@�X|���1�k�Z�/�:�`H��i������|�Z�/���zq �c�&��ՁR��i�a�87�<�Y���]�a�G�%&�v�A."��x�#+�?�:�-���j��"u@&DR��ߓ�c���`f�2��>��9��`���r�%Do��h��xw��Y�K��z�ƻc������`���Y�%a��V!ۋ��y�t⮁y�\-%D�#(�\�XvS�9R����K૴rMme<@�Y�f���kBsf�@�x�Au7S�r����>߅\-�L�c��
z���if ۩!�BC�q%|ހ9�)
o������x��<U@7iO�U-�Z@+��"g
�>�ڋb���&���h����zC��Z�- )���yW`�C�ȓ9�F��z�ź�hA@9�F��Vm�bo��n�Z�?�
}v`7����:�-%@�%��9_��'�a}0DmJ���VdnM;a���'���<@�Ⱥze��W9��W�@���Z@9g��k�&a&��>�Zd���E�Wfk�A@�?I��<�
zH�)a�
)ws�a��P@+P��H��&�",e�zw�@�z��k"�[mN��||�ց� 8�UH	)<��ڢx�K���)��,ţ�xo`�cq`�kBq�>F�ݴ��&��m1J���Z�z� �a�,��>?�)�7�[v�ݴJ��@�,33&@�� �j���a��)�Y��Tk(`�+�࣬g &'�Z�>�`�?�Y@9 &'��<iGJC�3A?:"��z�@���Z@+�Am�}�@�>�Am4�}ԇie�Z�hO���[vf`2��M����
A#a�:�3a�-��)|v���
` 3����x�)|׺|�q�غڸ�cw�
�cc�����|����j�%��z	{�H���t�f���<�ze*��,(��:�{_i#g��?g��Uu3ں��Z�a��$aqA�z��Ԯ!&'��kv�����i�A�zU�3���j徴�	VkR-�xF�:D�cN@�iB�cݺ�,l�?��&=9!�EX���>A@�A�i�E�1��)|�S�{-!Um &'X��<ĥ�,5��?� I��@��I"n�Xm��J{�"g�C�>	�i �i�Oœ����j;�9o�)D�N�>�����)nầ,���>��y��x�hʢX��/��	}�Z�>�9!��	}�Z]d��<i���z:A��T�h�D�)�ӢT@�,0�ߴ'�jQ[�d��/��"g@����z-�|�V!/C��`�U�c�A�aXȍ$̈́�i�7�{W@}w��ʢd
&a��|�?�hR��?�I�a_)T@�,��Ym�d��%@�c���o�9�����ZDmꈶ5��ܸ�){)@?��S}e2ѻc��z�i) �n�pgA �")뚄-�a
�$e�z<�
zP��wˠ�,=N;|�ZDm��z<�
h:�9��ZVm� Um')�-����|`�ɦm�
9!�ZDm���W?�H`�z���A}eT@�,�S]d<@�κ!���Z}e�ӢcJA\�Z�q$g�@�yf�)�b��CmmC�,o@]d�ZDm$��`�Z}e�ZVm]`�`R@�z�z a�ڭ%� ��!���/a�bkc���?�A�k�YmI�M����N��{���![��zkcq�M��!!�: �c���-ŬZm;��b:�v�빥>`)���ZDmB@�{A�ie����i*�_"y$g_�m#��$F��D�Ym� ~e���h|t|����ϥ����&l,j�A��P�5��)<�
zU��hT��9�$g����Z)!H�>G�-D��	��m��@��{�i"@)!@D�`�H�zɄ�����J �-�`D|N���(Ҫ�ȧ���jg� D���&����i�[m���`ʦ�)��b���`�-��<i��:uq�}��j�dC !��Ug�*'y��*�4�<"�iFm݀be3C�3��%P����θ�"mF +a`�J�i��?:����aEm�>��W�3�?�:gw`����=1'��ZE���y a"(��{�0��x����{�"�&����y]@)!�,n�I9(�dŀ�y �bo�-�}�cV�u\�c�(`��h��E�ʀ�y	`�g�	��GN�j	;Gm�@]m>`ۺ�{�v�>o��jf$�`b��
�e�7d�J`\m���ef�=gU��+2�)g
�o�><@���j
��aZk�{B�|�;�b(����G�'7 <gC�=u�8X9���
[�';fC!Gm
apc��a�	�a��}n�8���`;X9`bc[����j
�F9��?g���p+X9B�XmVA�/d�J�Y� 3�XB�'8Q:��`~'�J���´��\-����5�i]�a�A�����y���`�!�&�������W`�>�!�{��y���%����d�Ym�`�| ����k7`�>څ��O=g� ^m��F��ؕ ��
�,��>g���'�⼁�`{�:��l;Y�}��^{�?S����÷��@h-�f�a߁�z��;e]�a[�/�@���D�&y����iД�j�{�$�w"�i� 1��s�?�'+a<P��3@�+[de7$΢�@�'C@�o����Ϣ��$��ĢC����E۽_��k�@�?( ��1@�����;.�{�D��Ym��=F-g��a*��%J�L%���'{�-q­>0@��T��>�o7p�-�i��8���i6& �zyeAd*��'Ca�?��.��x�̢�	o@�m�hMm��Zm�KI97)! [�>��i²)![�-"[I+�o.AfMAR-u`��i@�?j��%#�'ma�?�eF�$�;u| �^�g��d��NLj?y�y��W�6\mĠ�'�@Amg�Zm�3�< }f�9{�-\m'b�?	�)g��'!/'���vW�HmW \3A?:%[�z`�bA$u�����x&d~%ċk��R@�z���7w`Σ@��*��Ž9��/��ImR�`�iC!�xࠬ,'�Jm� ��@m�d~��y5��?([�>I�mcV!�M`�?!�i^`�(P`��B��-){)�D�M5Z�:\m��o���a.ag��ggn*[-u+[I9�k��p��=���̀I�mc���htB�-�@�����h,{؀gA�`H�a-��{���+���`(�蠌-a�i8��[mG�@mS��i�黣���w�c~�kU�UB�)'�A�.�ëxA$u���-|��	/��i���?C�C�W�{���?�U D
!0a��~�Y��>���N 
;�����	�]tI��q��-�CЎ�dc��,0��c���_�ec���c|��1{�h2[ D3�hA$u4[@9�'hO&D5��k1�@m��Km�Z)6��?WL�i7{)5��h���'�A�a:��9��g�8��?�+geO�yqB�c6
]m�0'H��hb�{y=�� ��[F�y�v_I�mcK���d߁�f�T@�,I��9d@�a��j������ecc�%�b�yFB99[�c���:�k%�h_c\���X
!�- �ecF@#an`�z;]m`�yv���:����}V�he�C��PUp�r�hv��<�͌@�}7�f*�Rc�dF�W�Km={@�q`�x"��>�}e rc?�Km�b�|`Ӹ@[�a�-uA-gq��{���h0/���|�]�f*&�FF��,B-g�ʆ-'BM	)f��c��{.��䈠N�Ԍ�'��aףCfD��iE��i��iF��hy�N����&`�(t��\`�c�,�hga^ma�{F;)%�	���W �z���YÏ?J!*��`t%%O���J������iʎ�{eKg��(�G[C�=b�aC�
T�%�aK�����
�	�}kH�%���iP��b`�-`�?H��H[M9(�Ұ˅���C��O��i��CmI{�(a��b�f8��G�BmJۏ?|����-_Ï?�!��K�}䡭ci�ec��-�	deA,i�ԷL�iw@$|M��%@Mmg���N�&~"C9���k1@���-ue��c�vc��?��cN��'��?e�a*
 	��mWvcM@m8��ϣ�fkcg��F�=�x��E�N��{f�}O�}wB1aP�cQr%if�.�r%�OmR��z��oc�+���i��$D��wB1aS��c.�	}T��{�E��ao�M9�4ע
��{U�cVۏ?|`ע)��+ͤmc�Om{�{eW Wd/���W��?"F)!�����9���]j!{�%Q(4'U�	Ю�E���{���2�B��X��iY�O9Ir%Z�=�Om�F)��h�G�…,�c���[�%D9�xc\��cA �?6�i~];+aV!0ag��^�E9V�qcR��z�dC�6E1a_��c;A$|`��k)���i�5aD��g����|�J��(;���N��J���{��C����+{ �i��)a[�'S z��E�^�hޤAmA��1�j�z���7�beo�-a.����D91�i �.=
i8��f�D-|����h�Z�hg�cy%�i�f�Amd�b[o���#+aJ�%D���S�����z��%Di@.'tb�-c�cb�?��?d{�m�a�m?
}uU�h �i��
!+az!�ie[of�2�
}�fYf�b}�'�&ag�?{�)h�{AV%	�zQ�ii�i���.���`e����F�{�Ұj[�>��zk�zVat%w�n27Gm,�z\����il�%Do�i�C5'�
���)ocf[1�{�!�&{�?���hm�l#"�)�9�-n�E9J��o;Gm;���(
op[�b")I ,D��vcq;+a8��W��&DUVmeRE�-�@��͡'Dr;os�%���!A�jW �-tۻcC@Q�d ׀�*��X3V *�%u;�,v Wv{�mB���Cδ>C��|`�{{�w�
)x�Bmb�f#�aF��b�c(_�ԋyZ�W�=ͻc��{���'�(]���M�%����ceF�ϣ���H�{Yc�[me�x�io��h
�ZdV��{]��? �-l��{���� �>J�O	��E&@�-T@me;�Vv 3��Ӣ��zqB5'�V)@
�y
oI��i���%z[�,���h����{[me<P�,#+a�(G~Y&a���h]��i�� g
!��*�5Z� �`T�Y��!!!�ɋkw�ԣ|��hF�>g���T��i}�Hm�>f �f��b�m~Hi#�E-gy@��*�����UT�'D��%�!��Z�m#L�vc)@ԣC���;�y~J9���\g�������4K�(D��S�y 3W�5a���Q�"�-��-�m~S�:�%���ab�b���%@)DN@\�[)D�@?���y��k��m8��\c�dz�Yc�)����A��|��N� gwbpc�K)D�£�������a�T�'q�m8�לU�}�󾤈{e;N)���9�[�h| �d�cL��i�p���oeߥ�9eSBq�cCcSp����>��Pf *D���kR��Y�D�B��ZI9������k��V~��y���`P��@�� �#ע����A;�d�m}c$��^D	f������y%����[I9 �z���CP�ֈ����@-gQhpcF +a�`�0��*|%��?��a<�hc��9<��o{�m8m���f �b����ܐB܀>�h>�z�`��g ����if@f���-����l�V��^�Ja򑂞8.*媸� )�;nfD''��4S�ʰ�������`	`}��	f���'��j��:���:��C��5aA�a�&�z���9P�3i���ǧ=��8'�ۭ><��v��V��	f�ۭ>��F{!�cq��`&�ڀ��"g(�	f��j�{)1f�@�z�6G{�>�aF�۩c<@;�d�	��7��Jy@����#u~��aO�愗[�'�!�S�[�i�@Vm6��iq��c%Ԛi �j���k��#g�aZ�)'�G��۩cS��`
��-0��I��`���n@��@۞;�j)�׌: ���4GH��<���wbt%BC5'NƏ-�K9�G5_��<� 0��{�?�K9�)�xJ��M �%�)I���i�-0�x�K9�Eԣ��{���{u�EVm 亂Km��I��o���g��鉎�.�~c��	f�`@���L9� ��akcI���h��P�&���{�?�;�{���{�-�@���a@�Ӣ7`5u�~c���iF-|���{i	�(��YF`̑�;)o�x��,D�)� �hd���N�פ�������&��{�ۭ>)�ۏ-@�>W��Y�5a�[ �@�/����#u@�-I��0>}c�4�d��6a�{�-��	���%X@Vm��}cVA5'�y`O?	@۰;�j�[Vm��i��(�{�,��-�[�/I`r��� ��{�{�,|���[�/�b�>����oR�I���}�[m��(�`6a�>L�o�s���iA��ܷ[�{mA��%�-D�;�-��in�`#)�1�i<Po�;�?��xcDe�-ʀ�{��#`�`o�ŀ�Ȫ�-:�����heb��?aB��1&�i*��c��M9;��x|����i��Q� �h��-�ܔM�?W �a'(�?��n��5a-��xi	ۻ��>w@�[)XB�{�[M&@)��&!��@d�� ��;'"��,�a�m�/|< ���-��?D�ۦ���5�}c��]�H�����<�?D���i���JW����}��&�@۴��ۀ� � +a<�oa�-D<��}��i�B�ܗ��;<�{U�}c1Wo����B+=We���M+�j�Uj��-D�@5'�$�iW�(�J����i��i0@oe��hx��xN@�zܬ�{7@&gdN_���z;�&(�-i@�@���u`&}��%�L�a��}��z�[�io�`�j�����ܮ@�c&��n4}���>~��j�Y�&����7��cN`t%Û
2���Ÿ-�N9Q��x��>ěg$@�&*�c���`�`�jO�>��TTƛ
 ��"u�}c��*�T�h����(�R��{�xU�}cg�0'�`�y�[�����+��i-�>b�3��F�>�����`�<���m�c^��i��f���o���}�,:!VapcV!(n��'!@�n&`=�,�/Nn=�)3�{�j��tcF@�{Uu�z.���{�i̛�'S�:gvc]@�j�[�-�B��S
=Λ/Dϻ�ivX�	r%�d�C���WL�k�tc|�#�<@Adv`��'�B�`"ܪN�8|�c�Pԫ(��E�q@�xW
I�t,� [�]@�?
�}:���e?�<��oH�ZmЛ�i�A�&ޡc	 �-�{�i�;�/w���K�״��J@8a��a(��۝-�@��I@�n�Hc��{�y�p�ka�y =�۝-׻)4�;g�,4'�{�i�`���`&���*�;g& ܪqa�x�;M-�{�k�[�kR�"g��cU�����&��`P�z}VA�"�Zm'�_9g%
�	`���ib"4'��i5DoCDvaDD��Ӹ'I
 ��}cg�)u�&i��(J��7�)govNmf�&�`W�v`{�޻)�;�Q��?�[$g3E�x���ov��fd=��W����[ I�J���[�%�k�?⻞-HT}&@ԣ]��%㻾>���b� �j���#�vcC@ez����Q�7�c��5a�{�{��{0jϣK��>滞-J "�[�'�;�i�oeg��hw�[}(@״��`�B<'W�M+I����� 뻾>P�eon �&H=w��>d��+r��i���h| ��0ٴ���a�)�n��i�oe� �i��-t��-��`%@	)ݦ�i��vc7�c���!�y;N�'�d�&�8#Е���Nv~at%I��i�F��s}| �^E��e N��z1��j���aAFa� � �t�<�R��?K��p"�]9��ݰw�}|�ÀX`Nm�!�h�{}��`0��- W17��/(��iH�]9�@@d���^ao �&d����y�M�c�����-����� �z���aw�
��o�MR?��f�y��,�A�'ׁ��SF+!�G�`6E1a;n�-�;�hN!�iC�����\ʪ}�As���,!�;�& ���K@,��&�R���;�{g�-��xc�ؚ���d���{#�i2!�{x��{>,�ieb�?A��`�A<'� 0��a���	�Yvc�@���z�`2"�]9���]b��!A�� #�&^�	}
��,�����`���&<�-}�be=�Am��	o��9D��	}�|9I�y��%�"��G
�f�@�-hco�G��;�heH�V%w�
�w�;Umi����%���ۂ��� �&b�
ۡ����,�/�i`��:�zV�
�;Um_5q1wIc�X )UA�-�I�c]��-�!�-�s���Y#�ݪŀ�Y�{�+���W�1'}q�(�<o �bR-�!�&:�#����Y ���Fzdckg
@9<��} �?!��{e�vYJ�&��C�&V��l]��`�@�\�&�ib�
���a��i��-��&� 1@����f*<��}�B�ܛCDmq��`%��h�@۴6E?�|�i%A��
A�?�
�{���y�G�y&h�(�(�B�k��<av�{J�vc�a.u%�)\�&���`�e:@�x`�	}�`�(}"ݴoR-C��iH��<@��J�$�<�`�A[`
)I�Cm'F��Dh�_O����B����z\�a�2.b2a���i:]���i|
)��_f��cS��`:�v_	ܩx| ��~��€��R@
}�
@9��?lFa
|�&|�&����] �?I�K��`)YVmO��iK`�l��i<�`���'i�}��}�@D%��
�P\@<'��f
�)uA$n HDm��Fz�f�\�a0e�i��&���\
}�6�A@
 NN���;g|�{����c|�i��c͉�iT��k��)gB��':�/�K���5a���_�c��/!�A�'9��&|��(@�5\8a��W��A�'�5a���%h`�k�@��v���|ુ�Xm|@��	@�a��j"�?f�$)�)\�a�`$���C)���g���<8D؆�&1i�y<�`X�)o@�x@�NO�Xm)�)���R�z&F�+�Y9r��&;y�q$�OB 8D���{�-�P(�&DA[�|�&q�c�����i�Am\o<��}�a9d��C`P�&��F9�a�y����R�b���kx���Am��	$ ��&!|�k1�`��ى��"�;gC�d#�B$�xc�`+%�	f�b�z&\�'V��`���(T
}�e!n���`	@�
 �`&�G��C�ޔ�cQC�k��>���K �<m� '
}K@�	vY9��-(ܭ>b!X9)\f*<�a+ !g��,�Ym]��{-\}~W��a��}:�}cL�kV!%:J !���+z�&D�ĝ-��j��be�@de�.��v@�S.|��Sa�k�)/\�-*�Bm��̸�)���aN`@�T �&c��7 z#8
}�f�ס�ii�Fk��7�
}:���/�5a
}��?�*�����`Ѧdapc�Ʀ���?P�C��660Y+RT�
Vm�
oS�-�'�c�"�i�#ue
�\d�A5S��ә1<Gm3g;���{Z6�i&��+JA��ʢ�f@}~.Dm3G۴2\�cI@y��&w@���	۾��x��h����& I�BI9W��Ӂ%�AR-uc:)P-gI�iv@�{����U})�����eU��%��Ȍ��o3��/tA)D��S-�?a4<)&��`P���T��{5�4Z6�$"��-7�
�{)��W`����o��+�@-u���{F �|`Ѐ��Im� j��6�-8�)P���y��yB�	�$gDm9�ie�&���&�L�	�Lo���C&I`��a�=a�@ :<{�� ۪�!�2(�5;|�`�N@�	<�i|g:D��?�k��B�_y�-!&��*��SA� �#�S���|�$�(�l&��+�2)g��-F)�
2T�梶��ׁ���A�'C-uz���_Ʀ%N`pc=�c4A@9P���>|ɣ���`?�I@�i� �Hg��k�OoC�fF�EdA��cf�"gv�y��jf��z�L��3�)�& ��em~3`ɣ&���B|ɣ_g�-����c��q��)Idp�W�F9+JYU&�@mx�_@@9���+]c�c�F��I��YC\�`�@Y9��X� ��>c�}D�F9�
�f��������z����$�nt�{e�]mE�)�`o���&F��&W�<a*�y�݆�a��ȣI����8'X`�a_�;|�
f�A۴&��Ye� ���&<��o<�)::�tA:�t_:�t#ta�a!`�YM@Y9I`@�pջc|`�j���� C�����e�fɱBP}chc>֙G|5u<PoH\@9I\�a ���X`�aKI}�Ym�k���!+��y�˂&W�)�d�G�)���J�a�]m4}��b�r%�f#V�\9%���q�ۖ��&�d;F
`�&KܴW9k�&t)��c��g]m����"$g�$u���:��R��)`{��cW Z+�G����/d ˣ
"�-o �ic@�yz��?U"|b ��@<'C�oL|5g�Od0 �$g�$��7�%G+2!�aM|5u<`}�b^mC�<��@�f0�$!�U	 N@5�{I+��?R@�`N|�%C��v�Py��b����o�&4�zw�AMmB nv@zR�I+3�<�O<)łb���>�C��P�HmQ�)��E����i��)}N��:@��9	I9b�^mR�=.ċb�Ƣc�E�k�)}�C���[��M@�xY��i]mS��S�t_��9Ƃb;�Bmw�棋��c���OvA�N_mFe�&P�3���n�m#C�<�T�_9g@��S�'��<��bU��b���ȇ�#/e�ޘ�m1/a�H:aj��U�ܼ���֋�%@�kQ��4�Bm����.�ieOA�4�"íV\Dm��[m���'�:���I�ieW��V�D��݀m#z8 �c@O�w�壖C@"W��aX�Zm.��J���Y��`Z<}�{w	�1'<@��]�-Mæ�R�-C���B�ze����[\�&��eOC�cc��a��ze���&	vc��bw�(� �x�y��c��f\|\m��@v�b�%CPU2]��a<�[m�[M����c:!��� �&@ &��a�Bf��)^�fVۈ�躄�oo�ie&��+��&!_}e`�c�B
f:��금s����a � a}e�E۴b��c
A�+��-Dc<�&���i�=aeBDmJ�{e%��&��M9�%�&d�?D]�b4�ce}eL��cUj�f�Jmg<�c-��|���[c�J<�c �&�@�΀�{� �ih\9!��c�cX@ ��ʕ�c�a;��֣�_�C�$���iNFmf`�bi\]m�(�&v ;�5��i�&�� �cj D���h���+�a�%1@۴k}Ha&�y�T}��iU}eUɘ�|��	`�5��iL�}� �����,�#l\�iD���g#�im|�c�E�h���n�be!:uo�	}z�(gx�  :�:uJ৘0@����d �i���ܠǂk�:u}eA裏� �%%$�c�o.!��:u�~1T}M}e�:up\4Sۡ1
)C�4�q)!�"�iq�z��%h��L9!0�fW��i��	}�)}��+���VA�1@Ch���z0`�c�`�mr)aH�+<�@��@������$������$n� ��)��T/s�ae�a�c��_9��	o��t}a�:g�B�c}��Kmu�;uW`�id���"�4b�cg@��,}B�UQÉk*����$R�/��&�y��!�50Pk���m1��7v\�a���w<
)�y�'Z�i�$x\�a�"ˣS%�I΀BdY�Cd:a��c�;g!@�%C�<���m#W��`[�v�F�ceπ��S@��@۴U)yܦ%%��VSW�
B�c���{�A��	dkӹ�a���k������aO���z��<Nf ��S�;u%��&"�y����W��%��W��f<u{�am�):R@��Է���ܾƯ%d�$�@"Db<uH��p
�y�@C�3G۴qa�c~H���*g;VQkq�}�`)�!���"nI �R@����F�xd���|�)g���i�f��i/O�X`�c:��'�A5���iR�"nJ�{�@Z��P
o���͡Cd}|<g@UI9N�Cv�&��d��;�)|h�W��2g�n~�Cd� �<f`�?�}��-�܉y��`N��c��j��+{��&���F@I9��%(����o7��bV�<|&��H����}���{�L
}dFy��!T�}q"�>
!ظ�A�'�@��8Q*�vB7�F �o��oW(�A2}w"Pk�\)��-�$|N@�|`��a[���
P�ٟBC�X@Dv��%�M��D
o�a��@Ц��b3G۴�\"DD���$ngA D܎�'���{���nX��'|��?kV)���{����R�M�A��P���]�=!���i�
�%�%�a,ǀy���&�a��v`W9`W1��Imw�)P��!��}*��a�$n�� �`a`����k��)�fy�@s��F Dca�&�- �i�`�&;@I9U��i!a�{}��#M`#D���d �*?E���\۴:1�y:!�/�-�C��i<@�Δ��k.*�o�`@�D3)��t#B��b �3`̣�S��i���c��p����{]�$:���k�o탹> �-Ӂf#��}�<�?I�$Wv��iF�k��|�-gV���g�-1H�k�<�-cY�yd��<}��b��kfe�7�>;� ��_fed�8і�%Dw�M���Mv<1y��Fd3�x-�$n��}�ۗo2��b��{-K��N`�A��D�d���d�Oa��U�	fW��M"|��o-��f�\�k)�̀0��o͡�y%��y��b2`�i%��H`�i$o:��-n�)��9<p�i�@9;��*�*�&�=$�g]`ۜ<�&
boy��(@�+��@m��(�+W �&_�}Q�a���'�@��`�{��۟\Vm�<�& Ä-��{€���Ţ�܄?��a"����a��+e�&:0��z�ޣ<�-����\�'<�}ba�-��he��-��cfR���o�|o�l�`i *|�\�'1���z�ʄ-��k-!�i@6��he��i\��+I��uN`�E�o���-Ӂ��<��.g���fR�e��i:�1�y��yI�`8W@} #)��&D�ʌc��%ɚ�%d��b�Am��9�\���`��⣬	��3��+T@MmN�ieg��y8���[d��)%�L9�6��\�m�`�c��i�|oO%�9�&{<�i3��[b��:A�n��Iv���y߁�yeP븰�y� �<~wo� ��g�c��)��W�V��a��	�M�j�)��#��Fd��ֳ\}��y��A���-��h��"|��&_�@��&:��)䄦�q�[d��W���`�ca�Hd���Z���HdR@�I
u3��i��L�uI)`ɣ�Ivo�ie��he���-��c�� �\HK$|:��漜�cW�/|9Bo��'D��c��m�a�cZ�����iDH��S}�f�i| �^�����+A2�i�):߁�y&)D|@�jN\B9CaH� �{f �'����@��|�ߟ�M�`*A ڄϣC`Q���%��i�\�`� T�B��yӡ5����? }�`�S�%g ۾)Dcg�w�g�� ��f7 �`�<a��)?Z�Kv7�M9�j���9�Zm7�Ym:���se���`R@�@B9|�׳�Y,D��:�m�):Ā�dc�c���`�|Um ۖb�&�):���+�df�\�J
Dm;�J%f�O f;@B9N@�3�٢d�/�&�Dm��)|tAfe������'żXm3DmI���AΒU�c%����� �����ǜke��-nP�t�I`S$Ā��)n����m�f'���O�&ډ`*Dȼ�-�A{���Cm]`fV!Pksy�&<�
z�HմBH��u��Cv��{����|�&Vm�b*D�\�'>�<aU��(@����8�|���	 �#.��a��`P�CmA '�f�+z3�.�sۨ����b���-!}+��`́Kd�b)y@���| �F�c�@�z� gP` -@��&�tk���`��k
��'7��=f�+�Zm.@C�� x�9F��:�t�~8C��?��E�R�I�q�C7 !!�!��͜�&�J�c:��^&��+:��|�f�ct��M�x-������io@C��yB€ۍ�.�bOMd��yw��A?%j��<EmV!�,`��mw��%�ie�@
��\�c��?!/H�����x	�}����!�Q+D	�93�[m9F�&����?�*#�`��C5�-O�'y����+|��d
{��<�&R`@�A����<�-��_" Emo@C�0��`�\�&f�+5,�&�\}W�UkDy�b]��-��`g�z�E�7��kC�%�@P�\fJ��
yq�<Em�\FmK@Ǵ�m#�b�w@
�N�gԩI}�\$u#k(� �a��c����`Q��m&�|�,�\f�$!!�l=�� �@Fm]`�b] fJ@�I�< �W@�7`�`5��&�����a�W�&���P�|�&��&R�Fm��f� ��E9]��b��&q`ۇA�i-��'@�bAFm
@)|��g��-�X�<�"I��X���G{D)D��/��m1(��%$�	���{�E9�j�-|ƿ�_�F9����I]m|�L��-C������-|| #�<�-\��<�
zق�`Va�+�\)D�-|Kண@&���G9�-n�
o�
}w`�j|�^7`�`W��`�!�|Gm@�#�AXk,�2�\Fm�"�F� Di`I���
���
$��&& DUe��d`�bo �m��#g�)|��Ym��	}D�	}��)|"hf:���&`I]��?�$uX�F9 D�m~�-��F9&��YV�::���	���W� i�����x-e$u��é;�x�3!�m��`k	�\kA@���&�~c�A�`W���SpJ�-|�݀���I`@�H�zw<�Hm��#g&o���iF�>�Hm�`�ܱ-n��?v�Ϊ�}?%�D�tGm���i'tI@�UJ�Y��Hm� m�)|@:�me�`.|'�$3��9�A n�<%gS�$!w�ϣ�#u�\�cA�`D���'H�cv`�ή��ct�a��6���.D��	o$)	�\k���?*J���w"4E�kv`��g�c`V��#u����nvc"�[m��_9���eWI9:��J༈���C���z��N��)A�c���'��/Dg�����1wU��c�@�&�\�k���m
A�`��b��[m@���?�R���``c���he�`|�7�!B�g ��3Bv�F���� ���hR��z@�aw���h���|�`.|'B"D���zX@�c�!���f7��& I&g*����bJ9�|)��h�a�|���*��޲͢cG��'�zG�̌��-�``c��b�
�ǕΣF��}�Y�SJ��t !D���'z!q1|܀�@"D/��|�Jm�ynev�{lI�%e��?] %g��z����-��i��{�)U`�cݩc}�`5��zAk�� �
���'z�?D7q`c��'nm{>�6�_C&uC(ԣ��a��帱�"n{A&g}`c���9���b��
=�z}'g]c'u{AK+�`�9��a}'gX@)!A༾��aJ ��]-|��"n �`eB�U2OW��):Kָ���Jm�b�<	�h
]]m�}y�z'��+��Ok�=q�M�
�}�c@X�g�'!�D�U��%$�
|��R`�f�\�I`fک�	fg�'!�f#DC!�c
��a�FmC�����g�'!Uef�u��?|��^z�����a$��%�D���.��`M DS wU�	fߥTk���A�����d�ϰ �Jm�BE����(]@)3ᆙ|��LI����h�|��$nV!Pkt�|�w�Km=^m>�F9��}])!o�_�`g�|���EOЈ�Ok���Ӈ$D�EK9i)ˢI ��#�Y�}'g�(g�`�%�t'g��{|`�^�J)!�!}��'!�H	%]
}]�h��a�@K9��XmAn۪�+N���-D| ���'ʠ^9��w��i�ʂk���y}#D����a�i�aV��>�4i���� �V}�h�)u�ʖ�`�:{�<�Uk�o�(g��	B�₹����fR�҇�{=}Mm� o )%| ��{7 ueVA�'!]�'"��i�
2D��8хoA@�i�y���9#] D$$|�/�2D.\��<�)�h�$��k�;g@�#��x�r1���h��k�B�)!��]��Y�%]�c��&��zy��i�be%@�'��C�<�
h0�|�@wwb�{�nܴ']�-(��zi��k%�%DF��h+͔V��
�)��i�2���*}DD
"�?f�a+��iF���H�ce�$q���m��,��z&�bd@�-/�+P�G��Wde� �i
Acc��H9	)!N ��J X"�J�=��-��an`�-��az!)�B�-{��.]�'-@$��Z�/��hW�/|׷�cf�|�R@���@�-�@�&C�xZ��E0��i ��c1=�zh�)f�|�R��z�f��2]
f�Ym�LI9
A-gᡘd3��z�a���&:4�|�}@ 5=�zzaww���~ ���#"#DoM����٢�`#�k6]
f�`17��aM�Y�	`d%pm�j΀�c���'�BI+W@Dd1�w�^�&D8]i*9]�-	)-�a;N
f}��>&�	II����{�f���âQ+!P��:F�8�':!o�
`�.��9]�'D2$��W��b�A�o���c��z;�i@�R@' ��h'bDDf��+���c��9�����Uk<]Omc!ad�ǁ_� {1=��cw��mF T-O�Z�d��$>��h� \m(�����4\mA��#Jo�û+�&:�y!C����������cR�W�C��i�i~*�"k�'g�
W���?�?[c�HB��@��i� ��A��B}�.�්H�$��X�f���c��i_��{A):�fcC�,!| ݀Oc�5�`wwz���#�$D�
�J�@p�D�f���E����$�@ F-g3��h��	$a�|�S�,!6��hN�cG�[m.��H=�-����%���I"I]��J��hK�fʠ(|:��i��[m��9T�
2L=�{����!��}�$B����M��z=�5�N��g ��A}J�9��$D�@��D n��-�d ���O�ߣb���ێ��hS�g�
*�y�<�
zי)|�Hg�P@9a���Q=�zR �c�S��)"-g� �� �i�SFaT]�`3�)|;f�/�
U=�{V�!g|`�;�Xd%��y�Y����
�>ͺ՞'�� �be��zN�	=: ��B�TA]�!gq�g!W]�z�ߣw��oX]�i,O�%N�`_dw�`�
Y)D�'�-�5@e�)of+��H��x�:���P�W��Y�c��fC�!g@Xk����Z]�{W@Xk[]�c\�f��Lj����"�-/f)�b��):���`t8D�n U�|�|�]=�{��
z'�]�*j+��y�^�ze���yO�5����	_�?�@���	�@�i)���7@i#�8�`.u��z( ��e" � X�cj�o`�'ac.g��)q��z%��o�ù>D��h`��z=̈́?���&  ��ca]9Db}�zv�c��zĈ�hzA;|*��'��}��Z�b�&�Ѵ�!�޺��`�A���@����z�*��! ��d=�c������u{R�i3g�e�#u>#v�=�/!�o*D;X�{���y�D�f}�i�BXk�`&�]B�m�Ʈ�.�-���;���F`&d�Zvr��ꁃ"ug]9Dn`8D��ߴh=�?� ��K�Ѵi�M9KmG"P�H�@$u:�tj�´( �`�i{!f_��a��A�\�k�`	`�(`�����?Q�je��+|��a�ZdUAf��m0�a:`G�l��id@/u!`&BMm����܌�a��|�P �׾f�&]�,D�+|X f9G��E�c��Y�ac�h5�#g�f�f#B$I�	�O!:DP��{��/�A�C v-W�m�AX�. �'J��3Ĉ�h��h��zm]fn}�i��Y�>a�}���ho=��`�a͡�had%\�Y�p=�-_� d�$�� �gfa�}q�f�m8T@�hT��{�W�z�}eEcf�h��"�i��-D�M9< \�q ::r])Ds�	}���-g����	}o���3��it�)|u]�z%��{儹��&v�џ�Y�: ���vc���{���&WfcI`@���}w D	�\k&u���ii�)|���\��?x�;n� �y=�{!ŏz��?Oe�h�)|{� � 
)���cy��?R�|�A ��#�f#��8`{|�-D[ݹ��O}�	}NP�'���{C�|�%�-D[#=o}e~��i�-B ��.�i"���-T@�h��be�r�g�W���f�Z"F@�h�=)��-�{���\�K��%��ܺ@ �]�hy@�,:aI�_-nI�m& +7�.Dq$51&_�VqU]&c���~1�{$�o�A/��Y��.D�A�a=mfCaow �%@�h|`�Ri1�������Ev�ի���=!DǕ�h��z��zeA�I�{&��� �;|b��i��<D���i�]`���'\��V��i*��u�&i�&�G�)|C�)X��ta~e���{�`�+�~1�� ��壦&'u�\k���zq�k%� % �z���{<�N>I��k�݉y(�贁#Cd�
�܌=!D$mz�wbd%wbCd���P �TF�'-P�c���OI neg���}�-�� ta~e�݉kJ�\�UC۽ac)n�@F"`�-n�4be���8kc��)�����|�����i�
ٕ��z���?R@�C��k;@f���i���{��J'f�>�i�)&���&�}X9�=Wm��iza�)�Y�>DX !D��m# Wm��y���i�fg�����4S��>D�b�Y�0˕�)4�!4�%d�cYc�\`2=��{
  �Q-W�k� �y��h]@"D��b�`��>@\-o��i%�ؚ��ش1��)ޡ&��]�3G\-�D�⢜-�H��P t���h�])!C��{7@	 �݉yF��{�@;n$��z��F+���{z�]�C�?���-,ěk���noe��-���-7��{��"|;�jC�"|BՉkf��1}��PG|5u���{��'��{��(g�"ne��Y�:��"��<2)�{��)u�����':Aۅ=C2�3�k��
)�Dʈ"��<q�{�@F�%��a�}X9@�YmP��`�mc���zlNY+��&Ǐ�	�(�m�}1DzaHd����&�1&@�'����]6uF`�g$|��ke��)灋k�`,D@�kt��{� 2��.g����G9���a*�Xm� ��]�'�]f� !���y���/�����oe+2D��z�]�i5�$D���W�{a�e7�	f��ܖ�4bI�����1�%€�-�0
 ��:D�=�iqDk5�L�zg���"����:Aى
�Zm;���qA-|	��e� �me� �#�#kc�]-|���-@����-DI`�i"�f#�ˢc<�?:�K�E��	�`�i:a%��]�i�B�{� ��=&�Ym�a\q`�iЀ��P��a��u_�w_g !�}�i���W
۹ !�g�]�zv����\kr�YmVa۷�[m
A�i��k��	@" ��w"pc���\G�{N�%De�B"�i��i��=�m��Ǻ]�{���7�ɴ�4
 | �= 3`��P�y@Ӛ"`�z�]�zg@�:W�%Dg�b~kDqc��[m� �zZ�cV)C�j�>�]����ڡܩcwBqc)`�o"��/��c>�g�]i�&���-&`�!���`�{I۫ܩc'�%D( ��+�C��?Š���d��<���-�}\��-���/��Ad���y���	��i˕�ݩcF���J�Ȝ���� kc�)��,W
�%��a�+!
`M9�mw�@�>��%Dme�b�{��&D�݄?z� �}�{�������,@��]{�I_��ݩc�ݻc*��-Рc/J�z"�/&ɩc9i�i�`t��݄-�}�{�'"|V�\mFE
f�Ei#��%Df B���`#����ݻc̽�{e"�z���=f��iP��~]�c�ʟ-��a���i���q�>{fc�A
f|�|�%���Bd��]@�%�aͽ�{���`�&�mр�'�@-u&�q%��`�9u€q�:!��р�'@��oO�}�Km��}�B]��iey�����j����ф?��һ�'D��ϝ�ag�&�@��q��i�Dv7��iP���g��C���F��{1h{�{fc�@���B�[%A��%A�)J�4S�)��&D�]]m5fcԽ�i���[�ţm�
 ՝�c`í�A2D\ B��9^m��� �� f�f@�i���f����^9WDv��k�`�CA@"�Dv��8a�ea�'D��3`'Dv7���/�Dv�&�@�z�Dv4�:u`b�[��j��N"�f*�g������W���/u����&�?D���{р�'ac�a�/u.b�>0%^m7��if@@"q ^mS�r���z�ۚ΂k������{�:uJ৘W ��؝�cg��`w@�nU�$g /g�����5����{˂�g�'D*�ܮ )��B�
��?f�;uB
�J!�-��)n��)|{
f���aN_�]@�-_��S	2A@�a0ӂk��a���{p$��F�%�
f�]�c&`���Dv���z���?�ScCa�`�Dv�1f�����]@"�D����	�"�ZW@�`��_9�]	2��aۀ��:g&�i�}�ۢ�L9�T����i�)|>��w���{J�<<�����`�/�4G��`� �.�� �b*D���-| 
������{�
Mm;� ʆY�!�.*�vc�Fqcg��{-��f�`�A��܋f�H�c�Bv���iW`&���a|���L	 �f �y�C��|`��F"�a&A���gMmWL�/O[�� %@	2��ۯa&o`f�ds�� ����_9(��Ɂ����v�wĈ���%D	 ��>��=���CAFd��}��9F�{��&!�
�+W������{$D=g�`�"2BFdM��P�����S��c���{gC=u�k!��z���-n �'�'��P�h��i�����j�{�@i*yf���)��N�K`��� �.A����iӉyb����]�-:���{3 �F&@��d��y>fc]`*D��ǘ��Ixc�`&��}�{! �}�i`��c+�F�R� %�h+�
OP���`<u��+|o���.����i��{�!��}Nmf�8�
ۮ�&3ifc&F�oP�&��a5*�	}�]$u��k�d�^:��fȔ4K :�"�m�}�{g�Րn �a|��7NUۢ�i��{q@xcFi8�c��}n�,Df�Hd�,D��zW�Nd
`�hb�����[�f���c�A�?{fcg�vcw@=�@۴�C=g�����?3`ڣ� �=BSU2���=��p��Hd���f�cC���]Ddޡae�-|��<���ncW@�FV+D)!$�����z
B�i� :D́�h�]�h
Y�'aC�iWa��We�S�]�h��
)R� % �{>R�'��m1�։y�u0��%a�gV�%�(@�+�D���?�`!D��iU	jA@Cd��-�Fh-����f��cR�zX�?!����c�)�?gR�?�Afk�:|��?�]�h��Id�@zGW@9bFC�S;CW �iS����/��z�'�� H�'�]�h��h�]�i�(�z]@@9]��a@�{��h%@ D��Ɨ8���|��ŸE�i��ce��\d� ל7` ���a��Iv �c��)�į,(�(�ceW�cw��sI# DO	%T@ ^�i�`�eK�>-|��hP`]��
 �-|q )J�e>�i>�z� �c{�-D�(�i�"|�-|������	�"|�@+ �)C�?�fM"�@9�'��~H�v�Q�>�I"�H��Y��
�h�% ��z�H5S�Dxc"@9q`�.���{��-�@9
a=�<DfH��"|��?��cS@:*
��kW��w
��
�cea�eH9D��
 ��;|�@Ӛ�Iv>�h^m~A�<D�Jd������'��tc]�.D�"�m8x�i H�y��.D��
2g@��	�)��ucV�zeq�uc�ڜU��cd@�a*��[m~A�_9Z&H��t_f�>0@/n}"D��e^�k���{�45uf_�b?�τ"|��y��e��G<5ua�.�2�`��D����q��a]�?Th�.��%Ρ�ǡ�����;�@9^$|R`Xd���h]`�c��FaW �cVs�z��|ce©c�@feDŽ��3 &^Dms�شH�g2�z�m)2�#>|{�¤�d�Kd��tAfe��c�Q��A�$�J�k��{��Kd� Cz����~�C�|�Ca��|cH�f#�>Dv�s��!X�� ^$|`�mc��4:}e���/ͬ}�?����h��€�)^�kǠ>:I@
�g��cK ���i0���E���}c��?�-€�)��tc��͡�c࠯�"��<��i7@B9݆�kJaH.����	`!kc"��$d��,յ�z �9D!Md[���<��hA �����z
�DdA$||��o	 �}N�:��?D��<� �i�Dfe
@��"~�z�|c� t�)�;|4a�eI`�X@)!�C� Cz�)�	�F"y���#~Um���a�ef���a%��cI@
�ED�& $��'�"ުad:�/YB9y���%��-�tEm�M"��z&��-��'o��OH�ԣsR){�-"�>DR�	}z��,���K�z�H�f#�������ʆ�?'~Nd���i(�CmW@�h���i�$.
�@d��;T��)���oDm@{���<�{�|c��f�<��D��*��@Q��t�z*�y�cv�FC*A��+~�z�)�x�<��D�!H�L���zC9�VmeG
� ]�A�c�MdAAd�C|��!�C�dK4�F9]`1D7�?:Va�we��'��@dv :�X��{� :��`��8�,~�h����ق	ۢi1���-�ޣB�9�RV�c9���f�kd���`�S��{�I�i͢��:����)u~����k�{�'�o[�w�c��wT`�{;���a�)S��7��j. �#�a�e� ����.�W`Hd] 8!
�F**2�&|�xӐ�ie�y0�A�cQ%�>/^�i�
���@Od��| �i@
��P��`o�i�[)��\d�@
�0>Em1^�ieb�{���2�}�`�3^�{4k�)@�W@�\�he4~)r
�i@�{��£%��->�mcz!EmϤE95~NdH��b����i`ە`�~&��6� T@�if`2Q�he�a�)��i-�}�Q�cf�ې�i��nc�ҭ%P�*��*�R��'�{oD�o|��+g���m�{! =��\d9!kK�b7 �*�N�9!�#����8�yAc N���t)9�o] E�q�z:^�cga ;�:g(@&<^�-�s�z=~�i�]�cN�Bd>^�{�]�-���E9aBOd(����/'��"g �f@���:gLŭ%��BvV�z?^Md@�a
��LL��w�ޣ��iA^I9�ԛyB�
)C>�iZ�	�P���]�)n�o�@��T�}�B��W�F9y���D�o0	2b����i�΀�`E�E�:a�}Y�-%@}e| �5��`F>�|@��|[�-�}	 �i_�x��	f=Mde��keN��a& ո�;ukDvA�|cG�ke|[�-� ʦH�cX�?D�)f���I><g�"*D��
� (f)����Xo4)�8'���u����� .��<	@�itDd3`0���4DDJ�o���@۴��Т�-f��aW`HdV�)�	fmЯ����K^)DL��a��M�'	@�-N^o��cV�k� ���P����X�O��`�):Aw�P^k5� �m���iY�o� ~e7��cQ^��@Md�Č�	�'V!�}R^Md�b�S�`T~۵L�I9�ImO�Hm��
��Uޢc��)|��1���i<@ VI9R��cʠ}c���{f��<�h��P�W����/��	�E–"Ed�Hm���i��F��{W>fC��$g�Ϣc&I�>m��z��m#��fI�ke3A �r�p����X=u�W}J�HmY)���Z�}��?[^	2!��f; �m���� i��Fo��uc�ϻc�f��iA(�f%H��W�uc\f>��iy i��n}Bf7ѻcV!a~�]}��)>fc]�	f| �	g�I�^�	f��3aG@֢J �&o��)_�)���{.���i`�>�}5��ifD�L�*|`޻c( 㣢��{����X�Zma�)�A
�g�I�3���Bv�x��@�y0�-w�!g�af�`�8���z�H�b���`�Yc>�m�1�y��6z��F"��Bv�Aĭ��c]��'� ��+J�@�_��>!d� ���\J	2e��iy�&�ɪf^�{o� C 	���{& I]`o]`!!�bne���g^�>��i`Zd�`ne��mρKm�@T���i��;�JMm%F�$2a8Dh��i�Aĭ� X�f���7�F"f_�!t%�|c{A9Di�Jm�u)3ݣ��ij~Zd@?gk��i������F"��xo oeUd�W�Z-nC��Ʊ)b���?3%�=���f�@���,�dl~)1@��&`Ӣ1'fm^K9�ie�KMm���i�o��Jm&D-n)��{����Km:Aoen^�%:a֊��a���� C��|���o^-|���p�a��?3���{q��<r�)7`f���
�x��Uoe	��s��a��-t>:D�ĔCu��bv>�?w^oeE��y���i�F�ϳ��I!��u��Kmx��?��0�!��uy~�{��}�`���?�`��z~�iS-Nm@Dv�W-|���n }H �?dž)2�-�@U�1� n%����{>}��n�
f :D+J[�`DD���|^oe}��iGeDD�(���)@�+/f�{Aoe�@�$AZd�xH�Lg!��'~�-D���cW�|c@5�M DI@�az��>d`Uv�B�z��:|�`���-3��|���0��?�ނk7@�{g���C��w��Km���i�o����{e������a���-TMʧ����}�� nc��S����~!D)�&%�N��z�\>�co� oy�x�d�i{I"
���rv�ie�X"�>�?vVd�%�JO��#�-D;sg�y��@mVAu%DDd�!F��f%!%��~�{]�!|�� n ��CpU U�{+��ܬ�A��uc	��}U�k��@m`� n��cR`:��-�֢�@�{^�z�!������ W^n��kb\�c%���@;��k�>
)7�=:�7d ��E��-|aE�ae�)V!�}�R
f7I"�F�n�`!DJ�_�>�QP3
)��
}�Am��e�C[�a�P�=�@�z��k��Jd��L��f�~Nm1@{�];D%@	;��hi�zew��&'�(`�%�Y
f7`!D) ��s٢f��z���h��"|%�
2����,#DYfNm��fA��n�`�?��Xd�fn��'� ��J��d��.D��Id� �hPi}�@�?�ådqĥ�\��'}�<D���' �f�ˤxkVi8� #D�>
)z��cI��ׁ�cW�{�>�i��kY���w�Ȅ�y��i���c0`۽�Cxc!��"@xci �>�P�a�Z�+g�y& �YGljk�F�N��;a�?�ȣ��i��KI�/��i�+�yH@�?���h|�灓^�?{ ����/���ʺ ����y+���Oa�3�a	��Bm�^Dm4�"|��
o�c�ܕ^2D�~�F�@��i�@xc����*a�h��
}�>#D�|ć�i�x�O!>n�� ߃�a��z��{ȱ�-…%Фc��ZdP�w<�*`��}c�TR9%�zwb�)4�N
A���%��;�t��<DP7J��q�}�>1D���-�� {��U��R$n<-u��Cm��)'Mv��)e�aP�ئFmeo����'}��?�@���`�|c7�o��E�I@��.č$7�>:��=��E��-gS��i��Cm��C���?���a)9�ke�#nĠ\d:�N�/a��$:��?D:�t���#:�t}�Md���,���ܣ~3D֐�c��$D�~Em���-��'}�?D���;] >nR��`�@�?��IvW�>��}��ce *�y�;u�^2Dׁ�)'���2��{�'���a���`�;�^$|f`fV��`I`�߫>>nW�۾�S��[d ;T*�%D��x�*��}�����磬{��A�i5d%|�~Em`&J��{�~3D�����[d:a��i@�������-��[d]`
):��n�>}m� �F9X�>D��ۖ�پ`��@�{Fm8��zN�beT@
}j�$:���9b�}e�(2�3���E9) �i���c���$�<��{�1 yc΀&DS�{]F�`��5|ϸ���8N
}€�y��cP ��~�?�K�a��`F�W`ۨ"���@d�)��c����a�^Y���a��)@f\dd��c�H�ƨ��&��o�`Nd7�cP`u�>��)��	}7��a�� ���	aC)��aK ����)g��E9���a�����@�:�-�^ Dd@=u���aV��vJ6Um�'�-q!�-@�+g�ީc��m�^�yA�i���?|`�W��P�G��^Od:2�!��%+&�޼~
)���a]`)��C*�	�o�d
�&I��7 �?w�a	�}c_�Ad��c;@�{d@�)y�����}Z��c(`����|�ψ ^d<���nE��R9��)���-��
}7`!D�a�f{AB"( ���^�k������j1�3�}`�31����<�Ρ�>�c���a�Q�k��?�/Gmžo��|cF@ɣ���i��۷W S9þ)<��%��&� @d��}�|c_oI��}g����	��a�^]d�@]v�
�c�@�ᨓ)9��aq`�h�a�g���cB�$���%��m=

}�ީc��Y��G9&C�����W�|c�@I9�
}ǾG+�L]d�^-u(��5�^Mv*��}�Hm�H)::��cǤ`:A��P��˞5b� �iq��a�\Mv࠵`�`�y�.�{�8)A�k��;�8
o#˩c�`���^Md� f
��(f����ߪ(�����aC�;g�>) 3)�HmN&�i} �''�'��	f����@j���c! ���+|��3��<�_5)�^Mv�g�
AB�k"�xw}��cOH�:a�j�^�$� {�� {�/*a~��)|W��}�`}��}$DD'��q ��X��c7��ii@
�Ҟ�iӾ)t��a]�I�oCb�D���i���h�HmY$���ޠc@��u�X�΁�?���5�g�A�k�Sg�Jm�I���F$n���o�gӣN �`|U$nߥD�MGMv1&)x�a@$n�>�]�Y�Ġ�b���'�J���Vm	7)cg��c�")��a���%�{| ��Σ���kf`�a��oR �`����lUm`U7�@mڞ�c*��m�E��4��'& I���ݰ���&!DD�A�����{ܾJmA�a��%���9��&��$Dƫze!��-o`�'�`C�ݞ}�Vm3e�&�9Dg�	f�9D��[d���b'慬o@�<!�)�D�=�	J9�Ym��y�j�v��-���^�c�9D�!Ed�]�a�VmQ��c�I)�"�i@�cT`)7�CvUV�/��'�@oR��{?G���^�aM@�aa�+|�^�c��)��iw�e�eJl�QPe����{C�Y�`>���Z�^i~�ze��{�@����ivリ9i x�X�7�@md��cd��m ��WeW�`"
�!7|���
�x�q�8|+��m	 ��7����|@ۀP�he�&D�hX+Sƿ%�~X9��D�W�i:�n�Z�c�T�j @m�)�!
�:�)�@�c�_ʦ�y@��I �.��@{�U&| ���@��g ���i�Ao��z�j�Ob�{; �d�
f}�F"o�ie�
f����#}�!Ed�+n�E���+|]S�cg�iw�i��~X9��z�h|wD�+|��U �p�����$i��'�>�S�jei��}��E�J�g�mA����ۺ��%�o�칖�ޭ%g�v��ÿ%���F"w`�(7@�-�
f�'g�?���'�L-|��E�@9D|Ȁ�����v�n��&��`�i�^�c%���@Y+@Y+��i� �{�E������H}e��-DX�keX`�c<`|w��`8:a�o��f�~)@}eq
f������-DV�Z�(��^; �d��vc"f�c =*�)
�d~�^���ޏ-<�N�?\m
���F��<��,D�;|́�	灠c��%X�c%�,D��'�ke)�%T@}U��8؁f���;���'F�'@����g��Xm��/7`�b���
!�_aB����W�g�&���Cg� $��ke?o�
fd~%�Ym���A��$�`'�a�G��ί7@Y9� �{�a��t!EmD��`���C��&�-��C�l#n��y��ܕ@��gA	)g��`�����@����ϣ�eԔR`��õ�����`b�~~�I"��Zm{�ZmA��$?Nm߉y�Y�{$Dm�྘��'=b�;|* ��-D	�M9���T@}o��}��;|����vU�/D����ޢc%�Zm��a
�b��q��J�v���b��i8I��Df��d~o*\mL�+�!�_@9���/Dq!�cCb���G��£0�1��M�8ңa��J ��nMf�Aպ���c
�c?)S�J�j��Hd cf�q���c���3�΢�i8Omq!�c`
=�Am��f �x���i<�����_�'*��ibA�����Ρ�]�c*HdK	=|RŤxR���ŀ�+|@؁�)nC�j�i>|H���`��yw@�(ې�-���B�}A�?�{q��3�k\:��@{�F@�J!U��c�a��?Emm��&�ɀs�&Ch�<i���(`��>D��"uA��zA)D�Koe|��� +D
��(e�Jvр#7)	@�$���y���'��7�m#}����`�)[����i��i��"u��/N�	`�
�7��&?1DM���:�%[I���A�$�)|€o�f�E���'��EaG]m�� �)b֍-���ie�X"J ����b@]��oN�]���ƛb�&�}�?:`�iѢ�ܗ�i�b��oB�m}��b�m&I��ix���Xf��'�@V/�d�i7o{�Cmi�ʀ�N�Y}g �#k�ܖL2D���V�?D���Li�
}R�<*��}�S@"��/�t�{�@���	�it��/��m~:���~ͩc��c���c���Xt�<���}?^m( �F9P�9ga�{�?D�i�e����7�W`�a���A��?�"�]9�@�_�]9�i $gCPX�A �$���_C�-I�`e �i!ߩc"?^m{��i�H�$}@�-<@�,Vb�{A@Gz�� 
��.�7()��f�i\��@5��b�i#��''��a�b�{$_	2��o� 8�!A�g�%�|c �%�P�F�%?�iP�G�:���A �{�@۴i� 㡛���{&�{'ߕe�ã#�]9(߂k�0�Va�o��
��	oN� )�_9*?^m+_�-�Zv�J-n�`��o�գ,�£ >D��k-�{.�_9<�}zi8C�|c�e�a"�?DnW`#/_�-0��1`1� Zd2�}�a��3_
}zkW�wb�ՒFa4�_9��k|@��}��V 5`1��k�����k( ���	f��k�ٮ.r��e`16��W�E�,Q"D&�L�Z�gˇ����O�H :0j�#�)��1�J�р@$f[
oF� ��_9����m����F7_"Dd��|@�7��w`�#�`eT@)�#�8_ D�B�i9_ Dh/�{,�Ad:�?ͥ}c��`e��݌;_-|;@��<�_9q o��<�N�f8=}*�;u΀�?�@�,F
}�}�	�C P�{A"D"�_9�!ZdN��i)v)( ���	�
�<�q��i��_9���}�`�膂_9>��ioFף�o���A��#��}?�)_#o`1�(}c�k@��i��AaU���&�kb���q@]d\��hTN]d��Vs�k���J�ae}�Cvf �iU ����}�#J!�)�ae����*���HoE)^�ɣ������-JOg��O�aP ��:a�ʲ�o���-9I :<@�h�)]��yq@Od�@)A��{�}b��8B?۽C)
a&;e���W`�G�KmD_VmI`�����`�}V�/De�Cd:A��E_dwn$|��
٘��?:���F��S@wUF��-��%; �by���7�&�	odž�o`�S`��ѣ�I��i�A�y��"|�G_Vm�"nH��a!��V���I��J�of��z��
�Ko� )L�HmJE��'��}���R��^Q)w��c�bM�:u7`#DF ��N_Dd.��HA�T��9� �i� UU)�>3��ae����*ADd�Ϫ��awY�	$�`��`۽|����șI����:���)���!ۿxEd�����ae�{�O�CvPߛkQ�}l�o< �m7�Cv�}�aEv�A���i�}���R_
f%`Wm`��w�Т*�o"DSDDT�o~�
 ��}~	Mm:��m&�M+���]�M9;$|�Mm�"|����U��-��ceJ��V�]�M9W`8L��[�WVmS�}��{`�	fdw��ce��}3�o�Bj�3 �<�ou��{R��|V`�d۫�ۦ)T��a�	f3X99�I{ ��B���dwS`{ Ude���(X9����A	f�i�}�)!U�ce��)gX�}`۷��F"`�䶔�k& ���R�\�7��8|��ʀy���;X9Y�)Z_$|q�$D�!\dg��+R�_��v��)edw4�Xmq��}� �%[Wm\��i�$�Q�c�o��x" �&]det�݀�
�]?��N`�}{ ��G2D$@o]�$D
`Nm��^۲�F"��8�@N�kR`�WW��)��&D_��-�A
������ce���a}��cRoN�d9[+͢�;u���@�~������-X9Y�_��.y`Ӛ`�X���n�`I���H��.�a�c�cI��Kd��ܖ࣬b?X9�de_�Ym�9���B�	��%�
�cc_2D7&:J���1�)|�ш�8X9���v�!ud?DDR`�}o�o��2ÊL�'e_}�@o�"�i!!}��:�c �ڻc9)eef�)��_����g�a�Y90��xZe��92H��D��I��h?}o��W��@X�A�����'D˕��A ��2���V!�+A�!g���aW��5�� gi?�{jۍM�'B��aޡ�k_�%��Bml�am�'D
 �i*�!u3ݣ����37͢�f	fn?�a:��"�������� �ݣ���A��o}J��ݨ���>!��V��#��p_oq�-W��yg
f�����)r�AmS��i��k�|�T��kBI����Hd� ��-J�%DV}J�l$��b~Ń�y '�cI��-gH�ЪN����#�i�+�{[�q���Zm%@Mm� S�UA��\��E��@i#��6�k�)��"@i#W�{����n�i��ʣf��sߋk
��i�=)NI"I@��q	)��Hd��	}=�?!�&|����&��t���AfeI��€�pu�#u���`v��zw�m8���(��;|(��YѠ�C��iaC�?x?��.����Z��m8T�iwi`��)f@��	 �-y�;|z���`:D��)ڸ�a�Ao������zF�B9��&q��}!e��'<n!A;�o��o� <ncC$g|�ր�͋k��zev`7k�f� �@6nK��͂�|`$à��X� gi ��g���W��{��-aC�-I�K����cΣI+f�ې�
o|`�^���|�'H�<D��#u}�he�]e"<|���ܺ�5G~=n-����x�q� u��'Dk�m8?�c|����=n���B�	���>�N�����{�o|�7�ť�heS��-�$֢��he���6��0ߣ���-���iS��{���Ā�}�_�a��? Fag�{��@/u C/g��!gj�3��m�@`8;@$u����!\��fC�!g��e�ʢd�E�9��{��}�@��3��4a�i��o_#<| �Am@�]W}�@×g �<`�e�@}t�z:A3lf?���G�1Z`8o�JdG�hed��mu�ט��c��}��?�_�cK@��&��!`ˢV�Jv��� �:ʠfU]m�a㢘`>|&`I�_�.��7&gM�a;�懿:��$!���@�)W�@m��66��h��z�N)Dwb�#�=|V�-��N�<@)D	�)|���a�A�R�Tl��ށ��4S.�f��zv^mR��ށ=Dn�f] !!��a�u�o`��f�E� �-�?)T@�cv���+�(�jǘ�`� �U�h�Ǚc���E+DϜmߥ�;��i	ģS�!g��E���:'� � \��af�g����f��f���y`���,D �4(��I��a*��%� /��WI���:`���F9=���	����"'u�$�"��AMv�� C(�(�"g
�$|V!ۺy���:'��}
@�,��m1$)��M"I+Dg��y �ai}��%��ke�m#>H�/���9HMvT�f�i&$g$u:a�pv ��v ����aJa��PMdP�ә�
)���a�_MdA���Tp��a`��w"�9%��aJ��5�iW}R`9z��3�¾�t��g�?D
��ࠢ��fO
o;WDm�`�a���)@䣸

o��$|��o�`�a#�f{��'�rX�`��ߏ-ح�cb-D`X���}>��}R ��e)�E�a��#g���@��$u	��u+'g�a���9D0����K��*aU;��#uW@�-�i8 Zv%��ߢc8me&�ȣ���a�#���C�)��-�_}e���m� Q�i��}0�l#Ii�Tme�+|�&�A
}I��m��o��)k��a��@d��-DN��ߚ�9DX�(u�`�?#Md��m]@�`g ��fh� �f��}�jtb�-0�p�;�+�
b�?�#ua�(g�#gJ����*�
)b#j�F���~�_�a%F)!A(�Z� �q�w�@�a�a�i]`�?dN� D����I@
}W�%����(vR�|�?gme�@
o�_
o��Bq"ۯUO�(����p�(`��fe&����{��c��ae��}�  �
�R��cN&�iG۔_%� ��}l��Hi)�(�_�i5@f
�~1��	&]`\d��Av���iA���=�}��c�`�ܟ\d�(u�_�'��%Y�k���?mN$:AI�h� �ynenw�-N ��J�G�n��݇�{¢_�i��-f��-��{C#ǩ�_�a�ne{Ef �a�A���/|��-�_�'n��ݦ�Jm!Dg��'� dne��'q=�c)�|`$� �;�b2��i;�F9�S]d��aC�-DW@T94�"n��-Ѐ��AdO��ig� Va�#3٣J�z�I�'��c*�;n�����E��V�;n�z�0��(�_]d��/DʮCv8g���	�o�ݕ������3Gmt?�Z�'�_f��Cv���z$F�"`#DC��j}�J�+!�Nf�U 
k($2$'d U!
!)2+
#

!#5!
!
R	n�R(;

-				�( ;��
-
S
J
-	�1
-d�	'Z5-1�%(
	#S!1!
n
d*!)'$;2
(-5
-
n)2S!	5	
-+R�2�)2�
U)-�!�5-
5	d
h
)�
+J
!	
$h	
-�5dU		d('d�
h
2�!
dU521%R

�!(!2?S		
2*	�'k(J1U*JS
-�!#
	
5

-
$*d5

5	�?	��U(*US


(
!S! RJ%#	�
5 
�	
	2�%	
R%!�
	�%*J'�	k!�-U-J*� kJ
	
�(R1�5
;	
5%?�!5	k?�%R+k!?	
+
`-
�dU(d
?
d�#J"dhh
-)�5U(J
-Z!!dJ)
	)�JU?	�U � 
*U)	2!;5
d�#		
�k+�k
�*'%?
?

S
	
% Ud*�)	�Ud(S(S1;(%(
-�;?�
2�k
;
�;R!U5�
d (*��-d+%?�	
1
R %Ud
%!��d(;?!�'	
d
-
;�2!
$1)$-kU5'5d�Ud!%#d$;#!

		

!�d5(R?
�%'�

+
		
-�RSJ%k(

R;

-?�5?�
-*	*
-	�%+#

Rkk�2�USU-2�J*%!	;�%?�k(�?*�k5d( -�

*�UJU-nJ*�
	
	5'2	1U5S;(-?(R!	�2%)1
#�?%R
n)#J�*�+?U?
hJU
d!�k)�!?�
!(�'+2?�2�5
�;)�#�
!"
d�dS�)
��;R�!�%5;
�-
#hJ�
'�?!�)2?k�!R

%) 	
-
 (%�!*�1�
�?JkS
#?
�#5
J�2+
�5)�%#�S1#
	�?�
;#Ud%Udn�5d(�? �%S?S'	h%?
(�
�? 
2�;ZJ;	2'�?�*'�U(�Ud5�
�Ud
%	
?U�	
'�%Uk	%%!	!)2*5%	5)
*Jk
�2�
	�
)-�%k2?

*�

' ;hdUdU�k2�d
?	5d
	) %+
!J R?

!)R�U� �+'�'+
-	2?�UR*	��	
�'�?�	(�k
k?�)1(�
�-R�12?!�2
#��5 
-SJ�	
!R
�) 	Ud?�

	
#%hd;R;?;!2	�%U#
!!
?
�! !!;�)-%;
(!%
� 2�
%'�	��	#�?�%�#�1�;�5d;R!2$)�%	�	?� 5;1;�-)	n!dd %?�?�k U5!RRU(�'#
	�#! $h5
-
%;2?(%JhR	�dnU2#'
 �5+
!*(R#	(!	
-

		�25dS(�
?#�dU%�!"

-
 R� h

		?�
(Uh!*(#
�Jd
- $�R?-	�k
	5;d
?d�U$hU
hUJ1�2�U5(;k�
!	-
U;kk+�
-n2115-

-
�	�) J�UR%��S
1�2 *!;1Z?dk
��	
�'k)�(
;!�?
+S� 
	�2)-
�
#1U;U
Rk�#n5
)(
�*R#?;'	
2
	k5(*�(5	2��
)�;%h
-
)2�);?J5
;�
	�;RRS?�!;�(!2�S�5kUd)5
�	
�;?`!RJ�(d5(#


�-#-
'?

U
R2-
(US��!
)�;5U
�%(	h5
�(Ud� 	

�
(�!R�
Z
U?d�*;1Ud) (�5(J52�1;d�?
U 5�	
�'?RS*	!��2�*(5k
�(
1�2 U
$ %U()! (2�
R
!*d$2�SU�' !%; ?!'�	
2�
�U5R�
U
-	2�k�
�kS(��
-2-!dS?!( R51-
�
-��J?%
	!S?	� ?(
5�

�*2
nkd		
�5
�?SR
-	
�* 1�R5
)-
5�k	);�'��Ud5!*J�5;)
�?
�	
;#!Rh5d�?%2�!h?%;'S�
	
�)		
d
�;?#k;�JS
;Sk��#US#
�

-�2!!(

%R;`�?
	�	
	U2�;	k!�'k5U$!U�	n?2)!d;�2!!�1$1-"
n!
	�
Z1# �J#�#J`!	
kn'hkJ�
%1k!S�*%	dd5U	!
2
%R		25!dR-�%!�S	�
 **� �1�
;�'�#!'�1'(��#1J%�)�?
	R!+-Z2��

�
�	�k?
k+?	R*�dU5?� %
�*1�d5%k-%5
2-?	�
� k1?�
	Z	?
�?R*	�
	S
-
!Ud�
��5(dk(
S�?;#
#!R�(d-	
U	R�
��
U)J�52	�J5U2d	1R* h!;1S
R��#S%!U?
�	
#Ud)�?'d;�dkR5�
#5 h?*1�
)*�	
�?�
;% �*;
�
1#J
 U(S*R2Rd�#!!?;#U(%�
 k*!U# 
k�?JU
U
-
) #dJ5- !S#S

?SdS�
Sd`	
�;
*�U(5k(5 d�d;	
)*(% -
-
#�#�
!$J5
'�
	n?dR*S	#�%?
'�
*�	
%h*5U	5!
' k1� 
?$�(!�)!�?kJ�1	*#%2	
	�-*1� )*?'2	�	
!	�1�
kU!5;!U	
!�#n?5
n?Z`!hd+
��;R
	
-	5	
#k2�2�k'�
J!�J!d�R1(�# S�%S*
-!RJ
-!R#1
S*;d��(5Ud**
!�*
�1'�?	)�
�(k?�S' 1(

5 )#d*;
?#k5
	#'+�

�( �
+)--;1*�;�(
d�*d�?�
5?!
�
-?**
55
U(!'JU�2 k�#

�d?�U

�
#)(*S� �
d ')!k�?k51?	 �-+!n5;��#?�*S	!#'?#S 1)-�!+U'+5);
-'
k2)�!�#*�	�; !�'S
2;k��5 k1(n5�
!?�dk "'�U5;*k;J
1�(!;
-J�R##	!�
�%
#	;R!dU(?� 
 %;	�k?2#
k#+! hR(d2	�
�d�	
-'
	-
	�	
dS�%d-	
!�112 )Uk'US5!5(�k 
�%#$k%Z5hd�'k1)	S�%U1�+15;(1�	

	!S�!% !�k;k�-
�?�)%	�
Rd5( 5(�Udh!

	�S�
�?kJ	51?d( 1	

-�
-dU11
1	-
#d	� �
)�;(+S?�($J	#S�5R��!�
k5%�k	'U!!!R�

!�?�
S#d-)
(U5�R?�	5 UkU;k�1(	
�R;)2)�
)-�U	5 n
�
?Rk	R�U1--'S�	��
%R?�
-	
)�
Sd5;
	R2
	� %52 �(UU	�;?hS;�*

2�2	5(*�%�
	2	1�+ ?;
?
?5 	�	k?J�SJ;5k�	U?2#+�
	`	
�
-k!
SS) %
-R�?;k�	
	!!%(h!�

�*!1�	
#
	k2d!�#hJ�!
;Z
�!
	d�
�R�
# �? 2	%#��#%?�R*h S+d
U�	1'!d
-	Ud+hdJ;kU�5n'1+J2'�	�S%;	S		
�

U	!�%h!
�;d%'(
	�
;*kZdJU��'?k!k
�d(U;
#U;k�?

�
�	
	?
%�	


-#
	%U1k!
21J�
�2;5)�;n(%k�?�S1 U'�	)��k+�!!���-
�?% �hU� �'
-
�?!-
�*2�)!U��?�-%
J5J� %J?$!2�U5d�k hJ� (		
J5�;
-�5k*`5d(U�
S
	�( �	
?#U#2	�
;
%
%�U(*1
-�kd�!h�#�
	1
	�#'#�15$	
�(U)�
%*S�+h�5()�;UU?(5
(d!	
�U2h5-#�	
-
)�5(U
 1?5
;-�

dU(!
�?d�k!?	
�*;dU �?U5(?(U%	
(
1%
dUk
�?	d;dJ`*	R(�	d!U�*?�!#(R
J-)-
Uk1�;dRU%5�		
�+
 k�?�+nk	
;(UU5(�-?��! 
! (2(J-U�?S')
?	
*J	'(5Uk�%S
+?n?�?!%%( �)
-�% k	
�d5!	#%�

?�)�
	dS�Rd5

!;�?'%�)1�#!Ud)!;�)�!�1k�kdU!�R(	

'RJ�dS	
(UUJ�k-(!�2-5�5!%)*5!d?
21�	2J5U*kR�S5%Ud1�;�U5�d!)h 2�!%(�)UdR�U;d S�#�S �
%'?#UJ�1�	#�
-
51�
�1(5d�S5��?JR%1�*h%��-U2
hd	J
-	!	
)

-J! (*1k;k+;�!-
5(��?1�!5�

!*S#�5UR�2(�5���25dU	
�d)!J5(


)-� 
�	
?
#?$!��'2J
�!R�!)2*);
�dU�;1-d!(#-	J�1)%#�1	�)#�(5S;?

�?%�-��	�;d�	�
�d2�!)�*?Z
-�'2 �
2!
�
1
�(5�d?
S
?�1)S%'

�(5dJd!�
	�(5R�Ud�	
�
�-$dS�d) �S15�	�
-#�dkJU!k(#+	!?
?�J!
JS2`?

-!k`-)�
-
*;1!-�)-%d�	
�
h-;2`!�;
))

�';dR;	;	'(RS;!k�h!#!15R#�
J;�(!
`	�
;S�	
-�
;1�(U5
�;�5�(

�)2+�??RSU�J	!k
�k%U1kS+?)J%?�(#'(�	�-
)? U)#?	
	
-�%�	?%)'�;h25?-2�
?'�d;'Rd5JZ �S
?-
d;(!

S	�d!R�	-#�kS1	?�J�	
� 
#U	2'?�1R;2;S5�5;* k�S+�U2ddU?%Uh?;1?- J�Uk�?�#5U#*;d
�;1( R�(	
+	�J	
#	?'
"!Rh�
�d5%
?R	�!�%5 �
	?!
�-?)2�
	-
�	�)	�R!;2
!
	;U5�
S!
(5k(U5!S�(UdU#S`Jk1;)�	1�
!� 	?k5;�1;R!*�

	
�-RU++	
2 �) )%(?�(5

d	U5�)-!?	#�5?;U#S%(d5"1�(*RS
!%�?)�!h%
	�1
��d�1%S;*?

�(J	
)R;�
	�U
	�(h!;�%;5(�dU(k*#

)�#
�h?�
	U?)
�	
5�!5U

?�R?��-d5�?Sd�
	S?!#!�
�*nS#�kk�	

� S	+�2(-2
?�5'�
-%h�?� k�Jh5
%Z2�
�(	- �U
-$n#�	
-
S!d(�	�#2'12)(%;112
 !'1		�'n
J�!## 2;�
-(�dkJ�J
!�
�!-�)� �)
-kn�U#!
� (%S?

�5!�;R;(
;'�k	�'1�%
k(�%?�5*U' ?-)d-JS?�?k
'�)
-�'R�J
-
#�%5�R�U	
�
)�*	�!dUd�?5U�*?�	
?U512
-�5%k�;#�k*h5%UZk�5�'�?;d(!(5d�?�*!5(�?'*1�1 RJ#�(h(U�5
-�(*	?'*2* 	
-
1*�)
 *
*1;�#S?��21
5+)
	R1'2 ?��?�1) (d
55d)�	1�*
!(�2UZ?
#
#-
�	
-)#�
JU�
	�
	�k�
S	
-**dh5	5�k?�)S�kJ�(dU%%k
hk5�;	!SU2d�#
�'55� 
`	


(SR*)J *�5kR-�%*�(('�

	�( )?1%

	)-
�1; 	
;�()-
(%�hd(5!k-�S+%'"'RU�(d?�15J1+%ZkS��SUkS�

-
�(�(2�'#'
)�'S�1## 
	�%d5;
-)!�-
�k$Sk(h�	n�2 !;S!)	2�
�*;�?�
(5%	�)

n�U5(� +dJUU-)�;!!?�U1�

#;!J
�
5kd-
-JU-(kS%��
	1*
2
5�(5*J(
�#�R*(�R-h51	�J2;�	
!)�	!;1�%�?*
�k	!U)(�!(?�J�;?`�h5%R��#+�-$�!!#
*�kU hkR-�* )U
?;�R
)-�)n-#;�%S%�
	)�5Ud	#�*5(+�k(�'*�U	�k�	�k;)U(
(��%
ShR�?	-�1JUR()S)%k'1(dk(2*	
-*'J�*);h�;2
#
 '!J?	J)-�U	SU�2�)
-U;%R
Z
J#S
�  ��J	

�U(�2�U;'U	R	
-)�5-
��d5J� ?5?�5k?SU!U�5dU)5U(
�
-!�)U*�-)�2'1
�
	Rd2)d5S)*�d5�2-RS)2�	
U*
2� #!�!5U5hJ U) Z
�2%	
!1�
1Z!(	�UJ!#!R �*;�;(%
#+�(�	-?�Uk)kSRd�
	R5hU)#?�-
��-;
n#!U�(UR���5JR��-�
S%�(?21$�
��	�dU;�!dd�5;R��	

-	5dk?k�1
U��	(5�#%�%U!`% %J�hk
U#;S-�%J'�11R�	5#�;?�*�?Z
n+U%(2�(d��J(#�5JdS	;�R1�kU�!R
S
d�5
-	��
 U5
��?��%1� 

U
J1!
?'
��
2�J
-JdU	� (
	� ()(5!% %(;dU1Sk-) 2U*dhk;?�1dJ5d55!5USU�U22�?�5dU '!�1;#?
122;�U5d?kJ�!k%(5;!
%
J	 !!%S�1;�U
�	5
	;�?
d5U�	�?)�	�	(�(n�
- U(d
*��#?nd��J'*U#S'1�!-2 2 
?	�!	

� #S##�	
-d?�

%' *?
Sk�k?2?
-


1�
-+2
)?�
)�)1-�5%S�S'J%
�1)%�
�
5
(�
!!5;!k!*�;5

51R2
d�!k(U	dh%SJ�R5;d5�� S'
U5	�-k5�RU�
;	5R(�)��2dk#*R1 	
#�R;-SS��	

?-)
�!�#�5!k�?!J
 ;
�%+(;%5Z(!U	?
�%	?5	

U
� 1;%U);k�
-)!!5
�*�5� *
�U�
-%U�5�U%hnR


!?)�5k�1 *5U�-)�'(
	1�)?d;d) %�5-�+
�S5n' J?Z1)�d?�
!%�
-��%S�#�)U�
�;JR'k)	+;
��#!%#
%!J?$�5h�
Ud	�!#k
!U	��5d'n1�;#*��5�%d5J( �SJd)�S-k
#�1�
	-5(! ?)2U
k� %5�#�1R	�-	
S!�1	d	
	�k`;;R�;h
;`	
� S��

	
�!1R�5dU�-5 
kR%
;�%�k!
U
�
	U
%�
5
J
#
#U(	� UR! kd;n�
?!	�51;�;�(�
*�
	5(�;R*
5	h;?�d
� �)2	*?*
k*�	 k�RdUUR!'?�!
-2*!�?	%�(*�
	5U	(U�
�J!Ud*?�%k�	
#�?
kh!J	
*R?�*	?;(�U1Ud(!%1;d?�
-�!
�J+*
-?	5	2)�
R�	
+;k#d;(	
�d5n?�S
(U�d5J?h
�;
	��dJ!�5 
khR1*�JU'hJ�Z
-!*;?)5
�1UZ (n(;U
 #+!'�%k? 	(5d) 	
�%(��JU;�d5?�%U*kJk�;d2d)Ud;J?)
%;Sd2S)
k*%�

J�
#	d-?'2�;	%S�%!
�?;;?�
h 	5R�
-?R+�	

Z5	d�#��(k
2J?�-)�	
#�*' S! '�kU5;�!!U;h%k1��UJd%

� R�-	*�!'%-#%�
?'
�
-;k�*1 ��5d�

!)% �
k2dn�#;�(%J	��dRS%%	S*
5	
Sk#�+-
%*`	
-


;S�
	
R	
!n;?
;*%1?*�5dk�-)�
S%'U?1�J�'k�-	1(''1	?� �1 %(
* 5hd  �;��		JR
;
�2#%#1!���R-	
'+�	R#��5�(;
;�5?5U	)	?-�%�Jk�?
-)	( ;;d-	1�*-25d��!- 
-S

#�d;
n
U�#� �5U(�R(5dU(!#	
!R%R($Jd5�;;5kUS12!k?)S
�(R!#?�
-�5d(J�?!
n-
-	�5;)%�U�;#*d;5'�k�5U�(S�55+dd;�Un�U+!	R%! *;%+
-?�(!�1)�(!(S�
d���15?5RR�'-
?	1;*!
Rd�#�;J5	-�	1?!%�)�-S*

�UR
 *�d-
� �	)�)	U+%�*�!!) !�1#R#�#*)

-?1;*RU#�(�#
J%S1�?!
;)dUZ%R5�+
J-;%�)S!S�U5�
R�	
;�dU(UJ�%;R5J�
5k5d?;�;-
-dJ)
R�R�
	55!(1k�%#

?SJ�(#�5?d�?(#;U#�'�5
��+(U)�d?S
!d#� %��#(;
U5�
-% �	)�
�U�;	
�%''��k
;�)�S+�5Rd�k)-) �S	!;R�%Sh�?;�SRJ�?(!%	
51%(�(k�Sd;�U*5�R5d-)dU**-
	-
5UR!	S
S
�*
�*#
?�
�
��	*�*`S*�-%
?�dUS
%�#�;�d*%!��(d?	
-%k-S-%(U !SJ��'?%
?�(1S �		S-RS!
Uk�1�-1

J�5?(�
;(�JUh!�#�5R��+J5R;#RU�+J1*
?�dJ		'
	1;

-	?	kJ%
d�
!�k(Jk;;5%?+Z	;	

-�	
�%#�%5;�5)(
dU5h%�!%5!R5�!5�d(UZ	
		�*5%�+	?1J�S;-)
�;� U-
�5

*?�Ud5nd
	�%5�5
)-
;�(	5n�#U#)

kdU;	d�'
(
	�)!��5h;k
)�
k#%*#J�!
1
* ';U�
-+
?!
#�1	(
 S	�
;-+��	'	d5%kdU
d;d;�(2
(R5�+?�!5d
(U#�+5
?�1;%�S; 52US?'�-��5(d 5(!*'�?1*��(;!�S5(5S��(`!?�SkU	
)21 S�*�!-)'� �?;k '�$*`?'�dU#�	Jh)�*'�'�h!'U�J#)�
-
*J�#
*
;-
J
'�);)1k�(S!
 )�%�5d2)
�1R(*U	-	
5 �'k�
 ;!�S*�1
	�
(�%k�Ud�5d?U
%
�k�UhJ�

#		
	5
)	5)�-dk
15�+5Uh'�'�;R;!1 �*(��* Sk�
-�R!��'1*
#! �R 2�#?##
12�*(	� ?�2�#5
	 ��'�
-' '�%'?)�%	�(%-J	�JU�#�!	�1n?�%	�dk
S�
-R
%�%
-
'�d*-?1�*
U*;(U#1�)(1�)2�
-((d
dkU�'�Rdd
�;-
2k!�d%1�h)
;)'khJ+%)
(%S	
%
 5	h'!�;1!�S;�U;!5dJ��	;5 #�
-� �% �5-)2kU�'
-U�);;)Ud�

�R�RS
�
%�ndn	
-
2!SR�*#?S# 1�'�k'`1* �?Sk5!�)??
#*�n1;SU	�U-�
!2-� *J(#*d?dk*S%	J-��h
`#��
�
`
-) U�R
-)�- 
1!?�dU1�dR%�;S?��;!�
n��k55nJR
S115d�#
-%US
	
�
-�RUJ(!dh*
(�SJ�
	k?)
�?%�h#5k�#2
h�#;U(	(d�(;R�RJ�
dJ5	5	#
n1� 	S5�1R�U'R	'-U(�;�;kh?��U%d
#�*?�5hk(d�!
�!*
�%�S
�;U
�
 �(h1R*��(
�*Rn!�R;URZ
*k��';�!(S
U�*h�?	U%��U!2��	-)%'�(!k	;2�	Rk	k�5+?U-)
?U5d	�?S�221�#*!k 5)R;J5 ;%?!d�'�?(�dUJ�	�
�-
	�
)2U)RJ(;J#�	)2�?d�U!J'�1RS*
�S	)'%;�('d5(#'�5(
d!�k
!
	�*�#-
�!�	2�2?�*U
%5��	
�SJ
%S�S��'�
�2	* 	�1!R�')(1-� 1*hd	�Rd�S�� �;?*��!;�U5J
( �	'd5�? 
� 
-
(+J�)U	(	��	�;5�� �U5#hknURd�� )-�%d;(U*%(d�1
-
!(';�k
1�J1S�		?5h�
�1 5
k?#
k?h!(1S5!�-'5RS1
?
Uk!k	�d5U%
 R�? *�		�5(�5U �	-
?	;
	�

#5	��d�
hS-(?�?�d;(1;)�	(U'!;U)-!k5d%JUd�(U 1(!Uh(
�dh	)% �
?%USR!;?J
1;5Jn�1S�'% S�S+�
��?S�h
-�1+(;�Rd�S;S55*U�%?�
	S�)-�1�S
)�S
%'���(*
�!*J1�;�	+-(�	�
�	J2�
n	*d) �;
R n�!*'Z	
��?�-k�S*5!U)!S�	
�
�	d�?

�
+-�55U%'S	d!J�%�;'2! �#
;?R(?k? +!RR*�	 	�US1d�
�;	'	�?-;	`
-
?�)U
#R
�;JRU'�S%SJ-
+'
�5d)'?5 -	U5S51S+U%(%55S?�Rd
k�?%hU�* �(k
) 	+(h-
�
 #
h*�'$5dR5 U5(�U1h(;'5
�*5d-�k1h�SkS%
 1�U5U5*�	�+
�
#!�51*1!%
	d�%!d#
 U1�5-�#
# ;
;?#U5%�
k�(5%	

+;!��UdS�#!dJ�dUJ52�U2(U`'	5�?��U(U
'?�%

	

��1�k�d(55�J�� %#��5;(5�k
-#J'SJ'�U;5	2);*;*(;1��JU5	S!R��+1�
	)UdkJ�!;1U??	?$'k�k�(hU
(S?2�khU 2d
�;dUUk�n��U)
--S-%-('!�)2Z(R�
-'�%J!(
	-�U �JU 1��'5
2%�-'-
�U5`;kd1�'2`'1�?�U?
S?�J5dU 
(JR(d�#5�+-
#S''� 
--)(�1
5S	h+)�
-
�;
n�* *1d!2�S(;1�S�
R
+�;*!*(�
+RU5k;'�k1!)
k%(�*;;kU'!(d�%#�2
	U
�JU;U)�2�R!�?
;
U;

(U!
J?!U5R�5)%U�;1*kSk';
�
R)J%1%J!
�)�?�d;;(kU�2�Ud%!R� ?

5(%% 
)�
�U5d#�
	!�	)�% �(%U�#	�
R-�*!�
d'%d	�?!�
�n5�%U#�*2�
2
�dUS�5 U%-1U;1?d
-
%'?
?';'�%
(-dkUU5S �#'?(5U'%�+
S
'kk !dU'
�hd5-�dU;?U#	)
-�
!R5��U	?dU%5!5%#�	
�U!S	U?�-)
� %�-5#!?*��
(;

	S);R(�hd?(�(!�'(
Rd	%!?J�hUd) +S? (
2d'S)2�R(*�U#;�%*
;
 �'�1�?%�?
--)
J�;?
%��5%S;
	!�)�?�#	
	d(dU�
	)*R�R1-'�)1#%(+�	d�S�U*#dRh�#� '%*5�)()RZ-�#
#%(U(2	�(#
�)	% �(S
'(5(Sk�k�?
?k�-n
	

-#)
�
2'U
5�;k	'-	!
�5J��(Z-)UJ�)
-	hk
�!5�%5;
-		5� k
(d)RJR*5�1�	)#(
�	��)
 U;?��R5`	
d*U(5�
?UR
�#� )1�';1�2`	
�* 	hJ�*U�k**)!%� �?�?;k;(	5U5(�SJd!	-
� (�kn(k*kJR-�1 	%!1	(2�(U
�*%k�
�5d% !?-�	*U�d�h'
*�(
#�;d!?Uk;!	(SU	 5?!R��dU�	!J�?�'�5U(	�5%(�
	#�5dS�U	;�;;
dhUR!U
	k%#�	(5�)!()J5#
d��


?#�
SU�dU�kUSd5
(*J%��#?	k�5d1#*�d5�	*?�
Ud(�2
�+k!U�!%S�?	khJ2Z?;1-�
;�nS�% '(�'1(R�(;�
�
Z
	�(S
�k$�!+J'5(�%J �1� !� !�* 
(%%(�
#+ 
�!! h-	
R#�h	�'#;-	#�'#* #!�(Jh1d;(�*15%(5U2�!�?#?)�#�!�!�
�R ;U 

-�
*	+�	
�dk
-�d�;� *�?? ?�?##
�5!(US+d(;5
22-R?%!�?�J5
�*2�%RS �*� ;!d-
�kR(-?)!d�n)�!S�-�Rk)�*UdRJ)U�	 
'!	S2�U5
	JR5)JU(�	� SR5U� )J
J S)5 �)
'1;�(d)'d'
)	��S2
)#�+	)R #�5U 	;�!2
-��2�(;�	k?	#� n�-
(U	  h�*
)	* U	

�5!�;d�S�dU�!)*�k

	-21�;	U15h	!#�?
-%
5��-
;h�15!	�5U(nJ5 �5(;#U
2 1	5)SJ;Sk
;(?d�?k
�
*k1
S�-�%d�)'�	)	S(�?�#hd'
�R!dJ�S


�
!'#	�-d?�	�(dh �k�!Sk�
; )2�;!5;
5�

-'SR'(!('�-kS��-�
�#SUh5?(Rd	?�;#S	h!d�-# +�%
Unh�U5k�
S?
%2�)2h	��S'Z; '!d5d�%
-�)�d)�d5;���?J;dd*)2'Uk;%UJ�)U�U+-*
%U�- �dR
d�?%
n; 2�RU(�
�%2SUJ��
�
-2�%!S(5
)-�	k'?1�d2dk�%1�	`
	U	%U(d
k�2�*Rd�)S!U�%';!)
1�S2�	? 5`';5SU*R��%S	! 1;
�%�;''(�(-)(R'%	
-U-nU(�2	�
?(5'�;�%!1�#*	�'�5�	(;�2)
	;�51 �	5?#		'!�5+��%dU �1Udd�R)�!
-`(R;5�?�k�*2 ?�+2�5�d!U
2	 	
 Sd�(U�?�)d( ! �	(S5?�U?dd		�(
!U !
-
�U5*�dJ5*�d;(!R%
�d!(�;*'?�2�(*	)dRR?�-	
�k�S5+��k	?Uk()(�#U2
�Jk%��2(	'5*+� nR5
-�!?	U?5UU�
#%R#�S�-
2!%	*�;**R%
	*�'
�*d�*!2R;	�1� -�
?5U?�5
S!� 2�d!5	d?�	d�;d?SR)�5(�k!?�;�� J
�*
S?#�%#!k�(U
�SU  �;(SUS
+'*�;%S;�)
�Jh;�%;�R�*?�R
 
�#� -R1'	? ?;�k1R1�UR	%J(;*�!
-�(R �)J;%?�!*UJ!Sn'�%d%
*�%;
U�?		!(UkU-k52Z;U(
%�S 1k(	52(?UJS�d(�Sd%*)�	
*
�) 	R�%�(
'!'R5*
+
�
'�5 2?;%	�R�#
�+;�5(RR�	
�)2	'

!	U5R
�
-R))�R;(?	
--5�S)
-R%'
?dd� 	-(
n?�?	5 
*S'!�dR*%	k!(d?!R
(
dS�;d2
�U S(
+�#
�#	-�*%%�2*?k�%
hn �'(J*%+!5U5 �2)
-!khn�k�?USJ-J"
	JR(�'k�-1	'5!;�hk-*;U�;%�
kS�RS-);	'�'#)
%SdS
5�)*�R5!(?-!--Rh�hk	S-(�U	;R?�5U
-*d�?!)� 2;* �U5J!51)2#)S	S;�(	%
*U	S(%#'�)?
*	#')� 5 d�R#�
-	
�;!d*%J�!;;dU-5'�*d2� -+-�-�!*
--5�'	%�k�'
)�(

!JR	�'n#5J !%+�Rn#%J?�;	 �JR#�n5?)#R�)##!S 15;*%;!;k-)
R-�?R;-�1
-*;; 2U�	`-
� '#1�?#
	+�#�k1�	-
-h�
-�!'�;'!+ !�
-�-h)?;
-*dU#J
? 2�(
-k�	�%k!��(2
'
�%5(�5dh
*R�%�*!'�'+R5� U
RU##�URJ; %�dS�k)5�;	

-d%15�	?�(�??k�	����
��P3����`WS���RP�gp�c�K:	p��
��)���~b
P�RpI"P�#��r�}�@ʲP��`q~���0����u�P�R�� б�P>�@�#�ق �]-!�
�" �r# �S$���%��&Pa�'�d(`�S)�a0*�Zb+P�G, g�-P��. �k/�H�00�1���2�93@H�4 ��5�n6���77=8�~b9��:��S; i
<�~�=�I�>��?p}�@��2A�bBP:�C��D���E`�F{C%�sH>��G��H@�_I�r�J�S�K��2L�YM�B.N`�#`Z�O�
PP�Q���RPI�S��]T�cU�V$RW�	\M`�>��CI�[�XP$2YTpd�,���Z�R[��b\ �T]���^0�_`Z�`�v�a�G�b�Ec`�sA�%cd�!�e H�T�Ĩf�T]-��2g<UhP��:�bi ���]j���.��k��lpN9m�R �Bn@��;P�op
�p��p�[�q �xr��sPH@l�V|�t���u��"v֟w�~�x�
���y�z&z��{�"�|�S�}Ч�~ �`�O5p�� 5΁�Se�0�l@Ђ����"��҄��b�냅0�<0��e �{����lj��Z2��U.��"��l������Í�H��c�pK����r3�Z"��ZB�p+�p����} ��.�P����"�p��0�M�����1��؋Џ$`@v�pB�pY�a+�𧢝��M �tP�����2`��� !#��Z�ua�pg^���+������ӧ��=pʂ�p�R��$�0U��������� �r;�M�霮؂���,�ְ0&#��z00%7�`4��J�@]>��ٲ���X�pZ�I`�G�0��e���0�u+-&���º0+ݻ��@��VP�r���Bv���PܾP��P�, �W��Z�,�Σ���]7����#��C�nRp�Z�� !���v�L��~��穉p���X`�s��ƠjL�A"�"�pbp�C�`�Ȁ��ɰ]{P�Rʐ����{��W��Fn1@.]�0�)��#H������0r�Vl��#� ��А��9�`�R��~����0��p[B�0�9�0|�>���0k8�"��~2��H��'��p��)������^� ��,�Bx��
��t�0vL��U��ᠪi0���pՂ��` _�� U{���j�G] Ѕ]�0��f�~pA�IkV���^ � ��蠣S�p�2���M�zjꀋ����p��^3���p����#7�Cx��`��G �8�P`,�P��0
;ʠb�0�_�� ���°��@���`l{� b�p[W`�p�O B7P��E�"��~rM`-���c� o� ��`��U0lp� ���`tv�j�$R�P�C���1�ۨ$� в{P����op��y����Y��O#{�~�V`�J�������i��FG@B��m<��=��Դ@�ˠБB�ϒV���3��X�n4�xs��7B�P���x3!�t��bAI"=��N?PRg0R��<��q ���p�5�b	av
!y��ω����A�
W��OR�0ؒ1�&�PY�q�vw�6"!�̛���aP����Ҫ���0_7����PB��GC��(_���Bqx��]<`������ʲ��ҽ�"��]��B�`@�h �����񑯪Py3���ɉ7���@}S��!��áG@�Y;p�S8@��F@�rA" �:��a�de@{�����N3�bM�>mE�a��Q�c�p��>��cV�80#��)���0�V Qw�!�٢^`��&ݤP�"!�"�p&b��>�#A�k�p>��S$q�#{P3�%��H�]_&��Ӣ�$"��~���]�Ҍ��B'q�(!>U��)�_
*q�Y����$�]�H�����`�̠D�a�C�P�B+�C�,!��-��wO@JO.�D`�c/5
����pN0!�q1QlR2і ?0oo;P�3�ؑ��M`�4�1���5!b4��Z"%�a��]���6�re��7�oU89�$b:�%;��eO;Q�;�tp #<0w��@U<�S�=a���n>AB�Á����v�B�?��P��3������@}����@1��AQ��B�9�CA��^P�8����^4��T�i!�Yʀ�HD��{@�/Fp��@�P�T)t�RO0_�E6"M�z�F�f�G�4����ZBH�ʂIAlRJ�Z�KA�pb��P` L�'���?~;�ejr��J����� ]�M�nN~�q�"԰�ja���3r�0�rOq8�(���P���Q��RQ�SѺ�1��Tq��n�[��{���p� ՈUu8V�@4A�W�cX�u�;`H��:��O��Bqb�YܳZ_G�p�{�[��,QBy\a������_�]0� U�C���t�$2$��^Q�[aZ"�t+_Q��`��ZP�r�p�{P�
=#L�#�h`<a1�Sb�Bc���q�����R ��d��X�ZB{pG?j �e�c"*�Qlfq�6g�8oM!�Rh�zq�r��i�
dl�b���4��Z`�����[�s-8�jaT�kAg��X1l�Gt��Bv`�b�j���I� qLma�
� SG��n1m�� �oq����<`s�pA~3;�`���qA��P~|`ZB{`\lr��Rs!��"1�77��C���tq�b���@��mu�h^v�t,w!UÏ�]���xq�byQDO��V��;�;�z�tT`�� ����j��zu�/A�be0��?@(,uq2����{�H�]!�$��&`�T|1h��rP��s1Ww4A�"}�~�b���z�AZr���5�F���u|�1167P�B!Ъ�1�փ_(�$R������;�׊���]��|Sq�R���rDP��;!s�DP�V��Y<���L��B���@�����Q��P��rP��5��1��?���PvˊyӋ�M��MPtA�[q�Cb�ؒ7����P��1L��PHR QBP0�^�Zr1E_{�+�Q��A�	e��ܐH"��a�B/�e��+��t���Rn`Zb. �� U[�q�����t`Z�s��m*�a ��)�����J��!�6{05���6Ԡ����%����/1���Hآ��ِ��,��2��S��ת ��E�ecb�q�әQ�����b.`ڲ@k���Cq�Z��S��M p-������8nM ��ep�pL#��~�i�|0pR�:�Z›�0�<@x��"X�����B]����q�b�Q��1gXa��N!��3��WЌ�0g�Q��`�$sp P���s�Q���P��p���Q:����{��ӏ����u*��H"��Y*p���ؒ��<�`����qw�p�r`�{[ؐ�c�����1$,���i�a]�qʒ1P�e�!~9����?ǫ�~r� l��䰬���=`�3��41!����󮑔��q�_ lr�1�L�Axs�҄ ��oฉ��؂���{��;@,;@"w`�C̰'|�J�p+���3�a�;�o+�1�b`�')�q��p�v$�!0�qHι�?�q�sQ�s�!��]�A�Ѫ���@��q��5�@5�pHͰ�;�O�dṲ ��.��?C0��"���?30ز@�H^ �����Ҿ�~rN�BF�����j,0Q�qʢ<��
UP
iT������Ѱ�:$@u����+0*�ʲ�q[�������b�0H����@Ҷd��As��q��i`~�q���e�$����AqN��ZP�i�P��9��eЪ"�`��đʢ����q�bv�~�!��T��V2с�7p�� (���8�a~�P_O�1��+0mns��!wqu!�e�a~�IQl��1�c���̑�9Po' D�/a�:pP�����M�x5ƷnA�bxІ#6��b��6�TpʂΡ)a�!�>�`Z�P��p�|Ё�#�A�s�n3�!���@l���ӑ܋F�֠<�+ �b)�q�cA!���q$�i\y�I� 0C��� &��:�ց���@�q!�K�a��p����[{�p`h�ٲp �luA
שq�� �J�m�p��Q�w@��
0���aa�M�ua�#l`����@����c��ΙP&��`a<4V��ʲt�ْ���E`Zb����u��p�,,`ڛg�b ���r�V�1����|�q�cGQ�R�q�B��y1��{�A������&��`A4�؂Őg�D�";P��݁ق;�����`�fQQ��L���v-Kl@�m��
@�:A�R�Q��� ��p�= �P�R�ф���YA%�`,��ق�q;�sQ�1�Ղ���7 Q�.�A��!ҙ�1��Nw$���$�R�@؂�@�R�5� �M~:�Zc!����Ή`���Qt���RGQ�P�B������5 :��q�s�J��\s�a$b���s�>������60�u�Q�r���Q@�B�P�T��r5�~�kAvNГJ^�Bq� ŧ���i��ϻM�I�T������s� �**�"�Z�7��bO��c6�@����"�8��p��0�5A*U�!)tW�B�2�%�cR"��v�&qU���0V0p�����"�q�"/�����c��ꓵ@���ق�Al���3�5ϵpl�����@������c�!(�ѷ-���u��3����M��g�;��A+"q�}VP�R"є��QW��[TP[�{Ђ� <t�qw�a�����e��V��1p��!oSr����
�-�
b�v�T��`�B-a��a�2a0�7����1�b% fCpl��0��Q, 	jmQ�]TPd�90�r]���s1�)��{s�����B�p�r3�� �h�������!�V�뎀
p�1����Iq�2�1��!0�&��S�����G@�b��b! �I�r��1��p�r��]��1D������s{���/aZm���I�����|ì�<U���Šw�1�l�G����.h�a�!�b���piܞ���U���`��	�Z��0��p�žᴢx��RڀSg�1Fg�x�ʰC�<B"Z��q�A���saQ�e� p����u�Z��"; ����#�P���Ħ��R���Z���na�2����0��Q�#����q�b���{������-��-p�2p|���Qw�*�;.� ���c$������@ơs1*m��� ������eP��^���"Qp�B���r�c�ʢɐS�Qp�R�RW=0�OQp����DC	��B
��2�"D�L.��ьCA�6�{�����2;a}����b[1�^�[�T����p�[�p&R�����ƪ�钞it��B{����C��
r�"����D��s6�'e(Q�ҧqn�-$��@k�a�֢,�D�Ց����360h���BH��⮠�ðh�B�V@���`XG��� ���������p�"y��R������B ����	���NqzÐ�ʒ��CTq����~첑��Rp$���b�p�rqqY��J��/>l`ڲ���B:І�E�z�2Ap��p����2���ҭ�s�=Az��1�p�W(��Q10n\���о�x�?����Bx�l2R�r�`xB%P�֧��TR�B����"a6�w��u!-Q����G��JP����m�F��ps�"����M�
�1}k�`�b`��`���ŐS��ai�.��QR^CA`jT 6p��<Ѐ��ZB����%�R���09$4��ā���I�V���u��B` ����i��[�e1�W�Rj$r�y#�Q��ޘ�����S,�-��m"籐�
���nд`�rp�*�"�2p �15�r�b )�:PZ�x���6h�ѓ
�?*�Mp�`z��rG��J`��!�� �+#�\<���2"�:.���1;���"b���A��Dͬ ���R���RͰ���NIQ���6�	'�D�]Z���"`�)����Hpx
Ԡ~�O@�S�y��%R� |u��2SA�B�cM`s�@wU���\㑦� ��B��u�@������I$�����Al�T�Hb1��6�[�����az�1����6qQiى�ޝ@ʊ�!
3�<!r�b�l�����;���hЫ`�C`�pP��
p��^a�#BqcI�A�{0�9���b<�xa��"-K���%��@4� �֋q�Ep���Y5�˒Q)�^�uZ��9�a��"�"�2#R�C3���{�*Z$r��{C%Rσ�������4�q�2��jA�"���;!�6>�%�s�@���@E1��,��v H�u�*���7���"qb|�^,�� ��geA�B�s��=f1�sad&�RaP�R�",���5���J� `��!r��?��V`9D�C�'�S��ё��V��{1���0f�`D��p�qL(�_]���şp���#��k3a`����$��!q�C�:W���?��!�³Qlr�����r)�a�2�sDplb�P�˙P&�Sq�r�h��>��ŀ5+��R��s0�ʂ9���̢A��/ZŒ�Pb�qlR��¹pT7��7��r*"���B T� ��+2��eq[,rے����$*�ѐ����!u�`�9��B���-"{�.�R�1��TpxY�ܼA�E8p`[Bqc/�P0���0�>/�~b/�� ��®AI�g!� �@�C���30��0=����l‘a�[o�	f���pQV#��z��p.^ �W�i�;`~��0�b��3;�Ƒ��"��:b�W��Q��y�s0RzS%�C�O�2��~�ܰuٓ@�h@�ɐ�C�QiBQ�ҫ���l���YT1��W�ހ��C�l����"Q[,�1r��"2��r9�sPDS�"{�����"3�R��[��pMu�p�#*�7Б�!h�42��;���5�[�T0׹W���"S��Ea�@��r6b��6�o���%�H�:qe6@b搢㲡��Py�7�D�PG��%�;����p�������as4�g��Rހ��B��-828/@��$��Pd��0X%�@�2.�r�Py���7�̀�y�7,{���Ҡh�4���,Q@W�@��9���b�p@��ZR�9��?:�]G���r��2B��;©��t`�r
�ܳ���_<2L%�'	t�2��M�z>��Yce�Z�1O~�����PFvpآ&q!s���퀑r=d�v��Ƞ�E�֟ⴒ!��*P�NuA�Gt��>,���?"�y��̀٤��Ӥ �2��=��c/$��� h���@2-K�Pk��PZ��p��a�J��pÃ�B��C����QZ��I�z��!�����A����rB�.��"�P��T�B+���p�ԍ/>D
�~��q��EP[";|���=^P�󇡑�	�p%"��
�3R��p��C�SL�uQ������Dr����ׁ
E���5��s���C��2�nb$@fܘp��EB#F�btp�R��v~;T�}>`��+�C]P2!��Ő��C�6�MHE �=G�r)�E��	AZ�@!F�!�V��_z�RH��"A�2a��p�@����N�Oa�b<0f���R�13�z����`��P���1�B��%p"^s���6��q�c�p�#
�[�D��B����r�f�%�G����7�� ���I�6� �����e��$A٢P4�,�2�‚J�a��a#�r!�k�J7K��-���J���ᒔ����Lr��v��s/aꉭ��B���!n��l|]��3�s�Dr o�P��!��!z�a�R�����bM��B~!b�P�5p�ONҔ��*�G!Z2A.1�]NP��NP�8OBW�A�N�`n����4�Y�P�hp�# ��qr��_�iA�Pʲ���4��Pn(A&fTPZ��P�31�P��A�2�����8�0m��aq���Qr�2P�bAqy�RkM�G"�\�R���Ѣ]�����"�P���abʐ����"˰�i���
H�W���8P�葴���u�J"Q�vPK!S�?�p�VT~2Lp1BM�U��@��`z�OPZ�A�d<w@I"�ц�%�T�pl[�a��+SL�֔ �t�p��:�=�;�
��M�U��#ܠ�N��ʂVRڂ�$�p0�W����A�70t�;�uX�F͑+r�x�����#�d5�"p�Y��-!́��G!ro�Z2v<�"�I��WZ�`�Cy�vDP��<���u�>��ø�k������Y��xc�b\[�r���u��C\~2�Ш�S�nwV@Z���g��+�����]�n|]1��^r��%�eF8�v8�d�:$���.��M_B��j�R���{@Af��ղM��mx����"�y������#r"`Ҳ�Q�kqʢ0ж�"��"n���ސ™��Q�i֞���C~����+��Qʂa���U������F�B逢��p�F��"R�~"V��:PB/q[��E
�x���T�`�7��b�r�c򹴉�A�qR{p0,���fQ��ġh���6�'�d�G#ߑ��rP�=Xp��+!\�e�mBi����P�RM@�H;@k��`��/���~�x�ZR.p�R4PZb��mn+��"H�fra���iD@�%b1HH��sM�f�V���@�r^�"���"w��A�f{@�|�^�=�M������!�B�`6�� lRwQ��7��¨`���b�re��2v�8Д��2"�i���tQ�2 h� /� ��K���7�8w��p*�%Bwp��e���t�q�T 9]Z���"��S����6�ސ�cg~���x��$�+Q��x�A��� p�Y���M1�h������@s����0�g~¢�8[!r�b���Rc�AXc���{�!´�I�4��"W�~`��*iB�2���"�1]/>��4 !?�0^��wc���p�"瑢�z��r^pʢ1�~�j�mdoяk�Zbz`��j�<@E�w �V��꓍�wӑw.��.@9�7𠶜@�Sl
�r�Y���$���m҆cՐBs͠S�!�gh�Z�R��>m�����H_��n���H�o�r�p�q�c�0Fs�@@�;�!T�a��S;Q��
 2Ԁb&>q�N��]ްK�����[2����^�����1�nC '"�Y�pr�JS�q"{C�~�.��B€���A��6�����mB� �
�`�!�axcC����C�`l"����"+҆Sz�
��@�ƌq����c��o�X�[ʀ٢r�BJr���Rv�{�uї�+r~�"1�k&A��ц3$`��""ϣ��I��3HBu�!����2�aMqs��`t҆S��D�@�cB"˾Qxb"*�Q��;�u��,�/G��������>p��{�1*\2s;q1��#e ;V��"�j<4��R^�[x���vRcX����c�$������?�["��$�0�FM0ye�ŭ���P�cx��N��g�{�<4a���w�w�^e�0'��pJ��q�M`�Ϙ��Nx�c�òѬ�ж�H Po6p�bD�[�����t`";�Iz9@�2q�Y5���\�=�!�%0#IjAX70��{Pߙ�P�y���
%�U�=�n�y��bGax��Б�ב��aZ�zr#� i,�RL��;���r8АB{��<`�<a�{2}��a�g1��1�j�B0��|B�#�aJ��A���l��C�@��4�6�m�wY��R�ڢ}b��ΡUp0��nW��r���°%��`/P�HBI2�S
��x��Ѩ���� ��k@ܶi��Ur�ݍx��*�P�v�j�~2W�	Q��#~21!�3���8�� �?�����w�C�R�M�<k�0n�� �����IGq���1��O0d\-�B5��"6!�A"�ub�R1��a�6��b6�S&����`��Q(!�>��p�"}b�^%���`n�U����q���8��ӏ�ʒ{0�绀cx���������@�e5��D�� /1�b�ýr�"��S������8�R^�Z�V�ʂ�H6�ʂE���q�r"��pH���C�,���@�R5p ��p~� 
����� �Sa�s��[�Os�A���^jp~��`�r�Y�qO�0���QoߠL�OpJb����M���Дs�R�Sqȣ��8rx�z�j2�^�Z">Z�s`�”a�R���1�X��<Ҭ��2<�3ap�ME�Fь#3 �Q�b(a�o�����s2�5�l�e0NL�Q��P���"�� I6�@��F��ReP���]��i��Nu��Pz��)�\@�,0!fʢA�b�Γ�p�)5�$>��~2�цӤ@C��@Z�0�ß����b1�wl�ᑂ��q.�W�Ǡ�y�`�{�5�ZQ�⧀�r9^��b6�Y+���_a��p����E��#V��TA���`L��K�$@� �mr�����R��6�����P�2uІ�<�������61�vA2Z��c��c����輔Ѥ������ΘFc�
`��M�	{� ����1�`�"Xa|j �b�P�B  
>��&l@�R �t���F�C�@��VP$�]A��b�Cq��/���q�Ê�I�@2��;�� �25�6�8��rD`���c�m��?��곽a�"%�v\�A��{�GU4qJ�;��V��3�^�-�]o%�U��Z�E�,�`�"�����@�N�"1�X�h����2����p~]��sR�]UM��n��Cl!R�p�r���MP[@��:"kl$S��b�����C��ñ/��VІ�`r���"CA�&0v, `Q�1 ;�4��
��.���[@��\���~R5��VK҆�GAl��0g�G�ZBVp~�v��P�7����!�"b���ˎ%��4~�N����p~�uaC3�IcV�m��`n�>p&���M���q"���]B>l��b�F�1l2ıf�;+�oH� l���֬qJ�1alo�R|�`��=�c�ǀ�v`�y<P�B��@��p�$@w\�BӖ>Q٢ean2���6��çP�Q���͠q�p��R�o27z���@S����5�! 7���!"ap$�w@w��p�S��E³@����@PA�2���A[RHf؀볪��/a�R �[bpp���5��5��p�"єS�0}ˍP��9p~B�a�;�[²��C̀���r.d���M�P��gu9p~"�?I{��9����2�`�r��Buq
`J����룓�'RP�Ҝ�R���SHA2�"T�E�e������ן;�A#�q{=09t겒��k�
E�Z�d�q0��0ܓ2q(qЕ�]����a�UBRp�l��
�AIO`��FY�}<x@�0�v�hn������G��+�r������
�a�H,���ހ�e0������0��A���2�t��A�� �G�N� |��B\�ٲr�C�@�l–r�b���P����S7P�2��`<�PRu�
���l��azYBI�mr"aZNCΊ!Sn�`��0�{z <��p��x ��*�(��%_�!jJJ�ΓȐ��6��ɡ���u�S����T���|�e�+�����
~™C2і��ʂ̰M�T�"��6R�҆��m�U0�"U1$�N������2B���?�0�*t��a���\���^q���`��3��S
P����Пp�Vy���uܲA��2��kG0_�A聆�Oq���a3����"$����T @S�p��%r&™�mB������:�0Q�A\��q~B;��GBˢ�����!c�`^|�r|\+a�����GH�G��������$��q
�t��1P�/~�Q�{e��8������"߂�W~7�sM ֖O���H������[�l҄�Is�C�@a$5a�2�2,���+~�N�V���01p�81p�k1p�I�ת4���ᐄ�QB�6@���Я-P%���Z��ц��aqnC��9Za��% ���j �B��R�0mepn{a[2���Aa��`���m�h�qR��[�3��p$c��p���x�!"`������0�z

�P�"Gp���P�¹�I�g����0����:am2u�B��a�S��~�yZ@w�A��e �]%0D0}����pl����[ \���;��MA%T>�~B�pb4`e!;>q�ԑ�S,�i6���G�!3/�Y�����M�p-=�2��~�������)����R��Oa����H��S0n���働a�s� S���
�5�U���j1YJP�-�����I�=�mbv@7�G�`�JE��ДC�m�v�ʂ�a�M T�TP�ҍ@�:�ӆ�	*�xCA�S�b��;�^є�V`�BCQ�0�[\׿���0JZ���� ��>�BrM������`�����z�H�~`q����c��,AIBq�I���[�>�uq����˲�Z"�Q�"�@u���X$[�*�́C��Q��Tq*�ݒ�"���>21=�6��礀�*��`�yᔃ]p��԰:��R|�[�no�P��cqf���rp�y�����vTP��C"!�B��� Я��@�S�q����
.bf1�L-`�C��{�f��ABlµ��bIPhy���2� ����ߤ��3u�3��p�Ss�����c�@[�D��C6�ŒX��R��%=�3�&q~�q�병Ѵ��$���b,vri��X��`������jb8@��G�X��RJ�ꃒ@n�r��a��	��
�@�|�p�"�:u�&P���~2dp�#7�B���f ^�G���z�����"��`���Pc��{�����"��sq�⦒�30 ���B�C�زV���
���̀�
L�1�$p�Cp�-��C���m��B�9G�s�PC�P{{b����S�� $���{Q�bpТ�"��j�v}5 o�ɰЏhP0�ʀ~�82��"Q�"�p��E�>WN�����ŀm"���/�#1�!���F���e0o���s���M�G!zsOq�;���3e`
��� `�1u�
H�����Q�(��2@ٓ"0rpt��Ю�0J,b`c�M��3��Ga�_p�r�s�b�yc��Ǜ��繄`�.O\|��r%�	2Z��C��!;J:@�3Wp�QЏz��a>�c�1��
`~C��q������P���˲����s����S'���s1������„qR�2�)��+�Z��p��ߌ��;��q�?���Pr��R��.��pcArS<��B�%�PI�b_��mR�b�b/�{�a�f�ϖ����Z�5Cs �R^���Ҿp��д�6Qq��Q�Ak^���q��e0L1��6!���r�Д�A�L,Q l��p�21��2�`nBސ�4���bE(� ֥ܰ$R��[R��bm1w��"M�pz��~rv`�&x��V)p0@�ʐ2��2�
�)��T+�Er&16-1�c��S�Y� �Fa�w��rm(Q�p�.��b�@Lsa��@D�e �� l�ޠ׹a�r�oqn~2a�#]p�Bs�dd���"��/����!�������}.���UB��DAq��$��1�G��̸~Q��u�`��,�P�,���;!X$  L�oA
�E�˯�,�{��@�D:��`C�7�A��T`�s���¿���[�!S�a|�1pƍR���@�HQ`��C��RE�
��̐����A%"xp�_��~��`x��@Ny�0�J��vq�ֹB�T�H2r��D8l�_�P��  Ӽ;Q��`�ˑVC"Q6һ҉������Gpr�AN.�a��1{0�q3�;R�g�rb**G ��R�R� l2�;��2���#����Zݣa�r�Pi�`~B+�S�4�<㘑s�"l"��4ۈP־"J���L6�2�	��rǑ~42U\a�����sSa�� �g&�p��pq��4��������mr���юe�q��ZR]p��W�3�p냩�6mr��yP�Ş�P剩������⏈a���`���gm�k\����Pa��aR�lb�@}C�$�AV�H�mvb��0�]��?q�r�m@a���M�4���>�-��+ll����cj`�s?!|�a������Y�ZVp�"l@��g��1P�_��s�;�M��z�Ї��lrpPT3��EA?��2YQ��G�w<�*K^�Q���b���Rv��C���;Pl�ʐ�t�!+�`�9P�b��s�/v`V��#�лb��]3!%Gp�<���)�&[��16��Q�17�V���!IP�%@]fab��6�2�q�a�2���2eP��%W�[ϞPycq�b"��<�&���/�~B����� ��;0�Ģo���r���:����PlrNax�i��#˰��3�Ţ�Cy!W���d�`��1�bR@O����
p���r$bp�LQl‹q�2M^xT�-0R
�@�m G�$�A��)�mR=�������i/�@�p���ib�C�PES,СSǒ}� ��Qp�¢����w���= w&��R����pΠ(�0~�,�;�tPy��!E�Ca��P�1��`~�$"]`x��h@���]�����ДC�q��!]z����i|o�H9Tp���s뫁���]��6R��~B�BBnɒ�I��Z�Jr���PZl��Oma~"��rZRMC`*t�@b��;%g�m�Na~b�q{^p��7`�r� �r���e�&��xA�Kl?Д�7��9P������m�$b,w�Ӣ$���8��"�dPR�`~�V1Z"n�!MP%�~¿0,��ZM ��]�~Bʒ�2a"H`~�e�`;�sA�R1���B�޹`��,�w�\Xu�Y���#�Ѳ�=�Eu���A+�S6�r˒~�6Q62[Q̢���aa�X�
R�$?�����2TP�Zc��B���bBqy�r!�cFa�s(����@/$0d[�RJ����P�u���R��μ���)hrp�2zѳA�q�S�P����~�ςW@p�3�p��Ђ}!(1�Z�`},I�A�,0�^�e��1a��$�+�
�;��^q�2�~��@B$�p~�1����\g�1�a0��h20D�62 �ª�V�h�w6��0���ƑM �=iR��ĐmM�|��r��q�2`�YB��p�R+�F��q��w`}�Lq`���9��G�d��L��~��0��1�q;�\��0<��Ae<�=�.�ob����Mg~ lJ��U0�kr��
p"�1�%���p
Ep~��"mMP���
/���rxb�1L!�P���r�m����u�fc��ŽP�R?AhK���hDQ��{p���tQ��
�c����
��`s����2À�Ʋqr�ւj<GP�!2y�=/�C�a�q�#�ԑ�x�R���j;�s^ZB�6ZrZ�G�{#��q25@H��q��ꠠ��~��p����ܐ��1&~<�w"!2��e��Ρp��``��>0ى? �C��F,�V�pr�<дRON_������I�ϐs{��]p���~��2�}-v��lR`\�$���[��3 e�*��s�`�i#R,6�n;`^��s�]p�F �r�`�p%�
�U��3Ј%�q��� �Šw�6A߶�%2@�����U�0��� ��7 �"> B�@ɢꠠb �@0!EN� �� "�2�
�p�=PV�v@ 2����3@�٢�D���b1F_6a��Sa�{�g���B�I.pOy��w��W-�1Ts�a��u!�J@�M�`�l���à'�pA��YPE4�i��B�pyc`�{A�룀P�;a���t���U@�R{�VB�1�A�������Q��. L��u�'�;��N�Qv�1@�f�uRb�RRb�zRb�IRb�R�p��v��A��"��� �qϣ�"LC�0����C�8.4Q�0?�7P��;/ݔ�{�t26{��V�jE�|�62I<�I�*@�����L�L���#�s�Ǒ�;�Z")24����S+���^P��� �ڒ�]�Q�r�і�7 �ұRF�؂$����D�]��"v<�aR^�~B=��9݂��"2�R'�NH��K2TA��B���q{�T]r��\�v|�}�"s��⇓��$B;1|��l���q���%�$QB�@�2����Np��M�7ؠސW�C s�[�]r��6���uPrQ��6`�1�K{B�Za~rR
�����<��0!�7��b��D��b@D��01��P���A%�9Bu����!�grr��R;�뉂�aOnro٢p���Pb,q���lSQC��PC/��H�QH����wO0^��0��p���4�T|�a~r1�j�p��{{��b� {��qSQ�5��Z�ؒ�q�CQj��`h(�r�҅7��`U�#K�ѴB �wb ׆e0�2�a6x`3% ����#��~1�@��0瑂�_Qp$B��i���i�݃�pڂu�Gi�p���@�"������f�a~B�p��"��Q�V�A��o16�v�bQh"s{���2.Kq$����S�S���_T�r��2V���V�'τ0�r��e��Hc�w�=#��Z���֚@��r&O��b̜qu�� ��A�Ap���"��q���~�n�2��O�a���{��/a_tHr�A�@�~�qr|��ӹ`��^�����3����@2(4��#{���Rs>{���� ��~ҕ���=0L�G�w��q1�H�:O�j�<$���)b0=����)r&���1`��p0�x0Ir.1Wn�`}P�j��O�!�sw��#�˞�@��`�C�@s�#���g��B��~��0�u��H�]�~~�>WAI"i��$�����~�\p�Yڐ~�0�>���4��h�E�����8�H2���e��A��ay�}B�����SG����pl��b\̞�"*Q����o<v Q�Pvy�RP|��n�߁����ʢ7�I���e ]E�H2���Dop�>I2��jЭ����%0#����`��a�;`�Ga��K��a�p�uA$�c/!��/�*2R����2�r���ۤ��+�f�E`�2��S�@�Rx�'���T�m|p���g�s��Hr����{ ?��A���2sbR�B*�ÑJ�r������c!%���#p�36�!��u�m���c�
�ac���mG�;b�1n��p��݁��T��3
���C���ea�3�wA��t���+H�mb�p~b�>L�p�>���h����`#O}���(K=���0�9բ��r}Llr=f�!���Qѩ��T��m�*��dJbA�p�Tq C]P������.("�a`~{`�e�,����9�!č6psӌ�$f2xoJ�-���։0�r su!��12�q c;��P5@x�;�`�P
���١�"u��񁵎��B�PB��\�B�r@1X>R�7`~2�"�s@��P�]�r� �Py���}�+1oE���@���p�┐�l(��jq�"� ���ȣ��>�)��‹�z0%��M�,�~������߸���cs�a2A{�!Ղ���#u!G	���3���RG�T,
�g�E0�"� ���a�v&�%������w�DP�5PI��l�r�mZ�~B��B~��;�!2�!�"n!�h���L��5�͑~��0����J�22��!N���`�b��*=��b�����2U
�1b�+�|KO��� �4�2_R6�/Q��5P� "� L��R�TL�#�9PA��@C���Ҥ!ң��mVqW�Q|C�Ҕ#��ZrEp~��p�R��?2�P�JP���ՉMPgH,e�‘�GayC�H2JE���3��7� ��\\Yr~R�'&6`��*�_���i�l�� ��Z���mR� C=�@���@%2����������f)pwl|�Z��W���pEd@qA�p���~�1 �LDp���2������`(���A�~r�0�BC�ҹwP���@�b��✐ �K!�R���~,S�P�ƀ�K�`��e0�I�@�Ҡ�Kݑ�>�[2 ��I.��`�
{��a�bnp~�p ���@T�ؒS!E��e���ƿ�Z���B��’q�R�C���~���be���Ta���0h#���Sj�؂�`�s(�Q� ��3��G=��"a�R���`-T_���q�Q�݉@SH�0�c�цS����˒����󡁖�A]�6@c\�B���ꀘ~���. P.T�M�]���(�\�]�|j�"�U�@�b	ҿ�~�U���u��v<M��� �R�цàP���pA����ڲ���~��-/��5k P�}6�m�r[�J���MPh^���Ⲕ{~1�9w6p�7�a�r̠��6���@�G�Q����m�2��cp�S�m�;�.���'!�����n���Ӑ���pX#�@~���3o��jq�b�@}����r�;�S�`�]`��%�Dp���,`7&����1X'���!=�R��apb��1pv�a�*`*€�@����cސ���gy>��M�˜40�7��.N�H�[���P����q��&M�<e��8O�P\���.)�9��� +r~Ҕ�]X�R/�mB��=yp#ޓ��� �����i�"O����&u��z��s�Qn�T�Hr�0Œ`�v��B+BDtao|����pr�Qp��`��6{p-;�ilב���pR� '����,�[Ip��f��B�p�铁&��Vp�#�����H�ϐ2�p-{���1�����l����Sa���x�7�r��G����А�2�7@�Ray/�o
� �B�q��e�l�_��Q�bK񇲀0�r5��4����@W��mR4�x���eC�=/@�z	C�1,=��Ca1g�	@����Sk�cqwQ�����	�}t��S~2�Ӣ0��C~2Pp��@���5t��H�E@�tBQ����~S���! %wp{��~Z~l��TN��P[��x�m2��G��b�e"�<P/�68qv�u��F����q��S&�V���D �S�A��ܐI�{�P|�	㣑�A���E@H�j��������0S�����Q�2� ��4?��0g�C�R<_����H��R!b��7`�ӧP��pp�I��&��ύ�����M��H<��dP%P��1���p29��y��9��^��SC���`��� 0?�4є�h2FN�n��A:2�1V�1m�PH���0�&D� qR��2°�%����G����i�)è Zr��CH 0����~���G��'+!�Ԑԫ!��<�����?v�0��Q�����A���alq�T���X�Ӊ��Y)��e ��a"CRr�o��eۖ�ZA �}�EP���º7YZ�{�\���ê���$_(��>�)��&f27�u.�i"e�?Cd�u���`\����AP������ 0D�ІcR���A��~u�3P����s�!��� �3�-�T��C���?���r�P�r�p2�0w��RG� ���Zr��"G��3����]op0v0�o={�ԙ���B]qW��#yq�rj;��˿�����[�ʲ� �C��e1��G�@�S�al��@Q)̢?_�"��� �b�eE�xh�`�l�@1���y�Q��@�A��HB���q��-`�>��^��r�1�1���v;qN�1W���2Saw,�@��ѽ�z�����/!_!�Q>j��b���M�c�!�R{�)�EP�&�@��g��7�m�71ml�!�bQp[�4q��+�wx{������ >�1"3qJR���m"��S�b��u:BN�����p���п�M��o`],� �p)�%�p�+��ѩo!}�a�C�Z���,D��7 a��@y����p����Ix;�Z�x�b���}�Iuq�ã��� ��A;0�*{ A���j"��r�[R2�bj�F�pȣt��Ҧ���t~2d��.�yVꀪ�p���A�3S1�j��2$_sq�o�r���Z"����C�2_�q�RVP�#��lc��~�@�ڲ�b��CZP��M�c��Z�<1�lBH�M���eP*��m�<@��:��†j�<�jqV��0+0^8C�2�b ������ y�`����ҞA��IP��r���==!PS�APLoL����0�B�p�DcC��P��`a�6@�cD@w�@g+� �����p
Ts�~����6N_:�E��Ģ��#}C��3a������{�7R��؊\RI����`p]
ipC!�2�`Բ�����OG0��ma~�LP��$p�s!<�q�����Z ���І�Q�o�X�B��C��p�RH �pV窐�TCS둶�����2B���!��p�#
����Z��P���ʒ+P�@��Q���K����Rn1�IP��9�FPؒgr�#��S��~�ձ�Z����Z`����C	�rJ2wp��� �ㅁ�"@P̹p~R�gE�
��~�CZB)RrC�<@�� �C��OL���IP�w�p���.�@��5P�������pS��`��a�
J��RC�᧐s��� G�6p,�ܒ�D��%R̐�Pu�,�`�N�7W$�4��0a�EP�rw0��P��
u��rxє~�I�04R�ˠ��M�x3� �A+&TZR�qJ����"w��s �|�E�67�Z��e#��╢��(I@�to�crs��p[
�P��H����q[�1��T���Bސ��M�	��z���#��ц��`�#݊�=p�> �"��2��1�(q�W@�b��sM�Ų���R�`B���Bt�uw ����k��kA�2�s�T1_SÐ�X�sA���`���p�c`x�.Йߑ�R��"@��2B�Rz��70���p&��&�P�"�$����p"��]z.��Д��@2�@��Z���#uq훷���0Cg��9s!-�����ѿ!�r��"D��D�p��m�q���Q���q��Vp�=PD��!��zq���I��u�A��vl_��V�1��0[�*@_��$	S�¢��A�r�a�-�FEp�*J�fs�Q�����"t�� ׀!�)� ��Z@�v��%D��Cc۲z�i�޼�q����m" ��=>p�+���@0^������^����PorP����/K�[ӁQ�v���<@My:�j�gL���i�[\�q��q�bN�W*���D`-�{���ӌP�3�`7"��_���!��0�?0�;S�Q@M����bw�q%Biru\3 Y.��~�u����њ]e�C'��4Y��{P�-���� p!p�[�l�ɱ��ϑʒ�A����8/��E~|�Q���Q��dБ�uP���~���-�����W�d���|��zm�~R
s��9 �y+#�f������P�3怹V��r��]{�P�s~��ё����^B‘ҁ��J��-�����)(q�o�}6�#,�~"�`[u��2��s�`�R�� ׼�ІS��3�Zc���i��(��!�S:���`ʂ/1m�p���$��&�~�@ؒ���b.O}�ٲG�����Q�t~R#!_P�%��@m�r���Š�l2�p�Q�b-�m�S�l!Q�sAB�>r�����0�X^��y���]�_�~,`���P$���CF��3pJ�5�HS����c�����1���CS��B;a1�ܺ��B!I��c�#��"/�61�@W����b�����0j��!"�@�7�Q�B�!*�E�C-���⍀CA@�AP0� w1�`��60!|1�C
�@a�P�s�@�y���2rd�d���������"`�t�AP��Zr��E�M��H��-��?0�/-0��;;�/���ŀ-�PL���B�R��
�mBD��#AZ=�0�a�RR_�C���z�e`��ŀ��+�*�.�(p���� ��k���rCQZBPP�S`�C6���ᔣ�P�	����%b�A�Z��TSS��`�������u�`6�����3�ZB�b��%��P��~�ŀ���T�)e;ѫq� ����G��Yg�҇�s�쨯��{@WR?@����2n�$`�29`i�aѫ�*6��s� T<	��R3bi���T���(�S ���ѣ�w����w�[_�O�B+@:5��
Pnb��A�����@�قCѦ��q #G�Z��Л}lI�7@�7b�B��_�`�6����0S���ZR��W��MPr;��C+�0xLy�ç1�[�����Tp�9��u��~t]a�q!R}�#Fa��0љ!�p�2�Q�2"!�:M��7�q�uQ�K���S���]�Zr�a�,��U}����oT1_#G��C�p���X��p������a~"�|+��"�p�¬@g���RF^��t ������T�5��ulTA~�m����_�Cq�b���>�P<�"h|�0�$kR�A�����s���A~�r�C��҄~��M���2_��pлSׁIB�0�ͻ`ق��6����B��W�dr���c0��Dshp�ty����-�ѭ:���a��+�
74���*Aa<�؂/�NAd��Be��1�p~���PA�ʷ�����#
As���b*�
|�s1�J���`�#����[���R�Qh�Z��óp�B����li`l�V��)M��Ē-=�x���S*`�6��N4�{�d|��PE1��A�q��D%�����N�l2�
`�RE0F�{P1T �؂M _{���r*��t�Q&h�ز���sD1�r�0�ƙ��52P"���p����#0��6��߂��@{��@�D`7ZD`פn�[�Z}��i��r����p0�h��Rl0
�;���wicC�Ԑi�2�3�K�6�&Y�b��ڠ1�L�aH������H�����O��qe��0��N�1���ɫ1`[G~�ġT�I���2�՚"�JZ��1`�1`�+1`�;�[, �L(�@���0�%]����$��R����� Gp�@"&��!\\Y�/��DP���3(8���7">vb|�R�p~�`�>+70�:9p�Mܐ��
ļMA��T1'�E�쥙Q���7p&rc�ѐ��7���4�W i�
`�ax�� �Sb�4 uev��q!؆WpBwpؒ��}V������bjqF�Ia����cm���Ba~�v�%�����'%
�Ru1��P����ۻ1`��S듀��AsI��B}#a�"����s�p�����D
�dl�8pα5��!-G��ax2�i;���0G�3A�-"����A�/A㿹`��e@��p����!��xA��ٰ�B-�;2���HjK���1���,`�c�1e��:'{���]P��v��hC�2pXr�B���$A��3�
��^���+Ѵ�$�C���-�4��[B�0��3`��qp�W�p6Rj �b2��O�C��?�2Gz5�U�Rr �EFI�{"o��@���~� ���aB40�@u1�߂��vSc�QD�7���`"` &�4!�� �&t�����~�Z ��p)<�0����ma~��a��Q����~B���{1�b2�puȹ�ZR5��,�p��؂]a�"X��ق
‘r��Zr�u>�!t:A�$���GѨ��� �A}4q6��ȼ4�O�A���ڂ�@��O��Pd�T@$|�{��2Tby�8��A���b�A3�1B�T����Z�h��n�p8\+Bܸ=��g��{�,2��_p�l;�pF$ j��qRP�c3���q 3C�b+1ѫ?agao2� q���Z2�Ѽ�����s`���=�q��F�3���� �ZR�0�>
@Cn��6,ᔣ�0��e���q +"�@r�wP����tܺ��iMpՖE���qAv��@�Z�E0"��i`2�0�O!㥗p��r"��E��5r�@Q�B�R@�#�"d0
s�1��#�l��D\]��R��\��0�b@}S���%�A��G0��p�d�p�R�1���;���0���ո�`?";`���|v��~�4`u��q$�B�B��@�(���(���f]r�9�٢G�~�G �B�pU=PPvePB��3�`=��q�r/�+�e�3��0Ds�A�����Ы� �E��y��H�q��A_Uц�{���Gp`{�f܂����&�"!�j� �*q�G��at�|v��1}�/!���!n4(��C0�� "-� ��n2
�#�`�+�s���h`�����5�{l;��M�]a��b�����������Y��h���r��-����#6骞��
�u��b�1�TA�2Vp�\RƊ 2tp' ��P�28��ۦR����3%#ْ�0�����S՚x=:p14&s��� آ"b��$0)�
�%��D4�]��ؑ[��a~� ��M"��AH���]���٠�y��9.w�~X`ڲZ��8�E�Ջ����a�C�+��6* b	wQv/�a�wQVY��Q�����L M �p0\���b1��"1�f�@��rE St����x����"���2p�r��Hb{݌$Щ�s�]ϐsxV��� #uʀ�s5 e�ar*�S+���G0Q���q{���x�$R*��CpKZ�'�g�f3.b�'��g�1?%�7���	��������B���������(ӐT@g�j���6Q�r{��rqJ��!���0�S@�4@�#̠oE�H� �
j?��������M�9����(��"�QZBoP�cZj1�p��S0�P�
h
��£�2� �Аrz1�����`�®RAS"���P���@]uA��E�)3sx*c����@���� ���s�Q�+S`8��R�`I�UцC��r���60ҏP�r��ү� � ���2�g��6֡�@eb�2�s��ys,Sw��pآ��
;1Q�-ӂ�H"h��"F��6�8��P�R�@��G@N����*�`�g�p���`2p�U���G�����V���d�A!#vѥ�.� �/��B�R��B°p�PI��`H� 6}B��㐢�lv·ޤ�@CX�U����pt���@�٢��FԐ�r�b���.�@W"%�ب�A�0vCÐ�BS�B#4��-����plC��s��[|q����@�j�e3ѐRE᣷M�y���E�ibZ��BS����T��rl�9����-`��t[R;�P1�����#�0�O"!�bCa"i���/3e ���c(�aPH�l �q�C��g<wp��8�����ق���2=���Д���z��R$�`lp\7�������b+�+o�2���MH���VPZR]�`���bG�����b Q�Y�%�ނD�'hC����.��
 �����P�l�}�jЂ����׀�R�̸h��Bp�0�C�Zr+_Lr�Ά��Z����-������@�z�A6Ue���V����3��K�V�K��2�]&�P�-!���Q�Ҽ�s�rPKp�$"�`{�2�+P�2B7�pآ��$�`�$3�������`�!VCճ �s9��R<�f�p�\��(��23o0Ź�A���� ‘b����|��~�r�2�@1We ��ˠ��TP6b0�I�-`�aZr"�*p���d���`Lb���0��Bpv/a�~��2A�Z�;Qš(�v�����)82Q�.@�gbʢs�=Z�����"3ns;�s���2?��C1�u������S�A�������2�b9��¥pd�ep�CJ+P3SI_ ��r� �q��{�8���u���v��*�_/1i�0H�p��an2��;1��p�>�&�0@,
�}p K�1��n���Zґ��M�PѪp�s��3�vla
4�Bֲ�`�``+����0 @�.$����곽�؂����������S2�}
�1��a�b6��*��Q��*C���A��A�mr�!�"Q�4r�2-�rk\y���5s��"߻!�6�훧������pQ�n�2]��Z�����з�����{���`�=+��.B�3Ҁ��pl>��ؒb@M d��a�*pu�f A��q��� ��N@���ٲ��r�da��6���G���M��~"7ÿwe0����l��1l8�+Ѩ��2U�0�N�rf�A����H"3���A����pnB��Iru��B�`��9���Bfa��RR� �93�h2o���Y�P�D�����#E<�A��!��o q�q�.$�A���`��P����ã�q7p�G!�M���V�p$_6Bl��X1�u�1mVj�‚=��9��"� �j��"D��s� T�Cz��A�Aؒ�q౱�:s����l9��M�9���vsa`,3�@���R�BT��;!Q%;��c,�i˛1As�{���Щ]�07b '�PN`�a9���;��$BZ�=����������z�@I;�T�a C߰�dM���u��� �7pl�a��p ��F?Z��s7�ꓝ�d��p�<��$��2uQO��P�YK^��TNM��@��b��̠@[b��{���.4�K֑��i`lb
�������M �o���R=n�n����0��lbw9V �eDa�z�0u\�}� ����Rޣ!���о����`}�n���
��\���0m=��B�� �+��张��r�a���:�9�c���A�qʢ=��r= ���0��P Ǿ>c���АP�V8@"���r�آ��Z��`�����p�s��Q<*�F��®2*�(A���@�o`d�@�����
n�2�������"?�s�P��634��Rf��"r�RB]��B�@�r�P�@�a���.`вހjL�q�sj v�)�s�w�D@�4��"^ ��^ ��6P���b��vQs�U�2�HBS��R�q��b��s���ˢx��Cߒ��s��6�3_�:V�߲��Ű���P��b~�e@q3���7�p�E@�W�~"o����(O�`�#�[ׁ��@�����R���N7����~b#�<��b��r% <!��"=@c�{����'����"z�I��І``�M@�z���"��ҲQ�4��a�r<��QPrN������Z�@��a3~| ��@�Α�r� ��;��y%Y'�H�E��B�1
(�B�pr����ᐭ1{>Q�2��v�!��qbi�{�'�%��+o���������(��G��B��*"��cM���� o�@�����Ձ�)AS4P�`����Nb��!ԇ������> p��B�y���PbO�v�a����(A��6P�<��w�U����}x��r���Bap�Q�r�q��``��E (I"���a��A
���b��?G@��{��ңJ�QHz��.��{�Ѱ�!o�@���"d�q�6���3R�R0p�Yh�M	�@��!�!�У�`�
�T���r�xOʂt��RH�a�`��`�p���3`CaZ�W��`�4!C̠{ ���a`�,��i�2�"Hr��ݺ�@���Y��ٹ9��s���I�1��rRZ�SP*�X���ꡇ���eR����p�� ac�ў_����A�ₐj{Mp�b{�]$ �1�����s(drc<e0ō��lrm@���z��N���E��A��"������ R0|��ʒ�����ž����Bc�0����OB�Bw�|�Wq<�40
�jA+�A� @�4N!\���IE��{1�3{��@�-@�9,��Rp���1���آC�v)p�t �B��C3��C#�Z'M��.{��je AX�q�s4���A�C�A�3�`��(���Q��Xцâ��_b�b/�M���p�� l�7��s�P��P� #D���`��XA��>p��Ecb�-�g�) �?t �R��G�t҆b��-�1Q��Z��}�R�r��}��Rѐ5���h��θ�=�p��,���j�Z�(���x��C����w����”�]/��[\v2��M��8j�c���Q����K�r�Q%�E�w�uE�QY��ZbuZPD`��]@��*Фj2ˉ��֢9H�^2�buq�CF�ӹ�R�@�2l�S�*Q��,A���
A���.�G���0P֤L�[?`�gbW�q��Q�1�2�+��=P�Gc�1`Ys;;�TA�B��G� `�6p�Bj!#���?��a�@�r����c����)1݋(�R"��p��<��,��\���R���q�$���`��NA���B�8���*�����" DJ{��N�P�޽�""�PuQ�He�#�.���H�����ddR���`���ҏ
�࿓�B�¿B'0��f����pز%�g̠� ���-�+B�B�qAM�1�+�P�����jAF���d�<����!�tb07beQ����׹ՠw�*�3���B�����6B2��,��47P��!7�
��7(P�r���Ӈ��)�3�P2 �4�U�~`�C�Pb|50IJa��IӆX�p���p��q2�1%p�p��퐟5o���p0��Ѷ(FRr�p�s����^Q�
��ަ���s(ѐ�C�����D�<�A21`�Ym�� I�Б�a��A2�20BO�S�P�2B�^b���Z�Qb<a�V� ����%j\PRg!�_�<�jp�{pW�$P�bp�=�Pl�O ����~"�Pl�2���G��|J�p!��>�#Dq�C:�<�S��1�Q�+svܒ�fs S�@�|�V�5�Oހ�R�!U�]�,�i ���2��H���=�`Z�1#:�����b�aD�^" �r�
��pyC �@K�P"��2�cBA�S$H����P��.ހ�4���Q2��ZZ�����apl�,�pm�q �Tq�B�������"��"�#O�Rs���B�d�bĐ�@"�}:P"d�ё�	"�����#�p�� w�i2��Q�(p���Ks �3��! �� ��/1��e��Рv?�B��؂^p���Zt�g����Ѻ	c��ӥ�_zA�IS�ؒ3q�"ѐ��@�znp%�2PiD/�&31�B�b=`;�`�*�2�G�HQ�w<	=�A`2�eQr C��ْMr�Sa�v‡�p0�E�GR�B�
���sy1H�^pI"+ч�ِ�SD�nP0b4��@�LO��3ra"�l9�ҫ1�MQL��S�2�5�@P�R1ǡ�0H��I��+$��� ��� �%�YW!�M�Pز�QV�Xцs���r��R{�"��7�2���l!9J�`�qa�}�\[r�ʒ`�b!����4QZR�!�p�I���&���cN`�Bjb60;�0V	���.݁�Bp sW�y4n�R�p!���S��Ut�bLӐBl`_90�[�q�"f1����S�Zʑ ��P�#�$����@�!�3A]_=�3�Pt��r2Pj_]���p�R&aq<]� r@آ$���M���p�B�q��U+`���)�^��B�P��¬~��l"M���Q�RT�H2rزJ��vj/�0�����]ІcT����:~p��qU�a��M�:�0$)6����q��c�N�DAˢ���¹��N�Ǵ!��G�����;A;��0T�)���!��"�˂�p��:�I�0]/xa��2p�jmR؂�!aK�@�]�A�v��F1���S���I�0O4-`r{x4��>����3�ZR]𳄠!;¢��x��îasL	b�rs��5r�r:@cW0�p�3�1���l��d�!�F!,,�19�C>���Bu1�(k���rA"�ؒ&��?��+����آ*���p�Mp�� _�"a!qG0)���~���1jP���7������b�ct��"�L�p"<�|�Ԑ�~� ^`��$���,��.†RZ`���s���r�2S �H����Tp��!2�A��X�K�>�H�a����p�#�p��ҡ�X��b� ��D�����R���[a^�*����CO�g�!<=�p�eoQ��3�rr0w<�p��� U�e@h��#D��9�U@�5��@�bʂ��wQ��B0]_R��*�~2���P�������Ց��)b\��|w�0�fc{�e	�!E���/!|6���![V�M�!L�� ��
�~�t�2Q#�R���bB��nV�:"��z����6 -����D��Ba&�p0[�[�Rn`���`��w{b�nɰl�Y��Xe C�Y2:�saM�f��T�F�TᔠV�ћm��apĭ��b�0�b����D�|��`{�ĵ�P�	���a��;�]��C�q�!q�����;�~6T�m��J��Bw��ް��q S*��_�Rdp ��
 �p�~�Jp �ɂ�"��~R_���p #*!�˕�ON�q��K��7=S�P��OТS,��2�����{�q����"C���� g���	�r�po��ң�d�>$�Ց�!T��a���ݱ��z�1 �0��q7���w`�7D�?O]�R��p@M�I�%�@�!�O�G�;�7��8�;�MS��٢�/s2(�����-�C"�Q�IR���[B܀�q��!�kK$���hP����C�A��h���Y񇃰w�QQ��Q���M���A��t%���.p�c�1B�`��c`�s0�2��RA2 q��p.��r�HbS�"���M�(��r��%�G�b5�����Ir�Bq̡A��g�2�a�r�i�w��PZ�C#1؂.Po�SSZrM�6
�~9�Z��V>62����"��t��Z"l���:����2_�� b� ߋ50���n��#b2 �ѡ����;�[�
�A��=�@~����8C����y_�@[+`����[B�pzY2~շ��6/!|`̐}*�M`��#7���:��Sq��{ x`�$�;��@���P��1{�� -�P�����ْ� g�Ȑ{3nq�b�`�HF���n�("����"�p�vR�3@`<o`H3��{��,��?��B�T���  ��1򈣒����
���.�_c�ѐ�s�F�Bᐢ6�ZaqRܑ�6vPV���T�l"4A~����5A�@t<MR�Cs��7� �W��B9�����������ӟO��2���"���.�Bc��B�Іs领�9��2
��U�R�$�hБVcb��b��ؒ��빐��"a��a���"�yE��2�A���1�B�"e0}��і���[�,)b�נ	@nR���=��W��+09bdb-�h@�+1�3W��"z�s���4@2t�x��@�zg�"s�J�;!}-��P��Q ��q�.��C���ް�Qq�w(�����%�[�r�@��D���C�SR��Be0@�̐}a��R{��c 
�XCv�"�H���bR�G����	�һ _8�a�'}q��Ա����X�
�"�RFq��p� ��BjpC������p��Ā�b���,��3�+�R���1r�@�d���K4"=�ѭ�@B��D�M��3e@Q� hs�U~AB''�ܡza�8��b���	L��1�q 7�ݡ��T���a�'�?�����@����#���RH@��Y��6 I��@�M��p��� l0`�&���B���"�2��Z��aC��@ �6 T~�@�A�٢1�<���1������u�Z#[�7�Rn*�竑�(�����=��j�| �x+��?����_=,0h4r`t7%������Վ��"UP�A���0�aQ��(S�
�@�*�@q�v��7�p�P�/	;M0�0ز�q�c֡
u��G[�7rj�FO֑BS�(v%�Ry�kī�X�`�lsŁ�2�A{����~RK!���@Fh���?�'�a$�%P���;0}AG@����2v�-��pݐj@���6�%iF #��3�8���Ly/�8N ��A�Et��x�E@j��Ǯ"s�P$���Q4���91��jP�s@�����>����D�U����S�C��ep �x��~�Q69�Fގ�B̰]M`��ѹ���`?0 �m�P�� �AVH5 �05�����1��P�N{���
���wP�)�pyc���\��⼐8�����1`��PZ�$q��j�[r����c~��B��j�PluA��;�n6�A
�(������%`��R�G�C!"�4�PX�����2{��T1�]�XÝ!����2^��B�c�ph�{p�.����!9M���R��1�� Q9�p[����u�s���p����_T�$}��1��S\p�Z��!�~̐C
E������r�N!�37��C_��Û�r���b�Ő����ð���3j��{��l���j`K�g�Z���`�lr��L�&ࣜՠ��TQ���|��Ž�ɢM�c!��"@�~��A�t��B�|�UCR �b��1��%�ْM��u�Q�R�bM�2�����qB���7���G8"!M���q$��Ch`~"��`dr��;AR�O`��3���!��v.'s!MX�ѭ�G��� �l��Ӑ��ʠ�FCaP�#G����cDPZ�9�`�qb��` p�
M�V̀Í�p�c��]�c0�j�q��;`I�Z"A�SZ���@�Ѱ���� �b1V�׀��1y3e�G�Mc��U�+N�}H��r*X{@�?�`�l�� ��s��~B���P�e�@���&q$u�Ģ��Ё�[�"��s�Kr�a�6 0�Ԣg@P�a�6� 懚`?��@U��a*A�Q�*1u�le�O0�G�aSQ�x�A����B��WܳV���E���P��(B��j�v~@��B�H�LRZ�a���L�q��`��{`vNÐ�3(Q�r� �����ô��2������3QZ"� ��!���4lt�BEPS���Un���8��2V�s��ز�P��{��;��n�Pʢ��M�)���Ci�20�ҁ�$�ڒM�1u�����Yb��R��r~!ҹ	b�"�PZ�P؂S��r"+��~��Ҁ�MҐ�t��� ���% J,WRn�R�Y���0��ŝ�B�%��m>��b�1S;T���\q��p���H�� ]���T��!��h0�c_���r����A���q�#W�����N�"���Ar����@S�Q���QZ�9P�q�lq���p%KԐ�r�r�Z�+���/��HU�5���`@U<��� R�I������{�~���&QlBU@5���
b�x���N��r�"l���"����Sp$���r���Q���B�����o
;q)�C��N�qʲ	1H2`cV���]dH2�b�zq�����ҥ&;1�	̠����@IѴ�1;A�(�����W��r���Ep�s��"a�*+�]��G#�Ѩ홀[<����[q�
�?A[���t-$�R�����F.�А�8���1L�]@���s5<0�B60B60,}j��p���� wNp�p �]�p�C�pAOT��v���3�`;�.&1 �_��Ȓ���c� i�0l��q�$s ��P�	��� ��Z L����́�n�a�#c3e���3�Ag^@�b7�<n"q؂���ׂ�SY���Z��G��"p"�VPZ2R���o�D�4��d�e�B�ሻx���ؒ��B�PZ�-������~2�0Ԣc��{�P�������@9���P��h����O�H�AИk<���u�9D	`sS�Ҹ��\&�������0�xД� i�Q�5��w�AaC꡹�fSJN��K"I���$�����B�D����ѐB��bq���PZ�P��V�~2qA2���%������$���mQ��3�~rp��zP�w��QUӏ�s��@��!l2݁��5q��a��`�rN���wl��`��h��4g���+0�e�7QA�C��{R���~"CA�pOc�PA�5PŘ1�l�e�
�w�۰EHV�v�����PX�ePN�`` �ە��"^P)~*��2S/X��bͲ����'�b�2C�]X~!-1T�i�Q���Б��S�P�Ҫ���nߘ�6-%�P�2��B7�ҍqzp�#/�g��yQ��a�ý7���*�����$�2����?��C1��Jp�s��3��$�|���y���i�9.�a�#t��pp=����P�Rcxµ.r4�m�����6�~�$�l2�>p�b�`�ɾp��=`�:P��cം�P�{ >
��R�Qڂ��R��S@pJ��� ���`�2d�?�������`5��6��Pp�������2��sSg��T��y�or-a2�p��j��|.��UR,�N��QԆp�`�뒏1Ҭ@%���/q'/"�m���7�R�f�ˠ��� �y�iuql�
j{7��AD�3���(�ۘ8pBJ�
�t�r�0��?�Z�h`���X'p�Zpʂ�n�Д#huQ�1�b�V��B7r��1�PV�0آ�0j�{`�sq��]P����u��G��A��23�����M`TW׀�
�2sRt�0֢�a,`�3��.
�f���v�1�dᔓ�P'��Pʲ{0na6����@�cІ�E�t l��pu��ss�=!�Ah�Nё�10��r��b���۸�\���svr�†�BÒ͙����8p��� ����;A�Z�C1Ñc@�cpb���Z�����"Ѵ.���r$!$Ka��= ��2ta�!7f��N�x�$��0er��u���Z���s�� �ӟ��2���֛�=u��B��b����Z�����F�ز+0������86��(S~�!�ʑD�����*Q�i�fj�M�r��@Yo$p�0!k?4 �N���4P�����p��M��=�{�
�I7�?�(����2�v`��l⾱b%�U�����G �gG��r���p��d���RaC�Б!��������kӦ�`Q�2�`x��B��/�v<Q����<�@�duQ�rd�Z�q�H#a��e0�/�����Zr70���~B0 ��p��Ʊ��݊��ҷ���|��"���C�r��E��a4�ll�QC��J�$o�U�gr�!`������1Q6va�="�6����Bs�ݡ��a�َLA;����}��B�@W��lRxnr;@r�@��A��|���ʀ볈��K�ԣ��C���3EC���=��A�R<u}����@�{jA�'\3u�P���~"7@ǂ^���9�W2!���a�C��K'�DS���%�X'pA���B&��z��BSlv�H�Pit�1��, G�+2�@�e!5BA�lB����
�l�[�> ?����ʐ��w�!S��#p�_��B9�@���|3����0��J�P�VeaZ�Qe<�5θL�m�C$��aІ��P7�`�:r������bo�Ng�a����#���R��e � p�[�
",^�.`*�a��P�r����YҐR6 9(_�%"�?3Pw-�c
�R���B
 ��D�ZB]plb���c��>����]R6bA)g�|~P����2�!����=��B���`~"D�������//�r��ހ�b���ŰNA�5@l�����x�n3q~w��T7݁k<oCU�A�l�(�ULG�B-�r�T@7�D�s�� =>�[h <, L2�!%�QC���K����`���->/��q�8���ʲ�g�Bb�����e��̈́�BԐ�)�p��F�%���u�ZmX��w��J%��,� �����c����O��b�pY����B�C���� M�;��qdq�QP�"���!�5Ma��ea~4�r�R�bt+��xCK@��"A�I1C�{X� �R���9 �%I�z`Cl���!�< ������yI�Pm�b������%�e�I�Is�Y� .p9 ��N��C6p�16p�KX�b@��yA
s�������"�p�r�Т��O30ZGpӇ��1g�*�Zo ���}L�K<4��!�9R0���&Z�QlbN��+z�I-��V6�k��}�*�މŰ�p0:F��$����ctQl�{qʒ|@@�8����S��j���qLc�Pw�҉2S@ࣥ0j���R���q��MxE0��7��4ql"����b,�Rl�.`�"$�c7�7�;1�xtQ��`�I�Д�*@(�s�W��a�Vh���ް��0ũ��)��P=Ÿ�)fin�~�!�%R �N���T#��قD��GP����;q��Tq�"�`x
5�x�ӑ��y�ĝ ���va؃�@E��꾑�S.Қ7�s�� ��!g��=���~���p� `�
��"R���!WG(��Au��Psn��me��m?@t�0`�>?B����߀����7 ��GC�A����
��%�t��Ӿ���F�ʢG0��e��o� �!,�1v� BmO��72�aP�P�t���*q��aR��c�
A��3������`J"E���j��0��[�>�3%�35�#���p��b�W���\ �d�"�V�n�a�r�bb0E�r/o��S���%0�\� !y�~b�@��Ppz��"�s��(���UPЁ�ْ3`~"Mp������#���Iڀ�T{������uLpp�r��{��B%�R;�G�g�0�+p�:E`
u!�0?ڐ~��J�r�*�A�3G�����"x��"��q�.� ��`����n���	Cq�^q��7�b4%; �#�l���28��?���^��/�ٜ��
�]�� ��Š����q~bM����P�re0�5�΃�a4�����B��~�T�2/�Z��ycVPZ���w,�Z2*�$�A���$BD@b�Hr�R7`��pb�3�~��P%bQ�R�`x2	s~�����՟M���9�g|a`�2��˧bR�A����_yMP�e;��r�`�}��~r �#����H�հ�)t`��zS3�
 r�bEp~"����6�!�s�:R(B�7208ΈC�>єӆ@ľ���j�Ah���E����2���Ӕc��j��B+��Қk?q!cxџ�M����D`��cѵ����]�Z"9pK�t�~2%��RPrr�5-p��hp��`�?�����Ѵ� 2���"I`�?�Ѕ�0�2�
W�P��Pr.�qB��0>�Q�5@"��0�C�ʢ�1ǡ4Nq<!�E�GqĐ�20!a
�~�6pܯ����R���Ҿr����I�!"�Y{�:�̐I��`o3s�U�V�I�M/���3ss���r�p₰ep1�6��Hv��B/Q;40�=�	hŁ�#�A��qd2_B6r�0�ʞ�����o�(����Z�_�p�7�E�.I��s/q{b��;����հ�w�qIM�p�r��"z�pI"��8L�� ��ő�x�$R0��#� -ѝ��;QR3�qbu��_�Ѓ�q��.�6]`L!
���r"O�Ei��L�� ��%��N t�A�M�1�`&��pY� W
j��g���e�F\7�����S�qʲW���l�/q�gG���.Ӕ��`S��qb"��J2	P6r���]׾�Z2+#�"�;7 ��]�멷 ��E@s!����Q6�(��r��O ��i����?���3��U�q��|b)\�
JS�ْ!�l2$'�P�"`!a�@�%\�ax��s�p|bV�
rƸ6б™��(��� �����j����n�jp;�H�؂=
��Pn�q�C,�5�Ba��;�.��PZ�4�����˞&��S��`a���y?�c�����F��ݑ��q�Ql�D���|������N�ZByPF���<0V��6�  ?� �b�������A�;�t`����b< ��{@�pT�@�7p�r5���p`Z�P�2ɖQ���s�A�RQ mB9 ��.�(���&�b
u��|R냨0�6@��p(E�p�%�2��E�̈́��P�2Àl��@W���F�sGB�ꓦa�OыR�q�ǡq��"�X2��3���o ��;�A��%�f��pⴢ.c�ҹ~�o�n���V1��& ����2pP�[<w�+q�Z�!�C���sr�Z��S=P��E0GAIR�+M��a����2�`��d@�Y�t�B⹅���M𘆳���{��3.s?P�"@PǘN2F��0��lї����0g|�3o�0��xp�C�U�
��c���p���3qr;Q�bڐ%���~�߰�7��!�ל�Zb�@s[�����BCu�e�R����[r�=�kY����H"��/{"x*�l�����C+�,9�B�� ��Sa�
r"��Z2�bÐ�3�0�r%"�^u�3�����z�0�� ���N�:�ũU��Rh��b�#�".E��[j`i�@�bn�~�5SP�;�㜉����ZR"�Z�s�O@�@H�1�4�K��V��rq�Y��s��ªB{��E���Jr��b�3q��I��7v#��.0���a<�Uui�;P� �z�q��Qgaz�$B ��R�a"6���l��?�����l�,Á6�v�RV�m2��f�>Z�`~|R��u��͓"}�� ��G��^:>,���x��io1�;[`�a��a��M@��!�=���36i���*�(b�L�{�����lr�q��r�B��!��5��/1=i7�� f; �����B�P�7���adR�p��h���(+��L�b�r�!I0p 9��t���cQ10��2�#�����)��p�4���=�7��r�a��]@2A��L5P�b���yNp�}qpVs+q2-*0����w�B���!�j1÷E��C*�7���s]�+o1
���#�0��ur[�H�$��"��lW`�B����wc���p�s�'�>PQ��!���?&�n��p�� p� p���q����s�\��X�p~A�H������ð�q#-h	#.m"��#{P�T1�k��6��xp�{1�k ]�x#�?���:BS�;�z!�a�<jA��7�br��NaôG�f�w��W�rػ�q8x*�~��pySn�~a2��OQr;҆s�АbBa����x�9�Ҥй{�����pϓ[d�1�0s���mrQ�Z�7�~�E�h�P�tPxCp�c�`�~QZbR�R{��u�n
jQ���[�A�mb�`q	ʁ�C^0�
s��,PZ���e�7��c�Ҍ�����X��Ңp�R��X��#����W�~""��2V��S��&���;z)�$��rn0�hPz��iaF�:�W`���r^`����!�_�1F�sAVse2G_�[rײK~0�"�?�re<Pm����^P)�N��BV@��2"��m3�U�ІS�!� ��~2s�ڈg3�@�"v�lrZ�;a��p��6��"���Lu�+�;r�G����:s��E�%4�m��Ћ������ԸJ0Rq��S�AxT�0U�rA#�p��A�w�K]�F.iR��(q��9p$���uL��E`�pS�4 ��ys�r�Dt8B
h +2z�Jdo���v���γO+����P4R:a�R���`�Í���*�ep0t�W��C� F�a�r��A�/x`$2�	������H0m�{��A�B�r9�����G{M�M�B���&�,��`7��p�"dR��q�c�q�R�q��q����R53�� �q�r�`����^�2p�b]І�T�6`}ܿ�$"APxU5Vb2�9�����`�ZB	B��!�+p�2A����Iu���0Q {#�-e!{�U��!�I�q�n���@�ˍ���+eբ*P%�_q�c�ц����ca% �7l,q~���3^⑲swo�2DA�U :�����|�rW��Rq���p���Qi?q�o����D�janb
P��J�,��B�}A�aܳx`~
�1,1s��`�]�Zq����s1ۑTq�b\P��|��X�Z������H�HpyJp�rb� [}Æ�;��������fRa��ː�+�q~"g �p���7|��-�0
��m�[�#s�qڢA6[�Ci`lR6@[�Gp�9�l2]���Q@�B )�"	\�ݭ��u�~��Rsa�#�`x=v��'<�~G�76� d�������b��Se���p<��d��T`k[Xц�fьV�mB3Q�
%���TB��,pQnq��A�Ca~r�m�IB��G��P��GP�?��ÀU�qG�����𣓎nR *�(�[2�p��~�󓫏0ɒ,��	`��A2��0�vm����l��1G�9p?�pp�hCW�i�Fˢ<�	I9�3(C�s��s6���Ѵ�qJ
,ql�"B-�h���r�6�B�zb$�	1���&��A�r29�ꓪ�td�q��RA��ma�3p�,!ES��,q��N�1�RT �*���Bzqʲy���Q�Rr����0���A��1𾧍��K9p;��¸ �p����QAgr~Ґq�
A�ig4.0�q�g�PZ�z�f�s�f\6p~�a	󝡐r��r/�j�A��{�� �r̀Vri�C��?5��yߑR��~����eM�~21��Ű�����W���Ӡ��@���E"�%%!�d-`�R����P0�N^�|��q��:G��K3��,0�(Q�%�a�
p��a��10F$U���q�bװ.p^P)�`��Ba��9�l��RPQ��V0^*��M�a$�ma~�r��w 8���M����P:3�h�]�Gɡ��x���?�[�О's1Q7�`���vm� JRxU�����`8���|n�m򭒙ġ䚔�\��U- mae�F�e}�7P�����7�^a��A�`��0��aa�G0�xpZ���UP2�Z��3����&эE���O���L�^3� <,��c+`�u�
Y�}5@I"#��:�=p@�S1T(M��R���cOp����B�Â�څ�`q�x#�^�@@����� +�ua����?���ٲ��H��B�#�B_n5*��M �ɘP�/@]� �ߥ�����8/DB�Q��B�D�P�JB�G`ʂ2b�iP@7>���x��?p��jЩ���
�u��z��r��)&v�,-gBR��07P�6��k4�#�NY�¢0$"��2\K"��Q&�,��5b�����2à�qKrs7@AѾ��Q�b�`���;��2|��~b�"I#~���$r��s �c�ik���M^a$�����Б��7�ra`�b�����#5*�;g��pl�`2�"�AG3��n�"�����@7�д2��	�c\S���s+0	��C��_�F-�\�D�uL\�ZҴRu���b����������@!u�K"�+�BCG0���`���`ҧ1�\"A� �3/��e@CQ�`7z�p���@� ���Σ{��)Tp}���2?��s}�|���34�~"�A�r v,=�Z�H�D�q	s)�.~��Źd���4c��ê]p�� �´r�sb�w<��y��"��v7�Sn��m.
�rF��p�#E�Z� ��Cl�!{���8�o��Ѓ(f���eu��E`-�D�~{P�@����eP7� Ӧ
 ]����qxt�mb�Q�
D��R@�r�Q�Ri�Ң��M ���@�t$2�9�����;qs�sASf%�Q��K 0&�0�ٜ��lRCq��|���d\��b�֐�N1�l"M@L�+�g.�l"������i�vd�R�RPPd�Q�Be 
d�c��ѝ��r��>�7�@�r��ДC6GX~�A��!Q�b%�VB�N!aJh9���wzA��`�#o��3qdwM ��1��9@tJ`�(�GCaղ"[w`��V��R�q�R6Э5,��9r��w�V���շ�~��P��`�s+ }�<�ٵGp�A��4�m].2�ځ)�6��#����g<����!$o �X��G�K\���"���`�dh��m�آ�#�]���yV;a��p�3~��?Œ�aa��`[��4Rp�;���\Y�Q~�[�=b����p'��`�j~m��PE�PYN�PP��~��#��R"���N,���'�3=Q�R� �bDQ`�,b��සӦi@����?2��3�PxU���B;\���SC!�c�`~Bs�)
�p$B*ZG+"$���70��P��<A�RE�3���b5�l�<�z����8��`x�T`�>M�Iv�s�@�.������ ,1`-(a
�.�N��AIrU���%�1E��o�@��1�fQ1��RTQ�R^�u����`b����+��3���/�~A>p����c��0���pp+ԠM��a�쵠�իp��DѦ"p��q����"[��Gp�czp$B�~1���@i�m��S�� $�b0�����[$[�*8B��pl�T��\�`ip ��a�{	qڂ!����H"��#��T^� �y5q�("���cн<�e�������� ������eЌbP�bm�Sa�b*�~‘@��S�e��х}���;Pn�A2a�A2e��?1�N� J(k����]����c��{P��a�T���tpz#%�,�k�ˁ�eP1k/�# �����rDa�R��3>`"j �q
@�� �7���l�� L�A�rE�6�\���l�?���T�u1n�Mp�)m�C��c�`�"j1�@� �����@���Q��u����F��b6q�wZc���<�P���!"<��pB@�;���%�)4���, $�0!C�a0j�p\� �R9�߉Aq�K@e�q�C��3s:cI�6��D`��V�e�b0�oA��ڱգ�B{PCi���06����mrT��eP��`�n�RsR��{���D0�_�$�D0��v�vHt2_��qB�3�
�3\�AP��Bq�Y��s%�Zb�� Ø��n!��0�[5�Q\Rc)笑��(��3�R$ DR��3Na~��3�p@W�pҜd1p&��m��~�8�Z"/!��{!}��e�vP��7P27p���qY�Rv�PDkl�(Q�2��m����+1�#���*�R�Σ1�o��.@27��Zb.�X�ay#V҆����<�є��"�;Ѽ�%�7�%�~�p[r���*@AS�?B����2HB����l�l3���RQ�S?���+��`�⡁��?��0�Н�7"��D1��p��1НH 	S��?��E/!,B�¶"$Ru���P��t�(���4��61$r;A�4� <k��ْ⁇B���C+��BJ�!� ��l�<�7&��C�1��!�`s��mm6����" D1�70H�1�+g,�˒N���DBWC��
ګ!�1@��;�a@�`��vrM��*�����H"�b�2� ]Bl��� C�����;`.���6@��#9�b�p��P
H^�P�I�J��CY���p��D�N+3u�M������$�������q�R*�;�EP�Aٱ5`����A�# P	��Д�2R��1/�>PQV�����]ː�za��`�9@w����؀C^̐3�Bqt%`��1��0�k2��XM�W\  ����P������͑������4�P�@ذ��q�{ۚ��ʒԁS��r҆�H2�$�2w��"#����sp�DÜ���M�iu�L8. ㇂"&�v�`|�X��� ����MgЂ�j�i��`��-���j�n�ao�e0o�� LӟІ#Q��CP�B;�6��}����h�~b�A���Z�P��z!m�aVgP�2��?��r�����Q�����b���#�p ��aIK,a���:%��>C��B%@�ϧ��� 0p�Cs��py���kѪ62�0Hy�Pa~��b���`���Q�e0Ae��\�6;�wѴ`�9� �J��6�av�A2�"��sx ��4Ѵ"%Pgɂ�vT�Q)��f!vЩ]��zPڒ����
2�Po�6���ā|�)—�7 
��҆#�pc�g�	Ga�s�@��ˑ�]Ц���c���1�:�r�1�b(��Pڂ;�,�6҂�Xa+HB����I�G�x|��FBNx��r{��3,�%r!�)݁mp�
0�Y��$v#yepAe��.@�=�q"W����V:҇&��S�4�7�4�u�S0���Ð�qP���M��ee��/��Cq��;������p܁��Lb�߷���0�O����s��m�!z���AkS��wd� l"S �b�����[�a��r��G���+��ʠ��)T�`�q�s�����5֑�#V���������+1��+��,�X�Q���g�Q��+~����s�f��0�
b�@f�;�vosePK� �L_ �2/S�;�? ���R[�g9Э��Z�j�;1�WC!̨� ܹDaZ"s˲���;!r`ZB�y�fq�`�r\L�Bq���"�����g<[W�%p,�;���d)!�o,� �X4��b p%!�H�'�E�dO3�H�$ �M�Ěa�T�l@�c��X@�B�����٢ڠ��p�sc�IA�G�(�;�A�⹐v�3`C����#��bй�E��#C؂�po����"��%���CB�!�hpS�Yb�Í��Y�gP�2�@�i$a`�@P����?d�0��0|$�����C��I�P�[{��>��J҆5p����V��~ғǺHqB6�k��C����~rJ"%��Q/C�0�`��+�i`�-�цSPI�$��)[Rآ��7p��!؂�P�B�`\R����c��r�c>�C�0�]G`�p"�'��T�pZ[��Iӄ����pCC�p��1�$��s��LF��� �`� ٬�ʒR-l�H�0�
gm�c��m�9A�!l�[���>������m�D�Ѣ21hj����H�-Q1*�1h|3H�P�r���+�?��r��~r
ªiu����bH®��Nq�s�f�&�b�m��q�=���Pud
��re#2v����6�����,@���@���@�b
"�ӂ�&�wR��@Ѥp�G���T�C���2T�c�{b-Dq^�5%"`��q��0�x��֍]��=;��zVP�S�n
s��C�����	e@�'�d�!l�1`�bRP��6�ax�,�̀x�j!��3��Ӱc\�H�Uqo\�@	'�KTAGZ���@��TQr�=���m�+a�BK���L����h�ZB�A�ߛap��-��1*�=�7q-u2�rI��G�p&bD�lbE@�.8p��C߲���*A 3�p�R��W4��
��"b~R�p�J! 9��9/�ІC���
�IS�%B�`V#��꓏�n��q�P i��i`~b����S�L���{cž��,�
(�;Y4a���Zr.ࣉuP�2���E�B�����꓆p�0��6��b(�H"~퐆�^��j��G�u���`�`��{o1�
��3!D^��Sє��*r��sn]p�2Ґ"e���,Pnrv�Ҷ�1�`q��q��� ��']@"T�@Q��� �`V�p����5���Z��u����!"�>")T�`68`��h�L�h@ki�A�1l�a�㾑�r��z�m��/��u��oA�6�R�����R��V�Q��4�~�p�R<�~�"��a`����_9v�s�3"'�%�>$��B�bVw! 6�G`�$��R���b�P3�@��r�2��)�mr����X6���Ҫb���PtG���
r�bu�i�U�Z���{��1��9T�"5PSW ๋C1+�p0	��2���V�lr�A�.]�~�2����5���!�mTql�\G`І��lra@`�QX|�q~"ѧ�����0�)a62Tq��p1TQ�2�a��;`�?���@H�Tp~Bp��t��RCX�;10
[Ai@��c��}��Ԃ!Њ�؋<����KRM@_4�V�;���`�G�b����P\$q��A�V�AfS�Q	��RԡTi��3U�$�7�6l��3�qC�q�BM��$�&�DЦ2��Ƥ`�CV�Σ2{��Di�����D�4PAc�s�c���`���F�ʐ�f��ٹ`�RI���p+k߁m2�hp�R�
@�A���mr�|ߑH�����@���pq�a�s�G6U��vF��ࢷd����@dfn ���[b�0l����g,��@�G�c��;���n�@뿿��b��m����Tq�`R�GM� !�Gc��
cԒް|^��sI�؂�c�1�w�sp �-aZ��P1:�A�B#�z�����r<�r�hp[�`�R`���*�4P*�p@H!��`�5 �Q���$���A�-@��1`�^�J�e@a˗SG���ZR�Q^pl��Q2��`���
�آ�}!��<V��ZB/P20P����S�r�`� �"�P���@s{�A/5�`�On���3�R�`��.w���.�2`�sV�!0�_ᆓAr�R��b?p��,Q�#�A��3R����p�7v�Z �����۩U�s$@`<0rl�r�������K�r��~b�@yRDq�����li*0��Pl���ع<��@A��p��ˠ�=Oaa\
"L+^ l�"��l�q�b �&C�YW�0��G�B�7`�����!����"1�l�,��G���B�z�$92C�ư*B�q�s�p�SGsn�jP��,�ST�v4�B�� ���J���7aP�R����90l%@�7 ��iߛa�p�"��"�����]S��_��m��m�r��p�>H2���]p�r�C�P��>�p�b��"��,a��ðl�Cg�����c�b�RAq"�pS|�P�P����P�l�,��©ʵ�laD���i�~��B��~�CPг_p�k<<p��KBӱk�5p�<"r�Gq"!��N�t�S������s�4L���q~�E�ؒ~��s3�@)3�0:��p��+aγ��s���r�¸�q~���Z���2q�T�۱���z؁�2I`ݫ��~�tp��s�!��c�^��0�gv�"�^`�2�\���]�~�%`d�p�A�z�k���ц�ב��#,�<���zA[�q��R���G��U��b����m2���Ӓ 3!�ц��
Q��LD@��r�{AW�KQ�R�a��=�UL���>$�:u�p���vB�>�A��
�I��@�
b�b��ph�؂~y��	7�q�"�ؒeP>�tz���], 8±1�"���#q�2��1�jV)PR�P��גUyB��=�K����hP�#�cRu��͐H�� V2q�F����vP�",PP��n���ʢ�a�Tp�'0d^�@�s�g�>P�7�p�C�_7o�Q��؂��=`ǐU�~ҹ��NQl�wP�q{c]Cl�p��;���*p����A���@�o�o���~�s��sqa�·Ah+�0����bpP�R`�4rAA���`��X`�s4A#S�����2��x0��j1����@Z�	�2B�h��2BPlrS0~��p �.��&��^^�� 3�Q!p�ʛ�(Z� ��Eb�s61Y�61y����f�z���`�R>0l*X�2�� �Yv�P�d�2v��w��C�P��0l�t|v@��t�M
��+AL.?����p�\����lB���+jp�K.q�����
;�t|�&}�G��0�b�禸����p��`Re�H�q�@q��������n{ ��� �06�ٟD�b£؎��D`|��x��w`�Ԣ1wqRnP��2����c��$�f�p#�Cwz����ߩ{`7*e�
�"qI���E/���~"�ă��M��5NŢ`6� @<�^��{�57�M�,�cᐲ;!�m��b�Q��4a�B��p�k��R��1v��B�?��"��b^��%2U�~bu���0��1p�5Ӡ��3!�BQ�x�O`�:pڂ�P��O�0p��T+/q�|1��iq0�b�l��q~����P�LK�i�["�rZ0�������~�Q�
QHR/!]�n�0}����л�/n,�AV� E��r��1��'��IJ��,t��?��bDq��RQ㸑X�=0�qd_;`�C��HR\ ����a,p���7�f�4�"�/�q1���-��"S��A�,�a�R�@?��Q�jP4�5С�K¨Ţ0�9��*ð�G�#{@�R�T�⑆��`��!j��1�v2�?�~2�P%P���Q�'n@���0�0��t`n�~�H�nߗp�1@�b������ʲ�{�?�0�
�"x�%�L�=pt����a��]s��{�VUP��0ֈ7̼nq�B|Q��ސ%�:�lG�@q��p�m�a ό�%��B7��M4�9�blw�u��vP�2A#���S�� �A9N��6�n ��<���
�5���m�a�/!du�aA��V�]Ɛc�4Pdh `����p��s�m"�~r"aU~���Tp~B������m�����q�e9G���ISe�Ձ�i?�M�q2��|^�-���~��@G�%҆S���p�* �MIœ��{��bxR`E��b��۠m�`�-�0`+�YJ�s�`��PG�D���P�p~�G��yB�A�
 
�C!����AP�z@�2�p>�||����lbu��Ԁl� �!Z�ʒ��6�7p�U@��3`��vB3n���E�3��t@���p2&��@��% �c,q�6 �b��`6"� yD�UҜ�c�І�c��c�љ�{P\�6�wPi1۠��A���~ఎV�d(/QxS�|�a�^��rMp��~r�Pr��m�9Bb�G��# �H"��ß10�+"��$#G��򌩰R�0P�F3�{p�[|�,q��)���b��yx �rI��9�`�_�~W��G1M04:x�ZґI~@�a�l�� e4 ��g :�ځI�?�آ4�c�/��ҟ���l�S�E�04���I���H�_L�R�R����>���'p���>@���t1��@����p��U��rM���lRQ@����i�'["ц#=K(?���ц�.p��;��T0p!��A�u{E�Up��b�%��E��9�7@��c�}j��l� �ʀ�R�bƑ���4!��,�6�~��@�9R%���@ؒ�0�������sbٮ�~�����H�~r��N�<�W��A��mb�QK����pn����}r+Nr�+e���G x���MҐ��A�e�����ْ%09�D���*@�}QPl�����/1֟��)�����FVZ�DmE�|� ��Ó�r%aSH5�x���ԀI/���.a�3w�ؒm��bs���~�����1o
C0�ҵM`��T��r�0�☘=cVҦ��b�S6��z6�sR�QQ��e0����D�q�w<���qf�쐁4��ώ��SP0�B�Qeq��a�m�À/��'e0�l�~p6���V"u��Z��mR؂�H�x�Վ����Z�Dө��c��3�`�}�y�x{��[/�(\ġw�Er3���2���"�pJr+�+�|�ۂ`A��#X�p��eQ����>@��pׄ��?�m�p��r^Qi�4 �ː��uK��
Q ��ց��?���1��1c��4z��슡mbu�"}0�29`��͠_'!Bk
p $�F��@�^�f��V�6�1 n���"��Gc�`�Gr9ojp?`�#@�~"qd20��b�ҞH|�E���b��������@E�� ��r5��E��B��1��~�1�����lgr^=�BM���1@��p�-`�BІ��O����!�w����pb�uq��ݡ��c ᔣE�Il֑��pЙ�@��6Q"@�آP໧;�3�B!e4S!�	7`��a��B{+R�r>P��~rS�A3�>p�mb<0�P��6���ax�,�D0�p��p��o�pJy�kź,�`�3� M4Q�ס�r8��+q�a �]1]2� ��D�r��)u�� c�A�2�)s2@{�3A��i�r�`��.���`�F&��6�1>�b �!
c��*0��1	-�"�(�/�أ!��[���p�rH�Uҋ�)��D�����B,��!�^��y�&�0����.�?2U��LQHrؒ��gCQ�o�Q����,pꂬ0UKS㮚a��#�[R�`&��xS^����H"t��~�1����9޳�I�?�3Dm��0H�A)��~�<{����9wZ�3�Q����~�� �^a�@i<�A�¾a��E�EX�HP��ߡ�K�µP3��@2����k|ް�*���a~�c�	� z�Q�ӝ=ж��s[��Q������1�{��Ŝ3ݫ�p�R��sSp0��������������=JCq~��@{�G��s�1š-@v�>�~b��|p~�Gq��s� �/t��F�mri��9����@�����Y;��P��1�����H���#s��W�lp�P��_�`���Z� �~��s�2(���l"�@�F��uRbم0�&�HbW��܀��y�N�D�CGP-�ڰw�qIBnAOR,`�J{`��s�/n�1�{F.Ї`�򛡆�%0!&����cr.�ӛ����B6�bsbC��~�p`���a�t�Z2oPqb1��)��@.�a�p�Lgh���4BO�٢6P��p���
p�m���ٲ^�r% �:�Д�`�����H�~�Ѹ��s�1tb_�~�ua},����2/��)j�T���@�����q��G�U���b!�U�̲�k!0�5��EN`?]���7��2"�2�`y��[�C�~4q�S���!sB��6�=47���r�����tp#���+r�B��G m��І��p�Ғ�~BW������xBLJ.A�9�E)E��H���Т<�Z��a�l���/�Ζ�!Dn��#qbF����TQNM3��Dn��~�U���k�[���d�r���an6�J�Gqc�17a[��I�u|;8�T[Ba@�ʲ��5�I"p���1��R1���x��a+`�#1��D`���آ��H@�i�Ѓ�Tp���p�C�0�
`�2]�lR��rMp�f%Д�0 ����S�Mp�v��]R8`+��d�G�M��@(�{��V�!��ƒw0?�w0�w0��j0߲&�E�@��V@��*@ǖw~7@o��;`�����������}�0l��k/�R�pq�P�*�`'f�`gI��Ra`����a
caO� Y B�5��s4�~Bs��><��Ҳ��<�ܩ�����Ӥ@6	��"�P��!���"�����ZlB���r���b~�;������Ґ�mce@Mڋ��A�Q�>4����"@���Zsp���p��[�s,�0�e�ڷe �v�9���"�b�Z�PP*!g�����,0lR��d�F�x�좐r%p$R�
tn@�������_@&���@S�p�rC�g{c!�B���x���
��=����b���U�A)�%���Q�)U����ZҒQXq˲��Qb،�H�@�I ���Z��a�Q�G@v;�ѐr;�2�٢���ס�ru��z��m��a�2��
S��juP�r<p"՜�7p�Si`l�9b��czCu�)�!+D\"�Ne��.�1��>2�(�Z|k�r҆#e@CQ�!���!�,�w��!6�p��b��â��a��P< /Zrl����m2(qW
9P�Á��C-���a���'S˜���a�S�QB4�B�Iw�M�=�?�Q��v\|`�nMЬ���Z"����m��0�� �@`P���¹`��+�-�fc�B ^CŰ�<3q����B����"epD��!�u�������	z#��b��̠'��s�P��%��j�mR� ْ"!:{��%P_����S"`!��V@TS�P��?�ҦS��1��4QZ�s1
w{p�z�QŲ��ޡE`mp����������
�~�m�3]��R��R���qJ����/�Cɛ�W�qJ��A��ҏqZ[���Gpx����)t��~Pb��Γ��A�]�`9�NrE���"��~��]`�#�/��P���@�3�A�@����)����L
pC�,ġ1�C�`<���2���*P��4a�^�5�>��5���fi��k"��M�<� ���Q}nB�7fkt�S�g3���bG���uJ#e�~L&��;�o�o6/�P|d0��PZ◢m����b|r��|4�KCa��>ZbFs�B�Bn~R$@��Đ��N��`9p\Ѩ�@g�,!�2�p\!U�nl�ؒ�S�/�m��[V0آe��I������0�^4q*�o�ȲP��Y�l�.��"�!��l�fa��G���y�1�|l"�6�&cHB�c�4�CA�/1�؂U��Ss��cCq�z���!EH�,��Æs"��
�p��`��p��uqʢ���	1ؒ 8n3�"0Ӏ�3�FH؂nj�T�[D���R�w`�p��pa�B�(���w�L�!�0A�J%@
!��lB������ �"���pe��p��p�<U�l����������pIa�«C�T!��O,��I3�~���"A�@��uP���sh|��a�S�1�/��V�Hr�`��$��>�~"�q��y�z�mR�s�&BX�a�h>����r��#j��_;�	�[I�PA{8p���j���M�j,�~�T��S"62�a�zh��b�q�#<����QZ�# �70��M��h@����3an �O�ҕ�z!�� �a��6�hn�b�a�MZ`��xr��f0�G��~��Q��4�e"끈�M�$�2�.���P2����3�k+����Q�V������<z��b��i�����2c0��q�BSaH�
Q�WQ#�&� �3�^C3 [��p)4M ;�@q�=A�Â0Ъ��U��0�������QZ�=����`��=����A�ɐ��c��lP�@R��jž` ��>�c�Q������p$b��� ^"�Ǘ�m���~����nja��",����; ��`x��`i� ��n ��q�f���<^ʲp��SYr�`{����n&;y"<��r�O�ْ=sԒ"��,p@v���eb<�T \��<Q:���Zⷀ����뇰�	�p˪�ѱ��>�����G�������c4�Z�e0��t��Ӯ�P���#�������н.p`�S� 
0!ҹ� �h� a A�>�3o�{@�
�H!��
rʒ�0��>`xr��#���b'q�COp�#9�/~r���~V�m�I�^Ǚ�b�!'�c��C1�R�p�P؟M��@*���!�Iz0�~�@���Z���2FB�PZ{�2$�p���S{�6*=�z����Zb[��3���81�R��L�<��`�3y�np��2]�~�A0��=��"��@�� ���"I+B���x�b�$@��)R,�����[�rR��n��Qʂ
R�"���#,0r���!���R���R��&�nb�zBd"s��"ʠ�KQZ�n�m�\����@��S�I�(��u��5��%��[C�����ւ;p�
���`��]�w9�~��R"p������~b��27P�M��!�"�`���p��3���d��n�Z2D�C���яs{0
=������u�<-b�9��٢��V��MO�Qn�q�ٲ;�A�E���Sᑢ- �#���2M�I�#
=2iv����#)!�dG�Ơ*���~2�u�ucuSXG��2����>p�������dߑ�W5P�rw��� *;Pb{�Zp��P�F��p��^�;��B�y��p1�ߑ���pN��p�cҚy�Ӊ@9��>�C�B��C������I!��""��������!S�}4v�e�TQd��pdb�q��W������7h��%�i0�s�p�r�a�^�@�7��5�Ѐ��W��,��y}��#�¢s��f1��0��҂0S(03.q�ʂ�p�0{�A��2�����~2+�g�q�����';��E ���u�-��푌F��oڑއB;Q;��C���bŰi��e��0���.�P�ӳ�KV�1U.����9Oh��0�`�R����S�b�����#� �\s��Gpb��٢���qI2}rA(2������R%�R,��6�P�<�7���d����`��"$��̰�^��~ү���A�-��3E����7����B�p�rCѢS/�#rx�P�g��NӐ�S��I(�h�����Z�����$0����0Z^����B[���bˠVם��� آL��G��
�ә����1J�S��w�Ɠ�H4�� ��9Pn����b��0��H���{a�2v1��J��wQ�̠��pc�r�rAr[���L�0��Sa�_qB��P�iRv�>q���q���v�H"+a�R�bQ`�“`�!wA&��,�b�C�������d@ZB]@X����B�!xRwq�Lcڒ
�vX����lx�@�}=p������&�u��$z�t�����~� ����Hr�qR*1	�"���"٢M�ǘ%�G
���p�2�2N	^]���RM��ȏqZ�yB�N�1�bO�j�w���Sq�bi�q��� p4 �@o�q�oa��ˆ���,��<��Yl���ҍ�r�;�Z��"�"ْ���̢��C�#&]����xanB��╲~
�r��(qA���DA���q��w��-p�sA�2{�R���7@np��TA S��蒲��b���[a�n6H2�ғMy�3B-��5�S������{�3v��A0=]\���0}rh�sṡԀ����C��a:�"bJ"���
`~"���ь�E�\5�q���"�b/����0ɑ���6;Q&àp~��@�\�>^2e�淣�
`~R�0�7'S�9.f������U�#V��sY�֑�C�@v�a��q�"TQ�2! WMP�Q�@�:bHV�,��"�Zr۰�2;��=B�b��sz�C�����Z�b�5M7@��]����٢
`�b��s|rn�7�  �O����lT��?����Zr�p&b����Z��@���?�㱐���Z�}r9�yPu,8�Z7J���,�pb,L�a~�6���@������HpSZ��cڂ�a
e0�D �r�)��a~B���b��03d�){ �r�➑����Rj����Aw�`
ʛQ��5�<@�Q��4^�j`�S�a��`�"�P�.7;otQ�ٔAD�)@�t����2$Ǖ��;	Sι�j;�p c�@]�����Qa<!���t��D�E�90Z�!=�8�Z2n�Æ��َ`/p2��%o�$�@�70)t/�~b�R��Q��BT��>2d�0��N�~{P1�apx�ScXH0�/{�|0�в�<���@�B���R��s*9�
�~� ����#�P���hn#� �!c������G�r{@�4��H�f1	�Zp0<Qݑ蚙p
�+�r��O�>�R70��D0��0�B����r�ё"M@� $��J��@��g��BT�_Z�w��й��+��R<���Ab!6��rs���s2�4Z2T1�"Ѱgs��
�=�C�X�b���S��S��7��0�;0�U6ZG�~⼡ǐ���/0�p�r������s�B�~Rs!�{z�_-�2��1X�^�{����a��M`�pʂD�s/kG��C�rD�b!�{B���v�3����j���*�+7 �D��w�s���`��<��K�����RW ��r�v�pBIF#����W.��1�=����;�s�6���(A
�c!�ID�>��>�iӽQ��81�����!r�R� ���Aw̑ cV�p��P}���:���h+��pْM0:~�ǒ��D`7�D`��q[d�r]#*�l}2��uUd���Z��hN ��4�j5��bM����"wQ�r݁���Y�p�2C��0�*������FR���$B>��øу�a]�`��C��3���/!��?��҃��b	P�"-��TqxSb����ٲ��j4Ġ!cq�A�s@آ:@�
��C���/�-3a:"Rz�0�Cc���10��v�R���u�!,��p�%��%�9��TR��$�������O�`�b2S�"�2}p�!����Z*1�B�P�B+H�@�Y^N��;�ѱ�@%"*��'�wQ�@d���cp�⡀�3�`3��2���"S�ْ���BІ�)�	����6!�b�ra�1�[��;�1:��`�8`�
RB��$����~���n��[�Ss�`�p�����B�`�����������=R�"/1�
r8.;b�A�~�qr�@���!૑˄H!آŰ��1����7��S�@p|8����@Ѐ�5��U�6����Dh�|!���+LY>��Y����%��bp�gY�@g�+�0��B*O�㴲Pax]���;��
����1�y��s�)��3a��(�R�B3*�A3q��~|Z��C22`�Q�H"{�6A��֖��x6Aֲ��c]�`n"���o��@q��>7OP9nײ
���~�!��b7��B������xS�c����E@L��1ZrYцC��A���yM�%��Eb���a�s%�&����2����f㐢$Pa|�P�Bp��  'bi�="<Az�a��y�HrN҉�~�ޢ��?���"��>4�Q��Q"�@.��`���a�=��"�@]	P202C��R���PI�MP�e@1
p�[g�;�{"�r��]��%A]o��>z��Be@D�3qB��R�0��A�g0�B������P�����z��_p�C�0�^��A���NY!�Sl1h|��Iq2���EP�PQ��V@Hb��Mq�Rc��p��~7@��=pJ� �Cb��0��Bn�Zb0��#c�g����rq���@�B�Q42P�3���p�* ��p�(����0�|�1�&!l>7 �ⶱjP-߷�_W���j��������8�G  ��Q��@��J��B�1�K̒5�/��B��36�:�GAn���CPR���u,,bm�`"ԢK�v��$��r�?��[/q�\����|nf�c|Ap��$`�}1�[a���S.@v��:��<*}BW��p ��s���rސZRpL�a�������A�O���b���ARH�@
�[�(a\O����eP^B��֐;�7�x��@%2N���@��V�\�P�Bobul����*�� �<��𝝷0�(�Z��Q�M�q"sA��hr��pxB��P�z�^�g�Rۗ"q�栌Z;Q҆��}���t逭-s���Αa���2��s>��@���`�c�C�"
�[��Z��p�yހ6Tq ۠X���(X�`�R���t@�S�A�O �2��$��cP�
��p�W��_p��d7 2��xV`ق��B�h���c���R�p����s䛷0f��0HR��І��ay���$$$	-J$!-*BBU-d--$5B$n6
7 B$B-R
B1				76#
-		7
BB!(B- B	BB$

k-nBBB+-	,k7$
R$5;B-SkU%	BdB$?$J7-h	
n	
J
BJ-67	BR$	$7J	"
S$-
$",
U$B#--R5) --?
$7
"
6d	U?dB27B$)#171$)-+
+-#
-
B?
7n$kBSJ

-$B-

*

B
$	
1
 ?B*	$$#-!7)#
B +	B	
n	7((#B 7*#5-
UB+B%$%Sd,$%-
B$R#,	
-51
7-B'6	*4
d
nB	
;B;$S	!B)-
#7%);		$#	-k
-B	
#-
$'B7-#-'(577+6$*B-2-77
(7	-(5kB(hnJJ??	$+7	,*	,
	472
	
	-
6
5$BB

	7#77U
	?(		$
6
$U	-%7!-S#7kR-B-#	-
"!B17?!	#2+$
7,2d#
#
	nBn
!-!7
7

Bh$ Bk
#5
	
!
$
k7--,
J#R	#
h7h-'?7*#
%
B5-%57'
77S62 )
h66#
6
	$-	5		$d626	,"
7$-17
--$,$(

$'5-
$	-#2-;'h-h
-S75
S#2B*-6
-+
	,,
	
52$-
n
,"2

	-;7#5
			
#
		
	
4U7R$h4k5''1- $57;
',
-%
-	
5
7*
+4		
	
-7d6
"
*#	

	
 
	
-
7!)
%5 
-*"*
-!-
	

U5$4

	
h6	?	

,24
	745


1
6#,2$
;-(kU
'---
	,4
--U27-
(5$R		
$2*
	
!
-62
2!
h
62	2-$4,	
-+2!
	
!+-

-
	!,+
,		!
6-
	
	2+2,

	-
	,-
	
-#	
n	
-)52

�)
	6$1*

)
#			SS1*
!
!
5d%"
4

?"-
$;+
			
5
5	
$$
	
	
-
-
		-
+4-

*2d

-
-+4	!	#	
-52
	4
6	


*4
	

�
R,

+
B
R
	#
2"	#	
	
!22		
*+$-
2
52+++

*!-

*2

$+4	#



	
5

!J#
2)
"
	,	
	;
5
-
-

*

4			#
-
	;
	$


+
-
	2-
��
�ZL����P3���	S�J�d�x�RxB4@����r	p��
p"GBxBT
X��0NJ�u$ ���~ �p�JP�R�b�xB�hKR�ʲ��mb��s��[�����b����~�<�xB�9hB�Xz����nB葂H>�
���6���s0m�l�$b0|�X����P�

xB��ǒX�#xn��Q���	U��
�6�H�6h��tr�KZ ��=��7�
�L��d��[X��!�T�ؒ%2�Ւ`M�"���X�lp������ZrXv�ȕ`ط� �E�ʒ�~�#X�2�B� �������P�.���[�xX�$�|���@A� ��0�	x�B%0��xBp>����rXo�%���������;hBD%�3����Hˢ��b&�Q�
��p�5�3X=j0T�8i
�����n �R�V5(=J�!�
P�����%`���+�j���؛���#h�rp�-�2j��B X|D%�
�
h�d�102p&��$B��"��|��B$O8>�X��HXxr�B�(�hBt`�$����mq��h��B���4�}��f OL0
��Bd��(�+��0��
 [��"��@��P��Ȑ���r%`���ֲX>�H����
��r�B�x[�)�#�l��kH[������fc��{�ix� m"%p��о^{� ���R�l2'h�X��9�@B�s	`��(x�rh���(3p���Nl�b# �Bp$���`(&��x  � p$/$`�`����4%P��X�b
�])��4����H��b��"�D��j��h9R
x�b,
0�@�*�r(��#ة�*���`��x��j��2
�BD'� �l�0���B$X�.0=z�)�H��x��B�p� )��u�
x�X��B�x�"���B�$�8a��B
��r`B"3�x�P�B�+8�'��`�R
xqD��J"�=J`��HM
)��b�Z�X��)PbT!8��x��x�
�x�
�ikLJ&�����P�t��}�l20=�xxq�
�}dxx��B�#���)����0=
pޭ
`Z2 � �8a,��H �B
������{�{30м�dD�f���J�I�r~@s0��`V� �������
�u�C$Bt�l"��� ٲ P"p���Ǫ�`�F�N<�2�~"��"���B0�S��
��k-@��pg���RHp��_�h&c�b`��B���r��P��.�R�;�SP�B��4��/8�,��(��X�"�"����+
5���`%H"�
����R0��Ȑb
�?C
���`3�ȉCx�"��� �r
�wD�9�xx�b*�����h^h�S��
����pH;��"���0ؒ����V`)Rx2)��BX�
B�@��؃�`s��.����s�����Y<�T(���	#0��;�.8��@D���,�F���x�r�"X��
��
�2�
0�(��t(��BTx��%x������@V�Љ�(�l�؟D@D����
����DNl #��K%��
.Xnl@F(�.'������@D����X�N؆�px�b��D��� Bt��b���`�x]�
���
x�
��> �4���hk�BP�P1B��u,���D%��H#�@����q�x[�%x��x����������t���
xhd��3 p�~"0v�x��=����4`�$ ��@|\#���1�Q� �2�&0�@Rx�P����PNT@R�@Dt����� Bx���G�X��b�n
x$�Z���O<%�f���"h���\x�����ux$RزZ ������ق�0��C��7��\��
�Bx$��$����{x$r �е�#H&\،�+	�wdhB�(;�l �B�HB�0m}����s��Hl�<�$XB$&-HB(|���i\
i6����B�3&��hB$�Cd�%���
>H��8
xo�'h6b�Ct2�q���������Y���rN~hǾ��xl�ؓ���2	[�x\� � ��Xʢ,@Ǐ ������H���4'h6R )N
�"XB���H��`�������%�	�^�E	���(��. �(���(g��2()@D�P���a���8��
�;.X��0�@0.X�ʲ
��2
pf�
p�#�2U�
�C����������r��~�x�[%���p��
`���2 � �+؆����B�
�B�h2�O�()t�3h��
`�T� -psN ��%@��0�0����p�S[%�=j �ݗ  ��c�k����
xB�Ȃ�`B#�����9xrP2d �P��Xs(���3�{s.0�H�8��ȑ� "Z�S
 q���
�pz�����`�C �<UX�мt%�Yb�ٲH�
�B��e��~���~* ���3�
�g����(t.�x�X�X��p�$���'�6���"!P�dHB*P��0ؒ ���J��sX2X��؟2H��)����Hd�!8�u�>U�0;��X���+��K0`��`��O��S��b�B�x$����s�����sxBH�b�|-�G-x2��(L��b)x�b�BT0�.�rМ��Pd"O�覒�R�r��	8:����p���3�Bx$�h��	6�or)�B�x����"�� 
~��x���C�PLd�dXR�"T��rIb@o�l���.xx4�x�C$x�9�F�xZ=x*"��R�2x�2x�"x�B`� OH�2�y3�z�0l"
��2�Bd
X����=J�m������d���s��B�p��4`N�Skp�����x����1PGB?��c��sH�!�Cp�Kx=�##Ph���2Ȑ��Bh.M
�����y��<x�.0hg����T�T���Bt%P��>�*بT	H�r��.)n�5x�� )`z�(k?`:2��"#[��"�U�0g�0w��r�R`� B�
����b# ��#�g��^$Ȑrx�P�r��gXb�l2h��*�:����+�p>���2���=H��6���2���B�)��� T���%�nrH�Vp��r~0I�ؾ�4خ�ػD��� ��DS�٢��А��rp�
#7O�C
���x��h����H's��"�ʂ�B�(>/�Zr�B�@D�7�iv��RH�h!�"�=:H��q�0���������R�ph�#��B��^�(N��#�$P���@I�8�B8�C%�� �kHp�8�b@D�0�� �pʒ �(>�
8B� �$��<Hl��i�)���2%� ��N�(.�&��� h��pX@D�x�"�}!�"#��$ C�@�
 b�l"�P}HǪ�3hh��g�
Ld`B���2%Bz@D���Ȕ�`�4%X��Px������ S�Kx����X��)���Z�����e b���0���I��� ���B�b@D�@�nx0��r@�3 �l��قXD�)XlB �0.8�8�`�-��
�Tؔ9X���2Xl�% �+��%
p�� Cd.�3���:�� ��r>X��(�>,
Xl�X���R�~��ؒ
N�D�xl"���
��+%8�8`�RP��x�_@Dd
���@v���R`B>�H@D�@��
�����
0������ʂ��D
xBD ����H��
(~�@D�
���:��" ��`��.���*�%B�/�`�d�Usxc@D$x�h6R �:�"���(ћpa���$p��!��a�SP�cA2"�k�tL���� l�E����B@D� ���&�H�Z��O]����S�sb��&(z�H���4�
�s��ْ�Z@D� s� �HD�{p����I9Hl2�t:���)0�Dp�x�N�� �B`l�
�;�� 4H�����
x�2 BT)�B��� قB��l��W������Zb�l��"�%8���ɲ����x1BІ3 B�`��
H��h�� B
膳h�%;P<j'���x�R��~Pd	%`�r
�x�j%8N�l��l�)x��P��`z8��x�304T
2�8���".`zS(x~"���08����
��ؔc���8$�
���+
�txn����%Ѓ
�����:��؆�8g(�R�?� ��h��K���.�����B�
p{�����S�E5��� ��
�HL.��#���&���-a�C�ػ�)HBX8s`zS��l 8#q�<�N�"�ޢ���B���PG��
�C$�1p*%��l���T�Bd)HB��1��n���o��S�k(�{
�|K��3ؔ�'h�X��#А�X 3
�W��C��C�B$���4"#�"`�.�B4
�'��ϳ��B�#h��!���w�hB4!����0�%��(X�(p�rș��  �T=H��hB�(p�"ج-�� x��5`��%0�
���P��0n(>������jA��!���*.�6bp�S^�P���KSh���%�AJH@�%��P�r.��B�`�D�V. �B�D�XnkB$����=:@����B(������Pd�x��H��)hB�X�
xB�h����r�n�j�xs�XF$	X��
�'��l2$�xB��r�s@�c'`�� B����"
8 pxȺRЭ������kTX�'�bP��p'�����%���dx��P��#h�� �����.����� �mp'8�i�4���ȵ� p��C�����Pc*`B
���( �^hƽ0�T���@��o@=�p��p&��B4��"�l���S��
(�D�+�W,
��D���x����� �#`O� C4#�#xC3>ZX���2x�
X��5sh�x\�8P2�B$萢�h��#��2.N�
���8P��C�8�x�P�c���.2x��^�=���h��x��� s�$`�s
���B��B�%�f�B$!�#�b���rG�ʢ�B4p���p�������#(� �P@�M���y`���G��B�<���
���x[��/���3��� p~�B�����	����5�����/%h���S�(
��p�����������
P�?.��(�葫%����r��T�CD.hV�&��>�=��Ct�����$2@��-�	���(8�����
(��'�����hP�(�@S@�
8>��BDؑ���2���`_�% u�#�����
�m��2hS�"��%��� | l2pr�lRhBx�=h_Hho�m��I
�;��6�px�X�d�i����(�.أd��c$pS��{D���P����`L�`T��i���z0�\
��R
H��Yd`�������b�r��r
��'�B��c���
X����.��jx�R�����`��B���h����.����
��(,h�����!8���VJ�BD ��
B��Us
`N�@D���� l�xS�@D����@D$�>�V�Y�d��@D4
�rx��B��Q~#�C�9�m����z8�)�B����C�o2@D������@S��"*��!h#� 
C
B��L�
8���p4PeQ���@D#0����(c�@D9�C�`ld�B�(�HP����@�k �U@�s��‚��4x��B$��.@�`~�`n�s�ןx{pb���*`t�@D�`�T��.@D�XlR	�(�PA�p�[��l%`��xa�@�CT
�gc	�2�8��Xl�B�@��nR��n$ B���~��/ B� �m"�$��	��$��*��� B$ص���L
`xd�*��@DD
 
� B48� Sn�m���r���( Bt��	�&�����C5�q�`��
�� B��Ct��c�P`�P�
��hծ�Z�x�`
�&S#H;���^(
)@����"0B��r0B$&h�����٢�l"�?��� BT�����#X9�#����L�U ��Hڒ���X��Bd�~�H˲�4���`7�۹�c)�Wd��!0C� LC�-Gȯs
������@$ؼs0�(�qP��x��(���2� 8�h��h��!�\����4�K x�PFf�EB*��r���j��B���B��@��?t,X����/��#���

8
@%T��;
�<Z��R�+.XB�
��s��o +�*H�#'XB4*H_#���rC��l`�#(�4
�
�
���q�&�>"`ܶ�mb��h�J����XB����p��h6���S@�k%x���g��Z���c�#3XB����(a��ob ��h6���$l�Ȇ�%BT��k6�I�`��`�D
@<:�c�ld*����4@G��*	�d���hBT(��h #P2�0��~�]
�>��y#�lX�T.@:_bl��Rx��Y��P>
�L��Z����*pS����ߒ���x��P��������lr���0#
�~h-�H���.xB�����)P�R�l����*^x�
8�xB��C4 ���|�lB#8���;�Y��T�4�d\P��<�������}T@�� ��(��CD	���hB�/D
�Ғ����~BX���#�Ql�C���&�@�"X�C��x�b�!�p��p&�hVs'������f�ئ��G
�F$��%X�r���9��o�.���0�T��"
P�xR�0�4
��
�� lb
h����S���������B4X��������Γ��;�ئ���X`�!hB����% ��Hx���Џ�O�
�R�)�[R �<���P}T Ic���x����s��&xal�Z���)�Z#��Pln
`������x}��BT���Bt`�M�Ԯ��
���H��$�3Hq��l����C�P���<���"��&����"�E�X�N@DT86|'@DD���@Dt���b�?�,�x�$��r@D�������2P��B� Tr(<Љ�(,#hB� д����زh���S8;��_���B� `���aH��X�rHJ���Ц�X�t
��8H�
諴8��XlrP��C0p�(�k\�m��0�Mx��0gd8�� *B���KC���HH|� �؊<H‚��+@�4�������c�4(<�؊|
���C���������s��!`md:8&�3how���h�
��"h��
���
���&�C$�)����ad�'T�� oT�3���#���,C��*�{
���x�8�*��k8�P0� �Sx7���*�k�X.#�RX���SB�P���P�x~"�{��{�����
�`$0bl�
���h� ��(���FU I� ���]\	(���i|�6R�k%����
c���XZl"�2��S�E`@D�pAc*�V�hSU�_+H��`�$@D��<�H��Z��rD�{���i���CT���p�4���x~���#	�2૒9R@+�h�"���h"+0�����(�}�m�O CD��B�%R
��� 
PJ����B��{���@D$`C�C4#�Z"*P��	�	�U�`���B�Q��T��
Ȫ��T#�W�8��l"`E�7 �)���`u>�!�*�U�x�.��1)R���m�ثdP��C��md	h�[�I��)���� $�h�c�g(��� $�8<��jtxBA�����x�Ah�2��b0��C�P��%��Z�ؑ�8mm�k�S0�r��b
���B� IB$w"�&@>b9�D�@.���*0l�������x���tJ@D�`�$@D�@D4h�t@D$ ��&�`�4+�x��9�t�.��0�()�B����#P���X��4`��؞���sp{4@D���k%@� �(@D�#����
3��h8>�R���\���%(�b#Ș
�g�
0Ts���h���dO��P�
B�
�m� �r�i��S؆�
x�����^8T���Z*0��B�0��%`��PmTP�
Hl�ЗD�������{�����ഏXBD�<�
�H��V��l����xI�������-� �%P�
P~���hB�`�����)���� B��$����pg4�y�)���`����Nؔ��$B�)�@03���
8�.p{����H�H�B�DhBD���ό�C�8�Kh��hBdx��X��%�=�)#�)d1���P�"�2Z��>�Rh�mX�P2 Фha�xl��ͫ�F�،��P��lRx�YX�~h�D�GH�Dh8�(�`�~�<>%��
P������z�m��`������<Z`���k!x��p;��M��?�~#؞s�S*�%b%pG�P���BT���{���z
 ��0�r��*xp�xc ����S ��
Paj _3�%(LU�Bm�j��B���y$��3���%`���S��B��y�y� Wb�C���Zh��Ȕ�`l�m�8�9��� m��TCP�b�C�hH,8j�
@sЉ~��x%�#0AkhxI)h����K%���?�B� ��(/���(��>��+%�*PY1��2�S@D�
ؔ;�N�
8�Q�~�hVwXvy
��[#`�d�K�hS�
؜�P=a _��C�
�9T����DhB�Y�0��h�2�w�p�y����x~"�nz
�J�Jsh��8y�9@D�h��%�i�Ȝd�
��������g���P��
p�
��#*(a�o� ��O�P�3
�4��m�	x�cX�b
3�XҒ���S%���hB@i�)��C0�4"3*�T(ԕX�3X����(KS6�=�x��m����8v0+�
�6
���`W�PB
8;�X�����ؘ�.8�3hB�Ȑ�P��xv�A0�{ȝr ��H���j�*r��o`�&Ћc�Bd#H7���h�Rx�
��#��P��B�Bh؂��ed#xB%`,XG�*p�mp)^(`z��CT���p�����H�B�pl#�|R؆� �$ �2
`��Lj@��(��p���������t~xl�0=:H�BP|C@J����
����CTp� W7@�R(#�,eXgHXg�(�Ct� ��XH2�B�p�bpx��$���H�Bx��!�S��%x����$�H��΂�*B�������@�0=Jh\��B4�B$����Ct��mS�|��
�%P�
%@�u@HY��T��sX�'���`*�l��2��Ql���B`i��2�~����&\���`�N�-�@IL`2/����~H&���0s蕒% iG8IR�[D���=p����!�JD0�j
Zc(m�`���u�9
pԢ�Bd#`��(�y%`9
��>0[Jx�s5���
�+���%8>zhZk-�mR�l�!���y
H���.�h�r�)�~����)
���X��PR
(^Z.�����2�%B P)��B��Er �#p�� V2�[�5f�8d^h��`�< l��R�$	�ź�\�(3a�����*h�4)��s�o�%��Q��?0�Vкl�xR��������"�̓H1�`��x�i��$`�3�j4xH�wNl�!(#Ch�s0!X�I
h� \L�9|h;R��h�����B�Ch3�~�x�R�P��C��R�
���Ȼ*��+Ш�h��S8�88݋x��0��BD(V*���rC $B�b@D�����&X�#p��Xt B�k�ؔ� ��pE��M(I��Γ@D$�� `��@D4 ��
�2#�"H���(*$.`3�#@��B�dr�N@`n�8�7�@D�
�+(�~��CH
@D� ����~�(�����o"�B��C�@s�"��6p��3@D4Xl��(N�/�nXl�@DdXm4@Dt��B00"�m�0	��X���"Hh��I
�qDh�#
8�
(x��Zr(j
���b(�k���#@Dd ݲ�R���X�3%����,`��M7�?_�e�C��0p@��0E:	�f�L��Tr`��0�4@%�`��%�n
uzX{������^"P�kx2��=�
��(�"s�	�d
9P���X2����
��)�:@��1�	�~#%��k �Ȧ�xl�MT`��p�k�y�0/�x�2x)NX�~@�1.�r��ٛ�mB0�(Z-�����HR���pFX�0� ��HBD�� �����Ht$ Y���#h��#���m�`���c�����J
��#PD����B�0�b8<�����I�$�,�P~�hK�
p��8�H��~�0w0�(����)�lR����H�(+/x��x�b){�~2Xl�@�9�В'`tt����Tpg�X���~��S�*@� 00(չ	z��ג�5�P#��%b�ʒh0�%����l���hB(��l�5�@D
@	��g�д�x6B�l� ��Rx��(C�D�3:Pmt8��������ft.�<�-� ��0�B�lr�>E0�&�lb�6�
>�^��hd�����j�x	�sC.��"����Q�x6"���l��`�� � �x��.8/�Fps.0�Bxk3��~XO�s����Pc����1�Ȑ
�����`��~hئ��7�"q~�����T*���(Ilػ��~�0�2@��	`����0�b�ˢ%`f� �
8��H{��$0��X������[
��2�
M��sH{� �3�p�X��ɒ�V2�\x�r���+� X����@%dX��`7/���xؒJ��m�5�s�x�L�)(���{��`Ԣ(�`���Rx�����X��(a�h� ؑ��o/�A�0IM���CD��`l�Bh��x��X�x���c�ZR�׶�g����-�N
�:�	�B4(`��~�$��Bd�բ���N$Ȇ0�S3��%�h~2
X��X�(��x�
x�o�������=���%h�KX��x�RXb`r.C(=�x�bxr0xB7��
x�K�~}Pg����(�����H�hE)`^�`k4��(]�$"h\x���sx��h�;`�d�J���V���D!p�dX��H�辔~���p��
��s�C���q�#�M���2K~�Z
���x�������
 P;�mr�!d�cd������"�������� �{菋�&���T)`��	P� mR�M4hi��k��I" �������s�
�3�"h̫(�p�X�j*�*�0S����*x%�XS�h�U�m&�C�2�=z@ǖ%��� m"��
��plbGX>
�F�
����j�S�
���
x��x̻
Xzn��k*`*�.�k�=sPV��`N@����`�"�����&�BT��зt���� ��S�?�
H�� )�H��Z$@K���@DT����
��Wth*+h�SȂ���`3����8*�y���zX�9��5��2h1�� �
0���#xD�]c �7�
���@D�0�2�:Z�
��B
�����9hzC#���	�8�rȐ����p�� ��(X�Y�^�~v��pb���H�����D@D��()O�O�	 ��X]l	P|$@D$�
��!X� ��O�P$��B՚(�	M�8#�
8T�����~�X�.(h��s�Xl	��(:��@aF`�TW� s9Ȑ@Dt(�#"���hmpF��!h�X���K_ȑ�!��b�Q|P��
P�dx�'H�Q��0�
�~�)؆���2(o�
0�k�"5��C��C:@�k(�"��S�@D�%�L�0Z�8��`�.PO�"Pߒ��#���b��`�`|�
H�����/�
(=�r"`��`B�NX� �4XD����
@�ZB@D4B�@>e`��`��] �@Ddx���Y�0ؒ@D��mn�3 �Rx\�(`��f!!`�?H t3���
�#X��p��`��
��}@�BH�T�~��m"X`��nDȓ�h��(POrxR'@D�bPB�(n踎@���b�T.Y�"��F'`�4$��
�~��HPOtp�N�s5�ZR'`t4X�~X�Ȑ�X}T#@D�H���d(���t�" ��B�
�`�x%�襗! �bx%�"�
��]����m�zT* �&`�T�_}h��Pf���
����I@0�����"
�J�؆s
`��"`�
��;����mR�]@DD.�(��"��+��^. Bp&�<Q(�2�{�P
�
�����z��
�`�0���3�`�S ҹ*h�S h$�pex)
x�C%��  }،D(4�{��޹��2��k��R`�
0��(t"H��r�53�������)`l��)`l��]ȹ��mr8����"@�!�|T�`���`�#�T3`F:мT�f3
�o��!��s8?�V��nj �0B�غ��0�0��hM��[@��H��~B�
'��O�@�d��b`$��(���-�<j�{`��h���~�6Rp�"؆#p]R�r��&�0G�I8�&(=z�C`�
����b
��Ox��P؆c�p��H"0������m�x��X��Pn�H��Ѝ�
�pd
Ў;� �)0���BX�m��)�`[$�@C0� ��(�ؔ� �rx�bاT�~�7���(�r
��(��8t��s�����H��2��^�`ZPD#�^=h	���6=p�� �����m������2H�g��"% ���lTxD���pYx`�t(�5������"h�EhBDXi�؊�)��NH�B`V0l��g��3؏+�ْEvpq&0m�H���վP��(�����p5>��3H�����
�d�6��Bt�"���6��c�X���C
��2h���(>�^@IR`�k��H*\��rx%�H��
������H�����s`�,x��.�X���٭X�7���2%�����> +3(�#���B� ���-�����kC%x��ج�,3���Y��k������4�B�$@��`����3���`��0W��i�
X�b�BJ���$.��������!x�������`hd����Қ��3�b����)0�p�c��v��
�2J��
�I�x�"�ؒ�&cNl"�2J�utx�x�2�6B��B�� *B���ȫ2zD0�x��5`b�0�S$@}9#�BT��X�S���<��x'��I���!��2Z�T��TsHu�~x�
�i;��;`���TF%0���0�\
���#�U�
�e2
x����
@>��nz�ٲ�a\�Cڒ ��l"
�S�C�X�й�P��P���Z�`��X_�`�B��D����x2����n�5�94�Q��s`�. RC
��������B�`�
��O������r`d��� ��B���B����B4��	fd�E�5P~�	8!�9"���٢r�ȑ��")ķx�b��.'�B���8�1����B�H��誒`��%����^
��sh��pYpR��B$�d��p m"��B�X�c�r��h�����"��n Csp�$ ��tRX=�8�=0؂���8`� �B�
���BD`_[ "XPb)�AC`�~�B���� B��1� c%n
�����B`�d�& 8��ǒZr�[d���Z"P������n�"�<��N�x�~x��
�����"
臓. o�X
�%�H�
X�B������
�A# �\;��xH���>x~��m���%�.�&X�B�
Ј�X�rB���28�f@Dtb��2J%P����D������5��3��.�S49��R?(� �B��h,��~ӛ R �2�n�0�h�4����!3 �B���B� 7'@Dd؅��#x��X���64��(�T0����S��H�c)T������P�]�c`h� ^>%�]hx @�*p~@�B�����-�%�p�0B�90Bx��2����0r
0�dK�3J$І�踎0�Z'h]�'0BT �x c��c
0�s�� 
�`�`R�9$ C�xR��6�� #��~��	0Z��B8�d
�PpsJX"����
�����YثD�H\x�1��B�4�N� �r�q3(w�x�hO�#��|h��h�b)HBdpV�r�HB�*��0B����x�R ��H�� �H�
n�
�k�[#�Q�0��Pv�X�0n�	�#�)XHj�-�rN%O�������_W�=��O�X�R`o�8�e��$���XBT���X�bX�R�~PN<��4HB����P�� Ԣ�uQ%H���x��	��	��	�r�.�HB�x �N,���	�s��h&��b�H��.��
HO�0�~�td
 mLH�rH,c'�)�HB�h�"�=j
0�R#`�D�w/ �DX����
X=�!@�
hB��Z�`f�8��o�萒ȫ� �Xl�.�d" �9�N �&�"X͋�`�2.xB��.

2-2U5
	
	
	2-

	
	


	
	
	
	
		
	-
				
	�
			


�
		
U
	
������������ H
 !&'(*-01789:=>?ABCDFGHJNPSTUVWYZ]^_defgpqrsvwyz{�����������������������������������������������������	
"#$%)+,./2356; <@EIKLMOX[`abchijklmnHJNoux��W���^_���gry���������������������������������������������������������������������	

>o~������������������������������������������������������������������������((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((66666666666666((((((((((((((AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA�������������������������������� !"#$%&'()*+,-./0123456789:;<=QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ^^^^^^^^^^^^^^^^^^^^^^^^^^^^																																																																																	((																															
















































































































































?@ABCCCCDEFGHIJKLMMMMMMMMNOPQRSTUVWXYZ[\]^_`abcdefghijklmn))))))))))))))))))))*********************++++++++++++++++++++,,,,,,,,,,,,,,,,,,-----------------------------------------------------...................................77777777777777777777777777777777777777777777777777777777777777777777775555555555555555555555555555MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM=====================================================================================BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((pqrstuvwxyz{|}(((((((((((((((((((((((((((((((((8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666669999999999999999999999999999999999999999999999999999999999����C��CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC((((((""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC�CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC����������������������������������������������CCCCCCCCCCCCCCCC$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRREEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEESSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS::::::::::::::::::::::::::::::::::::::::@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF																									GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC��CCCCC�C����������������(((((((((((((((((((((((�������������������������������������������������������������///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////(IIIIIIIIIIIIIIIIIIIIIIIIIIIIIJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&000000000000000000000000000000;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''1111111111111111111111111111111111111111111111112222222222222222222222222222223333333333333333333333333333333333333333333333333333333VVVVVVVVVVVVVVVVVVVVVV??????????????????????KKKKKKKKKKKKKKKKKKKKKKKKKKaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa``````````````````````````<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<WWWWWWWWWWWWWWWWWWWWWWWWWWWWWOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOXXXXXXXXXXXXXXXXXXXXXXYYYYYYYYYYYYYYYYYYYZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ��������]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]][[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ddddddddddddddddddddddddd_____________________________________________________ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccceeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee��������������>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>�����������������PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP������������SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb����((((((((((((((((((((((���CCCCCCCCCCCCCCCCCCCCCCCCCCC�CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC�CCC�CCCCCCCC���((((((((((((((((((((((((((((((((((((((((((((((((	!$'*-0369<?BEHKNQTWZ]`cfilorux{~�"�m	�U�!%�������34��xe#�VX���!R[	K&�/t�H��"t��T����ABFi�M
$-7B																												






	

	

	

		
	

		
	


		

	







				

		
		

	


	
	


	



				


	

		

																													






	
	
	
	
	
	
		
	

		




		

	







								


	

	


	


		


		






	
	

	
		

																											






	

	

	

		


		
	


		
		







				

	
	

		

	
	



	



		

		
	
	

abkhazianafarafrikaansakanalbanianam-amamharicarabicargentinaarmenianassameseaymaraazerbaijanibanglabashkirbasquebelarusianbengalibiharibislamabosnianbr-brbr-frbretonbulgarianburmesecatalancherokeechichewachinesechinese-tzhTchinesetcorsicancpf-hatcroatianczechdanishdeutschdhivehidutchdzongkhaell-grenglishesperantoestonianeuc-jpeuc-krfaroesefijianfinnishfranfrancaisfrenchfrisianga-esgaliciangandageorgiangermangreekgreenlandicguaranigujaratihaitian_creolehausahawaiianhebrewhindihn-inhungarianicelandicigboindonesianinterlinguainterlingueinuktitutiu,ikinupiakik,iuir-ieirishitalianja-eucjan-jpjapanesejavanesekannadakashmirikazakhkhasikhmerkinyarwandaklingonkoreankurdishkyrgyzlaothianlatinlatvianlimbusitlingalalithuanianluxembourgishmacedonianmalagasymalaymalayalammaltesemanxmaorimarathimauritian_creolemoldavianmomongolianmontenegrinsr-memyanmarnaurundebelenepalino-bokno-bokmaalno-nbno-nono-nynno-nynorsknorwegiannorwegian_nnyanjaoccitanoriyaoromoparsipashtopedipersianpolishpolskapolskiportuguportuguesepunjabiquechuarhaeto_romanceromanianrundirussiansamoansangosanskritscotsscots_gaelicserbianseselwasesothoshift-jisshift-jsshonasi-lksi-sisi-slsindhisinhalesesiswantsit-npslovaksloveniansomalispanishsundanesesuomiswahiliswedishsyriactagalogtajiktamiltatartb-tbtchineseteluguthaitibetantigrinyatongatsongatswanatt-rutur-trturkishturkmenuighurukrainianurduuzbekvendavietnamvietnamesevolapukwelshwolofxhosayiddishyorubazh-classicalzh-cnzh-hanszh-hantzh-hkzh-min-nanzh-sgzh-twzh-yuezhuangzulualam,hyaraarmarzatauazeba,bsbelbigbmbr,ptcatchde,frchnckbcnzh,zhTcroctcymczdandeudivdkdutegengerespestfarfilfrafrega,glgaegd,gagalgbgbkgeogergrhathbhebhkhuniceidsinindinuitaiwjpjpnjvkckgkhkorkrksckzlaolitltumonmxmy,msnbpa,psperphpkpnbpolporptgqcrsrussesi,slslospsrbsrlsrpsveswesyti,botjak,zhTtwiuavavalvievnxhozht���������M����������������������M�����������M�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������,,,-abcdefghijklmnopqrstuvwxyz-abcdefghijklmnopqrstuvwxyzacadaeaganaoawaxbdbfbjbtbwbycccdcfcgciclcmcrcucvdjdmdoecgfhnilimiqirjokpkwlilklslulymamcmdmemmmqmvmzncnfnpnzpepfprpyqaresctdtmtptzusuywfye%s.%d !--font script link img a meta  http-equivcontent-language  namedc.language language  content lang:lang{Unreli %s.%dR,%dB => %s} {Unreli %s.%dR,%dB} {CloseLangPair: %s.%dR,%dB => %s}<br>
*<br>&nbsp;&nbsp;Initial_Languages %s%s(%d%%)  %s(%d%%)  %d bytes 
{Unreli %s %d%% percent too small} {Unreli %s no languages left} <br>lang_tags '%s'<br>
DumpCLDLangPriors %s<br>
CLD2[%d] '%s'<br>
CLD2[%d] '%s'
<br>---text_bytes[%d] Recursive(Squeeze)---<br><br>
%d bytes %s.%dR(%d%%) = %s%c <br><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;%s %d%% &nbsp;&nbsp;---text_bytes[%d] Recursive(Top40/Rep/Short/Words)---<br><br>
&nbsp;&nbsp;---text_bytes[%d] Recursive(Top40/Rep)---<br><br>
AEligAMPAacuteAcircAgraveAlphaAringAtildeAumlBetaCcaronCcedilChiDaggerDeltaETHEacuteEcaronEcircEgraveEpsilonEtaEumlGTGammaIacuteIcircIgraveIotaIumlKappaLTLambdaMuNtildeNuOEligOacuteOcircOgraveOmegaOmicronOslashOtildeOumlPhiPiPrimePsiQUOTRcaronRhoScaronSigmaTHORNTauThetaUacuteUcircUgraveUpsilonUumlXiYacuteYumlZetaaacuteacircacuteaeligagravealefsymalphaampandangaposaringasympatildeaumlbdquobetabrvbarbullcapccaronccedilcedilcentchicircclubscongcopycrarrcupcurrendArrdaggerdarrdegdeltadiamsdivideeacuteecaronecircegraveemdashemptyemspendashenspepsilonequivetaetheumleuroexistfnofforallfrac12frac14frac34fraslgammagegthArrharrheartshellipiacuteicirciexcligraveimageinfinintiotaiquestisiniumlkappalArrlambdalanglaquolarrlceilldquolelfloorlowastlrmlsaquolsquomacrmdashmicromiddotminusmunablanbspndashninotnotinnsubntildenuoacuteocircoeligograveolineomegaomicronoplusordfordmoslashotildeotimesoumlparapartpermilperpphipipivplusmnpoundprimeprodproppsiquotrArrradicrangraquorarrrcaronrceilrdquorealregrfloorrhorlmrsaquorsquosbquoscaronsdotsectshysigmasigmafsimspadessubsubesumsupsup1sup2sup3supeszligtauthere4thetathetasymthinspthorntildetimestradeuArruacuteuarrucircugraveumlupsihupsilonuumlweierpxiyacuteyenyumlzetazwjzwnjENGLISHDANISHDUTCHFINNISHFRENCHGERMANHEBREWITALIANJapaneseKoreanNORWEGIANPOLISHPORTUGUESERUSSIANSPANISHSWEDISHChineseCZECHGREEKICELANDICLATVIANLITHUANIANROMANIANHUNGARIANESTONIANIgnoreUnknownBULGARIANCROATIANSERBIANIRISHGALICIANTAGALOGTURKISHUKRAINIANHINDIMACEDONIANBENGALIINDONESIANLATINMALAYMALAYALAMWELSHNEPALITELUGUALBANIANTAMILBELARUSIANJAVANESEOCCITANURDUBIHARIGUJARATITHAIARABICCATALANESPERANTOBASQUEINTERLINGUAKANNADAPUNJABISCOTS_GAELICSWAHILISLOVENIANMARATHIMALTESEVIETNAMESEFRISIANSLOVAKChineseTFAROESESUNDANESEUZBEKAMHARICAZERBAIJANIGEORGIANTIGRINYAPERSIANBOSNIANSINHALESENORWEGIAN_N8182XHOSAZULUGUARANISESOTHOTURKMENKYRGYZBRETONTWIYIDDISH92SOMALIUIGHURKURDISHMONGOLIANARMENIANLAOTHIANSINDHIRHAETO_ROMANCEAFRIKAANSLUXEMBOURGISHBURMESEKHMERTIBETANDHIVEHICHEROKEESYRIACLIMBUORIYAASSAMESECORSICANINTERLINGUEKAZAKHLINGALA116PASHTOQUECHUASHONATAJIKTATARTONGAYORUBA124125126127MAORIWOLOFABKHAZIANAFARAYMARABASHKIRBISLAMADZONGKHAFIJIANGREENLANDICHAUSAHAITIAN_CREOLEINUPIAKINUKTITUTKASHMIRIKINYARWANDAMALAGASYNAURUOROMORUNDISAMOANSANGOSANSKRITSISWANTTSONGATSWANAVOLAPUKZHUANGKHASISCOTSGANDAMANXMONTENEGRINAKANIGBOMAURITIAN_CREOLEHAWAIIANCEBUANOEWEGAHMONGKRIOLOZILUBA_LULUALUO_KENYA_AND_TANZANIANEWARINYANJAOSSETIANPAMPANGAPEDIRAJASTHANISESELWATUMBUKAVENDAWARAY_PHILIPPINES183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505NDEBELEX_BORK_BORK_BORKX_PIG_LATINX_HACKERX_KLINGONX_ELMER_FUDDX_CommonX_LatinX_GreekX_CyrillicX_ArmenianX_HebrewX_ArabicX_SyriacX_ThaanaX_DevanagariX_BengaliX_GurmukhiX_GujaratiX_OriyaX_TamilX_TeluguX_KannadaX_MalayalamX_SinhalaX_ThaiX_LaoX_TibetanX_MyanmarX_GeorgianX_HangulX_EthiopicX_CherokeeX_Canadian_AboriginalX_OghamX_RunicX_KhmerX_MongolianX_HiraganaX_KatakanaX_BopomofoX_HanX_YiX_Old_ItalicX_GothicX_DeseretX_InheritedX_TagalogX_HanunooX_BuhidX_TagbanwaX_LimbuX_Tai_LeX_Linear_BX_UgariticX_ShavianX_OsmanyaX_CypriotX_BrailleX_BugineseX_CopticX_New_Tai_LueX_GlagoliticX_TifinaghX_Syloti_NagriX_Old_PersianX_KharoshthiX_BalineseX_CuneiformX_PhoenicianX_Phags_PaX_NkoX_SundaneseX_LepchaX_Ol_ChikiX_VaiX_SaurashtraX_Kayah_LiX_RejangX_LycianX_CarianX_LydianX_ChamX_Tai_ThamX_Tai_VietX_AvestanX_Egyptian_HieroglyphsX_SamaritanX_LisuX_BamumX_JavaneseX_Meetei_MayekX_Imperial_AramaicX_Old_South_ArabianX_Inscriptional_ParthianX_Inscriptional_PahlaviX_Old_TurkicX_KaithiX_BatakX_BrahmiX_MandaicX_ChakmaX_Meroitic_CursiveX_Meroitic_HieroglyphsX_MiaoX_SharadaX_Sora_SompengX_Takriendanlfifrdeheitjakonoplptruessvzhcselislvltrohuetxxxunbghrsrgagltltrukhimkbnidlamsmlcynetesqtabejwocurbhgutharcaeoeuiaknpagdswslmrmtvifyskzh-Hantfosuuzamazkatifabssinnxhzugnsttkkybrtwyisougkumnhylosdrmaflbmykmbodvchrsyrliforascoiekklnpsqusntgtttoyomiwoabaaaybabidzfjklhahtikiuksrwmgnaomrnsmsgsasststnvozakhascolggvsr-MEakigmfehawcebeegaahmnkrilozlualuonewnyospamnsorajcrstumvewarnrzzbzzpzzhtlhzzexx-Zyyyxx-Latnxx-Grekxx-Cyrlxx-Armnxx-Hebrxx-Arabxx-Syrcxx-Thaaxx-Devaxx-Bengxx-Guruxx-Gujrxx-Oryaxx-Tamlxx-Teluxx-Kndaxx-Mlymxx-Sinhxx-Thaixx-Laooxx-Tibtxx-Mymrxx-Georxx-Hangxx-Ethixx-Cherxx-Cansxx-Ogamxx-Runrxx-Khmrxx-Mongxx-Hiraxx-Kanaxx-Bopoxx-Hanixx-Yiiixx-Italxx-Gothxx-Dsrtxx-Qaaixx-Tglgxx-Hanoxx-Buhdxx-Tagbxx-Limbxx-Talexx-Linbxx-Ugarxx-Shawxx-Osmaxx-Cprtxx-Braixx-Bugixx-Coptxx-Taluxx-Glagxx-Tfngxx-Syloxx-Xpeoxx-Kharxx-Balixx-Xsuxxx-Phnxxx-Phagxx-Nkooxx-Sundxx-Lepcxx-Olckxx-Vaiixx-Saurxx-Kalixx-Rjngxx-Lycixx-Carixx-Lydixx-Chamxx-Lanaxx-Tavtxx-Avstxx-Egypxx-Samrxx-Lisuxx-Bamuxx-Javaxx-Mteixx-Armixx-Sarbxx-Prtixx-Phlixx-Orkhxx-Kthixx-Batkxx-Brahxx-Mandxx-Cakmxx-Mercxx-Meroxx-Plrdxx-Shrdxx-Soraxx-Takr	

	

 !"#$%&'()*+,-./01 !2"345678#9:$;<=%&'(>?@A)*+,-./3BCDE4FG5HIJKL6MN7O9PQRST:;UVWXYZ[\]^_`abcdefghijklmnopqr>s?tu@vwxy������ThaiHaniChamLisuZyyyLatnGrekCyrlArmnHebrArabSyrcThaaDevaBengGuruGujrOryaTamlTeluKndaMlymSinhLaooTibtMymrGeorEthiCherCansOgamRunrKhmrMongBopoYiiiItalGothDsrtZinhTglgHanoBuhdTagbLimbTaleLinbUgarShawOsmaCprtBraiBugiCoptTaluGlagTfngSyloXpeoKharBaliXsuxPhnxPhagNkooSundLepcOlckVaiiSaurKaliRjngLyciCariLydiLanaTavtAvstEgypSamrBamuJavaMteiArmiSarbPrtiPhliOrkhKthiBatkBrahMandCakmMercMeroPlrdShrdSoraTakr&amp;&apos;&quot;2147483647�������������������������������������������������������������������������������������������������������������������������	

9_nuvvvvwyvv��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� !"#$%&'()*+,-./012345678������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWKXYZ[\]^����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������`abcdefghijklm����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������oCpqrst��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Mx�������������xz{|}~���W����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������𠴸�����vvvvvvvvvv��������������������M�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������B�K�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������B����������������������������������������������������	
	
												
														
															
													



	












			
													
														
														
														
														
													
														
														
												 		
										  	!                        "                   #        ""             $                   %                   &                   '                     
	

	
��������������������������������������������������������������������������������������������
!$'*,.036JXdh			
�����������abcdefghijklmnopqrstuvwxyz���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������i����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������79;;=;@BDG��������������������������������	

 !��������"#$%&'()���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������KNPS;V�����k�����������������������������������������������������������������������������������������������������������������������������������������������������*��������������������������������������������������������������Y[]_a��������������������������������������������������������������������������������������������������������������������������������������������������������+�������������������������������e����������������������������������������������������������������������������������������������ij������������������������������������������������������������������������������������������������ⱥⱦⴀⴁⴂⴃⴄⴅⴆⴇⴈⴉⴊⴋⴌⴍⴎⴏⴐⴑⴒⴓⴔⴕⴖⴗⴘⴙⴚⴛⴜⴝⴞⴟⴠⴡⴢⴣⴤⴥⴧⴭᵽᵹ<br>ScoreOneChunk[%d..%d) <br>DumpHitBuffer[%s, next_base/delta/distinct %d, %d, %d)<br>
Q[%d]%d,%d,%s DL[%d]%d,%d,%s D[%d]%d,%d,%s <br>
<br>DumpLinearBuffer[%d)<br>
DumpChunkStart[%d]<br>
UQLD[%d]%d,%c=%08x,%s<br>
[%d]%d
%d lin[%d] %s.%d %s.%d %dB %d# %s %dRd %dRs<br>
<br>DumpSummaryBuffer[%d]<br>
[i] offset linear[chunk_start] lang.score1 lang.score2 bytesB ngrams# script rel_delta rel_score<br>
[%d] Hitbuffer[) Linear[)  ScoreCJKScriptSpan[%d,%d)<br>
<br>ScoreOneScriptSpan(%s,%d) '%s'DocTote::Dump
  %d chunks scored<br>
[%2d] %3s %6dB %5dp %4dR,
������������������������������������������������������������������	
 ���������������������������������������������������������������������������!#'+/37;?CGI,������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������	

 !"#$%&'()*+,�-./01234��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Ь�������������������¬���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Ь������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������X犾��g����r肆�vD���DHD�uH�HH�
v�D�
��/�5��t�/��ˆ�X��Ho�sU��vxHˊU����D~����������A���������a�,�K���gAˉ��bc���5��5o
v\HNs�DϊD�D��~#�~翿H�Ͽ忿�鿿HvD���xD����t�?�H�j�~XX�����y翿�����Ͽ�
����Xݿ܌������v�������Ή��s5�DX����uH���?�b��D����oX�a�D���X~W�ӌ�XiDA����#x~���Hv�v���U���yv�o�W�υ�H�oḦ��s�y�s�X���W����yv���lHHb���ˇD�W����蒆s�����D��vv����s����voK��,����HH�Ҁ����t�/�D�\���y����y��Ή�ǿ����Xς�~�鿿��|�/Ŋ��UvW�/������e�����}���ǿɬH�vw���оΆ���H�a���}H��H���鿿��묿��������i�[����ƿƒ3����D������X������̿WWHHH�H���H�������d��~K�k�Wv���������������L���t����X���������D����������v����D���W�w�����2����ǿD��y翁�e���W3����k���πwH���y���2�H�D犉��w���N�tb�v���r���v��K׈��~����v�X?�b�C����HC�����m����X��
�����Ԋ���\HvƉ�Hbv�H,J����܅�e�c��H�o���H��dŠ�H���t�t��H��Lxƌ�X�
�a�H�v�5�牉iX����
����
gg��C�K�vsӾ��X5X���vX���dwo�y���~����D΂����u���τ~��e�d�̏����x��̿WoLοHM��z��ƿ�������H��������������bu�������o�����������	

 !"#$%&'()*+,-./012345�w�t�gH��HϊW�̉W�mzj�W���Կπ��π��,L��K��DaH�u��v�C��������~�og�o���~�H�����#GA�vg���N�vav��W�3i�k��C�뇐b�?~�����D���~AX�X����D�D
H���̿?��s҉��y���H�DH�Ag���w���wϿ����܊ԅ��|�z�Ͽ����A�H�Dω�A�D��HW��DD���g~�s�v��g��鎑s?��Ͽ�x�v���܉��y�XCD���H��HXoUXg����~��X�����K?vw�g���X�v��ψ݊,���yHu���H���u���z�e�π�H���X�v�y�ɉ���s����g��5�XH��ς������������t΂�w��~�xv,��C�����D���s���X�v��H���Ӏ�����Hk�v��Dyv~s�D�v������m#�v��DH�t���H���yӐ�ψ�y��������X����X����v����k��,������`܄�vv���܂Ͽ�Կ������H��oy�s�xs��y�vi������������WԈ~���g�tWt[uǁ�y�vǔ��X��������ǂ��π�HaHD��y�3���������y����~���W��~ǁv���k�L\ܐ��v������w̑��v����v�v�yu��i�����y��,H�������a�~΂�����W�yy\Ͽ��y�����w������������y�����e�܏�݁��y��K��D�iye��v�y��y�������̈��ux��x�����臀������G�����v��K�z�k��������H���D�����������,���̍vH~o����bHHHU~��|�����x�������������H���������H�������H�����X5�����b��vԿ���Ѐ�π�Ͽ���b�~�����X�����Yy�����i�ωsϿ����������i���s�����m݈�珌y���������A��Ͽ�H�H�v�܌�������3J�璆���u���tu�������v��s���mstt��y���u~ԁ���������i���v�������b���u�?�W�}D�����v��z�`���m������������X�y�[�e�u������#�������y�U���y�D�AN�,?�Hty��3�������HH�ωH��g��g��X�����犈X�D�����Dx���g��jD���CX�X��Dvo����v��GN�̡��5}s}������~DDHd�v�����̈��v���v��ψ��?t�H�czϿ��Ԉ�gϿDԿ����v��,���z��~D�~��݀H�C�D�����ǀXW�~�j,U�����H�eH�����g�DvH��H�H�����C�v�����v�d��x�v�����u�?݀�~���#���3�HH��H~H�������������C��k����������bb��HX����������g�����������������v��������v�����X��X����x������������c��v��Ͽ�H�������y��������j�̃����������,�����CHA���L��������������������Ί~錈C�CDD��D���3���������H�}�X�X��D�t݌������D�X�X�A�����C�����X��s��X���WD�D�������s�����te~����sL3sW�KK�a�v���LL�X���u��D����܈碀�~����H�����~�H����v�翿~�H��CDot~A������g��Xy3ovΈu������sH2�k����D����s濿H�Q|?H�H���������iC��v���HQ�����m�����X��/�s���ш�����������y����������D����u5�Կ��c��j���m���K��H���nj�ƿ�iHs��Db�DHL�vԀ����Ͽ��y�g�ԿH��b��ς�H�υ����������H�~���/����������������/����������ǿy��ѿ�ut�����������������������������3�L3�����������ts����z�3������5���v狈��bD爿�v5~�o�g�z�H���������b�ݐc�H��D�3�������wH���[�
���Ks�������w��Hlj�l��Ԁi��ϿϿ�Ίb�H�����wis����������c��瀡XW���D[���������b����X�D鋿H�HHH�Ҍ��m�Ͽ���55�D����yu���[s�������~�݂����L�KL������D���M��D��,���D,���v�g����t��D3��DDX���
���\XD�Db�οl���bWHW�HbD�,�迿�ܡ��ܢ3�y�����X����c�,��vy5��smDU�犊����W�X�5��W�CſG��H�Ή�W��DX����`��z�d��gW������,
R����x�����X�Կ�Xܿ��΀H�Όg�Ԁ��D��ǿ��҆����v������x�Dܤ�
�����v���X�ѐX����#g�X����LjD����X����������������L��5o����y������UoDm�DcłDs~�H,���ܿ�H�����vK��s���~������H�����̿X�����ԑH�e�b����X��Έ�X��ΉѿϿ��Կg�g��H�������v��i��DUL�u���A�b�Ͽ�H��̌���X���D���炈��������g���DW��X����H�sǿ��HH��H��m�ǁ����|��[�D�W�̌��D������[��k��o����[�����[�������W��������g��������������H�Wd�W�������3��ݏ��������v���Ws�ɂ��Կe�HH�Կ�����ҿ���������X���~K��[N��3[�����U�DD�X�s��ΐ�
njǿHHɀ��H�e��vܿX,M�y�,�҆�D�����5��s|�Hv�x�v����}�����b��~v�v�D�c�sݐ�����������	

 !"#$%&'()*+,-./0123456�����o������~|�v��C�������v��������݈��g���s�Ǒ}s�v�y~o�‡���U�vg�Xys�,�eH�H��Xgv�v���s���ϐLjv牀�܉�v��y����v���翿\HH�Hb�dv���g�ҿ�����2~��o������v���X�ϊv���e��
H�HH�������H�wтg5��v��k��nj��~��vр~HϿH����k���tskX�݌��v��������e���Ht���]���X���g��Wk�݌������x�vW���X����,�H��b�H����k�v���vy�����}��������р�X���H���\�m��e�?��b������v�����ρ������v̊���[������v��������u��L�����[�v����m�~��������[�Di�D�3���b��\�c~����s�yv�����v���yM������g��爡�����́w����L�s�������v��y��D��K̒������Կ���}�o����s��HԌ��}Ͽ�2�������sW���y~猈�D�����D��Ԇ�m�i���������ow�D�,HC,X�����Xщ�o�g��U��H��3�U����K牿�y��HH�x����H��nj���ц��t̉�爈��XgX��s��H��o��y�����CD݈��5C�����g����X�����5�~�`�/Ho����e�
��kX����2�5��D�Ӈ�HH�鐿�o�D�ӽ�~�g�v,Ͽ���v��ga���D��v�,���go3����yX���܌XN����Կ�܁���5���ݏ��i�������y�ӡ�����2�m�L�������Կ���
�H�~υ��v����DXX����D��~,~X�������~s�����D����5���mg���mw�D��3�����H������x�X��D����/����Ds�H�u��A�����c�C���g݉������X�
����v�y�������Xό��d�����ueAb�ܾ��?H��������~�s��������iϿ���d��H爆�`�����#�D�H̽�HH̀�}Ͽ���v�U��D�3�����Ɖ��tuH��H��X#X�����t����ܿ�oi��H�������~��艉H��i�a��v��������D����,t�t�ܿ	܅���H�������������w�D����������݈�����w�[���Dg���v�~�
�s�Du�Hb����ԳbD���v爿~����X��v�~��#Ͽ�ϒ~�x��y̿�翿g��C���v�ʼn����t����a�H��#�X��������������b������������C�5��xH�H�L�������ot��~���v�,tv���L����i��W����b���HxH�e�t���ov��gv����ݽv�vL��D��������X��e���WX[��c����tb������������������Կ�e�Xܤ�yK���s,��M�������y�܊��\t���H�o�yWa��j�πv�H��������Wv���KH�������t3����ix��[����ҳ��~�L�w܏�����Ͽ�������d����oj��������y��������W��������|�HD��y������g�
~�A�X�����[~�Nv��X�wz�H�����v�DvX��L�X������g�X�\����ϳ�Կ��K�����H������y�w��j�\��D��,��X�?�ψy��`����U��C3�������v���������5��v
����v�Hy��ѐv��|�v�v��nj�,D���W�DX�����mW��HXyx�Dv�������s����U�kvCsxD�v��vX��od���D�DHu�HHb�b��H2x���D��5cU�kJ�Xd���ό�C������Ί�gD�#l�����D��Xx���?Hb��������X�����5��|/D��D�����#���WD��Hܿv����yݽ�҈H�������H��H��HHb���HDe�HJ��#܉D��y�X�ωX�H��a��RcX�Cǂ�π5�b���b�H��HHHH��X����v�g[?A�H�ov�����D��Hs�K���ψv��yy���x���D3艡~w���tuH,�H��tuH�#Wx�CX�sCXW��s���v�x�o��3���v��3�����#��������ܽΒ���z����AAu�H���v��dž���k�X������UK�v��x�������H��DD�C��爁����sx���,��
�bH�by��灏��yK[���d��tC�5����3���т�mxDyC���L�vw�u�H���������U�K�Ht�ϐW΀�ϿU����������x�ye��L�v����������v�3��H��i��U�Hc��[�W��?��H���ϿH����yH1c#3���2?i�W����������2�w3�o�ݏ�~Uom]���/Nu��y����������]�����ܿ�C���y����W���H�z����Ͽ���AD����?v�ԉWH�����AgԌ���H�X�H��2���Rc�vD�HD�|�ΆGw��H���Dy�3��oH���H��H�v���va�C����v���tH�y�aay��k����v�Ԍ����u�����ܿ�����w�܆���ρ����3�D܂�����x3`��[�ԪX|��ԉ�v��Xx�~��D�����o�v�����Ԁ�DHπ[2ωK�L���i�K��Du����X[���2y���������y��[����X�K�w3D�H�vWɿ�N��������	

 !"#$%&'()*+,-./01234567����y����v�������ݿ���D��/�D����ό�����yb翈H���Xv�������������y��g�ωݿH��\��Hԅ�����D���ܪ���Dit��Ӎo��i�\H����DL���������H�����v�t���Xo�\�ӎ��v�]o��5��W���������������������L���?��L�M�K����D̈�D�����X����y���y��o���������̿v��X���D�~�����������������������/~y����������k����C�yD����H�vӉ��XU�̐�y�y���mDDDD�݃?�y�`������o�s�D~�~U/�L]Կ��yy���wv��l����Cv�D���v�L�D�v���v�2�����#`���k�������Hw���������������s���Xv�ϊ������b�����v����M����������[��s~���g]ӈv�DH��v5���ӈv,\���������������D�,����̌D��e��#��i��蒿�uH�y�s����i������Ca�1iuU�����
��o��cv��s�H#X��oX�~c����݈��H����A��~��X�y��o����uH���������|k��т����y����y����������vя�̆�v����H�������܀��k��̃�����m��d�����`��������ԏ�ý��o���D���WDyC�t��Hӂ������������爿yH��������wg�w�Ό�U���vH���v�݉K��L�W�o�3L��vnj����݋�J���Ɇ�u�������/�������X����ǿ����os�j҈�AH�����΀������v����HJ�̉i�~�������v���AD�v��wz�v�jH��wν��vH�XoX5�����UX������b�������翨���y�����5~�����H�~��HH���y��g��sv����ubH�������gds�����W��vv\����yc������x���������������Գ��s��X�W�~HvDW��v�H�����D����?�����s�v���ss��g����������̈����������HvH���Ͽ���������������������������҉DCy���3Xi����j�t�D3��2xljXgD�i݈�݊��Xܿ���\H�o���2��5����D3����������wD�2Lmy��DH
sD[�XH��Uu�
��X������v�罊�Dv�J���H�A�����HԿ���댿����܊v��܀��gܽ�X�W�ws�����,����Cd��g3,�D�L�L��D����3t��z�W��X�H�oX��s�J�o���Hg�yx}��HH�X��HH��[m[�����[���[�Lt݁�������?�}�,�U����a��,������W��D����a������oϾ?���iX����o�X�/����is�v��Ό��������u�HƏ�t��g�XXJX��Hl���������v���v��
�?���
�����Ϭb�/���翀�̿�dž�����e��~ϿԊ��o�jϿ���珽��WDsӒ������������tvm���tb���v��v���������������v������������y����Waǿ�yKXˏ�������k���L����vm����H��e�����Lj�χ��H�eD����b�m�܀����v�H2��v܀�o�zt���D�����琌��v�璊#������s����t�ˆw������́ckW[�Hvk��W3�[�����W������ew����W���mt����[Wƒ�����k������t�3�k������y�
,W��ܬ���y�翿���b����N����,�������H��Τ��Ư3�L�w�mkwm���������u,�����k��om�z���WW�wW��k���y������������m�m����Kk��������t��yW��H��݂οu\�ϫWk����Ʌ�y��K��tDLzW���ܿ�o�Hzy�b��D����l�H�|�W���������?���H��Hb�HH���H��H
Hπ�
H�HH
��HH
�H�π?H��b��Hϳ�HbH�H�πH�
Hb�ϿHH��H����b�Hb?��H���������Hܳ�܅,ݐ�������������~���v?H����t�H��琌e���ݏ�]�a�����3��wφ�c�����x����o��X�H�̌��WW���o�2����Xgy�D��~��2�j��k��Hx���y������D���w�D���D���3��vv�X�H��s�����������D�H�D�D��H���v���3vv��k�u����H�y[K��yk�yWN�y1o��������DXv���b~�H��������s��#����Hbb��A��ϊ�v���|ʈ���5�����ѐX�g�v̌�W�uWH�Կ�bHπ�v��ϑ������Ъ��ԿԤ�K���Ǒ�u���HW�܆������vXHk�XɌX���������v���ς�X���Wg�H�b������������WшXwt��������X�u����`��L’~~������܏����N������3D�W܌��X���~���vj���������	

 !"#$%&'()*+,-./012345678ξ��sz�K҈R�i�U�g��iD�������ˉ�X�HH����g��z������z���������sK��������W�����gbw�?�m�
��v�������y���b���b��������G����΂a`����݂j���������u����b���oX����X�ov���a��d�v����?m�����D|s���D�ӑ������̈������Hd��DH���ow�v�版���ӂ�b��w��d
�v�b�H��b�HH�H�w�X��jX���HC�oKv���2v����c��������D�l?܅���H����΂iR��X���H��
��g���/v���D����iXv����������~���K�gv������b��灧�b/��LD��o��v������������X��X�����s�[���v����AyXҁ�d�������H��/�D��w���y����D����w���c�y����~�3��d���������������s��������g�������g�y�vLD����UK[ύW����,��Ԅnj����D�Ό~�
�����o��������X]��L��?������������y�y�tmU������[~,�esM������5�[ݐ��x��y���iz��55��oX������L3w��x��?��������������������������熿X��D����K/�D��#���s������ibH���a�x��ӎ���|���o������������y����������Ɉʿs�,�����~��c���o��dAt����iXӁ�D�5�������肂�H������?�o����d�Xo��bԎu�H����Ӄ���o������o�����L�����ӂaӃ����uo����������yc�|���������������y����������2��������W�o���w������w������s���s����3�K��ο,��D���Wb�ӌD��W��H����ӐHӿ����������/�C���iX~���X�����H��e�̊b��c�t�������A���m�D��v�kjy�H����Xs��v�ki�������ſϿ��Xy�����������g���X�����o������D���m�����X�������������xo,��m�k��y��u�����k�����K
H�
������D���������\��ݯ�X�m��k������L��/��W����k�kݏu���kK
�����������3k��WW�/k��W
�w�
k��������������m������݉Ӓ��[WL�[�3́�k��L�W���������,[����3���¡��k[j��3�L��[�[�WW�]����y��Ӓ�m�����kӒ’̏�L,����3�3��m�[��������K�¯���k�3�[�����o�KW��ky��u�����t�����L��������H�H�
�bH|�����܀�HH������
b�HHϿ
��H�?��H�H��HH��
�H�b��HHH�Ͽ������ԿH��H?��H�b����H����H�5��~���w�,cy�K�g�����U�������D���o��ѡ�����y������3W��W�m��W��m,�LWw��菏�w��wm���33���m�[����Wݒ��33[��y����K�����y���tm�Lt����t�3���?HυHHbbH��?�H�bb�b?����bb�HH������?�bbbdD��D�Hӌ���H���v�܈�����3�v�XL��̉y�����a���b���Dv�������������v�s����G���|������\���K�o����~�vl����g������Hv�g��������������L���v�����sd���������d�i�k�����������̈D���v�vu�����k[W3������݃�������ܒ�ݤ�uL�Ϗ����e2̏K��y�W�y�ǽܒk����y������珿��W����L�z�3y������W��t��HbH��
H��ܿ��H���HH�H�b煅Ͽ��~\Cs�C�K�L3�k���t�t�gX��
���X��d�
�
�b��o窂3�̬v��s������X�Hy�X��g
��og�H�D�s�t���a������ň�����X�k�����D�HD�oX��珏s�vo��W���[�3�KWt�5#�k犈��L�s�[k�3W�v[ov���[�tL���DbC���H��CD��D�
C�`�H�C�XH���D������5��������v��/��ԅ��H�D��H�uH����H����2̡���D�H��H��3���2�ǯ����������D�DaX�����v��?�i��~ň�vvHH���Xv����HƁ�sUv�j��j~����exvWK��zu�Kyyz�d�u����������	

 !"#$%&'()*+,-./01234567892���xX��g�w�����灏���gk���m��
�����y���������������̏w�L��H���������k�c�HU�����L�,��3Ӣ��������k��,w������݁t�kmWHHHb�����KƁk�t�y����������u���y�����z�����m�[����������w��u��tt����W���t���k���́���ݒy���[���3,ƒ��������vXHw�H���HcH�HH�x����HH���x|HυH�Hmt���m3�zzt�2k������ݒ��/�o�Cπ�H�H��b��b�|�HHHυHH�H݀|�tǁ���́��L����������HH��HH��HD�����πH�H�����Ͽ��π��́L�w����y����kL����������y������ο���owyw�������H�ܿ���̿H���H�HϿH�H���HHH??��H�H�H����bH�HԿH�H��Կ����Ͽ�����������?�������HH�ܿ�?�b�?�b����HH��b�C��H�b��b���H�H�|π?H�H�HH�Կ����bHH�����H��Hܿ��������������ǿ�?k���W�[����k̤3�3�w������[W��LK�ѫ��i���bHbcCǽH�b�ǿ�yiH�o����́w��u�z�wy��HHϿ��
H�Hb�?�Hb������HH�b�����5����t�2�v�H���DD�C~�H�sv�����΅��g[�w�v�W�u��s�W�L�D�UW�3����5Di�WD�k��am�sXk����t��,��~yK����?,y��g�
���a��XoL�D�b��y�m�WW݉Ήm�Y
[�~��c�b�s�~�s�y�,vɂ�sD����������a��o���w���K�������H�D�5yx�v����̌�ttxgv�����t���ӂ�J�2���X���D����������������L��[������HH��Ͽve�XHv�3�ݏ�w�mW�ݏ�[��mw[wWu�����y�w����tky����mWy�,����珒XyH�HC5\b̒��wݏ݀��x�����sǀ�y��b
�H܅H�H���b�H��H�ϳ��H��H��W���my�������L���
���b��W����3�������kkm������������mv��W��Ly��w��������聁����������L]y�������������?�b���H�܀b�Hb�H��b�ܿ���H��~����DDW�3�Wm��Lu,����m�L���w�ݯ���N��w����[�����W��,����t���W�ݡ��������K������[W3��/�
��Hb?HH�����b�H�?H�H����H�HH���?�Կ�b�ԉ���������v����,�������ź�t�������o�,�/�������������������������¾[���Έ�X�~g������D�X�k��������L�����������t��w������������t�m�[�������������Wy�������
��������L�W��������������������t����������������́�m����m����m���3�������������y���̀�������H��������܅H�H�H�������Ԁ�����?��?����ܿ�H�ԅ������m�����3��������[�,���w�������w�3������[��y�������o���m��j����[�yL����������������������������������[��m���������������L�����������Wk����������3H���b�H�����b�����H��H�?����ܿb��������b����H�H�LuyLKX��y������犉�3��oD�KG��m����ybuu�`�tKA���~Y�RuHUuo��H��M������������������D��������~����������X��L3��K�����zy��������H������3K��y����H�2��x��������������������������������������������������������������������������������t�������������������������������������������������������������������������Ь����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������	

���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������	

 !"#$%&'()*+,-./0�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������	

 !"#$%&'()*+,-./01�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������	

 !"#$%&'()*+,-./012�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������	

 !"#$%&'()*+,-./0123����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������	

 !"#$%&'()*+,-./01234�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������	

 !"#$%&'()*+,-./012345����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������	

 !"#$%&'()*+,-./0123456�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������	

 !"#$%&'()*+,-./01234567����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������	

 !"#$%&'()*+,-./012345678����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������	

�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zh-Hans zh-Hant ja-Hani ko-Hani vi-Hani za-Hani ja-Hani ko-Hani zh-Hani zh-Hant af-Latn ar-Arab az-Latn be-Cyrl bg-Cyrl bh-Deva bn-Beng bs-Latn ca-Latn ceb-Latn cs-Latn cy-Latn da-Latn de-Latn en-Latn es-Latn et-Latn eu-Latn fa-Arab fi-Latn fr-Latn ga-Latn gd-Latn gl-Latn ha-Latn hi-Deva hmn-Latn hr-Latn ht-Latn hu-Latn id-Latn ig-Latn is-Latn it-Latn iw-Hebr jw-Latn lg-Latn lt-Latn lv-Latn mk-Cyrl mn-Latn mr-Deva ms-Latn mt-Latn ne-Deva nl-Latn no-Latn pl-Latn pt-Latn ro-Cyrl ro-Latn ru-Cyrl rw-Latn sk-Latn sl-Latn so-Latn sq-Latn sr-Cyrl sr-Latn sv-Latn sw-Latn tl-Latn tr-Latn uk-Cyrl ur-Arab vi-Latn yi-Hebr yo-Latn zu-Latn af-Latn ar-Arab az-Latn be-Cyrl bg-Cyrl bh-Deva bn-Beng bs-Latn ca-Latn ceb-Latn cs-Latn cy-Latn da-Latn de-Latn en-Latn es-Latn et-Latn eu-Latn fa-Arab fi-Latn fr-Latn ga-Latn gd-Latn gl-Latn ha-Latn hi-Deva hmn-Latn hr-Latn ht-Latn hu-Latn id-Latn ig-Latn is-Latn it-Latn iw-Hebr ja-Hani jw-Latn ko-Hani lg-Latn lt-Latn lv-Latn mk-Cyrl mn-Latn mr-Deva ms-Latn mt-Latn ne-Deva nl-Latn no-Latn pl-Latn pt-Latn ro-Cyrl ro-Latn ru-Cyrl rw-Latn sk-Latn sl-Latn so-Latn sq-Latn sr-Cyrl sr-Latn sv-Latn sw-Latn tl-Latn tr-Latn uk-Cyrl un-Latn ur-Arab vi-Latn yi-Hebr yo-Latn zh-Hani zu-Latn bh-Deva bs-Latn cs-Latn da-Latn es-Latn gl-Latn hi-Deva hr-Latn id-Latn mr-Deva ms-Latn ne-Deva no-Latn pt-Latn rw-Latn sk-Latn sr-Cyrl sr-Latn zu-Latn { throw 'Array index ' + $0 + ' out of bounds: [0,' + $1 + ')'; }T!"
K'hnopqb ($	
%#��}&*+<=>?CGJMXYZ[\]^_`acdefgijklrstyz{|Illegal byte sequenceDomain errorResult not representableNot a ttyPermission deniedOperation not permittedNo such file or directoryNo such processFile existsValue too large for data typeNo space left on deviceOut of memoryResource busyInterrupted system callResource temporarily unavailableInvalid seekCross-device linkRead-only file systemDirectory not emptyConnection reset by peerOperation timed outConnection refusedHost is downHost is unreachableAddress in useBroken pipeI/O errorNo such device or addressBlock device requiredNo such deviceNot a directoryIs a directoryText file busyExec format errorInvalid argumentArgument list too longSymbolic link loopFilename too longToo many open files in systemNo file descriptors availableBad file descriptorNo child processBad addressFile too largeToo many linksNo locks availableResource deadlock would occurState not recoverablePrevious owner diedOperation canceledFunction not implementedNo message of desired typeIdentifier removedDevice not a streamNo data availableDevice timeoutOut of streams resourcesLink has been severedProtocol errorBad messageFile descriptor in bad stateNot a socketDestination address requiredMessage too largeProtocol wrong type for socketProtocol not availableProtocol not supportedSocket type not supportedNot supportedProtocol family not supportedAddress family not supported by protocolAddress not availableNetwork is downNetwork unreachableConnection reset by networkConnection abortedNo buffer space availableSocket is connectedSocket not connectedCannot send after socket shutdownOperation already in progressOperation in progressStale file handleRemote I/O errorQuota exceededNo medium foundWrong medium typeNo error information
	

		


			

			

		0123456789ABCDEF-+   0X0x(null)-0X+0X 0X-0x+0x 0xinfINFnanNAN.!"basic_string length_error"/home/eitan/Projects/emsdk_portable/emscripten/master/system/include/libcxx/string__throw_length_error!"basic_string out_of_range"__throw_out_of_rangecannot zero out thread value for __cxa_get_globals()cannot create pthread key for __cxa_get_globals()pthread_once failure in __cxa_get_globals_fast()N10__cxxabiv120__si_class_type_infoEN10__cxxabiv116__shim_type_infoESt9type_infoN10__cxxabiv117__class_type_infoESt9exceptionuncaughtterminating with %s exception of type %s: %sterminating with %s exception of type %sterminating with %s foreign exceptionterminatingterminate_handler unexpectedly returnedSt9bad_allocstd::bad_allocN10__cxxabiv119__pointer_type_infoEN10__cxxabiv117__pbase_type_infoEPK
!<]��AqQqQ.chrome/browser/content/branding/about-logo.png�PNG


IHDR��R�lQ8IDATx�p#I҅��j�<̴;�4���0333333333�
3333ږ�U����k�cر��]e[��eeV�B�H�CRĝb2p�s:0�NQ��q��UY�����C�����yœ�e�s��5GE�\Cǡ��yᇵԽ��tF��H��1(��Iui����E���O��"�	�����s��ޝRG()�Bb�����-�7%E��W�]��Kg�3���"`p7`�C�MG���7=�����K����V�M�Nj-�ME�E�;{5�.���N���&���PL�i��	PD��Hf��o�>�$k@:!��p�	��.P�aϹ����F��@�΍��.Bj`	���P�a]��,��L�߼��!��k�{�3@��-�;er�H�H�)�I2[��C���Ӏs�#��B�����Yzr'�����j��s���bT�aE��Z6�.G�D�3���`Opx�\@��B>S�@��)��x�$Q�a
��qq
�Z�ա��By�l�.\���B�3A*(���#�p?�����^��hDF�2����ڰn4��R�qM�v����۟Bُ�F)��)�m@9��햾�R����1��W�=C���$8R@G�q�ջQ�7x�A�7��U	�M_r�1Ə�kq
aX���^����=�~����[��C�f��l��)��T�l��<1��G=��T�:�qQDA%G�>j���ctB�<kK]Mo��)�ƃ�� ����f�x~�P�Oo��E"�,�[f�<2�'f5��h���#��
 _�o@^�[|y�� ��,�r���2�WPR?�ۓ	���������
ڒ�_�t��}9�Jڐ��Y5�zjV�Ȫ"+�*���Ə���տ�A�8�^Fؚ�~(��
������˫�Y�ż_���Ÿ�=y2�3�)����V�z_��RuKD�6��6�ֱ�b�<?��f�7+r�Bc̯�_��qƏߗHצ�C�����q� ��yp�C�
���&���ڍ�(8a0�v��o�&�x׌���V������y8Z��l�U��f%�ދ ���DO���7j~
��?��%>3�퐋�(n����^plBx�V��/�@2a߆�S�Q����	�=���^Y-';�uoV� �l�sҳ��g�<��I���gE����#������h b��a�]Ә�!��4���'!�lj;J5p:�?�p�znxnPv���ig��	�ή7��M�^�T�q챦3p��1�ޮV���$�o�͊�>��G�_`�1t��	�1A��Xj��Giy=䒘
ڻy�w4���̚����2���J�zT�w` ��k������Bk���H���g=�/U��8D,)�-�I���1V͇������~Vy��?���[���O|�k�BN�ܟ�H�bg�T���R�|��b��
�g���J/�F�<��Y��~M�q�����Q�b["b� ���/y���y�S�Y����j��l
�5�?�l�� ��{o��sDn�����\��mH�4����m�y�+���8��y�6�gD|T�~p8��x�dJ��u����Ъjhd[����&L_�K�`_x�"�`Y5j&��n�(%�nY�Gq��n%��W�@��4�?�$�;�5X��Y��wH��U�����^��'{u\'���Y��~�����	�� �1zN0����������?��736ù{Gе���%Z��Q�P�֝��yC�e,PO;@�_iq�I5����͞�aKx�ZK�~F[�4ν]^1Ъ�R�,-��Kh�&O����ڠ�!8���?
	B:�?�j,�"������W��Ͱ�\�����8�7Z���]0�De��3��ï��?8q����:��l�t�V�� D�]�
P��	��e�@��;ˠ�N�Q���[��p��{`�v�@4)�q�����hQ�5����F�E�מV��5�ϳF��1L�xA�U󅬞�T�V�P���>Ny�_E&�m`J�Zθ$5Ͱ�}�K�Y���?�gPGY�\H��2<��O�ބW���Ə`���p�F[��(��m��e����c��".!bѬBӇO�
��_^L�*{�X��/��!V���B�/z������wyR�_30q��t��w!0c�`��W��s�~m�
1x�A�dx����;?�?�y-k�B��N��p��
V���hC�_$��ղ���͊4���h~%4�"�Xp�8���XA�%�~&�}ժ�Y�S�cB�DŽ���HKJ��[���t�<:�Q�k��'�O�=�7����M���1A������C~NPH>�
����#+�t&�$���N�m���2�FwWUm�=��� |DB��{JLj4P0_��Y�OX5�+~lrC?�a�w�1���N�̩�7��pQ�G��A<�ķYZO0?PmN��[,���]��у�J^�s�d˷9���^���L*�$�30����c�c�(1%;Er*�����b�����~��N̬����j�@�[`�1����&�T�u`dC3��w����_�]��*zZxbN�C&�_F?�9�C^�@���.���r8����<�!m�dV
-�7�Z�UV�ME? a��/��.+�Oũ���7��?n�S3�I�'o��y ����ʕ���k�򮀱`\��|�«y��a%|sl��xP�{�� l�4�
@�&��
ڳ9�m�Ϻpr\Ȅ!8�r;p�VF�:Z�V��+�:��Tphz�Y�q8�ip
Ͽ�P~�xy�5q�^�zfU�Փ4>N!mW���t�(�8b{����%Wk�᥇xL��_�ī�b�4o=�>r�u���F�l�tc�6`!y1��nƏ�Cp|%\
C��g�~��"<֌�7�T�wJ����s�p8�i��OU���d=%�����5hd��q�`]f8�ScX��>@h~�\Ϗ����UD���V�@���|���@��D����
��-P&6�/F����y�A�m�6'݂;'VC�
[����[�Z/�h��jYS^69iJV�p�
��=���Q���5kʊD}��c��q�~�?���>���0���yuM�)��9�R^{⃠��$����A�p���F����.y�ΐ�noƏ�ñ��;P�a��������[#++ߎx�Z���kA��
qt�Z�%��̩�1�9"{m��G~�T�|u&�<�#|�@�@"	1~K^���������G�}���َ�b7�CN��9�6e�����M��M�m��l�qb'�c~hW���f��s������f4�}�@�n������%_�^d��7�nNr�+��`�Ӡ�����XM�Ձ�D+~h�um��L�zI�&`7�����>�Ŀ-�Ċ�n�5l���i�r��|@p�;���0�7�g�{�MS�Ih��"+�g�3<ͨq�:�ᚺǕ� Dȸ�C����E�x=de9���;ĝ��Ĝ��>�m����i��ߓC@`�9��P��m����śޒ�T�� �t���G�GG��C�	����#Tk�Gh��0��\��v������B�7���u��c��7?�o�K~&I`,@[U�N�K�+a-"��{ƿw�Θ(C�]0�O�x
����f�9B��*5e��{I�>j�^+��{H��G`�k���c��!�<�<}?ۃ�h�'�������Cp��h�����ڗ�X��@4�Y��f�z����x�z��ZϨ�z���:w��k#���'S�~&z�HG"��sy��~n�~y�;�ж�iWgP��g���q�����<��W�����=�3�[>fn�ȅo����	�/[�aT�8h������Bx?D�O���~����'BR�u�.��z�H��O�!Z�qh�G�G�F�㢌��(�gL���ּ��Ň�1@j��	�/���{k�h����`�cm�f�����3Rȏ"B���@W��U	��O��D������|�����bOg�~5�+���%(�S�L��yHR�x�A�ك�}���܍��8{���"Y�ݔ����ղ̼��G� �H�#�o�%t���|����U�O*��:��;A����
!�"�{j�1,n�H咁�Q���W���{)�n�K�O
F��=»���79^���#=(���W���9���ܻi-���{�n�&j���>�ף�t���/����NA�g�{�(dz��е�a$�1��PM�3��91���\����7��#�$a�G�m5-���G�RHg,~�8�%�aZ�����q�+��B����@fO��	�)��}A�gL�L����*�aG�M5���e�7OR�/?z�[��$a��8(zܙ^�ځ�A*�x7P��c9-�	�1W*@1���w�i���a�"�����ts��7K�m���P��^{b���/ʊ�^Ou���x�s�$mj����'�Y	x��?�8J�p��=�B�����wq��b�@Nܳ���2�
	;^|7�Q xT0��N�m��|,�į�36��@�cl!��Y�\�o.�4'��ss�v�/������zn��"��8�&V�����=��'����NJ�SF�P�1�"_'��~����=�E�?����co!L�R-�4^��z�'��"=�7t���@� ߯�x��_�͕�q[9��{��O*��\��p���=���s���ep�'��?�Z��j�f�?ʂ�a�?��w6��zSh$D}�#م|��v�*�D(=�n�#v*�C?#���H(х#�N]F]��+h[
r��-|s��^��'���!FT�8�Ԇn�ey����2���`#_�G/0�X�ǚ��i8�t%�4;SM���)�y�ɼ���>؏�J��\�e�`Z��;D�p����;�|k����{ʿOC�K��w�p�`w�w��l�wҲ�����%0����z�}�;7����R��&�?6�G�rJ�����3�<���o�/�G�82B.�+
(����;N90�`hK ��?Dz����Sc�g}7�/��{�(O�z�+jDp�'�e<i�������Ƀ��ȸ�cc
/�m�IE��/��t.}��\�q�!r��g&����&��5��~�*����K.��ws�0�<��^�f�w�؝�x�ؔi���a��⏪�����2@ZHc����qDBZ��:8���Z����#L��'�lS악�토���v�y>�~��9�U��C��c����`�(����g_sZ�����_08/��#�g,����F����7���~\�f_[�������H |��0��t�q]��]g���,�*:3�Z�=��;�o������6D��I��<�;Y�O���H����9S�	<���96-�����K����C�����C�z�
�Dk�{��sQ0��G}�ܙ��Cߝ ����1��"w?���`�w�g��<~�gW��J�V���*����*����1a�@�96���|
�C=.���@J��2�&�VAO'e=�a�����ŀ��%|A����O^�m���"I��c廼�GR����O�`�s��0�o�� �4�_�Pq���SvW��b|�N����*CWj
?����mM`	&��Cc�ߏն����
@���d��Qk������~,�.�hl�@��X����vЗ�'�b`��L�9֫�HZq�ӿ䆁��r�m��]�5���z�!�x}���^H������M�+���һ{TZ|ꍳn��_f�'�k�28Ηe�)cn�y��wcl��@��v��(
�Ұ> 
���� �����#�����rVH$\���n�/��3�o�SN��
���*)��GٿO0�o~�����g�-���nI����Gc���Ǡ�+��
�GU ��x��� -v[@l[��s-^=�m>��Z�2�W�8�(hWhݤ{��W!�����U �G���Q��e+0c��৆D�C&�G���dd�._���}Y����]`���w���=]ew쁰D�lM��ïf�#W�b~�ԃ���H��1ǀ��JE��Sg����ފ@1��Cz	��[W{��VU���Z����]?����>8�Tz/�d�!Z���L�V����iP��x��2���T��EՈ6�i�}�w�*6G¯�׫���Z����v�Z5gϾ�ˏx2��.N&Rs��3���S��Զ��;��@��!�S�_���_���[�x e��1��G�	f�О��u�a��b&�@�W����/�%Hj���8!H��}B�n�rQ���Y7��ގG��<^3������놀�����.�"���D���uCZG���M�%H���	Z�6��Cq�%0�A�7������<�PDbG
�n@�>RT�$Y�
���(�5GO��k�{=)��ޜ(�!�Dnl�{���Q!�[�뀞D�ܲWy��O���ɲ�(��S��p�s@$Lu�$�b-�ҩP�{[*TF�{����sC�Aq�G+~U��Z�CB�i,�F߄���{��/i�}��I��O�
Q�Cz1�q�B�Q��~ض�[����@�
����%�
��@����r�˲S�;�9���=xdHt,`s��)�����w�!?�W�����O[̶vt)�Co�Qs����=����/�N͊*�����|�׶���J�<�uǩ�`�CiY޿ K���`ydF	w��!�1@����H0�_L�\�%/����-X)��B�|�7E�
f_�ح�	��7ů�A	�[�\tk�<�\�4=j��
���E R���B��aQ��^�� ��1T���`~����u�2X\%�����@)����)���{^~ߠ�C!A+~:���"!�,���/L�)�ܡ!��ȳ8x�#���Y�ft����[���i�{gS����)n/-�`�,���U�^��)�\ iX�g����,��(!	�R��K��g�tX0�&f��9ehߦEԘ�d���..@ JBfx�H�
T���ypܰﭼ�&��#�.-��G�M���R�N0�A��ۣA��eI����?���z��ؕ�'��	�5(/�~C��U�b
��a�����n�m���9��?+�q��%.�
�9	�>J�wv�u��j�.�_��fL��/�Y�=����^�,��Ӡ{($�Bq��y��%���07d�	S9"+�,_�I�Q�����nL��p��^����O�;�%[6�`zr�!��j$�}¼7{������-Y����y}$�1��0(��u����y�}�
	=��L��"��2�t�(K��<�}�d�&F��d��w�����'�z`&��	!�9�.�I�߻�o���m�"��c&�����²O��~�Kg~�N��dI��	ˣ�z*�%|]/����|�i��3�(�
%=-�P�^�"I9r��E���.ڶB�����3��y;�v�
�wXsN�!�durq������>9�}�;�`+?:�Ԫ�G�y��y��e���-@�@{�a8w��3g����*����>$Z�n�'�z���@8�@t�����O���N��E���&����!�n�����l�X;� ��i��
G�3�C��;��w��l�dY�]`�6F�4��n����y���R9D�fP���G��y�������y�C��Bhy!&J��oz5kŃp��vF�q�c,!;�Ʊ�Ν�Y�5�ז�6N�W��_�_��>�9�cy����t��A��J��\��P���fj�Bzε����k�{�|�]v
��w1�l&*)���.�'j)���Q�r�3_{b?�4�;�;�7ͭLǙ�孥E_���ž,�k�Ǹc@ �7�v�N�h[�DY��=w���2Նd� �)��M�K���������J4((�A�v�$��ѵԩ^�P�A�
�q`��!�o�xCqĤ���UH�eJ�ǀ�������f�wGq�Y/8H�=R��;��[��w�
��1��a�O=���䙈T�5�*���&�t�8-��\��M�ՙ_��^��o�	�/v׋�!�v����<�xB!�6��g����xU8��2�k� ���aǼvU���Δ,WE��ֽ��z\(��d��SK*�T#�~��
�ʴ7e(e%�������I�@B $�� 9�o_㑶��g�Z��[���!�7@��g�F)%j����د�v�;���
��¨�E���$� $d�̍���h�@�7�
RL�����YoT������S�|2~�\�u&H��@�]7��vW�ޕ�XLE��BJ�7XC��U|d�y<�?��g<���J�w��\��f��*n|i����՗�`P�U�rA.� �����@�!�f�t���=�t|v-i�;��PQK
XdB@
��L$X��s����t�Y$�U��z汉����E�;�p}�7�4:��c�������V�RR�|P�B�~4���Ҽ��7�ra�;�:(�Y
;���?#�*r�u��P;(d���T����oZT��t�A��n+�����ck\����8b�C��6�݀�
X����!���`�D�A@���1�+� �ϯ�R�/�e�s��i������щ������g�D�E�E�7�!��]grw�|-�*�L�D�7��1�|�,6uO��ԃ����{��.bτ��)~Gl����,q�΍�Һ�ر?�'�v�����pA`�F��t�]��>��g:a�j������%��q`�����	O��Ap�z47�
�dž͔���Z-x��4�
o4W~�T5H�2!��P
��)���RxUMZ�	W}+l����eL�Ǿ��6C���'.������r�
���)6�@d�B�y�\3�X��B�VW�ȳ�OL
���Dz2XlZ��'B�n3���>>�?�=ϯ�W���k#���J׀LEs��@9,��ҵ�q��Lc=o��?��mξ3<�@��e����V�}�$t�
<��:�Kh��2��+����
���}�iu@8\��2��3%˹�恸��ÈS�ϵ4�WH�V��t-�@������5W�ܔ^��ƅo��R�/��2��N ,`����`�] =v��֨�Ժ�K'0��ʑ�d�@s�tB+ ���	�o�
{#<�\ ���N���M_,�/� �
�����$��?Ƕ�Z����Eb�Y�36�����}I:U�Oqڄj�
�ތZ�S���i+`�ބ�l��{��N��*w]�����^��?Vs�4@$�@z<�'����>xZ@5pTq�t/�����	B�:(~όL7ȱ�8�"�~g3Ja����+�u9~�i)���)|?��j[�+��ج�9'�ݷ�?��^@�/��E��}�s_q3�t�^��p�t�t��L"w_��D`���R��g$��}�z�P���K���c��`=n����S[�
��Pi�6����Q|?����Pca	z�7�� �'<�<��yh�|���q�/��De*�Jh�����<��=�&�q��Dȫ�V��A��)Y�Q%˓*��S���G�	Rlji$�}�J�������ʯwH� 5�g�bv��v���U��XW$a�p�*�s��ucܷ����W�!��&`����V�N�@�"O��yx΃EIĉ��]ݻ�+�B]��q���X��`�F�	R ��`�M�|^n�3Wy30-A4�(L�Ϟ;(��W�'�()����En�m��@?C��A�=o`�n����^�9�*�}�wA��3��:�&�2U3w�{J�U������[��J���^��3��.�r�2/��!�O�Z���W�ݟ5���+���cׁg?�*���-j�X��vM� ��!x=z�~�0�W�@���?�:~y�W�B�^d	�N/���^@��@��+O�S��=�^3�
<�,��B����n����;���YCx�ޞa$~��Q�X~"�y�<�!�e-"�X��*l;z�@��>�qr�9C����(DB��9^w�૘�g��K;u@6��UE�6���@8+$D����� �݇����IB=Ar�/�f��Ž����z�O��@W���r��� ��W��MG��
�:�z=o���<�`�_��}���;�⮐��u]0D�P�ׯ���_�u��K�C��!�F@&�Q0,ӕ���?z0>��B���N�����N�Lq���ؠ؍LP�C� y l��--���t�y�S���7b?Dy���?���AH����a_�?wfy�
��w[7zh�˨��<����雾�Y�з�+`�s@�%z���A�y��!�.+X6�M�3�������9���K�2��%��^��.��
rwavP<�{�� �8�i��l��!H�|�S�u�j�����y��}��;�50D/l�!P�aP �x>\r�_���,�gmX�u�H�		�z�!_�˫�L(��_2g�?P��ꮵo9�������%͂/�P�@ظ���q@��30����Q+��c��VH��P�{O��8�@hKU�Xݍ��C��S���
��"P؋T����Ÿ���-��r�����RI�� �t_C@[�T�y����j��LrN�O��5g�������8A�E�� �8 _LQ*��_��S��뾮5e�.�G�㗏�`��CgY�G�1P�\�GCg�1(t�l�+�_���\p��X����P���Mv��l�^L�yz��j/\��$�/���+(�������ƩP�0Ɇ��B�'(�q_)�o�/@�U
�!�hU%�.SP>�x����єT�.r��qon�����c�:�1��2g�V�R��s�����"b[��q�8Pp��
�h��6�~�M� *�+�K�f
�i�A`�\���v{D\������Z�C@���[{���}�p��(i_�~|��s���A�
��
�{+8��FX���������^�e�	�wܠI =0o�������xagz��w�1�ȯ�/]���=�{o�p�@�8@U�aك�$Z��;�ֿ���-�\)������9��b�#�c�F��\!a��,����S61�a'�5�d�
A��R
[�&���g�<�s��YW~ �P�O�*��PAAkRC'��N*�]@۰�j	H7��3o�	n���#FB�{ԧ�4e,E�;�@*�[����.�h~}y�Nm�����k��%�$x�)|i��1}�T�T�ٲ8!�۳񫇎g{�8�)N��e
:�q��{���<Jsm'��!ݷͽ�5��;�_?x�
v7�ot�!!D�k�c#q՞��a�t�*�֨��ݦ��
�r����=��#�J��Tw�/X�N��	-����]�RZ�+`����v���C �]��/��^���1�0��_�/��˹n���Bz�9��,����3�aͬ�Y����ˆ���R߽�U��޳)�Ĺ=o@aσ����[cs�O�%�{�?+{P�.�7e��0)�6
��w
�5 }y��.�d$d�޻��j��hܗc�:4�t�TP�,"5�~��ٟ���y3���ק�B�B�$^�Ow1�J��6f��N6S��w�
��o���.��Z����݂���0�N9
���n��g���T,]���!�`?U��=�3:�䫛޴���]�W���c�Pg��1�@^��y�n�n�Az�/xz��0�Ʒ���]C��o�C�����R���ܽye�X�[E��R�6�R��_�ͳ߿Tg쬥�eH���$��8���۳B�vf��%�F�����_j�b}$�n0�a)*�Cu_�N6H[=�q�[3?��ѢOY�l=?6�O��+�>o��'�eÁ` ��B��w>��W�>�N����9.�-��Ȅd@x�B���^5�����e�o����r�!�%y�d���8��u�6c��Ѽ^�C����8���Ap@�@מb5���ln�
��q��Х���//W~���T.�Qu�԰"A���2o��� Y��U�H�>ݷt��_�c}8N�&u��dB@�љ+��5�mBf�w0���wCp���y��?g�<Q��l�c��o�Th�b�6�����I�PC�� G�W#ʄ���K9V����ӿpһV�	P#@�*����5/�d��8dK���d�e���g\�� �jo�������_���h���S��{�=[����eg^�z��s��8@�L�r��߈	��y�qʙߞ�2R�����k�:hp�J�@��ٳA�M�JY�m)l���Z�B�/��-������i�:�l��W����\���,aX��g������g�`W6��	�[�p�����.���J��V�����C��n�U��%�d;d���X	0��	�G\��1�!u���~6bF������0P�ⷯ�.����mDzR\��~�q�6��/Ŕ�7�B�O@�od�>��^�}�޹��@8�?�=�¹*�+$w���
���ň"�>���R�FI��@�"�f@ |����EP�k����j������~;7<�ڹ�k���{�0(���}�k̚��a�X�����H�g�;&�랬�q�ח��w��+̘��ŒM�,{�e���I��0�pæ�Y
ˡ������O$�b h���i��s�F�ҁ�DԂ n)�yi
]
)�=u\~�\��b��6�@I��E�O�A.S���Sf����}3L�?�}[��_��|���ٹ�-Y
T�"A �ʀ���A�$n�z�!'�h ���v{�VZҡ��F�gh��P� ��7��\�_9�_j(��AF��2jE|��J�2�,����v�4��C�eBF};1�!�jXk���7��r��S�����IՐI78VC�F,`���.���u�<w}��4��·�~��)�������wv��<��5�pأD �۞��<��w��rf l��;�v���W�]9��;�L�D���I������ㄸ��Ph�G���P��
�EhP4m�9�w�a\���A�+��>z�'RLmvR����{ ��"��/���~�u�uG�?�o�u=��	H���sm�Ju|��3�|�o��5����]yҢ��jk �۠�`�vQ�حU�l�9���w`��F���ז@H=�[ᇠ�����o�ࣱ�7���w���w����&��7�ː�Y�
(�" ɤ��d�|ԧ�p�_��)@q�~[���~���!|�$h���$@ULv�y�m|�a��7��-)�7}��siY�3W���A��v���Z�����=����H�B��H��~�F�A�#�kk��(�D�1/��8�o�v/w�BL����_��^�}{�W~Eb��K�`0θ�?b����g�Q������~E�Pwl�_z�WW>��
���>�B��UN���P36�!�N�xn��!��^ϕ�*:;���;�D�H�z���̾i)4��q+!d����=����O$~V?-���H��J��C����s��t
lCE�o��3}���
�a��wYm)	ʶ�E��w�=E�`�@�;���p^���)~3�6WȑMd�k�8�f���xp�B��'1A��D$X�қV�
@T5R�Q<�U��f�Ȏq���K�Q����4���+s���9�P
�M��w(��;)�^}�C|�uךbw[�^�J��5���uٰ#w�O��V�������4�_g�֧e�ּ���;�	���Ip�������O�} ���zYQW`zjUd"1�-�Т�2J� 	�"�a�ւ�}����x�e��kWy9r2Z��X]!��!�o������=(/|��\�'|/t�ߜo�v�;�B��
���-�:P����s�:���@U�>��M��ů��Q;�� -~�����%s���#�ͼ�֣	��6�K̪.2���tHm*@ A(�PDHk�cW>ŋ�^�ѢT(��E��_�ќ[��+�H{:ğz�-u0�.�����*��o��-�꿛�����V}����O +��bߊW��b��!�`���ȭ�� ��L�
=���|��U(����Hk��<��E�x£Z��Ȍ~��)x���g�xa��B3'tpŇ�@s��'s���>����	P�~�~�(ʽ����[�������U�p�/�NtL�ܱ�P�Op�W�����k�q��)�2�c�_����A�dE��Ȑ��صpM�cS�{ l�z\t[Q(�Nn�7_�Ħ���ץñ�:�:��~/� ���\��	dֳ��SU�#��̀8�C`�B��8DڳAn+��
�;,���P�8�{^\@@H���`?P!Ad#���)$��I��ՂN��_u���il�`P���6��w�\Mc�@b�����k8�o͋3���{��^��t���1�ų��>?籔,g�bF6Ȁ`T���ˎ��<����<xwl^L��`���a�����=y:�(ǯz����:��2�X9���FA0�W#�)~R��٦5'�p�c�?�=�
����7�o�"�}��8v�F�ڛ�n~��2.� �쾽�7�9�5�_?���.w�I�{N����� ����Z�I\����s����~:�#��۝�}����!G���lY�
��S�H��8�dXS������ݱm߿�(z
Yv�Tj���'�a��r7��S�z�����m��K�@GqA���8��’��m�~��s�iGj�p��>��
��Lử@�nF���u[{k���,߼��6���a�08,���*�#�$�ȹk���3�:[���Ic�7����p?�j��m~<���]�=����_z�A���+�u��<*�~��- N�ڻEo���[f�ۇ�����S�&Ʉ��OO��ҙ���w�|֮��G(~�7�vs緌�w�F༳���	����9_��{��
\�}���An+�0���
K`��؝�L$z�7�%�����-3���yI��{�6q�8�aN|�&���0`y�@8t�?��b
(I�}:a�m���1k����g����r�+`���Z��@�`P�(�r����e.z\��<��Ԫ�[\�S�72?!]a�fm�
G>���_@HS�#[����-97	�NH#�}x"l�%L�Oz��X���V����C�R�8��E{�`�r<‹D��]�;ƺ*�xx�dnݴ����Va,�(�2��v_�g��yS:�B�տ���v��`��[j�ɐR��nN�uα{m.�_x�ѥ	��,��v�,iQܮ��!�Q�ɖ�<�m:Oo��Ɩ	�K)F��"`ʤm�?�y^��Usv"��@��̏C��e�I�f��S��)�f(p��R�[y<��4:�}�!��<��km;�KZ�~m�t� p��g�{��$�����͝
lhϋ����l�Wm��?GO�n)�~��\?�:�����-̝�’����9�;�I� e�_$�m���oB���
R
f�P\����p�`d�.��]yFa�;,g���]"�����v�"ܮ�U?�M��	@0/��>�PR$��"��M�AP�BB��"�H���qV辉�xs$~_��ϕ_����5��q�}�������~�~k�,o������%q�a�n�'Q�K�:+Cϋ!�ڥ�]q���1WB�*��o��	@H4�\=�h�j��w��Ǐ�J{H�(7L�t߲;�d89~���ּ1f��\!s�L��9�!�>'�9A�o���w�	��H}�7�BP�U���,� �	y8g#��wW�3k��mO`X���-?gUP�3���] [`@0���]7�0�m�F_Ñ|�w�
B`��)z7e=�>!Z�(_��R�>PU�g!�������: ����uk��MB��z��KwFh�
23Mt��\���J�C��r}��?����X��"A��P/�F�_U�����D����Z3�ά�&�}�S�S�%?������\!�%����:�$z���rD%t;V�|�H,xǹw�\�q�q�W�O��g ];�2���q�Ч7j�ܷ��V5��L�&���!A<@B�Ió��؜7���'A��A:�uo�	
�Ϣ��t!�Ym���B*��?.�zͦ�@���@����Y�S�������5bi���?`�$��M8�@@r�G�����A?��onn�vq��JP�0���!e@պg�綀�����g|w����v�*5��k�d8]����P�h�X@�AQ[)(CY�9w��U>i��[`0Z[l������c�F����Z��XQ��D��W���F��O!�e��/����W]hGm�@�B5�Xp�q��W������B�1׷��̠ص���τ{�^��b7�F���������MJ���޴�ڣ�o�e�"��64�>�׫����j�^�+�5Oe>��oq�L��#ӕ��82C.���܈�_�d��90`p�0:�>�ZW}=.���rd&�w���ڗ����4�^
��
�������x!��
�Xg��x��p\�����@n�G�4�5���ulo�4�8��|#��
=C����п�
併�]}�o��{=&Us��)_XvU
j���z���֘�[e�2\�D0���5v���?�$1o� 0����D���>�nR�c�oV��ԕm׮�^
�;�>�0u�E�Wee8�� ��c�9!=���x��`��A�8� �M��4��pc�GC�	�9Nl�ZHAY��C=�{��?2y��և�DZ���tzy��)�hK`�Az�0O�:\!��������Nw&���q�.W�X�M(���V��Aɳ]�C��@�~b⺋�P���g���O�/:����%p�D�h���(�@WF;r�	�F`���	���(+7e)������o<>�������/@��8���Ú�x"���jسB&F�!H�pϹ^�>�V���Y��cc�Wn
�OiA��z.@t~���FC��=��Ư�/}�*�O������a�j02���Z`p�P9�E�
�9;��i,0X��w�����ty�qA����;��m��
�W�����_���*�q㨴����d��Ǧ�!p��&�;,���]�O�)zC��.z�5� =�OA���c�֒w��+�]�_���_
�>.4~s`ɫWZ�����0�A9
�H �}-���?"I��1'�9g�k�����]�#=���(-1��R��Ꭺ����˗���|c`����}���.c�D�_�1�9]w����E�l�kh0��@Э�Ό�!p��������#S�����0ܡ���vpX���,gM�V ꛻ú�@��;�)�����y���K����>�����^Tݫ�9���+��z�e���M��s�4\Z�}؉a��"l�{Z�e<j(�V�
�q �
�Ԧ���v}�����o����E��X�]1@	v��7��i=�lOT�?
���uiʲז'��J�Y�É\!㜐��yt��"a��N}�6��{�����ml��w���Eo���M����߅_�������� �4κ�4��B��+d�@�k\{�E��p�����5�8�6*�.
({�i��;_���w�c{�6�M���}Xv��s������f
�-�lPE"���B�k�����L��u�1\ȑ�/z��M���U��nn��OLz�H�y�+q��:;��^��U��S2���p!��Af�p��1���d|L8Hr��0(����s�>%��͖������`@?�� :MZ����%�
��F�e�
2�����/Q�Ӥ���f��.�d�Op�ќ7 p��\�P@I�~��`k��lM��mOLz����ܝ}�h�mV����^:Cxo�D ����p�fQȆE�-nk�t������ȳ}��s���r�C���S���>������V��)�}����%�tiƑ�L���)Qό���(��=Ej�#�r_wL��e��*E�U?_ʰi�\��Î���7v?{	�7��>�(�-V���
�^2�U�d�	�C.��
-Vk��»���c���.�R�]��dž�sh��e�o�o[wg���p�0�.�T�	�r���LN�s���!�<&a�H�b��
p��.��_�v��1
�W�{Y6��m�)���x���e��j��'��>Y�P;Ke��N�t�5���>!�3CĮT��pc�ew���_��|b���|���/�t���^
�'$3�w�m(�fk�j�M
��s� �8���DU��Z�"9ċ�<-� r}��p���	����tv�(A����.P�)�M�om���F���'%�:X8eK����!�;�K����b�������+-�&)B�y!�Kd�d�5�`?1&��B��ޢ��6���6�C"c����sf6u0g\)�\����
�>���"�>�>kPԮQ�IT���kS2�����*,����p�������J���`}{�gڪ))-tOzԦ㪊L�.2>�gbM�T	��R��ǻjκdsӣ����wZ��j�U5��G�ۧ
y�/�����B�ذqk`�v�L�����hm����'�����@�K�x����N�Ԥ���~�2�x���ڝ������$k��z껨�L�]�A���
��熜�R7��=���>ͱq:x-7��?p������؛AHU@�J�����'�s���-]G%����=N`�زB����������򩟾iK�����4�5��>�J�!!�FT^4z�L��,�Q�=S�zl�z�R�T����g�
�x�W@��R�/������z�L�g�1�d!��Q5�"�;i��'�J�B2�G�̸��#0A��5�0�����ر��]��_���j��qie�u ��#
C�E�z��2}�L)N�	5W8,�v��VACQ=�A��G�P0�$
��%(���+/��pcW({�~-|����5��ُz�K�J��񂥍R�I�0'���$%�[;��M��\��] ��@[(�|������܍��gZ���Rr���`�12�f!�>�gV�/��h�jYJ�
R(�'[m �g��.�q�Ȅ��v__	U��;���'��z����R4�[���/�T�#��Tj�:)�M��o�j~�T�IB��!i�T�m0WTG��;B�~[(�y��?����g�2rk����퇿��`�@ĠH�,�?G���Sr�rɌ�RM��jB5�J��iT�/TMJ�Z)T�GU�x �P
�* P�/D
���,*�QDt(�5�h���֗�?��[�,�=@)����V~�D��}@P��4j����X50��j�}��0�@ح��9�@WB���>��`��=�.�1���+��W��}�+���#���laIEND�B`�PK
!<E}�����1chrome/browser/content/branding/[email protected]�PNG


IHDR���ǵ��nIDATx��s�{��筪�Nl��ڶm۶m۶w�v�'ΨQ�U��>���v��$3ſߧ�=��MID��MDDqw�x����i("�>�k���ܛ��("
���wZ� �>QD$�=��/�5�x����"
��t]�'���t��M,��ĕ_�\�C�zNl�t6�e
0�"��wo½�("�����U�^Fkѹ��~s�Q��J�D�}ܟ��
� "]H�qoX�L���@�Ν���76QD��{�;Fy�|�ѿ���'B��/u�A�ʷ�.@��ܿ������d�������&\���;@D:�G>�d�hi�qL����H�-y.��H������?�wo �l0H?�;�j :C)���H
v�� "������x��' �
��<.���j��}�眩K�� "���c�����q|RK���r� ��﷐w 
��t�/|m���'K!f� ��V\d�	D�P��mYz�[�("��~C���~�Gf�?��
� �����҈("���O����ГKR�y�SZ�jp�I�y6��H~�G��$��c!�������
��`����H�ӲÛ��%ډ�Q,}�>\Ua?��X`!�@D:؋ߍU{��D�Y_Hߛk� ��z�#pQD������~�?�����5s͐��B�?�����H���Ru�^�b���D���	>l!o��� ��U����s9o�rC
� ��$��=5�~D�@D��{f�Ҧst'��9���@�8	���Y�ځ("���W�W�a���A�!@��� ��J/�끅rQD���/m�9eY�����O�,dk�]$�>`���F�6�g���g$?�)���-����,�%
�����O��5��g�O+?�IQ6�<d�	~<Y�4@DW�����7�=�R���ZFٹ3�]s�|�B^Je� �����J=�w_�iJ	��?�
��#�(��=�nDiSom�`�F�}0��;^��u��b��ⷞ��E�,������[��8��\=��4T����F���$}o�����Y�@�l^���q0
�l�̆� ^��^���e��/��C3�Nb�8	�8������ޓ���Tu�>���Qaq�yJ��gX�@亼� ��M�?L�l030(��z������6EC��B/��u���]������E�����c�sR��7H⏀`6���@x�����DyٙA�-˜��l�9�
*�E�x��1��W��w	��c+�	s���<��˯���Um �I���0ڌ;�5�Z�"���>�{�с)"/��0f`��%�0Yj�/Kǀ��O���tG$�s
X��
W�].9�`���$K?�m�5�h��&�RW����_�r�("ϻk*f�X��x�p��0�+r��|uo�!��s�g��'G���g����'p-�[�,%�`!�)3�����Q��!
���g���c� <�ِ�C���:\DZ�K�$� a�%e��E4�D�g�D�PHͣ� ��;)�����L�G���-g�^꾉(Ҷ�g���{�3{fK1�e��x�%I�r\�����+~��.xW�4��A~7V4w}4��^�]#0������O�\�C���S�Lr�=��=ޙ7Ù�̻�@7�I:�k`�u����:>/+}GP��30q��%��]�C~�9�w!>T�n�s�� �i<��9��ّ���;Z�c.���PrW���V��,���b���l�X�0����q��u�4���2�23I�%���jyw���m�U���Q�ճ����鞪�q�����{���%�hQ/�"�Т�D����ób$ |Mj�\{kEj�ZiZ1q5�CA�E��o�o��}���@��C�
�����ù�oVVĊ�S�>����/r�G��O�k��hQ�֢N���� %�o��CN���3~(�b������/S<�����Cg�p?�
	@ ��zͧ���E ����6�k�ѧJ4N9S��D4>��#6�������1�"�iQo,�S�����#V�O�����f���C5�}��0y�6�~X2y{�@x�}��ǵ��0�@���}���8Ĩ�S"�4"-����뎤��Ћ�E�F��Y��WR�
���= ����H�Z�-�6��H���G�C�p�g&�:����C��	��5oH��zS�Q=�k�g.��@XǮ)�/s�_H ���>?�Q�`���4|��hQ�O����l�4L���Ǯ�31ѷo��"�̸���c�f��?�(�g�:�-!�׀���c������/�L�/h9'�j�7��`۔�S����t�ǃ�lTo�`���-�|�\�E����m�{ѷ��V�X'A�+�_k֟���c6r�H���¯�v�i����`���bh��yuU��o~��?M[r�͕\@�^R��� �B��D�!�(�6�>�?�`��^-2���I�W%����L��+�<��Q�OɐL7���_��w�� �l4���g�|�u�����*��&*Q@1��9ԹP�oU`� ���G���cϽ�Z��I��F�L�%�%��}��E_|��J؍롌��ҫ�W
������������A T%�
\(cX�i�|�G�a�
6nl��7�m]]T�3�?n��:��9JD�f��Z�h>�|�7�0��5Ոz�y�5�B���G�Op�[-��)@i���̻��S��$H-�6O���	,!
�V��F�m�{����(�>�!�%��T�s��c��r��I�4*4�g���������	��]T�O������J�/%�I�?�5��8�6�@˲�׶q�pf�;o@	��%T.�Yg���g׼6���#�=::jN��-T��g��戀9�'��:�&�?$LZ�W��N�����O�)-�W?��=,��Ң������mC3V��c��X�{8���P��{��ɫݹg�R���G��F�O�R^@%*xb�9u#�p�����2PD����iP�(����i�OjQ�Â)�&�J6�$A�?��?��O@`\���x߸�����H�j\/��e���$x��|A��I@&#`
�������e�_T����I+G��}�#�K��|Ę�#�5�A]A�m>�8�����Z�KZ�2�.k�<��O�����>xn_p�M��H�2l���6(<�I�yC�� ���=U�M}%!|�`פU���J�yQ¯�+4�0�AMﯥ��N��-��Z�U��nA'�+l���E��V��y�]6�au͖8���m�r�n
�k�u����_��k�đ	�m�� �>!���g�h�
����\���>{���?`Ʋߥ������j�:S�)��	�r���0�_$?��=\��Ϛ�M��:�H���C�A�w�&�S୬�\�S�e.�G6M:{�+���_B�C����跍mP��/���+���k����~B�^���
�8I�[���E�D��9�P�a�'��P��p��<Va��<�!����Tb��,��R_�
Q�V�FB�����v �
ՠV�5f���QZ�ZԫL������1��[��$*�TIXX>}�c˿"D.�ק���!V!iH�7x��kw^`��<-�w��?����?]���S�f��A�'%�?��Zh�;_5n���v�/��k�-22Z�Cm"M��*�g)��O[���	y�a��������qaR���<1t�Ʈn���=mYg^�jo7����˝�8m�V�|Z�
�ׁn֏�^�x�����/Ћ1礟O�"�Ѣ��"���"��"�b|W��!�?�P� �x�T_���^��Qu�X��Az�!�hc������`_"z��ͫ��J�`��oR����?��G������	?�1����xU����f��N��h�J�=믏y�6K $�N���v'���A��_
9(����za�&��o�K4y�;��v��@�_�I�3h���`�ij��ҷ:�����^
J
P�@�uOe�F���ݔ1�/�u���F�+��c&u���/M�(��q�@�E���+0"�Ğ��b`�Ħ��
�����F���~T`�_�����ԛ�o��@ �C+90��@A[��7{����E��r)"�
{��=��`?&���=~8~�O� ���\8�o������`�y`���<��PV ��,�jWT�̄��U��V-�u�(|�
߬�1�	�����G���$G/��V�|U�z����֛���K@k���3OѪ��TaNO��drjժ�*�e�?��8���@�D�zL�@���ON����C�G��;�s��>�[A��rjz��\WB��}�r>=�{C�|ڃ>$��_�P�x�ǭ���cN��8-�S-2Ǭ�?T��e�b^f������^s7��3��V��öW��2�pB�#Lw72�m0w� �@n!k���Kx�0�@�
 {$�"@C!��罉˧�����P�7C7@�~X��V�� �'�i�2��BW��WWo�DML����?��Cy���@
�����X�aƱ��Zԗ�H&j�V��@y� _����+��e��c�j�'��(��q���>�X����"��3
�
�ĸ���ۯ��x>xy��8u�}X��@jUE�á[������|������`c]�پJ�L��)�f;�tw{�*()�濫�����:��Lc��
�"�9�J���:�M`�S{x���y�1=�	�R�-��?��^��o���2V�����9q�Gl�:�RgU���	����vh�~�E�3���Q��TW��8�M��AB82�A��AW�"
� zg;��Y��܋}p±ߛ�E�R������=���3��>u��ˬ���UC~JZ��Eߢ}M��|����f�y��/���M�?��u걌�ۃ}��;�+�oB�[[��V�=��R�
����*��G�Dt�1b���Ox5���H� p�������Rk�?��ȍ���oL��@�s�+��|��buZi�C@���u�ߨOsI�Ѡt"��@���s���7��H����'�$-!��rY�ɶ3gS[�!E�B@*���A���b�A�V���)��̨`S]��s�������*��-��ઍ�Bn	�)�p٤��%bтoK����qV���#�8�ɩ�||�@ຽWq����!�B�	>b/�6�ϟ:��
;��|(�  *�=e��Y��!����Z9�L*�!E��LE��6��^h2� �+�T��o�`��? ���ݝW�$z `�0�.Հ�}��O
��x����~��o�;ʽA�\l��d��H��~�)�U*�F|���o�w6���ځ�
�Nט�o�g�p������+�(c�(D�R��w��
R<
����YSq����"�%��������9��*x�τ!*��C	�X=
�0�4�����@�����j���/>�X4��D��5�x-B�o��8hi�C�`�{9*�q�������DV��3�b�M<���}4�#(p�M`��dAP(����9�{V���jfT����`k�"v����I��
��se�d��a+(<���K�;3��p �@J��Oy��+	bɼ�-��^���,� ��Q���;��h>4�|p�7Q⅄?
�5���;����H�?t����
Ǵ������~O�>w���T��*<�{���*���˙7�4���b�<WW������1y%�s�o��?t�ց����;���O���s��ZĹU��	DDLaO1�~���qڸe�լ������K|q�;x����5�=~��z��P~Q��`$��><8s�?ѷ� '��	ڋ�n�E���
ޝvSo�T�ʣ��h�7m� �����j��?�W�w�L����ɯԢ"dLH�����zC���G�zק�j�[���(c���>@�+��ϛ�!���	�?��L��@W�J��˹�%����`]mf���n��HH�P���+M�q�{³~㻯�}<׳���vG4ÜN\�W9�S���dq������J-M��WpR�c�+L埓_����tB���@�����Mnny	@q�N��e`} D����i�?��.t��nm�l�J 81�x:*��φ���tJ���}�
2|�:�*��	�?T�C �:���_�6ݳ�5G�k}4U`���YnG���0���mf�Ӂ��3s6�6h�i���H[�l�G�7�u�	��'��:�N�9,�O�_��}GsZ8��AC�
ᷘ�ۮ�|�
���|�@ �N
T�} [@ZC.�B�����mr����BP&���5�ۨ@�����K�z�
_~��Bh�:�tt8�G����|�SYjS]4;ULt�P�!B�K�1�|�l�b]a"��g�@nks�y&?_+�;�:�=�^{?�TQ�t��+��.�m�1�@��6^6��uB�`?6~��C>�s��*�C��Y ��DE�ͮ.��u�g?����E�>�_TpK�[�حޓC�� ,�D�
�>��h���K�?�r����!�jܜ��ǀ�][�|u��u,�u.��]�y�Qڝ���XT���L?����}���_�v��f/7��%@��A>n�c�,�S]1'��mT�ߌ��
۳�ͪ F�D��P@?���̪ͬ��!�v�g�`�>�9�ߎ 1L�{M��eh��:�Q��8B<�f���7Ɣ������ر��ssA�j'˟&���k�*%��}r��kv\������ ����;�m�Vf�¯lP�|!�ƞ��0���Cg68s����G�"��Y0��x�s���+*XWWs]��? h0#�/��J�]�=���;�xOu0���u<Cm����H�3�`YW��o��䄪�XY��`�)�o_�c~���m���
E�����N�B�=+T��Ӗ2�~+�j4�K+����wC����	Ꭾ��_��?Q�z���YRgB��7A�CU�h#I��p�2R`T���n�"q���c?�/�`�k�=��w�2��K
�L��1��u�y���+�P����s8n�S؆q*�@���>���[����s�~/G��~8O�����.��5P��M������K��1nC��Rn��-�U�$��g�[�9a�m�Fy/p��U\��h�����;<ᵜ?�_Ċ� C+��$~�_{���!�i��=F��>�
����iU�!�26z��~�	�b|mV�P-y�8Q�bo%�I[=�[����(������
���A���C������;�w���Y�̊��-���@>�6���<9�R@�'�ӫ[�����X>Y@��m�|���n;#T'A؋��ޗcힷ��E�����|��|_ۇ��߉�4 �WB��,x^��e��-�	3prE�cDC�^h�~|A���
����x�N}P�|��{���o��(4���}�=,V�l\cYE���<��?�v.����k��������,�����@��!���i�/H�(�?���y��{:�>{9O��9����
����];��@�#��yPh��>�g��FF<3i���_~�'hO �`YV6P���'�g�"���	%}�}��;��+!��7$�J�9��P�*��D~O����н�pe��(Δ�h�$*p��{�K��a{���s#�RxLrZ���}q��[E��w�Yd��뺝W��0�v�
R)��l���n��W���硐3��r�|��m�
5	����ѴZ�~o�k����8��7e�	��Y��Y�3��b�ۆ+�y*���)�}c��n���&6�'1嗢��� b�� 髊%��6��	��2�
�9(䍒dx���Y8�q�E
R�S����y�i���bWS�;g��B����3Y�l/���M����I�Nv����?o�^P�������<z�:|����MF9�8��l���o򥠽�|��c�>��f�H�pO[~]xx@�.zv�
_�y��<V�:���;�G<�;�7|
Q9��m�  ��b"�+��GZ��u�?�9���ga�9�2 ��){)�1��y/������9�Jb����S�UC�vPx��:>s��8���k�_�!����ksw/b��X�3�ma	��嵊�o��/����_p[۩�N��'J�����`�1����h�تv������O%]��o�n�tz ��O��*z/*�����.��E^G�����n��C�gl0N���y���cm��V	�5mK9��_�����J[���L����>�AGg�]g���Fb�s4��?)�7��M�C�g���|�z%*p��� ��*L�	y�䙙j��#�8t�?y�ﳪ��'`�Y���g���n*
�1��S
�wK��䭢�*��=�﷋�d�Ŀ;����?�����q���	�r�5*P�����9��3y��(��)N;�|I�h+��^��U�?�u���y��?"��$~S%D��%�T"��Z=��At����V/��;n]�N���?!�7�x�Z�@���1g��x�PzW]�
Ȣ���O��w��y-���T��L \���|��e b��ٿ2E?��2$�)��ߢ�ླth7p��v�/X���2�<����h��>�y5��8+e�
��k���9�ïvd��s|rQ�_���"I`�Z�O�7�I���	�r �.-���@���3~+(��1>��B.Pϯ\ƴ�-V�����<I
���m�����?�y���F����L
�x��wzg~�Ћ�D�>����-�,^��&b�XY@_��z޹���u������`���3~,�6��B������Y>0�V���,��g��>-
 �'���)[T��{퐭
$�+>��vj���@��?�B��z��k��Hj����>��o	�ӗ�9�O&}`Y<Gi���3rܸ�.�����B�e È��c<�g��.���B痫,����ߣе)��S�<S��m����)L_�d�ɺ��Q�߰�	���ǵH���{?�5�}��ZC{�6��泖2�v���hpN�>*�~���Q��iY��OY���_j�3���ԈgZ>	��1�>y�$;֮h{����bޙ}�����2�=f�{堉�}[g;�`��Op����/�9dfT�����>
-;@x��g�]|ik����ֶ�u:ܺ�3�$�-D��*��_�Ճ96��Ó#!�G�21�����k�~&�0�Rf��a��B>�﹎�4��EH�;T=8�B��/',��r7d�xZ����G�Q�S��!�?������_b�y�����U��ݔ�����S#z�N���v^�C��<��~�r���@���H�tt��,_��a0#���i�*�_�xo'4>�-���L8=��a<]����ZG�f��
�#m��' @⎵8�gg-l�
~���i��Y���O���6P���X�]%~w�@p����_��RS�Z�*�~��o�zZv�ky�s��\	��
U�?�\	U�(��&�#���,��*�q��?���T�ݩ���+�?�jP���<�{6Pi(؁�f�����|�Mٹz.7{��j"8�i�_�g̱:{�B����=j����
����=��J�9
�g��}�.sb8$�v_��aP�s_�i�b%��A�~�gKXA�tv�c{��Œ�r��?sH!���]�~3
[��I�Oʱ|����Ήg}QD_���C��H�7�W�ܗ�O���%�
�٩�3���4�GG�
do	����CI�`�m-�aK�)5��t�)��Ju�a�~&����v�5�wd�ٵ'y�*�['�s�+��UH�U�!�;���}5��7�ٕ�n��5=�o^rfqb�/)(��_q�K��~P�7s��f5P�>�<��T2�j
8�$j�T����o��{@���\U�!Ʀ	��Έ���?���lw�7��CɁ��_���TQ �/ggw݀'Ng� ��)��~�߼�
��D�Y>�n�u�˓��u�����~"�,]�iSWG�3�v��*���Ɲ[3N�V�?:,�aHD�0(�`����n�Ƿ~��G�ѠqU:��汲�FZ�� 1�~����~sl{+�~Ql=�D&��J�:�Ӂt��7C'		v�
��Wd!WU`Y򹭶�k��v%��cQd}�H[�z��?>2�
	3[�1�"�n/�X>zc`��ٮ}��1AI�O�~�I,�hp�T����ߌ��xWG�jP�������">���谎���m��0ǖ�o��
C@(��MS�_�+yg����I��F��*�����/��1
���߀�~�BFU�M�/֙��H�5�?��~�h��,.�
��q�"�1=A����E��6G@'?��{`$Y�oak-l��]�ȴ��QV���s"�Ⱦ�?����D��Wv"/�p������0�\9�\���	�%���;7n�kl�8����ՑU3k�y�*(b�l����+���E?$�l+��az��
�-���>���L4"�_���>�iW�lp'���l`
��Y�M���Q<W�Zz�`�3�~@+(����.	EGC��E��ޓ�_�T��H���%�f�<-;>#'E�
6����+�R�{JZ?A[���������<6��(�P��	��
|%t:Z�Z��Pv2��z�f�x~d�cka��*H`PZ�㋽(>���Y	��O�7��?$2R'RI�&%���]�umKs�L�"�;��y��:��S��t��n>f��@�T�����i���F���0s0�y|�>ǀ��)(�&U�F�ɟ̣���i_ƿ�� ����w�o<S��^h}�8M�����M�;��o-�?Ƹ���_���T������j�\��+�öOH��&^T~��ncVpoaI%�+�?�Ǘn���s�	��y�H���
��υ�@�wd6z����n�{k�O���@�@"V]�qaP�h�c�������
��{���iؾ��.�uT<>�Q���V�G���0+�m��y���}�ϱ.u�ё�����Kd{�	��Խ�J�8h�j��]̵�>�ݭK�� ���X�����������Y�Nso�2�ZjT��/�ʠ}��
ow��o�0g��a�;�!���_�?�2���J�����ai�=Z�E.�����+	��缧~ˎ�8c���� z�W��:A���12����R�'�P�}t��Ö{^������T8�`�����Ӣ�|	���=a�?�O�4!�_�ø�!�78��9�;ǰ|�@�lC�%��p��9F�\#C����4��1ǒ����������~>��l�2�d��#��/��}ގ���g�_�x.3���+Bb�н��_?�!p} ��z���
�����M`*E���82®�����L��!��g˧��w�C���}���16N�^�Ԛ�$R�]$�#��7=q�7�_�w��w�R�o�G��z�)���[�6����ʟ>*X@F;���w��zP��Pb��K���"���wJ �|��J�ܗ̈́p��_���h�\6�_�a�x�sPh
�%�[���h�F~뺅��#_q�s��z�}t���
ѷ8�ܯ�,��Ua�]�[�L�GB�����`���������o&�� �&,��-͠��	����s�]��j5R�������Ӽ�>�����ė��U����Ԩ׸�P�g���'{�g�1g���@�`Km7�� ��_��A��w���c���K6���5\n�
���- �{>�;���֭^�Q��(K���Z8�p�ߌ�-~���O����y��W#/M��S�?�����1��.���P�j/��oO
��KA
��c_	��ᕂa����l�O4���H`�oB���
�\��� �X����OǕB/@@S��������qV��^Ho��J�yyJ�����5��k�ݗ����C@�G���@	(�z�}I��c�!re�U.U�6�~}�)��C�b5��M�} z�+����^]��j5"���P���>f����8|�UM�A�����9W�wD���b��&0�c��^�m�E���C��/Z
*%JAm� �D��h�,�`����6���C�����f�/�(�|�U�|��H6�
L?����A�����[�Ћ�U]?t0g�O�c_��	D�\�M`�$)l_Z� �C��e��@J$uK����{��m��qH��U@B����V�������j6\r�k6�C!��[I����6=!˧c�@��~�z��y��Hh��Al k���f�I������������0L+��
 ��7�`S�o�x�	�9&�OiF�y�Fw*�)����!`1�/Z����ߦ7<���K�R�t�P����� I�)�;��+�B'�O>2�����As���3�W�?+��J�6C�Cm���ߛ�����XB���Rb��p�n�w�bV��S�����F��j�h�>4�1E2�*`�Ǹ�܋q�)[HM�$5���{�}�=�s�Q�6V�O9��?�@��c���`�&0�v�RS�v�+�	L��0D�T%�D��8���o ?��{�?X�6ꆃr�! ���K����h^>��|�Ք5$ݝVC��-{�'i+�h�h����/�n�7�`���j����W=b����6o���A�w��pI�@C��_�8�@��J��$�b9�T��{)oz�3��!�	BJ��A���J�4?�K|䘯Q����5���G5j�~oӳ�sW���
�x\M��+��J|�a�&0C�@�}�c�)O
����Wcgo��_y1v���p��l�QL��N<H��zH���$`��{�E��Z �V�
7���}%�e���lp�T�}k�7�����9��
C�oW�G�B6)�B"o�@�
��
��]��(�C�t����2l��K�7�
�@�K�M�uYp�y�#x�p�2Հ$������ϖ�K��W�r�ݔ=�)� @,g���	/�yn��K��<�&}��:����{P�|�(����{s��F��c�W��!�q��>�{߁K��:�۹
~�n$(��� `c����[�@khn��������‹X4��d��u)�p��o#�6��W���:�N}o���6�M��)�����(�9@ү�F=?�l~Y����m����<)�;›7~��my)<릠fhH��@R�(�f��>���3��?��c��Ǽ���=��ǃT
\�~6o)�}�M����c���T��s �����X
�	,��-Tz�v'���@`��-P��v�6`�<%�y������[C�n@�0�[����mA���('�3��(�;La�t�~�&vc@AkZ7�3d�s�t�Ge�/�/9@���x�ŘAs���0�X���}�/�w8h�P����u_�O
g���[�k�"�JU�|�At�W��EE,�r<}�*��l9�
h3��;����ݿP�&��7W���
�Ǐ�}�_�,�|b���f"��?r<0*T�S�Tb�~��wK�I`�u�$�z�{l���O�ppC�k�/M�2���_�r�����ͬڍ�J8���p��ߧ�~�6P��SNV;�� S?���T��_��$O��`���X
�[ <2:\C��\@1`w(�Y
j��|?pU|o�%\��}��B�d� Sm�||`���޿
�����~��_q.s�6�HhJ��m	Iz��6��C�Ѿ���=�[�$�,j����#�a���(h��~Y�l^
i_
Z���b��o�j�pK�y|�w���G�{*
5Cz�,!`Y"j<k����9�l�h;��
��{/��E��WB2A^`������tc��ժ�/�X���V-z��O<�&;
z����f���$�DYA�>���^+x^��K�_�wv^A�T�p]����! 8�ٿEh)�^PT �r�xѫX9�.],/0܉���7�m��m�t�������W�+17�K$��@�`@�N<9�3;�Km�{�D�O)�����mo���'��Ę�(���G��N1,� ����@>W�ƕ;��L;�!=P1������P���3�a�z���G�Ī�%�U�?�(�����'��B�8�x;�E"�K��Cv)��X�5�@11!:T�;����خ��
2��u/d;���?�|�d��2ǀd�߸U~�b��Y��߻�g��(��g\�����@ �}����c?,@��H�rAY���P��^�����G���6�&,�&Bb�!]
�u��Y?�!`���@4(b�����7�t�tJh����!D�D�"$�F��5���r�����r�?��gX}	?��Y�c�1�k�� h/#A�95���}_�|���+�L=�2�gH��P ����7#�	���}�Ū�w�s^G�B/�R�!5u���+�����髫�
�O�›:b��۔��@�Ɋ�\���b�篃�HH�q�;�Z�������y:5���z#�س2$����_�IA��Ձe<{H�	��,@`��h/��
���E�nְ��{��sZ�z�pFAz.�j���A�MM���Ҁ�=�QoN��V ��{�K�-ÅS�U�@@����|�6�^��`�p�!w�&��x*H����B�!���_���@�6lWF��j���v
ZC[t�Q,�R���E�+�IR
��A����߷�׾�i9c�~	��ϛ�G�偰���ޥ���#�S�-���?Xx�ূ5�����o��͋���6�-�隠?4�� ��j�~�_��践�|����w��)$������I8�[F��
�@�r9yX���~
�_z�GM�������!���]�O�@һl+06�?Ȁ���I�k��W����E;B�{�>��"_6�[B���@��y�b��~���/*H_[|-�#T' �Ϻ� =�A��ۯ�ZGׯ�
�_��0�ʴ��*�A��/����MZ�x�nP�����w�0�@ٜ�5{�¯����>t
 `
���@�k"<��[�Ϙo�6q� �{�b����B�Β��i�2��
��{�52�AR�?P�}����o�{��r�k�L�gma���U��t���kX%U��0X��Gf���Y
�A0@���3]���m/�z@�2�?���R��
�$Z��A��
��6�,@`�*Hj�o>���MEEjn�s<1�\�&% ��H��2��6���-;���-n-�>w�?]o��j�kncҧF��,Q�2Ё�̢�!c_��?�X^��vFB|	~HUV�c�{oA�������L޺�D��B����-��`n�;�7V
�G�;�^����o����r���
�ܞ���*�����Z�Y�l��w�i���V6�%�T�3CAA�dx�^�@ �(�	�$��F��@��Ї�I��$���UA�g��_����mEL#h�[Ȱ��z��	=y����[�����>�~�ا�L���_��_�%�o��<����+�{��	l-֕����Q�Mmøh�G�?;�3�U�C�Z��L�I�TH|c���e���� 0�����_���F�h):����+����k�I����<7��_�ݼ���꬜vU������ǐ�)�wި~]5�ޭ�~�Nx�	������
��@�A��3\��/qG�с��F�~�7@���߭2rʨ�������-��`ڇ=[������x�o�
4�NF& ��c�*��\߲]�7=�VL����&���_vH�+�g��T�d�������4�f�c��0�����7�C�u�H���T5Z��X�9���'�M����`P�m��Z�����6^p:U�e��3��N�q�w;��y�^���BgV���)W@��?w�\�^q��e�_����I�~~���>x@�f�c�`Ő�+!�?�}��}��TH�'���Ў�)��B�A�j/���7 �Mb&̾��54n�B��p^{�W���£��i�l@��J��p�
H�l�n��`�z��j��������Y)|`E��r�?��5��b ��$�F��{�s�/����r͵���X�cQ����I�@�����>��}"�f5`�u��$�˧�Zv��t�:���6��PǨ��at��i�E���}�\۞�2gM�u��?�G/�nzK깅�I�N}�=Y�C�H����)��_�? �~n�p6o��}s���G��2r
D�!`���)މ�◅��@�Sڰ�BQQ|����3>ZB���B���ys�;2���~2��s�4��3�}�aM���j�ן�~��̛6�(qR�����@
&�|@�{��:��޺](˧�k(3��@�	�UU�V%,T�<@lP�=F@�WT��P�*:[���K^��]�14��
�0+�ArxX��O�P�PT���n�ֽ���������߿6���R��;���Y+|��C(���@�{��׳x÷���������BM����P�㚖OI���0�?�>`ў�i��/��Bٰ	|�b��U_��}6w����ŕ����t��KT)_smr��z#�7�m��1C`���c?���ެߦD���Օ������J!��@"g��sX�sά�^�l�0^/8l2��9��P�3���]��_���)]��f���9}��W5���᝙�
��%���4�-긆>(�����7l(��/�r#��})Ib�b��\GG	��HV�7� p"���}��Ķ���������
9� `� z�p8��Q�W�����7&xCN7�����{��d���}܎�j�����A�'1/�u�'��S,��b�uM�zL��#�ӄK@�Ⓚ�^����1����3	�Eup��H4��孇_U�Z�9�@�c�4{L��%�­�_�ݺe��q�8pŐ�5��m���g�
��nj6�h�_�
���h!��1B1݆l���b=!�3��/h?���6c2yi�	�5��z
"��5�$֏��Zm�L����u����1���<�n�H�mb}_�� �ro�;/�uɐ�x��Q���&c��Da��g��>N�+`�k�:���$�����ɝ��c��&�l�y���8@�$� ,l-p�(� ��cb�@��?%t �{��∉O�C�S���~ј3H�4�w�vd�HWmt�_�9s���@��x���I93��ʐ�=���ΘƙQ�/�AH�G��-!d08��ZO{Uf� C*�/���ܾ�p0�8M#���F�c@�y�`p��I���|�o�5`�*�|�?����_�:p���D��<��ɐ���̣����=�rΜy�۟��
�ۮ6g�z`0+��A��^�8`�k��ih��h�	$e�d�N`��3(��Z+�����l�M=cp
�4�^�� ��b1�ԏ��M����MR�`&a;7@�8`0uts'>w��`���-<F�Bn�^���$�w����{��&	�^8���Lh~�y����赵���1Σ���Khc���_�HU�	@�c��wi
y8�j�/����S���7?��~�|"$ ����D? 1�$-rA�
�	������7}�W�7����U��a�yr=h��MN��s�bٜ:�?�x�l�AØ���	�N�T>����L���a|R���?Aq�N�ZO~(j�S.�U/ӯ��M#
� !h�IU���ėzL\h�`k-��w#�����𳂪>��H�$�4'������<s���3MS����0,Lm�޼e�Y"@�:8wT�i��w:F��Ľ���A��¤0���<��@N�⿂}�p���H�?�u1�h�@|'������~��%��m-�ʑ����wM�@<�)��
|$�J &`�똌_'�r}_Os��,�5��ď��`�Z�0o�~� �t	��e��` ���?Cq*X�A0��BuϡR�R�f�U]���q�
ր�E��p2
2�n�A�P#�\#�Rø��<qgp?Ӈm��8��{_���A`��øY)�HWX��ܧ�ր��\����C�%T1��AoGd��?���r�M��@@�@�}p#�<�M��'�Uk�Ϙ�L}aε�9�n�l^?-H��T��|�7��1�L$�'p.��&�tn�&�k�COO�}J�,�jc�1�C���?����}��Tg��9z��\��Y|���p΁��W1k�z	�l��є�R�V��9W����';P&���he��[B�� �q�	H?&a&��`ۺ��'��y%����V���!�0N�	��8�P��M�`c�}�;�Ѳ������1��ᆴa���odO� ��C���k�?�3��D����ۺ����J���1�� ���%1`1��ʡ|m�˨��N��8�_p
��=���1���S�����`�_�E_��WϾl.�d�o~���'7�Ωq	����bv���@�'\����ey�a����x�ib���.���	��D¢��aP�����G��e�+�:f������mkk�?��c�4��2�=͆�/��ⵄ�7�K�����o��as�0�z���	����(2��w��䪎�<����х��F����9w<O�1��`���{7�wN���V-�sw�i��h|�\k��?$>Z�sCqZ�>TT�F���{=GL~��`+p��/����Zv�
���'��a�����(��^�ʉ,pn�vA��E�>b����lǾܭ4\�oNgOs��/$�8~��Oy�1|����>�~��	Z�0��)[�݇]���f_�y�_�����cu��!h��)�����J��h�}R9X�+<}< ���9\G��7�o>s�%���V%	������qI�b>�$����tC����L}X��ٗM���N���+�o��p���`��v��K��
[��
ccG@�#��/���`���ǥ�N��`����$a��[�ѩ�`�J���I�}����%�4Bi�;�@�{�No �J`���ɷ�P�Jl_��/�{7y����������� ���vU ����
�5�3	�n�><���'\�S!p�,��o��}B	�D�A���8M�>����.�mM���A�9�?�rѓ���I?K�8�T)d �A���m�g��:?=��>�aκ�k<�>����7��k�lE��^�A�Td��O�8p#�i	�4�uy$����q���
�+I��3�����s�ɦ/��D��nxXzn�1�u�eSޚ
H�����g�[���I񀄫�A]�\-@�|dJgg_�U�G�Y��'���]c�Y��{7�K#�?��z�	�8��8�\�F��W�
c������N�U���7ׂ�4��:H@�Q̡��D�@P��!�Ѿ�HD��'��ᓟA����'�I	�	�j�
��%��/���
@�_Z�і�>��g
\W�j毎#-~�ƨ���y.�b���rwKW+�:�C�lc����<�$!�]?�O/M@�B�!�)`w�U2�<���YKݫ�y���rEJ�<5�`\<��On�H���u-������_�󉶅P�B�m�-t��ro䞋^{>�>�rB�@���P�l{M�'��H�W�u�����
�a��9S�^��t��
���/^U =����}2�)���7u���c��u~�?�}���Qg�{�y�8+l� )�Y��f��_kx
���|~�z��߾쳃 ���1o7?<}��{ElC��BX�V���Yh�B{ۡ�Ch��x��+����'t�
>� �	��4�1��CfDJ�~Lr�)�u�����}�9{�A!��JG�W��J�G_
�{��`��$n�o�na��N��/���a�g;m�����[�n�L� s��`�k;$�z�����d����Y����+t�
"Pt���k������y��©O�Ч�J��[=�P��L�u��� �\�~|�����d,-`�j�)3�ܿ���:g싃�����`�wX1X�Dt
Z*���r���G*t�p�?�;>{_u]�~i��ɐ�����-Pk�:�l�p,�|&�]"�G��������җ����V/CǪ�-a�Q!����ˁ��/�p��Ь�XwZ�yf��]9Ux�W�[`��	�؜��9eJ���ͥ��2�;P�}D1�D�`�k�6�Rϰl��5��@b $
��{?X{��@��l�����K/�^/���V�L>$�ԁ��_uv�$�hR �](� �p�11% ��`-lY������5v��ܡ���w�_$f�������vw��to���8��5f��0h$K"|Tcx�[�Y=��
�?<T���@2X����u�a�g6��\��<��RPtz(��;N��d^?8�N��[��#��^/b���m��	�
�g��~�f������B�`o; ^���P�G�~�{?����΀��]c���q��A����Y`�	h���DP�������g6�g�̋��H�.-6}�ͭT�J�@��IM?�����	wy��ÈA'�gëxx�t���9�L�q��!�q�b^���/I@����M
��>~!�>�+�>(��/X���������1�$���pP�9ҏxi�7=MNk�挩��	�����đ�M�}�Z��`i���
�3���T�6�ؽ>X��k���b��X�;B,l�q�e��/�Q_�ȄX�a�>%�#����x^�No���;X��
P��V�O���{�@{'PS�\k��Ρ6EB�O���eEUq�
7�B��k1/��M��..z{+�g�`U�U�&�K
\�We�nAO9�!�1��#����y]�f,�80%��q�\l ���A
��.��:" �Ht�� ��4�l�b���ş5���ৈ���k��#�у���
ʤ�
"��_E`�Gf���;��h��JUd���X$R�exn˨���f�v��)��e֊����*�|-�HyH�R"R���븙@Ƥ��hY�=�"�sޤ<��o#rt����M��d�AӔ�@^���=�1W�0Ӆy�����{}����[�տ�u�=ڶ�{]&�j}M�pz+y�a$�;�x��W�����)~�LkR����/�_F)��	A�����d����H�R���E�N׶���})��e�(bY`��r��ۜ��o{:+�7���p����t5��z}�Q~
�o��L5�o-������j�a�o��J�7�����&�ϯ?�k�����82-* �[A����^AiD�	?
H����'�}й&��c,�O�u<"�
``�KAO��N4�HR��{�Ո"�L,�3H�d���w�Ycw���
S�T0L��
j1�5l��@C�(0����ξB����D�{����o����kN��_��4�<}�'��+�g(����A���
�5�2h	A�|[h	��
l[��v���
&�F�������� y������'�-���Y:Ådn�]��uc���Tom���;q�m�	����@&�r�P�-v��o=G�qѺY��
��'+cyݓo
v���ʻ�"=0N�p,i�|�����5�� ��s:2�*���xШA�r�Eް�73g��]��>�� ����������,�A~)y�p��s
��]0,��f�Kg��7}�2��o�t�7�>�3v���B�~�z&P�=��8X��j��ۆ�ڊ7�ݝ��3��1��ؿ�^o
���!�� �[OJ��H<ڴOB����� �@?/>���ć�`�]Z�|�y��]��e@G(��c��B҅`���8ȎL"�G�Utu�s�#����v�>pt����)s��j����=����e�m�SBE��@�� �'��]���ʶ�"H �@�o��q��#��5�A���%�4	�����<J�7*YHE�ܓ�y@�Z
��)�����O}�S�;�[��Q �Xs��I	���1-3��u��.~�H@S�%�.�n�t	��eߑ&��C��(;��.�����4��	$SB��v�{O x�������)��<�<f��u4
���Tm�@�-���{b�@��z�}�k��`A�����#b �������[�����A�����#�b��<��"a���x#R~q�H�H@����sf�*��������1��W�Zx�W��(c�6]&������;x���lW��l�6���6}��J�0�
�g���6,��K���A��$ ��cP���/�G�s��/�@쉘��/ɀ������I!������
�g���w��:�y(I��	�2Ƀ�����38��Vƿ?��ºgY�d�T	�ϽK�Ԋ��1
��[���u�n/��1
w������	�x�T��ɢ�����9�"X�j� ��l��8�<|����҇�ΖJ�8�n�֒W�5�.��@[,�!�D,"A]�h2@z�HRyO�x6~��?����`�O_�e�F5V[�4A`q]���O�L��������� `�7̃ٗ�\�l!�(��g釛1�L���w�<O���`��@�Ea��S)���J���~m?Hʎ��Qg~���wp_�d�}��� Wȴ`p�$��ֿ^�kJo�pr�Oz) ��<Ib���I"�0>XY����?�U^y�:��&�@u�=
Ȥ�
��yjr2OT1ٵ�|�;�ѿ^o�[�/�yF�pe����d��N�—�<�%���t�&�� �`U��^�CO`c�p<kB@=~[�(��A���5���ǀ��ӄ�d�5�����Y���ا!e_Ž�"�b���ǀ�$��y	$@������#��KރjT}�G�5�iyb�0ܦ���'��\([0�t�x��պ�"��Y�I��������
��^��o�[K�s�צ�	���h	�U�
�^I�� �Yæ�a�n�쀾��ty-��?Ww
z��ܹe&�%�"_�RDud��vG����'��}��d������fY�l��Lx�ൗ�˜s�}���(PK�1H^@����d�D`�_����i�������K���ߜ������1��d�'�U���}Wd`����#�O�S,_^W��u�D�9�$JF���@oغ��R4.o�<�9��L��$��'VPZ��{C%#U�(d�ȴO��Cג���Q;�V����.���g9��P�@Wď�zB �@F��И�`�ā����r`��s�0��+�k-����D	@T;�t�g����j��l|�h�k; �@��"8���I��b%KOoSt����r��t1
9'A_��m4�{�Ȇ��d�<�R�w$	DIEbNЧw����5~����>e���PJ@� �`��O4*x�W�oH@�|��;�I=�l6���B�{���h����+�<�2X(��	�����o��C�K���:�o
S��K��].ssi�s�I�T���@��5�^�-�)�s�J^��U�-Br‰ ���yۭo��o*GO�x�bM+	)�=ܚO��G��u�GJC�D5�fĆ�e�%��U�-��MZ�zt�)tw�g�Z!��y���`����0݃j�Ml�q�8�ͳ�x)z'>��T>���{��'��=�q�)�Q�9hO� [C�R�H=@O_�JFt�@�<"�3���p��'�������m3C�cpi'!_>�'Q�|��/1 �Ş�51��#h0�j/�{�.["����;�K�<��p@�zϠ
pS�	�;Y������ܛ,�����'�c�]i����?��K/ϭ��7�+.z8g�\�;FJA	��Z �vwh4\��/��%&�'�>� 7��R��3|�7rך� A
	՚�0H�����.H�A�@�~9����r'^��PP�Kߥ��n����� ���Дvȍ��p=����ߝ�e��-�q�3M��֤M??g�m� �����sCA�!�_6�����’��$��uww3�a�S<5�=MX�W��)�|Q ���r��?~�~���X	��8��C�
�7;��$}�O�;F�g���x	�b;���.T���Ǧ���n�g���G[�u+`R�
�d�0Iy2���a�#=xYY�Fu�{f
_�%[l`��:��/~��g�7��1o���8 �5�� l��sݝ���_�d ��ui��U����l \mD�������Wo}#�z>F@���%!����C��;J2@�h"^�&l�3�x��5<p���{c5Э}�rQ����0��_xh�>�k��Jpx�}�}�!�T	મNy���o���\_\�&�Y�%෉����zE��^���>�L���]Q����u��U��j�zxn_s0]��[V.�T%�<�$���'��#�\O�������5�Z1Ȍs����Ǿ�n�M@{L�?�u����m��a����^R��^P���0��^s��@#U�Eϒ�om�w{�����i6�����wt,@���3���������������&����$�X�-u�x��>�m߼�u<�u:��P{
*`��PU�F�}"�d�#��#ߩ��wog�s%�QE�?��������K��#ZBl�Mൊ�AȤp��il8������V(Xxc؝��k�
�?�/��l!�3@����bާ:$>FAX�q�����k
���>�q �?��"���
�~����>�Z�[��?����zk��\�����7
�z"����pM��0B����bX��@���Ff�|�M��Cg^�n�n`m�/SF3@.�`�Lk�'��rx�7��,-˽t�ώx����!��tyq�y�q>/$�}7�D:��m�jw�M�¬�(�?<�_|d�?�$��Os
ܰl��p�gs�SKi����3P{x��db��Q�� Eկ�Aq<�=omU�υ�h��op���ۣ$jT�������4�7'�?�7�P�ˋ0�.2�v���bһ�FT�$= ������~đ�����П'�G�f�;/3}�	4�ë�s�#���\sL��c�)|��W�����ݛ@Зhb��C�\�WE��?��_���cHrx�=�#[,�.� �Fn��P��Ċ�,��������k3lw�=����(|�gf=x	������-|�x2�{A��^�l��\@a��y�7�|:���=�`��zAD�-E�W�w���d=�y�-���^s��l�",u
Ћy�7 	BM�[��=v5�:�0�����)�*�N��|��j�b���Z�Ʊ�k+�Mcն�X�
O!�����`RAZ,&VX�ԁWj.�l�aͅ��0���!�ӎ�i��#�}��m̭��2؝׬������<��P����Y6��P�b�"H-��
�y�9I*�Y��>x5������}��~a=��q�3�0�a�w�ܳ���q&�;&�K@�����k�X�MZ��iO�x�̛�����I�}��6��ƃ�g�|n^��'6���Q�?z�'��I�fp�j�'i���
;����~�k�p~<�Ӻ��
�V�i��'�~qݰ��nh�O�a̽���|��6�_+<�ho����$��n���c�7 �#=
��p�{O���y��@��d=p�H�^@{q8?��L�}z	
�D[��'u����/�r܁��ęs���0���߫{�ƞ����S�����}�>��ZA�}!�F��'`��:Wb�r��g_�W���ӳ@#�����
B�0LF)�DX�[]��^�-������{PO��-��##ھQ���1��[^@�z��LAR�	I��v_��B����<p��?э���
�6�ߋ&�0��ï>�k�M���%��荜<�!N�� &?���p�	\߱r?���;�V|���*��N�䠬
V>a�����O�~��pΑ{��	Z�Rʦ�BL�'p�h�z0�*����|d̹��h8@�
�G?�1vB�8��/P�C����J�A�ϣ�4M����JB~{~�䧣8��;��-fC���l&�C�}p�ç<ˉ3��91}�&=�'���=��Pο�\X{��c��{R��	�f�;l���ce�#+~�a��/�*��M���<`R��-�m�����"�M�|S���9�.}Pr`�_�{�-M�.�P�lH_ �;�d:h:2P����J*a`^�+b�\D X��\��3� ?�n<��]�C��r�D<�ݶ�]�r��,����.��Oќ/���	���N�+׿���>���@��/�I��|�^�Qj�D�Y6��w>�gF6A2p'ž@n�#�y�X����d=j����?�qL7�
8@�ʾ�~ՂyE�8��po����@5��k ���9/��W̉ ��xa���	#0����r+�m��ڮq�����kz�M`M�*M�*c[��0l;SFlaθu8f�L}�w'@1�E�w���U�'���5*�w��՞u�z1R�xǩ��w��=k�"�#+(8)�…��X��W�y+��7~<PJ����c>*�^��(�Z����~6�$��5�茟�w�68�<�=���b���v�=�(/b�+�J@��8��Pmd���S�@|��$�
��,V�����9k�#����D�H�+0@&�8@����w��޲�q�;���l��wѿ�'�C!������}��b���ĵ5Pq���k
B|	��
i��
HiH��D����'��I��sL�IH�}4!�=9(@?Ʃ�^�L�gMdd!������T��2��L#(�r�0
��V_-,�7uf�>�}��h ����O�e7�t#��)H��&eUp|H�`,��b��Wq|h�v$��},@�$�$�F���:&��K�g}�x}�0R�x�:K�Q/o�0�ņ��C���{6�
��='�8���'�k��>��o�Z){
6d�U9`�kjW���1}[EwPEU��@匲�+�݃\s#��7P�O
��sr�~�'rn}�8^}�w��5֟�l�"I!����j�z���f�~�m���3ne����U{]��ȶA&-�Ĵ��Y�y����tp��
=Ǭr�7�qd��M:htF�1v�e ��Ss��&G�IP�>�Ez�FR^�a����>	���+/��K��ڍ��WX��� �"���R�
�X ��c|��#��h�'�$3v�x�q�l3d
z�_O�U�KZ߹�}@m�	@���{���d�(/@�**�#��FTד�ਗ��
��[�2�'��s�`E�O�ڟ��ߓ�Z$ ��{no���hx�h��J��[����Q/���ƒ�˹�K�K]V���nO���M/�Zpׯ�ߖ}۔-���8i��=���p�)�_�Jp�ꫂ	Χ�
��JP?����P�?x-�m$9(�\O�)z{���o5߾�m��}�WG8I���Vj���6�z��y�iw$�|�9�A�a�sRJ��<䚵��L�\�坣|��CM��e��'�=���X}wС��@������EL!�Zj�pŜ<^��^@Z@�	4<����{<��@Pt��"�k�8fЎr�j<�
6���x�G_dDK1
Y1�2%�_�;����d!�����G�틏��x� ������ַ�vH@Rd�{�h
��xF)'�\���Ґ��(2|t^@<�ؗ<��I@�v�S�y�/���� ����z\�^�R�j�f������X�H+
T�I�"��@�5�5�/��2��ޅ�	����Sj�F}�<{�c<'���HY��	֊9����r]̉v�p�`����8���4	�=��y	ۋ�Á>��ֿ�]�!�TK��w�.��'�,u`u���㤘	丐k��X$����S|�;��?�����9������
�����@M�&\7���B��XLFf�x!r����XKK�z�_\�'����3���р�@����3*�߰[�Ի�Q�fM��]�zr��U��	�f����Ɓ�<lϬ����>|���M%q�����g�E�p���;(���[�bw[D����&�ή��Y���_/�<�Ѥ�k���>��'L�~�{��}g��>�vX�N_���r|�����Ny,P6��+=�D�>�*�Г@o�M�{�T��3� 3�r����	6�a��2��&��U08(�r�w^�n�)� 8+ ���3�]o m?1��}=�_��o?µ�8�QZ��� 	"��j7���p��0����_���zr(�
t$�U�3���P��5�Vniz��O�g��7��Z<��ōa�K�W���@{���}������ӧ��E��:�@�@���5�c�<)t6E41��~Ŝ�E߾�5�e������P|�_�R��R�,��W�ȧ^uC�Ȳ
�J�嚛��8�m���d��yݏ����;e���@��^��g��V��k�?��"2��E�<).Vk�8�/s�=���վC���I@���;� X���	}��~�y��#d1�����;�Y���y��N:���-�m�۳z@�ki���gdk1ID�-�V�6�@�M1�qu�I�����^�ޣ�TD����.�9��f̉Q�`&Q(�E�$�X�Cd0X�)�-�	�^�+
�b�5�d��e/�ކ��+���'��5O��H��#�|�W���K���7�7�諬�6�2���(�2�8�V���?a�/��Ͽ�/1�$B{��@�`�$3��P�H;��@�n.�i�g�Y�1�@�z�|n��2��(Lz��p��I>�
`�:�=����5�z3�	��l6��k#�p�yA����&Ti�r0h{�!���Y�1��r7�1iD�Fu���#kf�?��5Ŀ��̟XY?f��/7Ҩ�+�
ÚK��o3���<Ҭ2�~@6�L ���=&ϣ�Ν���t���!&�N��'��Z��Y@�x�l }o ��h0XQ�"� ��u���D�-�Z+�]n��� ��x���/=
'�xwxf���MH�o��{ "�H�s��M�|n:yt�/�͊-c�ܿ�����97S�W����]����
�ɸym�7E����6g=zzWa]�������oɣK=�P} �&���ͫ�]]w�K�p����7�d������(;�_��V��b݊°����md�Ju
�
̇Y�Ĝ�O��C����	���;0��u	�{�G�
dž�+��,�����=G����`—�:|Bs�h�n!"�'�:$>��{@��I����1~d7w~��4��#J	ؚ��/�MJ��B&��y"�\�z��q���
q���{����=6Z
��-�5�DF��9	��<���c@j�N�>��j�Z�o�N`y&D�7"���IA�������G� 
Ÿ�Q�?B������.��8~{�+9l�-0�^�����yPf�X]PX�-V�[�}k��A��w����p:h�
t%��_�	8)	�f ��#������[�`{1S����F.��˞�?5�f�$A^�E���)S�擯ր��p�
��|o�Q�X���^Z�A�Gx8b.TR
��#�|���	���B��!��|@������H�X��~��r-�~��"�\k�`hk��	�Ԥ����w-W���Š���k�|!��(�����*�@��=����ғ���-����q���/���U�
��'�ů	�e�+��^��3p�;�ųz��R���5�\�ńIGR�w=���1)1��d<���-Uf
awr�x&�7pm�{���$�b-�ܹ���y4}���&5� ���"��L3��H�q�1�~��M�2��wq�oB|��r��u��f#PO�0Ŝ�<�Lw% �߻&��%sWJ�go$�-�t�x�xv�L�]��.�H	51��3K#)�Fc�w�|"�\��$�#�%%Q�llx��$
��p��п7�Q���Y��~�|�f`�L�|͈y��К�1n��M
�������A��M�x���o�L
��6�7�؀���̀���	\_Ž���ǽ(�⯽�D�w��^8�����	�X|2)�z"`��!-
���{"�Z��D���bP��"
	���Ў�P���%���C���>���L�w���7n<�U���_����tM��`L�4l��Wl0L�����^�4��a};�W���!��ɣ���7~�B�פ�(%�Co�'�	dR�d�`bv������y_^|������������
K�7:� �5�>����<-H�H���\+�G��=����Y��i	�2X�X�����&����z/~_��YHHy���+����;�{VN�'���N���L�i���
SkT�l�!��p�lt���b/%1İ�M�DOi#�F��咏���,G���=�M@���d�!��~5!�=�~���_�=�'{��} ���Ə�Di�Ӯ�r�i$#IO@�W�SB�GF&���Yzz�� k�~З�jLp=��Ґ�6�WؙP�
�VG{&@!$�O$��<�{�=r�~ְ"*�OK>�d)d��89!����uE��+�A��"G��K_�����S�ko�6.���
T���	��}1�r=�[��r_��c����/�d��˺��c����1�����`UJh�^����{;������L��i�H^�w�*h�1�p����}@Nr�D�3��<�eO��{�X͂1X	���ov��us�j�{c=!����A>�Jóu���x�1kR;��Oi��㣇Q���=>�'�c �d��a=J�]5��n�z��k	@�Ӟ��5��#A=^M@�`�!^�h�0�xBR�x�tuN��i4r��ս>I�_����>b�/I L�1�c��Ȧt�`p�;֏��=�
�-�bi�1͙6�NS`��L
DC��o�x�S�L�QQ0�\��?�i��#��2�=���^
N_������{�3����>�e��JRʿ�2e���p��D��H"^�O	��O\/ ����~=1X==��B�o��
��5�s��0RP������9�>$6�n�E�S��#�4e�)�-�`?�U
��=�{Ֆ�
�r��bu;a�o����������%q�K�y�B��c�5z���9�����+�Y_���T_VU�SB�]/@�*�_��Z3k6�b��)���X���KP72�M~�9�N�N�� �C����-#��S�b۰��PZ�}��hʹ⚬$
����!�$�Kb�Z���6�]�X,r��G��W#Gj@�@=A�����p���뭸m���J�}Q��/�9�m��i�]�h�t�aCHR�W�FX��n8�|�����1;��b�0�Уb�(m)"e!�s�7��i��<W�p�Ƒ\�b*�Z&�%�;8��-4�������Z�c�[9�����*o�a��q���鋗����~���3�Y@�p |F2����숳O�����w@���e�)�,�|�5
W���ؕ�b>FJh"�!P�x�@p����p�8�s�iX`T��)��X�Rg�i"[o
�e���ߋ Od�XIz�L��e�ڇq��ql+�eC�p��<�n+�qx%�w�=z��e�b�Tک6�X�m��^.�ҥLݭ{�Iq�@�ޢOT�1�����AH�U�ޖ�P���}��1�@����]+�T	ꪢ0���0,&"%T�
�0X���|-�zdxv.`�cD��1����`|��f�f���>b�	#��9'W�ש��fX֓灭�x�c86�B7�z��涒39-���A�bZ�1�~���覯ډ�\����̛�)9����T��� � �d��/�\����Xt�L�ܧ@�@����F󏜐�pf���l���0�:m/ M�?N6��ϡb]���3Y�ӆfd��m%f�Ԙ�Tgl�(�.�up���''S�xN��Ӡ�G�:l���/ey���U]�4��b���ʄ�6H�-N3GZ����-=!��j�_�Zm�讶�3~q˅��S_>���태�iS�\e�Qq_�:�4�S�����Up�����1��w������'�?),�0,F{�=��u��W[
Z�=���t:�FF���/ MT�4�D��d�dvX��	j��p2���@m��s*RPJBс�W����Ŋ?���^��O|T�"���R���D����|P��~hMۛ��Ƣ������-2�����h�Wū
��s����o�RFP����4��5p�}K��E1��+<<w>�D,F�.�n3y������Z2��=JBP��$�r��ڰu:+����7���OXi�'�&�@-�L}�զ@4Y��/��Z>v����_���GB�j���G^�*X��j��e��^�.5���=@Ǘw$i$*a���ك�c�Ž��?��3C5}+%	�����vz�^�ѓA�;�hI��S�;@���e	ȵO����}C��-�oqܦX�����>�{G���¯}�D6���+���+o�i�@��>,F��B�I���t��wǐ�Ĝ���w�ȲM��ѠD���c����200(�+@��(�<y��s�lL2��;��A4)�t��`��c{e+u[C�O���{�:TH#
��o���H���������ǿTo�� �š�s��&yM_�>�!�r�H���A11�uF�</ �b��� ���9��b#�Gn;�
S�@�:�Tm�:^��C���p0̛��ϛN�P��6���T{b���?*r 0o�^m��Uų������N����H�4t@��'�%I*B�ב�����g�s�awK��'���)��-�|�5^V�{DJ�L�T���h1^�V��4O��g:�y>v�l需v!T�Ӡf=,V�r��@��q�����q1�h=_���%.䣪�5� ���J��vP�U����7��k�L���[�S���d�nf����R抑�>�\���/�h
����؟�Y�%�3@C8����0l��C��H4 ��$�#������L6v�Syz;>ul���ų~,�D��`08�����1�Wn\���
b�����[v�B�{xt�vk�9_�﹎�[�2�+���V~����pq���&�G�&��?�yJf��_#?����N^T�g!��O"T����*M>�����|���m/b�	�z@ӗ?y2P�@i�rLb�����!5Ǝ���^�!Ӷ���0x����A�����td�g����œ�x�g�y�� Ț����o�s�.��Q�����u�P FZhrkzm_�0�W���;N癭S�""Px��@�Q�|8�ǰ��>Au[��
� 8�M��w-G��ސ^�P���'l�7~�8,�
�~���s�}6ʪ�}�Bػ�m3�� H ��_������!�#�{"H�f
ߺ�x�^=`� ��/ߣ���[�z�_<_��tַ�<^u�|�M���4��f�����A<�����~1��ƖO���?��ه	@�l���A��]cG+SA��`y��9�?? /�S�1�����]������Xz"�{
����5pWj�&2ݳ��wa+�l�/��v^w�
�gk*@)U�_��c��`?p�ף�x̋��nP���K�`p�%=K�4��}HqH��K��GDz{��N2�3�x��{���|���)Vsȱ�zj���r��$�����5�A�a��m|��e��0
����Ar�Ɯo$op���^��藟s�a�%y��Y���WU����˛�Ea6��?�:/�^c�h� �M��KD�o�k����3��c/�
d�y�^����zxl�wR�*��x�I���W�M.��[���U��걏�y/�BF�
'�����Ǟẇ����O��*`�����^���6�	��c��$Dv
���D@8�I��xV7�.}�H.�<k���� (!ڛ�|N��
�]x���禎��+o�������>�:����~!�M�0
��U����WO�����}���»˳{ie�-�f�I��n!9�I -T�	d���>��7�e���{Ofu�(�p�A��A-boB�sA��i���#��x�����K�)W�o�
������H8��$���'�Ѿ����^;��@M����A"%�w��
��Y	y���ќ&�
�N?�3yK?�y�^�xr�=��j#�����3�C��$1�)��.��#���^s'Om�~��>���n����l����h�G�S6��p��wI��SB?R���������z	(��p�i��g�w^�S_?t,������'�:���V��0qd�x�}�u�r%���#�T��p�'�F�ֿ�d�Ĺ{�@ե�pM��鯐��
H/�	h�C���h���y6�S(v`"-"�uIQ�?RPR#N ���:�K^�S���s��Y@�Y1_�5�{�V�Ȗ2�8�Q�r�c䳍��ݤ/���=���:�������񱰡
\�{M���/����?3��Ń��\wu|/�bB��>8^��8,����@��2Ԥ���#	 d��uӹ책,��B���N=)xxty}�y%Z������x�)�ђ�%����Jz�%}HI�'��6Ã�:K�pC�ig�'0@&�\�����	����k<E@XQ!�/���I�}��y-	��7N��Ǐ�翁}��� %���-�^[����2~xo:�	^{�S�������_��!����T�Q����[�A5�j���:�M�� ò1�e ~�P}L@NT
��b��Y�~��{Ww���s��9��Y��ӄ ����i����x뉏s��d2^|�7]�J	�r&���l���DK?<4�
�{���æ�t?/���x�u�T/ﱘ�Sô5�yEZ(���Z
J�Ǐ^�ϫI@��L����U3���xb�$,r컄 I�b�ezl�|���_�d�Nk�'`�v��ܽ �H�̈́�ڿX_��M��oʣ�_/\6�K�	 ��Pi��W'�ʼn�0�@6<5T��U��Jo@Z�IHA���T-}Gc�'D�{Sow���ݫd���Xk`�'h�RO�)�t�^��9N���B��w�������=I/�K�����2p��p��@�R���?[�{ї��^�����`�ͼB��@o�@�ZN^
J<��L��\O���R3������M���Snb_%[���`�*N:d-���a����Ӵ�ӯ�@9
8��	\=�t��ϛ�–���O�M�z��o.��~�xCe��7���5^^��',(�E�&-ԟW���HA�I(����'�XcY�1�'6Nbٖ	,�<�M���ۆ�����7c-��]���ɸ��5뿒D�焽4#����G�?�[��o{򚾛6�������^@��{���	^���l qn� �Р�?'=}@88�6@����I��3&�XB�
�}	��EW��3[DZ��ϳ[Ƴ|�8:z[�a����5y�m�i[�9���C������1��	�E���e:8fg���W�'��8_:q8P�O{����N�l��[3Ǝ�y6"5T�*����@iIA2�'V<@�w|߯���Jk;G��k������֞att�������T���Ĩa݌�����#;�:���:|_��k{[�W?�jr�
p��ZO#�,��.\9����ϩk��V�e�|������������b6"5ԟC�·�$/�?�KO�_>?�$@�~9W���&*��Z�V��im���6hk�0����O0�D�oZ�PK��O�<��t�A~�0
ֶI�\�jp�
�ʙ����q_�	 �GP�X/?�=G]�7̒�uӐ@"�Gd
Ō�"1�7�.��d�������$ו$��@���5�U$!�%0�w�^ž�$��y+�?��o���|<<���p�s0�̳���|��ۀ�~�u�����N?�6�;�c��oH�j�"����@z)(~j���k�;���;�[�>i�N!��}���ֿ���|*R?��޵��:��_���]p�z������[7�(��'����~�}�E�ɼX��E�L���k�����燞��]�C�
���?��-�d��u�u�ݛ�a�\V�?P���ף\wn*|������~�����U��yKyƍ��"k��B#
�yũa�HA������������H�@����`w�>���u�w��R�H��?/_	cK`a]w�ܩ�^�+���4^@�8�➣>1�k�� ]Z�6 �:8&	)H���� ��@��ௗ�"��4������Z�?����y����~�$��Ͱ`+����οmz�N��O�v�����?�]|]��y6, �B��B
���F�A�%���r�"��=��+@I(��ĭ��\���*����H�\仼�O�q�d���9���bս��ǿ��?(�B�>U<�ؓkc�ϘȴP���㤄
o@'I��	�{�{�'�k��?��Yh�����I�5æl�`�{�FP��l�%�7S�1�C�xls�+�h�@
�@��P��Ҟ%珵�7F���
Gt
R��cV	+I�I��CH�@^��
�A��`�����c�� ����O���h�z��PuB�3��eςk��oxΖ1�<fng9S짦�+ �zT}�/�zC��������;�����(Â"54�x@�-��'��p�u$@'(	!�4�6����h�d�e�z��/����r�aЛ�}��ݸ�<�k��`mw��Ӿ�俀�o�{�	 ^@��?�柳�1�BeJ���E
(�{�s�v�����P<���ג@�w�`=�Ƿ�Ž��g�?�&e0�c�7�.���K��ð�͡��3��̋����g-��oW͙���L�_��OFJA��^��1d^m��X���đ��o'���E��!��$��K��	�r?�}�`����?$��R�6�P��P�G��/|��,Y��5�g�ܛ���/�P��?�	@Q!��>j���6o�J�	��-���(N�$U}@48�$�ߓX\��u���<�����>r/C����7����-s�?9��<�G�s�C
��Rz��s����[����O���⡧Su�c<�H}?�:Xf饠pYh���1��_h3���{	(֐si���嚂����>��!r���{T�T�A��� ���2�?�3��L�=���{�G}
(����
Z/�Y��6���k�D�"zI��{P�Aa]f�\ׁ�$�䛽%*�b��Z���� �Sc�=��:�|P}l
���H��̌v�N~���&��t���y�ݺf�v��O����h���~��W6f:xh� ÞIA��$RC5�$KJ��~%i�'��->�k� |/vh����s���`/����)�0/�돓��t��d�*�3�]�cs���#~�d�����Ҭ/�M��5^�Q�h� M�����$H@����I�"�z�=Iu�D�i����@�>�:"^@���<�9+�徴Q#!��(�>D���Gm�z�;���Lk����1�z(�c���J
���]z�8 ��'���������Z��	k�&B
RA�C�n���� �
� ��{�o�@���
"�X�~� Xq���FhE�~�a��k�oN�UGB�/�j��z��d&t������C�����3����O�d����Y��#q�JT
���ׂ�����&3H�M��{�.�?Y�'
ܕ@/�����ÇIR;'�f���>�3p-������.8
��XI��`쀀/���]�oμx�w�"P�~H�@�����_)rE�0-�N�@tjh�$%
E��3��UH`w�Ŵ�c�1$m@>���'�n�SG慏U��6��Qɳ��c+
s�Ѳ�iL֋|1W��gF���SJu�gp��d�>\���ŵ�q���{��
�	�����'H�:�� В@��<��?	�K��W�#m�G�A��`�R��\V�z���R��WKn�vږ<�S(�}����򥻦��&/���w?�/e����C���-�h)������[G�
��J$�SuK�I4�?f���@zAIbNOC�������V����[����{d&���-���"B�{c�����ˀ���/��	`RC��_����ޠ���{����u�7 �5�	�g��4s���D �F��%A�%9�~�XO5�N�~�d��V��?��Ȗ���Y}��6׶m۶m۶m۶mۚ7��ī��"_E��*n���;����=_�9�YI]���ؖ�a�E��MD�8��y�P�q�+
=/h���nd�L �.�hJC�<����ʠ�B@a%�)�~�gh��H��+�^!�x�}50p�P �T!EXC��ρ6��e�	��V6�d���g�r�O��"�*
uX�8`���q�6
b�/�̮����Z��P5�0y�'�<4}+�w��^�� ��_~_�_���6����_I�B���"���?־w�bw~�'����<p�pn��+\��P�	�h�+�x�^��;x(LDT}yh�H�����_.�����"����XT_28�@���s?m��3U
=*����*�ʐ~a����+����?�A���Yל���qV	��P*��awO
٪*���@. ?�7�~P�O$t-��=�ZmPN�rYvO�����*7|�gÍ����+ �< �����[��md�aWq:���JC���5 }08�r�CkA�z������#���,^$?��kJ�ȫ��;��h�e��S�ߏ���}��.y��;���Y?3R�Wu��!p�?�ԏ���6�X��8�PX���F ���b8p[�ZHP��/p���T������Y�]�B��(K.KTJ��Ni|���}��C�e�k���z��G�
�4�<g��u�dA�� 3<�CI�(�C@lK�@���@w��3A+���PA�A�M

R��3|Eo��Һ}���֙��������&��V���ܤ���L� ��B(����{d�v��SQ
Z�`��_hyw���������L�-x��
�,eC�V���#��b/���ߛ��󖙯d�Z�O
s,_8b���kꉆ+��
$��^*~z������������_i�
�n`U,�C�*�~x�5DS�*F%��3��[���r��wL��5z�1�B���:��Ib-�Oظ0��zc���p(��_e��йG�҄@�*�k��8H�P�Cd(0���|i�����Ae`XC���*�X��*�xePCvȏ�m��+3��2P�����Ib2�uȿ��O��A�?�]
<V)@�U;���2
������ؽ
����t��G���ş�"���A{��οB��EYc[��s�|c"�Xā� A�]��B�j[�Ch�o���@ =����\�_�u�!�bC�&��*�_X��)Y�e���s���d��)�@��P�}s�X�����4���B(��@��iw	�0 vOu�@
�|�y���O��}��)_��Cv�*�d�+���ȇ�j-�w�k�F����w�*���ЖCr[��܄l�v^�
˖��J#"h�UBTCg�B#���AA�=�~&�_!�:����%�;����5�C!{�țfٯP1S�����G�`���p_L�,���U��r������ZB�R���I�/�`��#�e`��_����S��C�����^e#7f�껄�h�m>l�{'�������Ѕ!phn���sn��i�m�!��ApM!�. �V~�W��������A5Ap�j�5���c'����D!�[Q2������o����l��?�+v�L��E�,�:��<�W1a������t��5�	�~���:��#0���� gX�7���⧘��]^�"YA����w�v���7�<>�'��@`�y�/�=���^N��2Q T�wfP�#B
y����?�K�o��_�:^g�X�hTA�lƏ,�L�Å��P��?�N��މW��~{�����h޾0bֵK.�#��w��� ��e�]E�2n]�"�j�����O�ş�Ś
��P ��b�r�:n剾��![��a�/��w����`�A���߬8��z�a���H&������q�� v����S!D�`�}
�ැ �ؚ�2P���Z�b�Ȃ^?2�~�Vw݄�'_��6��g=y�ED4���R��{F^�lh�lU_ꃀ{������a��3}�r��S?À@>���5�L'e��
�>��Y���CVP�ү}�w�]^�&�|��~@�r��ܣ�g��d�N�AL�=�@ح���l���{��O��K=C���5
��\���;��_�p}D�/su7L�g�q���3��Y>�K`�m�~Nn�1�(�2|ey(���ZB��]{!�~�	�*]�|�I��{z�A0 ���&�8��H�}�O����i����o�~�ǽ7�"ʋ�~��!��Ȓi�$7�N�l�N����P��8�a���Bϟ�+��\�_�8����Z@��a�z�__/dX૯�A� kl᭿Ϟ�	�9�ϱ��b�U>|�t��O�O\}�J�Q��u�k@�
VTq@x�C����,�|]�@xK�����|@��ݿT��J8�l�Ŀ��������/�������4����(��9~�Ls��}
��Y���:c�v�����
�`@\��ň��2Q0���_��ڀ |p<v���ࡰ���W�� ~����~�w�}��.��]�|�u���۹8�fC3	UA�F1%�s8ؚr8��B����Q�'�)_�8��{6��[]���R�n�_���qK�l�'m9n�F��?τ?�#�C@hkSi�qjaܑ�(�2s�Q,<�#���`�B�%�������0P!H���<��r�'����*D��[�/��q*F~^�w����B��M&٦}S�v��j	Y�eH_$��zK�^��=!� ��_�.��=k\��Rnlu@��J_粷.���?m�o�~&�%�v��b�hS�+�h�N�3�?���C@� @DU�Ð%��S���Y|�������G:2zAW��l��m.{���?�ߛ��~��b�{�@,�g�P���ރ�.
;�����!�edP�t��2Q9o	��:*���/�=4¯}M(\6�8g���U�6��k�ׇ���/-���?�=� �Í_��4]������11�d�! ��;KX��!@r���1Z���u�@���`y��@��t��q��C��)��޸�[��c�_�?V��p��0��*�o�J���B���,���k9$��aY�K(���?��/�@�g���$�"�����Qt���V�ڗ�{�~�4���Mq�g��o��3� ��<X�ң߁�a���d�p��Dc##���l	��x����!0.����_߹�Y>|>���LO}=��C���^�{�%I�}�"���{��_�϶m۶m۶��m�����U����'��܊����'3�jqt��
��;�E��ֿ����P|��\����V�Ю
n�4���AP�����%�3SH����!�~��Wc�f h��v��3�
o.��ְ4�j��࠲�������>�E�{�@����4`���*H�����uh�_-��`��Zn�4�"I�5����1_��pSP~��L��Y7���@ i	�n���R��#��F�B �֟8C@h@@o�@�.�)�P��@��%�����^� ��e��/I��a6����
�X�Z�@ "�%�{�
ϬIY��4�?�`,!k���}nؿ��Q�]�I�7��<F2�s�O���Y@����aiX>�l�/�$-
�U@Pwz��"B�56���:�]HD���$���c�Muj@������Jv^',����g)�WZ$��w
����?%���MU�R���P���3��Ё�����H��+uC��08��j{,�"�ѵa��(��@��n��`�Y8ƻ��Ĩo�K��;{HlM��"г�Կ������Br{iu7�^E���[/�G;�,7ӝ�--?����9O���\B/b��DD��>������O,���KH�
p��{�ih{�M��0�G�jX�
uW�B��@zH�!�� �������7GDh�&
]�Z���~�C��G	�����Co˿@�Y?F�
���w~<�D�o���w	����	b��Q�3��@H�I{H�P��m)4B`EN����Ap����lx�O��b`2���e�W� ��=%�z�����h�M�iu�<�~�	]���~=\���ц��H���N&��b������h��u���_�`�x�/)�^��*���bxz	�
��>�^@�Ǭ��o>�ʞ�3�����n��� ���-{S?�#��A';�F���Av��C�^���bj(�ĭ����.�� �M�~������t/�����>qK?�&pܚ�{lj�/{�h]�žU�203�<�
|8\���x�Gr�*PwzVPC�O��@��!1�o郠�!��ϟ��_�E3]���x�P��'`� `���8f�t�ELW�~]�q��,�
̌u�n�Nf���>�~�p^lAvƶ�"`���@�R@@Pt�l@'$n)��w��A֫%�5Uv10Ӂ��NL���<�^@0�l΍�1���u�dŻ�7�?����ߘ�o��tN2��z�̽j�c��\����Pz�~��
������Y��~!������n�n��\;�5�rSm�\{kz簡w�m%�=U�o�{��׍T�&�ߔ���ᰐ؋��(w�c��^�m�< S+()�z1����CH�@�v����+$�� ��W-�:��֙6�.x�"K5eSy/���X�QŠ�"Vu��w�X��%�s�^���J��닿)�
;	���y�\vʃ��.Щ��!�gi�?�����m��s��A����[��/�HT$0S�~���b`%�=���
��X�ױ���>����8V��
"*�c��M�a�?��W�Sf�(_;����'��[࿲��hn�� 
@r��bš� c[���>4�!���Ϛ���b����,p묃ي"���,��ϒ�m	Ϻ{t8�#D���
`QԐB���Cq�{v,y�g�t(3���1e`l!6[�u�
r>+��4�^��� �H���P�"��U4s�C�lV����U���0�1Ő�эe�+[�_���@c�/�F�
L�n��������\����{i'��\��i+H
庁��"5�\�3��-;�'��U�kê��5�����w
�
��M�70�B[���@�>�N����m'���{4��W]+H�������b�YC�j�A�2�
�&�"B���7��a!�2�|��)�
$:���w����!n���pKH�
j�а�Zz����W
�gٰ�3Z������O~i�k�*��~����\�lE[���qI<�V������n �hH����V�@
�֯���v�|HW�|���/��wY,��x�A��20 p� �p>BK��L����"�@��
�ğ��Z����	����jS�覿�/<~$w&�ZB������1e`@���2�g��Ա�Y��q`=�!�\��#�g���`X���������ۤ矏��3��מt[ߙ5I55��_c^��j��20 `Ê���r>,z��tr�#������T!q�|�-���A@���Ԯ-�Z
}�_�󗓑8�o�{�ܹ�bu�ֿ0�xH��op���=d3�}��;������N�a=D�H��n����b���A�|���w��Y�:	�;���5��Nv
�_c���`��!f��`�kx(���[m�L�Cz�$�@.�Z��"��w��@�+�+����9IW\]u~����^Uu
��Ŀ�0��=�>{(�8|0�������MB<�8v�@X�	�m!u>�"��\�+H��t���~v�+�v
��ߞo��'f:��L�klB���e@�4ؼ3x�ն���{�*���	km�>%�m���Tk�A�r
��N��à�� t��X�w􏿖���z���X���_gW.����P-�0XP�@�$�=��6m���B�_p"A��P���5��t�!
�Hm��2_�+��3��_��x�L�-�Ʃ'�����2 H������Qd���N:N�ӖN�{�RWa��끀?'@��,!~��.[��J�3.����_η]x����b,"������70�[v��bC�Hx�v��#qR��q=D�9�2���ރ@�hf��l`��F�P�LFt�@`]���{��m;D\���k���o����)�����YE
0$�@�smgՃ,q�!�[J���a6�ބ�+f�@�XI�xN�
���`�g���~^ҍ�]{]`_���w�5g��x�F��7}#��ٔ)��"��>�q��{�%�d��$�m�\gCv� 
�dW�"T%F˒v�J�ĭ����ϩ9&�i�����#����)c)�">,>�� v�٢�A6��,�n9aM����5!�\fA��3�`Pw�� f��F�R����D�g"���?��#�VeA��q��#)��F�M��e`�B2G``P�@>�ͦ��;ܒ�r�\�>_�ˇ�r�N�d�s�:-���}�5~a�i]A�F@5��$�@�.e1�5I�2d���#���45��U�5���==Q@&F���
�oo�4�0g��Ϝ-���WGh��p�f���M��7|҇�w�P��@	�*Q8r����`�s]߅|����c�� |n����a���(�P$�s���~�А>��B	����ڭ�K��2�)�=����B�t_��4��Ӆ��[d�!Xӳ���=�>����r� |�'��S
\h�IEND�B`�PK
!<O����2chrome/browser/content/branding/about-wordmark.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="300" height="38" viewBox="0 0 300 38">
  <path d="M121.723 9.084h-4.747l-5.067 8.81-5.021-8.81h-4.93L109.4 20.45 101 33.14h4.747l5.98-10.18 5.889 10.18h5.067l-8.354-12.873zM20.321 33.14h4.2V9.084h-4.2zm13.717-18.982l-.411-5.075h-3.606V33.14h4.2V20.732c0-3.741 2.739-7.9 5.752-7.9a7.548 7.548 0 0 1 2.1.274L42.848 9a10.232 10.232 0 0 0-2.374-.274c-2.921 0-5.158 1.826-6.436 5.432zM0 33.14h4.337V19.309h10.27v-3.424H4.337V5.158h11.868l.5-3.469H0zm22.375-33a2.793 2.793 0 0 0-2.922 2.88 2.783 2.783 0 0 0 2.921 2.83 2.8 2.8 0 0 0 2.967-2.83A2.805 2.805 0 0 0 22.375.145zm68.042 8.586c-6.847 0-10.773 4.968-10.773 12.409 0 7.623 3.88 12.553 10.727 12.553 6.8 0 10.727-5.158 10.727-12.6.002-7.622-3.834-12.362-10.681-12.362zm-.045 21.584c-4.017 0-6.208-2.921-6.208-9.175 0-6.3 2.237-9.031 6.254-9.031 3.971 0 6.163 2.731 6.163 8.985-.001 6.3-2.192 9.221-6.209 9.221zM73.361 7.418c0-2.328.913-4.04 3.561-4.04a10.289 10.289 0 0 1 4.2.913l1.324-3.058A13.266 13.266 0 0 0 76.693 0c-4.884 0-7.532 3.066-7.532 7.083v2H64.87v3.241h4.291V33.14h4.2V12.325h5.386l.453-3.241h-5.839zM53.33 8.726c-6.208 0-10 5.151-10 12.637 0 7.623 3.926 12.325 10.636 12.325a13.113 13.113 0 0 0 8.4-3.013l-1.826-2.511a10.126 10.126 0 0 1-6.254 2.1c-3.469 0-6.071-2.146-6.482-7.714h15.341c.046-.548.091-1.324.091-2.146 0-7.349-3.424-11.678-9.906-11.678zm5.752 10.72H47.807c.32-5.341 2.419-7.387 5.615-7.387 3.789 0 5.66 2.412 5.66 7.114z" fill="#fff"/>
  <path d="M162.745 35.034l-1.825 2.327c-3.377-3.012-6.479-3.833-12.046-3.833-7.529 0-12.731-5.567-12.731-15.97 0-10.312 5.247-16.2 12.731-16.2 7.62 0 12.776 5.612 12.776 16.153 0 8.578-3.285 12.822-7.894 14.51a14.415 14.415 0 0 1 8.989 3.013zm-13.871-3.742c6.114 0 9.9-4.38 9.9-13.78 0-9.491-3.879-13.826-9.9-13.826-5.886 0-9.856 4.38-9.856 13.871-.001 9.355 4.06 13.735 9.855 13.735z" fill="#fff"/>
  <path d="M183.279 33.117H181l-.137-4.244c-1.688 2.829-4.107 4.746-7.8 4.746-4.289 0-6.753-2.6-6.753-7.3V9.253h2.647v16.792c0 3.787 1.6 5.384 4.7 5.384 3.148 0 5.339-2.053 6.981-4.882V9.253h2.647z" fill="#fff"/>
  <path d="M206.824 31.748l-.593 1.871a4.209 4.209 0 0 1-3.7-3.97 8.406 8.406 0 0 1-7.438 3.97c-4.609 0-7.392-2.875-7.392-7.118 0-5.019 3.787-7.711 9.9-7.711h4.563v-2.328c0-3.787-1.551-5.476-5.521-5.476a19.436 19.436 0 0 0-6.479 1.369l-.684-2.008a19.945 19.945 0 0 1 7.529-1.6c5.43 0 7.8 2.692 7.8 7.575v11.5c.007 2.649.737 3.424 2.015 3.926zm-4.654-4.563v-6.479h-4.152c-4.791 0-7.483 1.871-7.483 5.658 0 3.285 1.871 5.111 5.019 5.111 3.056 0 5.155-1.506 6.616-4.29z" fill="#fff"/>
  <path d="M228.452 16.052v17.065h-2.647v-16.7c0-3.924-1.6-5.521-4.517-5.521-3.24 0-5.43 2.008-7.392 4.928v17.293h-2.646V9.253h2.281l.228 4.061c1.916-2.692 4.563-4.563 8.031-4.563 4.335 0 6.662 2.692 6.662 7.301z" fill="#fff"/>
  <path d="M245.792 32.113a8.919 8.919 0 0 1-5.019 1.506c-3.6 0-5.841-2.145-5.841-6.3V11.4h-4.015V9.253h4.015v-5.7l2.647-.319v6.019h6.16l-.319 2.145h-5.841v15.787c0 2.783 1.1 4.152 3.514 4.152a6.692 6.692 0 0 0 3.65-1.1z" fill="#fff"/>
  <path d="M264.956 33.117h-2.282l-.137-4.244c-1.688 2.829-4.107 4.746-7.8 4.746-4.289 0-6.753-2.6-6.753-7.3V9.253h2.647v16.792c0 3.787 1.6 5.384 4.7 5.384 3.148 0 5.339-2.053 6.981-4.882V9.253h2.647z" fill="#fff"/>
  <path d="M300 16.052v17.065h-2.647v-16.7c0-3.879-1.6-5.521-4.061-5.521-2.875 0-4.882 2.008-6.708 4.928v17.293h-2.647v-16.7c0-3.879-1.6-5.521-4.061-5.521-2.875 0-4.928 2.008-6.708 4.928v17.293h-2.647V9.253h2.279l.228 4.015c1.78-2.692 4.244-4.517 7.346-4.517a5.721 5.721 0 0 1 5.886 4.745c1.871-2.829 4.335-4.745 7.529-4.745 3.838 0 6.211 2.783 6.211 7.301z" fill="#fff"/>
</svg>
PK
!<��1�%A%A)chrome/browser/content/branding/about.png�PNG


IHDR,�A�"@�IDATx�����_K`�T��}	$		AB@B��$$		AB@B��$$	�{������6�#�ٽ=Fә9f;��a���@!��(3�)3s�
��k��&������{w�3#�=���,�\���|�Q��̚.����4��C(�PҸ�F��TIwCɤ_�1�XƓ���X�JOI��Wѿ�R�/�����K՗�w1��7R��R���4=(&�a}�;�?wS�c�?���&�M�����aee������]�u�]�7��Bh��k����[G|���ae!�z�Ř\}�#��`_�s��������~�쨣+ { �>��	���HX�k g!�_�k���m@jd��]=��W���ae!�z	�8r�$UUpO����W�:�,�V/����''���6D;���/M���ᰲZ��:fz��?Y	�"�p�<���G_��Y�VU��Bh���[*k�#�]�c��u8�_>w���,�V/�.=s�u���y`c]�!:��P��+f��2$��Bh%	�`#���DH��rG
��]�”��?!�y��
�����Z�|�x�f�W�T��+G�z�@)()4
P	*
���.�ma=�y��m�ۺ�i�J���[���g����������uW~p����V�^y���ssu3g��ڐ7V@N�T�D�P6�h4�{��IW�>�!`S��!Q>�G!��ON=�Y�u��W���簲�;���a�rJ_���ƅA�����G�"UO��cS�.u 	�O[[�	�B�&%�-w���ݟ��!A���~�uW�3��
x�d!4O�iA���/�+�w7��G�#�;�I��7@����8V}�6�xrӉ^�&`\PN�XD�:��
hШF��[�j�i���.�&��
��_�ǿ־�#���`�U�TW�O�z��g'�@K��5(U�od��?�y�/��c�x�.2�:�Hȯ�.��#!A��ܰ�����MA��㧂0��lPC�Po��O�H��(Q�ޓ�����3[�~�:p��?����*<oYY�P� w�'�-��<L	��xMg�����9��}P�%�mP�85&�	6��b�qj��6�YT
�*��/����̼���&���﮼�s�Ŀ@B+� ��J��4�\���s�Ƴ�F����{
?33sQ��iq|Ie��9}(B����K�OW��5[�n�%��57M���&XY_z]+R�JNi@� �B���h4^�
~��\�؜P��蹨�ߡd���?^�^(�)��K��HM;ǣ �p��;�~fAW����Ա�[�Q"
�F��E�J�~��ڥ23E�6�s�i�:�f]T��MCьL'�%~�d/�=��zœ]� �7<8�ÿ�+�K�;���B"6"��� ��>���ΫO<�L/v<���4�U�0��C������k�0�i=���	�J���eX�;9$�+�8���U���4z�H�Y�)	QBB	�)�&$�	�[Vy6M�
(W�� ��������d#�ӛ�I}o��C�3b�G��IQ,���;O�.��!<"�4��?��˜cCKf�G��<
+�-
�ѯMI�P6H

D���.�Xk*�\"XFD?=�9�{��o�����[�x��<�y`�
���ocA"���0���G;�A8tOň�6a���[�������B�b�t�~\T*hd���C��U�o�
ʸG���1S�f1&���̺���,��L*�{��.yC�/㠴HU�/�D0 �E����ñ�D۰q%��	Y��y�n��B����~�X�(		$D6	@R�K�̧��b�P��"�2Ń/���7��p	.��u�G�?���;:�������9�����������򩍕/��!`e!|�����K*�z.
H�J���Կb�, W��	�X-렕����@���tˍ��[�\(u�	k˷�:
��RjB8�� <�T�p�qG��y���� �S?���;F��B�"i�p>TI*(�$rB@"�oq�vr뜺����n�����y��!���Ub���G����"���þ���[�ݬ"а�������9!x�S ��2BxN3b�28g&O�J�O���uM��v�-)X�Z}ʯ8���&@��4�׺�����T�i��9����M�/��G��Zl���+�~�����.%����"�K�P*�]
8:EG��6�C���{�Ǐ�Ջ��M^m�!{�!AC+h��e˜�	�j�,iD�M�q����9���
��f�<d>{֔�ۏX�X���?s$��;���=X�B+
������A���XO%�"#���s�!�Ƴ�
�����O2p�<�T�E���|��ʝ�Eǂφ<|��dҠ����v��}�V/ �V�%��_��D�H���,/�"����O��
�����7�eO�pS�����Fc��C�|�ŧ�x�H���g}I~�G����xݏ�����T�3�D���xw%�Q*hȤ�SW
~��
�?eO:k�o�
2~����
�AA�C�wݬ/B?d���ᐲ��~���߶+
�
B����D����'M�F����9F��� ��u7��o8/@����J�ÄXx�����Y2�Ȱ��ß�%�]�ソ���
B�+��"�����N�C|FJ��m	b���9Wd�^�i���48%`���y�Ɉ��|D�/�]����A�����]��U�^V
�B���I2��:t�@�׃ʡ�+u�ȣ����\�wrRi�P�
��
��C�}= �W]:��"M`ǖ��,1������:���������g$`a���&LK�Q7rQ�lx��^���¼�"ׇ�]�s�҅ �v<H��O���;���ο4��yBh�J�|X0N����IB�(i �p+��
�9VՁ�k��|��Ϙ��'D-ȇR��̓'Uo�9ɀ�Ɏ<�Lt�0noY~�˦��~^ZM��yi�$aO��Eվ�d�K����
��aS8������~��<Tș��1	����c�iỡ�d,��?#���B�w�ՑAhE�?��h��۠Y�э�H�����mU��L]JF��Y�=���i���t„���L��냟���n*k3����ױ*q◌�D����3�m�fx�Z}<_q�d��˜Cu�^_�/=#M�2�B��#���%��%G_*Օ�hsj��ƭ�yE0�o���ތ�
�̆���u�	�I_���_��{���_���ÅЪY�s5.��0`$Ъ2I��K�"2�&�4����K	�B+��0v����������=��b⧀$R7C�@�sO�\��H<9\��=�+KH}}�;�-�>�V�ꯟ��"I1qX����b�0⁨"
7 TA�@W���݉Gmx2��lDz:!�)U�9�B����Z�cZ�ݕn�*�񡎻��U�Z刺����s�O�^oX3�-ȘƄ[4����$a����?^����5��!�!��š!�4��L�L�z��y�'!��m�g��V�Chucv�Hg�=c}���L�3U ,@(@h4�eġ����+{G��{��+4ʜɡbp0�
/"W�֌��8T�.���_�K_?�fKV�@hu�_v]�C=μ���>!���B���1�x�I0�	���P��FN(SX���e��h�x&����ү�};_}\�š"����ud��ڕ{���	V�@h���Q���~�	��IS>��
��a�A�Fr�9��*��\4�Cw�ƻ��M7"]^��(��=�r1�v���q��CE�~�֑;"e�2�p���!�8?���d!~��Ƌ�
�dKX'�{����
�q��s*i��*&�	!ֻ�f��+��!:� �rQ���/i�	ß��9�ap��>��I39Ԩӂ������-U��	�"h��H��C�S�f�"�'�%�j2�U�YN� 42@~ ��P>�f���YZ��j��/4�\P��������Y�3��(�T�Q&~%��m�_U+�� 7wL��@�qu4.�j�+�G���p�B%<��e��T�����m��]g�1TԂrm?w!P$�{š��?��s/q�����BW%S��%���8yV�D����@vXH �4��a"�ɏ1�jf���d9��$^���w>�#m�|�oV�y'�@���*�^��B�(�����mX��E���	��Y��B�P4~�g=_��V�$}�s�$�"Þ�abE4y}"B�^�u�z���qo�'|��;��۸S�He�����j�*��3z�@��/הٷ��K���`H�S��ՇI�yٺs߂74��BXT�������S��A�8\�@(��
9�}-�j�Wdpː.��s�����Q��P�=�u�����&
�
���Om.1U�q)���K+ae!,�%}
o�&�@5	���K�g�����M�(A��T��
?�to�W
EP��1�PԱ�72�w���@�I�> &u%@��%W��[XY�ۧ�vz��ʟ:z�s�8����ް�C���p3"U)�+�S��&�(�"bU��
�c ??�kq��@�I�h�Q ��<PB��;z��AY�I?�>eG���|�Y�)�:j@x�c�Ŧ�2U%ܲ��1�$~T_0,R�A�#�vٹ�>"��sO�aw�a�N���� ��	����ܱ�#`�Bh5o��R��!5r�����t�:�n�W>��lY��������}��@��q���Q4	�!��8�p�|��n�Nl�#
�KwHU4X*��lϤW5����j�@�6'����y.˗�X/�tnjy��HJ?�ĕ��5��V��A'���ƽ��fl�z�o;�%b�U��}p����x�@=&p&���"?�Z�Z�;>W���Q˫�z��m����J��ۚ��<���5� ␁��sB$��i(ہ�}>tҏ���
܋P���:�zL�L�B�֔������5���� �������ݘ@�w�<E���6p'ڠɧI�>�d�t��y�$S���9��4�����B�b]�����[�s���c�:�L�	�wh����^^�9�م{ox��j�h!��A�F����� �>S}�Q������t.�}�)���or���U��_3��ck�v��9����0RV�S��H#��
���j��(�/>�a
�< (���?��)��rIf�]���#�ko�;oj���ЊK����s=�\�Hz�h駖��v��|ƙ(���0�{~�v�BPƹ�	���=,�o7�'E�5:~f#LhL\�Pԩ�5RĮ=����i?��Z@�T�)�	Gj�����~���D�+��%��8��m�B�fb�>$�g��-�.�H��涚�lY��Pt��+�}g@� TRGR&�3�bϔ��n;NIU%�GeS*	�撽�6hh�aW�!�YS���w"�x�3�J��A�2�&�8x��N����T��ňi���Ʒw����C��i�5	��ֺ��ØP֧���s���(ux�\A��q�K�2�����Pq�,�0�#���б��m_8(8�BL7�l[X=�fHY�Ø�-]x;ǔ/��0(Y�.�wnv��r*u,�kNH�����c���}���+��(^��5���@��T�*�Ԭ�y�V��y�,�H� �C�/�"@�z�̎ϝ?��Y�A@F��������4�38�ҍդcNfε�B���57_�B�X��޶��i&qB�lcKc2��g��o|��|5a.w�c�4Ҋ�(%*��/�XC��k�Z�A
@��=o[<i%<t"BqP��e�!i1����XN�OJO�b8�,�V��Y0*p]�si^����x�>i��䖎�~���'=P�U�Wl|�\�e��f��Z
J���I�	�ڷ~�7��S�g�l� 0p̐7�i4��/�g��yK܉o%��wv.���z�?�גI�	�_�aTrV`��B �f�_n]��Gs�a,��fp�c�T`��t�
p���if�Ž-����3n��x0������=Az79��qu?v9�Il��#�d!��ɶ���lH!U(z<i��,��E;s��lF���0�d��V���e��Ԑ����M
�M�@���@d)fر^��_��/�-�����5�$3���?=;}<�gg��W4C�Bh	��s�L�q�*儮�@c�Zb���^���L��!{\c$�̐�;⫻߸cge!~:�f����$!qFz�C�
4jg
5�:��S��^�-@�fȍc��a:~��Dwֱ��Nh!,(%���۝�8�����
���
�6�EU��]A$a�0l4^�P-�s�կX�5#�-��b�{j<�S�(&�@F
��������
�����g�>xk_�C�5T�Ξ��z���e��(N�;�>m�‚2���{l9g�5�U�x��^�P�1W)$m�q�ʡ��O�!�pD_�\�쒕��"ތ�ˡ6�q��"(��9L'�ƞ	ٱA�����O�J���`"��Q&u�n�
0
$~���{�̼��v��BXPE��2j�ɎМ0o8�V�17�f(����pm*HX��mTQt�.��G^}�c��j���
P�(�A�8LrBc�BJ�O-Q��_���<
��V�v�r�'Thܑ�\�/�}�L��܏�X���6��,�2��.U�Ūוp�������&�Zo~DKG"��i~EOsd�aϓ_r1�P�gS��P&���!�)��\�t���E\\`f�F?D"J�!B��h�䄁tl$�'��vz��z{�HF�S-�k5Ҭ�].&x�u��.���D��ɏ��%��cɟ�1�c!�:%;���	�&h��h�B"���M2�e]�Y�$�^�	t���rѵ���V�NPU�D�
�)כ6|kSeGUY�/H��׮i���_wPET���!�A_B���`T���k0�����KJiN��J�Cb���~m�k�yB�����޹oʁ=�	۸մP�f}�P�&�$�<��Q�B�
���abS׈o?|���gz�$0�p��m�L^vҤe�Fm(�,@��-nZq��V,��x��1�\T#PIt�}-��Ѽ��/~��}1~�i�Ǵ�a�B�n��>$�\;���!���jr��ís8��f�w����
6�~���I�E�&�*1�L�a{w�߶�Y�g��l� ���\}���Mǎ{zx������W��{�~���AH�"�\T�Q&?�;�uCʷ���/��F��d�df�<�F�6�a��0����
�,������wB�`��k�{.B3$�B��xE�|%��C.��q_,H�$�/o|��}U �Y�1QM��MRxn����,X]��ބ��T�[µ�j�oy�[~8�,�V?�qR�m�>�m�vr�H!�A��Ɗ:'��f�ȡ�͠�wO���ۮ�rQmNXL��|�g+ �S�g�27o:a�Ξ;)�u��5���i���!���v72H9,7DmF[�H�HKD,�S��F�
�+�K���߽��D*����Es���V����w���Y��C���}P�Y4e�ş���-CB��M_�7x�P�P�D��03R�F�5�:��șf8�%=|'$E�G6�z�O?k�`���8�@vW�A��>��E�7�n<�HL��k=O�Jy��]|�Gg��Bx�N�i��crL`�B##��
�8ƄPp' ���(����*���g���\?�-��X̞�P�9��"۽��'n��OK�i��m�J\�g)����Ÿ���S������P��_ ݻ ���]3W�M��=5�J�6(9|�(�#<�T���"s0P5UB%x��,1�(���X[�7�qߎκ�����;'�U
3UqD�������k�� ��N�\P�(̜�գV�(����PQ�oo?9
i@�(����!�4���T��h;[�%C.(����)��
�Nȧb����7��8�S�ރ'���X?�L�IB3e�Fī�f:�پ�A�;f���?�C�	���O�P#P��0–.~����B��w�̝5P�Ƶ��vr�
Es��JJ��
��I0}P�{U9��a�	���F#��I��,�S9�(�O����O~t�xIę'��Zש(\��1!��^N�?|����)����� T�	��Bu����}3�,�I:�g���jߌ��+a�".�H�f������~�
!d�F半���H�j���8�����o�c�=k��b)V�Y
��2 ������f����)�w�9~���}��}��� �:� �P����;��t��̽�� ��p�.Y�����@{ A�c�2��߷�/���	
�hNAX��4�-l��fh��,:ҟ�����U`+���ƃ�2�0n�;`7?3�O���4/O�

}��qf˜*��;���v���_p.̟���b��Q}D��?��{�/�'�8J��������>nH����$IL���tB+|b�񣽌��T����ÀG����
<7�젠+_y��c��e-uhX@�\1�T�I&��;��[��u�r>�.M��*B����U/�ε�V��ɟ57v��i�@{riT*��	��GN���u{3��d!,�%��^�9Q�H{Zy&jL�_��U$ ��fp㦩__>ϥbnM���|c*��(s*i�g~w@�ztMW��֪ߕ�Z�+\�2�+(*�p2���I;���ӼI�L����5j�!�9�;$��Zy�f�9e!�R)���0�Fs����͐I��q6x���N��A�v�H��W,B34��i��+)c�(�ƿ��+��3���܏�u�ɳ7���	�(f�CDK�$�~w��]������]0u��8N�Ahn��М�hz��X��?xƪ��r<��h�W(R�swI�%���#�z�bǗ���^k*���;�<�m:�&�Е̞Lg|xS��6V�5����
�
��@�*�:��@}f(A�r�a}*0��':}�����8@AI�S{	��@F`�^[cM��q��q-j�8�*��h�%S!�	_[��++a�b _�qb�pB�̌Tqh@x�LJ�5CS������-?����(�����g��h|�I��k_4s�W�t_M�@"�*���,T�/�����;'�*�`UL�vM��k#��PQ�C�#�g��M��ԒR�W�c��<~J�@��9��i�c=�}��w���\v�*��dC���	��(�sN�޼�Y�Ò��*#�/�8���H��Z0�F|JDġ$�h��h���Hw�z��{��E��42Mt��k��G�u�9�>�,�����G��@��+���;&/�ˈPmF�����y�b/�劢e�7Q�ݟ�����pT���dg���>u�ʷ��|tc��\��~Ѡ�Z���.l���
筯;-�V½n��4���`諦C�/W$$��?�t{u�/�C;�<�yʪ��K�1.��`��-��_�d���LN�?K �+4�"�Z/��h��?�AEp��]�,�V�uM� ���\�u�	/>��}\�!2Ư{��G��JA���v����:��k��^�ͥ�����9��mTc���;�O�]��2FM��X��K�O��bpJ  �gzS�ǴL�y�LB���_ر�B�L��{�j�WJ���j���RmP̘ƒʬ���΃L�+O{�rZP�
��\>?=��0���XW�j����s~2G,��	�#/�w��q�^?n�C��&ՓR�#�A�&i��$UM����\J�נ�QXʧ�p3�o�mG�U��#��Њ���ѓsU!x�P�E`�+�q�ɵ�bU��O��AA�N=H����DT��|؇l��L����n#3��ã��x>�Z��*>��8F�C�U{՛3CN|jld3'�:���/?3V�I�ޒ�39�	$H��[�DT�9��ΦY�o�s���I��x��Z]�9��fF�I_s�B��&��
����RP��S���\�QEq���A�c����.7}���	A^������ЊI��=ǎ�2�=�͓�q�TAXdrHc�@�CTC���� �942�$��c�ϲ��!>�,]I�ۺS�~5V�Jý�O�9�4'�s�kIi��!����A�w����	��`����ԀD�(�j �>pTj�gdGa09���fXY_�J�:�Q��jIi|�ɨ�F5349�ω��b�H�sC�FL?DB�d��$
6 �l+P#�֗���e#ae!|�uY��Ӳ#����N�@� 4�4!�H�0�Z��P30�ƞ����P�EhD2��3E�n�q����sCB+.�Z�N�U�Kq:�!�wu��:��ɡ���"�{�L �9!�P���@���y?B���il�󹿙-��BhU!���]0,p9���I3C�f��6�x�HHDK,�jAR.ʠC�q��D��/=���c3�=0+Ȍ��	,�—�=4o�W%X�u�����{����!5��d5�� ��:��h4jR'�<�t�t�-�^"-�V�����ή�LnRk�H�qJ�P���iqRj4��:�fFj��f�F,	�<( c3ܔuN��4���Z��W}�mF9�~��LJ�E�R�q���ɫ�I(��\�����hP?���P(�!p��k���^B�f���m�*$=�Lʴ�4�C�㗴.�DL�	$�8$��j(�]R��C9<�O���G�>�Z+�ngi��G5�C���xW��a܈�~H��%-�b���(B�D�
��1�c��������I�jY������e!x�nR�{Ԙ�a�E� $��$'4	L^L�
��j4j�%� �@�������o��1@�UJ��wN<.WCIC�["�A�)�"�M�x�+��8�f]4� ��N�Py{K�U�F��B��&\�;���Jda�"ӿ��J���@����Q��%���1�uBE)h���$]�u/^��I+῭f䫮�_#��3��Il��cѢ�H$�l0 ,iY��+��Gq@���{�޵��OPXY��U)�;���U8:���g,h?�P
h�h��͢���jA@�IJ������u�A'�,��Y:i���Q	N�;aĽD��0�P��R��ڈ���($��=w���M{�	�^κ~!w��B���j����Ы$D8Zy�x?�Bј""5K�g�-=U�%�!�������=�����e�������'�\��}#j@��y�Y2-�b��(@ ��/��̪��-�ՔpV�S���T�ҭ�V�|�I���Ɨ�j9�:Za�1�2���ՒJ5�0���
��CHٝ�{�Ue���l�xM��ꞆLvmO�9[*XY�k4,p.h8ݫ(�&�=�a��Lu!qL�6QTtf.���e�I��2�de*`D(�|I�o����N4�Ո�yu��l���<P�H5?,��(��4FQwE$@��R'�"p��\��zm΁���[5���U]�U�R��C��:��`Ű����=�F.jE���e���u�_ߓ����GT☠l�_�H�R �a�<�S�<E�����a�zo\�%�Fo�o�eV�U�IrbP��Oϓ�	�L���jNh�ĵ
�@cQ{��9�Ȗ���,�VLb�t��bɧJ�"��,���@
?i#�F�_@��/��s���Y���H:U��3%EP��P��x�ML#@����H�Q҇{J�
�>�U+����B��2��Q?`y�ZYYY��,�VVVB++���U�c�_	R�e!��p'��F?N�X���R��e<�W���3�C�/%��(�IE~%��
#D{���+}�>n�5�/o����zX��'
���Ia\�B�@(������(?Ey�l
i�ݟ��l��6Ȉ�d�#�C�`����-/v��	>�A���o�ҀA����%���wM���z$���	0x���"l��Ѓ����$$		AB@B��{�����y���ݝ���(�^RZl����[�;?���<�^��Y8��w�p�w�wv�}c�ߙ�f0@NV�c�Ʀ�蔄�4<	էd�&6n��V-;vԠ�k���޽�����˗��v��R��1|�С,=��WT�yzX	��;Vg||��T�͟��h�qê�2�H�pP�D}]-�1P����:p��d鑼�bv�p��)O]��U\\��]������nɒ�W/�3f�<	�UTu��9�u�����×RҚ�ذa����~���jii���HI
�?o&m�}+���;v�����jݻ����IR��G��ZCbԨ�/��6l�z�!��O��LEE���ӶM�ifFV�8|�m�7mZkc����қ
� ��$��l����*�q/!=Ť=d������m�1iL�;*%>>�H����4h ���*)륧�aT#��Iw�<B~�d��7ן]khh��-���ˠa����K`鯀Ų��A�L�`�:R����~3����@X[[�|w;�K�S&Oܼ�����ѣ�u�}�k�\�5s*��{�q�Z�%�!� ������F� �!aѺzֽ�J()}|��[���PRN�2i�Ƶ���=���Cuذ�{�p�f�+�����8�n���1c��~������G�N����!���űq�ٳ��ffSSSA����>''?(�� 7f�¹˖.B���`��!D:1���q���K�?�AZC�6m��+�/�2eҐ!��Ң�R�ֿ{�Iaa�F�9s*�ڕDA��RR��v�I�e3��IQ}��cNn$��~���ܢܼ|ls� � �۷��9kTw���LTg͜��U"��c&�>9���L���n���F[���a\|*��ң�*��"5n�hr�--�7o�8:��/(���uZ��1�_lyXD���%�Ǿv�rm�m��w�����
&�ή��_���d�葩�a�'O�+jf11	���g"91����S((�^ZV?7�5�ƌ���x����R��BA�d�V�ѣ��D_��M��DM�0.(�e��%�����=z�2%���Ah�����K��:-5�ò����#�������Js ��{�������Ujj�ɏ��m߶	7-�UM��h6�p�7o%O?: �*�_/�MM��Vr�?k͵�3�9�-"---�a�}�w��Y�-�`���
3�È+X���O~B��fQ�	�vz�R�t�=��7B�d������l#^�؋�����s��U���q�����X};���ƍ䩫k���ʪnݎ� �녂DE���̟>g��3���LM]���8�R���+W�g�^����9y91Y�

J�x媪� 
��ƍ���� ���^��-/**��$T��LM�A7K�£/_�~��`6`@���ʈ��i��y)��F�๢��g�RAA>�<hPJr��935/�X@H�{I1fX�x>�ʊ�����-�="�_���x�\H������̇nZ�A������������p��C�3��::{����PX�A���n�v�yiDR�����p2��W����}�����Q�F��xu��FG~D�LR��
B
B���<��%%���b��WuN�H�ᖭ���^[�?��4,��s�766��0=(-������_hwl8:HK@��-���m��myyw�E�|��
�'M�N�ȑ�;�q0��-!!�z�8���-�0i�������e?���O_ȅ/[� 1>UgWw��Z2w��w"Q�����*�0X}�"�Qp�����v6h���7p$��5��O���3L�4VJaT��ti57799�KI�c-��hBQ()I��?+�/^�r��S'�N�T�Q���2r�{eΜ�D�L��������s��v���ܙ�12�k+�:Ty��߿�@�AD����U
M��쬘(?bu
WQ3�OH����!��s���/��PO��D���r���fF6_{�m�������f�'�I��U��'3~ǎ*�h��jh�A�'8�u���h-��j�ؖ�@���E��
!��'��%wg^���4q�~��3���٩����86LXL
VAX�0"̋c�:T9w���f�xy�_�`��ߥ�fNt=�:u,�`� 8�= �V�����))a����������ը߆��pk[�.K<h�׌��f+��bb�`�{��%���xx�ZZ[X��^Z�y3�J�Z�����q�p� &��[�ӎ7!G�]GG{}��j�1�	�@�7!�VG�-�}.�Е�K��ow9wH�K�N��0a,����[�I���I���椥��
K/���0LIY�h.^��%�_P������zB
B#�K�3(r�ߐ�Yy{�d��2|�^α]�v��I;B�7��CwQW�94�*e������2P���
U��������,A��魩S&b��6
1��˗-L�D�3T�B�HN�9c*^�c4|Trss���/BHAgV2������{aQ�����'������ˊ���+��/ݸ�ڕs�F^��ֱj�9R�������~�>�b�����
�֩4
B
B�w�� .$�s��e�!����ފ*/��;���F$�\��Puutv=��@ޤ�ԩ���j�a�c'��]��>��!!ݼi
������ӣŽ�zJ�>��M�e��W>$����Q|B
�#�KN�1�S������g���'��(S'f(�&�B�%f"jǎʣji}��������7�j5b��G@�͝����pH����]+��
�D�!��UsssȻ��o$�8e���}E()W,_�G�fg���#����s�����Q��إصG��{��5���55��[����3�|hau�o0���PON��4^]Ʊqɨ��PR�D�_�z	�������H
���5��B��,��G5$,JO߶Kc8���7|֮YFsj\7��="
K/I�s:��Ȅ��55U4���x:��c�|����e��L#�9��9�4�ذ��[���L�c9�D�
��!�U݃rrnq��mﰶ9s�b��Ç�Ɋ�<~�teU���B�I��7���SN����t<���X�/^IJ~��a���@��f'�%p�UNQ?-�.]!����h���7�e��#��G`f�)��n�,aW��b	w� ̠]�!Aθ@gGcI	��X�����5�u#G'&�-���5�O�L�<��ú��I?C�̸(/_~�|5�ŋ7��5'������fϞ��|�����̌΁�Zځ��[���,�:�D�8N���
bdt�ܹ�/���$Wr�w��Ⓙ�m~�v�|S�0hkkaBM
9}=
T��RV3~���ai�5����_�|�m��i�P�ۜ� � �j�h�Q
da~Z��-d��AA!W��
���ᳶ<����ɧOYZ:��߼E�gsӓJ4�Pή���_�X��
\�t>��a�����)����i�ֵu��6��Ap����n��Ռ�LTgL�
�hwBB��b�ll��j*�ܹl#����Y�n$��ݨ����+�<{|���7m�=�8a�cGE��邝�	lq���/�Y�,,��[rr%�OTT�2��}�:�ᣍ+a^�~MA�o�~�7��<��>�--��?��.�/����ׯ_5�pw3RQY��W��Շ���?������L���+�X�x�	��-����ܼ,��A�^�O�D �п&���̽�k3!`B0!`B0!`B0!eB0!`B0!`B0!�pL&�&&&&&&&�	��;�a(Z�_���H�z�pc���&�l�lY2�.F��Uu�)������z9�
�Ո�����8��V�S���H/B��]��I�r2�IEND�B`�PK
!<tߩh��/chrome/browser/content/branding/aboutDialog.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#aboutDialogContainer {
  background-color: #202340;
  color: #fff;
}

#clientBox {
  padding: 10px 0 15px;
}

#leftBox {
  background-image: url("chrome://branding/content/about-logo.png");
  background-repeat: no-repeat;
  background-size: 192px auto;
  background-position: center 20%;
  /* min-width and min-height create room for the logo */
  min-width: 210px;
  min-height: 210px;
  margin-top: 20px;
  margin-inline-start: 30px;
}


@media (min-resolution: 2dppx) {
  #leftBox {
    background-image: url("chrome://branding/content/[email protected]");
  }
}

.text-link {
  color: #fff !important;
  text-decoration: underline;
}

.text-link:-moz-focusring {
  border-color: #fff;
}

#rightBox {
  margin-left: 30px;
  margin-right: 30px;
}

#bottomBox {
  background-color: hsla(235, 43%, 10%, .5);
  padding: 15px 10px 15px;
}

#trademark {
  font-size: xx-small;
  text-align: center;
  color: #999999;
  margin-top: 10px;
  margin-bottom: 10px;
}
PK
!<��at{1{1+chrome/browser/content/branding/icon128.png�PNG


IHDR���>a�1BIDATx��cK����Ov<���m۶m۶m۶m��k�1��t�]I�LWeu5�7o�[_T|��NW��4��_�8Z�p������AБ�3���S��7%�CI���
>G9e�\�ܱ%�#�qej�.�~�z�d455�E˻�1��Ԯ+��w��NU;��2����xJճ�.@)3�Tk�u��ZtpS!�`�c��4��K,ȧ��w�ZUOj���# �`*���1�$�?�{��n�Tg�V�E2���`0���1�$��s��i�9q�$*����L��������XPN�[�]i���AA��a\d>7
�N�����'L�FK'�ꈒj?+����r�@\�]�ϩ��}'���;����@�}w2�T�� Z�"���|n�R��%����"h���
�K��	J�J��k�W@;P���f�}R�;Է/;.��oKv_��\�Y
to„� /�T��3t���"q���h9[`61{��k���z3���:�����ת��"��A'����r=��
�uAj�m\�����r���f���Ph>��Gc�|+����ooTv��c���8U;h���hw?�S%�M<i�}_�߂."B A}�?o�0(
m��9��l?�Gcc9F7��)H�z�]��Ch.��8c��׮Vi�T�B����_�v[��~�����~�'�O�*^%�(v���~֢7�8�#���\���f��ɉ`3�D��]���p�_�aY4�o�zv:<3��ގ�1�G*>��~�ڟI�:�1���bms���x�����&�i@׾��O��~x���a�Kҭ�䢻�Y������~�m�z�^r�����q_4`�y���W�����:�V�XD���˺�"@�u_�X{��\`u�X>@��$�f����O?!|���f��7 Ƒ�Y�"s>H����9���PVf/*`��²?�n���(W����i���'��C:̭��~i
�:w?I����EH����/����׿.>|,��i�X���eA��{�a�h��?�om&��q��ۥ��W����7��
��(�,�.^����a��jO5x�W�*��Olx�l�L�a����_|�^��BO�$ZT㼁���_@�f
�����V�V-��.[�]��t|���v�}ꆿ��H�X�!�������]r��,��h���"�%!�ً��e�b�$���1�>�q3�Ϡ��B	FcXB6bz��ֺM�&��H�lw-1�b6'b�:
��J�K-w_'��ݱ2[&�}��O~nZm��rӔ�\�P��L�Zbe��@�O�Ρ.�5�]դP�?W0�0�+�+�D��eNh���r%���)��Ŏ�$ۻ���'Q�e��kC���&�>;G��Z�>���bך�Y�l��^M��XD;���{�����o�%�@:�9!NӼ~7R�I��@:��X����)�� ���s_�.��hu�Z�>�
 �|`�O-���a�
���7K��I�%�ʁ/{����{��L�K�_ٿݣ��Q��Ѐ��<u�E\1���������N�D�6o��-I�J��:�af�c@M_+ʪdZ��7lީ�D?)��H~��L	p �:_1�}w��<b��/VѮ-N}x�S�^��tx��ꮤ25
��7��Q'�q!�O>�f^}%`6�&��]h�4��s�ͥ ̓`�P`���Wu����[UsPJ���>���@�*�Ӷ�FYyć�{q�.���)�1�t3T9SUe�	Z���(�u��k�ȃw�3!&�,�l��üݴh�(�
B6Y�ӡ��]l�a�����1�iư�,ϐ� ��WU��Rl�$FI���Տ��WY���<�1mj���7�S��&�M`N�e����*^�2�2��	�U�\�x���O������y�QZ��y�er�ͻ!�H������~�|ב�p���0q]�*�?s��_�U��v�iT��عF	�����ۮ|�Y�=ڿH�!�EOV��8oL_�.���R��{���{g%G���OUw�̺$!	�H��w9w������wנ1������LK�������fz����{����g��3!�Q��9w�
�y�7��a�b�.�w5�`�o��S��5��Å�f��X�H.��.����>��߹��|v���%����Z���2Aq�Tr%����j��U>Sw!����ص��j&�e�S�4��j[��6�S��]4����g��
��w�k���̟� g#�|�T-m��q�3���/�:`'�;�D��G�Y?l����,��poc�{�����2�+��q��͌٩�+>���e�M<ΰ��rr5P,!v�<)C\�깯爭owp��l~��Ɲ$���n"þ�,s�}GC�w�R�+����t[�u<'����$P��Ϗ;O��l�}>o���6c)؄�s�?�+�?�<���2/�E�B��񙧦b^i�[���2�m
���n
�]J�:�-ݚl Rk��'nfae�
�A��B�DK���cwB������}]�Pz�������+
�cC�m�*�krP$���aC�[�~i��sW���2v���z�׏�qp��_w�(AV̟�Ƶ�B�C��7Sx��Y�*�y�z��5��W�����;�}s�W1��m��?[l3b �ܸ�Y3��z��.�\��
!����� �K�>›���]�ۃޥ�0�-|���k�㡏�Y|����k���g�����e�akO6�����&�����A
[�ܵJ	�
M�"]�Ͳ��J�J?P͛�߳���罏���J�D�����G�n��]�|e�t����/K� ��âQ�n-����/H���7�f
���9��y8�n6Y�5_K0n�����f�/zY�{�c��s^4��!��V��0Վ<�!�P�7��`a�W��S�X�p� �@��0{�Z�����z'mA8��^C0vN兤wœ[7������l��S9/|�&�Ơ�o�����s�o��}��>D�faa��yVo�Sl�'�x��3ݴ���;#*C/(ã�`����wџj���幝̆~�w�:�e��1'�j�zN��ڴ0���F�<��{�ǎ����ؑ�4�y>`]���j�(Η�\I�)d/F�L̬��<z�6�)$���V�����fݢ'Z��l>���ۮ-����:��,�	��HU�B�E�A`�6
�hXk�0�7���Л�3��Ŭ�ڲ�w#�@o�����]̉�^K���T��?��,�uO���u��[��F�v\��e��"#��\
���ȶN|
<:
Bx���p ����Y�7�	�5���n�*�2kOڝ&� �(���Q��{?��
�7>]7����v��/��?�+�_�.(�^٧w�,򲭇�������NF��G��A�lu��Bã�J�Z��sи����ע05/ĵ�e�
ɚ%���`A�k�7x�5�@����p3R�Vl�z����"����[��Ӏ�)�
�~�mS�	.α6.�z@�U�?�g}��]n�`���x����5}j�†����溂W94�*N|��?� A����H���K�΅�97L�[�-��(�[ b$���k��vnj��ʝ����@C��R�	����o�ݛ��-goZ�yh@���~oH
�-`��[:H��Bg��~_�k��F��O�⥟���>�Jߞ�9�.K�{�	��/~���t���~Q\�k�{���ӻ������z/�"�WVYR�-`�Px��
�����~&��^$|��d.]�Q+~��04	�b��Zc끰�&ᡓ�a��E�o�?�����qɨ9͞}7����ž�[���v�+��-�x�����ޟ������v�q�(0�#Hr�@M��+��u���'�u���K��/�ւ��
(�p��'3���ꛃ�q��()�fy�Px�U�O��y��{���S���7.�[뺀*�*��W��A_�E�@���.��;W���'[�s����kE�"H���.������r�[�o�M��T~K
�lq�R���Sk�x��^8?�U�� ��cW���a���K����"(Z��\�_D�𞕗�.i��>-��䬄D����(�B�����7N�Տr3�k{)b�y�%�MԼQ�L�����i�U�W^�셻��D���DBRxxT<��Qt8�]y	��+�g���9�p�t�p�й��Kg��o��WI(v�`{0��}�`�^�~J�O=Y�7Wf�Ny��6z�^8n�z�l�������׻pުw�*E��[ƦЀ�K�"�9DZ+|��t����_Á��d��ѐ��d�Ix�(�v�O�}������2�k��]S=���]����;�7>�&�򈵏�7�䛈=���t��U@@� �h�j(���Y��\��*0A�/�Ѝ��	*WM�@q�Ju�۸4���
vV�e�+O�E���5��'���~~�a�@R�2���=o�fB�����y[�
lX�����h��Z4�Aa�-MM��#���܈�&�����>}����ΫL�y!n�R�
|��ĸ�l��k�ok�=w�C��EF
VA�z����50
��C�5䧁�
ҫ
#�&B�Ӿi= ������Ҿ�+6����+�j_�]� S�{o-=
-��d2T�鵀��Y�1��\	�[�����ixhv��ȹ��$�G���w՚�]���9�W�,��j}ue晣���9�ċ.��n��ʳ7��V��}�%|w��|3��
.`�~F;(;;@�q��}���B'�߳��1��݀|�H`!�k� =�V�~��Lt
�Q�+?�	���Y_j�+7�?����^��*Qk
H���#J4F����
ACH�
��W��p�u�U{C��
���E��"��77
�b��
��Ϲ����W	��@n$��M�<2�Uk�-�w�-�O�~���n)�|s�W�ϊo!�u��@���5m3�a]o]�:��
�x�Hƽ�DnNf�M=s�-��]�A_\�L�>�:%�,�ۇ
y͌�$ܹ<��-$\4�w|����^�Y,
�t�
_�5�Mݘ)��f�uja4ڌ/ܩ/���+��o+�.�UZ�E��N@��R=��?���ZI=Lt_[{{4�>�p����Z��U���?>��|~O�`S
!��0Jg/�=#H��]+a�3L�O��8��b,��T����C�P~U��Qbm,�0
K��;Ns��?,�2a�V���/��Z@N|k@ƞ��k˺�X��8L�T<����+��`��ȄLh]ɓ��A��z���^��>���d���Z$ �ǚ'!�X�#߼�	v���K�z	i���`����3��>o�����J'�Z��j������œ]�����A�����my�S�
��^�a�w�&&"A��u)��z�P쵗��#��y����M9�.�6��h�K���i��_��;
fva�~Äm\K�����^ܦ�on��/R���m7���Y[�������x����$�ߕv��> P.h�x�0A�M����Vl7��^w���a���ݫ���ϝ��v�/�{�n���J@A�D�u����nU��
]���۫���;�S�:|Gη�?��G�<`D�ԆV�C)��Gݻ����� hDzx���.�뵃�Z{�ZYH�9ŵл0�����ӿl��<����qQ@�~�����CGܬ0�s}��	M��Z��^xzޫ��68A6
h�/0�R���VQ�Y�w���y��ǡ��_/���z��lPhOz�;W��n��F^ڣ�zV�����o.���^�`P/���8Z��)�5}p�'�:jBP
�o={�3���(/:x��$���.���
�o_�WOO)oE���q��y}��}��}��ӄ�Ya�\WC�ډ?t(i��@y�wA�r0p�.w�7����W���T�U�� ���~ẋ�u�����[�>����:M�
��DЦ�Ꝁ��;�I����h�o``|m�<�����z'�S-�	�Ax����	�
���t@�
����6��s�J��A4�Ea�[����Oai��=L�>~RSU�ҽ�oV�W���e[���~��
�74���A�@�*���,흊I��l��ʼ/��Dz��@
?xn��U���X���~���:�)���6@W�:u�	����d��;q���{߷u=��(�Ük� ���^���Rl���Z�O�M��!�P�|.|�,�� !�8�G*�N�z-R*kq 
B��az�����������<���F�ƃW��u
3��q�y$8y�ܪE�/��9��Z&*��_��{A��g�m`�hz��2磼��¿�ھ��Zn[�3F`Eu��Rķ��k���-Z�L�\����5$}�8�k����_�\[H�����!�&�w@�5ǖ���9괓����ݥm4z�iڻnH 
A�L��GXTM�?p��'�
�'��``����9�}�<�zj*���N�j�W�8�V��,'��1|����(~x� �<�e-�O?1pG��T��g�x{Օ@@�o���R���� ���( �e���ER���� 0�rΒ���b��p����ƽ'��8�Һ��D�P�a|�zZ�x~�r�w�#4�4�@���[��A�<��g0Q?�/���+~��9�	��@y!��u�o��(G�
A��FA��.oz��of=��O{z<zŐ. ��łP��
4(T�s�˯��	�
Ѱ��%�����m�'W	���-�I�����)��e��r��3��c�����/���/9*����};�ƭ��@1M`;2�|����#�ƨ5]޾c/�yW
�����ʼ_n�
�J� ;��'�e�����*bk��R�Hc���WL��gv��5��o�����V���%���lݺ<@�!�)X���U�]�=T@�7�P�]��y�n_�¯��+��*"�Yu@���2� Q`�����z�׷�	������w�u@ƒpj;(���;O}�̊-
@�q��1�
ѳǯ:�w���D���D���Aط0����|����]eS��xT��k*�ˆ������Y�P�뷭:>N<rŠl�Fe�� �߸�os���O�a�Mu�S�#�x|.��{H� ��Gwk���D6�����☹����*(����C��k���W���Ͷ���yϞW�ռ���1�Z�� Ӏ��� g��ڰ��U��W��iה<����
~yߑ��;���s����4�$�7�����C.�����(�X[����
���w����n��͞=N���_˵��ЈҀ2RD��	X�]�7֦�~n�����������|R�}��q�Gq`�`��|�ߧ��@?@FH�,���TLb��v�رo�鄞��]V���	�VKE
�s�C(�@��
��n�������8�\
���`�U��"N�35��n^4�K~�n6 �?��+�ri-a�ߋ���`���w�y ����$R|'%����RY����y'���
��7>x��'[���t�v	�fp��
?��C�(+��Vx1�Q�0���+b�����,xa[�r!��^���6��˥<�~�"Pam�� �=�bL����/����Σ���"�'R���\������~��p�?�޽Ϸ���,�Rvew�ـ�.����Bb!�bC�{e@|����c�
԰�a�~�t�qÁ�����=]��n����/�!����P��p˧��֣���zX�.:���P�w�$Ib�_�n����]"�g��8�u�M��Yu@�:�SX<�$"*���hMoy4Q�C�V\+��CA'p(�M�=G���ܷ�ۿ�Q���$�BY�I�8oȏ��{�RR;��ˈM�{O��ˎ����(UB� �s�H�)��7_��%ŏ^�wy'�����K�^%��AW�n�{I���&�=C)j��8��('��a BD�s6�=�q8,v�[V���{q�I��u@�o$��vU��@{����4��.J�NfO\��=��#��[����Y��@
�'�z�a��?6����V�~ӽ�_Zu�vY��y��,�_!��FD"���bi==�(�7�Č��.*�y2l�k�ua��L�M'�=�����yuCw���|CV
�0��]ZIS]����ێ�w[�W�6� ����M���8�P ��rA�Ź�L4�/ɵ�,,�A��2
d�wb��}i����1���)�� �s0ll�*���<7���˷"�V�]G���Nx)~�H�$t��B����2��ꯍE�:;͌�J��̓kꎟ�=�a��ھ(ҟ_�͛JSo�$�erN|�
�B��(��7��ua��]$��y	;4�3.�^�P(��>�Y]X��@�x�eS
_�4�F|��ۼ�<+�;0tU��2�<��v��C�r:�v@V�� �?/���•����#(#
l�$�Q����r��™Q [t�G:�y�-��!Ije�q-9����t+��*~�Ə�+�@%��}g��9�?(�O���x� =�;���du���>2�/�k������<^��`HF����(�&��{��֌�3wͧ���$W��J㫀R�/�r.���b]�S�\��{.�!�Œ@rW{P����ߺ���k��ph�C���7�h��: �`�DP��ܺN���Hư`�h>u����:҄Dѧ�;]x��h-M��W�����ŗ�_脫�Vi�aQ"�iI뾇|m���k@���^6))|6=
Xkw
W��Qq��n�q��,��a���d��P����(Eh":�����8�ڛJ8�3ď�ӿ��TY�]z�]WwS�U{T������q�V�.ξ�����
B��i���t�嵴�����=9��=:��J��!��Az��7飮e�>�v��\Q
w��(��s6�~@D�O������	W�Q~�g�wL0�f�
ʖ0uӨ�(^��������f�\�dgJaP�\D�
~�j�:�>N�o�\�-���/��*������{���U���5�].�sP@0+j���]����H}k�B �_(�IOx� �:�y1����I���4���K;����̘��#�>���=mW�rdC`�������גּ��~u>���<��k�?Mz��E����\:5���|_ ��^��2dz��U ���\���y|#O�ϲ
��Akè�
L��;O|������撻�Z�^w^b'`f�g������ ���/νb�@�	F��g�[t49�"�!�ge��B�#S�S�W��K��eZ�V�u���V)F9�*1P_��W6
��c��D
��k�Ȏ�����*���0J���m�#�|j���,��`�NG����B^���ض�na(#X��B~��;0tU~8Zl�>'X(�zvH�v�
s��SͰU?椧*�C4���|����{w�R'��;��wɮ���i3�������\Xm2�ר��⳽d�_��஭�~�:�	*pw����s���E��1���]�?����4`!ȮdW �a�C���k�\X7W&+d���U�lD�p$�����MC��k��
O�m8`�W���>�������w;w'S�� ����[��␈��(�2 �s)��B|q�A�ϯ���H��үw�k�pʣĄ�~&��g��-�,����~0F��$���.�u[h��h �W
�3�>#��/�@@`�ؼ;���I)G�W���\N~�����M�ٷ����K��c&��4�
^�m�єH�BWء\Q8��_u.��g��'��s�q��tdüKv�T�n�I�����`:��?����)�����͈��Ҵ���Fm�
�\��Cr�X����82C�����:'����_���YG� T�]��9���/_v�Ĩ
{�l�i�n�p�� ���/g��3�O�n`�Q��B%��!p�\��R�	��Rp7ďJ�90�l��u��>$V`;d4���l��κz�Cn��+	�K9��Ź�^y�}�����s-��@eCP=�Z�V4���/������gP�!�Bh'��!�}U��ݬ?eW����z g�����V���[�ZDG�
A�j����k�y�A�������}�C�.�Dw=�}�e{�v��}N�WY�m�ԏ�Ri�ڕ9@.�4 ���K�!�6	2@���^X7w �<wG��7R�^�`0�-���=�/���
��B
3���>�?퇭:�cXH�*�V�u#/�쵗�T@��lf(!~)�؉�bQ F�|wɨ�/�c�R�=h�7o���o��d�@�HO6�T  p݁�>[���RJ��2g�|'PA������D=t��SO{`}�j�$��@@иmR7����_�̡.8<k�
af= ZD�ȗz�����t��'��x{Nl�׃Qċz��_�����	ZL0�[�3?��R�s�@z= ��t� 0n�����~�>�a�]O ��"�iF��|������;�7��~zp�,���m��y��3С���/�zq�J�\�h��z�ῦ�?-�K+#�=����	bE����Ѝ�~tېO,������L<���ż����A��
����%c;HO	��FF�i@D�DA���Q��J���_;��Vxz7�;�s䀖S���_��\�N清!	AZA��H�t0�8)6���(1��S#�܊/�=;�?�K�_�}�v�t�
�T��' ������N�b��.�H;���՞���R�����(���^5���t�T��f�;���~�$�O�e��@>;�)��6{g?��%��<.�VK��*]4 �*a?=�g���zX�O�bU�h�0��R{��������</�"%䁦Q&h�z��e��wi�#/u��+3j�`
،�?+�G����U�cѪmx~�x��Ӑ��/���/]��[�_
�_�H	��h~k<nα4|�]'���[\M��R���Wʖ/��y����[�,]���[���)���0uܪ���|��%�g�����G����d�SwS��:nw)!%�����e4� ҂A�G���?iK��/�X�����/G��s�m�KLh�b|sW|�����+@�[�@D��4M5�1c�YS�>?��f/}}@�)� �D��!��ώ=1,�XٻIpM���\Bs.��.�!AA1�nC�9�>5�6�WV����h��B�,����5Ӵ>��㱮+azJ�0���F	������cH�,�q�sm���_-v^ ��F[�M�T��]�3۵��S�r5A"_7���
]4 �6k�On1����k�����W�jY��_ ���
ԟ�w8M����&�L��괔 @�Z�\T�q�OAhL����w:~��uMK���g����,�m�g��c�xf�&ev
Th[�h`Ј��U��t�Z���2�kc}�#��W�k�qI�����D5�y� @pu��!�iLQ^������7F'ӛ���Wq����	����
��!��A-Z����{�����D�%+:��W��0�97��G�j�8m�i�8�t2�^%��ʌTҴq4je���Lh0e�L)4�3T�#4��ۨe�zaY�_��,�^)�ʊLy��z��'��o�yv�ᮥ|g�<��Z��
"g��9I���!�P�~$��n.��H�_��
������b�
�����[��w�=IEND�B`�PK
!<LF*chrome/browser/content/branding/icon16.png�PNG


IHDR�a�IDATxb �_i|�KM�
n
G�hK�~5m��Se����
�I�r-Q]ݷ�x}۶m۶m۶m��m��XW'��쪍B�`�����^x}��Y��'��q��M��?r���X��`�^1���I�\�q���:-;o��F��c����y�-\����^��}eѕ���\-\�\�ۖ�Cv� R8�Ï��"�蟹ׄ��@�M���#3�\�b�Z��#��R|��P�f��w�C�a�䆟�i\�j�7������<��a�ڇ�<5�Jf-�������)-c���s-���a��О\?���V�Ϳ��ه�A��fc���eF`5\O3sA�~�]yaũ���
��q�O���C���Pl��b�;R�e�x��<B|ş��]��n�Z���q��p-el/4��u�)�A�Tyx�O��Bn���u}���5�z����5����8��v	@9�`��沰��aG�t��,L��?�P��_k�B��-�	<;�*�ͧ����O���k�������#5�@({B�q%��ȸֳ����"�m-r�"ggEn��<�E^E����÷��P	@w/�����Z����\��q����aX�!�P4�_����_�d�p��-3o���H��:�5�ņ�C�1��Ţ�C����
}C�m8#s���u���n�]S��B앳T���J��eS�rt�;�_���IEND�B`�PK
!<��jaa*chrome/browser/content/branding/icon32.png�PNG


IHDR  szz�(IDATx���YG�{����{�t&މ׻q��m۶m۶m۶��1j����9�z���S��}jW�?,��֢���,��ŀ�OJ��g|��4
��v���#����un�lrK?v�Avh�=>�ÞZ��n����kq+Rc�ѣ܏߼�s�k�S��hɺ��W��օ����W/��-o9��f�I� �@JB:,���A4 �K�&����9���vd�����g= >sr�9Vt�
v��Ȉ�>�w��>[�M���$UTe���,\�n����"=�1��B/������4��lE����Y�����n|�P��Z�	ZJF�������X$�%JW%Vν��댩��Ī���
��	����r��|�qM����2yQ�*��]�köd���sZ�|-���1��o�0	����k�?��V��]��
G#�6��:���,���
z�Ʈ}�G�B�k۝�J+��#x�mI 3'	&�&�>4u��g}��=�$�E���k8�m\�:1���/�E�����!�6��I�5�լ6Z�~�=��4/�(�-#�K���`{y�YvU��mι}�BDB&�u�m��m��
&�j�����^�]ߞ�Q�s��B
����IE� �E�X#~��W�������B_�?�x*h��P]i�x�]��FO���E-<Q2�H$�2�1�n22��P�<t=�?�
m^=�>��y%�
��-X$����+<���n����w��W�p�� ,�K(��G1��i�^���C��׶����~?�KD?z���t�'�E�p��HA)/�"h�$�Q3۽t|x3!�>�%��.����iR1�I�������֛��O;c�t�H����]]	���@I��f���	9�}9F��ASF�{u�������DQ�@J��7S�����}_���_����I�:�;�(x����P,�k^��A���}?CB 4Y��VAlt�'"�t\��K������gu���
F#u��C��%��XF)����B
��Y��s ��m4 ,�N#_
c{X���
\
V@�
il��.��vgJi ��Yx���7z�9{<����SFa�D���ߞ��dB���y�����v�i�*T4�B��k㛉��g��h~	�4��t�.x��~�ͣ_����@����|! �m�X~����
��� J�ali;:�V���,�gd�t0�F>�����������}�ު@D��C�1�M=�%pꗷFx����u�8U��(T� A,��0�å����] �D�1s>�xky�k],��j�
��NX�~(y L��h���g��0��3�m���#��Dl���T��5���N��V7n�Z�)w�Gk� =��R�L���|�5;]� ��^��c������2�K�~����a�R���J8ĺN�L�b�*p�JO0i�4���>q�<��IC4��{�;�;���BhB��`�٫�+ƢV��ُ޽5����0�|:,�R>�
��F�oC�Я>��,��yu��
�%2�T:;i5I�N���)Z�$��F�d�zmd��Օ�e�U�1*D7(a�)1�W����@���@�!�Yt/o�F�=���?�2��۫>h��QA���Bc哕`�e"	a���װ�X�e_B,�*���p�F���)����W�4�2~�ɖ�".c��L����[�F�ĐJ~�(�,]�*E�t�w���㹑���/���!�w.=�Ӷ���B)�5YR����$\��[5�WS�ǔ�Rr_��{N-�m@	�O�$#X8���������Z�\��*D�M��$��-�l��5�g|ۼ���_E���{NE"�����]�إ�G�Zsi�P��U�ɸ��������l�7�OO���r@��^��(f�L�{)�ID�3D��1��b�S���*4���Y[EHO�U����2�Nɽ��'��D"b'8�V����m�$�{ɹ߭�-,3��lIEND�B`�PK
!<�^�*chrome/browser/content/branding/icon48.png�PNG


IHDR00W��
�IDATx^�{��}�?�sf�ݻ{��]?�ϵ1608%@0$��jH�F�!"BmhP��F@�4$Ti�	��(	�MH	RR��1cl�016��1���z��ޙ9���H�Y�d����ǿ�^�����33Z���ٲ�R6��t.��s��*y��q���
n.x7� ���U��3�"8��JyŖ߭���K���kU�v��/
uq����T�tsz:"���ϝf�~yu[�E��iT'�vɆ��S�o䪷W������1@�}��7���o��z�NG�@�H�3�Ɇg��HW(��\��w-�r�E큎�Og�(0���箼r�Ǟ�h���.� � Jw�)(����M�o�n]��ȑv�z��S����S�[���O����s�\���v�5~��vߟ�mӶ:]��U��Vi�|"�]&��'?�R��H�)�.���tIyʔ��i�#�B>[�Y����U����S�������.}U����'
ۤUչ���U�‹���eB�  ��)�
�Щ���?Yui��HV@U���	X���<��Aкԅd�v=��nd������_M��W��A�`ѡ�{�ғ��c�,n���#�(��Hu"�>�e���yی�'~����=�:����g�뻂�=?����u�I|`�#�23�W�pÇ>~U��w��֗��Q
��Y����$��!:��#��R-�����Fh� 4�U�+^���@|o�R,Dq�ri����}P��nJ3�����mڙ�:�
���)vv���CTU��<6#������Z4LJu�V�y��u�/32�����j*�}��z���Sɒ�����{l�v���z:�r|h��ܹ��3�d���^��EJGW�{��ojC�b��	&�ݩ�Z,�"y�Q���"N1}l�W*糼�K�u�vΩ<N��}x��쵫kT�Fo8((�X���ՙ���������3�nUb�`Q⼦�ë��*������w¯�=�1�`*׀��
T�=#X �x�DQƁ�"�-��0����w�:�}y(C���P᫧("�yw`��)QK��$�<,sǶ�r��G8��	�;������G�m��{�P-µ�C�	����\~]�><`S�W�[���`�H5��Р;X��ٻ��:4փ��1�S�߿�n�{�',�4�!�W�B�
Մ{T7^E���<t�[0�K�\���z���> �ڳ`��@�BO��й���iC������g��k�˙��ܼ�Zbi���I�ɺ4��ts�o��6c@<
��A�����֙EI�T�t�G~-��knb[
�޾�P�Љ�()B.c��L&��#O��Y`�Lt�eh����x�e��Sj��&����'�L�<p�D�Vj#u�G01�=�4 �8"��3�1
�G?����T�`��U�hƶC�@0%�Zu��⓻]�I���qih��VG�����6B~�g2���[�}k)���E3�QJ���8G�,�+��/�R$�p�o_̒�W���@	�	�T��I�K�O
���1�)\
�K�a�n*��$�2jUJ-��o�H9���s��)(cHL�CĎ�ێ�%�������:T�S����^ ��u:nB*����o�Z�h�$�[7�(��- r�ݥJ�[�6	��I�F�Mй����(0�.�J��NI
��{�1�:��IM0�8�H
�i�$�U2)E|焻�¦��G��@�� ��X��(�(@ Cl0H��<� H����7�®�Jc5���F(;��܄�ibYԳ���&��z)[FfgcӤ�j7u�c��Ά}Ǒ�V�T')�Q`��%�t�g�1�L���:���QI�'i�&�թ����!kUQ6�-�"�c����%��z��u�7��?���R%%��{��_��3�����}��B&��s�/�R�@��ԭYOQ�%=�y��!�!ƋX�
hI���)���0fq�F(�.�<'M��=W|�.�AQ���^.^�"2,�<_p1X��w�NJ����ni3�؆����TD(��
�����h$
21��ؾw"�v�c(�@�>�S
+
DHL���Y��Svgu��(|��"���]+'?�\`�1G�bS�=�ҼؔHL&�75�d��d��2��h�F��Q�:����E��4�W����y��pҊ
*��Ѽ�MJn'_���y��]�xl�Јz��_O��@��W����g�DdK�S	�-�L"�M,�p�R�0�|}!c�N:ZJ-{�b�H;+w�"�	t�2V6������n��5a4|�:�<䫁��$��ҽ�Ӧ�ߵm\����wULyf�
Q��D�*�*�J,DYx��"�q���ṳY�j��B�]M�����:�o��7zKn��K>>���,o�w<�dى�oоB�l"�r^ܫ��TB�L
����8�C��*u�|x�6Z[k���K������h�]�{��W���9o�c�#��1��v��n�Խ���x�A��ʫ?�r�/����r� 2
B�
i1�qHC�>>g3��̓{���ީ���P��z&2���-̞:h�{����U�����+Q�`6û�vc5��YJ�*"�1Z%(�VBG���tV!��s��mВ@�dx��h_m!��WnG;n���/O~8W,�@cG:J�қ[?ژsr;W+�V�GH�*1�x�I^=Xɟs-9&�!o�T!iol]r�	�Di΂@N*8�]���\QۦR	�	d�gf+�⛸��Z�/�E�����)�D!8����Z�F�@>���*�>��Jڟ���>Q��=��y����0
\V3U�i(
��3bm�6���=:�E���w�*J���}*ܷf��T*W�� �>Y?�F�pRX�$�n��b�,��6
��<�#�><����9��ݜ�?w.�}�u�ڸv̒s��_)�]"�Gu�_5Vx��(��'�"���N�_��HC����<���^�{,��8�@Q"iJ��se��[��g,Mz�#�$��Cp�$�E�<|d!�ؔ<�����j�hǮ�v�.�iӔ�(�(v�p#T'K�l�Y��ݧ�W,2�c]N��4�+�XA�|l|�x�ؓ(l-�6\a�3xt�_��Bp�����fxp���H��A�����u�sڑ�"~/$~��H4I�y�@�����5�%;�����$߰G.P�0@�
�`�1��NUs�Ђ8AH"��N+�(����Uåϝ�Z�r��w��ȑ��%�%����,
����4���6ըj1����jN�h��P��]�3�
W���pe/P���2��E
��W
�C4�zO��.l�#x�29yx)ܢ�X<��G��������pIEND�B`�PK
!<���00*chrome/browser/content/branding/icon64.png�PNG


IHDR@@�iq��IDATx�t�ǿ3�{��I&Ɏd��?qc33��ff�27�0��039�kf1���v�i�=�j���}�o��p��f(���J:�o"�8��\~�X7M�ߧ�gl�����P�7���ճQ��P��uÇ���o��|u�א��p��?P'q6�H�_�2��݇D@��;�8�>ꯧ;�g�3��,H�[����p^ᇸ(���q�xװ�qc��Z�?�}	?n���$��:9t��k��Y��C_-���`�&��Dll88�6z�
�=���ɓ�\@���d�:�zo�q��^������DP_�+���	܉��|jD�P��_<H�^�tR�Vұ��+#mDZ�����~#�����t��n~J��5�����xQ��jH�J�
�elx��<�n��Q��̦��-l}��m��<I���N��x�P�2�G;_����ό��5N��-�ϒ8�KB�B��A�����z�d���UO*@rVjHÄ�t+���?./��S�����#g��zi�,j�f�m�&ow�� � d�$��X�~�[?�q�Bƃ��G�d��;c"�]�$�c��x�G`��q��YN��uJ����N�?w���I��$b��5AC[�G\>�:�<��\��(<@dDLƬ��u�S�;<~8���݀�hB���/��E��nU�P�N$8�D�\���}&Lk<�FB��$H�	$Ќt7�u�c٘Yx�ዸl�u8)����Đz\�h������ 5�v��M��V�?�=���}dM�KH:(�8����iU���?���?�ت�0V��N�~o�>'��R�h
w��k^�B��Nm�T�J�/�WhM�3T�v�&��M���@GB"P�\0�y���^W}a�)|9�*�P��j�?�ֺ_�Q��c���4\�!�6��v����}�r8@��/�A:YV�� �^�� i`�8i����$>���Rx��[�u2�_
e!"��z��w���݊f^�ڿߪ��*1�{DD����RQ
R�ӸmF��Hd�G�U���ZCc"ܿ=�g7<յW4�B�Û1Ń��!�<�=&u���_���4���T� 5��K8H��:&C!b��-�&����<BX�==Ō�!Z
v�����5�'������u�⪪�5R��!��P�3���o� �e@���B��!�c�����ۡ�a��c$�ʹ�Ko��e����!H�Q�g�,	O
9~KOŝ�3��#�T�	IY�"���TA"hg4]
!h$A4|eLB|�"�t�V(�h�ȟQ"6 Z	h��@�!��~�����^�Ǡs�(����p��%�)Hm�a�g��Z�"\��~Z^T��Qu����ֿ=�o��#="�$D�`ԁjH%d��`f���Ǯ�;H����૯߄6�VK@�:�F��T͠Gt���
8ߚu��U����}œz`����a�Z��wk���qGK`@�-�7�X��ث���l���g����L+�a��KtPKX�E�����a���L�P�d1�k'���~����ǖ�0c��.ב�����
!�]�9�uҿ�;R���9�pW�!�0�/h*N�P�%8�����ƃp����ӻ��` �l�����d�����-�{�ȤnߞU<Lw�r�:\	���;h��O��O�K�n���°Rp���ND�H���G`�#@>���S�u@�]@*̺"~N����kkb��?�
ݓA.C���?|���r�-���.|wणYgB�uUp��E�H���ܮ�8�?�\M"\��"	�S���`l]�y;�)H�= 5�g��J���0R�&@9B���q�� �q��x�m7�t@�	�X���6�q���AL@x�2�~�����@�4W�޻,��,4`q)qLgH�K���
�I@w��K�`���p�Q��.������v�z�%Ҍu�?�]UX�n(�.���F�ɕS��g���`E�p8��
��J�ʂ(��`�(Ü;)�]7�&�:!���ˁ�EwOBh:�v�F�$�V��R�ҝ`?��)�l����i�F؞�Ys����=�ȭ8��S��S;
o$xJ�@�&�=�z��|����݊U
���
��T���x������m�8��
u����erHca�p$A��4�]�<P����r��a�Q
�s3���˷��"�6�0"$�[iBG�kD9���H:V�BHjI�:�� >Ȗ���B�gȀ�W��8CB�h�n���By�>H��:t�k�
���E�+���@a�n��.aq`�l:��
i	M��b>�j�#խ�vLLmRACEIP���3�t
H(���@|K��	%��ӀP�H)<��E�`���a�D\�,��\���
U���r�"��샗k�r8	�l�j�N��,A��U5�pˡ#%¨*�2nIH�+)�����@��������+�߮*S�U� .��5!p�$H���
Fa�wxc�m��?@�ת;��Dxe������~���w�.���G�}^�w�Irxq�{[�p�@XX"Z�n�I9|��*̕�W`|m39�J�?���2�7��N�".���с��=���>�*����t���$$���;��C�� ��0�ˀ��Ϲ�Mq�7�*τ��e��j����� B��BD�
�%A4���x%.���m���K�Ʒ���y=�5�Q���ƴGp����Z���%@�=�-�������4[�FI��e�`�JO�2�t�>��0�"��V²n�9�w���=���p�v��^Ĕ��@ͽ��EL���4�N������ۑA�~.�����H
�>�P�/������O�/i.�HGUa��@?�Faa�,13��u����q�^���� 
�9�N��V9�MN�8�	/�snЗ?�Y��/׾Q���3�i	f	pk/P�P&-)�� Jt
a!V�/� ��ƒ,��³��n{n7�|�gp��≷��ͰX�:��P����ß��
l�m��������XEk���Lxe6����ՀQ���7EW�e=�[��e+�G �2��:ۆ*�o�E�U���
��n{f7�{�i��wG�9��ث{����O`=���ĺ-�����~���
*
8?Ӵ�(����a��uB��U$�(��d�6��'�E�'�e�d��ھr:VO@��qX�d~i�����'��[�C-R�A�X7I&|���a�=�(�!/�����^�1ч�@�Ì{(�"�^%@�>`�wF�#��-ŝ����p�[�p���q�+����	��[^��|G�<�9���߅ƚy����趀��k�g�B�^	@��VX�q��
�W��[��,А�ƙ�`ưNt�cIkֶ�S�`��B�H!ͥyB,��;�T�m|�y'��'@�}&�v�{�EOt�n�Z؜�| �	��+ø[����.��˜��WBF
ܼ�8~�9�P�\$�Ytʷɡu��GXc"[���S���t۳�1���yH����3�-@/��:��<�%O��r0��(�=�F��r�on??=�>�m�ɢ�~U�rVa �h��>�G�	���;p��X��=�1�s���Xݭ�-@��̼Uݐw*	Н�PUo��R�;�K��:R��S_�}��7�����8p�;�u�y���<��[���_�
�]�S�q�k�K�{/uv��J�
��)��dTG3�ѯm���]U�Ɏ�'�E�n�[	`��j�]m��zN��U\t�3��'p���33�0�a3(�&pl�u��	���pM΁xl��M]��ʀ�C�\�ps+	/(��$(<{9��@u>�oW_S4��yH������Xhn�O�i�cWH�ȁ�]���wvp�����I��耴/	1t���������p�0$Z�1����I0`���9o�Yb�o$B�;�,),	� ����3��u^p�#�
jX
���)�HԷE���ܽc��3p�?K/mi��c�,��T$�i	��h:APk�8N1&�P0�a>3�vg(�"�����5�������.@_/�̝�X����ϒF��,z����
��YXt�h&m��%9�L�?���ʮ�{U�Og���X@|?8��ݗ��#zx�P���nP��^Z�$���g;.>�6��R�����6t%���m����A��|/�oK���\��0�!�	�^��W�
lϾ�6q^D��5eͺ�w���݆��v�a�dv'��^��蜱���1���m�����/��.{�����#���{g��;�jNsfJ���|ވN�[��]@|O�Yr��z�Y*�u�47����Jp�
̄�[�ߝ��{X�a$f���w�q��;?;qf���D'	����gld��E��a6Js�h�.I�F���&6λ�]	��d���xb�t�xS�U_^��k�=러-A!�z��OVN[ĽSZX��� ��!�70j�~��bskM�k�’�X�R��C��	�k~���|ō�[�叧+~u��0qK|g�,/La�
��
���p���pB�S���F�cd*�`M���6��cot�
�nג��H��mW�a�g:u��u3�l)/	���nwD`�B~�mo�J<�^{zZ�_����%�޳�k�E��o�֦=��Sv(���w&-�_��R?j����xzY��:�<��3���*͇���Xt_��{�n�%�}�35+����~و/L]Q?'��m�"4�,&˭إ��w��θ.v���4��m̶��Z2�Ds6�n���6�Kl�Mz�}���|e��!�N�2�m���)���`�}A?������:07��|��o�"�R��IEND�B`�PK
!<�	�@hh8chrome/browser/content/branding/identity-icons-brand.svg<svg data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
  <defs>
    <linearGradient id="a" x1="31.36" y1="-.127" x2="4.307" y2="27.635" gradientUnits="userSpaceOnUse">
      <stop offset="0" stop-color="#ffea00"/>
      <stop offset=".108" stop-color="#ffde00"/>
      <stop offset=".3" stop-color="#ffbf00"/>
      <stop offset=".552" stop-color="#ff8e00"/>
      <stop offset=".816" stop-color="#ff272d"/>
      <stop offset=".848" stop-color="#fb2532"/>
      <stop offset=".887" stop-color="#ee1e40"/>
      <stop offset=".928" stop-color="#d81358"/>
      <stop offset=".971" stop-color="#ba0379"/>
      <stop offset=".978" stop-color="#b5007f"/>
    </linearGradient>
  </defs>
  <path d="M31.4 14.627c-.044-.289-.088-.46-.088-.46s-.113.131-.3.379a10.8 10.8 0 0 0-.375-2.04 13.883 13.883 0 0 0-.94-2.418 10.078 10.078 0 0 0-.858-1.473q-.177-.264-.36-.512c-.571-.934-1.227-1.5-1.986-2.583a7.826 7.826 0 0 1-.993-2.692 10.88 10.88 0 0 0-.477 1.761c-.779-.786-1.458-1.345-1.866-1.726C21.133.978 21.367 0 21.367 0s-3.773 4.209-2.137 8.6a8.43 8.43 0 0 0 2.81 3.744c1.581 1.3 3.283 2.33 4.18 4.952a8.4 8.4 0 0 0-3.154-3.327 7.593 7.593 0 0 1 .6 3.006 7.145 7.145 0 0 1-8.736 6.96 6.576 6.576 0 0 1-1.77-.6 7.2 7.2 0 0 1-2.121-1.96l-.01-.017.126.046a6.517 6.517 0 0 0 .9.242 5.644 5.644 0 0 0 3.594-.424c1.129-.626 1.813-1.091 2.367-.908h.01c.542.172.969-.353.581-.9a2.949 2.949 0 0 0-2.846-1.114c-1.131.165-2.167.968-3.648.19a3.13 3.13 0 0 1-.278-.163c-.1-.058.317.088.221.022a7.351 7.351 0 0 1-.931-.555c-.022-.018.224.07.2.052a3.591 3.591 0 0 1-.971-.982 1.746 1.746 0 0 1-.066-1.559 1.376 1.376 0 0 1 .6-.566c.192.095.31.166.31.166s-.087-.16-.135-.244c.017-.006.032 0 .049-.011.168.073.539.261.734.376a1.018 1.018 0 0 1 .335.3s.067-.033.017-.173a.907.907 0 0 0-.347-.425h.016A2.952 2.952 0 0 1 12.3 15a2.085 2.085 0 0 0 .171-.906 1.181 1.181 0 0 0-.069-.5c-.054-.1.03-.14.123-.035a.977.977 0 0 0-.079-.239v-.008s.053-.069.078-.095a1.434 1.434 0 0 1 .216-.176 10 10 0 0 1 1.469-.749c.416-.181.759-.32.83-.36a2.287 2.287 0 0 0 .294-.226 1.973 1.973 0 0 0 .661-1.143 1.593 1.593 0 0 0 .017-.178v-.105c-.06-.226-.449-.395-2.483-.586a1.778 1.778 0 0 1-1.454-1.364v.009c-.029.075-.055.15-.081.225.026-.075.052-.151.081-.225v-.016a5.153 5.153 0 0 1 1.992-2.474c.052-.043-.208.011-.156-.032a5.18 5.18 0 0 1 .532-.225c.091-.039-.391-.222-.818-.178a2.2 2.2 0 0 0-.758.178c.1-.086.4-.2.33-.2a4.877 4.877 0 0 0-1.547.585.315.315 0 0 1 .03-.14 2.407 2.407 0 0 0-.966.746 1.286 1.286 0 0 0 .01-.174 2.883 2.883 0 0 0-.475.446l-.009.007a6.3 6.3 0 0 0-3.527-.3l-.01-.009h.012a2.953 2.953 0 0 1-.627-.7l-.008-.012-.014-.021a5.48 5.48 0 0 1-.237-.388 5.527 5.527 0 0 1-.176-.339c0-.008-.009-.011-.013-.012-.024 0-.041.111-.061.082v-.006a4.322 4.322 0 0 1-.309-1.697l-.016.008a1.89 1.89 0 0 0-.716.937c-.062.137-.1.213-.14.288v-.035c.009-.069.039-.212.032-.2s-.013.019-.02.029a1.741 1.741 0 0 0-.252.373 2.366 2.366 0 0 0-.15.383c-.006.021 0-.018 0-.064s.009-.128 0-.111l-.022.043a9.526 9.526 0 0 0-.806 3.044 3.038 3.038 0 0 0-.023.54v.016a6.641 6.641 0 0 0-.819 1.1 15.65 15.65 0 0 0-1.731 4.246 10.379 10.379 0 0 1 .928-1.626 15.041 15.041 0 0 0-1.049 5.514 14.264 14.264 0 0 1 .451-1.634 13.848 13.848 0 0 0 1.25 6.489 15.08 15.08 0 0 0 5.968 6.768 13.057 13.057 0 0 0 3.478 1.667c.163.059.327.117.5.173-.053-.021-.1-.044-.153-.067a15.752 15.752 0 0 0 4.506.659c5.41 0 7.2-2.06 7.36-2.266a2.739 2.739 0 0 0 .639-.858q.156-.064.316-.137l.067-.03.122-.057a11.349 11.349 0 0 0 2.284-1.43 5.511 5.511 0 0 0 2.129-3.11 1.944 1.944 0 0 0 .029-1.433q.083-.132.171-.28a12.743 12.743 0 0 0 1.913-6.2v-.184a7.76 7.76 0 0 0-.115-1.388z" fill="url(#a)"/>
</svg>
PK
!<�o昕�$chrome/browser/content/briefcase.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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"
     width="32" height="32" viewBox="0 0 32 32">
  <path fill="context-fill" fill-rule="evenodd"
        d="M22,9.99887085 L21.635468,10 L29.0034652,10 C29.5538362,10 30,10.4449463 30,10.9933977 L30,27.0066023 C30,27.5552407 29.5601869,28 29.0034652,28 L2.99653482,28 C2.44616384,28 2,27.5550537 2,27.0066023 L2,10.9933977 C2,10.4447593 2.43981314,10 2.99653482,10 L8,10 L8,7.99922997 C8,5.79051625 10.0426627,4 12.5635454,4 L19.4364546,4 C21.9568311,4 24,5.79246765 24,7.99922997 L24,9.99267578 L22,9.99887085 L22,10 L10,10 L10,7.99922997 C10,6.89421235 11.0713286,6 12.3917227,6 L19.6082773,6 C20.9273761,6 22,6.89552665 22,7.99922997 L22,9.99887085 Z"/>
</svg>

PK
!<�ה�!9!98chrome/browser/content/browser/aboutDialog-appUpdater.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Note: this file is included in aboutDialog.xul and preferences/advanced.xul
// if MOZ_UPDATER is defined.

/* import-globals-from aboutDialog.js */

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/DownloadUtils.jsm");

ChromeUtils.defineModuleGetter(this, "UpdateUtils",
                               "resource://gre/modules/UpdateUtils.jsm");

const PREF_APP_UPDATE_CANCELATIONS_OSX = "app.update.cancelations.osx";
const PREF_APP_UPDATE_ELEVATE_NEVER    = "app.update.elevate.never";

var gAppUpdater;

function onUnload(aEvent) {
  if (gAppUpdater.isChecking)
    gAppUpdater.checker.stopChecking(Ci.nsIUpdateChecker.CURRENT_CHECK);
  // Safe to call even when there isn't a download in progress.
  gAppUpdater.removeDownloadListener();
  gAppUpdater = null;
}


function appUpdater(options = {}) {
  XPCOMUtils.defineLazyServiceGetter(this, "aus",
                                     "@mozilla.org/updates/update-service;1",
                                     "nsIApplicationUpdateService");
  XPCOMUtils.defineLazyServiceGetter(this, "checker",
                                     "@mozilla.org/updates/update-checker;1",
                                     "nsIUpdateChecker");
  XPCOMUtils.defineLazyServiceGetter(this, "um",
                                     "@mozilla.org/updates/update-manager;1",
                                     "nsIUpdateManager");

  this.options = options;
  this.updateDeck = document.getElementById("updateDeck");

  // Hide the update deck when the update window is already open and it's not
  // already applied, to avoid syncing issues between them. Applied updates
  // don't have any information to sync between the windows as they both just
  // show the "Restart to continue"-type button.
  if (Services.wm.getMostRecentWindow("Update:Wizard") &&
      !this.isApplied) {
    this.updateDeck.hidden = true;
    return;
  }

  this.bundle = Services.strings.
                createBundle("chrome://browser/locale/browser.properties");

  let manualURL = Services.urlFormatter.formatURLPref("app.update.url.manual");
  let manualLink = document.getElementById("manualLink");
  manualLink.textContent = manualURL;
  manualLink.href = manualURL;
  document.getElementById("failedLink").href = manualURL;

  if (this.updateDisabledAndLocked) {
    this.selectPanel("adminDisabled");
    return;
  }

  if (this.isPending || this.isApplied) {
    this.selectPanel("apply");
    return;
  }

  if (this.aus.isOtherInstanceHandlingUpdates) {
    this.selectPanel("otherInstanceHandlingUpdates");
    return;
  }

  if (this.isDownloading) {
    this.startDownload();
    // selectPanel("downloading") is called from setupDownloadingUI().
    return;
  }

  // Honor the "Never check for updates" option by not only disabling background
  // update checks, but also in the About dialog, by presenting a
  // "Check for updates" button.
  // If updates are found, the user is then asked if he wants to "Update to <version>".
  if (!this.updateEnabled ||
      Services.prefs.prefHasUserValue(PREF_APP_UPDATE_ELEVATE_NEVER)) {
    this.selectPanel("checkForUpdates");
    return;
  }

  // That leaves the options
  // "Check for updates, but let me choose whether to install them", and
  // "Automatically install updates".
  // In both cases, we check for updates without asking.
  // In the "let me choose" case, we ask before downloading though, in onCheckComplete.
  this.checkForUpdates();
}

appUpdater.prototype =
{
  // true when there is an update check in progress.
  isChecking: false,

  // true when there is an update already staged / ready to be applied.
  get isPending() {
    if (this.update) {
      return this.update.state == "pending" ||
             this.update.state == "pending-service" ||
             this.update.state == "pending-elevate";
    }
    return this.um.activeUpdate &&
           (this.um.activeUpdate.state == "pending" ||
            this.um.activeUpdate.state == "pending-service" ||
            this.um.activeUpdate.state == "pending-elevate");
  },

  // true when there is an update already installed in the background.
  get isApplied() {
    if (this.update)
      return this.update.state == "applied" ||
             this.update.state == "applied-service";
    return this.um.activeUpdate &&
           (this.um.activeUpdate.state == "applied" ||
            this.um.activeUpdate.state == "applied-service");
  },

  // true when there is an update download in progress.
  get isDownloading() {
    if (this.update)
      return this.update.state == "downloading";
    return this.um.activeUpdate &&
           this.um.activeUpdate.state == "downloading";
  },

  // true when updating is disabled by an administrator.
  get updateDisabledAndLocked() {
    return (!this.updateEnabled &&
           Services.prefs.prefIsLocked("app.update.enabled")) ||
           (Services.policies &&
           !Services.policies.isAllowed("appUpdate"));
  },

  // true when updating is enabled.
  get updateEnabled() {
    try {
      return Services.prefs.getBoolPref("app.update.enabled");
    } catch (e) { }
    return true; // Firefox default is true
  },

  // true when updating in background is enabled.
  get backgroundUpdateEnabled() {
    return this.updateEnabled &&
           gAppUpdater.aus.canStageUpdates;
  },

  // true when updating is automatic.
  get updateAuto() {
    try {
      return Services.prefs.getBoolPref("app.update.auto");
    } catch (e) { }
    return true; // Firefox default is true
  },

  /**
   * Sets the panel of the updateDeck.
   *
   * @param  aChildID
   *         The id of the deck's child to select, e.g. "apply".
   */
  selectPanel(aChildID) {
    let panel = document.getElementById(aChildID);

    let button = panel.querySelector("button");
    if (button) {
      if (aChildID == "downloadAndInstall") {
        let updateVersion = gAppUpdater.update.displayVersion;
        // Include the build ID if this is an "a#" (nightly or aurora) build
        if (/a\d+$/.test(updateVersion)) {
          let buildID = gAppUpdater.update.buildID;
          let year = buildID.slice(0, 4);
          let month = buildID.slice(4, 6);
          let day = buildID.slice(6, 8);
          updateVersion += ` (${year}-${month}-${day})`;
        }
        button.label = this.bundle.formatStringFromName("update.downloadAndInstallButton.label", [updateVersion], 1);
        button.accessKey = this.bundle.GetStringFromName("update.downloadAndInstallButton.accesskey");
      }
      this.updateDeck.selectedPanel = panel;
      if (this.options.buttonAutoFocus &&
          (!document.commandDispatcher.focusedElement || // don't steal the focus
           document.commandDispatcher.focusedElement.localName == "button")) { // except from the other buttons
        button.focus();
      }
    } else {
      this.updateDeck.selectedPanel = panel;
    }
  },

  /**
   * Check for updates
   */
  checkForUpdates() {
    // Clear prefs that could prevent a user from discovering available updates.
    if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_CANCELATIONS_OSX)) {
      Services.prefs.clearUserPref(PREF_APP_UPDATE_CANCELATIONS_OSX);
    }
    if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_ELEVATE_NEVER)) {
      Services.prefs.clearUserPref(PREF_APP_UPDATE_ELEVATE_NEVER);
    }
    this.selectPanel("checkingForUpdates");
    this.isChecking = true;
    this.checker.checkForUpdates(this.updateCheckListener, true);
    // after checking, onCheckComplete() is called
  },

  /**
   * Handles oncommand for the "Restart to Update" button
   * which is presented after the download has been downloaded.
   */
  buttonRestartAfterDownload() {
    if (!this.isPending && !this.isApplied) {
      return;
    }

    gAppUpdater.selectPanel("restarting");

    // Notify all windows that an application quit has been requested.
    let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].
                     createInstance(Ci.nsISupportsPRBool);
    Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");

    // Something aborted the quit process.
    if (cancelQuit.data) {
      gAppUpdater.selectPanel("apply");
      return;
    }

    // If already in safe mode restart in safe mode (bug 327119)
    if (Services.appinfo.inSafeMode) {
      Services.startup.restartInSafeMode(Ci.nsIAppStartup.eAttemptQuit);
      return;
    }

    Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit |
                          Ci.nsIAppStartup.eRestart);
  },

  /**
   * Implements nsIUpdateCheckListener. The methods implemented by
   * nsIUpdateCheckListener are in a different scope from nsIIncrementalDownload
   * to make it clear which are used by each interface.
   */
  updateCheckListener: {
    /**
     * See nsIUpdateService.idl
     */
    onCheckComplete(aRequest, aUpdates, aUpdateCount) {
      gAppUpdater.isChecking = false;
      gAppUpdater.update = gAppUpdater.aus.
                           selectUpdate(aUpdates, aUpdates.length);
      if (!gAppUpdater.update) {
        gAppUpdater.selectPanel("noUpdatesFound");
        return;
      }

      if (gAppUpdater.update.unsupported) {
        if (gAppUpdater.update.detailsURL) {
          let unsupportedLink = document.getElementById("unsupportedLink");
          unsupportedLink.href = gAppUpdater.update.detailsURL;
        }
        gAppUpdater.selectPanel("unsupportedSystem");
        return;
      }

      if (!gAppUpdater.aus.canApplyUpdates) {
        gAppUpdater.selectPanel("manualUpdate");
        return;
      }

      if (gAppUpdater.updateAuto) // automatically download and install
        gAppUpdater.startDownload();
      else // ask
        gAppUpdater.selectPanel("downloadAndInstall");
    },

    /**
     * See nsIUpdateService.idl
     */
    onError(aRequest, aUpdate) {
      // Errors in the update check are treated as no updates found. If the
      // update check fails repeatedly without a success the user will be
      // notified with the normal app update user interface so this is safe.
      gAppUpdater.isChecking = false;
      gAppUpdater.selectPanel("noUpdatesFound");
    },

    /**
     * See nsISupports.idl
     */
    QueryInterface(aIID) {
      if (!aIID.equals(Ci.nsIUpdateCheckListener) &&
          !aIID.equals(Ci.nsISupports))
        throw Cr.NS_ERROR_NO_INTERFACE;
      return this;
    }
  },

  /**
   * Starts the download of an update mar.
   */
  startDownload() {
    if (!this.update)
      this.update = this.um.activeUpdate;
    this.update.QueryInterface(Ci.nsIWritablePropertyBag);
    this.update.setProperty("foregroundDownload", "true");

    this.aus.pauseDownload();
    let state = this.aus.downloadUpdate(this.update, false);
    if (state == "failed") {
      this.selectPanel("downloadFailed");
      return;
    }

    this.setupDownloadingUI();
  },

  /**
   * Switches to the UI responsible for tracking the download.
   */
  setupDownloadingUI() {
    this.downloadStatus = document.getElementById("downloadStatus");
    this.downloadStatus.textContent =
      DownloadUtils.getTransferTotal(0, this.update.selectedPatch.size);
    this.selectPanel("downloading");
    this.aus.addDownloadListener(this);
  },

  removeDownloadListener() {
    if (this.aus) {
      this.aus.removeDownloadListener(this);
    }
  },

  /**
   * See nsIRequestObserver.idl
   */
  onStartRequest(aRequest, aContext) {
  },

  /**
   * See nsIRequestObserver.idl
   */
  onStopRequest(aRequest, aContext, aStatusCode) {
    switch (aStatusCode) {
    case Cr.NS_ERROR_UNEXPECTED:
      if (this.update.selectedPatch.state == "download-failed" &&
          (this.update.isCompleteUpdate || this.update.patchCount != 2)) {
        // Verification error of complete patch, informational text is held in
        // the update object.
        this.removeDownloadListener();
        this.selectPanel("downloadFailed");
        break;
      }
      // Verification failed for a partial patch, complete patch is now
      // downloading so return early and do NOT remove the download listener!
      break;
    case Cr.NS_BINDING_ABORTED:
      // Do not remove UI listener since the user may resume downloading again.
      break;
    case Cr.NS_OK:
      this.removeDownloadListener();
      if (this.backgroundUpdateEnabled) {
        this.selectPanel("applying");
        let self = this;
        Services.obs.addObserver(function observer(aSubject, aTopic, aData) {
          // Update the UI when the background updater is finished
          let status = aData;
          if (status == "applied" || status == "applied-service" ||
              status == "pending" || status == "pending-service" ||
              status == "pending-elevate") {
            // If the update is successfully applied, or if the updater has
            // fallen back to non-staged updates, show the "Restart to Update"
            // button.
            self.selectPanel("apply");
          } else if (status == "failed") {
            // Background update has failed, let's show the UI responsible for
            // prompting the user to update manually.
            self.selectPanel("downloadFailed");
          } else if (status == "downloading") {
            // We've fallen back to downloading the full update because the
            // partial update failed to get staged in the background.
            // Therefore we need to keep our observer.
            self.setupDownloadingUI();
            return;
          }
          Services.obs.removeObserver(observer, "update-staged");
        }, "update-staged");
      } else {
        this.selectPanel("apply");
      }
      break;
    default:
      this.removeDownloadListener();
      this.selectPanel("downloadFailed");
      break;
    }
  },

  /**
   * See nsIProgressEventSink.idl
   */
  onStatus(aRequest, aContext, aStatus, aStatusArg) {
  },

  /**
   * See nsIProgressEventSink.idl
   */
  onProgress(aRequest, aContext, aProgress, aProgressMax) {
    this.downloadStatus.textContent =
      DownloadUtils.getTransferTotal(aProgress, aProgressMax);
  },

  /**
   * See nsISupports.idl
   */
  QueryInterface(aIID) {
    if (!aIID.equals(Ci.nsIProgressEventSink) &&
        !aIID.equals(Ci.nsIRequestObserver) &&
        !aIID.equals(Ci.nsISupports))
      throw Cr.NS_ERROR_NO_INTERFACE;
    return this;
  }
};
PK
!<'����.chrome/browser/content/browser/aboutDialog.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#aboutDialog {
  width: 620px;
  /* Set an explicit line-height to avoid discrepancies in 'auto' spacing
     across screens with different device DPI, which may cause font metrics
     to round differently. */
  line-height: 1.5;
}

#rightBox {
  background-image: url("chrome://branding/content/about-wordmark.svg");
  background-repeat: no-repeat;
  /* padding-top creates room for the wordmark */
  padding-top: 38px;
  margin-top:20px;
}

#rightBox:-moz-locale-dir(rtl) {
  background-position: 100% 0;
}

#bottomBox {
  padding: 15px 10px 0;
}

#release {
  font-weight: bold;
  font-size: 125%;
  margin-top: 10px;
  margin-inline-start: 0;
}

#version {
  font-weight: bold;
  margin-top: 10px;
  margin-left: 0;
  -moz-user-select: text;
  -moz-user-focus: normal;
  cursor: text;
}

#version:-moz-locale-dir(rtl) {
  direction: ltr;
  text-align: right;
  margin-left: 5px;
  margin-right: 0;
}

#releasenotes {
  margin-top: 10px;
}

#distribution,
#distributionId {
  display: none;
  margin-top: 0;
  margin-bottom: 0;
}

.text-blurb {
  margin-bottom: 10px;
  margin-inline-start: 0;
  padding-inline-start: 0;
}

#updateButton,
#updateDeck > hbox > label {
  margin-inline-start: 0;
  padding-inline-start: 0;
}

.update-throbber {
  width: 16px;
  min-height: 16px;
  margin-inline-end: 3px;
  list-style-image: url("chrome://global/skin/icons/loading.png");
}

@media (min-resolution: 1.1dppx) {
  .update-throbber {
    list-style-image: url("chrome://global/skin/icons/[email protected]");
  }
}

description > .text-link,
description > .text-link:focus {
  margin: 0px;
  padding: 0px;
}

.bottom-link,
.bottom-link:focus {
  text-align: center;
  margin: 0 40px;
}

#currentChannel {
  margin: 0;
  padding: 0;
  font-weight: bold;
}
PK
!<�Fn�k
k
-chrome/browser/content/browser/aboutDialog.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

/* import-globals-from aboutDialog-appUpdater.js */

// Services = object with smart getters for common XPCOM services
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/AppConstants.jsm");

function init(aEvent) {
  if (aEvent.target != document)
    return;

  var distroId = Services.prefs.getCharPref("distribution.id", "");
  if (distroId) {
    var distroString = distroId;

    var distroVersion = Services.prefs.getCharPref("distribution.version", "");
    if (distroVersion) {
      distroString += " - " + distroVersion;
    }

    var distroIdField = document.getElementById("distributionId");
    distroIdField.value = distroString;
    distroIdField.style.display = "block";

    var distroAbout = Services.prefs.getStringPref("distribution.about", "");
    if (distroAbout) {
      var distroField = document.getElementById("distribution");
      distroField.value = distroAbout;
      distroField.style.display = "block";
    }
  }

  // Include the build ID and display warning if this is an "a#" (nightly or aurora) build
  let versionField = document.getElementById("version");
  let version = Services.appinfo.version;
  if (/a\d+$/.test(version)) {
    let buildID = Services.appinfo.appBuildID;
    let year = buildID.slice(0, 4);
    let month = buildID.slice(4, 6);
    let day = buildID.slice(6, 8);
    versionField.textContent += ` (${year}-${month}-${day})`;

    document.getElementById("experimental").hidden = false;
    document.getElementById("communityDesc").hidden = true;
  }

  // Append "(32-bit)" or "(64-bit)" build architecture to the version number:
  let bundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
  let archResource = Services.appinfo.is64Bit
                     ? "aboutDialog.architecture.sixtyFourBit"
                     : "aboutDialog.architecture.thirtyTwoBit";
  let arch = bundle.GetStringFromName(archResource);
  versionField.textContent += ` (${arch})`;

  // Show a release notes link if we have a URL.
  let relNotesLink = document.getElementById("releasenotes");
  let relNotesPrefType = Services.prefs.getPrefType("app.releaseNotesURL");
  if (relNotesPrefType != Services.prefs.PREF_INVALID) {
    let relNotesURL = Services.urlFormatter.formatURLPref("app.releaseNotesURL");
    if (relNotesURL != "about:blank") {
      relNotesLink.href = relNotesURL;
      relNotesLink.hidden = false;
    }
  }

  if (AppConstants.MOZ_UPDATER) {
    gAppUpdater = new appUpdater({ buttonAutoFocus: true });

    let channelLabel = document.getElementById("currentChannel");
    let currentChannelText = document.getElementById("currentChannelText");
    channelLabel.value = UpdateUtils.UpdateChannel;
    if (/^release($|\-)/.test(channelLabel.value))
        currentChannelText.hidden = true;
  }

  if (AppConstants.MOZ_UPDATE_CHANNEL == "esr") {
    document.getElementById("release").hidden = false;
  }
  if (AppConstants.platform == "macosx") {
    // it may not be sized at this point, and we need its width to calculate its position
    window.sizeToContent();
    window.moveTo((screen.availWidth / 2) - (window.outerWidth / 2), screen.availHeight / 5);
  }
}
PK
!<����.chrome/browser/content/browser/aboutDialog.xul<?xml version="1.0"?> <!-- -*- Mode: HTML -*- -->


<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/aboutDialog.css" type="text/css"?>
<?xml-stylesheet href="chrome://branding/content/aboutDialog.css" type="text/css"?>

<!DOCTYPE window [
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
%brandDTD;
<!ENTITY % aboutDialogDTD SYSTEM "chrome://browser/locale/aboutDialog.dtd" >
%aboutDialogDTD;
]>


<window xmlns:html="http://www.w3.org/1999/xhtml"
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        id="aboutDialog"
        windowtype="Browser:About"
        onload="init(event);"
        onunload="onUnload(event);"
        title="&aboutDialog.title;"
        role="dialog"
        aria-describedby="version distribution distributionId communityDesc contributeDesc trademark"
        >

  <script type="application/javascript" src="chrome://browser/content/aboutDialog.js"/>
  <script type="application/javascript" src="chrome://browser/content/aboutDialog-appUpdater.js"/>
  <vbox id="aboutDialogContainer">
    <hbox id="clientBox">
      <vbox id="leftBox" flex="1"/>
      <vbox id="rightBox" flex="1">
        <label id="release" hidden="true">
        <!-- This string is explicitly not translated -->
          Extended Support Release
        </label>
        <hbox align="baseline">
<label id="version">60.0</label>
          <label id="releasenotes" class="text-link" hidden="true">&releaseNotes.link;</label>
        </hbox>

        <label id="distribution" class="text-blurb"/>
        <label id="distributionId" class="text-blurb"/>

        <vbox id="detailsBox">
          <vbox id="updateBox">
            <deck id="updateDeck" orient="vertical">
              <hbox id="checkForUpdates" align="center">
                <button id="checkForUpdatesButton" align="start"
                        label="&update.checkForUpdatesButton.label;"
                        accesskey="&update.checkForUpdatesButton.accesskey;"
                        oncommand="gAppUpdater.checkForUpdates();"/>
                <spacer flex="1"/>
              </hbox>
              <hbox id="downloadAndInstall" align="center">
                <button id="downloadAndInstallButton" align="start"
                        oncommand="gAppUpdater.startDownload();"/>
                        <!-- label and accesskey will be filled by JS -->
                <spacer flex="1"/>
              </hbox>
              <hbox id="apply" align="center">
                <button id="updateButton" align="start"
                        label="&update.updateButton.label3;"
                        accesskey="&update.updateButton.accesskey;"
                        oncommand="gAppUpdater.buttonRestartAfterDownload();"/>
                <spacer flex="1"/>
              </hbox>
              <hbox id="checkingForUpdates" align="center">
                <image class="update-throbber"/><label>&update.checkingForUpdates;</label>
              </hbox>
              <hbox id="downloading" align="center">
                <image class="update-throbber"/><label>&update.downloading.start;</label><label id="downloadStatus"/><label>&update.downloading.end;</label>
              </hbox>
              <hbox id="applying" align="center">
                <image class="update-throbber"/><label>&update.applying;</label>
              </hbox>
              <hbox id="downloadFailed" align="center">
                <label>&update.failed.start;</label><label id="failedLink" class="text-link">&update.failed.linkText;</label><label>&update.failed.end;</label>
              </hbox>
              <hbox id="adminDisabled" align="center">
                <label>&update.adminDisabled;</label>
              </hbox>
              <hbox id="noUpdatesFound" align="center">
                <label>&update.noUpdatesFound;</label>
              </hbox>
              <hbox id="otherInstanceHandlingUpdates" align="center">
                <label>&update.otherInstanceHandlingUpdates;</label>
              </hbox>
              <hbox id="manualUpdate" align="center">
                <label>&update.manual.start;</label><label id="manualLink" class="text-link"/><label>&update.manual.end;</label>
              </hbox>
              <hbox id="unsupportedSystem" align="center">
                <label>&update.unsupported.start;</label><label id="unsupportedLink" class="text-link">&update.unsupported.linkText;</label><label>&update.unsupported.end;</label>
              </hbox>
              <hbox id="restarting" align="center">
                <image class="update-throbber"/><label>&update.restarting;</label>
              </hbox>
            </deck>
          </vbox>

          <description class="text-blurb" id="currentChannelText">
            &channel.description.start;<label id="currentChannel"/>&channel.description.end;
          </description>
          <vbox id="experimental" hidden="true">
            <description class="text-blurb" id="warningDesc">
              &warningDesc.version;
            </description>
            <description class="text-blurb" id="communityExperimentalDesc">
              &community.exp.start;<label class="text-link" href="http://www.mozilla.org/">&community.exp.mozillaLink;</label>&community.exp.middle;<label class="text-link" useoriginprincipal="true" href="about:credits">&community.exp.creditsLink;</label>&community.exp.end;
            </description>
          </vbox>
          <description class="text-blurb" id="communityDesc">
            &community.start2;<label class="text-link" href="http://www.mozilla.org/">&community.mozillaLink;</label>&community.middle2;<label class="text-link" useoriginprincipal="true" href="about:credits">&community.creditsLink;</label>&community.end3;
          </description>
          <description class="text-blurb" id="contributeDesc">
              &helpus.start;<label class="text-link" href="https://donate.mozilla.org/?utm_source=firefox&#38;utm_medium=referral&#38;utm_campaign=firefox_about&#38;utm_content=firefox_about">&helpus.donateLink;</label>&helpus.middle;<label class="text-link" href="http://www.mozilla.org/contribute/">&helpus.getInvolvedLink;</label>&helpus.end;
          </description>
        </vbox>
      </vbox>
    </hbox>
    <vbox id="bottomBox">
      <hbox pack="center">
        <label class="text-link bottom-link" useoriginprincipal="true" href="about:license">&bottomLinks.license;</label>
        <label class="text-link bottom-link" useoriginprincipal="true" href="about:rights">&bottomLinks.rights;</label>
        <label class="text-link bottom-link" href="https://www.mozilla.org/privacy/">&bottomLinks.privacy;</label>
      </hbox>
      <description id="trademark">&trademarkInfo.part1;</description>
    </vbox>
  </vbox>

  <keyset>
    <key keycode="VK_ESCAPE" oncommand="window.close();"/>
  </keyset>

</window>
PK
!<�e�q�q2chrome/browser/content/browser/aboutNetError.xhtml<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE html [
  <!ENTITY % htmlDTD
    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "DTD/xhtml1-strict.dtd">
  %htmlDTD;
  <!ENTITY % netErrorDTD
    SYSTEM "chrome://browser/locale/netError.dtd">
  %netErrorDTD;
  <!ENTITY % globalDTD
    SYSTEM "chrome://global/locale/global.dtd">
  %globalDTD;
]>

<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>&loadError.label;</title>
    <link rel="stylesheet" href="chrome://browser/skin/aboutNetError.css" type="text/css" media="all" />
    <!-- If the location of the favicon is changed here, the FAVICON_ERRORPAGE_URL symbol in
         toolkit/components/places/src/nsFaviconService.h should be updated. -->
    <link rel="icon" type="image/png" id="favicon" href="chrome://global/skin/icons/warning-16.png"/>

    <script type="application/javascript"><![CDATA[
      // The following parameters are parsed from the error URL:
      //   e - the error code
      //   s - custom CSS class to allow alternate styling/favicons
      //   d - error description
      //   captive - "true" to indicate we're behind a captive portal.
      //             Any other value is ignored.

      // Note that this file uses document.documentURI to get
      // the URL (with the format from above). This is because
      // document.location.href gets the current URI off the docshell,
      // which is the URL displayed in the location bar, i.e.
      // the URI that the user attempted to load.

      let searchParams = new URLSearchParams(document.documentURI.split("?")[1]);

      // Set to true on init if the error code is nssBadCert.
      let gIsCertError;

      function getErrorCode() {
        return searchParams.get("e");
      }

      function getCSSClass() {
        return searchParams.get("s");
      }

      function getDescription() {
        return searchParams.get("d");
      }

      function isCaptive() {
        return searchParams.get("captive") == "true";
      }

      function retryThis(buttonEl) {
        // Note: The application may wish to handle switching off "offline mode"
        // before this event handler runs, but using a capturing event handler.

        // Session history has the URL of the page that failed
        // to load, not the one of the error page. So, just call
        // reload(), which will also repost POST data correctly.
        try {
          location.reload();
        } catch (e) {
          // We probably tried to reload a URI that caused an exception to
          // occur;  e.g. a nonexistent file.
        }

        buttonEl.disabled = true;
      }

      function toggleDisplay(node) {
        const toggle = {
          "": "block",
          "none": "block",
          "block": "none"
        };
        return (node.style.display = toggle[node.style.display]);
      }

      function showCertificateErrorReporting() {
        // Display error reporting UI
        document.getElementById("certificateErrorReporting").style.display = "block";
      }

      function showPrefChangeContainer() {
        const panel = document.getElementById("prefChangeContainer");
        panel.style.display = "block";
        document.getElementById("netErrorButtonContainer").style.display = "none";
        document.getElementById("prefResetButton").addEventListener("click", function resetPreferences(e) {
          const event = new CustomEvent("AboutNetErrorResetPreferences", {bubbles: true});
          document.dispatchEvent(event);
        });
        addAutofocus("prefResetButton", "beforeend");
      }

      function setupAdvancedButton() {
        // Get the hostname and add it to the panel
        var panel = document.getElementById("badCertAdvancedPanel");
        for (var span of panel.querySelectorAll("span.hostname")) {
          span.textContent = document.location.hostname;
        }

        // Register click handler for the weakCryptoAdvancedPanel
        document.getElementById("advancedButton")
                .addEventListener("click", function togglePanelVisibility() {
          toggleDisplay(panel);
          if (gIsCertError) {
            // Toggling the advanced panel must ensure that the debugging
            // information panel is hidden as well, since it's opened by the
            // error code link in the advanced panel.
            var div = document.getElementById("certificateErrorDebugInformation");
            div.style.display = "none";
          }

          if (panel.style.display == "block") {
            // send event to trigger telemetry ping
            var event = new CustomEvent("AboutNetErrorUIExpanded", {bubbles: true});
            document.dispatchEvent(event);
          }
        });

        if (!gIsCertError) {
          return;
        }

        if (getCSSClass() == "expertBadCert") {
          toggleDisplay(document.getElementById("badCertAdvancedPanel"));
          // Toggling the advanced panel must ensure that the debugging
          // information panel is hidden as well, since it's opened by the
          // error code link in the advanced panel.
          var div = document.getElementById("certificateErrorDebugInformation");
          div.style.display = "none";
        }

        disallowCertOverridesIfNeeded();

        document.getElementById("badCertTechnicalInfo").textContent = getDescription();
      }

      function disallowCertOverridesIfNeeded() {
        var cssClass = getCSSClass();
        // Disallow overrides if this is a Strict-Transport-Security
        // host and the cert is bad (STS Spec section 7.3) or if the
        // certerror is in a frame (bug 633691).
        if (cssClass == "badStsCert" || window != top) {
          document.getElementById("exceptionDialogButton").setAttribute("hidden", "true");
        }
        if (cssClass == "badStsCert") {
          document.getElementById("badStsCertExplanation").removeAttribute("hidden");
        }
      }

      function initPage() {
        var err = getErrorCode();
        // List of error pages with an illustration.
        let illustratedErrors = [
          "malformedURI", "dnsNotFound", "connectionFailure", "netInterrupt",
          "netTimeout", "netReset", "netOffline",
        ];
        if (illustratedErrors.includes(err)) {
          document.body.classList.add("illustrated", err);
        }
        if (err == "blockedByPolicy") {
          document.body.classList.add("blocked");
        }

        gIsCertError = (err == "nssBadCert");
        // Only worry about captive portals if this is a cert error.
        let showCaptivePortalUI = isCaptive() && gIsCertError;
        if (showCaptivePortalUI) {
          err = "captivePortal";
        }

        let pageTitle = document.getElementById("ept_" + err);
        if (pageTitle) {
          document.title = pageTitle.textContent;
        }

        // if it's an unknown error or there's no title or description
        // defined, get the generic message
        var errTitle = document.getElementById("et_" + err);
        var errDesc  = document.getElementById("ed_" + err);
        if (!errTitle || !errDesc) {
          errTitle = document.getElementById("et_generic");
          errDesc  = document.getElementById("ed_generic");
        }

        // eslint-disable-next-line no-unsanitized/property
        document.querySelector(".title-text").innerHTML = errTitle.innerHTML;

        var sd = document.getElementById("errorShortDescText");
        if (sd) {
          if (gIsCertError) {
          // eslint-disable-next-line no-unsanitized/property
            sd.innerHTML = errDesc.innerHTML;
          } else {
            sd.textContent = getDescription();
          }
        }
        if (showCaptivePortalUI) {
          initPageCaptivePortal();
          return;
        }
        if (gIsCertError) {
          initPageCertError();
          return;
        }
        addAutofocus("errorTryAgain");

        document.body.classList.add("neterror");

        var ld = document.getElementById("errorLongDesc");
        if (ld) {
        // eslint-disable-next-line no-unsanitized/property
          ld.innerHTML = errDesc.innerHTML;
        }

        if (err == "sslv3Used") {
          document.getElementById("learnMoreContainer").style.display = "block";
          let learnMoreLink = document.getElementById("learnMoreLink");
          learnMoreLink.href = "https://support.mozilla.org/kb/how-resolve-sslv3-error-messages-firefox";
          document.body.className = "certerror";
        }

        // remove undisplayed errors to avoid bug 39098
        var errContainer = document.getElementById("errorContainer");
        errContainer.remove();

        var className = getCSSClass();
        if (className && className != "expertBadCert") {
          // Associate a CSS class with the root of the page, if one was passed in,
          // to allow custom styling.
          // Not "expertBadCert" though, don't want to deal with the favicon
          document.documentElement.className = className;

          // Also, if they specified a CSS class, they must supply their own
          // favicon.  In order to trigger the browser to repaint though, we
          // need to remove/add the link element.
          var favicon = document.getElementById("favicon");
          var faviconParent = favicon.parentNode;
          faviconParent.removeChild(favicon);
          favicon.setAttribute("href", "chrome://global/skin/icons/" + className + "_favicon.png");
          faviconParent.appendChild(favicon);
        }

        if (err == "remoteXUL") {
          // Remove the "Try again" button for remote XUL errors given that
          // it is useless.
          document.getElementById("netErrorButtonContainer").style.display = "none";
        }

        if (err == "cspBlocked") {
          // Remove the "Try again" button for CSP violations, since it's
          // almost certainly useless. (Bug 553180)
          document.getElementById("netErrorButtonContainer").style.display = "none";
        }

        window.addEventListener("AboutNetErrorOptions", function(evt) {
        // Pinning errors are of type nssFailure2
          if (getErrorCode() == "nssFailure2") {
            document.getElementById("learnMoreContainer").style.display = "block";
            let learnMoreLink = document.getElementById("learnMoreLink");
            // nssFailure2 also gets us other non-overrideable errors. Choose
            // a "learn more" link based on description:
            if (getDescription().includes("MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE")) {
              learnMoreLink.href = "https://support.mozilla.org/kb/certificate-pinning-reports";
            }

            var options = JSON.parse(evt.detail);
            if (options && options.enabled) {
              var checkbox = document.getElementById("automaticallyReportInFuture");
              showCertificateErrorReporting();
              if (options.automatic) {
                // set the checkbox
                checkbox.checked = true;
              }

              checkbox.addEventListener("change", function(changeEvt) {
                  var event = new CustomEvent("AboutNetErrorSetAutomatic",
                    {bubbles: true,
                     detail: changeEvt.target.checked});
                  document.dispatchEvent(event);
                });
            }
            const hasPrefStyleError = [
              "interrupted", // This happens with subresources that are above the max tls
              "SSL_ERROR_PROTOCOL_VERSION_ALERT",
              "SSL_ERROR_UNSUPPORTED_VERSION",
              "SSL_ERROR_NO_CYPHER_OVERLAP",
              "SSL_ERROR_NO_CIPHERS_SUPPORTED"
            ].some((substring) => getDescription().includes(substring));
            // If it looks like an error that is user config based
            if (getErrorCode() == "nssFailure2" && hasPrefStyleError && options && options.changedCertPrefs) {
              showPrefChangeContainer();
            }
          }
          if (getErrorCode() == "sslv3Used") {
            document.getElementById("advancedButton").style.display = "none";
          }
        }, true, true);

        var event = new CustomEvent("AboutNetErrorLoad", {bubbles: true});
        document.dispatchEvent(event);

        if (err == "inadequateSecurityError" || err == "blockedByPolicy") {
          // Remove the "Try again" button from pages that don't need it.
          // For HTTP/2 inadequate security or pages blocked by policy, trying
          // again won't help.
          document.getElementById("errorTryAgain").style.display = "none";

          var container = document.getElementById("errorLongDesc");
          for (var span of container.querySelectorAll("span.hostname")) {
            span.textContent = document.location.hostname;
          }
        }

        addDomainErrorLinks();
      }

      function initPageCaptivePortal() {
        document.body.className = "captiveportal";
        document.getElementById("openPortalLoginPageButton")
                .addEventListener("click", () => {
          let event = new CustomEvent("AboutNetErrorOpenCaptivePortal", {bubbles: true});
          document.dispatchEvent(event);
        });

        addAutofocus("openPortalLoginPageButton");
        setupAdvancedButton();

        addDomainErrorLinks();

        // When the portal is freed, an event is generated by the frame script
        // that we can pick up and attempt to reload the original page.
        window.addEventListener("AboutNetErrorCaptivePortalFreed", () => {
          document.location.reload();
        });
      }

      function initPageCertError() {
        document.body.className = "certerror";
        for (let host of document.querySelectorAll(".hostname")) {
          host.textContent = document.location.hostname;
        }

        addAutofocus("returnButton");
        setupAdvancedButton();

        document.getElementById("learnMoreContainer").style.display = "block";

        let checkbox = document.getElementById("automaticallyReportInFuture");
        checkbox.addEventListener("change", function({target: {checked}}) {
          document.dispatchEvent(new CustomEvent("AboutNetErrorSetAutomatic", {
            detail: checked,
            bubbles: true
          }));
        });

        addEventListener("AboutNetErrorOptions", function(event) {
          var options = JSON.parse(event.detail);
          if (options && options.enabled) {
            // Display error reporting UI
            document.getElementById("certificateErrorReporting").style.display = "block";

            // set the checkbox
            checkbox.checked = !!options.automatic;
          }
          if (options && options.hideAddExceptionButton) {
            document.querySelector(".exceptionDialogButtonContainer").hidden = true;
          }
        }, true, true);

        let event = new CustomEvent("AboutNetErrorLoad", {bubbles: true});
        document.getElementById("advancedButton").dispatchEvent(event);

        addDomainErrorLinks();
      }

      /* Only do autofocus if we're the toplevel frame; otherwise we
         don't want to call attention to ourselves!  The key part is
         that autofocus happens on insertion into the tree, so we
         can remove the button, add @autofocus, and reinsert the
         button.
      */
      function addAutofocus(buttonId, position = "afterbegin") {
        if (window.top == window) {
            var button = document.getElementById(buttonId);
            var parent = button.parentNode;
            button.remove();
            button.setAttribute("autofocus", "true");
            parent.insertAdjacentElement(position, button);
        }
      }

      /* Try to preserve the links contained in the error description, like
         the error code.

         Also, in the case of SSL error pages about domain mismatch, see if
         we can hyperlink the user to the correct site.  We don't want
         to do this generically since it allows MitM attacks to redirect
         users to a site under attacker control, but in certain cases
         it is safe (and helpful!) to do so.  Bug 402210
      */
      function addDomainErrorLinks() {
        // Rather than textContent, we need to treat description as HTML
        var sdid = gIsCertError ? "badCertTechnicalInfo" : "errorShortDescText";
        var sd = document.getElementById(sdid);
        if (sd) {
          var desc = getDescription();

          // sanitize description text - see bug 441169

          // First, find the index of the <a> tags we care about, being
          // careful not to use an over-greedy regex.
          var codeRe = /<a id="errorCode" title="([^"]+)">/;
          var codeResult = codeRe.exec(desc);
          var domainRe = /<a id="cert_domain_link" title="([^"]+)">/;
          var domainResult = domainRe.exec(desc);

          // The order of these links in the description is fixed in
          // TransportSecurityInfo.cpp:formatOverridableCertErrorMessage.
          var firstResult = domainResult;
          if (!domainResult)
            firstResult = codeResult;
          if (!firstResult)
            return;
          // Remove sd's existing children
          sd.textContent = "";

          // Everything up to the first link should be text content.
          sd.appendChild(document.createTextNode(desc.slice(0, firstResult.index)));

          // Now create the actual links.
          if (domainResult) {
            createLink(sd, "cert_domain_link", domainResult[1]);
            // Append text for anything between the two links.
            sd.appendChild(document.createTextNode(desc.slice(desc.indexOf("</a>") + "</a>".length, codeResult.index)));
          }
          createLink(sd, "errorCode", codeResult[1]);

          // Finally, append text for anything after the last closing </a>.
          sd.appendChild(document.createTextNode(desc.slice(desc.lastIndexOf("</a>") + "</a>".length)));
        }

        if (gIsCertError) {
          // Initialize the error code link embedded in the error message to
          // display debug information about the cert error.
          var errorCode = document.getElementById("errorCode");
          if (errorCode) {
            errorCode.href = "javascript:void(0)";
            errorCode.addEventListener("click", () => {
              let debugInfo = document.getElementById("certificateErrorDebugInformation");
              debugInfo.style.display = "block";
              debugInfo.scrollIntoView({block: "start", behavior: "smooth"});
            });
          }
        }

        // Initialize the cert domain link.
        var link = document.getElementById("cert_domain_link");
        if (!link)
          return;

        var okHost = link.getAttribute("title");
        var thisHost = document.location.hostname;
        var proto = document.location.protocol;

        // If okHost is a wildcard domain ("*.example.com") let's
        // use "www" instead.  "*.example.com" isn't going to
        // get anyone anywhere useful. bug 432491
        okHost = okHost.replace(/^\*\./, "www.");

        /* case #1:
         * example.com uses an invalid security certificate.
         *
         * The certificate is only valid for www.example.com
         *
         * Make sure to include the "." ahead of thisHost so that
         * a MitM attack on paypal.com doesn't hyperlink to "notpaypal.com"
         *
         * We'd normally just use a RegExp here except that we lack a
         * library function to escape them properly (bug 248062), and
         * domain names are famous for having '.' characters in them,
         * which would allow spurious and possibly hostile matches.
         */
        if (okHost.endsWith("." + thisHost))
          link.href = proto + okHost;

        /* case #2:
         * browser.garage.maemo.org uses an invalid security certificate.
         *
         * The certificate is only valid for garage.maemo.org
         */
        if (thisHost.endsWith("." + okHost))
          link.href = proto + okHost;

        // If we set a link, meaning there's something helpful for
        // the user here, expand the section by default
        if (link.href && getCSSClass() != "expertBadCert") {
          toggleDisplay(document.getElementById("badCertAdvancedPanel"));
          if (gIsCertError) {
            // Toggling the advanced panel must ensure that the debugging
            // information panel is hidden as well, since it's opened by the
            // error code link in the advanced panel.
            var div = document.getElementById("certificateErrorDebugInformation");
            div.style.display = "none";
          }
        }
      }

      function createLink(el, id, text) {
        var anchorEl = document.createElement("a");
        anchorEl.setAttribute("id", id);
        anchorEl.setAttribute("title", text);
        anchorEl.appendChild(document.createTextNode(text));
        el.appendChild(anchorEl);
      }
    ]]></script>
  </head>

  <body dir="&locale.dir;">
    <!-- ERROR ITEM CONTAINER (removed during loading to avoid bug 39098) -->
    <div id="errorContainer">
      <div id="errorPageTitlesContainer">
        <span id="ept_nssBadCert">&certerror.pagetitle1;</span>
        <span id="ept_captivePortal">&captivePortal.title;</span>
        <span id="ept_dnsNotFound">&dnsNotFound.pageTitle;</span>
        <span id="ept_malformedURI">&malformedURI.pageTitle;</span>
        <span id="ept_blockedByPolicy">&blockedByPolicy.title;</span>
      </div>
      <div id="errorTitlesContainer">
        <h1 id="et_generic">&generic.title;</h1>
        <h1 id="et_captivePortal">&captivePortal.title;</h1>
        <h1 id="et_dnsNotFound">&dnsNotFound.title1;</h1>
        <h1 id="et_fileNotFound">&fileNotFound.title;</h1>
        <h1 id="et_fileAccessDenied">&fileAccessDenied.title;</h1>
        <h1 id="et_malformedURI">&malformedURI.title1;</h1>
        <h1 id="et_unknownProtocolFound">&unknownProtocolFound.title;</h1>
        <h1 id="et_connectionFailure">&connectionFailure.title;</h1>
        <h1 id="et_netTimeout">&netTimeout.title;</h1>
        <h1 id="et_redirectLoop">&redirectLoop.title;</h1>
        <h1 id="et_unknownSocketType">&unknownSocketType.title;</h1>
        <h1 id="et_netReset">&netReset.title;</h1>
        <h1 id="et_notCached">&notCached.title;</h1>
        <h1 id="et_netOffline">&netOffline.title;</h1>
        <h1 id="et_netInterrupt">&netInterrupt.title;</h1>
        <h1 id="et_deniedPortAccess">&deniedPortAccess.title;</h1>
        <h1 id="et_proxyResolveFailure">&proxyResolveFailure.title;</h1>
        <h1 id="et_proxyConnectFailure">&proxyConnectFailure.title;</h1>
        <h1 id="et_contentEncodingError">&contentEncodingError.title;</h1>
        <h1 id="et_unsafeContentType">&unsafeContentType.title;</h1>
        <h1 id="et_nssFailure2">&nssFailure2.title;</h1>
        <h1 id="et_nssBadCert">&certerror.longpagetitle1;</h1>
        <h1 id="et_cspBlocked">&cspBlocked.title;</h1>
        <h1 id="et_remoteXUL">&remoteXUL.title;</h1>
        <h1 id="et_corruptedContentErrorv2">&corruptedContentErrorv2.title;</h1>
        <h1 id="et_sslv3Used">&sslv3Used.title;</h1>
        <h1 id="et_inadequateSecurityError">&inadequateSecurityError.title;</h1>
        <h1 id="et_blockedByPolicy">&blockedByPolicy.title;</h1>
      </div>
      <div id="errorDescriptionsContainer">
        <div id="ed_generic">&generic.longDesc;</div>
        <div id="ed_captivePortal">&captivePortal.longDesc2;</div>
        <div id="ed_dnsNotFound">&dnsNotFound.longDesc1;</div>
        <div id="ed_fileNotFound">&fileNotFound.longDesc;</div>
        <div id="ed_fileAccessDenied">&fileAccessDenied.longDesc;</div>
        <div id="ed_malformedURI"></div>
        <div id="ed_unknownProtocolFound">&unknownProtocolFound.longDesc;</div>
        <div id="ed_connectionFailure">&connectionFailure.longDesc;</div>
        <div id="ed_netTimeout">&netTimeout.longDesc;</div>
        <div id="ed_redirectLoop">&redirectLoop.longDesc;</div>
        <div id="ed_unknownSocketType">&unknownSocketType.longDesc;</div>
        <div id="ed_netReset">&netReset.longDesc;</div>
        <div id="ed_notCached">&notCached.longDesc;</div>
        <div id="ed_netOffline">&netOffline.longDesc2;</div>
        <div id="ed_netInterrupt">&netInterrupt.longDesc;</div>
        <div id="ed_deniedPortAccess">&deniedPortAccess.longDesc;</div>
        <div id="ed_proxyResolveFailure">&proxyResolveFailure.longDesc;</div>
        <div id="ed_proxyConnectFailure">&proxyConnectFailure.longDesc;</div>
        <div id="ed_contentEncodingError">&contentEncodingError.longDesc;</div>
        <div id="ed_unsafeContentType">&unsafeContentType.longDesc;</div>
        <div id="ed_nssFailure2">&nssFailure2.longDesc2;</div>
        <div id="ed_nssBadCert">&certerror.introPara;</div>
        <div id="ed_cspBlocked">&cspBlocked.longDesc;</div>
        <div id="ed_remoteXUL">&remoteXUL.longDesc;</div>
        <div id="ed_corruptedContentErrorv2">&corruptedContentErrorv2.longDesc;</div>
        <div id="ed_sslv3Used">&sslv3Used.longDesc2;</div>
        <div id="ed_inadequateSecurityError">&inadequateSecurityError.longDesc;</div>
        <div id="ed_blockedByPolicy"></div>
      </div>
    </div>

    <!-- PAGE CONTAINER (for styling purposes only) -->
    <div id="errorPageContainer" class="container">
      <div id="text-container">
        <!-- Error Title -->
        <div class="title">
          <h1 class="title-text"/>
        </div>

        <!-- LONG CONTENT (the section most likely to require scrolling) -->
        <div id="errorLongContent">

          <!-- Short Description -->
          <div id="errorShortDesc">
            <p id="errorShortDescText" />
          </div>
          <p id="badStsCertExplanation" hidden="true">&certerror.whatShouldIDo.badStsCertExplanation;</p>

          <div id="wrongSystemTimePanel" style="display: none;">
            &certerror.wrongSystemTime2;
          </div>

          <div id="wrongSystemTimeWithoutReferencePanel" style="display: none;">
            &certerror.wrongSystemTimeWithoutReference;
          </div>

          <!-- Long Description (Note: See netError.dtd for used XHTML tags) -->
          <div id="errorLongDesc" />

          <div id="learnMoreContainer">
            <p><a href="https://support.mozilla.org/kb/what-does-your-connection-is-not-secure-mean" id="learnMoreLink" target="new">&errorReporting.learnMore;</a></p>
          </div>
        </div>

        <!-- UI for option to report certificate errors to Mozilla. Removed on
             init for other error types .-->
        <div id="certificateErrorReporting">
          <p class="toggle-container-with-text">
            <input type="checkbox" id="automaticallyReportInFuture" role="checkbox" />
            <label for="automaticallyReportInFuture" id="automaticallyReportInFuture">&errorReporting.automatic2;</label>
          </p>
        </div>

        <div id="prefChangeContainer" class="button-container">
          <p>&prefReset.longDesc;</p>
          <button id="prefResetButton" class="primary" autocomplete="off">&prefReset.label;</button>
        </div>

        <div id="certErrorAndCaptivePortalButtonContainer" class="button-container">
          <button id="returnButton" class="primary" autocomplete="off">&returnToPreviousPage.label;</button>
          <button id="openPortalLoginPageButton" class="primary" autocomplete="off">&openPortalLoginPage.label2;</button>
          <button id="advancedButton" autocomplete="off">&advanced.label;</button>
        </div>
      </div>

      <div id="netErrorButtonContainer" class="button-container">
        <button id="errorTryAgain" class="primary" autocomplete="off" onclick="retryThis(this);">&retry.label;</button>
      </div>

      <div id="advancedPanelContainer">
        <div id="badCertAdvancedPanel" class="advanced-panel">
          <p id="badCertTechnicalInfo"/>
          <div class="exceptionDialogButtonContainer">
            <button id="exceptionDialogButton">&securityOverride.exceptionButtonLabel;</button>
          </div>
        </div>

        <div id="certificateErrorDebugInformation">
          <button id="copyToClipboard">&certerror.copyToClipboard.label;</button>
          <div id="certificateErrorText"/>
          <button id="copyToClipboard">&certerror.copyToClipboard.label;</button>
        </div>
      </div>
    </div>

    <!--
    - Note: It is important to run the script this way, instead of using
    - an onload handler. This is because error pages are loaded as
    - LOAD_BACKGROUND, which means that onload handlers will not be executed.
    -->
    <script type="application/javascript">
      initPage();
    </script>

  </body>
</html>
PK
!<ٚ����7chrome/browser/content/browser/aboutPrivateBrowsing.csshtml.private .showNormal,
html.normal .showPrivate,
body[tpEnabled] .showTpDisabled,
body:not([tpEnabled]) .showTpEnabled {
  display: none !important;
}

.hide {
  display: none;
}
PK
!<-�Q���6chrome/browser/content/browser/aboutPrivateBrowsing.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 FAVICON_QUESTION = "chrome://global/skin/icons/question-32.png";
const STRING_BUNDLE = "chrome://browser/locale/aboutPrivateBrowsing.properties";
const TP_ENABLED_PREF = "privacy.trackingprotection.enabled";
const TP_PB_ENABLED_PREF = "privacy.trackingprotection.pbmode.enabled";

function updateTPInfo() {
  let aboutCapabilities = document.aboutCapabilities;
  let tpButton = document.getElementById("tpButton");
  let tpToggle = document.getElementById("tpToggle");
  let title = document.getElementById("title");
  let titleTracking = document.getElementById("titleTracking");
  let tpSubHeader = document.getElementById("tpSubHeader");

  let globalTrackingEnabled = aboutCapabilities.getBoolPref(TP_ENABLED_PREF, null);
  let trackingEnabled = globalTrackingEnabled ||
                        aboutCapabilities.getBoolPref(TP_PB_ENABLED_PREF, null);

  // if tracking protection is enabled globally we don't even give the user
  // a choice here by hiding the toggle completely.
  tpButton.classList.toggle("hide", globalTrackingEnabled);
  tpToggle.checked = trackingEnabled;
  title.classList.toggle("hide", trackingEnabled);
  titleTracking.classList.toggle("hide", !trackingEnabled);
  tpSubHeader.classList.toggle("tp-off", !trackingEnabled);
}

document.addEventListener("DOMContentLoaded", function() {
  let aboutCapabilities = document.aboutCapabilities;
  if (!aboutCapabilities.isWindowPrivate()) {
    document.documentElement.classList.remove("private");
    document.documentElement.classList.add("normal");
    document.title = aboutCapabilities.getStringFromBundle(STRING_BUNDLE, "title.normal");
    document.getElementById("favicon").setAttribute("href", FAVICON_QUESTION);
    document.getElementById("startPrivateBrowsing").addEventListener("click", function() {
      aboutCapabilities.sendAsyncMessage("OpenPrivateWindow", null);
    });
    return;
  }

  document.title = aboutCapabilities.getStringFromBundle(STRING_BUNDLE, "title.head");
  document.getElementById("startTour").addEventListener("click", function() {
    aboutCapabilities.sendAsyncMessage("DontShowIntroPanelAgain", null);
  });
  document.getElementById("startTour").setAttribute("href",
    aboutCapabilities.formatURLPref("privacy.trackingprotection.introURL"));
  document.getElementById("learnMore").setAttribute("href",
    aboutCapabilities.formatURLPref("app.support.baseURL") + "private-browsing");

  let tpToggle = document.getElementById("tpToggle");
  document.getElementById("tpButton").addEventListener("click", () => {
    tpToggle.click();
  });
  tpToggle.addEventListener("change", function() {
    aboutCapabilities.setBoolPref(TP_PB_ENABLED_PREF, tpToggle.checked).then(function() {
      updateTPInfo();
    });
  });

  updateTPInfo();
});
PK
!<�29chrome/browser/content/browser/aboutPrivateBrowsing.xhtml<?xml version="1.0" encoding="UTF-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/.
-->
<!DOCTYPE html [
  <!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
  %htmlDTD;
  <!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
  %globalDTD;
  <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
  %brandDTD;
  <!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
  %browserDTD;
  <!ENTITY % aboutPrivateBrowsingDTD SYSTEM "chrome://browser/locale/aboutPrivateBrowsing.dtd">
  %aboutPrivateBrowsingDTD;
]>

<html xmlns="http://www.w3.org/1999/xhtml" class="private">
  <head>
    <meta http-equiv="Content-Security-Policy" content="default-src chrome:"/>
    <link id="favicon" rel="icon" type="image/png" href="chrome://browser/skin/privatebrowsing/favicon.svg"/>
    <link rel="stylesheet" href="chrome://browser/content/aboutPrivateBrowsing.css" type="text/css" media="all"/>
    <link rel="stylesheet" href="chrome://browser/skin/privatebrowsing/aboutPrivateBrowsing.css" type="text/css" media="all"/>
    <script type="application/javascript" src="chrome://browser/content/aboutPrivateBrowsing.js"></script>
  </head>

  <body dir="&locale.dir;">
    <p class="showNormal">&aboutPrivateBrowsing.notPrivate;</p>
    <button id="startPrivateBrowsing"
            class="showNormal"
            accesskey="&privatebrowsingpage.openPrivateWindow.accesskey;">&privatebrowsingpage.openPrivateWindow.label;</button>
    <div class="showPrivate container">
      <h1 class="title">
        <span id="title">&privateBrowsing.title;</span>
        <span id="titleTracking">&privateBrowsing.title.tracking;</span>
      </h1>
      <section class="section-main">
        <p>&aboutPrivateBrowsing.info.notsaved.before;<strong>&aboutPrivateBrowsing.info.notsaved.emphasize;</strong>&aboutPrivateBrowsing.info.notsaved.after;</p>
        <div class="list-row">
          <ul>
            <li>&aboutPrivateBrowsing.info.visited;</li>
            <li>&aboutPrivateBrowsing.info.cookies;</li>
            <li>&aboutPrivateBrowsing.info.searches;</li>
            <li>&aboutPrivateBrowsing.info.temporaryFiles;</li>
          </ul>
        </div>
        <p>&aboutPrivateBrowsing.info.saved.before;<strong>&aboutPrivateBrowsing.info.saved.emphasize;</strong>&aboutPrivateBrowsing.info.saved.after2;</p>
        <div class="list-row">
          <ul>
            <li>&aboutPrivateBrowsing.info.bookmarks;</li>
            <li>&aboutPrivateBrowsing.info.downloads;</li>
          </ul>
        </div>
        <p>&aboutPrivateBrowsing.note.before;<strong>&aboutPrivateBrowsing.note.emphasize;</strong>&aboutPrivateBrowsing.note.after;</p>
      </section>

      <h2 id="tpSubHeader" class="about-subheader">
        <span class="tpTitle">&trackingProtection.title;</span>
        <input id="tpToggle" class="toggle toggle-input" type="checkbox"/>
        <span id="tpButton" class="toggle-btn"></span>
      </h2>

      <section class="section-main">
        <p>&trackingProtection.description2;</p>
        <p>
          <a id="startTour" class="button">&trackingProtection.startTour1;</a>
        </p>
      </section>

      <section class="section-main">
        <p class="about-info">&aboutPrivateBrowsing.learnMore3.before;<a id="learnMore" target="_blank">&aboutPrivateBrowsing.learnMore3.title;</a>&aboutPrivateBrowsing.learnMore3.after;</p>
      </section>

    </div>
  </body>
</html>
PK
!<
j�]��3chrome/browser/content/browser/aboutRobots-icon.png�PNG


IHDR@@�iq�vIDATxb �x���'�Ox���������K������J?�1�fi ..\����ۢ��56Vm`����"&>z���'�H�y3F2��-�1b=oll����piDz>44�3<6\g按�g̘�:�����~����^�ֳ͜����KK����_�jᛍ��߸zѮu+��ê�,�6�9\�i�sl���]�>f��D��s�����g�e9�q#�c���٪�y�
��k�XO��6���
�gϜ����W-�����n_��E_��8e@�30,�������̶���e��tG�t�1r�6���أk۶=��/c�d2��fu�^�%��̪�*�X�z��;�_�9ծ�ז,����*l��e�/���oѷ�s�G_|r�����3�|�5-7��X~xpg����;�F�5ZWL͖�Q��h$*�B��K��O=�������w�97��e;�Ǭ;�=$�ng�o`w����9���G�G����M��b��֮a��(_{�Jq�a-~�<��NL��64V\F1a')��ض��%1�  IR�RH)ql�l&C.�����B!O[.��}8ID��Џm��G�}k�T~"�F��[wV��jo͊.2�sz�-��K�����F��6z)컚#�>9�矸����V�*�t�՗^�p��p5���S�e�B�0����0�@�%$�c��
@.K6��=l��w\���bQ�\r��z@G�4J�ZQx7��V��:�Ϟ�!|iKU0�j�|�'�>�jMV�1�Ghnf����L��^u1@�|��O�r���u˽��tm�Aip�Y
����<��4M���(��J��Alja��(�8b�8�+;��M�۲r�JV��V.���_�+���CE7��f��2�%	��A�j2Big�	'A4�gl�{b�Z�f}�ڃ��F��Oh�!�B�X�L�s�Z��	Ah����Q�EϖhNMR��gJ(iI�)����
��2BkZAH�ޤZ�3S*1<6v�K[��y����l�ba.HE�RTm��~j�D-(�5nQ�;b�նQ��N�Ʉ�N�~p�Sϐx.�J�u͹�����r�B�'w�����y�m��SWr��?`a{���,Ln`��4�q�"�qp]�j��ti��Ji�Hc�L�D�0O�����')XX���D�V�FZ��JaH�D�B�&1��"b�E�>{Q��FRϥ��8�I��V@�Z���)\A��-���bQ�˜K���>�hAgGF�A�$J׵I��D�ON3:����N��dN?�K�������l��))�tQ�B=�|�Q
�"��� 
&m��]�+�fƁ�C�Nj�Xv �o���a�$A)�2��O8��7܀�ӈ����p�Z��
}K;���{�MO�$	R
�~b��"I�a�mK&&&8n�5?�"�z솽N	�;��^�|q��[I��V6o����o����gB��.!EQrSvDg�%TUDłzi���M����Lyb���@,XBT��k��>y��!�~������AhTi4HirZc��(�H�bph��kV���d�u_�{�t;��/���ڶ���ʋ��[_ع�[uK
�B�Ϭ��z��މ����Ē��ݺ��F��H�4�LҔ0I�ٺ<�͇���a�nD�ƱA��y�$ U��BBL�Ǚ?�2�Ձ����r���>{O���K���P���A�eRe�A*4>>B�|~A���p��XQ�Bڂ4�D�E94r�x�I�HA�*��B�T-ץ��z}��|�2���'����}["M�K���5Q!��@B��@ �gJ��u��6el*L�-����zj�g`�/��f��#i��2�0�� �h���DA�p#߁�ҁ�&�z��{��FG[���ӱ ���IK��|b���U����G���V�\>������C��Ŵ�,­ᶍ������ٵ�d~/h�0Ȱ��&)�mq{�0Z��W`�E��>{l+\�RO�),��l�v�6��Â�nb$�1	0M5:MM_���A�T�W���N�/�[y6��}����T=�ζ���CHa�C���!Q��
ą���چ��9���؀~?r�p��;·���,�g�̰BgiSJ*���5$��g��<�Q��\.�@?��I�ab�zF��P)]�,���a��4Z��$Ij@�<�}��sK�.˭gl�"x�F��
�om{�붿����O�j!Fw%��B`QS��,$�4�|�Id](m�	l�f�y�3�
2����!���h��&Z�����8b�Jnk����s>�}�ϴ^h���?R��������N�wˇ���t!R3H�U�ᕣg`�l�B`�L��ܥ
�0�$F���皘oko3i��͚v�-�7�1�)��O�i�Ð޹���ތ��66�����vr�=
�k>�/;�äy�w/&��.��G��S�h��l��kVә˚R���36Z$MS�󳚉�&L���-(��&��'K��#��pX����9�w����FwO7=ݝtutF�s9��R
��v�8u��1�'	�֮e��1;1E �Ib���/���g(M�R�����ln��o����Ŕ7��8x��c�a��kV��~�$��Z|�P�ï��q}Е��m��iˠ��,�gΜn�z��B�+S{�u��EC/4�2>yۢ}A�ŝr9z�:�g3 ��4RH�(0K��9=��'ޯ�V���?�Z�c|��Ң�w�!��<Plp�5)M�Y�I�	�{O���GƸa�(��t�2�/�31[��XBVS��ժ)U�m��6�w�0���B
��_~&K��D�l���N���1��$1�`ۖa�eYLLN�m� �c�_���g'�����������*9��i\�Z�����`��[�Z+�>=C�v���0��Z�Κ��0U�MN#HK����F���v���M���Ķ�˒�\���+��*M����V���o~��C��ᑑ��,}�'���߿2>v�Ω��a�Q�͠Y}�<v�m��?�����s��#�J;�86+� �h��f|li��	��e�f��
3�+���Z+37��9|�3U��,34 ���t�S%��ūYO=�\����?	��vMQk''�z��~磫��u�[email protected]„|��Ka��s�~�h�Cw<��P��{�-��p,)�8.��Я�k����"MS��r�lJ�f��1�u�1F���
�'=��s|�3���W�
�
[�5f�V��M"TZ��7oy�O�
�n����c�^��
w1<>A��U�l�T��V-px\�uف��w��+�J���N�D������S��jKh°����r�FK�ͺ�q@��K[M:i@�m�6t�f����M�t,��KZ8�@�Z�V�-�=�p^���| ���n����>�����
2)dSIμ7Fc�ֻU,4�t�K��c��ELΔ�֚��y{�^ic��d�w}=m�r�E�k7�۱?���L�����aFa�j�J�Ѡ^��$Yc�$��ha;�x���n�c�;��#��k�fs�D��8Z��%��0�g8Z�~�F��N�nm��7����םch\����Yf+��*�����U�39e�%�Jͫ,)��b�J8�C�2K��H!'I��t�c�{>��v�5�׻����~Q�vq��^��o���&]e�SX�bI{�KcAFi\�p��F ��C����'<p9�Y�4a�����3+���/�u�D����`e1���qmD�*��eJ�F 0φ

�� ET�2[����v���]�]���
���Ց�X9�������~󹣀�%���Oo/(B�T�	:I!�d4�m��G\��
�/O'H
�y�wO�Q����&�3O>�z�k�64�mŀeH�6��$������"̐�
�������]����$S��
Z��0L��n�V���i1V\����̭����N���,�G�s���%>ѽ���^��|O(�HY��Y������O&�ԟ�F���G��y����|&�6�0�27��网��d��mװ��n�Ӂ�H�І�����ߔCs�i��14IS�a�ן�=�~VI�(eb�)\K��>
��3��K�����@G�m���i����
�{�嬳�J�iW�Y
��玲p{ڌG����Z�nD���Kb�ViT�	��uN
B�e�|��W�tq?�=��Y��[�(�a#�K��
5[F���0�αퟨ��yF�R��$5�c:P�M��J�e��
iS��4�}Y��x�}�f�Q\[�ǚ�ۭ���?�8�3��p���Q�w���XҖh2J����ܿ=�������W�a��{�
R�t�A:1˳q�o�8��2^DH���|�f@��4��6��2���a�m٦���DZ)-�!h~�	sj�Rj�Z�X�"	#����?�������(�t�ڕOv�{��X��0ޗ�/�J�D�h������aB,%"ՈD!����oH����У�ͩǣ�	A��ǥ��/c1�LXX�1`��ِ��e�@A�R`y.}�|��Sl�8��@.�I�V�_~uǂ���js������=�03_+?cm�F�e����:�
׶m۶�3�q�Ӯ�>�t�:���uϽ��[���Aʷ`7�{�����o'��2��?��Z@nA�X&_7�:0��R�O��o���,�{��:Ӣ�V4U�|}<���i��F���ZL�BB�A�5J�hq>�G�������7���g3��bq��>|K'F$�a�!`@�6�?�{R8��j����u�mL#8�2=
⭜����[or�i��?�cH�.�mq+E��by�I���!m�=�Ϋ��6��V��S҉�$�F}fjx�=��+�W�2��g�a�ݱ:=��c�<�X|+�#4pH��>*n��Y�u�r:F�{�yMP������%�/�O���>�,c�M�ko�h��$b�Ϊ��E�<C{�>�'ZH������Ons�s���a{�R��~��-/��H�e��BDe�����e�a��N�ށ��X�:���������r����{��7E��1�$��+|�*���j�V���#�KP���UI]ئ&I=�;�Nv����`�>Z�<gqr��ouq��jŽǷH�g��������q��n^5P`XZ��A�Z66��� e�}�� ;Dv"��KYg~~.�c��l�0z�	�Q\_��Գ,�{������Yhɺ�ӃC��6���u"e��	z�۳��T��ݷx��<�t��.;.sY�tƸ��.ɝ=�󫆣-���\���
�z�} ���B��<�a:�bo:e�mp��W�Ԇ �����j�e������d˅,[fK�F*^`i��|�&oQl�8kL��&���<g�7Z�
���<>| wK&������d��3���y'#�8�i���8��!��J��ӽ���Ț��]�T�8�@]Y���`!7bf#Y���z�س5�1=��|>
io����&dח�~�T�.��kƓ�R���>��<E�Y؞�7mL�j�H��$M�8�������k��O��>�/�R}���u����&���I�nO�'SZ�$e<N+_AN��RU�S��r��Ŝ2�جTŦ���ή�ºS�.Ke�U;�_E�:G��zF��7�k�|Ӎ+���H���&����N�� _e<�8��{w�ˏHX��I󮂜�i})�[K�E/�X��?޶<4�ڋ�i�c�5b��i��x*l��B���hqЏ�1��9��ۂtuu�j�T��ؙ�lm+���V���`������CЄq[�$�PA�6ȅ-���"��%o��y�_����{�S�¿�3*Z�`�*�����_���AH�k��,_0�����!���h�(Ve�wxU،Ga��V�nw�j��-���4���<���&�B�u�Rn��ZHZTU�L��ٌG{3F� �US���͋��ǀl
2��n�>P*����|�n�K��'���#9PX���˼`ſ��d�1��Rar�֭^I���#ma!7��{�H����,�J�EY*(֑�������l"�JO:�]�L�ƪ"��M���|���G35zª�g�������3��������{��峚�{��e������AJ���<�䱪�dwW}�������~�ok!�-���(��$!�
ڑ�0JK�K�ޠ�X#��!�F�5T�b�=O胯K�A�ڲRvZ�/0_�I�w���b�E���}�,+�</4����<�����e�z��4l�a�:����>�c�����&��Sc�y�RW�6Hy@��HzF�$k��'/Ե�E]u"��b�$��=� d!��K���vX@P�MM�ۦJ��]��q~v�����7ֈ=��sV��Vt:�pg��rj*�X{q�+�6E&�}��@�=s0����]�2c����z�8M�:�\�1��V�ۋ�ۨ�d�-\<9֋>���!th��
�yYGt�M&��z�!>�
X�d^x�9�'�ne�x�dž���v��'�k) �Fcmc߽s����l�]��V�
:�'73*V<i��;/��^Y�X�w��JH��l��*��b7)zl�"c4��T�EQRu�Ǐx��n'b�EJ%�e�NN���̺�L!N��bI�mi&�ۊ��痴M��:�=���}\!h"�)`	(��=��~�C���fj��D��RZZ�daa@��Үk4F�ݹ�l��:��4�ZP�GGt
�_a�m�Xs¿8�s���A]�����%�Ʉ�(T/�K�==��e���d���h/D��-�:�O*��m�h$!���X�3�7�H��z]��*�E����,X�3������;ݿ�瀍�M������~����S]�B����=<P<X�6�vz��+M����<���C!��ڿ��&�	8k�>��c���V�Me"���i�Jo��п#��W��*u�������ry}-4������˻G�<���
,����|`��*3+�	l��ٹ���BW�h�q��L�5��1�E�T���@����%{���5�fh�n �3��(j�
et���yJ���	�l?�q�X�7�����O���[�`<��k^ȇ����64�\���kk(+��U8��D��wVʋ�D�] �q�x�#��L)4P�K�Et�@h�!.@S�bq-�[�<�q��Ogo���ʗ��t�悰Q+��D���+�R>ܛi?P��Ơ2X��qq�,P��H�Aq�B�&К��A�,F..�tY-(C@p�,1��yB����0�M�J5��n�����s�A����IEND�B`�PK
!<k�O�VV:chrome/browser/content/browser/aboutRobots-widget-left.png�PNG


IHDR�Q1IDATx��@����5h��CB� Q��;DK�K�w��
Roqw�<���������<���3s�`;QP0ec4n��t��z�F�ְ���^
�g]��V��IV���ji�^��_�:'w�|���ڵ�
5+�5�{������[ٷo�-+w�!�b�G *f�F 6aR�3��=HLE�}j�j�Z��J.�)�ےŃv|N������_����O£�>JJ��&=s��u0_	z���Ҍ�y[�`��еzx3xx���?����
O���W��#cG�$��gs�Aoڴo�J�������~.N�u`P��7���5@w��KÓ,������������.Ԭ�ܞ5_���\�8M��r��K��Qtf�&�$$�%��ެ���M@�_L��c?EL��|��!�8��rb�ο-�;y:=kN��5�MM�ޱ;���iUھ�Ժ5$�	�����C�?#���K����Qq�ȝ��2��w�̹�sz���bb̗b;��8/�{�B5-�bK�_?��w�gK�S� ,���G��ӆ\z�3�jb���.������TK�ٗӱ��	��g^�/\��t�ۉ����6�浂�G+�\��+ѭ�c��) �;7<jx.Q<��2c�
V���.��(3�/�MY`-�4-}N��/<с6O�pv��s�{dZ��x�H=2Q�*�Z�L`�8,j *6ab�r��G=&���a��o�B����P�Y������q��xp[���ϩN�V=@gU��{�d��F���l(z*�`2�q��#թ�O�;-8���g`��_`_td��G��it��fK�`22�VII}�а��G����_E�4�t��8���djМ�n&P�C�;�LO���D�T�ii������b�G`J&�a��j���%NL�o�ʆ�LL�>1yS& Y<�-W�?�,�Q�=^�*5�Q�2�QZ�f�wa��О��ԩϳr��`�+&f|�>ǯ
��$����zӚ>G
�<����� ����:?�x����6	�P,��^�x
<����Q�E"�F/�	/���w�X�T>�=r�(�T�h;��i~B�w
�GPKW��d��5;��������:��m��@�m����T�7�hWL���<m�Ӡ�Sh����(թ���M�K�(W�~4&n�m�ȽK�j�v�Ž��Q����n	J�6���RO�yܕ�l�'	�r{��oC,z̆�I��]W�'�K�P.<��:n�
���Oi�
`��P��|'�;��D6��;�=�Z�V���C�
Z�����b\9el���솯9��2��1	L|��h�a�J+��	:�R$�O���?��5tf�UV݇�ǂv�����e�T��D/��De����gM�<��/C?���7�Y~u��CR��%b�e��0���yG0�#I� ���qI��f�,=ۣ`�4���[�ⵡʺ@��FQ8
,v
p<�!$��g
.Ȱ�Ď���e���&�@�#�j�+=�#���'�w�bx���*�u�/�yK,���ԑ�\ܚf��
ϲ�[�E~ǐ#������y@ :��Y��p�
ۉ1�_w?�N�i��@�;(ص4��R��-ω��a�~�L1}5(3���
D�	��J��G�<���j˒[�����e�_'���
Cl� R����8ȵ��<�}.�pri�2�����l��;�w�~�d����lxtǙ��e�Oqgeu��U5p�2�|�a�Ƅ��ʊݶ��u$9㤺y1ݚ�n6�*A|R?$$���̮�a0J�^^u���ځ[�=��L��,7�t$A=j�U��!쩥_�h�(�+�/8�\y䊑!��pO]��C�<xGI�E��(jk'�M6�Plm�4��y�
��E2E��U�5_��QxD���]<��r_CC�M�»�����.��H�f�*kk	�!��T�(N%J�R���&�P��ʕJ%�WT2@��8a�o9��cg0gưK�ߙ��t�}�IEND�B`�PK
!<�x���.chrome/browser/content/browser/aboutRobots.css#errorPageContainer {
  background-image: none;
}

#errorPageContainer:before {
  content: url('chrome://browser/content/aboutRobots-icon.png');
  position: absolute;
}

body[dir=rtl] #icon,
body[dir=rtl] #errorPageContainer:before {
  transform: scaleX(-1);
}

#widget1 {
  position: absolute;
  bottom: -12px;
  left: -10px;
}

#widget2 {
  position: absolute;
  bottom: -12px;
  right: -10px;
  transform: scaleX(-1);
}
PK
!<��vR00-chrome/browser/content/browser/aboutRobots.jsvar buttonClicked = false;
var button = document.getElementById("errorTryAgain");
button.onclick = function() {
  if (buttonClicked) {
    button.style.visibility = "hidden";
  } else {
    var newLabel = button.getAttribute("label2");
    button.textContent = newLabel;
    buttonClicked = true;
  }
};
PK
!<pe

0chrome/browser/content/browser/aboutRobots.xhtml<?xml version="1.0" encoding="UTF-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/. -->

<!DOCTYPE html [
  <!ENTITY % htmlDTD
    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "DTD/xhtml1-strict.dtd">
  %htmlDTD;
  <!ENTITY % netErrorDTD
    SYSTEM "chrome://global/locale/netError.dtd">
  %netErrorDTD;
  <!ENTITY % globalDTD
    SYSTEM "chrome://global/locale/global.dtd">
  %globalDTD;
  <!ENTITY % aboutrobotsDTD
    SYSTEM "chrome://browser/locale/aboutRobots.dtd">
  %aboutrobotsDTD;
]>

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Security-Policy" content="default-src chrome:" />
    <title>&robots.pagetitle;</title>
    <link rel="stylesheet" href="chrome://global/skin/netError.css" type="text/css" media="all" />
    <link rel="icon" type="image/png" id="favicon" href="chrome://browser/content/robot.ico"/>
    <link rel="stylesheet" href="chrome://browser/content/aboutRobots.css" type="text/css"/>
  </head>

  <body dir="&locale.dir;">

    <!-- PAGE CONTAINER (for styling purposes only) -->
    <div id="errorPageContainer">

      <!-- Error Title -->
      <div id="errorTitle">
        <h1 id="errorTitleText">&robots.errorTitleText;</h1>
      </div>

      <!-- LONG CONTENT (the section most likely to require scrolling) -->
      <div id="errorLongContent">

        <!-- Short Description -->
        <div id="errorShortDesc">
          <p id="errorShortDescText">&robots.errorShortDescText;</p>
        </div>

        <!-- Long Description (Note: See netError.dtd for used XHTML tags) -->
        <div id="errorLongDesc">
          <ul>
            <li>&robots.errorLongDesc1;</li>
            <li>&robots.errorLongDesc2;</li>
            <li>&robots.errorLongDesc3;</li>
            <li>&robots.errorLongDesc4;</li>
          </ul>
        </div>

        <!-- Short Description -->
        <div id="errorTrailerDesc">
          <p id="errorTrailerDescText">&robots.errorTrailerDescText;</p>
        </div>

      </div>

      <!-- Button -->
      <button id="errorTryAgain"
              label2="&robots.dontpress;">&retry.label;</button>

      <img id="widget1" src="chrome://browser/content/aboutRobots-widget-left.png"/>
      <img id="widget2" src="chrome://browser/content/aboutRobots-widget-left.png"/>
    </div>
  </body>
  <script type="application/javascript" src="chrome://browser/content/aboutRobots.js"/>
</html>
PK
!<,��.�.5chrome/browser/content/browser/aboutSessionRestore.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.defineModuleGetter(this, "AppConstants",
  "resource://gre/modules/AppConstants.jsm");

var gStateObject;
var gTreeData;

// Page initialization

window.onload = function() {
  let toggleTabs = document.getElementById("tabsToggle");
  if (toggleTabs) {
    let treeContainer = document.querySelector(".tree-container");

    let toggleHiddenTabs = () => {
      toggleTabs.classList.toggle("show-tabs");
      treeContainer.classList.toggle("expanded");
    };
    toggleTabs.onclick = toggleHiddenTabs;
  }

  // pages used by this script may have a link that needs to be updated to
  // the in-product link.
  let anchor = document.getElementById("linkMoreTroubleshooting");
  if (anchor) {
    let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
    anchor.setAttribute("href", baseURL + "troubleshooting");
  }

  // wire up click handlers for the radio buttons if they exist.
  for (let radioId of ["radioRestoreAll", "radioRestoreChoose"]) {
    let button = document.getElementById(radioId);
    if (button) {
      button.addEventListener("click", updateTabListVisibility);
    }
  }

  // the crashed session state is kept inside a textbox so that SessionStore picks it up
  // (for when the tab is closed or the session crashes right again)
  var sessionData = document.getElementById("sessionData");
  if (!sessionData.value) {
    document.getElementById("errorTryAgain").disabled = true;
    return;
  }

  gStateObject = JSON.parse(sessionData.value);

  // make sure the data is tracked to be restored in case of a subsequent crash
  var event = document.createEvent("UIEvents");
  event.initUIEvent("input", true, true, window, 0);
  sessionData.dispatchEvent(event);

  initTreeView();

  document.getElementById("errorTryAgain").focus();
};

function isTreeViewVisible() {
  let tabList = document.querySelector(".tree-container");
  return tabList.hasAttribute("available");
}

function initTreeView() {
  // If we aren't visible we initialize as we are made visible (and it's OK
  // to initialize multiple times)
  if (!isTreeViewVisible()) {
    return;
  }
  var tabList = document.getElementById("tabList");
  var winLabel = tabList.getAttribute("_window_label");

  gTreeData = [];
  gStateObject.windows.forEach(function(aWinData, aIx) {
    var winState = {
      label: winLabel.replace("%S", (aIx + 1)),
      open: true,
      checked: true,
      ix: aIx
    };
    winState.tabs = aWinData.tabs.map(function(aTabData) {
      var entry = aTabData.entries[aTabData.index - 1] || { url: "about:blank" };
      var iconURL = aTabData.image || null;
      // don't initiate a connection just to fetch a favicon (see bug 462863)
      if (/^https?:/.test(iconURL))
        iconURL = "moz-anno:favicon:" + iconURL;
      return {
        label: entry.title || entry.url,
        checked: true,
        src: iconURL,
        parent: winState
      };
    });
    gTreeData.push(winState);
    for (let tab of winState.tabs)
      gTreeData.push(tab);
  }, this);

  tabList.view = treeView;
  tabList.view.selection.select(0);
}

// User actions
function updateTabListVisibility() {
  let tabList = document.querySelector(".tree-container");
  let container = document.querySelector(".container");
  if (document.getElementById("radioRestoreChoose").checked) {
    tabList.setAttribute("available", "true");
    container.classList.add("restore-chosen");
  } else {
    tabList.removeAttribute("available");
    container.classList.remove("restore-chosen");
  }
  initTreeView();
}

function restoreSession() {
  Services.obs.notifyObservers(null, "sessionstore-initiating-manual-restore");
  document.getElementById("errorTryAgain").disabled = true;

  if (isTreeViewVisible()) {
    if (!gTreeData.some(aItem => aItem.checked)) {
      // This should only be possible when we have no "cancel" button, and thus
      // the "Restore session" button always remains enabled.  In that case and
      // when nothing is selected, we just want a new session.
      startNewSession();
      return;
    }

    // remove all unselected tabs from the state before restoring it
    var ix = gStateObject.windows.length - 1;
    for (var t = gTreeData.length - 1; t >= 0; t--) {
      if (treeView.isContainer(t)) {
        if (gTreeData[t].checked === 0)
          // this window will be restored partially
          gStateObject.windows[ix].tabs =
            gStateObject.windows[ix].tabs.filter((aTabData, aIx) =>
                                                   gTreeData[t].tabs[aIx].checked);
        else if (!gTreeData[t].checked)
          // this window won't be restored at all
          gStateObject.windows.splice(ix, 1);
        ix--;
      }
    }
  }
  var stateString = JSON.stringify(gStateObject);

  var ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
  var top = getBrowserWindow();

  // if there's only this page open, reuse the window for restoring the session
  if (top.gBrowser.tabs.length == 1) {
    ss.setWindowState(top, stateString, true);
    return;
  }

  // restore the session into a new window and close the current tab
  var newWindow = top.openDialog(top.location, "_blank", "chrome,dialog=no,all");

  Services.obs.addObserver(function observe(win, topic) {
    if (win != newWindow) {
      return;
    }

    Services.obs.removeObserver(observe, topic);
    ss.setWindowState(newWindow, stateString, true);

    var tabbrowser = top.gBrowser;
    var tabIndex = tabbrowser.getBrowserIndexForDocument(document);
    tabbrowser.removeTab(tabbrowser.tabs[tabIndex]);
  }, "browser-delayed-startup-finished");
}

function startNewSession() {
  if (Services.prefs.getIntPref("browser.startup.page") == 0)
    getBrowserWindow().gBrowser.loadURI("about:blank");
  else
    getBrowserWindow().BrowserHome();
}

function onListClick(aEvent) {
  // don't react to right-clicks
  if (aEvent.button == 2)
    return;

  var cell = treeView.treeBox.getCellAt(aEvent.clientX, aEvent.clientY);
  if (cell.col) {
    // Restore this specific tab in the same window for middle/double/accel clicking
    // on a tab's title.
    let accelKey = AppConstants.platform == "macosx" ?
                   aEvent.metaKey :
                   aEvent.ctrlKey;
    if ((aEvent.button == 1 || aEvent.button == 0 && aEvent.detail == 2 || accelKey) &&
        cell.col.id == "title" &&
        !treeView.isContainer(cell.row)) {
      restoreSingleTab(cell.row, aEvent.shiftKey);
      aEvent.stopPropagation();
    } else if (cell.col.id == "restore")
      toggleRowChecked(cell.row);
  }
}

function onListKeyDown(aEvent) {
  switch (aEvent.keyCode) {
  case KeyEvent.DOM_VK_SPACE:
    toggleRowChecked(document.getElementById("tabList").currentIndex);
    // Prevent page from scrolling on the space key.
    aEvent.preventDefault();
    break;
  case KeyEvent.DOM_VK_RETURN:
    var ix = document.getElementById("tabList").currentIndex;
    if (aEvent.ctrlKey && !treeView.isContainer(ix))
      restoreSingleTab(ix, aEvent.shiftKey);
    break;
  }
}

// Helper functions

function getBrowserWindow() {
  return window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation)
               .QueryInterface(Ci.nsIDocShellTreeItem).rootTreeItem
               .QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
}

function toggleRowChecked(aIx) {
  function isChecked(aItem) {
    return aItem.checked;
  }

  var item = gTreeData[aIx];
  item.checked = !item.checked;
  treeView.treeBox.invalidateRow(aIx);

  if (treeView.isContainer(aIx)) {
    // (un)check all tabs of this window as well
    for (let tab of item.tabs) {
      tab.checked = item.checked;
      treeView.treeBox.invalidateRow(gTreeData.indexOf(tab));
    }
  } else {
    // Update the window's checkmark as well (0 means "partially checked").
    let state = false;
    if (item.parent.tabs.every(isChecked)) {
      state = true;
    } else if (item.parent.tabs.some(isChecked)) {
      state = 0;
    }
    item.parent.checked = state;

    treeView.treeBox.invalidateRow(gTreeData.indexOf(item.parent));
  }

  // we only disable the button when there's no cancel button.
  if (document.getElementById("errorCancel")) {
    document.getElementById("errorTryAgain").disabled = !gTreeData.some(isChecked);
  }
}

function restoreSingleTab(aIx, aShifted) {
  var tabbrowser = getBrowserWindow().gBrowser;
  var newTab = tabbrowser.addTab();
  var item = gTreeData[aIx];

  var ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
  var tabState = gStateObject.windows[item.parent.ix]
                             .tabs[aIx - gTreeData.indexOf(item.parent) - 1];
  // ensure tab would be visible on the tabstrip.
  tabState.hidden = false;
  ss.setTabState(newTab, JSON.stringify(tabState));

  // respect the preference as to whether to select the tab (the Shift key inverses)
  if (Services.prefs.getBoolPref("browser.tabs.loadInBackground") != !aShifted)
    tabbrowser.selectedTab = newTab;
}

// Tree controller

var treeView = {
  treeBox: null,
  selection: null,

  get rowCount() { return gTreeData.length; },
  setTree(treeBox) { this.treeBox = treeBox; },
  getCellText(idx, column) { return gTreeData[idx].label; },
  isContainer(idx) { return "open" in gTreeData[idx]; },
  getCellValue(idx, column) { return gTreeData[idx].checked; },
  isContainerOpen(idx) { return gTreeData[idx].open; },
  isContainerEmpty(idx) { return false; },
  isSeparator(idx) { return false; },
  isSorted() { return false; },
  isEditable(idx, column) { return false; },
  canDrop(idx, orientation, dt) { return false; },
  getLevel(idx) { return this.isContainer(idx) ? 0 : 1; },

  getParentIndex(idx) {
    if (!this.isContainer(idx))
      for (var t = idx - 1; t >= 0 ; t--)
        if (this.isContainer(t))
          return t;
    return -1;
  },

  hasNextSibling(idx, after) {
    var thisLevel = this.getLevel(idx);
    for (var t = after + 1; t < gTreeData.length; t++)
      if (this.getLevel(t) <= thisLevel)
        return this.getLevel(t) == thisLevel;
    return false;
  },

  toggleOpenState(idx) {
    if (!this.isContainer(idx))
      return;
    var item = gTreeData[idx];
    if (item.open) {
      // remove this window's tab rows from the view
      var thisLevel = this.getLevel(idx);
      for (var t = idx + 1; t < gTreeData.length && this.getLevel(t) > thisLevel; t++);
      var deletecount = t - idx - 1;
      gTreeData.splice(idx + 1, deletecount);
      this.treeBox.rowCountChanged(idx + 1, -deletecount);
    } else {
      // add this window's tab rows to the view
      var toinsert = gTreeData[idx].tabs;
      for (var i = 0; i < toinsert.length; i++)
        gTreeData.splice(idx + i + 1, 0, toinsert[i]);
      this.treeBox.rowCountChanged(idx + 1, toinsert.length);
    }
    item.open = !item.open;
    this.treeBox.invalidateRow(idx);
  },

  getCellProperties(idx, column) {
    if (column.id == "restore" && this.isContainer(idx) && gTreeData[idx].checked === 0)
      return "partial";
    if (column.id == "title")
      return this.getImageSrc(idx, column) ? "icon" : "noicon";

    return "";
  },

  getRowProperties(idx) {
    var winState = gTreeData[idx].parent || gTreeData[idx];
    if (winState.ix % 2 != 0)
      return "alternate";

    return "";
  },

  getImageSrc(idx, column) {
    if (column.id == "title")
      return gTreeData[idx].src || null;
    return null;
  },

  cycleHeader(column) { },
  cycleCell(idx, column) { },
  selectionChanged() { },
  performAction(action) { },
  performActionOnCell(action, index, column) { },
  getColumnProperties(column) { return ""; }
};
PK
!<�*b���8chrome/browser/content/browser/aboutSessionRestore.xhtml<?xml version="1.0" encoding="UTF-8"?>
<!--
-->
<!DOCTYPE html [
  <!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
  %htmlDTD;
  <!ENTITY % netErrorDTD SYSTEM "chrome://global/locale/netError.dtd">
  %netErrorDTD;
  <!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
  %globalDTD;
  <!ENTITY % restorepageDTD SYSTEM "chrome://browser/locale/aboutSessionRestore.dtd">
  %restorepageDTD;
]>

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
  <head>
    <title>&restorepage.tabtitle;</title>
    <link rel="stylesheet" href="chrome://global/skin/in-content/info-pages.css" type="text/css" media="all"/>
    <link rel="stylesheet" href="chrome://browser/skin/aboutSessionRestore.css" type="text/css" media="all"/>
    <link rel="icon" type="image/png" href="chrome://global/skin/icons/warning-16.png"/>

    <script type="application/javascript" src="chrome://browser/content/aboutSessionRestore.js"/>
  </head>

  <body dir="&locale.dir;" class="illustrated">

    <div class="container restore-chosen">
      <div class="description-wrapper">
        <div class="title">
          <h1 class="title-text">&restorepage.errorTitle2;</h1>
        </div>
        <div class="description">
          <p>&restorepage.problemDesc2;</p>

          <div id="errorLongDesc">
            <p>&restorepage.tryThis2;</p>
          </div>
        </div>
        <button id="tabsToggle" class="show-tabs">
          <span id="showTabs">
            &restorepage.showTabs;
          </span>
          <span id="hideTabs">
            &restorepage.hideTabs;
          </span>
        </button>
      </div>
      <div class="tree-container" available="true">
        <xul:tree id="tabList" seltype="single" hidecolumnpicker="true"
              onclick="onListClick(event);" onkeydown="onListKeyDown(event);"
              _window_label="&restorepage.windowLabel;">
          <xul:treecols>
            <xul:treecol cycler="true" id="restore" type="checkbox" label="&restorepage.restoreHeader;"/>
            <xul:splitter class="tree-splitter"/>
            <xul:treecol primary="true" id="title" label="&restorepage.listHeader;" flex="1"/>
          </xul:treecols>
          <xul:treechildren flex="1"/>
        </xul:tree>
      </div>
      <div class="button-container">
        <xul:button id="errorCancel"
                label="&restorepage.closeButton2;"
                accesskey="&restorepage.close.access2;"
                oncommand="startNewSession();"/>
        <xul:button class="primary"
                id="errorTryAgain"
                label="&restorepage.tryagainButton2;"
                accesskey="&restorepage.restore.access2;"
                oncommand="restoreSession();"/>
      </div>
      <!-- holds the session data for when the tab is closed -->
      <input type="text" id="sessionData" style="display: none;"/>
    </div>

  </body>
</html>
PK
!<䉯���2chrome/browser/content/browser/aboutTabCrashed.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

html:not(.crashDumpSubmitted) #reportSent,
html:not(.crashDumpAvailable) #reportBox,
.container[multiple="true"] > .offers > #offerHelpMessageSingle,
.container[multiple="false"] > .offers > #offerHelpMessageMultiple,
.container[multiple="false"] > .button-container > #restoreAll {
  display: none;
}PK
!<T!V��!�!1chrome/browser/content/browser/aboutTabCrashed.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 mozilla/frame-script */

var AboutTabCrashed = {
  /**
   * This can be set to true once this page receives a message from the
   * parent saying whether or not a crash report is available.
   */
  hasReport: false,

  /**
   * The messages that we might receive from the parent.
   */
  MESSAGES: [
    "SetCrashReportAvailable",
    "CrashReportSent",
    "UpdateCount",
  ],

  /**
   * Items for which we will listen for click events.
   */
  CLICK_TARGETS: [
    "closeTab",
    "restoreTab",
    "restoreAll",
    "sendReport",
  ],

  /**
   * Returns information about this crashed tab.
   *
   * @return (Object) An object with the following properties:
   *           title (String):
   *             The title of the page that crashed.
   *           URL (String):
   *             The URL of the page that crashed.
   */
  get pageData() {
    delete this.pageData;

    let URL = document.documentURI;
    let queryString = URL.replace(/^about:tabcrashed?e=tabcrashed/, "");

    let titleMatch = queryString.match(/d=([^&]*)/);
    let URLMatch = queryString.match(/u=([^&]*)/);

    return this.pageData = {
      title: titleMatch && titleMatch[1] ? decodeURIComponent(titleMatch[1]) : "",
      URL: URLMatch && URLMatch[1] ? decodeURIComponent(URLMatch[1]) : "",
    };
  },

  init() {
    this.MESSAGES.forEach((msg) => addMessageListener(msg, this.receiveMessage.bind(this)));
    addEventListener("DOMContentLoaded", this);

    document.title = this.pageData.title;
  },

  receiveMessage(message) {
    switch (message.name) {
      case "UpdateCount": {
        this.setMultiple(message.data.count > 1);
        break;
      }
      case "SetCrashReportAvailable": {
        this.onSetCrashReportAvailable(message);
        break;
      }
      case "CrashReportSent": {
        this.onCrashReportSent();
        break;
      }
    }
  },

  handleEvent(event) {
    switch (event.type) {
      case "DOMContentLoaded": {
        this.onDOMContentLoaded();
        break;
      }
      case "click": {
        this.onClick(event);
        break;
      }
      case "input": {
        this.onInput(event);
        break;
      }
    }
  },

  onDOMContentLoaded() {
    this.CLICK_TARGETS.forEach((targetID) => {
      let el = document.getElementById(targetID);
      el.addEventListener("click", this);
    });

    // For setting "emailMe" checkbox automatically on email value change.
    document.getElementById("email").addEventListener("input", this);

    // Error pages are loaded as LOAD_BACKGROUND, so they don't get load events.
    let event = new CustomEvent("AboutTabCrashedLoad", {bubbles: true});
    document.dispatchEvent(event);

    sendAsyncMessage("Load");
  },

  onClick(event) {
    switch (event.target.id) {
      case "closeTab": {
        this.sendMessage("closeTab");
        break;
      }

      case "restoreTab": {
        this.sendMessage("restoreTab");
        break;
      }

      case "restoreAll": {
        this.sendMessage("restoreAll");
        break;
      }

      case "sendReport": {
        this.showCrashReportUI(event.target.checked);
        break;
      }
    }
  },

  onInput(event) {
    switch (event.target.id) {
      case "email": {
        document.getElementById("emailMe").checked = !!event.target.value;
        break;
      }
    }
  },
  /**
   * After this page tells the parent that it has loaded, the parent
   * will respond with whether or not a crash report is available. This
   * method handles that message.
   *
   * @param message
   *        The message from the parent, which should contain a data
   *        Object property with the following properties:
   *
   *        hasReport (bool):
   *          Whether or not there is a crash report.
   *
   *        sendReport (bool):
   *          Whether or not the the user prefers to send the report
   *          by default.
   *
   *        includeURL (bool):
   *          Whether or not the user prefers to send the URL of
   *          the tab that crashed.
   *
   *        emailMe (bool):
   *          Whether or not to send the email address of the user
   *          in the report.
   *
   *        email (String):
   *          The email address of the user (empty if emailMe is false).
   *
   *        requestAutoSubmit (bool):
   *          Whether or not we should ask the user to automatically
   *          submit backlogged crash reports.
   *
   */
  onSetCrashReportAvailable(message) {
    let data = message.data;

    if (data.hasReport) {
      this.hasReport = true;
      document.documentElement.classList.add("crashDumpAvailable");

      document.getElementById("sendReport").checked = data.sendReport;
      document.getElementById("includeURL").checked = data.includeURL;

      if (data.requestEmail) {
        document.getElementById("requestEmail").hidden = false;
        document.getElementById("emailMe").checked = data.emailMe;
        if (data.emailMe) {
          document.getElementById("email").value = data.email;
        }
      }

      this.showCrashReportUI(data.sendReport);
    } else {
      this.showCrashReportUI(false);
    }

    if (data.requestAutoSubmit) {
      document.getElementById("requestAutoSubmit").hidden = false;
    }

    let event = new CustomEvent("AboutTabCrashedReady", {bubbles: true});
    document.dispatchEvent(event);
  },

  /**
   * Handler for when the parent reports that the crash report associated
   * with this about:tabcrashed page has been sent.
   */
  onCrashReportSent() {
    document.documentElement.classList.remove("crashDumpAvailable");
    document.documentElement.classList.add("crashDumpSubmitted");
  },

  /**
   * Toggles the display of the crash report form.
   *
   * @param shouldShow (bool)
   *        True if the crash report form should be shown
   */
  showCrashReportUI(shouldShow) {
    let options = document.getElementById("options");
    options.hidden = !shouldShow;
  },

  /**
   * Toggles whether or not the page is one of several visible pages
   * showing the crash reporter. This controls some of the language
   * on the page, along with what the "primary" button is.
   *
   * @param hasMultiple (bool)
   *        True if there are multiple crash report pages being shown.
   */
  setMultiple(hasMultiple) {
    let main = document.getElementById("main");
    main.setAttribute("multiple", hasMultiple);

    let restoreTab = document.getElementById("restoreTab");

    // The "Restore All" button has the "primary" class by default, so
    // we only need to modify the "Restore Tab" button.
    if (hasMultiple) {
      restoreTab.classList.remove("primary");
    } else {
      restoreTab.classList.add("primary");
    }
  },

  /**
   * Sends a message to the parent in response to the user choosing
   * one of the actions available on the page. This might also send up
   * crash report information if the user has chosen to submit a crash
   * report.
   *
   * @param messageName (String)
   *        The message to send to the parent
   */
  sendMessage(messageName) {
    let comments = "";
    let email = "";
    let URL = "";
    let sendReport = false;
    let emailMe = false;
    let includeURL = false;
    let autoSubmit = false;

    if (this.hasReport) {
      sendReport = document.getElementById("sendReport").checked;
      if (sendReport) {
        comments = document.getElementById("comments").value.trim();

        includeURL = document.getElementById("includeURL").checked;
        if (includeURL) {
          URL = this.pageData.URL.trim();
        }

        if (!document.getElementById("requestEmail").hidden) {
          emailMe = document.getElementById("emailMe").checked;
          if (emailMe) {
            email = document.getElementById("email").value.trim();
          }
        }
      }
    }

    let requestAutoSubmit = document.getElementById("requestAutoSubmit");
    if (requestAutoSubmit.hidden) {
      // The checkbox is hidden if the user has already opted in to sending
      // backlogged crash reports.
      autoSubmit = true;
    } else {
      autoSubmit = document.getElementById("autoSubmit").checked;
    }

    sendAsyncMessage(messageName, {
      sendReport,
      comments,
      email,
      emailMe,
      includeURL,
      URL,
      autoSubmit,
      hasReport: this.hasReport,
    });
  },
};

AboutTabCrashed.init();
PK
!<Y�j�
�
4chrome/browser/content/browser/aboutTabCrashed.xhtml<?xml version="1.0" encoding="UTF-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/. -->

<!DOCTYPE html [
  <!ENTITY % htmlDTD
    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "DTD/xhtml1-strict.dtd">
  %htmlDTD;
  <!ENTITY % globalDTD
    SYSTEM "chrome://global/locale/global.dtd">
  %globalDTD;
  <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
  %brandDTD;
  <!ENTITY % tabCrashedDTD
    SYSTEM "chrome://browser/locale/aboutTabCrashed.dtd">
  %tabCrashedDTD;
]>

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Security-Policy" content="default-src chrome:" />
    <link rel="stylesheet" type="text/css" media="all"
          href="chrome://global/skin/in-content/info-pages.css"/>
    <link rel="stylesheet" type="text/css" media="all"
          href="chrome://browser/content/aboutTabCrashed.css"/>
    <link rel="stylesheet" type="text/css" media="all"
          href="chrome://browser/skin/aboutTabCrashed.css"/>

    <title>&tabCrashed.title;</title>
  </head>

  <body dir="&locale.dir;">
    <div id="main" class="container" multiple="false">

      <div class="title">
        <h1 class="title-text">&tabCrashed.header2;</h1>
      </div>

      <div class="offers">
        <h2>&tabCrashed.offerHelp2;</h2>
        <p id="offerHelpMessageSingle">&tabCrashed.single.offerHelpMessage2;</p>
        <p id="offerHelpMessageMultiple">&tabCrashed.multiple.offerHelpMessage2;</p>
      </div>

      <div id="reportBox">
        <h2>&tabCrashed.requestHelp;</h2>
        <p>&tabCrashed.requestHelpMessage;</p>

        <h2>&tabCrashed.requestReport;</h2>

        <div class="checkbox-with-label">
          <input type="checkbox" id="sendReport" role="checkbox"/>
          <label for="sendReport">&tabCrashed.sendReport3;</label>
        </div>

        <ul id="options">
          <li>
            <textarea id="comments" placeholder="&tabCrashed.commentPlaceholder2;" rows="4"></textarea>
          </li>

          <li class="checkbox-with-label">
            <input type="checkbox" id="includeURL" role="checkbox"/>
            <label for="includeURL">&tabCrashed.includeURL3;</label>
          </li>

          <li id="requestEmail" hidden="true">
            <div class="checkbox-with-label">
              <input type="checkbox" id="emailMe" role="checkbox"/>
              <label for="emailMe">&tabCrashed.emailMe;</label>
            </div>
            <input type="text" id="email" placeholder="&tabCrashed.emailPlaceholder;"/>
          </li>
        </ul>

        <div id="requestAutoSubmit" hidden="true">
          <h2>&tabCrashed.requestAutoSubmit2;</h2>
          <div class="checkbox-with-label">
            <input type="checkbox" id="autoSubmit" role="checkbox"/>
            <label for="autoSubmit">&tabCrashed.autoSubmit3;</label>
          </div>
        </div>
      </div>

      <p id="reportSent">&tabCrashed.reportSent;</p>

      <div class="button-container">
        <button id="closeTab">
          &tabCrashed.closeTab2;</button>
        <button id="restoreTab" class="primary">
          &tabCrashed.restoreTab;</button>
        <button id="restoreAll" autofocus="true" class="primary">
          &tabCrashed.restoreAll;</button>
      </div>
    </div>
  </body>
  <script type="text/javascript" src="chrome://browser/content/aboutTabCrashed.js"/>
</html>
PK
!<\�b]

5chrome/browser/content/browser/aboutWelcomeBack.xhtml<?xml version="1.0" encoding="UTF-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/.
-->
<!DOCTYPE html [
  <!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
  %htmlDTD;
  <!ENTITY % netErrorDTD SYSTEM "chrome://global/locale/netError.dtd">
  %netErrorDTD;
  <!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
  %globalDTD;
  <!ENTITY % restorepageDTD SYSTEM "chrome://browser/locale/aboutSessionRestore.dtd">
  %restorepageDTD;
]>

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
  <head>
    <title>&welcomeback2.tabtitle;</title>
    <link rel="stylesheet" href="chrome://global/skin/in-content/info-pages.css" type="text/css" media="all"/>
    <link rel="stylesheet" href="chrome://browser/skin/aboutWelcomeBack.css" type="text/css" media="all"/>
    <link rel="icon" type="image/png" href="chrome://browser/skin/info.svg"/>

    <script type="application/javascript" src="chrome://browser/content/aboutSessionRestore.js"/>
  </head>

  <body dir="&locale.dir;">

    <div class="container">

      <div class="title">
        <h1 class="title-text">&welcomeback2.pageTitle;</h1>
      </div>

      <div class="description">

        <p>&welcomeback2.pageInfo1;</p>
        <!-- Note a href in the anchor below is added by JS -->
        <p>&welcomeback2.beforelink.pageInfo2;<a id="linkMoreTroubleshooting" target="_blank">&welcomeback2.link.pageInfo2;</a>&welcomeback2.afterlink.pageInfo2;</p>

        <div>
          <div class="radioRestoreContainer">
            <input class="radioRestoreButton" id="radioRestoreAll" type="radio"
                   name="restore" checked="checked"/>
            <label class="radioRestoreLabel" for="radioRestoreAll">&welcomeback2.restoreAll.label;</label>
          </div>

          <div class="radioRestoreContainer">
            <input class="radioRestoreButton" id="radioRestoreChoose" type="radio"
                   name="restore"/>
            <label class="radioRestoreLabel" for="radioRestoreChoose">&welcomeback2.restoreSome.label;</label>
          </div>
        </div>
      </div>

      <div class="tree-container">
        <xul:tree id="tabList" flex="1" seltype="single" hidecolumnpicker="true"
                  onclick="onListClick(event);" onkeydown="onListKeyDown(event);"
                  _window_label="&restorepage.windowLabel;">
          <xul:treecols>
            <xul:treecol cycler="true" id="restore" type="checkbox" label="&restorepage.restoreHeader;"/>
            <xul:splitter class="tree-splitter"/>
            <xul:treecol primary="true" id="title" label="&restorepage.listHeader;" flex="1"/>
          </xul:treecols>
          <xul:treechildren flex="1"/>
        </xul:tree>
      </div>

      <div class="button-container">
        <xul:button class="primary"
                    id="errorTryAgain"
                    label="&welcomeback2.restoreButton;"
                    accesskey="&welcomeback2.restoreButton.access;"
                    oncommand="restoreSession();"/>
      </div>

      <input type="text" id="sessionData" style="display: none;"/>

    </div>
  </body>
</html>
PK
!<�N���&�&6chrome/browser/content/browser/abouthome/aboutHome.css
html {
  font: message-box;
  font-size: 100%;
  background-color: #F9F9FA;
  color: #0C0C0D;
  height: 100%;
}

body {
  margin: 0;
  display: -moz-box;
  -moz-box-orient: vertical;
  width: 100%;
  height: 100%;
}

input,
button {
  font-size: inherit;
  font-family: inherit;
}

a {
  color: -moz-nativehyperlinktext;
  text-decoration: none;
}

.spacer {
  -moz-box-flex: 1;
}

#topSection {
  text-align: center;
  margin-top: -90px;
  position: relative;
}

#brandLogo {
  height: 192px;
  width: 192px;
  margin: 0 auto 20px;
  background-image: url("chrome://branding/content/about-logo.png");
  background-size: 192px auto;
  background-position: center center;
  background-repeat: no-repeat;
}

#searchWrapper,
#snippets {
  width: 470px;
}

#searchWrapper {
  cursor: default;
  display: flex;
  position: relative;
  margin: 0 auto;
  height: 36px;
}

#searchText {
  border: 1px solid rgba(0, 0, 0, 0.15);
  border-radius: 3px;
  box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1);
  color: inherit;
  padding: 0;
  padding-inline-end: 36px;
  padding-inline-start: 35px;
  width: 100%;
  font-size: 15px;
}

#searchText:active,
#searchText:focus {
  border-color: #0A84FF;
  box-shadow: 0 0 0 2px #0A84FF;
}

#searchIcon {
  background: url("chrome://browser/skin/search-glass.svg") no-repeat 12px center/16px;
  fill: rgba(12, 12, 13, 0.4);
  -moz-context-properties: fill;
  position: absolute;
  offset-inline-start: 0;
  height: 35px;
  width: 35px;
}

#searchSubmit {
  background: url("chrome://browser/skin/forward.svg") no-repeat center center;
  border-radius: 0 3px 3px 0;
  border: 0;
  width: 36px;
  fill: rgba(12, 12, 13, 0.4);
  -moz-context-properties: fill;
  background-size: 16px 16px;
  height: 100%;
  offset-inline-end: 0;
  position: absolute;
  color: transparent;
}

#searchSubmit:focus, #searchSubmit:hover {
  background-color: rgba(12, 12, 13, 0.1);
  cursor: pointer;
}

#searchSubmit:active {
  background-color: rgba(12, 12, 13, 0.15);
}

#searchSubmit:dir(rtl) {
  transform: scaleX(-1);
}

.contentSearchSuggestionTable {
  border: 0;
  transform: translateY(2px);
}

#defaultSnippet1,
#defaultSnippet2,
#rightsSnippet {
  display: block;
  min-height: 38px;
  background: 0 center no-repeat;
  padding: 6px 0;
  padding-inline-start: 49px;
}

#rightsSnippet[hidden] {
  display: none;
}

#defaultSnippet1:dir(rtl),
#defaultSnippet2:dir(rtl),
#rightsSnippet:dir(rtl) {
  background-position: right 0 center;
}

#defaultSnippet1 {
  background-image: url("chrome://browser/content/abouthome/snippet1.png");
}

#defaultSnippet2 {
  background-image: url("chrome://browser/content/abouthome/snippet2.png");
}

#snippets {
  display: inline-block;
  text-align: start;
  margin: 12px 0;
  color: #3c3c3c;
  font-size: 75%;
  /* 12px is the computed font size, 15px the computed line height of the snippets
     with Segoe UI on a default Windows 7 setup. The 15/12 multiplier approximately
     converts em from units of font-size to units of line-height. The goal is to
     preset the height of a three-line snippet to avoid visual moving/flickering as
     the snippets load. */
  min-height: calc(15/12 * 3em);
}

#launcher {
  display: none;
  -moz-box-align: center;
  -moz-box-pack: center;
  width: 100%;
  background-color: hsla(0,0%,0%,.03);
  border-top: 1px solid hsla(0,0%,0%,.03);
  box-shadow: 0 1px 2px hsla(0,0%,0%,.02) inset,
              0 -1px 0 hsla(0,0%,100%,.25);
}

#launcher:not([session]),
body[narrow] #launcher[session] {
  display: none; /* display separator and restore button on separate lines */
  text-align: center;
  white-space: nowrap; /* prevent navigational buttons from wrapping */
}

.launchButton {
  display: -moz-box;
  -moz-box-orient: vertical;
  margin: 16px 1px;
  padding: 14px 6px;
  min-width: 88px;
  max-width: 176px;
  max-height: 85px;
  vertical-align: top;
  white-space: normal;
  background: transparent padding-box;
  border: 1px solid transparent;
  border-radius: 2px;
  color: #525c66;
  font-size: 75%;
  cursor: pointer;
  transition-property: background-color, border-color, box-shadow;
  transition-duration: 150ms;
}

body[narrow] #launcher[session] > .launchButton {
  margin: 4px 1px;
}

.launchButton:hover {
  background-color: hsla(211,79%,6%,.03);
  border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2);
}

.launchButton:hover:active {
  background-image: linear-gradient(hsla(211,79%,6%,.02), hsla(211,79%,6%,.05));
  border-color: hsla(210,54%,20%,.2) hsla(210,54%,20%,.23) hsla(210,54%,20%,.25);
  box-shadow: 0 1px 1px hsla(211,79%,6%,.05) inset,
              0 0 1px hsla(211,79%,6%,.1) inset;
  transition-duration: 0ms;
}

.launchButton[hidden],
#launcher:not([session]) > #restorePreviousSessionSeparator,
#launcher:not([session]) > #restorePreviousSession {
  display: none;
}

#restorePreviousSessionSeparator {
  width: 3px;
  height: 116px;
  margin: 0 10px;
  background-image: linear-gradient(hsla(0,0%,100%,0), hsla(0,0%,100%,.35), hsla(0,0%,100%,0)),
                    linear-gradient(hsla(211,79%,6%,0), hsla(211,79%,6%,.2), hsla(211,79%,6%,0)),
                    linear-gradient(hsla(0,0%,100%,0), hsla(0,0%,100%,.35), hsla(0,0%,100%,0));
  background-position: left top, center, right bottom;
  background-size: 1px auto;
  background-repeat: no-repeat;
}

body[narrow] #restorePreviousSessionSeparator {
  margin: 0 auto;
  width: 512px;
  height: 3px;
  background-image: linear-gradient(to right, hsla(0,0%,100%,0), hsla(0,0%,100%,.35), hsla(0,0%,100%,0)),
                    linear-gradient(to right, hsla(211,79%,6%,0), hsla(211,79%,6%,.2), hsla(211,79%,6%,0)),
                    linear-gradient(to right, hsla(0,0%,100%,0), hsla(0,0%,100%,.35), hsla(0,0%,100%,0));
  background-size: auto 1px;
}

#restorePreviousSession {
  max-width: none;
  font-size: 90%;
}

body[narrow] #restorePreviousSession {
  font-size: 80%;
}

.launchButton::before {
  display: block;
  width: 32px;
  height: 32px;
  margin: 0 auto 6px;
  line-height: 0; /* remove extra vertical space due to non-zero font-size */
}

#downloads::before {
  content: url("chrome://browser/content/abouthome/downloads.png");
}

#bookmarks::before {
  content: url("chrome://browser/content/abouthome/bookmarks.png");
}

#history::before {
  content: url("chrome://browser/content/abouthome/history.png");
}

#addons::before {
  content: url("chrome://browser/content/abouthome/addons.png");
}

#sync::before {
  content: url("chrome://browser/content/abouthome/sync.png");
}

#settings::before {
  content: url("chrome://browser/content/abouthome/settings.png");
}

#restorePreviousSession::before {
  content: url("chrome://browser/content/abouthome/restore-large.png");
  height: 48px;
  width: 48px;
  display: inline-block; /* display on same line as text label */
  vertical-align: middle;
  margin-bottom: 0;
  margin-inline-end: 8px;
}

#restorePreviousSession:dir(rtl)::before {
  transform: scaleX(-1);
}

body[narrow] #restorePreviousSession::before {
  content: url("chrome://browser/content/abouthome/restore.png");
  height: 32px;
  width: 32px;
}

/* [HiDPI]
 * At resolutions above 1dppx, prefer downscaling the 2x Retina graphics
 * rather than upscaling the original-size ones (bug 818940).
 */
@media not all and (max-resolution: 1dppx) {
  #brandLogo {
    background-image: url("chrome://branding/content/[email protected]");
  }

  #defaultSnippet1,
  #defaultSnippet2,
  #rightsSnippet {
    background-size: 40px;
  }

  #defaultSnippet1 {
    background-image: url("chrome://browser/content/abouthome/[email protected]");
  }

  #defaultSnippet2 {
    background-image: url("chrome://browser/content/abouthome/[email protected]");
  }

  .launchButton::before {
    transform: scale(.5);
    transform-origin: 0 0;
  }

  .launchButton:dir(rtl)::before {
    transform: scale(.5) translateX(32px);
  }

  #downloads::before {
    content: url("chrome://browser/content/abouthome/[email protected]");
  }

  #bookmarks::before {
    content: url("chrome://browser/content/abouthome/[email protected]");
  }

  #history::before {
    content: url("chrome://browser/content/abouthome/[email protected]");
  }

  #addons::before {
    content: url("chrome://browser/content/abouthome/[email protected]");
  }

  #sync::before {
    content: url("chrome://browser/content/abouthome/[email protected]");
  }

  #settings::before {
    content: url("chrome://browser/content/abouthome/[email protected]");
  }

  #restorePreviousSession::before {
    content: url("chrome://browser/content/abouthome/[email protected]");
  }

  body[narrow] #restorePreviousSession::before {
    content: url("chrome://browser/content/abouthome/[email protected]");
  }

  #restorePreviousSession:dir(rtl)::before {
    transform: scale(-0.5, 0.5) translateX(24px);
    transform-origin: top center;
  }
}

/* Overrides for onboarding
   These overrides hide the Firefox logo (since about:home has a
   large logo already) and make the helper icon larger */

#about-home #onboarding-notification-body,
#about-home #onboarding-notification-tour-title {
  color: #0C0C0D;
}

#about-home #onboarding-notification-body {
  padding-inline-start: 46px;
}

#about-home #onboarding-notification-tour-message {
  margin-bottom: 0;
}

#about-home #onboarding-notification-tour-icon {
  display: none;
}

#about-home #onboarding-notification-tour-title::before {
  display: block;
  position: absolute;
  top: 0;
  offset-inline-start: 0;
  height: 100%;
  width: 42px;
  background-size: 42px;
  background-position: center;
  -moz-context-properties: fill;
  fill: #4A4A4F;
}

#about-home #onboarding-notification-bar {
  z-index: 100;
}

/* Overrides for snippets
   These changes make it so that snippets do not affect the height of the
   main element, so that elements don't move around on page load. */
#snippetContainer {
  position: absolute;
  left: 50%;
  top: 100%;
  margin-left: -235px;
  width: 470px;
}
PK
!<X�í2�25chrome/browser/content/browser/abouthome/aboutHome.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

/* import-globals-from ../contentSearchUI.js */

// The process of adding a new default snippet involves:
//   * add a new entity to aboutHome.dtd
//   * add a <span/> for it in aboutHome.xhtml
//   * add an entry here in the proper ordering (based on spans)
// The <a/> part of the snippet will be linked to the corresponding url.
const DEFAULT_SNIPPETS_URLS = [
  "https://www.mozilla.org/firefox/features/?utm_source=snippet&utm_medium=snippet&utm_campaign=default+feature+snippet",
  "https://addons.mozilla.org/firefox/?utm_source=snippet&utm_medium=snippet&utm_campaign=addons"
];

const SNIPPETS_UPDATE_INTERVAL_MS = 14400000; // 4 hours.

// IndexedDB storage constants.
const DATABASE_NAME = "abouthome";
const DATABASE_VERSION = 1;
const DATABASE_STORAGE = "persistent";
const SNIPPETS_OBJECTSTORE_NAME = "snippets";
var searchText;

// This global tracks if the page has been set up before, to prevent double inits
var gInitialized = false;
var gObserver = new MutationObserver(function(mutations) {
  for (let mutation of mutations) {
    // The addition of the restore session button changes our width:
    if (mutation.attributeName == "session") {
      fitToWidth();
    }
    if (mutation.attributeName == "snippetsVersion") {
      if (!gInitialized) {
        ensureSnippetsMapThen(loadSnippets);
        gInitialized = true;
      }
      return;
    }
  }
});

window.addEventListener("pageshow", function() {
  // Delay search engine setup, cause browser.js::BrowserOnAboutPageLoad runs
  // later and may use asynchronous getters.
  window.gObserver.observe(document.documentElement, { attributes: true });
  fitToWidth();
  setupSearch();
  window.addEventListener("resize", fitToWidth);

  // Ask chrome to update snippets.
  var event = new CustomEvent("AboutHomeLoad", {bubbles: true});
  document.dispatchEvent(event);
});

window.addEventListener("pagehide", function() {
  window.gObserver.disconnect();
  window.removeEventListener("resize", fitToWidth);
});

window.addEventListener("keypress", ev => {
  if (ev.defaultPrevented) {
    return;
  }

  // don't focus the search-box on keypress if something other than the
  // body or document element has focus - don't want to steal input from other elements
  // Make an exception for <a> and <button> elements (and input[type=button|submit])
  // which don't usefully take keypresses anyway.
  // (except space, which is handled below)
  if (document.activeElement && document.activeElement != document.body &&
      document.activeElement != document.documentElement &&
      !["a", "button"].includes(document.activeElement.localName) &&
      !document.activeElement.matches("input:-moz-any([type=button],[type=submit])")) {
    return;
  }

  let modifiers = ev.ctrlKey + ev.altKey + ev.metaKey;
  // ignore Ctrl/Cmd/Alt, but not Shift
  // also ignore Tab, Insert, PageUp, etc., and Space
  if (modifiers != 0 || ev.charCode == 0 || ev.charCode == 32)
    return;

  searchText.focus();
  // need to send the first keypress outside the search-box manually to it
  searchText.value += ev.key;
});

// This object has the same interface as Map and is used to store and retrieve
// the snippets data.  It is lazily initialized by ensureSnippetsMapThen(), so
// be sure its callback returned before trying to use it.
var gSnippetsMap;
var gSnippetsMapCallbacks = [];

/**
 * Ensure the snippets map is properly initialized.
 *
 * @param aCallback
 *        Invoked once the map has been initialized, gets the map as argument.
 * @note Snippets should never directly manage the underlying storage, since
 *       it may change inadvertently.
 */
function ensureSnippetsMapThen(aCallback) {
  if (gSnippetsMap) {
    aCallback(gSnippetsMap);
    return;
  }

  // Handle multiple requests during the async initialization.
  gSnippetsMapCallbacks.push(aCallback);
  if (gSnippetsMapCallbacks.length > 1) {
    // We are already updating, the callbacks will be invoked when done.
    return;
  }

  let invokeCallbacks = function() {
    if (!gSnippetsMap) {
      gSnippetsMap = Object.freeze(new Map());
    }

    for (let callback of gSnippetsMapCallbacks) {
      callback(gSnippetsMap);
    }
    gSnippetsMapCallbacks.length = 0;
  };

  let openRequest = indexedDB.open(DATABASE_NAME, {version: DATABASE_VERSION,
                                                   storage: DATABASE_STORAGE});

  openRequest.onerror = function(event) {
    // Try to delete the old database so that we can start this process over
    // next time.
    indexedDB.deleteDatabase(DATABASE_NAME);
    invokeCallbacks();
  };

  openRequest.onupgradeneeded = function(event) {
    let db = event.target.result;
    if (!db.objectStoreNames.contains(SNIPPETS_OBJECTSTORE_NAME)) {
      db.createObjectStore(SNIPPETS_OBJECTSTORE_NAME);
    }
  };

  openRequest.onsuccess = function(event) {
    let db = event.target.result;

    db.onerror = function() {
      invokeCallbacks();
    };

    db.onversionchange = function(versionChangeEvent) {
      versionChangeEvent.target.close();
      invokeCallbacks();
    };

    let cache = new Map();
    let cursorRequest;
    try {
      cursorRequest = db.transaction(SNIPPETS_OBJECTSTORE_NAME)
                        .objectStore(SNIPPETS_OBJECTSTORE_NAME).openCursor();
    } catch (ex) {
      console.error(ex);
      invokeCallbacks();
      return;
    }

    cursorRequest.onerror = function() {
      invokeCallbacks();
    };

    cursorRequest.onsuccess = function(cursorRequestEvent) {
      let cursor = cursorRequestEvent.target.result;

      // Populate the cache from the persistent storage.
      if (cursor) {
        cache.set(cursor.key, cursor.value);
        cursor.continue();
        return;
      }

      // The cache has been filled up, create the snippets map.
      gSnippetsMap = Object.freeze({
        get: (aKey) => cache.get(aKey),
        set(aKey, aValue) {
          db.transaction(SNIPPETS_OBJECTSTORE_NAME, "readwrite")
            .objectStore(SNIPPETS_OBJECTSTORE_NAME).put(aValue, aKey);
          return cache.set(aKey, aValue);
        },
        has: (aKey) => cache.has(aKey),
        delete(aKey) {
          db.transaction(SNIPPETS_OBJECTSTORE_NAME, "readwrite")
            .objectStore(SNIPPETS_OBJECTSTORE_NAME).delete(aKey);
          return cache.delete(aKey);
        },
        clear() {
          db.transaction(SNIPPETS_OBJECTSTORE_NAME, "readwrite")
            .objectStore(SNIPPETS_OBJECTSTORE_NAME).clear();
          return cache.clear();
        },
        get size() { return cache.size; },
      });

      setTimeout(invokeCallbacks, 0);
    };
  };
}

function onSearchSubmit(aEvent) {
  gContentSearchController.search(aEvent);
}


var gContentSearchController;

function setupSearch() {
  // Set submit button label for when CSS background are disabled (e.g.
  // high contrast mode).
  document.getElementById("searchSubmit").value =
    document.body.getAttribute("dir") == "ltr" ? "\u25B6" : "\u25C0";

  // The "autofocus" attribute doesn't focus the form element
  // immediately when the element is first drawn, so the
  // attribute is also used for styling when the page first loads.
  searchText = document.getElementById("searchText");
  searchText.addEventListener("blur", function() {
    searchText.removeAttribute("autofocus");
  }, {once: true});

  if (!gContentSearchController) {
    gContentSearchController =
      new ContentSearchUIController(searchText, searchText.parentNode,
                                    "abouthome", "homepage");
  }
}

/**
 * Inform the test harness that we're done loading the page.
 */
function loadCompleted() {
  var event = new CustomEvent("AboutHomeLoadSnippetsCompleted", {bubbles: true});
  document.dispatchEvent(event);
}

/**
 * Update the local snippets from the remote storage, then show them through
 * showSnippets.
 */
function loadSnippets() {
  if (!gSnippetsMap)
    throw new Error("Snippets map has not properly been initialized");

  // Allow tests to modify the snippets map before using it.
  var event = new CustomEvent("AboutHomeLoadSnippets", {bubbles: true});
  document.dispatchEvent(event);

  // Check cached snippets version.
  let cachedVersion = gSnippetsMap.get("snippets-cached-version") || 0;
  let currentVersion = document.documentElement.getAttribute("snippetsVersion");
  if (cachedVersion < currentVersion) {
    // The cached snippets are old and unsupported, restart from scratch.
    gSnippetsMap.clear();
  }

  // Check last snippets update.
  let lastUpdate = gSnippetsMap.get("snippets-last-update");
  let updateURL = document.documentElement.getAttribute("snippetsURL");
  let shouldUpdate = !lastUpdate ||
                     Date.now() - lastUpdate > SNIPPETS_UPDATE_INTERVAL_MS;
  if (updateURL && shouldUpdate) {
    // Try to update from network.
    let xhr = new XMLHttpRequest();
    xhr.timeout = 5000;
    // Even if fetching should fail we don't want to spam the server, thus
    // set the last update time regardless its results.  Will retry tomorrow.
    gSnippetsMap.set("snippets-last-update", Date.now());
    xhr.onloadend = function() {
      if (xhr.status == 200) {
        gSnippetsMap.set("snippets", xhr.responseText);
        gSnippetsMap.set("snippets-cached-version", currentVersion);
      }
      showSnippets();
      loadCompleted();
    };
    try {
      xhr.open("GET", updateURL, true);
      xhr.send(null);
    } catch (ex) {
      showSnippets();
      loadCompleted();
    }
  } else {
    showSnippets();
    loadCompleted();
  }
}

/**
 * Shows locally cached remote snippets, or default ones when not available.
 *
 * @note: snippets should never invoke showSnippets(), or they may cause
 *        a "too much recursion" exception.
 */
var _snippetsShown = false;
function showSnippets() {
  let snippetsElt = document.getElementById("snippets");

  // Show about:rights notification, if needed.
  let showRights = document.documentElement.getAttribute("showKnowYourRights");
  if (showRights) {
    let rightsElt = document.getElementById("rightsSnippet");
    let anchor = rightsElt.getElementsByTagName("a")[0];
    anchor.href = "about:rights";
    snippetsElt.appendChild(rightsElt);
    rightsElt.removeAttribute("hidden");
    return;
  }

  if (!gSnippetsMap)
    throw new Error("Snippets map has not properly been initialized");
  if (_snippetsShown) {
    // There's something wrong with the remote snippets, just in case fall back
    // to the default snippets.
    showDefaultSnippets();
    throw new Error("showSnippets should never be invoked multiple times");
  }
  _snippetsShown = true;

  let snippets = gSnippetsMap.get("snippets");
  // If there are remotely fetched snippets, try to to show them.
  if (snippets) {
    // Injecting snippets can throw if they're invalid XML.
    try {
      // eslint-disable-next-line no-unsanitized/property
      snippetsElt.innerHTML = snippets;
      // Scripts injected by innerHTML are inactive, so we have to relocate them
      // through DOM manipulation to activate their contents.
      Array.forEach(snippetsElt.getElementsByTagName("script"), function(elt) {
        let relocatedScript = document.createElement("script");
        relocatedScript.type = "text/javascript";
        relocatedScript.text = elt.text;
        elt.parentNode.replaceChild(relocatedScript, elt);
      });
      return;
    } catch (ex) {
      // Bad content, continue to show default snippets.
    }
  }

  showDefaultSnippets();
}

/**
 * Clear snippets element contents and show default snippets.
 */
function showDefaultSnippets() {
  // Clear eventual contents...
  let snippetsElt = document.getElementById("snippets");
  snippetsElt.innerHTML = "";

  // ...then show default snippets.
  let defaultSnippetsElt = document.getElementById("defaultSnippets");
  let entries = defaultSnippetsElt.querySelectorAll("span");
  // Choose a random snippet.  Assume there is always at least one.
  let randIndex = Math.floor(Math.random() * entries.length);
  let entry = entries[randIndex];
  // Inject url in the eventual link.
  if (DEFAULT_SNIPPETS_URLS[randIndex]) {
    let links = entry.getElementsByTagName("a");
    // Default snippets can have only one link, otherwise something is messed
    // up in the translation.
    if (links.length == 1) {
      links[0].href = DEFAULT_SNIPPETS_URLS[randIndex];
    }
  }
  // Move the default snippet to the snippets element.
  snippetsElt.appendChild(entry);
}

function fitToWidth() {
  if (document.documentElement.scrollWidth > window.innerWidth) {
    document.body.setAttribute("narrow", "true");
  } else if (document.body.hasAttribute("narrow")) {
    document.body.removeAttribute("narrow");
    fitToWidth();
  }
}
PK
!<�
_8chrome/browser/content/browser/abouthome/aboutHome.xhtml<?xml version="1.0" encoding="UTF-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/. -->

<!DOCTYPE html [
  <!ENTITY % htmlDTD
    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "DTD/xhtml1-strict.dtd">
  %htmlDTD;
  <!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
  %globalDTD;
  <!ENTITY % aboutHomeDTD SYSTEM "chrome://browser/locale/aboutHome.dtd">
  %aboutHomeDTD;
  <!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd" >
  %browserDTD;
]>

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Security-Policy" content="default-src chrome:" />
    <title>&abouthome.pageTitle;</title>

    <link rel="icon" type="image/png" id="favicon"
          href="chrome://branding/content/icon32.png"/>
    <link rel="stylesheet" type="text/css" media="all"
          href="chrome://browser/content/contentSearchUI.css"/>
    <link rel="stylesheet" type="text/css" media="all" defer="defer"
          href="chrome://browser/content/abouthome/aboutHome.css"/>

    <script type="text/javascript"
            src="chrome://browser/content/abouthome/aboutHome.js"/>
    <script type="text/javascript"
            src="chrome://browser/content/contentSearchUI.js"/>
  </head>

  <body id="about-home" dir="&locale.dir;">
    <div class="spacer"/>
    <div id="topSection">
      <div id="brandLogo"></div>

      <div id="searchWrapper">
        <div id="searchIcon"/>
        <input type="text" name="q" value="" id="searchText" maxlength="256"
               placeholder="&searchInput.placeholder;"
               aria-label="&contentSearchInput.label;" autofocus="autofocus"/>
        <input id="searchSubmit" type="button" onclick="onSearchSubmit(event)"
               title="&contentSearchSubmit.tooltip;"/>
      </div>

      <div id="snippetContainer">
        <div id="defaultSnippets" hidden="true">
          <span id="defaultSnippet1">&abouthome.defaultSnippet1.v1;</span>
          <span id="defaultSnippet2">&abouthome.defaultSnippet2.v1;</span>
        </div>
        <span id="rightsSnippet" hidden="true">&abouthome.rightsSnippet;</span>
        <div id="snippets"/>
      </div>
    </div>
    <div class="spacer"/>

    <div id="launcher">
      <button class="launchButton" id="downloads">&abouthome.downloadsButton.label;</button>
      <button class="launchButton" id="bookmarks">&abouthome.bookmarksButton.label;</button>
      <button class="launchButton" id="history">&abouthome.historyButton.label;</button>
      <button class="launchButton" id="addons">&abouthome.addonsButton.label;</button>
      <button class="launchButton" id="sync">&abouthome.syncButton.label;</button>
      <button class="launchButton" id="settings">&abouthome.preferencesButtonUnix.label;</button>
      <div id="restorePreviousSessionSeparator"/>
      <button class="launchButton" id="restorePreviousSession">&historyRestoreLastSession.label;</button>
    </div>
  </body>
</html>
PK
!<��;YY3chrome/browser/content/browser/abouthome/addons.png�PNG


IHDR  szz� IDATx՗�,Y���/�ڶ�kۚ��f�m�m�6��momU��\&�$��|�Ө���k�{�]�wa��M}ӏv���|��'.l�h�$��k�}�����;�B�9\�1z�60ʗ߯ä��5)�Bc�-�<)��H�ǝ�DK76 &_46h�Ȕ�5�1Y�p}C@0w-<��x������?��<ʤ�l,U�ڛ���>�ň�L��0��,ܡt�VKpnS�Q�5��|^s���6����˺��
�W4kZ����
��Q�9W��|�!�� -�t�S"��apV��B�_b�<���|��u,4�c�
+P���30#�alϡY)t��_�,�/3>�qF�̲�5�R,4vF��K_�ƻB�kYh��\�X��1����u�(��'8XT�>v��t���:��q �8f�5f��*��]?wZ�$ ��z�`�$@c�9�\3���V��M�Hc�΁]�{A��%v́kHMޯ=�
k6›�y�H[GǹB��L�_�9ڂ	Zf^��`Z���%5t��FAG5�\�LL��jg�Fo�o&^|&W��Fܾ`Ew{I 	��G�ìX�B-��n�m���1Xq�[��!�Ï?���>j��>�����:�9�8�,=l�IV9Ab]�"�%A��c�st�i�]��7rҍ���z����Z�apuk����'�7g�jm�q�-H�9C�@U����x�q�΃X�|M�s.�@cd�k�ӟuY��N�!�=D7��#O����U��������N�I�ß(�	.h�M
`J���%�����]@Q'_�><��nR.P2W8�(���_��,L��'�N��+pLC�����3�$� 3zY1C���*k�l�b‰3q�,���hq��`ʵ�c� �#�	[S���ojZ+*��fL2mm��;L,�8/<npǾ���b֣��Ao�/q�ʹ>t�
P�2�����%~���Q�/�f]�z��2���i���ef���9#`t���^�p�Vb� P����,?X���t��A�����o��2)P�wG7���XTܟ�j�d@G(�:>'1�EmC������`�6�Q�x]����U�'���n�̀�聁i�m++���0��4FmO��������5������M�X��͗�_��2�,�\�=W�џ֜X�gj�7�����N(�~��w�?�xc/U��9O���������<�������cq����K��p'"�uL"��%�!��}�_�2dp��~�r.���q�)i��IEND�B`�PK
!<���)FF6chrome/browser/content/browser/abouthome/[email protected]�PNG


IHDR@@�iq�
IDATx�[lY�������1����hN�v�.�23333o�̜��#y���[GkKi����y�ݹg.�q��{?���wUH⯀��Co�&,��kÓ7��]�õr��R��C��
O�*8.["S�$.�L�+~�9	�ϖ�"R�al�<S>)" &s|V��Ui�Z�&S��m����W�$��d����0�_�f��(i���N1�LH��r�_-=k~�9^���I-����~Q�ɩ���l鑶ˤw��_tӌ�5��R�o�[��."��G=����f�X��IK��݂t���֎ɭ�H`lֿ�z�[��0}����Y!��q)i4MT�w��18QK߷,���UTҭ�tcט�-��ȯ�z>����]ە�G%g"��;a���E�uN��]"r��9v�-�L���u��{�lHP��nXG��1�'��|3�Eq�| �'@����дi��%��	b(A��1����N޷o���W,8����
w�?��ӊ��C�e����M��W@��P�$�Z�IK�Yv��y�,_sۏ�����)3��T	�CRRg�i�)ep��Br�J�,w��@R(�˚��b�_�"}�	���2����/\c��UHB�_d�G	��(q�Q-����� �=*56��X����#n�5.9�����%�T�r ��`��쟯�L46�
dI`LoT��҄׮�H��“��z��%�IR^�T�6!0�w3����:���Q�PY%��N�ӊ%4!��@F���dpm~�\��=�~���^_�u ���*a�Ζ��Y0׌�FI+�6<�5�1DrA�d�6Iq}+�]�>�\�g�qӡ�> �Z��$�\%��⒑RP+��s��]����Pf��6�u�JK˚�b@�6v��	h�Fn�ӷ
!�
����[p��;�ܿ�����	8D,'���3�e}<&(��[]c�m�98a�:�}
�ՙ�3�K���`9��._��u�*Tw4������XG��j�NYyӝ�z���j�+[͒��}N�
)�K��~�/�>����>��@��9G���ej���"�[�PX��h@�q-a����~x_���W"�#`���>�X�� �Y����}�T����ƎaWX>�Ё�bJ���>�	���}̵�4��f����&㷞�sE`�o�S�c�qBY�qM�1�H�Qؐ��Zﯨ�0��/n���E��J{�6,I��'�7�|7�~�Fg����!+;�><G0�iL�)�9�'�V��*��%���偱?�ryh�ֱo�H)`Å��Rr*$*�Hx���7�I������v�V��`��1+DVb�Φ�M
!�����8��<o��R>�>��~�teWXxeP���Фר ^�Hn�Z�4v�'���J��GjuN�'1�X=#�ˆY
��ض�hv��1��}���;�s�o�im}�>�;p�p�n�MpM%2`G�KHB��&�JLF��T�X#�:��*sx_�G�y�SW��?��o֮�D��	ʱ�&�jT�����o����M[�Vrc�ذis��[�^�8�.�����u�>��
�B~��1��1(�v]��<���g��E��̴�w��ϼhC���d�A4�?�V׾���l߱�\Q�r��Ƿ��0���b�0�	x\��S����8-���h�U�Ĥ��n{;�����,Ys����ŏ�-{`��w.��]J'�?�6�g�����z7*.��N��M/���ng��@�=��%��|ʹ'ۇ^�;�(4�x?�zn�n{���U��|���<)>L����dצQ_�kπ#Ρ�Mjt�|RMj,]�hb�O
��t1*C�	~W2�n.ǒ�*��[�P�2��h�T!�z
\OQ����Ne�aC*�1��1�1>��9�"��G���kx�	����M]��@%�(���w��ūo�qr�-7��
~ˌ��#

Ҵ�~n�L�ȓ*w��h�2(n'khn���8��݈�>"���&��F�$�&I��0C	>q[���SSS�J�fz�Z�x#
9�7�&h����%Z@
6����}��mF%.�i�~|$JƤ�JnM�4��*��A�)3�#��7K�[�J�O̫�n�=�D��D��I$��;�FͲ�-ǩ,=B�-Q%NuN>�g�����L��)��A�J�UXq2?k�v��$����qEP\'���1�O���*Q�_�C/��Ѭi�Td��;��\?�&	�v�.��Gz�?h�~��3����;^|��V^w��a�Ԃ�Ǿ;�F1c~��hBh����omh-�x[������,4=�~*�1A3&��l���>�`p~����E�t*�W��N�L����D�	"�ѼI��dE�76,LP�q6 ��|�h!4g��z%�1�5���~�=A^�q�0�p��Fp<��h�(��͑*@�x3W���<#-���@_$�F�X�5��=�Nc��	ޯ/��c
3A�b���c9��6wOu-X���<��W���޳�t��]eϼ�ur��kt����Ҍ�4iO�p�:�|���~�9>%�<��O����;����a��m���WY.+������������(�J
���:�1L]#�sn�toBGHE��������R�Y64;��2F=�H2_�:?�|���F��S��J���4����)�<�EY0�Ȃ�s=�C��;��N��/���:�v��� �	�24i�ȍ��Z���?!��U��'2x?��h_�I,kl]�����4M��G���ur+Ur}dʥG���½9�
����h����|�ɩ��5��(hV(1��� A��O]��c�MV@taDJ�T��<+9��h���>!�^g{�i��)�~�	8�q��r,vgQ7�i��G���{��Y�a`��)k������f�Fߤ�y�gd*
�N�|��/�VH�IƪQi[g!Ci����I���h-��Z�`��~M��t�5Ώ�*�����׏H�{��V���;�`�b���J=���:z��*SM���1hά�h�Y��h��3^?����*�UY_q�A`�.����X�^'��3\V�d�
;���Ae8����<4GZsG�!���[H�
f��s"xB3HRNy��	Y�"��O�A�OvF0Xrg}O�566+o��!&���_g�h�_���9��5��<,8N�8�{Lɫ��:Ͽ��)�|���uR��nQ@��.�����D��4ֽ�oݶ�o��6C�u�w�6Ts���9��5���ڔ1�l�a+^P����|Q
���Z����U*�4�U��6gG����^���dl_��t}����c`��U7�5���u5��'��?S��֭�X}�]v
-���9�F�����&��t�����N����z��z�c]���Q��4��(����:��A8&�{�
��?=d�Xd���q@��?��qp$�ds���d�N�M�����ͮ�ݏX�IEND�B`�PK
!<+�L��6chrome/browser/content/browser/abouthome/bookmarks.png�PNG


IHDR  szz��IDATxĕS�eW��Sl�/�Nڶm۸��6c۶m��x�Rk��U��5v�Z�jS �����H+�L/�ʎK)�}�y��>c��FD�f�	yUms]"=u��(��5����:Mί9�uP���[ �w�Zx�Ģm��/J�{��/�fn$ 6x����ñ+ҊV$\�}�)-�
&�"y�n&��M���uIy�'�˨(��t��rN߀�S�Ө�M��֘��Ԣe�@R^Չ��u�O=��7a��cI���m�6�p)�3P��j]d��k��X^U�����r.��H̭�����Yr�x	�c�5��J̩8��0ਝϗK5�R��n�.o~��c��&�&��~IÉ�n&�p�M�`�
h.�	c2J.�)����*o.�*�Gj�a����q��]�S}-o��1s`�"�Ѐ4	ڙeM��<g���~n�а�AR��T�	�:�0�d�/�u�j�Op�����E���'4�Mh�Fې��*[��m�{T�	>0�ٶ�_��m�PXƨkD�nϾ�Z�����'��X˨mԐܿ��{�{o£�z�e����F��&����� ��?�=���
������"��3K����Ͽ"A���|����لK`1"@�5�=�ijj4��=p��<f���v��Ű��X��t��O�褵ބ��|�.ޘ��8���\h�~��6":%6�$���,��A�c���|H�������Έ��&��t�4���~tr��{��[�˷�$178-}��0�
	��Iߎ�,E��
�ۿӈ]
��&��\���
$�V��6�"�o�LH ����@B��
T��v�t���� �Zŵ1�k��sx�_n"�k�4�	��}���Ɍ�
�cK~�'3RC�Hץ�y�E	�k7��-�V���n4���X#j�&�w��������/��1���͈i���3��k.O).��@c��I����8��	Ea�V�s��w�h��.afa.��DZ{���v�r�S���+ӊ�3��'}S�u����+���9�A�+�wLz1r�d�^|�
���}uT�@.�wS��e|��#�i�T�ܯ?�̋�Ǔ�c �����j�eR}�9���z�sD����f�v&��8 vb��RLJ�p�@+ �b5\i������IEND�B`�PK
!<1gkC		9chrome/browser/content/browser/abouthome/[email protected]�PNG


IHDR@@�iq�
�IDATx�Zpl��}�l۶m��"��X�
ֱ�϶m�{�m��}�[���
�o�N�V��t��3�sg3��A�<�9�H&�b�`,a�Q���Ny:4��$

d<*��_T�B>�D���%�YE�2�IitQ\f=?��=��D��P�Q@zG���#WӳT\^K�4
=$U�h�G"K_�2�A�j:_���Gy'�*��7���L�.&���*�^��dr7+*��{��`i�|%g���x�j�^�
���wP`l꤇|�Ǝ�<,͌�.�ξo�,*�HFZ�����F��~���*�١��XcFLP��2��j��������ᙑ��,�;B�$rdd�7jW������j	��-�cr�m����C�#��9Q��E���3��)x�1V��Ɔ��G�EU���8�ݜa���
�����rVA�/�:�A��3ip6���yHY�+0c��:�U�豀X�aI�!��1�1)��p�mu�hs �_����.�*,#����k�#�ij����� m&��~c��ϒ�VK��Jz4 ��a����1)e���3Y���0���SP��Æ�τ�y"X*�eh�7>H{���9���דD���П�=���f\΀c��D�p����6T�Ay�9���!����(�;B�4
mq5���g�;ئ��ød`�>>�q)ßQ�ƻY���v-;DO���'"��S(D�N�Iy����d��\0k��ͤP�)21�؂�'Cd�ľ�f�~B`�G5Çq�����ь{�lg�g^|
���3l�{3���,�Iɧ��BJUy�����b.\%Um�j�CY�/�ֲV�RQy
)�N���)盤h�!<.�k�
7K���Y�~d.m�Iɸ}o�/�_?�C���JƢ�xE'3�L/�F�VNl�NiU;ٹ�9z�ͯ�-�#{�Xk�l�T�l�F�&���o$�+��0��\�	ܘ���ݝٿ�:���r�XL 
#@T��dhK������Oxqyg�p�ٞ��'mI%Y*Z� �I���}RV����j:��i&pK�)&ߨ�� 9O?Sk����r5��� �#�NP���!E��]���5k�d��;�YVd�ܰ8���۩�U��x����Ǯ]@D�� Tq�pHGR�i8�����si�}�u"
�].۾}{ZUK�:�' j��^A
�
�����SC����q!8�.w#���u�:D
�#'/$�a8��g�"���n����{=�[�lIwԶ���ա�p��fPH�r�4�#|��֊VwC�wH,�_'B"ztÆ����ֿ�:a;��=–��o��[�κ�_�P"��7�O�^�.���i��?����%��|d�˫�D"���ہa�g��5�w�T=��-�Vۉ6)�aH��'���RO�u�׃���{�&�q��%K��]
���C0
5����n����3H���P�<g�B��^7Oo�Gq�m
�Pt�}�"<��~&FD����#��1{����z����02o헽�^;w�E*t��gv":�1e��B��f9.;K��-y$	a�͜=OCD����O":��τ���v���@e��h;pHT�&ؗ�S��*D��(�߁
‰ɯ�k��o��
�B�>+q���$h�":y��E
�ٍ7,��t�*�<����&׶�6ȉ�Qb~T��'�����N�s I1��۸�Ӛ�w��"Q$d���0�M ]c!߈�`���[��
�!G;�I1!l�lV@TB�I�H�T����P�D�l�wIj�s�@�����xP���bdi��D%���hY�zcaI%�ߐ#:���M��Z<�2���(!�wl8�!��.,�"V#�Ds�hx2X�TJ��!x'�h�6������'�%w��py�2��������`PV��|�����L�/Zb�s;p1�!CT����������9FN�#�	Yn�Z�B�M�����Fvh�xH�p3���������Ț���).#�"���Uw>Z���'�8�U����*�8���f�;���r ��s�W$MSN��-p[��'1�C��]��xY2�V��$*⊍��U�{���H���W�����r!^�0c1s���l��B�B�^�xc|V!g��,p�xA��x��ˊBgr��Ͽ�n�֭[�@���M�6����f�Y��X�.Xky�	o�֗gh�Ox�
���� ea2��:��$@�E�kU�s���E
��K�^�/gD���5k�Zz^|Mmv��Xܻ��`>�&���A��x��Z�v SH�1�.R��UF����Z�rUN?�X�U��{����!���K��׶���486b�b(�kkx�ō)�F
�*����w������$�#;�����������7��~�B�ˆ����j�u_�[�6l�����%]m&~C�������P�v"��Y��3���'0ngu�7�w1���?�'lU-?)
��B `��NU*�uu�ʆ-�A� MT�<�m{����o�'��e{�.bd�`�����'_o�/n�7�)9WG
�})b��W��^nQ��<}��RO�/_����8�|�q�@�C2g���g_��8��Օ��-.[]��kP���O1N���=�p�X��w2�2���G��Y��y?n9�u��١_�?�IEND�B`�PK
!<!ƣ�__6chrome/browser/content/browser/abouthome/downloads.png�PNG


IHDR  szz�&IDATxb2��'��34�Fp|6�vr��	��Q��h�Զm���9k{k}�:���m;��yT'����'�-�<3//�>~E��/���'�6�tD�� ���	����E�����))�)%<�g��d�%�=A�9�^&[�]
/��[N�	o�oLv !����O����GB��d�=L�9�����u[���B�+?+O ��2ٺf	��y_xc�@������7&Kɸ+��0n㾄�{N�o;t��kp䂎,�d���^��z16�=9�y�CA��\��b�ћ@&_��B��OʣpZ����[�q���go�3z���Υ�x�s(�RH�Ԣ����	��w*�/޼�� hhhhUqE�5�/�'�͍�B�T�7!�w ~�çb����z��Dye��Gr�YH��%)�-��(�������1-
���m�d����L���@A�:��ccck�\���_Q��|�����O̥�p�\�XIY�u������Y���~�g�鈤��?�J��xp�[Ap���GmmmK뚛O�]J�=��£B����ӹ�#�5W��&���q��V��T��E�!�9��ɻ�wF�{�����%�]��M��J�ۜ�+��g�H�]�,�XpUWW/����gr�M� OB�B����|T:g�����"���Tqq���F�Rg	$�[�D���s�z���j���o�?0<�I�}��g���şD��^�3��X
I�}�O]��l-��z_\���}��;�%�-����g����?��8���N��J�YN`?�M���IEND�B`�PK
!<�\��^^9chrome/browser/content/browser/abouthome/[email protected]�PNG


IHDR@@�iq�%IDATx��$Mǿ����Z۶m�6>�?8۷��õ�=kt���x��S����ު���'v��OE:qK�l�9A�Te�J>}D<��:4M�}���JO=�!��P+�S��%'%����%�G��5�h��0�\�TӢKN�=L4L6Uu}<���ʄ�������}
�������o�۶�������3�g���!'7iiiD��@4Ezz:�7>��@Q(����T�\.���2�9+���.&��ɖ��"<^�Ω�^�Ʊ8&���o]�9��cqL���B1K��qh���V�3��z�
8�n�;�
��z�
���A�|�6�a�n�t��*��������8��U���GT�D#���/�Z��X6�� ��o_Ƶ=�P!�i�9���l��
3�#�/(�"
?�4�]�` �k�MK��\8�����ӫ���ǰB����j�^Ew�g+�(���~��J�]�냔���gdd�������O����]��D"�`8����g�ؙ���5	~�O��~���*�[
����1��5u��

����[T�@�Q[[�ꪪ��]��!�Ow����=|>`1��Ƙлp��|1n�����}�t����ؑ�|C!�U4١���_��Yju��~���.�+p�I���]|� ;3X�c������?��6�^x�Eqޡ�p=�r�a�h��I.��H|#��@F��Kມ$"��\�l��V�4��L��I�}��u?3�+%�ߣ�!i}��?�	��7�o�ӗogS�uOv`w�������
�~De�2�}#��@�o������_�%��3�g�w�_�*�#%R��1XW����а���/���!�3?���s2�緯_��S��$�cr�3��?�sd�݇Oy�l+>?'���5���@^�9A����?6�ֽ�K�0@��x���O��}�S�����=@[0s��
�~�r�A@��_��sr˚�ܥrU�rL�[O�0w�UD�ܼ�J��
Hnړ�����W-:��犙�Y�S�l��q��*n�]E_��$(b�`���E�G6�\p�m*�U+J��g�	3'��Uʄ�ʛk!!�]bH{�Z�|�q7�׭<)�_�d�&���˗ʯm��`au0��Q*���[���I7�7�>*+!��L�<v8	V�t���@w3��	V�oתپ5K�p��w+.-�Y�bcN�/�t�R�&�1��0����޾u󽫗�9�*=�6�4ń�{�RyǓ��6����;�k�k��YTR����	.`�q�K*�?W��$ �$�K�6?^>�%��]n�pA�L�Xv���3����dʆ�!�py_@��ڱ��͛^���ݷە.t.��7�:y~���Rb�`ϋ3z���x��2�r>o7��9�	eWo�>x��v������zv�E�&W4Mg����0��ƝV{�ڮ�\�F�Kڇ�>ݻ|��!�MO�pmN��d�o�o���	A�'���~=�~kތ�W\⡸��
!�L�u�a��;�SQ�ϐ	�֞:ڀ�ݾ1g��.��A'
&߹��9-��$��_O`���={dp��ߜ5e�
H�s�9��gO��*�֏�mll��ܲ*@^���ڟG��Ic��p���`s8	����
�]�z��M�?���H��z=����ܝ5y���Z�H�+���B�q�!�kkh�3�\���О�5y�\%�#Ӵ���]�]{bIEND�B`�PK
!<��4chrome/browser/content/browser/abouthome/history.png�PNG


IHDR  szz��IDATxՕ��M�϶m۶��mkl�kg��m۶?�sX�<�ѕ�Yz{��������b�Os��I)�=?���ƙ;|`����I�ZԑVToI�F4鲆D�N�9Џq���K����wR
S�Kn���glL�3^������q��B�p�K�%�RU��wbB"#�����;%ܹ�ƙ;|`0�=��$�b���*�А���B���y<12w��9���s/r<x���>0`�!��ܕ��2˜��vZI[�
������:���w*{��gc���,1���po����v�8�#*p^�3b>t��:D��^��ݔ����i/��\pƦ��y�
��2��P�b

<t�u76���㿗Z���ڮÅ����Z���~b��.8�F�
	�4����6��YO<�l���t��sʛ�|z���d�5Vo�!kp�@H�n4�Z_�s�5����ĸ��'��l�WM��XkhPx����[��P\r�ǖG�]X��?k5xԦOK@�L�I<y�-�o��5t�n��Vf=w;,\pZh.J�'���&����?MD��i����oZ��qi�:���':t�[%�Zh�j��2*�i��K��~�ϵE�y�<�	iѹ��	�Ҙ|}hZh.;�{�0���h����N���Ӵ��;)tc'|��̷�F-4����A��Q��/��ӱo݉P�]|}SB\��{����ɧ�	n4�Ase�Z�A	���Ԃ�L-�t}be_�*	�+�>Ŧ˿���Wn9������Zh��S�Z&�YR�����O��I�&'�Qcp����
'ۏZh.;;M���Q����W���|�oln���KP�
�%�K+�n�/�nρ
��\v���,�~Z#:���ڎ�r��$&;�w���OB��맹\�-�
��\��Lv����HE��q�R�f�I|�.1ݱ;I��-iE��1�Y�k*���#�Zh.n������?�,]�Q^*��Ƿ�$^��O�O-�w�|wV4i�\�N��@���	&G����.0yy�z�V~aI<G�G�e+��?��N��@������]f�C��Y�/'YeMw~�GY���p�'�h�z��_ܴ����D�ż�6Ϻ�����/�U�b�N����m��w#�aq�l-�>�y�)o~J����諒r'a0`�!��$��Bc�Y~�⋟�w��T��q����G~�_���~����2~��>�J�S���b�N��n껪Gi�a�ĪZhp��^�l�K^e�d��`���,1�
p�����ܯ�0�c>`����["FoB�ΈtۂT�q��BL�-���/~7�s�['�~�,9<�K����Q>Q�Ju���>�[."|����>����O	{G��;�7�<���>0`��b��d�L;��)�PV�q���p���%	7���IEND�B`�PK
!<Ǵ�Qoo7chrome/browser/content/browser/abouthome/[email protected]�PNG


IHDR@@�iq�6IDATx�p[��3333s��a,$�03:�0'Me�e����̷L���h�o��&��83;>>G���j��q����������}B2^R0dBк��o6)�a�BO�j���g7�Fm{�|�?��W%�"thĔ�c�g����fjx��>#��J0�QI&8:� \s�g��-}��t���7����GN��8!(�@(tn����gb3KMJa��,m4�������.D���hcb��LTR>}�a&E�Q�".���ć*����i�EĘ��&&�a��"�Д7/6�V���5�q�zӼd�iY�ɴ.یp�=�ц��瘌�z�Q�N�nl��a���@�K��c�f��Y�T�`�HW�-7
���ږo�J��(Ur:э
laS�W×�U䧏������ff|�I-�5��.S۱�4-�`G�OĎ2��^�>w�cu���6�/�0K�?�x��Ln0�:'�$�:���p�-YB��k�!�k�25����=�ц��o�:���ļJa1��6�
6��i��5)t�!I�8[O�z���!E������i�)i�6�bq]��8�5�xF�҇����&mr�!*9�L	�g�O
�+������)a7�i��f�
t'�,0�Dƨ:ER%��g�;V޵r�[v����G�(x���S].W�C�>�ϳ����L[���/:�e��=0�<0�Mo��G�##����Z��[a��F�Xib�m�sGm���c�]��8}�L�1f~�>�iG�Љnl`��6X���Wag%��,�W���N4�UƩ�Ɏ���
���+yf���k��w��Í�l�x�/%_�|B�Ak�k��6������;Љnl`��LL���j����$'���7#�� O2Ki�=�=��I�I�%yo�҇��@:���)��f�;8��3�����2�4-5���r�X'Cf���z��h�������>q2���(ɧ/Y�>+�Mr��cWpƧЉnl`�Qׁm0���������	�����!�>?>p�ksSId5x��,E$(��k���7��|����t���ds_��[�-%�
*�\`�,>��ynZ������v�
�e1�&�j���u����kw.�!?\�>Eք�O�w��4J�ݤ��'T[�i�
las�u���,�$,N#�� .+��Ւ�J+������y���c�6�7\����/�����쨤<lC̬�6;���?���[�k�&��c;�"N�rmE��M-2��>�F�E�f)*Ѻ�Թ�x��7��g�ݜ�r�R#���W1_�y�(@�l�ZuL���4����'��i�)H{��&�S�
a^�JMJ;_x����y��F22&�RAu;akjT�PQ��O{��^8�'`��F�Z�|�)o6p��\Q����1]$Ekɪ�V�~�)�^�:t���x��q�Ӌ�OȢא�g'�45>��	S5��N�؊�,a�p��2�g����e�`�:�
��O8յ/��lO2���bB��G/�U&8�K���F;,~��A�/��/��b�$�k	�‹xQ��bm5�����
���1�Wq�Q�`9�Y&���}~��z�t�=���`����9)F�ن�^JIF���{|���l�K�F���J̽�\�2���*��י��٭�
6a��&Dwp�@Ec|����J~0l^ڙ<�#d��)QѹPk���vuP�
��������Bt~Z�:�S����V0�����楟��惽�����Z>�'��6�Q:i�{��̒g8�C/��C����g:�T?�˛���a�1��4�L�B���%!YϹ�IM��Ƙo`|V�+���68�G��
Xݧ��d�%�8��fyt�Nޘ^��D	����R�*ұuk���R���L�.{��n5"��I�&.�q�@��QX7���\��𹩫<:�JȾ]�C8�5	��
����\��̒�"e*vlj
7��G}a�`����X;Le33!�6�N��y�2
Ul1�%��xM	p�T_��S>�:6��5#�g��X�v8X;p�ctR����N�/�5<�(�6v���J�����*��D��4la�{������=P`3��m�`������t��ĒĒAҰ������A#e0G��vH�e:��*�����y��`�U!6��M8�gz[�N+4	5^iCSJ���l�f�/q��b��JP����iE5��r�u\@�@l��`���78j���{�Q�s��iC�0��.�������Oq��D?���h�t�/c��~�67�s`���ذ��G��<�$"F��a=V&/�K�􈘟��k6]ו��Q�䥩b�v��wDL$/�e����!�?3�mD[�pL�)ѣSZa�C7����vR	��p�1ƿs���Y��i���G�	�����Ї�<���03.�e�8�;�P˭�>��)���p^E��Q!p/*MptR���]á�k��5�x�
jwBBLV��q��	A�׆����7.p�w6��T�ر,����5;"x�IWrҼ:L;9��8a�;+.��%1b�O��l��C*)F�+�K=f�)�^l����s�G��k���I����d��#����	?��ۺ�`��p��G��`���J�o*|�/z�V0��:NpK+�6�Wm�<x�W~�wk�
;/�l�M����)aa����'�{���*s��ȫha��;ص#�Oexw�����Jf���	nJ��{����+��
P*�p�3����~����>v�7$я?�LvYCǽ��3Z� Q2����X��^I�0�+��9�n�e
µ�wq��Y�-��[� 
�)g$J�9S3C��v��I$���n޺BN�*���b�Z��0E��I!��#l�FX�l��Npǵ��ǞM-����x�Ӏ��Z���>"�Hr|����,=���"S�
Q��frȜ�Wx�rm�[�p!���⃏<q9?-�n�=��LY%6�P`�"c�^f��� 9��C�>�׶h������cU���]�e�(�����w�KQu�����Ю�G�	90�!�
��I�c�G>����?E�A���$H�&�����Iazl��`���8��w��W2>�����\E����'�gG%��u��3������`I������A�6�����������_�}�h����K�>ʬجL����|׿�O��\��	l�X����w�n}��dU�7�hIrH)aDX!\�k>n�f�_ʿ��l'�KX���pp6t�6kJB�KO<�lfFq�)�ℐB3��qY��'��ȗb�`���
fag�O=��3������o���:2�*I[5�pB�,BxCl2K�Ldl�J�F��?�����&�
�ڈMl�l�f�m�f{�Wo���ÒYu�짰`(UB!���0Bq����L��Ġ(��'p� ����	��
6�
�l`+�k��YR?�m���3g��Z ��KT��;Ӂ7.�v�����?���;_���J�u���6�mq���`+��.��R�ח_}-%��TVi��b2�E�!L��-�/�i�b�����0rJ��c�'=壒�؋.t���¦�0�
���<fa��/��*<��R�4�_���v�e����S�B�h&��T��qJIk�B�P��H~*���=�9�OxF��N��]�D�lx���F���*H> ���ד��Zd��Q�y�
ߩ�h�#��yi�'��QS��а��)��{<�
m�)��[��f`���=��ko$k �}]�}Hp����Ň٠p�c�abZ�;�*�[����ޞ��==$���3�Ж>�E����30�L'N�Jc���"���7z;�Ҋ���y�"�#27	KĆ�)ډT˓�������mld�����
��Wo[&�y1�^��wU`d�ʺ�c&�ڄ"�J������хNtc[�,o��ǟ����O��X^�uǾ�M:i}P��l�ٕ1�g��q���C_t���Ɔl=�}ϡfl�`��ﶭ��h+��i�Wo.��)��F�:�7�mhkI�C�8˿����q���c���U���d:����^�asAE�c�U����q�)�yNy���+��#���hC[�З��Z�~:݈c�k�r��J&H�Y����f���/Ys����fTOf:j���)�����7xF���k��~�6���F�D����?5~!�fOu��2ڿ�7p�{��#)�D���"yF�҇�����\Ϗ�ׂ"IEND�B`�PK
!<вr��
�
:chrome/browser/content/browser/abouthome/restore-large.png�PNG


IHDR00W��
QIDATx��$[�׶m���F۶��Ѷ=~�m�F��8w�{��Ϊ�̲�Og���q��������D~|��
�7��D���!&��-	��mM�S�g���[|�բ?�dCL�"R
Ur~�ʭ��������O�2�{ϴч
�
|��
_�)�D�:�ZX�J^e�v+G��t�)_Ϥj�ߥ�v���=�F6��/p�y��j�ݜ�swf�Q�8Z��eP!
��C{@��k\y���;^���.8�&�Ѩ��WlIp'�V�JK��K�vU�nQ���+�f�ҬM}���
p�'�� 1�"��k#?�.*���r�2�z�����B�Ŧ���'�ե�Z"x_��iVFAo�nb���>"��Eskb��Eu.*�<�v�-7�y��"�[os�U�f͍}��6����,�ELb���/^���{ʍ>�jQMLk��”�����L��)�W>���17�zgݥW^g��wꐻ}�zKc�+��a�(#���X�$6�rh�_�Aٿo(3���c4����힠�_{�5"�'Б���|O��g���s���3�$��&&�р4t��{��B0"D��
�z�ǟfQ�8���{?x��g����27��";VP,~�-h:(���f��I��X�dWX^[�-�V�s���+�3���sT\3���	m�%�#�#qYe�qr�{'��~��Po�J/��T=�ok"�v
i������y?�|��{�1����};��A���D��S'&�����i�B:(�dȢT�U6�E�Ek#�����HLF�ڞ���E�^-�%��H�C�<6g�v>*>mhC�B���r�f��ط��'*[_��Q���	W�ۼ�U��O��wu�R���).��E룷�H�}�]u]A���%6m4�
�h
J`cLfR�[�g{;T���e뫶�p�2_}��������~�3Z岳�gW�e��!��D��������h
J )��b*E5�p�/�1<�mr�Η[賴im�"��ݩҊ�I4(��{��uζ����Í�;Ϧ�Oe�_w�
���+��`��]Y��Y'!m���Ԫe�6�F᝽#���V�ЈV4�'�n-5x����D��(�<�����٦d�?�Hw�y��VY��C�V�0NΫ쪵�R�c*ZT���V��w�=p���9ڔh�
g�.�[$�:栢F/��A�Zp��ZeG
�A+�5	��50/�`��7��r�Z�9�k��zG7�G+������mCTC!������A�<����|�브�m��}��J�A�hͫ�<0̫�rW�����_WJ%2.��ۙ�_�v_A�'*͍*��P�[�-�u�mY7���Հa~�M��@�2��_�w��]���㨲zG�5���d˻DA��	��_ +FA���Z�%��ZB��%�yI8Q!#`t��jc
z[��ڟ�0M1�-��G��VK:���ߕԻ�6a\p��:~^,b�Aooo ���F���Z�k�<p9K̩�����<��	�S�:Jl4���Zn�\0��xw��R	-j�-��'�ϭ�Cس`w$�.8�q����h�;�K*-���Lja�3��������T�I������� &�Ѡ�A+��g�{�ϊ������5Y�j���1?���[36�-_8�SCb�[ϏF��Y+�U��V�3C�^����E��
_��I��V��������"&�Ѡ�C#Zь���qTSQ���ߣ8�VoK�	���9N"SA�
�`��p�y�ELb�}���5�
z�y���PZ�z҂�`�J�I���^�+�C%���3ګ,��9���_=}�`�O(�� 1�<��Qޝ�s��98�֔ ���ʩ�5!�ѿ����_��zFvM6��6)y�R�؀g���[|�<p�X��j�L�1�z��.3s�,3���nq�@��dG�T��V>0]��[���T�c��~��ˮ���9��L}؄'�� 1�Є6�n$bl�	�
��
�u�[�ߜ
�����_Vn��~x>����;�g���N��A,ml��	m��C����yn��U�H�~K�vE��'��5�\��?����N�C�D���q>���ɧ�^Te��g��y�6z:Y|Pk#R��mX���+\l?��r]�ΐ�ЀѴ��+�0�q�}	�FϔA�B�~��r�ϑ|1{y��ؓe!�i��_�a٦_���3m�-����/�e��\!c
hYx��g�p�myrh<\�j��shc��:Z���2K$�R�=)Om��R��ϴч
�"��y�Il4��ϡ$񑩙ٸzG�R)���a
	�n�x9�0����i���`GLb��Е�^��<m}����2�liT)��nb���>"�D~w���kl��Uۚ~��E8\p�}�Y�{����$� �4���J�w���UNZ�����?(`��p�'܂/G"�z��Wb��pz���yI��L���;{9[b )�3m�a�->���G]��,��"A�ig_���f�]#S�~.l"R�i�l����c虻�e�-��x��>l�9M�
��e^��IEND�B`�PK
!<4GZK=chrome/browser/content/browser/abouthome/[email protected]�PNG


IHDR``�w8�IDATx�P�I��_wwww��+�	���uY"���������-B�|�������YB`cU]������t��>���.�����M���"�D�O���a�ܥ�L����/L��sh����/|�w��-��,:�em���'�5�Mu�;9�+P�Y��|•��Z�-�U�C�����D�����hC[��Y�t�����{���q�4o���Gg-R� 0F��'���L�R�b��Ur~�J/iTY�6�[ժ�kV��T��ц�<ó�@:э
la�`8���H�7�7 ��/Ry�%���'ĥ7(H-ilW-]�z�E�n�&հf�j�ئl�;�ҵ��w��-��,:ЅNtc[�T�X�t>�{yfx��WKB D%�V��J�*m���C�j^�rM��!ۢ������m0�L�m#�E�%��x|��o��_��b2�Tv�MiW5+70z�H����� ���E?�Ml�,`�*�}.�ىs����
�ƹ��t��S��W"Tc�V��L}�f���|f�	���	l`+���g%��C��s��T@T�L�JU ��f�z!l���;L��B�}����+[:%�w��tɵW�;�=[߶�B��~��
f���v���_�)�>{�2Ei�VL�B�e��j$�Ex��^i_��֨���7�z��*�Tdݪ�OU�^D���m-�X�v|�|�7�O�2b���F��l)	T��sX��Xͳ���浪�v��wl���p�Դ�)��$�۰��]�����]5�,��yhqp����VT�ZF�F]6{�3�)%��W��;�z�oc7��v7��~;@��P:>&�T�>⫋��4r�����*������G}��BF`�td�ʋy��7R�ȟ(�Im;)�b73���|�y]ZFb6���#�Ⳬ�_0oi�
��b%
0Gu�è�^g��z���ч�'�璉�=�a��c���I�=��@��+Z��3��)�������>,)O�U�2]u5b*T7<#D1�7P�nT����}O=��G�,����R
kO�T�H�Xɳ��P��Z+����lTQ��&�}��gу����p0V�琘"e��@�c�e�53�e�B�&�
l0��)Ovn��➞�QBz��o��!�{�I���s���r�m��+���7�^��:؊��T�̐J	#����H�������w8���%��h7!?�Pb�!#q��BL��$��z�C��Ah�_žE�	癁�^��q��]��=��:E�.��u�_B����p'�E�Ǽ��'Ó�N�|b�&�X��ڹ���G������:�K�����E&�x���_z5�k�eK���H_G�gD�\�8�	p'ps�K���Y|($>G��
�-G?�޽��HZ"��,�>/�+��"�9v�;f�%�l�|�.3�A�ZLt���p"���3C������r+[H�:�9+<'�������>�lj�ѽy������eA�f��+e����M®�$KAFS_�N���j�Ou�i�KT%
�c:�
Kx�i_&�1����[۬QLat��D~+�OG<�{ayS��%�$j��M����#��������t��3la��C"�Vlz��8ڕ:��w�wwG�vn��6�6I`��O��8�+8��#۱r��lDr�*ŒAY�)�V� {�ij΢`%�`?{�'Ǡ#�*��ٰ����J�tRiM����H��R+r�⾎�#!,����fIbٜ�*9<�S:�����_:᚛�I^8N^�\%_����pg�]��V=������3��Jr$��f>*�Hϣ�T��&�^4��t�*��E6�������4:��)��{��N�XI��M��+8�;8vHF�*�UQ�����ɚ��˩��\5���#��IV�y��>[�׼%�j�\C�� �mc��K��u�o��	�%B���v�����,{K`��������j��ܳ9�ZT�&���Ϙ���|���JWs����ٟgPtڣaI�**�D�
ղ�l��(��h:DI)�ÿJt�s��M'l������G�p��/pwp�Nw�\�XO2�{�δ`��#��a�y�����yL��@8��v�Cə����Y����MP�|#R:�{��ON΄�
�r&���޾0Lp�p���٢�N�HB:���`���2?:��Q�H~O�s� R}���&i��RNvHg�U|v�⌗$?�����s�~�IQ����(Џ-K��N�;@�oc��>&�3�FS�ɂ�Ҥ�j	+�߃z%/�����c�ێ��-�S�h�vA�dV�d�r�=J��E��
;aƒ��)��.�\��wp�^��.�>�00���:U%SnTȷp��i�\9\�I��T�Z��>�d�y(RtG@^��;�c���?P�%��)���Ȣ�][�,�YI��Ϩ�.�T����)�
���ⲸsI���,E�
s����J��lϺ�F�5��q7��+z��xHn���k�~l^��=�
z�7�=�.�T����9��A�$~�S$W��qYe�;<�prLN�p�Vژ�!oD�R�����tm�dwM����&��N��]C���Y���f�4rB���x5��t)�b��72͝�J[r��rT���	�&{GG�έhy�5�	G|O���&6��dZ�f�@R�Q��Մ��$h��p��N(�?,1W����?a���t��V��|iA�\`2h�44!W����}š�*償U$�M.+������ۋ��V�[L����d���2�)�+���p
�����y4����S�y�O�yb^��
�&�Ǡ�VTӺ���aeN�V8~d�����s��'%���ZL�s]�PD�`�M�~�A|a���`��ceN����q� �_I�,ă���ZZ{7q�R�[�Z���dM�K�/�K��c� ��GP�׸N\;_��υR��'�(��f�o2K^+&ٻ�1�
��-׎��"����\��7��+���$������Uw�_�N���u���e1��0uYǔ:��)mYW�G�p���Nh\��E{�!�J?��qpl�5�B�wg�5��{V���\�ϵ��,�P���٦ /���][��%C��[8�ke�^�n�xELL�����Է��k�V��7���)m��yF�uY�'�S�/�*���;/wK)��|��.����m�C�3�U�1m0a��v�B�i�z��n�{���͒Mv�Mq(�
b�[�C/׷��^�+�F���n��w�9��C��w8�c�vP�\Э��L����ʴ����K��<��ٷ�u��˯k�شkgk��q��j���:<��l�$���g�n����~���+�p�p�,"9�2���tR�J�����K$@߻H*m�O�ԯF7�.)`�7YeMR��>j����,9�KQ+���p�]A	%��Q�uJχ��|A�;"����8��y���ƹ����I/z�d�:&��Ǚ�c�vP�^��^;5-�j(!���"�����|t�����{rYbN��&��\���~��c�v���Ô`Ԫ:V&�2��Q�E���
dZA��Lk���(`#X�
V~�;���N����끮w���4� %�qa*�DQ���0���SqMK�܂�\c���%l`�`��K|�-���N���z��=�We�~����c3Ke�*��
h��u��v��+
�V0[���ppb�p�p�,�����
�(����}dUa��*��^���~W=B��{|t��_�pa�p�p��ʶ���uψY��>��T��;
ʵ��ּ����q%�����%0[�������'8�[8ίhv\�մ���:��g�� �� ����j���)}1�M��[� `�y
�`��_2�^H+��K�~8�kem�8()��:��h	�6�kE�s��+�?�g����$!�Z�	l`��?��x�PNy3\X�C����.Y��_���´G�j��2;�1��%u+$��\e��&���W�c"����;��z ���^X�����	L4h�����&�w�`�L�ނjrA��1���	l`��?~wK��#��T�%?���lZ}���L��204A�Q-�:��Mmwb�騷�ǒ|0�LM������T|�w���:��1\��}�:����#�Ŧ�e�_~5U��!F���
���`�������N�S���A����d���Yө�(���/���~֠&_z��vq�;<�;a���6�&�fY��#��i��K����x(r�WTc���@����	N�<��Qq�l򼥩&%�H��m�U�8����q&�¦&_�L&�'>�+>�fl�%�V���Cރߺ��fjU2������(���!o�w��W:�G����%�C�N0#ۂ�n���
7��#��	7p��)���u����_}@�h#LEF�����W�����Uek��Q�	#Ew:2��k2��-lb`1��o����l�1pz��&����q
Ft�bb��lL�y��7s��
;a*��޾�JYQ�R���pN�EN�xt���6�
���o���&��
.�Tl����{�?�(=֓/#��Y@.�� �e�`��Y���{���[%�D^q�-��⺃��*[���F/��08�јЁ.t�W\{s6�m�^"|�'|�G�p�m8�K8�[�_���q�mԭ������N6,��U���e�Ly!���fr�}�m��5���.�Q�ʋ��_�紡-��,:ЅNtc[�4Ň/��o�h�X��N�z��#O�͖kt������aV��r��ʴ�u��D!�G�(�l^s��v�DI'NA�@�w��-��,:ЅNǷ/Z��|�'|3���p	��\Ri��{=:.�<���78-;)�w�6?�]}���^�h߰s��?��Eu�ʨ�����6���E�Љnl��;>�6����N����p^Z�%�[�)�ݹ�6�z�dZ��;��.��Z��:Y�pӸ[���L����λ�W^��}Î���7�z���u7��|�w���~�B�3�v|�|�7+������.��搩�weH���#�ț�H0C	mH|$��DR��tO�a�b�O"^�3%�gx��+����b�7��	w��"_�`ʯ#3K�_�	���ϡD'���*~�C'�������)���:Qd��<�����hC���#X�v|0�nt�3���Y�v�/W�pk�$�^*
��S� ���F$sn��;sA翧y���L`#X�vgȇ8�+8���z��RYH�@,̯��|@L��	���C����A���i߿�{�s�&���`v�G8�8�+8����lQ�f�ֿ�Y� �u+I��N�(����f+9̐W�x�J�k�����"ۂa
X�60:K>\�	�\�و����{��ʙ�V�LO�а*$�X	�h�j�\fZO��lxG�^����Q$����6�&�
�'�NH�G��F�~c�57�� =�Ŭ4��P���T	NP_{�/�0G�;|�{�g�y��L�nl`��X�6S?��N����h�gE�m�ue��n'Ж%=c����Y��(�4?�V�E~q��T�"��D76��Ml�,`:
�{�D�	���G&��wn޵Q��h�9A��M�g���઻
��䪣�+�7_����xY��v	%����+�w��|Qc�o>�;�H�byfǸYޘ8w1�Љnl`��ֹ�X�Q�|t�\�	܌�;6!�Ҿ��@%�;a8�[acAE˾K`t���M��0姦�y�6Dy��"%G�JV��ц�<���t����||�O>�����N��
;6s�)K�=��Ŭ���3��R%6��;�o�c3�!K��Ԃ�(Π��o��|�w��-�pY��H����E��o������p���`�%W����~;��,�qֺ�;]8
�8�f���/�o>�;��V3�-��8�
���4c�	��Z*!�0G}$6�M'�Q����O�������y�N�9IHv�r��^ �#Yo��:I�
V0���ķ�_�Y��*��}��#G��ԯ�>)���U%۲$-��W&+�����OG���G|�~�Z'|Q���[��*kx���U�(�C-�S�B����
f���
��U�O���.A�Ŋ�?��>նn����T#$HǩHt���z,D���F���/�d��u�����b�?�܋U�mwȡv���q*���
2Z�cSWX`��L�����&�b+�C����sYu��7I�x��������7�aA:6�
�����q3�
f��k�l����,�s/��J��]*w�ߠ�O+�e+Ww�I�x�;!FC�Bݞgс.tjұ�Ml�,`�
泐j�#N��"�8��r�}U�-�H�=!$>�S		���#1B"�B�6����hC[��Yt�������m�L`�8B<g:�"��pGB��.ϯj�+��q�[�ͬr!�������mh�3<�t���������e��T1��Z��t�8��c{
�_vM!��(׻wg�6��VT��#)}"��ц�<ó�@W���&��/�����/��05B�.7tc[7���"�3BD_b�'�H�Odهď�hC[��Yt�r%��F�[�4eIEND�B`�PK
!<�>��4chrome/browser/content/browser/abouthome/restore.png�PNG


IHDR  szz�qIDATxՖ��K�{m+���ݱͶ{_k<m^�f��l۶m���/��?�x���]Q�眬,�.{O}�VL����=���խW/��|0f�9�Vӿ�.�ؔ5�kڟ��앎^�tY���ɐ7%þ����1rȅ�_2�<��|Q]���-2�N�#:&��V�g�	�Ć��3[�vI(�S�1O.���h�S��׹V�K�3*��F�$R�-��v����8�1�>�M�������ZG��9����iOk�E����2D1�$7�+R������V�����e�\��q�6Hi1p�@M�[��e��;�d%��o�s��S|����SUW]w�5��z�v��x!h��&ڇn���E[;,�HA��|7`�"���b�!��yş���a{8��7�Y��MA[=��<i^��ۺ��gh�0W��������Y��VN�]ޠ���Y�>\R�g�|��w�p�;�I�C�6x�W���V�р�G�P���i�
�is�[Ι0{���-��x����������*ו�Ǟxr�5�~�ہ�x��u��pӪ���Z�Zv@s�i��t˒������A������:"Ҽf��v��9��0����׌�R�E��4P��5C�;M�!��36&]֠�p�9�O��n���3'+����ݬE|r��Ϲ����
�<��4����<�Nm�v�y��z�|�-&��N=+1���k�|�y�C�/<�Z׍�d�	�C`O��s�`���I�*?ft��z�(/<��{l/��EF+=�M�0f�����9YX�:��t��B�8^x�t���2�H
f6_�eω��ۏ���@�H�Ko]����2<z�������@<�V�9��R������'��?N��G})b���aY?�4<����+����,`u��EN�O[g	d4h�Ȉ�b�VC�A��w8�`v��r�O���Þ��"'�_:}lF�0b��x�7�{^��?����&�����,`�:��h�3Zddڢ�?���z"��{�9$�!\4T��h�Mq<�b�Mݽ�g���򦕻����?8��z��A��9��1��W�B���O����k�/r?I��s�Τ�?7�ԛn��aw��D[X0��:b�.h�j���g�����
ۇ�qq��S��F�����?�X��O(��7���k�L.�0f.�i�.rȅ
��D��`��ϲ��ɧ��Uר��sWl�.p�Im��O3�|���)�ϻ�
Wq��[c渶F��-硁�h������@�L�ӎcC�[A�hi�QQ�[�4}������qi�ܹ��7�5�M���`��|=�(�4�C	`ؓ���!�{_��E�*;�G�r��nCM��8�(�ף�����,����ڤ��◖5����Sf/k|x����ec我C.�h��&چ�ኘL��ԫ-���C*�׋n���z��c%-�0f�9��Zh��#��9�d��'�)�j��������	��93N.�h���-�kO=�LK4��K�9~`��g�Y)`l^Grȅ����AQ�BQ}�E������"�
:#�w[���;��K=�[�o�����\�bE�83G�+����|u� ��IEND�B`�PK
!<��X���7chrome/browser/content/browser/abouthome/[email protected]�PNG


IHDR@@�iq��IDATx��$Y�?۶m����jl�G=����m��|��yZkޗ��}�����8��ꪗ�����^U}���s�ߍ.�_x
�or
�w�[�+����=�[=B�s\cc�A�?���	�nq^z�W��	~Q�-(^y�MU���* :YĦ���T8���Xd�E�����݂��;��k`��LP��3Tlj�Jȵ���:�SѬ
��U��K�u�9�5�0dѡЅNt�������H
/�yGLS�S����r�]ޤJ�Ue됲v�P
�sTc�\�<0_5���ep�jZ��s��� �t��
��濃�?���R�olJ�J/�S�
��V�hۆ����
�(N7��VuݳTS�����y�uL_r8�t���6�
���ᒖx�MQ��*��A�7��zq����q�.P�>�,-�mp�����n�}�V���:㰁-lb[8.W��OK�G&�H�f���U�؇s8jG\�uk�LҚ;y|�=������ك5�I}������&��8���w��z���:Eť�|i\8f{��㜇,r�jچ6�Z��X�"p|:���@Yc/c��B�����m8�N�m)��wn�[���s+U���:���n��yU�0w���ܻ�9q��4q:N�k�7)��@��E^��h K(�#x������Np��p��X���eN^(�uZ�UU�S8؆�BJ�߭B��l'��P��+_�9{�}�,n6lښ�֚
%ϼ��a�=���Y�dU����Q�kڇ%�3U��E�6��&\�78
��p+�?#Q]ʂE�UU�ݡ����;ws�kd�S:�,NO�E�[�r.���b��m������Ի��3�]y�&78��p���2u  &Ye��V�ԁ�b԰��|~E�Yg��\���o~%�A�,8~�d’��@�D�����A�#\�|�{Ȍ+r^:k�O�T��R�Cb����-4�`>�K�����kK�:�˱-r�#kt�������Y�)���v4͉�����p�����wY����CY���YF76#��suQM��N�?$��+�@�F��'�[�,����AE�i^@s��2E���—K6|�w������I�:o���E&&�@�Ď��憉��@|Lp�`��
�$��$�A�����<�/�V�nA���I*��A�LR��G

�*��U���܃�Y�?&A���-9��`�`� K��;��m;wgW4��eU	Ǧ�������/�@|r�����K@�+S�ʘ��L?Lo
�(�vI�*��iҕC��8�o���y�ou
�UR�j�4S���5���0�
B�^�������J)�aWs�?��_\�c�^T��KȤtYa�1��YI�K�^��?��!1?��<&SS�ў�}Ӳ+TB�Eŧ�0Y�zK��f�5�@\� �&�?�������=;9���O�۸�%0�Q�㕭�:�.�:�ڙe
li��t�	�'��Qle)�6I�?:������d�I�v�L����0:j;�S�Q����/�r*�d�C�u^���"�L^e�1���"�s��Xi���%��{�M�AnE�������:ile��eeGF4�䕎z�,���R
�"���rħ�ؔ�ҏ��0�}#�'g���O$������C���}9��c4M�ێ����T�
�*ɄA�f��g��bk�]�b��$����q��iKYJ֊�H����nTH_`������s��t6ɘؔ�		�?̦�#G�E�t�����Q�d�ۤ!�d�m���=UX�a$� ��B��~k{���Ȅ\)��WMf��ˮ���R:�:m�|_?g����ҋI����SAZp���vVy��	2Kt��o�J�d�R٨�����j��)��0��̎��;Nd̴�r�:�A/�kau�J��!���k��p���Y5Q��4�g�l����Ye�˳4��TV��6��m�ϔ5���1��_mDY��"}�K�:59�]����o�-�L`�4��YJ��
:f
Դ�^�o���v�q��j;�� �C8���,ѫ��E�Hو����D��6Z�i��Ҋj�T���X��S�WƟ�<�_��9�`��A�9�*M��,m韽���Cq]绅5�gd-�5�0��;J3���Y��`d�"V�p"K��p�G|5	@���pL!4
�	�C��+����[�s��yU-��<d�A�)@7�y~U��O-�l�������0�c��7|�W;Avm�y�.�M�D������Pq|��[.�L J�vR��Rp��CR�<��U��,���?%�WQ�p?G~���v��<=���367���Rwi�ט<��\NE���.��u
Ѝ�B��Ԃ�5fH�.�&�l�n��7|�W;���w�g"L�#ڀ(���F'd{�ͷ4>�_�J��
�)@76��W^����e�-�O����j'�UR���(]�&t{:/�g��ц��^J���tc[�4�"s�
g��>��]|�w�g��p�9�$I2�}����
ki04L-?�@'���-l� c.�G~�#��	V5u�ɵ4�H�Q$rD]���,A�gi��K�G���Љnl`�f\�&�o�+��t��:�Nph�����zUN#�������n�1]�ο�^�K��X�����i�C��
�p�����-S��j���w^/���Li4}�z��B�Y�O�:�[Xղ����U"���+:ЅNtc[f�[5u37�'|���;w_k���˳4�#�t lL�ti.�h�
�>m"���+_�d�5��: ��8�t�3���$6�ekN��
�p��u�#���x�蟵��V9iLE����KP��'��X�3k�=3$�%�m-r״��b��#�t�7��\p�Ll`��]F"��h��r�|�g�u����7VŦמ)�T!���4��y�[`���|�$_�
�Z�(K�PT�N��M��w^0��� �t�St�c�֮p��<3�
78"oԇ.|J/��^�nc�޺�����u]�f��*[�
Ȃn:~)�2w�ӳ�OrG0�w�Ԣ���c����
'хN������::�ٍ���������{�1Kf�R��::%9&��佁Gp���}��/��vyY]�)	�!�U�4(tT��%�ǜ�c�L��c:ЅN3[�.pB�-_|�|z��g��P��KH����D�4�1��r��
��I��ށ�_P|u��Ot��u��iv�i�,B���c���=��l`[�/�t�R3f>�t.�nY+z&\�[��<�B�e#�S+:��K���i���|~�@ח^�A�v��ً���5�LEc��ŵ�[ǜ�c�x�7:�tc�<��lx����E׷.�U����k3iI�II)�1�S���L"�`۵wx��<�~$�ĩ��'2��aKln�vJ~\���/�����l��*�;Bj�Y�
��*_y)���%�]!�s_}�Jp��M�8��s>�6�
���F
g�g���>����-��nQ0ZT�tF�����o�������v������_s�i;���}�[li�m�	�p�j�]��˥��/E7v=.�U\�A�&�xBD�~CDvh5*T^AO�����~!�.��8�t���6ui8��p�s}��S� �<���c��k�Vӡi(tU��.	�o�f�;<�������>I٦��K����c/�Љnl`��>\�
78��p~z���\��4��ȣ/1�A���>�~�c�Ľ�k�1�{�l�6Y��py�����S����=s^�~8��#cw �,:ЅNtc��.\�78
׭p�7~h����ع{o�1@}�5t�h2�U-��J�Z��
Ob�$��G�Ka4L>�Q7��9�5�0���t�c��y��I;�cמL8��"��[8�Njim�*VU��aYa龠{�+<Vf��Y�Ѵ&���i�Ա
�I�q�9�5�0dс�stk{؆\�78��cڅE�P|����Ʈ��dc1��-&S�P�#t0h\LM��e�@=�9�5�h���h���&��QR7
'������2�S�y�NY'�͊g��I*R���.�1�ևnl`����~��N8�m�#�p�5y��<�e�a�d��!e/�%tc[��6�'ۚwv ~ ��3/,��Z�W	)�|J���v��r^0�fw�.tj���Ml���C�{�/���+j��zVdמ�)��4)��д���3��� �t�����m^�W?��ib{��O�1��Ye֎��	�q�'�Yb�"ͫY� �s\cc�A�ڽg_��qwl��w	�8A�&Kg~�٥�C�/h�z��ڶ:�Ҵ+���@f���s\cc�9�JN;�Nt�8���O
~}6+�p�2�����|�Λ5�%�KS�� P&�s�k�a,2W����a�}wb�IEND�B`�PK
!<䧅
��5chrome/browser/content/browser/abouthome/settings.png�PNG


IHDR  szz��IDATxŖS�=I��m����9�m��ض=��m۶�ݳ��ؾq�X=dDu����U��D�ŦD�|��nAF��8���y�j��?p����{�Ω@-$1���6��0�k�Q�-כn�K����׃�s�3$�mu�x{�-��Z�f�0�#�jѠ�c�����H��(����>�,����ix�H-��QZ�0Vw�QC-����L�R����t�����~cIm�4��HuǨ$��s�_T���5���1G
�h��k�'��v<Z�=!�}�bhꗬ�:�14b��p�QC-�xt���z�X��p�ڳn�d�:7<}���:��f���v�)�q�p:ǜ�G36{N��W�:�>�x��l��?{�%��V,!	���}��W��̲�M-�,醀&Fi�O�� kI�qQ�&1�%,����Z��F)4�Jm�4�Moh���x���b�4�;2�QZ�AU���LHM�T�
����;��Z�:��<Z<�o2��f�;���G3k�n�h�eO1Yx�����>í�14�	�d���S�[�C��g̙�6�L.@�:��|7t�\�ƛo'�Z/��ko��1G�b]U۰tM��7k>�i�+u3k������:U�W�g6�P��1G�b^�EUW�D�]�&�	�3:�E���tI/��1b�,`�-�<��3�6�P�]6���5�Zk�J/��� �LK�1�d��KAe�W����_�\F8Dp�
MM�p�{l=��G��9j����'�d�E������O�-R�:_	̧���~עpwT�5��"��@US���F����N1��Tר�u J̭���[��
=�p�QC��/<�&�,2��6��޻�Q�8�Ye��;���SۡB��5�Zk�l��:�̕�Vd�n����y��(����6���1s�,֕�u���cL>�#��o�:�Y�H�������Ū ��Dq��0�c���:�┧�W�8�6����t�跹�س��%��0��AҊ�c�U5x�MYK�LI�.Jk:���+��)W����X��5��1�d����k��ykR��UD��U�O�}��텕-oV(�v#@���ě��;��ಫ
��kԏD���)3}���lh�����=x�A�r
�(�U8Q�B�o)05���ϲ-���:O
��]4�N������;��F���kW�r�䛚��o�BcC�=pƦ^ �ᘣ�Z8��#�Gu��z]�yj�1�݀_dʙ�+&���L�IyM��_�@�2Kj,��1s�P�-��Ӷtֱ��gS���%���ͷ��o��%�s�@�v
Ш�
Z<�Pm���g�1qpb~�o/����1�ş������Z4h��l�{��o��������J��0g��E����\6�
`I�?`��e&l�aWV�:x��
��.�p�IEND�B`�PK
!<�Zb]]8chrome/browser/content/browser/abouthome/[email protected]�PNG


IHDR@@�iq�$IDATx�td���m۶9V0��6[a#��3׶m۶�z�[�k^�szҧsӹ�����W�ڧ�?��Ox?S"W~B����ѫ��i��=?��n�X�Y-ِ�Vn�<+ڤ�����1Om�(Qy�ͪ�ޮϴ�nM��)�[��o�ݐ��J��йS9��+�3m�/�6���q�`ޤ��Q��Y�Q-X��KP�+�J*��Q�'�t�jU��_���U�
�L[rA��`Klހm|�����d�1Y�7-X�6����z�]�P	9�j��d5c��"#�����A�(�3m��`���.�+|��p�K����E�Ubn�*�ulk�F*���>�ZT�L�0-zը�ɔ���������m| �O|�.a��U�Kc3�T��c�zn�٣*�TLF���lӠ�^Ne�rt�5���/�
la۾������9^���R�+�Jc�.�ڷ��$����*&cx֢u�5�Y��9�,��G�	�L}Ȉl$:��7l`�ھ��7�<��߄}�R?��~`eC����̎<�;�Uja�Z�.��K7��|�Z����lmGe]�:�!�,:�b[���o8�e2�/��J\eC�cu޵I*3;��{���l\������%k��5��F�(m�!�,:�b[�h9|��/��r\�4��N?�S�ԥ���Y���Iӝ�����⯲z�|e|A}��`��2�������u�?.�-X(���|D���c�&�gP�1S{��>��/h��x���o�G�y�U�H0WZv�7b�7�F��aƂ�j��u���L�X��_?�����]����'UuzO��|�S|�X�p�#\�wb �P^ZSq��˖�)O-ߜ�"Wl�ʻC.�VhOZQ=������)�;��
~��W8���@,fb����X
��jUbu�rG�k�*.�\���KKV�=e����UfSV�%0����o8�N�G����@,�l��R`TI��(;n���pMs�ʭl�]�X�g��f����rO�d���n�V)�^��	np�+����S��bK�]YRדj�`���C�U5r,���O|kp�5_��S��[a;3��Q*�^1�������)]��>!�'X�����o�[��\9�G�BLA��wݛXR�|�wtF�*�.i�D6���f�@��ĖC*9�<�F����g����g�TX��;)��_�l��}]ϸ��`�,:��;�-nHa���GU7wS��i�dC�G����‹/�����`� ��w�켨��>LM�`��Yzƙe��E6�r��k�����9{p�q�v:���}.m�!�,:��78µ�{�Ep'b!&�e�Gl��%W^g�����y� f&x�vH)�g��˯��{F�d�13�
�p�ڄ;1�|a��a���=�^f�Nz�����S�MH�v:<<<��%�\�_�L��>d�E]�%�
�p%��-��J��\���8���̆NGnn$�y�|-���At����|�	np�k�q��
RmV��8c��#W%���s#����B���Fl��	G���?s�~B�ϼeOM�\��5�Rn`7�urQQbq���!���/��ŗ_]'���,t�ņ�G��78��p�q��41�ѫo���j�ܴr�lJQ�k�U�\J��q��d@���r��Y���������-l��np�+��N�BL�F����+��6�-LF���	��kQ�u�
˨B���b��~��"/#
�����僮�8[��7�
g͟X��؈�XG9[�IM-����2�q�Xܪ�}`�x�l`���ElĸJb�(&�Dv�6�Q��]!ۧ
eԋk;�;�6��?��El�ț�(G	��/�ᰆ8OI9޻5H-6"@��.EI�伊����[��7��/wbA�؈�XG9��t���.J%&7�c�'�tf�c�͈�`D
A#ͬmJοg��
las,�p�#\�wb b"6b��zF�
���+�-�Cg�U5���c~u�*��/�k�#R���`L"Ȕ����Bn��j�'l`+X�p�+��N�BL�F�/I�F;�	� _@�\�`���Ơ�����j�|�)���V0>�G���'������FJ'�q����Ij��R�4dԹ���2<:�b[�<�O8�
�p
�fb$Vӳq��O/�nz��$�@�u��S!_^�˭mv���E'At���`np���.P��Y\P��lMS��	�Y��S
�I�;�_��1��D�dd�AW�;c��܄�sp}��Է�T�������v�,(B@�%і�"~Рf.X�@\+����s��J幝6��A�P|��bo��	�S��iQ+_��/6J	Y�y̌��rĽ��U�eE�Z�6^E�,�[��L}� ��i?p�#\�wb|�l�ߒY�a��u�H���2��U]�驏'�t~⒐]NEk�6��q��Y?p�#\�wb b2��`$����70����!L�d�"P��]l�X#��p'b!�� b٦�ل
�un�	^��넬��~�0�<����;11=1酗gHJ2�zfU]^���b��Q��c&�>�
��I����1={�Xǻ3�3N0����Fc��*O��(<h׃0����'��8��v_δ1��������͓�b���*+�m~�S���֮m��W��囆���gs��yp��	�k��ї31TX������=�?�WP���Uj�|}�wǁ����j
Q�)p��<�T�]
=��r�$�
��	np�+��N�BLf����kɧ�}A��x�O=��
|��>r��l����r]B�=�wB�m|����'2S}x�np�+��N��}ܧ��c^l���Eb���cҋ)^(E�2�/�������/>�O���pռ��T!�o�̸�������R޿IOv^�K�W��Z��Z^�0�:�e_�ķp��p�{Ho~���-�]�)Y
����@HH�*��U���lN�ֲKsb�h'r�4�6��QȢ�.6��Mm_��7����ŹK7<�Yi�2K.)I�/��j�3�>�~�Y�;;������0�D�dѹ��k,���#�o8�/�}�F�X�축��z�R}��<T`���}�Rj� V�I�%����[�Ч��A��:�>�
��}������Q��g^j��m��u�|?�
����#��VXG�5ϴч�W��c[��o8�%��O�4���a!ǚ%�\b�M��퇌~��U*\눔�6��屁-lb[��'mpx���~���������3��ȭp�sN���_x1���iETh#�
ϴ��/�
��K<6���p���\��0=�\���7
�G�;/��� �'#=�,�t���L<�F����)�/�G��:���Z�
�	8�L�zdD�������	�L}F:�¦��yFY6)�[���)5
�3��:��[%�Y�fi�8u���6>�uڿ�>|+�p���Q\�A�i������7P�|���
��76a����Y��!IEND�B`�PK
!<}�\��5chrome/browser/content/browser/abouthome/snippet1.png�PNG


IHDR(2.���MIDATx�p�:��N|��=fffffffff����4�����G��Sy�7δj��ÿ��ږ��^�j{��5&��[ia�P��z�7��o$�ҿĩ6��nԸS89=��L
��1~%�@�ntJPC��y蜐'9'��q�Oq��TN!/��q�Я�q�*��ж�L�c�6Z�����V�"O�!C���g��b!F��V.��*�u��'��.�e���;b�,��T�}�WБ>��PIn�)��PI����`�wJr�J��֐�lg�d+��t�Pwl;�fC�'��_Z��c$TA����f���=�"��E��#�	$��q��Yl�T/tF��q
�L�����ɵ���b��z�z�ۿ1����)C1��
�x����W���q��m��(pg¥[d��J}��'�AE]��X�{_�
���k�>�l���w����*�٘��6�`�U��V�
O>l�Sm�g��~��Sl�"�m0��5 ��S��*Ke��Z������s��
۬�V^2����K�o8���x]ۨ���>�/����[	���_�8K�u���
;c��;m�
lE���"�,L����a
�i�yl�.�t�������+.�l��hOKe�s�&[A��q�|ϓ�ak�'>,J)Xj�7Ix
^z��z�PH;��2.<hۮ�O"�0�ûN�ݧ�
>��VhG��޹�d�=$��Fm��>��tn+����:~HL\����\Ԅ��/�M1�Q����5���/��zC�2ǽ0ڔ���Up�,��2V�r���*�Y4A��q��j��=�W�-����;�8��Ob��j*��K��U��:�=Җ�"0�:������`<����Bu I���.��p��HM�"�~N�mK/	�J'�_�_�0�>$��:�4!�(��V��z m�*��Rx��(�G]�T%��__����L��j���*��V��x�8%�ӽG��	��F����:���t[;�=����~�oj@�����_�qL��,��Nt����SEC��ϴ���9鲩0:G����O���9W�2iY�^n&�E����\nB�o>
�.~�c�Q���<��֠��>�Ks�y�YX6LR�pew��~��5=ʧЋ�	�_�s����}+�(7��x�f1(�εV�@�>�g!�aU)�.+�#㚻�5����P47 �-O�A���@�T�{HZt[�&�)��М��F�X�V���ʍ�k/r$8��B��Li(�$�H'�}��i2{�%�RI�+�K�ʫ0&9�h�͋���.6�Z��bNJ����\�`�T��F���IEND�B`�PK
!<��ʧ��8chrome/browser/content/browser/abouthome/[email protected]�PNG


IHDRPd���IDATx��#[�Ow831�Xcl�k۶m�v�ٶm۶��w�V�e�����ܞM���u��\Wx��6�ؖ��W�Xz�WZ�9D��D$��[BL�6��1OlZEHn�	��D�`� ���!"�o���	+��_L7�y�<�T!�@�_oT����YS�҉ �D�T���=�$��4&�-�h
�*<��S�E�TQ	�1ycf�@��U�lq��
��琒��&�	TK��� �+���R���(>Ϊ
�FH)�(�^X���g��8���;��!�X��&P\}�c)�w��)�M��j�
�@�X���S�>�#0+݂��ʹ�W����	����p,ʶ���h1�Wު^X���@��V��y6pRf9`u�
%�z�O��M��P�,�ÚM���v�G�+q�K��u8˭æ";4<�9�J�t��m`�C��NhUEO��A�I��B�
DӖ����Ů��r'�j̈́^��‰N6
3?�8���7k���
���yB��S�b�1�o��@�f��bѩ���+Π�a�T�(�
��]��U`����@���jL���+L�Ցp���z�U��AWY���yZJ�
j(E�QMJ�=\�Ah��Z-y�&
�&���A�,��o�o�Vc�'���R*�[�h%�UE�g+�A�B�j�I ]�:�y���P��$�}V����v�}h�7M��y�h?f�����w>���vUER`�� .-쬌D�K1�}�-J��ϼ����~_~�V,=y�Xq�6,!��7�ڻ�=��;�~1i��;�
�fxWr1�-(h8[�#�)\�0~��w��O���o��O���}�>�^{�yR�[��I«Ll�
�!`C�Eq���[�|
)���_�(|����oH�ڇ_���z��i@s���%nT$���c��|��N%�D�Q����sDK]h�`��Iѐ"B�E�8|�i�!L)2;�@�z�Z}*'D��y��lF �P�X��[K��0���AL�Ì��J�#��/kkc�e90<ي@qYx��L陏��H�����+G�$�A�yJpŪQ�L��nLj��cf�m5"@����Ƞt'���鯐{�s�h�	��
�K'�]�0�<
�2�,
�윊�O.����Rގ>��d,^�T.8ک\[y��zS9QO���ۯ�\�b&:}ۓ�M����1�)IQ`ErR�U�݉''B�V���u��ܝ���\��
�0��ѐ�-�Q��!��W\��0���cF�������|ߊL�/9�;���3�+�&(�?�\g
����i3�Y�<A��"�u�����-��3���wm��(�	e�p�O�;��٣�0�"���1�*��K!Bo*V�.]1�]��g�DŽ����FE>�}�c/�-�Jd�c�w"RĻ,�w.��Cp���T�3�'���o{g
$Is�jP�{�M9_�̌���3��<1��13�2����/&��w}}����^nANMl/�6�
%���
pi{�3��P<�4G(y1$:����{�".l
-�I��\n���@zE�	�o����u^���򧧞	���C��
o�Ru)[�`F��>H;�g|�pq"����X���?P1�	�{���C�Ԙ\1�O�<6�'`�l�㓦��<�?eE�|��0�n�D�W�!Đ3�+E_�E�Ѫ6O�A�J�>�ɱ+7�+����,z K�1bP7���ț�9@�p���r���(W�s����A�cƘ���WfƤ^&�{�uЦ��u�����Z��ra8�q�*��i�tƋr�Ӄ�j��9E���(P�8!��>+��r��,G˵t3�H���H��UZ��Һx&/s^��W�Ѻq�0�AL����^�eI�SF�Q���bF9Xi9Yn���*S��Anw3w	�%���_�T��!f#ӷ	;C
�|���b�-��t
qL˜�q�儤|<�=�Nw^]�J]�1s���QfΑ�#p���@��w!də:���-Z�Gd�;�ʈ��ui,�;[g�s9�����ہ���X��mHJ���̏���V�vX�kK�֖)

��)�r���k��ǁm<�Y$1�h�6f�G(���]6}Q�J��:��9
`�rRn�Q��*�D�e�W�|yΐxp�6
k[F`��Xh��V'v:4�p��W���˫�۠���w��+P�<�ib����!qF��Qv�4���5؛�uMͩ��
�m
��	݁��4�S9a6y:�z�S˄UF�fo���>�}yl��=T�!�y��%���JJ7�Ri�N?�����!8|5���{�&�
��$�Ě�71�Ę�gk2��J@eNh�O{]~���=w_��{K���6��Y��$��{��.ͥ9jۛ8��ƞ�
��	�k4Vv��Ae��9�[#���{��U��+8��\�K���J�4V��oo:��Q�RE��SԺ[6�p��Mc�/n�-{�N�:	Z����Q�;���?{T�h�O�#�E��5�����{+��Զ���}�DA�ΐ��]�"%��̯u}_�
^�} ���i-LaB�;	�Q�b��c0>�F$��G�k��җ��\:��p@;2w����A;�^���!�[%�ًo�����^���	7��X{y�Zw���2q ~ �B��Isea�����o��p�
P��s��I�\_�������cG�����|��s$h-�Q�l��'��i���p];n�@�C�kq��⠏�%S����*kT�Gٛw��޼t�F���)��J@A��(=3&Ȁ���J@A>�[:���LD�0��^�Q	�p|8䎦�z�J@��O�D�4v*�޼ͨ:����
���TVV������"+���o�=IEND�B`�PK
!<�e��5chrome/browser/content/browser/abouthome/snippet2.png�PNG


IHDR(2.����IDATx�x��އ���L�)��Uj�Y۶m��k�6zNNת�f�2Ƥ3I�֫�f]�[~�Ǿ�ߣ�;�į�����	e�>y�<˲V�����:A8>�fN�ޯ�z�-�Y���U	z{�b1;O=?�{^J4����ë4A#Ր�'�����_}�E.�8j`I���7��<�vG�^OIH�'h���t�c������9�]��Js�&V��t	��͵!I�Q��$�����4���Y�s�A܋���^~�����;xfJ���n��뱄@�`��~ע����P�9�ĥF:A��<�BX��vb䞰�!��_�Ǜq{�8{�����[��n�촛�9尜]lY��՞��z�u5�3�װ�}+rdzb���S��G�m�b�9h #�*p{Sk�
w�{[ѳ����uwllu�vI�4���X�ҊA���Ҋ���ӭ��eBV�GҌ9�b̜6F4�x�^'�
n
eƏ�{
��_63�z��7
�,0��{ar���K�&�s2���lܴ��+��!Gcqvd[N-�!��k��T�۴1�I�{�Sёc'K���S�bNo�p4ŀ�ūkVܯD��߷
/6�L�oQVz�%���ȹ�4��~>�t#MZ���+����pW�b/���-���T3B��.@�fY!Դ�Dܾ�h�>����MI���p���F4
��:�ߗ��ي�]�	�I�t$a�f�'Ӯ������[ͫg���tƊx�7��&p���*�FWp���uz:ut�he��B��<�/�^�ߝ����a���i��Hc�<xz;<�Z�M�t�Y.�8��}�٬�?��8�F߉G�R~{l-ǖ��|�1�����7}*���E�=^�!�|�T*voRKN&I�	4S%�(��U��g�t���;��(eLE�)̓:j0�A�3�fF"��ҔKt�JL3��
�H���k-�1��k�Œ�v�_:����h���Uʐ��dK���*�%�dP4x"M�B�%�#Q��UA�v�@x�����ÚO��X��H8[p�p9�䨹���!�d�l���
Tl*$+��g�$�?CK[�]XK٠q��k��/�n`����y9�_fU�0�Q��T��![d�UR�1L��#�k%��R�*(�dA*a�cN�.�#1Z��M�=ົ���%ߌ�.��KN��˂�?rǙ�
q�.����s]n��S��9���1d�P��m\�
�
����>t-�$+(��%��9=)Lr�|�}�%�I��2�Z��_.Y1�
��Wu��a;-�o�3i��c�Q�,��*䂫ϠO�p��5�ι�^���k��օߟ��zԇ$Y���24�t��/���
y�J֬��؜�_e�`�4�X_x
�.�S�����?y$g��x��Z��s���eb�����ݲGo���#G�w�0*s�h�v6��Y�BJ�v:�дn=^G#��вj�~߅A�����n��3��	%TN������-��C�^�z��m
JoG�ޓ;Z=,shd1|�"���0��u)��9L�6��K�<��'ۻv��^s?#�0T�q�$;Rds?��G�"�G��ˢљ&�����O���\�d�����ͬP�\��ݶd"zа�*~���x�l�E	GC�:����x�֛�]�>�?a
�'\�g	%�n�~S���d3�*Z�._MWm3RDBNFX���u�
�d���X�1��1���'����׫�m��[
I:@I��ä�	������#x�����::�.gݪU4;bhd�
�2����(6���%���U�bonzQ�gS��,�׀�>������ꞥ{ѝ	/�����N'�ib��(���-���[4M#��r�A�9��jZA.��&�|���g�4m�ol民�4lt��
1����y��t��3`t5��yg�D�J�nc��������lma����z�z�زlH��eY(�<�/nj�FpÇ���f�rrm�x��z�D��`%/�����ݜ8�?.���
$

�,pt�.܋}��2|P1��*��;�D8�#džt�
�K�(HE���4,�v��,��9Y~#($�6�$�f:�9��	̾�l�(��	���Ê5m̘>��.9�3@�~�8�٬�A8�"Kӷ0�����_E1>~,��,z<�,#K�ǴL������&���m"�x���̋�����ܗ��8�&�.�~�z/i �������>�4��}+1�V�Ĝz����t��QU�ͱ�'�iFE!$$I�j��d
����O�Ϣl��x��:�,_�J�{MD��#��U8?�����X����8vd].7)] �[CL�2����7񐟘��f!6��v�)̱q�������+��'_^��6�i�Z��K��f��*Q�!\�B�[q�  i�lw��)*�T�>G��"I�m 	A<���s�Q���Et�vG�`E+��8v�&Td�y�Ȳ�bK�:�c���k�eZ�U���ik"���(v6Go�\;����9e���Z��΢X$�H��=9�(x#�&Hb'�CӰ��_�C��E��BV�̏m�Ȥ���9w�\4s ��Z&���`W!�[4�uz�BI�b�e�&�y%���8��N��$����or���'��(b��T	dI�)��Э�2)"~HEm&Y9v�����n7�4�o?*+2�P�h4�f��#	E|oڰ)q�b�+E�_G���/����!����H��v����"V�)}�!,@�E�T-d!P	OXc�3A,mbS����c�iZd�d��%��3���Al�d�I`�&�6̈́Fg���Y����`�f�ܖ$�D~Q���
ҳɟ��%IB�����q�������	!�KA��l�	Z���n��k#�@��5쪒iv�&mvhv�P���Nf�]郝�0��5�i@E�bƌݍ�l[fP���It�r�@	��9.���bk�S:�9*3f�NvVFrG_l��*��q�P�
��5Q-m"���xL�d;�*�[�m��*�߿�e������o_6���1����'�T/o��c+	w��9�����zYl���~��ث[�ϱ:����f�	�LB���_��@ӯi�s%��/�g����At�1ôIEND�B`�PK
!<i�	��)�)8chrome/browser/content/browser/abouthome/[email protected]�PNG


IHDRPd��)�IDATx���彿�2>�.�F7J��xpw�Sj��������C]�	��J�uVfv��y��s�?�l����w%�;��O�y+��������?p���b���M���z���ﱲ�wS��~���.��or��[_?���5C���d���a*�������ص�<y�[_'�C���{�{���u�>�W>�)��v򺠬�S+�|r��������P���&f�^��P��������n���p8@׍[����s��*	%�g�d�1G3k�,��������O����*9dI:�-��%?�����kZ��̹�x<^r���SS[B6c���IB:�
��J|9��s&��|��.����9��(*�jȧ��ba@ �p�<�<tD4��1ty4�)?�dr���|�$�E��Ų����SY�@.�fbt�t2��:��LC�?/K\z�{{����.)�տg�r����p�����áǷm���|���sdU�Fe]��X���P�E��i +
��a
���{����U�j�i���L.��P�r_	e%UD"���w�O��a�n�t���Қ�$�
0��F<AV �*�t�-�t���l�ŏj������4G�n*�T�t<@<:A44L��`�����O~w�חq����B�KM!!�NbSA����e��`��V��D��]��ddz�Yrs6���W���)�(6aC�4(��2#���)�̟��i_�0d��ٗ-[��YKV21�{�~�;Hg2H��n��I��3}�
`^��o莍nwZ^[��y/�HUݬ[Aڍ��L������AC�����o��3�LS���������y�<v�7����}��Z��X������)l���7�:��k�)���o~�/|���
�[�A.6���j�oF	�����^*�B �pii�.CXG1
���V̉�S��ģQ�S�(�8��&�:k�mE7�XӲ�$�g�#:����㋮t&�P����`hδ�׏��C��G�ۘk�� K��m���W��e��.��Ex��Q,��!M�R5�e3��ds��tͱb���_�=�����܇��Y�v.�N���B6���&^#[�rՆDF�����^��ŴT�e+UOE���(�3�����D����.^�����H�a7!,$4�"�ͳy��4��0�ׯY���ϡy�&���R����)�ajj��Z݀$q�s���D�}�
��c����G�	E�����@EV�6�D<�������9���[˗��j��g�vϾ_XB`	v)��Ne}��iȪ峕օ�1�z,����$L�.'��Fp*J ���ɦcg�㿤��+�mwݍ����+��0����?qqf�
$K���a�31��0�g����Ģ�l>�U$�G��,.�d]ױ!"c!��1߹t�
��ao�l��,-5I&"�ɒ�k��ٜ�aX�W����v�aY=������h��Ս���{lk��5��(�dQ,Yq�3�XdI��a���TUK����5��<c�\�4����dLK��r���H`׍r&�S)���sm���Dn��B��N<놺�9�M6!<��h��h4Dh*F*���W�T"{���C�·�B~Vrj���=#x��5>�|.M%���G�tK��
/A4
D�)jgo��?Ά��Ec�����ۓW����R�<#՜9�k�2ul��;ﺧb�Ƀ�o,;��^i��n<3�y���c/~c��֯Y�%?I6�F<x���Nzz�m��l$�$�߮�}�sL��'T�wn&� 65N6�D��H%R���aĄeL�#;�l���"������)y2٤=�Z��4���M�hh9��^#��g�φTw�5��넙��-n-ҁ��`�&.��"0�8�p9@�)J���Դ0N�70b�>YF�埅��wp0�xQ�)���S�aZ�S�t�\6����T,&?v���(W�<���� ɪݜ&F!�ah��_��,�@�>��D"z�_;�4^��u�&�l1�����2����X��ĭ�RM7d<t�t�O05�IU�����N9����:��$�i�}�m��z����fŇd����
	浪m#�1LW�
���öYs�r��~/�z�Ke��Tm`X�u�e�`���N%4͘GY����}Vv�N(�Cu�#�F.
�U��b*�N4���w.�X���+��I7�_�;:w<����w^t6�[�)h��/q�fְ��;�zt��/eŪ���� �UVei��ú��r�%�I��$I���؞����^��3�|��AEY)M5�����s7��K��
 ;��݊�|��r�+���Z�H��Y��j�j�gԳ
W�,��*(u���u奼P�1�z�6�:S�����S��m���+�ξ�aU�;*;�,Fr�Q�n�(�j̨��$�%�[�Rw�aߍYr�9�$In.�$)h����i�W�RU��%'uR�L���9���K�>��o+���Tw�+�e)$!3���ؓ�E��u̮Q�+W���3���*���0Ʋ���򁻸�7��j
z��R/��U�d��5�+&)2��PN�L�mkC�4,\��O�e��s��74�>�F`�@|�1cZ�UAd�L�u��319N 0�,tTo5�M��
NM�ȲLS��Ѧ:Т}(�(��xWR]� �94�6]��2TV�RSU������O���w����ӥ���N��O~@�߯�U����q�
|�2B��c�8�>�k�j�п|C���Ϊol�j���V�0or�*Z6JO�	>9
��$RIr�2����dl��C`$��M�"�E5b�i%��U��aQ�uR�u�t�Ȳ�$��L|^eX�	ܥe4]�s��C��v�~7�0?1�y��2�c9�[�e/60S0�Ӊi�(��7���D,ӸR7��I-��� �8.�>/�Ȕ=Z��ף����D�Sd�wS�L!L
��[_�چ2�ՕsTM	k�*h.�`"Pd�)������##�Z�C�s!�oيV0V~��Uw�gU*�zj��Ҷ/k�Nj�۾7��V�0���P��46�B���.;ݔ�W�*�s��l�������}8d��	�\8��,���-�duòЋ%+J�T$,|.���%q-��Ѩ���>�>�Z7��;3�O��\n/X0��}������<b�5�B�>2�
t�O�Q�!��>-M�Ӏ�%���y���=n��b�����rI"���s��i��.U�!���ڐq����(J���+��CU�
�e�$#�PH��}��R� 46���C����j[�x��g�Fw=���|���1F����e��-0�>؆�ðL�f�28��d�[&�	d�TxU{d�b�b�d
&�"#� ��d��Pq(��&�_H*kҸ�T@'��}�{��+�"�ȁ�q7N9u傥ǭ�{`���}Cc����7N��[��3\<eM���U6)��B‰e�9z����u�Ȳd�i&/[(lUUE��$�
�W���5�d�A�����+cjl�>+v�%�r�/�G�����z|�o�n^<�R�#x��.^���ޮN^ܹ��w�cO�� �`�/�Nf3����pYث���ǶP/��&��%q�d�,I6D=������.�'=�����*AQ����GI�[/w�;_Q'����O���L>�o�F��{y'_���|���S�*��.������l<�t2y=����SR1���QL��C�S�$���!jz�2�H�d�;h�lm��H�R��n��a���o
Uwv��f�)���O>���G�Ý�{6�u21�%2���w=wU@��n���GW�p����~�U��t��/��_^}��LgI��lش���
���/�]���c~�_q�;Ϥ���D2��0t�g��x���JN#��\K��͸�0���C:�ؘ7$j�%.ZW
L���f~U�Vѻ����|�,;��4�YK�Oc��!t�!J
]n��`�(��usT��'��8,�p��㫫˷�j������7\}�y����~t�\��h$���������ā�Q��/��Igٰ���z>��㥼z{�c��J�L-��eѵZ!�n��:Q,^�h7ab��K<4U���l�LS^��@{���f6�Ȝ�O�ڃ�ς93�K
<�;ɾ�۩��e��@E{�����'�wެN;@��yE��&�g����J~|���|�"P}4.9���e-�nG�n�#?��o}�`0�w�E�3ɢ��L����1�k���
Rn&02	�|�а,��2N !l`/��9
��g]$�Ln��q��Z����/����s�b�d��F�m�E��s�����\w�mX��tF��w��l6��i��~vԒ9k�,]L]c�{��5�Y�n5��Č��Jy�=��轏��N$�a��64]GQ=D��L
���8�c�Q{@�ձ%`I�P��
v�6T�40
Q���Ɔ��\UB*��Bl�dۖ>�)�֣��2�BY�#��M5�;4��^�ͪ��qW=��?3g�����g��`��ѕO�����r�!H$u���x�'���a�x�*�T�\lIVp8d�����K:a�,]����pp�E5!T3F`<L���H���aT�	Y�	�iڧo�^@/�TF�R�<~5��K<@��6
:L�|���Z%%dSc���#��=�[������f�w�T�,=�k�d��ڏ�� �H6X��7	��������}�u�������1���1dY�4
�TU��˩N7�L�}�mt�fɈfV�;IrSU����ǓJf�M������D�d*CiI9�u����
Y�LA΀|��O�
�,-�^��p�ճ
e$��IN��Ie��	"8�N���%F�!&�dFs5�$iJ˽�8r�Է�6�&4:fO�KJ|t����/��s���)����~0���|�K�����Uue$�9"�9s���)��y.w=ݲ;�d4@�P�����w~���}�E�7�r;X�b9���x�!y�B���0�cA�n����;{HT"��.;�̓����
���5B�,�h���e~ Klx�N^w0�dh$�n��itS�PWIMm=���ԃs �ljǭ�:x*��9e*�d���t�s���=z�����?��o`��{-C�D�iA�Cإ�P����Q0�D}�<�%S�3�2�"� ��~�������EP��$j��9���5����o������P�p$F^��;<��"�<��$�Е4Lla���5�.��I1��$��r̢R�j�M�%>�KV�2L��r�,<N������[5�(�`H���X2����v�TVU�/�dߞ^v�8@�?�h���n�{�1�D,Z�p�R.��.��$2&}�IRY
�ہ���_��WJ.�B�,:;�p׮*Z�@!� x���G���w�n��v�n�����Z�|���}�㜸�Ç6�Ph{�g~s#�X�5'���g}���}'+�Z�ɭ޹8ϧ�e�ܘ���Fw�Lk�r@V��i'����Rr��h���a��A�����,�n�-�Q��R]S��(��_����:�=�D���0�����o��EQ�E�h��|�\���j`OB��L���S����r	�y��0
��x�3�"��~�e�˟9f�JN9w3��:�P8AG�.Y�c'�r\}���ly���']��?�����@rW����П�޻�|r��8R���g� >�"o��i*�i���8�lk��-��'�aY::2�Lc�AB�x�t*O8)ȅ�U�eo�(�����S63���G}���q*+ˑe��/�6{�k<��Υ��������%��+�ܻNcr*��!RY�H$F:��`�}���IϮ{�:���=<�x[��1FG�9����t��[�����T]��_J��-��y�c��/8�s�}!ȕ�Ɍ�y��M
	�d<��#p{D㥸�j��&���#�3QF�'�\'�{�H�3���1D}f;CA���8��(#Ū׶cd�2
�Ba���g�ێ$ɔ�����z�������z^���]�m����n롳w��� ɴn��%�8T-B립��P�a�
�_O���ct������u�Ʌ���d��m�7.g�1-D��~���3Ժb��Z���u�b�L~���k��B��ΟK]c���u�0� 8ƙ7Ȇt�~l7�}�W�JT�d�F��ƣ���g����$1���PVV��*!�cO�_
�m߯d�]�JE!��2::ʚ�K��ɢy�x~g'{;i��b�Hj��hg���ZM̘U���eź5$3���D���<������b�m��N���(�ty-yÇ��%=�(fj�㹩z~��p+��:B�x�A��m{���Kb��V�̟�Ho�|P'2�u�[���A�̤`��8�G��o,¼�
%�H�;����}�(�r�:�?E�߹p��f�y�^=��B��D�Nٰ�c/'�xfg]=���^�+��`'ͭ���z�O8���F{)gX��an����,d��>��8�i��2k~N���]����!r�O����2��v����̒KV������b��$�%+��t� ,ɘY����ӃF��p�6��/�p\}��P���z��p���X����P��%.�
D�e2�<�x��s�y�i��:k	����8@[G��x�����k��@;�H����J��u�Qtv01�0u��B��81-e�,�
�1�s����'Nh�UU44USVYJEE	����"6���C6����O���9`Aw��ӽq:{;��r�U��w?B}e�kf��>����3���d��5������躗v=��y���DI�.��G�O������yaoѴL$�#����)+�apd�Dt�������#��y�T��p�lj$�C�H%�LF�����̙M���r��,�+����y�y��8�[�D6�EQ!U�V���&a �`I}#�F���?�x���2TU��{Cx���_��;�7��l^#��ra��Ŝ�:�w�����m�h`����SW�r�n�72ٜ}��p���I� ��Esc=��:L_�)�!�H�ۅ�p!	Yu�+���Ig2��e��E\��3�S�<��NLd�:a���<bx�?"���Y^�3����%���s����#�I�ۿnaמ}�O�b�K\l}��E*�#O3<`^�|.��4�l!��*K�j((E0$tA������B�@7%���r�U-�S:�D�W������>w����b�����׽^�$m���'������m�bBl��Cc���(��\z���f�JQ�����n��q�,�T���'�q��zξ�D{�
��ky,ab�)���1�����IL�"�ʒ�t
Z����*����I��`6���?u
�])y�bY��R��uo�B���18�i�,�624�倫��^n��ǘ�\�O���}�2�B	&'"����s��q�w��<I&�E�r���j�Q�Ì�4&�Y���,�L-��p���[eh2�c�=ēϷ����'��߾��m}x�~��
�#�9�:�"����tE�eb�,���w�2���}�z��t�P�>Z��帓�2�o'��A��I6Q`ph��pY�L���.�A�LMF�
�죃ɤB>fm�nR�}Ó��ᡧ�8z�<n���,��y���?Z��༎C�'a��֙���k,<�}_B���F�X�Ə?�n��G�=Ñ,��A����	���g�Yx�%����g_���=��P�c̋���� Y�FS�Mh�I��Ŝ*���gp�HZ���8��(�:��֏����Pmx�J�.lx��ҝ�O����9�E'~`ע?8Wq�@����oL�s�&9}�R���'Y�r	�>7�#�0J2����pJF���s�����m��{�M��/2/x=���1&�P1�s�n�kgǁ{�Gii��'�\��&���&���}�l�x��@Bڦ(r���y�k��,9���)�s�eY('�b�T�b��h�l�/^µ�}���*vtD���{$do�6�נ�n��igd"�fy0�K 3���̉����_��MwP��q>g�YŦ�5��w��{��3=���M�l.�՟��7���#��<F6�`�}�i���{�6�S9{s��5����q���h��m�X��)�X�;�{Ci���^{��
]�f$l��i��5��Q+�4�L�t���x�^�j��lx��u0M�Y���7
]�WT��n�Alr�Ϗ��0-6J�.�[���'��w���yxz���m+�o�fOo��ox����	���ct,Ƌmbi���ײaFc=���p{��u5;��D�X& � �
˴�����m�Cd����=6;��{�4ؘ�26>�qG��� ����G�W��y����󧇻���ɇO_�l������n��x8��%P��NKd
��!�7u*'!,A>��W^�����cɡ�;H�C�}%H�2-6Z�%����[���/���R?I %�@R���҅+�c��P�.Tuz�:�
HNC0	ˆyH붅��a�&
s[���%4�E���6��l��J +2�D��X�����o�<�w�����֙�����'ϡ���h*�{:��6�iM0�3 ˠʀ8D�v$���rY�Ʀ��0rI��w��E�ƶ񐗂���QE���?�o~�D�v�r~���^:J�G�N�:��:�j�9$��r:����p;KB�(h $�ZfSZZ�d����t��gڠ��Ry&#IV5�9}a9��Q��)\n7�8���<N�Tޢk�`<.l�
��{?P��|_y
3g�%�E��N&m�c��r9��S�w��7:eY���iY��2��a�^Z�4��_|��(���x=&�:�@Q8\�C
].'��a
��@"�L�|W��:���Tޤ#�� �V%�b�H�MV�
�m���#1��N6���;�%YB�uB�0�a��0tY��'�p9eYbx����<��6G�i]/ )/-��?��@��!�F��|}���r�ĉG���E"�Α��P�M��%�:�9��@��P	p;�#�,�%�y5�U�bS�+�g�o�o�װ��0�n"+��t�N<�9x�
��rH���T����a��,�8�_��
$Y-�؀�!31�K(0��:Q�׵�>NgOEl�,�BB��n,�A+���k�,�q�d��9�'5���_���~�N!⠍%Tm��Ì�v���py<�j��v���]j��e���f�yR�
���r�8�Ѱ��,��a�,���r�7
p?�	������55�v)����Q�b�M�Q�>b�d�4�'3H@z%x�:�K!�t����"/[7M�@���K.��i����eE�j�%���l#mt:Ģ	ⱸ}�hY�4o�0K^3�����uSY�
&�&u<N��Y�����w�z����_+�hF�"��P�m�on��r064Hplܾ��:T\N'��!
Z�H�9EV��$S9�P�:�J^3i��;�FlӧѺ��w�yg���n�$�� ��O�-;�s������˫*hh�#��3�ۏ^0�S��`�ǃiZ���$a
�>�r�*.��h(͞����۩���tY��������!������1�5-6j��~��Ҍ��dbt���L]��uc�*�CU��@0A�p��(�%�T$iچX
��l�u�prڦ1G_���Uo��j_�m�>*(t�j����A���t����x����a�t
��g�;��p�cݣ@�#����y��w}�*��
�N�!��!�2��кx��l�ރ�'�mu!�T�����K������p�O��\�㡵���:d%ɶ1���-���Zi��l����jǫ0}��fn�	��_�Y���\���E�|�y��X�jq�]78L1��<�9���Կm)��W�n��U+�o���h&�L���JV�=�T
�iΓ@ӳ��ێ����Ky����c7_���{��e1]��Ep'+x�m&l��5�?r�"?�헖��Ӑ$��g�·�d����,�92�駺C��LM�B�,�B���,д�+x���O��A�X
��/�$�G��7?�11pD<�Ś\���`���Ʈ�_�`>�7��@/�'��g������>�І��X��O��lf�m���G
i��:����?�8��/ۆ���=[ӆ��X���x�����W�8�
�K�Z`�Zw����}v�������b�^o���ܶ���� ?l�1�9߻�ў�7=�;��ׇ���K�nF�?
!���|�����9���׽���1�'���a�?�}���IEND�B`�PK
!<R�o�

1chrome/browser/content/browser/abouthome/sync.png�PNG


IHDR  szz��IDATx�Vx�K�}�m۶�ض��m;��g۶qmF�Vm��g�f�/˗�;If����)t���s��I7��Z�H\�I��F�~�^����?����ñ�]�E���x�)�1�>�|�#q���k��FCU*�
����7��DzU(,�[����rz(&��q�	�0����1��k���8f��!g� �ۆ���4�:�!��;��=�T�u�|����TZYTZі��¥�E�>2S�8��Rq���Lp��z6,<�O��պ��o~�#�#X��r9IM>�B��(�A�e柔�-��m8�X3��c�>":��_�].ۡs�2�ar����B�uZz,>�������c��G��_���`D'a�5�ƥ�6o��s�^���H�`�3��N*���>%�G��eR#G�����a�u���:�sџ�ɸ��ZE&���Ä\'���`T�#��="���r�6�5r|�h [c�Z�=),��
6^��:�����S��e��e)I�!R;�(��f��l���ڱ�h\|���f�&_9����/�:W�n�j󷿀�5���o~�3�1&�T��{S?U��Z 
q΃d��
����0S���o;5v
�LD1Ns7v�Y�u�s���0I?o�b��i��$X~D�n��R��p����Ĝ���Vf��\\�&'Y���u��_52zW��;�3G���oD<Gv"�g�G%��r�6d��y�A~��T��u�7<�O�}���i��q6_��@B\;�Y"���j��r^A �.���ED�M���f�೯�+��|�@�<'�c~�K�N���;��~����/�W��S��mP��< ��:�z��Tj�nm�S��^����JR���X��~�\*6�˥Ɲ��	��0�QYW��7k�¼u��'���L;��L,0j��ϥ�����W���Cq�dgç`X���3z;���+2U��dR޽�*�����j�
j+,��s�Nm�%ʍ�W5<&���K�y��L����>���S
���@ <H�w�*�[zD�xHl��4�RA<)�Mx���bBRŗ��D�G�����n�t���K��_�����l��b����w=�d��y<��D�ʄ'�I�%"8~n�>�S���F\�[~�=��v����9�d��>����g�xt�g��a���似�*%��n�~=?b]�?�D��@g ��x�4�9��R��n�d��R�bJί���=ί����Ǿ���oE:q�SV��@�q�
�j컟{�͌ɆƁϾ��0ވH@E�GGBV�2�>�t
jD�h�fRZa��@z�km�OpO��\g�|�o�M>2�;��ٌtl���/�`�Q�q��� X��b�s���+����l�z+�����2���P�u�z�Ņ��[!'��d���P��#�,΁���oƽO˻�%��:�عsdz/��J�?sbbNńH�IH>)��u���W���Cfr���/�@�ƾ�Fm]�·���,.�#x:~S&1�E���0yǾ��"��H����\�[���z0*�9n3T3!:�"(D��VOhg��OR.��:-E�̽���+�@������[(x��V���C���c�����^��~6{[>�7�+�}O�q����G
%�{����g�ϸ�q'�1F�>���2��/�fb7��IEND�B`�PK
!<;T8gg4chrome/browser/content/browser/abouthome/[email protected]�PNG


IHDR@@�iq�.IDATx�p\��0?ff3����̌����Xhv,33333�ٲ5�U�6O��+���cI;s{�{N�������7�W�w��0M��2ߵv���������i���6[�w�٫{Ts�5�L.N��d�7�!�^�����q�5�]�b���+�8�F'�h��m\b��c�1��Cx��v�5�{�c��b�R�����	aS��1u����20�34�Gg���\�Ql�+LJ^�I/�3�
&���,/m�w>c�$d����"��c|�Ҍ[p�q�4\�̥^F�����f.��h�s�K�ÍOD��J+4�5&��JSX�aJW�򖵦�u��i���vt��Ξo��csy���ݬ(k6�9�&Dq�3����:�F�"�ӏ��Ƙc�:thQ熫7jG7�2�2����4��z���M��y-�4��e�Z��E‰�b�l��{��ymƀX�Q����t�τ�B�ꞛn�\��]�Ӱ�o�@���U�f�5�l����6�'+�r�2��i�+,�,�%\6�	.G�	\�N��,��T<W\�~��Y¢�e��W(4v^u�����Tr�к�
��[er+W����|��`��i�b��I�v"���eޯx�$��0a�q L*��2��m�ݷ/J���x�En�&8>���:B��i��(�7�q�a�����9�ߖ�S��6S΢3�H\(1Q@}!���~��<���b�g*�[��W��+�؆ɔ��L-0��Qf����K���S�:��
M2I*q�ڭ��
(4�l�X���̕��o������R�6��
��j�3��A�����T�F�����#e��s���yG����Z1k����\����nݾc'�z��k���u0I1_�5G[�
	�R��g����n9ᄉ���>�b<
1��1�V���w�:J���r�]4I�Y��=2ag܊24���8!�J��!	Jx�2~��'�{&
)ۭصv�e���p����E��]�cH�����D�`�UT�I��v;�L]��6�ɾ���P��i��v����k�Jӱ����o�=���M��~��|�Y{�e~��JHb6g����_.�OY��~Xr.���p��3dhv������({�v�yk�m�S);�7"�+&�hwVY3�p2x�^'�_$��D��,u.��’PP��{v�{,hb��������?�|.Gl�we�VS׺�N�0���;vŬ�i�Bd'U�T2*���8!�Gs�T^�ZN��b�e��N�y�b��c3KL��*"О�K��\��[I}�'�<�|��5��I(DF/f�uB|�g�d�	�e�q�傿��_%ֶ�{�3l@�Ŧ�ŒqXCwI2�������ڛU���\Ȗ�(���-�yU��tu�s�����P�P	���)o~Oc��r�8^���U���Z:
p�A0	�����8sӋ�[PUa��"��b2�eH'� h�E�W��P.���P����Ai�U��0���VH��~�i_��Mݷ�v�0y��rw�c?Y�BL�9�Bb$0��'��F[��+���~�kvs,ጳs�����@�wt�,�Kr��M�����
j�x慗��$z6r7^s��0���
���8I4���[�Cb��AMO�J�(�J�h6"�?����m�<�}~��og&�VL/�yz��8�g!D9�AJ(�u,=c6�$d�;��>hwHb�F�U�����S�+�m���?B
�ufW6u=Pݲ����q�I�c��mq�eM;
�SH��1��؄m˼#���=NW�;�9J���_Y�M��z��t)w�ʟ�ҫod\s�Y����z�ua샅V���3�빿<([O��3��B �&ZA���+M˪M7R��c�Q^�h�8a����:%n/�i��1F6a���]��K�W2��`�G��z�s��O?��R�٩��/���ܿjW���{�R�{,��6l\��3D�gHBoRN��ui2!�aD0Ng���/khT�\p�o��07�q_a�J*%�b�H����uh9�J}�����G�X��7]{s�z2��n�>%9���`0�˰s80F��X/[_",(.�#v���	�C���ۍ7Jj�����&+-��c�el�(z3�l��b�0�m��0��Á1�PWS�*���D�^X�*�zۣ7�#8~Ş!�B��P�	t	�>�p5��_���8y2�V��U<�U֤:�E;�N�؝#P	�sI�I�	a��7i���M�뺤���T�v6F��"("5��^�Q��p`�9+�u� yy�����J1���Ƙsy��c����­�O����p`�9x=,!���4~�R�cc3���=z�raclz��C��]��Ĥ�+���FF/*6Y��g*������
���//~s�����G�����H�@���8�zM��;��F��s�Ʀ�q#�qJ7��:�@TTݲ1M�����DWϡ�O���s>�;����rt�Ə�3�q�/[�ص�/%I��VZ�4"X�PI�0]��=⯏��-ts��6��]0�Fs�8�c]O�����e�E�tW�CCǨ�I��Q�D
�dֲ?�g�sOZ��ʙ�a����$�8|�����uzA�dT+�ʼ���d�|�ә��B�N;_ڔx�C�17X!�mj�ޔ�����s��t�<Q��x�À���zk��4X�⥳N�f/���+19jk}��9��=��Ci����90��Ծ������+��%d����1�5�`�aABV�q�W��D�c�%�˱�݃7/��[]�������2c'��}{t�ل�Ͼ�J�h���d��?O�����]��#K<B�v��p� ��x�&�H�M�dL��g,����s+�V��~]�A6t0W��z�^P�6o�Fs��7�vOM�.
r`�v�C���x�<�\NQS��5��	v���F�"\V�G����y��_?��=�ݮ1�^��8T0��g�R�L��z6�͕&��,�y7�l��'�k�ZS}�/�\1��g-�Ўz~���M)S���F�M���J���˼y���cQ��d��?��;��s��]�?R����a���fn��@{ʻy1��~�q�(���[���[ ea^���ez^�o_$g��'�D�\�'7�� ��`�Avq��ȶ��hXVP�$"A�sQ*F�$��FIn@!���3�&߸����GXR��/N$��j�[��&d�p�F�q�=`.�p$���*��M��;�}���S��P ���]�re�F��7��������/>@{��|�`!�c��޵s��z��U���r��M�����MW1� o�a:���V�\��o�B��n����03�x�
P�c�-�Y����^0�
%�QP���{��MA�l꼇�UڈA(j�R�a>�QU���n��%6
Ϳ���~}Ėa�B��>A������E����}�#�J�_�JP��b��f̢`�PX�v���?�����(j�;b��2~���[ڴ��p�Dn^/����*���WɨF��
	[�_��%�kLRv�>2%�B�ݟE���d�[,9��Zk�f_�~�ܴ����WA'|�,,��Č��q9�m���!�K�݃t�vAU��� �)�īV���y�.z[�<��@������#u�~"F|���	9�
��(���H4�9JX��$U��?�LyD8Ò�ƦA��S$�o�<�YB�
5:ɽ��tg���w����>��)]�)�,J��+T9�>��˹� �����5` 41Yx֖��M�>��+9����zs���Ezo��5)�l������7���{�켭��Zz��OD2sQ��8�pk��p_���A���[%L�	ZX��DLz�`
�Z۱sW��|l2>q���_p�I�	+��ې#%�d_��*X�h�����]�;�}�j�57w�`�;���u:���rXb6�
�bc ��	k.�Yd�o�Q�t��О�f,���t�i�W�������j�y�=^��$��=����A�Y���|�|�XN$���;��rxگ��uo�%'�x���J��H�������Z�t�)d�L˫��u�d���S$���`���sƙG�S�(1���;՜�QV�`�ڞ�.W�E�>v�{ts�0�
[��
�O=�Rލ��۰���5��7]�ؾ�ƪ殻jW����u�m���q�>g��[�n���'�>��D���2�<��%G�	S8{p�<Pz��:�,dO�ݝ$�� �P���A�\ \&\,\$�/�38�{�gG����`x�=�IEND�B`�PK
!<��p;-$-$0chrome/browser/content/browser/blockedSite.xhtml<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE html [
  <!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
  %htmlDTD;
  <!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
  %globalDTD;
  <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
  %brandDTD;
  <!ENTITY % blockedSiteDTD SYSTEM "chrome://browser/locale/safebrowsing/phishing-afterload-warning-message.dtd">
  %blockedSiteDTD;
]>

<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<html xmlns="http://www.w3.org/1999/xhtml" class="blacklist">
  <head>
    <link rel="stylesheet" href="chrome://browser/skin/blockedSite.css" type="text/css" media="all" />
    <link rel="icon" type="image/png" id="favicon" href="chrome://global/skin/icons/blacklist_favicon.png"/>

    <script type="application/javascript"><![CDATA[
      // Error url MUST be formatted like this:
      //   about:blocked?e=error_code&u=url(&o=1)?
      //     (o=1 when user overrides are allowed)

      // Note that this file uses document.documentURI to get
      // the URL (with the format from above). This is because
      // document.location.href gets the current URI off the docshell,
      // which is the URL displayed in the location bar, i.e.
      // the URI that the user attempted to load.

      function getErrorCode() {
        var url = document.documentURI;
        var error = url.search(/e\=/);
        var duffUrl = url.search(/\&u\=/);
        return decodeURIComponent(url.slice(error + 2, duffUrl));
      }

      function getURL() {
        var url = document.documentURI;
        var match = url.match(/&u=([^&]+)&/);

        // match == null if not found; if so, return an empty string
        // instead of what would turn out to be portions of the URI
        if (!match)
          return "";

        url = decodeURIComponent(match[1]);

        // If this is a view-source page, then get then real URI of the page
        if (url.startsWith("view-source:"))
          url = url.slice(12);
        return url;
      }

      /**
       * Check whether this warning page is overridable or not, in which case
       * the "ignore the risk" suggestion in the error description
       * should not be shown.
       */
      function getOverride() {
        var url = document.documentURI;
        var match = url.match(/&o=1&/);
        return !!match;
      }

      /**
       * Attempt to get the hostname via document.location.  Fail back
       * to getURL so that we always return something meaningful.
       */
      function getHostString() {
        try {
          return document.location.hostname;
        } catch (e) {
          return getURL();
        }
      }

      function onClickSeeDetails() {
        let details = document.getElementById("errorDescriptionContainer");
        if (details.hidden) {
          details.removeAttribute("hidden");
        } else {
          details.setAttribute("hidden", "true");
        }
      }

      function initPage() {
        var error = "";
        switch (getErrorCode()) {
          case "malwareBlocked" :
            error = "malware";
            break;
          case "deceptiveBlocked" :
            error = "phishing";
            break;
          case "unwantedBlocked" :
            error = "unwanted";
            break;
          case "harmfulBlocked" :
            error = "harmful";
            break;
          default:
            return;
        }

        var el;

        if (error !== "malware") {
          el = document.getElementById("errorTitleText_malware");
          el.remove();
          el = document.getElementById("errorShortDescText_malware");
          el.remove();
          el = document.getElementById("errorLongDesc_malware");
          el.remove();
        }

        if (error !== "phishing") {
          el = document.getElementById("errorTitleText_phishing");
          el.remove();
          el = document.getElementById("errorShortDescText_phishing");
          el.remove();
          el = document.getElementById("errorLongDesc_phishing");
          el.remove();
        }

        if (error !== "unwanted") {
          el = document.getElementById("errorTitleText_unwanted");
          el.remove();
          el = document.getElementById("errorShortDescText_unwanted");
          el.remove();
          el = document.getElementById("errorLongDesc_unwanted");
          el.remove();
        }

        if (error !== "harmful") {
          el = document.getElementById("errorTitleText_harmful");
          el.remove();
          el = document.getElementById("errorShortDescText_harmful");
          el.remove();
          el = document.getElementById("errorLongDesc_harmful");
          el.remove();
        }

        // Decide which version of the string should be visible in the error description.
        if (getOverride()) {
          document.getElementById(error + "_error_desc_no_override").remove();
        } else {
          document.getElementById(error + "_error_desc_override").remove();
        }

        // Set sitename in error details.
        let sitenameElem = document.getElementById(error + "_sitename");
        sitenameElem.setAttribute("class", "sitename");
        sitenameElem.textContent = getHostString();

        document.title = document.getElementById("errorTitleText_" + error).textContent;

        // Inform the test harness that we're done loading the page.
        var event = new CustomEvent("AboutBlockedLoaded",
          {
            bubbles: true,
            detail: {
              url: this.getURL(),
              err: error
            }
          });
        document.dispatchEvent(event);
      }
    ]]></script>
  </head>

  <body dir="&locale.dir;">
    <div id="errorPageContainer" class="container">

      <!-- Error Title -->
      <div id="errorTitle" class="title">
        <h1 class="title-text" id="errorTitleText_phishing">&safeb.blocked.phishingPage.title3;</h1>
        <h1 class="title-text" id="errorTitleText_malware">&safeb.blocked.malwarePage.title2;</h1>
        <h1 class="title-text" id="errorTitleText_unwanted">&safeb.blocked.unwantedPage.title2;</h1>
        <h1 class="title-text" id="errorTitleText_harmful">&safeb.blocked.harmfulPage.title;</h1>
      </div>

      <div id="errorLongContent">

        <!-- Short Description -->
        <div id="errorShortDesc">
          <p id="errorShortDescText_phishing">&safeb.blocked.phishingPage.shortDesc3;</p>
          <p id="errorShortDescText_malware">&safeb.blocked.malwarePage.shortDesc2;</p>
          <p id="errorShortDescText_unwanted">&safeb.blocked.unwantedPage.shortDesc2;</p>
          <p id="errorShortDescText_harmful">&safeb.blocked.harmfulPage.shortDesc2;</p>
        </div>

        <!-- Advisory -->
        <div id="advisoryDesc">
          <p id="advisoryDescText">&safeb.palm.advisory.desc2;</p>
        </div>

        <!-- Action buttons -->
        <div id="buttons" class="button-container">
          <!-- Commands handled in browser.js -->
          <button id="goBackButton">&safeb.palm.accept.label2;</button>
          <button id="seeDetailsButton" onclick="onClickSeeDetails();">&safeb.palm.seedetails.label;</button>
        </div>
      </div>
      <div id="errorDescriptionContainer" hidden="true">
        <div class="error-description" id="errorLongDesc_phishing">
          <p id="phishing_error_desc_override">&safeb.blocked.phishingPage.errorDesc.override;</p>
          <p id="phishing_error_desc_no_override">&safeb.blocked.phishingPage.errorDesc.noOverride;</p>
          <p id="phishing_learn_more">&safeb.blocked.phishingPage.learnMore;</p>
        </div>
        <div class="error-description" id="errorLongDesc_malware">
          <p id="malware_error_desc_override">&safeb.blocked.malwarePage.errorDesc.override;</p>
          <p id="malware_error_desc_no_override">&safeb.blocked.malwarePage.errorDesc.noOverride;</p>
          <p id="malware_learn_more">&safeb.blocked.malwarePage.learnMore;</p>
        </div>
        <div class="error-description" id="errorLongDesc_unwanted">
          <p id="unwanted_error_desc_override">&safeb.blocked.unwantedPage.errorDesc.override;</p>
          <p id="unwanted_error_desc_no_override">&safeb.blocked.unwantedPage.errorDesc.noOverride;</p>
          <p id="unwanted_learn_more">&safeb.blocked.unwantedPage.learnMore;</p>
        </div>
        <div class="error-description" id="errorLongDesc_harmful">
          <p id="harmful_error_desc_override">&safeb.blocked.harmfulPage.errorDesc.override;</p>
          <p id="harmful_error_desc_no_override">&safeb.blocked.harmfulPage.errorDesc.noOverride;</p>
          <p id="harmful_learn_more">&safeb.blocked.harmfulPage.learnMore;</p>
        </div>
      </div>
    </div>
    <!--
    - Note: It is important to run the script this way, instead of using
    - an onload handler. This is because error pages are loaded as
    - LOAD_BACKGROUND, which means that onload handlers will not be executed.
    -->
    <script type="application/javascript">
      initPage();
    </script>
  </body>
</html>
PK
!<��e��:chrome/browser/content/browser/bookmarks/bookmarksPanel.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

function init() {
  document.getElementById("bookmarks-view").place =
    "place:type=" + Ci.nsINavHistoryQueryOptions.RESULTS_AS_ROOTS_QUERY;
}

function searchBookmarks(aSearchString) {
  var tree = document.getElementById("bookmarks-view");
  if (!aSearchString)
    tree.place = tree.place;
  else
    tree.applyFilter(aSearchString,
                     [PlacesUtils.bookmarksMenuFolderId,
                      PlacesUtils.unfiledBookmarksFolderId,
                      PlacesUtils.toolbarFolderId,
                      PlacesUtils.mobileFolderId]);
}

window.addEventListener("SidebarFocused",
                        () => document.getElementById("search-box").focus());
PK
!<�4���;chrome/browser/content/browser/bookmarks/bookmarksPanel.xul<?xml version="1.0"?> <!-- -*- Mode: SGML; indent-tabs-mode: nil; -*- -->
<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/places/places.css"?>
<?xml-stylesheet href="chrome://browser/skin/places/places.css"?>
<?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?>

<!DOCTYPE page SYSTEM "chrome://browser/locale/places/places.dtd">

<page id="bookmarksPanel"
      xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
      xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
      onload="init();"
      onunload="SidebarUtils.setMouseoverURL('');">

  <script type="application/javascript"
          src="chrome://browser/content/bookmarks/sidebarUtils.js"/>
  <script type="application/javascript"
          src="chrome://browser/content/bookmarks/bookmarksPanel.js"/>

  <commandset id="placesCommands"/>
<script type="application/javascript" src="chrome://global/content/editMenuOverlay.js"/>
<commandset id="editMenuCommands">
  <commandset id="editMenuCommandSetAll" commandupdater="true" events="focus,select"
              oncommandupdate="goUpdateGlobalEditMenuItems()"/>
  <commandset id="editMenuCommandSetUndo" commandupdater="true" events="undo"
              oncommandupdate="goUpdateUndoEditMenuItems()"/>
  <commandset id="editMenuCommandSetPaste" commandupdater="true" events="clipboard"
              oncommandupdate="goUpdatePasteMenuItems()"/>
  <command id="cmd_undo" oncommand="goDoCommand('cmd_undo')"/>
  <command id="cmd_redo" oncommand="goDoCommand('cmd_redo')"/>
  <command id="cmd_cut" oncommand="goDoCommand('cmd_cut')"/>
  <command id="cmd_copy" oncommand="goDoCommand('cmd_copy')"/>
  <command id="cmd_paste" oncommand="goDoCommand('cmd_paste')"/>
  <command id="cmd_delete" oncommand="goDoCommand('cmd_delete')"/>
  <command id="cmd_selectAll" oncommand="goDoCommand('cmd_selectAll')"/>
  <command id="cmd_switchTextDirection" oncommand="goDoCommand('cmd_switchTextDirection');"/>
</commandset>
  <menupopup id="placesContext"/>

  <!-- Bookmarks and history tooltip -->
  <tooltip id="bhTooltip"/>

  <hbox id="sidebar-search-container" align="center">
    <textbox id="search-box" flex="1" type="search"
             placeholder="&bookmarksSearch.placeholder;"
             aria-controls="bookmarks-view"
             oncommand="searchBookmarks(this.value);"/>
  </hbox>

  <tree id="bookmarks-view" class="sidebar-placesTree" type="places"
        flex="1"
        hidecolumnpicker="true"
        context="placesContext"
        onkeypress="SidebarUtils.handleTreeKeyPress(event);"
        onclick="SidebarUtils.handleTreeClick(this, event, true);"
        onmousemove="SidebarUtils.handleTreeMouseMove(event);"
        onmouseout="SidebarUtils.setMouseoverURL('');">
    <treecols>
      <treecol id="title" flex="1" primary="true" hideheader="true"/>
    </treecols>
    <treechildren id="bookmarks-view-children" view="bookmarks-view"
                  class="sidebar-placesTreechildren" flex="1" tooltip="bhTooltip"/>
  </tree>
</page>
PK
!<�*Ј\\8chrome/browser/content/browser/bookmarks/sidebarUtils.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

ChromeUtils.import("resource://gre/modules/AppConstants.jsm");

let uidensity = window.top.document.documentElement.getAttribute("uidensity");
if (uidensity) {
  document.documentElement.setAttribute("uidensity", uidensity);
}

var SidebarUtils = {
  handleTreeClick: function SU_handleTreeClick(aTree, aEvent, aGutterSelect) {
    // right-clicks are not handled here
    if (aEvent.button == 2)
      return;

    var tbo = aTree.treeBoxObject;
    var cell = tbo.getCellAt(aEvent.clientX, aEvent.clientY);

    if (cell.row == -1 || cell.childElt == "twisty")
      return;

    var mouseInGutter = false;
    if (aGutterSelect) {
      var rect = tbo.getCoordsForCellItem(cell.row, cell.col, "image");
      // getCoordsForCellItem returns the x coordinate in logical coordinates
      // (i.e., starting from the left and right sides in LTR and RTL modes,
      // respectively.)  Therefore, we make sure to exclude the blank area
      // before the tree item icon (that is, to the left or right of it in
      // LTR and RTL modes, respectively) from the click target area.
      var isRTL = window.getComputedStyle(aTree).direction == "rtl";
      if (isRTL)
        mouseInGutter = aEvent.clientX > rect.x;
      else
        mouseInGutter = aEvent.clientX < rect.x;
    }

    var metaKey = AppConstants.platform === "macosx" ? aEvent.metaKey
                                                     : aEvent.ctrlKey;
    var modifKey = metaKey || aEvent.shiftKey;
    var isContainer = tbo.view.isContainer(cell.row);
    var openInTabs = isContainer &&
                     (aEvent.button == 1 ||
                      (aEvent.button == 0 && modifKey)) &&
                     PlacesUtils.hasChildURIs(aTree.view.nodeForTreeIndex(cell.row));

    if (aEvent.button == 0 && isContainer && !openInTabs) {
      tbo.view.toggleOpenState(cell.row);
    } else if (!mouseInGutter && openInTabs &&
            aEvent.originalTarget.localName == "treechildren") {
      tbo.view.selection.select(cell.row);
      PlacesUIUtils.openContainerNodeInTabs(aTree.selectedNode, aEvent, aTree);
    } else if (!mouseInGutter && !isContainer &&
             aEvent.originalTarget.localName == "treechildren") {
      // Clear all other selection since we're loading a link now. We must
      // do this *before* attempting to load the link since openURL uses
      // selection as an indication of which link to load.
      tbo.view.selection.select(cell.row);
      PlacesUIUtils.openNodeWithEvent(aTree.selectedNode, aEvent);
    }
  },

  handleTreeKeyPress: function SU_handleTreeKeyPress(aEvent) {
    let node = aEvent.target.selectedNode;
    if (node) {
      if (aEvent.keyCode == KeyEvent.DOM_VK_RETURN)
        PlacesUIUtils.openNodeWithEvent(node, aEvent);
    }
  },

  /**
   * The following function displays the URL of a node that is being
   * hovered over.
   */
  handleTreeMouseMove: function SU_handleTreeMouseMove(aEvent) {
    if (aEvent.target.localName != "treechildren")
      return;

    var tree = aEvent.target.parentNode;
    var tbo = tree.treeBoxObject;
    var cell = tbo.getCellAt(aEvent.clientX, aEvent.clientY);

    // cell.row is -1 when the mouse is hovering an empty area within the tree.
    // To avoid showing a URL from a previously hovered node for a currently
    // hovered non-url node, we must clear the moused-over URL in these cases.
    if (cell.row != -1) {
      var node = tree.view.nodeForTreeIndex(cell.row);
      if (PlacesUtils.nodeIsURI(node))
        this.setMouseoverURL(node.uri);
      else
        this.setMouseoverURL("");
    } else
      this.setMouseoverURL("");
  },

  setMouseoverURL: function SU_setMouseoverURL(aURL) {
    // When the browser window is closed with an open sidebar, the sidebar
    // unload event happens after the browser's one.  In this case
    // top.XULBrowserWindow has been nullified already.
    if (top.XULBrowserWindow) {
      top.XULBrowserWindow.setOverLink(aURL, null);
    }
  }
};
PK
!<�;1ZhZh0chrome/browser/content/browser/browser-addons.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// This file is loaded into the browser window scope.
/* eslint-env mozilla/browser-window */

// Removes a doorhanger notification if all of the installs it was notifying
// about have ended in some way.
function removeNotificationOnEnd(notification, installs) {
  let count = installs.length;

  function maybeRemove(install) {
    install.removeListener(this);

    if (--count == 0) {
      // Check that the notification is still showing
      let current = PopupNotifications.getNotification(notification.id, notification.browser);
      if (current === notification)
        notification.remove();
    }
  }

  for (let install of installs) {
    install.addListener({
      onDownloadCancelled: maybeRemove,
      onDownloadFailed: maybeRemove,
      onInstallFailed: maybeRemove,
      onInstallEnded: maybeRemove
    });
  }
}

var gXPInstallObserver = {
  _findChildShell(aDocShell, aSoughtShell) {
    if (aDocShell == aSoughtShell)
      return aDocShell;

    var node = aDocShell.QueryInterface(Ci.nsIDocShellTreeItem);
    for (var i = 0; i < node.childCount; ++i) {
      var docShell = node.getChildAt(i);
      docShell = this._findChildShell(docShell, aSoughtShell);
      if (docShell == aSoughtShell)
        return docShell;
    }
    return null;
  },

  _getBrowser(aDocShell) {
    for (let browser of gBrowser.browsers) {
      if (this._findChildShell(browser.docShell, aDocShell))
        return browser;
    }
    return null;
  },

  pendingInstalls: new WeakMap(),

  showInstallConfirmation(browser, installInfo, height = undefined) {
    // If the confirmation notification is already open cache the installInfo
    // and the new confirmation will be shown later
    if (PopupNotifications.getNotification("addon-install-confirmation", browser)) {
      let pending = this.pendingInstalls.get(browser);
      if (pending) {
        pending.push(installInfo);
      } else {
        this.pendingInstalls.set(browser, [installInfo]);
      }
      return;
    }

    let showNextConfirmation = () => {
      // Make sure the browser is still alive.
      if (!gBrowser.browsers.includes(browser))
        return;

      let pending = this.pendingInstalls.get(browser);
      if (pending && pending.length)
        this.showInstallConfirmation(browser, pending.shift());
    };

    // If all installs have already been cancelled in some way then just show
    // the next confirmation
    if (installInfo.installs.every(i => i.state != AddonManager.STATE_DOWNLOADED)) {
      showNextConfirmation();
      return;
    }

    const anchorID = "addons-notification-icon";

    // Make notifications persistent
    var options = {
      displayURI: installInfo.originatingURI,
      persistent: true,
      hideClose: true,
    };

    let acceptInstallation = () => {
      for (let install of installInfo.installs)
        install.install();
      installInfo = null;

      Services.telemetry
              .getHistogramById("SECURITY_UI")
              .add(Ci.nsISecurityUITelemetry.WARNING_CONFIRM_ADDON_INSTALL_CLICK_THROUGH);
    };

    let cancelInstallation = () => {
      if (installInfo) {
        for (let install of installInfo.installs) {
          // The notification may have been closed because the add-ons got
          // cancelled elsewhere, only try to cancel those that are still
          // pending install.
          if (install.state != AddonManager.STATE_CANCELLED)
            install.cancel();
        }
      }

      showNextConfirmation();
    };

    let unsigned = installInfo.installs.filter(i => i.addon.signedState <= AddonManager.SIGNEDSTATE_MISSING);
    let someUnsigned = unsigned.length > 0 && unsigned.length < installInfo.installs.length;

    options.eventCallback = (aEvent) => {
      switch (aEvent) {
        case "removed":
          cancelInstallation();
          break;
        case "shown":
          let addonList = document.getElementById("addon-install-confirmation-content");
          while (addonList.firstChild)
            addonList.firstChild.remove();

          for (let install of installInfo.installs) {
            let container = document.createElement("hbox");

            let name = document.createElement("label");
            name.setAttribute("value", install.addon.name);
            name.setAttribute("class", "addon-install-confirmation-name");
            container.appendChild(name);

            if (someUnsigned && install.addon.signedState <= AddonManager.SIGNEDSTATE_MISSING) {
              let unsignedLabel = document.createElement("label");
              unsignedLabel.setAttribute("value",
                gNavigatorBundle.getString("addonInstall.unsigned"));
              unsignedLabel.setAttribute("class",
                "addon-install-confirmation-unsigned");
              container.appendChild(unsignedLabel);
            }

            addonList.appendChild(container);
          }
          break;
      }
    };

    options.learnMoreURL = Services.urlFormatter.formatURLPref("app.support.baseURL");

    let messageString;
    let notification = document.getElementById("addon-install-confirmation-notification");
    if (unsigned.length == installInfo.installs.length) {
      // None of the add-ons are verified
      messageString = gNavigatorBundle.getString("addonConfirmInstallUnsigned.message");
      notification.setAttribute("warning", "true");
      options.learnMoreURL += "unsigned-addons";
    } else if (unsigned.length == 0) {
      // All add-ons are verified or don't need to be verified
      messageString = gNavigatorBundle.getString("addonConfirmInstall.message");
      notification.removeAttribute("warning");
      options.learnMoreURL += "find-and-install-add-ons";
    } else {
      // Some of the add-ons are unverified, the list of names will indicate
      // which
      messageString = gNavigatorBundle.getString("addonConfirmInstallSomeUnsigned.message");
      notification.setAttribute("warning", "true");
      options.learnMoreURL += "unsigned-addons";
    }

    let brandBundle = document.getElementById("bundle_brand");
    let brandShortName = brandBundle.getString("brandShortName");

    messageString = PluralForm.get(installInfo.installs.length, messageString);
    messageString = messageString.replace("#1", brandShortName);
    messageString = messageString.replace("#2", installInfo.installs.length);

    let action = {
      label: gNavigatorBundle.getString("addonInstall.acceptButton2.label"),
      accessKey: gNavigatorBundle.getString("addonInstall.acceptButton2.accesskey"),
      callback: acceptInstallation,
    };

    let secondaryAction = {
      label: gNavigatorBundle.getString("addonInstall.cancelButton.label"),
      accessKey: gNavigatorBundle.getString("addonInstall.cancelButton.accesskey"),
      callback: () => {},
    };

    if (height) {
      notification.style.minHeight = height + "px";
    }

    let tab = gBrowser.getTabForBrowser(browser);
    if (tab) {
      gBrowser.selectedTab = tab;
    }

    let popup = PopupNotifications.show(browser, "addon-install-confirmation",
                                        messageString, anchorID, action,
                                        [secondaryAction], options);

    removeNotificationOnEnd(popup, installInfo.installs);

    Services.telemetry
            .getHistogramById("SECURITY_UI")
            .add(Ci.nsISecurityUITelemetry.WARNING_CONFIRM_ADDON_INSTALL);
  },

  observe(aSubject, aTopic, aData) {
    var brandBundle = document.getElementById("bundle_brand");
    var installInfo = aSubject.wrappedJSObject;
    var browser = installInfo.browser;

    // Make sure the browser is still alive.
    if (!browser || !gBrowser.browsers.includes(browser))
      return;

    const anchorID = "addons-notification-icon";
    var messageString, action;
    var brandShortName = brandBundle.getString("brandShortName");

    var notificationID = aTopic;
    // Make notifications persistent
    var options = {
      displayURI: installInfo.originatingURI,
      persistent: true,
      hideClose: true,
      timeout: Date.now() + 30000,
    };

    switch (aTopic) {
    case "addon-install-disabled": {
      notificationID = "xpinstall-disabled";
      let secondaryActions = null;

      if (Services.prefs.prefIsLocked("xpinstall.enabled")) {
        messageString = gNavigatorBundle.getString("xpinstallDisabledMessageLocked");
      } else {
        messageString = gNavigatorBundle.getString("xpinstallDisabledMessage");

        action = {
          label: gNavigatorBundle.getString("xpinstallDisabledButton"),
          accessKey: gNavigatorBundle.getString("xpinstallDisabledButton.accesskey"),
          callback: function editPrefs() {
            Services.prefs.setBoolPref("xpinstall.enabled", true);
          }
        };

        secondaryActions = [{
          label: gNavigatorBundle.getString("addonInstall.cancelButton.label"),
          accessKey: gNavigatorBundle.getString("addonInstall.cancelButton.accesskey"),
          callback: () => {},
        }];
      }

      PopupNotifications.show(browser, notificationID, messageString, anchorID,
                              action, secondaryActions, options);
      break; }
    case "addon-install-origin-blocked": {
      messageString = gNavigatorBundle.getFormattedString("xpinstallPromptMessage",
                        [brandShortName]);

      options.removeOnDismissal = true;
      options.persistent = false;

      let secHistogram = Services.telemetry.getHistogramById("SECURITY_UI");
      secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_ADDON_ASKING_PREVENTED);
      let popup = PopupNotifications.show(browser, notificationID,
                                          messageString, anchorID,
                                          null, null, options);
      removeNotificationOnEnd(popup, installInfo.installs);
      break; }
    case "addon-install-blocked": {
      messageString = gNavigatorBundle.getFormattedString("xpinstallPromptMessage",
                        [brandShortName]);

      let secHistogram = Services.telemetry.getHistogramById("SECURITY_UI");
      action = {
        label: gNavigatorBundle.getString("xpinstallPromptAllowButton"),
        accessKey: gNavigatorBundle.getString("xpinstallPromptAllowButton.accesskey"),
        callback() {
          secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_ADDON_ASKING_PREVENTED_CLICK_THROUGH);
          installInfo.install();
        }
      };
      let secondaryAction = {
        label: gNavigatorBundle.getString("xpinstallPromptMessage.dontAllow"),
        accessKey: gNavigatorBundle.getString("xpinstallPromptMessage.dontAllow.accesskey"),
        callback: () => {},
      };

      secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_ADDON_ASKING_PREVENTED);
      let popup = PopupNotifications.show(browser, notificationID,
                                          messageString, anchorID,
                                          action, [secondaryAction], options);
      removeNotificationOnEnd(popup, installInfo.installs);
      break; }
    case "addon-install-started": {
      let needsDownload = function needsDownload(aInstall) {
        return aInstall.state != AddonManager.STATE_DOWNLOADED;
      };
      // If all installs have already been downloaded then there is no need to
      // show the download progress
      if (!installInfo.installs.some(needsDownload))
        return;
      notificationID = "addon-progress";
      messageString = gNavigatorBundle.getString("addonDownloadingAndVerifying");
      messageString = PluralForm.get(installInfo.installs.length, messageString);
      messageString = messageString.replace("#1", installInfo.installs.length);
      options.installs = installInfo.installs;
      options.contentWindow = browser.contentWindow;
      options.sourceURI = browser.currentURI;
      options.eventCallback = function(aEvent) {
        switch (aEvent) {
          case "shown":
            let notificationElement = [...this.owner.panel.childNodes]
                                      .find(n => n.notification == this);
            if (notificationElement) {
              if (Services.prefs.getBoolPref("xpinstall.customConfirmationUI", false)) {
                notificationElement.setAttribute("mainactiondisabled", "true");
              } else {
                notificationElement.button.hidden = true;
              }
            }
            break;
          case "removed":
            options.contentWindow = null;
            options.sourceURI = null;
            break;
        }
      };
      action = {
        label: gNavigatorBundle.getString("addonInstall.acceptButton2.label"),
        accessKey: gNavigatorBundle.getString("addonInstall.acceptButton2.accesskey"),
        callback: () => {},
      };
      let secondaryAction = {
        label: gNavigatorBundle.getString("addonInstall.cancelButton.label"),
        accessKey: gNavigatorBundle.getString("addonInstall.cancelButton.accesskey"),
        callback: () => {
          for (let install of installInfo.installs) {
            if (install.state != AddonManager.STATE_CANCELLED) {
              install.cancel();
            }
          }
        },
      };
      let notification = PopupNotifications.show(browser, notificationID, messageString,
                                                 anchorID, action,
                                                 [secondaryAction], options);
      notification._startTime = Date.now();

      break; }
    case "addon-install-failed": {
      options.removeOnDismissal = true;
      options.persistent = false;

      // TODO This isn't terribly ideal for the multiple failure case
      for (let install of installInfo.installs) {
        let host;
        try {
          host  = options.displayURI.host;
        } catch (e) {
          // displayURI might be missing or 'host' might throw for non-nsStandardURL nsIURIs.
        }

        if (!host)
          host = (install.sourceURI instanceof Ci.nsIStandardURL) &&
                 install.sourceURI.host;

        let error = (host || install.error == 0) ? "addonInstallError" : "addonLocalInstallError";
        let args;
        if (install.error < 0) {
          error += install.error;
          args = [brandShortName, install.name];
        } else if (install.addon.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED) {
          error += "Blocklisted";
          args = [install.name];
        } else {
          error += "Incompatible";
          args = [brandShortName, Services.appinfo.version, install.name];
        }

        // Add Learn More link when refusing to install an unsigned add-on
        if (install.error == AddonManager.ERROR_SIGNEDSTATE_REQUIRED) {
          options.learnMoreURL = Services.urlFormatter.formatURLPref("app.support.baseURL") + "unsigned-addons";
        }

        messageString = gNavigatorBundle.getFormattedString(error, args);

        PopupNotifications.show(browser, notificationID, messageString, anchorID,
                                action, null, options);

        // Can't have multiple notifications with the same ID, so stop here.
        break;
      }
      this._removeProgressNotification(browser);
      break; }
    case "addon-install-confirmation": {
      let showNotification = () => {
        let height = undefined;

        if (PopupNotifications.isPanelOpen) {
          let rect = document.getElementById("addon-progress-notification").getBoundingClientRect();
          height = rect.height;
        }

        this._removeProgressNotification(browser);
        this.showInstallConfirmation(browser, installInfo, height);
      };

      let progressNotification = PopupNotifications.getNotification("addon-progress", browser);
      if (progressNotification) {
        let downloadDuration = Date.now() - progressNotification._startTime;
        let securityDelay = Services.prefs.getIntPref("security.dialog_enable_delay") - downloadDuration;
        if (securityDelay > 0) {
          setTimeout(() => {
            // The download may have been cancelled during the security delay
            if (PopupNotifications.getNotification("addon-progress", browser))
              showNotification();
          }, securityDelay);
          break;
        }
      }
      showNotification();
      break; }
    case "addon-install-complete": {
      let needsRestart = installInfo.installs.some(function(i) {
        return i.addon.pendingOperations != AddonManager.PENDING_NONE;
      });

      let secondaryActions = null;
      let numAddons = installInfo.installs.length;

      if (needsRestart) {
        notificationID = "addon-install-restart";
        if (numAddons == 1) {
          messageString = gNavigatorBundle.getFormattedString("addonInstalledNeedsRestart",
                                                              [installInfo.installs[0].name, brandShortName]);
        } else {
          messageString = gNavigatorBundle.getString("addonsGenericInstalledNeedsRestart");
          messageString = PluralForm.get(numAddons, messageString);
          messageString = messageString.replace("#1", numAddons);
          messageString = messageString.replace("#2", brandShortName);
        }
        action = {
          label: gNavigatorBundle.getString("addonInstallRestartButton"),
          accessKey: gNavigatorBundle.getString("addonInstallRestartButton.accesskey"),
          callback() {
            BrowserUtils.restartApplication();
          }
        };
        secondaryActions = [{
          label: gNavigatorBundle.getString("addonInstallRestartIgnoreButton"),
          accessKey: gNavigatorBundle.getString("addonInstallRestartIgnoreButton.accesskey"),
          callback: () => {},
        }];
      } else {
        if (numAddons == 1) {
          messageString = gNavigatorBundle.getFormattedString("addonInstalled",
                                                              [installInfo.installs[0].name]);
        } else {
          messageString = gNavigatorBundle.getString("addonsGenericInstalled");
          messageString = PluralForm.get(numAddons, messageString);
          messageString = messageString.replace("#1", numAddons);
        }
        action = null;
      }

      // Remove notification on dismissal, since it's possible to cancel the
      // install through the addons manager UI, making the "restart" prompt
      // irrelevant.
      options.removeOnDismissal = true;
      options.persistent = false;

      PopupNotifications.show(browser, notificationID, messageString, anchorID,
                              action, secondaryActions, options);
      break; }
    }
  },
  _removeProgressNotification(aBrowser) {
    let notification = PopupNotifications.getNotification("addon-progress", aBrowser);
    if (notification)
      notification.remove();
  }
};

var gExtensionsNotifications = {
  initialized: false,
  init() {
    this.updateAlerts();
    this.boundUpdate = this.updateAlerts.bind(this);
    ExtensionsUI.on("change", this.boundUpdate);
    this.initialized = true;
  },

  uninit() {
    // uninit() can race ahead of init() in some cases, if that happens,
    // we have no handler to remove.
    if (!this.initialized) {
      return;
    }
    ExtensionsUI.off("change", this.boundUpdate);
  },

  _createAddonButton(text, icon, callback) {
    let button = document.createElement("toolbarbutton");
    button.setAttribute("label", text);
    const DEFAULT_EXTENSION_ICON =
      "chrome://mozapps/skin/extensions/extensionGeneric.svg";
    button.setAttribute("image", icon || DEFAULT_EXTENSION_ICON);
    button.className = "addon-banner-item";

    button.addEventListener("command", callback);
    PanelUI.addonNotificationContainer.appendChild(button);
  },

  updateAlerts() {
    let sideloaded = ExtensionsUI.sideloaded;
    let updates = ExtensionsUI.updates;

    let container = PanelUI.addonNotificationContainer;

    while (container.firstChild) {
      container.firstChild.remove();
    }

    let items = 0;
    for (let update of updates) {
      if (++items > 4) {
        break;
      }
      let text = gNavigatorBundle.getFormattedString("webextPerms.updateMenuItem", [update.addon.name]);
      this._createAddonButton(text, update.addon.iconURL, evt => {
        ExtensionsUI.showUpdate(gBrowser, update);
      });
    }

    let appName;
    for (let addon of sideloaded) {
      if (++items > 4) {
        break;
      }
      if (!appName) {
        let brandBundle = document.getElementById("bundle_brand");
        appName = brandBundle.getString("brandShortName");
      }

      let text = gNavigatorBundle.getFormattedString("webextPerms.sideloadMenuItem", [addon.name, appName]);
      this._createAddonButton(text, addon.iconURL, evt => {
        // We need to hide the main menu manually because the toolbarbutton is
        // removed immediately while processing this event, and PanelUI is
        // unable to identify which panel should be closed automatically.
        PanelUI.hide();
        ExtensionsUI.showSideloaded(gBrowser, addon);
      });
    }
  },
};

var LightWeightThemeWebInstaller = {
  init() {
    let mm = window.messageManager;
    mm.addMessageListener("LightWeightThemeWebInstaller:Install", this);
    mm.addMessageListener("LightWeightThemeWebInstaller:Preview", this);
    mm.addMessageListener("LightWeightThemeWebInstaller:ResetPreview", this);

    XPCOMUtils.defineLazyPreferenceGetter(this, "_apiTesting", "extensions.webapi.testing", false);
  },

  receiveMessage(message) {
    // ignore requests from background tabs
    if (message.target != gBrowser.selectedBrowser) {
      return;
    }

    let data = message.data;

    switch (message.name) {
      case "LightWeightThemeWebInstaller:Install": {
        this._installRequest(data.themeData, data.principal, data.baseURI);
        break;
      }
      case "LightWeightThemeWebInstaller:Preview": {
        this._preview(data.themeData, data.principal, data.baseURI);
        break;
      }
      case "LightWeightThemeWebInstaller:ResetPreview": {
        this._resetPreview(data && data.principal);
        break;
      }
    }
  },

  handleEvent(event) {
    switch (event.type) {
      case "TabSelect": {
        this._resetPreview();
        break;
      }
    }
  },

  get _manager() {
    let temp = {};
    ChromeUtils.import("resource://gre/modules/LightweightThemeManager.jsm", temp);
    delete this._manager;
    return this._manager = temp.LightweightThemeManager;
  },

  _installRequest(dataString, principal, baseURI) {
    // Don't allow installing off null principals.
    if (!principal.URI) {
      return;
    }

    let data = this._manager.parseTheme(dataString, baseURI);

    if (!data) {
      return;
    }

    // A notification bar with the option to undo is normally shown after a
    // theme is installed.  But the discovery pane served from the url(s)
    // below has its own toggle switch for quick undos, so don't show the
    // notification in that case.
    let notify = this._shouldShowUndoPrompt(principal);
    if (this._isAllowed(principal)) {
      this._install(data, notify);
      return;
    }

    let strings = {
      header: gNavigatorBundle.getFormattedString("webextPerms.header", ["<>"]),
      addonName: data.name,
      text: gNavigatorBundle.getFormattedString("lwthemeInstallRequest.message2",
                                                [principal.URI.host]),
      acceptText: gNavigatorBundle.getString("lwthemeInstallRequest.allowButton2"),
      acceptKey: gNavigatorBundle.getString("lwthemeInstallRequest.allowButton.accesskey2"),
      cancelText: gNavigatorBundle.getString("webextPerms.cancel.label"),
      cancelKey: gNavigatorBundle.getString("webextPerms.cancel.accessKey"),
      msgs: []
    };
    ExtensionsUI.showPermissionsPrompt(gBrowser.selectedBrowser, strings, null,
      "installWeb").then(answer => {
      if (answer) {
        LightWeightThemeWebInstaller._install(data, notify);
      }
    });
  },

  _install(newLWTheme, notify) {
    let listener = {
      onEnabling(aAddon, aRequiresRestart) {
        if (!aRequiresRestart) {
          return;
        }

        let messageString = gNavigatorBundle.getFormattedString("lwthemeNeedsRestart.message",
          [aAddon.name], 1);

        let action = {
          label: gNavigatorBundle.getString("lwthemeNeedsRestart.button"),
          accessKey: gNavigatorBundle.getString("lwthemeNeedsRestart.accesskey"),
          callback() {
            BrowserUtils.restartApplication();
          }
        };

        let options = {
          persistent: true
        };

        PopupNotifications.show(gBrowser.selectedBrowser, "addon-theme-change",
                                messageString, "addons-notification-icon",
                                action, null, options);
      },

      onEnabled(aAddon) {
        if (notify) {
          ExtensionsUI.showInstallNotification(gBrowser.selectedBrowser, newLWTheme);
        }
      }
    };

    AddonManager.addAddonListener(listener);
    this._manager.currentTheme = newLWTheme;
    AddonManager.removeAddonListener(listener);
  },

  _preview(dataString, principal, baseURI) {
    if (!this._isAllowed(principal))
      return;

    let data = this._manager.parseTheme(dataString, baseURI);
    if (!data)
      return;

    this._resetPreview();
    gBrowser.tabContainer.addEventListener("TabSelect", this);
    this._manager.previewTheme(data);
  },

  _resetPreview(principal) {
    if (!this._isAllowed(principal))
      return;
    gBrowser.tabContainer.removeEventListener("TabSelect", this);
    this._manager.resetPreview();
  },

  _isAllowed(principal) {
    if (!principal || !principal.URI || !principal.URI.schemeIs("https")) {
      return false;
    }

    let pm = Services.perms;
    return pm.testPermission(principal.URI, "install") == pm.ALLOW_ACTION;
  },

  _shouldShowUndoPrompt(principal) {
    if (!principal || !principal.URI) {
      return true;
    }

    let prePath = principal.URI.prePath;
    if (prePath == "https://discovery.addons.mozilla.org") {
      return false;
    }

    if (this._apiTesting && (prePath == "https://discovery.addons.allizom.org" ||
                             prePath == "https://discovery.addons-dev.allizom.org")) {
      return false;
    }
    return true;
  },

};
PK
!<��#�=%=%7chrome/browser/content/browser/browser-captivePortal.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

XPCOMUtils.defineLazyServiceGetter(this, "cps",
                                   "@mozilla.org/network/captive-portal-service;1",
                                   "nsICaptivePortalService");

var CaptivePortalWatcher = {
  /**
   * This constant is chosen to be large enough for a portal recheck to complete,
   * and small enough that the delay in opening a tab isn't too noticeable.
   * Please see comments for _delayedCaptivePortalDetected for more details.
   */
  PORTAL_RECHECK_DELAY_MS: Services.prefs.getIntPref("captivedetect.portalRecheckDelayMS", 500),

  // This is the value used to identify the captive portal notification.
  PORTAL_NOTIFICATION_VALUE: "captive-portal-detected",

  // This holds a weak reference to the captive portal tab so that we
  // don't leak it if the user closes it.
  _captivePortalTab: null,

  /**
   * If a portal is detected when we don't have focus, we first wait for focus
   * and then add the tab if, after a recheck, the portal is still active. This
   * is set to true while we wait so that in the unlikely event that we receive
   * another notification while waiting, we don't do things twice.
   */
  _delayedCaptivePortalDetectedInProgress: false,

  // In the situation above, this is set to true while we wait for the recheck.
  // This flag exists so that tests can appropriately simulate a recheck.
  _waitingForRecheck: false,

  get _captivePortalNotification() {
    let nb = document.getElementById("high-priority-global-notificationbox");
    return nb.getNotificationWithValue(this.PORTAL_NOTIFICATION_VALUE);
  },

  get canonicalURL() {
    return Services.prefs.getCharPref("captivedetect.canonicalURL");
  },

  get _browserBundle() {
    delete this._browserBundle;
    return this._browserBundle =
      Services.strings.createBundle("chrome://browser/locale/browser.properties");
  },

  init() {
    Services.obs.addObserver(this, "captive-portal-login");
    Services.obs.addObserver(this, "captive-portal-login-abort");
    Services.obs.addObserver(this, "captive-portal-login-success");

    if (cps.state == cps.LOCKED_PORTAL) {
      // A captive portal has already been detected.
      this._captivePortalDetected();

      // Automatically open a captive portal tab if there's no other browser window.
      let windows = Services.wm.getEnumerator("navigator:browser");
      if (windows.getNext() == window && !windows.hasMoreElements()) {
        this.ensureCaptivePortalTab();
      }
    } else if (cps.state == cps.UNKNOWN) {
      // We trigger a portal check after delayed startup to avoid doing a network
      // request before first paint.
      this._delayedRecheckPending = true;
      Services.obs.addObserver(this, "browser-delayed-startup-finished");
    }

    XPCOMUtils.defineLazyPreferenceGetter(this, "PORTAL_RECHECK_DELAY_MS",
                                          "captivedetect.portalRecheckDelayMS", 500);
  },

  uninit() {
    Services.obs.removeObserver(this, "captive-portal-login");
    Services.obs.removeObserver(this, "captive-portal-login-abort");
    Services.obs.removeObserver(this, "captive-portal-login-success");

    if (this._delayedRecheckPending) {
      Services.obs.removeObserver(this, "browser-delayed-startup-finished");
    }

    if (this._delayedCaptivePortalDetectedInProgress) {
      Services.obs.removeObserver(this, "xul-window-visible");
    }
  },

  observe(aSubject, aTopic, aData) {
    switch (aTopic) {
      case "browser-delayed-startup-finished":
        Services.obs.removeObserver(this, "browser-delayed-startup-finished");
        delete this._delayedRecheckPending;
        cps.recheckCaptivePortal();
        break;
      case "captive-portal-login":
        this._captivePortalDetected();
        break;
      case "captive-portal-login-abort":
      case "captive-portal-login-success":
        this._captivePortalGone();
        break;
      case "xul-window-visible":
        this._delayedCaptivePortalDetected();
        break;
    }
  },

  _captivePortalDetected() {
    if (this._delayedCaptivePortalDetectedInProgress) {
      return;
    }

    let win = RecentWindow.getMostRecentBrowserWindow();
    // If no browser window has focus, open and show the tab when we regain focus.
    // This is so that if a different application was focused, when the user
    // (re-)focuses a browser window, we open the tab immediately in that window
    // so they can log in before continuing to browse.
    if (win != Services.ww.activeWindow) {
      this._delayedCaptivePortalDetectedInProgress = true;
      Services.obs.addObserver(this, "xul-window-visible");
    }

    this._showNotification();
  },

  /**
   * Called after we regain focus if we detect a portal while a browser window
   * doesn't have focus. Triggers a portal recheck to reaffirm state, and adds
   * the tab if needed after a short delay to allow the recheck to complete.
   */
  _delayedCaptivePortalDetected() {
    if (!this._delayedCaptivePortalDetectedInProgress) {
      return;
    }

    let win = RecentWindow.getMostRecentBrowserWindow();
    if (win != Services.ww.activeWindow) {
      // The window that got focused was not a browser window.
      return;
    }
    Services.obs.removeObserver(this, "xul-window-visible");
    this._delayedCaptivePortalDetectedInProgress = false;

    if (win != window) {
      // Some other browser window got focus, we don't have to do anything.
      return;
    }
    // Trigger a portal recheck. The user may have logged into the portal via
    // another client, or changed networks.
    cps.recheckCaptivePortal();
    this._waitingForRecheck = true;
    let requestTime = Date.now();

    let self = this;
    Services.obs.addObserver(function observer() {
      let time = Date.now() - requestTime;
      Services.obs.removeObserver(observer, "captive-portal-check-complete");
      self._waitingForRecheck = false;
      if (cps.state != cps.LOCKED_PORTAL) {
        // We're free of the portal!
        return;
      }

      if (time <= self.PORTAL_RECHECK_DELAY_MS) {
        // The amount of time elapsed since we requested a recheck (i.e. since
        // the browser window was focused) was small enough that we can add and
        // focus a tab with the login page with no noticeable delay.
        self.ensureCaptivePortalTab();
      }
    }, "captive-portal-check-complete");
  },

  _captivePortalGone() {
    if (this._delayedCaptivePortalDetectedInProgress) {
      Services.obs.removeObserver(this, "xul-window-visible");
      this._delayedCaptivePortalDetectedInProgress = false;
    }

    this._removeNotification();
  },

  handleEvent(aEvent) {
    if (aEvent.type != "TabSelect" || !this._captivePortalTab || !this._captivePortalNotification) {
      return;
    }

    let tab = this._captivePortalTab.get();
    let n = this._captivePortalNotification;
    if (!tab || !n) {
      return;
    }

    let doc = tab.ownerDocument;
    let button = n.querySelector("button.notification-button");
    if (doc.defaultView.gBrowser.selectedTab == tab) {
      button.style.visibility = "hidden";
    } else {
      button.style.visibility = "visible";
    }
  },

  _showNotification() {
    let buttons = [
      {
        label: this._browserBundle.GetStringFromName("captivePortal.showLoginPage2"),
        callback: () => {
          this.ensureCaptivePortalTab();

          // Returning true prevents the notification from closing.
          return true;
        },
        isDefault: true,
      },
    ];

    let message = this._browserBundle.GetStringFromName("captivePortal.infoMessage3");

    let closeHandler = (aEventName) => {
      if (aEventName != "removed") {
        return;
      }
      gBrowser.tabContainer.removeEventListener("TabSelect", this);
    };

    let nb = document.getElementById("high-priority-global-notificationbox");
    nb.appendNotification(message, this.PORTAL_NOTIFICATION_VALUE, "",
                          nb.PRIORITY_INFO_MEDIUM, buttons, closeHandler);

    gBrowser.tabContainer.addEventListener("TabSelect", this);
  },

  _removeNotification() {
    let n = this._captivePortalNotification;
    if (!n || !n.parentNode) {
      return;
    }
    n.close();
  },

  ensureCaptivePortalTab() {
    let tab;
    if (this._captivePortalTab) {
      tab = this._captivePortalTab.get();
    }

    // If the tab is gone or going, we need to open a new one.
    if (!tab || tab.closing || !tab.parentNode) {
      tab = gBrowser.addTab(this.canonicalURL, { ownerTab: gBrowser.selectedTab });
      this._captivePortalTab = Cu.getWeakReference(tab);
    }

    gBrowser.selectedTab = tab;

    let canonicalURI = makeURI(this.canonicalURL);

    // When we are no longer captive, close the tab if it's at the canonical URL.
    let tabCloser = () => {
      Services.obs.removeObserver(tabCloser, "captive-portal-login-abort");
      Services.obs.removeObserver(tabCloser, "captive-portal-login-success");
      if (!tab || tab.closing || !tab.parentNode || !tab.linkedBrowser ||
          !tab.linkedBrowser.currentURI.equalsExceptRef(canonicalURI)) {
        return;
      }
      gBrowser.removeTab(tab);
    };
    Services.obs.addObserver(tabCloser, "captive-portal-login-abort");
    Services.obs.addObserver(tabCloser, "captive-portal-login-success");
  },
};
PK
!<<A�L�
�
6chrome/browser/content/browser/browser-compacttheme.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
 * Listeners for the compact theme.  This adds an extra stylesheet
 * to browser.xul if a pref is set and no other themes are applied.
 */
var CompactTheme = {
  styleSheetLocation: "chrome://browser/skin/compacttheme.css",
  styleSheet: null,

  get isStyleSheetEnabled() {
    return this.styleSheet && !this.styleSheet.sheet.disabled;
  },

  get isThemeCurrentlyApplied() {
    let theme = LightweightThemeManager.currentTheme;
    return theme && (
           theme.id == "[email protected]" ||
           theme.id == "[email protected]");
  },

  init() {
    Services.obs.addObserver(this, "lightweight-theme-styling-update");

    if (this.isThemeCurrentlyApplied) {
      this._toggleStyleSheet(true);
    }
  },

  createStyleSheet() {
    let styleSheetAttr = `href="${this.styleSheetLocation}" type="text/css"`;
    this.styleSheet = document.createProcessingInstruction(
      "xml-stylesheet", styleSheetAttr);
    document.insertBefore(this.styleSheet, document.documentElement);
    this.styleSheet.sheet.disabled = true;
  },

  observe(subject, topic, data) {
    if (topic == "lightweight-theme-styling-update") {
      let newTheme = JSON.parse(data);
      if (newTheme && (
          newTheme.id == "[email protected]" ||
          newTheme.id == "[email protected]")) {
        // We are using the theme ID on this object instead of always referencing
        // LightweightThemeManager.currentTheme in case this is a preview
        this._toggleStyleSheet(true);
      } else {
        this._toggleStyleSheet(false);
      }

    }
  },

  _toggleStyleSheet(enabled) {
    let wasEnabled = this.isStyleSheetEnabled;
    if (enabled) {
      // The stylesheet may not have been created yet if it wasn't
      // needed on initial load.  Make it now.
      if (!this.styleSheet) {
        this.createStyleSheet();
      }
      this.styleSheet.sheet.disabled = false;
    } else if (!enabled && wasEnabled) {
      this.styleSheet.sheet.disabled = true;
    }
  },

  uninit() {
    Services.obs.removeObserver(this, "lightweight-theme-styling-update");
    this.styleSheet = null;
  }
};

// If the compact theme is going to be applied in gBrowserInit.onLoad,
// then preload it now.  This prevents a flash of unstyled content where the
// normal theme is applied while the compact theme stylesheet is loading.
if (this != Services.appShell.hiddenDOMWindow && CompactTheme.isThemeCurrentlyApplied) {
  CompactTheme.createStyleSheet();
}
PK
!<	�ZwCECE1chrome/browser/content/browser/browser-ctrlTab.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 file is loaded into the browser window scope.
/* eslint-env mozilla/browser-window */

/**
 * Tab previews utility, produces thumbnails
 */
var tabPreviews = {
  init: function tabPreviews_init() {
    if (this._selectedTab)
      return;
    this._selectedTab = gBrowser.selectedTab;

    gBrowser.tabContainer.addEventListener("TabSelect", this);
    gBrowser.tabContainer.addEventListener("SSTabRestored", this);
  },

  get aspectRatio() {
    let { PageThumbUtils } = ChromeUtils.import("resource://gre/modules/PageThumbUtils.jsm", {});
    let [ width, height ] = PageThumbUtils.getThumbnailSize(window);
    delete this.aspectRatio;
    return this.aspectRatio = height / width;
  },

  get: function tabPreviews_get(aTab) {
    let uri = aTab.linkedBrowser.currentURI.spec;

    if (aTab.__thumbnail_lastURI &&
        aTab.__thumbnail_lastURI != uri) {
      aTab.__thumbnail = null;
      aTab.__thumbnail_lastURI = null;
    }

    if (aTab.__thumbnail)
      return aTab.__thumbnail;

    if (aTab.getAttribute("pending") == "true") {
      let img = new Image;
      img.src = PageThumbs.getThumbnailURL(uri);
      return img;
    }

    return this.capture(aTab, !aTab.hasAttribute("busy"));
  },

  capture: function tabPreviews_capture(aTab, aShouldCache) {
    let browser = aTab.linkedBrowser;
    let uri = browser.currentURI.spec;
    let canvas = PageThumbs.createCanvas(window);
    PageThumbs.shouldStoreThumbnail(browser, (aDoStore) => {
      if (aDoStore && aShouldCache) {
        PageThumbs.captureAndStore(browser, function() {
          let img = new Image;
          img.src = PageThumbs.getThumbnailURL(uri);
          aTab.__thumbnail = img;
          aTab.__thumbnail_lastURI = uri;
          canvas.getContext("2d").drawImage(img, 0, 0);
        });
      } else {
        PageThumbs.captureToCanvas(browser, canvas, () => {
          if (aShouldCache) {
            aTab.__thumbnail = canvas;
            aTab.__thumbnail_lastURI = uri;
          }
        });
      }
    });
    return canvas;
  },

  handleEvent: function tabPreviews_handleEvent(event) {
    switch (event.type) {
      case "TabSelect":
        if (this._selectedTab &&
            this._selectedTab.parentNode &&
            !this._pendingUpdate) {
          // Generate a thumbnail for the tab that was selected.
          // The timeout keeps the UI snappy and prevents us from generating thumbnails
          // for tabs that will be closed. During that timeout, don't generate other
          // thumbnails in case multiple TabSelect events occur fast in succession.
          this._pendingUpdate = true;
          setTimeout(function(self, aTab) {
            self._pendingUpdate = false;
            if (aTab.parentNode &&
                !aTab.hasAttribute("busy") &&
                !aTab.hasAttribute("pending"))
              self.capture(aTab, true);
          }, 2000, this, this._selectedTab);
        }
        this._selectedTab = event.target;
        break;
      case "SSTabRestored":
        this.capture(event.target, true);
        break;
    }
  }
};

var tabPreviewPanelHelper = {
  opening(host) {
    host.panel.hidden = false;

    var handler = this._generateHandler(host);
    host.panel.addEventListener("popupshown", handler);
    host.panel.addEventListener("popuphiding", handler);

    host._prevFocus = document.commandDispatcher.focusedElement;
  },
  _generateHandler(host) {
    var self = this;
    return function listener(event) {
      if (event.target == host.panel) {
        host.panel.removeEventListener(event.type, listener);
        self["_" + event.type](host);
      }
    };
  },
  _popupshown(host) {
    if ("setupGUI" in host)
      host.setupGUI();
  },
  _popuphiding(host) {
    if ("suspendGUI" in host)
      host.suspendGUI();

    if (host._prevFocus) {
      Services.focus.setFocus(host._prevFocus, Ci.nsIFocusManager.FLAG_NOSCROLL);
      host._prevFocus = null;
    } else
      gBrowser.selectedBrowser.focus();

    if (host.tabToSelect) {
      gBrowser.selectedTab = host.tabToSelect;
      host.tabToSelect = null;
    }
  }
};

/**
 * Ctrl-Tab panel
 */
var ctrlTab = {
  get panel() {
    delete this.panel;
    return this.panel = document.getElementById("ctrlTab-panel");
  },
  get showAllButton() {
    delete this.showAllButton;
    return this.showAllButton = document.getElementById("ctrlTab-showAll");
  },
  get previews() {
    delete this.previews;
    return this.previews = this.panel.getElementsByClassName("ctrlTab-preview");
  },
  get maxTabPreviews() {
    delete this.maxTabPreviews;
    return this.maxTabPreviews = this.previews.length - 1;
  },
  get canvasWidth() {
    delete this.canvasWidth;
    return this.canvasWidth = Math.ceil(screen.availWidth * .85 / this.maxTabPreviews);
  },
  get canvasHeight() {
    delete this.canvasHeight;
    return this.canvasHeight = Math.round(this.canvasWidth * tabPreviews.aspectRatio);
  },
  get keys() {
    var keys = {};
    ["close", "find", "selectAll"].forEach(function(key) {
      keys[key] = document.getElementById("key_" + key)
                          .getAttribute("key")
                          .toLocaleLowerCase().charCodeAt(0);
    });
    delete this.keys;
    return this.keys = keys;
  },
  _selectedIndex: 0,
  get selected() {
    return this._selectedIndex < 0 ?
             document.activeElement :
             this.previews.item(this._selectedIndex);
  },
  get isOpen() {
    return this.panel.state == "open" || this.panel.state == "showing" || this._timer;
  },
  get tabCount() {
    return this.tabList.length;
  },
  get tabPreviewCount() {
    return Math.min(this.maxTabPreviews, this.tabCount);
  },

  get tabList() {
    return this._recentlyUsedTabs;
  },

  init: function ctrlTab_init() {
    if (!this._recentlyUsedTabs) {
      tabPreviews.init();

      this._initRecentlyUsedTabs();
      this._init(true);
    }
  },

  uninit: function ctrlTab_uninit() {
    if (this._recentlyUsedTabs) {
      this._recentlyUsedTabs = null;
      this._init(false);
    }
  },

  prefName: "browser.ctrlTab.previews",
  readPref: function ctrlTab_readPref() {
    var enable =
      Services.prefs.getBoolPref(this.prefName) &&
      !Services.prefs.getBoolPref("browser.ctrlTab.disallowForScreenReaders", false);

    if (enable)
      this.init();
    else
      this.uninit();
  },
  observe(aSubject, aTopic, aPrefName) {
    this.readPref();
  },

  updatePreviews: function ctrlTab_updatePreviews() {
    for (let i = 0; i < this.previews.length; i++)
      this.updatePreview(this.previews[i], this.tabList[i]);

    var showAllLabel = gNavigatorBundle.getString("ctrlTab.listAllTabs.label");
    this.showAllButton.label =
      PluralForm.get(this.tabCount, showAllLabel).replace("#1", this.tabCount);
    this.showAllButton.hidden = !allTabs.canOpen;
  },

  updatePreview: function ctrlTab_updatePreview(aPreview, aTab) {
    if (aPreview == this.showAllButton)
      return;

    aPreview._tab = aTab;

    if (aPreview.firstChild)
      aPreview.firstChild.remove();
    if (aTab) {
      let canvasWidth = this.canvasWidth;
      let canvasHeight = this.canvasHeight;
      aPreview.appendChild(tabPreviews.get(aTab));
      aPreview.setAttribute("label", aTab.label);
      aPreview.setAttribute("tooltiptext", aTab.label);
      aPreview.setAttribute("canvaswidth", canvasWidth);
      aPreview.setAttribute("canvasstyle",
                            "max-width:" + canvasWidth + "px;" +
                            "min-width:" + canvasWidth + "px;" +
                            "max-height:" + canvasHeight + "px;" +
                            "min-height:" + canvasHeight + "px;");
      if (aTab.image)
        aPreview.setAttribute("image", aTab.image);
      else
        aPreview.removeAttribute("image");
      aPreview.hidden = false;
    } else {
      aPreview.hidden = true;
      aPreview.removeAttribute("label");
      aPreview.removeAttribute("tooltiptext");
      aPreview.removeAttribute("image");
    }
  },

  advanceFocus: function ctrlTab_advanceFocus(aForward) {
    let selectedIndex = Array.indexOf(this.previews, this.selected);
    do {
      selectedIndex += aForward ? 1 : -1;
      if (selectedIndex < 0)
        selectedIndex = this.previews.length - 1;
      else if (selectedIndex >= this.previews.length)
        selectedIndex = 0;
    } while (this.previews[selectedIndex].hidden);

    if (this._selectedIndex == -1) {
      // Focus is already in the panel.
      this.previews[selectedIndex].focus();
    } else {
      this._selectedIndex = selectedIndex;
    }

    if (this.previews[selectedIndex]._tab) {
      gBrowser.warmupTab(this.previews[selectedIndex]._tab);
    }

    if (this._timer) {
      clearTimeout(this._timer);
      this._timer = null;
      this._openPanel();
    }
  },

  _mouseOverFocus: function ctrlTab_mouseOverFocus(aPreview) {
    if (this._trackMouseOver)
      aPreview.focus();
  },

  pick: function ctrlTab_pick(aPreview) {
    if (!this.tabCount)
      return;

    var select = (aPreview || this.selected);

    if (select == this.showAllButton)
      this.showAllTabs();
    else
      this.close(select._tab);
  },

  showAllTabs: function ctrlTab_showAllTabs(aPreview) {
    this.close();
    document.getElementById("Browser:ShowAllTabs").doCommand();
  },

  remove: function ctrlTab_remove(aPreview) {
    if (aPreview._tab)
      gBrowser.removeTab(aPreview._tab);
  },

  attachTab: function ctrlTab_attachTab(aTab, aPos) {
    if (aTab.closing)
      return;

    if (aPos == 0)
      this._recentlyUsedTabs.unshift(aTab);
    else if (aPos)
      this._recentlyUsedTabs.splice(aPos, 0, aTab);
    else
      this._recentlyUsedTabs.push(aTab);
  },

  detachTab: function ctrlTab_detachTab(aTab) {
    var i = this._recentlyUsedTabs.indexOf(aTab);
    if (i >= 0)
      this._recentlyUsedTabs.splice(i, 1);
  },

  open: function ctrlTab_open() {
    if (this.isOpen)
      return;

    document.addEventListener("keyup", this, true);

    this.updatePreviews();
    this._selectedIndex = 1;
    gBrowser.warmupTab(this.selected._tab);

    // Add a slight delay before showing the UI, so that a quick
    // "ctrl-tab" keypress just flips back to the MRU tab.
    this._timer = setTimeout(function(self) {
      self._timer = null;
      self._openPanel();
    }, 200, this);
  },

  _openPanel: function ctrlTab_openPanel() {
    tabPreviewPanelHelper.opening(this);

    this.panel.width = Math.min(screen.availWidth * .99,
                                this.canvasWidth * 1.25 * this.tabPreviewCount);
    var estimateHeight = this.canvasHeight * 1.25 + 75;
    this.panel.openPopupAtScreen(screen.availLeft + (screen.availWidth - this.panel.width) / 2,
                                 screen.availTop + (screen.availHeight - estimateHeight) / 2,
                                 false);
  },

  close: function ctrlTab_close(aTabToSelect) {
    if (!this.isOpen)
      return;

    if (this._timer) {
      clearTimeout(this._timer);
      this._timer = null;
      this.suspendGUI();
      if (aTabToSelect)
        gBrowser.selectedTab = aTabToSelect;
      return;
    }

    this.tabToSelect = aTabToSelect;
    this.panel.hidePopup();
  },

  setupGUI: function ctrlTab_setupGUI() {
    this.selected.focus();
    this._selectedIndex = -1;

    // Track mouse movement after a brief delay so that the item that happens
    // to be under the mouse pointer initially won't be selected unintentionally.
    this._trackMouseOver = false;
    setTimeout(function(self) {
      if (self.isOpen)
        self._trackMouseOver = true;
    }, 0, this);
  },

  suspendGUI: function ctrlTab_suspendGUI() {
    document.removeEventListener("keyup", this, true);

    for (let preview of this.previews) {
      this.updatePreview(preview, null);
    }
  },

  onKeyPress: function ctrlTab_onKeyPress(event) {
    var isOpen = this.isOpen;

    if (isOpen) {
      event.preventDefault();
      event.stopPropagation();
    }

    switch (event.keyCode) {
      case event.DOM_VK_TAB:
        if (event.ctrlKey && !event.altKey && !event.metaKey) {
          if (isOpen) {
            this.advanceFocus(!event.shiftKey);
          } else if (!event.shiftKey) {
            event.preventDefault();
            event.stopPropagation();
            let tabs = gBrowser.visibleTabs;
            if (tabs.length > 2) {
              this.open();
            } else if (tabs.length == 2) {
              let index = tabs[0].selected ? 1 : 0;
              gBrowser.selectedTab = tabs[index];
            }
          }
        }
        break;
      default:
        if (isOpen && event.ctrlKey) {
          if (event.keyCode == event.DOM_VK_DELETE) {
            this.remove(this.selected);
            break;
          }
          switch (event.charCode) {
            case this.keys.close:
              this.remove(this.selected);
              break;
            case this.keys.find:
            case this.keys.selectAll:
              this.showAllTabs();
              break;
          }
        }
    }
  },

  removeClosingTabFromUI: function ctrlTab_removeClosingTabFromUI(aTab) {
    if (this.tabCount == 2) {
      this.close();
      return;
    }

    this.updatePreviews();

    if (this.selected.hidden)
      this.advanceFocus(false);
    if (this.selected == this.showAllButton)
      this.advanceFocus(false);

    // If the current tab is removed, another tab can steal our focus.
    if (aTab.selected && this.panel.state == "open") {
      setTimeout(function(selected) {
        selected.focus();
      }, 0, this.selected);
    }
  },

  handleEvent: function ctrlTab_handleEvent(event) {
    switch (event.type) {
      case "SSWindowRestored":
        this._initRecentlyUsedTabs();
        break;
      case "TabAttrModified":
        // tab attribute modified (i.e. label, busy, image)
        // update preview only if tab attribute modified in the list
        if (event.detail.changed.some(
          (elem, ind, arr) => ["label", "busy", "image"].includes(elem))) {
          for (let i = this.previews.length - 1; i >= 0; i--) {
            if (this.previews[i]._tab && this.previews[i]._tab == event.target) {
              this.updatePreview(this.previews[i], event.target);
              break;
            }
          }
        }
        break;
      case "TabSelect":
        this.detachTab(event.target);
        this.attachTab(event.target, 0);
        break;
      case "TabOpen":
        this.attachTab(event.target, 1);
        break;
      case "TabClose":
        this.detachTab(event.target);
        if (this.isOpen)
          this.removeClosingTabFromUI(event.target);
        break;
      case "keypress":
        this.onKeyPress(event);
        break;
      case "keyup":
        if (event.keyCode == event.DOM_VK_CONTROL)
          this.pick();
        break;
      case "popupshowing":
        if (event.target.id == "menu_viewPopup")
          document.getElementById("menu_showAllTabs").hidden = !allTabs.canOpen;
        break;
    }
  },

  filterForThumbnailExpiration(aCallback) {
    // Save a few more thumbnails than we actually display, so that when tabs
    // are closed, the previews we add instead still get thumbnails.
    const extraThumbnails = 3;
    const thumbnailCount = Math.min(this.tabPreviewCount + extraThumbnails,
                                    this.tabCount);

    let urls = [];
    for (let i = 0; i < thumbnailCount; i++)
      urls.push(this.tabList[i].linkedBrowser.currentURI.spec);

    aCallback(urls);
  },

  _initRecentlyUsedTabs() {
    this._recentlyUsedTabs =
      Array.filter(gBrowser.tabs, tab => !tab.closing)
           .sort((tab1, tab2) => tab2.lastAccessed - tab1.lastAccessed);
  },

  _init: function ctrlTab__init(enable) {
    var toggleEventListener = enable ? "addEventListener" : "removeEventListener";

    window[toggleEventListener]("SSWindowRestored", this);

    var tabContainer = gBrowser.tabContainer;
    tabContainer[toggleEventListener]("TabOpen", this);
    tabContainer[toggleEventListener]("TabAttrModified", this);
    tabContainer[toggleEventListener]("TabSelect", this);
    tabContainer[toggleEventListener]("TabClose", this);

    document[toggleEventListener]("keypress", this);
    gBrowser.tabbox.handleCtrlTab = !enable;

    if (enable)
      PageThumbs.addExpirationFilter(this);
    else
      PageThumbs.removeExpirationFilter(this);

    // If we're not running, hide the "Show All Tabs" menu item,
    // as Shift+Ctrl+Tab will be handled by the tab bar.
    document.getElementById("menu_showAllTabs").hidden = !enable;
    document.getElementById("menu_viewPopup")[toggleEventListener]("popupshowing", this);

    // Also disable the <key> to ensure Shift+Ctrl+Tab never triggers
    // Show All Tabs.
    var key_showAllTabs = document.getElementById("key_showAllTabs");
    if (enable)
      key_showAllTabs.removeAttribute("disabled");
    else
      key_showAllTabs.setAttribute("disabled", "true");
  }
};


/**
 * All Tabs menu
 */
var allTabs = {
  get toolbarButton() {
    return document.getElementById("alltabs-button");
  },

  get canOpen() {
    return isElementVisible(this.toolbarButton);
  },

  open: function allTabs_open() {
    if (this.canOpen) {
      // Without setTimeout, the menupopup won't stay open when invoking
      // "View > Show All Tabs" and the menu bar auto-hides.
      setTimeout(() => {
        this.toolbarButton.open = true;
      }, 0);
    }
  }
};
PK
!<
��	�	7chrome/browser/content/browser/browser-customization.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// This file is loaded into the browser window scope.
/* eslint-env mozilla/browser-window */

/**
 * Customization handler prepares this browser window for entering and exiting
 * customization mode by handling customizationstarting and customizationending
 * events.
 */
var CustomizationHandler = {
  handleEvent(aEvent) {
    switch (aEvent.type) {
      case "customizationstarting":
        this._customizationStarting();
        break;
      case "customizationending":
        this._customizationEnding(aEvent.detail);
        break;
    }
  },

  isCustomizing() {
    return document.documentElement.hasAttribute("customizing");
  },

  _customizationStarting() {
    // Disable the toolbar context menu items
    let menubar = document.getElementById("main-menubar");
    for (let childNode of menubar.childNodes)
      childNode.setAttribute("disabled", true);

    let cmd = document.getElementById("cmd_CustomizeToolbars");
    cmd.setAttribute("disabled", "true");

    UpdateUrlbarSearchSplitterState();

    PlacesToolbarHelper.customizeStart();
  },

  _customizationEnding(aDetails) {
    // Update global UI elements that may have been added or removed
    if (aDetails.changed) {
      gURLBar = document.getElementById("urlbar");

      gHomeButton.updateTooltip();

      if (AppConstants.platform != "macosx")
        updateEditUIVisibility();

      // Hacky: update the PopupNotifications' object's reference to the iconBox,
      // if it already exists, since it may have changed if the URL bar was
      // added/removed.
      if (!window.__lookupGetter__("PopupNotifications")) {
        PopupNotifications.iconBox =
          document.getElementById("notification-popup-box");
      }

    }

    PlacesToolbarHelper.customizeDone();

    UpdateUrlbarSearchSplitterState();

    // Update the urlbar
    URLBarSetURI();
    XULBrowserWindow.asyncUpdateUI();

    // Re-enable parts of the UI we disabled during the dialog
    let menubar = document.getElementById("main-menubar");
    for (let childNode of menubar.childNodes)
      childNode.setAttribute("disabled", false);
    let cmd = document.getElementById("cmd_CustomizeToolbars");
    cmd.removeAttribute("disabled");

    gBrowser.selectedBrowser.focus();
  }
};
PK
!<����@@Bchrome/browser/content/browser/browser-data-submission-info-bar.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 LOGGER_NAME = "Toolkit.Telemetry";
const LOGGER_PREFIX = "DataNotificationInfoBar::";
/**
 * Represents an info bar that shows a data submission notification.
 */
var gDataNotificationInfoBar = {
  _OBSERVERS: [
    "datareporting:notify-data-policy:request",
    "datareporting:notify-data-policy:close",
  ],

  _DATA_REPORTING_NOTIFICATION: "data-reporting",

  get _notificationBox() {
    delete this._notificationBox;
    return this._notificationBox = document.getElementById("global-notificationbox");
  },

  get _log() {
    let Log = ChromeUtils.import("resource://gre/modules/Log.jsm", {}).Log;
    delete this._log;
    return this._log = Log.repository.getLoggerWithMessagePrefix(LOGGER_NAME, LOGGER_PREFIX);
  },

  init() {
    window.addEventListener("unload", () => {
      for (let o of this._OBSERVERS) {
        Services.obs.removeObserver(this, o);
      }
    });

    for (let o of this._OBSERVERS) {
      Services.obs.addObserver(this, o, true);
    }
  },

  _getDataReportingNotification(name = this._DATA_REPORTING_NOTIFICATION) {
    return this._notificationBox.getNotificationWithValue(name);
  },

  _displayDataPolicyInfoBar(request) {
    if (this._getDataReportingNotification()) {
      return;
    }

    let brandBundle = document.getElementById("bundle_brand");
    let appName = brandBundle.getString("brandShortName");
    let vendorName = brandBundle.getString("vendorShortName");

    let message = gNavigatorBundle.getFormattedString(
      "dataReportingNotification.message",
      [appName, vendorName]);

    this._actionTaken = false;

    let buttons = [{
      label: gNavigatorBundle.getString("dataReportingNotification.button.label"),
      accessKey: gNavigatorBundle.getString("dataReportingNotification.button.accessKey"),
      popup: null,
      callback: () => {
        this._actionTaken = true;
        window.openPreferences("privacy-reports", {origin: "dataReporting"});
      },
    }];

    this._log.info("Creating data reporting policy notification.");
    this._notificationBox.appendNotification(
      message,
      this._DATA_REPORTING_NOTIFICATION,
      null,
      this._notificationBox.PRIORITY_INFO_HIGH,
      buttons,
      event => {
        if (event == "removed") {
          Services.obs.notifyObservers(null, "datareporting:notify-data-policy:close");
        }
      }
    );
    // It is important to defer calling onUserNotifyComplete() until we're
    // actually sure the notification was displayed. If we ever called
    // onUserNotifyComplete() without showing anything to the user, that
    // would be very good for user choice. It may also have legal impact.
    request.onUserNotifyComplete();
  },

  _clearPolicyNotification() {
    let notification = this._getDataReportingNotification();
    if (notification) {
      this._log.debug("Closing notification.");
      notification.close();
    }
  },

  observe(subject, topic, data) {
    switch (topic) {
      case "datareporting:notify-data-policy:request":
        let request = subject.wrappedJSObject.object;
        try {
          this._displayDataPolicyInfoBar(request);
        } catch (ex) {
          request.onUserNotifyFailed(ex);
        }
        break;

      case "datareporting:notify-data-policy:close":
        // If this observer fires, it means something else took care of
        // responding. Therefore, we don't need to do anything. So, we
        // act like we took action and clear state.
        this._actionTaken = true;
        this._clearPolicyNotification();
        break;

      default:
    }
  },

  QueryInterface: XPCOMUtils.generateQI([
    Ci.nsIObserver,
    Ci.nsISupportsWeakReference,
  ]),
};
PK
!<��;��Z�Z/chrome/browser/content/browser/browser-feeds.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

ChromeUtils.defineModuleGetter(this, "DeferredTask",
                               "resource://gre/modules/DeferredTask.jsm");

const TYPE_MAYBE_FEED = "application/vnd.mozilla.maybe.feed";
const TYPE_MAYBE_AUDIO_FEED = "application/vnd.mozilla.maybe.audio.feed";
const TYPE_MAYBE_VIDEO_FEED = "application/vnd.mozilla.maybe.video.feed";

const PREF_SHOW_FIRST_RUN_UI = "browser.feeds.showFirstRunUI";

const PREF_SELECTED_APP = "browser.feeds.handlers.application";
const PREF_SELECTED_WEB = "browser.feeds.handlers.webservice";
const PREF_SELECTED_ACTION = "browser.feeds.handler";
const PREF_SELECTED_READER = "browser.feeds.handler.default";

const PREF_VIDEO_SELECTED_APP = "browser.videoFeeds.handlers.application";
const PREF_VIDEO_SELECTED_WEB = "browser.videoFeeds.handlers.webservice";
const PREF_VIDEO_SELECTED_ACTION = "browser.videoFeeds.handler";
const PREF_VIDEO_SELECTED_READER = "browser.videoFeeds.handler.default";

const PREF_AUDIO_SELECTED_APP = "browser.audioFeeds.handlers.application";
const PREF_AUDIO_SELECTED_WEB = "browser.audioFeeds.handlers.webservice";
const PREF_AUDIO_SELECTED_ACTION = "browser.audioFeeds.handler";
const PREF_AUDIO_SELECTED_READER = "browser.audioFeeds.handler.default";

const PREF_UPDATE_DELAY = 2000;

const SETTABLE_PREFS = new Set([
  PREF_VIDEO_SELECTED_ACTION,
  PREF_AUDIO_SELECTED_ACTION,
  PREF_SELECTED_ACTION,
  PREF_VIDEO_SELECTED_READER,
  PREF_AUDIO_SELECTED_READER,
  PREF_SELECTED_READER,
  PREF_VIDEO_SELECTED_WEB,
  PREF_AUDIO_SELECTED_WEB,
  PREF_SELECTED_WEB
]);

const EXECUTABLE_PREFS = new Set([
  PREF_SELECTED_APP,
  PREF_VIDEO_SELECTED_APP,
  PREF_AUDIO_SELECTED_APP
]);

const VALID_ACTIONS = new Set(["ask", "reader", "bookmarks"]);
const VALID_READERS = new Set(["web", "client", "default", "bookmarks"]);

XPCOMUtils.defineLazyPreferenceGetter(this, "SHOULD_LOG",
                                      "feeds.log", false);

function LOG(str) {
  if (SHOULD_LOG)
    dump("*** Feeds: " + str + "\n");
}

function getPrefActionForType(t) {
  switch (t) {
    case Ci.nsIFeed.TYPE_VIDEO:
      return PREF_VIDEO_SELECTED_ACTION;

    case Ci.nsIFeed.TYPE_AUDIO:
      return PREF_AUDIO_SELECTED_ACTION;

    default:
      return PREF_SELECTED_ACTION;
  }
}

function getPrefReaderForType(t) {
  switch (t) {
    case Ci.nsIFeed.TYPE_VIDEO:
      return PREF_VIDEO_SELECTED_READER;

    case Ci.nsIFeed.TYPE_AUDIO:
      return PREF_AUDIO_SELECTED_READER;

    default:
      return PREF_SELECTED_READER;
  }
}

function getPrefWebForType(t) {
  switch (t) {
    case Ci.nsIFeed.TYPE_VIDEO:
      return PREF_VIDEO_SELECTED_WEB;

    case Ci.nsIFeed.TYPE_AUDIO:
      return PREF_AUDIO_SELECTED_WEB;

    default:
      return PREF_SELECTED_WEB;
  }
}

function getPrefAppForType(t) {
  switch (t) {
    case Ci.nsIFeed.TYPE_VIDEO:
      return PREF_VIDEO_SELECTED_APP;

    case Ci.nsIFeed.TYPE_AUDIO:
      return PREF_AUDIO_SELECTED_APP;

    default:
      return PREF_SELECTED_APP;
  }
}

/**
 * Maps a feed type to a maybe-feed mimetype.
 */
function getMimeTypeForFeedType(aFeedType) {
  switch (aFeedType) {
    case Ci.nsIFeed.TYPE_VIDEO:
      return TYPE_MAYBE_VIDEO_FEED;

    case Ci.nsIFeed.TYPE_AUDIO:
      return TYPE_MAYBE_AUDIO_FEED;

    default:
      return TYPE_MAYBE_FEED;
  }
}

/**
 * The Feed Handler object manages discovery of RSS/ATOM feeds in web pages
 * and shows UI when they are discovered.
 */
var FeedHandler = {
  _prefChangeCallback: null,

  /** Called when the user clicks on the Subscribe to This Page... menu item,
   * or when the user clicks the feed button when the page contains multiple
   * feeds.
   * Builds a menu of unique feeds associated with the page, and if there
   * is only one, shows the feed inline in the browser window.
   * @param   container
   *          The feed list container (menupopup or subview) to be populated.
   * @param   isSubview
   *          Whether we're creating a subview (true) or menu (false/undefined)
   * @return  true if the menu/subview should be shown, false if there was only
   *          one feed and the feed should be shown inline in the browser
   *          window (do not show the menupopup/subview).
   */
  buildFeedList(container, isSubview) {
    let feeds = gBrowser.selectedBrowser.feeds;
    if (!isSubview && feeds == null) {
      // XXX hack -- menu opening depends on setting of an "open"
      // attribute, and the menu refuses to open if that attribute is
      // set (because it thinks it's already open).  onpopupshowing gets
      // called after the attribute is unset, and it doesn't get unset
      // if we return false.  so we unset it here; otherwise, the menu
      // refuses to work past this point.
      container.parentNode.removeAttribute("open");
      return false;
    }

    for (let i = container.childNodes.length - 1; i >= 0; --i) {
      let node = container.childNodes[i];
      if (isSubview && node.localName == "label")
        continue;
      container.removeChild(node);
    }

    if (!feeds || feeds.length <= 1)
      return false;

    // Build the menu showing the available feed choices for viewing.
    let itemNodeType = isSubview ? "toolbarbutton" : "menuitem";
    for (let feedInfo of feeds) {
      let item = document.createElement(itemNodeType);
      let baseTitle = feedInfo.title || feedInfo.href;
      item.setAttribute("label", baseTitle);
      item.setAttribute("feed", feedInfo.href);
      item.setAttribute("tooltiptext", feedInfo.href);
      item.setAttribute("crop", "center");
      let className = "feed-" + itemNodeType;
      if (isSubview) {
        className += " subviewbutton";
      }
      item.setAttribute("class", className);
      container.appendChild(item);
    }
    return true;
  },

  /**
   * Subscribe to a given feed.  Called when
   *   1. Page has a single feed and user clicks feed icon in location bar
   *   2. Page has a single feed and user selects Subscribe menu item
   *   3. Page has multiple feeds and user selects from feed icon popup (or subview)
   *   4. Page has multiple feeds and user selects from Subscribe submenu
   * @param   href
   *          The feed to subscribe to. May be null, in which case the
   *          event target's feed attribute is examined.
   * @param   event
   *          The event this method is handling. Used to decide where
   *          to open the preview UI. (Optional, unless href is null)
   */
  subscribeToFeed(href, event) {
    // Just load the feed in the content area to either subscribe or show the
    // preview UI
    if (!href)
      href = event.target.getAttribute("feed");
    urlSecurityCheck(href, gBrowser.contentPrincipal,
                     Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL);
    this.loadFeed(href, event);
  },

  loadFeed(href, event) {
    let feeds = gBrowser.selectedBrowser.feeds;
    try {
      openUILink(href, event, { ignoreAlt: true });
    } finally {
      // We might default to a livebookmarks modal dialog,
      // so reset that if the user happens to click it again
      gBrowser.selectedBrowser.feeds = feeds;
    }
  },

  get _feedMenuitem() {
    delete this._feedMenuitem;
    return this._feedMenuitem = document.getElementById("singleFeedMenuitemState");
  },

  get _feedMenupopup() {
    delete this._feedMenupopup;
    return this._feedMenupopup = document.getElementById("multipleFeedsMenuState");
  },

  /**
   * Update the browser UI to show whether or not feeds are available when
   * a page is loaded or the user switches tabs to a page that has feeds.
   */
  updateFeeds() {
    if (this._updateFeedTimeout)
      clearTimeout(this._updateFeedTimeout);

    let feeds = gBrowser.selectedBrowser.feeds;
    let haveFeeds = feeds && feeds.length > 0;

    let feedButton = document.getElementById("feed-button");
    if (feedButton) {
      if (haveFeeds) {
        feedButton.removeAttribute("disabled");
      } else {
        feedButton.setAttribute("disabled", "true");
      }
    }

    if (!haveFeeds) {
      this._feedMenuitem.setAttribute("disabled", "true");
      this._feedMenuitem.removeAttribute("hidden");
      this._feedMenupopup.setAttribute("hidden", "true");
      return;
    }

    if (feeds.length > 1) {
      this._feedMenuitem.setAttribute("hidden", "true");
      this._feedMenupopup.removeAttribute("hidden");
    } else {
      this._feedMenuitem.setAttribute("feed", feeds[0].href);
      this._feedMenuitem.removeAttribute("disabled");
      this._feedMenuitem.removeAttribute("hidden");
      this._feedMenupopup.setAttribute("hidden", "true");
    }
  },

  addFeed(link, browserForLink) {
    if (!browserForLink.feeds)
      browserForLink.feeds = [];

    urlSecurityCheck(link.href, gBrowser.contentPrincipal,
                     Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL);

    let feedURI = makeURI(link.href, document.characterSet);
    if (!/^https?$/.test(feedURI.scheme))
      return;

    browserForLink.feeds.push({ href: link.href, title: link.title });

    // If this addition was for the current browser, update the UI. For
    // background browsers, we'll update on tab switch.
    if (browserForLink == gBrowser.selectedBrowser) {
      // Batch updates to avoid updating the UI for multiple onLinkAdded events
      // fired within 100ms of each other.
      if (this._updateFeedTimeout)
        clearTimeout(this._updateFeedTimeout);
      this._updateFeedTimeout = setTimeout(this.updateFeeds.bind(this), 100);
    }
  },

   /**
   * Get the human-readable display name of a file. This could be the
   * application name.
   * @param   file
   *          A nsIFile to look up the name of
   * @return  The display name of the application represented by the file.
   */
  _getFileDisplayName(file) {
    switch (AppConstants.platform) {
      case "win":
        if (file instanceof Ci.nsILocalFileWin) {
          try {
            return file.getVersionInfoField("FileDescription");
          } catch (e) {}
        }
        break;
      case "macosx":
        if (file instanceof Ci.nsILocalFileMac) {
          try {
            return file.bundleDisplayName;
          } catch (e) {}
        }
        break;
    }

    return file.leafName;
  },

  _chooseClientApp(aTitle, aTypeName, aBrowser) {
    const prefName = getPrefAppForType(aTypeName);
    let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);

    fp.init(window, aTitle, Ci.nsIFilePicker.modeOpen);
    fp.appendFilters(Ci.nsIFilePicker.filterApps);

    fp.open((aResult) => {
      if (aResult == Ci.nsIFilePicker.returnOK) {
        let selectedApp = fp.file;
        if (selectedApp) {
          // XXXben - we need to compare this with the running instance
          //          executable just don't know how to do that via script
          // XXXmano TBD: can probably add this to nsIShellService
          let appName = "";
          switch (AppConstants.platform) {
            case "win":
              appName = AppConstants.MOZ_APP_NAME + ".exe";
              break;
            case "macosx":
              appName = AppConstants.MOZ_MACBUNDLE_NAME;
              break;
            default:
              appName = AppConstants.MOZ_APP_NAME + "-bin";
              break;
          }

          if (fp.file.leafName != appName) {
            Services.prefs.setComplexValue(prefName, Ci.nsIFile, selectedApp);
            aBrowser.messageManager.sendAsyncMessage("FeedWriter:SetApplicationLauncherMenuItem",
                                                    { name: this._getFileDisplayName(selectedApp),
                                                      type: "SelectedAppMenuItem" });
          }
        }
      }
    });

  },

  executeClientApp(aSpec, aTitle, aSubtitle, aFeedHandler) {
    // aFeedHandler is either "default", indicating the system default reader, or a pref-name containing
    // an nsIFile pointing to the feed handler's executable.

    let clientApp = null;
    if (aFeedHandler == "default") {
      clientApp = Cc["@mozilla.org/browser/shell-service;1"]
                    .getService(Ci.nsIShellService)
                    .defaultFeedReader;
    } else {
      clientApp = Services.prefs.getComplexValue(aFeedHandler, Ci.nsIFile);
    }

    // For the benefit of applications that might know how to deal with more
    // URLs than just feeds, send feed: URLs in the following format:
    //
    // http urls: replace scheme with feed, e.g.
    // http://foo.com/index.rdf -> feed://foo.com/index.rdf
    // other urls: prepend feed: scheme, e.g.
    // https://foo.com/index.rdf -> feed:https://foo.com/index.rdf
    let feedURI = Services.io.newURI(aSpec);
    if (feedURI.schemeIs("http")) {
      feedURI = feedURI.mutate()
                       .setScheme("feed")
                       .finalize();
      aSpec = feedURI.spec;
    } else {
      aSpec = "feed:" + aSpec;
    }

    // Retrieving the shell service might fail on some systems, most
    // notably systems where GNOME is not installed.
    try {
      let ss = Cc["@mozilla.org/browser/shell-service;1"]
                 .getService(Ci.nsIShellService);
      ss.openApplicationWithURI(clientApp, aSpec);
    } catch (e) {
      // If we couldn't use the shell service, fallback to using a
      // nsIProcess instance
      let p = Cc["@mozilla.org/process/util;1"]
                .createInstance(Ci.nsIProcess);
      p.init(clientApp);
      p.run(false, [aSpec], 1);
    }
  },

  // nsISupports

  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                         Ci.nsISupportsWeakReference]),


  init() {
    window.messageManager.addMessageListener("FeedWriter:ChooseClientApp", this);
    window.messageManager.addMessageListener("FeedWriter:GetSubscriptionUI", this);
    window.messageManager.addMessageListener("FeedWriter:SetFeedPrefsAndSubscribe", this);
    window.messageManager.addMessageListener("FeedWriter:ShownFirstRun", this);

    Services.ppmm.addMessageListener("FeedConverter:ExecuteClientApp", this);

    const prefs = Services.prefs;
    prefs.addObserver(PREF_SELECTED_ACTION, this, true);
    prefs.addObserver(PREF_SELECTED_READER, this, true);
    prefs.addObserver(PREF_SELECTED_WEB, this, true);
    prefs.addObserver(PREF_VIDEO_SELECTED_ACTION, this, true);
    prefs.addObserver(PREF_VIDEO_SELECTED_READER, this, true);
    prefs.addObserver(PREF_VIDEO_SELECTED_WEB, this, true);
    prefs.addObserver(PREF_AUDIO_SELECTED_ACTION, this, true);
    prefs.addObserver(PREF_AUDIO_SELECTED_READER, this, true);
    prefs.addObserver(PREF_AUDIO_SELECTED_WEB, this, true);
  },

  uninit() {
    Services.ppmm.removeMessageListener("FeedConverter:ExecuteClientApp", this);

    this._prefChangeCallback = null;
  },

  // nsIObserver
  observe(subject, topic, data) {
    if (topic == "nsPref:changed") {
      LOG(`Pref changed ${data}`);
      if (this._prefChangeCallback) {
        this._prefChangeCallback.disarm();
      }
      // Multiple prefs are set at the same time, debounce to reduce noise
      // This can happen in one feed and we want to message all feed pages
      this._prefChangeCallback = new DeferredTask(() => {
        this._prefChanged(data);
      }, PREF_UPDATE_DELAY);
      this._prefChangeCallback.arm();
    }
  },

  _prefChanged(prefName) {
    // Don't observe for PREF_*SELECTED_APP as user likely just picked one
    // That is also handled by SetApplicationLauncherMenuItem call
    // Rather than the others which happen on subscription
    switch (prefName) {
      case PREF_SELECTED_READER:
      case PREF_SELECTED_WEB:
      case PREF_VIDEO_SELECTED_READER:
      case PREF_VIDEO_SELECTED_WEB:
      case PREF_AUDIO_SELECTED_READER:
      case PREF_AUDIO_SELECTED_WEB:
      case PREF_SELECTED_ACTION:
      case PREF_VIDEO_SELECTED_ACTION:
      case PREF_AUDIO_SELECTED_ACTION:
        const response = {
         default: this._getReaderForType(Ci.nsIFeed.TYPE_FEED),
         [Ci.nsIFeed.TYPE_AUDIO]: this._getReaderForType(Ci.nsIFeed.TYPE_AUDIO),
         [Ci.nsIFeed.TYPE_VIDEO]: this._getReaderForType(Ci.nsIFeed.TYPE_VIDEO)
        };
        Services.mm.broadcastAsyncMessage("FeedWriter:PreferenceUpdated",
                                          response);
        break;
    }
  },

  _initSubscriptionUIResponse(feedType) {
    const wccr = Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"].
               getService(Ci.nsIWebContentConverterService);
    const handlersRaw = wccr.getContentHandlers(getMimeTypeForFeedType(feedType));
    const handlers = [];
    for (let handler of handlersRaw) {
      LOG(`Handler found: ${handler}`);
      handlers.push({
        name: handler.name,
        uri: handler.uri
      });
    }
    let showFirstRunUI = true;
    // eslint-disable-next-line mozilla/use-default-preference-values
    try {
      showFirstRunUI = Services.prefs.getBoolPref(PREF_SHOW_FIRST_RUN_UI);
    } catch (ex) { }
    const response = { handlers, showFirstRunUI };
    let selectedClientApp;
    const feedTypePref = getPrefAppForType(feedType);
    try {
      selectedClientApp = Services.prefs.getComplexValue(feedTypePref, Ci.nsIFile);
    } catch (ex) {
      // Just do nothing, then we won't bother populating
    }

    let defaultClientApp = null;
    try {
      // This can sometimes not exist
      defaultClientApp = Cc["@mozilla.org/browser/shell-service;1"]
                           .getService(Ci.nsIShellService)
                           .defaultFeedReader;
    } catch (ex) {
      // Just do nothing, then we don't bother populating
    }

    if (selectedClientApp && selectedClientApp.exists()) {
      if (defaultClientApp && selectedClientApp.path != defaultClientApp.path) {
        // Only set the default menu item if it differs from the selected one
        response.defaultMenuItem = this._getFileDisplayName(defaultClientApp);
      }
      response.selectedMenuItem = this._getFileDisplayName(selectedClientApp);
    }
    response.reader = this._getReaderForType(feedType);
    return response;
  },

  _setPref(aPrefName, aPrefValue, aIsComplex = false) {
    LOG(`FeedWriter._setPref ${aPrefName}`);
    // Ensure we have a pref that is settable
    if (aPrefName && SETTABLE_PREFS.has(aPrefName)) {
      if (aIsComplex) {
        Services.prefs.setStringPref(aPrefName, aPrefValue);
      } else {
        Services.prefs.setCharPref(aPrefName, aPrefValue);
      }
    } else {
      LOG(`FeedWriter._setPref ${aPrefName} not allowed`);
    }
  },

  _getReaderForType(feedType) {
    let prefs = Services.prefs;
    let handler = "bookmarks";
    let url;
    // eslint-disable-next-line mozilla/use-default-preference-values
    try {
      handler = prefs.getCharPref(getPrefReaderForType(feedType));
    } catch (ex) { }

    if (handler === "web") {
      try {
        url = prefs.getStringPref(getPrefWebForType(feedType));
      } catch (ex) {
        LOG("FeedWriter._setSelectedHandler: invalid or no handler in prefs");
        url = null;
      }
    }
    const alwaysUse = this._getAlwaysUseState(feedType);
    const action = prefs.getCharPref(getPrefActionForType(feedType));
    return { handler, url, alwaysUse, action };
  },

  _getAlwaysUseState(feedType) {
    try {
      return Services.prefs.getCharPref(getPrefActionForType(feedType)) != "ask";
    } catch (ex) { }
    return false;
  },

  receiveMessage(msg) {
    let handler;
    switch (msg.name) {
      case "FeedWriter:GetSubscriptionUI":
        const response = this._initSubscriptionUIResponse(msg.data.feedType);
        msg.target.messageManager
           .sendAsyncMessage("FeedWriter:GetSubscriptionUIResponse",
                            response);
        break;
      case "FeedWriter:ChooseClientApp":
        this._chooseClientApp(msg.data.title, msg.data.feedType, msg.target);
        break;
      case "FeedWriter:ShownFirstRun":
        Services.prefs.setBoolPref(PREF_SHOW_FIRST_RUN_UI, false);
        break;
      case "FeedWriter:SetFeedPrefsAndSubscribe":
        const settings = msg.data;
        if (!settings.action || !VALID_ACTIONS.has(settings.action)) {
          LOG(`Invalid action ${settings.action}`);
          return;
        }
        if (!settings.reader || !VALID_READERS.has(settings.reader)) {
          LOG(`Invalid reader ${settings.reader}`);
          return;
        }

        Services.telemetry.scalarAdd("browser.feeds.feed_subscribed", 1);

        const actionPref = getPrefActionForType(settings.feedType);
        this._setPref(actionPref, settings.action);
        const readerPref = getPrefReaderForType(settings.feedType);
        this._setPref(readerPref, settings.reader);
        handler = null;

        switch (settings.reader) {
          case "web":
            // This is a web set URI by content using window.registerContentHandler()
            // Lets make sure we know about it before setting it
            const webPref = getPrefWebForType(settings.feedType);
            let wccr = Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"].
                       getService(Ci.nsIWebContentConverterService);
            // If the user provided an invalid web URL this function won't give us a reference
            handler = wccr.getWebContentHandlerByURI(getMimeTypeForFeedType(settings.feedType), settings.uri);
            if (handler) {
              this._setPref(webPref, settings.uri, true);
              if (settings.useAsDefault) {
                wccr.setAutoHandler(getMimeTypeForFeedType(settings.feedType), handler);
              }
              msg.target.messageManager
                 .sendAsyncMessage("FeedWriter:SetFeedPrefsAndSubscribeResponse",
                                  { redirect: handler.getHandlerURI(settings.feedLocation) });
            } else {
              LOG(`No handler found for web ${settings.feedType} ${settings.uri}`);
            }
            break;
          default:
            const feedService = Cc["@mozilla.org/browser/feeds/result-service;1"].
                                getService(Ci.nsIFeedResultService);

            feedService.addToClientReader(settings.feedLocation,
                                          settings.feedTitle,
                                          settings.feedSubtitle,
                                          settings.feedType,
                                          settings.reader);
         }
         break;
      case "FeedConverter:ExecuteClientApp":
        // Always check feedHandler is from a set array of executable prefs
        if (EXECUTABLE_PREFS.has(msg.data.feedHandler)) {
          this.executeClientApp(msg.data.spec, msg.data.title,
                                msg.data.subtitle, msg.data.feedHandler);
        } else {
          LOG(`FeedConverter:ExecuteClientApp - Will not exec ${msg.data.feedHandler}`);
        }
        break;
    }
  },
};
PK
!<
�Ef�]�]Bchrome/browser/content/browser/browser-fullScreenAndPointerLock.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// This file is loaded into the browser window scope.
/* eslint-env mozilla/browser-window */

var PointerlockFsWarning = {

  _element: null,
  _origin: null,

  /**
   * Timeout object for managing timeout request. If it is started when
   * the previous call hasn't finished, it would automatically cancelled
   * the previous one.
   */
  Timeout: class {
    constructor(func, delay) {
      this._id = 0;
      this._func = func;
      this._delay = delay;
    }
    start() {
      this.cancel();
      this._id = setTimeout(() => this._handle(), this._delay);
    }
    cancel() {
      if (this._id) {
        clearTimeout(this._id);
        this._id = 0;
      }
    }
    _handle() {
      this._id = 0;
      this._func();
    }
    get delay() {
      return this._delay;
    }
  },

  showPointerLock(aOrigin) {
    if (!document.fullscreen) {
      let timeout = Services.prefs.getIntPref("pointer-lock-api.warning.timeout");
      this.show(aOrigin, "pointerlock-warning", timeout, 0);
    }
  },

  showFullScreen(aOrigin) {
    let timeout = Services.prefs.getIntPref("full-screen-api.warning.timeout");
    let delay = Services.prefs.getIntPref("full-screen-api.warning.delay");
    this.show(aOrigin, "fullscreen-warning", timeout, delay);
  },

  // Shows a warning that the site has entered fullscreen or
  // pointer lock for a short duration.
  show(aOrigin, elementId, timeout, delay) {

    if (!this._element) {
      this._element = document.getElementById(elementId);
      // Setup event listeners
      this._element.addEventListener("transitionend", this);
      window.addEventListener("mousemove", this, true);
      // The timeout to hide the warning box after a while.
      this._timeoutHide = new this.Timeout(() => {
        this._state = "hidden";
      }, timeout);
      // The timeout to show the warning box when the pointer is at the top
      this._timeoutShow = new this.Timeout(() => {
        this._state = "ontop";
        this._timeoutHide.start();
      }, delay);
    }

    // Set the strings on the warning UI.
    if (aOrigin) {
      this._origin = aOrigin;
    }
    let uri = Services.io.newURI(this._origin);
    let host = null;
    try {
      host = uri.host;
    } catch (e) { }
    let textElem = this._element.querySelector(".pointerlockfswarning-domain-text");
    if (!host) {
      textElem.setAttribute("hidden", true);
    } else {
      textElem.removeAttribute("hidden");
      let hostElem = this._element.querySelector(".pointerlockfswarning-domain");
      // Document's principal's URI has a host. Display a warning including it.
      let utils = {};
      ChromeUtils.import("resource://gre/modules/DownloadUtils.jsm", utils);
      hostElem.textContent = utils.DownloadUtils.getURIHost(uri.spec)[0];
    }

    this._element.dataset.identity =
      gIdentityHandler.pointerlockFsWarningClassName;

    // User should be allowed to explicitly disable
    // the prompt if they really want.
    if (this._timeoutHide.delay <= 0) {
      return;
    }

    // Explicitly set the last state to hidden to avoid the warning
    // box being hidden immediately because of mousemove.
    this._state = "onscreen";
    this._lastState = "hidden";
    this._timeoutHide.start();
  },

  close() {
    if (!this._element) {
      return;
    }
    // Cancel any pending timeout
    this._timeoutHide.cancel();
    this._timeoutShow.cancel();
    // Reset state of the warning box
    this._state = "hidden";
    this._element.setAttribute("hidden", true);
    // Remove all event listeners
    this._element.removeEventListener("transitionend", this);
    window.removeEventListener("mousemove", this, true);
    // Clear fields
    this._element = null;
    this._timeoutHide = null;
    this._timeoutShow = null;

    // Ensure focus switches away from the (now hidden) warning box.
    // If the user clicked buttons in the warning box, it would have
    // been focused, and any key events would be directed at the (now
    // hidden) chrome document instead of the target document.
    gBrowser.selectedBrowser.focus();
  },

  // State could be one of "onscreen", "ontop", "hiding", and
  // "hidden". Setting the state to "onscreen" and "ontop" takes
  // effect immediately, while setting it to "hidden" actually
  // turns the state to "hiding" before the transition finishes.
  _lastState: null,
  _STATES: ["hidden", "ontop", "onscreen"],
  get _state() {
    for (let state of this._STATES) {
      if (this._element.hasAttribute(state)) {
        return state;
      }
    }
    return "hiding";
  },
  set _state(newState) {
    let currentState = this._state;
    if (currentState == newState) {
      return;
    }
    if (currentState != "hiding") {
      this._lastState = currentState;
      this._element.removeAttribute(currentState);
    }
    if (newState != "hidden") {
      if (currentState != "hidden") {
        this._element.setAttribute(newState, true);
      } else {
        // When the previous state is hidden, the display was none,
        // thus no box was constructed. We need to wait for the new
        // display value taking effect first, otherwise, there won't
        // be any transition. Since requestAnimationFrame callback is
        // generally triggered before any style flush and layout, we
        // should wait for the second animation frame.
        requestAnimationFrame(() => {
          requestAnimationFrame(() => {
            if (this._element) {
              this._element.setAttribute(newState, true);
            }
          });
        });
      }
    }
  },

  handleEvent(event) {
    switch (event.type) {
    case "mousemove": {
      let state = this._state;
      if (state == "hidden") {
        // If the warning box is currently hidden, show it after
        // a short delay if the pointer is at the top.
        if (event.clientY != 0) {
          this._timeoutShow.cancel();
        } else if (this._timeoutShow.delay >= 0) {
          this._timeoutShow.start();
        }
      } else {
        let elemRect = this._element.getBoundingClientRect();
        if (state == "hiding" && this._lastState != "hidden") {
          // If we are on the hiding transition, and the pointer
          // moved near the box, restore to the previous state.
          if (event.clientY <= elemRect.bottom + 50) {
            this._state = this._lastState;
            this._timeoutHide.start();
          }
        } else if (state == "ontop" || this._lastState != "hidden") {
          // State being "ontop" or the previous state not being
          // "hidden" indicates this current warning box is shown
          // in response to user's action. Hide it immediately when
          // the pointer leaves that area.
          if (event.clientY > elemRect.bottom + 50) {
            this._state = "hidden";
            this._timeoutHide.cancel();
          }
        }
      }
      break;
    }
    case "transitionend": {
      if (this._state == "hiding") {
        this._element.setAttribute("hidden", true);
      }
      break;
    }
    }
  }
};

var PointerLock = {

  init() {
    window.messageManager.addMessageListener("PointerLock:Entered", this);
    window.messageManager.addMessageListener("PointerLock:Exited", this);
  },

  receiveMessage(aMessage) {
    switch (aMessage.name) {
      case "PointerLock:Entered": {
        PointerlockFsWarning.showPointerLock(aMessage.data.originNoSuffix);
        break;
      }
      case "PointerLock:Exited": {
        PointerlockFsWarning.close();
        break;
      }
    }
  }
};

var FullScreen = {
  _MESSAGES: [
    "DOMFullscreen:Request",
    "DOMFullscreen:NewOrigin",
    "DOMFullscreen:Exit",
    "DOMFullscreen:Painted",
  ],

  init() {
    // called when we go into full screen, even if initiated by a web page script
    window.addEventListener("fullscreen", this, true);
    window.addEventListener("willenterfullscreen", this, true);
    window.addEventListener("willexitfullscreen", this, true);
    window.addEventListener("MozDOMFullscreen:Entered", this,
                            /* useCapture */ true,
                            /* wantsUntrusted */ false);
    window.addEventListener("MozDOMFullscreen:Exited", this,
                            /* useCapture */ true,
                            /* wantsUntrusted */ false);
    for (let type of this._MESSAGES) {
      window.messageManager.addMessageListener(type, this);
    }

    if (window.fullScreen)
      this.toggle();
  },

  uninit() {
    for (let type of this._MESSAGES) {
      window.messageManager.removeMessageListener(type, this);
    }
    this.cleanup();
  },

  willToggle(aWillEnterFullscreen) {
    if (aWillEnterFullscreen) {
      document.documentElement.setAttribute("inFullscreen", true);
    } else {
      document.documentElement.removeAttribute("inFullscreen");
    }
  },

  toggle() {
    var enterFS = window.fullScreen;

    // Toggle the View:FullScreen command, which controls elements like the
    // fullscreen menuitem, and menubars.
    let fullscreenCommand = document.getElementById("View:FullScreen");
    if (enterFS) {
      fullscreenCommand.setAttribute("checked", enterFS);
    } else {
      fullscreenCommand.removeAttribute("checked");
    }

    if (AppConstants.platform == "macosx") {
      // Make sure the menu items are adjusted.
      document.getElementById("enterFullScreenItem").hidden = enterFS;
      document.getElementById("exitFullScreenItem").hidden = !enterFS;
    }

    if (!this._fullScrToggler) {
      this._fullScrToggler = document.getElementById("fullscr-toggler");
      this._fullScrToggler.addEventListener("mouseover", this._expandCallback);
      this._fullScrToggler.addEventListener("dragenter", this._expandCallback);
      this._fullScrToggler.addEventListener("touchmove", this._expandCallback, {passive: true});
    }

    if (enterFS) {
      gNavToolbox.setAttribute("inFullscreen", true);
      document.documentElement.setAttribute("inFullscreen", true);
      if (!document.fullscreenElement && this.useLionFullScreen)
        document.documentElement.setAttribute("OSXLionFullscreen", true);
    } else {
      gNavToolbox.removeAttribute("inFullscreen");
      document.documentElement.removeAttribute("inFullscreen");
      document.documentElement.removeAttribute("OSXLionFullscreen");
    }

    if (!document.fullscreenElement)
      this._updateToolbars(enterFS);

    if (enterFS) {
      document.addEventListener("keypress", this._keyToggleCallback);
      document.addEventListener("popupshown", this._setPopupOpen);
      document.addEventListener("popuphidden", this._setPopupOpen);
      // In DOM fullscreen mode, we hide toolbars with CSS
      if (!document.fullscreenElement)
        this.hideNavToolbox(true);
    } else {
      this.showNavToolbox(false);
      // This is needed if they use the context menu to quit fullscreen
      this._isPopupOpen = false;
      this.cleanup();
      // In TabsInTitlebar._update(), we cancel the appearance update on
      // resize event for exiting fullscreen, since that happens before we
      // change the UI here in the "fullscreen" event. Hence we need to
      // call it here to ensure the appearance is properly updated. See
      // TabsInTitlebar._update() and bug 1173768.
      TabsInTitlebar.updateAppearance(true);
    }

    if (enterFS && !document.fullscreenElement) {
      Services.telemetry.getHistogramById("FX_BROWSER_FULLSCREEN_USED")
                        .add(1);
    }
  },

  exitDomFullScreen() {
    document.exitFullscreen();
  },

  handleEvent(event) {
    switch (event.type) {
      case "willenterfullscreen":
        this.willToggle(true);
        break;
      case "willexitfullscreen":
        this.willToggle(false);
        break;
      case "fullscreen":
        this.toggle();
        break;
      case "MozDOMFullscreen:Entered": {
        // The event target is the element which requested the DOM
        // fullscreen. If we were entering DOM fullscreen for a remote
        // browser, the target would be the browser which was the parameter of
        // `remoteFrameFullscreenChanged` call. If the fullscreen
        // request was initiated from an in-process browser, we need
        // to get its corresponding browser here.
        let browser;
        if (event.target.ownerGlobal == window) {
          browser = event.target;
        } else {
          let topWin = event.target.ownerGlobal.top;
          browser = gBrowser.getBrowserForContentWindow(topWin);
        }
        TelemetryStopwatch.start("FULLSCREEN_CHANGE_MS");
        this.enterDomFullscreen(browser);
        break;
      }
      case "MozDOMFullscreen:Exited":
        TelemetryStopwatch.start("FULLSCREEN_CHANGE_MS");
        this.cleanupDomFullscreen();
        break;
    }
  },

  receiveMessage(aMessage) {
    let browser = aMessage.target;
    switch (aMessage.name) {
      case "DOMFullscreen:Request": {
        this._windowUtils.remoteFrameFullscreenChanged(browser);
        break;
      }
      case "DOMFullscreen:NewOrigin": {
        // Don't show the warning if we've already exited fullscreen.
        if (document.fullscreen) {
          PointerlockFsWarning.showFullScreen(aMessage.data.originNoSuffix);
        }
        break;
      }
      case "DOMFullscreen:Exit": {
        this._windowUtils.remoteFrameFullscreenReverted();
        break;
      }
      case "DOMFullscreen:Painted": {
        Services.obs.notifyObservers(window, "fullscreen-painted");
        TelemetryStopwatch.finish("FULLSCREEN_CHANGE_MS");
        break;
      }
    }
  },

  enterDomFullscreen(aBrowser) {

    if (!document.fullscreenElement) {
      return;
    }

    // If we have a current pointerlock warning shown then hide it
    // before transition.
    PointerlockFsWarning.close();

    // If it is a remote browser, send a message to ask the content
    // to enter fullscreen state. We don't need to do so if it is an
    // in-process browser, since all related document should have
    // entered fullscreen state at this point.
    // This should be done before the active tab check below to ensure
    // that the content document handles the pending request. Doing so
    // before the check is fine since we also check the activeness of
    // the requesting document in content-side handling code.
    if (this._isRemoteBrowser(aBrowser)) {
      aBrowser.messageManager.sendAsyncMessage("DOMFullscreen:Entered");
    }

    // If we've received a fullscreen notification, we have to ensure that the
    // element that's requesting fullscreen belongs to the browser that's currently
    // active. If not, we exit fullscreen since the "full-screen document" isn't
    // actually visible now.
    if (!aBrowser || gBrowser.selectedBrowser != aBrowser ||
        // The top-level window has lost focus since the request to enter
        // full-screen was made. Cancel full-screen.
        Services.focus.activeWindow != window) {
      // This function is called synchronously in fullscreen change, so
      // we have to avoid calling exitFullscreen synchronously here.
      setTimeout(() => document.exitFullscreen(), 0);
      return;
    }

    document.documentElement.setAttribute("inDOMFullscreen", true);

    if (gFindBarInitialized) {
      gFindBar.close(true);
    }

    // Exit DOM full-screen mode upon open, close, or change tab.
    gBrowser.tabContainer.addEventListener("TabOpen", this.exitDomFullScreen);
    gBrowser.tabContainer.addEventListener("TabClose", this.exitDomFullScreen);
    gBrowser.tabContainer.addEventListener("TabSelect", this.exitDomFullScreen);

    // Add listener to detect when the fullscreen window is re-focused.
    // If a fullscreen window loses focus, we show a warning when the
    // fullscreen window is refocused.
    window.addEventListener("activate", this);
  },

  cleanup() {
    if (!window.fullScreen) {
      MousePosTracker.removeListener(this);
      document.removeEventListener("keypress", this._keyToggleCallback);
      document.removeEventListener("popupshown", this._setPopupOpen);
      document.removeEventListener("popuphidden", this._setPopupOpen);
    }
  },

  cleanupDomFullscreen() {
    window.messageManager
          .broadcastAsyncMessage("DOMFullscreen:CleanUp");

    PointerlockFsWarning.close();
    gBrowser.tabContainer.removeEventListener("TabOpen", this.exitDomFullScreen);
    gBrowser.tabContainer.removeEventListener("TabClose", this.exitDomFullScreen);
    gBrowser.tabContainer.removeEventListener("TabSelect", this.exitDomFullScreen);
    window.removeEventListener("activate", this);

    document.documentElement.removeAttribute("inDOMFullscreen");
  },

  _isRemoteBrowser(aBrowser) {
    return gMultiProcessBrowser && aBrowser.getAttribute("remote") == "true";
  },

  get _windowUtils() {
    return window.QueryInterface(Ci.nsIInterfaceRequestor)
                 .getInterface(Ci.nsIDOMWindowUtils);
  },

  getMouseTargetRect() {
    return this._mouseTargetRect;
  },

  // Event callbacks
  _expandCallback() {
    FullScreen.showNavToolbox();
  },
  onMouseEnter() {
    FullScreen.hideNavToolbox();
  },
  _keyToggleCallback(aEvent) {
    // if we can use the keyboard (eg Ctrl+L or Ctrl+E) to open the toolbars, we
    // should provide a way to collapse them too.
    if (aEvent.keyCode == aEvent.DOM_VK_ESCAPE) {
      FullScreen.hideNavToolbox();
    } else if (aEvent.keyCode == aEvent.DOM_VK_F6) {
      // F6 is another shortcut to the address bar, but its not covered in OpenLocation()
      FullScreen.showNavToolbox();
    }
  },

  // Checks whether we are allowed to collapse the chrome
  _isPopupOpen: false,
  _isChromeCollapsed: false,
  _safeToCollapse() {
    if (!Services.prefs.getBoolPref("browser.fullscreen.autohide"))
      return false;

    // a popup menu is open in chrome: don't collapse chrome
    if (this._isPopupOpen)
      return false;

    // On OS X Lion we don't want to hide toolbars.
    if (this.useLionFullScreen)
      return false;

    // a textbox in chrome is focused (location bar anyone?): don't collapse chrome
    if (document.commandDispatcher.focusedElement &&
        document.commandDispatcher.focusedElement.ownerDocument == document &&
        document.commandDispatcher.focusedElement.localName == "input") {
      return false;
    }

    return true;
  },

  _setPopupOpen(aEvent) {
    // Popups should only veto chrome collapsing if they were opened when the chrome was not collapsed.
    // Otherwise, they would not affect chrome and the user would expect the chrome to go away.
    // e.g. we wouldn't want the autoscroll icon firing this event, so when the user
    // toggles chrome when moving mouse to the top, it doesn't go away again.
    if (aEvent.type == "popupshown" && !FullScreen._isChromeCollapsed &&
        aEvent.target.localName != "tooltip" && aEvent.target.localName != "window" &&
        aEvent.target.getAttribute("nopreventnavboxhide") != "true")
      FullScreen._isPopupOpen = true;
    else if (aEvent.type == "popuphidden" && aEvent.target.localName != "tooltip" &&
             aEvent.target.localName != "window") {
      FullScreen._isPopupOpen = false;
      // Try again to hide toolbar when we close the popup.
      FullScreen.hideNavToolbox(true);
    }
  },

  get navToolboxHidden() {
    return this._isChromeCollapsed;
  },

  // Autohide helpers for the context menu item
  getAutohide(aItem) {
    aItem.setAttribute("checked", Services.prefs.getBoolPref("browser.fullscreen.autohide"));
  },
  setAutohide() {
    Services.prefs.setBoolPref("browser.fullscreen.autohide", !Services.prefs.getBoolPref("browser.fullscreen.autohide"));
    // Try again to hide toolbar when we change the pref.
    FullScreen.hideNavToolbox(true);
  },

  showNavToolbox(trackMouse = true) {
    this._fullScrToggler.hidden = true;
    gNavToolbox.removeAttribute("fullscreenShouldAnimate");
    gNavToolbox.style.marginTop = "";

    if (!this._isChromeCollapsed) {
      return;
    }

    // Track whether mouse is near the toolbox
    if (trackMouse && !this.useLionFullScreen) {
      let rect = gBrowser.mPanelContainer.getBoundingClientRect();
      this._mouseTargetRect = {
        top: rect.top + 50,
        bottom: rect.bottom,
        left: rect.left,
        right: rect.right
      };
      MousePosTracker.addListener(this);
    }

    this._isChromeCollapsed = false;
    Services.obs.notifyObservers(null, "fullscreen-nav-toolbox", "shown");
  },

  hideNavToolbox(aAnimate = false) {
    if (this._isChromeCollapsed || !this._safeToCollapse())
      return;

    this._fullScrToggler.hidden = false;

    if (aAnimate && Services.prefs.getBoolPref("toolkit.cosmeticAnimations.enabled")) {
      gNavToolbox.setAttribute("fullscreenShouldAnimate", true);
      // Hide the fullscreen toggler until the transition ends.
      let listener = () => {
        gNavToolbox.removeEventListener("transitionend", listener, true);
        if (this._isChromeCollapsed)
          this._fullScrToggler.hidden = false;
      };
      gNavToolbox.addEventListener("transitionend", listener, true);
      this._fullScrToggler.hidden = true;
    }

    gNavToolbox.style.marginTop =
      -gNavToolbox.getBoundingClientRect().height + "px";
    this._isChromeCollapsed = true;
    Services.obs.notifyObservers(null, "fullscreen-nav-toolbox", "hidden");

    MousePosTracker.removeListener(this);
  },

  _updateToolbars(aEnterFS) {
    for (let el of document.querySelectorAll("toolbar[fullscreentoolbar=true]")) {
      if (aEnterFS) {
        // Give the main nav bar and the tab bar the fullscreen context menu,
        // otherwise remove context menu to prevent breakage
        el.setAttribute("saved-context", el.getAttribute("context"));
        if (el.id == "nav-bar" || el.id == "TabsToolbar")
          el.setAttribute("context", "autohide-context");
        else
          el.removeAttribute("context");

        // Set the inFullscreen attribute to allow specific styling
        // in fullscreen mode
        el.setAttribute("inFullscreen", true);
      } else {
        if (el.hasAttribute("saved-context")) {
          el.setAttribute("context", el.getAttribute("saved-context"));
          el.removeAttribute("saved-context");
        }
        el.removeAttribute("inFullscreen");
      }
    }

    ToolbarIconColor.inferFromText("fullscreen", aEnterFS);


    // For Lion fullscreen, all fullscreen controls are hidden, don't
    // bother to touch them. If we don't stop here, the following code
    // could cause the native fullscreen button be shown unexpectedly.
    // See bug 1165570.
    if (this.useLionFullScreen) {
      return;
    }

    var fullscreenctls = document.getElementById("window-controls");
    var navbar = document.getElementById("nav-bar");
    var ctlsOnTabbar = window.toolbar.visible;
    if (fullscreenctls.parentNode == navbar && ctlsOnTabbar) {
      fullscreenctls.removeAttribute("flex");
      document.getElementById("TabsToolbar").appendChild(fullscreenctls);
    } else if (fullscreenctls.parentNode.id == "TabsToolbar" && !ctlsOnTabbar) {
      fullscreenctls.setAttribute("flex", "1");
      navbar.appendChild(fullscreenctls);
    }
    fullscreenctls.hidden = !aEnterFS;
  }
};
XPCOMUtils.defineLazyGetter(FullScreen, "useLionFullScreen", function() {
  // We'll only use OS X Lion full screen if we're
  // * on OS X
  // * on Lion or higher (Darwin 11+)
  // * have fullscreenbutton="true"
  return AppConstants.isPlatformAndVersionAtLeast("macosx", 11) &&
         document.documentElement.getAttribute("fullscreenbutton") == "true";
});
PK
!<����J�J2chrome/browser/content/browser/browser-fullZoom.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 file is loaded into the browser window scope.
/* eslint-env mozilla/browser-window */

/**
 * Controls the "full zoom" setting and its site-specific preferences.
 */
var FullZoom = {
  // Identifies the setting in the content prefs database.
  name: "browser.content.full-zoom",

  // browser.zoom.siteSpecific preference cache
  // Enabling privacy.resistFingerprinting implies disabling browser.zoom.siteSpecific.
  _siteSpecificPref: undefined,

  // browser.zoom.updateBackgroundTabs preference cache
  updateBackgroundTabs: undefined,

  // This maps the browser to monotonically increasing integer
  // tokens. _browserTokenMap[browser] is increased each time the zoom is
  // changed in the browser. See _getBrowserToken and _ignorePendingZoomAccesses.
  _browserTokenMap: new WeakMap(),

  // Stores initial locations if we receive onLocationChange
  // events before we're initialized.
  _initialLocations: new WeakMap(),

  get siteSpecific() {
    if (this._siteSpecificPref === undefined) {
      this._siteSpecificPref =
        !Services.prefs.getBoolPref("privacy.resistFingerprinting") &&
        Services.prefs.getBoolPref("browser.zoom.siteSpecific");
    }
    return this._siteSpecificPref;
  },

  // nsISupports

  QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMEventListener,
                                         Ci.nsIObserver,
                                         Ci.nsIContentPrefObserver,
                                         Ci.nsISupportsWeakReference,
                                         Ci.nsISupports]),

  // Initialization & Destruction

  init: function FullZoom_init() {
    gBrowser.addEventListener("ZoomChangeUsingMouseWheel", this);

    // Register ourselves with the service so we know when our pref changes.
    this._cps2 = Cc["@mozilla.org/content-pref/service;1"].
                 getService(Ci.nsIContentPrefService2);
    this._cps2.addObserverForName(this.name, this);

    this.updateBackgroundTabs =
      Services.prefs.getBoolPref("browser.zoom.updateBackgroundTabs");

    // Listen for changes to the browser.zoom branch so we can enable/disable
    // updating background tabs and per-site saving and restoring of zoom levels.
    Services.prefs.addObserver("browser.zoom.", this, true);

    // Also need to listen to privacy.resistFingerprinting in order to update
    // this._siteSpecificPref.
    Services.prefs.addObserver("privacy.resistFingerprinting", this, true);

    // If we received onLocationChange events for any of the current browsers
    // before we were initialized we want to replay those upon initialization.
    for (let browser of gBrowser.browsers) {
      if (this._initialLocations.has(browser)) {
        this.onLocationChange(...this._initialLocations.get(browser), browser);
      }
    }

    // This should be nulled after initialization.
    this._initialLocations = null;
  },

  destroy: function FullZoom_destroy() {
    Services.prefs.removeObserver("browser.zoom.", this);
    this._cps2.removeObserverForName(this.name, this);
    gBrowser.removeEventListener("ZoomChangeUsingMouseWheel", this);
  },


  // Event Handlers

  // nsIDOMEventListener

  handleEvent: function FullZoom_handleEvent(event) {
    switch (event.type) {
      case "ZoomChangeUsingMouseWheel":
        let browser = this._getTargetedBrowser(event);
        this._ignorePendingZoomAccesses(browser);
        this._applyZoomToPref(browser);
        break;
    }
  },

  // nsIObserver

  observe(aSubject, aTopic, aData) {
    switch (aTopic) {
      case "nsPref:changed":
        switch (aData) {
          case "privacy.resistFingerprinting":
            // fall through
          case "browser.zoom.siteSpecific":
            // Invalidate pref cache.
            this._siteSpecificPref = undefined;
            break;
          case "browser.zoom.updateBackgroundTabs":
            this.updateBackgroundTabs =
              Services.prefs.getBoolPref("browser.zoom.updateBackgroundTabs");
            break;
        }
        break;
    }
  },

  // nsIContentPrefObserver

  onContentPrefSet: function FullZoom_onContentPrefSet(aGroup, aName, aValue, aIsPrivate) {
    this._onContentPrefChanged(aGroup, aValue, aIsPrivate);
  },

  onContentPrefRemoved: function FullZoom_onContentPrefRemoved(aGroup, aName, aIsPrivate) {
    this._onContentPrefChanged(aGroup, undefined, aIsPrivate);
  },

  /**
   * Appropriately updates the zoom level after a content preference has
   * changed.
   *
   * @param aGroup  The group of the changed preference.
   * @param aValue  The new value of the changed preference.  Pass undefined to
   *                indicate the preference's removal.
   */
  _onContentPrefChanged: function FullZoom__onContentPrefChanged(aGroup, aValue, aIsPrivate) {
    if (this._isNextContentPrefChangeInternal) {
      // Ignore changes that FullZoom itself makes.  This works because the
      // content pref service calls callbacks before notifying observers, and it
      // does both in the same turn of the event loop.
      delete this._isNextContentPrefChangeInternal;
      return;
    }

    let browser = gBrowser.selectedBrowser;
    if (!browser.currentURI)
      return;

    let ctxt = this._loadContextFromBrowser(browser);
    let domain = this._cps2.extractDomain(browser.currentURI.spec);
    if (aGroup) {
      if (aGroup == domain && ctxt.usePrivateBrowsing == aIsPrivate)
        this._applyPrefToZoom(aValue, browser);
      return;
    }

    this._globalValue = aValue === undefined ? aValue :
                          this._ensureValid(aValue);

    // If the current page doesn't have a site-specific preference, then its
    // zoom should be set to the new global preference now that the global
    // preference has changed.
    let hasPref = false;
    let token = this._getBrowserToken(browser);
    this._cps2.getByDomainAndName(browser.currentURI.spec, this.name, ctxt, {
      handleResult() { hasPref = true; },
      handleCompletion: () => {
        if (!hasPref && token.isCurrent)
          this._applyPrefToZoom(undefined, browser);
      }
    });
  },

  // location change observer

  /**
   * Called when the location of a tab changes.
   * When that happens, we need to update the current zoom level if appropriate.
   *
   * @param aURI
   *        A URI object representing the new location.
   * @param aIsTabSwitch
   *        Whether this location change has happened because of a tab switch.
   * @param aBrowser
   *        (optional) browser object displaying the document
   */
  onLocationChange: function FullZoom_onLocationChange(aURI, aIsTabSwitch, aBrowser) {
    let browser = aBrowser || gBrowser.selectedBrowser;

    // If we haven't been initialized yet but receive an onLocationChange
    // notification then let's store and replay it upon initialization.
    if (this._initialLocations) {
      this._initialLocations.set(browser, [aURI, aIsTabSwitch]);
      return;
    }

    // Ignore all pending async zoom accesses in the browser.  Pending accesses
    // that started before the location change will be prevented from applying
    // to the new location.
    this._ignorePendingZoomAccesses(browser);

    if (!aURI || (aIsTabSwitch && !this.siteSpecific)) {
      this._notifyOnLocationChange(browser);
      return;
    }

    // Avoid the cps roundtrip and apply the default/global pref.
    if (aURI.spec == "about:blank") {
      this._applyPrefToZoom(undefined, browser,
                            this._notifyOnLocationChange.bind(this, browser));
      return;
    }

    // Media documents should always start at 1, and are not affected by prefs.
    if (!aIsTabSwitch && browser.isSyntheticDocument) {
      ZoomManager.setZoomForBrowser(browser, 1);
      // _ignorePendingZoomAccesses already called above, so no need here.
      this._notifyOnLocationChange(browser);
      return;
    }

    // See if the zoom pref is cached.
    let ctxt = this._loadContextFromBrowser(browser);
    let pref = this._cps2.getCachedByDomainAndName(aURI.spec, this.name, ctxt);
    if (pref) {
      this._applyPrefToZoom(pref.value, browser,
                            this._notifyOnLocationChange.bind(this, browser));
      return;
    }

    // It's not cached, so we have to asynchronously fetch it.
    let value = undefined;
    let token = this._getBrowserToken(browser);
    this._cps2.getByDomainAndName(aURI.spec, this.name, ctxt, {
      handleResult(resultPref) { value = resultPref.value; },
      handleCompletion: () => {
        if (!token.isCurrent) {
          this._notifyOnLocationChange(browser);
          return;
        }
        this._applyPrefToZoom(value, browser,
                              this._notifyOnLocationChange.bind(this, browser));
      }
    });
  },

  // update state of zoom type menu item

  updateMenu: function FullZoom_updateMenu() {
    var menuItem = document.getElementById("toggle_zoom");

    menuItem.setAttribute("checked", !ZoomManager.useFullZoom);
  },

  // Setting & Pref Manipulation

  /**
   * Reduces the zoom level of the page in the current browser.
   */
  reduce: function FullZoom_reduce() {
    ZoomManager.reduce();
    let browser = gBrowser.selectedBrowser;
    this._ignorePendingZoomAccesses(browser);
    this._applyZoomToPref(browser);
  },

  /**
   * Enlarges the zoom level of the page in the current browser.
   */
  enlarge: function FullZoom_enlarge() {
    ZoomManager.enlarge();
    let browser = gBrowser.selectedBrowser;
    this._ignorePendingZoomAccesses(browser);
    this._applyZoomToPref(browser);
  },

  /**
   * Sets the zoom level for the given browser to the given floating
   * point value, where 1 is the default zoom level.
   */
  setZoom(value, browser = gBrowser.selectedBrowser) {
    ZoomManager.setZoomForBrowser(browser, value);
    this._ignorePendingZoomAccesses(browser);
    this._applyZoomToPref(browser);
  },

  /**
   * Sets the zoom level of the page in the given browser to the global zoom
   * level.
   *
   * @return A promise which resolves when the zoom reset has been applied.
   */
  reset: function FullZoom_reset(browser = gBrowser.selectedBrowser) {
    let token = this._getBrowserToken(browser);
    let result = this._getGlobalValue(browser).then(value => {
      if (token.isCurrent) {
        ZoomManager.setZoomForBrowser(browser, value === undefined ? 1 : value);
        this._ignorePendingZoomAccesses(browser);
      }
    });
    this._removePref(browser);
    return result;
  },

  /**
   * Set the zoom level for a given browser.
   *
   * Per nsPresContext::setFullZoom, we can set the zoom to its current value
   * without significant impact on performance, as the setting is only applied
   * if it differs from the current setting.  In fact getting the zoom and then
   * checking ourselves if it differs costs more.
   *
   * And perhaps we should always set the zoom even if it was more expensive,
   * since nsDocumentViewer::SetTextZoom claims that child documents can have
   * a different text zoom (although it would be unusual), and it implies that
   * those child text zooms should get updated when the parent zoom gets set,
   * and perhaps the same is true for full zoom
   * (although nsDocumentViewer::SetFullZoom doesn't mention it).
   *
   * So when we apply new zoom values to the browser, we simply set the zoom.
   * We don't check first to see if the new value is the same as the current
   * one.
   *
   * @param aValue     The zoom level value.
   * @param aBrowser   The zoom is set in this browser.  Required.
   * @param aCallback  If given, it's asynchronously called when complete.
   */
  _applyPrefToZoom: function FullZoom__applyPrefToZoom(aValue, aBrowser, aCallback) {
    if (!this.siteSpecific || gInPrintPreviewMode) {
      this._executeSoon(aCallback);
      return;
    }

    // The browser is sometimes half-destroyed because this method is called
    // by content pref service callbacks, which themselves can be called at any
    // time, even after browsers are closed.
    if (!aBrowser.parentNode || aBrowser.isSyntheticDocument) {
      this._executeSoon(aCallback);
      return;
    }

    if (aValue !== undefined) {
      ZoomManager.setZoomForBrowser(aBrowser, this._ensureValid(aValue));
      this._ignorePendingZoomAccesses(aBrowser);
      this._executeSoon(aCallback);
      return;
    }

    let token = this._getBrowserToken(aBrowser);
    this._getGlobalValue(aBrowser).then(value => {
      if (token.isCurrent) {
        ZoomManager.setZoomForBrowser(aBrowser, value === undefined ? 1 : value);
        this._ignorePendingZoomAccesses(aBrowser);
      }
      this._executeSoon(aCallback);
    });
  },

  /**
   * Saves the zoom level of the page in the given browser to the content
   * prefs store.
   *
   * @param browser  The zoom of this browser will be saved.  Required.
   */
  _applyZoomToPref: function FullZoom__applyZoomToPref(browser) {
    if (!this.siteSpecific ||
        gInPrintPreviewMode ||
        browser.isSyntheticDocument)
      return;

    this._cps2.set(browser.currentURI.spec, this.name,
                   ZoomManager.getZoomForBrowser(browser),
                   this._loadContextFromBrowser(browser), {
      handleCompletion: () => {
        this._isNextContentPrefChangeInternal = true;
      },
    });
  },

  /**
   * Removes from the content prefs store the zoom level of the given browser.
   *
   * @param browser  The zoom of this browser will be removed.  Required.
   */
  _removePref: function FullZoom__removePref(browser) {
    if (browser.isSyntheticDocument)
      return;
    let ctxt = this._loadContextFromBrowser(browser);
    this._cps2.removeByDomainAndName(browser.currentURI.spec, this.name, ctxt, {
      handleCompletion: () => {
        this._isNextContentPrefChangeInternal = true;
      },
    });
  },

  // Utilities

  /**
   * Returns the zoom change token of the given browser.  Asynchronous
   * operations that access the given browser's zoom should use this method to
   * capture the token before starting and use token.isCurrent to determine if
   * it's safe to access the zoom when done.  If token.isCurrent is false, then
   * after the async operation started, either the browser's zoom was changed or
   * the browser was destroyed, and depending on what the operation is doing, it
   * may no longer be safe to set and get its zoom.
   *
   * @param browser  The token of this browser will be returned.
   * @return  An object with an "isCurrent" getter.
   */
  _getBrowserToken: function FullZoom__getBrowserToken(browser) {
    let map = this._browserTokenMap;
    if (!map.has(browser))
      map.set(browser, 0);
    return {
      token: map.get(browser),
      get isCurrent() {
        // At this point, the browser may have been destructed and unbound but
        // its outer ID not removed from the map because outer-window-destroyed
        // hasn't been received yet.  In that case, the browser is unusable, it
        // has no properties, so return false.  Check for this case by getting a
        // property, say, docShell.
        return map.get(browser) === this.token && browser.parentNode;
      },
    };
  },

  /**
   * Returns the browser that the supplied zoom event is associated with.
   * @param event  The ZoomChangeUsingMouseWheel event.
   * @return  The associated browser element, if one exists, otherwise null.
   */
  _getTargetedBrowser: function FullZoom__getTargetedBrowser(event) {
    let target = event.originalTarget;

    // With remote content browsers, the event's target is the browser
    // we're looking for.
    const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
    if (target instanceof window.XULElement &&
        target.localName == "browser" &&
        target.namespaceURI == XUL_NS)
      return target;

    // With in-process content browsers, the event's target is the content
    // document.
    if (target.nodeType == Node.DOCUMENT_NODE)
      return gBrowser.getBrowserForDocument(target);

    throw new Error("Unexpected ZoomChangeUsingMouseWheel event source");
  },

  /**
   * Increments the zoom change token for the given browser so that pending
   * async operations know that it may be unsafe to access they zoom when they
   * finish.
   *
   * @param browser  Pending accesses in this browser will be ignored.
   */
  _ignorePendingZoomAccesses: function FullZoom__ignorePendingZoomAccesses(browser) {
    let map = this._browserTokenMap;
    map.set(browser, (map.get(browser) || 0) + 1);
  },

  _ensureValid: function FullZoom__ensureValid(aValue) {
    // Note that undefined is a valid value for aValue that indicates a known-
    // not-to-exist value.
    if (isNaN(aValue))
      return 1;

    if (aValue < ZoomManager.MIN)
      return ZoomManager.MIN;

    if (aValue > ZoomManager.MAX)
      return ZoomManager.MAX;

    return aValue;
  },

  /**
   * Gets the global browser.content.full-zoom content preference.
   *
   * @param browser   The browser pertaining to the zoom.
   * @returns Promise<prefValue>
   *                  Resolves to the preference value when done.
   */
  _getGlobalValue: function FullZoom__getGlobalValue(browser) {
    // * !("_globalValue" in this) => global value not yet cached.
    // * this._globalValue === undefined => global value known not to exist.
    // * Otherwise, this._globalValue is a number, the global value.
    return new Promise(resolve => {
      if ("_globalValue" in this) {
        resolve(this._globalValue);
        return;
      }
      let value = undefined;
      this._cps2.getGlobal(this.name, this._loadContextFromBrowser(browser), {
        handleResult(pref) { value = pref.value; },
        handleCompletion: (reason) => {
          this._globalValue = this._ensureValid(value);
          resolve(this._globalValue);
        }
      });
    });
  },

  /**
   * Gets the load context from the given Browser.
   *
   * @param Browser  The Browser whose load context will be returned.
   * @return        The nsILoadContext of the given Browser.
   */
  _loadContextFromBrowser: function FullZoom__loadContextFromBrowser(browser) {
    return browser.loadContext;
  },

  /**
   * Asynchronously broadcasts "browser-fullZoom:location-change" so that
   * listeners can be notified when the zoom levels on those pages change.
   * The notification is always asynchronous so that observers are guaranteed a
   * consistent behavior.
   */
  _notifyOnLocationChange: function FullZoom__notifyOnLocationChange(browser) {
    this._executeSoon(function() {
      Services.obs.notifyObservers(browser, "browser-fullZoom:location-change");
    });
  },

  _executeSoon: function FullZoom__executeSoon(callback) {
    if (!callback)
      return;
    Services.tm.dispatchToMainThread(callback);
  },
};
PK
!<�M9�9�8chrome/browser/content/browser/browser-gestureSupport.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 file is loaded into the browser window scope.
/* eslint-env mozilla/browser-window */

// Simple gestures support
//
// As per bug #412486, web content must not be allowed to receive any
// simple gesture events.  Multi-touch gesture APIs are in their
// infancy and we do NOT want to be forced into supporting an API that
// will probably have to change in the future.  (The current Mac OS X
// API is undocumented and was reverse-engineered.)  Until support is
// implemented in the event dispatcher to keep these events as
// chrome-only, we must listen for the simple gesture events during
// the capturing phase and call stopPropagation on every event.

var gGestureSupport = {
  _currentRotation: 0,
  _lastRotateDelta: 0,
  _rotateMomentumThreshold: .75,

  /**
   * Add or remove mouse gesture event listeners
   *
   * @param aAddListener
   *        True to add/init listeners and false to remove/uninit
   */
  init: function GS_init(aAddListener) {
    const gestureEvents = ["SwipeGestureMayStart", "SwipeGestureStart",
      "SwipeGestureUpdate", "SwipeGestureEnd", "SwipeGesture",
      "MagnifyGestureStart", "MagnifyGestureUpdate", "MagnifyGesture",
      "RotateGestureStart", "RotateGestureUpdate", "RotateGesture",
      "TapGesture", "PressTapGesture"];

    let addRemove = aAddListener ? window.addEventListener :
      window.removeEventListener;

    for (let event of gestureEvents) {
      addRemove("Moz" + event, this, true);
    }
  },

  /**
   * Dispatch events based on the type of mouse gesture event. For now, make
   * sure to stop propagation of every gesture event so that web content cannot
   * receive gesture events.
   *
   * @param aEvent
   *        The gesture event to handle
   */
  handleEvent: function GS_handleEvent(aEvent) {
    if (!Services.prefs.getBoolPref(
           "dom.debug.propagate_gesture_events_through_content")) {
      aEvent.stopPropagation();
    }

    // Create a preference object with some defaults
    let def = (aThreshold, aLatched) =>
      ({ threshold: aThreshold, latched: !!aLatched });

    switch (aEvent.type) {
      case "MozSwipeGestureMayStart":
        if (this._shouldDoSwipeGesture(aEvent)) {
          aEvent.preventDefault();
        }
        break;
      case "MozSwipeGestureStart":
        aEvent.preventDefault();
        this._setupSwipeGesture();
        break;
      case "MozSwipeGestureUpdate":
        aEvent.preventDefault();
        this._doUpdate(aEvent);
        break;
      case "MozSwipeGestureEnd":
        aEvent.preventDefault();
        this._doEnd(aEvent);
        break;
      case "MozSwipeGesture":
        aEvent.preventDefault();
        this.onSwipe(aEvent);
        break;
      case "MozMagnifyGestureStart":
        aEvent.preventDefault();
        let pinchPref = AppConstants.platform == "win"
                        ? def(25, 0)
                        : def(150, 1);
        this._setupGesture(aEvent, "pinch", pinchPref, "out", "in");
        break;
      case "MozRotateGestureStart":
        aEvent.preventDefault();
        this._setupGesture(aEvent, "twist", def(25, 0), "right", "left");
        break;
      case "MozMagnifyGestureUpdate":
      case "MozRotateGestureUpdate":
        aEvent.preventDefault();
        this._doUpdate(aEvent);
        break;
      case "MozTapGesture":
        aEvent.preventDefault();
        this._doAction(aEvent, ["tap"]);
        break;
      case "MozRotateGesture":
        aEvent.preventDefault();
        this._doAction(aEvent, ["twist", "end"]);
        break;
      /* case "MozPressTapGesture":
        break; */
    }
  },

  /**
   * Called at the start of "pinch" and "twist" gestures to setup all of the
   * information needed to process the gesture
   *
   * @param aEvent
   *        The continual motion start event to handle
   * @param aGesture
   *        Name of the gesture to handle
   * @param aPref
   *        Preference object with the names of preferences and defaults
   * @param aInc
   *        Command to trigger for increasing motion (without gesture name)
   * @param aDec
   *        Command to trigger for decreasing motion (without gesture name)
   */
  _setupGesture: function GS__setupGesture(aEvent, aGesture, aPref, aInc, aDec) {
    // Try to load user-set values from preferences
    for (let [pref, def] of Object.entries(aPref))
      aPref[pref] = this._getPref(aGesture + "." + pref, def);

    // Keep track of the total deltas and latching behavior
    let offset = 0;
    let latchDir = aEvent.delta > 0 ? 1 : -1;
    let isLatched = false;

    // Create the update function here to capture closure state
    this._doUpdate = function GS__doUpdate(updateEvent) {
      // Update the offset with new event data
      offset += updateEvent.delta;

      // Check if the cumulative deltas exceed the threshold
      if (Math.abs(offset) > aPref.threshold) {
        // Trigger the action if we don't care about latching; otherwise, make
        // sure either we're not latched and going the same direction of the
        // initial motion; or we're latched and going the opposite way
        let sameDir = (latchDir ^ offset) >= 0;
        if (!aPref.latched || (isLatched ^ sameDir)) {
          this._doAction(updateEvent, [aGesture, offset > 0 ? aInc : aDec]);

          // We must be getting latched or leaving it, so just toggle
          isLatched = !isLatched;
        }

        // Reset motion counter to prepare for more of the same gesture
        offset = 0;
      }
    };

    // The start event also contains deltas, so handle an update right away
    this._doUpdate(aEvent);
  },

  /**
   * Checks whether a swipe gesture event can navigate the browser history or
   * not.
   *
   * @param aEvent
   *        The swipe gesture event.
   * @return true if the swipe event may navigate the history, false othwerwise.
   */
  _swipeNavigatesHistory: function GS__swipeNavigatesHistory(aEvent) {
    return this._getCommand(aEvent, ["swipe", "left"])
              == "Browser:BackOrBackDuplicate" &&
           this._getCommand(aEvent, ["swipe", "right"])
              == "Browser:ForwardOrForwardDuplicate";
  },

  /**
   * Checks whether we want to start a swipe for aEvent and sets
   * aEvent.allowedDirections to the right values.
   *
   * @param aEvent
   *        The swipe gesture "MayStart" event.
   * @return true if we're willing to start a swipe for this event, false
   *         otherwise.
   */
  _shouldDoSwipeGesture: function GS__shouldDoSwipeGesture(aEvent) {
    if (!this._swipeNavigatesHistory(aEvent)) {
      return false;
    }

    let isVerticalSwipe = false;
    if (aEvent.direction == aEvent.DIRECTION_UP) {
      if (gMultiProcessBrowser || window.content.pageYOffset > 0) {
        return false;
      }
      isVerticalSwipe = true;
    } else if (aEvent.direction == aEvent.DIRECTION_DOWN) {
      if (gMultiProcessBrowser || window.content.pageYOffset < window.content.scrollMaxY) {
        return false;
      }
      isVerticalSwipe = true;
    }
    if (isVerticalSwipe) {
      // Vertical overscroll has been temporarily disabled until bug 939480 is
      // fixed.
      return false;
    }

    let canGoBack = gHistorySwipeAnimation.canGoBack();
    let canGoForward = gHistorySwipeAnimation.canGoForward();
    let isLTR = gHistorySwipeAnimation.isLTR;

    if (canGoBack) {
      aEvent.allowedDirections |= isLTR ? aEvent.DIRECTION_LEFT :
                                          aEvent.DIRECTION_RIGHT;
    }
    if (canGoForward) {
      aEvent.allowedDirections |= isLTR ? aEvent.DIRECTION_RIGHT :
                                          aEvent.DIRECTION_LEFT;
    }

    return true;
  },

  /**
   * Sets up swipe gestures. This includes setting up swipe animations for the
   * gesture, if enabled.
   *
   * @param aEvent
   *        The swipe gesture start event.
   * @return true if swipe gestures could successfully be set up, false
   *         othwerwise.
   */
  _setupSwipeGesture: function GS__setupSwipeGesture() {
    gHistorySwipeAnimation.startAnimation(false);

    this._doUpdate = function GS__doUpdate(aEvent) {
      gHistorySwipeAnimation.updateAnimation(aEvent.delta);
    };

    this._doEnd = function GS__doEnd(aEvent) {
      gHistorySwipeAnimation.swipeEndEventReceived();

      this._doUpdate = function() {};
      this._doEnd = function() {};
    };
  },

  /**
   * Generator producing the powerset of the input array where the first result
   * is the complete set and the last result (before StopIteration) is empty.
   *
   * @param aArray
   *        Source array containing any number of elements
   * @yield Array that is a subset of the input array from full set to empty
   */
  _power: function* GS__power(aArray) {
    // Create a bitmask based on the length of the array
    let num = 1 << aArray.length;
    while (--num >= 0) {
      // Only select array elements where the current bit is set
      yield aArray.reduce(function(aPrev, aCurr, aIndex) {
        if (num & 1 << aIndex)
          aPrev.push(aCurr);
        return aPrev;
      }, []);
    }
  },

  /**
   * Determine what action to do for the gesture based on which keys are
   * pressed and which commands are set, and execute the command.
   *
   * @param aEvent
   *        The original gesture event to convert into a fake click event
   * @param aGesture
   *        Array of gesture name parts (to be joined by periods)
   * @return Name of the executed command. Returns null if no command is
   *         found.
   */
  _doAction: function GS__doAction(aEvent, aGesture) {
    let command = this._getCommand(aEvent, aGesture);
    return command && this._doCommand(aEvent, command);
  },

  /**
   * Determine what action to do for the gesture based on which keys are
   * pressed and which commands are set
   *
   * @param aEvent
   *        The original gesture event to convert into a fake click event
   * @param aGesture
   *        Array of gesture name parts (to be joined by periods)
   */
  _getCommand: function GS__getCommand(aEvent, aGesture) {
    // Create an array of pressed keys in a fixed order so that a command for
    // "meta" is preferred over "ctrl" when both buttons are pressed (and a
    // command for both don't exist)
    let keyCombos = [];
    for (let key of ["shift", "alt", "ctrl", "meta"]) {
      if (aEvent[key + "Key"])
        keyCombos.push(key);
    }

    // Try each combination of key presses in decreasing order for commands
    for (let subCombo of this._power(keyCombos)) {
      // Convert a gesture and pressed keys into the corresponding command
      // action where the preference has the gesture before "shift" before
      // "alt" before "ctrl" before "meta" all separated by periods
      let command;
      try {
        command = this._getPref(aGesture.concat(subCombo).join("."));
      } catch (e) {}

      if (command)
        return command;
    }
    return null;
  },

  /**
   * Execute the specified command.
   *
   * @param aEvent
   *        The original gesture event to convert into a fake click event
   * @param aCommand
   *        Name of the command found for the event's keys and gesture.
   */
  _doCommand: function GS__doCommand(aEvent, aCommand) {
    let node = document.getElementById(aCommand);
    if (node) {
      if (node.getAttribute("disabled") != "true") {
        let cmdEvent = document.createEvent("xulcommandevent");
        cmdEvent.initCommandEvent("command", true, true, window, 0,
                                  aEvent.ctrlKey, aEvent.altKey,
                                  aEvent.shiftKey, aEvent.metaKey,
                                  aEvent, aEvent.mozInputSource);
        node.dispatchEvent(cmdEvent);
      }

    } else {
      goDoCommand(aCommand);
    }
  },

  /**
   * Handle continual motion events.  This function will be set by
   * _setupGesture or _setupSwipe.
   *
   * @param aEvent
   *        The continual motion update event to handle
   */
  _doUpdate(aEvent) {},

  /**
   * Handle gesture end events.  This function will be set by _setupSwipe.
   *
   * @param aEvent
   *        The gesture end event to handle
   */
  _doEnd(aEvent) {},

  /**
   * Convert the swipe gesture into a browser action based on the direction.
   *
   * @param aEvent
   *        The swipe event to handle
   */
  onSwipe: function GS_onSwipe(aEvent) {
    // Figure out which one (and only one) direction was triggered
    for (let dir of ["UP", "RIGHT", "DOWN", "LEFT"]) {
      if (aEvent.direction == aEvent["DIRECTION_" + dir]) {
        this._coordinateSwipeEventWithAnimation(aEvent, dir);
        break;
      }
    }
  },

  /**
   * Process a swipe event based on the given direction.
   *
   * @param aEvent
   *        The swipe event to handle
   * @param aDir
   *        The direction for the swipe event
   */
  processSwipeEvent: function GS_processSwipeEvent(aEvent, aDir) {
    this._doAction(aEvent, ["swipe", aDir.toLowerCase()]);
  },

  /**
   * Coordinates the swipe event with the swipe animation, if any.
   * If an animation is currently running, the swipe event will be
   * processed once the animation stops. This will guarantee a fluid
   * motion of the animation.
   *
   * @param aEvent
   *        The swipe event to handle
   * @param aDir
   *        The direction for the swipe event
   */
  _coordinateSwipeEventWithAnimation:
  function GS__coordinateSwipeEventWithAnimation(aEvent, aDir) {
    if ((gHistorySwipeAnimation.isAnimationRunning()) &&
        (aDir == "RIGHT" || aDir == "LEFT")) {
      gHistorySwipeAnimation.processSwipeEvent(aEvent, aDir);
    } else {
      this.processSwipeEvent(aEvent, aDir);
    }
  },

  /**
   * Get a gesture preference or use a default if it doesn't exist
   *
   * @param aPref
   *        Name of the preference to load under the gesture branch
   * @param aDef
   *        Default value if the preference doesn't exist
   */
  _getPref: function GS__getPref(aPref, aDef) {
    // Preferences branch under which all gestures preferences are stored
    const branch = "browser.gesture.";

    try {
      // Determine what type of data to load based on default value's type
      let type = typeof aDef;
      let getFunc = "Char";
      if (type == "boolean")
        getFunc = "Bool";
      else if (type == "number")
        getFunc = "Int";
      return Services.prefs["get" + getFunc + "Pref"](branch + aPref);
    } catch (e) {
      return aDef;
    }
  },

  /**
   * Perform rotation for ImageDocuments
   *
   * @param aEvent
   *        The MozRotateGestureUpdate event triggering this call
   */
  rotate(aEvent) {
    if (!(window.content.document instanceof ImageDocument))
      return;

    let contentElement = window.content.document.body.firstElementChild;
    if (!contentElement)
      return;
    // If we're currently snapping, cancel that snap
    if (contentElement.classList.contains("completeRotation"))
      this._clearCompleteRotation();

    this.rotation = Math.round(this.rotation + aEvent.delta);
    contentElement.style.transform = "rotate(" + this.rotation + "deg)";
    this._lastRotateDelta = aEvent.delta;
  },

  /**
   * Perform a rotation end for ImageDocuments
   */
  rotateEnd() {
    if (!(window.content.document instanceof ImageDocument))
      return;

    let contentElement = window.content.document.body.firstElementChild;
    if (!contentElement)
      return;

    let transitionRotation = 0;

    // The reason that 360 is allowed here is because when rotating between
    // 315 and 360, setting rotate(0deg) will cause it to rotate the wrong
    // direction around--spinning wildly.
    if (this.rotation <= 45)
      transitionRotation = 0;
    else if (this.rotation > 45 && this.rotation <= 135)
      transitionRotation = 90;
    else if (this.rotation > 135 && this.rotation <= 225)
      transitionRotation = 180;
    else if (this.rotation > 225 && this.rotation <= 315)
      transitionRotation = 270;
    else
      transitionRotation = 360;

    // If we're going fast enough, and we didn't already snap ahead of rotation,
    // then snap ahead of rotation to simulate momentum
    if (this._lastRotateDelta > this._rotateMomentumThreshold &&
        this.rotation > transitionRotation)
      transitionRotation += 90;
    else if (this._lastRotateDelta < -1 * this._rotateMomentumThreshold &&
             this.rotation < transitionRotation)
      transitionRotation -= 90;

    // Only add the completeRotation class if it is is necessary
    if (transitionRotation != this.rotation) {
      contentElement.classList.add("completeRotation");
      contentElement.addEventListener("transitionend", this._clearCompleteRotation);
    }

    contentElement.style.transform = "rotate(" + transitionRotation + "deg)";
    this.rotation = transitionRotation;
  },

  /**
   * Gets the current rotation for the ImageDocument
   */
  get rotation() {
    return this._currentRotation;
  },

  /**
   * Sets the current rotation for the ImageDocument
   *
   * @param aVal
   *        The new value to take.  Can be any value, but it will be bounded to
   *        0 inclusive to 360 exclusive.
   */
  set rotation(aVal) {
    this._currentRotation = aVal % 360;
    if (this._currentRotation < 0)
      this._currentRotation += 360;
    return this._currentRotation;
  },

  /**
   * When the location/tab changes, need to reload the current rotation for the
   * image
   */
  restoreRotationState() {
    // Bug 1108553 - Cannot rotate images in stand-alone image documents with e10s
    if (gMultiProcessBrowser)
      return;

    if (!(window.content.document instanceof ImageDocument))
      return;

    let contentElement = window.content.document.body.firstElementChild;
    let transformValue = window.content.window.getComputedStyle(contentElement)
                                              .transform;

    if (transformValue == "none") {
      this.rotation = 0;
      return;
    }

    // transformValue is a rotation matrix--split it and do mathemagic to
    // obtain the real rotation value
    transformValue = transformValue.split("(")[1]
                                   .split(")")[0]
                                   .split(",");
    this.rotation = Math.round(Math.atan2(transformValue[1], transformValue[0]) *
                               (180 / Math.PI));
  },

  /**
   * Removes the transition rule by removing the completeRotation class
   */
  _clearCompleteRotation() {
    let contentElement = window.content.document &&
                         window.content.document instanceof ImageDocument &&
                         window.content.document.body &&
                         window.content.document.body.firstElementChild;
    if (!contentElement)
      return;
    contentElement.classList.remove("completeRotation");
    contentElement.removeEventListener("transitionend", this._clearCompleteRotation);
  },
};

// History Swipe Animation Support (bug 678392)
var gHistorySwipeAnimation = {

  active: false,
  isLTR: false,

  /**
   * Initializes the support for history swipe animations, if it is supported
   * by the platform/configuration.
   */
  init: function HSA_init() {
    if (!this._isSupported())
      return;

    this.active = false;
    this.isLTR = document.documentElement.matches(":-moz-locale-dir(ltr)");
    this._trackedSnapshots = [];
    this._startingIndex = -1;
    this._historyIndex = -1;
    this._boxWidth = -1;
    this._boxHeight = -1;
    this._maxSnapshots = this._getMaxSnapshots();
    this._lastSwipeDir = "";
    this._direction = "horizontal";

    // We only want to activate history swipe animations if we store snapshots.
    // If we don't store any, we handle horizontal swipes without animations.
    if (this._maxSnapshots > 0) {
      this.active = true;
      gBrowser.addEventListener("pagehide", this);
      gBrowser.addEventListener("pageshow", this);
      gBrowser.addEventListener("popstate", this);
      gBrowser.addEventListener("DOMModalDialogClosed", this);
      gBrowser.tabContainer.addEventListener("TabClose", this);
    }
  },

  /**
   * Uninitializes the support for history swipe animations.
   */
  uninit: function HSA_uninit() {
    gBrowser.removeEventListener("pagehide", this);
    gBrowser.removeEventListener("pageshow", this);
    gBrowser.removeEventListener("popstate", this);
    gBrowser.removeEventListener("DOMModalDialogClosed", this);
    gBrowser.tabContainer.removeEventListener("TabClose", this);

    this.active = false;
    this.isLTR = false;
  },

  /**
   * Starts the swipe animation and handles fast swiping (i.e. a swipe animation
   * is already in progress when a new one is initiated).
   *
   * @param aIsVerticalSwipe
   *        Whether we're dealing with a vertical swipe or not.
   */
  startAnimation: function HSA_startAnimation(aIsVerticalSwipe) {
    this._direction = aIsVerticalSwipe ? "vertical" : "horizontal";

    if (this.isAnimationRunning()) {
      // If this is a horizontal scroll, or if this is a vertical scroll that
      // was started while a horizontal scroll was still running, handle it as
      // as a fast swipe. In the case of the latter scenario, this allows us to
      // start the vertical animation without first loading the final page, or
      // taking another snapshot. If vertical scrolls are initiated repeatedly
      // without prior horizontal scroll we skip this and restart the animation
      // from 0.
      if (this._direction == "horizontal" || this._lastSwipeDir != "") {
        gBrowser.stop();
        this._lastSwipeDir = "RELOAD"; // just ensure that != ""
        this._canGoBack = this.canGoBack();
        this._canGoForward = this.canGoForward();
        this._handleFastSwiping();
      }
      this.updateAnimation(0);
    } else {
      // Get the session history from SessionStore.
      let updateSessionHistory = sessionHistory => {
        this._startingIndex = sessionHistory.index;
        this._historyIndex = this._startingIndex;
        this._canGoBack = this.canGoBack();
        this._canGoForward = this.canGoForward();
        if (this.active) {
          this._addBoxes();
          this._takeSnapshot();
          this._installPrevAndNextSnapshots();
          this._lastSwipeDir = "";
        }
        this.updateAnimation(0);
      };
      SessionStore.getSessionHistory(gBrowser.selectedTab, updateSessionHistory);
    }
  },

  /**
   * Stops the swipe animation.
   */
  stopAnimation: function HSA_stopAnimation() {
    gHistorySwipeAnimation._removeBoxes();
    this._historyIndex = this._getCurrentHistoryIndex();
  },

  /**
   * Updates the animation between two pages in history.
   *
   * @param aVal
   *        A floating point value that represents the progress of the
   *        swipe gesture.
   */
  updateAnimation: function HSA_updateAnimation(aVal) {
    if (!this.isAnimationRunning()) {
      return;
    }

    // We use the following value to decrease the bounce effect when scrolling
    // to the top or bottom of the page, or when swiping back/forward past the
    // browsing history. This value was determined experimentally.
    let dampValue = 4;
    if (this._direction == "vertical") {
      this._prevBox.collapsed = true;
      this._nextBox.collapsed = true;
      this._positionBox(this._curBox, -1 * aVal / dampValue);
    } else if ((aVal >= 0 && this.isLTR) ||
               (aVal <= 0 && !this.isLTR)) {
      let tempDampValue = 1;
      if (this._canGoBack) {
        this._prevBox.collapsed = false;
      } else {
        tempDampValue = dampValue;
        this._prevBox.collapsed = true;
      }

      // The current page is pushed to the right (LTR) or left (RTL),
      // the intention is to go back.
      // If there is a page to go back to, it should show in the background.
      this._positionBox(this._curBox, aVal / tempDampValue);

      // The forward page should be pushed offscreen all the way to the right.
      this._positionBox(this._nextBox, 1);
    } else if (this._canGoForward) {
      // The intention is to go forward. If there is a page to go forward to,
      // it should slide in from the right (LTR) or left (RTL).
      // Otherwise, the current page should slide to the left (LTR) or
      // right (RTL) and the backdrop should appear in the background.
      // For the backdrop to be visible in that case, the previous page needs
      // to be hidden (if it exists).
      this._nextBox.collapsed = false;
      let offset = this.isLTR ? 1 : -1;
      this._positionBox(this._curBox, 0);
      this._positionBox(this._nextBox, offset + aVal);
    } else {
      this._prevBox.collapsed = true;
      this._positionBox(this._curBox, aVal / dampValue);
    }
  },

  _getCurrentHistoryIndex() {
    return SessionStore.getSessionHistory(gBrowser.selectedTab).index;
  },

  /**
   * Event handler for events relevant to the history swipe animation.
   *
   * @param aEvent
   *        An event to process.
   */
  handleEvent: function HSA_handleEvent(aEvent) {
    let browser = gBrowser.selectedBrowser;
    switch (aEvent.type) {
      case "TabClose":
        let browserForTab = gBrowser.getBrowserForTab(aEvent.target);
        this._removeTrackedSnapshot(-1, browserForTab);
        break;
      case "DOMModalDialogClosed":
        this.stopAnimation();
        break;
      case "pageshow":
        if (aEvent.target == browser.contentDocument) {
          this.stopAnimation();
        }
        break;
      case "popstate":
        if (aEvent.target == browser.contentDocument.defaultView) {
          this.stopAnimation();
        }
        break;
      case "pagehide":
        if (aEvent.target == browser.contentDocument) {
          // Take and compress a snapshot of a page whenever it's about to be
          // navigated away from. We already have a snapshot of the page if an
          // animation is running, so we're left with compressing it.
          if (!this.isAnimationRunning()) {
            this._takeSnapshot();
          }
          this._compressSnapshotAtCurrentIndex();
        }
        break;
    }
  },

  /**
   * Checks whether the history swipe animation is currently running or not.
   *
   * @return true if the animation is currently running, false otherwise.
   */
  isAnimationRunning: function HSA_isAnimationRunning() {
    return !!this._container;
  },

  /**
   * Process a swipe event based on the given direction.
   *
   * @param aEvent
   *        The swipe event to handle
   * @param aDir
   *        The direction for the swipe event
   */
  processSwipeEvent: function HSA_processSwipeEvent(aEvent, aDir) {
    if (aDir == "RIGHT")
      this._historyIndex += this.isLTR ? 1 : -1;
    else if (aDir == "LEFT")
      this._historyIndex += this.isLTR ? -1 : 1;
    else
      return;
    this._lastSwipeDir = aDir;
  },

  /**
   * Checks if there is a page in the browser history to go back to.
   *
   * @return true if there is a previous page in history, false otherwise.
   */
  canGoBack: function HSA_canGoBack() {
    if (this.isAnimationRunning())
      return this._doesIndexExistInHistory(this._historyIndex - 1);
    return gBrowser.webNavigation.canGoBack;
  },

  /**
   * Checks if there is a page in the browser history to go forward to.
   *
   * @return true if there is a next page in history, false otherwise.
   */
  canGoForward: function HSA_canGoForward() {
    if (this.isAnimationRunning())
      return this._doesIndexExistInHistory(this._historyIndex + 1);
    return gBrowser.webNavigation.canGoForward;
  },

  /**
   * Used to notify the history swipe animation that the OS sent a swipe end
   * event and that we should navigate to the page that the user swiped to, if
   * any. This will also result in the animation overlay to be torn down.
   */
  swipeEndEventReceived: function HSA_swipeEndEventReceived() {
    // Update the session history before continuing.
    let updateSessionHistory = sessionHistory => {
      if (this._lastSwipeDir != "" && this._historyIndex != this._startingIndex)
        this._navigateToHistoryIndex();
      else
        this.stopAnimation();
    };
    SessionStore.getSessionHistory(gBrowser.selectedTab, updateSessionHistory);
  },

  /**
   * Checks whether a particular index exists in the browser history or not.
   *
   * @param aIndex
   *        The index to check for availability for in the history.
   * @return true if the index exists in the browser history, false otherwise.
   */
  _doesIndexExistInHistory: function HSA__doesIndexExistInHistory(aIndex) {
    try {
      return SessionStore.getSessionHistory(gBrowser.selectedTab).entries[aIndex] != null;
    } catch (ex) {
      return false;
    }
  },

  /**
   * Navigates to the index in history that is currently being tracked by
   * |this|.
   */
  _navigateToHistoryIndex: function HSA__navigateToHistoryIndex() {
    if (this._doesIndexExistInHistory(this._historyIndex))
      gBrowser.webNavigation.gotoIndex(this._historyIndex);
    else
      this.stopAnimation();
  },

  /**
   * Checks to see if history swipe animations are supported by this
   * platform/configuration.
   *
   * return true if supported, false otherwise.
   */
  _isSupported: function HSA__isSupported() {
    return window.matchMedia("(-moz-swipe-animation-enabled)").matches;
  },

  /**
   * Handle fast swiping (i.e. a swipe animation is already in
   * progress when a new one is initiated). This will swap out the snapshots
   * used in the previous animation with the appropriate new ones.
   */
  _handleFastSwiping: function HSA__handleFastSwiping() {
    this._installCurrentPageSnapshot(null);
    this._installPrevAndNextSnapshots();
  },

  /**
   * Adds the boxes that contain the snapshots used during the swipe animation.
   */
  _addBoxes: function HSA__addBoxes() {
    let browserStack =
      document.getAnonymousElementByAttribute(gBrowser.getNotificationBox(),
                                              "class", "browserStack");
    this._container = this._createElement("historySwipeAnimationContainer",
                                          "stack");
    browserStack.appendChild(this._container);

    this._prevBox = this._createElement("historySwipeAnimationPreviousPage",
                                        "box");
    this._container.appendChild(this._prevBox);

    this._curBox = this._createElement("historySwipeAnimationCurrentPage",
                                       "box");
    this._container.appendChild(this._curBox);

    this._nextBox = this._createElement("historySwipeAnimationNextPage",
                                        "box");
    this._container.appendChild(this._nextBox);

    // Cache width and height.
    this._boxWidth = this._curBox.getBoundingClientRect().width;
    this._boxHeight = this._curBox.getBoundingClientRect().height;
  },

  /**
   * Removes the boxes.
   */
  _removeBoxes: function HSA__removeBoxes() {
    this._curBox = null;
    this._prevBox = null;
    this._nextBox = null;
    if (this._container)
      this._container.remove();
    this._container = null;
    this._boxWidth = -1;
    this._boxHeight = -1;
  },

  /**
   * Creates an element with a given identifier and tag name.
   *
   * @param aID
   *        An identifier to create the element with.
   * @param aTagName
   *        The name of the tag to create the element for.
   * @return the newly created element.
   */
  _createElement: function HSA__createElement(aID, aTagName) {
    let XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
    let element = document.createElementNS(XULNS, aTagName);
    element.id = aID;
    return element;
  },

  /**
   * Moves a given box to a given X coordinate position.
   *
   * @param aBox
   *        The box element to position.
   * @param aPosition
   *        The position (in X coordinates) to move the box element to.
   */
  _positionBox: function HSA__positionBox(aBox, aPosition) {
    let transform = "";

    if (this._direction == "vertical")
      transform = "translateY(" + this._boxHeight * aPosition + "px)";
    else
      transform = "translateX(" + this._boxWidth * aPosition + "px)";

    aBox.style.transform = transform;
  },

  /**
   * Verifies that we're ready to take snapshots based on the global pref and
   * the current index in history.
   *
   * @return true if we're ready to take snapshots, false otherwise.
   */
  _readyToTakeSnapshots: function HSA__readyToTakeSnapshots() {
    return (this._maxSnapshots >= 1 && this._getCurrentHistoryIndex() >= 0);
  },

  /**
   * Takes a snapshot of the page the browser is currently on.
   */
  _takeSnapshot: function HSA__takeSnapshot() {
    if (!this._readyToTakeSnapshots()) {
      return;
    }

    let canvas = null;

    let browser = gBrowser.selectedBrowser;
    let r = browser.getBoundingClientRect();
    canvas = document.createElementNS("http://www.w3.org/1999/xhtml",
                                      "canvas");
    canvas.mozOpaque = true;
    let scale = window.devicePixelRatio;
    canvas.width = r.width * scale;
    canvas.height = r.height * scale;
    let ctx = canvas.getContext("2d");
    let zoom = browser.markupDocumentViewer.fullZoom * scale;
    ctx.scale(zoom, zoom);
    ctx.drawWindow(browser.contentWindow,
                   0, 0, canvas.width / zoom, canvas.height / zoom, "white",
                   ctx.DRAWWINDOW_DO_NOT_FLUSH | ctx.DRAWWINDOW_DRAW_VIEW |
                   ctx.DRAWWINDOW_ASYNC_DECODE_IMAGES |
                   ctx.DRAWWINDOW_USE_WIDGET_LAYERS);

    TelemetryStopwatch.start("FX_GESTURE_INSTALL_SNAPSHOT_OF_PAGE");
    try {
      this._installCurrentPageSnapshot(canvas);
      this._assignSnapshotToCurrentBrowser(canvas);
    } finally {
      TelemetryStopwatch.finish("FX_GESTURE_INSTALL_SNAPSHOT_OF_PAGE");
    }
  },

  /**
   * Retrieves the maximum number of snapshots that should be kept in memory.
   * This limit is a global limit and is valid across all open tabs.
   */
  _getMaxSnapshots: function HSA__getMaxSnapshots() {
    return Services.prefs.getIntPref("browser.snapshots.limit");
  },

  /**
   * Adds a snapshot to the list and initiates the compression of said snapshot.
   * Once the compression is completed, it will replace the uncompressed
   * snapshot in the list.
   *
   * @param aCanvas
   *        The snapshot to add to the list and compress.
   */
  _assignSnapshotToCurrentBrowser:
  function HSA__assignSnapshotToCurrentBrowser(aCanvas) {
    let browser = gBrowser.selectedBrowser;
    let currIndex = this._getCurrentHistoryIndex();

    this._removeTrackedSnapshot(currIndex, browser);
    this._addSnapshotRefToArray(currIndex, browser);

    if (!("snapshots" in browser))
      browser.snapshots = [];
    let snapshots = browser.snapshots;
    // Temporarily store the canvas as the compressed snapshot.
    // This avoids a blank page if the user swipes quickly
    // between pages before the compression could complete.
    snapshots[currIndex] = {
      image: aCanvas,
      scale: window.devicePixelRatio
    };
  },

  /**
   * Compresses the HTMLCanvasElement that's stored at the current history
   * index in the snapshot array and stores the compressed image in its place.
   */
  _compressSnapshotAtCurrentIndex:
  function HSA__compressSnapshotAtCurrentIndex() {
    if (!this._readyToTakeSnapshots()) {
      // We didn't take a snapshot earlier because we weren't ready to, so
      // there's nothing to compress.
      return;
    }

    TelemetryStopwatch.start("FX_GESTURE_COMPRESS_SNAPSHOT_OF_PAGE");
    try {
      let browser = gBrowser.selectedBrowser;
      let snapshots = browser.snapshots;
      let currIndex = this._getCurrentHistoryIndex();

      // Kick off snapshot compression.
      let canvas = snapshots[currIndex].image;
      canvas.toBlob(function(aBlob) {
          if (snapshots[currIndex]) {
            snapshots[currIndex].image = aBlob;
          }
        }, "image/png"
      );
    } finally {
      TelemetryStopwatch.finish("FX_GESTURE_COMPRESS_SNAPSHOT_OF_PAGE");
    }
  },

  /**
   * Removes a snapshot identified by the browser and index in the array of
   * snapshots for that browser, if present. If no snapshot could be identified
   * the method simply returns without taking any action. If aIndex is negative,
   * all snapshots for a particular browser will be removed.
   *
   * @param aIndex
   *        The index in history of the new snapshot, or negative value if all
   *        snapshots for a browser should be removed.
   * @param aBrowser
   *        The browser the new snapshot was taken in.
   */
  _removeTrackedSnapshot: function HSA__removeTrackedSnapshot(aIndex, aBrowser) {
    let arr = this._trackedSnapshots;
    let requiresExactIndexMatch = aIndex >= 0;
    for (let i = 0; i < arr.length; i++) {
      if ((arr[i].browser == aBrowser) &&
          (aIndex < 0 || aIndex == arr[i].index)) {
        delete aBrowser.snapshots[arr[i].index];
        arr.splice(i, 1);
        if (requiresExactIndexMatch)
          return; // Found and removed the only element.
        i--; // Make sure to revisit the index that we just removed an
             // element at.
      }
    }
  },

  /**
   * Adds a new snapshot reference for a given index and browser to the array
   * of references to tracked snapshots.
   *
   * @param aIndex
   *        The index in history of the new snapshot.
   * @param aBrowser
   *        The browser the new snapshot was taken in.
   */
  _addSnapshotRefToArray:
  function HSA__addSnapshotRefToArray(aIndex, aBrowser) {
    let id = { index: aIndex,
               browser: aBrowser };
    let arr = this._trackedSnapshots;
    arr.unshift(id);

    while (arr.length > this._maxSnapshots) {
      let lastElem = arr[arr.length - 1];
      delete lastElem.browser.snapshots[lastElem.index].image;
      delete lastElem.browser.snapshots[lastElem.index];
      arr.splice(-1, 1);
    }
  },

  /**
   * Converts a compressed blob to an Image object. In some situations
   * (especially during fast swiping) aBlob may still be a canvas, not a
   * compressed blob. In this case, we simply return the canvas.
   *
   * @param aBlob
   *        The compressed blob to convert, or a canvas if a blob compression
   *        couldn't complete before this method was called.
   * @return A new Image object representing the converted blob.
   */
  _convertToImg: function HSA__convertToImg(aBlob) {
    if (!aBlob)
      return null;

    // Return aBlob if it's still a canvas and not a compressed blob yet.
    if (aBlob instanceof HTMLCanvasElement)
      return aBlob;

    let img = new Image();
    let url = "";
    try {
      url = URL.createObjectURL(aBlob);
      img.onload = function() {
        URL.revokeObjectURL(url);
      };
    } finally {
      img.src = url;
    }
    return img;
  },

  /**
   * Scales the background of a given box element (which uses a given snapshot
   * as background) based on a given scale factor.
   * @param aSnapshot
   *        The snapshot that is used as background of aBox.
   * @param aScale
   *        The scale factor to use.
   * @param aBox
   *        The box element that uses aSnapshot as background.
   */
  _scaleSnapshot: function HSA__scaleSnapshot(aSnapshot, aScale, aBox) {
    if (aSnapshot && aScale != 1 && aBox) {
      if (aSnapshot instanceof HTMLCanvasElement) {
        aBox.style.backgroundSize =
          aSnapshot.width / aScale + "px " + aSnapshot.height / aScale + "px";
      } else {
        // snapshot is instanceof HTMLImageElement
        aSnapshot.addEventListener("load", function() {
          aBox.style.backgroundSize =
            aSnapshot.width / aScale + "px " + aSnapshot.height / aScale + "px";
        });
      }
    }
  },

  /**
   * Sets the snapshot of the current page to the snapshot passed as parameter,
   * or to the one previously stored for the current index in history if the
   * parameter is null.
   *
   * @param aCanvas
   *        The snapshot to set the current page to. If this parameter is null,
   *        the previously stored snapshot for this index (if any) will be used.
   */
  _installCurrentPageSnapshot:
  function HSA__installCurrentPageSnapshot(aCanvas) {
    let currSnapshot = aCanvas;
    let scale = window.devicePixelRatio;
    if (!currSnapshot) {
      let snapshots = gBrowser.selectedBrowser.snapshots || {};
      let currIndex = this._historyIndex;
      if (currIndex in snapshots) {
        currSnapshot = this._convertToImg(snapshots[currIndex].image);
        scale = snapshots[currIndex].scale;
      }
    }
    this._scaleSnapshot(currSnapshot, scale, this._curBox ? this._curBox :
                                                            null);
    document.mozSetImageElement("historySwipeAnimationCurrentPageSnapshot",
                                currSnapshot);
  },

  /**
   * Sets the snapshots of the previous and next pages to the snapshots
   * previously stored for their respective indeces.
   */
  _installPrevAndNextSnapshots:
  function HSA__installPrevAndNextSnapshots() {
    let snapshots = gBrowser.selectedBrowser.snapshots || [];
    let currIndex = this._historyIndex;
    let prevIndex = currIndex - 1;
    let prevSnapshot = null;
    if (prevIndex in snapshots) {
      prevSnapshot = this._convertToImg(snapshots[prevIndex].image);
      this._scaleSnapshot(prevSnapshot, snapshots[prevIndex].scale,
                          this._prevBox);
    }
    document.mozSetImageElement("historySwipeAnimationPreviousPageSnapshot",
                                prevSnapshot);

    let nextIndex = currIndex + 1;
    let nextSnapshot = null;
    if (nextIndex in snapshots) {
      nextSnapshot = this._convertToImg(snapshots[nextIndex].image);
      this._scaleSnapshot(nextSnapshot, snapshots[nextIndex].scale,
                          this._nextBox);
    }
    document.mozSetImageElement("historySwipeAnimationNextPageSnapshot",
                                nextSnapshot);
  },
};
PK
!<���:�:/chrome/browser/content/browser/browser-media.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var gEMEHandler = {
  get uiEnabled() {
    let emeUIEnabled = Services.prefs.getBoolPref("browser.eme.ui.enabled");
    // Force-disable on WinXP:
    if (navigator.platform.toLowerCase().startsWith("win")) {
      emeUIEnabled = emeUIEnabled && parseFloat(Services.sysinfo.get("version")) >= 6;
    }
    return emeUIEnabled;
  },
  ensureEMEEnabled(browser, keySystem) {
    Services.prefs.setBoolPref("media.eme.enabled", true);
    if (keySystem &&
        keySystem == "com.widevine.alpha" &&
        Services.prefs.getPrefType("media.gmp-widevinecdm.enabled") &&
        !Services.prefs.getBoolPref("media.gmp-widevinecdm.enabled")) {
      Services.prefs.setBoolPref("media.gmp-widevinecdm.enabled", true);
    }
    browser.reload();
  },
  isKeySystemVisible(keySystem) {
    if (!keySystem) {
      return false;
    }
    if (keySystem == "com.widevine.alpha" &&
        Services.prefs.getPrefType("media.gmp-widevinecdm.visible")) {
      return Services.prefs.getBoolPref("media.gmp-widevinecdm.visible");
    }
    return true;
  },
  getEMEDisabledFragment(msgId) {
    let mainMessage = gNavigatorBundle.getString("emeNotifications.drmContentDisabled.message");
    let text = gNavigatorBundle.getString("emeNotifications.drmContentDisabled.learnMoreLabel");
    let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
    let link = document.createElement("label");
    link.className = "text-link";
    link.setAttribute("href", baseURL + "drm-content");
    link.textContent = text;
    return BrowserUtils.getLocalizedFragment(document, mainMessage, link);
  },
  getMessageWithBrandName(notificationId) {
    let msgId = "emeNotifications." + notificationId + ".message";
    return gNavigatorBundle.getFormattedString(msgId, [this._brandShortName]);
  },
  receiveMessage({target: browser, data: data}) {
    let parsedData;
    try {
      parsedData = JSON.parse(data);
    } catch (ex) {
      Cu.reportError("Malformed EME video message with data: " + data);
      return;
    }
    let {status: status, keySystem: keySystem} = parsedData;
    // Don't need to show if disabled or keysystem not visible.
    if (!this.uiEnabled || !this.isKeySystemVisible(keySystem)) {
      return;
    }

    let notificationId;
    let buttonCallback;
    // Notification message can be either a string or a DOM fragment.
    let notificationMessage;
    switch (status) {
      case "available":
      case "cdm-created":
        // Only show the chain icon for proprietary CDMs. Clearkey is not one.
        if (keySystem != "org.w3.clearkey") {
          this.showPopupNotificationForSuccess(browser, keySystem);
        }
        // ... and bail!
        return;

      case "api-disabled":
      case "cdm-disabled":
        notificationId = "drmContentDisabled";
        buttonCallback = gEMEHandler.ensureEMEEnabled.bind(gEMEHandler, browser, keySystem);
        notificationMessage = this.getEMEDisabledFragment();
        break;

      case "cdm-insufficient-version":
        notificationId = "drmContentCDMInsufficientVersion";
        notificationMessage = this.getMessageWithBrandName(notificationId);
        break;

      case "cdm-not-installed":
        notificationId = "drmContentCDMInstalling";
        notificationMessage = this.getMessageWithBrandName(notificationId);
        break;

      case "cdm-not-supported":
        // Not to pop up user-level notification because they cannot do anything
        // about it.
        return;
      default:
        Cu.reportError(new Error("Unknown message ('" + status + "') dealing with EME key request: " + data));
        return;
    }

    // Now actually create the notification

    let box = gBrowser.getNotificationBox(browser);
    if (box.getNotificationWithValue(notificationId)) {
      return;
    }

    let buttons = [];
    if (buttonCallback) {
      let msgPrefix = "emeNotifications." + notificationId + ".";
      let btnLabelId = msgPrefix + "button.label";
      let btnAccessKeyId = msgPrefix + "button.accesskey";
      buttons.push({
        label: gNavigatorBundle.getString(btnLabelId),
        accessKey: gNavigatorBundle.getString(btnAccessKeyId),
        callback: buttonCallback,
      });
    }

    let iconURL = "chrome://browser/skin/drm-icon.svg";
    box.appendNotification(notificationMessage, notificationId, iconURL, box.PRIORITY_WARNING_MEDIUM,
                           buttons);
  },
  showPopupNotificationForSuccess(browser, keySystem) {
    // We're playing EME content! Remove any "we can't play because..." messages.
    var box = gBrowser.getNotificationBox(browser);
    ["drmContentDisabled",
     "drmContentCDMInstalling"
     ].forEach(function(value) {
        var notification = box.getNotificationWithValue(value);
        if (notification)
          box.removeNotification(notification);
      });

    // Don't bother creating it if it's already there:
    if (PopupNotifications.getNotification("drmContentPlaying", browser)) {
      return;
    }

    let msgPrefix = "emeNotifications.drmContentPlaying.";
    let msgId = msgPrefix + "message2";
    let btnLabelId = msgPrefix + "button.label";
    let btnAccessKeyId = msgPrefix + "button.accesskey";

    let message = gNavigatorBundle.getFormattedString(msgId, [this._brandShortName]);
    let anchorId = "eme-notification-icon";
    let firstPlayPref = "browser.eme.ui.firstContentShown";
    if (!Services.prefs.getPrefType(firstPlayPref) ||
        !Services.prefs.getBoolPref(firstPlayPref)) {
      document.getElementById(anchorId).setAttribute("firstplay", "true");
      Services.prefs.setBoolPref(firstPlayPref, true);
    } else {
      document.getElementById(anchorId).removeAttribute("firstplay");
    }

    let mainAction = {
      label: gNavigatorBundle.getString(btnLabelId),
      accessKey: gNavigatorBundle.getString(btnAccessKeyId),
      callback() {
        openPreferences("general-drm", {origin: "browserMedia"});
      },
      dismiss: true
    };
    let options = {
      dismissed: true,
      eventCallback: aTopic => aTopic == "swapping",
      learnMoreURL: Services.urlFormatter.formatURLPref("app.support.baseURL") + "drm-content",
    };
    PopupNotifications.show(browser, "drmContentPlaying", message, anchorId, mainAction, null, options);
  },
  QueryInterface: XPCOMUtils.generateQI([Ci.nsIMessageListener])
};

XPCOMUtils.defineLazyGetter(gEMEHandler, "_brandShortName", function() {
  return document.getElementById("bundle_brand").getString("brandShortName");
});

const TELEMETRY_DDSTAT_SHOWN = 0;
const TELEMETRY_DDSTAT_SHOWN_FIRST = 1;
const TELEMETRY_DDSTAT_CLICKED = 2;
const TELEMETRY_DDSTAT_CLICKED_FIRST = 3;
const TELEMETRY_DDSTAT_SOLVED = 4;

let gDecoderDoctorHandler = {
  getLabelForNotificationBox(type) {
    if (type == "platform-decoder-not-found") {
      if (AppConstants.platform == "win") {
        return gNavigatorBundle.getString("decoder.noHWAcceleration.message");
      }
      if (AppConstants.platform == "linux") {
        return gNavigatorBundle.getString("decoder.noCodecsLinux.message");
      }
    }
    if (type == "cannot-initialize-pulseaudio") {
      return gNavigatorBundle.getString("decoder.noPulseAudio.message");
    }
    if (type == "unsupported-libavcodec" &&
        AppConstants.platform == "linux") {
      return gNavigatorBundle.getString("decoder.unsupportedLibavcodec.message");
    }
    if (type == "decode-error") {
      return gNavigatorBundle.getString("decoder.decodeError.message");
    }
    if (type == "decode-warning") {
      return gNavigatorBundle.getString("decoder.decodeWarning.message");
    }
    return "";
  },

  getSumoForLearnHowButton(type) {
    if (type == "platform-decoder-not-found" &&
        AppConstants.platform == "win") {
      return "fix-video-audio-problems-firefox-windows";
    }
    if (type == "cannot-initialize-pulseaudio") {
      return "fix-common-audio-and-video-issues";
    }
    return "";
  },

  getEndpointForReportIssueButton(type) {
    if (type == "decode-error" || type == "decode-warning") {
      return Services.prefs.getStringPref("media.decoder-doctor.new-issue-endpoint", "");
    }
    return "";
  },

  receiveMessage({target: browser, data: data}) {
    let box = gBrowser.getNotificationBox(browser);
    let notificationId = "decoder-doctor-notification";
    if (box.getNotificationWithValue(notificationId)) {
      return;
    }

    let parsedData;
    try {
      parsedData = JSON.parse(data);
    } catch (ex) {
      Cu.reportError("Malformed Decoder Doctor message with data: " + data);
      return;
    }
    // parsedData (the result of parsing the incoming 'data' json string)
    // contains analysis information from Decoder Doctor:
    // - 'type' is the type of issue, it determines which text to show in the
    //   infobar.
    // - 'isSolved' is true when the notification actually indicates the
    //   resolution of that issue, to be reported as telemetry.
    // - 'decoderDoctorReportId' is the Decoder Doctor issue identifier, to be
    //   used here as key for the telemetry (counting infobar displays,
    //   "Learn how" buttons clicks, and resolutions) and for the prefs used
    //   to store at-issue formats.
    // - 'formats' contains a comma-separated list of formats (or key systems)
    //   that suffer the issue. These are kept in a pref, which the backend
    //   uses to later find when an issue is resolved.
    // - 'decodeIssue' is a description of the decode error/warning.
    // - 'resourceURL' is the resource with the issue.
    let {type, isSolved, decoderDoctorReportId,
         formats, decodeIssue, docURL, resourceURL} = parsedData;
    type = type.toLowerCase();
    // Error out early on invalid ReportId
    if (!(/^\w+$/mi).test(decoderDoctorReportId)) {
      return;
    }
    let title = gDecoderDoctorHandler.getLabelForNotificationBox(type);
    if (!title) {
      return;
    }

    // We keep the list of formats in prefs for the sake of the decoder itself,
    // which reads it to determine when issues get solved for these formats.
    // (Writing prefs from e10s content is not allowed.)
    let formatsPref = formats &&
                      "media.decoder-doctor." + decoderDoctorReportId + ".formats";
    let buttonClickedPref = "media.decoder-doctor." + decoderDoctorReportId + ".button-clicked";
    let histogram =
      Services.telemetry.getKeyedHistogramById("DECODER_DOCTOR_INFOBAR_STATS");

    let formatsInPref = formats &&
                        Services.prefs.getCharPref(formatsPref, "");

    if (!isSolved) {
      if (formats) {
        if (!formatsInPref) {
          Services.prefs.setCharPref(formatsPref, formats);
          histogram.add(decoderDoctorReportId, TELEMETRY_DDSTAT_SHOWN_FIRST);
        } else {
          // Split existing formats into an array of strings.
          let existing = formatsInPref.split(",").map(x => x.trim());
          // Keep given formats that were not already recorded.
          let newbies = formats.split(",").map(x => x.trim())
                        .filter(x => !existing.includes(x));
          // And rewrite pref with the added new formats (if any).
          if (newbies.length) {
            Services.prefs.setCharPref(formatsPref,
                                      existing.concat(newbies).join(", "));
          }
        }
      } else if (!decodeIssue) {
        Cu.reportError("Malformed Decoder Doctor unsolved message with no formats nor decode issue");
        return;
      }
      histogram.add(decoderDoctorReportId, TELEMETRY_DDSTAT_SHOWN);

      let buttons = [];
      let sumo = gDecoderDoctorHandler.getSumoForLearnHowButton(type);
      if (sumo) {
        buttons.push({
          label: gNavigatorBundle.getString("decoder.noCodecs.button"),
          accessKey: gNavigatorBundle.getString("decoder.noCodecs.accesskey"),
          callback() {
            let clickedInPref =
              Services.prefs.getBoolPref(buttonClickedPref, false);
            if (!clickedInPref) {
              Services.prefs.setBoolPref(buttonClickedPref, true);
              histogram.add(decoderDoctorReportId, TELEMETRY_DDSTAT_CLICKED_FIRST);
            }
            histogram.add(decoderDoctorReportId, TELEMETRY_DDSTAT_CLICKED);

            let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
            openUILinkIn(baseURL + sumo, "tab");
          }
        });
      }
      let endpoint = gDecoderDoctorHandler.getEndpointForReportIssueButton(type);
      if (endpoint) {
        buttons.push({
          label: gNavigatorBundle.getString("decoder.decodeError.button"),
          accessKey: gNavigatorBundle.getString("decoder.decodeError.accesskey"),
          callback() {
            let clickedInPref =
              Services.prefs.getBoolPref(buttonClickedPref, false);
            if (!clickedInPref) {
              Services.prefs.setBoolPref(buttonClickedPref, true);
              histogram.add(decoderDoctorReportId, TELEMETRY_DDSTAT_CLICKED_FIRST);
            }
            histogram.add(decoderDoctorReportId, TELEMETRY_DDSTAT_CLICKED);

            let params = new URLSearchParams;
            params.append("url", docURL);
            params.append("label", "type-media");
            params.append("problem_type", "video_bug");
            params.append("src", "media-decode-error");

            let details = {"Technical Information:": decodeIssue};
            if (resourceURL) {
              details["Resource:"] = resourceURL;
            }

            params.append("details", JSON.stringify(details));
            openUILinkIn(endpoint + "?" + params.toString(), "tab");
          }
        });
      }

      box.appendNotification(
          title,
          notificationId,
          "", // This uses the info icon as specified below.
          box.PRIORITY_INFO_LOW,
          buttons
      );
    } else if (formatsInPref) {
      // Issue is solved, and prefs haven't been cleared yet, meaning it's the
      // first time we get this resolution -> Clear prefs and report telemetry.
      Services.prefs.clearUserPref(formatsPref);
      Services.prefs.clearUserPref(buttonClickedPref);
      histogram.add(decoderDoctorReportId, TELEMETRY_DDSTAT_SOLVED);
    }
  },
};

window.getGroupMessageManager("browsers").addMessageListener("DecoderDoctor:Notification", gDecoderDoctorHandler);
window.getGroupMessageManager("browsers").addMessageListener("EMEVideo:ContentMediaKeysRequest", gEMEHandler);
window.addEventListener("unload", function() {
  window.getGroupMessageManager("browsers").removeMessageListener("EMEVideo:ContentMediaKeysRequest", gEMEHandler);
  window.getGroupMessageManager("browsers").removeMessageListener("DecoderDoctor:Notification", gDecoderDoctorHandler);
});
PK
!<f�p�ׅׅ5chrome/browser/content/browser/browser-pageActions.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

var BrowserPageActions = {
  /**
   * The main page action button in the urlbar (DOM node)
   */
  get mainButtonNode() {
    delete this.mainButtonNode;
    return this.mainButtonNode = document.getElementById("pageActionButton");
  },

  /**
   * The main page action panel DOM node (DOM node)
   */
  get panelNode() {
    delete this.panelNode;
    return this.panelNode = document.getElementById("pageActionPanel");
  },

  /**
   * The panelmultiview node in the main page action panel (DOM node)
   */
  get multiViewNode() {
    delete this.multiViewNode;
    return this.multiViewNode = document.getElementById("pageActionPanelMultiView");
  },

  /**
   * The main panelview node in the main page action panel (DOM node)
   */
  get mainViewNode() {
    delete this.mainViewNode;
    return this.mainViewNode = document.getElementById("pageActionPanelMainView");
  },

  /**
   * The vbox body node in the main panelview node (DOM node)
   */
  get mainViewBodyNode() {
    delete this.mainViewBodyNode;
    return this.mainViewBodyNode = this.mainViewNode.querySelector(".panel-subview-body");
  },

  /**
   * Inits.  Call to init.
   */
  init() {
    this.placeAllActions();
  },

  /**
   * Places all registered actions.
   */
  placeAllActions() {
    // Place actions in the panel.  Notify of onBeforePlacedInWindow too.
    for (let action of PageActions.actions) {
      action.onBeforePlacedInWindow(window);
      this.placeActionInPanel(action);
    }

    // Place actions in the urlbar.  Do this in reverse order.  The reason is
    // subtle.  If there were no urlbar nodes already in markup (like the
    // bookmark star button), then doing this in forward order would be fine.
    // Forward order means that the insert-before relationship is always broken:
    // there's never a next-sibling node before which to insert a new node, so
    // node.insertBefore() is always passed null, and nodes are always appended.
    // That will break the position of nodes that should be inserted before
    // nodes that are in markup, which in turn can break other nodes.
    let actionsInUrlbar = PageActions.actionsInUrlbar(window);
    for (let i = actionsInUrlbar.length - 1; i >= 0; i--) {
      let action = actionsInUrlbar[i];
      this.placeActionInUrlbar(action);
    }
  },

  /**
   * Adds or removes as necessary DOM nodes for the given action.
   *
   * @param  action (PageActions.Action, required)
   *         The action to place.
   */
  placeAction(action) {
    action.onBeforePlacedInWindow(window);
    this.placeActionInPanel(action);
    this.placeActionInUrlbar(action);
  },

  /**
   * Adds or removes as necessary DOM nodes for the action in the panel.
   *
   * @param  action (PageActions.Action, required)
   *         The action to place.
   */
  placeActionInPanel(action) {
    let id = this.panelButtonNodeIDForActionID(action.id);
    let node = document.getElementById(id);
    if (!node) {
      let panelViewNode;
      [node, panelViewNode] = this._makePanelButtonNodeForAction(action);
      node.id = id;
      let insertBeforeID = PageActions.nextActionIDInPanel(action);
      let insertBeforeNode =
        insertBeforeID ? this.panelButtonNodeForActionID(insertBeforeID) :
        null;
      this.mainViewBodyNode.insertBefore(node, insertBeforeNode);
      this.updateAction(action);
      this._updateActionDisabledInPanel(action);
      action.onPlacedInPanel(node);
      if (panelViewNode) {
        action.subview.onPlaced(panelViewNode);
      }
    }
  },

  _makePanelButtonNodeForAction(action) {
    if (action.__isSeparator) {
      let node = document.createElement("toolbarseparator");
      return [node, null];
    }

    let buttonNode = document.createElement("toolbarbutton");
    buttonNode.classList.add(
      "subviewbutton",
      "subviewbutton-iconic",
      "pageAction-panel-button"
    );
    buttonNode.setAttribute("actionid", action.id);
    if (action.nodeAttributes) {
      for (let name in action.nodeAttributes) {
        buttonNode.setAttribute(name, action.nodeAttributes[name]);
      }
    }
    let panelViewNode = null;
    if (action.subview) {
      buttonNode.classList.add("subviewbutton-nav");
      panelViewNode = this._makePanelViewNodeForAction(action, false);
      this.multiViewNode.appendChild(panelViewNode);
    }
    buttonNode.addEventListener("command", event => {
      this.doCommandForAction(action, event, buttonNode);
    });
    return [buttonNode, panelViewNode];
  },

  _makePanelViewNodeForAction(action, forUrlbar) {
    let panelViewNode = document.createElement("panelview");
    panelViewNode.id = this._panelViewNodeIDForActionID(action.id, forUrlbar);
    panelViewNode.classList.add("PanelUI-subView");
    let bodyNode = document.createElement("vbox");
    bodyNode.id = panelViewNode.id + "-body";
    bodyNode.classList.add("panel-subview-body");
    panelViewNode.appendChild(bodyNode);
    for (let button of action.subview.buttons) {
      let buttonNode = document.createElement("toolbarbutton");
      buttonNode.id =
        this._panelViewButtonNodeIDForActionID(action.id, button.id, forUrlbar);
      buttonNode.classList.add("subviewbutton", "subviewbutton-iconic");
      buttonNode.setAttribute("label", button.title);
      if (button.shortcut) {
        buttonNode.setAttribute("shortcut", button.shortcut);
      }
      if (button.disabled) {
        buttonNode.setAttribute("disabled", "true");
      }
      buttonNode.addEventListener("command", event => {
        button.onCommand(event, buttonNode);
      });
      bodyNode.appendChild(buttonNode);
    }
    return panelViewNode;
  },

  /**
   * Shows or hides a panel for an action.  You can supply your own panel;
   * otherwise one is created.
   *
   * @param  action (PageActions.Action, required)
   *         The action for which to toggle the panel.  If the action is in the
   *         urlbar, then the panel will be anchored to it.  Otherwise, a
   *         suitable anchor will be used.
   * @param  panelNode (DOM node, optional)
   *         The panel to use.  This method takes a hands-off approach with
   *         regard to your panel in terms of attributes, styling, etc.
   */
  togglePanelForAction(action, panelNode = null) {
    let aaPanelNode = this.activatedActionPanelNode;
    if (panelNode) {
      // Note that this particular code path will not prevent the panel from
      // opening later if PanelMultiView.showPopup was called but the panel has
      // not been opened yet.
      if (panelNode.state != "closed") {
        PanelMultiView.hidePopup(panelNode);
        return;
      }
      if (aaPanelNode) {
        PanelMultiView.hidePopup(aaPanelNode);
      }
    } else if (aaPanelNode) {
      PanelMultiView.hidePopup(aaPanelNode);
      return;
    } else {
      panelNode = this._makeActivatedActionPanelForAction(action);
    }

    // Hide the main panel before showing the action's panel.
    PanelMultiView.hidePopup(this.panelNode);

    let anchorNode = this.panelAnchorNodeForAction(action);
    anchorNode.setAttribute("open", "true");
    panelNode.addEventListener("popuphiding", () => {
      anchorNode.removeAttribute("open");
    }, { once: true });

    PanelMultiView.openPopup(panelNode, anchorNode, "bottomcenter topright")
                  .catch(Cu.reportError);
  },

  _makeActivatedActionPanelForAction(action) {
    let panelNode = document.createElement("panel");
    panelNode.id = this._activatedActionPanelID;
    panelNode.classList.add("cui-widget-panel");
    panelNode.setAttribute("actionID", action.id);
    panelNode.setAttribute("role", "group");
    panelNode.setAttribute("type", "arrow");
    panelNode.setAttribute("flip", "slide");
    panelNode.setAttribute("noautofocus", "true");
    panelNode.setAttribute("tabspecific", "true");
    panelNode.setAttribute("photon", "true");

    if (this._disablePanelAnimations) {
      panelNode.setAttribute("animate", "false");
    }

    let panelViewNode = null;
    let iframeNode = null;

    if (action.subview) {
      let multiViewNode = document.createElement("panelmultiview");
      panelViewNode = this._makePanelViewNodeForAction(action, true);
      multiViewNode.setAttribute("mainViewId", panelViewNode.id);
      multiViewNode.appendChild(panelViewNode);
      panelNode.appendChild(multiViewNode);
    } else if (action.wantsIframe) {
      iframeNode = document.createElement("iframe");
      iframeNode.setAttribute("type", "content");
      panelNode.appendChild(iframeNode);
    }

    let popupSet = document.getElementById("mainPopupSet");
    popupSet.appendChild(panelNode);
    panelNode.addEventListener("popuphidden", () => {
      PanelMultiView.removePopup(panelNode);
    }, { once: true });

    if (iframeNode) {
      panelNode.addEventListener("popupshowing", () => {
        action.onIframeShowing(iframeNode, panelNode);
      }, { once: true });
      panelNode.addEventListener("popuphiding", () => {
        action.onIframeHiding(iframeNode, panelNode);
      }, { once: true });
      panelNode.addEventListener("popuphidden", () => {
        action.onIframeHidden(iframeNode, panelNode);
      }, { once: true });
    }

    if (panelViewNode) {
      action.subview.onPlaced(panelViewNode);
      panelNode.addEventListener("popupshowing", () => {
        action.subview.onShowing(panelViewNode);
      }, { once: true });
    }

    return panelNode;
  },

  // For tests.
  get _disablePanelAnimations() {
    return this.__disablePanelAnimations || false;
  },
  set _disablePanelAnimations(val) {
    this.__disablePanelAnimations = val;
    if (val) {
      this.panelNode.setAttribute("animate", "false");
    } else {
      this.panelNode.removeAttribute("animate");
    }
  },

  /**
   * Returns the node in the urlbar to which popups for the given action should
   * be anchored.  If the action is null, a sensible anchor is returned.
   *
   * @param  action (PageActions.Action, optional)
   *         The action you want to anchor.
   * @param  event (DOM event, optional)
   *         This is used to display the feedback panel on the right node when
   *         the command can be invoked from both the main panel and another
   *         location, such as an activated action panel or a button.
   * @return (DOM node, nonnull) The node to which the action should be
   *         anchored.
   */
  panelAnchorNodeForAction(action, event) {
    if (event && event.target.closest("panel") == this.panelNode) {
      return this.mainButtonNode;
    }

    // Try each of the following nodes in order, using the first that's visible.
    let potentialAnchorNodeIDs = [
      action && action.anchorIDOverride,
      action && this.urlbarButtonNodeIDForActionID(action.id),
      this.mainButtonNode.id,
      "identity-icon",
    ];
    let dwu = window.QueryInterface(Ci.nsIInterfaceRequestor)
                    .getInterface(Ci.nsIDOMWindowUtils);
    for (let id of potentialAnchorNodeIDs) {
      if (id) {
        let node = document.getElementById(id);
        if (node && !node.hidden) {
          let bounds = dwu.getBoundsWithoutFlushing(node);
          if (bounds.height > 0 && bounds.width > 0) {
            return node;
          }
        }
      }
    }
    let id = action ? action.id : "<no action>";
    throw new Error(`PageActions: No anchor node for ${id}`);
  },

  get activatedActionPanelNode() {
    return document.getElementById(this._activatedActionPanelID);
  },

  get _activatedActionPanelID() {
    return "pageActionActivatedActionPanel";
  },

  /**
   * Adds or removes as necessary a DOM node for the given action in the urlbar.
   *
   * @param  action (PageActions.Action, required)
   *         The action to place.
   */
  placeActionInUrlbar(action) {
    let id = this.urlbarButtonNodeIDForActionID(action.id);
    let node = document.getElementById(id);

    if (!action.shouldShowInUrlbar(window)) {
      if (node) {
        if (action.__urlbarNodeInMarkup) {
          node.hidden = true;
        } else {
          node.remove();
        }
      }
      return;
    }

    let newlyPlaced = false;
    if (action.__urlbarNodeInMarkup) {
      newlyPlaced = node && node.hidden;
      node.hidden = false;
    } else if (!node) {
      newlyPlaced = true;
      node = this._makeUrlbarButtonNode(action);
      node.id = id;
    }

    if (newlyPlaced) {
      let insertBeforeID = PageActions.nextActionIDInUrlbar(window, action);
      let insertBeforeNode =
        insertBeforeID ? this.urlbarButtonNodeForActionID(insertBeforeID) :
        null;
      this.mainButtonNode.parentNode.insertBefore(node, insertBeforeNode);
      this.updateAction(action);
      action.onPlacedInUrlbar(node);

      // urlbar buttons should always have tooltips, so if the node doesn't have
      // one, then as a last resort use the label of the corresponding panel
      // button.  Why not set tooltiptext to action.title when the node is
      // created?  Because the consumer may set a title dynamically.
      if (!node.hasAttribute("tooltiptext")) {
        let panelNode = this.panelButtonNodeForActionID(action.id);
        if (panelNode) {
          node.setAttribute("tooltiptext", panelNode.getAttribute("label"));
        }
      }
    }
  },

  _makeUrlbarButtonNode(action) {
    let buttonNode = document.createElement("image");
    buttonNode.classList.add("urlbar-icon", "urlbar-page-action");
    buttonNode.setAttribute("actionid", action.id);
    buttonNode.setAttribute("role", "button");
    if (action.nodeAttributes) {
      for (let name in action.nodeAttributes) {
        buttonNode.setAttribute(name, action.nodeAttributes[name]);
      }
    }
    buttonNode.addEventListener("click", event => {
      this.doCommandForAction(action, event, buttonNode);
    });
    return buttonNode;
  },

  /**
   * Removes all the DOM nodes of the given action.
   *
   * @param  action (PageActions.Action, required)
   *         The action to remove.
   */
  removeAction(action) {
    this._removeActionFromPanel(action);
    this._removeActionFromUrlbar(action);
    action.onRemovedFromWindow(window);
  },

  _removeActionFromPanel(action) {
    let node = this.panelButtonNodeForActionID(action.id);
    if (node) {
      node.remove();
    }
    if (action.subview) {
      let panelViewNodeID = this._panelViewNodeIDForActionID(action.id, false);
      let panelViewNode = document.getElementById(panelViewNodeID);
      if (panelViewNode) {
        panelViewNode.remove();
      }
    }
    // If there are now no more non-built-in actions, remove the separator
    // between the built-ins and non-built-ins.
    if (!PageActions.nonBuiltInActions.length) {
      let separator = document.getElementById(
        this.panelButtonNodeIDForActionID(
          PageActions.ACTION_ID_BUILT_IN_SEPARATOR
        )
      );
      if (separator) {
        separator.remove();
      }
    }
  },

  _removeActionFromUrlbar(action) {
    let node = this.urlbarButtonNodeForActionID(action.id);
    if (node) {
      node.remove();
    }
  },

  /**
   * Updates the DOM nodes of an action to reflect either a changed property or
   * all properties.
   *
   * @param  action (PageActions.Action, required)
   *         The action to update.
   * @param  propertyName (string, optional)
   *         The name of the property to update.  If not given, then DOM nodes
   *         will be updated to reflect the current values of all properties.
   */
  updateAction(action, propertyName = null) {
    let propertyNames = propertyName ? [propertyName] : [
      "iconURL",
      "title",
      "tooltip",
    ];
    for (let name of propertyNames) {
      let upper = name[0].toUpperCase() + name.substr(1);
      this[`_updateAction${upper}`](action);
    }
  },

  _updateActionDisabled(action) {
    this._updateActionDisabledInPanel(action);
    this.placeActionInUrlbar(action);
  },

  _updateActionDisabledInPanel(action) {
    let panelButton = this.panelButtonNodeForActionID(action.id);
    if (panelButton) {
      if (action.getDisabled(window)) {
        panelButton.setAttribute("disabled", "true");
      } else {
        panelButton.removeAttribute("disabled");
      }
    }
  },

  _updateActionIconURL(action) {
    let nodes = [
      this.panelButtonNodeForActionID(action.id),
      this.urlbarButtonNodeForActionID(action.id),
    ].filter(n => !!n);
    for (let node of nodes) {
      for (let size of [16, 32]) {
        let url = action.iconURLForSize(size, window);
        let prop = `--pageAction-image-${size}px`;
        if (url) {
          node.style.setProperty(prop, `url("${url}")`);
        } else {
          node.style.removeProperty(prop);
        }
      }
    }
  },

  _updateActionTitle(action) {
    let title = action.getTitle(window);
    if (!title) {
      // `title` is a required action property, but the bookmark action's is an
      // empty string since its actual title is set via
      // BookmarkingUI.updateBookmarkPageMenuItem().  The purpose of this early
      // return is to ignore that empty title.
      return;
    }
    let attrNamesByNodeFnName = {
      panelButtonNodeForActionID: "label",
      urlbarButtonNodeForActionID: "aria-label",
    };
    for (let [fnName, attrName] of Object.entries(attrNamesByNodeFnName)) {
      let node = this[fnName](action.id);
      if (node) {
        node.setAttribute(attrName, title);
      }
    }
    // tooltiptext falls back to the title, so update it, too.
    this._updateActionTooltip(action);
  },

  _updateActionTooltip(action) {
    let node = this.urlbarButtonNodeForActionID(action.id);
    if (node) {
      let tooltip = action.getTooltip(window) || action.getTitle(window);
      node.setAttribute("tooltiptext", tooltip);
    }
  },

  doCommandForAction(action, event, buttonNode) {
    if (event && event.type == "click" && event.button != 0) {
      return;
    }
    PageActions.logTelemetry("used", action, buttonNode);
    // If we're in the panel, open a subview inside the panel:
    // Note that we can't use this.panelNode.contains(buttonNode) here
    // because of XBL boundaries breaking Element.contains.
    if (action.subview &&
        buttonNode &&
        buttonNode.closest("panel") == this.panelNode) {
      let panelViewNodeID = this._panelViewNodeIDForActionID(action.id, false);
      let panelViewNode = document.getElementById(panelViewNodeID);
      action.subview.onShowing(panelViewNode);
      this.multiViewNode.showSubView(panelViewNode, buttonNode);
      return;
    }
    // Otherwise, hide the main popup in case it was open:
    PanelMultiView.hidePopup(this.panelNode);

    // Toggle the activated action's panel if necessary
    if (action.subview || action.wantsIframe) {
      this.togglePanelForAction(action);
      return;
    }

    // Otherwise, run the action.
    action.onCommand(event, buttonNode);
  },

  /**
   * Returns the action for a node.
   *
   * @param  node (DOM node, required)
   *         A button DOM node, either one that's shown in the page action panel
   *         or the urlbar.
   * @return (PageAction.Action) The node's related action, or null if none.
   */
  actionForNode(node) {
    if (!node) {
      return null;
    }
    let actionID = this._actionIDForNodeID(node.id);
    let action = PageActions.actionForID(actionID);
    if (!action) {
      // The given node may be an ancestor of a node corresponding to an action,
      // like how #star-button is contained in #star-button-box, the latter
      // being the bookmark action's node.  Look up the ancestor chain.
      for (let n = node.parentNode; n && !action; n = n.parentNode) {
        if (n.id == "page-action-buttons" || n.localName == "panelview") {
          // We reached the page-action-buttons or panelview container.
          // Stop looking; no acton was found.
          break;
        }
        actionID = this._actionIDForNodeID(n.id);
        action = PageActions.actionForID(actionID);
      }
    }
    return action;
  },

  /**
   * The given action's top-level button in the main panel.
   *
   * @param  actionID (string, required)
   *         The action ID.
   * @return (DOM node) The action's button in the main panel.
   */
  panelButtonNodeForActionID(actionID) {
    return document.getElementById(this.panelButtonNodeIDForActionID(actionID));
  },

  /**
   * The ID of the given action's top-level button in the main panel.
   *
   * @param  actionID (string, required)
   *         The action ID.
   * @return (string) The ID of the action's button in the main panel.
   */
  panelButtonNodeIDForActionID(actionID) {
    return `pageAction-panel-${actionID}`;
  },

  /**
   * The given action's button in the urlbar.
   *
   * @param  actionID (string, required)
   *         The action ID.
   * @return (DOM node) The action's urlbar button node.
   */
  urlbarButtonNodeForActionID(actionID) {
    return document.getElementById(this.urlbarButtonNodeIDForActionID(actionID));
  },

  /**
   * The ID of the given action's button in the urlbar.
   *
   * @param  actionID (string, required)
   *         The action ID.
   * @return (string) The ID of the action's urlbar button node.
   */
  urlbarButtonNodeIDForActionID(actionID) {
    let action = PageActions.actionForID(actionID);
    if (action && action.urlbarIDOverride) {
      return action.urlbarIDOverride;
    }
    return `pageAction-urlbar-${actionID}`;
  },

  // The ID of the given action's panelview.
  _panelViewNodeIDForActionID(actionID, forUrlbar) {
    let placementID = forUrlbar ? "urlbar" : "panel";
    return `pageAction-${placementID}-${actionID}-subview`;
  },

  // The ID of the given button in the given action's panelview.
  _panelViewButtonNodeIDForActionID(actionID, buttonID, forUrlbar) {
    let placementID = forUrlbar ? "urlbar" : "panel";
    return `pageAction-${placementID}-${actionID}-${buttonID}`;
  },

  // The ID of the action corresponding to the given top-level button in the
  // panel or button in the urlbar.
  _actionIDForNodeID(nodeID) {
    if (!nodeID) {
      return null;
    }
    let match = nodeID.match(/^pageAction-(?:panel|urlbar)-(.+)$/);
    if (match) {
      return match[1];
    }
    // Check all the urlbar ID overrides.
    for (let action of PageActions.actions) {
      if (action.urlbarIDOverride && action.urlbarIDOverride == nodeID) {
        return action.id;
      }
    }
    return null;
  },

  /**
   * Call this when the main page action button in the urlbar is activated.
   *
   * @param  event (DOM event, required)
   *         The click or whatever event.
   */
  mainButtonClicked(event) {
    event.stopPropagation();
    if ((event.type == "mousedown" && event.button != 0) ||
        (event.type == "keypress" && event.charCode != KeyEvent.DOM_VK_SPACE &&
         event.keyCode != KeyEvent.DOM_VK_RETURN)) {
      return;
    }

    // If the activated-action panel is open and anchored to the main button,
    // close it.
    let panelNode = this.activatedActionPanelNode;
    if (panelNode && panelNode.anchorNode.id == this.mainButtonNode.id) {
      PanelMultiView.hidePopup(panelNode);
      return;
    }

    if (this.panelNode.state == "open") {
      PanelMultiView.hidePopup(this.panelNode);
    } else if (this.panelNode.state == "closed") {
      this.showPanel(event);
    }
  },

  /**
   * Show the page action panel
   *
   * @param  event (DOM event, optional)
   *         The event that triggers showing the panel. (such as a mouse click,
   *         if the user clicked something to open the panel)
   */
  showPanel(event = null) {
    for (let action of PageActions.actions) {
      let buttonNode = this.panelButtonNodeForActionID(action.id);
      action.onShowingInPanel(buttonNode);
    }

    this.panelNode.hidden = false;
    this.panelNode.addEventListener("popuphiding", () => {
      this.mainButtonNode.removeAttribute("open");
    }, {once: true});
    this.mainButtonNode.setAttribute("open", "true");
    PanelMultiView.openPopup(this.panelNode, this.mainButtonNode, {
      position: "bottomcenter topright",
      triggerEvent: event,
    }).catch(Cu.reportError);
  },

  /**
   * Call this on the context menu's popupshowing event.
   *
   * @param  event (DOM event, required)
   *         The popupshowing event.
   * @param  popup (DOM node, required)
   *         The context menu popup DOM node.
   */
  onContextMenuShowing(event, popup) {
    if (event.target != popup) {
      return;
    }

    this._contextAction = this.actionForNode(popup.triggerNode);
    if (!this._contextAction) {
      event.preventDefault();
      return;
    }

    let state;
    if (this._contextAction._isBuiltIn) {
      state =
        this._contextAction.pinnedToUrlbar ?
        "builtInPinned" :
        "builtInUnpinned";
    } else {
      state =
        this._contextAction.pinnedToUrlbar ?
        "extensionPinned" :
        "extensionUnpinned";
    }
    popup.setAttribute("state", state);
  },

  /**
   * Call this from the menu item in the context menu that toggles pinning.
   */
  togglePinningForContextAction() {
    if (!this._contextAction) {
      return;
    }
    let action = this._contextAction;
    this._contextAction = null;

    let telemetryType = action.pinnedToUrlbar ? "removed" : "added";
    PageActions.logTelemetry(telemetryType, action);

    action.pinnedToUrlbar = !action.pinnedToUrlbar;
  },

  /**
   * Call this from the menu item in the context menu that opens about:addons.
   */
  openAboutAddonsForContextAction() {
    if (!this._contextAction) {
      return;
    }
    let action = this._contextAction;
    this._contextAction = null;

    PageActions.logTelemetry("managed", action);

    let viewID = "addons://detail/" + encodeURIComponent(action.extensionID);
    window.BrowserOpenAddonsMgr(viewID);
  },

  _contextAction: null,

  /**
   * Titles for a few of the built-in actions are defined in DTD, but the
   * actions are created in JS.  So what we do is for each title, set an
   * attribute in markup on the main page action panel whose value is the DTD
   * string.  In gBuiltInActions, where the built-in actions are defined, we set
   * the action's initial title to the name of this attribute.  Then when the
   * action is set up, we get the action's current title, and then get the
   * attribute on the main panel whose name is that title.  If the attribute
   * exists, then its value is the actual title, and we update the action with
   * this title.  Otherwise the action's title has already been set up in this
   * manner.
   *
   * @param  action (PageActions.Action, required)
   *         The action whose title you're setting.
   */
  takeActionTitleFromPanel(action) {
    let titleOrAttrNameOnPanel = action.getTitle();
    let attrValueOnPanel = this.panelNode.getAttribute(titleOrAttrNameOnPanel);
    if (attrValueOnPanel) {
      this.panelNode.removeAttribute(titleOrAttrNameOnPanel);
      action.setTitle(attrValueOnPanel);
    }
  },

  /**
   * This is similar to takeActionTitleFromPanel, except it sets an attribute on
   * a DOM node instead of setting the title on an action.  The point is to map
   * attributes on the node to strings on the main panel.  Use this for DOM
   * nodes that don't correspond to actions, like buttons in subviews.
   *
   * @param  node (DOM node, required)
   *         The node you're setting up.
   * @param  attrName (string, required)
   *         The name of the attribute *on the node you're setting up*.
   */
  takeNodeAttributeFromPanel(node, attrName) {
    let panelAttrName = node.getAttribute(attrName);
    if (!panelAttrName && attrName == "title") {
      attrName = "label";
      panelAttrName = node.getAttribute(attrName);
    }
    if (panelAttrName) {
      let attrValue = this.panelNode.getAttribute(panelAttrName);
      if (attrValue) {
        node.setAttribute(attrName, attrValue);
      }
    }
  },

  /**
   * Call this on tab switch or when the current <browser>'s location changes.
   */
  onLocationChange() {
    for (let action of PageActions.actions) {
      action.onLocationChange(window);
    }
  },
};


var BrowserPageActionFeedback = {
  /**
   * The feedback page action panel DOM node (DOM node)
   */
  get panelNode() {
    delete this.panelNode;
    return this.panelNode = document.getElementById("pageActionFeedback");
  },

  get feedbackAnimationBox() {
    delete this.feedbackAnimationBox;
    return this.feedbackAnimationBox = document.getElementById("pageActionFeedbackAnimatableBox");
  },

  get feedbackLabel() {
    delete this.feedbackLabel;
    return this.feedbackLabel = document.getElementById("pageActionFeedbackMessage");
  },

  show(action, event, textContentOverride) {
    this.feedbackLabel.textContent = this.panelNode.getAttribute((textContentOverride || action.id) + "Feedback");
    this.panelNode.hidden = false;

    let anchor = BrowserPageActions.panelAnchorNodeForAction(action, event);
    PanelMultiView.openPopup(this.panelNode, anchor, {
      position: "bottomcenter topright",
      triggerEvent: event,
    }).catch(Cu.reportError);

    this.panelNode.addEventListener("popupshown", () => {
      this.feedbackAnimationBox.setAttribute("animate", "true");

      // The timeout value used here allows the panel to stay open for
      // 1 second after the text transition (duration=120ms) has finished.
      setTimeout(() => {
        this.panelNode.hidePopup(true);
      }, Services.prefs.getIntPref("browser.pageActions.feedbackTimeoutMS", 1120));
    }, {once: true});
    this.panelNode.addEventListener("popuphidden", () => {
      this.feedbackAnimationBox.removeAttribute("animate");
    }, {once: true});
  },
};


// built-in actions below //////////////////////////////////////////////////////

// bookmark
BrowserPageActions.bookmark = {
  onShowingInPanel(buttonNode) {
    // Update the button label via the bookmark observer.
    BookmarkingUI.updateBookmarkPageMenuItem();
  },

  onCommand(event, buttonNode) {
    PanelMultiView.hidePopup(BrowserPageActions.panelNode);
    BookmarkingUI.onStarCommand(event);
  },
};

// copy URL
BrowserPageActions.copyURL = {
  onPlacedInPanel(buttonNode) {
    let action = PageActions.actionForID("copyURL");
    BrowserPageActions.takeActionTitleFromPanel(action);
  },

  onCommand(event, buttonNode) {
    PanelMultiView.hidePopup(BrowserPageActions.panelNode);
    Cc["@mozilla.org/widget/clipboardhelper;1"]
      .getService(Ci.nsIClipboardHelper)
      .copyString(gURLBar.makeURIReadable(gBrowser.selectedBrowser.currentURI).displaySpec);
    let action = PageActions.actionForID("copyURL");
    BrowserPageActionFeedback.show(action, event);
  },
};

// email link
BrowserPageActions.emailLink = {
  onPlacedInPanel(buttonNode) {
    let action = PageActions.actionForID("emailLink");
    BrowserPageActions.takeActionTitleFromPanel(action);
  },

  onCommand(event, buttonNode) {
    PanelMultiView.hidePopup(BrowserPageActions.panelNode);
    MailIntegration.sendLinkForBrowser(gBrowser.selectedBrowser);
  },
};

// send to device
BrowserPageActions.sendToDevice = {
  onPlacedInPanel(buttonNode) {
    let action = PageActions.actionForID("sendToDevice");
    BrowserPageActions.takeActionTitleFromPanel(action);
  },

  onSubviewPlaced(panelViewNode) {
    let bodyNode = panelViewNode.querySelector(".panel-subview-body");
    for (let node of bodyNode.childNodes) {
      BrowserPageActions.takeNodeAttributeFromPanel(node, "title");
      BrowserPageActions.takeNodeAttributeFromPanel(node, "shortcut");
    }
  },

  onLocationChange() {
    let action = PageActions.actionForID("sendToDevice");
    let browser = gBrowser.selectedBrowser;
    let url = browser.currentURI.spec;
    action.setDisabled(!gSync.isSendableURI(url), window);
  },

  onShowingSubview(panelViewNode) {
    let browser = gBrowser.selectedBrowser;
    let url = browser.currentURI.spec;
    let title = browser.contentTitle;

    let bodyNode = panelViewNode.querySelector(".panel-subview-body");
    let panelNode = panelViewNode.closest("panel");

    // This is on top because it also clears the device list between state
    // changes.
    gSync.populateSendTabToDevicesMenu(bodyNode, url, title, (clientId, name, clientType, lastModified) => {
      if (!name) {
        return document.createElement("toolbarseparator");
      }
      let item = document.createElement("toolbarbutton");
      item.classList.add("pageAction-sendToDevice-device", "subviewbutton");
      if (clientId) {
        item.classList.add("subviewbutton-iconic");
        item.setAttribute("tooltiptext", gSync.formatLastSyncDate(lastModified));
      }

      item.addEventListener("command", event => {
        if (panelNode) {
          PanelMultiView.hidePopup(panelNode);
        }
        // There are items in the subview that don't represent devices: "Sign
        // in", "Learn about Sync", etc.  Device items will be .sendtab-target.
        if (event.target.classList.contains("sendtab-target")) {
          let action = PageActions.actionForID("sendToDevice");
          let textOverride = gSync.offline && "sendToDeviceOffline";
          BrowserPageActionFeedback.show(action, event, textOverride);
        }
      });
      return item;
    });

    bodyNode.removeAttribute("state");
    // In the first ~10 sec after startup, Sync may not be loaded and the list
    // of devices will be empty.
    if (gSync.syncConfiguredAndLoading) {
      bodyNode.setAttribute("state", "notready");
      // Force a background Sync
      Services.tm.dispatchToMainThread(async () => {
        await Weave.Service.sync({why: "pageactions", engines: []}); // [] = clients engine only
        // There's no way Sync is still syncing at this point, but we check
        // anyway to avoid infinite looping.
        if (!window.closed && !gSync.syncConfiguredAndLoading) {
          this.onShowingSubview(panelViewNode);
        }
      });
    }
  },
};
PK
!<c����0chrome/browser/content/browser/browser-places.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 file is loaded into the browser window scope.
/* eslint-env mozilla/browser-window */

XPCOMUtils.defineLazyScriptGetter(this, ["PlacesToolbar", "PlacesMenu",
                                         "PlacesPanelview", "PlacesPanelMenuView"],
                                  "chrome://browser/content/places/browserPlacesViews.js");

var StarUI = {
  _itemGuids: null,
  _batching: false,
  _isNewBookmark: false,
  _isComposing: false,
  _autoCloseTimer: 0,
  // The autoclose timer is diasbled if the user interacts with the
  // popup, such as making a change through typing or clicking on
  // the popup.
  _autoCloseTimerEnabled: true,
  _removeBookmarksOnPopupHidden: false,

  _element(aID) {
    return document.getElementById(aID);
  },

  // Edit-bookmark panel
  get panel() {
    delete this.panel;
    var element = this._element("editBookmarkPanel");
    // initially the panel is hidden
    // to avoid impacting startup / new window performance
    element.hidden = false;
    element.addEventListener("keypress", this, {mozSystemGroup: true});
    element.addEventListener("mousedown", this);
    element.addEventListener("mouseout", this);
    element.addEventListener("mousemove", this);
    element.addEventListener("compositionstart", this);
    element.addEventListener("compositionend", this);
    element.addEventListener("input", this);
    element.addEventListener("popuphidden", this);
    element.addEventListener("popupshown", this);
    return this.panel = element;
  },

  // Array of command elements to disable when the panel is opened.
  get _blockedCommands() {
    delete this._blockedCommands;
    return this._blockedCommands =
      ["cmd_close", "cmd_closeWindow"].map(id => this._element(id));
  },

  _blockCommands: function SU__blockCommands() {
    this._blockedCommands.forEach(function(elt) {
      // make sure not to permanently disable this item (see bug 409155)
      if (elt.hasAttribute("wasDisabled"))
        return;
      if (elt.getAttribute("disabled") == "true") {
        elt.setAttribute("wasDisabled", "true");
      } else {
        elt.setAttribute("wasDisabled", "false");
        elt.setAttribute("disabled", "true");
      }
    });
  },

  _restoreCommandsState: function SU__restoreCommandsState() {
    this._blockedCommands.forEach(function(elt) {
      if (elt.getAttribute("wasDisabled") != "true")
        elt.removeAttribute("disabled");
      elt.removeAttribute("wasDisabled");
    });
  },

  // nsIDOMEventListener
  handleEvent(aEvent) {
    switch (aEvent.type) {
      case "mousemove":
        clearTimeout(this._autoCloseTimer);
        // The autoclose timer is not disabled on generic mouseout
        // because the user may not have actually interacted with the popup.
        break;
      case "popuphidden": {
        clearTimeout(this._autoCloseTimer);
        if (aEvent.originalTarget == this.panel) {
          if (!this._element("editBookmarkPanelContent").hidden)
            this.quitEditMode();

          if (this._anchorToolbarButton) {
            this._anchorToolbarButton.removeAttribute("open");
            this._anchorToolbarButton = null;
          }
          this._restoreCommandsState();
          let removeBookmarksOnPopupHidden = this._removeBookmarksOnPopupHidden;
          this._removeBookmarksOnPopupHidden = false;
          let guidsForRemoval = this._itemGuids;
          this._itemGuids = null;

          if (this._batching) {
            this.endBatch();
          }

          if (removeBookmarksOnPopupHidden && guidsForRemoval) {
            if (this._isNewBookmark) {
              PlacesTransactions.undo().catch(Cu.reportError);
              break;
            }
            // Remove all bookmarks for the bookmark's url, this also removes
            // the tags for the url.
            PlacesTransactions.Remove(guidsForRemoval)
                              .transact().catch(Cu.reportError);
          } else if (this._isNewBookmark) {
            LibraryUI.triggerLibraryAnimation("bookmark");
          }
        }
        break;
      }
      case "keypress":
        clearTimeout(this._autoCloseTimer);
        this._autoCloseTimerEnabled = false;

        if (aEvent.defaultPrevented) {
          // The event has already been consumed inside of the panel.
          break;
        }

        switch (aEvent.keyCode) {
          case KeyEvent.DOM_VK_ESCAPE:
            this.panel.hidePopup();
            break;
          case KeyEvent.DOM_VK_RETURN:
            if (aEvent.target.classList.contains("expander-up") ||
                aEvent.target.classList.contains("expander-down") ||
                aEvent.target.id == "editBMPanel_newFolderButton" ||
                aEvent.target.id == "editBookmarkPanelRemoveButton") {
              // XXX Why is this necessary? The defaultPrevented check should
              //    be enough.
              break;
            }
            this.panel.hidePopup();
            break;
          // This case is for catching character-generating keypresses
          case 0:
            let accessKey = document.getElementById("key_close");
            if (eventMatchesKey(aEvent, accessKey)) {
                this.panel.hidePopup();
            }
            break;
        }
        break;
      case "compositionend":
        // After composition is committed, "mouseout" or something can set
        // auto close timer.
        this._isComposing = false;
        break;
      case "compositionstart":
        if (aEvent.defaultPrevented) {
          // If the composition was canceled, nothing to do here.
          break;
        }
        this._isComposing = true;
        // Explicit fall-through, during composition, panel shouldn't be
        // hidden automatically.
      case "input":
        // Might have edited some text without keyboard events nor composition
        // events. Fall-through to cancel auto close in such case.
      case "mousedown":
        clearTimeout(this._autoCloseTimer);
        this._autoCloseTimerEnabled = false;
        break;
      case "mouseout":
        if (!this._autoCloseTimerEnabled) {
          // Don't autoclose the popup if the user has made a selection
          // or keypress and then subsequently mouseout.
          break;
        }
        // Explicit fall-through
      case "popupshown":
        // Don't handle events for descendent elements.
        if (aEvent.target != aEvent.currentTarget) {
          break;
        }
        // auto-close if new and not interacted with
        if (this._isNewBookmark && !this._isComposing) {
          // 3500ms matches the timeout that Pocket uses in
          // browser/extensions/pocket/content/panels/js/saved.js
          let delay = 3500;
          if (this._closePanelQuickForTesting) {
            delay /= 10;
          }
          clearTimeout(this._autoCloseTimer);
          this._autoCloseTimer = setTimeout(() => {
            if (!this.panel.mozMatchesSelector(":hover")) {
              this.panel.hidePopup(true);
            }
          }, delay);
          this._autoCloseTimerEnabled = true;
        }
        break;
    }
  },

  _overlayLoaded: false,
  _overlayLoading: false,
  async showEditBookmarkPopup(aNode, aAnchorElement, aPosition, aIsNewBookmark, aUrl) {
    // Slow double-clicks (not true double-clicks) shouldn't
    // cause the panel to flicker.
    if (this.panel.state == "showing" ||
        this.panel.state == "open") {
      return;
    }

    this._isNewBookmark = aIsNewBookmark;
    this._itemGuids = null;

    // Performance: load the overlay the first time the panel is opened
    // (see bug 392443).
    if (this._overlayLoading)
      return;

    if (this._overlayLoaded) {
      await this._doShowEditBookmarkPanel(aNode, aAnchorElement, aPosition, aUrl);
      return;
    }

    this._overlayLoading = true;
    document.loadOverlay(
      "chrome://browser/content/places/editBookmarkOverlay.xul",
      (aSubject, aTopic, aData) => {
        // Move the header (star, title, button) into the grid,
        // so that it aligns nicely with the other items (bug 484022).
        let header = this._element("editBookmarkPanelHeader");
        let rows = this._element("editBookmarkPanelGrid").lastChild;
        rows.insertBefore(header, rows.firstChild);
        header.hidden = false;

        this._overlayLoading = false;
        this._overlayLoaded = true;
        this._doShowEditBookmarkPanel(aNode, aAnchorElement, aPosition, aUrl);
      }
    );
  },

  async _doShowEditBookmarkPanel(aNode, aAnchorElement, aPosition, aUrl) {
    if (this.panel.state != "closed")
      return;

    this._blockCommands(); // un-done in the popuphidden handler

    this._element("editBookmarkPanelTitle").value =
      this._isNewBookmark ?
        gNavigatorBundle.getString("editBookmarkPanel.pageBookmarkedTitle") :
        gNavigatorBundle.getString("editBookmarkPanel.editBookmarkTitle");

    // No description; show the Done, Remove;
    this._element("editBookmarkPanelDescription").textContent = "";
    this._element("editBookmarkPanelBottomButtons").hidden = false;
    this._element("editBookmarkPanelContent").hidden = false;

    // The label of the remove button differs if the URI is bookmarked
    // multiple times.
    this._itemGuids = [];

    await PlacesUtils.bookmarks.fetch({url: aUrl},
      bookmark => this._itemGuids.push(bookmark.guid));

    let forms = gNavigatorBundle.getString("editBookmark.removeBookmarks.label");
    let bookmarksCount = this._itemGuids.length;
    let label = PluralForm.get(bookmarksCount, forms)
                          .replace("#1", bookmarksCount);
    this._element("editBookmarkPanelRemoveButton").label = label;

    this.beginBatch();

    if (aAnchorElement && aAnchorElement.closest("#urlbar")) {
      this._anchorToolbarButton = aAnchorElement;
      aAnchorElement.setAttribute("open", "true");
    } else {
      this._anchorToolbarButton = null;
    }

    let onPanelReady = fn => {
      let target = this.panel;
      if (target.parentNode) {
        // By targeting the panel's parent and using a capturing listener, we
        // can have our listener called before others waiting for the panel to
        // be shown (which probably expect the panel to be fully initialized)
        target = target.parentNode;
      }
      target.addEventListener("popupshown", function(event) {
        fn();
      }, {"capture": true, "once": true});
    };
    gEditItemOverlay.initPanel({ node: aNode,
                                 onPanelReady,
                                 hiddenRows: ["description", "location",
                                              "loadInSidebar", "keyword"],
                                 focusedElement: "preferred"});
    this.panel.openPopup(aAnchorElement, aPosition);
  },

  panelShown:
  function SU_panelShown(aEvent) {
    if (aEvent.target == this.panel) {
      if (this._element("editBookmarkPanelContent").hidden) {
        // Note this isn't actually used anymore, we should remove this
        // once we decide not to bring back the page bookmarked notification
        this.panel.focus();
      }
    }
  },

  quitEditMode: function SU_quitEditMode() {
    this._element("editBookmarkPanelContent").hidden = true;
    this._element("editBookmarkPanelBottomButtons").hidden = true;
    gEditItemOverlay.uninitPanel(true);
  },

  removeBookmarkButtonCommand: function SU_removeBookmarkButtonCommand() {
    this._removeBookmarksOnPopupHidden = true;
    this.panel.hidePopup();
  },

  // Matching the way it is used in the Library, editBookmarkOverlay implements
  // an instant-apply UI, having no batched-Undo/Redo support.
  // However, in this context (the Star UI) we have a Cancel button whose
  // expected behavior is to undo all the operations done in the panel.
  // Sometime in the future this needs to be reimplemented using a
  // non-instant apply code path, but for the time being, we patch-around
  // editBookmarkOverlay so that all of the actions done in the panel
  // are treated by PlacesTransactions as a single batch.  To do so,
  // we start a PlacesTransactions batch when the star UI panel is shown, and
  // we keep the batch ongoing until the panel is hidden.
  _batchBlockingDeferred: null,
  beginBatch() {
    if (this._batching)
      return;
    this._batchBlockingDeferred = PromiseUtils.defer();
    PlacesTransactions.batch(async () => {
      await this._batchBlockingDeferred.promise;
    });
    this._batching = true;
  },

  endBatch() {
    if (!this._batching)
      return;

    this._batchBlockingDeferred.resolve();
    this._batchBlockingDeferred = null;
    this._batching = false;
  }
};

// Checks if an element is visible without flushing layout changes.
function isVisible(element) {
  let windowUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)
                          .getInterface(Ci.nsIDOMWindowUtils);
  let bounds = windowUtils.getBoundsWithoutFlushing(element);
  return bounds.height > 0 && bounds.width > 0;
}

var PlacesCommandHook = {
  /**
   * Adds a bookmark to the page loaded in the given browser.
   *
   * @param aBrowser
   *        a <browser> element.
   * @param [optional] aShowEditUI
   *        whether or not to show the edit-bookmark UI for the bookmark item
   * @param [optional] aUrl
   *        Option to provide a URL to bookmark rather than the current page
   * @param [optional] aTitle
   *        Option to provide a title for a bookmark to use rather than the
   *        getting the current page's title
   */
  async bookmarkPage(aBrowser, aShowEditUI, aUrl = null, aTitle = null) {
    // If aUrl is provided, we want to bookmark that url rather than the
    // the current page
    let url = aUrl ? new URL(aUrl) : new URL(aBrowser.currentURI.spec);
    let info = await PlacesUtils.bookmarks.fetch({ url });
    let isNewBookmark = !info;
    if (!info) {
      let parentGuid = PlacesUtils.bookmarks.unfiledGuid;
      info = { url, parentGuid };
      // Bug 1148838 - Make this code work for full page plugins.
      let description = null;
      let charset = null;

      let docInfo = aUrl ? {} : await this._getPageDetails(aBrowser);

      try {
        if (docInfo.isErrorPage) {
          let entry = await PlacesUtils.history.fetch(aBrowser.currentURI);
          if (entry) {
            info.title = entry.title;
          }
        } else {
          info.title = aTitle || aBrowser.contentTitle;
        }
        info.title = info.title || url.href;
        description = docInfo.description;
        charset = aUrl ? null : aBrowser.characterSet;
      } catch (e) {
        Cu.reportError(e);
      }

      if (aShowEditUI && isNewBookmark) {
        // If we bookmark the page here but open right into a cancelable
        // state (i.e. new bookmark in Library), start batching here so
        // all of the actions can be undone in a single undo step.
        StarUI.beginBatch();
      }

      if (description) {
        info.annotations = [{ name: PlacesUIUtils.DESCRIPTION_ANNO,
                              value: description }];
      }

      info.guid = await PlacesTransactions.NewBookmark(info).transact();

      // Set the character-set
      if (charset && !PrivateBrowsingUtils.isBrowserPrivate(aBrowser))
         PlacesUtils.setCharsetForURI(makeURI(url.href), charset);
    }

    // Revert the contents of the location bar
    gURLBar.handleRevert();

    // If it was not requested to open directly in "edit" mode, we are done.
    if (!aShowEditUI)
      return;

    let node = await PlacesUIUtils.promiseNodeLikeFromFetchInfo(info);

    let anchor = BookmarkingUI.anchor;
    if (anchor) {
      await StarUI.showEditBookmarkPopup(node, anchor, "bottomcenter topright",
                                         isNewBookmark, url);
      return;
    }

    // Fall back to showing the panel over the content area.
    await StarUI.showEditBookmarkPopup(node, aBrowser, "overlap", isNewBookmark,
                                       url);
  },

  _getPageDetails(browser) {
    return new Promise(resolve => {
      let mm = browser.messageManager;
      mm.addMessageListener("Bookmarks:GetPageDetails:Result", function listener(msg) {
        mm.removeMessageListener("Bookmarks:GetPageDetails:Result", listener);
        resolve(msg.data);
      });

      mm.sendAsyncMessage("Bookmarks:GetPageDetails", { });
    });
  },

  /**
   * Adds a bookmark to the page targeted by a link.
   * @param parentId
   *        The folder in which to create a new bookmark if aURL isn't
   *        bookmarked.
   * @param url (string)
   *        the address of the link target
   * @param title
   *        The link text
   * @param [optional] description
   *        The linked page description, if available
   */
  async bookmarkLink(parentId, url, title, description = "") {
    let bm = await PlacesUtils.bookmarks.fetch({url});
    if (bm) {
      let node = await PlacesUIUtils.promiseNodeLikeFromFetchInfo(bm);
      PlacesUIUtils.showBookmarkDialog({ action: "edit", node }, window.top);
      return;
    }

    let parentGuid = parentId == PlacesUtils.bookmarksMenuFolderId ?
                       PlacesUtils.bookmarks.menuGuid :
                       await PlacesUtils.promiseItemGuid(parentId);
    let defaultInsertionPoint = new PlacesInsertionPoint({ parentId, parentGuid });
    PlacesUIUtils.showBookmarkDialog({ action: "add",
                                       type: "bookmark",
                                       uri: makeURI(url),
                                       title,
                                       description,
                                       defaultInsertionPoint,
                                       hiddenRows: [ "description",
                                                     "location",
                                                     "loadInSidebar",
                                                     "keyword" ]
                                     }, window.top);
  },

  /**
   * List of nsIURI objects characterizing the tabs currently open in the
   * browser, modulo pinned tabs.  The URIs will be in the order in which their
   * corresponding tabs appeared and duplicates are discarded.
   */
  get uniqueCurrentPages() {
    let uniquePages = {};
    let URIs = [];

    gBrowser.visibleTabs.forEach(tab => {
      let browser = tab.linkedBrowser;
      let uri = browser.currentURI;
      let title = browser.contentTitle || tab.label;
      let spec = uri.spec;
      if (!tab.pinned && !(spec in uniquePages)) {
        uniquePages[spec] = null;
        URIs.push({ uri, title });
      }
    });
    return URIs;
  },

  /**
   * Adds a folder with bookmarks to all of the currently open tabs in this
   * window.
   */
  bookmarkCurrentPages: function PCH_bookmarkCurrentPages() {
    let pages = this.uniqueCurrentPages;
    if (pages.length > 1) {
    PlacesUIUtils.showBookmarkDialog({ action: "add",
                                       type: "folder",
                                       URIList: pages,
                                       hiddenRows: [ "description" ]
                                     }, window);
    }
  },

  /**
   * Updates disabled state for the "Bookmark All Tabs" command.
   */
  updateBookmarkAllTabsCommand:
  function PCH_updateBookmarkAllTabsCommand() {
    // There's nothing to do in non-browser windows.
    if (window.location.href != getBrowserURL())
      return;

    // Disable "Bookmark All Tabs" if there are less than two
    // "unique current pages".
    goSetCommandEnabled("Browser:BookmarkAllTabs",
                        this.uniqueCurrentPages.length >= 2);
  },

  /**
   * Adds a Live Bookmark to a feed associated with the current page.
   * @param     url
   *            The nsIURI of the page the feed was attached to
   * @title     title
   *            The title of the feed. Optional.
   * @subtitle  subtitle
   *            A short description of the feed. Optional.
   */
  async addLiveBookmark(url, feedTitle, feedSubtitle) {
    let toolbarIP = new PlacesInsertionPoint({
      parentId: PlacesUtils.toolbarFolderId,
      parentGuid: PlacesUtils.bookmarks.toolbarGuid
    });

    let feedURI = makeURI(url);
    let title = feedTitle || gBrowser.contentTitle;
    let description = feedSubtitle;
    if (!description) {
      description = (await this._getPageDetails(gBrowser.selectedBrowser)).description;
    }

    PlacesUIUtils.showBookmarkDialog({ action: "add",
                                       type: "livemark",
                                       feedURI,
                                       siteURI: gBrowser.currentURI,
                                       title,
                                       description,
                                       defaultInsertionPoint: toolbarIP,
                                       hiddenRows: [ "feedLocation",
                                                     "siteLocation",
                                                     "description" ]
                                     }, window);
  },

  /**
   * Opens the Places Organizer.
   * @param {String} item The item to select in the organizer window,
   *                      options are (case sensitive):
   *                      BookmarksMenu, BookmarksToolbar, UnfiledBookmarks,
   *                      AllBookmarks, History, Downloads.
   */
  showPlacesOrganizer(item) {
    var organizer = Services.wm.getMostRecentWindow("Places:Organizer");
    // Due to bug 528706, getMostRecentWindow can return closed windows.
    if (!organizer || organizer.closed) {
      // No currently open places window, so open one with the specified mode.
      openDialog("chrome://browser/content/places/places.xul",
                 "", "chrome,toolbar=yes,dialog=no,resizable", item);
    } else {
      organizer.PlacesOrganizer.selectLeftPaneContainerByHierarchy(item);
      organizer.focus();
    }
  },

  searchBookmarks() {
    if (!focusAndSelectUrlBar()) {
      return;
    }
    for (let char of ["*", " "]) {
      let code = char.charCodeAt(0);
      gURLBar.inputField.dispatchEvent(new KeyboardEvent("keypress", {
        keyCode: code,
        charCode: code,
        bubbles: true
      }));
    }
  }
};

ChromeUtils.defineModuleGetter(this, "RecentlyClosedTabsAndWindowsMenuUtils",
  "resource:///modules/sessionstore/RecentlyClosedTabsAndWindowsMenuUtils.jsm");

// View for the history menu.
function HistoryMenu(aPopupShowingEvent) {
  // Workaround for Bug 610187.  The sidebar does not include all the Places
  // views definitions, and we don't need them there.
  // Defining the prototype inheritance in the prototype itself would cause
  // browser.js to halt on "PlacesMenu is not defined" error.
  this.__proto__.__proto__ = PlacesMenu.prototype;
  PlacesMenu.call(this, aPopupShowingEvent,
                  "place:sort=4&maxResults=15");
}

HistoryMenu.prototype = {
  _getClosedTabCount() {
    // SessionStore doesn't track the hidden window, so just return zero then.
    if (window == Services.appShell.hiddenDOMWindow) {
      return 0;
    }

    return SessionStore.getClosedTabCount(window);
  },

  toggleRecentlyClosedTabs: function HM_toggleRecentlyClosedTabs() {
    // enable/disable the Recently Closed Tabs sub menu
    var undoMenu = this._rootElt.getElementsByClassName("recentlyClosedTabsMenu")[0];

    // no restorable tabs, so disable menu
    if (this._getClosedTabCount() == 0)
      undoMenu.setAttribute("disabled", true);
    else
      undoMenu.removeAttribute("disabled");
  },

  /**
   * Populate when the history menu is opened
   */
  populateUndoSubmenu: function PHM_populateUndoSubmenu() {
    var undoMenu = this._rootElt.getElementsByClassName("recentlyClosedTabsMenu")[0];
    var undoPopup = undoMenu.firstChild;

    // remove existing menu items
    while (undoPopup.hasChildNodes())
      undoPopup.firstChild.remove();

    // no restorable tabs, so make sure menu is disabled, and return
    if (this._getClosedTabCount() == 0) {
      undoMenu.setAttribute("disabled", true);
      return;
    }

    // enable menu
    undoMenu.removeAttribute("disabled");

    // populate menu
    let tabsFragment = RecentlyClosedTabsAndWindowsMenuUtils.getTabsFragment(window, "menuitem");
    undoPopup.appendChild(tabsFragment);
  },

  toggleRecentlyClosedWindows: function PHM_toggleRecentlyClosedWindows() {
    // enable/disable the Recently Closed Windows sub menu
    var undoMenu = this._rootElt.getElementsByClassName("recentlyClosedWindowsMenu")[0];

    // no restorable windows, so disable menu
    if (SessionStore.getClosedWindowCount() == 0)
      undoMenu.setAttribute("disabled", true);
    else
      undoMenu.removeAttribute("disabled");
  },

  /**
   * Populate when the history menu is opened
   */
  populateUndoWindowSubmenu: function PHM_populateUndoWindowSubmenu() {
    let undoMenu = this._rootElt.getElementsByClassName("recentlyClosedWindowsMenu")[0];
    let undoPopup = undoMenu.firstChild;

    // remove existing menu items
    while (undoPopup.hasChildNodes())
      undoPopup.firstChild.remove();

    // no restorable windows, so make sure menu is disabled, and return
    if (SessionStore.getClosedWindowCount() == 0) {
      undoMenu.setAttribute("disabled", true);
      return;
    }

    // enable menu
    undoMenu.removeAttribute("disabled");

    // populate menu
    let windowsFragment = RecentlyClosedTabsAndWindowsMenuUtils.getWindowsFragment(window, "menuitem");
    undoPopup.appendChild(windowsFragment);
  },

  toggleTabsFromOtherComputers: function PHM_toggleTabsFromOtherComputers() {
    // Enable/disable the Tabs From Other Computers menu. Some of the menus handled
    // by HistoryMenu do not have this menuitem.
    let menuitem = this._rootElt.getElementsByClassName("syncTabsMenuItem")[0];
    if (!menuitem)
      return;

    if (!PlacesUIUtils.shouldShowTabsFromOtherComputersMenuitem()) {
      menuitem.setAttribute("hidden", true);
      return;
    }

    menuitem.setAttribute("hidden", false);
  },

  _onPopupShowing: function HM__onPopupShowing(aEvent) {
    PlacesMenu.prototype._onPopupShowing.apply(this, arguments);

    // Don't handle events for submenus.
    if (aEvent.target != aEvent.currentTarget)
      return;

    this.toggleRecentlyClosedTabs();
    this.toggleRecentlyClosedWindows();
    this.toggleTabsFromOtherComputers();
  },

  _onCommand: function HM__onCommand(aEvent) {
    let placesNode = aEvent.target._placesNode;
    if (placesNode) {
      if (!PrivateBrowsingUtils.isWindowPrivate(window))
        PlacesUIUtils.markPageAsTyped(placesNode.uri);
      openUILink(placesNode.uri, aEvent, { ignoreAlt: true });
    }
  }
};

/**
 * Functions for handling events in the Bookmarks Toolbar and menu.
 */
var BookmarksEventHandler = {
  /**
   * Handler for click event for an item in the bookmarks toolbar or menu.
   * Menus and submenus from the folder buttons bubble up to this handler.
   * Left-click is handled in the onCommand function.
   * When items are middle-clicked (or clicked with modifier), open in tabs.
   * If the click came through a menu, close the menu.
   * @param aEvent
   *        DOMEvent for the click
   * @param aView
   *        The places view which aEvent should be associated with.
   */

  onMouseUp(aEvent) {
    // Handles left-click with modifier if not browser.bookmarks.openInTabClosesMenu.
    if (aEvent.button != 0 || PlacesUIUtils.openInTabClosesMenu)
      return;
    let target = aEvent.originalTarget;
    if (target.tagName != "menuitem")
      return;
    let modifKey = AppConstants.platform === "macosx" ? aEvent.metaKey
                                                      : aEvent.ctrlKey;
    if (modifKey) {
      target.setAttribute("closemenu", "none");
    }
  },

  onClick: function BEH_onClick(aEvent, aView) {
    // Only handle middle-click or left-click with modifiers.
    let modifKey;
    if (AppConstants.platform == "macosx") {
      modifKey = aEvent.metaKey || aEvent.shiftKey;
    } else {
      modifKey = aEvent.ctrlKey || aEvent.shiftKey;
    }

    if (aEvent.button == 2 || (aEvent.button == 0 && !modifKey))
      return;

    var target = aEvent.originalTarget;
    // If this event bubbled up from a menu or menuitem,
    // close the menus if browser.bookmarks.openInTabClosesMenu.
    var tag = target.tagName;
    if (PlacesUIUtils.openInTabClosesMenu && (tag == "menuitem" || tag == "menu")) {
      closeMenus(aEvent.target);
    }
    // Command already processed so remove any closemenu attr set in onMouseUp.
    if (aEvent.button == 0 &&
        target.tagName == "menuitem" &&
        target.getAttribute("closemenu") == "none") {
      // On Mac we need to extend when we remove the flag, to avoid any pre-close
      // animations.
      setTimeout(() => {
        target.removeAttribute("closemenu");
      }, 500);
    }

    if (target._placesNode && PlacesUtils.nodeIsContainer(target._placesNode)) {
      // Don't open the root folder in tabs when the empty area on the toolbar
      // is middle-clicked or when a non-bookmark item except for Open in Tabs)
      // in a bookmarks menupopup is middle-clicked.
      if (target.localName == "menu" || target.localName == "toolbarbutton")
        PlacesUIUtils.openContainerNodeInTabs(target._placesNode, aEvent, aView);
    } else if (aEvent.button == 1) {
      // left-clicks with modifier are already served by onCommand
      this.onCommand(aEvent);
    }
  },

  /**
   * Handler for command event for an item in the bookmarks toolbar.
   * Menus and submenus from the folder buttons bubble up to this handler.
   * Opens the item.
   * @param aEvent
   *        DOMEvent for the command
   */
  onCommand: function BEH_onCommand(aEvent) {
    var target = aEvent.originalTarget;
    if (target._placesNode)
      PlacesUIUtils.openNodeWithEvent(target._placesNode, aEvent);
  },

  fillInBHTooltip: function BEH_fillInBHTooltip(aDocument, aEvent) {
    var node;
    var cropped = false;
    var targetURI;

    if (aDocument.tooltipNode.localName == "treechildren") {
      var tree = aDocument.tooltipNode.parentNode;
      var tbo = tree.treeBoxObject;
      var cell = tbo.getCellAt(aEvent.clientX, aEvent.clientY);
      if (cell.row == -1)
        return false;
      node = tree.view.nodeForTreeIndex(cell.row);
      cropped = tbo.isCellCropped(cell.row, cell.col);
    } else {
      // Check whether the tooltipNode is a Places node.
      // In such a case use it, otherwise check for targetURI attribute.
      var tooltipNode = aDocument.tooltipNode;
      if (tooltipNode._placesNode)
        node = tooltipNode._placesNode;
      else {
        // This is a static non-Places node.
        targetURI = tooltipNode.getAttribute("targetURI");
      }
    }

    if (!node && !targetURI)
      return false;

    // Show node.label as tooltip's title for non-Places nodes.
    var title = node ? node.title : tooltipNode.label;

    // Show URL only for Places URI-nodes or nodes with a targetURI attribute.
    var url;
    if (targetURI || PlacesUtils.nodeIsURI(node))
      url = targetURI || node.uri;

    // Show tooltip for containers only if their title is cropped.
    if (!cropped && !url)
      return false;

    var tooltipTitle = aDocument.getElementById("bhtTitleText");
    tooltipTitle.hidden = (!title || (title == url));
    if (!tooltipTitle.hidden)
      tooltipTitle.textContent = title;

    var tooltipUrl = aDocument.getElementById("bhtUrlText");
    tooltipUrl.hidden = !url;
    if (!tooltipUrl.hidden)
      tooltipUrl.value = url;

    // Show tooltip.
    return true;
  }
};

// Handles special drag and drop functionality for Places menus that are not
// part of a Places view (e.g. the bookmarks menu in the menubar).
var PlacesMenuDNDHandler = {
  _springLoadDelayMs: 350,
  _closeDelayMs: 500,
  _loadTimer: null,
  _closeTimer: null,
  _closingTimerNode: null,

  /**
   * Called when the user enters the <menu> element during a drag.
   * @param   event
   *          The DragEnter event that spawned the opening.
   */
  onDragEnter: function PMDH_onDragEnter(event) {
    // Opening menus in a Places popup is handled by the view itself.
    if (!this._isStaticContainer(event.target))
      return;

    // If we re-enter the same menu or anchor before the close timer runs out,
    // we should ensure that we do not close:
    if (this._closeTimer && this._closingTimerNode === event.currentTarget) {
      this._closeTimer.cancel();
      this._closingTimerNode = null;
      this._closeTimer = null;
    }

    PlacesControllerDragHelper.currentDropTarget = event.target;
    let popup = event.target.lastChild;
    if (this._loadTimer || popup.state === "showing" || popup.state === "open")
      return;

    this._loadTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
    this._loadTimer.initWithCallback(() => {
      this._loadTimer = null;
      popup.setAttribute("autoopened", "true");
      popup.showPopup(popup);
    }, this._springLoadDelayMs, Ci.nsITimer.TYPE_ONE_SHOT);
    event.preventDefault();
    event.stopPropagation();
  },

  /**
   * Handles dragleave on the <menu> element.
   */
  onDragLeave: function PMDH_onDragLeave(event) {
    // Handle menu-button separate targets.
    if (event.relatedTarget === event.currentTarget ||
        (event.relatedTarget &&
         event.relatedTarget.parentNode === event.currentTarget))
      return;

    // Closing menus in a Places popup is handled by the view itself.
    if (!this._isStaticContainer(event.target))
      return;

    PlacesControllerDragHelper.currentDropTarget = null;
    let popup = event.target.lastChild;

    if (this._loadTimer) {
      this._loadTimer.cancel();
      this._loadTimer = null;
    }
    this._closeTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
    this._closingTimerNode = event.currentTarget;
    this._closeTimer.initWithCallback(function() {
      this._closeTimer = null;
      this._closingTimerNode = null;
      let node = PlacesControllerDragHelper.currentDropTarget;
      let inHierarchy = false;
      while (node && !inHierarchy) {
        inHierarchy = node == event.target;
        node = node.parentNode;
      }
      if (!inHierarchy && popup && popup.hasAttribute("autoopened")) {
        popup.removeAttribute("autoopened");
        popup.hidePopup();
      }
    }, this._closeDelayMs, Ci.nsITimer.TYPE_ONE_SHOT);
  },

  /**
   * Determines if a XUL element represents a static container.
   * @returns true if the element is a container element (menu or
   *`         menu-toolbarbutton), false otherwise.
   */
  _isStaticContainer: function PMDH__isContainer(node) {
    let isMenu = node.localName == "menu" ||
                 (node.localName == "toolbarbutton" &&
                  (node.getAttribute("type") == "menu" ||
                   node.getAttribute("type") == "menu-button"));
    let isStatic = !("_placesNode" in node) && node.lastChild &&
                   node.lastChild.hasAttribute("placespopup") &&
                   !node.parentNode.hasAttribute("placespopup");
    return isMenu && isStatic;
  },

  /**
   * Called when the user drags over the <menu> element.
   * @param   event
   *          The DragOver event.
   */
  onDragOver: function PMDH_onDragOver(event) {
    let ip = new PlacesInsertionPoint({
      parentId: PlacesUtils.bookmarksMenuFolderId,
      parentGuid: PlacesUtils.bookmarks.menuGuid
    });
    if (ip && PlacesControllerDragHelper.canDrop(ip, event.dataTransfer))
      event.preventDefault();

    event.stopPropagation();
  },

  /**
   * Called when the user drops on the <menu> element.
   * @param   event
   *          The Drop event.
   */
  onDrop: function PMDH_onDrop(event) {
    // Put the item at the end of bookmark menu.
    let ip = new PlacesInsertionPoint({
      parentId: PlacesUtils.bookmarksMenuFolderId,
      parentGuid: PlacesUtils.bookmarks.menuGuid
    });
    PlacesControllerDragHelper.onDrop(ip, event.dataTransfer);
    PlacesControllerDragHelper.currentDropTarget = null;
    event.stopPropagation();
  }
};

/**
 * This object handles the initialization and uninitialization of the bookmarks
 * toolbar.
 */
var PlacesToolbarHelper = {
  _place: "place:folder=TOOLBAR",

  get _viewElt() {
    return document.getElementById("PlacesToolbar");
  },

  init: function PTH_init() {
    let viewElt = this._viewElt;
    if (!viewElt || viewElt._placesView)
      return;

    // CustomizableUI.addListener is idempotent, so we can safely
    // call this multiple times.
    CustomizableUI.addListener(this);

    if (!this._isObservingToolbars) {
      this._isObservingToolbars = true;
      window.addEventListener("toolbarvisibilitychange", this);
    }

    // If the bookmarks toolbar item is:
    // - not in a toolbar, or;
    // - the toolbar is collapsed, or;
    // - the toolbar is hidden some other way:
    // don't initialize.  Also, there is no need to initialize the toolbar if
    // customizing, because that will happen when the customization is done.
    let toolbar = this._getParentToolbar(viewElt);
    if (!toolbar || toolbar.collapsed || this._isCustomizing ||
        getComputedStyle(toolbar, "").display == "none") {
      return;
    }

    new PlacesToolbar(this._place);
  },

  handleEvent(event) {
    switch (event.type) {
      case "toolbarvisibilitychange":
        if (event.target == this._getParentToolbar(this._viewElt))
          this._resetView();
        break;
    }
  },

  uninit: function PTH_uninit() {
    if (this._isObservingToolbars) {
      delete this._isObservingToolbars;
      window.removeEventListener("toolbarvisibilitychange", this);
    }
    CustomizableUI.removeListener(this);
  },

  customizeStart: function PTH_customizeStart() {
    try {
      let viewElt = this._viewElt;
      if (viewElt && viewElt._placesView)
        viewElt._placesView.uninit();
    } finally {
      this._isCustomizing = true;
    }
  },

  customizeDone: function PTH_customizeDone() {
    this._isCustomizing = false;
    this.init();
  },

  onPlaceholderCommand() {
    let widgetGroup = CustomizableUI.getWidget("personal-bookmarks");
    let widget = widgetGroup.forWindow(window);
    if (widget.overflowed ||
        widgetGroup.areaType == CustomizableUI.TYPE_MENU_PANEL) {
      PlacesCommandHook.showPlacesOrganizer("BookmarksToolbar");
    }
  },

  _getParentToolbar(element) {
    while (element) {
      if (element.localName == "toolbar") {
        return element;
      }
      element = element.parentNode;
    }
    return null;
  },

  onWidgetUnderflow(aNode, aContainer) {
    // The view gets broken by being removed and reinserted by the overflowable
    // toolbar, so we have to force an uninit and reinit.
    let win = aNode.ownerGlobal;
    if (aNode.id == "personal-bookmarks" && win == window) {
      this._resetView();
    }
  },

  onWidgetAdded(aWidgetId, aArea, aPosition) {
    if (aWidgetId == "personal-bookmarks" && !this._isCustomizing) {
      // It's possible (with the "Add to Menu", "Add to Toolbar" context
      // options) that the Places Toolbar Items have been moved without
      // letting us prepare and handle it with with customizeStart and
      // customizeDone. If that's the case, we need to reset the views
      // since they're probably broken from the DOM reparenting.
      this._resetView();
    }
  },

  _resetView() {
    if (this._viewElt) {
      // It's possible that the placesView might not exist, and we need to
      // do a full init. This could happen if the Bookmarks Toolbar Items are
      // moved to the Menu Panel, and then to the toolbar with the "Add to Toolbar"
      // context menu option, outside of customize mode.
      if (this._viewElt._placesView) {
        this._viewElt._placesView.uninit();
      }
      this.init();
    }
  },
};

/**
 * Handles the Library button in the toolbar.
 */
var LibraryUI = {
  triggerLibraryAnimation(animation) {
    if (!this.hasOwnProperty("COSMETIC_ANIMATIONS_ENABLED")) {
      XPCOMUtils.defineLazyPreferenceGetter(this, "COSMETIC_ANIMATIONS_ENABLED",
        "toolkit.cosmeticAnimations.enabled", true);
    }

    let libraryButton = document.getElementById("library-button");
    if (!libraryButton ||
        libraryButton.getAttribute("cui-areatype") == "menu-panel" ||
        libraryButton.getAttribute("overflowedItem") == "true" ||
        !libraryButton.closest("#nav-bar") ||
        !window.toolbar.visible ||
        !this.COSMETIC_ANIMATIONS_ENABLED) {
      return;
    }

    let animatableBox = document.getElementById("library-animatable-box");
    let navBar = document.getElementById("nav-bar");
    let libraryIcon = document.getAnonymousElementByAttribute(libraryButton, "class", "toolbarbutton-icon");
    let dwu = window.getInterface(Ci.nsIDOMWindowUtils);
    let iconBounds = dwu.getBoundsWithoutFlushing(libraryIcon);
    let libraryBounds = dwu.getBoundsWithoutFlushing(libraryButton);
    let toolboxBounds = dwu.getBoundsWithoutFlushing(gNavToolbox);

    animatableBox.style.setProperty("--toolbox-y", toolboxBounds.y + "px");
    animatableBox.style.setProperty("--library-button-y", libraryBounds.y + "px");
    animatableBox.style.setProperty("--library-button-height", libraryBounds.height + "px");
    animatableBox.style.setProperty("--library-icon-x", iconBounds.x + "px");
    if (navBar.hasAttribute("brighttext")) {
      animatableBox.setAttribute("brighttext", "true");
    } else {
      animatableBox.removeAttribute("brighttext");
    }
    animatableBox.removeAttribute("fade");
    libraryButton.setAttribute("animate", animation);
    animatableBox.setAttribute("animate", animation);
    if (!this._libraryButtonAnimationEndListeners[animation]) {
      this._libraryButtonAnimationEndListeners[animation] = event => {
        this._libraryButtonAnimationEndListener(event, animation);
      };
    }
    animatableBox.addEventListener("animationend", this._libraryButtonAnimationEndListeners[animation]);

    window.addEventListener("resize", this._onWindowResize);
  },

  _libraryButtonAnimationEndListeners: {},
  _libraryButtonAnimationEndListener(aEvent, animation) {
    let animatableBox = document.getElementById("library-animatable-box");
    if (aEvent.animationName.startsWith(`library-${animation}-animation`)) {
      animatableBox.setAttribute("fade", "true");
    } else if (aEvent.animationName == `library-${animation}-fade`) {
      animatableBox.removeEventListener("animationend", LibraryUI._libraryButtonAnimationEndListeners[animation]);
      animatableBox.removeAttribute("animate");
      animatableBox.removeAttribute("fade");
      window.removeEventListener("resize", this._onWindowResize);
      let libraryButton = document.getElementById("library-button");
      // Put the 'fill' back in the normal icon.
      libraryButton.removeAttribute("animate");
    }
  },

  _windowResizeRunning: false,
  _onWindowResize(aEvent) {
    if (LibraryUI._windowResizeRunning) {
      return;
    }
    LibraryUI._windowResizeRunning = true;

    requestAnimationFrame(() => {
      let libraryButton = document.getElementById("library-button");
      // Only update the position if the library button remains in the
      // navbar (not moved to the palette or elsewhere).
      if (!libraryButton ||
          libraryButton.getAttribute("cui-areatype") == "menu-panel" ||
          libraryButton.getAttribute("overflowedItem") == "true" ||
          !libraryButton.closest("#nav-bar")) {
        return;
      }

      let animatableBox = document.getElementById("library-animatable-box");
      let libraryIcon = document.getAnonymousElementByAttribute(libraryButton, "class", "toolbarbutton-icon");
      let dwu = window.getInterface(Ci.nsIDOMWindowUtils);
      let iconBounds = dwu.getBoundsWithoutFlushing(libraryIcon);

      // Resizing the window will only have the ability to change the X offset of the
      // library button.
      animatableBox.style.setProperty("--library-icon-x", iconBounds.x + "px");

      LibraryUI._windowResizeRunning = false;
    });
  },
};

/**
 * Handles the bookmarks menu-button in the toolbar.
 */

var BookmarkingUI = {
  STAR_ID: "star-button",
  STAR_BOX_ID: "star-button-box",
  BOOKMARK_BUTTON_ID: "bookmarks-menu-button",
  BOOKMARK_BUTTON_SHORTCUT: "addBookmarkAsKb",
  get button() {
    delete this.button;
    let widgetGroup = CustomizableUI.getWidget(this.BOOKMARK_BUTTON_ID);
    return this.button = widgetGroup.forWindow(window).node;
  },

  get star() {
    delete this.star;
    return this.star = document.getElementById(this.STAR_ID);
  },

  get starBox() {
    delete this.starBox;
    return this.starBox = document.getElementById(this.STAR_BOX_ID);
  },

  get anchor() {
    let action = PageActions.actionForID(PageActions.ACTION_ID_BOOKMARK);
    return BrowserPageActions.panelAnchorNodeForAction(action);
  },

  get notifier() {
    delete this.notifier;
    return this.notifier = document.getElementById("bookmarked-notification-anchor");
  },

  get dropmarkerNotifier() {
    delete this.dropmarkerNotifier;
    return this.dropmarkerNotifier = document.getElementById("bookmarked-notification-dropmarker-anchor");
  },

  get broadcaster() {
    delete this.broadcaster;
    let broadcaster = document.getElementById("bookmarkThisPageBroadcaster");
    return this.broadcaster = broadcaster;
  },

  STATUS_UPDATING: -1,
  STATUS_UNSTARRED: 0,
  STATUS_STARRED: 1,
  get status() {
    if (this._pendingUpdate)
      return this.STATUS_UPDATING;
    return this.broadcaster.hasAttribute("starred") ? this.STATUS_STARRED
                                                    : this.STATUS_UNSTARRED;
  },

  get _starredTooltip() {
    delete this._starredTooltip;
    return this._starredTooltip =
      this._getFormattedTooltip("starButtonOn.tooltip2");
  },

  get _unstarredTooltip() {
    delete this._unstarredTooltip;
    return this._unstarredTooltip =
      this._getFormattedTooltip("starButtonOff.tooltip2");
  },

  _getFormattedTooltip(strId) {
    let args = [];
    let shortcut = document.getElementById(this.BOOKMARK_BUTTON_SHORTCUT);
    if (shortcut)
      args.push(ShortcutUtils.prettifyShortcut(shortcut));
    return gNavigatorBundle.getFormattedString(strId, args);
  },

  onPopupShowing: function BUI_onPopupShowing(event) {
    // Don't handle events for submenus.
    if (event.target != event.currentTarget)
      return;

    // On non-photon, this code should never be reached. However, if you click
    // the outer button's border, some cpp code for the menu button's XBL
    // binding decides to open the popup even though the dropmarker is invisible.
    //
    // Separately, in Photon, if the button is in the dynamic portion of the
    // overflow panel, we want to show a subview instead.
    if (this.button.getAttribute("cui-areatype") == CustomizableUI.TYPE_MENU_PANEL ||
        this.button.hasAttribute("overflowedItem")) {
      this._showSubView();
      event.preventDefault();
      event.stopPropagation();
      return;
    }

    let widget = CustomizableUI.getWidget(this.BOOKMARK_BUTTON_ID)
                               .forWindow(window);
    if (widget.overflowed) {
      // Don't open a popup in the overflow popup, rather just open the Library.
      event.preventDefault();
      widget.node.removeAttribute("closemenu");
      PlacesCommandHook.showPlacesOrganizer("BookmarksMenu");
      return;
    }

    this._initMobileBookmarks(document.getElementById("BMB_mobileBookmarks"));

    this.selectLabel("BMB_viewBookmarksSidebar",
                     SidebarUI.currentID == "viewBookmarksSidebar");
    this.selectLabel("BMB_viewBookmarksToolbar",
                     !document.getElementById("PersonalToolbar").collapsed);
  },

  selectLabel(elementId, visible) {
    let element = document.getElementById(elementId);
    element.setAttribute("label", element.getAttribute(visible ? "label-hide"
                                                               : "label-show"));
  },

  toggleBookmarksToolbar() {
    CustomizableUI.setToolbarVisibility("PersonalToolbar",
      document.getElementById("PersonalToolbar").collapsed);
  },

  attachPlacesView(event, node) {
    // If the view is already there, bail out early.
    if (node.parentNode._placesView)
      return;

    new PlacesMenu(event, "place:folder=BOOKMARKS_MENU", {
      extraClasses: {
        entry: "subviewbutton",
        footer: "panel-subview-footer"
      },
      insertionPoint: ".panel-subview-footer"
    });
  },

  // Set by sync after syncing bookmarks successfully once.
  MOBILE_BOOKMARKS_PREF: "browser.bookmarks.showMobileBookmarks",

  _shouldShowMobileBookmarks() {
    try {
      return Services.prefs.getBoolPref(this.MOBILE_BOOKMARKS_PREF);
    } catch (e) {}
    // No pref set (or invalid pref set), look for a mobile bookmarks left pane query.
    const organizerQueryAnno = "PlacesOrganizer/OrganizerQuery";
    const mobileBookmarksAnno = "MobileBookmarks";
    let shouldShow = PlacesUtils.annotations.getItemsWithAnnotation(organizerQueryAnno, {}).filter(
      id => PlacesUtils.annotations.getItemAnnotation(id, organizerQueryAnno) == mobileBookmarksAnno
    ).length > 0;
    // Sync will change this pref if/when it adds a mobile bookmarks query.
    Services.prefs.setBoolPref(this.MOBILE_BOOKMARKS_PREF, shouldShow);
    return shouldShow;
  },

  _initMobileBookmarks(mobileMenuItem) {
    mobileMenuItem.hidden = !this._shouldShowMobileBookmarks();
  },

  _uninitView: function BUI__uninitView() {
    // When an element with a placesView attached is removed and re-inserted,
    // XBL reapplies the binding causing any kind of issues and possible leaks,
    // so kill current view and let popupshowing generate a new one.
    if (this.button._placesView)
      this.button._placesView.uninit();
    // Also uninit the main menubar placesView, since it would have the same
    // issues.
    let menubar = document.getElementById("bookmarksMenu");
    if (menubar && menubar._placesView)
      menubar._placesView.uninit();

    // We have to do the same thing for the "special" views underneath the
    // the bookmarks menu.
    const kSpecialViewNodeIDs = ["BMB_bookmarksToolbar", "BMB_unsortedBookmarks"];
    for (let viewNodeID of kSpecialViewNodeIDs) {
      let elem = document.getElementById(viewNodeID);
      if (elem && elem._placesView) {
        elem._placesView.uninit();
      }
    }
  },

  onCustomizeStart: function BUI_customizeStart(aWindow) {
    if (aWindow == window) {
      this._uninitView();
      this._isCustomizing = true;
    }
  },

  onWidgetAdded: function BUI_widgetAdded(aWidgetId) {
    if (aWidgetId == this.BOOKMARK_BUTTON_ID) {
      this._onWidgetWasMoved();
    }
  },

  onWidgetRemoved: function BUI_widgetRemoved(aWidgetId) {
    if (aWidgetId == this.BOOKMARK_BUTTON_ID) {
      this._onWidgetWasMoved();
    }
  },

  onWidgetReset: function BUI_widgetReset(aNode, aContainer) {
    if (aNode == this.button) {
      this._onWidgetWasMoved();
    }
  },

  onWidgetUndoMove: function BUI_undoWidgetUndoMove(aNode, aContainer) {
    if (aNode == this.button) {
      this._onWidgetWasMoved();
    }
  },

  _onWidgetWasMoved: function BUI_widgetWasMoved() {
    // If we're moved outside of customize mode, we need to uninit
    // our view so it gets reconstructed.
    if (!this._isCustomizing) {
      this._uninitView();
    }
  },

  onCustomizeEnd: function BUI_customizeEnd(aWindow) {
    if (aWindow == window) {
      this._isCustomizing = false;
    }
  },

  init() {
    CustomizableUI.addListener(this);

    if (Services.prefs.getBoolPref("toolkit.cosmeticAnimations.enabled")) {
      let starButtonBox = document.getElementById("star-button-box");
      starButtonBox.setAttribute("animationsenabled", "true");
      this.star.addEventListener("mouseover", this, {once: true});
    }
  },

  _hasBookmarksObserver: false,
  _itemGuids: new Set(),
  uninit: function BUI_uninit() {
    this.updateBookmarkPageMenuItem(true);
    CustomizableUI.removeListener(this);

    this.star.removeEventListener("mouseover", this);

    this._uninitView();

    if (this._hasBookmarksObserver) {
      PlacesUtils.bookmarks.removeObserver(this);
    }

    if (this._pendingUpdate) {
      delete this._pendingUpdate;
    }
  },

  onLocationChange: function BUI_onLocationChange() {
    if (this._uri && gBrowser.currentURI.equals(this._uri)) {
      return;
    }
    this.updateStarState();
  },

  updateStarState: function BUI_updateStarState() {
    this._uri = gBrowser.currentURI;
    this._itemGuids.clear();
    let guids = new Set();

    // those objects are use to check if we are in the current iteration before
    // returning any result.
    let pendingUpdate = this._pendingUpdate = {};

    PlacesUtils.bookmarks.fetch({url: this._uri}, b => guids.add(b.guid), { concurrent: true })
      .catch(Cu.reportError)
      .then(() => {
         if (pendingUpdate != this._pendingUpdate) {
           return;
         }

         // It's possible that onItemAdded gets called before the async statement
         // calls back.  For such an edge case, retain all unique entries from the
         // array.
         if (this._itemGuids.size > 0) {
           this._itemGuids = new Set(...this._itemGuids, ...guids);
         } else {
           this._itemGuids = guids;
         }

         this._updateStar();

         // Start observing bookmarks if needed.
         if (!this._hasBookmarksObserver) {
           try {
             PlacesUtils.bookmarks.addObserver(this);
             this._hasBookmarksObserver = true;
           } catch (ex) {
             Cu.reportError("BookmarkingUI failed adding a bookmarks observer: " + ex);
           }
         }

         delete this._pendingUpdate;
       });
  },

  _updateStar: function BUI__updateStar() {
    if (this._itemGuids.size > 0) {
      this.broadcaster.setAttribute("starred", "true");
      this.broadcaster.setAttribute("buttontooltiptext", this._starredTooltip);
      this.broadcaster.setAttribute("tooltiptext", this._starredTooltip);
    } else {
      this.star.removeAttribute("animate");
      this.broadcaster.removeAttribute("starred");
      this.broadcaster.setAttribute("buttontooltiptext", this._unstarredTooltip);
      this.broadcaster.setAttribute("tooltiptext", this._unstarredTooltip);
    }
  },

  /**
   * forceReset is passed when we're destroyed and the label should go back
   * to the default (Bookmark This Page) for OS X.
   */
  updateBookmarkPageMenuItem: function BUI_updateBookmarkPageMenuItem(forceReset) {
    let isStarred = !forceReset && this._itemGuids.size > 0;
    let label = isStarred ? "editlabel" : "bookmarklabel";
    if (this.broadcaster) {
      this.broadcaster.setAttribute("label", this.broadcaster.getAttribute(label));
    }
  },

  onMainMenuPopupShowing: function BUI_onMainMenuPopupShowing(event) {
    // Don't handle events for submenus.
    if (event.target != event.currentTarget)
      return;

    this.updateBookmarkPageMenuItem();
    PlacesCommandHook.updateBookmarkAllTabsCommand();
    this._initMobileBookmarks(document.getElementById("menu_mobileBookmarks"));
  },

  _showBookmarkedNotification: function BUI_showBookmarkedNotification() {
    function getCenteringTransformForRects(rectToPosition, referenceRect) {
      let topDiff = referenceRect.top - rectToPosition.top;
      let leftDiff = referenceRect.left - rectToPosition.left;
      let heightDiff = referenceRect.height - rectToPosition.height;
      let widthDiff = referenceRect.width - rectToPosition.width;
      return [(leftDiff + .5 * widthDiff) + "px", (topDiff + .5 * heightDiff) + "px"];
    }

    if (this._notificationTimeout) {
      clearTimeout(this._notificationTimeout);
    }

    if (this.notifier.style.transform == "") {
      // Get all the relevant nodes and computed style objects
      let dropmarker = document.getAnonymousElementByAttribute(this.button, "anonid", "dropmarker");
      let dropmarkerIcon = document.getAnonymousElementByAttribute(dropmarker, "class", "dropmarker-icon");
      let dropmarkerStyle = getComputedStyle(dropmarkerIcon);

      // Check for RTL and get bounds
      let isRTL = getComputedStyle(this.button).direction == "rtl";
      let buttonRect = this.button.getBoundingClientRect();
      let notifierRect = this.notifier.getBoundingClientRect();
      let dropmarkerRect = dropmarkerIcon.getBoundingClientRect();
      let dropmarkerNotifierRect = this.dropmarkerNotifier.getBoundingClientRect();

      // Compute, but do not set, transform for star icon
      let [translateX, translateY] = getCenteringTransformForRects(notifierRect, buttonRect);
      let starIconTransform = "translate(" + translateX + ", " + translateY + ")";
      if (isRTL) {
        starIconTransform += " scaleX(-1)";
      }

      // Compute, but do not set, transform for dropmarker
      [translateX, translateY] = getCenteringTransformForRects(dropmarkerNotifierRect, dropmarkerRect);
      let dropmarkerTransform = "translate(" + translateX + ", " + translateY + ")";

      // Do all layout invalidation in one go:
      this.notifier.style.transform = starIconTransform;
      this.dropmarkerNotifier.style.transform = dropmarkerTransform;

      let dropmarkerAnimationNode = this.dropmarkerNotifier.firstChild;
      dropmarkerAnimationNode.style.listStyleImage = dropmarkerStyle.listStyleImage;
      dropmarkerAnimationNode.style.fill = dropmarkerStyle.fill;
    }

    let isInOverflowPanel = this.button.getAttribute("overflowedItem") == "true";
    if (!isInOverflowPanel) {
      this.notifier.setAttribute("notification", "finish");
      this.button.setAttribute("notification", "finish");
      this.dropmarkerNotifier.setAttribute("notification", "finish");
    }

    this._notificationTimeout = setTimeout( () => {
      this.notifier.removeAttribute("notification");
      this.dropmarkerNotifier.removeAttribute("notification");
      this.button.removeAttribute("notification");

      this.dropmarkerNotifier.style.transform = "";
      this.notifier.style.transform = "";
    }, 1000);
  },

  showSubView(anchor) {
    this._showSubView(null, anchor);
  },

  _showSubView(event, anchor = document.getElementById(this.BOOKMARK_BUTTON_ID)) {
    let view = document.getElementById("PanelUI-bookmarks");
    view.addEventListener("ViewShowing", this);
    view.addEventListener("ViewHiding", this);
    anchor.setAttribute("closemenu", "none");
    PanelUI.showSubView("PanelUI-bookmarks", anchor, event);
  },

  onCommand: function BUI_onCommand(aEvent) {
    if (aEvent.target != aEvent.currentTarget) {
      return;
    }

    // Handle special case when the button is in the panel.
    if (this.button.getAttribute("cui-areatype") == CustomizableUI.TYPE_MENU_PANEL) {
      this._showSubView(aEvent);
      return;
    }
    let widget = CustomizableUI.getWidget(this.BOOKMARK_BUTTON_ID)
                               .forWindow(window);
    if (widget.overflowed) {
      // Close the overflow panel because the Edit Bookmark panel will appear.
      widget.node.removeAttribute("closemenu");
    }
    this.onStarCommand(aEvent);
  },

  onStarCommand(aEvent) {
    // Ignore non-left clicks on the star, or if we are updating its state.
    if (!this._pendingUpdate && (aEvent.type != "click" || aEvent.button == 0)) {
      let isBookmarked = this._itemGuids.size > 0;
      if (!isBookmarked) {
        BrowserUtils.setToolbarButtonHeightProperty(this.star);
        this.star.setAttribute("animate", "true");
      }
      PlacesCommandHook.bookmarkPage(gBrowser.selectedBrowser, true);
    }
  },

  onCurrentPageContextPopupShowing() {
    this.updateBookmarkPageMenuItem();
  },

  handleEvent: function BUI_handleEvent(aEvent) {
    switch (aEvent.type) {
      case "mouseover":
        this.star.setAttribute("preloadanimations", "true");
        break;
      case "ViewShowing":
        this.onPanelMenuViewShowing(aEvent);
        break;
      case "ViewHiding":
        this.onPanelMenuViewHiding(aEvent);
        break;
    }
  },

  onPanelMenuViewShowing: function BUI_onViewShowing(aEvent) {
    let panelview = aEvent.target;
    this.updateBookmarkPageMenuItem();
    // Update checked status of the toolbar toggle.
    let viewToolbar = document.getElementById("panelMenu_viewBookmarksToolbar");
    if (viewToolbar) {
      let personalToolbar = document.getElementById("PersonalToolbar");
      if (personalToolbar.collapsed)
        viewToolbar.removeAttribute("checked");
      else
        viewToolbar.setAttribute("checked", "true");
    }
    // Get all statically placed buttons to supply them with keyboard shortcuts.
    let staticButtons = panelview.getElementsByTagName("toolbarbutton");
    for (let i = 0, l = staticButtons.length; i < l; ++i)
      CustomizableUI.addShortcut(staticButtons[i]);
    // Setup the Places view.
    // We restrict the amount of results to 42. Not 50, but 42. Why? Because 42.
    let query = "place:queryType=" + Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS +
      "&sort=" + Ci.nsINavHistoryQueryOptions.SORT_BY_DATEADDED_DESCENDING +
      "&maxResults=42&excludeQueries=1";

    this._panelMenuView = new PlacesPanelview(document.getElementById("panelMenu_bookmarksMenu"),
      panelview, query);
    panelview.removeEventListener("ViewShowing", this);
  },

  onPanelMenuViewHiding: function BUI_onViewHiding(aEvent) {
    this._panelMenuView.uninit();
    delete this._panelMenuView;
    aEvent.target.removeEventListener("ViewHiding", this);
  },

  onPanelMenuViewCommand: function BUI_onPanelMenuViewCommand(aEvent) {
    let target = aEvent.originalTarget;
    if (!target._placesNode)
      return;
    if (PlacesUtils.nodeIsContainer(target._placesNode))
      PlacesCommandHook.showPlacesOrganizer([ "BookmarksMenu", target._placesNode.itemId ]);
    else
      PlacesUIUtils.openNodeWithEvent(target._placesNode, aEvent);
    PanelUI.hide();
  },

  showBookmarkingTools(triggerNode) {
    let placement = CustomizableUI.getPlacementOfWidget(this.BOOKMARK_BUTTON_ID);
    this.selectLabel("panelMenu_toggleBookmarksMenu",
                     placement && placement.area == CustomizableUI.AREA_NAVBAR);
    this.selectLabel("panelMenu_viewBookmarksSidebar",
                     SidebarUI.currentID == "viewBookmarksSidebar");
    this.selectLabel("panelMenu_viewBookmarksToolbar",
                     !document.getElementById("PersonalToolbar").collapsed);
    PanelUI.showSubView("PanelUI-bookmarkingTools", triggerNode);
  },

  toggleMenuButtonInToolbar(triggerNode) {
    let placement = CustomizableUI.getPlacementOfWidget(this.BOOKMARK_BUTTON_ID);
    const area = CustomizableUI.AREA_NAVBAR;
    if (!placement) {
      // Button is in the palette, so we can move it to the navbar.
      let pos;
      let widgetIDs = CustomizableUI.getWidgetIdsInArea(CustomizableUI.AREA_NAVBAR);
      // If there's a spring inside the navbar, find it and use that as the
      // placement marker.
      let lastSpringID = null;
      for (let i = widgetIDs.length - 1; i >= 0; --i) {
        let id = widgetIDs[i];
        if (CustomizableUI.isSpecialWidget(id) && /spring/.test(id)) {
          lastSpringID = id;
          break;
        }
      }
      if (lastSpringID) {
        pos = CustomizableUI.getPlacementOfWidget(lastSpringID).position + 1;
      } else {
        // Next alternative is to use the searchbar as the placement marker.
        const searchWidgetID = "search-container";
        if (widgetIDs.includes(searchWidgetID)) {
          pos = CustomizableUI.getPlacementOfWidget(searchWidgetID).position + 1;
        } else {
          // Last alternative is to use the navbar as the placement marker.
          pos = CustomizableUI.getPlacementOfWidget("urlbar-container").position + 1;
        }
      }

      CustomizableUI.addWidgetToArea(this.BOOKMARK_BUTTON_ID, area, pos);
    } else {
      // Move it back to the palette.
      CustomizableUI.removeWidgetFromArea(this.BOOKMARK_BUTTON_ID);
    }
    triggerNode.setAttribute("checked", !placement);
    updateToggleControlLabel(triggerNode);
  },

  // nsINavBookmarkObserver
  onItemAdded(aItemId, aParentId, aIndex, aItemType, aURI, aTitle, aDateAdded, aGuid) {
    if (aURI && aURI.equals(this._uri)) {
      // If a new bookmark has been added to the tracked uri, register it.
      if (!this._itemGuids.has(aGuid)) {
        this._itemGuids.add(aGuid);
        // Only need to update the UI if it wasn't marked as starred before:
        if (this._itemGuids.size == 1) {
          this._updateStar();
        }
      }
    }
  },

  onItemRemoved(aItemId, aParentId, aIndex, aItemType, aURI, aGuid) {
    // If one of the tracked bookmarks has been removed, unregister it.
    if (this._itemGuids.has(aGuid)) {
      this._itemGuids.delete(aGuid);
      // Only need to update the UI if the page is no longer starred
      if (this._itemGuids.size == 0) {
        this._updateStar();
      }
    }
  },

  onItemChanged(aItemId, aProperty, aIsAnnotationProperty, aNewValue, aLastModified,
                aItemType, aParentId, aGuid) {
    if (aProperty == "uri") {
      // If the changed bookmark was tracked, check if it is now pointing to
      // a different uri and unregister it.
      if (this._itemGuids.has(aGuid) && aNewValue != this._uri.spec) {
        this._itemGuids.delete(aGuid);
        // Only need to update the UI if the page is no longer starred
        if (this._itemGuids.size == 0) {
          this._updateStar();
        }
      } else if (!this._itemGuids.has(aGuid) && aNewValue == this._uri.spec) {
        // If another bookmark is now pointing to the tracked uri, register it.
        this._itemGuids.add(aGuid);
        // Only need to update the UI if it wasn't marked as starred before:
        if (this._itemGuids.size == 1) {
          this._updateStar();
        }
      }
    }
  },

  onBeginUpdateBatch() {},
  onEndUpdateBatch() {},
  onBeforeItemRemoved() {},
  onItemVisited() {},
  onItemMoved() {},

  onWidgetUnderflow(aNode, aContainer) {
    let win = aNode.ownerGlobal;
    if (aNode.id != this.BOOKMARK_BUTTON_ID || win != window)
      return;

    // The view gets broken by being removed and reinserted. Uninit
    // here so popupshowing will generate a new one:
    this._uninitView();
  },

  QueryInterface: XPCOMUtils.generateQI([
    Ci.nsINavBookmarkObserver
  ])
};

var AutoShowBookmarksToolbar = {
  init() {
    Services.obs.addObserver(this, "autoshow-bookmarks-toolbar");
  },

  uninit() {
    Services.obs.removeObserver(this, "autoshow-bookmarks-toolbar");
  },

  observe(subject, topic, data) {
    let toolbar = document.getElementById("PersonalToolbar");
    if (!toolbar.collapsed)
      return;

    let placement = CustomizableUI.getPlacementOfWidget("personal-bookmarks");
    let area = placement && placement.area;
    if (area != CustomizableUI.AREA_BOOKMARKS)
      return;

    setToolbarVisibility(toolbar, true);
  }
};
PK
!<Hih�i�i1chrome/browser/content/browser/browser-plugins.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

var gPluginHandler = {
  PREF_SESSION_PERSIST_MINUTES: "plugin.sessionPermissionNow.intervalInMinutes",
  PREF_PERSISTENT_DAYS: "plugin.persistentPermissionAlways.intervalInDays",
  PREF_SHOW_INFOBAR: "plugins.show_infobar",
  PREF_INFOBAR_DISMISSAL_PERMANENT: "plugins.remember_infobar_dismissal",

  MESSAGES: [
    "PluginContent:ShowClickToPlayNotification",
    "PluginContent:RemoveNotification",
    "PluginContent:UpdateHiddenPluginUI",
    "PluginContent:HideNotificationBar",
    "PluginContent:InstallSinglePlugin",
    "PluginContent:ShowPluginCrashedNotification",
    "PluginContent:SubmitReport",
    "PluginContent:LinkClickCallback",
  ],

  init() {
    const mm = window.messageManager;
    for (let msg of this.MESSAGES) {
      mm.addMessageListener(msg, this);
    }
    window.addEventListener("unload", this);
  },

  uninit() {
    const mm = window.messageManager;
    for (let msg of this.MESSAGES) {
      mm.removeMessageListener(msg, this);
    }
    window.removeEventListener("unload", this);
  },

  handleEvent(event) {
    if (event.type == "unload") {
      this.uninit();
    }
  },

  receiveMessage(msg) {
    switch (msg.name) {
      case "PluginContent:ShowClickToPlayNotification":
        this.showClickToPlayNotification(msg.target, msg.data.plugins, msg.data.showNow,
                                         msg.principal, msg.data.location);
        break;
      case "PluginContent:RemoveNotification":
        this.removeNotification(msg.target, msg.data.name);
        break;
      case "PluginContent:UpdateHiddenPluginUI":
        this.updateHiddenPluginUI(msg.target, msg.data.haveInsecure, msg.data.actions,
                                  msg.principal, msg.data.location)
          .catch(Cu.reportError);
        break;
      case "PluginContent:HideNotificationBar":
        this.hideNotificationBar(msg.target, msg.data.name);
        break;
      case "PluginContent:InstallSinglePlugin":
        this.installSinglePlugin(msg.data.pluginInfo);
        break;
      case "PluginContent:ShowPluginCrashedNotification":
        this.showPluginCrashedNotification(msg.target, msg.data.messageString,
                                           msg.data.pluginID);
        break;
      case "PluginContent:SubmitReport":
        if (AppConstants.MOZ_CRASHREPORTER) {
          this.submitReport(msg.data.runID, msg.data.keyVals, msg.data.submitURLOptIn);
        }
        break;
      case "PluginContent:LinkClickCallback":
        switch (msg.data.name) {
          case "managePlugins":
          case "openHelpPage":
          case "openPluginUpdatePage":
            this[msg.data.name](msg.data.pluginTag);
            break;
        }
        break;
      default:
        Cu.reportError("gPluginHandler did not expect to handle message " + msg.name);
        break;
    }
  },

  // Callback for user clicking on a disabled plugin
  managePlugins() {
    BrowserOpenAddonsMgr("addons://list/plugin");
  },

  // Callback for user clicking on the link in a click-to-play plugin
  // (where the plugin has an update)
  openPluginUpdatePage(pluginTag) {
    let url = Services.blocklist.getPluginInfoURL(pluginTag);
    if (!url) {
      url = Services.blocklist.getPluginBlocklistURL(pluginTag);
    }
    openUILinkIn(url, "tab");
  },

  submitReport: function submitReport(runID, keyVals, submitURLOptIn) {
    if (!AppConstants.MOZ_CRASHREPORTER) {
      return;
    }
    Services.prefs.setBoolPref("dom.ipc.plugins.reportCrashURL", submitURLOptIn);
    PluginCrashReporter.submitCrashReport(runID, keyVals);
  },

  // Callback for user clicking a "reload page" link
  reloadPage(browser) {
    browser.reload();
  },

  // Callback for user clicking the help icon
  openHelpPage() {
    openHelpLink("plugin-crashed", false);
  },

  _clickToPlayNotificationEventCallback: function PH_ctpEventCallback(event) {
    if (event == "showing") {
      Services.telemetry.getHistogramById("PLUGINS_NOTIFICATION_SHOWN")
        .add(!this.options.primaryPlugin);
      // Histograms always start at 0, even though our data starts at 1
      let histogramCount = this.options.pluginData.size - 1;
      if (histogramCount > 4) {
        histogramCount = 4;
      }
      Services.telemetry.getHistogramById("PLUGINS_NOTIFICATION_PLUGIN_COUNT")
        .add(histogramCount);
    } else if (event == "dismissed") {
      // Once the popup is dismissed, clicking the icon should show the full
      // list again
      this.options.primaryPlugin = null;
    }
  },

  /**
   * Called from the plugin doorhanger to set the new permissions for a plugin
   * and activate plugins if necessary.
   * aNewState should be either "allownow" "allowalways" or "block"
   */
  _updatePluginPermission(aBrowser, aPluginInfo, aNewState) {
    let permission;
    let expireType;
    let expireTime;
    let histogram =
      Services.telemetry.getHistogramById("PLUGINS_NOTIFICATION_USER_ACTION_2");

    let notification = PopupNotifications.getNotification("click-to-play-plugins", aBrowser);

    // Update the permission manager.
    // Also update the current state of pluginInfo.fallbackType so that
    // subsequent opening of the notification shows the current state.
    switch (aNewState) {
      case "allownow":
        permission = Ci.nsIPermissionManager.ALLOW_ACTION;
        expireType = Ci.nsIPermissionManager.EXPIRE_SESSION;
        expireTime = Date.now() + Services.prefs.getIntPref(this.PREF_SESSION_PERSIST_MINUTES) * 60 * 1000;
        histogram.add(0);
        aPluginInfo.fallbackType = Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE;
        notification.options.extraAttr = "active";
        break;

      case "allowalways":
        permission = Ci.nsIPermissionManager.ALLOW_ACTION;
        expireType = Ci.nsIPermissionManager.EXPIRE_TIME;
        expireTime = Date.now() +
          Services.prefs.getIntPref(this.PREF_PERSISTENT_DAYS) * 24 * 60 * 60 * 1000;
        histogram.add(1);
        aPluginInfo.fallbackType = Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE;
        notification.options.extraAttr = "active";
        break;

      case "block":
        permission = Ci.nsIPermissionManager.PROMPT_ACTION;
        expireType = Ci.nsIPermissionManager.EXPIRE_NEVER;
        expireTime = 0;
        histogram.add(2);
        switch (aPluginInfo.blocklistState) {
          case Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE:
            aPluginInfo.fallbackType = Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE;
            break;
          case Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE:
            aPluginInfo.fallbackType = Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE;
            break;
          default:
            // PLUGIN_CLICK_TO_PLAY_QUIET will only last until they reload the page, at
            // which point it will be PLUGIN_CLICK_TO_PLAY (the overlays will appear)
            aPluginInfo.fallbackType = Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY_QUIET;
        }
        notification.options.extraAttr = "inactive";
        break;

      case "blockalways":
        permission = Ci.nsIObjectLoadingContent.PLUGIN_PERMISSION_PROMPT_ACTION_QUIET;
        expireType = Ci.nsIPermissionManager.EXPIRE_NEVER;
        expireTime = 0;
        histogram.add(3);
        aPluginInfo.fallbackType = Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY_QUIET;
        notification.options.extraAttr = "inactive";
        break;

      // In case a plugin has already been allowed/disallowed in another tab, the
      // buttons matching the existing block state shouldn't change any permissions
      // but should run the plugin-enablement code below.
      case "continue":
        aPluginInfo.fallbackType = Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE;
        notification.options.extraAttr = "active";
        break;

      case "continueblocking":
        aPluginInfo.fallbackType = Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY_QUIET;
        notification.options.extraAttr = "inactive";
        break;

      default:
        Cu.reportError(Error("Unexpected plugin state: " + aNewState));
        return;
    }

    if (aNewState != "continue" && aNewState != "continueblocking") {
      let principal = notification.options.principal;
      Services.perms.addFromPrincipal(principal, aPluginInfo.permissionString,
                                      permission, expireType, expireTime);
      aPluginInfo.pluginPermissionType = expireType;
    }

    aBrowser.messageManager.sendAsyncMessage("BrowserPlugins:ActivatePlugins", {
      pluginInfo: aPluginInfo,
      newState: aNewState,
    });
  },

  showClickToPlayNotification(browser, plugins, showNow,
                                        principal, location) {
    // It is possible that we've received a message from the frame script to show
    // a click to play notification for a principal that no longer matches the one
    // that the browser's content now has assigned (ie, the browser has browsed away
    // after the message was sent, but before the message was received). In that case,
    // we should just ignore the message.
    if (!principal.equals(browser.contentPrincipal)) {
      return;
    }

    // Data URIs, when linked to from some page, inherit the principal of that
    // page. That means that we also need to compare the actual locations to
    // ensure we aren't getting a message from a Data URI that we're no longer
    // looking at.
    let receivedURI = Services.io.newURI(location);
    if (!browser.documentURI.equalsExceptRef(receivedURI)) {
      return;
    }

    let notification = PopupNotifications.getNotification("click-to-play-plugins", browser);

    // If this is a new notification, create a pluginData map, otherwise append
    let pluginData;
    if (notification) {
      pluginData = notification.options.pluginData;
    } else {
      pluginData = new Map();
    }

    for (let pluginInfo of plugins) {
      if (pluginData.has(pluginInfo.permissionString)) {
        continue;
      }

      // If a block contains an infoURL, we should always prefer that to the default
      // URL that we construct in-product, even for other blocklist types.
      let url = Services.blocklist.getPluginInfoURL(pluginInfo.pluginTag);

      if (pluginInfo.blocklistState != Ci.nsIBlocklistService.STATE_NOT_BLOCKED) {
        if (!url) {
          url = Services.blocklist.getPluginBlocklistURL(pluginInfo.pluginTag);
        }
      } else {
        url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "clicktoplay";
      }
      pluginInfo.detailsLink = url;

      pluginData.set(pluginInfo.permissionString, pluginInfo);
    }

    let primaryPluginPermission = null;
    if (showNow) {
      primaryPluginPermission = plugins[0].permissionString;
    }

    if (notification) {
      // Don't modify the notification UI while it's on the screen, that would be
      // jumpy and might allow clickjacking.
      if (showNow) {
        notification.options.primaryPlugin = primaryPluginPermission;
        notification.reshow();
        browser.messageManager.sendAsyncMessage("BrowserPlugins:NotificationShown");
      }
      return;
    }

    if (plugins.length == 1) {
      let pluginInfo = plugins[0];
      // If a block contains an infoURL, we should always prefer that to the default
      // URL that we construct in-product, even for other blocklist types.
      let url = Services.blocklist.getPluginInfoURL(pluginInfo.pluginTag);

      if (pluginInfo.blocklistState != Ci.nsIBlocklistService.STATE_NOT_BLOCKED) {
        if (!url) {
          url = Services.blocklist.getPluginBlocklistURL(pluginInfo.pluginTag);
        }
      } else {
        url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "clicktoplay";
      }
      pluginInfo.detailsLink = url;

      let chromeWin = window.QueryInterface(Ci.nsIDOMChromeWindow);
      let isWindowPrivate = PrivateBrowsingUtils.isWindowPrivate(chromeWin);

      let active = pluginInfo.fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE;

      let options = {
        dismissed: !showNow,
        hideClose: !Services.prefs.getBoolPref("privacy.permissionPrompts.showCloseButton"),
        persistent: showNow,
        eventCallback: this._clickToPlayNotificationEventCallback,
        primaryPlugin: primaryPluginPermission,
        popupIconClass: "plugin-icon",
        extraAttr: active ? "active" : "inactive",
        pluginData,
        principal,
      };

      let description;
      if (pluginInfo.fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE) {
        description = gNavigatorBundle.getString("flashActivate.outdated.message");
      } else {
        description = gNavigatorBundle.getString("flashActivate.message");
      }

      let badge = document.getElementById("plugin-icon-badge");
      badge.setAttribute("animate", "true");
      badge.addEventListener("animationend", function animListener(event) {
        if (event.animationName == "blink-badge" &&
            badge.hasAttribute("animate")) {
          badge.removeAttribute("animate");
          badge.removeEventListener("animationend", animListener);
        }
      });

      let weakBrowser = Cu.getWeakReference(browser);
      let mainAction = {
        callback: ({checkboxChecked}) => {
          let browserRef = weakBrowser.get();
          if (browserRef) {
            if (checkboxChecked) {
              this._updatePluginPermission(browserRef, pluginInfo, "allowalways");
            } else if (pluginInfo.fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE) {
              this._updatePluginPermission(browserRef, pluginInfo, "continue");
            } else {
              this._updatePluginPermission(browserRef, pluginInfo, "allownow");
            }
          }
        },
        label: gNavigatorBundle.getString("flashActivate.allow"),
        accessKey: gNavigatorBundle.getString("flashActivate.allow.accesskey"),
        dismiss: true,
      };

      let secondaryActions = null;
      if (!isWindowPrivate) {
        options.checkbox = {
          label: gNavigatorBundle.getString("flashActivate.remember"),
        };
        secondaryActions = [{
          callback: ({checkboxChecked}) => {
            let browserRef = weakBrowser.get();
            if (browserRef) {
              if (checkboxChecked) {
                this._updatePluginPermission(browserRef, pluginInfo, "blockalways");
              } else if (pluginInfo.fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE) {
                this._updatePluginPermission(browserRef, pluginInfo, "block");
              } else {
                this._updatePluginPermission(browserRef, pluginInfo, "continueblocking");
              }
            }
          },
          label: gNavigatorBundle.getString("flashActivate.noAllow"),
          accessKey: gNavigatorBundle.getString("flashActivate.noAllow.accesskey"),
          dismiss: true,
        }];
      }

      PopupNotifications.show(browser, "click-to-play-plugins",
                                             description, "plugins-notification-icon",
                                             mainAction, secondaryActions, options);
      browser.messageManager.sendAsyncMessage("BrowserPlugins:NotificationShown");
    } else {
      this.removeNotification(browser, "click-to-play-plugins");
    }
  },

  removeNotification(browser, name) {
    let notification = PopupNotifications.getNotification(name, browser);
    if (notification)
      PopupNotifications.remove(notification);
  },

  hideNotificationBar(browser, name) {
    let notificationBox = gBrowser.getNotificationBox(browser);
    let notification = notificationBox.getNotificationWithValue(name);
    if (notification)
      notificationBox.removeNotification(notification, true);
  },

  infobarBlockedForURI(uri) {
    return new Promise((resolve, reject) => {
      let tableName = Services.prefs.getStringPref("urlclassifier.flashInfobarTable", "");
      if (!tableName) {
        resolve(false);
      }
      let classifier = Cc["@mozilla.org/url-classifier/dbservice;1"]
        .getService(Ci.nsIURIClassifier);
      classifier.asyncClassifyLocalWithTables(uri, tableName, (c, list) => {
        resolve(list.length > 0);
      });
    });
  },

  async updateHiddenPluginUI(browser, haveInsecure, actions,
                                 principal, location) {
    let origin = principal.originNoSuffix;

    let shouldShowNotification = !(await this.infobarBlockedForURI(browser.documentURI));

    // It is possible that we've received a message from the frame script to show
    // the hidden plugin notification for a principal that no longer matches the one
    // that the browser's content now has assigned (ie, the browser has browsed away
    // after the message was sent, but before the message was received). In that case,
    // we should just ignore the message.
    if (!principal.equals(browser.contentPrincipal)) {
      return;
    }

    // Data URIs, when linked to from some page, inherit the principal of that
    // page. That means that we also need to compare the actual locations to
    // ensure we aren't getting a message from a Data URI that we're no longer
    // looking at.
    let receivedURI = Services.io.newURI(location);
    if (!browser.documentURI.equalsExceptRef(receivedURI)) {
      return;
    }

    // Set up the icon
    document.getElementById("plugins-notification-icon").classList.
      toggle("plugin-blocked", haveInsecure);

    // Now configure the notification bar
    let notificationBox = gBrowser.getNotificationBox(browser);

    function hideNotification() {
      let n = notificationBox.getNotificationWithValue("plugin-hidden");
      if (n) {
        notificationBox.removeNotification(n, true);
      }
    }

    // There are three different cases when showing an infobar:
    // 1.  A single type of plugin is hidden on the page. Show the UI for that
    //     plugin.
    // 2a. Multiple types of plugins are hidden on the page. Show the multi-UI
    //     with the vulnerable styling.
    // 2b. Multiple types of plugins are hidden on the page, but none are
    //     vulnerable. Show the nonvulnerable multi-UI.
    function showNotification() {
      if (!Services.prefs.getBoolPref(gPluginHandler.PREF_SHOW_INFOBAR, true)) {
        return;
      }

      let n = notificationBox.getNotificationWithValue("plugin-hidden");
      if (n) {
        // If something is already shown, just keep it
        return;
      }

      Services.telemetry.getHistogramById("PLUGINS_INFOBAR_SHOWN").
        add(true);

      let message;
      // Icons set directly cannot be manipulated using moz-image-region, so
      // we use CSS classes instead.
      let brand = document.getElementById("bundle_brand").getString("brandShortName");

      if (actions.length == 1) {
        let pluginInfo = actions[0];
        let pluginName = pluginInfo.pluginName;

        switch (pluginInfo.fallbackType) {
          case Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY:
          case Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY_QUIET:
            message = gNavigatorBundle.getFormattedString(
              "pluginActivationWarning.message",
              [brand]);
            break;
          case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE:
            message = gNavigatorBundle.getFormattedString(
              "pluginActivateOutdated.message",
              [pluginName, origin, brand]);
            break;
          case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE:
            message = gNavigatorBundle.getFormattedString(
              "pluginActivateVulnerable.message",
              [pluginName, origin, brand]);
        }
      } else {
        // Multi-plugin
        message = gNavigatorBundle.getFormattedString(
          "pluginActivateMultiple.message", [origin]);
      }

      let buttons = [
        {
          label: gNavigatorBundle.getString("pluginContinueBlocking.label"),
          accessKey: gNavigatorBundle.getString("pluginContinueBlocking.accesskey"),
          callback() {
            Services.telemetry.getHistogramById("PLUGINS_INFOBAR_BLOCK").
              add(true);

            Services.perms.addFromPrincipal(principal,
                                            "plugin-hidden-notification",
                                            Services.perms.DENY_ACTION);
          }
        },
        {
          label: gNavigatorBundle.getString("pluginActivateTrigger.label"),
          accessKey: gNavigatorBundle.getString("pluginActivateTrigger.accesskey"),
          callback() {
            Services.telemetry.getHistogramById("PLUGINS_INFOBAR_ALLOW").
              add(true);

            let curNotification =
              PopupNotifications.getNotification("click-to-play-plugins",
                                                 browser);
            if (curNotification) {
              curNotification.reshow();
            }
          }
        }
      ];
      function notificationCallback(type) {
        if (type == "dismissed") {
          Services.telemetry.getHistogramById("PLUGINS_INFOBAR_DISMISSED").
            add(true);
          if (Services.prefs.getBoolPref(gPluginHandler.PREF_INFOBAR_DISMISSAL_PERMANENT, false)) {
            Services.perms.addFromPrincipal(principal,
                                            "plugin-hidden-notification",
                                            Services.perms.DENY_ACTION);
          }
        }
      }
      n = notificationBox.
        appendNotification(message, "plugin-hidden", null,
                           notificationBox.PRIORITY_INFO_HIGH, buttons,
                           notificationCallback);
      if (haveInsecure) {
        n.classList.add("pluginVulnerable");
      }
    }

    if (actions.length == 0) {
      shouldShowNotification = false;
    }
    if (shouldShowNotification &&
        Services.perms.testPermissionFromPrincipal(principal, "plugin-hidden-notification") ==
        Ci.nsIPermissionManager.DENY_ACTION) {
      shouldShowNotification = false;
    }
    if (shouldShowNotification) {
      showNotification();
    } else {
      hideNotification();
    }
  },

  contextMenuCommand(browser, plugin, command) {
    browser.messageManager.sendAsyncMessage("BrowserPlugins:ContextMenuCommand",
      { command }, { plugin });
  },

  // Crashed-plugin observer. Notified once per plugin crash, before events
  // are dispatched to individual plugin instances.
  NPAPIPluginCrashed(subject, topic, data) {
    let propertyBag = subject;
    if (!(propertyBag instanceof Ci.nsIPropertyBag2) ||
        !(propertyBag instanceof Ci.nsIWritablePropertyBag2) ||
        !propertyBag.hasKey("runID") ||
        !propertyBag.hasKey("pluginName")) {
      Cu.reportError("A NPAPI plugin crashed, but the properties of this plugin " +
                     "cannot be read.");
      return;
    }

    let runID = propertyBag.getPropertyAsUint32("runID");
    let uglyPluginName = propertyBag.getPropertyAsAString("pluginName");
    let pluginName = BrowserUtils.makeNicePluginName(uglyPluginName);
    let pluginDumpID = propertyBag.getPropertyAsAString("pluginDumpID");

    // If we don't have a minidumpID, we can't (or didn't) submit anything.
    // This can happen if the plugin is killed from the task manager.
    let state;
    if (!AppConstants.MOZ_CRASHREPORTER || !gCrashReporter.enabled) {
      // This state tells the user that crash reporting is disabled, so we
      // cannot send a report.
      state = "noSubmit";
    } else if (!pluginDumpID) {
      // This state tells the user that there is no crash report available.
      state = "noReport";
    } else {
      // This state asks the user to submit a crash report.
      state = "please";
    }

    let mm = window.getGroupMessageManager("browsers");
    mm.broadcastAsyncMessage("BrowserPlugins:NPAPIPluginProcessCrashed",
                             { pluginName, runID, state });
  },

  /**
   * Shows a plugin-crashed notification bar for a browser that has had an
   * invisiable NPAPI plugin crash, or a GMP plugin crash.
   *
   * @param browser
   *        The browser to show the notification for.
   * @param messageString
   *        The string to put in the notification bar
   * @param pluginID
   *        The unique-per-process identifier for the NPAPI plugin or GMP.
   *        For a GMP, this is the pluginID. For NPAPI plugins (where "pluginID"
   *        means something different), this is the runID.
   */
  showPluginCrashedNotification(browser, messageString, pluginID) {
    // If there's already an existing notification bar, don't do anything.
    let notificationBox = gBrowser.getNotificationBox(browser);
    let notification = notificationBox.getNotificationWithValue("plugin-crashed");
    if (notification) {
      return;
    }

    // Configure the notification bar
    let priority = notificationBox.PRIORITY_WARNING_MEDIUM;
    let iconURL = "chrome://mozapps/skin/plugins/pluginGeneric.svg";
    let reloadLabel = gNavigatorBundle.getString("crashedpluginsMessage.reloadButton.label");
    let reloadKey   = gNavigatorBundle.getString("crashedpluginsMessage.reloadButton.accesskey");

    let buttons = [{
      label: reloadLabel,
      accessKey: reloadKey,
      popup: null,
      callback() { browser.reload(); },
    }];

    if (AppConstants.MOZ_CRASHREPORTER &&
        PluginCrashReporter.hasCrashReport(pluginID)) {
      let submitLabel = gNavigatorBundle.getString("crashedpluginsMessage.submitButton.label");
      let submitKey   = gNavigatorBundle.getString("crashedpluginsMessage.submitButton.accesskey");
      let submitButton = {
        label: submitLabel,
        accessKey: submitKey,
        popup: null,
        callback: () => {
          PluginCrashReporter.submitCrashReport(pluginID);
        },
      };

      buttons.push(submitButton);
    }

    notification = notificationBox.appendNotification(messageString, "plugin-crashed",
                                                      iconURL, priority, buttons);

    // Add the "learn more" link.
    let XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
    let link = notification.ownerDocument.createElementNS(XULNS, "label");
    link.className = "text-link";
    link.setAttribute("value", gNavigatorBundle.getString("crashedpluginsMessage.learnMore"));
    let crashurl = formatURL("app.support.baseURL", true);
    crashurl += "plugin-crashed-notificationbar";
    link.href = crashurl;
    let description = notification.ownerDocument.getAnonymousElementByAttribute(notification, "anonid", "messageText");
    description.appendChild(link);
  },
};

gPluginHandler.init();
PK
!<��$LL6chrome/browser/content/browser/browser-safebrowsing.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 file is loaded into the browser window scope.
/* eslint-env mozilla/browser-window */

var gSafeBrowsing = {

  setReportPhishingMenu() {
    // In order to detect whether or not we're at the phishing warning
    // page, we have to check the documentURI instead of the currentURI.
    // This is because when the DocShell loads an error page, the
    // currentURI stays at the original target, while the documentURI
    // will point to the internal error page we loaded instead.
    var docURI = gBrowser.selectedBrowser.documentURI;
    var isPhishingPage =
      docURI && docURI.spec.startsWith("about:blocked?e=deceptiveBlocked");

    // Show/hide the appropriate menu item.
    document.getElementById("menu_HelpPopup_reportPhishingtoolmenu")
            .hidden = isPhishingPage;
    document.getElementById("menu_HelpPopup_reportPhishingErrortoolmenu")
            .hidden = !isPhishingPage;

    // Now look at the currentURI to learn which page we were trying
    // to browse to.
    const uri = gBrowser.currentURI;
    const isReportablePage = uri && (uri.schemeIs("http") || uri.schemeIs("https"));

    const disabledByPolicy = !Services.policies.isAllowed("feedbackCommands");

    const reportBroadcaster = document.getElementById("reportPhishingBroadcaster");
    if (disabledByPolicy || isPhishingPage || !isReportablePage) {
      reportBroadcaster.setAttribute("disabled", "true");
    } else {
      reportBroadcaster.removeAttribute("disabled");
    }

    const reportErrorBroadcaster = document.getElementById("reportPhishingErrorBroadcaster");
    if (disabledByPolicy || !isPhishingPage || !isReportablePage) {
      reportErrorBroadcaster.setAttribute("disabled", "true");
    } else {
      reportErrorBroadcaster.removeAttribute("disabled");
    }
  },

  /**
   * Used to report a phishing page or a false positive
   *
   * @param name
   *        String One of "PhishMistake", "MalwareMistake", or "Phish"
   * @param info
   *        Information about the reasons for blocking the resource.
   *        In the case false positive, it may contain SafeBrowsing
   *        matching list and provider of the list
   * @return String the report phishing URL.
   */
  getReportURL(name, info) {
    let reportInfo = info;
    if (!reportInfo) {
      let pageUri = gBrowser.currentURI.clone();

      // Remove the query to avoid including potentially sensitive data
      if (pageUri instanceof Ci.nsIURL) {
        pageUri = pageUri.mutate()
                         .setQuery("")
                         .finalize();
      }

      reportInfo = { uri: pageUri.asciiSpec };
    }
    return SafeBrowsing.getReportURL(name, reportInfo);
  }
};
PK
!<B�lvJvJ1chrome/browser/content/browser/browser-sidebar.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
 * SidebarUI controls showing and hiding the browser sidebar.
 *
 * @note
 * Some of these methods take a commandID argument - we expect to find a
 * xul:broadcaster element with the specified ID.
 * The following attributes on that element may be used and/or modified:
 *  - id           (required) the string to match commandID. The convention
 *                 is to use this naming scheme: 'view<sidebar-name>Sidebar'.
 *  - sidebarurl   (required) specifies the URL to load in this sidebar.
 *  - sidebartitle or label (in that order) specify the title to
 *                 display on the sidebar.
 *  - checked      indicates whether the sidebar is currently displayed.
 *                 Note that this attribute is updated when
 *                 the sidebar's visibility is changed.
 *  - group        this attribute must be set to "sidebar".
 */
var SidebarUI = {
  // Avoid getting the browser element from init() to avoid triggering the
  // <browser> constructor during startup if the sidebar is hidden.
  get browser() {
    if (this._browser)
      return this._browser;
    return this._browser = document.getElementById("sidebar");
  },
  POSITION_START_PREF: "sidebar.position_start",
  DEFAULT_SIDEBAR_ID: "viewBookmarksSidebar",

  // lastOpenedId is set in show() but unlike currentID it's not cleared out on hide
  // and isn't persisted across windows
  lastOpenedId: null,

  _box: null,
  // The constructor of this label accesses the browser element due to the
  // control="sidebar" attribute, so avoid getting this label during startup.
  get _title() {
    if (this.__title)
      return this.__title;
    return this.__title = document.getElementById("sidebar-title");
  },
  _splitter: null,
  _icon: null,
  _reversePositionButton: null,
  _switcherPanel: null,
  _switcherTarget: null,
  _switcherArrow: null,
  _inited: false,

  get initialized() {
    return this._inited;
  },

  init() {
    this._box = document.getElementById("sidebar-box");
    this._splitter = document.getElementById("sidebar-splitter");
    this._icon = document.getElementById("sidebar-icon");
    this._reversePositionButton = document.getElementById("sidebar-reverse-position");
    this._switcherPanel = document.getElementById("sidebarMenu-popup");
    this._switcherTarget = document.getElementById("sidebar-switcher-target");
    this._switcherArrow = document.getElementById("sidebar-switcher-arrow");

    this._switcherTarget.addEventListener("command", () => {
      this.toggleSwitcherPanel();
    });

    this._inited = true;
  },

  uninit() {
    // If this is the last browser window, persist various values that should be
    // remembered for after a restart / reopening a browser window.
    let enumerator = Services.wm.getEnumerator("navigator:browser");
    if (!enumerator.hasMoreElements()) {
      document.persist("sidebar-box", "sidebarcommand");

      let xulStore = Cc["@mozilla.org/xul/xulstore;1"].getService(Ci.nsIXULStore);
      if (this._box.hasAttribute("positionend")) {
        document.persist("sidebar-box", "positionend");
      } else {
        xulStore.removeValue(document.documentURI, "sidebar-box", "positionend");
      }
      if (this._box.hasAttribute("checked")) {
        document.persist("sidebar-box", "checked");
      } else {
        xulStore.removeValue(document.documentURI, "sidebar-box", "checked");
      }

      document.persist("sidebar-box", "width");
      document.persist("sidebar-title", "value");
    }
  },

  /**
   * Opens the switcher panel if it's closed, or closes it if it's open.
   */
  toggleSwitcherPanel() {
    if (this._switcherPanel.state == "open" || this._switcherPanel.state == "showing") {
      this.hideSwitcherPanel();
    } else if (this._switcherPanel.state == "closed") {
      this.showSwitcherPanel();
    }
  },

  hideSwitcherPanel() {
    this._switcherPanel.hidePopup();
  },

  showSwitcherPanel() {
    this._ensureShortcutsShown();
    this._switcherPanel.addEventListener("popuphiding", () => {
      this._switcherTarget.classList.remove("active");
    }, {once: true});

    // Combine start/end position with ltr/rtl to set the label in the popup appropriately.
    let label = this._positionStart == this.isRTL ?
                  gNavigatorBundle.getString("sidebar.moveToLeft") :
                  gNavigatorBundle.getString("sidebar.moveToRight");
    this._reversePositionButton.setAttribute("label", label);

    this._switcherPanel.hidden = false;
    this._switcherPanel.openPopup(this._icon);
    this._switcherTarget.classList.add("active");
  },

  updateShortcut({button, key}) {
    // If the shortcuts haven't been rendered yet then it will be set correctly
    // on the first render so there's nothing to do now.
    if (!this._addedShortcuts) {
      return;
    }
    if (key) {
      let keyId = key.getAttribute("id");
      button = this._switcherPanel.querySelector(`[key="${keyId}"]`);
    } else if (button) {
      let keyId = button.getAttribute("key");
      key = document.getElementById(keyId);
    }
    if (!button || !key) {
      return;
    }
    button.setAttribute("shortcut", ShortcutUtils.prettifyShortcut(key));
  },

  _addedShortcuts: false,
  _ensureShortcutsShown() {
    if (this._addedShortcuts) {
      return;
    }
    this._addedShortcuts = true;
    for (let button of this._switcherPanel.querySelectorAll("toolbarbutton[key]")) {
      this.updateShortcut({button});
    }
  },

  /**
   * Change the pref that will trigger a call to setPosition
   */
  reversePosition() {
    Services.prefs.setBoolPref(this.POSITION_START_PREF, !this._positionStart);
  },

  /**
   * Read the positioning pref and position the sidebar and the splitter
   * appropriately within the browser container.
   */
  setPosition() {
    // First reset all ordinals to match DOM ordering.
    let browser = document.getElementById("browser");
    [...browser.childNodes].forEach((node, i) => {
      node.ordinal = i + 1;
    });

    if (!this._positionStart) {
      // DOM ordering is:     |  sidebar-box  | splitter |   appcontent  |
      // Want to display as:  |   appcontent  | splitter |  sidebar-box  |
      // So we just swap box and appcontent ordering
      let appcontent = document.getElementById("appcontent");
      let boxOrdinal = this._box.ordinal;
      this._box.ordinal = appcontent.ordinal;
      appcontent.ordinal = boxOrdinal;
      // Indicate we've switched ordering to the box
      this._box.setAttribute("positionend", true);
    } else {
      this._box.removeAttribute("positionend");
    }

    this.hideSwitcherPanel();
  },

  /**
   * Try and adopt the status of the sidebar from another window.
   * @param {Window} sourceWindow - Window to use as a source for sidebar status.
   * @return true if we adopted the state, or false if the caller should
   * initialize the state itself.
   */
  adoptFromWindow(sourceWindow) {
    // If the opener had a sidebar, open the same sidebar in our window.
    // The opener can be the hidden window too, if we're coming from the state
    // where no windows are open, and the hidden window has no sidebar box.
    let sourceUI = sourceWindow.SidebarUI;
    if (!sourceUI || !sourceUI._box) {
      // no source UI or no _box means we also can't adopt the state.
      return false;
    }

    // Set sidebar command even if hidden, so that we keep the same sidebar
    // even if it's currently closed.
    let commandID = sourceUI._box.getAttribute("sidebarcommand");
    if (commandID) {
      this._box.setAttribute("sidebarcommand", commandID);
    }

    if (sourceUI._box.hidden) {
      // just hidden means we have adopted the hidden state.
      return true;
    }

    // dynamically generated sidebars will fail this check, but we still
    // consider it adopted.
    if (!document.getElementById(commandID)) {
      return true;
    }

    this._box.setAttribute("width", sourceUI._box.boxObject.width);
    this.showInitially(commandID);

    return true;
  },

  windowPrivacyMatches(w1, w2) {
    return PrivateBrowsingUtils.isWindowPrivate(w1) === PrivateBrowsingUtils.isWindowPrivate(w2);
  },

  /**
   * If loading a sidebar was delayed on startup, start the load now.
   */
  startDelayedLoad() {
    let sourceWindow = window.opener;
    // No source window means this is the initial window.  If we're being
    // opened from another window, check that it is one we might open a sidebar
    // for.
    if (sourceWindow) {
      if (sourceWindow.closed || sourceWindow.location.protocol != "chrome:" ||
        !this.windowPrivacyMatches(sourceWindow, window)) {
        return;
      }
      // Try to adopt the sidebar state from the source window
      if (this.adoptFromWindow(sourceWindow)) {
        return;
      }
    }

    // If we're not adopting settings from a parent window, set them now.
    let wasOpen = this._box.getAttribute("checked");
    if (!wasOpen) {
      return;
    }

    let commandID = this._box.getAttribute("sidebarcommand");
    if (commandID && document.getElementById(commandID)) {
      this.showInitially(commandID);
    } else {
      this._box.removeAttribute("checked");
      // Remove the |sidebarcommand| attribute, because the element it
      // refers to no longer exists, so we should assume this sidebar
      // panel has been uninstalled. (249883)
      // We use setAttribute rather than removeAttribute so it persists
      // correctly.
      this._box.setAttribute("sidebarcommand", "");
      // On a startup in which the startup cache was invalidated (e.g. app update)
      // extensions will not be started prior to delayedLoad, thus the
      // sidebarcommand element will not exist yet.  Store the commandID so
      // extensions may reopen if necessary.  A startup cache invalidation
      // can be forced (for testing) by deleting compatibility.ini from the
      // profile.
      this.lastOpenedId = commandID;
    }
  },

  /**
   * Fire a "SidebarShown" event on the sidebar to give any interested parties
   * a chance to update the button or whatever.
   */
  _fireShowEvent() {
    let event = new CustomEvent("SidebarShown", {bubbles: true});
    this._switcherTarget.dispatchEvent(event);
  },

  /**
   * Fire a "SidebarFocused" event on the sidebar's |window| to give the sidebar
   * a chance to adjust focus as needed. An additional event is needed, because
   * we don't want to focus the sidebar when it's opened on startup or in a new
   * window, only when the user opens the sidebar.
   */
  _fireFocusedEvent() {
    let event = new CustomEvent("SidebarFocused", {bubbles: true});
    this.browser.contentWindow.dispatchEvent(event);
  },

  /**
   * True if the sidebar is currently open.
   */
  get isOpen() {
    return !this._box.hidden;
  },

  /**
   * The ID of the current sidebar (ie, the ID of the broadcaster being used).
   */
  get currentID() {
    return this.isOpen ? this._box.getAttribute("sidebarcommand") : "";
  },

  get title() {
    return this._title.value;
  },

  set title(value) {
    this._title.value = value;
  },

  getBroadcasterById(id) {
    let sidebarBroadcaster = document.getElementById(id);
    if (sidebarBroadcaster && sidebarBroadcaster.localName == "broadcaster") {
      return sidebarBroadcaster;
    }
    return null;
  },

  /**
   * Toggle the visibility of the sidebar. If the sidebar is hidden or is open
   * with a different commandID, then the sidebar will be opened using the
   * specified commandID. Otherwise the sidebar will be hidden.
   *
   * @param  {string}  commandID     ID of the xul:broadcaster element to use.
   * @param  {DOMNode} [triggerNode] Node, usually a button, that triggered the
   *                                 visibility toggling of the sidebar.
   * @return {Promise}
   */
  toggle(commandID = this.lastOpenedId, triggerNode) {
    // First priority for a default value is this.lastOpenedId which is set during show()
    // and not reset in hide(), unlike currentID. If show() hasn't been called and we don't
    // have a persisted command either, or the command doesn't exist anymore, then
    // fallback to a default sidebar.
    if (!commandID) {
      commandID = this._box.getAttribute("sidebarcommand");
    }
    if (!commandID || !this.getBroadcasterById(commandID)) {
      commandID = this.DEFAULT_SIDEBAR_ID;
    }

    if (this.isOpen && commandID == this.currentID) {
      this.hide(triggerNode);
      return Promise.resolve();
    }
    return this.show(commandID, triggerNode);
  },

  _loadSidebarExtension(sidebarBroadcaster) {
    let extensionId = sidebarBroadcaster.getAttribute("extensionId");
    if (extensionId) {
      let extensionUrl = sidebarBroadcaster.getAttribute("panel");
      let browserStyle = sidebarBroadcaster.getAttribute("browserStyle");
      SidebarUI.browser.contentWindow.loadPanel(extensionId, extensionUrl, browserStyle);
    }
  },

  /**
   * Show the sidebar, using the parameters from the specified broadcaster.
   * @see SidebarUI note.
   *
   * This wraps the internal method, including a ping to telemetry.
   *
   * @param {string}  commandID     ID of the xul:broadcaster element to use.
   * @param {DOMNode} [triggerNode] Node, usually a button, that triggered the
   *                                showing of the sidebar.
   */
  show(commandID, triggerNode) {
    return this._show(commandID).then((sidebarBroadcaster) => {
      this._loadSidebarExtension(sidebarBroadcaster);

      if (triggerNode) {
        updateToggleControlLabel(triggerNode);
      }

      this._fireFocusedEvent();
      BrowserUITelemetry.countSidebarEvent(commandID, "show");
    });
  },

  /**
   * Show the sidebar, without firing the focused event or logging telemetry.
   * This is intended to be used when the sidebar is opened automatically
   * when a window opens (not triggered by user interaction).
   *
   * @param {string} commandID ID of the xul:broadcaster element to use.
   */
  showInitially(commandID) {
    return this._show(commandID).then((sidebarBroadcaster) => {
      this._loadSidebarExtension(sidebarBroadcaster);
    });
  },

  /**
   * Implementation for show. Also used internally for sidebars that are shown
   * when a window is opened and we don't want to ping telemetry.
   *
   * @param {string} commandID ID of the xul:broadcaster element to use.
   */
  _show(commandID) {
    return new Promise((resolve, reject) => {
      let sidebarBroadcaster = this.getBroadcasterById(commandID);
      if (!sidebarBroadcaster) {
        reject(new Error("Invalid sidebar broadcaster specified: " + commandID));
        return;
      }

      if (this.isOpen && commandID != this.currentID) {
        BrowserUITelemetry.countSidebarEvent(this.currentID, "hide");
      }

      let broadcasters = document.querySelectorAll("broadcaster[group=sidebar]");
      for (let broadcaster of broadcasters) {
        if (broadcaster != sidebarBroadcaster) {
          broadcaster.removeAttribute("checked");
        } else {
          sidebarBroadcaster.setAttribute("checked", "true");
        }
      }

      this._box.hidden = this._splitter.hidden = false;
      this.setPosition();

      this.hideSwitcherPanel();

      this._box.setAttribute("checked", "true");
      this._box.setAttribute("sidebarcommand", sidebarBroadcaster.id);
      this.lastOpenedId = sidebarBroadcaster.id;

      let title = sidebarBroadcaster.getAttribute("sidebartitle") ||
                  sidebarBroadcaster.getAttribute("label");

      // When loading a web page in the sidebar there is no title set on the
      // broadcaster, as it is instead set by openWebPanel. Don't clear out
      // the title in this case.
      if (title) {
        this.title = title;
      }

      let url = sidebarBroadcaster.getAttribute("sidebarurl");
      this.browser.setAttribute("src", url); // kick off async load

      if (this.browser.contentDocument.location.href != url) {
        this.browser.addEventListener("load", event => {
          // We're handling the 'load' event before it bubbles up to the usual
          // (non-capturing) event handlers. Let it bubble up before resolving.
          setTimeout(() => {
            resolve(sidebarBroadcaster);

            // Now that the currentId is updated, fire a show event.
            this._fireShowEvent();
          }, 0);
        }, {capture: true, once: true});
      } else {
        resolve(sidebarBroadcaster);

        // Now that the currentId is updated, fire a show event.
        this._fireShowEvent();
      }

      let selBrowser = gBrowser.selectedBrowser;
      selBrowser.messageManager.sendAsyncMessage("Sidebar:VisibilityChange",
        {commandID, isOpen: true}
      );
    });
  },

  /**
  * Sets the webpage favicon in sidebar
  *
  */
  setWebPageIcon(url) {
    let iconURL = "url(page-icon:" + url + ")";
    if (this._box.getAttribute("sidebarcommand") == "viewWebPanelsSidebar") {
      this._icon.style.setProperty("--sidebar-webpage-icon", iconURL);
    }
  },

  /**
   * Hide the sidebar.
   *
   * @param {DOMNode} [triggerNode] Node, usually a button, that triggered the
   *                                hiding of the sidebar.
   */
  hide(triggerNode) {
    if (!this.isOpen) {
      return;
    }

    this.hideSwitcherPanel();

    let commandID = this._box.getAttribute("sidebarcommand");
    let sidebarBroadcaster = document.getElementById(commandID);

    if (sidebarBroadcaster.getAttribute("checked") != "true") {
      return;
    }

    // Replace the document currently displayed in the sidebar with about:blank
    // so that we can free memory by unloading the page. We need to explicitly
    // create a new content viewer because the old one doesn't get destroyed
    // until about:blank has loaded (which does not happen as long as the
    // element is hidden).
    this.browser.setAttribute("src", "about:blank");
    this.browser.docShell.createAboutBlankContentViewer(null);

    sidebarBroadcaster.removeAttribute("checked");
    this._box.removeAttribute("checked");
    this._box.hidden = this._splitter.hidden = true;

    let selBrowser = gBrowser.selectedBrowser;
    selBrowser.focus();
    selBrowser.messageManager.sendAsyncMessage("Sidebar:VisibilityChange",
      {commandID, isOpen: false}
    );
    if (triggerNode) {
      updateToggleControlLabel(triggerNode);
    }
    BrowserUITelemetry.countSidebarEvent(commandID, "hide");
  },
};

// Add getters related to the position here, since we will want them
// available for both startDelayedLoad and init.
XPCOMUtils.defineLazyPreferenceGetter(SidebarUI, "_positionStart",
  SidebarUI.POSITION_START_PREF, true, SidebarUI.setPosition.bind(SidebarUI));
XPCOMUtils.defineLazyGetter(SidebarUI, "isRTL", () => {
  return getComputedStyle(document.documentElement).direction == "rtl";
});
PK
!<�N��b�b.chrome/browser/content/browser/browser-sync.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 file is loaded into the browser window scope.
/* eslint-env mozilla/browser-window */

ChromeUtils.import("resource://services-sync/UIState.jsm");

ChromeUtils.defineModuleGetter(this, "FxAccounts",
  "resource://gre/modules/FxAccounts.jsm");
ChromeUtils.defineModuleGetter(this, "EnsureFxAccountsWebChannel",
  "resource://gre/modules/FxAccountsWebChannel.jsm");
ChromeUtils.defineModuleGetter(this, "Weave",
  "resource://services-sync/main.js");

const MIN_STATUS_ANIMATION_DURATION = 1600;

var gSync = {
  _initialized: false,
  // The last sync start time. Used to calculate the leftover animation time
  // once syncing completes (bug 1239042).
  _syncStartTime: 0,
  _syncAnimationTimer: 0,

  _obs: [
    "weave:engine:sync:finish",
    "quit-application",
    UIState.ON_UPDATE
  ],

  get fxaStrings() {
    delete this.fxaStrings;
    return this.fxaStrings = Services.strings.createBundle(
      "chrome://browser/locale/accounts.properties"
    );
  },

  get syncStrings() {
    delete this.syncStrings;
    // XXXzpao these strings should probably be moved from /services to /browser... (bug 583381)
    //        but for now just make it work
    return this.syncStrings = Services.strings.createBundle(
      "chrome://weave/locale/sync.properties"
    );
  },

  get syncReady() {
    return Cc["@mozilla.org/weave/service;1"].getService().wrappedJSObject.ready;
  },

  // Returns true if sync is configured but hasn't loaded or is yet to determine
  // if any remote clients exist.
  get syncConfiguredAndLoading() {
    return UIState.get().status == UIState.STATUS_SIGNED_IN &&
           (!this.syncReady ||
           // lastSync will be non-zero after the first sync
           Weave.Service.clientsEngine.lastSync == 0);
  },

  get isSignedIn() {
    return UIState.get().status == UIState.STATUS_SIGNED_IN;
  },

  get remoteClients() {
    return Weave.Service.clientsEngine.remoteClients
           .sort((a, b) => a.name.localeCompare(b.name));
  },

  get offline() {
    return Weave.Service.scheduler.offline;
  },

  _generateNodeGetters() {
    for (let k of ["Status", "Avatar", "Label", "Container"]) {
      let prop = "appMenu" + k;
      let suffix = k.toLowerCase();
      delete this[prop];
      this.__defineGetter__(prop, function() {
        delete this[prop];
        return this[prop] = document.getElementById("appMenu-fxa-" + suffix);
      });
    }
  },

  _definePrefGetters() {
    XPCOMUtils.defineLazyPreferenceGetter(this, "UNSENDABLE_URL_REGEXP",
        "services.sync.engine.tabs.filteredUrls", null, null, rx => {
          try {
            return new RegExp(rx, "i");
          } catch (e) {
            Cu.reportError(`Failed to build url filter regexp for send tab: ${e}`);
            return null;
          }
        });
    XPCOMUtils.defineLazyPreferenceGetter(this, "PRODUCT_INFO_BASE_URL",
        "app.productInfo.baseURL");
    XPCOMUtils.defineLazyPreferenceGetter(this, "SYNC_ENABLED",
        "identity.fxaccounts.enabled");
  },

  _maybeUpdateUIState() {
    // Update the UI.
    if (UIState.isReady()) {
      const state = UIState.get();
      // If we are not configured, the UI is already in the right state when
      // we open the window. We can avoid a repaint.
      if (state.status != UIState.STATUS_NOT_CONFIGURED) {
        this.updateAllUI(state);
      }
    }
  },

  init() {
    if (this._initialized) {
      return;
    }

    this._definePrefGetters();

    if (!this.SYNC_ENABLED) {
      this.onSyncDisabled();
      return;
    }

    // initial label for the sync buttons.
    let statusBroadcaster = document.getElementById("sync-status");
    if (!statusBroadcaster) {
      // We are in a window without our elements - just abort now, without
      // setting this._initialized, so we don't attempt to remove observers.
      return;
    }
    statusBroadcaster.setAttribute("label", this.syncStrings.GetStringFromName("syncnow.label"));
    // We start with every broadcasters hidden, so that we don't need to init
    // the sync UI on windows like pageInfo.xul (see bug 1384856).
    let setupBroadcaster = document.getElementById("sync-setup-state");
    setupBroadcaster.hidden = false;

    for (let topic of this._obs) {
      Services.obs.addObserver(this, topic, true);
    }

    this._generateNodeGetters();

    this._maybeUpdateUIState();

    EnsureFxAccountsWebChannel();

    this._initialized = true;
  },

  uninit() {
    if (!this._initialized) {
      return;
    }

    for (let topic of this._obs) {
      Services.obs.removeObserver(this, topic);
    }

    this._initialized = false;
  },

  observe(subject, topic, data) {
    if (!this._initialized) {
      Cu.reportError("browser-sync observer called after unload: " + topic);
      return;
    }
    switch (topic) {
      case UIState.ON_UPDATE:
        const state = UIState.get();
        this.updateAllUI(state);
        break;
      case "quit-application":
        // Stop the animation timer on shutdown, since we can't update the UI
        // after this.
        clearTimeout(this._syncAnimationTimer);
        break;
      case "weave:engine:sync:finish":
        if (data != "clients") {
          return;
        }
        this.onClientsSynced();
        break;
    }
  },

  updateAllUI(state) {
    this.updatePanelPopup(state);
    this.updateStateBroadcasters(state);
    this.updateSyncButtonsTooltip(state);
    this.updateSyncStatus(state);
  },

  updatePanelPopup(state) {
    let defaultLabel = this.appMenuStatus.getAttribute("defaultlabel");
    // The localization string is for the signed in text, but it's the default text as well
    let defaultTooltiptext = this.appMenuStatus.getAttribute("signedinTooltiptext");

    const status = state.status;
    // Reset the status bar to its original state.
    this.appMenuLabel.setAttribute("label", defaultLabel);
    this.appMenuStatus.setAttribute("tooltiptext", defaultTooltiptext);
    this.appMenuContainer.removeAttribute("fxastatus");
    this.appMenuAvatar.style.removeProperty("list-style-image");

    if (status == UIState.STATUS_NOT_CONFIGURED) {
      return;
    }

    // At this point we consider sync to be configured (but still can be in an error state).
    if (status == UIState.STATUS_LOGIN_FAILED) {
      let tooltipDescription = this.fxaStrings.formatStringFromName("reconnectDescription", [state.email], 1);
      let errorLabel = this.appMenuStatus.getAttribute("errorlabel");
      this.appMenuContainer.setAttribute("fxastatus", "login-failed");
      this.appMenuLabel.setAttribute("label", errorLabel);
      this.appMenuStatus.setAttribute("tooltiptext", tooltipDescription);
      return;
    } else if (status == UIState.STATUS_NOT_VERIFIED) {
      let tooltipDescription = this.fxaStrings.formatStringFromName("verifyDescription", [state.email], 1);
      let unverifiedLabel = this.appMenuStatus.getAttribute("unverifiedlabel");
      this.appMenuContainer.setAttribute("fxastatus", "unverified");
      this.appMenuLabel.setAttribute("label", unverifiedLabel);
      this.appMenuStatus.setAttribute("tooltiptext", tooltipDescription);
      return;
    }

    // At this point we consider sync to be logged-in.
    this.appMenuContainer.setAttribute("fxastatus", "signedin");
    this.appMenuLabel.setAttribute("label", state.displayName || state.email);

    if (state.avatarURL) {
      let bgImage = "url(\"" + state.avatarURL + "\")";
      this.appMenuAvatar.style.listStyleImage = bgImage;

      let img = new Image();
      img.onerror = () => {
        // Clear the image if it has trouble loading. Since this callback is asynchronous
        // we check to make sure the image is still the same before we clear it.
        if (this.appMenuAvatar.style.listStyleImage === bgImage) {
          this.appMenuAvatar.style.removeProperty("list-style-image");
        }
      };
      img.src = state.avatarURL;
    }
  },

  updateStateBroadcasters(state) {
    const status = state.status;

    // Start off with a clean slate
    document.getElementById("sync-reauth-state").hidden = true;
    document.getElementById("sync-setup-state").hidden = true;
    document.getElementById("sync-syncnow-state").hidden = true;
    document.getElementById("sync-unverified-state").hidden = true;

    if (status == UIState.STATUS_LOGIN_FAILED) {
      // unhiding this element makes the menubar show the login failure state.
      document.getElementById("sync-reauth-state").hidden = false;
    } else if (status == UIState.STATUS_NOT_CONFIGURED) {
      document.getElementById("sync-setup-state").hidden = false;
    } else if (status == UIState.STATUS_NOT_VERIFIED) {
      document.getElementById("sync-unverified-state").hidden = false;
    } else {
      document.getElementById("sync-syncnow-state").hidden = false;
    }
  },

  updateSyncStatus(state) {
    const broadcaster = document.getElementById("sync-status");
    const syncingUI = broadcaster.getAttribute("syncstatus") == "active";
    if (state.syncing != syncingUI) { // Do we need to update the UI?
      state.syncing ? this.onActivityStart() : this.onActivityStop();
    }
  },

  onMenuPanelCommand() {
    switch (this.appMenuContainer.getAttribute("fxastatus")) {
    case "signedin":
      this.openPrefs("menupanel", "fxaSignedin");
      break;
    case "error":
      if (this.appMenuContainer.getAttribute("fxastatus") == "unverified") {
        this.openPrefs("menupanel", "fxaError");
      } else {
        this.openSignInAgainPage("menupanel");
      }
      break;
    default:
      this.openPrefs("menupanel", "fxa");
      break;
    }

    PanelUI.hide();
  },

  async openSignInAgainPage(entryPoint) {
    const url = await FxAccounts.config.promiseForceSigninURI(entryPoint);
    switchToTabHavingURI(url, true, {
      replaceQueryString: true,
      triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
    });
  },

  async openDevicesManagementPage(entryPoint) {
    let url = await FxAccounts.config.promiseManageDevicesURI(entryPoint);
    switchToTabHavingURI(url, true, {
      replaceQueryString: true,
      triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
    });
  },

  async openConnectAnotherDevice(entryPoint) {
    const url = await FxAccounts.config.promiseConnectDeviceURI(entryPoint);
    openUILinkIn(url, "tab");
  },

  openSendToDevicePromo() {
    let url = this.PRODUCT_INFO_BASE_URL;
    url += "send-tabs/?utm_source=" + Services.appinfo.name.toLowerCase();
    switchToTabHavingURI(url, true, { replaceQueryString: true });
  },

  sendTabToDevice(url, clientId, title) {
    Weave.Service.clientsEngine.sendURIToClientForDisplay(url, clientId, title).catch(e => {
      console.error("Could not send tab to device", e);
    });
  },

  populateSendTabToDevicesMenu(devicesPopup, url, title, createDeviceNodeFn) {
    if (!createDeviceNodeFn) {
      createDeviceNodeFn = (clientId, name, clientType, lastModified) => {
        let eltName = name ? "menuitem" : "menuseparator";
        return document.createElement(eltName);
      };
    }

    // remove existing menu items
    for (let i = devicesPopup.childNodes.length - 1; i >= 0; --i) {
      let child = devicesPopup.childNodes[i];
      if (child.classList.contains("sync-menuitem")) {
        child.remove();
      }
    }

    if (gSync.syncConfiguredAndLoading) {
      // We can only be in this case in the page action menu.
      return;
    }

    const fragment = document.createDocumentFragment();

    const state = UIState.get();
    if (state.status == UIState.STATUS_SIGNED_IN && this.remoteClients.length > 0) {
      this._appendSendTabDeviceList(fragment, createDeviceNodeFn, url, title);
    } else if (state.status == UIState.STATUS_SIGNED_IN) {
      this._appendSendTabSingleDevice(fragment, createDeviceNodeFn);
    } else if (state.status == UIState.STATUS_NOT_VERIFIED ||
               state.status == UIState.STATUS_LOGIN_FAILED) {
      this._appendSendTabVerify(fragment, createDeviceNodeFn);
    } else /* status is STATUS_NOT_CONFIGURED */ {
      this._appendSendTabUnconfigured(fragment, createDeviceNodeFn);
    }

    devicesPopup.appendChild(fragment);
  },

  _appendSendTabDeviceList(fragment, createDeviceNodeFn, url, title) {
    const onTargetDeviceCommand = (event) => {
      let clients = event.target.getAttribute("clientId") ?
        [event.target.getAttribute("clientId")] :
        this.remoteClients.map(client => client.id);

      clients.forEach(clientId => this.sendTabToDevice(url, clientId, title));
    };

    function addTargetDevice(clientId, name, clientType, lastModified) {
      const targetDevice = createDeviceNodeFn(clientId, name, clientType, lastModified);
      targetDevice.addEventListener("command", onTargetDeviceCommand, true);
      targetDevice.classList.add("sync-menuitem", "sendtab-target");
      targetDevice.setAttribute("clientId", clientId);
      targetDevice.setAttribute("clientType", clientType);
      targetDevice.setAttribute("label", name);
      fragment.appendChild(targetDevice);
    }

    const clients = this.remoteClients;
    for (let client of clients) {
      const type = client.formfactor && client.formfactor.includes("tablet") ?
                   "tablet" : client.type;
      addTargetDevice(client.id, client.name, type, client.serverLastModified * 1000);
    }

    // "Send to All Devices" menu item
    if (clients.length > 1) {
      const separator = createDeviceNodeFn();
      separator.classList.add("sync-menuitem");
      fragment.appendChild(separator);
      const allDevicesLabel = this.fxaStrings.GetStringFromName("sendToAllDevices.menuitem");
      addTargetDevice("", allDevicesLabel, "");
    }
  },

  _appendSendTabSingleDevice(fragment, createDeviceNodeFn) {
    const noDevices = this.fxaStrings.GetStringFromName("sendTabToDevice.singledevice.status");
    const learnMore = this.fxaStrings.GetStringFromName("sendTabToDevice.singledevice");
    const connectDevice = this.fxaStrings.GetStringFromName("sendTabToDevice.connectdevice");
    const actions = [{label: connectDevice, command: () => this.openConnectAnotherDevice("sendtab")},
                     {label: learnMore,     command: () => this.openSendToDevicePromo()}];
    this._appendSendTabInfoItems(fragment, createDeviceNodeFn, noDevices, actions);
  },

  _appendSendTabVerify(fragment, createDeviceNodeFn) {
    const notVerified = this.fxaStrings.GetStringFromName("sendTabToDevice.verify.status");
    const verifyAccount = this.fxaStrings.GetStringFromName("sendTabToDevice.verify");
    const actions = [{label: verifyAccount, command: () => this.openPrefs("sendtab")}];
    this._appendSendTabInfoItems(fragment, createDeviceNodeFn, notVerified, actions);
  },

  _appendSendTabUnconfigured(fragment, createDeviceNodeFn) {
    const notConnected = this.fxaStrings.GetStringFromName("sendTabToDevice.unconfigured.status");
    const learnMore = this.fxaStrings.GetStringFromName("sendTabToDevice.unconfigured");
    const actions = [{label: learnMore, command: () => this.openSendToDevicePromo()}];
    this._appendSendTabInfoItems(fragment, createDeviceNodeFn, notConnected, actions);

    // Now add a 'sign in to sync' item above the 'learn more' item.
    const signInToSync = this.fxaStrings.GetStringFromName("sendTabToDevice.signintosync");
    let signInItem = createDeviceNodeFn(null, signInToSync, null);
    signInItem.classList.add("sync-menuitem");
    signInItem.setAttribute("label", signInToSync);
    // Show an icon if opened in the page action panel:
    if (signInItem.classList.contains("subviewbutton")) {
      signInItem.classList.add("subviewbutton-iconic", "signintosync");
    }
    signInItem.addEventListener("command", () => {
      this.openPrefs("sendtab");
    });
    fragment.insertBefore(signInItem, fragment.lastChild);
  },

  _appendSendTabInfoItems(fragment, createDeviceNodeFn, statusLabel, actions) {
    const status = createDeviceNodeFn(null, statusLabel, null);
    status.setAttribute("label", statusLabel);
    status.setAttribute("disabled", true);
    status.classList.add("sync-menuitem");
    fragment.appendChild(status);

    const separator = createDeviceNodeFn(null, null, null);
    separator.classList.add("sync-menuitem");
    fragment.appendChild(separator);

    for (let {label, command} of actions) {
      const actionItem = createDeviceNodeFn(null, label, null);
      actionItem.addEventListener("command", command, true);
      actionItem.classList.add("sync-menuitem");
      actionItem.setAttribute("label", label);
      fragment.appendChild(actionItem);
    }
  },

  isSendableURI(aURISpec) {
    if (!aURISpec) {
      return false;
    }
    // Disallow sending tabs with more than 65535 characters.
    if (aURISpec.length > 65535) {
      return false;
    }
    if (this.UNSENDABLE_URL_REGEXP) {
      return !this.UNSENDABLE_URL_REGEXP.test(aURISpec);
    }
    // The preference has been removed, or is an invalid regexp, so we treat it
    // as a valid URI. We've already logged an error when trying to construct
    // the regexp, and the more problematic case is the length, which we've
    // already addressed.
    return true;
  },

  // "Send Tab to Device" menu item
  updateTabContextMenu(aPopupMenu, aTargetTab) {
    if (!this.SYNC_ENABLED) {
      // These items are hidden in onSyncDisabled(). No need to do anything.
      return;
    }
    const enabled = !this.syncConfiguredAndLoading &&
                    this.isSendableURI(aTargetTab.linkedBrowser.currentURI.spec);

    document.getElementById("context_sendTabToDevice").disabled = !enabled;
  },

  // "Send Page to Device" and "Send Link to Device" menu items
  updateContentContextMenu(contextMenu) {
    if (!this.SYNC_ENABLED) {
      // These items are hidden by default. No need to do anything.
      return;
    }
    // showSendLink and showSendPage are mutually exclusive
    const showSendLink = contextMenu.onSaveableLink || contextMenu.onPlainTextLink;
    const showSendPage = !showSendLink
                         && !(contextMenu.isContentSelected ||
                              contextMenu.onImage || contextMenu.onCanvas ||
                              contextMenu.onVideo || contextMenu.onAudio ||
                              contextMenu.onLink || contextMenu.onTextInput);

    // Avoids double separator on images with links.
    const hideSeparator = contextMenu.isContentSelected &&
                          contextMenu.onLink && contextMenu.onImage;
    ["context-sendpagetodevice", ...(hideSeparator ? [] : ["context-sep-sendpagetodevice"])]
    .forEach(id => contextMenu.showItem(id, showSendPage));
    ["context-sendlinktodevice", ...(hideSeparator ? [] : ["context-sep-sendlinktodevice"])]
    .forEach(id => contextMenu.showItem(id, showSendLink));

    if (!showSendLink && !showSendPage) {
      return;
    }

    const targetURI = showSendLink ? contextMenu.linkURL :
                                     contextMenu.browser.currentURI.spec;
    const enabled = !this.syncConfiguredAndLoading && this.isSendableURI(targetURI);
    contextMenu.setItemAttr(showSendPage ? "context-sendpagetodevice" :
                                           "context-sendlinktodevice",
                                           "disabled", !enabled || null);
  },

  // Functions called by observers
  onActivityStart() {
    clearTimeout(this._syncAnimationTimer);
    this._syncStartTime = Date.now();

    let broadcaster = document.getElementById("sync-status");
    broadcaster.setAttribute("syncstatus", "active");
    broadcaster.setAttribute("label", this.syncStrings.GetStringFromName("syncingtabs.label"));
    broadcaster.setAttribute("disabled", "true");
  },

  _onActivityStop() {
    if (!gBrowser)
      return;
    let broadcaster = document.getElementById("sync-status");
    broadcaster.removeAttribute("syncstatus");
    broadcaster.removeAttribute("disabled");
    broadcaster.setAttribute("label", this.syncStrings.GetStringFromName("syncnow.label"));
    Services.obs.notifyObservers(null, "test:browser-sync:activity-stop");
  },

  onActivityStop() {
    let now = Date.now();
    let syncDuration = now - this._syncStartTime;

    if (syncDuration < MIN_STATUS_ANIMATION_DURATION) {
      let animationTime = MIN_STATUS_ANIMATION_DURATION - syncDuration;
      clearTimeout(this._syncAnimationTimer);
      this._syncAnimationTimer = setTimeout(() => this._onActivityStop(), animationTime);
    } else {
      this._onActivityStop();
    }
  },

  // doSync forces a sync - it *does not* return a promise as it is called
  // via the various UI components.
  doSync() {
    if (!UIState.isReady()) {
      return;
    }
    const state = UIState.get();
    if (state.status == UIState.STATUS_SIGNED_IN) {
      this.updateSyncStatus({ syncing: true });
      Services.tm.dispatchToMainThread(() => Weave.Service.sync());
    }
  },

  openPrefs(entryPoint = "syncbutton", origin = undefined) {
    window.openPreferences("paneSync", { origin, urlParams: { entrypoint: entryPoint } });
  },

  openSyncedTabsPanel() {
    let placement = CustomizableUI.getPlacementOfWidget("sync-button");
    let area = placement && placement.area;
    let anchor = document.getElementById("sync-button") ||
                 document.getElementById("PanelUI-menu-button");
    if (area == CustomizableUI.AREA_FIXED_OVERFLOW_PANEL) {
      // The button is in the overflow panel, so we need to show the panel,
      // then show our subview.
      let navbar = document.getElementById(CustomizableUI.AREA_NAVBAR);
      navbar.overflowable.show().then(() => {
        PanelUI.showSubView("PanelUI-remotetabs", anchor);
      }, Cu.reportError);
    } else {
      // It is placed somewhere else - just try and show it.
      PanelUI.showSubView("PanelUI-remotetabs", anchor);
    }
  },

  /* Update the tooltip for the sync-status broadcaster (which will update the
     Sync Toolbar button and the Sync spinner in the FxA hamburger area.)
     If Sync is configured, the tooltip is when the last sync occurred,
     otherwise the tooltip reflects the fact that Sync needs to be
     (re-)configured.
  */
  updateSyncButtonsTooltip(state) {
    const status = state.status;

    // This is a little messy as the Sync buttons are 1/2 Sync related and
    // 1/2 FxA related - so for some strings we use Sync strings, but for
    // others we reach into gSync for strings.
    let tooltiptext;
    if (status == UIState.STATUS_NOT_VERIFIED) {
      // "needs verification"
      tooltiptext = this.fxaStrings.formatStringFromName("verifyDescription", [state.email], 1);
    } else if (status == UIState.STATUS_NOT_CONFIGURED) {
      // "needs setup".
      tooltiptext = this.syncStrings.GetStringFromName("signInToSync.description");
    } else if (status == UIState.STATUS_LOGIN_FAILED) {
      // "need to reconnect/re-enter your password"
      tooltiptext = this.fxaStrings.formatStringFromName("reconnectDescription", [state.email], 1);
    } else {
      // Sync appears configured - format the "last synced at" time.
      tooltiptext = this.formatLastSyncDate(state.lastSync);
    }

    let broadcaster = document.getElementById("sync-status");
    if (broadcaster) {
      if (tooltiptext) {
        broadcaster.setAttribute("tooltiptext", tooltiptext);
      } else {
        broadcaster.removeAttribute("tooltiptext");
      }
    }
  },

  get withinLastWeekFormat() {
    delete this.withinLastWeekFormat;
    return this.withinLastWeekFormat = new Intl.DateTimeFormat(undefined,
      {weekday: "long", hour: "numeric", minute: "numeric"});
  },

  get oneWeekOrOlderFormat() {
    delete this.oneWeekOrOlderFormat;
    return this.oneWeekOrOlderFormat = new Intl.DateTimeFormat(undefined,
      {month: "long", day: "numeric"});
  },

  formatLastSyncDate(date) {
    let sixDaysAgo = (() => {
      let tempDate = new Date();
      tempDate.setDate(tempDate.getDate() - 6);
      tempDate.setHours(0, 0, 0, 0);
      return tempDate;
    })();

    // It may be confusing for the user to see "Last Sync: Monday" when the last
    // sync was indeed a Monday, but 3 weeks ago.
    let dateFormat = date < sixDaysAgo ? this.oneWeekOrOlderFormat : this.withinLastWeekFormat;

    let lastSyncDateString = dateFormat.format(date);
    return this.syncStrings.formatStringFromName("lastSync2.label", [lastSyncDateString], 1);
  },

  onClientsSynced() {
    let broadcaster = document.getElementById("sync-syncnow-state");
    if (broadcaster) {
      if (Weave.Service.clientsEngine.stats.numClients > 1) {
        broadcaster.setAttribute("devices-status", "multi");
      } else {
        broadcaster.setAttribute("devices-status", "single");
      }
    }
  },

  onSyncDisabled() {
    const toHide = [...document.querySelectorAll(".sync-ui-item")];
    for (const item of toHide) {
      item.hidden = true;
    }
  },

  QueryInterface: XPCOMUtils.generateQI([
    Ci.nsIObserver,
    Ci.nsISupportsWeakReference
  ])
};
PK
!<��ALL6chrome/browser/content/browser/browser-tabPreviews.xml<?xml version="1.0"?>


<bindings id="tabPreviews"
          xmlns="http://www.mozilla.org/xbl"
          xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
          xmlns:xbl="http://www.mozilla.org/xbl">
  <binding id="ctrlTab-preview" extends="chrome://global/content/bindings/button.xml#button-base">
    <content pack="center">
      <xul:vbox class="ctrlTab-preview-inner">
        <xul:hbox class="ctrlTab-canvas" xbl:inherits="style=canvasstyle,width=canvaswidth">
          <children/>
        </xul:hbox>
        <xul:hbox class="ctrlTab-favicon-container" xbl:inherits="hidden=noicon">
          <xul:image class="ctrlTab-favicon" xbl:inherits="src=image"/>
        </xul:hbox>
        <xul:label class="ctrlTab-label plain" xbl:inherits="value=label" crop="end"/>
      </xul:vbox>
    </content>

    <handlers>
      <handler event="mouseover" action="ctrlTab._mouseOverFocus(this);"/>
      <handler event="command" action="ctrlTab.pick(this);"/>
      <handler event="click" button="1" action="ctrlTab.remove(this);"/>
    </handlers>
  </binding>
</bindings>
PK
!<����..8chrome/browser/content/browser/browser-tabsintitlebar.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Note: the file browser-tabsintitlebar-stub.js is used instead of
// this one on platforms which don't have CAN_DRAW_IN_TITLEBAR defined.

var TabsInTitlebar = {
  init() {
    if (this._initialized) {
      return;
    }
    this._readPref();
    Services.prefs.addObserver(this._prefName, this);

    // Always disable on unsupported GTK versions.
    if (AppConstants.MOZ_WIDGET_TOOLKIT == "gtk3") {
      this.allowedBy("gtk", window.matchMedia("(-moz-gtk-csd-available)"));
    }

    // We need to update the appearance of the titlebar when the menu changes
    // from the active to the inactive state. We can't, however, rely on
    // DOMMenuBarInactive, because the menu fires this event and then removes
    // the inactive attribute after an event-loop spin.
    //
    // Because updating the appearance involves sampling the heights and margins
    // of various elements, it's important that the layout be more or less
    // settled before updating the titlebar. So instead of listening to
    // DOMMenuBarActive and DOMMenuBarInactive, we use a MutationObserver to
    // watch the "invalid" attribute directly.
    let menu = document.getElementById("toolbar-menubar");
    this._menuObserver = new MutationObserver(this._onMenuMutate);
    this._menuObserver.observe(menu, {attributes: true});

    this.onAreaReset = function(aArea) {
      if (aArea == CustomizableUI.AREA_TABSTRIP || aArea == CustomizableUI.AREA_MENUBAR)
        this._update(true);
    };
    this.onWidgetAdded = this.onWidgetRemoved = function(aWidgetId, aArea) {
      if (aArea == CustomizableUI.AREA_TABSTRIP || aArea == CustomizableUI.AREA_MENUBAR)
        this._update(true);
    };
    CustomizableUI.addListener(this);

    addEventListener("resolutionchange", this, false);

    this._initialized = true;
    if (this._updateOnInit) {
      // We don't need to call this with 'true', even if original calls
      // (before init()) did, because this will be the first call and so
      // we will update anyway.
      this._update();
    }
  },

  allowedBy(condition, allow) {
    if (allow) {
      if (condition in this._disallowed) {
        delete this._disallowed[condition];
        this._update(true);
      }
    } else if (!(condition in this._disallowed)) {
      this._disallowed[condition] = null;
      this._update(true);
    }
  },

  updateAppearance: function updateAppearance(aForce) {
    this._update(aForce);
  },

  get enabled() {
    return document.documentElement.getAttribute("tabsintitlebar") == "true";
  },

  observe(subject, topic, data) {
    if (topic == "nsPref:changed")
      this._readPref();
  },

  handleEvent(aEvent) {
    if (aEvent.type == "resolutionchange" && aEvent.target == window) {
      this._update(true);
    }
  },

  _onMenuMutate(aMutations) {
    for (let mutation of aMutations) {
      if (mutation.attributeName == "inactive" ||
          mutation.attributeName == "autohide") {
        TabsInTitlebar._update(true);
        return;
      }
    }
  },

  _initialized: false,
  _updateOnInit: false,
  _disallowed: {},
  _prefName: "browser.tabs.drawInTitlebar",
  _lastSizeMode: null,

  _readPref() {
    this.allowedBy("pref",
                   Services.prefs.getBoolPref(this._prefName));
  },

  _update(aForce = false) {
    let $ = id => document.getElementById(id);
    let rect = ele => ele.getBoundingClientRect();
    let verticalMargins = cstyle => parseFloat(cstyle.marginBottom) + parseFloat(cstyle.marginTop);

    if (window.fullScreen)
      return;

    // In some edgecases it is possible for this to fire before we've initialized.
    // Don't run now, but don't forget to run it when we do initialize.
    if (!this._initialized) {
      this._updateOnInit = true;
      return;
    }

    if (!aForce) {
      // _update is called on resize events, because the window is not ready
      // after sizemode events. However, we only care about the event when the
      // sizemode is different from the last time we updated the appearance of
      // the tabs in the titlebar.
      let sizemode = document.documentElement.getAttribute("sizemode");
      if (this._lastSizeMode == sizemode) {
        return;
      }
      let oldSizeMode = this._lastSizeMode;
      this._lastSizeMode = sizemode;
      // Don't update right now if we are leaving fullscreen, since the UI is
      // still changing in the consequent "fullscreen" event. Code there will
      // call this function again when everything is ready.
      // See browser-fullScreen.js: FullScreen.toggle and bug 1173768.
      if (oldSizeMode == "fullscreen") {
        return;
      }
    }

    let allowed = (Object.keys(this._disallowed)).length == 0;

    let titlebar = $("titlebar");
    let titlebarContent = $("titlebar-content");
    let titlebarButtons = $("titlebar-buttonbox");
    let menubar = $("toolbar-menubar");

    if (allowed) {
      // We set the tabsintitlebar attribute first so that our CSS for
      // tabsintitlebar manifests before we do our measurements.
      document.documentElement.setAttribute("tabsintitlebar", "true");
      updateTitlebarDisplay();

      // Reset the custom titlebar height if the menubar is shown,
      // because we will want to calculate its original height.
      let buttonsShouldMatchTabHeight =
        AppConstants.isPlatformAndVersionAtLeast("win", "10.0") ||
        AppConstants.platform == "linux";
      if (buttonsShouldMatchTabHeight &&
          (menubar.getAttribute("inactive") != "true" ||
           menubar.getAttribute("autohide") != "true")) {
        titlebarButtons.style.removeProperty("height");
      }

      // Try to avoid reflows in this code by calculating dimensions first and
      // then later set the properties affecting layout together in a batch.

      // Get the height of the tabs toolbar:
      let fullTabsHeight = rect($("TabsToolbar")).height;

      // Buttons first:
      let captionButtonsBoxWidth = rect(titlebarButtons).width;

      let secondaryButtonsWidth, menuHeight, fullMenuHeight, menuStyles;
      if (AppConstants.platform == "macosx") {
        secondaryButtonsWidth = rect($("titlebar-secondary-buttonbox")).width;
        // No need to look up the menubar stuff on OS X:
        menuHeight = 0;
        fullMenuHeight = 0;
      } else {
        // Otherwise, get the height and margins separately for the menubar
        menuHeight = rect(menubar).height;
        menuStyles = window.getComputedStyle(menubar);
        fullMenuHeight = verticalMargins(menuStyles) + menuHeight;
      }

      // And get the height of what's in the titlebar:
      let titlebarContentHeight = rect(titlebarContent).height;

      // Begin setting CSS properties which will cause a reflow

      // Adjust the window controls to span the entire
      // tab strip height if we're not showing a menu bar.
      if (buttonsShouldMatchTabHeight && !menuHeight) {
        titlebarContentHeight = fullTabsHeight;
        titlebarButtons.style.height = titlebarContentHeight + "px";
      }

      // If the menubar is around (menuHeight is non-zero), try to adjust
      // its full height (i.e. including margins) to match the titlebar,
      // by changing the menubar's bottom padding
      if (menuHeight) {
        // Calculate the difference between the titlebar's height and that of the menubar
        let menuTitlebarDelta = titlebarContentHeight - fullMenuHeight;
        let paddingBottom;
        // The titlebar is bigger:
        if (menuTitlebarDelta > 0) {
          fullMenuHeight += menuTitlebarDelta;
          // If there is already padding on the menubar, we need to add that
          // to the difference so the total padding is correct:
          if ((paddingBottom = menuStyles.paddingBottom)) {
            menuTitlebarDelta += parseFloat(paddingBottom);
          }
          menubar.style.paddingBottom = menuTitlebarDelta + "px";
        // The menubar is bigger, but has bottom padding we can remove:
        } else if (menuTitlebarDelta < 0 && (paddingBottom = menuStyles.paddingBottom)) {
          let existingPadding = parseFloat(paddingBottom);
          // menuTitlebarDelta is negative; work out what's left, but don't set negative padding:
          let desiredPadding = Math.max(0, existingPadding + menuTitlebarDelta);
          menubar.style.paddingBottom = desiredPadding + "px";
          // We've changed the menu height now:
          fullMenuHeight += desiredPadding - existingPadding;
        }
      }

      // Next, we calculate how much we need to stretch the titlebar down to
      // go all the way to the bottom of the tab strip, if necessary.
      let tabAndMenuHeight = fullTabsHeight + fullMenuHeight;

      if (tabAndMenuHeight > titlebarContentHeight) {
        // We need to increase the titlebar content's outer height (ie including margins)
        // to match the tab and menu height:
        let extraMargin = tabAndMenuHeight - titlebarContentHeight;
        if (AppConstants.platform != "macosx") {
          titlebarContent.style.marginBottom = extraMargin + "px";
        }

        titlebarContentHeight += extraMargin;
      } else {
        titlebarContent.style.removeProperty("margin-bottom");
      }

      // Then add a negative margin to the titlebar, so that the following elements
      // will overlap it by the greater of the titlebar height or the tabstrip+menu.
      let maxTitlebarOrTabsHeight = Math.max(titlebarContentHeight, tabAndMenuHeight);
      titlebar.style.marginBottom = "-" + maxTitlebarOrTabsHeight + "px";

      // Finally, size the placeholders:
      if (AppConstants.platform == "macosx") {
        this._sizePlaceholder("fullscreen-button", secondaryButtonsWidth);
      }
      this._sizePlaceholder("caption-buttons", captionButtonsBoxWidth);

    } else {
      document.documentElement.removeAttribute("tabsintitlebar");
      updateTitlebarDisplay();

      if (AppConstants.platform == "macosx") {
        let secondaryButtonsWidth = rect($("titlebar-secondary-buttonbox")).width;
        this._sizePlaceholder("fullscreen-button", secondaryButtonsWidth);
      }

      // Reset styles that might have been modified:
      titlebar.style.marginBottom = "";
      menubar.style.paddingBottom = "";
    }

    ToolbarIconColor.inferFromText("tabsintitlebar", TabsInTitlebar.enabled);
  },

  _sizePlaceholder(type, width) {
    Array.forEach(document.querySelectorAll(".titlebar-placeholder[type='" + type + "']"),
                  function(node) { node.style.width = width + "px"; });
  },

  uninit() {
    this._initialized = false;
    removeEventListener("resolutionchange", this);
    Services.prefs.removeObserver(this._prefName, this);
    this._menuObserver.disconnect();
    CustomizableUI.removeListener(this);
  }
};

function updateTitlebarDisplay() {
  if (AppConstants.platform == "macosx") {
    if (TabsInTitlebar.enabled) {
      document.documentElement.setAttribute("chromemargin", "0,-1,-1,-1");
      document.documentElement.removeAttribute("drawtitle");
    } else {
      document.documentElement.removeAttribute("chromemargin");
      document.documentElement.setAttribute("drawtitle", "true");
    }
  } else if (TabsInTitlebar.enabled) {
    // not OS X
    document.documentElement.setAttribute("chromemargin", "0,2,2,2");
  } else {
    document.documentElement.removeAttribute("chromemargin");
  }
}

function onTitlebarMaxClick() {
  if (window.windowState == window.STATE_MAXIMIZED)
    window.restore();
  else
    window.maximize();
}
PK
!<�S�W4chrome/browser/content/browser/browser-thumbnails.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 file is loaded into the browser window scope.
/* eslint-env mozilla/browser-window */

/**
 * Keeps thumbnails of open web pages up-to-date.
 */
var gBrowserThumbnails = {
  /**
   * Pref that controls whether we can store SSL content on disk
   */
  PREF_DISK_CACHE_SSL: "browser.cache.disk_cache_ssl",

  _captureDelayMS: 1000,

  /**
   * Used to keep track of disk_cache_ssl preference
   */
  _sslDiskCacheEnabled: null,

  /**
   * Map of capture() timeouts assigned to their browsers.
   */
  _timeouts: null,

  /**
   * Top site URLs refresh timer.
   */
  _topSiteURLsRefreshTimer: null,

  /**
   * List of tab events we want to listen for.
   */
  _tabEvents: ["TabClose", "TabSelect"],

  init: function Thumbnails_init() {
    gBrowser.addTabsProgressListener(this);
    Services.prefs.addObserver(this.PREF_DISK_CACHE_SSL, this);

    this._sslDiskCacheEnabled =
      Services.prefs.getBoolPref(this.PREF_DISK_CACHE_SSL);

    this._tabEvents.forEach(function(aEvent) {
      gBrowser.tabContainer.addEventListener(aEvent, this);
    }, this);

    this._timeouts = new WeakMap();
  },

  uninit: function Thumbnails_uninit() {
    gBrowser.removeTabsProgressListener(this);
    Services.prefs.removeObserver(this.PREF_DISK_CACHE_SSL, this);

    if (this._topSiteURLsRefreshTimer) {
      this._topSiteURLsRefreshTimer.cancel();
      this._topSiteURLsRefreshTimer = null;
    }

    this._tabEvents.forEach(function(aEvent) {
      gBrowser.tabContainer.removeEventListener(aEvent, this);
    }, this);
  },

  handleEvent: function Thumbnails_handleEvent(aEvent) {
    switch (aEvent.type) {
      case "scroll":
        let browser = aEvent.currentTarget;
        if (this._timeouts.has(browser))
          this._delayedCapture(browser);
        break;
      case "TabSelect":
        this._delayedCapture(aEvent.target.linkedBrowser);
        break;
      case "TabClose": {
        this._cancelDelayedCapture(aEvent.target.linkedBrowser);
        break;
      }
    }
  },

  observe: function Thumbnails_observe(subject, topic, data) {
    switch (data) {
      case this.PREF_DISK_CACHE_SSL:
        this._sslDiskCacheEnabled =
          Services.prefs.getBoolPref(this.PREF_DISK_CACHE_SSL);
        break;
    }
  },

  clearTopSiteURLCache: function Thumbnails_clearTopSiteURLCache() {
    if (this._topSiteURLsRefreshTimer) {
      this._topSiteURLsRefreshTimer.cancel();
      this._topSiteURLsRefreshTimer = null;
    }
    // Delete the defined property
    delete this._topSiteURLs;
    XPCOMUtils.defineLazyGetter(this, "_topSiteURLs", getTopSiteURLs);
  },

  notify: function Thumbnails_notify(timer) {
    gBrowserThumbnails._topSiteURLsRefreshTimer = null;
    gBrowserThumbnails.clearTopSiteURLCache();
  },

  /**
   * State change progress listener for all tabs.
   */
  onStateChange: function Thumbnails_onStateChange(aBrowser, aWebProgress,
                                                   aRequest, aStateFlags, aStatus) {
    if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
        aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK)
      this._delayedCapture(aBrowser);
  },

  async _capture(aBrowser) {
    // Only capture about:newtab top sites.
    const topSites = await this._topSiteURLs;
    if (!aBrowser.currentURI ||
        !topSites.includes(aBrowser.currentURI.spec))
      return;
    this._shouldCapture(aBrowser, function(aResult) {
      if (aResult) {
        PageThumbs.captureAndStoreIfStale(aBrowser);
      }
    });
  },

  _delayedCapture: function Thumbnails_delayedCapture(aBrowser) {
    if (this._timeouts.has(aBrowser)) {
      this._cancelDelayedCallbacks(aBrowser);
    } else {
      aBrowser.addEventListener("scroll", this, true);
    }

    let idleCallback = () => {
      this._cancelDelayedCapture(aBrowser);
      this._capture(aBrowser);
    };

    // setTimeout to set a guarantee lower bound for the requestIdleCallback
    // (and therefore the delayed capture)
    let timeoutId = setTimeout(() => {
      let idleCallbackId = requestIdleCallback(idleCallback, {
        timeout: this._captureDelayMS * 30
      });
      this._timeouts.set(aBrowser, { isTimeout: false, id: idleCallbackId });
    }, this._captureDelayMS);

    this._timeouts.set(aBrowser, { isTimeout: true, id: timeoutId });
  },

  _shouldCapture: function Thumbnails_shouldCapture(aBrowser, aCallback) {
    // Capture only if it's the currently selected tab and not an about: page.
    if (aBrowser != gBrowser.selectedBrowser ||
        gBrowser.currentURI.schemeIs("about")) {
      aCallback(false);
      return;
    }
    PageThumbs.shouldStoreThumbnail(aBrowser, aCallback);
  },

  _cancelDelayedCapture: function Thumbnails_cancelDelayedCapture(aBrowser) {
    if (this._timeouts.has(aBrowser)) {
      aBrowser.removeEventListener("scroll", this);
      this._cancelDelayedCallbacks(aBrowser);
      this._timeouts.delete(aBrowser);
    }
  },

  _cancelDelayedCallbacks: function Thumbnails_cancelDelayedCallbacks(aBrowser) {
    let timeoutData = this._timeouts.get(aBrowser);

    if (timeoutData.isTimeout) {
      clearTimeout(timeoutData.id);
    } else {
      // idle callback dispatched
      window.cancelIdleCallback(timeoutData.id);
    }
  }
};

async function getTopSiteURLs() {
  // The _topSiteURLs getter can be expensive to run, but its return value can
  // change frequently on new profiles, so as a compromise we cache its return
  // value as a lazy getter for 1 minute every time it's called.
  gBrowserThumbnails._topSiteURLsRefreshTimer =
    Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
  gBrowserThumbnails._topSiteURLsRefreshTimer.initWithCallback(gBrowserThumbnails,
                                                               60 * 1000,
                                                               Ci.nsITimer.TYPE_ONE_SHOT);
  let sites = [];
  // Get both the top sites returned by the query, and also any pinned sites
  // that the user might have added manually that also need a screenshot.
  // Also include top sites that don't have rich icons
  let topSites = await NewTabUtils.activityStreamLinks.getTopSites();
  sites.push(...topSites.filter(link => !(link.faviconSize >= 96)));
  sites.push(...NewTabUtils.pinnedLinks.links);
  return sites.reduce((urls, link) => {
    if (link) urls.push(link.url);
    return urls;
  }, []);
}

XPCOMUtils.defineLazyGetter(gBrowserThumbnails, "_topSiteURLs", getTopSiteURLs);
PK
!<ώL!!<chrome/browser/content/browser/browser-trackingprotection.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

var TrackingProtection = {
  // If the user ignores the doorhanger, we stop showing it after some time.
  MAX_INTROS: 20,
  PREF_ENABLED_GLOBALLY: "privacy.trackingprotection.enabled",
  PREF_ENABLED_IN_PRIVATE_WINDOWS: "privacy.trackingprotection.pbmode.enabled",
  enabledGlobally: false,
  enabledInPrivateWindows: false,
  container: null,
  content: null,
  icon: null,
  activeTooltipText: null,
  disabledTooltipText: null,

  init() {
    let $ = selector => document.querySelector(selector);
    this.container = $("#tracking-protection-container");
    this.content = $("#tracking-protection-content");
    this.icon = $("#tracking-protection-icon");

    this.updateEnabled();
    Services.prefs.addObserver(this.PREF_ENABLED_GLOBALLY, this);
    Services.prefs.addObserver(this.PREF_ENABLED_IN_PRIVATE_WINDOWS, this);

    this.activeTooltipText =
      gNavigatorBundle.getString("trackingProtection.icon.activeTooltip");
    this.disabledTooltipText =
      gNavigatorBundle.getString("trackingProtection.icon.disabledTooltip");

    this.enabledHistogramAdd(this.enabledGlobally);
    this.disabledPBMHistogramAdd(!this.enabledInPrivateWindows);
  },

  uninit() {
    Services.prefs.removeObserver(this.PREF_ENABLED_GLOBALLY, this);
    Services.prefs.removeObserver(this.PREF_ENABLED_IN_PRIVATE_WINDOWS, this);
  },

  observe() {
    this.updateEnabled();
  },

  get enabled() {
    return this.enabledGlobally ||
           (this.enabledInPrivateWindows &&
            PrivateBrowsingUtils.isWindowPrivate(window));
  },

  updateEnabled() {
    this.enabledGlobally =
      Services.prefs.getBoolPref(this.PREF_ENABLED_GLOBALLY);
    this.enabledInPrivateWindows =
      Services.prefs.getBoolPref(this.PREF_ENABLED_IN_PRIVATE_WINDOWS);
    this.container.hidden = !this.enabled;
  },

  enabledHistogramAdd(value) {
    if (PrivateBrowsingUtils.isWindowPrivate(window)) {
      return;
    }
    Services.telemetry.getHistogramById("TRACKING_PROTECTION_ENABLED").add(value);
  },

  disabledPBMHistogramAdd(value) {
    if (PrivateBrowsingUtils.isWindowPrivate(window)) {
      return;
    }
    Services.telemetry.getHistogramById("TRACKING_PROTECTION_PBM_DISABLED").add(value);
  },

  eventsHistogramAdd(value) {
    if (PrivateBrowsingUtils.isWindowPrivate(window)) {
      return;
    }
    Services.telemetry.getHistogramById("TRACKING_PROTECTION_EVENTS").add(value);
  },

  shieldHistogramAdd(value) {
    if (PrivateBrowsingUtils.isWindowPrivate(window)) {
      return;
    }
    Services.telemetry.getHistogramById("TRACKING_PROTECTION_SHIELD").add(value);
  },

  onSecurityChange(state, isSimulated) {
    if (!this.enabled) {
      return;
    }

    // Only animate the shield if the event was not fired directly from
    // the tabbrowser (due to a browser change).
    if (isSimulated) {
      this.icon.removeAttribute("animate");
    } else {
      this.icon.setAttribute("animate", "true");
    }

    let isBlocking = state & Ci.nsIWebProgressListener.STATE_BLOCKED_TRACKING_CONTENT;
    let isAllowing = state & Ci.nsIWebProgressListener.STATE_LOADED_TRACKING_CONTENT;

    if (isBlocking) {
      this.icon.setAttribute("tooltiptext", this.activeTooltipText);
      this.icon.setAttribute("state", "blocked-tracking-content");
      this.content.setAttribute("state", "blocked-tracking-content");

      // Open the tracking protection introduction panel, if applicable.
      if (this.enabledGlobally) {
        let introCount = Services.prefs.getIntPref("privacy.trackingprotection.introCount");
        if (introCount < TrackingProtection.MAX_INTROS) {
          Services.prefs.setIntPref("privacy.trackingprotection.introCount", ++introCount);
          Services.prefs.savePrefFile(null);
          this.showIntroPanel();
        }
      }

      this.shieldHistogramAdd(2);
    } else if (isAllowing) {
      this.icon.setAttribute("tooltiptext", this.disabledTooltipText);
      this.icon.setAttribute("state", "loaded-tracking-content");
      this.content.setAttribute("state", "loaded-tracking-content");

      this.shieldHistogramAdd(1);
    } else {
      this.icon.removeAttribute("tooltiptext");
      this.icon.removeAttribute("state");
      this.content.removeAttribute("state");

      // We didn't show the shield
      this.shieldHistogramAdd(0);
    }

    // Telemetry for state change.
    this.eventsHistogramAdd(0);
  },

  disableForCurrentPage() {
    // Convert document URI into the format used by
    // nsChannelClassifier::ShouldEnableTrackingProtection.
    // Any scheme turned into https is correct.
    let normalizedUrl = Services.io.newURI(
      "https://" + gBrowser.selectedBrowser.currentURI.hostPort);

    // Add the current host in the 'trackingprotection' consumer of
    // the permission manager using a normalized URI. This effectively
    // places this host on the tracking protection allowlist.
    if (PrivateBrowsingUtils.isBrowserPrivate(gBrowser.selectedBrowser)) {
      PrivateBrowsingUtils.addToTrackingAllowlist(normalizedUrl);
    } else {
      Services.perms.add(normalizedUrl,
        "trackingprotection", Services.perms.ALLOW_ACTION);
    }

    // Telemetry for disable protection.
    this.eventsHistogramAdd(1);

    // Hide the control center.
    document.getElementById("identity-popup").hidePopup();

    BrowserReload();
  },

  enableForCurrentPage() {
    // Remove the current host from the 'trackingprotection' consumer
    // of the permission manager. This effectively removes this host
    // from the tracking protection allowlist.
    let normalizedUrl = Services.io.newURI(
      "https://" + gBrowser.selectedBrowser.currentURI.hostPort);

    if (PrivateBrowsingUtils.isBrowserPrivate(gBrowser.selectedBrowser)) {
      PrivateBrowsingUtils.removeFromTrackingAllowlist(normalizedUrl);
    } else {
      Services.perms.remove(normalizedUrl, "trackingprotection");
    }

    // Telemetry for enable protection.
    this.eventsHistogramAdd(2);

    // Hide the control center.
    document.getElementById("identity-popup").hidePopup();

    BrowserReload();
  },

  dontShowIntroPanelAgain() {
    // This function may be called in private windows, but it does not change
    // any preference unless Tracking Protection is enabled globally.
    if (this.enabledGlobally) {
      Services.prefs.setIntPref("privacy.trackingprotection.introCount",
                                this.MAX_INTROS);
      Services.prefs.savePrefFile(null);
    }
  },

  async showIntroPanel() {
    let brandBundle = document.getElementById("bundle_brand");
    let brandShortName = brandBundle.getString("brandShortName");

    let openStep2 = () => {
      // When the user proceeds in the tour, adjust the counter to indicate that
      // the user doesn't need to see the intro anymore.
      this.dontShowIntroPanelAgain();

      let nextURL = Services.urlFormatter.formatURLPref("privacy.trackingprotection.introURL") +
                    "?step=2&newtab=true";
      switchToTabHavingURI(nextURL, true, {
        // Ignore the fragment in case the intro is shown on the tour page
        // (e.g. if the user manually visited the tour or clicked the link from
        // about:privatebrowsing) so we can avoid a reload.
        ignoreFragment: "whenComparingAndReplace",
        triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
      });
    };

    let buttons = [
      {
        label: gNavigatorBundle.getString("trackingProtection.intro.step1of3"),
        style: "text",
      },
      {
        callback: openStep2,
        label: gNavigatorBundle.getString("trackingProtection.intro.nextButton.label"),
        style: "primary",
      },
    ];

    let panelTarget = await UITour.getTarget(window, "trackingProtection");
    UITour.initForBrowser(gBrowser.selectedBrowser, window);
    UITour.showInfo(window, panelTarget,
                    gNavigatorBundle.getString("trackingProtection.intro.title"),
                    gNavigatorBundle.getFormattedString("trackingProtection.intro.description2",
                                                        [brandShortName]),
                    undefined, buttons,
                    { closeButtonCallback: () => this.dontShowIntroPanelAgain() });
  },
};
PK
!<i�ff3chrome/browser/content/browser/browser-webrender.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 file is loaded into the browser window scope.
/* eslint-env mozilla/browser-window */

/**
 * Global browser interface with the WebRender backend.
 */
var gWebRender = {
  /**
   * Trigger a WebRender capture of the current state into a local folder.
   */
  capture() {
    window.QueryInterface(Ci.nsIInterfaceRequestor)
          .getInterface(Ci.nsIDOMWindowUtils)
          .wrCapture();
  }
};
PK
!<|����/�/)chrome/browser/content/browser/browser.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* eslint-env mozilla/browser-window */

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
ChromeUtils.import("resource://gre/modules/NotificationDB.jsm");

const {WebExtensionPolicy} = Cu.getGlobalForObject(Services);

// lazy module getters

XPCOMUtils.defineLazyModuleGetters(this, {
  AboutHome: "resource:///modules/AboutHome.jsm",
  BrowserUITelemetry: "resource:///modules/BrowserUITelemetry.jsm",
  BrowserUsageTelemetry: "resource:///modules/BrowserUsageTelemetry.jsm",
  BrowserUtils: "resource://gre/modules/BrowserUtils.jsm",
  CharsetMenu: "resource://gre/modules/CharsetMenu.jsm",
  Color: "resource://gre/modules/Color.jsm",
  ContentSearch: "resource:///modules/ContentSearch.jsm",
  ContextualIdentityService: "resource://gre/modules/ContextualIdentityService.jsm",
  CustomizableUI: "resource:///modules/CustomizableUI.jsm",
  Deprecated: "resource://gre/modules/Deprecated.jsm",
  DownloadsCommon: "resource:///modules/DownloadsCommon.jsm",
  E10SUtils: "resource://gre/modules/E10SUtils.jsm",
  ExtensionsUI: "resource:///modules/ExtensionsUI.jsm",
  FormValidationHandler: "resource:///modules/FormValidationHandler.jsm",
  LanguagePrompt: "resource://gre/modules/LanguagePrompt.jsm",
  LightweightThemeConsumer: "resource://gre/modules/LightweightThemeConsumer.jsm",
  LightweightThemeManager: "resource://gre/modules/LightweightThemeManager.jsm",
  Log: "resource://gre/modules/Log.jsm",
  LoginManagerParent: "resource://gre/modules/LoginManagerParent.jsm",
  NetUtil: "resource://gre/modules/NetUtil.jsm",
  NewTabUtils: "resource://gre/modules/NewTabUtils.jsm",
  OpenInTabsUtils: "resource:///modules/OpenInTabsUtils.jsm",
  PageActions: "resource:///modules/PageActions.jsm",
  PageThumbs: "resource://gre/modules/PageThumbs.jsm",
  PanelMultiView: "resource:///modules/PanelMultiView.jsm",
  PanelView: "resource:///modules/PanelMultiView.jsm",
  PluralForm: "resource://gre/modules/PluralForm.jsm",
  PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
  ProcessHangMonitor: "resource:///modules/ProcessHangMonitor.jsm",
  PromiseUtils: "resource://gre/modules/PromiseUtils.jsm",
  ReaderMode: "resource://gre/modules/ReaderMode.jsm",
  ReaderParent: "resource:///modules/ReaderParent.jsm",
  RecentWindow: "resource:///modules/RecentWindow.jsm",
  SafeBrowsing: "resource://gre/modules/SafeBrowsing.jsm",
  Sanitizer: "resource:///modules/Sanitizer.jsm",
  SessionStore: "resource:///modules/sessionstore/SessionStore.jsm",
  SchedulePressure: "resource:///modules/SchedulePressure.jsm",
  ShortcutUtils: "resource://gre/modules/ShortcutUtils.jsm",
  SimpleServiceDiscovery: "resource://gre/modules/SimpleServiceDiscovery.jsm",
  SitePermissions: "resource:///modules/SitePermissions.jsm",
  TabCrashHandler: "resource:///modules/ContentCrashHandlers.jsm",
  TelemetryStopwatch: "resource://gre/modules/TelemetryStopwatch.jsm",
  Translation: "resource:///modules/translation/Translation.jsm",
  UITour: "resource:///modules/UITour.jsm",
  UpdateUtils: "resource://gre/modules/UpdateUtils.jsm",
  Utils: "resource://gre/modules/sessionstore/Utils.jsm",
  Weave: "resource://services-sync/main.js",
  WebNavigationFrames: "resource://gre/modules/WebNavigationFrames.jsm",
  fxAccounts: "resource://gre/modules/FxAccounts.jsm",
  webrtcUI: "resource:///modules/webrtcUI.jsm",
  ZoomUI: "resource:///modules/ZoomUI.jsm",
});

if (AppConstants.MOZ_CRASHREPORTER) {
  ChromeUtils.defineModuleGetter(this, "PluginCrashReporter",
    "resource:///modules/ContentCrashHandlers.jsm");
}

XPCOMUtils.defineLazyScriptGetter(this, "PrintUtils",
                                  "chrome://global/content/printUtils.js");
XPCOMUtils.defineLazyScriptGetter(this, "ZoomManager",
                                  "chrome://global/content/viewZoomOverlay.js");
XPCOMUtils.defineLazyScriptGetter(this, "FullZoom",
                                  "chrome://browser/content/browser-fullZoom.js");
XPCOMUtils.defineLazyScriptGetter(this, "PanelUI",
                                  "chrome://browser/content/customizableui/panelUI.js");
XPCOMUtils.defineLazyScriptGetter(this, "gViewSourceUtils",
                                  "chrome://global/content/viewSourceUtils.js");
XPCOMUtils.defineLazyScriptGetter(this, ["LightWeightThemeWebInstaller",
                                         "gExtensionsNotifications",
                                         "gXPInstallObserver"],
                                  "chrome://browser/content/browser-addons.js");
XPCOMUtils.defineLazyScriptGetter(this, "ctrlTab",
                                  "chrome://browser/content/browser-ctrlTab.js");
XPCOMUtils.defineLazyScriptGetter(this, "CustomizationHandler",
                                  "chrome://browser/content/browser-customization.js");
XPCOMUtils.defineLazyScriptGetter(this, ["PointerLock", "FullScreen"],
                                  "chrome://browser/content/browser-fullScreenAndPointerLock.js");
XPCOMUtils.defineLazyScriptGetter(this, ["gGestureSupport", "gHistorySwipeAnimation"],
                                  "chrome://browser/content/browser-gestureSupport.js");
XPCOMUtils.defineLazyScriptGetter(this, "gSafeBrowsing",
                                  "chrome://browser/content/browser-safebrowsing.js");
XPCOMUtils.defineLazyScriptGetter(this, "gSync",
                                  "chrome://browser/content/browser-sync.js");
XPCOMUtils.defineLazyScriptGetter(this, "gBrowserThumbnails",
                                  "chrome://browser/content/browser-thumbnails.js");
XPCOMUtils.defineLazyScriptGetter(this, ["setContextMenuContentData",
                                         "openContextMenu", "nsContextMenu"],
                                  "chrome://browser/content/nsContextMenu.js");
XPCOMUtils.defineLazyScriptGetter(this, ["DownloadsPanel",
                                         "DownloadsOverlayLoader",
                                         "DownloadsSubview",
                                         "DownloadsView", "DownloadsViewUI",
                                         "DownloadsViewController",
                                         "DownloadsSummary", "DownloadsFooter",
                                         "DownloadsBlockedSubview"],
                                  "chrome://browser/content/downloads/downloads.js");
XPCOMUtils.defineLazyScriptGetter(this, ["DownloadsButton",
                                         "DownloadsIndicatorView"],
                                  "chrome://browser/content/downloads/indicator.js");
XPCOMUtils.defineLazyScriptGetter(this, "gEditItemOverlay",
                                  "chrome://browser/content/places/editBookmarkOverlay.js");
if (AppConstants.NIGHTLY_BUILD) {
  XPCOMUtils.defineLazyScriptGetter(this, "gWebRender",
                                    "chrome://browser/content/browser-webrender.js");
}

// lazy service getters

XPCOMUtils.defineLazyServiceGetters(this, {
  Favicons: ["@mozilla.org/browser/favicon-service;1", "mozIAsyncFavicons"],
  gAboutNewTabService: ["@mozilla.org/browser/aboutnewtab-service;1", "nsIAboutNewTabService"],
  gDNSService: ["@mozilla.org/network/dns-service;1", "nsIDNSService"],
  gSerializationHelper: ["@mozilla.org/network/serialization-helper;1", "nsISerializationHelper"],
  Marionette: ["@mozilla.org/remote/marionette;1", "nsIMarionette"],
  SessionStartup: ["@mozilla.org/browser/sessionstartup;1", "nsISessionStartup"],
  WindowsUIUtils: ["@mozilla.org/windows-ui-utils;1", "nsIWindowsUIUtils"],
});

if (AppConstants.MOZ_CRASHREPORTER) {
  XPCOMUtils.defineLazyServiceGetter(this, "gCrashReporter",
                                     "@mozilla.org/xre/app-info;1",
                                     "nsICrashReporter");
}

XPCOMUtils.defineLazyGetter(this, "gBrowserBundle", function() {
  return Services.strings.createBundle("chrome://browser/locale/browser.properties");
});
XPCOMUtils.defineLazyGetter(this, "gNavigatorBundle", function() {
  // This is a stringbundle-like interface to gBrowserBundle, formerly a getter for
  // the "bundle_browser" element.
  return {
    getString(key) {
      return gBrowserBundle.GetStringFromName(key);
    },
    getFormattedString(key, array) {
      return gBrowserBundle.formatStringFromName(key, array, array.length);
    }
  };
});
XPCOMUtils.defineLazyGetter(this, "gTabBrowserBundle", function() {
  return Services.strings.createBundle("chrome://browser/locale/tabbrowser.properties");
});

XPCOMUtils.defineLazyGetter(this, "gCustomizeMode", function() {
  let scope = {};
  ChromeUtils.import("resource:///modules/CustomizeMode.jsm", scope);
  return new scope.CustomizeMode(window);
});

XPCOMUtils.defineLazyGetter(this, "InlineSpellCheckerUI", function() {
  let tmp = {};
  ChromeUtils.import("resource://gre/modules/InlineSpellChecker.jsm", tmp);
  return new tmp.InlineSpellChecker();
});

XPCOMUtils.defineLazyGetter(this, "PageMenuParent", function() {
  let tmp = {};
  ChromeUtils.import("resource://gre/modules/PageMenu.jsm", tmp);
  return new tmp.PageMenuParent();
});

XPCOMUtils.defineLazyGetter(this, "PopupNotifications", function() {
  let tmp = {};
  ChromeUtils.import("resource://gre/modules/PopupNotifications.jsm", tmp);
  try {
    // Hide all notifications while the URL is being edited and the address bar
    // has focus, including the virtual focus in the results popup.
    // We also have to hide notifications explicitly when the window is
    // minimized because of the effects of the "noautohide" attribute on Linux.
    // This can be removed once bug 545265 and bug 1320361 are fixed.
    let shouldSuppress = () => {
      return window.windowState == window.STATE_MINIMIZED ||
             (gURLBar.getAttribute("pageproxystate") != "valid" &&
             gURLBar.focused);
    };
    return new tmp.PopupNotifications(gBrowser,
                                      document.getElementById("notification-popup"),
                                      document.getElementById("notification-popup-box"),
                                      { shouldSuppress });
  } catch (ex) {
    Cu.reportError(ex);
    return null;
  }
});

XPCOMUtils.defineLazyGetter(this, "Win7Features", function() {
  if (AppConstants.platform != "win")
    return null;

  const WINTASKBAR_CONTRACTID = "@mozilla.org/windows-taskbar;1";
  if (WINTASKBAR_CONTRACTID in Cc &&
      Cc[WINTASKBAR_CONTRACTID].getService(Ci.nsIWinTaskbar).available) {
    let AeroPeek = ChromeUtils.import("resource:///modules/WindowsPreviewPerTab.jsm", {}).AeroPeek;
    return {
      onOpenWindow() {
        AeroPeek.onOpenWindow(window);
      },
      onCloseWindow() {
        AeroPeek.onCloseWindow(window);
      }
    };
  }
  return null;
});

const nsIWebNavigation = Ci.nsIWebNavigation;

var gBrowser;
var gLastValidURLStr = "";
var gInPrintPreviewMode = false;
var gContextMenu = null; // nsContextMenu instance
var gMultiProcessBrowser =
  window.QueryInterface(Ci.nsIInterfaceRequestor)
        .getInterface(Ci.nsIWebNavigation)
        .QueryInterface(Ci.nsILoadContext)
        .useRemoteTabs;

if (AppConstants.platform != "macosx") {
  var gEditUIVisible = true;
}

/* globals gNavToolbox, gURLBar:true */
[
  ["gNavToolbox",         "navigator-toolbox"],
  ["gURLBar",             "urlbar"],
].forEach(function(elementGlobal) {
  var [name, id] = elementGlobal;
  Object.defineProperty(window, name, {
    configurable: true,
    enumerable: true,
    get() {
      var element = document.getElementById(id);
      if (!element)
        return null;
      delete window[name];
      return window[name] = element;
    },
    set(val) {
      delete window[name];
      return window[name] = val;
    },
  });
});

// Smart getter for the findbar.  If you don't wish to force the creation of
// the findbar, check gFindBarInitialized first.

Object.defineProperty(this, "gFindBar", {
  configurable: true,
  enumerable: true,
  get() {
    return window.gBrowser.getFindBar();
  },
});

Object.defineProperty(this, "gFindBarInitialized", {
  configurable: true,
  enumerable: true,
  get() {
    return window.gBrowser.isFindBarInitialized();
  },
});

Object.defineProperty(this, "AddonManager", {
  configurable: true,
  enumerable: true,
  get() {
    let tmp = {};
    ChromeUtils.import("resource://gre/modules/AddonManager.jsm", tmp);
    return this.AddonManager = tmp.AddonManager;
  },
  set(val) {
    delete this.AddonManager;
    return this.AddonManager = val;
  },
});


var gInitialPages = [
  "about:blank",
  "about:newtab",
  "about:home",
  "about:privatebrowsing",
  "about:welcomeback",
  "about:sessionrestore"
];

function isInitialPage(url) {
  return gInitialPages.includes(url) || url == BROWSER_NEW_TAB_URL;
}

function* browserWindows() {
  let windows = Services.wm.getEnumerator("navigator:browser");
  while (windows.hasMoreElements())
    yield windows.getNext();
}

/**
* We can avoid adding multiple load event listeners and save some time by adding
* one listener that calls all real handlers.
*/
function pageShowEventHandlers(persisted) {
  XULBrowserWindow.asyncUpdateUI();
}

function UpdateBackForwardCommands(aWebNavigation) {
  var backBroadcaster = document.getElementById("Browser:Back");
  var forwardBroadcaster = document.getElementById("Browser:Forward");

  // Avoid setting attributes on broadcasters if the value hasn't changed!
  // Remember, guys, setting attributes on elements is expensive!  They
  // get inherited into anonymous content, broadcast to other widgets, etc.!
  // Don't do it if the value hasn't changed! - dwh

  var backDisabled = backBroadcaster.hasAttribute("disabled");
  var forwardDisabled = forwardBroadcaster.hasAttribute("disabled");
  if (backDisabled == aWebNavigation.canGoBack) {
    if (backDisabled)
      backBroadcaster.removeAttribute("disabled");
    else
      backBroadcaster.setAttribute("disabled", true);
  }

  if (forwardDisabled == aWebNavigation.canGoForward) {
    if (forwardDisabled)
      forwardBroadcaster.removeAttribute("disabled");
    else
      forwardBroadcaster.setAttribute("disabled", true);
  }
}

/**
 * Click-and-Hold implementation for the Back and Forward buttons
 * XXXmano: should this live in toolbarbutton.xml?
 */
function SetClickAndHoldHandlers() {
  // Bug 414797: Clone the back/forward buttons' context menu into both buttons.
  let popup = document.getElementById("backForwardMenu").cloneNode(true);
  popup.removeAttribute("id");
  // Prevent the back/forward buttons' context attributes from being inherited.
  popup.setAttribute("context", "");

  let backButton = document.getElementById("back-button");
  backButton.setAttribute("type", "menu");
  backButton.appendChild(popup);
  gClickAndHoldListenersOnElement.add(backButton);

  let forwardButton = document.getElementById("forward-button");
  popup = popup.cloneNode(true);
  forwardButton.setAttribute("type", "menu");
  forwardButton.appendChild(popup);
  gClickAndHoldListenersOnElement.add(forwardButton);
}


const gClickAndHoldListenersOnElement = {
  _timers: new Map(),

  _mousedownHandler(aEvent) {
    if (aEvent.button != 0 ||
        aEvent.currentTarget.open ||
        aEvent.currentTarget.disabled)
      return;

    // Prevent the menupopup from opening immediately
    aEvent.currentTarget.firstChild.hidden = true;

    aEvent.currentTarget.addEventListener("mouseout", this);
    aEvent.currentTarget.addEventListener("mouseup", this);
    this._timers.set(aEvent.currentTarget, setTimeout((b) => this._openMenu(b), 500, aEvent.currentTarget));
  },

  _clickHandler(aEvent) {
    if (aEvent.button == 0 &&
        aEvent.target == aEvent.currentTarget &&
        !aEvent.currentTarget.open &&
        !aEvent.currentTarget.disabled) {
      let cmdEvent = document.createEvent("xulcommandevent");
      cmdEvent.initCommandEvent("command", true, true, window, 0,
                                aEvent.ctrlKey, aEvent.altKey, aEvent.shiftKey,
                                aEvent.metaKey, null, aEvent.mozInputSource);
      aEvent.currentTarget.dispatchEvent(cmdEvent);

      // This is here to cancel the XUL default event
      // dom.click() triggers a command even if there is a click handler
      // however this can now be prevented with preventDefault().
      aEvent.preventDefault();
    }
  },

  _openMenu(aButton) {
    this._cancelHold(aButton);
    aButton.firstChild.hidden = false;
    aButton.open = true;
  },

  _mouseoutHandler(aEvent) {
    let buttonRect = aEvent.currentTarget.getBoundingClientRect();
    if (aEvent.clientX >= buttonRect.left &&
        aEvent.clientX <= buttonRect.right &&
        aEvent.clientY >= buttonRect.bottom)
      this._openMenu(aEvent.currentTarget);
    else
      this._cancelHold(aEvent.currentTarget);
  },

  _mouseupHandler(aEvent) {
    this._cancelHold(aEvent.currentTarget);
  },

  _cancelHold(aButton) {
    clearTimeout(this._timers.get(aButton));
    aButton.removeEventListener("mouseout", this);
    aButton.removeEventListener("mouseup", this);
  },

  handleEvent(e) {
    switch (e.type) {
      case "mouseout":
        this._mouseoutHandler(e);
        break;
      case "mousedown":
        this._mousedownHandler(e);
        break;
      case "click":
        this._clickHandler(e);
        break;
      case "mouseup":
        this._mouseupHandler(e);
        break;
    }
  },

  remove(aButton) {
    aButton.removeEventListener("mousedown", this, true);
    aButton.removeEventListener("click", this, true);
  },

  add(aElm) {
    this._timers.delete(aElm);

    aElm.addEventListener("mousedown", this, true);
    aElm.addEventListener("click", this, true);
  }
};

const gSessionHistoryObserver = {
  observe(subject, topic, data) {
    if (topic != "browser:purge-session-history")
      return;

    var backCommand = document.getElementById("Browser:Back");
    backCommand.setAttribute("disabled", "true");
    var fwdCommand = document.getElementById("Browser:Forward");
    fwdCommand.setAttribute("disabled", "true");

    // Hide session restore button on about:home
    window.messageManager.broadcastAsyncMessage("Browser:HideSessionRestoreButton");

    // Clear undo history of the URL bar
    gURLBar.editor.transactionManager.clear();
  }
};

const gStoragePressureObserver = {
  _lastNotificationTime: -1,

  observe(subject, topic, data) {
    if (topic != "QuotaManager::StoragePressure" ||
        !Services.prefs.getBoolPref("browser.storageManager.enabled")) {
      return;
    }

    const NOTIFICATION_VALUE = "storage-pressure-notification";
    let notificationBox = document.getElementById("high-priority-global-notificationbox");
    if (notificationBox.getNotificationWithValue(NOTIFICATION_VALUE)) {
      // Do not display the 2nd notification when there is already one
      return;
    }

    // Don't display notification twice within the given interval.
    // This is because
    //   - not to annoy user
    //   - give user some time to clean space.
    //     Even user sees notification and starts acting, it still takes some time.
    const MIN_NOTIFICATION_INTERVAL_MS =
      Services.prefs.getIntPref("browser.storageManager.pressureNotification.minIntervalMS");
    let duration = Date.now() - this._lastNotificationTime;
    if (duration <= MIN_NOTIFICATION_INTERVAL_MS) {
      return;
    }
    this._lastNotificationTime = Date.now();

    const BYTES_IN_GIGABYTE = 1073741824;
    const USAGE_THRESHOLD_BYTES = BYTES_IN_GIGABYTE *
      Services.prefs.getIntPref("browser.storageManager.pressureNotification.usageThresholdGB");
    let msg = "";
    let buttons = [];
    let usage = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
    let prefStrBundle = document.getElementById("bundle_preferences");
    let brandShortName = document.getElementById("bundle_brand").getString("brandShortName");
    buttons.push({
      label: prefStrBundle.getString("spaceAlert.learnMoreButton.label"),
      accessKey: prefStrBundle.getString("spaceAlert.learnMoreButton.accesskey"),
      callback(notificationBar, button) {
        let learnMoreURL = Services.urlFormatter.formatURLPref("app.support.baseURL") + "storage-permissions";
        gBrowser.selectedTab = gBrowser.addTab(learnMoreURL);
      }
    });
    if (usage < USAGE_THRESHOLD_BYTES) {
      // The firefox-used space < 5GB, then warn user to free some disk space.
      // This is because this usage is small and not the main cause for space issue.
      // In order to avoid the bad and wrong impression among users that
      // firefox eats disk space a lot, indicate users to clean up other disk space.
      msg = prefStrBundle.getFormattedString("spaceAlert.under5GB.message", [brandShortName]);
      buttons.push({
        label: prefStrBundle.getString("spaceAlert.under5GB.okButton.label"),
        accessKey: prefStrBundle.getString("spaceAlert.under5GB.okButton.accesskey"),
        callback() {}
      });
    } else {
      // The firefox-used space >= 5GB, then guide users to about:preferences
      // to clear some data stored on firefox by websites.
      let descriptionStringID = "spaceAlert.over5GB.message1";
      let prefButtonLabelStringID = "spaceAlert.over5GB.prefButton.label";
      let prefButtonAccesskeyStringID = "spaceAlert.over5GB.prefButton.accesskey";
      if (AppConstants.platform == "win") {
        descriptionStringID = "spaceAlert.over5GB.messageWin1";
        prefButtonLabelStringID = "spaceAlert.over5GB.prefButtonWin.label";
        prefButtonAccesskeyStringID = "spaceAlert.over5GB.prefButtonWin.accesskey";
      }
      msg = prefStrBundle.getFormattedString(descriptionStringID, [brandShortName]);
      buttons.push({
        label: prefStrBundle.getString(prefButtonLabelStringID),
        accessKey: prefStrBundle.getString(prefButtonAccesskeyStringID),
        callback(notificationBar, button) {
          // The advanced subpanes are only supported in the old organization, which will
          // be removed by bug 1349689.
          let win = gBrowser.ownerGlobal;
          win.openPreferences("privacy-sitedata", { origin: "storagePressure" });
        }
      });
    }

    notificationBox.appendNotification(
      msg, NOTIFICATION_VALUE, null, notificationBox.PRIORITY_WARNING_HIGH, buttons, null);
  }
};

/**
 * Given a starting docshell and a URI to look up, find the docshell the URI
 * is loaded in.
 * @param   aDocument
 *          A document to find instead of using just a URI - this is more specific.
 * @param   aDocShell
 *          The doc shell to start at
 * @param   aSoughtURI
 *          The URI that we're looking for
 * @returns The doc shell that the sought URI is loaded in. Can be in
 *          subframes.
 */
function findChildShell(aDocument, aDocShell, aSoughtURI) {
  aDocShell.QueryInterface(Ci.nsIWebNavigation);
  aDocShell.QueryInterface(Ci.nsIInterfaceRequestor);
  var doc = aDocShell.getInterface(Ci.nsIDOMDocument);
  if ((aDocument && doc == aDocument) ||
      (aSoughtURI && aSoughtURI.spec == aDocShell.currentURI.spec))
    return aDocShell;

  var node = aDocShell.QueryInterface(Ci.nsIDocShellTreeItem);
  for (var i = 0; i < node.childCount; ++i) {
    var docShell = node.getChildAt(i);
    docShell = findChildShell(aDocument, docShell, aSoughtURI);
    if (docShell)
      return docShell;
  }
  return null;
}

var gPopupBlockerObserver = {
  handleEvent(aEvent) {
    if (aEvent.originalTarget != gBrowser.selectedBrowser)
      return;

    gIdentityHandler.refreshIdentityBlock();

    if (!gBrowser.selectedBrowser.blockedPopups ||
        !gBrowser.selectedBrowser.blockedPopups.length) {
      // Hide the notification box (if it's visible).
      let notificationBox = gBrowser.getNotificationBox();
      let notification = notificationBox.getNotificationWithValue("popup-blocked");
      if (notification) {
        notificationBox.removeNotification(notification, false);
      }
      return;
    }

    // Only show the notification again if we've not already shown it. Since
    // notifications are per-browser, we don't need to worry about re-adding
    // it.
    if (!gBrowser.selectedBrowser.blockedPopups.reported) {
      if (Services.prefs.getBoolPref("privacy.popups.showBrowserMessage")) {
        var brandBundle = document.getElementById("bundle_brand");
        var brandShortName = brandBundle.getString("brandShortName");
        var popupCount = gBrowser.selectedBrowser.blockedPopups.length;

        var stringKey = AppConstants.platform == "win"
                        ? "popupWarningButton"
                        : "popupWarningButtonUnix";

        var popupButtonText = gNavigatorBundle.getString(stringKey);
        var popupButtonAccesskey = gNavigatorBundle.getString(stringKey + ".accesskey");

        var messageBase = gNavigatorBundle.getString("popupWarning.message");
        var message = PluralForm.get(popupCount, messageBase)
                                .replace("#1", brandShortName)
                                .replace("#2", popupCount);

        let notificationBox = gBrowser.getNotificationBox();
        let notification = notificationBox.getNotificationWithValue("popup-blocked");
        if (notification) {
          notification.label = message;
        } else {
          var buttons = [{
            label: popupButtonText,
            accessKey: popupButtonAccesskey,
            popup: "blockedPopupOptions",
            callback: null
          }];

          const priority = notificationBox.PRIORITY_WARNING_MEDIUM;
          notificationBox.appendNotification(message, "popup-blocked",
                                             "chrome://browser/skin/notification-icons/popup.svg",
                                             priority, buttons);
        }
      }

      // Record the fact that we've reported this blocked popup, so we don't
      // show it again.
      gBrowser.selectedBrowser.blockedPopups.reported = true;
    }
  },

  toggleAllowPopupsForSite(aEvent) {
    var pm = Services.perms;
    var shouldBlock = aEvent.target.getAttribute("block") == "true";
    var perm = shouldBlock ? pm.DENY_ACTION : pm.ALLOW_ACTION;
    pm.addFromPrincipal(gBrowser.contentPrincipal, "popup", perm);

    if (!shouldBlock)
      this.showAllBlockedPopups(gBrowser.selectedBrowser);

    gBrowser.getNotificationBox().removeCurrentNotification();
  },

  fillPopupList(aEvent) {
    // XXXben - rather than using |currentURI| here, which breaks down on multi-framed sites
    //          we should really walk the blockedPopups and create a list of "allow for <host>"
    //          menuitems for the common subset of hosts present in the report, this will
    //          make us frame-safe.
    //
    // XXXjst - Note that when this is fixed to work with multi-framed sites,
    //          also back out the fix for bug 343772 where
    //          nsGlobalWindow::CheckOpenAllow() was changed to also
    //          check if the top window's location is whitelisted.
    let browser = gBrowser.selectedBrowser;
    var uri = browser.contentPrincipal.URI || browser.currentURI;
    var blockedPopupAllowSite = document.getElementById("blockedPopupAllowSite");
    try {
      blockedPopupAllowSite.removeAttribute("hidden");
      let uriHost = uri.asciiHost ? uri.host : uri.spec;
      var pm = Services.perms;
      if (pm.testPermission(uri, "popup") == pm.ALLOW_ACTION) {
        // Offer an item to block popups for this site, if a whitelist entry exists
        // already for it.
        let blockString = gNavigatorBundle.getFormattedString("popupBlock", [uriHost]);
        blockedPopupAllowSite.setAttribute("label", blockString);
        blockedPopupAllowSite.setAttribute("block", "true");
      } else {
        // Offer an item to allow popups for this site
        let allowString = gNavigatorBundle.getFormattedString("popupAllow", [uriHost]);
        blockedPopupAllowSite.setAttribute("label", allowString);
        blockedPopupAllowSite.removeAttribute("block");
      }
    } catch (e) {
      blockedPopupAllowSite.setAttribute("hidden", "true");
    }

    if (PrivateBrowsingUtils.isWindowPrivate(window))
      blockedPopupAllowSite.setAttribute("disabled", "true");
    else
      blockedPopupAllowSite.removeAttribute("disabled");

    let blockedPopupDontShowMessage = document.getElementById("blockedPopupDontShowMessage");
    let showMessage = Services.prefs.getBoolPref("privacy.popups.showBrowserMessage");
    blockedPopupDontShowMessage.setAttribute("checked", !showMessage);
    blockedPopupDontShowMessage.setAttribute("label", gNavigatorBundle.getString("popupWarningDontShowFromMessage"));

    let blockedPopupsSeparator =
        document.getElementById("blockedPopupsSeparator");
    blockedPopupsSeparator.setAttribute("hidden", true);

    gBrowser.selectedBrowser.retrieveListOfBlockedPopups().then(blockedPopups => {
      let foundUsablePopupURI = false;
      if (blockedPopups) {
        for (let i = 0; i < blockedPopups.length; i++) {
          let blockedPopup = blockedPopups[i];

          // popupWindowURI will be null if the file picker popup is blocked.
          // xxxdz this should make the option say "Show file picker" and do it (Bug 590306)
          if (!blockedPopup.popupWindowURIspec)
            continue;

          var popupURIspec = blockedPopup.popupWindowURIspec;

          // Sometimes the popup URI that we get back from the blockedPopup
          // isn't useful (for instance, netscape.com's popup URI ends up
          // being "http://www.netscape.com", which isn't really the URI of
          // the popup they're trying to show).  This isn't going to be
          // useful to the user, so we won't create a menu item for it.
          if (popupURIspec == "" || popupURIspec == "about:blank" ||
              popupURIspec == "<self>" ||
              popupURIspec == uri.spec)
            continue;

          // Because of the short-circuit above, we may end up in a situation
          // in which we don't have any usable popup addresses to show in
          // the menu, and therefore we shouldn't show the separator.  However,
          // since we got past the short-circuit, we must've found at least
          // one usable popup URI and thus we'll turn on the separator later.
          foundUsablePopupURI = true;

          var menuitem = document.createElement("menuitem");
          var label = gNavigatorBundle.getFormattedString("popupShowPopupPrefix",
                                                          [popupURIspec]);
          menuitem.setAttribute("label", label);
          menuitem.setAttribute("oncommand", "gPopupBlockerObserver.showBlockedPopup(event);");
          menuitem.setAttribute("popupReportIndex", i);
          menuitem.popupReportBrowser = browser;
          aEvent.target.appendChild(menuitem);
        }
      }

      // Show the separator if we added any
      // showable popup addresses to the menu.
      if (foundUsablePopupURI)
        blockedPopupsSeparator.removeAttribute("hidden");
    }, null);
  },

  onPopupHiding(aEvent) {
    let item = aEvent.target.lastChild;
    while (item && item.getAttribute("observes") != "blockedPopupsSeparator") {
      let next = item.previousSibling;
      item.remove();
      item = next;
    }
  },

  showBlockedPopup(aEvent) {
    var target = aEvent.target;
    var popupReportIndex = target.getAttribute("popupReportIndex");
    let browser = target.popupReportBrowser;
    browser.unblockPopup(popupReportIndex);
  },

  showAllBlockedPopups(aBrowser) {
    aBrowser.retrieveListOfBlockedPopups().then(popups => {
      for (let i = 0; i < popups.length; i++) {
        if (popups[i].popupWindowURIspec)
          aBrowser.unblockPopup(i);
      }
    }, null);
  },

  editPopupSettings() {
    let prefillValue = "";
    try {
      // We use contentPrincipal rather than currentURI to get the right
      // value in case this is a data: URI that's inherited off something else.
      // Some principals don't have URIs, so fall back in case URI is not present.
      let principalURI = gBrowser.contentPrincipal.URI || gBrowser.currentURI;
      if (principalURI) {
        // asciiHost conveniently doesn't throw.
        if (principalURI.asciiHost) {
          prefillValue = principalURI.prePath;
        } else {
          // For host-less URIs like file://, prePath would effectively allow
          // popups everywhere on file://. Use the full spec:
          prefillValue = principalURI.spec;
        }
      }
    } catch (e) { }

    var bundlePreferences = document.getElementById("bundle_preferences");
    var params = { blockVisible: false,
                   sessionVisible: false,
                   allowVisible: true,
                   prefilledHost: prefillValue,
                   permissionType: "popup",
                   windowTitle: bundlePreferences.getString("popuppermissionstitle2"),
                   introText: bundlePreferences.getString("popuppermissionstext") };
    var existingWindow = Services.wm.getMostRecentWindow("Browser:Permissions");
    if (existingWindow) {
      existingWindow.initWithParams(params);
      existingWindow.focus();
    } else
      window.openDialog("chrome://browser/content/preferences/permissions.xul",
                        "_blank", "resizable,dialog=no,centerscreen", params);
  },

  dontShowMessage() {
    var showMessage = Services.prefs.getBoolPref("privacy.popups.showBrowserMessage");
    Services.prefs.setBoolPref("privacy.popups.showBrowserMessage", !showMessage);
    gBrowser.getNotificationBox().removeCurrentNotification();
  }
};

function gKeywordURIFixup({ target: browser, data: fixupInfo }) {
  let deserializeURI = (spec) => spec ? makeURI(spec) : null;

  // We get called irrespective of whether we did a keyword search, or
  // whether the original input would be vaguely interpretable as a URL,
  // so figure that out first.
  let alternativeURI = deserializeURI(fixupInfo.fixedURI);
  if (!fixupInfo.keywordProviderName || !alternativeURI || !alternativeURI.host) {
    return;
  }

  let contentPrincipal = browser.contentPrincipal;

  // At this point we're still only just about to load this URI.
  // When the async DNS lookup comes back, we may be in any of these states:
  // 1) still on the previous URI, waiting for the preferredURI (keyword
  //    search) to respond;
  // 2) at the keyword search URI (preferredURI)
  // 3) at some other page because the user stopped navigation.
  // We keep track of the currentURI to detect case (1) in the DNS lookup
  // callback.
  let previousURI = browser.currentURI;
  let preferredURI = deserializeURI(fixupInfo.preferredURI);

  // now swap for a weak ref so we don't hang on to browser needlessly
  // even if the DNS query takes forever
  let weakBrowser = Cu.getWeakReference(browser);
  browser = null;

  // Additionally, we need the host of the parsed url
  let hostName = alternativeURI.displayHost;
  // and the ascii-only host for the pref:
  let asciiHost = alternativeURI.asciiHost;
  // Normalize out a single trailing dot - NB: not using endsWith/lastIndexOf
  // because we need to be sure this last dot is the *only* dot, too.
  // More generally, this is used for the pref and should stay in sync with
  // the code in nsDefaultURIFixup::KeywordURIFixup .
  if (asciiHost.indexOf(".") == asciiHost.length - 1) {
    asciiHost = asciiHost.slice(0, -1);
  }

  let isIPv4Address = host => {
    let parts = host.split(".");
    if (parts.length != 4) {
      return false;
    }
    return parts.every(part => {
      let n = parseInt(part, 10);
      return n >= 0 && n <= 255;
    });
  };
  // Avoid showing fixup information if we're suggesting an IP. Note that
  // decimal representations of IPs are normalized to a 'regular'
  // dot-separated IP address by network code, but that only happens for
  // numbers that don't overflow. Longer numbers do not get normalized,
  // but still work to access IP addresses. So for instance,
  // 1097347366913 (ff7f000001) gets resolved by using the final bytes,
  // making it the same as 7f000001, which is 127.0.0.1 aka localhost.
  // While 2130706433 would get normalized by network, 1097347366913
  // does not, and we have to deal with both cases here:
  if (isIPv4Address(asciiHost) || /^(?:\d+|0x[a-f0-9]+)$/i.test(asciiHost))
    return;

  let onLookupComplete = (request, record, status) => {
    let browserRef = weakBrowser.get();
    if (!Components.isSuccessCode(status) || !browserRef)
      return;

    let currentURI = browserRef.currentURI;
    // If we're in case (3) (see above), don't show an info bar.
    if (!currentURI.equals(previousURI) &&
        !currentURI.equals(preferredURI)) {
      return;
    }

    // show infobar offering to visit the host
    let notificationBox = gBrowser.getNotificationBox(browserRef);
    if (notificationBox.getNotificationWithValue("keyword-uri-fixup"))
      return;

    let message = gNavigatorBundle.getFormattedString(
      "keywordURIFixup.message", [hostName]);
    let yesMessage = gNavigatorBundle.getFormattedString(
      "keywordURIFixup.goTo", [hostName]);

    let buttons = [
      {
        label: yesMessage,
        accessKey: gNavigatorBundle.getString("keywordURIFixup.goTo.accesskey"),
        callback() {
          // Do not set this preference while in private browsing.
          if (!PrivateBrowsingUtils.isWindowPrivate(window)) {
            let pref = "browser.fixup.domainwhitelist." + asciiHost;
            Services.prefs.setBoolPref(pref, true);
          }
          openUILinkIn(alternativeURI.spec, "current");
        }
      },
      {
        label: gNavigatorBundle.getString("keywordURIFixup.dismiss"),
        accessKey: gNavigatorBundle.getString("keywordURIFixup.dismiss.accesskey"),
        callback() {
          let notification = notificationBox.getNotificationWithValue("keyword-uri-fixup");
          notificationBox.removeNotification(notification, true);
        }
      }
    ];
    let notification =
      notificationBox.appendNotification(message, "keyword-uri-fixup", null,
                                         notificationBox.PRIORITY_INFO_HIGH,
                                         buttons);
    notification.persistence = 1;
  };

  try {
    gDNSService.asyncResolve(hostName, 0, onLookupComplete, Services.tm.mainThread,
                             contentPrincipal.originAttributes);
  } catch (ex) {
    // Do nothing if the URL is invalid (we don't want to show a notification in that case).
    if (ex.result != Cr.NS_ERROR_UNKNOWN_HOST) {
      // ... otherwise, report:
      Cu.reportError(ex);
    }
  }
}

function serializeInputStream(aStream) {
  let data = {
    content: NetUtil.readInputStreamToString(aStream, aStream.available()),
  };

  if (aStream instanceof Ci.nsIMIMEInputStream) {
    data.headers = new Map();
    aStream.visitHeaders((name, value) => {
      data.headers.set(name, value);
    });
  }

  return data;
}

/**
 * Handles URIs when we want to deal with them in chrome code rather than pass
 * them down to a content browser. This can avoid unnecessary process switching
 * for the browser.
 * @param aBrowser the browser that is attempting to load the URI
 * @param aUri the nsIURI that is being loaded
 * @returns true if the URI is handled, otherwise false
 */
function handleUriInChrome(aBrowser, aUri) {
  if (aUri.scheme == "file") {
    try {
      let mimeType = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService)
                                              .getTypeFromURI(aUri);
      if (mimeType == "application/x-xpinstall") {
        let systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
        AddonManager.getInstallForURL(aUri.spec, install => {
          AddonManager.installAddonFromWebpage(mimeType, aBrowser, systemPrincipal,
                                               install);
        }, mimeType);
        return true;
      }
    } catch (e) {
      return false;
    }
  }

  return false;
}

// A shared function used by both remote and non-remote browser XBL bindings to
// load a URI or redirect it to the correct process.
function _loadURIWithFlags(browser, uri, params) {
  let tab = gBrowser.getTabForBrowser(browser);
  // Preloaded browsers don't have tabs, so we ignore those.
  if (tab) {
    maybeRecordAbandonmentTelemetry(tab, "newURI");
  }

  if (!uri) {
    uri = "about:blank";
  }
  let triggeringPrincipal = params.triggeringPrincipal || null;
  let flags = params.flags || 0;
  let referrer = params.referrerURI;
  let referrerPolicy = ("referrerPolicy" in params ? params.referrerPolicy :
                        Ci.nsIHttpChannel.REFERRER_POLICY_UNSET);
  let postData = params.postData;

  let currentRemoteType = browser.remoteType;
  let requiredRemoteType;
  try {
    let fixupFlags = Ci.nsIURIFixup.FIXUP_FLAG_NONE;
    if (flags & Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) {
      fixupFlags |= Ci.nsIURIFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP;
    }
    if (flags & Ci.nsIWebNavigation.LOAD_FLAGS_FIXUP_SCHEME_TYPOS) {
      fixupFlags |= Ci.nsIURIFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS;
    }
    let uriObject = Services.uriFixup.createFixupURI(uri, fixupFlags);
    if (handleUriInChrome(browser, uriObject)) {
      // If we've handled the URI in Chrome then just return here.
      return;
    }

    // Note that I had thought that we could set uri = uriObject.spec here, to
    // save on fixup later on, but that changes behavior and breaks tests.
    requiredRemoteType =
      E10SUtils.getRemoteTypeForURIObject(uriObject, gMultiProcessBrowser,
                                          currentRemoteType, browser.currentURI);
  } catch (e) {
    // createFixupURI throws if it can't create a URI. If that's the case then
    // we still need to pass down the uri because docshell handles this case.
    requiredRemoteType = gMultiProcessBrowser ? E10SUtils.DEFAULT_REMOTE_TYPE
                                              : E10SUtils.NOT_REMOTE;
  }

  let mustChangeProcess = requiredRemoteType != currentRemoteType;
  let newFrameloader = false;
  if (browser.getAttribute("preloadedState") === "consumed" && uri != "about:newtab") {
    // Leaving about:newtab from a used to be preloaded browser should run the process
    // selecting algorithm again.
    mustChangeProcess = true;
    newFrameloader = true;
    browser.removeAttribute("preloadedState");
  }

  // !requiredRemoteType means we're loading in the parent/this process.
  if (!requiredRemoteType) {
    browser.inLoadURI = true;
  }
  try {
    if (!mustChangeProcess) {
      if (params.userContextId) {
        browser.webNavigation.setOriginAttributesBeforeLoading({ userContextId: params.userContextId });
      }

      browser.webNavigation.loadURIWithOptions(uri, flags,
                                               referrer, referrerPolicy,
                                               postData, null, null, triggeringPrincipal);
    } else {
      // Check if the current browser is allowed to unload.
      let {permitUnload, timedOut} = browser.permitUnload();
      if (!timedOut && !permitUnload) {
        return;
      }

      if (postData) {
        postData = serializeInputStream(postData);
      }

      let loadParams = {
        uri,
        triggeringPrincipal: triggeringPrincipal
          ? gSerializationHelper.serializeToString(triggeringPrincipal)
          : null,
        flags,
        referrer: referrer ? referrer.spec : null,
        referrerPolicy,
        remoteType: requiredRemoteType,
        postData,
        newFrameloader,
      };

      if (params.userContextId) {
        loadParams.userContextId = params.userContextId;
      }

      LoadInOtherProcess(browser, loadParams);
    }
  } catch (e) {
    // If anything goes wrong when switching remoteness, just switch remoteness
    // manually and load the URI.
    // We might lose history that way but at least the browser loaded a page.
    // This might be necessary if SessionStore wasn't initialized yet i.e.
    // when the homepage is a non-remote page.
    if (mustChangeProcess) {
      Cu.reportError(e);
      gBrowser.updateBrowserRemotenessByURL(browser, uri);

      if (params.userContextId) {
        browser.webNavigation.setOriginAttributesBeforeLoading({ userContextId: params.userContextId });
      }

      browser.webNavigation.loadURIWithOptions(uri, flags, referrer, referrerPolicy,
                                               postData, null, null, triggeringPrincipal);
    } else {
      throw e;
    }
  } finally {
    if (!requiredRemoteType) {
      browser.inLoadURI = false;
    }
  }
}

// Starts a new load in the browser first switching the browser to the correct
// process
function LoadInOtherProcess(browser, loadOptions, historyIndex = -1) {
  let tab = gBrowser.getTabForBrowser(browser);
  SessionStore.navigateAndRestore(tab, loadOptions, historyIndex);
}

// Called when a docshell has attempted to load a page in an incorrect process.
// This function is responsible for loading the page in the correct process.
function RedirectLoad({ target: browser, data }) {
  if (browser.getAttribute("preloadedState") === "consumed") {
    browser.removeAttribute("preloadedState");
    data.loadOptions.newFrameloader = true;
  }

  if (data.loadOptions.reloadInFreshProcess) {
    // Convert the fresh process load option into a large allocation remote type
    // to use common processing from this point.
    data.loadOptions.remoteType = E10SUtils.LARGE_ALLOCATION_REMOTE_TYPE;
    data.loadOptions.newFrameloader = true;
  } else if (browser.remoteType == E10SUtils.LARGE_ALLOCATION_REMOTE_TYPE) {
    // If we're in a Large-Allocation process, we prefer switching back into a
    // normal content process, as that way we can clean up the L-A process.
    data.loadOptions.remoteType =
      E10SUtils.getRemoteTypeForURI(data.loadOptions.uri, gMultiProcessBrowser);
  }

  // We should only start the redirection if the browser window has finished
  // starting up. Otherwise, we should wait until the startup is done.
  if (gBrowserInit.delayedStartupFinished) {
    LoadInOtherProcess(browser, data.loadOptions, data.historyIndex);
  } else {
    let delayedStartupFinished = (subject, topic) => {
      if (topic == "browser-delayed-startup-finished" &&
          subject == window) {
        Services.obs.removeObserver(delayedStartupFinished, topic);
        LoadInOtherProcess(browser, data.loadOptions, data.historyIndex);
      }
    };
    Services.obs.addObserver(delayedStartupFinished,
                             "browser-delayed-startup-finished");
  }
}

if (document.documentElement.getAttribute("windowtype") == "navigator:browser") {
  addEventListener("DOMContentLoaded", function() {
    gBrowserInit.onDOMContentLoaded();
  }, { once: true });
}

let _resolveDelayedStartup;
var delayedStartupPromise = new Promise(resolve => {
  _resolveDelayedStartup = resolve;
});

var gBrowserInit = {
  delayedStartupFinished: false,

  onDOMContentLoaded() {
    gBrowser = window._gBrowser;
    delete window._gBrowser;
    gBrowser.init();

    window.QueryInterface(Ci.nsIInterfaceRequestor)
          .getInterface(nsIWebNavigation)
          .QueryInterface(Ci.nsIDocShellTreeItem).treeOwner
          .QueryInterface(Ci.nsIInterfaceRequestor)
          .getInterface(Ci.nsIXULWindow)
          .XULBrowserWindow = window.XULBrowserWindow;
    window.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow =
      new nsBrowserAccess();

    let initBrowser = gBrowser.initialBrowser;

    // remoteType and sameProcessAsFrameLoader are passed through to
    // updateBrowserRemoteness as part of an options object, which itself defaults
    // to an empty object. So defaulting them to undefined here will cause the
    // default behavior in updateBrowserRemoteness if they don't get set.
    let isRemote = gMultiProcessBrowser;
    let remoteType;
    let sameProcessAsFrameLoader;

    let tabArgument = window.arguments && window.arguments[0];
    if (tabArgument instanceof XULElement) {
      // The window's first argument is a tab if and only if we are swapping tabs.
      // We must set the browser's usercontextid before updateBrowserRemoteness(),
      // so that the newly created remote tab child has the correct usercontextid.
      if (tabArgument.hasAttribute("usercontextid")) {
        initBrowser.setAttribute("usercontextid",
                                 tabArgument.getAttribute("usercontextid"));
      }

      let linkedBrowser = tabArgument.linkedBrowser;
      if (linkedBrowser) {
        remoteType = linkedBrowser.remoteType;
        isRemote = remoteType != E10SUtils.NOT_REMOTE;
        sameProcessAsFrameLoader = linkedBrowser.frameLoader;
      }
      initBrowser.removeAttribute("blank");
    }

    // Set a sane starting width/height for all resolutions on new profiles.
    if (Services.prefs.getBoolPref("privacy.resistFingerprinting")) {
      // When the fingerprinting resistance is enabled, making sure that we don't
      // have a maximum window to interfere with generating rounded window dimensions.
      document.documentElement.setAttribute("sizemode", "normal");
    } else if (!document.documentElement.hasAttribute("width")) {
      const TARGET_WIDTH = 1280;
      const TARGET_HEIGHT = 1040;
      let width = Math.min(screen.availWidth * .9, TARGET_WIDTH);
      let height = Math.min(screen.availHeight * .9, TARGET_HEIGHT);

      document.documentElement.setAttribute("width", width);
      document.documentElement.setAttribute("height", height);

      if (width < TARGET_WIDTH && height < TARGET_HEIGHT) {
        document.documentElement.setAttribute("sizemode", "maximized");
      }
    }

    gBrowser.updateBrowserRemoteness(initBrowser, isRemote, {
      remoteType, sameProcessAsFrameLoader
    });

    new LightweightThemeConsumer(document);
    gUIDensity.init();

    if (AppConstants.CAN_DRAW_IN_TITLEBAR) {
      gDragSpaceObserver.init();
    }

    BrowserSearch.initPlaceHolder();

    // Hack to ensure that the about:home favicon is loaded
    // instantaneously, to avoid flickering and improve perceived performance.
    this._callWithURIToLoad(uriToLoad => {
      if (uriToLoad == "about:home") {
        gBrowser.setIcon(gBrowser.selectedTab, "chrome://branding/content/icon32.png");
      } else if (uriToLoad == "about:privatebrowsing") {
        gBrowser.setIcon(gBrowser.selectedTab, "chrome://browser/skin/privatebrowsing/favicon.svg");
      }
    });

    this._setInitialFocus();
  },

  onLoad() {
    gBrowser.addEventListener("DOMUpdatePageReport", gPopupBlockerObserver);

    Services.obs.addObserver(gPluginHandler.NPAPIPluginCrashed, "plugin-crashed");

    window.addEventListener("AppCommand", HandleAppCommandEvent, true);

    // These routines add message listeners. They must run before
    // loading the frame script to ensure that we don't miss any
    // message sent between when the frame script is loaded and when
    // the listener is registered.
    DOMEventHandler.init();
    gPageStyleMenu.init();
    LanguageDetectionListener.init();
    BrowserOnClick.init();
    FeedHandler.init();
    CompactTheme.init();
    AboutCapabilitiesListener.init();
    TrackingProtection.init();
    CaptivePortalWatcher.init();
    ZoomUI.init(window);

    let mm = window.getGroupMessageManager("browsers");
    mm.loadFrameScript("chrome://browser/content/tab-content.js", true);
    mm.loadFrameScript("chrome://browser/content/content.js", true);
    mm.loadFrameScript("chrome://browser/content/content-UITour.js", true);
    mm.loadFrameScript("chrome://global/content/content-HybridContentTelemetry.js", true);
    mm.loadFrameScript("chrome://global/content/manifestMessages.js", true);

    window.messageManager.addMessageListener("Browser:LoadURI", RedirectLoad);

    if (!gMultiProcessBrowser) {
      // There is a Content:Click message manually sent from content.
      Services.els.addSystemEventListener(gBrowser.mPanelContainer, "click",
        contentAreaClick, true);
    }

    // hook up UI through progress listener
    gBrowser.addProgressListener(window.XULBrowserWindow);
    gBrowser.addTabsProgressListener(window.TabsProgressListener);

    SidebarUI.init();

    // We do this in onload because we want to ensure the button's state
    // doesn't flicker as the window is being shown.
    DownloadsButton.init();

    // Certain kinds of automigration rely on this notification to complete
    // their tasks BEFORE the browser window is shown. SessionStore uses it to
    // restore tabs into windows AFTER important parts like gMultiProcessBrowser
    // have been initialized.
    Services.obs.notifyObservers(window, "browser-window-before-show");

    if (!window.toolbar.visible) {
      // adjust browser UI for popups
      gURLBar.setAttribute("readonly", "true");
    }

    // Misc. inits.
    TabletModeUpdater.init();
    CombinedStopReload.ensureInitialized();
    gPrivateBrowsingUI.init();
    BrowserSearch.init();
    BrowserPageActions.init();
    gAccessibilityServiceIndicator.init();

    if (window.matchMedia("(-moz-os-version: windows-win8)").matches &&
        window.matchMedia("(-moz-windows-default-theme)").matches) {
      let windowFrameColor = new Color(...ChromeUtils.import("resource:///modules/Windows8WindowFrameColor.jsm", {})
                                            .Windows8WindowFrameColor.get());
      // Default to black for foreground text.
      if (!windowFrameColor.isContrastRatioAcceptable(new Color(0, 0, 0))) {
        document.documentElement.setAttribute("darkwindowframe", "true");
      }
    }

    ToolbarIconColor.init();

    gRemoteControl.updateVisualCue(Marionette.running);

    // If we are given a tab to swap in, take care of it before first paint to
    // avoid an about:blank flash.
    let tabToOpen = window.arguments && window.arguments[0];
    if (tabToOpen instanceof XULElement) {
      // Clear the reference to the tab from the arguments array.
      window.arguments[0] = null;

      // Stop the about:blank load
      gBrowser.stop();
      // make sure it has a docshell
      gBrowser.docShell;

      try {
        gBrowser.swapBrowsersAndCloseOther(gBrowser.selectedTab, tabToOpen);
      } catch (e) {
        Cu.reportError(e);
      }
    }

    // Wait until chrome is painted before executing code not critical to making the window visible
    this._boundDelayedStartup = this._delayedStartup.bind(this);
    window.addEventListener("MozAfterPaint", this._boundDelayedStartup);

    if (!PrivateBrowsingUtils.enabled) {
      document.getElementById("Tools:PrivateBrowsing").hidden = true;
      // Setting disabled doesn't disable the shortcut, so we just remove
      // the keybinding.
      document.getElementById("key_privatebrowsing").remove();
    }

    this._loadHandled = true;
  },

  _cancelDelayedStartup() {
    window.removeEventListener("MozAfterPaint", this._boundDelayedStartup);
    this._boundDelayedStartup = null;
  },

  _delayedStartup() {
    let { TelemetryTimestamps } =
      ChromeUtils.import("resource://gre/modules/TelemetryTimestamps.jsm", {});
    TelemetryTimestamps.add("delayedStartupStarted");

    this._cancelDelayedStartup();

    // We need to set the OfflineApps message listeners up before we
    // load homepages, which might need them.
    OfflineApps.init();

    // This pageshow listener needs to be registered before we may call
    // swapBrowsersAndCloseOther() to receive pageshow events fired by that.
    window.messageManager.addMessageListener("PageVisibility:Show", function(message) {
      if (message.target == gBrowser.selectedBrowser) {
        setTimeout(pageShowEventHandlers, 0, message.data.persisted);
      }
    });

    gBrowser.addEventListener("AboutTabCrashedLoad", function(event) {
      let ownerDoc = event.originalTarget;

      if (!ownerDoc.documentURI.startsWith("about:tabcrashed")) {
        return;
      }

      let browser = gBrowser.getBrowserForDocument(event.target);
      // Reset the zoom for the tabcrashed page.
      ZoomManager.setZoomForBrowser(browser, 1);
    }, false, true);

    gBrowser.addEventListener("InsecureLoginFormsStateChange", function() {
      gIdentityHandler.refreshForInsecureLoginForms();
    }, true);

    gBrowser.addEventListener("PermissionStateChange", function() {
      gIdentityHandler.refreshIdentityBlock();
    }, true);

    // Get the service so that it initializes and registers listeners for new
    // tab pages in order to be ready for any early-loading about:newtab pages,
    // e.g., start/home page, command line / startup uris to load, sessionstore
    gAboutNewTabService.QueryInterface(Ci.nsISupports);

    this._handleURIToLoad();

    Services.obs.addObserver(gIdentityHandler, "perm-changed");
    Services.obs.addObserver(gRemoteControl, "remote-active");
    Services.obs.addObserver(gSessionHistoryObserver, "browser:purge-session-history");
    Services.obs.addObserver(gStoragePressureObserver, "QuotaManager::StoragePressure");
    Services.obs.addObserver(gXPInstallObserver, "addon-install-disabled");
    Services.obs.addObserver(gXPInstallObserver, "addon-install-started");
    Services.obs.addObserver(gXPInstallObserver, "addon-install-blocked");
    Services.obs.addObserver(gXPInstallObserver, "addon-install-origin-blocked");
    Services.obs.addObserver(gXPInstallObserver, "addon-install-failed");
    Services.obs.addObserver(gXPInstallObserver, "addon-install-confirmation");
    Services.obs.addObserver(gXPInstallObserver, "addon-install-complete");
    window.messageManager.addMessageListener("Browser:URIFixup", gKeywordURIFixup);

    BrowserOffline.init();
    IndexedDBPromptHelper.init();
    CanvasPermissionPromptHelper.init();
    WebAuthnPromptHelper.init();

    // Initialize the full zoom setting.
    // We do this before the session restore service gets initialized so we can
    // apply full zoom settings to tabs restored by the session restore service.
    FullZoom.init();
    PanelUI.init();

    UpdateUrlbarSearchSplitterState();

    // Enable/Disable auto-hide tabbar
    gBrowser.tabContainer.updateVisibility();

    BookmarkingUI.init();
    BrowserSearch.delayedStartupInit();
    AutoShowBookmarksToolbar.init();

    Services.prefs.addObserver(gHomeButton.prefDomain, gHomeButton);

    var homeButton = document.getElementById("home-button");
    gHomeButton.updateTooltip(homeButton);

    let safeMode = document.getElementById("helpSafeMode");
    if (Services.appinfo.inSafeMode) {
      safeMode.label = safeMode.getAttribute("stoplabel");
      safeMode.accesskey = safeMode.getAttribute("stopaccesskey");
    }

    // BiDi UI
    gBidiUI = isBidiEnabled();
    if (gBidiUI) {
      document.getElementById("documentDirection-separator").hidden = false;
      document.getElementById("documentDirection-swap").hidden = false;
      document.getElementById("textfieldDirection-separator").hidden = false;
      document.getElementById("textfieldDirection-swap").hidden = false;
    }

    // Setup click-and-hold gestures access to the session history
    // menus if global click-and-hold isn't turned on
    if (!getBoolPref("ui.click_hold_context_menus", false))
      SetClickAndHoldHandlers();

    ChromeUtils.import("resource:///modules/UpdateTopLevelContentWindowIDHelper.jsm", {})
      .trackBrowserWindow(window);

    PlacesToolbarHelper.init();

    ctrlTab.readPref();
    Services.prefs.addObserver(ctrlTab.prefName, ctrlTab);

    // The object handling the downloads indicator is initialized here in the
    // delayed startup function, but the actual indicator element is not loaded
    // unless there are downloads to be displayed.
    DownloadsButton.initializeIndicator();

    if (AppConstants.platform != "macosx") {
      updateEditUIVisibility();
      let placesContext = document.getElementById("placesContext");
      placesContext.addEventListener("popupshowing", updateEditUIVisibility);
      placesContext.addEventListener("popuphiding", updateEditUIVisibility);
    }

    LightWeightThemeWebInstaller.init();

    if (Win7Features)
      Win7Features.onOpenWindow();

    FullScreen.init();
    PointerLock.init();

    if (AppConstants.isPlatformAndVersionAtLeast("win", "10")) {
      MenuTouchModeObserver.init();
    }

    if (AppConstants.MOZ_DATA_REPORTING)
      gDataNotificationInfoBar.init();

    if (!AppConstants.MOZILLA_OFFICIAL)
      DevelopmentHelpers.init();

    gExtensionsNotifications.init();

    let wasMinimized = window.windowState == window.STATE_MINIMIZED;
    window.addEventListener("sizemodechange", () => {
      let isMinimized = window.windowState == window.STATE_MINIMIZED;
      if (wasMinimized != isMinimized) {
        wasMinimized = isMinimized;
        UpdatePopupNotificationsVisibility();
      }
    });

    window.addEventListener("mousemove", MousePosTracker);
    window.addEventListener("dragover", MousePosTracker);

    gNavToolbox.addEventListener("customizationstarting", CustomizationHandler);
    gNavToolbox.addEventListener("customizationending", CustomizationHandler);

    if (AppConstants.platform == "linux") {
      let { WindowDraggingElement } =
        ChromeUtils.import("resource://gre/modules/WindowDraggingUtils.jsm", {});
      new WindowDraggingElement(document.getElementById("titlebar"));
    }

    SessionStore.promiseInitialized.then(() => {
      // Bail out if the window has been closed in the meantime.
      if (window.closed) {
        return;
      }

      // Enable the Restore Last Session command if needed
      RestoreLastSessionObserver.init();

      SidebarUI.startDelayedLoad();

      PanicButtonNotifier.init();
    });

    gBrowser.tabContainer.addEventListener("TabSelect", gURLBar);

    gBrowser.tabContainer.addEventListener("TabSelect", function() {
      for (let panel of document.querySelectorAll("panel[tabspecific='true']")) {
        if (panel.state == "open") {
          panel.hidePopup();
        }
      }
    });

    this.delayedStartupFinished = true;

    _resolveDelayedStartup();

    SessionStore.promiseAllWindowsRestored.then(() => {
      this._schedulePerWindowIdleTasks();
      document.documentElement.setAttribute("sessionrestored", "true");
    });

    Services.obs.notifyObservers(window, "browser-delayed-startup-finished");
    TelemetryTimestamps.add("delayedStartupFinished");
  },

  _setInitialFocus() {
    let initiallyFocusedElement = document.commandDispatcher.focusedElement;

    let firstBrowserPaintDeferred = {};
    firstBrowserPaintDeferred.promise = new Promise(resolve => {
      firstBrowserPaintDeferred.resolve = resolve;
    });

    let mm = window.messageManager;
    mm.addMessageListener("Browser:FirstPaint", function onFirstPaint() {
      mm.removeMessageListener("Browser:FirstPaint", onFirstPaint);
      firstBrowserPaintDeferred.resolve();
    });

    let initialBrowser = gBrowser.selectedBrowser;
    mm.addMessageListener("Browser:FirstNonBlankPaint",
                          function onFirstNonBlankPaint() {
      mm.removeMessageListener("Browser:FirstNonBlankPaint", onFirstNonBlankPaint);
      initialBrowser.removeAttribute("blank");
    });

    // To prevent flickering of the urlbar-history-dropmarker in the general
    // case, the urlbar has the 'focused' attribute set by default.
    // If we are not fully sure the urlbar will be focused in this window,
    // we should remove the attribute before first paint.
    let shouldRemoveFocusedAttribute = true;
    this._callWithURIToLoad(uriToLoad => {
      if ((isBlankPageURL(uriToLoad) || uriToLoad == "about:privatebrowsing") &&
          focusAndSelectUrlBar()) {
        shouldRemoveFocusedAttribute = false;
        return;
      }

      if (gBrowser.selectedBrowser.isRemoteBrowser) {
        // If the initial browser is remote, in order to optimize for first paint,
        // we'll defer switching focus to that browser until it has painted.
        firstBrowserPaintDeferred.promise.then(() => {
          // If focus didn't move while we were waiting for first paint, we're okay
          // to move to the browser.
          if (document.commandDispatcher.focusedElement == initiallyFocusedElement) {
            gBrowser.selectedBrowser.focus();
          }
        });
      } else {
        // If the initial browser is not remote, we can focus the browser
        // immediately with no paint performance impact.
        gBrowser.selectedBrowser.focus();
      }
    });
    // Delay removing the attribute using requestAnimationFrame to avoid
    // invalidating styles multiple times in a row if _uriToLoadPromise
    // resolves before first paint.
    if (shouldRemoveFocusedAttribute) {
      window.requestAnimationFrame(() => {
        if (shouldRemoveFocusedAttribute)
          gURLBar.removeAttribute("focused");
      });
    }
  },

  _handleURIToLoad() {
    this._callWithURIToLoad(uriToLoad => {
      if (!uriToLoad || uriToLoad == "about:blank") {
        return;
      }

      // We don't check if uriToLoad is a XULElement because this case has
      // already been handled before first paint, and the argument cleared.
      if (uriToLoad instanceof Ci.nsIArray) {
        let count = uriToLoad.length;
        let specs = [];
        for (let i = 0; i < count; i++) {
          let urisstring = uriToLoad.queryElementAt(i, Ci.nsISupportsString);
          specs.push(urisstring.data);
        }

        // This function throws for certain malformed URIs, so use exception handling
        // so that we don't disrupt startup
        try {
          gBrowser.loadTabs(specs, {
            inBackground: false,
            replace: true,
            triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
          });
        } catch (e) {}
      } else if (window.arguments.length >= 3) {
        // window.arguments[2]: referrer (nsIURI | string)
        //                 [3]: postData (nsIInputStream)
        //                 [4]: allowThirdPartyFixup (bool)
        //                 [5]: referrerPolicy (int)
        //                 [6]: userContextId (int)
        //                 [7]: originPrincipal (nsIPrincipal)
        //                 [8]: triggeringPrincipal (nsIPrincipal)
        let referrerURI = window.arguments[2];
        if (typeof(referrerURI) == "string") {
          try {
            referrerURI = makeURI(referrerURI);
          } catch (e) {
            referrerURI = null;
          }
        }
        let referrerPolicy = (window.arguments[5] != undefined ?
            window.arguments[5] : Ci.nsIHttpChannel.REFERRER_POLICY_UNSET);
        let userContextId = (window.arguments[6] != undefined ?
            window.arguments[6] : Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID);
        loadURI(uriToLoad, referrerURI, window.arguments[3] || null,
                window.arguments[4] || false, referrerPolicy, userContextId,
                // pass the origin principal (if any) and force its use to create
                // an initial about:blank viewer if present:
                window.arguments[7], !!window.arguments[7], window.arguments[8]);
        window.focus();
      } else {
        // Note: loadOneOrMoreURIs *must not* be called if window.arguments.length >= 3.
        // Such callers expect that window.arguments[0] is handled as a single URI.
        loadOneOrMoreURIs(uriToLoad, Services.scriptSecurityManager.getSystemPrincipal());
      }
    });
  },

  /**
   * Use this function as an entry point to schedule tasks that
   * need to run once per window after startup, and can be scheduled
   * by using an idle callback.
   *
   * The functions scheduled here will fire from idle callbacks
   * once every window has finished being restored by session
   * restore, and after the equivalent only-once tasks
   * have run (from _scheduleStartupIdleTasks in nsBrowserGlue.js).
   */
  _schedulePerWindowIdleTasks() {
    // Bail out if the window has been closed in the meantime.
    if (window.closed) {
      return;
    }

    function scheduleIdleTask(func, options) {
      requestIdleCallback(function idleTaskRunner() {
        if (!window.closed) {
          func();
        }
      }, options);
    }

    scheduleIdleTask(() => {
      // Initialize the Sync UI
      gSync.init();
    });

    scheduleIdleTask(() => {
      CombinedStopReload.startAnimationPrefMonitoring();
    });

    scheduleIdleTask(() => {
      // setup simple gestures support
      gGestureSupport.init(true);

      // setup history swipe animation
      gHistorySwipeAnimation.init();
    });

    scheduleIdleTask(() => {
      gBrowserThumbnails.init();
    });

    scheduleIdleTask(() => {
      // Initialize the download manager some time after the app starts so that
      // auto-resume downloads begin (such as after crashing or quitting with
      // active downloads) and speeds up the first-load of the download manager UI.
      // If the user manually opens the download manager before the timeout, the
      // downloads will start right away, and initializing again won't hurt.
      try {
        DownloadsCommon.initializeAllDataLinks();
        ChromeUtils.import("resource:///modules/DownloadsTaskbar.jsm", {})
          .DownloadsTaskbar.registerIndicator(window);
      } catch (ex) {
        Cu.reportError(ex);
      }
    }, {timeout: 10000});
  },

  // Returns the URI(s) to load at startup if it is immediately known, or a
  // promise resolving to the URI to load.
  get _uriToLoadPromise() {
    delete this._uriToLoadPromise;
    return this._uriToLoadPromise = function() {
      // window.arguments[0]: URI to load (string), or an nsIArray of
      //                      nsISupportsStrings to load, or a xul:tab of
      //                      a tabbrowser, which will be replaced by this
      //                      window (for this case, all other arguments are
      //                      ignored).
      if (!window.arguments || !window.arguments[0]) {
        return null;
      }

      let uri = window.arguments[0];
      let defaultArgs = Cc["@mozilla.org/browser/clh;1"]
                          .getService(Ci.nsIBrowserHandler)
                          .defaultArgs;

      // If the given URI is different from the homepage, we want to load it.
      if (uri != defaultArgs) {
        return uri;
      }

      // The URI appears to be the the homepage. We want to load it only if
      // session restore isn't about to override the homepage.
      let willOverride = SessionStartup.willOverrideHomepage;
      if (typeof willOverride == "boolean") {
        return willOverride ? null : uri;
      }
      return willOverride.then(willOverrideHomepage =>
                                 willOverrideHomepage ? null : uri);
    }();
  },

  // Calls the given callback with the URI to load at startup.
  // Synchronously if possible, or after _uriToLoadPromise resolves otherwise.
  _callWithURIToLoad(callback) {
    let uriToLoad = this._uriToLoadPromise;
    if (!uriToLoad || !uriToLoad.then)
      callback(uriToLoad);
    else
      uriToLoad.then(callback);
  },

  onUnload() {
    gUIDensity.uninit();

    if (AppConstants.CAN_DRAW_IN_TITLEBAR) {
      gDragSpaceObserver.uninit();
    }

    TabsInTitlebar.uninit();

    // In certain scenarios it's possible for unload to be fired before onload,
    // (e.g. if the window is being closed after browser.js loads but before the
    // load completes). In that case, there's nothing to do here.
    if (!this._loadHandled)
      return;

    // First clean up services initialized in gBrowserInit.onLoad (or those whose
    // uninit methods don't depend on the services having been initialized).

    CombinedStopReload.uninit();

    gGestureSupport.init(false);

    gHistorySwipeAnimation.uninit();

    FullScreen.uninit();

    gSync.uninit();

    gExtensionsNotifications.uninit();

    Services.obs.removeObserver(gPluginHandler.NPAPIPluginCrashed, "plugin-crashed");

    try {
      gBrowser.removeProgressListener(window.XULBrowserWindow);
      gBrowser.removeTabsProgressListener(window.TabsProgressListener);
    } catch (ex) {
    }

    PlacesToolbarHelper.uninit();

    BookmarkingUI.uninit();

    ToolbarIconColor.uninit();

    TabletModeUpdater.uninit();

    gTabletModePageCounter.finish();

    BrowserOnClick.uninit();

    FeedHandler.uninit();

    CompactTheme.uninit();

    AboutCapabilitiesListener.uninit();

    TrackingProtection.uninit();

    CaptivePortalWatcher.uninit();

    SidebarUI.uninit();

    DownloadsButton.uninit();

    gAccessibilityServiceIndicator.uninit();

    LanguagePrompt.uninit();

    BrowserSearch.uninit();

    // Now either cancel delayedStartup, or clean up the services initialized from
    // it.
    if (this._boundDelayedStartup) {
      this._cancelDelayedStartup();
    } else {
      if (Win7Features)
        Win7Features.onCloseWindow();

      Services.prefs.removeObserver(ctrlTab.prefName, ctrlTab);
      ctrlTab.uninit();
      gBrowserThumbnails.uninit();
      FullZoom.destroy();

      Services.obs.removeObserver(gIdentityHandler, "perm-changed");
      Services.obs.removeObserver(gRemoteControl, "remote-active");
      Services.obs.removeObserver(gSessionHistoryObserver, "browser:purge-session-history");
      Services.obs.removeObserver(gStoragePressureObserver, "QuotaManager::StoragePressure");
      Services.obs.removeObserver(gXPInstallObserver, "addon-install-disabled");
      Services.obs.removeObserver(gXPInstallObserver, "addon-install-started");
      Services.obs.removeObserver(gXPInstallObserver, "addon-install-blocked");
      Services.obs.removeObserver(gXPInstallObserver, "addon-install-origin-blocked");
      Services.obs.removeObserver(gXPInstallObserver, "addon-install-failed");
      Services.obs.removeObserver(gXPInstallObserver, "addon-install-confirmation");
      Services.obs.removeObserver(gXPInstallObserver, "addon-install-complete");
      window.messageManager.removeMessageListener("Browser:URIFixup", gKeywordURIFixup);
      window.messageManager.removeMessageListener("Browser:LoadURI", RedirectLoad);

      try {
        Services.prefs.removeObserver(gHomeButton.prefDomain, gHomeButton);
      } catch (ex) {
        Cu.reportError(ex);
      }

      if (AppConstants.isPlatformAndVersionAtLeast("win", "10")) {
        MenuTouchModeObserver.uninit();
      }
      BrowserOffline.uninit();
      IndexedDBPromptHelper.uninit();
      CanvasPermissionPromptHelper.uninit();
      WebAuthnPromptHelper.uninit();
      PanelUI.uninit();
      AutoShowBookmarksToolbar.uninit();
    }

    // Final window teardown, do this last.
    gBrowser.destroy();
    window.XULBrowserWindow = null;
    window.QueryInterface(Ci.nsIInterfaceRequestor)
          .getInterface(Ci.nsIWebNavigation)
          .QueryInterface(Ci.nsIDocShellTreeItem).treeOwner
          .QueryInterface(Ci.nsIInterfaceRequestor)
          .getInterface(Ci.nsIXULWindow)
          .XULBrowserWindow = null;
    window.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow = null;
  },
};

if (AppConstants.platform == "macosx") {
  // nonBrowserWindowStartup(), nonBrowserWindowDelayedStartup(), and
  // nonBrowserWindowShutdown() are used for non-browser windows in
  // macBrowserOverlay
  gBrowserInit.nonBrowserWindowStartup = function() {
    // Disable inappropriate commands / submenus
    var disabledItems = ["Browser:SavePage",
                         "Browser:SendLink", "cmd_pageSetup", "cmd_print", "cmd_find", "cmd_findAgain",
                         "viewToolbarsMenu", "viewSidebarMenuMenu", "Browser:Reload",
                         "viewFullZoomMenu", "pageStyleMenu", "charsetMenu", "View:PageSource", "View:FullScreen",
                         "viewHistorySidebar", "Browser:AddBookmarkAs", "Browser:BookmarkAllTabs",
                         "View:PageInfo"];
    var element;

    for (let disabledItem of disabledItems) {
      element = document.getElementById(disabledItem);
      if (element)
        element.setAttribute("disabled", "true");
    }

    // If no windows are active (i.e. we're the hidden window), disable the close, minimize
    // and zoom menu commands as well
    if (window.location.href == "chrome://browser/content/hiddenWindow.xul") {
      var hiddenWindowDisabledItems = ["cmd_close", "minimizeWindow", "zoomWindow"];
      for (let hiddenWindowDisabledItem of hiddenWindowDisabledItems) {
        element = document.getElementById(hiddenWindowDisabledItem);
        if (element)
          element.setAttribute("disabled", "true");
      }

      // also hide the window-list separator
      element = document.getElementById("sep-window-list");
      element.setAttribute("hidden", "true");

      // Setup the dock menu.
      let dockMenuElement = document.getElementById("menu_mac_dockmenu");
      if (dockMenuElement != null) {
        let nativeMenu = Cc["@mozilla.org/widget/standalonenativemenu;1"]
                         .createInstance(Ci.nsIStandaloneNativeMenu);

        try {
          nativeMenu.init(dockMenuElement);

          let dockSupport = Cc["@mozilla.org/widget/macdocksupport;1"]
                            .getService(Ci.nsIMacDockSupport);
          dockSupport.dockMenu = nativeMenu;
        } catch (e) {
        }
      }
    }

    if (PrivateBrowsingUtils.permanentPrivateBrowsing) {
      document.getElementById("macDockMenuNewWindow").hidden = true;
    }
    if (!PrivateBrowsingUtils.enabled) {
      document.getElementById("macDockMenuNewPrivateWindow").hidden = true;
    }

    this._delayedStartupTimeoutId = setTimeout(this.nonBrowserWindowDelayedStartup.bind(this), 0);
  };

  gBrowserInit.nonBrowserWindowDelayedStartup = function() {
    this._delayedStartupTimeoutId = null;

    // initialise the offline listener
    BrowserOffline.init();

    // initialize the private browsing UI
    gPrivateBrowsingUI.init();

  };

  gBrowserInit.nonBrowserWindowShutdown = function() {
    let dockSupport = Cc["@mozilla.org/widget/macdocksupport;1"]
                      .getService(Ci.nsIMacDockSupport);
    dockSupport.dockMenu = null;

    // If nonBrowserWindowDelayedStartup hasn't run yet, we have no work to do -
    // just cancel the pending timeout and return;
    if (this._delayedStartupTimeoutId) {
      clearTimeout(this._delayedStartupTimeoutId);
      return;
    }

    BrowserOffline.uninit();
  };
}

function HandleAppCommandEvent(evt) {
  switch (evt.command) {
  case "Back":
    BrowserBack();
    break;
  case "Forward":
    BrowserForward();
    break;
  case "Reload":
    BrowserReloadSkipCache();
    break;
  case "Stop":
    if (XULBrowserWindow.stopCommand.getAttribute("disabled") != "true")
      BrowserStop();
    break;
  case "Search":
    BrowserSearch.webSearch();
    break;
  case "Bookmarks":
    SidebarUI.toggle("viewBookmarksSidebar");
    break;
  case "Home":
    BrowserHome();
    break;
  case "New":
    BrowserOpenTab();
    break;
  case "Close":
    BrowserCloseTabOrWindow();
    break;
  case "Find":
    gFindBar.onFindCommand();
    break;
  case "Help":
    openHelpLink("firefox-help");
    break;
  case "Open":
    BrowserOpenFileWindow();
    break;
  case "Print":
    PrintUtils.printWindow(gBrowser.selectedBrowser.outerWindowID,
                           gBrowser.selectedBrowser);
    break;
  case "Save":
    saveBrowser(gBrowser.selectedBrowser);
    break;
  case "SendMail":
    MailIntegration.sendLinkForBrowser(gBrowser.selectedBrowser);
    break;
  default:
    return;
  }
  evt.stopPropagation();
  evt.preventDefault();
}

function maybeRecordAbandonmentTelemetry(tab, type) {
  if (!tab.hasAttribute("busy")) {
    return;
  }

  let histogram = Services.telemetry
                          .getHistogramById("BUSY_TAB_ABANDONED");
  histogram.add(type);
}

function gotoHistoryIndex(aEvent) {
  let index = aEvent.target.getAttribute("index");
  if (!index)
    return false;

  let where = whereToOpenLink(aEvent);

  if (where == "current") {
    // Normal click. Go there in the current tab and update session history.

    try {
      maybeRecordAbandonmentTelemetry(gBrowser.selectedTab,
                                      "historyNavigation");
      gBrowser.gotoIndex(index);
    } catch (ex) {
      return false;
    }
    return true;
  }
  // Modified click. Go there in a new tab/window.

  let historyindex = aEvent.target.getAttribute("historyindex");
  duplicateTabIn(gBrowser.selectedTab, where, Number(historyindex));
  return true;
}

function BrowserForward(aEvent) {
  let where = whereToOpenLink(aEvent, false, true);

  if (where == "current") {
    try {
      maybeRecordAbandonmentTelemetry(gBrowser.selectedTab, "forward");
      gBrowser.goForward();
    } catch (ex) {
    }
  } else {
    duplicateTabIn(gBrowser.selectedTab, where, 1);
  }
}

function BrowserBack(aEvent) {
  let where = whereToOpenLink(aEvent, false, true);

  if (where == "current") {
    try {
      maybeRecordAbandonmentTelemetry(gBrowser.selectedTab, "back");
      gBrowser.goBack();
    } catch (ex) {
    }
  } else {
    duplicateTabIn(gBrowser.selectedTab, where, -1);
  }
}

function BrowserHandleBackspace() {
  switch (Services.prefs.getIntPref("browser.backspace_action")) {
  case 0:
    BrowserBack();
    break;
  case 1:
    goDoCommand("cmd_scrollPageUp");
    break;
  }
}

function BrowserHandleShiftBackspace() {
  switch (Services.prefs.getIntPref("browser.backspace_action")) {
  case 0:
    BrowserForward();
    break;
  case 1:
    goDoCommand("cmd_scrollPageDown");
    break;
  }
}

function BrowserStop() {
  const stopFlags = nsIWebNavigation.STOP_ALL;
  maybeRecordAbandonmentTelemetry(gBrowser.selectedTab, "stop");
  gBrowser.webNavigation.stop(stopFlags);
}

function BrowserReloadOrDuplicate(aEvent) {
  let metaKeyPressed = AppConstants.platform == "macosx"
                       ? aEvent.metaKey
                       : aEvent.ctrlKey;
  var backgroundTabModifier = aEvent.button == 1 || metaKeyPressed;

  if (aEvent.shiftKey && !backgroundTabModifier) {
    BrowserReloadSkipCache();
    return;
  }

  let where = whereToOpenLink(aEvent, false, true);
  if (where == "current")
    BrowserReload();
  else
    duplicateTabIn(gBrowser.selectedTab, where);
}

function BrowserReload() {
  if (gBrowser.currentURI.schemeIs("view-source")) {
    // Bug 1167797: For view source, we always skip the cache
    return BrowserReloadSkipCache();
  }
  const reloadFlags = nsIWebNavigation.LOAD_FLAGS_NONE;
  BrowserReloadWithFlags(reloadFlags);
}

function BrowserReloadSkipCache() {
  // Bypass proxy and cache.
  const reloadFlags = nsIWebNavigation.LOAD_FLAGS_BYPASS_PROXY | nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE;
  BrowserReloadWithFlags(reloadFlags);
}

var BrowserHome = BrowserGoHome;
function BrowserGoHome(aEvent) {
  if (aEvent && "button" in aEvent &&
      aEvent.button == 2) // right-click: do nothing
    return;

  var homePage = gHomeButton.getHomePage();
  var where = whereToOpenLink(aEvent, false, true);
  var urls;

  // Home page should open in a new tab when current tab is an app tab
  if (where == "current" &&
      gBrowser &&
      gBrowser.selectedTab.pinned)
    where = "tab";

  // openUILinkIn in utilityOverlay.js doesn't handle loading multiple pages
  switch (where) {
  case "current":
    loadOneOrMoreURIs(homePage, Services.scriptSecurityManager.getSystemPrincipal());
    gBrowser.selectedBrowser.focus();
    break;
  case "tabshifted":
  case "tab":
    urls = homePage.split("|");
    var loadInBackground = getBoolPref("browser.tabs.loadBookmarksInBackground", false);
    gBrowser.loadTabs(urls, {
      inBackground: loadInBackground,
      triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
    });
    break;
  case "window":
    OpenBrowserWindow();
    break;
  }
}

function loadOneOrMoreURIs(aURIString, aTriggeringPrincipal) {
  // we're not a browser window, pass the URI string to a new browser window
  if (window.location.href != getBrowserURL()) {
    window.openDialog(getBrowserURL(), "_blank", "all,dialog=no", aURIString);
    return;
  }

  // This function throws for certain malformed URIs, so use exception handling
  // so that we don't disrupt startup
  try {
    gBrowser.loadTabs(aURIString.split("|"), {
      inBackground: false,
      replace: true,
      triggeringPrincipal: aTriggeringPrincipal,
    });
  } catch (e) {
  }
}

/**
 * Focuses the location bar input field and selects its contents.
 *
 * @param [optional] userInitiatedFocus
 *        Whether this focus is caused by an user interaction whose intention
 *        was to use the location bar. For example, using a shortcut to go to
 *        the location bar, or a contextual menu to search from it.
 *        The default is false and should be used in all those cases where the
 *        code focuses the location bar but that's not the primary user
 *        intention, like when opening a new tab.
 */
function focusAndSelectUrlBar(userInitiatedFocus = false) {
  // In customize mode, the url bar is disabled. If a new tab is opened or the
  // user switches to a different tab, this function gets called before we've
  // finished leaving customize mode, and the url bar will still be disabled.
  // We can't focus it when it's disabled, so we need to re-run ourselves when
  // we've finished leaving customize mode.
  if (CustomizationHandler.isExitingCustomizeMode) {
    gNavToolbox.addEventListener("aftercustomization", function() {
      focusAndSelectUrlBar(userInitiatedFocus);
    }, {once: true});

    return true;
  }

  if (gURLBar) {
    if (window.fullScreen)
      FullScreen.showNavToolbox();

    gURLBar.userInitiatedFocus = userInitiatedFocus;
    gURLBar.select();
    gURLBar.userInitiatedFocus = false;
    if (document.activeElement == gURLBar.inputField)
      return true;
  }
  return false;
}

function openLocation() {
  if (focusAndSelectUrlBar(true))
    return;

  if (window.location.href != getBrowserURL()) {
    var win = getTopWin();
    if (win) {
      // If there's an open browser window, it should handle this command
      win.focus();
      win.openLocation();
    } else {
      // If there are no open browser windows, open a new one
      window.openDialog("chrome://browser/content/", "_blank",
                        "chrome,all,dialog=no", BROWSER_NEW_TAB_URL);
    }
  }
}

function BrowserOpenTab(event) {
  // A notification intended to be useful for modular peformance tracking
  // starting as close as is reasonably possible to the time when the user
  // expressed the intent to open a new tab.  Since there are a lot of
  // entry points, this won't catch every single tab created, but most
  // initiated by the user should go through here.
  Services.obs.notifyObservers(null, "browser-open-newtab-start");

  let where = "tab";
  let relatedToCurrent = false;

  if (event) {
    where = whereToOpenLink(event, false, true);

    switch (where) {
      case "tab":
      case "tabshifted":
        // When accel-click or middle-click are used, open the new tab as
        // related to the current tab.
        relatedToCurrent = true;
        break;
      case "current":
        where = "tab";
        break;
    }
  }

  openUILinkIn(BROWSER_NEW_TAB_URL, where, { relatedToCurrent });
}

var gLastOpenDirectory = {
  _lastDir: null,
  get path() {
    if (!this._lastDir || !this._lastDir.exists()) {
      try {
        this._lastDir = Services.prefs.getComplexValue("browser.open.lastDir",
                                                       Ci.nsIFile);
        if (!this._lastDir.exists())
          this._lastDir = null;
      } catch (e) {}
    }
    return this._lastDir;
  },
  set path(val) {
    try {
      if (!val || !val.isDirectory())
        return;
    } catch (e) {
      return;
    }
    this._lastDir = val.clone();

    // Don't save the last open directory pref inside the Private Browsing mode
    if (!PrivateBrowsingUtils.isWindowPrivate(window))
      Services.prefs.setComplexValue("browser.open.lastDir", Ci.nsIFile,
                                     this._lastDir);
  },
  reset() {
    this._lastDir = null;
  }
};

function BrowserOpenFileWindow() {
  // Get filepicker component.
  try {
    const nsIFilePicker = Ci.nsIFilePicker;
    let fp = Cc["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
    let fpCallback = function fpCallback_done(aResult) {
      if (aResult == nsIFilePicker.returnOK) {
        try {
          if (fp.file) {
            gLastOpenDirectory.path =
              fp.file.parent.QueryInterface(Ci.nsIFile);
          }
        } catch (ex) {
        }
        openUILinkIn(fp.fileURL.spec, "current");
      }
    };

    fp.init(window, gNavigatorBundle.getString("openFile"),
            nsIFilePicker.modeOpen);
    fp.appendFilters(nsIFilePicker.filterAll | nsIFilePicker.filterText |
                     nsIFilePicker.filterImages | nsIFilePicker.filterXML |
                     nsIFilePicker.filterHTML);
    fp.displayDirectory = gLastOpenDirectory.path;
    fp.open(fpCallback);
  } catch (ex) {
  }
}

function BrowserCloseTabOrWindow(event) {
  // If we're not a browser window, just close the window.
  if (window.location.href != getBrowserURL()) {
    closeWindow(true);
    return;
  }

  // Keyboard shortcuts that would close a tab that is pinned select the first
  // unpinned tab instead.
  if (event &&
      (event.ctrlKey || event.metaKey || event.altKey) &&
      gBrowser.selectedTab.pinned) {
    if (gBrowser.visibleTabs.length > gBrowser._numPinnedTabs) {
      gBrowser.tabContainer.selectedIndex = gBrowser._numPinnedTabs;
    }
    return;
  }

  // If the current tab is the last one, this will close the window.
  gBrowser.removeCurrentTab({animate: true});
}

function BrowserTryToCloseWindow() {
  if (WindowIsClosing())
    window.close(); // WindowIsClosing does all the necessary checks
}

function loadURI(uri, referrer, postData, allowThirdPartyFixup, referrerPolicy,
                 userContextId, originPrincipal, forceAboutBlankViewerInCurrent,
                 triggeringPrincipal) {
  try {
    openLinkIn(uri, "current",
               { referrerURI: referrer,
                 referrerPolicy,
                 postData,
                 allowThirdPartyFixup,
                 userContextId,
                 originPrincipal,
                 triggeringPrincipal,
                 forceAboutBlankViewerInCurrent,
               });
  } catch (e) {}
}

/**
 * Given a string, will generate a more appropriate urlbar value if a Places
 * keyword or a search alias is found at the beginning of it.
 *
 * @param url
 *        A string that may begin with a keyword or an alias.
 *
 * @return {Promise}
 * @resolves { url, postData, mayInheritPrincipal }. If it's not possible
 *           to discern a keyword or an alias, url will be the input string.
 */
function getShortcutOrURIAndPostData(url, callback = null) {
  if (callback) {
    Deprecated.warning("Please use the Promise returned by " +
                       "getShortcutOrURIAndPostData() instead of passing a " +
                       "callback",
                       "https://bugzilla.mozilla.org/show_bug.cgi?id=1100294");
  }
  return (async function() {
    let mayInheritPrincipal = false;
    let postData = null;
    // Split on the first whitespace.
    let [keyword, param = ""] = url.trim().split(/\s(.+)/, 2);

    if (!keyword) {
      return { url, postData, mayInheritPrincipal };
    }

    let engine = Services.search.getEngineByAlias(keyword);
    if (engine) {
      let submission = engine.getSubmission(param, null, "keyword");
      return { url: submission.uri.spec,
               postData: submission.postData,
               mayInheritPrincipal };
    }

    // A corrupt Places database could make this throw, breaking navigation
    // from the location bar.
    let entry = null;
    try {
      entry = await PlacesUtils.keywords.fetch(keyword);
    } catch (ex) {
      Cu.reportError(`Unable to fetch Places keyword "${keyword}": ${ex}`);
    }
    if (!entry || !entry.url) {
      // This is not a Places keyword.
      return { url, postData, mayInheritPrincipal };
    }

    try {
      [url, postData] =
        await BrowserUtils.parseUrlAndPostData(entry.url.href,
                                               entry.postData,
                                               param);
      if (postData) {
        postData = getPostDataStream(postData);
      }

      // Since this URL came from a bookmark, it's safe to let it inherit the
      // current document's principal.
      mayInheritPrincipal = true;
    } catch (ex) {
      // It was not possible to bind the param, just use the original url value.
    }

    return { url, postData, mayInheritPrincipal };
  })().then(data => {
    if (callback) {
      callback(data);
    }
    return data;
  });
}

function getPostDataStream(aPostDataString,
                           aType = "application/x-www-form-urlencoded") {
  let dataStream = Cc["@mozilla.org/io/string-input-stream;1"]
                     .createInstance(Ci.nsIStringInputStream);
  dataStream.data = aPostDataString;

  let mimeStream = Cc["@mozilla.org/network/mime-input-stream;1"]
                     .createInstance(Ci.nsIMIMEInputStream);
  mimeStream.addHeader("Content-Type", aType);
  mimeStream.setData(dataStream);
  return mimeStream.QueryInterface(Ci.nsIInputStream);
}

function getLoadContext() {
  return window.QueryInterface(Ci.nsIInterfaceRequestor)
               .getInterface(Ci.nsIWebNavigation)
               .QueryInterface(Ci.nsILoadContext);
}

function readFromClipboard() {
  var url;

  try {
    // Create transferable that will transfer the text.
    var trans = Cc["@mozilla.org/widget/transferable;1"]
                  .createInstance(Ci.nsITransferable);
    trans.init(getLoadContext());

    trans.addDataFlavor("text/unicode");

    // If available, use selection clipboard, otherwise global one
    if (Services.clipboard.supportsSelectionClipboard())
      Services.clipboard.getData(trans, Services.clipboard.kSelectionClipboard);
    else
      Services.clipboard.getData(trans, Services.clipboard.kGlobalClipboard);

    var data = {};
    var dataLen = {};
    trans.getTransferData("text/unicode", data, dataLen);

    if (data) {
      data = data.value.QueryInterface(Ci.nsISupportsString);
      url = data.data.substring(0, dataLen.value / 2);
    }
  } catch (ex) {
  }

  return url;
}

/**
 * Open the View Source dialog.
 *
 * @param aArgsOrDocument
 *        Either an object or a Document. Passing a Document is deprecated,
 *        and is not supported with e10s. This function will throw if
 *        aArgsOrDocument is a CPOW.
 *
 *        If aArgsOrDocument is an object, that object can take the
 *        following properties:
 *
 *        URL (required):
 *          A string URL for the page we'd like to view the source of.
 *        browser (optional):
 *          The browser containing the document that we would like to view the
 *          source of. This is required if outerWindowID is passed.
 *        outerWindowID (optional):
 *          The outerWindowID of the content window containing the document that
 *          we want to view the source of. You only need to provide this if you
 *          want to attempt to retrieve the document source from the network
 *          cache.
 *        lineNumber (optional):
 *          The line number to focus on once the source is loaded.
 */
async function BrowserViewSourceOfDocument(aArgsOrDocument) {
  let args;

  if (aArgsOrDocument instanceof Document) {
    let doc = aArgsOrDocument;
    // Deprecated API - callers should pass args object instead.
    if (Cu.isCrossProcessWrapper(doc)) {
      throw new Error("BrowserViewSourceOfDocument cannot accept a CPOW " +
                      "as a document.");
    }

    let requestor = doc.defaultView
                       .QueryInterface(Ci.nsIInterfaceRequestor);
    let browser = requestor.getInterface(Ci.nsIWebNavigation)
                           .QueryInterface(Ci.nsIDocShell)
                           .chromeEventHandler;
    let outerWindowID = requestor.getInterface(Ci.nsIDOMWindowUtils)
                                 .outerWindowID;
    let URL = browser.currentURI.spec;
    args = { browser, outerWindowID, URL };
  } else {
    args = aArgsOrDocument;
  }

  // Check if external view source is enabled.  If so, try it.  If it fails,
  // fallback to internal view source.
  if (Services.prefs.getBoolPref("view_source.editor.external")) {
    try {
      await top.gViewSourceUtils.openInExternalEditor(args);
      return;
    } catch (data) {}
  }

  let tabBrowser = gBrowser;
  let preferredRemoteType;
  if (args.browser) {
    preferredRemoteType = args.browser.remoteType;
  } else {
    if (!tabBrowser) {
      throw new Error("BrowserViewSourceOfDocument should be passed the " +
                      "subject browser if called from a window without " +
                      "gBrowser defined.");
    }
    // Some internal URLs (such as specific chrome: and about: URLs that are
    // not yet remote ready) cannot be loaded in a remote browser.  View
    // source in tab expects the new view source browser's remoteness to match
    // that of the original URL, so disable remoteness if necessary for this
    // URL.
    preferredRemoteType =
      E10SUtils.getRemoteTypeForURI(args.URL, gMultiProcessBrowser);
  }

  // In the case of popups, we need to find a non-popup browser window.
  if (!tabBrowser || !window.toolbar.visible) {
    // This returns only non-popup browser windows by default.
    let browserWindow = RecentWindow.getMostRecentBrowserWindow();
    tabBrowser = browserWindow.gBrowser;
  }

  // `viewSourceInBrowser` will load the source content from the page
  // descriptor for the tab (when possible) or fallback to the network if
  // that fails.  Either way, the view source module will manage the tab's
  // location, so use "about:blank" here to avoid unnecessary redundant
  // requests.
  let tab = tabBrowser.loadOneTab("about:blank", {
    relatedToCurrent: true,
    inBackground: false,
    preferredRemoteType,
    sameProcessAsFrameLoader: args.browser ? args.browser.frameLoader : null,
    triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
  });
  args.viewSourceBrowser = tabBrowser.getBrowserForTab(tab);
  top.gViewSourceUtils.viewSourceInBrowser(args);
}

/**
 * Opens the View Source dialog for the source loaded in the root
 * top-level document of the browser. This is really just a
 * convenience wrapper around BrowserViewSourceOfDocument.
 *
 * @param browser
 *        The browser that we want to load the source of.
 */
function BrowserViewSource(browser) {
  BrowserViewSourceOfDocument({
    browser,
    outerWindowID: browser.outerWindowID,
    URL: browser.currentURI.spec,
  });
}

// documentURL - URL of the document to view, or null for this window's document
// initialTab - name of the initial tab to display, or null for the first tab
// imageElement - image to load in the Media Tab of the Page Info window; can be null/omitted
// frameOuterWindowID - the id of the frame that the context menu opened in; can be null/omitted
// browser - the browser containing the document we're interested in inspecting; can be null/omitted
function BrowserPageInfo(documentURL, initialTab, imageElement, frameOuterWindowID, browser) {
  if (documentURL instanceof HTMLDocument) {
    Deprecated.warning("Please pass the location URL instead of the document " +
                       "to BrowserPageInfo() as the first argument.",
                       "https://bugzilla.mozilla.org/show_bug.cgi?id=1238180");
    documentURL = documentURL.location;
  }

  let args = { initialTab, imageElement, frameOuterWindowID, browser };
  var windows = Services.wm.getEnumerator("Browser:page-info");

  documentURL = documentURL || window.gBrowser.selectedBrowser.currentURI.spec;

  // Check for windows matching the url
  while (windows.hasMoreElements()) {
    var currentWindow = windows.getNext();
    if (currentWindow.closed) {
      continue;
    }
    if (currentWindow.document.documentElement.getAttribute("relatedUrl") == documentURL) {
      currentWindow.focus();
      currentWindow.resetPageInfo(args);
      return currentWindow;
    }
  }

  // We didn't find a matching window, so open a new one.
  return openDialog("chrome://browser/content/pageinfo/pageInfo.xul", "",
                    "chrome,toolbar,dialog=no,resizable", args);
}

/**
 * Sets the URI to display in the location bar.
 *
 * @param aURI [optional]
 *        nsIURI to set. If this is unspecified, the current URI will be used.
 */
function URLBarSetURI(aURI) {
  var value = gBrowser.userTypedValue;
  var valid = false;

  if (value == null) {
    let uri = aURI || gBrowser.currentURI;
    // Strip off "wyciwyg://" and passwords for the location bar
    try {
      uri = Services.uriFixup.createExposableURI(uri);
    } catch (e) {}

    // Replace initial page URIs with an empty string
    // only if there's no opener (bug 370555).
    if (isInitialPage(uri.spec) &&
        checkEmptyPageOrigin(gBrowser.selectedBrowser, uri)) {
      value = "";
    } else {
      // We should deal with losslessDecodeURI throwing for exotic URIs
      try {
        value = losslessDecodeURI(uri);
      } catch (ex) {
        value = "about:blank";
      }
    }

    valid = !isBlankPageURL(uri.spec) || uri.schemeIs("moz-extension");
  } else if (isInitialPage(value) &&
             checkEmptyPageOrigin(gBrowser.selectedBrowser)) {
    value = "";
    valid = true;
  }

  let isDifferentValidValue = valid && value != gURLBar.value;
  gURLBar.value = value;
  gURLBar.valueIsTyped = !valid;
  gURLBar.removeAttribute("usertyping");
  if (isDifferentValidValue) {
    gURLBar.selectionStart = gURLBar.selectionEnd = 0;
  }

  SetPageProxyState(valid ? "valid" : "invalid");
}

function losslessDecodeURI(aURI) {
  let scheme = aURI.scheme;
  if (scheme == "moz-action")
    throw new Error("losslessDecodeURI should never get a moz-action URI");

  var value = aURI.displaySpec;

  let decodeASCIIOnly = !["https", "http", "file", "ftp"].includes(scheme);
  // Try to decode as UTF-8 if there's no encoding sequence that we would break.
  if (!/%25(?:3B|2F|3F|3A|40|26|3D|2B|24|2C|23)/i.test(value)) {
    if (decodeASCIIOnly) {
      // This only decodes ascii characters (hex) 20-7e, except 25 (%).
      // This avoids both cases stipulated below (%-related issues, and \r, \n
      // and \t, which would be %0d, %0a and %09, respectively) as well as any
      // non-US-ascii characters.
      value = value.replace(/%(2[0-4]|2[6-9a-f]|[3-6][0-9a-f]|7[0-9a-e])/g, decodeURI);
    } else {
      try {
        value = decodeURI(value)
                  // 1. decodeURI decodes %25 to %, which creates unintended
                  //    encoding sequences. Re-encode it, unless it's part of
                  //    a sequence that survived decodeURI, i.e. one for:
                  //    ';', '/', '?', ':', '@', '&', '=', '+', '$', ',', '#'
                  //    (RFC 3987 section 3.2)
                  // 2. Re-encode select whitespace so that it doesn't get eaten
                  //    away by the location bar (bug 410726). Re-encode all
                  //    adjacent whitespace, to prevent spoofing attempts where
                  //    invisible characters would push part of the URL to
                  //    overflow the location bar (bug 1395508).
                  .replace(/%(?!3B|2F|3F|3A|40|26|3D|2B|24|2C|23)|[\r\n\t]|\s(?=\s)|\s$/ig,
                           encodeURIComponent);
      } catch (e) {}
    }
  }

  // Encode invisible characters (C0/C1 control characters, U+007F [DEL],
  // U+00A0 [no-break space], line and paragraph separator,
  // object replacement character) (bug 452979, bug 909264)
  value = value.replace(/[\u0000-\u001f\u007f-\u00a0\u2028\u2029\ufffc]/g,
                        encodeURIComponent);

  // Encode default ignorable characters (bug 546013)
  // except ZWNJ (U+200C) and ZWJ (U+200D) (bug 582186).
  // This includes all bidirectional formatting characters.
  // (RFC 3987 sections 3.2 and 4.1 paragraph 6)
  value = value.replace(/[\u00ad\u034f\u061c\u115f-\u1160\u17b4-\u17b5\u180b-\u180d\u200b\u200e-\u200f\u202a-\u202e\u2060-\u206f\u3164\ufe00-\ufe0f\ufeff\uffa0\ufff0-\ufff8]|\ud834[\udd73-\udd7a]|[\udb40-\udb43][\udc00-\udfff]/g,
                        encodeURIComponent);
  return value;
}

function UpdateUrlbarSearchSplitterState() {
  var splitter = document.getElementById("urlbar-search-splitter");
  var urlbar = document.getElementById("urlbar-container");
  var searchbar = document.getElementById("search-container");

  if (document.documentElement.getAttribute("customizing") == "true") {
    if (splitter) {
      splitter.remove();
    }
    return;
  }

  // If the splitter is already in the right place, we don't need to do anything:
  if (splitter &&
      ((splitter.nextSibling == searchbar && splitter.previousSibling == urlbar) ||
       (splitter.nextSibling == urlbar && splitter.previousSibling == searchbar))) {
    return;
  }

  var ibefore = null;
  if (urlbar && searchbar) {
    if (urlbar.nextSibling == searchbar)
      ibefore = searchbar;
    else if (searchbar.nextSibling == urlbar)
      ibefore = urlbar;
  }

  if (ibefore) {
    if (!splitter) {
      splitter = document.createElement("splitter");
      splitter.id = "urlbar-search-splitter";
      splitter.setAttribute("resizebefore", "flex");
      splitter.setAttribute("resizeafter", "flex");
      splitter.setAttribute("skipintoolbarset", "true");
      splitter.setAttribute("overflows", "false");
      splitter.className = "chromeclass-toolbar-additional";
    }
    urlbar.parentNode.insertBefore(splitter, ibefore);
  } else if (splitter)
    splitter.remove();
}

function UpdatePageProxyState() {
  if (gURLBar && gURLBar.value != gLastValidURLStr)
    SetPageProxyState("invalid");
}

/**
 * Updates the user interface to indicate whether the URI in the location bar is
 * different than the loaded page, because it's being edited or because a search
 * result is currently selected and is displayed in the location bar.
 *
 * @param aState
 *        The string "valid" indicates that the security indicators and other
 *        related user interface elments should be shown because the URI in the
 *        location bar matches the loaded page. The string "invalid" indicates
 *        that the URI in the location bar is different than the loaded page.
 */
function SetPageProxyState(aState) {
  if (!gURLBar)
    return;

  let oldPageProxyState = gURLBar.getAttribute("pageproxystate");
  // The "browser_urlbar_stop_pending.js" test uses a MutationObserver to do
  // some verifications at this point, and it breaks if we don't write the
  // attribute, even if it hasn't changed (bug 1338115).
  gURLBar.setAttribute("pageproxystate", aState);

  // the page proxy state is set to valid via OnLocationChange, which
  // gets called when we switch tabs.
  if (aState == "valid") {
    gLastValidURLStr = gURLBar.value;
    gURLBar.addEventListener("input", UpdatePageProxyState);
  } else if (aState == "invalid") {
    gURLBar.removeEventListener("input", UpdatePageProxyState);
  }

  // After we've ensured that we've applied the listeners and updated the value
  // of gLastValidURLStr, return early if the actual state hasn't changed.
  if (oldPageProxyState == aState) {
    return;
  }

  UpdatePopupNotificationsVisibility();
}

function UpdatePopupNotificationsVisibility() {
  // Only need to do something if the PopupNotifications object for this window
  // has already been initialized (i.e. its getter no longer exists).
  if (Object.getOwnPropertyDescriptor(window, "PopupNotifications").get) {
    return;
  }

  // Notify PopupNotifications that the visible anchors may have changed. This
  // also checks the suppression state according to the "shouldSuppress"
  // function defined earlier in this file.
  PopupNotifications.anchorVisibilityChange();
}

function PageProxyClickHandler(aEvent) {
  if (aEvent.button == 1 && Services.prefs.getBoolPref("middlemouse.paste"))
    middleMousePaste(aEvent);
}

// Values for telemtery bins: see TLS_ERROR_REPORT_UI in Histograms.json
const TLS_ERROR_REPORT_TELEMETRY_AUTO_CHECKED   = 2;
const TLS_ERROR_REPORT_TELEMETRY_AUTO_UNCHECKED = 3;

const PREF_SSL_IMPACT_ROOTS = ["security.tls.version.", "security.ssl3."];

/**
 * Handle command events bubbling up from error page content
 * or from about:newtab or from remote error pages that invoke
 * us via async messaging.
 */
var BrowserOnClick = {
  init() {
    let mm = window.messageManager;
    mm.addMessageListener("Browser:CertExceptionError", this);
    mm.addMessageListener("Browser:OpenCaptivePortalPage", this);
    mm.addMessageListener("Browser:SiteBlockedError", this);
    mm.addMessageListener("Browser:EnableOnlineMode", this);
    mm.addMessageListener("Browser:SetSSLErrorReportAuto", this);
    mm.addMessageListener("Browser:ResetSSLPreferences", this);
    mm.addMessageListener("Browser:SSLErrorReportTelemetry", this);
    mm.addMessageListener("Browser:SSLErrorGoBack", this);

    Services.obs.addObserver(this, "captive-portal-login-abort");
    Services.obs.addObserver(this, "captive-portal-login-success");
  },

  uninit() {
    let mm = window.messageManager;
    mm.removeMessageListener("Browser:CertExceptionError", this);
    mm.removeMessageListener("Browser:SiteBlockedError", this);
    mm.removeMessageListener("Browser:EnableOnlineMode", this);
    mm.removeMessageListener("Browser:SetSSLErrorReportAuto", this);
    mm.removeMessageListener("Browser:ResetSSLPreferences", this);
    mm.removeMessageListener("Browser:SSLErrorReportTelemetry", this);
    mm.removeMessageListener("Browser:SSLErrorGoBack", this);

    Services.obs.removeObserver(this, "captive-portal-login-abort");
    Services.obs.removeObserver(this, "captive-portal-login-success");
  },

  observe(aSubject, aTopic, aData) {
    switch (aTopic) {
      case "captive-portal-login-abort":
      case "captive-portal-login-success":
        // Broadcast when a captive portal is freed so that error pages
        // can refresh themselves.
        window.messageManager.broadcastAsyncMessage("Browser:CaptivePortalFreed");
      break;
    }
  },

  receiveMessage(msg) {
    switch (msg.name) {
      case "Browser:CertExceptionError":
        this.onCertError(msg.target, msg.data.elementId,
                         msg.data.isTopFrame, msg.data.location,
                         msg.data.securityInfoAsString);
      break;
      case "Browser:OpenCaptivePortalPage":
        CaptivePortalWatcher.ensureCaptivePortalTab();
      break;
      case "Browser:SiteBlockedError":
        this.onAboutBlocked(msg.data.elementId, msg.data.reason,
                            msg.data.isTopFrame, msg.data.location,
                            msg.data.blockedInfo);
      break;
      case "Browser:EnableOnlineMode":
        if (Services.io.offline) {
          // Reset network state and refresh the page.
          Services.io.offline = false;
          msg.target.reload();
        }
      break;
      case "Browser:ResetSSLPreferences":
        let prefSSLImpact = PREF_SSL_IMPACT_ROOTS.reduce((prefs, root) => {
                return prefs.concat(Services.prefs.getChildList(root));
        }, []);
        for (let prefName of prefSSLImpact) {
          Services.prefs.clearUserPref(prefName);
        }
        msg.target.reload();
      break;
      case "Browser:SetSSLErrorReportAuto":
        Services.prefs.setBoolPref("security.ssl.errorReporting.automatic", msg.json.automatic);
        let bin = TLS_ERROR_REPORT_TELEMETRY_AUTO_UNCHECKED;
        if (msg.json.automatic) {
          bin = TLS_ERROR_REPORT_TELEMETRY_AUTO_CHECKED;
        }
        Services.telemetry.getHistogramById("TLS_ERROR_REPORT_UI").add(bin);
      break;
      case "Browser:SSLErrorReportTelemetry":
        let reportStatus = msg.data.reportStatus;
        Services.telemetry.getHistogramById("TLS_ERROR_REPORT_UI")
          .add(reportStatus);
      break;
      case "Browser:SSLErrorGoBack":
        goBackFromErrorPage();
      break;
    }
  },

  onCertError(browser, elementId, isTopFrame, location, securityInfoAsString) {
    let secHistogram = Services.telemetry.getHistogramById("SECURITY_UI");
    let securityInfo;

    switch (elementId) {
      case "exceptionDialogButton":
        if (isTopFrame) {
          secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_BAD_CERT_TOP_CLICK_ADD_EXCEPTION);
        }

        securityInfo = getSecurityInfo(securityInfoAsString);
        let sslStatus = securityInfo.QueryInterface(Ci.nsISSLStatusProvider)
                                    .SSLStatus;
        let params = { exceptionAdded: false,
                       sslStatus };

        try {
          switch (Services.prefs.getIntPref("browser.ssl_override_behavior")) {
            case 2 : // Pre-fetch & pre-populate
              params.prefetchCert = true;
            case 1 : // Pre-populate
              params.location = location;
          }
        } catch (e) {
          Cu.reportError("Couldn't get ssl_override pref: " + e);
        }

        window.openDialog("chrome://pippki/content/exceptionDialog.xul",
                          "", "chrome,centerscreen,modal", params);

        // If the user added the exception cert, attempt to reload the page
        if (params.exceptionAdded) {
          browser.reload();
        }
        break;

      case "returnButton":
        if (isTopFrame) {
          secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_BAD_CERT_TOP_GET_ME_OUT_OF_HERE);
        }
        goBackFromErrorPage();
        break;

      case "advancedButton":
        if (isTopFrame) {
          secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_BAD_CERT_TOP_UNDERSTAND_RISKS);
        }

        securityInfo = getSecurityInfo(securityInfoAsString);
        let errorInfo = getDetailedCertErrorInfo(location,
                                                 securityInfo);
        browser.messageManager.sendAsyncMessage( "CertErrorDetails", {
            code: securityInfo.errorCode,
            info: errorInfo
        });
        break;

      case "copyToClipboard":
        const gClipboardHelper = Cc["@mozilla.org/widget/clipboardhelper;1"]
                                    .getService(Ci.nsIClipboardHelper);
        securityInfo = getSecurityInfo(securityInfoAsString);
        let detailedInfo = getDetailedCertErrorInfo(location,
                                                    securityInfo);
        gClipboardHelper.copyString(detailedInfo);
        break;

    }
  },

  onAboutBlocked(elementId, reason, isTopFrame, location, blockedInfo) {
    // Depending on what page we are displaying here (malware/phishing/unwanted)
    // use the right strings and links for each.
    let bucketName = "";
    let sendTelemetry = false;
    if (reason === "malware") {
      sendTelemetry = true;
      bucketName = "WARNING_MALWARE_PAGE_";
    } else if (reason === "phishing") {
      sendTelemetry = true;
      bucketName = "WARNING_PHISHING_PAGE_";
    } else if (reason === "unwanted") {
      sendTelemetry = true;
      bucketName = "WARNING_UNWANTED_PAGE_";
    } else if (reason === "harmful") {
      sendTelemetry = true;
      bucketName = "WARNING_HARMFUL_PAGE_";
    }
    let secHistogram = Services.telemetry.getHistogramById("URLCLASSIFIER_UI_EVENTS");
    let nsISecTel = Ci.IUrlClassifierUITelemetry;
    bucketName += isTopFrame ? "TOP_" : "FRAME_";

    switch (elementId) {
      case "goBackButton":
        if (sendTelemetry) {
          secHistogram.add(nsISecTel[bucketName + "GET_ME_OUT_OF_HERE"]);
        }
        getMeOutOfHere();
        break;
      case "ignore_warning_link":
        if (Services.prefs.getBoolPref("browser.safebrowsing.allowOverride")) {
          if (sendTelemetry) {
            secHistogram.add(nsISecTel[bucketName + "IGNORE_WARNING"]);
          }
          this.ignoreWarningLink(reason, blockedInfo);
        }
        break;
    }
  },

  ignoreWarningLink(reason, blockedInfo) {
    // Allow users to override and continue through to the site,
    // but add a notify bar as a reminder, so that they don't lose
    // track after, e.g., tab switching.
    gBrowser.loadURIWithFlags(gBrowser.currentURI.spec,
                              nsIWebNavigation.LOAD_FLAGS_BYPASS_CLASSIFIER,
                              null, null, null);

    Services.perms.add(gBrowser.currentURI, "safe-browsing",
                       Ci.nsIPermissionManager.ALLOW_ACTION,
                       Ci.nsIPermissionManager.EXPIRE_SESSION);

    let buttons = [{
      label: gNavigatorBundle.getString("safebrowsing.getMeOutOfHereButton.label"),
      accessKey: gNavigatorBundle.getString("safebrowsing.getMeOutOfHereButton.accessKey"),
      callback() { getMeOutOfHere(); }
    }];

    let title;
    if (reason === "malware") {
      let reportUrl = gSafeBrowsing.getReportURL("MalwareMistake", blockedInfo);
      title = gNavigatorBundle.getString("safebrowsing.reportedAttackSite");
      // There's no button if we can not get report url, for example if the provider
      // of blockedInfo is not Google
      if (reportUrl) {
        buttons[1] = {
          label: gNavigatorBundle.getString("safebrowsing.notAnAttackButton.label"),
          accessKey: gNavigatorBundle.getString("safebrowsing.notAnAttackButton.accessKey"),
          callback() {
            openUILinkIn(reportUrl, "tab");
          }
        };
      }
    } else if (reason === "phishing") {
      let reportUrl = gSafeBrowsing.getReportURL("PhishMistake", blockedInfo);
      title = gNavigatorBundle.getString("safebrowsing.deceptiveSite");
      // There's no button if we can not get report url, for example if the provider
      // of blockedInfo is not Google
      if (reportUrl) {
        buttons[1] = {
          label: gNavigatorBundle.getString("safebrowsing.notADeceptiveSiteButton.label"),
          accessKey: gNavigatorBundle.getString("safebrowsing.notADeceptiveSiteButton.accessKey"),
          callback() {
            openUILinkIn(reportUrl, "tab");
          }
        };
      }
    } else if (reason === "unwanted") {
      title = gNavigatorBundle.getString("safebrowsing.reportedUnwantedSite");
      // There is no button for reporting errors since Google doesn't currently
      // provide a URL endpoint for these reports.
    } else if (reason === "harmful") {
      title = gNavigatorBundle.getString("safebrowsing.reportedHarmfulSite");
      // There is no button for reporting errors since Google doesn't currently
      // provide a URL endpoint for these reports.
    }

    SafeBrowsingNotificationBox.show(title, buttons);
  },
};

/**
 * Re-direct the browser to a known-safe page.  This function is
 * used when, for example, the user browses to a known malware page
 * and is presented with about:blocked.  The "Get me out of here!"
 * button should take the user to the default start page so that even
 * when their own homepage is infected, we can get them somewhere safe.
 */
function getMeOutOfHere() {
  gBrowser.loadURI(getDefaultHomePage());
}

/**
 * Re-direct the browser to the previous page or a known-safe page if no
 * previous page is found in history.  This function is used when the user
 * browses to a secure page with certificate issues and is presented with
 * about:certerror.  The "Go Back" button should take the user to the previous
 * or a default start page so that even when their own homepage is on a server
 * that has certificate errors, we can get them somewhere safe.
 */
function goBackFromErrorPage() {
  const ss = Cc["@mozilla.org/browser/sessionstore;1"].
             getService(Ci.nsISessionStore);
  let state = JSON.parse(ss.getTabState(gBrowser.selectedTab));
  if (state.index == 1) {
    // If the unsafe page is the first or the only one in history, go to the
    // start page.
    gBrowser.loadURI(getDefaultHomePage());
  } else {
    BrowserBack();
  }
}

/**
 * Return the default start page for the cases when the user's own homepage is
 * infected, so we can get them somewhere safe.
 */
function getDefaultHomePage() {
  // Get the start page from the *default* pref branch, not the user's
  var prefs = Services.prefs.getDefaultBranch(null);
  var url = BROWSER_NEW_TAB_URL;
  if (PrivateBrowsingUtils.isWindowPrivate(window))
    return url;
  try {
    url = prefs.getComplexValue("browser.startup.homepage",
                                Ci.nsIPrefLocalizedString).data;
    // If url is a pipe-delimited set of pages, just take the first one.
    if (url.includes("|"))
      url = url.split("|")[0];
  } catch (e) {
    Cu.reportError("Couldn't get homepage pref: " + e);
  }
  return url;
}

function BrowserFullScreen() {
  window.fullScreen = !window.fullScreen;
}

function getWebNavigation() {
  return gBrowser.webNavigation;
}

function BrowserReloadWithFlags(reloadFlags) {
  let url = gBrowser.currentURI.spec;
  if (gBrowser.updateBrowserRemotenessByURL(gBrowser.selectedBrowser, url)) {
    // If the remoteness has changed, the new browser doesn't have any
    // information of what was loaded before, so we need to load the previous
    // URL again.
    gBrowser.loadURIWithFlags(url, reloadFlags);
    return;
  }

  // Do this after the above case where we might flip remoteness.
  // Unfortunately, we'll count the remoteness flip case as a
  // "newURL" load, since we're using loadURIWithFlags, but hopefully
  // that's rare enough to not matter.
  maybeRecordAbandonmentTelemetry(gBrowser.selectedTab, "reload");

  // Reset temporary permissions on the current tab. This is done here
  // because we only want to reset permissions on user reload.
  SitePermissions.clearTemporaryPermissions(gBrowser.selectedBrowser);

  let windowUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)
                          .getInterface(Ci.nsIDOMWindowUtils);

  gBrowser.selectedBrowser
          .messageManager
          .sendAsyncMessage("Browser:Reload",
                            { flags: reloadFlags,
                              handlingUserInput: windowUtils.isHandlingUserInput });
}

function getSecurityInfo(securityInfoAsString) {
  if (!securityInfoAsString)
    return null;

  const serhelper = Cc["@mozilla.org/network/serialization-helper;1"]
                       .getService(Ci.nsISerializationHelper);
  let securityInfo = serhelper.deserializeObject(securityInfoAsString);
  securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);

  return securityInfo;
}

/**
 * Returns a string with detailed information about the certificate validation
 * failure from the specified URI that can be used to send a report.
 */
function getDetailedCertErrorInfo(location, securityInfo) {
  if (!securityInfo)
    return "";

  let certErrorDetails = location;
  let code = securityInfo.errorCode;
  let errors = Cc["@mozilla.org/nss_errors_service;1"]
                  .getService(Ci.nsINSSErrorsService);

  certErrorDetails += "\r\n\r\n" + errors.getErrorMessage(errors.getXPCOMFromNSSError(code));

  const sss = Cc["@mozilla.org/ssservice;1"]
                 .getService(Ci.nsISiteSecurityService);
  // SiteSecurityService uses different storage if the channel is
  // private. Thus we must give isSecureURI correct flags or we
  // might get incorrect results.
  let flags = PrivateBrowsingUtils.isWindowPrivate(window) ?
              Ci.nsISocketProvider.NO_PERMANENT_STORAGE : 0;

  let uri = Services.io.newURI(location);

  let hasHSTS = sss.isSecureURI(sss.HEADER_HSTS, uri, flags);
  let hasHPKP = sss.isSecureURI(sss.HEADER_HPKP, uri, flags);
  certErrorDetails += "\r\n\r\n" +
                      gNavigatorBundle.getFormattedString("certErrorDetailsHSTS.label",
                                                          [hasHSTS]);
  certErrorDetails += "\r\n" +
                      gNavigatorBundle.getFormattedString("certErrorDetailsKeyPinning.label",
                                                          [hasHPKP]);

  let certChain = "";
  if (securityInfo.failedCertChain) {
    let certs = securityInfo.failedCertChain.getEnumerator();
    while (certs.hasMoreElements()) {
      let cert = certs.getNext();
      cert.QueryInterface(Ci.nsIX509Cert);
      certChain += getPEMString(cert);
    }
  }

  certErrorDetails += "\r\n\r\n" +
                      gNavigatorBundle.getString("certErrorDetailsCertChain.label") +
                      "\r\n\r\n" + certChain;

  return certErrorDetails;
}

// TODO: can we pull getDERString and getPEMString in from pippki.js instead of
// duplicating them here?
function getDERString(cert) {
  var length = {};
  var derArray = cert.getRawDER(length);
  var derString = "";
  for (var i = 0; i < derArray.length; i++) {
    derString += String.fromCharCode(derArray[i]);
  }
  return derString;
}

function getPEMString(cert) {
  var derb64 = btoa(getDERString(cert));
  // Wrap the Base64 string into lines of 64 characters,
  // with CRLF line breaks (as specified in RFC 1421).
  var wrapped = derb64.replace(/(\S{64}(?!$))/g, "$1\r\n");
  return "-----BEGIN CERTIFICATE-----\r\n"
         + wrapped
         + "\r\n-----END CERTIFICATE-----\r\n";
}

var PrintPreviewListener = {
  _printPreviewTab: null,
  _simplifiedPrintPreviewTab: null,
  _tabBeforePrintPreview: null,
  _simplifyPageTab: null,
  _lastRequestedPrintPreviewTab: null,

  _createPPBrowser() {
    let browser = this.getSourceBrowser();
    let preferredRemoteType = browser.remoteType;
    return gBrowser.loadOneTab("about:printpreview", {
      inBackground: true,
      preferredRemoteType,
      sameProcessAsFrameLoader: browser.frameLoader,
      triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
    });
  },
  getPrintPreviewBrowser() {
    if (!this._printPreviewTab) {
      this._printPreviewTab = this._createPPBrowser();
    }
    gBrowser._allowTabChange = true;
    this._lastRequestedPrintPreviewTab = gBrowser.selectedTab = this._printPreviewTab;
    gBrowser._allowTabChange = false;
    return gBrowser.getBrowserForTab(this._printPreviewTab);
  },
  getSimplifiedPrintPreviewBrowser() {
    if (!this._simplifiedPrintPreviewTab) {
      this._simplifiedPrintPreviewTab = this._createPPBrowser();
    }
    gBrowser._allowTabChange = true;
    this._lastRequestedPrintPreviewTab = gBrowser.selectedTab = this._simplifiedPrintPreviewTab;
    gBrowser._allowTabChange = false;
    return gBrowser.getBrowserForTab(this._simplifiedPrintPreviewTab);
  },
  createSimplifiedBrowser() {
    let browser = this.getSourceBrowser();
    this._simplifyPageTab = gBrowser.loadOneTab("about:printpreview", {
      inBackground: true,
      sameProcessAsFrameLoader: browser.frameLoader,
      triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
     });
    return this.getSimplifiedSourceBrowser();
  },
  getSourceBrowser() {
    if (!this._tabBeforePrintPreview) {
      this._tabBeforePrintPreview = gBrowser.selectedTab;
    }
    return this._tabBeforePrintPreview.linkedBrowser;
  },
  getSimplifiedSourceBrowser() {
    return this._simplifyPageTab ?
      gBrowser.getBrowserForTab(this._simplifyPageTab) : null;
  },
  getNavToolbox() {
    return gNavToolbox;
  },
  onEnter() {
    // We might have accidentally switched tabs since the user invoked print
    // preview
    if (gBrowser.selectedTab != this._lastRequestedPrintPreviewTab) {
      gBrowser.selectedTab = this._lastRequestedPrintPreviewTab;
    }
    gInPrintPreviewMode = true;
    this._toggleAffectedChrome();
  },
  onExit() {
    gBrowser._allowTabChange = true;
    gBrowser.selectedTab = this._tabBeforePrintPreview;
    gBrowser._allowTabChange = false;
    this._tabBeforePrintPreview = null;
    gInPrintPreviewMode = false;
    this._toggleAffectedChrome();
    let tabsToRemove = ["_simplifyPageTab", "_printPreviewTab", "_simplifiedPrintPreviewTab"];
    for (let tabProp of tabsToRemove) {
      if (this[tabProp]) {
        gBrowser.removeTab(this[tabProp]);
        this[tabProp] = null;
      }
    }
    gBrowser.deactivatePrintPreviewBrowsers();
    this._lastRequestedPrintPreviewTab = null;
  },
  _toggleAffectedChrome() {
    gNavToolbox.collapsed = gInPrintPreviewMode;

    if (gInPrintPreviewMode)
      this._hideChrome();
    else
      this._showChrome();

    TabsInTitlebar.allowedBy("print-preview", !gInPrintPreviewMode);
  },
  _hideChrome() {
    this._chromeState = {};

    this._chromeState.sidebarOpen = SidebarUI.isOpen;
    this._sidebarCommand = SidebarUI.currentID;
    SidebarUI.hide();

    var notificationBox = gBrowser.getNotificationBox();
    this._chromeState.notificationsOpen = !notificationBox.notificationsHidden;
    notificationBox.notificationsHidden = true;

    this._chromeState.findOpen = gFindBarInitialized && !gFindBar.hidden;
    if (gFindBarInitialized)
      gFindBar.close();

    var globalNotificationBox = document.getElementById("global-notificationbox");
    this._chromeState.globalNotificationsOpen = !globalNotificationBox.notificationsHidden;
    globalNotificationBox.notificationsHidden = true;

    this._chromeState.syncNotificationsOpen = false;
    var syncNotifications = document.getElementById("sync-notifications");
    if (syncNotifications) {
      this._chromeState.syncNotificationsOpen = !syncNotifications.notificationsHidden;
      syncNotifications.notificationsHidden = true;
    }
  },
  _showChrome() {
    if (this._chromeState.notificationsOpen)
      gBrowser.getNotificationBox().notificationsHidden = false;

    if (this._chromeState.findOpen)
      gFindBar.open();

    if (this._chromeState.globalNotificationsOpen)
      document.getElementById("global-notificationbox").notificationsHidden = false;

    if (this._chromeState.syncNotificationsOpen)
      document.getElementById("sync-notifications").notificationsHidden = false;

    if (this._chromeState.sidebarOpen)
      SidebarUI.show(this._sidebarCommand);
  },

  activateBrowser(browser) {
    gBrowser.activateBrowserForPrintPreview(browser);
  },
};

var browserDragAndDrop = {
  canDropLink: aEvent => Services.droppedLinkHandler.canDropLink(aEvent, true),

  dragOver(aEvent) {
    if (this.canDropLink(aEvent)) {
      aEvent.preventDefault();
    }
  },

  getTriggeringPrincipal(aEvent) {
    return Services.droppedLinkHandler.getTriggeringPrincipal(aEvent);
  },

  validateURIsForDrop(aEvent, aURIsCount, aURIs) {
    return Services.droppedLinkHandler.validateURIsForDrop(aEvent,
                                                           aURIsCount,
                                                           aURIs);
  },

  dropLinks(aEvent, aDisallowInherit) {
    return Services.droppedLinkHandler.dropLinks(aEvent, aDisallowInherit);
  }
};

var homeButtonObserver = {
  onDrop(aEvent) {
      // disallow setting home pages that inherit the principal
      let links = browserDragAndDrop.dropLinks(aEvent, true);
      if (links.length) {
        let urls = [];
        for (let link of links) {
          if (link.url.includes("|")) {
            urls.push(...link.url.split("|"));
          } else {
            urls.push(link.url);
          }
        }

        try {
          browserDragAndDrop.validateURIsForDrop(aEvent, urls.length, urls);
        } catch (e) {
          return;
        }

        setTimeout(openHomeDialog, 0, urls.join("|"));
      }
    },

  onDragOver(aEvent) {
      if (Services.prefs.prefIsLocked("browser.startup.homepage")) {
        return;
      }
      browserDragAndDrop.dragOver(aEvent);
      aEvent.dropEffect = "link";
    },
  onDragExit(aEvent) {
    }
};

function openHomeDialog(aURL) {
  var promptTitle = gNavigatorBundle.getString("droponhometitle");
  var promptMsg;
  if (aURL.includes("|")) {
    promptMsg = gNavigatorBundle.getString("droponhomemsgMultiple");
  } else {
    promptMsg = gNavigatorBundle.getString("droponhomemsg");
  }

  var pressedVal  = Services.prompt.confirmEx(window, promptTitle, promptMsg,
                          Services.prompt.STD_YES_NO_BUTTONS,
                          null, null, null, null, {value: 0});

  if (pressedVal == 0) {
    try {
      Services.prefs.setStringPref("browser.startup.homepage", aURL);
    } catch (ex) {
      dump("Failed to set the home page.\n" + ex + "\n");
    }
  }
}

var newTabButtonObserver = {
  onDragOver(aEvent) {
    browserDragAndDrop.dragOver(aEvent);
  },
  onDragExit(aEvent) {},
  async onDrop(aEvent) {
    let shiftKey = aEvent.shiftKey;
    let links = browserDragAndDrop.dropLinks(aEvent);
    let triggeringPrincipal = browserDragAndDrop.getTriggeringPrincipal(aEvent);

    if (links.length >= Services.prefs.getIntPref("browser.tabs.maxOpenBeforeWarn")) {
      // Sync dialog cannot be used inside drop event handler.
      let answer = await OpenInTabsUtils.promiseConfirmOpenInTabs(links.length,
                                                                  window);
      if (!answer) {
        return;
      }
    }

    for (let link of links) {
      if (link.url) {
        let data = await getShortcutOrURIAndPostData(link.url);
        // Allow third-party services to fixup this URL.
        openNewTabWith(data.url, shiftKey, {
          postData: data.postData,
          allowThirdPartyFixup: true,
          triggeringPrincipal,
        });
      }
    }
  }
};

var newWindowButtonObserver = {
  onDragOver(aEvent) {
    browserDragAndDrop.dragOver(aEvent);
  },
  onDragExit(aEvent) {},
  async onDrop(aEvent) {
    let links = browserDragAndDrop.dropLinks(aEvent);
    let triggeringPrincipal = browserDragAndDrop.getTriggeringPrincipal(aEvent);

    if (links.length >= Services.prefs.getIntPref("browser.tabs.maxOpenBeforeWarn")) {
      // Sync dialog cannot be used inside drop event handler.
      let answer = await OpenInTabsUtils.promiseConfirmOpenInTabs(links.length,
                                                                  window);
      if (!answer) {
        return;
      }
    }

    for (let link of links) {
      if (link.url) {
        let data = await getShortcutOrURIAndPostData(link.url);
        // Allow third-party services to fixup this URL.
        openNewWindowWith(data.url, {
          postData: data.postData,
          allowThirdPartyFixup: true,
          triggeringPrincipal,
        });
      }
    }
  }
};
const DOMEventHandler = {
  init() {
    let mm = window.messageManager;
    mm.addMessageListener("Link:AddFeed", this);
    mm.addMessageListener("Link:SetIcon", this);
    mm.addMessageListener("Link:AddSearch", this);
    mm.addMessageListener("Meta:SetPageInfo", this);
  },

  receiveMessage(aMsg) {
    switch (aMsg.name) {
      case "Link:AddFeed":
        let link = {type: aMsg.data.type, href: aMsg.data.href, title: aMsg.data.title};
        FeedHandler.addFeed(link, aMsg.target);
        break;

      case "Link:SetIcon":
        this.setIcon(aMsg.target, aMsg.data.url, aMsg.data.loadingPrincipal,
                     aMsg.data.requestContextID, aMsg.data.canUseForTab);
        break;

      case "Link:AddSearch":
        this.addSearch(aMsg.target, aMsg.data.engine, aMsg.data.url);
        break;

      case "Meta:SetPageInfo":
        this.setPageInfo(aMsg.data);
        break;
    }
  },

  setPageInfo(aData) {
    const {url, description, previewImageURL} = aData;
    gBrowser.setPageInfo(url, description, previewImageURL);
    return true;
  },

  setIcon(aBrowser, aURL, aLoadingPrincipal, aRequestContextID = 0, aCanUseForTab = true) {
    if (gBrowser.isFailedIcon(aURL))
      return false;

    let tab = gBrowser.getTabForBrowser(aBrowser);
    if (!tab)
      return false;

    let loadingPrincipal = aLoadingPrincipal ||
                           Services.scriptSecurityManager.getSystemPrincipal();
    if (aURL) {
      gBrowser.storeIcon(aBrowser, aURL, loadingPrincipal, aRequestContextID);
    }
    if (aCanUseForTab) {
      gBrowser.setIcon(tab, aURL, loadingPrincipal, aRequestContextID);
    }
    return true;
  },

  addSearch(aBrowser, aEngine, aURL) {
    let tab = gBrowser.getTabForBrowser(aBrowser);
    if (!tab)
      return;

    BrowserSearch.addEngine(aBrowser, aEngine, makeURI(aURL));
  },
};

const BrowserSearch = {
  _searchInitComplete: false,

  init() {
    Services.obs.addObserver(this, "browser-search-engine-modified");
  },

  delayedStartupInit() {
    // Asynchronously initialize the search service if necessary, to get the
    // current engine for working out the placeholder.
    Services.search.init(rv => {
      if (Components.isSuccessCode(rv)) {
        // Delay the update for this until so that we don't change it while
        // the user is looking at it / isn't expecting it.
        this._updateURLBarPlaceholder(Services.search.currentEngine, true);
        this._searchInitComplete = true;
      }
    });
  },

  uninit() {
    Services.obs.removeObserver(this, "browser-search-engine-modified");
  },

  observe(engine, topic, data) {
    switch (data) {
    case "engine-current":
      if (this._searchInitComplete) {
        this._updateURLBarPlaceholder(engine);
      }
      break;
    }
  },

  /**
   * Initializes the urlbar placeholder to the pre-saved engine name. We do this
   * via a preference, to avoid needing to synchronously init the search service.
   *
   * This should be called around the time of DOMContentLoaded, so that it is
   * initialized quickly before the user sees anything.
   *
   * Note: If the preference doesn't exist, we don't do anything as the default
   * placeholder is a string which doesn't have the engine name.
   */
  initPlaceHolder() {
    let engineName = Services.prefs.getStringPref("browser.urlbar.placeholderName", "");
    if (engineName) {
      // We can do this directly, since we know we're at DOMContentLoaded.
      this._setURLBarPlaceholder(engineName);
    }
  },

  /**
   * Updates the URLBar placeholder for the specified engine, delaying the
   * update if required. This also saves the current engine name in preferences
   * for the next restart.
   *
   * Note: The engine name will only be displayed for built-in engines, as we
   * know they should have short names.
   *
   * @param {nsISearchEngine} engine The search engine to use for the update.
   * @param {Boolean} delayUpdate    Set to true, to delay update until the
   *                                 placeholder is not displayed.
   */
  _updateURLBarPlaceholder(engine, delayUpdate = false) {
    if (!engine) {
      throw new Error("Expected an engine to be specified");
    }

    let engineName = "";
    if (Services.search.getDefaultEngines().includes(engine)) {
      engineName = engine.name;
      Services.prefs.setStringPref("browser.urlbar.placeholderName", engineName);
    } else {
      Services.prefs.clearUserPref("browser.urlbar.placeholderName");
    }

    // Only delay if requested, and we're not displaying text in the URL bar
    // currently.
    if (delayUpdate && !gURLBar.value) {
      // Delays changing the URL Bar placeholder until the user is not going to be
      // seeing it, e.g. when there is a value entered in the bar, or if there is
      // a tab switch to a tab which has a url loaded.
      let placeholderUpdateListener = () => {
        if (gURLBar.value) {
          this._setURLBarPlaceholder(engineName);
          gURLBar.removeEventListener("input", placeholderUpdateListener);
          gBrowser.tabContainer.removeEventListener("TabSelect", placeholderUpdateListener);
        }
      };

      gURLBar.addEventListener("input", placeholderUpdateListener);
      gBrowser.tabContainer.addEventListener("TabSelect", placeholderUpdateListener);
    } else {
      this._setURLBarPlaceholder(engineName);
    }
  },

  /**
   * Sets the URLBar placeholder to either something based on the engine name,
   * or the default placeholder.
   *
   * @param {String} name The name of the engine to use, an empty string if to
   *                      use the default placeholder.
   */
  _setURLBarPlaceholder(name) {
    let placeholder;
    if (name) {
      placeholder = gBrowserBundle.formatStringFromName("urlbar.placeholder",
        [name], 1);
    } else {
      placeholder = gURLBar.getAttribute("defaultPlaceholder");
    }
    gURLBar.setAttribute("placeholder", placeholder);
  },

  addEngine(browser, engine, uri) {
    // Check to see whether we've already added an engine with this title
    if (browser.engines) {
      if (browser.engines.some(e => e.title == engine.title))
        return;
    }

    var hidden = false;
    // If this engine (identified by title) is already in the list, add it
    // to the list of hidden engines rather than to the main list.
    // XXX This will need to be changed when engines are identified by URL;
    // see bug 335102.
    if (Services.search.getEngineByName(engine.title))
      hidden = true;

    var engines = (hidden ? browser.hiddenEngines : browser.engines) || [];

    engines.push({ uri: engine.href,
                   title: engine.title,
                   get icon() { return browser.mIconURL; }
                 });

    if (hidden)
      browser.hiddenEngines = engines;
    else {
      browser.engines = engines;
      if (browser == gBrowser.selectedBrowser)
        this.updateOpenSearchBadge();
    }
  },

  /**
   * Update the browser UI to show whether or not additional engines are
   * available when a page is loaded or the user switches tabs to a page that
   * has search engines.
   */
  updateOpenSearchBadge() {
    var searchBar = this.searchBar;
    if (!searchBar)
      return;

    var engines = gBrowser.selectedBrowser.engines;
    if (engines && engines.length > 0)
      searchBar.setAttribute("addengines", "true");
    else
      searchBar.removeAttribute("addengines");
  },

  /**
   * Gives focus to the search bar, if it is present on the toolbar, or loads
   * the default engine's search form otherwise. For Mac, opens a new window
   * or focuses an existing window, if necessary.
   */
  webSearch: function BrowserSearch_webSearch() {
    if (window.location.href != getBrowserURL()) {
      var win = getTopWin();
      if (win) {
        // If there's an open browser window, it should handle this command
        win.focus();
        win.BrowserSearch.webSearch();
      } else {
        // If there are no open browser windows, open a new one
        var observer = function(subject, topic, data) {
          if (subject == win) {
            BrowserSearch.webSearch();
            Services.obs.removeObserver(observer, "browser-delayed-startup-finished");
          }
        };
        win = window.openDialog(getBrowserURL(), "_blank",
                                "chrome,all,dialog=no", "about:blank");
        Services.obs.addObserver(observer, "browser-delayed-startup-finished");
      }
      return;
    }

    let focusUrlBarIfSearchFieldIsNotActive = function(aSearchBar) {
      if (!aSearchBar || document.activeElement != aSearchBar.textbox.inputField) {
        focusAndSelectUrlBar(true);
      }
    };

    let searchBar = this.searchBar;
    let placement = CustomizableUI.getPlacementOfWidget("search-container");
    let focusSearchBar = () => {
      searchBar = this.searchBar;
      searchBar.select();
      focusUrlBarIfSearchFieldIsNotActive(searchBar);
    };
    if (placement && searchBar &&
        ((searchBar.parentNode.getAttribute("overflowedItem") == "true" &&
          placement.area == CustomizableUI.AREA_NAVBAR) ||
         placement.area == CustomizableUI.AREA_FIXED_OVERFLOW_PANEL)) {
      let navBar = document.getElementById(CustomizableUI.AREA_NAVBAR);
      navBar.overflowable.show().then(focusSearchBar);
      return;
    }
    if (searchBar) {
      if (window.fullScreen)
        FullScreen.showNavToolbox();
      searchBar.select();
    }
    focusUrlBarIfSearchFieldIsNotActive(searchBar);
  },

  /**
   * Loads a search results page, given a set of search terms. Uses the current
   * engine if the search bar is visible, or the default engine otherwise.
   *
   * @param searchText
   *        The search terms to use for the search.
   *
   * @param useNewTab
   *        Boolean indicating whether or not the search should load in a new
   *        tab.
   *
   * @param purpose [optional]
   *        A string meant to indicate the context of the search request. This
   *        allows the search service to provide a different nsISearchSubmission
   *        depending on e.g. where the search is triggered in the UI.
   *
   * @return engine The search engine used to perform a search, or null if no
   *                search was performed.
   */
  _loadSearch(searchText, useNewTab, purpose) {
    let engine;

    // If the search bar is visible, use the current engine, otherwise, fall
    // back to the default engine.
    if (isElementVisible(this.searchBar))
      engine = Services.search.currentEngine;
    else
      engine = Services.search.defaultEngine;

    let submission = engine.getSubmission(searchText, null, purpose); // HTML response

    // getSubmission can return null if the engine doesn't have a URL
    // with a text/html response type.  This is unlikely (since
    // SearchService._addEngineToStore() should fail for such an engine),
    // but let's be on the safe side.
    if (!submission) {
      return null;
    }

    let inBackground = Services.prefs.getBoolPref("browser.search.context.loadInBackground");
    openLinkIn(submission.uri.spec,
               useNewTab ? "tab" : "current",
               { postData: submission.postData,
                 inBackground,
                 relatedToCurrent: true });

    return engine;
  },

  /**
   * Just like _loadSearch, but preserving an old API.
   *
   * @return string Name of the search engine used to perform a search or null
   *         if a search was not performed.
   */
  loadSearch: function BrowserSearch_search(searchText, useNewTab, purpose) {
    let engine = BrowserSearch._loadSearch(searchText, useNewTab, purpose);
    if (!engine) {
      return null;
    }
    return engine.name;
  },

  /**
   * Perform a search initiated from the context menu.
   *
   * This should only be called from the context menu. See
   * BrowserSearch.loadSearch for the preferred API.
   */
  loadSearchFromContext(terms) {
    let engine = BrowserSearch._loadSearch(terms, true, "contextmenu");
    if (engine) {
      BrowserSearch.recordSearchInTelemetry(engine, "contextmenu");
    }
  },

  pasteAndSearch(event) {
    BrowserSearch.searchBar.select();
    goDoCommand("cmd_paste");
    BrowserSearch.searchBar.handleSearchCommand(event);
  },

  /**
   * Returns the search bar element if it is present in the toolbar, null otherwise.
   */
  get searchBar() {
    return document.getElementById("searchbar");
  },

  get searchEnginesURL() {
    return formatURL("browser.search.searchEnginesURL", true);
  },

  loadAddEngines: function BrowserSearch_loadAddEngines() {
    var newWindowPref = Services.prefs.getIntPref("browser.link.open_newwindow");
    var where = newWindowPref == 3 ? "tab" : "window";
    openUILinkIn(this.searchEnginesURL, where);
  },

  _getSearchEngineId(engine) {
    if (engine && engine.identifier) {
      return engine.identifier;
    }

    if (!engine || (engine.name === undefined))
      return "other";

    return "other-" + engine.name;
  },

  /**
   * Helper to record a search with Telemetry.
   *
   * Telemetry records only search counts and nothing pertaining to the search itself.
   *
   * @param engine
   *        (nsISearchEngine) The engine handling the search.
   * @param source
   *        (string) Where the search originated from. See BrowserUsageTelemetry for
   *        allowed values.
   * @param details [optional]
   *        An optional parameter passed to |BrowserUsageTelemetry.recordSearch|.
   *        See its documentation for allowed options.
   *        Additionally, if the search was a suggested search, |details.selection|
   *        indicates where the item was in the suggestion list and how the user
   *        selected it: {selection: {index: The selected index, kind: "key" or "mouse"}}
   */
  recordSearchInTelemetry(engine, source, details = {}) {
    BrowserUITelemetry.countSearchEvent(source, null, details.selection);
    try {
      BrowserUsageTelemetry.recordSearch(engine, source, details);
    } catch (ex) {
      Cu.reportError(ex);
    }
  },

  /**
   * Helper to record a one-off search with Telemetry.
   *
   * Telemetry records only search counts and nothing pertaining to the search itself.
   *
   * @param engine
   *        (nsISearchEngine) The engine handling the search.
   * @param source
   *        (string) Where the search originated from. See BrowserUsageTelemetry for
   *        allowed values.
   * @param type
   *        (string) Indicates how the user selected the search item.
   * @param where
   *        (string) Where was the search link opened (e.g. new tab, current tab, ..).
   */
  recordOneoffSearchInTelemetry(engine, source, type, where) {
    let id = this._getSearchEngineId(engine) + "." + source;
    BrowserUITelemetry.countOneoffSearchEvent(id, type, where);
    try {
      const details = {type, isOneOff: true};
      BrowserUsageTelemetry.recordSearch(engine, source, details);
    } catch (ex) {
      Cu.reportError(ex);
    }
  }
};

XPCOMUtils.defineConstant(this, "BrowserSearch", BrowserSearch);

function FillHistoryMenu(aParent) {
  // Lazily add the hover listeners on first showing and never remove them
  if (!aParent.hasStatusListener) {
    // Show history item's uri in the status bar when hovering, and clear on exit
    aParent.addEventListener("DOMMenuItemActive", function(aEvent) {
      // Only the current page should have the checked attribute, so skip it
      if (!aEvent.target.hasAttribute("checked"))
        XULBrowserWindow.setOverLink(aEvent.target.getAttribute("uri"));
    });
    aParent.addEventListener("DOMMenuItemInactive", function() {
      XULBrowserWindow.setOverLink("");
    });

    aParent.hasStatusListener = true;
  }

  // Remove old entries if any
  let children = aParent.childNodes;
  for (var i = children.length - 1; i >= 0; --i) {
    if (children[i].hasAttribute("index"))
      aParent.removeChild(children[i]);
  }

  const MAX_HISTORY_MENU_ITEMS = 15;

  const tooltipBack = gNavigatorBundle.getString("tabHistory.goBack");
  const tooltipCurrent = gNavigatorBundle.getString("tabHistory.current");
  const tooltipForward = gNavigatorBundle.getString("tabHistory.goForward");

  function updateSessionHistory(sessionHistory, initial) {
    let count = sessionHistory.entries.length;

    if (!initial) {
      if (count <= 1) {
        // if there is only one entry now, close the popup.
        aParent.hidePopup();
        return;
      } else if (aParent.id != "backForwardMenu" && !aParent.parentNode.open) {
        // if the popup wasn't open before, but now needs to be, reopen the menu.
        // It should trigger FillHistoryMenu again. This might happen with the
        // delay from click-and-hold menus but skip this for the context menu
        // (backForwardMenu) rather than figuring out how the menu should be
        // positioned and opened as it is an extreme edgecase.
        aParent.parentNode.open = true;
        return;
      }
    }

    let index = sessionHistory.index;
    let half_length = Math.floor(MAX_HISTORY_MENU_ITEMS / 2);
    let start = Math.max(index - half_length, 0);
    let end = Math.min(start == 0 ? MAX_HISTORY_MENU_ITEMS : index + half_length + 1, count);
    if (end == count) {
      start = Math.max(count - MAX_HISTORY_MENU_ITEMS, 0);
    }

    let existingIndex = 0;

    for (let j = end - 1; j >= start; j--) {
      let entry = sessionHistory.entries[j];
      let uri = entry.url;

      let item = existingIndex < children.length ?
                   children[existingIndex] : document.createElement("menuitem");

      item.setAttribute("uri", uri);
      item.setAttribute("label", entry.title || uri);
      item.setAttribute("index", j);

      // Cache this so that gotoHistoryIndex doesn't need the original index
      item.setAttribute("historyindex", j - index);

      if (j != index) {
        // Use list-style-image rather than the image attribute in order to
        // allow CSS to override this.
        item.style.listStyleImage = `url(page-icon:${uri})`;
      }

      if (j < index) {
        item.className = "unified-nav-back menuitem-iconic menuitem-with-favicon";
        item.setAttribute("tooltiptext", tooltipBack);
      } else if (j == index) {
        item.setAttribute("type", "radio");
        item.setAttribute("checked", "true");
        item.className = "unified-nav-current";
        item.setAttribute("tooltiptext", tooltipCurrent);
      } else {
        item.className = "unified-nav-forward menuitem-iconic menuitem-with-favicon";
        item.setAttribute("tooltiptext", tooltipForward);
      }

      if (!item.parentNode) {
        aParent.appendChild(item);
      }

      existingIndex++;
    }

    if (!initial) {
      let existingLength = children.length;
      while (existingIndex < existingLength) {
        aParent.removeChild(aParent.lastChild);
        existingIndex++;
      }
    }
  }

  let sessionHistory = SessionStore.getSessionHistory(gBrowser.selectedTab, updateSessionHistory);
  if (!sessionHistory)
    return false;

  // don't display the popup for a single item
  if (sessionHistory.entries.length <= 1)
    return false;

  updateSessionHistory(sessionHistory, true);
  return true;
}

function addToUrlbarHistory(aUrlToAdd) {
  if (!PrivateBrowsingUtils.isWindowPrivate(window) &&
      aUrlToAdd &&
      !aUrlToAdd.includes(" ") &&
      !/[\x00-\x1F]/.test(aUrlToAdd)) // eslint-disable-line no-control-regex
    PlacesUIUtils.markPageAsTyped(aUrlToAdd);
}

function BrowserDownloadsUI() {
  if (PrivateBrowsingUtils.isWindowPrivate(window)) {
    openUILinkIn("about:downloads", "tab");
  } else {
    PlacesCommandHook.showPlacesOrganizer("Downloads");
  }
}

function toOpenWindowByType(inType, uri, features) {
  var topWindow = Services.wm.getMostRecentWindow(inType);

  if (topWindow)
    topWindow.focus();
  else if (features)
    window.open(uri, "_blank", features);
  else
    window.open(uri, "_blank", "chrome,extrachrome,menubar,resizable,scrollbars,status,toolbar");
}

function OpenBrowserWindow(options) {
  var telemetryObj = {};
  TelemetryStopwatch.start("FX_NEW_WINDOW_MS", telemetryObj);

  var handler = Cc["@mozilla.org/browser/clh;1"]
                  .getService(Ci.nsIBrowserHandler);
  var defaultArgs = handler.defaultArgs;
  var wintype = document.documentElement.getAttribute("windowtype");

  var extraFeatures = "";
  if (options && options.private && PrivateBrowsingUtils.enabled) {
    extraFeatures = ",private";
    if (!PrivateBrowsingUtils.permanentPrivateBrowsing) {
      // Force the new window to load about:privatebrowsing instead of the default home page
      defaultArgs = "about:privatebrowsing";
    }
  } else {
    extraFeatures = ",non-private";
  }

  if (options && options.remote) {
    extraFeatures += ",remote";
  } else if (options && options.remote === false) {
    extraFeatures += ",non-remote";
  }

  // If the window is maximized, we want to skip the animation, since we're
  // going to be taking up most of the screen anyways, and we want to optimize
  // for showing the user a useful window as soon as possible.
  if (window.windowState == window.STATE_MAXIMIZED) {
    extraFeatures += ",suppressanimation";
  }

  // if and only if the current window is a browser window and it has a document with a character
  // set, then extract the current charset menu setting from the current document and use it to
  // initialize the new browser window...
  var win;
  if (window && (wintype == "navigator:browser") && window.content && window.content.document) {
    var DocCharset = window.content.document.characterSet;
    let charsetArg = "charset=" + DocCharset;

    // we should "inherit" the charset menu setting in a new window
    win = window.openDialog("chrome://browser/content/", "_blank", "chrome,all,dialog=no" + extraFeatures, defaultArgs, charsetArg);
  } else {
    // forget about the charset information.
    win = window.openDialog("chrome://browser/content/", "_blank", "chrome,all,dialog=no" + extraFeatures, defaultArgs);
  }

  win.addEventListener("MozAfterPaint", () => {
    TelemetryStopwatch.finish("FX_NEW_WINDOW_MS", telemetryObj);
  }, {once: true});

  return win;
}

/**
 * Update the global flag that tracks whether or not any edit UI (the Edit menu,
 * edit-related items in the context menu, and edit-related toolbar buttons
 * is visible, then update the edit commands' enabled state accordingly.  We use
 * this flag to skip updating the edit commands on focus or selection changes
 * when no UI is visible to improve performance (including pageload performance,
 * since focus changes when you load a new page).
 *
 * If UI is visible, we use goUpdateGlobalEditMenuItems to set the commands'
 * enabled state so the UI will reflect it appropriately.
 *
 * If the UI isn't visible, we enable all edit commands so keyboard shortcuts
 * still work and just lazily disable them as needed when the user presses a
 * shortcut.
 *
 * This doesn't work on Mac, since Mac menus flash when users press their
 * keyboard shortcuts, so edit UI is essentially always visible on the Mac,
 * and we need to always update the edit commands.  Thus on Mac this function
 * is a no op.
 */
function updateEditUIVisibility() {
  if (AppConstants.platform == "macosx")
    return;

  let editMenuPopupState = document.getElementById("menu_EditPopup").state;
  let contextMenuPopupState = document.getElementById("contentAreaContextMenu").state;
  let placesContextMenuPopupState = document.getElementById("placesContext").state;

  let oldVisible = gEditUIVisible;

  // The UI is visible if the Edit menu is opening or open, if the context menu
  // is open, or if the toolbar has been customized to include the Cut, Copy,
  // or Paste toolbar buttons.
  gEditUIVisible = editMenuPopupState == "showing" ||
                   editMenuPopupState == "open" ||
                   contextMenuPopupState == "showing" ||
                   contextMenuPopupState == "open" ||
                   placesContextMenuPopupState == "showing" ||
                   placesContextMenuPopupState == "open";
  const kOpenPopupStates = ["showing", "open"];
  if (!gEditUIVisible) {
    // Now check the edit-controls toolbar buttons.
    let placement = CustomizableUI.getPlacementOfWidget("edit-controls");
    let areaType = placement ? CustomizableUI.getAreaType(placement.area) : "";
    if (areaType == CustomizableUI.TYPE_MENU_PANEL) {
      let customizablePanel = PanelUI.overflowPanel;
      gEditUIVisible = kOpenPopupStates.includes(customizablePanel.state);
    } else if (areaType == CustomizableUI.TYPE_TOOLBAR && window.toolbar.visible) {
      // The edit controls are on a toolbar, so they are visible,
      // unless they're in a panel that isn't visible...
      if (placement.area == "nav-bar") {
        let editControls = document.getElementById("edit-controls");
        gEditUIVisible = !editControls.hasAttribute("overflowedItem") ||
                          kOpenPopupStates.includes(document.getElementById("widget-overflow").state);
      } else {
        gEditUIVisible = true;
      }
    }
  }

  // Now check the main menu panel
  if (!gEditUIVisible) {
    gEditUIVisible = kOpenPopupStates.includes(PanelUI.panel.state);
  }

  // No need to update commands if the edit UI visibility has not changed.
  if (gEditUIVisible == oldVisible) {
    return;
  }

  // If UI is visible, update the edit commands' enabled state to reflect
  // whether or not they are actually enabled for the current focus/selection.
  if (gEditUIVisible) {
    goUpdateGlobalEditMenuItems();
  } else {
    // Otherwise, enable all commands, so that keyboard shortcuts still work,
    // then lazily determine their actual enabled state when the user presses
    // a keyboard shortcut.
    goSetCommandEnabled("cmd_undo", true);
    goSetCommandEnabled("cmd_redo", true);
    goSetCommandEnabled("cmd_cut", true);
    goSetCommandEnabled("cmd_copy", true);
    goSetCommandEnabled("cmd_paste", true);
    goSetCommandEnabled("cmd_selectAll", true);
    goSetCommandEnabled("cmd_delete", true);
    goSetCommandEnabled("cmd_switchTextDirection", true);
  }
}

/**
 * Opens a new tab with the userContextId specified as an attribute of
 * sourceEvent. This attribute is propagated to the top level originAttributes
 * living on the tab's docShell.
 *
 * @param event
 *        A click event on a userContext File Menu option
 */
function openNewUserContextTab(event) {
  openUILinkIn(BROWSER_NEW_TAB_URL, "tab", {
    userContextId: parseInt(event.target.getAttribute("data-usercontextid")),
  });
}

/**
 * Updates File Menu User Context UI visibility depending on
 * privacy.userContext.enabled pref state.
 */
function updateUserContextUIVisibility() {
  let menu = document.getElementById("menu_newUserContext");
  menu.hidden = !Services.prefs.getBoolPref("privacy.userContext.enabled");
  if (PrivateBrowsingUtils.isWindowPrivate(window)) {
    menu.setAttribute("disabled", "true");
  }
}

/**
 * Updates the User Context UI indicators if the browser is in a non-default context
 */
function updateUserContextUIIndicator() {
  let hbox = document.getElementById("userContext-icons");

  let userContextId = gBrowser.selectedBrowser.getAttribute("usercontextid");
  if (!userContextId) {
    hbox.setAttribute("data-identity-color", "");
    hbox.hidden = true;
    return;
  }

  let identity = ContextualIdentityService.getPublicIdentityFromId(userContextId);
  if (!identity) {
    hbox.setAttribute("data-identity-color", "");
    hbox.hidden = true;
    return;
  }

  hbox.setAttribute("data-identity-color", identity.color);

  let label = document.getElementById("userContext-label");
  label.setAttribute("value", ContextualIdentityService.getUserContextLabel(userContextId));

  let indicator = document.getElementById("userContext-indicator");
  indicator.setAttribute("data-identity-icon", identity.icon);

  hbox.hidden = false;
}

/**
 * Makes the Character Encoding menu enabled or disabled as appropriate.
 * To be called when the View menu or the app menu is opened.
 */
function updateCharacterEncodingMenuState() {
  let charsetMenu = document.getElementById("charsetMenu");
  // gBrowser is null on Mac when the menubar shows in the context of
  // non-browser windows. The above elements may be null depending on
  // what parts of the menubar are present. E.g. no app menu on Mac.
  if (gBrowser && gBrowser.selectedBrowser.mayEnableCharacterEncodingMenu) {
    if (charsetMenu) {
      charsetMenu.removeAttribute("disabled");
    }
  } else if (charsetMenu) {
    charsetMenu.setAttribute("disabled", "true");
  }
}

var XULBrowserWindow = {
  // Stored Status, Link and Loading values
  status: "",
  defaultStatus: "",
  overLink: "",
  startTime: 0,
  statusText: "",
  isBusy: false,
  // Left here for add-on compatibility, see bug 752434
  inContentWhitelist: [],

  QueryInterface(aIID) {
    if (aIID.equals(Ci.nsIWebProgressListener) ||
        aIID.equals(Ci.nsIWebProgressListener2) ||
        aIID.equals(Ci.nsISupportsWeakReference) ||
        aIID.equals(Ci.nsIXULBrowserWindow) ||
        aIID.equals(Ci.nsISupports))
      return this;
    throw Cr.NS_NOINTERFACE;
  },

  get stopCommand() {
    delete this.stopCommand;
    return this.stopCommand = document.getElementById("Browser:Stop");
  },
  get reloadCommand() {
    delete this.reloadCommand;
    return this.reloadCommand = document.getElementById("Browser:Reload");
  },
  get statusTextField() {
    return gBrowser.getStatusPanel();
  },
  get isImage() {
    delete this.isImage;
    return this.isImage = document.getElementById("isImage");
  },
  get canViewSource() {
    delete this.canViewSource;
    return this.canViewSource = document.getElementById("canViewSource");
  },

  setJSStatus() {
    // unsupported
  },

  forceInitialBrowserRemote(aRemoteType) {
    gBrowser.updateBrowserRemoteness(gBrowser.initialBrowser, true, { remoteType: aRemoteType });
  },

  forceInitialBrowserNonRemote(aOpener) {
    gBrowser.updateBrowserRemoteness(gBrowser.initialBrowser, false, { opener: aOpener });
  },

  setDefaultStatus(status) {
    this.defaultStatus = status;
    this.updateStatusField();
  },

  setOverLink(url, anchorElt) {
    if (url) {
      url = Services.textToSubURI.unEscapeURIForUI("UTF-8", url);

      // Encode bidirectional formatting characters.
      // (RFC 3987 sections 3.2 and 4.1 paragraph 6)
      url = url.replace(/[\u200e\u200f\u202a\u202b\u202c\u202d\u202e]/g,
                        encodeURIComponent);

      if (gURLBar && gURLBar._mayTrimURLs /* corresponds to browser.urlbar.trimURLs */)
        url = trimURL(url);
    }

    this.overLink = url;
    LinkTargetDisplay.update();
  },

  showTooltip(x, y, tooltip, direction, browser) {
    if (Cc["@mozilla.org/widget/dragservice;1"].getService(Ci.nsIDragService).
        getCurrentSession()) {
      return;
    }

    // The x,y coordinates are relative to the <browser> element using
    // the chrome zoom level.
    let elt = document.getElementById("remoteBrowserTooltip");
    elt.label = tooltip;
    elt.style.direction = direction;

    elt.openPopupAtScreen(browser.boxObject.screenX + x, browser.boxObject.screenY + y, false, null);
  },

  hideTooltip() {
    let elt = document.getElementById("remoteBrowserTooltip");
    elt.hidePopup();
  },

  getTabCount() {
    return gBrowser.tabs.length;
  },

  updateStatusField() {
    var text, type, types = ["overLink"];
    if (this._busyUI)
      types.push("status");
    types.push("defaultStatus");
    for (type of types) {
      text = this[type];
      if (text)
        break;
    }

    // check the current value so we don't trigger an attribute change
    // and cause needless (slow!) UI updates
    if (this.statusText != text) {
      let field = this.statusTextField;
      field.setAttribute("previoustype", field.getAttribute("type"));
      field.setAttribute("type", type);
      field.label = text;
      field.setAttribute("crop", type == "overLink" ? "center" : "end");
      this.statusText = text;
    }
  },

  // Called before links are navigated to to allow us to retarget them if needed.
  onBeforeLinkTraversal(originalTarget, linkURI, linkNode, isAppTab) {
    return BrowserUtils.onBeforeLinkTraversal(originalTarget, linkURI, linkNode, isAppTab);
  },

  // Check whether this URI should load in the current process
  shouldLoadURI(aDocShell, aURI, aReferrer, aHasPostData, aTriggeringPrincipal) {
    if (!gMultiProcessBrowser)
      return true;

    let browser = aDocShell.QueryInterface(Ci.nsIDocShellTreeItem)
                           .sameTypeRootTreeItem
                           .QueryInterface(Ci.nsIDocShell)
                           .chromeEventHandler;

    // Ignore loads that aren't in the main tabbrowser
    if (browser.localName != "browser" || !browser.getTabBrowser || browser.getTabBrowser() != gBrowser)
      return true;

    if (!E10SUtils.shouldLoadURI(aDocShell, aURI, aReferrer, aHasPostData)) {
      // XXX: Do we want to complain if we have post data but are still
      // redirecting the load? Perhaps a telemetry probe? Theoretically we
      // shouldn't do this, as it throws out data. See bug 1348018.
      E10SUtils.redirectLoad(aDocShell, aURI, aReferrer, aTriggeringPrincipal, false);
      return false;
    }

    return true;
  },

  onProgressChange(aWebProgress, aRequest,
                             aCurSelfProgress, aMaxSelfProgress,
                             aCurTotalProgress, aMaxTotalProgress) {
    // Do nothing.
  },

  onProgressChange64(aWebProgress, aRequest,
                               aCurSelfProgress, aMaxSelfProgress,
                               aCurTotalProgress, aMaxTotalProgress) {
    return this.onProgressChange(aWebProgress, aRequest,
      aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress,
      aMaxTotalProgress);
  },

  // This function fires only for the currently selected tab.
  onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
    const nsIWebProgressListener = Ci.nsIWebProgressListener;
    const nsIChannel = Ci.nsIChannel;

    let browser = gBrowser.selectedBrowser;

    if (aStateFlags & nsIWebProgressListener.STATE_START &&
        aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {

      if (aRequest && aWebProgress.isTopLevel) {
        // clear out feed data
        browser.feeds = null;

        // clear out search-engine data
        browser.engines = null;
      }

      this.isBusy = true;

      if (!(aStateFlags & nsIWebProgressListener.STATE_RESTORING)) {
        this._busyUI = true;

        // XXX: This needs to be based on window activity...
        this.stopCommand.removeAttribute("disabled");
        CombinedStopReload.switchToStop(aRequest, aWebProgress);
      }
    } else if (aStateFlags & nsIWebProgressListener.STATE_STOP) {
      // This (thanks to the filter) is a network stop or the last
      // request stop outside of loading the document, stop throbbers
      // and progress bars and such
      if (aRequest) {
        let msg = "";
        let location;
        let canViewSource = true;
        // Get the URI either from a channel or a pseudo-object
        if (aRequest instanceof nsIChannel || "URI" in aRequest) {
          location = aRequest.URI;

          // For keyword URIs clear the user typed value since they will be changed into real URIs
          if (location.scheme == "keyword" && aWebProgress.isTopLevel)
            gBrowser.userTypedValue = null;

          canViewSource = location.scheme != "view-source";

          if (location.spec != "about:blank") {
            switch (aStatus) {
              case Cr.NS_ERROR_NET_TIMEOUT:
                msg = gNavigatorBundle.getString("nv_timeout");
                break;
            }
          }
        }

        this.status = "";
        this.setDefaultStatus(msg);

        // Disable menu entries for images, enable otherwise
        if (browser.documentContentType && BrowserUtils.mimeTypeIsTextBased(browser.documentContentType)) {
          this.isImage.removeAttribute("disabled");
        } else {
          canViewSource = false;
          this.isImage.setAttribute("disabled", "true");
        }

        if (canViewSource) {
          this.canViewSource.removeAttribute("disabled");
        } else {
          this.canViewSource.setAttribute("disabled", "true");
        }
      }

      this.isBusy = false;

      if (this._busyUI) {
        this._busyUI = false;

        this.stopCommand.setAttribute("disabled", "true");
        CombinedStopReload.switchToReload(aRequest, aWebProgress);
      }
    }
  },

  onLocationChange(aWebProgress, aRequest, aLocationURI, aFlags) {
    var location = aLocationURI ? aLocationURI.spec : "";

    let pageTooltip = document.getElementById("aHTMLTooltip");
    let tooltipNode = pageTooltip.triggerNode;
    if (tooltipNode) {
      // Optimise for the common case
      if (aWebProgress.isTopLevel) {
        pageTooltip.hidePopup();
      } else {
        for (let tooltipWindow = tooltipNode.ownerGlobal;
             tooltipWindow != tooltipWindow.parent;
             tooltipWindow = tooltipWindow.parent) {
          if (tooltipWindow == aWebProgress.DOMWindow) {
            pageTooltip.hidePopup();
            break;
          }
        }
      }
    }

    let browser = gBrowser.selectedBrowser;

    // Disable menu entries for images, enable otherwise
    if (browser.documentContentType && BrowserUtils.mimeTypeIsTextBased(browser.documentContentType))
      this.isImage.removeAttribute("disabled");
    else
      this.isImage.setAttribute("disabled", "true");

    this.hideOverLinkImmediately = true;
    this.setOverLink("", null);
    this.hideOverLinkImmediately = false;

    // We should probably not do this if the value has changed since the user
    // searched
    // Update urlbar only if a new page was loaded on the primary content area
    // Do not update urlbar if there was a subframe navigation

    if (aWebProgress.isTopLevel) {
      if ((location == "about:blank" && checkEmptyPageOrigin()) ||
          location == "") { // Second condition is for new tabs, otherwise
                             // reload function is enabled until tab is refreshed.
        this.reloadCommand.setAttribute("disabled", "true");
      } else {
        this.reloadCommand.removeAttribute("disabled");
      }

      URLBarSetURI(aLocationURI);

      BookmarkingUI.onLocationChange();

      gIdentityHandler.onLocationChange();

      BrowserPageActions.onLocationChange();

      SafeBrowsingNotificationBox.onLocationChange(aLocationURI);

      gTabletModePageCounter.inc();

      // Utility functions for disabling find
      var shouldDisableFind = function(aDocument) {
        let docElt = aDocument.documentElement;
        return docElt && docElt.getAttribute("disablefastfind") == "true";
      };

      var disableFindCommands = function(aDisable) {
        let findCommands = [document.getElementById("cmd_find"),
                            document.getElementById("cmd_findAgain"),
                            document.getElementById("cmd_findPrevious")];
        for (let elt of findCommands) {
          if (aDisable)
            elt.setAttribute("disabled", "true");
          else
            elt.removeAttribute("disabled");
        }
      };

      var onContentRSChange = function(e) {
        if (e.target.readyState != "interactive" && e.target.readyState != "complete")
          return;

        e.target.removeEventListener("readystatechange", onContentRSChange);
        disableFindCommands(shouldDisableFind(e.target));
      };

      // Disable find commands in documents that ask for them to be disabled.
      if (!gMultiProcessBrowser && aLocationURI &&
          (aLocationURI.schemeIs("about") || aLocationURI.schemeIs("chrome"))) {
        // Don't need to re-enable/disable find commands for same-document location changes
        // (e.g. the replaceStates in about:addons)
        if (!(aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT)) {
          if (window.content.document.readyState == "interactive" || window.content.document.readyState == "complete")
            disableFindCommands(shouldDisableFind(window.content.document));
          else {
            window.content.document.addEventListener("readystatechange", onContentRSChange);
          }
        }
      } else
        disableFindCommands(false);

      // Try not to instantiate gCustomizeMode as much as possible,
      // so don't use CustomizeMode.jsm to check for URI or customizing.
      if (location == "about:blank" &&
          gBrowser.selectedTab.hasAttribute("customizemode")) {
        gCustomizeMode.enter();
      } else if (CustomizationHandler.isEnteringCustomizeMode ||
                 CustomizationHandler.isCustomizing()) {
        gCustomizeMode.exit();
      }
    }
    UpdateBackForwardCommands(gBrowser.webNavigation);
    ReaderParent.updateReaderButton(gBrowser.selectedBrowser);

    if (!gMultiProcessBrowser) // Bug 1108553 - Cannot rotate images with e10s
      gGestureSupport.restoreRotationState();

    // See bug 358202, when tabs are switched during a drag operation,
    // timers don't fire on windows (bug 203573)
    if (aRequest)
      setTimeout(function() { XULBrowserWindow.asyncUpdateUI(); }, 0);
    else
      this.asyncUpdateUI();

    if (AppConstants.MOZ_CRASHREPORTER && aLocationURI) {
      let uri = aLocationURI;
      try {
        // If the current URI contains a username/password, remove it.
        uri = aLocationURI.mutate()
                          .setUserPass("")
                          .finalize();
      } catch (ex) { /* Ignore failures on about: URIs. */ }

      try {
        gCrashReporter.annotateCrashReport("URL", uri.spec);
      } catch (ex) {
        // Don't make noise when the crash reporter is built but not enabled.
        if (ex.result != Cr.NS_ERROR_NOT_INITIALIZED) {
          throw ex;
        }
      }
    }
  },

  asyncUpdateUI() {
    FeedHandler.updateFeeds();
    BrowserSearch.updateOpenSearchBadge();
  },

  // Left here for add-on compatibility, see bug 752434
  hideChromeForLocation() {},

  onStatusChange(aWebProgress, aRequest, aStatus, aMessage) {
    this.status = aMessage;
    this.updateStatusField();
  },

  // Properties used to cache security state used to update the UI
  _state: null,
  _lastLocation: null,

  // This is called in multiple ways:
  //  1. Due to the nsIWebProgressListener.onSecurityChange notification.
  //  2. Called by tabbrowser.xml when updating the current browser.
  //  3. Called directly during this object's initializations.
  // aRequest will be null always in case 2 and 3, and sometimes in case 1 (for
  // instance, there won't be a request when STATE_BLOCKED_TRACKING_CONTENT is observed).
  onSecurityChange(aWebProgress, aRequest, aState, aIsSimulated) {
    // Don't need to do anything if the data we use to update the UI hasn't
    // changed
    let uri = gBrowser.currentURI;
    let spec = uri.spec;
    if (this._state == aState &&
        this._lastLocation == spec) {
      // Switching to a tab of the same URL doesn't change most security
      // information, but tab specific permissions may be different.
      gIdentityHandler.refreshIdentityBlock();
      return;
    }
    this._state = aState;
    this._lastLocation = spec;

    if (typeof(aIsSimulated) != "boolean" && typeof(aIsSimulated) != "undefined") {
      throw "onSecurityChange: aIsSimulated receieved an unexpected type";
    }

    // Make sure the "https" part of the URL is striked out or not,
    // depending on the current mixed active content blocking state.
    gURLBar.formatValue();

    try {
      uri = Services.uriFixup.createExposableURI(uri);
    } catch (e) {}
    gIdentityHandler.updateIdentity(this._state, uri);
    TrackingProtection.onSecurityChange(this._state, aIsSimulated);
  },

  // simulate all change notifications after switching tabs
  onUpdateCurrentBrowser: function XWB_onUpdateCurrentBrowser(aStateFlags, aStatus, aMessage, aTotalProgress) {
    if (FullZoom.updateBackgroundTabs)
      FullZoom.onLocationChange(gBrowser.currentURI, true);

    CombinedStopReload.onTabSwitch();

    var nsIWebProgressListener = Ci.nsIWebProgressListener;
    var loadingDone = aStateFlags & nsIWebProgressListener.STATE_STOP;
    // use a pseudo-object instead of a (potentially nonexistent) channel for getting
    // a correct error message - and make sure that the UI is always either in
    // loading (STATE_START) or done (STATE_STOP) mode
    this.onStateChange(
      gBrowser.webProgress,
      { URI: gBrowser.currentURI },
      loadingDone ? nsIWebProgressListener.STATE_STOP : nsIWebProgressListener.STATE_START,
      aStatus
    );
    // status message and progress value are undefined if we're done with loading
    if (loadingDone)
      return;
    this.onStatusChange(gBrowser.webProgress, null, 0, aMessage);
  },

  navigateAndRestoreByIndex: function XWB_navigateAndRestoreByIndex(aBrowser, aIndex) {
    let tab = gBrowser.getTabForBrowser(aBrowser);
    if (tab) {
      SessionStore.navigateAndRestore(tab, {}, aIndex);
      return;
    }

    throw new Error("Trying to navigateAndRestore a browser which was " +
                    "not attached to this tabbrowser is unsupported");
  }
};

var LinkTargetDisplay = {
  get DELAY_SHOW() {
     delete this.DELAY_SHOW;
     return this.DELAY_SHOW = Services.prefs.getIntPref("browser.overlink-delay");
  },

  DELAY_HIDE: 250,
  _timer: 0,

  get _isVisible() {
    return XULBrowserWindow.statusTextField.label != "";
  },

  update() {
    clearTimeout(this._timer);
    window.removeEventListener("mousemove", this, true);

    if (!XULBrowserWindow.overLink) {
      if (XULBrowserWindow.hideOverLinkImmediately)
        this._hide();
      else
        this._timer = setTimeout(this._hide.bind(this), this.DELAY_HIDE);
      return;
    }

    if (this._isVisible) {
      XULBrowserWindow.updateStatusField();
    } else {
      // Let the display appear when the mouse doesn't move within the delay
      this._showDelayed();
      window.addEventListener("mousemove", this, true);
    }
  },

  handleEvent(event) {
    switch (event.type) {
      case "mousemove":
        // Restart the delay since the mouse was moved
        clearTimeout(this._timer);
        this._showDelayed();
        break;
    }
  },

  _showDelayed() {
    this._timer = setTimeout(function(self) {
      XULBrowserWindow.updateStatusField();
      window.removeEventListener("mousemove", self, true);
    }, this.DELAY_SHOW, this);
  },

  _hide() {
    clearTimeout(this._timer);

    XULBrowserWindow.updateStatusField();
  }
};

var CombinedStopReload = {
  // Try to initialize. Returns whether initialization was successful, which
  // may mean we had already initialized.
  ensureInitialized() {
    if (this._initialized)
      return true;
    if (this._destroyed)
      return false;

    let reload = document.getElementById("reload-button");
    let stop = document.getElementById("stop-button");
    // It's possible the stop/reload buttons have been moved to the palette.
    // They may be reinserted later, so we will retry initialization if/when
    // we get notified of document loads.
    if (!stop || !reload)
      return false;

    this._initialized = true;
    if (XULBrowserWindow.stopCommand.getAttribute("disabled") != "true")
      reload.setAttribute("displaystop", "true");
    stop.addEventListener("click", this);

    // Removing attributes based on the observed command doesn't happen if the button
    // is in the palette when the command's attribute is removed (cf. bug 309953)
    for (let button of [stop, reload]) {
      if (button.hasAttribute("disabled")) {
        let command = document.getElementById(button.getAttribute("command"));
        if (!command.hasAttribute("disabled")) {
          button.removeAttribute("disabled");
        }
      }
    }

    this.reload = reload;
    this.stop = stop;
    this.stopReloadContainer = this.reload.parentNode;
    this.timeWhenSwitchedToStop = 0;

    if (this._shouldStartPrefMonitoring) {
      this.startAnimationPrefMonitoring();
    }
    return true;
  },

  uninit() {
    this._destroyed = true;

    if (!this._initialized)
      return;

    Services.prefs.removeObserver("toolkit.cosmeticAnimations.enabled", this);
    this._cancelTransition();
    this.stop.removeEventListener("click", this);
    this.stopReloadContainer.removeEventListener("animationend", this);
    this.stopReloadContainer = null;
    this.reload = null;
    this.stop = null;
  },

  handleEvent(event) {
    switch (event.type) {
      case "click":
        if (event.button == 0 &&
            !this.stop.disabled) {
          this._stopClicked = true;
        }
      case "animationend": {
        if (event.target.classList.contains("toolbarbutton-animatable-image") &&
            (event.animationName == "reload-to-stop" ||
             event.animationName == "stop-to-reload" ||
             event.animationName == "reload-to-stop-rtl" ||
             event.animationName == "stop-to-reload-rtl")) {
          this.stopReloadContainer.removeAttribute("animate");
        }
      }
    }
  },

  observe(subject, topic, data) {
    if (topic == "nsPref:changed") {
      this.animate = Services.prefs.getBoolPref("toolkit.cosmeticAnimations.enabled");
    }
  },

  startAnimationPrefMonitoring() {
    // CombinedStopReload may have been uninitialized before the idleCallback is executed.
    if (this._destroyed)
      return;
    if (!this.ensureInitialized()) {
      this._shouldStartPrefMonitoring = true;
      return;
    }
    this.animate = Services.prefs.getBoolPref("toolkit.cosmeticAnimations.enabled") &&
                   Services.prefs.getBoolPref("browser.stopReloadAnimation.enabled");
    Services.prefs.addObserver("toolkit.cosmeticAnimations.enabled", this);
    this.stopReloadContainer.addEventListener("animationend", this);
  },

  onTabSwitch() {
    // Reset the time in the event of a tabswitch since the stored time
    // would have been associated with the previous tab, so the animation will
    // still run if the page has been loading until long after the tab switch.
    this.timeWhenSwitchedToStop = window.performance.now();
  },

  switchToStop(aRequest, aWebProgress) {
    if (!this.ensureInitialized() || !this._shouldSwitch(aRequest, aWebProgress)) {
      return;
    }

    // Store the time that we switched to the stop button only if a request
    // is active. Requests are null if the switch is related to a tabswitch.
    // This is used to determine if we should show the stop->reload animation.
    if (aRequest instanceof Ci.nsIRequest) {
      this.timeWhenSwitchedToStop = window.performance.now();
    }

    let shouldAnimate = aRequest instanceof Ci.nsIRequest &&
                        aWebProgress.isTopLevel &&
                        aWebProgress.isLoadingDocument &&
                        !gBrowser.tabAnimationsInProgress &&
                        this.stopReloadContainer.closest("#nav-bar-customization-target") &&
                        this.animate;

    this._cancelTransition();
    if (shouldAnimate) {
      BrowserUtils.setToolbarButtonHeightProperty(this.stopReloadContainer);
      this.stopReloadContainer.setAttribute("animate", "true");
    } else {
      this.stopReloadContainer.removeAttribute("animate");
    }
    this.reload.setAttribute("displaystop", "true");
  },

  switchToReload(aRequest, aWebProgress) {
    if (!this.ensureInitialized() || !this.reload.hasAttribute("displaystop")) {
      return;
    }

    let shouldAnimate = aRequest instanceof Ci.nsIRequest &&
                        aWebProgress.isTopLevel &&
                        !aWebProgress.isLoadingDocument &&
                        !gBrowser.tabAnimationsInProgress &&
                        this._loadTimeExceedsMinimumForAnimation() &&
                        this.stopReloadContainer.closest("#nav-bar-customization-target") &&
                        this.animate;

    if (shouldAnimate) {
      BrowserUtils.setToolbarButtonHeightProperty(this.stopReloadContainer);
      this.stopReloadContainer.setAttribute("animate", "true");
    } else {
      this.stopReloadContainer.removeAttribute("animate");
    }

    this.reload.removeAttribute("displaystop");

    if (!shouldAnimate || this._stopClicked) {
      this._stopClicked = false;
      this._cancelTransition();
      this.reload.disabled = XULBrowserWindow.reloadCommand
                                             .getAttribute("disabled") == "true";
      return;
    }

    if (this._timer)
      return;

    // Temporarily disable the reload button to prevent the user from
    // accidentally reloading the page when intending to click the stop button
    this.reload.disabled = true;
    this._timer = setTimeout(function(self) {
      self._timer = 0;
      self.reload.disabled = XULBrowserWindow.reloadCommand
                                             .getAttribute("disabled") == "true";
    }, 650, this);
  },

  _loadTimeExceedsMinimumForAnimation() {
    // If the time between switching to the stop button then switching to
    // the reload button exceeds 150ms, then we will show the animation.
    // If we don't know when we switched to stop (switchToStop is called
    // after init but before switchToReload), then we will prevent the
    // animation from occuring.
    return this.timeWhenSwitchedToStop &&
           window.performance.now() - this.timeWhenSwitchedToStop > 150;
  },

  _shouldSwitch(aRequest, aWebProgress) {
    if (aRequest &&
        aRequest.originalURI &&
        (aRequest.originalURI.schemeIs("chrome") ||
         (aRequest.originalURI.schemeIs("about") &&
          aWebProgress.isTopLevel &&
          !aRequest.originalURI.spec.startsWith("about:reader")))) {
      return false;
    }

    return true;
  },

  _cancelTransition() {
    if (this._timer) {
      clearTimeout(this._timer);
      this._timer = 0;
    }
  }
};

var TabsProgressListener = {
  onStateChange(aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
    // Collect telemetry data about tab load times.
    if (aWebProgress.isTopLevel && (!aRequest.originalURI || aRequest.originalURI.spec.scheme != "about")) {
      let stopwatchRunning = TelemetryStopwatch.running("FX_PAGE_LOAD_MS", aBrowser);

      if (aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW) {
        if (aStateFlags & Ci.nsIWebProgressListener.STATE_START) {
          if (stopwatchRunning) {
            // Oops, we're seeing another start without having noticed the previous stop.
            TelemetryStopwatch.cancel("FX_PAGE_LOAD_MS", aBrowser);
          }
          TelemetryStopwatch.start("FX_PAGE_LOAD_MS", aBrowser);
          Services.telemetry.getHistogramById("FX_TOTAL_TOP_VISITS").add(true);
        } else if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
                   stopwatchRunning /* we won't see STATE_START events for pre-rendered tabs */) {
          TelemetryStopwatch.finish("FX_PAGE_LOAD_MS", aBrowser);
        }
      } else if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
                 aStatus == Cr.NS_BINDING_ABORTED &&
                 stopwatchRunning /* we won't see STATE_START events for pre-rendered tabs */) {
        TelemetryStopwatch.cancel("FX_PAGE_LOAD_MS", aBrowser);
      }
    }

    // We used to listen for clicks in the browser here, but when that
    // became unnecessary, removing the code below caused focus issues.
    // This code should be removed. Tracked in bug 1337794.
    let isRemoteBrowser = aBrowser.isRemoteBrowser;
    // We check isRemoteBrowser here to avoid requesting the doc CPOW
    let doc = isRemoteBrowser ? null : aWebProgress.DOMWindow.document;

    if (!isRemoteBrowser &&
        aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
        Components.isSuccessCode(aStatus) &&
        doc.documentURI.startsWith("about:") &&
        !doc.documentURI.toLowerCase().startsWith("about:blank") &&
        !doc.documentURI.toLowerCase().startsWith("about:home") &&
        !doc.documentElement.hasAttribute("hasBrowserHandlers")) {
      // STATE_STOP may be received twice for documents, thus store an
      // attribute to ensure handling it just once.
      doc.documentElement.setAttribute("hasBrowserHandlers", "true");
      aBrowser.addEventListener("pagehide", function onPageHide(event) {
        if (event.target.defaultView.frameElement)
          return;
        aBrowser.removeEventListener("pagehide", onPageHide, true);
        if (event.target.documentElement)
          event.target.documentElement.removeAttribute("hasBrowserHandlers");
      }, true);
    }
  },

  onLocationChange(aBrowser, aWebProgress, aRequest, aLocationURI,
                             aFlags) {
    // Filter out location changes caused by anchor navigation
    // or history.push/pop/replaceState.
    if (aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT) {
      // Reader mode actually cares about these:
      let mm = gBrowser.selectedBrowser.messageManager;
      mm.sendAsyncMessage("Reader:PushState", {isArticle: gBrowser.selectedBrowser.isArticle});
      return;
    }

    // Filter out location changes in sub documents.
    if (!aWebProgress.isTopLevel)
      return;

    // Only need to call locationChange if the PopupNotifications object
    // for this window has already been initialized (i.e. its getter no
    // longer exists)
    if (!Object.getOwnPropertyDescriptor(window, "PopupNotifications").get)
      PopupNotifications.locationChange(aBrowser);

    let tab = gBrowser.getTabForBrowser(aBrowser);
    if (tab && tab._sharingState) {
      gBrowser.setBrowserSharing(aBrowser, {});
    }
    webrtcUI.forgetStreamsFromBrowser(aBrowser);

    gBrowser.getNotificationBox(aBrowser).removeTransientNotifications();

    FullZoom.onLocationChange(aLocationURI, false, aBrowser);
  },
};

function nsBrowserAccess() { }

nsBrowserAccess.prototype = {
  QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserDOMWindow, Ci.nsISupports]),

  _openURIInNewTab(aURI, aReferrer, aReferrerPolicy, aIsPrivate,
                   aIsExternal, aForceNotRemote = false,
                   aUserContextId = Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID,
                   aOpenerWindow = null, aOpenerBrowser = null,
                   aTriggeringPrincipal = null, aNextTabParentId = 0, aName = "") {
    let win, needToFocusWin;

    // try the current window.  if we're in a popup, fall back on the most recent browser window
    if (window.toolbar.visible)
      win = window;
    else {
      win = RecentWindow.getMostRecentBrowserWindow({private: aIsPrivate});
      needToFocusWin = true;
    }

    if (!win) {
      // we couldn't find a suitable window, a new one needs to be opened.
      return null;
    }

    if (aIsExternal && (!aURI || aURI.spec == "about:blank")) {
      win.BrowserOpenTab(); // this also focuses the location bar
      win.focus();
      return win.gBrowser.selectedBrowser;
    }

    let loadInBackground = Services.prefs.getBoolPref("browser.tabs.loadDivertedInBackground");

    let tab = win.gBrowser.loadOneTab(aURI ? aURI.spec : "about:blank", {
                                      triggeringPrincipal: aTriggeringPrincipal,
                                      referrerURI: aReferrer,
                                      referrerPolicy: aReferrerPolicy,
                                      userContextId: aUserContextId,
                                      fromExternal: aIsExternal,
                                      inBackground: loadInBackground,
                                      forceNotRemote: aForceNotRemote,
                                      opener: aOpenerWindow,
                                      openerBrowser: aOpenerBrowser,
                                      nextTabParentId: aNextTabParentId,
                                      name: aName,
                                      });
    let browser = win.gBrowser.getBrowserForTab(tab);

    if (needToFocusWin || (!loadInBackground && aIsExternal))
      win.focus();

    return browser;
  },

  createContentWindow(aURI, aOpener, aWhere, aFlags, aTriggeringPrincipal) {
    return this.getContentWindowOrOpenURI(null, aOpener, aWhere, aFlags,
                                          aTriggeringPrincipal);
  },

  openURI(aURI, aOpener, aWhere, aFlags, aTriggeringPrincipal) {
    if (!aURI) {
      Cu.reportError("openURI should only be called with a valid URI");
      throw Cr.NS_ERROR_FAILURE;
    }
    return this.getContentWindowOrOpenURI(aURI, aOpener, aWhere, aFlags,
                                          aTriggeringPrincipal);
  },

  getContentWindowOrOpenURI(aURI, aOpener, aWhere, aFlags, aTriggeringPrincipal) {
    // This function should only ever be called if we're opening a URI
    // from a non-remote browser window (via nsContentTreeOwner).
    if (aOpener && Cu.isCrossProcessWrapper(aOpener)) {
      Cu.reportError("nsBrowserAccess.openURI was passed a CPOW for aOpener. " +
                     "openURI should only ever be called from non-remote browsers.");
      throw Cr.NS_ERROR_FAILURE;
    }

    var newWindow = null;
    var isExternal = !!(aFlags & Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);

    if (aOpener && isExternal) {
      Cu.reportError("nsBrowserAccess.openURI did not expect an opener to be " +
                     "passed if the context is OPEN_EXTERNAL.");
      throw Cr.NS_ERROR_FAILURE;
    }

    if (isExternal && aURI && aURI.schemeIs("chrome")) {
      dump("use --chrome command-line option to load external chrome urls\n");
      return null;
    }

    if (aWhere == Ci.nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW) {
      if (isExternal &&
          Services.prefs.prefHasUserValue("browser.link.open_newwindow.override.external"))
        aWhere = Services.prefs.getIntPref("browser.link.open_newwindow.override.external");
      else
        aWhere = Services.prefs.getIntPref("browser.link.open_newwindow");
    }

    let referrer = aOpener ? makeURI(aOpener.location.href) : null;
    let referrerPolicy = Ci.nsIHttpChannel.REFERRER_POLICY_UNSET;
    if (aOpener && aOpener.document) {
      referrerPolicy = aOpener.document.referrerPolicy;
    }
    let isPrivate = aOpener
                  ? PrivateBrowsingUtils.isContentWindowPrivate(aOpener)
                  : PrivateBrowsingUtils.isWindowPrivate(window);

    switch (aWhere) {
      case Ci.nsIBrowserDOMWindow.OPEN_NEWWINDOW :
        // FIXME: Bug 408379. So how come this doesn't send the
        // referrer like the other loads do?
        var url = aURI ? aURI.spec : "about:blank";
        let features = "all,dialog=no";
        if (isPrivate) {
          features += ",private";
        }
        // Pass all params to openDialog to ensure that "url" isn't passed through
        // loadOneOrMoreURIs, which splits based on "|"
        newWindow = openDialog(getBrowserURL(), "_blank", features, url, null, null, null);
        break;
      case Ci.nsIBrowserDOMWindow.OPEN_NEWTAB :
        // If we have an opener, that means that the caller is expecting access
        // to the nsIDOMWindow of the opened tab right away. For e10s windows,
        // this means forcing the newly opened browser to be non-remote so that
        // we can hand back the nsIDOMWindow. The XULBrowserWindow.shouldLoadURI
        // will do the job of shuttling off the newly opened browser to run in
        // the right process once it starts loading a URI.
        let forceNotRemote = !!aOpener;
        let userContextId = aOpener && aOpener.document
                              ? aOpener.document.nodePrincipal.originAttributes.userContextId
                              : Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID;
        let openerWindow = (aFlags & Ci.nsIBrowserDOMWindow.OPEN_NO_OPENER) ? null : aOpener;
        let browser = this._openURIInNewTab(aURI, referrer, referrerPolicy,
                                            isPrivate, isExternal,
                                            forceNotRemote, userContextId,
                                            openerWindow, null, aTriggeringPrincipal);
        if (browser)
          newWindow = browser.contentWindow;
        break;
      default : // OPEN_CURRENTWINDOW or an illegal value
        newWindow = window.content;
        if (aURI) {
          let loadflags = isExternal ?
                            Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL :
                            Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
          gBrowser.loadURIWithFlags(aURI.spec, {
                                    aTriggeringPrincipal,
                                    flags: loadflags,
                                    referrerURI: referrer,
                                    referrerPolicy,
                                    });
        }
        if (!Services.prefs.getBoolPref("browser.tabs.loadDivertedInBackground"))
          window.focus();
    }
    return newWindow;
  },

  createContentWindowInFrame: function browser_createContentWindowInFrame(
                              aURI, aParams, aWhere, aFlags, aNextTabParentId,
                              aName) {
    // Passing a null-URI to only create the content window.
    return this.getContentWindowOrOpenURIInFrame(null, aParams, aWhere, aFlags,
                                                 aNextTabParentId, aName);
  },

  openURIInFrame: function browser_openURIInFrame(aURI, aParams, aWhere, aFlags,
                                                  aNextTabParentId, aName) {
    return this.getContentWindowOrOpenURIInFrame(aURI, aParams, aWhere, aFlags,
                                                 aNextTabParentId, aName);
  },

  getContentWindowOrOpenURIInFrame: function browser_getContentWindowOrOpenURIInFrame(
                                    aURI, aParams, aWhere, aFlags,
                                    aNextTabParentId, aName) {
    if (aWhere != Ci.nsIBrowserDOMWindow.OPEN_NEWTAB) {
      dump("Error: openURIInFrame can only open in new tabs");
      return null;
    }

    var isExternal = !!(aFlags & Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);

    var userContextId = aParams.openerOriginAttributes &&
                        ("userContextId" in aParams.openerOriginAttributes)
                          ? aParams.openerOriginAttributes.userContextId
                          : Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID;

    let referrer = aParams.referrer ? makeURI(aParams.referrer) : null;
    return this._openURIInNewTab(aURI, referrer,
                                 aParams.referrerPolicy,
                                 aParams.isPrivate,
                                 isExternal, false,
                                 userContextId, null, aParams.openerBrowser,
                                 aParams.triggeringPrincipal,
                                 aNextTabParentId, aName);
  },

  isTabContentWindow(aWindow) {
    return gBrowser.browsers.some(browser => browser.contentWindow == aWindow);
  },

  canClose() {
    return CanCloseWindow();
  },
};

function onViewToolbarsPopupShowing(aEvent, aInsertPoint) {
  var popup = aEvent.target;
  if (popup != aEvent.currentTarget)
    return;

  // Empty the menu
  for (var i = popup.childNodes.length - 1; i >= 0; --i) {
    var deadItem = popup.childNodes[i];
    if (deadItem.hasAttribute("toolbarId"))
      popup.removeChild(deadItem);
  }

  var firstMenuItem = aInsertPoint || popup.firstChild;

  let toolbarNodes = gNavToolbox.childNodes;

  for (let toolbar of toolbarNodes) {
    if (!toolbar.hasAttribute("toolbarname")) {
      continue;
    }

    let menuItem = document.createElement("menuitem");
    let hidingAttribute = toolbar.getAttribute("type") == "menubar" ?
                          "autohide" : "collapsed";
    menuItem.setAttribute("id", "toggle_" + toolbar.id);
    menuItem.setAttribute("toolbarId", toolbar.id);
    menuItem.setAttribute("type", "checkbox");
    menuItem.setAttribute("label", toolbar.getAttribute("toolbarname"));
    menuItem.setAttribute("checked", toolbar.getAttribute(hidingAttribute) != "true");
    menuItem.setAttribute("accesskey", toolbar.getAttribute("accesskey"));
    if (popup.id != "toolbar-context-menu")
      menuItem.setAttribute("key", toolbar.getAttribute("key"));

    popup.insertBefore(menuItem, firstMenuItem);

    menuItem.addEventListener("command", onViewToolbarCommand);
  }


  let moveToPanel = popup.querySelector(".customize-context-moveToPanel");
  let removeFromToolbar = popup.querySelector(".customize-context-removeFromToolbar");
  // View -> Toolbars menu doesn't have the moveToPanel or removeFromToolbar items.
  if (!moveToPanel || !removeFromToolbar) {
    return;
  }

  // triggerNode can be a nested child element of a toolbaritem.
  let toolbarItem = popup.triggerNode;

  if (toolbarItem && toolbarItem.localName == "toolbarpaletteitem") {
    toolbarItem = toolbarItem.firstChild;
  } else if (toolbarItem && toolbarItem.localName != "toolbar") {
    while (toolbarItem && toolbarItem.parentNode) {
      let parent = toolbarItem.parentNode;
      if (parent.nodeType !== Node.ELEMENT_NODE ||
          (parent.classList && parent.classList.contains("customization-target")) ||
          parent.getAttribute("overflowfortoolbar") || // Needs to work in the overflow list as well.
          parent.localName == "toolbarpaletteitem" ||
          parent.localName == "toolbar")
        break;
      toolbarItem = parent;
    }
  } else {
    toolbarItem = null;
  }

  let showTabStripItems = toolbarItem && toolbarItem.id == "tabbrowser-tabs";
  for (let node of popup.querySelectorAll('menuitem[contexttype="toolbaritem"]')) {
    node.hidden = showTabStripItems;
  }

  for (let node of popup.querySelectorAll('menuitem[contexttype="tabbar"]')) {
    node.hidden = !showTabStripItems;
  }

  if (showTabStripItems) {
    PlacesCommandHook.updateBookmarkAllTabsCommand();

    let haveMultipleTabs = gBrowser.visibleTabs.length > 1;
    document.getElementById("toolbar-context-reloadAllTabs").disabled = !haveMultipleTabs;

    document.getElementById("toolbar-context-undoCloseTab").disabled =
      SessionStore.getClosedTabCount(window) == 0;
    return;
  }

  // In some cases, we will exit the above loop with toolbarItem being the
  // xul:document. That has no parentNode, and we should disable the items in
  // this case.
  let movable = toolbarItem && toolbarItem.parentNode &&
                CustomizableUI.isWidgetRemovable(toolbarItem);
  let isSpecial = toolbarItem && CustomizableUI.isSpecialWidget(toolbarItem.id);
  if (movable) {
    if (isSpecial) {
      moveToPanel.setAttribute("disabled", true);
    } else {
      moveToPanel.removeAttribute("disabled");
    }
    removeFromToolbar.removeAttribute("disabled");
  } else {
    moveToPanel.setAttribute("disabled", true);
    removeFromToolbar.setAttribute("disabled", true);
  }
}

function onViewToolbarCommand(aEvent) {
  let node = aEvent.originalTarget;
  let toolbarId = node.getAttribute("toolbarId");
  let isVisible = node.getAttribute("checked") == "true";
  CustomizableUI.setToolbarVisibility(toolbarId, isVisible);
  updateToggleControlLabel(node);
}

function setToolbarVisibility(toolbar, isVisible, persist = true) {
  let hidingAttribute;
  if (toolbar.getAttribute("type") == "menubar") {
    hidingAttribute = "autohide";
    if (AppConstants.platform == "linux") {
      Services.prefs.setBoolPref("ui.key.menuAccessKeyFocuses", !isVisible);
    }
  } else {
    hidingAttribute = "collapsed";
  }

  toolbar.setAttribute(hidingAttribute, !isVisible);
  if (persist) {
    document.persist(toolbar.id, hidingAttribute);
  }

  let eventParams = {
    detail: {
      visible: isVisible
    },
    bubbles: true
  };
  let event = new CustomEvent("toolbarvisibilitychange", eventParams);
  toolbar.dispatchEvent(event);

  if (toolbar.getAttribute("type") == "menubar" && CustomizationHandler.isCustomizing()) {
    gCustomizeMode._updateDragSpaceCheckbox();
  }
}

function updateToggleControlLabel(control) {
  if (!control.hasAttribute("label-checked")) {
    return;
  }

  if (!control.hasAttribute("label-unchecked")) {
    control.setAttribute("label-unchecked", control.getAttribute("label"));
  }
  let prefix = (control.getAttribute("checked") == "true") ? "" : "un";
  control.setAttribute("label", control.getAttribute(`label-${prefix}checked`));
}

var TabletModeUpdater = {
  init() {
    if (AppConstants.isPlatformAndVersionAtLeast("win", "10")) {
      this.update(WindowsUIUtils.inTabletMode);
      Services.obs.addObserver(this, "tablet-mode-change");
    }
  },

  uninit() {
    if (AppConstants.isPlatformAndVersionAtLeast("win", "10")) {
      Services.obs.removeObserver(this, "tablet-mode-change");
    }
  },

  observe(subject, topic, data) {
    this.update(data == "tablet-mode");
  },

  update(isInTabletMode) {
    let wasInTabletMode = document.documentElement.hasAttribute("tabletmode");
    if (isInTabletMode) {
      document.documentElement.setAttribute("tabletmode", "true");
    } else {
      document.documentElement.removeAttribute("tabletmode");
    }
    if (wasInTabletMode != isInTabletMode) {
      gUIDensity.update();
    }
  },
};

var gTabletModePageCounter = {
  enabled: false,
  inc() {
    this.enabled = AppConstants.isPlatformAndVersionAtLeast("win", "10.0");
    if (!this.enabled) {
      this.inc = () => {};
      return;
    }
    this.inc = this._realInc;
    this.inc();
  },

  _desktopCount: 0,
  _tabletCount: 0,
  _realInc() {
    let inTabletMode = document.documentElement.hasAttribute("tabletmode");
    this[inTabletMode ? "_tabletCount" : "_desktopCount"]++;
  },

  finish() {
    if (this.enabled) {
      let histogram = Services.telemetry.getKeyedHistogramById("FX_TABLETMODE_PAGE_LOAD");
      histogram.add("tablet", this._tabletCount);
      histogram.add("desktop", this._desktopCount);
    }
  },
};

function displaySecurityInfo() {
  BrowserPageInfo(null, "securityTab");
}

// Adds additional drag space to the window by listening to
// the corresponding preference.
var gDragSpaceObserver = {
  pref: "browser.tabs.extraDragSpace",

  init() {
    this.update();
    Services.prefs.addObserver(this.pref, this);
  },

  uninit() {
    Services.prefs.removeObserver(this.pref, this);
  },

  observe(aSubject, aTopic, aPrefName) {
    if (aTopic != "nsPref:changed" || aPrefName != this.pref) {
      return;
    }

    this.update();
  },

  update() {
    if (Services.prefs.getBoolPref(this.pref)) {
      document.documentElement.setAttribute("extradragspace", "true");
    } else {
      document.documentElement.removeAttribute("extradragspace");
    }
    TabsInTitlebar.updateAppearance(true);
  },
};

// Updates the UI density (for touch and compact mode) based on the uidensity pref.
var gUIDensity = {
  MODE_NORMAL: 0,
  MODE_COMPACT: 1,
  MODE_TOUCH: 2,
  uiDensityPref: "browser.uidensity",
  autoTouchModePref: "browser.touchmode.auto",

  init() {
    this.update();
    Services.prefs.addObserver(this.uiDensityPref, this);
    Services.prefs.addObserver(this.autoTouchModePref, this);
  },

  uninit() {
    Services.prefs.removeObserver(this.uiDensityPref, this);
    Services.prefs.removeObserver(this.autoTouchModePref, this);
  },

  observe(aSubject, aTopic, aPrefName) {
    if (aTopic != "nsPref:changed" ||
        (aPrefName != this.uiDensityPref &&
         aPrefName != this.autoTouchModePref)) {
      return;
    }

    this.update();
  },

  getCurrentDensity() {
    // Automatically override the uidensity to touch in Windows tablet mode.
    if (AppConstants.isPlatformAndVersionAtLeast("win", "10") &&
        WindowsUIUtils.inTabletMode &&
        Services.prefs.getBoolPref(this.autoTouchModePref)) {
      return { mode: this.MODE_TOUCH, overridden: true };
    }
    return { mode: Services.prefs.getIntPref(this.uiDensityPref), overridden: false };
  },

  setCurrentMode(mode) {
    Services.prefs.setIntPref(this.uiDensityPref, mode);
  },

  update(mode) {
    if (mode == null) {
      mode = this.getCurrentDensity().mode;
    }

    let docs = [document.documentElement];
    let shouldUpdateSidebar = SidebarUI.initialized && SidebarUI.isOpen;
    if (shouldUpdateSidebar) {
      docs.push(SidebarUI.browser.contentDocument.documentElement);
    }
    for (let doc of docs) {
      switch (mode) {
      case this.MODE_COMPACT:
        doc.setAttribute("uidensity", "compact");
        break;
      case this.MODE_TOUCH:
        doc.setAttribute("uidensity", "touch");
        break;
      default:
        doc.removeAttribute("uidensity");
        break;
      }
    }
    if (shouldUpdateSidebar) {
      let tree = SidebarUI.browser.contentDocument.querySelector(".sidebar-placesTree");
      if (tree) {
        // Tree items don't update their styles without changing some property on the
        // parent tree element, like background-color or border. See bug 1407399.
        tree.style.border = "1px";
        tree.style.border = "";
      }
    }

    TabsInTitlebar.updateAppearance(true);
    gBrowser.tabContainer.uiDensityChanged();
  },
};

var gHomeButton = {
  prefDomain: "browser.startup.homepage",
  observe(aSubject, aTopic, aPrefName) {
    if (aTopic != "nsPref:changed" || aPrefName != this.prefDomain)
      return;

    this.updateTooltip();
  },

  updateTooltip(homeButton) {
    if (!homeButton)
      homeButton = document.getElementById("home-button");
    if (homeButton) {
      var homePage = this.getHomePage();
      homePage = homePage.replace(/\|/g, ", ");
      if (["about:home", "about:newtab"].includes(homePage.toLowerCase()))
        homeButton.setAttribute("tooltiptext", homeButton.getAttribute("aboutHomeOverrideTooltip"));
      else
        homeButton.setAttribute("tooltiptext", homePage);
    }
  },

  getHomePage() {
    var url;
    try {
      url = Services.prefs.getComplexValue(this.prefDomain,
                                  Ci.nsIPrefLocalizedString).data;
    } catch (e) {
    }

    // use this if we can't find the pref
    if (!url) {
      var configBundle = Services.strings
                                 .createBundle("chrome://branding/locale/browserconfig.properties");
      url = configBundle.GetStringFromName(this.prefDomain);
    }

    return url;
  },
};

const nodeToTooltipMap = {
  "bookmarks-menu-button": "bookmarksMenuButton.tooltip",
  "context-reload": "reloadButton.tooltip",
  "context-stop": "stopButton.tooltip",
  "downloads-button": "downloads.tooltip",
  "fullscreen-button": "fullscreenButton.tooltip",
  "appMenu-fullscreen-button": "fullscreenButton.tooltip",
  "new-window-button": "newWindowButton.tooltip",
  "new-tab-button": "newTabButton.tooltip",
  "tabs-newtab-button": "newTabButton.tooltip",
  "reload-button": "reloadButton.tooltip",
  "stop-button": "stopButton.tooltip",
  "urlbar-zoom-button": "urlbar-zoom-button.tooltip",
  "appMenu-cut-button": "cut-button.tooltip",
  "appMenu-copy-button": "copy-button.tooltip",
  "appMenu-paste-button": "paste-button.tooltip",
  "appMenu-zoomEnlarge-button": "zoomEnlarge-button.tooltip",
  "appMenu-zoomReset-button": "zoomReset-button.tooltip",
  "appMenu-zoomReduce-button": "zoomReduce-button.tooltip",
  "reader-mode-button": "reader-mode-button.tooltip",
};
const nodeToShortcutMap = {
  "bookmarks-menu-button": "manBookmarkKb",
  "context-reload": "key_reload",
  "context-stop": "key_stop",
  "downloads-button": "key_openDownloads",
  "fullscreen-button": "key_fullScreen",
  "appMenu-fullscreen-button": "key_fullScreen",
  "new-window-button": "key_newNavigator",
  "new-tab-button": "key_newNavigatorTab",
  "tabs-newtab-button": "key_newNavigatorTab",
  "reload-button": "key_reload",
  "stop-button": "key_stop",
  "urlbar-zoom-button": "key_fullZoomReset",
  "appMenu-cut-button": "key_cut",
  "appMenu-copy-button": "key_copy",
  "appMenu-paste-button": "key_paste",
  "appMenu-zoomEnlarge-button": "key_fullZoomEnlarge",
  "appMenu-zoomReset-button": "key_fullZoomReset",
  "appMenu-zoomReduce-button": "key_fullZoomReduce",
  "reader-mode-button": "key_toggleReaderMode",
};

if (AppConstants.platform == "macosx") {
  nodeToTooltipMap["print-button"] = "printButton.tooltip";
  nodeToShortcutMap["print-button"] = "printKb";
}

const gDynamicTooltipCache = new Map();
function UpdateDynamicShortcutTooltipText(aTooltip) {
  let nodeId = aTooltip.triggerNode.id || aTooltip.triggerNode.getAttribute("anonid");
  if (!gDynamicTooltipCache.has(nodeId) && nodeId in nodeToTooltipMap) {
    let strId = nodeToTooltipMap[nodeId];
    let args = [];
    if (nodeId in nodeToShortcutMap) {
      let shortcutId = nodeToShortcutMap[nodeId];
      let shortcut = document.getElementById(shortcutId);
      if (shortcut) {
        args.push(ShortcutUtils.prettifyShortcut(shortcut));
      }
    }
    gDynamicTooltipCache.set(nodeId, gNavigatorBundle.getFormattedString(strId, args));
  }
  aTooltip.setAttribute("label", gDynamicTooltipCache.get(nodeId));
}

var gWebPanelURI;
function openWebPanel(title, uri) {
  // Ensure that the web panels sidebar is open.
  SidebarUI.show("viewWebPanelsSidebar");

  // Set the title of the panel.
  SidebarUI.title = title;

  // Tell the Web Panels sidebar to load the bookmark.
  if (SidebarUI.browser.docShell && SidebarUI.browser.contentDocument &&
      SidebarUI.browser.contentDocument.getElementById("web-panels-browser")) {
    SidebarUI.browser.contentWindow.loadWebPanel(uri);
    if (gWebPanelURI) {
      gWebPanelURI = "";
      SidebarUI.browser.removeEventListener("load", asyncOpenWebPanel, true);
    }
  } else {
    // The panel is still being constructed.  Attach an onload handler.
    if (!gWebPanelURI) {
      SidebarUI.browser.addEventListener("load", asyncOpenWebPanel, true);
    }
    gWebPanelURI = uri;
  }
}

function asyncOpenWebPanel(event) {
  if (gWebPanelURI && SidebarUI.browser.contentDocument &&
      SidebarUI.browser.contentDocument.getElementById("web-panels-browser")) {
    SidebarUI.browser.contentWindow.loadWebPanel(gWebPanelURI);
    SidebarUI.setWebPageIcon(gWebPanelURI);
  }
  gWebPanelURI = "";
  SidebarUI.browser.removeEventListener("load", asyncOpenWebPanel, true);
}

/*
 * - [ Dependencies ] ---------------------------------------------------------
 *  utilityOverlay.js:
 *    - gatherTextUnder
 */

/**
 * Extracts linkNode and href for the current click target.
 *
 * @param event
 *        The click event.
 * @return [href, linkNode].
 *
 * @note linkNode will be null if the click wasn't on an anchor
 *       element (or XLink).
 */
function hrefAndLinkNodeForClickEvent(event) {
  function isHTMLLink(aNode) {
    // Be consistent with what nsContextMenu.js does.
    return ((aNode instanceof HTMLAnchorElement && aNode.href) ||
            (aNode instanceof HTMLAreaElement && aNode.href) ||
            aNode instanceof HTMLLinkElement);
  }

  let node = event.target;
  while (node && !isHTMLLink(node)) {
    node = node.parentNode;
  }

  if (node)
    return [node.href, node];

  // If there is no linkNode, try simple XLink.
  let href, baseURI;
  node = event.target;
  while (node && !href) {
    if (node.nodeType == Node.ELEMENT_NODE &&
        (node.localName == "a" ||
         node.namespaceURI == "http://www.w3.org/1998/Math/MathML")) {
      href = node.getAttribute("href") ||
             node.getAttributeNS("http://www.w3.org/1999/xlink", "href");

      if (href) {
        baseURI = node.baseURI;
        break;
      }
    }
    node = node.parentNode;
  }

  // In case of XLink, we don't return the node we got href from since
  // callers expect <a>-like elements.
  return [href ? makeURLAbsolute(baseURI, href) : null, null];
}

/**
 * Called whenever the user clicks in the content area.
 *
 * @param event
 *        The click event.
 * @param isPanelClick
 *        Whether the event comes from a web panel.
 * @note default event is prevented if the click is handled.
 */
function contentAreaClick(event, isPanelClick) {
  if (!event.isTrusted || event.defaultPrevented || event.button == 2)
    return;

  let [href, linkNode] = hrefAndLinkNodeForClickEvent(event);
  if (!href) {
    // Not a link, handle middle mouse navigation.
    if (event.button == 1 &&
        Services.prefs.getBoolPref("middlemouse.contentLoadURL") &&
        !Services.prefs.getBoolPref("general.autoScroll")) {
      middleMousePaste(event);
      event.preventDefault();
    }
    return;
  }

  // This code only applies if we have a linkNode (i.e. clicks on real anchor
  // elements, as opposed to XLink).
  if (linkNode && event.button == 0 &&
      !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey) {
    // A Web panel's links should target the main content area.  Do this
    // if no modifier keys are down and if there's no target or the target
    // equals _main (the IE convention) or _content (the Mozilla convention).
    let target = linkNode.target;
    let mainTarget = !target || target == "_content" || target == "_main";
    if (isPanelClick && mainTarget) {
      // javascript and data links should be executed in the current browser.
      if (linkNode.getAttribute("onclick") ||
          href.startsWith("javascript:") ||
          href.startsWith("data:"))
        return;

      try {
        urlSecurityCheck(href, linkNode.ownerDocument.nodePrincipal);
      } catch (ex) {
        // Prevent loading unsecure destinations.
        event.preventDefault();
        return;
      }

      loadURI(href, null, null, false);
      event.preventDefault();
      return;
    }

    if (linkNode.getAttribute("rel") == "sidebar") {
      // This is the Opera convention for a special link that, when clicked,
      // allows to add a sidebar panel.  The link's title attribute contains
      // the title that should be used for the sidebar panel.
      PlacesUIUtils.showBookmarkDialog({ action: "add",
                                         type: "bookmark",
                                         uri: makeURI(href),
                                         title: linkNode.getAttribute("title"),
                                         loadBookmarkInSidebar: true,
                                         hiddenRows: [ "description",
                                                       "location",
                                                       "keyword" ]
                                       }, window);
      event.preventDefault();
      return;
    }
  }

  handleLinkClick(event, href, linkNode);

  // Mark the page as a user followed link.  This is done so that history can
  // distinguish automatic embed visits from user activated ones.  For example
  // pages loaded in frames are embed visits and lost with the session, while
  // visits across frames should be preserved.
  try {
    if (!PrivateBrowsingUtils.isWindowPrivate(window))
      PlacesUIUtils.markPageAsFollowedLink(href);
  } catch (ex) { /* Skip invalid URIs. */ }
}

/**
 * Handles clicks on links.
 *
 * @return true if the click event was handled, false otherwise.
 */
function handleLinkClick(event, href, linkNode) {
  if (event.button == 2) // right click
    return false;

  var where = whereToOpenLink(event);
  if (where == "current")
    return false;

  var doc = event.target.ownerDocument;

  if (where == "save") {
    saveURL(href, linkNode ? gatherTextUnder(linkNode) : "", null, true,
            true, doc.documentURIObject, doc);
    event.preventDefault();
    return true;
  }

  var referrerURI = doc.documentURIObject;
  // if the mixedContentChannel is present and the referring URI passes
  // a same origin check with the target URI, we can preserve the users
  // decision of disabling MCB on a page for it's child tabs.
  var persistAllowMixedContentInChildTab = false;

  if (where == "tab" && gBrowser.docShell.mixedContentChannel) {
    const sm = Services.scriptSecurityManager;
    try {
      var targetURI = makeURI(href);
      sm.checkSameOriginURI(referrerURI, targetURI, false);
      persistAllowMixedContentInChildTab = true;
    } catch (e) { }
  }

  // first get document wide referrer policy, then
  // get referrer attribute from clicked link and parse it and
  // allow per element referrer to overrule the document wide referrer if enabled
  let referrerPolicy = doc.referrerPolicy;
  if (linkNode) {
    let referrerAttrValue = Services.netUtils.parseAttributePolicyString(linkNode.
                            getAttribute("referrerpolicy"));
    if (referrerAttrValue != Ci.nsIHttpChannel.REFERRER_POLICY_UNSET) {
      referrerPolicy = referrerAttrValue;
    }
  }

  let frameOuterWindowID = WebNavigationFrames.getFrameId(doc.defaultView);

  urlSecurityCheck(href, doc.nodePrincipal);
  let params = {
    charset: doc.characterSet,
    allowMixedContent: persistAllowMixedContentInChildTab,
    referrerURI,
    referrerPolicy,
    noReferrer: BrowserUtils.linkHasNoReferrer(linkNode),
    originPrincipal: doc.nodePrincipal,
    triggeringPrincipal: doc.nodePrincipal,
    frameOuterWindowID,
  };

  // The new tab/window must use the same userContextId
  if (doc.nodePrincipal.originAttributes.userContextId) {
    params.userContextId = doc.nodePrincipal.originAttributes.userContextId;
  }

  openLinkIn(href, where, params);
  event.preventDefault();
  return true;
}

/**
 * Handles paste on middle mouse clicks.
 *
 * @param event {Event | Object} Event or JSON object.
 */
function middleMousePaste(event) {
  let clipboard = readFromClipboard();
  if (!clipboard)
    return;

  // Strip embedded newlines and surrounding whitespace, to match the URL
  // bar's behavior (stripsurroundingwhitespace)
  clipboard = clipboard.replace(/\s*\n\s*/g, "");

  clipboard = stripUnsafeProtocolOnPaste(clipboard);

  // if it's not the current tab, we don't need to do anything because the
  // browser doesn't exist.
  let where = whereToOpenLink(event, true, false);
  let lastLocationChange;
  if (where == "current") {
    lastLocationChange = gBrowser.selectedBrowser.lastLocationChange;
  }

  getShortcutOrURIAndPostData(clipboard).then(data => {
    try {
      makeURI(data.url);
    } catch (ex) {
      // Not a valid URI.
      return;
    }

    try {
      addToUrlbarHistory(data.url);
    } catch (ex) {
      // Things may go wrong when adding url to session history,
      // but don't let that interfere with the loading of the url.
      Cu.reportError(ex);
    }

    if (where != "current" ||
        lastLocationChange == gBrowser.selectedBrowser.lastLocationChange) {
      openUILink(data.url, event,
                 { ignoreButton: true,
                   disallowInheritPrincipal: !data.mayInheritPrincipal });
    }
  });

  if (event instanceof Event) {
    event.stopPropagation();
  }
}

function stripUnsafeProtocolOnPaste(pasteData) {
  // Don't allow pasting javascript URIs since we don't support
  // LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL for those.
  while (true) {
    let scheme = "";
    try {
      scheme = Services.io.extractScheme(pasteData);
    } catch (ex) { }
    if (scheme != "javascript") {
      break;
    }

    pasteData = pasteData.substring(pasteData.indexOf(":") + 1);
  }
  return pasteData;
}

// handleDroppedLink has the following 2 overloads:
//   handleDroppedLink(event, url, name, triggeringPrincipal)
//   handleDroppedLink(event, links, triggeringPrincipal)
function handleDroppedLink(event, urlOrLinks, nameOrTriggeringPrincipal, triggeringPrincipal) {
  let links;
  if (Array.isArray(urlOrLinks)) {
    links = urlOrLinks;
    triggeringPrincipal = nameOrTriggeringPrincipal;
  } else {
    links = [{ url: urlOrLinks, nameOrTriggeringPrincipal, type: "" }];
  }

  let lastLocationChange = gBrowser.selectedBrowser.lastLocationChange;

  let userContextId = gBrowser.selectedBrowser.getAttribute("usercontextid");

  // event is null if links are dropped in content process.
  // inBackground should be false, as it's loading into current browser.
  let inBackground = false;
  if (event) {
    inBackground = Services.prefs.getBoolPref("browser.tabs.loadInBackground");
    if (event.shiftKey)
      inBackground = !inBackground;
  }

  (async function() {
    if (links.length >= Services.prefs.getIntPref("browser.tabs.maxOpenBeforeWarn")) {
      // Sync dialog cannot be used inside drop event handler.
      let answer = await OpenInTabsUtils.promiseConfirmOpenInTabs(links.length,
                                                                  window);
      if (!answer) {
        return;
      }
    }

    let urls = [];
    let postDatas = [];
    for (let link of links) {
      let data = await getShortcutOrURIAndPostData(link.url);
      urls.push(data.url);
      postDatas.push(data.postData);
    }
    if (lastLocationChange == gBrowser.selectedBrowser.lastLocationChange) {
      gBrowser.loadTabs(urls, {
        inBackground,
        replace: true,
        allowThirdPartyFixup: false,
        postDatas,
        userContextId,
        triggeringPrincipal,
      });
    }
  })();

  // If links are dropped in content process, event.preventDefault() should be
  // called in content process.
  if (event) {
    // Keep the event from being handled by the dragDrop listeners
    // built-in to gecko if they happen to be above us.
    event.preventDefault();
  }
}

function BrowserSetForcedCharacterSet(aCharset) {
  if (aCharset) {
    gBrowser.selectedBrowser.characterSet = aCharset;
    // Save the forced character-set
    if (!PrivateBrowsingUtils.isWindowPrivate(window))
      PlacesUtils.setCharsetForURI(getWebNavigation().currentURI, aCharset);
  }
  BrowserCharsetReload();
}

function BrowserCharsetReload() {
  BrowserReloadWithFlags(nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE);
}

function UpdateCurrentCharset(target) {
  let selectedCharset = CharsetMenu.foldCharset(gBrowser.selectedBrowser.characterSet);
  for (let menuItem of target.getElementsByTagName("menuitem")) {
    let isSelected = menuItem.getAttribute("charset") === selectedCharset;
    menuItem.setAttribute("checked", isSelected);
  }
}

var gPageStyleMenu = {
  // This maps from a <browser> element (or, more specifically, a
  // browser's permanentKey) to an Object that contains the most recent
  // information about the browser content's stylesheets. That Object
  // is populated via the PageStyle:StyleSheets message from the content
  // process. The Object should have the following structure:
  //
  // filteredStyleSheets (Array):
  //   An Array of objects with a filtered list representing all stylesheets
  //   that the current page offers. Each object has the following members:
  //
  //   title (String):
  //     The title of the stylesheet
  //
  //   disabled (bool):
  //     Whether or not the stylesheet is currently applied
  //
  //   href (String):
  //     The URL of the stylesheet. Stylesheets loaded via a data URL will
  //     have this property set to null.
  //
  // authorStyleDisabled (bool):
  //   Whether or not the user currently has "No Style" selected for
  //   the current page.
  //
  // preferredStyleSheetSet (bool):
  //   Whether or not the user currently has the "Default" style selected
  //   for the current page.
  //
  _pageStyleSheets: new WeakMap(),

  init() {
    let mm = window.messageManager;
    mm.addMessageListener("PageStyle:StyleSheets", (msg) => {
      this._pageStyleSheets.set(msg.target.permanentKey, msg.data);
    });
  },

  /**
   * Returns an array of Objects representing stylesheets in a
   * browser. Note that the pageshow event needs to fire in content
   * before this information will be available.
   *
   * @param browser (optional)
   *        The <xul:browser> to search for stylesheets. If omitted, this
   *        defaults to the currently selected tab's browser.
   * @returns Array
   *        An Array of Objects representing stylesheets in the browser.
   *        See the documentation for gPageStyleMenu for a description
   *        of the Object structure.
   */
  getBrowserStyleSheets(browser) {
    if (!browser) {
      browser = gBrowser.selectedBrowser;
    }

    let data = this._pageStyleSheets.get(browser.permanentKey);
    if (!data) {
      return [];
    }
    return data.filteredStyleSheets;
  },

  _getStyleSheetInfo(browser) {
    let data = this._pageStyleSheets.get(browser.permanentKey);
    if (!data) {
      return {
        filteredStyleSheets: [],
        authorStyleDisabled: false,
        preferredStyleSheetSet: true
      };
    }

    return data;
  },

  fillPopup(menuPopup) {
    let styleSheetInfo = this._getStyleSheetInfo(gBrowser.selectedBrowser);
    var noStyle = menuPopup.firstChild;
    var persistentOnly = noStyle.nextSibling;
    var sep = persistentOnly.nextSibling;
    while (sep.nextSibling)
      menuPopup.removeChild(sep.nextSibling);

    let styleSheets = styleSheetInfo.filteredStyleSheets;
    var currentStyleSheets = {};
    var styleDisabled = styleSheetInfo.authorStyleDisabled;
    var haveAltSheets = false;
    var altStyleSelected = false;

    for (let currentStyleSheet of styleSheets) {
      if (!currentStyleSheet.disabled)
        altStyleSelected = true;

      haveAltSheets = true;

      let lastWithSameTitle = null;
      if (currentStyleSheet.title in currentStyleSheets)
        lastWithSameTitle = currentStyleSheets[currentStyleSheet.title];

      if (!lastWithSameTitle) {
        let menuItem = document.createElement("menuitem");
        menuItem.setAttribute("type", "radio");
        menuItem.setAttribute("label", currentStyleSheet.title);
        menuItem.setAttribute("data", currentStyleSheet.title);
        menuItem.setAttribute("checked", !currentStyleSheet.disabled && !styleDisabled);
        menuItem.setAttribute("oncommand", "gPageStyleMenu.switchStyleSheet(this.getAttribute('data'));");
        menuPopup.appendChild(menuItem);
        currentStyleSheets[currentStyleSheet.title] = menuItem;
      } else if (currentStyleSheet.disabled) {
        lastWithSameTitle.removeAttribute("checked");
      }
    }

    noStyle.setAttribute("checked", styleDisabled);
    persistentOnly.setAttribute("checked", !altStyleSelected && !styleDisabled);
    persistentOnly.hidden = styleSheetInfo.preferredStyleSheetSet ? haveAltSheets : false;
    sep.hidden = (noStyle.hidden && persistentOnly.hidden) || !haveAltSheets;
  },

  switchStyleSheet(title) {
    let mm = gBrowser.selectedBrowser.messageManager;
    mm.sendAsyncMessage("PageStyle:Switch", {title});
  },

  disableStyle() {
    let mm = gBrowser.selectedBrowser.messageManager;
    mm.sendAsyncMessage("PageStyle:Disable");
  },
};

var LanguageDetectionListener = {
  init() {
    window.messageManager.addMessageListener("Translation:DocumentState", msg => {
      Translation.documentStateReceived(msg.target, msg.data);
    });
  }
};


var BrowserOffline = {
  _inited: false,

  // BrowserOffline Public Methods
  init() {
    if (!this._uiElement)
      this._uiElement = document.getElementById("workOfflineMenuitemState");

    Services.obs.addObserver(this, "network:offline-status-changed");

    this._updateOfflineUI(Services.io.offline);

    this._inited = true;
  },

  uninit() {
    if (this._inited) {
      Services.obs.removeObserver(this, "network:offline-status-changed");
    }
  },

  toggleOfflineStatus() {
    var ioService = Services.io;

    if (!ioService.offline && !this._canGoOffline()) {
      this._updateOfflineUI(false);
      return;
    }

    ioService.offline = !ioService.offline;
  },

  // nsIObserver
  observe(aSubject, aTopic, aState) {
    if (aTopic != "network:offline-status-changed")
      return;

    // This notification is also received because of a loss in connectivity,
    // which we ignore by updating the UI to the current value of io.offline
    this._updateOfflineUI(Services.io.offline);
  },

  // BrowserOffline Implementation Methods
  _canGoOffline() {
    try {
      var cancelGoOffline = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
      Services.obs.notifyObservers(cancelGoOffline, "offline-requested");

      // Something aborted the quit process.
      if (cancelGoOffline.data)
        return false;
    } catch (ex) {
    }

    return true;
  },

  _uiElement: null,
  _updateOfflineUI(aOffline) {
    var offlineLocked = Services.prefs.prefIsLocked("network.online");
    if (offlineLocked)
      this._uiElement.setAttribute("disabled", "true");

    this._uiElement.setAttribute("checked", aOffline);
  }
};

var OfflineApps = {
  warnUsage(browser, uri) {
    if (!browser)
      return;

    let mainAction = {
      label: gNavigatorBundle.getString("offlineApps.manageUsage"),
      accessKey: gNavigatorBundle.getString("offlineApps.manageUsageAccessKey"),
      callback: this.manage
    };

    let warnQuotaKB = Services.prefs.getIntPref("offline-apps.quota.warn");
    // This message shows the quota in MB, and so we divide the quota (in kb) by 1024.
    let message = gNavigatorBundle.getFormattedString("offlineApps.usage",
                                                      [ uri.host,
                                                        warnQuotaKB / 1024 ]);

    let anchorID = "indexedDB-notification-icon";
    let options = {
      persistent: true,
      hideClose: true,
    };
    PopupNotifications.show(browser, "offline-app-usage", message,
                            anchorID, mainAction, null, options);

    // Now that we've warned once, prevent the warning from showing up
    // again.
    Services.perms.add(uri, "offline-app",
                       Ci.nsIOfflineCacheUpdateService.ALLOW_NO_WARN);
  },

  // XXX: duplicated in preferences/advanced.js
  _getOfflineAppUsage(host, groups) {
    let cacheService = Cc["@mozilla.org/network/application-cache-service;1"].
                       getService(Ci.nsIApplicationCacheService);
    if (!groups) {
      try {
        groups = cacheService.getGroups();
      } catch (ex) {
        return 0;
      }
    }

    let usage = 0;
    for (let group of groups) {
      let uri = Services.io.newURI(group);
      if (uri.asciiHost == host) {
        let cache = cacheService.getActiveCache(group);
        usage += cache.usage;
      }
    }

    return usage;
  },

  _usedMoreThanWarnQuota(uri) {
    // if the user has already allowed excessive usage, don't bother checking
    if (Services.perms.testExactPermission(uri, "offline-app") !=
        Ci.nsIOfflineCacheUpdateService.ALLOW_NO_WARN) {
      let usageBytes = this._getOfflineAppUsage(uri.asciiHost);
      let warnQuotaKB = Services.prefs.getIntPref("offline-apps.quota.warn");
      // The pref is in kb, the usage we get is in bytes, so multiply the quota
      // to compare correctly:
      if (usageBytes >= warnQuotaKB * 1024) {
        return true;
      }
    }

    return false;
  },

  requestPermission(browser, docId, uri) {
    let host = uri.asciiHost;
    let notificationID = "offline-app-requested-" + host;
    let notification = PopupNotifications.getNotification(notificationID, browser);

    if (notification) {
      notification.options.controlledItems.push([
        Cu.getWeakReference(browser), docId, uri
      ]);
    } else {
      let mainAction = {
        label: gNavigatorBundle.getString("offlineApps.allowStoring.label"),
        accessKey: gNavigatorBundle.getString("offlineApps.allowStoring.accesskey"),
        callback() {
          for (let [ciBrowser, ciDocId, ciUri] of notification.options.controlledItems) {
            OfflineApps.allowSite(ciBrowser, ciDocId, ciUri);
          }
        }
      };
      let secondaryActions = [{
        label: gNavigatorBundle.getString("offlineApps.dontAllow.label"),
        accessKey: gNavigatorBundle.getString("offlineApps.dontAllow.accesskey"),
        callback() {
          for (let [, , ciUri] of notification.options.controlledItems) {
            OfflineApps.disallowSite(ciUri);
          }
        }
      }];
      let message = gNavigatorBundle.getFormattedString("offlineApps.available2",
                                                        [host]);
      let anchorID = "indexedDB-notification-icon";
      let options = {
        persistent: true,
        hideClose: true,
        controlledItems: [[Cu.getWeakReference(browser), docId, uri]]
      };
      notification = PopupNotifications.show(browser, notificationID, message,
                                             anchorID, mainAction,
                                             secondaryActions, options);
    }
  },

  disallowSite(uri) {
    Services.perms.add(uri, "offline-app", Services.perms.DENY_ACTION);
  },

  allowSite(browserRef, docId, uri) {
    Services.perms.add(uri, "offline-app", Services.perms.ALLOW_ACTION);

    // When a site is enabled while loading, manifest resources will
    // start fetching immediately.  This one time we need to do it
    // ourselves.
    let browser = browserRef.get();
    if (browser && browser.messageManager) {
      browser.messageManager.sendAsyncMessage("OfflineApps:StartFetching", {
        docId,
      });
    }
  },

  manage() {
    openPreferences("panePrivacy", { origin: "offlineApps" });
  },

  receiveMessage(msg) {
    switch (msg.name) {
      case "OfflineApps:CheckUsage":
        let uri = makeURI(msg.data.uri);
        if (this._usedMoreThanWarnQuota(uri)) {
          this.warnUsage(msg.target, uri);
        }
        break;
      case "OfflineApps:RequestPermission":
        this.requestPermission(msg.target, msg.data.docId, makeURI(msg.data.uri));
        break;
    }
  },

  init() {
    let mm = window.messageManager;
    mm.addMessageListener("OfflineApps:CheckUsage", this);
    mm.addMessageListener("OfflineApps:RequestPermission", this);
  },
};

var IndexedDBPromptHelper = {
  _permissionsPrompt: "indexedDB-permissions-prompt",
  _permissionsResponse: "indexedDB-permissions-response",

  _notificationIcon: "indexedDB-notification-icon",

  init:
  function IndexedDBPromptHelper_init() {
    Services.obs.addObserver(this, this._permissionsPrompt);
  },

  uninit:
  function IndexedDBPromptHelper_uninit() {
    Services.obs.removeObserver(this, this._permissionsPrompt);
  },

  observe:
  function IndexedDBPromptHelper_observe(subject, topic, data) {
    if (topic != this._permissionsPrompt) {
      throw new Error("Unexpected topic!");
    }

    var requestor = subject.QueryInterface(Ci.nsIInterfaceRequestor);

    var browser = requestor.getInterface(Ci.nsIDOMNode);
    if (browser.ownerGlobal != window) {
      // Only listen for notifications for browsers in our chrome window.
      return;
    }

    // Get the host name if available or the file path otherwise.
    var host = browser.currentURI.asciiHost || browser.currentURI.pathQueryRef;

    var message;
    var responseTopic;
    if (topic == this._permissionsPrompt) {
      message = gNavigatorBundle.getFormattedString("offlineApps.available2",
                                                    [ host ]);
      responseTopic = this._permissionsResponse;
    }

    var observer = requestor.getInterface(Ci.nsIObserver);

    var mainAction = {
      label: gNavigatorBundle.getString("offlineApps.allowStoring.label"),
      accessKey: gNavigatorBundle.getString("offlineApps.allowStoring.accesskey"),
      callback() {
        observer.observe(null, responseTopic,
                         Ci.nsIPermissionManager.ALLOW_ACTION);
      }
    };

    var secondaryActions = [
      {
        label: gNavigatorBundle.getString("offlineApps.dontAllow.label"),
        accessKey: gNavigatorBundle.getString("offlineApps.dontAllow.accesskey"),
        callback() {
          observer.observe(null, responseTopic,
                           Ci.nsIPermissionManager.DENY_ACTION);
        }
      }
    ];

    PopupNotifications.show(
      browser, topic, message, this._notificationIcon, mainAction, secondaryActions,
      {
        persistent: true,
        hideClose: !Services.prefs.getBoolPref("privacy.permissionPrompts.showCloseButton"),
      });
  }
};

var CanvasPermissionPromptHelper = {
  _permissionsPrompt: "canvas-permissions-prompt",
  _notificationIcon: "canvas-notification-icon",

  init() {
    Services.obs.addObserver(this, this._permissionsPrompt);
  },

  uninit() {
    Services.obs.removeObserver(this, this._permissionsPrompt);
  },

  // aSubject is an nsIBrowser (e10s) or an nsIDOMWindow (non-e10s).
  // aData is an URL string.
  observe(aSubject, aTopic, aData) {
    if (aTopic != this._permissionsPrompt) {
      return;
    }

    let browser;
    if (aSubject instanceof Ci.nsIDOMWindow) {
      let contentWindow = aSubject.QueryInterface(Ci.nsIDOMWindow);
      browser = gBrowser.getBrowserForContentWindow(contentWindow);
    } else {
      browser = aSubject.QueryInterface(Ci.nsIBrowser);
    }

    let uri = Services.io.newURI(aData);
    if (gBrowser.selectedBrowser !== browser) {
      // Must belong to some other window.
      return;
    }

    let message = gNavigatorBundle.getFormattedString("canvas.siteprompt", ["<>"], 1);

    function setCanvasPermission(aURI, aPerm, aPersistent) {
      Services.perms.add(aURI, "canvas", aPerm,
                          aPersistent ? Ci.nsIPermissionManager.EXPIRE_NEVER
                                      : Ci.nsIPermissionManager.EXPIRE_SESSION);
    }

    let mainAction = {
      label: gNavigatorBundle.getString("canvas.allow"),
      accessKey: gNavigatorBundle.getString("canvas.allow.accesskey"),
      callback(state) {
        setCanvasPermission(uri, Ci.nsIPermissionManager.ALLOW_ACTION,
                            state && state.checkboxChecked);
      }
    };

    let secondaryActions = [{
      label: gNavigatorBundle.getString("canvas.notAllow"),
      accessKey: gNavigatorBundle.getString("canvas.notAllow.accesskey"),
      callback(state) {
        setCanvasPermission(uri, Ci.nsIPermissionManager.DENY_ACTION,
                            state && state.checkboxChecked);
      }
    }];

    let checkbox = {
      // In PB mode, we don't want the "always remember" checkbox
      show: !PrivateBrowsingUtils.isWindowPrivate(window)
    };
    if (checkbox.show) {
      checkbox.checked = true;
      checkbox.label = gBrowserBundle.GetStringFromName("canvas.remember");
    }

    let options = {
      checkbox,
      name: uri.asciiHost,
      learnMoreURL: Services.urlFormatter.formatURLPref("app.support.baseURL") + "fingerprint-permission",
    };
    PopupNotifications.show(browser, aTopic, message, this._notificationIcon,
                            mainAction, secondaryActions, options);
  }
};

var WebAuthnPromptHelper = {
  _icon: "webauthn-notification-icon",
  _topic: "webauthn-prompt",

  // The current notification, if any. The U2F manager is a singleton, we will
  // never allow more than one active request. And thus we'll never have more
  // than one notification either.
  _current: null,

  // The current transaction ID. Will be checked when we're notified of the
  // cancellation of an ongoing WebAuthhn request.
  _tid: 0,

  init() {
    Services.obs.addObserver(this, this._topic);
  },

  uninit() {
    Services.obs.removeObserver(this, this._topic);
  },

  observe(aSubject, aTopic, aData) {
    let mgr = aSubject.QueryInterface(Ci.nsIU2FTokenManager);
    let data = JSON.parse(aData);

    if (data.action == "register") {
      this.register(mgr, data);
    } else if (data.action == "register-direct") {
      this.registerDirect(mgr, data);
    } else if (data.action == "sign") {
      this.sign(mgr, data);
    } else if (data.action == "cancel") {
      this.cancel(data);
    }
  },

  register(mgr, {origin, tid}) {
    let mainAction = this.buildCancelAction(mgr, tid);
    this.show(tid, "register", "webauthn.registerPrompt", origin, mainAction);
  },

  registerDirect(mgr, {origin, tid}) {
    let mainAction = this.buildProceedAction(mgr, tid);
    let secondaryActions = [this.buildCancelAction(mgr, tid)];

    let learnMoreURL =
      Services.urlFormatter.formatURLPref("app.support.baseURL") +
      "webauthn-direct-attestation";

    let options = {
      learnMoreURL,
      checkbox: {
        label: gNavigatorBundle.getString("webauthn.anonymize")
      }
    };

    this.show(tid, "register-direct", "webauthn.registerDirectPrompt",
              origin, mainAction, secondaryActions, options);
  },

  sign(mgr, {origin, tid}) {
    let mainAction = this.buildCancelAction(mgr, tid);
    this.show(tid, "sign", "webauthn.signPrompt", origin, mainAction);
  },

  show(tid, id, stringId, origin, mainAction, secondaryActions = [], options = {}) {
    this.reset();

    try {
      origin = Services.io.newURI(origin).asciiHost;
    } catch (e) {
      /* Might fail for arbitrary U2F RP IDs. */
    }

    let brandShortName =
      document.getElementById("bundle_brand").getString("brandShortName");
    let message =
      gNavigatorBundle.getFormattedString(stringId, ["<>", brandShortName], 1);

    options.name = origin;
    options.hideClose = true;
    options.eventCallback = event => {
      if (event == "removed") {
        this._current = null;
        this._tid = 0;
      }
    };

    this._tid = tid;
    this._current = PopupNotifications.show(
      gBrowser.selectedBrowser, `webauthn-prompt-${id}`, message,
      this._icon, mainAction, secondaryActions, options);
  },

  cancel({tid}) {
    if (this._tid == tid) {
      this.reset();
    }
  },

  reset() {
    if (this._current) {
      this._current.remove();
    }
  },

  buildProceedAction(mgr, tid) {
    return {
      label: gNavigatorBundle.getString("webauthn.proceed"),
      accessKey: gNavigatorBundle.getString("webauthn.proceed.accesskey"),
      callback(state) {
        mgr.resumeRegister(tid, !state.checkboxChecked);
      }
    };
  },

  buildCancelAction(mgr, tid) {
    return {
      label: gNavigatorBundle.getString("webauthn.cancel"),
      accessKey: gNavigatorBundle.getString("webauthn.cancel.accesskey"),
      callback() {
        mgr.cancel(tid);
      }
    };
  },
};

function CanCloseWindow() {
  // Avoid redundant calls to canClose from showing multiple
  // PermitUnload dialogs.
  if (Services.startup.shuttingDown || window.skipNextCanClose) {
    return true;
  }

  let timedOutProcesses = new WeakSet();

  for (let browser of gBrowser.browsers) {
    // Don't instantiate lazy browsers.
    if (!browser.isConnected) {
      continue;
    }

    let pmm = browser.messageManager.processMessageManager;

    if (timedOutProcesses.has(pmm)) {
      continue;
    }

    let {permitUnload, timedOut} = browser.permitUnload();

    if (timedOut) {
      timedOutProcesses.add(pmm);
      continue;
    }

    if (!permitUnload) {
      return false;
    }
  }
  return true;
}

function WindowIsClosing() {
  if (!closeWindow(false, warnAboutClosingWindow))
    return false;

  // In theory we should exit here and the Window's internal Close
  // method should trigger canClose on nsBrowserAccess. However, by
  // that point it's too late to be able to show a prompt for
  // PermitUnload. So we do it here, when we still can.
  if (CanCloseWindow()) {
    // This flag ensures that the later canClose call does nothing.
    // It's only needed to make tests pass, since they detect the
    // prompt even when it's not actually shown.
    window.skipNextCanClose = true;
    return true;
  }

  return false;
}

/**
 * Checks if this is the last full *browser* window around. If it is, this will
 * be communicated like quitting. Otherwise, we warn about closing multiple tabs.
 * @returns true if closing can proceed, false if it got cancelled.
 */
function warnAboutClosingWindow() {
  // Popups aren't considered full browser windows; we also ignore private windows.
  let isPBWindow = PrivateBrowsingUtils.isWindowPrivate(window) &&
        !PrivateBrowsingUtils.permanentPrivateBrowsing;
  if (!isPBWindow && !toolbar.visible)
    return gBrowser.warnAboutClosingTabs(gBrowser.closingTabsEnum.ALL);

  // Figure out if there's at least one other browser window around.
  let otherPBWindowExists = false;
  let nonPopupPresent = false;
  for (let win of browserWindows()) {
    if (!win.closed && win != window) {
      if (isPBWindow && PrivateBrowsingUtils.isWindowPrivate(win))
        otherPBWindowExists = true;
      if (win.toolbar.visible)
        nonPopupPresent = true;
      // If the current window is not in private browsing mode we don't need to
      // look for other pb windows, we can leave the loop when finding the
      // first non-popup window. If however the current window is in private
      // browsing mode then we need at least one other pb and one non-popup
      // window to break out early.
      if ((!isPBWindow || otherPBWindowExists) && nonPopupPresent)
        break;
    }
  }

  if (isPBWindow && !otherPBWindowExists) {
    let exitingCanceled = Cc["@mozilla.org/supports-PRBool;1"].
                          createInstance(Ci.nsISupportsPRBool);
    exitingCanceled.data = false;
    Services.obs.notifyObservers(exitingCanceled,
                                 "last-pb-context-exiting");
    if (exitingCanceled.data)
      return false;
  }

  if (nonPopupPresent) {
    return isPBWindow || gBrowser.warnAboutClosingTabs(gBrowser.closingTabsEnum.ALL);
  }

  let os = Services.obs;

  let closingCanceled = Cc["@mozilla.org/supports-PRBool;1"].
                        createInstance(Ci.nsISupportsPRBool);
  os.notifyObservers(closingCanceled,
                     "browser-lastwindow-close-requested");
  if (closingCanceled.data)
    return false;

  os.notifyObservers(null, "browser-lastwindow-close-granted");

  // OS X doesn't quit the application when the last window is closed, but keeps
  // the session alive. Hence don't prompt users to save tabs, but warn about
  // closing multiple tabs.
  return AppConstants.platform != "macosx"
         || (isPBWindow || gBrowser.warnAboutClosingTabs(gBrowser.closingTabsEnum.ALL));
}

var MailIntegration = {
  sendLinkForBrowser(aBrowser) {
    this.sendMessage(gURLBar.makeURIReadable(aBrowser.currentURI).displaySpec, aBrowser.contentTitle);
  },

  sendMessage(aBody, aSubject) {
    // generate a mailto url based on the url and the url's title
    var mailtoUrl = "mailto:";
    if (aBody) {
      mailtoUrl += "?body=" + encodeURIComponent(aBody);
      mailtoUrl += "&subject=" + encodeURIComponent(aSubject);
    }

    var uri = makeURI(mailtoUrl);

    // now pass this uri to the operating system
    this._launchExternalUrl(uri);
  },

  // a generic method which can be used to pass arbitrary urls to the operating
  // system.
  // aURL --> a nsIURI which represents the url to launch
  _launchExternalUrl(aURL) {
    var extProtocolSvc =
       Cc["@mozilla.org/uriloader/external-protocol-service;1"]
         .getService(Ci.nsIExternalProtocolService);
    if (extProtocolSvc)
      extProtocolSvc.loadURI(aURL);
  }
};

function BrowserOpenAddonsMgr(aView) {
  return new Promise(resolve => {
    let emWindow;
    let browserWindow;

    var receivePong = function(aSubject, aTopic, aData) {
      let browserWin = aSubject.QueryInterface(Ci.nsIInterfaceRequestor)
                               .getInterface(Ci.nsIWebNavigation)
                               .QueryInterface(Ci.nsIDocShellTreeItem)
                               .rootTreeItem
                               .QueryInterface(Ci.nsIInterfaceRequestor)
                               .getInterface(Ci.nsIDOMWindow);
      if (!emWindow || browserWin == window /* favor the current window */) {
        emWindow = aSubject;
        browserWindow = browserWin;
      }
    };
    Services.obs.addObserver(receivePong, "EM-pong");
    Services.obs.notifyObservers(null, "EM-ping");
    Services.obs.removeObserver(receivePong, "EM-pong");

    if (emWindow) {
      if (aView) {
        emWindow.loadView(aView);
      }
      browserWindow.gBrowser.selectedTab =
        browserWindow.gBrowser._getTabForContentWindow(emWindow);
      emWindow.focus();
      resolve(emWindow);
      return;
    }

    // This must be a new load, else the ping/pong would have
    // found the window above.
    let whereToOpen = (window.gBrowser && isTabEmpty(gBrowser.selectedTab)) ?
                      "current" :
                      "tab";
    openUILinkIn("about:addons", whereToOpen);

    Services.obs.addObserver(function observer(aSubject, aTopic, aData) {
      Services.obs.removeObserver(observer, aTopic);
      if (aView) {
        aSubject.loadView(aView);
      }
      aSubject.QueryInterface(Ci.nsIDOMWindow);
      aSubject.focus();
      resolve(aSubject);
    }, "EM-loaded");
  });
}

function AddKeywordForSearchField() {
  let mm = gBrowser.selectedBrowser.messageManager;

  let onMessage = (message) => {
    mm.removeMessageListener("ContextMenu:SearchFieldBookmarkData:Result", onMessage);

    let bookmarkData = message.data;
    let title = gNavigatorBundle.getFormattedString("addKeywordTitleAutoFill",
                                                    [bookmarkData.title]);
    PlacesUIUtils.showBookmarkDialog({ action: "add",
                                       type: "bookmark",
                                       uri: makeURI(bookmarkData.spec),
                                       title,
                                       description: bookmarkData.description,
                                       keyword: "",
                                       postData: bookmarkData.postData,
                                       charSet: bookmarkData.charset,
                                       hiddenRows: [ "location",
                                                     "description",
                                                     "tags",
                                                     "loadInSidebar" ]
                                     }, window);
  };
  mm.addMessageListener("ContextMenu:SearchFieldBookmarkData:Result", onMessage);

  mm.sendAsyncMessage("ContextMenu:SearchFieldBookmarkData", {}, { target: gContextMenu.target });
}

/**
 * Re-open a closed tab.
 * @param aIndex
 *        The index of the tab (via SessionStore.getClosedTabData)
 * @returns a reference to the reopened tab.
 */
function undoCloseTab(aIndex) {
  // wallpaper patch to prevent an unnecessary blank tab (bug 343895)
  var blankTabToRemove = null;
  if (gBrowser.tabs.length == 1 && isTabEmpty(gBrowser.selectedTab))
    blankTabToRemove = gBrowser.selectedTab;

  var tab = null;
  if (SessionStore.getClosedTabCount(window) > (aIndex || 0)) {
    tab = SessionStore.undoCloseTab(window, aIndex || 0);

    if (blankTabToRemove)
      gBrowser.removeTab(blankTabToRemove);
  }

  return tab;
}

/**
 * Re-open a closed window.
 * @param aIndex
 *        The index of the window (via SessionStore.getClosedWindowData)
 * @returns a reference to the reopened window.
 */
function undoCloseWindow(aIndex) {
  let window = null;
  if (SessionStore.getClosedWindowCount() > (aIndex || 0))
    window = SessionStore.undoCloseWindow(aIndex || 0);

  return window;
}

/*
 * Determines if a tab is "empty", usually used in the context of determining
 * if it's ok to close the tab.
 */
function isTabEmpty(aTab) {
  if (aTab.hasAttribute("busy"))
    return false;

  if (aTab.hasAttribute("customizemode"))
    return false;

  let browser = aTab.linkedBrowser;
  if (!isBlankPageURL(browser.currentURI.spec))
    return false;

  if (!checkEmptyPageOrigin(browser))
    return false;

  if (browser.canGoForward || browser.canGoBack)
    return false;

  return true;
}

/**
 * Check whether a page can be considered as 'empty', that its URI
 * reflects its origin, and that if it's loaded in a tab, that tab
 * could be considered 'empty' (e.g. like the result of opening
 * a 'blank' new tab).
 *
 * We have to do more than just check the URI, because especially
 * for things like about:blank, it is possible that the opener or
 * some other page has control over the contents of the page.
 *
 * @param browser {Browser}
 *        The browser whose page we're checking (the selected browser
 *        in this window if omitted).
 * @param uri {nsIURI}
 *        The URI against which we're checking (the browser's currentURI
 *        if omitted).
 *
 * @return false if the page was opened by or is controlled by arbitrary web
 *         content, unless that content corresponds with the URI.
 *         true if the page is blank and controlled by a principal matching
 *         that URI (or the system principal if the principal has no URI)
 */
function checkEmptyPageOrigin(browser = gBrowser.selectedBrowser,
                              uri = browser.currentURI) {
  // If another page opened this page with e.g. window.open, this page might
  // be controlled by its opener - return false.
  if (browser.hasContentOpener) {
    return false;
  }
  let contentPrincipal = browser.contentPrincipal;
  // Not all principals have URIs...
  if (contentPrincipal.URI) {
    // There are two special-cases involving about:blank. One is where
    // the user has manually loaded it and it got created with a null
    // principal. The other involves the case where we load
    // some other empty page in a browser and the current page is the
    // initial about:blank page (which has that as its principal, not
    // just URI in which case it could be web-based). Especially in
    // e10s, we need to tackle that case specifically to avoid race
    // conditions when updating the URL bar.
    //
    // Note that we check the documentURI here, since the currentURI on
    // the browser might have been set by SessionStore in order to
    // support switch-to-tab without having actually loaded the content
    // yet.
    let uriToCheck = browser.documentURI || uri;
    if ((uriToCheck.spec == "about:blank" && contentPrincipal.isNullPrincipal) ||
        contentPrincipal.URI.spec == "about:blank") {
      return true;
    }
    return contentPrincipal.URI.equals(uri);
  }
  // ... so for those that don't have them, enforce that the page has the
  // system principal (this matches e.g. on about:newtab).
  let ssm = Services.scriptSecurityManager;
  return ssm.isSystemPrincipal(contentPrincipal);
}

function BrowserOpenSyncTabs() {
  gSync.openSyncedTabsPanel();
}

function ReportFalseDeceptiveSite() {
  let docURI = gBrowser.selectedBrowser.documentURI;
  let isPhishingPage =
    docURI && docURI.spec.startsWith("about:blocked?e=deceptiveBlocked");

  if (isPhishingPage) {
    let mm = gBrowser.selectedBrowser.messageManager;
    let onMessage = (message) => {
      mm.removeMessageListener("DeceptiveBlockedDetails:Result", onMessage);
      let reportUrl = gSafeBrowsing.getReportURL("PhishMistake", message.data.blockedInfo);
      if (reportUrl) {
        openUILinkIn(reportUrl, "tab");
      } else {
        let bundle =
          Services.strings.createBundle("chrome://browser/locale/safebrowsing/safebrowsing.properties");
        Services.prompt.alert(window,
                              bundle.GetStringFromName("errorReportFalseDeceptiveTitle"),
                              bundle.formatStringFromName("errorReportFalseDeceptiveMessage",
                                                          [message.data.blockedInfo.provider], 1));
        }
    };
    mm.addMessageListener("DeceptiveBlockedDetails:Result", onMessage);

    mm.sendAsyncMessage("DeceptiveBlockedDetails");
  }
}

/**
 * Format a URL
 * eg:
 * echo formatURL("https://addons.mozilla.org/%LOCALE%/%APP%/%VERSION%/");
 * > https://addons.mozilla.org/en-US/firefox/3.0a1/
 *
 * Currently supported built-ins are LOCALE, APP, and any value from nsIXULAppInfo, uppercased.
 */
function formatURL(aFormat, aIsPref) {
  return aIsPref ? Services.urlFormatter.formatURLPref(aFormat) :
                   Services.urlFormatter.formatURL(aFormat);
}

/**
 * Utility object to handle manipulations of the identity indicators in the UI
 */
var gIdentityHandler = {
  /**
   * nsIURI for which the identity UI is displayed. This has been already
   * processed by nsIURIFixup.createExposableURI.
   */
  _uri: null,

  /**
   * We only know the connection type if this._uri has a defined "host" part.
   *
   * These URIs, like "about:", "file:" and "data:" URIs, will usually be treated as a
   * an unknown connection.
   */
  _uriHasHost: false,

  /**
   * If this tab belongs to a WebExtension, contains its WebExtensionPolicy.
   */
  _pageExtensionPolicy: null,

  /**
   * Whether this._uri refers to an internally implemented browser page.
   *
   * Note that this is set for some "about:" pages, but general "chrome:" URIs
   * are not included in this category by default.
   */
  _isSecureInternalUI: false,

  /**
   * nsISSLStatus metadata provided by gBrowser.securityUI the last time the
   * identity UI was updated, or null if the connection is not secure.
   */
  _sslStatus: null,

  /**
   * Bitmask provided by nsIWebProgressListener.onSecurityChange.
   */
  _state: 0,

  /**
   * This flag gets set if the identity popup was opened by a keypress,
   * to be able to focus it on the popupshown event.
   */
  _popupTriggeredByKeyboard: false,

  /**
   * RegExp used to decide if an about url should be shown as being part of
   * the browser UI.
   */
  _secureInternalUIWhitelist: /^(?:accounts|addons|cache|config|crashes|customizing|downloads|healthreport|license|newaddon|permissions|preferences|rights|searchreset|sessionrestore|support|welcomeback)(?:[?#]|$)/i,

  get _isBroken() {
    return this._state & Ci.nsIWebProgressListener.STATE_IS_BROKEN;
  },

  get _isSecure() {
    // If a <browser> is included within a chrome document, then this._state
    // will refer to the security state for the <browser> and not the top level
    // document. In this case, don't upgrade the security state in the UI
    // with the secure state of the embedded <browser>.
    return !this._isURILoadedFromFile && this._state & Ci.nsIWebProgressListener.STATE_IS_SECURE;
  },

  get _isEV() {
    // If a <browser> is included within a chrome document, then this._state
    // will refer to the security state for the <browser> and not the top level
    // document. In this case, don't upgrade the security state in the UI
    // with the EV state of the embedded <browser>.
    return !this._isURILoadedFromFile && this._state & Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL;
  },

  get _isMixedActiveContentLoaded() {
    return this._state & Ci.nsIWebProgressListener.STATE_LOADED_MIXED_ACTIVE_CONTENT;
  },

  get _isMixedActiveContentBlocked() {
    return this._state & Ci.nsIWebProgressListener.STATE_BLOCKED_MIXED_ACTIVE_CONTENT;
  },

  get _isMixedPassiveContentLoaded() {
    return this._state & Ci.nsIWebProgressListener.STATE_LOADED_MIXED_DISPLAY_CONTENT;
  },

  get _isCertUserOverridden() {
    return this._state & Ci.nsIWebProgressListener.STATE_CERT_USER_OVERRIDDEN;
  },

  get _isCertDistrustImminent() {
    return this._state & Ci.nsIWebProgressListener.STATE_CERT_DISTRUST_IMMINENT;
  },

  get _hasInsecureLoginForms() {
    // checks if the page has been flagged for an insecure login. Also checks
    // if the pref to degrade the UI is set to true
    return LoginManagerParent.hasInsecureLoginForms(gBrowser.selectedBrowser) &&
           Services.prefs.getBoolPref("security.insecure_password.ui.enabled");
  },

  // smart getters
  get _identityPopup() {
    delete this._identityPopup;
    return this._identityPopup = document.getElementById("identity-popup");
  },
  get _identityBox() {
    delete this._identityBox;
    return this._identityBox = document.getElementById("identity-box");
  },
  get _identityPopupMultiView() {
    delete this._identityPopupMultiView;
    return this._identityPopupMultiView = document.getElementById("identity-popup-multiView");
  },
  get _identityPopupMainView() {
    delete this._identityPopupMainView;
    return this._identityPopupMainView = document.getElementById("identity-popup-mainView");
  },
  get _identityPopupContentHosts() {
    delete this._identityPopupContentHosts;
    return this._identityPopupContentHosts =
      [...document.querySelectorAll(".identity-popup-host")];
  },
  get _identityPopupContentHostless() {
    delete this._identityPopupContentHostless;
    return this._identityPopupContentHostless =
      [...document.querySelectorAll(".identity-popup-hostless")];
  },
  get _identityPopupContentOwner() {
    delete this._identityPopupContentOwner;
    return this._identityPopupContentOwner =
      document.getElementById("identity-popup-content-owner");
  },
  get _identityPopupContentSupp() {
    delete this._identityPopupContentSupp;
    return this._identityPopupContentSupp =
      document.getElementById("identity-popup-content-supplemental");
  },
  get _identityPopupContentVerif() {
    delete this._identityPopupContentVerif;
    return this._identityPopupContentVerif =
      document.getElementById("identity-popup-content-verifier");
  },
  get _identityPopupMixedContentLearnMore() {
    delete this._identityPopupMixedContentLearnMore;
    return this._identityPopupMixedContentLearnMore =
      document.getElementById("identity-popup-mcb-learn-more");
  },
  get _identityPopupInsecureLoginFormsLearnMore() {
    delete this._identityPopupInsecureLoginFormsLearnMore;
    return this._identityPopupInsecureLoginFormsLearnMore =
      document.getElementById("identity-popup-insecure-login-forms-learn-more");
  },
  get _identityIconLabels() {
    delete this._identityIconLabels;
    return this._identityIconLabels = document.getElementById("identity-icon-labels");
  },
  get _identityIconLabel() {
    delete this._identityIconLabel;
    return this._identityIconLabel = document.getElementById("identity-icon-label");
  },
  get _connectionIcon() {
    delete this._connectionIcon;
    return this._connectionIcon = document.getElementById("connection-icon");
  },
  get _extensionIcon() {
    delete this._extensionIcon;
    return this._extensionIcon = document.getElementById("extension-icon");
  },
  get _overrideService() {
    delete this._overrideService;
    return this._overrideService = Cc["@mozilla.org/security/certoverride;1"]
                                     .getService(Ci.nsICertOverrideService);
  },
  get _identityIconCountryLabel() {
    delete this._identityIconCountryLabel;
    return this._identityIconCountryLabel = document.getElementById("identity-icon-country-label");
  },
  get _identityIcon() {
    delete this._identityIcon;
    return this._identityIcon = document.getElementById("identity-icon");
  },
  get _permissionList() {
    delete this._permissionList;
    return this._permissionList = document.getElementById("identity-popup-permission-list");
  },
  get _permissionEmptyHint() {
    delete this._permissionEmptyHint;
    return this._permissionEmptyHint = document.getElementById("identity-popup-permission-empty-hint");
  },
  get _permissionReloadHint() {
    delete this._permissionReloadHint;
    return this._permissionReloadHint = document.getElementById("identity-popup-permission-reload-hint");
  },
  get _popupExpander() {
    delete this._popupExpander;
    return this._popupExpander = document.getElementById("identity-popup-security-expander");
  },
  get _permissionAnchors() {
    delete this._permissionAnchors;
    let permissionAnchors = {};
    for (let anchor of document.getElementById("blocked-permissions-container").children) {
      permissionAnchors[anchor.getAttribute("data-permission-id")] = anchor;
    }
    return this._permissionAnchors = permissionAnchors;
  },

  /**
   * Handler for mouseclicks on the "More Information" button in the
   * "identity-popup" panel.
   */
  handleMoreInfoClick(event) {
    displaySecurityInfo();
    event.stopPropagation();
    PanelMultiView.hidePopup(this._identityPopup);
  },

  showSecuritySubView() {
    this._identityPopupMultiView.showSubView("identity-popup-securityView",
                                             this._popupExpander);

    // Elements of hidden views have -moz-user-focus:ignore but setting that
    // per CSS selector doesn't blur a focused element in those hidden views.
    Services.focus.clearFocus(window);
  },

  disableMixedContentProtection() {
    // Use telemetry to measure how often unblocking happens
    const kMIXED_CONTENT_UNBLOCK_EVENT = 2;
    let histogram =
      Services.telemetry.getHistogramById(
        "MIXED_CONTENT_UNBLOCK_COUNTER");
    histogram.add(kMIXED_CONTENT_UNBLOCK_EVENT);
    // Reload the page with the content unblocked
    BrowserReloadWithFlags(
      Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_MIXED_CONTENT);
    PanelMultiView.hidePopup(this._identityPopup);
  },

  enableMixedContentProtection() {
    gBrowser.selectedBrowser.messageManager.sendAsyncMessage(
      "MixedContent:ReenableProtection", {});
    BrowserReload();
    PanelMultiView.hidePopup(this._identityPopup);
  },

  removeCertException() {
    if (!this._uriHasHost) {
      Cu.reportError("Trying to revoke a cert exception on a URI without a host?");
      return;
    }
    let host = this._uri.host;
    let port = this._uri.port > 0 ? this._uri.port : 443;
    this._overrideService.clearValidityOverride(host, port);
    BrowserReloadSkipCache();
    PanelMultiView.hidePopup(this._identityPopup);
  },

  /**
   * Helper to parse out the important parts of _sslStatus (of the SSL cert in
   * particular) for use in constructing identity UI strings
  */
  getIdentityData() {
    var result = {};
    var cert = this._sslStatus.serverCert;

    // Human readable name of Subject
    result.subjectOrg = cert.organization;

    // SubjectName fields, broken up for individual access
    if (cert.subjectName) {
      result.subjectNameFields = {};
      cert.subjectName.split(",").forEach(function(v) {
        var field = v.split("=");
        this[field[0]] = field[1];
      }, result.subjectNameFields);

      // Call out city, state, and country specifically
      result.city = result.subjectNameFields.L;
      result.state = result.subjectNameFields.ST;
      result.country = result.subjectNameFields.C;
    }

    // Human readable name of Certificate Authority
    result.caOrg =  cert.issuerOrganization || cert.issuerCommonName;
    result.cert = cert;

    return result;
  },

  /**
   * Update the identity user interface for the page currently being displayed.
   *
   * This examines the SSL certificate metadata, if available, as well as the
   * connection type and other security-related state information for the page.
   *
   * @param state
   *        Bitmask provided by nsIWebProgressListener.onSecurityChange.
   * @param uri
   *        nsIURI for which the identity UI should be displayed, already
   *        processed by nsIURIFixup.createExposableURI.
   */
  updateIdentity(state, uri) {
    let shouldHidePopup = this._uri && (this._uri.spec != uri.spec);
    this._state = state;

    // Firstly, populate the state properties required to display the UI. See
    // the documentation of the individual properties for details.
    this.setURI(uri);
    this._sslStatus = gBrowser.securityUI
                              .QueryInterface(Ci.nsISSLStatusProvider)
                              .SSLStatus;
    if (this._sslStatus) {
      this._sslStatus.QueryInterface(Ci.nsISSLStatus);
    }

    // Then, update the user interface with the available data.
    this.refreshIdentityBlock();
    // Handle a location change while the Control Center is focused
    // by closing the popup (bug 1207542)
    if (shouldHidePopup) {
      PanelMultiView.hidePopup(this._identityPopup);
    }

    // NOTE: We do NOT update the identity popup (the control center) when
    // we receive a new security state on the existing page (i.e. from a
    // subframe). If the user opened the popup and looks at the provided
    // information we don't want to suddenly change the panel contents.

    // Finally, if there are warnings to issue, issue them
    if (this._isCertDistrustImminent) {
      let consoleMsg = Cc["@mozilla.org/scripterror;1"].createInstance(Ci.nsIScriptError);
      let windowId = gBrowser.selectedBrowser.innerWindowID;
      let message = gBrowserBundle.GetStringFromName("certImminentDistrust.message");
      // Use uri.prePath instead of initWithSourceURI() so that these can be
      // de-duplicated on the scheme+host+port combination.
      consoleMsg.initWithWindowID(message, uri.prePath, null, 0, 0,
                                  Ci.nsIScriptError.warningFlag, "SSL",
                                  windowId);
      Services.console.logMessage(consoleMsg);
    }
  },

  /**
   * This is called asynchronously when requested by the Logins module, after
   * the insecure login forms state for the page has been updated.
   */
  refreshForInsecureLoginForms() {
    // Check this._uri because we don't want to refresh the user interface if
    // this is called before the first page load in the window for any reason.
    if (!this._uri) {
      return;
    }
    this.refreshIdentityBlock();
  },

  updateSharingIndicator() {
    let tab = gBrowser.selectedTab;
    this._sharingState = tab._sharingState;

    this._identityBox.removeAttribute("paused");
    this._identityBox.removeAttribute("sharing");
    if (this._sharingState && this._sharingState.sharing) {
      this._identityBox.setAttribute("sharing", this._sharingState.sharing);
      if (this._sharingState.paused) {
        this._identityBox.setAttribute("paused", "true");
      }
    }

    if (this._identityPopup.state == "open") {
      this.updateSitePermissions();
      PanelView.forNode(this._identityPopupMainView)
               .descriptionHeightWorkaround();
    }
  },

  /**
   * Attempt to provide proper IDN treatment for host names
   */
  getEffectiveHost() {
    if (!this._IDNService)
      this._IDNService = Cc["@mozilla.org/network/idn-service;1"]
                         .getService(Ci.nsIIDNService);
    try {
      return this._IDNService.convertToDisplayIDN(this._uri.host, {});
    } catch (e) {
      // If something goes wrong (e.g. host is an IP address) just fail back
      // to the full domain.
      return this._uri.host;
    }
  },

  /**
   * Return the CSS class name to set on the "fullscreen-warning" element to
   * display information about connection security in the notification shown
   * when a site enters the fullscreen mode.
   */
  get pointerlockFsWarningClassName() {
    // Note that the fullscreen warning does not handle _isSecureInternalUI.
    if (this._uriHasHost && this._isEV) {
      return "verifiedIdentity";
    }
    if (this._uriHasHost && this._isSecure) {
      return "verifiedDomain";
    }
    return "unknownIdentity";
  },

  /**
   * Updates the identity block user interface with the data from this object.
   */
  refreshIdentityBlock() {
    if (!this._identityBox) {
      return;
    }

    let icon_label = "";
    let tooltip = "";
    let icon_country_label = "";
    let icon_labels_dir = "ltr";

    if (this._isSecureInternalUI) {
      this._identityBox.className = "chromeUI";
      let brandBundle = document.getElementById("bundle_brand");
      icon_label = brandBundle.getString("brandShorterName");
    } else if (this._uriHasHost && this._isEV) {
      this._identityBox.className = "verifiedIdentity";
      if (this._isMixedActiveContentBlocked) {
        this._identityBox.classList.add("mixedActiveBlocked");
      }

      if (!this._isCertUserOverridden) {
        // If it's identified, then we can populate the dialog with credentials
        let iData = this.getIdentityData();
        tooltip = gNavigatorBundle.getFormattedString("identity.identified.verifier",
                                                      [iData.caOrg]);
        icon_label = iData.subjectOrg;
        if (iData.country)
          icon_country_label = "(" + iData.country + ")";

        // If the organization name starts with an RTL character, then
        // swap the positions of the organization and country code labels.
        // The Unicode ranges reflect the definition of the UTF16_CODE_UNIT_IS_BIDI
        // macro in intl/unicharutil/util/nsBidiUtils.h. When bug 218823 gets
        // fixed, this test should be replaced by one adhering to the
        // Unicode Bidirectional Algorithm proper (at the paragraph level).
        icon_labels_dir = /^[\u0590-\u08ff\ufb1d-\ufdff\ufe70-\ufefc\ud802\ud803\ud83a\ud83b]/.test(icon_label) ?
                          "rtl" : "ltr";
      }
    } else if (this._pageExtensionPolicy) {
      this._identityBox.className = "extensionPage";
      let extensionName = this._pageExtensionPolicy.name;
      icon_label = gNavigatorBundle.getFormattedString(
        "identity.extension.label", [extensionName]);
    } else if (this._uriHasHost && this._isSecure) {
      this._identityBox.className = "verifiedDomain";
      if (this._isMixedActiveContentBlocked) {
        this._identityBox.classList.add("mixedActiveBlocked");
      }
      if (!this._isCertUserOverridden) {
        // It's a normal cert, verifier is the CA Org.
        tooltip = gNavigatorBundle.getFormattedString("identity.identified.verifier",
                                                      [this.getIdentityData().caOrg]);
      }
    } else if (!this._uriHasHost) {
      this._identityBox.className = "unknownIdentity";
    } else if (gBrowser.selectedBrowser.documentURI &&
               (gBrowser.selectedBrowser.documentURI.scheme == "about" ||
               gBrowser.selectedBrowser.documentURI.scheme == "chrome")) {
        // For net errors we should not show notSecure as it's likely confusing
      this._identityBox.className = "unknownIdentity";
    } else {
      if (this._isBroken) {
        this._identityBox.className = "unknownIdentity";

        if (this._isMixedActiveContentLoaded) {
          this._identityBox.classList.add("mixedActiveContent");
        } else if (this._isMixedActiveContentBlocked) {
          this._identityBox.classList.add("mixedDisplayContentLoadedActiveBlocked");
        } else if (this._isMixedPassiveContentLoaded) {
          this._identityBox.classList.add("mixedDisplayContent");
        } else {
          this._identityBox.classList.add("weakCipher");
        }
      } else {
        let warnOnInsecure = Services.prefs.getBoolPref("security.insecure_connection_icon.enabled") ||
                             (Services.prefs.getBoolPref("security.insecure_connection_icon.pbmode.enabled") &&
                             PrivateBrowsingUtils.isWindowPrivate(window));
        let className = warnOnInsecure ? "notSecure" : "unknownIdentity";
        this._identityBox.className = className;

        let warnTextOnInsecure = Services.prefs.getBoolPref("security.insecure_connection_text.enabled") ||
                                 (Services.prefs.getBoolPref("security.insecure_connection_text.pbmode.enabled") &&
                                 PrivateBrowsingUtils.isWindowPrivate(window));
        if (warnTextOnInsecure) {
          icon_label = gNavigatorBundle.getString("identity.notSecure.label");
          this._identityBox.classList.add("notSecureText");
        }
      }
      if (this._hasInsecureLoginForms) {
        // Insecure login forms can only be present on "unknown identity"
        // pages, either already insecure or with mixed active content loaded.
        this._identityBox.classList.add("insecureLoginForms");
      }
    }

    if (this._isCertUserOverridden) {
      this._identityBox.classList.add("certUserOverridden");
      // Cert is trusted because of a security exception, verifier is a special string.
      tooltip = gNavigatorBundle.getString("identity.identified.verified_by_you");
    }

    let permissionAnchors = this._permissionAnchors;

    // hide all permission icons
    for (let icon of Object.values(permissionAnchors)) {
      icon.removeAttribute("showing");
    }

    // keeps track if we should show an indicator that there are active permissions
    let hasGrantedPermissions = false;

    // show permission icons
    let permissions = SitePermissions.getAllForBrowser(gBrowser.selectedBrowser);
    for (let permission of permissions) {
      if (permission.state == SitePermissions.BLOCK) {

        let icon = permissionAnchors[permission.id];
        if (icon) {
          icon.setAttribute("showing", "true");
        }

      } else if (permission.state != SitePermissions.UNKNOWN) {
        hasGrantedPermissions = true;
      }
    }

    if (hasGrantedPermissions) {
      this._identityBox.classList.add("grantedPermissions");
    }

    // Show blocked popup icon in the identity-box if popups are blocked
    // irrespective of popup permission capability value.
    if (gBrowser.selectedBrowser.blockedPopups &&
        gBrowser.selectedBrowser.blockedPopups.length) {
      let icon = permissionAnchors.popup;
      icon.setAttribute("showing", "true");
    }

    // Push the appropriate strings out to the UI
    this._connectionIcon.setAttribute("tooltiptext", tooltip);

    if (this._pageExtensionPolicy) {
      let extensionName = this._pageExtensionPolicy.name;
      this._extensionIcon.setAttribute("tooltiptext",
        gNavigatorBundle.getFormattedString("identity.extension.tooltip", [extensionName]));
    }

    this._identityIconLabels.setAttribute("tooltiptext", tooltip);
    this._identityIcon.setAttribute("tooltiptext", gNavigatorBundle.getString("identity.icon.tooltip"));
    this._identityIconLabel.setAttribute("value", icon_label);
    this._identityIconCountryLabel.setAttribute("value", icon_country_label);
    // Set cropping and direction
    this._identityIconLabel.setAttribute("crop", icon_country_label ? "end" : "center");
    this._identityIconLabel.parentNode.style.direction = icon_labels_dir;
    // Hide completely if the organization label is empty
    this._identityIconLabel.parentNode.collapsed = !icon_label;
  },

  /**
   * Set up the title and content messages for the identity message popup,
   * based on the specified mode, and the details of the SSL cert, where
   * applicable
   */
  refreshIdentityPopup() {
    // Update "Learn More" for Mixed Content Blocking and Insecure Login Forms.
    let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
    this._identityPopupMixedContentLearnMore
        .setAttribute("href", baseURL + "mixed-content");
    this._identityPopupInsecureLoginFormsLearnMore
        .setAttribute("href", baseURL + "insecure-password");

    // This is in the properties file because the expander used to switch its tooltip.
    this._popupExpander.tooltipText = gNavigatorBundle.getString("identity.showDetails.tooltip");

    // Determine connection security information.
    let connection = "not-secure";
    if (this._isSecureInternalUI) {
      connection = "chrome";
    } else if (this._pageExtensionPolicy) {
      connection = "extension";
    } else if (this._isURILoadedFromFile) {
      connection = "file";
    } else if (this._isEV) {
      connection = "secure-ev";
    } else if (this._isCertUserOverridden) {
      connection = "secure-cert-user-overridden";
    } else if (this._isSecure) {
      connection = "secure";
    }

    // Determine if there are insecure login forms.
    let loginforms = "secure";
    if (this._hasInsecureLoginForms) {
      loginforms = "insecure";
    }

    // Determine the mixed content state.
    let mixedcontent = [];
    if (this._isMixedPassiveContentLoaded) {
      mixedcontent.push("passive-loaded");
    }
    if (this._isMixedActiveContentLoaded) {
      mixedcontent.push("active-loaded");
    } else if (this._isMixedActiveContentBlocked) {
      mixedcontent.push("active-blocked");
    }
    mixedcontent = mixedcontent.join(" ");

    // We have no specific flags for weak ciphers (yet). If a connection is
    // broken and we can't detect any mixed content loaded then it's a weak
    // cipher.
    let ciphers = "";
    if (this._isBroken && !this._isMixedActiveContentLoaded && !this._isMixedPassiveContentLoaded) {
      ciphers = "weak";
    }

    // Update all elements.
    let elementIDs = [
      "identity-popup",
      "identity-popup-securityView-body",
    ];

    function updateAttribute(elem, attr, value) {
      if (value) {
        elem.setAttribute(attr, value);
      } else {
        elem.removeAttribute(attr);
      }
    }

    for (let id of elementIDs) {
      let element = document.getElementById(id);
      updateAttribute(element, "connection", connection);
      updateAttribute(element, "loginforms", loginforms);
      updateAttribute(element, "ciphers", ciphers);
      updateAttribute(element, "mixedcontent", mixedcontent);
      updateAttribute(element, "isbroken", this._isBroken);
    }

    // Initialize the optional strings to empty values
    let supplemental = "";
    let verifier = "";
    let host = "";
    let owner = "";
    let hostless = false;

    try {
      host = this.getEffectiveHost();
    } catch (e) {
      // Some URIs might have no hosts.
    }

    // Fallback for special protocols.
    if (!host) {
      host = this._uri.specIgnoringRef;
      // Special URIs without a host (eg, about:) should crop the end so
      // the protocol can be seen.
      hostless = true;
    }

    if (this._pageExtensionPolicy) {
      host = this._pageExtensionPolicy.name;
    }

    // Fill in the CA name if we have a valid TLS certificate.
    if (this._isSecure || this._isCertUserOverridden) {
      verifier = this._identityIconLabels.tooltipText;
    }

    // Fill in organization information if we have a valid EV certificate.
    if (this._isEV) {
      let iData = this.getIdentityData();
      host = owner = iData.subjectOrg;
      verifier = this._identityIconLabels.tooltipText;

      // Build an appropriate supplemental block out of whatever location data we have
      if (iData.city)
        supplemental += iData.city + "\n";
      if (iData.state && iData.country)
        supplemental += gNavigatorBundle.getFormattedString("identity.identified.state_and_country",
                                                            [iData.state, iData.country]);
      else if (iData.state) // State only
        supplemental += iData.state;
      else if (iData.country) // Country only
        supplemental += iData.country;
    }

    // Push the appropriate strings out to the UI.
    this._identityPopupContentHosts.forEach((el) => {
      el.textContent = host;
      el.hidden = hostless;
    });
    this._identityPopupContentHostless.forEach((el) => {
      el.setAttribute("value", host);
      el.hidden = !hostless;
    });
    this._identityPopupContentOwner.textContent = owner;
    this._identityPopupContentSupp.textContent = supplemental;
    this._identityPopupContentVerif.textContent = verifier;

    // Update per-site permissions section.
    this.updateSitePermissions();
  },

  setURI(uri) {
    this._uri = uri;

    try {
      // Account for file: urls and catch when "" is the value
      this._uriHasHost = !!this._uri.host;
    } catch (ex) {
      this._uriHasHost = false;
    }

    this._isSecureInternalUI = uri.schemeIs("about") &&
      this._secureInternalUIWhitelist.test(uri.pathQueryRef);

    this._pageExtensionPolicy = WebExtensionPolicy.getByURI(uri);

    // Create a channel for the sole purpose of getting the resolved URI
    // of the request to determine if it's loaded from the file system.
    this._isURILoadedFromFile = false;
    let chanOptions = {uri: this._uri, loadUsingSystemPrincipal: true};
    let resolvedURI;
    try {
      resolvedURI = NetUtil.newChannel(chanOptions).URI;
      if (resolvedURI.schemeIs("jar")) {
        // Given a URI "jar:<jar-file-uri>!/<jar-entry>"
        // create a new URI using <jar-file-uri>!/<jar-entry>
        resolvedURI = NetUtil.newURI(resolvedURI.pathQueryRef);
      }
      // Check the URI again after resolving.
      this._isURILoadedFromFile = resolvedURI.schemeIs("file");
    } catch (ex) {
      // NetUtil's methods will throw for malformed URIs and the like
    }
  },

  /**
   * Click handler for the identity-box element in primary chrome.
   */
  handleIdentityButtonEvent(event) {
    event.stopPropagation();

    if ((event.type == "click" && event.button != 0) ||
        (event.type == "keypress" && event.charCode != KeyEvent.DOM_VK_SPACE &&
         event.keyCode != KeyEvent.DOM_VK_RETURN)) {
      return; // Left click, space or enter only
    }

    // Don't allow left click, space or enter if the location has been modified,
    // so long as we're not sharing any devices.
    // If we are sharing a device, the identity block is prevented by CSS from
    // being focused (and therefore, interacted with) by the user. However, we
    // want to allow opening the identity popup from the device control menu,
    // which calls click() on the identity button, so we don't return early.
    if (!this._sharingState &&
        gURLBar.getAttribute("pageproxystate") != "valid") {
      return;
    }

    this._popupTriggeredByKeyboard = event.type == "keypress";

    // Make sure that the display:none style we set in xul is removed now that
    // the popup is actually needed
    this._identityPopup.hidden = false;

    // Remove the reload hint that we show after a user has cleared a permission.
    this._permissionReloadHint.setAttribute("hidden", "true");

    // Update the popup strings
    this.refreshIdentityPopup();

    // Add the "open" attribute to the identity box for styling
    this._identityBox.setAttribute("open", "true");

    // Now open the popup, anchored off the primary chrome element
    PanelMultiView.openPopup(this._identityPopup, this._identityIcon,
                             "bottomcenter topleft").catch(Cu.reportError);
  },

  onPopupShown(event) {
    if (event.target == this._identityPopup) {
      if (this._popupTriggeredByKeyboard) {
        // Move focus to the next available element in the identity popup.
        // This is required by role=alertdialog and fixes an issue where
        // an already open panel would steal focus from the identity popup.
        document.commandDispatcher.advanceFocusIntoSubtree(this._identityPopup);
      }

      window.addEventListener("focus", this, true);
    }
  },

  onPopupHidden(event) {
    if (event.target == this._identityPopup) {
      window.removeEventListener("focus", this, true);
      this._identityBox.removeAttribute("open");
    }
  },

  handleEvent(event) {
    let elem = document.activeElement;
    let position = elem.compareDocumentPosition(this._identityPopup);

    if (!(position & (Node.DOCUMENT_POSITION_CONTAINS |
                      Node.DOCUMENT_POSITION_CONTAINED_BY)) &&
        !this._identityPopup.hasAttribute("noautohide")) {
      // Hide the panel when focusing an element that is
      // neither an ancestor nor descendant unless the panel has
      // @noautohide (e.g. for a tour).
      PanelMultiView.hidePopup(this._identityPopup);
    }
  },

  observe(subject, topic, data) {
    if (topic == "perm-changed") {
      this.refreshIdentityBlock();
    }
  },

  onDragStart(event) {
    if (gURLBar.getAttribute("pageproxystate") != "valid")
      return;

    let value = gBrowser.currentURI.displaySpec;
    let urlString = value + "\n" + gBrowser.contentTitle;
    let htmlString = "<a href=\"" + value + "\">" + value + "</a>";

    let dt = event.dataTransfer;
    dt.setData("text/x-moz-url", urlString);
    dt.setData("text/uri-list", value);
    dt.setData("text/plain", value);
    dt.setData("text/html", htmlString);
    dt.setDragImage(this._identityIcon, 16, 16);
  },

  onLocationChange() {
    this._permissionReloadHint.setAttribute("hidden", "true");

    if (!this._permissionList.hasChildNodes()) {
      this._permissionEmptyHint.removeAttribute("hidden");
    }
  },

  updateSitePermissions() {
    while (this._permissionList.hasChildNodes())
      this._permissionList.removeChild(this._permissionList.lastChild);

    let permissions =
      SitePermissions.getAllPermissionDetailsForBrowser(gBrowser.selectedBrowser);

    if (this._sharingState) {
      // If WebRTC device or screen permissions are in use, we need to find
      // the associated permission item to set the sharingState field.
      for (let id of ["camera", "microphone", "screen"]) {
        if (this._sharingState[id]) {
          let found = false;
          for (let permission of permissions) {
            if (permission.id != id)
              continue;
            found = true;
            permission.sharingState = this._sharingState[id];
            break;
          }
          if (!found) {
            // If the permission item we were looking for doesn't exist,
            // the user has temporarily allowed sharing and we need to add
            // an item in the permissions array to reflect this.
            permissions.push({
              id,
              state: SitePermissions.ALLOW,
              scope: SitePermissions.SCOPE_REQUEST,
              sharingState: this._sharingState[id],
            });
          }
        }
      }
    }

    let hasBlockedPopupIndicator = false;
    for (let permission of permissions) {
      let item = this._createPermissionItem(permission);
      this._permissionList.appendChild(item);

      if (permission.id == "popup" &&
          gBrowser.selectedBrowser.blockedPopups &&
          gBrowser.selectedBrowser.blockedPopups.length) {
        this._createBlockedPopupIndicator();
        hasBlockedPopupIndicator = true;
      }
    }

    if (gBrowser.selectedBrowser.blockedPopups &&
        gBrowser.selectedBrowser.blockedPopups.length &&
        !hasBlockedPopupIndicator) {
      let permission = {
        id: "popup",
        state: SitePermissions.getDefault("popup"),
        scope: SitePermissions.SCOPE_PERSISTENT,
      };
      let item = this._createPermissionItem(permission);
      this._permissionList.appendChild(item);
      this._createBlockedPopupIndicator();
    }

    // Show a placeholder text if there's no permission and no reload hint.
    if (!this._permissionList.hasChildNodes() &&
        this._permissionReloadHint.hasAttribute("hidden")) {
      this._permissionEmptyHint.removeAttribute("hidden");
    } else {
      this._permissionEmptyHint.setAttribute("hidden", "true");
    }
  },

  _createPermissionItem(aPermission) {
    let container = document.createElement("hbox");
    container.setAttribute("class", "identity-popup-permission-item");
    container.setAttribute("align", "center");

    let img = document.createElement("image");
    img.classList.add("identity-popup-permission-icon");
    if (aPermission.id == "plugin:flash") {
      img.classList.add("plugin-icon");
    } else {
      img.classList.add(aPermission.id + "-icon");
    }
    if (aPermission.state == SitePermissions.BLOCK)
      img.classList.add("blocked-permission-icon");

    if (aPermission.sharingState == Ci.nsIMediaManagerService.STATE_CAPTURE_ENABLED ||
       (aPermission.id == "screen" && aPermission.sharingState &&
        !aPermission.sharingState.includes("Paused"))) {
      img.classList.add("in-use");

      // Synchronize control center and identity block blinking animations.
      window.promiseDocumentFlushed(() => {
        let sharingIconBlink = document.getElementById("sharing-icon").getAnimations()[0];
        let imgBlink = img.getAnimations()[0];
        return [sharingIconBlink, imgBlink];
      }).then(([sharingIconBlink, imgBlink]) => {
        if (sharingIconBlink && imgBlink) {
          imgBlink.startTime = sharingIconBlink.startTime;
        }
      });
    }

    let nameLabel = document.createElement("label");
    nameLabel.setAttribute("flex", "1");
    nameLabel.setAttribute("class", "identity-popup-permission-label");
    nameLabel.textContent = SitePermissions.getPermissionLabel(aPermission.id);

    let isPolicyPermission = aPermission.scope == SitePermissions.SCOPE_POLICY;

    if (aPermission.id == "popup" && !isPolicyPermission) {
      let menulist = document.createElement("menulist");
      let menupopup = document.createElement("menupopup");
      let block = document.createElement("vbox");
      block.setAttribute("id", "identity-popup-popup-container");
      menulist.setAttribute("sizetopopup", "none");
      menulist.setAttribute("class", "identity-popup-popup-menulist");
      menulist.setAttribute("id", "identity-popup-popup-menulist");

      for (let state of SitePermissions.getAvailableStates(aPermission.id)) {
        let menuitem = document.createElement("menuitem");
        // We need to correctly display the default/unknown state, which has its
        // own integer value (0) but represents one of the other states.
        if (state == SitePermissions.getDefault(aPermission.id)) {
          menuitem.setAttribute("value", "0");
        } else {
          menuitem.setAttribute("value", state);
        }
        menuitem.setAttribute("label", SitePermissions.getMultichoiceStateLabel(state));
        menupopup.appendChild(menuitem);
      }

      menulist.appendChild(menupopup);

      if (aPermission.state == SitePermissions.getDefault(aPermission.id)) {
        menulist.value = "0";
      } else {
        menulist.value = aPermission.state;
      }

      // Avoiding listening to the "select" event on purpose. See Bug 1404262.
      menulist.addEventListener("command", () => {
        SitePermissions.set(gBrowser.currentURI, "popup", menulist.selectedItem.value);
      });

      container.appendChild(img);
      container.appendChild(nameLabel);
      container.appendChild(menulist);
      block.appendChild(container);

      return block;
    }

    let stateLabel = document.createElement("label");
    stateLabel.setAttribute("flex", "1");
    stateLabel.setAttribute("class", "identity-popup-permission-state-label");
    let {state, scope} = aPermission;
    // If the user did not permanently allow this device but it is currently
    // used, set the variables to display a "temporarily allowed" info.
    if (state != SitePermissions.ALLOW && aPermission.sharingState) {
      state = SitePermissions.ALLOW;
      scope = SitePermissions.SCOPE_REQUEST;
    }
    stateLabel.textContent = SitePermissions.getCurrentStateLabel(state, aPermission.id, scope);

    container.appendChild(img);
    container.appendChild(nameLabel);
    container.appendChild(stateLabel);

    /* We return the permission item here without a remove button if the permission is a
       SCOPE_POLICY permission. Policy permissions cannot be removed/changed for the duration
       of the browser session. */
    if (isPolicyPermission) {
      return container;
    }

    let button = document.createElement("button");
    button.setAttribute("class", "identity-popup-permission-remove-button");
    let tooltiptext = gNavigatorBundle.getString("permissions.remove.tooltip");
    button.setAttribute("tooltiptext", tooltiptext);
    button.addEventListener("command", () => {
      let browser = gBrowser.selectedBrowser;
      this._permissionList.removeChild(container);
      if (aPermission.sharingState &&
          ["camera", "microphone", "screen"].includes(aPermission.id)) {
        let windowId = this._sharingState.windowId;
        if (aPermission.id == "screen") {
          windowId = "screen:" + windowId;
        } else {
          // If we set persistent permissions or the sharing has
          // started due to existing persistent permissions, we need
          // to handle removing these even for frames with different hostnames.
          let uris = browser._devicePermissionURIs || [];
          for (let uri of uris) {
            // It's not possible to stop sharing one of camera/microphone
            // without the other.
            for (let id of ["camera", "microphone"]) {
              if (this._sharingState[id]) {
                let perm = SitePermissions.get(uri, id);
                if (perm.state == SitePermissions.ALLOW &&
                    perm.scope == SitePermissions.SCOPE_PERSISTENT) {
                  SitePermissions.remove(uri, id);
                }
              }
            }
          }
        }
        browser.messageManager.sendAsyncMessage("webrtc:StopSharing", windowId);
        webrtcUI.forgetActivePermissionsFromBrowser(gBrowser.selectedBrowser);
      }
      SitePermissions.remove(gBrowser.currentURI, aPermission.id, browser);

      this._permissionReloadHint.removeAttribute("hidden");
      PanelView.forNode(this._identityPopupMainView)
               .descriptionHeightWorkaround();

      // Set telemetry values for clearing a permission
      let histogram = Services.telemetry.getKeyedHistogramById("WEB_PERMISSION_CLEARED");

      let permissionType = 0;
      if (aPermission.state == SitePermissions.ALLOW &&
          aPermission.scope == SitePermissions.SCOPE_PERSISTENT) {
        // 1 : clear permanently allowed permission
        permissionType = 1;
      } else if (aPermission.state == SitePermissions.BLOCK &&
                 aPermission.scope == SitePermissions.SCOPE_PERSISTENT) {
        // 2 : clear permanently blocked permission
        permissionType = 2;
      } else if (aPermission.state == SitePermissions.ALLOW) {
        // 3 : clear temporary allowed permission
        permissionType = 3;
      } else if (aPermission.state == SitePermissions.BLOCK) {
        // 4 : clear temporary blocked permission
        permissionType = 4;
      }

      histogram.add("(all)", permissionType);
      histogram.add(aPermission.id, permissionType);
    });

    container.appendChild(button);

    return container;
  },

  _createBlockedPopupIndicator() {
    let indicator = document.createElement("hbox");
    indicator.setAttribute("class", "identity-popup-permission-item");
    indicator.setAttribute("align", "center");
    indicator.setAttribute("id", "blocked-popup-indicator-item");

    let icon = document.createElement("image");
    icon.setAttribute("class", "popup-subitem identity-popup-permission-icon");

    let text = document.createElement("label");
    text.setAttribute("flex", "1");
    text.setAttribute("class", "identity-popup-permission-label text-link");

    let popupCount = gBrowser.selectedBrowser.blockedPopups.length;
    let messageBase = gNavigatorBundle.getString("popupShowBlockedPopupsIndicatorText");
    let message = PluralForm.get(popupCount, messageBase)
                                 .replace("#1", popupCount);
    text.textContent = message;

    text.addEventListener("click", () => {
      gPopupBlockerObserver.showAllBlockedPopups(gBrowser.selectedBrowser);
    });

    indicator.appendChild(icon);
    indicator.appendChild(text);

    document.getElementById("identity-popup-popup-container").appendChild(indicator);
  },
};

/**
 * Fired on the "marionette-remote-control" system notification,
 * indicating if the browser session is under remote control.
 */
const gRemoteControl = {
  observe(subject, topic, data) {
    gRemoteControl.updateVisualCue(data);
  },

  updateVisualCue(enabled) {
    const mainWindow = document.documentElement;
    if (enabled) {
      mainWindow.setAttribute("remotecontrol", "true");
    } else {
      mainWindow.removeAttribute("remotecontrol");
    }
  },
};

function getNotificationBox(aWindow) {
  var foundBrowser = gBrowser.getBrowserForDocument(aWindow.document);
  if (foundBrowser)
    return gBrowser.getNotificationBox(foundBrowser);
  return null;
}

function getTabModalPromptBox(aWindow) {
  var foundBrowser = gBrowser.getBrowserForDocument(aWindow.document);
  if (foundBrowser)
    return gBrowser.getTabModalPromptBox(foundBrowser);
  return null;
}

/* DEPRECATED */
function getBrowser() {
  return gBrowser;
}

const gAccessibilityServiceIndicator = {
  init() {
    // Pref to enable accessibility service indicator.
    Services.prefs.addObserver("accessibility.indicator.enabled", this);
    // Accessibility service init/shutdown event.
    Services.obs.addObserver(this, "a11y-init-or-shutdown");
    this.update(Services.appinfo.accessibilityEnabled);
  },

  update(accessibilityEnabled = false) {
    if (this.enabled && accessibilityEnabled) {
      this._active = true;
      document.documentElement.setAttribute("accessibilitymode", "true");
      [...document.querySelectorAll(".accessibility-indicator")].forEach(
        indicator => ["click", "keypress"].forEach(type =>
          indicator.addEventListener(type, this)));
      TabsInTitlebar.updateAppearance(true);
    } else if (this._active) {
      this._active = false;
      document.documentElement.removeAttribute("accessibilitymode");
      [...document.querySelectorAll(".accessibility-indicator")].forEach(
        indicator => ["click", "keypress"].forEach(type =>
          indicator.removeEventListener(type, this)));
      TabsInTitlebar.updateAppearance(true);
    }
  },

  observe(subject, topic, data) {
    if (topic == "nsPref:changed" && data === "accessibility.indicator.enabled") {
      this.update(Services.appinfo.accessibilityEnabled);
    } else if (topic === "a11y-init-or-shutdown") {
      // When "a11y-init-or-shutdown" event is fired, "1" indicates that
      // accessibility service is started and "0" that it is shut down.
      this.update(data === "1");
    }
  },

  get enabled() {
    return Services.prefs.getBoolPref("accessibility.indicator.enabled");
  },

  handleEvent({ key, type }) {
    if ((type === "keypress" && [" ", "Enter"].includes(key)) ||
         type === "click") {
      let a11yServicesSupportURL =
        Services.urlFormatter.formatURLPref("accessibility.support.url");
      gBrowser.selectedTab = gBrowser.addTab(a11yServicesSupportURL);
      Services.telemetry.scalarSet("a11y.indicator_acted_on", true);
    }
  },

  uninit() {
    Services.prefs.removeObserver("accessibility.indicator.enabled", this);
    Services.obs.removeObserver(this, "a11y-init-or-shutdown");
    this.update();
  }
};

var gPrivateBrowsingUI = {
  init: function PBUI_init() {
    // Do nothing for normal windows
    if (!PrivateBrowsingUtils.isWindowPrivate(window)) {
      return;
    }

    // Disable the Clear Recent History... menu item when in PB mode
    // temporary fix until bug 463607 is fixed
    document.getElementById("Tools:Sanitize").setAttribute("disabled", "true");

    if (window.location.href == getBrowserURL()) {
      // Adjust the window's title
      let docElement = document.documentElement;
      if (!PrivateBrowsingUtils.permanentPrivateBrowsing) {
        docElement.setAttribute("title",
          docElement.getAttribute("title_privatebrowsing"));
        docElement.setAttribute("titlemodifier",
          docElement.getAttribute("titlemodifier_privatebrowsing"));
      }
      docElement.setAttribute("privatebrowsingmode",
        PrivateBrowsingUtils.permanentPrivateBrowsing ? "permanent" : "temporary");
      gBrowser.updateTitlebar();

      if (PrivateBrowsingUtils.permanentPrivateBrowsing) {
        // Adjust the New Window menu entries
        [
          { normal: "menu_newNavigator", private: "menu_newPrivateWindow" },
        ].forEach(function(menu) {
          let newWindow = document.getElementById(menu.normal);
          let newPrivateWindow = document.getElementById(menu.private);
          if (newWindow && newPrivateWindow) {
            newPrivateWindow.hidden = true;
            newWindow.label = newPrivateWindow.label;
            newWindow.accessKey = newPrivateWindow.accessKey;
            newWindow.command = newPrivateWindow.command;
          }
        });
      }
    }

    let urlBarSearchParam = gURLBar.getAttribute("autocompletesearchparam") || "";
    if (!PrivateBrowsingUtils.permanentPrivateBrowsing &&
        !urlBarSearchParam.includes("disable-private-actions")) {
      // Disable switch to tab autocompletion for private windows.
      // We leave it enabled for permanent private browsing mode though.
      urlBarSearchParam += " disable-private-actions";
    }
    if (!urlBarSearchParam.includes("private-window")) {
      urlBarSearchParam += " private-window";
    }
    gURLBar.setAttribute("autocompletesearchparam", urlBarSearchParam);
  }
};

/**
 * Switch to a tab that has a given URI, and focuses its browser window.
 * If a matching tab is in this window, it will be switched to. Otherwise, other
 * windows will be searched.
 *
 * @param aURI
 *        URI to search for
 * @param aOpenNew
 *        True to open a new tab and switch to it, if no existing tab is found.
 *        If no suitable window is found, a new one will be opened.
 * @param aOpenParams
 *        If switching to this URI results in us opening a tab, aOpenParams
 *        will be the parameter object that gets passed to openUILinkIn. Please
 *        see the documentation for openUILinkIn to see what parameters can be
 *        passed via this object.
 *        This object also allows:
 *        - 'ignoreFragment' property to be set to true to exclude fragment-portion
 *        matching when comparing URIs.
 *          If set to "whenComparing", the fragment will be unmodified.
 *          If set to "whenComparingAndReplace", the fragment will be replaced.
 *        - 'ignoreQueryString' boolean property to be set to true to exclude query string
 *        matching when comparing URIs.
 *        - 'replaceQueryString' boolean property to be set to true to exclude query string
 *        matching when comparing URIs and overwrite the initial query string with
 *        the one from the new URI.
 *        - 'adoptIntoActiveWindow' boolean property to be set to true to adopt the tab
 *        into the current window.
 * @return True if an existing tab was found, false otherwise
 */
function switchToTabHavingURI(aURI, aOpenNew, aOpenParams = {}) {
  // Certain URLs can be switched to irrespective of the source or destination
  // window being in private browsing mode:
  const kPrivateBrowsingWhitelist = new Set([
    "about:addons",
  ]);

  let ignoreFragment = aOpenParams.ignoreFragment;
  let ignoreQueryString = aOpenParams.ignoreQueryString;
  let replaceQueryString = aOpenParams.replaceQueryString;
  let adoptIntoActiveWindow = aOpenParams.adoptIntoActiveWindow;

  // These properties are only used by switchToTabHavingURI and should
  // not be used as a parameter for the new load.
  delete aOpenParams.ignoreFragment;
  delete aOpenParams.ignoreQueryString;
  delete aOpenParams.replaceQueryString;
  delete aOpenParams.adoptIntoActiveWindow;

  let isBrowserWindow = !!window.gBrowser;

  // This will switch to the tab in aWindow having aURI, if present.
  function switchIfURIInWindow(aWindow) {
    // Only switch to the tab if neither the source nor the destination window
    // are private and they are not in permanent private browsing mode
    if (!kPrivateBrowsingWhitelist.has(aURI.spec) &&
        (PrivateBrowsingUtils.isWindowPrivate(window) ||
         PrivateBrowsingUtils.isWindowPrivate(aWindow)) &&
        !PrivateBrowsingUtils.permanentPrivateBrowsing) {
      return false;
    }

    // Remove the query string, fragment, both, or neither from a given url.
    function cleanURL(url, removeQuery, removeFragment) {
      let ret = url;
      if (removeFragment) {
        ret = ret.split("#")[0];
        if (removeQuery) {
          // This removes a query, if present before the fragment.
          ret = ret.split("?")[0];
        }
      } else if (removeQuery) {
        // This is needed in case there is a fragment after the query.
        let fragment = ret.split("#")[1];
        ret = ret.split("?")[0].concat(
          (fragment != undefined) ? "#".concat(fragment) : "");
      }
      return ret;
    }

    // Need to handle nsSimpleURIs here too (e.g. about:...), which don't
    // work correctly with URL objects - so treat them as strings
    let ignoreFragmentWhenComparing = typeof ignoreFragment == "string" &&
                                      ignoreFragment.startsWith("whenComparing");
    let requestedCompare = cleanURL(
          aURI.displaySpec, ignoreQueryString || replaceQueryString, ignoreFragmentWhenComparing);
    let browsers = aWindow.gBrowser.browsers;
    for (let i = 0; i < browsers.length; i++) {
      let browser = browsers[i];
      let browserCompare = cleanURL(
          browser.currentURI.displaySpec, ignoreQueryString || replaceQueryString, ignoreFragmentWhenComparing);
      if (requestedCompare == browserCompare) {
        // If adoptIntoActiveWindow is set, and this is a cross-window switch,
        // adopt the tab into the current window, after the active tab.
        let doAdopt = adoptIntoActiveWindow && isBrowserWindow && aWindow != window;

        if (doAdopt) {
          window.gBrowser.adoptTab(
            aWindow.gBrowser.getTabForBrowser(browser),
            window.gBrowser.tabContainer.selectedIndex + 1,
            /* aSelectTab = */ true
          );
        } else {
          aWindow.focus();
        }

        if (ignoreFragment == "whenComparingAndReplace" || replaceQueryString) {
          browser.loadURI(aURI.spec);
        }

        if (!doAdopt) {
          aWindow.gBrowser.tabContainer.selectedIndex = i;
        }

        return true;
      }
    }
    return false;
  }

  // This can be passed either nsIURI or a string.
  if (!(aURI instanceof Ci.nsIURI))
    aURI = Services.io.newURI(aURI);

  // Prioritise this window.
  if (isBrowserWindow && switchIfURIInWindow(window))
    return true;

  for (let browserWin of browserWindows()) {
    // Skip closed (but not yet destroyed) windows,
    // and the current window (which was checked earlier).
    if (browserWin.closed || browserWin == window)
      continue;
    if (switchIfURIInWindow(browserWin))
      return true;
  }

  // No opened tab has that url.
  if (aOpenNew) {
    if (isBrowserWindow && isTabEmpty(gBrowser.selectedTab))
      openUILinkIn(aURI.spec, "current", aOpenParams);
    else
      openUILinkIn(aURI.spec, "tab", aOpenParams);
  }

  return false;
}

var RestoreLastSessionObserver = {
  init() {
    let browser_tabs_restorebutton_pref = Services.prefs.getIntPref("browser.tabs.restorebutton");
    Services.telemetry.scalarSet("browser.session.restore.browser_tabs_restorebutton", browser_tabs_restorebutton_pref);
    Services.telemetry.scalarSet("browser.session.restore.browser_startup_page", Services.prefs.getIntPref("browser.startup.page"));
    if (SessionStore.canRestoreLastSession &&
        !PrivateBrowsingUtils.isWindowPrivate(window)) {
      if (browser_tabs_restorebutton_pref == 1) {
        let {restoreTabsButton, restoreTabsButtonWrapperWidth} = gBrowser.tabContainer;
        let restoreTabsButtonWrapper = restoreTabsButton.parentNode;
        restoreTabsButtonWrapper.setAttribute("session-exists", "true");
        gBrowser.tabContainer.updateSessionRestoreVisibility();
        restoreTabsButton.style.maxWidth = `${restoreTabsButtonWrapperWidth}px`;
        gBrowser.tabContainer.addEventListener("TabOpen", this);
        Services.telemetry.scalarSet("browser.session.restore.tabbar_restore_available", true);
        restoreTabsButton.addEventListener("click", () => {
          Services.telemetry.scalarSet("browser.session.restore.tabbar_restore_clicked", true);
        });
      }
      Services.obs.addObserver(this, "sessionstore-last-session-cleared", true);
      goSetCommandEnabled("Browser:RestoreLastSession", true);
    } else if (SessionStartup.isAutomaticRestoreEnabled()) {
      document.getElementById("Browser:RestoreLastSession").setAttribute("hidden", true);
    }
  },

  handleEvent(event) {
    switch (event.type) {
     case "TabOpen":
        this.removeRestoreButton();
        break;
    }
  },

  removeRestoreButton() {
    let {restoreTabsButton} = gBrowser.tabContainer;
    let restoreTabsButtonWrapper = restoreTabsButton.parentNode;
    gBrowser.tabContainer.addEventListener("transitionend", function maxWidthTransitionHandler(e) {
      if (e.target == gBrowser.tabContainer && e.propertyName == "max-width") {
        gBrowser.tabContainer.updateSessionRestoreVisibility();
        gBrowser.tabContainer.removeEventListener("transitionend", maxWidthTransitionHandler);
      }
    });
    restoreTabsButtonWrapper.removeAttribute("session-exists");
    restoreTabsButton.style.maxWidth = 0;
    gBrowser.tabContainer.removeEventListener("TabOpen", this);
  },

  observe() {
    // The last session can only be restored once so there's
    // no way we need to re-enable our menu item.
    Services.obs.removeObserver(this, "sessionstore-last-session-cleared");
    goSetCommandEnabled("Browser:RestoreLastSession", false);
    this.removeRestoreButton();
  },

  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                         Ci.nsISupportsWeakReference])
};

function restoreLastSession() {
  SessionStore.restoreLastSession();
}

/* Observes menus and adjusts their size for better
 * usability when opened via a touch screen. */
var MenuTouchModeObserver = {
  init() {
    window.addEventListener("popupshowing", this, true);
  },

  handleEvent(event) {
    let target = event.originalTarget;
    if (event.mozInputSource == MouseEvent.MOZ_SOURCE_TOUCH) {
      target.setAttribute("touchmode", "true");
    } else {
      target.removeAttribute("touchmode");
    }
  },

  uninit() {
    window.removeEventListener("popupshowing", this, true);
  },
};

var TabContextMenu = {
  contextTab: null,
  _updateToggleMuteMenuItem(aTab, aConditionFn) {
    ["muted", "soundplaying"].forEach(attr => {
      if (!aConditionFn || aConditionFn(attr)) {
        if (aTab.hasAttribute(attr)) {
          aTab.toggleMuteMenuItem.setAttribute(attr, "true");
        } else {
          aTab.toggleMuteMenuItem.removeAttribute(attr);
        }
      }
    });
  },
  updateContextMenu: function updateContextMenu(aPopupMenu) {
    this.contextTab = aPopupMenu.triggerNode.localName == "tab" ?
                      aPopupMenu.triggerNode : gBrowser.selectedTab;
    let disabled = gBrowser.tabs.length == 1;

    var menuItems = aPopupMenu.getElementsByAttribute("tbattr", "tabbrowser-multiple");
    for (let menuItem of menuItems)
      menuItem.disabled = disabled;

    if (this.contextTab.hasAttribute("customizemode"))
      document.getElementById("context_openTabInWindow").disabled = true;

    disabled = gBrowser.visibleTabs.length == 1;
    menuItems = aPopupMenu.getElementsByAttribute("tbattr", "tabbrowser-multiple-visible");
    for (let menuItem of menuItems)
      menuItem.disabled = disabled;

    // Session store
    document.getElementById("context_undoCloseTab").disabled =
      SessionStore.getClosedTabCount(window) == 0;

    // Only one of pin/unpin should be visible
    document.getElementById("context_pinTab").hidden = this.contextTab.pinned;
    document.getElementById("context_unpinTab").hidden = !this.contextTab.pinned;

    // Disable "Close Tabs to the Right" if there are no tabs
    // following it.
    document.getElementById("context_closeTabsToTheEnd").disabled =
      gBrowser.getTabsToTheEndFrom(this.contextTab).length == 0;

    // Disable "Close other Tabs" if there are no unpinned tabs.
    let unpinnedTabsToClose = gBrowser.visibleTabs.length - gBrowser._numPinnedTabs;
    if (!this.contextTab.pinned) {
      unpinnedTabsToClose--;
    }
    document.getElementById("context_closeOtherTabs").disabled = unpinnedTabsToClose < 1;

    // Hide "Bookmark All Tabs" for a pinned tab.  Update its state if visible.
    let bookmarkAllTabs = document.getElementById("context_bookmarkAllTabs");
    bookmarkAllTabs.hidden = this.contextTab.pinned;
    if (!bookmarkAllTabs.hidden)
      PlacesCommandHook.updateBookmarkAllTabsCommand();

    // Adjust the state of the toggle mute menu item.
    let toggleMute = document.getElementById("context_toggleMuteTab");
    if (this.contextTab.hasAttribute("activemedia-blocked")) {
      toggleMute.label = gNavigatorBundle.getString("playTab.label");
      toggleMute.accessKey = gNavigatorBundle.getString("playTab.accesskey");
    } else if (this.contextTab.hasAttribute("muted")) {
      toggleMute.label = gNavigatorBundle.getString("unmuteTab.label");
      toggleMute.accessKey = gNavigatorBundle.getString("unmuteTab.accesskey");
    } else {
      toggleMute.label = gNavigatorBundle.getString("muteTab.label");
      toggleMute.accessKey = gNavigatorBundle.getString("muteTab.accesskey");
    }

    this.contextTab.toggleMuteMenuItem = toggleMute;
    this._updateToggleMuteMenuItem(this.contextTab);

    this.contextTab.addEventListener("TabAttrModified", this);
    aPopupMenu.addEventListener("popuphiding", this);

    gSync.updateTabContextMenu(aPopupMenu, this.contextTab);
  },
  handleEvent(aEvent) {
    switch (aEvent.type) {
      case "popuphiding":
        gBrowser.removeEventListener("TabAttrModified", this);
        aEvent.target.removeEventListener("popuphiding", this);
        break;
      case "TabAttrModified":
        let tab = aEvent.target;
        this._updateToggleMuteMenuItem(tab,
          attr => aEvent.detail.changed.includes(attr));
        break;
    }
  }
};

// Prompt user to restart the browser in safe mode
function safeModeRestart() {
  if (Services.appinfo.inSafeMode) {
    let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].
                     createInstance(Ci.nsISupportsPRBool);
    Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");

    if (cancelQuit.data)
      return;

    Services.startup.quit(Ci.nsIAppStartup.eRestart | Ci.nsIAppStartup.eAttemptQuit);
    return;
  }

  Services.obs.notifyObservers(null, "restart-in-safe-mode");
}

/* duplicateTabIn duplicates tab in a place specified by the parameter |where|.
 *
 * |where| can be:
 *  "tab"         new tab
 *  "tabshifted"  same as "tab" but in background if default is to select new
 *                tabs, and vice versa
 *  "window"      new window
 *
 * delta is the offset to the history entry that you want to load.
 */
function duplicateTabIn(aTab, where, delta) {
  switch (where) {
    case "window":
      let otherWin = OpenBrowserWindow();
      let delayedStartupFinished = (subject, topic) => {
        if (topic == "browser-delayed-startup-finished" &&
            subject == otherWin) {
          Services.obs.removeObserver(delayedStartupFinished, topic);
          let otherGBrowser = otherWin.gBrowser;
          let otherTab = otherGBrowser.selectedTab;
          SessionStore.duplicateTab(otherWin, aTab, delta);
          otherGBrowser.removeTab(otherTab, { animate: false });
        }
      };

      Services.obs.addObserver(delayedStartupFinished,
                               "browser-delayed-startup-finished");
      break;
    case "tabshifted":
      SessionStore.duplicateTab(window, aTab, delta);
      // A background tab has been opened, nothing else to do here.
      break;
    case "tab":
      let newTab = SessionStore.duplicateTab(window, aTab, delta);
      gBrowser.selectedTab = newTab;
      break;
  }
}

var MousePosTracker = {
  _listeners: new Set(),
  _x: 0,
  _y: 0,
  get _windowUtils() {
    delete this._windowUtils;
    return this._windowUtils = window.getInterface(Ci.nsIDOMWindowUtils);
  },

  addListener(listener) {
    if (this._listeners.has(listener))
      return;

    listener._hover = false;
    this._listeners.add(listener);

    this._callListener(listener);
  },

  removeListener(listener) {
    this._listeners.delete(listener);
  },

  handleEvent(event) {
    var fullZoom = this._windowUtils.fullZoom;
    this._x = event.screenX / fullZoom - window.mozInnerScreenX;
    this._y = event.screenY / fullZoom - window.mozInnerScreenY;

    this._listeners.forEach(function(listener) {
      try {
        this._callListener(listener);
      } catch (e) {
        Cu.reportError(e);
      }
    }, this);
  },

  _callListener(listener) {
    let rect = listener.getMouseTargetRect();
    let hover = this._x >= rect.left &&
                this._x <= rect.right &&
                this._y >= rect.top &&
                this._y <= rect.bottom;

    if (hover == listener._hover)
      return;

    listener._hover = hover;

    if (hover) {
      if (listener.onMouseEnter)
        listener.onMouseEnter();
    } else if (listener.onMouseLeave) {
      listener.onMouseLeave();
    }
  }
};

var ToolbarIconColor = {
  _windowState: {
    "active": false,
    "fullscreen": false,
    "tabsintitlebar": false
  },
  init() {
    this._initialized = true;

    window.addEventListener("activate", this);
    window.addEventListener("deactivate", this);
    window.addEventListener("toolbarvisibilitychange", this);
    Services.obs.addObserver(this, "lightweight-theme-styling-update");

    // If the window isn't active now, we assume that it has never been active
    // before and will soon become active such that inferFromText will be
    // called from the initial activate event.
    if (Services.focus.activeWindow == window) {
      this.inferFromText("activate");
    }
  },

  uninit() {
    this._initialized = false;

    window.removeEventListener("activate", this);
    window.removeEventListener("deactivate", this);
    window.removeEventListener("toolbarvisibilitychange", this);
    Services.obs.removeObserver(this, "lightweight-theme-styling-update");
  },

  handleEvent(event) {
    switch (event.type) {
      case "activate": // falls through
      case "deactivate":
        this.inferFromText(event.type);
        break;
      case "toolbarvisibilitychange":
        this.inferFromText(event.type, event.visible);
        break;
    }
  },

  observe(aSubject, aTopic, aData) {
    switch (aTopic) {
      case "lightweight-theme-styling-update":
        // inferFromText needs to run after LightweightThemeConsumer.jsm's
        // lightweight-theme-styling-update observer.
        setTimeout(() => {
          this.inferFromText(aTopic);
        }, 0);
        break;
    }
  },

  // a cache of luminance values for each toolbar
  // to avoid unnecessary calls to getComputedStyle
  _toolbarLuminanceCache: new Map(),

  inferFromText(reason, reasonValue) {
    if (!this._initialized)
      return;
    function parseRGB(aColorString) {
      let rgb = aColorString.match(/^rgba?\((\d+), (\d+), (\d+)/);
      rgb.shift();
      return rgb.map(x => parseInt(x));
    }

    switch (reason) {
      case "activate": // falls through
      case "deactivate":
        this._windowState.active = (reason === "activate");
        break;
      case "fullscreen":
        this._windowState.fullscreen = reasonValue;
        break;
      case "lightweight-theme-styling-update":
        // theme change, we'll need to recalculate all color values
        this._toolbarLuminanceCache.clear();
        break;
      case "toolbarvisibilitychange":
        // toolbar changes dont require reset of the cached color values
        break;
      case "tabsintitlebar":
        this._windowState.tabsintitlebar = reasonValue;
        break;
    }

    let toolbarSelector = "#navigator-toolbox > toolbar:not([collapsed=true])";
    if (AppConstants.platform == "macosx")
      toolbarSelector += ":not([type=menubar])";

    // The getComputedStyle calls and setting the brighttext are separated in
    // two loops to avoid flushing layout and making it dirty repeatedly.
    let cachedLuminances = this._toolbarLuminanceCache;
    let luminances = new Map();
    for (let toolbar of document.querySelectorAll(toolbarSelector)) {
      // toolbars *should* all have ids, but guard anyway to avoid blowing up
      let cacheKey = toolbar.id && toolbar.id + JSON.stringify(this._windowState);
      // lookup cached luminance value for this toolbar in this window state
      let luminance = cacheKey && cachedLuminances.get(cacheKey);
      if (isNaN(luminance)) {
        let [r, g, b] = parseRGB(getComputedStyle(toolbar).color);
        luminance = 0.2125 * r + 0.7154 * g + 0.0721 * b;
        if (cacheKey) {
          cachedLuminances.set(cacheKey, luminance);
        }
      }
      luminances.set(toolbar, luminance);
    }

    for (let [toolbar, luminance] of luminances) {
      if (luminance <= 110)
        toolbar.removeAttribute("brighttext");
      else
        toolbar.setAttribute("brighttext", "true");
    }
  }
};

var PanicButtonNotifier = {
  init() {
    this._initialized = true;
    if (window.PanicButtonNotifierShouldNotify) {
      delete window.PanicButtonNotifierShouldNotify;
      this.notify();
    }
  },
  notify() {
    if (!this._initialized) {
      window.PanicButtonNotifierShouldNotify = true;
      return;
    }
    // Display notification panel here...
    try {
      let popup = document.getElementById("panic-button-success-notification");
      popup.hidden = false;
      // To close the popup in 3 seconds after the popup is shown but left uninteracted.
      let onTimeout = () => {
        PanicButtonNotifier.close();
        removeListeners();
      };
      popup.addEventListener("popupshown", function() {
        PanicButtonNotifier.timer = setTimeout(onTimeout, 3000);
      });
      // To prevent the popup from closing when user tries to interact with the
      // popup using mouse or keyboard.
      let onUserInteractsWithPopup = () => {
        clearTimeout(PanicButtonNotifier.timer);
        removeListeners();
       };
      popup.addEventListener("mouseover", onUserInteractsWithPopup);
      window.addEventListener("keydown", onUserInteractsWithPopup);
      let removeListeners = () => {
        popup.removeEventListener("mouseover", onUserInteractsWithPopup);
        window.removeEventListener("keydown", onUserInteractsWithPopup);
        popup.removeEventListener("popuphidden", removeListeners);
      };
      popup.addEventListener("popuphidden", removeListeners);

      let widget = CustomizableUI.getWidget("panic-button").forWindow(window);
      let anchor = widget.anchor;
      anchor = document.getAnonymousElementByAttribute(anchor, "class", "toolbarbutton-icon");
      popup.openPopup(anchor, popup.getAttribute("position"));
    } catch (ex) {
      Cu.reportError(ex);
    }
  },
  close() {
    let popup = document.getElementById("panic-button-success-notification");
    popup.hidePopup();
  },
};

var AboutCapabilitiesListener = {
  _topics: [
    "AboutCapabilities:OpenPrivateWindow",
    "AboutCapabilities:DontShowIntroPanelAgain",
  ],

  init() {
    let mm = window.messageManager;
    for (let topic of this._topics) {
      mm.addMessageListener(topic, this);
    }
  },

  uninit() {
    let mm = window.messageManager;
    for (let topic of this._topics) {
      mm.removeMessageListener(topic, this);
    }
  },

  receiveMessage(aMsg) {
    switch (aMsg.name) {
      case "AboutCapabilities:OpenPrivateWindow":
        OpenBrowserWindow({private: true});
        break;

      case "AboutCapabilities:DontShowIntroPanelAgain":
        TrackingProtection.dontShowIntroPanelAgain();
        break;
    }
  },
};

const SafeBrowsingNotificationBox = {
  _currentURIBaseDomain: null,
  show(title, buttons) {
    let uri = gBrowser.currentURI;

    // start tracking host so that we know when we leave the domain
    this._currentURIBaseDomain = Services.eTLD.getBaseDomain(uri);

    let notificationBox = gBrowser.getNotificationBox();
    let value = "blocked-badware-page";

    let previousNotification = notificationBox.getNotificationWithValue(value);
    if (previousNotification) {
      notificationBox.removeNotification(previousNotification);
    }

    let notification = notificationBox.appendNotification(
      title,
      value,
      "chrome://global/skin/icons/blacklist_favicon.png",
      notificationBox.PRIORITY_CRITICAL_HIGH,
      buttons
    );
    // Persist the notification until the user removes so it
    // doesn't get removed on redirects.
    notification.persistence = -1;
  },
  onLocationChange(aLocationURI) {
    // take this to represent that you haven't visited a bad place
    if (!this._currentURIBaseDomain) {
      return;
    }

    let newURIBaseDomain = Services.eTLD.getBaseDomain(aLocationURI);

    if (newURIBaseDomain !== this._currentURIBaseDomain) {
      let notificationBox = gBrowser.getNotificationBox();
      let notification = notificationBox.getNotificationWithValue("blocked-badware-page");
      if (notification) {
        notificationBox.removeNotification(notification, false);
      }

      this._currentURIBaseDomain = null;
    }
  }
};

function TabModalPromptBox(browser) {
  this._weakBrowserRef = Cu.getWeakReference(browser);
}

TabModalPromptBox.prototype = {
  _promptCloseCallback(onCloseCallback, principalToAllowFocusFor, allowFocusCheckbox, ...args) {
    if (principalToAllowFocusFor && allowFocusCheckbox &&
        allowFocusCheckbox.checked) {
      Services.perms.addFromPrincipal(principalToAllowFocusFor, "focus-tab-by-prompt",
                                      Services.perms.ALLOW_ACTION);
    }
    onCloseCallback.apply(this, args);
  },

  appendPrompt(args, onCloseCallback) {
    const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
    let newPrompt = document.createElementNS(XUL_NS, "tabmodalprompt");
    let browser = this.browser;
    browser.parentNode.insertBefore(newPrompt, browser.nextSibling);
    browser.setAttribute("tabmodalPromptShowing", true);

    newPrompt.clientTop; // style flush to assure binding is attached

    let prompts = this.listPrompts();
    if (prompts.length > 1) {
      // Let's hide ourself behind the current prompt.
      newPrompt.hidden = true;
    }

    let principalToAllowFocusFor = this._allowTabFocusByPromptPrincipal;
    delete this._allowTabFocusByPromptPrincipal;

    let allowFocusCheckbox; // Define outside the if block so we can bind it into the callback.
    let hostForAllowFocusCheckbox = "";
    try {
      hostForAllowFocusCheckbox = principalToAllowFocusFor.URI.host;
    } catch (ex) { /* Ignore exceptions for host-less URIs */ }
    if (hostForAllowFocusCheckbox) {
      let allowFocusRow = document.createElementNS(XUL_NS, "row");
      allowFocusCheckbox = document.createElementNS(XUL_NS, "checkbox");
      let spacer = document.createElementNS(XUL_NS, "spacer");
      allowFocusRow.appendChild(spacer);
      let label = gTabBrowserBundle.formatStringFromName("tabs.allowTabFocusByPromptForSite",
                                                      [hostForAllowFocusCheckbox], 1);
      allowFocusCheckbox.setAttribute("label", label);
      allowFocusRow.appendChild(allowFocusCheckbox);
      newPrompt.appendChild(allowFocusRow);
    }

    let tab = gBrowser.getTabForBrowser(browser);
    let closeCB = this._promptCloseCallback.bind(null, onCloseCallback, principalToAllowFocusFor,
                                                 allowFocusCheckbox);
    newPrompt.init(args, tab, closeCB);
    return newPrompt;
  },

  removePrompt(aPrompt) {
    let browser = this.browser;
    browser.parentNode.removeChild(aPrompt);

    let prompts = this.listPrompts();
    if (prompts.length) {
      let prompt = prompts[prompts.length - 1];
      prompt.hidden = false;
      prompt.Dialog.setDefaultFocus();
    } else {
      browser.removeAttribute("tabmodalPromptShowing");
      browser.focus();
    }
  },

  listPrompts(aPrompt) {
    // Get the nodelist, then return as an array
    const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
    let els = this.browser.parentNode.getElementsByTagNameNS(XUL_NS, "tabmodalprompt");
    return Array.from(els);
  },

  onNextPromptShowAllowFocusCheckboxFor(principal) {
    this._allowTabFocusByPromptPrincipal = principal;
  },

  get browser() {
    let browser = this._weakBrowserRef.get();
    if (!browser) {
      throw "Stale promptbox! The associated browser is gone.";
    }
    return browser;
  },
};
PK
!<z`��440chrome/browser/content/browser/content-UITour.js/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.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 mozilla/frame-script */

ChromeUtils.import("resource://gre/modules/Services.jsm");

const PREF_TEST_WHITELIST = "browser.uitour.testingOrigins";
const UITOUR_PERMISSION   = "uitour";

var UITourListener = {
  handleEvent(event) {
    if (!Services.prefs.getBoolPref("browser.uitour.enabled")) {
      return;
    }
    if (!this.ensureTrustedOrigin()) {
      return;
    }
    addMessageListener("UITour:SendPageCallback", this);
    addMessageListener("UITour:SendPageNotification", this);
    sendAsyncMessage("UITour:onPageEvent", {
      detail: event.detail,
      type: event.type,
      pageVisibilityState: content.document.visibilityState,
    });
  },

  isTestingOrigin(aURI) {
    if (Services.prefs.getPrefType(PREF_TEST_WHITELIST) != Services.prefs.PREF_STRING) {
      return false;
    }

    // Add any testing origins (comma-seperated) to the whitelist for the session.
    for (let origin of Services.prefs.getCharPref(PREF_TEST_WHITELIST).split(",")) {
      try {
        let testingURI = Services.io.newURI(origin);
        if (aURI.prePath == testingURI.prePath) {
          return true;
        }
      } catch (ex) {
        Cu.reportError(ex);
      }
    }
    return false;
  },

  // This function is copied from UITour.jsm.
  isSafeScheme(aURI) {
    let allowedSchemes = new Set(["https", "about"]);
    if (!Services.prefs.getBoolPref("browser.uitour.requireSecure"))
      allowedSchemes.add("http");

    if (!allowedSchemes.has(aURI.scheme))
      return false;

    return true;
  },

  ensureTrustedOrigin() {
    if (content.top != content)
      return false;

    let uri = content.document.documentURIObject;

    if (uri.schemeIs("chrome"))
      return true;

    if (!this.isSafeScheme(uri))
      return false;

    let permission = Services.perms.testPermission(uri, UITOUR_PERMISSION);
    if (permission == Services.perms.ALLOW_ACTION)
      return true;

    return this.isTestingOrigin(uri);
  },

  receiveMessage(aMessage) {
    switch (aMessage.name) {
      case "UITour:SendPageCallback":
        this.sendPageEvent("Response", aMessage.data);
        break;
      case "UITour:SendPageNotification":
        this.sendPageEvent("Notification", aMessage.data);
        break;
      }
  },

  sendPageEvent(type, detail) {
    if (!this.ensureTrustedOrigin()) {
      return;
    }

    let doc = content.document;
    let eventName = "mozUITour" + type;
    let event = new doc.defaultView.CustomEvent(eventName, {
      bubbles: true,
      detail: Cu.cloneInto(detail, doc.defaultView)
    });
    doc.dispatchEvent(event);
  }
};

addEventListener("mozUITour", UITourListener, false, true);
PK
!<��c�{�{6chrome/browser/content/browser/content-sessionStore.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 mozilla/frame-script */

"use strict";

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", this);
ChromeUtils.import("resource://gre/modules/Timer.jsm", this);
ChromeUtils.import("resource://gre/modules/Services.jsm", this);

ChromeUtils.defineModuleGetter(this, "TelemetryStopwatch",
  "resource://gre/modules/TelemetryStopwatch.jsm");

function debug(msg) {
  Services.console.logStringMessage("SessionStoreContent: " + msg);
}

ChromeUtils.defineModuleGetter(this, "FormData",
  "resource://gre/modules/FormData.jsm");

ChromeUtils.defineModuleGetter(this, "DocShellCapabilities",
  "resource:///modules/sessionstore/DocShellCapabilities.jsm");
ChromeUtils.defineModuleGetter(this, "ScrollPosition",
  "resource://gre/modules/ScrollPosition.jsm");
ChromeUtils.defineModuleGetter(this, "SessionHistory",
  "resource://gre/modules/sessionstore/SessionHistory.jsm");
ChromeUtils.defineModuleGetter(this, "SessionStorage",
  "resource:///modules/sessionstore/SessionStorage.jsm");

ChromeUtils.import("resource:///modules/sessionstore/ContentRestore.jsm", this);
XPCOMUtils.defineLazyGetter(this, "gContentRestore",
                            () => { return new ContentRestore(this); });

const ssu = Cc["@mozilla.org/browser/sessionstore/utils;1"]
              .getService(Ci.nsISessionStoreUtils);

// The current epoch.
var gCurrentEpoch = 0;

// A bound to the size of data to store for DOM Storage.
const DOM_STORAGE_LIMIT_PREF = "browser.sessionstore.dom_storage_limit";

// This pref controls whether or not we send updates to the parent on a timeout
// or not, and should only be used for tests or debugging.
const TIMEOUT_DISABLED_PREF = "browser.sessionstore.debug.no_auto_updates";

const PREF_INTERVAL = "browser.sessionstore.interval";

const kNoIndex = Number.MAX_SAFE_INTEGER;
const kLastIndex = Number.MAX_SAFE_INTEGER - 1;

/**
 * A function that will recursively call |cb| to collected data for all
 * non-dynamic frames in the current frame/docShell tree.
 */
function mapFrameTree(callback) {
  return (function map(frame, cb) {
    // Collect data for the current frame.
    let obj = cb(frame) || {};
    let children = [];

    // Recurse into child frames.
    ssu.forEachNonDynamicChildFrame(frame, (subframe, index) => {
      let result = map(subframe, cb);
      if (result && Object.keys(result).length) {
        children[index] = result;
      }
    });

    if (children.length) {
      obj.children = children;
    }

    return Object.keys(obj).length ? obj : null;
  })(content, callback);
}

/**
 * Listens for state change notifcations from webProgress and notifies each
 * registered observer for either the start of a page load, or its completion.
 */
var StateChangeNotifier = {

  init() {
    this._observers = new Set();
    let ifreq = docShell.QueryInterface(Ci.nsIInterfaceRequestor);
    let webProgress = ifreq.getInterface(Ci.nsIWebProgress);
    webProgress.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT);
  },

  /**
   * Adds a given observer |obs| to the set of observers that will be notified
   * when when a new document starts or finishes loading.
   *
   * @param obs (object)
   */
  addObserver(obs) {
    this._observers.add(obs);
  },

  /**
   * Notifies all observers that implement the given |method|.
   *
   * @param method (string)
   */
  notifyObservers(method) {
    for (let obs of this._observers) {
      if (obs.hasOwnProperty(method)) {
        obs[method]();
      }
    }
  },

  /**
   * @see nsIWebProgressListener.onStateChange
   */
  onStateChange(webProgress, request, stateFlags, status) {
    // Ignore state changes for subframes because we're only interested in the
    // top-document starting or stopping its load.
    if (!webProgress.isTopLevel || webProgress.DOMWindow != content) {
      return;
    }

    // onStateChange will be fired when loading the initial about:blank URI for
    // a browser, which we don't actually care about. This is particularly for
    // the case of unrestored background tabs, where the content has not yet
    // been restored: we don't want to accidentally send any updates to the
    // parent when the about:blank placeholder page has loaded.
    if (!docShell.hasLoadedNonBlankURI) {
      return;
    }

    if (stateFlags & Ci.nsIWebProgressListener.STATE_START) {
      this.notifyObservers("onPageLoadStarted");
    } else if (stateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
      this.notifyObservers("onPageLoadCompleted");
    }
  },

  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
                                         Ci.nsISupportsWeakReference])
};

/**
 * Listens for and handles content events that we need for the
 * session store service to be notified of state changes in content.
 */
var EventListener = {

  init() {
    addEventListener("load", ssu.createDynamicFrameEventFilter(this), true);
  },

  handleEvent(event) {
    // Ignore load events from subframes.
    if (event.target != content.document) {
      return;
    }

    if (content.document.documentURI.startsWith("about:reader")) {
      if (event.type == "load" &&
          !content.document.body.classList.contains("loaded")) {
        // Don't restore the scroll position of an about:reader page at this
        // point; listen for the custom event dispatched from AboutReader.jsm.
        content.addEventListener("AboutReaderContentReady", this);
        return;
      }

      content.removeEventListener("AboutReaderContentReady", this);
    }

    // Restore the form data and scroll position. If we're not currently
    // restoring a tab state then this call will simply be a noop.
    gContentRestore.restoreDocument();
  }
};

/**
 * Listens for and handles messages sent by the session store service.
 */
var MessageListener = {

  MESSAGES: [
    "SessionStore:restoreHistory",
    "SessionStore:restoreTabContent",
    "SessionStore:resetRestore",
    "SessionStore:flush",
    "SessionStore:becomeActiveProcess",
  ],

  init() {
    this.MESSAGES.forEach(m => addMessageListener(m, this));
  },

  receiveMessage({name, data}) {
    // The docShell might be gone. Don't process messages,
    // that will just lead to errors anyway.
    if (!docShell) {
      return;
    }

    // A fresh tab always starts with epoch=0. The parent has the ability to
    // override that to signal a new era in this tab's life. This enables it
    // to ignore async messages that were already sent but not yet received
    // and would otherwise confuse the internal tab state.
    if (data.epoch && data.epoch != gCurrentEpoch) {
      gCurrentEpoch = data.epoch;
    }

    switch (name) {
      case "SessionStore:restoreHistory":
        this.restoreHistory(data);
        break;
      case "SessionStore:restoreTabContent":
        if (data.isRemotenessUpdate) {
          let histogram = Services.telemetry.getKeyedHistogramById("FX_TAB_REMOTE_NAVIGATION_DELAY_MS");
          histogram.add("SessionStore:restoreTabContent",
                        Services.telemetry.msSystemNow() - data.requestTime);
        }
        this.restoreTabContent(data);
        break;
      case "SessionStore:resetRestore":
        gContentRestore.resetRestore();
        break;
      case "SessionStore:flush":
        this.flush(data);
        break;
      case "SessionStore:becomeActiveProcess":
        SessionHistoryListener.collect();
        break;
      default:
        debug("received unknown message '" + name + "'");
        break;
    }
  },

  restoreHistory({epoch, tabData, loadArguments, isRemotenessUpdate}) {
    gContentRestore.restoreHistory(tabData, loadArguments, {
      // Note: The callbacks passed here will only be used when a load starts
      // that was not initiated by sessionstore itself. This can happen when
      // some code calls browser.loadURI() or browser.reload() on a pending
      // browser/tab.

      onLoadStarted() {
        // Notify the parent that the tab is no longer pending.
        sendAsyncMessage("SessionStore:restoreTabContentStarted", {epoch});
      },

      onLoadFinished() {
        // Tell SessionStore.jsm that it may want to restore some more tabs,
        // since it restores a max of MAX_CONCURRENT_TAB_RESTORES at a time.
        sendAsyncMessage("SessionStore:restoreTabContentComplete", {epoch});
      }
    });

    if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_DEFAULT) {
      // For non-remote tabs, when restoreHistory finishes, we send a synchronous
      // message to SessionStore.jsm so that it can run SSTabRestoring. Users of
      // SSTabRestoring seem to get confused if chrome and content are out of
      // sync about the state of the restore (particularly regarding
      // docShell.currentURI). Using a synchronous message is the easiest way
      // to temporarily synchronize them.
      //
      // For remote tabs, because all nsIWebProgress notifications are sent
      // asynchronously using messages, we get the same-order guarantees of the
      // message manager, and can use an async message.
      sendSyncMessage("SessionStore:restoreHistoryComplete", {epoch, isRemotenessUpdate});
    } else {
      sendAsyncMessage("SessionStore:restoreHistoryComplete", {epoch, isRemotenessUpdate});
    }
  },

  restoreTabContent({loadArguments, isRemotenessUpdate, reason}) {
    let epoch = gCurrentEpoch;

    // We need to pass the value of didStartLoad back to SessionStore.jsm.
    let didStartLoad = gContentRestore.restoreTabContent(loadArguments, isRemotenessUpdate, () => {
      // Tell SessionStore.jsm that it may want to restore some more tabs,
      // since it restores a max of MAX_CONCURRENT_TAB_RESTORES at a time.
      sendAsyncMessage("SessionStore:restoreTabContentComplete", {epoch, isRemotenessUpdate});
    });

    sendAsyncMessage("SessionStore:restoreTabContentStarted", {
      epoch, isRemotenessUpdate, reason,
    });

    if (!didStartLoad) {
      // Pretend that the load succeeded so that event handlers fire correctly.
      sendAsyncMessage("SessionStore:restoreTabContentComplete", {epoch, isRemotenessUpdate});
    }
  },

  flush({id}) {
    // Flush the message queue, send the latest updates.
    MessageQueue.send({flushID: id});
  }
};

/**
 * Listens for changes to the session history. Whenever the user navigates
 * we will collect URLs and everything belonging to session history.
 *
 * Causes a SessionStore:update message to be sent that contains the current
 * session history.
 *
 * Example:
 *   {entries: [{url: "about:mozilla", ...}, ...], index: 1}
 */
var SessionHistoryListener = {
  init() {
    // The state change observer is needed to handle initial subframe loads.
    // It will redundantly invalidate with the SHistoryListener in some cases
    // but these invalidations are very cheap.
    StateChangeNotifier.addObserver(this);

    // By adding the SHistoryListener immediately, we will unfortunately be
    // notified of every history entry as the tab is restored. We don't bother
    // waiting to add the listener later because these notifications are cheap.
    // We will likely only collect once since we are batching collection on
    // a delay.
    docShell.QueryInterface(Ci.nsIWebNavigation).sessionHistory.
      addSHistoryListener(this);

    // Collect data if we start with a non-empty shistory.
    if (!SessionHistory.isEmpty(docShell)) {
      this.collect();
      // When a tab is detached from the window, for the new window there is a
      // new SessionHistoryListener created. Normally it is empty at this point
      // but in a test env. the initial about:blank might have a children in which
      // case we fire off a history message here with about:blank in it. If we
      // don't do it ASAP then there is going to be a browser swap and the parent
      // will be all confused by that message.
      MessageQueue.send();
    }

    // Listen for page title changes.
    addEventListener("DOMTitleChanged", this);
  },

  uninit() {
    let sessionHistory = docShell.QueryInterface(Ci.nsIWebNavigation).sessionHistory;
    if (sessionHistory) {
      sessionHistory.removeSHistoryListener(this);
    }
  },

  collect() {
    // We want to send down a historychange even for full collects in case our
    // session history is a partial session history, in which case we don't have
    // enough information for a full update. collectFrom(-1) tells the collect
    // function to collect all data avaliable in this process.
    if (docShell) {
      this.collectFrom(-1);
    }
  },

  _fromIdx: kNoIndex,

  // History can grow relatively big with the nested elements, so if we don't have to, we
  // don't want to send the entire history all the time. For a simple optimization
  // we keep track of the smallest index from after any change has occured and we just send
  // the elements from that index. If something more complicated happens we just clear it
  // and send the entire history. We always send the additional info like the current selected
  // index (so for going back and forth between history entries we set the index to kLastIndex
  // if nothing else changed send an empty array and the additonal info like the selected index)
  collectFrom(idx) {
    if (this._fromIdx <= idx) {
      // If we already know that we need to update history fromn index N we can ignore any changes
      // tha happened with an element with index larger than N.
      // Note: initially we use kNoIndex which is MAX_SAFE_INTEGER which means we don't ignore anything
      // here, and in case of navigation in the history back and forth we use kLastIndex which ignores
      // only the subsequent navigations, but not any new elements added.
      return;
    }

    this._fromIdx = idx;
    MessageQueue.push("historychange", () => {
      if (this._fromIdx === kNoIndex) {
        return null;
      }

      let history = SessionHistory.collect(docShell, this._fromIdx);
      this._fromIdx = kNoIndex;
      return history;
    });
  },

  handleEvent(event) {
    this.collect();
  },

  onPageLoadCompleted() {
    this.collect();
  },

  onPageLoadStarted() {
    this.collect();
  },

  OnHistoryNewEntry(newURI, oldIndex) {
    // We ought to collect the previously current entry as well, see bug 1350567.
    this.collectFrom(oldIndex);
  },

  OnHistoryGoBack(backURI) {
    // We ought to collect the previously current entry as well, see bug 1350567.
    this.collectFrom(kLastIndex);
    return true;
  },

  OnHistoryGoForward(forwardURI) {
    // We ought to collect the previously current entry as well, see bug 1350567.
    this.collectFrom(kLastIndex);
    return true;
  },

  OnHistoryGotoIndex(index, gotoURI) {
    // We ought to collect the previously current entry as well, see bug 1350567.
    this.collectFrom(kLastIndex);
    return true;
  },

  OnHistoryPurge(numEntries) {
    this.collect();
    return true;
  },

  OnHistoryReload(reloadURI, reloadFlags) {
    this.collect();
    return true;
  },

  OnHistoryReplaceEntry(index) {
    this.collect();
  },

  OnLengthChanged(aCount) {
    // Ignore, the method is implemented so that XPConnect doesn't throw!
  },

  OnIndexChanged(aIndex) {
    // Ignore, the method is implemented so that XPConnect doesn't throw!
  },

  QueryInterface: XPCOMUtils.generateQI([
    Ci.nsISHistoryListener,
    Ci.nsISupportsWeakReference
  ])
};

/**
 * Listens for scroll position changes. Whenever the user scrolls the top-most
 * frame we update the scroll position and will restore it when requested.
 *
 * Causes a SessionStore:update message to be sent that contains the current
 * scroll positions as a tree of strings. If no frame of the whole frame tree
 * is scrolled this will return null so that we don't tack a property onto
 * the tabData object in the parent process.
 *
 * Example:
 *   {scroll: "100,100", children: [null, null, {scroll: "200,200"}]}
 */
var ScrollPositionListener = {
  init() {
    addEventListener("scroll", ssu.createDynamicFrameEventFilter(this));
    StateChangeNotifier.addObserver(this);
  },

  handleEvent() {
    MessageQueue.push("scroll", () => this.collect());
  },

  onPageLoadCompleted() {
    MessageQueue.push("scroll", () => this.collect());
  },

  onPageLoadStarted() {
    MessageQueue.push("scroll", () => null);
  },

  collect() {
    return mapFrameTree(ScrollPosition.collect);
  }
};

/**
 * Listens for changes to input elements. Whenever the value of an input
 * element changes we will re-collect data for the current frame tree and send
 * a message to the parent process.
 *
 * Causes a SessionStore:update message to be sent that contains the form data
 * for all reachable frames.
 *
 * Example:
 *   {
 *     formdata: {url: "http://mozilla.org/", id: {input_id: "input value"}},
 *     children: [
 *       null,
 *       {url: "http://sub.mozilla.org/", id: {input_id: "input value 2"}}
 *     ]
 *   }
 */
var FormDataListener = {
  init() {
    addEventListener("input", ssu.createDynamicFrameEventFilter(this), true);
    StateChangeNotifier.addObserver(this);
  },

  handleEvent() {
    MessageQueue.push("formdata", () => this.collect());
  },

  onPageLoadStarted() {
    MessageQueue.push("formdata", () => null);
  },

  collect() {
    return mapFrameTree(FormData.collect);
  }
};

/**
 * Listens for changes to docShell capabilities. Whenever a new load is started
 * we need to re-check the list of capabilities and send message when it has
 * changed.
 *
 * Causes a SessionStore:update message to be sent that contains the currently
 * disabled docShell capabilities (all nsIDocShell.allow* properties set to
 * false) as a string - i.e. capability names separate by commas.
 */
var DocShellCapabilitiesListener = {
  /**
   * This field is used to compare the last docShell capabilities to the ones
   * that have just been collected. If nothing changed we won't send a message.
   */
  _latestCapabilities: "",

  init() {
    StateChangeNotifier.addObserver(this);
  },

  onPageLoadStarted() {
    // The order of docShell capabilities cannot change while we're running
    // so calling join() without sorting before is totally sufficient.
    let caps = DocShellCapabilities.collect(docShell).join(",");

    // Send new data only when the capability list changes.
    if (caps != this._latestCapabilities) {
      this._latestCapabilities = caps;
      MessageQueue.push("disallow", () => caps || null);
    }
  }
};

/**
 * Listens for changes to the DOMSessionStorage. Whenever new keys are added,
 * existing ones removed or changed, or the storage is cleared we will send a
 * message to the parent process containing up-to-date sessionStorage data.
 *
 * Causes a SessionStore:update message to be sent that contains the current
 * DOMSessionStorage contents. The data is a nested object using host names
 * as keys and per-host DOMSessionStorage data as values.
 */
var SessionStorageListener = {
  init() {
    Services.obs.addObserver(this, "browser:purge-domain-data");
    StateChangeNotifier.addObserver(this);
    this.resetEventListener();
  },

  uninit() {
    Services.obs.removeObserver(this, "browser:purge-domain-data");
  },

  observe() {
    // Collect data on the next tick so that any other observer
    // that needs to purge data can do its work first.
    setTimeoutWithTarget(() => this.collect(), 0, tabEventTarget);
  },

  // We don't want to send all the session storage data for all the frames
  // for every change. So if only a few value changed we send them over as
  // a "storagechange" event. If however for some reason before we send these
  // changes we have to send over the entire sessions storage data, we just
  // reset these changes.
  _changes: undefined,

  resetChanges() {
    this._changes = undefined;
  },

  // The event listener waiting for MozSessionStorageChanged events.
  _listener: null,

  resetEventListener() {
    if (!this._listener) {
      this._listener = ssu.createDynamicFrameEventFilter(this);
      addEventListener("MozSessionStorageChanged", this._listener, true);
    }
  },

  removeEventListener() {
    removeEventListener("MozSessionStorageChanged", this._listener, true);
    this._listener = null;
  },

  handleEvent(event) {
    if (!docShell) {
      return;
    }

    // How much data does DOMSessionStorage contain?
    let usage = content.QueryInterface(Ci.nsIInterfaceRequestor)
                       .getInterface(Ci.nsIDOMWindowUtils)
                       .getStorageUsage(event.storageArea);

    // Don't store any data if we exceed the limit. Wipe any data we previously
    // collected so that we don't confuse websites with partial state.
    if (usage > Services.prefs.getIntPref(DOM_STORAGE_LIMIT_PREF)) {
      MessageQueue.push("storage", () => null);
      this.removeEventListener();
      this.resetChanges();
      return;
    }

    let {url, key, newValue} = event;
    let uri = Services.io.newURI(url);
    let domain = uri.prePath;
    if (!this._changes) {
      this._changes = {};
    }
    if (!this._changes[domain]) {
      this._changes[domain] = {};
    }
    this._changes[domain][key] = newValue;

    MessageQueue.push("storagechange", () => {
      let tmp = this._changes;
      // If there were multiple changes we send them merged.
      // First one will collect all the changes the rest of
      // these messages will be ignored.
      this.resetChanges();
      return tmp;
    });
  },

  collect() {
    if (!docShell) {
      return;
    }

    // We need the entire session storage, let's reset the pending individual change
    // messages.
    this.resetChanges();

    MessageQueue.push("storage", () => SessionStorage.collect(content));
  },

  onPageLoadCompleted() {
    this.collect();
  },

  onPageLoadStarted() {
    this.resetEventListener();
    this.collect();
  }
};

/**
 * Listen for changes to the privacy status of the tab.
 * By definition, tabs start in non-private mode.
 *
 * Causes a SessionStore:update message to be sent for
 * field "isPrivate". This message contains
 *  |true| if the tab is now private
 *  |null| if the tab is now public - the field is therefore
 *  not saved.
 */
var PrivacyListener = {
  init() {
    docShell.addWeakPrivacyTransitionObserver(this);

    // Check that value at startup as it might have
    // been set before the frame script was loaded.
    if (docShell.QueryInterface(Ci.nsILoadContext).usePrivateBrowsing) {
      MessageQueue.push("isPrivate", () => true);
    }
  },

  // Ci.nsIPrivacyTransitionObserver
  privateModeChanged(enabled) {
    MessageQueue.push("isPrivate", () => enabled || null);
  },

  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPrivacyTransitionObserver,
                                         Ci.nsISupportsWeakReference])
};

/**
 * A message queue that takes collected data and will take care of sending it
 * to the chrome process. It allows flushing using synchronous messages and
 * takes care of any race conditions that might occur because of that. Changes
 * will be batched if they're pushed in quick succession to avoid a message
 * flood.
 */
var MessageQueue = {
  /**
   * A map (string -> lazy fn) holding lazy closures of all queued data
   * collection routines. These functions will return data collected from the
   * docShell.
   */
  _data: new Map(),

  /**
   * The delay (in ms) used to delay sending changes after data has been
   * invalidated.
   */
  BATCH_DELAY_MS: 1000,

  /**
   * The minimum idle period (in ms) we need for sending data to chrome process.
   */
  NEEDED_IDLE_PERIOD_MS: 5,

  /**
   * Timeout for waiting an idle period to send data. We will set this from
   * the pref "browser.sessionstore.interval".
   */
  _timeoutWaitIdlePeriodMs: null,

  /**
   * The current timeout ID, null if there is no queue data. We use timeouts
   * to damp a flood of data changes and send lots of changes as one batch.
   */
  _timeout: null,

  /**
   * Whether or not sending batched messages on a timer is disabled. This should
   * only be used for debugging or testing. If you need to access this value,
   * you should probably use the timeoutDisabled getter.
   */
  _timeoutDisabled: false,

  /**
   * The idle callback ID referencing an active idle callback. When no idle
   * callback is pending, this is null.
   * */
  _idleCallbackID: null,

  /**
   * True if batched messages are not being fired on a timer. This should only
   * ever be true when debugging or during tests.
   */
  get timeoutDisabled() {
    return this._timeoutDisabled;
  },

  /**
   * Disables sending batched messages on a timer. Also cancels any pending
   * timers.
   */
  set timeoutDisabled(val) {
    this._timeoutDisabled = val;

    if (val && this._timeout) {
      clearTimeout(this._timeout);
      this._timeout = null;
    }

    return val;
  },

  init() {
    this.timeoutDisabled =
      Services.prefs.getBoolPref(TIMEOUT_DISABLED_PREF);
    this._timeoutWaitIdlePeriodMs =
      Services.prefs.getIntPref(PREF_INTERVAL);

    Services.prefs.addObserver(TIMEOUT_DISABLED_PREF, this);
    Services.prefs.addObserver(PREF_INTERVAL, this);
  },

  uninit() {
    Services.prefs.removeObserver(TIMEOUT_DISABLED_PREF, this);
    Services.prefs.removeObserver(PREF_INTERVAL, this);
    this.cleanupTimers();
  },

  /**
   * Cleanup pending idle callback and timer.
   */
  cleanupTimers() {
    if (this._idleCallbackID) {
      content.cancelIdleCallback(this._idleCallbackID);
      this._idleCallbackID = null;
    }
    if (this._timeout) {
      clearTimeout(this._timeout);
      this._timeout = null;
    }
  },

  observe(subject, topic, data) {
    if (topic == "nsPref:changed") {
      switch (data) {
        case TIMEOUT_DISABLED_PREF:
          this.timeoutDisabled =
            Services.prefs.getBoolPref(TIMEOUT_DISABLED_PREF);
          break;
        case PREF_INTERVAL:
          this._timeoutWaitIdlePeriodMs =
            Services.prefs.getIntPref(PREF_INTERVAL);
          break;
        default:
          debug("received unknown message '" + data + "'");
          break;
      }
    }
  },

  /**
   * Pushes a given |value| onto the queue. The given |key| represents the type
   * of data that is stored and can override data that has been queued before
   * but has not been sent to the parent process, yet.
   *
   * @param key (string)
   *        A unique identifier specific to the type of data this is passed.
   * @param fn (function)
   *        A function that returns the value that will be sent to the parent
   *        process.
   */
  push(key, fn) {
    this._data.set(key, fn);

    if (!this._timeout && !this._timeoutDisabled) {
      // Wait a little before sending the message to batch multiple changes.
      this._timeout = setTimeoutWithTarget(
        () => this.sendWhenIdle(), this.BATCH_DELAY_MS, tabEventTarget);
    }
  },

  /**
   * Sends queued data when the remaining idle time is enough or waiting too
   * long; otherwise, request an idle time again. If the |deadline| is not
   * given, this function is going to schedule the first request.
   *
   * @param deadline (object)
   *        An IdleDeadline object passed by requestIdleCallback().
   */
  sendWhenIdle(deadline) {
    if (!content) {
      // The frameloader is being torn down. Nothing more to do.
      return;
    }

    if (deadline) {
      if (deadline.didTimeout || deadline.timeRemaining() > MessageQueue.NEEDED_IDLE_PERIOD_MS) {
        MessageQueue.send();
        return;
      }
    } else if (MessageQueue._idleCallbackID) {
      // Bail out if there's a pending run.
      return;
    }
    MessageQueue._idleCallbackID =
      content.requestIdleCallback(MessageQueue.sendWhenIdle, {timeout: MessageQueue._timeoutWaitIdlePeriodMs});
   },

  /**
   * Sends queued data to the chrome process.
   *
   * @param options (object)
   *        {flushID: 123} to specify that this is a flush
   *        {isFinal: true} to signal this is the final message sent on unload
   */
  send(options = {}) {
    // Looks like we have been called off a timeout after the tab has been
    // closed. The docShell is gone now and we can just return here as there
    // is nothing to do.
    if (!docShell) {
      return;
    }

    this.cleanupTimers();

    let flushID = (options && options.flushID) || 0;
    let histID = "FX_SESSION_RESTORE_CONTENT_COLLECT_DATA_MS";

    let data = {};
    for (let [key, func] of this._data) {
      if (key != "isPrivate") {
        TelemetryStopwatch.startKeyed(histID, key);
      }

      let value = func();

      if (key != "isPrivate") {
        TelemetryStopwatch.finishKeyed(histID, key);
      }

      if (value || (key != "storagechange" && key != "historychange")) {
        data[key] = value;
      }
    }

    this._data.clear();

    try {
      // Send all data to the parent process.
      sendAsyncMessage("SessionStore:update", {
        data, flushID,
        isFinal: options.isFinal || false,
        epoch: gCurrentEpoch
      });
    } catch (ex) {
      if (ex && ex.result == Cr.NS_ERROR_OUT_OF_MEMORY) {
        Services.telemetry.getHistogramById("FX_SESSION_RESTORE_SEND_UPDATE_CAUSED_OOM").add(1);
        sendAsyncMessage("SessionStore:error");
      }
    }
  },
};

StateChangeNotifier.init();
EventListener.init();
MessageListener.init();
FormDataListener.init();
SessionHistoryListener.init();
SessionStorageListener.init();
ScrollPositionListener.init();
DocShellCapabilitiesListener.init();
PrivacyListener.init();
MessageQueue.init();

function handleRevivedTab() {
  if (!content) {
    removeEventListener("pagehide", handleRevivedTab);
    return;
  }

  if (content.document.documentURI.startsWith("about:tabcrashed")) {
    if (Services.appinfo.processType != Services.appinfo.PROCESS_TYPE_DEFAULT) {
      // Sanity check - we'd better be loading this in a non-remote browser.
      throw new Error("We seem to be navigating away from about:tabcrashed in " +
                      "a non-remote browser. This should really never happen.");
    }

    removeEventListener("pagehide", handleRevivedTab);

    // Notify the parent.
    sendAsyncMessage("SessionStore:crashedTabRevived");
  }
}

// If we're browsing from the tab crashed UI to a blacklisted URI that keeps
// this browser non-remote, we'll handle that in a pagehide event.
addEventListener("pagehide", handleRevivedTab);

addEventListener("unload", () => {
  // Upon frameLoader destruction, send a final update message to
  // the parent and flush all data currently held in the child.
  MessageQueue.send({isFinal: true});

  // If we're browsing from the tab crashed UI to a URI that causes the tab
  // to go remote again, we catch this in the unload event handler, because
  // swapping out the non-remote browser for a remote one in
  // tabbrowser.xml's updateBrowserRemoteness doesn't cause the pagehide
  // event to be fired.
  handleRevivedTab();

  // Remove all registered nsIObservers.
  SessionStorageListener.uninit();
  SessionHistoryListener.uninit();
  MessageQueue.uninit();

  // Remove progress listeners.
  gContentRestore.resetRestore();

  // We don't need to take care of any StateChangeNotifier observers as they
  // will die with the content script. The same goes for the privacy transition
  // observer that will die with the docShell when the tab is closed.
});
PK
!<P������)chrome/browser/content/browser/content.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* This content script should work in any browser or iframe and should not
 * depend on the frame being contained in tabbrowser. */

/* eslint-env mozilla/frame-script */

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");

XPCOMUtils.defineLazyModuleGetters(this, {
  BrowserUtils: "resource://gre/modules/BrowserUtils.jsm",
  ContentLinkHandler: "resource:///modules/ContentLinkHandler.jsm",
  ContentMetaHandler: "resource:///modules/ContentMetaHandler.jsm",
  ContentWebRTC: "resource:///modules/ContentWebRTC.jsm",
  InlineSpellCheckerContent: "resource://gre/modules/InlineSpellCheckerContent.jsm",
  LoginManagerContent: "resource://gre/modules/LoginManagerContent.jsm",
  LoginFormFactory: "resource://gre/modules/LoginManagerContent.jsm",
  InsecurePasswordUtils: "resource://gre/modules/InsecurePasswordUtils.jsm",
  PluginContent: "resource:///modules/PluginContent.jsm",
  PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
  FormSubmitObserver: "resource:///modules/FormSubmitObserver.jsm",
  PageMetadata: "resource://gre/modules/PageMetadata.jsm",
  PlacesUIUtils: "resource:///modules/PlacesUIUtils.jsm",
  SafeBrowsing: "resource://gre/modules/SafeBrowsing.jsm",
  Utils: "resource://gre/modules/sessionstore/Utils.jsm",
  WebNavigationFrames: "resource://gre/modules/WebNavigationFrames.jsm",
  Feeds: "resource:///modules/Feeds.jsm",
  ContextMenu: "resource:///modules/ContextMenu.jsm",
});

// TabChildGlobal
var global = this;

var contextMenu = this.contextMenu = new ContextMenu(global);

// Load the form validation popup handler
var formSubmitObserver = new FormSubmitObserver(content, this);

addMessageListener("RemoteLogins:fillForm", function(message) {
  // intercept if ContextMenu.jsm had sent a plain object for remote targets
  message.objects.inputElement = contextMenu.getTarget(message, "inputElement");
  LoginManagerContent.receiveMessage(message, content);
});
addEventListener("DOMFormHasPassword", function(event) {
  LoginManagerContent.onDOMFormHasPassword(event, content);
  let formLike = LoginFormFactory.createFromForm(event.target);
  InsecurePasswordUtils.reportInsecurePasswords(formLike);
});
addEventListener("DOMInputPasswordAdded", function(event) {
  LoginManagerContent.onDOMInputPasswordAdded(event, content);
  let formLike = LoginFormFactory.createFromField(event.target);
  InsecurePasswordUtils.reportInsecurePasswords(formLike);
});
addEventListener("pageshow", function(event) {
  LoginManagerContent.onPageShow(event, content);
});
addEventListener("DOMAutoComplete", function(event) {
  LoginManagerContent.onUsernameInput(event);
});
addEventListener("blur", function(event) {
  LoginManagerContent.onUsernameInput(event);
});

const SEC_ERROR_BASE          = Ci.nsINSSErrorsService.NSS_SEC_ERROR_BASE;
const MOZILLA_PKIX_ERROR_BASE = Ci.nsINSSErrorsService.MOZILLA_PKIX_ERROR_BASE;

const SEC_ERROR_EXPIRED_CERTIFICATE                = SEC_ERROR_BASE + 11;
const SEC_ERROR_UNKNOWN_ISSUER                     = SEC_ERROR_BASE + 13;
const SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE         = SEC_ERROR_BASE + 30;
const SEC_ERROR_OCSP_FUTURE_RESPONSE               = SEC_ERROR_BASE + 131;
const SEC_ERROR_OCSP_OLD_RESPONSE                  = SEC_ERROR_BASE + 132;
const MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE = MOZILLA_PKIX_ERROR_BASE + 5;
const MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE = MOZILLA_PKIX_ERROR_BASE + 6;

const PREF_BLOCKLIST_CLOCK_SKEW_SECONDS = "services.blocklist.clock_skew_seconds";
const PREF_BLOCKLIST_LAST_FETCHED = "services.blocklist.last_update_seconds";

const PREF_SSL_IMPACT_ROOTS = ["security.tls.version.", "security.ssl3."];


function getSerializedSecurityInfo(docShell) {
  let serhelper = Cc["@mozilla.org/network/serialization-helper;1"]
                    .getService(Ci.nsISerializationHelper);

  let securityInfo = docShell.failedChannel && docShell.failedChannel.securityInfo;
  if (!securityInfo) {
    return "";
  }
  securityInfo.QueryInterface(Ci.nsITransportSecurityInfo)
              .QueryInterface(Ci.nsISerializable);

  return serhelper.serializeToString(securityInfo);
}

function getSiteBlockedErrorDetails(docShell) {
  let blockedInfo = {};
  if (docShell.failedChannel) {
    let classifiedChannel = docShell.failedChannel.
                            QueryInterface(Ci.nsIClassifiedChannel);
    if (classifiedChannel) {
      let httpChannel = docShell.failedChannel.QueryInterface(Ci.nsIHttpChannel);

      let reportUri = httpChannel.URI.clone();

      // Remove the query to avoid leaking sensitive data
      if (reportUri instanceof Ci.nsIURL) {
        reportUri = reportUri.mutate()
                             .setQuery("")
                             .finalize();
      }

      blockedInfo = { list: classifiedChannel.matchedList,
                      provider: classifiedChannel.matchedProvider,
                      uri: reportUri.asciiSpec };
    }
  }
  return blockedInfo;
}

var AboutBlockedSiteListener = {
  init(chromeGlobal) {
    addMessageListener("DeceptiveBlockedDetails", this);
    chromeGlobal.addEventListener("AboutBlockedLoaded", this, false, true);
  },

  get isBlockedSite() {
    return content.document.documentURI.startsWith("about:blocked");
  },

  receiveMessage(msg) {
    if (!this.isBlockedSite) {
      return;
    }

    if (msg.name == "DeceptiveBlockedDetails") {
      sendAsyncMessage("DeceptiveBlockedDetails:Result", {
        blockedInfo: getSiteBlockedErrorDetails(docShell),
      });
    }
  },

  handleEvent(aEvent) {
    if (!this.isBlockedSite) {
      return;
    }

    if (aEvent.type != "AboutBlockedLoaded") {
      return;
    }

    let blockedInfo = getSiteBlockedErrorDetails(docShell);
    let provider = blockedInfo.provider || "";

    let doc = content.document;

    /**
    * Set error description link in error details.
    * For example, the "reported as a deceptive site" link for
    * blocked phishing pages.
    */
    let desc = Services.prefs.getCharPref(
      "browser.safebrowsing.provider." + provider + ".reportURL", "");
    if (desc) {
      doc.getElementById("error_desc_link").setAttribute("href", desc + aEvent.detail.url);
    }

    // Set other links in error details.
    switch (aEvent.detail.err) {
      case "malware":
        doc.getElementById("report_detection").setAttribute("href",
          (SafeBrowsing.getReportURL("MalwareMistake", blockedInfo) ||
           "https://www.stopbadware.org/firefox"));
        doc.getElementById("learn_more_link").setAttribute("href",
          "https://www.stopbadware.org/firefox");
        break;
      case "unwanted":
        doc.getElementById("learn_more_link").setAttribute("href",
          "https://www.google.com/about/unwanted-software-policy.html");
        break;
      case "phishing":
        doc.getElementById("report_detection").setAttribute("href",
          (SafeBrowsing.getReportURL("PhishMistake", blockedInfo) ||
           "https://safebrowsing.google.com/safebrowsing/report_error/?tpl=mozilla"));
        doc.getElementById("learn_more_link").setAttribute("href",
          "https://www.antiphishing.org//");
        break;
    }

    // Set the firefox support url.
    doc.getElementById("firefox_support").setAttribute("href",
      Services.urlFormatter.formatURLPref("app.support.baseURL") + "phishing-malware");

    // Show safe browsing details on load if the pref is set to true.
    let showDetails = Services.prefs.getBoolPref("browser.xul.error_pages.show_safe_browsing_details_on_load");
    if (showDetails) {
      let details = content.document.getElementById("errorDescriptionContainer");
      details.removeAttribute("hidden");
    }

    // Set safe browsing advisory link.
    let advisoryUrl = Services.prefs.getCharPref(
      "browser.safebrowsing.provider." + provider + ".advisoryURL", "");
    if (!advisoryUrl) {
      let el = content.document.getElementById("advisoryDesc");
      el.remove();
      return;
    }

    let advisoryLinkText = Services.prefs.getCharPref(
      "browser.safebrowsing.provider." + provider + ".advisoryName", "");
    if (!advisoryLinkText) {
      let el = content.document.getElementById("advisoryDesc");
      el.remove();
      return;
    }

    let anchorEl = content.document.getElementById("advisory_provider");
    anchorEl.setAttribute("href", advisoryUrl);
    anchorEl.textContent = advisoryLinkText;
  },
};

var AboutNetAndCertErrorListener = {
  init(chromeGlobal) {
    addMessageListener("CertErrorDetails", this);
    addMessageListener("Browser:CaptivePortalFreed", this);
    chromeGlobal.addEventListener("AboutNetErrorLoad", this, false, true);
    chromeGlobal.addEventListener("AboutNetErrorOpenCaptivePortal", this, false, true);
    chromeGlobal.addEventListener("AboutNetErrorSetAutomatic", this, false, true);
    chromeGlobal.addEventListener("AboutNetErrorResetPreferences", this, false, true);
  },

  get isAboutNetError() {
    return content.document.documentURI.startsWith("about:neterror");
  },

  get isAboutCertError() {
    return content.document.documentURI.startsWith("about:certerror");
  },

  receiveMessage(msg) {
    if (!this.isAboutCertError) {
      return;
    }

    switch (msg.name) {
      case "CertErrorDetails":
        this.onCertErrorDetails(msg);
        break;
      case "Browser:CaptivePortalFreed":
        this.onCaptivePortalFreed(msg);
        break;
    }
  },

  _getCertValidityRange() {
    let {securityInfo} = docShell.failedChannel;
    securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
    let certs = securityInfo.failedCertChain.getEnumerator();
    let notBefore = 0;
    let notAfter = Number.MAX_SAFE_INTEGER;
    while (certs.hasMoreElements()) {
      let cert = certs.getNext();
      cert.QueryInterface(Ci.nsIX509Cert);
      notBefore = Math.max(notBefore, cert.validity.notBefore);
      notAfter = Math.min(notAfter, cert.validity.notAfter);
    }
    // nsIX509Cert reports in PR_Date terms, which uses microseconds. Convert:
    notBefore /= 1000;
    notAfter /= 1000;
    return {notBefore, notAfter};
  },

  onCertErrorDetails(msg) {
    let div = content.document.getElementById("certificateErrorText");
    div.textContent = msg.data.info;
    let learnMoreLink = content.document.getElementById("learnMoreLink");
    let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");

    switch (msg.data.code) {
      case SEC_ERROR_UNKNOWN_ISSUER:
        learnMoreLink.href = baseURL + "security-error";
        break;

      // In case the certificate expired we make sure the system clock
      // matches the blocklist ping (Kinto) time and is not before the build date.
      case SEC_ERROR_EXPIRED_CERTIFICATE:
      case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
      case SEC_ERROR_OCSP_FUTURE_RESPONSE:
      case SEC_ERROR_OCSP_OLD_RESPONSE:
      case MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE:
      case MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE:

        // We check against Kinto time first if available, because that allows us
        // to give the user an approximation of what the correct time is.
        let difference = Services.prefs.getIntPref(PREF_BLOCKLIST_CLOCK_SKEW_SECONDS, 0);
        let lastFetched = Services.prefs.getIntPref(PREF_BLOCKLIST_LAST_FETCHED, 0) * 1000;

        let now = Date.now();
        let certRange = this._getCertValidityRange();

        let approximateDate = now - difference * 1000;
        // If the difference is more than a day, we last fetched the date in the last 5 days,
        // and adjusting the date per the interval would make the cert valid, warn the user:
        if (Math.abs(difference) > 60 * 60 * 24 && (now - lastFetched) <= 60 * 60 * 24 * 5 &&
            certRange.notBefore < approximateDate && certRange.notAfter > approximateDate) {
          let formatter = new Services.intl.DateTimeFormat(undefined, {
            dateStyle: "short"
          });
          let systemDate = formatter.format(new Date());
          // negative difference means local time is behind server time
          approximateDate = formatter.format(new Date(approximateDate));

          content.document.getElementById("wrongSystemTime_URL")
            .textContent = content.document.location.hostname;
          content.document.getElementById("wrongSystemTime_systemDate")
            .textContent = systemDate;
          content.document.getElementById("wrongSystemTime_actualDate")
            .textContent = approximateDate;

          content.document.getElementById("errorShortDesc")
            .style.display = "none";
          content.document.getElementById("wrongSystemTimePanel")
            .style.display = "block";

        // If there is no clock skew with Kinto servers, check against the build date.
        // (The Kinto ping could have happened when the time was still right, or not at all)
        } else {
          let appBuildID = Services.appinfo.appBuildID;

          let year = parseInt(appBuildID.substr(0, 4), 10);
          let month = parseInt(appBuildID.substr(4, 2), 10) - 1;
          let day = parseInt(appBuildID.substr(6, 2), 10);

          let buildDate = new Date(year, month, day);
          let systemDate = new Date();

          // We don't check the notBefore of the cert with the build date,
          // as it is of course almost certain that it is now later than the build date,
          // so we shouldn't exclude the possibility that the cert has become valid
          // since the build date.
          if (buildDate > systemDate && new Date(certRange.notAfter) > buildDate) {
            let formatter = new Services.intl.DateTimeFormat(undefined, {
              dateStyle: "short"
            });

            content.document.getElementById("wrongSystemTimeWithoutReference_URL")
              .textContent = content.document.location.hostname;
            content.document.getElementById("wrongSystemTimeWithoutReference_systemDate")
              .textContent = formatter.format(systemDate);

            content.document.getElementById("errorShortDesc")
              .style.display = "none";
            content.document.getElementById("wrongSystemTimeWithoutReferencePanel")
              .style.display = "block";
          }
        }
        learnMoreLink.href = baseURL + "time-errors";
        break;
    }
  },

  onCaptivePortalFreed(msg) {
    content.dispatchEvent(new content.CustomEvent("AboutNetErrorCaptivePortalFreed"));
  },

  handleEvent(aEvent) {
    if (!this.isAboutNetError && !this.isAboutCertError) {
      return;
    }

    switch (aEvent.type) {
    case "AboutNetErrorLoad":
      this.onPageLoad(aEvent);
      break;
    case "AboutNetErrorOpenCaptivePortal":
      this.openCaptivePortalPage(aEvent);
      break;
    case "AboutNetErrorSetAutomatic":
      this.onSetAutomatic(aEvent);
      break;
    case "AboutNetErrorResetPreferences":
      this.onResetPreferences(aEvent);
      break;
    }
  },

  changedCertPrefs() {
    let prefSSLImpact = PREF_SSL_IMPACT_ROOTS.reduce((prefs, root) => {
       return prefs.concat(Services.prefs.getChildList(root));
    }, []);
    for (let prefName of prefSSLImpact) {
      if (Services.prefs.prefHasUserValue(prefName)) {
        return true;
      }
    }

    return false;
  },

  onPageLoad(evt) {
    // Values for telemtery bins: see TLS_ERROR_REPORT_UI in Histograms.json
    const TLS_ERROR_REPORT_TELEMETRY_UI_SHOWN = 0;

    let hideAddExceptionButton = false;

    if (this.isAboutCertError) {
      let originalTarget = evt.originalTarget;
      let ownerDoc = originalTarget.ownerDocument;
      ClickEventHandler.onCertError(originalTarget, ownerDoc);
      hideAddExceptionButton =
        Services.prefs.getBoolPref("security.certerror.hideAddException", false);
    }

    let automatic = Services.prefs.getBoolPref("security.ssl.errorReporting.automatic");
    content.dispatchEvent(new content.CustomEvent("AboutNetErrorOptions", {
      detail: JSON.stringify({
        enabled: Services.prefs.getBoolPref("security.ssl.errorReporting.enabled"),
        changedCertPrefs: this.changedCertPrefs(),
        automatic,
        hideAddExceptionButton,
      })
    }));

    sendAsyncMessage("Browser:SSLErrorReportTelemetry",
                     {reportStatus: TLS_ERROR_REPORT_TELEMETRY_UI_SHOWN});
  },

  openCaptivePortalPage(evt) {
    sendAsyncMessage("Browser:OpenCaptivePortalPage");
  },


  onResetPreferences(evt) {
    sendAsyncMessage("Browser:ResetSSLPreferences");
  },

  onSetAutomatic(evt) {
    sendAsyncMessage("Browser:SetSSLErrorReportAuto", {
      automatic: evt.detail
    });

    // If we're enabling reports, send a report for this failure.
    if (evt.detail) {
      let win = evt.originalTarget.ownerGlobal;
      let docShell = win.QueryInterface(Ci.nsIInterfaceRequestor)
                        .getInterface(Ci.nsIWebNavigation)
                        .QueryInterface(Ci.nsIDocShell);

      let {securityInfo} = docShell.failedChannel;
      securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
      let {host, port} = win.document.mozDocumentURIIfNotForErrorPages;

      let errorReporter = Cc["@mozilla.org/securityreporter;1"]
                            .getService(Ci.nsISecurityReporter);
      errorReporter.reportTLSError(securityInfo, host, port);
    }
  },
};

AboutNetAndCertErrorListener.init(this);
AboutBlockedSiteListener.init(this);

var ClickEventHandler = {
  init: function init() {
    Services.els.addSystemEventListener(global, "click", this, true);
  },

  handleEvent(event) {
    if (!event.isTrusted || event.defaultPrevented || event.button == 2) {
      return;
    }

    let originalTarget = event.originalTarget;
    let ownerDoc = originalTarget.ownerDocument;
    if (!ownerDoc) {
      return;
    }

    // Handle click events from about pages
    if (event.button == 0) {
      if (ownerDoc.documentURI.startsWith("about:certerror")) {
        this.onCertError(originalTarget, ownerDoc);
        return;
      } else if (ownerDoc.documentURI.startsWith("about:blocked")) {
        this.onAboutBlocked(originalTarget, ownerDoc);
        return;
      } else if (ownerDoc.documentURI.startsWith("about:neterror")) {
        this.onAboutNetError(event, ownerDoc.documentURI);
        return;
      }
    }

    let [href, node, principal] = this._hrefAndLinkNodeForClickEvent(event);

    // get referrer attribute from clicked link and parse it
    // if per element referrer is enabled, the element referrer overrules
    // the document wide referrer
    let referrerPolicy = ownerDoc.referrerPolicy;
    if (node) {
      let referrerAttrValue = Services.netUtils.parseAttributePolicyString(node.
                              getAttribute("referrerpolicy"));
      if (referrerAttrValue !== Ci.nsIHttpChannel.REFERRER_POLICY_UNSET) {
        referrerPolicy = referrerAttrValue;
      }
    }

    let frameOuterWindowID = WebNavigationFrames.getFrameId(ownerDoc.defaultView);

    let json = { button: event.button, shiftKey: event.shiftKey,
                 ctrlKey: event.ctrlKey, metaKey: event.metaKey,
                 altKey: event.altKey, href: null, title: null,
                 bookmark: false, frameOuterWindowID, referrerPolicy,
                 triggeringPrincipal: principal,
                 originAttributes: principal ? principal.originAttributes : {},
                 isContentWindowPrivate: PrivateBrowsingUtils.isContentWindowPrivate(ownerDoc.defaultView)};

    if (href) {
      try {
        BrowserUtils.urlSecurityCheck(href, principal);
      } catch (e) {
        return;
      }

      json.href = href;
      if (node) {
        json.title = node.getAttribute("title");
        if (event.button == 0 && !event.ctrlKey && !event.shiftKey &&
            !event.altKey && !event.metaKey) {
          json.bookmark = node.getAttribute("rel") == "sidebar";
          if (json.bookmark) {
            event.preventDefault(); // Need to prevent the pageload.
          }
        }
      }
      json.noReferrer = BrowserUtils.linkHasNoReferrer(node);

      // Check if the link needs to be opened with mixed content allowed.
      // Only when the owner doc has |mixedContentChannel| and the same origin
      // should we allow mixed content.
      json.allowMixedContent = false;
      let docshell = ownerDoc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
                             .getInterface(Ci.nsIWebNavigation)
                             .QueryInterface(Ci.nsIDocShell);
      if (docShell.mixedContentChannel) {
        const sm = Services.scriptSecurityManager;
        try {
          let targetURI = Services.io.newURI(href);
          sm.checkSameOriginURI(docshell.mixedContentChannel.URI, targetURI, false);
          json.allowMixedContent = true;
        } catch (e) {}
      }
      json.originPrincipal = ownerDoc.nodePrincipal;
      json.triggeringPrincipal = ownerDoc.nodePrincipal;

      sendAsyncMessage("Content:Click", json);
      return;
    }

    // This might be middle mouse navigation.
    if (event.button == 1) {
      sendAsyncMessage("Content:Click", json);
    }
  },

  onCertError(targetElement, ownerDoc) {
    let docShell = ownerDoc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
                                       .getInterface(Ci.nsIWebNavigation)
                                       .QueryInterface(Ci.nsIDocShell);
    sendAsyncMessage("Browser:CertExceptionError", {
      location: ownerDoc.location.href,
      elementId: targetElement.getAttribute("id"),
      isTopFrame: (ownerDoc.defaultView.parent === ownerDoc.defaultView),
      securityInfoAsString: getSerializedSecurityInfo(docShell),
    });
  },

  onAboutBlocked(targetElement, ownerDoc) {
    var reason = "phishing";
    if (/e=malwareBlocked/.test(ownerDoc.documentURI)) {
      reason = "malware";
    } else if (/e=unwantedBlocked/.test(ownerDoc.documentURI)) {
      reason = "unwanted";
    } else if (/e=harmfulBlocked/.test(ownerDoc.documentURI)) {
      reason = "harmful";
    }

    let docShell = ownerDoc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
                                       .getInterface(Ci.nsIWebNavigation)
                                      .QueryInterface(Ci.nsIDocShell);

    sendAsyncMessage("Browser:SiteBlockedError", {
      location: ownerDoc.location.href,
      reason,
      elementId: targetElement.getAttribute("id"),
      isTopFrame: (ownerDoc.defaultView.parent === ownerDoc.defaultView),
      blockedInfo: getSiteBlockedErrorDetails(docShell),
    });
  },

  onAboutNetError(event, documentURI) {
    let elmId = event.originalTarget.getAttribute("id");
    if (elmId == "returnButton") {
      sendAsyncMessage("Browser:SSLErrorGoBack", {});
      return;
    }
    if (elmId != "errorTryAgain" || !/e=netOffline/.test(documentURI)) {
      return;
    }
    // browser front end will handle clearing offline mode and refreshing
    // the page *if* we're in offline mode now. Otherwise let the error page
    // handle the click.
    if (Services.io.offline) {
      event.preventDefault();
      sendAsyncMessage("Browser:EnableOnlineMode", {});
    }
  },

  /**
   * Extracts linkNode and href for the current click target.
   *
   * @param event
   *        The click event.
   * @return [href, linkNode, linkPrincipal].
   *
   * @note linkNode will be null if the click wasn't on an anchor
   *       element. This includes SVG links, because callers expect |node|
   *       to behave like an <a> element, which SVG links (XLink) don't.
   */
  _hrefAndLinkNodeForClickEvent(event) {
    function isHTMLLink(aNode) {
      // Be consistent with what nsContextMenu.js does.
      return ((aNode instanceof content.HTMLAnchorElement && aNode.href) ||
              (aNode instanceof content.HTMLAreaElement && aNode.href) ||
              aNode instanceof content.HTMLLinkElement);
    }

    let node = event.target;
    while (node && !isHTMLLink(node)) {
      node = node.parentNode;
    }

    if (node)
      return [node.href, node, node.ownerDocument.nodePrincipal];

    // If there is no linkNode, try simple XLink.
    let href, baseURI;
    node = event.target;
    while (node && !href) {
      if (node.nodeType == content.Node.ELEMENT_NODE &&
          (node.localName == "a" ||
           node.namespaceURI == "http://www.w3.org/1998/Math/MathML")) {
        href = node.getAttribute("href") ||
               node.getAttributeNS("http://www.w3.org/1999/xlink", "href");
        if (href) {
          baseURI = node.ownerDocument.baseURIObject;
          break;
        }
      }
      node = node.parentNode;
    }

    // In case of XLink, we don't return the node we got href from since
    // callers expect <a>-like elements.
    // Note: makeURI() will throw if aUri is not a valid URI.
    return [href ? Services.io.newURI(href, null, baseURI).spec : null, null,
            node && node.ownerDocument.nodePrincipal];
  }
};
ClickEventHandler.init();

ContentLinkHandler.init(this);
ContentMetaHandler.init(this);

// TODO: Load this lazily so the JSM is run only if a relevant event/message fires.
var pluginContent = new PluginContent(global);

addEventListener("DOMWindowFocus", function(event) {
  sendAsyncMessage("DOMWindowFocus", {});
}, false);

// We use this shim so that ContentWebRTC.jsm will not be loaded until
// it is actually needed.
var ContentWebRTCShim = message => ContentWebRTC.receiveMessage(message);

addMessageListener("rtcpeer:Allow", ContentWebRTCShim);
addMessageListener("rtcpeer:Deny", ContentWebRTCShim);
addMessageListener("webrtc:Allow", ContentWebRTCShim);
addMessageListener("webrtc:Deny", ContentWebRTCShim);
addMessageListener("webrtc:StopSharing", ContentWebRTCShim);

addEventListener("pageshow", function(event) {
  if (event.target == content.document) {
    sendAsyncMessage("PageVisibility:Show", {
      persisted: event.persisted,
    });
  }
});
addEventListener("pagehide", function(event) {
  if (event.target == content.document) {
    sendAsyncMessage("PageVisibility:Hide", {
      persisted: event.persisted,
    });
  }
});

var PageMetadataMessenger = {
  init() {
    addMessageListener("PageMetadata:GetPageData", this);
    addMessageListener("PageMetadata:GetMicroformats", this);
  },
  receiveMessage(message) {
    switch (message.name) {
      case "PageMetadata:GetPageData": {
        let target = contextMenu.getTarget(message);
        let result = PageMetadata.getData(content.document, target);
        sendAsyncMessage("PageMetadata:PageDataResult", result);
        break;
      }
      case "PageMetadata:GetMicroformats": {
        let target = contextMenu.getTarget(message);
        let result = PageMetadata.getMicroformats(content.document, target);
        sendAsyncMessage("PageMetadata:MicroformatsResult", result);
        break;
      }
    }
  }
};
PageMetadataMessenger.init();

addMessageListener("Bookmarks:GetPageDetails", (message) => {
  let doc = content.document;
  let isErrorPage = /^about:(neterror|certerror|blocked)/.test(doc.documentURI);
  sendAsyncMessage("Bookmarks:GetPageDetails:Result",
                   { isErrorPage,
                     description: PlacesUIUtils.getDescriptionFromDocument(doc) });
});

var LightWeightThemeWebInstallListener = {
  _previewWindow: null,

  init() {
    addEventListener("InstallBrowserTheme", this, false, true);
    addEventListener("PreviewBrowserTheme", this, false, true);
    addEventListener("ResetBrowserThemePreview", this, false, true);
  },

  handleEvent(event) {
    switch (event.type) {
      case "InstallBrowserTheme": {
        sendAsyncMessage("LightWeightThemeWebInstaller:Install", {
          baseURI: event.target.baseURI,
          principal: event.target.nodePrincipal,
          themeData: event.target.getAttribute("data-browsertheme"),
        });
        break;
      }
      case "PreviewBrowserTheme": {
        sendAsyncMessage("LightWeightThemeWebInstaller:Preview", {
          baseURI: event.target.baseURI,
          principal: event.target.nodePrincipal,
          themeData: event.target.getAttribute("data-browsertheme"),
        });
        this._previewWindow = event.target.ownerGlobal;
        this._previewWindow.addEventListener("pagehide", this, true);
        break;
      }
      case "pagehide": {
        sendAsyncMessage("LightWeightThemeWebInstaller:ResetPreview");
        this._resetPreviewWindow();
        break;
      }
      case "ResetBrowserThemePreview": {
        if (this._previewWindow) {
          sendAsyncMessage("LightWeightThemeWebInstaller:ResetPreview",
                           {principal: event.target.nodePrincipal});
          this._resetPreviewWindow();
        }
        break;
      }
    }
  },

  _resetPreviewWindow() {
    this._previewWindow.removeEventListener("pagehide", this, true);
    this._previewWindow = null;
  }
};

LightWeightThemeWebInstallListener.init();

var PageInfoListener = {

  init() {
    addMessageListener("PageInfo:getData", this);
  },

  receiveMessage(message) {
    let strings = message.data.strings;
    let window;
    let document;

    let frameOuterWindowID = message.data.frameOuterWindowID;

    // If inside frame then get the frame's window and document.
    if (frameOuterWindowID != undefined) {
      window = Services.wm.getOuterWindowWithId(frameOuterWindowID);
      document = window.document;
    } else {
      window = content.window;
      document = content.document;
    }

    let pageInfoData = {metaViewRows: this.getMetaInfo(document),
                        docInfo: this.getDocumentInfo(document),
                        feeds: this.getFeedsInfo(document, strings),
                        windowInfo: this.getWindowInfo(window)};

    sendAsyncMessage("PageInfo:data", pageInfoData);

    // Separate step so page info dialog isn't blank while waiting for this to finish.
    this.getMediaInfo(document, window, strings);
  },

  getMetaInfo(document) {
    let metaViewRows = [];

    // Get the meta tags from the page.
    let metaNodes = document.getElementsByTagName("meta");

    for (let metaNode of metaNodes) {
      metaViewRows.push([metaNode.name || metaNode.httpEquiv || metaNode.getAttribute("property"),
                        metaNode.content]);
    }

    return metaViewRows;
  },

  getWindowInfo(window) {
    let windowInfo = {};
    windowInfo.isTopWindow = window == window.top;

    let hostName = null;
    try {
      hostName = Services.io.newURI(window.location.href).displayHost;
    } catch (exception) { }

    windowInfo.hostName = hostName;
    return windowInfo;
  },

  getDocumentInfo(document) {
    let docInfo = {};
    docInfo.title = document.title;
    docInfo.location = document.location.toString();
    try {
      docInfo.location = Services.io.newURI(document.location.toString()).displaySpec;
    } catch (exception) { }
    docInfo.referrer = document.referrer;
    try {
      if (document.referrer) {
        docInfo.referrer = Services.io.newURI(document.referrer).displaySpec;
      }
    } catch (exception) { }
    docInfo.compatMode = document.compatMode;
    docInfo.contentType = document.contentType;
    docInfo.characterSet = document.characterSet;
    docInfo.lastModified = document.lastModified;
    docInfo.principal = document.nodePrincipal;

    let documentURIObject = {};
    documentURIObject.spec = document.documentURIObject.spec;
    docInfo.documentURIObject = documentURIObject;

    docInfo.isContentWindowPrivate = PrivateBrowsingUtils.isContentWindowPrivate(content);

    return docInfo;
  },

  getFeedsInfo(document, strings) {
    let feeds = [];
    // Get the feeds from the page.
    let linkNodes = document.getElementsByTagName("link");
    let length = linkNodes.length;
    for (let i = 0; i < length; i++) {
      let link = linkNodes[i];
      if (!link.href) {
        continue;
      }
      let rel = link.rel && link.rel.toLowerCase();
      let rels = {};

      if (rel) {
        for (let relVal of rel.split(/\s+/)) {
          rels[relVal] = true;
        }
      }

      if (rels.feed || (link.type && rels.alternate && !rels.stylesheet)) {
        let type = Feeds.isValidFeed(link, document.nodePrincipal, "feed" in rels);
        if (type) {
          type = strings[type] || strings["application/rss+xml"];
          feeds.push([link.title, type, link.href]);
        }
      }
    }
    return feeds;
  },

  // Only called once to get the media tab's media elements from the content page.
  getMediaInfo(document, window, strings) {
    let frameList = this.goThroughFrames(document, window);
    this.processFrames(document, frameList, strings);
  },

  goThroughFrames(document, window) {
    let frameList = [document];
    if (window && window.frames.length > 0) {
      let num = window.frames.length;
      for (let i = 0; i < num; i++) {
        // Recurse through the frames.
        frameList.concat(this.goThroughFrames(window.frames[i].document,
                                              window.frames[i]));
      }
    }
    return frameList;
  },

  async processFrames(document, frameList, strings) {
    let nodeCount = 0;
    for (let doc of frameList) {
      let iterator = doc.createTreeWalker(doc, content.NodeFilter.SHOW_ELEMENT);

      // Goes through all the elements on the doc. imageViewRows takes only the media elements.
      while (iterator.nextNode()) {
        let mediaItems = this.getMediaItems(document, strings, iterator.currentNode);

        if (mediaItems.length) {
          sendAsyncMessage("PageInfo:mediaData",
                           {mediaItems, isComplete: false});
        }

        if (++nodeCount % 500 == 0) {
          // setTimeout every 500 elements so we don't keep blocking the content process.
          await new Promise(resolve => setTimeout(resolve, 10));
        }
      }
    }
    // Send that page info media fetching has finished.
    sendAsyncMessage("PageInfo:mediaData", {isComplete: true});
  },

  getMediaItems(document, strings, elem) {
    // Check for images defined in CSS (e.g. background, borders)
    let computedStyle = elem.ownerGlobal.getComputedStyle(elem);
    // A node can have multiple media items associated with it - for example,
    // multiple background images.
    let mediaItems = [];

    let addImage = (url, type, alt, el, isBg) => {
      let element = this.serializeElementInfo(document, url, type, alt, el, isBg);
      mediaItems.push([url, type, alt, element, isBg]);
    };

    if (computedStyle) {
      let addImgFunc = (label, val) => {
        if (val.primitiveType == content.CSSPrimitiveValue.CSS_URI) {
          addImage(val.getStringValue(), label, strings.notSet, elem, true);
        } else if (val.primitiveType == content.CSSPrimitiveValue.CSS_STRING) {
          // This is for -moz-image-rect.
          // TODO: Reimplement once bug 714757 is fixed.
          let strVal = val.getStringValue();
          if (strVal.search(/^.*url\(\"?/) > -1) {
            let url = strVal.replace(/^.*url\(\"?/, "").replace(/\"?\).*$/, "");
            addImage(url, label, strings.notSet, elem, true);
          }
        } else if (val.cssValueType == content.CSSValue.CSS_VALUE_LIST) {
          // Recursively resolve multiple nested CSS value lists.
          for (let i = 0; i < val.length; i++) {
            addImgFunc(label, val.item(i));
          }
        }
      };

      addImgFunc(strings.mediaBGImg, computedStyle.getPropertyCSSValue("background-image"));
      addImgFunc(strings.mediaBorderImg, computedStyle.getPropertyCSSValue("border-image-source"));
      addImgFunc(strings.mediaListImg, computedStyle.getPropertyCSSValue("list-style-image"));
      addImgFunc(strings.mediaCursor, computedStyle.getPropertyCSSValue("cursor"));
    }

    // One swi^H^H^Hif-else to rule them all.
    if (elem instanceof content.HTMLImageElement) {
      addImage(elem.src, strings.mediaImg,
               (elem.hasAttribute("alt")) ? elem.alt : strings.notSet, elem, false);
    } else if (elem instanceof content.SVGImageElement) {
      try {
        // Note: makeURLAbsolute will throw if either the baseURI is not a valid URI
        //       or the URI formed from the baseURI and the URL is not a valid URI.
        if (elem.href.baseVal) {
          let href = Services.io.newURI(elem.href.baseVal, null, Services.io.newURI(elem.baseURI)).spec;
          addImage(href, strings.mediaImg, "", elem, false);
        }
      } catch (e) { }
    } else if (elem instanceof content.HTMLVideoElement) {
      addImage(elem.currentSrc, strings.mediaVideo, "", elem, false);
    } else if (elem instanceof content.HTMLAudioElement) {
      addImage(elem.currentSrc, strings.mediaAudio, "", elem, false);
    } else if (elem instanceof content.HTMLLinkElement) {
      if (elem.rel && /\bicon\b/i.test(elem.rel)) {
        addImage(elem.href, strings.mediaLink, "", elem, false);
      }
    } else if (elem instanceof content.HTMLInputElement || elem instanceof content.HTMLButtonElement) {
      if (elem.type.toLowerCase() == "image") {
        addImage(elem.src, strings.mediaInput,
                 (elem.hasAttribute("alt")) ? elem.alt : strings.notSet, elem, false);
      }
    } else if (elem instanceof content.HTMLObjectElement) {
      addImage(elem.data, strings.mediaObject, this.getValueText(elem), elem, false);
    } else if (elem instanceof content.HTMLEmbedElement) {
      addImage(elem.src, strings.mediaEmbed, "", elem, false);
    }

    return mediaItems;
  },

  /**
   * Set up a JSON element object with all the instanceOf and other infomation that
   * makePreview in pageInfo.js uses to figure out how to display the preview.
   */

  serializeElementInfo(document, url, type, alt, item, isBG) {
    let result = {};

    let imageText;
    if (!isBG &&
        !(item instanceof content.SVGImageElement) &&
        !(document instanceof content.ImageDocument)) {
      imageText = item.title || item.alt;

      if (!imageText && !(item instanceof content.HTMLImageElement)) {
        imageText = this.getValueText(item);
      }
    }

    result.imageText = imageText;
    result.longDesc = item.longDesc;
    result.numFrames = 1;

    if (item instanceof content.HTMLObjectElement ||
      item instanceof content.HTMLEmbedElement ||
      item instanceof content.HTMLLinkElement) {
      result.mimeType = item.type;
    }

    if (!result.mimeType && !isBG && item instanceof Ci.nsIImageLoadingContent) {
      // Interface for image loading content.
      let imageRequest = item.getRequest(Ci.nsIImageLoadingContent.CURRENT_REQUEST);
      if (imageRequest) {
        result.mimeType = imageRequest.mimeType;
        let image = !(imageRequest.imageStatus & imageRequest.STATUS_ERROR) && imageRequest.image;
        if (image) {
          result.numFrames = image.numFrames;
        }
      }
    }

    // If we have a data url, get the MIME type from the url.
    if (!result.mimeType && url.startsWith("data:")) {
      let dataMimeType = /^data:(image\/[^;,]+)/i.exec(url);
      if (dataMimeType)
        result.mimeType = dataMimeType[1].toLowerCase();
    }

    result.HTMLLinkElement = item instanceof content.HTMLLinkElement;
    result.HTMLInputElement = item instanceof content.HTMLInputElement;
    result.HTMLImageElement = item instanceof content.HTMLImageElement;
    result.HTMLObjectElement = item instanceof content.HTMLObjectElement;
    result.SVGImageElement = item instanceof content.SVGImageElement;
    result.HTMLVideoElement = item instanceof content.HTMLVideoElement;
    result.HTMLAudioElement = item instanceof content.HTMLAudioElement;

    if (isBG) {
      // Items that are showing this image as a background
      // image might not necessarily have a width or height,
      // so we'll dynamically generate an image and send up the
      // natural dimensions.
      let img = content.document.createElement("img");
      img.src = url;
      result.naturalWidth = img.naturalWidth;
      result.naturalHeight = img.naturalHeight;
    } else {
      // Otherwise, we can use the current width and height
      // of the image.
      result.width = item.width;
      result.height = item.height;
    }

    if (item instanceof content.SVGImageElement) {
      result.SVGImageElementWidth = item.width.baseVal.value;
      result.SVGImageElementHeight = item.height.baseVal.value;
    }

    result.baseURI = item.baseURI;

    return result;
  },

  // Other Misc Stuff
  // Modified from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html
  // parse a node to extract the contents of the node
  getValueText(node) {

    let valueText = "";

    // Form input elements don't generally contain information that is useful to our callers, so return nothing.
    if (node instanceof content.HTMLInputElement ||
        node instanceof content.HTMLSelectElement ||
        node instanceof content.HTMLTextAreaElement) {
      return valueText;
    }

    // Otherwise recurse for each child.
    let length = node.childNodes.length;

    for (let i = 0; i < length; i++) {
      let childNode = node.childNodes[i];
      let nodeType = childNode.nodeType;

      // Text nodes are where the goods are.
      if (nodeType == content.Node.TEXT_NODE) {
        valueText += " " + childNode.nodeValue;
      } else if (nodeType == content.Node.ELEMENT_NODE) {
        // And elements can have more text inside them.
        // Images are special, we want to capture the alt text as if the image weren't there.
        if (childNode instanceof content.HTMLImageElement) {
          valueText += " " + this.getAltText(childNode);
        } else {
          valueText += " " + this.getValueText(childNode);
        }
      }
    }

    return this.stripWS(valueText);
  },

  // Copied from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html.
  // Traverse the tree in search of an img or area element and grab its alt tag.
  getAltText(node) {
    let altText = "";

    if (node.alt) {
      return node.alt;
    }
    let length = node.childNodes.length;
    for (let i = 0; i < length; i++) {
      if ((altText = this.getAltText(node.childNodes[i]) != undefined)) { // stupid js warning...
        return altText;
      }
    }
    return "";
  },

  // Copied from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html.
  // Strip leading and trailing whitespace, and replace multiple consecutive whitespace characters with a single space.
  stripWS(text) {
    let middleRE = /\s+/g;
    let endRE = /(^\s+)|(\s+$)/g;

    text = text.replace(middleRE, " ");
    return text.replace(endRE, "");
  }
};
PageInfoListener.init();

let OfflineApps = {
  _docId: 0,
  _docIdMap: new Map(),

  _docManifestSet: new Set(),

  _observerAdded: false,
  registerWindow(aWindow) {
    if (!this._observerAdded) {
      this._observerAdded = true;
      Services.obs.addObserver(this, "offline-cache-update-completed", true);
    }
    let manifestURI = this._getManifestURI(aWindow);
    this._docManifestSet.add(manifestURI.spec);
  },

  handleEvent(event) {
    if (event.type == "MozApplicationManifest") {
      this.offlineAppRequested(event.originalTarget.defaultView);
    }
  },

  _getManifestURI(aWindow) {
    if (!aWindow.document.documentElement)
      return null;

    var attr = aWindow.document.documentElement.getAttribute("manifest");
    if (!attr)
      return null;

    try {
      return Services.io.newURI(attr, aWindow.document.characterSet,
                                Services.io.newURI(aWindow.location.href));
    } catch (e) {
      return null;
    }
  },

  offlineAppRequested(aContentWindow) {
    this.registerWindow(aContentWindow);
    if (!Services.prefs.getBoolPref("browser.offline-apps.notify")) {
      return;
    }

    let currentURI = aContentWindow.document.documentURIObject;
    // don't bother showing UI if the user has already made a decision
    if (Services.perms.testExactPermission(currentURI, "offline-app") != Services.perms.UNKNOWN_ACTION)
      return;

    try {
      if (Services.prefs.getBoolPref("offline-apps.allow_by_default")) {
        // all pages can use offline capabilities, no need to ask the user
        return;
      }
    } catch (e) {
      // this pref isn't set by default, ignore failures
    }
    let docId = ++this._docId;
    this._docIdMap.set(docId, Cu.getWeakReference(aContentWindow.document));
    sendAsyncMessage("OfflineApps:RequestPermission", {
      uri: currentURI.spec,
      docId,
    });
  },

  _startFetching(aDocument) {
    if (!aDocument.documentElement)
      return;

    let manifestURI = this._getManifestURI(aDocument.defaultView);
    if (!manifestURI)
      return;

    var updateService = Cc["@mozilla.org/offlinecacheupdate-service;1"].
                        getService(Ci.nsIOfflineCacheUpdateService);
    updateService.scheduleUpdate(manifestURI, aDocument.documentURIObject,
                                 aDocument.nodePrincipal, aDocument.defaultView);
  },

  receiveMessage(aMessage) {
    if (aMessage.name == "OfflineApps:StartFetching") {
      let doc = this._docIdMap.get(aMessage.data.docId);
      doc = doc && doc.get();
      if (doc) {
        this._startFetching(doc);
      }
      this._docIdMap.delete(aMessage.data.docId);
    }
  },

  observe(aSubject, aTopic, aState) {
    if (aTopic == "offline-cache-update-completed") {
      let cacheUpdate = aSubject.QueryInterface(Ci.nsIOfflineCacheUpdate);
      let uri = cacheUpdate.manifestURI;
      if (uri && this._docManifestSet.has(uri.spec)) {
        sendAsyncMessage("OfflineApps:CheckUsage", {uri: uri.spec});
      }
    }
  },
  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                         Ci.nsISupportsWeakReference]),
};

addEventListener("MozApplicationManifest", OfflineApps, false);
addMessageListener("OfflineApps:StartFetching", OfflineApps);
PK
!<��4�u�u8chrome/browser/content/browser/customizableui/panelUI.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

ChromeUtils.defineModuleGetter(this, "AppMenuNotifications",
                               "resource://gre/modules/AppMenuNotifications.jsm");
ChromeUtils.defineModuleGetter(this, "NewTabUtils",
                               "resource://gre/modules/NewTabUtils.jsm");
ChromeUtils.defineModuleGetter(this, "PanelMultiView",
                               "resource:///modules/PanelMultiView.jsm");
ChromeUtils.defineModuleGetter(this, "ScrollbarSampler",
                               "resource:///modules/ScrollbarSampler.jsm");

/**
 * Maintains the state and dispatches events for the main menu panel.
 */

const PanelUI = {
  /** Panel events that we listen for. **/
  get kEvents() {
    return ["popupshowing", "popupshown", "popuphiding", "popuphidden"];
  },
  /**
   * Used for lazily getting and memoizing elements from the document. Lazy
   * getters are set in init, and memoizing happens after the first retrieval.
   */
  get kElements() {
    return {
      mainView: "appMenu-mainView",
      multiView: "appMenu-multiView",
      helpView: "PanelUI-helpView",
      libraryView: "appMenu-libraryView",
      libraryRecentHighlights: "appMenu-library-recentHighlights",
      menuButton: "PanelUI-menu-button",
      panel: "appMenu-popup",
      notificationPanel: "appMenu-notification-popup",
      addonNotificationContainer: "appMenu-addon-banners",
      overflowFixedList: "widget-overflow-fixed-list",
      overflowPanel: "widget-overflow",
      navbar: "nav-bar",
    };
  },

  _initialized: false,
  _notifications: null,

  init() {
    this._initElements();

    this.menuButton.addEventListener("mousedown", this);
    this.menuButton.addEventListener("keypress", this);
    this._overlayScrollListenerBoundFn = this._overlayScrollListener.bind(this);

    Services.obs.addObserver(this, "fullscreen-nav-toolbox");
    Services.obs.addObserver(this, "appMenu-notifications");

    XPCOMUtils.defineLazyPreferenceGetter(this, "autoHideToolbarInFullScreen",
      "browser.fullscreen.autohide", false, (pref, previousValue, newValue) => {
        // On OSX, or with autohide preffed off, MozDOMFullscreen is the only
        // event we care about, since fullscreen should behave just like non
        // fullscreen. Otherwise, we don't want to listen to these because
        // we'd just be spamming ourselves with both of them whenever a user
        // opened a video.
        if (newValue) {
          window.removeEventListener("MozDOMFullscreen:Entered", this);
          window.removeEventListener("MozDOMFullscreen:Exited", this);
          window.addEventListener("fullscreen", this);
        } else {
          window.addEventListener("MozDOMFullscreen:Entered", this);
          window.addEventListener("MozDOMFullscreen:Exited", this);
          window.removeEventListener("fullscreen", this);
        }

        this._updateNotifications(false);
      }, autoHidePref => autoHidePref && Services.appinfo.OS !== "Darwin");

    if (this.autoHideToolbarInFullScreen) {
      window.addEventListener("fullscreen", this);
    } else {
      window.addEventListener("MozDOMFullscreen:Entered", this);
      window.addEventListener("MozDOMFullscreen:Exited", this);
    }

    XPCOMUtils.defineLazyPreferenceGetter(this, "libraryRecentHighlightsEnabled",
      "browser.library.activity-stream.enabled", false, (pref, previousValue, newValue) => {
        if (!newValue)
          this.clearLibraryRecentHighlights();
      });

    window.addEventListener("activate", this);
    window.matchMedia("(-moz-overlay-scrollbars)").addListener(this._overlayScrollListenerBoundFn);
    CustomizableUI.addListener(this);

    for (let event of this.kEvents) {
      this.notificationPanel.addEventListener(event, this);
    }

    // We do this sync on init because in order to have the overflow button show up
    // we need to know whether anything is in the permanent panel area.
    this.overflowFixedList.hidden = false;
    // Also unhide the separator. We use CSS to hide/show it based on the panel's content.
    this.overflowFixedList.previousSibling.hidden = false;
    CustomizableUI.registerMenuPanel(this.overflowFixedList, CustomizableUI.AREA_FIXED_OVERFLOW_PANEL);
    this.updateOverflowStatus();

    Services.obs.notifyObservers(null, "appMenu-notifications-request", "refresh");

    this._initialized = true;
  },

  _initElements() {
    for (let [k, v] of Object.entries(this.kElements)) {
      // Need to do fresh let-bindings per iteration
      let getKey = k;
      let id = v;
      this.__defineGetter__(getKey, function() {
        delete this[getKey];
        return this[getKey] = document.getElementById(id);
      });
    }
  },

  _eventListenersAdded: false,
  _ensureEventListenersAdded() {
    if (this._eventListenersAdded)
      return;
    this._addEventListeners();
  },

  _addEventListeners() {
    for (let event of this.kEvents) {
      this.panel.addEventListener(event, this);
    }

    this.helpView.addEventListener("ViewShowing", this._onHelpViewShow);
    this._eventListenersAdded = true;
  },

  _removeEventListeners() {
    for (let event of this.kEvents) {
      this.panel.removeEventListener(event, this);
    }
    this.helpView.removeEventListener("ViewShowing", this._onHelpViewShow);
    this._eventListenersAdded = false;
  },

  uninit() {
    this._removeEventListeners();
    for (let event of this.kEvents) {
      this.notificationPanel.removeEventListener(event, this);
    }

    Services.obs.removeObserver(this, "fullscreen-nav-toolbox");
    Services.obs.removeObserver(this, "appMenu-notifications");

    window.removeEventListener("MozDOMFullscreen:Entered", this);
    window.removeEventListener("MozDOMFullscreen:Exited", this);
    window.removeEventListener("fullscreen", this);
    window.removeEventListener("activate", this);
    this.menuButton.removeEventListener("mousedown", this);
    this.menuButton.removeEventListener("keypress", this);
    window.matchMedia("(-moz-overlay-scrollbars)").removeListener(this._overlayScrollListenerBoundFn);
    CustomizableUI.removeListener(this);
    this._overlayScrollListenerBoundFn = null;
    this.libraryView.removeEventListener("ViewShowing", this);
  },

  /**
   * Opens the menu panel if it's closed, or closes it if it's
   * open.
   *
   * @param aEvent the event that triggers the toggle.
   */
  toggle(aEvent) {
    // Don't show the panel if the window is in customization mode,
    // since this button doubles as an exit path for the user in this case.
    if (document.documentElement.hasAttribute("customizing")) {
      return;
    }
    this._ensureEventListenersAdded();
    if (this.panel.state == "open") {
      this.hide();
    } else if (this.panel.state == "closed") {
      this.show(aEvent);
    }
  },

  /**
   * Opens the menu panel. If the event target has a child with the
   * toolbarbutton-icon attribute, the panel will be anchored on that child.
   * Otherwise, the panel is anchored on the event target itself.
   *
   * @param aEvent the event (if any) that triggers showing the menu.
   */
  show(aEvent) {
    this._ensureShortcutsShown();
    return new Promise(resolve => {
      this.ensureReady().then(() => {
        if (this.panel.state == "open" ||
            document.documentElement.hasAttribute("customizing")) {
          resolve();
          return;
        }

        let anchor;
        let domEvent = null;
        if (!aEvent ||
            aEvent.type == "command") {
          anchor = this.menuButton;
        } else {
          domEvent = aEvent;
          anchor = aEvent.target;
        }

        this.panel.addEventListener("popupshown", function() {
          resolve();
        }, {once: true});

        anchor = this._getPanelAnchor(anchor);
        PanelMultiView.openPopup(this.panel, anchor, {
          triggerEvent: domEvent,
        }).catch(Cu.reportError);
      }, (reason) => {
        console.error("Error showing the PanelUI menu", reason);
      });
    });
  },

  /**
   * If the menu panel is being shown, hide it.
   */
  hide() {
    if (document.documentElement.hasAttribute("customizing")) {
      return;
    }

    PanelMultiView.hidePopup(this.panel);
  },

  observe(subject, topic, status) {
    switch (topic) {
      case "fullscreen-nav-toolbox":
        if (this._notifications) {
          this._updateNotifications(false);
        }
        break;
      case "appMenu-notifications":
        // Don't initialize twice.
        if (status == "init" && this._notifications) {
          break;
        }
        this._notifications = AppMenuNotifications.notifications;
        this._updateNotifications(true);
        break;
    }
  },

  handleEvent(aEvent) {
    // Ignore context menus and menu button menus showing and hiding:
    if (aEvent.type.startsWith("popup") &&
        aEvent.target != this.panel) {
      return;
    }
    switch (aEvent.type) {
      case "popupshowing":
        updateEditUIVisibility();
        // Fall through
      case "popupshown":
        if (aEvent.type == "popupshown") {
          CustomizableUI.addPanelCloseListeners(this.panel);
        }
        // Fall through
      case "popuphiding":
        if (aEvent.type == "popuphiding") {
          updateEditUIVisibility();
        }
        // Fall through
      case "popuphidden":
        this._updateNotifications();
        this._updatePanelButton(aEvent.target);
        if (aEvent.type == "popuphidden") {
          CustomizableUI.removePanelCloseListeners(this.panel);
        }
        break;
      case "mousedown":
        if (aEvent.button == 0)
          this.toggle(aEvent);
        break;
      case "keypress":
        this.toggle(aEvent);
        break;
      case "MozDOMFullscreen:Entered":
      case "MozDOMFullscreen:Exited":
      case "fullscreen":
      case "activate":
        this._updateNotifications();
        break;
      case "ViewShowing":
        if (aEvent.target == this.libraryView) {
          this.onLibraryViewShowing(aEvent.target).catch(Cu.reportError);
        }
        break;
    }
  },

  get isReady() {
    return !!this._isReady;
  },

  get isNotificationPanelOpen() {
    let panelState = this.notificationPanel.state;

    return panelState == "showing" || panelState == "open";
  },

  /**
   * Registering the menu panel is done lazily for performance reasons. This
   * method is exposed so that CustomizationMode can force panel-readyness in the
   * event that customization mode is started before the panel has been opened
   * by the user.
   *
   * @param aCustomizing (optional) set to true if this was called while entering
   *        customization mode. If that's the case, we trust that customization
   *        mode will handle calling beginBatchUpdate and endBatchUpdate.
   *
   * @return a Promise that resolves once the panel is ready to roll.
   */
  async ensureReady() {
    if (this._isReady) {
      return;
    }

    await window.delayedStartupPromise;
    this._ensureEventListenersAdded();
    this.panel.hidden = false;
    this._isReady = true;
  },

  /**
   * Switch the panel to the help view if it's not already
   * in that view.
   */
  showHelpView(aAnchor) {
    this._ensureEventListenersAdded();
    this.multiView.showSubView("PanelUI-helpView", aAnchor);
  },

  /**
   * Shows a subview in the panel with a given ID.
   *
   * @param aViewId the ID of the subview to show.
   * @param aAnchor the element that spawned the subview.
   * @param aEvent the event triggering the view showing.
   */
  async showSubView(aViewId, aAnchor, aEvent) {
    let domEvent = null;
    if (aEvent) {
      if (aEvent.type == "mousedown" && aEvent.button != 0) {
        return;
      }
      if (aEvent.type == "command" && aEvent.inputSource != null) {
        // Synthesize a new DOM mouse event to pass on the inputSource.
        domEvent = document.createEvent("MouseEvent");
        domEvent.initNSMouseEvent("click", true, true, null, 0, aEvent.screenX, aEvent.screenY,
                                  0, 0, false, false, false, false, 0, aEvent.target, 0, aEvent.inputSource);
      } else if (aEvent.mozInputSource != null) {
        domEvent = aEvent;
      }
    }

    this._ensureEventListenersAdded();
    let viewNode = document.getElementById(aViewId);
    if (!viewNode) {
      Cu.reportError("Could not show panel subview with id: " + aViewId);
      return;
    }

    if (!aAnchor) {
      Cu.reportError("Expected an anchor when opening subview with id: " + aViewId);
      return;
    }

    this.ensureLibraryInitialized(viewNode);

    let container = aAnchor.closest("panelmultiview");
    if (container) {
      container.showSubView(aViewId, aAnchor);
    } else if (!aAnchor.open) {
      aAnchor.open = true;

      let tempPanel = document.createElement("panel");
      tempPanel.setAttribute("type", "arrow");
      tempPanel.setAttribute("id", "customizationui-widget-panel");
      tempPanel.setAttribute("class", "cui-widget-panel");
      tempPanel.setAttribute("viewId", aViewId);
      if (aAnchor.getAttribute("tabspecific")) {
        tempPanel.setAttribute("tabspecific", true);
      }
      if (this._disableAnimations) {
        tempPanel.setAttribute("animate", "false");
      }
      tempPanel.setAttribute("context", "");
      tempPanel.setAttribute("photon", true);
      document.getElementById(CustomizableUI.AREA_NAVBAR).appendChild(tempPanel);
      // If the view has a footer, set a convenience class on the panel.
      tempPanel.classList.toggle("cui-widget-panelWithFooter",
                                 viewNode.querySelector(".panel-subview-footer"));

      let multiView = document.createElement("panelmultiview");
      multiView.setAttribute("id", "customizationui-widget-multiview");
      multiView.setAttribute("viewCacheId", "appMenu-viewCache");
      multiView.setAttribute("mainViewId", viewNode.id);
      tempPanel.appendChild(multiView);
      viewNode.classList.add("cui-widget-panelview");

      let viewShown = false;
      let panelRemover = () => {
        viewNode.classList.remove("cui-widget-panelview");
        if (viewShown) {
          CustomizableUI.removePanelCloseListeners(tempPanel);
          tempPanel.removeEventListener("popuphidden", panelRemover);
        }
        aAnchor.open = false;

        PanelMultiView.removePopup(tempPanel);
      };

      if (aAnchor.parentNode.id == "PersonalToolbar") {
        tempPanel.classList.add("bookmarks-toolbar");
      }

      let anchor = this._getPanelAnchor(aAnchor);

      if (aAnchor != anchor && aAnchor.id) {
        anchor.setAttribute("consumeanchor", aAnchor.id);
      }

      try {
        viewShown = await PanelMultiView.openPopup(tempPanel, anchor, {
          position: "bottomcenter topright",
          triggerEvent: domEvent,
        });
      } catch (ex) {
        Cu.reportError(ex);
      }

      if (viewShown) {
        CustomizableUI.addPanelCloseListeners(tempPanel);
        tempPanel.addEventListener("popuphidden", panelRemover);
      } else {
        panelRemover();
      }
    }
  },

  /**
   * Sets up the event listener for when the Library view is shown.
   *
   * @param {panelview} viewNode The library view.
   */
  ensureLibraryInitialized(viewNode) {
    if (viewNode != this.libraryView || viewNode._initialized)
      return;

    viewNode._initialized = true;
    viewNode.addEventListener("ViewShowing", this);
  },

  /**
   * When the Library view is showing, we can start fetching and populating the
   * list of Recent Highlights.
   * This is done asynchronously and may finish when the view is already visible.
   *
   * @param {panelview} viewNode The library view.
   */
  async onLibraryViewShowing(viewNode) {
    // Since the library is the first view shown, we don't want to add a blocker
    // to the event, which would make PanelMultiView wait to show it. Instead,
    // we keep the space currently reserved for the items, but we hide them.
    if (this._loadingRecentHighlights || !this.libraryRecentHighlightsEnabled) {
      return;
    }

    // Make the elements invisible synchronously, before the view is shown.
    this.makeLibraryRecentHighlightsInvisible();

    // Perform the rest asynchronously while protecting from re-entrancy.
    this._loadingRecentHighlights = true;
    try {
      await this.fetchAndPopulateLibraryRecentHighlights();
    } finally {
      this._loadingRecentHighlights = false;
    }
  },

  /**
   * Fetches the list of Recent Highlights and replaces the items in the Library
   * view with the results.
   */
  async fetchAndPopulateLibraryRecentHighlights() {
    let highlights = await NewTabUtils.activityStreamLinks.getHighlights({
      // As per bug 1402023, hard-coded limit, until Activity Stream develops a
      // richer list.
      numItems: 6,
      withFavicons: true,
      excludePocket: true
    }).catch(ex => {
      // Just hide the section if we can't retrieve the items from the database.
      Cu.reportError(ex);
      return [];
    });

    // Since the call above is asynchronous, the panel may be already hidden
    // at this point, but we still prepare the items for the next time the
    // panel is shown, so their space is reserved. The part of this function
    // that adds the elements is the least expensive anyways.
    this.clearLibraryRecentHighlights();
    if (!highlights.length) {
      return;
    }

    let container = this.libraryRecentHighlights;
    container.hidden = container.previousSibling.hidden =
      container.previousSibling.previousSibling.hidden = false;
    let fragment = document.createDocumentFragment();
    for (let highlight of highlights) {
      let button = document.createElement("toolbarbutton");
      button.classList.add("subviewbutton", "highlight", "subviewbutton-iconic", "bookmark-item");
      let title = highlight.title || highlight.url;
      button.setAttribute("label", title);
      button.setAttribute("tooltiptext", title);
      button.setAttribute("type", "highlight-" + highlight.type);
      button.setAttribute("onclick", "PanelUI.onLibraryHighlightClick(event)");
      if (highlight.favicon) {
        button.setAttribute("image", highlight.favicon);
      }
      button._highlight = highlight;
      fragment.appendChild(button);
    }
    container.appendChild(fragment);
  },

  /**
   * Make all nodes from the 'Recent Highlights' section invisible while we
   * refresh its contents. This is done while the Library view is opening to
   * avoid showing potentially stale items, but still keep the space reserved.
   */
  makeLibraryRecentHighlightsInvisible() {
    for (let button of this.libraryRecentHighlights.children) {
      button.style.visibility = "hidden";
    }
  },

  /**
   * Remove all the nodes from the 'Recent Highlights' section and hide it as well.
   */
  clearLibraryRecentHighlights() {
    let container = this.libraryRecentHighlights;
    while (container.firstChild) {
      container.firstChild.remove();
    }
    container.hidden = container.previousSibling.hidden =
      container.previousSibling.previousSibling.hidden = true;
  },

  /**
   * Event handler; invoked when an item of the Recent Highlights is clicked.
   *
   * @param {MouseEvent} event Click event, originating from the Highlight.
   */
  onLibraryHighlightClick(event) {
    let button = event.target;
    if (event.button > 1 || !button._highlight) {
      return;
    }
    if (event.button == 1) {
      // Bug 1402849, close library panel on mid mouse click
      CustomizableUI.hidePanelForNode(button);
    }
    window.openUILink(button._highlight.url, event);
  },

  /**
   * NB: The enable- and disableSingleSubviewPanelAnimations methods only
   * affect the hiding/showing animations of single-subview panels (tempPanel
   * in the showSubView method).
   */
  disableSingleSubviewPanelAnimations() {
    this._disableAnimations = true;
  },

  enableSingleSubviewPanelAnimations() {
    this._disableAnimations = false;
  },

  updateOverflowStatus() {
    let hasKids = this.overflowFixedList.hasChildNodes();
    if (hasKids && !this.navbar.hasAttribute("nonemptyoverflow")) {
      this.navbar.setAttribute("nonemptyoverflow", "true");
      this.overflowPanel.setAttribute("hasfixeditems", "true");
    } else if (!hasKids && this.navbar.hasAttribute("nonemptyoverflow")) {
      PanelMultiView.hidePopup(this.overflowPanel);
      this.overflowPanel.removeAttribute("hasfixeditems");
      this.navbar.removeAttribute("nonemptyoverflow");
    }
  },

  onWidgetAfterDOMChange(aNode, aNextNode, aContainer, aWasRemoval) {
    if (aContainer == this.overflowFixedList) {
      this.updateOverflowStatus();
    }
  },

  onAreaReset(aArea, aContainer) {
    if (aContainer == this.overflowFixedList) {
      this.updateOverflowStatus();
    }
  },

  /**
   * Sets the anchor node into the open or closed state, depending
   * on the state of the panel.
   */
  _updatePanelButton() {
    this.menuButton.open = this.panel.state == "open" ||
                           this.panel.state == "showing";
  },

  _onHelpViewShow(aEvent) {
    // Call global menu setup function
    buildHelpMenu();

    let helpMenu = document.getElementById("menu_HelpPopup");
    let items = this.getElementsByTagName("vbox")[0];
    let attrs = ["oncommand", "onclick", "label", "key", "disabled"];
    let NSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";

    // Remove all buttons from the view
    while (items.firstChild) {
      items.firstChild.remove();
    }

    // Add the current set of menuitems of the Help menu to this view
    let menuItems = Array.prototype.slice.call(helpMenu.getElementsByTagName("menuitem"));
    let fragment = document.createDocumentFragment();
    for (let node of menuItems) {
      if (node.hidden)
        continue;
      let button = document.createElementNS(NSXUL, "toolbarbutton");
      // Copy specific attributes from a menuitem of the Help menu
      for (let attrName of attrs) {
        if (!node.hasAttribute(attrName))
          continue;
        button.setAttribute(attrName, node.getAttribute(attrName));
      }
      button.setAttribute("class", "subviewbutton");
      fragment.appendChild(button);
    }
    items.appendChild(fragment);
  },

  _updateQuitTooltip() {
    if (AppConstants.platform == "win") {
      return;
    }

    let tooltipId = AppConstants.platform == "macosx" ?
                    "quit-button.tooltiptext.mac" :
                    "quit-button.tooltiptext.linux2";

    let brands = Services.strings.createBundle("chrome://branding/locale/brand.properties");
    let stringArgs = [brands.GetStringFromName("brandShortName")];

    let key = document.getElementById("key_quitApplication");
    stringArgs.push(ShortcutUtils.prettifyShortcut(key));
    let tooltipString = CustomizableUI.getLocalizedProperty({x: tooltipId}, "x", stringArgs);
    let quitButton = document.getElementById("PanelUI-quit");
    quitButton.setAttribute("tooltiptext", tooltipString);
  },

  _overlayScrollListenerBoundFn: null,
  _overlayScrollListener(aMQL) {
    ScrollbarSampler.resetSystemScrollbarWidth();
    this._scrollWidth = null;
  },

  _hidePopup() {
    if (this.isNotificationPanelOpen) {
      this.notificationPanel.hidePopup();
    }
  },

  _updateNotifications(notificationsChanged) {
    let notifications = this._notifications;
    if (!notifications || !notifications.length) {
      if (notificationsChanged) {
        this._clearAllNotifications();
        this._hidePopup();
      }
      return;
    }

    if ((window.fullScreen && FullScreen.navToolboxHidden) || document.fullscreenElement) {
      this._hidePopup();
      return;
    }

    let doorhangers =
      notifications.filter(n => !n.dismissed && !n.options.badgeOnly);

    if (this.panel.state == "showing" || this.panel.state == "open") {
      // If the menu is already showing, then we need to dismiss all notifications
      // since we don't want their doorhangers competing for attention
      doorhangers.forEach(n => { n.dismissed = true; });
      this._hidePopup();
      this._clearBadge();
      if (!notifications[0].options.badgeOnly) {
        this._showBannerItem(notifications[0]);
      }
    } else if (doorhangers.length > 0) {
      // Only show the doorhanger if the window is focused and not fullscreen
      if ((window.fullScreen && this.autoHideToolbarInFullScreen) || Services.focus.activeWindow !== window) {
        this._hidePopup();
        this._showBadge(doorhangers[0]);
        this._showBannerItem(doorhangers[0]);
      } else {
        this._clearBadge();
        this._showNotificationPanel(doorhangers[0]);
      }
    } else {
      this._hidePopup();
      this._showBadge(notifications[0]);
      this._showBannerItem(notifications[0]);
    }
  },

  _showNotificationPanel(notification) {
    this._refreshNotificationPanel(notification);

    if (this.isNotificationPanelOpen) {
      return;
    }

    if (notification.options.beforeShowDoorhanger) {
      notification.options.beforeShowDoorhanger(document);
    }

    let anchor = this._getPanelAnchor(this.menuButton);

    this.notificationPanel.hidden = false;
    this.notificationPanel.openPopup(anchor, "bottomcenter topright");
  },

  _clearNotificationPanel() {
    for (let popupnotification of this.notificationPanel.children) {
      popupnotification.hidden = true;
      popupnotification.notification = null;
    }
  },

  _clearAllNotifications() {
    this._clearNotificationPanel();
    this._clearBadge();
    this._clearBannerItem();
  },

  _refreshNotificationPanel(notification) {
    this._clearNotificationPanel();

    let popupnotificationID = this._getPopupId(notification);
    let popupnotification = document.getElementById(popupnotificationID);

    popupnotification.setAttribute("id", popupnotificationID);
    popupnotification.setAttribute("buttoncommand", "PanelUI._onNotificationButtonEvent(event, 'buttoncommand');");
    popupnotification.setAttribute("secondarybuttoncommand",
      "PanelUI._onNotificationButtonEvent(event, 'secondarybuttoncommand');");

    popupnotification.notification = notification;
    popupnotification.hidden = false;
  },

  _showBadge(notification) {
    let badgeStatus = this._getBadgeStatus(notification);
    this.menuButton.setAttribute("badge-status", badgeStatus);
  },

  // "Banner item" here refers to an item in the hamburger panel menu. They will
  // typically show up as a colored row in the panel.
  _showBannerItem(notification) {
    if (!this._panelBannerItem) {
      this._panelBannerItem = this.mainView.querySelector(".panel-banner-item");
    }
    let label = this._panelBannerItem.getAttribute("label-" + notification.id);
    // Ignore items we don't know about.
    if (!label) {
      return;
    }
    this._panelBannerItem.setAttribute("notificationid", notification.id);
    this._panelBannerItem.setAttribute("label", label);
    this._panelBannerItem.hidden = false;
    this._panelBannerItem.notification = notification;
  },

  _clearBadge() {
    this.menuButton.removeAttribute("badge-status");
  },

  _clearBannerItem() {
    if (this._panelBannerItem) {
      this._panelBannerItem.notification = null;
      this._panelBannerItem.hidden = true;
    }
  },

  _onNotificationButtonEvent(event, type) {
    let notificationEl = getNotificationFromElement(event.originalTarget);

    if (!notificationEl)
      throw "PanelUI._onNotificationButtonEvent: couldn't find notification element";

    if (!notificationEl.notification)
      throw "PanelUI._onNotificationButtonEvent: couldn't find notification";

    let notification = notificationEl.notification;

    if (type == "secondarybuttoncommand") {
      AppMenuNotifications.callSecondaryAction(window, notification);
    } else {
      AppMenuNotifications.callMainAction(window, notification, true);
    }
  },

  _onBannerItemSelected(event) {
    let target = event.originalTarget;
    if (!target.notification)
      throw "menucommand target has no associated action/notification";

    event.stopPropagation();
    AppMenuNotifications.callMainAction(window, target.notification, false);
  },

  _getPopupId(notification) { return "appMenu-" + notification.id + "-notification"; },

  _getBadgeStatus(notification) { return notification.id; },

  _getPanelAnchor(candidate) {
    let iconAnchor =
      document.getAnonymousElementByAttribute(candidate, "class",
                                              "toolbarbutton-badge-stack") ||
      document.getAnonymousElementByAttribute(candidate, "class",
                                              "toolbarbutton-icon");
    return iconAnchor || candidate;
  },

  _addedShortcuts: false,
  _ensureShortcutsShown() {
    if (this._addedShortcuts) {
      return;
    }
    this._addedShortcuts = true;
    for (let button of this.mainView.querySelectorAll("toolbarbutton[key]")) {
      let keyId = button.getAttribute("key");
      let key = document.getElementById(keyId);
      if (!key) {
        continue;
      }
      button.setAttribute("shortcut", ShortcutUtils.prettifyShortcut(key));
    }
  },
};

XPCOMUtils.defineConstant(this, "PanelUI", PanelUI);

/**
 * Gets the currently selected locale for display.
 * @return  the selected locale
 */
function getLocale() {
  return Services.locale.getAppLocaleAsLangTag();
}

function getNotificationFromElement(aElement) {
  // Need to find the associated notification object, which is a bit tricky
  // since it isn't associated with the element directly - this is kind of
  // gross and very dependent on the structure of the popupnotification
  // binding's content.
  let notificationEl;
  let parent = aElement;
  while (parent && (parent = aElement.ownerDocument.getBindingParent(parent))) {
    notificationEl = parent;
  }
  return notificationEl;
}
PK
!<u�W�119chrome/browser/content/browser/customizableui/toolbar.xml<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<bindings id="browserToolbarBindings"
          xmlns="http://www.mozilla.org/xbl"
          xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
          xmlns:xbl="http://www.mozilla.org/xbl">

  <binding id="toolbar">
    <implementation>
      <field name="overflowedDuringConstruction">null</field>

      <constructor><![CDATA[
          let scope = {};
          ChromeUtils.import("resource:///modules/CustomizableUI.jsm", scope);
          let CustomizableUI = scope.CustomizableUI;
          // Add an early overflow event listener that will mark if the
          // toolbar overflowed during construction.
          if (CustomizableUI.isAreaOverflowable(this.id)) {
            this.addEventListener("overflow", this);
            this.addEventListener("underflow", this);
          }

          // Searching for the toolbox palette in the toolbar binding because
          // toolbars are constructed first.
          let toolbox = this.toolbox;
          if (toolbox && !toolbox.palette) {
            for (let node of toolbox.children) {
              if (node.localName == "toolbarpalette") {
                // Hold on to the palette but remove it from the document.
                toolbox.palette = node;
                toolbox.removeChild(node);
                break;
              }
            }
          }

          // pass the current set of children for comparison with placements:
          let children = Array.from(this.childNodes)
                              .filter(node => node.getAttribute("skipintoolbarset") != "true" && node.id)
                              .map(node => node.id);
          CustomizableUI.registerToolbarNode(this, children);
      ]]></constructor>

      <method name="handleEvent">
        <parameter name="aEvent"/>
        <body><![CDATA[
          // Ignore overflow/underflow events from from nodes inside the toolbar.
          if (aEvent.target != this.customizationTarget) {
            return;
          }

          if (aEvent.type == "overflow" && aEvent.detail > 0) {
            if (this.overflowable && this.overflowable.initialized) {
              this.overflowable.onOverflow(aEvent);
            } else {
              this.overflowedDuringConstruction = aEvent;
            }
          } else if (aEvent.type == "underflow" && aEvent.detail > 0) {
            this.overflowedDuringConstruction = null;
          }
        ]]></body>
      </method>

      <method name="insertItem">
        <parameter name="aId"/>
        <parameter name="aBeforeElt"/>
        <parameter name="aWrapper"/>
        <body><![CDATA[
          if (aWrapper) {
            Cu.reportError("Can't insert " + aId + ": using insertItem " +
                           "no longer supports wrapper elements.");
            return null;
          }

          // Hack, the customizable UI code makes this be the last position
          let pos = null;
          if (aBeforeElt) {
            let beforeInfo = CustomizableUI.getPlacementOfWidget(aBeforeElt.id);
            if (beforeInfo.area != this.id) {
              Cu.reportError("Can't insert " + aId + " before " +
                             aBeforeElt.id + " which isn't in this area (" +
                             this.id + ").");
              return null;
            }
            pos = beforeInfo.position;
          }

          CustomizableUI.addWidgetToArea(aId, this.id, pos);
          return this.ownerDocument.getElementById(aId);
        ]]></body>
      </method>

      <property name="toolbarName"
                onget="return this.getAttribute('toolbarname');"
                onset="this.setAttribute('toolbarname', val); return val;"/>

      <property name="customizationTarget" readonly="true">
        <getter><![CDATA[
          if (this._customizationTarget)
            return this._customizationTarget;

          let id = this.getAttribute("customizationtarget");
          if (id)
            this._customizationTarget = document.getElementById(id);

          if (this._customizationTarget)
            this._customizationTarget.insertItem = this.insertItem.bind(this);
          else
            this._customizationTarget = this;

          return this._customizationTarget;
        ]]></getter>
      </property>

      <property name="toolbox" readonly="true">
        <getter><![CDATA[
          if (this._toolbox)
            return this._toolbox;

          let toolboxId = this.getAttribute("toolboxid");
          if (toolboxId) {
            let toolbox = document.getElementById(toolboxId);
            if (toolbox) {
              this._toolbox = toolbox;
            }
          }

          if (!this._toolbox && this.parentNode &&
              this.parentNode.localName == "toolbox") {
            this._toolbox = this.parentNode;
          }

          return this._toolbox;
        ]]></getter>
      </property>

      <property name="currentSet">
        <getter><![CDATA[
          let currentWidgets = new Set();
          for (let node of this.customizationTarget.children) {
            let realNode = node.localName == "toolbarpaletteitem" ? node.firstChild : node;
            if (realNode.getAttribute("skipintoolbarset") != "true") {
              currentWidgets.add(realNode.id);
            }
          }
          if (this.getAttribute("overflowing") == "true") {
            let overflowTarget = this.getAttribute("overflowtarget");
            let overflowList = this.ownerDocument.getElementById(overflowTarget);
            for (let node of overflowList.children) {
              let realNode = node.localName == "toolbarpaletteitem" ? node.firstChild : node;
              if (realNode.getAttribute("skipintoolbarset") != "true") {
                currentWidgets.add(realNode.id);
              }
            }
          }
          let orderedPlacements = CustomizableUI.getWidgetIdsInArea(this.id);
          return orderedPlacements.filter(w => currentWidgets.has(w)).join(",");
        ]]></getter>
        <setter><![CDATA[
          // Get list of new and old ids:
          let newVal = (val || "").split(",").filter(x => x);
          let oldIds = CustomizableUI.getWidgetIdsInArea(this.id);

          // Get a list of items only in the new list
          let newIds = newVal.filter(id => !oldIds.includes(id));
          CustomizableUI.beginBatchUpdate();
          try {
            for (let newId of newIds) {
              oldIds = CustomizableUI.getWidgetIdsInArea(this.id);
              let nextId = newId;
              let pos;
              do {
                // Get the next item
                nextId = newVal[newVal.indexOf(nextId) + 1];
                // Figure out where it is in the old list
                pos = oldIds.indexOf(nextId);
                // If it's not in the old list, repeat:
              } while (pos == -1 && nextId);
              if (pos == -1) {
                pos = null; // We didn't find anything, insert at the end
              }
              CustomizableUI.addWidgetToArea(newId, this.id, pos);
            }

            let currentIds = this.currentSet.split(",");
            let removedIds = currentIds.filter(id => !newIds.includes(id) && !newVal.includes(id));
            for (let removedId of removedIds) {
              CustomizableUI.removeWidgetFromArea(removedId);
            }
          } finally {
            CustomizableUI.endBatchUpdate();
          }
        ]]></setter>
      </property>


    </implementation>
  </binding>

  <binding id="toolbar-menubar-stub">
    <implementation>
      <property name="toolbox" readonly="true">
        <getter><![CDATA[
          if (this._toolbox)
            return this._toolbox;

          if (this.parentNode && this.parentNode.localName == "toolbox") {
            this._toolbox = this.parentNode;
          }

          return this._toolbox;
        ]]></getter>
      </property>
      <property name="currentSet" readonly="true">
        <getter><![CDATA[
          return this.getAttribute("defaultset");
        ]]></getter>
      </property>
      <method name="insertItem">
        <body><![CDATA[
          return null;
        ]]></body>
      </method>
    </implementation>
  </binding>

  <!-- The toolbar-menubar-autohide and toolbar-drag bindings are almost
       verbatim copies of their toolkit counterparts - they just inherit from
       the customizableui's toolbar binding instead of toolkit's. We're currently
       OK with the maintainance burden of having two copies of a binding, since
       the long term goal is to move the customization framework into toolkit. -->

  <binding id="toolbar-menubar-autohide"
           extends="chrome://browser/content/customizableui/toolbar.xml#toolbar">
    <implementation>
      <constructor>
        this._setInactive();
      </constructor>
      <destructor>
        this._setActive();
      </destructor>

      <field name="_inactiveTimeout">null</field>

      <field name="_contextMenuListener"><![CDATA[({
        toolbar: this,
        contextMenu: null,

        get active() {
          return !!this.contextMenu;
        },

        init(event) {
          let node = event.target;
          while (node != this.toolbar) {
            if (node.localName == "menupopup")
              return;
            node = node.parentNode;
          }

          let contextMenuId = this.toolbar.getAttribute("context");
          if (!contextMenuId)
            return;

          this.contextMenu = document.getElementById(contextMenuId);
          if (!this.contextMenu)
            return;

          this.contextMenu.addEventListener("popupshown", this);
          this.contextMenu.addEventListener("popuphiding", this);
          this.toolbar.addEventListener("mousemove", this);
        },
        handleEvent(event) {
          switch (event.type) {
            case "popupshown":
              this.toolbar.removeEventListener("mousemove", this);
              break;
            case "popuphiding":
            case "mousemove":
              this.toolbar._setInactiveAsync();
              this.toolbar.removeEventListener("mousemove", this);
              this.contextMenu.removeEventListener("popuphiding", this);
              this.contextMenu.removeEventListener("popupshown", this);
              this.contextMenu = null;
              break;
          }
        }
      })]]></field>

      <method name="_setInactive">
        <body><![CDATA[
          this.setAttribute("inactive", "true");
        ]]></body>
      </method>

      <method name="_setInactiveAsync">
        <body><![CDATA[
          this._inactiveTimeout = setTimeout(function(self) {
            if (self.getAttribute("autohide") == "true") {
              self._inactiveTimeout = null;
              self._setInactive();
            }
          }, 0, this);
        ]]></body>
      </method>

      <method name="_setActive">
        <body><![CDATA[
          if (this._inactiveTimeout) {
            clearTimeout(this._inactiveTimeout);
            this._inactiveTimeout = null;
          }
          this.removeAttribute("inactive");
        ]]></body>
      </method>
    </implementation>

    <handlers>
      <handler event="DOMMenuBarActive"     action="this._setActive();"/>
      <handler event="popupshowing"         action="this._setActive();"/>
      <handler event="mousedown" button="2" action="this._contextMenuListener.init(event);"/>
      <handler event="DOMMenuBarInactive"><![CDATA[
        if (!this._contextMenuListener.active)
          this._setInactiveAsync();
      ]]></handler>
    </handlers>
  </binding>

  <binding id="toolbar-drag"
           extends="chrome://browser/content/customizableui/toolbar.xml#toolbar">
    <implementation>
      <field name="_dragBindingAlive">true</field>
      <constructor><![CDATA[
        if (!this._draggableStarted) {
          this._draggableStarted = true;
          try {
            let tmp = {};
            ChromeUtils.import("resource://gre/modules/WindowDraggingUtils.jsm", tmp);
            let draggableThis = new tmp.WindowDraggingElement(this);
            draggableThis.mouseDownCheck = function(e) {
              return this._dragBindingAlive;
            };
          } catch (e) {}
        }
      ]]></constructor>
    </implementation>
  </binding>
</bindings>
PK
!<b��&&5chrome/browser/content/browser/default-theme-icon.svg<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
  <path fill="#202340" d="M2 2h14v13H2z"/>
  <path fill="#f9f9fa" d="M16 2v13H2v15h28V2H16z"/>
  <rect x="1" y="1" width="30" height="30" rx="2" ry="2" fill="none" stroke="#08091a" stroke-opacity=".35" stroke-width="2"/>
  <circle cx="9.5" cy="22.5" r="6" fill="#fff" stroke="#adadb3"/>
  <path d="M12.5 22H7.707l2.146-2.146a.5.5 0 0 0-.707-.707l-3 3a.5.5 0 0 0 0 .708l3 3a.5.5 0 1 0 .707-.707L7.707 23H12.5a.5.5 0 0 0 0-1z" fill="#0c0c0d" fill-opacity=".8"/>
  <path d="M20.5 20h4a.5.5 0 0 0 0-1h-4a.5.5 0 0 0 0 1zm4 2h-4a.5.5 0 0 0 0 1h4a.5.5 0 0 0 0-1zm0 3h-4a.5.5 0 0 0 0 1h4a.5.5 0 0 0 0-1z" fill="#0c0c0d" fill-opacity=".8"/>
  <path fill="#0a84ff" d="M16 2h14v1H16z"/>
  <path d="M26.354 8.646l-3.5-3.5a.5.5 0 0 0-.707 0l-3.5 3.5a.5.5 0 0 0 .707.707L20 8.707V11.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5V8.707l.646.646a.5.5 0 1 0 .707-.707zM24 11h-1V9h-1v2h-1V7.707l1.5-1.5 1.5 1.5z" fill="#0c0c0d" fill-opacity=".8"/>
  <path fill="#08091a" d="M15 2v12H2v1h14V2h-1z"/>
</svg>PK
!<�=\��9chrome/browser/content/browser/defaultthemes/1.header.jpg���JFIFdd��DuckyU��Adobed����






���	����		!1A"Qaq�2#�B���$��R3��b	C%r4�Sc56!1A�Qaq��"������2B#Rbr3��C��?���Ҥ�Ǘ�(�"���6�r���˭j�Er���\���.���r�r�6��\�E��.5��\����.]F���4��ۍr���)�((�m�����]��r�˵˗k�.�.Q��.]Q�\��Er�i�.PJ��._Q�\�妹r��ᶹr���˯JҚ�ˮ_����j���.\JץN��.�|6�\��h.SU�,�uSAr�T]r��k�.z�r�ŗ�5˗_����*�T_����%�zltW(�|7�.]X�P�\�B����k�)��k�.-5˗�5˔�H�m�\��G��r�u˗\���)��.��=u˗
k�.�5˗m�\�uW\�v�\�M�r�r���˵˔TV����zk�.�\�\�v���m'E]C��\�E���5���r�O�\�m:�˨�+�Z��˔���˨�r�P]r��q�.Sk|u˔Q��\�T�]ƹr�7Z�\����q��"�}r��\�u����\�}r
7�"�z�.]�z�˵˔m�\���\�n��˪�.]��.U,�)�\��Mr�%�\�EP��˃���u˗^�T
r���\�H#�\�Sm�n_�Ar�T�h�]T�i��uW�
r�ŀ�-r�į��.]U�Mr���mr�ĭ:
r��T�P�\����.]T;�*?��.PdK�J�r�7!���r��5˗��\�2�5˔�kJk�.,�r�Tk�)u˗Uz�r���˶�5˗l���r�S�\�mr�#sA�\�}r���˵˔���.\:��w:��+��.S�\���˗��4W.�\�A�\�\�N�r�(k��w�\�v��Er��\��\��M5˔P��Z��hk��.]C�\���\�;�Ar�_Mr*h=r�:��޺��h@�"�M��]k]˨GS�Ar�X�u�n��F��M6�.]i>��˭mr��:�˨~:�ˀ?r�ֶ�r�6�r��\��\�@�\�v��Wh.]�]˽+頹v��zh.S��CW堹PM6��A�eee#��h������nZT�r�-+Mr�7/P5˔^���r�:
r� ���\�uEu˔ܺ��(�.]U�k�(�~��C)=5˗��\���k�.�-r�5���r딍���r�Ev�ˉ�m�\��i�\��w���\�2Wa�\��o�\�0#q��r떴��.\��.R�k�.۠���.S��r�h\�N��.�\�u�r�i�\��]r��\�N�\�v����r��>�ӳ���o�\�[a�\�����SM�htPPAt.��SC�Au\�E	�"����Si��uֱ�.u¾�Ar�Z��\�t(���+���r�ֶ�r�O�\��P�.!�˗Q�k�:�6�r�r��:��#\��P�.�k�\7�\�֞�+�k�(4��\�\�F�.UyU��|�	\�H��|����]ˮ�\�u��.�=u˔P*@�\�u�GA�\�?��_�S��m��~�\9y�ʊ7��5�C.4o$Sݤt`�T�mV��$@��0��c00�F�
r� *tR��u˔\�Ӧ�r�$H*Ԧ��e��s<��y�:��n0��r�0bAst^��S�+���8�J�'���'��[�"�*Fe�/�^9B��L�ፈ�M�[M�MNY�n@�+L;9J���9���2~�c�|>�k
�א���rE{<4|��Շ�#��{�u'�Yh��Շ�^3���_�[�ɉ�^��=��.g�Ɋ:lFD|:����H*
���q�?�o�G��|W������,�Y�׏�x?h�=����
K~���%��,��c�'�~���ӏ%�_����<D|�%�˚N>T�$�Y\C��?�G#$�v���'�,R�e��K���3|���n�;;���*32�\F5?�)Ӊ��\'�pG����yP�q���ŕ%���^2T�GDML�!=�F�P�.�@���Mr��/R7�.]rS�������r��z��r��+�\���Mr���mr�"�}5˗j��.\5˗l?
r��k�.��#v�:�h�WP=:m�\����^��.R�I'\*��c�?�L�:�%
)�Ewh�z�,��Z+O�_��s���_���r��m�\����\�������\��Os��ʠkh����!�ۦ�.P��h���j����d]IE\P���
r�f|��5̹��NǦ���P�6P}޺�E�]�B�'zPu�\�C\�n�\˝r�J����r�ڵ�&��2+�ܭ
s �����+���
��(YG_O��:,��SU@}��̢)I�����Rv����{��u˄�h��tW!� ݣ5q��+�WJQ
�J�*O����R�*�޽H��VK��ROZקM�ZFm��k�*�N��OMs"������T���J��B�v�.�R^���[a��,����O�k�s)m_�W\�q�Y�T��)�s�U_[='Ar����GB6�\��8�
w��@uPU�>�|4.%���'��Mr�!?m�;�$tв,�3#\���9@# V�O�}�΂�����4�COᠹ\d)!����\
�dQ�h޿-�P]ܪ�?�u˔U�}�)��r�*X{�`i�\��EOZ���
r�j�i]��:��E�j�i��r��Z�c]����J�=
G�h��MM�?碹Az=����WAq�u�AJ����� ���)�kC_J�:�T�S���&B��v^�ԓ�v�ə^�
(O�4�N�\�Z��6�.S`_�� ���]�)�:�˂��?����\�s%��m�e�J�S��r����頹_���dA�j���\�,��(}�.�����uԧO�
r�%J�z��\��ZC�\�V�va�\�%E?�k�(4.��\�ڀM7�\����?=�0\7Jo�����Z������2��+�:�\���+�k�PI;��d]R�����\˗T�h�j��П���*ԓF��Ar�;����hM�SmW]��u?
˃=w�� �^��o��\�>���\ʲKR-�S�h:��U[���Er�.�oM�)��Z#����k�ː��Ih��J�+��!��@ݎ�q���AI��Ei�A�딇5��'Ar��ScZ��6�|}t(1���+��MEA����{��.Sޢ+P��m�uˌ���O����̤�\���ZW�]s��!������
GI�:����`w���[�j�u���f{�q�@�O�\�RV��w�zk��2�z�զ�+�C�1�4�Y�J�J�@>�uȫ��Hc�@ߦ��ex�B�Uv'}2
�M�sC��:�\M�
{��\���iN���+^(C�i�WE�(�@R�U�m�CAr��z��:�\�V���ߦ�r��(EOM��:*n�}=tPPI%�
��
r�1��/��\U���Қ()4Zo��UXC�A�޿
�U������S@OQ���
�Kp
*OBi�t\B `�Q���L��e=7��.R�q�W�\⤁R��+�V"�C���Xnv�@�W(&�zr�4��c�\�)�����\����\�TU��u�����4BZ�O㢹p��th��
�v��A�*�!Hki�堋�*W�z���u$Z*1��@�pw�tUbQF���ตs���L�:��Pu�2�U �@*�p?=*��#P�R�j�I?�:,��E]��
%J�I*7#E�u���\
�	�����d]M�:�:,����\�DMZ��.u%:C�eΠ�=}ts�t����H������k��=\� T��u˗*���#ms.u��������J�?᠊�P�����˔V:��h�\���Ar�:���%��Ju=]�W+TЂ7����.Tj�!zҿ��*w��o��\�RiQ#,ƃ�|5�\�V����I��u�5-oV��=iO]0	J�=��M7�=Wڭ�+�4�.U2z�S�h"���u뢹W�lc!�R6�+��BPӯ�Er�(op�?����Ɔ�	i�:(+1\
Th���H���|�./Z�=N�*��#}����\���j>�J5�u����N��W*�
›�u˔Yh�����QM�Z!�h���.R\����J{G��zh.S�����,�Q���{u:�3KSs��tP��$zu>�~Z��ךԑ�A�\��Am-��i��]�%}�(�k�E�PϾ��w�\�7,ֶ�aS�4VbXvn�龊��F�w�k�R@Z�A�\�������P��#���3�}ֻPk�\���\�n����
���;Sc���ˈ܁���\���Ndh��p+V\˝0�i��k�s�:���7��JC&uq$e�?�Z\EJҵ�4�U�oMr
~:`�4��AԈ��2�\P����s�v�FW �kmƣ֚����h��۵o��2��J��s.uB�z
�?�J��ヒ.u6��.P#-�˝J�z�F�:����E�v�'�Ms.uR��ޛh.uc����u0��2.����=��k�.1�
6�]΢���M>:�ˬ'mr�Sj���㠊�ۺ����p��T�C��ER9D�%@@�0*�i�_��MAC�i?�B.�^�V���j@�~Z,��$�"z~�~{�59T�
��6���d��%�]��➵��!UJ����t*;����
r+��c�ޕ�\��0"��0�?-�{�I�ڃj`�ɖ�
����n3#m&E\Ko�ө��M)��e�+O��*��`�4�4DU|���=���˧��g��'{Ǹ��3��Cp���we);��NkF.�S,������:�O
�<K��G��7��9������Bk�E���P�Fj
c��r��/S`�>���|�~'��1��=�&VlNw�L�g���H������TP6[E�2����9X-��!H���.k�lH�r��|�D�&8�r�ȞvRX�$q2'�����hR:)_4ce�8�+�?��e���o�M��dyی\����ep�p�1Y�H���7���]��$�(jm���sǒ��b�X�����g}����_�����0��(aq��}<�cM?1���G_w��,Zn}�ss?����v�ip������\.��_�����Rh��S;���pL�Y&��2'�l7�:�ô��O������ �/�pj��11�y\��p�e���:�N��U-���-hJ�mSa+ʋ��p�Ŀ���䠩&F̌t��F��:# +���������8X'���
;abxɟ3�@����GXNFZ�0�;�U�c	�{*�;���?�S���U�&�ω���9�8��q����`�`���G�����sD�>�V}�%�o�ܷ�/�Y������h��{�_�䣊�L�&$Kcorx�j��K�~eP	�G�?
:���]Bv�]�GR\�OQ��.V����)W&���:+���v�p���6���V�ա�]-s.T.@��7�k�D�zt?"u�(V��ۯ�Aom�4��蠸��z7�u˔Un����-Skj��7?��)
���m��A�w�h�qЏ�w��Th:*Z7��U\���
� �g�z��L��	kV�t��h5˝IKO���~4Ѳ�Pb�_]���4\걡}��BM:R�u��ˣU~����?��s����0T�t�h��V1����wc��A����@o�}\�V#v�?�eΨ�A�hO��JC&uh~ѽ)���Y)+�����|��L��v�K=CV��h2.��R��MJ�eAh�G=()]r*��B����_Z�zm�!s��l��Z��o���Ҳ.���s
���O��r���O����mE�%fR*/Z�����Қ"�
֦ڎ��Jh���H���S�}6�RR��ߨ�֔�!<��RЩ��j�m�~YQ��rd%�PM��] �b��+(j�QO���%	gicjR��T��&S�CV���o��$��tQ*H����Z�f�d%4U"�EA;�=G�C.up��U ��
ҵ::.V��T^�j����*�d��45��m9@"����+����ڹ�4��� 
��?-q\�
�����ӯ�4�T�*�ֽ:G�ԟZ����@D�R�ﰥ�u��]�$������+�^��o��Z�J1����p��'1j�����u?�
EN�ҫ�2�amǠ�75�
+�tE���R�iu
v���Uh�J)�l�l()]s:�\P�HIE��0~z�:��U�X��@A
iS*Lĉ%��h���˕ˀT��n���i�Ub\0�������k�!tohg�� �KJ���.V�p�y�r	�����+�af�TbH�b�$�ڿ��2u�2e��h~}zWN�to$�h�ԀZ�����IX4����"�U�
�ir��ޔ��xؗaM�(ο���.���H$�C鶔�·ی��:��PGM(�������M>�DU���G�7Fm�#jWD �I�N�z����4B��>����Z���-��9�kP7$���@�F����m�Z��Ң�D�;�-�OP:)��itELR�K_��Q��ˈt��@�Ѿޢ�R���I-�\�YmP�S�ʕ� �]��*+O�s�ZR�b=в�!&�=((��Tt޿
3��X�`��C=EiS�J�ʬ4�z��
�nۉL��0�t"���t���)���,lEƝ��|�����m��ԍΨ�T��~��["��EV`\�QҾ��r�V��)�T���D�Φ5SDm�T�G�D J�[ݴ����̹�4d{�,����2�U�"��i��v�\Ȃ�c�$��v�ҋ�&�����Ui��A�lA@
�
��\�UT�~z�:�J�;u�;
\��,q^�'c��:�ʅZ��mZT�mE�l�'�-5ݺテ.u��M��^��Ά�Ȣ�]�1E��{��-ߡ�j��J*�{���`j?������e)[���Ms"�KoyNߨJWmR�Jfk�j-�T
���M!�aDKѡ��h"���D�(�D�M�~h�3.f@(�?��+Ӯ�k�ȩ�ShB䍫S��T�1���u�>�º
D]Dr8�p�T6�*�W+&P�h�'��賕ȩ4T%O����4�r̲�N��>:뮲����f�~Tۯ�]�r��Z� 8Z�=��АD!���
IjSr(	�M%6���f��֕�s#ۊ�1����*W#2<t>����S�	�Z<��3��
EI���A�)�h�u:|v?]	\�4p�>��_�C���^IC�i4��N��J��Eeo�N�	��ᣵs��H)9��%��P>�Hd]��*ҋX������@�WFYf(�J(�{�k�+�l���$�8ujU�.�To�oJ�2.�����ւ��~:��O��MT�r�I܂H���"�fؐK!���q�p���Ԅ�T�PF�
$E���M��:`%VC��J�z~Y
�Ӹ#f�I���A� TW�ũ�8Ee�rU��a�#�gT���$�޽O�\�r��R�77�����ƚ�p����(�
�j�>`�VV-���#sQMr�bE�X6�N�b� �$�1�ƣj��M#�AɌ2��"�-h�m�?��	�C��d=��U: ��қ���U�	�]���M)
�=�T���J�>:`��td5���@���M�R�B��֌�S��%�
������%pRYI�5�ӧO��\�YD�A�)C���D:���z��:��@
ߥH=GC��ʲ���Z��MF��DUc�\��JҢ��h�.U�����6�҃`kO�2<�+�l'(�@6�?�Y��A�I����[�T(����G���+�eXAi
|�=+�j�W�X�����ku���IE����S�(:�(g,HR�چ��]"vn�H�������A���Axbz�����)�����	�_��+�H�{kV����T1[ɯP6iHEՄ_@���tZ�:�D��@:�f\�lC
W�mu�P#�jU�o��:�E�TV:8�T����B���jh�u��#_�4@\J�qm�|5� J�'��o�F�.t4I�(Եz�Z��tt���N:�ο��nR��R�
�N�ĮH
F�$N�4Ys���Im���:����*c�in.W��T������k0��۽+�B`�QN�֤P�[h2�Y/i{j:�Jz|�ȡ�D��֠���h3.P��u�Wp(EF�~#Erd�^�*��J�ȒM��{H��
;��ʠܭ�zm��*H,Q-+Oi�]q�.�sO��s*�RLMijT��
u�R��X�o�+AO���uU��ĝ��Zu��4,�Y�k2@#!
�wB�Z�:M�FN�rB�����v��eE��w�-;����¢*m�V�m��ː�%d�P8���F�
t��t�ec,�
R�u���)���,���n�z�=5¡r��Z3����i�"��eq_�WoO�NR��@	,j�m�QT2��7�niM+�\�5$QWU��
��\Ȯ�N�j�6�o�=�@��"�n�::�h~|�ђ����^����.uU,"g
Q���(�VI�PZE��~zRQZ<�̆ n ��4�DYlgPB�6�=� ���V������MM�����L�&��X���v�Є�SL�����EF��g0"�,����
I�WE�qrp�-����
���"���
�0lF�Z�\2

���:��O����Z��
@���d�o%��$$U[o�Ҕ�KH��23
���w;�:�Vs+35���oSO���D�<P��j�����*��,z�{������~:k��4�
�n����ֺ�)����4
��M�="˙L�-�%��t��z��D��VT4W4bi�=4AAYA��v��:
%HYI6�������(p@U���-6�ʐd�(���N�
�Z�	"B*�h�~]w�A����V����4G������$$�Ո�	C�2��O����>�YAVX�Cj�j.ޞ�,���})���E-`�
	$M�PuQ
�
zz�~ZPuu�\A�DSL�:�P��(ۚh�.\c+N���W\PuWB��_`4&��:,�SC֛u�:�ݳ@i����s����ݾ��4H\�ݢ�&�J����(E��ֻA��AR��/��>?
��ܣ��+�v]�s��0��[��:f\�2H��ϯ�5�.tF;),�l����7;i"�$L^�bn���L˝bW�V�� �dU�B�d�ҧ�\��uW}��w鿦�"�D�#�
ҧm��N�*�?��?��%p
�dE!'��f��c�:`P!J=��R��ߦ�\�Uoܰ�|��d	Yc=�(+�t���2��
�u����P�BՆ�=>�S30J.�A���BRL�X��m�Ji�ڛ���0��Le��[�:A-S�ӗ`����5������o&B@�����W*���G!cY� |w�ܹ�!�� �A?��ҵ�Ey���s��`�I62�FL�>�>�wVrRu,��U��Ĩ�0�hz�=ht�S��|�ʸ��|�ɳ!������͕!�"N��,�TTPK4T�2�+��������4�p�����v�3r��,�o�x���F��y�&F#�!��^��g��;n�ut__
9m���nr�$�+�2d�?vA�~_�,�2����H�2����ʤ�N����q�€-^9W��\A�#�1�3�TA�
�Xa�E.����!-j�+E���	�Y�Y��>W��q�&G%��u�'+��$��&̒��D2���)��ь��iŸ��̡�.W��g~M��M���ˌ����x�n6��NRK{B��U�	TiKȉWу��R���۝V�D3A�/����ۯ�<Vg�j`G��')�?ri��˕V6�y�I${QV�ƀ6^�1�<��<�\�NB��
��zi�T�x��v���/�q>=��/:@px��eS�Z�osv�q�Q�tRT�������b���_�Kn�����@��X�{���b
�'�qT��_ݯ�d��/�bd�9�d<̼ZK���B��>���N��@�"*�F{ZS��k�����d'���^�I/��8����7��p���,GCѨ�ȫ'�P�LX�F���z�ح�'ğ�9?�Nb^�D�|m�g�p�IsŒ��N���U%r02�W �Ѩɫ�K}��4�bZA{��?��A�//#�~���p����q��L�#d��#�N�4����h�k(�eV��y(d�/�O܏
�?��9<\�;��rx̬Y�<�^#2�.�VE)V��)���!�Bқ�������_�F�6�np��I�P~�.�
=4�W<�=�[�w��"�� �GD�R�z�>�h�)WvE��J���ڣ}�V2]�;�v��t��$���@{�m�)��!d��ڧ�i@~��J�νdžf�	��M_Q�
j����H��
��MqA��l�7o���νt��2ښ6ATZЀ+�(hI��P����ľ��ނ�,�\�`�P�j7�M
"�<r�;�AJ���RT�YHZ���׭~ZfAТL"��)��q���jH�w5�8�ctjC�,/�UR(
z�a$��r?�֭N�t�?
�Hb�i�D�-�ե��h�ˁV���������%Yq%���Aڇ�	ښ;(����n��:��m�t���ԼT%���
��jk�\�A�a�'�P5�.C�?�`X�[T�z
����+��v5�Q�lM6��:���P}İV$�]���UX�]��ij~]� ĩ\p�%�Nԩ롶��r��a����:k�r�5����ߘ�(���*P��z�Q�]���j�܉��K���I4��DH���
!$(��
�]�	�DS ��Ԇ��i@*�]���U�@�XM�ЃAF��5�qVrΊ^��X���P�Pi�t<�xx��D(7�Z�>g�u\lJ�_"�̚f�f�LR�/d��J*� 1��G�ʻZ0���'!@]�	ՄAS%tN�.񠡦�'M�D���ݐ��@ ���@}��=t�]��2��ҵW��j������\Υ��m���PSm�X �\�d�X�B
�+�>�F�ES��(���愝�-�	�F(�F�3�=�M»O�?�� �I�]�)+�f;
ݰ���3��j�Z�I?�rBP$��wV�'����L��ga$�T��P7$�O�Jj�Uu�!^?taU�SֻT~	E{���ң`	���t
�&+�j\ʆ�?����.	l�Z'�#2�]�6�ޣ�>�YA�0.��D�N��� �/��V��4&�YX��>�4~��7�~���A\�$��ٖ��o@n���Uq]��b�B�e$2�I5��GU�r��Y"p�ݾ�)�5Ҡ�U)��{D�{�J�kMHS�Q
\��L�h��{���b6�4a]s �Ѳa��ۃ[@G�jS�끪�Y���G*��$t[���~?ᮒ!t�ر�=ūjEk�j��U�W���у�����)R*:uห��(���E�H��4ю�	�9i�
���������!*DFi�c�$I
V5��oP�Ս����%JD�f`!�AM�W����4LA��}��m�mOM+0E��U��ޣ�6��L�n�rD��6�t�.1C�I	hW�5���ML�8��#c�b�,@��.bjU�iDY�%�<�e�{UiH;�V����u]Mq�ɬv��)Ў��i�T�[��
��7�!�F�!�LlX;`*\�Đޟ����@�p�b�9+[$q��[E-$�Db��� �s��nJ<�-��9yH1����
e��u޺H�Sj�m�Hk-M	!T
�Z�k���"��d�-�7��2;�"@e��5+w�hH4�Z�Q%F(?I���H�2V$�#s��4�޴��"*�{.�X����JƮ.@h}�Њ��w��:����}�Zj
�4�r���r��q�ZWAܢ��-$�z�����@�qm�-Y T���t�WUK�Q���L̟J��!*ă�R	4�z��H5t�tR,��H��~�K�(�
u�c�AX�$��h6�?��1"넝Bc3�X0�h
A���(�P��,���}���#��_]p��O�f"&b7;��;|4�R�]&$��a�`GS�>��l������"�j}�f�U#P�)�Aҵ���U&@��-
w��4D)� nd����"6��t� 뤄�M��5��P.��Ґ�I#��7�G»��S\Q� �GB
v5���E:�#
��Sҟ��A�s��Q��3B}E>Z+��P#`��`��"�z|4�T(�U���۸����,p��b�P7&�\Eh�RXc��I+�Si S��i�ػr�5��mBE��A�އA��E�R��ۨ�������)Tx�Ec�M ;PS_���$AX��c�O'10cgbHZ"���&���,�"$�F(��:�9�7ܶ�s�7Ѩ=@�ы.4�fPP,��E��z�Z��BH
�yX�6�1c֛|��L�AQ�F�n�Q�	S��O��oO��T�$R�aF&E�\��=}5��K��8�i���R��
j:|
	ȅ���)u$�
�ߦ�QL�ƿϱ��Xh���m�`̕і�
����6���T�ѱ�q�75�t M��4ˆ�Y�Gj�EH�_Q鶁4A
������C�ZZ|����U��Lr6�[r(*�?+�J�vL`�u��â֟
p"��0ԒAZ0"�j�WL,�T	`�c�{�-%��&�L
YRVʯy��{6�+t�ը�:h'p�y7�v
��?�����$�D͈���@
K1�w#a�:��Y�B�:4��������N�[i�<r��V��Em__������~�(i���`�:�I��C�g�aB��{��@�U)�^�Z
-M5EQ�V&f.„�����`�Sj����R�{9)�%�=���ƻm��8d�n]� ��S�Ԛm��t�vMD[�2T�8P�w'қ�\�r��"�b���,(j��n�:��l��jJM@�;t�t��JJ;ک�j��*�V"���J
��G�i$B�wb�=U�U��+�<R���(R�r�H!��ۨ��M5�) PP��h���$D��}���M�S��֤�=zz��QtAd#��9o=�h�B�П��]&ʧ�E1�o�Q
#&2�4`޷z�\Ũ��yqԇ�0kX�hi]�Ae�N�g�*������C�o��BQ���ƒ�� �Z�@5Wt��o��-C2�P�}?=L&+�9�<Wo#�B#��?7"u�uU�tPI�N�D��[��'VL|��?��*G2UM
��
����&��<�?JdJ,�epH#��Һ���x�~,�����_`($�]W���$�)�cH�#�b�M+���ֈ���F�(�!�.�i�6��!(%QF9cmm�W~��tMT1$��\nhH���:e8�x�n��z�m�D.*��w��!�M
hiO���B��2�@ȠkQK�M0���/4�@�E,�pj�N�֚�)0*�:�aPjAGfW�6��m��)��v��K�����W��EX̔�f�����mp�r��C˚<x�!��"�V��gfѿši���֊��ؑ���g,�oV&��.�R2;�^<k�v2�1�;
R"���	OJV��cJ
i��\k3f�a'��ѓ��ֿ
�t�R��L^�Zww)]�}G�t�P��(�.���awO�������(�J%�HK5��]�G�G���1��Q|)+8��e�#j���b@\��^�p��R�B����w�N�hN[b�
�L8$�C���B=]���2*�Q�A6H�қ���E�@
��*w;�J��CAV�u�A�hnE���v�����]*�sH�܄��i�$m�jf@&V�5���q�h+���� �h臸��:V�b�.�d�R+��4�[� ���?p�;lON���4D�.e7z����A=vۧ��eΥ�qB�M�)?��hʁpU�5��QA޻��t�D�<Ơ��h�ԍ��$�ХXْ�/5!�����뤓�N7iU�� ����|ь��N�$(���r��c!m[�Ҕ'o�u�"���?Ɲ=iO��E�q"��A4�-�&�\n�
�VJ�n7����]e1ƉujȠ�V��i�%!�;}���T�fݍj*@t��K�*XbR�}�S�cO�����M�A����n��m�%�+6^,�V7��+�4�>bB*��4�2U�X���N��B1��1�22љn�Szmצ�(���S b���a�m�G_��A�4y1���
:R���G����xɘ/x5��V��]����(���Ǝ2rR���COʿ��P��L�2��R�AJ�M	I�50�2��� ٚ�P��MN2](��U�s�w��{����$R��u���BRhU��1f�x�b`�R���eS�I �E���F�X�.�;�����}4�K��۵%$�E���iZ����\�qM�S���E}u9��Q�(xVTv�Ku�QQB��+�y�������J��J�uBˠ��E`��m����Zk�SD
*�eW77�#qm6��:
f��/h��Rj
i]��tb˂L��B���Ů�$t��t��Mus7g�kz��*o�ںs@�ʦTIy-
\�����4�f /��`��M~:���*�0!#t�m�+�ڕ�}�PK�>&<ǻ��3m,ub	r����B&�8r�|W��"��UdVU�z�A-8"�j�2[f@@�Ň�
�M�
�Dތ ��6��v�����	+C�}�N¯�G�i�J +��Ȏ�*�4����R������˼}h:T��������!I
jjOC�Jz�NR�X�}��7S����8�Y�$�+��(��c���T_��_y��OO�HhYgVʶ,��M+�V�l?����t,(���W�E�5�Ez|H:4D��0�Z���}N�jҞ�;Pt1�]C�}�����~gS1�`Q�,��
U������`�+���M��H!�	�?���dp*�,�Ԓ����Sz��p�EN�D����;j����+v�����
o붹�t8���٘֞������]�8������2�#~��2�>�%����3��WZoo�ժ)OJ
0�R��d���J��^��)8Y�d �R�^��:�ES��2)'�Q�4�ExS<�L��8�Q�{��hT�=A�ʚ�)��Ⱥh��&�OJ�ޟ�@��ܑEt�0z������h�ꮪ�����j��ӄ�@A&�5*=k�\+H�r
*�$V�]���C�)��(�;u�o�q�7DTTy�{TT�Uھ�z�	Wh�iT�N� �צ�U\��1p{����me���9,"��|AO]6�A�s���4�:��G�r�SV`Z���N�4YT����Z�C׭vv�J�'n(�JX	7�#��i�!T �8��s��'�������	 �1���
ѫ�j`��q	��4��~{i�N�BѰ��b$�
���4�DEIVk*Zi�������dO�P�w��Ȳ!R�e�o������ĆP�$�?��\ڮugBm��PXԅ��$W���R�v�W�P���:���L�*�Bt��%⴯��K�r�9�o<�����փ��\B��2	���2��ޠ��]s��.�h�!X�T�z[�O���2�Cln�3;����n�n�a ꇶ�`sb)�j
�А}(4�0tAJ�D��D�q�?���=NR�p�͑��)u�C��Z����@Y9,D6BGq�M?��뷁dv���x�."�$#��Ң�S�
H�D�0�$��H�B��hX]��ڵ����B�DJ���S����9"�l,+�x�~�'!�Q������,*c8���b�;R�BS#c��>�e�J�����{�}�������,�����#�خʷcȫR%G=����һh"��2��o��|9��;����[~�aș|w	5Jq�Y"�Ω1��h�k���f�L;�F��K���Jbx�!�M&6$��Y<m'/̰>�K���辬�)>��9��q�-s�Y7���C�N���Vy�yL#�'v��_ܟ�v�*Os�8��
E8^>0�l���d��(��x���k�=���m�y]h���5�>�y�dd�'�q�����+��$�x�lV	_&�,��'l�엥_M��D���K�b�}����x� r��7��CYY-�/�O��DQ����33Q:FY��R@>�<r���u�߶�k�?��(x��$g���v�Ά����c�Һx��y�+NI�)�U݅TWm�=71B�˹�p��I2�"���0�?�ԧR��Qk˓Y#��wo����Ӕ����Q p¢�]�uAU2��ݿ�^5��3��ydr��K�q`y�Ti;��h/Vc�P1
	c�	�ԯ��W�x�'�˹��<^A�^U�8^_��������">W(s`J�;PL\##K+�X��px/g������a��[��^7값s˚��x�a�1��hY�q� ǐ#�L�g������@����x$�o?��y8�*�|g���q�3gq�Y�����iW���(,�	F
�r�W|d&)P~��ŗ��2�%�/�j����q���$Rx�\����2y109I�q2����ˑ��G*{�V��3����{�#&�<��3�+�������<�7�}�俫�''+��y�
�e	��8誐��j��TI(.Hޠ��>c�y��XC�}c����R�a��և�t����'���9v�ذ��j^�TSd�M#�Y,Z��7��DT���~�ŏQZt�o�M�+��"��r����:FL�ҭ�^�l+J�c_��� 
�K��x��jJjN�ATƈY�&O6�0~�!e5��aB�����W@E�#Q���"����GhA�W
����S���+��B.W���`j�����0Hgc.V$�L��243<ږ8�B/GRM+B7�M@���<x����-I$�@�f'r>'M��ܜ��'m��*��m��:�Z1�JRW��&�X���v'���1t72�Z�n��ȥ���	?���5�D��]l�ҭi��G��i]�����pD0�X��-�cQ��^�sA�EF��
P����JB ���|K@���4c�"ʗOېڄ\��Њ�>Z�����GuaF��~�Oq�M�ѐ�kK���Jo�H-�'�F{���iC�#O�%�JJ��$L�!X��h���C˗��i'�ua,����M�k��)H��^
1 I�T�҄u�eġ±����)Z3�=Z~M��&C�)xX�nC^�����Wj�Ch"Ɣ�������M>#D���U��b�G�3�]������	A���:���^ݻ��hcOO��v�b�l�[8��$�S��dv�-�jT�A�t�L��$_��G	�***7�F�)�R�{�Mܟ���:fu�H*�rʊƮ��\�@z����QZs �*�.Rz���5P��*��n1�S&pa��T Q���C�m�����/ظ%�Zf�eD ��oؑN��Y��1�jU2dr�����I��c���E�ٳ����N��L!J�JUK4�=��i��!P��wcA�j"���U�DE?P�7qn��R���4d:�+�N�%4be��$P)��'S,�9W`8F�[Y�(��w=�M tB-�U��O �y;��
��%6)'�65��"��t�2)>�	z�?Q�zPi݂]U"��ʲ@�$�x�P�*TPz�}�\h��+�{�"�n��>;Tu�5X�S(�N�Ww!$Z@1��3�5��d6�ōܻ���%nNޞ�C�:!Ф�#$��q��V�|���L��d�)ŚF+@�	���7�E>B�5
g91�t�xB�s���V-��Oڥ�tl4%�+EI�?:���j���HsL�Tf>��kJt�O��f��
K*�"PUQ��I!�w��7hZː�VJdVf�BF��'r(��zo��cR���We#�WcJh���.e����`	�:A���� B�;��q�2)�������.W;���ڑ��LX0S��@��,2�a��Ub�bBE��� �9�F�$ok���&Š�1ܑ�:�*��.L�&�q�s����V�0�zR��obC�šY	����jP�4Ѓ�:����4F�m�{�r��5
�A��6��mCro�N_��n�Ǫ�,EP<MBQ���n- ҕ��V���ٺ��E�N��5P������Ӯ�`�I�"i�iuI���Ճ�IW�:��^0�!;BV܇7 p*�P6������Yq�Wi!y;P�Wpm^�~[W�]r�!��[LS(7;��*?
%s�t����!�V�T+mBM>\�"�]r��b�2e ��l����o�~4�΂���:�VDTb�V���Э�i���ce��N��n8G,�ZI�ZS��tB-�ϖ���TiF#�o��9(ѓ�:I��d
�T�O@~tփ:)�.G��\
�qH]����h�\��I#V(�K�H5����lGr�O�������1�0���ÍX�ܭ����AR7GsZ8��:u^�/[&*��E���+Mt���ȥ>�0�R�[�� �b����t��Yc�Bu����NO?.lV#+F2C��ʷ!�c�C����Y�r'c!���ޔ���B�rᕉf( >�=uR�Y1J�E�������ʻ��.��X`1��Ĕ����=@�T�G@f��j���N�@��lY<��dY>�Dqc��ݒ�@������u+M�i@�l���7�p(B�������>�x�))��q�p
�HPn؝��5�k7D�H�b2@�&��;�ƺ�t7:L$ݰ6z��6h�Y��Y1�+���n��E7j|����d��jQ�T��t�a�s��g�#(:��t��iv:mɑ��$�@`��uhą2U;�}�7���bOA��Pa��K�m��~ZV�A�6�~�bP��k��h�H�HĔ�Di�]-1��p%����Ռ
A%Nܩ���>��Mpw(�w�!�P[��ֻ�>�'_㠊��e��#�hn+BF��jSJHp��&�(�3U��pZq�i
BaE\�y'�č�V�ާ�ދ���E���!-Z��˦��5Q��|�scR�oJo_��Le�A��yr�L�cIJ3�w;�J�*(�:*N��h�U��I!�6q�(�)Id�tz "��t�U�&;���E
G�=�ꌕ-�q������*��Q^�s��&���4�I�HĨ�Ң��އ��D
�T�8�d�ܕAJ�;�Sm0ꃯ-��/-Ι0�_uj+G$d1�EMK�_A�3.=�)e���AL�1��b��
��;u��֝����dceG��<By%1o�v�RT�Z�BR�J֛SG,.�����[�{7f�n��AU&���J�4U��<��I3����v�o����QSS(n��qP�I
Z����B5D�	��%d8LKK���TX�@���Th�WŖ8��hZ��
Ěҧ��WK�Yq�6FKw��uf-�U��=?H���ӓR*�\We�Q5K]Z������?i��^VFUi������j�,�e�{�ѽT�ڟ�֤P|5"t�TI��qH��j*@
�\LQ��Dř��q�䪧�bA���Hjitê�q&Q���\��22�p;TPרҊ��h��������!Bj۵. �պ$F͌"Q��9��ժ(+�>Г"���\�Fʧ�Cn�S�GMOo4�rK�1�(�I{dR����V��]'nH�W�ɒe4u%?m��M*	~:X�Q�MJ~�$�Qb�
���㪚)�&T��Iv oS��m��5��D�.�T��"�"�1o��Z����Z�@!Dǿ2��+���S�PX��kZ� n@�Kf1����p�J��?�m$��(d�eio��~�D����(]+j��J2�f��`�Y;�֪=�m������bkD�DND�w�w$bsC�=v���J�ba��E"������_D�����MY)Z8Qf�K�.L���q��I$�Yhi���"Z�R$=dw�����{T+��S�f��Z\���:ɍ�R��A]��d���� ʢ�%Ȭ-�YO��w���VU��4�lK��zk�:8H��j5{UV%�w��#�M�(*�"gxb*2@�����bH4|t7[TR�K�$��RT�]�-QCJ���I�8uh� _G-u�J��OJ��}�z�`�UX��Lm9���C*EhG��MH�M��iCRY��z��W�$�vU���2�8R��m(=S��MCD���!���FX����A�I:V$&�Kʼg�<w��~Jb�|�2`��I�eR���*hǣ
�<BAtf�?���l���yrNy��KwV+�$h%g{�$
���#��}2�d�
�7:񱄕U�0�U-�������X���`��C�����3�<��ŏ��.<3�G�f*����P
��zi�V1k��ܶ�WfEU�!A'�"��?=i�uP%9�&,��@*U���$V��[#��]���e ��,�	�Mu+�*.2H�!X�)6�6�j?᤺k C݋ ��U�$�R��m܏��8�,S��v$����e�|p�E��坔{?���������pJW$c�蕢�@w�_M9�HD��JT��P�	�N����`�`Y
��I32R:�e�-��;O���	�4��`	;܂ؔu�ޤ�;kD�QFTFpȫJ5(W���뮙���;y{/DP������m6۱Cr��3��&���#�iI�5��	�6.5 !Q҇�A���A����q�asZ�łRk�Ň��!��%,�2�^A������m]��HK@�����ev�v*�̧�5�3:�P��,�Q���o�w҇��i���Xd�9%Ywޥ�w��%4�8��N�-�W`������'Wۢ�*N*J�5�T���M>zIA���@�l�Z�
�,n���|�ԉd�J1��H5�_��uV���	���l�퇼=�m��[�
�I$���$��ƾ���i�
J]i�)�@nE���t�@UșOl�1��P.�}k��j�r��2	�@��%t�\qs�m�ѸUə�{�{BP(	�z~:IL��T�3I;����EpjH��oJ�ē-WEG1����0ay�v��H �Z$I���'X㎭`�S���՜�)���y$jD�goOq��1+�Rw�ڔ���7b��-�hJbh���U��T�A'��E)�J|�C	.|a#���B�;�T�?���+� rX��e��b�Bn\pʦ`EmVj`z]���Q�Q�˖I2��`�F�ȍKSUF#aQ�I(ֈ���Ø@�C@�թ�����]	UR\H�õ
3�ڌ)p?�`��Aȍ1��	?�1zu��I �0.�����$*�K��u�h�qM3Eլ�q�~;��Hn�fc���UJ�l�G�ω�v�4�S d=�E�,���*�:������D_Q�ܹ�!B
�(*}t�b$,�!����Ԏ�`A*�m�F���y9+I��X����J�[Z7�`t���7�[
n�U�
L�w���+�m��I�4�kG��;�J��Q�|)]V!˩Ƞe@>�c`�M	}Š�����j鄕���K��R��¿-LG�3��'�����6�2�����i0�yV�8��KY5ޛnk�z���!�O&,���z���A�S�ۨ�� A���S�]2cw'R��;DUo�V����V�Y�`�;�M!�B��
�»R�h��.���TD���B�5�����\��NҼ�0`�؁5 � �$h�r��J�$���� ����a��P��LC������JH�Q�5q��~�0�	e'Fe�vQ�
��~��D�2����<ѩ�5�Q�����B�ɪ��I4��w��J����J@�BpY�'Y-f�j����4'�h�8�h�!�D�F@]W��$Tt5�
h;*͉�rb��$��
�S��j����/T���@�`��r26��#�>z�㻢�Q?E���7e 
��j��]��j�o��1�@TcSP��#�Uֽ�.����-�d��+R�;t�Z�e+��	1>�	1�A�ʑ���w,����~��Ƿ�<�wKf�����#B^v������i%O�	|NJ$�vX�%�@èڇש�Ā/�rS' ���9*>�t�T0���
o����h������#$��B-4b-�;i�T�8c����2�8�<�(#��J�PH ��~G�GE9-(�YQ��Ju��X��\�t�7K!���k��ND}��H�V�1bFާ�ъ�c�FS�R�7+�ڌ6��_M�,�9#���/V��_]��,�A�r��fpR��o�A�ߦ����$��c|E�]�k�Ѐ��H���-TB;�#�I��i1_��~�
0���R2��B�����
�O����ZaGG`Y��5*E)Z-EF�t���H���ADu����M��P�L"�h�ty�y�	�(Y���`�PFƇ\b���OӉՠCP+i�ҵ����Qe�0x��M���W�o�:h\.@��q����[�PZ���Ы���LT�4�ُ��ˀ�.4���A���D��V��!�y�>�}�t����8��Hі��
mji��*�vQ��GlM�)�ңҀ��}t�vF)�	r�Z1*(�Z�S�n��R#��Q���&�ƙ�+�Lh�״���m�����v���Ы+<L�܆荊@m�Z��POTƩ�[g����:ӥuXI��E1D�N�Eʂ��b�j)�=�U�'��ME	��z��|5H�	]ʦn<ŌU�P�YX{A5���j
�2E�QC,��E�<�*��b�7��QuT�/Ӹi�bU54����m�İ�(Rd;	;2��G
���‡��s��Ԧdw���ms&ĚЩq�MU�%�[)��;T'����	�֧m67wB\��nOS[�Q������*�D=��ZZ+[�Wz����1"������h-�֝?�O�� ��"��;Wp�����\Ȓ�c><mjVuk��]O���b;������&.^$�8��ʃ�n�9�2��nȂ4Lh��LC�$Ѵ5�\#Ue�q[�}w�iaҒV<9"L̵Z*�rH��!�;�n��1��
�QeY�i�p,�4�$ւn�O��k��tE1���Q�����T%*3e���Z,LZ���==Et�dE���ގ��$��k����5Ӌ�NY�h�l�S۵��O_M!g�J:'�"�(����#�w�m��(5�	$ć�H��7&�~�m���=u"��W>V��4����
P��SC]��_��M�b�,l�QXa�0EM
��n:Pt�ᒲbH���d���ѷ���T١J$�NJ�Yr�.j��)J�M��jF=7(h �Ĺ&�E��e5�$�뤔^�DK��!�[�>���Z�S��o���Yv�NO���߯pM������d��K��w�k�|��}��s8����~3�yM�dǂ&���H{�Ӯ�먳_��_w/���O	�Ϝ���'���??�x�)|��=���M�I̒ʧ#�[��,�>��$g��~���x,�'Ï����8��x'�M�r|OIg�K���1Ro���Tj�M��E������z7_�{Y�s����xg�q?]��`�E�$��f�E.SMU����oi��%*5�F2�!��Ǣ�%@���G��7s𼋏��`i�����Α:���'?�k�KB+�Ub�L�_��?��ϋ�i�G�<��*�Le�B�Ā��R���3
'U���G7�;���0��4L�K�h��)s���];Ftyrr��U� Z�G�����x��y,y;��O�_����aA3l)|��;�,���/�Í�=һ��'ɐ��cR(�j1j��8�,�����/���c�/1>��3�F,�����烼��~,ou�=d(!�M��������_z�D�"��a"�e�i��r6��ԥ�$kI��/6>�U�~��}����t�0�|��$sdpƸ��W�2�n=�T�]��¦�U�Z�y���QF;���J�
�Zp	A��]vX7��R�Z9�b�B�%��J�'f��&�#�z֚9���u�>����3�[�~�Nre�,7����̑�$1�
���UY��R(��dYx�G�O�>C�/#��?rq�W1��`������K�qIL$�n�cJ�	�,A ���v�B�����p�0n;��
F6FI�X�^�m���Y�U�*~��$Zf�U�2&#�ey�&8��s8߮@��r�zǝ�	,5�Ƨ�Q�Afk����Î4O� �uXٙK�s�d�5'�?C�9���4��#�)���*�	�Qd� %�����kVf9�q�_���3���O��y�M'��fQ3cd(�ڷI��C�+�T�FL\x��J~X���y������.�(a��䲁��͚"ć�\���4)���F� �e�ز�������q����{�����>#�p������.7ɼG�ȓ'?��&(�a�.U��8��⒋$H�"9���;j���N~��$�?+��~X_
x/Ǟ0w���nwZ2���P���H����N@2J����B�I�S_���@�h��)T�-�h7
�8KeYe���\�Ht
��W��MQF�ʑ���	�z�iv�=}t�)@\��IX�V�b"��j���˞��d�2��ـ�T/��J�],�(��<>�ɔ�$QWQZ�@�B�H�?�Y���=��Y�+K����穽Y�O��	e�F
d
��`(*Níu�:��JW�g8��XI#�l��$u���L�pQ��CIA�=?�a��.�Y1�*AHڌ��iM�M�j��2��b�T���j��:k��o��-������4��:i
�H^7ÀH
J����PH��wm(�`�(T�FlUM
-7V�����tl��Ggdfԕ>�����BRU��GY\1u@�6$���}u�q*'=��K��E���!m �;��@���\ed���D	`�EiuI�=>���}�+�6P�B�Q�T�TPu�G���x���E/Ԗ4~;h�0�5V���E�%�r�J�۩�6;�JUf��0++�9Z����C�h>z��	ؠ��G��.!r~�I#g*?�5��T�=�D{Z<Uv�PrB���	���1R�Q�
Ez�n=59Ƣ�T�q்*!���V,���j�J\IJNr�'�$R�)Z�"�i��2L%�+��E��Z�0�T5=B�i��QU��H�FL,&�Q�VR�j�XTPt����%)'/L��W("H��҇`:֚�'st�k'.�dk�.V�����oP@=5�� "��Lij�F�j�Ă>?�jU.�|�LY�(�e�UYj�Z�Ҵ�,���t�/Eǐ�P���>�Hj��}��ƈm(�x�Hfh�����$֫E�
)])i
aDh�n�������馈�R�T��@2ZEi%Pj]Qj�iQ��b��!�eE�}G(�|x��'�Ͼ�Ll�ր�z��e�Y�mU�0wU�9DU21�w"�R��i�	�
	dtDP�o�L|�,��PI$0�z�_ᡶ���Βa��
5f��S`K|v$o�)�F\"
Urא�d0��$"�{G+9+[�Z���n�*����̌^6d��DIk���cֻT��&DOE�5N}5T�C(i���X���.�M(vm��J蘆H��;��n*V�Q��G����G�,��O2���)�CR7U��J�ZF�D��;��@z�v=~}t�(���ƈ�H�앭�!V�������R2�`
'Lb~Kϯ#�
�F��Z
�JW��r.�H,����,e�D$D�:\nJ������������6�;�&4�bM�*ֶƣF�H(̊LUou�n��EH�>����ɢ]T����_ބ?�]��AB�zi�����P^|H�H��<��M-S]�:�d<�1F�q98���k���BYI�^�li�����BQ1�$1Œ��u��ʲ�P�
	���T�A)	d�!)Ƭ%�iW���ݔ�v�U:��j�%�i��M�J��A4�ç]먻\�3���q�K;,���Ԋ��h�+��i�X��O6,X]���*-��[Jք�ԥ/6T�J�==���D�[!�-��QZ�k�G#;�'0t.
q/�w)s�=���Jt��P�%
n6�T�r��)!�Z�ք�Ӡ�1�Pȸ���c�~YD��("g��J֞��X��d�e�q\�
Ɨ�QK�@�:�@
@�Q�?S	��3�n‹uT��5CEAj�c�dq���LJ���*�k��(�O�k��!��ET��_�+}@7�GJ�'z#m�w�K I���?=��1��q��'�)H�g�$��E~]�FM��т3-��]�R��IQ�z�7�%�K7����~�8�M�"�J:ڊۂn V��d�ȵJ�@(|�!,h�q������TBiw�]	�c�1��s?#��Ȧ�H҈}�T�&�	�|tD�R����'(�M �f�uF�]�_
S|et�HME��p 
�UI����-0� B)YxK�#;�3��ƕ����v�aP��|�7�.�E�d���*H4V�	b?��d�#
�����&�B��5A�_mI��u�+ІDŬSTl�������
*��MSSB�$����$�n�%9&�\Z=��C��A�BrJ��	,ȧ�mZ�Tci�����QH�����$�����ԟZu�q��"���v��Ř��RV�9)i�]Bq�T��Lab�@S��7JC�	-J�����I�Q
/R��H�jW��M?���NL3�4Q��CFŃ*хGA��:i~Bb[1��XP�R6�#�S�*�(��)�X�W �ֵ�zW��\j��^6��q�B�3�'`G���T��Q,J��)AZ�J_�����UGL�a�b�!��۱iZS��4�5[�u�����R�P��n���M�[K(�?Pv�~���`�(�F�UX��Tm�Ǧ� �(��4hc��Gz�J�kֺf!/�&BJ10#�2��#�՚�P	'�ө�Бk"�ĺF!� �6��E;�ҺO��c����T�
���J'���Ghs]�����beRѨ��7-�Sz҇N�DFRQ���"��6���V�?]t���qSfb�͐"0�ҹ������M	`K��@�j.	�& �'*I��T�G��f�7�u�5Chˣ X�Œ�%TFK�7onԧ��V*����E��K�$�h��t��Y(��NLG��#"�
܁����r��溞ʹM�2�u�5���ySܪ��a�#�L
fB��#����ҡ���bP�z
O$��F1u\����n���d"J:0��O���j��$�E�q 3�"��+J����FڤK�B&F2<�Ƨ��j�.�ֆ�cp�H��9��l�?�C���P���㥌��LC��66F�{WS%��7>�:h��.C�d��8�w�4ȣ�E�7R�
)M
�h&T]�JQX ��j�W܀ҵZSn�i��K��
/���2d��;��*	54q�Y��1��\l�s��bu/l�t4
h>�hk���ΨcVJ��|Շ��dr�TA��m%IM`��|�BYj�P�*���릍
)|�hG�6C2@�)�o� �Y���6�9h^��,����M�ÍE�6�X\�w�k�hh}5R�K���c�
��Y�D��@M7؎��ҹ2 ���My��㰑ELmZ��}}��N"A��U7�51C�/!�-~�b.�j5��*R�����`��x'������DuAy�$��hŦbUM>;��
JR����KGۅ�T�H��ѱ�P��7�@�}�����Gn��UZ��jP�F�[&�VX2����J�U͠�ĊF+�i��zh��
�䑩T�q�ę�`G>��R@�����v�\���-E:�=�H I���E�2,�J.(��:Q�@*N��Fe�P)�q�5��S
��s�����vu=�GiN��H��r$��B�q��T��Ei��5��G���㗅�u�V�(��kOO]R��HeT�fM��,Wc+#5:�'�饒x�p��&	$��
1n�A5��MsМz)ɖ�g@SskT�}�Z�D�\�QU���"fM���Ck*;�@�z�Y�H�B��S7��'��Í�s�ԊU`���G��]�-��ы����աB�!�X�Ȍ�ZʚT
�n���R�V3O�{�bҤ-������]�V�(΂W0A$���hb�%��j����Jt(�>*��Q)�I,`V���m�4��8�<jrMT3�R)mPMGZ�H��,�������M6:I%�1_xCQ()��=}:���%`�Fdt�4��$�CP�$�@���XD�&C�{�l,�r�{�Z�t.� Ѕ"�t1�����6b��`�
]����W�=5�.�t�/���@d����
A$T�ud�ز���'+��\�B�=K������]$r�x&�Ӈ!���lNZX���;�ћ]L���b�\��Ȳl�ʻժz~cM�h�)E�%}�m��Ǡ����MU���^T|̬�����;i��5ڂ�,�;$��I�����$�[V{�c$
bƤWm����m��T�O@�)#�U�P��1�oAQC�O��R�!���#�<��ţ%�֐¬��R�\��qe~<8��ǥN;�ӹ�)cs�@!OP6�F?�d^�u��Xƴ�]�P@�S������!c�2�[(H�AQM�#U�`B\D92gˏ��W&0�E�������+�.b�]�)N2o ����ay
bC�����-�S#�Y�V�

�5:H&f�jI3�'�h�5,�!B�>ǣP��K3"\#8�#��?~�(�U��|�Z�i�J���tə,���8�kQ�7��i2��\�dc��C+&d�L4ee��=EA�R�D�3E��e�&Lr󪒪�)�E,�o]X@�mR��{���k��V ���Pv���
y4�޵�(n,��*�•�B�H�(!���˞X�x�\(R7�D��^J��n�.Ÿ4���M:��3�m�q�kP�ڿ�M�u!���r��#�壑�D@� ]�������JID�%�B���.����?�L(�Eg̗V2�	!T�Ԥ
�S%�&	7��NJl<�;+��fVV
��٘�b
�S�Y�*�Q�,�V}[#I�$�]X[h����~��ȏn� �������q�#+Z�Pв���ښx	h�D];,�W"SH����Eh��
\ŀ%Iފ�#��+� ��T���jy%��4C���OQY�&���o]f��
��B�"R��H壖��ڋ�4�]'��E�ւ@��7,M�qJ_��؍�DɕU)�m��%��V��zt�e�J$O$�H�G{�Jc�O]�ь�h $&ˁX��
Vֽ:o�dh�ز$<Z�P�W�v�~�$�}���)L��"��kasajҀ|)�^�C�J��"�Z(1��!�Ǡ�sM���M��I(ɉ��I��d�P	ZV�J�uh����f8��.|��PTH�ZmE܎��Q�t@&�Xr"�`\�5����Y�edF�$u�:�"���H�W�E
����s��0w��X�
.��c�����æ�`
��p��)��!~�P�eKSӯRu���&��?l�'�.{��2�z��{hw�7�
%K�'&  J��3P��,���$ק��Ȅ¨XҮJ<�l����=�Mtg�1�ښ4C��ZT
{i�`~Z�!��\u|@�L.���M@�|:W]�c&)n.G\E���Kn1\������hc���4�F�Gy�2������)�����v^�3�v������(>}t:gJe�b’H�5���bk���v�B@0rU�B�F��d��h֠�w���1�Bu@����
�6�Hާ�z�t�]&H��g�?ݤ2�ʍ!�B{�H�hv����Ց$,��lUd*���n 0a@�|EG�Q$�P
��9�̹�{�SK�(��}ulE��9ց_6ta�1O��^7phV�p��SU��d���!JDr�IsP�VG��m���@�RA�)�)f�K��Ѐ7QЊt�ϸ�D�t|��Pg�]�\%�'�Ȍ�C�H*�[}���%�MB�e����SC�e!��q��tuB)GS�*'�Z\7��^��Wj�:��.��V�800#q¬p�{b
)j��O���S�����ߑ�D�- R�������k��]���LS�b��SUF'ki��ۓ_㮈�.#UI\i�Qj��7����ĉy"�|:��A��Ε>�V���ь
�YS9�v�/������΍�H��++��k�]X�j��ZWjiq��H�x�D��C��*�j� ��}5|QG��ϵ-���Ȍ����V�@I)��µ��1SĐ��DŽ�B*���*h-4�ӧ�F J&�><���D�����Maq[6�S�Z�@!ʥ�,�g���@�#�J#�2J�E��SJ`
���y�u�V�P2��|�,ձq�P�-Vb���S�z�_��eV`��/C��#�1�pJ�A�e��}5��2�*����x�@=�hj���V��tX:�����J'p�R6
�S�O��P�#����_�\V~4�f\�j�$��T�����^guݜd5�'r���ϑ�<��1r��FK�mܱ��F�Mk��+�b��s�I��M����'ZC�"يMR
 -m�ubj���N�$��j�~�.V���dUrTV1�>��ԣT���$��BfW�UzP���S�u\M�I��ucBr���..�T��᪚T�V��23��)!t��h��)�G�ҿ�s' ���8�����oZ{�R+������P�[R]Pk6
;�$���h��Gf.<���+�w���X�A�u8��LC�"�@�3���m�Ԑ:|��OU�,��1x�Q1�+��^�*��C_��q�I6,�	4
!�{L�XF����O�:b5\��;�в [š��E����a�
�(N����4X�r�k1z����}! "�F@h��@dj�,�����T%ex�T��UT~�Z�k@Mt@k JT��4E&\�"��A�7=�BW�5b@N��`A(�!�C*�CqA�
B�*z�n\ʑ�9� \Vs�7�c�>��Jbƈ��\y���6�h�J��a
WuI`�8�"��K{�VZ�����:h�N6>�S�V.����
��T�$S�F�(j�H��LӦN=��ڪT�Xq�=�뮕K�.d�>(Ɍ?������V�����"u�$�� �IB�Ac�J�Sփ�O����gI�������zj��&d2rU����6�׵H����oMH�Q8�<NA�F��21dm�,�mb„��᛺Y�M��
4q�	�E� sT|��֭�(������%Ot/�5
�iB>ҙr��
���u��Y��Ҋ���]ʣ�衞#�\�����������P51�U����1K[m�;���x�$�'�AR�"��I4V=hjN�a��2B(�	#t�V��Ў�m׮��)%�`��,Er�,%HRiAB(A�֢G:�&�8�9"�d�$��nQd ��Qn+AS�oQ�	��p�x���9�3j�I�z���	拢K�J;Z6
�7�����>"��`Ġ�y1I���d`6�.�=5���.�H�q4P�I�I�����4��3B��;��]�h�"UQjo��_S���dRc���>_��U^J҃u	>�n�5�吂�"
�T$�ۢ�՘F����V�k�)AB�n�D�6��+@�۵~:��1��ʌ�ʢ�{��iE���W�Q��4�~��O��[]Y?h�?Uh^7b�Uf��ſ�#�� 7!�@>��7�U�*,hH&JP����GC�М0.�� yFUlDuԽB��}u2^	���bRd�x��Ї,�*C
���U5�b�f�̗)�v��)VWJԅ�
�߭z�I,��2�tr:�Ȫ�J�iܪX��i��LtRx�dd8��2���U�˱%~�$�N@Z�e�G,m9	 ����o����@����H�6J$�Svr�@��� uTi3F�gV���E���M�C5J�R�tk\Ra-Y+0)P�ƪ�W��1jQ;�K���K�\�G��Vg.�v��HZ0۝�X'��C-"����~f����<��k��(���@y���:�h���+��I8ȓ{~��Ѩ�;x��|3�u'��y3�q�2H���偱'�LU�Yc|B^�AmX�V#&�$���Uz��"�}���?�V�� �=�y8�-ǗFc���l	��PP���Qű�׋~�,������W�ϊg�|��V�����S��U�*k�v����������E�W������S��ҵ�	�����u\���fL���|���R���4PC8G��80F…PM"��sC��/����@e���O��"(q9^+>=$���ƈe�A�A��P��TX6:�"�,�_ܟk��^�?�ax�za�g�r�1#�f�"��LcSQݭj5����N��+��c�%[/����ظ�B�L��D�l1*�
"��~Ϸ��K�ͺl�)&�H���H��&�v�^�V��o���}������I�2�&&�=��^O����8��i�"J��8���Ğ6�^���wh��z�>:�aP��|�,��{&> "s�F;d��6!�Z�v��
f�Ŗ�&���-�*�y�o(�;����a��#}h?L�8�(Q��
�!Z2�[��<��1����3���k�X��'�ϻ���D���ȑ$B�4Q%�fd�e\{Z���9���8��|A�2�Sq�w=a�<�(	9��cQ(y{���F��c�]R!����-�������dA�+�8�|��d
�����Q�-�SCZ�ў2#f]	�Vg�/�>;&�g>����gy�Y�{en����"��W���WW�B�L�ø$<���~�H��D�(�#�M���ߏ�>1���
�g�eg&Ll��{L�⿰��HL��>�Kp3<Y����r?�]��ժ�5�#�|���N[�9��7�9R�Ijh�dCF�xٔ7�7[7�D�/8>:/�|�ۉ?�/��گ-is��1�^y�O����@2T������]7�4sn"�C!�$46�xj9Tr[/~8>���?�����7+�E�e�x?!ʓ��=�$U�D/:��l^R&p=��
/���cQ���x,������č��X���`��F�j/8�e@��nfW��ܦ�V��֕���*��|y�˟)�&5��$]���֚���%�&�f�����=����w��T$�:JyאGv���:�RM@���MBR��"���$YN��!,�6jS@G��D�Q<�xX�^ӗ-�`*��.����i��D.���]h���Ʒv�����vҽ8!-�5&�)�B0
��CZT��hw��f�+��G�fN>�@K�j
PH�)���{.�Q�#2�u�����Z�*?1�D���g(f�_eN֚
�S��j�LT�/��̢8�ʪ�#=A�`*zo�I�`��\���V��#b"���>��5��$��\$��ZC�+�H���dTI��������T/�"� c�Ǿ��?��*��M�Z�@Y[9B�|�ԯr�z�#�"��Qp6�zs&��PI~�L(#J�-,zM)�X Υ�"�CH�nԳD�_��~��$,��CH�;�Q\��5$F�Pv�p(z��
�r��g��mYڧ�mh�M�a�tP����ŦQ��$�ԯ�5�j:|���H�g�b,����Ol04�f���m�+�T��+���͔�1X�Gw�����߯��3{X�"�>.'Fj�f�͠���ڤ���r�R��ʑ�䲤rVG�QF#Si-�n�i�*5�@p� %qNB�e����,�(
w���=J��5�1qD��T)�`�c4����5��Z:�Z�]	.��HcN�t����Vܗ����R�j1�T ���#���0e�A���HaR=�ڃ�ԍ�\Y��_�C$����iƀ�&����T��%�%���d�Ǝlp��DiTR׭��:�6�uX̳�[;3�)'��i����ը	��\�&��щ-T��U� ���RA��RߺMʨ��ѿ�q��|�$��u=R�X\=+CM<�4(�R�'l�A�ϕ'�-���EcA��:D�U0$���L����H!�q#�H�$�
Uz�N�qr�d�-Njr"�Ȉ��K/��t�K�=�e��k�4eV(n�K!r�y<��gn��*EF�P(���nb[EP@�����]b\�CB{^�xZ��QAԝ�<y�`��]5�C����RrC�\_`��?���9`�0�,�yl��Nk$3B�-�-¤�O4�RT�H;B7�p���f�
V-%}̋r�֪*z���2c���YBQ�Hca���s����rɞ�
,�=d���B�Jҧ�간$K��R�/���a�'��H['"\r�=��� 3Y��
���K��H�$~d��MA������xQ�)�#{��=�}��t�l1�U/�eB�$pM������‘�
�͐
�W��V���@�'�˗�o$,���";�M��4���ŚU䯎!�9q�d�E��ChU
QQF��5щ!�~�����ŏ8���z+��1�+UN���r�b�ª�tUg8�,ea��i�����р%��̲n0�RQ�ΫQ!�B4��?���H$U���Ր9'�}W
+�$JH��h�Ez�i%X��,���f�cq��`֠��D�m)M�)������r����pm��M��԰�o��$�|��
�!Y9W�30�ı�,lʊb��V6PW�㡉�Fn�xJ�(b~AЫIv(��`��t��ֈ�:��49�4�(��2&Ƃ�5*:�����Ǔ?��%	��q�^�.X%��Hv2�U���1�R�RiE�=<�}��V��[]�\C�"�Ych[��@kC���k^���\�U�TEEsx�V��@����v�I8��ɣd��
I����̎�O��ޠT�������)䝸�$}<�!�dcaP�f`M+�R�����K2qt����gw6e�8mv�wKL�@qZ�Vb?�������U����ž�Ǻ"Ci�
l[��W�z�H-D�<�&=�Up"���Ջ�(zP�����HT���lĢ��B�HG�4��p�3�/fV+d�7��A`��/�te�_O��D8BE�q,�O�g�&Td�`�u���!�X�j=�e��x.W	�x���@]�HjI�R���yq��e^��M'�:ȆG���'!��K!����(�"��O]9�6�WT���'��IJGZ1z�z������ښ���I͂�fw�c@(ƫR�ңm���Zj�n�Ѷ�ܰ�R'tIKD�����O����T̊7�����u?jF�;��q����������Ңh��6O�A1�;3��"�T��J��&Q���;f[ᑉ�U�g�u���j]�h6֡>�$�Kχ.;;�eɵ�� ��ZC@k�ƣBq`K�їELLe�.;1VK��K���o�4���(ȣ��A����"c*J�Y�`�ާo�L`�nIcbd��I`MB���#(�!m��������������az��ŪOJPz�4ֈ�N��2�3I�@B��ڭ��5;�:����T�(���0)3ACOЂ���k<�R�)�[��{�5f$0,7�S��M�BE���'>��!)qJ�4N�g��ۻ(���*\7ۦ��R͘2��&Vd�X1�D)�,��5����A��v��MғiN=Q�u��H�%�����KPПn��z�LQ5�T�+iv��RM"�_��^�d�����N�C ��A� |�=)�Ȉ��VqU�O��X��Tu��Im&%���}5Y�Qc
�ޭv��X�Q�	�������QԂѰ��ju��$��lr�ۤ�Ĥ�.�kB:���HJ�	��d�U�-&ֆv4/MǼ��m�j#.a�fÐ\���RMOM�A�
8�b���
�����{݋tP(�TR�Sm;�eֺ�1,.������H=
i��@��3��{�W]����WoA�JO.J�Q.d����S{Mk��Ҁ��U ;�%IH�V4�L�	ƍe�T���i��
n=~C �*]+8fh�,f�
��A`w�KoC�BB&�9����#)~��2�gb��~�P�*7ۥ4�&I�X�Ir�,<k��I��,��J���
i��0:'��)�9n9�+B�*��I��T��4u��K;?>�H乕�UBl.ǡޔ��5�Fꑉ"����4@��5h�j��A���.�8�M;�ݑnA@�(o@���\d�,��nO3���$V"��ܝ���h�077B�4͖
@fvR��P
���5��Lerh�ҁ',�G,����vW�bT�Y��ԥ�
�ڇ�����B^��E�5+e���뾄$6�7�2��%�0O���`d�C��F����pФ2$�h*(�%���N���E�r~J@`E8%���q��|r�O�[�C˱Y�n�P��
<��X�����É��8���	PB��T��ҡ����jY�މ�K�"y�y�95F��� {��l�]���<�2� 2�� ��L�%�I-_�!љ��
OQR��o��$�%Ɵ�"	
�|��q�i�V����fSD=7�5�04��2a�ܬ��8N#1x�u�QucBRJ�$�\C��.8Kip��R�L8�H��s2����٬!jH$���뫝�YJ�����|o�V׏�wf$����I�C�$��n� �2ק��a"wq���Ḽ���"V1�V�
(b�U�[K�M���m����"��cI�eI�i���ku/��+��"��wU6�x���0v�� �-4ښ��Q4jVf<�QȴH�E2F��j
PF�]�� .X��`�h#�̉<״���@MTh(A����\���)�gY��lAdWU���]P�Z��f7{.}9'�elj%�"��R�rR�����G����R�}۳��F���ʑqހ�t;�����sR3�[���gU��ůp�
��~_��(����>�ԯ�PɔX�����+�rU�5~:8�W֜љj ��w�Ɠ"!	B<���pR�ҟJu����Zk&.k�c�b�L��n����+�'z�Zb�*%�L�)�h�c�c���P�Z@j�z�V1�(		t�ja��ȕdb"5�~���

���� ���4I���|l/F��f8�FX�b�UP�h��__MOhL{C,o�u�Q"�Fy��Gj�c?pƱ����"�'Ѝh%����b��/錒~��*����$t��笲�,U�gEyb⸕�F���DJ=�_��
��w��v�#�%���6j�)��<ȧ��F�����t�!mc�PE�1 �>��Yc!�(�0���4$���:���i�k@�aL���,QF;�lI$�
w���z��bw�7V�� 3ըI?�ST�5H$��q��*��DQ-}�Ar�@7UA4�RP��g�)�	��D
v���@�i��,�V�������}��D��T�P�Rk]H1iLh�f�0��9~Fr��,x��m&�IH�b.�O��t��N���L%���&@g�`e���jU�]��D:GKd��OI��li�KQA$�hH��j�Oš��(��<X��u
4��(��$�Z���F])l�>iq�Ұ��c�
?uDf�I_o���}��O,a���X1��3P�
��k�6��t"����Z�̃�`H� ������S�t�_��mW$�%�>Vt��.o�E���X�mv�j�&��Lrc�2af��:$��AkH!i��;��gܲ�4E�E���J�
��l
)���<Pt�1��?�Q�����{wU��t=h���$�Q���a�A�"��u����jB�
:
�4"��"HgN�C2)�
D�E6̛щڴڀ�G!Ḻ��)�C��Y_MHJ$�����…k��֌��ӊ��
V�����DP1g�B�R���oZ��7:�,��^1�S��H#��AI��Ȯ@��@�H��5��m�� �)�x�{sO$�C�K8=UI��
P�����t)9ɀ걦��*�F-�r�7�o��@%AD%T�;$Ic��#��%���7[�m�tr0��X֨�����K��R8ݚ�
�Pz
P�����9�K�*�?E*7�}��=+�Մ=�9J�A�T˅��I)RJ#����龳F4"�URR�P��}�"P2��p��U�"uY��Z<xdʏ#�Y1���W�X��ǡ֘�/�H�<�@�y�N�2&���%䀤�Tm��T�W����&�@�m*V�ju��-.끪6>"�I,[�{-� ��%�^� =Д�dHr&��aTvfP��ѩҴ:������*H"K�ԗR���(5�7����R܄C �������Z�J�����
H��"悡4��Lq�3���/mĆ��VV�������~�g1E8�M.7nx�MEE���SqM,�XQ-�/
fH�#�eT�
����m�i��Hc)"�� r�S�#Y):��Lc������7����f���.ܐ8�?J��^@J��\�6�iN�Y�X����h�1\8�FG�L��P/R�����b��؏	
��U��i��V�Pȭ ��N�ߡ�<!�85ꁓSDH"�A.,��$t.�)P�����/�"4	y���X[*Yb�bywcZ�-.����Y�Q�K#dl�ˑ�Y��-J�g�����P:
�[u��p�x�G�5��1f;&J�H����u��s���N7&BϜK�j����ߨ{F�u�Uq9��.�����Hb	5���>@�4����4�X��W"��\�!�`H�WӦ�c��g䣋㱎[�}L�*ObPX@��p��Ҙ�Ζd|V�Id�7&�h��U�m
��m�~z`:�t��64����nHGuޔa��SA�Mh�)K)�b^�n?�S"F��X�L��
��s�k��
J!�#$��c�K
����ң�]�k�@�T���P���(F�#'�2�hj�J[i�>�D���qʅj�j��ZF�~g嶛{�Sqr�<8R�lo&k���nҵ�s���U��j2$5�W+�LB�2(@���;`��w�t�bɀdAl; �0P
-5`k��JG'&<�f 8wgdkv�OEԧ!*��*8����D̫ƴD��`�:����R�+_]S�I�!�X]+��|v����A �P�"@�!�����+��(�F2�(o��{��q�sM��^���,��vt���,qTѲ���G�j6#�^���1�&V�1���0�<�S��1&���i�K@�Iq̀m#��*5y�E0�R���h��w6(��3���)���LcK�F��G�4fR��
��uH��?�� ��RM�n1�*��ESe�'m�)��F
J.'���ŏ&<��AId�F��-Jւ�4���m8�LV~vX��ʏX(��*����~f9|@tIJ�>34e�I��i�
�]��w��g)5IAș�P�\�T��T\w���P���UP��"���,'�$�du�ڔ���$�Zi��](��Z< ��|��;"�Z��@(�X�u+S]&Ɇ&��2��I�?��Xy����UG�-܁��Z�J�DҊ��`�x��W�#�P�EXGi�'R�X�4�m�$ ��̇7
T�)�Ze�p�қ
 ߼Xt���b�NIYn��^ͪPʬn���Ҡ���n,���$�^v(9����I�d:�� Ձ.Ct�O�ךq�2mĂu��f�`�z<HF@B]X�e�����R���a*8����\�������gi.�槧�jy%���x����|��,�>RUY��Ũ6bAP7�=5�Y5%�V1��O3L��oj0�-�����޿��c"Qo��H��?ʴ�F&`���B���R�iZ�n~Z�26ދ@}�β��pW�,9��F%U�Ȯ"&�H�����]�d.<�<Ml��n\�`�C��'Y�D��I�x����>:^�!� h���N��pY��d�
��T��U2(b(�Ri]{X�U��%� �q��-�P�o�G6�3
� �
Ө�sd�
S�:K˳ ?��<�CǬ1̹�J$*���"��}�G]$�E\&� ���#�9�;>,�㍣qٌ��Ⱦ�x�A*�{�ZoҾWxw�q_�n�k�Z�=4p�X�6��꿴�(j_bh:>zӏ��?�u[SL0��2;P훀!Y�!_�OS�CV�ً)D8)L�%��y�c�ab?�N���P��떄�f�Q��r�;O$a��J�o�X��F�:J��K���?H���o�=�v:(��MS9ٸ�^<r�E#��?q�F�7sJW�����L�t�sdn3�<�ƛܡ����1��E=��vҜ���k(N3�2r�b�F��Y�t`�zA�:j�\۝��`���pDo���V�����} �П����Q}Ry�0⤑d�Z��q}��u�7Ԏ3Ŀ���5:rq8�F���p�Y!B;D�m�Æ�-��E ��%���l1�X�׺�,ʅ�.���lw�
	#�/%��E�:&$;ı(y��G,
���Q?!���T�{XҾ+^L�|yd�9Irv�Q��sj��Pm�2�#���bd�#����(I_�r���#�F�M�3#H&�\�$��Xn
�3~�5d�|��h�ђ����z�]!���2dF��Z��ؕ"BE�E����}J277M ����+K1��fuK��@v���i��T$�ɸ�W
`_��T����C�D=BBR1��e��UY����Xmi=Mz���jr�'�z��@�Y$�Ȍ^�Ơ
��4��l��BMJ?��`N�>��8�M�o'�"Ii�C��ނ����8(��H����^��AF�@R~'�����G##���*��UڅkF=@�ԷKAu���Z�Ʋ���
c.���~;�΀�Y��U۵Y��dr�VT�mU�RAmV�ɸ��Zkc��7�h����(�2H��%���2C!sP6֠v�B]H����1�V������gAM�Z
��9b�$T�,I�@�T�8�{����;P֢�練=�4@M�Q&��X�I��A����-����㫹�2��0KDR,i��e-�FnH�-Hܚ��ޛ�oAӦ��n���ř®.6<
�IQ�w�C�	eRHZ�C�m��D��x33q�2Yq#���7`#x�bw 'o��A/�����ΌK�B-��d֔MQ���3���7�r��K�p�� ;��N�c�t6�B[+�Û,i����uv6�S�zԟ�_=(pAvL�2�s��*�:�	${��"�w'�����f7t���1"C����6*V�֠T��Z�B��g#�S-�ZB�.($Z� �|ux��IV�;%raw�>��D��0ƌ��n7�������b�����ʨ�2\�nJU��}5�Ī��,��[J�j��
n&��㾐d�#��c��I�8n�(�-�Հ�O��v�Ā���8̬��&�1Hd���T�E��A�T�$�Y��W!<S%��7��6���ڭ�}����rH
�$R,wTHǸh?U=�p4H�u�yg|��1�	dWd��F�,�mR~-C�6P����g��2
�) ��Ȭ�����O��@!�rT��,�\t���/aa,R���ck]���jq���9��C�\LY�S[��A��@� ��C&Q]��8H�2��+_j��E��}��z�`�{�d/D�_�K��dK���J����#�+
���@��J�\�h��I��$��P����d`.��uƺ2V�$��*��/x)�(�M��ִ���@�j:H��,n;ZV�{�֫C_�;S�C$
������yO�7�y�Θ|'�7'��5-�h{��lh#E���'�+����q�_�H9��L�.4Ƀ&6eRz���lV�؂+�C+���VxȪ�����GQ���,�3�=�H��}��l��H�~ ��k����׀����cǝ�^S$q��X��nG�O4��"B�Y��Dm� hv��^R?徢�U�Ho�>�j�?�~7��<����繿 ��I2KI�x����L���H@�J��JY$�7�Ǘk�H�_��_۾O�/�1��q<T��?�2�vn's?�JSꗴQ���	+��F�Z1=��Oy�켏��R`��}�Ͼ������DZG!��$\'��8��C��ӳQ`�	{u ���h3:�b���/�r�<dy��G�>2a��������YA;V���<���i����
���KG37+����8�`���	�.굡g&��c�p�.BӒQz_A��^;	#�̞	�i%��S*�zԁqޟ��^ؘ�$A��Fܚ�o����c���	��\�/�ȗ���8�χ��*I9�,odž��ծ�ʬ�3�&9o�4�j�S��cf_�<���3�~:�'3�A�T?s'��'g%	�S��b��7VPT�ԑJ�ǻc~��ύ���W�����bS7�x�^(��K�����*Tt���;z�O9%�����Q�k�<3?p�7�˶�#(�dk�S&�J���|�K�B�x�׸� ��Ɵ�28 ,(�R+�O�O�魙�pB͌X0����d��d�Uh�qbݿr�ܩ��=}
N��b$���/���'������G��G���3fa	�&&T�-QѠ� z�M@�^NBq�W��fA�����.C��o���'��2�8�.o.n,�I�N$���-��U
k�|C!y
�����lYz��A^@a���#�{����;L.4ldW�,�g�,)&�$�"I6]����,����>�}��:����^/�x��r T~�LBC��Œ�PX2\ݶK�13�턇gm|Wc�@}~ ��1��ϕ��;�grY�\Y��r��J�F�+�\�<2j��T
YS�P�H���{xr����}�_��;��\�_�y�'l<��A��ĨS(
�w�i�~�u�8���%���Ăm�^��+������u�7w�=��~;�XQx�JI
ǂC�.4���͋�h�?N�I����� p��o�<?�߳��YL~�;}�"~�>�a�ws%�7PV��Пq��3��j:q_L8����گ�C�_t>�yk>&vW�2������O�?nP
��eqN�fx�� ��3�j����'׏0W�߱_q�~�}���B,xy���X�K?S���fEȍž�t��	�6�]y�!�O�z����0–�g	
�c� %�i
���S�}hu1�|P1%;��ea���P\x�'UxM۰o��I1�_�я���b���$�8cG��̊$��QV$
V��|x�5%���H�A��r�"�2HI��{���5���O<d���B�����&@�Q\�VPZ��h`G�}+��
Iz.L��8�Mܒ|v��$3Ȥ�q;P�ꎕ���$���VIu��,��o��_�:��Nn��_C|8Q�$а�"�
Z�µ$�:��w6���K�K3��t����-`���������dH��(�5�G�3��G�D0i��@��I�=N���lؚ�#����v�-s1=M[r)��F��p�,���om�5-���z��T�2�JdL�8�*�bi>�H�7w��SM�.<�F�I81�B#̌�M��*ʁ��
�һ�k��*�R�7
9s#y�.�G��"�F�R�p�!�F�1��W��uh�/����K��$,�Nݵm��6��u:��I��a���ee�,�;<��0U m���Z���~z8%)R|WL�Q#��ȎW��/�	:���j�E�v\XZ�Fa<D<�"�	P��i�kq�$
�g �;��2%b��U}��,�PRu9D��ܲ�l��#�ַ�jڡ�&���5��B�M)j���,\�n"���U��j�P(�-CM�o]ZQf\%E'
)sW0��21�=I�I�K�U�.�F�gsCʱ<�����v���F�N��~�-+"1T&�$�5c��ͨ��i2�c�a�NA�2bVk�Tb�S_J|���a/o4�=��MH��K�d�Z�h']��j��Z)�p�lXF1�����U�E6|E+��t�Z���.Q1q B�����W%X��]��yE�ul	e��a��+2*��MI)BB��A��N��=�h�Ԋ�nPiC��ֺ�D+����%ȊC&�F�Vƹ�W�=iӦ����R�78��r�V�W��x�uj���v��)�b暪�ށh�sd�2�M0�.be�%��}���m��1��Q��ɒY#�|���zr��V��r��mOQN�-!��E�W��x�_=2�,i�bw4��I�-+O{P{Ek����
-�1,GT��#�B�Fe�(W��.ێ�n5<k&�h�3̀'�&kԗ���7\5����c�	��FF�;7��s>3��ѻȲl	
��������3�j�TM¦V
�o�P�O���W�6�m�jGy���!e�/o��dh�W��[* U��Һ��)_�o�F�88��v@^�
B�Vظ$A��Ze��]O}Vg�g��lC��R���UIeqi/p�=�u�@��ǚ�I�Y^n�DM�I����+G�/�gdb�����R/I
��)O$��x���4v����\m�r�??���������T%�ԫA&68vY�d,;.M�����j�4������*�n37��gH��9�+\m(/��J��F�Y��4Y<d�ܖNlY�!]�\x��@Ƞ1g�
*=u#�.N�ћ5Z��c"I$�=��T�]��hi�WTNjl|4S�ܦ%��\E&0B�Z�E�����ў ?�GU�~>Bʁ4L�E���v��]X��EYȋ Ei�`��Q� 5�؊n$S�k�zj,,?��t�'��Wǚ�J�*�T��!���ē��@�x�)L.8������%��H˅]���#�z��F8�9�!���8�)x�?�8A�ɟ���9�ҫ�)m�o��k��}ȃZ,�7���Rq��W�Ȓ�$ X��F�rI�zk	�CS��!A�mƝ���#�2;�FF*T0A�lA/]k�7H^�<�Ezy0�����,qWAp]��:����H��H���8�9��lyV��uYU�e��H�k�]C&(�~y.�H���Zp�`�%���Z�A$�˟j��E�4ڃS�2X��˛t�}ۯu���vG͒#�-�R���a�^��5	���QV{%x>w����C�{�J�1!���l��:�3GK2��e@�Y�x�fsJ���I�P�A!��X�AwZt�!��UA�'y^��Þw7�Q
�K�{��wR�0�U=�o�܆O3�64�p��K��*�yd����P���0�kl.�]e���ɇM������
�	�BH4?�U��tN��rU���P%!�cM�wє���tLL���V��$1f`nۊ�I�J�@H9Q�X���M a�.�kE��M�K)]ꌈ
ܴ��G���#��6����M*=:�� � �L�K���|�U��Z��B�sN��ԙ��w)>A��5V\r�#<y�\�t�=�u��č�"[��`cIc�	{�Z��[O�i�A �i�1/7���$O&4i��%o|�	�V}�QM��v@����+-��o��A���*j���wԍ%TtG����i�&d�<u�uS%��M-�

R8�N�,�2��xlR�.BKVJHM�� ��q�C�%�V|A��X��އ����ڦ�.��:m��!GJ�T�cl�Zc3�w��e���F��\�V���TirGp�1,��Qa(ٝ�V^�*�&KB����M@1�N]�_�&h���hՌ
ۭ���Azz�4 +@��*��i��X�A�9���7�zSQ�b�]\>�S��%�LUp���c��)_Bwښ��D3(�,���]�MR���MԴ�(E7=u3"ek&2Љ��#����I%�H�P^�z��Ĺu"2lA��偩�*��M�t��r��B������1��Y0�l�?k<��)�M�RX34��;G*�i�FtL�o��kM(��������_��e��!.��K��׋!��3�!��$��J�{)�5n%T)���t�NZ�ȳ&��x�-ul��[��v�a鶵F;C2^�-�\�^��$*�2���Ap5�ێ���e�s��%D� ��\[�H����2cP�@��ޤ��jDF1s@�Kj�&p�Y
4F8r*����:y���K��:�x��M�F�k�A]��;��1�"(K��*��d�L�,Y���&���Sm���n�MzkLĶ����$�G4�L�^�T�ߨ��$z��2���r1��E;ڡ��/�QH.�=@؟]Q��Wyq�N*VL�j3n{lR�0�_�J�R��HM���2��$��/J�V������&�L�)���*� 8n�%)_�΃��8�rj�<��<(L�N4��{��������4>3��uۜ����ɱ�#�|��ȒK��r�݆ĝS���+�r2�� ���nS�a˟������S�ۍZhI��_].(R�Rb�x�&G���d=`�g�4�oޞ�~�<E˕I�0e	��r�J�%��TZ���z�>A��.e�.TC����$���H�8�6���}���@�?����C6`-e���JR���O�r
i�JVHs��6l1�Ԍv�\T��
�T�Q뾌���qT�VQ�y�"���P�
Pp���R$0���|4ܚ�w�sJ�̔h��#45;���S��S�*Eֿ1n3�%�{;��l}
:�Z~:זl�	u���ZUl�n�$Uk�u"���Ǯ�����e���xe�r�)\Ib��1I'�׭v�����K!f�EHZ�t�y�4�ؑ�12T�h�b(�;u;��W�R��rxp�cIJʲ�[E
���F5t�gC!6Y���p�\֎Hn*�{�ZC
���,��\�'Z(^3
L���"�Ʀ�-�X��H��Z�jy1}�I���\N?){*A6w�q�*M*MO�4�cŶ�t�:O��$yĜ2+M䧰�Y��
���t��;�$�n��d�Q`g�|(I���-�����ۍ*h7�D�k� j�D.��� ��%l���B1�'����1�&��bM�1�'@c�o:��\f�F��0UN$�/o �fI^�-!@z�N0m[����/'��Dhdi\4�ҭE�5���X���֨����^8�1,��r�hqyE�ؑ��c��8��T��f�J�6S˓�l��E��}�C�$TmMHa��5��VY\Z�`A����U�C3��/*����W8�o����B�%+cƴ�#sM�����ƛ~z���3��X����džܩ\M#�P�ʢ2�V���j�q�0j����FDp�JJ�(%E�G~�	Z
��m��w
x�[�ǃ)��ą�i��L�
�UZ� �QI�6���Ėj�L�D���&/饥hK�
�6-��p;��o�Nq�!��qU��)��4C6X�fEw�T/y��j��W�Y����U�9�s�̇#��X3�'W�
���ЂMV�ښ|d�]�L1跼G>�©�Z�39d�T��X��m�q4n�f�S��`͋ i�2Z#)PؓJ�E6��H	I��$���i�
���{�L�l�AF]�~;�4�X�1����u�H�㎉Y��T!�U��kr�U}�l��a^��HV�:pnn<���d2Γ� �$J�ԭ*��o�X�!mV��3��r�E���)c9!��m�hKk�㮞M�dY�B0�\�!q�x3��8�-00���F�5
�nz��"ƃK����w���O���E�ƶH�ć��hwb��	���OE	�2�ళ��� ���ՙ����ܕ����ڤ��N�D{A"�5�ę�gM�ҡ=�kP+��Q_�m�n�M���./�,|��)�Y��O�J�j��#r9����L��k��\���j?��$M�,"��S32�X2��1�E��v����t7% ���+"\��TU2��+o+�CQA����`@
s_.|���Q""�V���6��4R*�oZ�sD.:�|��Y�9�$�b�d��S}륉rGTI�3��A}�@�GB�,��=�	� �D��p�)�YȪ�j��@��v��F�`����s�O���vx�gY=�Kwg��U�75��q��]�Bvx�S!���J�����-нE=5S�Ĥr�/>f�C�"��FYOr�R�1`����:SF �)���$X��4�v�U�{]`�n�������%%���F�F^FD��C-�}�ðPZ����Zj""]Z$�ʜ�L�c�dy�Ǝ��n]��P�z�A��b,��67�D��;�^<�F��j���E���w nz�zYA�Zȉ1U<N#�œ4�f�!�wv�<��1���o��qFD<�u�}�/A��.9҅�+��J(���„���L"
���ʼn!f�ɍ,�}�r��L�!���8�PJ�P:�
c9""IE�D�l���8�I��Ho1���ަ��z��O�1(�X��G�J����*j�0�}��q�����=�\BS��U�*��j�Ҥ��5�w5�k���u:&&�Ţ�5F$0K���cԟRk�cb�����Mp�FesKZ�R�����d1b�~���(�r�e��|��\xa�*J}Ȓi)U�3ր�����](�u<F<9��B�:�"Usdb��@Ѻ
o��1���u��q�ȗ+�4��B{:(Z���G��''���'f	\S�;�[u�^Z���хE�A"�tc����BD�+H��I)S'l�5�������
]L�L��ޢw-z�JTy�uڻu�5poà�񱱳�4Q$`Ԓ�Z�I���F�仄6�!���n���bj.���뱇G$�/1�2lliIĂh�e,��ո���ڛ�)��qA��H�G���%��Nܥd`��N���5�M��y�pN`�O���b�@`�Uz(��ۊ�Іݧq`�"^�J`A��qK��$�S/���
�Q䴒��u�
���9.�&7s����k��W.����rHPh>Z�eW	{�-��C�bcŐ�,�	�&� ��kCQMe@D�W�M4��'*Z�lj`��d.�j
�P=~;z�w��Ƃ�W庨ǿ&�U�$/�
�łƃШ��:xɈ{�@���s�������e�l�64P0I���5�z뜅�D����E˶$!%�H�����	b	#�ꤳ�J��v|׎�c���>�y���Y|�l1uuÉ��AcQ@+Q���Sr$9
�%~��0�X�oܞg�?����Έb�<����>*ʑ*L�"����Q��Ի,�� g����M�B0���49�D��6���mH4��k�\�RB/jL�b��^ۅ*�U�j��O��0r R�`��b�`x搿�#H�U%��V���u��8��'��r;�dwpY%��ABЃS]t�j���W��I���gFc�#������Uz�75���r7���K:V����r���"�]�/�H@*��ZН�:�1��,�E|\�a�X� e�f-DfD6PԐi^��	� �BQu���&qXVE����7�}`�~F�-d��,�V�j�䤧2i�^L���CsR���f��<M�ٹ��*e���k6��|u!����O�ѵC���y���a�֌��ki(��|��Џ�\���2�y�s��L������A(�mS�ւLUV|�4�@@�\������:k�su�%H���p��X�
��㫉$e�| �Ex[�H����ƒ�*޴�� �p�N�k�Ϗ.l�%e��#�c�1�tMy5��.^<K���ڵd?�efv�54k�^�AǟE�8��l�%"�%�L���Y��ڮ�V"5)����7o F���\)��ޭ��J�̄
����+O��`��2�1���n)��,��P(�%E�]�?E5��q���w�$����་;��1��+���㾁��qC�!x����ZkH���Ž�6�bl����儗��,����	Vy)QH�*k�u(���~<�%����&B�b��$hˇ,@`��N�BN|���@�.9dX��P�Wt-K��$|���%���2C)�5LXؐ�I���}� �?-e�]�UBB��r�\Tx�6T�#G��PEg4 �*��(v�<��0�ѿ�xE���s�ܑL�Kl�<!J5V*�����Xy��b���RS4n��F��
�f>֧�Z�k�H�Hׂ�KtB��uő�<Y:��ܡu]��T]$�jϪ;S3����d:�cQ۵jT�`
�+�~?;��%l8�%�,�P�"���\tW��kt`��_�k<g)��*�꧁�L��� 1B��#+�	W�Cnip#�Z�	9�ҭ�(`i��4���H�[BiC�5l���]�,y)x���g��T�̵��)qj�*4�25	��I'%ɜx�s� 
�$��i���u�X�t2f.��:j����reQ}`f��"Ѭ��:��O�$�該!;>O1ډ�X�*�eQ�E�2F���y��tS_T�nNv?�&duǒ��ʫ#9�  c���Q��v��*�c �]?>g%��QE“#����@@cҤ�T붪g<p/��GrW+�̰��eBV-
1T�bYhߧ������舛����S�?��Pn?&�T�b*�Zv��o�:�M��FYj̔��\�r��!$��D&�B��=�6�ִڽt1c1$��9&
���O�F�g碑���۵.a@7�:��V ܐ�ʟ#��.VD��`�h�3����:XN��Q���ҨT����20�s���z�`��݇hF�����:z���d�����^*�Nq�VYrF(��ťH�pƝ�2���
	����dy���H6H���b��?UC
�G᧜��\�n��s��)�n�5�B��QQZ��qO]��)�}���E�Rg���Go��r_
C<���"�J
\U�T�RH���Yc3Z��W�)G^��9<��%�m�ǖc��Uu�j��(R/J5�lj=u�`���Y2��T��s�c�I627wx!��ʑ+�
UA���O¤o���!R��P�ga�D�(�e��# 23�M��o���\z+0�m񭉅ˮBȑc�}��̒�U�z�7�7:�9^��:���'�;^L.�H)�������?���=�bVs�P�xs2Ѱ�̐�B�⪬CX�&��N�m�sr*C+Ł�F/7�x�
*�^T�2ގ�S�=OR	�jCۑ���T���W+�\��8$L �Eg��:�Ј[rO��9�Cs�ɜ�Ǔ#2<V2,�B{��b��~m��̉d����脌1���eZ�����ki��'�.ޫ�
n��ņ<f+�(�t":��+
l*]��i����Eև�$\��4f�H����
���p>��)�Ǯ�-'m�7�&=&�c�&?d���eް��ԝ�"��o���LC�0�ǖ����$v�+�kt��I�,�\�4�8dP���%�B����Ph��1ĉE
��v�$�*En��ւ�U�`�K�Ç~C;��#�]��SAӨ#zjQ��4d�F"��\d��bW��<L�\ҕ5 �	�v5��M(>���N:�6.DY;��B	�ڢ�)�������wpɡ<�$p˛oj�(CR�E�~��sQ1H�0�͟�1{M��,yrHH�J�j*
�k� r�ۀ�A`�����z��H�
�OJM������q,�WƝ�^T��U��p�,Nթ��/�nˈ��a2NVg�`q&��֟�@	��f�ԫ�b����N1��*����a-�s ���޿�][u[@�3�T	��Z�c~�*�*[����ӧ�qE�Z�����|�7o���H��c;�,�����ס�6:`It�h닗�2�hVB�\$L���(�cO�]4�|Z��1��'
��x���ìG;�8~[ʹl���D�C]���V,6&��my�s��c�7-�к��L΅����xwh�O�Wh��bI�-
o|	ߠף��(j�N�9yX�&Bq���Z�}�7Z|jO�5�"B���n9gď<���ƞ�7��a��붣>�䈐)�i!�Q�*xo�&K"V�*��׭	���D�wNh4A�����<��H�W%	���)K�*
C�H%Z&�NrX9�6l��$��I��k��Ri���<t�Tc*�5�a�8����x��b����OC��~�a�rR��+>&F9Fy��������ӭ)�u��Jc��]%�q]�y�;X{Gz"�mڧ���_����L�21_7Q��
�J�!V����}JP�@S��tHK�b�e�(����:W�0
ROT۷��b�k�Ô$�#-wp����U�]$�[*Ǘ6L�Ĩ_{�(,*z�T+�]8�:]��0+���^��70��$�zt��BI^s?���'�A<���	�P��
��P:.y�A���1F&M"���灇9��$�Y��Vr"��UOո
I#�N���d�%-�8�h͐h.�2ܗ!!�&fD+Ɨ1�rQ#9kgj�-��R��� A$7�|+���Z�d�K��2�@)2���j��jk�5�~Z�&,F�
E>��LE��ե�PT%ݭ�%[㾰� n⼖�d��gy����'?�r�x���G!���Q���N#jB�Z\v���	��o?�C�����>^1�d$�}���b�"�2�c]ԓ]t��|Q�pAE�ܩ12��S.�|)Kc�Ư�0@�P�]���O(�?p���E���j�\!+�!EE*7��|5�J����+/>E�� ����U�����WvqMC-E��{��0�8�w�3|{�q����"�7
�dL�yS�,l�=��-��:�H�<8�O	V��n�|n�C^�n����Q����ѰȒ*��H�K��<��x���-�~y����~��L2x�T�N��t[I2X�
�	e��:�p6��aƩ1��.�5����C���|:Ǚ��2xρaȲǍ��K�"G��;&D5�X`
���F����U�+��^G�ٟr9o
�r9��5'��~��y�O6l ����؋$dؐĐ���1�"#�_@5��G��_A�/�?;��O/���+Ã�>%��$ܗ?�G���1��� G�	,
�FjA1Ư��R���_�/�c�8����X�o��勐�<�[�Y�ۅ���ѱE&��$���Fuj=+���ph_�!�,	k 2D�rhֆ�v�(+�פD�E�t�>.&4�6���e�V�	�X��Q���Nd�Ifd�.�ˈ�,b7	�RCV��Iۯ�=u���������p����2&�|)�aq9y*�[#+6P$,�UkP
ަ����J����^�iB�l	�7��'�xyh���cbr���E�bVY�3�j�	�
����3 \#��3/���3�xs��x�����	&|7�b5�[n
j�~��� Ib���x�~\��p�~?o<�씹8��'�aq���<vA�'6!h1d�I��	J�*���n`�>��p
�r�����NV&�>=�2!Hۿ�4MuA�b�Q�O��w	bF�m�B>)W?ɲ���ɸ�Nn\REx뀬Q�ob�u\�����I1p�!��/����v$|Nw��.�����Ƞ/���H�|+�c�͏G�v1����Ʊsr��qҼp��Z��K)��PH F���˛���G�ebr\N]����ꍏ+((�
)zV����\1���B�|?��!���;�߁^G	28�O��%��nV$ݹ`��R��
4�*Gp��˛$�܏�-�{c(/�}��_�_!� ��
;c`y�rqv3���1E��a�	1��$�@슢�$�+e�h|���G���ϔc}������1�9f����WI�&�;
��`#M��06Tھ��z�~��P8���?;��
������g$�H��cʬfP����%MiM��#��� �Q�^]bX�~T���o�l��5u�4<g�QQ$��X%�����Y�E�X�Q��<bR-A!O������m����[?۷���v�x�4_���?�3�h��s���B�U��V8� ��Z��wL������D�[���9L>v<��ڋ+4�8sp�.��SO_n�#U��K�r�(u�#���H���
��־�t���fQ��R��H��Q7�$Hcp�G,�D@��=�v�����8\֋��2���w��V�V���)�xe�Y�]5�G2cC/���$v�B]��+A]���rQ�����A�e�&@���'�G5u,6;t���J���32,���r\��Q��~�r�L����*�So��A�:���O�{������Q�ح���Mߝi�����rT�]}S0�f�5)$�$���l��:�dbA^T#b^A#7�buL��Q��Y:
��������"��0�*ɟ+r�C�+1��ŗ* �=OZ��7�����ǚ��Z�C"ŧ%���X]Ej���^� $���{*��92���AY���b+��m~hT���J�4�N��W�*X䪱���;V��ox�\�$9x��,^�O�w�R6����	I�5��&�W����X���9NT�I��}I"���#����Hf�q�2���4�݉���-�]���։�;eO��26t2ı��"J��z����WV�D��I3"����a�����j�#�9R���w5��9 �e�x���4r����r�RF�����N�
(�a.��0���ز��E���}��({"s���ާcQQB4�L����@��f�*��Y�ڬ�h�
SS�!��$7ĎH+FL�D�v����%@��s��3�����!f^@�K��G�bk;��7J)��z�Tn��qe���K��6sHKM3��vj��খ�
�v=z�R���I�	3X$f�8�'DU�i �b���-O���߮�,�J�nA�o���{T�
�3l(}ǧ�]��LF��yc��^�9�!�%UL�TQ�������G�Y~g�q�#!�Х�� �I;�	4�0^�սS�ZΣi��h�E1R>����F��	��
<F��R��'Z8���dAE��	��j�5!��Ƀ��)�
�� ��%X������X�uh�yX�~:�V=�dB�T[�O¤�
�E���xU7ƿҤ)yrW��4W(@aZ�0���W�v�<�d���>MU��0��ᱝ	RPlX1�.�@�5�$d-ulr��	0�(]فYbUKY��5�S]�zk�1s�w+���4x��"�����I(`ץ@Rj:k.������9�/%di(����è�iJ�뾶a�ƺ�<�y�4]��V|3c��-
�E���E@����]�\�j9%�<]LH�I���bB���Tl
���,A��O99mJS!L��J��9O~�amcbn�B��M@9.�'ztX��|�|����H��};bDŽ؊^<��"ʒ�Z�C�:��5�)B[�{y5A�U���y����-�Eg�Fq��A��n�-�Wm�?�5�,K0&�0<�a�9LY�w��L�>W0O��,��VP�&»�A��Y�	���ڵT4�d|�;y�L�]8!�˕Tt�6Z���,K�|h���h!�,��,i�*B��g�vP'��<?�!���}P}���Ȯ$�8p�(hA
Ew�����щ1f+���~�*X�P��;�2�=�(*G�l1�n��!hqj����}�5��Gi������#Tnj�ӭ�L�7/�fALx��h^�K��Wa]�v�s�Lb��,-�r��#1���ʂ�ժ7�]�
*)�$��!X�gC�pF���Ɔ5�
�r���mi�b�h4S�Ia��g���+G��f��
~��R�����N8�^J�e�>
7!;�@�֪�P�5ֽI��KF$�B�e��×��Wd�B�ٞ�ޠ8MA*����a]b0��'^eX;�4^S��<o��['$K��VOhU�DJ�U�6$����8�h�30����ͯ�I�ɍq`�*�,u�!n�4�����9e�6����fÆ��g�L�D��7�kq�[�iRG�k����b�j`�1`%�a��lhB)���֦���֚�	�X��)�bD�G��h�F$ ���ky��c�_=�>�x�-8OW9���Ⱥ�:^�2��a�׃��K����z��iE�hx�9��>:�d��L)�B��a4���-'�kv,�DG��8$#p�?���5�~�y��I��Ğ�T�_m�=6�GH�!"�v^�O�H{2��.D�.r�D���k��C�����mp��C���ɍ,l��m��ZR��Z��
j�j�G):&��`.z�x���*�uk��������,J�=�O�?��l��I"�W�{j�	p�iy��]!i
�^4F�ּ��<f�2ƎB�R�W�H,}�4:���I�)^O��y�K��^8�#�Q(���?��)���q�P�|N7%�.�	<�$����$�V;
�OJjq��y�`IV�<^_L|�R��h�QQ�v1̮�A������E��vd{c�m	�(b�v�E�	@��@d���UX��VLi�Y�q�d2��E�O�I&��!�Q��b9`�;�e%��n%��hmTܝ�
j���xY�~$0Î���a��}�h 5iC��"���8��63O VW۱�0��
ےi^�����)���-��ڋ�p�8[X��%�>;�tY������~%00�	��6A��@����[�8���EhH��S �8"N�'9�ś
�u,-E$R�+]�龀��\b�<vB��N,�EZ�~��W��R�X	�p���C��Y�]a\��SQM����b\�5�n!NJ!
�X��$i]PQHj��)�>�ϒ�S��8%V�*tF"),{і��Ir��Au*6$Q�`�کd��Fjfc�
�BU��1j!튌�U�	m�ڀW��D"G�`AV�\jM�&Yp
V��SC��Z��ШJj��#Y�D�1�
8;
�7ߨ��ࡪ�f����Ŀ���$50��ĚS�md��.n�ifY���ß�Ń8_{42%H�#���7Wj

g�f$c@p��2��VGu� wB����4��kGm1(r�Ss�ŋ�'�$���#�0�@�M[a�������J �dȎ��Ցe`�Wb6�$�/�Q�v�\���2�7J�B�S��&�ܑ�aM�Eu�+&�%��/�",�čYV9��^z�ZTI��jQ�6�����rd���EE(	����O�o4#6�^>;�L�t{�{�Op�[i鰪��#��:��?�"Qc���Yq�v�Gdl�ʬ�P=k�=u�&,
�tV�|h��u&H��1��K--"���
RB�%ܤ?�^e��X�(�c�H� Fx�tk4��{�Z���Q 6��]WA��p��Lj��r����&�%���J bӯ_���c�2;�2��f�Go�T�@H�Ǧ��<b/j���(��I)��?p�/l�܆zҔ����M�.N��)��K��~"�X�J��J��1p=+�+�0�����L�|�q;�䅟"2Y^�$�5"�mR5)�� Vt��s6&�ֳ㪐R�(�fç��6��b$���W.9~�eDU;K|��R��]�j?�M"��Ж�%�D��*l��Rv��M��K/9/��I��Y�R̎��1�[w�-g�=�!����d���.J����$TH�Ͽ�⃮�@֥g��_�r��s퉖Hr{�)f�
.:Z������̍���)�4��ݾҵ�PEI�6�|t�$:g,�q"'�� MQ<B�Mn2Q
u�)�~z�+7�D��{9/#�����UصCiOn�
���E(��`�.Jl���6!�I"�=��t�]d�䴒U^f	��fE�2�@���D���I����8�:O��71�p"2ƽ������+��tc����S�r�4���\�&CP2����,�@�8ڢ�� ]���)��u����!w'��f19s@W�P��_㬲ύU���&�%�[��NH���,hԕ$�?�G0������츳�M)Ēe�G$J&�8
}��-(��GM@�5qŕb@5��ag�a����vxq��I�g��&��n �k�|d
���;R��f�%�`�4�;'6s��)�r��-
)�Г������tZ�\0�ig�#"�4�Ȫe�I�*�M�c��rN��ûiԠQ
.te���X�U��
~�=t����D�j��?�+��X��!C�M7Q
	>v��\rS��9x/�T$���QB�p
�R����lfQ&�]el|��b<����,�`	>��T���A���̵�~�=�`5b^Sքք���6�O�������r��T�Z�¦�����5���z��];���I�9+2�������EE6�>:ѓ���Um���&8�2]"�yTQ}ՠ����͢�x����b�NT�64��L7�V��61CC��ր�d�8=(쯼�T�3�O4q�9�P���/�Q�`w�ѽ6-������ �h)�مU�FD�
ȱ��W`.�
}N�Öce���|o�W1LLC?#)�E.0r�����+���(u���a�*��횤�{�����T�EP3e
�.QVO��Z���b;�.���m���rqA��l�K"L��YU�3wt�I����D��ԃ��9�B1��`�L��0(�R�����].CJ��%�~A���f��@�d*��
(�o{PmJ��k��;���wA�nňH��?���1�s<||��q5��%H�c5f"r&`£`ۨ�Pu��J���v~�c��C���D9�h3�w�e�,IPа�U^��46��2�C��=��Ǯf�c���<�Rk�b �kn�V"�ۯ]_�u,��-	�3-�Rg�̷U��hϸ��*��wK!F^�G�l|Lq$q 1H\�/�[�w��[�P������r����3�0D��g��-]�M&�h4Dģ�<������E��̒�4f�4i�)n�$���o��c$ύ���Edb�lq�q�>k�Zi�A "N��y��e�
"�Ȍ���4�I*ݶ�+_i��Y�-�=����2�$�����E��W��b��)N�і`K%��ω��^��#�c+܂i1��C�,��܍��Jd�	��g�H��ijC�䬡�#�)�1��+Q��%
�#�͊�1�\�#{�����UY@���Z
i19>(M�8�`d�Y’�v͇$\W�
�Z)ڀ:��*�9�&:KUV��L��P����+Jhc�5M8�S1&<�l��e�Di'pv�ke���m�j�A?�J��ؒ�r"91Ur�U��s3mA�`:k�7\$�)��9Yfw\L�e�Y�Aˋ���
g���5��v���d�B���C��:.z�2:�=�j4����S�Dðt��,�;+H�� ��W7�M�>����F�i���ː��&��d� P��
h�
X|@��a����T�A�.c+d4����i2*�Z��]�������,q��EiL�zL�&�[,$*�U��X�
�v��<��rY!�ܑ��Ɛ�#FȂ�C�T���	 ���a
O���^I��(�*.�1a!Q-��
NƟ�=tw^�5N��+	,�sĈ�@]�kRO��xX�-E����������QY�+}�=��YP��Q��i�8��"@j��`r��.w"fHҞ�$�R�T�;��Wo�]G)����L	0C���n^nLe�c8�I�pf��W+J��)�}u�3�ɱ��nI�	��[�O�UDg���&"��Z2��~Z�L~8Ҽ�k0.S2d�OH���e
o�CT1��(MI�M䄬~�m"�"�%љQL���296M�j�z5kZP
��Ŗ���ʀ�N����\or�K$�,�q�#+|@��T�ע�:��
<ed\�������X"�`���$1ҟ�tH�%�q�Uq���+�yk-�]_B(Uhv�4�Arj����%b��v��B�٘^=�Q^��c��g���&����#��v$F��"�ݥq�e&�,@�+��5s�Z��RAֆlQW�1�*�U��ݠ.h��Mv�t��U�X�@��~K6Q���\�`�"�fc�F�P��cֺ�.�������c^|z�����M���RX����m`k��kP�ē�S7����ϛ!�k3�M,�R8�@IU7�%׭N�'���n(�,G�(�v��%��
ѿ&d�̬�B�bѯ���$
��&�.�#��9�Ҙ��W\x�|	{��~�TQ�}N�H�M?
K&1
Y<dJL��}ng��<��K1Z�ȡ�Q�*�!�`OΚ����|'h�!�U\x9܌%��\����F!u�h�#W�X�ijH��Di�(�-2&sLQv�)\J� @΢��Ԏ��W�$�~�@�W!��%��Ce��V)���$���z���%ޟVT̕�|/"�+��07�|o��͂�r�˽'��(�ᖄP����v95x���1�?�0�8��'��X ��U��@Y؀���M�c(<�̋��\���Ō��<�̪���-�����H5.�M������䛿��IG}��Qo��u����%�3]��O9�?'��fb�\�AiAU�p7���-7s����@,����lcQ_�,Ap*��TPP�}B!P�TL:ϓ��/n)l$��F�ئ���4�+�ɑ�:&�@{*ly��f�hԺ�6�ە���Z�'���bR��:(�@^>\$��D�t�
���l �oC�uya��	�R��=��&L���I*�UB���
tt���#!,��? ���H��#uUA��V���;k8�y*���ӳ�X��!R)C#��P=I?�0T.�#}<~���C#��2a�.Фd�"_a6�H#���"A��R�D�1�dw�Gr12��p���$�}4��oDI
�LxD���[�D��e	
6�(@���&�E�ULX�D��\A"1��`�H
҄
iOZ�{~� ��>C�&VD�B\	e��ʨ����_M�Աܓ��N�%Õ+�Ťh�ʥ��\�Yz�q��Е��bc��l�C�
���<̓T\�]���*��T�������
�ZN�tLN��|��s�#�h�nieV��{�@��ԊR��bL��{PF��<H8�?����&v��{(N�uw@��a�v5:�q�4m.��9J�2�r�,���sk*��p����JPu�j�K
%����������>,3Yp->9Uy+J�@Q��妆8��\Eӑo��r9����ȓ�e���1����Pkhk��jQ�$VO�;|���q��-�c}��o����¬��J��E6R���D�V��,���ӯ�����O ��f�7&sĵŎ>�������Q@)]����;�O��3FD��W�wr��K
J�*�`iJ���v�k�D���vV�6�����X���M&B�QCR���KBV��Jo]Wh�{�T��e�>|x���\�<P)
L@~׸�Cj�6SMy��
�d<(
�bHB��<܉8��/)�/)����܅c�Z^��f%��7:[���x�h�b���݋�q�>C�jUrK�(@�+�R[��
�k<H$�Y�7����E᜖/)���]�.H��y`w�UF̥��"�
�:�4�X�.7��L�[�;��v�4��%�H�P���j��1��4
��(���S���0�(:�ݰB	��P���걙VK��W4�y3�����$���#[��**+�P��,�U�#E��?c����@P9k����҂��q�F{)ʯEّɗ�fk�(��"3!
q,f4�K�&Eˆ���h�j��a�!E��"�\�I�T�}<R�eqQ�<f<8q�FzML�+�w/ ���܁О��ɄL*]��x����e~OƓ?�ʋ��"�{Y9X�T�l�p6�B~GF}����W��!�EX8v�ax�J&f6��n����s�;�F;�&95�dr�nK��.q<Yɐe�\+��#B��	����J&02�!�U@����&��2H�41�Bv�#RV��{�[�4�	�hH�P�k�Գ$	^{nG/@���Hc�)�����V�I���E��Ј^{ʲ9sx�G;:^.l�����UEpԴV��܎��S�k��/i1���_�ل
�@-�k�7+IJ8���$�L9�P�[H�I���߆���&���Z�0��2b���K��G�c���lU��Y�i��Je)��������|�w�cåW���_ �F��Cd�T��z����М	�a�]	
��'1�G5��'j��H���H�u�/�PEv;�:�qE��3,�a�X�c�P�<�,�hȭJT�EH�߯]n��R�1<֟%���:e�>raĽ����O� �@$
�-��.@��H�t�58�ܚV��Ĉ�]^��#���6�l_A�)|U00�䐨Ux�"9XF�T�R�]Hp��[\L�4iǴ�rH�P4�Sa�Q�ס�;�z���U��dG�s���GD���6�z��}K&6�?�ћ�g`s��<yy��2Y�B�&H��U���7&���B��P��͋��4Ť�o�г�b�F�`(}u-ޞI��O���1�nS$�ܺc�ᅝ�2�Ԟ��͗�1�\�U�*
k6��L��1W�)ZPbT#�-����]ƫ0M.�
�ւS�0�]a�vN�AR����_�����.�T���)�&|�8�{b�_AZV���q44u\Dn�$�*���a�f$X��Ү(��j�#%���+A_�w�3܉O�h8��"#[�yVl�� �k)ؗp�IEV�@�~��5:�,�:��1ZyQ�:�;qޒ�oU&���lk�]<r�e9D�	>�;�شY��;�1s�
�͸F�NB��b
�3�ʒ��4�_Ȍ���m��}�u҉�@�Ӆ�������$l��
�Wmd}�%�d�v#��a6c�ҁU�Tר���X���k*��<���Nj�cQ�cW�
KXH"��w�m��T@�M�XM�w1p䴻��1����0�D?A@+�;WT�K0X!�f梼��šijm������t��HyÛ��P����C��AҶNg~��90��e
�r�4#
�q���&4�S����x��,���HӸ�1�hEo���&X⌤l::�!)�D7��y<�|~3���?{"y�$[T�����VCJ�JM5H�9?ȡ�BQ��g�YRq���L��(PP�BhA��j�7>�>�r1���/+
�Q�}��k��z)BzW~�Y�~~ȆR<�Ü�hR�Ɲ�60�D����J�hb��̺|�7�����O6�x\�;�`hx� �ʼn�|Y�^4ɧp%�nP�$�MHa��'�J�!k�:-x`�.lx�Sޘ���Q
^+���� �R	0)X"L�f��0C��e�8,��*Jԓ������[���X.���1�*%*I"�iN�
z��Z+�Vl�O�ϾF[3)�,4cw���������5���۟?����F&^G.<�|�;0���l�ց�:ֿ�bEߎ
1��9����j֢J�����������l�K�Y��'�8�(��h�"�&�V"��	A�C_��c���=T��g	��A"g��
B�ې��(d!�6$�i�1�a��*��K/{�코�#�T5)�AO��d�q�'�2��b� ���Q��O�=��A�t=N��u]/������FS�HT��@$����j�j�6bW�X�@��ߋ:2ӡ��Z7ڡ�A��B+�iG�==z*4�]8f�~�K�R���(X����*����E��VbH��{	��5���""-Eγ���Lt�[��Bj���!�R�I��z�3I���*3�N�F�ܡBbýH'a�:sd#u��K�`����Y'ǕRhf�T����+�����|҈��hD�R�^6w�b�qa���1��\}��ܔ�`�@J�zV�ЎC](�K��8r�)�B��E��"I�� *���)�[c�㮓�"R��Sy� Y3������fJ��I�у*��6zR4؀!Ƽ:I������ſ��$�� �"�@��վ8��IrYSE��3��R��q�D�!�g��S�ew�>?��IF$5�#ҿW]��z���I�F���r*�j�z�o�-�;��s;�c��y�RFB��F���@.�ʺ�\�&�M�QRyc��q���J���j�KHCPW��9OtH&���X�NS�g�ZY@1�߶�kJЋ�Z�a!��j�z�.�^k���y,���y!�O(�O�ʃ�;Qe9�+�Z��$�
zU��f\�,��^�ҾI0Gh%~O�.o����o;�+?�GW���B��l�>G-���"�L3)Z����?���l��A���;x�v���w~��}����O��G�����wĀE��K¸��NR��X�qXc�`N�ѵ	�q\��z��a���/��j�G�5��'?$|��x���d!�HRN{�ɛ"y����1�ń�TI2��_��_�_E�`y�/����NV^/����feM���<(sr�ċ$8��W�3ܫV��Ӣ�W������ם�{7d��G��X�x)WT{\,b�2��Mv��a��[_�L.�D��˅�p�Ƒ���ŀSvԸ�:�=���;35ra��5��3%���f7;�O#�K�_Ͽ���>�x������4��A�텝��?G�6d�YX"�Z��ڮB�7�Z�}�^�o"͡��lq|���㼧��^M��C��ʸ�Qy3%�ܭ�:�]KMja۰$����_����k�W)~�y/7?���R��|Q1��2�[���=�T"�m�/[�B>�@�=S�JT��m?d'��~c���g�1����q�o��d��l��$i�rHTA�~��;E��ƋhsR�Y����9�0�<D����`�j�8G�"vȴ�j�PM5�`���[����������$�7μ�)��74�7��q�;�lQS�fZ�Ӯ���u��S�G����i<lp�r�(����<�*݌��ȑ�Sی�*��R��ji�ɎG��/�>�y>���.[���rS��x�LeO��I|v&d$�&�f���dS,r�s`P1��.y��y�_���7��帙yn&1�sa�ySO�1n�
r55��Z�D����3����g��y�'��~G��<�b��WC�H��A��ǖFR0ϱe�h��B��2�}>�^+��^o��-�}�_�eq#���"��QO%6g�L\lXV�c�Tc{�̬�`��5����^o��O�	�/���/�\/��ݰ��?��Qd�C�!��X67�Yqf����}wo*�7/�VV������¦�s�{+;~�Ƥ,��ݵg��Ͻ���ނ���$n<��h+���o���<���� ���D�,��Rl|�M╥u[��i!��4D?u��@܂������v�x.(|�����L����m�^Iɯ'�ȯ�Чs�n";��5Qk;��&b6"P�zf���3�e�R6:�������7������,pǑ���Eb���'c�Z5�6��:�"e�I�?�����e�.?'��i#�>�+Ɗ�.�$��e���~�ց��*�"�Le�Lx�XԾrC$�1�#��G�$���g*�Ր����ş.�[����x�Wp^�֔�Zq�
T�ATb���\/���H�ѫ#)��Z
kT�J����n,��iV%gFH��T���y��,�Z1��Uy.c<��!%�Vx�dY��7f�o�N���}�A/vG�}���{�۫^�R��~��驿�tO���yʭ���u/|�Gv=�lS�PO�S�:�1&�!�^Hc@��&rcC��/<a�t(�S]�hn-�:�7��v���]·m-�DDxt�!��Jдe��A#j��t0� ��d\�Z�L �E�E4���l��oK�>ָ�;(���\%��xR�Ȓ�故��~屔�`��o�px�T7�b{X��y��N�������VgVڢ���N!0ZR����Ă@@ip�� �ΞHr��\M�Dk]�J�O���0�bH�U��,�l��O1�Z<R�H�D��� �A��c��J���4�C�v0s���o��v�?��E�@8��_�7�͋H�Ѫ�D��Z�q��}5c(Ńi^?I%ʶ@閑v��;��������()��$���DJ_�V�>�?[5v�v�m�1��;���\v.�x�&@�)Ù��ƠT��[����xn�����L
(��	7wk�N�G<��V)���4׮秦��r�W���2^S$�v�ҡ�1`	�;V����P��B9��H�0c�Z/��X�
I���OᣖQj0��c����L$���1�1[}YH#zm]y�s�Z�N_!��Y������.���ɷڜcv)����K����QUXk]��z�m���)Ǫf^w���0�!�R��*�����v���
��P�3.���Y`|�J�$ȹ
��������H���$�
��野���A܅@6;�)C����@VW-Ȏ	��qc�Dz;\U@zu,iN�˟(�u��n)�9�fY��eA�v���l�E�erQ�mhA�	�y5�,.�2`2}�E�c�(� �VXW������E�M*
S�W�L�<�TPCW��BK��n$
��i�oڔ�rƍ�9�l��Y�av`QA���]B����?-csL�V��$E��n(�k��+�4=������#�y&ˊh�Z���X�AC�:Ӑ�Hf�Q�Ԥ�>����祁V��
�aO�t�@��
RT���I�|f��B�P�EU:2TV�l> �m��n���EY_><��x��a�����A"����a/��G�����W�Ϥ �UP�S/�����D���}!��"|���\�?p*���*�Z��鬃(�����eN9�=d�y��-�v�~�kM�A����F�KG��2�n͔fHZ�i�`���w�^���	�/+'1��w5�g�p���_O��Y��"Uf��QH4~:9^qKw2�d	<��@�|��Ï��#�79Q#��chH�F*�M�]�%��0N%AO��C�S��.�(���me���6�u������@��k��v I��A�����i����(��'0,h��ȏ9')J��ȑB��a%��~�Ūh)��K��L�4[���k5]1������4Y/�"��3���jPԚP��Ҁ��L[։#-�G��|N1����7��0�6���[���m���X�	���[rN2����+
�I��F�#dY��B�jV����6���Jf����`+<҇2\B;��(h�?	鬹&q�9MTGu�Kfgh�7�dqn��&�p��PV��묲��6�Uh�Fu����?
�Me�
4��ijU�~���2���׏ĉ=ז�G���$�5Y�����F䂪IP@����7$�!��r��Ӌ�rD��<�g�L���`��4>�K��m�3���^.�������T��܎|/�a�sq�s͋4�T��Q���9�r��i����3��Ϣ�u�FLu��K�\|����PcG	1!UJ��;���`)u���df͍�c�yI��$H�y���R�ࢍ��Q��:��w�)�ܫ��
����+�l�*w1�^��#��֫��4�:��:8�|�?ɲ<�+���?'��'��Ǐ*�NF4s,]��
:ȭ����>$|�x>YҦ7�[u^�n��{>c\|<���ɖ�1DP��Ƶ1���Mzq!�!�I=j�b���i�K�\�yF��d�֟�N��6�PYKk��+�+������H�@2� ��4�A"�A����	'��>쉵��ݣJ�S�|�~={9K
Ct�5ԍ��[R�Б���׸��˟A�'+���6Lr��cG!V.�[.j�$Ѐz�BF�r!���E�f|��
Mk�*�K6Ȅ�@.����R+��M�<�۵���\ds9N��M�
�JF�[J�t"�S]����VFD�zytT�b0�Ԓ��|i����f��q�bmj�Q@7�����x!�GPmn�8��VT|w(�\(�"����5I؍�P���G�ʂcE�s�p�L��+	�(�K*�'q*.Y=��R�ޱ����bH�&N?0�Z��qd0�,��z���u9*�U�G/��F�S�᪢bG
	�D�^�����i����&�ѫ�'��W�����N�.Ia}�j���PE��n�|5�Y�,
��觉̚b�N9���U��w��P+�kJl7�4�$� ���زZ��-@•�{���6�`I$������q�"�,�{q�����6���h�
]v��YBW����U�!���7�|��i��N�}D��P�ljcKZ�P
�|�bc�~,����;q��#�X2Tk��eNխ>�}�o�g`r�Ni�d��5ZH�ʡPZH�SQ��P2�$��AhE���r=��X�jlzl	�4�5B�W�N$����J��f}�	�������� 7N�b<o�$kr)
n
�I�@��]��\r��f��?E����[���;��@w�+K�D�����\q(� ���UV�F�bN��P���(�oEkg0>4��̄,�M���J�� m�~]3��h�u�u��+e�<a�@"��Z �7,O�:��s�{r�kU�U�����dlNѕ\R�#�Q��1��1�l��l�΢���Hf�<,�mV_�Qif
�:P����ķ�$�#P���#e5B���v���+����x��M��.��Y3�̖s,�w�rT�UE����#b	ڵ��Ŵ�L��2N`Fb�1��r;��ݒӗ6C�]Y���@,uf�	i^�˪s
�
E|H��5�ZH(Iu�[\��$�=7�c׭P$˗��L<nDy&c:�&<�R2���7UAv!R����O�16��	��&����@R:�%б*j*k@z
���3�ҫ��R�0�,E�V�)U5��B=*t����D�!���C"L�dǑ�Pr���*�aV���>Z�	;��eŶ��5:Fr�L֍0�ЂU�w����P}A?-]��A�>^F���V4�����;�
�z�9�t�C�q%��U%D���"�[��?��F@RJdh�����ȗ;(�34̶B���!?Q�m���w.l~���I^C��Y��H�X�JLw+�)cS�f���i����b
�b砓/�&1k��i�@�}}*i��W/�M�Nas�����إ�x-4J�)�Wbzm^��$&j��
!r����.O�!�Y�jQ��Ē6��Z�"��H�L�'	��|��l�ǖD���f��WpMm"�o]�57N�+f&?qZ�j�2*��0��톌���~k�������r]�$O��C�W@Cn�mPu�9ə�H�N "���P��ǎ %pD���5;���N�}^[IaR�E��� ���g�|�i]��s�	U-v��F�����YK���q��>sG���0�J�Tu��Z\���UX��w	�FL\b�34�U{�4�z���}7o
�ƿ���Y>7��xχq>/�S�����[�&T��#�j�}MI뤌�@�鶹$P'ew�2Hc�'k`%�"%�#�������7�R���bc���47�x��C��mg���6�����^���bBT&TYp�B��X�"��$��A��6�]	@�l-���!G<���#v�Ջ��ܓ%�6$��w��Q��^l��R3��6���T�i�/CC]S,���K��͕��eO����P*I�w��L�b%�T����YP���ݓ���˽�֤�$Wmy�����ϒY��B��H�fF89բ�,�ʌAd)"��hEî�1̒�1ѫ꫾TW��\Cg>L�Nn1�(Q�:8�jSq���W�Kl!�&@?�(��w$<T�B���ke�ס�k�駞"'_�A�ʨ|��ʙ؜M��88���ܴDZvR!w;�]G;��oaG�h5	W����|�ˑ��,XڡO�g6h4���/�	śעY1���ey_�3���L��E�C��}…� t��'y�T�g'�iʪ�2� �5���Z|�5�r�dH�d1u�F#&���U�Z�:~�rj}8�&p
���9q��pȘ���.�C,��V���Z�{��j0p\j���gO
�e X��sH�[��1n����)�N�)�+`+��|f9�a�L�*)��5�4$��O�����J����9bH�)�;�"Hމ$SO$N�R�)�+���b�♛{�WA��yt�3��[�VlGW�a�**Ue��	�T|��>Yc'�;qE�BD�^�|?��s%vl̨�q�� Œ9YU�G�UU�3�|5�D<l���_N���`��GtN�n�D��HT5�_���|.I�񲶋s�Y�!�p�$^�h�H�!���CM��5|���F,�3�u��Ƒg�`�?�K�<����F"��~������ơ�j�1j/	�>]���G#����䟔����"8��us)FT/�uK�
��A�`��ՆD��b'�CN?���O4�2$r{�Y��#�7��q�,�w�_�^ˋ�\Y��c�֙%Pb:-�M��T�c�Ll��d�U�9+<6<H%he)
���;j@pf��k�p~;]���y|�|2��+��Vwk#�����Z�V��u߮�w�����9���#��<h�P V!Sa**�G�~^��|�Z�c��VW�Q�1pB�wvYT�$��T���[oJt�k,s��Ui@�����q��Fc����:�c�4�l!��oZ���Yq����y��=����Ь\$��Y&I$�!}�b6�ɵN�צ�c���JR��C���p�<�"��!�,.���#a]�T�o]����tf��lL|~33&y�.Bb𤑒��Ite�pJܬnSB�u8�Jb]�d��bKcE�$�Ig�h��~�n��U3���b$Q-�.vY\� X9)�hU��+�ܔ%�p"��}g? ��P����+K��N'���y��!\ōQ$g���ڵ��;}р�'<قL�eANH���eF��FpIP��"SAZ�p�o��K.�'�ت@RQ#)*{`��XV���N�J�P�y*T��NmvJ�ЕSM�͸��>�5BK:��/!`�8̌�����-��T�6�S��mD�Khq�٢�\�e�n�n�Z���o$��v۲~l~�{n��6Q	�R��k��t�1/�	L�r��� �q�SȸXY6d��)JF�U
��j�]vyQ6 ��S��D������hY�"Yh����H�L�t����i�*TC|���[��Z�ԍf;��^j� �^����St6��@e*7�S�]{X��<�	�d���LJ2Yň�,(���!,N�S�� ���������!a�ф�@��k�;ִ�4N�_D�G�g��+��U�}�,�$�!�1��F ���5!�.E\і9U>c�ƞ)��?;����#d%n!���{I����&���Kl��[ٹr$i*�<�EU#�Yv���j��
m�&���#�Ç��̡s�
Yg��#�\
	�t�w�S^|a)v�A&@5����Y��X%�=;-iB��Y��n��Z��ѴP�B����I���QDKy{I"�j	�"eB��=�͎	Ry��d
��$"��ܐ��Zq%�]NP,B6.L�q�q#v�	r�UؐX���>z���碎�%�>C�ɄL�a`,�����S96��F12�6|x�⩍�&�X����p��S_ǧM8/B���]c����ߊ̄wRR[	p�T��;��X�{�j��_�l�UU�Ŏm�T��� fSF#bJ�+��9�*�Z#0rL��%%�� h�n�*Y]�ժ�;�z�J@��@'��q�sO$HH@]������_�먌��+]Sa%���K)����.��@�"���-����?�F��%���L�x�,��U#���[��=�P4�g�%��$�-���ݜ��e�l̓?h�-s�ڷ����cDz@���tdL�;+�.G�r(
�e1v
$�Uz����޴�t�%+�!,���}��|���/�s�X�p��f�N\�\�H^��%��S�Ҡv0e��J�Jd�����c7����n
M.>��Z�6�J#�'�+e�Cm"�[(���wJ������"��i��Q
d���fnڞ�5*�E����'cM(��Iv�@-���N��S�%+ڔ�f�`XҦ�u�e�#V�Ъ5���9��3ɉ�ˉ�#R�L��7
Fn
�����Y�p	���Ǩ�m��σ���ET��I-��E�j�ׯ��8�S �^��`��$�p{��\�v��j�;��P#�K�l�|����ӆ/h��c9��@v���&�Gir���cܦ�13��J�
^��W羯��Jrx �g�Y1c��9���
���U-����XƵ���LA�&YȘ/�E{��\�[�}	���c��H��,�"G��I�G���r��j�ټ��7q ��
+Q�nw�������'ň��\T�f���%}A�A��X᏶�{�Pp�x��S2��H�R����m�1�*�Nr����0M,�D";�˽�H�1$W}�k��Ā�I1וev&I#-�E�=H��iC��S؅��ְlXI`EjkM�I��奎
�4�:G-+�Pd����7?��cn�zi	g{�X��/���O�I�BY�4܂v���|�����zsY�g�����8x��3l�Lr�3
��MC��a�-���d�ّv�G�c�D�B�j"�e&5�-В��z�2�k{
��$����&)����3J�1�!P�#j
F�=vN�~ZHNCM��\�lU�3� �FUP#�1
R$���)nA��V}�N�
S�e�T����V$e!�Z�Z�)&�z���;w����b�!XZhfK�xafB���oS�4>��;���*L�?�v�4�X��T%-Q!H(7�E5�s�e@�@B��b�4Rc�aB��J�"W�8�+R�q��%����wNr/ ��Ȳ3��,
������EuQ�L*�c T�DZ�2���2�@��j���-4`OQ]�-N=��H����DNSA���<K��#�IeI��gȱ�bUIH��$�SqA�˶����EH���p3��!X��x3�M�ma�����k�����e�o�A��LLy�8\{dK��@Al�PmQ]Hᔆ�W��T��$�����29���&+�z�%XV�f�
�u����Pj��+�*B�~*,W���P��}@a'�o���Ew�C�A��rn�S��r1X��0�hG6�z����ޝw5�M��M?�aB�ɛ?���?��+�����J�^֓�Tnz�S#�|�0iJ�V�,<�ɡY�y�"8�"��{o��A
F��>Z�H1����	8F����r~�lj!~�%��1"��S���N�v�<R��6_��nЎт�L��T�z��T� ��R�|Ә/��O�ѣ�p��R�E@$���Z��7q�.?�9���+���z`ʓ)"5���^թ�&���2�M��x�7@�;��v�8�����v������%��2!g�����4(GȆ1�YdiX(*�f`
M7��,� 3�w�U�1$�R�D�fg.x�8V�=�+�$F�A�,�j>'Z>iN{@,�(�`E�r�NI�՞,�w_��Bp��"i����‘��7(P}(~:��M��v&�!p~Y��m0!]C�@�h�#�1W*�$���x{��igO�
9�.��QƎ<<d�i2%	�̊�2�@,Oϭu�`�n��%/�y~w����^'��O$K�,LB$KHiK�1�+J�]�u�s
+�5�1�nq|��nN��*��F��ֶ�qe��Q� ��%⼋��e�9^N(G�$rc��CG<0c���b̲��1�im��̃DP��q���Lqc刱� [<I`SO���b~}7�c
&�Y�Ҙ��r~9��gc��s���q=�#2E4T!��I�3 A��3Mpy�ngr8)��p�-%�Ί���c����C�	����,�'����r�a�<F��h�K�0=��"�X�P�`O�Z�n��Q��_��~Ja�d�f��9n �
�֝jt�����J;�^Q�,r�l�x
/(`�5�5>�)�%���5V�gdˁU��2��w>��UGƂ��i�������|&Fl�Q�bday��Cw꺁��O�ף��@+&@pȓ&x;���N@�d����-�Z�jr�"�舍��y�/�&�Ì	q�c�1�ݠi�j���q]y��8�<���lBW/l^Ko,�D�H�˛�	���NĀ���E8�Μ�����J던��yb�K��7J%���erԧ��@K)yH����`�&2��]��̠�����]k�	E�;��e�$T
V�NjɃ2L��W��,aĉ�"�H�C���̙3�B0��F:����8�^��:�F�Кt�jq��5�Y#.|��l���)dGd��U�A{��
փ�:�!ij& ��ʙ�ɆS���R�c
���4b�
h�/U90O<��f����y����L~�nǦ�ገ�3��y)X8�.;�V�(����:zW�֩F�r>�\l<���tl����Gi�5e=zJֿ
v�K��+����G+��a!70kmZ�Et�-	C�9	|��|e�e�|���
![��;���z��k,�Y�cud�8�����R���+gO�?=N�`ȘJ�O���<��J�Ֆ�Z����@1w%J�d���Y=�3�U�̎��E�,̝�������>q#��ܯ�NJq&�A��C91�S���YMTֆ�O�^�,�[���i�Ð��$K�A&s<��HcR�]�U�I�lk���&-}�n��'�o�������/�.\nW��'�A��F4�G ee�j7�n0b
�8!k�T1T2ɹ��lz��z�S��x�'�e�ȇ��%�<�`��(#�`R�.��L_UPC����x�d|�n��+�U䌲�B��(	ќ6�'�����VG)�s<h���>�TS��F ��Z�Tm�]e�9��S��B1��D�9L'��x�V��2�Sk�G�2�pm����ǚ2,$?+���j�8H"��Hq��`�֫9����V��H�����9����FCC��QO�@�����B��i]���.Km!q,�0H�s3�_1�K���$�¢�V��K�%��](^A?6	�.Bw�C$}�\�b~�z|5�a�;�Oytg��ȋ�f��C9��#�h���8�PF��L@�tT�9aX"�y�&&R��؋@���hʴ�����,2ޔ�O�c��NJIhJ��㺴�RĒw5n��ڦ�1��d%�G7�#��+*�[i���7��,?!_#�ė��<�d�+BH77����@�!�&���ř�\,G����8R(�~���L6���I9LK´P$�.6��[_�N�ҀV�S=�wDf�7'�.(�
�k�h���8��c J����Ī��P�U�2��RZ�Q�@�.��FD��A�㒠��.���aI
�Cp�]]�]Ϧ�×&�������7���xŋ"\�d�J��a(4F
�U[��
��i�1���d# @o�Y�?�q�,�RGzPD�	�==?�X���&jp�N��x�~*L$�VS�rq#y�V:�P�ڇ��[AsGY&	<Qa����\��?�%�i�f(m�QPw5�B3�Hz�$F������S�+�QF���%����G�WWvWâO�u|�qJ�%�Ɩ,�� ��}�k��`)�U1��س�=�c�!ȹ\��]�F�jﶳc�A�a�VP����!�+�����H�
��N��ֈ2€)L�������|@w���O2�/B�5	r:ir��q�j
�X�W��ۋU�U!��.U}��ᣗ�1���C/4��gp��NC��,��#wTH�ɵ@�W�Ԏ���(x���Q ���ط�y�fa������_����o%i_$�2����܄���H�V�H�o�L´լ=J׆�����s��?/�O��>���&]�>G?�2}?ߨ�X���w	4�H��'�c������q�Z�ۇ���2�g�Wo+�XS�NS��Xa����I�&ȓrj��Z�WX3>i�OAa��@�@U�?4������3���\v�|ec�"乬�"4�Ͷ�� �JMz�n���{�W���x>-�&
pq��Fl�Y%rd��X�S@I�-��C쏧������l��C6vʉ�ձ2",ɔf�\3I(��Sר=5�$�Q��A��W`H����5d�9�b�A�GB�J�T�Z�$��]*�Eg��' r�ܸI�b���_s 
��+П���@s�ۧ�(�`�|��7��t8�S�W�G/&�</ �#7v6���^
�Ǖ���I��# \3[�Z�͚������,�o����>9����l�[ճ���f�#�#�|�eiC[U\T�����t�x�K��2�������~����5�Y"v�/)�6Y�;�wgcP
y�>�D̻WϞ8CE~��|+�.:@��nێ�e����jև^�{hc�W�d�e��u�����>~V\�1	�V�JnfC $\h�����&F5�'{���KE���%IJx���[%��Nf_
o)$X��ReE�A1H��p���
�:S3 ' �$lx�$E|[�τ����;�ۏ����F�E��c�{�l\��h��9!��Q}	�\}��۠���^�k-��.�|k�q���<��2e@�8����7n8��sG<1��KnL'�	��Mk���ˀ���^��'ߎ/����No	�K���w���Li��0�.9�1,����j��mǹ��/a�읭h�u�x��~_/''�ӂ��Z�h#�23J�c�4���B�1Ypڄ��� ��5ri�*Ca�gyN#��'�ᡉ��>q��<�>F,k���$Ы�qL�I�Wq��S�b�#Uv����y��r����xg��x9����PS#�2��G,]�4��kXR��v2#�_�<#���k�3��.^�|x��F�;��UA'�u5<r�a�q�<�^l���!���i�>5�.o:��f��2�3�Y�;��	�ߠ4e�8�����Bp�AZ�ly>��sO����qG'�6�/�'�,&�S�&v6nw��q_���h����*�?�#�y��dc��#���7��Le
J�ɖ��5EB�G]S�N��?�E��A������I�>�dP����sѧpf
�ʪ�M���~u������,+�o�e�]bd��UT2c�%���
A������+��Y<���DhF������@�›V����㔄�2� V�!>Kd�k[���yd�CCh�$��_N��+�P����z����N��Ӵ�(��w`
H6\���X��j;+��W��	#�'e�M��Ω+�Y��ܠ��T�����X#��\SU����d���I�^����w�ٖ���,����{�>��m�_4Aï�s�6<�B�S$uk��
	��
�k��=�����.HI�.^/-;w̉ H�w5�R��joR5���2����h��%���#ɕ,�bG�픯p�E���]�5қ���b	g�Q��b�di�
&Df�yh^Fd�Ҕ��x�;t��(��W��\D�����E{qeG$�UT���ԟM��.�
�Z���$9h!�$!�DwH���k�'���G�J[��g����a�k?�?����X���� �+��W��3��jI�
h|��lj�Vz�OE���2�cc����L�O��3���[���<�K�$�Q�P)d���
��~�$��&� ���P X>�y!˒4H��5X�p.h�����c]o�dh�Y�Ew�^Rv��
N��"�Tơk�_}I��P�	�Ր�VY�q�Ꮝ$�Q$+݇`�v��Q�b��z����NУ�]��

��%�vh)^���+����QV[K�Ǟs���#4ES�R0h�PT�zՈ{I&�g���ܔq�|�}vL�RSm�
d`h-P@��B)��c���'e��bE��c���8Fb鱥N�Sc���i.��-��}k�ZT�-�J� U�C���������M�Vqd�h�6\�P�w�P��f7�Ϧ�Y�z,�����͛�)Z��]E���?zjq���>T9w
�D-6f^[3B��Z(�1��5k�)s����
Cq��Xs�s�x�ȌH��r��E�T��V�>��K�
�>K��	;��,.�s%7�'��u�D�u� i'e�����fvj�*_�W�˒P9����yW0��Xg��2I�$9Z�
E��	n���c�.l��"��	�>���P�'��{+'����dR���4aㄯ����v�}�t�����<j��^�߂>.n!�A<S�s#�3V(F�I�Ԛ���F�-[����k̩L��/�B!�I�{|c$2ڌ1�K�X�^N�=u,�|��*����8|`cLRK*M$w�E�mA���ӽM7�l`D��7$�Y�9�7�?�7����9�S��p�lIg��<��j�G_p;��u�S��jq����A��^K��%��'![�˒-�K�p�p��Ρ�b*h4���'��?!t��1�^�&8�L�),���:�z�
���#9���(�T�\���B"W�ݨ��m�����"��T�$p`�B�2�U1���⭪A'ro�Y#ɇڤ��^o'8?;����Nf<+��yA
X����h��@�m�:�<�2G'N���ZD}���������8H����4T`�k�X�v�Zc
Р`�f^�h�0q��6~��U��v��T��4���0`$cR>�9gKr\�x���:E �p��h�(V���I>�v}Q��%�1[Ig�c���J+[Km4 ҄m�=4��c�J1��*]f;>l铓l��i\�Ic�ږ��_���/�_>;dO͏?rf�1�JbQOtmr=}j7�=S��ƴso�=�X�9�������i�W�j����uC/y*`Q�g�C͎8�1�� �K�f1���wڴ;G6�q�<��򜅫��(l��"��u=�4_���|��RkQ�TB6<fy.^TbGW���*PP���
w��fjEU {.n]�Y'�d�Z@=�p,T��S]�F�Id`�)�G�S�9��lj�BD�L��TY������	;���D6��SG㢮278�}V�-��$qq�;����$��*�����A�E=u�.\�m���X�Mep��>2Hչ.W3As$�}4f���E	ju���=�lh���c𸼗5��Ysd�>8L`���Z����AMp����Ve�+��\G�$J�B����aR����W`OSO�=�#��1!]�9����6eO�����:�C-��.$)�V�~Z��K\�d��y����7�6�Y�#�P��$Z���h!i��u�@	�>*H�S��q�զ�D�|ٳN�ESٽI=w��9-Ω�^a�x�~W�ϝ���	,�A4L�A���ƄC�ן@����R����8U̅�8q2M:4h·oh؅u5�u��}��T�U���ئ�1"|����A ��Q��U@V�:k7u�Ć��� .����L������WGMFU���%	H[�5�d�In!��V10}��C3���a�$<��b6Q"!N��ڱF�K[�P��{\���>0%|����8�QrH�t��U��A��kL�H]��]/�I�+�͒H��O�]%�s�?Q5���Y�\+�zlJs��G��D��\��h��P�ִӀ��}�ϖ�oc��$ݬ�BV�TQ��Xi_�5�;�d"fe`Ë��F���UP.bг���5�@ɷ��S�?�O��B`�O�Up�d`���H�o�S�v̆�#^b��G?�[���d�.��J�Ѵ��-����T��ɚ&DE4a )��##*�!��	$�V*�0;��O�Cq$:�,�C������rLKJ�$��,���T��i^�5�X����?#�����{�b�B䲝����?窍�.MԪ.�8~=����@C�&.%�8&�P�u=�>�]�lX�2Kx��ng'�NBeʀ�L��kM���I=G� ����Ĥ<���a��r�
(t��,sZ
P
�N�: W9Giq[LT��D�*�j�����N�i��*!WNFr ����
 �.J���7ܚ�j��,�j���ٲ���)��7ɳW��5,x��d��c��W�����9XQXM�R���m�OP7���8� $(��ع� O(1D��
	$ԝ�z��I�ᓩ���F�dp�;ȭB�Z]�kt29P1)�y<��Q�iĶ:2䩮H�Ċ�-C~;hL{����f#�EÇ7�l"K/mU���56pI�m��]��)dX!��_AfDEa0��XV�֪���h@�=N~=��4���R�������b���*��f�-�o𩦰bj�ʼ�V�Y��r��5���2CKhY��ګ�U�k��ں����-׊��j���$���-M��4'}��N$ �K��<̹99�YT��8���($!$������FE�,
b��Xg��I���X���+^�S�t�f�o�h�5�?�<�c����ĤI�x��`y71��.o)��aj,o��Z�*u��>C�J�ՙ�kt���Fx#1����W�E�N9�2�K��h݅	���b�[@7��i�_��cU�I���&V4Rd#��>�Ih*���hڕ����D˚Jڠ��Ark� Թ��Wq��T���2�Ĵ��xe�Y+N,���I�(�G�:'m��
�ʴ�FOZ�T!�*I���J�d;���V1d�¾�XV�V�K%�g�Ȟe5�bM�$��p,%&�T�Auv�Ǧ���
��D�F>E���
�)P.�XC-�Q�E(~Z�!�>䏑rѯ-D*��-sJ�����G���d�Q>(Q�Y�3��9i#^�2�¡����BGpw�Ȋ,�W�l�V�^�l�$*V�r'���ž�۪�g��G
<|?�X�d53�Di*�5J-h����"#h����I.���H�ei"�o��j��
�Q��.�#SD�9�EyRKD��K��������"l.�k�@�%��fP�_f�Q�.$�����2©HY~e��v��q��%��&;H#��CB�q���~�6X���wGc��ㄏ����x&�w�v���3K�,�)��kX)���~:���k��K�,r�
,,����sQ�,�I n�6J�ϐ�*����Q�'U'�Y�ڟCd��]�G)#�ħ�|]���߱��"�5�3����V!ܴ'���+�$u
�+�SC�*:�H d凉r["H�k,�)�����Ɨ�9�����ɋ"YOo���a�FzF���5\y[��S�J���C�ɍ�(<y��)50dQ��]r��P�t�3�c�y#xu�������o6�'=Ŵ�\�քˎ�U����U�����! Ԏ>�S�%Ŋ���c�T�B�p=U��
��m�
��u�S���b�3��eBؑ(.�b���V�sQAv�~'YrԆoǂ�h�?�f���l�^h����b�{��X�N�jEEG���^��#�WeE��O͇>V[�ypC�̄���T�����hk��0��bLkv㒤�T�w��S�F^�,�DIUAG`-"�F$�z��� "�E����,$K2c>V��q#N�:ƱF��~��*5�rK�x�rW����d���،塪�1=�uqM��Ünk�ӂ��Y���Jq]�I�3K̪�gR.a�+_�M�9��U�k��l�&s.U�Y#,KU@��Z��P=v�d��5��V�+��8Ǘ��EI$V��a;]��ӗ���e�;�Xk�Ƭ
IZ1z�P��mJ�{ja��O�����1���V,q'F�X�_b�WԒkw��L��$���V�bC��O'�(L=�p(Ty,�j�Y�v;���,�ؖ���V0��]��yaI,��䍚FpA%F�rOCA��M�\q��0Ud^vL��%��`�*Z"�݉�T�:�'G�=Qx�DnX�9~C7��%8dH�腨([�To�]��$C6��R1��<���M�2B�2����!���A�:lyrO��4��q��9+:�Ǘ)lsc��c�<|�,W)?�=Ehz	��J[_^I�f���3���+G�oa�ybv�[U�ա"�t���ۂ@w�FHo5��b��c�d�dLL����c.2�e7��u;��,��X��{'��.F^Q�ؙP��B-�@�D����Z��ph�d1J�ŏ���
Qpe`�"!��,uB��
M����Gt���B��8(�yQ�|����)���Ƶ�6��}ī[]�kqb��W�*>�'����ᡕ|��rj�qr��(M}M(�#�s2J����8�9���29�|`KI#"ܠ�
��ԍ�]�c8�@��u������+S�����oi�FEju�����&��sE����#`��ఢ���3r�W�IMEI.����� �|�(i�S��K��[&g'���"��Y�Ο-�jAA.^��R����a�D^|�.���~@�DPÌ�ZeZwj��i��@����P�)K�]<BeY31r�hr���TFg�+3!S�qZ���~�ֿ��u�3����Gj|��t��G�d؊��_]z��x��$��G�����^Z"�z�B�Lv]��B��i��e$���?,�����>Lu��*�$Ks`����^vX��GU�y`�yg�q�4ˆD�`2�g��20�v�kQ�������bL����c�$���UYX0�Y
� 6�B��š���YRQТG&/�|����\F��
Z�� uj��ۮ�d�#�D�)Q;�i�0sC)zE"�*e
*�Ai[X��ь�i-R���`I�nj�u3�+C4q��5u�d�R��oN��:|#$M(�Oi[�d6\X���̒��*���n����1
��C���NV0��IR(ia�5+j����m,@����^;��>�@q�R��#��I�A��:-`�%���4q���N�p���贸�6>�m�.�I`Σ/�����e�YC,l�hU��A��҃}vs��]t�7���w���3����DIds�
=6��/n`!�"��8�t2�5,��:��c�d�rF&b���9�*�m�[���A����~a6�e�y�Q�I�Lv��J	(Y^�!}��5�.q�H�S�����r��c�#y�#"���&h�[����i�IN���DE/����qx��d�a��M��C��b������7�j$N2
��|މ��N�~|5VS�Y���?��b����nj�1E���x�JW�b�:�,Y0d'��Z���h�X�BD��|�bp��X]�S�n�jƓ%vd���g汉J/�a�t�-���Ќ�qX��F_qC��z鏴(;���<�:H>�6[㍏p���5+Ju��&_y�5]R%��
|�!�.<ip���2�+����Z��B$r��(�	t�n.D36~3[4n�"*�¬��AM�5yĉoR�9�;#I�YdL~�Ve(�_k\�b*v�� y�����P_#���ܒ�!f���
�b@>��JQ�v��D3�A�n%�o�X���|�X9,��@�2	Y��hi���3��O�.���91ɑP;�ZP8V6�\�W��mP�$��W� �8��:q�����Hdty�(��54>��.���h�B��ɚO�����H�$�H%,Am��
^��"�Nt5W��#"8�ǻF#��K	�4*E@�zm�����8���t���L�ɏ�튴R�k�d�z
�v�~:���:y�>��7(8Y�g�6l�����ڱ�Q%
7Qw�c�8@�WHJE�	<�e����q&R�{�+�R�,`-��7����ĠZ#��O�&�Cq��S��p�G{�{e4�}��h�j�2D� 	5lW@�G/��ggv�FG$��@�S�]p��'�D6�X���|ܜ���>=������x��O���)���!ŏ"���ߐ�5��p���`,� $\����퉍���`f`Yh�3{�J�O]�t��,�yx'��]|���)Q�8�6�H��J�!��:�3K�f5
�䩰mt�粒��ud�9[�J2��>�����S�j��#<e�;ɇ+����Ejkw���2/�@����Dh1q�I'͉ZUycD,w^��ܞ�������C�t��/���$k�đCI���#�6�)Zҵ������`nU��8�9�%x�F.c7#%�K#��Kܒ�v��ޠ�묘;��jW&,��?�u���3b̵ST�
��Rݨ)��,�}�ߥ8?��в��&�,�� 1�B�Y��ܐ)�O��.9,�"Ytx��&P�b����A�W��V�4�=5x�\ꐓNH���d�^�9˽��
�һt�գ#N�O���H�2{��1X��7�kU��G&�����0O���E��QJ���㦐O�K�j��Y�c�#�v�V��"-I^��A�j?��"�Nq�K��IU�j=��0�u
.*
�6��i9
@��ʇ�^�N�y��M�!����mJ�#�twƯ�&5�Q�	�$��qd
�()�S��]��Tt�U'�c�d:n���A5Bh�lj����0��%��q�yq�J�!Y�m*��[`Jїv�A�l��F�eXH�w%\n4pd,��FX�lRI�z��i���Y	̥�Rk2������+P�_�V��5`�pz�⺦�+r.q���N����5+RI�V�u|�E\1K�!��q�KaXٓ���5Ȍ����Ԋ|�(b��χ���{+��q�.pl��y�k�srա6����9N ۜ��w�D�w�LNJ|9_d��fm��`
}v�%�H3�M"��^�x���t�4��Ve`��)V�(v문�0�[ir.������4�b�\�>��\���IAn�(���6�<��x֤���2�D��<>~<\�3���4I$G�9�R(�q�	��Ѝr��Pۗ��Ót�'`}�
ր�E7ښ͓���G 飏qths�4G%��CG_k^����
��]Z��:�8�x&%�����IJ%$VZ�H�N�ux����U&/�"�8���U�[�1U�;8����k1�# 55�Ug��n?�I��V�6�)ا�R�X�p��G���x�G�H���cC�b����c`�m��Ž~c�]c9L�cUv�O�&������D�a���BB"O`�Z-O��.``h�)�ĉt��p�9�q^K'[����
�!��@5#��{ߑ�o��M�QZ|o<��""��V�L���	�=�>�n�cB	�ysQ��T"���1�W�bK���?�Pz�|u��p1
�=�ÊY
.�d�~[�̛/���<�܌�"$!��Q���I�3��
h�Gd�9$|����-��$c�4p���e�KT�l~YswYM"Cذ>���CU��y'�cp��b���p�G>0���<bL���j���o�ɛ�(���T��3�'��7���_S����e.2�!4,�h�#���]���'~r���*
Hv�5&��k#����FyK���I�XF
A�(V�� ��jj�1��������'��{�^��|��/�B���9�c��4�D�MK���Mz}�|G�
͍i�X�c��缫���a�yc��l��P�*��UbX�X�E���Ys����b�6�[�ax��edydYY�>fT��^(��$�;a�V���(�3y�R�'�׊-2�>2��<�L\�&�7Ɉj�Υ�HJ3��+��
I�
}FaK�x?yx�_��/(G�qF���H{H���aQC�x���^�m��W�!�NQ��c��E�O�<3ܜA::���SU �@@�'Z��v����75��)��S_�$y&R鏎��f�F���i�bA�_9�i�G�V��p� 7<��Z�n&�[������1��r���K�iP�4*6ڀ|t�#) �~�E>�dA�����<jiy(�;��|����*I��D�d�*��&���N�3�$�=��C��`�יf���xW���G�a���>tqe$r���E<KjI��v�#K� �X�Ki��҆��-]x�^ߑ��Qa`��Q��"f|�&0�Ij��;)��Wڤ��
kFO�@v��
Cvl��7*����Nq�!��`�.d/l���x(H�;�:S�=5��4k�l/R�W+'+�\{�8D��n�yKv�0&�n����:�7Zx��#/�a�>{�f��DT��ge54�z
Sb���<cįU���{B/Hv�1g m��^��3�FNS(c!��f�0�@gj��5#��d ^�b	���5I89!�$�1 p#d ��E����k���0j�*��sӐ�ę�N���& �m2��}�O��_(5:�;��<�A��ZK$���Q��WқW��b��†I8aE��q�&'���������*��c�XڄU��|��2q�J�Ȳ��I���'�@ajХ�:�K"
B��9��%^\��FTg�M��:���Fn8�G�R�!Ñ#� B�8@��T����~���I���<C\�$*�՜�>>I2�R.��H	;`Ѐ'[��,Z���y��4丼?�F��2�����!q�֠��d0��S��+𬘜D1dN�3)��Fw�\�ܹ޻~@d�MWJ.ie��� d��|�4hXYluqq����o�0�C���x���ZfrXy(c�����$q�T��om@��ZNYD�DDH]*��崏RV6�-���T�M����4B2�D��,Qܻw@QR�]F����Q��A0�3��d�$�Ii�7d(��(S�z���x���&��*�BG�ȶvD@�]$H�[�jT������Y�)ntM7eLlI+����J��H
��+V$���MH-����0d���g����>O�ߟ`E�x1����`��ea�(�#D��4d�B:V����SK)�,-CE��/��W��x�q��?��c�7�q0.64�U� �Z-	��~:�s�;U�h�S���0��1�K3��J�uT@�����]b�eȀK��$$@B�ɒX#�iQn)�%��m;�P���z��攢/�ꌠ)�c+��������1���
��}
�AJ��l�#(�U<q �&ƻ&p1�(�P�*Q^��Z���P�%���:�``�BXH�c�Φ7��X�~�w��Z�{xHV>�C��A�E���K��gȄ�^�a;��Q�����nwi��L 2ST����%�e�vI�7���w��=ulYe(�7ǂ���LqQe��y�&S!���r��	��qCM�5\=��� ��A[9-�@f`�J�T6���;���rY@��/�A�����P+F_PI�;o]�]�d��r���RF�Y
�X�}ʤl�)�5���~c��`�TL"aɑ,�_(�j‚muv$�����/k���1nJ&·%d�(`[sK�����K8�Sd��o$�"�Q��(h�mT�]�0d��t2�>H?]�s��H-��:Z$U�+C�m݇]�ںif[y���6w����F�d�	�=k��]�@�3��q����+��b�3GJ.�*���w+�3`cH�c䠲�~�)j����L%詸�����ˋ���4�4��5V��ZN����-���Ws
�y,S�)����**����#�Џ]�1���BNN����+����u�� 
E;�M@?���6����/'��"f��u3*H�茴�z�ПO��,qp9���v?�M�*��x�̠���v'}����Ð�%M�M䓢5��h�4uQ
�N�Z�Ji'�H�U�����ɱx�#��q�?��ȕ��������Ԃhw�}�}t�Hm�|X��x�5��+ί�.\Y�I��ê��m�]��z�4�Ô�1<�	N<�&�	�7�����́d��1�efn��eB��`OŸ��b�"=���?�$�I'����H1���ca%�P Ԃz�0���~���I�dH���"<�P�)E��k�ޚ��̦Ð(�[B4Y����@�N�ȑ([�F����EE
~W�Q0	�i�`����,�|��G�%�a'��w#��A�|�!ƟqRGg��Z-Xz�=�3u^�kK��������y��#M���{��fdM%��qx�3�怂�S��(��5�'ۨUp6ʺ'��@�c��W+�<ɍ���ͅ�/�d�yd��P,s��͊Hl���Du?�x�2+w���?ۇ�`<q�7�}��<R|d�� �D���f�VBթRw�5і�'��������-�r��#��Xqi	�ʐ�$�*M
v)H�maU�)sUl.6<n&FD�"V��$dЋ�,۵��+�M)럶ݸ�yъ�Qh�w�
�H��+{�j(}�Mz��l�L�+�ݟ��'�?��Sa0�S�G������4ܳ+T]�@��k	�)�WK�/G�����/=��h�'���||�'��nG��hN:/#Q��̬�lbM�8�Xt:�*� ��/��y��1x�[��.s"n^,.>|��9<��䧋#�̠���*%N��;�s����G�~���e���?u8�N01��8qB�+B�转#\��ZH�5=���pw��q`���;_��5t"�<�c�n/��,N^�9�d���-�����c�ޠ|v�e�<�a���$1���O����\'�C���23�ȲիE`��P�1������;�B�<��kk�Uc�,-m�����~#���>5���,9�?���d'�UJ�a�(�+]ͻ��	c.�k��ЯΓ�Z8$�|�\/#̃���P©͒LrǶ��+4�.sq,��͟q�k@��/�����x'5x`��#>�
Uq��ƍc�%kV�x�e]��Jz�9������g��W��N;��<��gi���� ����t<�&��U���H�:Ƥ���'BX}�f��f��K..k'�9_2�6�D�2�F����+нI46�o�x�>�r��@����:fv�i�\�M�;9�>�y�����~�V
��z����<��w�>O�a�fy�ʯ��c8��LG7�h�PU��"p���W�u<���ď�@z��U""kP�r���|�)��V/'���r�/)�A�F[>)�c�5"���@j*�s�\��w5�<�&�2{F�:�����񏼾M�}��끛+!��C,o�f����9���3�H�U��u�]�úF���[���O�y����/��x�q\�/�,�_���>�4܀�.D�#K�J�
$kW���`���n܇?E��]���e�?�O��Gܯ��L��r���W�F�������yܤ����8����?�J�z��#"(L��?k't�@6�����O��c�?#�Cb���"��I0y)1{DZ�qԠ��%�
O�G&�@s_qC*�#Z�Mj�+j"5���
�
������]������/��y��rA�1J����+1k$a�7�t��F�.ؙܗX򍖲��C'z6G[D����W��
k�F����6C�lV�IF8lX��R��ցAOM��rK��x��y~c/'2d����U�v�$bh�V;��z�5��;OB��<
{�����7v�͗��Or�R���Z�,y��6���q�c���;(ύ��	daҥ�v�T��qf$Ԩ[�?�E˂=���K/"Q����Ä�N�qL���֔4m������Q�H�UŖz�$���'���yId�,�a������bR7"�t��;�7�Z�_���b�n<x(�'-G��b��!ȗ�+"H��]#gZ1 1���H�K�҃Nϑ4S�
�K�T�\v4>�Ԋ�_!1.y��K��y�̊$�J���a�m���7��~:��%�~��Bdy��x��,,��~��Nj?��q1��f�Y:K�~�0UI+��c�+��9���qp1��D��R,^;��,,,8Ս;i��"(��i�#��P�9~hY�f�Q�G��,BWWr������MD�Q�
JpA5_��n:>D�%�Z,���1fn� So�ץ�H3���(�h�簲�p�H����$���`����(�o�V��n5��>"-e*d����cB�Ap!�e5$�v;�);�q�c��� ���,�ԫIB�[*@�=�t��A&�<Q<�����'���\��*7n6/FtZlYj6��m�[�@b�
�]6���\8�lEg�kRIo�k��R�����3r������M�
3H��fpky"�m�����%/������:�B��5
(:ִZ
��c�3����D31\əJ)
1��E
��=�^Y��=4�^<j��r�CG���<PJ��;��7�T��;�|~Zm�/u�⋓H��=d�T ��IG��)��t"I��i�ݸ��d��{ʮ�X5GAAq_���@DT��.r�)x��]�x¶;�$����֞�ә5�:��	�
�Uu��5K	�5����w��X��U't	�5�!��k���-h*��О�oË1DYdq�O��r<���� D��	X�'Ueh�&��kA�����Mj�z9���E��X�<w/�^�e�9�#5*����sQ���f(A�X�Ə��3��x�<q��WO���A�:���hBp:ztFR5�O�b�wl����$w��"����կ��S�mh�wR$�O1c��>"�CL�6�2bJ�`�ҏ%
���!��/�Eɔ^��{-�/��%Ӹ�#��M(P4@z��Z�Rr�^8�Kr�a�/����i�H��;K*E�E���((�"OmEd�Shm�6�묰�wTۊ�,[V������H��Ul�KaJ�Qj|�SX���$h�*�����2��[!��Z��vڀP��֨�
���?
R����dC�qk��f"�d�|��"�9E��g4u���m�=�JK��?�X��ԟ�4�9{N@�O��ъ
��'�'pF���B�C��
�s�pC����I	�4�\��+:��Z�i��XH�"Kj5�!���
:kȧL[��Jcp�GnVS0XaR��B=����u�f&%��gQ���Jq3��~/�pY�r>����K��C6<�H�1L��##�2�J�_�k��k��#&��`ɖ��)�S)F�`�a���$���
=�KZ�?���g&�X�c ���J����A�-
�}ua���Q�b�aaM��2­$����F�H�Y�6��.H����j�x�e��:�ڑ���#��$�]��H;�u���P6C�L����Ľ�mY�$�
�]O$��Ճq�,	���.��8B�P�

��A���j�蕙1��ŗ77
Jq��<j���F �ZZ�$��$c�O���B}Uyo
�<?��xpe�d�10��� �$]c�P�i�>��;��E��tj(n7;+�hG�C|X�4LcW/�I?���"�I�����*d��d�������5�I��`�Me�n]�A�[�n�s�|W3)�%XM"Hj@`�$��z�Z���[�	m�U�D:��1�3�RI#�\��H$(�T0QJ�*��\�fM�ʰ���3��2���gI�f�쨪��Weh@�ޕ$�CC����',�"����
���.��r��bcY\(K*�U��+"H��
ҟ��)	T{|�`y�,��%�.V>�IZ(�rw.�
D��86�ГN����VD��R�K�s�x��#��nn%���$��q�!ZX��}��ء TSЊ�d�ji��Dq�9}G�=�����1��>���������E�zt:�,1��&ɒDUkq�gG��,��T`��H�-���j���[q�X�,��Z9��A�1ynC�3���0b��Y{FSbԊ��Q������&�">+#.v3�&$��,`%� �w��
4��.j��e�VS���X\0��R3#*֡5 Ti���Anᘳ!|5��Y��Z��w
ȁ�u���d�+��e�=��9;v)�qJ

�j
f�o�V�g���a�/L���bL�����ʤ�,#n�c��b�U1e^Ba�W�	��r��uc!�z]���u=ޜj��fO%nr`r�92F%p���G�V����ƚ�{���Uo���5�`�\Q��;!���܈�J�	bma@�?䏑#��e��s��ɏ;%"��X�eH��
������8�t֪��M�.6Dp
D�`Ȍ)$a
���>Ԧ�bA�$�fJhi�Y<��^9�[x�`y�jH����Z���q��D�84V�&�Ty�9dx��V{�rc��ZFe
�)5R@��eqEZ�d���^Pa��l$r(��z��]V,�R�h��>��,Q�Fg�Q��,M������)	�@0t��i1�����,e�dG#Wb)P)�֟�'T.9#@�������!ۥB�PԎ��h�&�QD�h���uz̄䚭��$��~�֚�9\���i�2er2���������-+Q�ԒFƔ���)����_7��6�b�)�l���8����
����N�4�H�@uC�NO;����-�|��<��{�);�����**A�5��S�B$ng믟�������X8��<utx�B��r+���} $�'m��3���p�f�$�����3H]��@
���
@��ɓ���sP,��Xb9\�RI9��8��a�$o!�~�+!�ڛ��B*�B�XЀuW�GS�KH�&��6�e�u�I���e[$H�PS���:�#"�Y�Bꙓde�4��	I�.��ݬ�O�E7�L�?�h�
�N]�nF��"�
�eN��2��v 5$z
4�b[�.��b�'�q>Q��3�����Y�CI�d�Fm&�J<�r(Ef!@��``��֪�d�rƬ�~�S�b�b"�$w�"nU��7���?秜T4@J�~3�/&�ee��R5f���
-;o�:�r;�}��PUoN����"E4n$��J�����~{��mz�4�
�+&I0�,�9g�f5z��l��"�~"�:�Y���S�(�#/77N�gY�7�f,��"� z
P�]��f~>��1���c�Q.!w�3��W������iN�
iŎ�)�HcG%���
Ĉ��C
�).��zln��#W�8�dA�R�6B��qs�͔�1ʔ��Ղ����ҍ_��Nq�y�/�M���pؑ�=ER-�
�rO_�>:�9����&A�!教J�"߶Ӵd�3#-B.����]CŐ�@��9��Ŏ�}�fZ�qF�Ս����j� �����v�Oo�#�qT�"J�!.����n(~Z����(ʂ8��YU�6	�Ro�)�&�����,�D�B�^��UW?�w��ܣr��i�m�Y��D���,R<o��X���CJ]@
:��R�w�� �NNlwI�=��*
�F�qh�?.�y�!���7�?��Ǖ�"(h$�Q�@ta�GR�Y��F5gV�,Q2%��6L%�3:(�)�J"Ch{==E	;R�4�JY(�M�N+9���V�O�Oq﹍ž�`v"���L�;]T��adn&x��\��q�bYY�+�r�ACQ�7T�F�j�Gܯ��q�	9N�=����]�
}O�j�\%��G'*��FJ����y#$Y���V
jƊ�����&Ё�]Ө%h6T�v����a�p7�^�A�:��1�~C��(yx#�\h����ح��]�
G'p"���!r�-�w"�O�B�GV7�}њSju�5<�� ��4=��#�a����f�!x�He�#��Ԋ�>���v�dÖ�譒�,k��&w&��^9�U��Y/R���a�|5��Yj5���g�/d�LL���LPd�
"%���*n	B�A�n�Ի�2i8jW�6ҋ�	g�o�iq���9#(�T,��p�
���dL�Ăb5
�h.�l`,��6;.D��=F�RHn��O��bAzqE�d}VD��?/��x�~~6?���8�fD�� GN�DPQ;������w�Њx=�J&3kX�٬ai�����-��<eJ��lZ/���47$�o���+1/�bإ� �Q=���b7�_��H޺ b�9y�d�e�VVfQyٮWjmJ駑�+�Kx�b�$�5a��d�VI�7Zƿ�����;|mPhx��CVe��3���b�"(v$��I,=�
�TWr5Y>�� {�b��lRK?mU��X�?�m�zh���;���=1$�i�TF�
��UY�!��tր��N��o��9�ʀ�YDxҗ.�#��3(���zv'R�d� �ކ�rFY��~:�g'7+����fKYrdL1���}�W��:�D��Έ�ፂ�ᤃ���X�x��7T��V���8���QX�;��y���ɱ��#!�3��8!17�jV��
t�p�r�jE�tT��$Unr�f��S ��S+@����*�[�h)�y^7�BVO��6lx�1�5��2�Ed�A
�%g��=���R3.���T���Fe��Ö.]K��	���$k*TR���X�RN#�$J��_=V�;��N"�_+�K����$r7nU�$�T����B+���}
��XyQ�s�+r�*���٭�(�^��ۡ$�M�P��`��|s������lY8R��'��a�3��&C$uU5K�ji�
g��&��Wh�z./���1alg���IfT�{T]��_M8 "�u�$�֧&�4��8m�gGj��ڄ�AJ�j�Nښ(ȇF�n;69��9/���K��(���L!���5��"�Mmd����2�M�xq�%�Xn��I8����)Pk]��O"��`�U���x|�%̔&PY�\�z�_
k��*9"�C'�\�>�7fV�T-Xq�@4�O����E��r�fF̲2B�F�;w�aX�.
B�;=)�I���y-��_��q%�x�'-c�Յ�`��m ��T u�:��y�UH��!�/�b��"���yƑ������b=��$|%�!�p,��R�
��E$XxH&�&1��p^shm�M�A�#S�/���i�
��C�숹L��yc�<{#I�-!*,��
U@#�
[7Բʌy�p^Hyv�ɂ?��H*	�����~;V�a_]<$c���LC&����p�g�f�3O��ʷk���\)@k�k�����!�&G�S��b���0#�|�$��6�`��W�}?o��v\uD��d��}T�܅�5Z�
o�B�Jl~Zx�gr��ٖ��pϔ���JL�4zB)BiN��[qL�Y���yᓓ�S	I;�1Agn0A�m�
�Gx2	�;�M+��gYR��2ȄP�BП��M1�A�)Dz�I:�o��G��~�-��ȉeZ1(M���}��B�O*_�1�l̑��n�c�hKQ���Y�����G%�Z<�cgd�q�Qr��@��*��iZt$
�k��X�G�+d��ԣX��G}��6�r%Sa.6,CR����'1#�t���k�,O!��8g�m&t�ac����-C�l9��ԝ�)�^otr_��t�a4�Or)#q-�`�Y$r�X\H��E���i���U�;\8�t�,��^Yc�4J΢���0+P?5X��&��$�%M�����v �Sw1�HªZH
���F2�5�̉K�0<|��E'H�6��Wej�pM��*��>�O���9<z��G#��2@&=�2���ڶu����a�';���ߕ�$��"F2\�H���ݛu���$b%+��^>�LX��)�,��
}ҩN��P��>��=���$��uI�P�rY�4�&�"qJMX�hbbT
u�5�8C�S1}Uq �<쪰:���,����'�:�_[}Q%�rI�C�۫�2:*�̲l��T��Ys�F�k�kS&J�5%W@XHJ������[�v�ʉ/$.:8�D�ᩀA�h�fB\�=بv�+mC���ǀ�$5����z�b�'�ωb^K*1��Dq�T�'�(v�j�b$$X9ע�M@�/7��ŞK����I�R&U 5��6 ��
+�י�JV��)��ܔ��K���y89��r�#X�Z�nI����0�$� �Po�B�'��b�ʲ��҇t���A�ץ�a�'�BE�q2r\��ɥݠ*!W��������G�@?�[$���y=��(�w���(6��K��r�Z����Ǚ�Y��e��*���+]�7Ҁm{�b�	I��%�H�.�֬���7�(֝����)H�p���hb��Y�pd^ẑ�V*?�Z����e{��东�*�K�Z�-�,�ը6�_��I�Y<�)K�%�"R�LO�i���1ܭuoO]9��2C��{��+���y��JC4jCml�g$\iJ�N�͆g���lG/_���1I�W���qy�=1[�L�� ��L��D�"��†B��m���s��"���ELs���806L���D�����EJw�w}�N� 	�I�Nh�̏�T	��<��udQ��w,X���
1@�Rk�}�_9�x�E�𛷵��Pu�8�J:���
*�6Z��f5����nO��j��i%���|)���;
V؄��x��{eF�ꏙ�p�3:��1,VZ4σ9�b2h��1�)$�+O�|Ϧ�e� 1���B�7�N�nj���cw5*YO@�v�Sǃ`�'�G&M�T�391�q�؞r��[s @@����
v��W4�oۡ(�ߢҜJ]��4�ob�"�(Ɩ����
�Q�Z+��cz~CD9@�ܜ��q����"�Ej}A��NN��4]��J�d�ܑ�(�C2�N��JMH�M*C�]g�';����S�,�$����@l�x{�@r����>z��k��(R|�����T����Z��O�Z��(��%ς����욪�)�P�S��O]t�!���+#���h�^E��$��F۫�b=���C�İ{x��d�#D�Zbc�<��l����j�m�*"��������<��3C�Ì�s�3�Z;0�JКWYs�pŠؠ���YXQ�[�,r͓�!�%�cH�)�PJҕ�ܚ
yS�"E�'�7JѽV��ͅ3�r!�s��bX���~�,0j�B	e����'�^ghDg�R�zczb�_82�G#�Fo?�v)\�&J�M�L&*�,���n6����J���/��N�u�϶3!�:j/>�0$1���ujBd�����nqN�}u��;I��c��6�Yv����|���	��f�vd��i�i��"�J�{��	�u�.?���`�8��/�Z}D�mY!P�
�zPz�F���)�����f�PVF�A
(ƈA'�hhw�������q�
 ��dG3>(kbt���G��ҌT�t��ed �y���8�w��u��l�h�J�5V�5�(M5��[#�x�z������9�r\�dƓ�K'h5
�P�A������D�b7�\�
�[��I��7 �� e1b�H�E���
;
��^�����"�4Y�M������fl�˘��bKvhiK�ڧ���JoM_�Dz�,ͧ�׀�9��'�Qs�'o#�T�5�	$(��RM}Nޚ�~��
Awҽ,�]�͙`���r��<���Y+���/i�P��v��Y?�H]J�e�N�'1�s�����Ʊ����#�{=�c�����N���+��R�坙��8yׅ�VbdUH��M(��:u�s�A�VG��d�'�x7�E�^0�fa�<��Pf�-�q}�z
�i%,b�&��u�&�Ź�J,�3(w]&�Ȕ�4o�xŊKp	rN�v
x���4���տkЁ�$/K�7
���O
K���I�fg�VQ+DT�2�j�T��b#%=�#�n�,Ĥ�Jn,�Ȳ��	��8"d���eyY]�J�;�Г��Xf�c#��>�[e��&<���12%����;r�TH��R�B]��`O���Fy1�+�r�x�HY��\
ưB}��]RkRu\��<��zZ�c�)G4�h��x*r��f7��.W��"@���ePZ���c�$�:����"RxƖ�5V����C��S�I� h�&��U��v1�ѵ�EE�^��kT"�jxW���������0��R�"pd,���P�W
��Q�h __�&�bMV~?7���<e��ܥ��@��X��Q`����T�5<��;S�S�ҤqK=t�?�����0�,��$���*Dk���*Ɨ)uN����u�w���I���[���ڀ/I����x�|(5�D��a��vS%
�:Ju���p�]e�"�;63���s�ř�n~�I�H��)1�t�
5��n�~V
FF�O��ț3�<o�	�\����U}��o��c��]R�J��5O��r��A,!����o�
T{}_]6HȀE��j���O
��Ds�y$U��f�)r7U�R�#m_�R�=��JqsLQ�?K���#�7��R)]��9,
\�Q��q��tv�, uI(rIJ�c"���:9!�:�#6<�2a�k���1$7�^
"�m�m��1�?,b�k%j��.O7��|��Ȑ��򦕏f���aم�q�"�t"�ÇII�;R�V��Qz�H�E�'�I��0�i
��
!�m��}{X��>f���%��Iy�m�@���2* �zz�91�kD��D���Q�� �A�$�X��Y���$l��qr�kJ
P
K@��I�ʮ̫��G���K+�"�K,���X
�f�jWmC.��
ϋ�C'���fy�+�q��r���GɎ��U�H��J�hOᾱ���p54��Z;\' �w�2�hXC�'&ayS�����*�m��Q]�Իn�T&5���2�~�
O��yX3������`ʏ�2.��QFF� ����ˏ,�$�Ye�h�wT0aϖ�1�Ǖ�,\X�q��2̬w�j)�:��q,*t��L76�Z�M7#����9K!dV���o��?-l �Gn�p@L�h&83r,g�5��5�a_O��6�d�gO�e�؉ 8�E}Z0�*A��OQ�j�!�)��ce��ȑTʊ8�\��Fߨ��l�k�@�W#�l4�Dy�l* �P@���t��c�l��z->��L���ڎ���P��iZz�gnLܐQ�Ѳ!Q���
���&�l�+T�B���Ի�M�{��@r��[�{oV0AC05�֤�����E"^�RBː��{�\��d!��)�:b�~��J�M6f>hQEE]�l�Se��#j�Ff@9U<@&��1��찼	#��4�ܒ�>�mO���/"Y��.�n����i�SH�䢼`�Вަ���V�D�$� �[+�“pU�x���e*	
t�JZE�����#TD%S�-�r�8��X��E�G�"�e�T2)Bƞ��q�e���ꪘ���+9�ՂСvˊ������R�� 2�E�����	yϦ��F+�/dLvf���Sw�Ab}}i�2 �@�n|p�I�5��2�	i�b*�d������רb�cv�S���r-�����C��bq}��h��1�S>�N�e!d���7 �L�Q�`h��5���b9)�En_?.x��Odi(sZU��j�c 
�XA��1�)�1-��ܺ8P�ʌ�*+Z���删�r�|3��QȊxrc.�#����o�Қ�囐Ae�O`j26o�X��)Z4�(�D�zW�v�벉�6P��4H$�h�Ǟl�<�)_p�ʤ(ـuS�����̉%JQ�:�J�Ռpl!Z�Y	��luW>IhV)�/
6��
.5��M8�%pC��c�#'��\�
$a��7�m�SJ�}K>@���*B{����������:FA^ڣ�E
�i��!в�Yy!�)$dƫBαшޟ.��7�"|nHQ���i�?I�F |i_],w]B��?)��`�X��{�c��a���@���kehI�U�xZ8p�V?� ��vS�=˛z�+���;x���9��R��2��sA$���[՜�$��S_�\&"�%�<�
~z<��b�(/�P�
�RXt�����]��x� ����9PO��m*2�ZA"����sN���o�2$6����s��m�_���<��Ǽ�>!��
�+��{�/��	F�u��e�Ld��&,FFT���F�$⸜d�q���܌��8�C*
�)ٔk�o�g���-�8,�_����Ї���+�N#TY����(�	����
��j��D綱����E9v����L�ؒ[D�6:�F�C+e��nm�$�ׄ���u�˕���:�Q�yG���1)L�)�?�
$'�&Ns!�۲�r�\�?SoJ��Z�[�k�)��w����7��ŭ��=���x�4I�1<�y\y�۰ʖ���'aI�s���&1r�<#������Ù������8�:�(U8�b5�3pr��h:���yw>쐨�Gd���x���<�r1����������8�׌�����乺�]��ϔ��Mb=*?Vq����q�0�&O�B;Y2HQ��e-E&���^�"1�`j��^D��x�������yN�����\K�9�x�'>F^B&D�8�'ru�		�Y���aF:͗���k��v�~Zʃ�+���|�r�y�xÑ����%<���lfi3yٙ�cRE=M5�Y�ɽ�ʋՎB�/�x�������8��x~d���t
������2�W�q�U�+�)c�Y,fni�^>�GdY��/���w��w��\g)�B�Ic�hrfTǏq�F$b$��y#h�QX7�qw9eq~N�M�c�`��/����
´9��6\܌D\H�l��V�tw
LV#��YԐ�j�c6W&�6\�8�j����L2�p�H�J�I��UJ�0�&�5����L��^9�2J�|�W�<�ᅬf�F&_�p\_3���IZA�>/'{h�1"&;��܌F��\���$Y������\����\_���#O��mI��Y��TQ���n��pCq�G$�Ke����S�%8�|�+yr�c�!n��q���DV̓;�C(qG6�c���*3R�\�1���ޫ�g����E��q02p��#O���ƛ���$ulčQ�=�ڙ#{h�j�I�GQn��)B��_Ƣ�+�0���BN3��Y��1I��W&�%���
�+8��Z�&1� �ե�Ɋ��vN?���>x��L�I��J��"w*�Lu�6� ��$��q�O�B���|o
�p�ȹ~Q��:e�ˈ��<�G�dTfCi���O��˛��/peMX��x�O.�m�'�,��gW�M��am��6�h�N�2b�A6K	m.�����O,ܧ#l�g�yT�(�4
�ÙbZ�m�-�H�a&��y(f�\t���l����G�e}���9.��$�L�qL�,dZ��m6U	��}AԲw�H�����+�K/� �[���x5���_��9x�����y���?��‹�cȸRhKFH
�F$�k>>�G؇��Uۨ�U��1iE�j�?���ݞ\�������y�#s ι'����R	�iף�ch�O�v_�~�}�N���O6�٠�O2�r�>#;$�����4V�IA!�:��'^��~D
FO�	)'�t��b��?�o�gx��e����a�s_�����Q!Z��T�қ~:��	��e�9�\\.W�Ȋ�>K�Ś@��j��P�sPצ��^�"D������
�t�*�
�if��T��jR5�BO�U�C�W2%�%�YP�ekVDi@B��	@z����H
#QO�:���qq���}Da��J�GB��(�Sr�ԀF���D�
�������qy9���FX�h��+x�!J���H�=Eu�w1�����1��[�7;��m�o_��Z�v�m��:�/�O��c���$w6�~����s2�b�R�{����
�'e�R�ğ��Z�6b��#�>��6D�
�\Y8HB��q��N���f4����aA��#��|R<wܿ����,��\��u�8"�r�z<�|�0fZA��V�YʃS�EiPM1	PY�֊fCC{�C.M�&FLE*�O�U���zѹ�B]��N@e�tgх1��!KMH]�*?
FD����(:��yo�#�U�g�;AEk�ATGJo��)I�e0�H��ܔ��!�㌼6Wf�7X$fXŪ�[.ؖ���d��^�����Ds�eG�<s���n⬍ieRic�E@�����f/RǪq���E���>n$��dU�c��RY�]���ۦ��@��b�M<<qlx�(�"�T�HRY\�
t�Za��J*���U�LQ�`r�ќl�1z��f�]���z�Sz���.C�46��kC5F@7v�u�9
��b�u�Z�Z!�:���VO��"NqU丸�
��`ƞ���u��5cWD�Us�_��/1@�Rh�b׭��թ��ա�p'D؀.�x�1"�L��B�u��ڊ��i�bm�_Mv��w��+���\���
K?t�+��ᷦ�<1�m�2�hx<�l���Pδ�P�V;R��ڜ{x�\U4��Kcca�:.L�$����*ڿ�m�zian�R&�Ep���U�9�l�1v��t:2��U ɲ�p�K�������
RhH5'c��8�Q��gL��A�X�Z����N�j[�W�8�����I������"<P�Y�ʈ�2z��S���>��+�sZp��Dl�7����^>s�Ê&�=�W������ SS�}�X~S|�"nVW;�>�"D�<ƒB���aU���n����ړ�5lp�c��	�˞䡎fL�(JZ6Ԡ�cC���=��wrG2I�x-|�f䥊�7z0�Ҳ���mB�����뭒&d|l|J����Z/�ece�Iƅ���.jm��j���U&A42�LA�jEb-�U��4o�
鍋��XrO��<ќq�Ҵ�(
��I��IM�>�֚�)���hvL`d����#	�2�U�*�*�#�ZI㫌�mRJ,*�?
���C{�Dk2D�K+\\���o��0�6�@G$8�i"�ZlTԦ���!�w�+��j~z�;M�q��{�j"s9��^g�fO�|sH���YK�V��_��(��I�H{i_�ߪ��y����󩉐FU�{�A��z�H�b�p:_���cc�g�B�:�P$Bʄ]�ۂ)���,���9�i�@*hVo�p8yj���|���T��qR����v��7�hØ�L.*���P`'��VĨl*��+��G�j�I���bɗ�)9"�*�f
QX��>�v�C<w���h�;Pd�;`��<���40��I"�(��XPyB֫Z
�iZ聴�F����.�k1��\��b|{�G�.$���J����Bq�?�N�V�c�fd��e�Ȩ
���掩����5���Ҟ
6vV~1��<�68��P�H��0�Of�Rj(?=9�����ӜFFT����@Ȩ�@Z���A#��հm���K$@IC����{����縍j�VUkK�ԓ����w�S�P���e��X�Q�V���B�ݎ����
	�Rm���ɍ���c��/,ѻ��P�6�z]c���jy꯰�KP���9\��(�1����p�) 

)*@bv�
[�C ��[@7I�~.\��2ɒ8�O,�ѱ!�U���7"��S�ALY�S�PDȀP�x���_./<3�̱������P�2�$V�A�b4����_4rb��ZK�D�ʰ2&ICV���i�����j���
"^G?/3��>3�I2��Y����X�YmGi1oi��AC���H����[h���bK4�:�;"��$SJuq�7��1+��b��o�s���X���"\X
�&U��0�]��V�5�e�rma���oǏk�V�ωF	2d@�c1@Ɓ	Et�
��M��tBB�b�<���rK�����r_[F��p�\j)>�SZP��R��/�Pb�gF#FZ\X�����	x\B�B�_y�6��cJ
����h�"���%���^9�0��
-������H`^B�צ��産�LQ�:ٙ�r��q�f���`�AW
�P�n�zS}4fI.��1]|�ˑXB��R��/�-O�7J�	�]P�"zr@Z�c!����a$Z�tV�{���G��N"�$��z<\�J~7H���q0�D\�کS��ݨޞ�5�-��ݖ2�*L����|��&A��*�hTSa��d���)]��8�q�Xh�V%ir��"-n�i�ו��&7��ƫv'��!yS����h�h��I�P@s��li׌��@uW�|����� �V��L��Uj�������d�(�I��r#ϋ��3���0	�X�1�*�)����FQ���ɿ+8 Ņj��c��q��`�)�@網6�c�)"��7֜�э����^^􅲰�DfW�!�1%vSZO�ᄃ8е(�M�cc�2��܎#��(��5fnݲ F\ە�I��D�e�����eÞV�9	()XݘF&�I�#j
�x	U��E����@�l8ecS$�7v�n�c�ּP��f@ZX��㬨�i��G$�V�1��hi��*B�=4(뇋�C���g�,��Q�S@GJ�Q҄m���%�Y�BA�z�iP,�;5J���?��@5M�U-�. �
\G���;J�!��#`�ߩ�Y2J"���T�7vo��\�#*#���";"3w�B,�w��ǚ4],d'��щA��#1TV�[��n(5a�j�S�G&0cM��L��_aVY��@��W���EB���ꌰ�r����G�]&�'g��̲Gx��k���]�Vrb�W�*��]�'�m�o�k��6� �|s����r9������4�M?M-����Q��rh�L/@��k�༟���ˋ��^�p9.>H�'_t3F���D+Jm�:��X�]l�8O*FiFB�}�RVPԁ��r���5}91M'rr����+q
�*-Z
D�D3Ԫ~���+ �S$������XV���}vH���K� _K,>G���P��C�F"��(x�S#��'ꠌө�}e�Yĝœ�^0P��rq���3��Mr&�mɐ*n�}��ǖ3$��d��È�.F�C��(-
Mj�7�:�)��R��tOA�$\��8�,jU
���N�N�=H����J�F4���?��ijD�w2�x�����+�$,�椓�ǐE�y#8�T�V?�hY\��`���(ƿ������7*Bd�b�ϋ,��ՙ%9o���{w6��iA�jps!��<��	_#��8]烔i�k$LE����@Mj45�_/nM�]yzQ9��X�j"X�R���*P(F�;���U�o<��!'�&�X�d���$6�`@����	F��B
�w�&4	e1ެԺ���}iJ|>Z�0e��\�&�r_��E�C��U=v���^IKߒ$�3D�M�$���˻u�zo�ً���Y�����a"���$05��C��q#�td�'�ς8���%fч[Nր)����R�P�,9�9o$��*_���dI�9[�zz_���2�j�_�Z�vAޒ?eI���	
��g��
m�Q������1��+���d���+�T�4��~�j=k�#�㬃!�h�
z�J��Z�$�<�d3C���&��X��E��M�(~:�{��$I���q��$����G�"4h������i�ˮ�1�U�5}R�B4	��v�d���E5ȩjkA�O��|[_�����y�D���K�19)�������/ƛj�)�[IG�\eόc�q�o�bs{�	�mQ���)�2yC�u���r\����<Q�
:�[\n*+O�W�e��e)�D�KE�6v>>z2!1\R���Ez�H=vK>�{	�n<|��)�87���)�ޢ�V���uQ�5�Z�]<�g����n?��L��Wr]�!̌dsq݉%�z�A�R�������#e�K�u�$�v,mf*��6[Ju:㌂�y�ѐ�CƞLyc�`�#>D�B�+I1�,�{���A@i�O������0��YxT?3=�Y2��j(�rBX�M�$�+�x0��}[_>���XZ&@���Yͦ@�VkEI?�
���h�m�R�$"���99M�c��u�O'qW��� *O�GL�]�T���f}9��q�ϑ㼢TG��w��ı�t=uH<�\Q�4K]�?6\����k3U�����ִ0��?�� wm�~�͙�G�Ǟ#,�ws%~�}�F��҇�E~'W�Aֶ��!$+�C��"�C#N����R�ة�k�	FB�~W�ˤD�Y�ŏ�4�G��;�Yቔ+�$nEv���9g��
Qʯ��^49ق4��1�@��	+�t5�5.դ���#�1 &�r��9��Ê�
_���@6�e'�"v�4o ���2����D%cC
��CX�Fہ�����[}�O��t�ӍS9�M�����q�v{�G�#�X!PC�n�5�rE�ޡ�|@���e�,�z�U�2�*cg*�����J��ZMk��j���ق�<.:��6L�s��v9q
v2�b�W�R"Z2Ȭh�}:
h�RjZ��Ǫ��Q�J�G7
%�L��{1��3���G��w��}�`�U��8�[O�N�12��3�X�P�IGsP��Ђi�a;\���9nJ�p��L.7��Վ�Ɏwɞȍq�++ƨ����!V���g� �*]�Ō����Ŗfy2yu��Ŗ��'���+�ƋQM�z�^d]�W~Ky��9��o!�G%���F8�r���
�2��
��M�i��jQǾ2.%��Žm�u����G�,CB��X�E��@5��`�̗��9�q��_|X"3B]��0j{F�7�I_A����8�k<��-��9sɟR/I��滸�*�~��"�)�X�
��b���<4�#3��.ʒ�-�i�5�L3єLkt�S��O��p��8����n.�}θB���,*��,�G��bH�1��B�̋h�Cz�})Jkf	Ks,�@e��<�WL�[�o�Y̊�YZ�X�Ͱ�$��ZMhE.�r�G.Ƌ&h�7pβČ�[^�`�����	3�� ՗��ė�͎²�I.$’�RW9Pƥ��r�@SB6��B�ֹ��<�ě?�P������8�AP��P��($v���/g>	@��~HA��K��5�u�-oҫQֿ��H�%�C�Z�)��L4Ǚ^5\IH@#!L��FC-����n:��a6�_�}V|��n?�lavN�R��*B��{ы�4bF�ֻӦ��;��fKsYr�F��3�J�bD@�Y�lV�MA$l'��?r�U����G���v�fTU�hY@MF�u0d�	�:w	���ŐO8I�<��M�ކ�Ѓ��Q�p�D��Z�Q���?��W�$�U���[m�t��=:klbb�D6�<��-(b+���q24e{,�B�����#z��5HŞ&�!��>7��D�6��/R̶�a�_J�)#�0�@j��#̮8��ȑ�w��P{��ZؓJj3�cNi�%ec�D�.I�������r��H�i#���)+)���Ct�Ieǻ�6��N�U�9X�>����x���� ��q��~�Wv�b:в*���?���f�C㗢,��.1�]����ի1zK7�0S�Z��g�"]���9|H1�qeuK`|�H�ƣ���j�����A�L\z�-x"n
�
�
����e,��u��h��0h���p�k��'.�1PEK���Hp|��Mي,b��ܹR��/��Q�E~:�2�,�U֜X��S	~����ի	����0пT�7��!��T��ܴ�A�J��Bq��ǧ��ꍓ4��c�Gp�Um�2Z�7#�N�g~�j��Dc��#�h��YG���4g�7�D����I�J��*�ᔫ9e�:ZhMպ�I9�3�x����F��Ǹ4�"7Hبbo���(A��A�:��d���(8=���1L�� i����j�}�>�""�_q�l��p�`��3,}��BC��
1
	$~>�=�=�GD��Ba���O�G�K$��i$p��BԚ�4�뫷��G�C�%�9P3q�;�O��elF7"��á҉���Mzꛋ<r؟I� �0���u���@hzl4~}�:!�����.#�'��8ɖEɌ�Q$Rmۨ�o�=z�c�b$&�}�Z1���Z�x�0���F�"�$�%,R��:j�����z}d�G^7���+9���:�
�X�CZ���^��(�:_��{����|X��NŃÐ��;`��ҤjX�	D�<�AFt�Ò��.i�*�J�t8321d�ImǸ����T��I��y..��2�}ǎ�6Ug�G�6<y����5�ے��"��F��������K�q�M��O�9D	���j�*֚uk�t'��F3�BzS?z('��ε!T5`В���П��\=��S�o�1�1�T'�b
�Eki޿�ɇE:�<h"�yp\M�>�$sz��MӦ��"2&5����\�?%.7'JE�Vō�.��ԭ�����8�!�R@P�2b94W(�t��{]���h(�*k���*��&�HZ��L�|n��Z�
����j�aJzW[%�J��&�*�����/��D!%Ԫ�h+���\�#�.	P�B��1'�;��R�d5�
i���/�DM���`Ku�u�=nd�ĸ���WҚ�<��v�>ʆ"%�0��Y�:�xLٜ�j�֕���lt�$Yv�n/4<�E7�Ző��E@7��SQ�]j"cGY|7 ��ɇ��,�ew"1^�T�P=�&,�_Ú��
J�A��l���#p���P�7��f7eL�BHqd�̋!BW�J1��93m��1��	���1��I"�~�a�K�gP�7�zz�?�.�	M�H��PLb�}	�NLx���&,
"��u�S��+�Z2S�ԡU�X���'��H,��ytsSF��5��IGk�v�)��<ҜF��p�i�hdSR)@J�n
��S�-
i
S�8�d.��GyZ�C9�S�CT�-U8 L��bGK�#��(��A�S�9�.�j�yrQp>�`?��Dq����v����4���6�:�ݒ|��r��.(�Y8���JkN�FXƉ�":%���K���u��dH��#<8��4�H�2�H��}�%UI"QF�0����NJ�ڧ�#�	�8��/#�g�Xd�H���\I����h
a�ٌ���vmZ9�X�%��rxq�|���
�3<+,T)#Gz��7(&���N8��o�c�H��G5���oʿ�^5l��DZ5s(a�`�� ���r�6Kp�-����SC6��3�2^<\EJ�=��f6�vij
�C��V�%*P�����zN8�A�͑l�ҽ�"����O�C0��,yI4U\��͟ѻFUU��5z���v����D�å�C<�% e8�P�UHV.*���;��q<@�μx�'W����q��R̨�6��!X���ON�Þ{��+f/q1ZJ9s�rق#�5��!hs�����-'�EB�Z���x�7�0:�Lи]��(ƴ����u��0�p��H(�ܼ�T܂�%P�6HR�"
�pwz������� �vM4	�u��+�"1B\�fj1�T��"���O�����2�y )5��4�O��>|P�k꩎RE��Ǟ,��+���0����J�>���%�Q�C!�ƤB&AgDž�����
�� ��s���t�a7��Xc���D�rw��ȷ�d�T�>�^��b��E���5����<ܾ�2q��킊b���u�SEZP���O��"�<qH�$�Ro/�e��㛅�@����"�ЎcV����-pT���4D��P Lc{��㼃������<�Y��E�~�(7T(��m]��)��^�3b��\|��J9a��@�|�	enq�ns �x���,�X�2����@�߅=5L?�1�I�^Z58�I>ܒl�o�����3Ǒ**	��G,��u5��#���X2��Ц,y�ˑ��dži1����;j)z�g��R+�L�� qEĴh�y�'�'��0G#���%$�1�b��a)5���nw��O� �<7_Ul0�k�)��q���s9��q#2�W&2f��p$��qEB�TPҕח����?CR�e�i����O�c�'�剹/��y�2#�6��UV���K0p@j�S�<�c�5����F>�H��y�w#����$���V���i�3�e�	ZT��|�w,��fS�Tn#����2�LH�2H��&H�������5�bu��$���y�^G�Y%φA֣cD�+[#��E�S�C&��tI(�6^�o���͏�<@%��v�޵ۮ���	�e��p(��l��g�Q��,=�f�i(���z�&$�Փ�u���21��i�]̲R��"ЫKO�I��Z{~�s��\��z�"~W�a1EBR�*Ƃ����2�d)mx�iF[ce������1�����
T~o�D��B���d<���ըhʪ�]�Mj@���!��pj��e��8v����� 
����&��׮��V-��k7O�=�l<4H1�3c-�1��o�m�������z,�2'�.T�����1J�*��3�UT݁
i]����.V���"�*x�2d�E��vd��� [c��q�$�	ؓ�^�ˏ&�E�n>��⚤���_��L|0����e���Zє6��ؚR�Բ��r|�i�_%\y�CGT�!�r2�l���,�,��fY
���j�Zjr�,��l�F6��ʤa\}z�x��
�g�W&��&DP��3a���܀w��oU.� v��j�\�߷4�d�bׂ)B
�ڵ�֞��(K�)a�E~��fY����Zl�?�%�	��;|l�K�o��H���U���8�b7
	�P7���t��:��Զ\d��̉*��1�P��O�tԎ&��Q�H�M��D��H�U�$P-I�u�xѕeT�|n�lo��ܠ~�؂tՐJe������sQ��f_k�E�v�m³P���X�2&�QZ[�ae��c&,�v�*�qw (45&�׀��
,��\�����i�.C	��h-�`�{K.�k�5G$}�w�2;i�8��J�"�/%j�Q�TnV�V����)�,��\ߠ�8�"�1��L����c�BPG�P� �iU[������Y���-�P�(i@w��ɸ�-�X:��ĕ���ͯ"�J�k+B:�� �R��Y 7I��sU�h��3��7'ٲ"}�j�ށn4����G3C���
�x,r�l��Ĭ	z'��`?H;��5��]��B��Ń��8�+,&AkI*��O���I�	քx�E��>�8��n��E����ނ�-h���)KT�VV�K�$��X��E+_��ֆ���CqN�P+�Z\l�/�K��X�� "��6`7?-l�# կU	�.��7.6��y��E�Am�cJ����� ��|����/(~��T^�3�i.6�kа�}�tۀ�J��%���ßlVr�,$T��;R�z֔�O6�6ע1�.
O?#'(r��jv��bXTԁ���ȉ�5�M
%�.7S$��S/v��Mn5 ]�~ZB���,�������qJ\4ks4��B��*(�O%A%ۦ�R�L��I;�R:69+ddew?��SZ���ՙNq:�|��V�[�e��W�O�k�LT�åf��~���N�M#Y,�cR(]���6�Q�yo�s�/�Pc
��q^;��e<���Q�j4��$wu@���:��5C&c04�<e̗[nL����HZ�w�ڝzi� $:S>/�̑p�����
��3J�XR�B�u)�	{tOH���&93��N�wh�ox����|ux1�Tg�g�84l�������o�Z�2�*��yH�c�v�f�+�a�2F�*N��u�<�H�6+�e&K,YpJ�N���4j�-��JԎ�=F;d��@�d�p|n��~.)~K){߾I�U�[�h(m�l
���ۉj�q����&�帡���f4<t-<YNؐ�ۋ%� i=�%�T[u	*,�1�h���No�~|�s�9�f~d,��cL��$@�`��%-�{)�?��?��d%�˭�n
�����fLqM�#Lx�B���A�ڴ*A�|~�Lh��DFf��^����s.o��J�-�a�Q�,��•Y�a8����Ǣ���~kS���Q��yC�l	&$����$��h��Za��Z�oOE)��_���J�_�g�y��9Y^c�|���A1�g�@YD�o�E�č�-���`�_Q��R?���R}��%LD�e�p<A���i9�*L�XJ�Y$�0��H��A�\��(�ӂ��1��n����k�8�_=�Ǐ�u"^G,�%���sZ�N�4� �.>���
��b�{ퟂ��g�#��^Rx��������Fj�N�� �A-H���+�x�O�h��I~��n���M�&丮3;�0�T0�%T��ڵ���k�x�c�^@���6_�y��|-�g�7�&O%��,lᦑW�LRݯV�͚�jZ�V
� �.|}��0�F,�o�˼K!d�i8O��ǟ��&O3��6U@�(�{���B@v�k���]�~�gq~�Y�qM'��x|4� �y�ʙn��x����j-Dc�wL����~�Xe�7_�����1&<a21�G�6�cyHuOy$�q=i��mW�7����f�zh���b��>IqQT���…݁ �
�\��,��ǵ;�S�]1�rrcj8i_x�Xt����C�1�Yu�O���2����B���;dCYŏ���P�;
���Ǽ��p�5YS�=W��<������x������L�Bl�tl$Ȅ�R���Jʱڂ��:��6�A~�~�@��[$�ó��ò8�"�f�
�	"�,�� ���tܭF���v�.?y,+MU�}���7�y�)ì����Zx��N3�H���Q�b TV�t����@o��j���p�����bn/���w��ī�"�b�='�L�U�t�d�0g�TUe�J� �6ܙc#
�>Ԧ�SU���J�}#���x�!2������.�#䦿릔I��!7�	��br��2�3.li,c!~�]T��.�6����\���QE�<�;��g�p<�9|o��r�G�( n�H��&1���6��s��%��/���L;c"%�$��_2������-�d��aD�ܜY����*�J�P��~��n�<�؈��z�G}C�����G܏�����>��M��}g��Y(��c��.�Ȑ�Vъ�����Ĉ�h��0�u���<�>.O>�,s,����f�6n�Ƥ k3\�  �o�@@�fZ;�=9�%��/�M���<�2y	�N#8��K�es��Z��VBKT��ֺ�fYDA�_кÒ@֟u����	�O�׏}�d���c8�y��U��nF(��Y�ǎ�2'��J-K#��e�JW�4h�5M:��O��㹼'�N�r77��b�����~�e)i���I��LuX�I��M��+�3U��đ]��
���w��Le�2m^���X��I��;r�V��]�z��B�Y'=�2�5:e�r���3���ZR����Q��H$���c�2I#i^�v�=��a@T��BP���,�!�Iy8Y!��X���m�5zPt=u������h�}�g�d_�{?U���R�խ;��[�+�Jjm�4�װ�p�3b��B+&�k.;�r���o��Q��1��Y�Pi�ic�I��\,��"��W��rT�@z
��2�L]�v����o��i��/��O�p|��2[��N{c�9<q$�*�v��N��jwש�DO�˰���B���������ee4+j��r�W���e��7�$#J����J���!HTKU�ڕ�77� k6<�2� �*��s.ha�"g��*�����]��ݭP����Q�	4Y�$hIq˅�gYjK$KR�aS����hvpO�Z�& ��%�	.%�O�h�[h��P��]�I;�K�#��̎LVƛU2F�d�х��ڴ���:�|��f�N��=��+��)��K�$�c$.M�Z;.����;�7jʙqm s�vL�܌���q���H�P
DwZ�����kD�ѐ%�oֺ�����$�O�X�4��GB{�ZО��p�#U�q�����%$�$oJ=���N��uHC��%�I208���ƨ
% 
5@Z�Z7����=t��C�9`f��q��@B���G������;B_xSd��S�d��+ҀTz��?��	�����+q�����|VR�7�P����:u޺�;�)Іb���y��3���vq
���+�k�i��
�[&Q��$bdS.n={��|3���X2{iV�v;Ph� 8�($����G����"�=�)�l��$"��"����`$�!�X��G���59���2Yy�V.in522f@Ҫ<��T���?
dˌH���V1��`b�ȵ��%�Z��?Mw�R��:�q��˥2��/���$�"J̕*�J�0Zt5 �������t�¯/�`b,��B3*�Bw��j��t�1F#��2z����/�Lp��R�ŠX�җi!��@<�'�S��۔<z�66H{����q`�R�)Q��\{�����v��!gr�G48r�9RK���\d�R�c���m;t��ϻ0��_K���!fp^O�e;��2�Q�*���(,�S�>=5߿�:FAǪ�L��||�?�� �����L���;��Mw'��SUŗ6#��w����)Pܔ�܇�舑sa���V���*����ֳ�$?>>�{M�*�60�1�URꖹ�;��è�l��܈���'�0�����p�@�^L_�A`���
��N��s��yz$�(�1��<|tlVR��d���-��צ�X@0#� ry���g'�Ci���C�(����2�h���Q�g�����r#A�w!�-=��-__ZK����E��z��/@��e�*�ę;�4��	j�ۀ@�����yL��
�k$|�L���ⱻ�Y9##)ٔZ�V!�j�
,�n۴9~���;���CM����–�0���4RT��Z�h+B7�5����j_�
��b�_�q��";�T͉�9!(��-+�k�����)��缓��>�=}���{W�@c���W}�>��n�B@K_�$��J@����_!ƹ#�,��倕K�8p����]���d=�@h�i8c!~8����"��4�'���I�!�{sJ��ۍ�F���Y��Y�Z$W�⪘�c뎨|W�y'���&��V)�������ʁr��c��㲜�{q�Q�_D�<���s��<~��&� ��Hc���Bڲ��(A=5ٻ�d�0畸nHc�Vf�YG�Dž㫕��	�DsI�rL���V�Z���֚�Tt��q�鑍ݝ��&�2�?�/e�\�	��?�	q�K&2nj��QZ1
�qw���\9a1(D�E)z[�b�		b���;��_V��xu�3Rg����WD*K\�
�7��3�`���֨C�кO�O%���Ń;H3s"���"���Pw=��H#���b���Fa;�t�V�>7/e�� v�h��5�14w��h�Fm�&���]��9��g=���U��e�id�xՇ(���UN�%ME��JBF���9�'UVx�m'�U�h���PllU�Lgh�B#�A�%���+�$MW�-�R��}Ra��_2�NG�"�Ç�q�we������b���y������E�믟�'�n;��s�zp�]�/��|�~F^o�o��ǶD�Di����75�TZ��[���`ާ����lm���_�����	yN>D�n$V�6#6 f��$W+XբԓCQ��sb����j+��1���g�6w��G�N?��ß�M_�����^F�fy
���Pn&5��j���kV���S8�[���z/o��뼋�8,e���>�s&0Ve�er]���iQBk]h���ɒ1�}��yx����)o5���:��A�c�Ƚ�bY�I$��m�i�nc���1oq�2O��P������-/VD7
��S�֧O"M�P���y���L���&2K&׹�	kB
����7'V��ȱp
�H/M1�X�Nᅃ2�:D�Z
��v4צ���;�rB5I��&L�,Bv�T3oBI����V�;���b�\��7$Ia|iDM@�CZP
V�o�;��Z�Ա�e��<��̹���V'�w��޵���+���,�*�$���E��b�g�7���$+$��^�`�Ո��5,�	����\�~6�N�����؄����@+�]���y_�)�EÒ8��tA��j0��B�
�+��A�2`*�[*V�Ev�51�T�UO�n?�Ak���<r��XQ4\{N����h�i*��B��Mi�6ԡ�ΞZ�������1����)"F�BP�X�ڂ�bW6�<��#��&�d2���W�j{��7���
�!���m�%��'q��cS.�ݠ����j���ֺ��"^���P���M����,����!�ڴ��u�x	�%��T��us9,L��ƈI!ZH�%�)J5�5��u�Q,�{��X�I:�E+�8�X�-6�${�]�M5�q	�R�l�d�A�iQ�D�B�������S�O��`I6�"�6�nc��D�D�\J�.P=h���$w�a��	D������~Z����Y�@\?�rb��C<8��f�betxmP�Z�
��5�Q���V�*J�Y�p~�}����r�ݾ�<)����yH��
��3���C�ȍdSR=:u���s�ˁ�P�
�Ҥ\~:�\<Q�q"(�p�pbC-�EZ�
�S���r�.���G�#,|�7Z�>C%�x�2u�i�zkQ,p��h8b�D�K�-�y�����J{�*��G���?�I@B�B~�8�q�6z�t��=���)7uM:
�-�3�`�&�V�Yuh�S#*���M���&�pڪĶ��1��r[&<d�k)ɓ��[u
~P�~z��C��]
A9Y���9I�(�
[�=���QŀՉk�Ϝ�佽n�B-x��-�ƙ1��fX�^Wz33Ğ���]lg���㢆؛��K���lI2
'�y�|h)�򮟷���K�$5]��r���d����j:�Iꂛ֝6ק)�3��p�|y��Icr,��D-��n�����@*r|̸���gQ7�
}�ٶB��W��u�&�����[�Xqc�.����֐��`7�SK��F+���⼀#�@��nރ�ir��)�����<���6K-�h�eߢ�l	�$��H�W/*g��ے��M��,yS�+�"�v�m�J[lY���='��g��ВE*I�ڀ��&Ib,���Nj<�����	ݤ��4�
,��0��hBd/}��Ƽ]�)�� TѕA&�H���9�	�JZW�[��V�V4���<1�\�U��E��c��(�XXZ��
�m�w�9q����j�d$�O��Ǜ.h��d.jC�ʻM��=@��$jA2:���8vʖ)���ҢNĩW+p�����J���$i�V9$ȟD�EL�݃
E�@B�J���n=6�6l�Ӓލ^h9C���$c�g&��eTPM�7'}�ޛ���:]�L0�[,w�8�D7%�6/�-$z�?q��n�k�����^�bڛz��m)iwW�oʓu����#+{���x^"Hkk@�A�¿6�.�"�t�&�B�,��dR<��\����`�[���F<�#f���r�}Ú�1�A���$�cQV���w
���6Ӻ�DA��I�%�y^[�ˆ3��|6�z�p�DN6g�;�>6A�˛�p��Bb�@CI�Z�z�Zq�L�b~���$8t�pI;�r�H1�e`mRK2�����4����y�NO>B���l72n��Uv$
m�zhH����V1��:^	oi�EfU�
=7��8d����ZM���^��!"����KX�$84I"����9Y��HjO�G�f�vnF�cM��*H��ؽ�%���o��Fs�����&E5�ƙSb�G�����"�O���*Mm��eCK�ֈ�T-��g�T�(e2�*{���� ЏN�uꗋ��5���8+��^V���|\��<��bH��DH4j���:]��E^�u�8v�q�
�� �,pY�…^gf�g
D�CF�� �W}N]��A��/�w�n��?7���c�籙�~������!b�D��bҮ0�H�Y#.�-Hq�E�n���`��i"�7S!H�.^;Z9lB��W҄��k�����}�]$�PX��de{�ϛ/4o��%Zi$L��#!���ϸ���F�NS�� 6?;�:ӗ5Q�!���/K�s~)�I��(�Ŏ(��Uv�#�8W��Ԩ����s�57%�T��P�9�
<���y!��'����E�E�`��������#|	 �~B�`���W�y�C�b�O(��t�ϓa�(H�4`�8���OO��3�@'SQ�2�=���ß�x�7��{2�E� (u`i��z�{��7���^s�Q/'��^;�l�����Q�TI�Zt`�	�I�.Lt�wt�'�#*��i��A C�yƯ���XN��	j�oJ�T$
LZ��Q�mW��Ha����ٸ���y�("M�����h�zk�А�@�R8��[;`e�W
�˚!FL����ʖ7ĉ�Z��➯Q" �=>Y�K!7���kیGÆ_B�9y<s��.u���A�6�۹�Y~�E�%��j@
z9g�����#�R 0ᗗ��g�?3��P��a��L�kV��\�(��1%���cMy�)����ʟ��Bq��k"}����I��9�t7�FŤ889X�,�����$�(�����舸�#���O��y�W��o#�x<�_+��,gfƋey,����ⅾ;kOg���db�ٿ(w8�9�%�/��y^C�i�1�bv�
,]X(����鶗	�wU6A��[Y9xM����Ub���IA"Ը�t��MW|�Y��0���A���s�L��h˵Q�k����Z�m��v���]c�v�|e�⼏G�Oi�6�+E$n=綯J���dό��d�:�y#d�v��&�1E�JW���2
X*E�ynW&h2sx�0^XfI@d���J{ji@F�m�י#�����F��� ؙ�y��iq튺�jmߨ 6���4K#��k
�����0C%��ǐ�{B�ܕr
}F��~)D�Z��e�5u����"%֑�P���fn��?
Rr��Jr���fD�E�`n
���4a�E�?=dϜc�W�Vnj�fdc�됊������9E�
1��
Q�9ʋDM��x5d�*b�EI���`��E�G�V�X8���$44ZX�..2�Hm�U*�F=�7V0M�=u�c����d�,�~{��,��%7�S^��S-��s���N�y�pɧ���s�80Ɩ��>���UC�@����򦡟��dק�$"�˞[��醬(�2��E5��T�O]4{�d����@�,HMa�r�����4���2��\ME����L�ʼn!�U2���OBr��dƝ�7�-���cP�����Ghb*j5T9A4
0�R�3�4���p�{��PIm�)�ڦ<ti<��n^6Z��r���,�:�ص�x�M�נ���|p"�
&GK,nWl� �x��&#x*��I �  _�w������-�Zq"G5�W
J�ک�$I,ww՜������})��E�l��Q;��'3���6�Ʃ$��?MI��E@k��ʹ^�N/}w�._��?��\b8q��c��C�@�E(X�6;һo�G;���~�Mf"����o�0U� �)UUJ֠
v'�Y��@���I�Ky�8���X�=�d1�u"�]A_J���=�(�.�4�(e��fe���T塖"���+!uA*�I
7#��J�X%)�����m./�Mf�1.`��2,�9Q`A(�c����֛�ou�C�����Ed�'�4c��,�1c���FHH��	��
@�j"A�-j9T�[�FNds��Sd��Ћ�U�����Һ�i�vM�U/�K�j�Y'�@�I���(m������(���ʥ��)f/�ʳ9�Q��AUPN���W9�T�I�Ե����t�/,�r�#ܱ�`�F#����a��I/_ҥ��b�l�
Q�éu$2����}�^*��1��^N,[�	'�.�;4�	������묲����r�n`�br1�O���I^8n�V��ydRd"���5�����z��g�:2Г>�C��=�ƍ߲	~�@�`ɹQQ�
n3�CB�"���#P�#��/x���zz�]KcZ��gD�!2����}�:���P�?�Vp}98��Ѐ�Iet�d;���>�,oϢ�V\�Q�,xȡ��Pfu�l4K�!���۟����U7/&\�,�L|%�(b��h����`f^��$5:���2:0���1nuB�s.�C:���*Cץ����룈�������`)Ši﨡�AZ�hg��_�C>L<�FVG�3Ǒ�H�i�J�:�e���EX�	���H��G��2~�X
F{�T��6�N"A��Q$I��1��xܩ&5��bnP(*	�O��c�q�!�S�[�J���ȋ��l֑s$KX%�P�*��=�,YH��ܬ��2���x�KZۆ��w.&�@A��1r��R	��/�}���A�)g�&B���;�aeľ�8�&�r�RKd��B�����l��A5�5,X`A��RY uKɗO��i
R;�+V����n���6��ܤqe��9S+���^y�&4SAP?O���M`�b��HKp|<	2X��#gfs)�;����]��F��<�d�D�����{�%���e���(	���U@�����Y��gK�Dc��&:7�K�
Y�]�MG�m�?�.<{���"�:�iH���6O��˒�4p�UƊ>BlU��q�K{�(�����]k�r���ۢ��nǗ�7)��ړ ~�K7k��-B6�I:L�%!�]�Y�/��?35�UD%�5�I�
Xɬ�R�U������r	�����r�J�@PA�mǦ�lI��",���e���q�D��$7!�<b֌Ҥ{�A�@Ҥ�N9�
-eN>\l��2c+<2O�;
�Ҥ��S�hc�q�Ω�L�3��hb�cp�zP!P�(Ƨ��iNH�舉T�-S$eɆ?lbJ>��.4�z�OM6�U�mt�$�<�����.�iP���l6�dԹT ��a��L��w����W�^���]D	+�yO��dB�[�Ej��f���!���٘���.�m,IF���w��Ԁ5x䌢�M�,�@�����"%��dh˹����V� ��JT�5,�4�	�MuJe�q'<�>eqv��82�p&�O�Q�8���N!'b�rį,�-�YA&��j.����X���E�o��C"H�7w %H-�dP�m,(XJ�u�c"�S x���9�f�C����ɏ�񲀮B�FZ���4�𮡙��W6*��mS�G����gRIA��b"Rh�U�X�TmN����(���ӧE3K�4g��e\�C/��o#�c�23��eh�ѣz�������LKw���7Gpf��T���C�6[f���c$8�#���*��I�������tA&[��%�H�~SdO���&2�@��ڨ��D�S�Zo�20m���p]NF||�Gb��U4Z��#W��q�FY��&�Q��4�EQ;���Նe�efQ�4�� ���!2)�	01��"(�l�R�Y���s׮箓4�W�\�9��EΗ'f��l�&3�wX\��@-�6��	�N�����@�}V���hP瓂�!?	$�Ȭ$�E�3I3F�C\6�ښS�@l$��TC�w��c��f��8�ݳ��+��jYv}S�=��fnJY�t(g�>D�s��9�I���7a,�E�&��4����!����蜀�?�͗�|C����f���l�%�X�왉��F�Z�(F�����$��M�fp�Ï��Ǎ�`�6pp��<���&�k{`1��:���^�dY�Y���RL��\!���rc-�ʩ@�C-E�r���F���#�<�gI2��:�˘Pr��e�=*~Z˛�F�����q�X/��!�K
�!:�I){[(Eq��#�p� �Њc��&��N>��9�^Eiq�s��X|F>:�c��R‘<C�1��pՌ�
��M�S���f�7�,��^�Tߓ�妝2����xʕsTk�T�BI�]�����W�5,B������f4���)G���
�����n�P=4����-SV�^~:)�&�,X�Z������b+�
@55�rSe�H9����0��	��"YUn.����͓3�i���5�rAl�s�Pǜ����X�uG=��wޔ��L���LM������~5Ii��Z�-5���>ч&؆�u<�sU�y��qr��9™Y� �b�������D>�)�%tY9Iɾ|��M
��G	_�*k�$j"Gy���n]U,��S�ȟ'K�܅���F�
h,�ͳ.��һ4dX�b��z*�x���y�"gd�č%��Ԯé����fW�&�!hs�H�0J�g
WRWe@�
hN�㪐ǢG���L<I�⍦"V���}�g$�R7'�}wב݀Ѯ�f"MU�.'
L���"d�Iq�v�#ێJ�ໍ���{N�N-n_�ٲ��8�N;�i,꡻�
4�Ҥ�o�^��E*=V=��S�S1UI	���M--�pZ�=+��Z��D�;�yrqD�G�#���E�l��zkFI1ʜb�����[TԒ�s���
H�=5)H֩��X��ӱʙq�H�IZ]�Ҥ�:�]$^%��R9QV<�q٘�,J�"
���:�;�\��v�![{�iqީC�&��fɶ��y���ٺ�*˰��w�2&�T�i+�A8b��q'u�X��6��ڀ�k���<P�X���qO�Edu�u��đ3VA]%iJ�]ƲK	2�U�nq,p��Cؕj��V����
z���ף��‹.Oq�&���b�ǘ̒ՔƭGPm��փ^�&"a�s�Y�����2c5IBէ�U�����Ce�F�4hD��y�*��+ZP�
0�~�%���n����X:XZn�W�I��!�٪�'�URhL�aɑ���qƒ
�Q�1�Ҵ�E�*�S�OD��m�8蘗��dY�d���d_{�,*M���Q�#!�8 ��I1`h�/ B.*h����_�z|���*��hO��X�BK�2-���@}@��(Ԋ1���~c�č�{n�C�Ah!F�Wq�:\S&�h�5P$�x��Ӧ'�e6����=�JP�p�Բ`����B���[6,�lhԦ9F�#���N���ZтSq�R�"ħ9,���#!R[�	>ꊀmA�=i͞�*ÏҔ ����N��_2 ��\��jI`M:����	�5�W��I��$v�(ĪR�R�����4!�|d�!�/?��=�&+N�T�����iA�ҕ:�q� $���OU��s�82���+���O�q�O�Ys1pn��%)���8H� �+H��Uh(h+J���Q���k�@:'qp���*�{a���+�ܨ��*ݰ魐ųZ)J{����D%��,�rY��(&�(޻t��o��c���O�Fg'2ljo�;HPPVIL���)�O_A��<Wd��^?��+dqY)��=��1��,3���7�B�Q�k�r��A{7$���뱣������_ޑ�UJ����S;�*�6��C��xʲcd��2if+��uF
PX*�Q��龍�!Oq(�p`cqcN�͔�rI-Β.�F\��.�zJ��`K��@L�娳ZcȀ�7�1ƀ��pP�;��N�P��$ۊ�	�&x�!�'�X�D+i���X����-1���O�c|��VEy;=(�h��v�
$d+^:��h��ȑ1b�2JDf��V&�(@Fé5ރK9�@+�1W�8�J�����U�zPu#���&�)o*�1�Ŋ���X�]�lj
	�>�Y�څ��lF�@���Ă(��n0D�z҆��O�_M4r���y&�	�M����;%������ѱ�:�����$������9L��8$�o�-Q���Q��mM�묒0��,��U�% ��T�����|!�L(��7��K	㫈rZq��$"HTzҮ+i�y��^��+�?������?��ۗ��'3��/��c���8qMz�\��ř�� ҇m<����b�5��ܯد1�<#����W\X�b�yW.@	�r��l��a�YLۨg���^�G���+��'����W#�3�e��|�j]Iq���Pvc1�R
��T;�����������^:�0��e,XŤ#<p$��}֘U���c��W�Hn~�,ݿ��_�����M�?p��w��#�'�x�f΍�\<cQ�g�I*%R����]f�@�ߎ�ڞ@dH����^U�w��p�a�x��p9>1��$�6��yX\�H��ASDm��� +�q�R�Y���>?���͕�L��ca4��E�.N;*����b}�\$CG�u�O�_q��섿�<z9�Y�K��\x2�۝�&�aw*c��]�)
��1�PX�u����Jx�'��ɹ&��A�0v���Ȣ���{R��Б�{��^a“�����'	��/�y7���qO#���!B�†K���(ޚØ�>ۅ�-����������/�>IP&0�*p�J����x�l��U�(Iـ��B$	ϕ?j�j!bp?�/�/!��;8���ae�I����|�sU����n)=�kG�[k�x�[|���}��O+�9�c��$�N"K
�̘	��Ě68����Zr{@gE��Y�F��-��o'�����[bp�vp$E���.�
�вc1�HY�N��1�}����D	��x�	:����j[�JA�No��Q�^ď��	s�y!g˿蝞G^�����Ao?&&h�5W��4H���T�E.B������q�%R�~���J�%]V��?S��T�7�C繨9F��1�b2A��#⼹��̠�E787��BY\�t1�W�|��|V1�q�7,R�ϐD��!�,�����Lv2
{�PQ�:��&W"�<:����;=}��U��_�
�8��Q���X�C�1��2$�2�,�}Ѽn�	"��оH�<����e1'����և'�����S��ᥛ��%�82��/�d��f�4�S�j�P5=Q'��[���rF3+�W%?�}���~	ա����!��spY� 
�aي��j8�bEA<��K�=��xw��?��~{��}�弅r�7�	>����Ȃ��dE��ָf��=5�S�	#�C˸����G�&?��`���q�A���d��8�c��ݱ��N�v-�7��M3)Ӓ��!�#	��"��v��$��@IU]���=4���&�Ύ�N;��o����X'��ea�"WT�e.���	&�"��h���âRH^�����B��l��HE̞Җ)�~�YL������܄Y�P�]��-��+!�J�T^�o_�O�0� '��C�C�7.(0�#i�'-c�:�D"�4��jT�����0��ꙃ�dĸp�0ˇ��Ra��:4r0��Tm֛~:y
�)D��$�>/뾾�;�]ݺK�m�����]������/M�@!��˓rh�[k=
�b�V����Y���\J�"��f6F<m>aW�v�� V�*��$���a�`��L����<�V�����[we������u�և��SA�gċ$�M���YT�.�hk�jSH�nd���J�9�5q���8\��0V����HZ_��	����S�hb�8��3Y�_���o'm��ְv��FCq�_����n�$�lG���~{m��C$1�ӏ򌤓��q�y���|�*��S��-� ֦�h>:=�Q[��Tء�luQ,Y��*dc�{2�	#�f��������t���7Cji�E�2�Yf�`�-�98���\H�N�eE)�Mܐ�-�;6
���3
���Elrp���
\�I�%\�n�6�	*}=F����SWQ�\�Q0���h��v��ID!���]T	�m��qt�N<���W�y;�54��P��O]k8���BNQ��Ŗ�W"�-\
]š�iN��u�, �%L�M0��&�0����v�Eoz���AZz
R$J��qq&+?./��^1���2�<dP�ZXS���b��,�c-Z:<'�ǚ��H�D�r#R+צ��ںð5|P�A"��������S�+����,z�wo<� J�ϣ�������%�L��dfrj���_���N�4�Lk+"�w\0x�����|zh�)gC�3"����S��r���M�6�i�Lc��D������WQ���ú=�����^�SQŭ�������u���i��m�$vә� "]Ll��)~���AbA5�t;�h9&�:W��i�!ǒ��^���
XR�MiCM�Ŋx�;�����8��ˊ�䌕���H��.=}5�=�(EL�9n�t<G��cf�;��h�5+w�*�@ޛPo�����|�8��X�.C
�T���۹#bG��&y�`(��-u�s���,�'���#Lz-�+mT\6�^NP;|e��~��jt[`NB*���<�q\/1X�|�c\�T1�w��
G�Ѫ?�
y��cN2�{��>Z*�yJ$h-Ʃ���aG����Ș��˒%"503v�F�*���Wp
k��Ԏ<ci�ū�>M�ReZkD�q0b�H>�����b�6F��e��FT�B?�`;Hv���o%=�Y�op�9�^s
dl�U���x���%�h�1���R9C�W�kx��cab���,���5~�V���ޕ�sN��#?��e-&w<��p�.�r�T-�w�؂�.T�v?Nx"'�6�V�C������/��0�$�~܌
���K�������M��%ؽ���e��cG�� �x�,V�Z0���ɩ�ו�d
�G-A��@�_���/��rT��I�.[9�+P�*)���k��S)ܵ<�s4O���g,�1�2rei��
�fYi�W����y25
��|�X3X�\.;e䖐�4�Yb�o�,u�m���z��[qGh.�x�Y�'d�%���	��aC�
k
$��slT���n=u�?dr0���+cϱ�y��hr^̺G�Ĺ$��,�*�F���I�:k4{=�bh��Z%�8�J�^Ʊ2W?�D��?O���OBECz|4�(���ܒ|�!�G/�p�c�ϑVxZ<��ş�QmV�F���ڏq��ݧ�69�!���<��|m���Nj�1��
���a�΍g`��}4�����L���4!)�.8���%�!;1DI	$e�i���PQj8�I��x�e�	u>/ï@Nq�E׭�3x>?3#�{�$GY�RG{=����I�k��ר2��#��P�п%��S5��C�œ����7k�X�nٞ�`ؑ��:zꛣ�����-`Z@��|v�w����^�L��I%@�ߩ��jP�!�>v�T�<�JU�/��8�L��?//{8䔶x��*P@PԀ>:�݆<����ڠ�Bı��	�ᣔ�f�<��̖,i[Bݹ��;��^�f�O��]��!9�`X5��0�L#��2r<|ɐ���-a��0�J�%$)5*��>t��(o�^
Q-�sR|V8��AڞZ12M.On��cv@��۸:H��#����r�gey|O|�!y�A<�Qd�i�
�m��m�ϓ��3�����Uh┿�x(�\>)���qL�!��Sdj���M����^Oq������b������W��Hpa��N���:��KTIJ��*+��W��#mG%�1����J�bqe��(�0��xs!���n6K�����F;F���!3"KQ�[y���8���r4-$�c@�`����Wo�kd�d�@	��~dμ�T8P �8�ct6��6�
vQ'��H��_���CA�6p�W��"��hɩ�kA�֬rqNJ3j�����0&>7252��9ւ��T��Jj�D�B>���-k(�8�\�.D�_>�(i�W��&�V2t5�M'q�N:��,�'�ʑr�,"���%AL�	�j�U�;3{>���w\�ƫN=�?���ȐH�9�Ғc"Ć�o�Ƈ�k'm�r�V�\��ק2B�i@2Kb+�&BZ7t��Au�m�״Ӣ�ݪ����O��%�ch���W����]���J
gˊdQ0�Bk������I'!�uٙ�׫	;�����Rt��
�OWJDC�~3����#,�݋"�#�Յ�bA�N��,�A���H�|\��=�����f�\k	gY%1�@�9�E���~���`����0	�]�y4���>3��d�$��r��O���jh}���1��5��ۍ	��v\�9s�%	��r>.B�]�d�Hk}��
�5�S�9�$���>�Y�d"��Ux�1����F�;NY�,E�d�Z���0��K��	������f40��H��-u���Q�|j>z�"�~K9��g3�،nɊ�K�j�и�#U��5�!�9��.L
u�����Q�P��v4����;Hw��@�/�4x��0���K������"���.P"j�HS�l�a2\>[B�� 謠��4��cMn��ok-^dd񫕟k8*���ǧ�5��� xq�g��ҳc��*A���&y�ǾF�����j
SYg��lk���@�@�Ƅ�-��H�M�
�J��}Ev֨�m�x���eǖ�`�\UI��܈}����n9Lߢ�T��Xa�Iц�Wg
F�(5ޚI�AJ#
�O�tJ��t�v����1?���o������:�3VMr98��,Hg��U
�7#�l+����C�$�!���^F,�J�c"�H@�{�k�H�ւ�m<3D͚�]q����r�;7��eA* >�L�Q�E���Ygl�55��}��1�I��@�x�}8�F^-�<��U�l�����%X�3!zTJ��M��,I��/$I�4�A1���NJ5��	!$�Ho��$~����^�F'v����y��
MA��NQ�&�u��
8�<��V����"�k�a�`�����⁝e��L��~VD���2��L��H�>�ֈ��WS6U�0W����[&`�F_p
��a��n��ۡ�n�b�KrQɏ�v��%h�'$emŴ��=>zl���*0q�6�E4y����H�oZ=u�G�-�A��CQ�嗒���!dE}�H�XԨ4�q����Q�A��ʟ���YNN'�J�"��/P/P�@c�筘�H$�H1`���{�$sM
P���$��'T�F�ƚi�>���4���!�M�MR1$�$�r�9$��#pfb��R	Yn;���k6\�gg+�>*с�S�S��6KFGf���hj7S��շR��D��I6fL(��̪��jޝ
t�u�f`�����H	���:*��JZ��k��B�丆䥑M��L\�5�_�l���[�e͈I�Ǐ%����k�0#��c�(����e@Qf�=wޚ8�8�Қh��H�?T����Lle8
4j�P���v#Ҟ�b6M���y>6^n�ɮ��4r��@^��(2ە5��s�=��.��{(����|��,#�h�#*+��	*B����k�ї�&f:�$��(�����O$�G���YqroD�64I+��͒��^^�<�[�M�N�裇����C�N&^c��]�8%�C�;$�@Vt;�Eig��D��v/�M�R��G��s��_�Lݣ$��W��R���Z¯�45�)�}����3n_�yT�v�C�S�qX�EAX�xV(`�E��cE
��
{q���/
V��t�1�����3��3$W�}�\}�p6���9D�{%�纖YX�8n5�iY�f^�,�4 l/��jl41>(��m"��U���N_��Bd��v&�
���Bo������[��܍�fI��gE7�N��s�dQ�UA�G�u&�c�L[�u��Wa:���(��q�#�䩁%ȸ~��a�#sOO�����h������KsV�Ţ�I�1���\���q�.��P��Z��šY��x�Yf�]n�g���φY�>LdK����M�ݍ*)���d�IA5e�9��x8�?+1�o�"��]�-����w�5��>P�PZߎKl;�h�#��pbN;?% ��ƽ�]�x�iJ�!6��AJ�]v��c��K��.����������J�c��Ҽ�V��SRQ���"0)L�K��_���9��!�M61[����U��6�q�q�8�US)ɟE�)�a�1�2��‰���9�YX4Abh�%C�Z����'w�A 5[�W��;�u~�౸���Jx��/"ș6E�T]$iq������m���*��^9*��'�W��y�\1�<��?8�as
�Z+��*TF��[��!��?��A�
jZ�d�g����9���qM�>H|�DP���Dl�j��R�cN�w�\����.�?GF9�l��/�I�q��}�e1��6�"�u���î�o������;���(c�##���Xyx��̎ЭM��T���6&��N=��|d
�8b�!7��CH�~�qp�|~�Ξ#,��L���[u4+Dm�=>�a�#�ŀr�j�g�Y��#x�+�&l��ElQ噞I/`�Kw@��������٨�f1��V7��r���q2�v���,HPI�����A�v���*��f^���K3�s"�lh�'�d2�.,=�\�P�b�(@4k��%�\>T#�2.��^w¸���^l���VB�%�p2��`�L%�[���5`������<̅4��1�3���
�+3�E�zI*��:�$���rsA�˟E�@�5V����sSp�b�73�ܟ6(V���܎i�o^��|;A &�I0H��p��v�1�D3�g�$Mp�8���@�}���8��RJ@�S|66w���т��2\�c!��	��z밓V���QE�U~��li�cx2`%�c"�jj6�m�=u�<�Y�
�h|
.sf��ΙӑuX%�'$�cj��BKP������-¤��)
o/��9R��BP�������C�	5��4�묓�bI�Dg�V_3�ʶ^<x���y�$�/tk�4rT@�7�^Vx�ʔ�Z�� V�W��2��d��mA�����x֝�P�
ն�ВF��!��U�4�����Y��B%EX���"�����mE��&6���/�-���dH2�9W]��P- @���P��Қ�,r��r-���Wp!�B~
u�3u�,d��\ ��Rj*@$W���G �M��)�c��<M�^^x���q�c��R�k��?嬟'�'$�������%'�	2�ˍ!���,�"I1�t��%�&���"�FR�\�?n���55�U�19���L�0�J����$�Dk�GU4=A�Ӧ�h��P�}�C�R��\WƋ����-�c�Q�l|�TJ��,7�����j"���P�1m^�&��z��������B^�*�"BKƽ(�m�Zu֌s��
_.TS�;æ�%���%"S#<�8Ł
Ԫ�Z��'Ӯ��fuS$J�jŜ����m��v�d�ї���R�~[kP�?��Q1"�ln?3�	�xh /��'!^��e>㸡5?=t�e:S��D��I�$�Y�kG���2��Q͕��>��,@#�HbI�V�]��"�1\|~�ΝӺ)r	 
�7���d#�!�2�X�<f.#2/��v'���EH=G��~���l;\Nj.g�+�[��x!W7W�C��zt:^�S������[M	�]����`eI��\#9{��n�����c��D�DӮ��O,?DUm���3�f:������A�־�?�[M�r*s,,����o"�8�Ɏ���P}/�IX��!�S+����Жbd1u�7�ɼ�H%H��P�z�&R_v=v�$��w��s���v���)H�R����K1!iSC�t��3 h*��j�)�cLJ���f��[��O�A�ᶩ� ��JT�f�:L�J�;A�1G�b��4"�צ�S�!*ǟ_ʡ����2�����ʔL8+�4kA5���QA�;��r'��Z1FQZ|�FH�܄�]oYB������֘J$0�Dy���A'GQ
q�#�3���)�[Ԋ�v�d��׿&<�!��ɞ9,I�4�>��(�!ؠ�N�k�(� 9�@̎�K�C�;"RI$��TX�z�SZkGnv9�(�9��6��\r�b�.]mX�o�2I5
SO��kT�����!�&��ћ�j��@XJ1V�SZh�(���Y�+�aݑY�t������MWMdnRd"�EY�8L��r�_�!I -~��W	9�V4X�A$m�Iyg
�Ҧ��6�a��0�(�D�-��$
���)����n݊�����J���q��s�Lr����Y��P0��i����B�Y�Ž���0��,�46�b���_O��`MH��s3a>{�&;_��KTl�n*J�k�FA�Z#'e�	!|�e�6���)pU�@��n�z��c��H$m�-��e�3�F�+�Y�ʁ3�N����1�P�%��-5\�K��d�`b�r��jN�E���e)\,�6i�idn�d4�#X���aO�=u�/���-��|�'`�D�H�{s�?���A�^�&
��˴�譍�Q�Oat��[�$֛T�����ŵ�ABswQ0Hf�f
T��eH15�������p.(�yNnLq�ߏ��U(�/�@*w�|*M+����L)ϒӋ�
�O�e���ͅA1�؀*"�԰n���mZP�h�Ɋi�8Z��˙>@��b�vL�K�`I��;|5�c3����D�q�|P@�dF)� �c��=����H�$s"�����x8�9��3��96�@7w�6�R5��2-�2�j]Y_&���Z./�Q�&D�A�m̃�[n�w'ᾭ�c$JC�P�fǟK�qq�iЀ{�Q@�>�ۃw_���������S$�`n�o�Z��J�O^�iX9���Ln,d��2X�e��C�i`����:�A�$�ڧ4��I�r��VST��eߴ�jO]��4�v��wX�=�q<!���H�x����tǎ"3��90�ZY]�����&����F@N/n�A)�]�e4��������d�(y�/�1g�d|�ܺZ�B�Ci����"i�t@���!�3g��͓��(�A*��&RA �t$����#4���eh�
���s$� #��]PQX
�>�x�w��X���"v��-�������Sᮖ�j���1�Y䁾��@���Hȷ]Jlh4�"$�
�����9��1��8�Uq2,x���hō
�J�T�w
�4-�Q���E��q�A�֍!�c-G[J�]�j>{�Gu	t�%.>$�����22�*���������묻#��U:���e2aq��8�K�s �RK�w,Ŏ�|?=.,�@��sͺ���,���1q���%d�H��I�`��0w�Qq�M�]ƫ<�J��i�$�Iz��Y99�������LČ��άqٕK.��۠��rZ���T�".K�U���������c�UM�6Z�i �j�i��S.�觸P����$�y40Gk�P��P=��)�[�����$Y���GoXs�ȎFueI�4�m;Ҁ�1�P�RBmz��_1���fD뙒�h�w���Mz
�z�'͒�_��T�2/e~�Ǝ�%Ќ��"�!<ua �,�w�5ز�r�p:"C69��Q1�Rey��N�M
���G+�&��Q,��H�ȟU��H�Dr�ʆ�@�O�MNs��7	��K7�C.<�Y�b�-=��A�
�@h�u8�j��<2��̩?�Hث#4m;�����7��I��X����p�TNVv G�\I��U�iD1�6�և��8���Bd�Z3D��\\�a=&�_r�x�R�jM���Շ�1��u?�h��Vy�vA\y��²�2���
�6�R��w�N̖�x����3*�
$Y�͆�HڎE
Wz
�>}u�i�N��]���g>~.^q�ˇ���<9�$`��$��h)R�
�#�U�.:�.�����1b��p�.JX	d�in���M��n}4����Y':$#��n�u�-P8�QFe���X��ƒ}JSzT�z(�r�<^O'����uՍc�B�P2�+P=w�i0�	�G$e $Up00��3��d���@����uhXT�j�۸�Cq�_Ll��<lX�Qr����&� S����&�
�p(I+P��!�͕'�jE��e��jT�M*i�s �-���;��>�d��d@��n�;���묮���Z��y�F\ ��YTO�&���\�p?H4iQJ�<�C�4"�x,\2�	aa����n9��G��;3�nI?v'`�+�p|�K9� �J�
����*��Z�i�O��a�ZUf�!��cb�L�F�P���S%�5H'ou��Z$����91�g�ȶ�Ua��
.�9p���JD�X�S���|_�O�gq\W//�Iۗ��'*gF�ߪIb��7����$hB�p�C��YU�m��ȍ�,d��CP����I�&�`����D��A��$�����1@m�:�g+*L�Uprf\(-�\Vg\�dw�H"졢TVn��fښ�:�8��������^R#%��"1�K
�&��Њz
Z	�JD���9�d�蒰U�o��"�h5�1#GR2t���n0�Ets�v
5AiVS^��-ץ~Zϒ{EIa��V#q𺫌G�F$�F�^�W۳{Tn�V��@���Bi��NFNU���(ʢ��"�ݶ���K���^I8�!�ɏ>7�x��xd��(�n�Zv0�w�9Ja�T�z���|�~�P��Пv�����~
\��{�"�Uq�W!m��F�BEG_JtЍ���;~�9d�;��.R����CN���J%t�*�xXΙ�<J�N"
#�ăR=OS�D� �wN@6�-T�I�J��ݫ{gޢ��m�j�t����O�c#(Q�EB�2�e`E�t�Z��Qvt?����0!���B�t�҃�i6Y�����shE
h5�.��I�
>��`sBe�Z�a�� �A��4�h�^�&��2�Q9���#Sꦗ�V�Z�@"��ڽtq���rP���(�b������F��ԋ�E6SR	��K�[*Ab��P�Ŕ��)�tIY�9X��aO�CJj1�s�'.��eE�����Gh���G1���fg5$۰���7���#u)&d��FR`�jw��ev�����҉?�\G��1ϭ����]1*b��g�)��*zZ��!��G�����{�ꑖ�C���ks�Sҵ�����X��F�NM��
a@Ȧ��e7�D���]����b�y��b���2�I��@��Uf`�^��C����:M��<�f<fh#r�Ԟ�ő�щ���6ۮ��&���"w2��/���BV�3b�㓼�����Z�
�7����tZ�14�c�p�ɋ�wj�l��-�n��TV��=k���5L�Ġ����8���V�~�j��xab��Y4
�c�!���F=�[pY	
RjH�Ld兗�Hx�s�Y\Į3A��
R�kJ����Y0<�f�'�N���R"u��JNW#c�r�^�'&��"U�2��#f4�Z>Z�����E�6x��k|j�%�`�u����5C *�	<��w,�(*Q@��]
�dEW����(���X�ue`���]������2��gD�
C�x�'�01��L��,���G�2�.Z��DFPl~܎�)�+:�;�PTiı.4��V
���3{��4lk��zjr�*� a��éTj�)1��CJ�~?-6�
踇�YӶDQ��@�;����/���^����I"�:pf9��_ rViI$��*�l
W`B�SRI�đ�G��K�x7��d
�-%�L��l�dƆ�k�Sx�*C(�#�}]�T��I���9p�l��K�3)U+_�H�]st�7���������fI`geT"���K*vض�s���H~:�фIZ��|�!�H���-M���pGʺ�\��B��!���oَ�h����[z�@H��Yr�p��_������)�c���''��c�F��ݮW�#���VFC��� ����k$�6��<��J�{I��_�?������<7��x��y��Ƀ�`�q��sp�{1�K^l�sk5�	؍_S�n�!ߨ#�""�g�?>�����jH�乎K�W)lX2��8ܖ(+i�β���;�6ir"5cG��J0�O�Yَ7��e<�³�/#� �,�hahf�.n7�Qb�r�t��*N��d�����L����{�����q=���g
L�x�����i������'���X�����~m�<ۛ�(�3�'���y�>\�^[�����E.�dGd+0 $���H	6�٢�F�_-�|��{�P��9�����.�1F��F,��X�c���RM$"�n2�a��P�	ĸ�h}ʃ�#�����i<�'?���ȋ1�I�V�j1�(�t���F9'�5W�4�jD�����b�s�����.$c����H �#��1�^���<�Z�1@4G�|��T�7;O���>���D~�c�~o�1f�<Ǐ�`�j���,�K�*9��:zj�&2J���q����3��7�qؼl����q|?��Rγ�l��C#2����'�R5�6=��o�"�����S�UcW��8O�YW,<����e��q�6I�V�;A�צ�!f$j�������\������1�U�0|���b�X�~L�JY�U�B����t-�C��Q��}��f����˦d���s��σ��Bx���DD"$�{}Ҥ��
�g�I=��r�mk���o��t{x_+�������'�ry|3O��.N6��Q�Ώ���	.;5W��i�0��I��g�%�����<k��<��r8�W|sxxnAr�˂9_���1�I�a/��7b��}��E��8�W��SE��C����c��c���Na%�	g-"Ig�~ټ);TMy"L@5^ɬiE^W���6��8��C2,x�Q$إ�ʹ��`6�����5C7�XuD�|3���o�W�x�™q�3Ǒ��eI䙐�OkX�T#� �
�����Z,}܎�<���<���d����1��hRi��[�Ɗb��ꝫ�e���Pq�U���}������!A���7	,���0��I����}��SC�d��XQ��k/a���>g�`vQ�8�t�$q��^�>�QS��I��T�m� �$����H�|����X�.?7+�)s��Í�r��U֑յ�YO�Z���ن�h���'�������@��ñ��^F"o�/�/��D��p�RM�%:��`�枕�z��p%��Ítx�bv�	�2,�{��r�P��q��y=����&7+ə�~?F��Y�P�{F��A(]�(�T�Z��"d[�.�
�L�A��i��Rg��UU�RX���tz�F9n|ʔ��zO||Vʁ!2N�c�3HJ�Գ-	��5�␋�Y�@K)�|L�~�
$�'��q����qi�nF���`5+��+R��cǂx��j$����@����]�Ld��]0��Y_�~��o��&�?��k���+��~����/޽�=)���O�Mřz�!�19|8c�Y_7Nj�s "��t�z�6`�]��-�W��n9D]�bQ�A��B��}�$��6�G��Dȉ"�j�w�憠�����Wm4�78@U/!����\����TA\i<i��H�k��!��ʚ�{�o9��1g,d�-x�P,���ГR�F��4���8����c���Ę�	"�q"�`$�o�M�����	�!���"�UI����^�嫀wUOJ&ٰ���k$���ҁ*�v�o���$@�+�@	�x��h���b�0!��!A��}�O���£F:'�'bh(��F,D��w��j=WbEv���ϖ1j�����K��!l��h��� sD15����i�J$��邞��������{u4p	55��2è�C&�Fds&�F���_q��� �u��]����C!\vcx��	�oZ0o]�e81c��+��RO�r\dfec�R���ڥ��K�==$2K$C���D
0���\���-�,�m�b��kZ�Q��E��v�tm�������E�1���$�����) ��T�Y�G��ZL,h0�"Gl��UЋބ��k������Ft�M��+j;#�9�S1i�VT$���P�T�R\h �s$�(%]KJ ?�M.n�㈕�?��BNn�y#)��R����0�	AR�bv?-Lv�!�$�4	�]��6���	�49�jX,kW�m��~W�?Zgi)�8T��bH��g䒅.!B��2�QJ�]&b�(�%�`����;M$RCHs>�kZ�H�|F�5А7�2���#��c(E�f褧��hH�4rS�XY8Śh�B�4�}��f?���E�(���]�A�H	KȘ���]B�>���c*�-U���<���?!%1e3G$thз�ڠ)z���ǒg"�i295�h���g�/����ֈB1�\r���3�fUU7$�4�\���~v���p�w�%��n��$��b�?%ǐ�Y��K$ G�r(j�XQ�:��C ,*�NOe�9�����9L�eN9\G�Y���EU
�4j	
�X�s�=��a��t�?�W�B�qG
��Fo���\x�kn�
FJ�ޠ�����2��0�He�N��ɞ�L��x��4x�W��H��`zR��@���@�J��G<2��
9�hd�0�3�\{I����:��ۢ� �h��y8і[�\��(\�kS�a��w� 8�u��1 ���y
��xl(
�1eK�6�E�G.2C���F��y�Yۆ���!@�S���V�ު�J���
�Ma�?�Q"��Z��|��K�sIg#�C�5����$���Z�de!C�P�T�֪C�7��[��UYك�q�h�m��O�8Y���_7���9,��92Y�4�@���@?�n�)DIM�,x�C����lG�E�55�O���P��]γ�0��Ɂ�W�����(��	c�}.�+�㬹1�H�U�"Auy�ll�"X��H��ضw=�P޵�I��KQ�5]���Ɯ�����$��ʑM�]u�.l$��Z,	�����̝�Œ��8��즈�*J����������ʋH�:s+��|��N;#/:Wŏ%X"�%љ!N�P\�@�}t'�����<q�F㚞�O;�E��&�Q���+��@{d�bHAWx�ʄXM�u�'�ӧ����>��|#?;�<i����Y�23�C�eOq;��N��a�v��1H�c��[��,D�*���<(1O�>K;;cGn��R�H�F������u���D��-�Ϫ��rO��^ό�x��S�H�$�P2�S,}ʷM�~{k�Ŋ2�A�9�o�C���\���r�aXQ�!Sruy4fP���硌"	#��0'����3丿Q�c��$�$E#n�B�K3z�H�H:ś0�8��ƞ�W�/�'m׃�>�y��)2�c���ft}�#�ܵ��K��՛jV��^Fo�<�R0.Kq��z8�80���+�o��� ���|�
�0x烑a�*Ӹ�T�J�q$lK!b�f��[�-�u�]�>��MF��q1r<�&ϓ.�A82<��YH���*X�@$t����j�	��ֈ�K��tS4�ZX�*��dއs��O�M���'��qYx�G�$�6�i��k@�M�*HA�MnF�c����j��eq+eNk��#�%*
8%��nlb�]o�0��G���R9�"�~��]GjUat[v�����}g�	�K*	l�o�r.2X��_
#}�K7���-Qo��[0v�Q˘�m��y��B�>�F�`�6R��nc��F�
�j��)W�F3�"��y�Y��IvH��ޠ��$���|oM��-�,��^��O��b�S�Ԣ��&��Ys���*���<ԇvH�F"�C�%j�1����:��G��ؕ��x�g�<0A�U�P*��Xis�=>5>�c)F䲓-Ud�
yrq��uW�wE+b��;Ҟ�Û3A`B� �5F�1s����J�DL�2�"�F,�QMϮ��&e�d\z#&Y����o�Oȣ7-�ƾCdb�q��\+)�r(åv�Y��Gq���m8�+�9�,o�W��yI�L���㗙WC�rK�b��o�o�t�2O(+F��OF'ǚ��n5�>����Q�eTh�z-^���nk�����؀C�Q�~�+��3��٬��$�I��k#GV�"�
�oM�r%��v.�D��OCU*fP��nCp��T��fR�7ZSc��x�<t���3P�Aj�:kQ��$]vvHZ\Z<����(hA؀�?��R�&����+�6D.���mH@��iJ�7�#"X��tneZ8_9B�*�� ��(�@ޛ���t"C�O���1	�F��@D�h��-�ܪ{�_Mf���5?^��Yn4�n�"��D ,2 �"��1��Jښہ�-��>I(xn7�|�r\�<�{ʣ�RE"�V�R�Q]
�~���D�>p�21��庖RU� n��~(��y,���q����i$,�o��c+��5��H >]��nZ��ܻ?"Lܹ"����ڌ.,
I���K�{��4"�g�/.nF"�V쭋#)4i^Gڦ�57�چ8o$�5Iɀ+��y|I��1�N���^���H��3�$'�(�&Q��Y&�*oV����7�:��z�J죑��kqZ5͉�N���ń�CJ�*�u,�!�p~��F�ᓸˏ!?P�cx�K��қ�	5֠CVͪ��{fc�4���6UB	RT�*oM�-d�XH�ӟ�m -l5L�5�B���GXԣ%c�SM�Rj6޺ݎ1�j�Y�LJ�>ȓ�Dd�Y����i���q����;S�'�%� W��B�����.Qb䅛����	�ݥ�e7��H����������\.��n���Rb�\>��Y�P#Yi7����7�^7עp��NSE�IAz�4�ր��>����L�[TG�+��/1��щh�a��jFjT@�O���/�u�q�!'x�|��ȏ�!6�(��뾶c;��fMG�� A)#���:n-��uxJ��E-����!�lLW�O��>͈��?
g���Fq�_���qc�)�8,^
$��D�bK��՗�X����mSn1�I�)���+![���@��-A&���GM62,WI�BBv�e�\8~�)*��@��-Pm��~��I�}���Lx�����+�Ԓ�@$��=u��X�qD�<g�_i
Ct��\zSo�u	I�t�l0r��j�s~�T4���N����p4	�CUg,��8p�&�&���$rq��Ҵ�Q����BR��p���3L&�����[�Z�ւ��=44��E<�Gq&�$���s�.;�\jqE=�[����4�Gc���HҦY�Dկ��|�#&,�.K�F?��|e�x�|����E#�x� �&UP��@@E>X����Nx5B�{�kTU�	�#i�� ΅i+ՖV��+i#������,�s�nCD�cgϛ��n�!��VI�u��m�ce�X҆��#E��s����x�qYO�>OvYH��������Wuk�"��`��'&�i��l�p�s��0q�(�<�vh�-K��j\ï��g �C�����"˄f�|�I#�>�KRI$�ڧT��'�n�@NWD��cDKN�U_K��R@���k4�b9�2�gGY�=��faO�`�h��Xaq�I��E�W��0JY `47�+f`wV�x��>�*+�8�@

�G�@���^�	@��˒2��~�Ę�<�0e�{^TlB�j:�{�4P �n:Q��d(q�����X<BH��&�n���*j���]��ɂ7�&�b綤+
�u�}���S8�EG��A�K��tH����^�=ğm��$�<!k�S3t��<��QU�		*#�M
P�_p�v�Y
�d��N�912T$�P�v��,hA���^ws�2@�-8�,�+7��5\��I�(]��E*2DE�B��v�ig�@8"�,,�dY�rܤܮ��&�rŗ��d���H��A�q�Ww2(��C��g@"+j�1�(�U��#���I����ǚ:];�.���z�)���_�]�1�dA��_ʿm�� �d?�y� �9~c$E*E<��13]+(JVEJ|iZ���oo�*�qUYa�}��[�=�by�/���r�>L`;2���0`�e؝z}�s�;A�(�+6\g����#�7����I��T������0�Mw���C��lr{�W�����^)�2⁤S��Z��*0�o�'ᥜ�`w��F2��G�y����bwx�'�l����'�&j�-�+
T���������bhRO�U�fLܱ
�?�.{�ϗ�<����a��?�`���'��Œ͐DN�M*@5��<}�L�c	��~�2�q�����]�x��C��sɃ��0�"b�`�D�X�3�D��f4&��v�Q�A�e�8���y<^XqP�Y6&�%�W�Y��
�P���58�l�6eB\}S��x\t�����^<��Dst�"��*
oS���A.
�S�d�iu�|s��~S+)�ŀ$S���i��I
Cӡׂq���f�*�f~?��qCI
���"fe$[j��S(m,8�IÕL"~c�ľ�?`�����ER�6
�]�!��/'D���\�R\�Z�1�KБJ��
����X�7��7��ܔ��̋}����۰$﷡:�b�Y饀㬲8v��U�P�[�HH����(	;��J9�2�`(�e6�����#��ן��^=��W~=��ɗ�>M�Y�>�0uV��ԥ�pI��4�ԖC!4jfA���.<��9�v8f�
�5kA���|�"�Ԡ�K���'��B,��dUJ�(�����N�כ.��j�@��1��f͈؁��젭���#�]��贯_�
vLr�(I����`Ud�q�w�9�`Gd��>L�,�"�1� �
j�����Ďض;^�����Nj�x���rkl�];$�G~H�3m�t'o�R�	��;�fr�9�g�/Q��v�<<v„�F�[Ӷ��T���^�q���J/;�cZ���\§�$ha�A�-X] T�5��zk���rk��j�}Òމ�8�(�(�]]��1�h���Nߖ�}�,D���qX�Q,���o����+���О^�Uљ���,���CDD���ċpTa��R�#��ǟr:譏#���bh��O��bG
@��ޥ���`j4@�md	�R$���"L����ү�#o�҅ǧ����!Ȯ���,����`�7Q%.�!Yʗt�����?='q�l��M�.
oft��(�	�R�f��V�
�|4��n����'{Vg	.K��X�.� *1��|�z�0�W��f�-�ʋ�g�;�!?�eNXd��H��J��T���?������j�U��Șl��P���W���b>�߸�$aZ-�*�:[֔;j��FW#K��B�<F�>Vy睒G	����=��C��B���p����Q����$91���·
�7ޣ��c�an:�D��x�+�nG;��l���Ɇ7)����$+8
���$|7��1�l�ܮq#�y.ȏ��XF���:���1��3��Y�<<R�X%s'���9H��1a�
3�o`�!_P>W}"`�p�Xh#�>8�ےq�#�ʥ�<���v�qvզ���m�쵇��N.t��y `�\~�F�U�ufQC���P��`�qR�t�!�	r�Yc��T�+=�u�#� G㪈�E��BX�
 ��&(��,}�UDE��n�
�S��@F$�H�E8�<���J`�`U�ܶ����{�Y��I;V�s�	D׎J1Jx�1K�1Q�ݑ܁�/)5t��11�=ɉO��B��ٖW��#�B�J~=uN�n��I��Bn�NS!1�Ԍ�5X�_`�MH���ؒM�%��V��c�̡�#� Kҥ�ޛ�?��d��Be���;Ϥ���ebbH�O,
z�ሖ�6b4�#P�K��T�/tV�L���y��	��0��0��,�@A��}4�� ��Cv�R����cU-|��f&EzP;�z�qͅ
�eQ��y&�$i+v��Y[v�
+����8�޾F-�/-�q�2�dȔ.C3#��l+AO�oN� �j�#�`�����7��,�9A����M�hK�\��]��ꄅF������h$TE,��1*=Ɣ#�tI66��y-1Ƙ�責.���F��-���1�E.�\�,|�)Ep�����T6�qR}u���ʲ�*sVA���dYT�J�b��ǻ���F,iu|ɜ�6FN�1��
��-�]BI|�2��
�T�
B��>k#".K/�,3"��u�����KN�X��BU��W������G��N'��/����{Ql4��J�î���2%�V�]Oe(��?���Rc�Ђ�X�%��R�5P�5�P���n���c�&[�0ub�����kHʹ�)��t�~\|Z�V���)�fpB$@�-B7�~:��ww?��R3)^?��ɟ��P�Sr�h�aU+�6 ��]g��JFLx�r��l��:���91�'��2�nHn�e�k]m�ԑY�MB�I�����*����e�����5iң��� H�)�`R�c"(2"����P)FW5�~T��B;d�B.��.��;񲙂�%���;�
�����W�x�� ����&<��5�:Ւ�K-�Ѐ��ƌ�i%`�cc�y1r�����<��-c�
��E6�8��OE�T��C"D�*�Lg���J�� �P�V����
�S��C�Yt�'4���b�,L	De6ҀV��󮄨�T��)l��p%��=쇌\�TLHj��"��H��$RQ���ɇ���
��x�W�(��P���i #I)�ɀD����΀q�͐ӈ�"/OB����]�,�D��$�0���fE��eU���c[�+|�E�P�G�S妆c�d��`�x��ƒV����
G��U��5�`HҼ�Ɓڬ���M#:�)�O��ތ��(��A�&�����_œfE8���	$�����ȵT`�*���M
H���80��O)9Բ҆8����3��H޲JK�7�������Σ*�d�|sM��܄�˹�g����if	.O�x�0Yr���`d��fcq#I/}ڌ
O�)�w�j&2��U@ć�&0��?+\U~��\�Z��]��}t��a":U	D
��͖v�7%%�GsJ)�*�� ���j�����4��+;L�dHdIe�q�}҄��G���H�㾐��A>h�˒��x�?��
�H'w5�E�@�$R�כg��	��'��%�a�H���NB��{h$�O�]y��b�wZq��"Y�ʇ�\VL�%��[�ʆ2̵���z
Ɩa���:�D��p��7��Ѽ�w�	n0�ҩp�@-V�N���j�6���%:�E�3�#,�,���,�5h�
-%A�jN��.]�]7&P�[�[c(%�P! �Phz����6J"�e�v��a�iYL�]��?�i��O����E[���G†L�3�NĎ� *�%�:6�:�u��1�R=���[��9�V|bc���
��]��1�R6�^o�oq��	cv�E���o�z�:dh#�Ȍ�c�ڂ�i����A�F��Y`/us�B9gl�Z��;���K)�)A_��},L��ݢdظ��E�
�$g��4m)�G���(O���h��Tz�����r���n^\L|\)�����>Bi�HY�1좙{�hڂz{u�d�$s��
��J8��u���/7���Ә�Z���H$t���D�@�8�U��x�LܩN"�;E�K��\Š��I��5��
�\��(��R(��<�C�5��2"�q%@��+���Y	;�ҵW���<�3X������|~�E�Њ��JSqZҚm��~�i}�,�q���,��q���[��0�@��5�¢�hAԥ�J'�iJ-%�yELNB`�2�?n���Z�,�}�Oκ�qʞ=3Z�^dpG�,开�;-�(`w"���L]@ǭ��X��e��� ē��tǔ㩋!"tl�;bo�R�3*@i�SZS�,󍪚y����Ř�(��b궕K�b
SQ�hju	n����0���n]�2��'9R�-@�t`��5!�̐������"U�>s�c;�"GT����
���-@����C�ј6]���4��I�C���TVr�ΥP�O�:��f�A�!�P�F��&0��ED|v�����O�hա��jf@j�fVʋ״��,�C�B�+ZT�Tl:��9I$��c+>7�)�dLX)�QTх�7�i�C�uY��rɓ���I��E�23Y^���UY�j����k��܉�@�~�1�5,��/�����ދ|I��,J1��A����^t�?�ְ���v��)�d�h.�ȯ XmK����Қ�iGz�,�{ b��I1�$mAp����zo��z�k<H1�?
��La<��Д2w�ҏ����JR�����iMT�*�!\|���,L��)
S$Wֻ��ISdh�q��1�C-D��-_�}M}u�5��2���!�\y.�<A��<q�_r�MC1�P�ZS���]�F~�M�;��d@��Q���1��3F��.X�e�e�hQu��-l}5�H��?
�4 8N�AT��U�6��DbZ����������G-�<Y*a��6PU}��)&����y�0<���e)L�8Ҋ�����������A��AyEȞ8�P��2BI�������D�q�̻?W2x�����Q�����^�i!�l'�FQ;�_Q�0lH�a�`�Av�L4?6=E5s���@YT�ʽTe@&�ĢY`Z�h�EP�S@A_�wћoz�q��]5��w&�l���+$�$S3�����v�Ӓtt���S�f>n[�ɂ��-����M	��SFT��ҳ�'seH���r�\�u��5�zlk���K���N8L�68L
a��싲�B=�=u8�5���N�UȨ̌93�`��*T���h*MG��6$HFqq@�9g�3�������&��ݫN��㥘#��S����$��w#�a�4X�Ǟ6Jǐ&"Ck���UJ��]b9dc�8#�Z.X��')��I�c�{Z1��8ى%�lA�t뾴b`%{)�C��֝���j,�,v�:�2ߦ�N��klK
��t��/��N�V7iK�%T_u6�
?ɼV�6ұ��x�׺�{�&�5&OlA�$3l[n�����<� ��Gא�Z�DB�����������
��[ ��m�5�H��(�܊�N_�v��7	��Ym;�6ئ����EB[�ꉓ��x'�kH�/7�f���J�׮�O�E���&$�őe�c��E�*��*�]&?�	yI�rU�N�sB��DjJȡ]Q�=�j����tt����ÇF�ՕěD�C�x��MG��A	�@�t����Sͅi3�ǎ:l��P*�%�
�:�#D�
�	0^~H�Hݴ�r���ZVҵ�jpT2��`3�l�Tԡ$�zW�},=
��e�<����R�*QKɧM�H�_�S'���x�Jқ���l�}QJ�I!4�Հ�B��ɚ5WG�tKE��p�Æ돒�iِ;�[B��~t�x��i���g�7"e�����k�7mX*3�xՉ$
+M�j�&f-���<�ŀ/����c.^;1�\��
|���JPn:n#������4�ק�!fqt�!��,��ʉc>�1od��z�kҋZie�;3�uH�7�/�w?k�����/�K��;32*��i��
KW*(�)��́�ӓ1O�LW��^U?���w�xT�'�y�
��Pb���rX)��JB�L�$�|����
Ӯ�� ��~�>��^�m������8�[�_f��!��90�-�
�c�Ɋ�>�.4쾦v=� e����$��j�׳�Y�4�?�����#H9����L\UT3���$j]�pq2�
A����		�(A�9��}��܉p?��Pf��4,�RL�>tY0�.�F�	cR�t��3ɅtYr�-VW������~�2t~S�c�d�q����
[+�֊���ܱ�Ӊ�5��P�!b����>��Ꮭ�Q\����1�ll�8{k����bʍE���(�˵
�DH���2�V��l8�0��{���.s�y|Ix�"�x��Y��=��$H�"��q�B}����v�deբ���/Ⱦ��3�S��/��'�_ �	��9�4�\g�e�eu%悝�����5KUYI�>�N�}4+��w�}����|ߗ��2�Y���l�)2�}�E�};w�$(=	��K'i5y�0�nչ����	2��c���ǒbÅ)4p�*͜�cp[��MZ8%Z�j��כ��8N0��~;����1-�NJ��Wb��4��ȅ����/�\��j�,K�~��2P$x���}9�;��9���6w��G���䔙;�w
99O�6{Ȋ��Dz�e�;�@�@~.�Vܷ��}��_����NFge���>��Ιݦ˙��>A,հ�CQT�l2����u��]���^���/�#���hD|j�&<x�o�y��,&��H�t��n�ՍC9+<�9"u���<��|O��\h��a��μQI��>]q�3���4��
��ob1���cC���/dD$+E�%��𤍼���&V���&(1�$�:��@Ր�) )$�]y@X:���9�&�A�q�lSrR�q���ǎ��Y��@LdX�n�
���*Ϣ��?�m��_5��E���!���>K�Ƴc��YX�K�;����֢���g�������yW�����<��/ܞ+���q��+��q��1$���xx����Ŏ���H5��m���rI�Ѿ��Frc
�Ÿj�=W��Ҧ7=�x)���4F�
Z8���e$mK'Z��$���Ćb����W��|&w�fr|���y��DX�j,,��l��P��+S��E�K#r�V����y��y�'��ŕ��܇���%���x�)�)GP͕�#�߆��$D�5|M���,^�N����{�$oyy��'���	T�����⃿lÌ�3�$yw�'Zg��?������9�_`��"乷���J�F�J�����R\���޴؊i�<���U1���q��$FF;����UۨUU�]_�mZ�F1(�[T� ��Pr�<yy&�ْ��M��'}��4����=,�('�O!O'��s9���LXs��:	��D�����U���Yz]h�9H6�J�*A��c�'��ÚI"��&���bE
�Ezk�J$�^1�&�)��;�c�f���z�i�pO���-b��@LE�V?{�{��η���]ӻ�N���s���̨�8+ضn.w'64�r�ex�I@��$%*(H4�æ�	HH݈^xHac�(ظcp������1�bU��g5�2�h���S]�_���B��W7�����
�zUȲ�\�&-q�\$���l�ue��&@FhBEjN�b)N���z*�4���
���.$������b'���gK�!X��u�r�
�	�����0B���
"��CJj��BR���cIHAw��e��-�c��lk��,��'$E
a�@E[l�,g�vj��M���(�������i`I&yp�m����nO�U͈LERBDUСœ��s�Q�P��K�X,M�����?�'$J�o�d�a.<�KߙT3%��R�O��Heh�%�� �)$��G4�"���c{Z��z�H�mM@H��tTffAʎ�k��cb�[�/��TaҢ��ʑ�DT�%�a�ax�W1�Ś�Č��'[iC@OM���B��>�d�N|s��@ǰ��Pܶ��IZ����ճHQ�5Y	'��1p�w�A,5��!R��+O��~^��g�A��	%w��B$��%�;ޮ
jz����F�BYj�ϝ�5�	S�2�(���Y���ڐ]^g��G%���˝,�G,P;ĻD��B�j+RF���5����fkˠ�n�2Dy�b�~7/��,��n>X�s�IM1�aF�
i�r70!���G�-Pj��d�)��Mzc���cPը�Ӯ��r�}S�Y9����dH29XQ�h\�\c$);��uXcԗ*[�D�ǖO�9 ǖU&�(�/�D/V��iZ
�t@���2̚rc�# I���˥�&n���ӭ~YF�I5��$�Y_.XRX��D�$ ��(ù�Q]�c��+�2)l��yg��U��)蠗�-�r)M��JgG��Tl��,ZJ�J;��w����"���3�U�~2`�c�-Da�V �Hr�N��R��עyt|lXR�+4��}ƴ7��N��(��X<L�cϕ6sI&y&v�V +J\OA�}sb���Ӛ�0K5�-8�\��Y%��b8�T4ln
���WmϠ֝�@V��H�R|��a�2 
�$8�Nᄘ�joV�ߧ��fE~�*��4�A�7�l�l�2Q64�Pq��IS��A5�R�=Js��4<U����1�*�?m�4w��Bč���Q�pT��R")�cd�I
�.�1Z�R�[.����D�b�䩖.��&vv$y
��sc"6���Շ�+�ޝ+Me�e&j�>^$F�Z�L���&�����C�DWu4,��j�)f����������.o($<*�.69.V��X�tdXw��qc�	2�:���4L�/��ⴸ�^O��gf/#%Iy^�ԫ��R�zkn(cc-��*
�͕7�1�
G0��Aj�+�OJ�s3	�?D�w�&�9��7*�;ajDuU֦���L���Gj�9���c�q Y�Y��h�zJ�C)�Q�#处H]	K��W�0��1��ڑ��4I`�6����������U��p�rǕ$��4�
�Si{��nWsAJ
y�h��S��A�E
���2)x�Kq�O���tr��GI	e��Ӯ�M"M5��j�.��n��r9\a����||X
V�����G�֜o��V�쥑�1�W'��f\�YEVʕ��[�m	֎�
V��$wEy��<��,��	9��Hܾ<I��b�3#�c�KZ�!��'n�,;<R� T��4��f=��K�9R��D�M�%�
PnN�v�F�<glD]��}��%����o�e�L��)��bW�@���jj2���'�j��|O��+*7�Ñ�:�b�{a�d+�V��P6묹�A�E��[s�R�ż?��<o��3��8�!.�̉2H����rJi�����H��kK�������o3�s�\Q��2rѤ�bD$r��^��UA��6֮�2f`�˗��r6���sM�3�Ί<�W�	��I$�5lE��P*+A����`G�(읃13�8�Ս�0G�**���\�6؊몌��z%0�t�8�3͟"�F$�T���ߥI]�Tuփd�	6Kd�aL��fV��C*�E��
ոW����Ipy��V8ڄ}2�*�<�͊"�Dl$�%��T�liQ�L���x�
��L��,%iY�g��,��J\�j��|�Z�h��BQsU����:i{ n�1��] ��;z�i�b���A�����ʖw�o*��?��mZ:��f�#)n$@� ����2rq`�Utɑn����H$� J��p¤��x����	3�W��d·5P��~�!i
!"�ZԧB(�;x�����,��w�ĔY�e�4o���P���M���Q��+�.Da,��aA�Tj�R*đC�����#cQn�O�N�?�x㐴��&c���yw�Hڤ��T���ۢ	���ec�-���l�h�l,����ULa�%t��k�Nx�&�VQ2
Ҙ| �rM��+4`Թ�!��QAF�c�G�i��!_�J<YZ^�A�jK7Z�=VRi0�S�\�L�0r��=LN#�9H�R��6��Q�$]<h��TY�b��c&B%�T
l+N��@Kk�E��N�E�JʂbnR���r?��ɂ,�ȍXȮª)s�*7��G��Ӳ��Ȝ�G�Tn�-�6ޚ�s�D"�S'4g���J֓mX"�`z�|�2����4!.34g��2P�)�vV��!P"������|%�ێP�nA��k	�L8�b^��"��`h_�?��HKd@j7��!��D��!�BRm���j��`��j��+��vI����I��u��D�R����=Ą'j�I  l����
<�z���b��j�CAR�YU���S���N\2hȤ�x1��Y2r���N�%��L�����c+ւ�H�G�*oj9&z��
h3�䂅!QmvR��۪�t��=F1��'�iA��J��KG.�Q�֨�2�:�r�KI���!kC%c_P�~B�&ZT�9�M����r��'���F�����w{A������2A��YI���Ώ$fև��
�6S�އ��O����<Td��Z�$��=ᒩ���R����_�;�[0No[7g��D�̒Wy�W�٣�V[ԏu��pM�?��U͘���, �ris��3,�bh�@�P����&�����23��6(�7�Z�ص)w��AZ
Fp���HȀ�U�>?�ħ���&IdM��*V���'�֝u�1�#�VF_��1�Tƭ!�U���������96��u���D|�)�dw�+<�"�\��P��ߖ��1��庪n1	�1��S�S)�b0��'�i
(+Z:kt!��R��,�NL�q�0%�3�63F@2Z��J�*���Y�� �,M?g𫍁r(����L��7�'���e��
��]QRT��;�O�����
��S192S8ٙr���H�"A��z�Ƥ*��z�s`R��q��fLVA���
Wrĉ�^��ԣH��L�:�8���	@�}�v��D�a��ƪE}�b
��)M�����Oq�7)Ň��fL[!/z���'�r�����(-u�E܀��3��vK"����.Œm��FRܟq�����	)j�*ZVOc�@[��9c�
͏6x� �o�n��I��z�=w:�d6�9�u^C&X2c�;�8�l+���N�|I:Yί�M�Yq�r�
T�i�nJ	aO������?�`wÆ<guf�R����B��:�)�٪C$���;�69	�02�n�	6�m�Ek��z���=���
�F�,d2H�I&�B��+ֵ�&�P�++#��"~')��+<8�$�E`���$����SY``	�:UZBD<��0�I�&9Y3up��*�c�Pt��	�Ϗ�g
|�>T�*�������]ʒeaU`E?:S9�/d1Y��yFl����d&��8�:��k�P�EN�i�L�
�8�γ��L�f�Ltn' |���ȍIFK*���)O��͇|I���r4�=~����Åel�{د��5V��MƔ=A�LK x�?��V����f'#����..LV>\��z�lEgV�GZJV�֨B[�7��^i% İ/n�[$����*ܩjȁ���

��K�ը���^U��`�i�9��#���mN�����u�h�]���$Hg.�-���Z-w�����և���'$
'�$ɑ�(�_��w�UV��n�҃���U�֌�^w����$�XӶ'&��ʨ���lI�IR޾�^fQ�=��V�{h��4�9�tΌ�k$���� P>��S��_�}:�"B�W�Xs��E�nx�^DJ�"�aV���hc�kʹ�؇o�jyr�e���TI)R�9��
�u�=[�2��g�є�m:�#�`x�/�Ç��llj�,d4��	n�iZ9dJP���N8�p�ڧ��Dߋ����C��111�qb�m�@
U�[�[kGo�G	4觗<�^���-��<tʙ�����"T�2�wZ���֙��DRy1E�6JJ�,�$G*�m�Kj�~��IoT?EX���5��l�w'.N|)%�:��+e�I1�J�*^���B�aMy�?����Z#�H�t�/���Z,������<h��aMYH�z����0��>]3�O��n?���U�9D\�D��)�!9j~�5�PՅB
o�� �&��}y,����,<�㳮F�U-�B����%w�n�σ)ݡRp���7�c;�-�I+Ɠ̱�?s�P�ޢ�ւ�u��YG%
S�K��q�qO",Ӓ+�}�4�c���0���"d�O)b�7(&,�%��I���8�޴���ez�a�#q�X��=gI$�UV�dslT"�Hrjzh�-"�t���_�)Ο'&7�H�d��_�F�h������Y�����1�]��s�d�7�,��X����
Ie`(7���=�-�v8�.C���e���b��s�,�.��Pж�橰[��g"@�D]LNF�r{w#�{���^�ZW�=5�"wCq�'
�J�|@�iBJ�^�6xbA#��N0�k�G5@�#�G(@l�b�D�J]Q�#�?
G�{�B�"]�^6�eƲEؔ9R4��x�/
hI���ׇ{�?5�ɢ�/�ʼn�K��p��h1#�
l/Z�c�A�k�k��1�����!��� 6Dq��َ�hV�����o�w��,��B6&+��Ę4�����(6��_*�Za�lX�JRr�W�ʑ�l���F^ډ��
��]L�pТcVJ�Cyy��O+0XUCޥWm�+^�B
ӓ�7)\�D����T1�E��P�:���E0-$������VB�M�n����:� ���W�3$�5�m�E�܅;�WsS�K,�l���ˎѩvX���#9��~�?�
��n!pd��d�"q�eѷ��y�
XU��MiRz����Yc2��/�O���d�,g�L~Z<��&��5P�r<L��֣a�d#�I�Wth��|<X1���;)�ff�YA�g�-Bhi��^�ch���L��m]�
����LqKZF��eE����C)��.���A��E�Ӱ���(!�]���h�\������6��|r�*��
y��q���%S<~�mCAFS�5ݞ��F����O�6Ѵ�t+g�LJ8 ��I���z|�k�Ō@b,ӑ$�MC�ǎ]�r�["0��H���
V��J�@Ŵ1��bx�)�D�l�R櫼KPn
��OAQ���"& ���(ܜ�,)�$��}��@���QPE7��Zr�&.�:}��"ŖG"���C,+l3��
R�������SR���Jx�T2a�!a�A���*�aK�T�#kKu#Yb$}��U�$
Q.^,.qʩly�2�k��PAmJW�����̹p���Fx�Vc,v�Ƭ�蔩�z���Qr�t�@S��*��!bT��U�,H��O��B���rr��K�&;�	�94��!��m�K(���\��7K�*F�/j,��.r�qѫ&w 2g����bdyV�.���7�^�2 S�r�l�O���[<pɎk���z�����+J�Z�J�A�! 
z�ƌ����C�e�fKH�{;�I@Ġ ]�������*VyƬ��d\&���1��j2���=w鶴�`�
`V�+�a��\>e^N��/�d��@��}E&UcAG|u
����U�Zf$�;9gg�j*IsbI"��30V�S_[Mhc];?�q	hr'�͚4�Ln�(���f�HT�
>T?#�C&��h�RQ�����^=blhU�v&�B�u$�A�A�k�vT
s�T�\�)1ɑd̀�Ђ�q$�1����qZ��I�=��]���$������� ��B�zuu8DjX��9y��ɛ?$��F��R�!�iv�,�S�֡2ip5Q!���qg��a`�E��e��Ǹ�A����k�礔�}?I�\$3<�/3�sC>��N��+�0ʨcy�5A�z�.L��5��gV�wZ9�����d�V@{�v
+Џ�F�6l�c��0;��)��;M����$-&Vt�Ub1��R��;m����yy�*:"G+rŃ<��D�1�K**	{EC�)p��@H���Z~S襽���H�1�Ol��;�-@[j����ڶ8�D���Q&|���&9/��I���	 J�j��'�;�����3e����ї�U
v�JP���V+��G^?'���3�������d�բ�F��/�hz��5��;��q�!n��Xuu��$�c1"��i�ZS�rsé�O�3�fi��p"�V���H�܇�л�	E���s�%���!HXgɍh��T�?�.ă��F(gc�w�D�̘�I�*��CH��V�4�T�z�tr�YD���
�˔H3̠�g,�[ڴ^���=TD�ҽd�sG�2RK"�$
�F-iHĞ�Z��:ǚj�h���K�yW/���I!�|�C	�GC��-W{������{��ۆ��	��aqP�ȑ�1�֡��u� ۵�o]k�{�]�%,��ߚ_���<_O+�л;H#��*�=�i����8��\y.&���<�1̎R!"�噙/[�@h�i�<�/@���Z	���[9b��Lh���Eݨ=j)O���dՂ��tN�\�6L9	�Ե�N��*���� ������d�!����h��,ؐǎc�r/���M�
�PtD\P�#�-��y����D��U�V���_m��Yb����0:���W#(�d�|��5*��m$Q׭)�X���Y�ANk�ecs��l�Ϟ	D�飉�q�K�(.�Z���Ls��2+F<?���*9�A���<vW���$q42D�R�'u��Pk��{ûd�M|� 00�,���bpر�g��|�Q�*�!��'�j)�§M,�w����곥�ʕY㉋3L�Dl(�V�Q��
~?
H�'Fnj�yq�;���5rU�T��yV
z���}wЇ|�<
i`��G-�
���2�n��(\ 
���t��2�qmM,�� �'r�ߌ��L���;e�jW�An�	��զD~:�F;��9�rØ"1!Q�JՋՉ�2�}�4�# t���ܐڧ0y�*9%*�Ɩ�c��E�VS�٫JoMh�h���E)c>�V��c��ظ��ޟRc+bG��tf,
>Z���"�%/��sD�?ƦNnD���D�ղ!y�Jm�o����p����#�l�Р���l-d�B@�d��3��o���_�u�r��k��V�S���;!�Z'C.K��D:Fѱ���Ԋ�	��7�a�W�4��b�c�ēdK�0~*P1�^���%�H���Қo�o*�idRظ8���ə�i�RD�z?�H��@M��:|t.��8��y�x)�1�r3��E"ހD`1�I�-d���@�jY�'�>��$�?*�7oqP��2����v3��h�����h9��ƯϚ����_��?�0�1N
��ѳP�i\XH�+C�5�2qR��'��>v~2��f!���wD��HQ����$8�@VtP�q#�b�૴�&	-q)�
˺+󧮒p�ȱXA�Y���)"�"��С�u�~Z���3!�V���jǔF�D���)�$p�䏧��7��� %
�B-*��C鶭����$iw*Y1�܁^AW��C*v��L��@�Jgx�H�%����US@�	T�w�����ʾw��bq��O<q�;����ʼn�դ!��7����y�Ƿ$�?���q��H�d��C*���T4�)R��������D���RP�p�L�IX9O���_�P�bY@���$��D���d5�/�WL+63́-�E�Ԋ�+p��o�5����T����z�q�"�ɚ��+Z�"W*@��o¾��cQr[��0��!gǎw�T�qs	"T�[�F��4�o��-g��.e�	̘�&������lDYh��-�i@۝��S��@[���"(�or�W�Ah�h�����	6�.�j�y��X�W��;:.���2�P
���LgW.�m(���W��e��"�)���[��5}�ɱ#��j9�N����@ɾ^<��/'$�+�Ğ��1��S��m>g�`:W^=R„Y�_�7
_���ȗ�g�fNrVE�ӂ薢l(**k\��8�&���VYwhw#7?����]���D��*Xȁ��V�B�*.�Z7�^�k�WS���C<q%Q�ԥ#J���	 n��7����Rqd~=,Y�F�����5�禓15�a�&�{�p���UX׵���@��
�@>�|u�f�j�dd���~5�٠����
[�Bhz�S㾜jSuE���P9F�Q@	ڵ��F�F2p�
��IeX1U$~)q�>�����`��UY'H��3w�B�F�_Һ\�d	�<L��kj�(wp
�$Ch�S�ki�MC��d]��b ][��q�>8r\xc�T�E*耘֠�A�:HJ���d�[�c+L�DȀ�
X
��(��0�B>{i���;�����7/A�$M)���&���mS��kӄ�,���rt�X�|ύ��L�8b�NZL�8�WtC�p�	�g$R�ASPF��R�f ;���<L2��Y���
機6D�mA)!u
�QJZm��=�S���'{&���2_dc��K0�6\m�Pº^�{ y�4���.� �����,J�X����J�'m�tv��s]��R2���&G�C����2+���6���Y��Y���9��2d�����
�Z���k$�%��U�@�n��q�;$I h �YA25�
�����ȍD����"����<q��2���;���jߨ����k� �R���78��^1�%w�9]�%����Ʃ#�v�(74����q����_������⼔�y�[�9KmDu�C�
i^��a���Y�q��"��H%X��
1qZ���m��C���`|~�� ;.�l��y,�&.R�?�w�H4�B�bB��?��b�Ly}}T�C�8he�����y1ǖ�Ά'D� �&
�ƾ�n�P��Af�����dȊY�q�e�Ʊ�h�ڛRAؖ=�Z�H��HYѱ��&@H�.)�u��n������x��$+���|f8�T3�d��Y.TyS��
��Ӡmg�3�c{���O�㑟�,Nǥ��8�s�^B̹�$�#92�-�9+�V�t���pvg&�����ߎTZ�g&�5ÒjL~��ѽ�H�ᵔO궂Z�6�ւ2�k�E�@˱#�yd<�j��U$T�w�l���i��Z1�l_�Y���M'VSڕU�k ZĂ������Z��ԛiA�1��
q���K<l�@�sв�A]��]�;����a���e�q�gN�%(���Pu��;k�u���WE�`�x�9׏S�$0�L�-k��Z)q�m���w�2"%�-g������"Í�p�0�I"$��Q�R*=��e���:�p�\��9�a9%���#۾�;�n
R���-@dɹ��Uv��T.[#ˆ\��n�q�+���@J��O�Y]�}R��LY�Gɔ�bܣ%"�@Ɵ��AAҿ��$u4����X��r8��d�A�a$�
\��PЂ:��0
(J���g?/���4I���V�=QM�֛
.H�k�B_�����+K�N,X���*
H���5,x�	�����@�5L�^�4óŕ�;�/fWZ\���\	��zj٢L�NJ�–?�[,�>~_��~R,��0\�����ljL�<�v��C��Z�63<O!"A:��ZQ��3E8��|I�E0liT�;"�۵�s;]N��Z��a�n�7K�����g���M�c��e�w�ŕ/�q��ى8vir21�HZ�1��$���"�P�d�Ix���1e�>�g��/�c�q�<�Y��;��m
��<K#|�کFF,�`�+qa�(�r�#'�j��V�b�_�k?���)����}���#��㈌|,�����%Qa��1�)��{Y\�є叺�~����p��o%���<���<��9A���.6V,}��U��񉂆c���Rm���k>2&\i���E�`��ر�^7��~ ��u$x�`�jYX�m���T��/��,��w�x���B�Ȝ	�c�/�c���t�R;��8�k��&�|��<�$�q}>x�)�ς�Dd0���.�������kA�	����
|�;�g>;����!�e�#s'7��L1����ʏ���M��Hi��_�������
ػ��t�_�f���qq�~�G����sĞ!rah�	����V4o����蘅`o�����C����Fa���p�q���O5�x��G���1ql�����BJ�ku(�
e���Z>�Jo�����i�k��!�x���lX�w'�Ye�v���w��Q
!�D�@�9gn�Br�=�1�|U9L,�ŏ�x�8��ȅf���f�`oK�\6�|0n�e�j���_�����|ˎD�bț��#?+2$ĵf�u���g�4�H��LH�y���Su�#����'����s�9~=�s���7.܇!���B��P�D�����f`�̰k��%�=��i�C���s�!�`y�x�o��˕��9ٙy8x�u��<�X!u�P$p�k�m���O�|�œ�b�(� ��|I��y��Nj��9Q��۟���P���M��e�e�@-"���CZ�i-Î�y;��3/��f~�r|8��7~k��Ix��S��'|���l��P0�qV����D�"P�rq�.�6�;z��!"��u�m�?���|9���1�S������Ȓ:�9]�S��b)�ZF'nd��z�X����8��c����Vw7x��̂D�3��ٌ	P�dd~�(Lh�X�Lc!�2@v`mJO��wg8D
�W����<NG���.>`F�ϓ�d��ɋ
�H�
���ܦ�m�؋!~k,1H�d_��-�W�O?���/-!��^y�eO���B�������-Z���P'��˃�O����q�k��_�޸~G���x�,\���p(�60��b�ś��U�.�`VV�K�;c�J��D}π|��4�y��'���C�i��O���q�ʴ�KA,_ԧ#f*P�E6+M�5��bGڡd�
E��O=�����?Y�B�U��h1ݨ���>��>g�#~K�p�W)6VG*,��\U#W%P�@Ac^���SE�X�&��dpƭ�
��,@Z�RGΝt���r�p�J��,#48����c��Ks2\���QoJh1��\�pE�h�1������.�.F��?Hf�����Tܲ��B1g�H�L��
K��V��\�)S�O�U�!��d���s�k��]N�W�:�ᵪ��/�zX�șN9�IG ��nko���DXܬӐ&�T�,h�L�NK�gfc@�Q�

�:3��\��1���HՕc�F �p��BkZ�򦄱���ё�_6I"P��𢪓5�BA�
�"��k�K�*O�<k�3�W!�EJ��׹!�nhT�7LY�H���f`	�d-��z"��V#㶌�gkUpgHM�1��$㼂	
ƦyZҠv���em~
�;&s"��O �ʙ
Z挽�d�U	R��Ҿ����_�쀑��k�D�rve݁D-B�zȃzmZiv�"�o�q/�Nٔ�<����v*%C����I��=4�����^KC�XKdc�h�3�՜r��MX��5�0ap�0��Kcr�����3D���C���jj��uנ�C9e�{jQ��@V<U1�h
�Q��ԭz����qck�YK�<Q�Vx��7<
��G�(ަ�7 iMt]����d`=�7���T6��{�� P7�����wDKh���-pŪ�ٮ9"DE�����((hhI��@>�
W��n��J&����ȂzŌB!.Y�E;uoZ
�uq��V�>A�L�Xk�R�*�R@Bmc�;V�yKm�RDu�����/���F��`ma�+M��j?��u�03��=O��u�^�?�w����M��v]���K(�WP��Sc�2�#pk�(�:�l����h�As����,�!hm���h�.GJj��YR�M��a�BHjm�V�JȀ�˘���X2Z�2{�d��P�=>;js�RV-���E!�2M!8��ՃCv��R!��YcxǏ�^;��x�����x�$)���qc���¡R�UUZP
#�Uh(�2:b�*���q�N�U���k�J
3��Bϝ�.<�IX��Gt6�ݳZSm�w�NE�э.L<������GF���7K�̠�;~��Ye	H���4a~��A���C#"!r�y�/j���H�t�?��a^�I5�1��b���-TaPZ��;$����% ����c�4��!�9c(��,Y�PE�}��S�1��`�12���
>��4�,�c���/�w�߮�cB�&Y	�9���1R񌉽�J�R�
�}
4���_�aD��?!b��E�P�����*A;�M������*E
>�ND��00���'�+@R���k�h�1n��䒗����^ǒ$��4��$I.O[T���ڔ:c� ��ӛ.$/�^l�9#���M�h�۵�*
{J�J����Ǔ,��7drFS��;?(���2,��1CZ�sZ�Ѕ=E+�4�Ðг�C(��'�~c��8��ȝ��5*M�Q{��~��'"v�L� 8W��%��li0K�����rǡ�7��67��!	7������F�Xw$9�k^�v"�Tq=~Z�<����UQ��Nq|�8��%�w�t��_��hWV��۱=jzh�( ֩e��030���eh�.Ҽ����Z�Cp�1�$�.0"�rD2���e��Lʒ�M	AcU5����9D����x�ljq��i�����bEj�����#����o��eL�+%qc�'�I&$fZu"4TR)���B��]�5�����v�H�����U](�]�6޻i���Bd�T�B6z�ˎ!,��-<R(B��;�CMT��s�O�Y++��d�0�3	%��=��
����]	�#@/ɒ��+�x���C�b�2�H��86I�����=�HX�Ta6�C���=�DA�fś��"��*2Aoq��haJSq�}œ�舥�.R���ʼfxPv��4�(
Ew'��]4��v��gm|P$>�H�����¬�今�.���E�BNڑ���U���-����Ď�8�KZ�EK�w�h P�F�^�4@��챓�:�q�QbrB%-�ded�D�6T�KYш
�����j��,���;�Y1�0`,,�P��hchn��q��
OsL�&#ڟ���H�.i�*j�-Dj�*K0�PԚ��U�j4�� ���8R$T�
$y��k���#Bi�鶳G�C[�������!r�9��ljp���D�Tn�r���s�v��4g"�By��L�c���`B�o����(\x"�%�"�﹥)]R,
��K*��W�Oɴy3$/�����BCF$@:T��k����\�L�S)e�dh�(��5p���t��4Ā��ʴG!y3
і2#7
\UN��&I�fb����!9�lY8�R\(�s`��^2(�fB��ė���#R�sJ+]^DH �$�P���$�.YJ`���ӂ�&EDF}�����<�����	�rpK����dCq���nO�}v���eVT���v��'2%�H��I>I��a7uh#��Q�$�խ65$k0�l���1d��\�WƑQ-��Ar6��m��@"y�ȝ��FLC��N�T	,���5M
�W�5q4I��'�`�4v�^�V;AZR}�I�G�Y�=�D���Ϣ�C�xt<���Ѥ�J���J%�ꥍ=��W`6ЙK�Hf���''���d8Va&��!,E�Sc��1�u@�d>5ߑ92� �00
�J��
��6�5ᔦkD�6R6�
�{�F� ����"ݽN�qԔ��1de�EV@.%�m��޺"�)V��M4r+e,k�!�	֛�4w %���8-����k	y:�cT��X�iQ�޺�9I��4�/�=��Xc�aƗLe�ܪ�v7Tz��d
��:В$ɰ9'(�S����R�����.z(��#%�ѯh�p偌�jEH����`��Ȅ�D
�fTsj nM��p���E�m�:������٬�Ƞ��kR���M\�BB�]�FX��
��d�!o`�l}�M�1�]99(Ye�r�g��Iɖ5��ǯ��ĊR�=Nr� 5F!�
$r	p�c|2��,f�!/2Њ_]��j8�=
U$�EҬ����I$��ȁ�ڎ�X՞�㶚X��O��2֕G�9�rBEC��[N䄈�qǶ�Cp+�J艒yyW�!r�4'�G�H���Qj�T�@@ē��"�ۍSb>�Nq|�G��YQ�#,Ȼ-BS��o�Z����G#�I2E�h0`�Q�AgAr�]����*F���O�% Z���0gDx�0���RB��5�(^�=()�j�2a~8�(�u���0�N?�1�n��A S}�߮���#���$�+�rܴ�<��"��c�wBd�L�`*h@�
j5�=Ħ�.��`Qh�ۓ������W�bg�V�ݲE�PMm�E+���q�92rȒS%R�PM����D����8�Z|�B������q@��'sZԃ���h�0��_�q�~[�krr��"���(z7�|�=5����)\��S�*f�}�O�S���2�8����bș@VH��gS�z��:�r	PF���U9G���b/�2F�#=�k� ־�ri�Y�����p��4P�\.��”���e�-�,9drX���bȊ��	����4�]N,I"��"hE���G%���98�7�����B��ŒPOr0.�C]a�Z���G�����%3*���TT���j��m�*�i�뤔X�S�����;,���s�"7�Ak:
�?�GqsZ}Q1u�)ɋE��jU5���J�0mϸWn�P$�ے�k�%�A��Xy��Q#�^I`X�I����а&�����D2$�`
�}��d.��K�W:g%[��:�6�yV�k�OM�s̤fF/���a�H���E1��1cr�H�CO��1c!�Id�`��c��&t1�Ϻ�0�	5�Ch�Ў�Q8��
ޝ8�0���=�e�d�%��UL!Ү��E 
�m�~;kL������#�����#oyȩk՛�C��}*5�& +F�*F^)����	�Ť��Lu$�Y��a@�n�5��u�G���Si �F�d���?����4˜��H��SZ��Sm^D�ws� ���K�\L|e屲�$�0dc�1v��"�v�]�5)g${��oHc囗�i[��<���.<�'Vr�c�e�@}�uY�AN�{,��#��^|�6�5�X+.Aj��	-A_�����y$hɁNb�)�I3�-d6.�ѯq�(=�_���ֈ��<�*e��M���Eg|��ʲ~�S�!����r[}�ֺ�FHK�Q�2�Y9����%���u���gej��~�~}5�$x����y��2'1bd�0Ǖ@PԼ�.
i�u�U�V���aF().(�lw�E{c�=����F�u�*u��g�1�
n#;%R:�{JƑ���FY���V&�l7�u�㌥J7�R�&@�P����+��+�XT���!��������˷�.��r!�"�&3@bd���E*E����c_AS�|`I�wO����ǕV5��`�L�X��ֱ4�����E�T�cH��r˕x��@]UbQ��~�փBq��	�Z���S����i�p�ݹh���n� |'Wx��7g��lG�E���r,G�d�r#x��Ċ��2.�-�k�llf���ں�x^��[�D~7�FnW#�4l�8�i��*"����}I���I6HK��)��cg��bM�[��!r0����������`@"�ٓ� �hP9l�'c�d�?a�Ò�F�R-5����D��J��B'�Í�a�4�X�iQL������������ҕ�11b���Ts2`a�Q����j��%Z (�қz���Ȁ&��~2w��q�
4���E[DW��[���>�K�!S�$A%гs8�W!s0�I��YV �����-R�sBF�,�2��# :#F�q��A"��S�'l�
����o�ҚA#���GU���vbs��P{ύ���&:�T;��HT
�QOM3�ȯ��Fz^'�+�ü���2��7���hH%A�Zh�O��Ϋyx9x�1,qD%r�~�
�����܁J�N��r�v����1}c�_Y(�BY�����z|u9��B"<ʾ�a�돜�3�����׮�߇A�� "A WU��	N3#�2�Q6~_#>vY��1B"���b�b�h[Pd,�3ҀvY�]s%f���#ɀIZ�ns�xՀ7Y
4�=N�rL�z�e���I᧐e��(Ajz��綴a�ע��S��̼9�774
2 �W���Y2��M�Q�q�`ľ��J~����8�l�ɣsico�j������,���NF;��`�4�*��#)ؑq�3qK.��T����d��T?m%Tq4SҠ�|�����ҭը�bC��:dJ�p��ב�T�����ӡ1�7MY9G!'�x��'+�IXIZ(�x�
U�נ>�:k&h8"%��*�	�6?V��HR(�c�;X�S��1v������	,��&N6\�#:`�ቝb��-E�c�b	��a`�2���C���Q;�ӸdG5K�}So���x̍I��,n{��q���r⋀x�2"Xę�@��i5���ςc  Ѫ5�Z1e%�VG��4i�d�<�x7,]����Ɇ[P+�D�� n+Pi�f�3C8��elr`��z|o�oX�Q��$aؽ�^�P�J�zX�;���Gz������8D8�
D%$�9��B�Q�Gƺ�7�K)�C��x�(����,Ў����Z�z1"����!&`�R����[��70���K��P�I�A�t��*��wP c�dbe���8�F:Hm�h���(ֱ�}*����8�R����`$�A�E�����%/�	S[H��Zlu����o�s��������r�w��5�i�]�9��>G���x�U���9)�#:��K!,[Gn;�G��åN�G��B�ߵ)c�.�3τ<�#;P��tiY��T%EC@z蜠ŅW���G;�����H���l�jȵ6�A٘���]�����x��p��#WGl�;
<^F`��$�E!G+0��R����Z�M6,�$FF���Q5 j��>!����>NlJa��4`T���z�M�;j���X�t1��O��_/����a�OlPؙ�>VFL�ʢ�-TH�V�^���1��&���]7��j�~��	�ɮ4�k�4+T�H�"		m�m5!G�53�8��M��O�X���l�bg���4��oVQ+�+�
��XҚ���
/Z+�dh�.|?S�bq��ғ+<�6/:-V�]Ҷ�V�z]�o��}���O5�daB������5bW�U���R���zu�bg��f���d���%h��z����w*2%��ǐ�;F�eY�*�.P�R�$o�Cr2D��XLMVo!��6RLJ�L"��`���L����Mg���e����%�Dݼ(č�7x�!.�[p�	 ���"*@�5U������2H��C��D�-y��
�p*z&;�v��Kl��9��p#ll�w���;��*��7�Zy9�B�>kf8�F�d�^A�Y�F��d���J�S �{�6�ǻ�R)�Hx�U&N<i�����`�2���3PT��{l� Px�e�����I6W-y�	^�!I|��Gl
�=OQ�\JF@y֥N�)�q�4o��h���"�Hn!V��T5�w9$��]�!�T'y����Pˈ�d���:R��t:�$����)�'!UǓ;�	<LVX�T*��ۮ�5���W�.BHr(�}���zA�)R�����o�W�F�c�`z�'�(^G�,�C�O�2�����<�c����$��̨b*�ih,7��Й,6�}���w�'K�X�>�$ј�x���	���}:�O$l)��ߏ���ǝ�DK31���X%i��.�R��p��G����@q�@ǖH2 ����,*�aF�b*V��K(�F��n^��A�R�@�C�1ə%C$փ6�޾���T<ݵj��F�&pH2�@Օ���?��IR�oC�>z�@}|�,�w럘N�f�I&\��*$!GZ�V��4��9Ki����;��?��{#�99�䰆��^�3=�X�
M�wY卋䩊z���a�����a�li�E�q��괩�`F��sֺ͘K,Lw7VT�E� ���8��H��S(\g�-}�"Ejh3S��c'�����".�����|�Rw������3�$�}7��=��e�T�&Lņi~��K�I�6��pH��̯�kQVIb���iU��C������l7�5��͊m���?�ď'R�T���
�QhkA4j���I�0�Me�r��n/���GF���l�cu��7:駘np�G2���rr��O�=�L�$-V�А��h t]t��t\h�l�X&��� hđ�c.�M��篦��j)Ÿ�@��g��\�B3;Kk#�#i
Z1�T�A��g��4!����R�44y��-��$���h�$�
��?,�hOU�z�(��H�X��f�8��Y�^'k����&�����C-6�˪�Eˏ�/�̏#
����^ҟʩ�&���Κ�n�L��{+e�Q�qN�,�[���g�B��X*�� ��ɃtH.���C+J}���l�G6s�/h��;#F��+�B�}u�fr$U����D���0�$�T�Җ����V@�v�b��O��";�R$�2k������R�7�W��"��!$A�?
���a.|�ɍ�?�b�?v<���!2�s<�K+��N��ձM�G���6�&�8�~������;oD%c��ֻ|w�J���(���9����	"�:"�纀��v�;j�˳�zp�2Z>]Go�wQǻ� $?i� �#�ڐ�H�
�zj����N!~������b�Lh�(����C|>Z�\2���"�u�q�#9e�20Y��K�t"B���^&��܆��7׋�tM��o>�^�ڒ
�qP��,��^�Rd@*�$�� 6�E	$�~���z��t�Ma�L��,$Q��1�cȍ#`�ٮ���Jj���K#:��0���c�ϑV�DȲhYP�/۫Z�����WZ!�����OE,�ʸ|l\G>3�%��w{�EkcB�S
G�~���0�es'����Ot
�¹�����Ơ�I^I�+R�%���M���u�ͫNj;^�Ky^L<s/�F�A�8�UW��i�Su��45'Q�2�����qL����U��X؁{Gr��(���=t����=4������6O���Xu*#I-�<��\zP�Ӧ��G��:�=W����/�Xٸ|g31$�%n�Yjc ��>��3?o(I�{�3�&��O�<�?�2fQ�8�,��)6��	=V��Cm�����0dj|2�E��s$���a�҈���5d��.�@P
AS�=
trb�b\�)���s�M�A`ȓ!DF(�X�8�"�q�Ѩ)��C{�Z�6L3�]��7��V��LI��BC���C���J�����b)M��c�(�:�����-y�H_�"exd���F�0���E;�w�֮�r%ߓ�ég�k�0��u�VZ��!�@��E����j2$]�� "Y3/��ea�u�R�Z3r�X
:��I<Q���Ϛ��`������3�qH/����5i��룂2�<�d">+�Ɒr�:9�Mk��Qpŝ���o]j�!��R��C��>s���XH{MV�����5�Mg�%:��=`Dn�גe��r�����sa˓��d���쐉'x���y@�����
y}ĥ�LL�Cqs�ׄ�&ݵ�����=��>�pO�7��)$Hy'��d��&7���U�ա�0v��%6���tT���~���&�ƍ{Q�܆�<��դe��V��Qr��?-wnd"h^^�+��N�8�#q�|�a���I%
��X��dt�i[�խ>�s���jr�
�Z��ۅLXY��P�73*�@0�Wj��\y��ӏ�X�%;�4��A1�(d�2TX�V�7�OZ��[\�~�,�G%��"�s�3�E;;(����>�z���|�p*��T�0�#O�yRv�׹U*��e�jY���7��+�ʫ�����?zf��'����A�z�DH�D�	|����Q�F��C���kH�'o��)�'O�p	��e�d���Qs��)5
��|5�gxm��l��e)�/��JfƋ�"$ƵUB�k��R�3'jc T^�0�9b��VHd���ܮH$�w�s�@j�)���p8؏̓��JP��Docva�צ�=z�F,"\��2u������%�Y�F+k���
UHu����u�\��Na��.�#*��k}(��CT�p1�%މc����S�ɗ.��rQ'Z��{Z�A
m4b}�vYJV,y�th�2�f�5�夕��b�3v�*��%#
�I��י�3��q��َBѡMc,��d����d*Yv�*��P����9(L�C�؎L�4�25��Gu*ak�UEkew��;�{eWd�����0ג��bÑI3$YݭV��s��z�^t{��+ζZe��zrU�k�� ǎ4��!�E�Ɖ*ii��R����7r�9�lj����>|i$A,*�I����C�A"���i3�{=��T��&8��>'���Ic%Y�Jb��dv��zj�g!@q�Y�d��I2#�DXV+��X�f�JSz����٪�
t�/��c><mK���)r3�6�Ij
Wn1��*r�d|rG<����lv���eSs(-RB��鶚.	j.gk��!�e��ў�8& UG��Kn@(+��l���d���'�����~�':X�>.v~L�yS����Z;��5*~zpdjG�& '�U��ٻ��Ch$��Ve
N�Sק��Sk���Y��9�'�APd���[�@�T��ahvbV��k��#�R�������Ze�G��"a��M�gee!C v��
�kSA�n���yK�ΖB/@���(�Y��D�+!���
^��2nr�����8�>���,���-�ڝ+�Մ�b�r�U6U�Įo%�yi�|�"H�n�(���B.����j(5Fg���6�,�o'~S1�#��9 ����K�5�w�� ��ӭF��E�1�+�@5Au�.g#�6s��,�LuŜ*vdd����==
�>�|����l<SG!�
EM��pG�����:���C"�
��
����`)Fa�I.��3s&������)�7�aEJ��@����擰����K�$�d+L�wmZ�]��v�
�T@�L��8䤇-է�;3LY��*8n���]p�����Z�|^C^H��U
IY趭H�0�V��Ǚ��I�����k��5��'S�$��U�"���!���@$A6�v�o������N��(�Le�fG�ZMY���hDj�dīc�:I����7u�j�jݺ���4�.{�4�o	�����V�D�2�UK-}G�������7
�"�fÐ%�����m
I['�.,��)j�ңq�-^6�Zv�
m�yXRVvG,2%��G5��R*vw�~Q�Φ��䧰��	������/��o��V_�����I�O 7h� �y6s�]���W�j44���W������-���g#�lY�
հ�2
�ű�N����F]�UI	
Mqg����?��{X��/1��x\HS��^:9<[=�TΠ�/�9��Z�H��ʒ9oZ�C[&_�f���nߐ=CrC!u_���yyg�<�y'���8��y\g��r���M�ɠk����f $�"�:���2�_^_��,%	Y�\�ؼX��6fvS�s\.Iš&he͂�Ǐ�����z�	#(aDzv�%	7�\9�c�G�����[�h�T��u�T4VA�w���Q�?�>P�e�jc6W.L��Q4��Ƹᥢ�)����O��/C�AٗKȶ4X�+�u�Y�`���
�5:�wH���p��%�����ppw��Sb�͍<���fK"����d�7�܎�WҺ�Lr�5��ы!�Qe�/���'�_��I�w�_ �$93�ܼ<&vc2�h��f�����2*ۂju�w=�bۀjkO*�����|��U������7�b�qx>#y(�72\�;"0�gͪ��*�㴊�*�\��蓠����o�i@Ό���/�a��q_p�ȇ;˸y�Ýq�hx�A�dI�7`�,�i�`�A,qd��#Qɪ9y=<�.��m�u��ǃ�Wi8��^:/��X��\�C4o (*R��Ru-�:�p�Ў(�,R�U�s����O�����|v�ʄ�$W��B�╩�R!¶�s�
'�����._��yG)�Ǜ<|I1�r
}NTΌ����;����Ss��V���E}����y���x�`��Ns$YrO6zL��*Ux�>B�-"1E*H�k|ـ�u��^G�]�����8+���u_ǒHr��>�|�4�>Ja��Y��"svNGp�#1��AL��V1�+��@�k��F��\~[�c�8���8>4O���C��6P@��68P�����ղd�55���1��W����<	���sp�F�Y2K�������P�B�E�f� �`@�W���ʠ����w�^E���>�,��+�'%�Xd��rY���d����ýI�N&�}���@�j�;x���@�*�y��y���%�~�}�ϓŐ�䜗lE�����e�pa\��E9@�!�S�sU7?E���rL;PS�ϐ��VI�����?���h�?�pm�������Ȧ�#��Ud��k�Ɣ�>^[K� {�'��hU�FY#X���aҙ #������b~�c���[�x�]��q����M,q��%�P�
ShS]�w:��&�9{7K�Pɐh�0^�nS*B�g�I*�ݢ6��l���
���Տq#MԮ�P��{��;����Yܼ��9^�ڈ��^������J1�Ò���v�e���4l��Im�+��@?�͸�0b�,�@�y,�#�}�T&�
���Y�mt�h�qd¢Y���K*:������[F[v�Vs>JE�q��~�j�{,n��! �h7`(:|�ҝ��]p�H�D��+�o���Z�٥/�zzS��O�Ooj��<פ�l���U�2%�\|mVAP��J�^��E)0gQ�q�d�&�U�%�8�V����҂�LЍ΋�3e��r��`��䩓�f6D$��u�n
��q����^<H�G~^�K$rcrc�P)JAҽO�MBn��2�"-D:x8���h�@a3]F�[b)BO]K�Kh�'�2wA�̏
e(�dёLq9�*�`M(@#�_�P���c_
��4bH���`��24�YV��r2�ɹ@��QM�6� V�F.�#E<D8�4�Y���T���.���(���tK�*JR=�l��|n%����#Z����T���
s�Xخ}E֞,� ��AT�P]J��v-]��:Ѿ,�LR�HXr'���`h�^�-o�|���pK�el�����9LJ�"Xh��$�J�+O��&%ݞ��GW�2��7��r�Il�����P�SC�U�nC�e�hYd�܄�s<�g��j������bP]��Ԋ�C	r��z+� �6� n/��9����u��;+[����i�ip|��.���=����=�E�Qd�
X�f �}��kЂ?�kwP��&.b&C�9��ffV��z�~z�s�+D�����ǯ�b�r�"[{�J��-�c����q�0&���.U��qsd.>�&,���!��W=M
�m��l'#*�-�$�d@Zc��d���cp�X��`I���@z���H�B�|��2��f�'0��{�ݕ��$������"��q�pP�l2��Ɏ�T���׶�5E���SN��SI� �}���]v�.>Q=�ڪL/Z;��ӣ
�~�鄣b����<�|D�̦9��:�� E	&�ᡒcmh�0;�O��6\|���EԉfUV�AhR��Ԃ6�hK��Z�\�g��E
��&�	Hݕ.VR�B
�����ڜ��Y<U2�T�yjX��D�Z��Z|�ѕ$D���s���<l��d�uAM�/R���t� }��DuL,��1A(����V�n���o]��|����"�%��~�4!��e������7�?
BY7J�%�mhԲ��YxL��2�*�{J%Yi��ԟ��dpA�Jb�tS
�~&+�D��W.G=�S#�u;J�� ��q�q�I�P��abB�5ĕqkB�w~=iJ��v%�K	� a�c,Ixqd�C>G��HZ$�@M(w��0ǴA͑�~�B58\\1Ⳬ� X�e-R]�]�_�����H�UYّd�C�י��d���S�e�S�jsܚ���8��HnCL#�$��iͤ�̬Mv��t�=u=��5G�.�8��D�<x�F�L���POJ�4���o�](�B��<�G�0sr�H�s�z5��c���ѓ��M�XKD�f"g��!di{�
��vƤ�z��M,	x��Y5�p�k	�+��#$Q�N�}65���f�t����m�9�K$��ܵ¤��(P���������Z#�5P�,����x����l���vGxB1Pժ.�A�Бzw��]�#�q8�<�4�C&;�Q��UjtZ�^��Sō�;�dӛ���7��\���&f��,�W�BI��k���s�_�;#��#
X�p��mmڵ���@��),|Id�8v� @��$$��cq�v�	.9�2/9�q�,$'.�?vdI�ΡI����OO���3��b
}Rb�&�'����L~B�	C�=�$�3�ֆ3�:����&6�Mȷ��Gu�F^ӧ5�#�I�e�S���f�H�
��ܭ�^�HfX��~���)��!f�$Z�PX�&�i���c(se�s�-�䰆D�ohEkB�0��-z����k�@-DD^��X�M��r8�8�^�<�~\�=��LTU�1$��i�Q���I�˴Aj��%�$
I^�A>Jϕ}@�F�g�Ym�IS@:��^��pH��e��3�.�r}��)UO|��u�v��߸����?��,�x�t"
���l���g��Ç+#>\�)G�AmZ1�����S�U�&U09(�VbDz$ś�B���"�F�lG]Q񓭒��DŇ�Ħ4�� �O{�;��E:�h*��)�$@�mg.�T�^�Z��6Ly�K�I��`R�IF�5#rϮ�B[�9�S2C��3!�}��-�2L�2�6�H'�M7:����u)G����C#��Z	��\Y{�$pH2V@94�L�a(F&:��\+���qO%�·�m�f�F<�����Ǖ�@�R�۱�G��]f;�TS�`�EH�~a�#�gg�Z%{U�(H`â��K �]P��>8��Ó�����x���f{l"T�K��Қ��A�8��ۅy�����cc<б|y���q��#њ�������G��OA��A�8j#�R䕯Cd�V!���ĝ�:�H�f�R$
:�q.j���Nٖ�Q�Wr)N�Zv��	)���0�yM���Օ�nk����Ph��M��� ,����c�,93�1�,�k��z���*7]��cy�v&.�Fv|Y3>IN����n�B�R��T��k��BK~�Z "�$��K<F\dY���5h~�i���b�.�*��#��x�����m4�t�����'H�DY1�2��<R�Hh�!7X�-E6 m�]Z6a����b�u�J�8�U*G�	ޟ�D0�@�k���"r���UA(;�}p�D�e"�eC�4ؒ_I+����UmXє��v�K�ȑ�L$�b^�R$��Q1G
�;�\.��ڔEy��Ӓo)F&1ƍ3�e��\.�z_Ϧ��F��EJ�x%�^<�9!]�w�ڻ�u����HY�o�x�#���M<�iq�c,��홖4�-YAn��]�������XHtW΅S��`X"JՃQI�OQ�cF%���a��_����`X�=���J�f���~c�4s�C/�	|��2�e
I�'���K)���e�'up�ÐP��}69%c�9����v'sR(iC]�,sziB� Z�Q����=�H�)g
�˷V����d����Q���|h�n;�Ēk$��P�5]���ӂtS�Qx�b9'�f�e��r�Q(��ԚP�zֻ���墿(�R�J"��E��2�������=t��z��T��cE����
P�Z���)Z�t��E���,ItI�U�띲Rkm��:��@6�s��[�&^l�Qb�[���Z<�,�[D�	Z���@�jM	�����!��t����dɛ��<k��*GQ�..J[��ڰ#3�Q���<��"H<�>*�`K�4Y�ϓ�[��%�fTXb�Y�P
��(*wָ&��0Z9ml�{��s��SZoAS���lԙ�E\�!��jM
;��3�Z�4b�Lf$�
E��yt�"e�Ƭ���i��Fu]��?��������[�'Z�SM�������nCqs��ŭ1�֣`k�:�G��+9,FN\�`crX*�	(��jUj��T߶ �!��w.�,�]����T���;��fA����0��d�����-�2Α����-�;o��,��&F.{�JI�iQm�#~�	����	�??����A,�s"�a��K-�SҽN�f�w7�$E��>3��Lh���ِݱb�����O��Mڝ4Ar��
���i�'j��ޤ��v��"�$h�Y����76FL�����܌:�z��Ɵ��# "��(�3���,���6��Ќ�ִZ~:|��&k��G�qő ��D��ge,l������A�G��+0�8�1A=���fԸ,m;�==+�Fd�����.[K��C;���D��Hn~�3����ڣ�^��$�p���RE�Xܣa��d���R2������Jܤ�(.S�Zt&�1��A���I�H>&�ˍ2G$Q2�,�Hܕ��KjjF��jj��];��e֘���h�3,q�{��wހ��j�{�7��R��+�?W����#��D��Y@��\6��rn�\��X0��"|y�H>���D[�en�=��O�ڢ���1������<�K�Fd�݈�\|wT���_p���]�7%��A��]y��&2�G"8戰I#1JZA_i��u�ydu�!J�_uu�ߑVS�;�1����]��� ;�~x�W��c�4��-��	�&����;b��.PAJ�x�?���J��a+{T��=jښ��`ǒ�r�KG�?��6^l>4J_��i�-��ֵR�5��9�ILN��g����1�:+�
��D�ICN�Km�Hj���ڑ�	H�	��V�k.f�FV�;F�ZJ� ����=5X�\����Ŗ:@�d\Y�v�‚h���lw#n��� 
��N@��Ϗ%d�D�<��!\$h�pF������R%�]8�/� �\�B�0�݈��"�A4�]�~Z8j:2_��M��\���DK��aT�aR�O�X�l���CO����4�儣Ph�Uj��2���W��͒L�x���ji
4h���Pև�BH;�z�!��H0�c"L&ɆEp���UuS[ma�"�Q��ӔhO��S$��&^2��@eV!OST]����2���P%��r�_,�<�2�hѱ�lw���|nE�j�B=|�N��/o���	����.S'��d��L�E��I4�1irL�݈ ��mM�FoA4��vX������̉`������OnH�b�14�7���(5�5���LH���S��2���}����?�A?*W�eƊ����.4��Y���C�gy����#�@h��s��a K���V������g��uD��v��+Hv5Q�������$5DW�'ÿ!���BY�u���tI����?�t1��rR��,�2\d� "@{���c������mjú 8��F.�kr^Z�/�S�^V,��x�ˊ,i32fUC+�q 
,��@�����	�s^�/-���?X�ߚ5`B���m��o�U�9J�R��J���x�3:Ƅ;T՛��Z���Z�Z�)� 9�V��x��{�\�ů�dui
��W�ځ�k��R�+���D�\<)!h��JM�.W�Y/EW_en�>�鱑�,��2	N#.#�6	v�����Fq�S@H��w��u�qc�]>R.�FH�+>0.��\#8
Y������֙�>�B��;4*�O3�ŕL�%��w��5�]�����l`_&@�Y1�}��m��OC]zx؅�u�y�rE�!��I�n�P]�%*��P��^nc�FA�y^�-p{�Ʃ�L܅�������T��=���]�龩�e���T��>iH�]�G���B�
�U�^z
�]Cp�}��*���M�G�%y%�Y�Z6�/�5&��t�T�j�id\'�9��/){E�3#(�h��ݵ7�S���H'�@�)ŋ�ċ�������p��}=4�"Tj%sTr��C.:�Ż���RV%�B��TH�@N�����'�]>��$��G��j��F�ҟO&"d�x̓2ɞ9���pv8�1��`��3L��q�V���X3��x���q����	$%.!��T�6���.�/d�P�M�.L}�? J�_�TZ\��j��-t�+h	\_P��Nd8����F��vuR@�K�ЃMSd?Ѧ��1�ֶ\�b�d�u�$d˖G*j�ش�i]���֜��:5T"���y���,\�x3+,�9QY��N��A$&��BG���b91�D�w���z����fw��x�?���'.U�E�@���kY^�
�E���L{s��^%�zsq�љRc�ۭ��'�c�O�[:s�j�uI{r2,�ӵJ�#�}>X|��� ���	m�V_&�|��ɋǸ�>~C1��Ŋ�M<�jP��b^�F�)+˗�P5Y�c�X�o�aK\D�L�\�#B�UV�H\�iMv\Xj,:��5H�e��8�N;��>wX!�8�Ѥ/�I����F4�~5�a�c�ܖ	���,�YpC�˭�W�:/!f��yD��[k�o���X�fLeB<��fH�3�Z�Z,V������D(젰�4�mZ��s�������V¾*�O�#dr�aL*Tc�+E��H^�X�nE]�:~ե��oq�I��8�x�X�3:G����n�v�:�e��{�G.(�'���bx�!�����͎-*�_�\X���U7�5�uy�f"%j�ΜQղ@���w+#+'x�,�JĦ�))���=5�C���<}U�.[%31�XYL�$�d&�X�܀M
v�ղd21`k�I�5N�,�s����Y�@Z
P��W�c��GN?��(x|�g\y��DRޮ�ͨzt飄��8�鶨8X����qa��P
²j��բ�@��y&�����y�36+;Ҹ @����A���,�3����92a������KL��� U��K��ϧ�0�K����RG2�˔$(u��BY&�i�W��}�1"��4��G���ț�f�YBݕ]��q,Azӡ���	3�~;	�z�
H{�������mM�ڮ��$�1^����0r���߅r;�C���#w7��fB/�:��#)]R|���L\)e-���ǑLYU������E�t�ϴ�'鱂
U�I�K:c�n�̎�ʃ�Oj�pE�}B�b�2�a���<o�x�o9�;bx����fO]Ջ6�VdUrŠ�)&�
j8�OŒf���E�^O��m�g�O��r�2a#�N�v�N·
�"������\�OΦ6,���'qf�{f��	��R�=jn���d
6�M��+W��g)��!�HLm.C�H���
)�$
k^����ڨd�v�-��K�������q�\5R�h���Ʀ�l�	�����~fT�@"<�r�0��1ʔ�I�M������B:�tr�� �<Q�ƻa�Ґ�ȥ	.@�.��T�=4��ce҉z��4xX�+9���(
iJ~z�(�Y��
��9�7�,��LRx�^�Ȥ�ڏ��Q�徟('m;��1�C/<��S����*(#kMP��'O!�Kh��GKr\F6$���K9��U���.�Q�zϛ���He$�n'
M<R�16��`���+ҧ��RɊ!ɺxH���n$W"�r�D�������衫k�EN�1Y�2�$?5X�[;�� ̢ �e;�d��o�z�]�_�W��^�A��I3�X���
ˏ��7D��u*}¿
iln�J'0�H�c�
;�-#K�r�Ʉ:�k�`�kJ�ԏ^�5�=~��!�}T���ᛆ�6=r�������tچ��}�Nx��5%1��SϘ�(�2$)�#��jZ��ž���C1�R��5avC4����[ʳy�2Ls٤�����Af`~Zےr���(c�'����$��������ꁏl�H��].<���\�]8]yH�\_�<�2�R9�%�8h�5E@a�my92���ҜQn�H�sZh�I�f%��d�C�-��q��m�'z�k����HYB@�䳱�N*,N+��×����+2U��!&���җ2��58�a�ćL\ț��N�
��9n��Y�P
L,A��)Ѓ�k@�����O�]W�,��^�r_!�H{f��e
���ހ�x��U���F���9����)�<rc�2$�5��V��ow磛L��&DB>6��ܛB.�]�%��$�(���ho�]ta�C�BFL��촲1�G����.),��h��A}��|~Z��I�ת� x$���L,��Q&_q�":2�ꗩ��E*=w���;�Ui8-����,�:7\#Io�b��}�ƌOZ��hG�%��*���	�c�T!�;�ޣ���,�)0��Wb;ï;���~K���_��2����\�5Vޝ+���w��Q?�u3���S�2jO!������$�B�7�h�,cv�N��T�Ju����I
_�	�c�]�>B�i���ic��	d/}O�#��b7�9�@?�|tK(��[|>ND��������W�3(T�n��6?��v�O��d5궎|nR䑒�'ni]]��	0Ĩ���W[!cV:�g22<�)���|�����	40!����VU�E`G���.n�{ȗ�π���Ao5�^]�.l�QH�'��|K4��dD�m���Ͼ�@i�=��y�>.,x��	s� �H��9dK��&4�ۚ�h7��e K;uM�b�
�þ7 ��Y1e�H��`$���w�G9����N1-V�bp\�9'+E�ŗ�K��(�k�
QGB~$�dC$M+qӎ��$zx�|��I�\����Y�E�Z�\Y���u�a�ZD?Kz-S�(�;��#.)L���#Ld�)��
��Zkq�ۜ(��d)y<dI8��jj���%�n{ej�
~:�)x:*���C��3�@�I��Ktq�K������
�VPA�i�q��@�"^��E�Xm-�<������XY�T<�0ϋ	�>��	-S!�5`z�r5.�!�n
Y�g��b�o�����T8��H�˚1;"�7����M�5�|&$1X2m�J����fZ�'��u���,���Y����f�~��QˇZ2���6��q0`A���,�3/���B�M6��ּ]���K<�:2�",)��
�%Vfi���.�$m������W�]<Nz#��r
6`i��5�$6�2�QOk
�@"��g�(ȱ
aT�������y�N�����)1��1A$�/�6*CZ�@�wQV��y��b(�fʑ���x����m���'���<o�V8��k������2�������.ccG=�#Hօ҂�����J1�"e�H�i����Q�:���'s>���r����/7a�5�n�&�1�{������@r��g�\�:�f�˖iy���Ì�
$�.AP$�@w�H�����" Z�^.����z&����`�f4�դ	Bd�K��=�0�p��o-宄JY-���l|�J<�اɖ44�E�F�
T
�
k�	;��h�2Y��l��o�77&�UPT%�R�iH���^��]*�m?�f�O�{�VwK-�ctl.�jI �����޺-&�+���
��K�q�؞l2� �Ly;D���TW�}40�ǐDT�~�%��K�Q�x��,�m����$l���׮��}���h�2�4���	"�WrMI;�zt��K�0�*��4�}��5P�U7R_����A/TĀ���i�;1LW.Ж�X�
�Ym]�]�&���mTc�8��HI�`�țP��Q������T��ў"�g��-E��gB���*�u��[����~5Yg���8.^|���y���W}�F[c�>zLY$+Q��3�F�C:�G}�iU��ZS�(^���$D})Œ$��P���sc�B�kXZ�`A���� y!	!)����3d�>.<]����1��w$�*��5٢c�8�S�w';�����2�W)	��Y7Z2�7�-$�O j�LF� �%��.T�6/�Y��B���k_m:֣j�,�Ar8�V��<���|��BJ;Q�DsU)%�����n���e��D0F�˕�I�%���r�����
+]d�^�h'�������f��S+�Ҭb�E��
Az�~:�0m����)��-�9,�ßSx�S?u*PĪ7�cp���e���iu��$����J�rb,�r�d@�R��c�I��aJ]��CPV�Z�G�ś��2�@=��b�Z�E
O�P���$)d�X��e��p��g�?���Е�$���R�k�-R�Ҧ�꓀�Ɔ�����Z��bO�649j�<I3LjS�J�QW"����[l��3���b��V\��1ʏ��d��C�m��Jz�5=i]"E�X�*d ru����A��~[�<f%��B�K�J)��ջ�J���s>_vR��$�k��>%12e�����e�`�C �w�=N���������^�	9E+��j���`
���k6�ƥTě&�|���[h���Yk31`M��ǐ�d�`&\R���e^Uae�=E������^�ą��M�������i���nj�-*�
+WU�S뤆*��ѓn�P���S2+r������-�l~���o����6��R
W�`d���t�V0�&�J�Ӧ�Gb�f])���3>6S�UV�:K*��[z�P���P���	�ȩQ>T>M���C�>�M!K��Ԑ��|k��Z! �ؐy�X��0gH�1����+��5[M)CJS\�k�D�R�/?�?�q�e`�9����d1c�b��@�5���JP��ߕh�(ِ,��0C�Ɖ�*s*V�5�j_��H19g��D�t|G�"&t.+�4h�H��5��kJ�O4c�,@g��.
W���vĖ9�����!�h*�����L��E�@:JNT��9�]$��+A
(s���u�KiZ�:\�Lf^�]8��
�S�<�&4+��O�DzS��CEdQ�U �zV�
O��7�������n*h��b��CP�B]ek@v:����Ӑ��0�~s��3��GO��It4q_#�j�h�Z���H�5�x ^�����%�y!2�Ym:��$l�#E��J8��ܻ��H�2v7�으|W���G�ȏ�������Y-;xr��.^�H�ƅU�f��I_�w"5�J�ph^�9=z�S����'��\L��>�K�rc��X�}NF*̆l�i�{�#3X�I��.��z�:�Zĝ9�|g�����hs?��\��P�22�{�����M.dZƲ$����Y��h���W��y����hpq�p|��%�Ppr$�Ib@�$�RD��	@��!G�66�.����L}�{�?%�$���g�o�<ّ�x�]j��eP;Y@mq$��k�-��aNH��_M�|��|�7'�~�9�V3��Sk#C5�\M=”�F��Kk�������r1`���6Q	%!�H�¤��=?�֟��	b>���sY�/�_��
��9�T)ޞ��"�����mb�vc��A��AU�wHD]!�a��\��d؋��� �IREkz������¥��W��jl�v^'�oُ��c����bs	7�G����,�%{Q	��������<�h 0���%����U�?�7�a�����' �3�No����Q �
�S�]�m�@ɣ��zx�|�{�D�������G�ˇ����r��C�p�S&E�쩨����%��͐���Fa�N�7؏�k�o�����y!�<���,�A�N.Vt������sB(�bc!q�=:h�Jr�<~��x�x�ÿ�������$\��fxV?�_Ihyp����!�$�d^=�e:�T��Dv�!/�}�5�p8|i��>i����a8�H�h���~܎�v�V��
�r�RdhO�#��@��G�q���}���1�+���6t�#(�-x��+�c��U��v���W��uS��:/�_|8��F����J��#>*�Ż"cdQ�U��UDŌ��j]t���z�1ò#�[ϼ�'���#G��f�)��
ǒ�ɂ�؆:��F|��s3�ƶԕɃq� ��a�/��W�A�}�������<~)ג�ȓ%�y�ד',�D���+*W�U
B�
}��<bs�͵`?J�c�U�O�?ُ�?�~a��}n�� K��<ly8�O����(ly]FR���� ���$\}	�x�GH�5�7������%̧�+������﷜��8^7��C�~\|f?�B����<+v�e���)�K���N:��=��
��dC���|~��$@�&�9>�~��9��i��rh���̙Aq���b�Tՙ��J���w�\��襇�]��V�������ĉ2^q)-<�T*��J�k�[�G ��b�e���^�/���ȟ%f�eQ������B��eVJ��m�Y@����f���o!.Y��#R�D��<aJT��@������E�X��*�9���X\�D6bB��Ok��?i������w�����xݩ��a�U�9��;bH�RH�x�X�0#�A�j�AJ���7����E��5	yynG�f�^#�1�Lo�#f#pkS�:�{�B�ap�0�\��.{�5����D�>�~��S��ܺ�v���_n���og����U���_֋��|xVR8�,�9Q�fP=�1Q�w��W� �`�\�����<�<3�d�l�yW�^Uf�Lc����%0߳YV��n)��;�DZ��d{}�c{�5k�=ƈ��ܜp��X�}@�.bY쥑1�(Z�*�t�D���41�v��Y��0��'[�
�,�t��Ч��D�~�Ȫ;K�Tr��Y�Y	�V�ohPO�>�֣R�=�o��*Lmf䅐��E
W�*�68e�?��;�t'��WU�;�I�ǖ�I$ }<�2DI;HC-K�ON��0@�z�!�pc���#��
�;��h
OR6�鮄E}�}P�<�3��K;T�X�2��~z�&dm���6%+�M�3����\H\�-�;t���>����~s�仸2E��O.��H�����ƀi;�<��?j�����%l*6R������I��C)R���?�(`\9K�md<ye�͓
�q�n��3K5A�4ݽ��H�H�갎�y%�`���ɜ���a�k,�Fb��^��R�>�N���jn�sQhgőt13~�RIB����C�k�X�bk��!
<��L���"�M�lT��[�"��걩r�����`�3�J�y�w����)O��O�
�� h���V7!�s�n6G�p!c�K]�k�CH�o��>'^�9sѕ,<5>g^L��1�"�><����d��$�Df�Y��I6���
�vR?=^0�F�cV���DU�ANd�"�1��+�Ө٥fguiOO�ulR��کH�Q=��;
�I,�ߥ+�HmM��p�l�(�)��v�S�4�ˆo��>a��Uq��,^K
||T�	ȓ�̡��5�X���jj-���#_��-�伷�s<"*�˖��H�F�B��1�dU(	&�W����!	1���G[qc��Z�r0��&wŕ����a��ݲ��#@zz����͓�I�w#��1<rȁ���{wj1�ڻ=I�,���]�Ě�|��&<dKN��^�\��M�@���JY6�>�@��Ppr9Ӓ�Ȕ�1��%� ���@�Gᤌ��`z&1�����
�۟e�-��
�N� �*��QQ���K�Ħ0FVNzi���c�E4���1,�Hz����ܷ�svs1�y&��]��C
γR��c�PLz��G��l#&T��� F�\*A=H���Z��f�y�,~{'6,܎Bq��'xY�Aٷ��`Z56%��������лh�K�E��iy̎o(p�q��h�+*FD���*�Z�F�o�G��vQ~:!����/+���0�s��a`��yUJ��҃��M<�����.<o^�|쌄WʈD�$��:�z���������W���ؾT�q�F,����b�%YAl����yǷ�8�F_"3 ��㝒i�rd��H,�����o�}:h���T�.tOr9녑6\F��$��͠�-�jH�z�Y����'����pe��Z���U��%�+OZ�1ζ�b�.��6�4�l��#3C[;�	H���6���4@���Ld�.;>3�da��4P2���,X����*Ӧ�F��<L�wc^��(��Y���9�yq��XHŒX�wZ;��PЦ���:�s��@�੎vLgM,xx���*�<id�p��56��jd�90-Q��$�H�U���Ǖd��s�$�(�au*
w0�4��.�%�ӊ&�
5T�:��M�Rp�d�{X&�X�ە֛�ЏtK� q320�q���ɑ �Q��udƉaSV�3�n]�&�����w�W�8����E+���d��[��$v衅}��5��i��O�� ���h�d@�Bݓ!�w9
��5l$�J=d��jY�$�q��u��a�D�T���Ԍ�H>�mv����˓"r9rGݺ��L*�}B��:i% �ē�$��ˍ�c��2c@����h?
h�r|�&����gar�Y��E'���(�&$U��TҢ�:��Ʉ�@Ȑz����)�Z�<�sX�9�nl����.T��zU������kV.�ǶN:��x�	q^�/'�e��>g�Y�l�<��Bbh¢"����e,(7"�c��A�D~6
��y/�8�gId�Ʊ2!�!���T��;o������ܓ÷;��;�9�E��'jH��WT ���Š����<Љn:�2�����x�y1Lj2^xm	������z�r>#O ]�) U�j��� �ϲU��ҸF"��ԃZ�A5��f�`�.h��2(PN���,O��X���0�c�|�� g�/OE�+ve�VQ��Z$Ƌ���J���BV�iE���;����[&��A1�0Nr+��iH�
��5G'���J�Ƕ\0T�s�����(ص����V����V�!6%�&X�W��9ԈQ�p�C%�W�P*��:"U��łNa��7���$Ȳ�T�5X5!���@�Q�r���PV�D�q�9�2|~��(�X��$E!�<mB���m룘�frC���O�dx�Q�|��r����ɔO(������g*	;:t�p@
đ��j�S�l�NnD��@ʅ�X��)5�<�Ve[h��R�uߦ�Bo'��ԥ���iT���f�2vJn}�m����Q e�Y��λ/d!����?E%�ShP����ϔ���Ȫ���ʼnp�K����,mՊ�+V'����m�WtۺEƬ��*f�H[�oe	�w5��T���T���椐c*��U���&���D��9.��^Yr�ʴQ\�	QZ�6�	&��j�����G�\0P�4�ݿO��$�*�����;/4�mk.KG0f���Ɗw���P�d�(��"�9��_3��pHG����S}�	�]�~�HU�V�\��1�y`{GQdk�~�mp-4�����H����
��>�w	�;�rF�)��5̍�K�C�e�j��?��<�+.�/��#�9B�Pg&K�����$mPᇨ;(��ɣ7���i��
�~������M$���M�^�h܀��@��m�ԪMBf$y��H��w�؃&���JT�>]5>�.�q�ULPr�/���x���`�im�z
�hkO��2b�RFdx,�\�c��$YAczG�`)���CT���L�2�����"�|�L�Q$��$҄����ry_�
(��;'����#%��`���ۭ�z�]�u�2�K�R#u�"���3�Lh���ە���nz���4�(FUHes�ox��f�#��R୑$1�#�	fr.�!kJ�u	���w��*�\YѸLƕd8I��V�B@�+*���].��4r�薛��yr2㑲��e�n2��b�������R�7'��GL`̉��d�qG��ye�L�xQ�EC�Q
�ր
��N��Dǃ�(�u����SK�(O	c ڻe[�
=wۦ�1�K$2��\�X؛�y\;���Z�ж��}O�Z�j����M�/�8�yH��[�Qԕ�
R��$ ���>����q���"���q�1���� �Wk�^����3��iHS�|����Q.j�����8Y�����
�<Bf�Xc4��R��S^�Y���!�t��W&Q�R2׆R0|�8ĸ�E.<�Y�䨮B���I%GSJ����1�C�!0'�_�f���х
�P�
V��kQA��b���`Dܶ��x1q'}�KFii�c��]h�b�u!�(<�?�4R@�څ�K*�UU�Ϊw����<uF���&����r�9$h͎MV$���^fy�wS��c��ǹ�'?���\ώ�E~"�m�JBk����^v���\8Z2b��ҋK#)�R]!�^6�HV����kʹ�To�;�z����Yc���嗏��Z!��uP�J��W��#���&S&oR�Q�;�2��$�p��HlD{�$��zQ����+�>��0g�f���&L)�r�%CV>��$R�)]<I!��������2aNM#Lx�R�5��P���zҀS}?�`�҉��Y+S4Rd�>�]J�@�ɨ
z��ԲD��!/����y�gJN4Ҵ�
�
IM�5�O�~ZLx�\��#9	3i�,NB6�9�ĆOvCFp"Q�{��	
N�][������K
oT�?%�x���R͝�\�0r���z�H�PY����he�o2$_�es�9K��y�R�됹7],)`P��n)E%w�ؑJ���&7;��:�����\fW�)Y=��D!#����ҽ}u3��4��F&�8�$9.n~/#��.|/�p��7j��V��S�Z�������&�L��8�.A !.�pRTZT�ǧ��]�e2�"#E�>Nf\�<�8��m��h�
���߆�����mL3�~oH�.�"+p}�r- �+�F���E�q��2dB�dO������m���(`�ƉN3�k�szq�e��X�{�ȡH"�H�kAJj��a��J#$i�U>��a4_�*�Ƹ�d��0َ����H7��4��ⰱ��"�����h���� ,�U�fA_ZWz`�,Rk���ʳ�$�o3���A���$�6f#Vfx�P�ƪ�Z�i�5���v�=B��eP���Sf(s��~�V<���a���W�e���զ��� P�U8as[ ����:C�"cJ;-$�y	e�Zn���\rFdU4A�E���x�O�"���I�(.JU�
�̴ކ�4�\NH�Z���8���ąS�|q�3,�!���2�5+U"��ҟ<X�rC���K�^���ŋ��B�b&I$VR�@FƠPS�ץm>�Y%�.����I��!fS�$L�#f�=d-��sZM�:��-�yL��� |�F�F�4x�<�w�E7޾�A���S2yP��B���2g�,h$�9
f�B�C���=
~#Q�@<¦9��o��["�K.Y2��B�*$g�%��OT��l�a�<�F����_6����՗��x�:}\������R� 5�4j(*k����D
���&�C�|諃̮<6b�"Y<S!h�VeB	�v{[`Xng�dF��&8�Uf�s�o+��d��2B��0�)`����Q�$��a�d	��1�8�����d�ά#1��UZ2�S�$І�]έѐ����I(���p�C��n���fP�)jAb����55�N��<2�	�z�#����NO<�%f:Ī^Ŝ�B
ցE}�zX���)�4G�)�&,��S,H�+("��	 �j(^�Yc$��N^������H'Yf�UDu���Z��=&��"%�q!��ĩ<��da��u\y_���P����r�uB�^�����q-u�bL���ڶR�����6��L�S)6�+E��I�5U�c��S��4��u����r�����&. y�C$N�����Pn��S�+��!Q��:int�3x��\�syS��3���.DL��3DkUY�
GOQ]C�������<�EA������)�������aH���)B�*���Mb�~|rH@5Z>9�&:�|��t�d�PD%���J҃�]z��%�,��扡$X��	�6��;��)���z�i%��l�e���r�6TJ�ܐ+�-�)w�^��<fF�j�
X��9�c�=��4��ﶄV��Ew�с.��f�cb��Q��23��L�Ԧ��J�tہ5o�M��H��EgX�;l��A�S���e 4X����M��̠�~��)o���|�i��,�C�,�y��rb��E��'�ljQ�Ǔ��h}�a`[����w,X�<�OD�$�0�.f�ymj���J��z�:�=uX�Z5HdA^{�W`q��P�谕ݤ �n��R>'�k��A���D���<\K$�p����dc��U�4%���A�ㄧ� k�DӐ��Nkt�kc*w�ܐѪ�E��@	���[w��V}R�e���ie#"x�1�X�AIWz�ZА4�@�z}S3�g�y2A#���)3�#��T�	N���l��o�xc	�����
+���[�P�Z=�W�5)�'��)8p93��,$��qIJ$U`�T���M��d��.� ���վ����7��)��h��22a����e��Y��l6$�v�k�\@=���g-VS��q������g�8��"L��LRFAR��j�Қ�(Ƈ���\|,�#��x�#�eW�J	
��>��2����PE���4�<b⌈L���Ϛ�Q	�*0
��-���O��hл����)��Y�'Ӌ,h��d%��BiR��N�� ���9��N\Y���$��*$1A��j��n.
X����8Hʭ��?~J�!���1�r2�"���D,�W��PV��c���5;m�GDl�}�.S��Hxˤ��h%�V�O�u�	�Up���,�t᳹Q�� F,
yȲ���H�E>b��Y�̀��%�
+O��|�8�O,8ʱ�Ȋ�e���A��|��8�ى�AҺ���<��Տ�i����V��Pbv�f��uץ,BUu�sQS3��$>:����c{�[�S�H��~��"�
�0�YVEc�R/ԭcCO���c��ۮ�ɨ�1�l��RLyH>Ǒf��V_g�@�C�@�|MRj���6k�P6<9c������W����EMM=5=�
[�	�U����I���cD�!E���.�,	�}�iҟ�������er���B��F��-��P�֛�>��7��#���m�f�9��$S��H�V�H�
~�em�X�#R�PS��'w	�#Q��J ���	��"�c@�OibaF
QP��O�c� �)ǹ�ᗇ��y�`Qd�it�Z��+J84��?-љ&E���n^������^M�2��Ţ�(�B�������H��ji�S�Ig���c�
S9ʿ
�dxذ%��J��
ރ�0��Fq&���9�	x�H��A�Ė�]���[H���עє[�4Y����Ḯ:>��\n:�Q( �QJ(m����DE�}�x*E��7�j)�dE�����u�j��
��2��;=]��Z�}��g�� ��i$8҆�B�Z8H�۶�ւ�u��a8�'m}�9��uC��$�Ҟ?����˃����7<�6����0���T
kv�c �(d��#U���I���ce�w��w����*��'���R�ܘ�1!�UX�$�Ȝi���'�忽IJ҈��!TшSO�u������$\i$P-R�)b^���!X��J0JB{��3�928�V��;j�RB����^��Z1�,Ŕ�:�#�?���K�sXq��F��'.���.b���;z�a��ǔ�ŝ+(�>�ˎY0a�2� j�6��Hޝu	��q���^�,�S���Ɍ�
&x���’Vchv�����{�й���"���ɕ���m#�=�!�ಝ�R�1qn�JLV:�f,�8�I�9��2/zX��ԥ��-�4���]X1(�S3�$ĆC��e�jZ�'qN�
�8.�Z�Pq|�ON޷r.��e=�`�HZ@�T�Rw7����|��)nj��51��P̨�@Ee�}֐Uzu�nHqD�%YO�<�ȗ�2EȒ.�b��0����O��3�Z�����(�99�s12vӷ"(bC�z� V��G�K(����b$�G�9\D<n���œ.vB�X�k@�н&a#��ŊH'�)��(n��t���I�R���f�|h�/iT�oKI#`H��ks�n����Ջ+����rQ�|���E�e�w{�e�嶅I;~ jXqQŅ��O9�3�*���1�^%�&4$�k#��M�j�"���@T2�k
�����Jm��W�e�c@��֠V���<r�.�!0�W?�C�r3;f�4��|W���j(�#�:hmf�s�]��gy?�q\��x��H�|��
C�B�0�"��YCTE()�g���x�TŒT<��7�qO��w��0Ǎ�4��iFb�N�ĝR��kw�o�I�E�	�\9�%\��2�n��^��?�L"N�uD�b4Ieq��?�R7u%c##Ī���	�V��=:�H�pa�M�3)?/�g���"<hp��n	n:��Z9v�'�fm��:�@�I��>��0	pg�������i �2�^eF%&�0&�=���r��pڑχDH��c�$�-�r<LJfA��8G.T��9ƺh�D�v[�\�J�]@F��9>_oXNp� Y���9/��#\3|���aEq�H���u��ch�}�sK�'����K^G��,�)�H���ܒe5
n�WbF��$r�D^���uID$j��y9,�ZY�e���k���,+�Q�,�~��K�Tn�	~,��
�/�m���fL��.T�";4��Jo�5�'j��T�laWI,ۉ6d?��㥚����@h�PD����j��QOC�K�0%�Sn2Kq|
��dK.:�Qk��H���Q�~�q�"Z�2��U~����q�bÌ��0ǂX�'��}��k�5��x΄[�A� �w�ɟ�'%�df�c�OҴ)�Yo�P��@MN����.�m��^4�f9��Dݸг��,�|{���`}���S��d����ˇ���l�
e���2
ԃQ����&�+Lr������&gB�G��cQ#��X�=�
	�~?=.\�E���?s��V9��̋��$��L�ݱ����IVZ5i�	:�4e]u�1�,������񣛋���|<�V=�Q��B�����TɆ
 
lE����&�Z\����x��l�b5\c;H#M�n���i "�ځ�tNOs�>�S���8\�0� ��,��q����=N�\�XJ�:%�(��\ת�8ȱ'���~��+(hh�Ơ���`i�}����V<�b�c\\~=�ņC�pe�E$;�1��h�;�o�]ZF$�Q��R��C�輦�ZQU@G,�p#�����C^���	���Js:{EȑI6$,���,VBK8u
w�5Ct�qb�ס��h����9c��\�R_<�K�27�Z-�]�z���z=��bՕ9S�Yq����^F�E��M8�<�"+�$%!ބ*��-�`v �y��'$6����~�_l=�wu�%�q�},Xi�W�;
�07TЖ���̘�e�
�'H�U2<~Hq�l�R�)ޮ��C�S�P�}b=��'i���3֕'���`�<K�`f��C����r�v���ϴ/���6Z#��ζ �܌Ȳ3�M�x�IX�����Pzz�����|�)�ɖ�gd��[pB�:�A#*k����ׯ8��mj�@��9��\(��u��ǚ�B5}�Z�ݺ�#bG����0��_i�ƋLZD�gS�r�:O�HnsI��ܲ�JT@��],�W��0o%yա�y]E���I��?�\Ca��;�^?�$L�vv2��,IZ��)�D�h�Qf\��\5�ڪ�����M�Tu�fe�}��Y��`�o�y^?���}�PQS��4g���u�a�-9[.R8a�,uu{/V���O�`�5�ŀ�Z��ZX=�)<�!��d�PE��}j���I~zx(䛷D�bF���t
�	�F�`�T��h��5R�Z%��8T�dZm!�5��~��}~�_���S∛���Ǖ8�a�� ����Q{HAa!/�6����Tg~=Q���!��b�,xY�K�2+ʙL���F�Z2�[� ML�N,�j������`��H�P��� ��-X��S�;��I��*d#EU�~ђBh�0f��oս+S��	��Ĕ�#L'!C+FU�@���)��QɎ"2��$p|SBD�=YdÏ>/$���I�!d� �ZMKu��mc�	��[�+�h5M�-��M��)\�r�c	r�߶k�iM>z��Ci-�i�ai8�o,�>30�rM��leZ��p�
��6�
��$� 9���'��9`G�(��,���KX��:Շ���Cp�a&F��x�Ll�����v�$���z@��`H����p�0Iqb����:�D!\�N;��U�tFz�bO�K@`=\���>�T�,�Y~A��,k�rBw�	E�coQSS�S���P�b8W�����8�#U��/l3~�4�zk�w����՗f�rJ���+���S�r0���yZd��ۊѹP	�������P����N�q�������%\�e
S���%��`�4���&�U��\���#sN��H�ǵ������U���y�1��Y0LSL^ #s:2Ѫ�6=�*WX�����։Gak��[7!q�Q�⬮\^�H+@I�n5�{����e���	"v0cB�6gkNቭ��1��v?D�7$��$�}H��UE��5�Щ:�
�f*F��`���\$��:���rddF�^P$cd��,��>��k<��e�c�_�WL��	�ZC#��$����@@oa��4�M#�s�u���t�7b<q�X���R�
�)��S{�WmT��8p4uǿ���
%U,Ĩ[A��m�]<���,%��S��.p���<��H�"ep佖�ܢ��-M���]��c������a�O�)�9&p'���&K��o���c�����T����Ld�DR�ݍ~�$���)$u}Co_���	��w�s
gƓ�V�Ș4�2e#��K)���t���h�d7o�?���_��g2+�I	��X���i���(�_��������("@_�Hrc��TV�[�5S_Pv
bd���"z!�i�r^3w?��<�wC`�ɍ�(��Ȯ32<h��u@`�e��5���l�'�~"�am�V��C��֯Jя�:��|l6�?"�pɻ_�g!$�,���h�ܵ���$����J�j�S�T���7�~�N'P<�~;�����K::EP����`B����#s�jjݾ)9���S�8����ﷂ�o�y$��Ӕ�T�~�?���CAJ�57"�,�
1�)�A{ӏ�xgb���� ��y�X�~�q��x�{��+�r$T�ĖbK�6�������r�jY���5���~5�~-�}��|�s2`x�N+���Y#Â�*d��p�IIIc�)�:�]D�6�+w��n����B	�1����2I����|Ǝ�&.�U"LR}�
�a#��~|~�S�'{�/�a���*E�H�����8y�7�;LO<g��n,ۈr���J+�w�1�I#Ž|�&q�e�2~�s�)���\����7����9`h�D��'UR�QY����s'?���h��^�+��<c����,��yD���%ha��T���a�w"� ��ƪL%&8�4�C�o�dy#�+0��97yR��g}�X�I'm�i���Y�dь� ��E��x�G ��X����&�q�,�2F��qZ
��']Wq��F �j�<�0�I!��̹FlnS7��Bn�?Q#\��ݪY�&�@����&����fz��s?k��}���?o�ߓ�i10~�/�?8̒�1�v�)�1ʄ�T�CO�"{v@.Eo�>M���q��<�7���r��
}dp?
�`�T$5<6^4A��X
�jY̥��:T~A[C�>���|��'�3ώ�%���-��3��I��=���͓6S�G�*e7
X��g9�n���?O�PT�/��2}��5��q��d� ll̨Ē����P�ʭ��Y]��$w� ��^鿺?�7���O�}�����;n>c��f��$�q�.y'4��'aZ�]�o)K�?o�<CVL�6�⿽f_+�<{;�~�d�)��δ�dxO�Й�@rg̗iX�ED�H��w�8c���ݵk@}MȲ�he@,�_辽���O���3��P�n"�[�쓎!�q�cĒBH���� pJ���_A�K�>��g�p?ax����>k��|k��|h��<8��1p�2\�����y��b32�fU#��!�@E��V�J���Y?��7�=�j<�<�|9��#�7��)��K�e�2UOb2B�u�� �6:�ͪ|ֹg�~��[rg��.��C�W���%H<��ai%���|l�H��v=�\���KQ�q�bL���R4����T��8���>!��������Ã���gċ��ņ̕Y@}�+ip�V�T�w97�e��7��dp\D��6.&:���o���e_i7��J+1?*��=�1'�b�ݗ��Ė�TA>'9<����IeP�hr�F���P]J�Җ�k�S�ˋ��9	��z��['�2�9�Ś#�b�
��^��N��Tg��9�`@qך��DlV�#���&WvK���Z�n�Jҵ]�F��vLb�*����G2������S�d�lV��p�$��,��塜H���V��`����q�|�&,R�+�cCv�	4����<ء���詎R&�S�G!���s1�rM�#I�VOv�Z�M��vp�2�Ѫh�����Hno��ޛ��7��E[o�>�o��V�ϭk��l�k���̹�z�^�C)��"�����āRR���MzX��9,sry��%8���v+�@�T����N�ոDns�Q�dY+7���F،cǖ��NB�����:Z�#�䋍Sf�,��nN|�A����4TD	u6����\��Ii8�Ÿ�����_"�HNUI�v1�U,e=@ ��n��W�|��j��Ò|8��rU�1��]O�Qv�#�����c�Icr9<o48�Vl�g-3N�YIh�#e	���a�V�p��?��G=�Ň#e�ő2�*�R�V�T oAJ���5��5�W��y�b����Z82I��q�PA�������M�#w7��p���I�lL.�Z�@(�(S���F#-����Jdm�%�Y���kq����
��B�
�}�a���'�U1�5�1��Ñ���H�F pō{q�����uv�I���:}��FF���hV ���,o$B�8�ٮ�h�}���ǎ,�K�`�X�=��$�)R��B֗��}lØ��Υ8:%�&'�K�p��..f&.4�#"S�P��,�@�D�Ĕv��)��7p�5�Ү��.��Op�o"���������=>:���D�,�N_���U�el�@9`͏\ʑ�j�zW^tL�1�^_�����8��v�?˜g�Hq%bZ���b4�Z(+AM1�B�ˎL����m�MyA�cZ��9�ln�:�d矕R��.�䉚�$!Z5��RCuSC�^��;ПҔ��d�vg9'�Í�X�ƙ���r�	Z��ؒM:jFR�؃EP��R��ܞ;ȱ�\���s?\!"��� V�]JSҵԳ��� ��T� K��Q'��'7�C8�Ǔ�^����&��”��e���73B�6ӂ�/��9�&E�2U��!��P�S���c��"�x}�K!"�ɉy�<{�—����$P-jH��@iP�H������E*v�j�,�d�@�fG��4e)���!X{t�dԮ۠C�t�x�Ǖ���00�����͸n�= x�S�IB�9Lu�JJ�XH[�V�hJ�-�i2��d��yh0�H�[�c�E,�W��$Z���HO�#\W�Bsk"a�����Β��k�j��)[����P�OQ�F-"H�.�uE�3����=����T
e�GR�h5�]�?\y��|��y�O����T�V���MI���C+���O�9)ap�
U�cQv�kvl�r�
H�OR����?��ǏU�"��TnIO#�#JX�������P��Л���I�(4~g<2�;��M"���&�w��z�B$������֜gx/__%9��Y�&)���Y�c�k2�~��P$@E@����\ �F�����]����ax�]Tjق|��i�y�^�cu��M�ڛk�Lj��u��Z��6,i��և3����;n%�j�QV
����@l�[UBD���|^xyy1��6\3��r]�tv�
v�]�j�a"�G�Y@p�KDZ�^?.3!}�ة�ZiK��F.Y��uڤj�f,Cx�G 7DX�&�G�q��E/zqZ;JhIP��lH?-j�oI�꜓����%��Ŭ�)
�R}ۑ�}Vy�77"�c����YeVƚ7	'��x@c�0Ei_�M'�Hw��Lq��Sή_7�h��\H�<��12��q��ҵ�7q�R��S�>9�&���9<Q�E�$Y��K,��E�\�>��l��jʚ�p��f������[�g�a�0��H��QDq�x�d*��@Af�-���}�����0Z�I89N$�����l�b����oJ�X�5�;Vy6���D�2d�R��+���d�{MH4i�8��-7��]K$�rd>>�r�Rd�J�d�����aG��AКu������̉���l��(haW0�K��%�R+I$��'�qb���@>�ZĘ�˪��g���b�1d,�K!6!uK
���
t���Y�f�l媮88�+E���"cb�nbl�f�۔+3hEX~�Mv"��iZx#6tk��yX��Þ�C�qR9��X�dcU
A�7:�
��V�����O���X��013�& �.5"0�T�K��ʤJ7ރzV��sa�2@b�
Vؘ͉���Yqr��1 �/�`|��#li�����E�t5��ǎB!�G.?��RD�+d�Ϋ2F���=�de@��ZB�*��;T�d��(5T��Z�!�9_�ry)"l�d��2r%��{�Z(8Ȍ4 n6�i��2Ku|\S��T�"���F���8��'�HKH%�/FQ@���O���(�4����H�j���_?�ȝ{fbѼBkݣ�η[Fe5��}z����||�l�
e����a��9I%{NkIƄ�N•��W^�%���]a���2�~*K��Q�U�
�MU�SZ
�z���4DL�Oq�����d9�o^�uc��@-AO�O��sN9$� 
�;���L�W��T=Z��.O�=5�"�jQԉ2��f����\O�2FD��3DܖS^�޺ϒ��ߚ��b+e�˰<'�����,�`�$�0�\�C5�	e�y�$��Ж 
�����bA,^,��mŶe�CS�s����WŶW�B(yN4�`e���й�)"h� ����SC&@���st�f[\KO�\w��eA�\��2^0��RhΣ���ax����%K�S��4��$JF
@�h�F�([iPP�Z���[Rd.��$Xu[���G#��8��V���rN�������qf��Dq���rQ��n.KKM a#�wP�Z�k�E��w�
j%�~�"�)�+�r�ؿR"�t��.SsOBN��u�5d+UX
"%�(�$�wo�enS
� V��u��7�T|�"�7t��g6/Pv�h"jW!)�e�q��aA�H����
�eS�]�
�m��O�]��s&1��CB׻SR
����R��֡��=��$��40������6�s	�\��ni�!�Zd��MDk�T^�|�@N�
}WI���0�Á�%pr;�$�(v%\T5:*:u֜8D2L�2.���2�I&H�s�V�d�z�Ӧ�@9`OY�J��y��9{ڬ��ѽJ��So���1��^�IF"A)NM�3"i#�X�����G#{��Dm�
�����&>���tu�.3����˶D���!VR��բ�M�ן��G.x����1ۢs
��-Ur
�$i%	IXѫk u��4,o������bM/-�,�����q�l	� �t�����Q�
���U��	.'�ňͅ|�f�J���;�k�{��SXqw"�k��<f�D.s����!�Y�!ےh�*�!Z�����ir����+���ZY��%h��UYJ��ZдdБP�z��֌��v�ҪQj?ڨ�	��z��V4���^�"��V֥	���R�?��3�fIa��4��		�F\N�m����oMa$��2�a�9��c�Qqp�b���NѠ����@�uxa������@E�����γ}5*?��K��nm���M������O.k��<vL���/x_#i ��I��v�Ȇ���1u �����0d�,��8�R�`�o���sdGC�v.^$Z+i�=k��h�&r0���P�&q10���:�ԩ4�u߮�B\��?v|�iy�	'1�I���c#�B�0��
�
y��N2���Q��A�+Q`Dt���l�{M��$����s[�SsP��WWB�ʌ�@�)X]�W���AZ^Ɓ@}uA�X�Jn��sy8�9�&�ˁI�k�[�RƵ���$��%N��ʎ`�>5c3�dd�t�Ƃ��W�qEw�N�
�65U&����"������qLr>���	I��)�u�:�龱ú���b�
��M��B�Ҽ�y3w1�HUhv�w���W��mg�f%�w��S_4�)���r��l�J����+.<(g��Һ��Չ;m���
��(��:����g��G�I��R+_y����Ni�h
,I�$Q�"�J3dG�G�,=
k�}3���E��K�1���Qe��j�I�nV���h��\]�<~v.S�D(����*�,P�-]�^��K�.�E��j���D��<�܄�:�K(�v$�}���_����c!�u��N,Pa��h��T0�E�� �|k��/�S%ꖅ$��<�6:ZK"��C&�@:��az����nb�̑�ō�2���HZ��u�|��$��B�"<����Al�T����F��#�@�&
�k����X�"[������S3���e�Ìg�Fa�!i��HS�v�M��	�s�Cn)��<��F��%�b�#�E_������%�[襸Wo���&D\.L�el�y`I%���1q*�CJG�ן���v�OV�Z��q��[�yc8�F��ԆYK~�;;�%k��
��
��4��L��ş�(L�q�F�X�~H�4��'U_F544뾬�/�V�U���-��&|\�;���9]q����kI����q+P�1�V���e�R�����UJ���͵����5�rg�R�����X��~k#rRH�䈙J�h�:��7��5 �+Mʜ����9�@�,�l��uW}q�w����6���ea��!����ۈȫ*=��Z��4���@Q8�5^ȱW��"?O�dy�D�'��}ª��Si4]��wx~9D�
�j���F@{��2��h�T�5hќ"�a[�)[�N�@dL��D�aK��.:,�ȉX�ɚ�А�ŏf�1������O�wpܪȸe��ř�|fD�4n��ҧu�WF(���J��}H"�:�,[�:�e�8L�FO%��yi�"�3��dRz"�s��֚l��	,��d��H�	��+���ɶH�Q@��Tg5�R'4`Cl
F�5|e��s{�$��������1�r�h���O�]�4$�kJUv=4�ci��a�y��q�`�E~���Pf���,��h�p5�9D��ⴘ�wNO�́�.e���G��Fp�
�oN��kAۢC��E'�}W��~!��9���:���2>�KO��m#����X�����1�����m'b���G�.$�<.%�G!���)�P�У�WaНf�d��
�M��E�)����)�	dIdG��c"$������ԹP(
z�CMn�-���B2j%y||��s0�(G���yb)Q"8�Ԅ5I�RJ �V��4���~4�*� iN��$��l�l\�ty�I%fu-@�	SRi�Bg�A����iLM�B��s�Ld�I>R�k<T
ҳ{A
B�cA�S���s�_�*�:S�
�8I�0��hd�Y�KՋ�(VhҀlP�����b���y�'��^7�']ـ(J:�Q(�Mk��t֩��ON(�&���rG��^���Ȅb�b��^�fb��9.�96+�!���C�q�-�E	��
�N�[~�>�&T����b���#H��3�ji�w���!���D]y�+��_��y�������G��U����!s�q�4����X�Ӡ��-8��v�ǍW�����-��Ǖ����"����τ����C��eP��u��yȂ�Ə�Zv�pƿu��#�9X�d���q�(����Ŝ�����`H��𘖍[��C/.~�9�+�VH`�Z2
5��+��]Ej)�u��/k��!���?��6s��#���{X��ˋ
nX
�}5����r��H<�'��ɐG��{e�&�i!������Ei�N�i׎k,��y��c�CƟ�]����MM+JR�zj�GqHwf����4�S+D�Y!#ܫJSp��u�"���;�+ra,�.�14UZn
:�����G�LY���b���6aE���$ݿ_���@HB��2rm�C���*�u5$�
w�s�TX�Z�"�⃑x#R�UI R��`�k��K"bnF��Q>�Zb�	�]�d�R����W����i�S�'K�'�\<|�za4�lvx��Ibպ��mKW�zk���q���J�E�q���B�+˕I%_&P��S���t�#:h�CD쑶d�y��܎Ԥ�([�%I�]�;��B���&Rh"ɂG0�r'bIaJX��RՐ%w�=t���(x�$bX��9,�|̬>*n�Dr�hH�hn��GK?SMc�ܘ��c�!�/>Mr^����RK�j\0��F�b7W��Sh$��?�ŕ��seT��u�yOZ-��C[�1�#��ݼL�����R�Z�����RI8숗�HaR7�n�G��u1��( M<)�⊙w\�Q��:U�.H�;���f7$f�P�$�]z������z,��}�Y/�bq!9ƃ���!t�
"���*�g�JR�O�^n>��d�qJy�3͹��Jp�s���Ȑr��H$2�)����}�[r�H��}���<��y��m��"Ix�p��UV�$7@5ڛ��]4s�~�2Q�mt9�l���x��VP�-M�_Q�w�K9��,a@��s���X8�����2=fr7��(M+Mj�!/�Re�����<O��3U"�M�M��諽i������<T́��7m)vIK�/�bb����*�WXm����r�j~=5�~19qKy�V0�#Z2ۋ��&)bB�)0��}�pTT�����^�����<�3�&2�WQ�!eΑW��\1�+�և�֟�V�Fz=�����$t�+H�m.�P6=MvߥuX4�T��c>le��!r~څt���&��$�Բ�s���=��G�+��_xZn?���F�Ĩ9Dec‘+c�{n{q�"Yz�M+�jN��7�vۣr
:c�e�K���{�/��'O���gY�eK��=���ĉ =�
�ed٫����@n6���%���·!��L옃�*K$��ER��Yzt�c����ui���N:��5Xn�D��
֛�?���m�����lxb�g�K ���F$EFr	4��7���/m�I��#�Z�(�Ü8�X�<8ો�� �P�lJՅt��
R@]�<�O)�����BC<�Fqr�<r����
	 Hԥ�X� H}<Ī9"�%.+v�� ��f��g�i�
��
���/���/���ǸN�dA�
؟�UdHiUb������k�o2
ϗI������@��?!��u��~����ҝ�m>H�b%܍?�s6k��<h�B%�村�YPk��ש�@X�Rz��gÀ�ͤMݼ����1����X��_f6�
��LF!iݒ�p+Rڝu�?��,�dMu7X9�_�Ք)H��Q���^�I��AQC�ـ���$�&n,Y�g�(���X�c������ꦄ�YDʭP����q
<�C��� �J�Et.�Z�=@%z�뾖"p��5�13/c�dJΈ�T?���*[s֤�[!=���P1s]�?+��6A$ݻ�@��F�3�&�w�c�ֹ{�7nN%�$�M3Cn:0'�����$��F��C��S&Z;r,���'?��
r9l�N�x�l�#,%e-TVꊂM�e"��ꑝX��˧#�q2K�	 �9Eiቕ�2H��F�]�:��},ܓ�Y�G�cN�$���{cW��ŨMǠj�����c�X��U�x"r�A���?�e1�$96Ղ,�f���n�����Fd��9l����,}�I#h�u��
I����?=NYfA��*�D�(���E@�6	"<��n�z
Ԋ�Zc)��H\��������t��
�����PV�51�d�Zx's�k���|~i�+>BG01U��2���+�Ӊp�0��ʚ!`O��G�c��E��Ej@�k@v�Σj�ª�ew�`�������~{z�
 �eA}J T�#><��[LU��H̥�b��$�=	��eхoTD�]�]���D[��n����]�ed��I��I�@�;\��w#��d���4-a��&�x@��S�_���JPx��@I:r>2��`��-T�̭^�����[G�U8���|�<Y���2d�N����!Op��F���u\ն����89rx���ɘ�$	2D�P�܈k}�� ��{v�OV�䜈K�	�.&��Ƀ"w���%�*�(bQ�‹ZV���{r��'�,�Px������\`I���!E��Z�SQ�2K����yJ���O���&�`r⏶�1n�
�������ZI@�cK������A���Ȇ+���37t\��
����M�<����o�&����CʎH���WF�!���H�!�6f��`چBw����X���j�7&\���!.�HI@�$�u4P�u�@�x�Y��x����0��Ɍ$�%V!�yH�碁Jm��ÊX̠i�	ԟ�z��$��e��$�ȓ���&��z�hKk@Hӯ�^�f9=�EJŒ^�]�s�l�%I��y����/`��VER�%�Z�
�l��>˒HZ�uH�x��s���Ȉ���!1�3��q�,�4"�\kp+�@����6-Z�PNc�6�qg2�r(NA�%ˉ%c1��p�eQa��
�����Iab.ǎ��R�E|<\_�baB�K,�������J�7�]h�cJ�3"MP�\��'��ȖH��LxѺ<���Ҥ�
|��y	A���D��lN$h�D?Q!V���fbI�H
z�Z̜���!�`�9p�1�1�,%+5�J�Ҥ�>Z��o�2�fƬ��~j>|�w�2��B���Aj2��`
�c�
�E��d1,�?*����W#/�B&li��.YIY�,���+��VR���i���7�f�OJAq�,\<\��Ly�F��d����H�=��������֜��Mv�S�����G9�WcA�h��^�z�b%	oi�	���%���->��^��#��j>K��/2/�c���\��HJ���Z�(�LJ4,�Y��4v����|w7+&T�H��0�2:��eE	�����{8'"[�E�(��Kr靍�&,V�I�@ȡ�Af#��&��؍y��X;�^���j���H�<�k�O+$�FM֚=̤��ʺ��07�VNfn��������!�Ų*8b^E)Ec�A$Њk&,Q�C�RR$�r�|,9�{���X�8�[�a,���}j>��ˏ#&,_&3$@����""6��.e�vY
�lݵ���~=5�30aPyi�
���f�<���㉚�(���e[T�_Mj�8�X;(�`��ټ�c���x�1�e�9@�\���B�k��m	ή=�\nuK�Ƙ|�NZ|��$�t��E$4a$��R�A�5=���D�9/R€�FA��
}�)��Ē<�%¶;4��]agU,+ij�O�q����,G�<����g'�oǏ�0W��6��$Or�ջBi��$�
[��׻�dE��b�A.9����$��Hy�IBi��b���z�|��;�+L%J�0e�8ei���6�䢨%QE�
��iB��_DҀGƌ�"g\��A�����QP+��D������G���,
�� �o��'c���t����<u.�(��ū�W��3�8D������d@�cN��0dm�	�����,�U�cG�7��Yy\�]W
�dH��~�Z0�!�k�Jp9�L��xra-�FʌA-կR*=�V��	O!�h�265E�>&��i���`��iCWӧĚk@�Q�)���a�Ĉ�w����P/a[~A3�P��\�R�&��&�LŰ�Wcb@�lZ��z�S
w,����aQs�^ב���R#`	�#\&=1(�T�� ��ۛ��
��]j�+J(
������d��$�s3"��D�2C��l�I%P�����6m䰻���V 3�3�{��G�2{�m��}jz6���ǔ9�z+B�P�e�1���ćUʒ����V5$��؝���BQ`�M(��"&iP�$��>��D���؏�+Z�!ǎb��π�t���F&L��SY�H��ŠT*�A��>S}g�	�%�G�yM��(��h2�� ��m	��\�Z�uH�뤖#
��Gp�
���w$�w~�L�L�(9'�l�
UcZ�SqJ�N����w��o$��bU;��,���)��_ҏu��%
iӮ�[����y)����Μr1�
Q�� ,���:�q$j��䐔�̲9��q��1%��E���T��>�kQ�k������3ɰk9�ӣ��U���殟��X��X�%�2����C�B��Mk׮�0�{٤,i^�Cbh�y�H���#���u��B��u"�֝k]��`�	T�X�C��|9#lu,;րΦ�Ֆ��
��τ��Ui��t��8�!��c��1@�DaZ@�Qc!϶��)�$h���df
j�O���{rVN�(�6j�J�1
�
��� X9 �)H�r�0MdT��b]�va�

?=oŔ�/��J,�?�Nj9�C4����p=���I���M���6���7	^[�>A���L��@qqy,wO��X�Y\5��0#�(�0>��-�##��&���F諐�%X�9R��WS�1�ao�pD�r����~>uȍ���cI$Ut�ƽ�X�B�U���j+��Q�x���=�M��9�]����6��G�jw��s���8HYy�Lp96�\�'�~x
�� !��w�Pi]��K)�-�U�0ES�Tܒ�丑���{K �V�-}�������!(�kk�*4�J��@�$j��YU�Y��P��ig#Q.hŴL��]�x&�!͚ߖAHE�Fɳ
���р$m�/�~М�ߚ_;�����>(��IQ�#����H#�͔H�*Adq��]Wf�c�4SC3M*�Yb�cv6�ASj��s�cTы�,/�LȢ��,�x�2U��w��z���J�'pj.���T�$9��1�TW���@�T����+�=kMH	ā!ne<���L���v��3� �DK�Vn%Z�Wj�|]�jW�x�	�^C�����LJ��3R�sF��ni��Z1䔤\7���|��{x�*f㼯�I��9�u��pjU�dR�C#��!k��U�9G���e���_��g�}���lhx/�1:�X��2�`��<sMd1��8�9��g����m�h�>ܸ���	�JK?������4|i�Lg�'���I��һ6;��v�5Eb���a"�?ۯ?�Z�G���y����ϕ�*�X�|Y���r�1�JLe@����2�l\Z����z���ޝB����䱸�<�5��|vN\~]�#M��Ò�A$.�\j+#1��j�5�d	#�㯃꼬���>+�;���>9�q.I�a�c�rx|ĕ�����
��A�m1�" D�)N*�,'p15����ɓʷ,yMߋ��?u]^���b��I���k4����U�����gʢ�hd��k8o�8�גO�Fz�ڂ����3oOWw8��x$���8���q�q9x<���2#�^��'�3�.(�K
�;����` �j<�;H���4=8����_�Ö�q����x'���:ΐM�G�v�Szdf�*���P�:8pO;���4��4�`��[��r�e��q�o���atQ"q�?����@V���64��V
P�A�EE�	��ǃ0=��l?h�FF�^_Ź_*�1��{��p2gl������"�	���2Ē� #�<������9�E� ����#�4EW�<�Y�>�l�y��8BWț;��yO1���3G"��xi"�C��TiM��S� (\�k���2��������}��-O7��y﹘r>y'����f�ұ�\;�42Ee%
���y�?�����%�'����7�}/�G�.W�~���|;��9|�,vbq1c=%I]X�yԉ��=+�m1B%�u�yo�y��
ʞg���+������R^>_!��Q����I`��e�,.��¢��L$1��M���wK K2��?��n~�q�C�SG���S!��ɴ2�����l�P{q�.����c<V�)R�^�O��ܖ"f�r>�w)��a��^M�B��l����B��o�/���O��������o��	�<۾/��x���c��|tx�X�G���S
�}�{�sM�$�W�g�$�h�$����&@�p�I���6KD����i#[_���W�w��z?�Fn$H��g'�&�M�"��vK�a�W5+�
Ɔ�zWY������a�(�zN����4�bHqԴjQ.y&��R
���4��$����MB��g���*h���*<�D#N�Uk�V�C]ƽ~�O1n���"R7�[S�w'��Qr���V@{K�PPlk�Z������ߵ�1-���|�O.8���ʂ�ެ�/���$���rkҚ��ɒ[M En�W�!���b̞.>u�H2�wU�H�
HZ%O���f��d�6�3`H��^ho�����H�'r����zѽ)���-�1!p���x��[�V���R��[}?N�|vnOyg��n~Zd�2���FP�א�ީ�M	�M_$ɕ���DDm�jR��	E��D���mZ������=R&RrN����j!N*�#l�*)�M��oJz�G��t�������r l��"@���FHco�h�*I���@�)�ˆr<��%�����h�ARPє��j8��"겐쁍�r\����f�c½��Y�+��_sm^��œ�ӍS	ċ(J�4ѐ�Y]����c(X��7��Ԡ@Cu�~�	��,�9�-t�HL��đ�-�����h(~$u�Y�D�mbh��c���"�بѸ�oZ5�u���֜8��Yg����1�s����H���iI�T�e[�܁_�[;l��K���,�a)1t�~Dr7k��1�H�Q�Ve
p�rхj���\Z����Q1>�3(�<e}��]�[����(��]N2�FD�H}���Y�Y>s�2q�Ĉ/&(uA4�
R%�B��@�PV��,`�c;�
��C����l�e�9�dk�-�į�T��:�]���@�E�<��|��Y�{�SƸ��#��Ed Sj��=u6"Gi%�}��5[�W����`K�Ĩd���3��RK��v�tF�x��[��7��l�#�vy����a��ESm��'Y�g�	Ӑ�~��\������+7��H
q1G�Q��TV�b)M�&A(�MM���`�Z)�*�qކ�PӥjtPAct6��<E�ɖI��Uu���X�Q����������9T�C�>^4��&0���b�FS��A�Bp4$x.��T}��y;DVr��鵌�4����FK��q�
%2��LNN<+�Y�@ T��5]���֚�<L2��LJ����Si�ED���
v�
j3�<Uc)%��%�ƳG�˒L|�X�Gk@�O@�$��Є6��x���1����:'t�e <��/Z1F
���@ֶ�b��.��χ��3~�{�0Y�WSC�WoƺX��d#.On,��`8X�\$�$ˑ8�Y����@�@�ӥv���8@�/����H
�r�~J7*�C�=ɑ$�I�#��i���M�FGh���%h�j�62c�Ə4��C���'c�v`+����ƻ��1�����^�Ff�}�_��� #�K�\��#�EvjW�}7:՚sV(�l����G��$8�����V5�ڤ������:?�C�@�'��F^O(���Ư K�m��5��n5�� �)�i�z;�}+���FSK'r@�;E
������=(u�m(H�"��$�n>\ظ<x21y<lD̾%��R�bM$pIUp�N�]2��E�n	G汎G
�D'3"�햭�
��;�6�+�]_$}�Ωq�rW�<l�f0�c���3�{���T�)RH붗w1��%	��8��Q�A���D�����"-�+�}�=53
�$s�&��w���̫���r.L]�,�q�-B�&�i�c.�y~R�R7�������D�Wcw��6/�ާ�ʟ;�A�t��nvw%Ib�H����P�Ҋn�=)�|���y+�w�����ͅ%Li�`Q�)@@
+B�����V-�$�|t�:,�94�e�B��+GZP1 ���C����J���S�	E͗���_Ȯd|^Os���U%��7Ȍ��2
���i]�^�k���	,ly�[GfC8�VT�,\0��,�g�N���d�:c���\�
PR��=�a0��s?�꘱�r���l"����vI^gX��k�-�N���m�g�p�`E.���+6L��y��η�1R#RREA���f<.Y�M�j��X��0�Cn),��u�1�oP+S�o�Һٌ��93�}Vy<�y�~G>D��~�0e͙��B�������Z�*�O�ڃX��sJ��_���8�f��Y��9Y|k$0��x��җ�‡e�p���~Zi���� 
��;s�|�7���v�ۍ����Ǒb�2I+�%L�ƴ��Һ����_עh�	�������p�<|��|pc!�cȠF)�v4�v����m�|�!��=���dq���\�,���(�cr���[�iN��њ�	
�k���	J2�Vw������)2�L�����+�V�Ԓi�������譖D
ڈ�9��*�G>q�0�6� *��BEnpiO��_�%Ÿ�H��Vd9L�A&,�Xc�by#`,W���F��򦲙9h��Ɗ�Q΋��[�r=�r$c.^<�,�^��8�3�6�C��Tk��
1J}]k�n#>`�b����6�|�l�GFU��ِ��*�W���{y��t��d2�	4�����ap���dÉ�.D�<c�f��������J��hkS�d��k�;L���w+'
Z,RF;�|�A P�J�	��ul=�!���O�e�����gM�r_~*�6B��3`�O��5;
��֬]��1cG��'�S�Ʋq񦋏äP!�D��H��R+��(~��w��|�6*:��� �XFKJ�J��F��*Ҕ#}��O<�g�n�c�`}������C�\<�<��I���	[�7r\FY��"�zPրu����-���R�d	ҫ�m��}���<�|w�~�/��ŏ����	�rx�H��r�iet0��ӳ�N�	��;��r�$�jԑ��9������G�ا��?(�f����a�x̥ʴa��ֵV���R�g-��Go�a&!�x�g�� 9|���<��	�Y!�_r��$)�S���lbq��C�[ϋ+o5�Ť1Ș��Cc̑�(���1�CqZ��᭸eZR1-Jz��erD����2ф��)i�I*�=V����
���N�v�8�:�R&�yx��[��2���"�g�{Jր����^��$Ww�e$��,���Ie�$��	$�# ֌Hs�P~��#���D���q<��pp�?���U�!7T#0�j��z�hb���Hzِ�$%�� c�!-�LR�%�%Vv�\�P�zP���&>�HS,+*Ό%=��E&��C@I'���Q1,kt�������/h���W��
N�$�gb�<�4�d��+�H�t`�E��H��zu�k6n僓VO��"C�,H��U�"�g�b*�>
4;��Z�,F�S����,�G�6����8�-)ڃ�t�
l�D�N.�n����>o%Je2���BA+�Z���H4�ia�@rq�FP��߇&(n	�����@ů��Ĩ�����z�/�H�C�1f�<MQ�2�<�;�75��FZ���%�B,�	NmF'6܆FK�+�p†��߶jT
��p`E|��Pʙ�5?%$e�����Y%R̳]U`I�YC0>�|t�pJU��T���6<$��]��VU3RF��ӭ+�﬒%��#���X��*2"��9�mN錤RX�Ր����+顒7s���#Lh1����]ȠD�eB�ڧ�k��6<o��V���f����&�.)V1�h��J�)445�
m�-��$�ۜв��9Lk�<_l���@�RTJ�1��l��s@5�Y#0ѡ�C��cx���X����<������{�(jkSJ���`�k���p�L:t
��Uh{EU��	4g��֬8�ۏ�䘲��|��9/7����O��ppar��&Df&�:�̡9a�mڴ���"
��;����I1d��<�X[��0!diHT-+l������#m5����wIp<nlҖ���,���I�&mEP븧�����e"�o�9$'O�/?&?�Y�!#��H��PĂ�aA��{��8�ߠ�m�	n:}�9��^'�uw:�c�+(B�J-E�S�A�T� rKIC�lXd�]���e[����	ZP��~Y��>j�$�T9����1ܴ	-P���]ȩ�
P9^�P�d���SdT8(�Ă�j�v;ӧκ�#Q�]gG¿�+�4��3��Q

��C�bw�6��C?��q�}���c�C�1`Y&e&2�YHPIi뾚x#��B9�����bDd�L�Y��;���O��;k ���Us��Nn?�ɚF�*E�V�b�n�DI"��RMk�;�}FX�M	�i��h�.ψ��2��$}�Ԯ#� l+�Sz�u�cm��bhaF|�rѤ�KY+�
(�|6�o�.J�I�������r��lLg��x�Pe�%�HZ�Zm���K��#!�i�N��%c����p�wZ:D�3�`P��ׯ��"��;�����<�%���Eae�D�_�$6�袀���]$��$�����0�o��41�9���*w�B��G
�߯�ס�R,�̅�@j��Nb�~C�I1Zz�G-b�!e��-]���Q�%�>���@�y�q�&'(g��u��
�H.ʴ�Cz��Y��2
�
��wA�h{���N<rƱ֬�*�J��+�Q�!o
&.|U'���7�Ȋ��I��(�(�ʒ[�m�AS�W,2\7�S�
_̫p�rqA=�����CcfR���EJ�kp$�������.���%���'�����Lĸ'��y{@�����N��o�<���dq��0�!�탍Y$�IR򀹧�
��Ʌ��wӯ�$f]�mPq<w��,'��7���������P����u���T����-|%
���y�}�`�.����
MMuj�v��z�D8��G��/h��*�eO����
�_��#�Cy��̚>�v��6n_>�^*d�Q(��OUh��-`��Mk��S�H����*m;\���g�8���H��)!R5�]@w:��pr��e1�NL��?���'%�O��L��b��Pw
�=��P�:Ǜ�0��8����f.��
������˳�H��0�Q8P��5��:�>�A�&�_K}�liοE��q�o��+$
3�P�8���ڏj�U(ƝFƝu�Q�Gj�OI�������JJx\e�l�x�d~��'z;�w
���W���p��P��'U�8NP����]�G�H�`��JՅj�;��ף�w9�Ch�Ne���S.?
q�p��^dP�c<�D�V�s#�ؑ�ϞQ�,Ơ
��<bR�iJ�f�<~+/��Ə��K�,A{B�����-���E	�j1��v۞�QP���:q9L�L�N��{h�f�Df 45��zi'�3r��.1`�8^Q1q�	N,�s�*C,�Ȑ���<ї'i��K��j���4��~V���Α�P'�cό��ne�V8͔$�7���5�a+�2��mRܟ�qܬ|vG����Zc�KV�ʹ@����Y{�Bh��+-�d2���y�9�P�*�aLh�ՒB�ʬŽ��z��W��
��D����b�."c��{��XQ�h��@U��v ҃�M.A�8�F$��.k�ߍ渎[%/㦛�>�&'U��7����!�»ңX�ܜr�ȡ<������E���|�y��x�m���1����L漃
����N���Y2w
�W=/�Z8���<W���;�_%i������.�'.����
��6�k��3f�j��0��U�����/#�r�%v�cB�}=đd�̒2�G������s�U����C��(�1�8r�r�(b�1Rcy�+jF��筟��̇�f8�N�E�䟔\.w6��!��+�sc�V���]����f�d@�SŐ� 	���ɛ����A���L�����M�Z5i��i�ةB
*�	y^B���RC<��l4�_w�$|��e=�B
/�*!��/��t���yFW��q��y��G<�e��N�Yd�L)��2��h��֚|�.�47J2 t��}U��$j�^��ɕ��g�����U�4bc�)�qh�6� �����<�9D]��r�E�����t�����#��ƖN��F�d��?K���
P��
�n�0�
��L+^>�x�a'6�EG��ɾ;Nd��EH�H�H���J��&���SPj,Ǵ�[R�2��k�;&.n&<�nU�)+��5�,�EE�ܠ�_�Lc�H��q�$bN�⦊h��2�R��XX;�J�e&��}�J�^�%6��
y�F����!�2 r-�H�6�v�]�;I�.I�c���3��9��K�&��
v��NX��C֦����bQ���v�(�4q�i�,�z�?1˲*a$�F��V�FY@����u\�H��K���M�6.U�$��d���rZ���A�b�$]�ȹ�K�Β����%����]�#��O,���D#
9�AǓ7T�F�kK2���6���44����|Q��-\Lvr�)ܔ���6�i���P��ڥR����>M�ڏ�þH_<�)~�%F4��^l;؏hq��-��nV�6F~x�ı���1�Z2;W�ڃڦ����fRb�sQ�S_�7�q�\ym��2U��%)ܕ�ʈ��r��)^����pp_��7-�'8�6l���.�B����C�
��E����c,���M��6:��x��Y�u���]�4����	Nr:�NM��D�9��P��.M��I"C���(ƒ�=k���K��ɰ�#��XQkq�C>	a�hV9��V�Gw%��ֻӮ����5Y��㢉�b�7�y<�Vg�>�*�EJ=�E���G���w�I�<���0-
��
�_��s��89b�4�%�T/��I�-`K������/���d��?#�����rcr�J�H�n;�U���kmAB
F�
b��(�T�p�.�	^0e�*�20�hY����-�|���]X�d �L���6F�?}L�њ�v���n-�V۶�WꖇD<,�ر��,؟��IUn�4-J
�� ����Rr�����12�q\��,2w�4�x�!M	ٙMA������L��j��4U�9lc'',`C#Jb�#��Ղ��
ڠ�m�����C�����h�0vz��,�\0�	�+��J+38?}H4���̆� �
?(6�\���Y��&+ZE���% ��S`$�J�i�g�d��I~f`�3��l��-˞G3��Y^�[����4w�iZl�
F=�2ݼ�����%�3m
�׎峔I��&$�]���0�.��E�� ����lıo�@� ��f���+牮��jIZ���m$1�k�,�\#G*�Lr��ZlD$5E	
7]�wں��3ӍR������f�P��xTIUe@*�ljGP=w�4���ً�^=b.�\��xpX�8̄
8Gkl�k*?�W#qNA ,꩝�����Fu��C�ֵ�n����!�TЈ�T$����-����[@�-D ���A��>�颽�\^b�S����zk������|�A����	'7Ҩ��a����,�ʽ��[RQ�iVؚ��CT�+�S35`��ŕ1◹���5�;�w�~ؑ��>���P�J��R>G����M�����0j�.��J�6ޔ�
L�!��9�5�;�l�q�.|sX�i#F��,q��d,��M�6�pD�k{��#)�����qyh`ōd�i��Fe
*2����J�MS�ˆL�B�Y���}��<O�Ǘ�8)0��#���YK::��Hܨ�ǯ�NC&B���!��?T�60G�����h��9�!�8��|DV�YwATj��=z�h��'�D��c'���L\~�(�(C�mg
h@�G�D�0
R�Hĕ�����/!�"f�edN����aԀu,�Ƕ$��V�7����#䠓+�����w���1�%�(��HO�}��Q�;��?�h�[[p>)�>J/��rX�k��`#�J{K�v�W~�����,E蕓"8�q�ȚSxXMJ��F��R���e�#r�0&VW��|�0!��<�l\�l�!0�l�|C \�#ȄJ�����0:{~�e�`z�Z��
�NK7�Y�B�ܖ��騭����K�����l��_���r��bV����U��PMb������u:�<�`�;2�qma�zY��^s��2�؎^QU�&�lw�r���(��;����q�ύ�FI���F���j3��c�	�]A�$s�V,C�ss��A�đ��e���T4C�u �����v��������vAt�)^F!���Du�Uz��&�Wji#��a�D�*����F �	�"�"�i
k���|�QO�@�H���⬂̞�r�SA`�cK��$�4�]�X�Öw�7�,��vB��l�^#�a�n�2~�	4f7m��>']�;�G�t�K�d�c,��h��û���ڲ�n���4@3� e����\S�'�l�Q�r
�ܵ���ֿ
4��BȲ����o�L	a3�NP׶�!�T�
<g����m�͏�VÐ����RiV�?r�ާma)�Es
�ϝ��nB݄H^Hc��E"�BWs�#O�B`M���_�������ˉv,�=��v �T��W��h�ǴҼ�H�lj�C�[+�l/XQD28^�A���A6|����$;?���f�>.�ݑ�Y9��!�T.f�$!�G�p+QC�k\I�"���H�o�W^Jh�y!�V3D�$Qr�*;w�jg1���0P�ʇ��1�ȇ��e�+��#�T�j)��Yw
⎜Ţjɩ��UrL�*Z^�j�8W�޾�+Bj�O(LJ��$�(�:g�5q��,�8\B�4����gr�kŸh����v7��U(��S��"�7L��MVb�L�T�65��4`H��Κ@B��,l���2b�x�ԺD�*��2V�Q��^���JF �/�-!�{$�5��P>L�$�#���~ٰ޴RO���L3��lgx��S�;V�K#��L�N��o�&���r��ҵ��]zQ�HO�g0�l5�t̅cGIE���1���mPN���m�$��!,��P��Ƈ
)��fA{c����v��w�]?��d��q��8�Hc�)��3 =�R��(]��kTd@L�ʉ$:fLc���Q�Xq���6���j������O���BY��T̃�fI��vˍ��:��Ŏ�[�o��'bv�k��x�{��*B�&4�G0� �##Kѻ2��� ���~��1$�t�n�p301c��d���Q�qkGRO��z�u��95	�]%��g�>VO��P�e$&�lI5�)O�!'��Ux��nG3���&S�/�bA�c4d&��-�MD�H��uA�P=��܋J���Nc�n�a!ZV�(�ԓ�T��_�+�P�!@�
��#/,�,RG!�Б/���#j��{����`ዿ��C�1q|����aaI�.VOq��Ɛ��CN���)х��EA���rH�t�?�6PɔH
�^�<,\�HaX�ؽ�H��;H�����M��?o�v�}�y��%��;d��l��#ˑ&9�)l@�Q@47<ݩ�L �I~7"x~�r̗݅'#��K����~��j�#`N�c�2c_�$ے0�<H1��Zr�YQdz)f*V��EN��";c���>��Wr</���E�$1��Q��@%+!F���@��~;WM.�)����׀�r�_�,�1�\l����!-��$x\|E���9gc�A��g���/Q��D�p
5+C��x�b™Ⓦ G4��%�-d�����>�i�n��x�C�Reɸ�Zo���-��bA$f@C�� �����[>�6�@L�UD�mT�yD�L
H�A�ְl�"��l�Axb�9
�&�lX0�Iǎy!�%��H�ᑨ
�@#p})��b{l�(Jnk���$��cıLl�$k�Z5�mbM)QO�i���J�[��A4�&Fa�}%���lbXoi=6�����_��:���1Vb��Ʃ
e˄�*C%]$a�id��F��٪��w��֡M$z��j4�KQ�.����!f�h�9]���Q�Wfھ���/�c@��Ǖ.A|y���"��+K*��Q]vrMA��l.a�����-�E��E�M6�A���Y㌃�S��`���3_�U7
�$�W���?�]W�К$3r����?�df�1E&���T�@.Ѳ�h\P)�WQ����>�M���9��<���ea-"u��,�5���f`�M�5�׫��{d��ܒO$f\��M}�o&F\���Q�.��YK7n��{kA�w։B����L��d�RLQŕZVi�x�� �]�=6<��C�](W��������6C�HZ�iє%6��3�3	<y%0�ʸ�:���A�'�"��Y�E+���R�=��lS%��]1fD���`�B�<� `Ό�¢=›@$�8��g)I!&L�����D#�`ۨ'�zSo����OJ�Z#:��S��J���[b��A5(^���'`t��̏j�v��Zb��ѮK&t-|��匊)��e���,[uT�'�Ie�Fe�cK��]����dn?m�RJ���
AО8�Rȉ��?�$��e�
G�lQAf��՘X ���
vԥ��SEX�z��~[&h9��s��*���U�~�]�Qj:�>��Q��h�N'�x%�.Q����Q�/E��@��V�[��޴⫏$�&��e��}��뼟�qxX��d��G!��I/�
���8c&C������"

���>w'�O30�/!nI2��W��� )�iר��MA����/�]?t��E	��+�g�2�`���U�ĩ��&�\}�?�K�q��U���?#�,���^Y���ą$7���ڻSM�p��8���O"�>�5�����^J�"K4&I�~�#�����a�Y��@T�$�«D���`+�U�7!�ΌT��eǒ�*�zn��QA�u_�[��N����u�"f����0g�1{��(e��ƾ孪v�kL��r`J�`b*��>/5?#9��0��R�1�⣨�z�;H�ӏ7O�<X�⹯#���S�����i��h�RH'��cy��yda,�^�EP�TH,}(�{�:,�WD����6W/�	�NB8�rae���� �OC�WR�wkN�ရڨ^'g�/�ϕ�
Ŋ� CT�#Կ���k��8���(,�$h4*�ߌ���h�s,��WD� 
�>�GO�����A1�P�2�j���w���-6�.Sqa)�uڧ�4�.\�(��$��9��d��6rm�ȥVC(6Q�Hz0�Q鬢@{d׻qU`�t�����$K���/a�B���0ua(ʌ)�!U��g�
߹#�AH�VVm����F��h�(a�i*$8�1Ta��-�^&*=��Ў�W}��Rw���[8,�L���/p+�^���zSQ1���ܚh��a�V>#$'����"�2�P�ޠMS6��qt�����'���/Ġ�s2��pC:�|�pW�H�>\yNڢbHuNNiq0�2�q��4�#U`�-�-҃ҧ�M&y0�H䨓��O#d$��2Kr���y5Q�>_����Z֢c�He���Oɏ8�y�ј�7uu�@��At��>�j��s������Dq\^N_*ϓ�,&L^&%�L���JC���گA�36ye����^#ʼo���������EUD�a��e�߲��bHJ�2PhJC4���4�u~��Xd��	�}�-��$n�r��Đ�h2#^ڊ��Z���&x�u�@�z/
��q������G*�1�b�1Ь��.��F3B{[ݬ�=��Ghz�1o��m�^+�W�98�S��6N\r��}�Ң���:����<q䌢5���-���@񷓈�Àd�a�+W�b�y� �l��%&�r��%��ز;	����?os���߼y�L��0)G[���K͉}�"ő㼇k���2�3
���֒�!��5��1��������<�n;� e�ɗ6>R"2�c�s�$-7��w�5>��m�7
�.m˯G��x�>\�U3�����<��6TW����NߡJ�v&�כ�,����Z!@�궡��/ a���e�n^$�GƪJز�5���cM��:^Ӻ9Hpkxy�ˊ8���3���y���~g7!N3G�g�h�ƴ�����Ҁk{���Y�5�f�{��;�^$�!f��Y�!H�C2��\@��j�<cg�J#"M�g��/��2�+�lj�l|<��3.�J},	t���T�#�5N�s�xŸ�&	u��Ü�p�y�^<�u<��b��úXm3��IW�����Z���q�V��M��2,������g�p�pq���/�`M>'!���2���_�Z��,ʭ��g\`�I��D�F<u?�Uቹ�������s_K�����eI���ʱ��ssb�]q\T.!�X�QL�,�
��=�p��/.B�?��i"F�g���}��<��O�\�'�&<Y�Jc��g�ˍ3r�W'��,0‘�/cu��>�n�Ir����y/!�yn=N'���R戚EAr�&�}�z�XLdE.�%kd̞C�<g�Bg#���NRG$�Fj�p�S�M�w9!��.���U顒]�.=a���Dh�"�<LT$�*�wPu:ۅ�^�@�&y����b+�����I�V;�-�ݷJ7硟�����,2��)�|�[K��o6:Lg�f�{N�},����U�q�6���M���9s�:�L2�Z���|����<�S�q6)�Z��{6���5��(a" 8�X���[����䍛.Q"0�T�Z�@�N����F�)#W���<�6�W�lj)|�2
�)1H����(�ׯY�u����Ң�	��rF̊���V5p����u������"
T]�f@*�l��u�W�F�6&潙^��j*�t��`��1��OA�4�b���|����}c}�][�r�wl�[�n��JS��I�O&��Z�~���؃[�W��]�ѕ�&�Z���ܟN�^��	{/+sQ/��'|2�lsx1���Z��ZQW�S}$�<f}T���Y@���*Ii��o���1�]!�dDdSe"�.qe�8&�m�FQp&2T��@'��q�ت��d��㸹���1�^�F#
%HV5Z(��7&��S���1��'5MdC wp!fr?Cܪ��5jhh[z�rv�3��RɊ��C.�G#5Пݵ�+�W��q�d*
�	�.F����\̓4�T#��k��m�l�ŒQ�/Jo�;PkxL�^�����4
�u9(�2���'�ڈY��ҡMH�i�SV��&�#�p�n�r%�*}�դUx�Pi�VJN�:��yL������ҕ��lj�!%�!�ʨ#~�eQ�@�ul���ς�A����b�����Kƍ��I�ۨ�@+M��M�Q=�2�aqT�/��yW�7�CTr;�3�4Rƌ�p*o���kt���y�B/D�y�#�F��gp<���� T���
j���qEL`��TNY�Wdc�c��R���5k��~Zˏ)���Z2A�ϭ����|zl�ț����Dl�[�[�� �N�1��5.�SYD����7ד&��N �N����c�2Nܹ��Kp�R�v&������A*�Y%-4u�6J�M�'� �3��A����N������O�;�6�Z:n['aȓ���F���s����ZAo�:8����!˫�n1��k;�N�;j>6�R�ԏ�T3��b��V�ǟ����nܐL��+r�H���Zq����J�Y\���ä��r�Y#ls#2�[�b.+��C.*��O�!�K�1�1A$���ǚ��i	� �7�'���b<w4_
<8g_�0<����G=.���]g�"���b.���D�4�X"�U�	!�PXȸ��C��Q.�+;'+3���㇊Tm�u�my()�n�ja�'�NT�E���ܤ�G*�E8���=���.h��lR��e��Vw���##�٥���5�T��i]7� mR8����/����	#��f8C( У�s�r��@?
VX�
z�z�8<�l^RI�<(�(� biZ�4&��H�:1��ޅ��Д	�(��͉6��ɑ��9/kV2�����z�<��eq���� ��q�X�k�c�<��c���26
BI���K�;j��@An4��L䭖_1��6L�'%���V�F�0���Z�4���^V~֦[AА�e�Z]�b�&<\�B��$l��O �Z������F�oBt��n���WJ@�׏��E���G�ŻK��%���R�ւ��j���߮��N��Sԥq���`���qr�p�
��ܢ�@-Z6�k�Mm�	����@�)T/�0��.b�%�H&u=�bY���Z
�ji��� ���#�	#�[���m�4��#Ʉ+I��b�!�@0'pXn	�\��@y����c��z2�~�I��#VV\��P��=�uju�kD1�P���Gy��%�ƒ/��?��a##���7��,��
:|���bm���l�	K;����=�J�X��r��T���X���V�#)?V��+dFɑ3�F�Mm�$AJ�M�
�_�ա�o��B�?�Ҙ|lYY->,���0ƛ���"F�sp,��V��Y�D���G�dµ�#��y�/!���%��ӆd�h�H�DJ��D��u%C+Q�����J ֔�+G&LDg�T�n%��8�G������w�G/uBц��v
w}6�ض������KK�$<65!��ibxe=�c+;�jԊі�zk0�(c�!��S��*�
�_�.Dő"f��+d�#I��.�JT*��
P�S�/���d�&0<B>=S��8�Ƈ��`����b{UG���N�;c����f�#��;�1Y��Ʊ����X�zz�|)�ќ�ru��ڟ��q;�ec�}��t���ѭjY����j�P�=*�<���0�v,��y�I��(�c��!�X��"�a	�ڀ�4�)Xጋ���Ҝ�!�)8�8��v̊w�yb�r�Y�d�`�Ȅe���6u�� s:�2�ѹ/Q�>
�䜮C<�ѤR��X�u�oW&����WfX��E�0�ŃȰ�����Ye6��O�6��~����
�����/��''lu�9��Rx�<�#ub�P����嶎l��ڞ�EЅD���,��DW�Vƌ�)��М�"��
qUe��T
,v���BNcE��K�o�NW�!�/�o��R�9[����U;��&sp����r�;Iȭ�.\|>5�	�C��Ȩ�2	�����B����Z��/`6{]�{���⸾O��ء���f�<y۬�\�nU�SsMv8�͈�!��Y�0�7�`pr�p�Qɑ&f2���2���@b=+���ڑ�j�R��ԣqK��KpVe���@r�
�t�}�Ljck}�$��P��C� ��"F1�r�P33��5��$�LG�D�gAD��J��Q�â�@W��VF,۫TR��?0�$E� ���W�,I�������eX?}&�̵ȴ��*Y�0@
?�����v�t�,�;�Z/'�X��I�8;2$��Qr��b!�����)S�����{��Ŷ�XQ��k�?����6G��$yG�I�em�����$z�����o�`��+6<���R�\�X�>�r;��frXJ���M�p�N�N-�i��9j���γ�>
)��\�"IYc(�I)
�B5?�oRz�Y�����m��FA�����q�r���/���θ��n�0�w �׮��_A�F$�s�?kB@4e����9������sGI,BY��@Ӧ��}#�@�������ɂ��7ƖekT�Kj�l�n����2Ƕ@���&�U�9�n1��d$V�dZ{���>Z�/i��]��tţy�T�k#��Rt.�S�:6%�.j�i�񓵎dp�)%Or��O�޿
��9e�̫��^�y��+,E�-R(F�E)��䔢B���Jb�9�Ĵt3����{V�R�v� �>c0"+��j��'1���J����F� zn6�u�:,�Z�Sa�G3��ޣ���)M�֞�E�z�ǒ����le�ed�q�XY���� ��
\v��OzZ\%�I1o+]�$�$�6�Y(M���J191~��ز�E��&Y%B/91����s4W���<O����K;3<cM�h"e��uy��T4���-T�Pz+C��`�$����E&@�%�BD�W ��=�RN��җ^�#FTɇ��,`�=��nO�T��|�u<���y��<b�s�8d㗒�z�Ʒ���Xn-0؊�f�H���m8���n"M���<���9���v�µ%UV��lN�u���GN�b&������VlU%t���X0��{��MT�_$@%���H��\Uhqi�&B`c�<�V`d`^8֋W�
��W������%N[�s�,��:�������D�(�K֠�C�E�=t��)�����?��b��t��a�ΐ@`��W
Ͱ'���߃$Kt`x�g��@W�A�I�ظ�ܞD`I�(��,���3"�\i_M��c-S�6��p$�d��+�T(� !@&�l�=�ZκÒ@.K��c��?�pl��/h=�`�CD&��������#��O'nrH�/�X�!�a�Ra��+�dt���}���Pi�k������O��]��~���8��ӎ�O�U�����S����DT��>�I��o��!@(,���k�Y�C�0��v:BV\0#��+^EE���~Z�	��M�r����3��~�L�V��$Ы�*�E>4��ZJ�}R�1��J�H$<��Ҥ�b%���4�A��1�>�5�HF�Nr�L��"F6�j8E�*��[řH�acx��7�<A�Ȋ��v�'�w@
��M�h=I�_�b>��s��T��ưN��NJ�,���1�2����Q�&��8�r�l�6�b_z�Ѻ1e�Ԁwң���{�U��H�h��P&T�AO���b1���缴�U �Z3��Wc���|w�L<�^BLȋ�G�R�F$�j]<I �!q�,�3��2��#�1�/~3�$*ְZ��Ƌ���@��ʳ�&"�2�a�6I����	-��XJ�p:T�o]_ãu�9�k]S+���fNlp���U�vR\��(}6�1���}WRF�k6v6? UG��9�'W� 0`c��n}5H��6�~�H`L�,����\z�Ɏ�b�ơ����՚�i�Q�3���d�Mx�" �Ei��yo���q�˂r����0e� ό�㖎��̤�0;�w�n�"����]�%^�}�4�VF�f��̘�%;"fr�9&��"���mμ���H
�M�����?D��a�x��U3��
��{�X��+Jn�֓-��C���R�^�(��;�H��6�0��-q��@]�ԒM+�=�)���EӉfC㸜���:����$�&R��QEkk����C�p+��말��P�&>?'v�q&��ѰQ!Z�_�)��&"B��HIp���'��y��1�	�6fxP��PU�N�C$��l/��y������A6��YV9-�@�jlHڢ��֙ď_B��W/��nHsrH�i��(��0T�{Z�jW���-�4��2�%���ɐ;cr&�$�"�_ܨ��=��|M5���.�KDjܖ$Q��E�	�Y�GF��Q�T[\ҵ�#��=�Nk�N/�$$�,�!�fq~%��sr˕��q�C�������c�;�nfU$�+�l˱��
�! �7n
7�;�pSf�n4f�Ud�bW�H���А.��]Xa��P$�K��Yc"H���s��׵��ެ�!b�nuE��z�^��3�����_x1�8�W��^y�Q���̇,Î�#���h�7(�@~��,���J~��5-����ʌ̉VE�Gx����?i�:V�Ǯ�ԙ��tI�4@櫝��`c�d���Ye� K^5W�����u����a��
8+�J�I�X��9D���1�����m%wV*w�]�����q�T�#�/+����&:.<��̝�{RC�T�j�+��B~5�@�$�-���Ş6x�<{dM������
��v��9ڶ�����;���S"�{'xHa<���$�>D����u�F�f�����_Z��j�'��e�d_(�����q!�I�2�hC��; {�M��U�B+��[��L�觊f1vX���		��V����7x�b0ۛ��$zt�\C�V�L���Li�3��;�d1�&<!���
E
�X��K֕�$%�����1b<y*���<K��ɒLI[�|�;K[&A&����tWW�c 
�_ҙ��U����Kr��l�$�Ir`�D���u�e[GN��>�@B1��O,���$���I���gˑ�/�"
�`�)�Ԍ1tE5)H�@ƺ"�j�w���x�1�:Α�R�۶��
�&����'�K�we���mM���̣H'��e�v��-@������Z�1	�����f&4�гq233�	���-���{��ªԐҟJ�r��DDS��$��\(�h�Y`������۩�]�+��ś`"D�z��$�dh��7*l�+ų��tT�:�R�U�Fa���k�V�q�d�-�NJ��"$�[�,�"�/�1e�e\�K�
�Xdfg���٭d�����$��󥑌}���R��9y��QZ"���h7���,*i�C�,Kf�-S��v۝�:��0lƏ���Z�� "6V`��Zһ�tk8��non=R|�"�S�"M�!���(+#{P���ঔ�6�Y��^Bw��*�ۘ2OŸX�8�ǡN2iLq�D����ڠJ��Z;?bv�_�rC�ɸ�ץ�p������)_�Qzֵ��?�_Eۈ�R�E��'pz*�
$����Sh����^I\��T
�@i�,������8f�����8��䂩œ��1a�i�%
R����c�b6�?*Ӏ%ƪj2�$����8"4�o��҇҆�-� ߛ����V+g��$(���B9V�*n�-���� ċTh�_Ȧ����,���.�=J#�E�RB���I#8���\�"��������g�9L��1u7Lu
.�4�i�'��G��5ߕ�&v4k�8_���y�hf�.�
����R����bl����1I0`Y%���K�����64,�#F�]{!���G4a2�b�Ò�% *��1b"��}ɕ+d0Y�MU�P
��㾟8	V�VS��R�3��&<~6)��c�o ����7�Ss����<�GҾjY2�_��Fv66bp��K�i@k+BAk�,T�v�֧Qɚ1;v��R8��#���v� �p�H���$1Q��:�""�j�.��S�����ȧ�O�9��e�Nc2�;8��h���1�4�=Mu�'�"�:  `N��ɒ���<��A�f���}jiZ_��,��*��
EG�f��^z"�ƍ���v�v���:�C�?.0g���V�N�cê�J�ұ[W|�bc�	$_�
.��W�^�U-�X�G�υ�x�#n�xd�%FW�<
�r�VV�Z��]y���@DjU��2��#�1�����&g,e"�Ibn�P?����@[�&@	����rp@g2���H�v��#&�I\��E@�zhS}M���d�VTsN#�	����ʩS[���c�P�~;SY��Q��Z1��W"�H�H�1�I�y���Dd5��nA�	0��$�6fZ|$9#7��� �*�r��\�Ax����֌ѡ�)���3H���n�� #Em���S�6;O��2i�ӎ�d]Yf�I���!f��J���$e��5���rQԮg.��af䙱������̈M�~��}�b��A��
<�g"�YY%~�w%�ʨ�u�j@�l�ZD�*{\�*��ٱř��
�{j䪚H�E ��ʣBQ��[��21�p�\��2�!�"S!���-GJБҵ��Bt&�(�"�01���N2@x�#���xc�S-Z�ln:��$�$����M�OH�%c�K��4R�;W}��2�V�r�J��p|��N6{�"Ll���@T�	ah+�JM�L�J2�#^���1q��LT��P‘�cF��!���M	54�u# )9�SY-)���0q�$*�n��
5Vj��n�j�u��U�7!ʞ�*�6w6LI#�9b`��B�S�R�M:u�j�� v�V�0��$H�*�-D%v5߭7�jg��n�%�,�b3��b�-R�ޔ��0�ӒyM7'Ir1�h�D��р�P;��U7;�>cTˆF�i�:%�ʼn�,���?|uuI&V2f;EU?�Me���ɀ�,�]8LJ��<����B�4��+O�(>U�8v�F��Z 2=+E�
5ٜ�L뗍�4�a��H���r��OQ���uAᕆQFȸ8yO�<++�C$�W$����BI�� ��3�&��S�D�{�1�~6<�ɸ/�D��3!��y&7���XD��C4�#�F��uL�7J>7���El9�FD\0�����x>5��1�h
�Ilij���{jE-]ƒq8��t��)|�[���Dx䘄��3�fB(�zR���R�iV%�1Ǯ�y��r��XX�t�Ƞ
Po�����M�S�$[�S�FR�'2q���0��@�r!�0����䤢�J���'W�����S�N���9�'������쩸)X
�Jj�fQ�P�H0v���e�q>S��ʳlH�T���P�F�+Q��#~��X/c�r���N;ͅ�2c��Du��r-�P�mkz�ק�,\E���gDˇ�Rf�f�bWbd�-�jh�4ل2�!'nk����?_9"�3<`�"�d��ڊh�|5��`�词ݕ ���ʄb[K$�/mʡ(��H��U�M�Zm�0
�q�]��1>vFt�p?���9X����Y�B��9��A��q�5&�7�˟�d�#�$��)������x�/�S�: �6G��s��c�E� "���O�4i���:z!Gi�ە-���Q#�c W�AW��
ӧ�V��棱���v/d�t���ߣ�(�Z҇�K�$h�Wy�=ɺr�1��2rSkX?4�s ViE���%?H4=7�,�A���Sx<�/ �@��df[�KAR���������mRGs��&��TeE3����j�S{�PO�jd�R�C[)�I�̨�d�s4捃!B�Q������1��w�_�g��23����d�2ػ�MM@]���/�@��F2.�%9��/�e�$+R��6�%k�7-���mK.h̽|q�Cq�w����|2�<l��cT��]�A�z�A�R�

� 8�7&V&&\L�����)j���1K��d03�"H(y�E���+,Y$���aE�2��a^���|��ӯ_%H��*�lq0.aQ	Ytr
�G��ZS�w�aް��9��~w���x�)R|�UUW�JI$e�!5
_[z�V�u&��b�	ת<���D�"�'��VF*�Df����n-]��gY�dc��_��t�	#�M0D
�hz�7��m���㢱�>ۺw�~%q�~���H�^��eG�*kO���Ȝlw8���4ZN�9
&fR��=��S	^8�*{����j��F��vּ��r�a��4�%��5@Tы(?��RM>K3���:���O'��#`�h�4iC��@ZF��o�9C��HÒ���YX�v��Ϝ�eP�i3"�U�v�?A���ۢ�.ND��F�)`�)�C��ثj�,����CZ�wɬI��Q��,��d��a�|S�Nq��t�5�еڽk�]d��R�h�����d�q�V+<W%�@R�,�
�R�T�����j��X>O����b�@�nL�,����(E��s)*�j�\կA�w���[AW�x�>6�q8�;��A�^P��'nڂ@$�?���X~�����~7�\i��k��h�44Y(���c]�$֛k~/dh9~�i�j�_/���ZҞa�|����Q���mj���-}��N마�d�/%���I��	�{�c�V�9h<FM�km����s��GHqjR�)�G�ئv���!"�乊�j*Oǩ��jC$��z'�C�>@*�g"�Q=�����އ��� 1RU|�O#v�5�"'.����GQ��+J�:��E��A�x�J�n5Ҥ�W�������9��.��ȇ'���(���P�(����]e�%��S�	�\$�����{K�#��^
�ފ�4��d �p��PUq����C�,�41�%{R")�J�֤�鶺oI�I�L*Y�����%�W���L^��X���,��]�^��9�
���r�'�G�����n�"��U�!���Tz�SLs@~�LM�'�>VN$PQ�́�R�ѳ]&���Pv鶯�.�MRNBB	�#і�����;h2Q�;W�uf��,/wV8���2�9�D�dB�Q�4�9)��T
��K)N�p*��͂;r��I��+�a�b[rIۃ_��FzU9�^���K7#hѸf���$V��=	�k��0*��$�f���0W�cj���H��Y�c#J*l`��XMY.��EQ�щ+���yz��Ձn���rxCH,$5=w�]��
Q�u2�Ed�]*��J��
��	`>L�K�(�y�dO�2O�۔�Δ���`>>��1v������&3�4�"�%;�E�	7
wQ[J�#�m4b]��YYZv^]rBJ�d��1�YK @�o�[��z�
X�#~4@rL�����Ǎ�H�1����$����R��I��4@"��n+.	���bG*�)iv�{u���T�U�"j�>4~H!FE�gɂ<)�������QZ�4����k�$7t�%�p<anFb�O��c��bf`�(���MP	��S�i,�ƺ�����K>&nH����Y!��79�;@�2
*w�Z���q���j��m5Z<T2ɞ��ԓ�kv�iv�N�COD��ꋌ��<�������P��(TU]�(
ֿ^�I)=�iȏ�I~��T$��@a� �]�;hBt\M�fb�O�ic��e�;��ap��w�rb�/j���ؼt�����:���E��Z�`I�z�O�uB�\����k�rdBdPX15�t�MF8��Jm� �,�~O��N
�ƪ��(fVd9ILMߤ�/�Nq��{m��Y�!����|\��G�c�AA�wDw�vd�����ВiM������WGP*AJr<X8�������bC(WW�N�ޛ���XN��U=����ȃ�ζ)Y/2�k����Q�A"��~Zl�eVi%0����š�8H|ךHW�9~��م��I���"�Q�\t[YG�b�w'PÀJ�T���_�ۛ1�,a�>��9���1�"�)�wK��@b�v ��A�ɒq4oT����g��yX������뒅mP�Aq
_^�]�C$I��FQ#j��.d���q�1�H���q��,R;�t:lY#7C⁁�q�W#/�
b�&�H�!$h�(J�Z�ڄ�]��Zc���_�!�{%��l��e��N3Ĺ2۴Ld�{��+ҽ�Z�u���W�93�^6�(���On�]���5n�Z�
�ml�(G%שx<�#>\E4yR��DN�x*T�c5���#���p�\�,�H4H�X��n&2V9Ē&@ ;�@�m��
g";�}�,�2�)�Vј�L���ʱ�.�n��o�X�7R���A�N!S	��#�L|��B�Y�����P��=uX�7qD>'P2&��Xy"�F�ʕp{���v�|�,˶-l^z�Y�rB��U@7�@�S���\br�?�3���Q�i$l��@ƽ��O��a�"7w�l��.|��8�i��9�26��֎N��zP�(}7��HR+��e�5��� .B�Igګӡ;k�	��5�1�#�ȟ�
�<�����J��Xb���OZ׬�x�9�~�c��-C�')Ð,m"3tq`A���65�I�ɔ�9��b&!���%�1������,Q�s~/���2��W+-c\i��LVH�Gn��^f\���v����^�]�x�k�3�ֳr�3r�a����&Y�P%XD��;�>�}7��p�}�&��@e�'���`> �e�9$��KJl��OM�@�M�ЖXF�}rw{/=*q��r�e��CQk:��b��V�t>��q��4Я����d!�>���ʼn�*dr%�B�Γl�c4I#��+ԇ�4w���x׋�����_���%M��i1��m���ė�e*r|o���,��~7�6>X,�C!"��H�ۃ�i�T/ȯ�"x�&U�.&L2����,.�+��u �A!���Ux�b���,ܩ�����%l~Ge��Y��Ք��IkB��X�^>~l��c���ظfqy_�AN6x�� X0E�$�K S}���/�/��� �$�.s�p��C�ȕǏf��d�nTH#�1��nW�Ҡ�$��> ~���k���(���G���H�9"�9���M
�v�0��7qc�4"۫�=�.ݧ��r�*y;1'�R^i��O���'#(�آ�+ĸ�Y����G��uZ�"��jju��G�*x�?����K7�#��͗����>?�8Ts�/�~s�N� ��
W�fۡ�����Q=�/꾉��ɾ��sy�{��6,�fM��d���s��f��t�$��S�y2o7��Oڰ���Y_u����/��{�x��&7�x�/��30��r�kCn#���β/�Z������>���� M��1?��|�c�����o*ce����[�Љ�y08�f�aQi(D4�d�}h�f�b9�&���� ��_��O�8�F(�<Vo��K-c����|�c%#�a�����uF���6�;�����}H��40���G��"���`��a�LXx�S�
��P"�_����"7IN@�lK�|�>7��m$�1�2����,�"���4��U�IHm7R1�/?�I���ϟ��.4,\n>di.R}.L��������!�=u!���>ҽ��H`��㱦u� Ǎ41�d5eL�Ӱ���z�u�v1�O��ᗰ<�D\��� ��7c��U�*

��ӞS"��(��)�S+9����� v�O{=ĩD�:�<K0%�|r����l�6|۱'�K\x�x����Q�'�㤗pd�j�cp������ʗ"C3��d��X�{�O���5�%�E�&!�etU2,�!��x�\��a�-��jѝRH~?�l�aNGْ��"f����i1��G�2�tɄ�9#xU��AB�,c�_k�A�iLØ��7Hw����im~����ߏ��u�v���ս9�uv���s#&.;!��M)��K�VT�z����U���a���:���h��h�Ib~
��V��W)dLU��+,ccN;�+�z��[(?��i��ޡ��K���c�$��.�f�V�M�ZM���%Y'���Ы���Y��PH��P��@�>q?s3�!���i�"X����pсP}�tZ�O�
.b[�v03eJq#9��KՄ����`65��Z�=�'v���h�M
K	��$��Ga�,T�
�3��%P�*޴o�Ţ�5�MuF�� �w{0eF���E�
��M�Ж~��P�xز�X���T��EqI��]�R�V��:͗��-G[?%He"��܌SO���ot��FP'�ץ�P��~z�����	9I���9�<6C*����i�ȠP
��	4���f�����B.7)�G�,W�cƙ�,B=�:�I��"�Tﶽ?��_�Dc{���*��2'/��=�� ���5u�T���?�$�5P<�K�nj�##���$q#Az�;��H�l+p����o��D�=�b%���6vUȅ2�^�͞H�`�
A>�A��c�C�n{�_�O�ie���%��c!���L�q��
Y���w�u�$
�<�Cku��@��β���PJ����V�҄k&`�u�%žo�����@G,���T��Z��;��������b#�>
M3���^��h/�\Z.�m�M�q�/P,ƚ���W����h����N�a���@��kf)��_�
2�/rRr����#�z�G�{�PЂ@��։��)A�!d����!���,RM[)6
р=
G^���L���Փ�E���,��f�\��02��5��E�r�1v���R�@��;'�ܷ/��Ji'�ye���'/�a(ѵ�eЩ6�77�R���Gj�0�Y'�$��*�'�c��	!ɨ�T�z�b�t�k�<���G=���+��)�'�o�I �h�)f��sA��Z�B� �g�x�YJ��uF�.(�շ��Q��$�:�0�!���'��kS�VV����u(ڛ�C��S����")����fxE��dc +�����lMI4S��u��$����U$�~E����E�戤��ɍ̀�+'SR��ZR2g:)D cfDq	ʓ�"�n�U"���m7?���Gmhy*�ֈq;rc�Ba�,�ˬ`hbH��e30@�� �r8��Yy����f8ő���P{�@�z
.Q��jZ�01aZ��d�Åo�c���B�J�2�r��O)�n�:��T�#�r�E
X]�V��%���WrV�A���4��x���͙��!Y��ԳV8�AБ���i�M����Vi���J����W0%JƯ8������e(֎y}DO4;+��s�(�x���	���Z�}�P�����<j�B�]1� ���dJe.�FR��%�:�g��=�'q.N�Ie��8�Sg>T�6.>X/	��^Fn�2�@߫����>�?����U�ޓ��d�$u�9%SN�R�Z��ց��^��IJ[��9xa� ���T��P��XzP��2�E���w�T"@T���$\���Xx�cRq���	���$nN�j1'ypCX�T�ms�hsq�L�cf�.9fQZ�;�>����
l�9����EF`��y��x礪�S-�C(xXn�)�������|�k� ����]9���I2[�M��ϟ��HqP�c��m6�"�	���N���@X�H���(.�Œ�� f�o��rD��(����(-]��I駝�������_i��<��᳷�.�Di[p!V��"��
S?Ǎ��^j�s��K��p�JS��If9���
���D��Up(@"�)�xĉ��v?���������x�1�jd<��!�0��e=�lj��E��j�b,ڟ-ժI�7 �/��8Z��(I���kB��t:�d�A��_ҵ9q�\���I{(c1-H؁Q@iMi'sŘz��WY��9�\�+�"ւ�x����e���?�Qˀ�	�	�B�x���|z,��&�B�$�Io��򣲖 HZ�Aoδ��������rb�K�as3���$�".7-Ly3�a����JH�jБQ]v)	�c5i[�rK:E`�n�π&�35o(�s+
�P�6�d�c"��F ��X�s9YRI�������3�s ĩV�uPl�����a@5�;�%���v�>���0�#6|%��'(�c^�/
?V���}z�	&Q`⼟�Y{�P������y(�&�ň��x�U��+���s�k��9L`4e�`D�u��I��I�0�Z)�hê*H�� '���z����h>�͛��c�:>f4�x����̷�2V��n�a�
�5<�e����גxLH��䆼V7�|��&��c�CU\0j��M�-Dc� &"=��y�g��v.�nt�E����pC�bQ�E�1�S��nWS�,�gl��^0n�u��I!,󈮱�h	jn"�S}f�61���(A�iK�ɞ=sau�K�0ߴ��(kQ�
`+����[dl^�h��
P�����K�;21��Z$���O��jņ�5�凜��]�F~.���u������4��S��L���[��B�x�)|M5�v�k�%�y�ZY\i�e����̇�ď?��L�,�`ő�ؑ�]�R�r ׮���,@��cu�|�=�!s�R�HZ�껑M�+����2A,B!�6VD���
;�h;�Be`U�n��}y9"wR�k��t�Y8�����;e��/�В-�����4� 1	�F�y�	��pTUm��T�>?������M���r�����������
�&r�q�x䂷�FހV��Z�L;��J���9���H0�T~�n4��4
��i�ו�.�H�Ǣ�84G4��r9���ƨ&y��Qp}�à?Mi��D[��y`�ÚnS�e���,tb�}ԫ���Ռ��e(H��B�Vd@G�)Z԰��)��W���H`�U��p���~�rFU.B��Pn�=5� D^�Q/"̳�H�r'bИ�TP���v܏J������UNLl�u�GD22��,�m��불�CiE�%��Ċq	�&T&QEeuRmf_Z�º�9n:Y�
�9YO0�k ~�(�4�
���V�s�a�����<��\�EY
�ð��W�2�j�
�u�/���W�=Q#���ř�lV��`���G�,�,�m*�c�s S�ZQ�ŬV0ofZJ5)�t�)^��Q�8�b@q�܎Y�v�X�y�P�Aۉ�|���c�#I$IM�
e8Rr<6dn(G�F��D6�N��h)O�@�#�H��_��h0�)�U�++F�e���@{��`)AON��i�FU�$�Pˌ�_�?��9��<�
2I_F�0ZЍ��u����`3�$I&x�E��{)8j4��B�vP
:z�—����fX�) �2x�Kp0KE�3#� �*(��Bzd�L�?�?S��/һ�Aj��Maa&I���9f�TTV�`�#�VըP?=i�!|CJ�j�K!�	q�Nyv
G4R���Z��$
&��@kAZ����tR�#��f�E�&)�r{M���qfV@��F�%�GZt�Y�4Z$5�%�6㹇����,x��f��,K�z���0�E�jqؑ����p���YwG-����ҕ�֜ckT��(L��t�3r�d�d5�-c�	���b���?�[1��&��R �X���Ll��Q	��F
����w�$����D����8��
<��y ��`uX�`ꒂ�pd���@��J`fkC�p;E-�%�,��.<x�0j;��vUX�ն5ߦ����?�ZV~~||g$���K.S>b�΁���41���(;����'�Ə׏��1�t�\�!H�wa*ȩ+u�Q�UE)����3�"��q�����#>�ls.l^�#�є��n����K�"�uU�Op�r�	�Er�4y�@����H$V��u��=���b,?I�'Eαs����{�K��osZ�ʚ���֪���13�:��V<��r-`I
pUJoQ�ꑀ�XSS�)��O1��r�yX�D�ˎr��N�����zm��R�M�C�$�b���b�4Y(��o�:\�-z�k�5륞]�(��r�nfN.\pG��E���P�Qݻ�Q�_��4�������
I�&k�gD��+���W-*�u��BO -�����dE�$8F)dZI�vvESw��J
קC�O�� ���&\@�L�ٙ���f/�hM�=��
��֌S%ɧ���d>G*���懶�FU千\¾��Q^�~Z���O��<`M�y��<|�q���I�K�P�Is(�fT�C�Hʇ]��7D8��}<�4���C���	(@��@[G��CZ�Q�YrA&�Q� KO'�64oh�
FUQ�]��(*���r}~Z�؍G^Hc����4����Ϗ��1�2A����5��SS�m4c8Ċ���R�"��f�?	Ϻu�Z��څ�5���q��i�`��2�D$�D�t<��4o��Q�gV&��5&��w�k6I�Y��?�E�3b�F��� �;���V����뾴v�1��|]K+�ơ`yo��r�|������0x�r��j@f}���y_�x�p�C5�j�[;Y�kj!G#�OYgb�dh������!�AJmZ
�s�Ib�UҔD���Ώ�c��$�dg5{(�H%Ӹp���(H;��;D(K�@K��,̜<~BǛ&����U��)2ZE����M�ƣ4�@�:*��J��xO�p�SC���Q�L��g{AkT���$�ơ�����*�r�}��;����U�j���} �"Vd�HvcR�6�p��.O�	H�����*��@�Nj0�U�T`�(}�v�zWmV{��`y}P��d����	��\��M!E�F`o�����t���FMH/�S.O�&d/,(c��]��4l�J��=�r
*���/+<��PtY1`�x��PH����x�UP��iڏZQ�YF�}�V�9��+t�ōqE��β+01����QSJi%)@�.��U������q��|fLq�t���I&<�qLFL��m�W����1c�p7�E2}�T�Y��9�^�*+.�w
�<���H%�ہ��Y3e�f�⭎#R����Ix�R�ԃ�Ӥ��+Qi ���To��@�Ɍ�cÅ�MX�ly��)[#:h��K�c+$ tfؒ7����K$@�$���+�"%��D�6�)�R���G�""�I�e2XG��H�N�~�M��w�n�x�v)1�s�p�$h8�e0�S%���$U��5'�u�y2��e�,�N�*WI�G��k���-e���)���?���|�Dd#jx�M�d\:&<|���qV<i;JdI:�I �Z��6��3P�ᐝ���a⣉�����/k"/k%f@��P�Ԓw��A�m�M�ˍT���;�ˬ�2@2f�R�OG���P+B}u>�)���l���29]afX��*�UXԬ�����І�l��㾫���I�n軟�~J����a�����`��Em44�v�d��r�t���D<,(c5S�	p�Y*̂5��JSu�|R��f8	���ZX��\o�˓����2��y��=��i����qѯ�V�=�׵� \~����1ϡ/3���T4-4����JP��M���S� ՗`����ȄĹWd,���j�r�T5c��F����Z�<�4S�6nQe��l`h����5g����H�`�8��I�V3 2F�ʧ��z�J��T���	]�d�<8�$6���0հ�)م?�Ɖ.W����L�3��Ua��"���$|MM6�4��p-ⶈ1}U����m'X�3!2	h�Ag�8�>�mJ�mƻi��#�[�m��(��~��徆.<�h�����݀�\�ʖւ�P7�l񑁋Gq�Y^��o��|OIqCw�NTJ���9���WJ���:h�Yq�s�~�wBR!�n3�FiBҧ��D�+�)#/�r�w;Һ�~>�Rf�艖�w"�r��;ѣ��^��n���]$�$\��FC�)i��,��ɴ3;-���P(�Hq�8�~L�J�n[���KfF�ǐ����3��\�7f%p�BL_�鹓V���9C��>*+�k!K�[��~��5�6�|'!�Ho2�7T�ݠ�W̻6o����:�YJ�jkO�~�MܙG$]��K�	Eg��o-�X#���"�����v�i��9�e缓/6|^5�'�(2�bl�0�kդ���Z��m���c��\"EO%����|�F�tF|~�+Ǹ1~]Z��[�c�"�:)̉_;#��w���ҨV�7Wb-5�I�k���B!�f툒�-�*��EE�Sn��,6cnj�)�_��e�ns"FÊG�}HU���n�����V��|x�}��~�������.y�3U��tJ���1`6�A��OJ����y|Y�'���c��ҕ 55��jɎQ@��3�s���ff��
���+ܐ"�����]�i?��%�TY�W!�g�Y<w���W�����
zP��X� H�$-1�@���/�;�mL���ė��A��]hɛ�Yy��<��Ƀ���p���#�D���,�c
	�E�ǥSz��D�M�胐*�W�p�^��7$G}�X�ܨZ�і�u�kI�Q�G��Q��80���"��%��C3����A�Oˮ�yߪ}R�˝�g�$8a�""e�^���vT5!��Q@v�T�y�,��HT�{�Q�ؙ�r�B$����i����z�4�{���$�kE���K
1�n���P4v�CԚ�q���#�e��gc��#�4��(����mZo�hcqV(͘���1������qvݮ@J^�C� �J�R�"\�NJ�R\�8��Li-�fFE���k3�ū��?�]7mRd/d����e�I���S@�?PBG��ֵ���~���:��ɹ0�̀i<r�
9�VI��)�_]>L�NJ�e\<L��͓$k>Bv�ħmbk�����P��ӷ3�e*h������TYF�a���*-��pI�:��Y�'�UKFI����4�s2�m��J�a��Gx�âC����O!;Br8飘�b���"�*����G�����WI��b�N#�����D0�ؒYD��d���c*����m l@�q;KT=U�sJ*C��W�����j�����ק��4� ȊZ�U��;�UȂ<w\S$��q�ލY��� h�����xʑ��ӎ����U�'��Y$�3�2��e�(r�@X
�u��U����Qe=v���/%���,|�d� ���h���A���P=4��H�!0�ܜ�BS&9��Lr�����IR5&�q�j�5�yK A��..��|>'ʏ'!&P��e����GN�hz�j�z�Q�}ê�r�amp<L�V�q��դhC�q���.���M�U�"h���&
��A�1�e���՗eJ\�m��ۊ�iŊR�\�I�=p�\�h�̂C�fe�ٚ;@�$����a��E� )nS3&l����@K�#Z	�@��4t$�����3�i�גhD\]
�\�JG^�u
=���(Ev���C����[גH��ʋ<�i+Je�pO�Ji�6��!2�gC��d�g�0�f�6�\�@@��~_-
��wT]�m\��� ��"Vȏn��H��j_m�Z�s�z�V]�^ES�S���nj�.@2 ˉ�F�G�ʀ��F��?�������"��
�K^0+G��0
m[���Z
������`�C��ʢV9j{�hw
�!��u>��98��8(�M.68X�QFF3H��&���C���QJ���]�����2\�\6�2��$�����a�����q����UĒ6�hW�l���ƉCR5ȉ��Q����F]w*+Ju��g9F��}У_��ӟ��q��eB�Jv�m���~bH )�S�T�����x/����χ���0H�m!;�.�ҿ������1~��x�Kl�2�P���i�S4b�u$zX���
�QE!�T�g܈D~']Yr2 ��i�G���
��eM��]�	�]
��!�mQNL-2I�\ؤ��Ek��T��|���M��d�cgJ+�*���ܪ�3�mebM��ޛ��ۥRA��2"�lŃ*�e���U��BT�j�_]H�.��0�j,l����/Ѩ��fBuaV�cP=+�$ȁR(
�E��b#�6B�B��݅�f2Gؤ�U�������!D��Z}�sY��).t�dJI<.� ����Cs�_�V��A��[mK&V?/���A|�E"�CۍC��R��k�Z���(I�o#M9./��p��y$��T�b�C0+���[��bv��K�S�=UsNo�Q$�i�J����(բt�jD�7�8*�zG�b�+K���AH#��(�R�bup��>XƟ;&%�3Q&8��Jȍ)Wp�R(�6#��d�\�_ռ�%�$^G���|�l|�I�����F��AT��XJ�_����Q��-J� �&V�C���1����H��Z�m;����C8��h��y"G���9c�H$lt�³;�#&о���{�jJC���<�3笹4N	�\?s�k	��ob�jj�r+NJc'����Y��x'H��t�Z7��m�Ҫ+SB6�Q�O�!��A�`�.W�oȩ�&V�ic&K�p��]�קOMK$�f��<b@d�I�4��)	�z�+��SM�(@��V���D�4sR\	!�*�A�S)4���,+Rޕ7!�-Z� ^�7��y93q"��|S$R����%GhQU)KCZ�P74S�?,bC8-�B�Av5OO�����giZ\�����
��S�ҕ��2��Ќ���\O̳c��.;�֎��,�w�6���w�ɚ[���:j-ƪ�GW��	��f�>4V�;BX"-,B�D������R"q�A���^���'T3��89yN��,���Yv�FT/Sr���h:i��#�x�9'�i%��e�d�J��?��p��=Mv֙����Ӓ���O�G��,�0�1ؠ�F�ȋF�J�@�z����)�8�r��c/��,��SZ�֬��E;�w�|Jq����$^��*Ku�Q�e���iI*@�o�m�߇L�{�y�C�2�*$Y�CeG�i{LJ��-j[RŁ�l6�5-�\I��ǒ�mx1��Њ�;�Bwe mʸ%Ҳ�#$�|��U��ЙH��XU��=u�)ݬ�<��y`�!@���5��4����9qaTX�H�lyf�{H�me4*7���c���S!��"�B0�*g��Υhv�
S���2�%)�p9��Qh{��~�#
�I5�����F�<�
Sȹ<4ʛ�
,yƅ�j���j�U�;l>:���T�Ʃ�E��h�gs��,KH	u
���֞�hwK�$�,������U�ɁVX��T��I��$�� =*}h�,\��9���|k���8�C��%VR�����JmQ�#Za�n�O�H��-�9\ț  ĝNVK�{m��VqU�i�=x�6
�ce�����C~)!�w^*0�2��:r�PY/T>7�˓��xm*�i��PF�b1���zC�Ԉ�F�����6*G��1��VږBE(N�mW-@�6Ԩ�r�c���\n�N��4u-�����Y�5:�db�t̘�v3A����� UP��!@;�������Die7�T�8�5�&< �ժ#�#�USc����7:����j�@��Orq��	�$F8nuw�R6��j�)
\)²Jbd�
��ž�8b�J�zWԃ]&p�(ՊJO��L���e=c�����.W�R7!��:�,�eϒ1�w�Q�V���)f��vu���/`�F�ֽ5���1M���
��Q$*�FId�����E$�S��}��һ�4��T$�	x�̘eA��u,ZFR�:�V��Ei�m$����.��`�_U�l2�3�Vئ�$5���o����#"ŏ��������q��qߖ8��6���Jt��������D".M>�љ%Ή!̓���H'��Ň��ڭj���~u�|�V�G@��B���i���9ZY3����������L2��]U�P86W�ypr����g�h̯��8)��(jX���ub�-������\�����R�K,VT�R�ZZ�FZ�t��gvס��4th93�m&W��cb��:+T�j(6ڔ��v�X�,Ɖ�px���EV2�ʅ��T�ƅ��F$�E���#<�T^�7�+�ҳȽ��ƀEr�Wpw�,OJPj�� EI��YāD��r3���ii����2Q��۲�QRm$�c��K�� �x�H�J)s/<�Q��+�V��Ԁ���J!,q&�\ZTYG#��y��<���8Q�$��h��6��������Q^J��zsE�l�ayl=�����ޠ��	�Ǻ�"$ި��eqX����RJ!0[u���Z��Ҵ�f��	艉�����-��Ɖn_�U��n6c]�
?���d�I\#7)���4ٹ����X;��#P���@�X��	�ɩ?��"�%���`�Ǚ��ӺPSn+�n�";1ϴ��i�M��cGA�COd�8Ř��V��¥kR)JĝBW�	řZh��L��^�Ж� �&�[Jn�Ly��J�d�
��c��C)��d>����O_p��MC&�V��WcY`r�1��3b��)������_-.�֕Y@ 5H�R�� D@>kT2���d~�Cs��P	q�E�)�.��R+uI oO�Q���b(�B��X��V>>V�$r|��.�ȲA���{QF;��z�$����ь8'��9��q�|�#|R�0�N��c�TXŻo@k���%� D�
W��<K����ż���ĖI�#��&�Y*�Fc��
�]�5w��eb�A^���d���r�LJ�L�8_!GΆ��l��FGOr��*��6=��7�)�}�D	X�|��<7������cE���n>yf
?uQ^/��5�A��;0f �t�͊w���]��	oD��i��rC��>I����'�~�b�D|g$Z�����'}	�w�q��f,�y���/�'+���q���7̸L�ݬ��b��$%Z��j�+�����?�S�%azL?�c����$>8y�>�c��|L,FI-1Nj+����� ~��X7���DE�t$�����

|W��/��	p8O�Ym�D�<X��p��]rkr0��UMT�Z��R��	����)Ș�&�VvG���.`��>F4�M7-����w�T^�(z��o�Jp��d��}5S2���C��O�~Uľo)�x��f�x�y���,�d�K��
;�*��۽�lI�awF�	H�G��fRj�>�u��J�p���<��r�d�'3�qO�qh����WW[��M$�AS�k��.���%�����?��o��|s�3�R�X�ɜ��P�+\�G`*đq5�>Y�.K�7F@Yz)��|���c@�����l���cȏ���\
�
�ը
 �J$bSة�E��!���ݷ�i���P�+Fgv ���
L�Kzy�H*��Ȑ`�	��܍��c��jI��Ѷ�@ԍ�M �C�B���ȟ-��^Z<x��T1���&C�
���‚�Zc���<��OU�<x��rp�rw�#n�?d�"��Q!�R�Г�u�v�D}���{ː�0r�p��u�h���f��:J�5H���Z0d�z�d���=�<j	2x�5K�ψ�"��+
��iQN��uyO�z�J��x)�ܑ�E�%l�;�MX�����k��H5��|���$��c���s즣��I;�A;�W�A��H�0���0���� ���F��R�P
�P6��>]Ĵ]�׎Hm
����ܮC�2r�Ls�nCL����UuR�XM=ĝ����	���H�ɞ��a�L��?,ȑ��ff�R��ͷ�h��&ԯ��%�'#������?f���V��i[�?Oʻ���~-�����o-U�nm���\����Uq�L������Ԗ;oB�+Mɦ��� ��?e<�����[�~~d��9�R�)��I��
��KC������t~.���y�ܡ��0���#�'�|�:M�ȵSl�S+0O�!������\R��v�u�N�˒��2���Rc�l�'~);��P�Ki�����5��î�#�Q�"DQ^g"�x,�8���^�-���ޠT���C9� ,'"Hs>�D,���y}ր�Ē)�O�Pː�]<"
y%1��&.\�$*C�c
[rؤ
M�Z���m3�]�K���
H0��rZ$K!�{#��Z�E���u_�������8�,[��c����7z7��ZV���Ӝ@��d�e�gI���7�|ܖ�ȎHb�HL��K�\P) ��(}z�"&V,�$�U��
,R�5ƻ#'"U
;hĸ,��wJ
�t:���P�=/��������L�*^�3w�F_�ۈ5�m�:�k��DR-�Tc���ߏq��ܼ|D�S�q4S2��?p�R*@p=~уx�7��$�(��X�9>G�rry4�#��r��)��#��H
���M��8��$hh磟��W,v�h�ztZY9���.)FXe�R����8�	�@rz��їp^�}�Q�-S898����cH�B6�6�$��I�~�}7��$���,�X��y�J��p��DW'��T�%r�T[I�K'q0% U��#���L��<dy\t�Ѳ,�!@hd�ؐP�h�N��:�3�0o�@��&lR�Gv�Ii]{M+�H'm�:��3�c]K�;�d.N|ix�|y��E�E`�ℕ2-��&\�,Ĺ<U<"_�#�<�l�=�̼x��&���*9d����ʸ�	#u��fuaj��&�{�1P�n����1O���^.�2_s2�K��w����%܈](�M��Ï�������D�P�j��SwzT��E����ٍ�%c�k����Pr�v��BdU��p��%6 �߮�KgR�)Ff4�3�\�9>�I#(�+�C�fV��FƆ���(��<�*�T>Vh�5fG�c<%
�Y	��P�]�?=��4f�]�K�f�xҌX㍱�,�Α���{�	�
�4=�ֺ���D�<蟚67I�ьAX¸	^���Ao�
�8�R��9�D��~j&i��X� �-��&�T�GOMZ��e�VI�<�*Ƃ$���hk�jv��o�1NE�=)���e�c���Ƙ�k|���,у�R(MЈ6?t+nH���pPG��$Qc$��!cP�� �wv���w]�+Nٱ���!\�^A�D�J���@��O)�'F�`��v4�f9��R9Gu��!p�h&�z|��RX���%�#�r|Nc�����U�ՂH�TZTh���V�q��c�������ǐ�jU�DT:7�@�-�ծߞ�,��
��<q�5���n`O��n;�*Lر�1�E7�ۀ�p.V?!�c��g㋪I��w	�eq���G
,ȝ�Ti
;�*n� �8�R IJ�y�#���9i!UU
e��S�mJY͞�� Ux�Nw5:�&\�C&4��9��4���xI�9Ц�h��7�##dG�Y8�2�4��G��BBE��2
B�VS�2k���i��'�)�0���nʱ�5R}����J�;c^5IsB�rqa|y�W��*���@B�1U�tĴX���˂�9�?9���r{��dYB(�D�
��C���JB&�}��r��
G���3��|W��dG�$�5�{�29(�J���kZ���)�e�Qfb+M�`^�h��LÓ�!��(��]ZP���ZA�zR�Qg扏>?Uġ�F�8d�w~�0B��05Hfz}0%�rr��9O����]�C8[�M
�F�"��;�]Lf2!�<h��0IZ,2���y��Ea��]��h]�OMj�MFe������	#�1�hN�9*Pns��7�c�,:?O�RX�����d,7�ܐI�3*�$Y	or5
�Ԛ��"2�
���T�*?��d)!�%�4�8�e�k%��m�z���P&2��CrJ�ag��HW� ?nW[*�%���G����a(��U����}ϩ��p8�]����=	=:�C��[_�+Kd�I���1Cf\���/<���*�6
�f��Xu���12%�;�N@�Ocp�q̜|�(���������ltZ
���,a��A�*��\���2J2�i��-����������H��AW����
�r���6#�G��de�40D��F�Y�sx
v�Z��F���d�>^#��(�U�f!^;C�,8X�$0�3FD^�@�(�
o��Tyh��n7�ϛ��c̏���"��?R���64Jh��CJ
.3�./��yD7��S��d���hFt���l���?s���M� ��ce�n�G�ӎ��8�&.?�f�8k��n�\�){��M��c��o�D�%��S��<�s=�"G�X�_�d���GB�:m֚y��CH�OCM�!�
_%�'�������flh&�\p�(7<l�)ލ^��k&Lo-���kh�¼f�pQl�\~N(L|��~,�[Q}�4fcEoӷ]ί�`�� �
9&��r��0�@ʸ��RX�%�	���;~z��DK�@���U�Ѓ�O"�P�F��@�@�j�?-6L������d 8M����!lvǝdD
�#�"BW�V��zR�ޚ�1�F���E*�i�8�..�6S��Fa�e߸A5����CD�\�\�p��Tb~�CGV7*�[�Zڒu��I#7�7j�*L�C���@܊�eˆ2g�fk�%SaGn8�ӌ�B9s��O��h�kM�'��*P�9Ův+B�����a���7»j���t�b�.,��2��d�[��\$j
�7Z�j��wpScfb��K�J�D��܅h�A��-iS���a��Q��At��O��,�d�R�VAU�u�uQ��J��d�-���d�nl�@��q��P���!&��N����Hfj���㹌TN3�e�	�B�m2��]P��$��6?���78�z�Nq5fF���V�w̕�w��"���j���JjҋVE�GH=Ɓ��șVlt���Z)O�(5=��O�!cJ]q�G\��䇎ę�ô��\���]�נ���M].ǫYyd�s����������)�w��"c[�t&��S���D�P��e��q�86֔���bϚ�])6k��S�E,Y�V��A!N��65�k�m�V��y����EUUl�Sf ��u;�4����\f�?=نA��GO\*A*�V���������r7?�HˢE��sb�\��X��#x��n��#������;�O��a3�!C����sm,�D�0�qX�`$�.�RSjӮ�q9�T�*WDId�6D�yl<��$H��m$��;��H1	��)Q��coJa�8pe�:�r)ڑ��a\���*�[���m�]�AȩM�M�p9�"��9�+���+:-�GH�\V��,���w4Vɞq�@�r,9u>Hb�L�hׂ�r28�^c�O�p�ԾkL�d��R@��xpߦ�)��
�/[�����7�?H2�`�|��"��Qܶ�lG��[n&���bĞ
o��w��i��r��!��d��,]�f>�E)����`�a��ں��9�%1���>�U��Ɍ��&D� (GZБ���c�`n�,/NAh�I�>��lYѶp��R�%Am��wRJ��)][b%�?U)c
���ó�9"0��gBY��jI_�o����,O��@�P�y�py,��b�IfXj�B��m��֕?�����"E��'�H�'1϶L~S�W"��r�K.2�e�FYd��H�*�HV`HU(�t&)������bo�F|y�
;�w{RB�P��CF�0 ��Q�u�ݝ<0�r��y�Lyn�lexK�FT �HM+�}6s�pO$2
��%�N�ዑ�4p��)�T$i�-1�\7�#�О�<�{���w�u�`K�\��=��F�q�=k��#Pt~�JC�&��I��x�H�.��v���)�MI��u�3��Wc�O�f�e�k'�!A}�H��-����#V��q�%9D�h�4��6d��A$s�-��[�����A���A&�Œ�.�͎d��@��᥄�����˦�6��]P�lX[� .\�0���?��F��GP���3/�

6�[m����.��p��eʑP�D����=�mC���ꑈ �RH��/��<V�D3�2:�Rv���CR)����(�5N`hU�id��`�!#k�YYW��G�5U	4m���3I���
����i�'#)%gN�C���QQku���z�2��	&��=�2&?�q�3bF.I��5����(N�*h�ً���@�7�!�͑�y��X"tN�68��er:���jjD/�O(���Ia��'��qa�k�c�4�Š��Aھ���$bX�!��1z�ib����X��@4R���`h�oJ�)��0/�B!г���A)�ʪ�F	k
�����6;��˗3�ԏH�҈���2x �|M$���ci�v���R�W���K�.�%SWLL&�ė���N�7Ș��E'iH-��ښ�`��$����brZ�5�vI\HU�E�,���g������v�꣐��O!��`JO �V�^hAb�ݽH�:�g�2�"ߔ���ĬL���O��
�ϊ���v�K�%P�0�B�>�|��0�nUZa6��M�%�rS�1�||�Y��ҍ�����SW�d$�K�Ob��d�y7�وӂ`��*�0o��n��v߮�rR��S��r�c��C"2��L�3oBI$�"����@f�N��e�E���bE��?�@�@����Zt����9�K�F6w�c
���%�ƌI3�)�7P��ԁo���M\HX^_�6:�#O��ɗ,�1L�H"*����-Anz� 5�>I#"�wI�tcf�:�$-$(����dRU�j��mQ�d�@��(��
|9?�Y�NJ%�d���1kF�u��Ҙ<�/d^�V^��*rx��A#������{@�`k�Q�	�M.�	D%�����p2%�2.L�dF-f�~��*/t��Y2M�,߄ьC�]��: �.�H�Q�I �=�1���S��O�%�[�C#
�2x.er"�8�;S�䅖FfV`�@��;SI�����=���$V��w��1�9|�Y2<����_�D́.Q@�jH6�ڕ�e��kR<En?=�"ޫk� Η�����2�<8�-�cŕI(��5��
�k�7�d$Yֿ'�=�&�H���T&��A���|�)p�?�K��+���p�'�m���&D�1� ʯ^�Rj�)ӡ4Y�I�~S���Ȏ>|��^��=IJ6S��+"2�&���������x�j[���Q��\���UÚ2�-e���v��;m��x�y��y��#<�7����|���|1�xQ�ɛ���YJ���
���G��<`��ː徟eH�ȡ�PY[������N��#�~����"�_q%z�OA���8�:��Ym!nc��OI��r,���՘������l>��1����dAm/gc�f@�$e��ܐ	d��zz
WmyY,�[&x�:�r{��2��� �\�wISM��`�jy�K�e��ġɃ�$��CH����tA�o��ZkT1�jX���d�DS���<���1�� !	훒�s�o��h�}���˅�Kզ��!RM喀U�QEm��#��\��r���P.Sc�2<��-�V�v*Mi�G��r�uq��]/!��cB%Q�*r�?��ȱأ�EҀ��r�#�an9��>L���
Y��H5����
�A<��{����O,�B���RHL�K
��i����(��)ıS��O�����F���X�iRC
�h7�y�?�I�Ҽ�R�ŏ7��<�>��Ff���n��$�OA����L0��|qw9Vh�1#�+�`I;N[H�
Hz�V�ޢ�H��Nd��`��V8r�=�ym`@UaZ�AZ�ңS�c �n��'�H(8�a}bc�3#ʒ<W��*�%.Q����S����]�)�.��Np�s\��4V�ȿ��ը&��H���]�|��_�%�6E�
��r	!ȒT�,�v�I!��6}�TzW�
O��I�r��O�l{M�:b`{F��I2�T�Zt�ֿ=ef�F)�'�G�u�b�E��b���%ŭSO_Mf�-��{�T�A�򜌴�&�I��S���I��:��̢��8%�\�><'�u���G0J4�r�r�ꓵ7�c�%ZV�G���kr�U!�N�VV�m�j�B�àӭu�a�@mi�h���:~G"|lS,2�!h���I�1�7J�M�#Q�1Hر�|oT%��Uu�b�R)Z1�*�a[�ğ@~ �ei[�S..���qx�C.E��$x�,)T{�����������Y�9+K�tǍ��Q�R
%�ڻԵM�
���/�F�<i1�/�JH��X�(A5
�T��צ� p�7Q�����8S�,>=����l�re�d���HR&T��C\M���SχH�%|{AAjBZ�b�B�&���S��Ub(�����K�rInH�tq��<j�Tr�ƿ=^1L��ȸ	�?"f�2��҆,�"�� �J�SO�+��F�R�#�]yl^n2d�{S�����VR�fWqJ|����/�4�A�BO3����f!��`�M���cV*Av�CP����3�8r�,uMar9qg}>F��qy@?�W��m��]&.�B[L[��pU�e�l��+S�[�H�@��2�F�:i�YU��H
TY��&Ha@��de4�P��(oҠ
�Oµ��L���0�6U�>��\;Zo,V�,��h=ure A#���Q1�d�q�v
��U\P1��ӯ�I�(1j8FX�'/�|L<PI^r-!����7�=z�8�o	��U��3&L+8�A3:�fQJ�z�)J��MZ3zd���xY�d@x�x�^ƅِ*�@�ԑSV����ȼ@<�1�Nk�8\����q����5����M��Mt�H�`¬�������9(RH"Y�(�1���$�	�Z��E�k�q�ӧ'�gs��	�n�J\@����t�C�h�R5'Jx��蛏A�gw�-C���h�O���w�V���d������ͳ8�T�3����î�7��}l�?�[��#�C ����PV��T�4�)�u��l��+���r����x�V@E
(�o��<���l�sx�~4K��;/'"h��ղ/ʚIPR�M�Utѐu{2;InK���$x���2�B W����M����^��~Z?+3n||���ȫ$��L����Pu$�P�v��\�4�,br��e�Tʎy!����IB��2/�P׮�Sz�W��pr���Ȋx�H�%P��
�0�jw�m���$��:**�e���gX�ܱZ�mfj�
J��[L��e6�9�)LR��w*�@t>�Jt,����D䋀��K���W��V�6"��~ؐ�`r@�T�t�#����4	)n4�<�8��~ԲD;J��HȤ�`pV�u(���d��_�0�Ʌ�/�48Ӕ�:�咖��R�����C.q�z����'!�h�W�&>�CRj}���Ӧ�d;O�Ew|�s������F?���;�*���.m��chǪD
�+�ݜ�֏A#�*�>�,�EPhI���\r]F�@����p�1b�t���N�T�$�Հ��o�g�� ?�><��_D<�������1���pl�e@��܏�ڔ�Ւ��A#c�����-DN;���\�._�{KdIcy�4aE��M�:i1b�&�=-��%E�ZF��H�0q�Gݍ�	fZ���թ��k�C��R$�n�'�Ԧ��~$�5�;$��k[�{A7JTE��{��x�32ٹ��I�)�8d1��u.*����많	n26����ݨ0q2�7��6,�S���(�s� ����A ��K������9r��q��e ����Њ�L�Ꮂ�)�#:
Sx�˖���FyV%����@K%⻑SM�w�z�LѨ��Żd��Yf��cW?�=��Zq=j7�q��WU��F��7�A��͇4�6+���""L�����nJP��Cr?�}E}PɅ�����<��;�k��77
T�
P��M(Ϲ�nB?N�ɸ���?;3�͊ &�m�٪��0H�^��O���:�`����7�٘ќ�\tyr$E�y٘���i�T����^�D����ȃB�c�s�Y�8<vB�H�*<hћX�$����e�CH��1�E�P��f�G'`�KI.*�e;�m�Ё�FX��E=�Y�f?���̑W�D,�(���u=Z�����M>��H��������<P��GV #5l�6�ڿ?����g����⏋�dgcb�dd�b'�.d{R�B�@Sm�R(*	��Za��͎�*'%itg��ŗ#* ��B��)�J���p�z��I����r���A�#7�c�2e�B��n`��(�'���2,�D���li�M�x�/!J���X��l�`4u�u�k�H1{q�t�.��a������n�%�!FI����T�����?��F���v�oӯ��29�-�B�+��B8>�Ơi,Gz�JF=i��z���	�:QzC����ct�8�T8ycp-�,NԯZz��;uY�ꍆ��c7n!�[�*�⥀m�M�zj�r�T����p�o�4�1ĭ�/��H��e���Q������KX����6d`ŝ�2���[@�"3�(�U��-U߮ېu������B)�>_+���I�c2vʨ�U��$��O��֔:\2I��s�A��#"�)�;�R⬏2��p�$r5��Z߈�t�kX��q�����,��]�<k+�Guċ9�V�X*7N��C�e���ҾZ'�^������Ӽ�d���
HBI��	L��}��?�,_|%�����#4qy2�!ePeev�D��@7�����N�B
�y-r�T5�/G���R>@1�P(��t/s��)���k���w����F,i�Q��gd��G 4dG!J,��(n���4�D�=�Ɂn
�	Qt8��)Lj�g9��I�7epř]�R@-�SA��R���S�@��ix�L�N���D�b����@X���?���$�7bG������C�S��� e,S�ՖkL�MAVݨ
�D);|�Y����<jy8�E��f	Dݭ!E���i��\%ZYded�aO��r�T��m��Ё�,�oBEu�
����-KtO�YU�(
��tZ�
Zߎ���hD�'LL�4�c��J5�~�����T���	��΂,�yQ��VL��xܐSkH�h�(2����`�����|�4��×'��
T�Y�kAm�
�^�u��['=�*�6Ij\�DH�+����OM&c�aa���M;���:�Z����,��Ҁ%t]�6"?/��Ps(��T(EV
�}�w�uSd"�D�.�N?0q�iH�o۞9���&��Qk���=K��_�:<�f+<i7ӳ��T1��c�V���֣���^�@��⸘�[ea��0�t
�cQ/�MK5��d�(���*0:�[:|�8I����%������ޝ=6�Ns(�7MT�n�ė�||YZX]bǼ�U�U���7�k��/D�u8i�D['"��dc�k$omA&�
�?-Zq�e��ǒq��+1�8N����r5���~=F��l��b,����,��/g*RY��.
��>:2�v+��(�#509���3��k�����@��S����5R�κD�I�J�$�;Fo4j
����U2�T�,�������hq
�K��:2�����ҧ�}f��Vr<%���]�#;9rg�#���
�ۏ�E@�k��H4�k�ܽx�D�2���Ds~���Y#d��ݎG�$��#�==C�ف:�!YbiZc1�V+A#9�D$
1v �ք�hZ�o�$
���/>
q�#�*��G��ۑP@޿=�<���|d�UdG���!\e[�%v�)V`�V�+CZu�|@
�U�ƪy�R�f0޶��TfFaTf-����?�`
�����8v�I❳T��K�h��C�^�mN�LjJ/�r���1�[�Ǿ^8�\YLиFh6��+�*G]j�d*0z����n
~.Cl9�d�M��+�H��{(�P0���֔֨N
NL�bU8��%��a����N�fynjT�*h޺��~|��hLq�ıG�t�&gb� �:lW���}s�1��K�F��R�H0p���a���JQ��Ђ�aZ��Z��SJ����q#r�w1��PXA(�	�v�����q��Jܕ����N�e���:�����֌	��o�A�<�p�Ι��i��/W��=�qꞔ���`��_B�5`$G�q�:�X�ٝ����"[h�CM�{�;צڠ�]��o"��7����n;"󍇋����?vf�P�=m�O��m�
F��I�WfI��y|�{��~��۩��T,@�f��=RS�΃��0P�fC;ea�0��N�H�ia`���-hגvҺM��o$ws���ed�$�l�Ա2�"w��3.��m���4�U�P�oI`���y?V�(S+O	F��YB}Wbv���F�*?ڴ�$̅�Ç����x�T�)�*��3wR+,��Ҧ���a2
Ǩ�!�5ZxJ��O�Js��h�i[���n��
D6-�T~:����
�>h���i�H�8��1���U�(A���#R�+w$Y�ǚi�ך5�?+6O�!���1��CJ��V;�)J�W�i���1FĠĤ��������/+��̙��&��}W�'��i�"5mV�}듣���to��0��Ij�VbP�����l��^�2X�$i/
�ɒw��IK�(�ʂ�ZIbzm��?hx�-���|{*Hr#v�QVgX	C~�F��PS�&0Q�B<�4^!����e4|R���<��@
�$I-+]���Z]g�=���О��9&G5�O+1�`��T�hm��`J�=G���2�w:W��*�d���8�FI�cF���$o@Z|59��hBx��.���%��/�/�M���$S�B�X��{�r�ޛo�Y�|"'c;�Zr�^���9�I����IW���Y�1B!�+T.�v_��J{)!G�?�P|V����3��d`�aLJ�r�/��#���\�Ԅ-ďMZZ�Jd�t^c�<s;��?
"ʊ�X�X�R�ܒ���Q�t����e����r�v6>^VN0�*�+���e얠��7���=42��N��"n��� �`��O�\��$Rc��X��#1BA�oV**c�e/4�3�u�����t~�s6l�eˊx䅱�D���R��\�rI����VZ
�و���O�>��?bD�8W�VF*�Yi�R��k�.�@���^�]���4�C�� h�ޒځ��n -J���O�Ck����%�e��Ǭ�?�Ҭ���! 5m5��۪g�'m�P��is3&���A�F�8��QE�t�LuPc����	�6���D��|��c��R�4�?���X��p��6��P���b��a2�Շ�)�lw��̑�7�в������5֨w[�<e�hM�b��y��=�Zѫ!�V�>��V����Jd�����L����d�����4VT7�������LZ��O�qt�[��V�@�����T[Q�#jJWQ��R$�Kڌ�4��]q�,�&m���c��H�y&�}��H5,j+��kF�Ho:py����1��u12L&y���Zҝi]�hEȨ��H���3��������Iؿ�u���S�]y� �����k6����_��PK
!<��EE7chrome/browser/content/browser/defaultthemes/1.icon.jpg���JFIFdd��C			





	


��C


















































��  ����+!1A"2#Baq�����(!Q"1Aa#���q���?�t��u7�[N��t�RZi�I-v���Č�āl���I=��G�#W\�</T��x#�@'s���mof�;����e��E0�m�<����pD�1p޺잽u�_ g(�Fc��(�f���_ݨ����-%����w�`�c�?�L��eb��A"��6@��H�‘Il�D��j�]�X���ࢣ��j��l�驖�����/+r+�9r��J�\2�U"��2���e����}Ӛ�}3m�\�~�c���qy�fP9#��y�9�{�Ff��^�]�ذ�F�#C�'�z�]�Ӷ�S}��S��m�	!�5��$��H%d����8��Cb"�(kQ�kK\w\�+;��H�89DpX�߯)Ua�wQ	�iO5m��*�힩��/��������X�3q_��� 祤�� ��*-Ɉ}?B:l�/�:Mmf�U�.w�3)J���t�%X��r��Q�C�.�����6�M���z�k��Iͷ�*�b�X,����A~F�
j1P�t0��-L��T|M����AY,li-Ҿ�el�s���)e���Lr������/& 9h=J�MM�=Zb}*u-;����.z s$����;���;�!�+�8v�s4���Xjz۵�۲wy*.��Kw��Qg8x���楘���>a�a�֗^�F)Ym�=��hv�#S�.�n�Ip��T�����/Om�B���G^n7qDP�I<A��
�%0S��I�����'���kr�O�,�\J��PK
!<�d:chrome/browser/content/browser/defaultthemes/1.preview.jpg���JFIFdd��C			





	


��C


















































��C���	��<!1"AQaq2���#�3B�����$brSc�����1!1A"Qq�a���2�����#BC��?��s��>?>�0��ͼa���3�2�r2-�7��;y�Ť��<h��<��[��:��QF�o�*f\�����Q���
1$�M����S�,@����3�D��ak_�S��0
m��7�º��ߞ,q�y�ij�R./����Q�)�7;��c(��ۉj�
�8��l��悢*C(�c̠x��l���E��
��8"�'y�޵h�e)�k�G$�M_頻#���=X�G��Ȉ�бaə���㯾6�ǩ�>OA唺n�i
SM��w"�iE$�U'�F!~���G���?�v%�!���}���E4Y�P�!5&��x�h��XT+�{ㄪ�e$�0e�nxj&vݍEe���:Qnn
�5�[��z�_�T�/�+�r�X���D����>�
�ldN�˛Z����fd�L��#2�9lMxV�-4�O���`Fь\��c_�!AS7NS��mA�Ú�Y�5e4����� ����Ã$ ��6*����~=��"�����N�2����s����NIQ���	��j�`�X�	VV��#�mǩ"�x���q��,5L'k��Q�J���%���aQ)�G �׵���f���^���M���x~pCo�����$�[��g�����f��@Ӑ	$/<���
�x:���T����$M�jZ	��T0�&���0� �����X�{m�qs�)�0����A�/r}pe\Wx�K�E�{	R�9�M;��d���Dь�9�	�g�o0U������&#��_�c��_�tn�RPe�ף�{��YYc���~�.�'l�[茅��w�t���-(�t���Q���6���ʵ�\ƺV$��q�[rB���s��a��.I���>��ª����
�
g ��tY��:G����Q���چr�w�Dj�bx'b�M�\`8�v�f^�r��x�:"�Ժv�*�Y�z����G�̫��e�H��-�-qe��q�i{��)(l�M��]��.���$��+�+Je��C�!�#��M�0�@�p��Ա�'9k��3���C�ͧ��J�*��H�9Dl�RU�*V�;�C�n��Sk�s�¡P!�4�}D�)*z��l�hsܒXƱ��}��4�`�)�V��<w}�l�����a����%ȋzO�����U������d��� I7*?�F��0�y��q^;3�̅��7� ]�x�x�6�4�M�;�-,`]�,Gn8��`H��AU���&����
��c��|)]hy�A�$��Z�s�36�m���z
@�S5d4iJR_A���1�n�B�C�U
X���i��������Ɵ¦�2�U��"cv���8���&�D�t��P�M1�*��+���Wq�x�a��@��@�x�CQWY�QK��|�t���� �f@]�m����@��1ؚ�0�NqW[�D���d��� ��7���7E�������os��+Rʖ
��FԼ�25=Q��-q߿k��ٵ1��ON��:��-�v�8zP5@O[Q4�J&q�"?�	���s�A|#Qۂ����������+�X(
O��A���j�b�Tհ�J�����ŏ7���a���n`����yԙ���I����h^�X�B�����ޗ��@2(o�r�؜}֭Y�󎻿U���2�Xt�9�U,�)
<R��Py�y\>`�Ce"��~�OC
O�讕��ѵ4Z�v����`�ȩdY��؃��a�ؒY������
�ɲ�P~1s��'S�i��iQ��Z�-�lnn~R��"���p���{a9�_����S��C��W�7�Y���|�!������=D��.�(�=����l.
��߾0�d:۞��hc��؉����)�(|Mu�d�SS7��VԢ��3*�ʂ���c�"df�ݢq���y�.��~��d�+�i3�e�[�(��D��w�U,�`��fVƸ��}:�ɔ�ۉ��5.g��oNf�1δUK��)-m,���׵�&h�-����4!�{�u"�Il��5Fz���MO��j|�1h2چ`��8'f�H�5��~<�8�\���M֢r��Qd�TM:�`���_y�>��珶,¥�~g���]���Ի�r�,�ؒOny"���%	��Κ
"&�f�(�o��;���GQ�5��y��bVaK�2K+����)��U�򵅉<��n��:�H�����ll��%��p��|��}��2�*�I]�.��Q�%#-���ǯ\���3�rz��?�G��ӵ)�Qa��>)o����lI<���zKw0nQ
S��g�Kam����u����)�����X�r�ڳ�
nn?�l$��-M�?!���v��l�h�B�r�[�qsOLY���)�<Dd]'h������F.bUg�{zv8�|0�DXv7M-)th�0|Em幰?ӌ>'�
>_(Lw�g�Sؘ�ȶ���Ɍ��9���
�Iһ*I#0wF߉���{bSыx��p�H��)
)��v�>$�a[z[��vȺ��nڥn,�+�2�I�lj��FI>��~W� ���W�=ko~]���,�X����.m��9
TT���N qm�"E��?��'M���`�ˍ�o��<�z��T��1�ે/��(�� �[.�"Z����U��7���&˅
��{}��Fb(yN�ɵ���gJ2=U�j���;i*d��Q-DҳI��cmǒl�
vS���FL�4 �&���5?�vM��g���Ғ�/����4�AW�X���k�T���rfE������c�o��]�st?FeK��1�E]M,�P�l�Q�T#srn/rm��`ǭ���vO��Բg�U[�;q���-o�\�V$ȴ�y(�A�P����):$�A>m��*�ÂpΧ.vK�������b���?�\̵&��r����k�˨*x�0)�r6�Xzn<X\�����$m��q��s"zYK�
}�}=�<�9�5N�(&%I�����j�Vv�A�Al�	c\g��n1�*r�������Cj������+�Q.eCuE��P!�0	V܌W���ߊ�\e���<�qAF&��{ix�J�%��#{�n7&�kr<�1�t��<\ߦ�{VCL�0�έ`[���X_��{�-r�L�C���m]����o��?\�q�����%��#͖e�S
��
�m?�ر#�݆��Ӭu�"��x6�|M`T��X����$����-��nO���„9��20 I��’��^
�v����!D��G�@���gs7�<v=���.�09;B�a�h�,^=���~���f�;�&��
ʖ�P�����sq��*q��9,46����ǜݙUc�6#����"�S{NP�X-M1��w!=�[�ֿ<{_���u�c�a�Q-]K3|�Ҫ�[�;��k�9?��VE���y�W��n��"��M����E�sk�k�7�9&f���y�u��U�0#��m�~_�턳c�ƪ�4$Fa�e�0z�5�ā���睧�ޘ�"br�㕙2�ԝ2d��w���Z�̞�ia[;DB�<~|aG<*F�Fv�3��E-.�J]'\)�ւ��s8H�eT��C]6�&�^�+`�|fFF?�z`2?�&��梨m'�i���#�t�_�+�IPc����`3�q������eR��r��ɞ���gU6���Wd���"�1�*
�n
L`�qv�ԩ�b�M��q_�=c:�|��Mno=s���(3]9��I2�C4NʹwH�.7Q��Ͱ9��K��W�C�	<�e;7�s��v~��-�Qj�f����#���gi�eR�$��R���[�+"�>w������6񍇽��9��:_�#�xL��5��J�����<ǿb%�P��i����z9��8�W(��5(Ƭ���k�Z*����%E�$)<m,{v.L;�c|{�?I6n�(Vξ�`��/W|Dd}U�~nz쒊�/�2��	'yj%���H��T�.o�1:�B�ﴁ��ٝA���Z��Zc#��Uy'��
��د�tEO;(b�ܞ��J�w��bإ��]���{�����O	�(6��um5M$�R�¨P�V'��{�zl��2`eFV�<��	L��5����ۿ�����f���{m��H����Ȩ
���&r��x�c�w\0e;�R?���p�*rPrc5$+�i*7.˩����<w<\�D(c����*'���x^���1#e�����1ٜ0�!d2G���P8���N���m��H���<��Mc���V���>��L�`�;_��߆*�J�H����Rĕ>��^Ǟ�ث N�n1�9����b�S�$�$�nd��#�G�rq���Ja�������y?��=5LT�_1�Pl�YI
X߷&�>�xuU���>�f���%4����
e�rny���į��(U<���-�d}H�tg	��_�-rEŸ��F��xB��eS�UcO�p��w�Ǟ��62x<B��Fk秠x�P�_*��{m��	w\d���+0&p�_WQ|?u�:�ȩ���1�k��dEk5=X%
X<)�%����M�!�����W��]]˺�ӌ��e����#��x�’g�n�K�R
ՒE>�į�Ir�(�����
aK��.��Kj\����B��+�<�n?��ņ�Q����pq�Mm���WSSRt׬]s̩��5>OM�
)������ob#sab.N<�Ծ2WE|���l};.�7'��t�!���Z������R�V@�gr�\5�`=�ύ�d��O�a�:�����v��������\�I�Z���%�(!%�,���YLi�u��%�-
��qs3�)����嗥�~S��	���u暟UVjJ�%I1Q��&�"�	$*)[��#XrqèǍ��>Lyr�@�m������|E�WWu�.�L�MPr9ja�ȑ�m�w���퉍�l7-`p���Q��ο�tQɗG[�Բ�_��Ǹ�/͈��/��|0�g���ia,?�KD����w(�'�{�a�C)c�N�4$^Y#SW�r�h�`C�-q~�ܯ�c�/�w\�X��o�S��bY�TCY$%�-{��O>���c؜]�#�I�UA���"z�e����`��1>Pt��!ay��j�(7.�J�	�K�zpn��`�$�i�S���'�����b���^�1����	�o>`9����e��>�8�W�E��y+`�y��e���߷��K�]���=�`)0ĭ�YeJ��S"���U��@k��[Ǜe:��ep��/�RES"�I��_]��Ûn�;4�8���v�p]KDן�MCIK,
M��dannI���ߟ����e�٘(�v6Z켸^Q/Z�co�������i��MR���bK�|v<��?�
ȇUЌVHj�i�"3�Ncn��6��E���-�2�*9Hk������<M�HiV��ާ�'��|��6j�M+�\j��B���[�7������$��n��`T��>!�):�`��Ze�r���'y��
N��
�������zGa�pe�ӓt�Ϻ7��r<ލ�x+|ܰ����;ymۉ�O<>'�����vL5�_�t��˵5sA��O�5��Z����a����m�}����UB�>I�u^�g�UQU�,�|,�c��x�k���,�
���޿��n�TV�9�Z���9.tt�I���}YZ��O,zʕE��x��	(�\��#&<V���c��4>�V�@�t�y���6[K�L�T�#;����$ۙeضbql!)�3Y�����P�|9g��3��Y�Z�(���#d0�y�{v��
jd!R�/�,)q�7��~��QjAp�s�s�zE4�EG]
�e����rF����ʭ��|���ؓ��y)��?~���b�v���4�Y͚֍V8��P����~���=��q|B�6�v}%�r@�U��,�/+�77;E��La�r�i���@�h�~��҅f�n����p;@`[k��UA��x��}��?_�~�^l����\,UBO�q	(Z�{G��ć)aDm�
�S�K�b�o�k#�{��=p9G {����v\���RcS�����톌�{@�c���de�frfu�_<����[�|P3�4N�
obON!�����|�G6���a�ǰ��E�1�����r�\��`�H���c%;���5�L�f�����#$�yb܎�A�e߆̀�v�߽�kDH���V�O�bH[Ų1,6�ϗן�˟�c��*��[*��(Lr�A�ok���>MCz��ĵ���:��F�����(�_uc���-L��<{��Qģ���i#UF��"�̌�b~��
���*|����u���@�v�;���{M(lo���PD��B:Qp
�{�q�l�H�L\d�+z�5�IKH�Gb���d^�1��m�yo�"͗ O�%X�&��E4�Q��1����Lq�eci�7�a`тo��6 ܟ<�8���!߉������%�EmM4e�Z�iwF|�S�������F0|'�q���!��WQ���W�3(��כ�����o�g����
��:Ld�?xuO�G[(�x�k�%�@J�������mbo醮|l�]�"OEGv��5]qβ��5AQy*�ʂJ�<��2��!�apop,���Ӌ�����qc6&�鎓?H%$��T��-naYV�j�ō��ÀI=�7��-�;�Bl`q��SHe�W���1$�4�#��]�vc�7��{���rc�7�WPtd:x���ᦒ��4q�Hc5�Z�}����ª��z�ɳ�uc�
;�$x���$�}�9����iˮ�6[�X���l�q�`��~��N��O�M4�s(}��[�&^"Lh��U+�(�$n�7���O�s[�T}�լ�����p��o��SAA<�,��Snl���"��6#Q3E�54lBx����‹��T���@*�$j$k�ܱ>�Y"��Q�<"�� ����
�7�@X��B���~#۶4^��8�f�B34� 
#���"�w�O�+"i)s0�����m������x_o����~�V��yA=�7��P0�%�Cr"j�s���BH<��M��R�ੰ�v�@���ݰ�5�0�3��aH����Yw
���~�c����Y#iqӴ��֑X9Sk�������WM�
�z�O�ՔƦ���4j�
�O^O=�ƾM�Q�3l�hK9e���=*���ja����fD��[\��ށ�ҵ�P�gY<Թ�_OSѼ�f�VļG|y�eH�K��`G�#JE��CM�b�%ڂ��p.���a�|D|�^By��NO��fIԠ��n"�}1v<h�w,����[c��Y�hh"���λ��_Ss�~5}�����nO��D�e�����7����m���m�)�����,�0�*�[�7q~�=��c�61}����!-���ľ��,o{��1ɾ}g6�?��PK
!<Q����9chrome/browser/content/browser/defaultthemes/2.header.jpg���JFIF��C


		
%# , #&')*)-0-(0%()(��C



(((((((((((((((((((((((((((((((((((((((((((((((((((����"��	
���}!1AQa"q2���#B��R��$3br�	
%&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz������������������������������������������������������������������������	
���w!1AQaq"2�B����	#3R�br�
$4�%�&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz�����������������������������������������������������������������������?����e��F���ߏb?
lj�c
�C�����Yed+-���0�1�'�ME�I$�|����v8�U�#���m`������xg0<N�cRFnjg�3��j3!m�hF��9��'8��
�DOg'�:*/����V��2Hg	bÜ�m���O��X���9n}y����HQ�M��?6I灜�{c8�ӽX�H�
5������;pOs���T�$±®e/����V�wcô稧YA%��PF畂�-�d�O�>��n<�2'#�����~S�zc��E*���l��`�����j{�d�ْ�S,�ym�T��0���!Nzs���ُr���#��yϰǮO�8��@��]�!3��`��?^�Q���$���0�Fv�=����v=���~���u�?B*�L�?f��&�����z����PX�ێ��T�:噗z����A�pz���T�ڱ���X�덼��s�����n?��dma�{��>��z��c##�*���ɡ-�F۹<�#�+BI�YbfA�5UP�O�7~���"|J�����3���~��F@��s���:�
�dj2����=�T���0à��QWQ�eYXw�O�Q^òRی��d~�����X;3>wW rI�����\�֒0ʮ�#t���k��U�����������ˆE��+(���~���9P�����֝�1|u�T;��7
7)@�1��~b��Rl�!�T�,�(�:Z��*��s�U� �e�Q��}�U�3�&G׷��Te���DH����QU]��� q�@�N�V`�3rF}O��8�G�h�� ���[9?�Z���q� ���\�ض��@:1�z|���Nǖ���J2�q��g<��@�"B����pW�W� �,�-�y#
۰��dTܵ["D�]ЈY��$�:��}jTm�*�v}�Vhs��n���
�n�Y��k9����jDJ԰�2&pq�@��Ҩ2�c�H<�`u�5t9P�C}�͏C�L�a��r{�*�L��
���Y
gs��@��.�)'ɵ�����q��'��rG�v����C0p8��Pܻ����;wfl�>���\.#��Lg ����o%
>�0���O��U�/�!L������UZX����w��*e�P���ad�f�[+�p����c9�=	J:!G*�H�{��b�Y�/@�z�
�m/�@�G�Xdu㹩I�7*Ӵ������p@��x���pe�;2J��K�F��'��ԓ��j�2cv7d�NrO��may��IH0e`m]���n��ҦoK
�D�6�?(ml���}���J	��t�q����V*'�hdg22�a�N��8$��#�֙��6��!@�yo�ϠeHK�,>b�X�.;s��ۀ?�aDJU����(�9�D?">	
�� �{��Y�"�
�NB�;s�#�ϥkɏ0lڎ2pASه��(��y��l�Lu��N[��IT$�X���$0=����6����:V����U�c1&��n�r�d�s�?v�©�ӯ�Z�J]�B��=���=�q������\��DjH������ǿ�a�+ <� u9���jB��',�(è�?�?�u,2�2��!h�`q�o
q�g�3֭{��V1���aK6���sO��#0�XY�<v9'�j#)Tt����7g��^�=�Pv����q�q�q���c%gr8��@a���$6�8a��
����jKp
4���7M��';=}��8�,J�t��6�?���zs�8��dU�2�F����8V$����9&�Ww*�Ќ)}��q��~�,����㓻�HP8�=�Ll�D|���{~Ui��n�H ��<zu�z��gr�D�Q�J�r�Nq�Gl�h�iޛ���Ni���X9wd����q���c$��6��uJ�”�/��8t8�q�s�1�ID"q*ɸ�c ��5"�ݹ]�0>m�uϹ�5vХ�D�C"���|�������\bg)ث���x`�FTds��Z�`�>§w�����G>���p�C}ܯO_��AS؝�Bs�t9��WkJWEK�$bW���dd��G�W���^?5#,v�s�qY�ݸ�Ts��s�~U4�X���H'�L>�ʆà��d��;~bGLc��ڠ�o6PQB��\�H�
�X�v��1������Ty�۴I�rO���nU=�w��
Ñ��p8�y㞝�)�6LR�WL�'`z�y�y�Zh��y�j��ˆ���8z��*���
D�� �q�oL�=��塴]�T�-U(��q�#9/N�N=�m��l�]�+�ml�69��ڣ�/B�˱A+��d���d��ҤF���.^Ll�2=A?_l1�-˛��9b�10WTA���������x$գ��yv~A 'c�#듎Է�	y����9^p>c���H���AW$&x�$s��m�*���.Fp^0GN�g�$Gq�7+���"r9�AV-b��)t]������:�#�*��,K݉�OO���ǛKgD�\�����ĺz�e]�("0�����$(F�J�P��B{S�ݞBY�n�����<�?Z��-\fv�%O/
�
��Q�S���+`�!w��r�CP�X�Fй�$��j�m�/���$�8�h��������'���A��v���$�H�N[�zt#b��� 玼��jbЛe�c�0I�!w��=�=�P�|�P�`��'Ea}@�a(����<㧵M ����#�OcQK�$[<}y<������*���,_9�ǡ8�[���'8��A����>1����Hm�sRZ�i��;���<����5���L�rNwzr?iʢKv(����G�Km`)�{�N�~BW#�R?N*KD_4�p�e�W{d��89?�3ץR�+���K2|�S�a��pFH�n8���}�@� g�<��1*eR5Xנ$/�'$��n1�i�Ev��x<8��4�KhY:�
����$�',8=:��)��v���|�L�u9���D?$�ܧ� ���T���G��Fr2:�Q&\QR�a���Nx�=����A��Я
{3��j~�A����U�/�v�:z�~~�B���?6󃞽�J��ұ� ��p1�ڣ~@A���O�!d����e���9����$�I�U`<���1�z�YіE'#�+���_�UX�v�	"l��yj���>>�ٛ�
��s��s�?C��(ǰ8����<�Ԏr[if�%�<g�3�����K�y�*4+D�3��!���1�fݍ��e�����9�%��)�FLJ��w#`�@���$1��y�;,�1(0�~zrx��<Tk����D%���>����Y�R�X�#���������N}�O�L���X�B$EV?x��g����v�H˅0s϶1S�������n[�L
��8�=�x5��fWQ�	��m8��s�:�u�F��V'hP	�(bH�?w8���=��Y��r���7��Q���!�w�Խ]�jvW9R���?N?�AV�%uA]�fۍ�I�'�2s�ވ�;�
�b�Uy��5S�<����^߁�vV1�rw'����pp~����w�H����f>�،���\%�t���;��x䟩��]7ٕ��|�O�{�OƖ��b[`S`�c<����?���r��i�����#�6��1׊�i6��	=��Z����&y+<�W{1��g�$�TA��w��	�$|���'�8��r���>E�H�'�{��J��6
 ?+d�1��OƳ�6Q�4�Q�`����{��V!`Q�1wQ�$��}���_�|��1�`W�F?=�H�B۷�GL�҅"\E��X?��h�N�`����>�4,F�$I�J�X��q�Ae��������Jn�<���U�MI�Й�3HPw��f��d(��rq��2�Y�3.�
��e n
Nz�(+"풹�P�G�8�NL�S_*����Nq��*J�ɰ�Q�������xf#''��U���ۑE��)E��BVA#)8<��`�����Ol�dܜ�n�1ܹ;"� ����Os���s/�BI@rs�H<q��~�j�mڮ�u3�q����R�P
ݲ3���{X��*^H����_�����K���9>��l�P2	'����j�Բ+��#�湞�iU´�nڠ���x�p4�'%�C�WQ�����:c�U���v����T�@U/�|���v�׵e���]Հ���Rp:q����H$J�8l7<����bQ�8a�'�)�,���FTf��:�nsC��VD�C�#K)3DZ�$���H8P�ݏ�>��G
�cd�O��<cN@#֠��J�m/������OQQF>dX���<�ק8=��6R�,J�+��V#dl$1r�?� ��OJ]�'�e�]��9�Npz�C׽<�"�p��!W|��y=~���$P�
�lj�p}���O�	6Kc���w�v�gw�?ʢ�	x�2�i'?ϥM|�i!�˂F+<���3�������$�9�x#‚��I>��bE~V6
����'�?���70X����H�S�֩w�H�QJ�,�r��U!˃�
��rþ}���&h�Lƹb�2�
�@{���R�O���Ny�sF<�!�6�.	�����q��d��I0cܠ"�8c����NCP��m�Za�19��7��#�Ӷ�P�ĸt�dO��	��=G�j�,4��%����N��rq��)�<��8��Gݽ��A��������"2��Q����\s���jQk$�Iq2H`��L�\����zT�W�fHl�Yev#
��r@\��y�pz��7�ᔒpdv�9�t�rh�c#td(�oۻ��[��s��QT��B�T`g��	��9�R@p�"��Yw(bA���}i-�˪#\9�	��1�휌g�CcH�od�s�ݛ����rA�O����?*���n��p��;s�1����Y�g��B����ߕ?��|�ypNr}}j[xVA1�E��;���O!�y��u�ߕ�����P��RT�*�8���v����J6%�"NP���ʒH#�߅�h��d¦�N�~@�#�'�8���Y��	ln#�����S�ef��aBl-�W?�N��ڍ�1��
��@�<�$!���;W��P�����#�UI'�q�z�y��4��q���'����Hɍdb\܌�0Gc�q�e�F��b�.Ӏ�8p�Vlu�O�����ahn�bgs#�Tf�I	;��g;}$����YD~R\U�##�9=�i�Cd[�Ui����ǧa��(�=�ȡ�v#�Q(��x8��mK#�' .9\�G~ߍ=c+1X�?=|d�q��zT����&o9�J��V��s��`��"���p�PĮA�^���~�֭ܵ�aY�B�F��G<�;s��㩪ws�8vv��<�rK89��8�W��2z���#��q�[�t����y1�c�b�C��U�́�rq�j�)�J�;O�*H�h���	d{���ұ*�Kl����yqlf�[�r;t��Ǧv
YH�#*q�w>�]��U�y8?(�=0:����je��hd�#n*!g��{v0��ҵ�c��H��rg�2�P�,#*22>R��%���3�P��r�g���_J�A�J��#�$�S���N=M:��%`Q@��rxP�~=GZ��!�H0u�Kaw/��\
Y	e�n� ��	�u<�$дx�P�)s��c�9�NA����5HU.5•pǕ��é?�1T� fS1+�����=3ק�z�Ii��9
���=v� ����4N#��Ҥl�\�����|�fϩ>���fH%��}����5H4/�D.I����1#9U8��n��Vo%.�C1�z����@��n;�Hc���>f|lt�U���p@���Ҧ�-"I$@�G.�ڻ��r��ϭh��z���N����99x�ϡ�eZ�@##�׎����q�7`�m��=:j��,	<��1��)9
+�\
�E������'���;H��cP��s�K���}���R�9��Fga�;d/$����8���K3m�e��<s�E�H"�ZBq�3�8'Ң� ��,	=����d^C�f���R��d}�
�"i6�W;#ă*�A s�S�X����
��'�>�=)��	J>�,�0ܜ��2X��z�L�vH� �����3��x�̅����t�-��6�����R�Z�N�i��I�Cdr�z�]�[�4(`�!�Fc�NO�
�Oj��ɗ[p�	��	�^�j&M�UG�ݸ�<;v�hh<�H��1�N�G`���jW�o!fUB�h,�u98 t�q�aJ�ҠG�S������F)&
��0ከl�˱�5<���}�0�w �0����J�’#���P܁����G����!����g'k$`
�u
�� �:����*�!Kc=y$r{��TnK��m�$���
:�}3�<��V�#��H��q�rq��$8?2���x�@�q����HG|E)l�X6��R>���h.]�%+�H8GfR�����<pwu��hEj�/��
����@'�MEo>P���Ȍ�
X�l.zq�9��6��,%���� =G��;tE&sNM;#�X��(3��,Od��}9�&"������q�ׯOP�h��]��]�M��;��b8�<��8�@�$h�(��( �
rF�8�N0pH��g;��)��#�l|��h� c���D�C䘖6i\�ߝ�F \��p=	����yc�
F͞�$w��9�&���yM��V��p8�<��S�3*�L�iJ��&	8�8�8� ��R�6�`�/#�FjIT8m��l@�
n����Q#�*�yRpʹ���ǁ�YK�V4N��Ird���@m<z�O��]������T#[��HP~P[�a�9��G��J���8
3�v���Տ��q�B�a����z�Y��O2y�� �� �<�''����.�[*6w�t���v�	a9���l�9�r;T�3�,�F�D�# c�ݕ�?�J��	؁�
!g`�.�y�מ	�F8,H�삽�����ԕ�aIe�;`�&�XK��nNY�����H<���Bz�"ޅղCmI�3�ӯ>��;J7)�^>��K�b%v�
��
v�v�s�&�.�)����T�B�:�+H�/��u���>��jxU7$`28�w�0���rq�%����r�S���m�p*A_u9�w�$0`�w
��9�_N�i��BB��<3(��3��n�~��p�~d��_��<Ċη�]T�@�f��}�Ұ|�IE�F���%G@Hy@<��HD7���1�a��N~�s�GS�׬O��<�T�[�X�~���X���U�Z��̈ (Ł!1�T��A ���Vu�j�����Fy�Ol{����)��~b�$�?7O�d�!�JH29R�~s��<�~���}�Oi+�����1�5�,yts&TFA�v�A�'�^��Զ.�T��Y�pNq��>�4�
�TLpFݥyR�@�<���Z�A²���%���=qN����GfBw�nb9��m���#HQ
y��<�?t�B�̅T����{⪦@_
9'���<�u$*�\����#��S���&s"E���i�A*8�<�=n�
\�!���'ڬʒ@����� 7��r��՘*�V5�K	��$�$c>�W�8b�0Ooa�4��k/�p�����t��̻�8Ǡ��֤E��)��9�X��V�6��E‡8�:g9��
Og0TF��[󐧡��W	t!��C�ah�YD�2~�Y��99���RYv;&GT�|�n��$��<օ�����*��0%��~l��wvsY�)W_-%,��f�8��ך��ڢ����m��`�0r�y�4��뗴���W�����F�p���8䁒$UR���i!���ۑ���ҧ��gT���%������q늕�
���̠��!�zP?�Ӷmm��w��:����sӚ)cUlmW�<2W9����$�Y��,�
Cu?L��Ԛ&�'��m�bl>��b��s��\�ך��(B��iFA�98�۷#��2��X���x�1�Ҭ!1̤n,wy���>��T����KH���Y��pOnQ�9<ԓ��F�ʬ�"����On:��P��e� ��~�=q�R���Fpw8X|��{s���>[��L1L�Jmݵ�#�	���S�|�!T�Nrq�@��N��X��w��acR��`��x��Qx�2�2T����g�C)j*��#�Y�_�p.ޙ��g�h
���]�g���*r:�;T�Z&f�;q��>�w�-̂HB��A� d�0W�<u��$�f28щ;w���==vՄ��X��G�s�R�#nrï9�����m����h<��A�<�Ǩ�jpJȻA#���B:�æ}�N�ʄ2��7��p`r}z�>���"H�%�2Aʁ��0?�^�����e��bˉ��W�p�A?6��ӟSU�I
l��h�z�qӿ�T��U�?ΊU^a�+��r@�#8�d���q�v3���ݞ;dR\�����f�6��rOS�׽>�G������	�;����"^�v��r�)���ߏqH�P���s��v���FBꪽzs���>��Z2 b9P
�r:���?:��B@Ҡm��J���OWy#�ewe��
�6��1���Q~T
 ����l�jyF�
��Dr>�˷�t�kEb��Qn`� ;���y����J��(�D��[�n����8�im�,�h�]����H��=���%�h]��ݶŻs�����>��'b��IICI��S�8%q� ��v5>�ng��L�b@�j�[���S�)-���Edd��U��*�R1��+ӽU��1%����}�q���ZD�� ���H#���9`�q���1=��`���8x���׏a�R<X�U�I��X�a��;�)�M!�dY$�r̥�R0s��E;��I�º�r���1V$f�,c/2��VNv����v"�rm\���vO�HYB8eW-��͎{�1��=�u��mX�E^�\�gf�Kr���5>y��g#�@���瞿LU��9n��8���L�lH�g��#8�x#=�3�^I=ؖLo�J�z1��5��vb�s/˜�x�ѷ*dpTm
���
��竱ba��]8dᇿ�����m`0��Z03.v�U����T��!��0p_��o-�x;#V,�(S��Sڅw���q��j ��l����q�r=1�?�Vq��oAn��դY
�,���g�����]�cc pO��;�?2�;�;���:��H*GC�#SSr�7bX�CL����K#t(
�$t>�K"]Ϗ��{�Gs�Hfʐ#��R����D��$�#FcU(��������M!1ڔ�*zg����M@�3ʠ1�6wc�'���#�r>�9UU�s�;�0g��2��.3�{S�UU+  �B�Iq����9�U�V�*Y2A#�~X#�3.L��q鏮9��r�Qq��p���r&�+�m�@��Oҥ�7�wnݐW��9��m�3��8'��^���F�^�{�j�#9�ڒ�K�4�W��`q뎵HG��7���߯C�
���!�T�����_�T7�:1ϣz�Mل[���5R�q���{� ~X㚑��1�7岭�1�0}��sˊ��ѐ�Ł��#���ӗ2�}�H�''%�מNO�@�q'�%�
9?�J�ġCdc9m��c���:�Z�}ўW;��pp@?��I��2�?����ɩ��p��c��&	�P(U��Rz��=���l��D�~�a�pq�g�TQ�!�9�g�y�wJ���n�#<gi`I�F:Ӡ�sSd7=D�1�"�$T?ss�s����S��˰9i�s�A�w|�S���
2�T12n@����}*4�F��`�1���	6����n�
D�B���HP��@/��;����T��t~n���G'�<w$bIO����4D���#�$�e�I��1��G,�i]�b���>�����S�R�k�̸V-�9n���Z��G�I�1�(������@�@���"2]�l)���]}kX��L�QU,pG<��4�%-��w=x�#�žgcوP�傈��7c����M��3��������l����|���8��i�T�*I=��9�y�Xg�V��=NO�i`�>7:�܏�֒c��t�Wk�c��?^��D����R�uw?�*\� ��x.=_ГR$��`.La��О�1��D�M`�im�[i`�6q�9ǽ6s��l��=}	'�4�'�}�xe��>��jE#���ܒ6�ߌ։��f0\J�,@��r�X�b	��x��ޕF-|�S��4{C���x\��Deº�����3�l`��B8'�K�ԋ|�G�����)�$`w�9w�j�O��+��`G�iҤ�
9n�����=:g�؁'k�0	��ןz�,D$j>�I �$
���<ܬ��4S��Ly��לEZX�G�[���s�|�.Ϝ�f,Nz*|_ `ɹN����ޅ������X��#��1��V#�xa����;�IK�������Wa1���Q���4m�8�#ۥi�Jv)�	��͟�\c��=���r�m�*	$dc���֣�d�6�o\g������偆
���FF3���-��m��C�ͣj�q�3���C-�̨�Ar�~�Q�5ES|� 0PwdOƟ1*���W��������q�"6	B�<w?Z����*���1���ӜՍŔ�bd�ʓ�ǯ$�v��T�0��c�ܪn������<�f�Ө�4�B�:��'��$�p�8����<w�Whb�4��1��Xz�����U™dO."�g�b�q����۱��^����N�#4���%�Žz�ϵY܆-�[;>���v�q�g�i�m"I#$�K�=�N9 v�8�֤-� nݝ��6�����ӜT�\�����3	
H��
�dp���{�ש�^�5)����b�1���
K��m$�t�G<�����>��I ���`��}��:�M��\G�c��z2��<�{Sl�3���r�c�ԓ�V-�2D�	�y9%�1���=J�F��nU��>�?�\L���4�ο9����O�h\�VˉU�a��?*� {���`��qUs�C�}��D�3rG=����qڴ��DhJJJ|ۀ�g#?�*k�S�X�y鑑��3�j&Q��;F��-�=�U���P6�!|��q��(���MO�w�pzg�����N��G���I'���=1�*�q���9����b���41�qD�x�s�[����|�I�4_�B�ԓ���=��őr<��C���ަfT`@��:d�_�T�ڻ@�=�~�����I��������I�Ǿ*Kx�afV���~}��!0��w	3���0{T��)ǘx�a��u�֥h�k�dU�@e��=?��*ItDC�0]�[p\�=2H��S&�\wL~c��4�q��"�]����x=y� {��Tet0\J��.a�*�©$d��g*3�R��$�1�?�϶F~��t[��Q	�I��*��p	�I z
�~�'o9�3���5�-�,����+6��`@'�s����E�ܩQ����8�ue&�2�r�p1�F{�A�Cj�cK�H�3�r}~Rz��Cw.(��2�#<u-�{�������rA\w�1��V7����<���*��A}3����Cv-0��u#i)�G�	�֥�]򩙙�9��܁�Q��b�BH�s�c�R�B�bg
�X�|c�����C���%A�O\�?��U������r�c=���V���`E[8�5' >ln�pi����d�����=	���f	�R�.�@���9�H���-�=�Rr�p���S�
�܎�GN{�f�ɑZ!���I��0:u�+6�U�f?�y�7��q���{f�����&0�e;~c�m�^��8���c>f��UX����H�9|�*7�D���%�bܜ�Y}z8�+;�h��n壒F�UbV9����q���r��y7����@q�����M�*�����7)\��֧�
s,P	#����v��Ny�O?J�-4a{���|+��Ns�P1�q�E5aR���ݠ1
�O$G^�;ӧ�%l�'8�_�jtp��F�H��\z�n�_=�%�Ctd��$��^���z�N��d#f��[
��p	��H��ڔ���j�N�)��^&q*Ȅ��[<���y��+�7��=���.��yS���}} h���q�s����W
��|ϜÃ��?@y
�Q����'���h;�&��c�3v�����f��O�,xg�q���l�,X
6/<��™3��;���6ՈI�H��E!�K$K�Bq���X~t�A.�aY!�$q���S���?/~q�H�8S���\/�9-���0��G9������U�/�c$��3�[�6��N��?�Q�T�-����Q���?*�7;�̌w2��3ןƅ"\D�C,2,�̀���6��{��qV"s�O��GRq֫�F)O9�}�>���9d�ة#�!y��j㦤Mh9�l��Sp��=z�@�-�@��Gך�p�w�̹\��D�q�y�Q'v8h�ZB��r�����W*�#>���7Ĩ�N��t���`>^9��&��b��T�%Q�~\v���F���sң�Li���j͚|�ׅ�N��늘��)Y`_.0򜳝�_ i������&5qЂHnrx�{U��FHۅ7����U.H�
��T�ϯ^��T���7y�re8��I��FOU3����O8��r�
��'�޿�jlˌ7E w�z�n�aPrŃ`�3����U�-n>r���|�F7��\��*�<N�L��y������H$��=���%��НՃs�,����z�^�Oz�pd.3��3�jL��
�Ł�8�NYeX3+_��g�4��Ց;C
۴�'�p�2I�ګ������T�����f6�ć�
��*q�C�8�*�DS�,|�����K׽D3�S��.8�x#�j[+B[x�a.�$��>�s�p��8�zhV������c��֦�[tH�#g�3	K�dF:�\���Ki�Y%
�Fʯp�>��=���%��L����I�̀�8;�QL�S�;@Y2��r@�N3ފ�$SS�G"�S�)����1��}��#>�pr2A@1��###�9�X����l\&A�O�aU�>�����yB!F�,f C6�Fv�1�\��u��!��X�r��
嘪�ԁ�s��8������a��@q�21�Q������@���$�s�X�$���q��L��\\+1�����dA����f�Y�&������;�V�<?4�X��pq��OLc=OJ�T�7`.s�@��U�!#ĸ�z����^���fD��m��i�ȬNN�@#`H$��co"��R�&>P�z���t�U��%]�2P��ݻT���OƪJ�l?#|�$s��T�b�H�+���a�K)Q��z���2@�F	`	䜷מy����/N����=�=?
I��gd��p���O��MI���@�{�7(' g��I��M��z���RF�0��3�d�G�DXyTU�N�px���T��d<w�T�KRI �rz{P�˅��q9�W.��O�j��}r�d��;�>R;u�R�����2pv��c��W���h�e�.1f���9�#��t��#�h�.�"�	;W#$��zz�]~��N�b�X�a�#1�Ul���$8A�p@��ר�N��rG��%0�\eq���k�)3�m#�a+������;Ku����U����0�r
��H�����q�x��
��v`��:�֢�e)�hAq�����$�#i�{�U6�hݑ��
���_� q�W��Ŋ�#v��n�[�O|�FO\c���I]�BZ��	0HϽ$m��P�;B�A 㟭;
���N)��;��ڕ�Ze�a������1��z�M�Q���ls����s��n� F�3��-#���zk�Xvg�~^��O�7��$�����M�{����Mʊ�Gͻ��;~F���Ks##cFO�pAA��5���X�p�	l
��ߏ�S��şa%3�8={UwPCH�	l���y��S�`�
���_J��,��Y' w#����b��b�+�s6й_0c���8��8���~\�3��~�Q�'c�5kS��2s��r��wc���8��
ޣք
C
�t�?�,q��W��˜�׷j�1�Q#��뜀N;�<�ʋ��7�I.�nU��ч�#�J�?xc�lu��<w,���#�d��b�)�#i ��p:⇵��) }�q��g��G|�~�(Fʬe8b~^Wp=F?�e~|�������ߜzU�n��	Y#�E��t
�0#9�pI#�)(�|�U�n#%�n�''����ϵW
���s`A�0q�N�ՙ���~P
��zb�������zq�a3x���Ģ2	�����yS�J�6�������JU��K���d�ǧ?�=��F����1�Lw�y�+��a�G-�F��Ϊ���c���R3(���p���g��>�(�t����`a�����o+�?<�H`��a�
��2�c�gܠ>X���Ԟ}rI<���H��*7/�D�<t�zRDm�xRzg�rEJM�8P��3�$:��
��1W+�����A��O�7T�	�� ����犞��{�Ӥ�Q�����^܌�f����-�`�(�?Z���l#.@�wc�N3��{U�+H���m8U�nL��C�l��@_�
.�$��l��\s��qZ-��++ɵ��8f���ӎs�q���#4V�d�(9���U�)h���B���;;c=�j��$Yo���\�j�U�G9'�v4$)H�j��0K0�9���@w6r��',I9=�=:)��8 N㌜V�V�H�L<����l���$q�z�X�ɭL�b��hN�<�1������Y��Ν+���ğO�>Xp��D�!s���x�tc˸�R��Hn������4C5�FF�|�]~e<�����9̮
�U#����J�*LH��-�@�I��I�׵Rs�v�6���oZ����YWh`~q���B�t�$��{~�ЀB����׎ث��x�X�ýTw&N��D�D��Nz������>i(rz�s�����Q:�,F�`3����K�N�P�q��j��
�!����\O5˴��!y_�bܒs�N�58X�H_�=��:��]�g!x)J���������؁0��PFy g��l�h�(wn*��wNq�%�		
����퓌�#�
6)W����89�����zu��I f*ћ��<�<u��U�n�	8ϒ�:�]��u�`�����Lط�d����� �Frru�qI#Fp���:�T�*:�=X�9�w��s���bwm�pq�zT�sH��>K�1A�n�ݳ��Gn�M;��o1�۸�����X����F{��§I�ǻ%�g����90i�Yd�̔�s��|�\IoPF�i,N����G,[�*ܕ$�ds��s���:	NrA�?��`dVwݐ�YH$n�����	dU�m��nrs��N1��9玵%ҡ��r1\`�:�I�	���UY�DFG�������}7g��,�G���|�{�{�U{�wyH)�A��֧�`&���r�Jmá��;�9l���h��`���S[�,��z�}��
"Jڐ�$`�$�� /�U�w{=�i����úN��0��q���9�c�L3��� �c��]���$�wH\^޹?�U�Uj���e@d�p�}{�A[$�ߵ\��@v8\���:dQnjH��8��?*N����z�����9ؤn�� t�v��W �d���T\|�@�+��ҽ�����xз�K,R�yGð�����z�Զ�]���� �$�����Cp��V#�[=��v�)n�v)J�&PP��ܞ�{qL�Ad8����t�V�6	R��F:r9��YK,F�Wt���݁�N:��}}h�cu�͸FHU\d�rs��N�<u��r�
C���\�co^�5�w�p��� �ޛ��,��pJ9BAQ�;�Ի��0�.A ��8#8>�,\����pr��#���D��Xg�~H?����G1.�7�fDB>|s�A<�#�CL��NUK4xVV#݌���U�����緅l�m��'8
�CӜ��x+�i�U/aH��t���2
�*��'?�D�KC>8ث���R`��1�q��H<t�?
:ɻh��N?#S�-�Qʞ�d?��(E\I%c�H�aFҨ�:d����ҫ�o8��q�G5;d���'� 
��h���"7���r��ҕƥq�Q���I�d_1Y�7��pr*����p	�@��
�ʆ]���:��5���J���z�R��U���_0�Te�#��֙�b����(���w�t���-����۸�5��R����8��ǽd�f��j:�y;8�`z{��V�n.�)|�9ǡ�;�uF8�ecm��ɝ��VŽ�H�D�H��	^<6J�������1�3�WB��?��r=:|Hp?/M�����I�!��P@�ߏLt槊��� ���� ޸<|����峹WV$~J�I��0�F�d~��FUc��	�������|����c�9�sւ���6��;z�I���C|�T��$`���*ͤ
w(UR�e����N06�⡙$@�ɰt�z����S�TE�� W��z9E}�w��D'n�\�_�Z��6�`�*�R�`����	4�*�68$8'�0j����@�@�ȸj5�6#���C�s���8����
y9�J�Iݭ���72�b'>�(��:���Q<��'�V�c���{�W�N{qQ�D�2���sܓ��*Z9%V�NJ�ïb?´���4e6�q�`u8�!Q���W�b?����x�
���yAվFm�*�qU�Cl'����zf�&,]ٌ�zj�v�68#���*��	�09WA'�GqJ½�V�yO�Jm!H9a�����G�LOˆe!��OO����J�a;F�Tu䑜�q�T�&2�>c�#�F��x�x&�J��N����Ns����j�`d�
���nw�U%�����ؤ���������+s��O?�\v�ޮ1�̪T�P���l�v.0�Pܦ�S%Ѵ��9#���S�),���!�W�s��A8
�Ċ��io�r~C�x����2�&���e���9!����Ӓ�u�X��3��nx�9��r�*H��z�<��qP�����Oeo-��G2S�	��E��4���)U$�M1����H#���X;#?uS�t���&���R�
1�h*Ln��HR���`?Z��N�t��=�h]�|�W(r�]�;{��ܑ$�j"F�HO�����9<m�ny �*�;�'�IV�J�3�=�{�T2���^�����c�1�b8�� ��9���!�$h:���䎤�w�j�Q���ΎȪ�a�1�O�zp*���V]�(\��
��ִ������GLw$�y䏩��Պ$��a�C���N8�ǽ9���[�X�qҒ�/6I!�7w`q�,��Ӷx�2�pC��xɑ�GC�[## �H���b��w�;��ys�rI<A�zG�i��d>aR��G����	�y�)ἒ�8�G;3+�9ہ�ӌ����K�'��`���c�����0C#���,c�s�(�8���V��,�o�	&;����� 1���K*F�1����q�>���R�ƴrNB���glp6��$~�H��7®�9$pu�9�:�!9T`����3�r9��7{(b\��I� ���В:�����F�s�C��=9�$�ry�R�/-�!O:yՉP�G�'���j�[�l66��玔�@�C�X��c�R;�]� ����c�9�R"�p�~�2�9$w�S�w	�G�OA���#<c���6	����-���9�y���T�W(��gs�����}�u��a���,e��2@.�3�z�Nzri�fh����2��T�pX�Ϧ{��!
@�v�8���`x�L���pLl�,��Tج�X�g���};R�Fp�#��#��E:bi��>l�H��N���kg�����2�
��\����mhCw!�ug�}Җ��	����M�"�d����;{g�9�rqNL=��a�#>Dj~r#h��R�*���ٱB�I�c��c���ۍ�J#9@����8�x2����xmد�.P��"�l0r^8���1�<�ь�6��fH<���(ZEV����w����翨�i����%�1F�3�.���y� t#?\�i�^"I6;�G8$)�;	�	����T���.�<�m#L�J�s�.	:`�}�%�Kx�w1E)fR�����^OͅPG�����s8$���<A�\`g<�|f�["5��9c*�J�PI��g�x�$(���ĭ��m�#9�q�<AS������*�@��8n!�� �}��H�"��P����9^9=;���m@�ym�����w��9t��GQW.�������y��Wݒ8�<�#$��T\�H�d��O�x'�CW,�KH|�>�2Jd�,�u�.����UQ\�bJ��g��� 'VV��*��A�1��c�ަRw��n|����a׃�y<��W)�ݮ�m%@@9,q�A�}�c�:��Ơ|��c�����&B۝HH�q��*$ĩo~���j�&䊈�!#p�g������jS�cF 3`�8��\��A�f�I1����8�$m�G�=��0,�Ao1����������T�r(����]�pq�^9})�yL��^B�Ai`�J�`�}���Q�:��I��X %Fs�?w��MDs�q���$���o���w�D�\O���$eFq���$�N2%�M̮X�G
q�N�N{梁Y����c���^��*̙����?�����Bm��L��ZZ�qq�N̨@*	B��w���Nw1�Tv�&I3�H�ǂNi��3�2�C�*J�U�Fzt^����QH�IѴR&w!�89u��5+�!�R0P�_p�H�pNr3��T�|���$�'$��dq���Mz�Yd(U�"�����w'ڐ3#,V6R�4c?�*=y�O^�6��-
��\�������S�à尤������RVX�Sj���}���ҫ�v��`=��I�n���o���T���:�D�7����(c�8�_L�� �cU��s���S���	�a���`�=��61L��~a$�ʤ��g��j%�IRC��ps��L�ӷ9��~bd,2z��C��3P��b��d�l�϶M);�tYT��Lr�p~s���A4��>�Ӆ�#��s��Zs�ʻvm�K"��H��=9��Il����H�W*$���I�i�A\Oql�o-2H/������ۡH-���0	'<����g��B�#H�
�x�	${V%�c��Xuz��1�9�<���iivg)��^[gάWo<x�c��֫�E	W3��)��_���,T휀H���ŖL0+��}�q.�$$3��0z��ߧ�ZF�ɻ��V�g��7�fNO���隞
���&�#6���)���J�ZQd�r���p�m����đڥB��X��?���z�MR�&M�Co�"�;(�ߥi���ޤ�L�P���=�*����Y�
�f�H�R=�;��39W8-����n��h�攵3���o�d'-��y�?�L�Y��1�N�Il�:`��whP,�nh��`�^;}8��*5;�yf0�)�ˑ�5�sXH�<[�Fق�?A�_�j��'U�G��%.�ٲl�x���~��2H�.�I*wy?w�9�&�$��Vʕ/Ĝpq�.zq�T�SV݊rBs�A+��$�����Im•?7A��9�2r8���W&u��gc.I}I�ܑ��S�C4�!g�'
��H���b�4��QN$������e*�U`Ýɓ�`��Q�y�*��&p70�s߮}��^�˴p�;E|���y�~�d���t7�<�P6�}N[q9��q���Ȝ%C;
�c?�3����9���(
.<�2��׮Ԋl@�	e@P��I�8�����CmC���d0y���P݊�VVX��p$P��<��Ƿ�4�ie�2~b�=�.{��#w�2��!+���i��>R썎��I�Z�3ؤ`�bq&
4��2�#�o^��(��l�h��� �T��d�H��h�^��h!FN:�=G�g�T�ۀ���*�m<��q���i�V2ϐz�$���[G�[t��Xʫ��z����2��/04K���N�X�:g���]�9dR�CC�T�y����3ۭ[�NH.��!�)��GC�MP����R������y���յ�
"C�vS�䃁��کL�J-��Y�����Y>R��$��9��g�̤�&�0ܯA���JӸ�h�ȦA,i�WaV�k�'�L�^5U�x4�N����a��s�8�<����W�*.cY�Hbf��h�Q���q��^�����v,�:�JH��ݐF�6�	�{.Q������öG��T1��px�����݋D�[|���8P���=�8�4�y>�|�H�#��u�x� ��`cDQ�S��G�Wx�W����@I��\$j��$`�G��O@�g��!YX0(H1s��2}�	�H˘8�~LI��ّ�C���@��$�I�;R2���9�'p8�5�O�ĸ��@-��zs�3�?#�CV@:t1�V`�P`(�����9����mW,�`q���'��R�R��͹�y��y�lӧ*�;C��0�O�� v�A�M����H�Hd{�U&6��!K+����Ԑ{��Skd��f�-�18߰��3,�`�&�ˌm9����A�=��=�
�>e�\�<�~��M����6��U���ya�@ �g8>��S�(ͫLZD|R��'�9�q�}���Ȃ%Fr�ۑ����ru�8����0W�󼌗Vp�n�v��y��sZ�S6gJ6�9ʶр�㧮�{�N���yE$��Tq� ���ՉR7�5�1�Y��F=@���S��䕊V���2@AJ�*Rs��8�q��[���(��˻!Nyy��9�9�Iq
Z�셙����rs�6N�N��$���mJ(�I�����^�ȣi�Q�A9���*���<��)o1TJ�O�zw�M5�'Б3��#@��##'����j��cfU
6�烓��`�ӭC#$��`�������ӽ=��Q��h�a� $�FF����y�i0v�\oT�J���=[<A����#ovg��	'�9�9��ju�0���Wp�s�s�co͐L���{�t�H�(J����rpA���V�ewX��7YCA�z.{��8�)cݶ������c�0H�4됥��#ܖ*O�ߓ߁���Z�+V�4S&�X�r�s��Ǡ��Q��NX��Nv�c�q�1������ �59���*��d�2L�2
�	���8�8��>���!r
��q�I����ސ�+�"0����G�*���̾SmV���RB:c�94�\���X=Wq�G��~���f+p02{��1@.���߹�����<��q�S�;F߼�G��n<�6��3�ǵW�6[vN0,F�=�P��i	�=s�zu��i�!��"F��*ʬ���}T��h�*��v�	�d�?ǥL�w�s��nwۖ<�	�'��Deٶ)*v1ʏ�|�>�v�\�1D1�L��.��=9�g8�R��Kv'pu�`�7r0rG<�✪Z|�#E+�Q�gwb����~��{���ym��//�p�T������w'���%����巚ij;~^8�����������e�p8���rx��*5�H�,E�@�@���v�Fzd���nQ�(��n�y�a=2��wi�2D��탴���jҬ2D��&>`Ã�0�Kv9��CTY�b��FK���1Wbr���#9=��l��Z���i� �X�V2�-�XUy�霓��U]�#���zt��>��j�q�������8��g��~�Q�Q�H
���鞕-ب�K>���(�;H�
G��<��'��Z�6�M�2�	2*�6:{v�
�����I1�H���`6T����N�0�9��*�S��H��PF��u s�G}ޘ�#�r����Nx�<�",H�Y���)�%��>�����QHN�f8\n�L
X[ mǿ_δFrW.F�+�U.�uT�}*��{<�}�%9^s�c���>90��6і��Ҵ-��E�
�b�:�����ܐ���y�[m�+(lc�`�<��zU�D	b(��ʸ�{�ɘ����p�{�?>q�W���L�9L���G>�Ғh�Qf$�������V#�@��q?��V�|�bh_w��|�^3��<v�j�*�hǛ�w��.˴p9��f_5�^�3!Y-+�pwA��y�f-�� ����׽^ ��*�,Aa�8��2�\���l�=jd�͡-�	��(8�}���U�.�v�8#�O?L���H��M��\`[ʕ�h�aX.�(%��� ���TSƟg�\�7}�6�=~��v9�\B6�.�q=��jdϹ�zs߭>���D�#vH�0p:�t����i�tgV*Uy
��{�PM%�R�6�Q�1�
��*�Ec&l��2�D�h�A8 ���jͬr��UÑ���pyQ�*�r��׷�`�u+0 8�pMTA�TyO�?+aN��\���85-ݿ�Z7$2ȥ�k(a�
�9^@����ӎ��7`�{sN*��Fr	$`}?�j�����T�ǞN2�ϧ~��Jڝ�zs�M,k!�l�ï�R�ba�����8�Ҫ͎���/9#�J�,n`���$���H��a�3ry��ÏO֜�ه��r���=�c��ܿ؄8v��ػ�c�����(z��ԧ��j�@
q�pr$��U���A�OR�z�)H�8�99�>�?Z�.�p�Mr3�Ʈc�
�<c�4�a�����Y���s����H)у�
��'��J���c������㏭D���K|��q�?>}+	��Y�M�.>�\��xl���9!�w.W����h]���X�tr���~�:��r��3��^��)XKxe��|�����`Kv���O���Y��%��O��T�*�r�pqܑ�r=)���؆�ٹ�;�y=<�$ѝ���;�>�+�H�~��9b�����p:�}y�Dn	9u���>3�d09��֤_�]ռ��;2pq֕j�؎�����+�|�<�Z�!I�h�7i$R�H6{u�q@25`��X�0x��V
$FbX|�sԌ�l��M�[��˽�n/ܬ{Tg���a�3�rN*&`��G�FH;w|��g끑�'5��ͽJ��Ʃ��B�y�'�{�~B�JZ;(��C~8��Q��g�̻� N1V��D@� El��q<c�:}NO�8�JDV���`r:�}h�
	�\���9�=�,���.�P1����Z+l&�%��߸+���Ƕz��8�h��2r��-,Q�0X��1��޾��"�v�|�G;7mq�;ԯ��%�\*�bv��>��Ij�n��(Tb@'�#���GJ9Z�h�=��J�K��V*p��pjfD���t���Pp��J��v��u㩪��.ѻ�v`/��=(B�c�/
�dmq�������,�A�~}�UbR��2s�{���(��l>f8���$�\�EF�"P	0�Sר�iד�L�Y�#����A�?�*��\��`;/J4��$�8�{��֛����ہ���;�~?���$`!�#�ӯzK�i�σ�v��_֧�Bb��#�w�l��]wyl�	���ǿ9�)���s;���^�3��:���P�nF,F�F���:�`�ӥI���(8S��dR����.~I�7ɴ�m8 ��z���j�<����d���`q�'�^���Է��(�y�@����e0��11�q��j^���X�h2�a�qf!�@��c�����ҐM/��s���<�۳צ:��C�Wo
�'���g��d����=ihN��_-�r<��0������4�s�	|�s�
�'i�J�8�+���}z������*�J��qH�x���h>fZ�p�w�xQ���>������ݢ��$�Lč����zu=j/2�1j�U\,�9��7�?SD ��T���)a��Ҫ�!����h\m����Ђ��1��Oך��@�X����'sd�<��H@ޏ�������L�+�?E`Ÿ��?�#��mJ�k�d��Ē��䑾�Bs�{{zw��WhtaF�YA+�9?�j����?O����n�U^�nj�4ds���U�31#�ί&�F�#-���9��l�X�z��3�py��Ʉ��D��B6�+��}��&w�%rv20M)Ж��rA��қ�UX�\/E�>jW7C�ygX�����9�1�RH#L���G�ۻ��c=M�U�U�2���`G?S�8��)n�4�6}�Ź�t���zV�s�أ8"Y���98�	�:Rv��ra�r:s���m!b�!�n'����z�S����DC�۹�.{���g������λBN�Aݑ�������zj1�
x�8E�a��\b�rY��) �q���?�z1�7B̛�r��xw��K�q}�;�Q��!�
���q횱¨��$e�Lg��9>��,��966��GOl�S�����Y'b�lcp�ӓ��
E���v`��6���6?c���We��⵵#H�(��F<�3�<��@ �=j��
A:ƈ�$d�Q�+�8�����Q&ReѼ�����J"2ʬ	��1u�=:RF�d�@�:����!X�A�N�@�������Gf�U�B��1�8�`g�=��9C!%���u,��ipNGa���1�h,��ٸw�8�
�\i܎�`���H9l��T�1���c��veF�%xQ���n�D�㑏��oJ��2��ǘ�7�S��q�A$w)�Tcq��9!�3��8={ԧj��O#�}GQ�4�ޠ�#��1��_�Ud�6OBK}��idc���\��]`O�X�I�pN8�ӎSX�H%��O��[
t��+�ѫFXF1,K��ߘ�	�:ue�Eʕ �#<t�#R>c�#��s�Zz���06�^�50���F�d$�`�`����=*���-�B9�+��nP�`��n���U�f <�����ڬ<��q��P/~� c��jO-�4†L����{�ކ���Ev-��w�j坷�]�!V0�8$�%GV9�Rep�_��8�J�Dl�'���1��oA�D������ʯ�=3ڙw�c����8�������U!+Ѓ�`g�EWVs|��t�ظ�F��ʄ������F@�	���O�;��5,�<�§��E�K	�$��>��ƽ��v�.0N=���1�m�-��!��=~���c	�v!B�=K9� r�����'#�u?�&Sc���!�]�g<�NMV�vb����g����ӑVLo��P)&Hv^@�A*���S��u�%1d!�8
�H"�b'`,0z�=�?�ҏ��y<����l��asX�~^�G����q����s�T���8a��H���B�Tc���8<��Z�\��#��1;�'+���<u�J�ɻ��`�m�q�܎G����
}��A��E+�GR:[_:7�f�P��n;���c�Vd�YT1�%?&ߛh���j��K�N�݌۝�"!0�=s�Ex��R	R���u�#��S��о@�:��8#ב��^�lV̉6��-�����q��ҢLҙJX�N�F�
���A��֪�Hn�h�3ɒ̄�bY�q����֩��8c��<���T�����4��Z�&0R?��S���Ns�P��r��9�q���Jd���;X����}}�c�$r�H<����
$�T�@=��C��ČCnl����N{�d��?x�^��.Ř�̭<I#�YHO���i�9S�*6]�
Hʡ� ��a�<sU˝�ýH���)�#����y�gԤi�^�-g��0��˸�'%A
����� �ʌ�6GLD�&�6���眰#���QA�)x���B�d��<w��TD�Q�rNs�I��?�k9�셝Y�$(���לt�E0�d� 0���9ǽ��H�e�F`cTw=s�a�WK��P�EKym��7�ۉ|���lq�s�KH�ȧ#�<>�t~F�Cu40��_����1��pz��:R�~$9ݭ�$�Ɵ2�.�`{8���z�J�W%F[!p��r~�=��]�Q4I/�G�7HrJs�0H�s��$���8s�Ө�=1ڢ�W�PA;jX��ly��a���#���#�}iɳ�c�;Y@�o��U�!A)|-�_n�u���$�	ʬe�1(�϶O��8����I,{�u�2v�©q���Nx�o$C)a�>AEb����s��53[�q�6��:��)�rE�`�x��c&1�'���œ����Ɣ���
�E:����l�z�x�=�8ȩe���͋H�8�?x�X�A���ǜ�rj)&�#v�L��z��Fwd�F8����O�Fy��{�"�(b��~x�g>��~5<�yH�f
�H�� ���ev2�fN2NO��H��[n+��5�3c\�ɵ���ˌ��b��!�R�=*022I韮}��ʦTf����@�RFm���b�$s�x�V�e�
�#�����I*��j� n��g4�݋p2���0;FG�W�bŶ�E'kt'�[��l
�����8���V���3ڶ���j����)�Ce|��z������~�+����ӯX��b�����J�7n�[v�+�U)�fg��&XG Fr�]���=~��	b�8�W��$+���m��pzu�������	�@�s��Iۡ�Vܭ�/�B{����L�����V	ڠ��
}���l�$s�9�:7Ҥ�KK��b�f�xݑ�8댌�H����#ӥZ,��p�u�jK�ry��'�b���?0$W���NhFbR@��pEw�ޛqf%�Y$
�͞�^
��5*1��@db��$���G$Ex�#�pz�J͢�+�>s�v���C})b��eJ7_L��RH6ݠll;�uǾ)�,��F�Ė9��q�q��桚���>V2G~�4N
�x�~��U�� �`�R��Fϸn������]�d^�B�C�I,��$l�	w##�i�z릴��`���_�Ӄ�CUP���G�?.i��
��X�8���A���I��¼	C�\y×f��' ���@�g�hF�_o̠�ys����������R���
�A���<����7��(������֒S��06����z/ʮ��&�%�ļCz����x�o�R*|�$�>V��X��3��3����-�v���1�ӏ֪�\�ڄu��~oJ���%�h*�G_�#��B��v�@#�����;�$��q##��A�FF�d�Ԝ�G$�����D��W��AM3�\�v�a��敊��&V"S���S�Ҝ���WF�p0���Z1�~S���'-�L�6pF}�֑n�3�F̨#&�p3����!H�^q���=q�!�X�	�#��f��ݥy:(UwFۜ�����jȋ��4��CI��fi�5
�8��c�c���6ݙay9#�au�&Q^0WA�)ҧ�? |�w���8
1��p�T#qN��'��e��`����!Iܸ<�ӂGj�͠���zg��*�!TfV�)e$)oQ����˵��#,������N���-��"f�q"�T@K6N$�8=��O�?n<ø3a���S� ���0������ʛ�-��=1��X�J��Y�x���5jۗR�������4��C�7#�t��ަ�8ɍq0����T����&��,�a@�����U��~嗓��'��^jF�H�w��ۂ1�E#'������A!*Q�C��3
����9�	��)[a�hY��	��v�O�5A���@�b�ht���� w�QQ��T�c1�ܱ���s��R��+!��h��y�z�"���(���E�cw1-��o�Kt�(!NA#s�\v�������ĩ�̈~YT����s߿�Y^��a�Պ�{��U;I�n$~<��{��1��d��9�������
X�b�.7��z;���2���/�c�Pö?�c*�D6�%Ys�j����S�YJ��5�4�r(�Kl1�[���<S��~U`d)遜��J���<�r�;8N0s�� t��\��O>��w�'�g�t(�Ox2S�k*�`9$v=�*ܱd��1��i̻��YK��۞��9����7
���>��x��ҡ��Vؒ���T�ً�в<Hʻ�oF�7<��d�����6q�=���h�<���[rȱm�p23��Fހ��H�	�Q6Hج_���9��r���V7i7��y$��(ž��D�dw�'��#�)�Hv&��(P��H=3�9�p�u����C(��,{Fq��@�p}�'�2�j�ĥ�@U�!�~8��<��F+T��y��#�@�}�(�F��^8��j6��]��Ku#���=*����$u@��9$�O¥�;"�ʟ:����=��~��,J���T�~�c�{��$�W+��ǧ�;�$�aиY3�G�98ǯ�=z���G�X`*p����{���8�p�'����S��dx�UY`�J���m�I7r8�X�b9'��}G�O�"�b�e(�T7<x$��8���Twg�$zq������A*I^��S���[�"���
�m��'��H$gN�0���J�Ữ㎘�@O%�H����Cc8�X�vt&>ٱ�W�1�����R\:��I�
�F9���)c�������$k"�9?x�m�Xm8<t�q�}O<S�'�아a7噁X�����j��v��6_�N)lb��V��#$�>@=B��9��8'�K���8��F	�#�j��).VR�o������;Ԡ-��R�>��.҄��zPFN�})X�k���W�I�q�U�)��Q��
��=�@���ը���09VB�W�}	�Xn�#�U6��1��إ1lm&�9c�4uT.��*O�צk6x�d��}��rN9�x�~5��e�6��;�=q۰���	F��R��	?���q�Jv)��%�H�JRG���E9GR�}C���A�.PS�@�{��y����(�0u�+�����NqI5���ZR����D�UFIS����cY8��N�B���x�0??�TAA�N}�X|)=`7n^G�����LJ��*�d�zc�=��#�h�RY
�����Sӷ'�A0s1�!���l$��ɩ'|m�F>��S�~5UYx�{�����F�(C���ǐ}�+F8D*�y��J�WvF��PL���s����ǧ)\�����Dy�&�L��~i�1�8c����)�����($(���R[��ɱ��@݌�,���Z-�.Ӽ8c�}��j//Gɜ�I�����$3��p'E8�A�ӥAz�$��p�8^8A�3���L���d��2A
�7���;�A�ë.�4�DžfD`�T1��A�pz���q֨�u�6�%���#ۮ?t#̗��e��8�����j���B�bRW-���
	���j���|����?�4�j8b��
���s�g�I���IeT�8�SҪ�C�f��㚈�$l�pN	6~u+�.��nb~R98<�j6�p%�*{1�����HG<m����ެAFu3ot�ޫ��{���޿0���z��'�#�J�c°œ����G&W͵����8�8��~��N��Y0�`7����zw���U����w[�zc�KC&�
��,��I��Ӂ�
�  ������Ҧ�L�9T�������V��#!	�S�>��M\����ظ���=��>�4|)���}(R�h�9S����34!�P��'�9�x�ޚ�Rw,��`���?BƬ�9����Q�M�)Vg;Q'n:w�L{�*
��$��=��j�QW1�m�hG"���7�Nއ{{z�w�C�e篯z�͋�N�rCu\R߷����!��̎�s|�܎:q���JI_�H�����I�'$�����L�� c �NOg�sYJW:a+�ܒ��%U��q���l)PHbz�
�0(��I�j��"7q�.9�=s�V��M�Wf��+�#׷�N*P�xQ�{�#�f$��$��iQ�<7�ι��h�	v���Uq�
� `�px����x��%aVbW���ENL{�lۤ�g��蝝�f�&K���٢,߽�@d1W}�Cۃ��ݟӎ�]����dgv8��:�G'�ӥ��_d�����z�c<W �ó���HL0��z�<��mpnO��MoL�@�n���]�"��	�jI�N�����P�1J!gP�pr1�鞝�烙"R�`-���%f(9�ۮ8�V�
����SwS�qɥ�1�LL�̄˖ �ʟ���pGN���u[�X���O��^�0j4�	Ͼk6�#T�6��B�Y�>彆8�~y��W��j+v��ݤX<D�(�!Ї#'
FN�_N��$l%�q}�>õF�I��8��R}�V�BK���#u;�l��s�'�tS��DIJ�$O&�p��Yz�9�)(wm�s�����}�IG�wNX��6Or7u��)�d��+n|gW�[?�I�W<�oCMR�t&P$�Jc�o��#�ө\P;�wL�L2G)l)QRB�N9$�#��{�K��3�̌�����_�4��$7��9O%6�[����Z��8�C
��K Q�Б�Jx�F��B|���e9U?�s�Qm�фb�Ð	�a�t��֬Y�}�"�¡=��'��2*`%E��Ht,�*�3�@�lt#������7�y.T4��q����u���'��"ʉ'
��y�8+�px��'�V`�[Io���o��T}��n�[i�#�ӌ��
\
�jc+�)R��Us�<s��ܞ�9�*�Ğ]�kI!̸2A�,�Fź��9Q�=EF�k3\���;b!Fx<��`�'#�L��tq�c�laPt'�x��<qWes&���a\�F����|�u��		�cI�#�ۂ�I$r	��y�3��#�ʇ>���n)��v�$����J��=y�4�e]!����.���̒
���99����i�%�Jp1H�ī�㎪1����W"�$Y�%D��d�f�A

�v����z�Qgk����3��h���Gy�"��`K[��B���N���O ��jFV�ő����+ʩ㞹�y���4�f�f�������G��a�rF*�ӝ���9����~zz�Iuw�[���w;��px�s��3��=�H���UnK�Í�zq��f�HZWH�+���g���g�A���yq�iE���ѳ�zg�jwb�Ѝ�*�d���v�`rwt>��EP�B�L����x���֭�@�f�ɸ�<��`��F=��V���Jq=��;`�SD�a�Ȓ�ss.⯄<��Kg8편�w��[��~蜰O�q�8�s��sO�&ڥ]w�ތv��	���⚀�}�#��FӀX�>�皗�q�X��Vc�03��?Cޜ�����D���(Vy`N�z�8�_n��n�vK���g�Ҫ�w�Tu$q��U&��*0Uv�,H�:u>����)h�D�m�̸R���92�G%F��3�1D1y`�'�W�F<���v$��d�<�셮S,���;F	#�x�ɦ$r�m��P7�~T$��*KPd��^�gH��Y��͐{�����e�V
6�̫�rĀ�A�J|A�3E$a�]˖��݌.z��8늈�!�I;�:t�ۃ�p@�
$�3�d)���w�>�б�C2���m�G9=:A�*��咤����,~m�`t<w��I��+�J�,s�@���ӊ%��bQ�29GȠ�'��0ON�;�[!~��8�z���`��=(�~p�nm��9=�Ƿq�4��%���;�UJ�௧@:z����AbXVF�A��� �Q�ҭ̑���AQ���C�ӐI�>��BţĒ�=O������<,�(
��\}�q��ZVyV4�fP@�0~���ϥ1�WUnH����BA8$��R���2N�1�\���GڡP#%��Ln��0O�#{��Sq����PJ�Ňˏ��0x����sО����g�)�W��p�ś�I�\�8?\�M2,�Բ� !f<�������}�ʂ�X+�[��*B%���&��s�GB1�u-�b6�q!H�)i8f����:��JY���a��઎}}~�I1^�WI,q1��2B�$�$���$�G$���bL`)�~��1b��qs�#��SGdY�:�GR���i����+�4@ۄE;�\���`6��s���~��c^�����r?J�A�d]�=G�q��Jc��]Q����O��kV�Q�wb���/�f=
�`~����,��#%¶�G�9�=���*D9�S����c�<u��-�XɆ�u9eJ�2O���R���7�a�΄��p[��8�S��
�e��0����:��LS
�"�3�y<�z�˜�OT�V2�Xr
�e�3����Ϩ5D��m�T��	S�~`�s����}ub����t*p0�K�6;r��ۊ�X��F"f�}@��unO�f�k
���̀�	� ���v���=4a�,jWfJ�|g��J�{�Rȁ���p/~�4��vrbi�b�X~FSP�Y	�3L�(9,s�6�s� ���5���=8uf���dh�w+�26��*��XÐKnpIS��ǡ�x�ݜn1}Р����3��w�<֌��jr��a6�� ��#��T�%�1�$s���ʶ�,��±A�;:��:Zf� W�TVWd�o)�
�I�ܞ@�$(�e�Y�!�*	�����략6G���"gk���=rzdq\�I�etV(@���N=	��:�RH��yY�@v+
��{>���G2�%�� (��/nO9�=i.�$��@�@l�Ĝ�����KZ��X+�q��qLpI�q�:���O�R*� dRv�wm��Iri��h���@s��'���8�����(F�%eQ�����;~\��SNNz��x����]u �uT��{���Q���#FLi� Epx?�։�Z�FD*�Fe+9���S��R�Jo/$�8���vD�ϴtU�b�'�rj�L�?�Ӣ�cdx��I��c�V{��m-���`��\��sP�P������Aq��&�pO�8�7�?Z.�Ve�H6��e/���pF{z�jZ�ȑI�6����y<sS�(����R9��ߧl
�ؕr��u�T�m�z�Wi�
4�$��q~K��''9�NM$�;hKn�	(�����;��J�aSy1�ˠQ����(,K�Ts�RO��
p�e�<q�B:�Oj�$дP$q��2JN��rq��p1ש��RnWFu
���dq���=ą�N��=��Jr��	 Ӹ�Uyr0�b�����.�=�1��9<S�yh���#�cnO�[���Ɖ�H�2ʇ,�Pq���0$�FH\/~y��A=ϯ�È�	�dW��P3�:���i�
�ayT2!*�6�� zH���6%�\��d˹d.	>���*W�)<���uW8<.�Obx�QUˑ���?61����Դ�C��@���n��$�9#��'j�G+J��
��HK0h���ܟ��s�C"��17ƙq���u��M*�������8ӿZ<�O
B�O�K���ʕ~~pW���A�x�l�Y#�BM�����=@�T$���l����l}H��3�2̥ٳ�e���N<�?C�ڕ��}x�Ȼc`���͑�.Lҗxݚ	��2	!]�I�W��S�Gx�Ofͪ�ǖ�(��ЌF>�L�!.]����K'$g�����sviY��r��ۇ�#����3��>b�d�f�+�$t��^�zt�iye8�]��3�I����OUR�y\��.�ޜ�c֚H��ı�X�(T�W���ק_ZI���Kb�u.~l��=N0;�$d[�j��͉B�;Hی�N޸�$j�Fw��\�t�8�{S�1]P�ċt��ޝO\��#���J�m8۸$*�)#� �����e��fe%6�B=O|~���!%�T�0���A��`�zt���i�
P���,F8c�A'w#��)b�uU��p��AI<��>�u�Ոa[��:�n	b�##�p:��oC$~c��I�;Xg;ǩ�qڒ\��{��%��,dxLp>����u^���1��O3�qw,�l�[{��X�ry���Y���+է�����Xqؒbaw�C21�8�y([#nI���p:�U|B���6�yF���:}s��ݚI"���[Ơ�F����Id��z��-��v�>\%w��X3� �A?OJ '���8*��q�x���d��+�'k]	$svDq�,�(IŽX��9�s�TL�$�9TD
۰�0m��1�:g8�V
��!I�`�<rH^��6@�Ϧd��t:}�л�ZUGܫ���p6��t' TIjTdP��k�38Q!����-���I98ϰ�I.$�q2}��m��%r0�#4�G��U�h�`�#�0:rsޟx�C��K��p�v�r?<��-a��̓
y$��cw=v��@�)�.ٞB�Z�(O�@#i�#9�}	�Ta0�{l�7�x��8?�M��!+�h�F�<�9!�g8�5=F�[p��%��&��7tn�<�x�^��e!	Q��y�	��#�pVI�)��p�Y[�(Cc{`�ҟ"��u$�v~S��5���2n̉A�O���n
��:���[�x~���{�[dp[;����_�r���#�r��IJ~$�(��u#�==EZK�R4�ɘU�*I�$I�c���ⶥ(G�D��a�Ł���	����r���=���UWt̠��s�y�Q�Dž�߮�ә��E��%�'i�^s��*��>Px �A�~��>��ڽ�S���y���Fv����'�R�ʤ89f�TQׂ3��h�d+�d�̥�GLs�t��ۀ�F�r'b��-�t
s��#���*.��$��"I#+�.J�[{�O��U��C�$ ƀ��9�H��8�V�μ�䑎9�t�=�Z�W�`.aVE��A��Xgq;��	Q�bfhf��-ʥ�Ȗ�Ǜ��g��N?,��9���ij�")T	'��8��U���L����Y��PH�P0�N20s�Fj$�o-�$���:���5(�On�)���l��y�<c<u�>5Q!Q�0�`��מ��ڡ��+3�y'q8��T�M��M�g
h���bT�� ���*�ada��s�^��H�ǯ��>�8���������V�#6�-�3`��ܜu�Q���+|�?�n�8���5#���@�B��֩���ؽf���6�?3�ў�ߊ�����=!s���lL1�$��^��[�n[ϧ�m	]��#cD�6�ȿx/F�}:U9n߃�;p���S�؇AT5�W~��Uܬ19�M�7fLb�А��%��n��yVf�N�I���Vdщ"�V�8�H�u�B��!��I�y}�l�t���r�����S�Ips�,y�{�?jby
rX��zc?J�KnPA�s�����r*M���#!�!�$��o�q�z�
�9S��'�#�Y�kBw0c�}�H ��d��Nr{�j��;�d1ls؟ʀD&<�9f#q�c���R�E�Z5e��w�ŌA�=�U&E	�Qذ�X�rqQ�`�c�O�EfѢeV��������)�S�<��zS��@*G�6G�s��X�@mC�ny9�N~��;T^Ɲ		>X�`���\2s��*�����U3�,�d�	<�?ϭisWY%�$1�`y9z�2=?:}��=�0eJ!%l�玧�}0x"����@#�HϽ2�Doã�:�:�����X����ܬȣȊ��J[��rI?1�8$��i���V�?�<{{�$��8cО<�����r�7o��
1�{~t���W%X����TlL�1���Fz��]����Ȫ���B�@�Ts���9T���^�F28����9�"��e�4 ��6NpO��@�T�؏��O���L��'~_;@Q�3��G�M�`�v�X�U#�v��OSd�Vˆ����5"y�LRU(�FC�}:��R���2	�#��ڑ@r��2��ʓE^����cȅ���p{S��[p pFG>�j+s�Iڤ�������O�c�Kg9=�#��m�H��f|����^�=i�"�0Y>�;��tXm�~�3���Q����hd��0����8,7c���@i�7�<�\[��,��c��PIb=O.O<�����	�\��N��d	0�G�u�@r�{�"�A�E"�\���v��}j��:�&��hla��O"pdV�;�%O^W��/�屷;N9���5zH�p��$v��q�}�Gz�@��2H��۳�����Ӗ�
cK�1�Ī��7`e�y�Ъd�-�
�X�=��m�pU�=@��?:g������M&R��`\���c���j�xP�X猞�˰��=��-����LT�$�n�W��OƩ
R�Ŀ)͓"�@�I럧n�p�$?x`��~T��(2ĀL��!I!O�=?��(�H�iQᘖb��H�'��H�&��a�h>V�����=j�B����:|�]��N7�lzf�c26�ѣ��'<�	�~4�pJȊ]���vH�$c�j����#��n�!FpI��9��c�тw�ܑ���/�c����9�Ҫ2�3WE��X�eeS�z��æ@>�}y�j����a8�A<�U���uۜ�3�r��\Ȃ�Q��3Oa��l�V������,N@�|ǯ��6F2C�_53HrI�����b�8���'�U)�rH,B��9���F��F���DJ�������;mL��ڤ����M��d���z�
�@��`g���qߥW��V�؝�*x]އ��~�qQ�n���׀�TH��C:���8�P�]eA8du��O�5gp΀����oϟҐ��l��K
�Ga������m��m�-.LIt��ΗI��m�n��P��Gl����,�o#n��Olg�a�	��3��{D�I�z� ��j���8�W'���zf�N�m�ɝm#t�Al�F�O�-��e9��{`z�1�HE�¯s�qO@O����c,#�z������6$w�>	!+�GA�N��KB��qրy�Z8�]��K��ۯҤ���8�r2z�s�ҠD���#oC���:��ر�~��9ǩ�<���/��ʙ4���S28��=�1E�_5²ȹR�F?�U�p
(�F�T��P	�H��P�0N	+����)�D��4�|ہXn!s��:��5~�b�']��+�M@�����a�NFF?*�0�0QJmuROC�v�}+x�rɻ�WE�VU�y�=�
�ҙ��$��A"������2=y��do�dq�9�ԃ���,�{2����<�}�9[q�:ܯU�7�����7�I=:g���R���9_�����R����B����#�k&���dжra��*8��N�էm����;x��J:�؃�@z�N?�*kأ�Yb#yy`��wg(q׿ڟ1*d�BϘI#��~z���,A���9$1�4.��\D�V`1�.c$����H&�|�+wc���U{�5��$	0Ď��:����V��O�;�y�#�V8��AA�$�sJ�s\lm�I�"��|�T�
�ԃ��Uo+p�_��sϷO��]�C��n��ES��SQB���]ʕV
�`{c56)O�$V�Ke<���@<�|*��
��"����Hڪp���S�O�V��D!8�ʹ��A��8�P:y��l~d\'�ͿJN)T�U$x ��&eI��a�~``3�eA�1ޡb<�.N)��a�N'��ф�H-�����*��*�NM2XY9��0/�,�6>a�=F{�VMXߚ�D�x��q���Dap28*y��*ǩ��?��{���j�r0ǐx��9����>��+;"Ʋ(C��
	=�j�M.ȑ��xBO��n���������Sm�G˞��Z�L�$_)R�2H�2�S0W$�E�X'$��94��9�0��=p;T�,���C�|��{T�r�:9��'�L�ꀕ��6�U*a\��x�9#��B4NF���T�_o��M�	�×a�b0H?�?�i{KQ�	$/��~��1c�E]��2H� ��:seC.(�*z�A��کL|�O�T�?����z��:��\$��LJ���88���0pH�C=���i4ʨ�����3����ڳ��Q)�$t�?��RZ����Gq��~��՜]�eزec,+4�,E2y�|����� D�ܐ�S�pa�'��Lc�OʣUc0�v��Sn�H�r�d|���c�);����H�
;�v�?JE%P���M�[#
����:�+)3X������{zU�"����
�	�Q�dzSr�	�w���G>��z�8��x`8�ߊqW��P�����9t�)A� �8��(�p�5;��'#�?��K��Us�A�ݞ��ީhf�`G�B�q�*��=����U$%� #����9��I��°��U��8�'�<��c0$R�������Ϯi�rV��m�P�?�ӏO�T��N�T7����`�@�Ϩ��R�#��������劥�2w,+�d���$��BF�,x�3���ڒ-��C�\
��Σk�m (8U����ҭ�D�A�����6nN��Kg��q��]�u-��\�i��n ��*+�+����u����崬6��dr�o
�����*��q�v�@ǡ��Ӧ`<��%�\��sQ'V\���w��J�Lcer��vB�gŹ�B������ʂ�끂��*y���<�u����tA�VbA!FѴs��3R:�0�����f�@9�<zu�ۊn��ݒ3�=�N4c+��ݽ9��+6��˧i�$vs,�͓��$��c�A�R��I�$�Lq.#�1��|��}qڢ�Ș2��0*{��5rͼÆ<~<}H�+6j���C\�v�Y-ß+~�!�FOn�?�@��9��V&̈X�|��u�G&���+n*Ww8ݹ��N;�W�n^Y�]L62F2@ �����}NH's%�2'u��'U,y,>�� �O=j9Y_.p�	����T��w�?˻�y��'�q��-�du��FN�vRF1�`���q��ס���D�:<������8��EG1T�Tz~y?�'4Ts�J�$����62T�B|�,g�z��'<1���*���>c�`�G�8>ݩ��b!�Y�)�<�����I ��+3T�;��H�#�9���>����id��Y��2s�7��*�9���S������?_J�|-�ԏhmR�+a���q�r}*�"�79��g#�^?�6�4>�c;�U�X2�%I�[�^������Nq�:��I�ӡ����\�R[��rq�v�w�����7}�`7ry~��E��<�69u(q���=*��$H�.@�v��qϵ9�qdC�A8�>��5���m98�
����G��F��Nq�1�z��I?( ��?���#(���vwf#N8�j8�[,a�r>��Y�CY
�7�d�fo*I�e�9ݜ�0}Ȥ��vF�#,�ʉ0N2O���jIn&���.[̑�PQª�QϢ���Rw3`�f�Wj����c�?�?r3�ګ�zdx�
��R��q��>��j�#c��m��8$�z���Z�c&X��f���	.00�#=�WB�]��w���h��L�'2��9�=zU�aDR!�����WBW��9���G,ۜ� �=;�׭-2"��I���©�m�7n��|�;�نVl�@����;�g%%�9g/Ԩ�ʪ���`�J���U�lܒ��CX{Z�T����+;*�xc�.�c.�UNָ�o�� �i"�ˁ�[����q�=���c���1�䎟ֳ1��
Eld��l�?ʭ�C(	��)�����U#w��+*�۹I��ˌ�w���Y�)Ĩ�X�o���=���c�9!�@����HfuU@X�t��ӧN�Z�H�+Kn�\����СW�v��8?�W�+��p���rsO���l���x�Ji�KV)���=G�4;@`v��=���M�6�����ʕUJ���s�i�K#��#!�b��a��	A�GspB�6��U;W*_^�����2�rN��?��Ȧy+�<�����A��r5���G��@.
�z��dt=;ԱȌ�<���U.�����,�02F�vP�/` 䜜���70����7A����8�G&���c�Cw�PF3��
������C��ۆ���*r��`<�t�?����f!_r|�����=�ap�C�.r��N�	��ח�e	����n�c��rp���9��g�\)��+[`#��S���S�cY3����$H�s���"v%�����9���[��?�j�w
�Z�[��Md���������I�u$r8�Eڥ���(���S.!��W�|�2����JI2�u���~��q�����r3I�b
���1Rv�rI�y��O1���(�{*��l;@_n�����Y�[��"s�G8ϧ��i��.p�瑞�O�U�Ī�)Bw��Ojn��߇brC�����
�)L�(� RH�>�p3��J�j�F@��MێJj��`;���==q���'��|�x�Q1�!�гe��H�w8>��$�݀GQ��皜�7�Y�˒C�OJ��~>S���
�$a��~9f�ONO~�ӧzrʨ <clt;����c0)�� T���S;�d���⍶k�� !��d�8��׽*�L�~USԟN�Ȋ��͓�r�s�8��J��fTe,�ݽ�v����{�򕞅	�àp�^�0?��@�EB�r��߿ӵ#���(����H�r�`d�O�[��~4=J�va�/���
۷�SдI�''N	��*�L���6����͂��r�]ܞ�u>��W�Ē|������������Hc��s�X6q�����NX]�2��[b�y?��<`I/�+�`X��I �Yy7�y�����I�J�Nr3��M�dyUT�R2:c��u*�KY�+H�[qR�I��0I ��g�0k���AG��$'8?�~p�$���X٣���玽��2�l�`#j�8���?�Ԩ�Q�1�?C���YA��=���[��Z��~�Sh�y'�`'�r*�⪃�B�N#`z�����XF����U�;�ZD%v1��=�U^��/�v*$�¾��r;�}*YQ��+r�v���ﶪ�e�L�A���h;W$`����95k*�]�|ǯ$��J�;�<���A(XD�p��H��EUVdg�\��! 1'���V\�Eg���n$u?6�GP9p*�m�w��r}��/B�ɦc�YL��.=�3�Z����:�2cJέ
��u.��N	�Fz��SI!pWiI9�'�����+�-I�y���r�m�6�*0N	�5�lgdm��s�0����,[�Hʍ�d`u=Gl�yU��(�q<;�8��=jni�nZ��h���#�P��APz��9��4���F2O�rx�8��;jG
*H�c/+�
���A#����;�*F꾹��S�
kbeb�s����u�zu�\��h�Ftn�2�Б����)ݴ9ʞ|�#�s�P�*�/bJ�==���W�����2;�F���?��@=z�޽)���,j��6�$���1ߡ9�4�ğ�"� ��oA�s�n*X�VC2�A��П^9��*ѝ�ĖrYc����L�����{խ<#�	�XwϽfN����vUPH<I�������Y����(f ��i�Y�V:4��̛y�*p��=zɇ��v��Ʒ.��8��/n���eG�7s��Ӛ���)]Y�.��$M�X�1
0�s��F�S�]UY��r{��~��:.	.3�N*�˷��l\���<`��5DiM�;�� 1Ue99�>Ԓ�#h$61����YĦ5w��j�ww#<1�隵<k��m�i-��2�.��.O�8$�p8JI��=��l0/�:������U��Ό(fr��^@����e�7$@du�0��x댞=1��ЪH�mV�V��!�{�k@��CUǿ�2s�K�6������P	�����PH�e,7|���;�S��PKn$u���!U%�w=G�횚���q��#z~���&PW8���{¯@wl�����N�ҹn*�&
��]���$�nb �k�G?��j��	�J�Ac�%������U/��X�bH��Z�]NWZ�nǖJ�~B~���Q�N��s��s���C�M�I��Tꊄ�p���j�6k�m�DZ�;`(�ݐ{��ҡ�bFs�@펹��q�b�Է��8r�b�!�~`�o��z�VE)�UN_q*�UR,F��Td(0J��#$����6��G� ��j"��*6��X�^*�Q:a-
�#�?)�o�;���V��}����1/��r[�OQ$�^��-��u�D˜�D�N2+�ӞǷ�r�71��\�7���;d��Ҭ��"���V��y��(S�6r�n9��1���T~W*Gp:~��LH��� q�j����e�N-�h���0�	V6U-�O_����Ui�D��+B�,3������^�*̱��<"�v�O�U
���R4�Y���!A'���SЈ�Ԯ���G*���Pt)�c��Ӧ*�~KYK��dk�ërO��u�2dL`��ih�:�e�
����&oֻD�\��<F �����\q�w��=H�h#m�9���a����Q�~��N�4gd�0�/R03��9�:U"��A$�ʡ�� ��v�'��%�ˏ%�ϐ��B��{qR�P��I���c������Җ\M1p��ğ/h+��01Y�ZvI���wHV�	f��<(�Ey�ޕ�D�HL����9�����F�TT�`G����jp�����tl���ڮ$I���Fo�S�㓷��}�$,!��!#5pv��s���*�`������֒�dFÆT�.*}@�$u���2 �T����L�����zT6\Qf��66�`ǩ$����zu�>�6�����œ��ֳ������>N���s�;�S�$��y*�"*+�����ŀ$���1�\��#M���^z彷c�6]�i,7��@8=?,ӭ|�ln
Ür3�=>��-�ب�1,�n�@#i
����=�k�-k�����Ksr�M$�w�!i�bK�9$�����23=�{oe�9���IX�wn�d����c�S�?�z� �8����s�t�hȑ��z��v����1��9�#���__�+[�W4���$�����֩�T��i��}9kNf���N��FIϯ�TeU.لC�ya���*jhoFM�f*ͼ�F�(!W����–Dڮ��<9�
� ��z���%���<;���ڣ�p�!@nIڽ?,�;gJel"��c��s�~T��8�⭸��#�=��uv��{��'�v�I�yp�|ı<c'=���n�o�˸�.���<��8��3_$,k�n�@9�i���_(����n�sӧjjl{�u*�Ss��R�Ԯs�q�;�M�Ὃ#�l�0܀�Oש�}j9����7����q�z9R��B�O��F�3H��� *p�7A�O@9��*��3�Ͱ�e�0��e*܀�\�לUs��w���? *}�rЌ)[�Kepz�n�Ϡ��1�l6��r�wq��jϗ[��>9Z�pK&g�d��7���O�>��F�Ȯ�$bB�lx<zq߷Zh��v�o�
�A8�O'�<�fcfҰ�d�@BY�\�v1��Ǔ�c�i�_@h�#1���>�09#�:��s��S�Bsɼ�|�?�7w�$߮(��� /���q����� ��c�v�k U`N��C���$��{�B�R2��h|ut$����P�d}�'j.9�J� �r�������oBK|�'?��i�2J��$s���׷J��K@�)��R2^�'��Z�X�[�G,�a����)E �A%�XF6����F)�i:m'�:qR�E%*$��21���֚@��k��*�����F��lc'��֍��J���+̈���1f)���G�W	�	�C�&]߻+�^�<�8ɦY\I�*G�
��D����A�X�v]
l����̷�o9Lj���I�wd�z�+]F�-\F��.�$�3��b;g�V�I;dG�u'o8�:���6W������C��w��)�HzDz2�����U?>�9�89u��v@�bb,��/;}�l�=z��۶��r�O�����T��	V��8�	�9��<��VX�[���"y%-�#Lg�1�9$�>��B~�"�A8�� c�����Y;�����w�܂=9�J��ʳ*�v�b́����1�^
re�EI��
���26�͎0w��9bGP
[+c4�DI�v��F��s�iʤ��$P��<ggbq��Ԓ�X�aS6ɷ�%����H#��H�Ɍ��#�U�oPd�%F>a����=)�\���5��i��a��̈́$���� ��:;s9t�U����O',O	=2j9�$n�����3�H�c�:zsJ^EE�#xôd:`���<�@)���H�� '��I�c��<�$J�@�~F��w#nO�0�G���M���=;Q��W)��n�Rd�$`da�.���~l*K���bL���R�w�7m� �����j5B�+�!������f�FdڱȄ��s���=3�u���C'
6(s��3���q��b��i[ˌw���Ԩ4 ���A����p��j�����I"FH�I�g}�21���=:桔�)��^p�������m�`���w�j�S.�7;gny�y�z��=�E����N@��G�Kk�Ngh����C�9l�`�`�:u��W�/�c,9e9���=�H���"�A����s��ҟEyRI"�g��F�t$rUy��Њp��
�A<���^��=�W&�@Gr>]�p�TdLՙ�kr�n]/��~�Is��LGz��VĪ�+����?Nئn�(�y1��'#����P~�"���"�\hѶ�?Ì��}�B������r@#��ҒIf�gfh��_����}�!�`X�xی�����WB��c�� %|��V�'����i�l�
�>Ӑd��?Ӛ���[�2\O4�)$��[q�O\q��w�L�D��$�r�>�0�����XI<�l��~��+����O�9��!A峻�
��A���}�Q�Y�q�`�OӑSn���/�s�<�[�:��J�qEi,�\�\|�(�s�0$��v��z�pE7)#Ȑ�R2��'8���0N}��d2�`��}��q܂}xjI&Y-�_%U')�䜱$������#�a�HX�o�C{g�����Q)%0Q�Ǟ��L�����9=N2�z�< b8���}�c�N1�j��mT�cud̯���������
��R�`Nc�1�'�8����»@�H��^�zO1cR�	;��^��5�I��`h�$F�Dn>F<3�C���UʅL=x�c����7��G
Hݎ��_�=i�8$�I�\d�j%"���@�~L��
��qH]��l���҈�.�,�:�v��3��j6p�I������=h����'�������5�<��A�T��q���)����ZF0���8���;h
�NH��=��zV��jaV����K�[4���0pO�|�Q�=��^N�8&Q+�	p'�pp1�NI��}��\3��׍���$rz��{��Y��[�oU;�c�q�g��j���rY?zRX��p@��'�C�W3.8�'��
�’�(�� ��Jビ�OQ�~�7*aWy•S�|�`p>�J��[]-��NШ2Wr��aG���=�B�FK��3���@�r�8�k8۬jX1l�ǴCϠ=3��o<�r�M�r=A�'9=Tu��F)Ǚhh_D5HU�&�Af�����Ü���<���d6:�#��z�Z���X��++T���ۚ�wo
�@�C��t cwn}G��N
Z�_g�G%�D�<�rx�)�ǹlŋ1d |�<s��[���D�іVG�n?�U�['h���Es4�:�%%t(�P�����q��X�6�\���ǽ,�̞V@@��	l�q�?:���8��>�^����r����v�P5�q�݅�q�Z�XP��!�$���;~uE%�_���
�8�l�W#�4E]܅���q�#'�����ȔZ�cCS��!�fMĒ1��ϮGN�I���q�7�^��c��޴��x��R��� 6v��x�3�$�ߐ���c��v>�ԥʌ�U\�
�v���'9�d��
y!"�l,��8l}�����!�N�29�瞸���{Tֲ��/ȞX�3�^A�Ǟ��9��kuk�Eg�o��,w-8H�p�|�󁃅�9<b�Gg1�2	,Jd�p��I぀y��K"���$�|��G9��i�+,��P8�8b8s����Q��Rݣʮ�xM�mݐy��G�OUH������	���z�v���@��2zu�U�X��Q��nw���:OA��P���r�9<v�I0
��r�7�9�נ�Ap�J��G"bE�7+d郒=�$�I$�+�%�r��bNI$��>ޘ�1Y��s'k���}9�9�B(�h��a��s�:��8�aU�x��af
���	�g��MY"x�l�� �(��\�;���C,mx�v�7W��?/1ɓ�vx�T����$b�Ό< -�FI�:�*)b�
�.B�����>��SCC�pUl��S�=2z����\.TuX)��8�<��4��
%�C���s���>_+�RC>�d�'蠎G^���R�i%h�<nH��A'�8��L͇r;��wr��$�mA�Q��=3�TRC#&�9�r�d�q��H���!������=H��®�d-!�K����I?����ہ9jm!\�W,K|����rG�{zf��:fX�e n
�|�����^9�ȾT��;H#��:�s�������1]�����Ӂ�Ӛ�a6ɮ �7���n�嫸ww8�x�$��A�CH]���-���s�~:���6���J�:�w`0���{#Uh�2��G̸�~�=s۞)��c����N�#�1��=qR����#$��2�nl��v6;q����]�`��nVRz���z㊑#^'�$1]�䌎�{g��I��j�tO#Gq<�cTIO���]͞�1�=���si�ǹ�()�9�r{�϶h��-��A�=A<�8�sO��d%XC��c�|�<�:U��!��@��k!�@G�F�1���FY����'��2	X�Ĺ����s��t� a��x>�pr9���Mh�]w>��2属:�z~�
!�L�-��O������ï���=jKFo6(�3�uX���:�<u˺�:�X>s�A������O�6�S�L��zzg��,�t\VGX"yD�|�*�h�G��q���m�2y���yU��D�t���'oQ��5#�g��ϗ8#=�9���Y(�ÂX�o4�R	����ҹ7W$���O������o%#+�g���ϯ@}pOj�L� DZ�~7n�<����B*,�I*8�`#e�+��;��1ң��Ih�+寖���X�d�I;�a���W��$�������8�S���"Dt�h�X�~��r�)�c���q(
��i�"c�y�R�A
��=�8�꼨�#e�e�=:�Rv܌��|�Jp[��n:��d{zs�(	���xex���<w�d���� ��Ƿ��p�,U �s��R�jBC���`��$�V-����EU#�&�?1�pI�EG�p>l��l��'��8
�Ny�z��R���q�f�^O��=�G�Rv%�=bi�#
˒��c=O#�	$��))�F���E]�X팏c��i�ch�T���0GA�#�=���&��k1$sr�%ـ�nU�x�4�ĸym��Y] �]�S��W<ze��{U\��݀7NÞ�#�I(C�)��s�G~��""�Hc����p��hn��F#m˖p0@$��y����ܪ���S��M��*�7Em*�����Ϸ��?G���?t���8�F1�i��B[,Lw�om�d��_l��ѳB�b;#?;����HqVb_���à��oR�g*A=+Ij���
��[ATl�NG �G��4�����O��f�BՔ"mNF��6��Ǿ$(���ް1�3��#=����i!"O�9�9=:U�c&0w��>���w�H���#��\_c��'����Ÿ,��\�Mr�[�����@,�I$��?ҭjC��t$���X�C���`���&�r����'`c�qVcP$!A`Ô
��9��d�AZ�cXX�mvb�s���J�5��d�q�9�`T��,��P�I_n[�{w�����C1����x�r�,��vwff
#7O�j��b�r0B�s�ڨ<D]�v��g j���Q�<��֝;�ɩ%-�������3U�Ao<~r"VV*~M�wOQ�FGL����\���N	ϧ~�궥=����<�,�HL��$��cq$d��Nin��e��g}�Rc�$����ϭYڥJ��x�6����·�"�<.:��}U��7�J����N��zq�y��e!��6ONOa�1��1O*���o�18$�㞝:���%1�+��!��ژ�)�mF={u�
�H�-,Ko&s߸튔l1�m�
����c۹�1�(?7<��NpCm#$�
�9����Z���G��Ӑ�$H�FI!F�-�z�Q�ᰝJ���^��(�Y���M2vd�
�7��p=��Q��ґ�\K;���K4��x�eF=��i�6���3�8��{i�Z�2��q�nF;Vr6���C'Bx(nr����~K�q~�@<�9�Q)YZVx�%s�l@*��ۂ0�sOUo(9��7�x�nq�:ӈ��DS�P�1͸un@�=F?ZC�t�>`�$�m�S�X0�8��<��p���#*1�g��W��3�*Oͻ
��x�~��1��YN��d(x9���Ü�=�{v�ڱ’���ġ�����9�TdUv��8#*olv�:�S$M��� ބ��H���qfs�oN�)g�9�X�-���6�8'o^���<Tr�l'��{�)u�hԷ����H!O�PpH�r����03����2Jc={�@�U�s5$�H�$�p(�s�?^�S�S�l�H(��0�20:cp<��&"r�w��Pz���Hp�*~Q�:�Z�-lj�T38!y���q����a6�#*��n�>���\���Oܐ���Σ
�eGn!��;���I�lR��E(�'%�m�{�sV%h�|���}���L��ݳ�P^	��Q���9o�~U���IQ!�P�@Ü�@����l
�s�}��S;��r��T��q�=���=+D��3".��dlr��3����L}Υ�v�A�n��5��ł�L���S�yL�r�P��G�Z6�NI���?��NN00q�{�,,�B�s��=�@.�A�@bC9��8�Z��;NN����&�I�s�VvF}�egua��$8�?ʤ�����t�r���s�~9�0�˹HP��w�nT����H�݄n�#<�:�?:�9�A$�I<�x�p}EV%��vN� 0lc���Mr�fVeD�"���0��RD��^��
{�Ӟ{�M:$e��GBQ�?��i��1���:��3�R0G dՓ]�7-��#w;FB�m�9�Z�VՊ��S*W2��ޕGyےN9�1�֬]M���y%�PΦ"񞤨-�A<^ I�Em���'#~�:���O�$��Y�H��v@�ǑӦ;�_J�w̤w\vO��[����r��E,J[p��{��s8�R�
�	���Y���F1�/�	�r;�w#$uSVlT,����C?ҳo�bv�*
�:�����G��D�O
ؑ�뎿�Ôu�|��[��;���{T1�3�m�q]�I�;s�$p9�O@jӑ!]�����N=���f�ΛXcC ���pXd�
���^�z��t�T'���7s��8�1U���r���'�x8�=j����!
������I3a�Ur��pާ�8���c�·
���C��!�mTq”9��<�q�ڌ��0
�rN�z�O¡ȫr�2�4��K,���_�;��85[�Sϗ�}�q�q���54']��>�ҹ��s�ƩmCF�䍧ql���n��i�+��73K��F9���H�A�C�ɕ(a�3�z��cN�D�R9��]����d�r{T@��
�R�p����N�[[��x��bB7��'o��h�ݛk1���$�z��zb]�"���F��^�~4��܀�ۗ�#���8����ua��e��0ǀۥRyt�;q�$��(�DWM���'$c=B�
6܍�(1
�����ߞ�ԑ�0�Ԏ1�=?�O��*��ނK3K#(n8U�$9#О���W,6��nD`�&O=q�x���!�F�ʡ�Tn C�x���v3���''���e����B���F	f`��3�?޳��� �����Zڂ�b�듏����Um����=*ڹ�9]jKr�E9X٤�@*�̚�*Ȍ���܏S�?�5nX�o�<{sU^`��	�9� 1�^1�~�TKDiO�0
�T��w�S��y|��<d猑�~5j�T���^M4P���W�h�pR� 8
��S�=ȁ�e���q�����O_^���dՇ~VRݱ�~]܂g����8死�� �+�v�#���sP��Vi��W'����y<�?Z�ٖv`�,����i�Q�C�W�Tc?)�*Y�(��c
�a��~�8�U|���R�Y��z�A@Fw��B��-�u�FWצh�"�����i�*�09��+	�xR3��SJ�M��$^B|����9=��$�>C���ۀ��71��~RG9���A�֪��|�Tbͷ,��>��et\kRc�lb��/���^���3����S�,%6�� ��*tX�~RF{��>���+MbGl 9#�G�P�[c䌜���=9�W�u�Xn9���
�C�F�������}�4�R(:.�2X�(� ��3���	m����`�	��
�ɕn��� �8�����P��
�+>����Q:a-
�[+n ��9��0�z��hbr�c6�	�ǝ�:1;I┤��T�˱�x��cj�m9\��z}hkA��;���7d��2y�n�֯���`�q��Gt�#'�)bT� }�p��\Ĥ9����*Xa/ ���8�j��)�X�c�)���%b#��P0��C�y�g��+�� �c�8�z� ����R	Q�=jI>rp�;z.�S��?�ʜD7�
N@���}�Sd�_L������<�}?Z��L��lƣ*_��O9�@�xEf_0판X���&���=ÂA����Ol�x������tN�ŲP���|����h��`�*�0G��R�吱+��݈�}1�ޚ����`���1��9�����{����00��)���1�.唡�(����!"6FXѤF�U�Y�ps�TEP�g�����r��Դ�>6Y]�.���c���rN�
=���� �!�s����ژ��F,W�q��OҦ(�pC$��	����+H�&F#f�
����z��!���d��zd�–�!q��i#�m�m ���Zd�pĀ=1֖	Wd!\A�{�>���D�r@$��^0y��QǶF ��6��G��oR��v�6?>��^?�0���O�E� �p`���9�K���Uc�w_�=�rSڬڶ�H~PWo'��ZE�5p���Tf�t
�=Q�kuq �qH�|�'�O����F��l��q�q��*֛=ŭ�O��8ud%YN~�~�7��k݈�G�O�	S����j]J2YOr����S�pPy���mNz�����ɷI$�w8�͎�Jٴ��m[����ʬ�@��<g*|i�7 �H�c��y��4�;�Rw`c$�~����m���:��z<V*7f΢J�[B��gQ��r���Q�|���v��\u�)�9$�F�002N:U	.|õT��r���Xs횊��m���x)�>l�8�1��4�.؉݈�����o\qNR��3��r=sI�Z\C5�yS��X�A*A�\�l�*(&R	*��8��<��#�\�2�=	�}�*�,�|�ᔐ��P�p#�-�*	�W"�$+�as��d�>���C#01Ƹa!��:n����!�����=�0l
��ɽF>c�y�:r��)��s�e����u){Öń;Xl>Y#�M�<�����PNv�ʒX2G�9��T������oΡ\�"c��h^?1�ǧ9�_K�h�
�8�JvȪNJ���p8�:�O��$@$R��Y�|��lu�Ee�sK���L��!�7
���vw1��3�G���*��z�9�����5���TB|�����Wx�&�&��3�R�=	�8=i&7�.?������1c���G���/�Xd�ֳ�}�Q���xy����&n�X#8�oc�jI��/�+���e���=��Mt��L�>�6���c?Lp0��)-�|�\�$�8���ZXRfh�+�(H'��	�p=�Ҥ�d!�@Fv�9�OS�;U"%��?.0���@��{d}i�A��mE
�d Bǁ���c��J��t��+��kc�O�4me���`���!
��;^zU�V%^UvH(`���uP��,����;��l����8��T��D��GR�FH$w����V\,�*E �*	
�i*O#��X�I���L�
�s����}����X�@#�M���	,zc�ޣ
�����rN23�c&���J�;1�Gr=}:���"lH���YU!�!H���@�i3�;��sԒ9>�=>�������n�_+�j�!�`�#��8��2�1y9�
d����Wc]��%�PON���.���������y9��V���"�ɮ�I#��}
��>s���Ą^�n<�O�He
����0s���88ǥOm(w�2Q�	�#������_-�ؖ�����ck~^e�Ȫr�328�ee%��89����Y�O�y�+x���k��6��]�a���y�?��ը�:�DJ���݀y���[3G-n�����7s�*2�.U���py�L��{U(�r�'r���O~*Ʀ$2�#����y�������&�s5���3�ʭ�NAt'�J�{��D]�Y�E�-O;~|��9�t�r
E�.[$���׷�*�C�j��x���Wp�%����c����w4���NyÀ����q��~T�Xo(���y�R��ı1-������>Q��ׁS<X�v.v��x���>�m��M2[ԬgĞ\n��[
��r��1�v�Iq�GONj�N����9�r?ȫ9�%�ԡ׿=(N��q&��0B�u�Pr=�׽*3yr+.��s��FI��h�b�I� ��s�L���*\p���:W�0���'>c�+�����_0��H�A>��"lg8?Q���S�g 
�r8����qU��܄!P����3�8�id�̸Y¢8�I�����l��T�۰P�Z
��������C��8�`��:g��X�;�����K&^L`�I�t�V
���?xpH�09��)�T߁����O��8����d�/�S�G�Ww�VE ndp9?�XR��ɼ�?M�t8=8?�6�@���'
��l�O�Q�X>�U���)�����W3�4����gU�2F3���s�U�2
�o^zԴa4��`�����W$s�1ڣ��E"+"�S�'+Ӫ�^ߧ�&�w��bX���h��`�8�g�=H�X�2nX��?|��	�����j{x��Hя�`��PRx��6�k�#�6K���#>���Q%p����v��I�l��q"��1�Ƹ�O8��P���x�=?
�QYBǸ���/ʪ)��C�XĈ�Զ�Y��p7�M!L���6# �����>�S���E��7v����;T�;A���8��z�蕈v��ڧ$���"��p�0�@���TQ�QIݞp\{�O��@���8�y�>��U�CV$���bQ���o�=:ի�B9��q\����?QQ��c�!$���g��S.J��󓍼���#'�ִn����~�r��C���Ҧ�v�b�T���	�#��RA4��L�g�l��h�*�[�Ry�@�t�p��U�/	gP��(�Zt��lV�0$A�\~�k�	?�s�@n=��=꼊�^�nbp=x&��1�b���b2B�I'�9�ߞ=�V�K,����)�FG����M�����~�n2GNx�{u�J���#�(����s�*S3g���s���E5P2�Ė���R=��1Gr��pI\�N�J;�&�� ��$��~z�ƦM&���*�+��s��z�P�ie
�۸1
ǁ�#�G?�YI�����
�l�����K���ۧZ��E��e����Q����Y2�'f0�ۀ�O_p#Z���N����n��H�3��9k��I$�L�����e"��e@��ʂ1W�7	#�62�j��d�:�����|�Gn�V*Vgd�ֆ�ڍ�� �P������֫K��z�O?�®Ds!Gq��>㜟�����S�����V��F1��+�#U���2��6
Đ��s��P�b�@�'O�[ڮ��|�8�3�q�ONrq�s���ٮ�D�Ve;D���[��5�GBwD�2�|�u%I��N3P2��V\H@�x�c��Z+WQ�v	�Xy�"7c<���?ǥS6wm=�<�s��
�@�>V1ɂ���p��zt^��,���1�y#��)��)�8�&%�C�rrG���|�Qr���`�G0����$��HS�9��MjD�X�4���eF�#��g�
z�;�tq�^�`�O�0q��?������w���aVV�8g]�
Ϩ�j�2s�-��
K&�'���K� �6G��F'��>�)_.ég<�z��7������7u=9��Rj*�Q����1�'?�5n�*��K�:��IHb�l���=;Ֆ_ݦ�?w8�2z~>�0���d���������:�� �ʩ����\{�|U��7(��9^��3�~q�x��Һb�y�I4K& W<'?��l�-��D��O!9� z�~)��m͝���>��7W,�aB�;/�9���Q+R��$a�͞pc9��_�/Ѕ��9s��;[�v�1���Ehʹ���e�؞h��NM=L�@����d��	�v�����Y
�	�w7L�8��V.")��,�$�=�'���=�81�[#8��g���a��E�
�~\w�ԁ
��,�p@ rx�j���o����R�}��q��#�?O�T���b��Q$�
�vn �g�O|g�z������s���K��J�M�\c��By�֪[��� ᱌���7�5���R�ml�0bx{g=���#q�}?z��+[��Xym�2F۞3��'��჻�3����9��)1�[r&ȓ�d��ǽ5��x�C9<c�x�O�6$n/�Ǩ�P�-�>���-�T��Ȃ%Ed����b$ݸQ؁�����EU1�v�_p��4�F��0v�`��'��c�#�8���\m���[;A䜞����6:)a�Dd���Ӫ����e�-�&�_<�3��s��#?���w4��K$k��<�%Al��w@A�d�&��_�I~��p��h��2�ήc�h�
�H^=@���Ǔ)��Kd9�S�0q�W)W���LXg��2"
�cw$�y=�*YDvQ��w���B0	?�����Q��w*���������;�I�wRɵ�aGn:d� �?
�0�G���}�^5X�e1�����O����(�F��U��{���Ғ�N�\!����H��w�s�8���Y�f�$B$$�W�l�?�X�1,|� s���zz��i�X_�n�n;����=;Vl���+��މ@�cUL�>탟�iUUw�߸屁�U��?�$���0	#vz�9�qR<m�ʎv��xӧ=��)�7t+#"�9b�7�{�
�F�R�6��3�����х���A��ڡ%��B7dr?��O���#c�ے��A�>�k(�+�7e�c�3�n���	P	��m<�?��¥,�������c�N����E��A�>��P�NJ��}Z�#�FB!ʏ��V��ީ湊�*�8�`��Ϊ�Z4lG�Ü�=G'�ҙ��gi=I~���qs�V��J�F�w��d��ϊ��F�Xt�s��ӯ�)X��ա���F3�s�cS<�!Ua��H8Ϯh� C�$��=��{�V/f�K+kF
�Y�Q���<��v�@�l�yU�`���z��V�G��hb���I�Y�#�)݃ӧLp8�X���pb�I��8$7��q�w���.��%���g��}N+hr�L�6�)�4�nd%G㞜S��fV!W'$� |����{f�)b�*ߗ���犷��<��BU�3�����ӧzi]�s�j5m1	e0I(�(yʜ㞀���Z���,��ᛀ�G��V�Ȏ1U!�NFX�9�;��k܅�:�q��JȘ7'�E�����S�H���3��NjG;���N��J�ad,DlX��
�~�9�;t<���S����n�]���>a�8#��0��ZXԩ^|�u�1��k�rî��RĠ���w�t8�zɛ\��1!NᑽCO9��#�H�đ��仌����=G#8��?ސ� *�`��9�c֚&+�b���?z�(\��9#�Vl�,$u�F�H�;ps�w���$�$�ԓ�����9
���~��x�c֝Ou*�2��nm��-���*̱�Y��*��9���冐���yJN
���s�:v�=N)Ĭ�`lcp8Ǯz��t�v�
��)?7}�z�zc)/	#i���8��n�'�^:Sd"��ɵ]���	��zc=).fi~�U
�>m�O'��VJ�
��V'dx��#�'��s�U�cr�{������>�9��7�kKU��q:EjA3nm��0E?3.F1��5YG2ʱ��N�F�{�3��Ozl��ʃ�����
Ue���6㎧?��ӡHLt-H�L��qޡN_�z��<�g#<䫅������q��u�5���՜������2��lT����+abV,"��U@�c�S&Z����wBPȬ�s�7��8�O4͠�Q�NK��98�����n�H†���{��K�ʦ�BFI��{�Z5��4$acTf�Pv�ӯ=��N�ҮIU�\a������Pۨu�8����B�Ol���:�'�QT\��\�@B��I��Jwv��o��e�e��;����ǦOs�U�![u� t�Y8ʲ#� ���KC�_-Ēe`�� ��}8��i�����R�����23B����y74HQ�J�*��e�I��nH�}i�g��(@6m�Q�W�ϯJnPJo�mIJ��A��q֭��s��.|��P��gq�*^O$W�n�72%�čm!	�&�7�3���8��1�?��P6�ێO��N�5~V�u��İ@!�;�$wV8�60x8ǡ��l\�U�;�?�OP��#�yFi$w����<}�py¦Yq
ZE+�� �?#p�.�\{�K���)## ���F�1�x]�/��ާ�;F=r8��ZFR�n{hB�=�Ȟ�9�%q���d7���Fzd����`��s���Oo��U�b8,�P|��=A�'�@����%d�F�2)`�n�a��x��v#���P�X�,�(�!�0r�5G����8N�Q�p:�4P�)��!��)f$�A��c�����Z2.0�0� �z�~�/SX�1!;ʤ�c��N�1�|�8=I�8��D@�#�	�(���4���t�``�(����rǶ(��\��ǂ��矗�q��m�m�}��
"8��UFW
��$w梎�y�������2pq���sש5,����I&F\�mǩ�s�wI�r$�A6B�v9�v�u�i���V�[�,��d��<���T�oqn�%��Y,�r��:���z�,d�۱��p�q���qҤ1+�fYF�d;���~l����{Ka����U����Rrs��ѺJ�bb˫d�	'���z����Ψ鼶8�^���w��銝��vB�W�#�(�3�~��Q�.v+4k��w�����j_4C���#���G�jc@J�i
�q'�GLt<��D�(��6��ۗgb�����FW�ڟ�J|ڔ�3$Ѭq�H�T�j��1��1���ڒܲ�$3HP�1�;d�׹�c�t���m��+��p�=�N<�C�nہ������Q�Sf�9�45�o�ZV��tW8�����'�܁�Q\�r�T)����A����S]�a1H�ʍ�d#s���� f�܀�Pc�q��3�=;�7m
J���&L������@C�`��v�A�N^?�Qr3��BoM�o'�8�9�O�,PI79��!>a<{w5-\�d"M&Œϴ�2�ldo���&���ݳ/P1�����E���aps�c�=�VKz�,(X�Fn&;�W��:�ڵ�.eR�E�F8��E�.�$� ���'�AQ,,c_+;�Py��J�6�����q�@�u��B�Df
�A�0�#󭬣�����%�"@�����ё���~�
�9UԔ����ǵ>k�$�̓*��*�>�y��4���w�|Ң}�O@���:�ۨ�nŨ����w@
p[9p:��>�J�p�'B_p E�A�ă��P�G�R��g�\��ך��
�q��O��O�I�i}`N���F$�>�Px�֦��U�%��]N�o�����SA
$љ�υY|Вcp�*	S��O5�E���ea�'�w�t⚍�SQخց#`�E�`�R�E�s�c��s�Vb������猎�_�_��(�iX��7�_�wԟ��g�4�FU @�2zn8�ӏ|��[�ӭ���b��
�~�W�O@}�zw�}��B����9�~�_y�������<�|�ϧ��2�Q:J���
�UA�1��}~�����DS���{�j��%�'��ߑQ$Q���<���w�x�#��>����`eڊ@�t�q�� q֢��$�
�%��7o��pzc�w�-����j�s]:�񪒎��H`�����i�+��
�8�<6N3�	Q��gڙd
w���##���S��q���9���
��;��b�hh���@c#7'��PG�;�JxH持'��88�$�~;��_Ɖ'�w3eUs�*=�)�2"6��s������`@��9R��z�X������I�`��Nx�=r}2jٝՎAo�7dO#��;b��0�M��Þ�����j���b��2��@���;ֱvZ�z��c��	V�[��+ 
��ۧ~cOc"o1��;���~�t��Lp��c���ӶO"��ט@�w`Fw���
m'���
o���Tċ�l����s�s�y��2�9O9}р��|�1��L�p+w�qK_5�"1��?1� ��t�5�<2o
��G�w�j�T��N��2���=p9�w�T�BE�2,�d m(z��}j�Ѥ�1F�F�$���sB�l�L�T�o�2c9��s�Oң�6��'���W�9�c��j#"�K��I#-���;J�8US�x�9��R��m#�
	ہ`d�O;�>ܞ�bK��#YF� ��~�1�ƒ|�W¾r�ǾLS��D�2�:0�x\n��t�\��xt4�o/`r�vV�������&��Ml �$Hό>K#�n9��Ҡ����~T�Ȁ4lFݤ��3��Y^0��@�3���N���Y��ǂ���X�ot��%��'ׯ��P�ЕR�3�3�&�`%�+63�$�x�B��YT�7g?�4�*�1;�?On��:H�b�������	=�${b�e��$��B���`J@�3�J��x�}=rH%ԕL��y������֤��9��;�
�3aN��r�c=FN$�.[�O5��.Z8�;�+2�#8��A㞄ROA#� +p�pG��q���)t卦U��S�I8�T`g9��A�iJF��}@��\�}z�is	��n�r��؀nTW
�@�� g��*x��oYIܡG��}qӱ4ֶ�nB9w~LQ��RIu��q"�ň���q��H�=�'ҭi�-��aX+0�V ��{u�J"G�a�*�~iT�taϰ�=sV��a|��͉���9�ӏ��y,YKI,��^��2`i�܅R��iJ�uG;�1��?w�~��H��dVֲK.�ȋ/�
[8��/��
7f��3��F9��)�+O�J�����$#9��8��д�W��F@�d�;����859����lp�gg*�����ќPs�i�3峖Fgb�2��z<���R�S�H�a$�`�гs�BG8�y���H#��V C!$�['~A�9�Q�#s�g���#�����y��32�������:t�i���y���rK�?{ ���s�t��W �3���oBAǯ�{�0� i7�����n�9#=����bye���x+�r?��3�s�)�ʎȡJ��B
�9'�?�%��'�E@�8ݳn�۷�s�~���U�_1F0��#Ǫ�Z}�q|�Kᔣ��z�u��q�$i���QS�V@4a[9�����z�F+D̛����a��"X�,�~�nQ�Xu�x�\�H�{�[{����`A��&2P�8��Z���b9��(�68c�r�v�ry5
.��8%�?˕V8''ٺz�-�S�]�F��@`H���W�N &UYD��,����<�Ƭ�|��1������8^j�P�d�c�1���*6��9X�.<П�&B�6�x#�=�S\��d2�£���<�^*N@8�>ާ/���E�x�w!!�YI!x���o"���Q�=���qɌ7ݑ���%��N�D���T��B�d.����	�p�<�9Pw�mW���'p=8�t�zf���L�-�,�^0�q�H�03�z�Zl��َB#`�����8����-\�dU�)냰�A�zzzb��e���0�YB�vPI�_�������8܄(��8�q�{�.1%ⴌ�6�2�A$���'=�8�T[jH�bA.ə����>�NEB��
��Y2���<�Vd@���r�˒T��$���Ң*���F	�pq���F��\*�1;D�`�X��0F1өϧz�2�HF+�$)�A��1S������)l�I�B�J�
��2:z�J�	ݓ\4
J)yA��!�PA;v����,�2��"��P7 # g��s���i�4��2��,�~�(��s�;(#��!0J�� ���`�
��-�Ŭ+g8{b�Y[~�q��c��m3[�*�
ʥK�c��,ü$��'�����=��,��6�e�(�={���F10*�x?yHݞ����~94�T�� ��{�_����S�F-���d��T�w���=�Kl��X�FY�yb~]��Q��U#9�î��2��P6�'�<��j�!ϜP	'ȧ��}�n�q�O	Q&�(,Fܰ���\OlR.�fq!\�`��{����S#�w$�4�?���I��F@�j!F�)�5���x���9���=jĶ�[�&S*�|@`
�\c�&�iɁ�G%��&ߘ`��p�)4Z�[1*(^0	U<x�=j�*����#�0P�%�y�<��Z��@��rW�>��V1Uo��:Ǯ=����g6,e�D��v�
���~@�=(�7-�Hw��I�Cߏ�P��y<��ӲF%u	�H�� `�	>��j��rR�1O5��ޣ�ү�$O,ᕲ�q�d��v�O1�{g�����?"�U�tBH⬝�.��w�.ӹ������g���[��Q���)ݞH'w^�N*ի��˨12��g�?)/��!f��B8o|���[8�-hԳ�)Ao�)s�O�F��oO�no�a���I�c����Ҫ.�xC��s�y�qI"!|���.}O� ����jI-�|��w�m#y���}>���Qے� �G班>�\NUܖ���Oϧ֤�.f��l�~��L���?69*:Z�[�	�r���5>Z�c�s��8 �WӨ�j2PI�Ã��O��Vv}�-�*���:w��5�/
�#w_L�<}x��N摖�,O��F�cd��>�0E���#�?LԪ����2�ĀI���'�u�[#o$��`6z���~s�S��-�V3�r�f	��ǭK�hC��@h�'��מx�j�N�X+�m8�8�P�XZ%$�2�����&�Y��*s���p3�N;��E
���%x|Ò03�jaVU�p@�Aӯ~��2*�GF 0���i_Q�B�Q�F� f�v�r>�zc���VY3��xl����Gt��֥X��c�=�?�̞�qn�LV7
K(�C�L�r��M�w��;�v�,{�6�v�=lc֦���lF���;[pA� ���98��ƭ���<���•�S�I�ʝ1����<� ��(�\�!~�9����1m��x'�G�ZH�x�,� ���<��.�7��FO;v�8�qצO�Vbe�(8o�~	��ǯ#8�i�D�6̡��;lD�#q>�>�SP��B���ǡ�׹���i�;�I��o ����Gn���U�w`��K`��ȟv��+m�$p~���A$*��"�8�Nz��})r��J}�X�ئ�Ы�o�d(�$��VfXY�Q�A�Y����x�{�3���P$��w�aw�l)#���]�TW����'������W"�eA�<�	>��mcGc�9U�U�� ��HK�I�z��VJ D[�I�.�j����:�4����1v�;�Q�K�c�r*8ۦ��c��~ ��P!1���(�Ǒ֡U)����zd~8��b�j�۰����8��4I���0����b���H��<�FCb_w�\u�
�~�zU^�5bŔ�݅��c���?}��PĀ~ldz}H���Iô��I$d��{�ErP8;2 �hB���{��}Eh�dc�vD��Tcx�$sס>�����
�=� g@<�H�E-��Dq8�"�U'r��>�$��V����e�t<�Ϧ8���S���iH9�J[��o����8a���c�Օ�`H%2>|�c��T�B�g�:zRi�+[)#�п6	�s�?.}�V�yY�r��b���b�T�a��
�0A��׎x�z�יw\�
�98�@{�y�BV�dIF�9�1�UE��dbz���s��X��e}Ѣ��ے?qU��`T�q���-��"���~��Nq�1�<�t�*�i�*2�8�;v��I��m���z�}~��D���WqڴM#(�]���9b�'�Q�����$���	���C�־v��A�Sק'z��` 'p�;x��+9H�4tl����BT1�+F�$��\��ןγ���Hlt榊B�|���0�Y)��n�.��%0Pu9$�NF8�U�6�]���z�OƯƻ�ؤu]��2O?C�P4/�*H㝻��5���M2��j�9N"D�d�Ibl(8�r=�
U�JF�Q�^�֭�mps�����@����m-$�3,^^�]�4�K��'���:w*�J�U;\�RF�y�?)���z�.�c����I�$u��E`<����v��x�F�H<�[�����NqY�k
�1e�f9>_<c?S�R�!dN1��{~��8yJ��2��U��?8�7g��w�wj��R�c��l�9Ps�i��Z���4eĒ��#�`CH�<m�=�'�9Q��Nz7?�9��oN�V��(PA|0�����~je��J��.z���߰�2��)���+lc$F0^��)/�˔��X�Q��>�;�����I��T�?J̸s���ܜ�V#=zӓQFq����
�;I����P�������Tc�s���GZ��F�󓌀��=���J��5ѝ�g�&Pv��:`�!�� %m�@$}�v��Ƕj�9����)a�霁�<g�Wlǖ�I�v���;6�'è!r���-JHB��c��v�G=kE&	��S+��<���3Y�N�z�Lu���Q+Q�ة�2
�瑑��Z�� �M�:�b�3��pi��$�d�`\~x�j�$�U����{�Zq����˻O�UՆ����aS@��J�Hd��8�T�1y ma���a�$t���'�Fi����ʱ�	#�h�NzhC�5p	'8Ҧ��@w��U`��R2~�;�7.��}=�jO��3`��s��{UX�ձ����=2}O�^�f�b9ʜ��?��\;�,h0��Apb=�@���Pڼy>a)���(2q׭O6���W$���Lo�N�~c�^1Ҫ/��\��*�i�ĐnI�b'��\��ǭT*�R���`�ۿ'�iI�:3gv���v�0=zRH���Y���N��UλN��|��r?�F�dTm�p3���٪]I�cX��)(�Y7��l�DZ����G����8���O���g�˩�a)nzy��6��`B`�{�jT+#/�;x%N���{g�f��&S�9u�B�#���w �8�q���Y���N�}�^\�a.���O�a��`tjϑw1�X�2ā�֍�N��/�A�m�1�A�3�=�8�V�c����rq�ϵW)W��kh�pJ����jV�S݊�'ۃ߭Or�m��B�*A�ld��"�M"�m��g�XB�ۿ94�d%�%t��ko@Hݷ�:��*X��垇�$`�o��M�(|��v��+��I�s�G�1��X�� ���ȥ͡N�L� >6�����>�I;�\�qr�唫;�>n���1�51�͈����>���S^5R�X1�89��߯SY���$��I0��#
�O|L��cJP|��y$�ls��Ko,��GW�aب<u8��ϵ�wr����du��'t:ddY"|��:�隄�bی
�'n{`��s�I��@�9Ž�:�l_�f'��-��‰>��ã���C6�T��O^�u)ڰG�bBD7+�g;��H.�Iݐ8<����Yٌ(��#'��n���N���<�*3�\`���Ӗ5Y[7ˑ�?ƪ���e�

���8��օ�@ff�"�;�_���<~5W��Ш�6>@��A�v��k�>B�8���W�EH�P��^�8o�Rp�x��U�P�k��:v�Oƥ�4�L:3�`���zgԚ�v-��x�����E
��P��.rz��=����6�q4�gm�DžQ�z�;����W������:��ny�ߧ�׵i��W6��^��N��=9<���Z}��	8�1����3�kVc#o�G��|�W�:�zG�3[ǖ��cQ��)ab�!]�bF}�����=����N�o垕��`��:v�~0j��j�
4�j�T�3߀=�?�^�(ݙ��jF-Ū��L�	�>\u��UY��v�Ig?3��U�l*�@�*�9�2}O�Y�7 #���
���iI���rz|ք���,1�z�,r+�8F<I^O�ARɖ
��z��N��y��,�w��;���� ǩ��9\�)�"���30,���r8���Q�(�!�0U�������Қ�)���Tʃ�eg8�c�}���Y�d�j��QׂT@{s�Pc�GyX|�r��˟Ls�Q�8��q�%w+� ��*���x��"�S��2��p0r@#�5��^����k0܃yɏ<�y=Jl�"-����a��sվ��犒�w�S�h���$��ҕ�9R�̡2N���2�_̚H� �v��~�	�9��3P�c��8\��������<�L�@H�a��V<z�O\��z�q�]l�Õo,m$��:t�<�=i�QZGi݉f�;؂��s�OO֊����k��(?  ���2�dQY�oC3=�yf;��9� t��qӀ;IiMq�tk�v9D���֕���<nS����n�:�Q��Ld������1ҡ
�M��W ����=�zz��GO%N0{�������'��d)�@Fq�t���l\-�HP:!2�<���F�Ǐ���I%k`��yq�e]���68�<z�N��ku�N��w�����2I�s�I�9$��#y�G�r��'ӑ�~=*+a$Q0��ѐB�+r9
��N}z��d��lD�U�R�Q��	�:c�ZIg
"*G��r=��''3D��*�w?&��y�$��>��<��t��<������^�i��R9����<�3��
3�^��Ҥ��xV=�F�!�L����P�+����x���Ŭs�����6���ga� s�<Q#y��!�Rvqӏ���v%gV�f�ݎ��'%�'�<�H�s+�t #�l�F8�1�)�_����@-�N���G��|�@��1��89���Q�-��vl(6�q�v���N?,ԉ�	����W$go�s�<�U�{g$��=�C�)��]�|���l��Z}��ea�ż�9?"䐣�Sǩ�ZD��ļ)Pq�Ǟ�D��|�,��\����5:[�.^O3�n�=�������G���BV@<���=�b	|�mђ$2��6��k
�6bN�ϧs�U�4��ƛ�	%3��5�	����;��[im�#>���#�C�v����*�����Ρ�
�`�c��qO���/�A*O�A*s�'�;z�6�ef�D+n&m�B���ɷx�=�zT�n����(x�Y\|�r=3��g�<t�W�љ����V`0�s��u�\�T�tm�]�N�q�ښ�4�Yl;�TZ>�5�� �bb7�ۑ�x�J秷(g��@���V��Ůg\�I��Î���+6e�8u~7(����Nk*�:i˩T��N2�7'v^}OүN�Kv0�S�������`mv��AJƈc��78$c'�8�FY~F��>`	�>��}�
ϔ���r�Đ��~?�oJ�8�@$`�c㏥XU��.,w!�7q�A=r?#N��k��	ː��:��RJ”�#�6����S�~L��0'��J�c9b��O#��2��UB���F:�#�^���S�,�@}Oz�\�m�<ng$~�<o�G�,a�	n��"��(pʧ�'�{Ԭ�jyݐ�z�%c)L���2���qǭ>,��n���_j�
��1���m-��8jx�t�g�r#��]z��9�pG��h&݊��[�4�f�H �3h ���sLI"����oU))f�B��9S�䌎�/����#��ߐ�㊬�Ű'���"�?wQ�2�+�98�$�
��y
��8f<�0��N��m�m��� �rG>�ʘ�&9]��X��[=�b�
����#8$;}8���jw%���Χ�9�{��OQ��8$��뎿��V�cDP�j�%�!�b�x��V�1���3"�)`c���$P������9o|�c�Oum啒<�~T�9��9�R�J#>cƎA���q؜�m�m�ф	"*$���H����9��]TFX�8m��۞:��޴��60�e�����G9#���҅�<��O�ӓ��X9�m�X�~�c��L�ʇP�'h�=���ڞ�?�X�8�L�~`wO�>�#RJ��;H�u�R�������=>�q�
���"P�Ef˖�Y@�~^����-Ѱ~�~�b0}�~	���`9��F�q�M�qOĸ��(��06�S�2>n�8�j��d�# Y�a�~�N���j��U~Ee%B����s���ҫ�,�C�$n������55��w+F����IƩ]͒X��]��������)�ۯ�TR�@+��t�q����*��]Ι�NNO�P}�*���mb�a�!��nt<�8l�#��(���̹��� 28�>7nrGNq�8��Z��'��Kn�T�3�cn
�Б�z�c&X��ON��X�'z��1;s�KWfSn�)�H.d�{�l�Lu�jKY��)��#5�1�j��s��c��V���4w�8!J��OOΔ�OCHUmX��,X����j{x�I��@�jTv���`2W���c�$0}�1H�G$�)�gqJZ
.�َp̠Ěő0�3�8#�ю��l����3�}k&9P7p��g��S'r�����"�%����4��ewڞj�u�����Vf�¨2g��=��Q����>S�Nz�VW;y��� t]�H�˸�;Ku���rJ��vi@�J�ӑ럩�DY�5V$�$�$�fa�t�W�3�O�Nr�И+�\�C�[��F���d�0���5
�/*!��ʠ����&���@����}�2p:v�Ͻ$��n$�Fz`FNG^��qR���,��9Pܒ@G��oQ�����gI����ˀN}�On��g� Z'�eB[���s�����
*oH�fG'�\�p	��������< d���&2�ԅ���,��=i�r�3��w��dg�x�j��6�r2��:�u��8��߸c����z��z�ݍ�k�Y��v�$�>�(?J`w|�@��c9�?n�-�'�Ue_����:�E�r`�t鑟��ٝ�4��#d��#���zp:U{��)O-�#;��縦���АH� ���C����@N?�^:�$�#��BIS�pH
x���`*�`ȋ�P��	''�OC�oZx�U�A�9Ž�	�����2����pI"�H���,S/%ʃ��
�@<��rO?��;EB�]�md#����֥��rJ��Ϩ��НՏ:_D������G�]�,ȥG<�c'�b�br	vVo�N޾����M��7V6�Ò9��#�#�CZ�V3��ulNA�5�]�t-c��L�gH����ۓ����"�h�qF��#�#�s!��ck1�z�zjW3�G'b_��X��w�A�ߍ68dn�9�O9�ۚ��e��
p8���ZzH���y������`��g���Zj�qJM"��iBբ.��m��m�yw�L�$>�uV
X4�+�d{�;Չ#)D]ν��Ӯ@�sU����]���?�֛�*M�ί0����3�8�3����e$v��	^�u��՘byf}�w(,w6��Ӛ���!�ܒr9�{��Vv�7Lk��g��U�v��#�>��O �c���T[����Q�'b��%�n.X��01����ړs!�¶㓁ԑ��u��0��VpC�&�S����TR EI���t=�$�����oa m���'�/�ƣ`%�C&�q��=*D]���=���2�vݠ�s�
b��a Ƥ3`9�e����ӵ	
�v.���+�G�Uj�$$�f<��p����8��T�S,��2���9�N:�q�z9Q.l������<�����,�9�)!T��5$p��ʀf lx�4j�ȘPHg8����\����2Yd����[��׸�>��m��2�*nb�2C������T��2v���*��4�Y�e�T��~VHf�Ǯj4�y1&!v� �H��N2���5�d��Wv܊���>oƘ���ˀ ��v�t"���*��?ȏΡ�ŷ˩<Gȕ��s��|,�0�6����p1�2d���z�N={f��@}���I�q���늱/x�ӷ@2s��02��I[Re6���!��w89�ie@�!�o�9#�V�2'�)2��o��� ���S""C��3���Bnȩ"F��?�cq���	<��j���xO_�j��M��s�³�K��y$���˖(	�3ש<Swa%:goq׎�EMm�e.��K.	�����Jt�#�
��˂���ߎ��+s��n=p?���,�YX�Ԣ�>��lmV�G=zP��
�8� �?�ֈ�� dڧ��=�;��a��C4�,�xnG��8�s���P���q�>���=�h�O�?
��:��JHԮ�$�%I�I��:�k�9+hZ��W�"�0��ל�����n=:��4���I�����G^L���U�����%Sh9�c߭k޲����<cz��b9w c�C]0�Z�Jv(h���ƀ����s!�zg#����X��k�Df�:/S�ҧ��`~t*ٟ��Ιy{�8w8<1~2=09ڝ�9�IM���M�)��}�A����J�d��PJ�~����9�T"2��YX�F�z�����"�.@o�~�l����d���ʗDR��q��:��g�̭�xl�9�<U�K��2�����<��&O
�-��氛:�F�/�e/�სa����z����U�1,�'h�$�I��NEX���ΤÀ1���b�O��l`��q�y��s�/��(��>aB��=�==��A�̉n�-۸P<g'�^�w�Uш|r3��x?�Y]���E�D%Y���8�>��f��J�TI�K�b�:�Jf�(��.O�䄟pە�Sr'�*�'��VU|�2|��1�����I�8�=�O^����jR`���$���e��<rrO$�N�%�&�…ٷ��zrz�H�O��}�>ֳ}�2�v��'��8>��[7���8y1�
�y8S�{d��P�n����9Sڭ��#) ��#�>���j	�F7"eK��'�����Id;��zu翹�s�%�H���HGd�Nx,	���{��,R��kI#څ�lc�z����/M��'�劻�ɉ�0Hc���������ߥG{�b����B�''�7����D@Ȥan' .y��y�=�nb&$�\H�NYz�=s�8$}�/�ѣ
�c1��RF;��$s�&��I$��r�I\�N	�O�>�-��G���;��zg��,�҉����3w�<�nH�҈���܂��l���*q�idm���3����N��}�!�Y��/hp@\���*���ȎF�����=y=q����B�;�|�S��(���`ap��
�����@7���8����$�j�.�[C3\�h�Y�������n�w��X���4�%8Fݹgbq���S�{�~�h�<�JG���;���O9�~FJʯr�S%��9;�L��9 �:h��ل��dS��kf�����&_6 �F�ۀW<2���-�+�Yh �9�\��*��#��N2	ܜgha�x�~�n�AX�v��ܼd�(����j$��)f�E����s�=�;uQ֤��-^F�(�ָFx� r9d��i�	�.!������p�ǧ<�ڠy�{�s��^z���k��b��
Pg�[��׿�x���/!�����q���L��	,z����*	��,�$�2=i<��\�';B,�e`wr	�=s銗�ӹ$y��Fv�a��$9^ը�"��4����t�c����"	o_,ߞA%v��s�yG�l���k$�)�t��U?�|�(r9!Onƴ���$�d������L�eUH<x����ޙv�:�B��B�~c뎹<��@�Z�;[{xQnY�$�UH�Q�!���g|���3��~��|�6����M�'�rES���z	q����ȡ�#'�S�s�N냎*����ެX���p1ߢ�8}�I����%��펾���:�Kt�Gp�	�HUɎm��8�	��ڲ�GDU�*���I��)�8��^�*_�a)XT21;Y�^pv�~	�<(�C�HC����]��H�2��4�fw�TP�H\�|��ݰO���q��H��C7�0]��f+���q����GZIai�G�e�ڡ���g�q�z!`-�٥�{�ڸ�=���4gj�s��S>�z����z�w�arDN�4��[{/$/L1�	zj��*���c�oRr9�\Z6��g�5ԗ[Y]1�9�+��$��5,�w",,#<��%��<�vߡj-�)-���׌iCJ������׿�SmHY�1�pm��ns���O�N
��+ܾ6���[p���:A��Uf�8�[�ڭ�|�>^'�t��=����Dd�
��p*	;s��H�qPF�u�g}N?�ub���k9�1.�
���tnz}:�[{O��噕vm�'��9�}�S�j�r9���_�瓸cی�Z�C6T����0}ϷoȚ�cX����v��s���ϥV-�P����.ݬq��0/z�hiq�����s�.SOR�@�܃��98=p:�„�܊��̀��0s��w�קN�Ϙ���'��q�<�ҡ$�$dtu�T�S����B)���7�;�r98���.�K\6A�Iv�$u��
��2%S(��x����޴I�2j:�����4@����z�����괭(9d
n�@Ẁ����@�b	�)f�CM�s���?��}��be�!z��` ����>j�|���0|�Bp͌��x�c#�J�0��YD㪺��rx=>�Ɲ�>F�ۈ	�D��~�T�B���S�	
v�ž1���R����|�̪ʌw�@�T���摥1�
2ľR��p9�;�ڦ[v�lD�y�A����
4,����)H���I-ӯRO�}�h9��&R�	��dch� Օ�p���
��㌞�3�J�€Ŕ��~V�~~�����p	-�q�3�{֑�ٔ�r�-�:9l�c����'���iƱ�n�@60�/��ze��L�,�Q��Rq���⡸�H�IP�������s��V�G'��cuH������D2۹V�9�������>R#H���	,2OC�a۱�z��$� ��a���|�T�}�w��ر8n��*d�4�^�ii6C��UH���g��U�q�ne!N?��Κ�m�fRry횕d�#��YH#���~�=k.f�l��d3��T���G�����:~u+������8��Ӟ8�MΠ%4~P��#����dTSȥ hU��l��bK�O͞�
��JW�"�0��is����x��)����"H��J�1�#�a��lԶ�}�^2��I!�'$�c�c�jx��r$�i�� rWz/=3��r(�{��+4-�K�$BC'��ϧ�|S��q
 E$c���H���jI���/�c��\c��;�4�'_=��^DUf	#��9��ӵ	[po�Ɋ+b8
0����|�1���\�Wt����q��7��@ ��� �$r~n}r~�w����P
ʬ#��^"~��V�v�J�.�l�2��m�x?w�EN�	x�k6�m#irp;g�\`�qUt!s�6��zs�֖�S��K��p:�9�����丷��n̬c��/��z�G�ӑ�^�z�բ���X�\B�$��|�����3Y�z���U�a�u�9��<՛Y9b�UF`Gק^Mh�kC��3�,�H��'��?
���L#S�;Nd8�z�H���H#Y�g�YW������8�1B�ݣ8]�nC.r�>�R�-.������A�0cL1Q��=�O#�椎F��̀(a���׭Xh��d}7�@���ȸ�rU�r�:��nV�2�i�Ψ��`:��A�р#��4���قJ�~�������xʖ�,�udq���O��[�1�q���~�g� Sj�+[ĒK��#k�,�Hݍ�q�'��z9��>��4f%l�xeUːO����W#�
IE�㌌��:�.H�P�!�끞��?ޜ╙���"����p�Gl���q���s����rzs؎l��ܲ���ў�9��J�y,�(�W	����	?�5��ѥ9�FT��1��,8�=�8���)_+tDI���J���v��F��6��<q���>�j2X��v��n9'�q��k�F�U$y033c�x���"�xg�x�ǧ_F��ڧ���ݻ��0y��J���r����zp=2E$�B�,�ZF�|�2r�#�M$��2����œn=���Z[S�S*��i*:����`���~L#�,��!���`G���ޏR8�����Wp�=�:�*yJ��7`|���s�'�TP�)��B���������msp-�ES�s�#�T���=�4�3q�F�P�<�Y
3��
��� ��C��!�\�%����x8��F<`00u��$d�����s�H�2
M%�@��2��d�#�4n\�%N;��� d�u5�Q>#�5�2���Y��Q��ђ=<Sm�0�(�b1��8n��W�9�==}
�x-r�,�1Ϊ�1�~0)���qU�nr�>\9ϸ�=󎔍�H3BIF���vKm2zt
, VE-�0p1�á�q��2��Ǻi$M�p͞����֚#s�ݪ����H=	�LV�d����� �7�.x�qЏNOJ�	V4��"e}��0x �A r�iחk��m�E��˂=�	r��Go|��F����v�d�}M v.�G+!�\�Y����8����<pjŸD�9Z`�?9���^��b������hG-�_,/�pH=�v�jU�6(�wRDܨ�0A�2=QZţ	��Z�4k4����*�2�Nq��PLҙ7����Hʎ1���ӵhD-������l���pPOc�>�#��Am�K�.#�k,1	��B��N�[�9�[}����t[r���
��<�l`��%��U�GP����y\�l�`����fFI-�<H�`�9㍠��w猓��<q�'{�Q,��;��1�x ���
�W��	&A)�DD�9�q�y�y9��Q;�1D���񽕰����N9g�f*����uH���|�֟s��U72��g\y��BW&��k�$2�&�.F�@K2�g89�8��L䕓q!���nFx��ʜв�O$2�t�A
�=���zջ8��h�Lj�H=W������i�b�7�;X���d�w7'
8 N�j}��WV�r�"�D��6D���	�A��;�g�ے7d$����-�8�7��$�R2��:*��ya��N}1Ǹ�:��v/hR���8�yH�;(gl�}�7p8#=��Q����#H���󃌕+��ե�@��%�M�$c��
x }9�5E�5��`�!-�o;A�!�'���	8ĸ���!�nC>���P3��9��9�r
T��l����o�)��z`���m��ጺȱ),���Bi���T�ws��U�;�����}��m�n�V��6�=71��y��-�ĬJ*��rq�gh#��j&��F���:�=s�z��QDҩ�F��<�s�O���ɛ����.��Rx���ߚa��h� �ן�c=�?�H�
��\(8$I�POw}N*[
�#�����W۵Oˑ��n�����,<�Z��DF�UX(;�`q��4땅n�m�Qv���H=N@Q�ׁ�s�\4
2E)�H�!6�b8���>���3$W�B$�T���26�g�9��=C%�M�L��c�PXs�r}	�jq���:�# ����q���'�N)�A
��2����O'�'�����*�!�p��hN�/pFx8�N���i�B�
��b��vl`�:�L�<��.[K6��#̄+噷�[��q������\�YEr`VC�����7)����5�cS�Ē��Q�7a�4j�|<{rc�UXu�?�=�<:6��eP�X����{��B��"�r�T��.ON9�?j*C�����NP)�R:v�Fs�����6\��_�>\��F3�_�ڦ����� 0$�zw�>���r���c��pNƧ��7΄㝠�\�u�*��f�O��Uy����r��	� u�*���oaN8#�^:�I&2+�2%Q�O��>�4CI$���߿QZGC
��zͰ�\8$L*�I�v���$G<dw�h�"�����z~x��-�+|깈����3ǿN��	����r��a(��A����x�Z	 ��2	 ���g�㊈�������N�5-���r��$b
��3ӧ#�Өm��I]�؉�@т��e+���y�V���:���om%��C�$�w=��~:}y4=���0���p�P`���j?g�"�i#��d��|������!V[5k���5�`(ہ8ېqY�W%�p��zu�+����9�}�x�zw�Ҩ́�td�W��z:ƥ�uS�R�C8`�������pj��2ZE#4$9e��0��2LSI
���)��_�����JS �m���B�3������CNmJ1&�F	|n#��O�t��͟/hb��Ӡ��"	�O�2�v`y�知:WFW�F����:{��'�i-)jD��`���@r��9'@�s�Q��Ib�@��?�Y�H`��'��@#���q��4>`@0O���<�h�F1�H䎽��h#���cP
Č�=���o��YW�I�=i��7	[��8�Z�ܯ圖a�yʀO��SB1v#�?��:��|a�ZD�3)�H�r02H�|V
4*d��i~t�S�8�ӎ��U�JN�[��c�I!S�!a��#������lQ$р����v�����<���S�T(*0N:���{�X��
7z��Դh���/�Oeۏ�G���MMk�9I�8PB99 ��1�#�5 (,F{lg�1�E7�B��Tl����p0[?xy�9��	]�4l���!��<�랇�z�p��GPpF?��R��Fr�rpsǭZ�4�0]у!c`
���N�������i�L���nq�;ԫo�� #����Kqn��A�7g�x�ϯJ�i*�H��g#�׎0FO=(hN}�x_"A� �`�q�=s����&���8<��{U���O���a�3����x�w�W���l�}(�s1��W���� ~F�rY$��z��T�.�7��1�I
�X���2���R5!��{\M�2�[7o�9�>���I��}��MK0��A�8L�/�,O`>�G=���{�bx96H��s�4��dѱr� �y�L���ΘQГ�:�B�|��S������'�U�&J����)�sǾ3�{g=�܇t18d2���Fx��=j�Ѻy28_�t�=�n���eO/cƤ�Cg��J��3�ВȈ�q$Q�&������	e��	��$��.��[�ϑ�?��D�L���pO�y��>��a��.$��R1� �����0*�2�3��&cޟ1.N��C�?�]���:8$)稊E���8�^2@?�fnq�_��Y��X*�(��ۧ�%i;2�kS2�	�)P�}A��Kio4A�+�q���[����D��������9����dL1�Jw��~��I�k���b�2_��?
�g��S�� 
�`Ѳ���<u���4�T�g�N�p3��u�i�gr%-ݲ�>$l��s�Q�5�"�*	*G9DZ��[z���A��u8�VR�v��d�𩖬ҋ�B���Kݒ����^�S��uˆK�����V%�p�p�̼`_��,mf��8l�1���+��Nŕ�U�����{��C+����q����Cq�#���Ο1=2	>�*�� aT����˰��_��ˬ��ġ�x]�Fz�l��� �]��u�#��T�8�h�)S��� �����$�b�h;�6#��q�u��Srփ&p������l����9隥�y�ه�'����-��㚱ܘ��|���8��})�>O+s�9��}3��v��e���b&V�33d��w9>������̌�+��R9���]�p�nN����|�N*�He�i8v�:I��}+HM�Z���m��"®�]��1��ݏ�G1d[�(������Fe/�:�$c!�#��n*������H�p���M�;\�2����Fx�+79�<��Uk���1)p{�}sDr<h@b	,=1�נ��:|Òa���H��#���1�8#�G׏�j�)`���$�zS��F��"*������)�)*���'��)�$���j�0���${�PL������֦��&�p�B�83ϭH�n�Fs��_oJ�N����g���2S�A�ۚ���i0R�����L֒�o-a$����x�Ux��0	b0u'��Pֆ���*�[��F��b�8�n���Na��P��|��P��q��#,Ǔ�Q��Gtc�����:6���*�Ud0�FGƟ%���Q�؍�1�V2��y�=��DQ88	����>�>��C���A'#�_�Uk��e��e�	`6�$����qT�)I�����6E~DB&(��I�<�<��)�s1(\/�q������^�,�D��%h$�8��s֡��<�������P�a&����a��˜�w�s�*�`���#߯��6���d��c$�{Py<�\�t8�����z	!;Kd@��C��������*Y�xP1���W�d���Ԥ�i��� /���8���5X�T\�:�s�ӭX�FA*��S�(�v�{���#���R[�0�?�:�T�i{
Oϑ�;A<�{�LaP��i7����㊘.�PXo<�:S�빶�Nq��?�1s�f�A"�$������?ZC��+1�
����\��8�+��O��=�}9�-ܫ"����ʞ���=��%͙��La�Km<��qSZ�����8��RC\M�$�b?�ŏo�ҭ̋$D���M�X��H�<���ϥ+�����s�NI#�Kuȣ̩�!b���rF1��m�_h-�3��8������T���3mY̸+�^0	 �@#�:�C}
)��)�����uN_Ο�E4qŲ8���-ٕ̅�;�뀤�z�2-���s�p{u���ɐ�V�����P�b��ɭܢ:E$���7
��d)=�)��d�C�c=8�y�z�H�.�m�+���9���`I�)�7��eq��$��8�i+jL�ٝ�7#�9����tUD�"��$�Z�({r�ʌ�@q��e�n=A��H�y�9��	�\u�d)Jڔ�B�w@8�FEV;����?{���WZ��D�F����#��g(�Bw@���3��gk�vO9���"�d�#qB7$t�~��B�Y7�3�I8 z�N���Dʨ��Ȯ�xq��3�֥b���#
�`ǐ@ϱ�`�m�u1��$z�2�y�L�)!O����m�I�1H��7�*�#��rF}��!rg��
26���A���?*}�%���	�{����T����ن�YFs��?�p�}3�M�A_)��BFH���ދv%k#GH���8<�E,�69��Z>&���ڵŨ��/ �,J�w��8�k&�s(B$ ��A�3�q���ץh����a��B��'��s��+�6j�<��ThXc�5$b5�vÌg�z�{��j�Z�rT����~�?��X���y|����ܸ�q�;��ۨ�y|��� �	9=����N��r�Нb�4��%�܅l����`:R����ܤ1!�'���qT�K���&[��p��>����B�V��s�pG��`�J����N�o�~a�?.�U%\�����9�ڮHKD```��~ �9pw�%G0���l뤬A��F01�cY����=pMF���iX��@Ic����=�c�y��g\�#����[�;n�# g8���_s�/��H\��+�<���2:K)�4�,j�j�P����{p85O��'�t�?�
�2���bPNr�HGa�+6��\4lHB=�,a�*��}���U����ӖM>|ۄ�p��'d�P9 �r�G�*����9a����>��pCym��:`q����T�-;	�Ip�+�����0O��9�5"��y]�&�	;1�'=0q�w���kt�9��[��Ձ�`s��#'�!�rd���n���� ;~9�
�1'�7ݔ #��r
ۦs�+	�Qp�y;����@u9NGE�O�B����AI�#���e�<��)UڪO`��x�FNO^�48���Xe�a���
IQ� pN3�s��V��@�I�S1�v܀�d����ڊ�K����n�e�	<�����k�eK*�".ͫ·��08끎��C\G�ْ���8��=�n��?dv�ێ��9��@1���Lӛ�X����$nRdc�ݏ��8s�q��Q��>@������g9��=)Rc^\|?��xP2A�'?_Nj��%�ȵ�ʹYPgB���
އ���$֑2���,Za�	�����)s�	 e�����������?�Ձ�9h�^3��+gN;�h�8̻�XG���x�228�*�7,��8Z9м�1�r�bC��@`	<�� �M�+̈�F��P�p�Os��dg�:qP~�Nd�l���E��md0�$�U0۔�[�v�8�J��c�į�>v�gwN@'<q�N�S�@P������ۭWeB���9�2}��yY�=9^����:�K��s��z|�q���l2V(P�b�$`q�zT�kKya�2H���e!�o��q' qM-!.�#oP�Iϩ)\iX��+:2�x�1ېsO���x�x�F�|0#i���\����%v�ĮN��q@�<1L���H`8 �?*�k�\������;��sY�B�"���Gn3��j�fL�N�HL(€]��s��Z��H�}�1�Z/1�D��A*���zt�
>	#���ݳ
�sܨ98��G���&{���O�ٛ.�,�9�{��QX1V���zc�<���]�Ɩ�3ZV2�ʃ�f(�ן�?�A���K2�:gy��*�m9�l����(�B�oN�d��5>�Iv��-� ��3 `d�8<g��8�ps��'m��s{�/��G��C�<l	��`er:�8㡨��h�{v6F���8��}
[P���$�-qHNw������{���N̦��q���)R��,D]�1�̄:����?�B�<�X���3���Zd�;+.��2��?��HV,{�HAۍ߭\�Ʉ�R��
�"��p��C$�bƏ�3�#1�v�<�EZ}�)�c���N߮}��ƒ	ZIe@�6\��2��$����F��15�)�`�鴝����;U�VP�nL1�X�q�S���jV�be�A	V%�X����:��4�"��%�(9�A랿�\^�dM�솘̛UcU^@�=}H�ih����rN=�9/���)+�J���?�W-������$�Gq�d~tӋ3��u"Eڊ�r9�g��B��e��)�rB�)��1�9�B��UU�>9�'=�왟7VV[`N9Q�%��<w�.nY�Hَ��ج�Ƿ�ےM\�6������T�>x��@��c�,J�p݁ǰ���;#X�Z�������5!lDž�t\��r_(��R�����0G �;d�qL�c��A$���gߎ�$i9���1�0p[�#��hHY������[05��N�Ȓ�(P�vF�Q��z���(FpW���j&�/̖�!ʪ����wL��1�'�|ќ�(3��<q�N0O~��]���Lg*��������em�\�r�23�?��ۡw�$��ţUH�&��X��A?�G�RPy%�vn;6?J��#r���a���zzd�����%�/���8����S�qw�qi>R�&	^���2y����G�ZH��[4�o��A��zz�=�_��E;�����8ǟl�W���[�*�8�P�?�4G���zU�T*�pH�r1��⅌��f+����=}�ق�dI��O���Ϸ�ݸ�-� �1�����;٧v%ً���cԏ���qY��J̪���\t�i�F��Y���"��1�1\�}�TQm��@�������А>�U��,q����f#�=��2I���I+���p���9�j�0�&r��.��d�q�Z�X�˳���d�R̘�F����J���d��ܩ�NJ&�p1�{�LӼ�Y¡���0j�Ic��=H Tv�E�ޱ̀�U��9�9�ҡ�ƪ*KSC�I/UIW$�T��_�*��B"+���:�[�w3�1����=�T�����3�jw�i��s8U�{�!B�岤t�q�ڴ���d�F���F:�gim�8�1�UG'��?/AYnV9��O}�sK�åK��CR��[��d�v���#��2��d�%���~I��B���ⲭ�X��&�b2�O������w۲�����8�9���&̪BTؚ��h���^���c�ι�բb��#��8���(�������6�g����^��0�r$�8$g�g��*��I[s4���������}����e�09��B SiVF-��$ž��lF�T7$rF{������=V�"�r	߂NA�H2?M����#��^p<��-���0 ��3�H��TRp��`�I��N�\�Jm�Uyц������:���p�]��"�B$@��-��@��y���,�$�/Įw�*s���ş�bL�ZB&IdTɀ8'��p	��$c��6
��
���N���c�Bv�ѫ�J7J`�Q����>r=G'ӓ�P,<y�S����c�<��M��\�����)��,�2�b��}I9��q^�y_��A�H�?�:����`���DC����R:ld,�
���'���U������g��?ʅ�o�fT�����E���c8���:�a�҉"�	!h��["L���q�=���A����d��R
���
�0@4���P	��~\�<r=zx��m%�i�x�m��,F�{t����ٜ!a�b}3��<�G*�h�B�gv8�4�i�v�`�A;B���ߡϵj�Z��p�9���)�nS�Ϩ�S�e
��0�����~��jK(e�#1P
�}�s��h��'e����P�9�v�FqWn-��9�
�#��Ϊ~Q�$t"�K�G�:4�YI�r�����7�3n+��}�c���5��y-�U�dV�����ħp9��u�S�Z!P^I�d����?��桞�|NC���nFA�����Ȅ:�w��VNvgMM�#���I%��‚�,̠�fl�Y�H�x��'#��%�E�2�.2�8��.�#��`g�?N������{�VuS�����n���j�(ngw��=��9gb��#*y�}�N�A���2K�Ձ8�p1�1����R]I�p�m���r*܌g
3,�;�����?Zρx�9�v�l��<��S�s�U'rd��SXʲ�ct�5$y)�
�S,[�l�e ���z�T�6o+q`J��wzt����j�3o�cfF�`���?�J�Ɛ�%F������k]a��c���X��z��9`a�F��à��đY�鄬�����Tʜ|����o��V����~VU��u�8�^x=��dfu��9\t9Ǿx=hQdJ�3�i�/@;n�R�b
��ՠ���2�Ã��1�,�.���x�����3��)G��p��NߥXH�&2�)s���H�j��E#��۽OA�(���5F��4�G�w���8�<ҽ�W���x
��\+�By��x��"�ʏ0D����Bc'קO˭:IwK�Tg�g@���qU����:�㹬�#zQ�RXK;9]����$�垟��V�r�u�>�֦H��丌��ז`��S���f��cD�s��~��I�Vj�+2q��8<z�԰��� 1���?�Ko�g�|��#��Jd���8z�
�����p20:��t���]�+``7 6O���%���(x�GQ�*�N}r;����)U�"�6�>`�p00I��@�FNO5%r�S��<ĸ�B�Ы��8`I������{���*�,��%`��T�21��� ��:��w!���?��	��"��g)�B���m��>n>�T#(1I�<��]}O�V������I�8|��~\�	<�M1)��PB�`�べr3ӥ9�4H>e
j�Hϯ��nj������̀76G|�v�5<w%��mq��c��9�O��N7��,Ҹɼ�
���n@fߴ�	��'9��^7Tp�D%,�/��8���Ҭ;��J��`�[b���m$dt�T�h`�@�LvǷ;����c�OL`ĕ����j\�6��.d�'=2Ǯ�}AQ4��9#n��U�r����*�x��ҧ�G(�$ӷ8�8���#75�%��#瞌}=���m��p�3yjܳ�89,q�$���#;�wu���ck:M���dq�c�&�M-L&ܶ%�q�H��)+"䢢��q�zg�1ӿ���iM����c�\��� ��WZ�o{qou��p�*F~��V��V��9�o��5�8��VC�=O��X�#����R�R	@�Ŏ�Hg#���O<[���Ϙ�R�*��y>�~g53��۫�BY�9���c�2��|�P����瑘���S�#��8��s�s*k]E
�"T`\�UX1��á�^G���V,��*@��8튖�-�_n��zm��r���c��N6�r:�Mg'���Ƞ_*S;�E'����x�����>�lpz�?�6k�w�F�q��P;d1��ӥ8l
K• �{�px�����u���ے���~oS�R�IE�2�A�w���i��VNY�$��{��N���ҧ�\m�x,�=@�PRW�Y]`c�,3���T��%����H����6��-�Q�\�1��U��3�9=A�2@�]VS��($n?+�$��2y'�]��61�H������=j[�T1�x���X�8��'�x=j&}�s��<��L~>���B��!�%H��c��?�
�E41�(�ԉ#�\ ��RN3�֪�$��w=�'��?Z[aeBq��0�$r)�f�ic��݈'�<�{r8���h��^�o�V�FK������q�����
ϐ�X��3�:��<�:�E�6F�F�)8��=v?xd���$l��2<����;x<t�x�y�W�e܅�7����U�v�	$����*���ȥW*T�(��:t�گ^�V�[[��A��5�cӪ�8���MPHp�&cD��A`�y��ҳm��װ����ᵈ��.H��s��� �=ė1ǐ�"�k���~�~�zK�	h�cL(b��`d��1���|I�*�Х��b3��<rG�A)_Bx�KSx"���@�4�
����)�&�H�cc��y�G�H �Nm�b�H�����q�g���y.ҙ
���8���WS��K�Tk�N��<�#3_�|�q�x�ϰ�pجn���8�ࠂNO�ǦZ�����U�w�l�'�)'��y5�#�]��>[G��`cӚ�Q�-�9�JV2�GP�-�I�c��;t?˽VI��	۴� �y��sV�N�bVy��q����w���C#Wf#�8��L�	�d�#�j�̊��S�AePSn����G?��M+�C1nP��{��{T�DDRT|���3�H�@H0�c
������㜜��ۙ=�r�Q�dR��Us�'��n��	%�ܥ�8�]�þ%��9�E0��B�D�Wc��
=G�֮B�bG�@�A�<r���s9V��Je��y�U3J噖=�{��<�8���e�	W��0�K��L�}�u=*�d�y�Ń l-O#��#��Q�O	����N�2�f�G|��������QrԒ�Ѳ����:pG�~5ReX�f9��`��`�Аp�p��:x��Ғ�n��/���;�u\q���g�#�����u�8�_�5��oN�<j�U�Z,�J��L�?/��r8�@v�!o��+m�_N8'#�o3��e`o3�
�v@'�O���V��6����n��גA�w�Iȶ�J����ݵ��8�?��{R�3L��ՙB*.26�����j�\#),�0>m��N{�x�:sU�1��1)/��X(8��j�Z#��[���j����J��QD�_h
�Fb�%�=���Ӟ��P[�wd19(XJ�򣎽�{�MP1	YT�q�{s�A���֋��l�t�FQ�.=O_��¼�̲�a�@�0d������{⣊0�$m�0��1��I�ª�WЙ;��׮rߝRF|ډw�.$��"2�L[�?�'=Cu��&Y��J��nI��Lર˱��O�$��?R�R��D�T˂ۃ���?��.2Iۑ��m��o�L�`;�9�x����@�j�<����=8�ӧ�~j�ye�/?���;�c�c�`�
�p��@�I�`�;��� ����`Vm���+��0@�����֬�gu5ܐ�0���S��d�:��'�ƫD\H#=c��f��ȱ��r��̞Z�q����G�T4٪i���2ne��2A��<��zr)wirA
�\w�'�$f��1`�r0G|���F%u@�UX�
 1�p�;�ĉL��$ewGR��^8��b��آV;���f?0bA�~�zg�g�6��N�޻���N\�3�N��T��X�vɰ$s�rON�r<�]1�J�����\� �w2d���Lz�I�<ƒ��t�=�w��x�<c�~H$�r`9�`@�8�����62G"�6�w=��s����ަ�ӱ6������OV������;u�r-�!GCc
��r8<�=M_Hw[>c�߹mڇ30�s��n;���F�Y���L��8���ⴌ;ʵ��d�A�ܧ �#��yl����3j8S�ǟ��Ze�۾�ے0�cQ�00==���
VFۿy^�(8�c���G��D����
�<ET�%9r�Gt(\rOQVl��ZC#!8�p��ץg��\I���f�p�N���\��ˉ��6��ۿӧaW��Jz�ϐZF��GA��9�����L�+g��ǎ��I�#�R�wd�~�X�B$rc�,I
@,:g8<~��v7�F�RYu;8m�skr1�׷�M'��K�FI��Jp�H�FY�o�"
3�N0~��<
�\lY#RY@�ܠ�ێ9Aϭb��Р�- k���Z$v�	�"�^K��iVP!�5�W�
�>c�`sۂ~�����P)�T�I)=�����U�`H]��Rų�'��?�QM1IY+��ӳy+]�A*:�T��S���DZ�B��-��H�<z��pD�Ԩ݇
9=z��+��D�� ���:v�:�(�y	$��R���;���~\��]ۚ5���x���'ڡb����0�8��'#���҉���2�E�l����I��H�Sm��c/�,�6J�Ў���Sɽ�`�����bK�f��C�཰ONy͈�a�$2��J�1��sެ�J�UYCd�z�n�	��M2%+�0�.�����Cpz��*(�����eה<G��K,g#���1�����"�F�I U8*pA��x��[�4&/(��A"�@��;��<�sLy`p�PA���ɥ`X*�.z��������J���*�v8���@�K�����z�A�L�6�Td�|n팞I�?�M
���윷A�p�\u�P�H�E��.�J���FFTs�Z�I���+�*pr}O���V��Dʚf�����S���dU��-�:n����I�#ӑҹ���S��	C��J�~#�kxwǼ�7w����p{f�#QX�tZ�+h��[��o)+�a�@�c��#����s�%��c�c=�^�rfxdl6�n���x?Ϟ	xh�y��|�����$���~2I�Z��b�Z�QJ38�F{��r:�֘�j:>	*�1��G8���j�q�����:��s�㎃�'�֭�[�ô��R�,Pwq�8?�I=�ul�3���ljHܨW�pFr��:��wm#2���)�GQ�u?6	<��J�0��#tl��e��d�$`���=�T�p!.���{�N0;T�F��f��Ց�Y�1�G�
�F�d&M�6�^
�s���O̓�A��f1�����]a� r9�#�FOpq%��'�.ط�$rN	�}�@jcʜ�)On�wI"�N[<��u�͕334Y�R�2qש�렻�*1#F	s�s1�<�A�'=k4��eg��Hr���w>�3���=73У�T7`0�ר9'qSOkwډw�R.b�����b�H���[�lEeeF	$�=�3K4���+�9߆Q��p@�1�3�XIJD<�~F���1�8�g�>�*PB3 a�q��ٿ�QI�r�A�m���:��T��<�W��9�q�q�F>��L�ѣ��"�W
��d6�;}>��dYVB�#g92NNq����^���SH��p
J�[��0���##��S�K��y?z[y������pp�"85��s&�$�̾gRAh��r$���9��Z��fy	�a���Dc��}��<�j��X�����! `z�s��zU�K�<D*+d�J�I?xg�${��jՌ�~�Nvy��@�K�Ğ���t���UI ��@$��Г׏^)^%T�g������?z��rf<�T7$�r?"z��&�	-�Ī����IQ��$��{Tv��)4r�l
>c��c�JҖ6
��F�R6ыc��s�pFkH�%��(�'����<:py�cNb��� �*�q����+��3�	``��ԎH#��WR1+.�!B�پ`{������lP@c�#�����^:�G(Ԉa�1��w�"4���#�NF@�nzU��Mϔ�`�73����}3ӡ�9�J|�3�������$g��8��\�MI�N&�|���!z�}G$�<�2�M��l�U>�`�5�H/8�;�*ēA5�H׀����� �O=��m��Y�U"O-��C�$g8#��2=��<Bmē��jF~��8�ⵌ���<���*3�c�m�ǎ��/n.�5�Hb[�r���9�6�#���31����iJ$��c�r3�H<� �ՏW
ʉ��`e��;y��Sq�؎g/����l�1c���=rO��h�Y�b�m�=3��a��T�Y��7ۑ���?�w�� y�ZD��CoQ��\`t��٭�c>mJe�č�P�3����rx��i)(p�0$()P>��y�#ң�%W1(�NH��:�ǰ�&�	��v�0O�<}9��RA&���T.r@�+���ޭ�Z,�98�@`A�?�I��˛T�"r�סʟ�s�}OS����"_&VYv��9�����>W�9j�J\�m�m�&�$���*�r>e$�r?�Ea�ѱ��.LD�@%x!O�WO�D�X�a�4Χ,�� ��y#���.L�D}�+�1{����9����*���-KIoa��j����Òs���5�Ė����*��I���<r*��\"$�c��@�#$s�}I8�T�6���H�)Rh9��<}q��h�����F����+�V�q��\�޳w��cj��Oˀ����A�:�S�{���J�z�3�8�C��X��|�.�C8 ��~�du�Lӛ�پ�0�H��)�Đ�9,7�E5J��,�<�OOC�q����Ѧ���Lг�5"@dPp
�S�NӼ��3��#C��s�ӧ	�"e+�n/$����97Ï�-�p�9ݴ|�-�$�{Uw0^7�#<�u*d�u�`a���W�N���)�^(Uʮ�Ien	�c�r;~*��i�O'˕����0��U�#9�R[��6Œ$�p�g�=v����3P1����
�9������jKb�F]RNF:\�=���I�ė�0%��͂������b�����W��U$+��61������s�S���9�1���Ȫ�/Q�R[pIT����rG�!W��Eo39,|�s�?��N���9�-�M�%�
���t#?)n�̞Xʓ)'�BcAQ�9�+�+(ܻ�<�n{�>����ͼ��99�F2r�F9Pq��>�b=�Av1� erÞ	��>�քL�+�b�"B�[
�p�����<�f|.2�q���z��Y������9�y���kWL��d�Ie�Q�m ���>�c�qW���J����QB�J�c�L�Q׵Mk2+;U�@E;Id|� c�H5JR�����i8�j[Vż���8������q�ݳ4�S�Q�:q���q�;�}�i�_�%��y��#TQ�đ��ܑR$�s��w��rxR@�=*Έ�V���;k�p��6����2#v;dT��4����%���.�7h�� 9�8c��銊��[c��*�>e9��qǭf]���\"�,�_-I$�c�98��M]���G eNx^8�F�s�ȯY^$�25�#�,H�/�qT&�	G?�dz�jne2BB��OnH<����;7&��P>�t�OƪJ����B%e�*>^9��\¡�M�/ȩ"������8�~
X��4!K
Ý���_���T�%0�/�P�,x<�:�@�NF+)6��bŹM��^[�em���' ��J�
*d߰�dn#+���aP��,*�[pm��%q���LԲNcA���0�*O�<��j�V�"M�d[;/��p:"�q�n֙�ƛ���	�=�9/]د��2pI ��3�>���<�0ޠ�A�{�?
��̤�
HB���a�8�^���jR�8l��;չJm'��u[�i`�|�4_���Ϸ©��涥X�����$�ӧ��뻦�!�˲.ѓ�:@1�*���y";q(8
���?�x�v�k+&O�9p����#H�Z����*9��5$�v�����zuʣI�bM��/��C/#;y8��<�qL�T��2�*�q�����H.E������Ǩ�$-����9'�jhM���c�g���X�I������*4*P��q��>�4֢l�PDq�
�\'_��8?��ϳzS$�!gL��ci��0:g�'���fD)ѡb�p\``s����q�,a�pL��<��=���X,��&n�6�);�d���xr7'�W�;{�|q�Z�cǒ�@
�����5j��d�pA��q����{ⴌ�9�}a!�d8%B)�`uh�2�H�[��߿ӊ�.K5B����=�g���y�E�Y�w<69�דګN�nvܨ"Uܸ�]���1,y8,݅h8RsW��<gsڟ1\��*���[#ЀF3����
��!�NH|��1Q�|�1,~�`==�k\[�����;���_��<�MQ�i7�r���Դ�c4;N]��ɑ���9",�`y����*�Q�1�!I�<�p@'#�j���8wm�W'w?x�@1��q1�%{�
1����5r�<����.K8 �N=��0Ϲ��
�f��mX_z9#$)��g񧡟9T0i��� .%��N٩c�U�#vIݞ���2�[8�� �e�3&�I2��8#��;g�s��*���	 �I08ʑ�����wB��8 u�9��b��'�p�l�Q��b� �����p3�5JW�i��yt�.��7	2���}j����
4D�A�ЂFA��{�i�Wl�mP�{8��?Z�B�&̀IspG��.{1ӥ�ԋh�ݚ��m���y�>��og<P��!{w&5vR�y��q�q�Yq\��r�r	
�?�c��\�El�]�m�WP�Rۀ�����
6cR2�V�2(GI8��#ӯ�W9)h���<v ����{Uı���%�O�=���_@��pY��X�s���뚊��GhٔIު��=�:����$L�*�ېJ��s������˱Yns�����0�ޟ$�mוd*��9��^�}2�����$	|퀥�^I�3�T/4I�<@J�'%���޸�Q��*J��B	#h�����{�����$6.qߑ�t9��>�2*��%����'P�9 �@�8g��1ޒHJ6�;2v��	�����Ӵ�2nT�0[�F g'�c$����q�cf
�w8 `��ӷ��P��$fO.p�%�+#������ޡ3&��z͏N�x��r�������4�Wi���/�g\��w{P��q7�G
�����%\} c�:8̏��<(_�KpG''�)�f7�6�[�*1�*�`��s�L���>Ǩ�h��Ir��ZWl�l�>fc�}I�S���q��C�1!�6���=�c5L�۬`��@�N�n�=T����RD�(q�l99�:�����	\ri"�@RFI�����	ハˎ���.T+W'���?\�T��w4���x��=��9�[�2�p�P��O���Z��b��!ʪ��	��J� � ����Ԟ?Ɛ01(ڸrR�<����Vl��6�R�P�pQ�~���Dn)Ynm�
��S����O��i�Ĉ���]�r��r⥲�V�ǜ����y�=���]]�a*��!�wmH�ޜz�R�G��݆�t��Z�UC��H�qTf��W�i�8��)���5�
��Y�'��o9�k-������,�+R�ti=\����3�WـO����C!Tw-b��=z�֩�1��IGc�Q�~�j9e�g�3������U����x\"glQşEn;��٨��^t���㿦8�Td�v!A;��{��–��*>P	]�,r~�={zq��w�����Ȏ��#��늱:��f�d��� ���銫n)�eI�^���n��i�8�7���o�Zw"N��0������*E'���d�{������H!Nq�9�R2yyS�W<��y�����Z%�^�V���7�{s��X����w�����ӻG1�k��e�,9�{x'�:��$�
�"�x����gLedF�we���&�X�@����Q�*�6�S$�
��!8��G|~8�n�w�ʦF=qנ9�v�E�ʢ(4a����U���Iw8m��읳WW ɒ�z��8�HyUMķ<�����#�X��Ԑ���3���_
�|�q���U��j%`<��n9����5��%_�K��ߞ)^��-�P�CD|��q���#�z�rI�]�ଋ�K1��6����#�^�M��<�p��]�猎��:��� p3�8#���g)㦤��HP0���3��n��[�e�ߛ�_�3�Vb�Kț�%�93��NA������ت02>�Lg������6h�!#$��c"��C��[~�����X`�g�ј�Xa��G��V�q�J��K�#a�0\�n<�>�]!�q��1�s��3V,�^"�[aa��{����MB�Bo\��	K7'�d�G�p���4/�'�Թ�б-��!$����g��H�RF��j���y,���mOEn}���6�6Hcx�GA���g�ƵIZ�r�B&\����玃����<�`��s��~?���ը�m����H��]�;�ې�r�񣠔�Ȩ�2B)
�.��u4�#W
X���d���9#�*��u�U��A?3),������΍���v�?�z }j��זI\��W�\|8�A`p	9����QH#v,�e`}���A�䁞?¥����7�.wd`g=:UV�V�YX���x��Q��ry#���"J���KK1�U3�%����\�f���d��ݶ�q�ը�I��y��*]�cgip�9�8��U�'4����0�ir��==M]��A��a
*<Œz�ګ�O�"F��/$��O���#=�*x9�x��aV�Z�ɹh���^k�K
�o�F�@	��$��$q��3�>k������̬��0$u��?�Ck}sor�9L��0%x�9������l�U���;�Ɛ��`
�i'���2G�b�܎�H(i�N�&�Ƌ��g�:��q�����Vp~V�E�pe8K�lzq��6e��|��_�$��?3���˶&��1�/#�$u��A���W9SZ����|���*�8{�~��B�`�[�x�~�j�L��,���x>��L�%f��^Fi�y���7O,�N@9���oK�Ӎ�Rg-��Vl�c<s�$��g��C�6�Ќ���%rm��?)����Q�{Q�X�9�:p�_c��n�[��+�6� �xR�t�6ƊF����g�=?Nj9�F�������Sգ1�����z�ݤ1`y,N?RRWC��p3+�ʞy�p>�jY��l��'+�����*;8���O��4+2�gs�P07s�H���\6Aۑʩ��^߀�8
18�8��F�s��P�嚒�)��Aў6,Fz������8�I
��s���On=?:u�y��QH*H��CcD�2y["B�H��,��
���'�T�.��T'#��?�R7p�F7|�p�H�}3M
���m";���c�8%���nh���I�ͬ�����$7s��$|��pMZE�n��P�IhU�03��$�y���Wu�=��2�M��3���s��#�*)	uĀ6x݀1��rO8��m?��H�R`a��=Gol�ƨ,d1�#�dc��C����D�iGS����{U�	$����<u${犲CD��;���ccX����=q�*u�;�U�\`�H�u�:�)4h�t>D<�	�X����j��@����A�>���L� �gB��+�����rq��j0�v�W9����ևr/b�e�%$ ��` �c�N��Ufc���On;�
�6`��0����1�­!-2�"8%����q��y�"�,�����3�u�>�T��UW�x��V�!�#C*$\� ��I�8���5��A��i�=G�s�:���2�rX.#�::G|)c���.��y�(�#T�n]�2?'e�V��'�<�F�}���d
`aG@C��O�<�(?���
�8��A�hWb
� n�*��z�KÆ(0��^���}*�;�yE�B�ia�X���~�ow�!�X|�q�f��7HC���H 8�FG�Ӿ*{y�V�-�^	�$~U���v�<Ckm�y##�^�����g3c%W�=z (P<�إx9 t�n�jޥjm�U*�����z����%����Y6�q�����?1�X/B=H���J�#n'��MhJ�'P���?C��"+h���4��r��XAR�
���u���Z�i�6��Ñ�;����$�ːNr)����aC����3R��|�F��(�fS��#��Q����sV�~�Y�`��a��WOµ,J��o�9N�O�Z���s�����$��Gc��VՕ����f:��;�)����N��!���n
q��M�0�����d� ��`gpO#��U��浗ʸI!t��J0;OBA��b��앭��JTT�
����Ѷ����������s9�{�l��;c����<�ߏ��je-,\!��aG̹������j��!�j6�w�à vl�¨�nDU\�Q�Fz�z��=�;I� 䓎1Ҫ��z�M�F�*��0[$�}�EY�D*��0#,A�qQ����Wd���c�'ҭA��E����q�<��kX��}��껊�w|ͷ�N8�>��IS(�6J��_��V����^D�����ױ�}�V��f6Y��{��ZMkB��s�w9����ޒ.r���V#��Q�$��#9�S�h���Ldw�Y�W-,W��	�i`BTg$��T�6��*�(
�9�p?��!B�碜���;Y
��$vI$�N������6ˮX���2)�[�!W���ja[�r8_z��23�X˻�:{
���h�!�v��*\ ��3�N�psے1�|}*p��3�*�ݴ��S�w:"!!��-�\�9���p�4-��BU����u�ڵ.�2�3�N0��ߍe8"�=a�s�V���%��?Tp6�t�b�*�%�$q��E`��xq�g� �Oj�H�t�����IRb���(9a���R�	m13e��
p�r}3�ֲ/.K�`rT�c���jc��D�J#*��H��Q߱�A��l�����9<���P�B<��rG��u�����P@�$�I�<T9��]�T��r��S��¦��(��� �)�'Ӟ�8���>pP����nj�ż�Pȡw�t$�I�e%�+���ݏ�唞q������m'�'��ά�'8B�Iglg��J�����03�9�}���jRh���l�꼂3��J��$��h�f�@��ާ�o�ɒfW ��:v$�Oj�e$��_��J��_޳�7�n;|���R����4���+n�	
��s���Q2����3�ޣPU��8�}�)�5b�p�W�~Q����U/���ǀ@�<w�-��;��	���E���8���?��lj�h�K3s�y�^�V���� ��|��[~O���H.`o.~a��#4�?����Ǹ�CGs:���u+��GV8���?J�H�!�"ec�&���.}�V$,��9iSQ�$L;�ǻg�=:񫓹�;Ħ�R>em�F3�q��=��kB��O�I�<����m0���' ������ ES��$d��u�>DQ�8YF��G8�r?OqR����	ܭ�d
7�������H�$#,͔d���$�UY.^9�n��D=
�Ͻ;{2+��t��5�i�
yz��_B�0'�]��%V���>U8p�8�9�N��[y�06�Rv��E{4�Wq+���|�͝�99�O$�¦H�Jېڏ��y�%F�U,꤀_#=22N*9�$3e��v��#��n9��L*Q��2pv�3���u�.�&
3�O��p3Q{-KW#(Y^]�7Ub[�rl���c4l��wE曥$0##fѴ�:���s�UP��w`�'=� �u:�c!@ܻ1�Fy��{��a�KHK�������Zo���U�� ƖY��"K�K�Q7�;T`u�~vV=6������ZE�Ż���˹@��'nz~}
���>�t�̲�����<�G��E�n""K0��=���V�W	
�Y&*�;x�<�j�sQwbz�����{��z�<f��[�%A�$y`‚3�t��8�(�Q��9+�g��qӦ1�VdY䄝��<�u�ڪ+V�y̳a��#p�e���֢i��<�|�C|��7MÑP+H��\�e�t��;r@��NZY(��l�rvS�����py?���A}��#�9�>ՠUVW�V\��.:��Tԝ��Þ����O�e��On�"�r��� rFGZF��s����N1��S\²m%A�p�@���!Uy(�N;�6�U{����rN2H‚�=:t���6�dbȟ/$c88?犩
��H�8=jD��2����3��8���]��6
*��b��OJ��ڬ��=_�P��܀���N=�s��$iTy��Q�����գ�j��Q��y
wI�?�o�VmdH�$����0D%Le�l�3��=>n=��Cl�N�Ā���;�U�)�ŸH��*������G#�@���!�	s��n�Zm��1yC�q�r5H�Q��Sw���}�9X�F��$�lgF��#>��ƭ�5��]Ip�q)Q�����9<w��P���Fp;��{�G�I��
�{��,�4�ˆ��O����p��>�>�c�Fff9��~B��s��ҭ���Q���c�Oo�Q��1B�1df���c*	�w��5�h��f7V1��a<��y=��Α6���.ђb� u�՘������ь�=���`61����k)jwS�zH��H��+���0眀N�z@�(U������J�c��yS��D��
��1>���lH��m������1�S8��oN�w�2[�$hJ6�RK'>�9��U�ع����O�LW"he���Z|��^�?ҥo/�%Wi�nG���Nhׁ!q&�S�wsڋ	=nST�~~��Nx�BA@t*���;^�*F���q��N\�i�[���6'8
��9��7>�نc���/$����G&3d������$R��զ!FI��98��+�<*��P�Ҫ�i$�~��kC	j�SD�e�7(A�=�
[I<�f�J�eC��>��$~4���.6w��g8�s�U{��`�I��O��'ҦL��2���K��GLg��DQ\���qS`�1y#��b�Nɼ�h<du�>�k��t�UcF�P�j�X
��=���*B�$|�ϱ�ݱ���]�y�A��8�ޯI��(*�p�N��?���n�J<�B�3�m���@�G�5f�t���x�������99܌8lw�#\�jӲ�.v���N@
S�p	�G�U�bj��vH�[�a>c)�YI��U[��F�Ԗb6�N�;p:�;��<�������q�^
a\��l*.X?��+I��g{��\M�&�rK����3�T���8$�n=��X ����Ԍr�,6�AR�mM�;� ���c]�x�pҪ����l6X�z�J�Q8ܼ��x��G�
Ei;�X�)�����ɦʦ��A!g^NL��zq�Q2<�,*��b@q'@,�Fk"Į��+�-�ۡ��vJ��YF��@88�#��tA��G.�U�Q�9������p�0p	$�#<���<Q�<̱#<�p1�9�w8�����`X�(�=H�����Y�tD@-�T��p���S�(̈��x��y�F���G9��O.봇!�H��$��X�Nz��=?ɤ�{
]�E¶�9�_�5a�T �y��Ȍ{c�	��S,8�#=���w�H`��s�zu#�{�3D�ُ��<(s��MN�)mʭ�6�qАN��>��m<��n�搨L��3�u�
H�Ay !��#�y��R%�5"4fx�����!Ǩܵ\�/���#��S����\Չ]�-�T��A�vL����W �d�ʀF1��Pˎ���]�+9'%H�<��AQ����6�2}:0*A�b2g$�F`G���9�{�4�#x�Ur.������=�F�/����$dg8��sߥK�(pI�����zc5�C3$�1;��E;�*�>���oؕP�H���y�ڕ�.S� 
r�0�u.�+s���U�"Yb�h�ҧyUP��J�֧���в����d�q��1����H��*s��p;��V[f�$�y��v�+�Q�#���,�v�E�C�bH\�G+�GZ�#�B�o��Z��A�q����Zז�����UIb�$��`��=�F��V��C	kD�$�H�����F�ך̞s���R܃����zq��Su;���G��$��c�'>�=��-��Ün.Ct�O�W*�+")�mݏ���y%�"�bA�@t��14۟�4eb됙(%@��$�rx�Jg�����0pA ��݌g�q��]XD�c���26т=��sX&���I
̅U�ɑ�U��$�_t��N���g��:����}zbFd����G��L�GN:U�`FhcU`3ձ�By
�n�Nq�i��R��"�"�$JB�*䢓��̓�N܌��Cq��$1��aUA��`s�={��6�<���6��F[��3���#X�Yٕ��v��	_�z��P�;��1ȞT�a((RBT��1���3QL�M��<�ʌz����:��rU�yc(�h�א?��܎�Ug��R�J*S�q���>��Z�I�Qi�AH���m��1"RH�p~n��z�Śa#�i����y'9�<�'��z�$E�;َߕG$rv�;����
��k��3��q�Gˑ��jyn>p���^hٜ� ;�z� ��!����$p�����Y9<�c�zUB�V�y��9�Nz�|ڮ�m����b��!x��S�� ը��s�+�q���6!�~���=���[�5#(��.�Cz�I'�b㌃O��o-nZ����!
<��d��b��kp��#�����N{��=�m
�38�	����=}q�V!�n��*�A]�O9��9=~c�g�i�tje�q����\���	�ӊϹ��u�<�C�Fю8�|�y?J�.SH�%d[yd��gG�c�`'�<����#9=*�4�H꠳������QB��e�fA�NU�P���3�Ҟ��E8��Y%n��?����qӟL�w5T���K��xK4���n�b�p	�?J�9Ub���|�@�R8�s�֟+nC��j�r�'���On�	8+`�q�t����R�Iy�H	�d�PL���0�5	���Tm��?�`��p������0�~Q��n}�� ���O��ۙ�,�#��[��R�.�**șb7`������Ԡ28�h*]_OC�HP�L�z�G\�;暅���H��׭2[%s����-�7gq�~_Z�h!�2ʸ%@&�	�2;�*c��8�Vߟ���۟lU�H�������2P�G<�q�pW��H�LKIv�~bK�@�|�=Gq��>�mc��%R��v�p:�G�s�C��$��
�I�=�ҵ��I^(��R������>���Ri�,1�o5��	J|��y`B��O8��*�UtW�F$�Hy�q��И�	a+;��x�9���
X��)n�Ɂ��_f{s�^��޴PL�{��$b�@���<`g���'�֫^����Fs��#�z�
�ߌU���"�BƜ���[9���X׷��#LnN;u�{��'.]
�7��Os*'���wga$(|c��>�^9�Y	�n���\`�����%�$�v���t�$g��j��ʀ���YW
:�ߎ�Ղz�v�n��I$�%~RH�s�3ך�8IX��V܉��鎀�A<{�0L�h�L�~_FE�:`w9==�%�I&�I���S�FrIrO-���O#��+7+�#h���c�|�+gd��=��ߊG��,{B�&�NI�$	9��{S���P:p ���
��s��
������i�d��ڠ`{�ZW���d��� ”��ңvA��ʬ�@0��#�19���֖��H�#�m��%Fr��9�]�j�=NY�{i^�����QD�c-��mR�3����S�Ķ��ʅ9�I����/'=�x�H��6g���%)�1קC�T�Q�Vʦ �H'�0{s�6�`��3���ϩϧ�ǽW�l��ʓ��n8����)��Rk��.O�$!#'ʗn�-��s���9�ߴN��f��W� a��:�=O��ՕZE�X)B�w���U�!�`wt�on*/��,	�nv�q�q��r:z<U���T����YAwN޸'#�r3�UiX E
�H����1�}�SU	�m����FG�zw��,ֶ`Ĭ@��uF��<>�v<�J8ļe@���^���x����������\�u����-�K�<܆F\`cgp>\~=�X�4a(���$�q���^���lTB2,[$��`(래�>����]J��.�dp@�#�di��C)8*8ew�Ċ��5"�|89�OE�A�߷�X��q�E	�����*Ct���~<��>���;���q���i!�Ԑyf��rx��:s��aY�F���a_j
�d��~��dt�F]��r,�ϧ>���yg�T����c�8�Kb~-�8nFh���B@���z�~;�w)!� �c �8��<��I�yu4�h�g����d�r͌g��9���b����g�y��׭!+�Lg��m��dx��{�2mf�7.@8���svvd�3�<���U�#�ˆ��U�!��z5���"�a��X��r#����\e��jEIsZ��	\�o
�$gi=�8�Y�cD��r���u���R2ܣn$�a�R1��pq���zxh��Õ�O���V�)+���`��(k�%U�	R,�)��$��zUY�1�%M�@g ���?LU�T	lFx|���'�N9'��x$�y��8�.&���2n_tmb��pU��Tt�'�N:�ZF�Xc�`��OP}@�^��h���0T#��};�<㚯$k
�^v^J�>[`�8��`dc�a$�vSi�5��m#0؀�I<t���crT�θ�
�g8��^��S(�eFXm��1�T8 �.8'�?�F��N���G�GP�t�����LA���<�r0x�?֥�c�VP)R{���L�%����,pp�����=9�I]�B�Q��+~N��ǡ戝<�8܇*��;�Ǧx������{��/����^ܓP�����Ny���)�z2�夶�em���*��q�a�����4/.��~x��}�Q.�3*�
����J�-��H�qe��s��\���ۀ2i�������Y��dL��6A�x��tϾ*i.aT����R-��q�u 1�gE�X$��d��ڮ̪Yy�?��q�JdO2?��|���7�\�8=j��C�mQ82!`	(������C��[�a�Z�=8�y�G���FO�$��8�۞Ǔ��&��03+�oe!N3�\��Zo���z���ybW��jY#ݻb�ۜ�d�9ϽR�W�/?6
��ǂ^��Gh�9����H��0\-�v��8ڟv��(��.»p����ӌ�z����	�4c�LK�^��xd���t��2\>��!;�x�$�9$�u�ko!�b\q�
�տ
X�R�Nݔ�ps��t�׊�H�b����D�R͆���#h;�8�HG==jW�f7�����A�~��	�K�Y$�G����;�ژ���������j)�����8�~�wz����&���T�=�	�2��$H��;��ȊM�7F�����N�n$Y"��6>ЬF��98��	`w �V����$0��c��\ܻJ����28�&���������ў:t�;T��+�x��̀Wh�I�М�<��s�s��yc����	��kK]��J�z�����l2����}O`Z�n6]3�dD`�ve�� 7rSڬ4[%F��F�A��z�}}i7�arb|�����P�5SE{v�<��6{�?��sWc���Vo)D�.1�0ݟ����A���$^ZFT����g�O�9�x �V�w��=8�J�ܩF��!�e�@��+|��g�'��cұ�4ۅ�`;;m܌s��	=��a�z��UY���2+/8�����?�����̀�2�
��8�z���h�M�g%��<�I$	�J1!�$ry�t�.-�l��(� r9�gV��$	�b3�!K.	���$���s[��Ÿ-�m��8������vB���Y3%��"��*��� t�j	1&�O � ����ٻ�ħ�_#	���ۏJ��Yr2O ~>��d�tF�eV�K��\��Z��}��F	��
��槗1�3��܈���c�q�;��&�
�.~B���H9-��ϯn�U�r�л�Xf-�#��0��'�9�U]s��
q���~�"yE^6�\/�Ӟ��1ۭFrT
�~�O�҇r/b�VH�bT1)i$�8��(�ޤHÒ'py�H���L�6�;r'�l���N���4bdޛHE�:�8�ɫ�!��ݖ�7�2�?Oz�#*�;��W12:���[l���q� ��r1�t��_�����y�����8����<�??隞GM�F�����u���K'�(m�����[F89�}#@��9<�c�Ri�?v��$�����Ξ�t@� ��$�����J�σ������.�YL����8��;�$u?�S'r"�d.�Q@�H�'�ZҊ��'c���d��϶;�W�HV �S��#�8!�	9�z�n�k���zrG��Τ�V%�*�e;wq�{��Υ��K�Ku<�֥��ϽH2��uP��@��]Dc۵��9$s��Ƕ?_J���'��ČK��>q�g��c���W'��֫����W7<�®.J�#��V�WV9���e!���:���t�V��e�@Ts�<��Ҡxz1�$�֝h�+6:�A�R��|�F���ʕ�@W$c��?���(^�,������_ƶ�v�6H�
�+��O|U;�0�d	 ������ی���ӕ����,`
�d�}	�"�pQ[p�!1��L[vk��#9l���(���?QVKm3��h䈐�H!��=G#�+�vJ��K�y*�6I#hdQ����I-�����N�:���L%I
� �	�q�WGM���⩔��p����U�NF���kQ�[F1H�7d)<�}�q��
�h�|�F� s��~5=�~�E��ݱ��c�*��ܕ�~0�� .`7~y�1��Y ���@��5FѠt
����z�~!�Vc���M�����m�Nx���k�yk�V�
"� ^F�:��?_��U�3��2\��8�<ք��sf)�
�0G���{�9��D���8%��x���Rh�I�R�2�m�FA����x�'ץN�dX�3`��r@����,
��33l�t�s�u�f���Q����RD�Q��`�9�)鵕�Չ$pzg=�;RH�B�}�p{{!_��Oa�*C���)�|�*p[�c���EIok.�*��d�l?2�Y��3�U���|��by#�Sr��(@>_<����;Ձ��p�-�XF\���:z��_sl]��2Iۜ~$�z��.UA�1�$�FC�⋃�\���%��ˁ��1�#�ۭ^�L�۩�XeO�z�����̔�F0�=z���cL8���s�
O��b��Es�(b�����l���N�Y2[�.Kr.L���)
Wٳ��6��T�v�@�/������X�ܳ2Ȥ��u�|̢�����*K��l�)���z�a���@�҃����	 P�
���~Ƙ,{�!@s����P�j��/D�� ���_��w��2D���1n���<���T�"M�����8�zU˫im�(Y�^��OC).W�T((���<����n(N�'=��?�XhFoʡr���� q�y�u	o,�����<}=��)4@�\w@þs֠,�c!@����S͏&)���� ���}j�H�[*ɺ0wm�n��rgD"H�:)¨C��	��9��9e�s��q�>���8�A�ژ
��x�\��MX��	Bxʆ ��<��3Ui.~bÞ��v�-��&8��p��K�������=q�����!�τbǖlq�˃��Zbx�DE媢����n���AWr@�@l|�rs�y�Ҥ�&I�E��l������љUWF��0���l� ����ѡޒ`(PW1�9����z	i]���8����e�b_���x���c�T�2�x�싌�˰�����8�N���!v�!�I���<����sZ�Y��[`g=Fs�=:ƫ�D��1�Y!���Gn�J�[�P��<�s����s����Ov
l�Hf([>P�����p�d�|Ѹ;�=���#��koPwn
2N>�����w��/R��\m��ǿ���#�d{��JTy%:��Ԝ��Ns�٫p�%��{y�9�}��c���>�N]�&��Kd~$�52E�V�rI.bS�%��	U<��#�2pqQ	9��_�;��px�Ӝw�����e ���O��\6�p�1�����ң���\�v�<Ǒs�!T����x���:E�ɎK4�Ȝ2�}�`�m����{m����L*��%z����'�C�S���A��%��#_�-���^��*�8[v�~��3���I�)f�K��y�2\���1<���$ v�bppy��Z�db�c�<�.2�K��n}�kK�I(�y�6�J��s�#�a=��2q
r�p@���5v�'�v�1�B�@'j�@��ӽU9�ܙhF��r70u�Cg�8}O�"=��"ed��F�H,Ǟ>��_’�ӆ���Ӯ2{��,�+�!'j�\s�{��Z����9�m�5O�!B�r9��U�Ѭ�$�H�����H���w�F�K1�O�P\I�r@/����J�I�dc�׹Z��Ua�P�F/��Z�6��m��2��ɫ�be����H+������,�`z�Y;�qw,$��O�±�m���'?�2i7+(��99���u�n�+`��b�d���P�C3���7-,.T�.	]�6���'%�$�N3�Sñ�I���H#���S�v��lԉ+lT2w�=3MKK%����z���G*�<��NWar� ��ϽE<�f'1�=��N�C0�����Z9����6�Ԁ3��*�,�Ѷ�,��p�}�O��P�#jn!��T��G>�s�T���4��3'-�pp'�U��G*���++s�6{t��?�4�Xb(��`2\��_�A>#,[~1��8Ͻ0��I#i݀1�����cXǙ�$�,�N�dg�:~�lKl,JEy�nL�BB�M�X냻'󬘿wlv����ߙ�T�ND���t�	ܞKl\0�����*�H\�r��@�bA�-WdNӫ@�O 0��#Z���s��ʸ �;�|��U�3��n[��`1��	o�S��ԯ�Ff�$�R3zvA���S?�1;w�ñ�@<{槌����p6�}>��V��,@a�$m$;���e��e�!”+����~|u���>E�~\���U�ۗj��@����?*Kx��m�F*�3�OӥE�G�7#�6	S�?�&�"�E89�DZ�Y����gR�@�����Ҡ��#�c��f�����p���Q(��|�jgب�*rq���z��U��]fV���Q`Nڔ�=���	ȫ/T�1�
��{S�^-�d�y�uq��!�dB_	���}9�=�9�l��U+�y�8��ji3*;�'9�?��ҝ��fI���� ���Iy	/�V#�+��b���5kC���R
c
���~nG ��x���;iT;l�`&��i�G�)�8-"��U�O�ƪ���ƭ�g����֦L��I@��X�����"tF;�����s���R���!$+p��nA8�⩻��m�珻�x�/gs�>�o.�}�A' ��	��r���1�$v��A޴[�갱���$�#f�*��Ȑ+��؎4�$�����[w3��egg1EP7*�Б���ن�
��N8�j����t;��F�>�sѸ�[V��[��r�$��q��]�Y�������T����U;��V$+��<e��O^���V���FȆ�F	��;;v�aJL��*�0����5�Ռ(��m`C�
��;>�OrO�Ո�*�|�#$���5a䂡F�늕U���q�+�SY��.�v�X;���9$qUdG)��ݏ�~��^�,���ێ��–�# 9?t�$��S&�.
ȫ(b�I�H�G<ϧ��Q�Mt#C��0\.틒s���l�_��׭nH���X����n��7TmN6�@y<�ⲑ�g�2�v�zKx�+�{s���bb�r:1����Ʀ�9�Y<���c�dm%���GRq��Q�(¬<g-�{�'9�Jɝ�TBTe�*s�$`��qOFR�pٵCq�9�=�nj{��B��p�����Ǵ,fG-ׯJ�$�4�ەH4�'��?֢ܸl�#�'Ү��v�o,*��.w��O#�d�c�֣�D{�m���:��P8�:ԚY2[R��g
r��#��J�D@%�p� u}��`��wX,��br	@����{�J�C�ȼ�U�;���oA�0zz��KD!�q�Œ��!�*��J��j��������X�����̗�pOL���MBs�3t`1�����T3H����DR�+0v�[p�W�c�4R?�"�"%A�	�s�/^{QF��΢+pᑕ�r�'z����Q5��/�����9�w5n��K(�R��Ď*��8�:��*#~S���d'A�;��Wd��&.OTf���ڤ�rr8~��9�'ڨ�em��j�������A#�6~�6��,�o8�g9�y��`�߹t��x#�z�~���}��ut�2Q��X�.�O�����#�})���YO�dq��v��_Z���2	P�T��	?篥4��v��T��=x��~���d�GDd��^DF�HY�z�FH�T�ӕoҘ�0�Q�
1�"�J�$>|Ӊ$y!��\�:����
Uf-!,[�9?���`���l�ю��O�JT�`��}���A'���4�}����}������*�g�x�G|c��5T�^f�6�CW�z��
+H$�H�y�y��Jr"���GI�A�g=I=8�04�
����������9�9�]1W+#�7q��v=������@
���B����\(�:�U��D8�m�v���<��Z�ͻ	8*^Fe#p��zt��?v~le�8�#G�9m�)9��n�j�<�m����`>`c���oN��ƍH}ą��Č��n�}�W�2��m��>Q�
8��t�׽dƠ��>���_��A��c���c�VC��Ns����DQeYv���q��]U��}��c�z�r�*"�����V�I����TY$+�����:�4�ep�2@��3�g֩��#���Y�l���c�Q_��DPy{Hl�9%�N��t�(I+��O���������V�����H
W?{��;VM�m�<g����Z�g8��N~G�,��� ��9*F�n6�N��YU����m���?��cܼ�9���R�6��?��0;WMg.��](u`�Pއ=���ֳu-:H�\[�<Y�a���v8��jEn�(V��9ƌ��A�����*$�o����^8	�|s��}�?�?/Z�+T���lUq���q�����.u�d�j���_��7��*x�.�)!��߷�>x@]�*H�9�����D��7
G��u�h����-K�1YW��=0{���u%���rѳ��1��IA���q��f��.-���m9w�jE����I�.NN2�*	'�<�;�*�[�Gk�A]�	 �$w$v99��i���]��J�'�@�ZV2`E<�e랇=y��U[���qOa��qYȒMѲd����v��m�$����	��g=�����:y;$iX���$�N�������$�Wv�du�G2{ӊԁ"ߐ0�F2K��5���Ld;�s��jy�c�F��:�d�s��ҳIdv8����Y�cH.bf��1���S�5��n�8�@~t���f`���*@��~p~�}�'��4�.�8%I� �=�՚|�����?ƐەP�Dj�u��j���kD��I�%�/7q ($��:dw�M\�Ag�Ϝ~�'o�G��:=�	�\�F+�o�:��B��>s���'�F?j�hsssh���03:�a��>����gJY�hى l���j���e^���a�@
���?����T����X���~�߭0��!NA���3�����&���ӿ֡�	%�b�@;l8��q�~b�<�8U�������#��������X�{c�%���ws��{�I�ED��� Pr�,]޿J��xf��5�#h��}僒N	���<�P\H�?x�ۼ�Oq튎�-*��)!9��6f����PAn��<~�Rp��Y<�H�R?J�m&fm��|l�#���c�@?��,����N;c:ԻB覌��� ������Ҥ�S�ڦTl��K��:��`�=�$X��O<��}y����(8E+��g��1�8b�@�1���S[��[|i��݁�NT��̺F�Fl�Y�ўN:};f��9d��	�"F���8�+����U�wn��I�P�rKr�!W���#�M't���0�-�zИ�f��2JK�PXc���8��i��F�Y8��OL�)���F�y��;��$V#d%CmL�	䑞sVd���7��la��;����R8��UVS��Xg�?3U#`�>��������m����dl��3p[�ӓS{nR��Cf��C��ZH���h9�Gry�ڢ�!O��N�Y\�
O89#��j&�f
�7����G8��Ѽc'�a�t�	0Cn��$�->�'�8�8��h帶`T�n@�a�#5y�7;|��rd���?��Ф�vh�r�ː\nPSj��0n�) ��;���r0��[��4��͵�ܿ)���&��3�"�����_����%���8ڍ��l��ā�AߌۯLْ&fv�����'}��z�b��� ��`����
���2�]���C�98�5PV&r�� Y��`c
�q��S��Q�0\�Ԑ1Un/&��%c�0��~|��"�na��oƛh�	2ݥ��I9أ�rx��5~�$TS+/ *�	c�z���|SJ���$���q�x�zӒy�U�� � `�1�NJ���2j�4��*deUp:����{W�i$�5ddr���3��a�Q#�ޅ��1‘���h�ʺ��U�
�;g�;{z����&i3�b0I���>�^�C�Yx$�1�ӥK$l�d�"2�(�z��Nby
��y�I�����6�0x ��:�Н�F�0A<Զ���
�r
�8�p3�S���L�� �H���ޕ˳/��@*�w8?�01���,���9��Ҭ4h�ŷHg�.�/V$���J�w39�6�(�p=�q�?�I��	��2�}x
=J;��W��#߂?���;8VUS��~�Υ���UrK��orr8��Bl�л�m�?{9�=�\��k�yn"[�F�9M�<�����
� S�a��NO|d�9��1�Q�a���c��z��s��L1(���b:�`q�֢iLgp�*0�4�yerN9�ϵ:񘱒Y;�u���T�I6G{"�\���#��~Oኩ��p2#nO�ב�U� ��Rq���i��sUY^YK�v���I�ʢRgDmk�,w�n:��Ep0HOҠTU 67c��Z��snl*/��TIvE��mV �I#���)^�n�E&g�9�O+���A!�o�cQ�=2zg�Ψ�5�`���}1N�����@ȥ����#��8?��V�P��K�����0�Z���3�D���������|�	��Ӯ?�j�i�e��t���Q��7S��y�*��;c-���Z����s���Y8�Q���3gz���g�A�5 d�'�H�$B�������A�G�n\����?*\�*�Ə��-�H��O���ݶ��s���֥��	 >[�������S��\�e�O�t��kE��d��:gyn0HV$��:�`<v�ٕ�%�K��:F�X��(t�!�?�cUc���\3��q���)'�&Qv�-��y���98�iV�6�)YK�S�0@�uvO|����T�.8�iZ)&���
�m��b@UǯoƛhI��UB�E��NF1�a��]jѰN��A����Vc���y!���</��{�8�s�K��9�l�T�U����2��ِd�P+>�w�,I~�#��ӆ0�r=H�T�Y������q�r~})r\�5�Ld���A<g�W!�T�?rϏfa�z��?1V�����q���S����H�>���(�0?N�rXn������y"�$�4������$��8�G����b��R�?���N�᳴�����<9�{Oj�5�� �{&w6y�1��}+xǕ]�|�)��!�_(�R[s�8����Ue�'X��$g�SY�
��x�Z�[Ķ�1E ?8�㯦1Q&m��A,
|P_ {�S+��X���9��ϦA�Eg,XD�9F@0>pG#=�%��z�;1�c8<d���*�$n�uT���������B"�0���s�T������p}?���ި�+ >`*eb�܋P��$����ڠ��m��}ER�2|�d��8�{U��,䝧��PIwB�(�
ݟ�݃�+	����tb�+#�a��px?�M�2c�P��F>��:�P7�'�g�p�;�9��!Q��I$���r*�vتNaٴX�L�����$nRq��F)�6�*1�������!�e%P+��0O>����z-DS�n}�jybN02����x���6�c�v=�=q�߯�M�^2��Ъ"��`u� 1����ݡ0Yrތ��j[��b		�T�@�tt��Q���%p#l�PF
���=�$��� ��8�{R1-��ŎI<��Ͽ��9�(�x�$�q���3���09�y$�[�C<�Y�vė`s�99��>��7H̯3���On1�u�(ȣ����0>SOC������=)����
��N=8��>���
�=
�����W"��IJ1������B��3��Z� �ۑ�rr3�<q�nH���k9�T������NGb0:�X����fi7n2��g�5�,�B�� 8*q�01���II�JmcT��ݼ`Ƈ#�^}hKh��<aq��=3��?Lh��4�3ˆv���	��h��W;J�a� ׎�?�h�g+r�~soDf(��@�I#=s��t�:c��Z�������-�<8��H��Ϩ��:�|��l[�W#��N��X7�=���B��[
[' �������vЊO�~���bۙ����������H�J���p��3��׎�†$�m��I�	����2Kf�ʱ�q�:2G���T[��ю�JG�7J\������V-��ꤌ�ܑ���nzU��7��1FL����O�M�cr��N{�}9��b%>b{x?z8��T�`tl������\�R'��
�HQ�b9�S�G^���H�y��†�	s�=�&�B�f�<� m���y�"�$A,J���F�w�����9�z�U��ܩ�]vU�c��{uҭ<S�\o��$��31��'�}}i��\D��
�3pO_l�Ͼjd�\em��{	J$���g%��zd�9�z��a\I濕
)`ʥ���d�ϭ^�D�-�
��{��z��
1o�0Ҝ@ld����@Ⳓ7��R���
+*�̢6�G���s�sR�D#n��7H�d�;r3�r3׵6�΋r���>B�@8���?�C#��8�b�Q�@�p9?xq�z�F�Rxn�ƻp�{s�����N)���QD��>0�w��I�m���C�n�#G�s�;Tsqw���*������I$�O9�����\̍�y#h敕Y�7�
�n�Nx����c��o�*9�)'���C��O��E>�Q�`���`GN���P>a�j�@����O�x�O�P�[E92+�\�#;?��I'��z���TQ�Ԃ�>Uk�e��7ʫ�p#fs�=@�x�:�c���ycUHՙ�ݜ�r݇?�d�Έ�$PD�L���P���8<��qR�A���bT��G�<��q�J���$�o�3����"���Dr�1�˰�Ns�?�*��nL<�4�8T���`�|G?ϩ�BaÅ
��z�N���r��dG�|�A�}�$枪ц����=�I�a9
�}�"a0����8�۱��Ww_.B���(���N~��n�l�х�ZL�˷����^�Q��?yX����i4
Dp��2�!���ϯ֮�J�hu�� �
ӭ����nP1���AH���uݘr0J������H��M������@�`�:����
�@3��s�y�b����<� (�O��K+	A0">�Pc�9?�>�q���3�nO��ZW��i7�1�x�H'�
�V�n�x\?��z��V^
�v�"�Kq��������7�vU�����-���D}��)s��-F�)��g�w0s�����LP�2���&H���ӿ��^�$;�WKX�IR7#��pO�Fj;��RFt�O�P��`��ӎ�=�K�TK�{���
�3pN�t����ຍ���0>��<��$��d��?��L,6�;�)/�9�}z�޹�-nvӏ,H�v`���Ş�U��!^��zc����O$чڸ#����?��
�8L��8E$�3�t��+6�t�6W$1*Z#<���G����)s�$��m�#1�Vq�``ds��>G2v�PG+���������9�/Aݽ�HVs�����:�l�A��8ddd^��O֧DUY>��0P=y폥P���M=���#�&ʑӞ�'���Bλw� ���`����R2���]̊�z��מ�4��"q�d|���5J�ԛ���x\���*�J�����\3��'�N@��C�hj�׮�\��$�d����qۮ;�����tNn���x&�m_CX'mHՂ���A���
�I2T�d�|��F�Io-�T��&:��:d
�j��8�=�
͚�
�YC6�ڣ��<q�q�ԫe+F�o�)R�2(s����
B����{nFݧ�0:��~5(��.��E���Mѡ8��F0O=�ۚ��V��P��r���:�~)�*G��pTu�8"��葡XF����:�ry=*%Uʲ�N�\@�t���(��?Z��!X����ps�\�I�W�Y��H!@�'51����<�>��qN�f�0��#ô��B�g� z�)$F���nV������'),oZ�ѭ��Շ,@ ���c���,��};9gie<��%����\���[�J�ijN�9]YX�p
�T�2:�L�}�����7`��5d�C�����*)�B�X,��H���F_ZA�SU�q#��d���}~� �E�c�r�\���
���2����D��P0��wя'?��8�=�`�6�_2Q�g#��W��7=6��U��[�vᏯ�z��9�.���ϭ7�<r� �6F��}i�obty	)�����ޕ`6�3��p��T�u'���zpG��5cr�`\6C�a�ީJ��j[�����Tq�g$��Oa�n����dR�3�p}Q\�I�p��|Ã���[6�&c@d�1��q��@ǯ��E9[C��/�fDޑ�F��T�+�2z��@ec#J�L����n�C���
��X���7D~I�``�/�ǦqU��BK��ԍ��9�c�;�gm��P�ɑ��6=2ޙ�?�k�id���_�‚#'�1�q��]l+��<�o!�r�`�0�2�O�=�f��`��X�G��^՝HIꎊ5�U���)�(Re勒6�����jc,l�k1L���=>�q֤��۶����#�?�jd���/
���'�'���s�J��q߹������3���`џ���)��9��Ƿ㊵-�m���pS�s��V7d�G8?���C]�a2IJ��&Ƌ,$�{��B�@z�������n9������V�
D�J�o&6fp9q���"a��2>}���?ҕ��gr/$�2���Fђ���y�㓏�Zt1,�yUc=&l�$c�#�>n��Uv�˴��]K��O&�����~7�?J,;�R)�X���H�q��OL�Q�S��U!pG|��'Ԍw�'�������9�v�l�Α�Gr��B�O`{�:�BjZ}���`,���:�+\�w13�,s)��s�OB�a����bT�a�c���������*
�e��u‚njv�c��+H�ne8�l_Kyc\y���Z)�;�$��}��2)�%<�d�IϿ=s��ۭ�����@�3�y����|���/%���\n�2Mu�0��#<n�A$�NkX�`�1�3B�3���qʐG�������"�bY�l	
G�F	�O�8��Ir�q�CK0�(�1�$��O�x�j�݀h�%ps���W�����=i1�+��8p�P�	
���=�\(���]��*�ɰ�F�C�4�e���tA���,gg�r^�欋�"R�#����l1_N���g���jm!���f(΁Բ�ޞ�=;s���	��$��h.���'#�}O��U�Hِ��#8ׂ׵Z���dGa� +�ݰ�g�s��7֮�;�r8R��%����� 7%{s�j�(&$��y��˵�
u���'�t5$�x�
x��C��\�w~>�bI���c�n-��wxg={�V�M#)4�#��,�,����^�Zλ�F�R92�#9���V�6%XmLoPO����ǥ4���$B~Q��2=q�)Hpv3Y(ɻ+�ǿ��y���y ����������(nYq#���[��*X-I�R�*1�p�s��5�7U�L]ۿy����@:/?�|f�=P1��`T���FF6��@�Z�5����LQ��d`�o���9��L6�6���$: �L�s��҈)&*��H��ѥ·�ˑN[�G�F}k2[T�eM�sѶ�rH�c޶�A!�ip����ȕ9$1F�0�˒y�?:����9;���G��P|�v�|v�*�ؤ��j�F@V�kpG^Z�~��⍣ bEݸ3r8�׵U7!Db3�	bz�v�u���tE�td��	`-Ԟ2͸�08<��UR<���IϮ
�*ߘ���[�m ��#*j��{bO-�D��@�F}=�+	F����fe�h�hS���FW������{Ss�#��#���q��m\ˇ^�dSӯ��d�0dm�)��S���QY�Ѻ�փ%��vH�IUq�9���ۂ8�ɟ�W\+�
9=8��vTd�ZEw`d#?2�ǩ�NO8=;U��7u9�=�G$}M;ƕh�e|C�V%I(���5� *ې�A�|{q���Ϩ��.�THdbI$�z��ip��(S�v�����Q`�f8�d팫T��׎�1K��UAP�ڒH��i�JΒH��/O��9���4�B��I !��'�����
ᐒ
��:��f�-���e�G�g��J��6c���:w���<h��/*T��܏֭�a����Gf)�
���?_ʔ����#$t�S�c����Ny��*ܞO��\2�&�C�����{z�}������<����c޵�S4��c.�}�oQ�x�dB�����i#	
���,r��.%��у�#�rzt��[V�Ze��0�zw����NA1��qN�wG	,1��9=H��j����oQ��e�$�ޘ;w��i�lŊu8���=���bǽ�bvNy��5v�iDr ��W��NO���	�I]#��.�63�'8���֌q��:�l�zc�{zV4��f�������i�݈S����?�
l�J�{�r����J�#�އ�{�x��zȺid2�� ����'��S����I�xnԞs�d�ǿ������[�eb�H
�S���8�S8�F�+[I3ж[9#��3��"������ֵ|�U��Na�9�_��R�%v�g���q�Fz�d����l�PF�d����C\��V-W3�gh'j�†��>��٤� O�8�@��Q�!��}8o�	X�I2wE��F��[����S���$���PT���-��è� ��X��_8�A�[?(�=?��t�#�x\���8��sڨ��b5����g� s۷��hJ���v�<�1Ϡ4�i�Ѩ##��=G_Z�5̞p;�=3�
w��Iɓ9t�����ǎ޼w�\1���UL1!@��Lg�oƤ��gT�"s'�ɕ z}?,f�Lg�k�'>ïӯsE�طx�H��8�RNI��O�APn����:���o����|��W�\�z��γId;�	#�?����ư\ȝܐm��{���j	O�B~S������I̋�A.3�@�}O�)�3��
�����Z�u�w�Ϯp*�f�iw�=��Mko-�p1�5a
�]��l��RF'}��h�b9��8϶x�֯G?�X�����>�;Ӄ"�	�!!��x�cިŹ&����ϭZ9�ϣ/8YU�م��T�>��Tu��Ke����5}�/���8�T��� �3��~�
���BT����<~��0��0[�zb�

�S<n��p9�:�����Էh븟��}܁��Ƭ�9��:��隮���S�c�l� �ʟj�B��ݜ�����T��ʢORr�0��3���o'��P��Dj�͒-���\pp��-�٘��''���ۜ�J����1M��D0��(��z�_�U)��v�Fr��ԊѲ���K9!���FS��q}���\�q��s��2��� :�X�W9���Q�r����sS�#w�N��8 z}E2Ep�����zz{��6M!@d���ې7g�L1��`��ۏ��SG��"1c�.>R�����)��h�q�1��*�*	��H��;���/�|�	�,��g��������@��{�ɤP �
��<���p{g8�D@�\��8�nG�zi��]G`�r�=�����:�ؕ�%w����d���	��b���q��r�KĻe�
�@��g���2�W%���j�`�
���9��u�[H!�B�:�NT�F==G�
6�Y�0	���*�I*�aM�s�c��{`�����۫�0��}7�<q�ӷ�LaH�>�A�N�hk�i"UU���P�y���V�Y	eء���8#��H�d�-ʓ/�Q1�ˁ���֠�7$��wq�~������f�͍�z��լ5��6Ɍ��8$~D��4ʳEi�f�ʘ���$:�=}�Jg�[�[#�����1���v�:Һ�1���,s�z
`IM�
�����¢Q��e�400�G���!Pn�wvϯ@?\Hc$n�~c��I=}�t�#\J<�l/F��M\�г]�c��6�g9�&�:#9;�ʼn%p��9��5�%s���V[���pwa��@7r{
T�PB����9�~(m�"嵀��m�7�H'���_���(�u���GQ�z�Y�*2���70��T��+��u��'����i#9�Mnh]������^�߯��9�l�c����vO �i�H�n���ЎNz~T�T,R6��r\rx�8�=>�w1�Iy1�-���'>�ޫބyH���q� 䓓��f
�JH`IFa���8�j���\�?0���4�#I������H#����8�Lx%_�+�8��-�m�[+��Xers�9���8����WiD?��r��2���c���+�f6C1�k/V��$��*���Ӑpz�
[�8�F��H��x�av��ל}j��`�v�9�={���!Nn#k6:S�*N�-T� � �c��zM��0���prO��*kH�����2~�9?A�yH�2�ݛP�8%��N�AWl���b�������;O�����8#V��\rrq��6�n2q�ڒs�cpTG��Z�%�R '*�?tu��ژ&�&FH�^�[;G"���̭�j+�d;K��;�O�j����y*��WpN2;��z�V8�2	\����j�|���^��_aUl��7l�G���j%&tFֱ"�wg�NwE8E��z}�c��U@��z�j�>d��鷜�Ϊ$7b�m�,�C黝��v�&�h��@�ryq��h~��H9yHڣ��~�*�!�s��,1�{t<S��5wce��dwq�g���g�j��Ǹ��8+��J�,'����.��r?O�gH@wg,o�]L��!=L�p��z��G"B��NOC��}x��s�]�NJ�;T^�e�����Z:�5aRB��n�1�#����?tc�a##����U�l0!�
�3��F�J�m�����G5�k�RB|��9�v�=(�o&t8S������}=�e`au;1��
�=�]�~U��'8#�*ӥ��©��y�r��OC�|���OH����봩��l�"�^'��u�d!p�q��5�f?;�_�i�&)E�YTL�P�p9��<�i
�p��@�K1�C�NH���h�9 �AOFbK$i� u�{t���H����$9-��?�46�	7�]1E]��᳃�n1ӷ���"Sr8v㷿֭Co(�U]ı+��rz��RDws�G'*u*�a�H�%x$�?���gL��#%��߽k�
�n�rp>���R5�#���Y�	�<�/Z\�5�e!#d!����[85v٦��ݽ7C�c# � �Ӝ���!C"���d�ON*ޝ�O;v�jr@?�8?ΎK
U���l��g27����ϩ��h�
C�U<���{��N��8"HmP�;���j�s>m�2@,᳓�.�����Q�7)���d��*�1PK`g���=�#�*��cf���0R�os�=�,������y ���泓�o�����H�����zԮ�7�@Fd|��q��{�?zJ�u
*r;c9��I�!8#
g��?Β�fҋKR9`'s ]���}�C����(\o�G�Ns�n���B�X`q�r�~�*�J�E,j%b�vV�`�N3�v�<,q�V32ɜd�y��O:uc��z��b�D23�6H�Ny8��Fy�y���X�/!Uv�N��;�$�\|� ���Þ����c���2q�Ƕ3R�T�[�;W��>�����j�m
��H������B9W��Q�9�p:�s�bц���?�8���5%�6�'Оsߞ¥�����1��	rIs��	�L�̪�r���@���.��;(�C�����8�9�r�Z`P�eU���{��Җ�sX�n�@�y�{��{�CJ���gi��c�'���Rmdb�p�ppÐy�n����q�Оq��9�Ks3��b�'*�$~�M=�
�	\���ǯNx�U�P�O(�T��A'�=Npx=�3U�gy3+n����9Ϸ>���hc����X���V m�9#�~4T�l�66���A#�Ҋl�Q&t��B�w�U#kd��zz��U;�y"���@��~O�V�yD�HP����N2pNA��sV�̛��L�[��S;��=	=��O+{�:[
�X��.����s�J�9R��RS;X	���$��JaU\�s�����$o<1*8<���M;1�4e�21i{���6�s�8=�c�UU�U%��@�+�:��BTo:e�&e_����;g���f?�F#0:���ɩr�iY2Dd�k.$���X�F1���I�S�"U���wa��9��F	]Wb09�y���]23�<g<����g�2�[�d�}Ͽ�%rI������"���{<��G������+�$����ULe8� ���L�*�{:���[L�1Hة$�����Y&}��+}��ʌ����*ߒ�x�G�s/s�M*�'�g`��Q�x�#��_b�q�$+Ȼ��+א=9��b�X��QQ�$E����ʓ��1с}�9#��cJ|�@Tlm^sӞ�;g���\�[_�=�Wr�<r��~bFz`~�*<a�
��y��{`��\�;�0!���ؐ���s����=�){�v��*�Q��W���%�j�i3);Q���V����02=}kX�s�,4��5P�m��$��M[�_n�b�c �m�_cT,�X�n\2�����PP3*�����ZGS��H˽�d�H_gc(Lﻯ^qӥGg)���io�*��I'@�0?:���p[��d��r�L�II
����(9*A�2��?�� `��0�$pO��VE�%��p�p	$�8�z��+t
�$m�-�?�Z���V
24�v;�>� u���U�'e}�I�� GnQ\��H~fbz���W���U������ַR��V�TiMe�O1�!���:��*������89����֮A2���T�!��}s���6�k���B�Bq�}��?N��
i�s9�=N~��`R|�v`�v�>��C�
�E���9�=�+��'�Z��C22d�Pa�8����\�v�m.�t�'o��#��S:IkM���V����#*�HU8�PzTk亼��C��ST��Ԍu��5�\3���H�?O�֫\��2�K�m�%��s\�V4���]e�8<��=j;�|�ʌ
ݛ���gޥ�
$\@�pӽK��UQ�������ٯ7.�p�o/1�f��O�=����d���{��[��Vm�QО�{���Kr��V0r��/�Z���*��fN���@<����B
�Lm���MX�i8Eؤ2[>�َ�Ą`u���O�L�|���2Ad1���$7�qʎ@���QJ�K�ϖH#q��'�>���v �������ң�K+���ӡ�.^A��,^�v������*I�@e������ҵIj��+8fS����x��?APy3!Ϲ?�|�ƣ$���Q���`��61�ʕ>�d�[�0bJ��B��?Q���
'\)Ry�~5~��F.I�|��qb�����\6�/9!x�u$AU��!O�^��{l�m�%]��)��T�H��p9#R峹q�̬I:�l++�_n	>�SN@"/�G��I=)Tn#gG�4�.W{cp8�9��z�{����w�9~��ѐ�N�]N6��-��v㧨�D�,�
�����	�)�Le]���O�֋�
,��p{�Fz��ډLWQ*,edz����w�A�n������T�*�	�U9�pN}=>�����6���lyd���8#�}x��ؤN��V����I��W�y��m�	>����� '��Cu�O�N�I��ڥ��5.R!����#xǟ��$��2��8����:�
ǡ�A�Cl�|�T�V۸��#��=j�w "II2�yϦ==���!�ʼz�T�p�Xي��Bp	���{�|T�+@��%`�Kn�����<�@��ڕ�V��#$.�2d�
��ey�yjq������6�xۍ��B26�	^�ӡ���%�E�D����:8�9�M�-�����U�:�~<sDϝ�	���b�݂c��¬j����0f�68��R,�XʱP�!Ю�z�LT6o��9RP+(�C1�>���qקZl�#�uVm�XRT��<�#��<��n��A�6���I<���|Č
{���P��`d��OS�zVl�4�*+H��p�7,�e:�8��OO�C���G�9�\�u����T�~8+������#�=���$W+�)��$�A�N�;ҽ�\T��#��o1�um�z���OB8��U2�v�L�=:��T1�c���W�+Щ���l{Շ��1U@9e7g#���N�V�&��#��w"�$��sϥEw(RU%�9�'��}}0j9�wE����제��ڡ�Y<��R�����w7�^z�u�ҥ�wՉ,�;9P�H�K���-�O�ӊ}�$V�y8��nb>^sӃ��<��v����^��Mi$@|p7.��N�w���M$K�$�M��R��H$�	�H�9��h$��c������>�ϚR��˺Lf6q� ���9''��u8"��%� �b0^����z�F-ߡ&�}���q��p�?�:�P����G���Qƨ��-�]�u'��r@'����u�r�'T��<��Z�s�÷�y���	l�@�#��~]q�6v��$�x�{�-RG�FD*��(�g$c��}*����`X�y[��A<�$�y#?N(���U";�v��<�w��_�H�h�9��ds��U��_&&�ِ	�9��d��c���MG��yFڀ�z{f�)��d{�L-¨ߌ�W
�ˎ;�9����^$qӧ��;T��#v������y4%ry�C31F��A���<�ר�>�ի��	������nX�88����!�F��@r�(��q߆�%M˓�eP
���8�j���������I'{�׎��߯4�$31�1��$a�a�a�;Ԇn̪�;c��~Zfz�R?"`';6�����1ۭ W
P`��s�?���Ut�g�����Onj!+�N~^���Ӹ
� �d8wl�M�UbȲA�$c#�Ҥu�~a���>���L�j��<���=t*����^0�<V��,v�b0FH\�;�KKV�Է�prG�[�L�b(Dv�����ԉԾ�k����T18;��翷�5M����2��1SM����8nN	�?�F�P.��s�?J.RVC�Ly �$��q��1��̧�g�ORT������!�oF�py<5�#+�0�aOS�֣�98#?�kK���S8%� ���YW�R����J6W9sH�:<��Ff\���ߓ�q�U
�C��3���'��71�F:���Zϔ`�g�����ϗ�딬D�#����֧�sD��1��d���{�K�s�Gvٿ|L#c1��?�j���.vwf`Rc�f�a�_Τ���A�|1��1W�c�@r��>�>��IR�/ˎx�:�P��փ	X�ye2q�:s��H�,�6�-���;�ۑ�#�#����F9��^~�[�;+ȸ�g?��׵;X�շ/Hy��J�g�
��r�Ǹ�Ze���	$�U��4�D�RY��qmŷ6rI'�懷icH�$z� ?Z�*�<�=YZ�$�|��/
���?_��#�<�ђG�O�O�[��kWD���ۥR0Wl������]m&�F�d`d��@?�(�Vc:�#�[.|bX���~�<��TQ�U�g�=I�>��<a��`�2p;�߀�j���Ï,����?��YN�%sO��S.����)�:�;aJ�%A8sש��{�S+;\�|�>�$�<
#ɟ�7�g?���X�_c��Z��G
� ��7'�"�ud�ԉw�z� �?ƥ�Y9��2�T$U%�%���������rh�MZ�f��n������� � �����I
��?,�1��89�O�a�*��/B��6�(��
s�9�q�c��UZJ�i��e�&6�Ϩ�:��ߗ�R=���˰�9��'�K�q��q��n̚{	r��B���;F}{�]�(��Č��g���u�w�8&=ܱ%���+?"R�>�+�uRM�������er䟻�h�<��^)c�k�p�y~y�x\rz��}�5W{8����7|���*h�!_,�$1�x#��P�r�������
���:~#��}�6�-��Sߎ�ޜ�w8$�����0*��3������C�������I!
%c)�1�v�q_A���S@$s��U��(<�Ҝdm ��q�ž�ʀ�R����S�z9.��2Ll���P8���V�3;��J?y��J��=J�/'�jي�4�â"�'�~uZ{�01T
���~>��,��M�dF��ȥ�R#ddy<ΉR2ZIK4�#�2�I��g�f�P+V��H��6m�<G�ʹ�5�osJ)SnDh��S����=�ӥIur�)��v�9m݆O�T�EHVA:��f�'�����t�lu��4m��;� �5\�hK��jb���bz�g�|�zT���#l��8,0<�OǏ^*x��ye!T1�rq���͖�X�
F�2@p<��|UF�VeVj;���u�Oe=	�#��kt�Uw��F1���3�>�jpLZ1+<d��HfX������kjз����;�{q�֪�
�F�Wmc Y�z�
��u���jȷw��Kx�~1�����@�_i�@��Te���#��늷;�,q�-.[9'���G�Z$�9�➅]>��VEe6⬻W�^�~��2��NҡD :z�d�@�Ԓ[}+�Y
-��Qۯ#���U�
[��60�	Xn=H�O�W����$ѝ4
2���8�I=��@>v!�W�q�a�3����5�dK�d��*����ԓ�~��q���w�
0~���2K�B�wV+�X㕝v$�y�y��
����"9C0˄nI�_�A�ry�Ug����.7�A~H.}I������ʰ��1�_�>’f�O�
ۗ.����*�
���8�?Z�+��N�F���x�*K�����a+��J7v�L�HǦ0��U"M�u_���^27I�<t�w5��mN���f�_(/���@� �SY��c*�W����9n�OS�MZ�9/�J�
��Pd�	��ל�v��M*J]�@xRr3��}{�Vrw:`�C8/3�<Ɛ�ܗS������� ���v�lx1�g��^��ըa;�gr��|�x�'�'����'h�	�h����
:c��
	
��S0�6�4L$k/���8+R���C	6���3�I㧨�Ҫ�[q��;瞝I��jC��b���Rx����F)7`J���N�(ZH��؍�pz�Fq��T�V���#����9�8�W,�\?�adM�ʥ���9���Uv�ʍ��q��׌z�Z�1�D��Ld������gߚ�(�R�q�v��w��5-܍��C*䑎A=���*0�I3��
�H�N��zi���m$m�p}A� `�q׎�p)-����n���#9*��i��U�8���߂?:tр1��~^z{�h�$�*�!Vo+*p����=����S32�����d�#��!�=S�B��v�`�'�H=)�m?!�v�$��� l���ʣh{׿~��-�Ž��J����>��÷B�	:����\T��4�"D�8]��O�'#�"Ʒ���(F�4g ��FqӜ��$Q+U<�<��9�q�>�b�5_1
���~\0�؁����wΕ�n�,Y���d�����ڴ�9�jVQ+�ĉ�ű����kn�D^F�?Ȫ�x�c�=�V�䬍>v�W*���Z�#�e˻n��|�*�{��9�n�I����TrO=G�Gϥ2q���%%F�~����Eg_o#8ع ���x�:���\�!&'F@�ПǷ'��+-X+�_\B�c!B�>��
e3��l������Fjuԩ4�*�B���Q���88�n�*9�c���'hn�`VS��;)��Ay$E٭�Ā��g
�R���2��z�9f��z�<w�
��xbw7���ZO+qQ�J��62@���XI��6 Dup�n';B��5�����j]�!��r����8��Oʻ
�9r��#�zP���k��lN0qʌ��'�v�$w˳��I�'�Êq�3*�
��2Iz{Tj�h-���s۱�*M�.�H�aP�:dz{s�sRD����e�ݞX���\���U%!��Tn# �O_´,�mb7R�U�X�}�FxNAO#�M	u	4�#��KC�0v�3�9�'9#�O�Q��r2ss�0	}zJYei�̥�����_l���V�3��c����%$5.�;|唨��s��>�Ғ8��o�G��9;�@��8�HT�3�3�#�=;u�<H���ۜ�0x'?N�Է*2Em��n�\���*UWy���L�>�`���gz('��&��A��<�_:��1�U�����ר'��P�q�Q���=����fY�P�݈�?Jp*�p3�����P'+��G(�n*g��p�sڙ�7'��g��ާ�p��	�S������H�rA9'�43KRvC*�$�;T�"����w2���9'�ϱ=9�H��_��3�;� �N���O'���
H#(��#��?\��w�DrI��{}?Ɲ\�1��r�:�:��@X�g�8��"�L���{Et`�Y�%H�c��p	F��lpO<�:}:SB�7��䍣q���\֔�om̑��b4`�zd/h9�S�Z���
8����B<��'��������<�G+,��r�Q�G ˚��G�A��Q�)Y�3�(�qr�ݻ#�lS.T4����Mݽ�Jj��c��^H8���Ƭޝ����'��,�j�D�ӹ_c;��q�jd�0`�;�ӭJQ��lc�W�jD�?3X�ϱ�'�Q�2ky0i���0s�{�H��z�i������[{��C��;�+;o��	�y�_˽\Y]T�U~n��x���
��
Z���f��vT���yV��FGz��ü@.�F+ӑ��ˑ�X��3��7� ���'���?��Z�\,c~�T/*H8�zw<:��VgH8�-DdF;�luQ��	���&��!uR��/͆�?{BA��ҤE�pV�.ܖ�g�q�	�ߓ�u�<Sv1�l�c��V�5�����+GN=�1����yYPm�p~l���]Lf;� �f��&Cs����>՟s�N6��RI c��0{�r�sjU��0иO%����ǜ`2z�d�S:UXn$�ϡ���ӷ{�@2�����#��c�zT3[���T. ��2��j��;�zhʊJ���1\�$���^٦!$���Q�e��C����YsD#�7n.& ���ڣ����$�8�h�5Ԩdvb
�}�Lt��@�Z�{f�}�7H�����m;��	
�rE)��?*I���w9lu4�x�bs�q�GN���2��a���L��q��s�4�bS�#�d�s���==*��I�F�~'�=�]��,��ǐ��`v���T����ь�6�?�v���J�J��I��Lg�I^?¬ʻ�rr
����Up
��Wzp~�簤�_r�j唣ci�0팁����O>�}������,�E��I��sYH<������_�U�fg�R�n�9p2H�GL�������rV
�k`�����=i���b\*L���t�^I���
Ć�9�֯��k2��0���Ӹ=}qW���L�[��V����r:�Չ�?;n�H[��O|�����ڸ��<��pWpel��O�3�j��Tu����1n��^��.���(`�pw3�zaS��)e�8��(�x�j�,k���<��©���s�jW���x�I�
ݶ�{��NqǾ1V��b��;P�DG���y�r�c���
Zt"3��I%ʂ�&r�
��d��ipAEf�PƘ$���r}Lq��́J�+�-��t�n?Pke+��K�؁�K����$���=8�'ڇ��V���� 0�,�<~��
��Ό�ʀ��0{{���ô��`A/��w�j-r[kP��vi�6�l��}���ޣ��U�o0��9��Z�l�fs�M�fN9�t��[�'u�Bd��X��3�Oj�1]L=��X��[4�H���i]ێ9ێ9�
�Ӿ*ͬ`id�F�8��U�d��������VK#�u>b#�s�'ߐO��޲��A;)$#
�� �ק r>�.:�R��GЭ-4�*>���1�:�ƪ��"�F�/�ݴd��׊����_1�FF'�'��Q�sǭS��7�G�=7u��������$����s�q���5�s��\
��v�>��>���C$s��F$@3�2�<N3�u��ioVYm�q"�Dž9F=����c>V����ō�6Ȝݱ�=}��*H.�T�J�C�s�£�B3��1\��x��vi�#�=y��v:������
�����%�:��?�n�ka &`S�@�n?:�jK�$ꮡp�[��z(�?N�RC��Fbzr>n��ϰ.E�6F���B�(��y'#9��ۯ?SJRETS�Q	x�1m�0s꣞�5R�Y>U�T�)�~��*�7l���W�{��֮6{�.e��f*33qс�;�O�z�a=Fx��浀���DȲ~��\���:�Uu۴��8��v�U���ն���8��f�N�F@n{��I
�Q�*��H�1�G'���QBC���W���8������0�@G�ߎqG-�T��%@"e�Hˮ���d�sǧ59�Xy�A�1���
$��0��*?-�
�[�8?��:`X��F<����jlW7q����1�m����݌8#=q�c�u�{f�#p^0zg�����Q�h�#b��� ������?0�>��En�/ۇ����2~oj��y���rI���5���/v�#����d~}�KJ�5c��妛�p�Gʡ����frd�HHic���Cc���U-Qy%�g��WZg��]���Ol{u��j֨罤c�:1Y����B0=O^�v���
Cv<g���r6�u���UU۱$��g)4ΞU$tqɾ7I���p*�O����`�pH?�Oӽe[r�|��\	���g޴`DY�tQܞ��m	���(1��.q��ǧN�[��u\0pYI���$������O=�N���p��s�ⶌ���V�T\�O��0�
G89���瞹�%��>V9���6{�
_�'
�&WL��|ޟ�j��4���H3
��^�N��aN/s�Ԕz����J�� ݝ�R�#�{d�;s�Qڬk%T �}���ַE"����A�Ƕ3�F>���l�3ƣE�9dg����t�v:!YMY�2�+���>�S�8�g���`[HC��r*C'��U��
���O�>��C�֪���0y݄��9;����XIX�[��e���W�����q��^x[?u�"�w<��~^��XH^0���$}*@I*6��q�A߷5<���O��
v<��0��9=�G,?��J#�d�l�O�o�jݽ�3��Rp�FT��_u�¹�x8�{
\��)hgJ�������(�ɅIs��?ɫc�;@!r[;�o�+������KC��Iadwڤ�,px�~���WS{	c��7?���*��8y�'��~�5�<>�����ۊ"]WtN��5�^��[h������8��?��m׹+���ozI�cj�1�q�z�Z�$r*�%R�*����A�>����a�|��:�� ��^�<TlJ��V�Ϲ��.���Y����^Ƨ���,�27�zw�;��Rd`����aO\���_���.WH�g�>��5i�?u��UU����5��~�Զ��1O���^��Ç�+>P{�V?����9�ܺ�)�!ҬbB.�~��gߞA4�1ʨڧ��l��*
�ِB�g�Ƒ�
N����	���jw%(�ƨr��>��������.�)R0x�H�GR;b�F�r�)R�:��l��ƢI�	�V�i���(��B��̊�]�������ҋ�K�>Z�01�$�{�g���P��=�T� ��y�=��~���L�H�m��E*� �S�I�g4ya՝p
�s�06��?Z�{e�Y����![9 ��q��UV�]������Ӑx�1�{Ը�ƥʈ'���u8ǧ���9'��$}�d��U>Kp�0'i,�sߞG�!��bK���=q��?j͢�B�#��rps�q�v΀N��)��tάZB�n*H,p���9㯦y�L
��I"Ap�𥷪�|�#�\�˭�_ 8���n��ۈ�{��}��"��1үZ�Ȃ`N� ��	�O�${~&��ZG䬰�F
��u��1��U�2VR�)�!�v�q��	�c��C3썝$̇��	9:�����.�˕f�-��	�~���.���Y�9 {��6tF�s$R3@�` %�c<�#<�\�HBF�o�2���'��:c�6ѢQ�	���	��I<�@Q��k���ʪ��3����Y�X��ax�`�"�1R�\�#��q���k(w��6y�6��H��t�U(ȊW��!rpzv�T��)i�;��Pnbpz��׵.k��3ɘQ��1'9@A\��1�s������-�C��s����1$T+)v��pz��C�:��I���?��#9�~�ꓹ��K0>�]���_���K�p}j�ԡ�1��r;q��#֘�t��o���w7l�|c�A,���D�	�q���ӡ�R�aŸV����AX�o!���$��=�{�ֻL\������͂ys�����#:�l���\w�y�X��c`	#��ԃ���>��ܹI$=d�-�����v������z��x��2"��D�#/���@’GA	�?z0	u�(@?�G'w^_�)��!�V�G��s���F2w�I��s:�?'��s���:,��fx��^	O�
wl��f‚	�09�"�i֑��Y�0Q�OL��=q��,睆1�o#7';W��=��*�
��wrK��	�;�S��s��`�\���U�l�YU �,~aUm����ܓ��ry�D����;�p��X�t8����֬�[�ځ�6�~�yC~d�<c��Q�$�$�+mO��q��Ц��%��8z�Z��U
�T�o�;�\A�Aڜ���.	PU��?�?*x�U�S����$t�t�(J��X�b$�
���v�r7c��N	�>߅^�D�L��G���x��gy�������1`f�v�S��̙b��'$���j-J�؍X2�؀(�s����ڑ�d�,�*��҂~Q��#$u=���Cϝ�URN�>�{d~��Ղ�Ƒd`�I��K���Ps�l�9�:��|�C0
FF}�Ӟ*5��X0�o��#?��N�b��(N7��qP�O�$��z�L�Hq�Q�����p$�#$dc��a�Y�ZP#���Z�q���m�~����zzm����P`:j�yK�1+3�=��s�3�V�a�(�A>�׿WaU�s��=�Y�;�ʛv�J��ZE�@�u9�E��F�������C<d�Ѩs�����7�~4�X�A����֋��)CgWa�<��9?��nE�%�9��O'���Y���zg����ʥ.i���x8�r�o^q��1R��
[�o���65���-׸=N?*��� �p1����~�/1�)X�0BO��1�Ir�<̐U' w��J"!\3��6H�?�]��"����m�?��j�uc>Wvfmʤ���z�
�{~�KLF@,��1���j�Ͳ#�@6���?��J�T����;�N/ƅʵքLV&+�?����zg��Qs XՌĀ���zcК�ᑷ��q�?J���X��#y[X�9�s�z{&��q]A@���r��d���U{Ō��K.>`���N��LV9!+�U�0�#<�sҝ5��T�c^p?�'���Z�\�I'v�m`�w
]��;I
������6�۹ʁ�ߞr{��J�<;����@Ť
�J�'�}x�y?�?_Ӧ�/$��(��ݼ{��ہ�+�R��z�ʼ��0���6^p>^q��9�J�y�S��(_�Cw�;{��S�I
���!`\�ÏJι�1c� �)���>����%)��v/��*X���ҨN�(V|�07��Gs�j�%`C��@#���}�_%H���ǡ����k'+�u�Vc�P��
���h%���Xws����VnԨc�px���{���Q�(�p@�?�s�Oc��5k�X:�df˨�G�wGPG�qߥl��8�D�I~\�8�_�8��+��<��N�V�V��gWP��9��S��
�RQ���
�=��5Ud��v��3��?�Y�U�e)����m/8P����P؆��e����ޫ��?7C�z����ϡ��F����[뚥�! |�lXͣ��v�r�ܛ[}�"ϔd�\�3�y�Ι�2L1fF�8L�I�p�S��\�Hv�� ��'8���X��<��L��NV"xǔsmPYvFz�=:rG�>{m����pJ��v�^��|���������:��,[�u�����y�~���rei�ewHY l:�䪒v��z}*�Bv��c�ַ�db�g�)q�v�rM<�(���G �N2:d��#�X�
�.\1}���#�N�0�f�.��	�V�l��z��~�hC�P�9��U8����U&�%G��
�h�:���Z�{!"�B�������0y�)��+�Fݘ�������P\K&
�ڌzg*~�u�U��8��_N^��k6�5�osR��T*��
��u q���Uv�qD�24�̅r�9�^�=x�QV�f�7^�;��1[���B:~u���oǂX�Ǡ�GOJ���"�^�!�'���>����N�#^7�c&�S�s*}���W<�'��p?�*��7�S�F�ܓ�ӎ���S��H��(���ĸ��q�3�R�_��7$3�8�=yr
+)
sD�Ud;����@e$pFz{w�O���E\d?3��0	�ݎ�u�)b7dr3��u���x6FZA��S�2?��jlo���b�5��A��`;�}pp@��<���6g��9�����±r����rzq�)��5 
��^��yԒ)�L�ˊ9	��ǰ��ǯ�Ӡ��&Y*W�z��J�yr
��1��Ǡ�=�ޡXT�M�?�>���Z�x��з��T�M������Jt<�~�*��>�����!FFp8��6����L�y)����JѲ��?>�~^�A��z��֐o��K-bK����Q��0î	��ێ�L��m��ǫ1��Ո��Eÿ@����S|Pmm�Ho�)���3֩�Bwz����L1��K�����⼃�?��{V7���qϯ�JԹ27���pFz�s���c�3���O��V��S}#��Ojr�$h��'�ZpD�n�rz��T}Z�=�,K�A�צ��D��l,�:�܂9�[����q.&��A�B��*#��+��\��N�9Q�?��NEMI?�32)'���ğ�֑n&5-��Bݥq*�+&[ t���وDSۄ�z�V�#�V���*}8��Z%a�ɍ��n��x�6���K��Q8T*<�!C��ρ��{֥�ѭ��$�~$���ۥPf�R��x^0GA�*6*AV,����?�4�c;��J��0WQ�xeE
�F�s��t�;TM���~���V���#9�9�����طcr�0����r����r;v��K���A��1��<�k�XG�<�8#�í[�Ui���aA��F���[W����:����rT��ei�q��99�=���L���@T���~�O��c88;��lS�R�he�Z���̭�;�'��qU�@ey#.��=ϧ�_�k�B�����a�3�?�g��"��眒9�Msɝ�Չ�D�U4�8�@7g�x��$+�����O8�~�O�S��"1c�u��8�a��Q�2��Hw6@�Ӂ�<Ƽ����(�1������;�kk���xч%v�c~?>���Eq8�� n� �ˑW��[�e�J�1����~���4�QpՕp΄��rv��j�j7�brq�z�ۚЎ�FfD�l,w�{�L�ܰ��U�H*���jZ)N��DP�)���<���
$��\F7!\>��I����8u�?Z���c��?h&�.�0�j�����֪�)�����q���j�$�d�l�OpH�F3��MFH��W�(X�M���y5L���Z6ǞX�HS�݃�g�N�X.Xg��?�n��Q�UC.��x4�*U�7�@
��ۃQsd�D�&X�?ʝg8R@��8?Ρ�R�wc�֕a��s��ա;loژشS:"�X��z�+7P���B	xՊ��c���q�O\��W�A�?��?*���4T�T��9�����U{�ѵ9\�x� ���BGQ��$ܫ"`o�c#?�U��y"��`>a�r~���T1� ��1'n���PѺ�RW �;V��� `��T��מ��Gr|��@1��r��Ǹ�ӣH划�
�y�����2��A�����J�2[��ض�����a�h����R{�� ++�<͇q�NM6l�'q�q�\~DSWBmI���X(l�3������mݴmu傝�s�z�VPdg�HǷ������yL{U��vK�r��隕;��Y]o�IҫAݔm�c�w�`81?�5I��m�p݂N:^
]��ñ�eHRF�rH�VpIct�.�R�g��52�eE�]X��W$g�?�¤Ӟ8�����xW-���'�P��NA��R&e�JI���_s�U@����ۼ�Ng�t�ӵ�6�/[D��pK&	�NG���5���������
�k�b�YE���0H��`�u��``�~�����s�>mL�.�
0X�����sמFx�o�әB�e<v�g8�涢X�m��<��;w�=x���&�#���8�����`�.74�"��6!,�P��
�<m��g�=F0:g4�*���h�<]�uq�r0�u�⭺+	�!C"�01�<�����"o&H�o��,�2�xR3���V:�4�3.���`�J�!A��#�z�����1�w��08�s֦� d�>]�������օL��)ڮ��y�^�ʲj�O:���?*GX���IRFy���O���pu�2F�=z�x�����oI1e���G�����jP�r���Rq�N:��'��M]�+
�%|�J�`F�1�9=9ZdeU�#O���,_����8�z�mfd�'v�' �:rW���=E,q�]��Ң�r����=��CWer�H���ګ��Ԓ������kJ!�I��7���rG���$�T�,�gs6�z9���~�L�B��aK:��=��9��&�յb�K3�P37�$����:�1�QI��r��� <1'�'��p{�g5?��6�F����������6<��EQwEb�rpNq�����1�j���R�*�(Ok�����
p.x�G|�=FjH�y�D�$d|���I�@�*��D�Y��N��'���w�jM�XF���ێ�;~f�Q���y�)�Jm�W�)㓚d� ���8�M�ɖf�g��P2z���M���*L*�N�3�:VT��Xٷ62�ϧ��ޔ�T���U�*�I'���'�zS&Vǖ�]�*�1�ޟ�~�n(�C��(�s�}@Z� ��d^�����?Z���3'��ۣd�O���#�zs���K$K$�FV�0�@��N3�����Ә)VX��:�A^v�K<��@�{yN9<.���vD�2��Sn\
�8�Q֔0��Il#$g��gb���)����	��r��`�b0�����ܙSI
�bHX�͞�R���l�����r޼{���S�&�0�q�
_朰�Ԇ$�3�=��JvbSIXs*ۼ;m�x���y<���)��
�?�v*��q�=�t>޵"��.�
��
r?�<��D�2cf[�����J@�j�,X�p�G��0���r:c?/�����,�A\�Ҷ`�KU%�y��͐3��}�=;n�FR��$��eE�$rr����q�^�}�d^�m$|���J��,�Y~l�����}�D���$�~�7b)�'���͒r9���AL�I
�3�1�tПƖH�D�AϽF�Lo�F󂍻��*nt�2�4F<�\I�߸������ҢY��2#8�qP�߼�r�I�I.W���I���N(���-��GS���#�w
�B��;����a��A�EP�l�׾NO��v��n�1vc���dg>ރ�oKEZEX��f��Tٿ%v�:u�$����H�cc�Ut�?���N۴d�s��s��jXai˴��bxQ�k=���"����(|��Kmʃ��8>�����_r?�YxU�Z��m)��<{�ǭ?tvqneg�(��M��Bз0;��`>���{z�9��qQmBd�=�Eݒ�w�.~��Vt��2�	8=���/ƙ��3������>d/g-٦Lv����J�;��'�q��֢��
ĕ��F9����8%ڧ
Bg�F�֚�b�2�cp@�?:D���.��;P2��d򣃎�>�8�Z�T��m�C�=��J����l�Y{���S��؛s�<�oln��֑v0���o=�Ȗ&���B��kSV��Ԯ���T#��7�0�~^j{�#�ڋ��)�ֺ�]52wح{t��J��H<p�A��W���vƒ���zs���ֶ.$�h�w�}*���m���?�rU|��e'ekw�@���q�٫Zu‰Y�1�H�x?�k?
ˌ����@���ǫ��Y\��&��G(<�d�d1�A�?��eKI2¶���rG\:d��I,���å2Tr�w�������n]9��R��-*�+7<��[����®�ۂ�>�J� "������O㊆T�?|񒛙e� 9�nL�Z�
˙��eBv���9<zT�I-��1�%F���Y�,�U$��=I��T�+�]J�ے1�d��p
C�f騣2efbq�:g��*%^��#�5�р7I�#�sU�F��$FZ�y��}�:V�׳Ƭ�78A�םĶzg�>�������RG���ヌ�Py�Ď0�q�^�k9r�
Gf��۞���&U;�<�' ($?Bz�py?QO��H݄jVE�r�FA������	$�����z�j����	�y��L��S�j�J�
�n�%���u��ɜ���ۧ-����ɯ�%VU�}�(��?Z�{YeR�~�?�?�A%���=8Qž?�s�M�T�DrDw,����x��Ͼ*U�4��y��oS��'����;�U0
�'!�yǷ<�ҫ*Ȓ���q������4�+<y�3;�ª�q���Ӧy>�-��y�F
$GӹS�~�\'9n��Z�1�����=}sR¾fX����bH�,�?�Sa��lW��2Y�ἠN	�9=jX-������'�9�1Vc�`(T���?���R��3�HNN0�pϾx���{�ʭ�4�ێ71$�z�S�2Q�p
��$g� {Q%�A
,{d
����nFp}�z������v���{���F|�F����)�m��q���.���Ȓ�?.=x��[��2� 8�=8�~�4B6�vR{�'�'�#���MJ�"[�m_2mP=���Y�a�M��!����>���+Hُ�1#��?�{z����n���@��F�&���x��P��q֏dd��I��^s����k�L�|�p����:`�#5��n�X���0WX�B�n�����L��Y2F�+�u+;(lsӧA�ן�*��g�J�+ey-!1�+�`�(8R��8$���~YKr$��
��On~�󫺳��Ù%'a�2p8�q��6H����@T�K6�g'�N��rI��$����:�Ԇm�+I�~�=��A�MM{$e���X���^O�#��'%��ň-�$J�����	�p{�NMQ�C:�R��,�)��㓀G#�R�E��J�a���<���,O��3<�'�q�Q�p�������1OpF#�E4�W�������G<1��5<O<��8ˎ6c��9�'��G��:IhW(d*�Ͱe�r�V �;dw�ZO2�˦���dڣ`Vm����<�~U�5�*�Ț�1۟��r� #$t ���3�f@�*��I2Y��}GO�t*Phj���a�09…,1��;py�5Z�Y�BT�p�ۡ��SE�aw��v�z���s��*;�� ��68���xn*��1mܯ4q��c;X��vH�#pG=y�ח��<����s�g�8�4ۉ�,~e8�z��B����%� ���W��!rO�5��ge(��!b<�v�s��IϿ֢�1*��wz�_^*�*�:��C�1�F���s��Ɲ^0B>\��g�R?z�l�A���w*�qӑӿoZ�E���hs�z��:�%,Hdc��n:Z�����,O�W�3�=��Eis�܆P��#���(pM��Nq�:{�i�&�.~pUT.��9�#����(1���^���(m"�( F
�>Q܏�֒I��c�G˒O���u#,q�}?��b��� �c��=*w��j�V��
pX�����3�W"cUH��~s�B=�)����Zum����ӯAW��*������s��ZEw2�R�"E�C͙H�c�s�r>�v��(dg	�����~�k>常(���.������;��lk���#8$v\��\֐����z�#)1F�d�yK������=~\�4���=�Ƞ+|�:�
{v��Z-�[C$�dG�w7d_���L�K�;w����0r=3�G����IDʜ%Q��%�ȸv�HFl���t�?�4l��c�h�$z�fU(w#6�cמ��Q����@w=��q�sʧs�4�\�������$U���ۭZ�Qa2�<�e@RA����s��J$r�z�
r�����yp�h
	8
�r=
]�/wK�e��r��,~PH,��s��u�<%��E �Fzv�v�*�p�m
��g� ��a��a��O檳)`H
�����=:g�,���ڲ!‚T���$`dq隯:ya	`I�?PI�ҭ�)2�e�bU��A����ҦIe��wH|���X�9�q����o��>�Ԥ��Hr{/��ҮÉ�Xż^f�p��0��Q�,�b�!X�ě�u�C� x��Q:a��*�y^�H�NO�D%5a��\�������G��Ӵ� �T�=��${U�Y�3���z�h�����3�pM&�	]�VL�R���dc��գ������p@��s�S����PG���z�ۧ㚝�PD���#���t�Qf��jȂy>Ҥ���I�Gn��ҫ���ŏ8zv?�|���wFB�r�'�ۜ�U]��%�^0q�^�T=m��b00�
�|�F9�8�=�)R9 ��c���' �L�G^N;�V9&�T;)ݱ�=2:�N:�"�t`��@3
��z��y�\n���ƭ4�J�ArO�Ɍ�p��pz�c
��p{�Ƿ�Jَͱ��܀ �O|q��L��P\�Ry_(� ���>lg������Z�+�cE�+>(����9�~�������_,���8vM���;T�p;~5�%��!?uY�	����A(B��oΣ�rq��;���ht�(��o$RFVM�8|��d6qǯ_J p�z���On:άJ���Q�����=@��=)���)��ų�q��ѱ|�D�}��R?:V���!*C���Py�N��ҥH���!�n${�~���]C�a�a�ps��N)�ٚi;���$	�a8
p�;�����֟3�"#�H�w�x9ԲF�@�c*�R�Θ��Pp�{��K�ܹMI��F1<�$�#9���z�d�mVH�΂NY�Bzg�p~��G/ʓ[���<PU��f�������q�*�nb�ffH2��T����{S�B�ː��H�@֫�"Xh��0F��1��6�`O|��*��K*eC3����G=Ϡ�z��i̘mb�p���ޣ���a�1��{O�O�k��%��R�1��>yn�1�n��<2�0
�!x89Fy��y隸�g+�,H̋��L����{U��٤�L�2IߟQ��9?3Q��Ƞ�W��}#�q�d���T�5��$a7Y��w��O=}Fx9�It2���)J�ɑ�6�2r��{�O��@#�����*��N����p���;�$S��[NJ:�=>�F*y��pSWF�S$`��'8,ø�]?�Y>��o��r1��{z�99�U&B�g$�R�`9�q�t��m?�d��Rr8'�#?�g5��m�*Qr�n����4�f,d@�z�=:�N8�z�m��1^�ӌ��<{U��+�5�2W���8c�O�LH~V�H�Б�����8T�~#
�z�,`��77=OBG�FG�j	�CH�#8'��*{��|ħa`0��9?P*a �ޣ2dnbH$u���>�J��grvI��h�E`v���1�`��ƒ7�N<�Ǐ��z��
и��mxw+��z��T�#��8\���䎈�IY�Fy2��z�-��xBGNq�pqު`�-�a�}J�r[�A9Rq�}�ޒv+��f5����	��sӶ
�bj��rq0�O��9�j��B��<��n��}=et}�T˵[�8�{����M4e%fAu#�A|�������T��I*N	���?�*����w���8�t�=�VL!��:�<"O�Q$i	ة��Ȥ�=�?.9#��i*��hRn�:���c�ޢ؋a���Ѯ0q�psߎ���V�6�V�v�����'�9�qS{4��8�����u'k1ޣw��Ã�y9�]��j���$���z�+:L��a�!�T���z��8��n�*~Pĝ������X�rTM��T�#p�۳�H>�_ g��#�v����� |�c�'�����m\/�' K.�=3�<��\�0ʀ��z�#�rx��y��Њri܁��X�L������=���=q�Ԑ�cˬ��[q1�#��5G���x㵉f����Im���oLq���/�n-.��B��#vC�+�s����
6EJI�o��+�G1�a�:���z�5rج�U�nn����{dɎYW)��a���7|q�U�p���݅<���`��NJqv"��з�E�̑[�,�p�rG^߁��#J1E�
�	�F8u�ж��-��͍�Q��gR)��#�"P�c�I��2@��9�
7}̣$�f����!?i�EGB��9�:��g=���4s�U�'��đ���~���l���`b*�d~_
���׿��o,@F����Fr@��O\
�5[S���G�Ns�c�\{gc�=s�+O�g�H�[�d1��ZJ�@��:����'5cþ��L��k�"xP1V|��k�ӏ38�.i3ί���2B����?^�T6�.dH�yW �����(՘o�*�>G�O�=k)�<��L�{�H�z᫣���4Ng��Y3��0o\��5&Y�l~X`��R���U���J�݇�����zg����<q�\�z�i��B����N���u��<��?�#0�rI�#��Oz�kt�Fз��@f.B�
1��;�P�
��p9�F��~��_JVR)9D�Ely@�n�ul������,3`�.�lj��Lr� �~\��^����z{��ӵC$^R��
�#˷�P�ch��RX�hܼ.��������*Y=�Q��ʬ~\g���8�~�m��u�����>��н�;��P�q�z�?�)$S��!d�7��2v9�#��)��K�C�H݅����������q����?j�8U�pA�d�w���jKq��i�yΩ<fb܀2�eNq�g�V�#/j�%F+�3`s�S�q��^��
���1��z�ڴ,�d
�)�?)�x+��bEi����$��+�9@�'�p;���k�Q�sr�I��$u@Mj��m� �1c�z�g�d��r���9�:�J7����z��֌�9~픩ws�1��>��Q�Y����x�kB��l��3�Ӛ�eP|	��=k��7maYXg�	����x� ł����4o�{�W�f�&=�K7��g���RN�nZ�6�D��I8��9ǯ���[]M����d��y��UU��';�0Gu<��n�:pj�i$���	X���'Ӗ>��[��K-H/mH~A�A��dVY<��aP;�lָO:��y�K2O����-��7�f$mV�'�}28��bcQ�?N���I�݀ʨ���\��ү^Οg�G�prHa�׎s��R.U��į�p}�
a�w�͞���Z�s��C"HeL���9��c�eNrs�?���7;�N>^p1W��X�(p�y�	�ᚫ�/t�gt�@�����:�5�����G-��ȤT�~����6)���_�PK�3yO�Np�1���U,�8j�t�w�����y|��9��z~5U\��F��H�{�z}?Ƣ�2�@=
����b��6���>�4N��#tD�*l}��p�[8~NLq�W��`������?���ź��9<�I��5A�Q!�>@I-�Ϛ�: �XgY�qa.q�c���Q���ْ��n��?/����X�r�ʶHo��zv�Dw
�����ޗ1�#�j�n��.`�ǃ���� �u�xg`vlR��\�?<OZ����~d�@^�=3�*�̗��Rl�F
���(M2�\5e9��1#$��q���V�@H���G�~(� �e��H�/QP���[�@����R��%G��	rAP0�q��{zR۪�r@p��֣
����$?�q��j{np_9 �8��ښ
�B��Ĉ�	^y��j��T8R	�<���8n+�-�~k3��A�dt��=�
e�2�\��\H��w�fPD�ѐ�R�|���Q��~�GO�<�O��
��$"�� ��; �J�f'w�	}*.j��T|�%Ԟ��֧��$��N;���hjF���8�m��}�ýZv&V�謣��iR@�8$�߭a�Y^=���7�vϷ_ʭ�g|c��u��;�ք]D�}��.�Ñ�r}z�U����ȕb� 6z=��2�FA��l�g8�9���>xXVc���9�L�{Y2N~c��4n�$iQ
�� ��ܟ�Cp��s�?0��cל�;p~�:B�������:�3���(XrT���v�z�t�k��Gr�3o&Pr�vx�U��z1�d����GE�Mʧ���#��c�pwr~�8?�4�Ю�Ǜ���>��F	��z;а�S�������
��z����[;H\`�t���h�@��rr�y�#��K������[�Z8���@��EV<�rsߒI�A��.�`����N1V.�0
��!~P�d�s��S�H0�q�!q�J��e���@X�B��@��:���]#	��c䔏{��0�G ����,H4�LG�g�����F�t��q��X�=N���r#&�_X�Z������OLt��⛺Km���R	|�����s�U�����C��ܮ�ps�b(���#�g!X���ӵZV0���̸��}�|�	�O�<t\���Uom�w���!|n�<6H���^���#�ݷ.�B��`�'��.�7D�����1~bz�v�<b\.kO��~X�,�[$�9�`9;�N;�1M�j����>w���q‘��{�"���D�̛�*�y�y�sZ�7*df6�[o�?����d`VMX��z��)HZ63Ŝo+�x�NpG|g��ݩ]��0FT�1;�'<u��t⤒=���g���ѳ�=�w�g`�x��|`�Xc�?<��M��L��h�YQ�&3��px<����������y=�?xb�[i0��F�݇N1��۸�c����z}9�s�֚��(��WW�U9둒~^x8�;��F��{Op�w�F��<���v�{i|�,�>�$�6���1�N1�ӌm��;�D���{��$Rj�SD�p�?�v�v>`I�G^8��ۅ�g̎ �
��}x���1�PG�P��&���s��O®��Yfdm���߷Ү)�"�Щ	�g���Ib��9�``Ӭm�ܯ2m\������rL��0܊PK�d�
�c���8��}0�q���@��<c�~PNs�p+U�j��V�\�Cf�]|��(����H�x�9�X�$��ƻB�e3�`q�W'��_��)�=G�<t�l�ڎ��K��?�?Z�F�#��U1+Z ܽ�:s��ˤ���8����ع,N�����J�y�{dVL��1���9%vg��ҦH�Rr���c��~b��1�9�>�>��U�, )b��Ϡ�5mS1�$�G��Ҩ��y7�7��a
ЎO��R���3-YeR@�1
ɹ��'�d�ԭ��I]
y�H�$uo����=�<rI��������T�ri�W
�"ƫ�6�r�#����H�v�Ln�����Ԑ�#ۭ<w/+��g�J�Tl)fvt<끊���,�W�����SN�ΚHd������_Z|�)W��F77������M	s�����JsC�YI�rB�gq�4�MZÞ8�:FP�\���{�Z��ټߜ�f��$��;w���T�B�Ź�.�3���5	�®�|���;���,)��F����h_\v�"�%�H�=@>�Պu�'*��	P�9�oֵ�i	@�����F����3��Z2��I/Rl��6�*6䃎���X�[ٹ 0$p21��dY	`K�*��=�۽gI��q��ZMؚQKQ��\�����b~��-�<�yh8�?��Λ��j�㱦�<�����d�lT��I2č��`6�3q����Zh�{Q���q�A�{�.2OL����L�����9���N(�A�7GtQ���ʨܪn���F���Zs�G%ɚ�1+�P	`�����=�s����pA�s��5RwV2��!�^! xJ���˷���u<j)����\g+�{�L���h �@N��<��SEn��F8�ǀ~���Pt6G
�Uy�D����8=Ͻ>�t�*ƪ��+��w��SK
ʱ�����s���f�b���8
�Zh���!��[�8���=�鞧�2{F��w6@R�Y��l��ݻ��������y$rGo�8�#�!D*�S�i�!{9-Mq;�C)"A��$��0���p�:�j3p�+��B��/OӊToU����:Z.�q����+���BĶ8\�x=8'�}{U�c��䞉�L~?>�|��6�r?�qK5�>Wm럮���>ޕ�]�y��n�qe{�C!g��q�{��kQ����I&��	ܬs����2�*mV�q�dT�#q�����U[S=[�J�鶑�x$����gx�T�fא�Aǯ�~�˵k]<s�.0���Z�qp���/m����U����b��QN��GO�]��O*�r�v2C���Y��q��.H�Z��w����n��a�W��7!����=�fII,o�%VN�c����FB21�I�9�^�¹�F��p�s�8����֝Gb���)e�B�,O�X���Y"���5��	'�՝�TI��d�!G#��j��,q�p���r�2?�J/m�����dIIR�b	
�U�Y-�T�
��*�A�Ҭ�;؁�I9�p�О:`��'H��cێܨ�A�;L��t)$��A,X�>��#�*�T�`�?�_x�yq�yPz�C�.�B��9���g���/H�Q�$����(Rx�rs�1I*G�a���BF}�pq�����Wn;㎕dcs!���ͻ�y�"U��#`�Sh���QRZ�V9UW�_��30�x��v'�:T�,V�]0z�*P��J�}1�' ��9U���I+�)�;��	XS��ˮ�d�G���v9�^O/,ُ�^���*Y��o���O|r;����`T\3���{X{��%�D�V�ey���<��1�z#�?,����1wbz;�g�^�&AXs��wd�v;g�ʕv#��Om���c�����-KR���8H��)�Ubs�G�=�CR���7Ym��y_��݆�𴪦2
n�\��7]�>����2<�w�W!��R��4�9UH�"���,�s�8L�9���lB���0x�>���QT����v)\Vp��z0'=�@�4�r7���!�\�����0['n^x�5�UV;��P9��1�ɨ�eŦ�$L9���\�$c��y������ιR;/�>�UGDE�1,��o��+߮x� ��r7��Uܙ�.��~��>{���{�E1J�0@R1�@ ����1���h�B�$�'�7<�:}kK���t��b��r2OV�s�sKsp�H�$[�A(1�.��y�y�L���1 r'��3����8S��zQtZ#b���n#N��@�p�,3�:t�1N�1Q�YFӻ#��*H�
%�l��s�i$D�A�/�E���_�RFa�ϝ��8?{��D�4�n`@��8?�jXc�rKc��񫱗0�a��(���l)�qצh_2D�q,|`d|}jX��/!��<�'���>��qш>���Ub9�2�d|�\�����:g�4��N��2)�1���`�5r{X݈G���P߇�1Q���m�6|�ܝ��y��5�L�3OfV����1�s�g?��K&UՊ�p����㚈��Rq����^7�+Pp�O��T���՚"�T��ȩ���e��A9�����U�_#��s�݁���v 6#��ST��V.��(�w�)?)�u���n�*���?�W@�K1�)c�r(��0�Y?vNB�x8��_S�­4r�&�I�Z���!Fs��G9�* �H�>��h�21RwO8����wZ�*�!?,{���L���z���e[�2�=*ɶ.�bn.���,�S ��v��.[�{�V0���������	\�;�#‰��d�>\���U%������{Z�H�1�73d�>�]�=�?��>��b\6_�ؑ�On��n�d���f.�	|�4�i���q�oa�Kn���W]˹w~��=�+묺�mU`�8�8${�߹����e�Ӵck��F����o�����+��P��@�=z�%���pA%����B#. 6!6�#�����r;iӲԎM���y�<�3�c��*t*��#;�����c��%`�3�Ǧ�����t�T|�����[��B�У�\B
�-��n����ӭ�R�9Pyd�ݷd~u�U
C62�G�wc��<翧j�T����c'�ջ�9�s�ClM-��:	&U��y�p:��$и`è���}�:u�YѡT�$��}>���9R{���kH�	%
��+��=����Wc�8T>�w誣+�R��"�gvio�^:�+�]:�����ߍ7c%'7anW͐�,��9����碍��)��G��T�f��!�;G9$q�ۧ�F�%ܧ�O1�%�,<q��R����'�G �X����F�L�AP�8�^����x$���<��^�)'.9=+F� \���ϵVDq2�Av8�Y
�	&� �$`篧Nբ0�l��m.$�)�`��q�P��mP�`��;�Z������TO)]�H��;�v�(r�
4١�(վY�A�G�� �ީ�DS��<��u�G�Jd��Xm�G����t�R$�sˍ�9�@1����Er�����|�3�#'o�N:U�#���ͣ���\sө9�R:&h$#h���"��y���H!p�?^���=V���+#0tm��d����%Q!%��^��ҧ���i��HV����ҫ@���ʌ�qҜ���m�����6�Rp@�r^���.[$�q>Ǧj��J_`�H}�n� Z�	�c*����2����ԛ)J�\��9���&�䫕YA��Ӹ� �y�����p9��
�"r�C0$��8�Z	���kn�lg(�&����d�J�yj��x�ٸ)�h�8�L���+OM��g}�b2��@!zg�2{�g�j��FJ�S���*rA�?J��d�^a�3��ẁs�:UBZ$�X���v�p�BH*N����:��Yױ��(� ��N�ʜ��՜)1鸍� �U�f*�/�$'!���=��t�ڲ�B��O\gwu��S�|�#��<z��[��{`$�6��#_������;��\�ۣo��ջ�Ȯ>u+��d@����W �n�X�iX�������>c�@'�'f\g(�dO3ܢ	�vmʼn��9�$du9��sɨ�|�?�g���U]��}�s�t�޶V�g��?.O9Fy���o�y$ɷ� WQv�۴�Oǩϥ%�<BHø�M�A��'���\�>�i��B��#���.x s�G�En�d�dG!m�˅��3�@�4��u$U�#A��z�n�{`�i8X)�Ws;C��<����0G����4���i&eS�X�g$��O�C���~���݇@;�?�}�ʤ�.J Y�b7��I�/�N?,M@����c��T�(,`R"6۳x�`���#�1,��"(U;x
:/�:��m�����E
�dJ�8\��z�\
�m�"R~A���GR	�9�h�N���u���k�"?0b:���kI)�2��*n˖?�<��:��Tf�mr�k1*p7rG��Đ;�~rȬ�ݛ=q�|}j�Kc)KrY�R�7�`��q��j(�.�p\�ҒG�;Cch�I�۽65I,O���t�{�$�C}�5+�1�������Yp[���͖(�pd��������[Ȼ���V܅�H`��*��k)E����=���UfE*�lW��!-�#P�rx?^}{t���X�;v�S(ARW����׃Z��J4�P/&0ʱ���{U?%�{�Dd��="��S��U�:���})D�K��<u�քd UM�7 \G��P�1@�#�S��s��0>�2m�AL"�bv�d6��t��隝#E$��`O���Ni���A„a��8�*i�Q�b7�����)ɶ��,w(��q��ۯ�V �(ifc�9#u�O#�l혔s��ʓ�}輑�N�Ee�@�~���U7��֮�w���@����9�0s�4�c;�]N�:�L��S�-�,�%%B`��g��i�(b���~�~lt<{�R��m�D֩8i���H���צ)�ؒ��7m׃�����I*ope�U��z��Ê�&,O�\2�u8�玜U)X�f٩熅#��r����׮�Eqn����+pGB=��&�`{dpI ���9�$�:��C6w�`:Ro�<�n"fVd\�9�U�8\��@�W�WfC�N�:�qM�?y�������gD%��
+�`��7d:�S����,goeq��z��B�#a$`u����wc<�{R汢E�t�L!�'$w��]U�uPᔞ���p2��a��`�R�9=��W�I
�0l��1�~��y�4����t��]����ZC<������j�G���)�@���Qݩ�%� ��[�}��3�����3{2�2�J��ɍ�������
��N��H�
��\�u��MX�	b����}�qP��'��Xy���N{Sۄh�(Yr���Wr�~{TR�bRm��������9��n���9C�H����M�RR-y�Yaݻ��y�:�b�[��*��#�q��U(�h�&�␒���1�G$�$o}͏`H{�����tf����ˑ� !pq�p;u�j�T����A�cְ@�(�r:{���o/�� �±�;��?�]N*���-���ܼ�d�5B�O-�;�v+؟��j�a�0���`y<0��˰��em�
��p=N94��B)[�G3:!�8?^�W���!€r��ޯ�@�_ސW%X�,X�<u<}
W*�[�Fl�O8�^�?�g���RKb��!�`@���q�H�tg��'���v�S.
��q�����=ꪁ�0*8'��K��J�(��C�e�ʎMZ�3.]��T�/'�N)����q��@z����6[��R�n`I�T`�rx�P�w{��t�A�!d��=�>����^X��=OAϷ��WUç1��n�8�}�S�U�
i%A�8��gӚOcJw[�K#6Y�
d�9�cMQ��L�/��,��1��o��1��iQ]�T��t���fپ�I�ɜ���3�r3�u�)�Ć(����\0}����?*$�!�s��*@�d�\�q�<�k��b�b00�\�ո�-��%���H’2��x8�R���F[�����	��\�yh#0wu�@9�q�b�"%Q �A��$y�i'e?^:�I�ؕ���\a���{z�(D@�hld���נ�e�T)�2A��>�*�f�Wgy���x����0 d�ڗ�a��x��c�$�s�G��KS(P̲���R8�#���h�ў2�1ga�dd`m<q�ǽ/�#�=>��>��v�X�8l8�:zw?N���"l�Ԯ�%���m�N�mg�>�)&mϜ�<ga�;TMq$�T`gQПz|�#E�T?1��<��1�qz�.<���<����y���Y������?Oa�T��#����1��<�nF�(9`��õ'�آ��F�W�oRp�8��*��!�e���� �‚i�ev�\�[�;q����n?�q�h���@?αm��$�F���1�����V$';B�Jwԁ��c����#�F�9cՇ�^����
����<��7���$ě��d�U#��_�vZ��j�10�2�'$.?0+���|ɂ���3�����A��0���0t�8�|�ڽ
V������k��!2�+�[ �[�����Y�mn��8fr���N'�`~��,��;� ��:c�b��
�4��v����J��ކ4��(Cg,��W*2�;�:���b]��X�
`�8'��Ҧ��+���NI<�|u����U����0G��-Ⱦ~���9`
����Y�x��Ga��˼�>`�*d��^?*�ƀ�������RI��60�c����t�I+��r�W��!!���}���{��O�E،��V��g8��GN�ޯ<��r���#<`��c�0�3̥�"�
]���^��f���eYݮ�xfn>V8���v��V�bQ���=1�w���M�ǿ�X���`��9��(�NT*��#�'��zdq��Rm�m�6\��\H�U�<�t��?d����s�Քc���Է ޛ2�rA ��^�j���k!}�>U�Q޲n��W!^F{���>����8��l�18�gl�#��Dc�m�`��#~�����+��[$��~�9��6l�V+��!�N���'>�r�Ŷ[F̥��7&zeNq���{����8�'�O�S��W!Aa� ��<o��>���IX�Զ�d(G�lG#e�6�CӜ
m��YQ�#�K���z�m̥��#qeَS�����z�L򂤅q$q�9�(r�J�Y�3l
Y����1�w9?�)�+��0n=�աP� �2y�?���7sye#h퓊�V�yА[�J|�y�:���քP�FT�9z�^3��[d)�)����1��X���2pA����=�Z�69gR��F�-�?�<oa�z��F�P��*���
:�?^�*�
���꫁�c�GjY�_;�G$r�+E�ҭ+�D�	ػa�n���ֵm�7�R�e���Q��v�d�A�Z͉Y�o�E�s؞Xۓ�
�y[�@��N�d�8>٫�V1�-Q��܋�T�m"0F	؎�����@+�.QT�bCy?OҵoT����pr;���}sT�3l!�S���o_�J¢=
-(�<��x93��?!N�ٞQ����G�ی�W��#��#�����֩6yQ�T�Ѵ�m��Ԩ>����+:3fI9�wg�f=Ny�f�i~b���v���N1��|��rŒ~uDH�=8�h�\�ڲH-�ȱ�#ǚ��n$rO��d	��d0����zj�,�z
�#����C��8�$d�–�涢"Fd8-�q�rq���n�O�4�W��������i�b�$� w�*��5�q�{Ke���PF<���֜�FR���� apwa�N?)�b]�\��ʎ�L�UgbF7A�؂Q�c�'���M6����^Vo��*8�����X�;��6=�۔I����zw�����n�nen}����"b��Wqc�cۜ��(��G\�-�n��=3R�m��J�`�RĒ������lg�#�V�i�c����s��h�e_���9z�=���ެc�e�x^_� �9�}M.[��NUb���;IO/�u
�{�98��i�TD0ɐ�+�p���������-�N󎾟��J91ơ�����=�r�J��� D����8#8�6�H�.l�`�=OC�֞-�d;��Ę �Ƿ֡e�T����p7�O8+߂H�j�M>�of�S�o)����(����Z�+(�!@
����>�t�TD��.H�h%��q�`��֣q����
�p|�1_\{dCgD=	-d�ꁷ;�Q�pN��Ƭ��B�j� ʂz1�*��d�N�dg�b�%���=��׭<�J�69��9�ߟJ�e8�݀�H��P��P�sן�]
��
ʑ���{g�U��Oı3�F~pzu�q�£}�:�"Vrvl�OL��i�n�Q�g�ʡ��c��{z��b�ʳ���<��-׿���2z�U��Ll݇�d�t{U��!�ӄB�|�q�dt�BW%UkI�{\b@�|}�O_^����rh��9��Òz�<�9��\�SdNpʊ�2Y��}}s�T��mY!v*ܣ)�9�8�ʩh)ɽQN^i
���dx?J���`����fQ�p8�iBD�w'�� �����ܑS-��DM�pA��:u>��BVw��-W+.�W���?�V�ѭNड��p=:��kW�IX��ç�0B��q��"��bʒ	b�����z��j�^c8�h�����0�Ǿ�Ћ��	' d��d{�֞�w/-��r}�<c���$�Y,q�r3��Q$Ѵj)h��-V�-���$�9=�
r��ȑ��8`��s���?SM3��>��=��
�/8�q�*Ko���~x�0�؁�#���@��33%�K,�ҭ��X8U�@`�p�*�<�mɁ���<j|��{Gd��nw����NF �/ɀߞ1��WͶ� ���0=1� �#��ѱ�k���.V����K�E��nH��é�����누(��1���o�\��k����0���I�G�Ͻt6���3��#$�?�tA~*��ϸ��c��x�Q�G �?��[� �� �H�pG�O_^�j��m�%�78%z��H�tJ��sۃ�nO��bUV�-�o.u%3�������ɠ��ٺg�s�׮}�b�#(���$z��c�Bc�7pr3�v g��qH����o.C��`9�{p
U���+��7)��}��U�qo�/01���O�U�)�@�cd��b��NwZ��|>�[%�g��{zb� �*�3oUo��s��1���p3�6��Ϯ:�ۈ�[p�`���zԦ����~�C`d,cx��3���T2�d��hX�{���>|�?Je��т��	�m��ю1���V�U���·$	���V�]HM��̸
Χ7n�3�ߘ4��C�`�Ī7nV�Ɏ��,����+�Y��?Ops��$�2ʃlY�����ֳi�e4A��i#�=[d�{�1W�n|����I�A�^���UfA��U�A�m=:��=�A�4	�N\�m�#On�䑁�zSM�I&]����`YJ�7c���׊��38ܧf*�A����񪶸�Pm<�����xz����<�Q�v 1�[��?�h��] 5������>�9�>����*�I���Kq�O�A��Nz�]5Yx���-��N3�c�9O��2&�>0C�p9�h�0��YI��Tbp077�>��zH��Ƥ�d�8'�;t�kJ;f̮�n7_<GnĞ
�
!W*���=�*N6����
@I����k+���:Զ�d���pJ��:w��';��)P�e�� �08
�ll��Q���ӿ��}Dґv�Ŕy�)�d�<�>���7rL�����m��t�-dH������aDn]�||͎@����V�my�4lZ�$�+��М��zb��I<��O��~q�����`�k
^H�V��-��O��ֵ��Oe"8
U�]�n��}+��cc�G�˙d�d���ď����LH^�d
��8<�?@k���12����8�9��X�6�,X�g$py��Z��w=z�Z��p8�6�׸�riaeX�$�r>^�py����<$�A�Ns��
X��v�H�x�����ҹd����(_��F�}�F�ds�y���������T�P�_����hRW;py�{԰��NB&�/�3Vض�F#
G��oʰA��)�\cҔ��r\)�@��Q՛����y r���΁ԗޠo$�=�B��뫨乚e�>fY	6�����`�&�6�I����N�o��Go�^Kآ�FB;q�'��Ǡ�T&C=��/�%�P��t��\}jx��`z�yv��$����o�1������>1�q�����q�ߠ�M��;��G�Z�j�>M�����
]�����6R6;�=)�Y��9`�m�O���⥊<!�
3�=�<�L��;�l`��88�ۥ6�S�1�(���}O���R[F�vg��X(�@<�~�
�֫+��@���Ƕ�Nz�֢�)`vK��+�*q�����N�2���?.0�r{�/¥��kn%�za�J�����	'$����)lJ��O��T
�d�ѡ%��:�;����I�m '�	ϱ��P�
0c�A����J��f\}�~lg= 8�LRJƅ�c�\�,>W���>����[�َcS�2���S�w��" *���p�r����M��U2�(2T)ϯ�t�V9m+�I�����]���>���F`�>��p�,�$a�e�g���##������S�����Խ
Rdqn��q����`ڒ|�r��>���RB���B���9\��:%y�J�d�z�8�4�D�bQ3F��˄ے>�ZJ���g���Ģ4��NC���?>9�C�!e9����#�洋h�M݌�w�z���4 \1�^���>V~l;�О�Z���;*:��N<U߹����}u$�\�a�m�FO��Bi�!�~P8���Q�F�p�W
	��T~Ye�6HPp>_c��V3��N��gF\�� �x<�~X��vmQ�;+ڻ�3ӁU�p�2va��W�O_֪�i%�	�~\�-�Î	�R:y�y�Q�0W�ǽK��mǙ��#��TQ�,�f����FH����J�݇!��[�=?����v&���YRM���Q�����^��:ɿ��\�����*�+�=������Z2Y��A@2}2;��"�yZ��'rw/�8�j���.�<���Q�S�T�C@��O�O\�To&x�m��S���AM��77`�I�a��c�O�a�6�2�C�	#?J�$�#��,yl��_֜X,�{{��(r�LQ$)cj��;��r�w��y��6%,�3�F85x��\>F��H���z�^�!b0�}j�۬�F�L����1��P,�0e8#8lg�aV�l���$�	#�ֈ�we����YQ 
�'ۏR)�n�W��}��«
��x�#�Ƕ*��NH\�O<_QC��T�i��ܪݸ�v�N}?#�Th�h��Ŏ1���w���cW1�����q� �O�S��UKO�`���,t���Qq"W0��n>o�v3�y�{f�4K$J�gq!@���y��q�2<����2*+7���Hٸ�9ʏ��Qz��C��dff��@=y���*)#V�\9�Ќ�����Kr��wm�8>���z�ݝ��,p�
zg�JrbOM$I�"=�Ыn�	*�v�sP�<��wg�'�EY1� Br	;�J-��w�UF ����6R��풀(�>Q�F)�J�d翧_C��,a\��@˜���N��&��xw�A�V�s��8<�(J�|v�h�*�i�/<��v�=j�ݿ�p��$2eD��NÏ���FL�MkYXM%�ˮT.�H���''ӡ�zF���6�$�y��9��!F��'�]m���=X�y�S.�"�J���:�Z���'��v�����șs`a��=黣*R��K�fP���df��ɘ�)Q%��6�2@����:�BA@�=�S���<̨�*�v���A�����q�Qn���1�c <������Ѥ��<n�̧�tvv0�W��
�T�q�ހ�ps�Ө դ�.��
̓#�18<��q��$�NS�4��̿3|�䱏� (��pNJ�# �q�}U���?�^��ҷ��bF$���>y���c�2884�P<��	��zrs�A��dz�T�T�	#k:c$P��ۊ�F7�9ی{�P�YL{|���Y�:��^��y�[�m�#}��WNr���*Cc��f<���mܻ��wi�ғ��V�ȷ�ʙ^H���ۜwg����QP���c��G8�s��S�z�٥h���`��Dg9,���N���J���������=ǡ�sM@����dy�'��4�\���F���88�G\Zl�$����$��<�s������5�-�Y���%�$��x���w4�d��Y� ����`r0zc�(�ԩV�اe`c�v�,3�?�>�����J�X�F�{�e�랜���Oq�����J�3�ǩ=@=HJ��GL��F6{�\w���-�d�-�g�g�L���?���D�ҫ��u����/�Ñ�rN}ϵ:]�X����i-B���2�(]?�989ϸ$s�+2��f��4�F0_s�o=��q�t�5�CHL�b�~@I�x�OZ�uo1����l��9ǥ;2iU�ht��p�eX�7��`}�����ɬ��-��7t��'�ε�e��ؾn�i���$�}j�B�fn1�a����J�ӝ�*�p�}��JF�s���cn\���J�L�Wq�����(
�� <7nOs�(��di9��]���M�*����s�*�4EƁ�1�=����r�!��W�#O�fL�l*�3c��4��۰���Xpq��OƦH�8L��-���lu�S-��2�|��|\�w��tPݥ�����*��3Z�-�
�A��]�����d�!y�6(=�;�)�C�kN�pvN�gh�c�lQ��@x��6��?��֥
��,B"�W����lu��~f
;��>����g�#V*N���������+(b	��q䎽9��K�>ͽM9f����n2�v�H��P��,SJ�C*���t��P��;����=}��}i��0�(G�a��=8�)6M�3�"r���=�L@�������vev��<�d���E7����['���T��2�����+u$�n1����`BF0����Ү��&}1���–/���j\�4H�n����/\v�*����XxW����8�I#�B�
��3W����bq׸��ԓ"�6���-��R��pp���ք�䔹!��
�	<u=>����,:�A��O ��N��5�E�Z=̌�$�m�;z���H��́�H�&������*��*cr��Jc�9��gR����i�J\��8@'�ҡ��[�ݴ33�˃�?A��ME�vRʀ�e�z�������n�O��s�SU�<sr#����G?�)\�G�b�s#6�I'q���t��\�r��[�|�s������T�ubF�\�wzc�=�ST��+�nj��A�ҋ�$��ױ��$,��p�ry�8�ڬ�q�x��^J���=ǥb#��d��'�}+[O��t��`0ǡ�OA���8kA�bH��/�T��y�˩v1_�����Y�v�0ΥF�	\��䞵�:,��烐�����I��ҷR+Iec��A
8����3$�(8� �Gݫ�D�[��,�,�rzt��*&R�Է�c �߷�Q���RKc>Rw��/<����Y�\d�L�:�U4�A+��+aq����
	
�pJ�FA=3ڡ�m͡$���,�G�nG�V-c`�t̡@+���^��T�QYc.6���9��QIvI�mle��NH��i�r��[��w
� e���������T�}�#�18�'��n}j�*�wG�o8�v�����:m*�1��z���kOM�\3;�B���3��FO�
�������5Թf��N��s�$p���no��5�f�RY]�^��x�Ƥ��Z@c��+����$zy�h�;�ʃ�`qR�����͓����)L��!���w���	?�\%����R�8�N�h����J������\�pP�UL�3�����p:�RFr��U�;�7ŝ�O��'���zTs�xX���GS�cZ�"���ߓ�N��桕m��8����UX�2���ON����ك�Ct�<g�U�ˏ��?{��֛$*��$�0qЏ_j��5Ԥ��+�!�}�}Z�iq\�)�n�d�J�NH<w_ʛ�E�1'�@9���}3�}i�D��e|��ߏ��w�3t�>`��-�y_p)d9rw?��r���I�<HQҟ232	J�P�q���iܝ���؈����ǿ��R�1�$v����>�S��'^��ڱfC�g��R��ƹ�Jc�=�� ��E)$�JM��I�*4q��p���?�Rq��) �� �#>����QY3��͕0���zձ��r[vpzc�QE4��2��f��<��|�1b�T��]K
è=��EFe�$��1�P��ő�
�l���:��6HJ���ED=G����i �T�:Ȥ2�e8QBl���Sc7N���5`�J�fcb0Nܫs��U�Ȅ�*�ԕ?�;c߯��\�d|���nDD����'�?(�@�帲ZGp��8��+}1�߯*���B�0T��}�ފ(�Q�l���0,��<�T���073w=3����Q4Rc���Xv�𿔮��7)�
��~;UI8!՘�d�ˌ�E�ǙK#D�BąV��>��:�'{,Hl��ӵT&�g-��7�C�8���ǭ]��!ʆU��x�+S*��_7%\;v�1�֥������Z(�brM�By�ˍxl�`
�G���Y����6r���H����>�QJeQ仟0eS�X����A�q��Y"�5�#��Հ���s�E�Ҥ�q��p'w��UAl��=y���*�����t<�(���&�])��#n�l�/��'���<�Tr8}��.�7o�����gԞ�<�
	�g��^ï�j�qU���ۓEP9�nJѢ� �@(\q��u��Mڃ�ӷӷ�r&��VvY�=�(r7(���T*I$g'8�8�~�QEb�Ct[�y�^P	2U#L�=0}�P�d��ߵ���+�~~��)�$��Oy봫��p�ǿOcO��7.G�E�9%��6�
01ߞ�����3)`͑٘��c�Q-�����H�A8뎸��g��:�Y��<�9��>�}袦�C*Eq��mR�E��F8'�y9�)��9*A��x��3��EZ2��֟�ʆ,=���E1Jy�eC��08�u�򢊤H0F@Y�<m=���?ɪ�>P�r7���{�E�7#���#dP0���Ӟy���)_8�3�������5�j��G�zn<.GC��Oδ%�Hc�vN��B:��QW���Э"��>`B��O����9a �FFF!��׵Re�c�Kb,��!��x9>�\@��h˶Q�����h���������VV	��3�q���
Jf�+��1�pc��=��}ET3�;�wr�BU%���c������֭�p_8kW- �[�
$��.3���+Zl���&�߳��Q7�H8|æ}�q��$`yG|�9�8�W��=�:(�lqJL���o3p
�H���u�։"Y��Pĩ+�;(�|���QE'�*
�/n����DI?$���#8���6FW9r�:��,9�o���+�M��$�F��*Cd��9}�@Pj��H%���380���<z��P��&XH������y����zpi��UHWfWi
�����򢊴�d\����{�%Ns�U�m��6�/��QEh�:�d����OO|q�PߺR��9=�������5x��*)
G;���'�60]�������۞x�(�ot���8e���@��v$����^zs�QEY�ԚL���J��1�ۅ���ʪ�/*F�ĩٓ�t��QC�y����I�#��T��y�<h
����3���?Ҋ*MQN�q��0x������}1���D�$�h�2I���E+r���i�wrp�pA~I���g�c���<����4QV��C!O5���j2�#�l��j�s*�Z5�郸q����E5����ŗ� F�|k,g#�9����Q$����|�I��|~=����CՋ*��rAl����V#߽�3y��I<���f�)Ě�Єt�ЂXn�n���A"�*sٸ�h��{�+�G�Ȯ�9� ���jΉ�\��w�o���E���{X�������Cm c�:*��3�32��@�X�c;q�ϧ�E�A،�؆&r�>b�B���=�\��Ii8R�|î�Ҋ)!͎0�Bѱ( �W<c����})@>xPql�xtNߍU���j��	@b��Gq��׵6�X0@=�����QZ#�Z=
�De�B�B��;����n��O^:(���5ՙ�©������B��,p�u��QY�Υ���1����x�ڡ��ʦ �/�Np����wA�(�4�â�Q.$�ª)�#��j{�6�G�m
�z�l����/t[d�
� ?)e-���Ȣ(��#s��t��֊+$�ܝ�V�>Z�����\����S3�P�պ
�N:`�c�3�(�!>m��.X�"
Nr88�班6$=��g�h��"��ʀ𤏺:{J�$K�	�,?>(���Q]I�
1�ʴ�҃�}�1�4QQvZ�ȂB�%gB�۝��Xѩ$�duDİP�ws�o^y��h��^�M2F��u��s���b��Q�r�O�'�}�=����2���졀'�N?J�7;*��8�8��Vm���h\��H����Y|��#�s��M��eGp��w7]��8��4QZD�2�[0\�R��ǎq���^��Q!gb�p�s��?��QEk���ݍ\-�+��#���~5T�ʹ1|�ǯ��E����Z�6`!]���'�$*�;�4�eR%*X����g���
)M�cz{8�[c�p�8�q�ӭ��7�Z�Q�%�� `�=�H�h��:�h��]�o(	<``v�=}�lP��,v"0;ʂT����\w��q���vgG��7�}�$��N�e���X��J�2�EB����c?���Ei5�φ�N���%�th3�W$��<t'���"��<��!���NOb=8��QX�׳2��� �_!r����>��85S
.p:�`1�q����[��رJs�����*t_�*�,>��c� Z(�����\�#X���%�2��oL�ǹ���,�"����q���s�֊)�G��r��>p��I��$sҤ�3���N��~�QM!JL�h̍�WyPOj�
��x�,X�G�w��i!JNŠ���g�=���ʖ8��"0O��p9���󢊳�6nس,rɜ,��h?/F>���f�Z,�N�JP�x�~'�ʊ+T�9iɩ�@�1U���.x �ޫ�Wr�hX�2���Lg>�ާ=�T�kOY62@��mq�냀C��}�X��$d8��EK5��T�#wVs�b�������A��䃓��PQRL�U��.1��& �y����lv�+	�4�؀/�/g�'���l-�K0(èS����@=�~�QUk;lKu*�ث���g����=*��)	�l-����1�|�E[3����K�O�0��������@��\�	�$�� ���8���N�}YI7�s`q�����j%~��Y�GC��z���(���NMl\[Am�J�2��r1�zc<]�BRΣ�N�QE6b�ۻ�a�@��#hbH�s��R4�*�8#�Fx�8�t�E�lIp��w.����cީ�H�qeݿj�G�?{�Lr{Vs7�C4�XLaݶ�D;�d��<u=�v�>��Jes�@랃���QEI��['��C�бl3nۀG+���ޥP`��]�������#�=EP�WW,ó�•�G����ׁ��P%`�rĦ	���u���lq��8�х(���),x9�m���p�����*�g&�amV� /�ǘ?1�����$2C�$"?)vp�I$���'$g'�QE&(ͭ
���۴���:~��9���Xm�g ��C�T��x�Mp�eB�'���ǯ^ïc��$�s���7�0J(�F/�=����"�-�>��{�4_A(�A\�*X����
(��N�CDg���_��m�H#yy�?�VN��aKų��@}����UP77T��;��������"X�zU���C�sژ��3�n��g��U3"XJ�%�2�T��5��A��1���z(�L�E����*��J2�91��?�j�sG%��c�U�Nx�����d��k�M�"�]p��'9�	�:)b���ݴ��g���Rbû\i��*�2��@��?ʪ�C!PF}��@��(��gE);�'�l(=z��*Y��ǖO�QEr�~fF��ʡ�6�s�㡥���f�a����>lg���E
+�GcBd�h݂���A�����cw\�M�9���Ш�J�~��}
*����+�jv}�
�;��U���%[��98?��x��ɝ�ɭ�IF�Rr8@0	��+a�IAv`Ŏ3���n���ʊ+Hb�hˣM�JOUX��G��Hn��?�F��*�:n���=�y<�+TsK�N�A-��Gy�6��t��=k��a+*�:&H���ϱ�֊+\GŽZ��da�D�*��������y��K:Mu*���[x�IT���8�;�Ebֈ茝�.S���v��pH�:g���+�,������0 �z��ϽP���<,�DlN�p�ON1���h��0���Hݞ1�QEj��4���<F ��@_��=}G��g�Z#9(v�`2q���ۮ(��WZ��Т֌�0B�1=��U;��$��1�m�˒�A&�+$wӓh_6FG��t�s"���'����ќ�U��?��E�:�Mۗ�P�_�ղE��<`��U&e5�!�f��.����q���l{Б�p���YYp@�׹�8���A$���mD1|��,<��J"gf$,��7���)����]�`��'+�{�*��YW$A��E�ٓHH�d�+��@�r?滋�:b<�	��8����3�l{��}}���W"�eS8S�(=O {c<
(�C�\�=��)x�W���F9��5I�ڃh��� q��E����ʲ�U�g����ڱg�BF>f^r}?����E4Ra���ڄ�ѵ�&E��=A�]�p�b�-�_nrW�n�QD�D��n�������ߚFye�A�:��'<QEBn�op�:�NrIP�A��F�`�>��(�Q�UrU��a�I�y�^��xbR��>�n>��V�8��Й�I"���
�������;3�\�|�t�?���S4�)�#s6�%O��c��qL�R$ݑ�@t^�lQEbt&�h̍#8P���3����ju�����:2�P+���'#��{QEK6��d�GX��:ۓ�9��?��G+�}��v��1�NG'�ÿJ(�{��vq��pͲ5���c�9���6���mܥH�a�s�Ep9��:dM�W9q�1Ӟ��
d܍ϐI�'�QE)���$mŎ�w)r2q�>:�7��Ὁ�tQX���~�no�$S;����U'���?�(�u;U�v��9?�p�B;�EZ"iX�$Tq��d�����)௕�h��֊+C��;�)���q��U{�2�`�s��ߟJ(�=�
^��G�8��:/8����hI䷗��*ɴ�l.��ߟ^q�(�;�l2�Jdlpw'���~C�4�9�M���'���U#nY�B�R)e\s��jx�1e!�uS��>ޜ�EKa F<�g��_��T�R7(v��pF:w���+6T7-�;��W������JF$�u`�S�\�e�I����'�ǹ��V�́�%��3�/�V����*�	.Cr#���1���6�p�s��(����z�5�r�r����}xZi�Ȏ4)+�H�9\��'#Ҋ*�+2�PWpd����*[d�n0
�������J(�f�cr��t����]��u,y$1���iGo
��-��E
ZE o�x��}ߘ{�+jl�E&�ևj��I%8<zs�� qRa|�y����q��~TQZX┙��b@���=���3�=��X��C��N�Oq���d��*^��
�̻ۼHF��%0��2�e��_Zù�f�î_<�9 s�ߘ���w=J	%q��-��:�O�p:��5���uf�%@�R������)�II���d�KD��� ���N<v5��#�V�}GL}�ӷ4QV�$ː!ʱ'%���V��ٛ�]���`����8�Iܩy"+�ԓ�t���QV;��w~�QY��ݸNV&P�)�Q���G>�u��i�����;q���ǩE$m'TN�����ozBȸ;s.̜�y�9�9�9���`�'�]��b���GL~u^3J	r>f=���QE�	dll\?�X����y���!|�Di;���9��}:d�h���k��r$''�@$r�1���*��&���>Rw~}(��;�.����NH�^��@�p	��O'?ʊ*ٚHH��b@��� {u��R�u�.� m�0GQ�����������0�6��a��e$`�Q�c�"�d�BU9<���E,�H�Yٗ�.��$����Ջ���������q3��5bD(�NJ��c��ۨ�.!�n�ӌa��y�}(��{���ql����㝤m?\��ک�X�ʛ���9��=(����
�6$���!�`J��03��{U{��#Ir��͖�Ē?�Nߠ���EOCJn�j�1��m
&�?r0~��U�9W<�ˁ�;��
P�6;�'1x�8���$
H���b�.�_�zQEZFOc]K�+@鑓�V���?��C4 6��n<x�#�(��Gvz<~XVlG���n*�A�#�R3��E:#��yuk�A��ç���NsӀ=h��m�]��;|�6��0����=�>h���9`��#�q�Ҋ*M#�6��u���Di��>��$��h�I�O������E���v+�E����	�
=b.�3H��֊+(��H����$�P�/�s�ON;�*Y$�v�/8#Y����g�'�����N��^BCy1�Lg�8����"�V�����(���]�<�;`���ޝ�;�X��\����ʊ(e��s) .6��҉�py6��{�zQEE�q�dA����B`2T����F��a���!9}{��QE&^�&X<���X�s�;O�	��ښG��F@�z��ע��4TyJ���Abx��(��gB��PK
!<��uM��7chrome/browser/content/browser/defaultthemes/2.icon.jpg���JFIFHH��C


		
%# , #&')*)-0-(0%()(��C



(((((((((((((((((((((((((((((((((((((((((((((((((((��  "����&!Q1Aa#2q������!"1��?�U��N|��4�Wb���Z9�5��""#�mh�s�u2<�2��pfzɮ���n��&9�퓰��h�xW�B������$�cKék�o��o\}ׁ[i�����Q��n�(����z��RT� ��o�
�mI�H�6���
V���/��� ��5N�a����a���Z;�x$�B>����`gYE���^#��PK
!<X��==:chrome/browser/content/browser/defaultthemes/2.preview.jpg���JFIFHH��C


		
%# , #&')*)-0-(0%()(��C



(((((((((((((((((((((((((((((((((((((((((((((((((((��C�"����3!1AQa"q�2���#���B$3Rr����$!12AQ"Ba��?�i@�{�@0y"�x����L���Ln ~��J��f&v����T|�?`w���̮�W
�&W����	PI=WX�`@293#���YJo��6Ӈ�(�}g�IU����I���d�g�^��wɓ�R���FW�#&�ů"m����|R��뛶�uh�ʲ����:�l���J�bk��X�ۀ�H �"��t$t�|V����TQ�f�֋d�3B����̫�k�v��
l�����ٛ�Хy!Df�{D� :E%�Q�g���x4];n$��]��s�4M*�m(�v�K^Ü�R�mK�@!|qW�ڑ,OsL?�‚ġ��5[�6��c�&�օso`o\d�bc����U�.skjD4��2y���I'l����J�и���mˈ��fª���3�Q��2��h��[�OH��_P��4�Y���}E�[(�+���.���<�t��IQ�-�bLY-��&����#5���UDWa�r��Δ60SbV�i|Ԫ]'�6��o*��?Q�-*1KsK_��P�;���[[��"O���ckC3W�[����Ͷ�J�44�Je�I��0�Z?Cڃ�,0�p1ǚK��%�Ƭ,������oSm��U.�G*�~�;v�ؖ~☥�"[�4�Z���c��a�R:��W!.$�PL�
�ۡ��r�Df��c����˓��x�"�vHa���l�[�
��h:��1�&8�ޑ���Ў�a�V��-
�I�4��v"<P�:��Koc�*Fa��`p3�af䀢9���m1E��"g�U/�6�cB�3�W��^���at�B1�((��"M:���"s�ơ��$@�ǚ�݅�)P�B���\�h=g�]��V��-��w�"�h>\��v���zd-�.�O��?��i싺��R@$	�+Ph��C��d�A��$Nw�G�Ȣgݲ׭���=z�ע !I��kv�b�zRY��T�Y����+�f	9K�3/(�p���h+m�y��wj��	��j*
�j����D�Μe�k��@� Ԣ�H�i�C%���{?�ZݪwI��"i;֞�kl�I�փ�CU����
J�Y.žV8����eI!9,@B���@ّ����U���H8�ش����{*MU��L�b�;w=��:��b	��6\h��$Mh�� �NL��3CS��bŲ3*�Lv�HY�\k/�02p""�y������+rװs�OJc��D��4�E��V\���4���d�$�~�=䉅�=DQ!K��h�/�2*�[�S}�`�OS�GmV����{��0:��OٺSHC��֦�����EԲJ���)����)�u�R�v�ҔV
�`���)oeX�$.�Á��VSԶ}Iޘ_�"YX�H��ҨOc��y)�Bwʥ�Sl��q@�}Doj�i�E�̀�)}6��^���6�?j6�BI+���[Wr�ʌ�w����{ ���L��bc=f�MoCd�C]c�Hv���I��z6�J�G*rm	��C2� ��mI��!y���in��v��e���� ��+.��H^p'&'��Q_~�23W#� I�������,Tp\qt6�>*WZ�V���jP�>i�Se��RK��݃� �	�^��W@e`�@������$���|�Q��qy����r�8�Ϛ�w�^����?Ih$[  �1\_���Y=ď�Ҹȯ����-�*[&r#���Yf(X�^d��f��ͮ@����I���˹ڌ
��i��vO�Ƚ#6��rI�������8$V��9:�'�b��[.��䓚*��8-�HHm�e�@��s�-�
S'w�衐�U�5�5{��:�����Lp*jJ�q�Zy�'�'��������ɞ��@FV�tZ[�ku�{$�3�!i���#q�H�L=��u2s�Lld���i}dې�H�G]3�+UFH'��A!���?�F&�j���d�k9 �6�m6�5�#Qu-Z-�LE�����1�����];-�@���+�i=U˷w`,4LS�҅|�+���%��u�5ō˂J��o2{����G�k��T�v�-�M'n΄$����+T�n�����.)"�6���zS�)D�h�k
��������5�T�{[z��L
\��s��|`V��ߪn	��H�Q�	���tl2�����M��?j���v�7�
�*,9e_G��� ��#qn��#���*��\'��pt;�
��٦B+ob������R��G;'��u���K�L���
*����ԩH�gI��B�	�vQMerm�Y��)�'��:���	��2�q9+Ϡ��d�fRd�SQ�.G2��R�������\PL*3�kWI�!#�pjT��a��V�X��O~(I�J�����4t�m�ۏh�≫P����Jb�^�0'k�Wt����ad}�T�.�WE����P`��X��g�*T��'u�(�"Nj�e�NT��R�]������$ԩR��ʱ���PK
!<9�6�(Z(Z9chrome/browser/content/browser/defaultthemes/3.header.png�PNG


IHDR���R;Y�IDATx업�0���cf:��vt�NI�r#Y��8�f�y5<�=�fg�������C���b��+2k��$1�X%]�|s�t}H�j�l�[LHv���ֻ��@R�Bwi��`3�)����c���Ym=�����Y҂V����������~�|��'���KB^�-��A&��18�:c`D;{`;P]ng�.��]��SF
&�R�z~GA2�l(!�KtS{ZIY;#h�ǫ��B��S3��Y�2QPm��>������������9���2��]�b�{F�h2/	]�8����pW�D�'ܣq�R�?��{���Z�M`��nۘ'
jdv�����w�V����:�
 �X�]V�#c�����ǡ�6ƚ�=/ޡ�ڧ����Q�^�}ޣ:$'<$U2S.�
�B��x}��I��쓨�h�R�o�}�
��������T���%3�8e�0=��V�f�j"���y��S��2x�Dx��;��f�����$�@F��R��ݗ�(
g�,�ħ
~�:�rC�O^LL�Ԓ��I����!ޏ���Çͳ��V�|�'�vۚijo�C�a��惎�T�_?����ry}���f��d�.p-G� �3�L�3�f��t=��P*Y���xC0�?�D>��Ǐ/yD~@Rp�)��߾����������B
2�}3�{����k�Ф�Z)��J�Б�;$�R��8O��x�Ha��!���fzGAV�d=�E�lg���O�|�~�H�R���.G���[ ���ϭ��C��c�k�~1ZO�W�;��D��K�Lot��( �����!�J��P�(�O�L|ޅ�	R�����o�����y3��̲N뱪�l��A���rX|=.5������v�\
�?��xш�+,f�64�[{Ѹz��WaH�ElR��#���
x��8��,�y3����V�踚������=����#(�2��,�K�� Ň��6]?:�u� :����D��Ơ��n����z�*Y�5��N��_~=oO����f���;��ue,�?.��w2��f���6���V=�"���Mf�<-�q�y[���N����~�R��+ن��m�vs�ָ�*�wܡr�qt�Ӫ�2��I��կ���\���u���|�>���ٱ٩��m.�����[�������e���
��G:���޽��&K�H�[[ߍ�gM��`���c�c�ܬC[�|�j�}T���^1ob\
Z{5��ϓs8�������D��q!���V0b��H'��(m� `5��戇^�fҕ��jX|� i�DJ2|'{�۱��Ho}�����t�(z�%AZp�n���9��~m~;�����}'u��l}�9�*�~Y8O���~
ݡ�"�&'���Б��q˂Tg�3P�a���_Bo���k�gp�]BĦ��;�����b^<�Z�������ؒ������<R�]�~<B�u��.<�@���.`t�}L+^'���)EȦ�����P�`8\
��F7/-����O����r�H)l��Z����5GWS���O�v���\�K�0C�@�/ر�{���'=�jU��z�����`�o����3ck1Ҭi������"�p6�_��'�2L���e�.b�=b���)�8�"D����y~��p��J����jD��=_c���� ��M��$=��0�A��R8'���zYLYs(�7A@�(�ꏂ��,���<[AȀ��2I�'n;�cxd�	
��p�����I�X]��m�q�[�}�#��ч�U#�I��W�/¯��ш����=�Pu#�(��=۱���F���QB-��-#Mљ�j��\z�K�;�ќ�fK�H�:
�-�83�DŽ�.���7S�c񓼱���:???�������6(ap�%m���I����o�7��#�:,�HϠyk�8S%��s����d;G���t�;i.ު��$+��G9[����O���$�VX�UF��̙-�Q��t)�PV1�.L�Xh��x�G���2C�3E��t)�7,��)��!�ꈎca3WPC������?�������o������26^��B�^��3�7�+���+�3ę�s՟�L�E
����ϥH����l+�7P�"2i��@�R���y���t�|�ȯ��M�+�a $!]�Se�S�&�y}�ѤY�+�N�u�8.�w�4��x��Q[��[��%3Ϫ��}H�D�1-��þ'���Mx���d	Z�1nv���#ѱ�gמ,ź9-O����w�ɠ?>�H�VB�ԇ�ǒ�U�F������z�13Ot��'�_w� Aհ�_�6���p���itT���և�a}�g���Ӝ#U@#��Uհk?��u]dT�B��D*Z�XTF2I�Pbj�f�kK��C�t;OQ&�(�f|R��(��IK )����[�#;��Z��*ͳO���J�=�r��������h	XD
�����r�2�)K�!Y��>l_��r����؅$m�-*��Z�nw�Ǎ���H�����`���vƖ�R�_��M:j��ѭ�{B6Ҡb�?}c�ך�n�>@�h����H����������v#�����-�NgN	�?&�~��Ì��A�!!��C��r�'�^�������v��Mc|����?9���ܻEO����Qj:s�z��@kG�=W���Q4�sc�]�M���\h_�dɥ\_r�����%BZ|�#	�k�R�h�b��	Խ�9�ш��`eu��
��iW�lǟ,���Ƽ�A���h�7{��lɍs�Z��r�μ�C�F��ܠju|-h�1�a*"'�p3�L\@Ҁ �H_�O����
�|�
i�c��aH���*�j�Ez񅿫㐟���C�W�ط����#RDG�ѻ��ԭ�A�Lv�֓���p���հs��9u���>�b�����i���.�����UI[/�|.�cTa$3 �,��6E�S������?��_�F�&�!��gf��z���ف]�b{%ޭ#��f���J�P	؊�ȳ�f$?(V-�P�Ɣ��]¦0�U�	E�ysf�$�5-%s��p5��Fw��P�-X�*m�f�ݡ.T�KW�b�4D�?������������O� ����wm����X�lG�T��H�q�YP4t����Jmtɧ"�
0��(a�j�"Ϛ�M��ai-��H�&�pyj3�@^��"I�kj&+��9wz?Q��v��5`�ԕ������b+�*$*���|�z7A�D�$ffA^�[�6��,ư���4��K�	E����g�O�Z�aH����،�`)�U�T0��p��:����9���������ַ���^��p��a�u�O�gs�NN��?K�*���M6s�ߔ+���O_�
 E�弛F2�T��m(w>$2�	���,A7�&k��}r�n#��x�%&����J�c;°	�K/�Չa�A6<Brȳr���`&_�p5Vr��a`�>�HYP��u���¿�hw��-�G�4+�P���Jj0��.��?
�+J�T��L�\���g�4��E�>i�ݠz����W�'�l�0sh['	�Q�f ���B���y�����S�f�����0�K���P!B�s��&M�$m34j/ܘ��3����*w�������!�]1R	8���������� γ�b���� �z�vp�C�Z���\F���?�P���C���o�]ʪ��o���M�O�q���NmJvGN���Y�@ }i���~gx�ϵ�iD�X;�w�D*���F����ɪF��m[��]֩��:ij��@tx�Oi<K�$^�v�j�%������`��_\c���
�͡��5�b��
[��6W�*�$�k�$\X�sQn�G�o@���fq8��-|.V��+��w����/V~��XTs�M='�a#�[vO��'��N�4��姈����?���"����$9 ;DM��H&Y��:�)k�5�	"��"L�B��He{{�bX�vQ(�[�;^�-;{��&���^z7g� Q
����<Ü
��%�.T��I�P ᘟ(1���l]�
�T��������?���/�|i��H`c��n�S;M�(1����E���gs&s�Y}�@*�)Dr��0��:%u�`؅ϡ=n"�Ȏj�LJ��<�E�I��e�u�$�ݻ�S� W�gc,�K���b1��7�0G�QT2�@�m����e�mH��	
��~�4�;�1��h;0��>������s
<ÇI?��l�ofFև�sM��~O�S�y��~�b���9V�}���ŷ�lu����3��NZ����;���e<�b�3���o��k��	H$q�Z����7V��1�+��b~JdH�1�e��E)3�^�ϗFs�M���<�4�„��'J&(Bu�_c�ȱ�U���s��Vt����&�����Pm���(��&|N�(φ��m*΢2
z�6Mɣ�s
���d�?Wɒ��	�����کn�n��!%�r.U"��3A�2�3���ō�������U�9:+�[��y�(���r�����
�G>���!��NG���4w�6&���C�x�?��r��#�e˟�+�K_:m�Ք�Д}��������4X����[�m����g����?Ѡ��I�ֿ��������v�d�����ַ�����T��r������_�jϰ����D9X����(��S�P$�[�v������K��
�+�^���j{����y|���
U�~\������V�jFb6�e%�n�M��[D-����m}Y.�.+!�]Ϧ���VFN���2�ԇ�u���YI|��+/�"����.>0�g	�ΟLl���p�\�����{Z�����v\@�nl��^�v#o5Ɛ���&|@~n�]�b�2�
xB*�Y�-�K
]�I�ԓ�?E�?���������n�Kf�+{P��
W^�jC�@��_,콦�Z��~�4��>�a����j���y3S��->����	�I�j�p/x�1|��1_q��*�K��̔x���2�-��ݽqp+����y
��\�����?��������-�K���ѷ�oU�
S9�f�������	�W��
30̻����;F��6�6(��$0V��{�U�A��2�fU@� �a������qE��_
�z05zf��mD�IzI�0Hg���@^�q���,��ᒒ���
�QA�Iy����T�	E��i)Z��WV���``4@˲�r& |��K��-�	�=��e�5gu3	&qlw�A� $u�H�=��I�aNnA�ϖ��jG�j�#�����~*�'v�Sو����
����$�_�(/�Z��||s7�`WT�ikJVRb����+��V��8R����P9(��psTV"'[�f0t�=h���' �+���@@��E�����[�3��.�M�^}�s(��P����␦p���_xk�R�qIB�2�R��yW-(���Z�+�-�@�N��]o�Wg�u�!0K�sM8�~��[���}D��j$A�R�-w���Q�:B	l�o�u���D������vMiޘZp"����K��E��� i� �_FW���O�_��]�/%�Gj��O�A��K�H�r������]2R=�����;$:_��U�'�v�G���h���r����¢�Nm��_<٘�ߞ�%�R����o�G��`�s�>Q$�i�ZRMO����b�p�R�(�����R�ʏ�9́=�IP�^�Bo,�ݳ���"�Ʊ���Dְ7$����R��w[jY4�'�*�BLY�B�ݯ��s
�v<��`T���KA��+,Q�<!��c�:�@�g�As�G��eD낸vJ�&���qS�p�� 5��ܽw���_<�(˲ ��X���S������?��/�I�:�(���j��2��*[H��2��B�����]p�'VT�*V����R���vho|���e���)vɶ��u]�n1�@ѳ�KE �����2�
��h�\�|��RJ�����le����?��������?�#m-�̽t�嶚�6�p�TK}xR�_�N�L>��눷���nM���8���.��f�ޞ��6bW���W�0g�[֭��CH�6<�*��X#�RP���`�K�2�o��:"�M����ʯ,�NF�sY��Z��:��Ӗ��
u4I�<)�&��{=���+��Ls���F;%��u�����c�>��NY���7A)�T|���@`��7�q�Rz�^��gU6��,`𴝍4��[g��IY�Ԛ��x�	�����6�|��tPY�@h� �a/\�Y��6M`ȻB$��xZ�d�J�a�Y�
?�4�'�HR�
ǼmXñ������Njn��� ���d8,��LMr�Pj���P؈회H:�m��ͭ5K�l
���@Ky��
���E���mJ�m����Y�4"�0�^m�)TS�J����%���,�Ƹ$����
�'F��\�J�y�uמiK]����l��=���J܅׍��g��a�a��(�{��������װ����
9:
��e����$��?��c�2^S���o�	1��]��۷��W����O�����͵\����O�Q�C�()/���F�7���L]��ݡ ����q�k"C�>���WAݱ����:�]�ر��������~��22�+�~E�*%��7ܓ�*[��J^0����{v1��v���X�#H.�K�/ts֢WG��mQ\O�%�KVTC&.4(�-�]���r�oU�ޑ>k��7l$o���TZ�ȋ�<�!w�!�u�W�Ø���*��6'�âB����.秈����?���_���xT�`V/�.�T"��lR���~�]&�#�ʃ�?vi��6U���'Urչ�S�d��O�.~*ҋaI��nIqH	��jw��-z�|������lM�iX}��+��R5r~��������<��~�[��P嬲�@��1y�2)dc/-4�zW�cE	#��Uǂ\�*�ߪ}e�'R5K7�)!%,y�3�2�+ث��ƻ�[�:âDI9kE������Ew�}7��u(����_���V�3l��ҽҠR��j��j��}�\;۟���ʊq���o�/�!E�a�q��}0�t�G��s�?��\��"��'h����O6��ө�/�?���X9��kh���S��12��,��%+�)p¡�J�=�ӧs�*#���������C�P��,�f['�q'R��q�^(e��%�p��J��4%�?w��a//A*K.�'���4F}#F���J���@���ſ���M��j5�z�=�^L�|?����?0Sq*�EQYM�j�R���s[��ϼ�m�3�*���^��R�8	�厂�o%0\�l�8o��_��Tg�}��'CL��D��U�7�|�O�]7�e����;�����@�J��ق��ޥW'�(X}2Bupd��f��0��*�R�V�G�����b�g�['ϖ�ث�w2���d~]ʡU�a�\7�&�g)�������"��8լ~Qu����w�O�LW��[��I�l�PNj|T��g�Ƨd�#~m��eb�S��2��U���m.s���]	�0莓O�לĿ�E���_婍5w,D�3J}��ybCɡ��Ĕݙe��[)�W�P������\�r�Z��3a$�f�*s�ȋj��S�V�R=�Z{�̕m#�z!�"��?�������Q"�{��j>#U?˒L�0����0����Vh�ѥ��A��o܏o���U��~�r�m�J�[�Rw-����� ��9�K~[�>)WI���vH�o��-J��K��ޚ�!�[J[^�F2)�?���������jf��L	��uf0t�y�
ڷF^��5�~S~�]���w7��>��벼<5t�����h�Ce��pF'�;��,$�]�;`����j�
�R;�p�k���e�]�
6j��z�W�4�{�0�o
�$޽+�G�bI�L�����v��0�[�R��12�X���ӄ9-�{�&��Q�vQTz[��S�.�t~�j(��aej�����U}�C`����W�l���b��+����*J�<�IT�?�Q�:g�4w��WG�������2>�1������L��x����(LI_���%�6���?�!�*���>�*a��Q�+it{��\�1�`���l(�>9�����B�|!�'���M�����_��[�3�R������	�)�[�+O��>P��xR�P$���s},
�jT�$�T�p��J5��<^���Wg��)w4E�cQ7D�'r[S�3��j����k��N���W)��i�W��������+����y+�2$}
N��Pf���p��O�\$�\<�ES�\i�i�:�^]C�:�v�6r@�����F�Z^�ԥ��))_��X��Zg)��GR+ 򬻕����Z�B�+9�^�X�W�pMD��#�ߍ�
�վ��2�l]�=�ev�k���~�~c���2�&�����kHFQ򄙻���ܕ8}�����1�Ukf����Ad�H{����A��4ˋFk�<H
��	�r�̯�?(T�U)m�('��3{L�0�#�{LH{��Hb�1e�熗i���5�����yYO�O�*5���"�FJ�݋�/Jx��Nz�IN�>���ؒ�O���?����P����O�@03ה-���V������98����x��<7D?��]׈��ISMax��j�Ӽ�ya�%�R)^\n�n'�{��M�`��͎�mS������6ӸN���t�5�A��H�Qڴ�)��O�ci����2��Il5r���d
0X��j�r�{T���}x���Z�n[�߃����?���#񟘁k40�Z���]e����bP9
AYh\��%]b��\�?��,,��.�5��J3m��`��<R��z2��F�������xK�޶kX:u��$�	�#�@Z��������[�;��g��S�����m�S̨��b��ʌt0D'��sh��rM���d6�O�,�:1���m�
n_��72B�ƀ	���2
��	�l��q2�?���ilo���$�O�'n��7�����y���a�,�L`*�t�I����O�,�Q�s+�����_�(3!0OO;N
r7�4���O���'�I��ن*L����R��y;u�צ���>�9�O+L Hs�Űe���ӳ'Y�E��_|1P�×_N�8�s7ׯ��b��Xh�u��^s��_�lϿL2X�V�92erN���s��0vr�*/#`��o���|���ǭ�S��Ê����T�m��w3U�x3Sڹ5�$qAK����|F"2C�>��;�*S+Ӭ���c�1w�~�u`��\�ۆ/r�ٯ���c�����\���l}�ޓ��U�4[!ގӆ	�돶���@��րdX2�n7-Y������4Y�s9�E��u ���j�E�4���s�TT�de/�`u^:�|��W�#�l������Ɓ�d�����r~N8?'�\�f�@��ұ�dDl�5�`�����Қ��O�\F��4V��]��糵����ڏB�.���̓>ؾt�-��h'_�a�h�Q�ч����`?� �G:��n�I�ޞ�'~d�ן~��ٹ~��/����?�����K8��������ϣ�&~�'������|OI�?â>䧄���<4F�L�&r@2��d5̙f
�����@�Rm�;}��|2Z	�K�!�~�i�[�t�+'
���J���&z{��\�
��룩�����u����#f�u�'~�d<b�7��hb�F7�NN�"�S��o�
j�GQ�zܺ�l���_�ѫ�iy`Z��}��.ք��\���&7�$��m�,��/����G)��A��3�=��4l'�p���Y2��<1�	\����|K@�k�Џ��i�Z~��a�g����}��i�ߗ:{�&Ɔ��I<O	�Y�%`ɠv��ss��,��N�9�	L��s�,G�s�뵹��3�!�g��_��}N�����朿	����,���NjX��Vb
�7���Of8��������U-�U�隑�xFQ쿱�ֻ��1�)wHYs��U|�O�l�,1��:��{�PV��^�xJ�Eҏ*u=�@�un��U�J�lq�>�W��/DT��'�QX&��B���L#�����J\eK��^��<��sb>�tLo�����������m�H ��ؔ���O���<>�
��ۚ�Fw��7܆ol���ߛ<�Y�yޒ��o���T��ùM�*�h��7vPq�����vI���6l��*X�ݷ2�ֽ��Bu�8�\�R/�W.��?��������8���+H(��XW�"d��{��8���N�W��*���u~g"Pu���(��8�[FX	@mk�PAH���$��2K��b�f2T�2�q�N�J�֓nɽƊ�J[�W�>k���?�.���N�̌f�͒[f�*���.�=z�%��

���eǝ@��j�z7:��/1;e	o��A^�!�ܠq�î�������f{K��?=��~s�V�T<5�5)*�탊f�o� Ċt�0Y�U�w[3cߦ��3�ꛘ��P���և@2K���4����C�ϭڝ�g�M��p�r����G����~U*µ="$s��{����:�\�󶰌<ַ[�H�b�Ga�E���o6��5Y�xɒ�2F0�v�4.� SD 1���"_���������*�G����d�ϱ4��=��U�T�j{�/x�L�?U��]�rb/n�k)�/�'��muA%�)���D�-�˯?m]�vt2�1�O�ډGJ!O�M����aJ�ǜR;�y-qgM}���h����}w�ՙM��Gn?�z�(�.�u"F:�\"DR��VGC�¿�3(FX�y���6Z�|��
��d��]ZvH����a,k��\�FyS��l4��qJ[�oѿ��?�m0zu@Ֆ#
�0h���@�q��"�JP߀q*�o
&�D�0���
�!�F��w=��IX�v�z�ev�u6�:{��S(��z��
{�n!�O�+?C���N�R�w��%�5���>�w�>��^՟���U���]
l݃�a��=,9h��F�<����\y��L5a�,��H�2߽%���oB>��X�ݯK�嘨�у�Ɛ��62L��N��j�� �<�[��6���?������O�J���"��<�q���uC(��eW6<������p�p<w���b�w;��k�옻~
�GL�@�g婉l����Hv�=ű7�~�9o���&k��u���P�U�ֱb҂��E!�(��8PK��'��p������?�����lԪ��T��]��
C֥�)��
�E�ƞ�7nx��RJM����Yk����]@�	_@��5�]�랝t�`)iB��>*���;5I���%��K�7�i;���!PxY�R�b������O��e�u���PU6��d����[.��dD�R�\��C���s^be�ܘ�$R�IV��Y�!_ۙV���2�W��t���	����J��F�pǧN<�Sd!�2���|
FYQ����!_����ײ�J��M��x'��')},���&��m
c�!`&&I?�o!�g�O@�9��8kQt��Sv����H��>R�kp���)�W�qg�v�����q�˛Bh�?.����ܢ-���]�}��4]\U��H��0T��alډ<��L�vbJ7-m���
�V�����3ä�<[�Oi]�Ϡ�\�.�2��o����l�Pml��.��ia;�_| F�
����J�O���0Vbc.�x%��m��|8�����3��P�'�@�G������%�|8�R�Q�#?���\'���9��;?
�۩��%��(8����x��G�Ȕ��6>�&f_���?KYW3Á�+H�٦��u��|ʰf��}n��P�Y��3�:C���[��������]�nW*T:��ې�pr��F"uo^'&aF	��2�É��2�dܶk[]�r�	
 �e�L�/�o�+F=��|�v�$�Ń����}~�#US�)��`�����R�H��!��j���&�bn��m�-f�Jۮ�pY��{�,��\Cu���x������I�"�#4$R>�uiW�ф�O`O�4�z���w�v���퐩�:>\�j7�J�	�՞9xSY�IQ���"�N�I������?���7(_`(�s�W!o�C���+��cT����U���<Xˁ�I�aR������bX��eH����vْU�I�g�Ĝo��1%�c�;���
�6�.B�8��U�~���Rm�۵�EE����4vk����?�������砲3�Q�l��]�����,��͚5
V��St��i\������5��V[���r��7�\�����v�՚�-��ŀ���JP���@
�ee�j���^iJ�.,�t�.�BGo(��&~�j$�����毢�$x����]uM9��rn��Y	N|8y�J�1s�S!�"��`�,^Z�c�L ŀ�Z#�	PYH��%@�A`*X���:P��4�GRԯO������V�91�1l�j�qqϗ(��^�L���p뽁z�(�h�@Xr8�i�'��Á�ֲ3�P���`�ي�MC�*���'�
����"���!�Rh�6����C�|e�R�RJ����A$D����ݠD���&T_�Q�����<-M`���2n&��?on'R[�&9� �D`kl��.��>�Ʀ�k'�e��G��<>������� ?4i
�D������!��p�8�Usl��o��I���W_}��sD#�y�֖���P>%)a�ΐT��z�p�Oxn�?�W��_�"C^R�'���:�1`�Q{E���R�mg�,窙f�҅�j������3|��<)��I��V�G�o���U��"�>�o��O�Ι���=aҽ��a�n0�PZ}�\_p� ��@=�}ы�m�T�K@��$�2k;T�(��࿛B`v_�I:�[�@�C�t�!*�W�$����z��O}@	�D�EV{�X\��X���$��o�
K�k0}w~�@�q�I���)q-�V���Y��a�!��~�)@\c��<\'N�o��;����B�W+�%	� !{l�|�6հ���jaU b�<�?������O�ik9[x{��g����L\�����YS{w�<�غu��"1�xEĊK�,��
�d%k�R�W�L����Ht�v�a��Z#�.�v]Ch7�@-�ܟ��#+�`�l�)��r�d�V�R�R5���������?����V�0;}!�O)oze�=/\C���sAS�=��Ԭ0?�����@%��n�}��o@^=S��I@�����y��
·��k5�/ O+(���5��4�j�V{��"s��k�Tm��
�}b�8����Pa��ks	ԋ��r�T���#���'~� �P�/�R��ƞډn1l�
c-)L��qȶ�~�Y���{ xt��"��K��n��{n�&��2�XH��t�Ǭ��+O\b��^0�DZ��O���x���[��@Y$�CY�F�6dCa�摸�ً�J�����*�S~R���� ����<�C����s^��$OrD^�_)M|m�s-�6�k��iTM�m���=�.�K���{A��n/���~ם6˃ص��'"�<eY4`[�x������Pf�w���Y�v�T�̛o�?�%F0Z05�ة��HިTJ��T��}��"\�~�ðW��W��-W�K��<��R-X�q��ȃ�W�k��vu�=D@J�Qu�J�m�0C�����y�ٍ�J��r}[�����]�CxRC|�]!����Tӂ�%��l4
u)|����O��ʣL"w����@X�ĸ�����H��bX�����XF���_�C���$�N��C	W��w0���=�o������=���.��1W	B_~��z�庙�PT��A�IVVû��io�-���4T���kl��;.nA�����P�enU��]�_C/e�NZ���� u�{}�/��b�8-+%"��Uaz-���7�޼�����?���SĿ)ɮ�I�\��F(BTt��z��.J���-h�p*0D%~B5�R�T\��,���PM�&�-�c#"�~J�ػ�w�Ր�OG���@� �|�
y��;ir��S��뮳����IR�����E<�HVKM�+}Z=�������������a����?T�+���-e��ª��8��'5[�̡*?<h�@�{2�1�\o�w~��q�1=�	��иc���Ԃ���=�0�(��Gr�����7L�������	��to
uה#Ւ�<��+�&���d'k��gN�q�F8����N�����
�Z	N���m�.����0.Ć���A�tݎj���w�}7��9۠��l����X���8&�<3*�֢cc�����B�������
�	�]^���Pâ�ا~�M6�s1N�O8�Q�W)��̟<���{�s�L�M�:�	�����F$�����4~�7?sM<�|��7�p5�~��`e��c���{�N~ ��]�9nb��Qu.�
H3�b��'f�O8ԩn�V��Z�K�@�H���?
����1��c`P��J����4~J^��*-Wvb>(�����ws���_��!A�LK�����4A�?ͱmk����D�+N�s�[����.~֭�|���\�����D�2ȟȹ���?#����(e�薋����l��H�d�!c���('��w���9i�`&.G�M���)��u\l�;�c1��=��vX��ϼ}�)�pzG�_�囹�h��\q߮N�_N�n�'��dSU����v�#���P�W>�`_]#�J����!)�'`�=�?�`ǭ�w�'�ar|�9z�^��q~ְ����R*~.|j��^�ڧ�q��򈹳"G��b�oU��P�Ӱ7�R�;��I�j�>����zu�aW���x��S�P/�yde[/�E�[����Ʌ�>�"m���e*�'���[N��̻�<�ӿa�/0��CK����Ue(����nȳ%������[�W脞�1�H��L��# |�Y���U3��5O�k�Q>zZ�FϢ/��R"*������:f��w!9���N$|-�ӐIe��?������O�x�4ګJ����0C�yЦ1&P8��4Y0@��T�^�F��`�ٽR�+!97�.�|�מ������-�b�݆�I�j
C���ˠ�]��ܴ*�x|3�����R��|���^C: $�QC[I�ۭ{�@��\7�k�����?�����k�737<[PW����]�#;t�Zw+v5���ww:���&f�)�R�o�q=�a%t��Fp����A�+G�؞���=���`k��{$k�	(���(;+���4�	�j;-⥡ғ���bK�O'�eM}�9b��o��/�؎$A
`
�ޔ:��|j��-z\'7	v�qJK
0��+�7��:s�F��=�<: �c,r���!V֭1G���s���0PM�2�b�������������O9_c�Yۛ�1/���I3K�����6e�ㄽ|o��P%�o������D��K]K���4�
�&�l�Yn�4_��#�B��\�嘻9q��q�>@Ǻu�?e���i����Fl)8�L���lFz����4E�Νَ|�����D�n��r��ǜFm�f��d���(�V����ɩ:��a[�|��T�r�
�yQj��曛R�#�J��Q!S�9�i��`��'���,<̹]�앏�?�i	6pr�P��PM��%�c������cM2?�^��	��D5]cb�7��Bb{�C5�@ܛG�5�(���`zS�=����A��/8a,i�^2�������Cʶ��t�+��Ոy��*U��i�4��4�uQ&_K���:���������yX�����𐞁�Ԣ�?>R!�8j��I���$p�?1��8�4V*�w�����ۺ]uIV���/ڜ)A��k��9��wL}5B`��i��O��P����]�z�{H����ʁ����!��'{�!�0�x�!���TG�w�����o7��D�-[�	Ll�z��)�c����xe9���W��+�5#����ŕ���/�S�mtL�2��aE5$�%'$�<�V����W����w�~��@Iv9���q�\Ea�O����)ئ�C�Y!}�M�f��!�/%���
I�k�^ª�x{�9�?������O��0��#�G����g;-�]���>�6f�N���S������nA-������y]^����6l���\h�L���\Z��K�-�s-�JEܭ&�cx�.�k����B�&0c~9�-��"RޗJ�����?������?�՚5�}�Ա����?Ύ�_~��ǟ�i��{Y����j�����M�r��b�nMt+e�k���S���ڙ�i�]�Z	��O������DwnVn�ʼ�!��l�ܤ�v����/_�%�u���x5��g�m�T����	$M0?�l��k�'��'�y���c�y��{ ôī���	xo-���	dq�@��E�-��dOU��<e���P�Y
R��!C#��b��wJ��8�~�9����͍���a~�DE
CS@�OBB�������߽���X��+���ж����������'��+�B���]��9L���kLB�ڑ�U��	3��#f�B��ב��Ja��̊� ���������bj�!0�Rn8yz�][4�ma)�O���9
�h�d�g����H[3�8��-p��ZP4(ۧ�1�O���*ii-�Ǖf>�W뼧za
�� ?4S"�|����
y.�5V�o����hdSX����==>]ǔ,�s.�'��#`�wi��sX��#~�y��9�z�\G2RU��f�H��l��H8L���H��Ε݅I��>�Y�%8}�!Q7K�i���l[zc�v 4�x�m<�������w�������˥�ԅ������5�JWY�0��ͣ���a¡��2
?nihqTYuV���ڐ"w��s{���i�>{���,+���8
/�\G������,��Ϛ$@s���R?:��TE�ln��3h����e+����q�k�
�h��9�?z�	�o��Ԉz����=	L���H&eڕ���>���˰�/r����D��L�&��/Gn�?"?M�g�<��
�xF��2���@E���H�.Z�BS,5y�ކomEぎ2mșYg7+��p��;b�%�$��[]����5!�^�9��XkJ�|4���9q��Z��/!w˷�z�,:�4�ҹ�_�O��'+��e�g}S�L
�m�O�mH�8O!��ܣ5J�@������0�]9Z�1xG�P�P���=�]y������/�����#�yֺ�f���R�Q���2N�
��=��%�jX��)P$Hm3���0m7�Om���an�a�-�0��z׶�`9�(J�o��Ii`4Q�p��m�/�kJHL�_��/����Rkɰ�K"i�j>�Q�!0�Ҙ����Z�ѭ6�;�y�)PBIީSZN�c����e��JB����A%��0�}���_�����_����ǿ'>�6RU]X6�N��I��)׳�Ϲ�<2����t��M�1g��fw�t�Ϛdu�v���qe�%�����i2�R~��4W�:Cd�H��XxdF»�]���9A� ��uL.�B�	���ޟ�3N7�(֋�P�3#�����݈q���/��ɑu~�¹A�P���;��f����׊�V���+����Ø�i�����J���l+��ˍ^��M��(SV��J��J����9J�P�`�"�4M.ڐ�3�;��ya:�w��I�O�{�ⳓ+c��I�#;)^_9�m  ��^c��{��8uPz�����	������y1�;���i����4j-f`���#\�)�ʪ�W�,mŧ���p��L?��D5��(���T�������)aC�Sl�����o��HcM���eY��j�|*Y�L:������A�_�]�q�7Y2/R�f�B�.2�b"q�.`O3�JBvЉHW
�S~�1`P�[�����M�?�l<{�ȋWoHƺ����y�KYVNeIL;����Q�?O�VHJg�R�Ր����KiF_獆
E����p{O�3~�Tm7)��J�z>�E]o�rQ�2�(��#%� R��t"�I��_��!�SЖ��m�+�`��yL?�d��ꖿjXCQDox'}}x���G
�}��v}uCy�_��0�=��~���1�
�킩�䈢sd�K���6W�"�d�m0��%�	p��B�c�Є�
%���
d���q��$ef�ſ��H�a�3��0$��,rB��,�����}�%�o��_�N.qhJ��7b�0���j�K�amY$�J梲]I����8�S#�JRߔ:���
�6&����4�H��r��"hj��<����x+vS��рZI%�r�U�>kSxƩ��i���X�^ڟ�?
j�G"�'=EN�65�t3�n��a, �❟�qy'�?��s�%�y������#)�a��5���O��$eY��w�Ù�y���Hv�a��_�=y����66:�2����P@� �`��w�?�ֈy>6� �N�0�fD�'�>�Jg��3NK��#�\��}�s��dI �0������n�?�E[�K��	#��ʼnqd2�)%�%��?�~r�4?�6�o���8��+W۷��A�I ��BI8�%
Ր�]��X�^��L��;)?�!���Wm����;�xd0���,��G�PN���Q�}�� �j�]��c9��Ǒ>_L]q���@Ȁ��OKČ������(��R6w�t�aJ�Li�iz_Ԉ��_���t�$�~Gr��xu�Zx�S>���m�����Q�0\�4�#��ִ��x*�9?�������_#��d�M�G��L��~�A{�|q(���l����~{�3���b��wK��w^w��_�쌹�5�㷽�� �z
͙�7�›>�-�n���27��z�NKn
zb;�*���m����?������_�9)���1���8l�<�5����7
�Zi���e>9X���s
��)�����l�Z����5g���Z�W#v(��w��Ȯq�;fM�H�Rk,$���	��vd�І|C���?�)@;}�q-`������-�G�NFc��v��+�c\�d	�=!Ӌmk$�m�v&�����
�؇�Y��Cځh��S����sҝ�Dged�܆���Y�kNl[�+�퉤��$7�����U�+���;I��!O����1�� f�nE�c&��C}����T���xq‘�n�[*N�X�m�&��/�|QQ�����l�_F5���=����qLG��?��	�yL�h�al�H
%�*Q̠�&���{�5��-�Gj�Ӗ���CJ�Ɲk5��sX�U��iuj���'�̹�O�+�+Á�<鏜��NJn0��ҽd�>�{�Z��m����k`	~�?%���v���G����T��Q^9i���o��;�RD��-2���q�-�)�����^:�f��7,�W��#�@��+�Tў������ӷ�8
d���?�`�||e�3���(�ϩ�8w�%����7���k�����v�h����:�_�M2�;�ol�ٯl�u����C&B�����&�X��l�L���A�O�7��՝9�|���2d���]�7��>�{u�|�vTN��
4�4h��c�e���-[Ů{��D�mT�SbӌQ�����"G��v������������Ӡs�ϫŌ4�z�ƌ�(I��)�ڭ2���΁B�-��j-�O[IJ`&�L�ԉe�-54�0��J��ZNJ��?����W�g{2Ӆw��M���&��9�r;4e6Y1��k�^<��0�y�d�H�����U*_J[m:���ȳ�7|�&�[
Iw���6/�J��D�W�>�Q�����_�(�|�zq7 �,���������?����W\u�Q9���}��ms���G���_&����q^!8�oZ�6��&iv[y��f3{��a���hV���=�%��‹Xo%���"uʽqt��Ш��g���O�������~LLፑ�v�6�j1d���x	���{�.i�-
��$hm���n�$�:��	u,��2�_du�y�?=�3�n��`|�Pl�?
��llT�\~p�Mө�tL���/mN@���fC��+U��W�l�n\	Ӡ�(��3�ϖ|,?Q����B"��#f�'��W4� ��AJ�o7��)�pURF!џD
G�JzIp���Mv'E��_�<2n����^=�Y��̰��v��p��_���hio�Rn5����nM�µ'���jܧ_�����{�_F)�(�S�&�G�ԇ��bh�\���@\��A��$���%a���`hO�K`4�5_�K89i?��u}��P.�G�r������_�[��Վ�1�7cf�#���Z|�Z�Q�b4-��]�W�_�X*g5f���!.��V�su\�ZR����JH���3�`�����b��h�5�	�9�W*pu�<TbKw������Ʈ��n�M�Y�Jv�f�]pM`�c5��x�?
gbQj���/�11YL���l�z�f�e]��-�{
��B|����D����ɹ%�?�Q���ޒ%˰.���m��<�xhxPr]eA/9q4�|�T��
�*�s�l��z}�ӵ���@����:Y�~�ĸbA����0ޖ{� 1}��Y�B�G��G7#��Tq[j��,�۵ěG|�zh�(6�?��������
��D��ɮ�v/�����y���,�7�n�a�t#o�f�Vm���i>OX�"
$|�8��ެ�mdI53�����=�xi����1�Һo��:}!svA��ޯI��޵�k1"�ڜ*����%��?����������ЛF/��쵋?P��Oc���s�Zi���ES`�<t�ˏ�nɓ�YiYY�����c���N��~ ��h��h���&�۞��]�702ʍ"��Lj�0��0��w�6�С/{��"�ꔴ�cd�;}�7�M?2ֽ�k�O���k��H::=�9~�l�[ڻ���غf�+��ILM�?���[�����X�c�]���݁��W����
?^џ\��P�!걵x�r����<��M?FzT'�j��r_9��"�H��w��/r�c�.�V�}.�[d�¢��DzQS"ۖ�^M/�hn��Ld�;Bu�z�(R�m ����Z2��[�Ʒ��qG�)�{�H��)$)Q�HCFl�&�o��o��[�)O�!�@=zG�k�I)y������F��ړ�!=����i�o�رR�������$����� ۚ2����6�9���o�\��ws$Oa5g2
�~�������$�;�W�.�>%��=F؂��u��Lc+PH� 37��ϼ�w��-�_�@�J�^��E;��C������8�<��?�lwa��!���K�C�)fʘ+A�a��^�R��?B����
F�8�I�0]�L$3%�kn��d�a���6�+�oG��d;�i��?	�Y�Z#��S�F��:��f�C�e2�ݎ���n�B�:َY�"Kk7�d��>C^�)�f���(>;KԑT�Er�d�K���;�V�3a�2Ϫ����&XK��3�:Ӆ��S\�b�;viy
4nn�Ɛ��I�U���x�$S�y�:-��^ͻW%WS}s�qJ��!
\�pnc������l/�
�Tr�q��5�0�2��r{L����փ:|��������F�_�-b�I���_*�&�1xnu�8��yZ���F�D�e�=1yY�d�gZ?��l$��2ǝ{��j���vVtf]�XR��4�{N6O.��j�Y�R�v�W3��|��9�?fr��|/tKh̃����?�����b��+d❂]���TYz��"S꒶�ߧ��4�(%�=��w����7禄�t\�V�%�)�+CP.X��.�w��䭻M�F�]�Ά٨��:�k7��n���m�cĶ;^�Fv�o�}�]�,��u���
�WǂI	�Kc��C�}�o�G����`YH`[N���B�����cy�����ž%̫8n�4b�f� hǀ��l^2��E��0�q���d�*�\^�Ι{fo������2Sl{�w���x�0���b�����իp�ކC�՝<u�)ƀ�U��P��@p�f�Gx�>�]	Фᖶ���7�c�2A>@���K�R5>�=�wO�!�_�Z��7�?��_��\�񞩳��Ļ��u#�l�/h�����⟟1~��a�������O�1j
���ߦ�e����Om����mxx�^��ѣ6�Az�-��Y~Y��]�wg}dx�o�w���5�2�ߦT�k<7|�_���a�G�\����G=�{��f�?��%����:��	1����?��.������KOI\E��1t-9����9լk�Z��7��3��x�Zl\����<#��§�vM`�͍��þN���0|gw�?۔�ޞ�\u�C$&� WQ󾣃����k@��v���Qa���.1w�6��������4����{6F2��~���jz�o��+�c�_4��B�b�c��%�f�~����:�T����g�.�CS5�4*���d��������-u>J���]=6�[��)Q��
�t;^�d�A�Cl�*�;q�W>,�?���������^6�w�;����q!QY������v�����ԧ�L����,?��r�Z��'T?煆�>5�J�9�˔\���n�v
��-����\p�Ӈ���oW�rf���~d�l�o0��}���������z�K7�7'�w�_n������j�K�g`?黡ۗk9��4��ٍy�����u�jY�0���Q�Hik����	���L3�����~ݖo�0k͕�^~w��,? �1�b�1���P�����AH5�W���/},�M����\��㢱� ,�S���ܱ�zӗ@j�RE��j��o}����u{�p�ɥ�`�=�d�
M}O��SH����ܑ���ʝ�#�s�1$�����M[鐷I�[e$��'g�%BĿ�8nNĿ���c�	�d���R\��ZݤG��.�R5�.��Pb[ԑ�Mz �+`c�i��������m�ȓv���g�$Hď��e��Y6e¼�6�.�Š*�R�t�G������4e����=�/\G[��**M�@u{Dm�P~��i�D�h���a={��ߑ�#�[����΄
�c}��g��+�����sv�u�G!awQ�kڧ��V���m�`13�#�pL����i�S�*5x���jm�D^�Ab��ܗ��H�C_��8�Zc�?5[��Z�:pNi�u�\O�q��=)����/�?��M2��Օ���\⿭�_��~]I0�|�A=��^0����?�Z9��4S�+��OKv7��w��Oߥ4��VZ�o�3���ѵ0<�t�Ft3��n�O�> a3��9�]|�or�۷��(���uwZ�����z�-c�	�l��K��gL����Q~C�>ʯ1�58�h3;�x��礯W���캅�.]����]3�ל�ayk*��J�u��u�*H��m����C����o���u=7����*I{r|�49{��ZX<\[�E�n)��q�JނI�SŃ����?��5����
�����@�cTs���]��ի����]3?�yNl�S���^�(���6r�S6��!#��b�-��%q�f�K��1^�OC�Z]cזa�fW�i�L6U����lM���������?�����@���[�x�E����?�2r�(\�"���E�;��F�����c�Z�[90��5]M�
�-�|%wǺ�|io
����-^����M�i�w�Z(!�w���+"����=-x���T�8���Fھ�ٽJ�#4�^����a�~z��j����#�B�Sd�~R�BԈHw����i w.�7�}Ɣ�*�O�,]=�9t�"uM�D��V�7�iJ���%��Q��7�Nj���������hވ1y���*�\����5{lV�潿���]l�c'�SFe��c��3,�Δ�z��1t�iU`m�I�	�)�a����5�B�M�4�1-m�6�����󊶨!`����(��Ls����"+�o��e��9"��&��Q Ļ(��oІ��t�/��K�%�Ϲ�f	�M���$^1��9ԼAHu��++�C\�����J�ۅswļ�_���}iȧ���S�Ƙ�o���Gz�E�U7��u����-�a3]^*��bP�;
@UE��?�G��V�Y���=�P���	!�(��}d�@�9�Y$f�Uij�+R\�ArFWnsf���C�����B@�_���}��
Ѕ1`�
����,w�h	�4��R�����.����Q�D�Kh���s��&K�Έz�d�%E�i��Rף�p�g�n�a9�%̘���P�Ƙ~h���6劑C�G��c��ej��>P���gwE��'���r}��3��h��	���s��<���~Ĺ����sYr�������=۸ZZ��_���A4�C	�>���
U���dҫ��m���,�\�O9�v�`S
���,C#_�g>�������_#�}�W7����>�М�yw�b����n#��0�[�0\ze���t��Ē1��ۜ�'���8��m����0'¡��?Z�� -uǥ�YM;B��٦���`�;|E_��z�V�^�N��)��A�K����?�����ǿh�=N�\2��7~��K-�ȟ����W6^�,��B&���G�}<�j,�,'�=WKN�V������0_���%���Б]�O��#<2�{�����'��P�y�	���,��P��\��>eح)��<5nXlx8�+�
�w	o'�1���DJ�����UbS!IU��+u1*�Ƚ;IR�_a�� �5(g¡v��_�6�!�ڌīM)��`f�7!39��k$�.o��l�aa�q�т�ݢ�%䘹]�R�w��(���,h�+�g�`���oT�HTD�n2
���o���oCK�eP�Pp]9/����Zq��%`7����G�U�a�}��`�e�!�f����8y�I<'Ђ�Ε��|��*8��,!5���N�Q���+]Ä��~^����#'��:Z�EV��ME�t�o��jJ��|�7������*+b{��>b��IyK{6p-[ӿ��+�ſ�'��D]�;����X0�=�sI��P�`�X�O�+�W�S�k%�z[����Ae��#ү�t���j���TB4ͅ���&����g[�7���(S:(3�8�$+�N�,ޛ��l�q�t8��:�8�i�	�ƿd�a��Y��Ʉ�(�w��=/Xxb�H�����ϻnj�N��L?��M�q�!�]>蟦����K�ߕ��J�u�����_���Z����1Ϯ��7Y#��hM�U�MSFIY��ؾ��:�?֬�c���|�{��"`#]o�J�àM���g[�s���f��*Jcd�JR�y���ڣg����n�4�s�W��H���np�I4�o
tt�f��;�BД�Ë�F
�%q�	F�Ě�r�%F�&Gk[�t)�%�1��$w����q̫����Z�,
2F穤G9铛�i��d��-�W�����?���4�\�sN3�䭁�\|-
���`Y�&��`��NrNrj��~n�Ȼ؞�*���k�9�/b�Y{?���#i)!���x��%��1�nϺ��b��R��'���Qq
i��Xϡj���t�K����I�r���=}M[���bS�t���'�_@���4���ZQ�!Ĉ^�{�c���O>[ȹ�o�
��#�������?����?��7���?���J7�],fK8&(��am�p�U�!
��'�r��+<��r�N�>�OqX-ZZX�R�E�9��?���ǿ���}q`�Ǯ����	}�R���h��;�Tl��,K�x7�Y<ef�E����:�ey�ͯ����rA;'��5�"r�,�6���\X�{~�*��QDk��Q���C�p�`L��O���z��~ι��v�|"'��T�;�+�J��U+�g��Sa�������]eՉZ��]H	�'�x���B(��8ev�%��UA�)r~�!*xT��iR����@y�I�X](uY��������Ǟ���o?e��o����û_��H�ǕΑ+���p2���� �IeSؼ�$&>�L�8��ʳ���#�˟�tJ��?�ɳ1�b��)F�ҷ|�]�#!Z-���	й)^�|�ɽ;[�i�~����Ǐ�? ߒC��ta�O�	�*��8���hK��">-Ҋ����9���� |`�����-�����ѧ�����$�/����~�����k��p:WB����T$�>�/����4S�,�HɹӅ�I����>�3�ޘO�)W�?�/�}����~���4u��{��&�(O~�������)\��>�~��S�����4���ډ�'*�A����1��� 0-��2)�c��+��?�W�&�sW>ف����s���e,1�~�&�o��.׷���9Cc¿ZH1�&8dGP�������߅ǿ����?��+���$'��`�>�H�lP���z+:O�ԓK%�ƿ)��iCUW�%��-���IMJ��?�>@����J��)Ul�1�;�Fe�_>�H2�u���߂��A�>��Ȼ������	���29O�Ȃa�t�$��_��M�܃�\�/�B�g֑��?1y�Y��,�6Kyin�?,�R��;�]�%��*o
�� '�,���N���^u��,EpA3�������
B|/3;���od.<�år[�?�qa��z�#.@4Y�|�i�|�Ϭ0�\aR2P݊��[�=���8xH�e"�c^&`���`L9'a�@��EbeĤ����'�����;�l�ʬ���^JW�U0�-v[��ܗ��vx���zeJ��	\$�bfH�Q��|��nt�:�����Rm�Il���	x.0?�q?���#F;C��؏�/����u���_탹���B�F��υ,V��՛s�/�rA&J�vw�*Hc�4�B����Cs�HA���P��l�{���5H�1���xe���ťپ��A��m�Tdl*�ȎW���·��{��bkiM�{�0�h��W�����?����������lEL�2�_/�E���]��=r�ڙ���f��ȼJ�M�WG_4&��s �l��s�`>
_H�|�-Is��C�g�M��4�h��"I��jo�WDxݳ�+C�O�ڡ�
����?������k�z���QA���or� �sj��u��A�\�S8�1��Y�u�]Jo�p����T(�����eȱ1�>��l�{��f��)
�I6�6��_dC���J@�u�%="4����c��:h��vLJ8��A�ƿ���Zq�D"��
!0?�7��W��
6�ೕ�����Ż�B.�U�<4���
��S����Ѐ����?�_{[3-�J9H2(w���+{�n`$��K8m
��8h!��!bF9@zb�zF����BZ��������s�����
}�o݅�]�>��D�b;�2��)$�Ho�3��{�|��`�^�laBJ������o?�P%	׹�\Od�!�32��#z�?
��|�mR���w��.cȁو�W�Eɠ B�7�?~�S��#H,g��$vN�_]�gx�E�k;�" �h��]B�R�DV���O�����sz��E�'�S�����ȏ������g P��}=���՜��~��aZ0��2J1�qlō?���GY�C�*�4c�!n`��T=�d/��FuX��v ��]ѯ��3��Zx��g������-��D�@S�]J*!��a�jɬm�HD�	�����*�j�w�[�4x-T?�����b�`>ڊ�=1_���hDZ�u�/���[Vn�lV;�����[5�.�27	�Ѷ��xD�I0S��h�?�y��T%!��t���;�����\����K��4��w�cV
���?yWM?���'�����hH6������A�|0޶v�~W�c��jv���c�Ah!�ud�"se����sf��@y%�|��ӈRA��׌�<�:^!!�b���H��b�c�s,c�2����3I��Ik�'�ᄅ�����i�Ь��B�?���������I��D&L#r�v�����x�T�|/w�4q5�I5\)�݇�,Ԁ����������[l�E��X����DS�6��j��Sǰ�w���;fo%7�ļ���g����xclAssy|�翲{��y�%�����n.����h�����{�7�����n2��RpW�>��;_a�Ke����_�����?������>[׍�.+�x7�ܖ�{]ݴ��A���]6B�C+b3���B�_r�<$ja�RK��,��>a׏�Q���0���F�`�G�'	�����^��uVO������:�I�j��#YB�wφ��Fm^�I��od��.��m}Wa����ij��@�5r�S7+�ΰ,�RH�Hnv�߅K&�l�W񃍟&��|�+b f`���7�k��n���QX��`�X&w����	�Ai����?|!�<�N��kAg�-a��_$�ƀ�����0����g�@~��#릣muo�������=b_��x�l�s�.PӾx�h8�+�����C�w��S[�;#�"E��
��W�A�x��@n��91g�-Ѹ�H�p�sY(�����*�(H� �n������ɣ��lH����h�A�����O]����*��,��k(J0QyI]�_�.�?�Ax0��
���=��?6������?��j0t��A��Lb��9���8���1�bt��
1g�hb-�\a��>:�⿗�ءG���ݳ�$�F�9-!��m�D�
;�+�ߊ)��@}����J&#�!�����'�r������w��J5��-(�p�y	���*Q�'�s�~���`���/bĹ�~������'��7Վ�aE�KТ@$�ds��t�{�S�H3�4����f�?��q�ُ8���%7Oɟ��d��0����~����3���F�/��\=�wܑ����"q>_	���{�Wg+�`���t5@�4�)�8���r�z����/\���U�w9D6��eg��w(Yfo�4�C_e���f&��ճ(	F
4����ǂ���wi��v��Ť�O�oRo6�Fq����u��p�U��O�g_y��;�����k��#v�;�9p0;��t~Il��5�%���Q��_#��?������{~��O2:̶����:�)�<�M�0������g3we;p����K��;
^���f�^�=�V\�w�f��ʺi�9H6<�}��X]P�0�&�ǡ�ςV�n�\�Zh}��QM��9�p�/��Q�*1�දX5���������?��U�y�8.�W3��F�_��Ě�7���Œ��kyź���|-uBn����JN��LX0<s�"���X��������bSH�#���#����$cU�Q��'��N�4����D�a���~�x	b�S�lta=�wͥ��h�v�6R3z����y/��g��|��-ȥ>�->��܀S�n8Z��#�pI�h�`T4(1n�R�OgF��e�H�?�rj����К�Aڄ�nA4i#$���t���!�CƎ�}���'6����@��w���-%L��YJ�J�-G���NJ?��1y2��ƂZ��E��efn�"|���̤�y��p�"�+�=\���#�|�3iY���O�J�4C�0�%f�
6�%6�-�E2�p�O���h„�@����Wb�����i���Ob^�#��m���9�-��i���������?��6�ѥH�L�0��Ŀ-�{g����Y��\���+����M�v\8ʗg��Y�bQ�����6,��^�W��h_�6�Nl��p�T2#M�����+���E"����x#�%�W�? gP�1��n��"���2���W�E;�-.F�/$g(a�@���3D&'j!��#ߐN��-�h���\Y��
�3�K��
Ϊp�U5�f͵���-��^���u�e;:���Vo���=
zu�)����Ox\
o�t
}����˥|P��sz�g��J��#���3�4l�zuhG�@J3���3����>�p5����/�y�[TV�o��5K0�])�����s�kb�ќ�t�asX�t+�cySW�EZ����m�i���:Y6�Ti�jǓ����5j�l���#	�.��xEc�
H6dh����?C�u�^w?R2���(�Mor�7�+�_o�W��s��������?����?C3�8
�u�9�=�ch>\�t�>��{/�E1��xs�	�5u��X�潓16�%3��y��Y$�Ӛ����tw �Wr?��7���H��c#7~���]���d换�`��K2�����?��O�/.,�X�L@�'2ެ��=�4��v�.��V�77.f�<̔����w��M>8�o�S~�0���y���k��*A]����38�=�/-����ڕ,�����`��ͨF]�2��>����vQ�Y��a��6��\n�i�A[�F>o��0�9x�Ej�w����R��ګv͸ƫ�R�_ }��G9��ޱ��n��j*�������4GF�|A������R�1���B斪+��\3�.
�f�-�����+w�q��7������f�H@���JY-�EZ�S��'�C�{Ʊփa*�N{6g����5ێ����X:��K�$�Ӝ�A,$��ͬFA6&O�Ya�"�ont����̑�������+�:A�/B��H������9�j@�D}��v���+�l椰48Ѱ��3���ƈ�4
��mEL����t��w�5R�CA&XĿ�.Dɭ�X�v��J�îv�Ӯ!���EM���80��4�G�C���w�8m��c�瞺��M�1�(�H�t�������l�/��6��~wܡ��f����E�^g�����;�Q/��mN�(���WPO�}���% ����?a!�j�]������rt�o�®�F�M_Ɋ��=�q� T�.�q���cV&?�4�ɤ�
~��ܻ�Q�X��l'�'`�
�I�e5��XN�O��gɼ�r[�5����Fת�c����$;f
���l�K#�ዜ��&;��[z �<8Z@CLF���Rۑ캨Pl9ߌl��W��	�TXi5VC}���ݑ����s%�W�V���3��| ������I���9�}�{���3��*��������7�l0)���(�O!�ݯ���]�CY}�ro����IVsxϡ�y�sɐQ���-C,����>�j�2g�
��;�-m�F!C��+�it����q�)??�۫���f�Kx�Hwݢ�	�G�a��<o�7M$����?����?�N�Ǹ�U��G�Ћ�1��İt�eg�o@6�q�F���J������f52}�-ۛ'\u
��<��\���� ��`~�v�@su$��П�fj��z��s��H\E&<��lj0�����?��y�p*�%�\h��3��XwX<���z�+��޲��e�Y��m�����W�˦q�tN�k����L�\1�=�͝�L�Q}
�.RV�s�W��U��:Y�����m���=c�ȼ������ICy���1��6��f���O�d2��Ȥ��|�M�1�:b�~-z�?�Wȃ[2�t��n���M�����[R\�:��ڍ��^�.W�b��s��w3A��c~�UT����(���cL����v���=��@��@VgƎ:����@���$	��S9&��7?��	(�����q&��d��v��������'�[���c=ϴ��[�=�҃e O��_&���V�'�t����T����A�h9�o�K"G���x�,��|�f?�V��M�'3�3�}��.���{��1���ia,�l!s�����A
ȱ���q�?rv����"�~���<?��1p�g8�_
�1M�d� N3ҾO%T�<��Y��2�"��,G��K^����^Zd��Q��_�G�g�
;CL#�G�_UK�xΈ�t��,���vq�%T���g]
�t�a�&W�	M6�v5�`�}�*Wg�>�%�ô;�w@��e��t�x-���-��U�+3/����7��ߩ��H��P���ٯ�K#gFԌ�^�KvW��LVv�����$��ӪM��ൻi�����J�H/�WȰ^���v�1|t�7���6]�CP]�����i趇8�I����ɮ�T���
̫�)�̒߄~��(l#e�l��j5r7f��~�b>�v�!>蚪V����V.�	�����?���?��a<�b%
c�h������$��z�y)]\ޖ�˷I����NLjk�k�Vk��ח�M`dK�fe�a;��N�U�]]�V�z%m^�	Y�w�q�N��v�.<��87Ғ���Wt��a~�F�<��������������uCc���kq�l����:��4��x����7m]�K�)9��9
_^k!1��Q:|��ع��I�#K��P�a��%�m~zJ8$���R�����#W;����
���N������\VUd�6������?��֠R��_-,���|-.\M���q����6�}�e�30�Sd�i��l�v�w6M�2ݾk�u�'|�F�B�+�	(�58�ѳ�,��|����A$HP�!�����nD���h�dc���Ji�����i~�x�nt�vn�x���\y����(��i��9�
h�Tk�&�j� _8�BCk��[?&`�g�8�O��Q���'?�tI`��&2�8l)�[�����ٟ�$/�	�y���Cx5��h�#�*�wT�0U��k��Rwc.aZ!>����?�n�R�Ld�⻐�}VM�h6C�ֱK��-Z��٣%�C��8�:p��/xW]�NS��'�
~��
�υtL�����p�Bv
��"�q�؄�v�?��0�O�К�~�U4��X$��#�=�$���/@��d?ۦV�E&����u��iLPX*�&Т���/�߃�#����$�^ќ�l��S\?ȷ'V�$�1��;Z���'cJs$�yӁl&YBW��dO��Lw�3��Y�І��X	_F3@jT�^��,a�>r�3�2�j��Pw^'Yw�:��/��s-C�{�b�s�M�
�sf�z��.���	�?g�d�_�Xu��&au�Bt�����6�mkK8Y�a$�1r�TF2������R☀����#�b�a$�5Lm�V�B��d�;�di"��L1�43�`J�w̟�9�G�y}i}��<��8�0 �o��
ˊV[k�
v���<Zo�r�g~3�s���4R<�S�Y�\�~'�4��o������?��������QT��Q��
ޭ�kw�nlt�U\s����շ{���~�t��Y��JOIBs���g!�?҃ļ�:ѷ%��m�՟e	���p&��R���e�o�#�M���$P�X��%������b��?��������l&x��n��n�W�r�XN�5H�Ԁ�.�LsU�~�dն�-�*b�J|��`U^o��S}a�@��LMUh��;�m����X>����g���w�Q<�{fJ靅��U@
f.���?b%WN��7�������#���$�p�T�'[R��"�R�|��vGhHՊ����X�\�F�"\�7
�g
����7��
���*C����p�+r�^C;��l��
����68�� �o%�l�G�}@j��9�	�V�0%w�I��Z��Y+x_�a���M�j�DZ�$'�V�b��Ki�t�ds�$M��St�Q
QHkLj�Hm�Gy�[
ڎI��d�pD�C��L�ć]��vn�a&T��,�
��7��4*���L�
Z5��Sj���nwͱğ�B�mB#�;��� ��0�RC��q�F��6���>�z�]��!!��⿅���).�m���,+�7.��|��V��Y@2u"��j��e��[�?�s��b��W�;��A���O����c�	����v��>�i�u��.N@�"1�D�p)	�l�Q����ԯ
r�@+�qW����Wl��	�a?;�g:����oנ&xp�΃��h��w�k�3���(�r�y�wc��Ƚ
&0�;��$W��� <���1�����n׸6`0f]9��
H�Ye�z��7�L�;7h�]ؿK��cXJ�S��v1)�drX�a�v��d�7�������Y�r���w`�E0k�v<��嬯�?��`�jh�D��ܛO�қ.��e���],�$r@�+2����a>����{	��;��o>|���?}�.��s��֨潹'̶�����Q��fN{�
�H��\�Vi���8�~� ��Ak*Y���
0��$4C5uB[SU��-`�x3UM�u�eDh=�pL���7��q"�F�?�������}�0iz
����ʕ���s�t�Yظ��c���0V��	��'9��x�U�AN��u����D]���m��y�!1�܂�9�{sr�*����l;?���%6N�Y��}"]p�}Zzt2Lm"�¿$1�+��.�ܳ-�_��0��%X��Qڠ��5��1qO�IWM~�Sz:R���|�*9��@����-�������?�����Wz|�]l�r~i��L�{�(o3Z"�/��7�i"%��_�%�6�51��8;��ju���E/ &�,��|Z�?��R1�aS9ܢ�~}Ι�����'AtL`O����2��Xx��NF���{�v���o�(Bz��������8|*����?��q�@2HX�"�C�Ed88qC�7�<�W��H��o�υ[��$x�4�A@ŝ*�*ܾ
Ե��?����t�f�J� ����#0���˜
HN}S�0�Y)�O8�ph�(����YFx�@%�GR�E4�Z}=l��*1���'��-7���$6+����$GB*6魘�����s> ̱4!i;�_�/A���;��̃��Q ��_*+�E�M@��`N~�BoIK��l�w�0w�+��)��%�KBI����J[|���T<	ؘQ��Ob,�p��$�ID���se��a|�2h��p�'��8�mR����O	�ٱˀ��A���?�1�=�����ϕı���$Hz��0��bi�=��R�p"�ɠ��W�0�^�ڍ���q�@89$������ƒ_�(��*oO8wޞ�{S��JN��`��';K"�o�|ƾ>FsD�`D1�?��S\��
�s�����ľ:@���]I�|�mZ���D0U`��s6ϰη)�Z��! ���Ӏ0�%��0M�LB�Qd;�>uO�Y
��(�A|���M��!�ֽ�3+���?x��^�@�'��H�J�b~��o)�ɐ����=�>-��]r�����ѓ+	��\�#ǂ�$H��SI�O㘦�|f�yuxH}��aO�'����Z��ܙ�4��o��-��_�4eDȧ������1W>���Ȁ8��T���?]^�_�)����nG�`���9a��/�IO|�߆�~���?�b���ɧ�� ���K�ֳ�Y�N����#jrPGz!�(Lo�r��͝�mZ6g@�鱫U�{�+��;���H�'2�7�� �㐤���4��c��HMI�yd>s!�E����y���#$y.lk�4����������:QLؕ?!e�\�^� �uqRԠ��l*��KP�t
����n�c���G�b�&|·p	k|���MoO)0�D�Lc3��>sr0 ���q���<ݻ��M�V�zO��$C�X�ЗY�*��}R��)��|C�����
zz�$�m.U@���l�yL����`6A�n�jԵ@P��t�W�7F�[+H�4K������A��?�?��m����%�ӝ���[��-��M�q�!]%��%���'€�cKp����1�ۚ+�ɕ�U��0�_��UW�kVs�R�j�_����k����)З�}KV�׈����?����|�T�S�Iid�!t�F�-sB��4M8O�U6���yл4~1�{չl�1E��)�?ܶ�=�ү+۟
�:-jI�S����ԌB�x)mƘ�&�-�h�xҵ��1cF���O�#=��?�������o���3sF���� -����ͮ5�!ۺ�.Y���k�=�~�����>y��Ė�.'퉗is��u�L)Y��5�n��<�eZ�O�u��|SF�x��5�4�׷=[v�[�W7\	�3�[Ɖϛn|�T�e�͡nDیi�.��4�2���Y�
b`��<�s+��۸��E��o<�I��M5�Mi@�j{@DZ�;��z�����c��XYj?����,���<Ȗ	\�DFCB���|!�a�
���&�?���d���W�W��m}��ݎ����3�_��~�T��U1k��
L�ڗ�O_$i?��
97=�>,��|BB�d���Yh�u�� �uq�'L�v���u�-��a�b��C�:VAmBb[���6?��.�uB�F;JW����{(}!��;�����XY�/�+bo��=S�W��c#���������$�;+�/�M�#�]^0ρ���RA�|���(�D*���h���k(�jj2�M�T�6C[��m<��+�m�n5\ǎ����_�3�?]����}Ol�t��9(��y�5�Xy�K�ſ8�l���Y��ɏa��bl,1����?���j
�k���kj4Zkl�8������Œrì�ϥk���>��6�8�|�v���[�oM��e;�.ƫ
�k}j&W���84
�qHa}y�`�qx��1f��́�w�)�h��=���4�4p��$& ��`���9m�,k7��z7,��z{�w�)��N��	4R�G>D�����p��Gh�Rri�4�x�Ù�߳>
�&#�4�<�m@�Nhy,�êŸ���}���!_��nĦq��k������?��OXnߵ��U��׳X�*��<&��ai�����ܓ��c���QVd�xv���P���)���o[��ʗ�t�k��R���r��*E+�ΓtU��t</Y�WV��9���4��M�����4�E�Ҧ����������?��������Zp�d��u
�:3؋7�ū�G	7�^i������_�Z�e��!��M��8�k
q��8�7N�����?��#n�$m�{PX�0�}<w�<:�}#@޲�+��!�,8�|��Wg0���y
U7����=�{˗v��<:F��]ؐ���~��oE]���朐��W���7�)��G�kO"�IJ���9?Mi@���|��)��थ���H���ozw ˃�l#X6�LX�9X�TtЬ�e�IJݡ���<�/��6�UA]U-o�����~�;'�Ǥ���S�\E_<B9�d�)��]���G��!��r���Y�ipH؏f�u=y��S5���J���s�Y�CWF��������6�M/>��B������?sZ���_�K�_�K��CG
�� T��r�R����
��(����Z �?�VY �������(� �M����G�W�?������.���l�wG��;������`s��?ǿ*��>��KK�_�)�)�T���R����d>�G���U:T��Y��{�W8i�R������t�I��,����N�;j9c_�C��;�I���ٱ	jEhJl�>�f�	��4�aVJ�益/-�����-G���4z��;b����;u�P7�8��	$w�
{��✟��*7���l�U[�ə�f;��?��;�+
��r��M͝Ψw�ji��*��f���b��ܯ6�ğ��S|�`�(wX˝�����9�$�
8Y��Bd�5@����Wt����RO7m��{���gdXD< 5@���]c<�Mv}6��sC���;p��ה_#��?�������l�n�C�o@cڠZ���܅�`�i�ڋϫO�IZ<�o�4�,'���vJĹ+��L�ab��W�>wr�wAe�S�(ׇP����]��HÜ�h4 ��kbT2f��\S��Z�^�����G$p�~�������?����_g�bަ�}r]�D!Wd������Uj��6b璉�?�],��h���J&�LLi��ƤaŅ�F5[���U�s������a?�'+�A��_��l��r��o�wYNKB�l`�$S@��o��3ٞK!�=e�4'O��u;��bp)��oi��D�B���Z#sz_�n��P��t����p��C�r0�;i�����OQ/ʀ�:�{�-^ܐ���!
��LY$�]_7r,g�PP�����P�m�a�	����~�1��r��;E�[�~��;1h�I�%����z^���(�������z7i��47�5<:aG$wZ���?�O�'��I-�Q
�(X({��?��L�WK�qh(�!�����p���w3�ûlJ�
MS���(�7EY�^�bX��
j����5���H��&c�'�0�g�9F���K�-�ȹ���SƊ�@��*����;��cl>�Z��[ԎD���u����
�I��$��IZ|��<G�"��Kk�;���O��¼�?$��|�~"�$�*��_���™T_�!(��������!���H-�9	��
����^w���ZAm&���/��fؕ�|���Y���{����?�T��o�ߩ[F���9��<�\�9n4�i�7Z�𻵴����"N�Y���7J3�nR:�h�C�A	��a'3c�M�N�c�[��/*�)��&lrOHϲ�ڦ�0��}��c��������r�G3���E��M-�]˼"�oY����Ӂ�A3��8���@x5R]$��ڵ`)Ɛ�:zs���RЅ�=2�[�?�p�ng��X|uL����,��o}���kO@_��|���Q���-s��֗�a�֙��f������F�?�������3)P]�5ce��ш�Gg^M4-��k�/N���g�����+�K��vy��(���Ζ��6��]̎�"Ȕfx
l�H�	o�CM&c�.n��wL{I
���5瓄��s��n�@���L��'����?�������o�y�=hhk��V�d����
��O)�᰼�`�#��>@�4]@'"߳W��g�i�uJ�0��Hӝ�o�O�G�uw�	]`��9�0�Dy��i�4���V>���=H~%�](���ʃ8�Y��l��}������§Q�_�!��ɸG�**�j������v'G���]	�ʳ��mE������5�v8�>.�"���&���>lW�)�]�v��N9b[	�c��V��|Vʑ�[Ay�8�}Gb�OWw�A\�+�M`��5�x�r�u� �/�Z��
Z�_�@2�Z������/���
���x׎;y�
?E���=qt����k��7ԉa���?دǝ�
4{~��_�1r�����3�>+m-��m�o5C]}������bIw����JI���'{�d~�/���]�+��XM&��[��\��-w��(�|��-���H�?O�
�s�]AVX�K��Zk���3�K�\�	�+��-����p�~H��/�.�W�g�������/~h�Q��d������Q�!�b.g�/�kOQ$�o�����]5���?b��?&`,�z�;bߩԫ�jw@ǻ��N�K[���2�4P��=4w�)�{��{������n��Ò���M1_^�#c�n�8	5,b���%��0�(3��|�f�LZ�F�F�]�E���Nݠ�u�u�]�w�jf�LjW��t�Z���b�Z���56��<D|�Y���w=F_
�A���o��Dx���Q��׽V[=�F��)�&Ƕ����q���*g�c��
�D^h6�
ܻ�e�G#J���ָϢ}u����������sCx�7�v.G�68�J�7Y�Y�P���A�(��l��m/�Z7�O��Rj�5߮��>u��?����(X�]RZ՗~������}N_}�
���w1�঎~���w�^K�.�j�i���~%�u�~=��?�������6�SS�˻��WA�
�@��>_h�\�%�X>�.s.ɇQ����/גǻ�'ϣ[�W���J�5f��e��r6�fgݘ�fh�����5F}*՝Zt�"�����{Y�6�}尺������.�d>R7�P����4�d�;?C.�ż-5��1O5�x���� C��k��Q�2��m����d���!W��<E�h]�w��O�8%�(s؃���4�ø9�la�V,{ov�����l,#���US�����
�8)wv�Q�<օ�wu<N�F��t_�&k�Z�@���g�W��7,(B�R�,��	o�=�H�CAs&��Bc�>�����c~�
K4'�	Ro��ّ�j�/���x*qk_j�6�B��Gޥ��@�g�����A��o"h��BƎ�w�[u�D排�V@��е���ӡ�}sX��H�˖���W�o��v2_0�}�r���fu�����f��7ܟ�ĿX��(�0nDZ#�
6�^�K�kQO��� ��BHQ���`ԣP[׶Q�����ng-(H����_yW[�Q��W�‡�?y��w
�SE�]��誊"�(����.ښP���Nj)� :��{��!`�Mr��/����OW����#�U\���y
��پD1�����	�w���YT�2�D�+w�����A>��1�o��1"�Z{�n)�Ǣ�������iGR�J�)�.�4ީ,tm����,:�~�h���j�Qd�A[.��d�sӫLc\/����'�����3t�X��ݟGr
Jl�y�</M��qK8~��Ԃ�xk���,�V5�{oJ{��J�����P�<����r6	��<�Zu���������9w���ΙD�Hеv�Ln�9C�M,_tA�6����,50 ����͍���$�T�YcxQ�a�"ׯ��7
����J3M?�$\;�?!��%�$�_�ʲY���T������W���������?����n�]|����h�F��U�<���T��5���X��>���z�dk�1�M�Pl�?��k6vz�b�i�Yc��o��F{O̪��4�I]�a�+�n$�\",�J�����$uw�oz����$n{�UÅ�{W������4��\�O
�Wb��WS�K�#���e�ZXK~�ۢn̼¡տ������B�WN^�Q�0��m�}�1��G�c��#\ߑ�jк���!���'Ғ�V�רÀ��|6i���%j;Iv
�E$^w�B�����WvA���I	�����\~��H�G��eI�M>S2�����{>�����aw�)�;��I�W��Q�B��E��Ft�2���n�vX�0���)�lk�qO�}�C�����_�D��y��lo����/���8��P�&���*�=	���.�}�~w�����7^����ZL��0��,J$~���Л����tf��G�NC{0Ed���l�������)i!��k��&w�=�]����W��nUhNF�}��Bڟ~�&`~�?v�F{W�n���ܛ��c���Z����1�c�}�ꆸ?���iO+��wUӔdƦ��M��;0H��yp��S��_�I���X����s�<`Ƚ@%��p�va|4�kTi�4r�����#�&���z���"}��1��cR��̮w���6#���,D}�_4�h�AҀ��!�.Yu�N�^�����FӀI�X��g��z�?_��~���k
R��D?�꒟ޚz1<�+������?����.��G�8�}gd4{���3�,�.�欌�khA�ig~����d�=i'�6���|R'�Dk�ֿձ�%������[�/H�

����tC)�W˙Q4[��俹���66��t�l����������?���Y����)���~����r���OA���
*�W����'�~z>|X�()	��{.�u��s(qe.H�"_����]�����xC��)�Gx�������|�&��
1c�P��8�<p;Cw�O�y��̜e옦7�
	�>+Q�q6��̠v1l�0*q���&�!�?m산YHi_�G��nM��wx�8_�>��m[��܃t�ߖ�۾�>�&%���I/���Q�&���_�0�}	��6ʓ�?4j���*!Q�\� `[�7;���n�(bѳ��8�Qv���J��*����;_'��2
���R�v'i�����x�j��*���5b䶴��G��h�p�C[� ܩ�[�M�o�_��Yc����Y��$��O�	f;����@�?���+���Z�Fеv����"Q�w[��C��x;��	l�f��م�96�.�b3S���?��
ȉFk�#�Ln�Z�Ӿ�dWc�Z����Wg2G���JC����Q�%�bDw�&xD�(��Y�	Z�o�g�#�i���3�q��<���uu lPm��H���_{D�3�y�)��i�4i3������n���L4�Π)�\M��k	R3��<������_>X~���{
@�Oc+e"�Q@���y��&_1��z͠v��l�;���CՊ���L���a�Tm9�J���Bd�k�"�swH ��}׭�	��=AJ��,��5Ʀ2����4OP8��\���8Y�����f٤����ݞ��p5�vc�-Yk�ԧ�X����O�8��D(�r(�Vj�D�>�'L�!ء�۴�|�z�v�գT���5`����:��ont��D��h�����������F�����·�X����Ļ�����ꕉ�J?����9X �)����㩌�Y��c]\��̰0��2r>�A��{
�}N��;��H��	�o�|�wIy��.�c�5���o�X<8cκ!6�Ϳ�Ľ1�XSQl\]���؂�R2O�V�xL�՗�sVO�<�gt�۶sexK�{���[,~|������������H~����̭�p�ǿ���s�����<�̧��~���{�M1�5m���g C`�|�T��j��O`f�XڐQ,�A��󏭠�WT����Vޏuw���g�	��B�>��BI�G�--?s���]�.��;<`�Õ��4w�(�SI�{��|h�\�Nv����Aޘ��'����!�g	ʙp���o�y�DJwγ�+O% ��,�L W�r��1�$�� ����!�X }}���s����x�EC&�]�������?y&R�O>V|��{�k�	A�*�FML�7�Y~&�7�����K�����k����0
�EV��$qɏ6DB�s���Yw�:�=��w�V�O��$>>P��~�p_���fiPH�4b���G��>a2���0W�c̢���'��͛<�ĉ���J�%oOb�
W�C28T�Y����s���G�Ŀ���¤�:I����F�W�Ȟ'��	1���z����c�����_>|����M�8y��O?~��C2Ln��p)���j�WoyK�O��X/��E�
����I�h��߹ӗ¤x?%1hL�H�p%/��,\!Y�(P��B�ֱ@	&�^>���B2�R�I��M����$�hQ�_*)0�&����n�N����+CtT��#����q�n�v){^M���̅�pdt�L�t�bN��G`;��Q���>�31욤�>�rڟ4�INg��42��|�������]�±o����~�4Ne��ܑE��3��M�����]�KH�G>'g:&S}>?�v��($�O�ÿ�M&���Dp*�5$����u������?�q��������l)�==���Ђ���֯%�9�+�q�Q `郔�I��с|�'�"�̃���D����UGAM2�j���nI"Viqr�x�h&Z�O�c�������'��{5�
ᔃW(���r�����z�5:}PVw~�=��R>��r��C#`K�h� �F�ljX;�4��v�|��FCMt�y�k�V�HYhgp�B��h/�����,Q���6�z]Ѹ�T��I�1{�Y
�1}6�,'+�G��^��}
*wjLfIkc��C���J�f�E��Q}�⪥��}�c��0�d�[��3� �/��?�N/�}L�"�lܘ�d5'[�ڵ�g���t�+'ͤl{�S�y��q|5
�:{�H�q4��X����y�7����jy��dQ�F�?��������mO����ױ�u:n�l�K‹H6�f��|�/ɍ���Vix~MnN����ÙVr���yco9H��B��C�TJo��߇�S�&��1so���t~^ܶ��>�4o�F��{HT���e�t��������������I���JX^��]�iA��]�y�N���)����Y)���O�/���{���ixV����ճz�!� ���?eA�l�^|e��U����a�}箑I`�[S�b{�2�0�u�ۍ�Uk��nX6�{�{7�����&�넎u7�
�&��+���w߸�G1܋��u�85�{���c�Ə��6/�g��-���~������/J�*=H-���u��}�A��b`Q��J�Ď���dnC��j�a�Y�[�Y: �o��Ghs�E��4,5���x�v\��ĵm���9E������4nk˱gL`�㹡���6�}"�������JQ��8��g�k+w���e;�C��E��8T�Od;����iՀij�M	m}��T�ڸz�hP�G?����9�ʑ��qR�4j���o�c��{u���E���5-0��=�
u���^�3�K��h�B}l@~��o��%���E�t��_-�+�iA-�8sپ�����9y��i	#����X�����ɏ=���kh�j��
��5��gۙY5��{s�V����l'U�i����,�������!^��]��ܥ��-�gq��C�ſ������qm�|5���U����9R�kd�M�p�lj�'؈i�ư��}��D��j`�^hf;�¨�z�Q���u��OI
B5�V����ﭳ.��ư���k�{`=η�;�3���ӕ&2ډ�p<4�2�T�
�:?k)���1w����9;P����R8kq��'��o׷(����ɾW&�ƒm��^z��^O�6_#��?�����b
{��g��cl��I��v�򿩰e�LV�[��R�����n�<dQ/�	�s&��^y%��%�nV���!x��?�v�n6*�y��ʆj�</)نQ~&Ў�|�N�kݰ��E��M����a�ͯ������?���ߜ
���V���-�(�K�9x]�ɐ��/i�77䏑�mG�*s��͘��V���y^���6���{ A<�]U���G#ah���lN�z�:��c��~��4n�"S[����K��qd�""+�ߧ/�D�x��-��8(�������e��}E����Q���Bk1-���nw��Vs8x֛]w��Wh$�d���v��]�i�3�G�L&D��$O[#�vEwpA��������w��|,�8�ގ�*���o
a�lm<�s=����y�pQx���lD1��se7���&�W��铭`����E=�4��-�5�ׯ"�?�������׿����?�n��ť������g#΍��Ky5�Z�f^L���O�����7�'�oy(�0U�R��vL�R�$Ṙ� ����=���i�����t���_OE���0:(mzY��}��f��.���t;,3�L�Z��N�x12wv	��\	D��.�)�C?���JW����w��P�C@�?���kn��_����/�j$��6����t
9�_䌱I�}%���;D�CFC�5��n�T-�+��f��^�	<�"�'@a��bX���b�ͧ��/z*;J��L��
�
к
"�Tf
0.����bS�=I6p�?�7����{����zG����ju��J���/����{�oʫ��_�8v%um���C�P�KTD3B�y���5�}A�$���'����#�?���F�j�H�̣�SP���lb���,<m)��4�/��F�OQ7N�y�]���T���s����ہ�`��t����C��c�2�	��/t;��^����d1�#VF\���iޜ�z70MNVND��=��Ԗ���b��e��{h��_��f�v��xy��u����D̵^0<��+_#��?��������(	M� �%90��.?���x��-�X6�,|6<��T�KF�F�|��v$���q#l�=a�[�I�sҺ�&��)+�����.�1�y��.�>ෙn�db�
���?�h5�-x�v��V�I#���|
x�A�ެ��z�?����������L�2�҃|d���M%;���y��i�_1�o��ϖ�<8zW�A&�(��a����G��_�27m�绮t�Ⱦf�����Ǻv7YlQtuF&����%�p^���̽�����}����1�9�����'��dEOb����"v�!o�ޛOC:"����g/#��j�w�g��B����?�����M�;��Lc�oXl����3�n�Xx�~#_�1�͡�d�'��iY�Q�B&(7f]Ug�o,@H(Uo.Bս:�����gS*%n^{6���D��d��6�K�ƃ�!ό�$��FZ�*���V��$���b��֡��GO�^3�1s��!�
���o��#��
���!���Bl��ag������6�,[��{J�^���*�|P<ZGFv�A�h�)�96�� Ӱ�&�H(���YxAD؃�i�^��/��m̬�‰:km�J��K��謨E�@~�J�'��?w� �����jF�$l������y9�N��o��o�z��i�����
��
^��Z�L�~�?a��xi��)��4�O,�[�E16�W��bp���Yq3��yKfhPdٔi	8W�7]Cbc���=yڧ��(��w�1�x�?)W8���;�!��W����r<���=�M�Lv=��dwڃ�t��@�]�ހ�/�B�U�~ezPNRa6L#���U�T�ԡ��kE��4���ՙ
/�vk�9���%Kh�4�2�al�Z�u��;�7�$+F�b�@�yd�܍��Y�O���OY�Ed�F��v7�L�l`Mi)�U7���X���8��xeT`������~M���huMV�����Ǻ�Xj�͹Yu�@�X��4��	��N.B��4|;�r���ॷ�4&��׈����?������]-�l ]mK��Pnj	}�j�v�KNR%fb��y,�_�;�����bl�k0����Z���3Ȱ��|�F.��QԄ�OP�ZR�HBw\o\V�SJ��ݸn��=5�_7d��H�~��?����������]g��0�ҼHj��/��m4a����:gY���eb�A~Ҹ&%���W!��$;�,'�F�5i.�'kڋ,Yq}����Ft�b�綥4j�x̴��e��S_�2\��'��6.�f���`�����^��6z�9,�n�>�����.c�v��}��ߚj��b�4���7w�RR���[�Ho�L���K�6����֖��̉�Y8�wJ��3�p�e2�q�X�+�4Ns؀w۞����B��7ulP~�"�jy
�6G$��-��GT����ߋ�D�����[b�I�3�g)�S%�Y�q�B޻lq�1:j&M��B9�=H>+�u�#�$������
�d�)p��֝�u�����)���t���P#ْ����!�t�c�&DG���H66�T��3�f�N�dQ��_�g��Ͽ�_�w��y�w�/�ʢ����C�٥Q+Ɗ�+����W��ǣ���w�G~��Jf�׃y��z��j���Ir��5 gП�:��<k&N{���k�R�_{��=�Ƚ���Fd�����á�������,=��K2|���k�2o\�����&_�dAc��ũh�l�~�D�"_-����xQq��q��U�����GF�ϫ�͠#8��Z{�@o�׫u����f�uS���Q7U�Vo�Ӽ��
��T�Z
5�8���t�#%�G90X��|�p3qZ}�E# ��=t�s��K��fY{qle��NtS����v��9���Xխ�U��ݚw+�AHn�M��Ӗ֘����?�����!jn�X�ϫ��v��Ȋ9^p�*D/o��W�+�}�4�p�,
3�w�C��+�[)�>ظ5<dw�ewh����a�쪺���ڍ1�$���h�������~ �����4po�B�U�yl��z�?��������{m%�`f2T��9�;n2rt�=��WV��U�՚:t-s���2����D�su�Z��6����[�o!��N��պ�I�^�{;g��8����ul�O��H#��fy�����U,�d�p�7�IS���@�P�A}
�T��l��<}nb#�H`k��v[t��d����������S7�d;c�0���_���O�
s�߻�e4J�p���5�Ӻ�AK>6��VB].]�J�y�0�:�;�s�����y�F����<�S=x]�A�����o����=1w���֯���SS��F��RQ��?��6;�3\�i4����0̐ӆƿ	lD��޷�dG�C��ԏ|v\h�_|s�'Ď
���v�ት�N��!�m��ŠЛPT��A[�(��P��2����j�[0�m��x�-luxg#��d�Z���1q��/"��)��N��+(	�*�R5�!������r�O����/�C��76��a��H�/�l�g��>b���jm
�s��4Dhl�q�\+>�A8Fu��]�ԙ���<�3�^��x5�.����kX1������K��Z��O��`'2���:<ëk��B�ņdb��j��.��B5j�M|7YF��曣�So8���ִQ��km�O-����\�~[h	�x1��B�9p�%�=��K��L��s�2�B�$�6�L|W��ۻ�7��ষYgI�pv���	����J%�R�W�s�e�J��8�=�!��Ů̲��.3G�d�t؆y��o��u����$��L0���9�-�2��ʀ�=����#��i��<sn�	�� ̜��
����_���7�������������1�>�`k�4G���w2�jv3��ᶉ%y5�V�;0{f�̇�uK��xw��6�u�uOr� �X��jo?�@������ٰ�P!�L֥i�f�_�\�z���Z�~�HK�r�]��-v�ڃ����?�����^��Y
���K�߃�ޛ|D� �ou���.�~q��y�"��0{��{��y�r�$m鄃½��2��<�\g@�)K������ێfi=�nd�O�Zd�mg`6��f]{ߡ�ȃ
個����|R��z��2kn�����53�,��{��d��ޡ�
��ٛ�
����2m��N#�C�3:��ՠ���i��I@'�aޣY�
�ZRC��j,����#���&c�	��vP�ߍ��\�n���ra�?i"0ʩ-���ݐn�a���?)�l>��i�^��.Y�W�d�=�cMY�[�ٓ;;�z�1�F��p�g�d��X�2�8�vO!D��F��@|dS���L}��{�C�XG����J��-Rۃ�֕�c��WL/N�p��.��u���6���3"�!К��}&TLd�=�2�a���*p�xN*�Q�]���	����S�#������G����-5D�
����n���?��U_w|B�l�y�Py�3��g���|jV��r��:�e�cP�	����2_9?�O��ĝ���_�q��\�j���3���v2 �.Z¢���
}=pAw���zk��4�LwM3wH$����!��{
����{����DO�G`:��낪i��/+ �-����.f� � �M���C��k9ƒ[����4����5U�L��Yr�d�ny7{xSIa$ڦVŋ%��[ARJ�Ʊ�a���
��Y��f�d�F����#���5�eFto��>�u�Yɴj���Zv#��l��W�U��?���������{i1�"g�s{ʘ��N�n��@�f���7���w����i�AZG��q���7fK�Q�K�c��?_�|�lˉc��9�=nY�'��tɎ���w#�O�V���{��=��9he�g��*��������?��'�^t'���6�#��:�7 �G���O�Χ���r����N�pu����,u���r'�E2O��yP����t���{���L��>��œ������^i�F�,aT+�#
�Ş"w���9�Qֹֻ
ūvû�в�Z��P��F��T�nP��)8�+��,��	W�w
�@��i�!|���n[�Ś�oȱـH�쎌�E�#�ב��c����'
p��G�8�P4[3����f�d��������o��������?I6�������r�S�Z�-v�-�t�{��T���"TR7H�3���BR���`��l�~����#��=y#�(��\AڰA��۲KH��?�)M��s^��%�"��U�O�S��P5��t:76'ƎN�l�Ё6��{:3��Ǻ�E5��࿾$&A�3t1`�<[��(�K�y'��"F��� �5�oOT_��!�T�G@	���g����@們���'-�R�_m���Z�J*o�� �=fi���?�/��ml�J���uVo����'�QW]Cش!C��K�.��>�8��3+[L�b�s�ѝ!4�_�N�3��t�ZY�Z�鿶��b����}e�U�ٶ�N@����"��m�Q7���`ۧ|��;P�~�0�ɿi�Û����cKי�|�M����Q�KF�@�KCL��ʗ��Oߠ��&zZ#O	^�'��~�yMO���_S?Nk�䵝�F����\g�Z%�*F�ٵ����V�;ew�5sׁeF� 0W~���<�e�ͳ	�ȅ���'�Β9�{�X@;���2Դ��՗W���V�-^�O$��G���4�*��4¦QkN������]�K����O�������������|�J��1Q�0��p��:��D�튷9n;��24�� ��j��bŤU�SJ֝ӁB��qR���u���g����f�232MX*���ZW��.�:`� ��(���ߐ�_B�]�>���.?��?��?!R�$� �G����D�^.�	|��Gd��@��L]V�X�"���m�FE�<|J�pK�`x���������Z�����|u�?~׆�շ���a
c�]����o~q���/��u�����蚄�|�U��`��}ܼ<���r�G����K����U��i��Я��A�趮q�ù@��O�|���N ��ɳ)9ʜ阴{j���S<���„$WH	 �&�zf_6irO/H���\XOd��Ȝ@8�+��pɵR(`oÌ�	��e�O,W�J��P���u֬�=�����x�!e����Ȕ��S����<��pC8���'?	+���@+���nF�� BZҏ�/���緿�]��遼q�+oGт��=�ai
�������N��P@��J|ZPal"�|4k�����1���O���>�P�#H����6��
���U!��2�
�)[*E�)���g,�U�HM��a�Ci�<��N�(�fº`M݈(�&I�g"��s���r�����gY�™�!��� �]mJBgq�HL*N��
���H�!��Q�>����8�<9��@J8�9oI��SRݔJy���ĪG�� �����}\���~���?��Y��x��B>�5��j>���_�,������i�?��O���-��s8$��/�k�lI$%OGH[08�pQ�<�� |Z'�Q/>!��,������?`N�=$�Eb^�.f���&@Mc%�4L�ڄ�l�p��Tl�NGy�)W�M��0]�O���
������_0�������ڈ�p&EM��-0�U�[ɼwm)��,�Ɯm�|��h
�OyI^-�{G6��	�T��$=W�o����̨��Q>!:�Q/�S�4A�#�s��BM+��C�Oa���xr���~���4J���`O%̉G��S�`Ez�9m�@��\J��Gi�aT����=l5�DDX��
8��_w)�O9��La�ZJ�-U�o::$��Ag�/�� )i�dBi��r�?J���6\J�p ���\tF$6t���W�!�`���C�\@�'1-��N�c����s!VjI��3V~��ü�m5���x��&�-����.�Q ��@+�&�8��
�V%�?��?�j(1I�*1l�$2�F���7�
Nf�w�s��D���e����j���Hإe�5M�p�_ZyĎ��}x8
��R�ft�{5��\V;�g�C�=��$��o(��A���z|,.�Guv�k���
PZ�szMF�S�o�/s�"����]ܻ�#Y>qM�Ip
��|L9��sL�;0fu!\����x�t5���ϯ�Mm�W�/��׺�f��c�ن��b�����,�ubÌCS*ʯF�#�=\�o�F�q��¹O�?��������7R�ܓ�Wǜ��H��|�}�}�f�{~��+o�LK
�-�;v�o߼m=���as��w2�r��t��')C7�n�s�O�cs?�����{��v�G��a���zEm1������?����?��;k�#��͙N߾6�r�¡�+77n�<��ۇ?$�4��]~Z�a�,F�n��`@ݗ�gq%���Ŀ�R��ƽ�O`����k5���<]__�/��P}�yק��9	��T�p�F��{��^OR~��X��+X�{s`k�5��\����OI$�?v�:�8
c"�d+�9)�ٲe/�}M�Z5�L�Pq�k�wwD��q�	Rk
V��������-vŶ����!͇b�1c�g<��U�0s���@Ʈ�-��%��^xk�^������K��d��-�k�B��t��Դ�`ˆ,@X'���z��X�;��r�-���\�h^�9ߎfh-��7�O��i_�� �a��ը�����f���Cص�E�H]+��hG��F�?���}P{��y�����h�/2{� ��.[S��?n����s��R{�H���.
�:#
tc�
r�_�+���tן��IK���o�#�'��@�Qp�?���	����9J��I�����.��Ŀ�N�(��ʎ}񯶺r՗^�k:�.�/�I���Y�9��,�fa"��~�*�0��Ӎl��^�^���H��k�k���3���$xg�/9"O0���v�L^�gOg֕x�r"�_���W߱���_�c��ҝ�s��/���x�B��;���^}���^i|
O[�ɹ��}D˥W��>�4���n�A�xyj|�Z�:@�_��%��<�d�kʖ��p��C�_�49k���C�$�P���5���@�%n����bCv����w�[ZN�/ƈ+�
t;F��k�q\�"?�������_���N�f24�ݗ�Z;�E�|�W��ӟ�vW�c�q�ad�J�̧0���^�[Y�~�oS��
O̮�;��C�h�&t�]��>�ӄ�I��~���cw�����{��6�4n5�_>?���������)�7<ܾ`�������MU��>k�s�64 �^�z�:>�4dH%7~��xp�h�@���\)��&F�D{Q��[��v�1�H|
���ݚ�H�K����~}��|��ừ!n���Kn$x(B�Jҋ���WP��qF0�$��l���U�/s��=E�'�^����
$�
���d1�b���8��ߍh�N����b,�_ݚ���i;�HyV�HB��E�`���Յο�_�6)^쌾k��C�z���.�p0��j��Wt�3|]�0��Q���ߺ?I�ZL^���f��Ѥ�gS����$�m	�F���TIz�#h%�}G��F�-�	i��BJdOW,�LT�B�EY�V@���)ds����ݧ��,��*,�W��ƿP��,�%��U���%��7��߾�'Ű����m�P����	��v��Gr~�Vu
�s�)�W�u���?�����r�;u	���Xƪ���kB���9�<�;ԣw��~�j�Ò�5�U+�M��}{V�h���x��w�ByT�x���.o^�]�\��9�y(Gٴ�Hb^��S��~�N�yRH��+��sj7�Ym@�����OϨ���|��]PH�LiDj�ec�,�-����۔�����%��r�:�	�}!�W.u��f)�``�g���F=t�F�O1��W��ﱁ��c��/Mk�!ީ���%�(ez�������sWq�� ��n�E��P�L_�,�g��P4� c^���n������u�����L�<�r�i�����
���|�:V�*�:�]�{�)-�Ko�z�-�?���������,o�d���W�[��7fP4��䋙Yf�@7�d��S�i��*��z�/����Al��3��W����V�V�ԯ�]��^{4nC�wl��ޘ�#P�]�z3o��z���������z�`/�^)��WC�t���ik��/�Ƈ�t�p�e�m��d��30D�}��B}t�_'��5L�㲁���O�rgCH�����T��ߦ����u�43�oQ3�3�[�ۿl�{2�2�d��$>[��v���e�~03�'T�@�>k���w뱵R<p�;���Hw:}�E5��o�I��0
vUŭݿe��d�l �>;��
�oK����ֽK}�MMC��l�9��}�@s��j
o��=69�3��{��;>�(��Јي#Y�34Sa�X�������#���
w�M@X�Z���|���=F�B�����D;�{Ԙ�&�Y�
��4��
s���K�� �Qk[���T�����Os���+�gt�B���sR�[�c�l0֯�����Ob���"\�ҭ�ϊ%���?�lf�E&�tC����&"�1][@�����#��d�ſ����H�^H�l�72�DO��������|�N��]A��W����0�+�۳��ſ�Jk��%�+t�v�0�%3���pi�%���"�ܻ�QN�i�*J�	�h�:&9nY݋Yk�D�+�&�?��:�-(͂��8K��,�F�p~�-z��_f�:�G�4�L���v��]ک�a��m�Rο��2�W�J�O�|riκ��$P�5���k��V��Y��o}���T�ӺQ-�ڧkM�����͗^UJ�oH�xv�e�;����mv�P%M��y�X]��Q�&��M��c�`�^SH�q�)������	�E��&�a�.��?����׈��R��l�t�͋���u��4���ϷvS�L�e藾ҙ3|\n�]���	�3�޵��g
�������}�O��)�y�kf����8�gC����2���B��oOy���������S�/��tO���P�B[A�tl��k2�l-�
x��9ߚ�Y��,�$�s���4��&�"�����S�wT���$�[6-��1��3Rw���z����nD��;�u耸����[)�h���϶�:B�t���R���	�9�s�����P����3�sX1
f���)WS��cWs0���S��a+���a����VӀm�ڙ�ԍ���W�"\hf��>u���}z�W�̍I�+��DGJ�jZ��=�V�^�_�zZ�W�T�`�7�'�7*�HC�5�~?��&�o�&�)tտFb_���u
�I���[�V�է����:G:�c�6��b��7��r�~�W+���~��}�����m��jH3�_���N�Z��?���6֌r�w2�e��L׫�ѿ���~g���p���B��68ԡU2��_� E�[�&��R�dž�c��y�D�����!�{�g���~qո�t��_l_�;$]�/�|cO��ƿa�;������ޡ�Mb����FAPqA�Q��w�󾮼3���M�H׉�|:K��=��w�1���_t�K�ޙ��o��4h�W�L��_�;�R����j����ҏ�+��kf�{
��+����Z_��j�v�N��2m�)���ty�ॢ��K��@��k��û�^M��k'�"�nlxE��	H��h�@����#n��?i�k;�ns�B���g���%�Ʌ��L�T�+ f;�H�b"��=wuasAx�)�]�^-������������u����+/N>b3)�9��.n�M�&+~IPݝ<�z�뉄���Z�p�6����E�9s��h�j/��xL�.c�k�`��|oӔ��r�O]ƷP����:��Ӵ�gMu[ַ��?��������D��J�1�S�)d�S�EcT�RH�v�E�T�C���V�s�Ug
����Շ��)��Rǻ��@撚�}��=.|�)�d7N�7�8��Q��.l�$�q�^�-+�1J���#�o^3��ׂK�U���=E0�2���il�V0��DR���`�K%����]o�a��L����3^'��R
�yr�=�[�]LZ~N�4ߚ�����]̵����ZIV�"]H$U���@�K��gz�(��c8G��"��
ť'����vE��趙_��OL_���㊈�l�JB�0�K�´�7�+s��gH��q���?��ʆi�q&�y����FrwL=I��=�R&8�>����T8�׏$)e��@j�%��<���	�ciwv�uoG[pn�l�+�gƞ?���X�H��/h4�ӱx��	*����<H$ܜ4rf��g`O�+W��+��f���yş�Pp��_�+�}W��#�	��/Z�A�ߊ2��?+gQ�|�b�����?4j}V��=�%�uS[:��w18읁��l��>��è΀��;����G��iA����z�r^=���LQ	y*�{hh�C��4�(�.8��9Bfr�WQ�U�$���Ȑ4)
hQ���w��Y׶���ۗ�gf{U�ar���?�N�cgQ-ٷ������a����?�m��[���8�������L�����W�n4��ԅ��y�OKx}y�@�������Ea}M���[V�o;Z�n�k��2w�qV�	������?��5�K�˂���2K�F�2腦�cX�F��r�+5�_�̲�K��4���z�m_�Y��C�3�����	r�R#
�?��boA���k0ˬ�cb^Y�� �幻�#�z�k���Wn/�?�����������ĢW�-u�mV�)y�}���ӲŎ9�:^��)ո�����^����?�M���i�)�@>\�ރT�4�;�uƚ����nL�^S��yka��{y�昢��Ŷ%��c�����2|�î�T:�K�b�"�\w��ၐ�b�:5��%�?=K\��M�8�>��f;��K�0}6��t��s�BExpo5�0)�*
��9
����FW���I$5-̄6B�;�E����vG��6��ݟwh�¼�^��aQ.ܱᗁ��Zt��:�+S*���-�%ar&s�Zha��li	I�7�@�?ۇ�?�b�
�)WM<��v®NaO?��N�q�CL�F�@�~�E� ��=���G��U`K�4��؆^mC::P���s�J3���X����:޿^�����s����~�����J�x�픰΀�6C�D�6��09SG��1~���0�^}`������\�S��41��:)��o�K9~����_d�@Ц8�?���i���HQ1n�Ŀ�A���󥺧��K<\�EE��H�[��iV��m�B��n7���+ϒV�9j�y��)f��mS��#Hc��|�q)y�J�;����9�J�(:���L�n���^0�5�u���
z�߅�� rf�oVw���I5x	��|��v��3+�}w�l��$�FŪ��,��;����Kb��d��kM�E�O�q�&��{U�wv��O�����A���j���W�U9�fL��{�<rv6��U�M&x70·}0�Wɘ.�h����پ:�~?�H�	u.�e�<r�y�iB��׃����?��5��J3���XyM|�<��=	�>]�s3���4^$$�>�^��I#��4���{eGjd��A�����d���2�\��Pi���s��^?��V���A�l_�K	w�ޥ���?����?��F	��C�5{�m���P���V<[�.W	L;�ש��+5v2d���oC��Xy[P7u������٪X�;C�m����i֋a���r
7�]|�u-7�Fލ�|vT�}ⱥ/�[�d,�|麐�jUVӻ�Ҍ)��,Hy$G�c�#���B��q�B8���*5���W)V�ꚤ���3��wE��&��H^m�b5u���>��?ſ��h�I��.��-
�nPykeC��X+��	zE�
�W�l��gwj��`>��|h�
�ɘ/�M31L��fp�g�ii��Ɠ��	�wh�"���1�??s���[�t|��|v�x��'���F�����q	]���f�p�%�
j� ��6����E�d�i%�bV-��n�t�`�_K'�z�Pz�"Qh���v��=)��^]�t�`M|"G���t})6���y� ��W0j�B�Loj�J3�ŠK/��O��ƙI����I	 \[�"w%3 �T�E����:���&H�zZ���J��±�r���9����F9�E�������y����4L�>ߖ�R��4W?x<�YM��|�p�$[:�Xֿ
���i&�4
�Cp��?�֝��@�|,)��iޟ����X<�B�OU���sn߲�A;�S�z�'��t�%ϝ�_�]3��{L"9��Wg3�W�4~iΙK>��p��5�i���� ȬQL�(����s�9<��\���p.�����7�޴�Iţl�`ʹx�V�Y��(BG�떰�LQO�^$��"������'�@�m"UW��pb�1�*���Nц��D
���%ឫɊ@%a�L���:����ֺ"���]�Ҏ�*��Rz7�ϩ���+���6c��f߰�(@��$��#�T���uWy��������:�˱��90	�r�����V�j?�;�&+��y�~�s��Pr�����q���:���Ҋp�caGH��6���=JnK�>�R�s�me��
��t����� 2'��?$ܞti��B�\�^������P�D������k��K~�H�����L?��0�<h�����BG7.\
�>�G(����>��������=�����W�i_�K��,p�e�n��sV��� v��5����:����9w��9�nF���ߝ����7�$��2|��ohn���i��@���J��/Rj]��F�+�V%�sG���O��E�S$����@/"\!~���w��h,�o�
!�6�����E����pŦH��sO8��,~�,�'V'Ad���^xH=wr+�I�W�o�>��	��4�-H��"(Y%��Q�
u��tt�ܩ���R�
�#�������{0���?�!���vOQSN?2h�'Z�g���O����d������l�����O��U�9���i�0<Y��Ơ���_!F��JV)	�E�rΝ���H����~��H8��!Nt+�̙�˯��O��8# ۽�Ƕ�*wL垷��;/J3a+8����xo���O�	�� ���V�˟�b��|�F8��A>�$-�}.i��S���E�S�gҧ
$����޾�Ҍ�$�$��Omb,�"���0�Ns�U�7��-w�/Hn�p3=?�����?�=�������`���?������`�Fs&�?�ڥP�T	
��@���/Ax�/�O���s���H���S�<�����������P�!Fd��Z�i-4cJ�P�����M���������󹒣�?y��<ǝ��d ����S�B�QBy�G<��0)��4%�J(�'�?�	��(���KR��(��e�S�/���G�$���hФ�͍���3姿PδiR��t�1o֊&�k!M�C�[��p��˸z��6,�s���!�>Pe�Ss_~����o��.�dyK8L[`�E�1Y�P��e����??@�YG��B�a�q����C�!I|*U�^������ReGd�"�����|�����4�
g���Rr@��N*�o�2��\�	$>�xצ�Lɺ@kCT�>o��3��g���)H@�=?����'?bXWo��s�@(�呄ӎ�d�1�U$,H�aDw;�%��|�g:�Od�� ����V���U1���@���	��O`�+�JQ���A����>�)F�11�����e�K���3�C���c	懩�.���o�"�7��Y#�ieAK�IB�w���7^jr�6��N��V���7�)>��/d�iUM
�w�W��^jڞ�0+�$��ʗ�p��jBeD�bl��{�*�:������nN��O�R�*ju3�S�:<�cF�Z���w�o��Ch���N�1j��.��N�ۓ+����'�Sܻ"]Ϋ�+��Hޣ]^4�oF�]��L�:^��Wd�U��mL�m}�?W����?o��\G���?����׈���_�P��HC�cN�?�
��Q�&pU�'�a&��OH_�@�fҰt4�]�gy��s�N��4�u���������J����D��:�hg�?��%�7Q�-k#�c�5��Z�#W>X����?�����ǿ�����)I�j������8
ԁ=�<����5�z=5�sgae�m�߭�+�nJ���U�]��suz�:����?Qr���U�uo�uL��k	�QI�?59�3޷�R�\�{8v���\*�
`!G�E5�!q5�����7�	�,ج��4e����N����O_�H��e�m�.��e!�6��u#b/@;��)Ju��=W���־n��)�P�P4�F���doW�*_��ˈW�^�м2+��-�r�~�Pe�z?��.��أ��1�aa?E��N���Zz!3����l���I�����Y�6�x��̛�Ǵ�L�)�����$��������'� ��+��^�f�~Sڷg�q���S�1�#6(|���9g�Ð	)'�0��j�|��S>�"���G؞ȑ�x��B���oaoj?G��l���ϝk�̀�������+�ڽ���W~���R;P��c�b�bm)�S/?~Ҕ"�n(I��I�����������?f�Ҍ~`#����="��O���������3�0`�����)��}�L�Z�;�֎��]�9�A��1_�)�#`����J���ʬF�w���v�0�����+�ד�|p�x��������5�Wt�����!�����""�.��9SM����q6D䜃���)�q�$�gYj�'f 'ؤYn��%[��'�<Q�j����9W�{f���3�;	i�H����.>��ŞyJ�S��)�J��ٰ4��F�)v�6]ג�d�+��m@���dNྮy�ē��y��pI��ճ#��g?�Hl��߾1(￶񺆣A��y����xi�^r�pk�r:A�;ch�ݵN��@��W������������?��*�zOE&_��q��z���H�[��jK<way5�@��.r`8<�^#��|(L�!w����b�}]S�W��,�1��%�~����*�sv�l;�J�S�x�ز�
�ϩY�6R�����?��k�Z]C�O�R�O��<ۦ��$��.ɼw*�o��]��z_�y�
6�Af"=
����.���.LĿh70�m]���"��ר]!m��o�\e��C��W$�ke��mvh|��Y��W�W����]�����|ć>v������'��I�"Q!si4$�z��JK��(�ݢ�l�ҝ ���o����)�����-��$ʉ�8��>,`p��<I�����H' ����F����,TdY��d�S�+���v}�N۹�~T*��˃���?���k��ٰ`S?����l��"?���(�5��̜��O��JA0u�l�q�{��j���y�{�O+����3 �K<�ϟ�`?�P$��� ��3Y��-4�A�%�'*^�Ԣ��/���N�Dڈ�i�p�;�ъ��T�r���!������<Hb�*���z%��y�Bh��lr��*��k��^�S�j�֑:��Q�1U��찫��k�'�#\�S�����ˇ.�	��E�9\e��ڔ�E�'mD%����y!�?�G/�g8��+�1�93C�������+��"F
퟉��T���(e�/�������k�������z{�������gM��xMpaYx��*B��;|�X*�9�>���O'W#���ou���UC?{����=����%t��|,9ہ���](�{�x�=�Qm֙�t����g��@ew��$�-(�ߒFfJ��5��\��9?�,�(���gyֹ���}���h�:˄I�u}�����ҽ;�+W
�+���م�n��Ʀ+����-sDU�GIM��+X�1
��x���E��%�1]�sݮ7��7��>C�I�
�K�L���4�o�j4܃����?��5�ߪ��.LC���z#�O��|?�5�-�)���Ss���qu��_��_��}z���pCb�#��C�NO�x�ѡ�\|�H0�E�e`+�� ����k���.���3�I����'Nh,1�FBNB��?������_���Wz}��Ko	�D�G-��+��bե���l�	�I�t�0H��D�J�^T�֣��+���mT\|�Qi�4��~�XC�Gl{�������\�Q^����9
lլ��Ž���ɒ��=���oh,%��v�4Ql���u�'��	��~J`6lZ�-��`n���-.�̶bo��/T=�x7D��k�By�H� �����W���j��mK���9u'� ������&����ΑsO��"��DHů��u���Mr�]������%Na�m�ɭ��?�٫��r���ɀK�%p�
u�Ӧ�J�<�KVJ~�BE���ی��3��~��P�
�[ԳQM����q��]��i�K�l��
���/3X��ЎHb $�uKd�T��W��yX����?����Q������p�.@_�ܮ!��6�����ņ�G:]�7��7�;!I���Ŀ{(��<T��禁�[��G���h6V��7O�U��O3�hG�X*,6�-!���x8,�Ƣ�
�D&�� !�5B�K�!���;:�eUp&C6י�B�1̈�F��?��7�z\�u�I�[�)���~�A�����~���8or� �Gak&�ԥS��o�a�&���>�H�Jg�\�j�ϋ��򻽵�*=�@�8��b�ti<��f��Q��]SY���ɣw��:�w�v�²�	Y���Jb3y5��+�d@��VΘ�۾z�>�\M��Lى�Ϻ^�,�k+�����gק^�*3�|��9�.ݓ��ɟ�Jr��5^X/�_ӵ�
������I�5�,��m�&�����1ݶ����1J@�&P]kP(��[͠��zӽxs���w1lՠ�B��1�$�Ꮽ����������ga��U�?��冀��1#$]k:���ޣ��t�,j>�2�t���%9,٦��;dL����6��1��Xs�_wOJ]�Y���3�k�IQ3���掣��-&{u2����$`�9o�#�҃����?�����Q�oy���`80�:��)�����n��dɿN�/2lCkC4�vA��
ڥ�Y�L&�\��R��$ϡ��&wWO���y�=e�H�)�o��CדּzGd6�6��Y���&�ul;��A�>��A��6�XW���ƈU�!��#�J�	��zF���uGp?����7�r�?�(0��ƹr�mo�#:7���ޤG�$$0�2*nb���a�3�k���<� |����/��~�j�)+l"���۴u�P��n��f]������-7�2F�����
�_	9�a�$�LӨ�r5�C�k1K-�\�K��y����<�7;�-��p�T�mĔ��R�vb�s�L! 0S�׳�X�RU���o���ň�'+��#�/�}/�y(eT�7���6\+Ӭ���~��1WP�v�U�%�&�27����V��0�4`�x:��+�i���O*�-�M�]��}f����M���W��U����)6�-HA��F�T��Y������W�_��vϠ6D����ic�M�k�5Z��]7�"�7�������Y"�zǕ��A�ܓF�i�i^�F�Z���8t��:�r�-0�W��׍�ĭ����zg]|>lG�6��dHJ��`(�y�ܴ�ٖZ\]�Y O�!�C{rf��x�>{�ƿ9���d�B�W_��>��Ѷ�^�����:��ޭ����Z+Mք/��L�U��|�5�۝�ن����B���ξL�����TviU��		���r�"���k?�Vv�W�O
_db�"�]&Ko6�m�� $+'�vi��jm0maV�F��i.�H�)��J�����?��5���)�i��+�|F��M[[�)�t=�^c9��>�ŭ4����5����)����֫��JGj&�S�!��Z_���R7�4���sؤw6�}(�o�~��'��7Ȝ��G�A{��3�����������?������
6ﶠ00?}˨>\WV�Xq�M��+��y���_�M�͡��)�u�6��ka7KN��lťW�}�1��DHĚxnF��ַǹ�{��&�	���8ӕ6�%��660H��rf�T}c��z�:�Ѵwf��镠��ds��VG�N�M��_�Q}�?݃��gY:t�G�T�f7��n�u�U$;���>��&+�hD��B��������h�$������޹�볊����='?�l���}������g����,�G'��+����I�E*�/�����0�#�P�o?X,�k)9��W
��$�>�>����Q<j��6��g7<=��5c�3�͕1�tvۣG��[7�+=�Q��}�>FC�C�*ڟh6j��B��+��ҏ�zԹ�n����(^�,[l�����B��_�L܎���l��6��Hȱ�n�K�E2��_����R�ƿ�s.���$�RH!�Z��:	��S�_�3�	|36$���7�ֵ�W�|�л�0�W��r��ϰH���_!@��7�v2\Af��I<����F�S
9_d������>OK��N���LcK&1r�J��7���)zy��
�w{�r���Td�"WH�]�"�!��PI��u39�vF�j��_	f��2-��I�<������C�s ����c��o�Y�{N"��t-��a���9c_zuᝓs�.a�)���vQ�~J"���_���2�4�е�w�ܻ���������:��0�n�+gEmxtܤYr�ά�4`�.�+�7�����2th�Rө�V�qV�m|��5�8@�Ĝ n�jnbZ2eH���6@�L��aU�٫�՚q�1�KiĢɊ�K�]Q�{Տ�ܠv�+ȉ��������F��3>$����~iޮ+zK>-�MOؘ��wu����ZT��7ph�P�uvݏ�Z�R�v)���_�g^d|�}�+O��nHK&��'#���n#�/���k��÷m�9r���{���������s��^
�?e�G|�w�.A�+ꥊv�C�|s0���8���lZ
�i�)%���v�i��?t�Kd[�o�]��*��n�y��7�-w�A�)a�O�j�� �+�.�m�	����k��
�^?��	�)�ږmt�-h��!�@�.��3�
�R��70���_�P���b@zY�J�ը�1�� �/�;W^���A�[�џ������E�u��	_>_�����h�jl!GG"���E�9H�R�.� �bH��o~����_�6��TM#i���*� 4�+�K���c?�|+2ʲ���@lz�jq�Q��&�:-�ja�S(�����?�c-C	��n��}���FJ��(��%l��|ht+�h8F�bD�,�!�;v,-။l_�Uԓ�ʈp����ӒGJ8�W(?�2x����?�9��r�i[Kע�=T&�(���U*_��)�ŭ�n�+��M�˦I3�G���`d������!�x���W�_��.NM�A�j��ܯ�*�ؗ�t��<4h��Y�������l`��T��/x솑�*r	�=�=���Fl�t�f�_)`1
3�a����(�YB����a1<ң���Ս;�1���7`؏�7�;��U�ܻ��F�pw����
'��; ������3ҝ��c-��S6N�����{�w�
e�.6F��!���hɟ�KW�_����sJ&�jy����ݩ�ϼܻB6,�fp�D���i:7A��Q�x�z�KkΗ�Ө�^|PZ�j���0@�����6�ϋ��E����v��^�Y������5镵�I0H#��5==4�fP�������������җv����K������{`FJ�q"�^7��|��
0�b��~�s8i1�˛�lU6H���������a7�^�X��L3_ܐH��}�@�^}P��o7
p��B��F�G�=�s����?����?�Ҩ��aJ���^���K�<[��웘�#����	���&��s|��qe�Ɖn�236^�M��Y�@�W�ya,�\��϶�:-.���d�o�6�e�Ry*��/�!`��#�(�Rc��(���_s2}�V~B��bSR��U�2�qqn�#�$��^�8�@r{��q�#Ο��8�ȣ��k1��mJ�@�]��K�����.]�e��/�)m���Φ�]@��X"`��!}�7�1��L[d�p���fh����c�W66�$q���,�U��N^�	�ſf��f觕'nb�F�e�J�k���f�լ�ͧE"`��N-l�
���s�1i�P�O���e�G����M�e�o��ؓ����l���?��O�r�9D���n���l8al��GE{֛�o�b�"H�7>��C�m�M��g��E�æ9��$�Fu	�S���4z��@��Ѳ
�G7Q��ؿv��O��D�;ɏ�����Fn�`l�'�_�v�����5���<z����?��)	����-��^�������fh������+-����,1�L/�z@�4Ś��L�dKK�7q~]R�C����+�(?��8��f�T\ԙRZ�V���b[�K�|��ְ�
Dx�*�wVOs�E3�����8B��tOg���E�\��3����?�Wfn�Q;�`�5�ҋu.��i�.��+.�i�5�-�7E�w�<1���'��
��h=S�� ���d:%E�j�X3w
}��
k�����3fȮ��^�ͽ��4���*w���|��]žD� ^��Y���Ӕ��gx�d2�_�U��06M��@b&�Tv��jA�a��Q?��$�H �RPu�7�:d'S��4bԘ��V��F ��1��`j�l�{]ދjbZ������?��5⟟VAN�YZ_w���F�H�I�t��y������(:��1�{�5@2���:����d)z��[]~�18�F	pOIv1���o��:��WQ�'�ܐ!
���>��}EM)M�S�	�^�
��U�ƅ7+j�o{vo;L�;]�D�^��^��C�� ��_�����?�����w(�+/u*k	q����h�-ص�o����<́�́��o?~G1�n��7��yD����O?�,�i��(����V�WX���r���EBq�L�� �Cy3g�{A�)OH�Eذ��Ee�7�p=���^H�����
�[�l�&���?�K��e#�R�|�׉���I#� F2��)v�ɢ)���
$��� �-����LLJ���^A4��KzZJ�'��ɔ�l��A$�.�S��!%�J!� �#1�sI�=ѫ�g�"�OW�	S�I��ͳ��Ou:Jå���)$���;��,��5Ν��H�_	�U*�D���D�L��JU<{�+h�~�*�u
��+4w�h�6if�H~@`lO��?�^Cܢ'�+߱�!,�i����:F�mr���`%-�t_\�}��b�-X;>�Co
j�@?ʝ��-�Or ���d��/
����1/J^��+��1�W�3�	'&� R����c�޼�p�a/�����C�'�I��Pe�>�i�3
���Y�!� #Q�hz>���ĤjA(�f����c��e�>Q�l"2	0‘7�o$����=GƼH��#x��
�iS�Ү�HO)���)xev(���(K�?���P�Lr�cI�y��h��'��O�p9Ɍ¾��|@�"��J�9�Gő���j�}��y�3�Sp	�v*B_�JرL&[*ЛJ��0�?Uɼ�~��_��?�C	PGGy�;��"r���$��������k��V�3�'AI(	���<�hj���yҤ��Q��	+�{"�s�#
�y"i_���<�x;���xa�����1i�J���n�D���{����N��价NT��aX`�h䤋>:l"����;)��P��=�	7���?��F��E�F ���\J�p>��Q�e;Z#�������%(H���R]!�G���;�T.$iq�{v1��h��
��\�=@���V�O�ɂ@҉�np���~\�q'�4fB���"��Q�b>�~hn�a��Ѻ�\>8�x"�Na)�G8����ꠏ��c.�!�����"�5�
�{|=�sYj�ln�mrc�D��E��-0�9�*��B�M3�׻�d���,ᮞO�HW����b�j/�}��La��>���3��=-��U��^�����{�J9�_�ƀ�uUa帻�)s�[�۠׽�Bz�ʱ���g��o����t�iI������?���?����*A����fS��׾��(�5�h�M0���9��ܐW���L�1�Zu�.]�=�2����6�K�p,��fN��s����~�]����ej�$�45��A�0��J�Ҳ�u�Ny�?���������c��/�&�����\���@/�$�4�)gQ3���ާQ��%�T����/�m�s�z�5�x�E�[���1�mMA~=�&�6�;�XOϺ��ml9R���H�0��4�Ɠ'��a
�<H�~$|V!C���H\v�$���C���@SHZ���t�A[��'�-I���>���I�.5u��W�R��f�db�].�o#[���v�dJB�x�1^%?#$�օ'�7f�W����O�6�况�E�^ Z���Y�~�Ői�,���qJ֝��!�m`M��jqe>r��/H�ጓr��ĉ�Ŀ����-�T���nf� 
���-P��WNjۈ�+�iǶ�v��H=�J�zi\��n&��j�Ꞃ�]썯y�颕�9�#�[yڗ*7��5%ph�ϫo�i���_���0�0�_7m$�x����s��Qp��~�,���O>8��O��I��1aN�U"����⧣C����Ƶq�
��#"vm���o��L����K��*Ϧ�vE����dv���(�����C�]m��ϝ�^���&k�fOK�Č��9�a�>4G��y�me��ǝ�X���2XR�
��Ҟ4�^j��v�f����ݎ6��~���zZ޹M�,�r/���mQl�?�^�Ҭ�oi
]��ů����|�����)|��t���|�ٿX�c0l�M3��`.��\�i��<e�+COw���+SƮ��m��׭�0߬`����7�z�ޤ��RuV��Y��_96<�HI�B�W�3m��m4�ʔْ�l՟��?����׈�N?+�;A�ީw�����pZ��@m$2��в�|�۶��Y4�L�R����j�TvF�׎Z�=�ܦ�K�a'�$��rF�/�l?w�?���眍��Ɉj~Z�i��	w��H��^c݇|������?������x2���i����|u�Î��	�Yd�����7�۽v� A޼
�`i>���wc|�E6�mV�3�:�q��+z�,��G�NH0��Y3^�8˜��
q��.K�ix^�[���G�f��Ԙ&u���Y|L�9u!y���Fg~�����J*JN-xVD�!�V��}�6`F5AW���n]�j2�k஑�o Y�DW�Z����NV�v���>c�����l�-N�!��-w՞���OU7���U~%~|1�r^�R5K���>{�~�Ui�t�k�-prK�G$��QZ����‰��$��()�5�)�ƥ�{�-�[mBƺ?�Y#��O�4��.z�]�u�韄Di�E%(ȏ�_s'���O[���ю ّQ����&x"��_��̄SVH���/YZ
9$`���d���D2H��\ao~���&����9�W��O�O���T�u:s����c}��0�[D�Z�_�O�(�>�)��d�0�W+:P��	���6��G�ϖs㿍�]�'.w������n��{t3�G��LGFg><�!��p�?�����A~̴;0�{"Bv�ƥ��
\��!p�?T�H�9�q���:b^��$��ܕ!��$�[���m�g�z"SD�n#�ۜ��m�^Ӌ�W�D��3%D���f�b�ȉ�9�^M��� ��*��m�o�~&M��B��_W�-]7�î����M���"z�wu
]����I|u`�-|w��h���`d�t'��LC�"��i�n�9�r��5��S@�1@�{�����PN���P3�PC�<�B$E�D}=01R�Y���gǐ�hl�w��?��������w�jp��WN?7xFG�u�Z�!��ܾ�����vd��,���d���)j����r�վ�:�s�wC2����F�nb��ql�%�n}o
=�tfns��8��h`8�ɔ#��?������������ϊ�g-��VD�P�Ko>w��T��)�	.��K
��u֨�\[���1����r�kܷ�y+أ��tj�#̋�۳�&#����n᠁Kk�P�@�(-�]���@��$��L=�dW�g	$Rm*ۊ2�[�a�y$���M~��	ͮ|toH��k����s��l4��i��ҧ�D�������[x���"�C��|c���*�H)@;b@SBo& �1#9�w8*oi#����?��O�]��i�f�[J�_��H���ۍ՗���ݍ�v�����m3*�g"�9ݐ��?*���ILyzўl$���a��.l�k'c�3\ŝ�Z)�5Ť`=h�ۖ��qkSK]E�V4��;G��%�����K�?*�K�uG-���r[&<���ꯌ]=A~M4�W��b:����l�<^9�Z5�Cu��9�Ԫ��G0v!/��~���r돴�;"��)'�����a��,�����NA� ����v1�7�R�WJ��JIE���n�/�G6���#�/_(
OĿ���D��O�Nǿfg��T�$�weT�M���/ �e��O���Q�6>$�[[H�G���O�9ɷc����%�%��O[��{���P`uz���̡�����Y��NW��~K���K�]��I���^��7��;�U�k~̦�jCf�*f�0�Uu��މ�M��do���%�9�=��n�)ȍR�,���{�4�(�ȿ�rM01w�{},������_+j$Z_�#�?�Z��)"�.b����$�ڽ�8��Y��8ex����1��7���%���S��j���-M:u���3��m�7O�_�
��k�wJުUP�����2���ѐ�H�E�.&p9t��d��?����׈��h��ʤ��w�X�f��Ə0{g/���/fZ$]>C�W�i��S�hN̿2��Ze>9p�U�O?�άn�}ed���Mi��&s�L���:�����_Xw��o���4���M��������?�����ǔ�	�<|
3��&6 �fg^}c,g�Hԫu�A�\�l�:��w���������sz��/?}�QA��tc�>q�2d6�GD�Y�0��[_wM`C�Y��ڝ]g��(f�Bjע��oM�.@�W���s�A�
��y=�
z)�=�nx��݀�J�ۿ�Z��L���@�9R9`v�|W���bU�ݍ�v�gӼ��#v̤ҜhW�"��o���$u��ds�s���@7�=$k���iG���2�������y(�b�������ߨ�"N�.��%�a��%�-�k�Nqq�;�uh�fs"{%��f'Cђg���OK%R�U3ft�VT�9�ſ�S�m�[kd
����q^$�����dj]�m)D
�����al�jS����C9vW��~-6]}n��S�Vs�F����H K�D��s����~�_ޙ�|m�9���-�A�߇G�װ+�ƿ�4��?����/�j$�LQ�+�Uxz�Ê{p��$�8až��|S
�ve+�@��_�<~NT����ﵟ^[�)!'E���Zɏ0�O�#�1���;�������㫡�CƷ_s�3�i��^�	WXڮ$��=9 ���b�y�W.���sO/��*��4��t�"��Z�U�d�Z�Kw�Q�p��E��w]|�P��]��Qu�Q��q���+/i�r
�q㪠��HML3ζ��b����ar�K��w�|]2�4H2lO���Гc���(/�,D�H�}�ַ+^�:17�N,�&�><��f?uv�/��i�N�����������7���~1�V�i���V������W��.!�B���cn&�8_�y��߿"m��W���/���‚UD{��:j:����VCb�����x׾�����3p�B^��~U�۴���������?�����ܣ
CP�,Xg�����Yj
�,�;��}��<�D��+#
��<����͚�"��g�E�[�sص�nz�o"�_�5�GQ�n֎n���7���	{�A�k�HvM��)?�@p�,�4�J�3�n��Vf��}Y�
-"����W�=]E1��-��ܥ���n�8T��2N��wأ'�]�q��}J&����[VZY�mB�>
�� �i��@�/E�֨C�b4VDžA�F�M>����4�0P,�����A���^e�_�S4]�!{��|vb��7�[�KE�v�U�xo�S��'�m��q|v�g��m�Ƙ�nD�8ޢɬ�f,j�̊;
s3��v

�XZy�!���k�&B�{�R�[�:!Ұ�$C93�j@Ws�>?���q��8�̞X��kĿ�G��'c~�D�Od��;r�a�FT�Fg�˒��9����ouj���1t�7�Z�[�*�-��V��������ţ5�i�;�ш����$�fi0�.E~k��Z��N��Lq��"�>8�eiC����h;ǵ+����S ȧ�{�Bv�y��!���&kk-L�S���s�n��$wv����m��7Z�{Dvki_����l��
v�� Ϥ-u/M'���ھ��_#�}�o�Wo�0o>�N���s���=w����u�5ֆ.u?=[-�#�u�'1�(��r�����;����~���ֽ�10U����M��필��t�
7p��;U���}\ۼ�|��wM6�9����@��m2�;>
��i��M���*�ݟ;}���%�$%�<�������_#���;������">t��'��{�����:��\y��sQ!��]�f,�uÊ��j~ڂ�.��;���Z�:$[#���>�J+��*dn�O-�n}MiLW�F�.oe_~%yA��?������_�O3'pOm�f���j�Mo�;��a΀�$i�W7};��0��>�[kK�Ž
��b��
j��Ի��8�+ji6=¢ޒ�
����>�"�]�&��yv[ߓ�k�g��Y�|�
Z�:NFBcm%i�Rc�+��\C���8�5��o�(��we�|��.f�ע���$���ܭ}a0���������<	���F������-I�q�?���t#M-�g�T�#E8����N ����Gf�1�q�;��C��Nt����2��#�T=��:)S�����N!,*���AB��z�h��{�I��
 �@�M\T��+�N�-��]d�"?/�i���՜��6Ơ��?����y6�ݘ���Y/��N�0��;�/�
�ܹZS�@ځ;��"�!�PH�N�ŻýC�@�5�g>�iFK��_�@����h	�--rB$X)I_[�~��L����X�(�4���?a�c�-���#8M9��7��=��A9�O���ܥ��y%��^�\x�7p�_�.��v��j��^@�vdx��c�h�c�������hOZ��F���j-��;I~G�wJ`�{z��˜�w�F��.`w���%X�Z��p�2e7�&��.�/FCed����
~�*��V[�?ܳw@rR�/*�ѥAԍM�9�l����$�o�u;7@�u���0�u�'P,�}�]c�q[%��g;l�#2��|���0�+dK��qu����p��d�Dz�2;���٠�C�u|����_ݍ�K�D'�lSi�	����|V|v������?���_Z��w�>)=1�`�p�g烸<�~a�˘�Z�H�l�0����T�Y��� Y�G�q����Q��#�K%b��.���&�I�H��=~zyO�_�^������ ��	u��_���������?������`26�(Xa�:+[G�[��j�55}�6�U��.Of�p�#���s��V�M�F
b�%�C�Q�u|
x�lXl��+ݽ1���&5��j�(�{��37��w��;��&����$L26��R����O���:�~i�͓��d��v�u5b�i�f��m4��h�9V��8J-
l8
�L���W��Q�P+�Z63o�ŵ�#�/k����G� �b?���swc�F�8Qw�<2Z��͊> bK��)�h3��%�td�'��?-%�yoZ-16��л�k�6[k���Fte���l���aq� ſ��ע��,�#�(��S��pd��6V[�.��s�o�\}ku/F=�^��w�xfw�s�6���h�m~�?�F}��mb8/���̷���=.d�	u��TY��6��O@�;���e�w�qg�u��ʄ)���J�2J]
M4)������_�J��dwB.�Gq��*z��3F[G����GW�[!���("Hrc~vG���w&�M��j����\��l�&�RŖ��5�:�~�*i�n�?�UB��8�j�i�Y�;:�_�X�����	�G��
�F}��c��z#�y�]6��7�糦�:�;��:C�䩲�W;,<k�ں���&�Cڂ�۫�.yB��ɕT���
kڳG��㻸~��;C���ι��B���Ј��1�O��IB�v��W�sm�7/�	uV��wm􍾕wZ=0u2a���'���{�_�S��>��@�o?�&?H%�&˕0M�a��"�D[{����AZ��_(L�ǻ�[�ʫs����0�c���'�d*.z��x[�~\����T����[Ъq7Or���A�E��$,�I���6T�@;k�"6���?���k�?���_��Τ�\�W��8�v���L��X�k�;4�^�SK>��Is�C�joi������i��n$�NX�	��6�8f~a�O\�[��k:x�x����dɳ�0�sQ�0�cY��'��sS�AN�
kcV��l8�j����:y�;K�5�4nI/䬝躾T�{�!�	-b�wԣH��?������_����ݎ�k*�Ě����1W�}�=�_P'�	sw7%+�:˔r�ᅤc�F�eԳ�)�+;�Z��rw�R��M�T����ᪧ�Y���������-<hbRڬnũ]��r�c�G���|���J�lf�J�����C��cZr��<sS*�|.�$W��E���)�>G�O��_��$N{%@	ٗ�̹��8�H$|�VVcc���	͗� ?<��Ii��c�Y���G�7h�I-��(����L�=�|� �oH� ������]�y�S\��"3��!����T�׺1F>a~�4D�O�T2~7H/�u��!��v�҂��Z�9|���{�D��L��u��F"�EMXJ��_��	��
�x/��[�v��S��J��ܨЅR��3E����W$$���%������D����$K|%�=��S��)	�d�'ώ��)���-5E��-^"�����Q�N�R
��yQ���?:�5������|��<Ȕ��IB�^�O�~��Aɠ�Hv�D�'O���y��&�Z}q
o�v4_����P��A`b����w��CR��
��	�!T��bT�w�\	8��sh�QT�$�n5��M��0��xm�>������V�@C[� �/��M��̅����3�	8S(�J�<��?�ȟH�l��RN���򧟿�)���2����I���d>O�ܙ�$)��	�����3I�w%:9�I)�I�; w�1��Ń,a1,�v���gLd�
2��y�(�%~�Kbt�$ԝ��B�[
1�Q�g��q���H
�[�;���6�y�C�eȊ��Zz=��^#�4k�6����ĩ��VIQ�6�"��
@E5R�&�l��.�H�'��2����ܝ�F�l}�3�8y��\cx���Q���+�O-:>Q0���񡾠7�o��y�va9Z�׌eo���L'��?�:�Y�X$�	��<!�9�D�	��m�L{�O���#��g�0�/#��߮I$[-%�����K-�����ѵ���K@v�`]=�׼f4�ˤ믝�u�u�	��!
�[8TǸ��[�O��y��[���Ko����}2l+�f7�Z�1���f�c����2h���
����t}�#�Q��,�A�^����d
�^�>�������_#�=�f>cq�
�J׃�|�S3|N�i�������
��a�����kҰ�W�kq��H�B��_/���m�5�H��.���p`N�T_$�7qՐ�q�0t�7'n����
,���n�Ƣ�)��>c���������?����d��6J"_�7�K��Pӧ.e�d���R8��	f
�YF_7žƁ�}�Yr�1\5�ʚ�x��a��.�<�.���x���$�@�m~x� {����!(� �8F?�W�w[��.����ΫKۑ�-�T#�L9�R�>���&oSymJ��D�k��J��7��O�F��]��]�V|�΂�e֙?�"���m�U�F��9�,M�6���TG���v 
2e���Zyɾ �ٙ�v��C�I��& 
JVQ|!�3��(��h�4��mwhl��l��^�'����O�|6H���?��F��͒�"�����+�UI�\j5�����G�&g���#�W��u}�^�sυ��[��)��ϊ��0Yq����O!9���@�b�Y]��g��6P/P�`�/��'=���?	����$%��k�P4�h��&�nj
,Z�/���o�/����B��M�?�.>,����W~�l�&�_J-�� �-����an�{Tw ���=��$���T����_
���6�џy�n�0�3vF��<�	�'�e����ڥ#��A>%'0��P��?����b����Ww���|���h������c��A��p��@Z6�!��%�@B�'˞���"�}7�W�|�j�a2�:�>zӒ��"�K�$��cNLze�u|�Ϊ\~�l�]4�,��G��D\��	^��A�u�R�L7J�`�i�F/��P�7�K7�����cf/�B��X&6<j�Ѕ�F����HC&��fe�+;��/�d��շ��Y�B��ֱ�˶����:��<��RQ�H0��.���]���|�`u9�����nLJV����Ұ������?���/���뗺$���mD�zŠj���;�od��KnHܬ,�E"��+qybz�������d��K�����[�10b��(�i��61�w}�Kʐ#�|���'ҕ�q*0�;��F��������?����@"M�N7�$�����hW�\&O��չ&�L<X���|��G����<�w[���#��s%Xkg�s�a��M-z�p���х��Po6{,U]����6�3W;a�W��nB	�7]�&{WCyr�7�Cy���z9�Ժ&����?�X6‡o�p:ȴ���F���F&@�d\<(G	�]@��Mf��D=*�K�euKx��⽥MJ`cľ8��3�����#��)≝qME�I���}��O-�����T��XJ��_5�Nn��h����Xth�#��c+h/ǟ���:3X�
�]�+Ei����ӡq²|�/EÀ:*�a]�4�?���b	R�
���|W�?�0�V�Mt���s��\�'�����Zf���߮:-1$��NTEG�ZDZ�Ѫlʮ�F�K�������r|C�շ���Ɗ���ਧ<4T��c��j=��(4t��;5�t��j�I\�T�?�zW}H�[���N?~�A���^�_�Jx�B��QG͡�7
�/8!]z���l�д��X���1P�*�����[Z��4mĿ�����}#����yڹ��^�ޘ����~G�?֤�]�T�F��>�*�%�2�)���f"=o��%{�پ¿��lz2\aՍk9gۅ*k��P��>˸�S������X5Y���:���Nx�.�30�OsM�ڿ�oHK���f���z�Y�u��站�ts5���<�6���\5�[�#X����<f�%c�{چ��b�E���и^�Z~�'v�miK���6�
�N��l�4� xF ��qe�����^as�EX_agG��
��6�8���3�Z�ưbC35y��қ5�?������������
�i�#̙�)[�CH��G��Խ9Y�a��A�f�����}5�N3����{��C�O�ݦ�>x7S�!�R�uu��ٻ"�f�{9j��s���`������J����=��8��G<��������=���V�
68G�H2�K�0���s�>_��7�EQT€&
8��Qu�2������T��w����eB���G�jDPtb��h@����m15R�NDu�k�3���W/�a	x�9��{w?�C��W�r����)�O6JblV�!k'h�!�/�B�-���+g�i�����L�59�.hܸ(���ٓs�,o��F�4%��4�i6����F��(G���ko�R5��b�r�i���A�v=�y�w��M-�11B������FN�s%C��?�-'Qk��ЭT�H�QO��hz��eԪ�$6��3�3�9�]�^���f�L�K�ӏ�#@�?���
�
-=�,mI5n������ӂo�?tM �V�x��� U�i�7�?N.,��?���6���L�!K���0��[P�8my��_�Z�m/��/�Ŀ��XR��+���)�M@�m��������#�bY7=�b�?w�k���~��I�:L�	_4���} -�S�
��e�@m�$3����?a�?֡0;$�G�w�x�##�DZ�51��֕�UV]�m��PN�T��X�PZx��9�R}�3I�u9�I���wڣ��j�;��Tqn��4�d]��|�v<��g/{?�9��uDVc�~`y�ࠎ�ծb�m�����%���P�b\�P#���3��>v�^s�U�0�`�*�]RIb��7m���ZM�'�O�Ӛ���E���<�I�^
�V��}���R}�?��:�yV���	C�۲P�B�6�&YSB�c�8|J%;�d�|Eꆗ|�	��π�Z\R�z����6���W��X	��Woδ���w�)4���-{&�.���q��I�����?�����F��}���@u���~��W�ѝ��8���ϰ��n�>�mY53��&��Wmk�UN���'t�08|S�h��{�N@������ �n�Z)!1���@	`�`�%�t]cȥ�o��༛�]����?����?�c���i������%����5����_�D��M$fE9=�n�Xk*7�Oq8{�?�"S��`����U ���`��R�_�<db%�5tg���=�n8+h�İ��Ź���ޏwOB���!�qy�=�5e���k��{���Vئ*�G;����74�����D�ܙ{^'`ږ.��0/Ҝ\e?C��a8$���0D�v��঎�o�N���p���.��]f���ǐv��n��� �%-�4?)m�9�_�P�6�	p^����'�æV��En�
MG$�v�����ǟ��@��
'�t�V����Ž�l��R<�߁;?�^��;��6-Tut��}���<��
`�+�TIE�Ŀ
����|&Ю�o��h%�{�#�孅�����[�7忐�d?C���-�-��D����o�:XN�]�;"�|]C� �i�~�
5��Y�����_[a�%�_��=dV�`�N�׷�"PG���'L�>���(x��ã����T�M��+��}����~]���!��z�P�s�fG2Kx�?�
�g�3�7�k?�����[ӆ�g���u�3�IHOyƨ�w��1���ʂ�{Bn�<��<���ɿu�{�L �j���?4�Z-p�:s&�+]��4�Um�.�;�ێ�4���w?����6��wc�H���N�M �){H���Yw5�S6I(��.��
t^y�Rٖ�4F�I=��@�����e�����2s{;����ߋ3;��
L!Lj��Q��2�	�4Z��	��]�0Z�=�<�������_���w�>�2ga#z�ϩ��Eþq�i�Ɖ���{d���@��w�9Nb�B�t��_,�5�Kn��J�H-F.B��t��-`̖�T�	�"��4-��Ln��2��vQ�^�:F���䳲�.��px�����?������"�)j�D�����Ȧ��a�=Bju"i�����]�o�`~�S41���a�+oX���6�//�J��Ձ�jjy�M,�v#�fm��RX�wm� p.��gW��;vnc碢���-���R:�u��;1�=�^̙�7�<�yxv=so=�g/��Q�2~�/�7���Ď��� �؊&���UD���:�0�ʡ���Q�mD������g\J
�f|Vw��C;�?vr�qƂ;���k���a��Um����o]L3�<i�N�o[;��O��D��Gު����l4�~�^y-'����D������u�����H��|χ����J�	O�Ʌ�"��׺R�8��1}�T�g<�A����
�CѪ	��+�����hi��)�6D�j�;���j�q1�����JX�@���{\�[���������+Ŀjv�U��]��ve�îߖ���!���)�I��%�O��P\��#{��A/����@��~.��%�+j���»%���{��G�E���Ŀ�0�!_*��+��&�����w��#sW�	��OG�
��ym.���3�~%����oy!���bLJ��dr!CG��8|O��c�Ѿ#���ylY}~s�3�^i��1�N��v�
7UJ�?Pg2F��c���S��6[���+6��|��*K���΢բ����9�#Td�4:�S�kZUV4og�F�#͞Q��J�L��w�"ԫ��m�tw��YO9^9�[���o抉w��0�`���p�ڽO��r�>ލ7�&%��TR�v�\]�CS�)�ȵ�[H8�p��i���c�a�.��_� ���]���-����?��5�ߏY�`��_�☫1�M�P���+�CM���y?m�W|�s�w^t!=��OTP,]g
"ar���.��0g"/���ݥ��F�P����@`���]���~�Ҕ��swM�a�u��!�u�� �D����?������_��1j.����.�M�Y��V�	*!'�%b�'��zD����S��|����55�d�t
�d�xk*+�r��4=B�\�%�’t�.j�V�W��Ԑmg஌��U�ظ���W3�c,�(Iڠ˸��G\���2c�u��ja{񯮆
���߽䶲Öd[��7	�8���CR��M��k"	�v����0aT�ƶ�5��I��$*#h�(ZGf�^ݞt�n��0[q~s�����c_J\��^����l8�"gz�D�
�������X������t	'�`S���M��隹���� �j&A
x�DX���GE��4�OL����<���M(��2���	d�#~m�hF\�tg[8�U$������O��g	��o*�1 �����=�eIZ�\��
sk�9yuђ���W�^��qL�SY�叚d=.����cvB�y���������x����C6�|��Z��/桞�!1
�����Ͻ�c+�߃!����w8��o����>���D�o�|q��1�����P�U���]��x��������'�#6���yΫ#�b���f�)��������	�w�@�,f�;2��K 80^���a���[]���l4Y�;�o�]?���p���LXU��-'
�%{��w�r�0�LW�m�?��qHE���	s�G ���7q���)�zG�o����iL?��l̫a��l�.�G:t�!3��A���1�o>~��j�S
�����-��$#%�k�kos�ѥk:՘;��)x��S�y֧�`�R���4=�-�����;��qF����?���ÿ�r\ԩ~�!��3��F��k=���̣��FJ/�9���O<L+C7�@��wgH��ЯX�N��<��J+�y�M�Z�W��b��;����-�_���nr���d�F<J�-w���8��������?��O����v�9Ly�h���_v)���&�|�7>�$�Y\,���jpv���+Q/3�����(�L�|;��e�-5J�r��44[n�[Y�Bm4���%�}ݬ�(�冫�f��K�pĎ0�E��ve����1<Ύ;+PxB�Fs��a��[4J�pK��_W���Z�&op3�\�en��R#�Q�yᛔ?y|�
p�ٞn,�}��W��n�)W��8���f�ם�9��8ۨ��RCwr�a���:D�zӽ�1Hx�9�Ĉ������a ������Po��.����`�ޔ�;����G������>�4��9 �L�
%����'��?W~�����5	�q�t̓��;��+�ۧ\�?��mu�?:4�l���Q"��If�
2LˢUCJ�I�)��������6���T"a��k*�5m%��rJ����m��N�D�/g|�۟��S.�-�����:U
ΖB�֦��`忢@��!F�+�
D������oK~3Bʹ�*�8t
�I�Hi�Y��H�o�F�+ui�P��)p�?㕏��G�S��$�ؔ�y�� b2��;4��<8$���45�Z�w��K]ɞ�����G��p���;��s��)����g�.n�{�f��t^w]'���8KB+�1��lW�
ř���ᬻm}ţs0c�ʟd~�����*���&טp��2�V�Ŀ$��'�r��b���S��j��\��'4d��.�Cxc�NN^�a��^���ƴ�T>
e��}8#���>���I�7�I�*��V~6afj����~�J�?~��w�ɝ09����>�9`��3}�ԼA��=���W~=mn�CH�ijs��I���	箄My༯�c���$�0��,�����4��L9S����?���k������/��?����4���:�w
��:a>X�:���A�=[AWm�HN�9w���K.���(uGs���L�U�1�Z� �^���7<���~�)1s
dl�j��R���_c
Wro;��6r&2�?��֗0��=�J-sf/-"+9���m�n1
xJ(���D��Y˽A]�̓����?�����r���:�L� R���|�u(&�}�R���N�uk�EӇoT����|�ա��}����߰�J 9�ʲk��LI�uV���~���n��i����?�S�H��{����_�e����edA7��[�ה�3�C6�mIJ�Jd�+(j��)i\����.����ef���B�#�궯=�T�y��	��n\��y�Ҁ�~)���I��M� �t���Eퟎ�&�+o�-I���?v�{�#�'y��^kr#�aj
��x��%%���|��F!�*
���1��xt��#H�_���7�?�#�w�����S��[��ӟx$9%cZ��]xZ����jsV���T�r��rJy��@IŠ4=u�"�Q	'>���~`�$M���zF�H��X
�y����
ϸ�E*���rO���ǥ����U[{�_�F!��pr<+��Ńmi��?�*yOx$���>1����K��-�����'<�?���÷s��7�����")�ML��
���.ё�uK��"����G��{�t�S�`�gSZz
�%sw��A _��0<Q_ ��<�Z�Ij�=]S�c��?��!�������{fJ��߾|v�������_��0�<C�0k�%ӽh�ZO�#��x0����:������Ր�����钷�QA�����H����w?Ў��R�d��7�8�OR�'w4��%@j%C�E+A?��[��������`������n���c��?w\ހ�$�yH$�	�X�}j��(����Gh�\vI����%��s9z�K��㫊�}9���w��%�1���+?���:��TI+�	�%ҁ�����:&�P���~��(��d:���S�?���<�4L�(�z&)��00f!`ӌ�O��!X��b�� ��?�9	�U0�ވT��?�`�-H�P�D��rB��~�r�#�dRߖے���I#glJ��1��~�8/��?�u?���8�$�`� ���X�0���'��־%����8cbJ(�n�І$�z�J�t
��A$`�M�clͅ�Wd9�O���Xg���~�l��ȝn�j
ݜ4���VUK��,��t��0ll�p�!DM2q�9f;����V�uc�?Թ��'��K�+��5c��x)�k�
�j��_�Փ�橜�C9�h:�YUj���F��*E��h���(4�ꔩ��C
�q߆Xp�%�]�R쐟.�Y��A%]�p�8^���4�-1�:o7giL\�9�F���T�%�

�ޱ*l{�v��/����LkȊ��{�~J~ܹ�ӡN֥}e���i�-�O�n�
�Ù>�A��ខ1��_�?���������i�u���~�����tМ�������Z��s"M�\ӗޛL��k���S�[�oIob`����v����~8)��� 1<t����-�m-��pN���6�:�MoQmG1p����s̃����?������n2��7��=D5żo�������'�gL��~��N��%���[������HbI�]_!鞘�MV,��S��n���P�9b�
�L�!$6�
�����ƛi�	H��s#Ml�U,���+�0y(��o��%��ӡ�H�-��U9w[�,yE��&g��EsZw��`e�&4X��X1�r{��hN�ͭ	����0�0�&;�]�9��I0�ۈ7{�59C���Q�k4�Z��Ŀ}�'n�J�ɵg��*����^sv'$�e��m{�4\������A���
�	~�S6��EA��
���r�Y.����dT�*�v�n�V[s8��� �G�P�Cm���ӭ�h�X��
�p�I��0܏�D��@G��P�	�v�
=�,��?t���Yj���2=���Mr�~3�K#�Z�S0x�X���k���hB����&^ݶ�Aɽ��F�/9���I�$4�
�(5����Hݞ�������C��f���[�+=lY1�JG������>�.���[�q6��z�mC��D�ſ����>h1��NM��@����*>h[8C�G��ϝ�։��Ӫ���6U�������rF8M��WF�=Dwl�|p�g��#��8�L|��W>��X��3�9w�o��d��r��9_�7��f��#�ƛ�"
���+n��u
�q��qf�m9z��e���bןv�F*��w���/_�g��3�·c���btJׁ�6~�k�w�	\.�Li>��
K�<R�������w�6� ��5�:�u9l��hҌO
�/	��e�����?��5⟬��3H�w��	�R�񽽶�C��^%6I<�s>��̓�M7���3!���(�
��W�_{���I�����!s&���K��{cM-��H2�R��w2��ny���V5��ς�����������?����/O���╍"M�'\.����o�u�fQva?K���'\}㬳�Gx�l��H4�[�o�+mz닻�]���p9E"
� ��kY9&nM�߅��b�1<�����2uqoخz��V���ݤLX����ƞ1n�X��!�7����}q��a�t�6'�S��b��8�����g�ÁA����Kuܦ�F�@�
�/���l6�
��Ge���֐{?J{7�ݟN2����.�2�l���7{f=�^��B�%L�.�R�N�R�ǣ4`+h��-m��\j���aQ�&���E����2�H�6|b��&��F��P��Ќ_w{����Z�/B�d�S1L�h�9p�OJ~��h�f�oW�+[l2��sl>�cϏV�[�&;�hqKUg����
iV��h��Γ���i��j��t�y��X��@����@�s0�@�_+&���_����L�?�i.ܳ}�$���������F���\�O��,~@~�Z~j�;)��y%�-JT>�7�6��Iٺ/	���M1F�k�ϯ����
�&v��ʥ���
tJK}�1=ޚ�!�i�؟�����"�j�M����:���t�'{Ǚ�hq�팁����z��.�X����~I.չ���rxנ�z�^������= �~�B�3��i���+��9TE}Av=��}4�J��qn8X��,�vw�'�j//��w̻��ϼJ�믖��Oס,��q�7<��p��fM��Zd_�����8�F��o�s���[�J��	�O��@�^q�+1��ru��]=�H�sAՍ>'�fK������?���k�?�C�pm,���r[ZTK勼�
��4�fc�1� ���47��t
K����hX�O�}�pK�ҫ���驮�P���sP�
U���5C5'�N�uC��vJl�Ch��|�m)��|у����?����!gi��m���[����?�1��|�|����V��Գ�2+��'�VX�S��FmK8��A1O��}��A�����;r{�qh-z�M�^�͋�4=���*y�]�Q=V��u(\sVnu��V^i} U
0\��f�-0E��ٸg;i������hc��d�Eqͻ�	����y�
��������6�j����W�\�)�&f�+{�X���5�k^H���I������t��+�1�0��m�?w�&��.^��<�$��"�LJ�@r9?��x#<��,R�S�7�V�c�F�-����µ�ߴ�Т��UD��!�1'���a�/)�y��B�j'�g�A�h8�˯��_���V�f4I�0�N��3A��g���;�'���ھH̓qG.�|����'����5�be	6��U"�-�{�p�����?o�7g��ǺE��?�>�����!�?�ߢ�L(�{��Q�����es@�<A}kU��Y�������A��<�FE�D���89�mNF����\h�K@�y#gN�`0p�f�Q��s俺 ����Oͨ�R\_E��
't��[~����Vi���n\�GF�zh�18z?��8r���kO�g�*�\
y�Up��`_N�îx;�
�*٫�ž]�}�u�����Ip��f��-�O!��y���̍�?�?��`�|W�#�'�i�OF���>�:�<�vl�RM:)�,mwFK8x3�aM�\8>�?C�Y�%�kG�2��U뷂�Mt������*kk����.Y�L,Y�Є��H6�Sm�i�|U�m����j0FRk�<!��]۫�8�R���I�b��t���)�1�ws$M9�.]+�]B������9�3�F��c`J�ۨ!��vQd������?����!�Pȓ��`p�
��b^g����.�����m}�e�p貫���A�2����Tr��(t]�[�>�k�&�	$�t���&�B���2���3��>�y��W�����5z�<I��D��Im��WQ�������?����?�3_���̆_xu�eίh�g�9w� �>�0+h,�Гz]Ǝ��^vA�ʅ���n�N�^�ž��77}{�|s�مiA5e�;��փ�8	��&���h�{�eL����MgW���&�LF<6�`-1�Ȧi�S���p}(�Hp�����"O��,@o܆zӂO��ї�/~.j��x^ɜ�*6D�U|R���d�#���bN�K�A�ÈM6b��$��B�}6)}WR:�u�R��ě!����*BJ�������N��w> *C-�Ys_�#�R#ٻ�9�X"��i
�x���U!��N\��4PR�a#��,����w`>�?��~ ��k��ƿz�4"�����J)��c>9���I�G�]�~���9��P����w{Ҕtm����#�E��ϟ�_�R������(�o/wH�_`�b��mi��_�w~�HF�N��W�f�k7��-������E����!(�UM��]�D�P:
��"	��������+��9��	)|ܶ|nuL���� +�w���)�ſ�{���a�S���?*�������wvԨ{S�Q`÷�8����󹊌w�#�?��ļRn�r��&�����Gx�I��N�
@���^�l�n���\�B�1\x�7[�D���7e�Z�lz]���^�1�ԺP�Z?4*�6���$��u�ӳ�)R����뚐Ȇ���H�dǼ&���Ea~��&f����8�����!|W{1y=��c��9	њ��T�l�Z������3����d��C�r6P�f/а9��i\��U�ʼ���	���L�!��&�᭑����|�20̑z[�%�����9-4���S-�L�Lyk�Ѓ����?��5��%�=P!�~A��ȗ�"S���AԜ#�-&�^}63㗽��ύ"i\T���.��Ŷ	��Y��1�+���[�A�pu2���,*��+M춖8
�9�e�{�������=�,,�l"�Zb��݃����?�������0�5����)�һ�e���\�YS�ἔi�U-2p�b4}��~ξN��!I|N�/g��+c���^�d?jZ�}����8�G.�ظ%7߮���_��G5'FF;�n���t��p�ܦg]Dn��g��M'�t
*�q���80��ɟܪq�Z$��¥�[����ڊ��Oy6����ً%<;^���W�#fmb�C�W=–���g?�:X�Cm��B6B�-��i2Xja��P��Q׀��_k�"ㅫ��8V�u�W�iD.��*�l/7��0��b�A&��x�!��9N����?�Pӱ��˜<	X�n�чSA$���]4�{�d�;�M��mE0��_���侻�?��a�,t��@�?�0q�w�"Qq���]��V島�C��K�z&�J�~!7p�"�}\�S����xDu#� �=��=������M3�����T�� �;������St�(":�u����y��w'�����T睇��J9�s�����r���|��7 ����^�#�E`��Ҹ�wY�p���9P~�"-*gP�#H�o�.C,^3�A$f�?�d��W���4��nb�?m��Îe����C�G"^-���
�?V��[l ^
�ǎ���1$)قzKu�ҝ�7��&�'�������G�@�AE���̻G;{���c��a�¤ֶy����X��/;��)��r)0�i��m�?�u5!;z��6a�j}l�у��� ��Y�^��6=s$$믜C��n�i%�.!z����ͿJov�W:&����|�}�}pL�J�N����2���gz�+cVޯ��̜G�*�~��������F���~P3��,|ڂ�4H�	+d��C~q&�+m�5 	ˡ�),��+�\~�ik$�F��~���`�ĘfDS۾�*��[S�ܥ�����u�-�_�!`�Ƶ�rT��vַ������>2��������?��g�@/�ZE�Ӎ��l��΂�ixl|����i����Ϯ����%�V��ȗ�4��Wzӝ-��O�*����0yJ�����'���xD�០	KFB<�l��-��/����Z�}���خ��u���nBh�S/9I��gm�*��
�i��s%�-�&%��CW+�6�����tT���`4�(n���P�?6߼��J�W�3���Ὸ�`��PW���\�C��(I�¿¶�V�k!�@wm�>���]�x�1~i����rSs��uZ�S'�A��BU�2=rE��a"����goj����i�O�����耏-<
�����zc�Z0��9	�����>����N��]ݧ̼�m=��Nem�F~{�����{��M��~
Њ��e��#b~<.�v��C�sZ*j�[$�ڢ�a�!�����i6�n� ��#�߶�����J&hݎ����r������/U���������[�nn�����3�V�S�p�N�z��LT#K��V���؛�_�o����1~�m��Gk/Y���
�Gu���ߎ@؟�_�C&�_�"_�ږ�H'0$s��C���\�J0���J�,�Z�R��"�o݋A�]�\/!�ֶ./t�N/M�c�E���t7k���f�9�ap�j�eS��5�ipՒ���WK(�SM��Q!����H���� :���4[ ��F��9_�BIMXm��*ϡ�w��0���l�F��m�J��zM�W��~պ�:��������~�q��}��t�"d�����Ex$��J�*kP�ۍP]'c3K��X��?����׈5�����fz=���{�<z����+�#%90��su�d�4�~ӚQ�����=�ω>mH>��]��.�4�x�^�T�4�g�2$�p�r�p�k	W,�e��P_���M�����&3]%������?����?����-r��T��,�O�tg;�{�ǝWu�����4w�u�
]<?Ը��"������,W�qU�>��Ԋ�F�V箳�
N0��}��E�̔�(K��E~+(��Q��M��r�7�C�}�nHI�SI¾�m�J6�[�#F�-��J��Vo9�p0�y��Cn[�bG�k�U�@���ngJ�d�Ed9��EŌw�Q�_�[�"�0b*z4Y�*��᝿�ƀ�
���nTW��R�LH�Z���W�Y`϶��̈́S���_p�7� �Ϡ?��Ϲ�6E����Oޅj"�?����^w*�=�A�}f�jR���
��l$A|֤��r�Y[6��KA�*�(��چmJ?|�&���2J�*A>n�h#Z�_����#�I�҉��x��y;�{�3�I�������o���6as#�[��__�cb
񨪖�7R�\�~0�K�K��3
�U�k��i���;�x�\���Ln�9�h8b4��O��n=[ܞ�`���-���W�O��s��g�������򿭡���gfxj�W�@���=m{�@H���h�f�=bdžS�{���;�O��4�����m��*�����*wS�)�iXӏ�P)���f�*�w]�郒���C�/_��i/�X�u�f�/9Y�3��*_�O=zv�:��0��B�5ծ�Н�����Vu���]AOW���^@�����9=�=��?����o>G�=�\�)]e��u&�y6���&��Tŵ�/1���V�{S[��g���_K���!1���8�\y(wT)���T�7I��'g�%&H&��lY��6�0��(�m&��(��m��Znz�P}����&V���R��-�G�`c9d��N�6K弹�>�������3T���H�k9�l���hć�0�jn�,�?��������}���Z�w=֕��-8t�܋v�
:G�lG��gPg���$�f�i�1�0>p�i�Uݩyt�9VD�iEl^$�d>�D�$�4r2 ��/�-ᷧ:����\d��.���oC�6D�鱎�r�kkx\�X�Zz�Ւ�cO?�N��o-�/P�{���ԥ+��:��/J~�lC֖�X�r�M)N������?����P���̋ش��5��E������do�G��5�������=��6���S�@��<�[��2/aq�p���$����a�����B~A��`G����X[�Muf��n���v�,�)y+�����w���#v�,͑+�\���Ye�N����"+o�F^^�����޼0/�„w:�0���l��8N�R��5�'��o��u{���'�v�][�A���%��4��=L�� �L��n+J@rgY�<��u�A���p9�0��K���@�g}��)���E)l^Kk��f*%��r�r&:��y�o����!���[��������#�z�!j�+�v~��W��jLAtϥw�X~3�'�|����P��)Txϲ=	Ҳ�qF�hl���,�2ƅ�<��ĥ~��]/�����>.)o	8�pÒ���M�/��4�:a��龹]kA��}�ݡ�J1��:{zYj���S�$sc���ʿ��_���7�����~�����G]���Ea��|4�q�o����~��?��Lr�H�H�h�&���IL�,�U����7�h��yR�ޔZS�\�'F>��(�~��O���|�k\�!>�E@!�5ؓ�������Qm^(
y1�}\�.����(�l#Z-��/��9���� _א���	�O����!o�g����Ot��*(���3���w�����_([�r�S����O����7���=w�?�������$��/���d�A�枟�!P�Q�3�!��w}��w)w�S��U�9񏐁3I٪'`�0��"<o5#�Q�LZ����:8e��)]��?&]�:������@8o����`{,4��<�d�C���LxĿ��8��w�F��p�0��
�S$@l�[���T\�I2�w	���GSq�?�f��]2�@%�,��0�gLw��dכ!�Ҿ�3�?���r`��ߟ	3�r)�填s��`����R�(Ͱ��$  :Z�A��y0CA�?�qPG��\�+wU���
I�Ռ�pϼ�͗�� �)Ib���h��V�0����|���,1�^�~��(��I��e=�c�o��Hl��ʟHlӷe����rwOِ�=�G6&@C+(F���d�����8L��h�殚N�H�����o�2x`� ԩ�/%�����b���M���x+������&%��G�?$�`(oL�%�U+�е���sn�����	�!�8}l!�ܨ�^CR.G!������}���);�Wk���g��vK(�ۦe3��$��
#�-���8Y#����]zӞ�=4n�'�Q6]��^]]�ҊOJ��.��|Zv8�d>�������_#�)�ؒe	q�ϔ�O�������k2mtOKO�ܴ���-�������H7�CE�����<�"�ػz[d�q7�����^zS��2�Nyۑ+��6s&û���H��)]�6�4C������?�����s����f>�A���R��uW?���B�m���؉��k��/(y'���~��V��~�4����{²]�X$+>rFʞ��?��m'c�Z����c%C��ٔp�5�BQZ��JK	ӛ�E:�s�:)Pi�0��3\�0"75KN�q6ErO�o��Wj��F��m���6?Ƿ�W�rY�ƹ���%s���0<�nax�#�����"cF��G璍#��e2pzվ?���" ��tHӕa�ʹȡ�	
�=m�?�'!G9�(Q�	��H��ȗVA�ȍ0��A"��6OdH�l!����p
�s�9�����	�I?+i%%�#rWt� U�9�
;�a�75;��m�G���Oy���=���-L��im�c]t
����cЗ�L+�����Y+���#O� �R�$Ҷ�8ŻΪT��	p�2��{�?�Xĺ7,�B(Y!%�'!7\)�LP��UH%��쾋��	K�����m����G�'ڑ��X,hL�,���=�w�n{K0M8Pf��#�Y�	�D*��X��`��}M�/��I#�E�+�/[H���^o�^ �����&�v��WG��?�	yn@-���Z��2<��u��%Kfm���T����,��l�V[��Խ������3�UZ���i|6�{aTw���p�?"�?��-� 1
�O��8�sjz]\s�];�3M��s�M�K�]�"�NtI3ɰ�2�F'�N�8��>Ko�̢��Y�
��8r�v�Ǭ��/F�/�^Rb�c��0��ݥ�5�$������u��JC>"�!�������4'[�i���u�]��n&��Qř+��j���>A�Vhl=
�)C��
��q�;�ȯ�W�q-�+����
���?G|����?���k�æM�~��3Sa��?8~R��u��M�܁kj��b�,���wI��t�����a���lMG�Hӏz�rO$��ޢЇ��CVꊸY���:�~
��,�V1	��ci�K,倯���M�?�����������.�<i�L���7�}ŠQ�6�m�G�Y՛��ח?y�s�]a�s�&��w ,�!2g7T�.�?�]g	��;��Lv��a|��L�i	�|�=t���dp��to&��A�؅u�7C:�'¦?��Y���'w[��a�}��'A�VX�~��/���QlcP�~�4��ws�S�,]����D�R�T���i>7�XV�7�j�"v-y�Z�`ض�ޤ�����p��^�r����z��!��"��d�>�z�n�-I���R�׽����ݝĈ>L�Ǚ�_!�Xv<��ǥrߣ�Rs��*�̖��ٞ�����i4,w�Zĉҿ�ۿ%��d13�Q��10ᡰ�*t��@\eR��C
�˯��<�~W#c�M=UYND
�D�!g;�(@�_�]��f�`b�U��;5�i$��ok�X��߶���t����3.�,�����5�a�����Ŀ����XC�ִ�_�[J��	����j8�Lv�4�?�;)�����Clo�lH'�}��dJ��H2G,�\�s������+�3��/�$?����[[�`���z�w�c�|���+�w�o��ӌk6{�S�ܓðȏ��[�F,]��.Ɲ�t��G-��a�.jF���"��%�����GGB���F���
�J`+Xf�C�r�V��H&�3P���$ȾsWa�l%�7��
��"��
�K����S�
9-}���$
�~�1:Oj��y�e�OX<\���P��Eɔ{SOq������g�B��=Mnx֊,�R�|��˹;?����)�D�l@���tkwk4"��,�i���W���ȟA�(���	X_9 ��i�w�&�3����"��O��`�a���?����׈�]����,d���8�5�%ye������|�6,6��t@��S|�9|w�̗�.�J�&�WW�&v��猡_�%c�j�=����"�P/n[��B`ېP�GD8�.��WoOқ�G	�o|�����������/��|��>��i�1*>��7���UA��g���{6�����y��vy�}�s{x�r��!�y�[�܅�kD��6J�+�cK�9�q��p7
՗�[�o��(�k�����{1X'�?����a��P���Z{��δ0�(ԕ1F&�^��ۈ+�]2gS�l�W�~r���*�
0�}Α���;>6-�X9L����(f��E��s��TZ�RAS����C��F��y@9���ӵ�w�QB��8�VM�5����)�I�D�uBd3��$�W�(m8��Bd/��Z����?;�r;�DH���k'ߐ*b�m�ΰ����W�^*'
.܅����I�d$DL��[8�ɑmх$0��o9x�H|��ʪJBQy�S��*N7�͟ޠ��
�F �l�?�d�?�$��z�e�
���/4W#�ŏ @�Zs�,v�FF"G�ۆp�ݛ,�9�0��d��-��T��Oy~jӂ:e�I[�깇�
��5�5}7S���LH��^�C��E����qٿ��vq��`�h�9������������;�w��}MF�e��X�����}�+�)�t��	u��
���p3�|.�[%�x�H!�\ ��v1�3r�p9`�ڄ���V�M/�I��ڟY��4r����#$�n�^�H0�ԊY@NX�(�}���&�z�l�>��Qdw�<"�ɘD�ܵ[�$i�l����\UN�(K2'��}4[�e��R���.c�ϙW�W�����ah��"�����R>6��ʥY��r�U\��g�>��=�c0�j7]��K%��Y�v�S��CP�+Ug]��1�Mc�g�r͘6��w�����;��žC3�9d��!�h2j�~e0�~��3Ife�C��np��lVv��>��L�O�"��9��Ր`~��������F�S��wu������p��м�L�9�L>T��;����^^����@\t-��1�c	�?�ޝWڼB�ba�<	��}��0��+����
$蕥�{*���Ӎ����9k��K�|������?����G3����嫥�a�5��Ѕ�me"_="M�N��퓡�ڂ4�\�en���W�㘫��-2�B-����l�
�r���.�D��m�^w<R�]����1��17�c_�bl�j�^��؊�y�������X��'�k�c6���M�`��
��g�
b�b)�R<��`�O��y�3
�����.0v^ݯ�9���6u��I݉ZRYȶ�&
��ƟnR�4�]��Q�	=�0ԛa�n�HURx���^V4…��tT��B1A�l	�JB�	#�Q/c|�W�&�.��J�U�t_2_�?�P*��e�q	9�J\H3<�K��
:#�w�O�4����q݂��99lXθc>�O7Ŀ�-�[j���h<>,t�?%�!�G�����������?�M�O����iL�������ƿ�$wh9�P3�OL�n
�Q�k{6��1낌y9�J�O��2W-XI+��I�����)��)9 ���Jx�ԉ�>�U�ƕ�����a�u�ޑ���v��?�����_��V8��,��l�R�������Q1�33%��΄��45Dw��LrDi�`����O�N�;ޚB3!e�s��m�Y�j�,���$�Aey�_�ai��k�'D�lH����Z������a�Ԇf�]"��l���,�\H�A�����Y&K;�.��Е$P<�����c2�"��f3ft��VtO��dw�תa(c�L}tߌT�7CL.u�p엫�Y{��0��xQK�i�V!�fC1�e�>uE����I#��f#`�֠7��s]-r�6�m��]�izy���dS�����	�A�+&����҃����?��5�z�ڰw
f<t�����Z.��j�N��ry�2�;������(���L>s����o|
	�������;-ﯬ_6��v��v:��ݍ�7�i�WJxFz_��U4���)O_:4�ҧ��e��ʷ��&=��������=�m�sm[B]x�ԁ�:kU��'H��Gz���Ư�dƄ�=0���QKE���|����f��]��f����{�B�0���F�}������u�՞O��	�s)�X����Sb���f�cGK�z�qN	���1-��ļ.��ے���Ǽ�w�DrJ�OT^�Xn�I�L[�D]����� ��8�P�=�^��g��� Dβs�ۭ7}0<v�xV�QY�b`���/9;�H�����{�
xH��|���w�GCEro%6�zO�Z&N5����-����w��$��>5$JJ�����b�ΜflPh`@V*ٰ�ɖv����*#�:㔇�:�[�W02��i�Jx�2B�)��ߖMxl���Ny"�1ͅ��\��W�;�����_�ǐ�S,��t?+A֝�u�D� $�Z��ƿCL�_��&߃�86��e��co�X�+�NK<���?�O�	��?s~�?򟪷�l���35�F���I���k������HݮsZ(i_ʆC �ԃd���6�0G�Q'�C�j���������S����W�1/	��>{)��Z��_
�n���e�U]�ڮ�i�َw�z�ke�f!�%�?b_������_�W��]�ޥ��D�%��z�M��������Z��,��!����-PKN�˲͆����-L[�.�YM�]����4u�jռ:�L&w�w���
hb�Ľ3�-���q�����D�^g�Yb�	�f�,��Ö��ӦJk.�$2��u��Ow|[��i^(���2#l��=��tǵ��؀�kGk�#��|�g�U��v˖������7�d?�)�(�vL���Zv?~][�c�MPA��ns�|��l�����?����[���4���s����m���7ڲw9�O9c�w]da${ӭ��H�6f�,�L�%�o�\o��*���{�@z��q��KP�"h'��0ק@ ���~/V8s�N�]��ӡD{��A���x������������Ok^g�,�H3C���]:���"
���q���1���<��<%h{���@�q(l\�/`��W�C��kXE��S�CI�ϱl��ƿe;C���$�-�SP���o>w<���>xl;\��q�|N�퀟}�_<���kI���ߌ`�� vCہB���>k-O.f}:B�X�^f
		9�� �{����>��e�i�N��A����Mo]�"*��
�W�6yu-�X#+G��%S*؅��S_���$���\cG���2J_3m� �L�w5�m}p���\�^�2��4�0�bbڅB�}X��#a��-e;����	%����p9�e�n��
'P�T9	�
u��8�C�@6Iq=cs�����o�T5�9��@-$�_� ��
*�u��ԮmHL�����A�2MM�d�N����PnS��Z)�	��ї�� IZ�"��t�eG�/T�,C���`�H���>�L�-�?S�?�c�3��,��^k�NJ��6
�@��[���b�"θ6�Y���M�9�����
�=�5 ]�`���H{�/��kb�#!�h��5��5)�����l����サӖ^+�����8�ɴ'WR9�I�T>��Կq�w���d��,-§�34��Ӱ9�<+V�q�o����.Ư"��@ͼ�?"�:���~���3�o����U��R�5��<��B[��R�V��ē���eC>��5�No����nG,��=w��{�r;�u��ܛ���ӳʒv�e����*��g/���Y�^���B�?�v��$녜4>/g��Oi?~����J81`�e�]���]�]m��m�8[��D�]^��!�Q�+�e��NЯ�2<�������_#��g��9y�~K��>"����lN��J�wIfk`�pIv�dĘ��4dzn_�fEma���|8 M݇����e�����]�J�y�&KC
�h�nsZM�{-���G�M�,�f%iek�Z����?������F��(j%^���.[��;E���O�h�˖��`CzSgK�;�G�[������zVV�yt�'
�L�V�@.��f�z���z���8�A֩?7û���_�,J/����™b�I�e��x�	���M@٨����on���	�9��Ώ\��.ퟆ�Y]�W�͒�	�W�*����1|
I���=
,CT����b�����4P��%?���=�s\�����yk��4U�g,DLoC��=�4���vTm!a �Us����8*80G�ZjK|���Ry
�o?~O>�m�l(��o���Bh���Ҹz��FWH�61�pȒ��4I�6���%l~�����c� t��X�D{k�4�ߴ<�^���k*�M'�ɟ��}�??�c�k�o{��� _j��r�)`�P����ߺ��2�+^�'[�-)���;�jFB��T�J��Z���%f4���E��?�<�M����0͘���i�P	˫�����'���yG�ߓ�=�Q?�B%��TP�^�I4�($Y�N��:��!����O�ܻjĐl�`i��P$8�)(��c�0?Gk$�5��.vf���^�Rc�FClɬ�C��e����]s�F����Uҽr`�W>�U��lw��x:�Ne�+�D�r�cH�
�W�r�5�1�3�e/��ǝ2��U�_�I��;�H�6�|������?���^.7�q��0l�ʫ��^N9vxo�*%��P�9���
�Q��n�R���~N�>������8�Z�E%w2�����<K>(���0�,�1�W�*���w�7����HD������~�9?��fR�����"�zmc9��b��ϒX6�{P��	�U�녨��a}�:�4�
'p-��P�������?����:���R�>wؠ
uI�(%���}�ԯ����@�7k�n�jr��'��o�3��X��;o�М0�$:af��Ӎ�S�"�g�P��\��oc��_���9���F����,�-җӋ���
�"jlD{�^9�j��.	���CwR��@!�R��Wv�\��%Oufo��g1(jR�9~�]Ƌ��9V��������?������f������hh�L�h�yO�sO�>��Cn��VR�Aj��í_-�^���`?��	���t� };ߨIJ˞w�����E�y��J7�]d5�����EJA�'ް��J��Ǘ���B��icjԾlZ%eN�[)7��a^BGH@w	Y��-��R"��N�_������0w�ȘMJu���V+;z)˫@mN���CA#0�b��!1ocS����D�H$��0���l�S3$ (W�~mH��W��s�+
��{��w����?��r�R66rXc$>H}[/��<Y�Q�)m(9C�$��?�_��X�l;\�c�v�t����H�W�����}�S�OL
����4(��S^��ƥ��}w�an_���r�����9��Oeo����Lj˟���ܓs
����?kc엪S�����T�pX�)b�^#?S�<��R�_���~@K�I�{����٦0�Ò�̟���[�����˧�������cpt�ņ! H�t�'}Q"�6 L#�$HT'��	�b�#�Xp"�^�(���F���~�k����H� ���JЦy*Y�F`	�(�o+}pq�JS~RSȩ�;�t]��K)X��Y��#��Hْ6��c�r�<�Ө�8�ӱ��s��o�Wv�C���
K�@�'��XVC|!~�KN��?#H���u�|�x�m��R0���F�a�wA+\�e��Z?J�YD�ON��G�+�XR����@8�ʦ�?�V���KAB���*�d���H��uaF�+�� FN��㎝��?�v���o�W.���rb���m
u�;��W���������L<�%���t;�͛/ȁ��9DMF7
l��M�$ߎ����]�����p0e���b�>��F������*�o��(aC5�fG��L�*�*�1g�9
�A����)haً�?@訡���RB;5c4a��HT'� ʵ�Ҍ�0��E�n�ޥ�)di�?��|��|��2rϚ�?�Q���9���I~a�>�~B�j{(�����`;�Sƶ[^���I�ҕ�V��.�(���/R�׍_�J`�L5�1��ZQ�����^�~��gq�W�1h7�|HB���
.t�煠Q{@�^��m���)�!s���pS��~�c�f���8Փ5:ܜ��
(��\�����]�YO/�I�Y�_�M�T���6�̀��K����5Bx�4�R�bz{��pX�`�7�<7��5{q��Nz��������F�C
�{�P�^����W/?/��e ���z:r��_?Ό����\p�Kl��1��(��*S�JH�Ɲݔ\�5M3������3�2vN�s'�GȦ���3���OE�A�+)q-K��9&xdK���N��4l�?���������wec�F�]J�~�e��+�R
?�6��,Q����8�0O�Csb����\k��k�B^�n�Q�ԇe�y��%`����=��n
����'ƿ2J�.��<�쪊.�'����H��t
���4��YIH�fO�Z{MIٺ� ��t�	 ��_s[{
�v�{Һȹ�1�OQ�w����q�Z���+�[�(�����h���uf�+�����N<�r)D$��+�Cx\���.>i(�v��oE:�"���'y�?�T��>{]9��-�tm���P�Zt�+�?�I ��RE�:(��	3.�j�P�C�lba�{-�c�w���`c ��� -�.ڕ���(@JG�ѹ�ô¤��J��3��YT�hE��O6zE�C�Z}$���ɖ�L�T� ��k��O�W����41�j7�ە!}�ծ��	x�RY�1O��SࠔI��0�Ei,%���_9�&�t=A�/?^�����qCK��"�g0�v��HSD�*�&�`��E��v���-�Pu�k!��߾i&m����Tѡ�e�'�?�x'�::zFK2������b��n\���re����F�t�k%d���=He_��Rr���pJ
�lJ���)�3S]cL����@�+x�mSj�k� k��k�g�?�p��״-�t��Zh,�YM��,q&�`��[H�م���#X��(��	���x3��e�ݼ�aE�C�	�����%,9M�˳3����\�v��*5h]w8��;�Åt����KD��,����@����<�XX���^M0�»�q�E����շ�loQ	�I`W"��jE�����Ri�BrϡZr��d!��B�\����J(�m���%�F�|Ey�
\�V�<�������_#�erk���fu;�}Ǣ���2LLΊ�^�Gï�ac^��Q&��&���R/�r7Z���>�<k�{�n���|�L[}K���蹷Enh�mxjwi����C]��ah���5D�i�hK�٫��?������������������,�6�84И��0�~��	�R���R7�@��$P`1hed�����7j��w��n��׌�	k�/)��P�C�M昸�_�)�Z}~�x��_���#�������Rc��|��-���4b�T!-��L��f>pu�s2o~�h�I�LЎ��
2���S�A"��i��gp�
�hwǚ�Y���Ma����W�	a�|�S71x��4�O�|RR��)��P���rQ����1�?A�ߩ׫��ֲ��GT�i��*w�U9�{9/ؐ*����s��V �6���sԋ����sa��R3u���ls�׽��]2�<�����q��O��{�K,���Ϯ$�#�b�k�
��9����Is����#���<��`���4߈��Q�D���'����ExN�����?Lh��R��.�1�⫱�A�w����A��E�x���c�τ�mZ�+�~憩�h{��6hnJ;���۠�w�}\9,5�3/'�qب����hn%�������t]T�ҷ��
�)@����{ſ(��'���Z�:g�cv�����!�."$�~c�F�(�� C�!�g��F������ɝ��~��5+���њp�t�*hI)��;���%
��f���*a���Fׇ��Z\��۝W�4��J��4rw����΍ܾ���AǴ�~�d{]�V�&x�˵i���iY�ye�\�J7P, 2?^�w��obz*6$�d�=2%�G��x�Do�p�l>^���5������f���wk!�n���nh`�ן�h�L�I��z5ӎ�cHؐ*��V
P1guF�2#�(i:��֞�s2��7�Vn��d�9 �i�	?�������_��]Ҵ��3�xg��)�5W}�H�or�H�C�%�Ŷ]R�C���ɔpX&���?_'� �s�M���)���D�T��e��q���O�%e�u���1l��.5������8�9n߹3RQN޽���]���}{ă����?�����*�+�RE��%����,{7�Fk������x/z礁�7��^h�yh��n2���3�*̝rw�-pW_��F7�}��l�2@ڝ'��k�ߍd�N` �$�����g��-�pu����̐���}����q��rޅ%��g�5���>\;f�]B�'��ẉ#���ʑ'vm��_�;�O��&�^R���W�r��]��F��d<_����ʶ
�u�'wJ.����?VAX*^$�,�"�
a!񥾹�x��n%��n�`9.u?�)N?�R�nK�3},���M�0�O)+�}2�����P�_��VGԡ��
͝�x�&U�s��Ɩrѳ����Aߊ�$�ϽOiO]��߽a$��mx��-KV_��w˟ZC<[T7��;������|4��T�1��p�(��ў7!�a��ȍ�ަ��Xm����~�R�
aXWBea�/�;�U{��ajt��<VVm�ܧ:N����M�G%�O[���•T�H�L8�aωa˃y�?�a!�
8��϶�Ob\M��z�?��p�H�+uNcQr��n��'��b�\�獼E��R
u�O����`1���+�ſ6�Z#9ǃUަ�O6]�����[7����ʾ*��0]�ј�+�p�	�'e��^#��:���&�Z�|�"�{���)|�f�u���hc�]��J��$��.6�A��#�,�������ϝ���=�?�礱�·U�u�L�����5K������Zf(��D9�?=������n���}a���V?j�?G)f�a���\]�c��ܑڐΒ�sq�rK`u_�,]��Uk�7�$�{�r?P�{��׻�f�-/Ė�����ܕ}�7�Bir����]�1�S&P�
�=Ӻ�~�ݲ�����KÊkT@3}����%�7 llކ�?���������|��r[��.��F�|o�H�Ƭ@l��C�e�|�t��GH3Mc`>�Kb���7��K��-�����*ҵ��u��24�,��*����K�1�z�pAr� [���b��b�������t�9����?����?���rBVG7��/M_���*{/�f9va`���S&f�u!�>e�J��J�ح�ƌ��#�Lc�k+g:=�tw�MCVm�S�B~�)��.I����)�U���
΅�M^)LcHy���W�-]޾���g#{��/��D����2e�};�J�0�p�a�{7l���lu������$��6�o&��OX����o}ژH��&�K��FZ�~[�q��K�����6<���/��T��<�B�T
.�~܃C3�35��m-]�w��U�n�ձ����HI@���wt�~���<��E�l��F���ʶ&�U3���q��E�EҾ�h71A;�š9�
�3�z@I)�$��BS6�b�*8���q%#Y����U���K��7(�Q������"ܡ��
���1n܍���;�枺������;�毴>��԰q����Ef+�Ƌ��	�p|M4�+7A����?RT��p�o��6�i|�z	-�K�xd�c������_�]��Z;=��oz#�'�Hp�DQ���GB��d̝�4����z�3�,I"��Z��%0��J�|���C�G�s�n�7�?�KN�g�ED9�1R��kG�b�݄��ni	�߾�oJ�k��zeg�2�e]�K�3�5$��bs`��
�ӻm���<�+��Q��p�+l��k�J�Uۮ"�T�f�7w�n
Q�oH����՝�v#
s��.o'^
�.ò�a�]\JWSLA����I���I]#ib3yӐ�h���jf*3B��)�E��e?����������'0.��Ag�s"�E��Wx|;4�S����b���c�	�1EY*1sK"{�_G�	������6f>�����/��H��>b���5�j�t��ex7]!cL�yߋ�u&��c�[snBZ^q���ey���������S�+�>ӵ%3�C��(�
_sM�7�]��aQzO�N����"�0s����_���U�Ȯg����u�?٤��'��ZŨ���\��mkDl�����ښ��n�\�jK{8"����f���J�s��gc��'J�S7xh�2��ضl�*�p+F/��P��%��ۃqv���8>�&�)q���HR�t��
�t�Y��y�e;p�
��P7��;�(f%����=핖����Ǒ�����&�;Fz�|���6��w�M1ڜ����bD�d�%����,m�&��)yj���lL)r���A������ �
���O�HR���� 3�x�_JE���q�;�Eh��-�Q�_�N8ܝb�.T3��H�������z�?:CO����]�+��s�V�a���7��og�	>
��.��0ԃ��4��������
�}��M3���3|��"
&�#�I�s1������Xf�^$�d����>��'4e��%��^�ߏ�$�l�V҂W�U7�!���׶�3�?r�����*���Q��
��~��Ck7!�/��+��~],�î�!�L�lh��0���S-$O��oD���~��9]�N��R�s+�Vs��O���g�u.CU��g�+�Ԅ=]R28bJ��Nj�ʜ)�Cil�\c�"$�;�����j�^�('<=Kt�ɛ�2	�U�h�4VQ�P`_=��,[Z���a����}j0?dΆo&&n���p���T���,����ߟ���e�2mA	kh��d>39��:'wӌmO{�=p3���0��{�;~��y챎m.$� ��^����a��h(���ǎ=Ko��v�2�d��~�=ۺ�@�h����3ն���?����W���-�l��ƒ͵�y]w6`����V�s���˨i����o2Rؘ��Tzg�
ك���U2��O��I/0zi*��^�K�N�ܳŋ\�/�s��4�a�p���m}I�t����j<�ޘd����?������X�0���� A,�Bѫ#}��_�޻_H�a-8��JYGr����f�{c�5��?̼}�d��������+�9
M�=r�����YM`,��ܒ�����t۝Z�a\]Z�Z��֢�x�иB����w$�U�f���UC3ojr�������rnĦ�w����w@�f�Li`x�ș�I�8��+;��y}�-+~��Ӗt�R�H�`�T��B��n���d�#��g���ۖ�8i��Z
)���:1��}w�j�ž����koh��ݪ)X��\�4&�$2����ȱ���������$�1��y����N��),�
�12i,��ݔ|s���B#�������:�|0ɥ9�v��h4l9}�_�wG��i�1�2�L�"�b�$����/y�o�����_���Ռl�dʙw]�7g��ǭ���3j&KjBO�*,^�Tj��c��,��(�g��~���_t@4�j�p�����߱��ۋ��zA��s����?���䶔��ΊJWܧ"כ�ſ��x{e�Q嘑�y|�&�i+<vA�K�u�cf�c�^k����|�3�%И#��P+2
~��ݓ^M�g�/�(Қ���Y&�y��A��*�[�,9�o{r*��.[���uߗ�M�\�-������j�����H�������#���� �&?v��5n6���eVB}��su�&a$\�FK��K�{���7�kuS��e��#�F��1�~�'�	����6��_�.V���ܕf0J�c�y�:d�Z���n�$u���.�	�xH�'ԬH�;���?����׈���蕌�:H,]��-�a�!�l�ϣ���/��K�{�UO�eC�t/L:�^�)s���V�\ȁd�)�6���u�
t``x�v|�4	t?6I>8
1R���X��F��U|�Qm��i�}�.#���z%i�r������?��O��o���F�4�k8;����w��g��8��f��i.M�z}H�z�ԝ��,�U��A���M�������@Os`�6\oʶ!�W��>�_�#��|�T��[���,��s�@�CXH��_�I��!�=f=*zl���� x���M9��(�q�~d��o�F\8m|�/�uҤ������b�r�t7tI?�\R��`7D{t�_e88c�Td�W��Tl����lho]�&�Ƕ��F����X*�R��%w��0���Ǣ�w��?)�II�F����|�׿��r
�(���ǁ���|�6��)�!�I�M��� S+mE�-�QA[$Lg�T[ψ�S�g����:(���҄�v�&�S��*�y��O����W����l�Z�
�ڽ��+��Fv����*q5��?5�=>�C����_wT��W�C�|mu�
a��ۙ,�֝��l*&����l��-Z5��y��Oy���`�_�߶�"{�W����j��_��܍��q�91W�t����2��x�/���,/�<��#�i���'�Ҙ��ա?$^��x��Ƿ\��]���z
��y��
{;��Y�(�.��@56�ؖ�͗V<�d)��gc����s��cV��g��r�����S���mt��l�����s�V\���d����>��
�I{C�}@b>�zrj盉�y%i��(c�OsiӞ��d=Zl��R�3�KL�J�	�����%�Q/�ݓ,��lR3�	�n$��(��3�A�6F�xi;��S��VX5t>�n�O?����Ϡ9�3�'3$���A9	ؐ��R
,����x@����11z'+m*SV8�v��b���l
����:������2���8�11�3Hےd������������c4��3`�d�3W0F�-]�����:��Xr�@�y[�w}ۋ�)�S���c�:��~�&|\�t��rE&O���ò�=W��i5�� u寁\�"L��4	�	��_�P�O���_���;,:��s�Cg�����?gݬ]���KM[�U�$9���Q�+���;e�">wg��G���Ū1�&�N|�@�������?�������5?�F&nn�0�@=+�/A�`O[6��넓�'�k!�|�L�OӤ���D�c�.�\^�����:�$��®�|�F���8�>��EAV�[v;�d��}}��΃t��z��9�����Ş�s!�R!�%���G�ݩ"��f���%;+,c��|\2�|E�ӹ\��/R`�l��i�����瞋J9�~�������A��=�*��O}9?�tZD��@�N���,wDw�kZ&��gȕ2�-���*����K����w#3�D���]�E�m<S$R��|�+��k:�̇���v
<h���?�1�P�6��tw����!uP��O(\8-R�P�툗����揂=�!�e���v|剮d�5��!���>2|Q��].�C2���,�A�+%���>;�Z7���
9�6���5"W6"���7lK w��"���V"�Z���(S&���aj���
(zD��T$�JNܙ����L����p|��z�����T%��_y��@�"�4|OC:�8�����ѥ�3ڗ@����4�.���Շ���/Ɋ
`#��[jj���g�)����	+�sE����3�{"I@�"�\�o�>�[�#�X�x�������w��!m�h�E�0�s���r'�'s�/[�+tƴ�7(��[���%k�~:yp<B���d�#P3b3I@>C��/�~��D�]�G<d�'Y�Rؒ;@�st��k�C⟾�|Bt�%�h����d̥9l��Jd���ք�'DT��{5��%�$@�(��/ῂ;7N;g���c���Ƶ"E`�^�5������(���.v���<�I9J��f�i�&�fE�瞫����S�;Uh�.���W���I�3A���bxJ��?�;N�	��˟f�$�L&ikf>�YjI�U"�)~mD�����M������3��A�cB���ѣ��̩��D�S�i�?�\i8�2a`
������ �ar������=��)���.�1��؏_�=���OOq�st;�;/w���W�Cd	�!�İ�������?�>�Yn�p�.6�f����0w���`p�zć6�3V��~���t��C^^��I��^�8���x;_���B�j	���]9{��7���݄LrZ�/d��5c]��~G������!mj�s��h���u2���2q{���,����	M	���O��dۀg�l� �Z�{��9��Z��J	?)�?���������� ���"v�h1��cV�ʋ��}zxŝ2K~!a���ᱯx���o��5
-��10;]�ۏ��}Α?𐁊t~[G�X�,����#	�]�?6u���8�j��c��_!�
�@��rz��Cs`��g�;��}c����=���������?����r�|��j�+�Q�K�I���k���I��!
��W�9x{��)�Lݻ!.�Eu���8���i,�0+�a��!.�/uJ�KTʰ�}��4�"����]d��O�#	�s9F��#s�<p@e�,ئuE��#����0�%*�V�F�����L�0�#2��G�l6�|Ae���K6�˨�GQ����Nq��Z��ň��`�pu�HrU�<�������9�ClA�}o�jk�B���
*ɦ����	��"����N��@��=t��'�/��+�6��!�1���O�Rox��_�K�!P�Q�=��;����#Fc�mN�Y����y6��ƿ�H2��`�ŀ�떌�1��
� �W$���E���}\3�Oj���J��|����������C�x�
c-�Gz��	���ͷf�����%\v�m�������O�kӢ�����]a�?���4�KYw
�
���I��.#m��A��'�P�����ML��%$�,� ���x��l�����OX�q-�]z�MP{%�2�.�F���ſ�������[x���o%�Q[��T��iO�`S��h�q�?"��?2��N!�?�ל�HsV�C�1E+�C�O7���^�l���Y{�r�Y�\�P�w�K���!�|n~ʫi��G�g^R����cMGLj�t�][�$S��;RB^5��.2+Iqzw�^�R�����ܸ�^�bI�;���xK�M\��T����pi���eV��ڳ9zM�ؓe����$��jΣ2v
0�R�O�����;��

�f�$yl���;v���MH����~��He����SrϒO���"�]�?���������̫w���@�ܙ%���#h�$����W�ʠq �-�4�d���"�����W��>S���E�k�7��e���zc܉�]�
�)����S�:~>��縞�~S��`B���O���������?��g�_�H�wp��
���O����� 76�Yp�R��T���y�d	��1Խ�pn�]�����
S�w���j
�xE��d{�Q�_�+����PE�Ls۠��4��e��›��b6z�n�1�Y���?V�Dz�Ƴ�M��#a�(a�W@�qK�
�q�;=�z[��w�hɵ�"y�xV�N��9x��Ƥ�y����wV���ԗjI���V�M
KZ����ZSHV���Q�8

�)`X��Nʎc���v杖�dTd3��s�[�S�+jT!<����w_�M6�i#�g6�2��T���|�?����ӟF��oqW�P�����m�9K�E��T�~K��Q�cσ&V;0Qי��j�iR|�l�{�OH�H�(H�?�H��F!���$�Ŀ|h7��e��[@�8����pzR_�r�ⓝcĹ&
�?y
���+��ai�9Mz��i�j&�R~r�%���'�����(r	�k,�@��~XZ�����ׇ{[j�Q������I���k�)��74�&�ܜȩh��t�Ο�;����j:�i�͉P���&HV[����	����[�7]
�S����/�FRq�?4"xЭ�yz�4����Jۣڢ�7g�?gH�잣H}�B��*a_~#�G(�(3��QI���;�7���?���)9��7��#N��(�f�W7^�Q]�;,��&Q*c�)�����6��gnc�Z�tU�2l`�{�P�_�h���o�h*%��85��.�\��ޙ~.��w�h�d��D0Z�Cv����^�Ӷ�:Õ�I4?�%��N�Z��������
hz�E ,��#y�#u{��������F�[�a�u�z�"7}����o�Wl��3�]��v��s��)iWd4��^s��fI̧�(
�m��ȠE�>�s�՜	����ca�'Wk
��l�®�?���9u��&�x@9�5��U߱7���L`��J��~��sx���������z��i0�J�՘�4�$�dQc�.�B���m�����h�6&',̚�?�~ӳ��q�K��f��fE<��U�Aw�������Q�l����9,a.w��w�_�g���>�R�k��݁z��XgIu��
&;�I���SK
T�A��+a�nb�#6&�/�YC]%�7�E�7M��&E4�Wq	�˘�!����a!�5�%Y���CL��U�ʝ�{����+�b�</��Q�#6J�ǧ���l`_�F�f�q�mZvC_ܲA�n.�uH���-��"���?�3�����8�氈�@�i�(�����$�)�yW�O� �-��'3}U�l�����H��I#D�v�xZT��C4��L�GI��c�af;vj~�`��.E�i}^����?���c
��2���߉Ӎ�(��G[l��N1��p��O�Ϋ��Q�zv�-?9&������?w��O���[��?��A��D"�ɇā��m�?����t"�7�O���+!���E��%�&X�0��e� �k��H�;�:���;`�$���!�/�A�������C[ئ����=���?������7h��)�s��w���}��"��On�R�?�l�C�|{��O�t:Wb�����8�6$0���`?e�ݙ���%K�^��r5����?4�қ���l�(9/��O>0�2��1�qR;
�tk��6w4×of����
�g��3�l�3g�⑓�B�\�cWج�Е�sf?����7��9M5o1�i��0K<�*��dz��wp����]��G��Ňj��Ϲ�߇�s�ǧ�Nu�5�[WQul����
w��u�ID^������D�����������7��7!�y��6H��o���\Be�3�5�x�	w~^z�L�/��ԛ,}���ms�n{���i��
6s�S6
Z\L�J#�羪���͡W 	��L�S�{;�t��]3��SJ�n<������?����?�U�_!�N}/���l8͇�k1���x��M��ʢ�j�5@ا�k�p��{+����S�O��k�;�z�����M� �	����3�O9ֻ�l���Y�5[׎����ƽ�՛���
6u�Wx�}�{��WLe��"��&�n[��7�Vn��X�� �}
��I�H��]]��k(�ʹ�k���y�-g�Ƈ1���•�y4ҿ��&��K{:����5�qM��o��*�{ɝ��z`�0��_i���y��*�C�CME�\kaJ�A$o�M0�U���6L�ן"D�{�!��h'��Ӳ�0�-R��P#ޛ��E����g����֟�$�v:k����?��z��i�V�|ܿ|�3����䠟#1l��,����/���;��G�a �Q�B/$������~��#�bI���)��ſ�)P}g���4)�
Śo��K���@�	��v_�C�V��w�Oz�S��߁��ſ�g��;�|��Z�#���� ��ʈ�[OQǘ�hl��u�Y���o��hl��o��?���$˓��su�*��gF=�f�����P���F6t����+�ޟ��9���� �gv4��u��mA�4n���2�g����?X@^����	9w����m�+0/�^��r&W�i��m܂�q�	�N9����7��D�!I�}zQ����\c����X��W�7��n�����u��W}�&�r��J�S��
��v�F�N2tO� R�ҹf�Q��9n��w��㒺c7��?����������r��@��ǔ�봸w��G,���,��K��H�ϊKS���aɂ�i���?{}
���FE;4m$YH��҈����
�b�id���cC#sp7抋{x
� �f�s�������?����j�.L���/'�-F�39�"�r@0��\���4_��3���G�����	��ľ�mfI�V�5�	��&�Q��ќ쿧;�����3��B�u��_������u��i��|D�9�79.��z	T�������^���+WM6�ؖ��5�p]3�y�,�B5oCRd��v=�,���K�B<J?բ8�KpI+WS��,� k��F�h�	�ƿ�>g��s7���������x׏RrU��|n�F�=��Dsc<�b�
1eJ^W*,��J4�ܢ�
���V�����p
�
0H��w��F-��Z��m�����|@��o���[ʑߚ�v��(��E�OHj	L�5���P��/g�˒�d��L}缾�G�)�a�L�PŖ�
�����P?hw�eWj;6�ォC���l�-��v��o�q8e���]}t���dc���l�[e��1���Z�w��#�p���Y�$����x˳�w⿿Ӿئjm�)���r��o��^�:��Eej���5��Y�Ij9P;*��+���K���ed�0����d�W��_guF!����.��Uk�a������4
�>�ʵW��-�'y:jd3��> w�ֱץ�8G��G��c�:uc%���-%�e�����A�f��u]�I���?%i��a���߹��cw�m`��.���;��H����_�t�]}��P�)��ϻ���12|�|9���qg
��I9�K��V��	�S�&���6-���,��.�;�	O�g(��� �
9���֘�ҍ�?��A/ˆ�۟�����@W�Ny����������_~�.�	1�ݥ��{PO>�R<?�� �����~�t!��?��j�2n��2ߘwl��k4�._ی�<k	۩�,El�W���^o
�pO��[oK������y�d_������\���:������?����o�f� Y�a�`���lR^7I~S�E���+��~wK2oÝ�k�'b��ҋ9�nt&L��9�e��G�3�~�'0�1�؅�EH/���%�+%'_����?[��g��6����-���sso@>�n.���p�%l�.�}�%_a��%��@%�ge�f��+�֏f�E�<�����S	�eF{�1p�ͷ��{c��.�!��>|���r�a/pNj�T��������n���!�a�X�٨�Ռ4~�96�����$����rA�Z{�-p�7�ݲ��6kY�P��O�si�!��_$��T�(���+Ӟ>�#t��B(vk��v@�E[��`Q�'�&�c�̰��5M�
�ױ�|���Ϳ~���x�H3�t}�N�;\�#�M��,��?�J��1e�RT�cN�IbL������\���w���������21����W��w�^k7bL�s��S6{w����
I��5d8
m~�}��'�(D��;)�����]}�D��������A�:��:.])3��K�y�/xS�����Y��캶H!��垆�x��eC�1zly�ܞ��q�%Y싊��u߯����7�3?y�gt�2
�ô+�_)w����ļ����mVKb���`4���̖�L�ɗ�0�ĝV���-~>�M;^�J>
�M2���Oυ�S�x�ʥ:�x�K�N�c'ir�ؐ����=�2?y�\�^
�
'炯����4�"K!ݑ?gnR�X�t���H�O?��00��m���L�k�m�]s�bI��%�V8'�z	�r�v.�XS��/����;�:v
�����M{��	Oz�.bq#(��Qu���������ջ�j\���������j�9t�BrM��ܡdv�Z�S�T�*��Q�#�U��T��v��)k�Θ)<9C`e)3?M��d�V��/��p���A���I=s#�~N���|%��O�촱Z2l��/�ê�4������?��/���6و1��*y�2�\���L�R�4^c!�#Kv��!����z����n��1^�L�����d�:V��
Q���I���seO/AST��@!k���&��5B<ܧ�h�GOz�[:4��}������p
��K��$9j`}L��4��Wy�5:��WEC�B?�Hr�N��u�����	��\��2\��2���zNX�|ZSq��G�Ho���[����9B>P;��Oi����I�up�K�pd�OJ��t�#y��U��7�o4(b����K4�u΋l��r�
Z��d��'F��g������dp��V*”#N��C��Ԏ�%�w�&C�S–^W��u
�l�������ʿ�Ō�@��O�wo`�j*m�2+��L�(W�M�5H�%3��d�.n�a+�Y��M9���9`�Xȿ�%�C���ռ�dv"'k$Wrd�dq�U�����]�<_��.Ϳ�#Y�˿��W�?�c��O�L]���Y�,4<��v;5x��	X�)�(�1�^�l�	['�PGc��t��'gs�1<���=C��#�J/�ҥC����K��;�X} :%�o��Ӭz��k㧳`�P:p�hI���
�E���_-��v��H�s����$�LX�?�����I����jD6��9����XeJ��k�mͯ�r��eDe�7����E���b�^����ͳe�B�a}A\��@b��n�;�c��tEg�z��řH�Q��9���i��
ņ��>�t�f�@�|�)'Io2%���WZ�t�w���dW#���|e�<t[ǁ(J5w;���ܢXeO|�B�C[�-zLQ9sg�)���mSZ?�%��]{�V��G���yZ�c|}�:g&z�k_]7�}�1�ʲo;0<��h��*>�Vi0Аo�98ħ�JX��0����~8�x8�5���� ����T�E?y'
�����B͓~�o��{�W=�`D�V\���Z�6����hr�̚!���A[�~.���M�n�'52l�$�I��R`�$N��M!j�p��A��״�����;�w�����=��l������Ke��w����i���}�On����8�+�����f��f��{��c��ME}w��C8`䲲¨_�v����M�f�2X�6���Z�p�b�;����7?�B�m�ᗶq%3R�}�e�
���(A��������'Ѿ�6�AcU.��''�cmp���r�i"t�D�%���b��Q
�ILF~�Y�w�M�fp�N�%�dJ���.CEJ�G�s�im<��������;�w������i{���������q@X)x�S���dz���Pd�:#��mJf깇@�~��n;;����Y	/�j|�NJ�vT|���"����A��L��O�;F���P܍�
�ͧ��-�5�)��P��B��'���;�|�G�J��xi�@,������j�!;��%�0Ye����n4����}��Z�,�;����9x���l�Og{���6�qDH>��x<����x3.'d�ܳ�1�bR`��<���5�Rf��CڜLՐ��<
i"����(��c���U��tH����0�z�_.7|c�u����D֝�x� ��z^�s8�P�����E��A��I���F����CV�:Ka�s��%p`_�I�#��[�:_�q��+2U�"��yx�Ci2I�����<�6C8�`C�\^޵K�֌4��]��+;��7�=P�2E'�jP�5�J)����
`:k�������o��p��ҵ��y�cX��#�_H�N��9s'��jUO�N�S�Կo+4�d���و�d!���-9���je�@�^Gh������O�3�<��?"�������	���.�4�u���.dI�Y��b:*��t(�h����#Pm"�8
�3�bL9����Z ���&��-�?��F�|\	_���=�����2;i�i��?�u�tXw,4��,!=+}���`���j����mK���E�E��u	IJ�?Xw�A��
ӦF9[5��k�s����=��4adp8�eš�w��KaIk߬!��h�����|�?��L��_A�OS5k��$ؑ�gr[�k���d�����	Au!�!�����;�m�
�x=�8���)��8�es<R���i�y5	(/ybs��i����Y
a�dk-�h놄F>����Ҫ�*Ȋ@��`F��$��<u�L��R{��d"�����cIm����lk~���-"��P����ԁ��k����Q7�ᮖ�^}���1[/Rlk��LD�uVN���vb��o���÷���W"��%)\�]ǒ{d�Ɩ���W�\���.m��Q(�4���@H��d�3�	h�pE/Ŀ8�=GduP.y)\��6�c����NEP�<)T��;}:Y�Y*U�e��c�±z+:�Ҟ/���8�A��K�\3�:BL��Ԇ�)�H㜶� pz�{w�2I�#���=��X���ṫ6�W�А�(\Y�i����,u^9�Q�j!��W$�[Ѱ8j����-.G�'�t���{W|�5s���hCغ�d�t/uN(7%�
����'��
[W��\!,PؾƨB3�ުTʎx!���X!�k�I������/��	��$oK�u��Td�Tq��t�C�V���Aۭ�m��fy)¡o��쿊��Gw�ۯ�����?����&�{d׈�vܮ�l��b�Ω�����+�gzgӰc�3���Ѡ�v���3���畵ݞ�[�xoV�%%}Q7��
U�J̭�Wt�h�9`
"oMY󅐪���
W)�t�ݹ^g�g��v������������X9x��C8�I�6W"-�P"a��ֶ���K�FGW����3�ˇ�Y�����
W�ϖ���{��6��w�)��{���Ku��c���Ĥ��x�K
6�03S�n�qb�YC̉M%#7U�J	����#��`:NoV��R���i~Fe��~�8~���&kC�f�OoMXxt�ڧ����/�[�{d���"X����A�-�6�9m�A"G)�*�Ԏ�;��E�G�x�LHЂ4}0?��P�G����{��H�_��ҋ�kN���\z�rUNfM���U:�^���t��K"m�;#5	
��bX%��m��	�!N��������3�C��?��bTW{��!�ҚބK�y�8o9sC�<s��b$�{Q��8��W�?�ـ�����g�I{��C��
�A�8��3��aXϲ��ʭ����?���¿с��G��gT%�[;�r�&��bg�2Ϙ���<#Wx7�"��o��zTt@�gD��g��za�V�3��x��	�T��)3
LH��0�U�n~����y�P\���f\pX}�v�*�z)����e�k��o�W�K!��DX�FD;}'�O��Ǖ�s]�l��nj�)e��uZFv�s�_�����Ȃį2��w�qg�~V��\U��ۅ���S��z�6��K�BE��a4	f�;�������G\���]��T�CJ{���?x�7�4H�vv*g���1��l����JopT��>����װW92sB��(��Z~_��9k�zRC~H���1�4:��ƗyH�o����M�Uk�BU�Ez���넫AP�e�6ÑV
?�!p�(��?E�?��������G����	w�6��L������Ɲ�8?�4��\�����e�>�W�.�����<E���L�Os�V#z,��G�ޠ<B�Ե��<��*9C��-�p�I����M
.c;*�]�xmY��`����?�����_��Aŷ_�o�'
Ej�jl
��g�oE����Ɍvh��C�3��^�W]y�.+p����>!8�2Ȯ�.�f#LHc�7���&98�;��=�+����l�K��4�m���H���$]J]$�d��GQ�wQ�(��N�g�m��#��,�=���1���4���f��w�v6Oφ�GW��\��ʵM�gO��Ǟ!j�I�?)��_ڵ�k���خ�ݨb��>�ǔ��h�V��50�#nM��� i�䫺�ǹ�v�9HL�h*�\7ܽ
�4�cG|���	{~�(k�{�?����m�Ģ�O��X\��$�����c��ۉ6�P�=�Z9	qň�Q�����'
B�cٍ�KS#��!���R��u��G����=�1Ll�k��qTYZ�a�-���Dn���rW�G���d��h2fH��D�ts�ظ�#�F�{Ó���
8|'K�#މ����X8)����W��O�����ט7��A���ȧ��)FR���5jg-�CW�_NFj$${��1���2�a�����!���V�F���T�8������n�D�+�W�V���N���U�Y����Z��?j���(؈�����PnH�m�j^ȃ���摂x&%�$�N��r��B���
?��ޔ/�}��mH2�����,®k��v0�Ж�+۫p��nπ�W��}B)�������=RE�p�N�T�T�o��z�J�:�&�u�I�2?s B�mtMj�y�F��
��em�p�@�B�*�/��&Q��;���V�r���V$
�`���1���W��3��r?Z��.��pM�W�R��;{�J�����������?���_���c�w#gCB���cS��݊9�!�U�n$U���zAE��*�]|�T���r���ĥU���w�O��)H;72	`���f�oYpJ�ߊk���|�`"�z����p��F�{�k����?������݉
��ܶz'�[�.���V���I�vD@rw���,צmR������N6ry���K��m�H���7>+��at"�^y,�VWu��f��ȅ��yb��l��WLsi��+�S��'?�y��ڌ+#
s·�>O���)��
��=�_�UH��l1B��}�~ړ�t�~��m�`0ȭ$;6���ڤ�[pr��X���UOc6�op���k�
ߘ7�
��GB��׋��z��XE���f��k��eN��:G�I" �W�˖�d�H�(|n���ƞ����s{�l����ЋWۊV�=���g{D�����f�̙��|��VS�]O*ڇ��P���c��V��_��%�3f�3��$�J6��m�m����B��ӉK���L�<Z
�_��+�=�8[!����ޗ��PI��	�
�7^]�h#_!)Wjj���/*N,1�<��f0<�#1���ͩ]���0^���#|�a4���z�.��29-�vS�W�6�7���/m��h��q�\X�oL��SWi�H�+7�;ߣ���TI�R����*��L��g�_����4������v4�5ŵ�^�%3����ȃ�����-8w�ç�O�J��Q���J�]��������cʎdfn��{^�Н��L�����B0�%�U��?�q$
��rdI���|�?ɘ�{����UU��{hs�4�(�	i�yWm������h`W��ճ�9�
����hL�/wg�BH3va��o��Kh��7�n7J����s��V��d
���o17Tj趰����֖�S8�
�:��k]��ۑ;�����Ozd��Y^mL�a�M�P��F7��u�K�q�M_6v?~�=���2��,�xmP��,,����������1
+�e?�w�
�x��_!�u�FE:]n�qB����Ù+��I�_���P'��yȄ���ݔX��W��@��CԔ���صI�V���T�z�p-aʷ�W��n��5nCT
�4�r����?�������h���.��\����wn2���i�5*f׊��k��c�	Y�	nؕz��y Q;:��v��5�s��6εo�}��}$�1�G�L�c1�����%�N@����?.ٱ"��)������U7��>r���q���/�+�����%xƾXnUT�8
V*�!Si��Ň4��l0��L�޴��O���*B�3or���@wx.�n៳��WE��|����?r+��9��9[���?,�b�+5r��RԉX3>>�Q��Or��9���>>:T�/�8YcD:�����:��J�{�Ob�k�Cvv�d�}��v(P��{ߴz�XV����f�(�o��KoY{�������%8�v)�h�^���1e����������8)_F������\Ů�^�~�
v�?���@��k����-7�W�۶YW�q<��R�Ź��`5�?�����M����'�K|�!�����<��?����s���joߚ7���o:���O��w	}�i�N�;����f���܅=��,�!|H���%nk&V��l�����|E��]ʾ���o6F��*גo��R��)aP�͑�)��zc���7ɯ��p�ΑR&�U�M�;͜E�2��u��(h���q�(��ש'�݃����
��~�iLY��L.GOT�u�z���fw�K{B�΍/�T����ړ�bP~e�
��7�
��ܼ�UV�^�}j��*�X���`1��)��nno1���oȁ�tQ�!��w�Uܿ����<�2d�S����N>�oI�dX��|�Ph����������a-���\��)`�T��_u�9G����S�,�<�o�]����&�r�x^�݉x��SWմ����^�rU
�U.����=5���CaG�B/��u���r�\��ՊM�=�����*T�ǻCs�����������<��V�fQO]0���R�0z�WU�P��P:`�1�6���Q��ŠRN9�窒;����
	}Z���n=�����lr�7��
���B95�����sI�]K�ƨ�����b{�5n�(�WUJ�^9wjWm"�\��͍�		�7*Ř��<~��#�s����	n�7v���*��B�#"-��'ZF�sd�O��oH����T*�H�tBHWX)\ݛѥ��0��,=�ﳥ{N��D՝#e�Э�/T���sZ1��0���m�Sn��B�TkP���?&HL(���	��6�q���X��O`L��9_���q>�pZ������
�����a���M����oD�0�;ܝ;��5!N�7��d���ȥr`?�9�e*5�+{�h{�i��\Ki�cfH��n�?��@\&w-�f��Px7������ä9r��p�\�?�&~��72ޓUx���S&���F���^�?"��9�*I�}J��0ڣ��Em��l��J���۷��c�1И�_�o�3We����3��Q;����7N��
j����~��P$T>s$�V��S*m���~�	W�������v�p�Tv����s��o��da�BiaM��oI�[����wj��|c����';����b��� �<�7����qrВ��x���s0-�jؚ��v��kCz��n7�#L��Դ��i
w�8��嵝���Ҟ9��Vʻ�M8��x��h�f�n�
��ϴ?��yc�������Î�ns}�0����©]{@ZI���h��͕�.V�f:UR��W.�`�o�s(��+�$Ȯ���N�mU!(i��Cx���ZG���;�x��ߝF������o�ֳW�^��3o>���+����?�������36�O���F���W/����+���m��.*L�YL��-�y/x׿���{�m���ZR�g�����8�u�Z��O�mn#�_-i�dpwk�U5��
bT�/��U��9�n٨�O�?�������?�O���߉��rb�˷���|�]l�Yl{R���
bq����&دr��v~]����Kp���BN�v��q�ܜ�(x�մX����q5k��괋����ǙR�_�Lo�;�^"�K�	�~�>rB
�5]x���?�0�m`��ᅦ�9s��9g��|+�;0!ȟ��c<��{�}&{�6y�z��UP�7Y~^ǩB��KJ������(���4D;�ԕzl�f����1
���������o����z�`)�?sU�k�n���r�*�C�VV��w�x	����N�"�	wsp�ޅ�/��'#���������Q{�s���xr#";�ۉ��VAäN�(�L���_	��e�H�/�n9�nd�|/�绺/��T�*���;���q�}��c�?�S�������'��y{��t_e�{�����e(��y�� �ׄ7�#������_j��?�kw�W���S�|�@����N��.ASqI�W��6�w#�l3��%'p@Գ�`h�Z�����y�^��}����W�b�)��|�{�x\|�?�������"k�4���

�)��LӀze�8ȣ]O�MAu�5�	i_
��'������+r����O�Q�4a\mn5���|�W�|S���'G��+��0_�T��:����Fa�S�@�{���̿n=~�Z�ZXж΀�.o�Vg�W5f�U+��l��5��p[o�Ƹyz���e���ss?�R�i�v��R�������P���ߣLg21�_��vC�oD�A�>�2�5���^��s_��m�J��P�:vH�;�#ʿ��?�����T�ɑn�<��M2T6����jZYߎ�+t9o79r�!�0�[|�,��^��H�&G��V��.
{���s��� e���A�6=�tWM�o5������3z�G>�Aɔ!n���҆߆�O7q���������?�����x�W��γ4���(�o-�W_3�8����[~K�P���V�s��wV�K�M�zSw�#O�=k�n�2u����l�ta�M�^�^��P���s�T��=��.��:�n�V) I_%}L:.����P��+۾�;
��IA),R�6�,/L�����{�'ǝ�G�u���?�&v�����|Ƀ�D0��LUX肇-�2n�J�)C�ݸOգ+�;Pw���o_����[�o���R��\��&	��e4ϛr�+��6+�;h��v?��;(7�=�'r!��$B~�75��eN��O���̠ڇ�g̵�Բ���w��K�׍L�rd�\
�#�L��]x�����](�rtq�4F(�y����c�i��RUn�
�-�j)5$�_�O?������ʭ!�b���Y�O���O
�ʄ��!��MKrBBve����7��}:��%�&W���/�|���
�{�p���?$�T�W@�m�t|�g���X󫏯�I4A\l���W�O���~����V��h;��w���.]��.�5*&P�'��]�y_�R��[?��u�����`.�
�3|l7'��������;.ܳ��n{z����A�y���y�Joat�����5G{�#d���'��3|�)�a�5��y�_�aV�
�4��|��X��u�`�8�������"ۊv�
#�F�h�y�8k0����	��W��Lj��T�rG
tj������1�Q<�oc��x'>�
��!:���?y�.!�I��vF[]:��b��[���}<n�tN�3O�M��	�W#�v���=(��:��ȡF�4�G`>_�0ǃ=y���a%g8`E`J�@��F�!!�di�
�|�0�0��s��tn$���"af�&���*�Y�$m��
�dF*hl��O�J�����������+�W��+Ӓ�<�3��Ǖ8u�Y�10n6�*8���R]��%J'��H�����ri�<�@n �x�‹}�!G��PR�m�Zb-��\+��
��8��y˸�"��t2��㋥R��D�;��H�
AN7�J:A�&ۊ���O�Գ�?��UE�@�ˇ���*, m2����J?
IMHx�#ly1��T��9	��t	��C+��
�,�4AZ��|�؊��+�W���_���?׬��_��v��1��}C�|>�\��XO��oɘ��4��w��<%�����a�%$�z'Uc�O�6���Wv��٦/o�|=?x�an�/���Qn����R���^��ԍ��ܮL0����"YAG�1-~>H�Ѧ��7
��4�)��EZ�"���:'YdF,N��i�!8� �ɟ��E
@�D1�J�wf��P2da�!!�x��ϟ�9�Y8h.�T|q2�;���+&Pʶ&T��	�����9�o-�~N;��؆n�~�ĵ2>����R��]OH%Ũq�P,�_d�'��!3l��␙��6���%g���̻�����ɤs��uI�.��r�ET��U|KrL/��$��XB=ڦ�V`˖λQ�R��n�z�<ĩ�����߽?�v\��x�i���T��n��[sLEp�n������î����2�29�@y`7��Ϗ��x~�(��K*}�}�)��;>�����r��7�  �@4���{{� 
��$]�Q�I��,N��7 2�:�"ӠK�qKA钶�����-Y=�L}Qw�W�h2ə�]Oi���U�� ��<V��Yi�L�>��ϭ�X*���ͅ�K�w�l�	
+�{vy
�4|�&ؕt���ݤT)�����{���"�;���6mwO�J�����q?,��"׈�*�Ԗ��>���`��V�Ұ�q&O.d��箱�[�Sr�đ	��Ru��A�v��9S�N�⟞�QKt���y�������.�Y4
+������|%��.�t.z$(֚ �YZ�K��F
3%jk�0M��">����"q�%��r�u5�8�T��m�?;+�0�xG:�$7�HyK���ϼb>#��D)l��w��S��ĉI����24+���_���F��pXp
�("�j�v9lY0Ӛ��TT*ſ1(�ܐ��"[��o/�ķ_�{][n[K�޾��rN��
t�q�$��G��00��j.����YK�J"�XR�"M��D�e	T[��ʍ����e�?�'
�u\%���_5�Эd������Q̨����>:(ܢ�7�7���_n�Xu2��W��}���cs��q���)~�?_�`���؞�4-�0�7`���a�	�:������d�>�w�|�ZH�6�
�#|0��?�>���;�zPk�*�uA��A>�,�3H7k���mI �n�7X����D��d�|�L�@f�9~0�W�I����U�Y�����Ii����t���W�NJLs'n�^��1#��a�뜋:]t)�Ϗ7��S�Ԁ�f��+G0e4d��({"��ӲH���a0�NU�[�%ϥ7}��e��eI޺��&�፾����������ϙ����~�X��S����9�,�I?`P#��t�_��	�t�@YSM�w.2A�o�Ks���Q=��r����$�[��m`y�WE`z�H�ڣ����p�w~4B��`uX�I���q�+�*�V�X�y�o��7�������?���,�Q�_
�-O�n�o/.
cm�q���:�W���f�$��j+7M1R��F]�����R9��a6��H?#�η7�Tp��8�rA뤽)K_����:�+���/��)�l���ء�r�[_5{��(���i��H�II�y5I���8s��H�ps��dLd�0=�ϱ7%+kQ���5M�خ|�`S-�D n�9�3r���O[K�f��B��⅚p��`F�bn\U�h�>��p��$����c,��rC|fu(�$?�=ǿ��o9�O������I��[m��w�zS�L��v��>FIe�?Û/��i�ߌ�qS�)�x?�W������S�`8x!��#"u7���Df�w��߷1�:dn�-R�~�0S������ǂۜ����ŌP�����{ΐ��o��O�x�zt�f����M���hY�,��@�� l�r����?�i�|>h���������$�1ǐ^��?�i�׫�s����/@���ʔs�Ք��k�N�Q@}��u��,-���`�ߙ�rMx5Y�`8hO��C�JW����>���ԁ�_�Igr���+�vY�
ؓj�V�/��?��Qݍ1B[]�U�C����B�'�3 ��Hc��B����=��j�Z��3U<;u$B��
�P+4iR����:v �!`���u6���$�?B�ҽ,4�'��K���Pʰ�V���E��}"i�ؐ�.�)�H�+t�����9��
ד^�!ЕK��~5�;t�ڿ$۹������7���.�opY��o���I7*�d��f�?k�3ʂi燉�:�ًg}"h�W}�z5d�+��I1=|��C�'�I*5i��B���MRK����D:�%�|i�)�ß3_��!���:�+o:�J3�Zᾧ�N����0-nk⸊/�Ǵ7��d�W�%�����?���w��nDe�3���ɒP���Yt��4<��%�r��Bwe+�/Ӥ�6��Jw1�\&�K�wi�y��tu�
�j���o��0!y���}�˲7�
���Q�K�q#��G�j��N���R�?��������3�[��$xN��֑c\ߣ2m���7s/;�TR������d�E����s�x4ɑ�����=p�:�M�+$b�����@�k�܎3��O
9���jK|�_���UCk�^k1�P��)�Eo��&�8�)�Vs�r���81HVP�-n�Fꄷ!�S
�?��O��k��]�F��+�My�MԵcG�y	*%�~�h��q7�O/���t�!���Y$b0r����m'�f��俈�lO�X��k���`��|��<3(rYJ���+8DF��iX��#q��̍zo;b�]a?~�g�\㚓#.�6N�;�۾a��E�#^9λG�}��pF��\�F)���R~S�_�ʡ^bH�L�1�H!��?�9Gޖ3��)N�?�3����j&|�Sx��[���M��ω��ǀo0<�(Ohr����R�;���b�?��%S�n�����|9R!��E}!,����E�b�>L(�l��n�>�qv�H��g�C@��O�+J������_�ˁ���N:
�)(��@���j�c��[��YL��~+KU���1i\�����!^���3��ip�{��w2r��1��c��3�����{�<�W3�u��) �QP7�G�J������:��]s�9��W�������ȧ�_ۚ�je'�/�ܗ=����(���*^��42�C�[�����K�;��#����M���	zR��j/�c����
Hmӫ�v�k��bM���`:�IT��d��������Xvi3S�vz��N����-���l�۷o)���_Ǔ�'�w�!\^�Y:��x����軰�"�U��PׅJ����R�r��$ْW�p��7��zZ��6�z�\�\���VCYy�!�� +|�rs��r�媼����]�J��?�������[�R��1/��z�<0<��U�i�i�ۀi�=��;;���N��b�R6�ҼtK3��?��+�<���>:XfשL��-Z�:�Ň��D��kcv��H�d�\�w�l*�Z���Ӹh,�n6L&�{��������?��K�q����?-Ep%%��Wւ�>�m!6���b�P���w�
���olN[�jKeK�3�sXm�G�GUg>���/N_ȇ{��g�#�9���S۶��2������FҐ֡����F�)��_{�hr��E�B�9C�K�y
NRY>Z��r�T���[�K١��_�ooUM!<k?ũ�(�n�e7,L�<�o"���������������x�!#"f�ΜQ��`(�����Za\�l���d��'���Dʇց��*��4�dH�d��#^����|>��0�R��k ��4��Ƿ��b0��0�8�d@Ę�Y��#j��ru��B�k�Q;]ip�o5�*����]'���Qa^��L`S����}��B�d����FY��1ir�YA>�h��w�����wΧי|��1&}����IP/�Ù���2tc�5���⟛������|��9nĉ!@Ŀ�#p{���v��\���p�ռ���ԗ��zX����*ۍ̴�|tVE�T��)\�w˺�}
��,�/�9i��D����?��.���!.�����Q�/�s�g��o�w̒O^r�sXRF3�i�n��p����?ŗȖ��]"�F�#3���/�a�wFʤ������ϸ�I�
z=�yI�Lq��~>��ƙ9�ꨥ`�kƦ�q��,9:�^�sgJ��p���A
=Sz�=)��W^=I��!uoG�U﹘�y���*W�_[��fw{ �7���N��8����ø�]z��;�I�dЬ�f2�.�	�5�A��W~��X/��4��/�g&*�Ŗ|��2��h9��������n
�9.�%���~ͦ�UTИ���i��٦�Y�z��#
 $u�K0��|�"��l���A�:6�5��L�89B��9صI'}b)nW}S~�������w���d���}�,�v���a��s�ѹ�������r[P���;[��r�ڷƅ�}�,vRN�ntx�F�o�^�)o�ax�=R��Tv=�."��M��o
��
flO�4I����eYd�h��h]G���b~�dݕ�w����?�������|s�&����bjH�{�@�SǯR������T��Ȕ���\�ʖ�l��vm���dV�xו�Q�WS�''8��i��|e�����k{���
��ǻ���
rUjT�����m�"�;��y�
��	�p��'lL"��Va���Or�Vذ��K�_�,J�D�#kE�5�E h��\����ez�zM�)[����:��+S�I"\��F��"�J�D����g5Un���&��DP.xR�A
lG��7ɠ��u������ד�l���
�߷zB�s�8������/��_��#�Ŀ��9�FbY
8ῃn�s��$�\I�P�o�%��Л_��DҶ�(J��*b��}^PN}
�!���E�4)��%r�+#,�I�5�?�?���3�tǔ$��M��jN��t	j��8�j2w%sr5��k_���#U�����w�^��j���'�g�H�<��?�"E���F�����a��:_��M4?i��m�Da9�f3TM�����8OY$��n
Ξ�_�������9��ϼ�ƗK��S(�p����q��h�dj�3�G.�m�k�.���Q~�[�/�~���&�J�J@���3�vR	ϊl�:��6��;�+�_3���"������
�K ��\8O����k��o3^B��i��1²M���!�=/f�o�G�s�H~md��f�������>8����gCw�pG�ݱqsZϤ���_�I���G���2��vm&!��Uϩ�4wx"�QZt^z�`�;}����
���q��ܹڴm8�$�����#��]���y4��W��,\�6T����?������6ƻ4<�K�}݌~��Ox�C!�1Y�¢۞1��
��N�ew�-'/�A��S��M�����H����Hy�=C@v��Y$؝7!4�&[Im�?'9�,�"
aä~����P�;�zY�:�z�IVki��ˇW�^�j�N�?���������}���ևF�Sz'�R�N�O`N�2���F�q�~W1�(���B@����4�̓Cm�����~��:�~��]��_%2��[5
<��g0'�^���?��T���nc57J����jǾ��j�J�n�h�լp��.������'`m�,�)�ʳ����'Z���H��ѱ�4���>���~�!6��#�\Q�O��x͍_�ը�����_����iq
$s
�7�)-�F��`n�&^1��e�HS�2w�0��si���ŧ/��	3m�8�	�"������OF���QG��˙~�]x{R�(�z�i0v4�D6��4m�A�Z]�Bz�mɳ'
���?��e��g����%v�3���
�H�W���c��V�0��g���akuj�ER�̜�}0Dj��u?z����d~�#�8Z���z^\�G��7�
��\�:m����qa�g����g���f�����y>�@�6Rw�ƿ���I�^{#�S���(��˭��s8���R�t�����n�|���4���X�L�r��o���?�J��&7��ֈ�5g��\P��	�Dn8�ۛm
�>]����Z���?*�qz��CǗb��)c��Lx��iR7@�B��	�����ů�#�+gz��Os���/��*��j��:�ܬ�j�	7��ڃ�?Gr����JZ��,YӖ�A��/>��M��2d���¹r�kf�_�Mо�{���t��g�LN��*p3�jw����<�!^g|z��$n��q=��鬀���4��2�.�+�y �,�P�K^���ʄ��-�{����"N���,��[�l���S�(�0�Qpw>��OT_Ʃ��:��㕲����������/V��d3���X���wƇ]M^�͠�7+A�}�����mʗ�elX��(���j��К�ףy��e�0�2�F԰N����+��nw_s̽f壶1����u�
h^'�x�۲p��/�o'�Ճ����?���߂��ܳ1CkA�݀��h�s�?��	2��.�'��lj����B��'d��7m3ώ��|�Ú�Q�ni�D]�"F�>c��)inl ��_ʍɟ�Qv�?���c��c��Mlg��jm#�<��Q�������(�i30���ˬC�����@�+����b�Xnm�[;O�ɪ,O<���6�����W��31�4��\Ӌ�M5��ja��$B�9<&�<�R��%��k�Ό =��7��||�`�$����d��^�hp�����������
Q)���BMrn�N�~sB�,��B��2Y�(;j��nvмc�Z;@`3+�x^���|���m?��<gD��_|�ϛ�7�G�·�� �k�:1,��\�����gg�\ΥiZ���t����Y��[�6��x#M��]hg��?�u�P����� gt�:��gp۩�r��t��R3x����W꼙ͧ3*ڗ������1Ɇ(K�4�!�����n��81(�!���gV�����}�<�_����ږӆ	�.V�=Noh���+D���'�!�d"d̴�'l�_}�/�sm����07ʙ�s�5�i!�{-�^Rϫ7^���q��U�8R�3!�ų��L����>�9:��%��8�M{ʍ�)Cn,(�مY6RV�)��:B]�L��;��0��K��^�^��vC�&m<>o����%F�Úg1��K�<sٛNZ�(�4����}�a�0F��D�#
��ڇ�j�Ҭ� ���p[�*2����LZ�&�a'��_��a�͇-O�Bh^]���}?v�4׿r%5��HΖg���)�3����\�}����wp�:�P�w\IY�Ԟ�V<��������S�S�n6���r�CJ0|FA����Y]	�,ɇ��"ix��ٙmy�˥��}3�	�|�|;�8|����D�u�ݡ�G�ΊkZ��n{�7	��~���ϻ�h��P�[�3��|�K)��?����5������?���ߋ���S>��F2~2a���E�;�� ۻ�5�����,��U͙�*��2��3����0�šm�[6BLȜ;{ui�z����Z�2�1L�go�%��+x��%C��I�I�1&�Vh^�����8ʸ&%�
��{^\�l�?`��r{��[W�r�o^Š�GǕ�Z�{M�۵?��?}�*S�g����Ť�ү4������YĀ1o��R3����,
�!u��o����o��+�I�"�I���2�q���d+v�M�2`/9&h�O�/�H��%�3����[�h�m�?���O_D�9�":��_S<t�;��������pg20��߷j'E�]D��W5�͍����J�'��Yhs؋�#�c��X{�0���H*ԜW�s��f���.
1*st[Z�Ӥͩ�#�lUc�%� �00�[��ר�_�Jdh����b�h���Wrr؀�i�d��<�g9�?7g2f�$�D�с���!��R�1����S�I�;.�qo��[~uS�<���S=	���W"����~����I%�0j��7ݸ�W!��Wʅ��;69iwO�l�'��/3�4�����Q���4j�,��k����J����sv�~����|����� �q�?*s�`�>Z���֝��8�kb��2d�P��t�}}���g��dϱ� n`�&��U�N�w�f�:�84+���_����Sٞ��rQ�`����.��z5�w�c���\�ڬ�F�2m
�'�'Cq�d�0�I�|�}�=�6<�@��>��NV,:;��5R��aϺ{���	K��p��Q��`�ی���t���'��1�;���S���������k��ћ�a�vV:�1{�&>�D|"�,v��+�Q�?ѽ�6������j@V�Oxst"�]/��@�Gy���3��C_����J�H�����;tmĚc��Lq葳.�24υ��8�B�)&�@_:*�W}������������7�5���.nn�
8��P��m*ePA��h6��a��Λ�NKe��rYʎ;����O��J�`������؟�"h���1��/����p���͑r�@����3���+���A�ݿ�:��3�2e�v�@N�5v��BBx;��T;�:2;��T��BN���瘮��p�@����0��h�l����
�r�0�x2
������?����o��*��NsƕNx���\�]��*r��2��0{i�A&ϝxW3sn�uD���S�1>�>�}op̔�I8��Ɋ�)���/��YseX�Q��k͟P��JQc��ا�APe�d�h��^��^�C���_��t�յ-A!�ؔ#xh�ڜ�r�ё
�?�^S���+G7Ik�����-Y�}��9��0Yަ��c���V�]��]�u�dI@��s��P����B���a��yW��vA�k3�ᇿ�k����5L&\.���f�6D.�>��\?�1��r���t<'�c�1�YV�fx�8��ӏID��5uz���L����������/���7�4�+�?���&���#\�ό�ؿ�`ʘǨ������1�$'�,�M14���&�KK|��i^�q� �F��7_Ձ����b���E��J�<�y�F��4�����"�c��?8�A/)#��):*�I-�l�B���oS��CI�)_u~BQ�@�I��7��`��%�3߾�5�<��"�{ǿ�-=�)*.mK��D�/���N��Xt�`-�_��=[��I��$�
�?|�-�S�#|V�_�|�?On�?�ڥu3���x<80���V�������к�I�s7?���s�ur��-3
K�s���C���qJ���#z���+�
qPi|NP
��S��4�VΛ:��#L}sq5���px�?�J��/�HӣI�D�k
̎t"t*�R
�������N���8Ng�����~����O:F�������t�?܊Χͺ��_��6p��Ӗ�8��3���B:Ws��q�v9��}T�T������!`���*"C �x� �i	�A�W��$B�/�,���V�,�Kf��<���|���?�����ƎP�����c�A�Q���<�����&9��u���t���-,w�K�7侃5!���Ó��0�$Q�q�����|���z�� S�g��<!G���bz`�9�$iE��Tq�~0�C0�Nc��*]�"W�0�����d��.�^B�瘖��|O���O�]G���וj�[s#xœ%wg��rR�u^$��Hw��e�T4ߐ=0e����=��!�>�b.�q��m��x=_�T���LGԗ
:_�l���:��g�
v��2-nM��ھX����&�qd��P�n̲}��‡�E�a���KH'HF�3��r����YYu�4�m����M��K�i�(�a' ��7��s|J7X��w��O���!�g͙��$�'�x�A#�����(�]x#'��1���I˳�a<�uʖl0U�|Ѐ|�S�~����wݪ�5������������������eʅ�(�h�@B�X�ضz�t֓�sŚu���'����x�(��,u��ݎ?stI�X�tw��F��_�N�+~��[ٚ3�8��z��}Wȍ'��tb+jQ�������:o�`��v��?���������wϺ �C�3�<$��a�'��`��I�D5i?��bR��4�XC��Q�i{�3g�퀰�;ڪg�x�)b��RBW�Yg��rŪy+�E7�]�z��~��jPI���Lo��*�,Yd��sl3��
�.�*��fZ���w��0m��R^A���u�!Q�uFf��C2tD�d���I����G<?:	�j�4�A�� 45F&Elf�nXn��?�M�<#;KE�9�� �1�����'�&��,2���&��
�a+B
�
f���A���	�:j�ϫ+�Z=�&���!u�H�Y�r[�+8�������ic�]��H�EQg!i��2��*��?�Rbw$2�����Z�q����H���4��K�	��V(���%����'��C
��H�l.�1�H�*qSVM�!'xN��s������rd�O���
5�n&���_h��6�~��V�(�?�ؘK%�����2o�J9ƇO��4���yn$O:&������-�)닠}�p�lSP��~i-u�� 9�3��/�^�8�yJu�ȥI�(�?�p�rZ2�߸���ſ���}%�(�{#�$1<��XV�
:z�f���u?�+5ȁyA��I�]cN�?Q�h��1������A��1�d*���a����U���5x5Z.���c�u��jR��$J��-���c*O�{5��?��I����US�C(bd��A��̽�W;�
g|Y��8�쮆����s��4���5��-��k�kd��.��U��Zk��MQ1'Av�h����+H.�n��Uicf�I�K�Ηnj�3�K7���Α��!����$�כ�'z�hF�}�2K�yCa��j+��(�k�)l03��Z���Fz
|�#<�������?���?��Y�^�W~�.<$��mIl~�[[+�͝�B	��r_s*L��+2;S�Vj�4fR���^�!|�^��b��Rc\�^h4~�9Q���f|�s�t^k�`������}�h�P��0��a�����?����oǿ��׬q~%��R�_n����v�ң+�ŭ��(brP?��im_l���3
_}���y�)v��	yF������N�r#n�Zg��]~�a��>Q�r~0s�&�;�æ;�Uֿ�����yv�"�O�,�Gef�oB)k�"UN9�A�R�}E=o��1G��f,�-(HD#i|�cǏ����L�#'f�$�βԅdZG~�{o$�(Bb��1�
B�I@����FkN��V�}e���Ü0���3g����:窻��m�)�Rx]
i�Z=߾/�Л�5���8�P�?�$�]␾Si��[�"d���Z����*z�X�T�شa�"R9���;�����'��_}A%e�B#�z#\��ž��+�������pE{r�5��Q9PMe2޺�5�:�ց���C</E��=�;I�Ә������N�)�EŸ�Z�CN�������A��)4�� ���kO�N�f��#U�O�J��}�8�r�A!�t��a�ϭ��S��|C�/��|v���kZ�1*d��x�
�������O���<F�2��4�}���<�1�[��B%�[]K��I$�����:����zf&{�f�j�wpD��G���M3]T-H���\@}~��Ԝs�lU�_P��_9��}�nL
k]���vb��*!Hƭd�#+��m�k��;���f������K�y���F���)��G�\gr�I.ߤf�uM^{P�̱Ƭ{��)riܖۿ^I_��
��y��M�)�g\I�&o4�JZ�^���]���2оz=��RVn͞cTy����e�-E,5�=ӳ���r�>OD޷S�$��;o��{����MW��G�?�������"v�@ܖx��1DY�.f���e���)��^XBo�	T{���B�ک�L�*p��`T��ǚ�a��FY��m<�4\]���"�aQW�er�_�x˾P�����;��U�f�&_4�<轢���?����oǿݴ���&y}ɯ3_����2�{����]%U���3�S@�7��kl9��_����/��ɰh�2��"w�#��K�c�vʓR�
�`N�Sc�e%F�
qt�mkNJת��@۰����cB��'l��
=i�)[N�o�q�х�'���ʱѫ��hoq�$��h>јd��t_0�Yo:��{MP��"$� �F2��$�!@���������:4�,��bM!7����5�L@����k�<�:���	��UF��x��R4I+#����N*ЍW�rGj�
y>^2�*��;�P*�$�����K�z��G6B�%F�����*o1!'�
n����KSg��!BY��������R�5�'}'��0���hr@��2�w��»h�;k��4��1���qM��Bo�~�	v5�.�g<Y����!O���� גOm�߃�v"�ۛ����FD���0F�y0yw3��)tHw�G��Qn���</3o�"i��!2�4�	&�
�r��'^����N’^4�C�%����AFק0�0�*��(-i9ô5W��;N���;YmM%<n��O���p�6�Ixe/0iG7��:b��'1�
z~;�B�������L�F�yR����L~��>�g�p�3�U'ة�ßp����&%�y.�?_~%ܺ׼b@au��w�;�^�.���GHnH]�X�B���	��+_�����Ojɓ�֎���i)�r2�m?
��A�U0��X��Q�
���4��U���3a�K)H�rt�!:!�� ��)�W�1��ƾ%9,��z�:Tn�04����$�g�C�}��r�)��N�.@ʹ��{)�e��>
�i�t-������Ŝ'������?�����)����4�ҕ���$_6�;�a5 4.e3<dy*+������k��.�1�f�y�jNY?�F�9��&t�E��Q�M���
]�޼�p���e���&WP�Di���_7L�d�<�rNj����琂x���������v��_�K��v6��r�=(��z��Pܚ�4�PY��� u7�y��lX�ںƅ�%���	2Ԧ�&�"pK����@���qDP��-k�O��
W�♆)�W� �
�B��	{8�ڑ�3R��ↀ�'�Qڙ�� 6�N��
Ό7@lNj��2���}���[��S���Ȝ
��q�"�<��%H�B�w1F���_��ú�
f0ї���q!�fB���g�@h�r~�
� �Lk~53�nʍO�xb'�Z���a{8�|B���3%;O�$GIN�Ƃ���v
�� ��Հ40���3�|�s51<����9c(��C g_
:�Z�G��ڪ��?�s=��*�}�:�iG��Ú����8oE.��3
R�3=p$n$��>|V.��Fr����O�]�F�����$�I��^�W{��l��)h*n�q��7NFg�Y�ƿ!|z�)���D��1ל��Bw�O�&�w�F��'�ꇒ�6x
��3�Ȭ�j.+�gB˯�j����E&k�E�<���@�I}�/�{Z�D�in�U΍��? �=8��Gh�.�2e��u.�&Yk��1�O^<�]�竈���tm�ݪy��䇱	��;��u���i?���<:KZ�a94�?���Y���G"j-Y�V��Ѥ�܄d�>޷�w�:�=��kjdRµ�e�U:�k��Z�:�EbF�}7N�@$�8���q>�����?�h�Wf]<��G��O��ta��u�
��t���;@����4��?x�K}�nǙ�I74n{��(=ws	dYߟ��?��o֔d�Ri��!e���Q+�I��#�,����d˥Q8�ܕ��Z��gSѼ��VI�`	K��7V�'�7t�xa
����ᤈm�Ɂ��{������?����[/g�~��'ǫZ�KݟX��o����*�n/<�$C\i��q�H�ƈ�׻��'��
4r���Kw���#��o#G���/����XL�p�]m���š���q��jTU�la���2�m1r�߅����?�����o��D\x������{9�}?����)9��)�i�Jd�m�'$�R��k>+�-�Uֳ�g��Ѕ�]�\r}7<�?o ݰ����~�?��%������0çUb�&��F��A�Ђ91�b~�9
�*�������5ǣ���eN7��;@Ŀ
�sV���/�*��h������;�H�2j_�j�tf�U���H�dF!GĄ����d�/~�n��4[x\��;��ֹF����2{�;�ہc�{��L& �C��d��{xx���`ħ!]�&��g)��a���jJ���QȨi����F>)#�Aٟ�v�	��d��'�Cl����pŪ~�*������)��m8����5σ�|էP�{���'��xhd�3àJ�ɱC2Á7�/[z{�M�a����X&w+c���_��FSpb�����ʴ��a86�C��T�>jE�,���������}}"�U��0�b�F^��ϊ�p�>�{�*B�ݵʡ�gd>zՌ�w�����g�����.(��M2/T��J�u\�~�L�7�g�ư������ON����w�Y��ﴟ�(�+O���{�Պ��~�R]s~!�(ƱFW��~�j�3�Ќ5\:o��W�U���ɾ���s�	To{���S�������hT�~�
{5��Y��{�Xs"8�TF>�<N�$լ��^_R"w}�]��, �wW�~�Ҭ�&0��g! K���~�5��@�[\�3��j`�-��LF��WSQ�ɣL!����BA�fiL��T��jh���M����S��8��.��?��
�6w��9�Hy��$$n�<қ;�}H�Ndz]�8��M�{������?��?�P���93�Ÿ�I��az"�L9���d���7,���|�Ó��
��
ԋ�1S�ZͶ��!7_���Л��L��k��:)�I�Y4��>4�pG�恛#\�5xL�7�/Ó��f9�o������?����F�j+Kּt+�H�4��M��u_�Z͓g��#1��+]�X�j��� hnD4�v��@�YK�����?����W���Ft�ł�!�Yk�9c�&�ALah`�R��C���qO�3�4j7<e�íӲ|�+QrG54�"���$��Ա4T��S��߫�أ����� ��*#F��F�OU��!��j5
�_v\�(��i�~��)D]E��N�}�S���J8�(ŧ����ķY�N��g�l���gbU\	~/�9�>�8?z~�$Vb����uw��@Gr���i,"��(�1u�c��0p���>�^�[kI���f��<�U>�/��k7�cA����3&ͼ�w䰦�ɷug"3'�IO{�E��	��c���
p���"�(n:
���I�E���Q5>(�О�y�����r���)f4_�S6�O�/rƲk�#�/��=��1�|��H���sY�5�WX����Xy]��5[��"����޾�$1��1>q����+�/��<w��
��%��U��v'�0����?���ٍ��"FW��+�^���H6�
8�yg��t�Y����t��4��&���Fkd�&u�������-�;ci���oJ��+ce��A��q
����M<Q��DNu&<�t6���r��ѝ@��L�~9T�e����jwv3_��H�.$�︬���\�ϊ�6�hzu�Y�����	P�M^{����æ�#����2VY���@Si��y^�(�yˣ䆜ˎ�۔'1�����i�{���û�dT��d��2|x>�grpA,
���5��XI�Y�D�4�,M?ġ5/���O�9���Q����z��'��P�{������?��?�&9��[8�VS���O?}�Cch�8No��|sR�4lw��yO:�D�+�qr�8�hA\7�y+�qb�CW]H�qH�� �g�M�.���Go���[�`�����I��;�����r���:��f��[��������?���ԹQm��8.�=�O$ο�*bZK�I04���ޱ��α�����,w&?�6E�������%�U�D��ސ68X
J�>�]d��r���,�4u�:���;�t2�*]C�l�V-X�G���R�2��^�����	Aa67��AP�A%E^n�8rG������Eי�7���a,�����uC�4�:\.r4;	<�������
q��|%r	x�a��($�a�:�8�iP�w)�IY�T⿃<iw�����c�RQ4�f,�۴&Ƈ��u���&f���v������!�B����'��C�R�NV,$����C[�4ҔZ�;<�%��i&�w�9uB��l��lJ���'���z>�7���\n��,�tE��o����DˠW��8��CS�xwӈ���Q�Wd���

���[��S��o����5�:��
����\�j̆:^͉������\��N!��?��u�.N�?�}��;Xr���_�~>���^PӸ��'����I�i�#�H'��y��-qVd�����ZAs#5@+%���������ю܉��<��v�J��w��}�I���rzJ��Gي�C�?Ը�� ��I��,�Vb���d�ms���d�*3@l!���މBt�/�
��%�U�3���ۯ{���C�`�JT�cѩ�߱+M"���U�!�I�xVL���I��8(\��r�+3�����`����zo�K}�:��D�)��)��3cV?�?�+r���o?|�e���2�VF�ٻ�
�sCښ�L�(����'��xl��8���d~r�u�n��=-��a��=�TK��'�¢Om�/�u���T����ȇ�w�������y&�ty�8�4�δ6��>����D��D��|��7��GQ�A���/�Lj;�����������-�j���\-��G�瘛���s�|���_$E9�|���ax�+��rXx��%�Ln�{��ͳ	0W�,�>���_�x�F�] ��)�Ϝ�x�_G�m_1h�j3d8F����_�!GD���H�.����w��8�N��վ6�o�c�
q����w�|^7���ly��.���臥�w/�ZH�v�?��������s�\�@,�:-_�����',]\xhƂ�0���)�h�=��+m̭���'Ed���S����Y�{��e�Ƥ���5�&R��pb��,-V{ۆ#�@�|�.��z��c �R� ��ַ�|�3�2>���=��j�W�Êf'�*�H�+�1�Dž&lWm�@��� +��_�|�RK�|���x��֯>|��g2S��f���/���'k��?-���CiBc����1��']�^χ�CO���2�JHd�p�����z,^Ә܈Q	's�?��b�]����9��;'�$�r�������j7�~Nh����0�0�~�i��*[�#fT�\��d�q��Мxc���?�1"/fD|f�шȸK�r%^��J�����,�6�5�0��!�sӜ��L�俹u?�4�����+�pIEv�<W��p:�8R�����s�����������������Jj���h�/~٪�ˊ�|���^>�L�������t'�S�#	��D|p/W��9�.^k��b�~a{���I.�@b �*�(����sP�]J>�s�+$��\��DD��߾O�p@#$EP���3 S��?~��є�1����y�%	�����TD8�oƨh?�v���/x���S!_����\�o�84���E#��V("q��BpF�}
�TD*Fʼd˙���[��6L��
�����bE�*��P4�D�?�	�o��.q�8+AW�_ʼ�1�Ĭ��昻�L'�WnĤ]�F���g��\Bk~��ŹH���_�{>�A8t�9LQ��ʟ�p����}�	;NN�D,��?5sA0��E�k��84?�짟���H��3(5s�׌9��K�B��ɪs�T�yD(���v�'�N/hF��S GT�O�46����q���A�_����|n�S..t�J!J��Q��MՋ1�Ї-���3�w:��rp1���S𷮰���??}����?��	=JA��_}�ՇoQȪ�75��sDs�7�?��	��I�L��I}��MV�@@��t'��>;��Āqf� �I��jݶ�������c�?C�J>ԧ#�i2b�^�Ą$�E�'�,�ʀD�����YU9�w��}�-�����ϯ�z�9G
��L�Y�dy��+e�,z
�
��A�&��OM��Y�e\�̲ד�� �M����O���?�H��g���JG���ۮ�V
’{�}���(��JOP*�r��I3��]tX~']B;�#K�!��	�c{�v�o#9u#�I}~�5}�����=Br���{߻#�:�o~"%B�?����}<$��%�L��csͻ�G]pAu_u�&̈́�a�f�ㇶ����K�<p�t�?�������'�'��8~��	�̑Y�$�nA�%w��=��v��l�_����lY6:�<I,b_�؅M���<��y��m�&��+Us���Az#?�r��R������y�W�WK@�v�ߛ�]��f/b��C5�:v�;���/N�v�?��������-X�h/�?���?��e�?��y�Y$��v�d�9��h1K��Y=��Z�7W������]7��7��/�$qT�����g`��,�gc	��m�������2D��0��%�VP`����`�� �b��/�����_�iC�vD���c��Q�X@�2B�	���/�Z�k��1Q�0���MV�36'����n|�R�?�&�ӟ�����E���ǍCϚ?�Z�g�t�M�݀g�4��2�g:��M�W�#"_B@�я��1&<�~������}
����1��1�,��g�|���K
N����4��}i6q�����bB�'��w��Y��)�쥹Z���6�������3�����n�dΓ���ȴ��&��ީ����#����&B�̩i��H�t��6F8��s�>�7��U��D���w��7��������N���B�cB��
-:���9�60��>�8�C
�R�y_c���zO
i����4�h�#���D)�!��+����=�#Y����Fb
���i�@�zf�$��o=��A�<�hr�q�03�?g�p\L2���&�п�vA�;n�w�c��+Om�?vAr��y��Ϯ�����GH�̣_�G��ͨnd�'#1Hs��
)�r�t<6�D�[׭���Ko��3)$'���^Il��k��1�p�7�X��=�W�OSCʙ�� p��L���!���M�sC���mG���ܷ#�
�3v��w���ŏ&�&esy*׼���>�(�[��s��+�3�5/�*׵�x�zkG�0}3���Fx��p��i,�y3�<3cx�mR/nC��6��k����,�!ǰO��37	��-�j1�Н�p����������������8/���{^'�a�'�w�a{��{�N�6ς�ļ�Y�%���NHAxwc���%��
��I�("]=c}��ҒD1^h=�t����&g��2���%�ޯD�wħDn�j��]��o}�ꐗ޼�H���������?��w�|��`��ܦ�d�[(�Y���7Әz��z~@Tsy�S� ���߲�r�����Q�0M�״0���2a5A�v϶�jq�bSo���fTV|�i3�&�3ҤN�h!�
\p�3z,B���"��Nns������I*�[�O�-�aT,�T�ֺz^2(_����;�%����������3��W/�}��l�_�/������D�z
"fO��/g;�.���O�p#�o��ў��2���85�idB� �����|�>t-Z�������'�_��ɞ��-�����jG�$�#����zP��D� ^[��)}w�!\jOD���}U�hiջ��ֱX�3?Q�B0�Mx�9�`0�����p��)�|Ճ��x�{�`^;"g:@��iCd��`�B>�Xm�7$��/)�C&0z>Af�9J����B�n��h%x����!�xx�� ��^���<-xJ��9��!8��&�o7�IE����wJ?B���࿂_�K֜w��
��n�+G�L�(�!j�/{
��Ҧ�÷	�o�A�X���`���T0��Glp�3�p��ɰ���[�s�;��3�w�s�{$�KW���UbQJ�񰙙$��PwvJA�!K{�A�֏c�u��������1�=:�������$��ԥ
B	J����n�c�=���
�Cf�r�$]����Rf�O�|,:����3w�xg��r�i����z�:��g���+�3�Ez/��_sr"_izrD�^�v�Ԯb���Ck����+
uN�]� �6&(��mQ�4Q�����9�_�d�C*,E;�W��&A�_]��Nfv7��o�'��(������چF43�yZBf��QP�@yү-�k[�̆�8f����^��HD�1�ʜ���?������?�*
���գ��r�k`h�J49���p&{�y���.p�2BW<-�?N]2Vn+�9�o�9�y
M�a&�o��j,ȫ�d�%���2J�ɴ�r�J>����]�8��l��Wڦ�W�rj���w@ky�+�zP�F�?����������s0�k"-��y1�����r|���������moY4H�A#�K�X-��&���C�W���.X������J�e�ƌF2��#�!;%�\���@��')��ct��b�j�學ib�=A�F����]�
3!fr�]Fġ�"��9:�*�e�Q3��^
i@6���CL�>9#�a%K9�I�ɝ�
�4�$x��P���Q9v����W�\Wv�7��2�=-$�x��-ZVZy���f�h�`^+��*:Do���"lQX��ǰ>~���6�"׋�5�&�L�,QcTV�F� j[>��?�� �C��=Z!t��n��ԙ�� hC���j��H�?�5	�����al�Uztu\4sW��n���3^Ճ5�y#���֝��Ag�gt,��5���C�ܜ�Q���$���0��cW�����\Y���-����f�k��.�q��Fa��]A+��F#����?����C�c)����`�]0ό�µ zB�l��0*�)��#�Ȱ@`��Qp��^�1!�p��=��a����Vz_�sA��+�=��������O�0޷�^�	O�P7ҡ�^%�j�'&�n�����/����%c�}���:�V�&i�Jw��_�j��}{�� �<��0�����uZ�Xa��j��/�%n:��
�I������uSă�c��$�&���B�����94����j���bo�C.H���͘�Y�/d��A�6�U����S-�d&$8�&��� Y`��fH�la����w�K���'�$��c��Z_�{۔;lf������.5�T(]�l�!N����ޠ�lQM�P�ߎ�K��!Gț�o�W��*RpC���=��ٍ���ǟ�K���v}�k�+\��$�#�Y�w�#,�ƒ����?��{�sx0 ��
�cҴ�l�8�e��ޠ�"�́c�19,����F��I{]��/n\'iǻ�y�{l�u�m�o��;�n̤���6ض��!�Յz��y	r��)�E�R��
��~6�g`��6�&=��������������&�d���ڑQ�4o��<5Vq��WҘ��]דIe�m/�,H�)I-�p�%X/nv�_�`��S�H�����>v�B�̚k�eN�
TP����͈��u]*z���hYשŷ��FAk�������3��:EڮMV���b>�9��]����|��Tc;5���x3��y3�ʰU���q��N�G�6���x;��MUG��qt�o_-�>�ݻ�� [�I�l��f���
��e��`�Q�D�jsƲ�*N�������O�^��>�Ys�m��@��ƹ��m;}4��S�7�W
��H	�:ȇ��rn$�C���ʠw�z�<��>�3]�H���}e�*q�z/D-i�u�I��f���^]Vs�n{��^��ק�G�� '�T}"��k�K�h������|��`���������!��7e�Qa��$����	�ѧ�/�#7�@;a,�`/C�� ٯ������8޽v�}#-9@^Y�=3ih����͍����;OӍ@�P~S��$5o���啖⥡�����xhb"ڑ��?�����~��S�ſ�&]�?���yL�y��(v��\�T23T�����4�~���9�x�j���C��=���@���5tg��Ȣ��,�K���jU��-l��̸>U��F:��7��Ӷkѐicgq�ś�i�=�*�5�\n��F�ݍM
�Q�����@8��p5��zd6.H�S�s��Na�Ƥ#ȱ���Ne�>V��M
F�%R%.�C��aisEw����}�'�-s�խ)���nz��C׀�d��y��:����}3��d��!�%9)|����������:�n����śN`w��+ͫN��d�&�D�9�zH�+�&�'ō��f�!ˎ>M,�(�6�ŷ2}�(�5�tEF7B�/�����7�K
��f#����mCӘ��,�z���֫�]�С���;��?�������g�-|�8s�h'䍠Y�$���8�眡�,�e��r_p_��U�KVx�
[>X�i���x��:��=a/hEx��3T�f�ʄ`!/F>���ky)IS�l�lO8,��k9gƄy�I�5c=#�U���P�fC�s#,V.��a��u|)�K
����śc��k�i]-N���F&���9D��GE:��= �jJ6$�0��z��v��5ۘ�Ɲ�7����A��M
#�M���&��f܌����:O��&���pc F�|Hh�f"q|�>D�E�}&b���6�P�(����uS�HgC���U�K"8G+%�C;]�"��%���v���.���P�i�c��'�Qdષ���M�(��p�|����5Q�+"����"�L��w�Y@`�v��:���M�Cu�u��mE��k�|��;a��Њ�N�d7<x�f�F�Mb>9�>�?r���G���~�g+y�,�I��ɠ�����9{��jUl51�����5�_[a�m��)72/����7��A���t�ӎG�И�\?_nR9��YV������yY��PS�kjo���y:�?��~�~��Ŀw�����t�$4��H�G��G8�|��_�F��ob�W�tP�!ۙ�8����ݕ�u⑬s
�m��Y_���G����LJ:��*��/�GA�t�ˆ.�f�o$���{�
�ʔ������S���ֶBD%V:��4y��:.f��68v���K�eo�1�Z�٦2Á��j������J��q��n��4�)hl~�j�Og����VPS��RK��~}�w�oe�<-�0[\�㧵��/v���-i����3�K�s�N��c;r���o�5�h}������^�m�f%���&�F���������N��ڕg�fe��ם�I��R�
��;c9���ի��{�I�2_�5�Q $��	���7���vI�/����, Kw'�]�N�a/5�8�(n�l�nn��~��T�݆�sZ�u�r_M�P��^Ď��@������������/�-ʥ��7�.��>t����[3#��=�p�-��b&��_��W�3b�6�ݛ�Mm�} ~��W�� �����ב��+�lG��_ B�D7����F���1n� �C��qt�����[��F�.�����,��I�E���-Jk���:�5s=�	iǵ��f�Y�sf4�_�e[|�?f��
��Q~[즘o�}�3���k�'�5��x��b|
y��vd�O
&2!{��~���.l����c*�K4��v]��3{�mA�q?P�c}ў����w0�~��B�|���ˮs�|��
\0�$AD�� c
��$�Ͽ0��f������q��$�Ü!���T���7�Ǟ���K��#���/��q�yg����(�I�Gf�Ͳ7��`�k�|d|����OG���m�����2ě��ZmU�\��_4�f�5�-7*z�q7��N��3܌b���Ȕs��2�֪�5�+%���A�
.��[��|/.�q���ia�95{�~��l�=�t5���oH'njJ\�WE�D��4�w�(<�֘��y�t��e>����?��oș�qVd�/ui\R^y�x}�%9��ui��ЫD�WĐhz\�^D�_�;��p�-���gE���†a��=
�xV7���2�I/�g~.
`�¬�/4���޷�%6d�p��&
�=��ȡW>��h�5:���iʛ6��lێ���i�Ҽ�;��^b	�q�;�ǔxw�xA�u�.�������6]%H�q߃fT�bҳ�|����Rޑ��W�6����~{^s�MUy����������oQwp�|�˥���)����zg�r���tYF
Po<vh��|9��e�|�M1
�X�H�r[`�>���-c��t�N�󾸃¶��$�e��7����#ХV52�,�خX<�6�Hwa���g��̚����?�������׮]�T�2͓S��m���n�����/��'�a�OJ��x?�n�ǿ�2������.
�X��,W[	\��'�B�g�^���s��!�*S�h;N��;@w����h��,�;���7��>.*>#�a6לp�t,m�p�8� �`�;^M+�=:0��9k��H��k����3�ɽ�ؓD�#�cZH �-�B���e��'O�!M�epe�@[m�?���J7�_~�U>!5oX2��4�V��˴g���|�#�B�Q�q�#�sY���6�����V[�َ�9���@�����
S���7�'�E����P�gz����|���	+��F��-ܓ	`��ۍe�fNnۆ����ܒ�F�9��=G���%�c�r�'�9����0[�����g�'�La���w ��q#�tH��c!yq��i+e��E��CCV�e����v��p�_����w���l;H��@G�
A����ў�N���M%�a�ct�-��Ӏo��*�ًR�~-�w�F�2]u!;;Pƙ�Ɋ�'�񕿈�l^#���~5sc�HB*�M��q`�zĿi ����~�W�;�"���q�dK�4j��5��'6����'��ǜv:���X�~�d��\	�F��No���GIF9�f.]Uvg�t�KG�OPZ�>�.p�2���#L����US�;(�3�V������19��r�vz�A�+h (&`}���M����'��
�Uj�՟��L�B�"3������V��?8S����9�7_����C�]�:�����V�%�p��0����y�8s�`0����z۽�e~�FKO�0�&Q�'�����������	������kJC��d"M3���nS3,����j����������7�_U���-��)���mY�j�R-S��+\S�x[���!s��v�+�[F&W���D<U4����㋐a��Yq9'�T��
�SGH�38"���j'���R���u�f{9`Cx^�)�x�gN�
�?�r�	0�esDi��q�L
��L9�!Î�H��MϽ���x��_~�;I:�5���n(<Rl�Ѵ*X�"���bn�o�o�o�o�o�o�����C^Ss�c���%]�\��U_�[W6��G�ё���,�:d&�A�_�,<0x���N��R���ܶ�A��_�B��;Ϊ�D��m3}���s ����Ǥq�(� n!�5c�"#`�;���hP|�|C���¨.$k�G:����-�b��BB��F������x{�p��a�
o����-�Z�<
�{��k۽���]XS�pR�-^XU\��Q�/�Vw*�,�"_�W)�o��2
(�V݋ǂ���VS�G$#0��kB�x�n�HA�"<�o�aX���i�#S�B
�@^d��w�aq4eR}H0"�ԩ�p��9����m�9�Gn-������o|mʔ�9�0]�t�*�����*]�c,�����Pʌ���՞'9a�˝�]3�oVkj��<�a��@��H�_��8z��~@���N��5D�y�̸PM>m
��1��$F-�4)������D�M��̍�r/�SGͨ��_8�m��Nr<>��S����zxx(TqW0l�d�&�gtD�t����
W�@��ދ6dn]��.]��f�Z���xL���;�X����=E���*k���<�vZj�pR¬�.s�j�4\�q�q�^B�,-���&{���M�s��i%3�;}���8�??`P�@;�����v`��	g�n˔M��D����j��T�홎��*���ϧK�)�2���'ia�Ϝ�o�>y��{��)�E�74b��k�U�dy��ɂ
<B����Ԭ��|�xQP}մ�!|�e�l>VjZ�xY���,�6���,�ݿ�K�4(�q�4�^f�UY��r�|���U�%�l�p��ĢN/�f�����&iP-�QC���^�_��~%k�/�03��TfӦ5{y{�3B?�i��]�G�P��N��־�X6���̳��(�(�s�*5�[�?�?s#��g(c*b�5,E̬Oې:��%�a1�'4����i7�<7q]�qWG���D��-&$�'[[��U�9��s��{bS�^��.ޢ/��e!ӆ�������l�f��aȼ�-gMO4S1���@��6~.?{VF�|~~��z6��6���'��16o4�x�ʟ��
�Ə�陼����G��f�Y�����]F���6�c�X'XC@�Υ���n�3З1�$���.��oGR���L����܉����4Lm�.q�|�N汰��o�њ
�ꋩ��}�hj�H�L^vGM�WE��V�P�\����8l9=���r��e,ɹwo������߈�x���^HŷBH�
u	�����$V"�ۡ%��+v��<~e�NT��q$�3{0t����z&�A�����Ր�(\�<l9$�)�4������j��϶B9�{��h{���;�OL�]ϷxHM>tt�D��t^��T�r5*�8��Z{Ǻ�ʶxcn���<����~�n`���?������_�� �́9ݴ,7�-N^jh��>���ae�Xg��3��,����&���m	½�;�17c����d�;�С�|�Nװ=���z���m�_w�}�F�"�POFD�%Dx�=/ȾB��~�⇓��L�^[��=
'q��`]�Ҩ5�'�^��6��J'b��aH�]�U�V�	1⯜��'q�13�-�N�8�n
8�,^��ac��G
���w���ET��ǐ��#�G��{£��c�A���;�a��O�o����#Qf��;�	�t$��P)��Y�5|ب��O/"}�h���.$ؘ�
��)R`�������N������o��N|��gH/��_8�L���gWӵGi��yW
<��~I��~��-��NS|@��u|[c�6MD�倞�A+}���'�ɞ9�D���z����k������Y��������:�T�8l����_��̺�=��O�џ�	���C�a�S�2��Q�uM��Q.��]� ~�L�`!��U�[4����༉��(;�k�ϋQ�Y�O�~���#b�2K�̞u�
�C.<�����|����Y����^�f� 
���[�g���ހ+�T��i~䂣����0���5�Ws���D��v���PoÑ8FZ8�(��
�Mw�o��
��:����t�]�+���kH�k�̧`�5��I&�^]��+SF�)��v-kԵ�Tg�	1괺�܇�Fk�DT�z�ո)�.���!�kߗ�����T���yc�Ӫ������<�C��ι������Ñ�8�4���L�D��'\��K���c&����5��‡���?����/��
35�l�8C?��!\��^�:`���`�ږ���M��@;X;�!cԀ��ѵL�_Ⱥȑ�[TB�q��|�0ש���O�����t�_W��h�2
Lj���%�s즆��t�Ń4�P�"�?����������c�14Y�B����:րt��>�̈�F�'��1)n�9)*�ۍ	�Cf=斋�����E�y�����0掌Uc�Ќ�Ϙ�
`9|�����۫x�H�Tjd[G�����M׋���V=�L!��_Ѹ���/��m����\Gj'6ДX��'3N�ǜЎ��ȡ�fghY0�.�Ho�S�=y��t�ݢ5���B��Cu�}����˙us�w|n�b�>^���:�綧�`�MB��c.��	K���(�P�y������/�C��]�O�qzjRp��_�=�'���Tq��?	U������o���r@��$45M��ql�N/�/��b{�u�����q�r���qU.�8`e*.;1�G��$}ң������$GL��<�F�b�m4<xܼ�9��!��R��/�Nxr��	��2�C����D�?zA}����\���	r2$�������-�o��jX���d1n1X�Fd���D�1"ej�".2)�I���MM���h�*w	QV�y��=��g����[v
���}
�y�sO
��?nG��3�,Ƃ�N(F��fp9n���'Q�ޭ��_)5͍^;�Y���~"P��y�RX{0����ӣ�3��)���F�+c�|_{
�S��u�������,h[�i���\���c����\�K��w�4��N��+d�-1����[4-t���Kn���G�A ��G�����D�j�����lo��W��S�:c��s��5*jP/��@�{�}l�`2� �l���N�M��Q�d
{���j������S�?�u!�F��B��Z�����헍��~��0a��ږ��	�Q׷��{Mc5s�}������������V�6��h5��Vh�(,F/��o A��E��_c4��X1���4o`�>�O��n����j�p1�B��+������'�\����<؂O�b��Vz
�ER���=2|���iE�w�o�
<34��?���������M����[v|O��(p"��{O��׹�sZ��3~�g8���wʤ�E� �	��NM�� d���5�]^j92��Z���<s$�k��!���	A*F��{��+ϒn#y��A�W_p�5�m<�&��!��tg���r���L��$3ErL$��x���#�tp� �'�7[�f�ٰ�CژK�x�d���q
�1�>+�R�2$�P愔��"���nW�V#����5A���5�'�����]���щd�τ��N*��R��3�yÞ��}o�!��7�Z�^�lC�,{.�3�ʎ2]�����?�,�t��V�ź���㮑��1�xb>��dAM���p�����	�#���(�Y�-2Y�o���;^M�ł����E��Nz�sT�?�K
��Zi7����K�Tz�T8'g���9�|iy�:~�c�z�Q�`m6�]eP����|�ěl���c�Ґ[X�?$w^������J�w(�@Hk������������N��D�I:�$�y�������O�sd�и"�2�g�^����)����&��f	2�Z��`��Of�1�?��Y�h'd"r1�#�K�k��i�v`��oľ^����x*��D�;�1�y����z� 
��b��2N�(5Y�]�[m��_D%���j�V?F���\��MwL�֐���r�_�H�:�r�W��?
d�q���s��� �>��m��Y4��?43ִX?�4jm3�&N�6_���w;=60e�}�v#Gc�����EbƵY�����Fr�gt��$�c���ܦ�_b䞐��wP&�*y��E�������������>L�t�}$�����Ձ�����b׿�� ��
6��jߍ��'Ac�C��[�斧ˣUP�&J�/����H�(��6�m�r�p�R�1��hd�t5��W�������?����������T7���k�VsA��Э��Zvf�?��F��{�
?�n�x�v���-�[��g�
$�y��Dy��MӼ�6�Y���n�?�k�i����._���16N��!�Yޫ�<��.R�.�G�1av���Ė|CA7q�1���5ϯ>�7��n���ǫ�����0�(��l]1�п�k9���Q8"��0��:���of4w�	S��2�ϰ��w��r���i�/q��]�����8�8��D�y�X�e��-��Ϙ��;�����5�)`T+��k"��*�
xG�?�{>"�I���:��u��.��c�/G�f,���"�?��t�8]��;�Z(��~�O��D�-r��?"�>�h�?C���A�^� ��%x�5]���8%�6���!�/�G�O�����8���S
�r�O����%�#n��7�"�5�mv���;k��-c��׽�ϸ����Ѥ��,��H@~=9�o�b~}&�xZ ���YҬ��h4aaڸ��)�, Z���
�8=�]>G�����.���襻�i-�^4�
�-O�4'D����������y����3���ӵك���7���=>�����V)w����z��ө�����A>����ѿ�.@��
]Y��jq!4B�\rskH�nUu��W�����͘M4*��f���G��-{�t|(�-�ݑ �s
����� �jw�Ջț�q=4}�(Ջ�������?�����Gy�\ns���h�;�3���\��;J#p :;ؐ��]��pu���]��Y�[��{D��J~�
B��6���d�lc�m�m�ln�sn��V4���w˽{�4w�jGVo�h��_�����?������ǞqSӺ>�����f����A˄�5�Ef"w^���ݾ�Wy^��4ɰƘ�o#���m����)v�قu����\��Gv�c}R��Ol{BF��4��s�q:d�C�t���&y9
ڮ�A��nw���<�a׻HOQ�s#�$=4�
��)�W�r��[u+
o���>�o5�P7o~��uj~}'\m���@��{��nE��?�ixS���e��t|���~`-��R�'����h�p:knc>��!�5�r9��_��a��<�(�v���r��hv�_*c����75���[q2�jBA��k���\���PC^�����4g
���B�?cj�5�+���X����sI��ܠ�����z�}MK��ߓ�/���֑��׃��\�5��_��s%
�y��-�5�^�[�e��끋���W����_�g�OK�HF��^	�o�C�h��)S/��
A��k����,�Gn�J�J���H�� tSS��=t�5�wDp�c
��	�׈ބ~<I�糿s�o��D�y��M��_�q���������=v7�"�ͨe(]*�2�`c���Kyގ�l�'�v�5��Y��H����W�������Эd���&
�����);l��*�ܥ�F=�Z����<�F��4��Sp�m���1<"=����U�f��
̟
Z�|�nR�M��fhnw���ĩ��M�7�,��7�!��DC��k��)64־����{Mȡ?�f��W���L���I�f�嶧��1k������������?����__%�S�#���r��9�ZA~=��Mְ�i!h��ϳH��Y>M4��:2|��-��G]��4"~2m��Y�
��e�7�/?ݐ	0����N�GZ�<�r��rk��jf�5��������?��u���ʰ•{��k���>�o[݃�7��,?oJ�a/ۘ�6���	�j�ڣ�O�,5�9x�i�����߮��'��(�ً��\�MҴ]>�˽)�\ľr��tK���_Nk����������}�h��&��)�F� 7챿2���H�D��Cj"�i��͘" ok�_�� =��]&5���8j��^�89��~�lf��tij��fcpubA�dM,�� 0vh~$�����L|�%���:a˱f��<��`���8�,Kr��	_G�4{ݥa�~����?�8��tk�%)g��&$����S�k:�]*c!2+�(��ut-y�fDW��'��rD�u���Wx#���5Cq8`�Tֵ��/�=
�m�M&���0V9��n�JدI7о?cCOj3��H)X���vRO�U����Jm�X/��DĹ��������ʁh�m���d:{u��)��'G�05)_�O��Z�M�߂c��K��ec�WG�ݜt�#��|��.����ue0�p����d�������D`��
7��]���M|9�X�h�0�I?���n�N�N]tM�n�to��?��'�Nߴ��L
�>��b�5���0]��(e���o���l�����ߓ
x����eQ��6���1�����Җrh�M�1��hs��\y��"�Xx�wN�8F;.���Ӕ��;�a���?��;,>����ĎC�K�SH��Y����'��	L�ݰCp��
�"��p�/�&M������;���H��C��N���k��������I0�������7�Y�G�۽6�D�8��$umƀ�f`��n�7?y K)܅p�S�w�o�v���]R�[޾P�qI2�l
Co��٦0p�L����W"2A:riN�6}��>�{�h�B�	c����ykq&�kL],�/�/�/�/�/�/��X���>8�]�=d�<&�Y�0�s�%'�P���C��>M��l��-�������7>�~�j�@?�[��GL{����wG/�쇏���%��L0\O��d��%$'���[��2$X�
�?��H�J��F���*8υ�*@����^���H��a�(K&Gxİ�d��$q��*4���M��Ε.c���v��P�k~#���J�i��
��b�xEӯ&�x����7z��2�(�.D?U2�{zo@9���F��F�� �b�;�L1��~�|�:�3a���Z���g@3In��A�pO�<i
��2`�!Ɖ6��Xr'����S��ɧ�I%2�j���LS��UC�����'�+x3�`{�d���$O�5�B�O��;����O���dn48��5���7R�a�/Iei���6���ꍺ�/��n�Mٯ�J7���tPz�z�Tr?���g��e���a;�X�|H�������c���'K���)��|�$�#�i�.(ư�ي�[x��1�����
CP����pl� !D�m`��?1�;+-�7����A�i���V�h����xOf#��A*S>
��h_9�i�G����"��g�PI�>o��(=�`��},'=LMAY�F����s:�W�6�>?���?����y}KC�W_�TK���J �Ҫ�0ΐ;�2v��yxځ�s����P$�ܙ��9N7�/�x^Ys 䨠T�Ĕ�
�:�'l����oQ�����B���S'�V�z���7WlP���Q��^۔H�>X�ߔ��bIs�6S���-�A�Ce������V��x���;N��^��fGD�9��Xմ6ć�R�r�?=���׷�O/~kU��0d/Ow�@*3=Ci��08�
�c�H���Tc�<y��v0�����y3 u��U��'M�*�=����N�������#��J)@�F�ᇞe�X�B!��ĐC��wПg�6	���ͥ���
�f�"�>*����;��.-���S��bY⦻\�p��>�=ͩ8�閮t=VB����qz�im�$��	�h
$�rzJ�v��{�y���N�`�O�fs�^���Q�F6��v����
�&P9�����{Lq�THѲ'<�Ά��.���V�,G�x�2I�K�h��νi�{阳��(�]�_�_�_�_�_�_��!�OO�w��C;v�xz/[����-���.��(X��?	a�t�����7�i��~ L�]�0��#L�z���AA{�g�/�9�_]��Yd�y	��Ó��ou1t���
���#EeJ�H�	��D��
P�]��vC0Vq0�uԨ��-�Fۻ�j[��I�ٕ\��}�ߥ�E��5������<<<�Foo�ȪM%//�W��pshlu�_�JO�*lc `4��v
(���$�YL�YP�~�\�֣��jp�8���,��Q�v0��?�`���o�ٜ�����4�پ�%Ø��IX�U��-�U�MFݞ�_`����M���4v�kN4�vg�ơY)�dt����KD>��dDz����S��\�Y}<mj�]
��
K�Vw�M�|�0"+G�(�(f2m�ŲY����-V�5�Dyc� !1e\��g�%��M�����ÃrD��ݷ4��F�y��%bz>߿�I���!��1Mr�*e�'��$]����I�4-�Idޥ0�s�QL�GB��iha
{�ہ%!A�iKN2h�A�"�0�T�w�Νp@ȉ�[6H����m{�����뛋ӳ��[H���t�TAo���Ԭ�������v��ݭ��zW��<??�����}�]ݬ;3\w��s1��:�9�2��J�wTHn��:b�k@8��z��C�B\�2<=�ԣ�5������?C������[�FYA��x=��at%E�)d��\�D�y6�m�NbKE��<O� (*����8�9,K�d��6iN4����.tu�8�wqB��ʜ����)_��qg2�_���o�NU������Q�M4�c��_��S��'�u4��]�y���>v�e�T���?���]��DO<����Տ��a��|��dԁ0Z݈qn3��&����2�C>Qf���c�B��m"���_]�=a��g�=r�EO3��wk�C�T�E�h�K�W_��\M���8�f����A;�d��zT:�!���?��96�����U		L̃����
�������oI8k�vN�u��5��d����UO��Ϗ��p<YJ�σ����C��p�j
�k��3g��&A)�<��L2MF�r/�]w0��B%�ݭ:k���S�k�Ө]sz<��ܬ��j���ޟ(ir�H��TU�Ytz�����z�,��w����#�{L�-2XX$_&�A�N��@j4���Sz; ��K����1�F����I�߹��W��h2&�A�E�j�OAt��w��w>2��y�N�����>�}U��Ծ�غJ%`�.���)�5?@与c¢+��ܖ����2�_֫�4�2r���� v-J`N�c(�<�ݣ'XM��<',��q�q}�7I��5�L7~l��?������˒|�g��.W��2�ڟs����_�Q_���׸�m#<�p益���F�a@2Mp{��������D�v��\-m�Û�~�A���/h�2�#_�Y��
�-�i�n�QUlV�@z���2�F���������?��/�-䰫�h�_XM93nd�)�z$��P�c����v����=�:��2_Q=3@n��M_��MA�du���i��Q��9���Hz��?�nH�N�.*}v`L̵�4Pw��9�Z���M�L�闟^�]@�C���#�����5٫�9�j���ܓ��\������ �)��&�w`d8���� ]_]@�4`[���-D�w�6�,�%�W?��>��yl 9v��	�ߦ�
�j@KTh#�OT���#)yV\��N �'���"WfD#m����b�@��ڿ��)&�\R�&^~��O�[~^�g��a������}85A ��@�O?���?��[D[���r����O?�`���8,��_w�������8�>N�Oh̷޶Vj|�u�J]���+ftI ~������n�69����q������ۏ�>�6�R[�d�I��JQ�o��I<�Di�G����{
�@���>RghC�\O�
S3Gf��g������PTRE[�:RЏ$��B�"�a?c�O$ґ����3���bS�̈́�;���(`�M�ohoi�@���q*���q��Pq;�̗|D�I�+���q2�kϞ�X�ʤM�,?E�7����c�?�jwl89^����b[�/��tg�Z(��@��v1��Fz9<Z�(Ǵ��LΨX���wy��������ZU�ڦ�_m$a`b��������S��M�`�G~Ne�P���|I�/�rxp~��UP��`��FQ(����Y��_��5�4ko�BG�������͟>0�l��1Ә��H�K�@>Dķsh��4͢^�{��X��]h�s=[�1>�n�[�q�}S�V��~���X`R*��#�������?��)��+���
Bۉl�ƌ��.Z�Y�l��%_}�S��U��Ϙ��wSz��3�7R�݈fE[k����fc�VmZ�Ї�;�50ۊ�zپ�7�����-. 	���V��u�4��;��	����{��
��������?��;�Ϝ|�ջ,$��}x�bk���6Ƭ��4Lo�Q0�7�x���a�	ցl��u�ƿ{�
*�!
���ƫE�uhkwhx\�5�m�>a��y��5�x�f��*M�3@�{�e�ܧ?ǫ�͏"#�������U��A�|���J����4�d�͡A+��W�ɖd��i�qp=J�`�W`�D�����B]L�y�����>"ȅ����P��$�M�<U���BR��W}��h�`c"H�!V4��X������ˆ���'���i��;��b(U@�/:ai=�� ���Ƨh�Q�<^��W�q�_��Lޒ9ά��6m+�*.�v���u��h��4�v���Qv�۱�•��R�'QqY*c
U6����
�)��W���VR�%��y����Q�6�'zC�?}�9)�G'����;
�C�K��Ay~�KŠ���?��F��4��R�?�J���<�dǼ���W![���G��uĶM���9���m��G��������(�9�V�˫��.r��L������B ��/�G����I�H�|�K��R7�C��_�����6���tZ������g��OcN333�V���9�z]�*���(�8�}g�ci�@� +�}�oV�J�կ]�u4��OC���hc�~&T"�k�j�0'�g	��`��r3e�(_�\�����T��ԮL�*��O���g�f�����[e00�Y׿9����:��s�2�>�R��r�-��4~�犣S�7OHx�w�`u���I�:r�4�l|r�f�BAn��Zc�!��rp���o�?4
 �vY=���&b�bm��������d-qfP�����@�{��}`� �Ľ�	f�%$�g��Fz�M��V
~�A������������q�Q�[��������mx�G<�$�l�9�}���7�\m���~�v�l���OK�! 
~���*n��������<��l1�W�Y�K�OY}Qч�'�7��q;
깺k#*4+��Hi�t]V=��������?�����v��jHUu�O9[M7,�`^��M9e��0D�xd��B�^������M�I;dz�̄�UP�楒h70�(I�F�J�W�mrȐ^m}��w�Uj���V���e�2M5��%did�}:��Y
]�p�XjM86���&�|�T$��<�wjۋ1��v�	;��P��=���H㍽��#!}Ik',�S�!�V�F$�����*;��'kz����!~;�=<b�p����P����	�'�0��7x3�b �o�<��#E7�z\Ɖ�'�q�'�(����<.~���H�88ˋ����[_����^�RZuz�+���ꦄ�������\$s4�ɉ����[Ds�_ڞ�=�O�&R�~����Bi&j|vq	�Ւ�'��F�]�+|�U�+v�p��qS��D���w���z��j��*���r@�B�x
��{EC��՚j�|���XW�,��@,l�}=!��}{(��~�<�#]ԅ�cߘ�0�Hrh�0�'�,^�?XM6��a�k���&?�cϵH
1�"�+��UKeG���͟��i��E�|t[��l>L��>N�6B;@�=��W�S��>zreXa��Y}����U kyH)m�����B�\���F���Sjђ<e{X�‡fȩȨ�X}�d�]יcE��@o���̗���Pr�e���`�R�a��!?�$��pw��{w�]G��3�ϝ+�?�sY����_�H%��Q+�-<�v'�.{YHw鄠��t��QB�)�zg
o��9�#]#�Đ!���ݱ��ֵ��$Bl�W�!+!�v��Q˒ԙR�|�0���tɛ�r��O��v>�{����mog-�_�@�&􍐋�)�wGǡ����Fp���v�)Go��͑��v���TЛ�����~�C9?�O'.��
E���)�E�>)Z�n����������?j13E�7��
�ƀ��͚Hg�t���5���H�ۃij�O��ge���p�}dxh��f;�e,<6�F����/����?*MWM]����(�5���Asl�-�9[��sE��v\L�5F�k����?������?9�*t5t�QYvO�=<��~�,·�mC�4�)�C�uK$fA��nz��}�M/_4�~�~�ԣ�S�ݓ��I�R�,�x�;r���8��p�)H�yp"�#^���>PZ��
-f>��FZJ�}։�(����)�HE���
�P���`�p���Mz�h�v9}7'\č�{�����Wu�]Z��K��6��>�l^�f��Ic:`�U����/���H��`ï�6�A�b
=�y3�N�_]U��l�x��m��h�OX�D!<�~Pd@�w�u\e�L�5~!�@�|�������L���}Yd���s&�jm����JF���<{D��G{�R�����n�ӌ�H�������r
�ݘ�>��?1
i��`mn��j�
�@���d�ȼ$a�����{���	���?�:�3�*v�Wװ�����<���%4�� *[���pr��<q��M�½����Ϯ '�>f��Y�(,�����a�~k������o�T�ų�ƵX��>G�#t�2S�tye�p���>��d�
���������
x%�ǜ�P�I��\�R�bܣ�g`�'��=�Lh7:�����U�fW���;u�}hOJ3���i,��������'�]��`'^�s��e�j�U�$f˩�~�H���3���.�g^4�<	�_�O���O�;�;��k�C����.�o���1�4�(�O�M�ׂVװÖ�e:��м��Z����%'+��%
s��X���@�ƺ�q���(/�O?��?�i�#X�e�9G���ؼ��}����3%8�\d�4�S����������ߪ�N�x��^�G���6��Z}�δr��o�F�H�gD[͡��%f�s^��Vs;�<CF���C�k�q�=s'�B�&+2s�Y,Yf��lr��6��9����pr޷w:���V[�n��S�w���rJ�?
���?������TR�����M{J���Po��ܰ�� ]���k�N��l�X���ylyTTM39�+53��5d�{�5���7}D��:�ՒHc�ӆ�����p�6�&�NU�Gquw�D�ݰo��v'�Z�,�H�U����.$F�ѭk�]��t+����@"q�;��,��9�wH�J(�P��OS 0���ɽ������_�X_Hr'g��Ī��WN���(�x��>��#�̵0��۱�� Č/�Q������Q���P�O2�.������׺�81��������ןr��t����h�n��J������(ƴ�%{0��3%�U!v�t7����l"��Ș�-��=i"!�}���0�q�K?���47�Z��N�V�]v���l�<e�
��~
�Ǟ
�1��Z���:�r�����n`g�2�[{�0��G��?]��;�g�sj����ô>��q`]�_֙?z')oH��u9�қ�ys���;0���5����%{�6��z�
�ٹv�PVpt�`�݄����tj䕞�>|�*] ���gr�{�����w�rn�Xy�U�Q�0��o���R'�{O>���A����� c�H�ٵ�,a�Ir�߃����
y��ّGC����~�$�'�����]���m��w(�g03��S Y7)[mN����E2Gs��=Pja�
���մ?�^K��ށ���y5��O�w�����4�"-��F�0�S�������
������t�[���dƙl�-��>�XQ����=^-~�Kۇ'`�`V��u�9q>����Kn޵����?�n}�CS���h�
����1W�U��{Lj���}NT��1���4��U�ay>Xw��yif��ǀnQ����������?��#�_���R�̡Ypvy�΁r^
�Tݲ�MV�^:��X��W��e�e�e��O2�������)�d��D����D�4/�ͼ���vZW��6��-f�d�%�����9�f��������?����<��ex���@۟����=�n���M������`.��^�F�{��{�����ի~m�F�@�Poڭ�ܸ20��:pc�%�G�\bfr�����DZK��8�K�j�(*
Y���Biņ�J )r�R#�'�<�n�3�{<fe���[[�zyek�j��@�{.b�lB�6�Ff>cDҘ�l��^�_���4��N�k��v�/�/Wa&�sVA4����pl/ ��9%>�
0W���/$ۓ`�5	#A����4���.�����n������k{Jc���_���"�c�E�����O�7NW>����㒬&��X0�3����P'S��Ogw8��VS��&��}QE5�qr�߲.D��آ)�F&\�
���^{v?Ӕ�ao@�*�N��z��(V^%�M�{N�)~�~�x��qi��
�+����n�qh����=�_�H�9�ͷLT>��#��v��%�C-��o���V�8���uE�g���6�Lz�1��0�l�M6v4��9w6%�}�9;[P�zs����Y��?-|�z�3;��t�Y ��yԬs��Ėp����5�+BM�y���qTƝ�[��@���d�b�
Z�K#�W�f5�^eJ�9�7F��ۤ'`G�����3���;�P�0r�.�%#/D��6d��^�����$L�zB���Φ��s��=��ʙ��K	v�x!�]B1�9hK����eo����%�g�0���ս�p�_M��!n�
�m���KpH��'ݞ�>
o��:��
]Ӣ1aqu��c�j����������7��D��}��O>��Vgv#fI&o���ԫ���)�Z0Z�3XR?k����Rh���K���+p�dž�����ﯟ���_�;�h[�mH��a�͕�t�.�\ԉ�S�mG�U����+T��Q�����������ߍ�`6]ע�oS�Q
w��� �h�5�ǩ��а��?<0���G0:��cz=	�K��͖�]�FEͬj���`����E�H�B��tAhC�+Bħ��:��轍�S�����[/!JnuB#exKс���d�ZV�94��R����z�pDr�oÐ�����X�Yh?`��Itb��9�.��
�o@B3p\mB�c�iv���j�k��N�]�P�ZB����ԘFV�'s����?��o�����C]&=6~���
��_5���H��q4�l�7,	0�C�5�CL^A����L�&q
�Pb�>Ma7d;���*���n��"T\�߶�I�� l�Lu�<������k���V����nZ��@�gTm<�FÁ7���V�
��
ҌP���gJ�_	�-$o����_?��y95�j(^��%��Z��4"��[a/h�8͖f ��M�ɣY*��������6Ҵ���ϫ_�_I���ʛ#�H�6�-A�XG]�!���<l6��=~gh������1ڌ��%m8v�5*yώ�����Ŭ��.@n����=��Gv��#�/l�;w�������0�F����Z��Y�J�z��.J[�����aٮ�P��EԬ1�O$�g�xeQF���j8Y������B3���q\7J
3���4���5�Q�����z��l���d���]r��;"��i��[�:�Y�h�A���|�5�w���٦�����_���W����+WF��a3.�39L��Ơj
����pv\>3:~H�?����ӷ_�!�\z*��
�D�$��*�{�4fr	�/�;��r%�k-s䆑-�F;خՓ�L���%�h�,X~(�����}i
�dBn㋽���n���s��ޚ}L8�mf?�������8�ۚ6wG��YX�s�����ʼnS|gծ��9XY���	��v���JR��<b��{KN��E�L"�ȫ����nK�K��F`c|X���o>�$�
�3��)*����O�d��<g:�|�S��7�3�3�&?�6L*0��k1?�p�XG�G'�f�Kq���O�mS�hQn�b"[��d���j =U�~�R"��+��,}u�O��k{��������?����ˀ��
����3
w="O����
T?t`v4D`̧�	��g�a@
�z^*s�|��D[�ʣl�"q����1��R����w�Ӂ>�.��.�rM}����f�Oa���V��_�#��$��Xg	�C��ª�=��
��'�(�L�����
����a�n���ŎU�ժ-�'�K��Ǐ8�I�{zS��Ө���*����Ɋ9���ۄ�sd3������6�����S�l,&B3 ���v���t蓀r{�p���?��0��m�!�B��JX�m?��zq��Q&�.
x��)NJX��i}(����`�N�M\�S(i&wssW|oA���P�,�񖈉G%�-���#ѯ����!VXR�T'G��iI�r/�ceG�ۏi�����@.h���7M���j7�zq�8��Mx�C�c\J������:�l�M2�k$L��I��wa̅�޽���ؾ���A��r�*@�/�x�
��F�+ph�����6 ���"�gߟyN̾�O��RM�Nx��OL)�aR�\���F����W��($��D����m�L�������H�#�q�}^d���>(
c��iՖ�tn㗴PʎHƉw
����G�'q2D�G��"[Ы������*&�0�e�q�i���?�JL��y��׿%��3@#c�=Gjm4�S~��K���*OZ�����i
�у԰�P�	�������*A�A!}$1yQ��h
������p��>�M��s��|���=!UH$
dΙ�@����(��Uy\h�����&Z��!Y�]�$�Sԇ�V�<��y3��qF��\b����8|�$,B�ͷe,l��s*?�b����mf/aخ�+�u�_!�J@.:�"��ëK	ݭrO�!�H��+/���`���"�?-�3������9�ai\�D(�Ѣ�3�Fz�G#2��fl���_?�`���kE�s�R��i�bz�`�l��,����g��R���3��71g.D���S�U)2c��-͋1?�罺�4�|�'����1�d~h>�㏕9v���q�m���['F͘<Π�Gr�9̓���ɸ�f���a��EG�9��װ��r�PN�\g11�Rr����o#���O���ס�D�c�ߩm�4L�6��XoZ�
�7M��������L����7���:�C��	Y�	(���"Z����eW���?����6�ݶ�H��]���՗��z�F�,�}�9�څ�ҫ�z�����?$��?������W�ѽ�/,a���&�̙��{z~LS����v���~���L��J�[��-ؠz����?=<���es�R�)���T�������3Z�&�P�����A���͙a�5��T2�BT���p��S�~�T�σ����?����h����.͋Ɵ)iD{�5�(݊\��OR�"�+,/W[s��v�x�6f6�FLJd�+N��>�y����-���	�LK؅�b^0\T�
]z�x�um_�	?#���=�������z��F���W��wq�.?&��v;T�Q�+��ռ{Q�+���+�Z�����1�Ý�[<�@��q�ю�d�3�ɓ1ۣU(ab���/�e`�;��

�!��͞ƿ5���p�m�����4w�u"p)e���:��m����|�_+����D��l��$<&d���FW��yѕ�ׄ�G�@�'&���;s��K��;�B�S+*�9�7VF�C*;s�;��q���7o
sP߱)���qJ"��'��w԰������/5���ۗ{��]�����\&�����UF�J"��t�+����DWv��֭#N^៷S#;`Ƅ����2S�/���9�|(�������-*A�����,u�T|��#��[lJ!�����БQ�5�9����i�.}pޛ����d��N��_���csE����~���[[�D���!��O;s#FcnJ��R��GjƎ�E�z
+��(J=%�����U�f�?w
$�%�2�q�����u�$�����WX���l���+5s���gMJ�w�]��8��C��w�s�3Hץ�;�P��Nf8�?~h�K3IUv� �	7ɠqrH���~�Y%+ɝ�d�Mxm��aq)ɋ~����˯�Q
��{�E��}6 ��4��L}$tV���I�A�Mwe����^���unMo�:�QVφ7�?"��?��������t����K�0GL��U$i����wZϽ��p����n���]���[�?���]E7��;�c�ZM��I��Xkyn�v��M�"�{�
`soޚ�չ��/��J��+I^�b>��������?�M6�=�+F���i���t]\
tGqꃞ���K/
��)S�A�}ʚ���ʑH�Ț������ᶉ����oР�̯i��G4ֵO��]g�<�����S4�"#�"�͍��w�	��ү�����1?��i����,-RH9�����C��z
k����)�W�i�ľશӻF+\�]�F�-�
�{j�i6���������#�H���W�S�b7f>Ը�ܧ�.��������{���#���ʿ��(�	D��5_GI�s�1,W�w�Ϩ��(L�н��l���Q2�ޮ��ϗ���^w���(��UD�/%0��5;����p��(��7�M�S�j��aMM0S �Ƃ`�%�cD*$�-U�D�.��ߎ?ڟb��+��
v��������J9`��Td�N���y��%*���߂E-&�e/Ш��+��
��~�Տ;�x�ª �	��uЁ��͇��W�J��|{Kȋ͆�XI2"���_����8;�襍��;k5�
���QU��Q���"{��%�
DQH�O�=�7R�?������h۬�˒��3kR�,2�rOCT>�S*����@�5؏\/���8!p]�6`�t�g�<�e���~�t�9�;2�ם���9��s^*��g>�:$ff��0�l�&SJ�/s�C7w���dF�Qѱ[-*<�������U$L��~]���`�
�n{~Z⋕F�5'<�Gϋx��x��4��=��dW	ˎ-�3L�1s�j
�f��u�t�e��u}���{Vw�ӈE�J�cW�n������6�+'���<�H�e23�G������?��/���C�i�6�]8u���|ļ�6����bް��!3ܖՖĀ|������{��CF�~e�.��W#�ś�J`b��Q,���{�L6%9h�3|�"͇�k2��u�-��KM;&𽋺���?������X�F1€
��x5G��22�=�7ߵ�Wk�����Y�����ĀL�j�(�=��~��9lj��е��R]�w6�Z��?��E,o'A�a�?�5ې(hN���4�Cm����po�w�.�Y_���.�e}�B�NC4,��:���Y�C6��
$c�쨯�R�(=&v
<^a��	���7�1��	c�D���7��2�"z��|d0�h��j
Qf_
7���a�ܛ}Gk�������әF�m�K��`����&�t/nU�������V�c<a"�	H
|�e���C��
�6N�S&#��Lm����'��.�1��e�w}?%��
����i��?��_&�9�ug�T����Б���e��И�Q�o��ÿ��=^~��%��p]m�[��?屧H����7��<�CX�:�4��o�F���*�_Ώ�Lnm�Ww�C�BH-��[N�<3.�;����]�'|����xw�u����
i��|�	R�|ӎśߴ����#��f{[�hȥ�]@�c �����~-�1��$p�V��M�ῦ'r�?Ĺ�m�(����`�j
#OBc��r�l\H^9,3
j�)�q�S~s���[qI������y��p�s�����Bt����(��4�2��=����1+V㝩��������[��|��`,����γ_1|k9 Y���
UVʧ>��o��
��9쭵~e+[M^U�y�b��&"%c�O��D���x	h芞F��Xf��!�lw�0t��[��[Ԯ���ݷ�9�1�g�ذ���M�?�0开4l�,�P�?��������GV�"�ҚU���۹�h��m�
օ�'$��K����j��
����v���_z�i���/q���5����n)h��$`�rf�o3��67�	B�_��=tv�<�ٶpp?�{���׆mw�(����]0?��������?��W2��n6P�	�]���@�]�6>/]�Kv�W�:�V+����	�u�[��%��UJ\�ܟ���'M�_
���4��t)�+f��asAF��v��c1��Fo�u�}�z���]�H���Ł-�)�=��F��E����� ?ZA������

�	7�[h��cHF!���n��O��f�12���7qEa�2�ԏ�y<�����zCim/�'٣mwk!�'�{�T���re�=�$ۍ�Չ�51C<���WH�y���G�O���a���O�=�)S�W8�y�&Z�R&�RO��Wh��C�ܓ�������ݻ�ܽ�q>��lz{l�r{�	��ſĿt������Ŀ�N��k�E�[���z����F�H�{��ψ�E̤�7������"z�ޚ�)mhA:�+�߀l���V�̺��T�[�|�ph�i�ߛ�-�-��_���n�$df�f\���ƿ}vLb�"f�i��"Z�S�vvF�{�Ӗ��=�e������Y��*�/�����Ċ���3���uX�[Hn;�C�hJu�d�����h7��A��'X&��j�o���u�?�����K�iTp���w���0̝��]�Y��X�|��g�>dެ��i��Zd�l�>\uci���:W	K����N�5�[�x�oW�H��R�"|��2*�V|83�	E��+��"�t��<2���t���6�b�Flr�3,W_�+v��/}�SMG2�9m?-"�D�{6�%���D�?�������������B�f&�[5��}�f�N�܂�����۩�a�kd�ۚ���g�k�Q�=��������f�M��+�H*R䎜�:Ҡ�iz����ٳ��]�?��(M��+�Ǡ=Pw�I�l�I��������?����4낡���Z���O>�.�s�\x�9�U�n��Y��5כЂ�/��j��&�c!��}K;�A[�V��s��`� $6��+�f�4
��7a�~h����O�G�8~j�=�n~O'���
���n�jw��&���f�}Vw$��y���3�4�l�v@m�Š1��yzm�@���#�„	~�^�L,�8�λ|�[������g��žj���J�:|h�(������Jã!�2h�k�kR�� �'#Q=��C�H��A.J+6�Ze�u�0
��/u;x���[�	�=��W����W�S$����xƑl��Ց�쒚��n�����C�p�����;9|���']�����Z�Ol\���HH�O�����f�kl�����3����u�2<�Y�N{^៶v�n1H��-('�ȵ�^��l��C;�0͡��l�a�?�㚄�ߓ45k�HNJr����kc)�
�A�OJr	��X-0�{Ds�v���d8mj����鍙H���Pi��f`,�/U�{

�:�lL�t���������k��(�m~;�q4��V��_��߆��o��r4)�)���������L	;�J'��%��o�G�O����M�]��ݭ���]_�Q{Z
)�z�����@�C=z��e�q��\�f�Yf"�w��?���Q�J��A6��2�m����3z��a��YGI#b��/��|KD&14'�&0�V`�̸kZ~*���Sف��@��@�?�������K���7pvMh�F��`<nV����4��^����F� �4����K���1��1
sK�5�`�7M<ȿfB���L��a	��4�pC����P��YtIﺄ�.���3�Ѕ�����?��������Ņ��F��I��:�R�"L��2."�Ow#fS��~{�d��e�H�
Iشu
�yY�u��������4�g���n�Ŧ�������"��+"|a�6��F4g�E��%�iVN�nx��Av	�a��B���XRi��|���8@�%D��
a���ZHVǺ_�5s�.o]��(R�cD���Z�$r����MCb=�����Ix������A�Px7��b�����b��Sb���\膙�.�+ž���۫�Lk� ��|�|`�����7�qrl
�F�5סW���~!��*8�������IS:
�ʲ���%컌�3�V�

��3��w���������4,.@�<��H3�n@mDX*��ڍh#F*`1�S���8���ſ��¿HSS�A;&.�1��c��6�}e�[ػ�>��H��H�;C0�^���k�?D�?�A�w�[�����(%=�\o*
��鸋7�����ג���E��2?gz0���m�p�1�,��;͏�(�G�v���`R�/�:{+eG]2J-�1%)��6=�ݼ�������EN�G��Kg���|x������ob�vW��(�g4�G���$�D]��C4�6Q0���ʎ��fb��L~ �C���Y�`h�)]{f왙�x����
ȫy׵�u�w�"��j��
�R�Hn�?�,_,�$0r��	�a�v�6�(+ֵUHɝ��v?�lS����'�b�FRbJZ�l�o���i�����6?9�5�g<�_)9��|�O�ƽnwE�]��uz2|��8��m,H����G������?��߸��]���l�xb���jmƊ��2����h�7uy�>��<�WLz׫w��z�^9�S�n� �oM�����d�8����j���{�q�[����TZ�*c�ߤ��~�4�E4�w�X5�=�Ғޗ�7_*=��������'�M ��w�X���J���ƻ�(K]�^0���T�|fye/�;��%/�Io���^gy��ף�U߇���n���s
V��);O!�:f1�NL�f���ρ�cz��|�VP��Ul3-�=5���޸[M�%9VW�&�<4>�j"�긢^?A�I��F��R��h;^��n/��n�4��Ў	*�N@�t��*��9�q�1��';�h\�S��M��8O����ۙ�?���z�h;������W�7�*�-�"��c���"�[���y�<
r@m�ø&o&��!-18NQ1�vt��Q�����p�s�=w
�v���V�ߗ���Z�Frhý��|�e]��Fq�t=7��Ɔ��U�ó�{���g�v�i\���ɪ�'i����\*ug��o����l��u-Ke���.ZZ6�-���n^Ul�)"������a�(�
)Ӯ��V���4:mͿ��[\�v;xh��։��]�_��*�4��c�_�;�̹e�'<�T�R&(��?� g�`Z���Ѧ����aѱ7O3�K%�t(1`OT�o�k`��*�)C+|Lu�%�x���&V�l�_�����+u��G���d�(��[H��<�|�_�Ļ��F<*�nppotuf�'��Ģ�����mk�ɖ��.c��~u�s���T�WUw|������6ؚ���5w��Y�24���#ѹ4����������'��+�"�Si)̾��q�E�3i���)�_2���O�'�)g~�p������o������;ˑ�&C@�'Kn�/�{��i��];ɉGGQ�uA|=�� ��]�4���|�R`ʏs�T|��'�F�~�!W�|Ε����/	�^2�Fyʰڒ�x�k��7J��/��ԁt��b䏈����?���O��C>E}�>���0|�O/�J�N�E;oho�Z���H��~��n����R��_�v����{�Ƕ�S�Z;{�uc�iv
��󨨳��
�jư~R�o?}�f�`�xI����5�ν
T���X}W�|�����߯�θ�SX�{R�]2�n���>k3y�h�}���ǧx���J�	����sx���v~��S�eHl����|���������~��ޜ�7�]VUA~�Ҁ����ۃ�/�O�	� ���J��6Y4n�HOQYVd����e��;�fn-ӈ6mv����d�5{��Yg%�,�Ҷ��X��Bd��E�ɭ�*rC��S�T�"qn<��L;��y���s�2�8�V���������K�<�~C�t�Ia�`�r���s���-"G�s�
�o2O�;+��ny��<͔@b� <�NX�n�?�����_���͇o��ۏ\��>�)�e>}���o���O������O���/_�VǼ9�O@���ـ�(�=�NL�2WX��RF`�(Pn薈��j[.{��Ϲg# �aE�XgS�^`\k��L o�ӉcH��8�
�S�$H�T�����s%�8-i�� ���9n/!9�$���Hi�!�����r��#�,<�LH)M�i�y�)dO��??݁���(R�O��
<|1 ,�]��O�<H�:���G!U��?�;N,Up��\�$�
��������� <P����ڃs��?�[��?���K��oߥБ���X�>���TV�D'�h~&?	�<������^�d��i}�,z��2�0M����j�^�&i��+I�j���O��|�{�s�?�)�u�@Df�%2g���;�鮗Ď��K�8�$7�H@�_�$r	�x�B6)�O��G��y{k*�{��ۓg��G,�7�L7ќI�<�:�ֹ��������ޯ>~�r��A��'�b��[N�Pk2!�I�˿�.�o�:wzA��\����0��hÐ�Iy�RT�&K}�;͑p
vb�&�<.,C� r�A�L���?����~��ÿ����w41��CoJ�mR��r�_����%�]r Wb�"yuO9�4��C�8��v�ڴ:<I�:�m��(,�d_���D-��00��������Y�ю�Iь��L��L�=="W��O03�\߾��a>RBI��'�ᮊ^���O͡>}- �l�9O��L�2�\(3����_?�����ǯR&�J��4��0f	4��E5M�`�!����H� �c�²U�,���i��c�2�����A��p���%|{��`���a��n�r"A���H�0��Y�d�ot�N�.����ZH��vÊ���s'ҍ�����!��Z��X��I��DE����o��ǥ���8��c�u�(���؈ո#��/��v]���%�R��O6��A��i+i<t�
\����UnB�y�}L�V�M����"�@.V�z�$��L"���u���l=��5i�}�(���2T��*��CI���#�W��Εi��%�Pc����9S�kU�{qz�������������_~�5�|���?|��K����շ��U�O0ͪ��}9�I�B�7zuZ2�==��u�{
����%���dz���ո�0�F]~7{��u�,���>L��*K�W^��+j�wHf^�H��]�W�.�G�$@_�����?�������s���?v׺��?�n��렪��'L���g��o�7 [`�M�>�`~b��קE��\/�k+���s�7B�~�Y��[��͡.��p�Ŗ��#����L0}W�V6��$�<���
�
�ʽq;�����Ӕ��St�r�#�I?��ˏ?��Ï?}�C������_�1_�*��8�}�����&�O������|x�o>h����T쯘�u�s��o��F���;��m&�\�mIb�	r>Ϡ9�*��z�YC]U��a���qDV}m�-�je�?3|��赜��\D�����$��Wނi��SCW���1>d�r�3��?P�������|�������_�I8}��������B�
3����t���?�U�N���!w֡������"$��)���@�u�&�a�@�L�(������K�,�4F�yP�S~+�I�-|;�'Z{�)��wTs�j�YB���������׿���@F�������1�{�� ?R=�Υ��s�s������J�;FC̔{*ZĿb'r�Y}b��L�[)��7�k^}��v�z�'q͖P�W��L
z	��?��d5�4�&��~
ق/�o�
���7�6066�
�ז[��7�d)�� ~��7�?cEJע5Vg����B��������0$�=}!���I0kO���&�����j7���1�mz��t�g�$�L)]���\݉Gh�f��Y�]��S��{��ql���l���u���d����-�������ܧ�ni�{T��i@�H]����{j�hXKnȜ�ERsK��
_s���Wȯ��7.�������2H��̒�EGt_z�n�]
|:�͚=�-qgE���~��%�FV*��g�0}Ǝ}�d^g�j_��s�Fp�m�.�]Oߐ�Ow��?������?"�e����4�16�g�@�5����_���6»<6�H�{�}E�<�7P�k���&���0D_�i�V��ori^�w�^��^�~�\�ⵁ�iG—��7Rα{%��*��Yaߛ� �T����������~���[���O��<z*R�\
��4;77�c��r��k�������lOm��˓�E
�����)�6�o�AXe_���2G����)-��
g�p9�Y���VA�u�^H�Yx�׻YnI���	w��7M3Y��y0�oz��:*�s8��6����Ə�h]�{�0��ڨH��xg[�l!��r9i�����!��ݼ��Y�@1Ձ/M��(�F�M(?�\��n��61��9ğ�����v[zh�Um���]կk��y���OJaF����Jj'&).Z@����_�q�V%(�[�0�3Br���]�4nCk>�i�+^��]aB�B���P���qu����*b�R��i��2��~�����n�������������Y�i�����6FoF��y���O��5���U��U�x����?
�pC����ݼnH��l�f��)�6�'GMn�Ġ�oלځ�E�c�䣷���/�F��9����$24��[n���ZB�)�z�Z�@�{P�_�ψ#���V��bǝ��:����wfkL�+�,������ҬJ���LB \��]�O@���t�ߪ��Kv|�2�TN�=Vv��h:s|7|���?^��BX$��H+f!N��C�TM|��"�{���K7�O�^�
�9�p�H=����R��w:�#P�v�>h/ĤV�=�����:!ǵ�*��,
|ώ��u�������l�_jNJ�W.}g�O���4w(���R���`OZs��y�7ߙR����7�L���ϼ��C��`�VNcL���3%�Z��d5��g�J$���4A��|ᒦե��7M�5��ۗj�w0��c�.t:��>�ҫ�G���o6�iW�'���f��y��������p�w�-����BY���00(�[������B��A����i�ZVۈSS�3��Vv�j��7e�O</��6*�Y��B�`~��&7�Gl�6F���;�tPo��QCnL�;|�?<�� :��vW*Z���)�#�S����?�������H������3UnP�\Y[�J��+w��~���O�9�VFo�rc�S��:��F�iؕg'إ�U�]���J�e�1?�%��*��ģ^6Fj�m�����oImU�};���Z��5Vs��Oav�9�q��׬���E����yy��/�ÿ�O8�"Y�K��������_�?�Tޗ�y�F�[���N.��8 &�e�@1"���-��r�UŅ�V�Hx4PG�[zJ<|�����I%��GL<a�ම��$��5nkAij/+Nr�?���A����
t�7���/ۖ��R99c�t
�����[���x!f�vA~�-
�� �_�������bF~����P@�R�Ȳ��j�;0��n��4��Π�SAP&˄{���B)��察1�����/�I�������ࢣ:�ý�?}}~vG���VE�{�?�ЗN�s�_E1&V�C���(�ƥ '?�)�Y��\�m��h�%�h!@CY�}��|	40~HGƗ:���_�M��g���=�@~�$Нe�߾|�g����3h���@�ּ�W[` �K�؏�7(�{��l��&Z5z%�IFQ�4,X�%���86z��� ��伒��r2���?�R�\5!J��)!�3+>�^y��'|�h�,��~^��;�Ʌve�~��.��A~��4	��Zt����d��>K���&y�+C>��zt�ѕ�w�3c�t����9Ж�Ғ��jLޅ@m��;>Nf�o/�{jچ���O5e������|F�_:
�3�1^�K����ȄW�"�����f�H���%6ݍ{��,��t!�K��.��f�0~�*�1��S��x��*���Ÿ�����~S�z�bZݔݥo{ߜ	ܟ�SZ��7'�WQ�.�R؊u��>cS�ͫ�I�ah����������+V��=#3�x1�V��	�3�rRv���wTbU��U|Q��֜e�+�SM����}-�v�V�p�8��Hߪ��9�93m1j���:�g��+|�H3�|�g{�)��5
x��FѴ�$c'Й��iv�46�u�_b����?��������g�%H�V��Q=�F�/g������>�6~7�5��'W#
ܰ�b�.�����5�$��]��M<� �WY���	4%w��w\����ٝ�ډ��2�7����W�X#�~�hr�WM!w�jBHr,p'~:)�������)v��ѫ�.��DӜ�ї��u-�w�ٿ����]��An8�X�r��cٵ�Y�ٻ��Ķc
|x\���Pj<�G—�݃49F�E�e�ѕ���k������iNj���m`�V�)'�l����nuD 0�o�i�#���	D�@��٧<���sv���_�cJ�'pG߂�מ��~�Yc�ԜI�9���_SU���<��d�X-�}�]��.܃�a�-�c�ƿt��~_R����ܛO�&�D�/O�����
]�f\�t���S�ǡ�r��k�Z����~�/����q�OV1�Mk+�����O@gL��v�*��P���d�u�x'�o��bv��w:4�ՠ㞇T��}���_����ƍ�Y;|���c���O�ֹ��.s��دVL#��F���,9˖;�q�C14���ho8��{�e��̈�4�L6bе����ߞv �|F���
�
w�z[��\_߹�U�Ht�xժ9�F��wd>C��ܷ�\ؾs�Z�|@ҕ�,U$)9����N��ռ��UZ�9�t���X��r��
<ݥ.�LyQxoyם�?�@�����l�W��1���`����d:��?�����x��Sƛ^x�)d��(5{��	��N�N�0T���-�O�'l���М�o�Z��;{���?�������*̹�Q�3wtk�끸��9M6��q�1����3�@�9���;c�de�^L�ǧ��N��}E�Efy	��d�b�Hy+���~!���}�+9r�COT����(�[�a�$�V0ƎseQ���Ϟ�@�[߻)��qy���������~��m=�Z�[.�J�U5��͘p�5׫*x���n ͒����Sk+�t�kfզ� �^��䪑�c�P���Ī<�[�5��{��C�*�]�6����.$4a�M_�kI�^�W�ژ����{�T�����oc�t@�a�.�a~f��=��͟���_��j���峻nt�Q'rs:��y%%�]2�-7�I������v��n�6Ƀ�����^8	TJIx<��8H>I���wL��6(�W�>�x�K5���� "Fz(�1zFmrV��&�|Z��T0:�Ŷ�t��W�Z|��[z,X�7Ŀ-ն�F� �/��͟r����_�����&W���������
���3��'V���l�SX�w'�o�UnT�3�IB	��f�~U���Ƅ��tK�P=��F���qg����9�H��+���r�q���Rȿ��&�֜
s�j	��ٰ�����5�p�4�z|�K��3í�x�C�?$��;
d�{b?!?�?������D�
Q���S��\I<ډj�Z��Af�Wc���G�o��˕��s<��:��\o�i���9:�	7���T���<�0Z�+��?F��:"<����d��C���h8 �{�T�Z������vgM3��A�������y(�!a���t�Ѱw���nY�w�̨XضZL�w�s��
�5� �)�M,�`^��A�A�p�%A�	���U��
��Trv�@�}��`�J�&R�K��F�d+L'u,���L�R��%�w�a����f�Q<�����rE��
�R�;������n���=A�oCw��oY`��j$�o���w_mt�_�]K3/q�~�cX����}�T�;�:l2e�t�7�.��]�Y�A��|��mA� Q�ƀm����������Ҵ���U��(���x��eJE��+?߆������c߰���Lc͖�P�Gp&�W���}�96d��ݕ��M��#;cl�Q4�|łY��5w�ӡ+v��40��tw=�_���~���G�������?���WBL��b?9���lB�BX�;�\C7[�T�ǀ���(�KR�~���4�w�v�Ø�w�/x`�?��*a��/�1�d'7�B4�(r5��"�,�쭽uw�#���$W s�__`��rCֵ�k�ʀE�M���%���50b�x(���l�~�=�	��J�&���i�S��a7˟�����#�N�^��aX8����W���	r͂}��|�R�_�\�њ���a��=���ѾJQ�(<WLJ$�߈P���N�
!�!��<-XX�v�(oɟȱ��5��ݭ>�_ޛ����
Ɋ���0'\�����8Ԕ6��(
$�^�:�#���d�&��<,�J�Y�M�g~�-�NoX�
�e2�I��HU��*�E��Vhﲋ1(���70������Q�"�m��I��I�&�y�i�?v^�;�I��4���Ɖ��{�8u�F�o�>�6R�x-xE����_�<Pq�;�QmT����7y��#����u�
S�mu���L��uc��+�O�M���A{�����; �<�>�G���%0��/x�ϳ����;ɏ+���4Ʈ���6}���g>6��TJ$;���l�)���?�D"P�j�
�t3�6����wv�n�g:wO��f�l)����z���Q�W{O����k��٦���8�%����;�i,�,��I�Hw=N��.`âqs�h
��;�_�uw�p�j�t}'[m�k�}����#P,��K�������'�n�_(�]J��`C��t�jv���
]aDX��l�k�߂�b\rL���²qh�4W7������?��#�?̺Wu�<�;�c	��Z���w/�Z�7mʇ&rc�pW|v>��2�r?Mݮq�����Gt�B��+��WP
��S�{֬Zόv���'��\s�.S�Y9̆����=�~��`O�����`?Ǫ���������?��/�	K�.���Q����~�m>�e`�8�"��V��{bp��GcX/)0�ޗ���y��_��@땱����h�w5�t{�$��u�r�v�-����E"�{��o��}$�;���+��kE_�@?���W�s%���_~��1�������?��t_g��ڐ�����LжP.{D­{7;�lS��<^��Y+2��/��%Z����./���B����4���E������?�J`���͆_�)�<�\W���5ޙ5��q��]�;�$�������q�g>���H��kLoe
Lg���4���_rp��ӏt�>�͕�\���9�0큂b�߯*��7�U�>��S�ϖ��V6m����(�)gp��7�?��$m<E���U�|��_�JG�q�?��w���?d���ų�-��9N�:�,�?��gtI峎&��-����ɟ����&O]�S����g�xG���8��y��@��瞟�?]�.��G\��:���X�E"��h!���gmA1��`��LBK_�_��v��5�>B�9)��;����Y�%�忟@����^����=��E����Y%��P]wo~U��Ϩ��o�P�(�+F���M��KW�۪ݖ��f��s�X
��_��P��4,�mߎ�x����.�?��#҅t��1�:���i�/r�������lyu�Na�[$_���ֹ�#����4pw�l\��}i�iV���~$�m��g��Ն��۬C&"�E��/���c)�;�D�3	~��U�N�e�h=�5
u�ɫ�=�X`Jk�	ˑhܦ9"W����"��h���A��4���W ��f؊�\.�M��!����f��&M�N�"���s<F�F�M�Ӯ̀�����c��_?��_?~��{
wrH�TS]r�?��~1@�(�j)Q*�j�0�A�4EH8~j�i�i�PCS�`����M0��ԱEn���N=�?������?"��P�۹�����SSRN�6���
��t?h�Y�|N�9�����>��(a�XJ<\�)�W��9ZA��D���P�S9�vS۽
{j�t�>�|E%~N�bu8WdK�
�5�<	��J�H��>׫��lG�EB�
3�3��;Ȣ�`�W��}�	�%t=&��r�$wk4�!�=��i�{NWG��0���T\]��@����W$JBM�)>t	<��������?�}YQ~d�����>��o[M�6`�_������Y6VB���/?�s%=k+'ù��s�p��&�	�1��_R�l5(6��8�
�"R�H9�mZa��Z���*��x��&@��	�n����}��E��4�!�g�*���$��S�T��
o�/F��j�@ ��W*&��G����i��C�쩠B!��n�.��B�m̄R�T9��ʐ
������'y��{_�ˏ?}�������$�Vsa0Pl�W.���(\�|�ۥ_ �H�/���dA-��\*
��z�[��x#@��%�dH7I�	��W�DyBz$������#N)��L�޵��L���?���`jǮ����S@�KAB0�CC�$؎8j�:l%%\��q�D���Ʉ4|�I@M�I�)���w���� �1��_��H_�2.���
�s�;s���|Y'�z�7�#2�O�LI١������|��!�7��fi�͇�|�@�{F}7À��Q�&�|�07�X��{��'�������gj����O2�9�W�ߛo`��dP�!�>��ш���-�<���i���Kj�&��w�{6�|[K������ <�W�%5J 1���o"���ۯU?�K�Bt^�/fց��Nꀽ3�$*�0�J)5�e��v�D��:��M�9����L��١�~�!�4w��/~��?���h'-��(���B"W[����߮�~��_���s���=�����M‰O��~�=?����V�;mж�?���NaP`�ͯTV�O�q�A ��(`OM�D���&��?O���l?���յ��:x��?���	Y9�ɣ�<
i��>�i(�YTS3Z��<��{F�T��������Һ��K��Ԁ�!s���y@|�p�O�b_����g�fv�;#{��(vF�(��G[�r����G�5
vhN��"�'�|����J+�&�BaR~���
bN5�cy��
��H{~&��!���`8����ƄǪ=�@�MI����2�k��N�c`��@���Ǚ��|��&s��"8�O�#�_�N��tA��Cci��I�ӱ�05��4�GF�|:4#����)��~�<^&�=�!����).t�������Ӂ��gN���3����,K�7�+u��.�|�(+��Z�N�AB~�8KELL95��G.�z��w�O�D�[��A�|x�$��Y|^�ճw����q�E]�=�񮶝��M�	h���R^em�5=e�+��惵�S|��ݼ����S�=�k���ur�HW@������|���-��.�\�.�jЃ����?��ÿ��	,��k�g j���$���:C���ruM���&��7�˨9�(NC��KM&Y�wj_�r�j��׎�Tg``�Bc�Mߌ5 ���}G��<?��>��ɝ�����^�ۅ��?���������Л.'���Y�N�9"�o�i$r�;8�ED����G�?���_X^�l\>/�g'�O�0��tr60
!�o�3$n]��[��v��i``�n�4�C�fc�z�s�dͪ��JA�9)1��մ�c�ԟ��YT�Y����F��,-[.����q�0~���+�W
�������w���aF��{O��)�#�ijj�ɘQ��\���;
��{D��|���+��#OL�����V\>gH��H�l����Y���0쿠���v�$D�c��٧����JP<�
����`�� ����H��`��̰�������*[ȊbJr�?<��B�T���|=�+99<(��$�ֵf{B`��t!"��%�y���2�Q��XJ��'����7ݬ��響6��i@R����x����'Kv����Dsb�<��v�e�d�W��!4��l�S�ɡ_4�A��������O��ȼ�;���;��>|�����v:��l�H����Q�4�u}��]<�@o�v��k*#����XK�B
a0N��+��_�uߙ������t��������v����w�/f�Ϥhl��5�\�*�!c����Q
���Ty��]���0�$O���~��ג��?��Xeŝ����f��8�-�K����X{s%u�;>��j���4L�4���4�̭�%�}�Ɔ��$�-I�N����8�Ke��c2��>+�Y;Y��`�gx ��0tMY޽R�ɜ����F�=�5Vp
����Hi�kX��Cr:�(�S�i��V����r�lо������������d�+K�v��]Hh�0�����3�/�%���oV�cڀy�k����tJ�?�a
\5�WZf2�{�3��>�b��tյ3�l7<(��P	Q��1�;}c��ӕ��L3�b��O�y�m^��\4�L�?���������S����y��",�c�Icr��I���D{�wL�7%�N2�;����K6ӫo���}�m�I�"���zw7(nD�O��E�t!^mV�Jy��PG��X�c�׍
 �I@�?w�Sޚ��W�ʨQ�3�ʽ�(Ê��
u#Z&�(~��u�!�|��S�(-��=T%�ٖ6 2�ȫq8a���V?u�TSjN�Y:
|��v�p�8�!n��=9����됈⳦�wr��nz\~�/j��~E��+c1F[�&�Ul�RK�+"f��T0�<�~��'�_��Jvݸ4��<~&aœ��ݮ:��#�9��B	d�:J���i��@ȑK������v;��Y���aUsH��sw��8>��{D���^ �-sU�g琹�4��?�E�ݦ���2�m�0���r�{q��}��CGi�!�g�K����D���B�M�x
'hc�
]���t�&x���w�o��V��X�$��y���І�ĿZA�?ũ��W�[_���w�-$�.a8�'��͜%j���p�WN��x���mhD�;�r�ѧ��3��*׌n�K�<ZSx���@��L�����R�,�z�Q`��)�f$ڥ��_�}�l���*O�H��9�^�/��5���`bk*3E�$zϥ������]j�%�ȷ�]-bp�!�cP�3���j�"���N9^�?(��_ù��NSlݟ464���Ć��݂��$SNoф��딷��=>�_�Zx5�c���j���)����Ջ8�N�����.��G���@�c�$��,i�`O4�7G��dz�4o|��������p�7^�^�{�A�;��>Q$��^n�<еV.�:;�Z"E�p
f��Mw���H���Ȍ���ͻ�4��k��ĭ�b���濳��tu�-��/�9�
�r��]0X́�_L��Z�,y��
z�楒�6�Ű�x���������~�˷ar���+��@��d�ԊG�\5he����q�j*��ٸ%<3�q~ڰ7Mrն�1��bm8<Jf��L{Q�n�ǽ>�yM��Ƽ63F�C�#ջP�%�H]a�j3}P�ڸ�f{��_�������e���K�
��q^\�qe�`	��6FJz�qD��MD�������wi�q�?�A�a��Wy"�j�[�X�9o�oN������J����b���n���	�|����YQK����LE��E���xuMy��?Ȅ�ma�%�Y�b�"] �� ��ۿ�[�$�����:�gO�Վ�r[NZ����9�|]��΋�0�,).��/�wDP���-�,����1-�*��y��ݱ"[Ya/h't�ώ4"�e�}��@���1Ótf#y�@)�{�^m�~�	�9_��qOxh���f�+,	�1O9�$��������j��R1q���!���$��o��jl*(�Ŀ��C=sk�����q��8�B�u���	��o��/!��H��|Жd��&�b���?
��`?��oSR�tV3���I�H��/��eIx�<V��Z��t�WD����@����F[Ei�]�s�On���r��ߔ]H��oS�N=�_~*�GVH�M^�:�s�F	�?���1��TV2f8��/����){�v�.m�* :�����k������^q����uSp�a�5����YCD��F��ν��M�M���A���]!;w��D�#�/��[������/N{ܖP��g��ݝ슽u��60�7|M_P4��2�<��Mc��6.Y����1g˦Y�#��e�mf�����Mz�����������#3��LD�_�
t�o�tλ����^���X{�Π�ҰZ��r�;�k��J!${���5����r�jV	��%����D�_��蕣
��w]!�4�=�$�Ɗ{R�69\yn{I��|���������n�ˢ6Po���f+���z�o0���V�Vs��)5~,g��IL��0��*{��v�8���c�Jf�׵%��Uaj3�gl��1�C���[A�A0�U���)��������@�LJ~�myȎ`�]
�T�v+C�|(��6~0ޔ���|صU�[^��Q�^�Un�4�/�%R�¿%i٨�RW�Š����ulD���n��ꌋ�^�hAw0����&h$@����Q!�5&0t�E���F��"nLGiC�k
%^Ari(�M��6���	���܅�j�Re�w�>'0�v�=8x$�H8f��Ch=�A�(�w�y�p�SD <���L����~���V�G"r�z���+XMk�"��?���K���ws'�c�jE�Om?���ǐ�k"KsA�_���6���>��&
'�y������p�����;�����q��{t���[�n��f��	p�H�t�1�ƽ!$��K֮�tsQd�#�����}�,	�"��.�G�KV|�t����� g=�]3?���Ҟ[�u��gf P;]��((��#��Wjm43��D�u��jE������g�2A镳'i�p�>,�w��o7���s�k
�4�Xw4����K��L�֭�y��՜��>+��g8�N��.�_�B�q>Ÿ9��=��H�;�O�Υ���mjQ�|�QMF\Ra��oG�FX��F5v/��丠S{�'ifX2�t�+�sܪ�~�b����M�hʹ=�]_bJ%K�BTVC~���)���2��ih������3E���0�̓����?��ÿ1�H�ͳ�;����x���n%g���<�[�y�К��(�6p5��e����ƒ6F2f@(����=��{��w9�<iN
�Y��곆/�c�w��� t�L1+d�z��4WΝ7J�K�ȟ7]������?������(��Պ�����^9{W��c�nD�=�{�Z�[wo*`�g1K2U;�㥼��<�J `�r���՘kk�+2��o���-U
%����9b2}gp7Y�V0A�FW����Y[I��ƛ��:a�o����:K�{w��������E�$,'sa3&����k����B�4�_��N����Λv�/��o��	�A��s���7��h��G����͘���	�
��n0����>NEH�c��%��+�_W�3]@Kr�J>w�R'���a9	e�����>v���W{�R��T�������(uk(Rh=�*8 ���i<�`�!��f$gt��/��,�x�_>������0H��Wu�v��t1���~}7��[�:K�~���vx��̈c�4R��.�!�s�/��#m�l�S<+[�~��O���-���s����rR��c=��ZL�H<�B���@�o�����?e��G����4o6�1!��������}�b��I(�T��13��_���5�}c�3/E�h俉�ջ��(�3ީ�� �۪̔��Y�)z�5�N�{*�Ʊ�
���v�.%r"��c.Ԁ���K3���%�nV@�pJI����S΅��4��Do[R�r���o�{�|{�JQw�?�ʖWj.�F�$���v��1�����5m���f^_S��5�띓���׍e�oz�Q딼�����s�;���C2��ՈUaH�u�cH����Z��w>�Lfo%4��O�A��F��lM���KamX
n�	5u��pW!�ȁ����̉�~�tm��{��n�Zot@���-|�k71#���a�w�&��*��caq�*4��4���W�~4�m/�x� ��S��׃����?��G�oMI�������u��Q�uu_JʡWF�f���=�ۧ��Y�V��ص�$74mm�Mc&��|��j��aH�{�;��J�!��h�5ݣ��x���(0iDܬ�\tμ�n�u��k�B��?������߉����:����.f�}�w��7W�Ƞ�IpM������M��V�7��醗��
��_�̑GYJ+�����<�/���!�Un��@��o��_'{��X�ˠ��	*�mA��ѿ�K�իr~���)�X����ޥ�&�׃~��2���20"�2�4��L`���v�D�Ƽ[ݞ�h����^�/��?�J ��
�ؽU<J	򧝭�I���r�{w�6�w�>OZ��g�e�֐��#��\���E���|4�YvX��(]��};�o�&��/$8!�:��;:"��dyx������0X4����d�e��WT#k��gc�M7���ա����o�9����b�'i�0�����Q��A�+Z�֘�&%.i���V��s�<B"Lh��T-m8��$V�����������
(J9��}.��rH&S��V6b���j�'`�ۏ�������z���H �Ԝk��ƿ�Λ�_�&⿽#���ґ�2d�wN2�o"��wb�S7YkT��x�5�?Ύr!���������0�_�մ߱@���6�VS��Y呠'�W�#��T�H �|{�`#���`Jh|�6z�'N�������f
��ֹV/�Ŀ��$>�v�!Ñ��ղ��+��k.�'S3���hӊ�F�饻.P'2ᙘ]u��W��+a0v(�Y�8����gXOX���H�(�����j���H/�����Y�Z�9�"�w=/M�-�M�T~�����vO��@�t�J�Wz���]U;�L���L[%�Cr���������$�M������&��^Pw+�e�:S�~]c�T��p�F�"�l;��\gfdΰ��8���w=��x����?����W�S���x
˿��-ۅ_�3m[���Ѱ���˽��j@t��5��咁��Շ����u@k�t-��1�v=�^�Ao���;�š\�}��R���u���,q]�)�&�o������p��~<�d�4̓����?�����9�����]����^0��|�=Tp=�\���&�8��No��W��������Ĭ��FNa��l˫�{d�P���ͼ���t�8�D�ݭ�rV�G�`ꐠ����S܆�H�q5YG�O�:6c��l���M�eC�m����(R��^f���FXy����~�;�l_��C��`4:�†O>���
�}M�Z|]{_�$y�3��x�/r �k��ܧ?�Fe����1�����
�&�.˕6���`���ƛ��>r�Z��MŒe���ϊ����� �Z�1:�E%O��ifHR9�N�^B*E(.�5���?�8$R{��K%��\x����2D����>3V���lً9�ު!DY �)y�?�o�_}G9?��C�u�ڱ���^���2�l4%�fB_��<b2	8�,i�7L�D���͵+)̈́M�%��W���ơ;��q(�G���Ѵk-c^��ؿ|ċZ`�
����O�A9y������i�+읱\�ߞn.݅ԓ��}n(��hc>_�y�w^�*�#���m�Am^�lSf7��~#f,��vvIaO����2а�z���,"Lp7Y� � �<x��S}i&c�;=ܫlE���%�ï�'�3l�:�9�E�l�d{%J�ɒ�����m�p��6���zi�e���7w�f���л\=B7�A*t[Ŭ!\B(��	$�w"\T��K�3-��(�ޝ:w��3�B$�oM7-��.~s�
A����wv��Z����1GXڲ�eFXˮ��+�d��C�fĄ�=A���CԌs�S@�|��{\�K�����J �̳]N�ͷ�B�֙;~���O#u���w�$QcM�~uX]���?�����}
��B���P�H��d4��t��jܗ:�����kr9��m�8��3m��_�7t�
AY.,��	��"�ڹ�d/�&���e��m[G�!�w�"�0��G���_i��Ȃ����gk]}�1i�wQ$��T��)���uf�ל�j?��!�Ƀ J8��nۖr��o�ZH&�F��_����������?��zH6��弫����r�����l;4�o��K�L򭗫���Ԁ����S� �lŞϚ�]wlY����Q��7;Ե��ᬼ�xdȃy$���R�EZ<aO���>g)�u�O?����'2�#��:�ƯP
˩c*E�W����"��_!�}�h+�v�V$I�(ל.忬^��<B��;n_r��<�+��B(�A���D@�y��$@ݓG�K�rm����Q���du�4wVy�Hn�dV�$[��3+xc��?a"���?'�.F�H����l�,�#eȫY���$���mD9��@�+a���H,�a`p���&��\����~�11�-�3,;�X�&w�����c�#��Ty�Í�3m�)ua�-M��T!8ILvc�c���M�t��x�P��_�����w�UQ�f�_��*K��+6�!`���"I��?���$!s��X�A:��s�v!���ޫrB�7��(��_���?���W�c%%I8���B�]~��z��'�cM��O�ܩ���Ȅ��p��]�
o�T��Sdp؀Ä'ȟ�
^���]�K����S�"�I�/��W�0�i�D!�T�O��_=��s%҂��g8�;-�&��򯰋)^Xʳ5�?� ����A�'L��~�'�ҝW�?rP����.�60�����碻o�s��.W���^�~�C�'P�?�Qf�4�G�������p�!%Wdm�{��0��G��ʋ,yQo�������0����0<����)�����-(�j
R���`���g
Ѫ��*���n�$�%���DGZGT'@8`LY��~�)w�����~O��*w�&p���$R�|e�u�G�ú<����**�I����6?�D�w�?�[u��LN��Fl"yr����f���W���2:"1�p�u�?���<�0]�y,��'��cu-䈌D���	+Z�WySޘ;�#W����A�D؎|E�s��\I���5s��^H	JH��Qu}FI��+����L��t����Ԅt�t2ľ��	8�O<s�G>�����DX�v.-C�é�2���ߖ`i֔����۹ge1'�d��>j��
�d�����,|�yD�f�^p)aJ�b�?~߻].]�@qs�������Е%���m�}ƾ�7�| �c��ب�VW�$j����o���&s�4�h6�'��Ў$0��"۾���?�a
���y�����-�zϕ��j;��ӘK�2�Sm�G�S OK?Hk9
��lR����
] ��@��'�;�����&{eRn��[�Q��G�?�?�?�?�?�?��6���{ٮ2�Y�{�����~G��.
G���Y����>fb#d�����
PoO�;�w��]��{�ku��{VA��9�^��j$����
R �[�r�8���S����]KϔC����P���Ķ��DɲV?�?�?�?�?�?�?��M�i<�O����j�p6�͙4oᖬ���/���[�:
��.	ō5�i�l�Dvx_��;�1P�	�����]C+bK�[�D[^����3�pǏe�������&���;o��	���w�w�����d��O���+T�����i����J<ڣ�����#\h)m�e�5F*��Ƒ$�i{�
�ᔹ�#����#E���Aq��uY�q~�6Ֆ�̠�.[�̷^�c
��c�M��zۣ/���	De���S��ƿ��Ă�*��.��5��.}��m�<��<JP"�X��ȵ�����eຝE쨳���tB�ͯ��]�j�ԅv��^���>ςȀ���5�'��U3����e���P�c����5���k��:���
�v��V3CIz*��	�Ej��)�ҀYB��ÿ�����ꅭowh�a�:w�����as:m�f���w-D}��q��V�q�SJz wn��w�<���c��!e9N=��'8;fF�*����s�M����P�aT�فj��`6�_�c�6�1-1���X�^�_h_M����ݵhB1d��Z��"w��ք
ѻDc�ߐI����E�N�L}j��>�q�5���j��_*<I-31$%l��.Ug)��Z��ɿ_Ɂ��]�f���znMb���
��o�F�1�a,�W��t�\�������
t���6A��4�z��G�����I>�HO�<n7b<�2�Xxl
%޴�>���һ�6Y�
ʽZ�.��x�qf��^�^��No���19b�Չq�T��8�����s��w���F�|_���'s�+�PKzGח�V�#��k�����ք"g�p�t2����������������;���é�����t� 4.���D�b�z8S<��4\��l9'$�]�xt�N��C�UR
��m��D���
��<K�&���M�Y�@`ۀ,��]q���앾��v��3����i쪧�y!��_t����������
�z�z
?w���m��@]�8s��>���:�N���j,����l����V������c'���~xXB�Х���|��b��3�D� ����IGYe�'f��(ݰ1�*���4��o(7�㶡�c1�Ơ�?����DU�m�ٕ�|����\�*
:x�s/?q�\lc�6����S�&Q/�&�	��"����dr�������l
WX�\d�5>I����ۿ�K6ċT1���ND"g��������"Mv�c3�L��F��wC��v���>��Չ�:49�L�ai\(����?��?gv6i�40m܇Yԕ�o��
�V�~싎��K��+h��L�O����a;�q���A�}�G�T���?�L�1�
�8�Q	~�*Q�q� ��Ł��ds����lO���bir|FP��L&� �O�t�V|k.���06�J�\�K�V[�x��M\�%o]�0<E��_�7�߿��?M�$g�m埏XW���_��k:�r�ǹB���3<��Ox�{@7��r�?�o��𓉱
A[�i�4| �D1����>�D0!0&I�I����t�����J�G^�Am�|̌&��O��U���"Ϸ�'�����T�����vƚ���>|X�&����Oj���W�G����Y*�|��tB;�b�'�C��*]�{�+9���V.�i�L�gn�W�?��u'p�\d�!�>|�ѿ>�wӯ���r��܏�fJ
f����q[N!�&?��]�}���z�6��Ǻ#0���n�Oz��,h/U�]H
Ъ�N8�6����f{�g 	�8���9�{�Tc����O�7��Ƌ$3*	�`��h�ZЬI'~ʌ����r��V��\;K���]y�݇��o�ָ����uM3�H~��k1��{��Ot6ދRػf�-�[�Fd�{������ÿ~A���q�U�v�W��k��g���m����I�7�2��cE�#��~�G&�:\��7�y60�V�带��
��Jb�������y3s��E/@���	U�~�H~2�����n���p�����&������������;��᧲���o�o��c�������̬w�ސn�K���e���v�U��stA�TX"�~���u+7�mT`���3HT�J��4��RM��<0�B��%��Q�uo��J"���m����Z�0�0��Z��ᕫu�Z� N��8�T�?q~��:l)�V�F��� �b�D��t��Y�*<�bH���������{ܕ�Y�����vG��kOׄ����.�c�����a�.�#�p��.
H�I�S�>��1��T9C!(���1��	Z0i��Ä��%�`I4����G�����������~��q������������j�֘��܊w m������¢m��5K�\`���ܐ�9t�W=svτ`��;s�.9��a.�a�ۖ�����q�m�����A�׬������/���
H��%+m�
l9�
�92�P�V�w�����Z��������s���^@��$���d�n!s����'�k8;�!����/��{�sw~���&
A(��8�4��4c���s�?};�I�+��@��#~�ꎉ�u���o���(��ƩEee��3jhS��^�H�v�)P���5�A�H=�t\�r�t�o�f�*�ضn�>��l��k�V=��,%�燓�>�<�O��r�٘1�v%a�}���vUh�~�ή�!��
W��\+�=eI����v'�@��2mY�P� k��[��e/l)�k��m��y��g��*#]-��	)�Uw�5�P��uE����Z��B�p���J�ݭ9�KUP��)��k��K�%����ȱ=˔�&q �ޣ�.��T�~8n�I�JR"%��i�
ip�H'qo���!�����Q#����!�b�����#���������N��*`;�s��c�S��̛�o�O�.��T�*w��ڕ�P����]�0�ϫmT�ø�QUr}w��[��d5�g�^EQ�m0�I�l(*��=����j�B�/+��YG-(��-G�/�������ÿ{�kz)w(�U>w1��pM��� ?��Y퍶�4�9��vE�70#<Q��I��8�(=^����$��@*0�V����Z�*� ��cz� ݜ�}.���)�V�}�M-��� 3?׳�`y�ս��S��n���Y�i��?�meEƇ��AA�T�ƿֱ$1���*���E��B���DK��e��ɡ*���[r-X�Tc�=V/�U�G�r6̢�������6�f�ތ$�����mb'�<��
���4myTHzahr��	���#x�_QWF6*�(�
ʤ���C��K�p?b0K�2X��>�4!���sޞ�M��=F �>�x�KPEd�̝h��=iL�2��hu$��S�u��'��ϝOD��3SN�%LC��B�?c�������4�/A��?i]�C��<��*��῏J���g{�n����?���>.g�_�)gO���W�y��\�_н���7��c��7r��;x���XK
Ȼi*�z�b6<*z��Ī6�z�,2*���z�)nЅ�$W7���ǚb`�$�?x��M{����G��K�C������J�fl��4�k���;��/��y��|�/�n@P�6��=�W��v��ׄ�ͽ%��-{U��ñ��χ���@r�fӡ-�J�i�G�Q�
*(
TۖB���?P�j�tܮ��39�xO_r�4kVȐ���ΕbG��V}4cH9U~�#C���5a���S�%�4�V�b׽���ܳ�şP�'W/,�ܒ��E�Y�G�1���)p�^i���1��5�^ל�c��Obsb����������:ڠ�%*�<(�J�]H �'*��������6W�T\�T i�b��}�q�p��3�8� ��}8��k	N=%�
d�m�]���*���=���[}*�:�\�J�g3
�ɭYl2��h
�����������������˿GbWI���ׄ�ͣ���V�Ux�Db4��a´��TP��H���W�D��~�l��&�[@v�!G{���J���XL��[i��&�b��a{$rU��н����y�J?��c>^��o߾�
�<�����!�9�)g��C"Ҩz��%�������mSw�4#4s��^�&��}��?
jr]��,X�y�������Z���]���Դ�����1�_�1�\o�kD$?�_[\B�崚
�ir򪪣b̹�������_��׼R�;'EF��$#H�48�Ї63AKC��ە�pK,��� ܩU)��I��E����k���*�O����a��4l7F�5x�WQA��qn?��VI�T�_�y��е�����=�#�txy����Tp/��A&�ey�6��7]�ʰŔ&��oY؉�_���������OO�|�O��03�f�QՑ���sg�|��$�`�g�)P8�ƒ��/_L���:~q�����q�I�1<���5놓羭F���M�j�
�&~W�ÿN���k�x��������?�;�!��>+"�G"�)1�`>:?������Ot>)�������rW�WV���Z��4�?�	�z�����=��tD�K{��9a�N��������Wp��!����*�@eT��0 �k��m.���=JIQw��A+"Zk�͂��hC�|��|�?��R�$������>�E�Gu�a�aF�v�5�ۨ~9���Pa1���BX�G�:UT����PB��h^M˰ۃ67����t�dh��U[���&���48|�y������*�>���S\��Z��w��E��m��n�l;�������w����/��_�ru_C��v��4��r|L�F`���v�;u�6^�l��Q��hE��L�C�b�o�JX��R��n5�¯�K�‘S�ඪ�dD��v��:�W���U	U�L��~\�F����������Lw�Io��XG�t!en��^���w�h�Zn��4]0�>-���J��S��n�nF�;q�64�t����rP+�B��B
υ��K)��a��z
�#>ڻSJd��_=X8�F�/�Et�u��-D%���E�o�o >2�õ�l�O+r�0ܝ-�T�[tW�r�_v���9h)i��yd��7���Z�<}G`*=s�
��K35�<N��ȁ�
a�qSD���eb�t{�^�@l�<6���ej��N���[��p���w�!.Y��=���l����>�e����U��� �r���#�\p��l���3���O�c(�U�K�c���b4bn�5��3ߕ��X����?��i�u�]5�QG�9��+�+���|ĕq�����9�/nߊ<���Z�mnf#8~�?kkn`}絜�N�r=�ϪчH�q�i]�[�"�&$w��W��k��[�?��s	g��sn鹦��_�賹w_)ׄ��<��Lk�
w�d)���)�����n���?���#��"�bBM�k�S�FN�Dt~�J(��Ed��o��.+q����G&>���:�iKz��3uH�z�w���S��I!�z�(�`�
�W};6�IG�3�r��AWXr�+��+����;.�B��Cw7���4��Z��z~�پ&	~�כ��B�����|~}(56���*q�7������fX���WA�tH����d�˝ģ2��6n���m��Hl����V��S�!׾�Op��B���[�,��i�r�������{���b]�Y!p߀�J&l����b�������,v��.�]r���|�����;�7�� �p�i9q#
�)UNC�?N}�r�H��z��ܕ�)F���'C�g���N��$@�aJ��f�s������������
��E{�����G��֯�e@����I����Gh{\�Q���'orJL��S��3�>B�|݈���M�Nl���B�l��V��� ��rh�s�"��ilr�v֠���$\7JE���:Z�J��v�Q��m'-A�ox�:_�3�^�����:_&^>������`�wU=b%���Gl�	
�#gL���o�+�w�����L��[�
p��W*���6��,+�q�G[\
��1�А|\���>�ۯ.^& �i����`�t,�S2�,m��#��]�L+�sh�>pM ��f��[w5��^e(�pc��#�ÿ���bǥ.���aC+�ڽ�3@������#�wC�f�������ƨ�2�Ȥ;��o%�*O5�;�=7ñ�T�As���d	B ��O����$�D��#�8$�A��gH�ZC�����`����V�������I@��W�/�-V�r�b����1�M�kHtxDa���m�C�m�'I�!�� �-���u�SgOܳ8�w�T�^�_�K�����OTkNn��	n��SB\��!��W�(M�ϵ-*O����J��g�k����}b)�o�^�+O��ߑ�ѧun�w�
��+J$aB��	;swcЮ04���hw�!�X��'t��x���p[��>C����lm&��B�ѩj��>�a�t����Ӣ;�1�G����������	��2هL�*�^����-�v����
y�/G���9[�g���Z�MX<�	�9�H'g�Nys���rh�k
5�'n�f.��)��&�2�|Z!	dzge�{���bU/&����$��p?I��Qу��N�	�]-z�� '�R�1�Γ��n�|#�<�	]5ݼ^T�{��̺�2sxu�������w�=➲��|��r��ĭ5��W��k��`8�W�k�:9�,<�n��W!&���h�`�1�#��D�=�d�˵�{G�$�������r��Z����#~	��%�5����\Gf+�nVs�p�Ҷj������p�g��7�v�A����
���{LR�s��OB�O�Y]�1��~9���������ͬ~�9�_���h��7a���5ц�����[6s)>�	.��G~��H����H��ijK��X
a�H�>��I�Q_?���I��)&���9s�JM�(
v��p��J��!��d�VY������P�
���:9I��A�a�5Ֆ���������4a>�5����4!N@�{b{��|��r2c�+�]0?���x�?�����o�5�i�uT�$�m�Nj�)TP�r��ZE�צ��}�t�`�׋� �:���-�i������%ٱ"�fc�="oI��1��ߘSxX�'Cu���<��\��i	�61��>27��>�b�v"�a.H�������>�zz!�w�Àn�蜞ሁ}LL���|xs�\��	�g4������ڃo\��������W��
L}#S��M38ˆ7�Y��v�@�k�������#ޢ����8�8g��8J/���P�LG��t ���F����L
��Fs
�3�y�To~P��;�d�#o�(�,�x|#������c#�nm�l��):B��X�uܔ��G�:���~/n��..��B�K�#9�18�;\��E����gq��	���	��G
���o���.��/@��c�����I�k���o���VS��:i�a�?��}�]ft��S����
�|5�����y}7�O���z9���%js��E�P�
Lͣ�#��~ʟ{�Gii*�F"�M��-�&P7�L�r�
�����HA��D�|�N��\���ވj09՞Ğ�����A�گ8����6�'N�|?�dn3�0E1�6�"Q��O�gD#ev4�OiD�7\�ۦ�ш��a�<sda�{��������8'M��;��P��a��Ԏ�̑P�δG4�|�r<Vf"�?����Ai֡i0����l'���[�R�Pj���\S�ގkiÑ�C����Ź�p;��6͟	�
p��Ll�fL��00�1���L�Ȳ��v�.wL�vL��
2�߾/���*�o�Wr��;a���,���"#�e��==M��c��7���6���seaǼ�c
v|ө���{3
����Z�����?�n��c�/�T\؛��}[ךX C�O�������ѣ�*���P�卌t�/�T!�u�9�/U�v0�|	O�~[ş\э�J�m����pF�'+��ߖ��-�����w��Ч���ܼ�[��G�?�?�?�?�?�?��w�Ķ])�mE��UL�]fu�������x���k;9�Sڻ1~U`7�>������$�n��g��=S���я�!�x8���C��VXy�8M(>\_���kl��!(7�K��.���������?�[�͙�ދ��=O$��Vy\�QN�U�/Z
������&|qYk�a���W�]����-\�ҜJl�h>�k%H�%�!��V�$R��h8Vq5�aľ�߻V���f+%�Oٛ[��_~�h6b��ZD���5�q��B�1����X�cW�5��CL�7�Z�6�S�=Q�S~J�+ؾ�vp����~�r��q�t���V�9;�J:boV�O<�ե��،CO�7H�SNZ�=a�rԡ^u_�I��I!h��:�,���vl��;��?��?��?�)���p��70t�c���R�ӛ��X|����|�+�}���Я�.U��I�}��}yuCV�U��v���P�z��0�O7�����{/�aȧG1Ґ2Y�|�/*@��)
�Ob�A�'�����ME�o�����:q�?�)X�5�7��ƻ �d��0��S0�A��g���\���?�EąH�l�N>Mh������J��� �э��*~��?2?���3oRa�O=?�0�ޭ��)[����b������<�ϛ�C�99��_Dk������/��!���[�m�k����Ѥ�,������o�/�V�0��=m.�0D���h*	�p����A�A�Y4�FF��N���Ztm.5B�-$���XL����b"��#���sK.���=�@B���:
p��D�
�s!*�؞_U>�_[�j�L{m	Xo�j���D�n@����xOV��:8�
�Ea���7�c[z�k"�u��W}�tr
i��R�[�T�:3�ʆ����(2�67��C��#�I)����uX��K�2U���,�,> l��������1���Gl���m��m������@�?��#N1���cI=T�A��u_��u�ҏ��F�~�$S2H���"�f3MB�Q�҉��t=�&��ꖼ
r����z� ��䑦]e����w (���������/ǿ��v����7m#�^z-K-��U���p�m]Y]/s��~�����TX�� ����,p�����VW$�oRN1�!y�l��?�[?w�O��l��E0*���h]�	�)=�W��N�(�+�,?�W�
���r�=���|[^z��_��>�&&LA[Y>�bKKk�=Y%�&��PF�̈M=�;�a��	�2�O9�G!�L��cW�v@�ǁ2G����V���:tJ��Xa7�e� J�F�0�f~8F�����V�;hn��P�Oڲ�����-U?�m����O��R����	�|���A]tB:z�j�đ.B��b�2��ص��Z�T`h:e��<��vHi5�;�(e��b��IK�핵c���8[Dž�$��yұL�צw_O���fn�R�|�|O{����O�g����]�Y��w�?�ihDŽ3ٸ���/�7W3��ĩ���jVI����?��K\�Y�q5�V�q3��?��xx^���@qK�KvxH��s�ȈN1S����W�7�g�/\�'BF���|��3es��y��Io&��2��-,%��뀪���	�~����N��K�(�G��2�/�O�o�o��a�ı��m�v$�7]W�;��g�[ ��"�LŠ�|z0�M*_Y�\��h0F�_V���#G���W��{n�4�qQ-j�S/H��n�r��Ao͗�K�B�`wm���2%������Q"N��@� H
p�>��UPW�<��x@ѻ_{+T>SJ��U�FI�aZD�-���i(��!�=����{���vc��Mg!�W��PU)���EA�������%�=������ῦ�V���M@��|��tQvp;9Bث��uh�@��s�&�wRy��s$��O��ڋ�;�m��jC�j�W\�.n`��� ���'�/���尵[�P2�,��a��	�L	�ֆ�k�I�����Sk���������������_�Lv
)a�-�F�b�Mm���O��_����y�o�#����Q^5���I�~�,
m/+XR�m* ���$|���I1���S��C@"��I���\�]a��,�N�Tڋu��{�RK`���dp��.Il���z]B"+us
M�f�K��5M�׶�~���p[�m�X�
�W����O9�ꇂ��0�ZE�S��`b���d/8+��O8�L���ߴS�)cj�-�tk�$BA~TS�4�����_'��p*<mO+���Sd���. ����N�"�t�PM�X�˚�#KA>��O(
���P�rǮ����u�#*���@��a���k��	enWmBG�Il�D�%�G߾}�k:�L�F�>H��?���A���$r-�Z���my.ꠤ��gɲI]�Jh!l}?�����0!��[W�s�\N��_�+��(g-�4�J�g�B������Q�O��_sv8����q����6.�{��aG
Tc�ou1��o�ݕ���;��\$�4�W� ?��Gϧ�I�V=X����vD��Ɩ��&���7���O����Ss��(����Tj���;������g�a����s��w����,�_6�+���K�r��4�q[�yϽ�J݈-I�~�d�MI���O�_w�R� T�m�m���s���?��;�|���0���?��s~i��8|]��Z��@�¶�mi7�"��°���DzO�?�<�o.n;�����+݆��T�oK

�b�|�v�!�z��U{��`G�-a����k�
Y����r���v�ZiKD�%DJ����}X�-���?>GoZ��ZT�p�"�q%��P��%����������滳$9�S�����Dmƺk�ZG5}���F���ۮ��O��0��^�:�A ��(��t[�R��#�+>�%��}-@~2n��/U��C��v$�K��_�Θk~V�Jl���κ��������9�o����,�EVi���%�n�DW$n�"���C=[�ŷԓ�T��/
�H[�"�2��d~�0�ز�wKK�i!���"�j����V�E8������.u�����u��$�_�;�B�L�UAUm�h��
G�����M�n.��b���X���r/(��ޑ����ӆ�k��R��rR�ɤ�%�7&��H�Z8z�`*<�U�D���ôP���<E+l%Ob���������qw�W�Z��2���M��x`]�}�9�'#����u
�5����y����)���O'��t��f������R�%tъE�ڐ�:/�~��:�Q��S�4��{��Y���y�����ǿ��/1�N���������q�W��fz@Q�XB�*մЋ��K|(s���w�4����H�����o[�{����8�ﱾ�)���s��t�yyJX��|�_�C��⟣g�	�~VH����Co��ڽ��6�s���*�m��V��;9.�"\O��5�����ƙR��!6��=��5�E[���:3h{fՊ���;����r;P�u��b��q��ƿ�b	ej�S+�qկ�k @�D�����
d��+G�e.�B0�K�5!t
A����>�]��it{%)�W��ڭ@BB�L�+vږ}ڻTS��$�θ4��B�z��r������Q�TB��C����K�ʬVɿ�l�ȯ	V�k���}���=������YՅۭ��p�dwVMp���KTpފ���v*WU�V�����R+��/������������0��(�۪EZ�VEuj�v�ķ�^
�I(]K)�����i�.(� Њ��9��h�����s����,�����Y/>�k�T����𽰫gwB��QڥbBY�
��EE1�*(�����������_v)�]x����\w����һb�~�I|b����ߘ��JŠ�<�����p����'��ȗ��	��6=���HLz�^E.Y����)L���nĩZ��my`�D�4�6��ܿ��.Xh\-!�Ǝ�^GKmѠ߈O��(�m�%CA��j�3���b�0e%G��Q(��m."�n���wJ�u(�AЯ��b�Z)�@-����H�,蝍��K6u�-m�C��
�2]�c��%v�D�����k�3|��~~.�`~�ȷ�`���}��ѭ�:��Hc���c/�~u^5;;5u�IK.0yp��t
��l�ƨ����saP�/AFvm�H�*`m)1�=�1|��豔|n<ˈlW��\��҃����SBXh�<R�-\</���ݞXX�O����Pt�S�����ݏ�淁wCR���+"��O�\�${�H��C9u���?�_]���=Y,��U.�C��������~4JZ����T2V�O��������#}U�)���G�S�|3���''�Me�o&�s�W?��4s���ޮ�X��u�?�z�8\������$��G�pN�nd�/�`|Ȍ(������?X�e�Vp��Aܪ�~[#ڧ ���s?����=
�n�������#چĵm�/-��������F�����#�2�k�]#�BL��Qw'3�}��=w�q���F�P?�Q1=O���[�yy���i�B;�o%rO����
�DL
��c���OqP�b„}�L�(�>�kQ����\od}Tn\5/����k������I���OJA�D�8_2 }ל�[���w�����������������g��v�>T�O74:F�;�e6��*�*����޽Lax�����)������>��]�Ro6ռ��eG���ܰ,&�GW�T�~��oEw�G.���.Æ�%u��Tix)x����������
�Xq��۴���gߚ~׬���6�Z+��þ�m���p1��mͭ
�_-<��ZX�	���\�p�5�$�]��ĮD�*]2U�G\�B��(��B;$�"�O9K"����:;�nY@�B,����J:t
n�#BW�C0O*t?*?�R7�oS�ʯ��N08��Z���t�y6��'��)�Jrk,.��%}�
]3Ͻ'�(���l��ɘ�oZ?Ï��P��3~������8�a2�}�˜����8�IO�@=���;aH�L�������b���{��P1�P_���
 ���œ���Å���)w����_@ �0�#���@��U��<�8�Un�H�D�b�G�N�}��/��Ǹ(c��۰g�<���58�k���I/�s�Ն�����5>�f���!�{��C��.��QEa��ū����߯v�ub��-���@��5p�1���	������ܹ�����G<��h��J���t��+7|�!�Sn�p��{�k�ȬƁ�B�L�#ކ�f��3���X�u�~�t�
�ǒ�Ğ��W���u,G��k�3����Mw�1�V��~nڀ������}���v	��|r�J�*�zt27�X�Rl<�6�p��}隮
��"Q�_�Y
�6��r!/�uSw�]#�'�4x�O����\aA�>S
�Cu�&7w���V~���[��B!��2υ�B~mh�b��>��,��	�{��b�����W-�����o��wG�\�j��ZZc�j�;�p m���~�������_;?�m��{�whgn�j��_q�2������}-����O6�]�kݤ}��㷻2���w�bW����o/,u�	}������Du��x���ِޭ&&ed�4JƂ��8�l���x�����������������_�9��Wk���_�Τi��b���1���пѥ�ufӍ*����^v��h�S���FOo{�V8,�&R�:̢0,#A�-;<��b����Fl�+�,���/�.���gEӹ�l���6@�{!7�_��e�Ֆ��P�n����
`���P:%�W>�-���[Z�Dl����n���f`�� �{j����'���\�N���Y�Ta�f��'O��>�;=��\Pn��.l�����-.��}�c��G�D\���i�ə�dF(E��4�H؆ݕ�d�	M�N��D��P^nF��ct�M[Tok��@�1	��3t���cñ}b��=�^���<E:��A�ß"ϒ�7����u����_�4�ٰ���\��7�����m���Tt��'�N<W��E�wr5	�٩.����y��Y}�?��߄��{&����U��ۥyj��u�Dž
^���)����.���m�Ԅ{�=�{�������+�]Z�����4�`�!���'�"Ѩ�V���2w��QP�y[��������؝�]Wo�׫�Eֹ�|��|�j�@z�΂��zg��~V؈�@&���8���T9_Y�F����\U`���J9�S�՚��75��v	T'+�zD�����yl������f�C՛�Ժ��p"F(�0�IEND�B`�PK
!<J21@@7chrome/browser/content/browser/defaultthemes/3.icon.png�PNG


IHDR  ��IDATxb\�b���#�a����k'���܅+y�g
I�cKҌ�{FU�mi���qh�#��m���4������So�Fk+;���}۵B�	��ڎ�&	j�,��C�R��+S�Y�:)�x�d�oI�l,�Q��2
q\ʈ�x���D8�M���MB؝e`ɱD���k�3@d������Zö,&�	߄��_bϴ�_�^���&v�e��Vϒ�3X��c�Ex)tر�^7����0����3��e���jm*Gegw�?��<ۏ���?�x�c�p4G,����٧%���_L�g��i�p�#F�a
eU'�*���'j#&I��f��p)�[�Q�I�HSU��&��@n���qy%d��RҌ�GwU�))�Ln+Lŀo�焎�����$5�c�����w��2H�,Ns���%��>��������P����??>:8@�]��1}�H�ˀh���/��t����͛'�͂��&J+PBP����
���T�|m�.oN|�h���6��L7췾�N��|2�������UJ\�ޒ�?N�aʮ�ˎړH�œ=��U�\e���9N�5:�	�_�U7��(m��2*�@s�ҒI0�!H?�mˆ�M��s��J��2��c��{'5�nw��h��f��U�x���;�$v���{��r�шqj�ч�Td��
7Ja�F�4����f'���"d��2�R X�F>�ʕ��}�V��yg	�̶/E�yߑ����)ڙ'��IEND�B`�PK
!<y�������:chrome/browser/content/browser/defaultthemes/3.preview.png�PNG


IHDR�d�oU��uIDATxT����8D��i�w�'�4K_uo\���TjUW�;v+q�����{?���F�7p�>���-�A)d���Z2|�Q�bWQB�U-Ԑh�v�J����lCm[=�Ɲ��T%hl��W���g}��H���bH��>{�wJ���7��pD)}9��VI�g�`�(޴�M{޴��.)%x0���|�.Ѳ.H����(\��w���}B�.�H7��̟��LD(��T��
C�8J��)�Y�.!(�NU<'���yX'n���]e����1��ܧ�U��P<�7��)�M`�:^�~�m�O댔�IN�]�∦��㤄r\��&��#�wk�]�-����"��D��&�������ܤ�q�["��!�7�Ƕ,��Pv�a]@�ۊU<���g���h��@s�
'~��RE<o���rX�I`:T������΀>�C���
O�����h��h8:�%?Nf�*�Q�
��e�5[J��VO����8s�0q갚�8���i��?�=��Ɏ�@0�ר1IqU53O��$v�L@�?�e����t�����t�8ے>�
��	��c���M0��%m�r���KI_�<��\bPU8�#��@W�U�}R's�r?� ظ^�!RW7O����J��M~�h	!�c�r
(�E�
a��B{}�1��+Q2���}ֿ��p<&R}��&�YqB�+Nt"%A�/ ¿��s|ԟ����(���vn�6���=j,�c�����k�y��rM.��0��wv�Q���_7$n8��}ߏ���q^�y���Uc��!Yd��$Į@A}�<��f /�a��-J�76`�'M�z���-��ZVvaDĭu`]��o�����s���I��@M�Ex��Z5$-	M��%���b�b;l�:__�۸����c
V+�clc{��q\�vC�ڂ2��wš�Yu#��+��t���)[AZN��K����a؅�yrn��2uV�����!����+h����o�^�t�MɎ�	�͓/5o��3A��0"��(�ؠb9�"�[���ЇgiE�9}9l`�J��L�1w]m���k5��oϓ���wGn��DOO�v�����fG-�
�-��j�c�ˇ���m�`v'����b1���M[�p$�"?��2�GJ�����r��ړ.�����Y�;7�q`�c���u?oo���L:�ڈ:6���dF����6C��}����~F�y�J����Q@�?��k�=�������'\Xn��@͐���P[�2ﯴJ���hE�x7�����������a��6��}�[��{.�1u�0���2)��K5$P��I�H��@B���̷�ga��
�aV �3c-��*�3��]֯��h#�LGfqҡ'����Z�xx鰚_��Q���]Y�ע4��i>lה)o�ב�F���X�w%�Ttbp�,�e_3VˉA�'���v����/EBp��?����Y��?g�1y��5�_�b�1kR����~���PɊ��<S����+
?Q�~=JY�od�l����'&�~��r�F$.��r3͂�Cw��rPKt'�Rs�W埗@.��=`y�Q�/ҏHμ�}�����y�����G�;ʖ)pJ�=+��Q�`��#^d�]�_G�3.�I�9��ttfz���^wژ�eAp��x6�)���V>�Z�6��
pe�����v6�C�A�y~�f��C���Gc�Du�
[w���vC�W>qi��ӬT՟���Yd}��J�i����^�f���\E�Gɏ��E�,�ݍ����t�i^lk���]���d�{�ӎ�-�"�I��?�H^�G�V�#��������F��𯪠��u�*54s[�l2m|=�K������ww}�?�X������5HP�v���{S�ydP;�
���v�T�l$6��U�f\����Un?����Պ�_�⿾��&�ӽ
]���27=1�)�w^����iџ\(�(wO��~����~�@�P�3�>Md�yC�`V|��NzD�v�+�+V:*$G����f����h���%7(�_GHr~�q�Fv���	���Ѻ����rz�i4�|��S��X��0/�1�P��AY��
!������Q���7@��e��x��Ldz���^��k0[�J٩�ٯ���[Ը��C8����K֫��L���7ث%y�F�y�l�߻�<4̺^N9K����s!~�Vz��]�K�_���~���߂��Iߑv�s�\��Y��o�J?�hqr�k�3.��;�Wλ](5^�p���wb�L`��.%�e��4��
�d����L��F� ,;�?my��W-��)�Zbq6Ü�y��Je��	�A�J@�;)�^R��^����&�s��0sLH`�jF�p̋�`�J�Fs`Iɬ�"���6����{QZG�)�kC�3��{*���Ȳ=?�Q�_�x�7��OXZ��2.���s/��J���|�Y�aKp!|���`��F̼�ά8�(
P�n�� �gՠ�L�9�
y�1>�|�e�����ëO�?a�EQ�f��'8�ʓ3
[̠���֨�#n���+5����&~w���`j+)�K�[��M�|̓.��&/�$��ph���a�Tl�Yʼ�|q��� ����l�M�3+��_��B�[�(��leaOq��+"�u 5�n�l���{f�c#:���-@;�e�Ԇ����o,]K_���!�Z�=+C\N��+}PG��L�e��4\��ڐ�Q@���x^ޯ�hH�ƀgc�L:zG��k�#C�#rd��u��Ua��F��B`E'Ԥ,�4�+�t�t�Cە�uXcLLD��\v�=�cT"��x`Zu�L��<ax��n1��[�t�{�s��	��/O�����6Y|��E{�� ;��z�!��K#�*�u�s@�/���cN��D��6�'�;(��l�v����R�+�I-\y8��1}~���]�I��D �%BS�d�%�Яo��
�Af:$ɽKx���
;V�.�g�^�J��
Ʊ�/y�c$M���툂�;��U*��'�R��GG�;t�\���HbX��.�_,R�=\`�;R�w ��:e��q)�r�Y��q}C�p�����q�1m��]n%��6�8��)Ctp�O������c���I�i�dV'�����w�S�\��$fr�BH�b���b�uЁ�q*�3:�o�����jϑ���R+��[l`�5����/^��_�~���8�DO��)|�r��I��p�p'��p��c;�������_Ws����owN#�P���]ш�J�s���A�Κ��8�;l/��ik�?��@hwY��j���#.Gi�?��3�M���N�μr^<��9i`ڻ��(�[��載�{d蜮d����`{�����
�����T�m!9Ͼ�c"�������Go-{��B���%��'�u=\�φR+����l�;;G���ƽī��
�<�i�==�m�[�w���5d��6d��lo�
mM::I�f{�߷5�n�q˭2)�'d��� b�V\a&�b�����_�'�ȠM}�ۢ����(v�7my'��ۑ�<)��~��~phh���VsЭ�CK�i��=�]D����˲zC�y/�	��L������WW�S����NY��dęR���]YO�Y����|����`]_�S�k�')�߿�?�ܫo�Gp�#^����1bJ�.$��kŜ����OO].3�|��6�Ҵz>��
yi1�7�����A[�&���8�Y�f�ğV���'���S,Z�x�P�lH���_��#��7�w�R�⛲e��	��i�=E��k"y)���1	���OpeB�0E�
}��ȩ��Q��w���P5��űl�_s����mAC���޴����n��称�ҭ)�uh�Os
���'�����F8�t��j�	+���OE�Q����k�t��=�"�rY�����\�}�6��j���+�U04^�
o˱�c�8��g��i�|�LN�M��<����mQ��T��N���3���l�񆳤'�9'�a2"_Ƚ�i���JZ��[b����a�,{�6�!���d`qx���
%�0;B+��&c�Hm�c>�-#5���]v$#r�B��O��U�k��L%�˜n�E�y
���GAN3%�֨ޯ�����wP�t)�}��c�)�6�=��1��12����Eڐ"��(e���G��'�5�������G��CALd�F<_��<��=ܠI�{�?˹f(^����q���	q��b��b����I��f�D�Q�27�[3�Ӯ��V�
�2Ѿ� �X�?F�#�Vz��m�� Z�n���ԫ����-���ܝ*�2@-�.孄���2C�D9.�Uo4��T�ƿh�W�<���A��&j�r��l=lC��{���)~B/ 
6��j$�|��v�8E��T���V���5�<�Ǎ�fˎ6>�����L*bօ�`��v�'I��������ޒ�=�D�Z����RҞt�S.V�'�˃� ^�Dɝ�~���
+^�GTa+���+eڒQ��5^rd8!�"$"�;9�e�;.y���	�w
lY���b�4����[�ͣ��t��Z�X���� |~y�{�/��@�O����꾞�74�nY���A�S�_���t��pЊAa˼�7��
�4G���̘�-����p�q�R�����!;�t�,$�V4���wkR�@Jt�	����&�)r�>5��s듑z��;�Oc�;��'�6�6V�t�������KN�S��[�9�+��*�
�b�O[7_���!"'�h��دk��:Ѽ��'�{�%5���ğ4š��ޡeY���;m��g'�=�м�Bi�5�jv�WB/��j.%sq��n2wL|�y�*--��}�rI±2nsI�w)�Ɛ��:���;C�_������a��Ul�Z[�Ȳ�o�96]��Ǟ�Y
��a�d����8pC��ž/��d��r��k|v�iXkK�=�o[84��/vx;�>�WDpڈU��̾��Tc:A��ȯl�8���:�ނo#yDb߯<z�إ�J�9>y�����m��DUp�RZ�*���	�+�V�e%O˭BF��b�}�C�#C��%�V�0k��%�_��k�79�%�*qo"�6�.�?c���D�����qN�Ƞ��HC.mf�pe��tDD�_Sb��솎�9e���?����J_;��]Ai��m��6�ŸI}Wi���a����g'g�K�8��q�(L;J�Ql�����C�[u�xx.�=�*ҧ�_��d�7��%��H�l/B�s���k��.mdi��l� �[9rv�k���o˜�A.Ǚ���K��'3�g�\�>�I+Yx6G��!�(�sr��_8F���{>�WG�A��
��ž��'w������v��BV+��ص4?<�z�����3��=�5�^܅�j��E�x���xɧ��	���M1����7֒�L���p�Ө"��3�{�j��:��..���<Y�	e���oWE��AX$��+��0�2�����A�؉�ib^�o�R�%�#`�n��U/���=�/�gZ
)(�L!���J�MH[����?�t�#�(�Z�;��.���Nc~ʠI�������|�bM
M1�HA�<��#��7;���FO��i>�.C�|E@�H�L����0��L>��f�|�L��o��ƹ]@��;0y��&v[U��qL���Z�,sQs�-�1��}D�>�H/���q�9y�K�:�3��?�����ۢ��#�'
�5�m@��D�؈0䜫#m�Bs�VL��� ՛#U�,�\	���q]˄=J�>ݚ��ԓ�#r݇�7���@f�ǂ����}����׻/��G��
����~T�ZxsWT�*u��}��o*)~C�?fK��O���3<g7t����Q��z�N�~.uo�-�����*}�8Tʽ�(tϕC6���B�������W�}�K�C�Ցj'��#tWD�w���E8��:��_�����U�b�����2F}F��"����9��N�C���]ů�W�Ӟdx
�4ˇX��P�pW=���؃����njs��f�r�?�jp��k�J��o��g	#k�uE��y,S�b�H1Ä��[t�TLm!���o�d�'˥�c�?*�ĿWEéOHGꩼ�<�0C5fs׷�����fiV[�;���5=�k�I)`��Z��8`�@��,6{{��;���>Ǵ
�,!��{p_��yu�qS�Z����!�;�\�YbcO�앴�|K���t�h�)��_q=��3K{���q�T�cb�k@�ٌB-#l���l~A����@�>�fV�m�h=�-�S��S��~�E��&��9��U?�7o	<���-ɏ���/șzK�� B����V0,é�F�ͫ3b�>���ӿKp���>��g���~�aG�Y�P��l�˨-)��)�7D��ி9)�T��-���{��Y��}��Qt�#�s?%�-��s���ի������j���^��w�U��
"�:=���c]���4�s�>��1�g��`o�D��6c�`�1Yp�V������/�CO�҄��g��U���ϲbj��7��ψ;���Q��7�Qbj�_i)��:��4�-h+\��T�4"�[1��ɟ]���1fM����o{<9����.�gn�f��.�y��,Ag��'���j,R��a�Ō5ׂ�� ;����ޫ���������?�s�����v�`��Q�iQ�Z�W������n`������{ҭ/�v�N��%n䧝�̴Ê��$��L�,}d��Y�!�3}^�%�@��(��n�eOƃ��39i�0	�$�h��(���u��v�~F��b]hQ�±�T�̫E���l�G�l��^o�9�W��!?���T{�__��AI�w�<a�7��Q��;� �(���Y�� ��\������샧y�CmuTV4��֞�Ĕh��)��,5��꒭�6�_HZa�����(��_��sMlQ�B}(�D~p'k �?��ƴ�%���D{��nf;�D���:���g���T���.��ĩ�����k�w-�^ș.���"kG}+i��:� R�����H�~0e�z|M���ϒM��Lj�.�3�9������YsJ��1��-叱��%���v�*��?���hY!���� �?�6����#h��ۓ]kJr��7	A���XZ"a�N&Z)��I���%��^d2�;fa��n�-�0kQq�ղ���ѵd�z�p�|R{WE�7����+��Ֆ�d	UC��[ʯ�����Ƶ���X.�x|XWab �G�ɔ���P�]~ݬ��C�b��O1V���F+<n_ܵFV���?�x�8
�c�-!��i�H�T��p)91K#4
]*�s-P��V��B
8\_7���NKmM����ܣ��̥�V���#�w��E��B�����$�+��za���y����:iUf�ꡫ����錻V1�cJ��TXO���ʖZ������#�#�ĿBd8y��~���zaw�w����J�wGr��E+E��*�^�_
Q����\ָٛ/
�V���3a�BG��A��$�Ej�^;�l���r1��6������'�E{@���X�0W��i9�{Nٳ�Y�?V�S�%^ΨiT�+�V��t���/,��Y��VUg�e��}�-�vM��u�I���A��.&x%��e��I�S��ӻ�_}|sc���PVݰ��5�dK
o�'����1E(օ�fW2���Q
-v�[�(��j-����Z�9�`����)�!҅�ik��HÂ�knQ<�p�Գ��B���Q���|���'��u��b"q��9bE4�UʱO�Z)�$c�.�"Җ�7�S�����}�ˬ8�	f��O��nu'-�.�To؄�l��]���>>�+�@��ZH]��p����<XY>>{ݧ,�̊48&f�"��q��F�b����O�����8���F�3�[V,�e�M��qv4ه�J��`�Q©YT _p�/��ԑ��z����fy�1`6�VW*^�S�>��p�D�A��.�����(п>?/���ػhG��L�yq��:�_w��:wj�p�<���d�*�D� �������Nɖn��{�3�bUGʼ>B¨a=�x�`R@.Fo;�z����un�Ci�!��zhǏP�'��,4�a":��s��m�2ق���� ���z��-2#s��|�"{��x�=�::c��b�@��Y��zB�0��UX�f0�n3 �\ۚc|��;)��H�%�N����S�H~H��*ԨtH����?���v��
4�q7�W3X8���E�܎���ٰ���˰�bh�ϖ;阻.�D��+$�ȡo�%�t���B~EZ���)F�3����W�����d�t=p��N큞.W�H�{
����z�\)T�ɤ�ڴ�Of���S�?%�Np�f����S��r0[����N�^蟹�Z�� �3�H�}�uD��ך|����ľ]UN����1bv`F�&?D.�V	�v�*�������f�R98y,�ȹC榼Z�(�v\�!C�\��d���"s+g���Q�
2)W�nf�+����QX먵����:i[١T���T>��B�|"yX�Ѭ)�<�6��&��'E��I{�E���W��g\)d��٧n$��k�#9^=]��r&�l��'-~rc���*�at��6���1C��U�a�D����:�m�DR�W"H
��L���-��Y�)#FP�Q�����"s7L[1���\{��xZ�m�َ�7� yP\��v�y��UM8���W4Һ���H�W�G��ʨ��[���MJSSQ�V/B��)�k}[n ���O�(�eX�u*ۥ����$'_�O��ܓBws����Ϸo��f����\q��R�����(*�+�j�#������h9MAWt�En����?��@���@!B���.�GK��z(0�`�NY�υ�46x��%�-��B[��Ԁ��襽�YEȟ��+�؀U!1�J���I�n9�y����<�?���%v6_���ݝ�u
̰�@X����|�)�)�K�;�?���
d��Em��#32�����
�t�5R����u�{����3���ZO@8[�gY^Nu�K���4��L�e�U�iZed:r��yf]q}6L,�c��x�T���YJU�@B�݄�J�ʹ'%5��}FKA����D���DL�Z ��2A|�o>�<���xO��6�D�Uֿ��+��ҷ��23�WC�$) ��x,H̕���%:��~���~�ZC��.Ac�0P�e�)c���_���3L�
�\zڙ�`��T�3�V��K��d~ݟ��xf
$����+�4Čr���=Yp⬣/�W+��Ǵd���!�^e5�Y_:RR(�JȇT�"��>�3O�O��9r�>ܬ�)%a��H�O�G3�2ЕRg.��1�׼�ޙ	$ �e�ڞI%��V��&�y�3���YvnD3��s�#��Tj���AKZ���R��֚�c2M�i����X�p$��M�ʁ4�qV���i;Җ`�9�ց�n�|�2��ka{ca<RNr�3�\׃RW~�a��K�Ւ���]�>'��
�Xy���!�G�'m����y�:ֶ�@��es�����)+L�?"���-�?��n�7�_N���8��*蘻�O<��U��DW~�N�ڂ��Ǜ�������	�`��79������lȕ��~�>�#�m��P�y��>�@�B��d/�+
���I�������H>�
uhW��­�i�W���8��"N�Z&^��oG���;x\�������3nyS=��
���B/�������䤘AJ���	�2���3��RFf2>cjp&�X���3x�lH���b�3���A-���o����+4�������x-�B&��}�/W�:�����Թ
�Ȁ*���7��3R'1̶���]9�d����b3�̮jj[�n��+v삭}��s��~�=�0������EJUI�����O�4�ƒ4�#��_d�ݸx ��&�2S�!������R��j�W�:�n�T���,�ӗ1
�v5b�!SwbD�|T�7��ۜ�«�ﺮP�\Ishn	��t�o
��{��w�~1�F��f��h�}~jȝv)�t�fI++-�^x�s����"�b\Fx5z�jbp$�����J�DO}Q N;�W+M�I7莑����@�b�����=�N4礿ġ���t��mV:q�q �l����it�m���������4*g�cVz��QPxAt�8�Tz%�������Y'c��%�CN.��@����e����,!囅���d�k��ͽY~d6[Į�y&�b'=,ҿ�Ǯo�Z�� ���7�x���q��k"H̟8��ԿxT�Z��tA�6�_�Y�b��n4A�f�Y^0jB�k���3r����=H�;v�(^�f7r��%'��M�*�-���k�T1�	⚑�Ov��q��
n�ME�Pnj��f�ؑ}UU�à��Zz��
P%�?r��n���%*�����>K����x�����-�����z��F5R��Ƀ�d�
f�_�/䒴��5�خ�\d:L,Y�喛��<��,���|`�п�1���q�?Y�O��ڲ2�5Uy�B�.�{��B��)ȋ�&�G_�-�M���l��tFs�S8"S[V�����|��QFu��6L�wX�L�ttT\��aOc����p�mO�7�m�Tmd"�!�&���u`Y �z�m��Qޥ�:�3H%-dm��0�f���D*UUƒ.����J��az��+���x�����W\���YR����<�y��8�͘���3��h�v�b�x�ŴH㙫 l�HygpRX+J��;��Xdz*�X.�ssc�\n��n;�ъ�G�Y9Ila�uJH���w���د�"��2��T��xd���v�']�E���+šǜq���>�Ё��Gp~�����~wc�&���]����C�D�ra�Ÿ�P�Af\��F�[��>x��dؕ�J�Sg�c��-���8BS\��0�*�����5��r��m4>�X���!����["?J)\�z3x�ѿ��&5r�lQ�i�]�\��%���Bm�P��+����W�\�=,�o��fg�	�)��d�px}}(ј�i������SFA���.�a�~��el�,��~gɅ�i��`��)��k��9����G4�?͍fH:�UDG�!z�����r+4z�:�`��K
m����o�`��wpM(ܜ��-��M@�r$�ڠ@�d3:.�4�y8�Q<����L"i�~�
Z���Ń]��$y�`�.��ȟ�#7����3��e��4n{����~��g�%���z���r��х<cU~-
�Z �pf�z+b�@�!���o��꫶d�7chI�9
�OHGy����<E�	�4!Q]��nx3/~�4�W��QQ�uEn�W��a�4�
W(�j�Ќ�ƾ����8��x�"}#�C6��魁�.�q	S�dhUI����J��[}�F��9&إv`��X�g��6(|�Ґ�?:.���Q؃BXC#y#�̀����:�VE���
A�@�,��7��_�����_@����
,?{��L�t>�?q�͕E�� &���L�_��Jl��z���TFFM
�_�f��Ĥc؎k�U.[��E��AQ����/�
�f�W.�v��!��D�@u!ctDE+�C�#
��a"L���q�g���f��B�y�y���o���UaBv�8C"Ԇ���ʄ )ie��3���H
�.�P6p��U�+_HR�m��f�q��=�ۤ����]�	�j���p"�Ъ����vN�k���.�0���5����}�S�������"�.V|wI���y��7y\&����"�l��2gn�~0�y[%�r��
7,�E_�R��t�|(F�Bg	[^6���$,�:��Y��K�9�+�C��l�N��o�.*H%ͽ���S�xs�&�����ĖP'P�'�}M��
�]�#n���p�}��N�y��8�pXza�[�'1�#�N~3*��)���M��?�k�Z��Cg\�T����)���	���<H��=�O�!Z����Z>�`�K�����BO�c���?��sb�!��v<�#>��zZPtv~z0%W�}뚕���-���U�h���iJ1f���?�hoUMssbh�\��3o5c�Z�L̓�<�U�/L�^i6yD쟆�����d���uUֶ�^JQ���F�`��n�:s2g�%�m�y��]G��o���7H�Ջ��B����pf~Vb�|��a�s�T�O��me���!ֿV�!S.�����U�j^�)�e��j�!�d����LShj�6�������EaŻ!*�Zk�|�Rѵ#D'+����ME��H�t�h�fY]���m�����kY�Ř�>f�Z`	����ā�5^}�(�D�.���i��Ʒl��dz�������_����UA�w��I]�2'�/'Ѽ�竩^O��`��g��|Y)�͝����L�[�Mz�D�a��?�8T��W�T��U�E���fL��}Ml0?���В-�V�/4ר`��̚��K镡�^\&�����DVO)���3�&ᔌ��S��6���I2�,���:K�KQ���+08{�"Qt�	�GO�ۛ�ބ�G�s����݈w{��w����0)���v*3�4]\�;�������u;�CҪ�c�v��Farϟ�~`��I=�H+B؈�Ll����y��2K�pr�Vb�DGօ{'g��*K�-ݏ��Ȣ����h��+e��)סN4#�~�Q'�B�;���`�c�$�L#��+3���R��OڛL����czL��,
i����(l�l�z><+���DI@_G�%�!N�d-�H�aC:I�?���9���Gİ~+ȉ��S�f,����'4V�3�Ū���(���?ʎ�*�U�
s�6���m���;d���bL�ϱ
�y��j����6s#iR[�vXs�f�o� ���?�B��p_�p�߃0�G���D�U�MٝR/IdR�Nfw��0#���0�l5Ek���v"��HX�	����|��<��xW�!��1v�ĽR9}.͘J���A-0o�ۋ�ϔbR�L�UI}_��/�>Wωx2���Co�����:��7�{
\z�?X�׎~` �R��Ph�p�`��L������j�ĬL �mwl����k����qd?㢁�q��L�%���5!N_)�L
Ra�a�d�B�����X���'Rr�$�sҿ�e';uf�z�E�k�#�c��@N��@Y��SU�i�ww
��{%���G�����^� #��&�IG���"
��n��"_J�9y�Iղ���6�h{��3��������k�P�ލ�
ލ;�U��|(v�do�|�ÞH
k�g�0�h$W����{�O�˕��:	���R�F���հ�1Z�ĥ���?��'D�����W�#�|Y�l���i�n�F����@Hn4߹$���f�<�̕�V։��=:����O���yc�5�t�I;�F�5����x��������m<w�g!G#��-�:�����fUr��"��o�x
}�K
ϘR��DU!��g5+"�CJu�6���Ar�t�|��s�  �F���b:���:��u���2��vxK�����W�/�
yG����5G��A�T�I���@6�Ͷmoǟ��q��3#h�O_M�_�A��fL��s���)X�x��u�%_M�f��k�Í���]�;�=�KY�$�����T��O���%�O��yU�!��G�Jҿ?���C�]��M1%������+1r���D�$��2�VQ*^:"�α�t˙+����
�5NWoU"�m�w�+�:�>�
&�݉�ZֲI�$Z[�}qIj{�'�D�~��̜� �kѹ$�O��f�ivt�H�<oۏ��H����f6���0M���"zuC�T�� ޞ�2wۉ��D��N1�!'�!�:#��ԍ`���z����ZU�j�&lxJX'��@u�;�w��2�s!�E��� �`j8��{E�e#��ıwL<�Ǽ��$�I��u��Q�$?7.���O����)�_7�0P�Χ�h�چ������,M�R�K��^�5��4K{�<ЕG>��s��wGK�Z�;�w�U�m�:I�z�7۟.O��~D9�N)��<�zLJp������;k�	����e+�{9�5=�K�
M%��r�O]N�������@���SQX�?7�]��ȼ��2��s��O[�j�Ю�(�����-k
�Mp��0�;��se��$�xߟ�nH�)3��ޔݜ�U���+�6�01s��2�u~���2�Ԏ�}ȃ�k��3P�u3G$�@�� ��=��w���+3.U:9<.`9Ƭ�����/�X�����l@����le��G�g���h�W���F��a
b�K<�i���m���K�8ӑ�O�Y�ܱ!=?x�K�m����DDRrF`5��w����P�eB�7�,����l��	h����05���*��_2L�E<�)��R	n؇��O�ux�����g���Tb�¤��@��A�{��ɨ��3"��_پٮh�O�����D3[������\��%yn��e�D
�L��йxw���2���y��w,�?B}C�KeI���l��S�G���p�5�Msw� �'E��`lZ��뾫l��S��!����GK
� �f�|-��fiB�X��[���ϰ���>������?{�!��X�yrw�:oOX&�_`A�3Ʊ/ܞ�$nO�v�cu�P���(s-��JHj����v�A��8L�X���^�S��})d�S�Gx��c�
)��(��L��YH�����:mY`�sf:ܣ(H��/�H�!�P�㡪�J+_��=R�#�`iV�	���ļ�I�����2�м�nʍ��+8��KV>��K�5+�3����+�i�V�H7�@����d��vF��N:��1�b���E�/�!(��xy���3w�ҷ��@�>|"�/�]���y
���Do�5�:�Y���=)
pB�z����L@c����LJ����^�yH���CR!F��Gk s��K�gy����}&aJ";�(�!�N���g�((W|]�^9뾪vG5�_�������ˍ��d-4�LU������)��{)9��o��;���W(B�{�Q��֑�jQ�MF�
S������t���m�]�á Фֹ��(���zc$�����?V˝!��g�b�p�h����p�[I7��A_�L{������گ�Ic-�����P$v��t@�Ia����$p��J��wj9��jp��RU��˙�R���_��`�O>��s�<�y���\��&�ҷ:qj�D��{$�l�D���|��Lֵ�<bӓ7��˭�U�'ՑJ��+�U^�J�}E�@��yἳ���ê� uah�ۄ��	�kX��C:cqњ���=!�����-�����u`fS�R��+J�	���Cm���΂�
%�I��3j�'�Z��?�t�,�-!-F�A��<�i��>m�7ǢKc��[g�
���6����(����h�f�r��7�UF�>���t3H���*�`�h��ZO���Or$�L.�s�_���S�V��kS����0���2�F����Aχ&b1�1p�4�9.I�d�҇�)��E��1��+�U:Æ�s��P��Tw�����;�Y�@ms��x��~'^o��B�
�ϟ-��� C�%{���o��7���P<i�D0p�aˌ&7[f�3k	i�d�E���vk���K5*F���5i��"��d�Mp�e(k"���4�|��1GNJyѐb�N������
�2��A��S�HX��ʫf�%%��s���]��tN�9����F���-��O���jO�vƦ�k�ډd:'�\�f��ۮ�&:�'_'W�Unf[1�lS��5�
2�X}�Փ����y��w.�h��������ՙ�)z��n0�c��O�
k�}b�d�
�N�����]���� #���{�_Dg�פx�?5�9�IIm��P8"TM1��MH��D3�������քRb�s$�qgn����ic�:��-'�<S��g%��=|���cVG։�}������qN~����F������(�6�o�t��>�Vw��Є��Nt���o�����?�?������a�k�,��?������O�4@\E���b�M��ҕp2�3B�)K�ܴ>ؙ��"CA�?�flAN��P:y�s3�.���gK���E)��v����WS��w��o��Q�w.l(�D�#.���힦M��׌�6����=��c�\�t�OD��+��wqKm[)s�#�j\{0|wR8���96'��V�JV;�W�����s���8�-��s�N����ѥ�k�74��WU������\�dB��1�F0_��z����S��P�w��`W�n��0��ӑ���*��9�	��P��=k��{F\�Ep��z1(
Q]�S�&Ǔ��> ����ԸcW��#n�9Vv����W����E�!���Tznf*sj��)8�$l��FB6�M�c��a�֡kYJ<�ٺ��aQނ�'�둡x�x���^V9��Cg��qIt_F�C+X
,ɏA"d�wƫkkё�;�����= �j�û�n���[�}ӱ!���y�~���ID�e�R"�$6�>�ϡ���w��zs*�-�o9io�?i3b�B�?���/�S�=�v,V�f��2O~>�~{�ї�Ap��#��`�hEq�M�_i啒ά�m��tp��&@��ϧ�4',QF�h���r�4�I�N�J�O��GXc�z\S���]���t/���'�l �r��F�c�\��T�
eN����K��L�̧�I��FHiYe���U�A��_��HChj�	�m}�ר�
EY�E�V�tE!�fd\b���(q�A�J"_\:|�0�����Dbʍ�p6��Z��(�`M�@"V��^_�,�f��N'b�J�D��c�Ӧ#/�	�ft�V�O�2BE
Ф�
t�4�纀�X���7�HANRW�K�yhX��`-�YrYr�JQ��%"�]�/��`0$�vr{g�D]d}l�ȵ�7�^��(�,ȶ��o�I���:�@I_J�;�+��`f|{�yI�S�qƥ����y�����]���m�L�RH��\�? ��gW`���%&���r�3N�c���G��e%�K3�z}sK�n�-���W8��/~����P(��7�?GHBO3�۾l3��ZE{��p'-|��%{ǝL�.%Ў�b!����������iJs쇓ՠ�_yCu49��ܵJ=���K�R��y ��u]Ys��K���L
gE/(	,��V�:oƺ��#���ܣ����t��:U�ȓϟ�g]�X��۲�����⫳���j�{Г�������&T�^q�~�W�: GQ��xq�x$`.�q&�ȴ��;k��ש P��P��p*�f�JѲ�
L���^.&>t޲�@б��w��A� Ƨ�ύm%�4,$�l�$"�3�F����f�]��U?�M��nx�WE�6N�)�
O\�ψ(yjC﷛
���h=��h�4��Cیꨢ�|K7{����?�H݊���z�[Ղ�F��g厐r*]���|/���6��t���,�0<��6I�;���t���c���x����D
� ��Zؐ�ry��BR)�ۇ��?8HU[�ҹ�ǵן�d)�YWj�ah����c�D|P��C`N��T�ͣ�}Fu����
:Wť�����n+�V���oHGz�[U�}L��?%#B������a�%WLLF���a��5�O�ҽ���`���C}oVXQ���t�׹���۴
�r�HM��<&��`I[����%�>;���U�w�mؾ�Sπ�TĨBN���
��Ox$�?�q%7�8�Ԫi�5Q�"���h7�1*���T{�aX��-F�����2��������J�ԢU�w�D)7�y&��G$�g	�B\���3����F�|y7��.�$���7����"%���o���d{�����jYT�0�+�\��'3�y��:�]/3��;۴]�x���f)c���U/�K��~��g�:,�5O��0�����N;�܉�V(�y�f�p.BeE��4�궽�)ڪ�f�_*��r�X �8�VL�X>���v�a�'��e�9Ɏn}�u������
�8v-��<%\l-䡓zo(�KA��p0�\��Wc����8n~�'��s�Ŋ����r�-���G����,<�.��(�Y�>�n��?E��'6�y��-1���M��6����Q���ɭfͭ�L�'7��a�t޿��������:��\At�%pf�!d���!+�f��$Ab/P>η�~n��mMIK����f������(?��>�t��lψ����)�	��+q���
1�.��}��?C(���+*�n��>7ŝ�)�5�>��!�5�ͬaRT?,�.���o�O���@�`�@��>���
���O�SП6S7����ri�袊i5PU1�_	<�:ht�BɨyPYo�Y�_�Rn�a �����Tr��l?��ϔ��;6V��5~��Y�l��%����+tԆq�3M�&�3�&g���fn��gK��@�m�$h�\g1��$"ݜ4�W��e3�����5ߤ��n�{�s�G�8.�{H����u.KX��lt��I/�4��6����&\���]�_�N�
26�l���1ӝ�W�v5n����XAI�Yx�A�P)g�A�#ֽ�d!dm��V�E߰��ڕ��S��K2�@]s_�a����y�?���Y3w�v�œ�33mW"x��NC�$5ƆC�;S8lE� �1\]��rf�?�����CW&�&b��"��?)�e��]�@��;�8�9.����#	���Z\�O:Գ���-����p���R`�K�Ӥ�,���O�:�gVyݭN���;7�O��m�x�|�ik�;'z贇�2w�`�~�MbC���ats���J?�.<�ʇ�n�u���w����>�Ύ��M^+k��pYi��TQI���e�	�-�ϬwIjY-px0���Y�Ծ�h̢ΔE6�Q=&
�^ |;lwnGnt e4m��O&�d-{,]�g��J���Z����GT,�u#�ke��Ŀ���;0�7�^vE�A��y���xP�w]/���~��M
�	ʳްߺF��v���s�� (o{�	�@
s$�a�_6�Ҥ�?pQ{=~��D6�����Y�	�]�_�Xj)u�?_y�g���<�f���+Md�<�G���y2[	����X����M[U�85J�p��
Nf�J���Hu+��v�@�)Q���Q���V�2�ٙI�9�RRt���n�f�c������*���0��B���Y0�I�԰l�G��V{�^��0��7S\�{�h;yHm"s�*�R���ځ$�ט��Ћ��d57�6ö�NW�9��e�.!��"��\	�"�M���,�		�����\���Ԃc9�D��Zd��}��I2v��ԓ�&C�oa�$Nn�u'���iLcO����TiĖ#X�J�����)�3�y'̆�4�:'���2�:Y�NĜ
d��vH"^s����������3
eTiř�V�;I��N�'�:�Rf���W��'Nd�ټv��+��E��C��D�0I�Z���\|��
��OU�.��Z�zₚ�'ʟ��P�O1�I�',!�D�(*i�lr٬�/8��<t��&����y1,V~Á��7��F�W�Z�%�`sG���O�W�>��qݜ	�w��_�cf�M����3����V�%�`��N�39��|�B/��H��m�K�H_��,��;چ�i
�<{C�:��wP��e~f��|ť�"�]fk�#URY� �!�eF��SŕŚ��E�uL�x�7\T��y�>u�Vx�o�[,*N*=�Ԍ�4!���[��W�d9�7n�A��%�
Z�gC"�StoZ�4��8}�d�섺O��ȫf�#��1��?K(~��"����&�q\�ܟ��OQfo'Q��4��o� �5��憬����DH�������Ɲ�o:8���ބm�2��>����=�p������&7�/zZ]�z�h	,������k-��}���R@���`�:����a����=�s����M*�ql�ͺ������u�2!�Hb�g��E0�J�/K�q��_`�����-�{��=�X����#�֨�	���~2	&(8����!F��\��q����2�x��X�����;}`-�AQe,�a�CɟK�;��
ضl>�gŞ��2�]_['ye���О;���NƁ]P�ul��i#-$���7S)���v�E?��6e�Q�װ��<��$�+���T�J�xG��$��I��#�4w�H�-��Usr���5�?�vݯb$O�L3�*%��aҼ��{l�٢�hh{�����G�����W���J2�~Y�%P��k3�>,����ٴ1
��Of�R3�:g�@Hr��	
/!Un�����$\��B)�;*Y8�n��E�P�0Q��C�ܤ?䪨f�W��³�I&�b3ӎeh^f�Ij�H�¼�i��94���3�>O�
)&*�2A�(����c���0L�(�*蝶M4����R䘵aʈ�wO)�2a��2V��v���D����^��[��
���#H�Y�.�y�����#%r_�v*:,�{��9�=qr�ǯ�i�Q��2?h+Q��ӗJ�z�_��O�� �~r�D�Il}G�q���p��Ϫ>E/�j�����/6��֓��C�^u�s":u�"���S���eJ�O�w����PJ,��a�h����uՔ�Fmt<
�q&߅��N ɩH��2lD���)�˚*+٠Z�a,��c!�>��_�&�K}(�E�[%�h338'U����=�̥�"^|$����^�}�n�0o<<�Jj��EW�b�XBs�ޘ��q��,ɏ��H����e>����(�x��v��Dw��ҿ����5s��jꆵr��]M)��
����-I�(��ĥylj���E����¶��b�L[yU|ke�$���%��Z�sR��K�O���Z�4,�7���H.���7+��t$R��O�*dg��Av�_Ad�Ju��\�^���X&*�%���}[�����6}��6���c�Nr�'%lV޳??!��?S���(P���5�͞�L�\SX|-��|���:�kmk%����g��:���{�^�/�P5�g�k��W��iۯ��$i#c�va�+�Ġ�ٸn�g��La�4W֠r���j���>��q=�R�f���s$zIz.��a���qϤD^]/�ﻞO���+���V����ϙxpd=UA�
E�O,@
֙�W���8v��~o��"�yn�����v�I�̲)�� ����5;�n��8�v�\�ۢ��@�#S�\�H�[T���T%�>Vd�,���C�TxŽ�Ȋ-w�)�#%���\��,�;���ڏ
"yZ����+{sA�o�_5�]���q�œG��}Al��
ϗ���j�_[�6�L	�2�5�A�S�L2�]=�Cr~�v���]��e3��y���y���nʳ:NVM�_$aN��A�g���s�<:'<�N&�x�V6�e�
�t��_͸��G�A-a����TY�p�{Cv� �e��Z@�F��i��‡'^�ɑ!���im�&�RďmY?��f���c��l-������v�Â��;�=���e<��|3=Z�6aT��B2L��i9�rM��%�/�B���1�μ���</�&K���=rt���!��$�č��7�iw��ºA�(׶��F��6��J
�z�'���A�)�;&�Z�w
Q�F��o>0?�:h�֠�^�:~��)X5ޞ��%N�[_���_P�h��s�+o;�A$&<C�Z�*�q!/#-g\���S!�d>7��`�ﺲ��`f����J0	k�E���{�4��MD������]�b_�
��M3��,�MWh$��x?��P�Uu�M|6� a 
)a��p�/�"�QD$8�7Jѵ��%�WO��PZ.���V��4<��>d�)ۍ_��9��q���|�#l�ML*��#U�5L�KϮ"�R�s�-��4�C�4��+���ͥ$�Pj]��X����aB�Jd�ַ�J�k�y[�d�'��U�u��Wn-��������@�:E�9	�BF0t��*i�g�k;�T��?��lY¸AY��D�#ֵ1h�ΰ���*RD�"����9���U�Y\YE�b���ָ��L�P�BXFӓ)%y�U�m+�,(F�/0ԧx��(
�j඼�y'h�/���l퍧�
n�7�כ�NV�M0m=�֘���t�O�;�W��LN<���NHYZ�N20��\c���i$����l]N�LY$|�[*��uEP��~�w�Ӿ��/YEW��ĊV6'�)%��+7&���k\]
��������Y�^WE�+�d�4�?���f�0	�{R
��Ai��,M�5ҟ�ki��ԉ��	�K
Ȇ�?����/�j�_nԐ)�ks�O�lI%�%s~�����=�H�!�Z1ȴ����%�N�'�~O�:^
s�O�7����JVG=�i�g�
L����/ϨMf�2*0bR�D;�wȬѦU�K��?V)��Z,�R[X������9� B���)q�l�Q�^p�^|wp_�xV����x/>�^�W��Y5��0„��bXZ��Lp_�����A>������z����B,`u��(Hf�^u��fh@����5��-l�v���^��f2X& 
�4�چ�l�
�B�%W�
u.�Y��B���R[�OwoQm�9��ߌ`��,d�4ޤ�x��6�����H7x���@Tl=�|�%�R����d�+��y��&���N��@=Z�UP�疾�p$�+D�W)yo��; *��2x�d��v�O��A�����1����Q<�]���҆��<'��g���&
peџU��S-M��]�o,��>ҿ���`M��^-��EX������4�������_�2d�5l�T��D���G�/�}i�@J��?Q�G�BJC.�^P�hGR�MQY��	mbWܭ�C�9�\��Ն<��t[c����j�Kt0�Dd�PYZ���[;_�f�0k@)3S����$�E��)c�-իEx�v�]A�v����D簢�Op�,��JׇNe��F^�����s�����U�Y�U�
�)"��ח>W�[��W3�y�P�0"��E@�T�jFAD������T�ظ�6eY��1�7m��%��V�sff���	!�m,p�Q�.
i�1~��vg˼d�C
`R[@:�$����8�0,.���_IZU^�������"Ԧ��:&�1
Ԗ8�T�>R/W��u�'�V$O8VY�la�0j����ZS�����	�ye�
��c�ט��j �����ٸl?#U%���k"Oe�D�,��N�	��d��D����P�c/N  ������Ps"?�G��H��Ϯ�I��}��H��v`v���(+�(��|���{����U�ZZ�k*i����<�����4�a��O��������ґ6���s��E�Zm�������ݖ�\}��n��ʌ��?�UrŮ"�r&�e���{cz�|�����M�VT0�/��˾�;Gﭦ5�,]c����r���Ax��S"#��f��FD1�
'����&w�!��S�3��z�k'�f��~�u#"�����~��$�\T�����)����&ƕ7�w����i	K�C��r�.�_� YKW�\���-p%�3O."I��%��tfCx�9��ūI�@-�6:?z7�$��_��7�;;���eƍ�L��>�E�b(���agd������X/dH�^*j�*i�/�ƈ�<�Rjx�Ҵ��������/k�3<����; W�7�P`��4��g5�)2�aw��ݼ���8瀕ɍ8�8�1��{����֭7�+�
�v��{AJ�J���be�^�7W;n�K�=vp�L��`���~��HRɫ�P�xV� C��@Xm�M+�R��`��s)d��W����tw�x�ֽ��lj�o�3���iCLDo8B!���,+Y+����\��)ӻcs�H�U�蚑��‘/Q	��0m��(������r.��Q�UK4��?�պP�5bE�CLb֐&�K/��/8QUR�<�h�ˉd������<�Wމ�xX�9�D=�^�� �>�b�'�����%6KY;�
��*z��ο7��,0��ɷB���`Y��I�m5Gaū_����i��S�!���#�_K�s����̉E�G���W���T��9c���"hN�s�)���u�!���/"Bܢ1.��:��������L�+���U�n�1߅�)�3e�����NF�L����4��!٠m���,��ᳰ�m�a��?$�������_���O��K���<����W�|R��'�R/�z�Nx��P�u!u�!K/R>(���(��imqö�u^₡�j	"�;ݽn���v�e���V|��=��}��h�[�s¬{�v<[�n�k5_Ch���g�Q:N#�����q��z�ܯ3�R�R�e��dQG�@��1�ͪ9�qy,��į���K(���$����a�)�B����PU��(>XP!EQ@@F�O��iڕ���ҽ����i�I�֪t����K�N�ʢ�9��$/)�
k��á�ڬ�k?燧��u��Ac"�m	�Q�"1p�
�{�	��~�D3���n�D)f?͇��T����l�&�4�S�S�A>3�{5���5Ub{�|���/�l0��t����M?��Q�+����rW�3)�^�Ц)fqK�Ec�{M�+��!�o���A�1��7q=���P�Bs(Z�d����U�W����眱PF��3���\z���x",� ;���_d!NB�n��RT�K�I�@�����8�#��ۑ�(Uf����?m	Ez�f����N���.�R���\��iu��*�nE��6����
C���{�g`
�R�ʚ���p���Y3�j��Ȧ�����3�6�'3�_�s{����@O����ARפ�4��&�G���LT
�D���1��w:ߺ��F��UM�iA��`1iXh���x}=.#�yq��g��V���tx�%puA����,[W�X�r��fm��)Wz�cn��UI�Aaڱ��*�z$r�R4
;��O��aJ	�������*��u
kc���v}�B���K����5*��É�D"/e<eEJ^�Qz��#�ib��)��A�^{�4��>�/��w����"��0�3���7�����x�Ё.�K�'V.�bл4�+�N�?n�Z�,�SZ;y�����%�N��\�ɻw;E�1
4���o�⩊'��&��|���ـ4?��Ʉ��@���@�������8�a͝�W8�b�F�k��9��_/8������邩��4�y��cL��j%��C��,�$��OĐY*��7u�V�Y��\!Q֏�${G%/K|����αOLs�?��g����$�!�	����;�VQ�r�B��4>x�z'q҆_�N+SFM��ȏikC��ǁ�yY8�����{��2'�}��~�{4C���7�_�w~<U��e���'���'T�M8�����gw}sAdgNpp3̛�����Y�i�{/e( ua�ѕn�O���.��L�2�1	����T����e*7f�>�}�������訒�q��x�wl�S�%�^�I�ϝ�(&?�[�ڽ2����
����7�Qn.N�����hV���
$�W)�𾔰�,��b* &\��߆�]'Q�Α�i�s]�/BA|�YDg���
���T����3/gg�1�o*|��oʹB�2�߀|@����
�5����d3���q��u�;��(��d�M��u�^�w�����=���z�PM J"�僉p�A���G�ܗ/�yZ�M�c˳Q;K�
!*J�4$g�.&E�<�B;Q�����>�铮��z�̽�W�I	�Eyy�,��e9���0�d��;u��#��_d-��%�I�����k"��u�59�Q���9��TY3)ñ�=��#�W���z�N'�5��M��؝��< u�KU�����u��BQ�ȊP��*�3=v����h����kH�\*��s�\��p�;Hs�~N-����p�A�
N��Մ�Yg���B��m�Kj�/�c�?��	Z���{�B��F̚W���Nd��������Yݚ2AF��4�M�VFv��x�kѴ�!3|aS�B�R�>Ş���+�Β-I�;�t�`m�K8�!�����+a�/��:~�?��ce�]���<S�^^��ٚ�ҸhlV����f��tw��F�����8bP�DzmGy�M�1^F��0���L8�*�
VkNo^ ���n�+�1)�����(�tG��c.�f�NgB��0H�u��6�@�t0{i�ؗ��Lz�����`���G��	Q$�&Ɍ?�uW��4	���.�@�4�M��p�1�\-ķ&-�c�|��AR:���9\S�MR�u�+��C�&
��RsL\0��O�,�j
��x-3��� >�8��1U�Q6@�_�YR�~��e'�Iʟ����*�����M��m뤡ț+�Q��$�N##�g�DK�LnԞ���aM�Q���.bZߜ�4�����9�E!IѮ��eH���T�����wҟ��exoӭ�v#'�K�=[�[��XQQ�F�ud쟿D��'��LQ��L�[����WB;F�(4����d��=�c1V��	h	00�V��Qs���7	YZgD�Ri��Ÿ{�
41LU�g�y�rt����S��}BIʟ��(y�v�˘�J_h����}�+`�$E!�O��@��B�0���x���ݍ��D��i,lk35�1c��v�꓆S�j�+��RlƝ� e;�*�a]��1C�X����f�1����	d��L���a�)�3O���Ft{���-b�
��fՙ�p�絴�k���7w���eK�*�T%���H���F�gD_�:H
�oh&8vNl�T�S{�}����ڙ�h��?���Y?3S�5J:=�U�v�i��͚��E�=���k�+>���Q0���q"�F�0ny>3��'t#aYHCE���D.Bp!��\�|I��/���_�����/��"���)�ij%A�w"��˔�,�o��0̞[X�&A��nྂ�+�;{��M��D�o��5����k?<[O����$�LI�F��T�M(ٽ�_x��@z';� !�"n�p�J
��U^A!���N���0"�m%��	�N&��ʖ���������u��r�{����2ߟ��AaS���v�r�S���Mk(���e�O�ش����,qm��n1bs��:@�h��-x��K$�[X��ć:�y�3�srj�v����%F�)���!�4_�:V_?�[�ug�:�Οעu0��F=��Ç/���49<�f��t'Q�Fw5��<g�޷�\�����zq�"�wǒ�{�������=�T(��m1��0f�)ݳ	�q_�]��Ǣ?r�/Y�nņ��-@}�PQ$���H"�
���߭�:�Z_'�/q�X��~��4D�<Ҟ�rs5,
�F���u�����MlxpB�hٜy��+�"�O|CR�\lt�N�f])o��E�ϯM����'�Ϣ�q�{�\D�0�r[U�/�F�@�=�x�{���Ϗ�CP#Ѻ�?~zF��Sq������ZS:L�,�c��'* ��N�X�Z�`�@�4&����
�19��.>k�EO��#�wߔr�.K5)������� �ݙ�Mg�JZ��!���T�`K,WV���<���3���D�@Щ�:�g8g26nh����;7�� 	pm".��"�N�ѕ�2�0��a3��P610���sh���31��g�ռ�kL�h��[yDH�i��԰ʒ�ip����
�Ux�O���6�SiP��3�R�5�LJ�-ް��S�y��@.�L5�ښ{WP9{�'�.����ZA8�(��&��\S3/���$�+P����Kzɷ?U"9��6�)v1�OMeWj����]���(����"�@D��<O\���'���>��K�{��ܩ�GZ��Gc������wHI�0T���k-<(TsEG�+�Qmodx�V;��O\ gbg�gB�%�Θ>����籈�g�<����H�M|=���߸.�C��s���ՠ�7�z�^#�^���/�]�O /^Щ��Xԟ��C�fصw��d����:�&�n�}�l�Ѹ7����߬�����iY�qw;�,(��gߙ���Cm�.<�ncAfFw�@G��/�ҧ����nv����L`���i%��U-��I	�©y��ߋ'���{�x�D3����OZ��,����ͫ-���T@Y�eM��f��[J�X�/�c��1�O<�D���mR{p?mIE�W˂���	��0�;7+�בօ�0#�
�Z%�s5�����W��\ҍ�	X�
� �5�8�88k-��ʨ��P��3�!��'r؎���H=@~��.�$\ �Gd˕��w����t1�	�)#���sM�ʪ����̼���~�\���A�n��������`m�`$�����/4+[g��
��3�*���&g�#Ng7��`����į��ʫ;���-����F"���HM<_?m0П�&�`4|,3f��2�N�q��o��D��TR��]�W�PF?���q����/p��?�@*��o�{�+���8�^�X`��JA�t�!����\�v��DS����`�ݣ����u
x�����v:�յq�!�k��h$��0���kc��b��>C�*����LN���K��_Y;��?�a{X�ƆN�g���'���N��S
���|;������� te6��'��x3�|��0Ҵ���}��Ib��k�Ѿ�?��Lw�0�M��2p��^깜�!Q�[5k}c�{�j
F��♟;��~�Fa�|�����_��.��>{4�{2?�-w��>?>wm��)/��9�/�Z��%ƢUg�zye�����Jl���^�D�(��� /�n�����ehG.�Q{A�֭��|�{[f�52����y��o/����2��A���C�OKķC�p
=�+)������P��Ւy&VAgR��"H�\ ��EG)��}��_\�e]��}Z��C��&��B�x�?���*6�mc��͋k-8�������ɼ�"���O�:R��E
�5��FZւ�͘@��-��
��b�)��I��7�k�R�&ZeO𭔰z�����F��e	�	��x�đЊ�Morl .�y}��SB�j�������y��p݉���.����I�A"�[*���3`�7F,�v&JX,+�N7�'�u��C5�ٌY���X}�=�~hJ@v,��8����p{�cJ��_�k+��%_Z������5�� �"�/-d�����B���w�;�!�R�W;�:Z�=����Z��ۂ�X�%�8�k玙��^��ˌ,#�OZ�?_�kK��ƹ迷��m�����V�4��
���^�Z��?��T��Y���Q2�]NM�;C�Cܙ�4Qw9�71I&<�
�W�j�[>)����/X���i�K�I��Q�a�g�,a���Y�=9	�n�;���=�_�����:(G��Ib�Rv��u,���6�W�'����w�t�V�X��.���=���>��(��^
�Fx�������k�Z$�=��!#�����<	���,�\ u�B�A�?�?��
9���w�id^D�u3�!&\�&R0?R;7��!�PR����|����W�٪�S=ݿe\o��G^���(�.�SWE���B�%PC��e�3����њ��%�*���J��$�'��[�T!gT��q�w�f5���9�f��j�qj�Y���H�]����[�A�%�
���6sX}�X�^���ڱ�R�ǵ
�Kpw��<��N�E��_�2���%��9mL��|X��Y�)1dg�T�����������!j˸�Y"L��[R'��X�(�:�$���D�kڅ_���N窐F��0s�uv����I���9�1'��]��؄#��d��_`���Q��`h�YA4�:�I��4$D�C��yY2�F�
��9i�^�;E��'X6�c�%�j�e͔Q�쑛��y�9 *�Y	���Zн7��!|�_"����>w�H����l��D�Xה0ir�mw��̭��j�F���Qx���A��W�8�����@w�
g��Q)4������~n&L*���������s��<��Ӷ_��RЖe�8m�����[C�3� "����-t��z�g��(��]�Bݓ��rpThy-P�a�1wi�QTmF��	�#\1��Wg˛�0:u�M�2�CŜ�tmz���BC�3�T~�t5{@�idg�$2��g4��v5�+
����Z��L%;yҙ�*�/�/ވ�K��r�m�\y�kL�j�L�������[C�l&
2�aQI�	$��R��I����y�5�]ҟxo����QZk�[�JI�)2��� S4iST-�{*��r�5���cKD��@�"�mzK��D��T����K�+�\�*
�́����W��4�.���C�"�)Iޔ�.jE�V)��Uj�#�*��u�d ��v'+�z�N�dx�O�}2�r
)�&L�NJ ����U�$W�!�Ȏ�k�o��]/��O5D��^|s��9�<H4c�fG�ՌkH�$�E!7�	CC�T�s#a5���9�SQ��Ӊ�O�#k
���_M�mV�/��`_��u��+�(��e5j�ȝ�����R>�t:�wC��r���8��%ٜf�f����Pa�$@W�{�)tN;7&�z!�k��.u8�A<6m�):�Z>L&V�w��q�b�t_�j[#,�a �@�ؐE��S����3���1Wޡ�%�����z`]Q�TV�F����6�qZ�c�*� �?��du��y��Z'K������f�_�9}�J�������D�i�!U�P�PJe��D�0�̨)ҝ�ķ���[Ľ�H���F,�z��-�_��^��4��2�Q����tg���.�ѵ�0�<�����s�3�_a^)<��.���;윽�D�b�\JS�"Ӛe���)����T,�W-�L���g[��E[)�МȦ�4��/,Ho��
L:�}\��-��ᄚ��>�>���0�3ĩ�>:��$r��_}���ڦ�*���k��k��?g��>���Bh��ۅ�.ho���@c�������G[֖$W�E�LV᯴�s�6��D����:Ib�8��>%��=DR`^���o�n\}�C�w|�˥K�M�#j���m1�V[���������jtc�6xj㋀b����BOR'x2z���y۔]�e&hύ��|1�:�������D�mXH���6bB�.�;z�E�dW���X�3"��������d��T'��k���"v���f�E�k$]���^֙<{��5KQ&�KT�cAi�zn�)|��+��u�t�z�:�D�]�77���إ�|��G�[�e>�	�h��_���>�[�� ZG�~�|�z!XT��a��k�H���ZE�q���80�#��M�vM�Z�dY�h�k�J�
O����Mwۓ��-S���(ą��eSt����6�q�r,6b�%LĒ��v��E���-�� ��?�9���ub�Q�M�|�jI���O^�F<I�-͞�1k��|��2�Yb?�=?ÞK��LR�T��ǫ��$Z����Ql��B���f���'L��('�e�����R�F��;]F��k�JECZYdA0�7�@.�uG�iu(u�Ӵ�Y�!W�pI����`�D����,�p�+:�e1&�<ʸn6^�h�q�.!�@��k�X�|\�BZ̭^��udZ,�9��������5�!�y���s��.��>5u!K�=	g��Z�0N�vܠ����0���3�H��ҼT}������^"p��Ks�0g�J�/3�hޢ?Mc8/��0kW�yks�/Q��˅3k�
`kkd������Ԫ�˺��uT?��LK�K�g��
������@.�Wa��>�!�4߫l�_Zo>��<N݅�p��[a���u���Yd#��
K�����gy�{L��C��b�r��~}f����|�I�L'0�Xk�h[RtQ�PV��+^��S�j��<9�M�T��0
�.9�e�~�*�硬���&s�We���ws�y��L�F�C��d�L�h9�#G #��r������Iy �iFu�9��CfV����FB�!���`Y8��Sxp[e�����KUZW�F׮��Ԥ�"'gNF*8�
���
����K��ٯ!@��Q冶]�k'����9��{���$�9�!�V��:�[3��.mє�3A�+9F�9'��2��B��B�@�Y	,Cah��K�q�j�B���� ��3�P��Ul#�?aU�$�S��I��J�[�/�B�+�nJ	-mS�N��W���_)=����+Y,�C�7�����p��?rqm�m�(�������	`�|�Z�S�'�N�^<N•c;���3x��a&=�*,��,%l[�,��2L���M�|c^���Aw�½	��H�_�����t'��x�pY	,��R�I��e�[��VR��,8��B��y~�TQfш��d?Oۚ�4Z��eSCA$��y�y��`�ړq7�@�	��-k^�%?��s@:��`���=��;SC:3��hJȣf~5j[�-;
��
�^����B�ŷ��j6[<+��w�S��P;������+��a���_��W]�"v�N�a$�c�kg�W\����CD�}�z),��Č����=��2�r�C�}%�H�G�^�(��;d��#v�!􀻛c��T앇UѴ�p2S*jl�	|g��A�iu�(�Ԅ�0��=�?[y�/�� "$�Iw\s������sD�@dG�^�f�&���
�0�K�kz0����4o���F�,��Y�3i�N}��!}!�|[{����/s��;��'�89�ZЏ*mBټ�e�N�+����o�e�bI��p�"<�g���"O��|��]Um�c|�b�PJ(�(�~Y|灨��چU�W��C�&���7��!���I�EQG ��Ax0	Ĺy��j��p�@�6�g��o��㛅�A��ւ�eR�Q���/D�Oӓr�m���W���&�+��?���y�3�V:F��=��"��ZI��fi�c�?�~9�2�@�B�@�O�Ѝ!���X$��d�P���>T@�Ì>LSd���2R��Q��K	�2�tm@��S�ǀr+�l��?���S%u��p���\����*tbN�ʺ�/�+eW泇w�����^El��G;���M����ذX<�L�&��*���~�~��oz"T��W�I�rz�L�	k�J�X��-��D���m��/!_�V���D8n���f�f�g��ކ�@���DG��
RыG��5']�("��{����)YԒ��73g�/NU��S��qP�=�=	�'l�А�D@�"���2�r[���(z���2�E��W�eg�Q�^<�C�On~ʿ^(��sᛊW�5y��[z�\o�)��jS������va�C�'1ޜ�}��mgr�s�.����"�nkoG��L}�:�x�X���6�o�hU.C/Gar��"�-&2��f�KsNn���-d�>Q� ��'��[S'��ć��e&����連]���Z��g�Q����\��PA6I��>ݤ��Q5偷�J��~5�Ś�6��G0h��|ae&�˗�@�OY$q���H�t�+5t3#��}h�W�����{{�ʦ�+eK=�_W�"�{����y���y!�؜\�a�?"����sC�� ���{��sW���y�$��lT��ͷWi.��
B�C�X-UP'Ϥ=��Pn�c>����y,vLx[����YxFA`K�!�J)Ʊ��ݝ�[�u&h6�
�
����N`f}G�
��Yՙ�ɜ�������B>̘LFS��d�	�����WUC��P�,}�xMG<�}�l�[F��j�ZG������O�Tl�F���y韨��
J|�����KR�*Ao��jg�RV���1x�K�fe�Iۤ�41��m�/">'��)��^�7<�k���*���v��}|��yd��J�UaC������*i���E�O�WV���4�,�s}M�G� �ዴ;I�пE6���&Lx&5x�ŵ)8��ĥM��;6� 5�ڏ
��@tıl\F2�0�o����fr�rc釻�M��u�M��[Q����?=�)��ʢ��Eݝ�L�E�Aעe�l�8Ghf��z
G���I�E\l����G_ BuKDtҦ�@u��!�����f��ej��#�yb�uhv2��� ZU�'
��\V��&먃5��	�`��2_�z+�!�ok����zЁOsK���VT�<�H�M�A�^�+GS�a@˹��Óda(N̑y}�O�5S^sU��ACF
/x�0�d��(^
Zل���RIɴ%C�ؠ��i�+��S�x�;9H�}vI��ɝ@�6��s5��Ⱦ����YҶ�o�G��xS%��V]<�?Dw�4���-�X6	�ܑ��~��-?�m�&X0��՟����6�,���`�wm�ʌ�BC+u=2�U�c˕�^�,DzܘV��Z�#�O7�=$y����5.%0e��������ީO˥;x�*�iin3C%����$[~F���p�Q�p]\��ؔ��֌A#�8o�sI�o�W_��E"4��<y���'�]J�A���.y��4Z��m1�O��F7��f4�V��Rƣ�Ls�rŲA����~�@�#���a��+�@���sƦ���ġ�8�/E@VYD۵(g@�=?��~>ʠ��f�� �ڑ"�&DZ�;c�s�xFf��Kn��Ä�Y;�+n�>{����c����WÞV��rA K6�W��~Xi�!�A��X�Z�����ੵ�M�Og^8����E���
�a�Ӳ������b�}����Lh�wK*��@�o�F����қ����/���\��Yb�bL���~.3��ϱ�X�${�D�A��S�N���m��M;c��A<G�%_�O���T����w���f�E/~��bp8�vH
�$�腄�Lڄ��D������]O�<�!�w���O�RG��GK<��'�.��!�j;���e]%㟶��K�qc��SN��lI�m�R1�K;?���t����V>B�f"~��/Nq๶�{:��!�<J��4
��`U��+
�vO�dzU�d���q�Ԥ����p[E|��JC���6��!��n��gdЕsG�U�f�F�t�ށ��Ѹ�i��7}��*��h&g���Y�yd|����Ѳ�_�J�ǧ~^�&��:0�|�f�}�S���%Ȳgpȋf���ݪ�'��k���O�2�Kl2Da�w6��yS�y��f$c�B�C�Ť�WY'��(g�Q��)��.�j�e��:���d�n�<ƿ��Y��ޮ�8u��+JN
�,
~��D�:C<s���ώ�N��Σ�X��{vnz?-�pd�f�n�P'#��\�ǻ��I��04$�hN̍KbA�].L�
NV
)Bm�A�M�a8��,�9$9-��*8i"�T�窟�����#w�S��h�f�¨Ho~�k�"�l�9C�"J��j�̽�/;�QA�x�x;�j�C2	!�g��	��oX����f����o�gd3���N[l�?��r2_>Z7C��k#�E"����-�]��d\B��zl�	�f�-
&
&���N	�ĸ:tS�mbЀ�������/��a}r�@��}]k�ޢ�k��d��j��dLS/)�'��e]�\+l�Eh��VaP|g:���e�ig@���5�wQ����6��N��?V�a�t|@����z����3�yb

��V��>91�lc�3JQĄ7��g�h���tϘD�4�/+�*�p.��t9����T[ŋ�9
�|ێ�b��G�p�~�L�$�j���6�N��c8���i��&�}�ơI1+xC�>�8��X����v;�t��A��ے/�]%ͥ�0ٯ,
����1�8�ʇG�f\�_'��|sf(Xr�VΈ��h{b2��;c�F���~!
��Y�o)^B�:���|�C�b*d�P��	4#쌉g��q�jG��9T$#��w�;$]���m�Ь�ce#����~x�r�Vl��%�1 �G^E��Q�Zl��l�^�?Qsie���^�4A5vâ�<
<���.ڬ}�vC'�w$|���e���C{(?kK��f�<��P�`*I��=I����6�@oP�3[Z�����Ϧ��qZe���j�1#�i�JKY�3	�
��#��=�YTd9m�r��&�5��B�B-
L��ȓ��Ǚ-!���$�#+��)��X�Ŀ��t��\��U��ww7�L��)�$o
�{�˚��ƅc.�NdJ���4���m�m�W�?ս$�C�����	ݣ����^7�<y�h'`��.���xeҡ�p��o�|�L���*�2�N���i�a�]���k�HuP�!y�h����iTo�r�m:�_څ^ֺ���ǵ���g$'Y�o^�4�,i���&���%�LpJ��8���<U�'p��+ۂ8����ܑ�ͨ�${f�I������7}_��&�_xs��d���
A�_�͌d���PE��?��!�|�������7~�w��S/�ٌ`G�`����A�[��'��=�NȨ>�(� z֍�GT-���.��p�q��JC�Qq�w���k���a�E�\Lb��V�E��ךVb]���'V|��{6�'�u�o��Ur����3��>��a���AF�-���b�旨;��y�kW���蔐����G&���Jg�Ȱ�xAӌ��k�sS�7����̇��z��с��)��t�����Z�?��U<V�[�g�Ds:�.b�SS E=4�E ��?�?<�d��Y�Q'q4�(�/d�/�P�=L��Sg�Q�v�dS��	_lj3�����s�wc�������{��3i��?&&b��f����4
M�h���3����x���)>�՜-�lI��!OIM+��Y��ƌ��W�E��F�b$���ʂ�_#�̜��h�
o���g�ù��b
X��C�2���\P�����Ԉ���ѹ�־%טпb�pB�]�I������	�Dc&TrSP��s�z	�ΘP{)G9��!��j�:I���s��D$���#@K�jh�mTɢ;���Z��r�&�_5�9��үm`�����a"�W*V�c�%�<_���P�m]&���V��4�W�bЍ9�����̈arH1����B��jd'�bп#����/�_�U\%�f�ڐ��0���TjM�iT�s����U�":$8�-���1�j��9����_�_�㓸�y�M��J+P�����K��]F"�Wd�蹮xY�sU�4
8�;R�w�-T��+��jk�xx�p$*n�F!�ur� ���A!Y~\e�}hÊ���'��c	���H{�ժB�;������'�U��|w|@���j:�Wixp:W�?��D�L$���7��\��(�����Q�M��pC���q��� ��'IŞ8l  ��G��#l~B������U5\������.�?a�3�>Z��}_�sF��ӄ�zע���
Lឦ~�
"Ӷ}��𬝡dMO�4WA���hi����qI������pBJ�Î_�)��$�k6Uf���2�09�t�fݬj�SN�$�Ǿ��g|��/�<J��Y'���a����+I�Ω���8
̙�7!29�4t)+]��զ�R�m	i�C�Wp�����KN��=�~M�I.�m�W?5�e֒JH�O�=̵�b���y�0�3�<I7am��#�MT&v�k�O�#S	�r=czK~�y9+W�͌9J����%�Vq�D���v�C:Q���lv���yw���/�Ȩ4���l&8��y'����ۜzIOʦ���H��Y��
�.'��~�g�����'�O����S�Ml�Q�'�����~�@p�C2���$�
��
(�
fN��>�&�~�Ԓ����"��jψ�6��Q6���vw�*�!��X����u��$h�U:�7m��X���m�����T���O�Qe�e4�R�?*@�Ч?ÑV&�e���n��1Y�����m>���yY�|��)���̦�d���x�Fk���^|səz��;D��R��ys�ˌü�5��_����3g�Ae�d��B���{�6|{]	��3����ր�EL׼M���1ZJ�_F`Z	y&��oZ$�7Ȇ����h�(�S|S�u}N��}|��^U��EG�?�PwnUu3/c�)�����KE@ҟ�8h�=�l�Jy9�!I�Ke(q�!љ@�D:�ɱ�*�#�JyK�Gl��t�j�̹�#3{�,�u�V�u
/��s���e��pQHA�|,�kP��ǯO�
�~���1~�Z���9��D�O�e���dݤ���BL�"���1밮��al��~[���=8�JW��B�s
���ß{r�sc,��d�0@�r��R��K�M�5�n۝��ӷ�I
]0�j�?G� C#�T�@�FN8�;B�B�yG������yrD9d���+��M��F�\���3���b���-}輅O?�Ŭ�]b\Ӻ�獧Bӵ�؄����82

0X
Ɨ>w���b�*�?��Tb7�8�]
R�pŰ|�������,��=>t߲���W.^���%s��`U�s�b�m/�,|��c}H*9�u��2�_�y{�&�b(�/���n�ˆ������5�L��a^5q:��wE��ͼ5�wܰ£
3����sE��ϱ:��ڏ|��l��l��s�x����y�#���� ��i�_��b��&�۟2�/nc %�_!3�f�tF&����6L��ݸK%7�%���z��`	d�u��*��9�����-����kUv_[����\��� ��MCW�0e��7I����@^��U!�}^�@�-�1�i�L�2���
gh~7��"���i��2�c��럊��ɒe|�w�;b�Rp�C�_���?ߟE�׿��FdŔ}�ji�N�p�)�'&`��ߩ��/�x�������dO��\�h��x��b(���p�uf¸��L	�@ׇ+�w~e�M�X��-d�}�S�ܐsK��O^������QfA�%�G�>���!D�mm M!I�2�>���紒�zQ��4���g�9h�
�7'[�y�d��02�c�S���&\�dU)A!���V�n|���L�x���|�>�z�����m<'�f��c���c�t��3V��4�Th=��bV���௪"��!�ϫ�Z޸�������[M�i�)�����Fm��[z��)��	���69��������؜
*k�{���(�7؟H��#�����+y_��@����l�Y�6迨m
|e˚���Դ�;��H�uӳ����[+e�n3^��n>�G"���R"	 ���9{�q�,�i�
"��V,��S �*0Sqg�_�I��u����:�����?K�K4���F鿪9��������ڐ�J1�E�a:b�"�,�*P/G�f�.�YЍg�����zn���3��J� :���Xn�	4�+�"YD��J�o�Ob"�=}(�[��O�6\]3��]���e5aQ��c����2_1Uř)�F�yV-�ak��
�)˿I�bYA�ott)�!���Fی�;�^AC#6�P���m"
���T�� d'kyN]��G<e�gL�Y�B��o� �~�8�7o>�e:�<��G)f#7�r��_*s%nbȽ)�K��X6�$�_^6uOp+w��6#�og5������y��Z�ǣw�ӿ���+�i��O�BǁK��8�����2��*��"�=�z����D�S��_��u-�ӤT�~��D���d����Y)nuGt������h����^�{�s_�~���㌮�(��KE �B����2|wR`�4�{�+�q�]9�F�wm�C�-��u>��A�f)o4W�G5��gfQwΔ�
�ܱ�r�al�̿�wD��@�O�/���y���~�0�ʄ�`=h�����{(���29�{���e�J\4��J��f߿~J���P����Ca����+l�LI��˞����u���3�*�)]�g�&���k�O��-n�P#���r�kɟ_�M�%
��Uqs�ɽ�2��FN�J�/�׷Cj�9�U��eD�e*��^����ΞO�y�)�j?�m��"c��|��>�ts`�Y����?1"h
����׻nE�]��
*F���d�2}�T��#5��&�"'1uV��g4xZhyi�h��%��̵���?p^�F�����+ҙ?�^���e���n[l�>�ׁ�f�s���b�L��v�H���?X������*��O�6��pslim}T�1��bb�����K�ôT�jhlF����`<��
�[o��5�����ߛ����p��#]_��T��D��X�~���c��'�!�]k�ޤy,V }�4���>Q��u߻@����~b*F�97Jދ��&U��.W��18���^�
�ސ���3����} e�VY��F%i���7�1Q�Ц?���ދJ��R��l�N�}s�@D�\��䑁��,��%��8�?���3xW#��+�%���Y�V�>��<��q�����g�FȟM�wX̛��r
�6g�P���M�w�G
���m�R����VaɌS6Ԇ�����䆗�C��M+O����}��
,zK�G�ؓ\>q�8'�'��3�c��B�@,��3,�� ���2*dShd��)N7p�_%�u�v�j�����f�XV04��8��t��F����u��A���
��l	�41e?�|�U��6��o�N�.ͺ�.s�Mk|�K�7UN�Y���e���{04��M�
s��#��
���v�R؉kx�Wm�!�À�w�\]�Od���?)��B��Ϥ�E�e<�`j~@�3��ds�P}9"	�x�H�sC���f<߲ke�VZs�4�y�Pv#4ױA��#��͐ݖv��3���]�nQ¡��큛�+ރ�N>A�CL]�@�dƱ����jbc��u�[9�M:L��'�^�'���K����`E��|jU��9d�d��״�
���+}</O�r�b�z޵�7�
34�CI�h�}5E���G�Rœ4�Ϝ��"��0���y|̾I`ݔ����vXe�PF>t��B��FD�p��e�,���@f��{#uh�<:"�`�}u��������↽�]�c�����4�UO�_;�xA���T����/�l��*�`�r;:�A��K"� l'�;s@��Z�i���!�l@��n��4��96����2w)��6[����z��u��:���O�>;����7ae�?UX��l��^l�y#�u��y$�K�܅ X��ʶ&���	�a�\�J�T�*R��:V��y�	��/w�An����®�9
-;]�!i����OT�ٚ���_)S��_Hu_�C|��o�&>��)1o���~g5�k�I�h����@=���Kba}X3�v�����5)�ܟ@W�ܰImջ�m;�Xhw�(+q��'���S0����O��9ӕ��4|�?a�$����!(���C��*�մ�]�ۣ�Z�9%�m�*���J�z���.i�y>�G����Tf=�BAF��<�`���x�B��;�+ܶ��M���^���q��EԕQ_0{Y���S�bo���U%O-M�jM�I�F�49��\���(�}��a]jʹ���t��&;�Z�M��C���=��pu��'�1�D��Dp(Y�EOI�!JHR����+&�YC2B�z��W�r���s�y�nud��R㓢L0pᳰ�d��Q�%
M��BS�ki�C�e ?�ˍ��=��k�0R�=�r:�� ^�閖�'~Ib��o����[�Դ]�c�)^{׭
�೓�(����j�*�IW*��
�p�o�s�ƽՓ��XD����������;�M3�`�|���I�>�X1�d�p����9�kN<%����w�
�3�$�0+�y<w���֧�ɳ�q����w��΅6�ͬ��x;��Ӡ��*b]��,�C��M����;$ROǴ�aF��z"X`t����D����.G��G�k� �U�ڄ{^��ko��dӃ��h~����K���H�:o�"�˰�`aA�7��H�(�Y�L5p�OL-{��L�3U�d��]Q1��� h�S0(��+2
�����"�[=8q�з�Ν�����9�>WkV����=��G�y�3�>7zR�z���9�.�{
G��ú���m�i�O�3��FZ��?/��湰
#��ʭ�C|��N���u=6,���K�z��my�~�E��-Z�zBs(�`1��m�k��Z��ANH�LH�'�4_'֧uuS�٪�Zz����h�P@����M�P]��bii�x$�A����jK���G4���(�A=�(��&8!�Xg�x��BU�4Y�F��V
��t�>�Z&@��͕��?͘	�i.Zt5h�6�1�2AG#��544xð�t?xeA�0]9W�C��xP�i��21̅ר�d�r���P��D��A�\�t=�fs�Om&����ɿ&.�^��Zߌ~/K��ⒺP��r^
��$��w1@���5��6
U:�"fK�$��.���y}�b<�'rm3��N�k���A�&�e< &t)��:�E?��"p�:��+�v
�#$_{c�'���O�y'�O�v���&{\95nM}YwK�aF#o�Hj"�dy��w�߰>����8�}d�1�����}��]K�:"�����k�@&O�,���َe{��d8��R��)b�?-y%|j+l�����:AlrYa���R�?��qv�o��R��oU�����W�>g�,e�w��=�k9ʁK�M��R8�;�����ܟ�Vi���ͺF�ڧf˹�i�?FV���r͕W�:
{`Y<����;��yF�^Mb��JG�����Pm�˹4�2夋��TU�i���PB�k�D���j�9�2Pʖx|�״y�jcq��k��L��6�]M�d֌MK�Q�_�O.�`Kdh��eyJ����b��W+�2���h9��i�ٴ2s�3Z�����G3���y_f�u��=�׹�^b�5��P�J�e=��Z/��^h$���R�2h�X��B�
��K	G�,��>����o�(e�/vY��ι*O�^:H �7���6�-�Kj����2{�JRџo��(C3�ߦ��>�&C�@��yVg��gFR��v�r�e��Qu_얹dL�ڰ��������1i>w���jW�V
�P���PN�WTv�Lq��^ܲ��r=�Q���9�.�����5�SY�}Ld�YY({�A��y�m"��ꝫ������g=�*�0��i�q�qLg�=�'8��֑�Q����%}���ϟRԎ��M��Z�����G��z˚K�+��YL�g6L�n59ʹ�\2�d\��.�G��*y`�@�S�:���Cn�4+��~�>�f.K���śDP��k�L��*aD�w���:b2��Y�+�}�n���k�k.M���3y��|�ᒆ�8��]6ey����(����������G�K�^x��E����C�����P2�)s3Z���^���`[�e��2Q �迾k�W����u��-�
͓�	���ኲl3��kt2#���Vv��e��Х#mA?�&�I�?TM��r�Q�ER�JcX�vJ���"{ʁ�!�����R/���m�N�R��������
}u�p��<I�Y�&E��<�Ң(>a��s��69�I��$�g�&$B�`1�����-�&9��Y��0E�
"��L�O���p��Fo�r��B�J�;����Z�Y�NǍ	?`�H��A�I7�Snz��)?�O���zu,��6��O�)����
����
Z��(>N��9��<)���� ��I��Ź��"��m�g��%D���D��_����뎳��4]-�uy4�)��xUk�ͭ�nП���+�C��meK*�2�z�p����q ��s�n�ڡ�7U�Miߋ�*�v��j�;�|�lS�A|#a͝ɀ�Wԅ��l�����lOi�*7"N��ߐ?��R*2\.��C��$�C���"q6��N��å����*.�O`w����,�
+ۿ�췸e��tc����8��lҿ����J�П�i��5>h:[˄�:{�\0�O?��6�����`Y��S�gM[��s�$�(��͈$W�q����B��L�}R9F�֗U
��:u�'�
�\�V��_��J�v�|T���^V��I���9� IX����ŏ�'F6L��2�����7��O�/=v�@��Z�@1����PI�&�i9Gu��I�ϕ̶��?d�`P�=_�Y�y�i�C?�B8��F~�:R~ҿ�7�����8�鋃�<�b�$H�~��E�_(�"��W�]�OVw�B��-$"�Vaȑ���rn
��-�S�����gn�r��W^�����*�G�8��PR�g%���|5LAp�+��{v�÷��um�
��(����$�˩�{‡+B��wd͡�zu-L�?���wU�NStnc8��my��gȋqԺ�WUٶ�ۼ��s�VAnA����.U���Oed�Gk��}j�7w�9��&3���I;_�P�Ԉ餘��Ɩ~kۏ�e������/�۬�<;F�)[�%r&���K��k}I�*,�̭�U"�K5o�hS��������M���9��L�"��
�^Q]�
���w{�p����Cs���2���*}j���$
���H)P21�_�PV�td���[v8���C������;7��Q����y
�ɏ�a�����?~cVBJv���,֩e���S�$��ь:�?wcsg���2%m�_�)��B"�cl�S{[Ek�W���%���`S{^:�e�G�w��_~)�_�6�o�AΚ�1X�OÛ�AE�����]�&����x�|�����Q�?;��G�^�"����c�-�TN)P3��Xӥ�y[Ϋ|ϩ.M4�!@�as���3��i�Z䀶V���3�$~�s����<�(���:�G"�g�lZ�ӏ���)ʹf�ܯ���)U5P�g'9��_��\T�Of��u��C+˃f�L�գ��j�����}��_��@��#���q��`��Y����-S$FN�
����.�4�{P���DZHb'�tt�c�Vla?|lj���>��Xnj��բ�hI��Ud�X���㤘��@(mO:O��J��pѿb͸}�F�|���k�q�K.�^�4�'H{���g5��dO<��Sh��{a� \Յ���֔Kݤ��Y��EJ(Pޒ��z�2�ќ.N����*\��s;�K���GgVIEND�B`�PK
!<l�D�
�
9chrome/browser/content/browser/defaultthemes/4.header.png�PNG


IHDR���R;
�IDATxT��s����?��|�m����Q��"9���c�	3�0�޽��_I�^{IPQ��/�T�i/��k�_��R����Wϵo���q_�z_����?���6�������S��<����_��������O�������C��?�/M��$��[�ۧ�W{�������?>�_ߕ��Y��Q�f/}�e�c�LJC���c�����?��~*}�XD��C���k�������}��7hx/�n/��g�T��^�+ߗ/y'}�ֱ<V�/}��s�k��þ�˧���N������PJ?��q����?�cq/�t/���8{�|~���[����oom۾zr_����=:o�?��q<H�
?��?��N����/o
�^�����܊�ܗ?|j9�$<������1�.���Oo�?�	x�����_o���Z����m�������Ӎp�9�;����8!o�������y�vU8����Kݽ/�o_F��ׅ�拾��s_9���������|�x�u��
+�2��u��G���&��M�7��x[�<�|��&��"�<ʯ�#��^��G��v�E=���9�U��쇋��W9�m�X�^��
z�=�,X_��k��˞^�No2'�ٓ���*�X���y�.��"�q��+8��k�y��l3'�S.s�]��,�oS�Z�Bd.��}-xUrlӧ��M�w�M�X	ݖܻ�e��x���2g��e�ڠ/r�M�r���M�l���#-8�ߥO�&ur��_\�2q#��I�&qh�f���}�w=��mޱNZ�����&�/�g��a�I!|�͸�9ϝ�.�׉s���s��mR�Mֳ�Ҙ�v���_�]wq%�)'�6e_&�����+�;ޏ�UҾN9�*�Yg��*��yWq;����g�r�J�6X%\˸S�M&p'2�|`w� ��<�Wb�sF1�2�\'����6\��˨~s���	݉ы<��y�s��.�yܷL*�U&8�ܨ��k��/��[1�Ʌ�1�2��9�{��:C\����z��f�YԿL����U:4�<�(@�!?O�oJ�U:����&�{�3ԕ[�H�N9�<�x`��I)l�����,�r��Rb��p}dE>4KF.��:�L��	,�Ą���<E]�i��S.�~�LĎ�/3̔���Ќ#�lx�E�n�'i~„qBN��$N^��i��|��'FLx�`v��*�Q���${�������8r�F�th��K�Y�Qx.1e�M�I����8{���!����LjH����̈��,=���Ba�F$9��1E!0f~���DbD3C��iR�a'�&�_�����r܅P��#��̀�4�\t[@O�i� i0�'69aOIΌ �$kR܀��
����Bb��8�|� �G8���8�i�,�3#�bFT^
1�4`�x����:+���A$n�c@GM21�2��xFrC"������C&c�b�H��5l���^f�Q4g	����f���(Z��I5ǩE�Tz��
��Q5�Љ�AgV�ڐ�P�N�Df����`�@B���;޳n�y5�V�g�PF	�u2�Lׇl#p��܀�N5�U����Y�(�r�dZ
��(�J��d�dEd�TCy9�U�&&+�Yp�
�p~��(�s�����ȷ\U	z�,N9pi�x�/� �e{�\?",ҲJH�������|?((q��t���c��0���$�թ��z^��/�p,-3�)C��E�.��b?,M�N�eo���x�~@T"�q�kЍ�T�b��|E9$��j/$!�N�����b�F5a�n��[�b�[R���xo��+��R7X�����H�-����qU%[�mW	@v�59�r�N��.����JӍ�J�_�%�@��.iy�9M%�w�唺�j�Un�+r�ep�B�p]�� ��J�]n�k��l4��tU�j�UiZ�ؠ��lzj�p�`u9��|�<ͦ�5NLZ�6̑�;0&7�#�C�Qm:�ug��jv���l��͖�_s��3���&d��j8�ug��x�V��N��5G������70���
o{�5����S����^
[�����Ҏ�a�Bb��uz,���y�j�4���]�w���C�\���v����1�V@��{U[�b�I睪Cnx���:L�\����]A��3����lJ�ޗ�{u����ߨ��+V��Ԭ0!��Ek��R
v]sk�R��k6�ݣGיU�k@V�UL�A�g*Ģ�I�
�Z�ҙ,Y��S3�
��z��Ur���T�Z�ҙ"ZT�X��SѢO6�E<��N]c�5��tjH�A	�3��0��@�V�Ol�)|��([G�3C8�+Α�ޔC�ҩ)Y���'v���2�xf��9�m���GS:�
'x�>�ӗ��X�W
gC���>q�-\����`1�V���›�S8ы��×���#x��j,F�;�R��oz����։��n�h�
q!��(�U�=.X���c�{�E%0��H8�F�ܔ|��������9zY�q
����(�t�3���Z`
/؇�U��6��Gfy��=�x�(����f?v��칑���7겻r`Rps6��M/��ܢ7��<k7A�5����,z�r����@2�Գv�Jh�p�f��1�X)�-v[ϲ�A=�aΥg�Z�?�'�p�\f�i�C�R`؍���d�c´�M9<9�G���%d\&H;��[�z����i�=��y��]��	nSN5�5Aڣ�f;y�����0|#�)F���n=�7��]J�������W+�VzW�R^d���OOzT�zb]�'i����GrT�/Ef��j �χ�ffW �I�R~3��A�Ԓk������t�@�j�U�#�	q�\�l�.
� �Ґ	�鐞%�Zz]d�������0�q]�F����6�U`�!x
�fn����f"��J�T�k��$BF<��'�z��S+!:���a�2��(lq�+�|"��)��Y�i"l��@&	#ύ˙%�6Ra-VӔ^�o���@D�F�8��Cʭ��a�0 Z�22�@�.�	R�a-I
��(I�U~�fM$᱅x���cx��Cb�f1�*��1���&����'Y�"l��Qq%���8]*,s�!$F�Ei5�꩘Q����1K�1��(��U�-�QJ?�p��`D~�K���
N��ɡ�/2�K�4�FY�c4��钸I�
�#�QV�)W��e�#�r�ã'3���0L?բ1E�V�Ʋ_�8���,��д�<�*�ɔ!�G f0Q���\\+�hB�X���Ē:_Z���4��c>�2
�ɏ����$קcj4�K���(�3 �� .��!����&�x��e";�dT���4ؔV�m��N�t:n�q
��F��H��&��	�I����giaL�T2�PI���I�2���p9���dB�S$F0��<�!1��|�0�O#�HL��lV���c���ֈ����\m�dJ�dJ�3:L��H�"�:cF}�ؼQh�^��HZ}G!2*]в�W4a�דY��hi@d4H��Sy5�Z�o��#Y5��Ӽ��Ϲ�&�V@8��y��du�Fa|v��T�$rZ0%��i�1�'�����؃<򑬆~x�h��Xe�O�@Z&y�(���L	j��$y%�����袖i͙�Ct��ŠF�9e+�`N���ü�BoK	���

�D3�^2��׉)�8]³�ї$�wHw�D��K��BԒ�9S5‚v4!�)���&�����yg������d�Y�!�E�'(��A>R�';�������N麉>��J�Ռ�B<�z��%�%UJ���?�4�jƻs�9��E9X����=�_�����Jr@�#U-.�Ȇ	��H�#j��E��AP:���2T�!=e�H���*jL�E�&�a��>����hoJ��Xw�]_U�Gg��%�U�������w������g��1WY�ljN�5�'�#x����T���&Ǟ��<>����UeW���59PWBM-��B�S�!=�><2ōE����=xg���
�`[�t�e���>������Jl��4�\-���;�����*g̃�C�뇧�0�,$ m��q�����zz���o��1
�twS�t���F���&3����)ۛ=WG����Zt��v5��8Z�����#�ǒ#�h�3�b"fk�Ξj��
n��
�`0����c#�<=���wt����n��.\�bkwm��{�C�����ӵ�z����J'A�D� ��ǣh����=��ԣ�Gi���!��
r2�y�GSg_q)*5_8��X9zb:���xH�����'��y��v[]9Wz�6<3���V�w��pڵ�C�����]�*K�p���l�14�#,�l�Q˹[G�!v��ljEf��5§����:1��
=V���0"Vs���*�3]>A^�1�#‹)<���1�ϵ�b�iV�wjt���i1z�YO��L�`t�wO����Wx�"yb�>��k��=���Ք1�~d;�#��� 1��F��j9< ܱ�=�X��'����윍�A�Doۆ��z��-z�b�-<�uf����3V��#�=�χ݃_���b5�gF�|ض��t���n�u!���٨}:l�F�Zi�I�1hچ��a�b�q�g�����dH�죖cܦ.��	޹n�l�uX���n���p
i����MǸ�vɭ\�\úcXsN �>�[�%��n�4l�:���*�y�5���U`W�Ӻo��;&5Ǵ�5\��g�`�T�����$�f5ϴ��y�5�8&�Tr�ʞYſ�S�Nx��LʎQ�5���X){�U�J-j��蘊�y�9�<���݇�u��U`��{*�V
��C��b�sVr�E�B��%�Ư���&%�L��[���2w�"�U�TpMy�R�y)���3;|y��q�_�>��E\$�d�߲<sL���j����ޅ\����n��Jp!x�X��b`U�m�Y��Bhy0��3�*%n�V—�Yn��5Z��V�_���K>8�c^	GRw
j[-r�UD6<��e!vS�/E�Vy�2�5O��_c.��*\fB�ld�'�yb�K�ר-^e��l��\��^��(���<�-�o$��Hn�H�7i@�3�&��/3;��
���M6}'E�܆�)b��lS�&�^�2�b�@mӑu�ؤH|-m3 ����I��.��0J��J��<�MF6Q�"A_&�^{r�.E��#xPx���)z�/�G��4�����"	C�8�"�H%�������r	���]"v�,<���$��2�1�٫x�*�����m:zg� 1����w�(�5�+a��/��M"z�E��#��(<�/JD����� Mߥ�}g�I&v͂�e,u���x����5R�\�6��s�_q��

���t�!���$���o���|�

D��J�P�6{��R�}4uC��;2qK����}��>��M>uO!��a����
��E>27t��)='�3wF��-=��L��LޅA�>���A�%Y�ә�H�.�} @���~��'2���]@
{�����هp����8�Hf� �}"@z����Q�",�i����>��s> ܇���k��D��|�{on��}�����/"�}8���}@z����3t��Go�ɇ0Vjot�%\�!�n�|�1X��!�?�
O�“+��,�}�s��|������+>zŽ���-?�û|v�O���#=��oT�%Tz�#y�ғ���k|��o����G�?��Ńq����o��Ȼ�_��'��<����O~��#��K϶ʓ����E��؅�^m������@��#��Ձ|���x�v��jo���o�곣����}�_G	jo��'{��@����f�|��?�����y?���y��
ʯ�ڳ����h~� �(�Z�/��7{�Oo���R�|Z�t�x��>����n���I��i㏓�Vd�?�f�ݸ�$�ڰp������$��V��
���s?G�N�"##$��|�&�}�pd;�yry��	��:��x�s�4�����61Fs�-�|�ݺ�m�nΞ8�9�{<��]H�<�r��8��<�Mjg����r�߯]jڮ[��<�����C�u�!8��k|��w�Î���?ڶ�#|\��h��i,/��}��C��bۦ6&���q�	��LJ8S�O<ؾ|(!�ʧkSJ�xʀ�z�duh��)��׽�T�� 2
c�����Sl�?��ı��|�d�M��x�4�od�/�78�Ŷ�:r����.�á?�%��4�>�p�I��TӶ����+��n����a$4-!��4/��~��X��?ԑ�l����d��_/�>�T<��>n��t;PC�?MPl��iĢ���nצi�o�3!�)�G�'��pP�1�]�{vM�W��i9��z���x$sr+���N_��~#O�'�����F�=I��\Rpį��#0��l����}�>�/n�P�7���|N<$��˶��z;>���8��~��+AON�N�8�oL-�~v�}����*�8�~���Aq�a=��|�#��b�efp�3��z�ʾ��y�ά/a|��`���,�I�l���^���d=␚x�3�B�S�M�2O���M���E��t�^�^�$^�^H��|X�h��3�7�ķ��8Z3��H����R�R��*���_x��PFֿO�y��ٕ���\>NgpX�a����a|�C��b-���Jԋx��tv�|P��om.�CP�>�^����������"�@ �����=A\#������3g=���\��A���/W�	]D4J_��ԅ��s��I~^�3�����k^#�{����Pd!�Y������-t�8ug_� <-��3�Hݙo��(����q>ouw�Ķ����I��B<�O�c�՘>,f�0�Uw�+��W�Q�72�RX��X�����>u_�!y�DMy]LI�p4�6e!�j�T�Y~�u��}b0�G�֟O'�+�z�Ҧ�8�s�q�41���l|���c����`=����3�t>�R\�(���4�b=��l:��s9�o�??\0�+g����p`��O��+|Ї��Q~��ǹ�1�#5�s>����anw�򢿪>���"4�x~}}~|p���5/|+��������������p�7pԿ�|�6>��\}�`|���)�a�ٟ�o�p�V�q�|��z����>?�|Q/�+5����?�^>TQ�#�OpV���#���YugqՇ��<d.ɟ����<L|�
���>�����M�}!(>�{���3<����1o���1Q�p���_��S˵3����~�MH�s>W}������D���|�Q\8l}���-�5��>dIW��}:�:�`"�������g��R�s���r�鐃E�_��}BC<���8_��ׯ��q�)_�C�DV'��¡���o>�+�'�~����8�wu��|�����'|��m�����xd�doE��Yy�~�8ҧ��2g|�^�����_�wqk� y��5���=����9V���.˸��/�-��������m�����y<^΅��s��>P�����-|�%�S�p����|���pсR�9	�8&��ׯ_}߅]M�x��ɷ���ś��H������8]ߐ�~���\۶?��|�q��gTQ�!�Ϗ߇C�L�Y��}m�Ǟ�P/�U���3�����ʧk�~��{��n)��O��>ğ�'8!��O�2Z������WۦV?���׏]�l$%<o����z����U�����Ǻә�ª3����-�D�
���Gͫ^O��{���T:����E�]7U��S��&u��)�?����^ІL�38��V��-6W���q��]6�o��t&�~������G)&�.?W8���g����]�'u$��W�P�O�b=Lԧ��\�~gu�y�����Q7�>m��:����
����#����r��ק�v�.��ϑz�\�9�R�\���}����-���c(>��7���#>�+U��p�'̙i�����J���F\i}���@��|�l�����.>�g�3|�}M�j����'qի�g�~�>�'���"�y�#��g�#�Jqp�a^�8��_/�k�"���1����T�#=�w�F�F���~gJ��������p첳�
���S�tWwp��^u�>�C�L��Z�M�~���Cy��o����
j�<�T/":䟫p��*��5�Uw�;8�)����~�p׆~���L˜g���:�?>���W�!��O�S�5��z�ξM�H������n��������W|��˥A��.��Ч��z��gP��O���_�D���/�T��tl��z�rJL�K񉘿Dd��}z&�����E�A]�����c��Zw���nz�?t�G�e^��/�c�W�YL�=��Dj07�27����r��5�K~-!)p����+�~~j^������u�μg���/ũ�IPz�i�av����CRS^���P��~�Z/
l����o^�y�z�U0�����~8���Y��T>�aɋ��ֺ�TH�8�T>�L�ć�.5/��K|XO��8�y��^�o����飼H��>�����~�^'n<�K>(�=֣3�N��>������Xӄ	��aH����n�2v�㈟Ň����ص�ʋS,�|P�|�
_o��6�K}��yA��[�?�I�S���%?�羿��Z�V��C@�z��9���h���s[범"R2����!ӄ8�{��Y}�z��R����ȇ����H��$�y�~xg���������?�D�����!=�������zի~���wbk�2�]�|���O�~�Ԉ�2�t�%_����|�)��ۭ�|X?�ȟ���k��/����{�R�j������pʆ$�|�?_O�B���+��u=
� >����HE�7T�h
H��H
ؒ&��>��PZb�Kz�������a���G����o̟R_���bwph%pھ3�%Mu�+�ð��_|�j�C�0�Hפ�+��?���9ϡjJ�CJ�.���U˰��J|�~u	~�Gt#��MQ8����6!�a}-=��=�{�2?�`��K[�f�hZ)�\7�i���1�]zx�!>ҧ�a~����Y�)j�ϣ�D|���.15/�B��	�).b8P�z�:r �����Uޥy�+��N,�����܈��?׋z/� g��>A���۩�x_}[�S�t���|p�I�|�z�1�gBT>n�\�
2Zk�>N���5��y7D7�H}�f=#g��0
��������C�8�G�i^��/��#RW}j��u}�	�v�x��Ș��	��2|X�ٗ������v����
�����z�R_� ��e/@ؚ|�#�֯��Yu�?/���/����)p�]�YO�Y�Jg^,�A��|A��O�ʋ�=����e8X}�w�ӕz�/�cy�r�B���ɫ��9{9/��?J^����&�Q�uCsU��	�����?:DW�`볥P̻��
YL�����1�)�+/7�O0Jg|�X �{;�	�)�}4���}&�q[z�3��M[<�8e�`�zO�Vf��M7t~B\y!r�;g)̆��1|6����-_=r���O�t6�e��}�5K���8}`��<�B,Tu�]Ex�[�?6�E�cM|䫔�fk�����h�%�9@�[ۏOF?[���"�cz����v��u�ѝ�p��j{�>��LT>/�MNa��|Dᇷ�I-�X6��8���T��ᒷ��߅�
q�Ç}cӲ	4��?o	����+C��t�{&�Z|3��r
ш��[.`��n��%�ъ_�����`2���i�p��|�2�3��q2.�mA��.U�Ñ@w�E��Ԅ����l[du���g߀�a�7>u�h��2ā��9�f���HW�֓�����l��Gk�,�eL����9�-.�N�l=\\�D-��J���3��=o��'>l����>E}�V]�Q^�Gb�K�#³��t&_4M�f���/U$]�ʬ��~����P8�iu�)>Ե����
�)���1-{k�3�5��x��5o:��/��|��OO��5�צ��^��,>m��wjO�k����ؿ��եਿL���g��I@��!-->��j��Wч��Jxj_���.~1�U>�W壙E��
>� �s�n�I&T���}�§;������Bg��C�,e��.��[���]&��ڭ�%
�rAT>W�hz�w�j�;#()/_nN�z[�U����M����5���`��-�h�w���;$__�+p�r
���Ŷa���*�kY/`X���п�Q3e|U?�O���?����+�/�8�)����x���3���X?�8��ott��[�G�C��Js�M�љ���J^��EM�Xψ��$x���d���:�l]��wBd!^�+_�|��Q�XJ�#]�;)	��RtfY_t��n�����b�|�I\�j=������o㔗|ȡ8u�O�'����$��ˋ�*�pt����;lA�j^���5�O>�e�Hg����¦i��X�u�y�P�Bџ���k��˫�C��3����/�ޟ�X��'/���v˗5�M��M��n��R�|��S�뎘�>�3�_��P�C^�pJy5m��4{x������8�J|�p�ǒ�8���oe��\���t&^q��_��2"�w�����/~��18���71�1��+������������sʫ����>�=�փ�ի����n�oy�I����|���t!���o�ô�ˉQ��ξ��,�{�.��)��9~��o|��~',�~����
����i
|�α>4a��8>nÓ�<�i�(?�ŨJ�o���
M�L7��]N�֏�:ZR�4CIy!��)Q �Ѯk�����)>�f>�7O��?���g��?8�3EA�E_�Q݉����y�?i*���>��}��ZG�9���|mp�����U�>�<S;�ǟ6PI���e�s��4L�מ�7q�-���Y����cۭ�8u�5�{{h��|H~˸���}����5J�#�n�!>M38����e�=(�ՇG�@�|�oU/W�u-��3T�#>!M�">���Z�Ž��������n��ƹ
���g{��4>҇1�����<$��8\љe16�+/���J_�dφIT�N���)����^ķ�~OoG�����p���C^�PwtXo�/�z�A^M~�.�>�g��P����O���p%/����>Ugp��v�̞�uA}ߩ�PyIU>�g�G:��~��)��‡����`�*�W�$����W�@��#}t/�ߖI��Q�΍n�{љ���ǂ#�3<5�SG��ٮ��� *R��7�J���mS�ڍ��-�?n�̟��
�S���\�߈�ߋG�1��i��Z�6e���⟹�G�����u�3�b�.ƇQ@�W�ŧ���9��b�)�k�"2:�����窏�,�Z/h��_�ϤS��v��య�T<�o�,H��wpƟG}��C�Jt��\p&�YqRP�n�&m��ϐ���w�~�g��s�Wp�As�d�Ay��k��oh���L��[֫/�g�)M���+��ӫ�e�\���:֦-_��5/�s��>8en��b�����?p���,/o:���٧��>��"�(����so���V���Sm��7��^��nW���������eԷm�ҟ��W�k_����)Ɏ�ψ\��6�T/�>����}���G���[%�6d�v}9���t�l}&A\<��M߿�!.}�[m�G����ɇC�g\�7������9%/��N��-�������c�h�7����!h�i���}۵(#�\��@~�'?l�k�]6��T|�ƹF�vč 땿w���}�[��œ�©�}���%#�
��y�S��۾���>IG�u?p[/}��uK�>�m�=����A��ly�%c d�Z��O�3y)NP�@d-?�z�I���_�@���n�W��)�lw�-_YWq�d��8�Y�}��H%��aէM
��w�l��g�p���.�{���q��t���.��?`g����:�_� ��0��/���鰬�%���>���g�_*O:��������w��/��5_�[�)�	e1�'[�7�æ�w�U/��_u�P]�:�H�����Ƅ�T��G�}�'|�:ʇ�K��Ct�e��⟷��-���&_|��6��֫O��Y#I��q�ȱ�Bh٦�7m���OM<���
q�i�Q�����_?����|n}]��)�>�4W� _��.�R덹�*k.
[^0�l�3}P����)���C^�͓~X���}�-"�Z(c��K^��P$�K�P_�O�s����o�����mS��Iv���͇��i^��� �����-�E�-��O�/�1���[pf��#>*��X�,��oԖ2����k5��)�Ї�sb�bJ���[hΠ'A��y�:�7\�C
��1۬F���5��B��7��C��o`c�r��~��)��Ɯ쫹���_�$����US��2��5�T>�#���;f�w����zZRq�Q��)Rz�P��AІ�B�8���SE�(�AI>׵I��^p���:�7v��;�8�	�K�=��"#�S�d�f�f��N|����h>Б>/y��ašb�N����u�3}p���O��[��S�#륏�Q���N���uh�����o�Ok^~+���x.�M�>��3��}�|���
O�Lb�Mg�ڞ?"LN��UwB��_q�T�\���-��h[��Kq՝8oLY����U���8���SŁﴌ҄|�K�@*���D��>Ӳ��۶�yQw�)��O�Ň�֋ۊz�n���Dz�8�N�5X�%�����Y��T�A�z�̽��}��C���ZN���·ċ��s,�‡�����sɃ�7����h<���.`���
�/V�����Q��{����MRp���Y<|��?n�G�c�a�Sp����i�?�����҆��,�R��b]?���k9e[�\�2>���A^v��3y���?��D?�[G۽cY�d
����/�|�O��H��UIVW{+(��	�|�k�/���p8�Vz
���~��חՓ\,[
F���j�=��*}J
(��|��+��^��M'���#���/��D�����l_��ɕ���z��U,U+�ѥR�v��"͏��;˙X���� 66�B��`{���M5��������g�L�D����.�����Ԣ�Ʋ�v��ݭ�;��!�g� �>��5}�J"��h�O߆(� ���i���'����,|�M���%~��<�p�A��W��=xD�/A�R&�˓^�p�LN����Wn�����D���W|�򞐽�h�6	(�.H�ѵ?@S�3N��'L�^�'��y|u����*-U�P���y����!�q�a_���9�e-v���n�iB�=�֖�kUz�,���8��}4Sit十����E���i+��)���������N/>��bD-	���k�R��C��ꜽ�w�H��{?p��¤a,��=�+��{��f��s���Otv�4_�?tu�җ;Í<�X~���'yTLo������[�������!�o�o�	z�s�A���'9�y���€��&�>��V��5��*p���*��w2I�"����5�3���o��y�]~b�c�?<��90jo���!a�fp��;'����o���ӫl}^�Z��|�a���>�uzŷ�m��SO�>{���v4{�޿љ�!� Ɣ��燭�s%�j����
�?�!e�S��rDx���O�჎
9�����s�ݧ��a/�N{gzO4��/83��|����|�߄3z9y�0z�j����5|ȃO��lo�9%�]��g���x����v��ȃ>��9.����Q�f�G#O|�c�*�&����H���;>��|Ư���Ӌ�L|����<���a�p�]�rS�q�(F?ɯ������O�Fo���H��Qf�D�>�A?�o����}a�<�[�ׯ	�f����!'��y�)>�����ݐ��W�t�!�������$?��gω_����5y<�����n�p�9Q��I�(�����O�G/�h���{{�x�ެ�2?~�<������b��'�#J���2Bj/p���yof��D�e���H�C��1A�U(����rS�p��?����<�����k�+{�\�0����E9+?�{x?�s�:�v.���=
�b���齍C��C��C_a��c9=��ÇQ�_���\���{L�	�iP��k�Ϸ�/.
�;���1?�*o������>��=B9"�E/��0$��2�Sb������O�(����#%�����9��c�;����5�/ًx:�O�?�_yS�=��s��7*�F�f�'���øf���r�o��۳;y�Q�uB��鲗����`���}ʕ�?��%�#yf�Ui�.��p���:
c_u^B�\ݾ�����9���K;���&�ǒW��q|v��%{�B;3fW�čsKyZ;y쎿��ʠ>�N��:��s�l甘�s*�<���Ku�>��rw�c���4ƽ�EB~qz��ON��Ԍ>�<z��W������&T+?�`�ħ��?�����������n/^a���9|�����-><�<�~�M�1�V�M��O堰�J��1��<V{�_���E�>ܑ�~>�О4�1��ۣ��3��+y��^Z��go�i���A@U����)�F�16~Z��C�U�'M���rex'�z7�k^p���Y���^�R�I�r_H�;-Xi_�!����?8`f��/��ʑ"�Qs����g'�׮�����^Oȳ���?� ���?�
?i��[�����uG�o�?�W��h��	&�����c{�?s�������n�)7ŦA�k?��?��=�O�m]>��>�'>q�PP��_�F���~��<��y�{�$���E�v��cp��`�C{���㣰�����=�C#��_�g�}埠f��Z9R��}������KA�q��sl�g�**���3�1Ư��gӾ��S.��S�M��\�.:z->���D�yM�<�"�E�^�霛��ګ�{�/�QJ��<So~�^����'yj;A&�b��<:{��	�w~2�d/|���6{u^B�{�jo,���󪞄_���w>7�U��s�@/+��a1�[��8qC�&z�Ω���}�[<�~����������'_�h�5����!i	�r���~w���^���Z|�D��Ӟ+:�vG��@�2D/|*�N/���bU���Ct(��^�Ӫ-�g�2tG�	����K��nL�y��%dw�+:[�{zY�Ň<��X���Y J�^ѝ?�^�쫳�y���tZ'��ϙ�<|jw�,��j#O_���ap��	l���|���M�(lv��sh--|��#�UBt�;t�X��k�70���p�9��S�*U~�'��?���I~
%|�t��K�9$y�&�1A�Ec/*�-|稏�M��.2�b�>��%����<�"??����=�;9?��G��W��[�6���*�G<���OqZ�
�=�B��^��[%�d�'���w?“ߖ[Sy���`q�E�(���
X-�db�I~|����jB�S.E煴X����V��DO�����џ�g�b�j�J����fw8����s�T�'{�k��i�p(�"y�K�p���c^�
<��Ɏ�X�A������	��A�7q����M�6W��TIU��7���BA��r*[<�r�&�ϋ6��JC��W�ͥ+�NȸB��4����i�K��H<���E[T�=��]2<�l|��������M�<�
�W�8�(_�"L���
:/$�B�&%�S����	�}�>@Ӓ���/��rԒ�3.��$KMg��;�8Oa�J%���|={�N��?|�W���W�}�qi�`�v�����8R����&�t��ǖ{���Q����rM���#�L�r#�Z˪|�N��[��|4��j�r�f��l�D�^��?��??d�hF�rHc��T|�W�:�Ջ��)��*?z)�m!�����
��]&�CL�@8F)>���r�����>��������7�C�!rr���-y�ʕ�����\�ѕ�&��o^2�U��h�����v�fw�X����`��W��ѓ3>��+tV�[94�rG�q��I�/ܞ�γ�0��ѕ��:x|�k5��ʏ]��:��^�.�ގT��g�f.�S����5�^8��9|�;��Py|аx���g�Q���S0,g����Z�eQ��b����g�O��>�g�p�n*y��{�se�4I<��ڳ����k���p�ŧqT�gű����#}Wy�bq�+9ps���O�=6�i�O/�o�i{+��͜+#ug逑6)�F˸Į�AfK�,o�
v/XZO��bl�y:���
��}o�h�U�(���^�م�R���C�/^��i��n6�@_���O��+�D��e%�Xݸ?S��E�0�:��0d	�����;��lE��%�]í~���
@�+�f��9p^��D���eE��b���f��3���Ї��otDz�9H?:Q�L�9ù||�{Oя�"���L��}�֒٪P�FN�ʨ:ʜy;s��!y�+��4y�7B����_D�ό��׿񙳶�J+Tأ+�I�A'��=jd��Sa�}�~��|�3�t����3v���Ğ
�w|��\��'n�^çIxs�앾᜜�W��Y��Ղ��q��� &�t����z���?�a�<'gk�>�~����w�d������(��:��9�y��y���3��?�w>���^JN���4�|Ⓝ�ޞ�`oG�<�iL?��u��WS˜a��jM-P�)>�g���0�Ϝm���˪�����#|&��8O��z��ԟ��T�wk��=�óz��]�Y��'y�}ڗ�j���|&�c�L;yP���S�O��3~�^�G����^�U��G��OOv���?�N\�|�Rש<�����5�Ύ��H��TEOk���ȟ��3�cҷ����'�?�ɦɃ�zc����+0|��E���7��Dy�O�v*O*�k�#ȮO�}����k�;ΝE<M{���3�
���~GY>��'㎾�2���b��ݓ'!�m�
������I�]�).����k�ޯ��B|ć��y�=y�=���z�����^�O��p8��ޯ�F�5�m$g�h��ٳI��>�{�H�^�C�y�'9�k�gx>#$�{{�w��3ql�|ê�}p��w{!�r��Z�ïv�ɼzn��烾�I�׭L�ᓽ���^9I��o�鮐�i������I��qrΔa��5|��*�A����sr��;��O?��}���av�[y<��������u�?c�S�A��8?���p�M��Q�Ӯ'N�[�}�������?�ԟι�k���+9�ޑ�q��|{��6���8�=Bj_�˺�cY�re��ӾN��i���Ӿ�o�H���2��^�>��&>��x#����uھF�r2�G���O�z'��<1��M�S�>�Jߔ�U����>^��q��|�<��S~���.���#Oʞ�s�G�(<s����;��}�4n�y�����c�i��q�?�/2n��=e�ş�:y�����w�ǟ��^Ӯw�������O���;�?�]��)�h~1�i7/Nf�v<w|�?�Y��(���~P&���=N�S��J�}|U���ȓ��O�w��z�?��s9
��0;n������<�Vi*��	���
�O�Q��Gz%��sw���Ly������	�}e߽}��`������2���u:���ū<�+�ԟ��G��\��2{B�LD�<�����{;���~��O�L����G�l��<c��ow|:ba��-ތ���!t��c��1��{�=�b�'��6�0L���z�ԟ���p��ζ��?�}���z}�'�ޟ=^���:�sA�
�6��Õ�l���g�3�T�ř�xv׏Ozun;ޫ��!�zmI{��#w��{�sw�x��O�N�Y�;G�r2ГpF�n#��U7�ϰ+�]�v���>��]5q���G�bue/���.:��g8�-9�G�}���>v���n@w�t�Q��[xȿ�t�Q�����uD��F�:�C^Ѿ���鯯 i���_�)�}¨:��<�`�W���O��ϣ�.�/���VG����$�����2K>cwSG�m:���S�?��T���|�F�Z=�55��FԚ�mX��s���ȝ�:�*��K�KL�H��s��P;M~����_� ��iG��Bh+n��°s������l�e�Y��s8�ކ73��Q>�S^���oF��
����y����g��S�e8�}������Z��hI�o�v�����$�(s�3z
�;�z��g�����;�
�H��x�g��^oq���(���?i�8�g����}�T�j'|���|�v�ǟq��k����z��~>|�{����g/�}|Dv>`�?��
��BBj���G_|��/ʕ�'�;WTP~^K��gsQ|�O��ϗyڏ&�AG,C�l���O���ԋ��|j��W�)��!��\@�1)�Z�������"M_�w���/�%�7�����]�����%�*���'�,��m��dw��!|�@���.z���{݅�����c��y�B�J�Aع24W�Z�����^��uE���x�]�sx�B�u����\����R���wȡ�Q�K���&�
��vpћ���a�}ݑ�aX�H���Q�U�#��Gk'R��;M`>��sej3�4RX����\s��fd�N�7��9��C�fC+�y��>���x/|�/����D����ywԁ?z?�]8�b�s�
�	����Wv�ͣ�߀T�K�(�;��$7��b�����|R��,"����e���=z�h��Ұ�����I��_��;Ȃ�������%%���Z >�9�A']��jf����P�/yrj�s�;����h�`㷭�`��a�;�Î��\W��oi���Z�?DW�]_�WN	��ܝDM0��k�97ً��"?
�D��g�4�_��O�Ыԇ�L�Un���RX�cPG��9p�4�>����K-m��d���䑣g�s�������!��'ȣ]0x�<[vO�
���<IyN�)��?R:y�1&'>p�O�<���4pX�3�ⷰI��:M��t��K$�.�B�q�˧���0y�]�K<�����7�>�&$�?:�e�����vg
�����{3�oO����/��<Ȉ8����96��%���)e�!���3V(n�\�lJ0��cڧ|�̘�"�$'#?y�j�=��gr�G1{_�׷7�����Tl�ÿ80q5����=����=��<��7�n��ֹּ ���$7��^��ۉ?D�Ot����H����O||v���M�ݽW��|���VP�]�r���2��ϧ�w�8O"�{�����8h�ON�Qoq�ԟ��݉3�Q�t��#y����o�w���?���ߔ
U�;�+{K�m|���RS,?�^<���O���?T~�=�1��;�#g8>��[�$O΃N~��(}�~kG�
P� ����O���Yz�����=��8%���?�A��U��w�tw�pu{3~���0yĽ�<�؋���)�t�SN�D�JT~6*�9�Si�)n���0y���'��3���⋏���#{�s�<����>���[��':e�Yq��y�d���|�k�g(��lE&||��z-�B��f�m~�81M���%zL�k��8������i��	e>0z�v*��s����o=��=�݅�O~���T2���/��K|6y�+y���o�����z��~��Փ<���	>�
�ɯ�S욢N.i�����@��y��5��}Ew���o����|��]�5z-y���i��y��b��?X5On��s/�_姽;�*7�����F����k�<FL�>/Ы�PD�;�<9���F��1��([�pf���:(j�����9E�i�O�0�.O�ꡛp���؝<���_\g/�2�s8�����|����k��p�xz=1�
t9C��^�,����O��+�E�i��Or��݉3~ZyLƟ�ǿ�-�y
��=:JMFa�noo���	�5/�N�}�`Q�=9��/y�/�9ga(|8F��8�38��b���V�ٗ��C��6|:Ǒ^���9r��%|2J�*o���!<�<��kQ�z;��?��qw��X�xzm|<����׉�ﮫ,�9��+d�hk�!s�^�;�1���	�����4jǙ_)��]�;{��Z�Xv���緥W���?���4���&|�C~�6��i�1�ۗs��+?���wy����W�1��)��lv��[��?$|� ?�?�]��83?7�8�!n���&#��3�����i_�/�j�8������M��^��5�{7f�ƎO�]�����'�|��>�S�s�S�U�o�/s���6��_h�O��{�v�e����s��yT �e�ڗ'&�+k2�"8z�׊?��G��ˎNzB���U��5����zA����S;��m���v��G����_�O�5qu�]��#?����Tr�͚����g��L�=��J��ˉ�)>����q�y?�V���;��]�7��v��:�;f�*�^�/�<§�q��1!PwL�R�J�@Οk��݂[w���M�:<k��K�(h�x|&G�D�L`5y?��"<��}�C"�[X�{>@[;�s�Nv�D��YЮ��^y{�'W�8��A��r�<9˝:�˧G��A��F!��K
�
�B���<�vsNڝV���>|�f���杄n��_����Δ�u����T��a�.>���Z
���ѕ��ˑ������INt~�^��Թ��2�A€�B���ڗ�����Oʕ)���>�C�^��<�z��u*Oz��e��.��^���G~��C��%�s}�ǎD���g�7п�.ȳ���O��a��79;��Έ��QN�\�V�:���9���V�G���·\�4���U\��/Ζ����)K���b�9׾��8g��S<�:�g���`E�x���.����8���۞\|p�_�uL8�A~>�����~9m|&�#:v��;�4���+��y����V��lqc�C�<�㳭g���N���|�#�ܝɑR��@>s�P�=�yD|xcv�8��-Iu�-�k��r����7��}�B���\��	��ӝ#��vm\���.��m��_�L�1킜�+�߽_+�x�S-�I���v>�"����s
;���K�c��@>�y��E�O����˩<z�ӧ��Ӆ$Ѥs5F����V�G/��Bz�kD�+Otg`�]<���:����38��) ��<����H
��Dt�0�c�Ac���������$
�e��^��cw9sݵ����.lo��n��<�,��Y�v[7)�����!�m��qi�}�<�����G�Φ��OC
���Π�׿�	!����#��
zg:��G�7�#N�זe8�Sy�\n��Ђdz��!������缮��=cP�M�[���q��(��<~���T������c�rV4<�U{����m�.ܐ�]��h�T��y�Y|�QOʳ����Ӗ~%�:�׸e!-.ewS<�wȡ�aǿO���i~x�Qy8��(���E�<Y���M�C/�(�
:�}�o�:04�������݇?�	,i��m�kʳ�\��v�lFhKz2[ǒ��r.�Q�S�mH:����lh:͑Z�r	7�|�jt��Y��K�������Ǽ�K�05i�����dc�ߥ��=���Yw�
������3��0���]iI��>�l��;2�V�__�%��B�xt�_ԂL	��K�;����컽�F���k�;�U�F���*f����.��E��wlç����������ƛ��r5y��>�0tp��y@m�
蘣~+}���v�4%(WNkc�6!dV�/%�hK����>+�Iy���:j��vo4����䁅C}Z�X>��!�x�|��2u���<�mݹ�t�d|TpذG�x8+	n��hg�A�=�U��K����K:~��+{�
�������YF�)mc ��@�y٥Xc�� �3�8>��]|���]]�99���ïM/����ΑG�]C��$b���r��M��K���y:v�
�.�SP$�0��K	��)��ҷ�p����+��;FO�'>��`����AQE�5~r���zv��E~�$>��@�X�弃�j�B���w���OY�S<A��;(0�|�‡0Dzh/;��s�O��ah���4ۄ���B�O�pr�C5�4�?���1�qw��/v�<���,��MJ�PAx�уr�ʜ��Zj��sg~X��If����$&�_�$�Ci5U��}���
y�{ჿ�����t�����^�2�ÿ򪎏x��<��]]�����7�Yq�X+� Bx�z���\��j��y�C&z�������L�y�+.5'O��W��OSn#�����A'g���m+�d�����)�?�����Ͼd�>��I�������)u�W���>�T:�0!��V�W��I_��^�cw���󂉳�r|Z3����W�w�jMw�<��T[Vb���e:�C��=Dͦ�\{T ?'�ȃ����^�{��}1Y|j����^y|�S�ph-@Їw�t�Zz�os��.5y ���ϕ�p�j�f�
�vQ��ً��	Ǿ���E�^��0>�?|�'O�,�t�d�]߉��g�_yL����_�Y�y�?�<�_y�ŸH��?<�w�I���?������F��ű/�_�Wy7s�n9>r��M�1D�����+O��?9�՛^�Ϗ��_}|.$p�^���ܕ�m�|s�7&�a���]T^�щ�/)��s�
��&n�Y!���!���z��H���A"Q��)Q><�#�'yȉޞ����C�~K|蕜]��pS~ř3�$:ܬ�G����c_�豇?;�6G_�mi�<��B���#~[8Dg�ބ���Q�kԟ�S$��1����&��X�gძ�O���!Oq������3�=�<ȳ���Zq�8�@�v�n�u�6�W8h<�%σ*<n�2��<����s� }&'����i���ˡ�-��Sy�䙻��g��'�Ao�6���?:>饕���䯽��x8�U�����)&���D�
�D9��>6��e/�
Ç�C��i��;§��?2�����	���߈��
��0���9;�*|�!�?�w�||������Qi��
�TV\OK��Q1|���S_�t�^~�^t��O/tvi��|��Pn��s�z��}���t��tzu�	%�a���պ!y�?�(��Ϸ�*g=C�:�Ѯ���]�rh���3��z��g�o���<�#�	@�Fx�t�.y�C��N�gt~�O;<ɉ�݁������ƴ~�ǟ���_N��|0����p�G�S>���6y�'�Ԏ��8�Ç0#?|&>D��}e�������{�=N��ǧ�X�ß�^��k<;��Յ��s�:4גȟ}����zz� �!j�1tzU�ܲ.W}�����k����}�>���"�VSziGa��gz-������?��=��7O{�3��<��\NLt�Ϡ��:�z���S�i�������5��ϧD�'{M�����_��W�<�=w�����!�I�UD�㟕�'z�j�>��_H�/�Y{����;q�7����yƷ�:���
>[�L��a��O�zԋ>�Ǵ��3�[G�a�69�e��D7=I��}�D/��h�ݑ�y�=��`v����b���w���DLJl����0�[	on8��M����g�.�@'�������Q�)���ы<Ϳ8��ɐ����t�œ��A�K�(f��(s�<���Oz)�^�F��<��ԓ?9雟T>>��׾�+�M���!��7�?��W>��U��Zj:�=|ȿ��j��*vuq�fo~�Zt����C����*�ρ�o���gg����G �a/z-����ҫ�yQ����ӝ8X��|�c��*d+���?+ Ε�^�E���G�?{Q��R�F_DЅ>nj1�S9��:��/�-@�x:���9�'�t�y9_�S��O�=B���
T��C����Ca�=�O��H�6��n�;_D�,o�?�(���;ۄ�h��7�Do�جz�a_t�f���2�K�%|h�%f�_�2}��y���Î����$���Z��P�U٧��p��ꇿ����]}���؟׆���Md_¤����Ž?!�s7)��W	3��3{m�C5\|ʥ��j�|6�c����Ow����~G���<�C��9����#��Y���?�\Y���p�N|&磸���zu
�V����2�	��:��!�n���}{�:�9:���(�N�Uk��\�O$`���u6�/�h�|�>�$g���w���8~B�����Em>�e*�'����l%Oe�_�=CyR�8?�p�����z�C��A����w��9�zmө���]�������@|ک�|?�7����c��ɯ}�^ݹV_Y.�􏸤�����ʹ��J�W��sl�'9���k3�[�y�����Q���=���ϕ��.}�}�T�}��3���}��;�7�
�M�ԛ<�z%>�����O�p�O��cm�K�T��w���S��K��f�����p�#~εb?��)������#���g�)�r�:@!����p��^�b/����ù]��ı+v�C�e��'���Y�ʨ����'>_~��ϸ�hZS��<��ۑ
�'���7ç���~��u�ax�y袳�lx��A"��Eu��_]�5�
�W~���7#���k:�?��?9�����z%@��&����d��pH����:��z��'T.��h/|[�~q�(���4լ�Y#0��bU"���B�#�cO?f�0�n]-$>Xw9YP�8j8�t
D�3a�m�ƪ������PIR����y}V�޺'��O�(�)�H�ˆ���։�C$V�}��k�-&��Q_N��l�X�"P�=�����؍]�Α��qbE�.ցy��i2*�|�KQ��yݹ�d����<&4l����k';	XY�^qs�2����y�׏�iMǦ���n���k�7^f�'Z���o=�n���Q�"�Jv�^���Kd�	��1y&�`Ҡ�X�A7d�Li�e傔�Y��Wm�9��FB��6>�$V�̚-Z)�C�%�9w��0����=����*��#�*8[���|0��h����ʣq�u��i�"��|i)����'��L��e��?Q/�#g����W�g����`�;
��L�^�d���o�G���ц|D�^��.�l��������.�1�ſz�	�5K��S�=_���PˀfwEi�X��ʇξ�31y�'��z*z� �Ө��.�&;��r$�&Ok
�qvG_���{>f��8�����%���8��,��OzoI���h��y�+���r�y����[�pN��D"
F��oU�+�ک�rM)}�=3sr ҷ��1`����S>��k�ہ��'zk:
3Y�h��ɣ��^gm�<�!���!�p{rͪ���v��9�&J�o��o�	e5�&��}���GO�y_����Ƀ��Ňֈ��{�,��q)nD����kh�%�[t?���0���}U:�'�ҋ?௩��B�B�A�ٽ=���������$'T/�}5�L��A�����+M� ��O�	�؋<����vG��e�R	��|E���	�Q9�M��U���:^"\i��a�}&O��45���Lq/�qW����g.�t��(���}���PyAC���I���S�/�C;m����V�p��1S5������j���>�T�H0P[D� 3��sgJ����	g��'y�O�����O|?ԏ��!�V�3��S8���k����#�.G�|/�.y�npPG�[�F7��W7���CtUO<l�)��a���ɜ�px�A7�Q{Y偏P���Յ�y�#O�9��?�4���^v�^�����f���'$"��9�Tjʃ�������J�d0|�8����ʆi����g�.��\�[�:dE�X�Y��{�y�@u-�����L����y3��c���S�ɟ�%�	�'�+y�t�''�մS8+��韡rv�ы?p���<�}Ÿ>�E,��ha�;?�^�&���ٷ3���҃�����y�IS{�o߾�r?��o�9z�ˍ|F��=Ri���q�1�ؙ��3
�W���K]��{6���+mg%���"�u>��v�<�������a�k�?�e~��vᐶ�]�LC�g�rJ!yPU��!��ψ�3�|����̛Љa+>�z�}��!�Ԍ��=c��%u��k|r�*1G��N��{{��'��Y��^>��\���T��'��c�;8'>�Hdk�-�"*ol���_��je�x��„&�C��5�|�㣒�O�Q8c��_�bx�^{�Ɏe̸E��¤��g�)�:ځ�8Ծ�>}(�������j���,���҇�OM �g�a�=np�ԏ:&�U��>�=y�Ap��g�|��f�3��/�������ϸ��sJ�,�~b�3�}�3�j_k���&ѱ{�ž*��|��8*>/�x�z���ᇊ�8
q���!@y�	�b&�����3����Ϟ��iT�=��';f�ƙ���zg������\�v1�E�����٫�����Wb�i�~.�|���rt�Ε���X��)
�t��4|hL{����w���i����vAJ�-�e�}�s�'�1Q�Q�A�}U�S��݁/�Aq���ÄnR{>��W3�,�jٝ��^���d�;��
��i_��<)^n�">-�'OM��k��8>>��EDU?�vW�:��_��q�[�Ӑ�ӊ\+����T�Y���|P�X����Qu�=�áx�ίf�>�g�#'`��iC=Ƥ�y����u11���^|��r�
{r����`8�a����T�!y����)�*�r�FQL������_,|�����
�?��z/}+��
ů�|�]�Y�ct�0ju��C7^n� &�!�p�/zq�B�=|Z��	���D��Q=y=���]�8r�'��m.��}��[�k�5��Iwx����O�p������E���;'�s��H��0zF�C���	#y�����9(�/��
��AJ<��	;�ɟ}�{��'�\ϑ^�'O�h~���3?��ETx��z9(�)��'�h������ٱ�
:?)|�ڸ97.|�-&���ѕ�r�����s[�';�W��JUt�|J���'�5VIw���Ë�7U�XC�#D���&ws�L�6�䁿�ٱ~�@��kmE�_�L�HY��^��ר]��G��Q]�ɣ:�]��ʡ,���ٕ���4���Q��4D���5���IH�3�t��2����'	Y�<�N���?���;�i�'��Z)������c�4/<��R �5�U�G��W��y'���N�����JԚ�øJB�-u40-0���3���|
hV�ؗq�?�_Co�-ř��ci(Ọ+��O��U� n�U.���?�
�+��"T.}�N�rAH�<�igD����2B&��R*_k��]��<�|^�s�i���V����/E�8�B�\IȞ�[��<���9��c�����'''FQ�.+�
��oj/��s�'���LA{סy����S7q>_"P�mY����բ�A�SͅM�scA2�S��	��qh�|��8I��f�щ�u���أ30G���Q�^VD0&�өu^H�Π��
�⶙�\�O%����\�5���Z4E�O�8����}�ˣ~�g��Q9�V.�����-z�;φG���<���S����7��^	zO����"�����o�vGO��
�#s�·�T�4F��h��\�D��d��[c��A}�K�I��[�=�'����rh�=&%�P������P��PUw���;>�~�n��򄕫ɺ�(�5;(����%z��v�~𷈔�����[z�i�_�����栆	�jҤ<??��B"�V��[Y��O�!*δh���rr�C������Z|��_K�wv�Y���[Q��^t.��!�7��d_�������_n�ò� v�����J��U��N��%��դ���[a O��|�F��289��9�S�7���!���_�㡪��
��_�)_j�QX5$�C�E�eiN�L>�.�{�@�o��}�I��X��R�G%	˭;h�
^Վפ�7�wF��ϯ�q
j%��:�ƛ��SS�0�q����k���@�k�>��1VT��PB�'}�<Ε�U�JO����5�/���/������I,��1��xׄ���%//�웷6�-����F\)�\�%�	+Gl���Q:���=' ����>p�T��:Z�9y��Vo'����%���������=���ސ1��K�{���0�?�#Ok"����\����W�:�/J-�k����v|t_�D/��ӫQ=~�޹�6���E�c~��^Yw�y�tk�+H̝,���c�4�!Ot�%�+���#��}?�|s��Ʌ������ލ��Wç1�o�O�>������`���Ξm?��k,a.�>r>���'y<Q¹�Ϭ)�U�7>�1�ÇH�`�lή���UɁ������d��?9��Z|F�D��3|TA��	��>i�W����3Mz��܋��^c�����<�<���io~�g��	�j����k����1>�c��%f�>�Gz%O�"O���}��g���=�9�:>�CN8�4>��g�9y|Ы4bz�n��Y�*M��d��Wu�]��3k���fN>�9W�f�̴d�G���ݟ�X����
�ݧ]��!�n�����d��p�?5.ϔ�(�ܯ��ͩr�TH����W�	���PM�pv�~5�������>�Ĵw{*�3z?����O�U+�x���+���p�xt��Sӫ��k�cӿ��lp��!O�ϴ��X���3��^g��<�	ړ�2ִ��)�s���U�'��$�.OF>��Tz�{��EU��g�p�v�����}�'�#�<�kr8��s�d��$�G�}��|�L��B8Ϛ�ī�go���3{���T���'��/��Ͻ]��W�^�<�<�}��ܔG~����ϴ��7�>�_D�sv���8v��墊yi]��G���4�+��$�^o�D/���+?�F��i>�3|<�~���f<��g��N\Q�9���^J�o�'y�t�{����O�9'�L0��\��}5;��;�L��:��z� ��N�G�w�Qx��D�h�Ư�4�C�S������=|�+���i_#��;�3.=m�	��L��*\���!n�U��l��'���������ȓN|ΈU��]�]������p&>'<�t����?y����?G�qn/�O4��.����"�~�g�W�U>b�>�)b�4ި��?'ɘ�_��}���>^�O8Z�S�>��N�b�߳{ʎ�L܈>3^M����	>�z|��|���3��L�F��x>�'g�U��}�������p��	?���W���>?���>~�d���U;���g��~8�M���&ϼ�0���{Ɵ�+y���e>�_�O;��uyq����{F��̺�̗g�ho��+�p�ǫ9U&��č$1�b�}�K|&Ώ<9O�3_�=���t�;�������q̛b�vM�ic�ݟ��c�G�i�?{���$ϩ^�'|¿�����j֣�DZ��ף��`ę�v_�A>�	?��4��4�k���
}��5��u�}����C�}�/�ߒ��Dz��g���]&���TZ�g���/�Ň���Z��	�����뾽l���,5�W{�vVnG�{חk��a�pa���4�'��*�3㊼n�cs�L��h�9V��q_�̎��#���ޟ�@H����C���s�BR*�<�ϔݟ4��tw���2!0Qk뇬�'�Ď�O�$%�q��j�7��j���ʍ���	�@R���ߕ7��܁���Z6�*}d�P�T����au�tyv�ݼ
��յ�Ͳ�lC*���ϻϟ �e����Z7;o_�.?l����?X?�8�&���\��J��S��Κ��i�ŵw�������=|P:$?�؁N���|��O_?�i�4gz}�L�BYk,�gJ9�C�d�}�N�Y?$pF���(���;0���h��X�3�D�X�2�>�[��g�)��N�u�[���^�8
�>���-�β����Щ��Q��t�_���߳�%A�'e����e�h���Lf����e�	�K���4�r%�̸e���c>h�b�~P��8��Qu�V����������T���#��sC⑧��}�GV�L��{\*ng�=��w��w�����'�)gn�s|�_0��;�}Q}���g��ϼ�<�x#>�[�r�Έe�����|�Z�3�;3��*��G��9��ݧ��W;Ώ�A�'����7g�U��<\t�[{ᓄ;t��F�G.1��GC�����	�{��g�v�|��8���#生�T��wS��ܶ�E���CBtL��� �a<�S�~�훯u��+~��7�����D���:%��'�5#�Ԋ?����͋��}8�9���W�=9I��?�:vI�Z�v���=�7(�:��@����_'���ד_T(�CP�G.��ZtI=�'O���Vh��<�Y]Ņ�����\w�y}Hyr����s	#7��{}���^�ִ5ƹ�8g\<֙_�Wb�T��u��g� VSu2+�,��]��}���<�$����5�����<�Hi�!��[�V���$"+��~�$O�b�-z	FMl���]}��MY���D\37�zRo�]'�y%ה�/��oqT�����u'ť�KW���4���������0�~��F_|�8x��zu��LG�Y���|ȉ�E'�y9릕�8�Ƙ��Q��
��J��Iߌ�ݲ��/�1�7�1A��/��T��]���
�k����t91���q�|Zy��O�m.�i�/�
7�pʗnr^�	>F�:[����ûw��7T6�B�����+���uZ�������5a������!f©�\By���o��|�LrVȏN��G�`���K?�_���Z�D.n�f�>�s��R��5`���/=�	�r����8F�Ua��O��s~>����
JT+1���ޫx����It�d.�ݝ#b��N��ჱ�W�
��IqG���9�c��{�[�+r2�D}6);���{���s�L��'
k��S���:��q���bԵ�9/��q|���cЖ���ڻ��JD,�2��Z��"�ɼ@��Gu}��A_�o�s�2��v:�!y���p�^I��5��ɳ��+���ƊV1�t��3�K��3s������~X���e�^^������?
�
�9��	��?���|��x����ߋ���y�A�:o�W0�yus������^�{�k��h$Be�)���-��8�G������=�F�{����O���Q���(>��ϫ7>�cE�m_����-�y�|�.�<-uz�K���ؽF�^�dw�h*�	�����Yف�v�(���^&�8|��Nٝ�y2?�S|��kI���+v�Ҡx�����g�ݵ_�
]�F��vj��y<n
�<�&�M�;�S�<�ꔏ&ڝ;�>����]�y|�^ڣ�@�=����o�"�ű�>#x|�;r�dz��$�8;��n��'|�g�|�T�%���ʳ����C�W�?�xus�;d{�˝�w�4��L��&��Su�{x�ǟon_!�!'�[�M��o���(>�>�k�Q��u�L���g����s��ȣ}���l�<�s�f���N��=�?���ׯ^q�v�
g������_pF'��9�����W�?����]<�ܩ=wM�╪�^��w&�qŧv:w2�z��ϟ��_�������{z�g��A�G<>#����o�SP�9��|�!�Y����(��B�'�����7�����ӇO�k��p68ך�Gzi��X!�y-5Z��F�^?h�,>7�NNq;>ϒ'?\��.y�)���ݟ�Nc�κ�ٛ����8��4��,+����?�^�����sn:ji&��y:#�q�R8��ϖ���������i�EK��c/�]y?�<���Y��y�,|�=���Ot��;���Q��f��i�?;��~���ٗ��E��l�p�yf�h��L�?��]�1�}��y�V�x���@����9>D|���^�,��g��:���.^{�xn{���>V]�Ǟ>V(�<���5�	�+��O_�ۋ�<�㍄��.�]��hʦ>�_�N��f����:���[;��W���ǧ�lg�ݻ�_��ӌ-g/�� 9�f���_�cWw&Ɲג<�}����E��k�+�Z"ޝ�����w��w|&���o_ug
���b�1����	gt�0�K�E��pg4�������C��� ��V/��j7W���U���7���<��Sn��g�k�Ɇy̰U���{8����Nǂ'����e슮��3>�W��e�:�ƫ��}�=>[Ny���U��ķ?���~�W�lv��;�!y������A����Wo��;����#Ƈ<zi��	�-٫�U~�Rx��=]:�<٫� ˲���]�ܼ�qeNM�'�����^�ў�k�������ƌ���d��-{c/�\�ɓ֎�ɉ3]�^|^�-�7�Ԗ�v��㏷��yOk˟�4����u�?����iC7Ԛq ��A��gO�C�Ñ�^�g�3%>��]����a��L�^{�������x�O|4�<=>�>{������z����}���My�����>O�}
���{k~�?姝j_F����9C���[~��:{����}�g����J>����o_ݾY�LZ��,��x�W��Y���m���ݧ}�x�^��}�Ŵ���#>�gD���.�EN��{�����U��^?���Lgw���^�!�v��j�"�����͛d�z缽�}�^�S�b��O����R��>������F��;q��k����+����}ֿ����U�������>�m�A%�E��4"P�(�e�O�X	"��g.�+�?����b�q��?�W�U��#v^E9+�t�fw69���^���&7�&�ua��R�6��\��T���:|�I9�����]6�`O/t���ݳWl)새U�fS �tnAz�{:W؜+y������:v�u�0�?��^��Gzu�����\��6V�ӻs�t�mz���j|("���ڂ3ѳ�=;vd�O~�`wr&����CB�����z�DjV�<ᓟXP"ϴ/e�������l�����G�~R��"g�\���x�Xm�u����z)�����}����gp&$nmg���J�
 M�&�w�U�^舩��n?��^<�T�^������9U�G���'L���c���_�ӏ�K|:]�u9��5c���^�|:���@].Zw���v!�\�Y!����Ǯ��u6�K����Z�s�r@�k�g���4����8B�*��I AH�N��
c����Z�?N��K����A�d8���S�y�ʟ��S�*ת�ؚ-h��ȣ$��I�l#Je�^�z9�Q<�S����02�!=�x��}�㒿S
>~��_ӽޥIux�.�fD���)g�ׁX��S����?���{��yr�NԝG�c:�e�G����C���X��U4�}m>�"�O�s�_�W�ټ��z=N׈ �-�}�:(��BfM�T?;�e]�.gh��ǵ&(����5I��v=/�x�P|�(�C]K�b��9ԁ"-�k�p�[���:w�Ws)?)��������^��^٥�5��o�7>���` �N?�	�c~���/N�?v�Qv�~AaM�0�x��3�y�dħ�Թ�]�b(2�A��|����7#)�n��I`�{�ݚ���OT`�����8n������$�ӹP��!�
��w]�쉣�aiX;C���=ݺ��8
����8`=<W��3��y��U�`��;���+v�3��{��a� �yb�n$8�]�9t�l>H��D�
�p�����b��x��k�M�㒩���=~":8���ϊ�{�G�O�����U�I�"����;���e/:ϡ}*���'�.��ur?Y�gs�e�/�W��t
����k��l����s�s_���	grY�{~�
5�=m����:���+T�cc\�.�k�>������Z�ud�iH��n��˔P`]����}���v�NU��8�rw;� mvQ^���OyU��:y�~{�ߨ�<����G-����K����`0o_t~�Ֆ1HT���Q���G-�����u�'�P��O��ҵ#f;��QC?��Z�5��xa��:OE������D�v����/��X���ޯ�ln~b�̦�i����W���mRk<��l��a��dgY{�gϸ�5�����5�������i�)�2�
��us+P���7�z��;/�!Nvy�����#ې����'�֪M*���Pe�`���{<��
�:T�41�H�r�
 	�r���c�*�7����v\!�>G[��Y���JM���T��F5���?��<us��O
Bn����ԇ��G����ۇ��`ݫW]�(0u����˫�<;�W�,�����N�A�뮌��=�k91-�Ӎ��_u��(��Kx7��wc��Y`����+w]qP$�YAy~u�Y�3|��w�����Cy�B7�|y}��UC��e�ߞ��.S;F<�֋?>�/م8�^�.�v��
��)�
�'��g"pV���_^�j����}��W���"��"����
��i�D/��ƅ��_���f�p�ϫ�ؔ{h� ��/��6y�By�׷�Mt�`jH�'?�]������o���[�)O���O�������fr����:y���c��w������\7v��Zq���֑����*���a��;��0�j��o�GW^���Zْ'����~nv�
�7Ҁ��/kd�����h������>�1�k�yy}՞�0!V�yu��c�A��yys-X���'������Ǝ�{`��Uz��gr(����[�d�I��ᓜ�]D�s/��n�a��/W���ș��s��r��rM��,zs�pNL�Ro87է��]@�<��}�*��_����ꍞ}�<|��;|�,;�/�=�\��4|^^_)�x�<����ʓ�Ӌ�͈�gt��<�om��3}��j��W#�R�ݯn��_�F-�I��W����p��J��f,��>
��.�F��U/���Oza�X�a��X���4��R��
�<7�^�W�l$>����?��Wğӫ�;��5<�N�������乼�*g�K�.j_���+���<�9�.������<z|F��Y{	g�F�����9���t��v��!�j_���T���^�e<�<ť�a����5�����w�\]_�t�+�H�S��A������W8����8�>�������r,&W���>�^�X�\�ˢ�u����.�y}���#�v���[ƫ4�F���
��ֈ�^�/��؞.��ᇇ�^��w̛����W��떦g�#�D��i��Ǯ�?�1�2S���zs[n
{Q�
�corSЉ�<��œ���ez��sm0�n
e���|��p=�߾�y{��<��<�G69���f�����=�`���O?'����>p	�f�ٝ����>Ƀ��g�y��9|���.y^��^꨿��W'�}ծ���8&
���<�7���b��<�_�*�<�Lw������S�f�C���ϯ��CP���E���2��������;Z���#�g/�c2��U�f�?縎�._�'�*�כ�_�S�(l`��^��C�(C������^e��?ʣ��}����k�.��}�Q~�����gU},Ɛ'>��2|�
��H/v�Pk��O��e�*O5z��	z���u��<�n��Y�n�G$��J���˹D�s�ߘ��o_]�S��pZ��W~����^��3|0!j���W���ޯ�/Ջ��ģ�SyU�;�Wş��k�����/7|�n����U^<D�^�Y���!�P>�����w�B�G�Xv|����z��8��,�а�S������JO�+z��O��u���'~Z�R�t��P������Y��ѿ��r�G<y���ر��E�M��nn��9�U���9z��]�g�2�k��E{�TS|�s��N�rYğ��+��(�
/ً<�<��؃���z��[UW^Ө��(�z�.Ɵ���3zM�)n�a�y�&�����!>�?�F��8_�ы��{�c%�ԾF�=>�S�^;ݽ^�7�.8�*�C���7�jG�!z����_D��Q��v��յ�	����<���t�W��L�_JK���c��ʎ�^�
|�3{�5?}yua07�+>��k���_{���g;Jٝ�~7�K�i_>�g�!>>����~���Ɏ�y���/��{���L�9,*z�1_a�$�9��<7z�i�R�rE�I��^�Q���6��<���ݞ��W7��_�g�#�5��~�>83��U|���ō�c]�^�#h��ׄ8A�i�F����Kq����K��H�څ�y��o{��:>-����w~���`���&,X�G1�����I�ֵt��@й�F:��O�ve)ɩ|�9z��-���г���w%t����H�y���/Аs��HN��C��)7�T�O�Zx�'y�i���C��]P
��[�O�}j�^cP(|�r;��?>p]�)t�]��J_����b���=?$��Fn87��Pٽ�O�#���H���O���ֵ��޺:�쑫��B/L�Vv!�����hw
�Qj�6�__���ȷWz���������IP{'?o�:�Ey;����ֈ�a����O�i܃?��?�j�~3q�߮@�<�%�;�Z������yf�a]�U�
�+��g��v���%i�.�b8r�~*���ېX�w���3I|�N
!$�	/�x�Vi��?��O�'k�G~(���M����̖7��^��j�-'OvW𧻓���ş�K��� ���kyVcѱ�]n���_|[c8�^h׎�`܅��E·�v������YD/�"�v����;./�\�Y���e�W˟���r�
s�?�r,\��^`*>=7�ꕡイձҗ���?D"��3򰯗E�����#����.(�b���񟲖K���,�}��3Iҁ���قCw$Q��~��㿈�/��`������/v�BD$�e<v�>ʧ/>~�.�:g�9���������Α"�7�x��B�$V	�����'?4>_��lE��3g��7�ϯ��|~|vq

�g����֏��!U�`�O�򏟰��Y1vT~�BR�7c�&�g2���<��7�ƿ�K|�Gh�?����X�D�)t��������^z	�3Y;bv�~����R�p�ɬa{��J�y�rh
k^���@H�e�����}���.p+�j?��Nnf��?9Kt��6�7���(���ŕ��k�1��ey*|Э�%a��O�
"y����x�,]�391����9�_�����&>���W���>�)�&��������C��y�|>��U0��7:|��,?'L���8��K���8���H�9?�W���%a�'�7�����{+?Q~��{����ʣ�9>ɏ��i }�.�=>�e/՟ka�!���_x�Hz��3t���(���O�;��?k����>��?�ϟ.��R|�E�$Z��k˸ֺ-M�Wy�{�_8�w��P����̹/_џ?� �u'�>Y�(�Ԕ�����c���?O9+�C~Q4�G�$I�Y����k�.�h�fG����>k�O{�Y���з8�N~�W�,�ʅ���(رx���
����]_�M�3�A�<~�o[`/}�y(�n��c�⣊�<���q��r��F��Ej�Zyԧ�V���7�{�-ċ��#�_�O������&���J��y�#sH.�%cVÈ������k�
�h�,5�6�V�
3�?�<�����
�y�Үt�2��I�G1�<3��/˵H*�i��kERB>��\���[/fsQ֐+ tHy��Y�ɯ����Xt���omR-����7s҉��u��_+��z����o��_�s�Fܼ��&�Tҭ�s����j�D'L;�j��N�g_��VɃ�=T�p�Q��r��0&��@��X��/gL� �Z��&��Wij8��LRbA��.`gK����eR�
*xoFTY{u?�E��$١�l��.���+]����b^��λ��ԃ?/�hT���=`{���L�v�{O>�9����3��)�7濾v��d���Zm񂾊���z��+&��ך�F:�%	���?
������ �+��x��	y{ݾz�<
�ΧYy�gW�9kל}������o�4#�-�H�</%|��>����r�8⚜�/웜��Á)_�ySyߩ���k̑<�I߫�}�C�l���˯���9)���'?����<3NX8+�N/�	��#�V�	w��=��W":�ؗ]���.LI_�=yT�Qϵ,�0��W�=�u�&-9��}����K�T�*�bh��,�|�{���A�0*�p��OF�D+�]�q��5;���m8�8��oe����cOZ���O��p�׿�Nβ.���z���I3sQ�_��A�������z��$Z�}��9O9��#&O��y��Vq/��{�%� �W|�_��M�χ^oި�|��s�������Kn��z%O�a��>�=L�[�#>��@���7��Ƿ��΀M���O�a����ܝÎ>�}9q�38|~�����;�u�ӯL������ɣ(>��i-��*?�#�ğ������D�Fϟ�K�-��[����|^���R3s�W91C����>�i��H��<�m�6joM_�⶙$ӌ?�#���8��=����*���\P�����z8'O8s6�fk�
�M�?�s����k���*�]�'����λ<�|�ƴ��
{��O���ɟᏞ����H�J{Er��;;����\�)�?y^����/)��<����[3�ݟ_޼�j����>�GT�%�����%ަa�g��+#: �<�W|���ɳ�\��i4�W^{�r��������?~��7���O�h��f���39(�4�>���?��v�a���o���?����]����tć�z�M��|8�5p@T:����;>��]/�D�T~ٗ��>�_���I��ֳ�x�ļ��ʞa���ǭ���+���3�������rbV��n����M�s������B^qb���K��}ß!:��<������{w{�k�<̙+yV\�>z��a���.သ<�9yDg��\��z;�1>�<�m؛��I�D Rq䱇��<z���_3U��{{�'G�n����<�A��2z�y�
L�3�\��
�)g��||5���*�3�>�1 �i�~(��3r�����?�]�
�9>�ة}�<|Z��{�	v�����{�7������S�.���qT1�Y:ѩ<�/�-{�!x ߖ<�������e����],�u�y��g���k��<�<�T��U_�U�x�p��R|g�ְ.���%�i�ǽ���0"H��Ɵ5v8�xu�(����Qo|�!>�ž�C�ڳ���+>#O~�^3ޠ�v���>k�&�ʟ�7U��`��8O����e,��O�<�7v��O~2�p��_�Y��ğS{e��v���s�?�O8����;Hd/�(��/zM�jE�����~��S��(�9�x~��עvz��ʋ��P|���58��PE7��>w�-����dob�x솽6|f\W;-�p��&kk^�?�,����D�ZӮ>r����Y���}ո�w����N�3�R�g�j�Ų�y�#�;�<>�W|x߮W8��)"W��<<A�����^����u�9�hk�7s���/;��b�^��̵,�f�9�j�;��ş�(s���\L@��Z'$�	��	F/K��N'�a�S{/7>��ߛT����1��|�A��㺃��)5��=���ߡi��ݭ=�r	8���[��gVTz���\+�z������&9�;���&�w&-tm\�sSK���4=Q>&�E���\)���UY%��鴿�I��Mq>�aa|z����a�����5R������W�R{���?������'��{�
�)�V�.�Y���A�����֚�-O�C�%�	��ʜ}�
7k�p�'?��˅��e_�^s����Gk;�=u�-|�ײ;�p���𒓄�P��t՛<m��v�^��4����aZN���<���;>�ew�c��z��u'���%���g��v��C2��Oxrj�ϵh���˙>��К�~%{Y���rn�Ƶ��rS�ʌ�1�V#�`�������,yb§;����zR�#� �uT�8��O�W��U���c�)��J�v�𫹳�?�.JNz�b�9.�?���sv$@����r �	/�砻H��<m�8��(�j��i�S{�R�^��B��{}U~�?�x�0��x��z/�O82��	�z#��Ň�/�/��k"��ɟ<E�{�O�P��|����Ge+_{d_&�^���
��!���)c5�˕y��v���1�pc{�g��s���W�Op�?隶z@?��R�ƧK�a��e�O/[��Ұ������g�)�
�˾��W�]~����%'擾��V����j_����~�^G|>����_�6���YDf�,���&}�dJ�)4U_/��z[��؋��gaR)�:�i�+JN��^?�N��Ƨ|�ʣ���>��4qR���I���.�e���~��q�2|�/;7}�;'1�%����	���V��ِ>��|��=�����g|���˥p)��I	�G��|�m|�\3�/k��~n|:�HJ\����cf���3�PFٖ���,�;уE7��a{�'�����:�|m�g-��M���R֎�|ȃ��	l�Ny��[��Z������|�츩s��0K~�C���ӓ^���ӎ�O�,��-|�W�ʰ;����}잜fm�������lk���''>��.��oC�Ț��#��ѿV����ҾQ�E|�k-iǹ
���:ن�˃鳃��U�������_pP/>�@��C�#>�����(��c� �?;~����U�����ȃ}��O;��<}�Ӥ�KG4a8+ߵV����y���I�G�F��Y�z��";4q~Z��?�+y���K���a�Sޅi�_�Ϗmi1�H6
�R:DOt�?��3sV�"3Wsc���
_��	�K��|�D�V\��2u��Y{���KU!�\
)���D�)��8 �q6�[��]<DHcM�o~`#�U
7��u����E���8�o���}z��j�z�K`�,94�}���θ��ɷ�YiV]�E~�|��U�������g}�F|��޴�-C��c8fy�h�����{K�^R�AG#c2�RX���ych�Gj4y�\]���58p��
|z�Ϛn܉<1��8b߮�|�[t�|���p�i��������٩�1As�93vΒ��l14�y�*<#i�5V�O	
��Om�z�j[���N@�W��ѽ�F!�H~nla�B���)��qޏg���j �9`�)9��T;�F9
FgLYy�Y��7y¿J�w�O�e�Z3jn<8>��Z�OUX��ћ�$O�v�0��]���q�!ۜv�cr���wW�[F�<�ΆS�E�W�>c/q}4�뭼ݳˣ�*�Ћ{��+>�3���D{�?��֎��pΈݧ��^<�w{��<��g���
}p�ힲ;���3];�ϳ�7>#��]�t�+�{�^�P�0������[����R��+y����Z���~�3���Rg�n�x�	}�����yd�Syf.z�W��.��a�i_���<C���g���?�s�k����|������̙��?c����3�ȴ���߆��O��G�
��<�q�I�JV��]���Q�H/�g��ā�ʟ=�b���^��^�ԯ*�˳��ܔ��n�p�����d��g0�tBb�ſc���T�����ħ�����3������Ŏ�~!�)��+y�+>��a.��}�Fq{�f�}��.c����)>+O��<����yd�ݟ�|�d��3�����H�՞�̲�?�c�8;�����s��'|��#���G���A4�-���+�Æ��r�v>?G��g�h�g쾇�G�K��'�����C�zړc���4��ώ�W�^����w���O��OR���ޟ�qu�{4n��x8|��] �8�5���G�8��?�G��δ�����>�<{<�ⳏ7���u*Ͽ�g<|�go������y�@�|���3�G<�3h�">�g���	�O�g�>�$c���<�3��Q�O��wy��x,��=�����k�<��ç���K8�O���u�'?���)�ϛ�O�i�)�����O�u�9��p��v��q�����8�����9�<ⓓ��S�=8O{��i����W�<�t���q�?�/��గ�'>�������ԟ�o�M{\U~?�v?�s*�čΥ��CO�����G�ɵJ���)����ϻ��>�e+��i����{{��#4�3cUv�q��_�s�9���pֻ�9�Q�w{�y���$��s��t}̷���}^��q��=��x��O�o����rۍS��>����=�?������8��z��w_O���#��<���g_�z] �~���u(��;n�����V~���c9��n��k��9a�ף�81����ٻ����^ᶯ���T�O�Y�E�����_w����?��3�p�ױyf=�8W�O��k����y[��l��ʇXir;��?�i�?{��k�K�qJx�ىhM����ñ������<Q��ݹ��ʻ��cһs�AY�(o�]�y8��6zM�����N���<g�Ո������Һ[ʞ"�g>�	�<u�|Hnh��6�����uP*��y[�q\t��8�d�C<���ɬ�[��d/����o�9_���
z�f�ޥW|��	zm�����T���kLy{��2��k����!ϑӳ��!���:��S��6���t���hrt&�����HY�0�9����٫è��3�)etN��|����������I�)�
��o�W|�����A6Cz��~�����L��5Do�k����F����E}�è_����Q��c�m�8��3�^!��隋�ɓ�s�UiU3�q>b~R�3�

���o�3��gp|�u��˓����
��G�݁&�w?��t��Sy�ep|�pN�p>�k����>w�
��6z�ō�3���yƁꭽL�Syf	t��3N�<�9��N�G��v�R�?��g�Bd���ބ�|�f+�z���꣼v��?�^�u��_�
z+�N�gٽz�/����y�e�vUi��}���\D]�?IY��ε����d�u��o������wE���䉮d�}��I��*/`˄����䄏PV�V��'I/�g_�+�v��r�m)|�F�E��/�t �#��S�	�=�G�O����qe�y�K��^�ll.�	3I^�E'��.��ϟB
�ݑħU�R���I��	������d:�D~"\]l%�}^b#���<;������|]"��R�ZT�Eʃɹ)���칧_>��0�5�}��ĤW`�x�]��re0�j�r��}:�F������]�����s\^-�il�?c��/y�DN:����6/,8'����?t�A߳�9.�0�t'�;}�P�U�TyX�Ӌ�2hW
��S�D�1_<�+�HY^�����U���7|���������B�Owh�6_|Vd��j�|�@>����s,��EV�C��F����
T?�K��ւb�m�>��S�2.h}��Q����Zj�l��7痸;� E_�o_y����9Y�W˗w0�;���TX�C�A}��'� jBmt�J��Z��x��劘��8/ěIJ�Z+�%8��{�p�`ar�p�xo~5��o�3����=o
��H���42��򚽝
c}�5>�Έ�^�����E�+��_R���W�\���]x�#�+�	!�鑗\� t��[a�̟�>z]4�O�,����{�>�$$.�sMR��Y�ڝ�D��s��W=|>�����H�U^��>��9�/�0�sp�*k7�k��	�eշ�?��B����ޏ����	��^��}y;�����W�:ו�M�؂�e�~�<�C��W9�Oz�J$U5���Q��U���\b=��W^d�k�y�;�|�\8+�+M�`/Bt�g���g�7�8������~����4ރ'�$����Թ�<�v�8{��:�c�i��x��V
f����w��c�̲8�*%O�O;&9�[�B({��st"�����o����7�n���ʋ{�;��툞������z'n'�W.��~p���IByj�?>�Q:?L���f�S�4�&<>�W��:�ȣ�]�+y�1���>�ˈ�M�.�����O�ιI��xoO|f�����I���%�9�FS̟�q�?�x}�����^a��o���c��'L�nk���^M����{�]iI�p��1�~��������PSBWoL��j+V2��_�<|F�]VL��ک8	���ޫ΃�#y����s�{~�3�~���v�?�9y��QM�U��sq�����}OG�O���sp��E�<�L{����.������^ᣢ��͑�J��O�Y��I�t��:+���çWo�{n�3����8M�)Jo3|x��җsS�%����/~��y�#�?��ߜS����X-w�?J�w	��s�)>_�87�U��|��W/_$��M>����_�C��bu��;t�3���ݟo�y�s�l�5�||�%�7����}�'�ʊ3�Ο^�ǜ�xy~������g2�#O��w����������>-O������:ܱ�(�y��۴��z���O_�bN�ʾ��o�x��\�O{�
v�&>Z�g	_��_��љ�����؁��f�����D�����<9��ޏ?F�Z�������G����c�_��]c3|�ӿ}���
O���s���w�e~��7%�зv���	�<�@�˟G���>��{�F٫��5�.��Ι0�g��f�r�D�����
|�$��yތg�!#����G�z�Ė��c���x����7��}� G�O�E�YNZ��6����7b8�՝G^ٗ���<	�³�~��
'Oze/�V/y��{������y��ӷx#��3�,?��.���in��{�–����:�}�g�A��‡�>���
r�h	���?~ �_�<|؝^��ȹ!�s�e�|v>c�o��7DYz����|���(��͡���V�����Xm�O����r�wߒG�����y�v�O�C�����ʛ�>�t���i_��1��p*c�^w3��#>�kR܈Oz�?�31�l�6����v���B{�C]'�5�UBN����>]�sC��������3㺱W��2zi�~��'�W�����v=�E�cw�����<��!��:�$O����W���t�������M_ɳ�����+s��x;���/��9X�U"a����HtU8_�����v*^�g�'y��y�$#:��ʸ�v�WfG� ��fw�H�0�k���+|Nq��3�0>���3S;�#y��)�]��)�O���2�!�>���Nm�/�õ��.��LN���a�w?4���1x���"�?s�:�vA��Ù�
>�/�QTܵ���~�^�dE�Uz)o�~'y�C��8�]�_Is���s��׳��%��\N����	/��#(�v�2��^��>!f
��U�T��vH�v����s�ә��t���<���11$Ozdu.9�**߄��N�����D?�$��G����ʼnz�Ga|:�E��'��<�������A1�t����>�cZ��A��+�����	gD�p~��5��o��;e�A���a�Ϝ.�助�e];||�I��骏]��{��C+>jQ����Mv7�&?y�:��58�>�j��:o�u�ޅ�p�w-e��:M�,y�7�K�������!]����!wmP犥o_��|݉z[9r�q�DQ�ew'��k��ǻ%{��'�9Q:9�ǣ�B�[�:@�`>|��c?
:f?�\�w�����3�<�U��Z�U��5]����
�kW{�;���S~w��{�=�W�\���qe��GR.B{����v�Ћ#:���+�k���B�F�r��-�t(x�w�?�[.�k$�^���'��oE�@w�
��G���=�����O�����\۴�9}��z"/2�gSB�¡#�a��yN'o��l��V���~<�k���?�2|�	*P�ɡ�:��r
�F}�ʋ?�6?�(���������/��?�s��]N!��Y���#C<�:.�0!��M�����үܩ��]�!F�AF_O�:�ݼ���tɨ@}��ҳ��mq�!��ޱd�u^]ҭQ��3�iSz����w��ɇ��� �����U����gq�f?|௼>����'�ܣp�<�&�G�él���\���8�O��+�>����sH<�K9�諭N^'Ew0)�\�m��}���B���L�#�����)���sL����j����0t�˨ԧ��
��;� z�@����ըO�i�ہ��ytL�ֹ���u:1�~�s"�~���r�
4o��}Sy�����HxNn
~���\ʧ��4t�(��ԝ>���M�Cx��QI�`ݝ;��UdX�1�':{#δA#K_�(ןm:�y~��L��J�C�W�}�O�@�I�""ħg�(w��|��J*�J���/3���Zwv���;�ir��H8Qy����ˆ3r�?�;|�Ι���]9|��
> E�Ͼ�:��=�Hw$�1�Y~Q9;�>:�g��s�m�;�A�H�����<{V-v�e�m�(lj���W��pw�LU�[/'^��X��l:+�f߽u���C��m�B�Y�Ʊ�{�
nx.�B�ʘʭI.�H�@$~�q`�;T�iVɷvy�\��u������7n�t�]�$W]s����A.�aҶ�N0~q�9��3:���y9�����}�cb�;U�x�T�UX�;��x �����˲u��e���ɥ��;|�}K���&�(����И6��;���+�/M;M����^4���;y��s���<��Q'���vWѦ��Y�d~��
ɇ�u-
O}E�F_g�){�C0�P}�ӧ�O��;\��ď�V���x-@�\�J�����Q��í=��KD��{�M���orqlw���Z���c2ȸ3�9��G�M`wn��)C
�a(�";2�y��(�ݲ�U�g2u`��Ƌ��x�tpQ���6�!�/�YkvrwxkIU�!.y�r�):%s-����|�Ԟ���N�E��9�lh0�RM��:ױ/0EW1��\�������Q�#'F[�X2e)�O?�=r�v���Oģ�܃�r�ǃ9�sq�7����j�nsrN1�����й��>�A
?̢Tb��ߌ\���OrC��|�R4Q��ᑩ7V�zA�?=���>��8�.3:�v�`�7��U�Dr��G��g�c08낯8+��9=��Ϟ+g'��R���H���@e,%�=:O�F��\��غ\7��}z�}���a�la�X�x�G�:���+~�+��
�xka��k�z�[J��iG_�eg��������Rԟ?|���k�Wٛ���ޒk�vM8ޮG�}���ਜ਼�T�/����X���L��%n�Z��r�/�.��x!
'�LC���f����r�J�_v~����.v
7����r��A����L�I�DG��\�N���?�ڵ���_�M��3|N��b�ё�H��z�����*�œ����q�V�)�qre���7+�c�S<T��������aT�L�W�v.�]񍰐bT;>\���'��?���^���~�G��>�������SM�Ow��V�ܧ���)�g�Տ~w�E>�~�l)�V���qS3��S5�I������	�̭�<t��O����NP�~��\�zS���^Όe���E)��>c��!Wk"���G��r���"?;�9���჎�v.H��F�W��V$��pƏq�G9���� ���{�'�!r��I�hd+W�Wo�B(����]��mc5��E�h.I.���F��"��<�#��t��\p�O8�!}iB���8�⡟`�_�=g���2�q��!��q�O��1�2����Տ��#u������[��hO�Z6nfFrW~�r8.���'�e�C�M��w��#|�-?rJ�Ӥ
�;���55�b����p1ƏF5��w�z¹����'Rf��S�m?�������*�r�bf�*ÏI���C�����T������He{�r�R{~|ʕ�`�:�c�@'��s;t�?���>��Kr)�2[�O�[-U�����ݦ���F���k+���+��]���t�d��=��e���~��]t�*�=~w�0��Oj6���eU�q�a���J�Rhl�����ao�<{���G(�wk8?���:�f�M����f���c��'䊎��?�
�G�<��+5ş�f�c�6��#�e?��w�\���G��v�]��*6���FG�C�'��K��O�ΰ����/n�%�%W�‡\�gD���\��σ���/�R���/�y�,[���;�t�K��y��D2t�+G'~�e�M+���}��8��T��#�G���~d;����gɛ��*�+$Ww�>�;>���<;���v�p����-���<>$W+n�ww3E'~��#(�[�k�Μt����N�/����qf_�Q�A�+��C���~�bg�l}Mbč��%G�4�q:Dn���5>!:��������+Sꏽ�!+���d�a��K9�l~��=oOvΜ"?M�^�Ȟ��O�����B4Ѽ�|�
g"Ǐ��m���a������\�O9��M��h4����c?�~�3�O��%W�[�0���C���g���O9:�5��~Љ�H/{o }�^�����I���_ʣ_��E.���aT�wT^��l���x>�3~����?��Ʌ�ׯ���4>��rS�ql�?��\�'�8s6_ο,�_����
���'���o�4�Ob(c��3:�st�k��a4G�Q����<�Z!8�3���4��8����Qs�mb�h�剟�(�����Ui�շ�&����WŁ�G�������O�ezm�����O�7��*y�U��oޤ�Ag�X�Z�s�Z�
~S���b.�ډ"�e�|������i�;~����h/0':���v�Ƕ+	{?عꇧvul�O
g,X(�.��#:��7hvw�l��4g�����]-�W�)�+O.w���^�&�/�Uy��yT�;J�+��?��^��F���
���Ct�O��r����9R�!��O/�ɏ4�'��S8{vyt�}��}��n0����
��Q���O{޾RYy�L/�!:{�f��K�U�M�u��gv�N�?zAt�_y�gWuwD�[gC�Z_e{T�c�8��������q8���Mn��I. �:6�Ԙ�؃�*�ɞ[���z��0�Տ=In�mo"~N^�g'���Z��.����܄�<� ���'D�Ӡ\����������"�h��8�?J=j��U�2�ٳ��r�l5߻@�T!0/!�=�W�k���B�ʾ>��})�xz�=�_^71���~t�؆R,��/�P�U�5�E�;!�Ogv~�K�`	��g��c~�Z�(,y�\p��K
;	:�-�)O.���\�>��Ty����7���HqA'���2��5A�p.�
���@.�e�����-g���,�qI��_è�3v�x��p���Z;'���/�����B���]���K�L!}_?����N����������
ɻ@`��x���C�k4~�1��ݦs�"0Yt1f�'�ȓ}v�Q�L~* ��_F_{��~_��΁��.S�-W*�q�3�V|�v��6�2�M��X&�N���`^n�V�:%~�-���+�Ƅ��Ga�/��-�����ܩ�7�&b�?a:9"��T��OC�ї�������ע���~��b�Z��gt�K5ѡ"�Eg_Ƴ�;Y��Pͪ��r��"Y�h~�to��$!���w��n{��w��o|��8�
)pfoY8�&Wɂ��@g���Ǫ�
��`9m�Q?��5�K(�-p�羻J��C_���~٘�\:���]� ΅������~�3t��?�ΫpsF�
�$:��rD�&ߏ\����5���N.?��������y�o��!"�^0=F�\\f�����U��@�p�$!/r�y8?�tp�n��3�*G��)����#����T�n�T�s��=Z������W��}�o�ݳre�G����kv�=�E'ܸ��s����+�<�L�������C���3V�a����G(�Շ����
RT|��0�>��/7�_�W�M_nz�_w��G��
����q�i�������~~w��T9�W�?^�g���$��7�l��n���xXӸ�C�|��%bGȐ�P�c�jڽ�]q������;$�^�,jCne��w_��>�ec�s��8A*�?�v�ݞ���=�;~:���e�R:v�
���3�ݽ
%|��u_~����')y��$ܒ�9y��ǫQn�!�#��}�ǝ�1����Ʀ'�/�ё���UO{��}6�H=ٹ)Tn��?�ӊ�W�ppؐ�x\���;tc�Qh�^����9m|P�`��6�l�5���ze����O�§�3|��oX��	r�����?��Նa�o�;Q��X��>��Wh/�1��҂�\���v�`�E�����A\���3�^�λ��}F�i�
\�t����,�CZ������|�FZJJ���D5*���&f<;��H��\̀\���n����=����L�����x
��g���靼�Cd�mN1?rD�Y�w��)�x�9b	?~�>]�ۙr���p(GD��>��2��[���� -�5�sH���z[��A�iF~�U9"��;�H��>�ѱ�/J�LT�%ܰy�b�����l�V�R.��3�xU�\�����O(�uǽ��žeG��s%��Oz����)�D?�D��_�t���¹]r��VW�jVI�����w�YOP�����w��2�on����4�����Re���K��������⇇q��Q��kr��i�yG���t.E{�M�����}�槳��]ڻ���+{�~^bG�g
��O�p�m�ao�W�k��G8S
��5:�K�f��?�coe�:�]Tɩ��hX��U��]�v	�%�֡���<�)�&~�kP��k��{?F����Ʃ���Z�3lwj�[���sP�]�§cZ�w��?{�qo������[^mt�Gy??]_�m�F�%�?�3��c�0�����)g���O~��K&Wcbt�-1��L�󄚩�:��>ř2iTF'���n�0��>�S�n9XXR߿��ásk�7^k��V'�vVp9[�2F���?XJ9�%���V���a�Ln�L�N9+�-� ��ݹh�@a+ uj�)�����~?~ڨ@���ԯ�^ f,c�������+��+z����iϦ�+:��8�J+�~�$����:L�r!�ޑ-�(����x	���}5�EG&��֘�y��Wq8}�����r��C���+�~���v�F����u�C:���T߆�1:懶�m����;.=d�X��B�m��6J�|��~�]�Ď�av(W�xh�\��:��~/�m;�����{{�4,�4R1�1O}rA<�:P?��燝��i˅��~�;6���o�#�o��������9�O�vn�j� _����ۙ~��s�ǰ�g�"~��t�v�j������-f��4��|��M���m��U+��m?��=�_@��{/�MJ������`@?:���^��<�\��>}���Z��~��/�3fۍ���;�e�h�Ȍ�qŜ���x�����~��mEfha��#{�V�L�(�S��ζ:�R(��L��n��������~��s/������g�A�>:���.�E97�=�z��7J��t�(�V.��㉫~R����糯��њ&��:��^�i�|���}���ho/�^0�Œ��C�������'~�?�h׀ܺ��jt蠬<���D�Q���I_��Sg��ߏ�Ɵ��~�9�
��ʂ3��?��V���p:�Sn��
8�%�Nq�WG���o/���j��=z����~��O�9:��o�^һ�sJv��߯��R7:���*�:��rh�p;	�z���7n�a��;��̳~�
?p�|��O���K��[�=�8���/��#���d9ǻ��E3�����~t|��3��QA����ކ��~�����ϔkn�<f�A���e�Dz���ࣜ]��2r!�]��=�3����c���S5t?�3㌟���U�S��q� nT�_���c���Q_��3r�}pv�����Q��:ݲ>�'?
���GMc?D�8ߑV|ȿ�k���x��a>z�'�<�v;9���O丨��������է>��9�/nLBO~�P��QZn|{������6zv�5����~���{���N���.��E�y�~�4�n���~ʝ
O�'��� ߼��W��lvr��<~憠�ֈx�]���������3t��Ok,�>��������g�b�\�{���6���k����g�TG�t�	�]<?�[8��v�k��r�/�!n���i{�8����K��:����o��r����Tn�3zo��r̿�,u��_�yc&�w��e~��aX��/�M_��X�K.���5�3��z	�ʵѐφ���\
gt���� ��d.c?X*B��MX-U_��k�f*�-ۖ6di��Zd崋���)ǥ�)�̑9�T�ær§��hq���__Y�h��љC�:')~O�ُ���岰f�`�����;�>�A=�r��<{���N��ry'�7�[m�\�N˰��8��K���'V�/�]�W�`��-�I�iٍ��WPJ_٭��sS}�)�a�5-Ə��AMv��ҟ98�}�Ƽ�Yoy�9X�;d}�>��	�+
����(jI��jF�s�$�9�O�Ev�Ŵ�\�����ޕ���~�
}�`�Ƅ,�4�ѐ�9��X'P&7.i�����L��ܹS�Y��[pw��6���#;,എ��S��&������=�d�&�
����|�D�g��Gy�k�����L��q�ʏB}�ɅԜ?W@�;�5�S��c�);D�v��~?�k�OO�
M�KA��*^o/�՟"�<�C����9��׀�Otb����rA��N@y���dϽ9&�§�o�u��E�W��֜�'f?���Z�2Sg�g�3�+|�D����:���̒\��T������)n�C^���;�}�ū��?Sg��C�|*�	
���J�N���HX�+��i�JL����wT��8#��O�.�nt�J�XS�g��hH~�.��h	΋��{��ᅟҬ�T9\S!��,~�\܃��m�A����t�J�R9�./��^���s�������+?��l�O�?s�TW�X�ј�����_��s	��%����C���!�^��)�ŠI���5�ώ��&�;|�DC�뎤��S��Ҁ��N�%�>�\��7�����8���-�f���I�!"��0�\>�;��A���Lzt�:/�H�?�.2+]�0��B:�Q�(ū�F�ڵ�8a�ɞsI|ٕo�.��K��׮�W�����3�vh\���
}�fn���/;(T^'�a���Q�u���JW�;���
�9�)��|*G�kQq{"[�T���O�5B�=��_���=$�F` l���r8�}A�*�K��+�9>�;ڋ�*|�Ђ��;7�;�P� 	���T�������97�2�����{֨����YC+����SW��. �5
!*'m�f�y9}Wn�ho����%�g-����C�,���uI-t�xԡls�6})�L����?t��c����?�hk[�ꟍ��)�3��yJGHܖΏ�/^m�*ʡ�faf��6�?�R3�Ed�Z
�Q"��E��}`�[!�;��M�c�o�97O��K�ϛV!>W�-5��lk��a����i8��(��+ǀM�b�,�g
�~T�4a)lw�K	�^6>�'��p��aT9V�7� p{����K���/���4O���XPy|�5h�b�)���꣥Q{�Ԇ���o��c��#9�X�iF�r�@�f�

����jV��r _ȕ���3c�bmC���ׯ���z�7�BH�����OD��]�3�V�!��=���N0ʛ���"���k�����M�<8;�D؛c�㳫�1ғ��'��9��ep��ќ���6Sy�^}�����}1����<Rɻ��}{?��u�wo�ǏB�]��IA��7�Գ�����2:Z����&���J!~Z�K�D��l:��?@H����0�6z�&�a+�g��"?`�I�B�Nn0�cn�D
QI_��	1�pf
��>Ӟ��qJ���h$~���14�D��ht�F�5)�s�{�6��<Γ��!t�>��M&;z�n�O3�#;תXj9U�F5Z�bF�sut��L#�Ʃ%�q
�&�"f��\�}솃���<��n�O�<#:���+R���Wb@]

�b쇸���<�+���Z�>|���]�9����5#�T��^J$}I
������}U���t�S�[o9��~jZo��l�4���A b�I}�e{�.�zzi����+���Nާ�s�K���ös��=�sre�5=�0|��G:ɘ�{f-�xo(�"�_
�p��������g���[�.Ԅg��b���~�C�����N��]�A[�,�	�=F�١�ⶏA��N}��[ڒ;�}9�����7:��Q�*{�HjbO��Q�iF�-�z�Jaz7�.�_���-��=H�ԪAC�P����!�[@g�jB��PL��������Œ:�O_��ыUC�
}�w
��G��Q���r���CMW8���5})�Uޤ�N}i=�`���n��0�%ԧ�u7��{M�D%~��T�q�ٸ��Q٦k{�qt��P��:�^f2���������A�߾�=o_���5��:N�j�ʱԸ�l��w�ٚr��M��(l#��{���$W9@���4�=}��Z�Y+�`����~:}�8���ğ�U�#���%|�7�	��O�q�^�Џ�_[h�Ϲ��~������/̋��&�~��9Q\
�^�W�㹽��ymT��)>ù7��?�7��N��O�O�k���$xpt�c�8����p�T�EoWQ�3+J����u�z�e�P�5㟐,�'���^c�j���2V�'���)�L<<�u�N��h����j<VЖ��7,�����"d?~�HkR����C������}��	��Ӌ&�&��@��f�Q<,>E}����
����|�ޕ�>�5��7�{|��9~?~fo��-�;{<|�~f��f���8!=�i�í=ş泞�����K1{Ƴ�?�����L.H�[I~A(M(q��|6{���\ɿ���%�ɥA?	=8��/�X#J�*C4|��O�¤���Vx}ŏ&F_�BDv�K���^�S�M��a���J;0����}����
��r?~|���ez�G�v�������ƽ��P�Kz�qTre?�Z��֋L�L�#��~��gF��al���U�3���Fe���ŕG�T��7���ͯ�4�M��~��OTf'�C����F��}��Y`k���U����S²C
��l��r8����~r���'�w�u�R���p+j)��JX?	���'��^�_]�nm�E��\��p���_W{g���?>�;,�f�j2O�Q�^U�c��~Y!{�]?�+�J_��"i�W݀����C���2�����Q��o����/�RX����졻�-�Ͻ���J�L"|����@�����O��O^��(d$�o"+�86�-��>�IG��%����N ��U�Ղ��I֌+S~��h��_��}�y�w�u�wi��\���+��\<9�
M>wk<�7T���u>ۼ>y��_�c
J.�Y4�L.��^T�Uq�3�TX��sw�w�z�g�?�,����wE�wx�O���
�Xy9���z��Ӻ�zf�؉�ɫ��w����ޥ	O�}�^�Pp�j��߻��y_��UظW8Y�7��iU\B뢃?��:��������:Ɍ�<����m�ş���?�;�I8�j��';Ԅ��]���R�������jzәu��x���/��Փ�wf1*ϺJ��ú��ǻ⹤������k�w�ł����l�w;_!�a��.�(�u�
Ɏmm	�;M:(}�����o~T�@M���p�֑"<'�B
T+Ǐ�J!>���N�$�f��R����P;��‘ڌ�A;�3��9R�%���P����#iI��	�?��V㾓´?w��:Vd�V.�HD�ܠK��ef?���,��+?ʅ�*���F"�1�r~�K���\ԹM��7�A'%�J�������^v��h	K�$���8��<7�|��W�U�;�W?��(t�&H���{A�+"����e���c��h���P�?$}��*՗^�D-��s�����[�a�~�#ʍ{�G���,������R�.���[ijC��o�.�-�h�����W�	��@/��O*���N�������� �^D����R���"�s����N�;����B��#�no���>���¬�T.��9�bQHU�U� �6�9j�DJ�G����(g
�J���h�a!�z�(��X��;�f
Ω#���ý��ӡ}h0-g��%�^�)�#�Wʩ�tb?i�*}G�<��i���ON笗�Z�(/��d�k�xF�#�����ݧx+�7�8����+�+g�W~���%�~Z�P�����Dn��c�<>g]}�9ɫ�wK�0ٞ�N�蝷 ]� �%0�p�
�>q6?7=�0ؘ}����=��^�9|1��%oq >�r\H����t�	
%��~�i%;,����n�~�<��+�>Bshģ�v���Vt&�a��?}�B�ǜ�'��;�U�;�"�6��'C��w��zK�{_�-���6{�
�	�y�2e�f�N���ET��k�8–܊W�D�/���**���+
�@o��v�cr4�ٙ��͍���{sJ���6UL2���G3n��%��w�Vq�Y�!��y�+eq�m�8�&g�y1/x(O^��M���&o����,ΆX���Y�ޮ��d�
�~��Q�2z���ˀ�!1��Ct�8�kv��I.�S�������W;����y�5��!m1�"t���x0��ӷ2j�}Z>��W��i��2�,fԩݘ����(͟��� eI��+	���QMvCc�u��8X�F��5@��V6��œo}�y��^>3&F��dbڬ�KY�@f�<o&ys�FQ����n:k(��5�n�"��!��M5J.H-X��l<�/��<~O��Y�>:�@!;��y�D��#Rq�B�bzE�`7��xw��5�������F����9
�Ԉc����#'o�C�"^�h]��
�(W	���F7��p��Ux"���O��+����r_��N�}�?"������S��r-�R/�⸙�*���"�"om�D>[(�=�C5��W~J-����)�с32�Ǝ���)�5�	��~hVY�o���Lȟ�d��3{��s�eTת
B.?�3��o��vc�����mGPju�����N�p���
;��/��_�:Iw��;Cyk��2��l
F*7Я/l�u*�{G���w(���2:�W���»Ny:�o��ș�_׮�jIa�}����:��z��({�	gO{i>��Q-:�Ce"&}�Z�h�i=��;�߱�<:�������Xg��Wg��{9�blT�H��ߚ���Ә,���ތ8|�&c`*�Ӡ5�W�����O���%�'�q������ƿ_D�r��7�}�/zL���PLB�����"��ɮ���O��M��g_��݈��4�G��4⃸e��rF�7�����N�j�D��l��X��^�����&�+��AD�&f�~��Uoq�ك�2	*�z��y�3k��75swRDf?�)�Yʤ	��H���X"/����3��d�ׄ ��8�D�����F�i�/�C!�h��g7c�����g��t�3񰤘y�%X��`�?9u�a왩D�h@T�yT-�">��֔��5�����R��I�3nR䩳KA�`a;�Մ�-�L����9{��Z�z�,���)V�E��R>r���{������SJ��֙�A���P���̤�9���!c��Z��>�>:�oֈgo;~�'u���[����M>���i!#��&LzG�[B�s����l"�ӑ`&�b����_�)�N����t�򸮡��
�N�J^���2�n�{�";�^�G�}V��~���ͳph�K�*�E��Z��ֺ��#��fz�L���3C=���^4i�w��(��\ڵz�RR����ZN!����W~�N�d?:~�=w ��*��n�1n0+*�o؟rg���[u�q�`r�/�v���g?�w����r���x��mH��m����'4�i"{�{����fgrſ�+4�o/��w��>���~�y%�=���1��_s@_җr�&��%fw��>8�ȮCv��Cf8�;d?�(~�SJ~A�(�)�2���ğr�C�C`�J����[�̐���=G~4�+?�|�?
�S�d;_a�	Ͳi{�
�Pp���j��}�Ogï����t^A۬��!::���*lxS��LnM�]�d]9�q<Vy�L�^�o�f+ʹk�5�Yp��9��i0|J�R2�d��\�5��xL��e��w9�W[�K��g����3Մ|�k��T��V�i��Կ΍��'$����f���6}�w̗s�3��p���5`f-�=㤨������G�;t�C���;k�����a��O��3š9��y����D��w��=�f<?�Lv��OC�����Bd�	��k7�����7�ܙw��0C=�'����<�7����gxΠ"{�O������ɞk7����K��<n��ϛ��'��Nn���+HV-����̬�hqi�{��t��x"�f~ڿc?9��Ո��l��[;���6~AJ���i��v�S���(W��tR�ܑc71�љ���;��$��+=g
���Cg��(É�co3���3��;>�'�s�TЋ��n����r��%�J���^V���Ѳ�9�ͣ�vB�闸��\ة���ӷ.7�Oh����d����^��M��C��'s[k��||�����,�3�·V�p������tO&�����\�-�%8	�:(�m�/&ۍE �_���v���+{`��oO��W�oo�`�)7�#Tno�sѱ�\ō�u�ٜ��륹x�t��t*�s���� _����W��&}��w7J.�q���t��t��MXzJv��RJ9U���1�F�*�|�I $��W�$Vn������'�5�=w}��N�`���_��S���	C)]�a��u�A9:�S���/q��(U�G��p�{��T���Lj����č�YN�m�2�:����'�j!�(��oB���,�*�g����;��>T?�M��'d=��?E�9��M'_�.*��EK�/����a�Jo�}��A�8�t�j��
����"��pv�g#��A�9~|��§�_�Q]�ع���S�(B����ƥ�9�j�s4���Q�T���%��?��V�G�T���R�|QCp�����2"��^�r|���.��ү��o3���83q%^t���w���9CH�F��q8n�-�Re煣n��S.`eW�:�U:
wD�|��9M���y<�g?���,T�5�7zg䴣�n��Xbœ�K(ߌ��m<�Po4�H�� ���X���x,��5���GbQ���Š]��׻Q��V;Gw�gn�v�a��{�!���,e+���P��CwT����Z�Fpd�m��*��T���o�>~��%p-Ρ�s�Et:������l��2�i4<K�)���ݐ�!���,��ijPgk':�?�#_�g����Zu�:��$�[��|�>�H�9�7���-���vZ���&�ܗ��p~)wb�L/X^���nC�\��׽���|=�f}zC�o6���-�����O8��j�[)բ���ۂ��hvٲ��^��z��c}S�BF�0X�n��*��
|�4-�v;��TGB��W�i�c��$o�]���9%�prk���X������W1P�^R?+�@g�܋�"�-���sk�h�-y�Ml0� ��c,lr�Ӛ���6�
��<lH�W��z�G�U�y��X��hK�ju[�ݯ~f��C޻��vZ�1��^�y@?�5���/��w�8�P^b�9���״8ᄎ�w�t3�������*7��ww�_9:�OL
�+���[関���+���t~���D�NZ����~~|��-'C�Cb��(O?�o���Y�����}���B�`�����5w����Gmb�^:25�﮲�������Zg��@��[e��q�؟�
��`�ï�p~5Tcn]�'��:
Ƨ~א��5VP8�&�LF=��|��_��斾�~���ŏO]�J�*qD25��uKW�.�U1�tUF����$���%6Ѯ\����sHS�e�z����R|��_���Jf�b��XCbӾ��bLC�NA3��*~��`�÷�QNᏮM�O}��i��m����m찐}��ŀ;>0�C}T��ئs��owZ���w�����;�MK��#?^�W޼TޓA��6~��6����������`�S�����;hN١87r�vr]�ك��1?�R{��Et�]?�CX��[aR�x�u������L�T(U��W9~��[ߏ,��v-#y.�����Or���6��g���W/��,��Z�k)�>):��J��'��{re�᧼#�N�����],�(?B����z\=!���ar�� ��5M���vOb�g'<W!A�D�)��\CO��g�񃠋<���tg��8�'�\�~A�f��N��z�K��C񐘰ê��甾���������a���=���G�3�`m����C.:�I����{�4'I���1/;~��/v>��N��ً\�A�}��"�7���`��(�/��5�T|�t��N�Ug�w�e��{n�J�q�=�脰
I
��@:��&��U5Y*{k���I�3���9?�É�k�!�P����Mv�]9(F5Xm��.|���}5?a?��,�j�O8w�w�>o�Ɯ	�f*X)^�&�ϔ��������3?*�縢����Q_���=���Os�L=�|5z/^�5ˏ����-��4�ġ��C�e����#"�Ct�n�_E�Ě�(uG�,У���!��~�c�_��l4}��Yx�ėI��w��
��Qm����:����$��C��s&?������l�%��+�����LB�F�كD
Q�@����
��>�0��`n+����l��«�nPM�Щ�0�A?|����~��fT�Ot,��Ϣ>�Q�
)��,�`) �><A���NV�~�A�����gKC��e��kw>�ڕ�33d���-W�,��(xbI�9�_{o��J�m_#:�#�&~�o�G�5�E�쎿��)0�o�	|�����O���K�!�F�~$�'#QG��K�<����o�w��֎��W@9�����V����=W��I��%���&wF49G\��W�~^���ӓ�P�v2x�r���:p(·O�����\��V:��
>�{z?�ì�x��T�+W��"~��?bF��ѢC��# �Y¹l0!b���s�NkEf`X�'�ؕIW�_�C���b�W~��_�Rǖsn��˞�Vr�G™�S�S�Y�YZy��rmg��������v���_�=s���<n�CgoCa�������g<��g"��y���e��(��%B�j�8��N�H�����Ռ&Η 2r��?�]u�K+hG�k-�'ov>8Ͼ&��8�<��"�Q^י��ӊ��
��]���͸eΑBg�Z���?��,�E'�Q��x5����9aߌ��������
�Er畕�3��-�p�g{�	��Dg�i�J�c?q^|j�38[�6~Q��O�Tҿ�g¹�����g��kϓ�u�������|��E@Ux���5����k�_�s�+O��R�ɕއN�p��7�ƞ1���o���=��υ���f~����u�ʫPb}�ұ�'�3�Ǔ��'
6�6�u�y�'����7��G9�ƿf�1���!~�����<�3tfo�8�H��'r�M��5��y����ϛ���������p<㧸�\��!���\`
Ǯ{�@�a���H��6�V�):�^Ϯ�ߙBa��vK\t� %;��w��a��v�jq��T.���'/�2�V��}G�(G'|��?rY�M_*�������I���Ÿ���E��H�z'��>��~��xhL���:v#�ʿ�|��Ž���o߄{�o�4�g0p~}{���<�oa�rk~���oZ7%�ə��q�q�����\��۵����Ϸ��>�^��h71����WΌD������Ơ��ӿ(b�?��7`T�aL��� =��N�9�{�:��ſ7���n�k��S��CI��I�5yw9�����{hp��e��ӿ�y����Է�����=}!��X��?X����|�����P(7h�.l;D?;�[ʭ!����=���6��}��yQ���_~|���}���L^��j��[�R1QmH1��>?�~�ח�2뫭ϣO����������r�f�"|���w�����eE����[�����>ʯq��e�Ƿ��?	O弞��Wij�8�$5��i�g���q��O~�A�>�+?�'��9��/��u��{r�O�t�3�H�={fp�)W@0����*����y��ۍT��Xm��O�v��g��#�8o��������*����f���<�r|ju�]+g`��땿�,}�_.Z�LnP�n�^9�}U~�ؾ�w򁙿G�>,2c���GuOt�e�Y?�]O#��L��ӶxH/�S?�T�꼟��m��w��Ө�$���3��L/s�M�b�ay���T�L	��i	��/Ǐ��*��W�7��ȣ���[9��9���>5�ާ����^y���4��DG��ھ�:�`G�>�*#���6��
rk%)��o;z'~����]��HZT��4��~�5�a�������w�I�h�~��.]�9�]?�Mg��…��������Fur�����|�����q9y��-G�{�ѹ)g{��J�W��?�^�����_��
l_��o���C/�fG�\9+�P����6:p���ƫ	���|Z�B:Y4���<��qaո�#�f�Ę)Gg�s��뿴���5�&/:k�c�@.��3P�S��%�Y1-Gg���Xh�_"K7=��g�X�>Kp��\���f�(���N���ᕑ�
�z�o����sI�ߝ��"Zw�؃���4�.�Ղ���
bd�iu��Ǡmy ح
_���S����9�1+X�3�Q�w���v])�|J7K���$j���ٔ�4�i��9V��M��=C.p���(�7'i��T�H�뽁���G��9��/7��[7T-�Ƴ疏���	�*�[aeDq([Ŷ�y�-�2:t?�]C��x��x��?<���T��Mk��g0w�y�'p��5�v�I{Ƃ�\!�A)7L1c���3���Գ3?x�r����O�!nЙ�m�'Stp���*��M	;��/+�N}ltk�'~,fl|P_����ڍ����M�e�n:/�q�Em
��P׍��h�s�_kׯ��H/ZY�eV��E?h��u��{J��!+.�e,�>0����)�s�m��=�gͅ���3��ra�^�~��(��Ǐ�mo�^�]_�ڄr��A�������@���Z���W^k���X^�E�O��������^oD%^�K�����;��W�W�;,K7�w=`��}ç��ù��b��ln���E̬��~lp�=;ۭ�Pt�L_☯�t���BR?��~�{m$��S�:xƧv{�&AZC��d��"s��iu�a/R̚��c���J�nͷ����W���
Y)}�j�2M'T����	�GJ�����8�r�|O#6����>:����ЉϧmϽB���W١�V�fkv訏Cz���U���.œW��Pr�ZUru`~��c޿�\J��B��Я�+�E�}�8<��O����T�x8���-�ktp~��n�Ռx�LnS�3k�ǵ_���Omf?��=	̜gP��rjk�!�k��3���ԣ��WO���8‚��
㬑a���s��ٗsäŇ�6
�����w��O�Ç���/��5�XM��s|�h?�Eq;<ۃ�4����/��G���=!ZlHމ?���L���p3�:;�Y���S|�͈���rS���f={G�+r�&߼=��c����I�c?����}v(��2���P
��j�$�k���ؐ�'c?�#N��Sy��+[g�#Tt�:��R8wqFM��N��U~1g�7pOR��꿑��1�2�����S�]���)S��$O_��ٹPH��{�������?��τ
��|b2���PX�F��V-l����q��Ⱥ����~����=�r���[��k>�Y���Z�Ƿ���'K�2���!��FE�b���O�~Ƽ��?��Õ�6���*g":������;�/�+�?~l݃?
?����58��m��ʕ�^�z����Ñ�4���6,���/.�^8���m�w��n����R��K~?𑾀����R�Oh�Z����h����r���������GtvV�e�*An�C��A�I_r���9���?��O���VF���}�J{~H�l~хJF��A���
����������F'��U��~4�]S��Í�ƞ+��'�Z>���=:
���H+�G-�X�(�[S���ޣ��y��&��z�y�O��jfZ��+R�ꘛ�$��x~:��<��C~����8��3:�G���_�������_�Z��~ԏ�C��.;�8�ſ�+������u�n#]וhJV�ص�֪�{��y3?��I��QY�T$2@ F@�9;�*;,��ܵ��l~?��7?o�j�K�M��3�:��y���x	��vv���e�؝���_IS��WPv��W5�=��A��BQ~q���yq��i߁�� f��y����$�O��/Y���Uɞ=���������ʞ��Xٌ�D��_F��c�u�	��9/������g�k�u��QK;�>A�iLfϟ���~2�s��Lt�sr^ݻ�����~����||m_�`����¾�o�,mz��#=��x�59�9%�e��>�uK|���R	�Yw08�5����G��S��y�t/�F��բ7�>��~�<9����3�&����g����̟l�0}O�������|6_��}-}m��R�{_��,PKm�y�(��?����ɡ���p��b�t�<f���p�ھdwX'纘��{�;:7��|z_1�ѳ�׳���W�:�?�ᙜ�39�g��7^��~zO���fOS���:x����)ת;�Ȏ�c8<���f�U�^��w��/�"� �A+�1]���:�-^w��~��Aѳ�Xw��|M>��^����$��.D-��f?7�����
a����7'�x��
��:�x58z��o�O�P4�^��~�#$�h�Ѳ"+���!���ձ�������{��J>�'��;H�	��
��^1��GG�"��^x�g�z����>
<rV,�Ʋ�Ĉ_�1�d	QX���<��Bhrf<��#�":C>z)���~N�B�+f�x:����/H�h3��F��|Ps^8"'f��}qiX���c��)o���`&y.�+���G*�Q�}Vj匇w�U����������Ť�{N�-���]����;�]���Ue<}����S�-�i]����}�-�G_�>SO_��tD��Λ�@'��??�r�|�����~�0�J>�ϊ��
L>���jCY���[��;���7r������b�h�+�f���ű��54�=�)�b����8-b�W�u�f�TʥqAk�k�1�w^y�/�i��m�|s��3��n *7e���+��]�j��;�Dɬ��_z'؊�����]�ڍi�Z眵�+{?����F��]��x�R7�����SJ��d��a���,	��Q�|�}v��?dB�π��u�O�lŘ����> �+��ξ�M)�ڽ%'=L��d��աIn&r�s��T�9y�0���&	�3�-��-`w�_ȕ�] KƳ�&a0�c���N��*g~x�r�{@.�6;�"H��'0�s�,��0sʁP��:����q��~�bx2��ِ�1<�3?�:Qq�{,y�oLs*��M9�g��s}��#>`ߞ�K��X�=�>��~�]Z�,wGc_��/"BXE���ja�zL9��&8ҝJ�mE��� ��XK��]�ԣ�����J�k"_��-�
m�C�:4�Y܆�+Z�wf��_n����$�~2x�)�_a�;Է�7�U���)�r�0uqq���0�~���.u~��/{�+�0�P�7CҚ���j߶�*e���~e
D=�Rz��:L�ى��ܵG幣Y��]�r8^Q��C�=�=7r�[�R���\��	�N ��}��&�0S�O�X�n��飇�[뻎�����5=�hXYU3 �1���M��-v��D_(����/��5ba���F���b�i��gߞ��c�=0��:�k�?h�]�J��]-N)��/k֦���v���4��|�կ�+<~�������gL3:�����=�'��j?�Θ�Y�y}5M>���[�L>A�W?��óX���L>��}E�w/x��޷Z�!�O<'=t�9or;����)�\����M�W��grNԵ�Oz�|���T.|-ּ�ȣ���ў���؏Ə�b�96.�����7?E��&�s\h��qJb�I1�_'��$�y��+��;����Ku�3z&���=8<����]�!j/v8�>�\��f��#�t4{.v����=���r?���_��g{潿8}�#=9s���<��i^�u�/�W�'���葯��v�Ӌ|j�+����/�ƗoG�)����g����U�ԬE��
��N�)��|��Ʃ�Q>���%ň'��ܙeH�x��q�O_�	(�z�yx���~.r�E5����v��c����]?ѳq��3�>��^���y9o<fW�d���j��3�1a��'}m\���~���~s��zק�p�=����M�ɧ�0��L���=��'����h����>�G�ah�&@r����|g'ѳ��>�09�Ab�Z&��e|���<���jHƗ?�W��?˅
����*ѹuf<��79�w%�W��?
?�#;�ȟ'g�H�L�\�=���<�錪Ivgl�@�)���|q����ă��O��x��=������9����E�>�>�~<ُ?��y6Y�L��ϟ6�����5�m�/�I��?����_���W��ox��e�!��S��y9<����m~����Ë|�s���<���sY�-�;�|~�2}~>��V��<&\rx��h�=����4�K���G��x����?�@dr�����g�sY�m}�qw�+�J���sSƹ~F�G�duPf?���G9o�M����=;S�=�����g��r6�?�<I�^������x���t�#���W��>�˸Xn��~�}�良������S�nx�y<�O_~�q�r��q��<���י�G]��YBxг8X����c�����w��|Y�_�|j�~���o�>%�G��g�$����s�O��Ɨ�O�/z��յz�'���g�>��?�W���'<���x�9.>����>x~�ȋ��7���o9_�R'u��|�1�=���Q>	�W��x�ƽ��x��|�09<������1��|��ƻ���8d�|*�]�K|�34����bϋG�u�>�W�����s���|���x��\�|j��?��:O��D�y���G�G�ي��':7��{��Ƴ��z_��o��2���s�";txs�?��o�����1n씳� �U��{w�_�:0J�H�w?�_��Z&�_���.����ˀ��_�C��s�XC�لG��Uڇ�|�v��*:O_]�$wǹxw��:�����Ӹ@�;���Kz��Q=���
��C����Pw�T��+z�'Q_�F���+��W�J�@��k9T�]��ow�q��X�l�\�����u
���	��r>�On���N�g>W?�a�Źn��I�����X�r��q|='��c����H�����C>wz���ę�.ww���Y$�;�Oz���,�1�~�'��>�S�u�����
<�<�j����S�s��a��u��FO::�y����L���[�}����v��]����C<;</��E��J_��ɖt�񋜇�Q���]�J>���-�b��rX�$z��IO|u��j���L)׻�����l<�y�x�+�6%]���W��?��g̽�NL>��u���7��'�r��,�z��U4�FU�*E��kSނ�����]�����^c�L<YJ��S�e��/ۡ�2=�|�A�S>��?�]-*9&FF�r�{���H��>)�S��Zz��׀U����.sKQ�B�Z�I����E������s�M��I_H7/)�.Na[���n`�z-��<j���<CkI���4�^d�b,���b� !Y6�{!%��MCf�;����[<~/e
&�z�Jg�D�n�g_���Ƹ�[��~�5DF!��ࡂ����N]�Έt3S>���rh��n��׷kz����>
�˫�F�����+���р])yre�U���^��)(GT��[���r����S:T򲿼�R�Ֆ��Ѐ����|��q!�^͑��4TD��R�2��k��|Vkr�,��
W�DuG���Eꐺ^���C/*'n��J2虇�c�]Jg)����mO�$�2-Yz]��
C�����DM�Ɖ�;N1��k�ų�/��n��=J�G�J�G��h�grDw���=��E�����o�W��W��g��;�����tOm���/o�i@К��8����$��B��-��{�"Wī�B�TyO�����1�'�|����ߺ��W�m�1��&��'�ȵ��ec�KUu�z7}��˴�/gu�qXD���.�;ar3�.����;��w��M޳)Y�營~���kɇ���i쌰�q%�����A���u�k\/Tn����Gu��>%#~��j��y����e�O�-,��e�M0���36���W��m@�[s�/b�'��H�3��
N�|���G_��������x���D�
_կ����A'z�/���&��V{u:�Y!���^؅u³�q2�{oƇ޻�j����]
 {�w��'a�S�.����+���\W����'{�I���_��BgN<�;Ă~�g�L���i��o��7��L���O�������E3H�uH�Q�$����gףnM�N��CDIxȇ��w��k�w�T��8���ٽ��+�Я���3RA��Cdw���Yt&Wlz��~�a�<�ɞ	���S>���-g_�!:��g�n����G�7
���c�j��;������}M�V�c�|�"M�o|����w���i:�b�[�36v�/�c�l�4	��������7��?��T���ߓ϶���/^��,_w�ƣy�2 ?y�whӗ�+#��?\��[��۸8�x��4����B�'�/N��6.�Sթ^�I����rbs����uq=���ÿ���io�'&_�C��/��$9d���O�[�{�r�p�T�K�;�j�rk.�Yl=���7��1[DO㔑�Z��x�[�� ��_�<��N�/gۍ��/@\����?K�N>�s�*(�q�k�3z��mPCDz�9�'{ߺ�[���{q�7���A�Q�F��7���?��j�pK':��7K�/􈖨�"j��c�����O��������T�
�c�ڪ	S�Wg"��|��Y`w)=�����A�o������-�[���G�|�#���M���뛯��F�~����?Z�����4=���R�Î'{�~[W?������a'���0=nº��{�~�}�믿��~����=��"��}�Տ?}�������r���s��w�R�l	��3sF�O��Z*��և��<���޵?�'<��{���B�����C�a�z=�����.��/<��<8�0%4����S�0g��gr`[�M>>���B>�aZJO5&�L��]�?�9��3�L�ү����������P,F�uz�f؝�ޝJ�fo�_Q=��������iO���_G�t����`�1�_�u�=QI����y�
8$�c�>_�?����\毭7����9J��Tm����s�kV�wv�� c`����o���
R����-*.�xΜc�>ҳyy91��Y��9�,����{z��/v��M�������/,\��f��Xh���ꠜw���?G��|덏�g�ښm�i>�u�_�?����[��Sn��q�r�K�{.����j��Q����y�s����r�0{���̢�rV�h=�;�'�J˜���/��rS���7N�gtn��8������s�M���Ų�79�l�s�׶__�����γ�f𝠯�x-����fƋ�$������t�b�d��β�z���K\�~r��N辶��].��g�~�ܔ4�鶿^�猓�~l����kvd���Ov�0{N_�G��K�BD��H�A�/.q�'H��rī;<��͞�?�=�����x��qj���~�����~��3`u/f�s���@����<Ϫ���n^���M�rz���/�ٝw?�vЃ��?_�����=P����)z�����P�J�� ׅ���ӗ~�y��9�9?�Y��.d�����N��
C�N�oq����z�KT�.ǂU��|���)z>����N84X+W��w�"��8�pz�8�:Y)��z�y?��"��6��1d�:]=cXl�6r�ò��.%F@�t�+}��җ��.!��^���}��c��ݐa�YNL����E��3�� �=�K�=_�9��3;d����%7�yf�и\rF�CEtV5Dx��^�zEn?uZMǗ��79g?�K�;��>�����t
~owQ�t���u�r%�֩;�D�\��h�/�Z�0�$�d��vG<<�?�}'��4���i��R6��[�S�FC�<#w�[c)��Z(pv"���/H5(2��o���Z��(��5����÷z?��mw[GU"�y��s�ا����5_�Q�]3z0�&�7�$�{�������o���h�����s����4����c�7�;�>s,�jF������u&�r)����Ϟr����n���0}���"�sU�h=<���1J>�x������ҞC}�~��OK�$���!�h�hOh������%�2~"B��|a��*��ԯ�v�=�9�:�s�}��O�iN<�/8��
��'+f-��W�>�_�B�$>�S1��eUof�{�;=Աܝ����ԣ'?@8�T�W�1�Q�+����TS.[�����|�'���-<���p_N>��aT]�^��h�Z���Ǫ��n������v��ѣUTʅp�g��~��ن�ݦ]�	�=\N��9�Ծx�~}��b�x;�J��Ts��p҃٥�-w�	$�W���[��]�
z�Wt��r8���6�X��\��r�Xh� ��/U�ֹ���gH�B�Mຎ/�T�)�ܤz7Q���Pn$�[Y�W�b-���rY�S~]k�%�Ѹ;���B��$m�=� �0hO�r�sr��"�Ï�3��;�����'~�ñ��̣�C��i��^����kIFo}�_&�袵��n�b��_f�_��>��1��s�tu���wo_��
�3qRF����ZA��׆طΎ��
��1�m����_�.�yk�jǘ{k4���n6V�}�-k�պ�޷�7���d̽2�5�UIKR0�����#�����"�ő�����h�t;c��Gw~�_�ݙ
�C�t�m��v����|T�qȯ��Wz���#����-<_.��2u�K}�k�e�-�d��{a����p��xߙ��c9	.�R�`@o��������-ϫ�-�E��JH�2kͩ�;��n�D�?�u��վ�׳g82O����n,>�ҹ��~O�=���l��o���M��^��1����ߪ)j�	�Jio�pﵡ��
=�i�t��=�	=7{�E}��`֧	��A_���鿰�b�z���!L��ۭ���L�o:���hW�_���TwZ�8Ϳ0	ڪ&��)��i�f�葋�_��U�vۖ��wD��-���P����(^N�ܬn.4���}kͯ]�k�&�����ߏ/z����A���x���k�`�X��x�-	�a���l���L%��oi�څ������cG���F(���Wo�{�eQ�,�C>��x�~��<��W����%B��ֈ��޲B��ՠ�{��a'�&Z�#�J���H��.@�x�T��~
j���:��0Y{��_"��Žn�����C�h�j��(;�X�X��,���y{�ϴ�c:%��.w�<��8gk��r���@���ǻWb���/,�|���b�%��Yv���u$�#L9����:�?��?��'��.�?'�=��\�SY��1zH/�*;����� �I#�<������I{8ӻ��ݞ?<�����/zo��}���~pr ��vwzo\쬢�^��S���fv뇵_���^�go;M_�a����k\��
~���~]��&����I�ɧ�3.���HE�#�V�2!d�g��Y;:{>�E�W�Y���U����Ŏ�O�k��}⫘2�[il5�u�~��'����]oL�E0\�K�8��ғ�"\��_���?�,�^�v����,�x�Լ����.?*��/�������\4�ٖ;��6����m{�e�dBgWg����vv��˕�~���b�<:����0z�?���d��t�����~O�Z�xv:^/;���g5�=�=�D��Px�g`�+K=ň�[C)��C�a��'�qA_�{���,�3�˙���!�J���0�@ڤ?�����)�p�+y��0z�ʀ?���G�q~VX��;;o�^�M8���ǯ,49kL~�
��I�O�U���9�4�+]�?�<�c趎vD�]���/z�|�5�Y{�N�?9Cx�:��!��1N��$�Cz�@�?�#�z���M����_���Y�ˉ���g?��i�omi�Wn�����#�Xs�G���뿳g�<�#1e��׸�/��Z�		�|���?r��Mrf��|���O���/W�"&�rS����N_���<����R�4C��җ\!��<����yn�g����"�����΂��Ɨ�+�}��Ʃ��+�_�v��wz�}[>���ύ�$��7;oΞ��!@���@>�O/(�o�ñ�L���]W�(���ϳ�����sx�c�ІG�B�~��g�|r �4��+~/r�Q4���ƻ�+�Ҡ�m"���<�k�QO�( �%h�~�5�*L[�09d���P���M�1�E��y�W��聧\�~��&���o���_���[\7��dYN	H��Ί�J>����7!x��	y$�ˑ�g���Y}���޼_�z�l^�Y���;��T�sg�;9�w9s�b����ܲx��Y���e���\�4�.�������׸��_�e�|���y�x��4z��l���*xT�߹��'��yg޳��+z�Ὰ�5��	����髽g�4��/Vz��	m���u�9��{��1]�(��>%����<<g��G�l|��r��z��k�!ϩ��ywf=���k�;n�9zf?�8��	��ƱJX�|�E�y���it6pf-�ݡ9U�G�E>0��J5�/��K&���w�?M,;��1<�j.��S|]�pY�/w�~J_���=���<s�r�����B����kw�>��i	F��x_}����u����w����pr�~�|��D�,3z,;�/��\޵���@�ձ���q����8����|��n���}�yḻT��O�r_����8D������'��^��
��/.$J.�����ZSw�d?����,3�t��H\�rg��Dh]X�ߋ�t�}�8ߊ�{Ӽ�!��dV���ϻl� 'l_����7�j��[�s�'{FO�B9X��D�쯗�>�>��"Z�_���g@���~�w�9d!��m����^���w�u�$��G.����g��!�I�P�»/�z��mv�ؾ�~�W�������Ef�9A�ɞ��ܬ�2�<�����0�t]�l��q�l�J��B>�F�/�6��7>#��?��w�|K��vJ�{��{��G���Λ�k�G{x�ş>|�M�M��'/S��!kv.w���~�������.��~�����?��:g'�?��C���5n�n����_��rc፳��.��{��LA��W�F:�!Opڄ_��q�ިR�B����'���A��%�¯��ȇ��������_�r�ȿ������;��4Y,���`���6��%�~��}�'��G�h�L�z��~,�g��?���"�ƗOqK�"�"�妠1>�>��͏=���O��'��ǁ~�Syf?M���/����?ܭm��On��xJȠ/��\{r�z�e���������U-_�1��e$����W�t:���յ�03��Œ���c�x��-���� ����{�<��o��;�¾.�����@􄇙!iw�!5:!��S�N]��՞�!�=�n��t���tQ��ޞ�$x&z����'�<����N�1Q�n���>��o;{�[t:�"�����w\�8�IP�K��h������M�kX�N"L����� |ռ�\��qZ���5��4D��T~?��;F����Sxn��/��ߔ���K�ح�̿�x432��WI���|};�q��3��b��N���-!C����L�L�"�~`�ѵ��%��
U�K��F���}hIs��Lc� 5�r��W�IU>V>^u��=7�R8Pu3�'<]��F��S�;��ë6Ic��z�?��!I��\�r�(�J��ǐm�[�r��\hQ��/�����JX��
�ƈ�!m�#v2�Px��d
H>���7�ʉ)�ٙmv��_��/��;>���op����577�?Ɍ�}�@y^F]b��IȺ�)ʫ�����6F��L���HxH�4�;�x�ݏ?hG�W�\�R���R�&��Ycw.�aH:cfCP��{Ϸ��7��'s�!���3��&�5�s㠼���xR#�(�=I�]K�������K�N{Zi�mxp��^�Dw�k��|YE�׳�+@9��`~�MJ���?okhu`��oW�s�~nF0z��D��,�=u�Ï?���d\�c��7�x��l��xo4�9�����LS����PQ'��f�&O�c��_������Yut�_|���d���^��_KV��2����:�v�%���N���Mz�>-��Y?��~�m��ɇ�B�=F`g`Y%��UoP��~���WD���X��C��O��S|q|?�l�IJ��i��� >Bxco����Ҟ ����Ϗ?|xOK't���C7���d�խ�r^�~����D'w�_� ����fH�7���
��_�Dgr�-�vk͗/��g2�`��~��D
!�������G!�֔Ã�`���	ӣz,��a`�ͥ�FO��j3��Ծ3Ng�>�����*Ү�V�����
y�Tפ�K���\��i�T}@͆���<V�����W��/�r���z�HL>�9�g����W�Y̱��]ؿ�{�c�1z���d]�E_�X{��I�a
�ȹE�[��^+k���V��a
�0f{��?W�����h��i|EO9
re���O��*{�i�6�?8�'y����*ѧ���ˇ���s�C~&<ѓ�4$��C[��O9S:�h���{5����zv߼��/"��N�}�~R��}˹\N���/��t�Og��_7�n
?���~Xȏ��ׂ+aU��b��_Ck���O�����ګOS-Y��t<z/e����]�㢵�!�=�ZgE��h�0��������!'��C�yڨ����3���/��/<�|������)c��[�7j���?����?a�=�z�&���x�,�*��4��j~�=����%��z_!�|8���?����{�{T�����d�+5_�;�c������'���µ��sƦ���-�ӽ/O���W9�g��e��v�����K���lڥ�'z����{ѓ���,�?4ϒ�O��3<�aW_GX���D�i����]N��ݓ���;�d��q�LΙ�3x��q��{@��7�җm$�����
_�3�T_L\�{Y�o�����s�_®��� �Eȅ����=/_��_�#��E>�R�laN>��������Ս���K�hٕ��#ə���V�X�$��[�g7���"��~=��4E	%�P,A�K���L���u�h��}����G��#E<9$�=�+��냯R��9z�(-I���+
Oz��/���'�s�]�a�~�P�?�Ƕ�NҎ:�ʯ��^�'�N��� ���=�;ՐK��f�cw}}n�`A�;z6�!O�P����d?�I-�g)@��M��v�
��!`r�n���'<�U�D{��L�
�i��1>�,D���~?ׄ��C��������
_�gNJ���w"Z
���Y�Tz����^�9�Q��O��/��p|�IC�'��~�	��ּ۳��;��P!�)s�n���:��䖕u�;D����0�����Q���z�=s>��t���N�T�HT�k����[N�f��z�9��d?������)�gu�=ɧyy�K��.*[�x�;�A�ȋE�i?�:�ێ�հ��s��s}u�C8�_]7O�X����ؙt�S����z��0-vRV����7N=��6xw�u䙃O��1�<�{7.�/��ִ�*5�l���f��'}��ܦ֙g��֙���Zox�L�9�XK����	����g�,|w�אM_���Er���Qe�fڷ�Yݑ�_}�ѳ�řÔN�ɧ\�s=�w�_O�.nsޝ�x�r}<[�W���r�X�[�F�!8Q������/Ϲo�$9d?�3!O>���9�Ozλ���ub	�/�Nn��~J_�{}O0ቘ�n���Y�zu�z+��.������I�;6���+�|�3��m�G�l�l�'O
fϋ3L_���,b�?�k.w���:2���+s�O�|ʹwo�#&��积���;a��Ob��~xї
��,ֻR����r���^�ƅ�zQ�Om�l��_^_�']�'&`�e������m�rT������[��b����t�a��\���4�fW���/�Ͻ;}�
=2�h���V#�;P����
����?~�%�"}�Q��^��Ξ9eӾ`�g:C�ů?��Yl%m:��A���Seˬ���Gm\l~Y���/�ǯwng�����~%�:�Q=�9�m���O?�IԣG��:7�_�6����(Ed�픵�/~�����@�~��H��"1V������_�*T�e(�4(F����R�`���Lu��f��+�9�>��{��zɻ���,���]Ô��Ÿo����_�vr���׌��_���j|�ߪ���J��`c?�˅Z)\xl����3�ş�3�.�����<�+Pn��غ������|����sY��<�_������o\����
X�t�|��;�.N�J�eL_���v�?�$SG~� 8��e'������z����͖��T0��;�y9|�Ϟ�O�R��i<V���~(kj�w�XZ|E�0yz�"�����w�y�׃���B=��&W�Ya!R����*^Et�)+zX�P��td����{
�sk��=Pu�+)}�Fj�w��_���w;��p(���]�?�_���b��g��%�2�-!�:��#O>�%��%�U���/�8^{:s0��& �q�rRnyӽ�J�9+�W
��=t�-,[?��b,`ywT���9$�%4���M-�J�ޫ�H�m���X���X�A�tf��)��s��)�E�F��:��ߠ��}D�x��wW�z�����]>����{n���g1�%˙n����cF�ڨ��eͫwk��僫�|QP�+~Zr[n�rF�yzI�~�]�r��O~O��OM����UL����)7���$g@$�~9�7㩂��r�w�U�'R3O�AMf��q94�;,J`���.���$��L>��Ӟ�5��Y[�J���X���{�ئ�*~>����A�D~�[����RJ�f�U��jhJz��wE�_�ny29�z&���w��o|w�����ᛯ�~������������&�M�_~�3R������٬'���у��-F|S痦Ux|�٩��lU��S�{��Lbxn�)���k���%��ɇ=(4�%��֋"�@/^�˔�%�"=^���l"�c�Y�DP)��w8�i/�FJ�.��|V�w��|�%��&�Fբ,��߻�9��
=VG���H��k��[�ZD},&Kk�ˆ�t��bl��_�S��>�������&y�_���v/�-'�q�w[l��aP�z�7� X
�f�…у��"��{=�i��Z�r�p�N��"ثi��~Z�b���*w�������[C�Ǎ���=�;�C�+ϻX�j�"{�.���8˪���^�+�$���RC��������Q�b.�ϮWH��v����c�YQ��3x�L>;S<�L��n̩�'�����z�q�I�i$�z���<>_}�I��<�/����@�g�<;<��f21 |�~@g?�WC&:g�7.����Y��3;O_����$���n2���T�d���l-2{�����A>9�rڼ�_
��8��,<;/�[��Y��yv;z�9k8�o��r�j�1�����s�Oxtڲ��6�O<�#�/f:e%��;��\J1����bs��'�<}�o�/��$�7.��qO{�
vO��⍋b�ѓ��qQ,/�g��!<;�+ڏ�'�/����i�~�+/�2�I���É�~�)v�Z�
�S>��﩯��ƻ�N΋A�o��~�����6N��j�s��1��~�A��g��އ�f�~�ڌ����鳮���>�,v�H��Lk~��}�t��޿���'��iA��C�g��+<SV㥻'�p�����?�#�|v������|���u1�j�ڼ�Sw�nz�U�ѳ�������N[��?[�L���sOr;�W��'z6��gw�,��X[x@6�����ї�s��'5N��
�S�E_�
P;�Ҳ��&S9�'���?��s{��B쾒Ϲ����9����Wz�Z����X��{�$j��~�g,̏m�=���oD��S����N�^v�<���>�)19�H��a�����['��,d���+���3���q)iIZIg�)��m����ut�l�v&��/��z~J��O�y��:#��<Ս��ֵ��~:ƶ�/��C�x�C�k�}�;��n1���'b�ē�̞���?���W!�i��\n~��e^�È=�=��=΅'�h�;���޷�C���r~c�dl�o��	�|T>��~0QϞ�W�n��yΧ[o,�s~8���jx�u���s�_gՁ�=����@V���?Qx�'��᜜cg��O�w�ӁPD>�s�\��9��� ���:�����wv�}ܴ���i?�I��c3����	��s�pFҳk>�{�sgϾ�9g��=o��w�j.������9ދ��D�8
�O�s�S�B���<}E�9�a8�,'#��>�%x�ۑ�F����:�r������~�0���w���$I
�9��z_<����O�tS��H/�n�OΩc�٨Y�eNi~lr>��6.&��y���s��zu�}��Ob�[ϧ��s�N9�.n~�%�On�{ƑF��?�:���jy��S>��l���'����E�h�N_��8=�v����'R��C�q���u�%��-�%�:��<g<s���c}_�'oR8�-�����Ev�la��iԮ՘|Z/��
R��$��<����p���7��q_�?�����C����xZo,~�3ޛ^.�eG��7��8��	���%���k~~��3��	+�{v8{�|�b.����n�'��=��;��z~r����q�3>?z�WԾy�<Hh�N�:dϿ�n����*���,t6��ߜ?���1�w~�^W�i��!�Y�R.��~�	R���N��+yHIg�k�d�+��߿��g۷D
j�g8�r��W9:��-�_z?J����*)���I`�՝_[B.z�ݧjC����9MѾwôǗ�ׇ�\���む�3�yӖ@��A6�^��KT1����a��z��!�wY��
�5I��ަ�>��*�ÕF�F�ȳ���'q~q�ըr����w�ӎ��#�--̞�q���Eϒ��9�A6��s���ͧ��ϕ��L�~Ǚ�%`��F�~)M���1���oOq��<ӗ/�s���i�3.����>?6
����w6���>z�7&��~xv'cC���Sy�m�'�ŗ�%�������\f�k[��)��g�/��r��G4�c�AΞ��#���HQ����y\go�x
��A��T��L*���>���[�~�����=��v��4�����rn��k��/M��]�>����l�<����39�~����P���v}� ��}��	��傜��WZ�[N�ߒ�u$��@a��Yn���WVF[���u�/VJ��j܈L�4`��Y���G4���0VY*x޴o�ɠ]b���eX�.MT�kpI���+�\��F Wm�O�pk�g�B��V
�c�����x�r��Q����ys�b&-F�1�W�|��߼���g���Q>�a ��ν���P{�K��\2���1�<啨}��<)��6�
mT�G5�j�)ː���TM�c��_b�o����^��?߃�Io�'�V���f����Y��'�E�н�����'R���	��U���/���kZ�
9:~`��y��	�:(��ilKuS�O��|��/��
���ˀ�t���x�P���iJ�݇�����zXg���'[���@����	06`ѳU��+=".��U0����u��H޽}m��x(�����@��qs�k���$��o���v�cg�V��%�S����3�������R��5�N�a����?�X��ԭ��=×_�h���*?���K[�WZF{����-R
�S�E/��r�w�-7\�[2q^�԰&��+A��)�X)���,Q��8_�$]T�w���ƄX��	�I���Cl�שI�r凌���R���j�˚c�~���P2����<�yOB�:�xt#	�%į�����h�:QOu���u� �����ݞ����Z<k�fe��S�]�U=!ε5�&=�!z�sk#�bz��m�=G/��Zy�Gٹ�؏z���ɓ��<:��	�6z�Y���<�*��5/A�:��_O�I�T�kt%(V��	xW&�U��A.y�jk���X.v�b�R��y��5�aWL�Y����j�o�#���u#*�B�"�=�O��'+uv/;?k/k�kzT2j���+�L>�����C��e�f'�%�{�79'��_�O��=�%}�;;rz�~}e'�\/��ω���0Bu���tz�6OS;��U�皬��8C��/<�P|У��1�W�o�/G�R;0�ȵ7�f|A�������ў��{��5�γF����5U�t���ȅՌ_F�z?����
��_����A�;s+Av�>��qnJ��6����"�s���3���oc�~�����?�Cb��Sn�����85(�cQ8x�˂���x�,M-^,)$[S.;d'J�5�i�?���O6�ή�{Z{?�������짽�b���]�gEk�Y.�gNl�)0����Hn�i��m���wHm�����ɟC\�4{N�ڷ'<c�Ŕ��@�l��3{cW�0�F�&�W���9�~�|�>�Y̷
�b���=g�����Jf$jZx�����S�T�,g3�~*�³3�v� {?��}���~^�3�}��e���ꯜ3�Uw⬄�U
�Ua��&|����W_d��[���'�y-��~�֌�b�b"7��y�����+֔T�*s���#����)��=���_�z���V�ư�Ox�o����@�w�\�;1rb��oխ�ỳ=������,�+�=Dt�W��g`+y����O���e�!z|�U����Y�Br�lk-5����797��F�b!�I /�Su
�E��9+pt���g?�H�w{��n���<�W�8�o~u�)�gU�I�V�qJ��-Y%�3~�i촢�j�C/����}zD?z@6��7]�g�(٨ĜJj^�%"�����,�s���� �w����˜���M%�a���B���5H�]}�TF>�i��w�\c��ը���Qr�|�'�JO�q�����ܗ�z������t�_��,�;�%��/:�wl`��<ؓ�5ᅩHv��=�!��r�o��b|���B�|v��O�ק�a~�r/J�4�A�\�:e�L��
�ހ�=�{R�a�Ļ�j�W�f7�v暇��s��y�J�/?�q������r��c�W�%��=섿%Z�Տ�|��o�������|]/-{��]�y0~�,�49����ζ�E�k-��y9�ˍ[]�����Z�-�z�i����(Aܳ���jDҙ���`t&g��'G���
O�պ��s_p�
O>纥3��}��O��ѿ��i=��瑞�t���l-6!��֙�C��~���evҸ�}b�ٸ���Yѩ���熌�\ݩ�K�9_�ޙY�������e���,' �j9��p����$���r��G�	�{��KO��S>�:<���6���c���ٍ�O�l���n���=�������M�቞Kn�䳳��:���;�-!������rYf��̞�(�s)gN̙��
�b���2.vM-x3�T�x�וX��1���;^=�/��O>ze��}�$�X�9o�͓?�Vh��g�b>?�rNЃw&�p��/>ܧ���G�
���=�����.$ʮ�y?c��pZ��2���/�RG�q�[p*��\�*���G��T�F����Y
~ӗh���ka�O tvn�:%�������a��W��9��+�"�����w߫�An�#���z�i#V
��u �[���s��s�JNh{B�6�֛;��;<y?�)re������]��:1���aW~_����Rr^D�z��}�3z����&�Θ�dP'�����J\|}7Ax0k�D��v�u��P
�Z+�����J�]����	���ܑrk�s�PJ�2*(=rz�'x�PX$:���r�p<���kR�~���9��RC>��T�{��`��ơ��{*�_>گ+��M׽ɕ���x��<:u꣣����p�U"�F�`V�줒6�T�|��3���rk��8:߭���D��)*�ڧ�`/�@����#�ߢ��i=�Ūf����B�"E����k���v�`r&��[�@c�D�/�/H�Y�N_��z����Cs�4�Vͫ�����G��]Я\�Mǂ7�T j�H���/�szFS�!��Vu�p�����=k�@!�_,�y��!=V�fJ�����_-�G�.��<������k�!
�>I%Eų���A���V�܊�b��W�K���" "�Ap��}.��b���y�J
B����.�q���rYj�y���K��W�uuS�9
�T�j5��oΆ�~v�����[�Kʏt��H|�O8~����ތ���d�@��U�0��+g�|���f	
�S>4�s`���I�$�>ἆ�k�|V��<$K3f��>]�ˉ���=$T�dv��Jx\�׋�ߵS�#/*�R�r����䀞�@�x�ye'��ph�/���B'`�u��+�3�R@��Ѿb�p�V'��k�䬴�%�?�=�T2DZ�8�>O7|!����w���&ӹq����Kt�og�]N�_�w~�AV��'j_Yiu/������o�s���J����Y��4�R�~�mx���򆅜��=ky������J�C�_;l�/��k���I�2�͛���WN�6*��9�v�����Ŷ�Ҟp<���צ�g����|~��oqb@v-�";h(�jiq�+H1�V�I�3F���v]�!W��T��y�eos��8�-��x�w9B���ߐ���r;���6��N�	㹱a���h޷����~,�`\�#O���U_����қ�%���+t>M�_`�5yRp��@�u|����b��������Kb�Ϋ����_"�6m���";ı�[ޜ&'�%z�nz��#ק�!<�3z8Z�z�$��'P�hi}�|1�g�o~L���W<�ۙ؏�F����=��Jr
��o<9+��M<_˽h��GOy�P<��ܯ��f��m���8M>:f'Dm�q"��a%7{�!����K{�X�Z�_��ܷG>�F/��y��KW�_s@�x���-�+���p�+#��� �&�j*�	⋥��{�*���
A��I��.,��.R��;����Ϩ_�ߺG�l���u�v�^�)'����
y{r+�r�s�4���&�n>���-��[	[�*�7X�S|�%!:M�(��(�8<ѣ�M���)ob[n���U9s����j�b
=����e��']�Q������D�o�W,�q�SK��|�"O��5��SU��8�o�N4�7�+�7;�}�-��/KL_��_�/w�;q��\�$�1�����hiQ��{��ŀ��ܠbd��~��/�%� 	<}-牃`���O_�6���ۅD�:�b�7�=���n�%;�{ۧ�(ca%a��X��H���ُܝ�_tƗ~�ѩ��'��wSz�����w'�rA@nv;���a��7�����"2�)��������X�H�m�s��K�~��o���-���5������஻�D@&�3���Ne˙ D9.�?�$�~�94�mu�z�bw2�GO��o�D������ß��C��|_���`���e�x��Ǒg�[�^l��mS�}�ˊ��@�H�$}m\�ɝQ������_�2���ԗ�/�L�����j�/h�$V
�X�b��PG�,��8&�#fr��3�DOv��z�3y�l���r��?W�k���
�D?�sq�m�ue�cP|��Փ ����ޯ-'fz�_
\�P�eȱ��s-��w�y�<�6����+z l��V�.���BIZ��<;��b�2�U�l�}��D�U�B�r�:˯��"��@��ЉK�vh��@$�O��H���1���?�N�[���C�n�����g���mM$�������X���M�������X��x�zv�Kg�tW��X�	��i����_�"����|I�I_�B�"�~�`���Q]z�s&��~L�����έ�~�����Ń���ߢXD������'<�>��3��ן5QG��ڝ��vT��K��>9Q�_$�ѡ=�S;��]b<�צ/8��On�������U�����n!O��$�ޞi�pN�4����5&�r�7��,7x!a�ͧ�q7Y�ts�䓜WS��?y{�� �!����w|
�����Xn~L>��ލ��c����Z*g�ʷ��`/5N��i�V�W��w�������zC��y<kk��?v�Tܸ�'y�G�C7xi��	�O�˷_d'F�\�M:˱(qd��#���cN��.l�W󇽟}��x�������j�cz�7���z �'H�gg�gz�y�Lcg���6�dB���w6����v�O����V$���XR�W��~�����bf�W���DA�[�Ne뗨�\ꍋ�������k�v��z���dr;�s�gw�.�}un�D�:�['����L$m�r�;;l?����$�F���c=�����/�X����{ |�_l:z�G�ڛ�owV�q^궆gE˶�-�!�W�༛fV������,�i錓s�]�3�����Ug���d�Q;���UÊ����m���W�gz��&��;L/|-�:�s�ϙctYϏ�S>�{x|u����r�b��W�Ó�w������+!ӯN<����	�$��E=�C��ϳ��}*n=�\����q|͞GgTm�'Ϲ�����Uj�Ϯ���|V��|�yC�2�WJ�x����Kn�ǝ�ە��F��B��m
]ÞEN��t�Dw�twF�;��/K�:-�%l�JM>b5d_���o8؎_������h�P���3�2�D���)�7�t
`pI�
��l*�#��<
�����[�OqQ���D�q��Y�����_��>�u�%"�<�
�w�20�|�� ���S"��2x�tĬq�=�ea%:�� ����4�����_ެյ��u�x8�9�����=)��#TK>��=q@��H>��=�_��}\�uDČ�:͛����|
~ρ@��E�Bd�W�#�kE$�e�=�_���'{N_%@��{�!�߼=y�/��ᮥrA�H|����#������p��ꎔ�:{f'�v_|X�wA0q-r�p&��|��7S���?���������Ʋ\�i�;�H�ϝYg��������ܛ���8��ר�Π�����(�~�ׂ_D'8�Zo@^�=8Z���a��9��-��B?��J1ׂ|��HG:����~�7{D?�Dd���Ǵ�pp� 5�$Щ��o�8቞�:���$��]>�a?�Kw��/�k���I�A������o3��oX���=��o�'z��c|���5������=<tGbʘx[�G*�O��>��͏�'���Dۊ��ȓ�$O*�p�al�)x���8z���3!�}ؘ����P�	��7���G�Y��\1g�0�rk��[V��/�y��߼��ym�w����A�o�}����:���W�lQ�Y�;��3���9J�/��;/ӗ��'�'��@^�D]�1�����%�2���a�`��)-^{.���S�GHZ���S.x�hw��KS��IY-!/��>x�C1�.���;�`�N�~G;����;���h=�2��s�쳇��KJ��>�{7�x��)���;�~�Y~a�=���@Orp`<�!�[��=wb4��[��I-O�d�?|�vf�������[�>~x�$�/�*������~�t��O�Y%����H�‰NQ�KH��T�=<�Π�ܨ�C���������x���\�ȀG�����|�.�������	��Z��~�x������$�r�Sr#j���(ч<����Uz��iO8��~n�\��':LA^���+�
{e��H�������8��Z�rKA�R
�צ&��#��q�Y��m@�E����B�ׂ:�F�#}Z�b�i@b�֛֪��1�bpӉy�TR�Xct�
�2��i�9��*�n�n5��E�R��A�[΅sb����
�C�����z��_��&U<L�̋��"_���W�����X��"��IJSW|Q��53(
&�����#�R��*��t��w����V�)��8�/vp��G�7=�j�yB{�K��mi���4���&�q.HN��Όaӵ�����/����|�o�f�fi�3V���^!��8,�qp�������&�G��)	_
0���_��~�C�5[�W�*����p<�7:5�"���۫|��$�@��G#��?\����SnS9%��E���y�e����=�w7�,r�I�
�1eX|��}2B���Y�;��b�8hb"4_9�g	-v�S,��[0��U��@p����ul��o�'y~��+�����'z�[�U���U�;�~������h��-�~�A�D.o�ڒ�ib���{��%��{$��C_D]���P�߸`l>�:z�h/w�  �/�S��T+�=�4_����+�A`�9k��#�b�{?l1��UgT{λK�~��^zO�L�D
�e]�����z$�XG����91�8M>�K�}�Or�G�Gy���J�jmMt��X��OZ�.f�7�a%�f��D�;��s����{3g
C��Vc�N<ӻ�9��;2�����4��VK0�Emx�כ����2TЃH�s�ϯz��`[�i_��
��i\�C{*]
A��o�4.؉����/�goI�ա�}Z�߳��j�'���ը\L��� f�}��4.vw������4B2��?����wve\�B���/��׸�9��UQ���9?��d���3��a��t��=s�=��j�&���\4M�N_�Ώ��4���&X�Eg���/�w�s��\}�h-��a��L��/$�k/K��]����?�?s}&���w�w�Og3����?7Z�>z��\��o|�C�����-ғO�B\���)zV�:^��b����ʧ·�D�C���re�qZ�/�����������Sݠ�Ia8u
Uvh�%�7U���̭ѾJ��/�s��^ų���$�VS-�ã=$B��$^mv��W�f��Urj�r��s�)G��^.J����}�9�����ju�[Y�9��o��s��Ki��<�oϻ�EN��}�Ϗ�Kn
w~�-e�C�r�qjN��{ڳ�?<�g�1��
y�~ i�z�Sbmg[{��In���d��#��,�v�qv��G~q����r����6/$�գ�\Z���r��pd�k�g���;:�I��sS�y�)<�U��${C���{?�ʮ�[���IO��S΄�]�{����qڡ�_l�����rA�p���8�|��<��Ɨ'~
5�	_H���x��'��?�!Fs�7�Ǝ�-�uPD��]��N9��^�Ga�r��<#�O(�';L/+�p�F�n��N��E�|����{���:�~��3<�:rߟQ}�!k�/����~�9y��гqݾ��:9k����|��|��rF7���:�4=���`�O~���^�]�E�yip���u��|�w��^�s=��k��&�3�x��w��3�um�ֺ�cg޵��:|�;�[��]㳎��
~���:�_��~x����L��/_�D��~��s\�N_��>e鼭���Jrn������}w{페�U]���l��D�y�JF��}]��_[�ϋ|�w뱄�����3+Z�Ƅ������_�u:��]�-|9��y������6
.�Y�������o\l�Q<gZ���X�}�O;O���Fj�d����Nz�s�̿�O�8���W7�}�Hj�8�8X��=A��%���n��������n�����n|�@T����Wڤ�ƛb�ڟ�]���{��f�y�긠Rǻ�dE MH�ޑ�'����?:wG��	(�"{Oov�_���zV�P��=�1>����8�T�;yR� ap��5Z�Ƚ��?�X��؉���~v�A�g<J�A�]�|5�O>�%��Lil�Jc\ )<��4�P�/91�C�g�SM>���_(w���������n����E9Fl
��������x��D�ʙ��B��8�����g�唜~�!E����鞂�����˝��	�������ɟ�����H�%�����Ol���j�/�������O/�gvB��}�
��8��yG'�}Ջ��:'���z(xEk���՗b1��Y~%�J�����?�W�?\��J}���?����<�£Y�l1=؉�fR􈀝w
�cT)h�C���^]�!��G�L�*�t��}rF
�s@~r�o���嬇�wz�C�'�������̭���ic�ɹSKB(�'9�\���ᩂ����u���r�`��D��H�=[�3hY��"�g?��
��#�#>����<�ym>EO�E��B�u
?g����(��p�_���f9� �&Ĕ����~�/�����j�柅���)�"}��]���ٸ(w�BH�il���,���-����O��p�Cr�,�䐺���L�{��ѯ���WZ~�������h�g�
Ge�p����'H�_�y��L��֟��pNd$�dxl.�N`�f<�����²��Θ�կO������~:�t�(v=s�rߚ���+���`�~����-� �����i
w�k|m��K�E�:7Eb���}-O��Dc?��j1���l���G���>z����	Z>Cr8��SS~���r����O:˹	x>ԕy������ς���l�":m�)��s��\Y�w>���rtދ�a������������ڋ��Օ�oE�pN��T�ȝ;�p#��O��<���wO�^(4�9ݛGڴ�}�ʪCWݏ�W�����F)�D1��BTW)��W�GX���РS�d
]�
	��I�#�j�O�	l��I��~��K�yӘ�2��6xH C�?�h��{W4��^U�)��d���	�N�龮�0���b��՛�{�$zN����4��Då6i|�^�~|���!�wo�xЏ����><�A[�^����&�M�����~��b�9��
�g|�g�\ރ9�������Ck�ɭ�'�s�Z���W���+�^�gJ}��Kk�G<�(��y9����~��gLa9zW�/���Ǯ�K�`�L>�g|�{�H��!��J��v��ڟ���Qz�W��Ɨ�O1��]�eZ,�ߓ�y��������AC�^7�_�l���X�~r���E��?ν}v�W�V/'�9c�[+�N�ܠ�Q�_��E_�(_{������������=�~���I�z���e6��`�\�>z,��z�=�$zN}�xj?�G���'��r��y{��)����-�s�:}����	�L:��~!��5z<��"��<�u�5�̏E'&���<�=�6�x���="k�=�Зg��2?6<�0|�~��z�����y�����4�|�g��K�{b��5�������gl@�޷��8=�������[Ŷ�筗&\�s��������4z��V��2_�dt^���0���k�0}=���0}E���lur�^ѷ�г��~���X��5�D��?缜=�g���Sα��VϠq4<;|��9_�L%�.|�~>���{SU~1�o$.7�S�=�h���'�|���Z�@��'Q�Y��$=�=��s���q�)?�X��3�lrw|~>�Dg�_��p���x΃S���y'z��g�9��{�<���tֻzWz&��"��)z�}�o|=����S�N�3}%��œ�����_�C-?���u���q��E_�_o1}]�)���OJ�����W�_�gv2zb�~�����Og�_�����\����c���Ί�\
ʹo� w=����R��Sv�ˡ��{Z0�x>3���1����uݧ�Wz��z��/x��!9�<�]�������.��s���?|��~�'	������ܟ~j?�x�e�^����K�&<g�w��$�ŗzf?̓�/4�/����~}
�/:��rzf?�G-ny�g��ܿ_�;��|�Q}=�s�z�5�8zv6|���o����'�������wv�T����+>�s��3>V�k���8��3���Tiz�=j��|.����<�׍�r�>��x��|>���px�k��♗ܲ�xK_��y�JΓ�G�E�s��/v��{��qz�ę������g�?5�/�u�G��������GO�;S�N"��������Hi��2�}���:��|X��/(�Z�������+gB���c��n�~'��ɞ�uM����q��83�w��;��r��NI���:8�/Ez�<�(���i<�'�a|,���8�e|yF'<�8�ş�D��r���g���	!d��{���tW�Ox@�gq\�X���`��U
�||�s������-2&�<��=�q�y����Y���+N}-�f�g���Sx&��	���~~c�]�����]br��g��s�_���έ{�0�0�ubv����e	��:l\̞�q�I��󝓯m�67-Q���蜜k?���;��o��*����-W`�^��x��Hqx����Ð�z1��s���N��$F���?묀��љ
υ��gFr�?9L_�=���|.?� �+xx�9�HJ�Lk�b�'���� ��/,��W���a�!���e	C9%C�_��2�N�9��J��*��OjLH�bx������?���:��-O�"��w'T�B%hWf����ut���_=����/���%xtz�LP��{�'�䗀�P��И�߹�Om����p\M�o�_&@���B��ߡ��k�_Z���
��/�?K5ӏʹ�QQ��<~�e��(����}޿��p:���^��u��,�E�V:Ef�J7�k+��,�C:<��A�Xc����u7�}��pĭFRe��L��_�y+G�߾�|�l�;U��New|�ɹ�N�{f�Aτ��|�t6q�nc�p�A<��T�R�M�k�OI��ڳv��V�xpcf�Y,FPU�z�.�\�S�~df*�S�a�dqwE��-\���LD=�u���~�<�ȵz��<����~�¡�%�7`��U���A's�="��gz)�ϻ�
��,c�
�6�#CSʗƻr8}i*L�	/
��.u��;=�Znz�znC�P	��U}ڲ��$��ˎ$9�?�Rwc�&�Dнg1�`ʞ�_��A�>�r.o�z�b)�=�e|U2�/�C�j8��1�:��H6�ZV���.\�s@��
d���������m\ty���^�C���0~_�\^�=�vѣ���J�G$�i�~����"�z_���ȭ=r�M�����^D|�+�c����C{u�ܗ^�J_�x�*�Td�56,J��:g��������f���~�zóma�����G�9.�t'ī�����=�ބ>c�ƙ�J@�z�_!מ���f$�p��y)�Z�Z=k6|�g�ͷ���+&g��!�N�A�wl`��vȞw6�fru�~ѓ���č�$��r�@ß#��%�G�oa�����C����,�]�����N@�� ��U�؁"��=�*t߸>�<KKξ�V�W�	x��@$z�˳��%��<�����)zW&{�1�����c��,��h6<ٳM��O�Q�⷗�얷��3gq1��˟w�_�q��Ϩ�>V5�Y׺`�o^��.�.�j+W�_��x1��g-n�ŧ�YBz�����{�?>��S.�|���ν���W��J���L�r���������x�U�f?����� ����1��V�v�^T{H����2X�!����9�H���,\X��IO�+������8<~��^�:;\����GN*��oz5	��1(� ��W�������^{.gw`y&�b@ཾ�&���ƴ�L_�nY�C�?팯�|��o�P��F�K�~�^��.�^��L�&�ɓ_�"�`�1�d+��s؏^��b$ƅ���Sx��'!OJ��[����G�dq�Y�B~ȓ���v�|�{r~�.�=��,�?���
=U���
r�٤���_���~/R��_����%g��w�Rw��L�F��+��{�����?�-�l7���|�:<���ї�_��?�z��`�D�-���=���X�����C/�O�k�
x,g�[yr�h���	�G1����~�/�p�J�ӝJﰥ���T����W���=�h�u QTǥy���[ؓ[�
</^�B'z'�	y6�G���aǢ��q�G@�6��;��aN_�O>��q6z���')ioԀC5g�i��Ɨ�AG@�F�o�zQ��ُAD$�OԳ+�t�&0x��|�'{x��y���3�䉞�c��0Ҝ��=��O�����|$/q��<�?N/�M7y6O����b����~����̉�pV\ө��_�'�~����h=do���è#[D6��\֫�c|�$D��9�����^����֫c��:15��uAv��U@�"��TWwm���p�_�����N7��G��K��rg~�R��;�j6��?z2����s�Wg4�%wmzO>;������	�<����\�~�s攜q���]��ó}�r���{�s�;�@N�9�+,�Z���|���8�웲�:��x�w�n�s\Ln {�z����|���;	����G�r��3;����g	��5��y��|^>�rސ\��y
����9��<�s�s�q\�I�+�Şw��oY]����'�}%���4���E���ߓ������ow&:z4�s\x�s�u�ٝP�9����"��^D�?�U4:��E�|���M_�r�ʕ��s_�9�����z�E��w']wߠA_�������A�#�1�nvx?�@��	�cc�J��R{wǐ��S�؋�E�+����!�9�N>�{��$�zs���狀H����껊�cT��O_�m豈D��1}!�T�I��Y�<����^`!;����N��ؙ��?�*����6z����b�U�b���!���Gǜ�/D��i���@�}�/���2��DD�y�����|���S��|UB�)����]���Y�
�'�җg���hJ��!��O�<��~�z�?s��e�o�N����W�2w��ѩF��p�a���F~�e �I_�x����߿�g���P�Pn��t7����o%�KC��`�18�@*�r@~�+�	��	�ҍ/����o��C�	��n��JTr��Pr�:�����[w�W�<��*��L��țd?�F_>=�Bu!У��e��=��d�my~���F/�UƂ��k~�I�r�9g�-{�5&O@;N�Ѻ�fB��9~�x�|�s��}�G2O�p�G|��I_��~�s�cx�g�E*1��"z��.��DOsP�I��͏�>9w�t^^���]�]��W�r���9w��y�Cʯ�|3<�1�}~��RDf�����_�}ғ�Z~��b�L���|�|%����ٳ��ª�r����4z�!��wW���X�ﲣ����;Ϋss�e�C��`��}�;���?��s��2]΄� }�)�8�V��[�+��=�>K��<��oD6<��3Hpu����W��(��&OtB�?W�]�[�����{q�	>z�y��
W_�o讟.�ɳ�Ĉ���>
S@��t��1j�����ȝ
��"���~=��$��GN}����j�~��B]t,ٝG�$�ow�C�`	��n4�k�\��ڃC�$�n�������F�br����vIxFOI&ၦ��v{;�rk����z���W��lj���7�����8n\��ekq2�/ڬ\ir������I�NGF�A��w�֮�
�����?z4���U�ѷ ��6t�ڿ�R�\�!܍�ؾ|���G��3=ͅA��,)x�8����� Q <�O^j?��8�5� ���ނ��W�2��=x�C#u�==r+(�����O��bdj���~jG������Js��_�A��|$��.>Q#tֻc�����{it=�[�
A�|������#�䌈ٻ6��xh�	$0�8���FP~���B;���):y�PŤY$��$<2p����d��~������fԍ��nh�P��򸸼F�a�jNw=�B�G��~J�\�R���l��<�[ڇ�� �+��U�!�8�+z��X��+����Qպ�+�K�ڕ;�Ҹ��L夯uI �|:�Kr)���ǯޝ.�&tp�-:�:��?^���>{1SuU�ِr]s=�ѣ���\7zH�%�P�8�M�KS�W/j�W�̊~6'78�T�4"z.�����aR
�TAR|Vs{@$�ߍ9��-4�&T�C�1b�ѩYZ�>����[�0����B�LE����?�6���[�A3vO�9��}	L�Mߔ���2ji:A</O��.؄�	[��O�E}�љ]`�W��>ZA�zu��=>P*$���q'.�%� �vQ+�oi���g�''�p����гmH<g���vg�넒��S/<�����ܾ��y�~���3��G�=���1�3Tʏ%/�`��cQx0�N�6����Ϸ}*�W��
�Y���1���+�Ax�u{nz��X�W��|�s�L����ڄWtcֆ�{��@ѩr1���⳪k3UE�<���/8T�K��c&-'���� �8M<����==�;u����g��'��j#����M~�3�[�v������(��@>�O�޲��Pn\pM��]Г��PӠ���;zF�
{��G|��j�O�f��h��J����9����9���5�|��f�|�g��5�y��ߴ�'����r�����ƒ;�!����'|[9R-(��CY8�.������|�3��c��Z羋.}Vxןɵ*6��Y�
���vu��?8Rf��ͳ+q�j=�8k��R��n�
����rS��7�^_�Z6^l&Rga��#v߾}Cϯr�shF�#8z<�]7'��-��w��w�T����?�s��x��/?&w*@4��?�=	}��]~�g�v��ѓ>8��l�gr��吿��,�v��ѳg�~������f?V3�rP>�Ԡ�SW���q1�a*�����b۵GU���*��'�GB��|IB������Q`����W���`d�MV������ۛ����\.=%~��a�����i�bx�]���o�����nl?b���
��{�d+-r(&�k&��|�#�ɫUiLH^?��n?�g/��+�`�%/�Gr�#jW��o#w�4R�$/������y�#�r�~2�cuv��^e���^n��Il����n>�_��.w�
[�@�)��� �5�*��Y��O����S�V�2����
�/pr;ʒ���L��x�g�‡�KaC�[�#�'��?~f���C\X�|nG���_&���^'���p��ұw��rM�NU{?v�t$�>9m�|wɍ}A�<Tٗa��i��*S���,
��9],>���o��Wn\��cu���%��(�E��ʿП��e��������-�<��ZWJ^s7�����=���9i�?��
���xFQظ�zƯ&w�>��DI7/��aWu�������ϵk?�yoD�����O��q��Jᩥ�݋3^��?�6��1���}�|������n�霿���=��^�~�yA�t�̏���?1�9�qLl�qlr�g�v�^L=���(��l�sײ��=<�>7nJ�?���!<���2��x���G�'��u~��q�Ρ���=�޾�1���9;=�a��ԛ��g��^O|�3��]�S>}>G���{��}�i����DO�[�f���a6�L���\4�����r���Q���}�W��]����m�G���џ��h잛8x���/�B������'yE�����
sJD� ���5��'��3�w��s�ӟ\n9a�=e���L�=�3��p���k?�fr�&�%}���ƟϾ��Ix -4S�\������?+v�3pL!yy��B��lLw����WQ9B���X��t��)4� 1ڶ�߸�r�~�>����]�(��9O�n��Ow��G�V�=�Ԩ��8&�ӕ�F����b��Ԛ`���q�݁Oa�����/Po��X�|�c�ͰX�@����P����i(��_v���BR�EWohW�L�3�e.�u�18�2>��Ɵk9�.��5?"}��!���(_{?�����Sׅ������ (9�oo��
G�Ca��K���+?/@ ��Ժ�䘒��(셅oѠ����=��N���2��Zs��+x����9���_�.z���Y�³����u)�	z)�_���-�|}?ǟ�����5rcN��3}������֥�3���KQ:}Da u�pʏ	����St3�\(^\���
Q	Tƒ�Ӳ�~*o;"_9(�tU���������~ wqҲ���.@F;;���g�ϋ?(�GEo9"��C	��߄+�%��~_����&v�^ޣD:����]���6���G�'�(� �Lk}��2�R�L�CF��x�?��υv�K��&hU>�@���W^�’��;�b����3K�>��'�6P��9�M��Kb�_�P�ƇC�Bl@�En�υ��}�'��cXA��n2�Rơ�?���
���GU)��g�:���H���$�„F�;94�b�
ֿhBx�*jb�q��/�Wyx��1���Ӄ��4�/�{
���+_��x�Ň�32!�u�	��E.+nߣ�=��;h��@��r��8&O�2����\��x(?pD�g����)�Ɨ+�z�\|8��2a�O�|=�x�#�;w�.��
�@=��,�v7�s�}%e���χK��T�;��Z>���?��^�Ӫ��G@ڟ-'r�~;7l�v�W)wSNI#pt�Z��_9#]��o��ѩ]�H�gQ�h�ʹX���W��x-9*�~�]�O������~-����LND{���Or�oM��Ϣ�G����'�	�g��%<��Ƽ�?��O�0����˷��O�,+�d�Z<T���C╼�JZf�zY��X饑�P�qğ���Cǎ@���Ĝh���p�M3~�n��^?���'?T�Rܛ�Q!p?1B4���!;����4Ny�u�����\+���ݐ��C�ܬ�`����<
l �w���-U�
�]�0SEb��n/37ѽ��H�W�ڿ�'��g
)��H$q@���1L��tе�@c
5��Hx@�k��Lm4D�K̒l[��0(��5�[mL�;�B�rV�>�/�5T�=���~b0됚5�]��l�h ��<�#���U
���w;�� �ڜ}���h�qs��!56	��cVR�ɡ0@�t	��U�y�8ϠcN��n��e�Y\yd�1�S�Q�4�E�a����V���y�i�<��<�?��]�ǍU�-_=�=�(�%u3��X�Uos�Pu6�����.k�<zTA
�R��p�	���
��c�хW?���5c���c КfQ�r �9�K=��Cj�Κ��-�`�{1S��i�8oN���zп�1w����=b��6���~}��;�C�f�O��}K�K�ßܭp����Ƿ�
eL��m:n���1>�~�bTW�M$ѳ�P4�L�aŔn���{�+��Z-����~���s����~�%��������$��p/��(�Cg�Gߞr�3��R�E�� �����'f�֥bW��V@�oU
�y�w��~��5��|���Aj��@&��_
�wg �C�EsA10D����L�]��r�2k���UIUkok����_�3��<�Z[�Q���|�Mk�ŒG�jWkͪ^��gz��S��?��ɾ���a�*>_lv��n��VӒ�T���\�}x�,�����&yv��δ%<�Ɋ%�џ�<���O�0}֭Dτ����b���]�k�sWBƒEs�(���Zж�@+
oP6��bĮ?��O%��=�տ�����qKk����?tUoH���.<�0|�kW��~L1�(<g�{�� ��_v�=�]����#��Y���Xv����o妼;�����߷v%w�l���Sl,̳��X�=^cg��O��9xt�
;��L��ga0��d��rS�c�gD��}-��m�������^�j�i�o~����\��xk�������z�՛�ӫ�t0��^"))�k,<sO�.�c��2��`:ϣ}�%h�~Wl�̇����Ą�{K���F�mh?��Oh���GB�����{�Ck�0Ӭ4Sآ���.hBDŽh����v�)E��|�A겈��R��h������{�+��]�wr���t�D���3��͌��CE��h/�Q�I3�-*�7�O��;�������m�h�Y�Hʎ�ߠ���'G'>W���+
c�?��5}@U��]�v�f��X�y8�+߹A�_Ԩ�F&�רS��C1x@���̹Ds>S/>#
~̏�6��?T8��g�����>�O0�\aT-?��U�Q>�з�I?�9o	��W��J���%���c>dhVߚ�(���?�6��?�Ws�ʏ}aZz�iV�{9���M�X��#9��L��-�(�@��p���o�߂禦�P����rA�1��K�s���+嬖N����|���?����~��Ǜ��ɉQ5<�iQ�b��҅>{���<�2�5�)�q1����]��'�u���P��yU$�r��+sҥ�L�ˮ�$�3���zFR	�g�1��g����?���o'����%!���%w�q�o�^���q/!�a�4�L�{�B�9W&��+$�Q��J�
2��LM.6�CZ�����������8j�{c�#p�c�s����֮}����L΄W��v��3���g��۱�3���(g�{|x��3{$r&�w�(��3��8���|g7�d�H�s(����\�5�gr���';
O���󞓔&�{��'7e�}�<.�������>�y!3~n�ᙄ��9�1<�?��&���(�Qٯ�
�.�4m�+:ת�κ��Qf��c�{�g\�o�rϯv1|���`0�$��e�|�0<3��������P�W�<	b槵449m��%��d١���=y��8�ai�V�عur��C��G�	�ۜ���n�F( �L�	@nM=�`�(*���u��gK�%����3�J�1��ݪ�����0Ji�XixVj���/�E����S�o�N���qr[��Z��?-�4�Ӑ8��Cy����u���s ��(��)� ���%f���k��P0
���X�J^��c_�8��sJo�^�t�HZQ�:c)�;M�G���o̹D?�w��d0�'�L'��_�X3�C��Ԯ�M��o���I\��=!�JRb�wd��Cz�L�}�{�����*���{p2ʤ����3?�>��?�O�� �kU>
�g�/���}�Vy$̺+�!5<�{���4���A�;���|���{���g���+ju�ݽ�cx�>#)
���=DVn���
���M����H�ܚ�6=Po�[�I6����iE��x0�\�fx��iR���ꈟ����l��U���yf\}�w.��dx��,��J���W�]�}&�r;����[��U�1Q�
=�Y�̼�����πu(�s�h��L���E���?�?��4>O�c�?q��T5K�~6=���hx��r�Q6�~3�
Ӻ�X��m|�6�g�\ñ���Q,x��:UpV�c�Cz�_��[�%v���\T��F�PZ�$�[�,篾��t>��kÔ�k�m�n;�΅������V���ܚr+���3�9�%�q�f��!'p�<�����J��n��ϥ�h�8<�<�v����n�H�DO�?���n�"�p��fv堔b��B�z2���b�S���A�����z�0t�&��~j�R/xc��[br�r�
���C���x�{��
����Ȩ ��v��CD&�r8�[�@��e��HY:[�q��(����)�	3i�?,!zL��.��{��z�A�z����r�s�D>�A�
M�h����3:U�!Gxf�f�Ч������,V`r��'z�]^��>]4ٔ�C��s�C��snm�`',��ȫ*ؘW���7t���lٵ����Q�j��\�rn�\a���+�n �_���~5�zR1��<���&V�J]߃�p	�_膟��|��h�Y����{�w(om��ϲ2��t5M�𙔝[pmt���{��_'z���,�O��-��x�HM~~zX�ׯ
�O�(�9�9�]�>�k�����}�}��w���qx�<w�HE�����]d6w�V�p����5�Z��dAx�T�+ 6\�Y��)z�:�!Q�ӑ6qy���p��l�M���m�(p7��M�R&��PR��nP��"]��2�%_|�؉W
?=�k��&��
�
(�q��[�>�HV�
��J3r���)��u�t<��z
Ng<������=9>M3���R���!��H�0�n���J����f1�ڪ��5�P%�ԗ�B�+y�i�!�O����O�@��Gy���ȇo}������������~��s���#��<���ǧ��X�0J��;��^������ݡ��,���2;��*3��ϻ�H'�qď��O�*IXt����w���[ϫgw�����j�]0��y��N�Y37�� ����ˈ��@��׭]���P��_H��V����f������E�_�M`�@�J/W�0Y�^Y��e���̆^F"	��3��>��e2s�ϙ?�^�_x����]y���7�ߧ�e;�O��K����jY�����C������Uy���G���?�ءޓ����z`r'G�=su�Jif�O�^�z���1:[e�)f���*���u�Si
�QH��xlŸ��`�xck������&4�J��Y�J{'J1N�Aw�+M;���X���!�g�9L�D������J�n����_P�R�+�qb!WW�w�Siaw�ʣ���ˣ������M9��
~��\�E%У�&/�'��%��QM��-<r;����Y1iB9�dD3�~�	k�/L�K1��9�+�����Uo�������M���;Q�Zq�|�]�'!���Un!���~�(s�����TQ�pܼ9��6=�>5W|��x�)�����2�i�����
'��?)������<��.�X�H"���
�zw��=�;ϖ��+O�	O�5��t�hk�����
�v�q�~����L4x(sz�Yؠ?�Z��9d]Ư_��^�D����?��i���u
��P�3�Ƿ�_?3���EJٽK^���a}����d�T�e����J�C��MAϻK�
��3�
��'F�0[~L�P`5T���D�J)m��ȝ�B;��)�
O�D^�t�pA�chO���w.ugJi���iB��{�D������=�Wɫ�f}�dt狑-�pe�~�V���vzƕ
�c�nX��Q��qKG�N����N%�{�+s{}u���)�dv��҆&c��3��P��5V�}�z>��'/_$)a
��?r��W��)*�e����������u6����)��p!i�ܝJ�#SD��,مĝ����T��kr�!Eu����^����y������M�;7�\K�-���<>t��U��E��C��o���j� ?>�|�v��u�����7SN��[,�|�Y1"/k���.r�?����r��v7���CђWz8�)�������1
���ڽ��"�����FO�O��$��C�w���;�t��O����Y�=*���_��Gy����X�}z>���˝X9R��g
Pab���O�e�@%�'�٨۹��ʗ\�b��\"���V�σ
L�����TO�3��o������IR8?�B#G���-O~C��TZ�	�zɽ�u����H��Y��g�M�oU����)IB�C*v�	~~��r!D�x�织b��VT��џ�
���w��&�
v�>�gD�qȱ�����C��y<V�8w�z?
?���~��7��'������{D(ʷ�Gƌ�qB�"�L��Us�˯�q�v�\�jZ�;��m�N����\|����K`�)�{��I�[�����e���w"��3�1����~���?�'{�4���;�b�C�^��S@��2��F�L �Vi�N�>��\x4��Gʕ�⩱�Q��ew�ϻ�L�?�^�C���-Hc��ݾ�����J���]sR�u��+�_����X�#��}w�����_���s�F���r"����ݫ����u<j���F�|o�|y%�ow�-���~Y��;��m����bZ�����l���ƫ��ǽ�9�k�i�Ϲ����3�[�G����wn��3�4$��q��?��+�ݨR��oΧiD7,ͯ�a���~�T�Ԯ�p�;>���HH*?�>�c7�k�r�����;Ȇ��[Z�����i��sv�v�2Q���;Ҽ�'�@,��I�cb��;�_��O����w��2�ק�ױ.�f��)�Wɫ��7�D��R�`�9��{=����Ⱦ<�.�8C�K�Sorѱ��7�D�dÆ�݅�'	
�a�ꑞt��[{�4zb�d.��F'���_���§'c�D��b��,J�}�HV{��W�Tʫ�i2<�ﶎ�4��(91<�lѹ8L)U�O�LD�s;|��!=G$xZ�	��;5�(�i<f�HC�j̜kX�G�p�j�I��W���y��"�ٗN�J��O$�i��K;trGh<�
�s/�0Jrz|�~�eb��WO�<���3z��s^��3~�
\�oX���V�Q��ې�`1A��Q/v�/��L�1�z���7Q�|1��^�	�S��k=�.<�e�<?Lߊ%{K?�9<s��<�,!`���鼟��J~�����W�[s�7^�;�jײ_�M�#���[yߏ�Q1��\z�|z�|31�Ջ�4$��	}�
�*AS�P+�#z���lM4��ʟ��G��X�q
m�l{U9d��P	��;��U;e�;|:m~u��)w޺l�3~�^���>��m':O�=4OA��֙�.,�����sb��G8�No.:��;�z;�޹8�k�o�?Y.���<��q&�u�W�#[:WrLRKO?�6������'���l��3�'ZV�Ȣ�mz�\�������*�@6�$��'�3c̛�:�]c9������
��Ls��6N�D��2RFv����%.��O��>�^װ�e��*�'�.hE���#���rcsR19�_�H�<
|01p��}�;&	&�"���~��'+gɜ���k	���m�7�d���l��$�U�>J�y���^S#~%
��xh����f���_�_�� �uw��/�	�"'<�h���7S�/�r��W]A����(���Dͬ^�^'?�{
��
�k�ҋmW㗼Jk��O�Oj�ݔ��~����{
{@���[�ac\P�
����
���1̛S3p��z����y�C|+T��	>��ov'ؓb|��/���bq�V/~j�V;H����f��E�Jy��r�L����ӟF(���1��n��dE���YOO���Jtg;a��e��YA��RFOO!��yF\L�7��"t��C/��T�|<H��B���f��/Nn�rrk�:>8v�蔡��>�d}��(�vaԇ<�*�-[����c����f�6��o�%����)�8�l\u���A\:���%�m���e�Ï��K�ww���R�p0�k��a��<?�#[`^aP�k���\)o�G�ba���a��d�GV�� R����|�`�n<j�ͼ�1�Cw7�;Vs��$�Q��6箨ʛg�6"{��J���_���z���M'�5Ƌ���W�nn?C��O\��D��[��'cm����M_<��e��f�^{M�Ew;�4�"�w�7L�Մ)��;�3ڒ9�u`�ɲ���\Q��Lc�J!�,m����
�6�>L�]���fV�ޡ��^}^�\\����cT���.��i���<�T�E�{��Ql����u突��������;�>^�y�؞��d�,�.��Ϲ^���@g�yAܢ�K�t�`D�7�Q�lR�����N�	���d�ڏ�͹L���������!�d�V��BOo쏵ٵ�yg�&h�4B��Ebi��\�_��	���z����Sܢ����8r�!���\�|@����|_�0V�H�_F��/4����_s��'BU�'�hZ��{0������OY���3y�ʀ�ħ3��c5� ��Y)�ZܽW&F�檱J����k��>g筊�H���W�N��Ǩ�0֚\�
Љ+l��v>�AnhY�9's��R\+9)�H��^�Z�烂pN��y)>v�	sK�:���HB5[���"����&㦆���u��%�57A�(�&���z�/�/~��w�M�'�-��l>��Ջ����]�S���+�w�3���y'������v�8g�9x� ˁE省���h�&�ഐ;����m�����
����n�_�w�1CZ<�8��e���_f@�/�=���Z9x��Z���C�2�>�h]�}@�)���]�}W?o��͜��V�fF8�m��AV��i����:߷^�#��3u�/	Dg�=	C��ݬ����m��a>�b�$G}�fԼxw9{�	?{�>Š(d-�\�8��}�G���D|�A��P���WyKt�{�<�s����ܕ�$�U����O_���>�?�i;3�/!B�O���wZu�z��7�>{�˹����d����‡}��|y�r��2N�^l�o�B{�y�)\����<�J��D��Ef<�����?�w�5�b�腉��U������n���,�o98��~�(	+�/��5�3G��ѻ���r�g����^�D�!�S�q��#yk<U?���L�$��!�����S8p�έ�/�z��%y��5Y�ch����
Uu�����yG��� {tձϭ�<M��`���0w=��*W�d8�|'>��%?�q�ǯ|���{t��C�3^����W[�#N�F�9��wK��v���G����#����s�l��}��aM���ؔ���r�}��A�V���Đ� ��/�W*�'���^��PBU�5>���1kv�$���<;f��#��cy\�hMU��eObSo͝Y�R�M������I�k�?�Q	i������! -[`�ҕl8��+�?##>��Œ�S�����$uL���0E/������s���ߋ�OS�?���;�h�؞w�f��s1������=��A���󏏆��A���!�7GN���m�wgVzd�	�b2�C��/:���~���t�X��k5N]+�|w�Yxeľ��+�6n�G17�*�s>8�˪�]��3I���)��/��ٝ\y�ۆ�6;�4yn�=/��ю)|�/�\�L��Yrә������ᩞ�-_� /�߅O{��,�Ӎ���{�iOW	�6iѮ���B��Y������5${�3>�n��׳3���T�Ο?]7.��Ry���J���)�S
\_�+S��XԱ���#T���1���1�1o�����x/���r��"��*i׆}�D�y�?�>�O�=%?�%�%Uj�0�`��M��~	'=�=1��H!Ǝ�Q?�sxd��a��)��y&�F6i4$�Z���>�Q}�G��+�_�O�	��"��l�'��XN��W&�tbFg�|�9|�;&��,����:=��U����}���;��F���$?8~Tߓ֕�Я�EO�
��8?l|K�\��3�(?�8�d�����?Q�����Ϯ���&?FN����W�|�O�ϱc�$�_!31��x�z�gK1gٱ�5)?}����9�h�0��M���o��u9�b�!�a\(�lb5B�I�r�u�Gg��s���W�����v���t���j^���W�~��eu���uz4����8*K���X��oP+#%U�>sW����_�9:�/՟�{pf>?����V��Ly��yu��#���ǻs�_tkp����SIH6��g�G��|BsGp�R��_���Cc�05_�����Y��(f7?��N1�Q&�5�}���_m����/{�\���U�eN{ޓ_w�Z,�_�(�,��Z��E��_�tF�r���[������d+|�!�	G�Yy�S����O��Y/��K���m�w����RԒ[����'�{���ȣ01
�o��E� ��:2�v�����?�_Kc�c
jL� @�d��
�6��SGD�F�<s�̢>�O~�.?��`����,�({���?j�U��Ӟ}z���<�o�\��|��P|����l�9~r"�j�H�_"���gƋ���B����G|����k��z�[�_\�\��_�J��$���h��i�l���׭��a�����\�@Z�)o^�<��>����
��>������+����uc�Y�	J�-��~�E���`�J�F�T�WXLCv�:�������>�26#���Xr�T���c��G���KybD�tV�+�h�ܶW	��T�8��N����p�8R��"�����9���@V4���cT6�bq��¿�L����T��G
��-�
}�W�0��w|O~�w�o�Wf('��n�'3KT��'�~_��k7c�Qt�w/f�b:v�;����"�������侮r���@X�k�4nn�G��� .�4EA��;�uv�CU
�a��_�pԷ����
�n�q6=]��K��׮���i��3�Ժ`Hl�
(:�
Эy~��mW�³�5�t��X���Q��Ę�����_����8m�p�.|2U�'��j�����ń}��I�����4>Ν��h+��X
��CB���,���<1���n{�Q�}�(�G��m^��پՎ�m����X���{}�g��U#���P����<mꮥ�%�C�#��ؗ��HSK5x>蜽z�q5$	�Yrk|�ǦX=��#5���/��{�="7pt�!�b2�gNM�.zr�Y��=�&��i܇�0uHܫ���U~��0D�]<*�\���;>ɂ�ߺ�nO��y��C��PR�l9�fK_4W��?��ON�W�)ja�0�'<uJ��_���'?Sw#�.3���J������_Huk�U?84��Uؒ���T�U_��M�P䐡�Kx����TT�)�\v�8����](�QC^GPtfŚx�қw.|L�539`�=*�#�������q����ݗ�o��S͉EVob�/�@Z�ѯ�೗��ú����ҕ�B�o��K�u��݄�^wu
�R�R5�t�a�0��$�AY9��_�Q�b�(O��73n�����гTC�z+��f��m~&YB�V�uPԦ"�޾9s�!���I�Zw6�ƅ�w��v� ��m�o~��{Ƚ7����*�Ƕ�T8��v���L76{�Ó���K�8�2Wl�T>(����}�Lq��f��ДW�٫��^��+YD ꇃ��=�D/�a4�z��(�H���[�b\
�����`=bkX2��73b�ܱ���!��$��
|w�vzv������pb�s�
�,E�tv�S���<��ݞc������tNU���P��1�5�մ�iT9-��O�xև��@X���vm$��_7ٜ����O��7��x��_!!k�*���	�˂��א~�Ƙ��R��aq�ۭf�
��N.hV��0�ڋe�Z;�|%��Ȏ_V��7}^�ԝ���tUxF1+���,U�d���Y��_���'DI��~�Zv<�����Ϯ"�=�	ޤ�	��ykt{�P��8I�e�J8h�-����
�9�o�(A��5�X�v�z� N����r���>�B�nӉ�]��E��
�a%i����>��L:��{]~��џjS�����'\{w����K-�Z��|K��#m�_�d\`0�u�m{�?v:V��W���_n�}N��h�3f��wl~��C�����dk��i�@���ч�#�&��t�c<:�����-����2�d�K�b��%���)M���Ӝ�Q�崆,_I��%������#E���r}#"��͉::��M������&Z$��۞7JOT�p�~�� 0�9ϴM/D@��%�c���o�Z^Kg�K=e�}�D޺�Q�h�SG�I1=&ēm�b�����F��/狿�p��x.��Q�:"��vCL�пbٌZ��w���T���e��fv.NN������m��]%�gb�fo��p�v�@ꉍ��z]|�>���o���8|;�y�y�Ƣy`?/O��)b>���y�ѿČy��.X��(&���#��Ek݄�,ְ���#d�*�/|Њ�/����E���|�#��|�ɡ�/��a �c��|O^����ѧ��
T�s�Z�_�X`_3����� �F����Zf$�sn��񉷁�����D��?��جf��5�:�g�j4�ŪR�|�s�6~�Q�]>S�
��#���N�g�Ħ0˜�$��s�	�#��>Č��K䚓1��w�y({�:�O*��Ⱥs�`|��F���
z)Xl��n u+����QB�ɏFc�i������<�G��3б��-JU�5�?�/=�=�V/���zڎB����]�;���_�kb�}f�{X�����su���(�Sk0�oȃ<P�����lB��qRa]�d.������:y���;�R��y�평M�cH/�oyfo8��o1=�dž�m@����R��!!�t�g��WG����L��T�/{"67�h�o�+�c��?�&�n����O�|�&�&����ZL�l�k9'ߞ�aZ�"#8�o��{(�~��+��� (f����/�*����D����g��x�}<"�5�n{�xq7�}����K��08btl0w�оX��X�F~��R}�f��D���`7�R[��55��{�g�P؍iUY%��HE@����#��>��T���i�m�ᗘy8���-|0��_Wn�e�<�M�^�;M���p�
�ݩ�D8��_���X���fm�}�������o�]�u���*��A瑷نy��ɳ�17`�<O�z��sqq���0Q?~u���{���'?�}
�D8(��j<U�-|������x}v�����5�gH�����{*7�����{��n;�������lG~‡i\��k�P�T4�٭��������(7��d!&���)W�
���ۻ���/t	��]먟v��1�;��X(������p�����I�Ÿ�x$�C����	�B��N��f{x��9��m���Y��'��q�+�<x��������,�����Wn痲�|�_���?f/��ލ�
	��C�ǸL��1_�육���_��w�.�A�1��W��r��y�.#d�@�$����l�#�ꎧ�}�!�H���U���}3ؾ{��5�34�����t�V���s{K����_z����O"{���< q��J��ڣ|�69���<��ש��#w����ÁXf��%�f��WL��5���Ĕ��;�+�<��	��k�T�W9#6����B�U�}�$d�Om�ѹ��̣:�����r�k����?�A����#��{^����=�؅��A��8nc*����	}���zݸ3pF߻����SLƑ�1���#}��k<Ɩ���>o]9�<�Eo55�t�OV�,����r@��&��X��3t;���a+)�Z�7�E��>3��<G��L��g��<<�o�b�{��'>#?������=���n˛R�ͬD
._ȁ�Lt����h���ļ�ӕ�M@���;�'_Tr8�>O�eH.�G��	�����n��,����+�m�Q�?*�"?	�X�|����':����E�.t�<|~�=� �N>'�:�v��E����L������X��+��u1|X�PM/�+Uȭ�
X;��W_��YT+�3�X
��
�_�}��<i��qkXA�0{z��&!b/�ψ_�5�n/%~	,]~����B���/��|��(��D9�:���tk��	⢏��1��������upM�D��]�
���@l[��"�>�{#��n��(P��#ڻ�Dg��EK�tͿ������U�X+z�U{��@��a}� @����?��JB9"k=�\נtpw���<|�7�Ul��=%.��/_��/���d�I8�I*���H�>�O�y�����o�Y�I>�z�����H\=�1�S�K^q{ժ�xG�%�I���t��%ls�6��1����вWmK���K���[�A��z�!��������o~��0b�W��z�� �#���Xl
�I����Y/�[L��PeOX����R��U� �u0�@��d1��j�U���j/y����'Bϟ�P�t�t�@��nb*f�tƚ���7���.��a�ż�6N�&����w�Β�6��m?$?�	�6X�	���6ׁE�ݝ�{Q���sK���}g��h�J��O^�0D�(b#v��	��7��>H�Nb��Q׵V��$Y}/N���/BR�7��}h~��}�?Ē7���IM�G�R�����E���<��0yf��1|�Yi\�9lW�7o*zb�W��.9|�G��#�;������nv�;��3�(�1��Y���9��<�	T��G%�xuY��aa
x��:�(/�+�'��x�t3$A)8e��j�g\��Г��/�k�IE�����i^���:���p����s����	U���_��6��6|���W��N��nE=L��I'2��d���op��fˤ�[yú�|�J�7M����})ܓ�q��qZ�s�aZ[{��.�Cd?�Y|H{�t�b�Đ�I~��|�ؚ�,(i����n��_K��0�S�ƚjb�!ވ���Z1�b���oO_OFR���8����6M�C uv���	�}T����7οxE�L�VxbB��R�0)��$d*BO)���]����؋��~�~�z�E�+��k�������s�VXV���,�GP���3�ɟ�r�I6�5�4��zĕ�kHޤ#C��W�l��_����|QF�_H�b��;��͏�o��AO�<=_����{����%������)%RG����p����is|WC;���L�z������_†Rj�G��;�4��/���%�Lp��[�˼�+���7�	��^�}�E��+���⹾�mNqJ��9�%Fޑ+��^e\f����:l�NI_֭bݽ'$g�[t��1�gQ>�e���9�*?э�I�yMtϠ@��^����K~���!�%�ݗ+�谝���X����ϥ�d�!��\ȅ��m�|�=�=�z�~�+�>�1r�G�lo�-��>bVLGA�����kMٜ!h���Z3�'u�)y�����H�i�Bg���7�T�d���
�XNU������i\����B��W	B��)>*��`���⯩�ѧ#ȅ�bS�sc
0�
8[����m���^鉡J9к�/��:��E��G4��d�AJ,+��+�J{�^���}���ubpo�S�K���,��.y���l�Sn�vP�o�Rd���M���O����t��`��.�;�U��/W���N=܇�A�>�?n�}u�5�����nA�8Hy��@c��{ȣ/�	����]�i3�3�R�ɦE�˞
e)R�\u~����9�;�~��$?r�ǯ_�&�E�[PRZ��P�5��z��ö���Z�v�y��ݯ.��䚼2��{��/�<b��z+����}������DW#��ͦm�j��=m:鑡{�����܊5��,�* �]u�^}�%���'�+pʛ���Z'��1�Pn���t�b����5-6=m�v�Mr��
����‡���x��dQީ�!��%(�
U&�xt����|+�>�����/�÷e��� �������*[cn�b���<:Š���I�R�
�մЦ�[�?�Ct�%4�=�x�k(v}�^(UӬ3ߐ���2��M�i*G��X�_(~1"e��/��X�<C���ة���*�&8�|@�|�z�O�Z!y��K�CxNF��N�U�b�V��}��ӝ�e��S��W�������_5��牕�|9�_u��Nì؝��o5�=����;Vn��5R_���ʻS�KK��3��2Κ?x��wLj��#Z����f3/��>����k�L��G�T��t"׭��������t��Rrvy�k�vЀh�2��;Y���?�*�l�2�6�)�
�E�������Z�8��pm�ĉ)z�<�p�>�G�]�G�9e$��bS�+���z�kc7�g�C���2X� �2�t55�������^��/,	�:ł`���z�c%;e�9W�t?aN߳?ѧ$X:���T�lx̞8�p��jF�q���	`��{N�
�̞�r|G4@����Cޯ��Rl��y�~ְw��K�7>���
�[}�GdW�o;����ϕ�W9����nٟ��u<��gO��9����~�gc�7�iЄ��F1���aq��Gh�<����8�9�!��q%~��{��;��]��
\�n,|_3������^2�m��w(F�=�F�R�y��1y�ZD�6�����"������?�Y�	�@������� C��䙁�G5+߄u�M�����?uբ&��o�2���q�:�kb�1OFm��-<���"�m?�W�i�{����؞+��;����c�����N[`�[}�B���3��yL�u��0ݍ%�`TNe����G捄�HG#虜����e�I&��p
����U&��
H�&J�\�w���ڡ�&�
�ƾ=���n<ǘ��be�Y!�D���I� ��}�>��E�!��^��O��z�����Y���q��)����F/�iŕ�j"|<z��oaj�OW��}4P"�XP>���-W��u��⠥�rK�S�bq�'�%�~vzQ@O�M�-+F\}�xZ^�=fM�{v`����yz�o�w�rĬ-?��v�:.!Z�3J׊_ٟ�~�L���-
�4߀����}����i�d�5:�#E+;OS~,��F�gP�zB�r��T�kZz�P�n�޽�)jv+|O���i�+��U�����w3�^�Z��#�Jf�٠�rD�ԯ�
%����@����o<�ٹS�9f�b����K(�e:�G��c���hp�;�i���w�o�i�j|���Ñ�u#6�!l>���0�JMG����/J�xDҲ���,��D�����&��{�:M“�R���0f���v�W^$����q��*�|��W?9��#J�W��L�7�>�@�]�����3EJ�t�a����iϰ��ǯ'w�L�b��s-�,U�JC�e�[R�1��Y���=��������pLLO��^�F�{cV��Y����E`A8�3JJ_�08>�LO#��|L��ϔ|�۞n�����ubk��˿�&bx��3�L�����d�Ra�%?�Oh��
����u��Z��)���p����gH4��
�>�D�ftI]�
%Ix��Oyq�'��J��uaRE�����ձ�óX���i+|JQ�_#�>�k�H��ܣO�)aج�����n򚴎V��*��F�����	�E�ͯ�Q�r"������<��[��f�|
�����'�A
~�y�~+b��T��
?���V"���,B����>{��u�6u��؝b�
\H���b���P��R��D��b�8��M���i�j���(�<�'���Vy��S`���v-�X�T���T�X!���M�r��ň�Q81��<z���w�6{�T����*W�!�_�x���‚�"N��T�NrX��������%&��x�ᠿv���g��'I�����˧��r�~��/��sb�錉~�_+�W��Ŧ���z'�m���Izr�4�g�n�b>ĚX�E��],��$�c�^��^�%���5��+��^������=�k%�LL�H�*?��<g��#��rD;�dc#g����I�\�z��h/?|�o�I��7�a���>��~��MgHw�Gm�GkEJ���O�\���nrNr�3���\5�pC�2�4�at��@)v���G�ӝb�q��H�~�{�>�A�5[�G��P�Ex�)�>H�qV8l�Ŕ�/�S�$$�c�"�z�ӈ��wp�D�J�}y����(QQ>�W�ؑ����n�;vj��ʁ����wE�����y�V?������D���.��~�/$NY�C�
n#�.��}��8���Ǹ�I��*��m��=J�/��J*�o�*�������,�����>w�?y�[��)ƈ�0
M�[%c7���#�'��e�/$EL�R�RG'��3�	����'��ȯ�ʀ„ϲ��.{X2'�����ǙG���O>�h�O��]Mt͓U�X��Ŷj>|xe\��Q�
��K���W}�[lk����ѿ*0g�S���_�@�k���be���.9'��:<_!�>���5�04��|R-�CZ*�G�%tC-�
�S��'����
@Ьxۮ7R��hӷ��u�j"l��u��&�*ω���ܒO%���N�����D��~z��ŌB߇��g��3y�QC��'��뎧���xױy�9C���胏��`��>|П d��e�OQ���![�ç9:P��\�w9d���O��
M�h/��F�.��@"4�\�D��r�
���0�b����
՗I�r���,��A�>ti��y�4A(b��
Ÿ\/zn��w����f��z�l�����.�.1:����ŧ>8�9�<�CwHiw�����������a��,vD�	��{9l����{��T;�g��@�̉�W~�i.8T��>;Vm>H�:]>dS���BF�����^x�_F�w+��%�h���-8��+���p����7�gj�ئ���@�R�V/�ʲ�	��������_4�yij&�Dd@�П�_"��!6ӁE|�
>�<�P"�;��'�p�Tq/]��	@w_oN��q\3�]�g�h3u_�ix-,`��X~p�pبz�V,�k��,�o|��6�/�	Z���(Q���%+Ď�m�kx�X^�$i��7���;]0�I���dp��U�������b�
��%��R�������gN��A:�k��S5����zh�|G-�0����\Y�O�Xب���_��)s��`�gF9�$��l��.�-ں<L�������>"� Ν9��%r%UZn|�a�Y�H��~�+/��9q��5�-���BȺ��t
�bME���C'g���ny����e�`�Ά8�n��֬1��Q�H�0@t��Mk{r@q[HP=�	��_�H!�g#�^��(���ý�a��\}�a�x���	xp|����~�'0h
��isͦ��lA����g�
��!�KSG�5����K�rz!4Kb�5�8;�`酽���┙Cv��*�D]�'5���C�~)�)�-~���$_@S:��~�e�+����/jg���:k��v~h��.8��ח�YW��w�S8�S.�#~�ZG�|.�or,�����՟�J
��
�{���Lz��^�� �k����c�s�ֳ�Ҕ$���|:$�b�~��Qybb�]�9���g��L��/%>�?�C�w6�ԋ9g9�p��'%*4/@��t�#$���l�sT�s�'��eI巘���uʋ>����#����,|؍}sv�(��4�ڒS�!��|�#���I;z1>��=�R�!���8�/?�[|i���p��J=�wU�\����s�&��͡���_��V�>{*�g�|���x{0��'1�tNq��n�k�E��~�`���">|�#�,=
�||�̆�-��?���锧���>�_�s�s�N�,�'Ap:���2�=�Kr�2�����9=e��Op��Ok����g4�
��g�l�ރ�'��ir�c��C�I����;��W��k�&Ǿ����'ww�>\�L}���苆�Wr�D~��57{�N��;�+=U�ؠѯ��Qs�JT�@�3�{2���<�/�G�v�:�����_d���έ�gL�	a��H^���#�{�m˧e��{��%����lo������s�B�N{����~)O��'�~�!��z��ϓ�φc���@�����W��B����/?���Y��|ʯ#>��w���؋G��(fe��\���a�D�\��|0V'"��suu�(���)+����?|x����zU����n�Zy�{��O��'y���-�Q���Gy�z^2���-�c��^��ԙ����c�j�f^���?�y��$�%�/c��冒�Ӓ�`������L����
���C�6�5>3~E��a4��t�7�����G:��'Ͽ���W����xd<�!r�Fv�W�3��q'��ߏzzԯ�>�3��p�2�G�'�M�7�%�Łe?����R�/��k4|xG1k��?��C䱇�y��)��u�:�����؟l�?�k�F���q�Sf����$_����>�<ӗ���ܧ�a�ڎ��b�Lwyk�_�i/�P߻�
8�[�p�LD�>3��G����8�'6�;��jضA�t����?ڱ�5{x�����ʟ�	�Xм�	l,�~
�i�(?�v���^c�~i7:�2vx�#�,F>��:�1Y���#?���"�y8��_���<7O�����8 �=�_O�+{8�y�ף[�z�*D��q��j}�	�)��>��@|L����3�F�'�E��v�3x"�ï�D�8��|>�g��?���"���?����/q�G��$��
��nփ�9+�ڍ�������:~�|l��r�'�>&��3���S�+~�d��uw%�	�Y/�+|�O��S��1�,��q��y���k�~=�?E8������c
���W�9��<����S�z�0w�θVˣО��k�3|���%�n�%PsG�/a�.0��ƭ��Y�A�<����G~�T{�W�vc���/��>�
�.�r��	����"؟�O}�8�U� �)�g��p��رJ�;�-����I�r��(v�;���8\T���Wx��ўms��_��6��[/��^��5BO�*�s'�^��q��1��BG�!r��]�w>�����yG�3����p�.�#�v'�k�޼D��;ޛo�z
��H{�Rwh�;�(�"�z��[�k-?�����D��i!<Rb�N��.ߌr����_c��ro���+���d4\!:����j�G~���o>�㜿���3����6��3 ��h�B��_n�uA��k�i��;/�I����Ћ�fy�d��G��A�ԟr���@���*/����F�X�!)����+b�0(l��8j�_H���E��?y� �>^A*�_����|�D8��H9�u�ɍ�/�KN��H>iu�!Oү無�;H����8�Z��>�k�
�^�8�����3��.h�����Fa��	C�U�8?{��X��k�|���^�/�)�d��g�y�g�c^ԙ�}������x�x�:=����ೳ �3T�S��;o;�����@����ٓ�>��CJ6��W��C��J�
���o�g}6=���U�}���t����Tf��L�K�\ �_��ol�a�w����Y�m��C�X���(��:�G�B1@��F��ԣìm'?�o���"����C���Y�c3V�_�SVhx�u���O#�����EB:�ٸ�i���
孿�9��3�4��(]o�L�EO�ҼW���~pf��8�_叜I�O���^�m��g��}�gV4yF�b�u�:+���o�)�?w�����(��13�,�y1&ư�ּ]��t�Tp�����Eɛ�2�v���>E�_o�s�#���h!�p
MW��sQkn����ޗ�eG�[����d��Z� pTV��!p�\��Y��
�G�|����>|��A�%�C�V4��դF�� 2����k78���;�/�C���.]#<���8I>
��/�U�cׯ�a2
�r
81�p�|iF���]�!��s.�N%܁�~A���@"���9����?�(��^�~�r��/y@]d��������,�A�wo�C��p_����&�n�X�\Z!qt����1i���я�vh��6���0"�$�lVfet�� %OL�&̨jB��թ�ܰ�L	G�᧾k@U�B&n����Ibg�W�D͙�d/*Oo��1���My�P�%ۖ�yb�+x8b�FCBFS�_�|�/��V��Ź���IY����9�j� �/��	I��
wD:�bq�D����K,�^]��;z����2E#':o�*��X3w$ݱoq3�^ԃ.��S�%+�V,t�>��3�c�t���i
Z�#��q�,��}TuyM:��V9K����P艀P�lA
��1Ws�lq�Q{�t�aK��'[&�C~"���U�୘'�!������Np�`f�,���=��-��1j����-b��,�#�Wh
y6�a�k�L��C6��X.��Y1ó�m"_茢k,��ķ�a1vG���w��B_�і<4k�G����%?+f��2]�#�2������.V`B��o�����sU���\�^����S�7�-�-��w��xI�ˊ$�.�Guh�HM�l�l+�
�Ъ��V���6��4����4�G��ƗDKсh�`tb��$W�X��ͽ���O͡�1�iz}n��j�M��w�\@�s~�$1>V��
��yVƵdWs�	���\JL.�%�'�e�[/�~n_�	-�������S�u��N�d�;���j�#��m�<oQ�>���~M���M�M����'�U��Z}�36�s竔k��4�0q��sS1M��3f
ʻ1�����߃��E�6��W�Ԋ��'���5a>M�LT���N߱���߽+?
8L���o�sӡT7�_޲�	i�A���1��CH��1Ǡ|[��7���l|TSر�|&e,�m�������tmc�Ĺ֪�C^�e���G��D7P���,~���1֙/�2�������D��v~�'�*�ϔ�W��u!Zy8�<�^�����9�_@|������>�$U��~s��3}Ob�ȹ��\�gY��:����;��s�+��O�:w2w?A�%GR�}�|'��R_j�Gg����9|�Qk0�&%����v����K��!~��]���Z�1����o�	�"����L7l1T}|o��+�	��֖��m����|�L|JL~�܉��>�kQ�&��(��D���خ�z�/�F��|3�y��D'B�*��ǿ��ۢG�^�t����D��3�5��þ�={�>��t�l���O>q�<�����ئRӧ����S�`�onʒ������]��h	��y�A,5�\��R0�ZC�	�U/��[dOa��7=E��o��_d������\�<�,����l�c�a	��������'��?�s�rf��z���s�0��Z�g�x�s�Â�ի,�����O'[����7}�� ��s���0!�V����}�-�Ǧ:bb��2�e����R�(4B6>vd�[R��̬(�M���K�1��;�G����+�2G�����[L��ԭS)���w0���/��Ύ�8�nb/�Y�	�R|�A�xr(���9�+�ꜽvV��O���_�,M�a�����Ry��ʶ*�B!8��0����^���9y�|���>cD����\�;a�JM�]�l��S]0�k�Y��;ߋ�������1K�̧|�_q7��cdܷ���yć���"��7����L~�>@y��=�݈�G~��֯������K�7c,�շ���3�ƥ��?�3 �1́���O���'��L��|9�VyJ�|<�/���tn�����.�|r��	���HO�IB�o��0�.��^E����j�Öx]K���y�r]�ya|��v�>g�ry�\�t��[K�b&�/�F}�B�(����F���f�˞���2h��E�K)0*V�����>cW�_2�S�����p؟'T��	* ����
��N�o�%	�u�!l���f@V���3|>��3S�����r��c]����']��$0|��d�oľ�H'���	��g�++�<ynR�O�5Jфm?k���]M�3��.r����R�#)��{���8�2��g�DNL^�O)��Є�ʛո��{[�4�M~��(z%�K���P0?Z����U�S�h���v�t���x5;�y5��n��/�!?�>)|��
�GU�vg�31�3�P�O���:Ӌ�E_lmh�PA缁Y	�R~�4�,mo3�$8��m�g��b��}|:"?��I��k��i�“"�u��=�5��J��r�gb�B5~M�~���a�r�>��̯&)K*��ԯ� �pԉ5i֔�<���/tV�~���'�\���Q���c�4�_&va��'}�څ�͝����~ů*?����S��˩Ѝ��ޙ������!C�M,���?����YM����C�j�\a��r#�kox�W'y�{�]�lۯ"n�ڞ���|��L��q���>䵦;?3�@���O��p�O�f�'��cX^0����
Hfa�sc���\3��e����;�X�V��}�{�r�kҋ��<��T�f���U1ƒԼ~1d���6��4=-O���ja��ىڔ[��)�q)
 X����c1"�Ƈ�O.,H���=�V�א��uO�OCmpZ�")��b�%�6��9KZ�A���ww	�p�c.�t=
G�~�<GaJ�GA��Q�&�������W�6��q�>���r���١]���={�9]N��/���-��$��1�Ty�`��u��	q����k��/�>yt�Y��^@ެH}]N�u
}��GՅ�>L�����=��qQ�p��·X�����+�~�!��)�K�f��-b>����_����e.,Q<����#��<4HDO�C�0��CJ�]�a+~��+O~�������_V�W�_�a#G���~��ܝY�xA7
�щ� ��Qy/ �
N1�l6�|����H�P="�_��|��m�G��s@�k�S>��Oy)�^<"!�!$�k��.#�٧�eQx�t5~�/�_�OX/�k����9~uG<;����T�eM>g����	t�0�J1�I$P�r��b\Jꃿ�	7�+*�g�kM���B�<1�<��:D�
w���2�RB�����/,����ׯ�+��
p��f�t�+e���+���W&7z��iTs��A���e�l�4�a�}���h>d�?Q��c�@	���C>;��(��N$��G���Lw���5y��m8�kvRy�@^��I�({$Bg��
��s�Aʥ�Ij��in�ށ��="�o���E�����7�$m(k��X1�Ie�;�a��zQ~Y��KoJ!UL�;�+}$��
��2~>kW��v�?�,����S_e-Սj�;i�]�F(z��]��0K~p`�������s�H�X��NY�{����a�*�)O[��-��,�U[���_�)EJW-�+���x�U*-�?|.&~���|����sq�e�ݩ�GG��s�u�g|�}�Fʽ��A$(��;!�עmaI��F�X�oZG��@6ҹ�7X���U�e��)+�?��4����0���#��}��O�7}��9"ʭT�Jj�,�;�b�A��
��rj�J�����ז!�~O��b}�����}�Y�:�c��>�e�_z���8��_���[����}{�s��)��Ks��O^�O�H�,Bz{'�!�u=�`@Y��^���[�5��I�����>F�{�{I$ڹ�H������]�e�Ҵb�o���Kb,��a._��c7��/O�>8�]Z�����'8?_������]<ӫ7g�;�4�5^�] �5��2�;y�
t�&�ow`J�W�S���.i��n����㪖��I�Ld�C��a�y�<[��7�u^�O����|���.���;��:�`�1"��l
}"eD���T'�n�DZ��?�PB����ʥL�.lp�E|1-^����]��1�3S��lvRb��%M��q ���e��s��L|�E11	 ��P �5��������v��6����ҽ�-󖙋]9���zlw�cc�[�`<�|a>����2ԋ�?}�^a����w��{Zl�|E��/چ���ow����Vڦ�H�X�=ƴ&Dy>O�b;���aE(3�U���Q���A~�������CQvJ��|��X�2:�H/��z�7z{vN֥�A"=`�]���}�g���2�G)����W$�Q��ȷΕ�)$$[@3��4���S���1����[U�9�H(�tblH��p
	����]�n���(�a�ߜ�">Pu�A�sr��[��+}�E�m�Q�#f�������b�O�MV��F�aD|��5��f^����7�L��Y��sXʬ��GI�寷�ܜ��8��#L ce��$���+��z��<��)�����:��xkse쩛�.��h�G����a���B�K�A�V����F;Dķ=o
��>�M�1u��9��ڒ�O7���C{��}�e�]�۱2���AA:(	�7�$?��o�l�>|��*�sG�u	���/���H>�饷�tk��Ii�v�wkQ Es/`�q�-��i�/��Ӎ�����R�չ=��'u�e8�� �ɆT.�c5��U���w�D�������c���珟$9a��p�|�ʁN�M��m|�yD�q��#,]���SEg�.<ʇ��L�O�rC�v�B#����F^i�n�Бsd�wN�I�%h�a�l-��n�S)�=�"u�H��!����3Ǣ��7<'G���$<��%�_VH]3Ԥ�ҋI3�'���X����|��G�h������T�yX���_���5*ʊRx�N��C�A`
�$]��㩿�=	��"�(\*���˴O�N�
����$�H�|�*�	Y�D�;��8�ߩ�� �<0�D��v����4����9���E�O�&��%����o�N|J�����r<��<��<i#g|���'�My�Ч.���S�*I��SiO�n�'D��X��|���G�1�y��/8�y���'�P��D��F%9��aX�XC�
���'�[��Fg�)��g�����kJ���1�?�85����/F3��ᐷ�nC��V�o��}����e�Z�;Jf]�o��g�׸89��;��zv
�����=W��
j�KL���!��_��Ƥ����<[	��
ip�XDxH�>����IԗV�o�15�v&W*��ɡ�7IGR�����4��yk�j��W�?�uJ�
�XX:	��UY�pd��';�Y��p�]6��@.��Lx>;[盉Ec�-B=5��Q�����v�
\�tv��!���/3��ODPbqhw@1�yv2�
3�'W���G�+Ǒ�e���H}����O���e�'��_�n(�>��@��}��>�`���_��Q~��h�ӘPj���%��
�D��p��U���������cU��/,�_�C��upLm��޿�ΊB�7�'0��>��/�n���nKY�>�)�����lD7��	s���5�<��[�7�ï.~�;*{�ݛ�o�P�O����9��XS�0��"��J��	Դ0����	��}�e��~E����a|���TUs�[l\���.�da|Z�17�uN�S`��wz��
����Ep���i�R����_�F���ѯ�UL'>f�}�pxTBS������S�fm7{����2�?
b;_K,q�lM�/�'�D��c����J3�	����C/0�����P��E�Q��?�_J�QRl�V�;�)�`{��|�>}��U�-
G�՟p�	`�*<Ӌ�i(��Qr�'�7�o&�B���)z
fv5c��G��O����I;�?��S��SO'�V�s�����a� }�G��_�a�i_I\�St��f��R���|���.h_?��J~�WJ1p�lgWg8�Y�O�|o>�d~8�Q~��#�fʗ�[a���Ĭ��f.�,��=�0����'��9�>>��#}�h�U��ك/�SJZɑ_��$i��.O�����{�W�i�=ۙ����Yw�h�|&�P|O�zT��+�?�bq2eQ>]��A���AZ����biŔg���58�Pmr�0~�3���a+��e���@)7,Z���=E��<{��匿ϭsw����s5��>���D{� V���z�I>*{ɯW�ڽ����G)t6~�й�@�ʵ��~�G��a���*M>��c�n�ߡ������'��G�_�MA���┊����4����b�Чk5��r��� �¦#�O�l�O����m�"WQc��n�G�w
y_^��j]�n&�Gp�_�?@+�藆;�v0��X�u��I��+��wM��"��ܿ����eYWb����� �?��-w
n�`�eZڴ��l��\�+|G�(~��@��8�E��@j�Gu�[�I��
���&��ӑg�#�)%�U�p�*��P���i"}��+�]�;�j�Z��Eg-P}"��f���M0K��ƎI������{�Z��r�S�
��1�S�Ј�%�dx���v�D��/��1�1���7�}���k���^�Rʈ��~�Wajj&�,T�%ޥ��>{�TI��2��<N(��ꗒb_�W�t��1Hx�Hg��,�H&��a�ʋ,�n�EyD�m���>�����/=�
8v/�>���������F���D�B/��<d�?~�OԺ��/uo+V	�;�T^M#B���ư3�����w(Ӧ�
~]�GPB%J-R��ѭCkoC�c��+�be��䟑ic���P� �ݑ4y�˃�7�t��1�1�E"�‚�X�֜�ꑺ��$�A�b�؍���,qmY����A�Y�a��Se����s{E�����2�DƗ�a\}GV&�>��֐���F�R�
K�[P����m�DÚn�"$�`���7w-��|l)�n��m��l��L�d� 6\��/�������/�b�~1)�KV�o������Q�BE7][�?�B���O���O��^^����,����x�n�p4���
Z��nQY�X-�,��Ӧ���/aBK
h���.�hN��:�D��5�o�6֝M;V#��
�!��tvf���*�_����&��>x
��8~%�;���t�4����bkS�b��t���-?�bȆ�}������C���r�Aq��o?�~�g��3C��>nj��4������_�����-:�~����Kl�D7l�-:C���|���)|Y����w�^�i��I�ؐKtw���ݬ�
�W�CR�k�MQ�yi~��ňhwŔL���s!��^,��k��i�W0������<K�;��q���p��u�r7K��n��i>�
��gy/���VṤE��K�j��l|(��
�B���v��7J����`IA��)|peT��r�R.f�m�_���P�[p�l��+\f�gNٳ�p������\��"��$���NDL��$6c�]�|�z�e�H��Y��?�;����y۳N���>3 �\4�	m;��j�I�~ wW�9�FWb��b�Si�Ȳ�>���sa%�2��j?MLLkQ�]��[{>+��_U�8zU���o�6�+c�^�
��;z��B=R��07A���Kw[DI��w�*�	�i����@vszr*�9�*�N���=�\��I��v�?Ο��C��ґ!;o&��zIK�6�Gk~�_�KPUUIDY�)��R)�����`b���)_:c�\��>|��}��E<Sw�E�:|�c�0~�AY�a&����M�e�	�fjnŬ���c�ʚ����
�2�Iw0��S�S�������4�~{zf��|{=m�����•�6R�e��\���X'™E�am�ӣ2+ػ�5oo�F���($���<��Y��_s�[vM�%,�B�
<�a�&I��痀�^eΧ��՜�<@��ǝM"��'���;�>[��ј[d�wP���L�&?l൴k��:M���y�q���X{�'���!&'ڿ�����/����e�|�i'($�܊�0�l�ۭ�O��^����5g�n��T�ǫC5tʼ��>��ú�h�V(�P�#�_M��9�?��_�T�n�Z'>�w��CG�n�,�����T&�y[9�;S�be(W���OLZ<1��;�T.�,�j�Hg-�S�V���j���P1{���Z��p&7�8%v�1��2�
e��K��rc4��y9��9��r�C>_|G
�/�Ĉ�o��m��Q&8X,&F�i7LGp���ΗD<��W���s)�o����C��1u���b/���]�ڄ�S�,�O{�G~��w�u����U��|���7�&��G�ԟ;&ϐ����O�=�X�g�Y䊐肝�p���=�O�ѐT�y �"�HAD�^$�N�#�����vv��@GR01v�/�39��~vգb��'�����9���-��M�����0t����'z��Q9~�[��J�m�<�a��܄�@%���s��j"��
��X��՗ݢSM��ԡ�q�[�E��|�8h=I��� ɚ����J=�/�Q��0���
��G��g�<[~�_|<����

�_]�`Anqp&?���P���G���+dy�k��ܯ�l�[�����&�����grc*oS��T�3b�G��To��9f��;ę���k����>Ci�h/�2��*���;Y��]v�z��F�g6 �J�WkE;l�z�PL9�%�)�Q���8���OÇ���Ya����V8����]��Ƣ�����P�_Ԏ;��´�DК�팉)M�4��3
��hmi�d�����c
j~���Z1X��X�h�� �O������9��ѯU^�œ^ULI>􆳈`\�/_�i�8.���	���Ć�I1���c�YfZCy�㻏�S�����=�Wfn����/V�W��ʄ>H���"��[e©�Qݼ߷�"��i4i>	#���t���4�b4��l2ŷ��rz�>&�\�ݴ0���J��Kя��)$I��b&�DN�����%yVn��_ɏ�l{��'��>�g��
�P��j�I�o1�f7�?�c;�319��Bp�x����HT���3���uY�^wfe����W|���
x���ر���n������Q>���F9'v}�.����� �>
��P_��11�(Sގ�A��mC�C�#k�@Og<�>�3��|���z����a���TeJ���D�.k�~1^�/+0r8��
�:��}6q7�|�+�/{^^���Oс>>�|������S�����iZl\��Y�|ʉbd���Q���9�(�:B�|� y��N:U;Q�^sc>��<�<%���G�J��]B�:y6�cߕ��{����P��W�
���i۸y{��	�$���;|T.�L~U+քQ*P�����[��	���G��'o��m&�0�t1j�®��kƋ�i\�u~X_&9Gy;�nMu����N�H�@vR}�D�_dl�ǫ�@�1j�d�����kۃ���g��[�����Wut����u+�مFφ����{7a�_�`1s'�up���>ͻ�s�2$��8��*�x�45�K~���ڱ	���ᓾ�H����7 �W8���>՟;���$�姙�&����a���� G������|���k8���07_=��lסsz�~�>swO����/3$�ǟ�}J�V�I�8aՙ�������||��W{ں��7���I�1T����o�2k�Fa�^��e�O�U�l�n����s)؛��+��$]��<{��E���1Q�}�f�
�Q��+M �Ai� 练��~1.����Y�G@�6�p׃�Vǜ�¬�1�w�����*uj8�)&{�Id
�J�AK??��'}��0�n��g(?h{�E��[c�	�	|�>�b�]�6�Z5.�l1ѐ�ߓ�K��.�X�b�(IhC	�����_ɡJ�����d�9{��lJ��`�AX����L�G�j�f�T Ty5�'_�C��l�d�����n)�����_�U���!�L�C�v��\�O��{~B���v���E�71j/�yzz�%���[�58�h)�,S~k��v$?���H�k�{���;:(/�Hw0%�v�w+K|�++I�ԃ��ԮN�¯��	��<�����v�?^_�m�QLI~ȃ�i��sN��ϝ$�HV}��5�={���� ��#�s�6|~jt����d,}�n��r7-|��_�^����2D.:�W�~�v�E��i�A��+�e�G3ʒ��/|!Ϻ< l��LJG��\m�9|����v�˦#^�P���G�*i!OO��o@*V�~!&�څ� �q��J��>N6�>&:X�|��{���l�BUV��Lz���w�S���^�A��
����*9�3��>��+�L��n�H�r�.�nN�	�&�G�R%��I����e�F����#���	瘲b������d��$!:�=�E0{�h�� t�>bz��c�@� ���c��P��a�,�_�s�=T�b�t6w}�T�\5|�>CR�H����`�w��x�\��Dk�k� �"��
0�I�.�|��b�<�|A�XՐ����VŮ�"P����J�4Ơ�iC�2.ǯ&���:k�kB���iN6J`��S�2`f.��d�JT�Q)��y�aX�	��^�Ŋk��b����,����_h��w�yK�	n�%��>r��5="��8+���D~-z��e�����…~	���A���ұzG|l�!i{ȏI�Z^"1@�mJCE�����o�����k8��Z�,���E�盟@��B;���$������`��n���[��؀׺���
�*�RZ@�cJR�7�E�٣��A�']>2#,�Hr�Ȉ�{
�4/����X���Q:��H�˞��X�7]���0�̤��\�^D�	� ���4���;� o�n|�VG��7n�i�u0]������lp�ȩI]��V��(?�s��?�hc�%y#���@��$x2���of%S�uj_�ipӰ����|3;6ez޵���yHȮ�����iH6�2gvNj��C�<�X��4|�-R�o��F�^� ��0[ɫ���B�U�Lឿz��N�@#��Ƿ߰Fʾ�E_xv/繼'�&v��V�|��/�.fr�qT�QU���B��	O"�	d��~w%�D�ʳ��Z�.�)�z}K�^��UdA_�A��9��W�c��\�-)m�!$G=x:+�1�N�#��%{��xZ%��>T��s#M0�k��l�뺉�I����hկߨ��ŋ��_@>b����Ks_�4빻��B��P~;b�)Nd��]���nD
s�oXB�@��O*e
���}N7����l��j�,>����:�ׯ����}���FT�nZW���2Ĉzh��
�8�A7��U.�O��ˢ ƣ���	Qf[�̰���/�����4Hd�T�Dl�Y����~������ɛp����M�M��k�m�I4�	TL������G11�O΍�ʽ�7�>�Q|p ��ɽNg�M1�HҮ�MOjO�\:����"���P�u�(��|�< ʱ��8��m,Y�_���\��/�~f�D;�F�O����R>ہ�5ߝ��~Xt��
U��"�$���v땗z|��7	k�p��$g{�؝�I�G���H�Mw��B������F�`fC	J��M�geu��9��U����v����(W�B�@���ʹ}���	�d}�c5��_s~���ھ+O[p Y�b�׈�;z_�}#�ԍ�@�c��Ê!)]!@��$8E�^l���6���D��4�Kޫ��_m��P����_V7����'_I���蓏�x�ysD;9��!�P��!����b�g���u@a���i�]&�'/��Q���p��ů�H�}�3��S�&g�Q|p�B���á}��w�~=��\�k�I��a��	JY6a�}A��*�E]xvw�����(y��c�.�� �hڗ��Owx�d7�<���=����`ʳ1����!���xO����[c�MI�'�H�>��)�+ǫψnv }Gyp¡5C�WL��|��3h�ơ?��!�׏Yf{M��q@������z���s����5�G��u(�S5��9$���_s�}m��UA<{%��6�sr��O�?���;r���|��_q���'o����S��85�_��XL� �0{5�ѥ�ݭ��/�vl;6{��3z���nOb�fo���sH���|�C���M������'?#lJ�s�OD��~�τ�=�Wt�N�
��|-��� Ԋ�m�w:���o�h֪m��I9��nz%V*/��+a؞��������Z���"�7���k!~��4jvM���T��	�*k�I���j�1���n�hO��O{��qv���
��|z�_�$"�����ք$��r��j���i>&��e�l�a���O�/�c�e�/�c����O�oO^�_�!�be��g����㌿��<�w>w�5s;��I��A��_�}����P��ܼ��פ���m��_��%p��
>H�\�>c��sg�ދ�H���*�c�<y��3'�1O���n��<wG�.�z�m8reu+���V��ټ娿#ϳ�*�������W37���O�����?|��=�����>��𓁹�ʃ���_�/�_��o���ߤ�08A��Q����g�5q��p(�D���n5#r�1��d<��N	���e����!��^��$����;�f\n\����~E��ףƯ'�I߃3��q�>#�c7�o���O�@M@�С�#?C��vY�>�?�'P�g�u\��q|�z�{2k��k��_�5�o���#?��:�I:u�?G��<��>g�ϯ���\ꈄs���د�o�ӹXj�6򬰞N��3�7U��U��3 ��e���	��}�g2�y�5vFɔ�g�c'��#�u�����W8��ȯ�s��d}q�����'=���"����_ጾW�Ŀ��ˬ��_G9y���g��|�׳{��t�g���Tҧ����B�k1}��Ƭ�G���L�����C���O/��7��_�;���ؙ��q|���d��W�?p������:�#?>��'�)u��S� W>�ŬG�3�^��HP�C������
�U��V�	=Z�^��=��;YB�4wML)y�kpJ>�,Њ��Jwo�@�����R*��C�Q�\�D۠]w�lO�7��kPN��k\��^�S˱�Rk��!Bt�Fqq��+����8�V�孙,�V_���@&�����X���bz��\�Ý�!�j��a#����D�����0���/����t��OI}1�b3K7��M�z���r�yi?С2�6�Bპ����^���F��Lg���wo���Np����΁�]L�_@�ϒ��C�7���
��A�T�%��^D��(ˋW�;���$��^A��a7�ٱ�I��0��K��~B"�@������I��5���^���5X�kЀ�ם�F��#�I���k��GtV�F��rp:|I>͈��я�^�_��-������9|��
��~p�[���B%�Gp�>��?9��39�z�P�t�((�EO���璷���ʣ#��A5r�~���X厒�� ���Ri�<xJ�Oyh��B$�����c%q�b,:�,����W��<��׿ȡF�G
���(�W��$j�}����{���%<䰘��������/�s���7��#������_�Z[r��]`��G�T�������<~���+������R�>fO��j
�7�(W��}�Ŧs�z�a�
�Kt~��n�R�gV��6�W���:���d6��Y�����syeГ��w�/�*�	>�+�ɷy2<���J�8CjT�7<u��UIE�a
N{[H
��Q}�e���w�zq�[_JQ�M� ڴeg>�g�
-�`KՐW4���&̊X�L|&Fa�!���Y#�r`J�!&�}d���P0%��� �u� ƶO+69�����cZT���n�W�1yJ4��m��#�6c���O1������iy7yG�h�S9��&���ﻄ��n|��7�Z��Ƙ�譋��-�;�q�G������ _���D)���T�PȢ]x��9���G9=A"x�r/��_A�s/��](Q��X�(�����'��L�Ԕ2��<1�N��B��*<�_Zo[��A[�cF���D�~DS9PN�R��kU�)k�]�ϛ��i����@*�����(ܱ�����Y�o�����b��IH�\k�4d�$o����qmQ�x�?�V6��p��v���W���ù=>yh�A���$I]nQ\�7R-��3�����*��K�mS�%ܴ��/�k3���l��@ɏr#����}��>ܯ��ԗ�=T&#�H�'̦��?��2EX���[���"S�i���8�����ho�g�a��bSZ��4��?����V���Av�$p�n���w�3�z.�N��w3����6�PS�R[-w98�F���Y7}t!�&B�M�=��[����;h���Ȫ��:e��_�d��e��O�VBbE��
UW�p�g+����_8~> ��%��+<Q����iv�S&t��3��cp���4YP���������q�dmV���c�@���4�Dv����c|/���_;N��A>�ӐcC_pwi7�i�C'Y!8�-�z��U��1&y�'�hJ�m~!>���J���%N~���TK�b_�Q�2d�S���A���p�L��PE�\?�1̍�֔��!�|��՛�|1	+/Bgg�@��
����6}����(��k�K
>
Z� �u�n����M�����zm�XX.���|��9�U𭣌
Fl��r�J�u��G]����$��{HN*W�n����:����N�Hj�3d�v��i���-�?�3�be��>�߬b�0���|•TA��>�>��`�F�sX*8��.W�O���gݤ>lK�ۍ�y�пvÇ�����P0):+/�=	&e�����,�x��a|���)�B��'�ʸ��5>v�9��H=��H��GuD��~*ҕ7��`ǃ�#�6�KI<�����yV��#>M����9Xtɓ���ï��ta�I8�{���W���jɃ���H��9\�ݱ8�p�),:O}&���'f"�5̛:�V����)O�Fp@{�����>�# ��S>;ܷ`Z�T+�_�M��͚a,��f�2��Vw
S�
NS�I���1v�,���1��V�Fy��y�-r��GO#��=����i��0�F�6ƈ��Ƨ�ڛG8aCkt�N7�;4P��j�>�������*�E��g�b�&��=!��G"��`ⲫ��	��8F��K�G��������O�b�Ӌ�X�e�W���c���u�(�s}�'�n��O�73�05�~"��U�1L�8�\��걇q�쯳�!������Uv��o��&?���dUׅ��zJ� �N�zx�n����7�0�!?�;��Ql�����=�����dԘ�V�d��j�[�8�R�)|�ΐ��K���7�cLj�CY��#9\�c��)��w�@�X��)>
QP��x_��4��l��-p�{f16�)i�d	:5�����5s�NX�3���0:�C#Q(׿�Ǐc,W���7~t�,��x���E�.2Kg�4NM�K]�}�N�&�u����n��s�bz�N�wn2�O����·�s_">Ph����?��/�F��jB~�*�@���m�D
+��Oߤ�d���w�	7<N"�4�W�[�n��6yK��rk�Z��R^���7C~TK�V�Y���g�l�p�P���?����6�r�������W>3�=H�����a�=�����S��n��T���f|��qm���M�>}`�8z���g��kl���V��"���?%��_�6�P9z6u�'��"}BM��P��yM��6��V���k�LL��llO����{�]^]�����>=9˾i�&tK~�p���0�E�R^��:|
��z>%�Pz&��S.|�+�[�	�������ޫ�<?��(�귑���L����I5�Ο`P��y�ov#������N������[JQ �1m���d��>|�[��b������#��(�z$��	���;�F�F����)3�������~%�s�NL����'h�Jy�&v�IHy�� g9�DZ�C�8��{'��7a�N��mD�O������ԯ��ٶ��ɒ�������c�>%��K�'�/O���)a��	��w5���1���+|�_ٟ�>	���IL�������L��R��'�a��й~�J�R��cv#8C�䳆j4�ή�3���	>�{���#>�S��0�Ĉ#}B�y��7�~��3������ȏ����GN,`�J�����4��Va�_��5�,
�Wp��H�f�S��,��6�B�hgO1��ҿi,�r�qk��[`���`|�T�	0���xd�Vl o:���_�Z���c��ŗ�}�;�r���=����\��Y�S��:U�O����7b��������jc1k&��[0�	�}�n�w��r��R4߃JD��<������S08U+O������O�WuV�iY�H�c��Pg��a�F���*%����*��|�v����hL��Vw���D�����L2��cXp��>؇k
r�;5�ʻK�<E噐}%ə;��Ć�k��
���^�r�O�3�m#<P�*�O�E�2+�ӝ��|:�>yA��2T���r�>b*�P /+�=EgA�4�s�uNJB O˜�I?��	8�_D��Ǒw�f�+�:K�f�8�2h�P�Y�;�e�:�UoV�Pes OΑ�DuO�߰Fy~`�B$�e)�}�e�T&��tS��b>T*��|���M�^�ngG��=�Dv,9�#v��p���"*ɡ��-eO;�KO�������04�q(7!.S���g�j�律3��@�2$�/ ,
�2��t[�9��[�U��f�P�%��d0I	�p����.��rȓ��_�{ѳ�3I\"]v�:�}.�J9j4n�� $���	%���_�\���zm���JX���ܤ��� ɏ��w�~�-�L�����FN:��[
ۆK*~�v�S|�<(����@�Օ�_�h�X
����y���bP�TO͋$�rem
_��d���(/FP���[�
Iz�[�=�����Hq�B��M��W�A>��q]�՝J՟f*�x��:8��������/T���������X;
����ޭ
2����L�U(�OH��$�;L0W7��w/ͧI28���fyĿ�#9Z�P�g�Uy��8-H�f�rM8���+���g��+��G�2�w'����qwOg�Г�&�Σ��K/�k�
ٶr�_b�Q���_��H�ۖ�Wou�;�
����A-��۫�n�
���ʿ��ۻ&��I�GAA&�V��j��j$�<7=���&�7�]���[AW�"��y�a�~�C�iwm��Q��'���pg4j�I�������BT���;�V_���oK~�dt���\��CMP�������[x��QM��,���q�>��t�1
��F���8o�s�ˈ���M����UO��2hʗ����lyֲ���M���� ^�E��M���N�R�S
���_���G[�?dP��&��o&!���*��+T��2�]{Ld��g�Ȇ��;����ZK:��W4C*�%pCć�m���Eb)pӥ,/��_�\$4�QA~A�����(lj���@qS���l7���U~��H�>H���J�!O	2�~u�]�)���#�|0�
�.��"���fAX�]�<벷��`�z�2��J�zn�\���4Ѳ<1FR�
�q�+,����o.����/PPb>%��0�6n�޿��,�������r4�4k=3����}�p_$ݚLev8�O�<H�������B�,ϝ~���Q�N�8Q�ق�&�����T���,m
��h{o�s�\ç�"��>;f��@0;�J6ۻڑ4��ѵ5Mz�2���0���t� �9!��U�������)V��O�*L�T��
OYA~b�ݱ���>B]��T,�i���N�Aޚ։�a��2P�B�s��.5��tB7tm���!�R�oo�*��3/��n&Tj��2���Ƈ�9Gh��<'���{��+���9Ͻ4�$����QM�mGmyX>tbi��T�K#�r1O��O��xÄ�(����"��F#|X���X��c=\1=�ꈘ'��[KBM<�d�W^Y�p��i%� 0��&�2����}�Ԉ�8X��d�@��i�-���}C��*�7_����ۋ8�45눛���)
�L8fG�������gv�'��5s�Lļf����-�>���ĝeO��w��R�l9�k�k�G��z[���e4Q8燊zA�9Ӧ|f�7��5�@
r�>mo�_:)8����E��^���6|�c��kmo��2���=��[�M��Ɨ�De���$r%�|fO��%F��q�D���&g�� &	!'M�ɏ�s�|%�O�Y��q(���=�Rδ)ʔ���%}�m��b�ئ��|��>htJa���o�v��\J=ۥ�H�8K����o	~�
��+��V5Lq�O��؋��3]�S����ﴗC���X���Ǔ�OK��%q��"�E���!�s�����m���������=I~p���ÅO�t�����g�be�;��Έ>�j�чd2���7�&���1�b��F��Ԝ�d�ɉ
��/�������$@�*/�͔���lR�/|��9ܩ�:E���F0�<��}�e�oZ�X�֌��a_��õ����	+�&�r�W���������'���h{{��LfT{�*�e��OJ�p�v��\�Ot+��l�D:��.*��ʱ��U�$-.�-8�����pU7HA�%�l�OAt�����G�0�8��vn�bA�Mr���n5:��nCO‹��-�m�����:�D�>��Ei��D�+F�p!:[X"������"H�̇���|�9�����!���C�	�1��\�]X���0	c��+�q�h
���K3�5�K>տ�|�,�(���mI��J-r򇈥@(w�)��qB���O'�4�쑺�i3Ѷ7�@.|H��[p<r���/N�bT1╌���E����vM
�4�
�a�]�)):�Aw���ؗDJ�,�>�G�&�W}?��p���g��/z�<,��@"v�"W�\_}5���Z# ] 2�Y�I]Z鑋opy�*J��	z��!��_��9�}4�� ��D�0�Uj���&����N.�z���.���Z>�g�pژF����%K�z�r���_BQu��[����W�2���:�>˞x]�͝ٹ�0/��#�+]��//Ϲ�|���ZR�� �P�-��2�j��~i��r.�J%�o�� 6����$��%�>�-�.��szrތ.��0���5�ّ�|zʏ�5:�(�ϓ�p���3�����.�E��G�Y�(�)2�g�/�`�z��bz Ӟt��M�ů��k��ۼ��b�~t�T��Cl�x�
�_侱؟��䙋�[���ٍ��!yO�Q�zd���b�l6>�W�_馑\�mc��7�PŦx+�Ϟ}S��G:kn��XU�(пPM����8�[���D�B�)]��������^�S�D�m�2��F$�f�^>�>aRy��85��j4?��O.�R�W���D4yh~��H>=LA:�3Ny7<5=������ۧYA��%ï���)��X)�*{8�p�)p�s�Q'yn=�~��6�'�w�_��_=�3�x"?ӯZ�'��O����'h�3'yF��<'���<:潛����	���	�����k��W9H�>��=�3����Ia���s�ׯ�\pO�����gb�җ�;>���ߣ��i]f}1�;G�
�B���D�}/��闑>�U��j:�b�.9d۬Mf����~�?�����;;�IY&{<����u�ppЁ��
Ȋ��Ӵ���Hrڼ |xm��1N�ݣS�V����5��#˴¼�B�3Q���{Lj��~�D��)#�#?��Y8�X
�V+\5�k�u���ڳ��>�ό�8I��Wj!n����v��Wӌ8�ѹ2td̖8��ԆO�Ss/��X�K�c�[����V%m�jȣjO�L"
f�Tf��B"):��Sx�㇏u:r�2.��ۋ$����#��kD��!4xC�<��>i��v�O��Kf�����n5��hWG�5�i$%�X��]�3�\Ԉ���Jk^�
G+���=�z�#&�������_���{�P�]<P��.+����7�G|��C��j�C�;�_�M씧�*����/�8y��/�����1�Mq;��Q0���$%c�lD†��4�iJ�U=j;��d9z]L���>
��A����DH���$/X.�C���X����EyO��f`O��S�g3�����ݥN/���0�_�K����)�{�q4���8����D��}��賯��h�G�#��;8c��7m����UT�Ht�շۄ��O������5n^U��Ŵu��^�.+�e�Fg5�y��/*
\�UI��ED+��@�4�ĘE�}���tź�H�Za̕����)df�Eߢ�w���8�k�{��U�Y�Ka埐�_F}�O�-�Wl��_�����>�E���[�9	'��b�B�bk4\�A/k8㔦[�k�[�[˛2�PՉ���W����;(�є%=m����=!�2D�|ea2��W�2*@&{�6���ٟƋ��N66y4��
OD(�Q}�tX��XMV����5yg�>Al�I����fȿ��3����0/�Op
�?|��iڭ�J�}��e�#ǝ$:l9Y}?�	��
�l��M/H��A��`|����}v��a���y���)��5$E�� ��h����lGxh9�~=��/�>otA�ꓲ6��+�6��A+����wM��B��o;W~�/Q���׋1�
��
�?|i��E/NL҆����@f���-;.D��<�>m���G�'���B/U��R�Dc;Er?L���i}M_�س��O�H���͈6��&�-�, �/��'�0���>�P��b��kag����#��n&�� 3���CF��M�?�WK[ވ
 E��&�\"��L46Cﯸ����T�+�N�؁��Ј�[�!����*'T/�o��C���`a'�W�fL{���ߗ���ȕ&�$:�A�~���7���A+5�	���7^�/%�W�b�.Go�����u>�{���,�{H�֜�/�ܲj��ʧbN#"����f���D�xJ�H�j1��é/��	�N���ϳ�$��-r��Uc;"
�9�υ�}}�
�b�8�8���)�34C��`��$&�|����+��G2�H����58�1� ���Bb074nޣ��b����/�\C)�|���/{3p�G�5Ç
t�@��&����ˬ0z�f�wo�X�:�ܛ'�����5�9��}�[�tL>bS<�r�+!V�~���u���բ{���L��T�Ԫ��lLf��I�|]�Ş��M���Ԣ˼���>�Ug�P��1D�����#�{H���	�gҲ�t��m��%z�t���]��(�$�Q�NO�+��Ki�v�3�[��e=X�"��y�<��l=��w�i�"��>�˕�>1��UmA�_]Wd�H����	��)*��E����ʨ?��RN�B�	�cg�+bG������Η�hf�&�B����<&�S����s%m_u}��1ax֖����w��-�����G���7帽��Zko]�T�y��/��1�iQ��d��@
!��@�(%��y���*/�.1xN�/��8o^Exr�|�����N��B�&��k���Mb
q��|����0�~{Q$��v]�Ĉ�v䑥�m�*��ѿu`~m�e����]��/�:�0&���W�iצ�����7G��NQ"��MV �����*p>8b��ԟx��֖EW�x
u��ы�E›�B�HHӄHM-�׿�+�ōϢO�:�pJ�a3������#�����b��]$����p�38��ȧ�S�c*��&�xG�G�]��L=�|��ŧ=-����v�0_6��5
�b�{�\!~�;�r�tXa1b/мp���F��96$u /z��� �$���hy�kf���?�ysu��]^^��֤������Q�]�5mU�=�U�J�z���G�	��s�L|G|ê�t_7�/]�!�#?�U�	Sk-r���L�ΜcN
�y�3�_�]^P"�f)5�"��C~T���A5�_}�)����m����V�/��_��P~���~�*k���ѥ�;8/~}�L���d���~� ���àu[�է�.���l����*�xj>���I��,p�>�4:H�P?~T��ݥ�酦��
��=��0|D!4����Le��(��G�C2�)8f	���9?�_s�>���
��|2�y�ۛ��%io����_T��?��� Ҟ�D��s5ѹSY����;���5J��I~�!�m�(O~�E����@�+#R
>j���Ͽ��C	}�XF���[̨��wk�O17�p�>�2:D���L>׋�3�XP�2U�E�i�
����?
���an �˗�+�J� g>__�R^�;��]�е�B���W]la�!�4�4�_b�Γ�XI	��󣧿��;�\�T���^P����w���VJnA���w�UNZ�����K�vAZ���}ЮX��=�"��޿���eټ���� ����k�^;�O�eV�~��?~���ᒤ�p�F���ǿV��W&�U^��������4����"�k���y1�f���\�b��8fb�eVI�U�Q�V)Ma"�>]�D������e�):Çw�������=��:�� �z���碵 ����y�t�;���{h:g��4���г�������Ḥ����?������
}`�>�>w{��X�ф~�����h���tzں��&�dh�V�>+�:���	���l����ĻŚ����������NM�t(yO"W�|���‡~e@>����\|���G��˕�?�1��y>�����ԇ�竏[~�%ryv<E�.BR涣�c��E���1d��38*�
����}�kͣbSp,��������ҋNq�|��a��nG��[L~�r�D���kZ-�2�����gW�z��r��@����ȫ>��m����O�j�}!�/w�����HQ�}̃~]�_Z��V��!���B�ab��pӝqt��?��V�9|�}6��}f�p�)Œ6_UN�|��)8:1#9�����ٻd���;�I������ō��c���;yJ��h��]��X�I�yx����ˏ�t���iƱ_�3A��4�c�;U��X�gbn&K:(�����{����i���Gʠ�P��P-X�7H/�g*��:3�31m�uev>>��:�1}<;?��j�䗊��D^/�YtN~N�
���<����'�c=jT
N�j߼E剽�|.0}��*��|пuA�y�>����ʛ?���ۘ21v��xB�G���K��N�&_�:}LJ�W��\�Gy���~�J���Kᬿڃo�+��O1��չ�
�_��>E��El�3�J�F���}��f��Լ�c�ur:�<�����w���_���u%���~v����W77�U[rH����G��/�$1�j�P���ӳ#�,�?~~�/O'�I���v�A
Ny>nv^+�l�U�������'?)lV��/]l��<�X�/����iN2;��l!�~|7e7,�����rFB[#?�ԡ~��v�<ϳDVU�������>��̾�X%x���ݛpB��pz
L�[��'?��2]*�"]�,���x��\�!L~$ҲV��s���z]8��55����<��������{��ӧX��(�Ɣ���C��}ͱR���ָ�?S��U���o�P�a&��Z�H}��v��@��<�S�jPC�:�/��љ>������"��Wڲ�L��わB��2�'��w���gR;,*��_�7|�%��&��3��A������G��+ԅ��/%?�� ����P���X��v���N~v��bG�)��8T���|�{b|�
y�����'�~e�O��{�G�.d�O�W��{Û��Gn��V£��s	�y
ή���/3oe�NnI��O1����/�=E��~%�'��cP�mx�u0�X��kl�sG��B:�}0+)�9i���������Ա�$�)�Q�����C,T�c��'��dh����N�|�.�"NM�,�_z����
]`?�X%��o%2���v��f?PH\�п�5��W�Kuv����}�>���89LH��~�@��>��Ҫ�(�v������?p�F���'���!Vxk�J���]o�c��τݢ��%��h�"#��.�ޤ�׉�v'K4d<m8.Fg���{?~�ӵ�8�֋�S���i���/i�ϲ�*~��8���c`>��4&���Xd�_�W�8>v`c��'�{�EqLB5��QX`þ���$}�a7�C����g�]�����$+��@�F��픐W��HP�|�&����fw�F�l< J`E�c�d ��X=۰	�z��pm�C���1
��RVN`�Y��gZ�|H���0�/�����#�s��~叉>m���a
�.�_�K�K���cA��t�E��/6�g��=ѿ<=~�y|\&Ɛ���c��o����Š����ѳ~�?8�n����I]ݷx�Q�'�j뿭X���>d6>�X�}��B�P9�R�����z�_܉o*��(�K�D�t��
�|�7�0�l��/�&��I%�����FjW����v����~E�ʣs�|�2]D��f_e_�e�m�/z���
�ߞ�}�ō����D"��������~���ۨ[
|�E�&솢g��W._�D�}�(���nXg��3�J�g��֊���`ݝ�0V���bǦ������,^�=�x��c&n��X
1�(���w�r��v
�%��㶦o_��/��o������e�= ��ȫ�����+Ղ�@�6>'�Cl
m�����6���w���a�����Bnjʰ\�ɺU�k��D}�+b�AA"6��
���Kx}�]�)B��;������]��[��[��MY�P
#Li��%�_>'���R� �F�q��O޵$��_*>wB����|ׂ#)b����%0_:��2g����7��6v��˷�b���|�zdy�����9��l��/y�`��41orE�������A�66�I��beb;$��A	'Z�.HR�;��袿��\�2����n�ͻ�� <A�M�n����tm�]@p��T�dQ�
��yB1�7��p�˦�V���@�W8��tt)��6�����y����!�}�c���`.:1"A2�%����,—�[p�����SȌk�W��Fj��b�5��1��Q4�ruͤ��מ.81lη9a��.�\3K�\�i�tܘ�(7���x�rr�S_t�^؃T}s�@���~y$��Zτ��k�l>*�5@��G}�1z
��%*b,�1{�b�ѱ?�Y�:w�j|�s�����4:��ƍ�^5~�x�2��':�D�|s�-�JtF
�Ӫ}p<wa�~����� �g�r��mT���^L.��(y��Co��'5��#>�ݎ�DՔ�>�Gҋ麠d���O���M�4}��B�1�,�;�c['��t������į�������<�}z��Z��kT>�m�L��~
>ѧ�Oz�X~�P����9�d*V�	�JnG���\���(���㳆�#�sOD��'�R�	>���.�'9���K_˪q����!P�a�N,��w�}��g��z�e~�>GyNx>�sr2��A��WçX�����k�Ɍ�;M�Oz4���aΩ�5ȃ�B�r����.�C{�)���/��O�����*�Ǘ�Y�	��%x�"���K�����=y��~U9�Gg��G~�K�_Q`����S��8蜾'�G��az>ٟQ%�(|�sJY�����W��	>�2��٫�|Лy�]��}�~y�|�1.�?~%��'� �阅�X�obe��:*�-�>}�2g�qp�<X7�O���{Vz����������5�ڱ����K�)f�У/�;��#}����{Mg7�7���s�b�󎢹n:���������q�Tl�:�mOݭh��p L�3O�t
>�k���sM(ɘ���~���i�#�X���9l�
���_��$�:���G8����>©�_���,֯���`!��^��[O�+~%����|��	m����>���/�$��8�\2��(@=�wu���b��p�/��ϯ��q'�}�O�\����G3��>a�f�����̯�E�_�	ʳ��K���xB�b.g��
?�Ίi֊r<�+���'���O�ݼ�3KթBr����dPhG/~�B&:��>�(*�£���x1z���
��@)�����g�T�W8�g��ɡ�J�^g���r{����;���sD(�%8�s�ٱ�9����ʛ��+�/��_�L~����A58�/"f�x�0:�*?�ůt������8O�u��X^K�����ޙy����0�	Oy�_��|l�}�}�0��'�=����d��hW'�D�1e��/�%�^$T��_��r�&�)���F���#���}�����k���O�����;��>�3��_��cGOg�|f�u��ï:;�'��~�έ|�H��?����k�
~]��I��}��Q�������k���O��Scߎ���$��:�+�$=s��Ox�@�yg~�F-~�x��7C�ӯ#>	j�~����~�_�	V�)�Nt60���~����}:�G�YNpt;��t����b�,���7������X��w_hF��3��J�b󢋫8�mq���Q�Rs+{�u�5�ݗ��_{�Ӷ�����CS��T�����O�a}x;�=�3t��
�Y�VR}t�_P��J��ם5��{�?�H�#��]Q�o�5�k%˧����=+p@?�,����>�n�фN���N7��G�7��9�]Qv�Í7e�>(c��8������-���Z'=����V1+�>�r�L�^���Y՜��Σ���؎�I@]�srE>��~+�̃�����F��/8� �R�S�^��^B��UK�\�b���b�h�@~8���g%<�ӣ��^�/�r|\����z���*�=�By�]Y��2S�l�Š��‰��w��5��O��.v =AR�qt��>�W���s����q<�<����	O7�W�#XX\g�u�
-����#��'[}�r�=}�'L~��)��Б�+
�9a��6/8Ƀ�ز��H&�XN+_�?�n�������ڃKɐ�{w���=���=���h���/�$��Ό��M�<�����w�_�G!8����_�_Tl�ٱ}�ꩬ]t�J1(�ٱ��y�[y�ʛ����/|��nKD�U�r�PE�
���̎���5�}CL�1��$��_~��?��
���1��~=��(�P"ό3�����_�A�b)�F����	��E�}�b�;Z7�6o';��)ƫ�8�0w0��yR�Я�_�_�Be��_�W��mF�Ơ�y�bEч<4��OLpG���Y�|���z�U�6<=�y�-��;�	Lc��&�g|�!�Dh�S����Nq�_)|��}j�:�E�b�E�<7�Gf��)��P��'�}`�ͺ`{M��0��v�)�QX��O��Ѐ�#����r�����ʋ��y��vi�z݊T>���x��sB��r�kw��F����^1����o�}Qb{�S�1#3�������.Go��F,�oRh�<�i/��?q��ŸI�d�y��W���~�X
b�4�|eHbG�d(?��?:o��c�!����t���G}�	�/�T�#�_���&'�_���R�W<	8љ�b�����O|Դ���A�ѣ}*{�
LL_Zs�_߮$1E����m�n��޴����ױ!m�/.`�-$,@=��ҏ�dy/���m�#�\�C����CS���L�}kf1:�,�x���������b�GS���(��؈�@2q;�
_�!���[��ɿ��I}q� ]�.�B�G�FEj��N��A)�� ��[����ͯ��M�~S1]���#\��&� ���X �v�}>�e�[�=	ZQaN��"���/�ϊ�Wj;�#m8��޲���e)Z�o�l_ҙ��W�FM�П7R\͋�!0΂_*R������^�1r.�꾧Uxw�m���yѧ��ez���iϜS�ܳ�ʻ���UU�o���`����G���A�'Ve�|{r*�:e����kR&�a�HP.����'�]�p�"X��mZ�kQ�S(c�.cG��uI����_~���>�9L	�Cnؽ�b�:�B�w�Lp��8�t�"7�g�9�@�Fj�����^TvT�I
 �o����I�7WW�N8��]�%�s$ܧ���7�g�G
k*x�OfC�5$�>����F��7��-�kz�n�9�Bkom��e1�P^����!��ǚ�J�;��\�3qF�b\��W��s>V6=�Ӊ���"&P<�L�YcA�G���7�R���g��Bw銄m�w�=[�O�2�t̶Uk8,������O�JڢO�t�O�f[��X�n��/�&�Z/&�p(��'����79�Xl�P]'�r���]�,{���N
�;ʃC�Ii���u}�[Z4ʇ�w��GjU
��?�p�Y#ꦏ��2cj)�-~�W�
�a�����6|��A�T�H�>8�L�R�x}�ae_I�P%ѷ��..��>�{�Ϋm|��{�#�
��0Ç�w���*���J�	ᠿ����E��.\�5^�2�v7y��!o>���{hb�k��C�����HD�۵~ɿ�i,�A��>��/A2eĐ��/�@,�H�>��4
�OyH��BrF~ʕ-v'�P?�qp���h�Ow�hNQ��c�]���δT.��$k�g�D�'yI`�.ߩ�Q�:�%2��r0�8f�?��F�e����@��<���b&���w�j�|\���@#}D���H�?��7Ӊ�|��
D��9�!d|��Oz��X̡L�)��Z�����C��u<
�:&2��?�D(��RJ
U�U�J�C$Ni�aB�&g;�dy&��MQ8{o��y�c��b��ks��#���#���^V�9ʜ%?ŘR�b:�N
O�j6��ʻ�70��4-�z����*����K��_�X��,��Z�p@��y]Is��b�f��D�s�>=*$_<�Zrlk[���~Gc�t+��jE9��2�X�Ç�����-4b��/D�'?Č%6�����]�B`A��u{�Nx�Nׯd?���]Rl
ҵ`kI����g���h�t{�/Q��ׯب�hYh�!�%�����@�@�Һ���-���h��OG�_ԟkJ]����bD��;�n���r�t|�@�hp����ࣳ��X#oD��M��
$��X"Q��2��i���Kj��h�G�u���!�]j��x�����k��I�L�[�v`"���{a0�*�F�����t�� */�o�*^���4��%���"��Ҷ�u������?�3:�t$�2�pˣ}7V1+�� �8J��>伋Z�*˂������mq���P���/�Bd�!��ՄR�����9���$LJ~�n�
�+��S�@/�w$��]x*4k3]�.�T�����[^�3)l^'�
����#2��ߔp*�f���>|��}��C&�R]ȁ՞�]�*�0�&[�o��n�'z�C!��Cg�!�U�l���/�F{iێ](���g�4���S>��?w&&�8=9�����p>D���!��
+�T���쪒�gL,�����T���] ���!u����Qh>]����W��#��;?�d|;��6�����zjJLB�!� ���r)�a�qA�|�c��>��>]�g�z�O1����N)/(g�[��+"�@��yȡ�v�6�į��&�
�R�3������c�Q�>J�:��8�������Ӌ�<�yo�8}ߏ�ù�'|�g��i2pd��'������W�����9xfO�5m9�R
��;ґO�a���!�œ���ϊ���;���|����Q�#�I�NY='f��2m:y{�_铦�Td�_>ǔ*���|5'�b�NÔ�o$��	���x�_�2�Q�;a;g]*�X̙gN��\�q�s�<s��ʍ���D��ؑ>�3'O��if��!T�y"σOD��}������I����?�aaO�+���Ί�'yHn�oL�j�y>�<e��_�V�f)�R�����OII�?3Ф<c2�u��BOf]9��i��u��*�W�)�bt6�*V�s��=W��O�3�6�^l�Br��G��!1��_z�^��>������bPX�S��4��Ҷzㅹ����&?-}&�����/�W����{�{�F۩
{���(�Q��F�_%VL��Է�ʘ(�O~����
��
��������xf`�wjT�6��ʫ��ϟ�K0��~Y���k��}�z���(��n�O$l����L���A�t��:�M:K�(�ڬ��o�����r�k�����2S��	}��'��Q�q�t1�V{�*&I9!�������9
d:��ƣ����c�D牡c�p�
>�^�W�'9�3yJ�%֧����W����LG�0|0.|�G�G���O��W�P���/�1�4�a?3��ů��򣀌�EkB�M��h��ѯ���ob�~��R��E>vp���+|h5��w���n|�+|J��H�=Iӻ#1�+���ȳv	s~-r(|���X}J%uW̮9���@'����ɇO�-:���Ћ��C�?4t&����	�b� �s��W1��C��Etv�/;Fj�mE������؍�@1dL7|��o)��[����aT5;ȍӦ2��g�����j�
��_��8��. |�3��.�k%L����-��T�H�x�S�#��
��\� _N�+��(=v���τG����>Sչf��~៚?=?�݀�������)u�B��$Ϟz>�y�K�j�n&M(G�����v�\�|u�I��+�Q��(�_�(�z*��2m�ߧ��yo�b=�$��������zd8�|t�b���%���!ow%�Z�?��׮��<��o�|'=�d�#��lh�段,�_+/�rtx̥��¡�϶3M����L��ibaC���%�-���:�ߣ�G���1�G���S?��>���]TK�A�h�����+XP��7���=b��\v���3أ'u�q�S�]��MU��B�b���8P?R�o�+����t��9L��!N��<F���<�[:��C��j�'���o�C`�i��>]:ZTp�/��H��%n؎��mqdO
w߿�v��¿,M�!�+Aϝ��.�p
��P��oI��?r�[1C�_�.S;�I�z�9��샚�}jn��^�������Ų(�O]���L����߶5���B��6�r�e�~�C�ÿdR��ٳn�c^���<X;��=���y�l1��a�n�n�x������԰f�: �T�8�"zB�5�!Ε'�
*��-�0�۞�'��/6�R��-a�\�7�"+�=n������FL3��Dŏ|����	RS�r��ƾ��Sl��})X��;��q�o���z�=Ȉ$���l�bqɦI�n��9�c�g���R��7�/&m
|T!@��]�>�c�~�=�o���ծ��x�0L�]0w�ye�E���;�50欋h�=?[��B�����>:�N/�.r��1�����p�BW��x}�ɞ.���w��?��֐�\|8Ƴh�堜���f��k/X�NАzu��F�������T2Y�AWџ�o�3�,����i;�}�_������R
����?[+Ÿ0�bk�Մ����뫏�X��={Q'��,��fX?�,��:�@(�؆1J?�!��%��7�b��������L�GYj
��v��P���$��>?t��7��6�Ё�8_��*�Ec��ؠ��&�p��"@��Z��/K��ϟ����1��5��r��ߩ�^�q�;����+S9�G����2��qb�J�Tz��f6ؤ*1*v{���H��H���}�3$#�����5��6���L坼)6�V��)�y����$Z|��ޜh.�`�ut`
�2%H;&�@#����?M{M�Q�W}w�`�y�"R�s�y���cx;7cr�54���Sq7z)����n��w|��F�J�^�H����Q���G�B�ƥ�9P��>�`�ڿ����V�|�'�;P������5��m1�Ƞq<�E�����=K5�ǚ\H���\Q�ѯ��/9l�����1v����dh�����u�D�G!�I��*��gj�>p ���L����w;fG��#>M�rF Z>St�ϸ�J��'8�u]3D8��o&�:;w��w� Z��Ag��&��o�|׮�����/��ֱ~]|x��-��[�0n�>�߼m�!̮w�O�!���~]^,M��V�k�9�P�ɉ���~_oV���q6�[��6���G|�>M�}�K��|��.7;��=�fZѧ�.�/��Y�/v'~�;��>��Q����<1:[z�֨O�aχ_%�&���$�R1�Ke]h]�3E�5�'�iRZ��3��RHF����C,�'�`�6ׂ����)l���Dz9/��Re�>��7�c��*���/6}�Ȯu�~]_�)�q7���֨���[r	�0��Oaas�rΣ�>�!�G�|]���#r:��&�H�G8�����EptCm�2n?����ל^�}��{�߉q�9���q]8s��W(8ڝ���>��5b~U,|��Y]�>p�l�������{�ʥGn\�DL����=Ē )z���ى��!d>

�l)49ar���!wq�Ɨ�M���S쵽̖}������İ��O�������ש�nl�Q���c�nVn�>�+~q�ǜW��xz�$���������a}���TD����7�y{	��ڋ���	}�0`9|�FJlk�̠�A){�v��xQ��o�iR�B���>�,���׿vl��l��\�o�6����v�6�w��$�P!�9�u&X��X�'�c�d�oSM{��q<�u��4t�����/z�Q2r�� ����lN(�H�
Ŧ���:�sۍ_�<������O|��K~��Sw �)�z{c�`}�B�E3�����ýùFup�/$mo����3���@]	��7��|�+��MB~��=�c�%�%t��!Ͽ��R���HC�GG��o)F*�G�&?~X�8;��&�g�'���0�����i@�gҌH���dV?�$WƝbw��d�i�fB,�DG�Ư����QrP�#��_��>�i��A��<��aSȯ��.�;9L�:���l�>��'����8�����>.�/"Z�]M����k�>�]!Vv�N�I� 6.�3I/�C���g�+oo��f�,�l�{D��%�&��_L��#�d۽�l��m�L���1Q��ׇ��Bp�J�����/�(��\�5��~��qtV���_Z<�y��O@�n��`ז�Ǭ������]�W�9���/�'��M$�}�f����Tt!�{��It�uJ{�I�Ra��x%|��q��Bp�_�n��>$Ў���hkeE��v��w�T,�*�pù|���:NI�g>�=,b�����xz�N����&S�U�A��)֩B� ٹR���<�l2���D�
M(%�&���>��_����e���>�����A����t�i�}�k�ް�|3�|?�'�/��S^�����I߫��CO��w��?�O�^���­r��_��m�����'<������K�C��Wz
��a�����;��p�op��1��j�P���?�y��>Gy����$?�O(���K��_��k(}���+2�/x����#uk�����}X�Ӟg�M�C���p����+�^���I��Bw���g�?
��z#�0��Шl�h�`Z�n�ֹ�l���Eap�4��
��aR�CS��}m��}-�p���-�9�R�>m���b����]*s�avl����sM��f#j��o|n8F�z��(���3��F�X�m{���93���k�`�}��F��j����8�|X�*Ey{�ڵ�l�V^
�z��F���f]�!�J���@F�
k�TH��� �N�G�0|�z��I�GD��h��:<�P����6�~�
�����Q�w�q�-?�|�������
�]��^�d��vy���Z��cZ�_��/���Q��G�0M� p�[�)9̽C��~!�@���&���/�yw~�/?d��y�
p,���ǺR/nĚl��oO, EH���G�ə�V~x�Ӟ*!6IB�X���
�N
��ts-���y{u
2�):g^J�|�_�bm����T�}��mЂ�����`�&�;�J���O��kR�\��Sm�68��3�`ב���	�=,|�8	|�"���L�Z�u����]���G��,tvW9	�Tfp��Z���@�FH߳ctJ�w6�<Af��pf88{e�>k|� z��Jh�8/��e�'V�ѧT(�N��Ƀ��0'���׻f��}z���k���wm���S�۠\�SBT �=k7�}z�\�4�]�q�k(�wjK����W@�ruֶ��@��+�}؟�x����Z�/}a#Y��&z�D����L%����/��`:+tq���O"‰ԁ8������� >R�诃X��}����fw��.C��/&���ғ�9�:���Ҩ.0��4)0Nys*�	��"T������_�輥���_�A�p_>$8{
�‡1ݺ���yE!8�`a�)�mz�=��g�_vC_xMx�w�هP�?j��kb�r���W���	yC�V�'��.	ܒ�oS��O��c�WSq����`cء����R�=�eڻ6�~�0w�w����1nI��o�R֭���fU�nv12�6���H:���5,>�>z�2L	X������>$I����[��E�^C��I��z2�*'?�%���OE�+�`~��KHzB�[�. O9}�L��MKykp�e��$�r��s��S��c�������c�X�u@��"�ؚ��gx���sm�CV�{��D��>f=��*�#�N���]w�ʯ�&���ik�r:���"��o�r=�N+-+vd�:v��"o�NA���Sݶ����ʠg� �$�DQ��(N��׾��I�2��1u(PI�v����]������>�/�g��Y��]���r����rx��:�`����xg�
~dz(�S�e��C�Z*���r����XC�;�
�$���e^<W_���{���.FIK��&����u2'��l>�Op�W�Vc�V��6���r�hHN���HX����ƫf)9���Z��D#�. �{�VӪ�=f;�xu�j��Nͥ d3
���q%�m���A���>��5G~��}�������`��\3Q�o����G��-���!�s�+Ww���!�l���۹=��`������$�p���s��aJ��>�x���X��#�E}��t ���f�>�#����W_���a���C%�61����tklSA��娯kʡ	6���I�b�a���i�l����Γ��ץ]R%�����w�yn���<��-��UV�*�ryAx����C �شXB%�F[Ŧ�W��h
8�w��!>t
V�k�x�R�MM�֘m��:�����Eez��nk�9l��{cE��{c�����0�OW&C�4��F�Hh�_�-{W�IU��A/�7�W����t�EsVBS�C�P��λ�ү�+�g���Z�.��ݾc�b���>����B�b@�Cɳ9D
���d�9�=B:"{�4� l>��f����@4�J�Œ��>B���P?2NLI����tD>�����V�}�򵄿�mׁ3ټ)cp���7��_Da��$w:���y{C�b�j^�Y{��<Vq�4��^��u�6�:{�ȩW}���<pvII>�����^��	�0��C�:���=%�ե~�����dԉ>�����}���|�ۧ\�.߽�&�c�7�
��Ҥ6~���^�&P��>�)�u�nf���Gٶ‡�'�Ny��O�S���iF�k=�G��A%;�P�T{ޗ��Vh0i�_n8�7hp:��..$�G�f1�J���Ol��:;o4.�nf��V�Ч���xa��S X��;�+G������k�FM�ͧS����+�RG����3ySM�.����_�AzJ���/�΅h�;@���]�/�,�do�
�:޹����{�>��̿>��<��:H�C����)|,��e���i\� �0ʽ�
�9_��,��T5���Q��݇�s�2 >�l�����ٕ��˨7��RJ�C~�o�Ўb%�/溥�'8��d��mR|\�<�a9�d���A�턏>�����L�}��ʇ�<��^TvUa���3P�z:1��Ӫ����oҳG
�1��ύ��O
�/�K� �i�Y��D�i��b;M8vRg��2ZO/r����I~�$�'�ڸ���߰4�ޏ0|���+z����gTL��=,����ؗ��}��:�a��~ZyPHM��~���'��8��Q1��Ąi�B:D�ce���VP�=ߺX�
�8~�Q('Aj�;��T~�D��-���>�i���x4{��ý
3�2�M��-lOT��)�mcMy���_����.��(��� �+
ɏv������7�^&�F%$
��(�}/6Ÿ������%9�S�X�î꯹}�._{��[cf�:�"��'v�ŒI�!��ڄ8�w��˿��f�7o�ӝ��'��裕�3�#�\��-�W�d̼]��9�A:�����??4],�Iy�15p�GO<�D�Q���i%��/���T ��o�y8J�ap��j�lK�����$�����w�k�X�md�a��]�|��cw	~e��ۏ>��� Kp��e
���fA��,�&����&
�/N���<�O�j�\�v/�������:�2Ӥ>�ç|�������Gy���v���xX�7`؁�b��#��X�\_�?S��e�F�f-��|x}���	��|�|�sv�Sa'*�_�Cw���e@�
�O+����������~l;��Z���xd�ͷ���|�D䧁�46�;�`�}�d�^�w��	p�9������v��NW�׻�#6��F�������h���������iv���Z���F��g���&�����k�_��!?3��L��b/����dmZ7��(�_��;���O���YP�
��*�<��n�����ӄ����S,Ŭ+���>�yqy^��7e)�[��16�)�t����O�)M�����_�Ьw�w��;g&V�r��Y�h4��_#��7��|�gR����>qp�&�1q�;��/ٱ'z�P��~i�h�Ovc����Q�V������}�U���r<�O�}?��	��8�
ά֑Y
}F/���
�}����ԯ��\b��Ч�u���d�G����/�V,Ԧ�ǻ��$�3�(��ѧ����5��I
~�íղ�Ҹ~��i���vBy���]�3t�-�g�*&#��_>W?���5�σ�_^�Y����y�Ӻ�e�!�	��XQ�R)��tQ>�P~�z��R�-<B���P�I��_62=jw��3���-J�oe�F*h}���I��`M�L�y^�m,η�t�'8��������	���oIMԇ�if{ubq�<y�����^}/�p����q+�jt��ڻ��X��|i���P�~��϶Ts��Ti�}���e���ۇ��$|�]�ov,����J$�0^�/r�(��xAZ���e1^�����\>�_��遀^=�z�n��=��8}�g/��p��"|�!E��5R���-ru��ӣYw!D����a�u���B{��)�t(&ib�9�9��GL��.������"�%z��MݽRf���%�^�+�#��ӯ,I�~����2w��C~(#8-��_l�;�L�}�bt�{�`��b}�o���J~�K�Ą)/�HyG:3�_��펰�N�۴��7��]��c�WA�������[O��?�_��ؐ@�d�&o���_�gW�t�`�O�	;��b�ԁL1
覲����m�������:�D�с��)O��>�4&�}�1�Γ�5�7wv��&�W^�2L��}��Fy�\}a>�>�d�?��Q�h1�]֒��|�S��ߖ��!�TIѡ��P�Jy�1~�Cq���1�l�{������|�M�%����W����5o'��&B���J�㥳��b�0I��'N��:5���ѫ-��y� H1��J5M�ԏ>�-c�`'~�U��ܫ�FSp2��t�Ŝm�T7�أ�&�x�r�5��0J�G�	�J�Y�� |������o�@g�/}ӯ�]��^j�.&����!|{��CkRJ�i�1�]�O�,�O�T�[�R��\��>��X!1���g'�i�a7��$K�}Y�6�A� �'�ޒs=���9��O���������#ON*�-����+OI����v�o��M@joϫ|?^MH�D�4d-k�QSy�SF��*QO��1W��R�@��W�~���<�<I��]�n���]�])����
}

�_�]#.>
����(R��PT��/���\��b��!�*�=>�np�'Mڐ[�`}� �]Ƃ���E��.>5�uϭb�k���*%'����`��y���?yeH$�3�_>�btg�k���ԲF��	=�Jo��(>���o��kf�Ô�����?b��������-�jQ�4;��߳��~+�6�ʧw��^Q�=%��fx�ϓ���*��m^���_.�btV�$�u-����w��A��1U�4+�O1@�����"�Ë����EuwD��w�s����Q����Q��E��ðM�ڶ��3:�w/G(e�C�M�Ⅲ���	5��2��2�]�?�1x?a��:�V�a ��S��m�M���4�#��ȭ|�3��Z�6 ���Ur*6���/{a�k_��Tޚm|�D�g{3*��
��R
8��gMRl2�^L����(>��T��e�䓖4'͛��ʥ�>�g��E�$>�YlA4�����*9�M��w�]s��T�v���V�	Lyt�gx�J�/�#��Da�h>��Ā��o{}ɯE�EN���^�Kr�_:�Gb��t���%���UL�)|�l�֕�泽
�ܛ� �ϣ< T�E�|f���_�{{T�����͵.�]�R��O1��E���V���Y�NK�䟩��J���I�a�#��Oވ��38Ŕ�p֟�`�t��(���4�;��?gG��@�G8�1?EB�CLq��R��^5�+M�v'9M�Z�*O~�n�?��D�4y��s�3_����C�q�#�/�h��/$��q�߮IB�21��Tz���F0חh��Lv�٘���V�j��n�_D������n_I�N�>ܝW�̌��m����ib��P�v�/f�k�c�x>���E���$��@����.r��Z�\5#�R+�`�Dg�z��';�í���!�F��,k��|��=���6[�[�n}�R3;hN�g��Ov2�?�y�<L�B>��ooO�bh�_>c#�G��=�9o݋�K�����kc��_��ݖ����ګ��-k�f ���v|Wb|A�b��LM�.�.'7�j�i���7={��H�eٻ�b�e�A[���<k���L��>�D�U��#��}�����1k��F/�A��F���,�Yzd̊>��߄!�Q�����T_y��9���ec*�c�{T��9M��z��_u�ٔ���
;�>tN��3�=��h�>��Ϝk�(
=�tn[”���גצH�|�g�՛ӆ���':o|�[Z�-��:�5�. .~g���ۤ�~�ݡP�[�Z�|�����#�1��k"`.
v|�-gAry�>:�a�������Ƌ��䇻�0G�"���0��{��k{�>�"#X�lq@Tf�P���8����ه�T�@t�I�w�ce�WzJ�}�o���P17�Px��N�c�(l{��tP�uM>4�`�OW��m`J���E����Cw�:M����
�.ZvC0��u�S��5�ف;�1_�~��ʷ�?���A�����؍��ø�$A�b���$I�F�w���o�j�b��Xѹ�@ҋ��A��p�o�Ql��dA��!������X@6�W ��w�z,�(�v�hJʻc�X2��y����e)�L��i2�����?>֯4�yE���_vL$�w��Fj\m��t-��zc7�[�r�owpl}/
9�=-�)M�a��Aɳ�7�������<�s ����Ed�Z�+�;������x��ן� ci014U�׎Hw��G8�W��驻�߃Pl99��ž��Jٟn�������a�3�m/�U��S��筧�� ϟ>~6���6�EB���э&� �y��bs/���Q��Y6|l����ü�?�s��\>��cԋ�R"s>:8��į�әo�i�Ů�OV���~����|�>���|I`�sƱWtAxt{WG����B��3�F�6}mO�c��S��A�M�Ea|B��-IM�9��|��ڼe�J�i��̿��[�nz�x�d>V��B�ǽ|4<���=7�Y>���('~L�§��9�q�8���&���+��z,(ƺ���N�������Ly����G|	�̣N��?�:�����|��@sq~��y�;r��t���~0��eL�Y#?l�̣֞��+��mS����G���'}�0��0z�K	<����潳^�����(�?�f�7�8�I��C��H���р3wQ5ϩ_��IN�-�_�?�<1�%����Z���1��ot{,Ws�>p�z�'|���f��y&8Ų$����}�31=�O5=�ĝ�^�qI��s��9ҹ�Z��,�>�z��^�g�y�e�b�C��>[�����ȶ}�b�,���Ƕ%�	��nee.�)y�_�d��>Y��Ay���_ms���)�����$|�T��{��ʋh�OX�_����I��'*���q���Xt�F��yB)F'�x2��ӯ>~U>z�P\]�XG;+_��ǀ��M�iw;�F`bd��	���
��/���G��w��s�J�+�]ǎ�>��]~���ߔ4!<�E�W|����<1��c��t�Vy#�fb��F�U~N��Fǽ|H���/�_C��G�尜��ӻ�Ì<�qʳ�0>�<���F���֒�^L�j����/�W�O����+�	��*|�~5i��X��b�I7G�k������S��� �.�S��|�<ڟ��<���ާJ�6&��jb�1��f]�9�����'T���\>9�:(��B�M��[����Ӻ�q���i���G�Q���E3,��M}٫�^�3~��5�g��{ŠX���'�݉#�|G���\G�7�m��Cb����5N��xJ�'�/�g�(��Y�M;�L�z�
�&��?z�<�q����;��/|v�	�+�.Q���k�����+&�����ri&�>�����=�CǫJ]�`�q��}+|�.4�id���Jޚu�V��>[�l�|k�t�qwƯ�/9�O�'���%��Z���ß�+���U�rU�������p��r
d��W1%��@7$�+zg<���w���w�I�$#;?�G��Uw)�i׺ְ�PH(�_�i?�'��ge�x���Q=~`�m�Z^0y�Qv��w"�$�	~�+�/��s�fL_�.Ι�v�45�LS�ZL,QQ����q[��e�߻�]�>�vێeר:��߼"C��!�0�i@�<�O]�`q^���(���f�#�l��� j7�?r5^M��/�d�	��^�`c�_��t�rȗ�m57B@�����S�8͍ÿjn�BL��n^�1�L'�O!&J�Z���7�C ��J*z�gw0�Q|U�*�)'�˒с���L��=Ն�WDH>ѭ٪J�@��3w!τp��ϊ	��ϸ�_�K�TF"��]���<Uw×|3��*�>ܡ"��.k����^��B��b�%<�G�����)v�r��Re�KT�v'�|���/����6�/�y�M��R}�4��3y��;44��t��E��oS�o���ޡ�/B�9}~�tb�asi�.�F<SOB�"꯽v�WrA��~��<|_a=z-4B��w�i�k��|�%�ž�GP��<\�Ѿ�O���n�V�!�ɋ�(���XH2'�\ȣ��e�'�C}��x6;1֒S�g()&ƣ\6�I�m�N�	8���s����=zBG9���1���ݙ��@@����7@BR���6�x�u׼�n�>���fq��dʼn�\d�w��	H�B�m�'����c��@g�S ��@c!d[͖'�+?��-E��>�RXBdX!�1�(��򲡀S�=�S�ѽ���#�����C1I�'��!#���qշ��\�"(��j6o�L� ��B4��b_�gs���-�>�k	��1�[s��[��ԣ1����/P-?~���MJT���vbw
����C<�<�|0[$=+�L��Biiᓷz+|��+�(��c�=��	�S4n��>�K��3%���vX��c�O�.^��f�[�뒠�[׈����'b��sЍ��t���joxdXc7���n.��1J��,�ˢ~���������3"��Ń��
���O���D��>���ldI&ɿ��;f��d�y�A�[CB\k�}��'/�NH$r�(�jfz�˭UJ��Ln|(]f������2����D)�D�|�B>���~5�J_&�O�;�/��Se����3{�ř��Ky���cv�x_;:k"|$��>��<8��ux@�=	r��	(,����|N!@�_�[IR޲�mb
OT�8�k�E
}�~p�q�s9�7F��?IU&{�y�����G�iI6���1��'1���A?RJ>F~ ��|<�I���j���/&_�����ȯz=��g��W��gVD�;�C��mo�#�ߢ%������
�w��fe߹ߔQB1I�F�
��+L�P�cm�"�H��I(�f�v�'�5}�_�|&��'���|��GO�+aS�8��7��_C�vagg�/[���W�Gyp�W�T��D%|��ʿ��(?��~�6tnO%��+}�K�J�}�a�_tf�;vs�?Ƃ����?h��Q,*M~��ՍKG}�n�9�E�Q9��^��E�J~�����{$툳���|��Č;(oNY.L����`�g��u�7$˧R��'��w���Æ���ي(�L~l�&����HR�?�M�{<m�Գ���գ����/�wlʺ(v�]X�йT�",����E9o�?��ќ�3[���QG=u����	E(�S�Rh���ԯ�A��U�G���E��I�j�u���|X귚�,�c�>yM��&���tՙ�L����jq�]���vP���g�Q�*����}t
`��n9�_K�{��kR�X9J�%P�WA����>k�����IW)@&����Q
��>8�O{�cW[##�F�w���)8c�_G������9���S�J�k�L��-�K��jwK
�#�X�Xnh"��.�~�w�)+��
������O��G���9I�
S�qG��cj��c�_g�~2JO�O{fӵ�	N~�F��҂��.Y&+8�m�EG�b@��C-��
G��щn�~�r`�C/*O�դb�Mx��3;P���K}�H%�?��E9��i�xHl�!y�6�C8�'��.���ߪ)�hzZ��n�{Iҳ�r��’�ό�b�m{ѰȑZ����s.��?�Y�<g?X�:�m��H�|j�㇡���l��ƌǀ�~90���ڊ���S����s'=%�Gr���b�
E͟Ӄ�j4�T�|��C���l?��Ҫ����/S��<�оZ,�(3(����W3�j<����g�;�=�������?{l�/=��x���G�*�2n?�~0<s�H�N}�ʌr�OƟ+�i/J��w�5���b����O�\惍���<��y��g)����g�M�|ǟ�z���d����+�O2~^��g����~^��b�&��3o����sD贫��_��J��WNx?/�ԋ��a�)�{~���'o������<��4�)��e�=v�i��C=��/�k��ڷ��-��^x�La/m�k���;=�7��i/u����������m�>6�ښy1=�Q�f�xkO]��V�������-�mw䀑{xdm��v��vT���]��0+�}�V��J�=���q�^�霕ހ2�n]�moa����S� ��O���./���1��p�b\�����Bt��b\:�"<g���@��	�X�Z����/��S��3J1
�K�a��@��ǃt�����R�X��ښC1
����MIĔ�~�tJ.J���I���>�@Ŀ�x�ޙI]�R��\���e����n��Ƈ��R&�q��"?n5ۂ[1F���Z�*�%����W�Cu�F���� ��|�	�bS�yDk�ÏJ�	d�����ؽؚ���~&�a�s���˷���3�D�a�A?�c��!Ӳ��ʽ�ݏ�����k��槍�����U�FP�Q��>B��zg ������i��)��k��Ꮯ[,Ś�7������b�Kώ��t�J�����u(Qb 蓋���MO8�p�O1mY���g
;ڤ%�^Fj܋g�N0��m�;�*7cV�-=ȕIU�=��5͌��Z��
�}��{U��J8P�dp�$��s���I���+v�}�Uf����{��l�8�L�D�n����s��οQ�(�I�X����D��)����{�-&���s���E�H�I}h����ٷtx�<<�Do������'�<�;���d�~����xn��Ʊ��n:|�^�w,�Ϩ� �\�<��*׺h�^�	�,�ۛ�k�߳#�g�f�K;��ri!�޽��\+�&��~�u��$�}W��=�:@Q�'�3D~�H�&Kq������
~'�n�ti5Ww�مG�N�g5|��ߢW`j}U��N5�R�v�<��y��Cz^|��p!��;�x�_xBB��%����y���Ow�X�R2��	|�|W^��-jo׶�\͉$�DX�h���?'�H��ٱ��_�UŚt4Kb*'V	~��O)���N�ӏ.���nWF��/{�	Mi�ˁ��ժp�8���W�LxSw<�M��^�*��5���RN����D�h�e�:���ǻ�9��>�Kʥ��h�/6����.1��^M���G�\��d�~M��LJ��~"귢}rB�SBd.�G�p�D� \�" @˝eE^�|�Oϓr�E%��U��?�0�d����`!�Yx�b!�E"п��x�u%�7&6��U���=�h�����^}���>�C8��
"�_��q���w	�g�l~z�+/�ը�V�3���g�U��q�/��<FQ�_���j{P�M�`E-���1���\�-�վ|��}��eF�c�|��)��-��~��4)bDV��+�2�c@@�����.ٛa���OY�qJ����;E�ĭ�7��g��ݩc�y�2��>�_H��>NWh�Ӹg����1U���?�+S[�����s��;�}úk���k�ݽ�ڜ��^�<��bn�d�A5��d�񬞏f�c�n�[�V[Y�җ8���L���9=�J^����0++�r������
���Q]Q\6�n�6��R`M�*����L&t��S+�����ӗ�V��1"'��A+B�������4�g&1�; 43���Z|�t��ɂ��,�vSLU<�P3'�k#���
lVM�؅�=i��[p,4��S-k�4h!���YC9kI���!?x��OU6<�8�ޡ�<A@�:쇅�U���Qu�B��X���j!4K&=���z��P���+k��y|�{��[�{ԍ�?��R��E�Ņ��9\\�](�fI@�v���[(ew�cL/�~	��nVZ�b��X%�����.���(�gxzE8~�#��v�TvD��6vO����v�2���z����8H{�S���LJNN[�@�m�����&�?�?�5��|��/,e
O��[��skX�\F�	�K��g9y���oN<�k��j�}��[k���k�)~�j�c�;�;�e�?
S�	�~��P�����3k�-8z�U�-�5655��t?f�9��NO��'��d��腟�}�=����|/zdz�C><������De1�=8��#�+�ZՄ#�?x��Qf�hb�3�Q�~���~�R�o�osuʵ��I�q�4ݢ_��X��L?̎�C[<_�0Fޓ��ӂo{��+��g�5�g/�D��"�}ǟ�'_��>_����!0�:�|O;�g���5k�'?�CxƟ�0��n��� ��g�0�?����@{��db�
��<-�{g?�<�����S.O<���رz1�~�ϱWk��+=�~�.lP�+���02�.�ܶ�4{�9��^�:�PTI'̵/�0��Dkp��O?5B��p/��$0�5U4�B �"Z�������	�T��G�@
�[�uGl�F1���埏���葸;��F��U(����xV��X4��P�Ύ�X�Ʋxl�o�Ӝ� �^,<]�7��U�<�C����G=��mM`
~44B�뿪�%�	���'Uw5��5B�a��9�Lg|��Š��8��j?��H�X�r�?e�)�
�}��[�}�o���d,U)����5{���^��������Γ+��a-S�͝�ys`��f���?�x#��؃���+��6ۢ;��?�COx������Ϭ�(�c���_,HJt)�T�D�oo����o/m"��-fr��|:�>�N<�Z��E0�W��_�b�Y��t^��>1�x�˔k���
��د���܎��;;��#�p��S��(c�M�E��9
�1�Ó'�$�v_�M��<���|�~�G�^���K��>x�M����?�H,K�e�K�U�b���Il�S}>����g?�Bj~#P��8O�OyG��^�Ě��,��;/Ӻ������J��^�� �?1��6g�G��~���E&�^�5({�3���CzƟO���_y�^�?��^ӿO}�Ix�8�����qT�?�sH`�\�j��9������u�������+���4�Ͽ.~x�;��䥜11���e�|?L~�?�����~���3��^$?�s���O<D�࿟/�3
���|�{�����R�r�#5���?��+����6��Y��+�{�繓>��_s�V�����ԿZ��X�X��g��cc4s�SO<O?��b�4�կ��-��0(��7f��`���g�
l���l8�\{�
2v/doH���mZ����pX������"�G���v'1�R�B���ҍ�����7�a�/�މ���.�\�V�ȶ�uW���K1t��<|;r�/�]���I^�[,HF����N�7]�#YO��|;G��=��X�
��2����	���6��-�o�N�O��}������-J?���3�
�*/�n�~���Y��t������;&��ã�_A?��S��b��[��{��G��yԋi?=�`��[y`��q6?�iF3�-���,�cA�rV��o�\��߬�s7J�}�L܄��.�U�9��K���<=�_<�=qHF��?K���v��
�۾��b �z���Y��gv7ʹ�P ��v�$aU��=[��o�A�l	7{���
�~������8a��
?꧸
P�mw_�$��h��<TX�p>��j=T�Ěc��Ɉ�����h��pP�^V���={�g�f?����'����x�̼0�}�c�~�E�� PÓ{�?f5a#ϟ�\S��9~*�
��K�������Mk.A?��!L�ekucկ��K�g,A�P.��o�����B(��7
e��f\<�S��W�d��2�ĭ=���صvRl�����~g�u�/(�Ǿ��W�#��W�?�_S�C8�g�
m���WsW��?f\�4��r1��J��ߺp
o�+2x@N������o���;1�jd_��T�U�tm\�%��\:��R�X��W��O�f?P�z�p�z[�����D����9�uo�����7sx��ȏ�	�Z'�%��|~�z��E��k���3�&�)$��"]E��'~�D�$0�$���qg�5R��=���˗|�.����+�&���Ox|���7��zv5�E(�����K�牅nM��֯��5*�}���\~ɬ���/޶�b}8���H�^�Q��(؎��W��NݵDX�o�X�Q��wL����*zw2�PiP�4�[��*G㺅�7�?~�W�]�����m�/�N]�)�}����/�k�=1>-�qs�������c�#���7���?�Z!V���(q�=�i��J�}���t�R�2�|G�‰خ�g'�^N+��%Q��Ϸ�dM!�p�����]��Խ��X��?���`$��%XI`���#��;��-���12��t*/�)��_kH�|zf�W�^��K�
�'��l�`���w���Ee�)0K]�~|�z��z�o/��@�z�_���/��X��������Q6nZ�0t�T��a*K@�߃60X入��6F��ؑ�$�V�+h�����C�M�\9��u���%�O�D�Ϩk�������!u*�ѹ�3&��g����_�A�!1yJV'��%玘c����U��Z��AS�u=F�ώ�Y4��ۚ,�C�U�dPĸݏ�=p��3�#p�B!-��"N���h�ԉޏ��������N�cd*��������C�	��Ĵ2k�Ԟ.\_~H��߽��"�3�1���x�D�ܷ��p�ҫ� i�]\���G�k���ck7Z^-� ZF&1��[��y��e?F�^ ��?*N��wP����vi������b�eznk=�����G���i�)T�˙�7̎u�=��s�+y�p,r�5��d�%/���|��Y���4L�����A�6a����Kl
��\�G3�ӿ��i��a�sqU
�Ϯ˝3�~�/x�5_<�>{����kAg����^�PP���~z8�iF����a[U�a�WA⊭	�7��v��V��������~�
�+�G{X�f9ؓ�R*{gJ�oC�IQ�|��஽�N�����

?c�i��Ӱ����9F�#$�I��QOE�	g��S;+WBϚ�d������79����ܩq�,e�>wt(�}O1�P��9yz,I'��]���9<��� �t���|��/��?pfw^���'~rڞ�g��.k����<HS/�7��5�?�n�ޞ��J>��Iީ{��پ�������Z�V�P�z��O�Z;X���������cH<���	�����	�)�b�β�w��O7<�֟�?3��{��?���'����O�}V��u�W��0xf�z�{�b��O�9��eof�u�C,<e?�64�z�F��3���`�iՍ2}�c_A���4C������α��q�.��ؔ:~�\	��g��1���S�F8?���vk��]�VDvѪ��>��6�={��8����.�!a�,}�h'���r���7���ޡ,�����<�[��$�reJYu���;�C�2�rI�#ŝ��d��}��GzPMek���� 0*�qq�"ԟ���*��H��SD�	�>)&<&��Zl����ݕ�C���R����<�G4z�K����<j6<�CO<���kD�O>��{���;����L}�����1�^5����IJwZ��Twk"��Z�{uǨ-��4�b���t�m��N{�W�?��5�/t�ɀ'FɌ���o�W:&�7������B�Wdd��KwE�#]e�� �����H��V��7y�<��.��J:{�Ǐ��?,U���/:_�0��d/�]=�E2�W|���<*#�g���xj��!����Ow�����߉͞ǫ㖔d_��;����d<Sߩ�^���g�y��CEx�oo`�\�����,<*N~��a�	�߯h�x����1�-*,��������$?\���tx�#�	�5�S;6�CP���3ǯ��p�8��ŷ�G
��y��^xx.ㄹ�`ƥ�<%�����=��'ɞ�s)=����t�S��'���O��7�s����}�-�U�����o^�y�g�?��~�eܒ�؝ث��?�/��s<v��k�Ll
�/z���9�5x����q��^Ħ������B\b�<��`z<��H/�k�qg���ϥ��O��3z*��k�g�x��S�"�2_>��^�zȟv�NO������oz��V�s�t?���.���~�C�W��Ͻ?O��vcp�x;HQG���g/��?��v{~c��<O���=�<_����pZ���<�`~����|�g��������ſ�jT�|�ω!#���Qw��b0��:^/��l��@ �X��ݯ$�޸(�d��;v9�V��ǂ��+�����wR��p���K�߮�,j�[	�e�����Cm��SlP혘���M��rų�[�+��V�Hg�<�:<���S$������cx�}�^o���Y�W�f?�J�,�šw.���;z:�`��Ky���[�n��{ݘ�=��bA�g/b_����ď�x6�3n��Y��ܥ����:�'�U^#,��Ö>Yď��<��j2�'vj���o�c�:7bb���:옌�9���Ex�?�;0Us��;S�{i��g������Q7�-�Ag��y�yn1�-���/<�v��nh[�������;7���R�*�F��
?��s�@EѬ��IF����Ox�T���֍��\�m�^�G]�����?�O���(�`���C,Zx714���a�|k���	3b�I��[~?c(�/��v`�~�,(5.�C��gՎ-��v
��9��^�0�Ԯ�2w��3�s�����
��a_=w�c�X��*Vw:UU�b�N{i�����o��1K��������G����*��%�J%��	w�r��sYT�9��:E8�x�x��N���˜�m�f�/�c�U��uO#\~�W�|ՉAeԙg#��WxH�sNݶ����]|�~����9��8(v`��A��/{�]=ݹw�Px|�
O�M���˽X��~y�)_<n*~#_,���	��%T���w�F�X���|��@�\�2����Z��$��S�v,��	8E6�.o_��9&�9���S#�#�Z�*}-���8ۧ�+W1%��J����S�H˗q��Y��y.K���]�'�م�$�ߖ�n��I%T_�i�r�h�}� �_�j�����#=;v�P��zf�PؑBK'�=�Z��3���ڑNےz}�����Ƌ1:��+&=<��՗T��1�<
y�/y��=��	=~��;u�?g6>S�j
:_';>�eSGϊ�
?�z��\����(�C��B�=+�be�{�Q�翼җ���-Ӽ���Re�$z�w@5��E�^�&<�N����I��^�PЫ�(Yz�/�M��lC
O_[ar��2�|�H�_ݵ#
�0>J�۵W��iȨ�O���쾣�A
FA�xX5�-�u�=\�i�kXȂ�Z�P6����[ʻ"#e���M�=?x��#��e��I��+��@�t���D�%?t�b�v���s���
6�GL�! r(���Y��q�ƾ�	��e�Ѭ*k����g8�x�	���b�� ʧ5��z���}�4��c58 ��q,Wj
�*�#E��1����P<ÃO.ax�7��usZ�r)~8u�c��j��@P�k��ݿ�����U�e���#��[������?l��1�b�ދ28'3|҃��T��ڡ�������3���cr�Pl�e$�3��3�}���ֵ����n���A��s6�Q��Wq���9��O����2@s��q�q���l���n��!�=k=���z��1@�uf�tbi[ۊ��k�[���tό�=�>x���G�3�{�ˤ_=��a6�O���:��A僇Ԝ����؝����?q�&E'�����t�M|"?y�մ�����i�7.g&�C�1<�?��t�O��3�*�D�K���g+��[�x��-����&��'�w�I����sM*=���^�ϰ����"��s�'d��m¥���skv�ox��+<O<3�┋�ş��O��O�	�?���^��3��~yO8����|/��)���<�}\�=�O�S_�������\�07
'?g�Oag��^�o��<"���}�+������<�����=�)���?���|k�1�*�	�<��dU/���\����y�j�$��{;���ث�^�y�\��>�Ƴ���Z����%��OzJ,������;�?�Z�<���9��������E�y�M�o�~O��s�9T�%�KL��υ�?��vc���u�g������U����zRK�y9��+=����)W�8�TY�y�X�G<�;=]�}֯�z:�X�����#L��v�k75���;G����`�y���yr�=��o��z����-�H���\]�]�0��'Lqoz֞��V��#�����O]0d�$"D�EPJ��h1�G���r5�H�9B�!NV�YO�G�{5�sC9ϒ���s��ٰz~h5|+r�Q�y0��z�O<O���s��{?,=�����\�g���k�x~�W�ĩ_1ֿ�ӿ����+W����~�g)v$x��֏w�F��U|�\ڍ�K�^�3z���c��K�ǥ�_�E=`=���N}���S��َ��TG�p�x(ƥ�u.�&�W�ProL;��x���<㖩��+�����_�c��L����?_�qɏݯz��Ջy)�U���?ff���s�;��\�WtMLdê��s�Z�����&0��s�_>�L��wb�&����:�Y_�0���q��j�i�Ϡݧ3�$��9��̛f|N>�ޘg����Ɯ3Dx�|�ŕ�W��W�%J�����=�� ��{<�z(=Sӳڽ�N����w“��/'����<�R_*\bP��~͐c��x�q�����Z���i^��M�:�ū���gJ&}��˟�i�g_�H�I�.Wߕ9�;�%��\�����}=��Ͻ?�}GO8/駽J�?�Kz�w�G�o��[�;G�����jo��_�k�-�#=:�ni)�Ԩ��y!."�0�������F��u��z(�|�,>��z:��w��\����k�����D��w���2�W��I�}��'���m��1ҭe�W�/��yk���뵦W��eR�5�]�"�����W�'&Wl��Y�S���|�?��\�o�ؠ��k9��s�_m���CNj٢�O�$U�����?�ݧe���S:y��0nx���h��C|�ꧯ�#��5����N�	<�����O���;S��ow�P(U��#WE�O,���qԹS�U/��\4��|�� 5A���C�L���b��?�a�ӳ��)]�ւŐ-y�%�"z�[풆C� ������gν���������?�O��G��z��J� ��B�W�>�o�]T�?��$���[�×�X��s�e�����V�P�n��I�����˯0��h�P��\1R�E�����XP7�u(�=�޹#P�`��mw�ʵ:�3(�W-�}��ԞЏ��c��U�e2~��bz��6J��3=�	�N�U�΃���^�T~���p佩_,����;]����ʲ�
�vPc{O�}k'�aw�U���8
�)g�Ir�0��a~�՘1�ܞ�u�>�'��5�]��/뇍��3��==�b*T������sء"�����{�qN�-�V�֑�S����d�2��]e��6�'�'�o��L&��'=���y��S��v�<���ϗq��k�5�U$�)��s����vS���C�r�m�K\�
��)��]LIx̨�#�4��-{�����I:z�6�ڀٻ�K�:��cxPga [g@�C#��e)��d�!4�$ya�ğ-�
��-0{C]����:���rI$�"]��%���R�㫛�Ï]�O���n�+w����Eg�)�Gn�~���dg�v3�@��^�**�0�u&1�I�G��t��n7�D>�^�3}�tP��Cq|�^��
o/҇g8�o>��Ŗ�t�<b#ɷ��+��ɣ���|E3���]yYy�㹯^�C�8��9?<BP���k��9�����л�b2���
;^n$������8���?�W'��<�bc��?3.�"�-Ď�O��au�+F����-<��s�k�g��/�-�soI2�\S����zЭμ�y��1�Zqw�N�>�����L^�ۧ?��Q�u�tw�|�w�߾����U��菟7&��j��|������i8ϧVT�:�L�����%�dĿ�bOH}Q���O�W��Ǐt_�|�;�Q.uX�	��	��+�fǥ�5�����:��tE��n��̥���i�/�(V�ۦ�0{{dJB^�����s�ܑѷ�rh�fC�ߺ+J�=�mX��;ѳ����{<��LM�K�;��9:���+W�/��s>Q���~x�I�!D��I�ϵe�ר=+}�ßME���L���������ђ�����tn���N��Q��9dw�\'w�����Te4t�)��и�p�b�r�����q}e��~~}gA���?�����+��X�Ǣ/�a/Ä��w]e�n�k��1������8%sm<��Q�q�Ř�䴊����Ӑ4&В�X��ӑ0i���Q����F��RK��g�G���`{Z�=Bb�iZ�.��n�´ox��+���mi�
~$��ELurIo��KqDzε-�P͚�	�����V�&�K^����n�D��ݹ�
���t���C�!)�����ʅ�O�N���F�}��W���3=���G/>��y��лs�:��#i(V�ν��>���n�P���L�p³y�����=4�!��Ϭ��e��=������G��O���k�$߆��R���u*�c?�=��[�*�'<�E�7��^�3�瞟٫��v ~�,$#)v�
�����tb�O}Y{3x��.U=-P�̴��f�G��޻u�O�S��cw�i��W?��GO��n��H�z��}��i�G{a��;�Ļv�����|��B�����6L�O�D:��\�/RyÃM��y��u���\��9�ɏo���TY1�\�ꫩ_�\���>�g��7�V(O</s�u~F���kx.����!�����/�=<SO�+d�E�����ly��	�z1�Di�L��3�zUO�X�0�y�Lgk��3=l���T�<w�t�G{'�_��5��$�?O�~���y�~M��K��xI���X����Η��sAȀ_���?�{/]J8���%?��"��8� ��zW������"�aݟ������TO��`H�~��Sv�sv�~���>�<��+�EQm�(.=t���y2F
������I���Q�<ۯt��$;�e�	���#�|v<��9<��^�jJ�W���Rl�X������r-O��?��sP⭍m�~NO�H����e��<X�,�e2�9���%@�.�T����Ԏ9#�5'��B�|b�c;p�I��ђ{�=1Ŋ�W}��ٝ��6���;��Lv�_�Ӆ��N޹�L)�-��?�CE5��K$?�W��'?���]N>|��y���.f��]fی�LP��T���l���o�F���Q��k�m�'ު_�[����n���'}�ߕ:�L��S������ʜ{x�t�U�[�_ܰsw*B�T��̈�V+�գ_��qB�FJf|B���K;?�b0]~'�|K]�����<�E����{������[\�� /�`�Lf��ҳ�9���>Xg�g��)x�;���qB���_����cb5.�δ?3~�����6�<�i��pbm{�_�^�����_��3n������>���{�����8|b4'��ߧ~5o��Cx�n��_�{�Y���31����x��z��?�)�?f�Z1�:��/���l7N<��f�t��ԋ��#I�.L��nӉ��r�:��OV�?|u�������L����
���kx�z1�"?�	'�{=�~.�ܙ7�xjO�����ܕ�X<��3x��9�?�O{M;_��s�+�{~ϫ��c߳ޭ;S�8���m���ᆧ���Q�_���Oe�x{u�;�| �4��41j�nty@�#�'?���CC�ew�.R�]N�edgRcR_�p�!aS��;��α������c+\f}U�Ȝ��c/��H=���Fv��ƦJ����ECg<X��^�Y��s��߹����sv	�+`k��� _:u�s���>/y��C���W�Cw�����Y?��|5xIJ��7�b/v}/�݆¶���-~�KȺy�M�O��^,"3$X��h�3���b_2֌�(2]S��'<�^��X���^x�y����=�l8I��Y�M�錄��Ϻ����������8�G}���y�}��u��>o��w��s���ӹ{\D��H���g�(R.��A�z�/U�%��7LCN;t���^�?���TA1aJ��������d������^M�h�Z|��@��u�ױKˡ�����jݵ��³�p��y-Ït��.��Ϩ�?ӏ�T;���h�/_-���1��yv�?�A=���u����p�!��C1t��x���v
c�G���$\�U����<���y��IzӤf/\�?�1�\+���1����U�p~��ƾ-��B�[�v��"3���<�1a�)��U0y�Y���!?�����Ϯ]�n���w�u��~�����z�?�_�S�h�>���(U�/�;����
�3�b˗o�3��y�>RG��r��֥��*���h���z�Xb�@#糽
�a�?>�H�~��~ �;�*#����S��Ϣa*��d����q?�W�?�Ŀ�r1ks��e����X�
d==J�;�V���bX����M���.�9�s_��_�W����S�K"ჟ��b�6����o^�/y4��K�p��翊�~niȿ��&wS�u0"�y�����8�%O)V[�l����ۡ,+��W�W�W�r���}`������ٱ����?�����S���:Â��t_U���w�q���1�pJ�m��o�i��{z���"R�|W�(=����Y�;��?|�^��y�A������fƇ�,�{X����]H ����X%x�i�c���(��@�����	�X���"y�@DT-f�bb�@�̥��;4��+��†�0��q�o�,���ϡ�����?;Mr���v]aU\6���Z���h���GH��8,Ĩ��g��t9�qsg�%]8��v�7'�gшǴ�H�v]�D�>�N������~���Zi����}��Z=��F�ĥ�/S�����VL+���Ң�d�B�n"P�ѩ�����d�7��O��oX3�����߱8�R�V�:/G�]QC��kڤ�i{�c��Zr͜��,x�!T
$+2@��b�%s5ډ�I}z��ҷ�m<;䦹���;�[Sc�bS:_�n<��phY��І��"uQ-�NjI��fӾ�PKۡ��
��Se��?�Ϝ�O�,�i�Q����P߃M?Ѽ�N����X�;ùc5~��JVe�KO�ax�˗,��D�,=�6'?�l��$�ф:(-<��\Cߞ�nO��m;�[�3f`���+>�̌�8���[�څ��fͳ�3ΰ���w��놧�_ab�f5q�Yv����\�r�u���O���s)W.U��`>�'Ŕ�P��O+�[�Q��c8Y/�{9S���nE��[��n�$S��J�E����,fmNK~��(v7O3�I�(��O��7�p@w��h&��E^���|14�L�?�4c�Owa�x$2bo`���Z��BO��o=�SL��s�����Vs��^���NI�~Nϔ��A�jRx���w8���*_�9Ck
��8�0�] ��9#z���s��WJ4gf��(�'�k�'<\�)iXw��G����;S}����c�����ɜB�U��3�\�>�|���*���W�j�\���?��'�����?٫=��[�L�O�)6�*z�Ϫ��n��Iv��~���hk�Ql{%/��Â��O�A��g������<����S{����$�=	�cG7��Z}��j�����W�xv�rk���#�l���Y[���R]xZÝ������GO�'V@�O=����k����O8���ds��	��1�R�)������T_�W#j9���3���)zT��U�H<S�~N{|�k^R��x>����$/���%==��~�3�)�*N���X
�'߉�5��H~���s���΋�d��r���OL	��`y*T~H��v���9y=5�K��C>���Q�g�;�٣��X���ك��Ff���sY7;˥��sP)��S��}�5�ӓ��ȁ�(�h{�JWȿ�U��]K
�/A��@Vaѵ~��⏏v%H�0{���w�C�8\D�����Wr9���ʅr�P�|�z�.Q�򟥳�yU$�$*Zz��׽��^�|�27�|�'�1ފ �=�/��1�k]2��h���I�Kي)��?���A�Z�ڏ6U�Fz6�<�~x�K��p������Qs&���Q����Ŕ���K�
E��Ț�6?9�v���]ؑ�eG���ڻŏ|��2T�t��_G
?�ट-�={��W�/�1�7��G���y��O�'��ʕ?Г��el_�v	��=�؈�>x�g��.0J�Q }{���GEQ���38��8����0��?�ʙ����7N�+Qb>ʛqM77^�GyI���t̍���=��r���0���a�ǞzG*�ڟ �~M�|���d`j���v^���ش?ӎM{�Oc�I[�x�h��L������s!ι�^;9�<�|kmӮNlk/��W��I�G��/3����F�[�s/>k���쿒���;�s���s�|�a�¥�B�������?{!m�ϸ�q�ؽ�F�طq�e�2����{�
5��8���l�A��$,���W���_bC�>����SlV�h�3�W�T�����1z?�o�s�|8�^'?���j|��t��P.�3���U��\���U���7^�.S�����2��\~�v湕k앱f�Y��y�ԋ)��/�AJ��K?�Ñ�����w��{�'ϔ���ϱW������<����S��qv_�3�ibk'�y�e�|��Ĕ�'�������z:�N�g�~J�x�+.�K��x�9�L���
?~�X��;x�Ef�)��;���_���'<5nӞWņ���
/�B�������L���ϖ�wp_���+�δ(�z�U���G�|�g_�%cwG)�@�p?j�@��Y1��D����*ŏ�œ�|�U�m�dڇ�gB�ϝ5�I41��X�?�b����7O�x�=l�^~B[�ګ�z��9�/%x˟�	?�µ�-y`�?�����x.�w�g�;�*K���\t�Gy���?�T�l�{��"߹�r�^gw3�pZ�����~��m��k1v�l�����p�v�+/�b����F�;����o��fT��H�`*}
�w�K�h�i�*�Pl�
Ɵ���K�N1
F��*�{[c��x�'ݿ��xS
?�$/��w�?��P�H�9�io������ɢ\ҕ����د;���C�O֔w̎��k��m�<	�+#={��L̟����T����y�'��p	��s�?�/���bPxۜ��R�]Ƈ�I�J��� �T�'֤q������!1��\��'����nЉ�>�f/��UQ�'+3C�_=��M4H@��Է���S#�[����pvۥޤ�O��R���&�{fkn�8����o��j�s�Ϫ��|�����}�E��fG��3t�����|�S����sb���䷽f��ԭ�{��_�x�[��`�g�|(�~3<�����w`��f�
����o�Ge���|�-UxX:�3��^�-x�#�'�*׉���}�g��;g�1�\0�-��Q���
ݚ�>8�������f_Z������c���!�_��+�9�c]|�>>;�`��r�V����|��j�|�^��~�����S�������C��\�Mr9<H���!{�x�o_��b�~ʻmLJ~5�tW����o�}�G�
�(���$�s$Iv'�FY��p���O����H�V���ό����N�����P��({ܻ��?o=�~����ӹ)���3��l³���T�yH�5��V��7O�`)\�w#���o�_cie����Ma�;m����c�'_)��w��r�_w�Wn,�R�QRM�O���]�5h %YT��c�N�N.�Bbb��0���������E�䵓h<�-V��֓�b
�R��
�e��I��Ӹ���`�HdzRHg��1Y��]�ݧ��a��3��o����Vn�#�O�	r������+��Ѽ��Sh휘r8�?UF�˨�k�>�$������s�+���.�V;��A��%7�|L&8�-HgY|�n�?��U��j
�gOTװ�>i��0����xZ&P�W�1��Ç	�o_u�ki��ߺ6.>d?O��x���BVw�����-
�������n�+�3V��ӕ>}�l@�3i�����uD6���A}mO^4�ZL��>�8Lg6y"Ke��nH��Eo��]d(T]��>��/>2I�~QJ婋P������W$��D�&6����]��b_a��Ch�u׫XuS�{�2t��`	>��E�".�H��|����:���k\i{��?�U�x������%�?�{���A�3���=��0������m�~zȆ��*��������b�����6{�ŒkXp����ʒ�W�^�oh�띠�OLMZ3ɇ4�2϶"���z
��&�@�:��`���t�O�ޠϱ�>��[Û���[�J���_���C{��pA��,iu�o����e�)� =��,._���AǸs_8|Zg8��&�i��_����*��Ƨ�z��}���g�%���0x�ָ��>�T2.�}�����<�c�N�O��c�����p�7�����}�Ý�"M���Pۜ���~vGN�ڼ�߿�4�����0�z���Վ����K�i�_�	���貇�1��S��C�kOW���?�p�;#?^�?�sl}���u��GyMNշ-dz���>s'�Դ���#��Cq�>�>o�z����?��[����UY\eZ�d�]�:��Jĩ�����Wk��љ���H\�|x��iO��X���ǿ'����'=!�Jq�v��ɑ�����UF�-����ҟ��c2��cNUt�}�#�����8�?���^C��"|~��OH�A�>X��>�)&�	���U;�;��l�|:�r��t��}���.���x����? �z�5���l<��\�>����+|���O��=%�޳�����J���gp�W�?��ϱ.<�yn��ȫ:8ɽ��>?�k�<�'�v�Kʵ+����'8�08������7i>�$}���7������9gN��q
�D�����3r��p�Ê��CWF4g����Gyş�Q��|8T˕/�ƺ�V�� z��￿��
�P���G��}���/� |�
���L����X��m�"a�mdi������c��*�8ɽ
��d���>�o�Uy}���� ��^ON�MB���7���J��ogz�r�
t-}CHu���������O�0	�*����(��L�!�'�,��B���=F�?��)@�o�9��
N��kY�o�Gg�:T���=U
L��[�� ���-/A�^V�}ӵ캞~F�
�Ƈ%�Ʒ?��>�os�$�oO|�1�%�w�ܕj9���?���'>�I��O^�{��K~5}�O�/%�S!�M>PM���,ٺ�Xj3d�l>%C���f�mjIJ���\���h�?�޽�����n��,=�Qg?���^痴������-6���m.t�-?���}[����O��ǟ�������\����{۔��O~�L^�í?��E�L�]����:7�����<xY%*����pub��z6/����s�/#<��_7��o~^|C~��g�CX1mE�>hޣm��i\�4�������_�?8���ď���=r�ɾf�8�\�dt����Y�M	�rC��܎P�`��x�#]�8���g��+��SNX��}�̃�W��8ΧC�s����߭�W�uQ���8�ݙ�1t�����Ǿ�vq����Gj�u��O�'��rO��k2w������Op��G���?'�>�zN�S����.��Ν3��|�z&��<�NѺt��1��OH�θ��tZ4z8��S���3���52GNr<���i��X�|�Mg����3q���Q^��>ɫI���ɥ��S������uڙ�}�y�O��w9��a�}��h���=5����5r�n�������N#!83��;ǻs��$���??���'<ՍX|n���?$��c�T�XA{���'��������Bҫ*a�U:tV-���Ƨ{�m��39�s�>�Xq�r�^��.G����(w�
��N�it(�xK|{/�!=����jW[!jLJr;�xJ�;��E$���^�eAT�E$Zp!n�������Og��'��ڑ���k~cOۃmș3(�^-�@�
��.ȱ���Y�L^ݵd]ZL��*��"g%��~Yr+�?݁��y�N�3�Xě���6��W7��%2i���!�ş�{$����8񿄒�����q��WbD94��=v'�~�j�+|V=!�FZ��T����3��_���w1پ�)���?[���Sn�zq0��{���]��[`�ܩ��Mʡ�O���H��Ňr����M{~��	=���q;륟b��.`}�f��y9~x�Ы�c\<)'&>�8{fћ��������'������I���E�t�%���?��s-y�;�1��
��Dhh$|r^�����.x|�&tO��311�Sz�\�	��1	�a�C��l<�)��XVc�라���������>?�W�[�Ww�	�W�^;���3 ��r���W�-�ǸP�r����K��]�\|"+[.�����_.��,���E&y�>[�_���i���/��H�챵z���6~���&}�4�N��'8T��`МOtew$�>�虯��bN�8���h׿�^�r��?���o�Hb_<m�&�r��b�^��9Z��)3�=Rz�/^��W}K�&*�Er��|
�����л��Գ�$0� Ą�%7e;+��E_��G���-N�|�7}���A�؀�w����6A����5K�@����?��ϫ��R_@������-F�%$���H���OtC ~TK>��\�q<���3�ǟUV��G�C,�N�^���׾2�C�@�Bɱ�|׸N�յE>���Ӛ�_��X����<��Ԛ���!h'!��f�&�ļ��Ԋx9b#V�����i�0=�o*7H*�V(�K��4��.>^�>x^.D;��"?�XF����SS����e)ƅ�}Nn[@:��S��ū��?�r��N�F���G>È��<�f��I�@�tM.N�׀s�O1C�O'�N�}�o
>�0��2��;���o�]"�o��\KCp`�]1�w�C�3c?��Kaȷ��������p�`�8����?>�=���w�X%Q�8t���h�ʚ8��͝W)����&*����o���P|��:�������d�4�i8ƽ��A��@L����o�>���T��H����
Rx���%�DG�,3�#�����7W���n�iW"�3�`��o՚��i�Z͏\��w~-�&C��?8��?2��<��b݃U�cAզ�aJ���
�ح���
G�*���ӷ���W��wT������Z���)*�\�7�PӮ�#��7�����su�V�Q^X�l��r������4���g̀�h7e���ۻwƤ�;�͌zO��k`U|���a�Ar��B �g�r���!8T�Do��q�Nw٢Swla��O�U�r�#�P1XY1����
>/_�v�u�~�%�q��k�?=��ŵ���
�K���K���Dt��G|�郟�ȥ���߼�]��^r��>C�~C�W�PG5d��O��w7�z�]H���7o!�{3�ɹ��Ekwُ��� �	�o�ޠ�V$�}G�-n0? x9��94�c�Ă4b��r޾#CI�2�)����uh��׌՚ə�M�ԕ�7鬴\�bX����X4p��5�i�_T�>��U�8�l&la�ck"��}ħ3�|zp�J��;Dž:_��.�#)"�����o�Չc�v}����p{c��Z�����,��çc�����ß�Oڌ����ş�7w�Co�H�Zn3��79��{�s{����{'�çܼ�WM�8?��xYp�Ow��/��>@���Tg�k.��\�<-�…�w���]#ŷ��>g��*,�˃ڋ"�}���>���	N�$&c��ğ$�>SG�Ɖ�G��$��C掃c��������y��8��*l�}�b>���{��?'������h�~��q2UN��g�>:'|�3w���R�:���d���V��ۊ�������GF^����-gw�EϾ³q�ğ����YǨ����#�yn����w�6t����;��!y��(�=�r�W{��DW9
Ŕ��5���L
s��3z��bٵ�!y�\>���Z��N��q�<�9ҕ=�%��
��Oy�'y�^�?CW�f��!|����㞘��d
F�|�8>w�1tճv�G��G>�����y�"|����~���
NT�s�o斱��Y�n�/pj�G�3_���'����u�wp����E;�4�}�?��OL8֜�?��L.�#���RLN^�0�г�
�Е�����������T��G{���ώEe_;#��|���X_k�x|���rw�/j�K>����5�O�㎙�j�/F���b�c��׿��ܼysG��J��͟7{J%^��F���{��Ͽ��[r?����Sz���*w��>��(b�B7.8�S;�����R�rY�,I^�ڡ3�ϧ3�u�;�t.�C�̃��7�q��_���ovqI��1+uE��шo���H^��֥�g��"9[�*z�'==9�W����Y�:���_��賖�[�X���w�o�����9L�L���;U�v*���0�SV���=�ʿ
��W��2�q}���o���7P1p�#ߠ
>u�������Ӽ����$�����O�ğ9[��Y�$_�ͺ%���^w/g��[�P��Z�(�i��8_�Jf����]�n���̺.�C�ٺ��Hȏ������O��Ҽ3w9
>��g�(�h��O�y�ig_��5��9����h~%OԿ��:��{,��<�Çi�N���u���Y�ˬ뎹���e�ܾ`rP����9[�gǾf}X��YW������L��z!}�CO��������u�$|F�ڇ�{�G���S8�9v:�ñ��gt��qu�&KF������5��X{���i�P�f�>|�0��}�z,VL��qg}�z>8M1'y��%��7�'��>��G�3��O�G^G8ǻ_Gߎ������9~u�S�;t'�S��r?�C�?���=�٧�:�=����p��g�ߵ�F�>>���λ�;��}_�+c�z�@�k�
i��Tc�:����Drv�:?�����N|�e��}�9�Iͼ3�<�����~�������V�����1�ǻ�Nt5���rp��͟��ފ}U#\)g��r�v��*LT��)�x��=���_?ޡ�ֻ�;�^p�'Oş��,�w�
߄��B��I���^U��u)�e
�ч�c_[O����g��0��k��պ�H��YN�����+=O
Un>��Y�/�����N?޽)Ga�6�z!�q,���AB#�2{ę��7��k���k�I�?���irS�$�����u����^�Qn
8��0��?|��Oo޲��]�E��K�ũ�y/�dx)V;�9��]�2�:���;S ��.�F��Q��^������	�&g��)�ނO��.�o^�g;��'�rY�b���?�~�
�F/I�;��W�Ń���Q�$��w�An���3 T��`t��#kǐ�v� 7��]����J����;z�@.�'p��;�A���pv���C#���.r�9I������O�)��]nD�|t�+�E^���F��p�u����mP����K�᷍�N�6��>���$�ȵ��v�I��и@!�Ht'|ؗvr��z3�	dt&/t9˓cD.��[9=�'����g�ɝ>cEw-E�2��(<�@6����>��?�$��)>�3v��Go�8����!�Xt�C�={|�`���Uv�Ƿ�P�-"��?�wgV�1���O��ߠ���/����:П\z�����ܵg_��e%Rt&�]ǥ��U��O9��TG�I�)^���;�γ&�w�q�������]]��(�L��"�lz&Gt���Q�8�s�#��S?&|B`�.���ϳ�8���Y��s�w�/��6>s�l<�;�}J�m-79j�+)$�����y筵�N�(1(�w9�����R���Y��7#�X�%�
��j$���ȶW�&w� r��7Xh��b�A�5�#�ˁ�^�ZZ��A��L��O��;��v��W'���$Q��|����,��<��y��m�;�	�W��i$F�����$���w��#�%V�5���rF�ӵ{]t�T���~����E��"p�+z7]�^�k�?���Sjg�A�~�.ǃ�_ׅu�uf&i��.Nj����}�J7r?��v��Ǚ�eox�>���.�V���vB�,||UN�6AUv�8���v�!�k�8 ���p�
����,Oゃ���3|�}e�< Ј�Ľ�
�]�n�����1j�����K��5���ia�s����,�]�jA)*��뀳]w��x%1�R]��q�k�ѭ��Û�TV}�=����
�����w����&+�>�/��zξ�F*ru�FBŮ�C�D1��ܟC`|����)�Ց%G�٤!0œ�+{�>�/H꾬|p�\߸˩��b�y0.nI.�T���H9�֎�7��-Q�?%������m[��)4]}~PyD&��H$���a�Tt��;/<��v�ys����
���3{�����y��-���r�vXȉ�PJ��UyV�6s�k/����^>�6�����T9
��Z.�iM�iJ!E�0;|�	N���Own@���	vY�yG��E�%<ɚ`I�n�^a׎.�[�z�| 7��x�b�������3f�˛���O�]��u���+��G�w���Z*jh�+9�;����?��Z�E4�
�M��[�tج�d|6.y)���V�.��_N���[��>�+X4aJI��"q����d�f#���A/8�����)�^X�YS�;���K�R9a�Mg��+@�W���O�ތ/�Y{{�pb�;W��þ&����Dz�R�i^��.7�qĄ.��.�'�La*Ɨ����2>S���$��[�e�=�_�q����S�\K�!��yn�n�{�
�Ў��'&t7��m��P�ԇ�Ҋ}�l�O��ea{�8�p�Œ�W�R����?��Us6�)���<�+��:*/��矘��0y����E��7ԅ8�!=I
H�l1/�gS����HWzP�–���0A��*7}��
}��z��80��X~	��m�ꎀ�GsMO�X��tT7���IK:���/vƜ��
�>�Q�H� �G79OH�������e�O������[�	r�������R!�b�JS@�q�3k�J�\9�ʹ�ٱ_�ԢQ�t�,��N��-7N�hOZ�G1�bm{u��n���+�\E�^f�L��r����
���ܮ��w�i�tGC���uZ�F��_�	��ѫ��4�L�쎞��s8&e��^j�gݕFocB����W����^]"��]��}y��a�ڋ��a���|�&�‹?�팶��~���Xu&7goS��=gz>�G�;��=�H�3��<s�[g��M|�0�;�dAir���Ov�?��kN��>P�����4S�����˭箢89�i��

����ԩ�;Y�}��8�٧�}g��q�T�I�� �����';�����O���3�}���^X9K�18z������a���$�8���v1g��~7�N��O/�>o8wv�a�[rdT����j��|@�1������9w0<��˯��kYr��)�����F�?޽{������=|~���6�~l��a�gu����i('�%� T>���4��B�F����O冹�6c�e�2�yI��3��Y������w��o��b(h�gt��9KNo������w�8u�,aم�j�J�'��;/J��D~��?�
2&�'�t�C�p�xu&����Ova�t�v/-4�W�9"WX�}�wj��BKc�2��?���v��t���nU+�oU[�����s�=mԎ��̉H�<�����}����;��H�5;�R�Es�ոb���=|~:�毖�?S&+3�S%/��
>��5�y��>���_~�교Yub>{g�<n>}�E[���ﺫ�I�4�ڏ?���s�y�9����Љ`�F�Y���2���1~����?k���W�'8խy��)'I.K�O��σ�����eS�u �lm~91� �
&�K�r��@�J�#��7����I� /�A�b�ir"�=�M��k.eH�cG���1}�������'|�[#6t���q�������\L���Vor��=mI��a=uY�a�c����E�X��ܧ�9>3/�?�.����/g��'�8&L���=}pD�59R�<���k�|{/sx�J�=�
�W�5/鈯&g��X�Fd夦�1��GΜ�OO�`����L�џY�a�)�JF��8�ݓgՁS�\h_e���1uS߮�����#��i��O|ɨ���YB���[B,��O��3��U�y�O�O��'��b�r��p�mJ.��>�o3�0����\P�]�̻F�?皡9���]H�?�%���g�4?�ܫ�3�3*W��%��Lh���'�wN��1�q�ķ�x�����_�<�p�����A9�q:��ӂ�������r����%dڠ9#���G{p=���|}��;|��Hj���ɕ��z{rP��!i��+�)"G{/��[*�l@�(� ��gz/?���պc���7��ǻF>���ӗ���A�������d/B^�h�F|�ݭ�ֿ�&@�
�B�c��t�o@�ks�r)��.�l_
���N����mg�|DH�3A��aW���6zr�Dt�U�6I�g ��՟����l\g���J�cc�LTt%~
�p�o8�L	�Au��Bop���׳�ָV{ݽ%t�|d�5�?ჟ���E��C9$�>r��$8�V࿸�p�]��t�3��;�+�$y���ҟ���C�O�����Cg(�\��Z`
2rp�^m�ѵ�SN����BO]���f4Hb�v��o��^�#�b_�NO*������ӟ݁c��v����o��Q�#>?�:O�E�q�w�/�P)����v�+�Ǝ�>���2�*��g�a"��\R�
��>���'����/���M��g�����(�?��>�L��G��+r�'���u�A^�a>�/������ӝb�����4p�%�Ope�����N�|EX;��)9>z��2�3�]��ڏ�o�L��?8)%�m���6���ٯp�q������5�.z�_Eײ�
?�N;�a�y�5�Ҿ��ζ��.�>�W�/��,�]9I����ίSz���ɋ?�.�cd��禧�?
>�w	Op�S{����A	��O]���c�l}6�L��^���q��(�E���"HgrJ�`w?i·����>����t�>#��,�M�� �v�X�N����z&�@���>�w�ßK��ܼ��m�+j��ѵ/.	�^[���To�v��'�ᣑ�ڤ��:}����:�����U|(Z�>�g%��]�G�3���|�<���[-N��?qX�~	�	�E‚�����<?��Q���&�r��Ε!G�|��U�K�E�����0�%L8u��p
ϭK�W�C'�J�h��1��8*4w��M�rb�o��_
 8��i�?-b/~��|�]�yi_z�����
��3��Q��*B����U�� �s;࿺u��KGKP���/.�}-�HLQ菹br���5彇���_����-���]���0�F����W��a�6.i�	�P�u�O�Nu�y�n)��k_���)�)0�$�-����t��^_!O(���R冮X$�㒥!9N�\�n�\p�� ����ٓ+cBa��V��Ǣa�����j�>��k�_K�]RK�
N��������e�5�_��p�z�T����E�����e�8]�2��K���9.�ɛ�Z%O�Z��M�J�$=�
�o/5�@c�׾��^���98�/NG%��@�g��=x|pȁ��ݝ��Y$�\��ȕZw�$!�/Dž1�⹤����{�uw����S���$FЊ]�=L���V��0d��Ã���F��ɀ�_׌Y��oO�}߉3e��5.�E��<�41م��J�ɆOi^�GW��rnZ���r/�Q
���ɀ$�.��!wC�>9�~���ӫ6;7Y�=67�Ʃdzs)�]�-�8T�wZ����N�Q�vH;��V���#�P��t��j�������+L�ӳg�����M�<<��b-�PI��>�8�$u�Iޜ�o0D���Éў�+�s�ͬ��U��$���[�s���6ن���{�IΤ+l���0�>p�v�"�&W�|���,��lL
� �ej�X騐cjN1����9��<�H^�����޿���_-͝��'z����)�N�ٗ��7�se�H���������ZI�M�Tg����S���4�=�oy]D/�l홿<0�՟]X�6?�J�o�)C���~*���{����	طb��{��4>_�k�۵�-�J��`7+1��/_�P���@I$z��0����TRU�D��#*B������}��f%0��M������e��ۭ�<�Y�貇�}�Zc,~�M7�� �f�z����4Y�W���'�;��KT�3��>F�<���|O5��ɥ�>�_%W��ڶ��[����)��	������'h�#>��Lf�57I��[�&]`T�Ğ�&�/�2S�L�\C�ۍ�;�/�˯�2��a*!�e]Fm/�����쁼�p�7/�Vbg�4�*N,r
�OYis���m�jy�ߛ�31d�]3�2�/�O�{[�b����Ɲ����
>#��G�����5��y�v����-f����#�o���ɿ��Ξ��i�ܑr�<��
�6��<�?�i�/Ǒ�3���o�C�ugE��5w�şV�����g�����[p�G�Կ���:K�h\�'�>w��z�h��$+����!�&��$���N	��^����q��5u�Ĵk>:�q%Jpz�2'�3��2yg��ɵ*WOKg��ްO,=���ċ�wF����G��2���
�3Z�3�r�f���������'J�:C˽�	�E�����?�����#V'�r��������=����O��w�`��"��i)îw�/:��D��]w�,��!�_���~#am��g�J"�t���n	�.(��������eP$��o��޹r֎=���(��.�GN�-��ͥ袇��'��!�$!+����o=��Ů*�ny}��c)��4QX�.}�Xj�?�j�w�Kk�����[�������+�
��G+��s��Ͽ~wU ~Ɵ��˘U���E6w[��0�2��6��s�{�['?i��/[��:����wo�)����)?�22PB3�����4|�"�>��;t4�)f���6P������$/���6~#�=|�?��O���t�O�w�u�]tW��w����Cn�I:{�.�g�L���$��HKiI?��\ɽ�����5��&�Ͼ�ᣅ<>�z@k>�7և��h�5��mb_q��a�ܲ����Og��W���)8{��|K����q��|�g��c�����c��ڼ��U#��w�Tp�̜��w�건8�+�gD���������r�I{��0�D�����b5}���;6�?�'��'a>�㏜��u�-Q�u|&KirF^
:��O=��:�϶P��~�^���紫4z>�1r��s���u8&@�'|��g�3��!����ո��.�g콠ı�\�����ݧ\��zd綪�T��c���+[��7�f�
=�_�:�YfG���9ӄ'2�?�9���8�&k����!����#z�u���~�#����j��+cܩ�7׃�_
}��C������uݬ���6<�L�I0��M�j��SW�t����Q�T9.P}�?
�n��ܗP
��᭄�O��Q��BӇ�Z�g��C)�39sS�1{��y�_-��xV�k�5�0}��W��_�]K�O�a���=����>�?�ϝF�;#l��g>�gU�x��;�d�G��:.��-}�Y��^@H�p��l�~����3r��qk<���]�D��8_ڽ�>o��>�~�v�V�맄0�ٻ�E��й�y�	:	(sWE7=�Kd_E��Z�[@�Hg6��'�G{��]���z3�����A���U��-R�h�H���������p�1�>#��b�>�y�Y~����{�ǒ��:8��"�I��n��#r�>����௴�J���D�|j���O��'|tF|^J@,�ѝGa���Ά���v�?)Iq<�Op�򕻎���S���S�˃�e�c��k�?e���V���p����lܣ?�;���
Qܘ>S6�L�Ug}�v�� g���1�S.���K���o���9��������&T�J��|赗'\,%���]K�@�����S���3wٜ�;��{��O�<>�+|��X@�_}#;���\���^艮�a����<��6f%$�߿'�4�y��,��#���fi
�9�I��?����!�f��3�/+�r��s��?�=�Oodi$���˙��rAEwQ_�[��s��� �%ye_g�\�)��zH@�~lt�#=��ߌz�'~7��Hч�G������'����=��/Օ���_�� ���b������
��
]>x��J��/���=2�za9v���N0<� \��;�d_@&�`�COmy-�����O�6���]E�5�����?�����X�����B�z�)7%z���^��ϣ=�Oy��'���`���>�~�UJ��'�Ld����w��>PH������.^�R��!EK]��_zu���v��o�Zr�\�� �����#p�/�O�c���~��޿�$�
�'z4����\��h���|����/��?|.v�şew;��'|���h����?������(�OQ��
~�����P�Yn��c��Z��*Y�5=���Y���A~�Ūo�0&<���ј�D��C94]�T���= �^ݯcN^v���N���3�#�h��%����^(��#ݦܵ�^��煏�[A����sj�x��l�^��=�����3�ܚ��_6���'��v��0�t���;@��AB�NEwZ����ϗ��~��{,
�v�U{@fM����y%����H�~J���'��]�EE��X�A��m�,��LyUĠIW��蚞{��=��r5�*��7�5V���ga�k_ڴrG��́t�;�W�U��,ܬ�v�΅�W��)��gQrL\F򃹪=?n��:�7^�7 '��pd��ȅ۫���Z���)Q��Jz��Mw�p�腏�|"7ʝh]�X�=����Oi��?���$vs�F~J'�x㒙�DJ��N���u��X}piz˔rǐ�&|l-�x�6�þ	LU_�s�_oB_�B0�,�W�='�(-ۢ[:����̵���o��E�b����9�����
�m���g��SOŗR�t��>ݿ����ͭ�[!���X�_
�S�x!����*�����l��QaGp���S��D�h�G�ox����U;ӥ�Uj)r���SW�7H�)�E�̒x��z�ÝJh�#��U��,VT(|�����P�%��۰���(����g��� \5�`�+�V�,|��:sp+��a�ug��j��p}�zz_�ܦ����E��1�BL�kͤ�
|�����r 0�W91���/��C&|<���$pԿX�z'��n;]�?���|5u-s�qt���w6�I��ƥ?�a��l�z<Pz�c��ܚ�vn���)�tq�To[<�/%�z*fQ��O^~�<��?�
�D1�8l*ges��'8�T�2�Y5V���C4“:�����ï�۱z�`�>D?��|��DW	a�aF*�v���)�hyz�~7=D,��]�������|��$��Ќ����`���L�F�����J31����p����ä=���D1�v�Q�a�?x�t��,�{�FG�o|����5r��‹�H��s�eO��}I_�3��r�?c�ß�W471u����-�P�F_�GO�}�g�o�4�`����?D��R��	������ӔU���=⎝
���d���뗟��@�³�����.����'�@l=��������ȧ3c������[(3^�;�ew�f*D7��0|���^8���᭳���
�b�������v'�q�}������|;��~<�i Կ\�c8;9�g���,��4���=lz�G���%G�ˡ���O�G^E������9��u����n9�겔���ǻ�t.�e؊b���-s�X����7n��<,ҟ4��T��n`�h�	�k�%���8��O��x�
���u!�:G�B�]�v������-�>�������k�&ǣ�u�;�<5|��:܁%��+�1��>J���j?�QK���ߥW?��f<�uc����tf��r\�
�rk��n�2n;���jME�#!�J�����|�+�Zٞ�g��d�?��^	B�]|+������ʔh��b�����I��Z�zp��r|E�+C���Kr*}�(��Z�����r/�k�7g��O|�����B���d�'}�RH�'6�}�'����Z���.�6��_ ��]`�.��>��L���_.����Ҵ�/4�3�9�������Թ���Z&�0<�W��T�ĸ�e�X��ڷ�	�n;j���Y���p�/�`��'�F��y)����c���C�ȡ�o���rk`%�,�~��=�'y�/�5_�8|�[g�q���̞/衿5��
;�d�Ly��|:u�f�r����1q��k�R��4gx�?��(/p�+ym|��3��s��F^�����ά֥>���������L�:G]~���ڎ����r��=:}���o�O�����g�/]�|lt�L����`Rn��,KP<�O����b�%f��s&��ΐSǐ:d�r�D�������3�)�g�u8���hu*4�It}��Ϥ]Ιb���<�}�{r,gwr[��gJZ��Oz��K�x�C�"{��s%��;Z��[��j����۷׽�'G*����f>�ψ�I�G�)�7Jg��<���o�����}���{��]S�!f}>�3�4�9{o�?�z�#��<��3�����ymfr��M���=·X9"�r�7zxv��q>�09ʍ�:!��M�w1k��q���G��^U�p�"�b�1�r�׌��L{rO�qD���3� ���r�MQ�����A���}>��O8�Q���8��E#,��
�W�7���ӄ�z�}����K��ҟ��z�͛��\
�����+.���O���u��ϧ���"�Wo6�P�su>&�bf�#�}���]����tg?�?�O94�I����_�.�be��TAi�L��\.Bg�����^��������{��[��:
���@o�];�qħ�f-���~��]@�� ��^���X|��yy��ʾ��C�������f�/��Q��aׂ��D�;Ö��)/#��W�C^���?�S`P;�Kl%/LNJo�#��o91�����#:�\���� �Ov���"/�
v�Q<vM�Yt%/��w⟗��C|�; \��,��L�R)�s�U�	,�ko��L��7yQ�����6�>p�����!��6��/rğ���ל�6*�o"�Hk�2~��#>/������-8�%��/�H������\��O�,|p�Î�
�`�T�W/�hV�oޡo&ق]�w����O��w�O%O���;�ҟ�3�cXW�)}8��������9ʀkup Kd�>���jPNF�����7��os&S�v��9��Ġr:���86/D�4߁����W�����]+G��F���S.8y�r�|����Gg�E}Ff���G�Op�S�ߓ�8q~�V.��%��o����C�'���߀$|�0L�;9+=�+��/��ڑ��h��jX�?�NtճR�>�M�OJ2�yF�Q�$��NK��N[�=^�o���\tX|�����k�r���5-iE�3��/�>���p�	���C���g��K7��|9|�ł2�b��>9�W��NiS��u��@f�K�i�`>�gpN�p��*bQ�����G��DW|s����*.v�,�NJ�X
u��+�u~�!m���T����Y�
r�����IZx�P�����k�/i��;�b	A�G��b���_B���e���w*�C94�G�Z.>s]Կ��A��\d9���u@�m	2S���1���D�}�b��p@=&�XՏ�"y��^���OX����u�� ��	��F������ :ß����?�Li�I�g4���>񓾁��U�_���Տ�J �~�=|�����w��������7���ʸ�?��
�W�4(��7�s��\P�"���4.�.'�%9��K^����
F^�Yc�lJ-���O:��}6]��c�Q}���G3V�v��#Lo_vv^Z�jF����;��Oc��
�S�,o�"�(8��/pRDI9�]��0����ԍ�=m��
Nc��I;����{�4�0V�qy{z��yk���.��]q�~}�i���`)����d���H���%����&O�0��𘻎��ڗ���V	�m�g�_�>�ګ��
q[�|���r���癀cb�������GU	�=U�q�ں��)�v��3����j7���.x>�¢g{�^6)�
>t�Z���<C����t�F���b�W�*����-ɭ�r}�p�"O�ҟWW�Ҟh
�����U�E[����υm�k�rYwHE����61L��^�B�G�} �r�Gm��|�.��	�]��C��z}i�2Yj�ڄ�vre�xi�i\�8�5�a�α��џ�%GjS.E��*]A�4;���Ȁ�=j����`5���T���aV�s�L��9��ۭ���%�E��F^�ї?�����������<ݑt	��;���a��.`�iz�c���N0����M��3u)Z~v����y�o�#�ǧ����;�����
�/�)��w����(�3�c�5@>�����\�r2f����TDEB�Z�9��N�vƟ�?U?���C���6�r�Rp��ؓ��>�P��Lq��f�܋��M3��K�y����ϝT������n9�����鮙�k��ǚ��6uV�СT�L�9�L}|����,�|z/�3�c�?�'פ5��*?��qu9����3zX��W/�h�-�#��������홍;���?ŀ��O�Ą�9tM�@z�>b[s@�(�ӵQ4��T?��TK&�x��g�9����������ḿ��]�~L;�h��H2��G�Gog<#��>��"�y�?�F.Ŏs/�jg-mo�P9tǓb�#��N(���ǁI��bm�O�>��oB��������L^�"S�v S��x60���OM��]�cB��Cٙw��K�7��s~i��,��(�y��!�?�g���?y#>�H
�O^��?|��v����Gy�������.;�3���ǟ�
Zro~�� |�ΐt����	�����W���1a��\��?#�#]G8g|M>����N;�d�'55���gr��)4���|��3��ut�s�Ϝ��䱲��g�8Gym���}'L�cf��\X��SeF�ymׅ��_���>�^p�s*j���>�+��1|�7�X"��ȫ���0��u��W���r2�_�������@�Ўkh9 Y!�`�vp����4�x!��+�<��[�5�!3|֞��K��.�/v�EP�%�k�`��x o���է��t�<��q)O~���O=�
>�C����g��q�2g~�����'8��g~�9>���0'=I����3f�B��O���}ş�~��_=�_�|��y'=��ü�Wr�?��G?f��?G|�����s�N8�}�M�@�r�l���\������c��1ȧ!���ZoG��u�Q=;z��M�Ȭ7���#��ᜭR�Y>�o=8r�>��V̺i������Dݬǎr�~�S�\�ÿ�����8���{�X=g���O||��I����q�g3�ֱg��5���4��}\C����P�p|{�O��tH����#r���H�1�Yv��_�0�h�qq�#5��9�u����.Z���`�����Fr/�f��}����c`��IʓvNj��xWQB�q�Ln�s�F��St͸A+-8}��\�:��ci�E�1}
��3Q=��W|���
|?�>���N��g����߾R�3��S���x�1�Q���}�~?~�t��C`5VG�z$�R����ř?��">?�{�1ۄU@ғ�Ԓ�<��TЎq�	vM�����`?��񨱯�P����G��*8��2D��W���<��>�6&6��t��R1H�
8:i�qֵ/H�&_y�Y��@l9
�r�+�1ₖ�^wX�'ym�C5�U=���÷��:Ͱ�u�<c�΃n�!ׁ�>��ǸBs�&�R(��:�t'�㉩7���5q�å)�E���)i�|/�ѫd/ٛ�"������r&A���A���QT·��1Y㳀�&|�n�iU�"�0�.X�SN	�'W��:� /1�.d�.M���F��!<5���ǾЅ��W�C�A��+�Mݥ��o"�N�ȗm��sv��CѾ���Z�#��pw+t����:�p���l�z�-���gu�;;�C��
�|a��U]�6����9��T��D!VVG��a{�+T��T��}>�O�I��|��N ��ӆ��ȝ��6����"
&J�Q��Ȅ��բ�����]���*rG��A�;��1���L���B�Zx��i
��
�=S"��������I���T�胃
��I.����x�τo�T|��I����S�==�"O�ڹ��?���Mzk\z���OF���'��z�i3bw��>�ۨ�p�	���:4�"|e\��^!�]�'�g��$|��?�a\|����1?��)'���s����*�'/�Ea�;�]���z[{�yο���|ո�+U:�XY{���'�����yp&�ڧ��u�)LH��H^K�?�/>g�>s#|�<�ߛ���C��ȸ���%��w ��ڏxVg(����3p���Wr�C�38�g�9��\���~���#�g�&G���
xo�S%���⨞͜S3��p�;��IFݑk?���O�Q� ��V��W��`r}e�ۏ�����������1n��}iٲ�Tפ��P�17���S�pV��|�_����'Ym8�����ƽq��՛���|��-.����Z'��?7�ҟ���yt�o�g�mdہ����d�Y��bj'/�����Ը��t֎�3��+y��<K��C�!��l���u�ϓ@�ԧY|��%�Վ��ɥ�5/��W���ݘK|(�	�v]+��&W��5�v�ND�G](��s�e��[g��	�A�b��!�=s�h��3��BG�F]��݁�������)�֧��l	�Z�l=�wb�dO���܏�W�]W�y>VGrb�]�KhP��% 5�Gpm�&��׷�J��8��!52�	ٛ��b�G�������,7��C�V���'Uys��#�EФ-�+^�
�XK�P	(ޅ涍qA�@9n��cy!]O�G�_�=�\��ps�S(��Ҽ!��?0���#�PQ�9[� �"_�
�j��G����;L:h��O�����O.�Ef�^�"��3!OV����Vz�X�Pk�A|(����'�UZ���N�T�09I��+=B�X���`�kP�C���Q�݆A,��X,�8^�	��Dl�}ܛF�"l�	b$I��R�P�+��vɟ�l�)�tbEh��Z`��+�v﫧fK��3a���n𹾽ѧ����]W�NƏ�<�B���X�RFԡ�Ѝ�=�� &։j(Us�P����j��k����}��2�hI��_�&��.W��r�����]Pr&
��N���ʹ�?>S��p!%W� ��]��E˥��?d�3|4�p���~����F��@|�g�$u �ζ��RTh�Qy�ZH��k�r"��P*F��vU�~$;���z���"As����}\;%���Յ�w�k�\�k���`z%�~�K�`X�T�?���	|O������)����̴�u|lr���	����ڱ1V�>�B����U���f%��vwL4��t�������������F�'џ0�H���\�8U���k>5��*>ɥ�}�!��a�?�Ϝ��p���W���B�;�3�@�]��>x��P����A�uq�o}u'��1.<�^������qF�K�9ћr�G��H��	��.���c�ﹷ����t9����'M�Ll�*���}��/Srh^�w���?�F
aH>����1aqΆ���=b��Vh�I��.��qk�y��P��M1���X���v��}v�9V��ܕs��(�:.}r�Lz8�WG�*�tR��V+=�G󗯺�\#��!�
�w�”���?	���v1�7�I�|�o�q���ӟ�p�)����OL�ܯ���;|n�~���1-��;��� �uH�� d�@]�3�������?�._�������&�.�&Jp&�Mcڥst�JcC$/_�Ȍϓ�E��������S�~��F���ap��Xg�z��۝-��焾E�.��ܝ����S1��M1Z����d��?��2�؛����ӏS�9~k�5.�z%b�Ow�v6�K�_������	�s�nE�!%��᪒�ٞ��E;��Uvg����\Z\O=�w���sv�"~�5�L|������-願�$�V�8��<�_���i5V�(�"�&����m!=�>��+��t��ҍ���;��q�hP�C4��@J�G�'�1��JƝ�)���2�v@���]��Q`�"�'���Ϝ���m�0���F��Q������c�=��c�Y1�҉�u&XD�����l�u<��%��e�k��@�B�Zr���?�i��(4-���a���0��}�����ЅV���2���F�5��0�J{0yud�qJ��?f�g뱣���c�����W��(��y|�<u&[��>�I������ 49vR;��?.�y�]��y�W/6X�n9EN�>y�R��X�m���o\l��;>l}�"g�*I+�
�4�bz�gBAݜ��>���m���l�8���0w�#a%Jub,�{���7|0ُ���(�\pB��ϑ����Yo�dmPR���Ƒ;�~|� z�k<��~���aѤ�Di�΢�.G��u��>!��g~����5';m]׈
�%���>Mʳ��B�q=�i�¼�Gܓv?tMl�h��F��sWQ&٧�o�p��8�֙�a�ͩ�wv��$XL����8�������*'{ֽ�~��g�<ǧ}A��{^4���I5ˏ�b��q�y!�/�H�HK�+5FBH��TbrV�����#b�-xC��g���?ǟ���U��I|�4��'>�=U>�܎��u`#�#>�8QG��}�>ڟ�=�%�O�%�Ov1FWY]�|�`���n��/�0y����]����oT?������r$Dg&~�7|������"ˏqp�h,gh��&weV��w���ǵ�9����G�=����z�+>���,�ώWbu;��;�FK5�O)��t�����"�%|��d,�(��<aYM��DLF�pX�ܪ5�TD&�[$�=�.FO�+�?��&\g�ɨ���_�dr�8���|8��cC=�,W�":���O��it�
Um@��|�_����a_Y�D�r&<N��+�L��ܫqg��-�_�L+� T���4���m�U�iA�b# ��ղ�J|I^���x�����'�A
 *�a7w���I�DФ�]0��+|�B�c�1$���]Tz$|*N~��_n��~1�����B)�  8tM�dw����B����	�XWi��7}��<�L��5�?�k���1i�8��pn�Ju��CF�'8�C��o���܄�'U��r��_(LgC�N\M�(%���y�yt�`�%@��NO|cu�^m���4���à����9��
����>cl�T,�?��~g�H!��4�g�Nue�?XW���K�蚴	�*��[p�E����]�g~��ɟ��1�j�x��О#o��K�¼T%g��7�����N���ј����$/�-����g�&yx�Q�a
�8֓����8>��3w�֏��_t�e�����='����>��s�&y�z�W�i�b5���HK'w�b���gzUK���s�_��'�����ȠZ@�Ӱ�-��D�A�����V��GI?����#������T�7�=v�}��Œ;��좻o@�e[XV�C�+��E���g�i�����N> �����H�O$��K��kz��[]�
I�!|E�@�;w���V]|�ծ3��I��~��%�F��Fx�L�Y�*J�� /� �X(a�]���mO�@Ct��|<�_��۳̼_�Ӎ����������GJu�qwn�ձ�������,Ҏ��ț�����H�ߠٍG§vC>��y)y1.��aKb������Z&	>@j�?>��"W~m������I&�q z����1Fw]�_�����D|��J���}͙3-�/�p��'�LP�v`�5�B��}��`��y�n}'F�J}�n4�r_��W>t�4|Z4��%�9�5�s�|z�6�'>WD��{�B��R~~з�<�-�z�ܦ���Y|�Z�t��2ז�']��V9��ž.����F�{����>r>�Y�=c 
��=2Uґ�.��Yk��ʖX���2�K�+��&�6�rbVq�3�S��}�W�ΊrS�,��v����۝Ľ~A,7���������1���r��m�_���)�q�W��n�LV.���m{�GA0j'$ޜ~��/v�_�Q�DvI~t�XU�4n�yg���I8����}�K9Lt>"L�}%���2�O�s�a(Y�߲߁�'�-[5�N�R�,��KN�F�w|j���g��O�f��R��dv했]��HqɅ��!��_��W?�+�L��F�ǫ���V����M؆D�S�4(u#���?��PJ�s�Vr����=��[�KZq�%�L�	�>�Op�>2����'�ff-��o�%G!Gf1��{=ӟ���?ӟ}��[^nw���A����J�ꎫّ�zk �L��b�=�n#�[�1����&M�"��7Yr�%�4�����-rv���(�cg�Ljղ�%��L���E{s�O��Ϊ�����,c����(r�[�����$]�q�����)��=�3����-eQc"`�9�|����
��bn���%hW.��%4tV�d��_y��1����PI?��t���Z1��띃��&0���k��²w�*��z��r޿��D� �Z�ՖS����PzXNR����"�h$�@�Ї�G���9�N�~����茹l�����T�uk�B���\����[�Fʕi?O�-&0����\�a��䩐�I�#\��6�D�>�q�ͭ��S���/z�rx4�Lq֦5֬��t�_ç=9e�����ì�N�R���ybh�-6T��X�?��u��A�@���:8�=�tƓ���w<s|R{��J:�����ƻ�ǧ��Lju`f�:9�S�R�`��9s2ki�����BnV�' e����R^�o9�͛7w:��t�>�Ep���`o����?��b��S�%�Dr�?�ڧG��3�ո���)�[�m��pe���G:�Xp������i����ށz�=��;����?8�N����W�,P;���s���~�Ē4��1v��}�og.���M���.�;���¿���y�g���I�4�����ϼ*Z�1�;_�c�����9��������uJg���f���j�?գ�@����rf_��΂��Щ6�������~~DŽ�p��ȟ.^�{�Y>B���q#��%�ݻ7���Es�ۃ+qd���ݔzZE��ծ�~�ٌw�VEE�S6�/r
o��~D�����D��mZ�缍v�tyPg�L+>��>���B;`gkW9vJ����+��ǟ�Z���a��'yi���?��oml*{�_왻�DCTw �Wٯ%�w��q��.!#�ѩ��}�&���\���+���[Ǻ{J��j�C���C��>\�����rA��..Q	�����z����n�7* �}�C���m�A��-�uK�;,�y�2�T���j�{�5?���”�O�v��p��[�
}&/���q��7g�+��M@��?ҹ�����'�������Ș��G҃�ܹ�!��	�"F�]]%�ԹI�W���.�9+{��f&{i�?���c��;���@�38�)��u�W�{$��v�3�MU�^��ѯ�pVƸ��̺��
ΊƵ>4�Y�]��H�u�fZGQ���;Q��F�8N��q`�Y�������f��ѿ�'����U�R�KJ�I�t�g��Α&_<árݴ�s��i����[����d��4�������~����y��E�ʽ�W-�6�ɺ!t��wu�Z�?���h~I�t�̿�g���n�!�?�.ʐ������^�S�Ѳ�[�'2
>ۏ4/�/��
_�$�e�=ɜ|K��Y�c��w�NĮ~X���R��}M�����'�.-i�3Ґ��w	�zN{���c�}�&��t	��!�#��o�����x������D�Z�//����Z_ű�������1ML��j"X]Վ�R�����^Z��H�X}j�r�FW���Z�֜��ӟHn��n{���oQ�]a�����y�W7h�>�/�����l��G�Op���*�U�G��$�������~�.�d�[��{�wI�OIy�Ƃ�J��<lWu���0�1�Lx��y4�{<�_��k��?�y~�݉�I�G]~���ӄ��z�?��)q��@�2/;8DM�%xk��� �uؾ�3�2��Jb���)��p�LI�ѿ��R?�vj����_�"�?�?����T%l��ϥ3`�ܾ؆K{�x��+A1��.��:���}��z[
�)yur�O�͏
��*�U���~w�П̪@LLN�`A-���]bA�(&`��k�y���5(�jz4���?���Sx�}���H�ax�:�n�n�/3@���� h2:7.���{�����y
��Ӫ^���ѿ�J�������]]ɗ��
;�����v��R
V̍ѱjz��Jh�ow-8���ʋ��U)BKʙ���P�?�z���Ų�h�]�$	ҟ/�>M9"�CNw��?��Oz��r�����/�z������!��@\�O��
r�.���1�%�q�zw���!Ä��<˕[�e<�ur8��s�?�X��e�}	u����
��N���cȧ��7�I^��������S���Bv�0q��D�*_̟�����o��!��O|�֟?'���g���0:z[�biA��=���J����
��]��_���%�l���8��Š�>O�-��O:����ӿ�N�>��~��V�W���v���X����'��s�T�AK�C�˕�,�\tT(��!����4-7(z�7n������O�:J ��w��������k�����0��gy�oç3,��^}Sge�<��3���B�}��t?J~�O�/ε�K�.Ո�ϓ���Y�K|K���c��j��}h?�4�����o�����!��U�F����8��'Ѓ�tl����&�E,�7|:�_G�� �?>،�C@܎.�6�z��}�g	C�?�[�P����|>--^��J�x�-��\�;�.�&~��R�,z�G�?�'_��4����D�����]��O��D�pf�`?�������R�r�Q����i\1����wq��IM�ӟ�*��"Z{�c)܏`�8�����j��/�h���JZ�� �6���c?���Ȕ�R�g�����"ɚ_�']�WD�����p%��������9���:7�ƌ�nç:7������Ա+�� �r+�
ͧ�Ŝ���e�R|$Y*if���,�f=�`N��&`Z�3o�6?`��|�u2�<�#������p�\u>gw���M�ן��\�pV�S��P�8|=�`:�0��rS����r�l���:����H�dzm!���R�X��)�f��f	�/�ǔtl��\��	eT⛜�+��dN��r� �ן��]B��<P��V2��AA�z�lLʙk���J�4|�Ob~�`?�A�#���1|(/���6�Y�J�^�dr��y>7|B	>��w������K^���վ�ٜ��.��4z��������||��z�<��H�%`���hD{{K8ꪄ.��n�m0O�B��=�k��\!����?�%�r�S�����V�g^�/�k,?H?�'y�n6�]Ȋ����)isX)��,p�C�s��K��?���U��J�-˯/F
��W�=�Nn�=uz��O���8��&	m��Lpҟ���1��V3Y
�ƛS��5>#,;�U�R7���C��i��5�QT���؟��[*_����g��I�:��TҮ�M,���ϯJg�݇j�CE�:F����妔{��\-���|n��������c��џq�ˁ���B�?4#�)�!�b�`x�fN��T*>�7b�-vSh5���I`�3pk��hZ+���{�o��!��r��+��~u�������[j3�(\�1��Na����>��Z���}A��!�}a�`H�V&�����ą�Ŷ�D��"�L�?M�?�����T���1y���"ACz��0.`h��pX�����]�
W�n
=���t�tn@�
����'��.�n{�6�ў���36ݖ�SĶ���1�v��[Ϝ�	�PNI�k\�n}�c���g���:^ҩb��Ib�L�:G��w%^�V:T�\�ڧ�?�֞�i,�lw�t�96���^?9��gb�=�� ;��eWD�Q��F�6-�U���0J���"��!S>�a�<��;���![v����=Ũn�bБ�^2�$�{l=��L|N?��kv�#���գ��
NE�Ȭ��[�]SgŇv�E�O-wƙS�ু����cl^���T�c�r+3�3���N:����`�S]����W�)B����;�X�	6'�Jhj/G';�<N����3��\l��!����3<^��[|���=��uv�G�2��o�g�И����$�OO]քDg�i��)�~�4lk��e�K�;�F]O�l�V��
��J��N��u?{�8��t�o~�⏩�I y�\f�Ԟ�ȯ�Ѷ�^�ٱwG3=o�����"S�-�����y���n�N�=-��U����h��]x~��th���-�f�I0����lܻ}9=�?�v�����'���PT�P��+zdE�#3�������ƞ�L�C훿��Lq��.�SX�Qu��G���A~V�z'���o��]7&ft�-W8}�d�*���c;8=/�*1z��y�q[����� [}�y��'y!Y�_��rpq���Yl�o����f1��p༺���Nu�6��vhP�On^Q�{�^r�Q"��ʽ����+�Ɗ�H�βS
��^d��ںN��/���g�'�.6붏FBg�%�u"��:W���p;!z'����w�>��n��k�l?�>���i^���L��a�kgx�s���և��Sݔ��|	��7�ĜC��k�v�T��O�#<��Y~�Y�Š�n��M.����Ǧ���Ճ���W�Qc�/8��娘n��=zW�qu�&�ı�{�߃i>���摭W����|��'���TR���[E4P���i�䮥o󇳯{m����j~��澐O���^��1�����̾���`7�n�9���O�u��}�~�����@t-�9�.��N��\�r���7�>�o�������?<��;oϐĜ�kb��?��7=O7f��R�-��`�����<�{�![�x}sC����X���f$ً�ϚV��t�\��^ho^c��W���s�>:3�}��%&�°z�ؗ��2��sωY��((dL���6�./_-O��T����b��e��:(�-�[�	�e�W�Ol���7>w���N~2��	0n�%W�j�<§��e�ç�܋P两$%E}�.���n�5ځ�x�3lCw6�C���@�sc�e|9�`�P���1�Ŝ�d �`�����Gla,�..�Oҟ*p��ѿ3�����~�Q�
4|n��an�s5�K�jke��+��ГWg�:g�������r’#��o�k���8]ĔP�8��I�+��<X��8|҇�%6�+��|(2s:�+�������T�����ٗ�>�k��:(��y����M�U
<>7Cu�Ouu�}�?iK�N`�[F]���{��s�X`x��y�$��=�Ė�D��v��TZ�ӟ��g�;�rt��j�&�^@�?���w�>w��n |�9U�"�Uw�;x2_�L�P���,PD�7���ΟP��'=4.=���n?r7ztU�c/��+��l�[Z�/�
�4����W�Q�}~�?�0>�U�g�d*��˩��v#m,m��o�[k���6/�y�7���"8I�cu�J8���)��/�.��r�׳''5"�끗#�ō��X���v��oºXq�x�ޠ0?���@M
ʖ�"�@gӤ�����J���u]�N�u
������&�>Nw.v���$��#K����E����%/�/�~��4�zkaמ�)'���&��r,��셿}>տy��-�S	z��ȇ?|���Z�щ��˕AH� ��EՃY��1��
|'�r��ﱣ�@�q2���6�c_ig�e�=Dx-�ɷ�
�����f�h���z����8Br��?wyYǘE [6AL�,�P|b�~��������Ǹ�"��ӪvȐKy&�Z��Ki��>�嗰"���|O^F)��|�g�!��3)ᙼ�b�R��
zL-~"F�*���
h_E�РN	)wWt�F���]�����t�k��I?��h=xh�॥���㖟H S@�&���Q�l<�ׅN�#}�H�e��r�!ѓ7�J��>�(,��"����
Hr�ʘ��9$~,��~˓u##Z�����K.B�g~a�`��dp�)SmE�K3�\�l�D��PG���͋�c2�
������KwDA��4�0�mT��$\����7|xhtȌ(�I;��C�(�����,ҌuxVh�+� �?}�ˀ��Q�a���g�8�aL�*�ˊ�5�RQ`�(Ue�׮Q����N#�Y��7���G/�.�	�>��ҟ��/���7'�������l����>S=��|MI�ޖםY_<3�%����7���:���#�/>�
�
(&�Q�8�nˉ1�:�ϕWh.���
ށJ��oJ6���bȷ�fRu�SQ��+�r�
�#��O�O��G#�S�9'z�#k�Ge�==w�q��{�mZ��ͅ�%��;C�GU �}きd�r8vw�A��B G�����8�/��oh�d\�]�|��Y��N��v}ZEt��b���>?}�X
J����)w�:1-d˙�޸�{�s���}����W��:��y�`l-�$R��Jf��n�W��8�
~�nui?��D��*$�'y�N��W��?��^�dr�'$+vb �t�|�*|����J����S���ip���aM(c_���;�h�N����/���K��Y�§Hi�
�� %V�+��2T�z��֮���cK<�O9R�7�/��t�Bl-�Cx.]|�<~�?zWp�\�v�T�2��>m	�g3��Jj�i�\�K1M̡���N���o�V��S�by�"8���q���VDC<.Z�C�����/�1�!�A��N��z��󴜆`����Y��b�~��@ʙ-3�:m�ӄ�b�z�(P��:{$Ů�m�?���^)�K�Q+N�pi�߽׳B�����{�B4(���臅�fşvX�ag�ۣ־X�Ҽ����u�z"0H'\�J�v�oG�:���:{�O�ڸ"�Y_i��s?>�t3�wo��֮��X�?�y��.����p.gh�5;���u�̞��b��<��+��?;�LO���w���J?�G�	�������t,��Ϳ�1�uz
����t��=�3���t?�o�� Do~�F�e����:�&DA��:L�3��M4�O�VRa�"���|�=dZ451;�5��5={�v�"=�߻ʪE�\�YO�J#¿�%�|�&��ty����M]0��Xn.P�"����L�u�Y����`���-�sjt�8���A�#F|u�~B|_���]�C����B+$|�,�>����i�gLୟI��a$���yB~��)���zqd>.����\
����v�Q|�M{��'��_;<��ک�XA��X�,y㚭.��?Eߩs�WW�;}.�(���+ཀ�Ϣ
��������"�.3>��o~� ��C��@�~�Lq~]�O�fE�䅟F��N����'��\�h��ͧ�+/A^�K`ҟ�3I�T7��tA ��;�ԟ^�C��qv�|��0���pJ��҄�3Wh���IuP��Ȇ(ds���	�4�gwO��C�̈�،Z��s�'!>��w��S���Qo�������Gb�]2�E\r$��!�*<52��@��ç,+���\��1�W�o��z{z~.Wl���񇚐��qɶXvA��||��I��W����HO���J+e��~����"�;;j����4��E�Ϣx؜�E���s�-�Ɣ!�$���ӂ'��o��W��U�eH���Ơ��ے������K����{���:�ܸ��C��[�\|��7��xT�oO�����'�帯s�^�����B�Ǿ2Ϳߵ�u�m����-���G�l��_%��1�'�4�wQ{�Zg���gpb{��g�k[�&�_j~��r�_'욤�Cm��i���Or�~�����I��g��n	x�?����~'�LJ�'��,�ǿ��p� (;`�_鯴z���Z�J�n
z���48|Yp�ۭZ�S�;�w�,'��Q�O&/�N9�%�b��?��kz���A��"��oAX���l>��m���p�:&�?��k�ċ��ۿ�*%ѹ}J��qc�<��}�j����l��CE
󫼢F>$��p��@���{g�Yt%I��]<:�k���Ɵ��U����]-�j�	D��(���Bg�$,�rMVO(ղ�;g�_�D���>~�]�>=��b��64渴�B��R=�h�<}����#[���7|�Yl{p�L���wWĚ(!UxO�?g���-���R���Id����Ԙ
S���|�>��*XW�����>�'{�H����\N�����EP�jn�9:�����uX1�����e���F4��(4�݁�k�E�_�-ae�S{�2K8X;����������	�u袜K�
�S~;P���+������Cs�$z$2r��О���=��	0��j$=��\e����'��;��N���Ǚ���pg�9�|����Y����.^���u�|pDW32J;�2����o��;��"l����ў}��`����*���N����Z
��_/���L:m�D����|��-��]}����� ���ᢋ2�b Ė�>6�W_�R�����E���^�-8	+y5n�
x��B�~bD_��m�/��5C]�$t�/L�'?��셽w<�k�*6����'W�,�vtx��:����/���M��V�kL�(
u ����4��x���3v$'��i��]\֍��Kz
��T��#-b!�ߖ��]E�IJ��[��I&L��~��5Pu�8=��hP�oǞ?Ͼ�3��6�ρ����Ԧ���'�J��?�a<C2U��K׺��e�X�{�#�U��I�rb���?���W�F=�/6"_ѐ�G�p�,�T?ƻ�3�|�_ܿ�
}(7��l6�G��
>t9z�\Iyb���;T�E����g���V�s�%�a�R������ө������[Kh�3��>���(�Kh����ܠH��NAeϽ���'Y��^+��P������]��@:��*���bҼ�4��S�
S�"�e/�vi%'è�.���
K������U�]�޸���%���oP�?n�����
!\�"`��F1�Ov�����ӚW��Tj�*�弸�
�[�a�ޛ��\'�mW��W}���*U8:����ݡ�b��܈�^�)��.+�ߢ��?��d�C��_nM��Q~�XVՀ;f��ˮ:�qwLuA�@��=7��v.�9�A���-F����S�w�f���X�9��7}�j���yۈ����տ�)H�Ⓞxb.�P��Wh�Y�6�]�������7��{��j�a]�-��~�#�<���ۢ�~�fz��4
�gh<"%�l#���vo����N��$�'�B-�5���-��ω��<15��xU˱
��)#�0#�!�`{��o/�?���*ȽY��ډ
���ؤ�c���S��w�"�jcbnf!΀{��7�/�K� �x>�='��bR�������a��}?0?�?1u'\���W���EC�X��/��:����k�]�˙YՖ��M���
�럾u�\�$��c��r*��Iq�~�ʠ‰��.�8|4�S���G��d�:9�m#
&���im-�5��0F�]9L<Z��U���޾}KZ���/�_=���ZdV��ᓝ���kpwI��;�N
�T:�b/ഠ�%䫟��=Ol���{�-
rw��?��˽��=�����w|`��.:/_�y-#؈=O&�����J{J^�!����X�֫}�>��@��=�i�M�<5s˅�Z�5�:�0}�Nź�����ҍ��k�1>,ֶ�9�ǿ>�Rl�/A���^(�R�w�@ѿ�`�_1��y{�_�*jS�re�8Z-y�	�y>N˙о�̀�?{\�I�s#6��ã�1W#:&��	�
�Pc��ORP7u"��5YM�6ح��v���L����	��*W�O�ItQ	[&*���߸9�
]�I��X�L�3��!?O^ˡ	���%�]Q��]��.(8_��{�Ȩ�)⿽h[t4�����&��4����A@��*�����bC2����ޫ1^D+�(���@���I.z�`�O@���V�e��\�(?���<�b
�ӟ����a�]h��)��m�?w|r���T�4��?��X=?68;3���#������A蕱���J�(��.vAu:�P2pz���bp=7��Z�C��@��ypv�up��7O��g�ANC��Ox��j����F�?�?��<'�dO��.J��|�E���
�ֱ!����tt�Rp�����(�5�L���d/5�}��?{�E������躘>G^���%�]�UMx(�qm�(T�W?��w�,[���Yn�_тs6�!�Ҿ��������7|~�	�9��'>�����)pp����wQ�W�,�?=�|є�8(�+�<b���^w�虼:���?�%?f#p�x�+�:%lZ�7o�T�gz�}�rwʹ?��$x���?P�!|޿�x9��i}��C�+]CX�S���2��Wȧ��[��d��$	��ᙕQ�
�?�w�]=���ׯ�C<=\^�����p(��Cr�"�ur�^
�Y��Xr�oY�]Gi_i*� ��g��\� ��_~��;������Z'��>;*��N�_~��(�F����?���'���Ph��?����Cl��_�^*pҸ����G �}]t�y��
]@���Z�A5��	*l6���n����+������_��:?��u�S�U~�����ap�g~`v�΋���əL�?�g�pp?�(��i��A-������6��α"8���g!���,y}e׬~u8�
�:�b;ۋ�;�7g)����?�K��6��o$��Q�V�O���J��'��Co�+6���0�	����2gw��83y5h��ڧ$���>�י1'y-�d������7r�ğ�Z�yw3m��kj>>�������w�s��?g�rBLvaM����}�JLgp�;�q�:��kE�����?>Ǚ�ޙM����Yi����A%՝^����ۣ�}�\����י	q��1�j%��w/"��Zݑ^I'�
��v�_�z�/��"nǥ{�
���#�sS�w.�q��wB1�a��}����I�Ο��.�����]�L^ѵ��H���
ϔٜ>���'���v|^�LJ�?T4}��)Aw0�?ڍ=�g��3Y_�y	Xa�������6mIR]��>"���%������]��`�g���ݨj�p5v��$�M8����W{��{����c��M.��٩o�7�;�|�o�玽5��Η��|�;��6��𞼄-��$U.�pY�%�?�ó���u7���μ��3|�7���eE��t�ҟ�'���Ez�˻�A5#G�o;�N.?��ß�����S�A��Go��V_��*���Wѭ�ڳI��@O�=<ci�C�s�K�-�s��A��iI1
lh-��]�W����:[vg�vXV�w�G��$`���*�����
��C�H�9���yyF��\	6_�����_BoO�55�j�2��ӟ��>%�.�xm=Vё��\tC�-�C��O�D��o~&��?��Df�?8������?B�@�k��w>�]x��S�g�����r}�k�?D��d�r���s���ݝ�!�C�u����;�:��>w�y��o���t��VJ��;�Aj<�v�äI�{������ԩ�?�{�S1�&>w��G��嗲����{]���?�w�S�M�ҿ]��d}+Nc�E�16�5_�z��o	>=Z鵴��`��?�������?[�
?��G�������ɋ�q��FfO��.���!ywe_?c�_�L*56��c�)����p�̓�>7� �y��y�֙�R�l�sR��_ʼn���u>��|t�7vj��W�r\�7��k\*{��@���x��񲂀l�/]�Q\E�C�g���'�P��}�-8red0��D3����,��y$���Ƿ�?�??y�c�r��;��aS|��9C���o���'�Kߡc��]�`�//��񧧧Q������E��vr��Z*����������x�_�
�ή�"��)�r�`V^M��=y�+�/��O����]j�f��`XZA�T��o�W�2?�Y�?(d�CrAF��B(Eh��G�$�	��i�m��}=;�&�"4�}/*_ >�v���4X�)����g��y�����I}����bg�d�|^��"�9��*"�ø>�,l�{��Sp�!7��y9�y�9x��(�dt]�Г��9�{��z�dP̖�Q����c�~�F���I�u���1�RR�U��<����|��5�3T������x%G=�m��I2+Af��:#C���"Nb���Q"�˨>?����FANڔ��� w积Z�#f�����-�לw��sWpL��ML
t�-�LJ��'g�G����+�?>���V�3`>Z��{�	�<&�I�Jc���(=��F�Çw��h��W��$��?�.x-ߟ:
d���q/9bQ/uMȧ�LN�h�&�#����`����M��G8�=���hI{���C^?�e����xL;�|a܌D����CP*���=�h�?V4�\�\l�k�zX|�en��
[6��E��'�Oѥ�}�������#b�&b(��.����?�ʔ�&��G8޽���	?W������A�ڋi�ݟ���?�ޚC��rg_>}�U{v�b|~���ψ&�0��Ç>�ϑ�;�1����L���o����A�gN��(����g��-�-'>��t�]'��\�
�h��,�]����I��l����]�u�7���O.K�X�C�Y@��-D>�|
�%�b/���i��Tr�y���<���b3Ѧ?re��B�����XB�u��f�<�}�S�Lp�������*Q�B���"��f��ˏ���y%j�����N��l���u��>�9n���Փ���o�|9RFK�=����u��r���gt�Oxr�Iж���	��e�ũs'��m�;3�+�۝5�>:;z�]PJ[҇�J������5|�zB�����s���gL���؃�Nj2
|ʉ1�7�J�ߢ�ϷoeiT(X�wt�OC^��hG+�v�wM��1�?���~�K�����a����$��o�`�@��~������ե����A��l?����s�gnۉ���o]�ji�*����qE1�����J
v�V�?=�rLJ��'P4�Ubw�
������oް|��Us��X��p4�a��"8�B�Ƌ�s
;�9e~����2��H�4�Ҹ�$�<�瞷�$ߝJ�S��v|�b��"8�XO��A���~>=�Xv������/{��� �g������~8yigz��g�/8�
N/>k��ǿ�����gp�O���.����§�Xg�~���=yᏟ��{���.��ӱ���n0�L?����ç'������^�(<�'?	�q�]]�E���ϊ�M����/����?�'8�����z��P��f��y��T5y��K&�^j�?�߻�C]
LJ���w׾�����3�r��'t��7|�k�8U�����tG��9���?�8}V�Ĥ���P��W��Wzx�c8�h>k����ӧ'_a�$zӼ`Y'[9���㭭F�~#8���c��JiaƵ\m>��-�9���o��ж��>��#������ѡ��}Y���t�^	���ż��s|�}�S�^<�뷻�-'9���z��W�3|��Kɝ�tN^6��Y�����|��\��E�K‡}�]���Z�w�Q�}��M����I^���]>������'��;|����{Ce�OO�$/��r��!�98�'��&��{�?��z<�>g����ҷ=f���
cZ?�ҟ�+����|�R"����Cp�/E����gr��`?	���]4��������Ϸ~��'>�-ß�y�-���?GO�r�r������/��7�/����"�c�?y�]o߾CMzK�6��{w�j���f^�1p�q���ǔ�:�$��;>��Y ۙ���8�=�!3
<w��O�A@�ϭ��	���:4���E�>�_v���gzE��џJȀ`�`_�'N1��޵�͂x|�l�Z�d��Q�Z���wt����4|:]��A�uf�����/6/gt��Y{|&�����׫ˇ�~ݫ���u��>��pj7���9�
>���$�:�l�uTfu�_�������o�X�ۿ^����;��puw�J�㳇ū�ҳUK��.�?�g��N������9���i���i�ٳ7;s��到��%z���ӟ��>w�D�->���|�ζ=>�<�}eX�F�d��/��.�9�6�t�~ߝ�k���ҟ��K49&���Pn�?篖�>�?���Ղ,|l�:f�|�ڋo�Pek|>/(>P���e�f��;�r�u�_�yt�[�c��rw��;�}����+-�޼&gB�c‚$.�l�م�����w�7��C�p�K{��X��=��������k�����	@���oc[������g�P9��W��>�u�e��9�����ƽΘ����5������c������-��q�s�yg��?~�;����Ǐ��=��'��7�S�3���D���k�g�ޅ�>�|�a�;�u�>C����w��ہ���
N�U��"��j�cZ��?~��:@ٵ�N!z@^��t�����q�^��>����;"�3D@P�����[D`�CvGo�U|sJ���Гr�jv=
��y��֎Fr�^\T����SfBy�CS�R�>��Auʈ���?���O��E�ػ�p���+�[��ꂔxD��R(����Z���B���Wѳ??}�������cE;�G���0�S%X�j�a�9�]��<��I܄�.����5�	��uF��,�W��Х!����o�[��SȔ���<}�����+��j.ٗ��?L�5�?�g��W�Lj�t����r��'6v���.f��>�CG�I�ӷ\OE��R9
�m�`p��e��gr�6��Iw�kЦ�W����ѡ��r�^���4��@~�?��EM�\��	�v���`��P��o$/��=8�ʴjǍ��Nˡ,G�\��_�~���������9��ꚼ���1��'�GW9��N�'
~��uq۠�3����$5_7���Rb+y�+���Ġ�\�?'w0>w�΅�r�K�J�yttm��w7��B�Ec�]����~z����!�rR-6z%������a=�[�=��;Od�/����/�4ro�#�E{�`��������x�����U���]��G�Bo�?�HwHM)^H�i��7�M��_:K-�h؋ѫ�3��cŽ=| Ӹ��ڽj��^���Krw|�+g���cz5����\�����D[��X�D�g��������"e����Bg�Z��,oF"1c
�x�/(���J��ր�I��_�2wpw}`d���`W��-<�O�z�I�\��Db�	�N��wI��z�Mw���Nx}�g%��Lmb��S�8�6�y0у��_�l=|C�Ϊ�MKJ" ���������-R�`�z��`�SjB�n��Ƕa�[��D���~��߅�Sa���y"��!\r��~a�Cc>�gZ%���B��}ґL�&�s��}�������	|��������W��c�o���<�S��~u������b��s`�-%�,�Q�W盧�O�5��
:#�[�{&�'l��wk�TsO��r����RG� ���b�^�l��X֏Q���U
��Q��~��pu+�$���������
ϻ��(̤�+����jDd��;��Z�N]9����?�*)���4w��j�xE��(и�UV��~���$�ݿBY�Q!&C-��?�������|�:����|�o�聇�#���1�j�I䮩*��}<�Sek��%&�!!Rcxz��!�� *�/��Ѥ����/|�7z �cW�U�A����9�=��\������_��~��^ԭ�,��To^�����2��~�M���*	M�С0l��O���D�x޼Cfu��y��#z>��F9�!��Gn~	
��˯?�&pׯ7�a�^&�zx�ɞ��3H�D���.]Ic�Tt��Y���{�?�y��a��+�B�/�=�=�_�
*rv�w?��J����[xș���֍ȇ~��j����kv�*{�z�ٱ�$�K��{������Ag��_~�����5�`��}�mN��i�p�t3NrnM���d��Ө\J�ž֨:w��09DBO�O0�%�	O��Ы���~:�\):�8}�7�+�3��'�P�)��g�/��/z�����<ʗ��	'"jL����=��cɭß��''=�3�r_UJ��vd% �H��p��Gyt�p�7�Q>y�C���Wt�s�4[Z7DO狊3�
ב	�-U(h����mM;���ܲ�i/��;��|��Ѭ�&��)�#ƫ�S�t��_��
9;l�7zԛV/����<<�rf,�W�H	)]?�=�_ŷ��u�OzǬ��5�22�ѓ!ޏ҅h9=�e�ׁՂ�W
o$ ��Uv�/-Es7��9��8��w�j�q�[K��5��t���Nz�[����m(:�=�D�a��($����;�����c�d�/E�����J�ZK�g��c��rfϻΩ�6<[���?<}29g?P�v[�IO�;�
_n�����Z�M���@�t��X+�����3{o~��\�����%�����xЙ<+_�~��pr��Լ�/zUtp�U܋��۩��Wq܏1�/���4�86��Υ>ʧIy���C&�s��4���p�u0u�,��,,�R����1
�X�i^�6c\�Ǔ��F��ɄR������
�a����5���)����Ͽ���3�79�j�N>��� <�K,|v�U:<��Z,?'��h��z��^��~��#�����b���V$ԝ~Z��:��|����>�����N����_r��E>�}|%���&�0K9� �S^'��x59�K�(Sy<�!0��r^���v�'���P
����U�����:���;ʂ<���W�g����k���*<'_]�"��W�G�0�n�=�=��W~�W=�-��O�t�2z���Y���Ӑ=7��<9���ܔ�e�=O>�a���݁
�@nf!;T~��萏/���3�7 �S)�I��Ϩ|�ex�����Ū������$@�����r8���:|5>ߏ�nw�i?�� '��s�p�˫x�_|��q����_�G�Sy�Vk��}x��O��8�r��|�';���&���������#霌�7����#=�Q��(�f����?�u*|x�3�f*��vg�e��-{cW�/4d?��o��a��%��?Y:���9R�i|��W]pq����l7�xJS�����v�/��v�����XՀ���S.|���?�x��'4���������ϣ'x���U���@�qw��>z������8=9������|��٫�?�/��>o�2����*$�%z�3:�0z³���|�'�ʗ]Z��*��)�$��7�R�d�
<���ˑZ�MƜ�F�8\D��=�o���{s����m�X�#:�|wG|B��#�������x��鏍���>�N_�[���~E�K���{�������=�8�CU��ɏ�yۦ��6�UQYwE��z�_?�p����E��!����}���
dG8�W�n)a&�[�4�~�3���W�?� �#gB�5��_<�����n��T��m����\bB���?���9�(ڳ�XC��wB���ûw�٭���ps) �C��~�	>?B�Ϸ��C*���e�=e�l�Y|!>�"R�*E��A��=	�G�rJ�%��/h��{�k�Џ|¿{�(Q`B��s>�/s)Ã����޳绿$�?����|�O���P�����Žƛ�Q�#o�Sf�;/!U���c�����;ˮ�{�|��LuL
m�͂!9�|�&��Q�v��� �5�H$3*<S�,����=�rRU�9:���L"z��_Ο�D���-�`zo���-�R�1Dw�a�����kDz�f�߾z-nt��<��u1B�I>�Η��'��=�b)�=���8�<�v19����{�'�?�\��<����Ι#�3�	S�vU��Ohu�g,�f?����kJ�~�bvRO���B�n���gw�t�z����+6����OJ��z�sY#�K��Bn�k���ջ�o�G|���ɭ�e(�Fr��Z'j�î�m|}&�����N������ YN+B��/�^�����A�d�� 4ӿ��.\�ζ������KYq�?���9s��>�����%�\~�֏������0<l��Oz��x%>3��r�
�;'}��=w�V�GO��̬��J�G��R����(�#f�g�;�^:e�JJ���<x�<��G=�	?5��N�b�kw�45�b�/a���z��|���T}�'d|Ϲ1��z�0?���D:�m���b)�U0��|���C�罽���x�����M>FK����X)���������%ost沞��g���,�ޝPk|w�
 U�6H)T�1���G�nF�L�0��7���e˕�c��B�Y�g��n�8kT���=�ف/ps2r([��]W���91���9.���L�O"�v(x'1�k���ܳ?��+�o��<�eQ��U�܋�K4q
�z
��A��W���rM���Xyt�.�Jw}��I@y�$Nm������8�G�ek[`!��ڬ��ܣ�����I�K�M�?
�D�^�C�[����������B�4��Q�SN���W��i+?~��8�'|�ߩ?]R���u�]��@�����$�/i	��98�έ:F�97~l�N�R܇��}�;̭c�k-;N�����<,�8k�o&"�P7�6!8wp�?�gc��=�g�1�
�q�5����$<–53�xv��w��@�}߼}dx�=<;6Q�����r��ȗg[����ki�1�IOx��–'z�Ĵ�dg�n��֟�G����WNUkf�a�O6��!U�	��z��.�:�꿓�E��9O�F��0z��?z�$�e�W����'<z��ڜ���|�'I���<����ʌ)�ʗ
�e��6��������'�����;+{q��ɞ�#3;�����n���q�s|%��ž�_��q��?�K���06�
���޷?oGP�����ӜcB�X(z@���2�Z��g~
�Ӟ7Gv�y�p��\짵rN>�۷����8������Ɖ�'�I�n�_�g�����m)���^�?���^�Q>z�/�.��*�=�����TQ|����-�f�����'�7�?<���da�y�+�Z�D���L��q���'�9݉/g|O>�z�����4���i^[���b|a�<�g|�ˆg�jztr������4�l
�Oz��3����X���CH��B�E�T�|�e�^�_�N��I>�u�&q
?���友�^�d��'��O/q�ቯşP�~���ٹe
�5���\��GO�d?s��)Ͽ�c�+<L��wi/�'�<ѓ��09{�H��_��ók8���5�\�,Gg[��s���l���~|�x ����~�F������_�}�Г�ϖ��S�:`���Z �%*6��px�Z�<z�'��a�S_ѓ|�F�C�v����\Ox6���N�GO9�}M>_���|ƍ��
�Z;��L>��
��k�{G|�c{�~T-�:��)�dȯ�<�1�ɧgr��--����G�D��ʔ;{N��/M>��x��ym

�@��~�S�-�udWx��	�Ş�g�4^;�/G\hwjO�k�)b�9�0f�����پ/>���#����_�~-�O�\�9��_�b���4J.�}��F�c?�~��𔏿�	�_�{��+X�y��ToRnY������/���s����Q>�IY�3}}���<g��F9%�y�L�<�����c�<z��G9��s�����Z?�����j|���~�/@�����xP���N�D�8_lz�ȹ~˅��\����Y��|=��}r����r�.w�������x��~@N}�~��/��`5��j&�/.�A�=_�֞�aw�6<�k�=�|�뒮_����m|�x��rA�8~_�~β6���3��x��G����6�G�S_�<ۮW�O����M_!�錞�5#?�ܵn�k��ܝA��'v�/���~��s�g�=S���9}-�q���%��dr(�\��Uw����y��x6yRE�3���q���_̯�O�կ����Xx�{U�E9?�S���g��Kv�;!���ھ������̇?��_�'�619m^}��mT�:鉯/������'����;<�'9D^����i�o���+:��
��g�gZ^��~�����'g�KI��?9'��g?��=;�mS(�y\w=ՎO>��LG���Ѣgx.�t�D��/.�<����j��W��xƗW��bx.r��/��v�
oO~k��?{�_�����Ծ�����Q>iv�SP}���_����sNnL�<���QΏxv�ڣ_\�cg��S���]��+���N�C�E�V���	>9^�AV�#=�=�7��'_�xF?��|�=����)OO�_�z'��;�O.q`��.ގ��~���������t>�hg�_������k�Z�=��om<�:��w.<��Sn
�����/4�y��x|�0z<5^rV��N���y?H,��Χ)/E�L��t��͍w<ꕻS������DOt⫝̸'��R��+�P��ŷ6��21o['?�cB����W߿v���L��d �J��2m�t痈A�����f#�)�ZnGM�c&/�[G���
)�3��/L&�FS���_��G�dX`�@b}y$�^����+��)�Z���.X�#񽹭?n��Z����/��n�B��Z�Z"��ky�w�����Oo_�,�Z��FQ%�Xҙz����2�}�_~/��.{� z�,2�|;��g7��;o;���рUc�<T_�L�}���Hѷ�&yN621vg<��-�T���Z���X,�I���v�e��c����I�7���0�W��7� 3�P%�ı��H�Կ���e�w��D@�*zGϫ�/��~|���WZGR���r�6ș�%3��nX�%��5��G2���;�Y%�v��ml	 �נ��Y��������L.[��T/��Va�#_���әLbFB���3�|�����ii_}��O�ߑ,	��){`�	?�OF���
I�R�V��P�{f?P�Z��W�<���qu1�X����ի�_����KՃ˪������R����<�@8���߮�6@U�o!@"Hi��D9:���
o������XX�V�7����
��ѯ0�i߳�r����>�����yVkD�[D�������O.6��ʱ����D	���[/�[;P�ax�.��C���xվ�j�;��1�(K>˵o�ətƗOگ�1��KD>��x�9�&�c����9�_�!��>��c��]fD�A���������Я�k��#�g�[{�'9�������4�3�*��P������5��������1"�r���Z��T;���AVѣ?���#G$h��q�d*��S��%~�/�w�G0�hܹ�Kx޽}���
��9<�)�=�d��39�N�-B4�e�xH�TP�59���\�}K~���5������z+O���@�����P�:�6Է���tr�I�2�kv؜xזy�̤�]��/v�a�>���N��:'l�l���;S�>Ʉ��~��*_ãW>����Y{���V���Q��`�_�d�A;9�[0�v^Hs�I��nތ�ǵV�eOhMXL�_�+��G�u��䐏��Wy�;gk����X�U��;�fx��-<���im#�'��i��H=�|�㹬n�3z&�ѣ�ރ�����G�ѣQ)L��?<�NŪ�t��~*^�h!�������v�'���z=�y���SN@ÛS�D��N��::Q���z̭��pu;ݙ�����7R�ۇ1?���(G"X|&����:2�D�25R�z�}�����:�Uty�.��3g�������%��~�V�Z��GO�~��À�ʧw�ީ�E���T)��[&�ۜ|Zw��7��5�~�{q;ޑ�[{A����b[���q�N���m��|G�1Q#�̿�k��vnG�I�%�;�a�"�N�@�,��䣰Ov��W�_t��i�wz�������mX��A�׃|�݇�y��D���/��W��y�`
I��y�)���'��$դ/���|zT�cl�e?���}��w��\�+=Xc�>	�v������~��=��'}͞9�iϐ`�s%5Q(���杗v�K�����	��|t���+;�_�~xv7z�ϣ_x����oL��`?�
�쇿�y��\2!��Bz�
rvM[�%��1��c��œ��]7D��_�,�����8�q�t}2<��\R�Cr�/�%D��w�'^�ox���Ek/~;۝���]@���d���� ��3��ί��R�{���|���_�'4>B�����u�*��?���boݹv�Fe_
�D��="f����������_��]<��~���]sӓ=���u�~�$��<s�{�39'�K.{��ƕ�5<����������/��	�c�y�A�W���3�ڸ)s��������CS��ZE���@Lx��O�fw҉b˕y�glOE���Z.�"v�$U_�N��	!���l��z�Wׁ%�����89/�x��c�סg��qe����<��09{2��@�(g��d��˕y��Y+>l�9<?,F������/���-���=��j��rͷMn�-���EzLM��ݙ�n�l$Oq�~��g|�dW
<;'�@�^�rs���a�{a+<��ߌ�:��\����|�b���_��FO9+� ���׎H!�B����Q�qA��z�!8~� �{�6��l��Q>z:OԾ|?x<���'̅(L�^t���έi�3���<�e�_��#����{�V���
O�����c��C�
���;��Ly��9�ꖥ����/4��c����u��?�����/�A�ּ}B��h��(�A2o@=!��	�w4!����9{NΈ0�����������=�ȩoX;{�Ĩ<����Ϝ	3NX(�����jr^"2�D��=�	���v�P��#^t�8
݁ʂCz�3�D�0o-�s�Q�|��3j�;r+�\���..F�s�����^�Iv�i.䣻��]@�t&'HY:�5�sw����
�,�$d�I8br�"?��]0D���Ȳ��N��_�� ��0|���-4!���Bp�/�&g��vf������?��9����8���G(�^��|G��<Bl�c�~^�圊�;	_�˴l�*P˒�K_�����)>����9����I��o+��F�D<+�}�s����t����z�0ѣ�n�s��~:��-	��\t&�ç���$z��z�W�!Lx`>��[D*�\��0F����DO��U���Hak��8r�zf�����OA��Ρ�.9p�j1�*ɄL��#̉��'<%
�g�B����?����@��Gϩ�Owzjb<*eS�^5�_1R�e��9���'�O)����ט�Š����ң����'����+_O��1�[�מf�Jz¯RV�:�I;�7�C:�n�h��;�ò�����C�sGA.6��/�g��qT��B=��7��g��7�L'|�t�������RЫ�kOs���1�8��!�Ő��%7����F��S_���^W�*��N�輯Y����δ��~��x�B��4�gW�Otd��)���ç�p?px��wbn�O�����W,��_� S1�Z@�Q呿�w�����t_v��6��t��b�!�g7��D���#xrS��5R�Z�\ل�Hs�B>��Qu�?���[��[�����g�ɧlTJ��=�Nr#L��;�+i�~���eg��t�tȋ9�H�vM��=�%o���ռM���kj�>}Va�GYf�4�B�7��Df�_K��M_��K�;�6y�
�����?�
��U?����?|���n�羨�C��<�����%:������ ���n�������
��L~6��J��Z� 8�3h��=^���t̽��_��� B�N
4w<���c6���)��"���mM�|��Y��oSrJ�My=D&EDD�E�.��q�jt�nG�[;d�X���%�I-��zIC?�*�6U�ks�o䎴�N��Z�I�A̿?�F<�q�R�C
�|���b�F�_#�~' ���L�r#�	�P��V�~�y��M_�!)��[��$J&�ុ�T�i��B��j���҅��&�*�'��wI.���#N6��?���W�Na4a��;�I 
܏�2��f�V��W��Ԇ�ǻb$��8�z����c�k�=LZa�֧�(J/��o��RQ�z���X���;�vs�<�G<�'��I����9+��r�i~�!9�z�IP.��r���V�ܦ���{p��XnDM�F��A�����HF��_�վ�R#`#p�6<��R�G����4���v��O?'E��<�]}zp���0��lSxs@�g����_x2�_������O�t�|�%�l�J7
��ָ�\E�����_�����c�Q�(���>%�p=$Wi�?��ȇ5t�"���"R��O�E��9y������5��I�熻�3S@R�8����z��
k������ߟ��9u�eMzx^�jh����_��`���O��S^��3bD��ĸ+z=pM���)\���������<�����"��Q�uv��'�`���c���\�f�z:�';�?'���So���_��~�&#��O��W�GL/�s�؃z�3����M_
��5�f�	�7wl�_{\�?pD��r�n}Cr�Ɯ��`��ʢYs�"-/l���,���	1��s�"	�
���es��s�͛�;�ů�˷��}����mڽ����"&� j��`�Tg�����r�!��\�]��Ү�6{�A���D=�R�!g����;�Ѓ`��_�$�Q�Wzonw��ܺWi6<�F��in�{�z����'����KJ����r�
O�"��1�r<
�d�,�n�l
�s���<��nM���W�J�(fO_���|�Ͽ���O�_4
�_���Ex��L>"�=�k�ڿN��0{`'�޳r@-ZK+���_�9�������_�Fr��3��ß�p_�w�P�Q9���|�=¡E���S:�(l2XxFx�O���W��G�c�h��:$gH�O�=��jG������iR<<�@<<	M��ڛ�*�O>ZBk���z�̟L�uW?��V������^�p�_�>��91��Y����$H~=���y��Y����F�v�i������p�/��џ��S�L�'9���Z=<DW���u�W��[��~������WO9�;�L�>��?Y���Wz_.�K�j�/:^�
���5����<��p��>�)�&�6�
���U���ք^F"�7z�+8/�Ÿ<!Ͽ���哏o�����r��0��_����3��w��-�OqLA���A�Q(L��go���r+;5��?K��É�D��S��_�>z�C��w]�_����՛|:���y��/�`�k�g{�8V{���|H>w{���
�lYn\��/qčߦ/#I�����Sv��#���
���`��_� 珵��~j-
�r\��ȗ���=����G7@�+�a�C���R�z�O���~T������<��HR���FIUݝb aަ����<r���,��~�"���H��-gO��������,�g������Q˞=��}����l�rR��3뷬��������:4uP�Z��_��c�����J��Y���W��ԍr�GOxF��<��dlm�E���β4R%�ѳ�3��H��d��s����9Q��}����Nzʑ�M=ޮ�bY]���JF���q�r�LJ���F������yW����{����)�@�mH�|�v�cŅ�����M���}�'�t\
gҗ�|��&��9=a����5Մ'sz�OF����E	9/���wg�H�~������z�v�<�U�y�E����Ǜ��[�Ld����m1
�3Gj� ͷxN;�Hm�������-����F8�7������򄫼���u�2��3��o�O.B�Q����񗅐�
-sf��h��HY
b𛟚�V�º�*2raZy��I��u��	���f�97?%�A�25��H.�� 4,dW��P����Y�N�	��T�E��w�<^�Q�$i��+AN��C>D=��_DOx�3�����|����A?J�����_|=�O�G��|��koƒT�29/��j~1�:���I_�
Laa������f�Ѹ�ٻ�Ǭw�E�s��X�O>�>a�Ct��⡯�O|E����'<Ŋ�/_�XaB���ɟ~�����j�����x��B�	�8�Y��M���嫚s|���X7��#���x�������y>��$Ȁ�K��n1*�&U�J�!T��`�E��w��r�I�ޣg�٪�*z��f־�,�D*��=�S�uGE�̀���)g��?�V;R���RK�'j��*��H��N}������Qk�D�,:�ױ���	���'�^��`@�~��uQ��7{.�d���pm�\�͞�q::GGp��;A�/��Ĭo�$4���Y�(ADy�(n��]+�E���ݫ�H2�8"����J�oz���N ��T�)�G};9	��� �|��P���Aok�!���]0�P 
(��θ�*���ˌ�xN�C8^��ű�-+
y�=�q�*�U~J_^��-����>����u4Ee�O����X�sS�;���>�!`y��	X�R<W��I>��d�8�ã<<�ŮT�D��C&�����N8Q�/�/N��ߋ����!���㗵���Z�	�Nl�ʡ�/�|p�-���re����Or������ާ��DS
.�R�=p��S��	��'��
�wr�Ǻ��H[��]�߽tG�����-t�ƒ�����\y�JGb�<�+�'~E������]�����v��P^R�O���h~��y�J��DO�/�����׻k�
�;:౼��Ɇ����o\���l�.���z����Ǒ���+���z��19����9�a�n/���l�DzR���T�f���J�E���i����۹/O�3i�:�E_����C�\�~��,y*�~��Gg�G�K>��X[=��Kh��t$!&9ӤDMcy��]Ђ���}���i�nk�f�ܩ�?�Ӣ��͵������>&ק��g��.��Z�d5��4�$��P�"xx�F��R���MN��_Y�U�lX��e'qEkŰ�R�[��+�}k��OjFԚ)�>�I�1�b_�J�f�)���i�j9���G�O�q��6t.�<���������z��>�m�P��$��'F\YȲ8 Ǝ2YUkq!ғի�����[��c#�rm�Q<���d?PM�oU�l�$���U����n��:,0[GjJ�=J�N�Z���T���?趯Q���V��8���]\�rJ1¨��s7t������%>�E�vS��ˏj	���M�F��(_�%U����N�!7��Sά�/�QU�;=�׏Q��݁G���*%����WF�0��x���F>�+�� =L�m���%�3k��8�������������G����PC����J��!F���c�J�
<����b����/Bs��}�T��d����$�~��J8�,J�S|������9�4'�9�~�ʣ�yOc�b!�z��D�0x�<:���NP��^S�i�pg+�5�%MoW���Pl�(8<���wk���F:��Tv��a��r�ׂ��T��~�����B�W�%��o�j����
�"p�w6r��69+_��@�L��j�
\{o�@�;��'�g�C����I��w�Kö�$l
��F���o.	1�W���z������/$Fs��ѓ�>�H|s�����8������[un�.<��A2	�{.̑��I��P`w���rj���[}�rS(�I <{�\g�x�٧ى��ݕglBks��iv.E���oߕx�'n�~Z��&<�����6#���U��C���(Ky��;z�ǐ�}T�ٜ/�����9�I�P���`�&
�}���LAJ&���<o&z��t�yGF�i��r��_�K�dϾ��c��k���Z:��H�/5O���DO�8���Os���'z���_4b�d��ua0�%焳�l<�a�<��m����kަ��C��\��I_�U�*9�O�q��w�5=���<sr��i�(�w�\������I��^�`k3
�I���y֥���i��/��bd�Z<�J�`��7���:�����b%]�>�ã_�����p9:��-�%̯�=�Er�г5��%a���=�v����n����Έ�k�-xk'�x~ӗ�s��\/Ҟ�'�ޚn�Д0�����
���SyU�cqLhV��4=���(�­��֪�v ��y�}��-��z|B���\������v�_~�O�G*�y��1,w^�>����0zG���=�!��{����3�R<���
��0�i
\6�Z���>�����kß�Z30�At��:<���1t���J��G�*Ηչ���o��x�W������%}�/<�����T_��GA�pN�O���'��5z��N{VE��@� )����������s�C_٧�px��v����$~�Cx � _����OA�|X�"2�̿����}��!8z�K~����O����'�~	~&k�:�/r^\����cf�͞���Lbw�*�6fzG��J�)�� ��Xx�k~���t棜�#(.�a��)?"��V�C*��5�t�v2�r��'�xP��~
�~.���x�5_|��3��ݭ�;�<�ˏ�i��S����yf?
��M#��?ۿa#9���L���a7����p�p`��ȫ�Xr����f?��^2	�
�ܝ���.�����쇟x.�it�O_I><����oi<؞�˙ҳ糿Z���M�����?'=a��t�kx*?˙H�.x��:�B��'�zg�7��/V>T���Bq~�kŒ�튎�������PP���56an���M�_�)9���E�c����S#I&��ܝ�ߒ��Y�r�6>m��O�8�|�ɧ�~!�|��t���K�0�g���rn>?�{ɹ�^j�F����)����7���Y	U�����wdK97���'�un��������tX��χb̍X��Pݎ�e�}W���:mo�h:�WA���6�/�_�F�號�n�E���a�>x.��"��O�'��Y���r�F�s���q�ڞ5cc�dd�yM��}r��t��C>��JN>f�jL�d�U�Ҵ�v�/ѯ�i���N?ʓL��6�WnA��x
=r\J,�h�
K4
�f��3�Wcl�R�i��a�w~��)����O�ο���im�yCgN[t>#��=/���^��o����SbxF}��?9;��ڝ5RL����w���qh�����U#�{���.U�؎�������F��=r{K_ٳ����T�)�l�mB�
��G{���]g�Y������ɭ�b���h"F�B���\.�g�(�r�1�و��
9�D'h�	�������#�s�_�%U��s�;;4%�]B���B�oMbV�|vwњ�W8y*_��a�M��������%�Ԏ0v����g�u��v��.�4A�v63Ɨ?�K_��Re�S�{�y[<)��g��
���̉l�0�����}�k%��wq�|Hrs�71]���-�sv"=^�ӓ��X��kzM_ɡ\޵��e�8z�;��zƄ�,��LtH9|��jX�J����<�<?U59����‰�cV������a�2�����Ԏx�ӡ���t�T|��{
㚾ȧ��UUk
�鿌ʫΩ*��I�^.K����=~�T�#C��n��ѓ=cF�D�����h�s@o���	�~K�$��|iR���(/䦗���}<��S�=N+}.�w~��O%��<���^r#�c<x�M��?���w�l�s_Jz_Ms/��>~Ozd-8�&�f����NWǜ����v���(�|����><Ua0L+<�W�'���jRc��l����k'��3�&
��k�}x����'�L�cuE��F>ϻ�F��R
���١�)���ߐ�fu��tzI����Ɨx��
���b���W4Vy*�?ѣQ��uպ�i�G�j��\���[���_�6�����Eg��B��y�~K�@�N���L����3l��_r�����ɿ�&q�Yz��xC>���T�+���#��#_��e&�EP�+z�r��K_��l�9z�#;� ��rk�%�Gպ��D�[ƺ c�_��f��9Wּ-�C�&�(�=�91|��ȈOş�j��x��e�av�������,:��N�뾼c놧�z�tk3��!�[`a��DZ'~#��g��śo�˕�{�H���G�cue7�{n�d����������(��>�����9�XGO����[���"��� Mo��3�)Kݡɱ��٪��_�b֚��ܴ�ow�>zv��b��o?x�6�[*.u����ne�O�V��(�>
+x��u��Kڿr�Pm	<k���֘�8;�y�<.���Q2�k�ʨNz*?<;;7:7G�
��'��d�s����0<�����mZa�DI��i
Mo�4ȹ���|�w�SG��ln����~���vr��6���gmd�d�]6yY�\k���w��]<�=unJ|��wo��9���k�#��V�3z6�q�o.)9Dz��U��gx�7'uN35����O~4͞w�l�zrIs����:>���ۚ|������_�S�����]$�5C�	Ozo�*9�����33���eN�1b�?����~wL�'�I���)oCr��Ix�揞r��:�-��1G���ڝJ\N	zF��L�����i�,9��x���F��N��"���x����w�����ѣp򙫆�gg�OG����in�h����/�'��|��ar.n����W5U�T��9}�7�2}�����R��_�1o��G�n
���Wx�H���ş��Dg�z�3��N�L΋'�g��9����K�gq����mx�z��\�bϻ<>:�q�B����g�e?'=g���S/��O?�<N_}R0�t�FpŢ'��g�����O}-�ˉ<��/�\���g����U�	ς�i��W�]��q�^�'z��菞�O�j/�kz)�(��9������3�Ã��o�zv[����a먞�=݅�c��W��S�kwf?��0����yz_|���s�k���y�d��[�۸��P{��DRx���y��f�ó���~��W��Ɓ���3��޳��ypo������K��&�r៽������CT~������\��s_Ar9?����'���uglwD6�á��{�IB�������	�K�7T3���ex���G���ڋ�S����~x���9<�s�ex.��u��e�<��޳��=g�y˥�3�l\�5��;{��w>Y"�|vO���~��p�ʞ7y���k���o��V�Wo����ϩ��Er>ۯ�'�|�a���Z��/�3����ƛ�ҳ���I��3�4<=��'7/�~R���>����͏�+�;7����'$g;����gο���8x�s����y3�W��߻8���iL�sH.���-�Yo�9�'�?�'l�4�I>��Ƨ�k�����=d?����O8���?���+xr.[q����s�O���S��gz�3c�\�ax:ea�Vg|S�q�g�9�zW>�d���
O�Q�I�����s���k��ሙ�/��l�|�u�w�Z�.��|yZ����[�%<�l^b��59�g��ߤ�y���
/�-��>[��:�g��;��y�s����r6������S��ѓM_	3�����42��?�f?��棪w|-n�ד�Ƨɪ�'z�%�,�y�ÿ������q~5)���L�������s�<�^'d�'�G/����@��3�l�wx�g���awD.�.�,�c�-�Ϟ��Wr�Z��,����yx��9�e��l[�9�;����(��g�#�<����UϞ�����:��q���Tv�����D�i?�w�,����p��hK��7�g?'_��̞������(���~����Ρi�6��vy����w�I�K�KK�̞}>O��o�����k�����3��2�';<�C��_!Y�\=����O���i?��W��
Ur>�y���:����x�g�S�e%�Kc����^f'im1!�������9"�s��'eEO���,9i��|��rN>�3����Vu:�L�����'���e�|�9�uʧ�=���቞�,V~�W~tvhջ���'�Ϯ�x�'�O����l���1����c|}
Oz9��G��O�=���n��e��!�)��Bɗ>�-���+�<��1<�;$��!�L{|���F�݄ӎ/�ϑ��4���%T?=�O���g���o�eR��b�̗�JU����94�Ѽ�a���on�:��D��>������%5FҪ�z,h
�D>G������eSHz~?�C���}�	�L2��+�A)6K}C��������k�tA|N� ����S��E>k��=��#I^MV�t_Z4n��v�{�%��gB��=������FX�H�R��ge�)]���?�Ǭ9��+�_�@ޓ���-��ā��;z#����%<�#�(�0���F�m6@��b��]]����]���_�@�?�tM},h�HKcKY^�$�;���%���9n��+}�w �\��|����[�٭��-�њ}?�A�'+gM�G]<Ny{�TƕT����@�ΆB�0	�f���$�D�Yl�H��]�.�=�ͬ���
;"R\���՗n�|�~$����Rnw��쇜E�lݏ�������$n�u��%z<��~k�ƒ_����z�$�%��ɭlD��C_>�~=�����W�o^�>~�!<��	�g�I"��W	�;�=��\$�ܔ�Z7I�E��'��������?��֛��_mm1�
�\�#�ѣ^@�q�^����Z�b7���#�
EoL9����!�'�~�)g�=���5o}�ո���5)�3���1J�ű�-1fr�y,���yi�gt��›�Q������P���wؿHV�L����z�:��7�yk�=�L��3�4!�4�G>�y�#r�O�����R��o��C��c3��,a��L�U�R�vO�n��
��Z�k�D�����^@�Hr���+� �C��Cc2<�3��:��C/@~��l�քjV/sj�тJ;i��! [+|�� $�ӑ��ݳ�${��cak��cT�ӂG�-�,<���U��Z�b��D��ݔ�8x��� }�dW�j�%����)��:�'z�}��:�΂~����/��˨�W��ߵO"{;홿�7�U��!�0�m!=ׄ�7�@1R��z;��&�@��V�T����6�,R���ɭ�F~��[~�t�5��99l��-5�KA
��e����(�l�=�7�̞��쇷�T�iF����/�̉�Lh���}�g|Y�l.��)���؉�y���ߗ��)��50C�ϣg��\�*���yK�tX������gn��zعA���sT�3wpccR�'�Q~x�����!d?�G�`'��R8��_�2�?W���N�s��C���[�n�(�C���"�nr����w#�gr8���˚�<�R��ϕ���crN_
��?y�����~MI �.����E�:r%�k*�޲D$,�|�|��cHН�ЗM	�$@�=[#a��q�&"���]w��i�#���w���c�h�޽��Y�(�Em����#=�}G��!���$�L�*�g�䣣�(��0/�w�����9�"Gw3M��N�������_����
�/�M��=�#�3��Or�|x�˰���wzI��Iq#�S!*�8�z�G��@ox��z��~��	Ef�����K�O�ϝ�(t�v�L���M��O���t�����=;W���o�\ۈ���d�L��Uޫ���E�P�0]�/^a���)�q���$~���Nx��.|L���,C�*{ rF�*���;}�~7�����.�>�Q�w�A�!4;!Lx��靡9��ɹ9�������c$~�i�~��>ӌw.�u�ީc�ߢ�/�Y|��ϳ�s.�4Ɨ=w�aMc���o���#���?4ےS?���×�g����s��9RS'��=��~,|��E|E�y�&�{��.č��˞�b��ٞ��
��U%�F;:�\r���D��.�W]��>�ߝ��VD�ƹܗ�I��$�*�d�١�O97�K5ů��5���ܚq�N���!�V�|���L�?��~Թ��L�v�ä�}r^��Lz������p�2�e�N���\��~z��T��9
��~�+���?��&w��O����W7%��9�sN�������r�Dv�R{�<������#=�U�t���}T~�t�ʣ_(��I&1��| ��C��P������m\O3���I���>�g��g��w->�gS�sA��W���U,9��g��/@җ��	�ӿN}�I�5�_�ZN��O�Fϔ��Ӗ9O}m�G]��/�����������6F4�p����t1��{���N�h>a|�j���sǦ†J�y��
�B���s�z����cJL���94��M���mO��,G��I�ok�ݚ�M—s&�3!xMu�Nr�T�u���G��-��OxZs�~��W�j�W��t~H�n�@*�ς�JU���w}-="x���M��]�K139)�/q�@�yP|un(��`^K�5.������ٽ���g��U�l�L���^N'g�b
xq5�i��#y�~Er�b����а���_&���Y ���W�L��1)���/�z�,w���Y��hv⟔U�ɤ�—	����w�X�BzW%���hz
��R{�{��+l�~k�˵*��!O������Dp�]WVa6YΎ�a'���|�C�f{h*k���H��z�YnM�s�ҫ.mw7PW�/G
��(xHf�7uW����P�]aܓ�^������~=�_���usP�B���ˀ�pG��K�D��z�[?�T�Y �G�5��UxN;$���g�\�T���TP�[��鎼�Ǡ��L��_�$
��1 ��^-#ɬ?�ܒXJ�wHꚘn���vEx����t:M�
�ߥ�@B>�<0�hA�s/�3�t�9z�v����v��YP؏�b�,�P!-G���=�In�+~xpz�+�ǯ3�`V-<�5�״�g^+��.��L_�����mq��+�'vO�U3��r�i���S3{��i1��YO;��/V�Y��uZ]�����#a&�
R�m�Q��	�	kq��2���y��:�ő*0O_��u��g�z&Uhy����g��d�<<�;���U��H�:3�j�3��z����o���s!�WK��+Ł�KS��!��4?��W�$Z�o���孂*�L���w�˹�����u�L�^g˘��n�'D!��T�<1Z�%��C���F�7�A�N���Ҿsnv�����U�;�&A~b��"�� ��>WJU�Y�л|�	o{!A|�pH���
ի��C�YhWu��m���|�4#�M��3B�OaA��;^���(otO�˕qc���Y�S,�P�P
�ܣȤdzu��NAM��_�����ε�_��]+o��>oО���y�[��
�� �e��%Wd��ָ��槊�ޠ2���w��~��gX����.~
3T�Vp���Ϭ���,��G� =��.�3w%]�.���#x�ת�L���f[���c�I�
 �W/�]�C��g��7BE�5'?�]�g.����Z�?ë��\J�J�>Ќ~zš��!�6fxi���J�y�Q�H�K�/D�!�@��<i�.��@ɶ����zi���
HVry�_�|�Ꮮ?sw������E�o@.� ��#)�q���!��N���0��|�DT0E�*
p�\��C��L_"b�w�t7Qj��5���und;����@=F�fdk�0�׃�����=�l<:�)���FO��ݝq���U���R�{AN��B�a��;O��Dc�@U׀�:9ӋO
��L4�B�Udz(��ŗ@�T��+��7�����3��v�;<����p얋U/��?<�K�7k��i�����ӆ����-�4~�CiL�Ո��'�nx�9�g�4�<;~�p~a��\9�BizGO��N1�|���ߴ��b��cd�-��	7BR|	Hm	���(�(:�y�L�4�hH���<z_���]$[�1?j0�卄��Ӛ�z9W�1@=��IO:�H�1���jrPE|A��<RE��"�:����nr��/�&�Hx�SL)_@����6�b<Ft�]�x}q���'<�H=��:�Ru����o��.l������&v�Nr^��0�	��2U�_(�xհ귻|�G���=��N�؜)�E�\!�\.���v��|����<�A���Bg�@��M��}� ��D��(w9U�G�x��x�/f��C�Ȅr��gϼ������>cp�Vov�_,�3�6����3T�m��9�
o�'��[�YNF�!'4U+��_���Q���ȭ��$�R��q�����H~��o��������/@�s���W�\�]k-���P�$����<�Nt���N�����-8�nN<�"����p����֐�Cr�+mr�<'������l
)z�W��~5eFO�f57�Dq��=���>��<?=�BCn��k\�9��v����\�<��Y�s��M�o��6�l_T�Aɹz��A>)�r>M�&I�v-�?�Ok���|��������yxt7\��
���ବ8c�Nr������Y��t�m~a��X(}�ws���iV]a�(�(U�%�� k���&9s)�*ɇ��F>9d?O�l����A�Tr�d������A֋~=�����'~���_ڮ�/���r�O�n�O��т�ݤ"�/x�_�<�4���$����=�'zN/�#�&:l�w�${�d?�e��/�'c&Br˿ʷ�Rye�L_��~�v�����*�
�!g�d����C/^M������"��m|�%�n
��K>�|�����\���d�_ģ3�J>
_�ù�=�+�;����R?'�Ϟ��=<��w�����>��??�'�*D�;z|����~T��i%��.�^�3���g|�'�78��?�D�K�����vN���ѩ��<���rš�������=/7�gI���{����<���A��x����6;7���1z�᯽I~ON�k\~��<�59/�b�g��ğ�����2S�S<dϷb���B����9;�T�~��s�3xq�v62��p�^��u�<n�i�uH�\���`x�k炤�5��-ϴ�w��OΧ|��<Tx�N�#�_x5z6w��-��̮.�Y,��޲�>�	��u�s��2��Oqɇ����<�s|mM�B���q�9۟�$��ѳ~f�iM__���*��)?{�sk�[��?�ѳs�N������y�P��'I^�A���jkT���+F[�9)�y��<铞�u7����9������<����������h�W��ݰP�'�}-�q�@��J����y��NJ�G|}1n$�f�O��C�2�7���k��ݘ������>~�8��/�;n��cz׌�~(�@�?ְ���}�I��!Q��TI8��+�0Q�L8D�� �|�ՓO�G�Fռ����c*Qr�]W�4�C��~P�s�~狨W�堧z����0��1��O�����8�?{&,�9/'La��W��^Tڄur���+x<���Q^���wD{3-x�������b,	�_��V���b���'��4�3}�=ɐ�1�y-f���!�m�rw���Z4<O���~�=��3�w�E�5�_���*�+b����ـ:����5]���+�ʞ��ʕ�{V���ߗ�P(��)))�I_���盾������ԫ�w�{ZhD���	hvh9C5�¯�Dp@پ�ɧ��n(����*�C�jVr�z,��
�PZ,7+�<L,�:T�z�Et��b�u��_��N5�\	�z��?��n��4��L�����ٹ��xz!��WɎ��o��Pj�4z��w{��l�|����TY��=�\�;��%������ɞ�Ţ��=�e=���7��+_�U�U/8���(�D�VX,�/RR�kGه��2ىo�̪��j�n�[�H������'�/����Xي��vp�������H-��8P����ƥ��Nx�#	|9=���*u�RT��-��w���ӭ�=�eK�E����byx�'<��]��\.��,gc*��?i�{k��I�<x��'*�"�v�DC�Ԉ3	U�=��F%*��ьx��Bq	��Ty�O|݌g��$����Gɍ|>��;|�� }�K.p��aO�-��I埑hi���0kFwg
=���~�$}u�Q����fo�1�<��?<��õ[7W��rn���g�޷Ԡi�
)��w�f��|�������~��W����j�J_���!2z	?ei����p���
$
��B�iϐ=n聿�bh���C��G	y���o�]j��r
��
���
���^�_�Z�%m}�_�Y@��7���x��3�jAb��[��("IB
��=���>��0�$�$��n�
=zp9.�.�+����w0���|�A<�*_�A���/�Ɨ���Gߢ�NR�;�~��� 
��N�wQ��p?�s��#�@yT�Ia�0+NC����o�ӆ��fd�Ǹ�r/$Hx�\�$�w3���
�t���� �~,���y>��e"��;�JP���� ~�k���k��O:����=}���G\��f�p�>���0��=�D��Ȧ,@�tS<��<���*���v���G����;k�*����\���dF܏��+%熉���;#�!PZP5Q� b�H+��7z�[̔\@�v�w~Ƅ�&%UQ
�f���z�Y��Q�x����I�r�k��T�)b�}7<�t�<�{�{0���_$�$�T�uЋ�*�By&δ���\�S�s�͠�W���1~���)qJyض��<����H�����z��11U�r�Wޏ:m_�$~��)��VI�"@����pX�G	%��~	��
�*�Ja~�91����5�@7��o��$�������ȓ���G�!�������
��K�zi���O�ݡ!ȓ�*Pg_8�g�w�nxdJ�\�R~!���A'V���Pe��;g�3�P��l�(;O����ɿ�ng�q:J�?&�GO�$�S;�>�v�9��ŷ��mIĝ����y�}�c@��b�X��%��I�mk'�<8���d��04�.���pK�|�����kq�D�Ӿ��?��~����t2�O !7@O�u�0z��hF
�]EkK�ǫ�r��x+q\�י������H*����0{Fd��S����M>����#d�'<��G�L���!%��on"~�������1&�K�|tһ������SO�S:�5Ux�9����RU3pD��'�hz���~��'���˕i�ѣnU^�J�o�^r |�`���������Wr(�,ʗ�_���s����:��=�����ɇf�D�9�_tXNr�/����k�������$U�\R��%�W�;���~K���C�	a����Şg�����=��΅ʮ�+_. S)�k��Ʒ**��*��yƁj<�0����"���/G0<ͭ�w���γ��'~+��*�$���΋�D��O]���W��Ӻ��3�6�I��5?O��Y��H�='�-s&��<q��|�LΕٝ����Y�7�k�wǟz���G�!p��r&!�Q;J�/O����}����z�
�T:<9���*�/���3��M��&���7	��Ջ���`�w��JU�����AUm�z���;w�K7�{�U�h�a�xT��%H�~��P� ��I��M�9ͩ|�����P����Ԩ|9U���(_g�'!�<ל�HT�|��!��1��Gl���g�"��9z �`v��z� #��3y�����C��:<�@�*�6��3�BO�����a|�(^������gM�[��3n����0$�䣊�~�7�Q�ک/ȉ(?~$�g�y$�Piz�����C�^M��+?er� ��
8?U*��1X��JU
���b��k��Jt��S����X�J��/�����ʉ��_H�w�(�j�������{�4�ŏ�GOv"h�����w���_�(��/q�<�;�{��c�u��^� Qq�l�!���ɡ�gg�o9�3<����I������;�{w������g|e�{�K���_���f�'��ϓ��l|�ϻ��~�r�c${@�y��)����t��9Fj���W�r�jz�,*��O.�Ox�o�ş�->O��k�YՏx���;����|�j~����~.�3�F��5;=���>	��2}���=��|z;�4�عw�^y@fr�W�O�;b-�Dp�<幸1<_�L>��'{��iϛ����Z%��r2@��8<�]ye `
��r��=O���
ϣ�gN�9�<�?��
X�Z��ϋ��}����5}��E������B�3p^�yWj�Z�݅#�Z���<����!����A����FL/��W��=����=�M���'��,�K�2lA?b�=����/��P^o�*��@D���rq:ox��Z��ݚ�sk�\<9O_�M���0:�6G�Nk��=u>�o�xk��y� �T���/x����O?�?��gA�z;O���D�{���j����A�Gn8��'=f��+}�;\�)�+��;b��e�A�;��9�@�t�z�=-����ܚ=��Y�sj;�dr#�ۜ�����?�'���i)?;������e)<���b=�P.T~Q�*�=�%̣�~�e���
��ʇh���`�J�����؞���|�g_q���޴��_�I�Q5��B��d|��kw�+��8�W������������'��m}^X�)���r�:�<�Q�'{&_�yw���ϳ��^jG:.e\�M�}��=��J�����������BIp0w���]{�w���':�C2�_pH����T���(�߭�'�1UQ��ćs����ə1��)7A <c����E�g�������W�z�_�6���声߉��V{��t�J|��Z;U��j�߼Vx�?<��%>�1��,>0��.�p�)=��0q�8O5�v�Q��QR'̂��3�$�(�'[�X|~lOO<d�^T��B��l���!��#�<��~��Q�ŗ�o��.f�#^}�L�Οw��.�*���q_>�(^�?$��߹)Rg�3��
�|_|�}%�
px�_�>���N�������l�e�E�ɇ�,�L�_�s�N���S���2zX;D�� Y#$�_�w�O	<R�%�[Y�ukU!����|(�O��wr6-��ا�~��<����z?������_���D��G���
#Ѱܑ+�������N��[Ro��D�%n�x�O�,~�N>N��gxj��VѼ��E�A��~�2"ַ=ݩ��<�����γ#g�߾GO�E>J�)����z@zA�@=/�3�<W�#J�n���	�����,��TNd/�v�E�E��.E�(�o������Z��0v��W�…	"�LD�X
�6�,���;"P�,���s�����gSٛ��ɶv�9�[��Q�?g����ys���-<Z1�{�{��=�����x��u	�����Y�<�c}��Yc�K��;-�����ó>��Q�V�=�܍���'ʳ?��=�gx��7���s�G����ƨ�)<������Z�yo4bι��F'������[y�s���WE���=�/��z_yh��5=�=�K��9�k�ln�3<�o���t|EFZ؜f�Іs.��gt^�5}=�!>��O!���e�,���d���"���g?��l�.�6w�}~�&�~�f�%���:�:8T�9>�ݗ9����g���6����G}�=�����&��<�C�b�w�-��I�Y��8{|xf��/�5zƚ'z&�={67z���mz�k������s�sz�	ɭ�u�����Z*ds��[��S������G�b�W���/ҳ86�G���F��g����Spx.��=\���I��Q`z���3�_n\�u��e.a+_:T�0x�Ǘ�Sw	m�V��k�Ih�}�Ï��}��{5}
O�j��艤����~]���⌟_����>��6Unm{5z��%�t�)��9.�;̓���U��Fv����x����Ɏ�*��'��^l�Y�^���yk3;�r��1�ٯ=�şs-sr��N�������KO��S����/��ε���~Ɠ����w��/����/�ex�v���D��k�:yN#���O��>��Ct��?xv��M��q�9��⧞{���??ɟ���#��̬εjo�Y܈��$�=��i��-�y�7H�'���/�>�=��陾�g����?���ō���^������ߝ����[��S�;`��~�|�g���=����A��4�+??u��y�?��	_���ȿ.���'���
ɧ���x���{*���ʟ�g����S۝����D����X^\E�����Q�k���kqf��?'J_�s�h�ݥ�Z|����s��r�<�^<|���y~��_�E>�3�X<��������z__l��.���x{*}�W����:��T�.���߻��E>��+����9.�b�p��s�ܗ�ـ�s�?��ߓs�<��OO�Y{��#_�g�z���b�~�����~��k�y�ן_\��ƀ�i���E�%�姳���|m���;��t�/xq{��B����kq�o���q\x�y�<_�]���猇_����7�s�'y>�_��-�����9L�z�s��1o��s��/Zz��.�JM�|�9>\�>��{B�Þ��O�𑞋�?���u�����2ߵ�~֫I���[�l<�X��˸;:/��mv�~�T��\��N����g�tq񋿞���#_����<�c��x��_��˼�r[�8�tɉ���G��ӗ�_����2���u���ÿ������q>���9��y��_�v���'=�gx�"n�����<��d��y��ź�u��i��a�U��|��z��ui_N���a�o����z��S��z���
�\�����pV~.Y�{�sڏ�=?�봟��
d��0����=��#�=���;�����q}�g,/�~�:�'z���O�jC�u�3�w�_˥8マ�G��� gHȉ"T���>�_�gxbax@zu����$��ڵ�>�I��3��0<�_U�c��7�w��'�>����(=!���e���υ��+�L>��b�r�`�F�)�s��3y"|~_�1�ʏ���\i�
x�+�0T'������z�#��_Λ0���S�]��<�L�M���*�|�l��rFU�ι��L8Ѐ{������5�;r�)��v>M�A�)��l�B�
�lpY{wx9Iw�JSx��@����!�rk�+�K�r��<G
=K�-��_�|#�
-7����b�|HF�%��+-�뗯�%k� V�g�_8��v-���?��+�:��V]���s�^�󳾣�;B����=����ym?R5��
�g�Vl�tT�50��+Oq�"��F&����5K�6��ٷ�(^�Ia�^�!?�gF�Z玴��V���(�H>���g^�Eg呟|~˓E'"em�@�P�\dp��3�@�ҕ�~���$��hMP /܁Ve���jx,��c���g��snG,��}� <���2����{p����~��s��cѺ�y��_~�I!x�%��b�Ox�)�/�����o��Ɩ�=tNF���B��T�J>��x�O�;�K0x�Rrs煌*�
���~P'h��_�g�w{�P�L>����������ʋ	X�,,�_�y��=���j�Ɂ_��R>�O��gpx�۴���3z�C�r��$��"jᑹ�>e�k�K"�=�8:����/��9:��_~�Wc	�
;7<�j?M���XPi�(O��x7��z��2�^�T�3��g�_�o�<<�i��Zԋ�޽y�^�/#��ѣ<��wb��+<�r�q���)D쐜�!;?�P������D|B>�߾�W�sM7�������'xk��g���=�0�K�o�ٜ ��ڄ�ѓ��?xt"����α�����W�n��0�s�ē�I�W
���y?Zvm��x��֗�Z�pqr|�Gy�.>W/�b'�?�x&��*�L��K��#��O?���o�{�YN��I�ř�@����^�v�}��z��~�����O}='�[�	�b�[|������w����:߽}�n�]�Ϟ۷��O{V/�M��O��<�x=ك/�����\Rz��c*�&��N_����P��k�go���wTxГ��1}��3w�\kW��i����s��!�6���W�?:�'�݊�쟟�穯��i�B��W�[�_�ir��^e��7����V�2�G{����0�@���{'�z��Mv��E�?�{ߑ����i�d�3Ov^=3[Kn(<���1�9w��>��S���?���,ܫ|�k����5��/r���a����O��y~�Y�\�N9�<�)��~?70}5_Ђ�G��!Ϯ�s�e�6w��k��sr����IT����w��?5S�� �O/����G_�?=���/�~0J�=h-?�˼{��m�v���j�c(���;�6�����=��A�n�z߼~�
=^������W����߲��^�m���sW�[pu&��=١z'���ڦ��t��S{�i/^�)���E/�-��gSy���7vȞGOk�Փ���S�!��˅��!����`�q��]���k�.~E&��X@��2�T�>H9%�m���/�}��|6���������R܎�W/��:�L_�����C_��!����+xL��\�s
�����(�FN{���~�s��yx���g���w�h�1pƟ�~ڏ'�w~�.
T>��?z�7;Tu��I�䟿w�TÃ���#�O>�i�C_�s�����
�.������z*_\�}v>%z���I>����?��������|��:�ӗ��ō�3�?�N���S����~�	�ٳ'{|}�';Ql	��0��^��Pec���q˜�j����x�}�"�ƒg�6�g�x�}�kw�Wot�]V~�������ۋ��^{�\̭]e?�.ma��Ä��L�Ƥ��:�e��}�#����Kn˅�/�{�s�v��q3n
�Tr��OH[Ø<�gn�2�_��J߿���3�o���4<�y�[��3?����Ó�2�<�U)��r.[û����f����~f����Or���ч����쟧_,G�JWor���_�ף|��Qx�9������H��}|��pɽ[���Q>��e�x��)��k�*v�0�0���?�p�j��s#@��/�$?�d��O���'~g$��s����x���Y�W��E���V�~eɖ|��oq�������_��`�$c5f5&'<���.D5�:���9�ӻRz<8|����*�p�Yw=�@٧���z���}~���zq�w>�	��;zY�̯���]W�'��{�{;M�Gq�|�#�:��!TPr�\W�^���+DG��3q8G�������=#Q�,�3z7���V����^�}�	��~�=>tڢ�s�ɟ}�g_O�|P����kk�A��Oخ83��r�ө�脼��K�v��D����Χ�z���n�Q@7��
�31J�Dǧ�!�+?�d��i��vZt�q���{�&��s�����s%G~��'y�
�{�9����5$�}���l<�rD��������'��?��pH/�B�?�E��R�(+��[i|�w��.����;�<f����쎧�l�̂��8w��!�Z��J��x���&���f� 4T�b���=�X��D�ް}B(y&�j0i|�r����xf�Nz�s��׎��7r;7l��;����s����i;��F^J/e�χ�ѯG�w�G8O|E���ݚPB�<��v�?�2̋�_u$���巪Vd?_з��?��i�g��t��['����VO�^�b���v�Q�s�*����K���IB��~�[�$�N��wޙt~�>��#�s��ɃO�T���<t�sȨ]�sk��nU�dc�S�A�x��/b��?q����j|�w*�w��p�_h|��">�_FiBw+l�,5W��98�J��q_%O�Q��^y��Lq�K�D'a]|z�g��E�����^�>�}6�=C����7WǼ5����Q�V������m�=�>���eI~?Y8�_9���>�6vs�Ř;��O��'�Q�W��\����
Km�p�\DǨ8���&�y���ʧW�ˊV莮>��8��%O��mk�u�u��Gy+'8D`5txv�A��%��9.JFs�
�����}:�q}u����Z"�B�_K�@�5��];E��;���x�+J�685�
>�j��Q+���ә�+RZ75�����Uڧ�9�+_��t�H΃��>k2�^�גڿ�ŇN��ߊ�S��U|�[��bLb�g���Eh��^��듐d�*4�p'�T�L����x�g!�<O�6�W��ؗ�6��ܚ���)��?���{�ȧW�pSX�n9oFN��ꫯ��k?�1�v�2�p�쇺D]��/~I�_?�UNM>�%Ä��EZ��gw$� �9�J����0,t'����M�>9�L�RXc�!gB�LJ�R]�����s�y��5�S��]r�_�z�r}X19�~�?���G����a9�T�6G_2w�x͊A������h��sy�+��z8|��?�Y��so�{��zT����a��@ �<�8���qO�FQ�?��,f���C3�x��OC�ã��>c��v.�#�\�YFon4m�,���ϻ�%/�d���&vG��^@�$�/����3�hc��*�ǥ/���{��-@���n���S�=f�g9.�u���~zM���ə<���y߭�LJ?�<�Wo᣼l��~���#�u�iۢ��?���� �U�����I��Y�W�'ʓ󼶓O<��O��y��9ə��������I���U}?�����$��=ת���^ޏ^	L*G�9�?L��g�t�G�|y�����<g`���Cw<�X!�z�x�1�v[3�ß���,������'�cy�=���V|�������0��>X"�áYXK�^Iē�F����h�ę������|����=��ix��u�]�(��c(B��Í(ɏ�p�	���k�j'�$���+�����>��w*�N%�6Ы��2�v����r�A�y�&��z~B����>:鮜�g����7877Pxp�D\*�[��{����gS�m�9<��Wk|��?"%�a����1ޥB���<�?$�����'�����&��Y���.�8�����8���A�ů��7�'ᣊ��g�XNL�C��M�#Z����?p>�
@~��O�(��{��^�9���˟�'�ș<����k3��v��7<{�r����;
};�{��4@�M�5FP'y�P<���o��٨�;6|���s|@���?���
�U}ʊ{�䟜jpN��$��>�i,	y�j;>��!S��z�b�N4�����w?ĈH��_�5��#O��#��P8{U8��v㬍����5q>��'{�����q�Rz�B1��D>�3ʉ)Wq@'Mh�o�������c�����y�ו�9v�g\A���*����v�v�8#�y3s�Fx�g�b'��MFW\����saZa��
�a������{,��ūU��֍�矇^X�������q�D�c�!�_{! V�r���?������
����<��>���ÆM�_8k_�ϣ�9��>w���������}��R�ϬcX��?���J�yP�?|�v��s�E���(|5z���񟏟�	���pr�b5��&��>�Q�����)�/>L�<p`��2���4y���_|�� {��v�^������*\��3��F�GPx�9����W��LN�������m<c�G�����܎�0>C��s�
��n�걻N,?l���@���T�>�H�|���
��?�Ç���$�b�$j��.���_S������g/��i�<���%>����v�}�(?��C��i+�����C�s��#��I���{|n�u�>9�Ȋ��Y#{==�W�zL~| ���q��?�Ô�/�������w�q��~l�P�h���^�\��<���xq���9뇳T����r��^�'�>yu��'{�s�����3���/#O�3ŧg���/���F�R9p���
�Ϡ��k�_yM������=��+�z<?�z���4���֝��qf=��Ɵ���^X�A��gύ�IP�'{�B6ʋ������ݳW���&�����I��5o�)�?�<_�q��e�J¾�����jrƭ9�kyo���������{_�{_��ϯ����������]��ӑ�o���?�U�5��׮W��ݓ�T�r���Z�W8��<��e�N���^�������W&7��2����#���y�]�?��W�3���go����+��u�]v������m�|���Ȩ���w��٩�)��U���=����I˟'a��2�=�ƙ*�i��L�l�,a�i/��X^���]Q���<|�k��^yɌ�|�o�Y�'*f/�<�'	�n��У��8}��<��Gս��҉0}�<:�g�d���i�O|%��W�ѕW��� g{l�.���U�^�cA�m��"����|U{��iG~L��ֵ�hT� 'z�n�����]$r��g�"O��y�3�7{�Y��W�uX�Tsw(k�ʫ"|���'>�+y����DSQ������.���e���%O|�7�ȓ_	����~��MN|��,���n�^]�|��pWp �n��W�3���ɟY=[_y�͟A��)�^�G�9�٢|9+7'q:��Kh"q�[\㣤pܩ�e҆���
���9��l���g�.*إ��/|���x��C��U3��U^Ǯ}�g�R�)�!�õ�
��U��A��wW?�y��?�n��YП8�p��� �����T�rUM��^\�\(|�/|�/Z��n�'
?�{�5E'|q�_1����g�e�C�˓�A6|������U�脱���H��K��A/�fr�IȎ�<�R�I��ݑgA��_��J'a6a�>��9�x�.4��>(n�0p���L��T5Q���&�?L@�����ʞ���%n�g���<��:�99���Ε�^����ÿ�@Yn�����G��o�o��#8D�s�����c%$����>��F�?;��*���[�`v���K�ʕ�?����ثz�
��Y���c�������d��g�|�sڝ<W�K���^��՚�����,���ݩd�0>�^�N~���᣼����6�+���~Ry�͠ן��z?Ծޤ��)z���iGw=������a�s)�ex*��[>YG�H��!!_�(⃹���s84/8��Y��t��=>��;�noW���'���ܟ㺟rYl�K�?>L\z7�yQ�N�pn�G���)[�`�px�Q(ƙ
���z!_*��tmk>C�|o�a��8 ���M7�*oA���v�0�Ix�0	?O/��Vog��nNՖ+�\Ϙ�����|�y0�D:�c�w�=a���4�+��* Oz��;����Ov��jW��:�J-���𑧃��8���vY�Щ�9�Z^$T8���B�s,p���őY����n�.�C]k��#<�(w��~O]��-SB�ཱུ��
���<�M�y� y(��������^�A37d>�w��<B�oPii����Nl9�'��sMY����)��S�y�A��� e�N�7�^�hD�ɫ5l����6��ej�z�#����%�L����B�y���Jo4B/o�	�LP�!�@����^�k/Є
��ҩh���8?�s>�6�g�A7�B��	�ĄC1.����s>#�����^s	`3����c�KS80��b����1$��p�3���G
hk��A��삤��[�	}|>|���gv'�7�Q�Iiw��+�X4+ꊁ�M�$ޏ���iPJV<�K��U��(�̕<�j@"���{Br8�w��4�s]t���n�.��S������_K�����Xo�r�:_�}�LJ��k��2���™Q�x��^\�H��B����O����Xa��\��}�2����V�^�z_�sY�<?��*H���
�Z+�.�ӱ��?�yE�™<��.�=v>��p����sY�X��[��Cw�����='.���0��KC�X��'�f5M'��C���|K�s*�O��J+��Wʗ�BxR�Y�=0�^�_>{k@��\�	F/�ɾ���u���y�s����la ����/�<�K|��1����s�.|���>䉏�����s6i�h����с�6g�>�����~"`��0��\�̬��9
�[�0g�3:e/!��!���c��%�:���3�G��il�J��"�`m�����8�O{���'��	(?���z���f�M�ѱ?T>yZ�	��wDo��ȯ��C5���z��x�]y���2�p)�<k�G��{/_-����m�;k��ÄH���W��Y�u��?٫�ݚi��łmk4��
g��#}�Go��{H��ޡȪ$Ӡh��p'�I�9�꜡��i����:�s���=�PyD�.�V�a�q�'<�����3����?�ә��(6{���~�k\� ��v�d�އ����ۍ�-���s��~��೿�S��1QyO9[�*U>yҫz�3(C���ˣ)��g�ť���'Ok�ѓ���@{�ݥ���U�	Z���m��u������f;���]�����d+�O�Hܘ�ǟ���~k�N�3}Sy����g^�/W>��[��a
��鉮��rК��fH�x���r�Ƀ�t��G\H���W~�?��I�
�Ȗ���>�ݞ���0{��1���>A�.��?������懽�n�G�/��ƣ���:��p>ʇ�z[!�F��y���k(�3w<5�R*������@������CJQ��J�,�����/�J��t������t�P����v�}j�s"������/�t��#{凚W縨� ��S��E�"{W�m
��ʳo��H���Y�Wa?�ܔ���Ƴ��3���+�?:;B�5}�0�9ٕ�����"�۔��s��X::���^����-%�$��?���{�Ey�t5��v�8�^�Ce��6��oQ
>�<����"���v�ȉ��ϗ/������F�� &��w��^�?|��O�
���C~�}�����FF�]�xUy�����j���\1�
(?���*����7�+���'��0|��4���?>�G���d�{i,���ş�gt�.���;�G��U�	�+�ŧv�Ao�3w��镲
u����y��xrq��Q�OU[�s���rG7��|��e��`+M(mZ�p��8��N]L�ڻ�s�:Ay�gĉ���&U3��'��/������3ǡ�W��>�ͼ������G�>3��;P&�t�#�s��_�Mi��O��3������M�ip{qX_��x��[�K(�(��}6��+y��c
�f���٤'�|g���9�#�c��o�'�s��xl��yZ���g|F��4�\1���r?�����}������j�\���z�g�|�5É�3ϩ6|>��S姝&d�
>���kx��3��@s^Q_=��H:_3���b�)'���sȹ�]8�j���3��z^0�{�e�/�w�&���v1�$5ê ̋�;+o��<z�7;&O�p���K~�ў,d{��k���>ww��_>-��i/z��=n�!
�����Aq�C��?�߹��)i��o��o_�f�g�I/���?M����<�i�)�L�88�+�<�΅�\��,},Ԡg�0����KΟ����凟�;X��R�k��ݵZN�!�q��ϣ�5�y\�߹�sH��Xw�ӧ��0��������X�C��L�8ˁ�;.�-t�����s�p�:��0茕�;�"f���!'`,��,��]��g��n�}�>vl/?�$�-�M;���y�rgS~��`���k~��1,w��;������$�!�&����} Y����OG{<�eGp�����Ѿ�v���?�2bP�~�t0��Q�|t�=3>�o-���X��X�;Ϫ&�"��^���G���՛��c��kg�s��:ʗ�3�}�����9��VpS���^��m�q~�)�0L<������J�p.����W\�*
q�s.ZQ��q�-��U�C.=�%�.j���l��}?q�D���0�����s'c1���K�Z0�j��C9a��X�D��C!��`K�9w0�2�G�,'~�)
}r���ur�3v?�-05IqUnV�\|����AO�������?br��§���Ik���x�v/W��K�t.ZNj�L�Rؾ&>8S${���S�����q�����i,NR
stK�s�Q�m�s������?'m�h䟻��/���;�~�Y;��\%t.Q{�>>������?G�u.yK� }'��F�u]���Civ��.�O��|�<|�)q�һ+�b6��.�.���Ҋ��oF�S�Ο��9+7����eC�{���},�O�5kYF�G;��~�Y��M�b��AT�)?Y��+�<ă�[�8��g�7"|�����_(�?>3.�.�q�q&*%e��`�ޫw���@|�d�(�k�c�끸��������������2���59Ώ�;��D���?e��p�9�uMް�Q$'?*������\���e\l
����ڪ� _�Ps��*o����DB7DE�2
�'�8��p���s�&�D0�p蘣���"�d��qI�T#�2�T,�7�	g{ps�6�W�D�5[�=���991��S#P#��6��VԨ��r&�V�#���.�A*\�8��O��4����]:���a�sg�O?K�ia����
sa7c��8Ň�_��zz�VH���|t���R�~|'�:�ڑ6�ea���o�ƅ��o��<,d-[�����ç�5����(���S��_�[�.6� �܇����B3������A�{��Lton�sV���GxҞo�m�	>+����0��V��� }	�=t��cJ����d/�Gl�h�݅Ԙ>�ZJ�J/s��œ'��^���>����Ƚxs���Gފ�&a(<
sq��:}��c�d
�W\-�u]髷5�ᖟ>|�ggN�g�W�ﵺ3�
��Gs�w�9幽��q���{^�9����٘ ��>f��|ٜ|��q�О��ߜs�)?���^�RL����_�WSH��5/]�v!������Yʝw���y5�Q�g��g�7��U��Ah�09�j��]*�%���E��
��"�g�o��D�<��|��'>v�gkC��/z�~�Gy����Q(�s�Lo{�_���n
�O�
�>~��غV���oM�Y��T>����7>�*M^$�ӋU���Rl�
�xw�9ޏ/)R~�+/�Ţ]va}"�6���(/�?��+!|�[�w�$U��w�g�.��͂����6y&η��<�T7W,�~���7Ǩ���]��b��9�Yv>�9z��m�`O���k�i�K�/�Zݡ0����Z�!���m�g#�Y��3��H��iͫ;Kܜ?���PJ�Aq�jk��������ΧE�/:�eew�e�O/�i�I�Zw����ZRzUr�?ű�9��`�(���䐉����28��vy�����?>����k|���Oy����!O�<��������W��''rK��N�.��U��p���m*Ƨ��g�� |�ނ���?�W{���
cqU�����Qs��W`���cؚ�y���r�u�s�����<뭸�52���K����3T�O��_��x����W��&Or>����o�"�����t�Z_����mXZ|�Y��"�b�܂���UIC��Q����١����~��:�����Ӱ[yU����i����[���×��ՙ��y�ʡ��պ�4��ʗ����=TC�3|Σ��|�\�k->�f&�U�#���vq���⭙���ƺ�i��L�����|̼+c�/�^�eZ<>+�ԗ��|����cr����+|�G�Y��'������{D��<��_���e~ؙ�\��,^�o�i���W�T���ڞܗ?��޽k�KGP{����y��?jON� |��,���?������h&�������O{���Ka�S?"�a�c��s�i���n�i�.l��r��Uq�����/³z+H.Ⱥ��bt�	���O���}iGs�@�/?|gR?��L4�`�L��魦�4�4HNQ>�5�|u�{\-�~���#��p&;�6P��_����»�c�<�[��=���cw�	��s3��� �i
��\�5ι����o_�7����W����>�Ƹ��������>���J%��2*U`r
���a����e�?W�B���{�[S�A��΍�Vg��C��z�X��S�䏞�蕽�[�?�Y!9I�s�F���u�<�s�-��''c�*�K�8��ͧ���m����_�~f�}���a?�arX�?z�/4^�Q>I8���D>3���-ř�����]gw8�v><�1��s���7/^�����N�N���.퉲K8O���]+kU�Z�b���3��w�b/���(>�c��U�Wܐ�<ݥ[��W�[Fj�í�bƓ�8���
�k����������8͉5����O|�g�3yZ�L/�&�GGD��Q|��Z��mH�->:w���w��L���Gy���<���
>�ԯ~�|����b>ڗ���et��|�������5�}Ԏf�)�>���3�0��!�k_�7쌞}ѕ�'L7H~+����a�o��q�c���?��}��`���8"��T {�
*�!�$���+��z>��7-h���Ko��>�w
�g��ӎ�v
��|;���sY���o��j��EݩjlS� O�P��UX��޺Ygt_1�S��ƫf��>�q�q�i��}�/�}ŧvꏙ�7/0Z6�����������}��1����?[�u�a|ְ�mv��W�7O1�Z�'�N'����]�%�y�m�T�2y�m�~��lZ���c|9�j�xfw!����N��y�3��O�C�a��,+U�]��q�z�a�~y�g�e><�m�d�K�h�d�z���O�v}p����w�9_��*��cf��'g���|ēs��W�'Ǵy����
�fu����.�q�qr����3wfM?��͋''&z��i����{2���b]�59��w֑�y��U����m���*!�;P�H]��Ē2ً�λ�R�3�tp�yʾ~�]f�9}�i���&�a�b��YO��_�^~[<%��ah(?�u��n��.�9�f��F�����>lz���r�F~�g�G4{�#�k�5�44�k�4��L��n��T����ki胏v�v���:�u�����������'����չCJ���Q�fx}��?ǻ��<<��$WߠC�����e��1�����o�O�|ȓ����*�-?a)���:π�Z��Ci���U�<��Լ�^��#Σ/y�C���T;ד���n���>8��;�����P{l=��MBsP��"�ox.y.>�m�ԣ��&�־>�v�'R$����>��+l�^*���KY8��R�]���2|�g�ˋ%�����)t>Ug����e��[�!9��9��8��e�B�����rK�c����b�,>��'�R�$<`�|	
#����������&�ç���E���o��9�D�����ag�>�ҫ�_J��s�Qx�cK;$�k\���]7�
xk����r�7����F�h����}�l�{��snh��Ƈ�����k��,�G/MJ�4w?��no�s��.���'�+X��\���~���Wϙ�"�]���3�zqnhvl<0w�z�g���N�}����}�3z�픧T���ӷ�yY����A�}M��'Ϝ#�a"S�Ar��r�11/�1D���Q#zG��y�|��>��tH0�w����I���'Ok��og^ru�}J�R
W�?s'yx�Ʌ
�[�c&y��j۟��#G�}/����x,�i�����D��ǿ���<Ȑ@�7�X�Cfw�1����j�'䑳���~�B�v��֊��,yT΢�<R>c���#���ҞKwQ����ڮX�皁���?��v�r��%�Y���5�z]�$������N3��}o��k�*�3��|�O��X�3ߘ�'w����_��sٷ�����N�y��sS��|���o���~�ʃ&��̩/�?�0I7��t=U��;�P1�1��H�2�8��:CF޽�e��~�opv�"��SP6&`��j.�7FfY|���h9:X�1��ȎS��R��/|j�r��D#�I�-�6vd 3T+�O�mg����n>(]����x<}��q�N�@���i�$��'-*�Ǜu�
0yR�,{�4��o,�\:���G}�5��<k�5�B��Pv���Β�xv*��[�{G��@�_�m�������#|�/懬�k�5o��]�Ew�`�<�㣙�J��-�;�j�Ni�3�����vL/���%�'�3P��oU�z�Ô���Ϻ��E���}	g	x�8�C��(KUsx�˿�@'L�
��?rzX��y��Ћ�w���^@kt"�Ә�b">|��{���Okd��$�����%�>��s�u�et`L��������~ij�-�����D>ܓ�Π�b�S������8��0y���b��}�s�$B'R���c�+I�,�p�wpf�F�!|���is{K����Ձ�
����^�~��5���$��?�^�+y20�ol���(��"{����OV0�m�ss<LU��.�W�#%��p�([�����i��u�*�-�雳��+r���m�v�d��|��Y��(�_��V�E��N���H���,�����_��4���@I�wFz�{�ג%�e��@�o��U�̙�	�O�=�����]W��5�+z�����3c�^���^���4�w��!��L~BΑ'?d_�[{gw�O���g����O����^��y�~�V�Zm.�L/~~�ö':|���q�g�[�(�i���}n�^����<�l��^�$�X0����x�^��������O~�y���^���k%����W�S{�\���y-�^��53��oaޝP;>J�pk���Y���/�r�3|��dJ?��
�����83��)>�����W<�L����]����k�ÿ�#��d䡤�?G�ㆸ:�z�ß�k���^p`��9�����˙5nL~E�NG��Es��v�yn8?�.�7��7�1���:��ğ��̺gO�5����V�Z���4�k��S�
w!?�&&��y��).Y5G�l%�����G{aw|��C�ܧ�'n��g��/�D�!|��y���{�yLҋ���1�.W�0*&ow�8h�]wr����fC�S�������k�^�y�i��H:�V��8���L�.7t��Sc�ΉQݐ8gx�W禎?K�r���L��c&���lG�
B����'��r��q0QTɊ�??}��Gz<�p�G����+os�8�o�e�CW^�u��>N����.��L/>oȏnL�X����ݩ\�OzY�D���gpf:vg�����{h�{��~�Z�uP�p�l�k���1$�:'f�p0FZz�{�y�����@��G�]�������9'n���t�s�f9�F岬v����N3>f$����.��!��n��7���׏$O�c恿�+�^�&����Ё���CN8T�7��ر�K���6�����=y�r}�����Yk5z�3|vy���>��wl͸Ŵ��1�=�?�񱐥�t�����	RZ����u?#O|�����A�	���|XӴ\��df�'Oz%O炔�&��=?d/3</����'y��#=Ž��[�E�8��^:œc�+���n/vY�z/"����~�^��S��^��1�����}��k�v?��`v�B�Oi��.*��?��ɟ�G��?q>y����܈�ć�9��
��z�'Sf���^�k_�I��</��A��m�t�W�9���m?������k�?T	���'��-:VW����g֦��k_�!�t��W`��/��8߆�-&t��o�e���ݻ
�A*I�z���������g���j\��3y�C|�v!�'�v���g���l=��3�� �[>�����ټ��x�y�P���k�_�uס�v�vn\�3G�������s|�HS{w�M�=5��oʳsG���&>Οy ��]�f���W�R߈�&Єϴ�����%�3��Z9s�y�J~���#��Г'�)�?���2f�(t��f�c}<�/��ӹ�@.iq#�ˍ60�����U57�yz�g,������J�����>����hG�#>䱫�B�۞�=BIy����G,�O�?�"�/����;�?,D��Z��f���@���<�p�ًE��-��_��״����%�}rt���>Α	��0�Y|���7��!|��ծ���+y��?�G��H'�Z��^i�ʧ�k�s����#P��+y,=�!�P������wl���O�����!|F��/m���[~e�1�b��Gz+�����[p�^f��C�}�^7E�q��;������>�ڻi�z
8朶�S�˂5	+�.�<ʛh�?DO/S����p����-��*:Ϧ�q����&gY3� �OL`tw	����Z��[
KR����lg�=�Ϲ��'����cA�w�	��)�^�:���AЧ�1K�[X����Q��#>�~ն��sG+Y<�Dl��u���H����I`_^��n�5�3���H��k��z8����?�U�R�@1�j��m�ɕh:���ok��u����:*���������M���xK�ٷ*'Ν���.�~��8?|򫃸����j�=�h��DBg��/t��,V���ı����vO��0������+~R�9c��"��^�վh�����1�)X�ܯg���S<̯���ɣ}�_`��_����"�9�A�W�Ž�T�~|��s��s��.�S~���Ï������h�WpS�����$Ը�R�<��ޝ�
�[�����+�76E6p'��9,T���Iw�ħa�5�G}�o湘���p�Ƿ�G��0��E���c8�S���Y^�}�N��/�/,�Y[�N��@*�?9���􅏋 �
��,>Ѓ�c|>]�cMr�#j߼5�q.�mc�,EH�)r*?�>>�sB�f|<��Ήv?��rΒ�[%��k�k�I��Òɹ�iT�a�k�]{���6���6ꖋ�Z���cd�ظ�4ǣ�e��W��C/����x���W���Bp���8��>vy��7�c���sBD'':�)n����-��czh�E��"W�X�>�Dyz�9P,y���L
ã���U^�A,.��s�sЁ���X���m��=�C���<Q:
H'5��v�O9��o�l��
��ąQ��|:�$(g���ւn�ܚく����F
�p��F�Wɳ�;�hkY>���9��'�}4�ߥ�(N�uƃ�>t8��#��>�jGd�u#�>�'�_��L$���|.��_$�^'J�0�J,���}D�Ǝ�2���E(o�`P%�����v�ͣ�*����i����{?��G�4P��V�O	4~RN��}�p�ί�;_��.�M#�;7�Z8�Y��*O��ڣ�B}���%���<��髌���;��Ƌ�H���D���}g���r�mcћ<��@��Kl������NM���$<B�u�N�����=o��O��8��|�}��D��#�89���������t3#����#�����ظ�>����G�e���~�:Rh�5����@�������jm�j`��\�SbM�!���y->��v0���C��7�F�#{'+^�����<?)�K��C2��oU�j��aj�_Y��B����?ƈ�DžZ�PhZ�Ħ��b�/��X">Rv�[�����X���o�!���}+	K�B�T�_k���9�X�
n�V<���Ǝ�V�g�p���aٺg�kᣑ�F��w�?�ۑlj*ɩ}�WY���On>�,5F��w|�Í<*�lə<ڗ�=�^�"Б�7�C���]
����k�H�Ag�D�k��^�p`/�:Ţ��\���m8u%y{���3���镜G{?�a/���K7�i�%��"56M>&�'��=7F7/�>�>91�i������\QDQ���N�;�AJ��}+��k��M�q�d�y��&�.Ƀ�s�F�2�;K���S�}�s���g� x��;���^�U������N����	�Y���e�I)|�#��S��>��MJ�9|$X4��
έL�g/v��^��?��j�&���:y���^�x����	��ʾ�����W.����^k��F�������Qo�¿SI;�q�i������)g��+��E�=��3铇��<����䇼��ïȟ������;t8d��W�W�[{���<�!O�,<oz3�`��G��#�a%��^��ڣ���q��������pN/�^��x�1{�>{�(��v���k�;y�zO~�����|�{�XF�皦W��R�b8'�l2�j��[��~?H���^�ȓ�,��^�i��[���2�k��u�Gyt��ḹ��ۍL�o��}>�A�}��(�����c�s��Ny�����s�
w��wo��sS��^Ww?<��=�*w�?��X�����'�G���^c\�^��;<��ss��V����p{��.��l�59����GyS��Q����\��2fxmؠ�������To�l�M���~���:��{?������#+o���ǝPW���ߧ��T��-��~�?1Yf��e������>|��Z!y�y�k�����y>�;򰯊�K�>�͎��n�yn��p��哇}�>���s��O��]�#M�^��e������tOvi��O٫���שׂD�.����O�刜q��^�;������Z��F����z���u����.=�<m
��-�ݝv�{k9�Nk�s3�)�n�<�K�dö��л�C�7��m����ש��M?��?�+��|�=N�Oj�O���% *>~��(�SuJm�SD�?>ɓ^zB��w�=��q��u��t��3zŇ�����2U�(?�4��?|�q������C���Cp���{|ȟZ4�[v�g�^\5m��{�c�F��)�t0��������i���ii|$$�!y|C�S�'�e_7��_<4~D�=l+
��\�^7�.'���p>�D��W��'�Z��?4��]dwK�_����e�^���>[�:���ٟo��A�Z��^�����.8~�v�ë7����� ;N�{�s��3?)>�_;�w�W|�ck8{��|l��.:�r����<�W|����S���^��g�TU��7Г��d>�mk����z{~���ϋv���y�}�3�\��g����`���_�s��v�<����G�pΟ'n@T.�ŧ�:���i�/��p��$�C�E����5�\����<����ƫ�鶜p������އ���w�q����'7e��s����8���}�)g�[��+q�W��Sn�Ps�;¤x���������a�rs�]���>~B��ç��r������M�g_�*�X�W�S��Mg�c!�N>�<��5\��G���9����V��B*�����i�z^C��Š������p;塰��s���bH<<�Q�E8��k�^ǫ���ν1�b���^��]z��s��>^�#j���k�e�<��~"��P2.��/���"O{�X�Ǟq�C�6������{r��;� �:�}Ny��c�ŚU�X�1�<��+�P�����𷼍���_�{��Ɵ��¿;#의��{�;Φ��3<K��(�s�̳������׀�^=���w…ژo�R����H�]�Y�={q��8���n�;z��zO<��E��g�˃؍*���.owa`�z&��{��^K�<v���c<w��9>�G��W�Ͻ��E�d���B��k�
9�@a�b���+{���#���k��-;��v�㶾j�I��W�J�0�½�lb�q�	ň��ヹ�h
#��Z~�ǧ��hk�Gyέҏ�g��,�SԔ�?���K@P�*(�<��{�7�i��^�W���ˮO�DML���]N���^��v���]�?���sǑ����?��I򇧿|�v�] U��� �����'=�Z �`�L�;��IY*�1?dV��U)<����\K>j��T{i}�RX���4:$��/��:��Sv8���.���#�^K=��o�>u

a|b�i�)��㝫���r���_�dǫ��V{��&L�^��;�P*W���u�0b0���'�$c�CUw�sG�	�
��=�h\�{	��9�0�"��?��p>�#�+n,��!�=Ñҿ�~�re(�!�c/Ӕ�����5�*�p��^��n��$�Ӓ��,�FN�W����-��K���)���_	bV��f��av��O�<�L���"Մ9��.K����^pSk���J/_�?�8��d�d��� �S���B��/pMO{�^\}��M=��;,���p�w����.t�b��+��b��|���Q�f�<�\������'gc?3�����Aː��k�Sy|�o�i�B���/�6}ɓ^L�C<�y��G�:�������/��'�k#z�?9�G�_�.Ŗ��>��嵱W���5��9^ɓ>�'���s�~�%�v����ɟ�+U���"X�\��W]?>����.꠯�3�Op~$�_;U��⣑��
>7:�}~�6�}���\[�68ʿk5�@�U����
n���^�??e/8��sv���^���:�X����Ú�|�,�A�7�\]�]��O�St��}�k�{M/U�W��Q8[��ߏJ�Ye≸���џ�8���fǃ�4y�C�cg ?$��7��?�T�P��gzuL�s�ԁ�#t|�{�rV�l7�����I��G.�����8��
qY�;�\�5�2��_g�_��VqG�Kw��q�A;�Hq߭���ҧJMr.H���K(�D罶��
B��1����otq�
�:�
�����q�ǽ����{g���wCxSG�^z9���E:+0j��8��]�WԲ;TP���=�hu���r�Ӻ�������7�
���C����a<0ҙ�#��)�VFW
QT��,|���v*qFv��%��Yyxo��V�R�_�.Ks�ٝ&�9��*�	Rv'g�&�y�bɳ�otm�[�^WF�>�"��:�m�$���KΕ=���J��9�(�G�<Ϡc ���� �|���=�L�����r��v9�0��st��J&R�R���̼;���%N�%��F�ú���?�'�Nm�`*%_+�?��`���<Zn��;2"��7����,�r��;
y��ȍ�ׅ\���<Ƭ�wB���-�IM{�J���<�M[)G4�Bǧ3��>Ǟ�!2O3�A�F1���fY��I�f/�sS��f9�{���.8
yh%���@��`Er�x��7B��I�e����a/�f;��K΀����E�eI��]Ff��No?��	�Z��A/^�	?T�y	y��
����c{���c�+QR�p�=�(�t~�pG7�m��u9��y$p�N��&�e*��^���B�؝��L��)��z�1J�S~+�s���G���<�!?��Z��g��䜵�U5#t\����U��{3*�O��a|�[��ݓ'�ۃ�y͓�oMM���>�2��k�����&���`�΂Nt?�;hm�?��F�O��>�iͮ�O����c��9���й_���<ㇻ<�sE���)'f���<�s�0�':'���~A�������çc���y'<��x™����w�Mk����p�b��7UT�~-��g��q�F���^��3�[Ê������~�^���g�D{Qn׫���]�z��Og+4�)�8�W97/�ize/���^����ٶ����y�g�0J~�{u�G�mR��	';�C�B���2	���<M�ጒJT��7��h����y9+�J��'?�C`��(:&B��&�L��?�
ݴ�W#O�@
O�#��>||5|ܭ�O|ez��O§t�i_���n���[z�Ot��$��X	��~��j�ʉg�u�*GD5)����]yD?�^���3��w�����ѹ�vfx��nja���]{�����
������N_�U��n:Ց*�s�/�<���rS|E�^������{Y,4>zwf%���������'�R^e���X�݉W������7�<���l_�1���p���\�m�dx셮�u:��vl�B��	��#%���ȯ��+�Mb&ڨ�u��Wtc��h��Ǟ�s�W�v��w���4?�r��|�r���v�+߉��O��J�8����G�œ�}��StMQdE��K����ضç[gO�p�=��gܙۈ���������S�����@ӾFY�>#>��BTD_����X�G�U�>&�
�g��U�/�����+>�T)��ӟvi�k>��<�O�T$�s��vW�.y���È���[�rJȃ�2�P�W��<��w������w7�����(�.��Ƣ�pH�Z��݁z�T^a?A��Y����ڋ��}��ɺ�fƫ�y����^����u�387n�?�aF�ϴ���0|���h9��?��'�ϯf��O|�����h΋2���]d��1R��qN���㇣�ky(D.��������^���;�?�/���8���ܥ绽�'zq��^o�����B�Ο�����B�5�=�Ծ�gg�����K8���[O�Z�ݧ��g�i|ؑ�v��>~�.3���-�I���]��g�����W�;>�4�}�|9.��i��.����Uq�g���O�����ӹJ�\�x�z��ώ?wG?�������}�G~xM3�?r�ΟyCr���ޭ�K�wXyBZ���:'��g���9kO������O/{�(����	>�ow�t�c8Ǵ򭳵�M��Z�W��‚Tm/���3������>��Vq���h�I/kڍ��^,9m�~z�pnC��Mi������k��yN��s�H��Ϻ#���),��~�yH��ذ��V�UL{�M��|�ׁ�&���L��ڜ�~���O��}'�腏��L��I�+~"g��F�gr����c�x�	�'>�Ywݪ��8��^ӄKt�?뿒��t!<V����5z���좱ݤ�+y���J���;9
�i��^��5��W�p��s�#�W{�g��f�'��&���{�gpn�˱;��;;������y�q��e���U{�^��r��ob^�>�'O��R�^w�W�v�gg|<�Q9@{?�|;���aw��6��wW��)}�����hDK�3}��$���e�s=:���B�Bgo�o�t���q���a��	��}�\UP��7��y�w�D7|Z�׭���N�^��ff�ć�-R�z�`�=?�<�\�(���ۉ&�C~_��Q�kx�9g�<ˎՋ�<�(��ۿpT���ԋ>%`)���F��S����o�֟�5>���~0꼖�'e�����
 �;����X|��`:�Z���o�$|x3Y�ӧ���>ō��Q5��ŧ�|,8p*v��/�g��;�ZO�T�xڻ�4��f~[n_k���ꎳ9�W�wUí�,>�iE1y��'��~�2�5�'�구^Ui�/��!d�dU=n}�	��bw��2
>Ӿ��g�?z!���*�]@7ހ���nX�t�O���|��*:E|ֿO"u���[��ۯVP�k����G�.�Ϊ�����<�U{7J­����`7)|�@|���C�Y߾��D��̅������7|�A�C�7���tл�)����otM����3{
��<�᳖��gi�g�.d���N��<����/y�l������!W�$�5�8�]�����W��-��Ke����.�<}��F�7�%O���5:y[�������-�r!��榾b����$����<L��¥��c<@mv쮥�xR/�4&|=V"?�������Y��@�X8��^����װu%��_��.�ώ���Nnʳ��Yçsȸ�
/���<�3�U�Ѭ8��\�_G�_���K��<:�?�/|��v�Rb���^pkv�����*����r!M��)醇���<������Ă�G�r�KNĎC]�v������vo��t�g�
�G�cՈ��|ۛ�6MU0R��l��[��;Q�!��ͨU��[>}�s=Z��q��;�Ο�{�"�z�^�r�L
�H�ЫJ㠰�H�Μ�^2i��T�x>�NV�-9/��gK��1,ܓ��+����x;�X���Άbv��0t��s�~��>���_��cD^{�V����AG?�Q��fl@���pC�-��n��M�m"Q��~�^눯n����P�̝�E[xc/��4����@o���R��X<�Ы�G�gvuk�Q�܎��o[|n�H��8T�]HgDdd*��1"rU`�象�<oν��oO�G�Î�C��a/x*X�z���5��s���8|�P�6�nҀ�_��8M��T�d�'�Q�����-���dž�	i2:��As���<�Ǝ䟱KB��(:}�,�����q����mc≉�CO��ÿ5��5��;Ny��^�9#�X9�C���;�������(Ҽ���>�P�ky“��ÎO������`!։y��w��?'oM<�;W;�{R��E�
g�4��إ��+<r���S����(}�I��f!�^*�U����R~�����\�o[�N����Χ+�;#1&Y'�1��J�]Σrz�g���WM{�??	���L�!Og��N9���^@1|�}
=y�?
̱��I��^����CU'9��<sΙ��ZdF̟[CI��S�ky��7���Q
�<>����w���Ƨ�T:xe�/����3�&�j�1�y�\�}%{����8w�P_E����yP�B��n�����Oz���.O�~�WvO��|��|^��i�����8�U�꺢]��K�ѓ���_�M�	��4~8�����k�=d�Ia�#�7��
�]���<��Ut|�kԞ�Ŀ��O���G/�쵼����?z�����w{���U|^�>�Y��Z
r�L��8|0I�]�֠�]�Bg�&y����|^�5��a�L(�O����GO�d��5���B��>�O�2���+㢏��|�w"
�i��GG�)��������Ce��|F���C�^����}8��ı�)��\��yX�4��U�.$�f�D���k|����y�NN���M/�.��Ke1KU�P%�����N��
;�Ab:/��]������kR�<��y5ny�'��Ϲt�g�g���e<�}\7�y�Ux�O�}F�]��	Y�{��v�L6�r-}�z\=yPR-�T����?��Ɵ���8��F|^�{����F����_����<�i�����_��k�gt��n�]����~����q�>|^���Ǚ3��*&Y��u�����Q�	7�{=�!ϴ�]���'|<��/��#O�H��+���jp~�g�g���g�y=����b��x��Ksޘ���_���_?�=���yر�I��č�����87���%�>�V�}}#�g<�/�_7���S��۫��\,�j���<����Ḑ'�y��g�_���3������z���/Ux���/��_���^�n\�{!�<{���yj�c�*z���Lq���U[�h��_�|U������ڟ_�t�3���i��Yo���~0V������+K�|�o��A�p~��¯����?8�>	�</�37�}Ow���|��>�o�3�Ӻh�(%�����׾�_p&����8�[>3���Ëv:z����'�E����0���8
��{���;���c�H��l���DG�=/��]�g����{��%�2����B���kݞ��0���^��]uͯ����q&N�y��M!���JS�O��͎�l6���^3�x��/p�k"
�Or�W��iwə �>�ǰ$M|��JI���)�c��J���~B~�InO$��N��*���!�٫sz�61G����ٗ
��}�?.]�5�v�Y�����^����z�]B�.��z��ꐡq�*"�����.��xR�<���#�����	z����Y�?��'��?Q�	y&�G�\�A��f=�x��<�'�m\
|��,��<�K߼��^�_L��I�Ǣ����hGN��w���?�O�䈇��8��a����Π,^�2;2q�!+z�A:�d��l?�u�g��U&WFJ/&�I>�����6J�Qo]�p�4
�S,^��(�p���<���x88�S���D-���`��l�s�1���������ș<8���C�ƹ~-z��d1|z��|�}5�|�3�����^ヘ�p��lL|�+�Y�!�,�(^?8#���?�99_�������R������4���/1Ir0b]�<�/�>[�c���E?p�Uo��)����R�]��F��vΣj�h�69ѽ__�}$�&�CWW_�pR�����j�I0�K��0���?y���W������|��+&'IuD�lE>_�;�����;kDV|X����"T�l���L	e0�/��K��`V���S����'�)���Y𯊖����@�a6eJ�'q���҅'f����s�������;�]ʯ�|u�O.��H���R�uҫ���ú&�x���@:
�8NQ�\��2b�c��|�SH�����ٷ�^��ΰ2$s���=cg���u[摯�}�ac穰3_�'L���ij~�Y-���(���ZzL����z�)?\���.ޝO�.r5X�0�.�S>�㾇��?K�wp����^p�(>��(䑈b�`4��w9�9΃��~qf3��XR�f��Ö����_��)�Pj��U��7?xo�)L��A�7'ϱ��<W���;���������2y�H&:P8˃�ڭ���c����R���$��A%��W��?r�#��b4{Q����9r ����_�n��$�Қ5�a��?S�9}v�_���F�1IU.����aF(c�btQs��
scm���t�䡟�0��x�!�W���Sm���N���_�RK�%��Ÿu�ǀſ�$�]�u����,�	�_:��d�����+�$�N���.T#m���N�=���)?��{|�1�բ�,_��똚�?{��txj�A�����[�<�Š���<���%�9ye���wvl��b"3��9��+s�^�=�yT�w��Y�:?D�ހ1:��g>\Z쮽�y~�hΛaJg�KH�p���n���3��Qc���X��Q�ܬrF6�}�@�=��dJ�TI|�C��p�E8�c.�p>_a�^���.��K�]�?�RfD{�Иu���Np|��[�F����W�[�R���2�F����7?d��EX��ky��4�d��iabpn0z�˗�}�-Ÿ�;w'yJ��:7VK���,P�*��Zy���<'FL�	e�y��<����!z�G.��T�I|:l�	y���ӂZg��?�7|��Ǘ�4�qNZ(�����A�<�o�����v>��.ث86z����k��$��Y��=�!	��w:�f�>���|,ƋK�}!O���5����/��n/t�"Vi��G��9>��^���/���.�<��5��=�<-�����9{����[�HT5��:�ε���C�wfu�99&�<�߂�,���������Cq,>��6��������Bn�&0���4�A�O�&��]x�Mک�th^��c���M��{i��*,I)V^����|>�#�;�sť/��y���&y,D�H��&s5�\|�~�:|���o��7�Lju���$6& :��.px3�dw��=�?����<�b�g�q�<��0,$1��v�]�N�����E�|��|��/���ы�Я��l�^�W޸��h��{۴7N�ώ�q���<3|~6�]��I�LS�&$-�����s����5��_�vu]zȟݕW�]W~2��?�Z��*]�]�8�;y���rt
�LqwW�aw��i���Z|ޱ/��C8k_�G�<k
�u��v�7�o�$�>���>Y|U>|����s�,���X���������K��o�;���F�u|f_+6���i�^�޲��Y����E�m>�q��owW�{���?!U���<���i�"�^Z�Vx�W���q��������^�1w�-C����E�w�A��J��Y��gKw|�Ӱ���A�gp�i�O��ɣ'�_��ԋ�`q�
c|�_K�ӾS*������B��)�[���O��������g���Ӂ�G�pN|�O���'>���e��k�?�'y�^�g�<Vq��u]x��3�1n�'�'�>�c|�%>��G���	y�0{?K�_�>�Ni�?���Y]{}�'��׃���k�Ev�.�.+���^����O�?*�`p��!R��v�
v��>�͟;�c�Y�a|j�ᓽx�s�u�(�g|�Y;�g�Ѿȓ�vy�K�ʯ���^�i1q�����xi��}ܛ?��s|j_%�M�,��c��n�_+N�]c��n.��7�9z9ws���k��?cw�wy�k_����W|��Y<�ڻb�����|����~�g�d�F��W[l�5��.^�U�˹D��7�̚���7�� ���9����9���݇b|�����^�]���7���鿆O���_��.��څg�{9��:�>Dy��9�t��~'�>��Z��?���/���?�3|�F���ʋ3�#3��b�`�O8�8����:�k/�%������$�_tb~�Z#>&h�3�G/.�=z�w��>��V��@!���N�nY5!徠|<�<ڳo�s5Z�MM���r��
��gu�𩽃��b���~8�W�$����z�m�w��\��P霒]K�����ӗ
l-����<
?ڣRX�[�C ��#��CTt�8HL*Zw�����10I�
��3Q;����^�r�ɉX;E����wR�q�b�7�Ϲ�n���^�ŧ���9�[�����DU��uj��f�N�9}�ϧ�J�u�s����W��^e�v*���\��Y���s8�-{ŧ���Iv�sn�Ɵ�y�Q>yj��:${�?�>��&R��O͙q�-8��\��Q��������97�c��]l{�ѕ�i^�Q>|ZGm��+�+��L�Z�*q���!8�W��f�s<��ZV��!�	�F<�s�
}��)_��U�:�F�=�����Y
�9�#�2G���H
�N�m�E�ʿ٫v���k�㬼���Iت_���M���ȓ��
�Rp���3��t����/�7T���*����K�쮼/���?��l���|G��ڂ��O�cQ�<�ߞ� v����;�<�az�;�״SN������i��b�ɽy��{�3|f*n,�<�_�w>F�Sn|:�O��5���|B����ٗ�Qܘ8����v������8{�A�\���a��z�P�&?>�E�����3�y��?jɚf/�&�Y?���܂�ճ��+r����4�����ۃ�|{�j�U�Q}_?bX��?@6�1N�L8���z�c�>��
�(�n�h��y
�!�?-@Y%�q��Dq��Ό�����g������U��:�X?��R�)#���^�<�0vo�l�Z�3��U睄�>�:֬���������7v��|�q�E��$*�M��nxad����⣼�����o�_w�ԾN�|�4��'|:ߥ������8m�)�+�y��s�����I�.�9����c�g�M/*����Ũ�+�$�e�v.���ȫ�'yJ�n�V���c�]ZrG�������j�u�P������uމRm�v���+ߜ�6�ȓ���\�Lx�������y$<�g�}ڡ[�|�%r(ߙ�p�K��#y�*
f+�n�?�չ�V+>����|�#>��rY�I&iL��Uy�B�����#E���A�Ǵ����w�(y>����b��X�6H�2}�!�C��Q���5L���.W�M�Q^axv2:;ؙI0��򩳰��)�i��҅>^W�^�&���^I΁K>��f�Kq��R��ܚ�S*$CQgK��G0ez_�G��N���Lش̇��3b�0w���Z�X��KG�p�K�w��p1���]�p�@W,�v��t�	��Q�	Ң8���n�Xm�x���߆�SV�︉�y��rE��re��Q���_‡�����/lVA�i~���>�����@���|y���N@���@`^ª�XΧɣ5�ᴗ��P[�g�˵�c���?|vPc�\����Ҏ���[�}�U^��eG�C�'9:�x�n�O��d�|X�ܲ�Zy��"'���{�:"�k��RpU��}�%u����_nӺ���D��)����g̡��M��@/�Ŏ
�U*+�.��5���j��3�&oq,�3�
�j��p~�����¹a��3�V2ww!�3����.c
�5fw�f��#�4�;���
�\g5:�2z�j��N�z�#)y�f�dVt�Erz#�#�>��Ơp��6��D�<�*Ƈ�j�B�&��i'-y$�U/ș�<-��
%y��եV�қ&f�~�^I���p��B_��nK_�l�aQ'��>�/�9��V�`/���̢e��;|N�UL����`3�K��Ov׾z_�0HdT���9�A���ke�?��ժ`��?�7�š�@�JOP��Snʳ?P�1>�N�à���F��|=%7& g$�3��1|�>N�T`�i��_�c�@�������]T�S�%����X*�O����W��F
b����k��Gϟ�A>c/�i�Ŭ,�D�۝D�`A=>��1X���+�{`��{�c��<�������Yy�Ń��j
Ѹ=��3vy���bA����1o�0�C��>�>�3���ùCJ�|r����?�y�>��a�^���}��k�?�b�9|�k�sk[���_���!�a�zᐽ���sC�p:�?�O_tSq�9ip�5y����G��!OD��Gu���	gk/کM���;��g8��3z~X7ȟ{���c�_��O��yⓟ�}���]����g{	Y�I~�< E$���a�%�.U�:q/y�%O8g������!q�ٸ���R�C�l>�����ow�1�N��Q~�c�L]�E��W�&�����:��R)���B���4�<�
���ݴ\7r��|nw��*�_������F�=��ߤ6���>f���*����	��V����C�P��-����N�f�tY��������ؒs�&�V�L_G/Y�$���������2z�3:�������9, |��g�����9�:�<�e�l7nt��c�}��������1>&g�Z��W�t������lō��K�u����?���"V�~�'��)���I�'�j�
�5��%��c-��9�Ղ�~�ߚl+\3�G��|��c7{�ّe�+o�輖&�Ub
�!aC���^Kl��bK��6�9e/rژ��d�T=<Et���B�R|�Q�변����]U�
y����ENÇ��}ϹiA�P����C��C��+b|L/:W�7ʣ�I��E������^�����k<=�Ω�·��[{��3��
����XQ�v>ʳW�3|�aٖP�B���É�u������t�vX����n�/�=���=�R>|�?�C��Z{r���g�O�N�g�F���/CN��G�4n��G{����O�1��x���Q�\�x�y\�[ŝƫJ)?|���?�����h��?k�'�W���=����3�?d/��g�y��[>F#;�LN�/~�'V�=��s~��<�����4�S{������4>��g�0�}�grnʁ���[|j_����>�vA���{�(���og|�O|��c�چ��/���g��]��-�W���#�־6{�4��U��������<�‡�/���_�H��?
�ݟ��<n��k���x��q�j�"�1����K��Á����֮t�?������UM>y�����)�R��E~8v>֎�\;��S��?�KoKY�׊���g����Nj�&-�l�47�?��F/V��9y,���M8�sa��Y|N{���]��U��-�d�Q�*n(}�-�}i��c칞�����Q��z�§sZ�A�=�؃\�J_������i
y����3�,
�Y�P�K1�Psm��;�h�#���ǒ�q��Or ��o��|�#&|�'��.8��Ek��7�i=d�����狐�"9��l/���ѱ���ߵ�b��y0��s���G/v�Ov1�9�����Х�b��
��V��v����ם�.����^��s4���q�q�.W&|V��v�ɦ7��ȓ����~�<�?������w���'y�^�s�:?���_�yz�>�����G�
���y�ϒ)��W|V����p�|��!��p?D�V�t{2>ݙUn����}�m����re:�W�Zv�ݍ&a�R����{�����u���򭅃��͖}y�o�����3 ���re8y{��30Gi�X&)=���|���%{����>��+��<�쐘Ԅ�>T�EI���vA_�Sc=.�@��-�
����]y�t�}����?^5�;�yV��f��lqQ�B���$|:�Ǩ��S����1�,1%k�����޻p;>�>��4s�V�'>�J�dh�Or��R(�Y���ث��y����C�V�R������v���	�;?F���ѹSG��:��?���t��Jy��[v�?t�Ѧ�Ѯ��
� X_��]�r���|ث�k�E��%:�'>���R�ȯ�Է�e/&6
d�~��z�n#�:+e/|&�����׮-��w�t'���+>x����
��Wr��>\����?�gϭ���G�x��'��dth#�7��)q���K
�'���ʬ�~-n���s��<˹��h���z��pB_���N�3n֭����o
y��+������G��!�t�X�HN���s����^r��1����e����Y���SfT����W�<�i�z��ip�2���9M���W9@��+�<r�t����5n��Vz=>���I_���-�����Ω�'��Wv����Iz�C]��I�D��#pȳڗ��o��|�<��q�g�n��[��W||�]��z[�:�ᱜ��俪,���A��f��?��CN��Zi�:~~o���Ҫ��^�\����=6��:
����4�G�_db/����R��W�v�K�>�=�q�Ov��G�^�8�����׸�W��}~��"?�T��Ͽ;jhu�������7�Wj�~�~ ��d��C�<�_�w���77`��ZP-���������WHzqM��wP:�\9WL}��[�W݅2���;�)4?���}�;Ys=��ey�xM�x�ZDxd�T�<�T�W��]X{������A9ƔԦB��<?8h���! $��;ځ���N�W�~+w����Bt?��M,&/�
`�y[��ť=$�Tc�RX���
��>:�a�	P9㈢O������
�R���U��I��%���O�H{�d<|T�����!��}9�qq��$�PG��Q�)z�1��EH^�r���;O�����+eDvQ޳�~�2�9΍�Q�b�|�c���ɱ�[�`�
�¤���;A_���1i�����J����a��w��^rٽ�~���1����Y���g偶���5��e��J�ʓ���rA)�������`W��k��0�h�����Cɺ���;z_�����gW7?��hJk8�}u��?g��GS"|��y3>�S>��-�3B\�.�a��7>�:��(�J��d2/�:�e%-^�[��,p@a�$y��a}�w�<��AY|؝?䇂���	�%��	��~�"���9�K��l[�
�C���E�q�	C
ƕwY@��W8{��G�3�����{�sY�����1+o��[�n0J/(�z�K6�U#�:�cw��/iT��}�@y|�5	?i嫳L�i��{�kؿ���0�Ç<�t�|�.����;��|n��]zO�W�k�<����Ӛi�Ft�gs����P�}��oL��>��O{�r\�!R��5��	ͣIg,�<�����!��_������8ӫ��pn���Z+�<$�o&�ȇ���!�����\D_�>��P���wVy�
]D���	�ә�������H��i����{Z;>�V�����L��R��9��(���Ӝ��ZЙr�}�7�G��#������4��z���!�Or�z%gy�(��$����;��y�F�6)tv��W���j�8F앚����?y��C����0�'��O����s�����ϴ��'|^�#��%��;A��7�k�ym��/�����U���^Ž��>;���9A~���]tЫ'>��_��2�i�ə�O�Ugc&�z�y�‡Ƀ��!�gҊ���u��?OΊ)�q�*�_�s��?�EnϷo��+��ω���Y��s��Y���B4�2#V(�ǐ���>O�7c>���(��j����z�3;���腏%2�j���v�T���W����O=�/=����o�
�X{��z�9�sP�u��Y�;Є�z��M�}�������ZC\X��Cv_����j�B��'�JU�B/`i_ �OsT�ӊDѓg�=!���S����^�?�98�'�� �N0Z�K*3"{e�3gEЏ_)?�R����rHr�Swv�„iO��L'+Sf��q���K]���a�
��G�
��
��9�5y�;"5:�
r����5�H�[*�߿�����O�sq��o�T�������g���^��F�.N��:������ۓ�q���!��f�s���yE?�c�5���չ#����#���/n���M�d��O�j�@���Gy�����ۅ�{||�O��N�v>��0�w{+e�c���)�t>A��?���3�Ŧ��>�^�ǧ��sV�m���%'��p���ʍ��Қ�UPі��38GO��d^
��c��k`��u��s��<��A�ʹ���wF���숎�|�[.��a�b�45�p��;9���||��Pw{��J���S�g�=꧊�������Q�W�?�'��_���=y�I�t�y���'~ݾZ�kAf��k>��g|Ύ3�	�9#:>�N��ř�ʣ��+���j�5��p�^����t��Ӳ;�W��3w��v���ypn��K3���_^q��cm1���ՠ��F�Y���ӉO��|�`����_�]���·��O�y7�\�7�m�'�O~>�i�������&9�bڋ�_�a�߲y�ݙ������ش_Å�g�Eq/�z��%����O�k���~����CߎsF7�G��jg�jcs�w�s �9�:FL��!�c��W�\
��˝�k����m�\
r�ԋ�b�ww�|���E�^&�.�Y��9':&>��g��;��ܹJ�?��%��z�O�m~�&7��o}u�\oQ�ܣ�L{�8^'�ڋ5P@��s��vqg@+�8�@�ݏ�c"/j̭p���>��\��鼓�h�s���cFW/u���B�k���֟<�c/un.-э��n��������+yx�֜���<�W����=��9�Z�B,@��K�.����t^�z��Y�1f�U�|{NaﳵD���y'yt���?q�zj[�g�?<�|�Xނn1
G^����y�����?�A��qym��]��oѓ�]��{�蕄��8>���~{�.���x8�b�3{�f�
gvz�������Ի�3'�y��δ��%܈�>�%hu��ŢUt�y|�G�ݕS?X��Sn��1����y'��m HH�L�r8L����.-���W͝#ڣO�k�,�(��Hf=���?$a��c?,=%>�ou����G��&U��
��}}m
�أ���?����:�%j9��
X\������1n���w�L�r8��o�@���9������;R�\
�k� ��|�I��[a~���F�}����	��(o!@�K|0gzhXh��^�Əŧc�Zn-z�{�[ҷ3ڕG���W�m�9קvw���r���D�����:��xp+{8��ɩ��Վno��/p{qя�AP�*%k��f>��@g��T�����W��#g�\��Y�-�:�4�3:�}M���d�Gͺ��
��9,j�tfM�IxB^�ə^�'�P�Ƀޞ�lp��r����N��S�!�#Y
�
�@���Z���/t�!|�Ϳ��|V�x�b�t�O�628,�5�0���ӎ�+i`�Œ^�]�A?��S���VJ��<�^��֦�����&V�չ8�إ��n��M���>��1!�Dҷ;���V��zC�	>�|*�(���� ��3Ϟ:V�Xx��7���;����7���5r���U�)?v���@K/��*_��~If�L�+��RГ^�����q���GkN����Ƿ�O�r$�?�sㄎ�Z¬ElUS�h�\�z��Ǫ0.I�
�}}�E5b#��&��߉�'>Kv�?�W��5/��4�1f�j��,|y��$w�9V�i­��~>�g|IN�q�գ>>ɣ-,3t��L�$��Z�#���P2�o�;�'鎏_��v$��8"�R���{����p��O�[�_{� �]K���x��n�x���e�Õ
�*^�߮����`�Yg��g�.��Ij�W�S���_�$�\���ϫ��lgiIw��p���򣱁۞:B���ñ��l�OOm#_���?o���H�@��@I�_%�ؚ�.���sˍ��0X��y8�|����'����K��袼��:�ۡ/�������<O�V�;/���X�{�;�D�����k���}ƭ�k	�]�+�����Z�7�Y�=��ٕ�d-��D#�8���]}��uY��~�>ytZ��oL��O��sm8t%x�������q��뜼u6�a��v��q6�í��O�ܥu��;�km����.�)�ykp����C~������E��xM��й>T��G��.��^���F/�dB�n���<���Zj�91%!'Q�0��7�Α���U�3�ʣ����}��s����Q���;��݇��;�
������*j�)�`d�xh�A/~����t����>j|�y�mnF��O�{��,И�]!��~�6#���Ӵ[jϯ�ݤ`�%�D��6ʥ�y�@��}�"��7�sO���W���,�O�(H������83y�٧2P�4�4*	�K��Ә8��?�(�j�i�uʧ��>��
��?��g�	jI@6�B��K�G����	�y���{��2�̥ݞ�>��oz���qH����ݙz+��z�Uiz�'>Ç^9����vp뙳(����y������'�a8G�w{5�n-�0����?�*0�3� Ƈ��1�v�^;�ɣ=*��U| �����'�_�1|��m|�L���]����I��C�->S�ć���kڗ�vT���pyv{
�~2|�׮p�+&/p��b�����(Q���ɓK�3?����q6�y�1��~^�����v�=o��9�ɳ�����p�|��<�T8�gߡ��k<�Z�{�t�>��.ћ���{kd�d���
�'y�I�^�����b2�4n���z^����F���a���H�އO�8|�<q=�i�-<s�i�}�R�\J��Ν�}�go���^�V�0�hޯ�^c�����s�<(~vG����~��������%C�W�}��@ {e�s�?�O�>�E?�\�S�}e*&�?���b����8jo���<��^�~�a��$��g�9|Ɵ玆�A�x����35N�{�a����/D�p6�ue����8)pw�^���j_�8gw|��1‡<��n�i_/�!>5��5��m�0��7>���|�Q��j��Jv��a���~��Z���.���b�y�^;U`BS8O�y�3���i5����_��\4�L���n�8?��0>�_�2㥼��x/av|F��k��ɾ{�5�t����ط�^���������ko�Q&:�O|�����=����>�Q����F��qu�ִ�p�~a?Wr�L�����xL���h����ƚ�2��ׯ����?�Oqu�g�>�`>�p�h�=ב'>iZ�;#�Ǥ���9��gW�|�T~���<�K��b���,O�J�s~�B��*����ϕj�|m��y��>��/ɓ�D���<�gp�
�^�g<�)��}���3�(ԏ<ř'��4���w�N���/&�x�կ��5�3qu�?{��^�d�7N���}�C��?Ɵ�Wr��/����ήo����ף�[��|��{��y��=|�*��o���}��v:��������NQ&�^�5�W�>�?����Gα�?��1�^���]��w��{��z�>�����y��w�Fz�<��<�}=��x8�<��)n���_��w���P�}�O�~q,�����yp{�?U7|f]+ʋu��8���?���{�yo/W_�=�c���8�z��ź��Ϟ�g,8����z'��<t�>/���g����M�z}u���`�Z��q��]�����j���]��/r\y�����#VΜui��<Q����a��uw�����uz|��o{�a�Y�d���m��K�w��J����\Z�}��e�,�m��ꎹ�
�ػOA���~����?���\�-1΂3y0��çkZAG�(ykG�+����S�P�Y��|�����ѹV�t�?!>|Z���'�EJ���Kf�p�a�}�y��iL�TU�0đj�^�vN	imԑ�&�*��l�{鰄�r�����>�v�5ij��#a_�t��?�sӾ�ę�%ٔ�q՝bu�}�rw��d^O�_5�!���a������b<���;��c��?���Ӛ�x���r�$S�y`$��0>�]:'���kOB����2�S��g�S;��5�{��{��	���-|���
����yݏ4l����]����Ԋ΃)�:󚙟���Svn��'����~�.r���?���J�	��'��Y����mcʁ,1�>s'W�y�f�~��dz5��'��^�^(쭖�Ғ˕<<�җ��!���^iW3�M�=n���o�u��J��>���n@���޻��M)��b�y]u��;��=C�ƙ{T`Gy����ߛN���3���8;��S��5��&������,��~S�r��sDP0赖eD��s�~>�;����3���|8���woV�/fG�ۧ�0��r�_1�����`�U�$��f�#&y4;��-��]���̌�m
�W��U�_pa�8@t�x�|>�*��z7��؝h�u#龗-��<ɪ��?]Wۖ�Y���?��Dm��W�ɣv��_�b����I��bd��?����@�	7�
�7�~�.��(����7���ןn�27�|�A &����ב�ۏ[��ow*���?���7�*�LT��Q��ӓ+¿���8s��ǃ00�-7½�r���L����=�o��^�e�C���?뾴�)��/�����jF�ˑ�ב64�O~q�9uP�;��b|>�m��na��w�O�^Hz�z�F����;�bk�+=4'S*�e����X��Ep�N�l@z�h@���]�r矱6�?`8�X{m�����N^y̵��9�����D�f�*�{�a�Z��h~-IK���J�]�~�p��Tޫ�i���uGE��%
z��CI�(�E=�� �� ?x�Qs�����i!CJZ��շ��>ۂ��Rkk!���:�1"SV���g*���.��3��͎�欋cT��1�=y��1���e�{߃W�s�-=��J�in�ך4�ф�!�l�DŽ����)�qD��f�ݜ��=�o+�R�E	�YKw���*�MJ7�\8�<�-U{vWzmΡ�ٮ�@��|4ʨ��S�#���x�A��G�,a�?�kR�p'��$��Я�����Z�>K�__}���M�7����H1{(��Ï�sx�$�?֨}8�<Ϊ�����X>���2��8�ST^f�܅c��~o�G^3�Sy:��3�H��P{/@.S2ל�<ԋ>�w�׵8qL)�]�o�U���A�=t��K���g�%�`2g�c�$�J0�/�kJD��y6����g>���__�B3�g�h���7���
��*y�W�tA��D�;9;9՘4�����ߞ)��ܙm�ťB�7C��e<� )����QonO����!p��������IN��~��J�y�Q�3p���U�Rcu�Wb�_8��
�no;��'QQ���ӣc��[�L�Yæwv�����e'�G�_i"՚����h�f����Cg�}��=���Q���i��p{��'�õ]���gSC�%g.���ўJ�g�A�on/N�J{_{
���7���7u�3�|��1	e'G�o�w�Cg��v(�vS]Ƈ��,��s������sL|�_g��L��)�
9�O���~Э���	�f�[	������\�r���񉦾�-�?~����4z+�~��:[�S؄Ou;����}�O�_����ln�_�+y��f����j�3��W� ��0�9=>f��L������V��[�����?>}8��\�Ƶׯ_VS��~I�Yn����_�0<�������S�3:>>��G<��Z�''{Ƨ:Cz��<W�>�����}�?��I�%�~U	���\]�O���~}��gqfݵ��~�[0<��ss�_g����3۾C�/>tszr�/�s=�,��_����T���_�<n�a�S�c%
v�~>~�5�g|������c��߱�L���N�%�e��}syv�8�o�g�1�==:��fR�96$%>�'綰�f�/7��/b}K�L���h�f��\K����!v��'<𹽺9;f?�����n�s�X=�q6y�3�v���.��ef��������/���.���痟�o��<ӵS��f�z̳�Ү��s�����}h�[��W��>\|x��sW��o���e�C��)��<��Ͱ���~suK���}un����'j��گ��| ������g�-�~����~UG����s)�w�B���_>~�����Ӛӗy��#n'��49��|�o�#���|����O�>���§��R�o��?<�ח?��O.\��Ni�]-ϳ���eT����}�+���_��8Şw����1cm7aű��,�K�腳��ѯ��c���c"g�,���V$?e/���Z�#&���n/�?�^~��O�w�Vge�����������]ơ���\q����A�=��y�|��������ڳ~KH�ܭ��yZ�"��77w�1♑/?%���O�c+��'�����Ǘ��SM����ztx��4�屜��_�,�)����{�/���p6=Y��e��Gυ�/�)p�x+�9f?oY��<B�����-�fg�G'��>�wD�Ã#�펤�P{���b���lq���#t�o�a?��=��~�sy���ᜲ�}�/�(˞ɓ���x�w�uq�Sw��2��;��o���+}Y�}��=Z���h_�e?�k��џrX�L���mˇ5'�%�/;��-��7wkO/�*{�N�}�9}�ݚw�夽�C�9�uA��������{�壘mƯ�9�lj������_���.�e�찪���Y�'!�;����_�!g��w�nv�0X������LQH�/8���}��N�}��e:԰��A�͞�3���١ �?�S�������s��"(�����q~��<��]3b�ABY�&�e,���u���م����vh���F�Q-�ppr̿���e�y$'�1�g���⤯�f%��.��r3�I�ǧ8O_eW���U{�W��7��g xIJC"����&�a�Զ�Z�>��#�v<�Kp����ZWz�8����7>��~֛*�!O��v=�_=w�0]��6��%E�c6a�ޯ�l���xX]R�����[/��0�&��IvE��L>]}!�ч]�3눨�%ZT�¹;��o�u��ٗ����|FO���7���hX2k�L*w�bҺ�G����5=�l���|�E_�G���A\�()��1��%N�O��
?=��-��Kݑuf��߉������{��[z��/���Bj�TcB�N��u�՘��W�n=� <�8����[\ѻ8�z��G�󇏸�k�`
�ǗϽ��n׵�����_�l�����,����^����KV�g��g|�Wuy��a|rgtQ�Z������K��6W���s�����Y|���$��y�ە[����]]�F��d�L}�wB������u=�*Y���� �Ó�W��䱬F��r�F��]EO~�b���m�[IY���娅bC}�����s�ү�+L)��on�!y&g�K��0�g�Duݿ���o�����g�#�d���k`�>��GO��7�����g�mXC�}Q-х8����u��<�	��l��QC�;?�Ћ˝�<�e�"|��U'��HΕ���grV?�:4��>Y�^ta_���|l$��;��.-��j�������
P��q�C�ɉO��gO�|Ƈ?��َ�6���l�6<����s����<�A�,�7���CD�g<�>�յ�/Ҹ���
�Ƀ8�v�s��u�6��V	
U��_l�x�����gFŠ�ϧ�p֯�ٴ��u�/��\qCT�N��)�/�w���_�Yue�?��/ל��[K����
�ՠ���6Wf�l��잻}W����ڃ�/}�=�O��??���t��ZO��VqC{��y2Ш,}U��c��7��Χ���g�UpǨ���� �p�5�_����Ð�\���M���Ug��h�iP�����[�ǖ=��y�=��~�����nÇ��[���3��	�~R=�^-O�n�
�=���ʠ�c�y"m/�}7o��gp>��ĸ�h�dW�����SS���NIzٍ���=��O~�}r>�p��<s~b��>�f�ٛ��2�������B��c���]~�]�t���YQ����z>��m���.H�kܩ�{Mp�_־�y���%�]S�5&�i͛�3B���>���<�;7���T/<}�fć���	+S���a���KI�#�`�k�DNl����>�e����s�	7��G�ўK�FN8(S:?�Yv}�w�&�a��v���MP m���&G�a]�
Or~5��)5��8���������c�NwQ��TD|��?�'�e�C� ��3���ekh��ϸ{����SD`��B~���?Rs޼��e�M����b4�xz�nU%:{f-�G.��[�U�LN8��{�Ic�B�\�ep��	&��W/`۽=�(��T�qWK-z�$g���w��5�׃6,&��͕����ƀaf�s��u����s]�2�F�}��mP��n��cخ;��e�|��>>�\#���o]�#�H}�����ԅ3�]�V�/r)��a�O���x��{���{(�@7��:2�
�Ҋ��2���wDң�f�X�;-:r�el~�S�V�1��ڒX?��V�G{�L�;�	zC;�Pn�NN����)��_���9(]�#͂�cL�6J$��>«�M��n��|3���׿���:mʦ�]03���X�������+1,��F���q����S����c�@m���hV����k�'���Ω����0 D�����~�3�$7sgSk'8t�8n��Y�v�=��M\myê�m�{�yO�7>�]��Ά�N�ٛ��B$b��}���緥CaD�����ܱ���eէ/���1��n�up̥��h5g|�gꥻƆʦ���~7���Fzቜ��աa����8<�F��Ѷ/�IT]�-���{�x�W�^F�ӫ���wkE�Y��K��+$����<Eg�BJ���Ҹ�1�6W���yV{K�������/���iN��z.y��&[@yC�Ik3�,}�~y.�5�v2קi���{o����ş�z�:��4m�==N|�9i|�N���%����WY�ڷfȿ
n�������w�l�ޞ��œ^2fD⃮�+^=,>�'y*p��G���S+�;{���g�L�5Y~�=yz{պ�|�r���w�s���ćVM�0�K��@{�姅���3qN�!�s�&��g%g�'��{n3*=�|qq�]-�8�D�r=��H�*��<7
.O���Mv��N�9�{�yv��s�G/��Ň����Am~d�N�˒g>�7٭�������n��s�C2��;|<�h����S(�P�-VGM�Jz��z�w����s|,�|<��������~Ǟ�?�6vM0�����=�Z�x:��_~�s����Sr�~2�O����2r�3�x�+���ѳ����1‡��r��)�>��*�]���Z�/�9��=���O�C�'�ۛ����7�g�r�����r��P;�/����S~+ޟ�Ӛ0?�~��h�����#�S_��ӗ��a?��~�W{+��~����b��g��/{��g���/��ѻu2y�[}j��G�����$��1����z[�<=㱟!��g?�	g��1;���}�8��hLZ��y�7�֪�+�EBFw�X+h���� �&�a�!���ۙ5ź�r��WM3�O>������(!O�e�Q1�{ws�{�r�_|��2eGo�l��#��
J��۫����3�L��{��yl
���C��?�O��rM�i�c�>���ܡ���[a�H�8V�v���RE'O5�ɹ�?y�I�V�D�)|ȟ��#���}͇~{;���&�K�wh�T3^�s|�9>��>y䁃������F�f�>}!Ƈ=/��j�N�탧�O�<��{�C�_>}ɿ�w�aW[����)��b����>�_����-?�n&���Pz$��}�����5�}��L���_�|��~,�ȣ���'|N�]� n����OO�;[��D��K6�\���v8w�Z���t�8P<d)�cE�g��<�{wG��LIs��W�3i27ٽ����f���s;p~�'������]�Aw�D'��_����ݵU�|�L��x�ۛ����!c�"��/�tS�-�[�\8������c���S��thk�~��p�_�gt��_"�?>�3>�D��Sv��sB��i�'ű��{�{��⼛��a�C_�L���X^��iWj�l�Vf��l�͝����q�Eʅ��W�/�M��<ۊ2}I����Y��w	
�<-m�ܰ����g}�߹���N��.>ѳg]&g�c!>������I_ɓ�����/y�Iz���h��>�+W@��ڗ˕�سq'���3>~>�O��p���C�����;!K���}� ��S�ѧ��0��5B>�����g'�̞�֞�a��I����מ���'y�/�����3����Oq#;L��@���⡕:�[W���g�re��}��(��]������2�i����z��W��l���?�(��l�m�5�/n��>�Z|j���>�V�Ѿ�K|f����=��/|V�y��/��*��6�+�����g���Wv¨:��r�<N{w)�{0��+���ؼ�ܬE���b�A���0�~yn8����i���������؋��<�]��pf�^rO{8_���/�Vr�~}�]��x�/�2�q�wQ4&������q{�����{Ӎ��Hպ[��e?����yw�
y�IΪ##�1;��J����oW��Y�!|������M��ާ������j�a�E{�3>x\�H_8?9���3T����/��Y � vw�3��eO�~uM�������#�	�s9<Q�;����hm��Gp8�k��=��t
�尝����
����l���e�������:��bx��c�E��W�O;y��v�"u�֘��t���hჾ�>Ϻ��X���^�e_��\~j\�~�=r�!��	[8��������������K�%�җ	%9ӗ�/����M\����o/��g���z.z�"�\���s�>���η�=��r����s�����k��;�	���#˅��~ȳ�G�җ?���=?�?M_C�Y��)���ۙ3�ݛ��k�|��<�U��B@w���'|�O~�<�|����vS���T�!>=*O����#����e��*�]��LJ��!{.�wp�8@ڇ8I/���pо�����Y~�O��;t�v]�lD���'�e���+���:���h����ҵM��3>��.h�śu=���7�G�:2��}�̜�b9޻[�"���R�ѯ��P�_���<s�2��O��>�uI�i�+5&O;y��8f�0�9�~���;:����M�'>�}A����i�k�0�T���#�2>��sY�U�7�Pz=n�L̗��@T�#�6�v�'> �|ս���;��n��23Q1;��v��O����s�ɮ ?z4�`�MuzL��)o�<���6��NJ3�<�g�u	J�����%]"�J��m�A�/���/��q��)рޡ�˞J�
��x
&������?�+��X�+C��Dk�v�ɝ��(aX{�Ts�_�������DN���wo~9|w�;P��)�\�S�#Rwi�U�6�l�<�ׯ�]N�xY�9>0�ILT�iw���Z9�����徸��Le���v��U|tJ{��<O�]����ߊ0��a�;���:�{e��W�JA9%����O��Ƃ����Z�)�w����,|rpt�%��������@�^��/����#�%>���Q�:�Cg��CwաW�Þ�m��{��_��Ù�F���[&D7MNN��O�rwZ���|ȩ��Spv�d���k�>�߲�+<��D��s3�I��;v�~����"�����sC�qz!�s�%�o@�rV�����S���SY�A��g�y��
O �G������`���s��ہ�X�~�O�䰢�|����q%;>4��)s�C�[ۏ�D�/�粅��<nI+��O�^�fs�`���vſ���<���ݑ����kSm��?$<w\�uҗ#KԮ�򌐑����iА���!O�go�N���<��@�}�����	��[!�e^Ǜ�L#}��7�nD]��]\,L~�>���d���#���7�3��"���*pS�֊�;���k�66�Ķ�u<��L=:��"��A�ÙWdFY`�����`�':�F�"���	�8�&Zx���94���O�����Ӿ�'��K@+�q��:��?<���א=sl���<t���5�(C}`�{GC�\W釛�F9覊��T�&!cwyy+�Ζ��.��]t�4������Zd՘�������!���2�鐅�����,�{��Ç��Z�����k�}����0@iC_���(�v�w��>@���t*�jYe��	��I���yϬT����G�O��3|pИ?�����ѐ+���,=�<�[�&�W|�����Sv�4V�`��3����<�11��{>���tE�����������n�ѱ�F�N��1�2�TT�^hx��i��l�xo�[d��~�f��1��P�3'�̵�$*oe[6��},8���C!���#l.{�Q��M�K~39x���U΀Ek��<���C/f)\��Y�ᓓ
=�NG]N_�|�>��@�dr:F���z�8�~���Cw�;2rJ����zd�D�MV��.`�K�s��1��z�z�*�X�L��(<��G�����7�8��I�)��a+��h�����^���w��]��6 ����f���,
�=�l]j\f�Y̛K��`j"��bĿ\x>�I3���	 ���}ɣ=}����*I��Gz����y���\3ozo��FtB�Gז<�
���Lԫl���ɨ�(ZS�g^!��l��[��BY�����+y|�G|z�h��.���لl�f��˘ÄȃN��������[��Gt��ُ�AuJ�7O�fo|46�[	)&�-Vg���{E�<����B������h/�$>:�O�
��r�Dց�-L������%��2��X�2��c�Չ!dv��z!�§�����W.�J���nZ�_§3�al3
�e��
��߉�W�������j�{u�}�3����	w´��q{RL�����m�a��Ogh�ɯ���Shf�D
y�)�i>���[yz��'y�s%Һ�șA��;&�W8g���_8��Ը�J�<�2�29����G�}��pDz��O��)#\<�΁�ؐ������Ȑ���R�p�_v�q&1sA,�Z�	�J���f!�O��]^���ZO���Ӻ�3�;�<�ȃni�'�cU��r��R̖�_��w��;�
#���k�Hݝ���?y8Qg�{�'�W�e����3v��wr��0�y��{�Ӟuuk���_|4���O)g�Çp~0�\ Ϻk��Ģg�9�����4ί��Z�="����ɑ7�!��l�1���D��H��@$��3>��s�J��|��2�[�_S���orD�f}n����?>*���4�O�B�ˠN�����%>�����
?Y���΍D���~���P��c�~��h��~�vޣ�?>�3y̘�'�],����:86��7٧8|rv
��	��w���4����_����)�H�3}�x������K���U��ݼ�z0��G�3o1#�1'W��{/���Ս�l��
�2��ࣽ��^��<C=��1���5�D�srz���Y�Z8{����X�s�\�L�){N��O��ݴ�~��vu��™^�x�[��S|�����z'|�Wl.kg�_��s�z�2�{�1y���
���w���9*��㖿�f�s���O�'�O%d�O�-0��Y��'�E��� ���t|���ٮ�y�AϞ�w�h%�=T�q�b�*nhoF`�5|�W���B�[;�s����?��?pN_6q�I�M��[t�����������)�&��ك?m��?�{uP4F���]Yio[.|һ]��R3#P�ײt&
��q{��=���� ���"|��&�~����%|�{蝰��쐽�w;G���/���r0ű�J��֯���w�N��3�������ϣ�'{X�ՕK�5OH_+��%��xJ_K�3b��@ڥ���3���$b�%��m�nb��4gY�+Sxq}�.Ȟ��'��w���=>�.�����l�
�e���Ma[ﰓ&���Y�TǨ�ÿܯ��}SvL�CH�rql��'>�t�ÿ�6�g��/��G>���:��Oq5}5g���K����Q��^�F�m�z�gq����i��p(>�֟h�y���+��2�a�c��c�_��_�g�OO�,frk=��Ïl��o�F\�Y?<��(>[�8e.�P���8����92����Q���䁛��ڠp�]��﫽�|��8��6�Y�Ӱ[��逺��[E��,�h���VS��;<�ǠXq�8C���T����/��F�/|���'3'�w��`@r�L~�p{��~9��N��=�W=Σ�߸�^@Ȩc�z�1�e&.J���+��<���5	u&��ˮēq�̬��<�_F�5D��4#�S�!��7����'<�
��u���*�:�!ߌ�s���L=�O��r�l���O�u̒lm�E���}-q�kl��_w���Q�w��(?9:��'>����ןq6��c8�O�=�zHp&��髳
_���W��S�}i_	�u��9�.g��L���p�z����Q
=�m����'>���T�C�����Yn��ΦDE�����Vć�G�l�^��c�])�쇿��ᦱa�?�Zg��.��X� �t��/���c���m�_�y��Ek���<;GԾM������Y9{v�<To�p{������H*v�1!ɣ�f�w̌I�#�i���[���a������-|f
��tmNn�da��H؆�VUN|�)s>���T=|��`@�Z��r�t�_����=i@�sx�<�;eO;��k��R�3z�⟾:�о�"��'œiϭP
���D�q�!���V����-��8���y}7��?�S�s���H�SD��S�_��k]�!%�5�(�.��Y�	�:%[{�/�~���X�d���~1	��qzD�Oz. 7w݊�T�<gˇ_d?�r�]|�	���ć�[`6��r���j�ա������^�ͧA�8�'岬R:�Cm�\��R��$���GC8�ș�99q�yq�%$<��?�A�x��A|����Y*.'I���"�I_FLr�/�/���Q���@�?<=��F_�<
{F_�R��x��)ע��?��b�/�v�.w��$ѡ4�쟊����/=�T��<�ɓ��c��/%� �[ �V�e�������2|]���7W�:����)���q�^���d��������F��x{\��	p�ܙ�pp<��E�~1��ꦰ퓧p۸,�����Ϭ"�I\Ai����/$4���3��9��s��Ÿ�#6��X�ɍ��/��Ըl0�%3�E����0��=��x>�y�As������-��}�t�V.x�ߚ��PU=aH�Qt�k���p����ړĚ[{�ra9���16L�|E�<s��|�8>ѓ_{�l�17��w�S���g���
՜���ִ��2zBB|B��ԗ��G@�)��(T�T�c�Xa�Ȏس\F�>�O���<C~rM�z"8X�5m$-|ءyl���ޤ���g�����oO��Α{��g5��>o�A:�7zu��k:Y�3{��(^�^KʮN���í5�Y^/%K��jӑ��k@�Ñ�\�~k���}]��:�g�h������c`u�#�����^K^�8�����55z����)��3szh=�C�E�����Ol�8�'�!�*,M�Ý�
PP��5���u��†ň6*kppD~,������^g���M�ƞ�9��!���ܲag&[�ͯ�}L|�F���0R���ݪX'�i��ɇ��n�����1L�'�e���7�u?�v�(�ȤFx��~����D��)�3�ݶ���ks#§3���=>Y��w<=ŧ@��o�ᓮ��[��ә��Ҝ�P{v��ܜ��y7xY9��}Z�<^�������:s��S\��~��y�׃Ψ�Ws�*�N_���Wg7���)����[9�<~b�2�j2da��t��%Λ��,�9�vl^{C�}�[�l�v�O՝��'������_���+���_��F�*֪;���3�cD�m�2;�pq�h��g+g���7|�<�<�̣㟾�����l�o�|��2�<���%��ȥ��g�r�J�hMҜ)yn���d?���3>[����u���M7%�Wd�43�Ve���W���������O���0���ZZ�є����Z���g���70��5{����Zhɓ�]�d�]��md;�϶_��칲��G��=h|Z��ѳ�~�Or��$b�&��tҦ��=z��<�ۜ�'pzn5��ȈU��<�ѩ>b�9>K��O�y��/�0y<w]-��B9�T<b�OkZ��$�S�x���iJۂ3}�s��t���z�����'J�n+gb�h�U,�/>���,y2�����l�p�Cq�g�+y�ii�->+n����
���}�
Ogc��b�j�=l��K|��K_�h��O�'�3�r���� 2�m�ԯ�cN/����=;_q	���B%b�m_N[[���ζ�$�_���k+n�q��^F��Kcr.y^�s<?�{F��0>ݚbI|^��ѯ�=�W�Uz\z_���n����8����a���5v[���/��#'�J�!��&�k;L8���?�WR�r:��zN��(����WWk�g��ky��ǧ;&���η�x7s�ǙS|������j�2��ֶBg��/;|�_�YHQ�G�M���ŧ��;��}L���gzg��R�J-p��o�iz��\�eψ���Ƴ��e���YX�[܈��I*^���6n`��a�W.l�r��8����g��|sv��6�J<z��k^��rq�����|V�^�;y��ʄ^�����~��k<��w�E|��9���zmz��s�V|��s�1x��/�����O�{���i�z=ߨ_���Z��v�#y��f����짯ǝr�?��'$O�,ߎ�/�)����kȨ=y�d�F�8���Bz9�9�	���xn��F�
�����O��_,>�b�J��r�~-a�r����{J	"��z�qd��Z.y��u�k>�������?}�_+�m��yˋ��f/��O�;�V���O̷�֕/�Wo�E��_����_��/��A_��_�Þq�FJ_믿�g���:���p�����w|^�C�~�^���>�=<�����j����Z�������o��y������u�ϖ�b�����s�v�<��m�/�H�~����N��z�����k�ٞY�\���/�c���U=���E_|ί�E���T���v��y��6��p]��w�Ӧ��XB���~�!nġ./?��M�!τ'���Ȝ�I�!�3=�U#�l�h�����)DZ܋�q�ɺt:'����c;量q�>2ˍ�]|\O�1���@YD2c˞%�;����<���s���k�yʯ_�̒3�ƒ��B�ͻ�,�*��?����·Ou\�!�.ȡA�dX��3|��2�����Z(���R�F����_r�؈�F���G��Ͳ�pVڇ<����H�N{��'�Zh>6�FB$yf�"rz�C���W|��Q��\��K��G�Y��dtTSiq�q��w�Ew��{�w��L���wD���%���ӟ��֝k��_N��
X$�o~����3�ŧ\۲���z���2mV���U
��Ar�%Ow��?=�CQ��Ҏ|4f?�("�g�z�gQ�j���tf��T{(�՟xl|(����7O"���}�,N�8�)Q	�K�J���y���7nU����*x�w��8>�C§D�
~�/�%Ƽ�����Օ��nk��筠��-ZvPp���O�>K������F|��Wq(�a��X��)�Q�]W%����mnJ����񱇾���z���j�y�g����_��k���W��š~�^|P^˿�IԵ��Jßۗ3�9�m���}�e�K5�I�r������AԼe�'9�iP~$�����u�މ�?�2+��I�����6����=����$��&7$RXW�r�^�|��3q����+�0_�A��7p�@xw'�}��|�;��-���L���FMR�N���G~�_�����O�2�L�K����3Grn���SW�x#\��ߔ��'�Ǟ�w��Z�1�?��-naK�%����~9Wj������z#qgt�O�&��!�R(7�S}�J��WG��p������3�	��o
l+��@�0�A�̨��h��ԽS�(��m����;����=��|��3��Pf�O��:F���4�qɩ��K^H�<|0)�Fo&t�ż�F�.����o�,8�4���c��@X$*�a䍾��=�OD�
�c��T;�W�<�y�Hd̃�;8��Ɣ�i�}�������b>- u�]<M|i��1���G�g�JP(��{Q����%�
?��ȭI]�'���d�35TN���<+�U�bA����Gd���kNpL���s��`���@b��]sk$c&9��H�����Ъ���ќ<G�c����
>
�=��|�w�Q�TP��B
�g��=��_�E4d��i#�����I�t�o�(ߑ���=��>�/�<�θ�����A�B g�A��ے+#d�1L|��Y��rM������'�k��q�	GG*VD��":�7�[�W��û��p9(��NF�/��#���Hcw��žG�p��<��Pnͼ(D�M�
�ø<;e���r���&��yꫩ�lp��y&S���=�n�w�p����1���o��<��6JPcn�]E'����Y��9�����]�^�
Ѭ���x���V��x������c!祮��yǍoF5��Y����.��@��_^���M8��G\���w��I���d���F@7����~|�А�Q���ꔴ��jr��D�N�$]�ź��q�gfcn2Y{��S�<;G�m@��d��l���z!�U�I̻�����̈��ǿp�t�3�^��N�g��$�����sl�q�!D�B�kˇ<�N�گ�6//.zmq���������%nc�O���{�y�<�C~�������Ӿ��4���6�s��3�������б�����8������%��'*��gV=�u<���h
��{���<� $�����#Q�b1�ڧwv��<��������sJo�h��νo�km��]��sײy���'v(���T��Q��5u
7�(��k(>'I7V\ɕ99�,�M�ۆ�I/N�9+oe�����%�̮������%�e>Ï�A��*�r'��2(Vgkɓ�-
}��8���S��\����!�����i����OqR�,'�����3�����\{�:ůك4��l��y�NN=�H�+�ݵ��qw� ���_��!�O���g�O��|~r^ݑ�׵���T�����3�����6ՃO�����Q»�]�v��ךt;���6��d��z�.��"
��1��w�m ��U=�ia�n�˗OW?��N���^��2/�FMw!->�y�r}xrhPD
?����O�i�7�Y���w���%<�S+���o���?}��`4��2�%;Y�$����Ѽ�C�h�K��k�c����%��S���}ub�Sz������+jd�c��\��O��<�K���(=f��G����z��ҩ~��?�l�g�w���K
�:����_1NJ<���d?[>�7c���R�e���/|>��[�����?ߜ��?�G_�2S�
&�Û�(z��F�������Mq�zx(?@-����?�LJ�ѧ�ۼTO���;��9j77�ս���:��_J��m������G�v�J�a�z��@���n�j�]��yw�}�ݿ�~�ͦ�̈�ʛ��C���_1Y��Ŗ��O����ã})����M�Ƨ;�p^󇅏���i\����%��ֆ�\��K�կV����r�>�/�>�s���>���{?����N�N���:�~�_��^��8���F�E�W�
�Y�����5��?NO��jS)�,�,��
>���Y}�5�A��xqv��4D.y^�k�Ay$���A�s֑��/^1a��U]L#��ch0�>::,5t�O�/NNά�*�L�vw��o�sɟ=�Y�^�ϴ��ѵ����<|h�>Fxˢ�C���A�/�U��2Eʽ�/�fv��G�i�b��EBR���bԣZ��G����/N�e���M��3z�����!�k]>ս�Σ\C��ճ)�|./~���_���Tڒ���>�}MJ���%{�>k��<������ʞɳҀ�V�� �碯3�ߺ	��7;aAʅ�ςO����J�����?��w��fl~������*;�U��k���>�xC�Z��__ݜ��Wg����y�u�����~s}�����]�wr|�i�Z���N_oםJ��7����I_˿ءY3>��rw~��,}��>�ڝG��C�e� ʯ�)�Ĥ8���3뉥����b�/|D��6թ��θ`{!���Q�O��@��w�5�G$ܺ��l؞�-0sb����݂h�Bc��F�e�ƞp�4V�#�۶Z��B~����LN��67�/�v�_�
�~w�P}y�3-�r���T���_|ֵ,�-�^��`�F�k/}�
c7���᧾p�9��胮�2"�u��
&�x��p�
��w�v���[�T�q_����<��Κ܊�o�������8v��y|����x�&�k���ɰ+]��3y|e3AqXEP����ӝtt��R,$�!+v9�~@����Aе�[�_��������Y��U��1>�5��љt�.3�?�����ޥ����酿��:����_g��8��GY@�g�\
D?���K����<�N�ܓ_��N���J|*a�k��K�e����޷�N�GgϾ�+>��������C/�U]��W��ŷ��_�H_�] �svtʢ2|�<�ѝ|jO��0>T��|�"���e?s�|������y؄�4bq�~��aǭ$��_��9���Gu�?d�ŧ��<�~r�ދ�O>�������zrGl&�D��1��Sx3�]9������O��㳵�n�A���n:�*��p^/��^;g�F�j����g졒��}qc%N-y�U�{nv�9Qj��3���,��TIȕ��8�e�k�S��M�Z���R{��Qy���Vq�f���X�".��U�5�|6z�Pz�g�T9[‡I����_e�@�nF,3��;�q�.]\�Nԭ�8]�C���/���e��h��pg|/��<��r|	�+^��V_��Ku�5���Β3��O�D赇��dQ�z���I/xh\Ə�
|Lg�)���b�_�ކ)a�s��/>�����k��qi�[�����u4^��a�ݝ�e?�5��]�B�X�*�Ó0��V_� :譿�gś�^}�Ot���}կ��
��OAr���s�1x��}I;]�&Lu傘����ſyK�ikB|t����� �_���I�П���n�gu+�E!����P��ڋ�)h�v�S���yh8�"��wS91��k�w�˿�1���[����j^X��j����3}8 ��g����+�>J���I��7��g�J'j�	ѱ�Q{���xH���#$��%�;��Ξ�d�މ�(�V�Ȓ<��-��'��v���<4���}��g+T�5
��t�9�ގ^��i�V���=�#:>��%���;+
�J4ϫĵ��:��p��2%?a��C_?j��ÛW��s�O|�O��?#�R
&B�W��)�xȨF��r�ʾ��7���%�?�/���_%�5�q�tl�^*Z�U��1Q��O@��<߲7�����h]���g��a��X|��~��3}U��#��t'�"#^{���I럐h�7v���֏���=�+S?�]GO�)b��;0&9�(pL�7tO&�p}�]����X=�{��q���W�~���yN���i�H�r)�:��b�����q~���e]��=�O�gXT*�w�������#���n#��W2G@%��?|>��g���@�[e��D��pÜ�n����7�/����Z;�?`�p�C��l�S�f�bV���2���n���L!�;���$���a5f���n��S���6��+|�O�
y��O1k!w�����@�����9�}i�Ξ駒M�Nr��#�67��*��OwE�c|�i1�Q�v��1	y�e`��~��̻���n���}�X��7��f��L�7�N��oc�H�DC<q�'�ԃ��߾��7�?X�Q��x�~
�Z�W��_�r�޲��d�� Ҳ7����l��<7�K�_�߳Ov%�R�b���9Po��~���~�u�qz��[�9���
Y���7"(?
�/>�%�52ً�2X:}�'�Q����p�PS�bW�����"?s�Hj�LN��=W|H�_�,l\��x����i~�D��]�#@��<��v|�v�f�A�=��;����<fW�[�������7�&	��/�}}��������>���
m�!��<����_Gp��Z���_��d4c��2�e�����\t�Z�<�UY�����}�b�c>\���5k›�����D�p��5�w��cN?>v�'��v�ߺз�tt<���;��ޫt��x��|��S�o�"�&��8���b��<�U����k�y!b;)�3��HB�������N��L���c#J+v��>�C������\t�$>f�3گ�}�a��}f�Z|��i�u�s�c���p@�Ǟ�S�.!���{���^"@+��� rxt��A����ٕ�ׂsv�_��<ū�i��]	���1)7�����QpW�YLv�Nx>����=�6w�O����~w���e*�����$��s�;|�������v���<OON�iϞQ�-˞_ȳ�aɓ����w��"A�G~��K�V���<�S�f��o&>�޳7���Yn����''_���I�b��#��y��0f�$����k��ӓrw���F�+�a���m�}_p_+4��ُ\K�䷜C�Z9����~�1o־����z?=�'?5�Ȩ��yr�X�b���#w=>��t���َ�i�M�]<,W��s�g�ϒ�~Y5����K��D׾��H�q+j�����ĴB�8����Pt|����t�ș<p���|ޠ7�8=?5�/6X����*��<�N�%O��\	��O�d�?}��91�+9�C�]����/8�/ϵɍOwT�w�=����A�����qz�Ζ#�
��s-�aWK������r!y�>�C�����`Z|������?���G�z��׿w�N?��EL����1z�5+�BM_����@�N��_��tݤ��h+��~���G���=5''G�>���8$v��_��q`���g���
8hl��,�$&z7��-�<�	g���N7�ʹ1�*���i��+���h�P5�>�n��c���>gg'��áL���3��}'Ϟ~%���՗RiB���Kh��Кo��_���3�g��=i��?>�/�{w� �R����a�	gKϲ.l$'��饯���A|�Lxn����*rꂏ���/�S��f�DJ_��k�~y0V�Ï��ڋ|�l�r&M��|�:�[�y׷���f;!���λ�	��L�m`��
��g�K�������\;?h�{A_�lO�=ۑ������فe�s&'�=���L�|��I3<u�O~a+y���-{�Ϟ񷹨_�3G� ����c��=�����M.��Q{�0)�����_מ���
4v۸��ؕ��=:?]��}���s�O��o�t��?�Y�..��ݭ�}r
5�+�K7��ğ���z�Y�xn�tf���g��z.>K��F�K��U=3����Z|�8�O���p+��_������q�|��sKC?���1���<:��v�����_�d�=7{�O��/��s��,�\=�������W�h�wF����y��k�K_�,z�ý���^�a6���3�|�_t_�e=�|�3ď��W��g��_��v8{⳾ ��F_��u�&��ޜ/�'��?��H�r%��z���U�*��_�����ʩ� �?���9)��Z�/�C��g�wx���?{C��No��.8:x/\=�?y�a��lvL���~����9>ط$6Yuj�P�z��r�W�b�:|�kGd�6�ˍ���c-P�w�i�?o"�u���,(�b��[��Z�{{_��L�J��r�̖��xN��ۘ�+z��b�eg�u�'�e�k]�]!�k��/<�~%�9RU9���n�����G�����:|c����m��3瞋]�7�&�C�ϯ����ۏ�A�^���s;�}�i&6� �Yi�
��e��[���;��y���	q���X>�e]�r־��y��D��3}�&��8To�����Oλ}8�yć�џ����R#'��KyN���!?z�������j�X$��`M��Q�y�����%�<��_�|�^�eѧ��w�w��Tw�3��q���`^:q�_`RA9^˴ב�,�ѓ�:.Ƀ��k}��g��g�/8x�����۱KsAgWُ.c�ɣ���'�[�	8�yr��7�f��]�U��ܬ�<�h��u��nq��C_����F� d�Ly�]L���w��z�W��?��~�Q���_�.����=W��g�A����-���C�;g�n��/B
�t������f��x�<�����nę�1�'�`�.9ȴm���^���L�ֹ�	����F��s�����iy.��ό�?7�g�'w�=}5�q67^ߝ�M8���LJ.��݂$�ӯ���#:���%O��F]��A/������܆��.��NJ/��}"D�G��c|�fu\�g���k��W�h�ٹM9��쓗e��C0����;<�]�V�9ܪ�8�5>u���z��	�Q�w��}�N<i�q����œ��ifr�J���>Ç�Ly��G����R��������y��Ԑ1�M>��"q0e���r�ȿ��m}1�0#1}~���d?D�ӳ��('�H���L�q�ҼVg���7���m��x1*(�s`�I{3(d?�����+��B*�
}l��3�B��eVx�_���c�t��y���Dza6`��Fz����y����x��s�ǩ?yz��襵���`�{��g�]��vK������7����䏞��<;��{uX���կ���~���Q�e�GM�9���c����ߢ/>�OkpofGo#�s���k��s+��,KNt��������L�����ӏ���R�!��8�y�iD���w97��g
{���W��\̦��ut�f�FG?�s�B\R>`�O�i�-�������#�������'��Ŷ�O���ɟ����L6z4��?��^�.#K)<�����l�����������_v9U��=��P-;\�������kƇ{���	r�
��m�8��,F�w�w@A���+�K�����Ir�P��jl�ڹq��O�\}��Ve0�י��*�:���G>�1w0&̴+�ӯ������f�Yc��]���_>p/@��}�sq�tK�As>�9j�v\�?���'z��<ց���FsaW�=�ֽZ�]ћ�Ø*ϲ�mLOo%p��H����#��㹪"8����s�\
���C�}bC�;=u�l��A;>��Ų��Y�[�αP�|��i"HG݈���;ko��O�A�n�7����\pO=�y��Q?�MF��SB_�kSt|h�+%�1Zj���`�\:#�]���zF�q{�7g� ���L����4�>a��sf����i|�Y��X��Zf*�q/���Z��\��͏n"�1|��v���{S���>�g9���ǔ�)}S]��Bb��E`M�QE㙫a�=�+�TU0��1�u႟��7������P�1���\c���g�}�i��_xUh�Ɋ&8H!co���3%ъ����3��=�ٳC�޳q��t��N
�輟��`�K�S�p9�(�D��~��s�ǣ�j�#><�v]��֯�9�������r"�)�3W�@�"}����}�����|'>�<m�'rj���̹эL�ztO<�*�8��xrzjF���צ����XT'F��Y�e���O�[mR���3�M��#=wwo�	�`��'T�\�<�C���=����q�'��g����_������Nqx����g��'e��,v��J�2�Foe^���-Ʈ���j����$��ڕ��>��>��Y���o�l#,N�g��MX�Z�Us�ӳ��rI��ç�>C�	�j<v-��3|:���G�jGU܀O���`�A|�����XbhO������ ��x�|@�0�=�8��|oW0����M��e-]N>��N_倢��RJ�7S��勽Z��n��~�~��>��9�#R�%|� �ZQ�w���3���X���m=]F���%��e�V\�=y��f��y��_O��;�"|{���1r��]�_�
@hG	�{zq��讶p���W۱j�:϶O���=>=��^]ʮz?�<N��?��rY��ٶEس�@���_+��NV�ٳg��n��/����)��uU�4|�v+�2��_��~��+���b��<|ʕ���/y�?k�s����졼�^��G��]��J��M�ļ�q�ᒶF|�8��?�W��f�(�~�����~�C�_/�CD+�R���Ի����<�<�x����;���K��K����o�� �rAf�92g�_Ƀ�1�9ݺ�.�:���˞�����G�/x�M��?�1,n|�xi�+�4{��Ln�������!��U�3�¼�2�?�Wg;3�;�d��W|~P�s��.�H["E����cFf]nO��5}Ň�%Ϻ�~��SKAON~̧�f����^�00�,�_�
C>�J�kV�w��T���޻��9�
>���7��|;cv����-�����SVB�1�!Δ�_t����J��g�	��Ӝe��{�CtG����I{vy��ݮ�uG������\�5�`xtzHQ>�~n���٬k�:X���T�����㬝5�Liǣ����&y��[Y�ӋӮ.}Ag�!�;��Loe�~ŧ��>8`�ϯ��������4D�G|L�zaJX���q�b���V
Ͱi*�#Q��`M��^���g��͞�W�ؐ�_?�Π3N��χ؛�amr�P�މG~���S|�2u��>IWן˕A���Y87�6^_\��>�T�B�_9p^W_'�pf����}~�ﯿ~��Jm���z�·�o�rP4M�ڧ���s5���>�#nL�Ƀ޼z���g�7j�S���U�I����Փ�8��'y<�������Ve_���-�����g��e5[����\���׸l��x��H�B_쇿�k�|~NN�`��<���Ƀ�t��}��'{������g��ɲO-�Y�?���i�a�A�6���8��YȞ{�ʁcW&,��͞���Uw�e��{�o1�\vu�n��y�{�m�@�3bv�w���e-�7?���S���r�����jӢ��=�o����g?���M��y�}��NA�Ŀ���#4�BT�W�b�5hf#K(�C� ����yNZV������4����_�,�/�&?Ϛ��� �>�`�G�ۺ2}��y���Ei��Aw�?mj�{�Cv��C��r�re|�_���I_�A��p�C�����]w���N_{�i�d�¼����Z�S�����ǐ�k�d*�_�=���V.�.�?��ۺ�޶D_8��\�ZG����tƿ�ޟ_�h�������ԭ�9"z{.sl<:���z;��{�H�����a�(h�bN7Ѻ�Lf�u��<�|��>��p�DBIHs��H⶟��T���}v�x݉�s8�^�Q���	z8ç�|���|VeDtsu����A7=��7|����Y]ϓŸY�X��K����/:y[�	(Vw^�s�^oճHKP;�������G�?G����G�aY���U�>�5�O�q�kT=����J����D�җ���F]�/�20}���{��Fa��/��AA_zɳ����9C�����Ka��%�f���&�w�ӳ�]y4�x�n�N;||��o��v�����Q{���)G����=tj�"�P��l94ζ����� ������Ng�s&��'������e��@8(U�ro�Yx�{J��-.�����~���D���X����?�Z�X��Wu������g�so^��߻b	�7��!���'9���ř���V��g��<��~��謝<��o�<���-{H��խ¿;���Y�S�g�,�g�|���>��f�S��>�};c�%t��G]?��EA:�����L����vq =j�OqCH!m�r�Ǥ����i}}X\�ӧ�	��4��C�sq��	�Y8�$O|�j��Ou���V�)�j����fύ;8������WO����m�w�*7Ų����D���!O~W�eue��(@_��X�,��\���p]�Ĩȃ�r���>ͣ��3Ŭkz���>v����~���~�1���y%���u+yJ?�\�����q�a?���/J|§�����Y����׌��q2��_��0�eo�x
1z�>�C��6�p�ޑҳ<��_������Y=H�a�/�g��ʄB�T|7.�3DS]��}��E���p%���t���"�ew��=;G��^$I�V��}ޭchn^=��x;�Adڭz{���)��cT{�Cz,-���j�l�NY�?���8�1�!�*G-9��L~{_�A�=9]%��\�d~U����}��c�n/����?�_{y1�E/'�<Lc�o	�^��I�/9I�0���'k�M{���}G���������
�s�xD�i�o�񱡘��1� �^\���W{�����ݴ@��re�nOy��
�����'���f�=+J�Y��G{Gb��^���ۋ��Ï`��	̝�Eg`��ڷ�:�8�'����0
�d��C� m�O��\Ʋ��g�I�z�'�aϸ�-�R�ĥٞg�~*�=|�
�B/�L��N���&Ҵ��p��a���"��`5qC�wZ
�jom��;�)E�G�2ϋ^�ƃ����G҈o��|ƶwy/��L/��w:�ƫ���C�u`��3�N��U[��X�ZT����}l��pë<c]�%��/V��}���oڻ���Nsߪ�q6�:$�e��6VJ2ri�x#Hy.V=Zp���<�S�b���%9~卂"y�WM�%#Ok�̝�hh��Ѳt���vt�b,V�!�l<b�G�2��:��
S`��HeE�� �^sq�C@��~�Z5��<��r8g�k&�>��S�
�J$�QI����,��ϣ�ɧ
���\��S�$_��7Zjƅ@Bdz��A��
q�{K��\�xT�\3K�����G%dv�G-̃k�*:�vŝd�e@�c��Ԭ4�P�_�],FX�Y���빆a��:)�Y��s|C��׷�`���>�wEON��1�Պ4E��*�ko���e*'g�m�壯kDl��<˞�[�ok0�d�N��~��t�k+wv0�uEq�0�6N��Eo���k�C|�úrx"4گ=���ʳ��_u��]�u�+6���|��ë��qb�3�Й�A\{pn{����� ����6F�z|H��TL��50�)�1����	ɗ^z����iO�a?w�NUמK8w�Ž�����/<}¿���->������.�E�ƎOeǖ;kL���<叇�O��2y��YoK<��gw����j"���;��trua��v� |�/yBfɹ�g�Ùl�W�)>�}�������I��*��6����<�j��'�ɓ~��w�5��I$O��
�u���]�C��3�ދ-��!V�r��{�L�͸q��I~�B��eN�_/�=��,>K_�?��s�~[[.��Wv��z��D�T�綧��S��s�_/��j��������ͥ����ϴ���1���������7[p����k�í����
��yG(�>�J����M����8�O̻��j���
�&��ȯ��-y��O�5����=j|�U�,g��I�t�������~����V�/|�m"����^ؿ�M�5?�Φ2kw��o�ko_'U�G��_���4�~!�X�&y�;:�L%����ɳr�I�([������O��n���9[�,=FϞe�|����k>�=g�ٳY�n�&m/1�>���:y��蘤��Q��w֨�+>����֮���!�w�^�\��ҿ��}-��kq/>�|��p֯(-�fn%?m/�g�)|�YNx�[��{sI�m�t�~{�䷷>�
Ù<��2��ߞ�S�o�/�Yk��o��v3���Mz��(l�;��M_!ӿɿ�q�\�l��+7m.�N�t�KM�i*���/7�Vd���O� �F[�E����CV�W�+�A�ݴiU�<�/��ɳ���nK_�e�r#�I׾��r�on�������E����%���~�@T���\� ��|��Ԗ������'�ホO;d��[L��^Q�r��M�y�7[��]��t=�x��Rv�xc�ο���f��m��5>7M_��u|������ϵ��^�����<���!ω���г=r��9��OL���=��]�>�Λ�l�3y<�������)Gp��u~d�O.)}�(����W�<�59�o����W��'9�������=�_\��.%��ڻ�n󯆚����l�p��r�6��O8�/���ƍ����w)~|���+�j(>w��ɓI�_8wV��&�2帯��`����T��q����(��6N�I��kz��f?��)|��z8��4Clz�?`j�#Ѳg#
�ټ���o�#
�\��C��z�51�J�;����X�G��a�7��e�8����[~���i�� ���Ӌ�\a�|��de5�=������S�ڕ�ӗ��p�s��;f��[6��M9zw�媪���X����،u��o<v���^�L��3>��o�	nq�q�֋�ko�n����q�C�X ���`5|��eq2>{#=�~�N�O��s���V�ڳ�\+j��>����g��;*���sP �zK749R�.�����aJ��Q�"�wxH�\���^��28cS���~��y��|�����45�Uyz�d�ݺD~�Otk��ϱU/�.�_�S|�/󌿘�8�ẞɢ��~�4h����B�_����?N[���gU4_Ӗ����+�^Z�����~e��I���K|~X�3r	!�%}Eߺ_��w�(�kr&>kl�7��B����]�W���5=�v�[Gj<�a�*��uJ��5���=CJ���u��Ty���G'O�>�?�R������伮�n\��q�������_����,z�(>"���<�=�x���T�Tb�'�Ġ>�u���Y�����@-���
��:��M5|ʎ�v�	}]��)��zZ����7+�}j�����
%O��ye�N<����X8|9�|8�@U�r��	=��rz�%}�~6@���po7���a�E�$e��s�u�yfW�)>t���w�9�&p:�ڷ������'`�Ŵ(�MZ�$�^����̞޾|_e���ˉ?�/p�����WD�����FxB��$l�']�ZN��B�C����G[��jhШ���s��8�/�c�_�z 븱+FF�*���K�����W�-n�w��1�Qf��]{Q��K���0)�0���2�ŧ�y9�lz9�]���~\���YU�<�N���ܭ�����9_輢�6�qS��B��
���ȡ��
�-��rS	��i"����7^R�=�_����}�$�-z��L��}��^�oѸ�R{�9��#z���s�;���R��w7����F�=x�(>��ó뀯���g?[7}���	����-�e�7�g��=�kA��Ԙa�p�@��`�~�l2[@���)�'yt�_q�d�N�'||��y���/Ͼ{�~�ɛ
%[�ޓ��"���1���&q��a`/[J�Z�v�{���
�:����C����<��O�ԣO�\~�m�E BĀ��'�^���N���h�O��h�����I���'�A/�Na&��
��ǃb�}���+/g���;sb����%E{��7�R��ؙw�E{dk5u�G�v�4����q�G5seAT�
�����&��3�CCdU5���O���JD/г d Q��aJD��<AB�����IL�!��>��~j
3[D�#UfC�Zw�`|x�xNy�7y��3�	�W D�K�I�k��xe�:�Ǝ�V�������#a�yttj;+���:�t��c�E��7.v��@@����pC�RuN��ە�D9{38*gf�`�4�B!iiؾM��
`��+�4����t�Myg�x*U
>�k���fD�s�$�@)�=~�|�^{�p��v�Dc� �}+��EX2�
"��i�<Յ	����U��LM��S�4����z�Vq/�ڑR���Qg�`�~v�|�tō��
��a�$�_e-�n�'k
��KLj�K`�u�M�K�̰�5��zE�bJt�$'��xu�:7y�􅵀�)V�B��v')�axZJ��}nt����:��E����R%Oy�A:p��9�A�O?���KFȮ�=��;y�i�>ъ��!}�Ga�o���m�}��s��p��ƙ��6Fw~��T@lM]�5yD�;>�K���^�9!>�ۚ2�ŧߏO��d�ړ=/^�E�R�S���Ѡ0o�s����3y>~Rƹ&�͢/�'|ű'p��UN��:�#>�9���X�*|�ٽE�֮:���ޯ�+y�~��7|�U��/������v�e��C_��I�B,����i?�O_��Β�;�֔��3w�>��
� e�0�^��D��d�(ÿPԞ�P�����^ZR,y�0}5�MNv�~���^E��C�}�J�ἱE���ʹ�� �]��K����G�<z|�����a���Y�G��w嫷����K��;Lj��!��?�֛1��G-��������ip�
���ֽ&��/��'}��4(�H����y(1&{�KH�Յ��F���nǜ��,�ɥw�|G�pFw.���Ԝ���]�����{��=;�/���Wz�<�y����\��W� >�>��z�5;L/��տ���
�`#Έ����y�d?�S1��f6�Ky�c?]���0���f��i��n-F�<�:F\��끉b(ް1y���^����1�\��1rIwe��{��<JsS����{���d����s����Hb�̿>4ntW㡗NbK�S� *6>�D2��@`%��G����|��Úɉ�;�:H��^�|�J�S��nÓ��.���У��Yy��g!Ղ.�З�\�=7>�u�1��1>F|T%~��u[�{�	孫��|ſ��?v��`	�ә�nw��������aX�Z���s_N�8���K:|��7�`(����Z�o�P���sN�;�4�i�{G��	g��t*�C�EA�>�7۽�d����WQw���s.dR�h1��	����;�9�W8a��]u.i��=˩�s�N/�
t��R�:��ݩ���i�j�ש�ɓ_���
i�����A�vU'��{w"y8�$����%�8�8�]�A��V���{�{Q&{���2��zLJ�5����+9��X���lȉ�Ş�r=<��/�<鼢k�8�N�ы?Zf]�j�� tC��k|�B8�yh��tOЮ�C����쭘ΥX��7{��=�/{�zf�-{�gM.�孅���Ʋ|�z�g�0{� �H�b���p���Ň����;:;W}����E�B
7�<�����w�	�۸BG�4�:�By9.�v{�wv������{P��p7�t�]|�0';QR�*��b�+>�k����;��[���b?�Y�aψ**1�/�|�o�gȗ��<�Y��C���n�3{�|߳kZ9�V�>�ŋjw���������&�������G�<���G�b|�stU�w�}6R�g��W��+{��){`?����Nx_�9y�sξ�9���g�C�Ӝދ����?#��=\7�Uo�p޻F���))��Ur������=�7���G|�0�󜶠l����U�K��>N�aF[ .�H #:���
�s�0f�2 7Q���9�z�����h��<͹��5�^�b��H�9�=�'>
#;�����^4�M��!p�v��9��*�'O��v>�U-�U���]�cR�Cs僮c�e��h�a�9�;R�k{9ʇ��+|RV�.â������+t}�Λ���ڥ7�kW�X�r�J/�x�����A�<���}̅���sb�S��3[�]�ӹ�����tn+�A>��{���R���!j�O��z+�&�d?����aRy� ��bK~M���s|�s�=\�$&~�N�W���>p����������a�'r���Kiv����X��w��<�H���>����E|vS~�<�ъ�Hy�����7�Z��I)���g%�I��Y΍"�!!�=�W����{���[Kgى�O��� ����ʞ�N,�<�_�����zG���CB�*�ɺ��8Ͽ4y|�q��(f)����Pa�(�(�������Ex�5%���o�]\
��O�0;q�*
A,ye�D
_��m��,Π�ӷ�k�B���u`W�9S�o�?ڻ
f�1E�-�/�l�ck�'-"�Kw)�ڕ=��u�E+ҽ�QJ�1�b�?�����*�	S��u���G�M>9��*�ޭ���a�.>������Z٢L1)>T/}�G�%�>�^�8|p�2�䤯[�@���*˱��%:o����Z���~�#�Os��W��\���/����r��y.��ӥ�ɟ�(\{���_��I�������o�9zz)�#⃎O����R\RIz"i׹_\��!�O_*�?QL�:�ģ��A�L���KN�縕w�&�b+Eֳ|�ɦ>"u>���Z�Y��]K�A�*�:����)W!���� ���smW��<������<8�]�c����|�f��L�©]��o�U��z�&9�9\����T���
�'�w�ŭ]���\���\��'*�O����og��>�����H���D�%>��#4�Y1����_��������Z�����V���:�����F���Σ�S��V���;F�[��[ߘ��������^hG�3�<��G�>pC�^A\����4�T�Цz��(��!(�[�^�'g9.'�:���d�/�K���_g�Q`Bf%֒_��v��s�H�%��<
�eÊ��zsң4�/�P��hA��t�7}q�|Ч�c�?A��j�E�b?�8kN��"/�79���;TDZ�fhhzf��)4��X�x���t�G�~�\�rb�
�#���RΊ�kJ��̬�I��n�ʷ��! bkl��e�XL�����eA[b��b����
`DL�W�"8DGJ�/�D.�a?�Ze��t
���+��N��=��N��D��B6N\�Ȯ���EEp^���,mG�x�h�oj�U[��{�v������!�ׄ�(O_�/�x:,6=�_�N򸛉?�w)���r<��e3��Iy+[�<����~4O�co�\��~�J02F�'���BK��	9���'5W�K�Ez�'yw�x����&�3^?�s�<O���ڕ<��^|0e��G�z�;!ʹ���=��|^�d���?|�՛=O������x�%}�;?::��]_�A?�7Gzv�<�����Euӗ:��3;<\���E��˃�����l�쇜=c�i�W��<�.yjo�����,�cǞ<�<�F�'��/�%Ι^_n���<Ĺُ���I��0|���ֈ��5#{��Rv�7�Gp��
�����3=�� y�C���U.��Sg� ��G�����䡪�V�%���y��<J�{����ww����^Z|�O`ϙC��(�&���N��T�`?����,|nsu{ު�<�g|��E�#%|:Ŋ���>�~'����ʇω3.���5Ï�:\����L�O_��_����ˁ�1})E'���>yj{�<F�Sz�}�A�!gwuS�P��y�\L#g8�[���=|�[jY��{9.�Ny��3y��i9wS|�J��$>�)]9y�
8O�+/}*�N/�]�|2�^A8���9��O��9���$;8�n8+v����O_���evX��Ï:Ҷ���^t���9�U��O|�_��_��Ur&O�s�L���u"S7nQ~8����?'��O��ŞO���	5t�=�,����P��)!���)���O?�����C�'��Wy�R�׮f�+;�"?�~���_�/�3>���=�9;D/�a:��.�d��+;�<{�'�_���C���A��2S��L��-|ȹ���;7�Cz��׏�m�������~��>��7|�w��M��L��|ztޑw�O��dHy�>�{>�Ջ�e�ݿ�g3)��7̪��`��/<�þ���]��6���Qo��"'�[��+g,ۋ����ɏ�<\6�U>��������o�����1+2�v�([�y��^�@���8<���H_���5{����y��c0k�8o/���ǿ��!>$�?�^���CǾ���K�Ӟw|�1�����E�s�����;z�E��zK�n	�خ�L�d�����4�V�X����A�O�(��� ڛF�?>�+�A����6�t^�D��_�n��R���-�V��|����#q$?U�W�g�x&Om�'�Ӌ��s�g9*�O�ի|8�O_��d��s�k��~���I��<��%y���L�ren����'��n��G���G�u�O�3>�D�}�p�f��8{���I�����):���:W�6�k�2y��z��_��g?���p~6y(mv���C"x�.�D=��T�u���(��'��������ce���JDOw+ߛ����">�F�`|��)a.��S���!>p����>>�<�����T��������է���Ώ퇀�&.�'O��L_�?9;����5��Ƀ���.��N��'��wT�Ő���!K_���f����_%�`������Yſ��`?�a�Dfv��<8�y���r��v�\�򠇭�J�ν�oW�:�Uy������/rf���,E�y��!m5yR���r8��'�/
�/�E_�_����ع����?�����k�`|vN�Ջ���SΖ���g?�7���%(|v�F8/[z9.�L|���I�L���C�;p�s|*����<�ԋ8yv��5S�N8�z� �~���=�ޑozGW>|Ӽ�h-|IA�Ov����;{�g�|���|}?��c
��9FQ7|Lг�W�pov؝M������1�G���F{��ա+О���Ƀ�N�e�ݵ���at0���-�i������U�>sv���;DD�~�������+|��]���.�e�
�rH���ʞ]K��cFf�؝,����s�XY����B�[����Nsŧ�p9<ypA,!�����:}����W�&Ot�{������g��
u�ku�S��#�����k�5��w��<S9髜�����؞��O|��'{���d���T����ow(��UG���iu�
֮�?.�9.�<�a��wK�B��0ُ*�O���?�<�uȓ=�����B�r��{�W��ҡ�W��b����pF��{�->ť�S�;Qo^��\�ݩ�6l�f�Ѯ��<��Ʈ�������-���}�C
7|���(
��߼|E��F�<1���=�'}�w�f-4y��tq>�<�;�
�����ٮ�y{�X̮:'	>���B;�y���_0�/�����C�/�);<ۅO~ڑ����}q&?����yN�Bm<�É'�Fy�O-zn��=P��i(l`Z
,��3���8{^B���#�`��R��D�w����
��j��k���ş�BA��]�ʓ?��wq�8�79�����Q���1�ֱs�ّ?���'�Q=�</.��'��eo�����`�'�5A�y������<�
���>�\�}�]�?�����>܊��evH�_��<�ow�,����W��r���>);ߞ&υN��c���O�u�O�grZ��$��)���׏�W�?��}r�w�̩���s���O�^��{�ǂ��]�_��ˎ��G{�㳜���D�����/z8�9�����C~ޮ|�u��n�/�����<�p�_�'� zʕI����}|���ת�<%��uvȞ�w�/z�,�*Z闟tؒ��-�D�^�c2<�7���N�˕18���䑒����?�]�}�7��K�iO;�'|Y�tF�1��'��G�䡯�ݦ�i7;�I��]r^:�B����3��0�8��}�J�'��O<������-��emA�ŪQg�E���t�>ǻj���Sn�-�Q��ēݐS����qz6v�Kt��4���<���	�_g��mRyY<"��{	7k/y�i��r L���.H���X�Vё�&}�m��<MymU�I�^� �i��b����0ɶ��4���H����Ɇl���Lr
�ݑdO7f��D[,��Ф�-�^�ԫ����y8�]n]����9��gW+">�cs�r���)/�ԨJ�>	�s�>�=0-B�-|;K�(
g����!�=q:��R�l��2���uxz gy�ΘG���9�Ь�z�����S��Z�t�z�?����-�ۋ�VY����Z���2K�G7r�.V�ϙ���-���K_/=gVl�R���хe�G�w���b�g��bS{{u�ŧ�*|��ggyM^_�+��^�6�!?s��q��ђ�p�s;�+|J~����/j�Z
�e\�8��{/[�G���Rar�y���c
����	g��'�Ϳ{Vy�Q�ݲ,S���It||��r|��+J�Ҡ]�$���k��A��v~f��C_�а�On�EXC� ��ә��'>�z�M�Ё?������?(�/f�T�3�۳O�,�9?�y��ζ��?���i���~{��\|8y����8F��?>;�}|TJ��#��X4BUik�@�2�u��ࣼ�p^���sy��A��L��zO/��*O{�O��S��O��������'>����3|������O���pnF����+�}vf;����L^���~�w��g�w���'Gv�X_�N����s|�G)�4�iq3y8���0|��g�@F�=+�]��8���Ƕ�>y�L��G�]�j>>�@@O�ÙS�*>�"V�<De��o-��UϞ��½���ZB��"���]e�������Q�M��v�<��]�=z��m�WT
�/_�^��°�+��
�G{}v�:c&*��$O������!���Q��.��������y8`(w
ΈZ5>]ȅ�$O8��G�����k��_���5>����<ş+��V�C�|���p���;|T1��GT��~}���*ЉnP�
���ᓾ�|��#�U�\B�*�٭��׻��S��s��?&�X$vd�i�Eb̏$м|�¸�'yn�藿��:FM��&=�M6��N��lN}�׎w?�����
�A���+q��<|��?�!Yy�
�i���^���U�HəF��0;'a*�����e�?�e��w7"�5�)y�'<�s��/���R�����է���C_>�S"}�i^�}����Rˆ
O_9F�7���%�F�N�|���+����3����pc�:"ژ�Ρ�17B�O�XED�
=�d��S���؛�r/HX�>~��W�U�<xƧ=i�
CD������"v*"V���+�At�c���<���~�����Wߨ��0a���wش]�ȓ������`����Ka�A$����C��3>�2���w�ӑ��=������g||��څ�}���}i>W��#ՇO�k"U"�*���z���AW�N��)>c/FJ�Z�lVX�T>�g�B���Y��/��Θ�n������e��;yı�|�SS�������/|�3J�jڵW�W��ϓ':#A�X�SnA�Uʮ�����m�_
�O�>L�鱸A����e�ƙ�f���2е����3B���Z��]������U8g���έQx��kܘ��?*>���ܚ����h��!�4_{��
��O|���)G!�������뿂�c��O�r降��q�r>P�z�ξ�̞=��x�߼`�<��~�vm~��k|��I/������m��~<�ӻ���O����w��L_�]a�%��Ɔe���~�ҙ��Y�P��L�ɳ~���;�kT~����E��QNL�����g[�k�U��4�+��~�Kz�C�z�~���=�3kt�����#@�xg���	l�[*80pco�O�?s�s�0N&�}�
ԕ��*{�̿jW��\ej��~���w�����|:��#|�ʧ���n���K���G���BW=<�#�>l��^o0����\wb�+�?;Ag=���(#t��Ӈ�����s�|'_��~j2�N_W�����ѓ|�����Mڃ�Z׮��c�5�m|���.;�g�D&J�?y��Z���s�p��y�O8#����� �	�r����|S��'y�\���EtU_�G��玏��랮9�i�U_��>�o�||�aJO|�JN�\�U�G��v�Jy�50���U����k�0��˓A��^wɮ�.-qkוbqC~�W�t���څBb�u6���+�_�+����7|f?��+>��)FWε�KA�Q�do�a��d�cv��y�.M(��.���������<m�Xy���[�uZqF�QG@�v�����@��eS.B+{��F�ϊ�ӹ2C/>TYMt�ΈJ�������G'�U���ڕ�Ξ�k8y0�3n�~ڏ��&������W�9��_Ƕ��b��u��=��F~�9��n(t�Wu��W=ɿ;e��W�`(C5@!$<�cRy(:=>j\n�r7eƹ~+��f;y�K�'>(��9g��/7t��U�PI�ӻP��������Wuv�c��f�wr���@�ƇG�CAy�7*�@�/�	ޖ+sIv59�gE�^􋾜���g�2�wn��n��9��Ƨ7�(z��޲��Ү�a����+�X��(I�4��1��S��+o!5�`�+^3�V�����==�,g�W��t�z/9"����Ɉ~�W��o�hk����vج�M�^��k� �]@�<|*/a�1=>���c�C/�;���K�O�'W>��w�/�8|~��1�c|:c��k�Ry��zq-�)��|�|����{��֪�+���+��1���769����qas���ޒ8(�4)�dt����m��\gy�$��q�=�ѵ����i*zz�J�ī�Q�Ju�`v�O��|�\��v΍�,MEװ��U��V����c|�0��B�����b��v��L�_��|�\�=;��)ף7����'�]�]8�����}0���ZHU_�zz`��+�I[#�g����f��)̑?}>���Rr�;�L>b4�6������NlZO,0��>�]'*��ϡ�Wʋ��˅����ɘ}�����^���4>���~;��+vh,yΎ�_I?�]Hu�����<���SU�o���o%���O�V}DkD�@�s��ܑ�zJ��B�k���b>ʛ���K��DX~�wrsii�e�DCez�{Os�wy����lQ#nL����W>��������ر��1z����'V�c��<k�<n@�|���+y֮ɣ�jB8>����QZ1؟<�C���}��n@�kW����W�ֶ���<�rnLJ���%�G�������7q�}Տ噾b>}]�b��cy��_�/G��<��uQ��`�.��᳽=UG�'��s��{��N_�`��*���7vL_�B���_�x��$�?��1>�t�l+w��qN��e�(��v���
k����g�ѕ���S��k��޿����w�'�?֗���Y�T���n�w�
��(I����W�aJr~�~<�~}7��c|����^�rb&O�&�ߵ��qhY��Z�G��[ý�*�8_��n�]�L���J�N��3y��㿟ࣰ3�w�M����D�gs���'b8�]W|һ������c?��p��<�ǡc0���'(*�`?��m��$��7>󋫾�e�h����u���A����Y����Ìvz�乴�R�a��Wa�]Y�x�u	�R�I#D?)[T��>���;#%�5���jI�9�N��\j!���z�a8��ߪ\yd��q�.Ĥ~��~����e�<������k|��c��;������S۞P�M��]��5�P��&O*P���jW8��{�9&�/��~n�:�쓇��.
���I&>5���۷o�c�c�
��õk�^��ǧ8Vg��E��6꾥��:�+\�v������N��Jj��~�7���S��nZ��������q�z�ٞ�q����c���ڮű�+��Ur�ű���{v�8>���_׽�ɳ~0�����ѯ���������c?�ypx���窯����]�1�W}����Y��N���q���1>��_8��:�-�rk܍=�ܮ�~y8ߍ�֟>��w񧜌]D��).�����S��!O�T�Z��Y����A�_�0Wy�t����A�����bN|�_<���qf��2��q����/�'{~�_�o�s�w��Y��ܮg??�n���Z�׺�x��x��xu��!�$�ƫ��)�'}]�ש���D��R �<>���<��/Ϟ�KI?��w6_��i�(���/�s��g{�|9o���'��C�M{�<��g�b�l�����|7����zb�5������3�m~�~'n�]�7>w�	����?������O|����O�����ל��Jw�Q�q~�Ζ=GL��.�o����O��\�<��Ю�	�;{��'��n]�z&�߭�M���=c�~���S����3��؞�ڋc������Ts��ͧ���x]}��|0����	w����g��/� �ks}%'��c:�;en�2i@����T�p~��=�L���i=�=���z>>S��.��5d8?=�rX�ϳ���=�.�,�B��w�
}�u;���.K�__	#�ĉ��� J�;�Q7��/�g����L�o��\s}f?�W�1g�݅t�5Vy_$ϡDx�rwW���dl��:>��K�.�_Ƀ�x����eWɯ
�;�*y��'�<;�>��|�js�O��g�������=�<���r��g�������{Uv�h��S���K��[�|ӿ*�Y��_����`ϵk�k����yl�~�<Q�8�޻z�G>�2}]���\3W>��v8���UE�S���s�s��u8���څ���8>=�����u'���ť+n�����q�:��<���j�7�5�gr�dC�f4w��S�
Ԯ�C�Q���X
��
�rIG�<�W8��I���E�}�b��.�>���?�|�O_�5b�����^^u���K���\f���99�W{��~��X�%�D�[�y�����9��|j�Y����q�7�^���+��<���d	�!�9!
���+,-B��%4AwQu�/���A?�b�����D�,��.�q^ΙO�G8��ǐğ�K97�|�c�H5O>}���J�o�3Dgq���<�]'�����<��XI�S�}%���v�S���c�+��8�~}�W
�H*��ø/�t6��7^ږӗS�Q���SgM�u�,ԋ���5m�D��gy��t�����A�ڝ�r��8��B�Ζ�A���W�]BO�;�{����q+����*/�~g�өϳ���u'&
����/��`�
aJ�%,����\�+D�Mw��HP�n�c�O�%R${�;n�;��y'2"��J[7�����޼M�Y<��z�kwN�¾�/!��ȧ���4��C�cm��ER�L'�A/_��ş�NS�]��]���軷�pI8�����~�K9�5
��3W�l��{0��>��<�E� �.���v�A��iTm4�	zY��/$M����,���]�D|�y��UMu���sw���a��G����|d5�ݡ��][�r��i�oA/Xh���Ih��?�2�^�{杇<�+|�7�˗���ۃ��r��cl��=>&[�W�h')���*����7�'�2|����Q_��rW|��z^�zM���>�yg-��K(y�Q/|���t�>�S����i�b�ؽ�����gsT�g��m�{��P����+�!�a?���&�_��a:y��{-�$��F�>��q�����d]��h|�Q{��8��u��{$��7fb?����cF"�Äl@ ^��OP}�ϵ���������O��]����?��Qܠ���	�Ι�d|��9m�
��˃�_�5}����@z�p�%O��,���}��Cwr2��Óϥ]�Tx.�\�n�Oo8g����g8���S����>pN������|z�gyj����ӹ5ל�����~�c��E����)�=|�k�ɹ�&�~���}�o���#������ �B���{��Cqc���r+����'�V ��U�P�޳<�۝_(�^*�<>&x�W�(<>�����Bw���T�u�]L����կ���~�$�"I8������>�a�Ù��a�>��s�0���Oﱡ+�{ħ��v���ş�L��Y�/$�$z|��Þ��M_��E�a�W>ك5���_�ϻ��v�j�S�Qr{o�>���������y^ݝ��JTt����~�^�B�So}H�:���N)V��/~������?J�'ͮ{8�!�z��{�;>/^�@��z��g�����t}����IJ���\�����?��_��'D��Nq�I�|�M��s���L�ٝ����?��w�yף�*y�k�wВ��>����_W����������u�W��8��5���+�y����(�DLy#��~�]G?eb�X�n��8w1��sn���S=;���hӰ��%��
|Џ�#����m8���?��-�\�ȍV@y��f�u���&����8�J���0̮�U��L�>�_��ޓ���"?��o@Z@h�3��Cf��{�W;L���'��rb޾���3�;�1r���l����	|��z|��>��Ӄ<�I_��E��z]aⓜѓ���೸�'.��]Wy����o޼+Q���1���-���	����<�Wq�j?�Qӗo�99�Oz��]>WW���K�+y�g��>˵���U:�h��<���ӿ��������!�5>��/�>�]��[�����8����ͮ���'?�@h�'�!��<����~ʿ�|��;�6]�ӻP�ߟ��/�q�����u��~�g�1z1@S/y�Wzg��pfW��>o��]lW>ڥ|�\�Y(y�aWk/y���<��籿�g�֤�����rO)]���5g���K|�E9�I蘝[�/�	���|����g���<%|��'|���\�<Tn�\��?�8p{�Kә<�o9F�Ï�|ݛ�~��]���Zp�����]ӻ~�@&���=��~?��V���x�8_��[`i�{��Α�駟Z����9���De��Η�/Z-O�Nu>�s8�]���y��R���A�wwX�q������@l�B�w�*��q.N�˭�O���P�s�s�p���I�p�ݮY�#w���0�_ݵw�H����/����ݽ?��噄�>tW>f����rPjW�g�ۣ��՞븵w�����
֚��㚋ֹ��w���q�"9�%P�P��v]�t�+|��d���~U������W��ԯ��|M��ZZ.2h|,�\�$�~Ɓ��^:�G���"�S����'>��sA�����W��xjr�s\�O8k/z�[k���Я��s�|:?U`;�,N�+_1t�L_�c�~'�z�'g���=�I_�m��_�r����B�䙾��~җ��W}��I���7�9�ѓ�W��.�.w\c}z�n��O~q� (�<�ϓg�3|Jk�_�g���U�fk�U��~�Ga���''}��,H��,�ӻ�Ƃ��۔^�|�.k��p�SB~��p+Jw:>�v-㳻{�@m���u{�R:���8F��]��րUX�'��.�m���ve���n�V����(����;�(q�p�]]칸�~
��.��g�i�":g��6n����bwp�w�O
y4���;ޗGD��>��Up�O8�6{����Ƀg�9'qfW���>{FgT�;}����d;��룱�
4_9�UEɿ�t�g���o��.n4�!�rwo�1*>���:%}e'�G����Cx�^���#�Ξ�$�5Wo|�5��b�D�I��Ǽ�b"��3�ۅ���/���I�r���h��l�M�C)�3y�ǿsw���L��S:Et

4��^���C�|������I�.��<�>��2>��>i�_����*��3ϻ�O��7��vw��%n+���/D�Kt��˷��r�Fw�@����w
!�se�3y�O�zH��kN�{ܝ�A�o�smׅO��EX=A�4!t�����]p#?]��o"+C�Dt�K�\�<���}*��i�wz�I/��\4r2���C�3 ����*ϳ��v�]E��+qD�>��w�]���G�'�G�*-CW��<��|wKM�PxzH�]i��橼*����_ �B.f>N~�d���p��)����x�Uj�&{P��e�>��8�v�%�$||�N���#Q��7�?�˔�2��{������B�ն��۳OD��?o5�O��u�P�5�V����*v$��y+I��1rg	YToV���u��DN@y��o���s$w9���3>k�Xms�`��;��&y�~f��e�y�p�͡� E?�Y�����P���c�/^��3�}�?t�:�.��b`�?y��wD
���9ׅ���������b����0w�z��
�ԉ�F5�O�F��Ry��aW�re�>l���JP�����oЮ��Yy>�_4@y6D�b^K%L�i$��W*I�Ѱ�֕��R�Й��V���!�Iy|��&����{��"|�ͩw�O���C����Ʋ�UA��$O�R��~�Д�r
�<�S��|l�Y%��s��� ��"O|C$R͵}f*��a�>�"�g�T
䉮^��!s�������=�	�(�{&js�~>vI�a���s����u𿵗LHH�W��QΦ���3{�=y�5���p�Ow���
��\��s*���l����9:<�xw����CmX����R�P��Q���
x�W����p�~�����5֬�����[L]�#ZKR�*���a��r��-�2^ݮ��1
���.n���
<�����A��k[�V�_����M����_o��<�ycS|��4I�WyL��C��A'�䷑>�Q�������G�nGh��P!��u
�X#8����E�p�����$y藾�3X�@�%l��A���D��#
`2y�s��"8$:��4~9R�=>�m��k8�?��g'��׋:09|�w8k/z����G��r���}]��X��'|��'��9����ݻw����臶&�{�3>�>�(w�I�
�C��?�1�d��gm��i퉝\�?:�p�7y���D/���2�;?L�Z�OTC���S���0��K��/�s���c�E_�!��ɓ���Z�������̯����rz��O9���M>�4�=��b8g����c��������u�����s�&�����{���7yX�HBC��ş�A,O���~1{���Ş��mg���"|�i�]���O��{_�����j���ь���+��c��W5Z�q�$%�.*�~�)�kz�"#E�d�SBϹ2"�<��ՙkr����%Q�8�/����36|�}|�����s�������E�H�o�ػҞ����у����<�00���T�q�z�|��p���W�6s$O�h�e��yȟ�Oy�f��H���᣿)n�[���5o���A�W?��&��}����d|�?%�~��1��o����'�ɥn�.`2Ë�I�^KO�a5ު+n�!��.ʴa�8�k��Ê���v��[�|z7����"�M�'�������|0�džtz��V���[|(�&ϡwv���g��r�q�yЗzu_7|^��Q>}���'��
��oF/�)��>��A>pK��y�����t�c"�N����W���"���b�ǧvyƇ<��kt�_�G�����%�|�<'�ыc�W>y�kQ#��+}%UN��p|�����㜾�&�O��O�0�L_k:�L���Ћ~	�L�kzL2l�7�/n�/t�<�;��rAx�p�Ő�<����-���'��_���(������F��ܯk��<?U6>��\�����I����a?�w�g�]�s��13��)T��LJyAq�١��?>F�~��������W8��T���ϝƇ<�U�S��Ǔ<譗Ň<w���O�pN������Ξ�v����>���١'�H9:�'>�a��;����_H�g��(�83}M8׮���ɯ�/�����j���=C��'O~Z�����?;jw�4%�]N��/w�zѯ��bR��sꝛr��'�K��޿�k�5!>���;��<���{��-��;R���צ>���G�;{��w����|��远?'�0��\sG��;�w{�ˡ�>G���/Ny�{v�����	b�r�b�J���{(s*�<�0y�{�;rw�zr���&�[��ޔ<I���Sf�`~>������:�k9m��諸�����
�U���<7�]��\���R\�U�ڹP
�#8z|֮�^����ae:>����cO�f�	�;�>�惯�3f���wR�.{�ݦ�B�rPڳ�Q_�
���5�����ϻH��K�v�ö��io�z�X�@O��I��&�m�N�C]�n|�?y�g���7�v姒�=w7��v����\�/:�#��no��QJ/�s��څI8��F/�=�ħ�B���.&�����0�+{�C��^�b%ხ;��UrFǓ<��;9�/�rGj�Ջ����z�����%������C�nz�N����ca�s�׳�|�kz����*O��:���/�O�^���C�naV�w�0�go�C��]���ޫ<6��z�3a|�G̖�͸5�D��>�v�;{��<;��^�ҩ,9�M����n��eKtO|�1>�DϞw�ew��,
��_�O�J��8��(����|�����\���������>�p�=_��3z9��e%Z���>����'�;_G�����\‡v��'z�ŷ�t�׮�8��zլ �?��O���K�re�8�E��_�y6�*��O�9<�V��b�D�����M<�'|_M~�~DH���~��e8������ܱ~��,~q4�v2�����(.Ň����pqR��80$����/������+�\��o�'���yv��)�윜�'ыkם�.n�����`�
DJ�?�����3�=&O~4{�ӻ��p��w7;�皾�s�7�H�ϧ�pN�C_�˞��ᖾ^X�	t��G�<�����ϱKt�c�`z}�������X�KL�@�**G����k��r�����7:����t��7a�t��h��k�
�􎮝᯼e!|*�����?&Ϲй������{f�>�
���MG8`��=�`�O�B'O�8,��&����ʣ���Cz	��K�4�ǧ\�a.ի�[n��2��GD��H���;�jW��ߞ��W�-�n<F_����䤇�\��Xëヨ/�ϡ�S~�cV�q5������S_dK�/_�OP��~FY��`}���䴰���<}�����Y��ȵ:��.z��^s�O�r�8�L_�b��ϭ��;��<��̟��jmR�xgs:��,D�ի$�	��ٯ�Y�:�I��i���v��"��X������<�F�t`��댯�^��,�!����ۜ�5�m��~���k����'�Ta��t��5��O/�!�Zm�
O(�_���j���?ȡ��ތ�3.[�ʋŶ-�?��Z6�=0�ߟ/��*}�5�i�b?|ʯ������-�2a?O`.�ՋW�`KpK��>O�C'�s�t��ϓC {u/�}�cKB{�Py����K�9'Ν�&1H����xZ�Cd�*�./<Et�=����i�(�t�t�{۽p�����C׿2^Uk��O��/�[�9:6��W�56�����5�q��ŭ������c��R\ϻǴ��C��o߰w��!f�c�WoN#>p�.��௽�ZT�{3�F���5E��/
Њ����k�������v����4��Խ�R_qt�2f{{>���M�A���:�>��G,KÔC茶�I�R�dB?1�`̖/I�3}���}���޲?#��"�=˽P意�����U��P����vQg��޵&rA�d�,���+�`��#�R���7�t���_q���=O���!�!��� ����D�p�e�(�������d��|����aoxG_ٹgw�{v�ӽ�~R�A��ƞfw�_�Pa��'��<�9;ߙ̐z�����ߙ���]HU�'bk��w��O|S����M3�Cz�s�\���s�W�����>���Z۪]���e�B�
U2���!�^�4i���<��g�d?�wl��y��;��D_��g�Kt����㓾�z�zī�;�D��mW/�~��G���3y^�y����<�ꜞ���ӏ�0��������N
}�I�gy��s��]p���v�zŧs2�&��y�V��݉��v)��ޕ�@���>�˄����ا�J�d��S<���ݏ?jO��+������Z ��y��U1���?���p�O�{�'�Ѯf��S��_E�]o޽�z|Z�=��
�����
�c<���<�y�ӏ��\t{���z���k�鼖���A��r�SΦy�J_�,�P��O{����/�y�\v�=;��K�<�~�I���x�gW-�n��v5L�BN�O�'}�֣R�{��\��KWy�����=����D�{����a�ӫB��~�>�������5�g�_�Tq,{8E}�S9�f��;;�?���L��₌h_]䁏#������c��!��#�ϯ�z߽��I���?�̟�A
��Ky�@3)�ٿ�厜z�������W���QW���c��E�p|�o��������~�_>�>ʿ�u�'O�5�e|�8l��ڥ|��^�9����d��8�,)>�_�
x
�x�)}��wހ����
���B��~|gX+��h�w{�$�6)S]|��'�D����Q��*)k��7���6]�?�/?ۇ�һ0;�@�4j��䉏H�
?	�rb���W�����������{�'r��{�����>����]��1���~<�E���L_�����9�����j?]O�^��?�5��<������j��s�.Ԯ��o|L����<HS{��~O�c~�=��M�����&OYv�kW��J1UԮ������z�+|��[z�����k��z��|�C������f}��Vo�,n!��5|�����dtK�����]�w��^��ϴ����9�<����/�k��#;�G����'����g# �f���r�����~ؕ55��O|�S��I�z�Z����I|>R:�1���Wq,{��u��޴Ka�D���;���~č�Ƈ�)��㯰�νh�L��!�e�
{�|��I�����^�P��/�̮�/~T��3��I��gwd��kt��� �������ծ;���^.�Y��8�w��u���/��,I��v�!�Ϳ��91�kql���ϳ��-4�b��Q�W�>W��=�F}�S���qq#}Y$����W���8�>�б��~��g�u�+>�A��֗�>�I�(_/�n%�=L�p�s����zwї�w�M|��������?;G��F��b���Ov����^������w�[��|����DkWg�{Q�9���7��x�3s�
!~��ʳ'���ʣ�#~v���;�i�u��y͞=w���^�ʨ��hׅ���9��a|4�ޘ��/�����O?�.�~�6&�w�vL�P֮�9:�¹��#`���t�洗lG��<;�@��˳]�ɚ�X�������|�x�~�.��~�+>U�^�s�/��Ξ
�ӗĂ��~>Ju�M��1��7�~mo{���/k��a��Έ���:��m	�;�C ��k��68wLz��3Ag?��&&�ȃ���k>�=&O~���e"��;?x�㿶��s�֮rG�GV�<$Z\�4�nBj���xڕO�B�_���s�'�>�O�����eoW|�2;�;o��*�C�W��ӗ��a�C,G��� "���qw�8�~�(W�y�WO/ڥ���Ut9+�����x�~9|H���A/>(���Bǧ�J_��]0a����'�|���{�������Է�9����X��u���.��;�wi`�x�/_�v�6���8��s�JY�c������t1-�%|ȃ+k>�t˝>�?T���/9�#��V�>�s�\�0�ΥN�� Z���p�ي8V���ޝ>�Ā|�[�!�*�#�:�膳��?����I�u(p���z�=����օ���B���n����wR��0�^��a'��E��jo�g����t�`�\��~�,�3���ο�S��)�_����O�R�I�D�/�u:z��J�E�jL_�v���s\S��7�ļǿXO��������(_����ⓜ���z�懒�\�D���-���Ix�C��w�W�v&���]ֈd3x�Gy��ٸ���ϸJ-��ӄ&=�����`o4��O�'PF�\��İN'��*��j[D��'�t��x�
�k��%yD#�l�,����N2�@��p�נ]��Iit�qՋn�y��չP�9p�����?�L��_���y�>y��zB/I����/��}���ի��]{�o/��Y��z��N/�U_��篿���z�?���E6�ʳsk�g��x��ؕ�'x���4�䏶�������_����]��Gy~Wc�q�ȉ9���]Z��t$���Q����l1�_��re�3�ځ�-�I�=r�W��>�l~S�4�
퐄����{�%�L_�<�
�J�YI���H�v̹�Q<��cF����T���8���<Z9��+�,Oe�\�*]�,{�i�I�?ύ��㸄��2��}����*���;�<u�����þ*���+X�+t��TFV���%���N�|=��Z,4h�)��ӯ�{B�m�=m�"W�
��}m��!�_]}�7$��	:T��MB��g���\��~5�~q���hy�O�_{=p �1�ڭ�Y���j��f���YK�6yy��`��
�6A������-0�����1���{�l�q}��qV<� �f�l�)?0��c�á�ĭ.��?��y���Ç_��&����׫���30��9��:(
tL�^�w�3V^�w��
�>#gn�Q��G�����
3��ڍa�pUyh*�L�:㼱�FZ�LŴ*�蕟<����������V�]3�g=&�c>�G�
���?yַ�sc��g�w�t�DO���M�|�cO�J��C#���O
W�q�ێ�mn�ُ�ͩ�<w��L~���8[�0X�]����C�Ƃ��1>����L������v�|
>�S�{l�>�Q���޲`�nW����Ů���O�YsĊ��L���&��+���4���_8�6��J_��s=S�?��<���eǧ�Z�CO�~~gϻ�>Q׮��,n�'�Ӝ�g��g���{��Ӗ�w�P
�+�L_��::V�	��=�>|��#�
���یvvn��|8�ު��|�W��ޔ5�!�ow���ڵH�=����k�ӷى�񩽳�o�Wt�
�A7���o>������+>�g����z��5��Ǔ�w��참1>�L�w�ߚ8y�O��6|<鷽���X{����3��]����dW��/&��~o/!����^�G_��}��������Ԯ�*>�'>�+���g��b�-��g�T~�d���<��<��O׮��̯���~b�k�+�xX��=_�h��gW�et|�3��:{N�ky�ԛ��U�+���/��U{'6�O���I��=�p>�çgw1�Dlσ4<�ٮ_~�?�X�ƽ���L"}M�T���矣���W�9z&zg?�=��e?�������U�Zk�٭b�c�y�\Oz���>�ŧ�,�Z�=�r�ڋ�e���+?>�p&O����3���{���7�������͞���3�c�<���A���-n_�"��3�]��.|�������(W}��
g2����Clo�]!��/�q�����${��k����7���� &��?���(P��G�x���)����;y��Y��t�^��Z~��>�q��S�h.�����l<�/�
�~0-o~1y���vU�:^͞��\ە����������c�?|�9�_�.��I�x�������s*�3|�꽎�3�œ�(���ǡq��
���.>_�e�L_����7����n�ē��?y�mq��Ϟ+?��||�/�gjoq5	�n�ԑE�v��q7����<.����J���+����}����/��-�u�=_��Ƈ�/�G�O���p��w�i<?�'�p�ѓgvx��G���������u|b��e�?|j��٭Z������u��k?��o����
���X8���Q
l���qo��y��v=�g���D��S6#�@��{�����>���� g�l�o���w���s�ko��vݭ;�wq#$g?�v�z��_��7�z�_l�S����p�g�e��w|*����k�`�L�o����_L��I~���0}��]�MN�ʏO��g��^�x���j��,�ſu��x����a����u���vE���ӴsY��<��	���K����e|�q��]��2eod?��`]�������\�Uc�7�[�<�q�����b�Nj8�Bۿ��G�s��jsw�k�\�N��;H���kWv��՞�֡��Yܾ����=�=�ԑD���|�����b�<;�Ao-��y�&����Q�?�[c$O/՟��|'y<�g�����7�\���{�o_�"Oq�<�맏��<�^B��qlM~���W��Εy����b�����n�5gk��.cc�����^�B��쪻�N=��]c@<�[�������zw�z!–R�Nϒkξ��p�|8���׮�~D{�7.Z��]3�O��8��L�=���|���}����+>�+0!����;ڕ/'v���Y�$߸k� ^�
;3/�߿���?y7f?�']��:���1���Ul����g�k�D�ű�/:����I��E��{�%}M�+nɓ#�u�ؓ�Z�ާ�AT�挞+��Y�0>�C8����C��]��(w��*��'|����Y�0�-��CfzO��)��+{�������'��^!\$ϋ�9��r�Yr)>��0|�?�G_�<K7q�"1���Y���Lj(��I�������pF�̂#[���o���h�d�NJ��bE~I7�	���Q5�zG,K����<�/��G7p��[��ѿ:���G�~
�7�}�k^U�`��o�sޟFmpϏ}7~!!G�N���#��b���O� |�G��/_m�Qˢ��m{�B�\
��"w�(W_�~Uy!TEҍ��9���w�nN��O:����_�J�ޘ[f�d$|��O5bN���y!�y��ъb�|�y	#$��B�ߧ�[rA`]��V���#w�C�lT����:HA���z�
I.�q�5p[�<����v\�tp�<R�$v�ע�"py��>���3?au~�RAQa��}\�#��{g�3�:�����N9��ś;�O�2�����x�H���4�����EW�''>��(ŝ�1�*��P���e[��q����v�/�Y�>�p4LC,sf}�q�\c�e&�s��#�=o��gn�*ec�sވ��@��罭R��ޅ(�Bg���.�(�u�o]��qH	��Уk�.�:��W7�`���*��/��]�}�'�}1-�Ջ.�y�[OZT���A�i?9�P���U>�G2yz��^�/�`.|d�/��ɞ�D
ˆ�ƈRk�G���'��v;�^${�����a�b����!/DjcZ8����b"%>���W��!ea�Q����w�����<��h�aT�>�bZ�1�LT�h�4p�H���c'
0*>���|�ݻ�L�\n��:69o=(=�߹�O[k~�ޏ�aC�u�V|���fV�E�s���/�����fQ�R���g+;ʳSD��=�]>p�Z���xD�9�jDѕg�p[y|>��@/�{�NOtJ��؛ �v�xL6���ჯ���EMNty�����#
�(�g?�쟝��o�?:S�"�à/8��p�|8�o8w7͖�؉�:b횾�c����v�-?����d]�B�����!'�'yC=�h��?�V��f�����s�!��z悜%�b|ğ��_g��Ʈ|�¿��&'<�3+>�AO�pN5�hWz�ڡ����Q�F��T/g�w��^B2�E�B�_��{��ï�
M�/B����� ��]ə_g�a�z�}�眮&����!�8&��:Ȇ�Ⓒ�_��!|�/����9�����5���7����g��~�3}���_��-.x���?z�i���H�	"|�~�5���|:�o9��GWr���T|�,|���>˽+��O���nI�t��_��g~4W;>�*�B��Ћ�sF�}5�L�Op�̼ �!N�y~�;�Paz��şх,�$�|N?zo0J�Bk�������m��W�)y~��S�"��_���b������Ώ�y_Iœ~D��o�u�ߵ;�ث���Wo_��l/䴇����rB/�'O|��-��@�_�~��e���|���o|�s�)��"V���{���}�3�=ę3�����7?�!?��������_nw����<���Ka�P��ϙR��x�)��`���-h>ΕA߲o��+q�,i�x�y���������߯�}��5{V<}���[|��$�y=�\y/�ϳ�=���|q���K��@�Х�����z�履~��� ��{��V;ç����x	�9�0X���iJ��Ӧ#�v|޼���5��?[�+�;>>�A�O��򆝶�|����#�kAt���/�N0A_BstV��lڲ�|��wg�r��G��c&�w�G������ǵ;��L�r��]��j�/ru�S�\��o�V��q~�#0�8{�shZ+	��}�G�<f*����k���-7�H�Az�G�[١_�;_��~������y�W^{�O�AY����@~����*v�s9L����zQ�_~�M�O����M�5�<�q�A:� ���7���OM��+Q�%���!�^�*_�$/�Wy��^��I_T�n&~t��/�Ew05dʯ��?�_��N���^C����ɉ�T���~�|�~a���p]v�~:'��d����.�	ve9	e_%O�0���|��	ϭcdϦo�T���G*��aq&�)(�� cz�|v8W-���$>�E���s�d���M���/���E���@u�ɳ�������3>��?n�δ�9@I��x<��d�;�q��^���S�=,����v���s���?��u�)o��~���ⶒ�<�sU8|ʭ�<ַ�!��'�yꮥp����nK�L��WK�G�?���d�>)GN�%O�׿pv��I�[\=p��Q횡�r��删�\���t��Q�<�b|���W��D��|!:�'Oۥ&8*�Ş�!�7�	O8��ӯ	�%���şW���!�3>�w�&��̞�����P.E�u�'}�g���;����1>m��h>�u�cĘ�O*�O���y�;?�jﰜ��G�����4z��g!��{�Sʣw�m8O8 ���r�:�ED�)����.��CQTKs��,J�(t����>�W�OD_�g�]�ǀ?}Y�,���O���s2:w�F�v�3�5a���šܞh��a�ṕ��_&�Dz��S_��<2:7b~ѭ�ʋ2�)j��3y(�<�%�f����}흺K��γ��9�	�	�ߪ�s/��9��m�(?��g�@X;�'yf?�Ԅ���a̠�����h|,�xל����<�wP\��'9+�0��G7��<}%O�f�k�g�X�!>�7�AW��y��eQ�˿���e�N�*���y��i[D�C�E����~RH��zg���f-�ߴ�"�q�_�J�K��[�>o^��~*g÷�a���^8�kW�t�5bq��l�.9�O|r���	G_l1�uu�
ˤw?���C��g��O�Cw�>���_��k�(rF�Y�]�ZnG|������PR^$��a�lx#9;tD&�i">嫝˾܎r>�K�g9
a^�M�#�ޗ��]EWy����O�萇���r)�g��oƉ�-�O;t�C�ce�Ww��Ӂ~���/��O�ɏ��Ö*^�cΥ���w��	>>���G� �܇�1F�ɏ֟fϤ�&���#���e�^��st8G���C&������Ru���>��P{1o�"Smk&�#�g?�[?�����C�n�7�hWڻ����_�B��O�=K`�/����<�#��.G�W�莨�q���+}�������B�������3T��w.�'��G��(�y>��2t�Rg|�D{��(}5�@Or�?y������s>����χ�!�S������tt���,����F�y�y!��cN8ru��>K���l��0�r��#Drj�8�v�&d�[�Q��L������T���(�<Z�]�Oz,6J��Gߩ�W�•i�#$sG��+̽�q5<���#��gz)0����<����h�AȮ���oa�څ1�x��3ɊWʃ��vk�_D�H�b���H�/ص+MTV��m0o�����Ě�|��o�Z���*�7t�?�����Ħ����W��
�v?;�˔��Nw�����֭��;[�,а��U�V��HO��_�<�rn�Sͬ�#���c�O��������$A�^N}�X��|��o�=o��@>C\�&�r��O�Q�#�	�%)�v��1\#�?>O��oꂜCg�ŸʋB1d-������zK. �Ә���ܗC��>��l�v�>���v9
��WT��\��O�pA����B'�����;�^	@�S����O*x�s�.�Sy�����)��ً7~�<�`����Ggl"�MN��\�)
݃N ��5埓��\���%�PM��#Z�t�(�s�ſ��rq⟜�?�еw��{�щ�ѳC!<�t�'>�VW���2>�G>���Vo��ʿ�ή��9N��+�ǿ���M�&�A/���Ӟ��x�����+��~���<���r�|w�Ѓ�Rf��)]��~Q�'������L�O��\��'�ʟAU�͙�w�$O8��W-����5���K��gzOc Q)�=�\�a���"~#!�ԫc�8M
���0�G}-���䄩Σv�ٔΎ�<�������AOΣ���
2��W^�M_�����3�w�*��%{�8RNL#`�r�Wy�1��tϪnb��M_�?����������Q�,>��+��+O����yș���VtP��8&�(�p�B�s|�9>�BJ��z#����U�=�`W���Ȫ��>@�'��
�<�o�"{P~�4��&�^i"�Ӌ�/8?��39k���Yճg8'�������g8�{�<*���vM�i|N���9��l�=���(ߍr嗨���9{�|Wר+'��M��X�?{�{y49�B�ko�L5�-�"OzAǧ5hJ]~D��O��"?U��ʟ�rr����A���[��M���/�?9��r':���/���2�X")��Þ�a���#x5���C��!��]�9rVn�������!;�~��F-��Y!�z���W-�ۤj�{|4
n�k|ο�ْ�vU��)�c���h�'��]������G�]q��N�*UE�$�5:�Q�� +`�;�I��6��iA
ba�Se9��֮]v�O�F�K����^������9�LL��W�k��I�I�-�o���l8��?y���[f?�3;�Ph�tKLz����hGY�Kv]B~!�������^�#��5Í��z����W�O/ګ|��vIJ���/t����S�]X�.||z�Qa?ɮ�R{�7���ɫ7�C-�↏��e���dn[�k��V�z�co�����1�tdx�[j��]a����s;�K1M0��Ƕ��jP'�ilWv�?�Ѯ�=�
����`>��l�+3>��[<�\<�/t�W����1�{�|�y�g��ѡ�l�q5>ѯ�}�^NIov	�{��O��F��A/���G�8��=�)w�O�
�
����]��������_p���v�څ�����D�Ot�(<y�/L�E�t��A��>>k�U��Tuqq8ۘ��ݱb!��@�r_�a?څ��/�@�]
�Gu�(�=�W�-��2&��ߔ���*��x�Saz��`��rS�s��cM�f?�=���'�M�җ�⍕CN*�#F��/�'��BBO_����?#�4�P��O~��*�9��T���o��i��T��zo<�_o|h�������F�^�>�\����G���䜿O_r2�S��U~ʞٕ�(&>�ǣ��s�pß�|�C�՛<�$O�^�7�|�$��O�����b�����™��;�"�z�c����=Ͽ�ѓ���ŁG��ޟen�{�჎���w?���mu��&�g���.�m#���Q'��_3�=����=8�R�\�2.V_T�*$���;3���u
���8�3�X����)v��;�՗x(�d���W��yuū�3�G.�M�9��}��ud{'�OK7���<���3���B�vL[Z��~G	���}'�3{*�=�薝���pj��z�zA/z�nn<��=�p*��g{M"'{�*3�ٳ�S�]J�H�zƯ�0f���(�[�->�_y^�����n���ɞ߻�)��TY��~07�Sܻ�:�i�HO3�I-ok����zᘿ��Bw��%
��K���F�=�/��[������� �;�O��`�9���*^ZU=U�x�rw��;3��3GཱིIGֺ��q���O�$��u�ܥ�s�ɵ�Qž�CN}�q�9��U�Q���_�8�_���
++�ׂ�x�G3���~�Ȥ�UƠ�x)�_ŋ�O�[��<~�F^��/<8ùHX��<�E�\��E9��ï�gay��=�e�ǃ��������A��>y8�]��p|AE���/��
��|��?.!������+|���gB�٩����D2���$W��g���)^���_�G�(Z�ҽ��~?i�v����J��C-�N�;Wv*���3��`*��yse|yֻgq�D��{y�o�L�R����'*�r��?��'�I�pS�¡��|��'��o��J�zQ>��Nr#]kDz�ss;�O~��s�<Y?x��?�X����fv�!�cs/�_;�]���3{�Iwۗ���4g�8��3�N����k<����Ow:��P~A�P\��;��K�����R��w>K9qo�{ZG'��8�R4�����M�<!�~F^8�$M�F���~��=�\��c�ϵ{BF^���M�q������<	�3�t'^峃=����∙���c~�����w����~��!}�L��G~������짪���_��p��\8��7�a��?�=����#���?�s]?������a�/O¡,��39��W���%_=
'}T0f{Q�x9�׿W�m����}U��1a�9�?n�p<���w[ȋ}���cP�"�\�BNh'�����R�Y���oW�w=��c�"��>~N߰D~�+:��=���\���/�rM�_�#ܧ_��������a?O$'?�w?�c"�b�%���E��2�=x����Ƀ���۞ʍg���'=yv*.9O}ry|�9���p�S|
0~��M���?�ӿ�"�*޼����_<;����/ߖ?L�~<�|`$��W���G��2�}y�U��Z�G�?Kr�©�
�NXm@|���˥O�l����V�|�-�`*
���?�]˚\�|�*W:g�M>���`g�&�tTyN_Ixv�_(�C�8��4XG���u��;�.c'4��_��v��y�O�h�,œ�=�a2�P$��,
���I	�Z=~֜��͡ѭ!fDA-�ݧW$���v���a4	�*@�ѧ�OS�%Ԝ	��E��q�6�W��	LF�9�M������ֱ�^����%?ф�1���!�>dDst<˩M�c�'����.��6�X���x�+�A�Zܼ�>��M�z�F���m�>8����z�h��f�G�?���7�~�<|J!�^�Sb��E2�~��_�x�\�{p���[���4Fh��*I��Xp~�s=E�'7}_j���N�h5���]G�z�*��)�m����{Z/��ghV���X�I����^��^��jM�v�R4]�?w��
���y&�'p��ZR)�\�-�#��c�_c�Q5N�"p�4a��ᗆ����`|��g��\������jFdhIؚ������=N��XɬW�m�����@�d�5qM|���;�$ZS��(��iZ��_��q������ǯ�3���/�u����K��7�D�iwJ~Һ8���)^��m&�V��?�s�=�=���k~�?���ܐ5+�k(�������"��.��Zy���N��=z��n��ή��p���@���'g�� ti8�u �g�c�^������9j'k���r��G���9H��C_|Q}�)&.�?xޚ�Jo���M��/����1�@�
�N���3{�$?�l$'�~�4��=��㡱Z#�~oph<�Ӝ$��mo8�>��� ��xG�KhYqA����
���Vm��3�™=pH©�j�V����p��td����>{����#�?���g��F~��t�S;�='?��a�*@Ξx_#)��q}8�S�W��:(��m���L'��~5Aġh��>�O�l/��3|T�^�r����{y(�j��l'�~�ϯ��l�[�����=��P���3O{N��G�5���:R��3�\�B�#ZקAS��(�}���_�?���Y{(^pt�
j���k�Z�T��6����g��s
d~�)�����U���_��+���a�ϭ^,.xK^\V_ä������\MS�@Ə������U~eO��)��J�dN�/�&����[��
�#��'w������W�gd�zM�I�v��x��dk�D�'o�k��R裒���}_	��:G�NП�
UO���=��M�	��l.߻�.��[+y��W���u�1�A��w��U�;�W��C��/��5�Eۓ0���0#%ξ�?��q���?����'�O�N�0�>����5�n9���1���/�{�
���;Kͧ~\}��ߏ��|��~�_�W������J�S��qG�={�2��2M��n/�#����<�p����Y>�a��?�xWM.C���JJ�T��7?�%ߚ�[�9�ȷ�4��U�K��]�Ӻ�]:˅ϯZ�^B��_!�ƌ=�[���%r��-P�NƤO�F͉i�j�	٣Ae�s�`/���\��SeQe���(�2y^�q�cݑ_�'~�����{��\�����pȇ���?{�O���/8��\>�y����S��d-�,�(wq�j�����tƫ�ЫF�O-n��?�q>���?���׹~Cy%�ջ}�<����-&Xȟ���/��Q�
�4
(� ���������,O��Y�|�Rh�}�}Eȫ���I�y�W�t�L_��Cί�Kg?�3��%�v�#^�����yE_=-��s��)��?���~Cj�N��p��m�r��Z�,j�L�;h�,�(*�ū~���\+��\�$xF�s�Q�t{�?�~��|h�̹�x��8�WO�G�W���+c2F2���~���}��S��	��2��O{����ŷvC޺r���Rh�����#�W���;�?�?v�\{���÷�y&�/��F�p���#^��YO�����\^�xڳ�p����q������H����={��>�<T�u���0�X�ܩ��)*����$��<C�~��iL?���L���0r��u,"g�Ņ�x�~��"�Ƴ�Ls��6�w���N�*=�?�[ׁ��:��/w��g���*~����E�N�L�+��}�����c���g�_���"�ƾɷ�^�x`~��Zު��솫�7'����n�cN�_�x�/r��c��>��N�O|z��)�R~�9�ڳ����?�g���x
B������k}��C�����;��9���w�/a�_e~레K<o���0ͯ�/^o��I{Ą�N��	��=�*��t�y��H�rٳ=����g�֟ ��H�s8��\��^��V�/�1���nϋ��zA�x
�=�
�m}�xn���`0�N��Ǐz�^d���';��|��������xAǯ��}��7��{aN��j]
�\���<�����\�����=���3'��s���~U�9_� �s���^<�Y?x����yʪw�?���:+����K�܂�~�r�➛r��y̑�I��*Esw��N8�ڞ>���Y{Һ��]�R
��ϸ+��x�è���Gr��RK���`��{��r�b���Z�9R�pl�_w�~e����7����v��ڑЄ'����w��N7��uh�?����5�G���3��NsMk���E~������}����?�4�Sy���Y{U�Q�>}�� M�Q��7�z!	��XS�v��X��g<H�z�B�\#\����9p���`��~̴�y�9Uv?������.i���\�,ow�@ޜ7-�J</q��'d�C\t
����ԉ�ϯ��@|�=LsG̏p�歹�&�;:�x/�����3{Z���-�S�Wo�ɔ�1*����ֶ	?(z�/W�x�������k��l쎾�׭�3{���E��_xs�N�_
ݞb��h����av�>r�>�{���36E�~��ͥ��O���6;ó9ܿ�^�����ُ����w�iү8�_�0(��1�?7>oḰ=���08ۃ·��s�D��Dq�v�.5�97�70��҅��R�%��=ه����^���W�ͷ�0V�-�����oS	���ә��g�K$�|��[���cy�W��9/��\�֧Q���O��|��V�f���*���N�id�	No.9Axۉ�i�)��	�"�G)�ox�C��[?�����w��r�|�&^䄌keF8�OJ�yXu����]c���Q���Us2��A�Z���rmy�{�?�"�>Bp5����&ꃄ�z��S͐l��Zk�]�-|s�/ZK�~"G�#\�Ȟ[o�3�H��4	H�yk���'���k����xqw�v��{n��沘�/�pv���/{TQi%}�o��N�n�8,�~�_�k�����U)�5�~L̵q�ў9吢?~����xp��1�e���C��x(�9z��n.=���٠�n5_�\r���?�'>�f��pO���F��wT��9qh��N���+ι?b���پ�O��q��_���=�+7|���|o,�g�sk�u��w�5Ù_���B~1�g攣�<;�_E����-���;��_��?�N7�ٯ��aq9�=�x�ϩ?���{���|�C��������������8�=���!���oq�u�	|<o�d��~ۣQ�6�����t�<Y�v�Nk�&��~yr�=�G�^��Hx�����y�o���'ϧ=�J>�ӯ\>�92���<��"=�;'?�k��j}?qf�x.^g���կ��k�����y����T/�9O��R��ߘ���~�k��k�]����Ǝ�3� �v����p��xYS��儍9��7W���5^��{�*��]{O~�=������_m[S��5��s��aO�����xG��Ǒ1g�_�S����K��~<I9B�(��뿊׮..�5��3X�O~�?����C��#a~��3^�kgR>���?��<���k��a$�Wn���ւ�*oO�WO�)4�-�ܲ�!y�q�'"O}�X�[S�8�)7v�z�~p�,^��k&a�� ��Yܟ�q)~j�kv>m9,O�S�_q�{���bO��e?Ia��������]��a�Q�⭸'��K�=�;�veO����I���ŧ�O~Nq�\X��?������=�d�ks��S�E�8~/�zY�=p6�Qg�َ�B�L�Qz�۫_�q�_��o����yj�z�������g���7���/�'{J�o�C>~jo���C�[�9�j������~g�������y�����s�'~θ���Z��mqONxƫ��x>�+���H.�g�72��I�g{�k+�q�}8��FNk�O�"���z1Ì��~���Q�i���}���p��WH�HN���/o|�$?���O8Q����w�>��k��Կ�8�i�7����絫�}�7qȋW�,�g��R���~�zM��.�����
����S��3�����^�io�s�͙?k'S�^����{�k��=�g���#���w�מ_��~�f�3bi��O8�?{���=_��z�y�w�O����w�z���j�T�fO�g���8	���N&�s��ڟ�_�����9u��k��g�v���Y�ʫ��8�7��k{x�s�k��^|o|c㐯�H�kW7n�ds�^�7�v�i|�l���}oҌ�=W&?ߌ��u�yǮ�ɿ7>�ME�#gn�͉�q�����ř��������Ut|.�3�h��s֞#�B��ξ�[�O/bԻ|OY���;�ܓ2��ϛ�?\;F�8�9� �f�k�Z7�{Q���S������{�%"���iݑ7����ssw�v�藟����_~�e��n7�ˋ��{����Hb'��F5G�u5�ym�S���_��n���3��o�(��Nĩ����6)ܜw�x��7����(���kL��f�8��$���:��rl~��_�r�Cq�i�7��>�h��+^w>�Oh��O,��L���ȏ��x>���By�K>D��a{B�g���+7��{��$���'��O��n�]M�����
nyKyv�����8��}T�7G���G��!�����g�k�9��{=�x^����㤟�^(7�#c�!��!�ب]}���=�ɋ��U�?�x^>Ԯ�8+w��_�<gSȯ2��K�5{�r�N��_���¡��5��=��q�|�Sq���Wy�8���s�+��/�@R���~>a6dj�$&���'�O��:g���(
=��Y��g.s�ו�&���';�%�W�ˇ��j�2�K8�d�mLI�3���G�Y#C	�O1�����IΆc��_$�	Y28�����ŨK��§�I�s�>S�6ys@+Vsf_ s
��\S4�O?�����x&^?��ght鋀�"k^;ߘL��_����`�0
�Ws���d)T�X�{��a�H���=��
��|o t̏
��b	 U̮i(�F�����?��#9�D)�7ߍ6�n�-�{�Ԫ�|�g.9%Su���-�η�)�ڜ�����*M����[��w�M��0ǧ�͕1�U�Q���M2R�����?���8��(zo���S����W����p�I�	s}�ŃE&�E�%&7чI��Y��9�o�zS�X��0O�?%7��!�V��!��d#�ѿf�׃r�1��e��F��z����F�����W������gͬ�e|��+�'���%r��D�!��uGTT(49��®�����:������m2UN�$^��f]S��<R~����Jx��}0���Iv�r��Rq���=�/]$]����3~�9ȁ�֓1b����b�|��š��D!���<�1h
T< ��
�� �WrN�ˊB
�3����J���=�k"^vn!�U�#n�Z4.^!����@�����������#�GR�S\ �ؼc8~���2�5�����L�|^GB�~<S����Yk1�%�5��_O�:�p�k�S�v�=�ʅS?�����H�k�	���96�4�=d��}�D��,�����&�zzB����)3��Pq'�I�%�@@��0�cp��8�Yܞ�ɯ��<q���+�ӽ�'x?�3��ˁ��ֹ��p�Х黄��=��i���;�GХ���O�G�˫�}8�'5����6{�i=��h^릜��oH�g�R��f4L)��={jǞ�O(�y��ш����s���!C��l��4+��42��K� �]"�=��x���i�C򜊷�g�@�T/���M�$'�ӳD�.��_��<�������OW5��'��ag�Q�8�q�<���'�!o,@>��S>�d�`w�xvJB�l��
/�+���3I�-�'�[�괧�HX�o�n���z�Y~�����Q�I�g��z0�O2��x��[-$����^H��^����781?�T/©5_���pX+Od$e��_�fϒ�\>��=T8�\~�[s���c��c�o�.=��혗��ٻ}{��4q4v�A٥�s�]���[�~]����8�ɘ��	_3���ػ4��>��=w��څ�����9.{��'�~�\_:��k�|�����)�ظ��=��L��p�摧��2��>�G�}������O����i⁣}��]�+�
��I���^�Yn��䥺�N���%|+%��2��~�y���-����H��ې����ۇ�X���;j���b��N�Í)Z�sn��o�׵N$��J�K�1j
�C��qt�E��_�5$疀�L����s����qI#K�r�(p��l�� ���s7�5�����`*{��>*{�G)���������R�gl̫��:�`���߳����՗�=�l �����:.Lqݘ�x���8���A�8�ɆG���e?�?��|��̞~�ㆩGjN@����o���=a[#)��R�Q֭Ԟ�3�{������^<���p��!0S����=�F{>]�F�/��%G���G�_�/ď<Q���]G�P}L�K���wr���+����}X^���v槯���	�<j=��B�g��s���\�^�π�U���O�q[��P&�h`�]>���ƵA;��X�������b�<G~��&��w�.�ӧ�|�=�8��2Ā�/���ړ6,�!&�������o���A��K����L����u���}w��2sy�ml����7����듷x��zį��nݸ�W��k8��d�Q��q�&��}��.��%iqs�;�Oc�=w�ȯګ����_��7��=�6v|>���Ͼ��=S����v���G��q�x�Ϲ�����3~N{&<�ɤ3�1�~������;j���p�dI�Zy7)��n�Q�:��tFBL�r?�� )��g�dr)~�H���ݫ����I�J���yz������|~��ݢ�r�p�� :Ƚ����|ϯ����x��ϭk۞;�Ž����l�R8�DN=������zŗ�.%oۗ���z�u��;WAq���h�VF��j����z�)'���?���?�պ�f6p�!��5a��)�����!�W{�%�Y��[�`�-^�~�w����u�.^��e���������E�O�D+��A�t"�y��13&��'{�|�M�H����X}'����?���╜��5�[F�����߬�$���O^A��{V���3�i�����?A����G2�\ڟ���8d�g��Agg�F끵��ř��S��DV0�2��ۭ��D�֑����y�I�:%��;�-�,��\�*w��]�x�|lnS������,��o/��ʑ@�?�<n̪=������|A+���� Nʵ�\ed8�o��Ł��p�š5�y�lnG��=�%pU
 ��m��?$�>��8|r�ǿ�%>�n��@Ի�+����:�����0����_�ӽM���1�/+T�p�/�遳�K����_Lc�����
r�|������G��ŵ�C�R��ʈ(�u/���|}�G�\�p����9�3��"�Hƿԕ�Bߺ,��5�W���ʭ�(^��(�igO<o���g�rA�ʘ�%�×o��p��~s�c�Sr�����_	Y�G�<�����H�E?���F�N�k�d��( ��b���Aɞ��R����nt�	'��rK�{'e��j<��.�j�rB�����o�n��Z �\b؃���%��%�0t�!u���׵6D�>~����*8k'��%�4@m�g�O/��(�E��켛-G<����/���1�?
u�=�!��y��L�/�3{��J�K9�Rl�RB���#;������z/u�����_���9)v#������U&W~���ij�|�o<����2l���\8����B���!���/���=����>و�:5>���'�XK��On>N|r���]8�����(�3^�-h���J�m�$���e肈����!R�2�=��4No*T�:O�r�#�]���ϵm����󇑿�Nr龃�6�����jf�1Yj�T&������9��j��AD�W�|j���D��JUDݐF��>��E�:L_M�h��.{\����?>�oN���=�g��,q��������a��ظG�g��
9f���9:��/��:�~�{w�X��zg�MČ�=�娿��
s>$�A#�����܋~�Hia{��n�<��{���
���5
�&W����*�� ���������k/'&:�>������ ��O"�I�����~���s8�����\SQ�S�tP�ӀC.L�����Q���'=k�1��E2��=��\��r���ʕ���ꖷ�S{`�?�@�c	�p蓻�<�Z���Q�t.Mc��{д�r&��=�����J�
g���m^�ۙeK{��l�nE4�T�\�����ܚ�8�'��J0��y������3꺡vMJk*�Imi��,�O�/{G��$��6y����gy�?G�ɳ���+�����5ӣ�#���Kx����9���=眿��3~�r�+�h�p����\�h67�\���k�(W�V~낒׏�^~�B{�_��r���DJ�wޠ���qO��4H_
+^p�ԣ8���x���&5�:����ј��|������Gr�t��~���g'y���xQ#�s��x�yEޜ�xfjk�>��0^�a<S�)/&Q(�ť�+��>�����O�t�紇�4|����9������I�K\�!T�p�08�å��؍r�h������qN�%���'{�={�y���p���#�(p;�u����������d�����>t:��z�~�Ȕτ�m�J��ߧz��z�_r1��=�p�I�d#�~)��ŋ=�G}9q�v�{����\����V�
'y8(��_��9�}b��?��2u�kO0?�p�T/��w&��'ReO{Q�a�����S���\���kn�#zn8�P�����~��g��}n�`��e�_���׮�|�����ك7��#!�a^��_������'~Z|����!�姿�'��\=m/0��2�/5�t�������Ww�==iӿy�(�W)ד�'L<�O��G>�����)�����A��~�/8��yrE.~�!ܟ\��x�p�=t6D��%���ևӎ�={�W������Wܳ��s��B���#�����dO~���/B��@�xU��\PpZo�kᐻ� �7�t�@�ʌ�C�9~\��p��/��kt��~���g��s5�o�N8}w�L.���� S[��2�Y�ڣ��Qi9J�6A1d��=ۋG�\����K$L�)�ŋ|{�e�
���p�`�4;�\��r�=��B8\��~^��]�y�������&��ݻ^����b��|�n^��/����[o�1���������qi~��/�쉟�r�쁟E��I�pPQ>�À���Ov�<�'��o짐>��Au��W�|h�HKe0����ŝ~�L_ѳ�=h�EĿ�4�Ӟ��'�%	A�/-~Un��� �aϏŋ��2�՗�uI��}��݄�62Se�8J��K����} {t��W�s>>\� �G
��1r�"}�aO{05ѐ
�`�a?S��lse�0{��v��p��(�O����B�wB �G+Q>���Q.y�����z�8
��
����v&��g�>֯)�9O���9a���L���'hV��<$�\�w���p�Sc[��C�l7N���W�t�LY���c+2(wy��6����'���{t�I�u���c84ӧL�nA�����������n=I��C?Ņ�$gί�C�x�ޤ�07k瑀��y�Q8����WO����G��K.��Ӟ�%~�y�̑�|z��ۣ����x�<�O�b�o=��_�l=*��kqߺ>���k�?���3{N}��.�L}��`���Q��r�?�݇<��Y3�O��\|!d��n���z��x�^����
�붂c�,����y�x�W�gO��wC�Rv
�K�oo��4�����V(^�H��Yɥ�sU/��?�#WD�)���9(dw^%��?���8&S׎*7~8�!&lO.W�ڰ�Q6W9.5�GQ��D�m��xн$o����_�dr��?�E��3����=��Ͷ��g�0I�?{\�=�	��'��ֱ������uJ���i�������/Oȹ�Ga���Tn��Iv�8}�ݜ��4�=��g~ѷG�Q�\��^,˫�qa�K՗峲S�'�_=�/��^�N�o�0��yr?	�����W߼�a���Σ���|�]�py��'��e�O'���?��4�#��r�~�
�ə�D�W���_����# ��;�_8G�¿�`��눸DH߮<R���#�)�������o/76�a�U���5Ӝ�kF'�c��T��rqW����p��Q��ٛ��-�uP��i ۔+��0z.d��G��G��p�p�š_^��Wg=��������IM��T������5�����N��d�G��~��O>��[㓼~���Ng�q����+��/�Y{%��17��^�w����Y�ݽ��wr�
�,���O�̌���
��ƞ�~�/�Y����C�)%����ſ�j&�?|�=�i��(��s��N����9̪n�4I�z�N��8�_q!�c��M�+?�����ڽp�{�������Y�*��=���������X��?w��3���gy��?�>�(��^�S#g\~	��/��!��x����]0�dl*�	U��g��{b�=��Ie�Ӈ���!��{#Gcn^Pu�	�91��x��.���1v�-�rg���9�vW�/��~F�~���F9\����a$S������9*�_p��v�\�W8��e��q�?�/���u����	�q��(Ɛ����g�r�'��O_09*¡��
rT�?�1��<}v�+�fi\�8�]�A,�La�9�����9�O�S����e?|�������=_���W|�|��o!�xǷ|�?�Dw�_8�_������=��d�ߛ��
�*�_y�qxg]� ��7;�?�F��?W
Տ������m}�N~/��g�
������5�1^w�ci��%:���ϑ/�l��y�K�f����/�������}��52��z5�7%sىk!��|�W_���T���`�R�z@V������zy�4�B��ީ{��<-��	ܑ͕�k�т��EM�|��7G��ʸ�6{\�Ա���LG('���Cm:#�
D=���D"F���)�Kz$�� ���$�i�ǘ
�7?�AEz�F��m���~��>�7�_�o�n��= V(TGi�����R3�M(q��F?��>掟O@�
&I�˕�A�K���\�Ma�������>�p�w���e�in
}���z�ƒ�@pLkP�if�p؃�f��P%^�i���-�{���wSh��}�1@�5�!6wGkR‘%x�r��=��g�������_v���X��~�㙛r��L&uITn}�L�q��PE��	>ԣ��?Y	�7i�7?)Oh�X����h�J�?��E��[W԰�f7�'���j۲츩�7�[�YGCUNNč$���>���QE��=���U���a
z��Eފ�������]{�l"���+�_�?�O���=E�M�;�>����[��|��u��~��R�N��,C|�:D��BL����#ːO
Hy���>���(�=`x:��*��,�a�h���V�ΗL��Ιf�&1}U,K1�Y��勱7L���oTZp�o�Yw����4�c�'�~�Q�;,<j-��y]p��a�o��3l��4�����|sƮu8o�x}K�Z{��+��ė���/�م���/j�*�:7W��
�C8E{���Q?=�O�}+_��tF�Ƌ��L���9o7��5�u������UM����k��q���w�3|:E���o����
��^��Âe͂Ӹӆ:�}���D��~a��:�\s���/�h��]�/���;h�q�
(0�Ծ�G���C��a��׊%���s�hrx}{�]��R'JD�PD���
5�T�{Kzk?<��9e�D���S�MIT��|��-6i7�k{-�Gar^D6�;	���ޚ~�_iJn�|���'r(f����jz:��+�Y�,�?�w�zM����]�F��KyW$�8~5�63�g|o(l�V�~���cv#��}����
���\�Τ]U!=-�
ɤJ��ۛ���#'���-FOU˞L\����n��GH�'`�����EG@�-&��HpO�W{�7�����c�S� q����g�������:��G�߭_��f��XOv��\q�(��s�>F����%� ���ϱ��!���	&��^���j���[��#�����M��E0�)�'�Y>.���	��+���C�3w����wd�DB���ѧ���Rv�ؔ�-���7r���k��GO�ָy6׃�(���C��-n���|n��Wlʘi�/DN��7�f��I�J�q��k��f��b}p��B{�裿&�H�]���ϗ/w�g�B�-y#,SB]�?�+����#a{!���"��Q�EC�-��[�T��S4ځ�嶱����wy&���gl*�I�Z1w.g�F��1}�~�-�W�����S�ܶ�|u}a&f<Q�r{u�c�iw�A|R�_��ÆƊE���{������
:��T�<-_�–�ʋ5����iTU���$�4�C��k�`o�_��ƣE��=���g����g�)�92�oƛ}��rK�bJ�ׯ��Z��Ⱦɍ_3�@&�9�O~��qW�a�iJO���?�X�dJ�����_����vb��{�P�_�^���^7ν���+|on>�gS�	Tʞ̞=�ؐ-���?��z�ђa�4^h��m�*-0h�K!���O���Z��=��ޚ�4.�G	=���S|�u�G�)������fݡ��Y߀�;9�4���	N�ȋ/��ӭ�������]�))���r�=`�䭡s[�0�]��؍����5a�{�S==��2w��dH�'����'�+Ӽ4~���oI�N�98#��{�J�INp��_�1|�8��Ř{�_>Y����]y���ON	��?�G��D�&�.�w��#?�a�GIp��T28)�+Ӻ)�t���N�P����J~���5:p�}�0�sD�ۘ[��“�(�a�&�;���&؟&���/�_
Oze�d'ܤ~͝k
j*7�T=����Kz�8~':��K~
}��ĸ��N�&�s'��C�8�>�S�3~U��M�����S8����'X�g1:�l#|�6�Cr���ǜƝ��8�yn��R��[���X-��ﹻ�!:#������/�D�Hd��$
�]��eg�Y��؅�3:̝�J���_u!:�$p����=-$|��lrȟ��瓡����Vy	����x������ ����=���v"�#�:I���HУ�4�b�3w���%P���\��s���V�ʣO�|[]�g��}�3|p�	qz|	>�uwOx�_�(GA�M~�Q,N|�B���/k4|o/��f��c�eD|
o���1�>�����;d_�K���,|��8B��s|�d�)�����sp������/��^��pΏ��+��eGtV��ss�Y�%�Ŋ�|�+�ԯ��YD#��mjD��K�s�G�~)#b*�G�s��>��d�������Tiί���=Q�<�+�k��E`�
Ny#��+6���=U�����T���2:����򌚈[�S� f��"�De�Q>����|X�e�(��~�?�⹋->�Y
S�/*�UL�$e,֡�F�B��1��$h�1=�E�+����O]�i'GJj�!=R�78[������OB�ݩ^�����n��Q?'L1���ݙ4�>�T�+�k"| ��ip1L�Oﶭ
;Ι�����٫�&���Dx�3YJ�'�HM�ԉ2m�e����n���6�k�rȘߵh��!��[�Z�/���Tr�g�����*���0�ҷ���O�σ�xף�r�4Z�jW��]�ida&i�v1E!�iD._HtFV�}R����U?�h|Ge5���	Ny����ZC�� �����Nā�d"���O�f��HR���4C��	Ԙ��K��Ⱦ��D���
M�,h�U`D�	�/��{���vBh>=�zIn��mN���߸䄱�<����S���5&P*-j��sZ:�?��P��x�M����r:�)�W~��n9e�Q#!�3��"�#c>��YB���w*����#bZ��cP#̯^����y<H���������� {���7^���)����W�-0ȇ$m����|�x��w~���y*�ȇA��C�''Y.*c���]�p�v8�s2�+􅯿�iG��{��'N�'Ϝ���<Chj>Z���\1+���s%�(�buMT$��&��}t&�W_yi���P���n#��|�)5Ը3�mA���w[W=�|<9	���~���p�ն������|���g���u�
|�L7\���|sq-��?a.ՙ��{8YY�΅�h�=�y���L!��v<�v�8��<��t��5���0�.n�4�����+�"4�g�ST��b�1Ē�7��w�ڡ���-8�(��%�[�|^����5�DG��1�
ٗ`�h*�L��0�b��݃��T��B���f�-�[sG�����uۧ��>�i�L�����_��r��PX�l�Z�o�#'��_'��C_�cw6�x����Q� ��%p�>�fr�}�v�sҭm�Pȸ�&�I�\~3�O�oe��2��栙�7�<?�^8ƃ"Q���:g�1��9E�v��A��a����7���z࿾�s��e����x
�YX*%���	VXx�Y��!w��ү���k
���
�*{>'�O]��ٞ��Ģϳƺo�s�;2��5�����;p|JEYb��G
��cpT���_�����C�R9� ��e+WM&왩N'2���xy�yI9y�G�/�ڽ��;� >��	��4|��BT-�]r���	K����1��}>;6!wd'���C}��-�K�0�����O�=�s��N!ܜ�2�W`榍@���V8��|O̊�+g�li���OAʭ�+ғ�
��_Me�����"��P�=i>]�?��E���q�q�I~h8]��3�H�}uy�9˪����1����$�i��w�<�ö��j��L�>���]�����ã�l�W�9ǖ/cr�CJH�G��B�n�ʑTI�+N�ν��p�O��N]\\ul}rb����L箊u����.��i���B`�Rt�_�,kf��<=��9y�; �~�
���C���}S�[�xݒ��gN裿�}"��u�u�>�53T�;��t6���ɻ\4��I�V�>�.g|G}�g��dJ��wO���o�����+��7���c���+�T|�c�Sy��_g�C˒�P��F�>eino����)�%~�w����̪����쎘�ӣ�p��Ā����3H�<�CH�'�+x^1�����*����Cޒs�żE��&�-��)6�#�uY�����q��b�%�:ӯ�w81�
"ao.�yt�g�PW�R��ˊˋKwpe4��ʳJ�K>ܼ��\��)����}ǎ�/�7?���o�Lq�uvccN���3��e��Cp�>�6�����D$o��y����=5|��q���=|ܒ����EOk��H��I�/8Ў�9z�w�<ѡ�ԁ�����WgV0�{�~���8(���
�Pэ}cݕ�]X����h{�M/�����?<lv��QvLT��D��R��C7����=5����`\�|Ɨ
�LJN����u�<za�Tp���>*W����Z3/���~[|��=�m�Op�? ɢx��F�կ-�%8i��UA��W2�d}�[��>)׻�>�f����yY�|5�n��*|�$�t�8�c���!p���v�~�ceV��I�4��6����W����_����%�V{��/pK��(�ZV��`�����p��i�?"��T���1;��3?l����[љ
=M���bJֺ�����u����i�87B/y���TV1:��W�C������5
FԈ>�_V�@���Õ�9�/"�>^A��?z:p��"�b��o}��Q��5�߾�Or*�P��ύ��Haw^��\n��x����Jyrb~NZ�Kuj���3�����L�Y_���f��0'?����z��,p�y�,5����=>vWK��#`����.P�V��b��}Gn���KMI]�w{x�_ࠉ�E?K�W�07:����|�#y��*��~q���o@����>PU�� g7��O�ѡ��c秜�hNyl�'������։����e��=�v�[�'��QC=Zx&χ���V���Ni��8t��)����Ư. ̃g��Epּ�'��Ќ_ʣ�a�"f�qտ�g�����l�7G�5��%�^6��OvXI�1���ݽvǓ?�ʼn!�$��.(*;��/���	�o��>���I��(�>�0��C��;UhQ�D��y��D�ŀ
���}Zw�G���_�HJHBf�-�%|쪤_�&��ӣ䧽1���ڝ��j����3?X�*�p�z�?53�����f�X2�z�
OM4~E��0��-_�?�t����|���g�4���S���:q��+:���eֹ�Fv���ݩ�xm�� yv%��yɳB��s�{bD�ˡ<=U�g�l�Dv�;�̂~��o�˦���_�\m׉��/��<�pf�7�z�������5���g��S�^|[חI�~+sK�w����͍�7��f����bn��Of�=�+�ם�H:�m8u��nw5��#�_��1X�o���C�jt6m7gF��������{~�Sp=��9sBJ���
J�7��n%��E�N�~)�s����T��P��~�\��
���:g7&�#����<x>s�<����HN����<-]��~v�j�S�`����I��#��c�飼����'��"R�|&�Q獵�֐��5X��m†0�@IyL)��t�7;c4�E��l�Z~�bJb��Z�laL���m�o���0/�nv�j�+�ڑ��t��
ǡw��K�V����3�Q��jݴ���u�O��|Z��@�����f�0��'|�4~�ݩܫ/�t���-F�p�KwE�|�)����h�<�W1��_�b�y>0`ݹFؘ�o�����_�鮟݊W��@��'��_�o�5�s���ԕ���du����[�Z�(Ip*�_��R��'C�SC�x�y�T�1�lzZL6|�%�et�6���%@��#�>�!����|�5?$B(��c��1�0�*�
7�!����]���]ռ%WC1X�����4G3_��ٸ���*Q��!��.~�
�$z��>�ߛ/�>M
~]t؏��|�s�;��2,�]����+��n�<Iфc�y��y��:����*��&��%?�ؒ��ON��y�����T��h��С3�.�B��>ճ��@�_�>��.�i�rz��q��*��v���9+d�g���0�p���t��D@v-�B�
ᒳ"��U�S�R @�n�j�n@����Kk�|`c罐�z���$|f����b���Y/gƅ��h�3�$
>�Ӳ��0��[�j��M�b���3y|O��o1C����]��e ��C�e��,���"��֧��8���8g��T��l��ަ�
>$����S̔�+"�_ׂyX�2w��;�D�D�ꃗ������h5�J��g|Db�i�<��)��<�31m폗0�;� �,�U�;�*�zo��~�|��������h��@��'�**���M���g�}a�g��ST���)"$Y���ܙ~i��*[ya�!�>�o8pl"]������F��A=����H�S�w|�)s|�I�Ӎ>9ėb��9�nq��E�
N�a�w���x��Bnj���^��`7{��.���6����5��:�V��"}!c�|ux��n�-��q�g�O�����
���T�ʋ��ccW�m�(w�E�7\��W}��H�M�}u:+y��ak���:��Z������Ĕ�#]��)w8}�ZR�M|:&+ҬPnpЫ�6k앫p�G���I�G�OWߘ���C-��fA���iD�7��@R~{�h�-A(����-7���0�V}���,?����^���1o�>��᣿�m�����$�������ݠ�k�D�0��.��*��Z�:B��n�Ʌ�>d�2�)Ƃ>szEWE�(��A��@�\�A�ކ0�?|�x3\�F�Bΰ�� �ق�u�\gr��
V�$f��Q��;?�r��͡�oy�Vn�b���l�~1Ȑ����8��){����&�D��0��]<XV�0�HI�|���w޷�}�4�sƪ�j2��iA��Ȏ_�|@(�Ɔ�r�~��=�td�]�3�s�Ǻk͏��d�|Rs��z �d��Y�v���q�\��}�DJ۠�%*sn&���{���V³x�vg��g�d|�ʣC�3OŴ!P����;�2�tz�qH���]�&�mh_a� ��4M.�E��O���ڀ4�7��_��'8�a���,1v�W�B����w�P���G%���)����m?�4���_��w`39ɧ�n��=f��Ypt�t����U@d_��΍SYT�#�|R�ǔ�yz�sw�1e�B��J$
9/h�fQyA�vG�����ڥ\��~�r{�C�P�WMtӋ�g�ZKy���yp�'9�g�c��C�d�<�أ�3{��yV�i{W�5w��i���o���s�ۆGy5�S�ݷ���g�45oS�^�&�дB(�mb��FJN&",|n��~�]K�K:�E���؋����Q9�����^�5v)"L�h���|�i�9:c=yN��4T>��|L��!�/���׋��W�Á<:�c���#���#�h�<$絛���	�
���瑟 *�kLk7���1�;�}����1{؉���K/�{N!v�
���j�[��3��	@{�e��;�n|�_}<�>a8{���Ď8�dGO3)��01v������]��UI�R�<.��N{����;!9�'��g[:9�\�Ga�)�e����Ep���s�`v�o�d^�n#�����˗H�$�����
�3r8p�>-�w{���n��cB8z�^W5��[�s�����|��
�(o���?�lu��y��>��gW���R�ؚ,�E�:���'��;2�OB���Y�|���i&�
��?�>�;}�~�n0�y���Hh�i���U~�/��bJ&3D�Y��>h�X�ѵ/6Jy�
�����i�Dp������ǜv���g�Y��ƻu����#�Q�`_}�zڞ���Q�Sl7�+6Q�l+���|�t�Tk�󱇣��٣�������;~�/��
�;,/�_1ye	rRg��G�� �f] ��4&ϳ��ڶ8�I�U+�#|nn�ڍ��C����|j�>�~�]Ec�=�_\��(ʬX�m�;�2s�>��7�p����
��O$W>�O�b�t���!��g>hP�z����Ag���=�ڤ�د��=,ٌ���[vlkn�B��J����b��Q����Zǒ(Ÿh��W�y����G��Ŧ�A�c���8Y6|�ʈ�=|p�mc�����Ϻ4���[&F3V�Ђ��a�_萾g"�(X3�T���X
�
�|������lbq�k�)��O����d�(���f�@����3^��s믏�4B%ϋ��D��8�>@��d�F�^�|�����F�Wuf=}��F.���uOL���A�k�"]�d��F[z��^��":{+����_��'8�y�_��y�5y;z�qd�?�I/�����6���v=��-��9u���_U�ɸ̄&�)`1lj����!<�9N��/N��<�Á��	}ʛ�?8�&��V>eW�u���g}ZL[��8�WzQ��lƬO�COg)Լ7�Jk�%�%}T΄ͼ7֌>&���c8�9�r�-�UJ�p��0ܴ{¯�{#��=�1���FO��ѡ.����0��o�G�ڣ���O�Ӂ3���F��S��'~�?!�7yeN�q�DƁSy��ڝ;s֥�	���j��qL�����±6���e��bR8���������|p�?V��Ƌ_#N=$?�ȉߦ.�.���"8��q�_�/�ы֕�Z87.ǯ���N�;�ڣ�c�^S9n��twI�x<�3y_�>ï�~���эt���;_t�O���w��>]A��Q�' �[�>[��G��楔��㈏ł�����_}�\|z����b�>��>���a�!����ÂpL�f�%}/V@5M�{��6�_�b���(*�d��,��&n��+W���}hϯ�||(�R� ��>���\�V��H�}����W����"Y�WhpDO����S:�?#���~��i\Pٷ˿�a�<��X��Ns��,
3��&���*����9���p��h�������N�|^�ʻ�):�\��f�
�5������e��
�ׅ
�X���/��,B��ة��@�x�1�W�Gݕ6t�a���7�~Z��=A�C8=J��X�q(v��;t+O�5����k"|�o&AA�������>�ᛐk�}��A9���XE�~��U����[�������g�v���')8beT})�X�%���}f|I������}��Q2딹������|�J�
�ѳ�S��~U��#��+��>��n�.��	��e'�u�K&��՛o���S���}����d�~ź%?���8�0%Dj�_�1�1z5.�vT�rc���(���g�ˋ�2bY�������_���/CgG �;~
�#l�ӑb��kh<*�OR��V(e(|��ed���Qm�v6�P�M��Ӹ������'�6[��'~M��N�ׯ�T����g���QC��G�h(f"��x�[� '0�G��AĜ��9>+���Ky~��.�[9NjVY�g������_@�")^_§�!P٫�/�ı����p}�Wwv{���1z
Ճ\�+�O��+�׮T%!.v��x��IY�V�~�L���Ţe��ni�
>�)O}�_��
d��iE��w~�˱����
��;�'z���OpTF�c���ィ+M̶��h��ו�Z��k�:�W�d��ڮ
�!����kO�����x;G���D׺\d̺r�.�잿��8a��F`�`�(�
o��Z�����Ĭ��7g�P��G���P��Ъ��2�ev�t��1>FF���C�7��A�q��U����.��r�>3-�t\��6�Z�^w&�Xc�t��N��A��B���1�~ɶ&���|��߯���lʺ�h�x�3Pѣ���qN�7t��O?�M��W^'^>^\S�F#΋�'�st>wD�Ds���a�S�w]��s�侱׽;S�� ����0�	��������i2'���QF�@�K�����j6���'���G��6���w���r���	�9��0w��rՍ�Ϻ��{��g~�,�!�d-w}��B5j���6�}��5��"���`�l�ò��P��#�۹.���>~���{�"�Ϩ���bn��T�1���v��g�H�cҁ�2�b:^l"�2(�f��=���+�8�����^�g�O@͒(�40[�T�;�}�����5�bm;��My#�Ub�����%����/��S/Z��*�G�Gt��A��E�8�3H{a�~�+����JdT��
wb��x�#{�n���d?�H Kpʋ�>z�[�0��&?�~�[�$�ok5>�,8�%�x��@ᗻK �W��bw6�w���GKٶ�C{;�ʚ��6��V�[���8�@��
�JW�ά
�[��21�07Ӻj�g#���9��L�T�_��bnGi�I����)�!_*���Y�$��2����*���I}sAL�/tPMgu�<�s�>�?�3�!ZJ��+�A����Ҵ����
�S����{3�w���X{�z�@�b�>۰E8[f�ۅ��5|���Cׯt-�R, �}�6�.��O�>J�BI9굋�)�K����WyM�{H�������
�W�bh�h�_*�ӱ�u�d��?B�|�����,	ɞ9嬩FO��2n][���\��f⚅x�����&
��(%?�٣R�#eN��Z1���t�F����J�i�R����6
�{�!Y�q"��6N��ϱ������ �|FO�9��=o%M�A�����Mv����,��?�x�
b^V��W;�LP��s��_y��8�㾯_�Od��#��V3s��Ut���t^��/�Յ�b�������3���v���>���^���=��q����wb&������3|O��'�L�p�f�p��^H畓�����i
�b��9��Op�OĬ��:7�4lE�����փ��
W�d��Iq=���+��φ^�R��>�]b�R����߲��,��[�r�Lvk/����/_��>�r|��ȵ�Nm7���>�t'��C=���|�ns5{\�8�����➽���g��M
�]���F�,�b��~%��5D��<�5���_�ž{�}�𹾹��P贌����9���/�Q�A-"���.�}}PGypLqz���}������Y4��Y�E�U̇����[g�O�+���r�z��������B�%?���G�E14���6�߿�0}��ʖ]�_|G/����?����~�����RMN��}}����]ڜ�o�������m������q��,e� v�uw��8��G�����7r��>0�M����!oO6|���N�m�@���;	}���9;O�F�O���E֦�!{���3���7�/C�N�s){K��S_Iq��98�Y�O�|�*�t ?��w}�tE�$�>����d����?ޅ�;��xQes)��ח/��0��؍_9�LhD�<����gf�0;>��y|ܓ�I,8�2�4������}Χߛo��<S��ú���׿�˫$��?}?���f�-wT��D!�sg���Óy����v����8����{p"fD��D�[%u_a3�|"��f����9kEV��hw����ɼ�'~�ǣzڳ�?��W�L���~����w�<R�-������񼥘���n�:��ǯjF��}&��yf�:�{B�P�yf�Q8�:�r��|�$Q��}i��x��_�SFç�}��կ_��Op�W�O�SLRp�C�����U�����o��c�<O��1>��cc7/�;�>Ϻf��ԯ����Jf���M/���O�����8'zڡt,�����Nw3
}~�O�F�����P՜��1t�	��;�d������jj:�̝��s%ʜ�H%g48��o�7.uh�7��O��?������DT�c����~�o��z�����")4N�9����U[�$���>�^�����W�U�޳Z��D\"�&��|9�w�v8�<��ϑ2�����++���@�-�gs��[=�Y�&�;�z�����Y��g�׫~�҆Ow�x�eUȟ	_�z�a����kJ�%(Ƃ�yE�#?�U蛂=Ip��_f��w%�G��%M�Z�H�R,�<���<���Y���Z��.K������+��G������n�ٿn�|�=�s���/�F�*9� ZO/6���1�Rg7.�*YsbM��K~����0У�p4�Y�A"%�KS�O�~���5�9Z^-�t�E��}B�h�I=RM�<��5�0~A��~��7YwH]�!|{�>iF��_��n�����|�HY|h)��j5Q*#>��%ϒ�.� }�_�X�¿��%��ǗOW����bS�9()�r���3�tx�,2/����:���N犯?]h�)�W�nRg�b�5�������[����b��.�~/{�kW�C}�	�P���g�wmMp��N;O�-+4�Zul@�ʲ�a ��P��k�Ѝ�2M���~���N�?a�:~t�����>oo�>+�φOS�g1��%���?.�3~E��>��p4�=/^��S
�M߱�$4��1��.A7��>"H�O51(�<����W��~������[���9i������G�w5Z�S�ĊiW>��O�	�*�1��5����Kw�%'�O���ε�"�/<Ռ>�K�,=8�Nnͯ��u���[-���28�w�筟�L���Ι����\}�d�>�Kz�]T�G���Ȥ�	~�y~�9.�S��|�e\�y{=����yU���Z�E7��T]r��T�~M��>Z��~1�,����$Q�����Q]���C�Z\��s�k7=*3�,���n�&ȯ|����]�,�T�m�[1��O>`��~�E`��ݧu�KWgo�oV�y�Q�{��Tnd�0���=G[�+W�
�(�_L�~M}�RU�/:l
����>�'��S�r��/:������~�~��~�ǯ��[^_1:P�.|T�_�;��'iĐZ�=7�_|Ey_t6z��.z��<�໙@}p�����
��_.|�mib�����_mr����ׯ�.F�s����8�k6vyU��fH\h�	�q��a��nA��M��R#B��D�n�06�~�vh4���<(n.ꫯ��e^�0Az}�fmER��J:b*u��=�ó˙§��^�Y`/]"�����U�?����±��;�E��)к��t�ӛ�ʞM���&Bӭ/{p��x��n�G.��w��r�>��${Q�_c�+(D
`��$|l)�aB+۝J���
��T~��[k�����`v�%�V�C��}P�9�e8A�ĩ>���&g��#m�{�]�
���ר\��.Q1]leX:!�\+�+!T|�ꋙg���_�Ly��|���
{���7$��d�c���?�lf��s�n��8�'���;�§�g���k��e�1“]���=]�֖��ڰ���z�Zyq��C��	|�<c���d?�k�����3_6;Қj�䜜��1��g�2�<n��w���c�������:?�|�(`>��׌�99T5jL���-D�?�y��V�9�T���O�eH�GM`����ł��~�K�1%|�_�V.Dfr��ZEP�¹���b��':x$/9,�zѹ^PG%�'9���7��(����נ]��Z6)��N�Ǒ�1�$���T8r؞n�ӟ[�noб���5��‡57k�C��ч).7i|�'uus�^sD#��C�çL������CZ�#��
������
�B_7�|$�9��{�M�::�4�'|��JtH�S�唛SS�)xk�HW���R�x08���5��|�Q�0�B^I��3����j׌�_��>�|���i����k������� 1G��o�U�#��'�c���b't����(�	��e^���<$}̷�������uc�(6X߭����{�{�;؁��c���$�C�hkA��12re�Ƀ'�}����I��(Q񃇻ϋ�����w�����B:��l�r���P�t�4��X�w��z��
�]s�����%2�U?e-�s}t9I\��h/�8�|X@&�+|p����>|�H���0&����L�m^n��X��(��h��U��s������H�Vt4*k��ɏ�8��'�)>���ѹ񳞝g\�=BsM���N΍Bi?���E�ۿ��v���l��f������G�K��n�X�����\��=9sn�5�[���ר���۹�D�~tj��Μ�vO���Y��3���L�<5�vY1�/��Nwո|�}�7��� v��'�2}��T1� ��d�kl��)�{c�ߞ�z�L@��Ey걇�o�� �W|`���@	�����׳.�I|��1:�\0�z'H��彟'����O���(�����O��_μ��O���q�G�:@�����''��Cؠ~�Ͽ�8�v���+��{�6bw�q\����?����|>�8k?���;%n}TF#�W���ڏt^��/i4XvO�맟���=���A���\�c&U�~����	򰢧8����(f#?$c>����w���x��/�G��:���xf|�Vq����̙�vb>o�G�J~�ǽp�"��7�1^�|���|�=��Ջ~c������;������9��������{C.|��Z�p|�䧳?N�<������<�������]׬�|��?�����[�?��~�\ჾ&>�ȅ�P���	�S~�����3�6~��T����ҿ��q��Ɲm|��*�9�dp���ݽtN�_��_���x�ޭ����;:�u\v�i�U�0��1h\�~����E�q��\��4�����h�����?��P=�#������/���w<�y�v�� �� ?~,G����PgO���\O�rS�y.������M.��;lt6����-GJ:��w�[F�,�T4��
ݴ�󝐭��S��d�N�9@)�rg|'W8���B%���v�|�3�̡)��_eJP���b\��"PT�z9�_� ��^�6�o�gg�@G{N"Һ�������u�r+�
;���#��Y��y�8�����'��C{9�������w֯"/7�s�A�}wp	���?�б���*U�Rƪ�Yzk'���c�8�[��>5!����]�J�x��d�ȥ�ȏ^���zO.q�6��~�_�[
2��7��G��OCF���m~Ő!�N���3��t�Ԯ���YT�SG�a^.�H�;�Ѹߘ�Mq����3�_��-���Ѡc]�y�=�q�+�k~�iZ�3��ys����O=�j�
s͠�i*�7rJ:X��:rn:ݹ���zH�B���������O�C��\�o����8tq&��Oh�^��4l�!�W��WC����oG�v�3�	��6��q�qvұ�vH��\=M�t�Z�����u2��!�4�:?�?pq��$<k��/�rs~
�`�&�Q���f�q�m��^4���ͼ�)���q�*>�R9��1�9������\���RD����F�����x��T��XE�m�h�c8/J��% ��q'>��y/�@s�|���T��w�zK���8!��&<u&���NG�+�a{\����N�?H�N�x&f㦡*��4�?���Y�Q_���%��5���:jo�C���E�����S���tl8�_���4~KNLZS\{V�2�O$ڦ�6.����<�E~���U��{���g��9n��8h���n�]�\�6��C���?b 55W	�]n���\X��L��X�K����nK�{�?�>{+�|� ���7
���/����?.�_�S�S��g����u�g�ށH��;�I���w �_}�>�{��U�+�N��\��|�t�
t���U�Y9�K�V�W:�F>�F0�m"�8�1o���WjQQ�ps�/��1�G���rY��s��?�t�Qٓ%��v�3�������Ϣ����\9�t�=�O7��A`[S_���R��\H�p�+p��|�̓/��h��_�礟�
��Kqή&�[q�L�u_�8�8#C��/T�����t�a�_>�
B�	y��U��o��K�҂�p�����x���3sn�I�f>_wz�t�I�!(���Ĉ���d�F=����\?U���]>[^��d��E_�IC2�Oʳ��NsFd�^(a	]+��>���K��v�@=�{Ԍ��(�8V�p�g踵=�����8��zF.e�y`,�1P��t��mit��ށ��i@����~Tn�{�惧$���0P(4I�5@����!��7�m!ڌ��L:-��q�+�G��0���b�˴A���~:'��3�8�'�NUh�+��ϠT<ڧ^����P�ר���1ln����f�sn���x���W���|�-$ig��=�N#rI�|l��Bi'���մ7\��,��#�"g���:n�L{�F��ݍU���v�C��;�"]�v':Jd�2\N�_-���OE��l�qJ��6:�I�x�n-?y���O�{7:?
�U��v��
�I����`UV��9��+jz䧷��G_ϔ:}I��#�3�"r�\N�� ���gs�$��N�$W1�4 ��G:��\�q��#?�\ZC���FG��a�9�	�#?�or�5��jWU^mg�j����=K�7|JG��Kni<���w�S���A��p[��j���~aJ���v�0�#~�?��p;�cl��PkT���N�u��g�K���rsrw�X��bL��a�Ԟyn�)Ǐtz����ނ6{��]��ͽ�P��O�d���Q��>�.���~�+��3��	g�m��9��!δ�ٳ�}w-A�*��.��#&��.?�*��/?
"��#R?����{E�s�B�	������_�a��Ov?����
8����p�N�p�(��T�Gy!|���g{?�K�ѩ���U_�\��|�Ϝ�^��@�����~��� �y�C��~�����,?֫P}-�':�,?�][��YZ,A;�P�"��H���YN��6H�zBѱ���H{��w�vQ.�ivh�C`��{�- �8GǽOdI.sڊM��G{����{	���>ͽa���?y9F'p���f��O��sv��w���ޘ?Ꮛv�\Ӟ��F�ao��ˑ�����r����nt��1�s<���{���܁@|�Qh/jz�����{��I�O��.n�_��q0,*ře�<,�t��#�� (�f?\��������貤cl"�/��C�p��=�Y�O/���?/���J�oL\�G��?<�1n>����7�?�m%��7��x��a~��w�g�tȏ��{��=�F�8?g��O�����u���6|`�=Ӟ\����<A>��n�n��]���}��9Σ�ss���F	���V"��Ἥw̹��u��C�9���7}%�������9�]��_����o�ծ�S�]Ҫ���?ۧ�yx�l�M���g[_�M���
����o��g�����ku�g�U��/.���zG��_�{�\_������9:�(/xʆ��`�w�u��L�#?���P���d��Ƌ��}��V�n���ҹ����֧���U�2q���7�8���q��y��4��H��_���ώ�;�NJ'l��p�5:��m}z�����޸������lO=��_���җ�j?�q��3>F�nq��Wt����Ջ���T��m�:�޶H��z���
��Zr'����g�;/:�m|ħ��^r���{���r�\����Ћ����?ђ�ȱp]I�g��s�����U9����+��Y��+�
Z���08��M��&��ϑ���������X.t�n�Lvn6��A痷o��k'�.:�{'�8ѱ
���ބ������g~Z\�ϊ���xv05;q�֑���?��:���{ѻ�<G�~a�5t0�#��K*�U��HӶ��'}=;ce��L���>���'�p�R�O�*����w��b_��;]pLv�&����s����0�b)y�)oGg0M�䂶��e�ǜqݓ^���:��pw�!T.O	�į�����@V���%��kL��J���O���tC�~�Xr���=pf*��u�i|��|ӿ��.�O�Y<ߞ��������naЍ�g�g�+_'|?�|�}��]J��}aR�)�G�F��;�����釴�IQ4ۉѱK
j/R����g��CgW��D�R����||����
�V�\�E]�ߌ�W:�Vt����J'���v�sų��3������~W��ϕΕ����gz_�Ǹ�|�M���?��L���я�E_��|�L��|F�&6���Y��ss��_������
�?k��W�O�	K���I�N���ؙ�>��������ʭ8|/��,/~���Q^��`*��=+�6���V�0�n��bk��}z����G�Z�W �j��q�H#�*����T���)^B}"D�8��Up;�)��N�I�
�]��܇(�oؠ����ɋ~�"/���\'����H��G�ч3pңK��U�����n;��|QT���R)|�t�˗�y�Z�'w��9|�x��؅kc�sp���q����W�}ν�lNۅ
��ܼ����W�L�.�M
v�i��̿��Vr%��A>�O��#���y�^��2�&>���h��s�E�c��FS���Rg}�D0=9�K�9��"jX��@�_��б"��w���LU�p@��
��D�}�
D��O��d�
��/^+-wN��4���|�M3�&	6Bz1|�E>'���!�HoF����&�g*�G.��=���ݓ�	3b���D��9�������e+&f�%舊a�"�q��؀2��[�\�`@�>R:�߽��pu�^��L��p���?��Q~}?[��8�<{q�K��LS9~�e��� ����dA�y���7�����C/����w��z�����Q�5��z�G<�_��Ǜ��Vn��|��j5�+G��Z �;�ߌ�����C�"�~
�b�ʜ�{-�=�.��?󕨼�N\ꙶ���b=�-t�Z���>�X���ٻ �R��{3zK�gä�����Ht���7�	�)��/�`_N�����	OO>��c����?�<
ť���όA���\~Ο~�Q�y P��3���Q��8�I�w�_w��[�	��|�u&Q�F�-����B���++�$����=!k�A�3�0b����pޞ+|(>;�><�i��������l|�F��ޏɹ�?Ϟ�<�(��?b���p�~�_�]<���O����Op>��?k�Z߻�Օ��C�[��iP�/��y�B��3>ǏaC�SI��⣜�&�{*�,ϫ���B�]���	w齿��B�Q�Bt�{V.��lӮW�:��%��a����
��<m�so��%Ҝ��͓4�a.���ᅴ=��TXV�ƙ�ʰb��A����9�����'�=�����Ā����]��p�wτ^^�.!������+�}�G�g�)�d';��<��������Xզ��b0�-�Z�����~6^)��3}U���#�~�78�E��Υ�#������/���^
)O��~�?���՞�M�P���'o����t����s#����L��_0��':�ݲ���~��atڻ�|
7v��4���`���U���l�=��x�j���Ɵ���Fg?�=|�S*,W�)�F��3��F'~��I�*�KޔR׈��n�)�Ev��ʜ=���8�p��u+�������؂�vi�bv�WFP���WL�`�|v/Ņj�Z���e��$���Y[g�&
��ɞ��W���^�������&y5���I�:�56����v��� 
��p{'� (���8�H�u1"�B��{�X{!�Y{�T^�σ��}<��g�T�a���7E�7$:Z���u���k8b�|9��ߨ쁔�=맍?M#_㿘��$b�?z�{z�����N�K�c���`��g{}Сw�h::
�?J�1y�r����Oh�Ӈ���O��X޺e�a��;�7/n#R��=~ȕ��O�+��ȩѰ��s�M�[�d��C_��n�?��u�^�T�?�Կ������S���Cg�~�dgW����u>���{\j�����^|0?5���G�Y���}8o<�8֐�>:�
��F��@�蔋�����^2��@�F�w �KLֿN~�E�����8���D�UC�<�~���s��1���?�y��'}E?|Ȼy��ʩ��w�t�W���U4��ZL�����:j�WzL��e����n�=7��Q�2�vV���I�7ӌj	�b�z�3���WN_��7>^o4oN����W��YKƽ:K8�����s:�f?��p=z�q�{���l��N���b���"���uC��:
?���٦p���|���d�|��<����Ξ�{^n}�zL'<˵��r�@M�[�l\�޷����"{�����uW|���eKzL'~�_�c[;�YN��;�fz��7[oo��ص�۾/V�Jg�xW{�u����y�u���=��u�b��/fr{�ga"�-�%���3ݭcɋ����j1g�f��1;T~���ٸ���C���,6��5����-���|����������ޮ)�n���=�k,H]����~4�z~1.��#]�(L_ɥp�N��gHy?
��kr5.��:�Cv���Fg�3|6D$W�Ȍ���W9�ԯ%׶�燴��;�y풫��ڋ��<;f8�ȥ���Œ�ao�i��ׂة��y�g�̞
dWH���ȳ�A�S?��s����F��:Ҝ�X�@���3z?*�G�v�4��3K݉Ϗ?t^�ӻ"��O{Ƙ��B8o�򤅈=�\��8˩����?�}i����_Y��ˏ��ъ���~煨/�_]�i��Ӎ?�#Ԇ��3x�_��z�����?�	ķY�}��B��ԦGt���;>&�~x��qP�C�p�O��"|�Y��}�U�@x��/
���?��`��c9������]��j��rK�ġ/���m����ʷ)��yb��L~����{�PL��{�~�#* o�p�t�Ϗ��^�j��u7���_`
:����O��8ӧ~A_n���n�v�n3f'�^��q\��/�-g�Jy;�/����4zw�O�d?���S
�������w�������j����p��\��O�t����)%E��~��~�[M��٧���ሇ95�4��ɥ2nX��ggH�^��Kx^c;T�t(=}a�=����ڤY���`_@��U?���Ŕ�㖭g��԰q����um�P��\�����]M�	�R�o,�]�M�7��Q�\��s9�T_=�w�
�Pl����c���4vO�̓���5���s��s���k�G?[�l;&m�&W�X[!�\a&����ى���5��@%��tnF�H]q+}��dz���Z�+>m�ùvA�u�q��(K[��=N��"]��(?�w5���Xz
7�~��Ⲕ����7���^��VLC����L��U��;�&o�����y��L(;,��<?W:72�9d.��ͅ��1~vO�7�}Nfa��vE�d���y��Q��B?�1���Q���[���?���g]A�b�
��|w���be�o����Sʓ~�*AmT�v���L�)�%�U�r�3J�f���g�+$r�E�i���p>�.�����y�{�{���Dg-}v�G衏6��>
'�g'��ar���Bg-)eQzc���\���,7l\��V����a����G��a�=���\��qK�!"�C���|�:F]�'[�Yd'V0:wf��o�ڇ��γo:�6C��G�����#�Ww�����ⶰt�P֩�s���c�&���b@*F�mQ��2xJ�m��c�R�9��=��?D�� �I���]A�e
VaL�R�{f��X✛��X��с���;��B�l��be����#�	�Y�D�l�6�cΦ�g.�%Z��[Qw4~��y��;NŦ.Ve��o������O�t9��ؒ4��s�&�5+,�Ӝ���ѱ�Lc�F�ַ��(�):�?�
�g�g��m~���п|-��D�K^ž���f�'�R�e�1�<}��™�����gA!�_���Ǐ%]�#aR�b�}i�Rրu<�����ۗG셆���:������*�����Wߊ
�ǥ���v�	q�o�G�c9�d#1M�5������g��'����)�Y�4���ZO�d{�¦-��\I��T\�I���m��-��\�/���L3�L^c%�9�H�?�S�R��|1��T�#P�qJ�[Q���1�Q�%Xd+GG���l��x�o�9�؞�)��Y9K�N�
��s�wv"铏���8�Fa������N9u���#f��n�4f2G�D�i��кv��0ʊҗ��|��cm}�?r8�1yMa���wr�p�����&=�Gͧ/_�'ի�����s7t�~�˓v�"�suV8��5��XƼ�
�Gx}%���S��}2Z+t���sS™-�9� iX��)I�DK�%�K4��=:fi��� u�+T���ᅢR�c����C���㼫4���L��ۓ^~��C�N��oss�ĔD�� �8�\�:�iJ�p�x?���$���4���_CX�Q�G���,�C�RO��L�bh�3}
��e�w\��H�dH1���Ra�ءw���/��d�O)<�3�\���f�8gB�M?�W��g'di�u�
��\�>�1�}�NOM����7���KP��|�ֈ,���;��
x��K6������s?�~z�L�b��p��L���{�[�|�j�D�r+Y�j��u�{y�1�Y?�g
D�O���W~*�\��/�����;.a؏��{{W:ç&{��ً�`R���a6�����mo��t{~�8�8"�|`F��:&)|b��C11��`D�
��5V?���i��#Q�=�T�ɥB���|�)EJ�l�I�be�l/m{!�}�h���?�5���^�%{��*�`��'�7;h7|37�?�y�8n��^�Q����C,��~a���[e ��Bgo�����?�qҹ�w�RP��|S�j:+�$�������[�͋ޟ��8vF�<I/���H5|���z��� 2+Z>��ё��ce��uJr�s`.��_����\@�yx������֏���ן-������Q)���+�x0����,)��<��ӯ��-\?����O�X����b�^��wW����S�w����lܸ'��o~��o��s��9~�?�_O:����%��c���Ò�œ�?���S�O\iȞ7?�0��R�	���}�2xu�q8�����ûy3T�z#g��
tr�<�pf?n��_�>��U,��=����;���@���|��d���??�s��^�mь���^dw�7L��&m�\�˫t�������|u?n��5��Og]}���[�׿~�L��~!V,��Q��8-��yCV�a��^��(gt\��E(��8���9���u����ő����8��I��Ζ�:R��T������<�r.&�rտ*o�Q*�,�g�aB���o
�U���=
���)o�F���넍��;�{]�o��3�cђcrݬ[������'�Pr��<>6���?�{��?����uB������u��<��E¹q�IV�]�k]&~���rm#yv�����X���c����X�1��'[L��u��E�n�?7��?u��9��`���[l�
�}����1�|��+?�3�y,�9.:{��=��
��܌���g�B�k��6ਯ�R�D����,�t���+\��ߗo�&o���$o��؝
���=*eEr5���`��z>a���v?H׿�k�\��Ɇ�b����q�Й�F���ѼV��Ɵ�3�?�'��b*{��?���W-��g�q�Y?A��ɮ���yl?��د?�_����D�&�ղ�X
(|��v}�|���n�I_���6f�#΁��a`�Z���xϧ�+>�s3�,�G|.�f�G�8��U_�y�$��=��x�%o��,jpN��ď�_��<u{����y��K�3��cV�%��jcj��h*��PV�(�;�Q�y��m��w����m+O��]ru}�����gO��	,��d�\�be��s:�_N�W����Y���U^��G�&<x��o����Լ�ı�y�I#�BDe-f|q�łX;"��ί����n�B���������댽@j���Q,"�	׫SU���-�>R^�U>GO#�T;b/X��1����X��W;����-��T���轗�O��a]�:�S~��t��>���7���M��lR짎\Kdk
��$�]���Qz3�k8�gz��3އv���N8{J8������Y�9T���ϽZ�>銈W�0Q�&�!�"�ϧ�X��4�^Xx��/�����!)��Y�O	�7���^0���y���$/��𱡂�
F_�Y��׿�~��2ಈ����x���%�ٳ�8�?:�':��Ur���x}v��򞧗(9pVt�!�Ԯ�'s���f�������/�����������c_��k�׿����?;���v�s�W� ��X%�������o�w%����g�+�zGW��!/�y�iӽŴ�Y��M��_���?q�?X��4��1v��Q�Zr�c1I����O"L2f�a�L�D�����2t���/�:��O���Vo�<Ģ�u�B���иjp�棚_��S�>�Ϯ@�\�E����>۷��V��K�J��Lݬ�At
(�v����b�¹�:���,�~�M��r�7P\�����~z�~����?Ƶ��zl͟�S�_�������J�3�l�ѯ���s���f��\�dW>�W<o���'
'���!_�aC�Wr�@��/��-�t#�9,(y�ʫ���t���׭n��8�kWL�Y�	�?(W����ч��+*��ᬏ&/k��ɉ�O,�_JO(XU���W/Ĕ�恎��u�����v�y8
�/o
�Ŏ��Z9�O���I������&���He����YI/��R���|6�zT��"����>ҙ@g��i�"r�Д	}��7F��[P����J��'e���D^��\�ا�o
�sl��I"TN��G%��_9��F�>�O:�E�� ��o�@/���vx�{t�H�yJ>b�Cj.K��o��<E��TO�k~s��_�����*��q�w��c��$��Y�Ta(�~BЫ�f+��"����w�&:�£<:�I�Y��3O�d�#�nR��;�4Ϩmr������g��h�_?Y�G�M�x��w�C�]Q�gt|�+$�9,쌗d���9|ȅ������;��3�q����[���7��5y�G^�ȣ�ݲ���|'��R�4�p���>-	�KL�&�Wޢg�d�ɓH�>��T�3<�{���=ck�p6Ƞ�W��k��~SNHC����_�|�
�k.��<�	�3�4
"tn|�n����ÓRL���~j����g�p���3h�3�<bN���~=��Wҹ}�����ɏ�8h������^|�O[Z0.�8|>ϥR�MϢ@�w:���{��y��	C!0�!Z�33K����ϹGo�I��^����y7��ʥh�W�*q���s�Tl���L����j��s�iUv�f�����,���ws�r��NH��MY��3p簟����U Dg�=+����C��cH(��n��ϹP�?�9:���M~�5�{�Ii�F���8�/���s�'�W�ҋqo�
"�"X~ai�N�~/€�g�278W��bw��V�=��$��n�M��ox#��4��!�P-4с�ʹBg �ZPI�N�U�u�I?�g"�G)�#3Ŏ"�p��yQ��7�'���g
��4�޼�'{f��Usk�#��/߳����[Կ�?�)���ň�1bF�@h��v��w�)�~��Z��B��O|<ck����u㞿j�K�cMd�DŽv���ز�ߖ�';7Z�@ �Ǫ��7T-���L�+��~Q�>�y�N!K��C����*�;j�P�t���tLW��9�ʷk*���Y��3�&�c���W��\}�b�{���Qy���g*�)�-�_����5:�_齴E���󒩸:��X���
?�Q�vЙC9�1�b#Fg{˰5S���c�q�����kDG9��O���Γ��~a<4���mϯy��~F`��e��ڃZ�|��'�,j�)O��cI��J��,64�wi�*�%~�+�X�G���,���X�;��b5���9��B����3�_e��l�gF�d{�W���/V�{aF�흷l<d'�o8Bd�����s�S>ӻ��_���r�P�B�2!�5j`���k9��𗎹�%�7�����
��=�iC>	�g���"ç=��q��-��i���/'5=�E/���.�i��"`p8@���a?��3�O�^��I�}�|�z���2G
�Zׄ%�_��]ri�g����cg}?�W��~>����*�ϱz:��Br�B���V�/;���~��=^�
Q~������_��}�')lp�?�łxR_�p�G��	�|�ɍ˛⧺��Y�\�^�����Y��|���J��A�����X�(�@P^j�#�%��^��8>���l���b����PL�rz�w߿"#H���#a�D��&W#C_�w��?Cl�3��ˆl�n�)�
�'�T\��D���6�Cyl�>�r�D� �_�F8�g�h�d��[p�/6����u���8�e'�?b�P���ݯv����?�/o�c�u�ƍ�ۛo�Q�o��y95e<��ygয�uK@��*�Wx��Ϟi�~��.T�Z��
=ژw��O�Q��;���Q�:
nI
������4��<��j��'�W��N�|y��z�ƙ�:�Rz�Ҫ�0:�Q_�'�['��亮���1ĵ�q�[F��l]q��g��O8W����|K&�u��aK��ؿn֙�X�Fg����pŹ�Xv��RD��*j:��dg��|��t{���\�ֽ��>��Ŕ��q�b����í�[�y��z~I�v�k�I���O_Y������[���oݻ�.���u�+f��fr�/t�b��կ�-�Gj��c}��uo�O_!߯7ti�~σ�R��I9���op�v��:,7U���7g0!~����7~"U�δ͞7/��Qs�����+>���ΙM����B~��Y�{͓�y����S���/�r)Y��z�����=s��'�G���
�?�d�ʞ��>����s,z8P�7{�p��B�۰�ڳ��x.���	��@�i݇Çƿ�Gm#�F���:*a����=���XLaɎ#|��8�ҋ؅2�k��A
����•�%1@E9��F��m�|}t�ТcA��'��0���`y�����~��G(��8l����'_���K�X�P�~­X������_A��/t�V��I�Ӛ�����Ġ���̣�(�TNݚv��Ε�X�$:���ü�^��_���Q
�J��'�e��I^g��O}�������H�_�)�း�Q�v���fz,�K!~���1�؍?.���G1Z�O���S��=5*��_ME򸔶���O.�V�O��
g�L�Q��N�X����@���
_`�nQ����K.]��n�Мl�x�4���7KƏ�ggW-<=Z��K��1��S~����#$o����Mt�=�_������x��`t�]��<cH��kԴ'Q%�¼�w �cC����������Uh3b1�����Կ��Jw�f_���g�1�׼k��zz�
��v��)a?�	���n��G\���
��l����� •�h��+����<���:Y��@_;�/@��'�-��=3�ʧ��<r�UJ���O��4��y��6�E��5Pz?U2�Kx�~t*�O�Z�c?��A�X�b�2+=��:Z�V�i�{�nr5�E?:ßo@�@�۰g�P��d�8����d�w��տ�{���K�ҫ<a��r=c��QC��U2���"41:=�X���߮�6X���"��x�<�o�π%Y�������'̟jD'>��"�R�z�<=>��T�g12O}��� 5���͔)9�{����O�?�~��+}~�dD�����q�R:����=�G�Rt��pȮ��[1z@�U?~
�p�ۅν���G1~���_���O��\`n7��r鱙G��b_�3��3A>G�Ӊ��K�:�ߥ/����Ľ�3k�?f�[Ȯ��>8��:�[K�_�:s�ܝ��`�ލ�u4<�8f���3tK[J�:f�o�`.��^E ��ځ,:��ϸ;V��E�����R�|����:�n+�|���V*o����cE�,&y�!N���N�͜B��Yб%t� �V����\d�'/�C�*[���?��5�?��:e7���.����'�踎(�2��VR?c�-��w��z��h�������h�]u������J9�ɨ��֩�81���`H����_ �l�~g[��G�V��A��sk��'~�k�5\sv�Y�\��(>�K��b��@c\Y-Γ˘��<�<��Q�9�
�i@G;bD�,��^=�s���צ��/�>)6�.~s+��"�^E����#f��ZJ��tmڳ�:�]�Λ3��9�1z"�؋�a����|��ws~^��p���r�k�X���$bmҰ��ѽv[;F�&vd��\�G���l��y�O�G'�\71�g���ݕ\U[Z�A�����'�W�l�w�D�����=O��gr�w�p���OuUx}h���g˯��X�Bm�J��
gB���re�Fj��M��_�!��U��ѵ�o�^�Le�U희��Љ��ʫ?|�'�K���*���Ux},0W�N������N�|�Aw�ӏ��=:߆Ot�u����F�>�s�\�՞�4>k4�߯��w�g�Z*�
���$�i�������p��˱ۧ�k�N��Ϊ�6O��C5�����6g����~�����zW;�����3�ה�������g���u�}Q~ŧ�芟�6������MF��<x�i�_Us���ِr�׹˲=����_���f���7�4/\�iE'���&�x�.E�9X��Ŋ͞�'�l/*=��͞�uf�g�щ�
J[�D�t�k�q&�=�O�^�ˉ��_��Lk���q�:�]S�_��r�o{vu���z.uL��x�IP<{��z�SBư|��\�<}
��|���[nr�^�)q5�~���v �п�u
\s G'V����_��߷�X���՞�O�~?�|��	������0}]ޫf�
J�u���5����؛o��)Ŧ��j�镟3�h���\�[l�|R��u�p3��r��_n�cR�M�w��Í�3�M�7�i�_u�O��o�'������k�$�+;����JY"_c�^����|<���
Ϟ��5{��u���k뱭���62�g�R����J���-5���p��6�n��Og�O��@�~@�o����gW�^���oƍ�k�s�o��|:�o��}n|Xy*��6_�`��笌pԮ�׼��q#����_Dd���m�l�U�wt�9|�?{v���ٳ�f����ç�R���z���~Ն�pK_᳼����{���=�\�����݌V0�_����K��>��A��y�?W��NF���ø��̼ײ�i%~�S,&�����������u�o���DXy���[n�0;�[Zoܘ�(�Χ�t�?�k.R��>o�+��]��뼼�p��>��@t�Ɵ����ob��oi��=�����븱���qu��ƟA}=�i�]��\�wv���ZD��uc?=���~Qa l���ƻ�I|�Vx���k�Wm�4�f?�(]��)k���|��çj�獟5w�+:�������ټ<�ܬ3}��6.
�J6��5{޼?>�|�u���<�^�똛bD�:����̞��˪���s�%|f�:��r�<��yjv8��(�h�K\
�w�Y���7�������ƉMe� P?���O��B�/��&��SU�}�]r������'~A�׊J�S~�ƍޙl�����>�r��W5}���'���B��D�S�hԿ��8���w���)O� �ę��L��#�4�;]�u�r�~zAH���R(G����]�1�2�i=�$W	�tT!�v)m�||��?�H���Ͻ����S��U.jʕ�������T�E�z�����(O������7'��~��1��/%|��S�9XJi�K&
M�MбYsҁ�r�P�XxbRy)�ԇ'&�ZQ�U�i�`)�l��a�χ��ow6S�;ɋzħ]��}��<v-�5����\������wȿ������ni��:�ͻ�dޡy��4���(9+�x�^H�O����w��P��svx��`�}��7*G�X?��'��/.-���X8�u��=Wx���`�]�+���b~����u�?��Ϝ�Wܮ�)���+1�u������+�F��)j�����
������z+ܵ�t�G�z�
?���x�������V>�V�v�zOd]���'*N��O)C5p�5:{�Zg��=V�%V�U��
�[�_얼�;��T�_����'g�܏4�Y?�����{��t���Fw��-��T!<:Ⱦ������bb��j?��,�G�Z�7����C�b����á�M5\a��*�v&QH����ߏ�y}�-:F8w]qCG��4	��6�9ďB#���A���9<��X�7A�qK�
��G_J,!"���]�ٱPG�Orl�KQ��~�/�B�� �����rjR�x��YI�E :(��e�=%��j8��kx{�U���H��E�������@�����Ĭ����*Q�}0�\�1L��o"a:��L� �í򗘗��w�|'o�236g9lP؄ra��4#B΋��A��ɺДP!��bW^���;��֙M����7e�g��ݫO�C�3ۓ6�Q.�R��!���G��룊�A�L����A�
I�Θ�z<g\f3a��E�Gn��ٸ�}W2/~#����Ag>y/AZ4Z���?�9�r,O}�g�
|�P����	"ӂY������aP���a�� �}��=�/=
,ȨR��cmM���~�|i`��
�Uj����bZ���R!���C^2ƹ�0����C^���e��kJ��~�MW)ss�WY�Ȋe��{�b��B�_w�H�{(�s/g��9t�p�us��=���;̓jM��?��a1���3�5=~�E��_y�?9��
���*�{X�&�i�?�Yy�F1I1=��2s@�&����Ț=cI9��U�|��#�`�=^�v���/f�-Z��
��r13�2�H)\]r�R�5Y	x*�*�� ޳wk2��|��o���f�G��M��+��%!�����N��a���XMYx:���k��ħj�ӥ�G�E6t�𸐗�Hr�]�=�p����{o���o�?c�I�)~`��	?�k�$������_`�?H�׀����O�O5χ�q_�P�5�ח=�9X�����R>�Y�Pg?��|�׋��%E_�!N��rĞ���g���{Hn��0��1"�d��gK��[�CG�w�`?:�U~��V�°���c�"Yj��ς5�/��ޞ�G�rH������;?�7���5�) ��kg��[��9�X�Ln��r���q�2	6��Yy�Q�� ۙD�.����'>d�X�}�W�[_��G���Z�Y�n�C';��g�ʷku�sݨV8_�ux�1
1�k�O��q@���6̚#�I_x��{��ޟS����~C��ň\_y��{�J��gN���w��O���G�����ʗJ�ؼ����F���h�=|�o̻�	��V��r�������p��Q�]����U_�/�T�� �wu����싍�B<��-˷�|�\�'�Q�\�s�]�_�EPT~�e(,_fԧ�X-��L.b��v,��C�G{ ?����?��b�'�6\�������|��uo"l\�Ϊ���
��#�#���%S�|�6�a�c�����S��Ԃ�#҆�y�-j�ޑ���~�d�~��x*�O�t�8�Ny�
X��':^1�?�+��x���Cv~�I��#ꧯ�jp[��_~~��S�:W�12Q���I������	kǩK�;���M��y���Iޯ�G�nс�߉��M٧��k��3|>���'|�^�bt��r��ӻr���L�ݢ~�
Vm�1�㧔��I��/o�7UN/
;�J���H��`�Z�-Q
Ռ������-1����ٓٺ!�[���ݢ2{�^��(���&L��#f��i��JhM����9�j����拍��w�S�]�KV��&z�X��}r�LH�n�y���e`5�:��U[ṽIө�~���a-�y�z�����s�n��9d]�6U��I,	3�+Y�{��rM�'�$�3f�N��i�S���
�s3�b{{�W�e�T��:m�:a��|��ya|ɫd��{�4(u]���)��(��y!3��)���37����ᠤ�'W�����xX������)~B̵�q�U�����y��k>�����f��m���Z"�
o�5|�����lE|���/�Gg먛<W���b;(�g�6f]�s�W�D��l�������*k�H{��p���Ȑ�ퟙ=:�}�g��x����]����a[�-�x��D)���[�������k���S��bT���Obn�9��<2;L�d�>wK�L�rgT5�~Cg���I_#>���}�`����k�d��;kI���ݗpX��z��R��~�Wն�ѽ���O��iiݚ�OW~������]8���ciy�������>ۣt��5�zĮ=&�X�J�n[J��^c���r3-`h�Vݫ���+?a�W�z���$�k�>W{�;�tVO>
7n�A��=|�����9�O��_�R;~���H��@	� &�1}��3;W��>}/6e����p9Pjx)&����#��?V^>ݎ����v��T�T�.p'����1pw�}ǯX&��쇼�}��%�><�C��ٽ�����|��9JN��n�gg*���'�1D(�9��?�
�b���
���]vK)��B9|i�S:��$:�*"Gn�y���G���|x�'���~:��O'>\�	[L�uH6��9R�yG����Y��[:�Ɇ:&�g6TJESm�M!����뻷����R��Z^��U
��{���ώ��J�~IG�L�t��}N}�c�d-x��rxRAj^QZ�:��U���|��6r�`�PT�N��3(�CIZ�b}�N.&Q��"��|���>s���}�g��2���rL���|�~gy�\��U�v��pc'_N;�����ol�������'|�ɭ�\�;�F�I;XG�O����yzd�>.j���DU$�f7��0�QG<��G�>z����)��ҩ�~���bk:sJ�чC����i�sg��|�t�WD��Ʈ����i��T
gE��+�`�6V�-���,�A�P�F�>�h/�,��qʭߊ��B|��?�BӚ�7`��-��F��ʱ���\�٧�X�i>-p@��"1��u8@��\m���i=��QF!� ���ӭ�\\�U.�G|���N�L��O��;Y��-M���Rx§�7?fn]o�D�ꖿ.6Z���ej�(��q�9�<jt�S������?�'>w��z��{�=��9�wj��B\t&��:
;��B�6�u��է�2����$�[P?)��)&C���{�y�C
�~���>~J����c2�7$�'J��ۤ�S�q������.6E��(1E�[���%�I��'�+�
�30�9�|*�E�pP����� ����Qz�
u��S���4�Ŭ���X�j�J��Qs����h��#)$r)m�q�:�9�u�/�^�5���\%VF��b���p��Q
7@�^,-	�\e�\�[H|�"9�M/���j	[i
��AycA�n��.������ңr��9=��L��3��/�9�M�khWx�_���Xp�����'�8���\k�~������?�2��M�ӗb\��B���g��L���,������=3F��P{b�~fn��rϡY�c�����������j�A܌��G�a���;��Ck�gOI�F��X��d��ç��O��]�0�>/^�GHU[mۅ|�����Z���Y�8?�s��iE�+M���E6���?��7�^��>;���s���d�q�C��#��Q���xN^�S:ޱ��0:ڥ_l���=�߽-�8�a�z���E���I
�-�U�t�7f�n�|��
�I�0p�
;���
�:ätkڶX�.�7��Cx�3h��`��>sf�n��1��Է�Y�2���1�ߝiH�!8���%'��3�Q��.��gΗGL�I�c�8���%�3V��zzF���G�}y`b>* ���u6	u�¶�>V�����X�C4^YϵW�,K=�7��P��	 >sC/�ł��{ҒٱF(��^Q�=x�Z�Z0,|>��G�'���O_�g�ϑ.ƹ=u΀��Hy�_�P�.���㎾��|�KNcXx8��N��s]�3�\Fn�)�`�0��^;>5���dyAPV�+�m$~z�!?KxRn�u��[
?��@��˩��%�ޗ�{��.m���k>���F�-�{�@}���t��i�g&ǒŸ�Yb��8S
�H᳃Z��|����nM�x4������-�X‹o�~���9��1�S��~���I)��0��Sϣ?ߨ��L��!X���~�I���n�=�������)����aOT�
�'%��s�U~v}P�|.��ٻ5"Rbh|�c�e�F��j/D�q�?
��%���pU�H�o}�B��W�g?����ψ8)W�{�?��]��G�ITg��ƍ�1l�Ct�O�}]�V�����(����>Ɛj�!�̈́��_
��[���>���!Kv~��U��@^�<�<wd?����rAW��(�`t�K�h���#�ߵ�v?`���"�I���J�	;V���Ϸ='��=_m���g{��d�m![�b&��e�Ot"�m����_����7T�h� ��\ff4���O�V�\���?�=�y//.�CI];��3���V���3����$�;�r8�py#��?:��6�{`��c�9��O�{�&~r�Vv�k^�~�!G���\�9|g���<R�rY�O�W:�<X�)��4E	ZV��Oٕ�����b�kI�5�c8t�F.�H@�ʝA��_?�N����~�
{��ٳ~��|l���w/S
��y�w�\H-.|(�zƇ:�j}���=��㓎�͙oxt���v���I����"Ù6^�V�1�����1F;��b�{��ϋƟg��s�w�4}����V�x3�q�e�\���v���߽�if��3�߿����M�W�t�(o����4��}��sƒ��@��a?���/�'L+�;��M�x��C߇*���?�wo�A�R��IpJ֊�Pz�n(�9WW��	i�H��S�6�����Kt�u
ڠ/�>�������_~yc�Z�-a�k���:�~���N��Q?�`��htGAg���I_@��O�4�A�g���L�x���ҋ�����1�,�
���!��=R����7��R�-?ç)�g1��)�0|`�\4�,��w� ��Y�����xl���
䊍W�_(9�0�cy���/6L_?���NQi\m��Y���������
�1�Wb�!2�6/��͜F��{�,��h���a{�pkn$���g{��Ǐ[��O�ʚ�һ�6λ�1����	��k�F'��˻p6�B�����
�_`:��h>�+����X\�t�F.�\b=�!�=Q-�_��~T�X;%�L_�<^��->����Y�@h��և�J�n��o�Q�J_ې ���gtj4:�cr���B|t�;&�W]���8:��U�N��篽��m��E꤯�|��ɕ]-b{��5��_LC�,��@��2���S��~��	7ז(=_�CrQ���pn/yO�g���k^�Ɗ��'���D��5�R�����tJ�g����u�C��]�?�����p�̞�����k����뙛�H��=Gz�^f���XR��y3�'����L_����9>�
�S֍\�=����Ay���!���Nz�Ep
�V��#7�z����5O��|e��|�/���-؋
�o�+�[০�o(nt�������r�ޝ����3~��Q뿋����^z�����#>�C���|�ξIY
Y�be�Ot�GI�^���yk�����^[�U�s��{�<~mx�7sE�+��J���D�$I=�ؙ!��?*[��'A���J*�,8��g}z#����c��O�`	��w��8��^~�;t�r��i�:~�S1��!v�XOP��o|<�8�'���ҋM#"��I���-`Tri���S��|;�D�`�B�i�'>b\�}qVTﭣ���#����+�cq@h����,����/�I|�|zԏ�j���i�#��r�*��+o��G˽L+�3ߟtX�Q�3�e|��~���^�g��P�ۏ�{3���1����ʠ�s�S�G��8�c���[GB�~��g}Nu��@��RQ��s����S�O�[�����.����gB�5�/>��ب���j>' ���F�N�~Ա4D_+��`	>~�%�A����'����wƃNgE-V��>�~y ���"
������-Fj�`p�_�q����|����x�
�d�m���Ĕ�i�A(��yi_�O-A��\H���~��	�@G�b�ꏵ������}�<�&�=�Í\��?��p�O4m7H�C�*�~������U@�z���(�s�X�f�e%����K'*�R��t��0��I7��(�ǯ�rt��J�CD{pK���M��t��d?�ר���Qk|�^`��~�~�]�[?*!��|������r�
�;�Ly���-
v���л�?��g�A[>�כ�W)���"͢���憎�n�/6�W��ġ����3�LL�+��ЁL­w�}��ޙqs����5�����G=��l��K���'&�g��ɍ"oF�[GOgљ���e1��a��W~|_��C�)���뿘�c���U���}�ܴ�~�y�|Why{ы�L��K{^��t(
��tZ��p(7��#!ǽ:eF�ab��0��
~tZ�]pF�౿�y8��!�#���빅����p�E�B�����|2!��>��Y_(Wz�$����y��/�㌓��gr)+�i゚��c
P��ؒ����Wg��W~���I.f���\����g�’&W�}�u�Ӄߞ�<=�4y��vh8�\�7ዼ�k���#�^�v����|(+�ѧ/Kד���7~�{&P���A")$�
����t�ou�Bg�D�k��;��q���cyyIŔhi���l���ε��ǁ�F�q��ˉT(bt
�N�YW����5���_�X��:��}�F��m�EG!O0�0s�@��=몏+ЉL%�[��O���hV��c�Řπ�eKSo�&ց������HC�o�4����O��5���	9�(��]̆𣧪O�w�M�f���c}��{]�ݯolF�	�@�X�0����M��C皞�v`w����{_��@!|~��Q},��� }����y�1dUz��(��K%p�9���#��,�"��Ӟ$��BT�4�iǀ�'ڱ�5��2ӄeo��Cw®���m}�ىOv����( |zpͭc#��ZF�O*��I=�)���GO�⿼������u��F>��jܦ����Q�����//����;'���>���E��Uj'_@I\T�TH��4�.���<~J�L�(�8"~un����7���I�=�[�#��w�g�B�ˣ���]�D(E�%���!�Oh�`#?�_^tg�Vؕ/uv����Mz;��|�Vx{�m򖒁�*�ѣ����;��+vR�Ė��d9g�7"ُ������A���hh�3س!�g�Z�O�S6�dm���#gy&26���w�9�2?�_+p���O�G�aW:`�w�/j�9�ᓅC��}��[��V�K����F���=�J����^��{�G?�_� �k���E)��5��O�
�yn���=�zA�(g�
�Rxt4�O��;��#K*d�xN�RO��|���5��jc�K}�G�V`�X�PM��hD��
���kB�-�ɥ�D<Ј�>�B�ꕝ+>����җ���=詘����9~zҎN8,�Ŗ�>a{��-4Dv��{6���:ׅ;���W�Q�
Ej�I���D�~���'_��G7���O�m��E���d��~&�IY�s��\�!Uyt�Y��Ua]C�9:g��`�����nSKg���̚ztoQD�S�of�-]�$vA�������ɓ��5:7�R��S��)q-�BMo�2��x:\?��k8/�At��l|�6�5�ծ�=ڽMҖFt�%��ѕV�Q���䊟��W���J.�������/����㝙�a_w�o8���]�e:�<Wn�`qT��@.z��taI���=f�K=g��BG�ĵ
������7�y�R��G�$����;�$V-�.\�Ԉ�����
m�?\p޸񭦭��|)O�=���j��&36�l�_�c���Y��)����'V}������-
�O��ܷ~�ttf�D�?��j9[8�c�^�G'|짼G}d��aˏ����	���b_�4ۻeG΀젰�z��#������zc]�O�~q<
0������5��,���sS^����CYCD����� ��A���J
?�X��4/�����<`M��~|��0vp��&1%�����.ƈ��`�J�4�l}��';�b����[
���r��w��q��%��S�׽~Q���ˏ'�_���'?W}5��W㳕IS0���i��ưRe"l���s���k�>
5�U�D�U�g�4S�����.��!wA#��s�vz�w��I�O7ptK����k~����fJ����f1m���b�N)�R{�`v��>�Nt��*��g�����i�gt�IC��r�L�L>�,�{�,+@8_OiT�<7�{r�A~M���9>wLg8�w_R�€�/���/��1>�)��[*�n	����J'{��)5|�e�<|�r��/�OMG$:3����;��u]�<y5��j��(\c|c>�2κ��u���b�J�s]N���f}�u�b�f?)7�f�D�_�w-��+'iƐ�R��:��)�/���s�|��ߦ��|�<:����F_7����*gT�3I���g��s1����W�!i�����:|���5�T��-�
�׃A�^
����DՏ���d�,�n��Wt6�|s��E�hQ���zc~6�ͶW:{�p����b���
W��dϋ!�ڦ�غ���įt6�̞�����I=�+�^����x�һ	���%�>%�(�p�_��5~zߙ��u�k��3��*��3�^�ܱn�,�B~��B�r!~��������r���Q}��/����'~���F�+:����0�&;���禶g�v��O] :�n�"��v�I5Oη’�_��
\+�^�/tv@�k/�����?k�eO�*v�"P_I隗��O~�ٕ~靛�T�f��Z�
:u"�տx�0�K�U�9/��Z�͈�5;4�e��k�X��d�(�3|<�pF��X}@�N�^��́�l�s�5�����w�{���ڙ•�\����A}g�5hb���_-?(����QK�O&��2���^z�z{��jf�@8��:M���תq�;�H�Ml\�wt��O��t�4T�.�lݢ~4�'� j�qs�`�D�,P�5_1�сO~��ǢY�g�L��Ju�=/V�~�>���@�T�\�}�0j�L�)毘�/O0:����'�	"
*��ij�s�#�9~�9?Iz�W�����d_�٩���t1rr$�I�>��`�L}/n�2Z>�G��Λ���}Լ⃎���Q�7�E��u�G?���C�Y��^7�]�}�g�{c��)�Q��gar�/�C�W�O�
i�ӎ��������g�1�t���+�%Vv�u���}3��_�M��J����
�Yy/Kç��َ�c
8P�u}Z�����\��5?_%ץ��u=���Ӕ��7�F�Fq�C��\��
�0�졬r���]�8�S�\�q�O�\�q4�gy�,��r
�Ae�(>�q�|a�XlG�Ko��U�13�]��`�rW��Fgr����t?ſ_[ss�	�ϣ~�J��$�KJ`��~�rė�f����uv`��^h���|F��ᩐSB��j"2���5��	�Q�n��7Ռ#�����|�P}���̰O�
�J�:b��(N�m�8��t�����1��d,�
O|���)?�])��Q��p��}f���
C�	,�p����D�V�^4��Ū؂_�o�8�||w�.��H�K
"�oз~���o�yO�u�˱���N��Y�M}8~:�J9_�$����C.Z1P���8>���>o3,r�[y��.�r�+����|V���u�s�pO��2z��g��/p����C�a#6���z���,%n)ht�����?�\�A�y���ɏ9��Fߨ�Q��+Ӱ��ƕ^�0~���$��:�@�%�rr!�T���=�l�/�?��%�a��_��\W_�pX�fϴ���-�|IU�cd{0;�wO��%;��3_^#�|W:{�>��kw���F�GG����l���U�|�>�X������щ~���ͫ�1>�ztj���q�*O_��Pg>��/��4�m��3y������z�K/�*l�kF'𷍝ޯt�kg���&��t��*��WX���$�P׿����c~\�c�/�l���vx}�-�.|gk�����䚽M�s;d���.m;�X8���K]�!	��O��O���[�w��������?��27�͞������T��~�C����-zY���s��oO%S	��Z<)ȕ�/Ny���_��������r�����=Ͻ�їrr]�Y��giCh�w��|��^�*���j{��YF��Ƈ�7�W��].��#�����o�n��=-�wt�����O��h��m�����ӹ��;~�g{z����~&�+:����3?W:���y��=����<�A�_��r�+I/�
�ܲ[����}�+�V����3��6~�/n�Y���gz��g�>�_�{g�\�W~,�/W|�C�<����N�*��~���+���B�ֿƏ����������R�ԯ����Z�J_{���<;T~�s�I_Ϯj��ʅ�U_�S�
���~�/�C_��U_��s3>+t��}DF?���?��zە����g��3�D�ġ����P������}v�����ʏ�>{N��ZR�=��cH��u�H�ͧW~��Şg?;D,}!~�{��k�\���'����tN<����_^���.A�Qޠ�8�!��%���c{޸1;��g�ƍ�q�ٸ�m�\~����l�RЕ=_�@	�����[��ܴ�x[lvpC�ϝqy=l�~��X_�O����7���:m���c7v�G������|�
\�_���+�����
��|z=|���#�11�3,�^m���==�'�Ɵ~j>��e���>}=�k���Y&��ʯ��c
n�z��E��h�3�?��'$���_���}���[�e��ç��s���4��-�7>oܘ&��L\��)߸�����/��'{������δ�s]�_��\˃r}No݂��ZL�
�ˏ��D��}7�_W~����X_K����w�E?fz>�=_�wD�����~��ˮ�\���ց�RvRLCh��y�*�3~Й=�A������Ƨ���N�~���f]��t��':�|z�m��M�rm^�s�nY��seϛ�x�g_n��O����~�R��߲~��5	�/��竾n�~�?��g���_+|��gI�j⊏��u���a~Bt�竿e~���a�����8����n�c���ԍ?���$���k~����os���W[��5��?�g��#)�2�֍�W~z��ũ����e/vzo|;R�й%��0}�n��ڭ_�~�Ƒg�3��Ș�y��g�޿3h��i�`��2&���p�Q����~��O�u����w��p�i���r�f?U_�����?�;o�p���'��ʓQ}|�����?�
���8=��-u�бP�Et�/�n6�L"���3h~y�&|����5ĩ��Qߧ�!�/����6�|S,H&���D�r�?%�y������v�˗�>��#�ʯ�qJ
{�v����T�%�/����*g?bh|�)F����o����)���+�f�gGyY���~}�.����;���}�����m���I�p�.��`?��]m�(M�O�݌����*��:F
>���_g���/�h�F_�|]�_�$vr��ߝr)L>��E���}��D.����{zg?'nN�e�����G�#|�+��r]���I��o<�Ή���Ӟ���i�Ϟ�W�7En\�"����u�j���
��_@����k�o��Z^^��~sYTx�l�oET�~&�	+?�Ay������
�щ����՟ݮ�5f媯ʯz\�@��M�v�����gŏ功�Ȏ��g�+����,H��ϵ~�W~h2��+���v�Q߭�9C�T��^*�����;�N��W^>v�������D3ت?{F�����zF�E��oP_���7r]��G��
2ފ�|�`�ď��\�V��t!�g���\�Q���/�A� ���>�T��rj����]M�#ŧ�$1�U�'��'8<�;���t~���8�U�5��'���+<��@��'��ͨ�v�}f���l�)�y�Q��o��t��3r�
��cj��;���)i�pd�&P�]�Y�D�	�es�E,�/�k��<dQ����Z�3�|0#����~02N����|f�����}��;�<���}	�����Ц�8?y	P&����}���2����W�g|���p�������|��?�po�����/_�#����<����v~X1@��Nj�&0�G���^�xM"�s��O�!1>ń��S�?Ne1�B4S�>��^ȅݞ:�!���ϋg/%|R�������}��8��`���3�)X<��V>��,��#R�m1�A?���~q���4p�%�N���;v����"(�9��H��5�w��\���ȿb�e)I�ӗr�`����	�|�Ʒp���Gy��I��2B���7Q�/?��Й#1�Ob����v��ӎ�W�U���c�΁� �7?��Q�>��4�O�Ŕď~I��z_$��R���V?��3 ��ү�\&�;)�D���B��������U8�|����p=_}�SbfS����:��L��+�L���ۘ|s�K^�N>N_:�����d&��ʙoZ}tJ���}�@�Q���o�j�W�:��]�{��$�2��E��|}�_���{Z�ħv_~�z�IkN���I*?������|�k)��W��]aD��-p���̎���r��}�|@bP���"U���O?� 9ʫ�^�}#��7BK�YF�Y���)Xz��ފ-P��ʘ]�?���%���Y-�f�x��-��?�a�~}�T����_��O]����������	�*�=+���4M_�P���?�����E��:RR��������ӈW��i�	t��*Bܯ�1��� �����Ϟ&ז�(���%�Μ䭉�av�~����Ǣ���6���������맺�q`���g;`���~���?Y)o��	�w��8C�]ŚpL�����R��z��4>��vM�1�i�U�6����c���e�;�a���~���,v�/'>����g��jH�A1� 7�����gt�(��c~�_lW,�a{�Cgybjz{�q���|��>~��֙GS���7=�~�0:V��#����L;��*��_��D�y�j�3V���a?ѩ�5<���U�I�����VV�o}��ݍ�pF����=��sn��at�]}r)�Of�g�b/���֤/�.I����hrm�3��>'U����/���S\�S���Y�������/��_�O8k���_�R8{�����?��O\7�5��i� �3c��O�����4{>ڍ���	�O����!a��(�^s��se�9�>�~�~��o������ᅢOv~��~����R�C�&7��8�1~~T����p�%�b�_���C��޿F�#
:��7�<�-\���/���1?y��k����\�������'X��&��>g��0��9�1���_��X�����_����X�%�������:�޿�8���Ŷ����o[N8:�g���bJ}tʵq������Σ�
�%�T��oC��6R�\b�H]XjDb�����/�k�B���gZ�/�>b]֙|��ncZ�����_�1 �����7ء�����?���e\���n��� ]������ۼ�>p�n�z��䝣����6�>x�?������R�蔏A��q���/�k��링���[�t>��
跒~"���c^��?��j�^Ja'<3gP���Ȍ$(�N�\���}���'�I0�Fy�kl�b�����v��˼\��o�|�9�����	q�;
��[�Ur�s�kg�<��+�Ɇ>�3cv^N��v�_ÿʥ2.�vC,{n����
����j]�GZ����ߛ<�q�{�u]�R�0��|��(F����K_�����6��n�:jtUI�nݲ<�%���}�s�k=(Ӎ����Y�\�=w!��u��V6����q��8�g%�\key���(d�q>ك���������1߼�w�y�����Ʌ���x�k��@k���$�r��3:�2~�?׳�2��5;iݨpaIf�ɂ~;C��6�/��5{����n��Մ�80�Y�^�XZol*�y-��gS����f�K�A���&��1?�^]���l��n��c:=���~��TC�w	�|���޶P��ec}=�{~-�7~�h񓼋}��A�V>{��ƥ��傏Nw�.���G��Lg��O��5��z���[��ÏD*�~}��yo�s��΍���m<�
v��_^���K�r�2m��pT�ٸ���'�}���_={.��'�Ĕ�ϛ�|��"�;���c�z��+��Te�{�;�mot���B�$�=1>��[Щ�n&*Q�[�K��I��?�F���׷oT�?��G��(����Y0��]L��z�*"�P/���Vt����є�����������&��З��9
������':���CF}�A}�N��%bW}��	����C�?�L�,�}��
���%��a���nq`�؋p��}�}t�:�܍�����ٌ�1(�?��Ct�I�b�J�-LB����Q��o'}���)_�]"�h�\n6�h�#K/��2�w�����#?�)aOad �J��������C�;}����`�c�7aC��3�ޞ��C_�廳��eW���~B�<G��yKX��ѕr}����(�gv8:���-�o���������9�$'"��/�#�S�
��o����X��ȟ�C��0�S/� �Ї�k�s܈N�E�
#�O��ڥYxvr�J�RGg���=�$��*����a���ӌ���;�[��g��5LDa,Xҳ�. ��N��w�ď����[
\S���?�(�D�w��˷������4ӝ凾"���� 3n)?��sy����%�f_�)OJ%|v�b;\:�-�֟�y
���4�������[{щ�k��
\>�����:bԸ��W���N��̽�e���5�O��|��3���cYJ��v�(I���r��OӼ�@��,/��\Y{��)1.��A�ˬ�R~���G��>C"�	K�i�c�N�n�­O;)|J�ݾ�m%tҗ���`�~z���<.�<�;����O�t���T(��Ny��T�Pɪ;�*�ɫܸ�y@��y
)�x���*T�S�5�_���c��Q���|��_y����ÿ%O���5c���$�?kC��|��O*k��ȋ�ܞ
�ǻr��\��eFǶ9"꣯���:��^9=��7J<�PtzTg]���U2{��:>�D�*�B���51�<�l������A���p<1vn����ҙ>��`o�;F,T>�ғ3�8�z�8���o��S��_4+�m�	/0�1%G�
�X��F�g/^��<2F��i��ω�ٞE_M�:�a�_�����ɴ���X��ҰW����G��ش�LgV��.���鋯��W��f�i�x�0�^�$��g�����bL��qᎼd++���#.L����s@�.�8�>��={����w**B����DV�����oan��O��;ܛz>�^S�<m��>�Z��X$��57��N�@"'���7uy�����%��3�'/`�	�5z؋�D��5h���J�y��Y�O8㇎ԧ��pF?VI:���%K_K@q�6=�w8hA,<�#�d����˥�޻�ۧ��c�����̝�b�]�!�ϐ0�g�"�����KkZ���:=��`8oT+N���8�	N�������N���
�K�d���~�UL��Y�1�_-)��g���f��k>�k����E�Z���Yj߱>s��V�q-�+M��i���R�ul��8���+m���ˇ#rQ��D�r�iTI��$���7�YV��3}�Y��}r���R��a�c`s�Pk\���B�~�~�m�/w�ٿ�Z��+t�RG�����</_��O9f��,���{�S��:*�Y��V{�r���]N�~���>Ys+`?����
wv�'�f$e��x�ݴ�	P���g֝-��K�;�C�|���nQ�o�(O[��|�ⳳ6��亷��{��;�-|����D������PyI�\rW�I��X}fY|���>[�"�:�119Ԍ�V��?����E���-�W���)܎��|���m�C_X�
Z��Ϯ*��.>�H.�(����5����K:�������^�R2y�/&&
�c2ߥr|�>�{�ĶM*
��a���
?�sk��E�O�Ur/y�s$���w�hg��/2��r5'�^�����u]���ei]���g�	_�,�;�S5����xDg<�[�|�׿0�k���O_AQy��="5��Wf��k����{��+�!�VQ��8R��ٹ�,u��N"S"b��k��|��9�k��ǺV�%��I�^��%�?W}�Fz��t7�@&<ۓ��V�{���ڳ�ES�b_���t�
����~�1(�J�D�^u����o�J_K�;{V�,>��\�����=��������Y�Ͳ�G�m��F��F�E���y�҉�U���?����������-�������]K�R\F3���r�{�5��+~��noo{Q"��|�G�����;D�1KǸ�N�S�������y���=W�ݩZo�xG��7�0��__C?��|<o�CYD�(:��ν�߽�p��Q>�#ޛ���?��/g�p���q���W�b�z8<�8>���Ӈ�o����Lշ:V�i��ӱ>�R��+g�x�~��iއ2���1y@�~�9�{��P��GM���E�X��!��,H��?�Xyz7%�B��C��A�\He��������������2j���D0�/��]i�>��iCC��CD|����{���~!f�#�D�*����χR��O��_��o����;�-u�V�E��O�d��y�4�O{�b���q�\����C�,�b1�s�O��yU�����SL�r9c���=n3�q����ϒ����f�L?�*L"�іBt�R��]�T�:�ئb�7),��5�PХ/�#v��X�ba�)�ʇ��_�A�5�Z$cUo�N~O_��[Lazo�m-���B�'V[�Ggo��1
�-���'��z���n����c�֒7~����?B�`���-�t�����՞�Kט�k�n�@R��^667:|�Թ��t͞���Wb>�}�f������C[}�.�s��{�EB�ʗ?㱾�7q��^��}gUm;�0|�f�u�b�.P���>����Mׯ�gz_��9�y��}xbf{��X������_��q
_�
?�����5�|ZW?C���{J���3��Ny�_���W�%���L.�}O�����zf�9e�����d?��s� ���!~��g�[�+����M��W~c��_�����sy�*>����8�'����?����K�5��
��1�q�=��s�)Q��5��E�/zѨ~����'<74]�Og�F'��;>����Y��ɇ{~�5�vl��z�__ǽ��z|���\ǽk��c�*��Ǘk̢.��2�m��u���U�Bj�̍�|�i��wu�y�/�)a������Q���3k�����$��I�$��s8�0d�U/޳c�9����/LjX�(#�z�|�])>�WR�>�=se��/^�tv�r{��%��_�����"����Y��2�rn$ބ/�SOZ�F/����7��!���t:���<�,�T�`�Y�g<��Ԯw:��T�	���#���P�(:�R���[�!��7o��zƨa�{pr��[ռ�+��A��|�'4�\�Gg~��mj����ߛ��_�vw�E����q*��/�|9�n�g�89F~�h�����z��mtbK~3�'���䈣wb�����&�h�h���@����3o$����r*�9�i�ɛ�}:��^�g�(~�}�h�I�+Ր���?|�D��O�������?<�z�s� �����ɫ��F��k�bA���U}~~�8�*:�*�O.T�Ӯ>�{�'?�1I����r1�1�쓚t��#���O���h|P�Ɵf3���,cvW�O�����@��|o�ɞ��/:��|��1C��,���f�5��'��3�/�}�6�E��?b_�����rQ
v�5����~�=�he��a��%��^�!�7�X9f�	�<�yO��N�[,?E��멏������A�G����k; _N}���I�;Н�s��o�yD�+G9�P�C�2��'<돔�c(/FǾ3:��\ãiP����sr��ܢ��$�W�����!ק�>M�4�1�����K����}�5*'�5�J��ymw|�n�y"B�<��i���ƿ(�1:����`�i�]�����`�۲d� ��@�?��9wD�i�A.�vnR�ဟ�7Z��G�w��0:�l�q��T�p�k�{f(a>&���$�rwaS+�����d�˅?���Y[���t�ӵ����)=�_�0>�,&���
p���'~�y��_�M��v�7�ԉ��u��׼D������>:JEC�罎���tC��/�K_���a?�'~�����S�}�Ϡ��3y�_`�zфO�x8��[|����
�Q�1������Y�'/H�O��$�����8���W14�հz�)6�p��at-u�sN���g�EHw5����i-K�l��P A
�9��]z�?~���X�Z;���o��Y�iP�w$fّ�{�?��.�;L�'��9��p�ry?��τ��̨ß'.#9��8XO~���e^PH�9��)�-o��i��1�͛�o�+vG��%�6M�|*o(�3J�)V�XӼ�����uf?����|EY{����M�Dʠ��ʨf�؆���pgm�:wX�0���6ǠP�D����CN$p�G���OCEYB���D���-闷J��<$?���~yJ^!�hk�7��@��Ӄ\1
ָ���$�
����#~��wKrg��\8�p����UaZ��R����e���
�w��<��<b�57 z���dII4��{�^��6���/oް�y��C��yf�0��.�R�2�Ó��N�s��3;������vߖ�֥�r�/jd{���W��VIe��%g)|�&K=@�@i﹆�Q0�_��(Ǧ3܄��@�|�Cp�K�@�jOJ��Ss���L�W��[GAދ�Yh=�{~r9����g��?13�ZW!�0~Bl�C�Xl�x�*�*�q��-��"��|�i��|XJ� H�Ox)[�C_1_|��8}��/{3�X���~��D��X ~,��M��k����ے���>;����Ι�b/̜�X���-�����d"�Η���:�A�~�M�=�g1LH�ڵ�A��=_}���=u8��[��X��f���Č���?(���W���O�{�c��V�������n�,���ˋ�~����)�#��S����9|9�����뗯0����o��>��sϙ�
�w�+ry��y_�Y^��ҥ�_��ؗ�i��=+�~9?��&{3�oud��H?�����+����u��>.�hh�S������{]�I��u}0_��5Q}{$���ioi�7{s�Pi>��{q�'���yr7��L+:�=(_L��,�H��v�M	�J��X��_7�W~�S��~t|�O��D>mDK��YM�z��{_�1�GĹ>ɛ�I/M�高�k~��.ƴͫo�3��q	4P���y��&�
�T�X��o�P�/����A/Gn�������C�I&�%gt΋�)'�����>�bw�Y2�nq#�b�|��loH�n�O���߇|��G+�8F���
/���n����u{����d�3�9�����#������'(dyS�!
�0#~��
K���='Z_���/����ϟP"�$�	%�6eD�+�����?�-��ڗr&p]���#b���z�=���/�i���ӗ��Ab]Sx��
=~����̛��>GEl�h�
~|��/�v\��0�����л���}�X����d?�i�L��\�C�/��|!�	�����ڀ#�K�s	T���~��`.E՚	=r�-��1����}�N<���(��^3�He�C^����[�?�Ȟ�^�������/��W�%�~>�����_�A�6���yu�b��ۈE����L��/�$�2��0|�cW����h��w���է��%U���s�ӗ���"��`��S]W~���c&|�mź�cvC7�lv�r��׆o��'���@ç�=?	�kJ�b��O~���p\�.%,|�`��Dz�\G��ߞ�_�'�:��ӈ�|f��:o6n^����	� m4kޡG�W+LgaF����uT��DK/���ԃ�*4��/�c,a�خ17�n����1~����v�G�Y��p���o��:D�5`��k����y��l�q��b2n{�BgRӗ7�o�����W*�럭O�?~=�|*W|b��'��0A��%$�_坽���.m�Sb1=������e,���op#W�
�k^��$=����s����<��rr��c��y}\�u��?[J���b�����q]���5�+�f��=�Nr��M���I��{��1��5~fϖ��uig]�`�ˇZ?=W��k����������;{X�˅��?�!j��Y��:��WB1@�ky���Y���l������0{��,�EÌ���$UgrN7<���9�lսql%���`)�1R;#5XvW�n+y�O|r#,�L���v���N
����6>G$:=Ad|*�`tv�U�y���#ç��˚\��N��i�����yA�g��_���;��*�'������ST(�����͍K�l�����>�c'6���p@���l��9�ˋ��'~��v�Y>f�8��s3�K�=��I��“C�2:H�>��'Ѷހ<<���F��yΞ\�Q�g���G��푻E ����M"n��B�?���A� �Gk��V5�P���n����u1Xw'�%.P�y���.����/����gf���\���g�[#�	D�P��g��<"Y�|�����k�l8�ǝ��C���_���s���yd�jj��Y����)�u�!;)V&�X�s<��n��5_�y��>��Yle�w��w��
~��v+���<��N_����s��k��/����uT�L�]�x��?�gtl̴�i����8n8�?�8�/{��G�b�*�/x�ha���u�.Ye����Z�����⧼A�M��w�t0>��D��G�|Ӗe�"������veFؚly�?�/a�*��{�P�p� &� FX�w�|�dz�B��o�Z��K��	2Z�0R�<���3Ntx���%i�/f婚�<dw��<.�3f��A.@�E��y]rN����6���y���{�st�_I���=��Ú�T��0�Y��l�cdp�|BlW�����Ps�y#KzpHf����k��k
�>^��L0�o���+A��I�Yn��H�P�,�^��.A�h2w��S��QG��g���2sG�i�/����͍��׸Pf<�@���|5�������5�C���a<n���wO�q�3Z<C�:��k����h���#X�o��X���R���bDf�>��XE�	X9�w��%�u�JA�K�iFp�>�4�l(�]����w$T�v=9x����t[���(��d���x�Fg����[?��E����F}�����_�N�8��R��)�z���x�g�v�}���g���IM�sУ�@E�3�w�+�K>�=Ua��j	,$��R�y�v���|y�&��a.�����W_'a��6���ݙX�w��=ȿ�����d�-�Σ[�	��J�wD���[=9�(r���:B~�����Xe��j��;�w�\�1�&$�}c�s�)��q�96��F3b;�@Ʈ*��Q���A���Q�Ah秹��}��R��a��`�j�*�����f�wT�4њ��ȗ`��x"Fd�MA$��A��}��9{�:kT��F^�'}~�Ǡ�-���?1IK�Q�O�{�:b��	uymAl��zw�)U8[9����JD�$�Ll����$C��K��:���+�H:B�҇;�?�9;�F�=��<_:7��B�$`��9I��9}�Õo�̷裳̖��pD:1I�i@H�")�h��]k0f��'�_=�_x���yx�ec�5Ck~�c�/��GX�O�j.¼"�g��(�_(j
Ɣ��k��M��g�!;#�^����^;�^,�us�E��e�|����9ru�X�I��"��*��Ұm�ë�
���g���a)�=�;�����=�/�t]���bhpВO�7�11L�f:g+q�s���UAO*gң�7��9�tR��8=]�$|�/97�h�&э�����F���'��O]���r+8fΔ<(��ك�_�?��k����3[\eOk��x�L����i�H�lq�3}ʻ{�|?�Z��2Փ
N�n�|�rv�B�a�$����e�{�D��-�ϒ�-��X���T3)²g|�;�WL�!FlM�!C|"A�P�9�'�k��aQK��c#
'���5n��@����ug�Я��›�/�F/t:*S�s�0�Ç�I��}>�21;�]����sA�p�����P\���v��2@�ǥ)��(oQ��}̃XI�a���ޫp�J��'�N|�-{�ä��ϝA�
��]�I��A2�n� �����n;J�������g�U����q́���%��A�֖�vd}�S�dP2�@���W!|��*�4I0
�A]>�_�KO_l��;�������F�}1a�/ăn૿*oN��A" yw�o8�U4�N��*�}���q��3�I�!�}>G��ᖦޠF���Nz�65~U_IV(~����bdq!9�O��n�������59���Ӳ�;��ڈ?����<�������
�w�#�j_D�w�6k����B�<}�
�YՖ*g&?~���s�*#^�<���܅T��\u?>��{����럟�|��I�[���Ͻ����������?�7���,��cn��՞�
�IH�+8��;���z��E�ztI1ke�C~>�O�H�'Ud���OO�>��΋[��P��ލ�e�8���?�K�t?��
�-�
K&]���H
���#ĂP��L�ԛ�d2>�_c��`h�Df3�I{�$b_�f��|?�����?�0�̲��P�Ӟ�0��^�*�J�������q,Ft����9�u���_���Χq��%s^�Q��:U,��p��b�]g��J�
��=J���5^<��`UG��2���7+o�Ţ��y�d���?�>�W�L��[�t��$2C�|��� {~�s2{��a���ϠL�-�L����'�L/tY�Ĕ`\{~�r�
�K��������8Ȋ3�!�}�~Aݢ��o�_�Et���_����{F�󝄽~��H
�w���.1K��'�*va{3�f3���e�J-�~5�.�)�"g��=���e�1�o�.Dg{��cg<�V����R��'�S�2�z�_ƲƩ�����b�x��	D�qyb%�?�s�~�S�Ș���}���Y�a��]�z�b<EP&���?���R��cz̄����_7D���%�cRO)�Ag�~��,�!oB[���?:�ȇa=b@��x.�"�ܭu�ؗ���HI��x���L�(ka�b��yn���w��;����U��%㲉�G�zw�A��qƆ���;�bȲ���ž����4�/`hƧuDy��h��}�/X��,	Q���W}�R>t�Ư�l��g�_���b��R^��9�Z?r'�S"��c�W��$����b==�G����G�\�4y��ҋѯ���L�_�	��%;R�x��<a�MG�������y��<�ñ��Q���M]1>�2�����b�JC	Nv��yE��n�]�4�AM���,5�1����Z��4不a>"
�1_�gb�5�ݠ�]	�>�ŇS.���|�>�!�!~��{���P�-QQ�|���G���)��כ[��?~�?�4��y|�q�Μ������~���x��e�v���g{HVI�חI�31a�8�>T2y �p�S���3yh&�&�cIȾav���ڛ�{���)��n	/[79���8_m�S��qϦ��'�ٻ��$�؝�T�@͵��M9���b^Чy��1:�|��
��ܻ�:����2L�}�ʶכ�w�;���>�N�������gb�ڹm{l�y|Б.8�%կ4�h횣��~��ɀ�>��p��+�1���
W�~�\���Q�@�O�>�C�Б�Lm��Ĭ��[��櫄*��އ(���\�f�P���}�1��i�`��9�fs6�}f�}��/�9��R?�?'!��?��B�_?���d����o!ϔ�݅ƠW�,M��\���Z����7O�ɏ
��]&�&�����}5F����=Q���/H/�2�g�p=�/*ǯ�י�5�*��sRL���[/4ȓֵ#P�����.���5Y�e��"����3�$�4Rx���FU�.�ȪB�ڮ�/*s{�e�v�i�P
TBu�"h�.�X���K�
k@~.;���I�怘!T�nt�kb����z��n����I(5O��"��?n�;r�p��Q�Œ�|3�l�k}��p5?n��CU����� #&��f���"/K����1���ѝ\s��
?�=N�	t�.:'?�w�d�H�����x���z����tS��VS���t��~N���:3>|ș_�����]QH��T���5��7��.���D�,�!W~\9A���y��w������#� o��zG����(B}">�i���N����~{ -bw๠`��xl���,�!%��W�	�?-m�o�,��y]��zAg��$�@���-��g�ʤ$��S�z&?yM�T�S !>Vn�0��29�3�9���<�%UO�!ٝ\�Ú%�g/m 8�k�	+��s,�WM��K����]G}�R�d���eȗ�>�-V�v��y��oYt�����e��gǦ��wM=���n�q�%u�?{Hި0y�"?'�N�8d�
KN7ۋA��{�����9ʏp��U/5l��=����/���^�WLoT"���t}�	&���>�����+y���蜾�)o9�PN��֤b8��O��s�++�N�~a��MT�;�b��[�]�7r^�G�­������1����WpV��h����^��u��5�?T!� �lp����R��by>���:�F4�,P@���溆4���X����&_;÷ǧؚ���]SPw��v7��Pv/7:��N��RX�w��U�a��k���*�B#{�4�>������7��~|��o��W#�6HL��*�/�Mu�S�}m�_������F��ǿ��񾪍_��6�S��C���ӵ���a�w���^樜�młt��v���	� |ֲ\0#����G�p�K�h[�l�r�fw��V��w��H	�����!�'>�����v�U�rLV�f#
�Wʹ�x�p�$��1�v3
6@bB���m��Iv֩��8vt�<��f����6|{��Ar�q�_��;��)o���-%'|V��ύ����0>j�Kە�w�tX�z���Vm���iT��B}�1�wf@M���*��C�O�W���\�ۇ�����ü~_^,�!��S��S�Ξ�;��Oj�b�K���	3�+5�Ru\���_�~eϲ��fs��H��%��/��_���n�<cD�8�;�F-%	RT�mp�"e�6�x�kѳK�S��<�4�(U�n��o�׹b
(L?�V}m�Q2}B�PɻBq` {�k��=���7;6)�Y� P�C1,�g�`T$��$�P��QX8����`��~]P�	<<m�<���H)�����ڣ���@Pp���l��w�; ���_�A}
��ė$rJ)ǽn��5���t>ޔD�>�{��6���B��"�}���v�$��iвA�U9$�`�z�t1��[���D�]��L��r�e��k[��v���lcA;z���/���$|�;�b�Xˮ=R�S�8蝑Īk�M��³�_f#�*�g��A���,Nͬ>��>����K!�M�1���W��?h�x^)Y�bA����Yr�I�t��K�مbѺ|��g�!�D�	NV;�Z���B�<xK\p>D=�G%�RiƉ �u��	� p��י��+К�^�>�!�_,:���I
c�-r��\U
QV	�:)KAf���Hvè��=�y�P.6�$%Ŕ���"�
HD��c�7E{�;T���/z6�y�������ǭ��_>��)QG�6(u��bDpg�������(�s��7��)콸r��Ckox��ZAo���$8Z-�k1�|rwt|R��_y����̮�t�pN>�쉿��V��"�v�vp���':$? g�lkH�'93:Ó�����;Q���G��y��79���I���/eN��bo|��y����O����nCmѻ�h�_�ZJ�ة��kZ��tć�g7y>T�A�9��A�L~�wr�gR��|�Ok����C�~ia�|���p�.8�Ig��t�Ce�:��J�0�-����U�)��H2��q㙐����6|��/aF�G5�crb�V����]N�-|J8�����%������wEmǂ@���3cXl{����ܘva�=gj�Wv��u�J����]e���U�\��<�0��v8b�τrwf7��_��뚼'8����vIBGx�o^�s.p���~U_��v�N��"�zp�ԯ��'��,�!:()|ꏏ�掶�SuL�?>�� ������������<�5�&�bv*˹.8����r��|����N��Z��@eR�\��}�Uӛ�}Sz��'Ǿ��;�����o~�ȗ:�ӯ���εCވbY�G�ł��C?�������KPɌc�'�����2���I �N�W\����#�E�^��m3<��U[J�DH�p� (=i���>���]s0w�xJ�bZQj��������n�"&@��Ž�+��ۧ���J�����ܼ�B��s�����3+����t�Q����a4��a�������9~��=<�`�7ۇO��,rI%H~�-8�6=��H~�hz�a�I��2rMd���~�2)���$�)m�ACi����e�^�رb2�;��'�J�rO��Ij#
>֤8����0K��5X�
�L8B�f&����;�N�S����=��Z������ �W�>Sm�@u.��@	�9QʋS�)���(p�be@�>��tF���f��M�]����B����O_ND����dž|����;G�ɴN��@zZ�T��m����7~��w�2�'~�&������%�cOf���$�������5�����D��W1��k{�e5�ث^��k�!�~}�7�G��/���kTkE�%vCp��ɏ��E��OG�ç����w�}}/�(}o��0G��r����Tů�����{����&���ϫ� C����s�H$o�(L�*��X�b>O-&����\��۾� ���^~ap�~?�l�C���=��-�i2�Á�`�D@��[�}&�f�0���7v�F�ʏ�y��R���/�[�U���P�>���ǹӧ����sL
�$?�yb�ҋ�_B&V��z>��e��z�5�������t&�Q�;�QœJ���k�}=�_͟g1��>8u'T�t.�ק�{���3!����0ԯ#}^�/��sCՋ�=�ٟ�魣�����pg]���sSىE{=?l>9�i(�||�11]��tV�u��/ӯ�K׃/�))uo5u'|ځ.���'���'�)�_r2믚^�,{.�c�f�g�"m�6~���J��I��ŝh��p��D:ړ�Nj�KrT�Y�~�5{����o��찒P��0�듇�.gM��?��rx���YM�wu��Y�g��'m���%*���GO��>��-�S(��;�J���@������Dj�p��]���D:����E4H��'d�C�LC�a�3a �d�B��e��rĵ�,vD�ä��wR��>�i[�a˳��(�|T�;w��N�AO��f�!P����4n]�@�Ÿ(���
����+���
?�r�3���޶mQx�|�Na����%�K5�5�C�b5�[���y��)�c�>���&9Y|O՟�Wv�]��yrZR�b��u����71�|�6�S��G�L�J�]F��Tw?�����
���T�Њ�A��
cJy ���~�8h|y{�31|���P��(�WĄR1.�ο�'��LN|�K�]�a�;��Z��&�(Ø�wZo�GLR�8JjB}�+C�S1�ч�t�}�%��C�t�o
��_��-t�g#x�>Pm���N�A���/�s�*���28c�;��6rz���b(�y�@ �E��CJ��<O�cA&/�}�(p�y�5�Ч�	��6�m�"�j��У�ڇdH�U�~�H�?�2-\���
ON������(~U^���4y�<�ɹ'��wR������5~J�'ϓ���h��u<"Ϥ�&r�Am�)I�)V��ɷ���v���_:&��'����R�_Ҭ�u�
=�[�����i��}g3uLg�#�i�����)GA�ǥq�]k�M��r�EI�l�iؘ��1��>	�0�3����b�-�Q�(�Ŏ"��G��a����!{���\5^l�i�c��xq���(AR*ZP,Z�������(�;���1.�����D��xQΈ�?�5о2"�Tj���,`W�`~�/�g��bB�����Y��~w�� ���O�DX��
K�.c��׾ϫ�����ˮmR�@�_��O�U��AC��Z��[��&#��~�n�˰0�l���Wvc_�4�I]���t�/�Ԯ�?��H�����n��h���>Pݾ
~r�a0�(W9�T��HT����Z[��
�v[�D��D�����O,�rr�=��)���d�U�/~R�����-�D�M��<�i��SBgԩ���5s�&	�rt@�u�u���]sc��BiS�bk��xNN��Fm�<�a��2�]�ϧ�vޗ}�6�,�/7r*\q+: >��-V^��u�5vN$���X��~k������yw�+�ZE��ӽ/�@�vz10C0ѻS)a��O�x�{�EkG|���K>��X���I�H���AW}{+���.��Tu���S���&|��8�t�Z�r�飿�#ڻ]_ɪ���;?	bkb����<
�>�l.$a.���ޟ^���ofq��O�I�YtÂ^���E2PM^�b�ň���!T>,4?{4T�Z�:�&%|����,)�2]��:��sc�8�yv‡�կtC��2c�~Qy?��'�@t]К�S��.2?��(��Q&{��_�.���T�a�(P�g�G夔4�O�R�DJOP��>�
���q3���֢��BF$Hs>1:D���Z~�������i�ZYOC��N����Y&���Mw\!Tp��*$0�$���@����w��g稡Y��dL��W��G���&VL�d˾m��b���¡hz��R"���G8��9Ϫ�b}�S��L��?o�}�	O���=���}T�M��Ӌ�פ�8��P���׆':�a��@���B�_�Uk�=�~��m������K��[^��h�:{Iz�'1|��uӾ����[L9#�=�3>���Y�>�g�e�)�Yu��hߋ�q}��*�9�����X	�0����#c��WЯ֓�����>�?����Y	��nP=�sYz{�G8�St�P���d�r�W��r����bڇ}^yb�
;�s�Ţ�!�Dg�੕���7��ːf��WM1L��+Gy�`�gm��r}tn#:�s�O�LJ���_�e(�b3��<��%�~�Op��<}J#��
"}�kЯ��s���Opҋ�z�����J����YE��>����nj>��;�d���s�������{0����W���'�K��<t�n��M�� {מ+�H~p��4�9��<S�V΃��'c�'����t�\:_�_'vj������>i�>�9��N{�<���mfr�F��s�i���sD{!����裿8BOAh���gǖ��>�I�EJ�H{�:x&��<r2N-U�v�2�iS$8�~�E7�f��c\%\P90ԟ���o���p�-V�����ϽXd��gWq�	b\}L��r��i�^��!e�җ�^��U��f�ħ����^���I?����3��rɂ�x}�>@	�o{���{T�:������c��2>ǜ�+��F�����n��W6��=�o�8�������B�gǠ�o{���A�g���7|Ї�:9��;�A��8H��;������PZe��������6�.�7�Jv�Ʋ{X�SN������}a��}��]8c���do[��Z ����ݫ�{\����[�§�
�!?@eO0k��4�7;���`*����k��By�����a~�s�E^�;b³U��Ĝ�p����0� �����b���s� �Z��7輨;x"�	���7�_��Z�]ȗ=��R ��fs)H1|��S`Y�]�i��ń���A��0{����G��s�	3�4�;��љ�5�\fR�6$+8�ҍ�����/��d�;:{E�sg
&“H�'~�_�I�K���"�Mxa�r*oތ�z�[Dl���/���Pl�V��_3�+���Ƌ�3�P�*u�D�*���Ŏ����_��nٱ�5&w1�QҐ�.9�Lw�i�=��{�J�_��Qg��T������-�2؋�O��<s�k:�Q9�	���w0���,9�<{�>`
�6_
��]�㌧c�>�y����|����*i�~�{�C���V��cLs�ñ���R9���|?��1'v�ո�?WL����u�s�1�Ůmz>푐7�)߃'�m�P��g���˨����Pm��'N5N)G"4���T�X7�h<�4������z�>�-Cy򜘵�=�ܞn�Y���O����{p��O����?�?:����F�Nr[�&�i���]x=�?�S���[��2a[�A|��+tbn�ox��?83�}��Q���f�<���;nj:8_������n�;�/�����]��k��ơ��6��W�^��E��m�_�s}!�smJ�ٹؑ����~eg�C�^(�|���E���c��؎�kL�� �}���A�_/��x�B��7h�>�=��	N|T����z�O��[�ܱ2�4l�jg�X��v{�j�Ha���
��U~ңR��H�6&�bV��%��w	�����=�a�3ه�z�o	�L�֞��.%UP���8m�>|t���V��
uL�}��Ns�o�m`�e1)FĨ����Waۖb\�?LJ�9�z�NK��DX���	���>�P�	o������m{cA�m���+�)���j���ؔ�W�۰��B������J������M��lT;1��x�?l9�}��ԯ��$o��~�D6<P�|�������+��c��k����t�
���J{�hJtD����:;��g1C���ӣ��ua�|޾�=�b �'�Nx��@�'��b��G�,��U_��c��~w����O{��z�
�$$/�)�Tz�[򯜨4�� ?�1r��$��@ZEOp�m�'=}�+	��V�N�aX,i�z���NΩ�>N�E��0|�?��\���!)/���T��/��S�KN��w ����"|%�y���ek���{��}�ˤ�~�~4Q~�;U�A�H�)v*Wy�dyJt����_�1"�H�S���Ƌ6z����=�"����i�%��-���S}��x^I��G���u�GI��z��:���s�L�< ��
�7�����9����VS}|W
��W�<��xi��>�,�ȱ�D�t�X��ǯ��/��b�>��&�j����\j�+�QX��I��r�R^M��˯�J胘v��:�A~��Le6&BIÓǺpj�R�@��g���f��5��&v���'K��-���	�o=%��c�Cl��No��W���^��P
%?������C��U�4֒�2	�&�e� Zt�X��x��>A�����>@?r�𫡧�=�O����+m�~��
3�*��.`�B����><�ʂ��?Kic=���Eh�+���$?�h�GdX�o!�\\����删~�A5ʁ�癘K�5=��ފ>3���x��n�S�R��n��66�K��!������7��&�&}�>�ט�A�PZ��f��$U���%B^��bR�/�Y��<:4��}�
]2X?���'�1^����(���G�Gd���
z}TߏfX�d�h��6����o�OO�4����@\PV�0>~�蔦}�Fj��� ���4�(��vC�pLQ�ݿ��/�P��肰��'��|�O�ߴ���bz�v�B��h��1��F���c���g�z�ל�n3F����6�N�bPzQy���6�������6lx�4�ɶz�̣h��=���MSRF�-ɵb#_V'懊�����A>�q���*F[L	�c����6����S}�H|�)�6�,�I�%Ng��֞�;���������DH��>�,�
�;/�-�������ٜ�{���i��V�`�U�L��)ݐD��~8�f���lS�"�ު:6���YhQ*�ѡ�+TsL@�`����R�#8�e�dr>f�jg��ג������A�q��gqhE<��Y��l�	���<�-�0-��j�@7�/��/8���R,�v*��R�
�Lj�$�<|`DD�k,��Ze�}���Y��yl}�ю�2�tt��q��@��[Xj�=��pJn��d���?�6vb����&Ї��o��OM�?���2�@g�W};�+����ú>�����S��{��5+�>��7=���T���ʋ�^�{ʽDO��n���=�}76�q
>����C�Yk*����@R�Q�Y�No�NN�M�
���)�iv�\�>�+p��M�n�>琼R�)�8��$~�f�9��ߴL����u&���ĩ<q�����7�4��o1�Q��s{�z<��=�w�д1�@�(g��%�Fi��T�$M�r�`�+̻Z��1"E*I�W�{����n܁�}�"@;��(��08��sˏ���@�.������Ƀ���bA&ց9VX���':w���G!zvNH��M2жsf����~�nz��Q����1�P>�X�]pt�}���s�4H�M|/Ԁ�0R�@�j��u�&|�G��oN?t�6y.au)^F���<��L���;ץS��b�hg��$v��`�ST����-���7J������w@H��b@Ny��>��K�T�y��S|���b@���'OA>kw��q�=�!�>����#N5ԯɉ��K�XlP{��>�_)67�qwE������B��_}@�9O�<��$o�=�&?G~E��y�0 ����Qr��j���0|0=���*����M��*0N��V��s��}��Er�]|�kbh�pZ�b�k|����~5��;�g���5{Ƴ�]"`(
S��ȔWc�g�2ƛ!����eϜ�V��b�R~%��ί�2���w[����Ǽ�!"ShW ��xh�uώ�
���!�L��L[IP��ˍO%�Q�l�/� ��g7��4�Զ�(_�f#���_V��|����7�D��Sv�=����N���f�1o��`�,��=��>H�7��9�H��8~RX�R�x�erGJS�ݵ�R�	��G5�5���K�5eRY��g�,���b��R�������p�ؘX-�U�n��We��l���1���O�T�}��}A
�%���W�I�i����bD�V�M�@r��C�Jb���9�a�|���~���_*��\#H:֣1���3�h���3y�}�u�=]xN��A�!y��.�U��)��g��~5'#}y1�뵀3�T�1`�P[M�sw���S�>�����B�Q��Q�������"��*l|io^y�\�K�)I��?"7���+�e����\�=���~�����w�'}�`�k|�ǢR��çv/��W��=��rP�I���P��i��������|�?�IIr�'|i�@��+��ۄ��={X,��Ҭ�'��<�}��K��F��V[��=�-����$|�_�� �P�D��CAi��8/�>�)�_�g�sZ,���3Q�����:�7����/DH~<� �/�AP�;Ԣ�B�?�s���E,����r��=��m�9{��'9��9)7��W������ԣ_���� Y,鄩��h|�@����/���V�!u���fE��B�a�_�ܽ�4�-�9}�ѧX��k�s1�8��#c�����:w��<zz�|��d
�ԯ�o�����F�.����$|��|*o�4p�H:O�����<6}�_��o�QX���3�@5�ӯ�}b)T���7�|��|����Q߭CG~�#�l��=l�ә�M���<��D��I/�l����g��l��[�y�͞_��~��8�I���]-��_��^��Iy|f�~���ȯ�e�g�Y(����v}�m��A��E��-�?���n1}bU�gR��vW�)F1@�w;t�0I-���?��X�}��>����Q���>V��zV=�{r�{$y?����:t'��m����.!��>�R���zDŽò��_�y}L��99A����fQ�a*�%��)�ػ�	E�\� ����
���l3��[����K&HB�?��\����"fv������ئ�w
\J�'y�rɱ')����?��&�R��n�Hފ��|��_�_Sޑ���B�����W�#z��hT��d�+�����)Q���|���h��%f8KO�^Ģ�����C�����A)Ъص�����8�?�ǯ�+�2�m�Hl���~vC��!S��u��;q���tk#�=ԯ�-0�?���)����S���裉�����+���};zAM��@><�s�Ƨv���gb��E�Q����:Xf�H��!���`M�%�g��O�L
7�7}gr�/�f�iw~�|��
��×����^h�[3^�>�Ǯ“��o���J�����GRr��S��'��������1gw���=���@^z����q)���XW?����~���2��7���)��֯UN&7|�bRJ$~|o�-{Z�_�����3�\azݞH�s��I�F��sa45��ށ���ny�h��b�� o�{KM&���\�z
8>�'A+���%��Oݝ���/�W>e�Q_�%T5���n�Z"���b=A��$�:��>�ĔOn��9..��	�:͈b)�%��#	k��|�.��=k����]��y�
 �0ݞ�<:������(�k��cg�|��?�W~�hQ��$|6$��/u���m�yP�����Ź#I��Vm�`�������5]�-6Z}z�T�h��p
r�cQf���������Ne�=�nq]ќd$��ܪbV�'��|T�k����;
������Ƨ�>"	J��2g?�G��i�>Ј��,~�saR,��H-���<�Or;�ݝ���4T��g��;a%l�F�k��+��|�]���xb�&��M�>�:jWX���c*č����B���<1�2p�U:鮮-���i��>*�p�ʄ����Oٙ�l�M~x��Un'����r��${���[�!�Ĺ��Y�\2R�YU=yY��A��X�!�������f�]ye��Ÿw؆��h7�+�o5)�k�)�������[�dG�ܺA��QN����ʕ���>�с�kfo�ot���� �̽9=�Vw�����!J�ݝ�>ڵWtA��h�Y�o��9��@e�K�7\�;>>'�>�X�Y���D��>�m������ �w��F,|V�����fo�����n��/���u�@����輌<]k.�:8�5��w��K��>?P
�^IV��l\�(|6�k�6��Yh����"�b�v�Q�=�t������Ci�����
��/V�l],��[��;�y r�m	�L
P�>�`�!�!}�1
��Y��3�8	���^	o,�C/|��X��-����b��C�^,��yY*g`�̖�gɳ�dj����*r+�R}���9A��S���P��Y�$���{��7י�d�5��F�[���3G�f-7��A\�}0�u�d�_on��ؾ��Q�䷈ݒR�hҲI�>�b�8k�c��hд<
��q{�_̲E�_	�WJL|b��r&��U���ٗ���n�7��E�2�4@I~a�������47-�࠿��1{Ѯ��Ol:�t:Ϛ!�Z�~<����)Mq�2�W�/��io�<ۗ�bY��Ч{�>�!<O���Q�����獆�x�<�<T>�sE5�K"��"��F~�5|?�DpB���[��3|����'���9�4�
��|�{��~��,�_
���9>�§뙏�t��W�e�g�������J5������~�׏_�;18/����9�>�����'���5��Kǽ�xe��gZ���ebe�_���'����G��bC���I/�HQ�c'G~��Z���>�<�����?�3�3�IG~��8S~ԯ�����-8�y|^��a5{`�G�
���8ӯ#>~�_ɳ�<�����ɏ_�?9��|MOtr��+�y�H^��.�z���Ϯ��:�R|��
s�d�
��� ��D����9�}�`^�y�d�3>��W��/�|� ����\��:��@�w3^O����G��g�^�_�K�O�¾�v��Q~3#�"F(�%�uZ1�`_��s-�Ű7�O��=Wp i�%nr��~A?|L2��}k�g�'�>t�kR�v�T�3��m�[�b}hخ�o��{� ���eM����w�\9�}��#�#?j�~�'}W.">f��M���7��=�*��C��.�C�ɉ�^�0@�b�G
������i;~UnE�_گ�y�0����4|�qw�ߣ��:EN��k8���=8�>v���6�y�I��ȏ؎o��"��9��:3~��S�^�q�|��NrH��h�6�_̃��'����_�72��wc�_��B~�l�m��g�RQ>�����nT������ZO�n��>��9�_�ǎ�<�ա����
�ԟ���O�����L�p��^����̸6���x���u��O/�ɋ�������1+�S*|Z�VJ#ϕO̍r������t|^��S� �^���^�3_ϣ��^��Ƈ�T>y���S�C���n�>3�:�g�E��-y
g�>�g�$8��|�?C�I]3�g>�z~����ʡÄ�L��9��F{%8
���ީf~�^�s����e�\��3���~i�5��o��3���gr�-�K+e��y���|z���y�}����i�?��o�3�Rr������3��R�U/^��]�b�|��	��g%���؍#�_�ߓ��W?�_�I�g\s�h�mo���WTέԒ�Oݕc63�oy8�v�䯽�Y��?�+'��_����~�_b���}��v;4Ps����蝘��t8�6����T#c�S���'$�}����7|��^+���칕�F��%|eC-�~��6>������}�R�]�^����+�$��)߃&��B�A.LD}?��J@��@
�B���w���+ �vK�bt/���G��W�C�v#B�-U7���3Q��A):3.U^�B���/pa.ȱ��>��˓�;����(��������|���h4#ߢϾ#�N���廿	�~a:����^�/覲.�����xK9�r�)l�ꇿvWy������p|�Gp�E�����=���M���]�����S��'J��Ƨ���n9�OLe}�gw����3y�+|�'��_�I��_�.�J>���#���Y?h�\������|l��Mg�PǾ;���
ۘGg�qS}tF
M(���<�H~�`q�O1
�|�*�@���O�"��>�Q�0�W����I���z�ʠ���_t}���x�A�S���g��??�G�V��%��
R�����x�"W��8��<�|����&�+Ϟ؋�w��/
RU���_���-	V����O��&���r��~A)�-��X��-u�Mh��o3{
/ֳbeD�&=�ѯƩ�Cs>����X��>߫~��,��Wy��?�{u�3^�<|�i3�h–{���ϯ~M>�D��8O���:b�l|����S�䣭������T�|����y�i�0^�Ip��h��1:G|�<=g(ӯݧQ~�.�i;�#N����L	��>�M>.������*/5�f�/a2|�i�H�;0�|t�(`��������Sr8����,�D��ׯ�̇	w�ӝA�bt�Z+���P��\��Y�j��z��)ߏ���>�����S���6��J�>�8�[�x�9x�cs	z�V��7�4l�E�’�[yqH�W�	D�!�Ŕ�'��?ڏ.��S���]�Z�R*�R����?�G9|TS8�q��_�S9�M��_��J�C_��̯��
�χr�'$Iu�J����ׇ���ءp}Lz1�?kx]��;ryHSw���Kq%�P�}y���>|��Y��P��K�=�Uw.������.2G�A�A7p\Äaʻ�,'�N%������w�VFc\+��adk�̦�� ^:'>��638�P�ny8Y᳏�k~���TN��6���/��v�=����!��8��i�b����1Z��h�0n�!,�|�N~�Ec������}
E��,����էM�6��G}u��+"��æ>[)uA���'R[)��H/z�&8��t��}�����x��Wn�
	���?%;���Kޝ�k�`����Dw�@��ƃ�Ȋ_^��0��O���g�4��>�͗>d&��"�P,��-'��ŝm<����>
v,���5�-������U?���\�����	��h<�����aG��}��@�P���uS�<|����[��X��BE���l�)O{Q�'�/P����ЄT�ݓM��&��践�8T�/�Z�8�e��\�9ז�%�GOt�B��QG�6��RW�	k�@��;~�E*�1�4wގ����@���b&����{Ȫ������?ǔt�V���|I�Ħ(�p7�y�.�s�Ju
���>��8~��M�=���z���abe`_}dJN�u�k625sV�3�g2>x�]��i��"��{��~�uo�Y!�v�	�I����{�ȍ���P���sW�#f}u���1W>�}S^��q����/���Z�Ƃ����_���2���Tޡ��c��E�\��y��ͯ��*:2�iy,��?.7>����6z�O�5�r=�	?����}ކO�f�I�אG.�;��8�#�I�P�+��s�R��ꣶ���L�f
>���$P3���'�iO��:�>������8[���N1XN@u	q˘��?8�k�1>J�/#���'�O���:�@a�~edž_���~�9j�e�6>C�^	�A�O�b���3EE����}�;5�#z�������~�ҡ%D�'|�����yΰ�/C�q��~�S}����7�X��rz�:\ݼE���#������0��<�E��8���}�31d��`�'|2)�{���Ϲ�`�� �wİ���O�Y�?���C��ް�{��w�L�DtP*0�=����	Ýʃ�ɜ�Y�e�'��b�2����л_�X��U/̈́�����4����ϯ�'��_�H��^����Kl�y�5�ׯ�[~&�G����}�o�R�؎�m�'����ŢAr������*���9My�����]����:�~�����K�uў�"61l�� Ag2��eI�۩��e��l�>o�}�(��>�BS8w��h�'*��1��ʍ��۞9_����/�+yb�%��l˼�M>�c����#�o���㗘��n{����V��]T�v!�@)���ξ�~�E�=����lR���=��b\��_'�	���姟��n#�'�-?��;��jOF�;�����Ý�?��o4���ъ��F>�CdVq�!y�s��ǚ�S4���%�E�����_��)��h0��_R3�O���g@���y�w�{�%ϕ���L�������w�gW�G��WiJɏ����߿w�U��>�gn�ʾ���Φ�裤ؠ�C�����P��z�-���7��i<�ϑ>�|�7�@$q'����&_Q�u��ag���+�	��'G1h䙉h<%?����3Af@�_�3����i�U���J��@�`�4|K����x�sUz���og3��'�C��g�P�J���W)�g>���P�?y����!����E�'!QM���1�:"��V��^5�)�q�yƯbѴKB&�/��+	T�Ȳ<<�/Paó���#C��t*L�Dv>|��'*q�Q�ca��|~��
b=+o���/�~E��W2��sW��s�f��ź�_'��lo4?l���a��ݣ}�����Q~�ˎ
���:�`�^���+:'r�g�9�y��kG8��!pT��*�8�H���b�"�k|�>Jf^
�)��ъY����>��1!�b�A��5�	���gb�ԯt�S�C���z]�:��'�L�<��g�y��x&���&n6M�_����&�]L<w�$ϯ�Uyt�Z���/�JO�����;��I�e�䅾D�.��/F�PiRޙf6��=��%�C����V�H�PoO��D�~�c��b<�at�_���<T�:{��|g�_io�^�n�
��_f�&�17�î?th��X���b"�0'�8��.�'/M�ޝ/�(��"cԏDbM��������:$?�=�S�x7|�_�+��	�C�V��jD��>����U�	�G�*�
���7�8�8�����ݡ#_N��~p�W��h}|�����@�g������a���u6�<�P����=B=���F��DI��ER(�
���_D�����թ�$!N��y"*�*��Lr�|
�`=���_DK_p�����.��@�3�����A���W�]J�Ng��8x" |�H��p�N&M�bD8L�V�1`$�ʋ�A*x�zJ�-��K�����צO1
�e����g�2y���ӹ���+���
>Ӥ�
�>�g
w@4��e�OYط����|!�E�Ä����x��'G�I�� �b�2�ɭ��Ge�4�~���.L��K��;����<Uܓ�Fp�S�����"'��Uȣ[v_Ä���g��鰮rp��Ǐ��AO�*��q����<z�[9�!c1=��|�C�؎�Lr�_Gy���êX����(A���$��
�_%��_�ʱ������f��>�����k1�D7��Ik�O�#�|���ʻ�	�s�_C*z�g��>s0�rur*F�䧹����axN�+�)��TF�!�c��в=yF�^�����v~��7~��_�W�cw
[���<W��X��[�m:�ůMO�q�{�����g�r�St�tw/��|���f��•F�(��v��)y�C�}X�-A��Q6�� ����,���Ľ�aߊ�Z��.WѼk۱k�4��&ԇ7i�"��C�ʗc��*c�r>=(/���{�C�̿����^c}yJ�~��G�-�\���ۮ��lz�෇������d�%r��4M���Q�o�S!���
���J1F�J�T�ivz��?,���]�塁+RxE9��-V���ڍ���>[��y�	��kʃS����J�ٯ�	U���dye�X�be*'������o�߹2|�ړ]���������b����]��7�^9?�oã]pޫ�	�G��h��P�"L]8�MTS	[�'���%H���D��J�rrq��6����R@
u��odZ����?�T֧|��*�����7�Y����7h��ݥ�?!I�8M�� �'��V����fe+����oF��|��򯔫'=�8Ya��A�<�h=�3�2�	)Ύ(_"z��;|4EY�f�h�B7t�u����u�;�=z@�u
|�u�U�*i�N�'����B�U�N�ߟ]A�e��L�)���I9�bO�Tn8�ڮ����^�	�#t��z�\E�6�%Z����~���(8����`��� ���"���H�E�tߐ&��b��?Y�.�
:���1��_fI���5���_��.�'��5��1��j�k�5'�h�>���1}ilN��R>J4M��g���NE�0��#y*����ؠ欃�"����'o�&�7L��O������Y͙K��wG�b�>ƫ�C]�f�7�����]Ђ~����Y�F��	�=�?����W��~A	�?��Q�%R�i+Ɛ��dC��^���R��� �)��2|�o�"��"[������o7�#�jnT�/�G�El!<��ǯ�0����Y��8{f/���}�Y�7���9�f��y�m8�Dt>�_j�^�[U��b��7���稿�14���F�ݲr�ut��R5?u]k9��+�0$�9>�*&`������H4!(�c����}��yښA}t�[
����k� a(�mo������)�oa�ŏJߝ�!���zK+�p��{!=����Щzd_D>�:8�[��b��������{�d�i.1����n�>��3���w{��*�b�C<��iXr��*���+�9��o^�F>5u�ReE�+)�G����s�x^2s�N�t�3T_�4��"��>�3���]fO�o"Q�I
�s�)
LN��t7PK)�!�/�!K�Xڍ�}�C�c^�J&���M�P��	�'8���A�$?ȧ���ɥ\�֢vl����b=�>�w��;������Tߧ����$o���y%��L���|9���b+}�*����i�~s�ľh�����3I���:U��^�_�c�����8�+�z!?=ѳ5Xt�h���C��6s�O7|���V�Œz@88C���c������n2�
+�I~LC�S��b���Z5�S����jzٽ���w�U�ߙrw&�j��_�J��^%�lԿ�Z�Z�������mv�~|�_,1A�#�H�ح�V@�ce���EHM�Hc�1�����ƺ�h�ա��y���P���">i�%
|�:w:xio�D!�_���d�I���
�Q������L��c،��O��> �&��#�.�}������O�]/ι(�C�Z<��o�)V�#QA�b�1	�O*�\셎x����d���F4M�V�
���s�8U�Lr�x�]�n'��(G���Knw�
��~)�V�,�t�rΖW]�|4A6����GՅ,qS��]l�O�?��x�����0��@%��k!��c�O�#'������������8�~�&>���R���CO1.�S����7��Л؝be�,X�[+E�S���-Q���F�ͳb��˜*
�g�AXP��>�	����F�_�;8��^l�!eo���O~�%~�Y�S�>��Y����H�- �/�A?��b���X8Uph�X��ͯI�����ܕ�>��1ƣ�ػ-?P��|>K�}��:KCD���=���%ݟ�e�
���>*�=�Nw��R��p܇�*�"�s
G|^�����9ig��=�iH:^��8�v�c���ʅe��z돃l��W���=��'yV��b`ӯ�3��ᓼ)9�O������8Ej� �=��	�{�c}jQ�&�M�}�����[�{�A����
�����Z����X��K��{E&b䟞���1IO;��>b�?����{���w�6=:}|��[lD1���g�z��`՟�s��ï�R��T��0�|��8�h�d��Y�G/��ϨS�i�!��'8�T��R=�˙�@�g�i��q���/��X�������V^�Oގ��)���O��<�k���!3vc�a��:8��uz��9&�O_��w<��W��Z�����쏑�.���:&i5]��Yg���=N^Ow�ZO,��5I��}�?)�Ǘ��Ҕ~~�&�=w�P-ӷ�����O��������[mFj��q�^�L���<l|�c����<OlS�*X�b�Le�p�<��4�Zxn��.Ldi�~�Ut�nk�q;
��<
��.f��˫���q,AC�'~��t7x��}�pr8�;{�p�ࢡ2����k���k'�����B�<.��O��|w���c����EJ�\��/��E�K��[�4O8�>
������c�T�K&����m�b�޶�r���F�����^~^uJx\,?��
�dT����>~��rh���/"19������eu��>%p�Cʋ1DÀq��>>��Ý,^)O|�Pأ�H��!'�1.K�D0 r���i��8y> ��<�=������=rv�(���b#߃��S9��0�V�f�x��wф�5`+KyWogg�0��:D�{���O����5�7�A2|�o����|6Y�b��/�4yS�ۈY;@~t��!��M�����%���>��]%#��x��_�Ƀd�I_��>H�P�){�}��>��)������G���kUy�6W�Q!9��bt��<�}F���Q��)���D9�\����J�S�a�L=��z�[(K����RGQ�����pL($�W����=�z��1��4A���#�����]��
�X�Q�M���`��Y�z�2kzR�M�F�G�O)�p#9��
�dok���ϟ�G��T�"�U���ӓ����C�魿G�h7ܔ7NE��K�5�!r�%��l��z
�n��+�N��WAˋf{�ˆl��-Gh�s�)��Ь_�=Ἧ>�O�>����z�0�[S *�c8��ؑ�_,ͯG8��0�����d�)쪫,vD}�����O�
�mhi4|�i�D��Ѧ.xڢ�Ė��c�KmaV~���Y=�:��} cM6�Q�>�\hފcb#~l~��(�‡����rW��M���/�j�|�\r����#�(]&�彙ؗ�&!�qwm{p��ڹ7�N?���Z���j&�:R^�k��!4vL���uW�Σ?����㖟sF�>�D��ƕ��fB^�beZq�R<J�/�����{�wŲ<����_�?�;'X	��h�ut�?�����d�&|��$�����WF!��{xN�}괩���]��Oyk@V���}�
	�əqu�ҋ
MiFO���QsǗr��;a˾$���L�
`L_�	��f"�Q��F�vP�����˶�0��ZBOXhyG��.��/Pku�B�Gba:��lʉ����@ٍ�)�M6��
��U���V��	�'��^��'#�
-�T_��
�D��ΰ�^���c�߈P���S�Al0e�X)�yq�$�����cx�S��/8���l��{P��!��;��VT���FκSi�k�v(�Q��I1���������ַ���}Xiw3�u��G��VE^�߹d��w�.b“�Ç��������/l�a��nb�����E�b;�C�-7�O�f�b�n~/,f����v���)8�8l�g�K�����<�~%Dg;i>|�v9��_D��{��9Q�f�%�.�YEp��>/�st`���-ƽ+/��}��M�E̖���Z�{����Ͼ����
�s��X���T'��8Ї�tGX{Wd[&�2Z�]�"����U���;��l
�t��>���kx§Y���o�yQ����K�ޔ�I���Y�5�v�|��Kz�.���C(�?E"S�=Nx�]^ڽ^�`����λc"���#�3���%��,&��S�;�o؈>�[\�A�l8��K|��Mgo��FPm�X2�c����'���@_��a��S�\���٫�/�ˎ��f����E�F���P��|�o��>�,B7������#_H��S?�ϊE�{�wV�Q@���A�ƮI����M�uB}��A��[ʍ�Rw|����G{{��T��ǚ.�d��bD�:��w	��b��Z�ܘ�CJŪ'��A��&�+�k�t�)_[�>J��&ﮚ.6%��{���CV���_|��s{�XO�'�w���=Q>��+X���̿�|���݅�bY貶�I��jZ}�z{��P�wէs�|������r�%���S>������\#����LTN����[�W�Y]�y�
/�77�C���~M�'Ɍ_�n��:�X���'���m�������E?��_�����;��|!Q�S�$���7ә5��Sp�rV�I��}z���G������[-�����0x�1|��|��o5�2��,�RB�>��ݯ�����:<i�Ǐ�# ��'����%*��lG�.�#$�\^_)T������_��9�w�t�'�Ѯ�R��	����d���>��f�vb�~���W��9�@]�<����M�v�T�+�٢�H�>��k�~u�v�:<U?��\D�����	�E�	
��'fo5�@﬋����&^)7��;���Q`�d������D�NY ��>t��3����[��Ǐ�a�x�������[����Y���@^���3�ш������V�bS��X���G7��U�EƛA��=����y��Cb�>&��/OI�)�C"�2:�b2���k�g|b���;}Jj���Ѯ֏{Ks>�6ş��t�������i�_��¿
���PT�����7����̄y������?�y����Z�l[L�i��x�X�^���"���'�`ѡ�#|��ITQ�zo}��n"��
��~�d�+������bY���'��W[�*���,���k~
?�/5���29\p��V�2��+<1K�:+c���U 2࿎���n#\k�|�*�D�C���i��1(KG��bz�N��8�˼�]�YzW �v�e�v�g1I:�eGx�)�O�;�E:�V��v�W1%��<II��i����.��*��_�G98�?�Y�?	�+�)P�����GI���T��#�J�;�E��_�~}�c�ʟ�$�.9��;�Z�,?m~�we��d-B���5��A�Ĩ���/��P^����E�L���ݑ�bw�qOnj�?���O��'��b�>5��(�/���<���(�TЄr��s>����g�ǝv��z����Y}#���`�~�s[6��)_H�BO�m�4�����Dې��C��d�*��E���b`7�"0�/�����{1����PBO����Oʭ
�9o��G}/F�EL�v����[�f�q�a��ï���F�>~�o���@�.6h�:^t}�n5QD��G~/�ү$d��L�a2�A�f����>�S�+8J&�$�y�O�j�5��_��_�]!��
}:�.>~U�{T�n�!��P|/pp�<��=�v"Hw;�gVF*��R�$(��&6���IL�u���N�W��g�����>�e�5p�c�ȄĂ�N�۫NN�����|Wy¦�c��n��}�Y��'|F����Y)�_/��l�n�/��ر_t�f�떞�孈�>Ÿ�k�I�1@��[�s/�����/C���Iޚ�k>����3�3�2{k#���hc O~�o�e��?�X5�̅S��c�]��q����b�3^<*��=1�v�So���%� ���v�Y��+p{�~��+�M�RbUP�BV��
��j7/�����RE��/�O��
z蜾�e8������rW�wD!yK�IQ�j��u��!C3�F{���*��y3�אr���<�y��K�iK����ӯV���]������ec��L�����t֩R#�z�HB[j�蜜>S~��bѿk���`�/]?j�����H唀��gȟ�S�K����nW��	�7�ӯo���3Q��=�������<'�e�D�	\�0�_]����Ha]ߵ���m~�^GO�M���e��F�7�-H�c�ȓWS}��/��ˌ�6�<��_;���ӓ�OaI�����x$�B9�l�~����Z�Sl�)���:�a��_oo�o;�܂�V���hڛN�y��_��3g<�M���3/�@��)�2I�L�>�Q�Aw�4�p��4����ʞ�S�mH��=��S��ӝ���ڀ�/p�Tƚ�!(���d������=}G����G�QpG��>-�5�Qu]r�[r��O���ׁ�f�0�;��*O���S��F��Ul��b4��t�n�3�0�LD�/�||i<R�����g�K�'R�+d�n��'�B��tfl��ᣜ���b^���#�:Ny��W������ާ,ɳʡ�]��Z/�A��4{>��߮b�^�$�;�n|T_��eϝ�,�a�@�2
y�IV�/�ܓ<���	8��s���g��@�:��9_��b�x�t)���U�k��:�:w0���|b��>Un4'ނj��$!C�e�v��-�|Q�`������Q�!LN�:�,�c�[þ�>*����]?��/?v�ޮvg��hw#<��O͗W$�Z3�CzK�C��� �6$�hZ)�7��~�	_�'
h(��+T	�9c��|>PB��Zl9/&XLDZ�� %<��M�w��tl�|V}L��t�m����Ȃ��{�	V�z��&v�=_��䈴�pA��1����+0��J;?�f����4墬;�K�����I7�{g*��*�D�
ݎ:��Ժ�w���
_�ʨ��Ov�|7m'��"�~��Q
Y���Fyt��-���c��W<�N!E��&_y ��ϫi����P'�-���	�կ�2��=���aDK�+SN�C>�.6Ꞣ�G�9�q	r�=3ŀ.�j��5Ɩ���'||t��m{EB�mٽ�)��<@�h�g����~
N�c�ԫ�[u��?��d�ȁ�\.�Iۆ8�rd�:vz��ԂE�~<�	9q��'���1��ii�����m
����� ��j}O
x�Y�{~���u�5FA�_n�	����X�eRn��l��yb ��{H�b��-�1��x��>��ť��S��ʈux��
����S�5�������7P];��SO:�n��_m��rT�+4�9Ӆ4�PNVo�N���Ԟ���۱���#�E���
���S1��M9a��{�o�=s���s��Ή�o������ýX r����
|�)K]O*F��Nb5���w�t�q��W������|!7��^���v>�+G��w7ι=���B���.����_ڑ�D�H,F���A������7�GĪ�*Ɉ��Op�/.!N*��
��o���.�X�_�C>�R>n^GbV�pਯ��}鳱h��	�cK9��s�s��[���l���_���G�dfLlM	�f�kF\��ߵ�:�����'g�DEB����~]�Y�n;��1t���'�=��}�{W��0�~�f�X�P��Y�}�
+o-�Zb��*���"�[���ھ��Am���`�*��p��R(kE'�B%������d?|��޿3�]ӫ��}��^p��êl*H�z�y^{��W�V�?��V�<8�b>s���B�,���$�Y����uY_‡'2|L�t���/�����Z�Ԕ?7�V;��T�B�bH(4�%�V�X����_�s!F	�b &�0�U��Q,Y$��ݑi����-lWi/���~��եΪ�S�z��Y&?��E��x]������b�{����ϟ?�No�1gkr�>D؉�KDI�C�Ғ����:�_/��{�,?Y��|j�W-��Ğ��'y�_���/���ɴ	>P�'oW?�:ߣ���,�T�=y�{��>ۛ����/���, ��O��O�O&��7c.�`>C��3T�h�iO��+~͹�5�!f&QQ�O�ц>����'��+|f���4�Qs�سlM����X��jn~N�G�/_��Ob��'>}�Jr���<
�;p"B�>C�6Pc�$�8ѧs���>qjb��������ۑ��f�5�
2^G=�9��/�y�"wv|�Č#b/�8���+:G�Ư���_��{|����9ru����n��"kzݧ�=�w'9om\������J������p�|N?~�	0�_�D���hOT��=��)fe�o/-��#�}�׿���=$��5r>Q;�>���pҋZNv�/��6�?���̉��QbS�ӧ��(hz�;M�U:'?����W�ޞ
:?�mip���J'���Y�8f4�ݿ�G�����J͈����n��{����쬃1�M&˦	��h���TS�$������=�ߣsr>DN��W!��d$�s��n�co��i�P�]���յ=u�i����p*|>�"P��v��Y�Dύ��ߛot��_�!?�A>���GgB�<��#��R��\�Hz���9]�i�<(�ꏾ�[�;�R��b>�D�5C��ދ�w_�^^Py���q%~!�!w���6������8^@
�!��v{�+aӍ�|Q%��v8:����ٽ0�Y�Vn���k���>�/��3��/�`�:>7v�w�$̣�t�j�U�x:��?�@>�@�®��R�m'�����&�n߃��
b�l��=y��|�sܻBh����_�Iߕ�?�qd��g�	�y��#ϙ���M^A�^O��?y���W|?�$���_��YΎ+�t�����u��O��H��H���	��<����S��0}/�4�_��M���3���O����g��Y��'�d���>�v\綗}&���z'�M,���g��y�D�Ưw&�GT��ܪ)��*P�S</�a�M��i�����5��/�Ѯ֯�Ӑ�~�Ñ���9�x��3?<�գ~���μ%|����g�o�'����g�:a�>�yT&.�tv"��v�F���ǒ�?׃�wbt&�ʬ���݈t��Q/���|�y�y�7b���|�Ϸ>85:�<�5ư�j�qG�9Γ_���g櫯�	�t�;_av�C?�#�vG/f�<1U�|����b=��6�9kqԯ�N�,��-{���=��31d�3wk�����L�*?.���y�SC3��x����h��_�=�ya7�c��`[l������u�����Ů�}NJ����.8���E�2��T����^f��+�s���{���f�?�s�Rߞ�ߥ�;�����.��z;�g�Myw�\�<96�V�>�7Yl��2pйHC
+_lh�A����)�%v��{t~�C����n�H�Z�l�nVl�r���:��[���?���H�_�vw�
M�9��Q�<���S��jZ�����#fB�Ÿ�E,��|�5�|K9ր/o�Y5j�]�,n�)�vԟ>9�Xa��qX�E��m���ŗ�θsu~���<��X�G1��q���_r��ۯ�܉�Kt91��|�y���>��+�1�C�_�N���|��/�E�Ɲݯ�K�э�W�ȇ�k�ۃ/��+�>e.//������`u����/j(G�Dp���s�9.��^j��{yG����a�	�,6H9���z�-��ئ��Q���/:�<�௻�����a�b�+s���Wa
�v������S��_�'<u�pj���Q��X�:�!$��j�b��^�~�/�^'�{U^#嚠�5�u�>v�v8��a.�N/�e���#fE�eg�J��J�7+/:)�v��/T�8�}�.8�W��	:�|FO��b��/�*��)�����b
�_�b����>�	>�����.��>ਏ����}��>���~��2[N�?<ѹ<=�?8���������ɳvǞ�o�r5W98~#��-'N�\>��c�r|!*%J!W$��n�/hVgbM�i��[~ 	�q��.�SD��y!��\i�4:���<�w�>��Q�x�<����r�=�(��Yڝ�V�A҉��K,���-�ah�3g&��G/�����#�
��W�d��e%�^�_~;��f�f�N�*:s��3��<�f��Q^�6ne�_�pu�;
��iW �>��u� ��WF��]DjWXI��r��Q�~s�(�F'���Z����Q�[�q��廃fy\8������0����N���'�rn��U��p|����(w�=y[^��΀[�|>�-������һ_u���m^?��7(�C�G�w���E����E�ι��[���7�4X˧�D.���{��;�O��j��\�q��^�LӨA|�
}�����[(�_*+l�P�p�ׯb��<�~�y啉�
a^�`!_X�v�1wX�2#r�����BN��G��	:�2V�v��
7��:,~�K��S����|9�hYt��m�3$�v�ږ�67��M�����V9���j�!(�Π+���S�ÿW
�J�l����4[pw[�8�@���()A�w�^���{�L�YLJ���S�t�}�kO\"��ܓ�$�j�d�%+�3jh��|�q���MuP�^�BX��v����1b��3�6���gz���+��j�=�k�qqy��?�,���1�y�r�+�4袏�
��'�U�8}�n+߀�p��"�>�kH�2�$7;6|���-1�N�<�`+:zZ�\\]K�a�Mz쉋��ִ˜P�~�򲴦��X&�p���T}bgC��]����8I�>�D�D_N�%_��]~�h�E~D� �� "�!*�P+(
M�
�0|�c�tn�c�Y�>��_�†(�CQl�����|���>�[~������_�
CL�f�qj3Mu
}��ZV�rP7t��~�,������_�KD|S9��\}��[_�G�C�H=��0rhߠχ���P+�m���6��ݢ���A�D�]A��A�n�~�����\��a��1ZeNO��"�#:s����Yzoօ�;�+��Zf��` D�3��y��8j�#g�'|o�-��5!��G���t1�7�Q;Zr�/xg����.9�ki�5Xd���[�_�Q����@�o��C�Kɨ�R>xĂ��<%�`n>}�X��r��a�@Tz�/u�+�YK ]'��5sYI�)�Ƶ��iK`�(�;�B����/�HǮ�'O���w��U���ǨU�D11�6u���p�Z�L��+(f�yuA~�7	�ʢX���#ϻ��͗/�>|(_�:	�r�����eJ��k)��L�t6��T��RS�]-X��c��.�JWZ
�%a_�����Ӫ=���X�0�Њ|:H����6*׫x篿�� ���DH����)���#W��N5Fb
���ǏV+*���v���/�p�ΊIN�<#�C���pӥ�0yM��g�0��#�#�'��J���5��3��w�ˇ�=K'�r:�|�>�g�O���CNTS9_*�X�D[����:�D�1n�)�t�@�.�����U
��M�"��D���8E���'�|�{-A�4Dt���?>~*�d��ӧ���v�P9��;����R�C/���@���(���s48�uc�[�kq�����_�٩M�j�v�a��B����q'�I�?�[�_�Sg�ݻz���y~y��b��<|�r�]��������r��������㫺�pM����NzQ>h���RJ���[z
r��x�"f"�1v��],���j

���۽�����4�O~>���u&�y��gՎ�L��j��)�����:�1Z�=��+w3e�H��Á�E1?i�r�����C���n}iQ�]���*�2��U(�2?A�a��2D�v�p���W�2D�X6�[��cu���N��E:���=�]��wgY�(����L�͏ڛ���J�7r��C��G��tĿ=-0љ����p\_}@A�;B�����^�ʤBy�&LNJ�*�>*c^����������$�$JN��Ç+%�� Kw!~�����Զ�]�h*�u#9���8���M��_�܀C��h*i�����<���Q��X!Κ�T�^9��G��'˫�B.|���L��	�n�U{`,+p%�=0�S+�mϓ�\�Tv@��H��A`F��Utή��hb��t�+���,�� O8w>���'.�'�!	�>?�ii>	s��.e��(�
8���O�i��t�M
�KحWR3>�� ?ഇ�	Ŕ3��wy7Y���/|��M~nRwGƲ�H�'8tsw$)7�G��藰�ϟ?ή��������kt&ϸ�~A�~�C]�2y6���p�k����~1���5tq����ͽ[�𯯟?�����_�/��ً��>���H��Z�|Й�;���p�<:����Tu�>���p�7��r�j�>J��#�ʯ.��>wi�Ͽ�"?�Mb���1��Y�����?�wQNw%7�׿���p�%�g*�}�?�CzJ)�[�����<#z
�NJg��v��8��s��Q�gO�K-���qP�0�y��_��Gge�;��q����b���Z	����?T����jB�@7�v32 �ԑ�~J�=Y����5�@�hDe���o���)k�J�ŰƯ��}bJ�~�b�[VbR�7��/pzf�E�Nvc�;���K� ��Ep�g��}J�W֨�9;��jn�ᬍ�k��c�t}䝘Q��ב���{����!�X��Gק�հ�/:���켟��_[���Eh�];?�c�OB��5�&W�wb�@���]���_c7"��+L&��Qv�Q)9��%@�)�OO��y�V�Ny�:{2�^V>��	HJ��G��]���(jb�Z/ϼ�����S��r�0<��MNo𭿚�j
M��+��[`���~������/ן>c�<s�j���|>}�h�:��S6��պ �+8��I��P&�ӯb���$o�˗���f�Uޑ~�ٲ ��.Ԣ,{e�/� $��}b\���?=�����F��$��gl�г��X�a\�_���������^l�񌁙��e����o�v�CW8u'�T�&��k�{��aLs;(�/�Yk�0�**��)���*֊>�,��_�9y�
${r�/�bA:1���=�N4�o���h�w]��=y�.�,���sα����ı����� �edtO��AwT�ӭ�����(���K�SV)^?ه���3ۺH�%�X/ Zvsᓝ9]���=���m������J��8xy���9=<{Lώ����/�\`�A2H���*������\�W�S��w;����o�C�s��2t�t��/�͓�#�E�]p(�������'G���d�
x���+V��m��1�_��oF��v��98���;p~->CP~&  i��UQ;�'����;�?�����e�P�O@9��U�����^����]Qݍ����%�Tl�}v�?��q�����u����d6�e�ǯ_��5��f��#��I�����)F=�'?�݃&�'���/��U�S��=��H�~�;����jw����KK{�Np��'�T������*}���l�.�n1p��w������V�����]���<>`��_L���e�IU#���{2O$�����j��M�����A(FP��C����O���|�IW��V��
�&/yFd|��䪻���-9����]�[��Ҵ0q�cv��
��뎭�3�/�ϱ��4��*z��>T��w�n
ы��̙��"Qs5�D�M9e$d@MLm|�-����o� �G�R �w�#Ų���ݯ�`* �C����?����<���̢�g�� |��ʿR>��T�}���/�ɱ��}&6h'�5���r,��
o���������f|��{ �'DOgu�Z��+��6>��Z5�/���G���h�����p�帳��<hf.�������!��g�M�[�o@���]rD������)��Z�܍>-�;i�'����~+J{=��������;9
��v�GTA
�7��/��_��qC�=�1b�k������w��*Ŭ(�<�u
vP�\H�s���[eEԝe���O�bD�ό�[їx�ǖg�yKh���]�{G[,�n�^�Ag,S3��ӛ΢��]Pʡ
��E��Ω�p���b�������W�AV�ѳ�}:�X��|�"r��H�`͹/�=�������ł�sa��\��������L���3&��;Bt[��1�2��r�k�w��48K���ŇϿv���������"*Rn���3f.��8�p��{��G���E��`�nBf��D߿}��ĝD+�ȶ������i��L�J�[���}���?ޮ;t�oSH�xֻ�����-�bzn�.�/ߝ�p�ؖY�צ�gM������;���h����|�M��m{�r]#E|�� ~��R�����l�-��+~�HjVG�O���G�����>v�ܗ}n���I��†6�Ba��ڿH*��Ok��\�\VagO�僓8��hT���Z�қS6��jbk�_��:�L
6�֮<�t�|Ȝ���a�`�Ԭ�б�|�>��N��Q��͡�I��佁T�i��&۴����m�~=�����!�Y��1%���ɂkQ��-�CLb���ŗ"�7��,��'pt��.�2��j�fgkۤ��}l_�'�������b����h��ӡ��A
}��;�����em����*;jK�+������c�jS˿3�[9��(���MH1ې8p��/�D'M�շ��A��DN�-i@��Zʧ�Er�S�y@Λ�O��S �>�|���^��DB��p��N��78���ݮ��~y�3�)]���N�Z���o�t�Q�ώ��z��t�RL"k�G>��8��:�[����jp�N>h�<��6��"��>~�?����� �E�v�[[^���c�\��t��q����$Zak��g����4�_������2�P���.*��T�O��D�����+�
ńO1RFᡏv�+!��8an�r��($�}���ݱT0��h�n��%:�W1���H��t���p��5_�ʆ�|W
�9�r�4΁C�QH�a;���VI�B����LX��?�n;���5ZEۓә��mʫSD�p^�/k�}�����������s�UR�>I�/�ԯ���������*�B�6o��QwP�~�+3/e�L_�C�)��\lL�"د)��f��5��-b��o�L��E~�2X����$��O��m�;}�>h�V�+٫9oݸ�s>�{������C��J����8��>��a<��Ї6��Y��.�[�,�zer��39�1���x��땡8���fwiQm�B�xe2>�O�<�v�����b��Q*�>��R�_p�C|bMvɗX�^��+�x�
����(���0�4�/��G�����_ǤP�����K�X!,N�Q��H���&��s�>�b���ov�~�^Zv�}I8�a')�w��A��4֌\T�͝պ
��O\�gt@gp�0k�Kw��$K���L����>{�t�h�1%�"�y���ٞ%��y&�����Ewm9���5S0wh[�!:'�*~��#ŖC�b	Û�}go#���S�����8�Z
_����V7�Ov�0J+�}�&�g�����n�[�����5{{�@�b��p�ț"�@gEo^2Up�gN��5���
4
��z|ש�����"=G[����c�d�#
~]�4�^��1|7��J���R>��V�k�{z�|�[��l������_��/�IS��9�^��{���`�*8��;>���?z	p)C����/IJ�?yM�Ix����}�zi�H�9�
�|0��M�iך@��g�PvMxkIuz�Ip�-����+~1Y�q|�M�d�H~0+�i��n��	�	'�䇈bz�
��2r8�\��B)$����"92�X,�L������5�gҥ��38P*Ne`������d�Q���^�_�s�Kl�<KO���D~4�G���vQ�J�:�0���8���e\^��c��>����4�(�����c�P���Q�G�9,��_�t��Ч����3�&��pp����F�����z���xƭX�*���^l�{�"���5���p&��!d�/�2n�XrȮ�y=���#>�O���n��w[Z�-|�E�\�ԇP4Iyg��R�r��}z+9LO��8xħ�U!w8B��K(�L�u�N��f��!o~�z1_�=��E�{H��l���	
S�>�〚����}֭�+B��:�	(V�}�-Gt>����*E��b��
؟T���xaT�ji4����+z�����k����E�����>8t�:=�_�k�5��F��"��_A�8�>���%�1��Ӻ��9l�䉽�nv��sR1��įY��q&uM)�&F��*O��,���}�k�#Zp��3pү�g�i>�����𩼼#��(�YK�Ԩ�i�[�癣�+�үЛ�s�i��z���B������ْI�m8پ�Ҙ״؋�@]�P�+�{�Ϭ����i�"g�2�딉R�HO%���S�}JQ���?6���`x�ْ��#��Ob^�O���F��ү��q��L�J����E �N��mu~�c��	k�3)j��bS��U��>
~E�bhN��^��+�Q��Գb��X��9��z���5��m&��ȟ��s��z��U��#�md;y%ka'���D�3�Eo�6��MO�|���K��2.���ע�������b�}��n�kf���!������B��;O�_�<=��K�-�W��4=~������s �/�i&|��P�ښ�~�%�o�[�ֹ��`�Q	�藎X�%?��	����]*��uq�n�_�����>�����0#O~��Nj+{m��H����3�D�ݤ/�:P�9�x��I�ʋ��qNv���E�����G�e�(��à�[���߂�86Ć�o��9�G4]����Rg?_^�
���eG|O~�#��O��p8�vx���Fzڑ���efϞ��OyG�}M����n��,�?~���^���tV�#��c��uD���hya@��QE�ÜV#D%�<<�+
=��|9��A!zꯦ�'�*�T�얽?/�N�(/�H}�<����Ϸ��˶����y�&����ʃ"�D*�����f�!�_Z��Q���]Q]dV��|O�C@��X�E>	z|����I��+,�H�r�󎩠T
��3�	:��
˳"�&(��A����A��I���c܉>߾QՒ>�����������-��	��DFI�Pg�@U����7�^v&:c�����lL/6��E������	��n�|pbn���9d�3w�Zw��Mw�C��C��On�3���A���M���ˢ��c�OU�J!���r�x��I�~u�W�����.䚫��F��9	Hq�Ŕ$^���w�V��3$%�sK���@>�2�;��#R[�2�XÎ����ï�\��rĠ��{�����y��)8E�Y�;lJ��g��]^�#�%�ۻ��g�S�:T��|~��h�Xw���#�2L���o��h����������z��E�~��';�_�v�OT4m�_�K��b%�_}�C��%:H����~As~;y�ύ�F�����5x%�>Uٳ�;��5	⎘��uy�g���N<=!�D��,�G��+
}���E}ׯӷ�诉�/�~���w]����
>��$�����*�~��!�c‹��$~|Ul�
3)�X߾�z�8�x���MsD�J.|
���l��X�߂��'���l�&ž�����9{oRa\;) f��0�kWƗ��F�ܓ��XA���g?�l�I߁vV����l�{���%|��ɑ�B�yܥ�����)�?6���"?�O)�X��VY^,�i,���+<7��ǟw�>��<�o��^.��6��TU;�w̦o�,ٿ�ХH�QV��R,�?�v��|��\�L,�}W��Qn�&�Ml
��աj۵t0>���W�]���mv$r|W�c>�?���9`\���N��"�����د�0�yf\�0��}�a��j����0+���0�9+��5=�ckV��]�W��]�%�B �i-����;B��I����L��}��tU���=*�[�E�b0.��7��&	=���T)?��:��_C�i׾�}ޞ�m����
OH\w�Y�``L��|υ;X��4
14�o�m*��'ܪǜ��d�v=dq�J.�N�NO��k4���EN��.|���bV�n���2z��ΩR/����#3<�&�
��/)&��|{$�+�~�ך��8v�DW>2�K��7��$a#��g2��ҷ��G��tA��n4@��:�x�7[����o���e���XP���At�Hf�[�ޫ���1��ٮF��Q�6�q����w�J�H��T��]�ouO�Td3���ߦ�f���ܦ`{���@��?���/1�|F
h2�Z5B�B~AL$�ᶡK>5�p���jR�Aɡ&�#%�d�<�n��$��K��`��h�+��UB�α����>9*��O�Z��|1�h�Q��(�Z��jV;f����x@rA�+��U�O����_X�<�r�Ŕįa�X��Y�����9 �v2}A���f6�K�AK~���֩V�Z�w
����g��bPk����}>~��J���nШ��NZ�	�^H�0ON����"�X�b��+:�cĬ�B��}��ҩ���0�tTnJȷ��h;{D�͆_ s�SQ>�bk+�e�o���ܯ��Up§���9(�'�r�c]��D�	�յV�-8;�c{��/	p>��b����n}2�6|H&|�����1�G^+a��>J�>�mk+�r��06��~{�t%z�ç�
��OG%�z����S�?�ui
���	��XC}�x
�Ρ����ߺVF=�G߷�ǯE���VKg]T�.�}'���M���ï�^Bp|�HW�hG�ç�~y:ꯂ���H�R���	��e#?��=|O�`Ȯ+,����HW�}������':���R��>���^EgOx�^$��(��*��O-�_|�m9� p��>�ݲ?�-��N�6���]?�삓���� �wK��U�v���g������.8fQ>#?�|��p]�E���?:oeh����ْ��A蠃�L��wT�>Y��G�Sy�5
F�bM�C���7>?Jk�ǟ��|�-���n����)��\|���ce�'��4]ԋyT	���4�tO�K{��!u4\	�?{J�:``�B�]�i|DS�9~�{�a�Z8��'��!�('��H����$:_6��S,3�<��������QF��'�E�ۤ~�֝1��߯��<(c��k�}͞��gt>1�m|��S���/pڝD+읇l�p�DP��;𴇴cy;y����&����V�e��؋U��;�4�����>�L�Ý7��oa�>�=��r�$��=-U��-&RlD�o�_��R�����7��_����S�)�H��+y.��W��$?c7v��<�'�F����G�`ёwȗ�1���_��m"�raޅg�'��/�Eg��&����u{l֛��=�ѯMϵ2�Q�Sۍ�Ҝ��[�6�/�bi2ۨ~j/��	�_g������Ȟ������_��@�:Z{��A�<��$:�~��>��W��<H���+q�·�q~H�y�Yt��w©љY�K�R�I֬�BI��T�"*�B1���T"�������3�!�Bg��9!�����}6^+į��i�a�3
`�ȳ�C�SEn��W�)7���'?v����#?,�iBz�sׇ=���J+�b[�N%�3�(�l<�c�"T���NK��N&5�w�A��A��dQ}�o�J~�=�wK���Ŏ�x��dO2M�=8����3g<��y��NX@�3{�ѧ=�헰�y����]�tH�;�U������8�ő<�=����}�w�9$-|H���5�f��o�:��1�b������~�>��3?T�~����g{8��9�fң8�::8sǐ_g����&'�;�Z�&W�uSқ�3�j�ZLIzZ�3O��(z��?����h%2���~gb2�>ѹ�{{~a�O�O��W�g���
��V�-m�sr8מ�'<��W^���¤~9��I���4|7�_�§~E�c��"�'����j}�pj�q'8a>��į�_�_�䪥b�U8�ft���EӅntr�L����WL��вT��}�I*��<y���ip�b)*�?>*7]�OQ�0�F��;]�T8��r8�S�ϭ񕧥
�K^1�|/����M~�r@�;��e�@���y,��{{/�?�������`Yj�m����&�	��T_��K��W-�At6�G�l|Q>љ?��ә����<����C(�[��!�$!PN���{�5t��|'�|��2Pe�)�ѕ�o�߆C��0)�~������}�g�`��]��5���)���O���J�����b������
�1p�3��e#8�c�)5��?�)8���ll��|4W6c8�q�u>m&)�
B	����Kx]����P�O<��,u��ѳ�Tl�T���L%�Í���'-Q?;��^�_�Ty �/��_ڗ��yk�g���q�8��Or�p�Up
���ҧ)��gP���):�J�/�i�%6=�T�J�u1�C��G"�A�Li!��r��ͩM�zE��O����ﻃ����!���:��[�	fum
�o������)�y.eT�����z���H�<G~fo��I�N1�]i~?xJa�!o�́_^r�kL�
k�B֨'�|L,_Q
h��
n~2�7�n<��۔h����
[wStKOG���/��;��E���c�1I_�}�l��u]@��;,�X�+�	P0��H�
���߸��d�DpTUN���Ma:ˎiw���`6�$x��a��tJyr��Њ~��51���]�k�+C��/]�^�U	���	�4��J�Tl�Ns�O�7�Ú�|r�˘S�C��E?:���u7�����*_h��g�=���.�|���]Kou��z&?�v��\}�fA;��*	>��܉�QB��N�F�;_����D�x\6༳��ox��
�yQƼE�"���)����zG~6������ط'|t��;�7���ݝ'f��'^6�񷫝V���o�0(,p���c�M��#�>�D����yu�KS>���bJ�/��<[8v�����*}g1�-�b�_����͢�P�u��{Ԇ�3|AO��T#J�b��m�� �N�?�r���CF<K�Ϸ]N�ߡg���;��'H��e��V���_�vI����7|�}@����O�6~-��p`>yY|�n�wK�q����
�3������xP�߲:������?�1?O�T�]��]^��О����}��
���6�[�����x�-��W����a�с�T�6����ǜHѧ���r���� �{$t$���,Akw�p�-Ѳw���*�!�^\~��]$��7����4oN�0 +�c�
�˨$�����ԉoF��P,�pF��b;\���t���)���p#�F�lK�O�p�Ŝ�|~�E��1n���X]�+���D�!��?�sy�	�S�ÕGGz���
g���!C�7l�g)W?��T�㾡x�.X+j�M���:%����8�p+Ld��}O���A���m6
$� z�1��1��g𔵑��)Gc�Z7O�ct��O�R���`'֛�n��>+�=Q����F
iV>8	y����}�^^0��ao���,4$bj�q�Ѯ_���?d
_tVO��Tb���ү������8L/!�be�L0g����C/�N�i%����E�`�>���[��$�B�M������sy��䲈�(X>��-1�D�~K�l��=�s��4�J�<ɏ�#b� �t��~E"�9=��p�Dk��gOy��Mf�f�|��bA��mݑ�,��Ճ�Ӹ9e�5We磙���=�|���$xB�B��&�n>%�0��&Q��=Z�Ty�C�h�կ&�*x~}UL�B�la�k�`��)57��,>�

�fu�����߅A��f�W�}fN���H��<��>sz�0���6u��bt�q0чm����ү���n�lLz�O>��3t�h��]���Z3��5�s��?�Y�iP~@��qD*��F���KW��m ��j�j�G���c��|����c1vo��!|��(?H�2r�t����gf�'�y�b�քiKN�O�<�o4�z�'ϳ�?���p���x̹����]�Sg�"ly~��,�L7������a��#&����c�SzT~�|��s�(?�d�_*>�9��z����Op�v��B	:����s�s�A9��J<5�=9���s���-;�'~)�_�Ҟ����}�JbqKA_§s�!�2l�	���mt5~��+}�|�!>�U�|�#>�!��~
�^���r�ףX�>G9T�6���yA�c�Qp�+���5{!���劏2�=�q'�*6�~��K��7�P�����D��C��>*��g��K�}����LTUX�:�#>��n�y�[����n%�P_LCy}�:>�_����hkAa|!?�븟�>�C~b�}.�9�}�	�QO_��x��W4j/d�'%$��ky�_�C�C;Cj�4y�a������b/k�VlAxګ��&�t���ӵ�$��l�|蓾�.os�Q1:
E3�e�G�U�\�@f������X��,t¢ű.1.:k��z����Iކ>#�чݘ�˴Ӵ��L��j�]g2,��3R;h�1���N�~�b$״N)�n������;�b��BK\�Z�2���D.�g7<	��G��_��_a���Oc�_4�
R^�s��~|�8��˒�����X5�k���
~1��ҷd��Qٱƣc�	�D�R�iei�_��n���;<��|��P^
S`�cW�XE����@b�p��K���
O�2*��)?0�?����1��^�^��M���0��Qr�Q����F��Gr"����"6�;�ढ़?�m��i�
��{��Ӻ��G��<(��0��>T�͟=r�_���|G��}���#�Տ_ӯ����:�<A���E'?���g������O��x�<�q�[�ȇ�4t�݀端�b�Q~����|?�i�g�g�3�����C�V=g>��q]�b����#��_��?͟�8/�98�W��3t~=.��U�����ά/���5����t>Ο��swj%�Q>�'L���	ϣ�̢u��F���:e��%�5y�|?��;>�?��-#���nz��x�n
��p�;y�C�v��K�g�A��q}
���u\�z�5�ƙ?����Iy_ϟ_�O~��u���Z�9Ó�;���l�jp�g�5yC�^�Ϭ_�'~5$����#Nh��^W��/֧�L��'��G��zg�`ԅ�'�����L&M�ht{A�1�C��G��e�ք��[���G^�N)���n�a�:>���p�s�Ԅ�R��tv'|�1m+��س�>]�Q�׉i3�J��L\�A{��/�2�X�����굇-�Éأ�k�&���H�w�c���T�u;��t���
���g�x�cn�Kd��n&�ϸ��s0�u�2|a�cO�q�Ot֮F�co�=Bw!I��~:��	�@4�@u�������0��)V�~����$}v�>:^~�د�7�RD��Q�/
�z2|�(�>MحQ��Pb�<�m��t*�k��kB@�,��#�xndD��X�_��� ��)��)|�B=�aw9=�g��E��$n7}.w���YBn�o�_wꋩ�n�bS!���x%HNn%�Q�oL(<�2�XPވ��tS�Q[�*�swiE�GMo~�G�.w���J�c�����"8H
�OѿX
yn�/�������$
k�Y�o��rލ�j>�@>ɉ�$��.���K�ˮ|� �Q����k��x>N~cA���v��tSM}�hb�g�gXٸw�.�C��ry�V7��D��YfB��l&��EC|���A(�'���U'��>R�s���q��G��B���d�A����w�����P�������<��J98Š�'q�� p�C��|�+$�@T�ɒ�yϗ���}�������rV���dU�N�
��
1��k��Y��>���ʋ�Ɂ���旛ΐ,6H��֦t1m��_��|0<7>k>���s�Q�J�]��?�6���%�9��-�g���y
��2�-=�'��w���/�;�U��~u���օe�X5�?�}����S����_���OL�E�����c*�)8=�k�b�;�c}�%�Δ�_�9ҙ�A�䙤Ɨ����B�=!����r���X�{�69�7�U$�����������/����W>=��c�����X��L�'��ʻ\���v}|~=ޅ�vw8�z��.?�V���A��A>�Ő�����I�S���V�z�+C
��e�S,Ȇ3���	�U�:�۵be���_��/���\��_�PjT,Qt[�e���Ǩ��]��q��!���Dg��ׯ�/>��.�gS��>�����Z�_��/T?�ځ��"x��=~�.|�,�QtPO�?ys^y����}3Ǎ�Gz�\�؂nvGPCyeNw���GbaX*e��+�.�5�����N����*�7/���#��WC�{h�4|CȤ;��m�bET��K�b�M�*{���P5�3jw��=��Evw�Bnl7�i�T�4�!������uLY-����_�Fb��>�3��PK�
�@|d0����
}��������'����\'}��Q&.�RG�Ⱦ�	�I���lYc���W�7�0�I�$@��u����
���A�����{sG�,��2}���z��n����5b�/�X�v%�YJt��oZ;�)	:?�|ȷ<3n�S؁���
c�R�0��|$�Q��^���	�V}L��mIZ��cj�e�o��s��`fk�sػ��^�9B�F5��R>�r=cκz�/���mr".)��.w���C�<��>�`%��79Q���U���b��I����@t7ECR�E��O��_Z9�����y�B�}A	�i����R$\sAS\�!Y
N��Mhdo/�w��j��[�i��!��-�ޡ�����^��ڲ���[{l��YR'r�<��5�)1�4�&���`[�۔%.�u*M��I�:C����t.�,��Ǫ�*�s�tz�\3��bz�t����l�����yCw�*���]�D����&u1����a���1�q!s�y�@�\���C�@�@r���z�/R�]9|�J�HR&,����6�+S���]k�V}�@=~E�W�����]�e�08���o����~]^���ү*4}�1_nR���>�QwdΉ�.����#�`�k;9~��׷'|
K����D�P�a�d�-�������B�ç�D+�Ų�W���S��b��C��S�3 J��ڗ�	Eh��_n�jt�ѣ�7W�<F�/��񽻙r�BC	�����DKy��<��J�������0.���O�K��I�H�'i윜�v\�6�􅡈>L�j����d�w�7ZP:b�_����x]����_M;�E��>����h��A���ٟ�1�$�����aU���ޘ���j��"l��XO�����ޘjϾ	%w�s9ޙCp^�S���g��q��'yF����;)|����;���C)6����4�iO���/�z�;�_�^�����1�����=���:>�����/��7Y�b\��暮��5|��k�P�Y�ף���'f������$?�I~ږ��u';йe�
�ZYطY�"12�-)���笊�k�����j�o��g����{���;�ё��cb˳W���ܗ����z�ݾ�X[�/[L@s��=�a#s��|����b�6�?:�[^��rL&S
JF~u'���

�n���[�ʤ^�O�f���k��ܱ�]g��M���H����n�ohoҴ����}�r��ޞ�����u1�@u=�Ƨ��nQ)+J�[��QuD��/�@�ç�/&F���̗h��1
�Y�ZGX���Q��!���—�Hv�1w�˕��by�a��V+�c�Q�L�Ͽ���W�����Q�bz��j�h�.��� ��3�:�:Gyw9�.Z�� o{-������$>{|�ϲZ4Q����XwT5��I�L�[��XH�F�����1:S�bG&\��*�'1�)�������Rh�$3b~haX̍�����ٯ&���pFؽ�_��<�fO���w#�g��b>�j9k6݀�����/Ja��_�+��X���Q�����˯���o�����"�ᓞ�Ot��
"�̋>�&؈CeЧ���a\+����=��6�@նEk7���SG�TA:X�OO��>�w���4~�"�kㅦ�cNA�OK�ؓ?C9�;;�$��P��ٓ���,�į����|���?�K}o�'��3y@��i'u��]���1c��9�L�lg�����y!K���w���
>b�p>�{���Bm��}P���jB�m�A%�z��݈��[9�
��"�옕�'}�m�����%�� �u&?���+k����2�Pž�}�^()��ڼ78 �3��P��gb&�x��*�|J��J�5_����U��Kߛ���Nه�P4o�\R��cLO���'|��W�i�J�&�-���'����Y��ކ�ѯ�/�j�_V����Ww�U�-�wK�lp���d��Ԉ0|O�ՙ;�z�^䏚� �+�����q>V��߸<�����"��~�ЯG����T>닉��k���'81"y�L�Z1�;p��>
��:��6_-?��{1���g�8���i�/�_�����K��{{I};a�3�sFb���k����'F�>/�	Α��븎�/��~rX�
����\�5|/���6~j=X��NsY�X���KpZq�_8G���;i��v�m;n���Ꚓ���V�h(|ZF�Gyd,<��L���0~n
�,���ݱ�^lK�³�n���O�~�	��3=�*`��y�[���Y�+͵�M~�n�`��|�+Vfߵ����&E�b�:^f��&�[�S>��^�Iy�A~[^
������F� q�=۫�D����-ȀoLKT����{ד���䜲Д�-)O��eǠ��k>��ճ�J��yVߺd{ې��h�~�OH΍!��d@�Bm�Mp�'����Xa���v~~	�/�^�����t�Q���}�cF#��0)�_��b�B�g��ן��]DR��b��U^a�51��_lJ1�~-Vf��Zq��<w�Ф.�
�EP�,��-��_�y�W�D����6b������_��:}�EK�>�c8�6Q�m`��_�A:�:�T6���^tL����/�/�M���@�/X))�Ȍ���>|Տ>�'�;����.�B��b��S�T�[�f)<�ӛ��t���fT�(Mv�����^X9T����<���9��R���_4��e�ݼK/J���v��T��G��Q�/ɹ��a���Lʢbh��žl~-����|%�"zN^��Q��A?�J��g��#�5����h&?�W��+~u���Fϲ_��Y}��m*����s���ɳ�sU�f��M�q�p�ޢO���J���I�Z��>p��F�K!��s�-�K��ު���봆�ʑ���n���O�E���x�ڥpNs�g�v��ʼn����(���$�ٷ���?�pd'�Bp��4Rp�.|��
�*�i�L����nF���*َ�b�6��ŕ����/M*6o�7���"�F�Լ��"{E�]v8��L�+O�"�r���O3@�<����%�U�6��k���j�;|&�Q)6�o᦯��~���k�+��P�#�۱�#k_��=�Lx�9�ҝ2;÷��o�R!|l��V��ɭ�7��2@���>>����������;t5|t��5�q7Q��@U�a�YT^}����	����Up4��Z�Kջ��@q:u�G}�$w�'���0v-x��pT�[�Oy�2A�0��m��6|*��w�Lᘈ�@0<��̻�S�.f�/t��hچ��Q���L�j�X�x��J8�jLQ�ʷRO����+G��1R%| ������3W��~���N�#�f]W���G0��I���	��#Rk�ck�	�+&t�d�;XvyOv�)���$'���bJ(��U4VA��B�;)at-̏��	:�@�fX�\FA�߫M��Ʉ�k�#����R��.�x�'u&G�
>Y+
ި'���Ε5��uM���1�6�Sݦ"�}�ѵ�2G���
M��j
SI�)�=:�_�7n%��lu ������36�XC��]���_�8� D���ƨ�#ۘ'��1�/v�\k�A�Z�&^��vx�ގ���(���;&i���E{r�\��t�Ѷ�@�wNI���b_���a���A)c0|X��=;|���(��
�b�m1�#R7�į�!��0u��P�l���:*��i��9q�[*�q��"�����_'�K�a��2XMGt�<���7�Q�I��jF�v�Fg&G���ħP�����3�!'�F�8`��Ք[�ye��
�m��j)\����M7��6In�����KX6��A(�4|��<��>xx>'���,�-	�>��P���\���� �C%�����B�|�4���!��1��Zb{����S��:$}�Ӌ���.ei�]BR��b�L���hG���5]͞�;Vy���G���m�g�]Hz�}-˭UVLƞ�6��ä�޿y!�@�o��\_�{>������MʈGq*��.��#�rw��r$���X�~�=`c����/4�\{{n-J�|�%��-׋��	4|��,)b3˻���O���RIV��17{���JE�†�'8]��'�� /V,%��&Чi����EdC@-f���'�����>�u�[U��~B��'͹�����2��ZĊ
۶!!Oν�ڜ����w�<�	�9�z�W�礳�{*%��d+F[~�M�д����!W�_�Y�qK�xjP,H�b��b��:݅$T�g��P�S���}\/U�y�8���tDǙ_�/<�̶V��Ȼ=��^��z��]�W+�B�O`���b�B��o)��
���Z!�s�U>� �‡S�~�.���$#�G�1v	��c�e��m������I~���t ȋ��^��g[>°4E���SR�3A��b%�w�Wn�>��+��{�>���O�)�yk��l���~�ذ�>�.�PR���pp&?S��$��d�i��jM>�v+��<��-�����͍Z�Q��hO%�	h��G|<%U͑>�q�����C��殄�;y�L��.��}1�	�ݓ�_��I��U�P��f�&&h�`IB￳�(������_�����U�5w�]m����鋆�vͧ'����"�TF-���^:<���-������O1q����b�[�n#��/�C�g�<+��!�������n&���Ow�� <���~��eg�;V�&�N�l�KE��ˊ�(�U?}y�ˉ�j"|y�>�裞���X^���Y���&���4M@ޅʦo&_iϛ���I�f����]�%	~�w��$'�����<9$�Z�-v�x����G��ftmƧ��� ��a2G������%T�
��~�W����5҂���Y��;m�u�X���o�?�7%@�fM�_~O�/a;���V�ڷS~�7�9�8������|���\�w[r��8�S�kϛ�DLO���G�'�D�-?������#�����/֡s��'�ֶ��('R�aAv�Ѝ���U���\lJ��;�he��/8�n)�|6��1=�U�Y��={�	`�=:��Ÿ�������6-�[{���Q���,�hT�����
ߟ�3���X�d��3���PF�Vf�E�	��M�FgzJ�ѓh�X|���3�h斕��~���$�nį��١�-��g8��{d������cd�M,�~�-0^��>�>�=�Ŗ�;����I~��k_���/u��$?�{����f#����YtN�PU�Mi\�>��II���Ч�b�@`O�ߩ���+|f;�=c�_��O�y���Vԏ_p�>�{36��=�=w�MR���ܔ�X�4�[������؟Eb��z�'�bO(�.О晛>k�ܐS��>`�\5)�/��SS�����_�t�̞�bZƁO����w��b�{M�@���^�������ͯe��˸3w�L͚�C�äyf���G{�&<��Aq?�+��ʵ�����i%2�� O^��[Vg���D��$ɏ:��\��Y_��Á�<O���2*�GЎ�����L�"ő_��>�
���������*�*,�#W����U��BL��׬/�>�R�^���S���Pr@��P�fQ���M�b���I&�>-����1��'�!��2{6�ɣ�F�
s��m�z���O�Z�g�=���坏��������������k�yA���΢RH<c��j�h��-�Q_/æL{�~J�&EP�� ;������`��/*
����3�*�Ñ{�k1w9։ˢ�l��Φ�ww'����ՙ�R,�>s��\g|_"V�f��Ad���a�EՆ=�\~��O3۳����b�zl��^�q)�K)7�"O��	~��BM@�;�o���)FHWt��M��f7�7,���Z��7��IP�#?+�Fgw����/1(���
��g���>����(\I�����
'�aC�bs��.NÇ%W��?�L���U7�xA��
��K��T{�A������zR�!&L�6�G���0/�YxR:[^�M�b��
��N�L���|R$�
�_�_�3��e灸y�s����>&r������1}�Now�,8�J��F�����	?����I�e�,=ڷ���_��_ ç&���N�"N���κL�̄
A��/���`��h�2���׺s�+��LS"҅�O���l�R�h�;�
�B&�3�8�'DO�m������|�B'cȏ��Z���_�����_m�?�3����1�� ֝w]KG\�o�
	�Pj�lȈ��~1=�	����2��&X+,�*�d�d��*,���2t�8�E��`*��Y=�	�i2��jB	CU�d,权��g�)J����/vJ�[@F�f���p
�G����C�A}��	ht���}�J��I!Q�E�#d��<7�����܆�J��o���r)[�s�ؠ=��W�������P�Wyk��K�<CF��17��6P�L�r�_6D�R%<�G��a�%* C�P�N�b��o��)��}ï&���:)�|��D�b�^�!���~ư�T�m��*�~�(jŠ>Ƽ�u�F�)/��O� ɡ�2��|�Ӻ�BxGC'��#z|�@`z	���va8==����h<ؠ�
���
<�%��b��Ś(���i~b#1���I&�ٖ��OYg��I+b�G���ۭ	���y�˻������L��u��S�ݛ�Nc��Ol���(�5c��Y�)?ӦO�ZtF��_I»m�Y���l�r�:��!���{+��!םD;����;�D|��{,�3����2�CᏳv��{��|��Ë}ZV���vxE<�/���nq�?Uf�~��b��|�/��9�Eg��0kN=`������
88��Hv^8N�R��o��x����D����j_��>�
���m3�y��p��E���t��]�
kB�t��w��u6��d��'8�
��/~}���!���쬪�^"��V�#t�N+;��^����(O/6��r����v���ϏB�3H>�,D�8�G��*�.�"�~"픦�`)�x�t���]73�ے��΄f�x�DzЕ�-�}֕�,��d��^o�A"����1��(��t!@�Z���]f#��������k��@�Ζ�)X����|ٿq������ȴ�H�!C�QM��~h�Ɔ}_9�<ŏ�]q��}�nyz��`����N؋�`
7#�J$_>�=97N`���Y.��Y@������?��S�4,�A�f��%�`K��~
���s�U��!�Q��|�ڵ��O3@�]��y+����$Ch`�6���B��1��E&�o�'��G�{
��C��O��${G=��f���KN�n�Q��L�r;����w�(
τ����t6���f��+��!����IG(62��B��z{��O�4��"B�u� *#x����vB <MCi+��o�*��"o��������u��b>:E5����%rb�nmlb�`m��(�YlY{�3�ܲ�b���~U�~1�ث�0��U�ď&��i�4h�V��ǔ"�����|3���7C�_D�(���g�,iJ�!r��y����'�:1��sa��?�Q;�9~*����4���W�x�N�=��!z2V̎�.�mo��&a�5��P)P�I"k=>v�m�Z.%���QǼ'�;E
�f���b��V��M>�|���J##0w�F�b��9��O����Y��<kF}�K^�G�F�U)yD0�"":�W�����PP����U�x�?{ж�X~	>��'��Nl�'|T�<|
���l��%�n��lǺ��|p]O��Jf��ڕ�,/��NJ�v�������3�Sp��_��V!rj�Ya��>ug{�I��>�r�x�H,.�K��z�C��^�>�n��9�0�WA���N.�Թ�s�1�b�И�ˆ��ǚ~��qty�ZWB�[ar�K��rrh����,l�i�'���&����[�5�<����~*?��Lq,>���ׄ)�_ٟ���$8��W�&��ϲ��Ss�G����Lr������2ն�����G5�y
��7Ta��ϴ�m}ǐ�<>�?�)UU�������^Q1%5=1%�v�>S��_N�ݦ=-�-�|�s�\�5ܖ�g��?���lҬ�\/�u^ٷ�A��A���w�R��&{��o��ƣ^��<���)��X��(�����۱�i�n5�f��SÌ�r�K�j�|<T����C������W��*��uA}��?nKL�����뾛�v�EU�J����w{6`|��:�8�ƾ�g�4>����Ь}*���W�2�rw��<��!*�+�*:�)�³o�F��e�"�u���Ī�@�5��b�47w�yT����
�1�G@��Q_�<��CO��iPE��7=M\;�HD!�P����	�v!ح�
3y����4��$����hLLO�}K>���<z�[�m(8����p20�_Փ�gy�߻
>q�t��=g��.%'��uM�(2�i4�[4#5<��>��en0T�3g�>�1@�����(}��cKI���9W_M$�7:�/+���D
�+�/�8�;%�o�ٸ`�d&��k��<E>��[�D+��0�y��kP����JI��2�_�Uy�EÇ�5�(�g���=�%W�Q|��G��A�-Іعf��1��&Jo�}�T���6f*/�S��킏!��@���K��Gu�
�`���Ϯ��5|X�sy�t�ǜt�����fV�)ζ��S9�!���B�VU�f�*�
�%�* ����O�;9Lޒ�! �����uS�(�0��m���z��@�c��g��[cgf~I|�<�ez]�	�m=Ҹ�[_���b�At��xj���	s>5|T+���O|�fvf�U�G�YO��l��\��4�!�D����}&o /P�F<0#?�؋9I\��j���W��*�S�Ra�)�yq�~5?m�5��ML��=)
�^�׮�P����Л���BЂ3+־>�'+�n1kbA�g����̓����(�G���ر^?ڥ�<�<'I��o/-�Ô�E4>��e�>͵�?���\�9��(��
!�*���<+/&���gz>�α#�OD��ѯ��ǙW�=��oe��h�z�����F�������z掆z���/�	��O�i��<��ł��@N���W���_�)��C�ŌQX�w�Me��_f^)_��������/N+}���_}����ʙ���\v�y�&5Y����E�Z ��<Ay�$��H)�9���,ES�����_��#Z�	%��̅��
%y{�
��B�n#�D
�'�Z8�KB�>�GJ�$:�^~�T�l��y ͏�e�|���?������f��.�	����r����!!��#�F�$�b#��M��N�_M�����Y�F
�b8�z&��/-��ң�?[�m�gOrW¶�����0}��D5)쌄�r畑���Y*�()������͈���
L�/R��@�'?-�L�QD!�bw3�w�:ă�bS���3�l=�$��DP�'p��ġz��KL�sv���er�':�}N�hT+����%m�A��k�B@G���)ڦ$F4��$u(��V�oS�;��gJ(,�K��Ϋ?wN���_�؎�T_���|��(�;�fP#��]��V���>�,��du�/��a��)f�y`7	�r����5ΪGCkl] ]q\�M&b}�o6���
���NC/P�W��Ctȟ5)}'J_���������j,n2�hޚ<4�/�H�r'�c"]�0�7��>�+���H��x��e5I�׈��Ŗy�<��R;Ǭ��N����Q~DL��+�y$�B��bJB>�u���@���M� �q�߬zvn�����6��<
p,�<�/h,��^�@%��^�?��P�t�>:��r�=9��ϋ���c
�8�tv/jR�
v���V
]N��
�]3|P�)O8k��f���+�)���W&��=��7�0�9��'�G��E]@��s.��P���w�����<�_�"-#@EZ�(�s��
�]]+��B�w�v;�J���ev5	~��Y�*>`��D��F+�*2'L�=�E�����SDD�2t�4t�\�J�x�k��T���HW����Rl (d�|������T��~Y˜���wpt�Tyw'�3���f�
u2�
��\`�r��v���n'��>�o>�R+aW>w�E��sIBd��)-�Q��	�V�������W�ܛ�鱠���_|I�!��
��7�����կt�ز���`������р���K
qdݦ)���xv*W�f�ިz�>*;���!�f4��e���K's�J ��BM�Qɓ���ڐ�d'��Uqyʹ��x3���`���F9ډ��=����ߐ$��Á�J�S���OR��~%l4��53Z��t����J�YC�cK<C	� ��W���1mv�2W/t���h(/�f{�����ϚxF:k���f
�n/J��v_�gޒ�Ӹs�JQ�9XK��‡��F�K�!��D�IwHс�Re��q�Tߦ:�$;!<�D@�p*zjN98�aF6�Zk��sN��c�M�K�t����ME�,:3�;��Nx*�n�?LV��9h٨�Z�>�E������B�&���g����lCH���	N]���݌h|C妫��mHHz�Ӊ�ԯ�~������V}_�Jo=�9	��&'�@:K:�9=k��Q�3�T�M5aj���r�r�}�d��y���d�{�;LF����ha�@D�ep�o
��V��AFs(
yT�M�h�i^�ТX�7���HO'�_�=ݱbՄ,�-�ӻ�w�R�#���vӴ9��k�̼*��U�j���0����cѻ�yqy�k���
ّ�c���t�}(����F$uY	R���'<�*�d�P,ͻ6��J:�%d���]�!�WИ2� ��e"�IY���_��l�߲,�/��g�&�r���w�1s_V�H������u�v[:g660W�"u��>~���5|�>����^Ӷgr_�܆�=��3k?=�d�/ʓϚ�����o��A���5
���h��J����8IfOzL05Yy���hw�bˎ�ʇ>a>vf�j�g|�ݣ
��j�7q�z�_ު��s����^�pUAـ�}v�p���s���e�Y�Ƀ�o�a�_��Z,~�.���.�6�"�
��J�H��':�w�5���q̵��p!���1�J��˺�1��E��:n�ץ��o�T[�;vM��&vNt.P�<�CI+�mpg��ay�)	>�O�+�♞��]d`9�m�o��!��S�V:5fRf���J�X7�h��3�(�L)��R��n>$V>����3*���rT\KvLM�2���7�_&1~�+���k�Ϸp��RH��vNZ9�vN���V�D���>Ĕ���r����^v�N<��Ȯ���	a *�g�i�룿�ϖ� L�ʻ�$������K��O�H��p�驛!c~�!��(`�!ï��{��E	��ь��':��6�
���ݮC������nT��,h�2d�B�Ή"�O׬�~R���N1J[��2�j�Pb�5m�X1=~���{��DZ������
ӻߕ�h���N����ﬤ���n�ʸ�yx/22a�h�������-�v��5��E���y�)kQt6�8�<��0�-j}�G���|;�d�K�I<�̙���&���f�	;����C*0wRD7���4��\X@Hү���_
>��ӣ���.6��?��f�1��K@��tR��߭C�F��/��x�?_ƪ��g��ܶ�j{���l������2J�,��ͧ/&P�_��b�pײ�7��s�3�/V>�'�$��*��(9y1�9I)<�|������̼��O������ʓ�Ƒ��K����V.�Tӯ,aT�y���ތn�L����<Sg��=�}22
R���|;�
������H�ӳ�ɂ�P�_�s恑a,�7����qP���:%8�c鄀�����`|�?u���/����c�/�ʏ�����!.0)��2�
N��UHN�e�a�����ncSj�Y~���6ҕrG���)�O?~�� ����;r8�Ӆ�;mM��P~sv	G���^麳{�<��O��@�<�__���4���>��}����G�Yw4w�>�?1[��y
���މ�u��o3�s֡�:�A��ԇ�G�+͸o���-�+�vo*�'
�3�G	,�3/30��%��F��wg̼帮d;�����g��^��g�O~���n:;����|��G;6�[�g�ٞ�y�!r����
�-]N%���/�9⑇H$'�Q�F2iOްʇ$%���O�V�Y��!�*�>�c�ѵt'^�6���/���%���#��=��7�(�����~/�l�[�A71+�_
�vwp�l;���'�j��L�ʟA�P���T����D�"�2ƃ�ȵq���g�Ye+�u�v'��+�$���q�_D(�ɸ@wV$:�8���S�Kۢ#�����iˠ�dĒ&��'~uF����$F=Hں2��;�i�sK�P�w���!�����Y���Pp5M&�οЧ=��$R} 3�>�|@7�g��6��r�9��1&�S�i���/M���]`j�kl��jח��?�u�:%�3	>��~���P
_~-QTSlG��>H�rr%�!��5QE\�������sp-����N�~�G�'�����ٷ֛�3[*YN@�O�e�P���-x�̣r���b���U!�%V�gܬ~1|�xL��}C���WXf����)�7�&�Pn%=�V����*�Bqo'{&����$�kb8௖7��G"T[�w{B����g;��V���� ��(����D���K��&"���ueP�VU�������8~�v����t�؟����~�c�Y�U��V�Y�@իfd��ܶ��GH�6\�Up�����+qN�rG^;([��fwfMX��*��w�IPֱ%�^��|ү�:Fw���G&w)Z u�gl&Bϊ�o�P����I�}�͇[�[���|BT��l��dO�ۥaG:$\�M츒��V�iF>?��8�,乃���U��I�}30���(g�ΐl��nu��?V쓂�xA��W�e��x����&VR���t�a�"fh��K��
�)��&����HYx�oF(N��P��pKx���Q�Mȋq�
���Bs���p�z�0v�EX�Q��O�Am[��S}tH��Mar0�j�<f~~-Ƣ��F�4
��oe����1	��"���%}��".�O@*9,��wm���:�-��`
�;c��]�K������T�G��
Ԟ�þm7&�x�Z%�N+v|��.�B��5��0~$�]�<w*E�T)|�ߓ�W�;��S��q*o\���(�Op�>>L�s-�/�zrC��nd\��@�����r�f�� �dގn)�O�Ԡ�,����J�H
����V���Q�ًi�o[K3l�vo�A]������zvxַd���ߙ�KDgY�\�S<�mѭ�5��ѝ2硙��	����E�kH2��4��Z�Il�����~��N<l��=wt�-m���a�Z+ՙ���Q`���ȅ8v�$�i�gD��6Z��N��|�b'��[��:�T�0��4�^p�=�ԝ뜫���}2:o�f,�svyeO�.���0�!-;l�`�BϜ7a�XK�"ޤ���ˣyN5�9-��q��
���.,ѐ1��OJ
ߺ�_����O[z?��ۢ�եj<�ֽ�3uԋ���b �E|����)�S��bfჄ�E����N��Bx�!�{ee�~��4k��c�]���$.�M�|�M�ۃ��>�J6)�,��#���f�%&�D~P���2��"%˶�`t&���	��&��J`Й��	�b��7�̷��Os����HԬ~�����Iy5�?�GhV>/�WIto�:H��gg���L�^1U61����G'|�D��s���]K�&�9{W�~|TH_H�j��l3��	��G�����ԡ��B��Q�����h��~���S�x+����4�=�y��=_\�m7�n�9��4ˇ�������Y{�f��亓�@s��U�{��L��]E��\,�r�)'6��%8Q��wWN1yjSa�=��$��K��#�IuP?�78�G@�ր>&�ZEG���l�~���
G��'�@�������1�d<{[j��G�bSV�r�_v��33�O���]��Ģ�L�|$�Ϟ_|!'r�>�z�K
�ˢ�$K���}()�\��	��+�"
�C�r���HT6N��sQC!�ǯ(�!w�؀D%]_�މr�jc�OM��.��ΦbM�J��N�o��k�a�|:A�0�G7~Ŭ�Q<��0n���(��
�a����=>}�/����F�"��֯P"u���v������7_����=D�dA�;S]�T �PU֙�Lhtȿq�b�Y�֫Z�_���s>u�31sv>J�]���xӧI�R��
��:�I)�<�}���=���I#u�Fπ����6�!>US>��/���p ���A{7�1m�H��61g�/Q9���[��H��Q>�;�*ԯ.¨��P>0_X������!��E�'����߲��;Hn��=�G���'v-8��Nb�N�N��&��P,<ܩTL���0(��9��(�G?��$=~�C��e�X��,�)���)��ԯ�*F���Zk���y����>|=8�1>%,J�7:�Bq��rs�D�Wܹ>�iU~�53
.6��7;�z��0~�G���fm�+v1u��pe�3C���-8T:���t�
"`{%kDŽ�~i����-�$���x}*G�5�@�c�ОY�JwA	�٤�ɘ���.�6*,�#A�U�Α�Z��t�"�ӕ���eO6��~�X���܅�u�^�$9�f���������N Kgb ���&�������<���k�Ђ�
��g{lZ[-�IE�U����9�i�����ї�s��;:����SFB:���:t�n6�;��n8���"ZWn�
�|:������ѝP��7{H5���7�io=�i�@����6.�po�K�.<-�Gg�3�j�X�{�׍gj����$?��
���3�k�q�_y��F�9�
x�Tw7�����ى�g�#�J�x�Ku��B6mǂ9c�l]pG~̤؇�/j�-�T�k�S�N����A���O�<
}2q3�nOT��]����K!y�
���%���2��|&��O�Ў3�C�7_�ț��=��a6�ߛτ����of�	��(���~j���[9��s��O��f�f��^Qa���T��o{x�6�ؙ�#�_�?���5x��xQ��.�03�In�vˉ�ή6/:���U�w�Q�+3?����u���Ӭ��+�8�K=��$<����xZ����[g�w�Р�B8��0C��{��B�L��+Q��X�g.��$?���� �hJDgG�~�T��`N���X��>{�s��Y�Nyxvd��_�>$�ue�N8{��<��i�V�Uͤq�sv�>�uyd�ub���OD����<�z-?�ߋ���T���ʓ��|�5I�C~x�֖^����Y�D��-�����zv�x����M�f�9�3�Q�
��>�˜�?aϑ%*iz�}��S���z�\�64�"y.a@"]\5���ɕ5��~͙��zA�/[1�vXKL��{1dˏ�riD7����By�kbj��)�w���2���/[ʻ��~�������v���3}�w���Ǔ��>�Jrޭ+�h�F����b���_�9]j�
�T>FH�p7|T���( ͱ��e�ǯ�9����-���(W��	�ѯ|D����o���
���^��i�S�3J
O��|�U��T1H�abͭ`�2�����	9��f�a�_~�-h��'>v�T��O�KD��9��E�_^ ��+=�<��L����n�]����
�?9�������<O�"3w���j�օ�w���FO��Q�{Š�(9�����}8�r��S؄�!l�+�����_���>ݩ� ��0�z����0 ~ǔ�W���?��yywv�9�Q��_�uq��C��L>��Y�L%�{��u��
��PK��Y�f��ĂƋ���_� ��n��Ҧ 䳉, As��{���ޕp�i�ڈ�O�[���p|
��מ`��?߹a
�����I�ȳ�3l�Н>r���o�ĸ�J=����\�#?MT���D:+��L�Ɲ	lUR@OC��|�D��B��nb�I�rU�D�+���k>�+ӭ��(T���݂���-
>��H�;^�x��z�xW�nߠS�w�5�S��k�o�s0�_�v{����߲� �A�?	C���k�0��mk�G���oavM�'f�
���L���_�g������%@X<����� �� |dr��+��Y��C	�7u�Ѡa
؜�	�%_
|��W�����f�w��LT���"��6��i�g�0E����)���Jl�d�-V�R#�iQ�����x^R`K�[tIF�'���V��/��'D�0��!�{k�Z�-;&:A�\a���E}Q�~��6���Ƒ�@b�/~�<�
�bY����X_�<g�g3E�܆�2:?
��Wm���=.��r����Z�"�B�k%�P3���h�L�H坜4����{�wJ&V������ȧ�n~z�_'=���v���*���7|z&o+����CQ��sJ��ܞ��G�}��+t�BpD��n$T��S;ZK��H�wZ���|���_��Bc�k5�x����@ۜ����@�ME������c�N��4KG�9��+SLA����0A��5<�����C��bo���Ez;h�6uз�wb��ࣃ��QM;�\�'�λsjHHL2`�6��{Q}2~RM�`�5�>9c i�s��7ƈ}\���v#79��|� L(0�*@�ɓ�F���,!�
m�w&9�����oqf�Dh��5J�x�_�g葿gg#_�Z��A@*�*�#CIws�\���FKV��Z�`��[�̯wze��n�ݣ���fG�LDyG�,3�ك�0u��h#���ǥ��Zw���&:�؝�A��%�������
$%=�|03Pd��]l
�WD�����bt�FW�%�����=dW��P��j���8s���VWwr�gjC��^cs
(��[��˚� �/��X���T�hd޼��c���?��&�no���%p'�w7-��?"�i�e"��ܩ�!&ZG4����K朷ǐ���%�w
�W�
�%�9��kb�Y�
BWh#Z��./&۳52ܚ]}�����KOﻵ����Iɛ�W E���Ƚ^,H���/F���5����M��+e�E@��U�a7lL���5H��&|?6���r&��"~6����sZ)����%�ťڃ_dN��㇑�`3ц}�e���C�h�ڞ�G_��:@��W�F�O�w�17{n�{4�Vĉk�]��Z�X�O�Cy��}S~�lF� 0bf~ל��G�f��;�f�3��/�{Ѿc�2un��Bt���ckr�Ex�H�"r��"�ܡ3�:�3����7~�j]Ԭ=#�O���KM<�D��nj���w3��������bq���gV�s�)cko��#NŠ���C��D�D�+�{��Il��[J��2�˭��X��l�}��)_d�5�]�a9<��y�}�W�n;��������E�R9��Okck�u�O��RAD�||�[%G~���=�u��=��_bepZ{�xΜ;���?c\\�i��4nb#F�go���GwE
���������"G�a&��}�;�l�D�}�>��}�����O���Etn�ab��ϑ�ȫ�^��'�J�ۋU>q]�>t��z7�iHz�>Տn^ǔ��Z�Dp��|��ƣ�Y�O5���>��4�ؔN�����z�]��T¼
���L~J��A�}F~�U�)`[+�[��m�Q8�1	ƪ}���++�bݢ��!0�>�Ip�<zK�QMT�_5={��y`�s�s?���^�tPJ$�K@~6��w�'9|�~5g~i����O��>|\���v���2����-r��Ѿ�a�#0�譏
�g�_Y	ǯ�(�ԯكL�g5#�$��/m��u
My_�TL�gm�^�>�0�>{�ݞ���*|�'9��>^	Q.��%�P�Le�"��5���W�����<��x���t͔������_;�B������w�$W{�)*�J��J�[��(�l1XZ�X�2���~eE;קQM��~G��K$��F��pY�Ξq�2>�����)�h����Î>,�8R�M[��7|{E}1U�\d���Ĵ��$DE������?�z-�[�p�h8��>]܄��3^�!r��KMK&5�'�&%�E����������|��%۶W_�*�9���<�W�G͉�+��0S��a��P�W81��)4�j����Z� ���C7�ZO�9;�	��-�km}��}�_�\�4�=w�M�Ǝ)l�jǯ��ȯ��)=y�=-B���o���35MU�vﶘ���oTM��^���b5�g*�E=:ڟ��$W��Ɗ=|���Ӈ��=�bf����̲��E�O��ݚ�'R4I>Io��h�X<�c�>������ �v}=�>g��O��1��XO��R��!�W��B���fw;��p�y�Ĕ4?T-U��I3����g���*�6�‘��yF��'���r&ύ��[i<��f|o�$�ǣ-Ճ�جwf���LOկr1ͯ�?�j�Op�u�+}��B�̼h�YTn��`��~���V蕙o��|Z�+>��s��_�y1_M~�3�ZCC��Iє2*�>�!#PL�jcZJ�9��p�P���ߓ����U��ٟ�C��懍��w��D���y�>����蓑)�;T�k8�W>��S�|j7$����q
���ѩZ�¨��g�P�5��[�k�_�01^G=
��s�L�Y~�$g’�B��p��6ԛ*�b֧�|be�g^���a�3zݪ����zݯ�Q>���g�>���ܔuO,B�A{$����Sr��G�����������c;:q�\�~A���/�ǖ���mRR>���M�K�9�į,m���+K�Ht����v�N����JGy����fk�����ܗS�S�0=R�Ҧ��ӂ�DM��-x�����r�ז�ѳ�uu��������I���_���9BU�dy<�f�W� �'�����'䇄�@��y㗓�������}�Z�/?%�X����,s�Y��."GC��>��/uw����49<�?y�}a�d�)Lt�oה�n��������ӆ���}{�f�ݷ���.Q���	i�k�:����|J�BD���͉mO�^��t���K�>3+V	}T�x��
��a�W����51+�����>���;�.�J(F�4�:�z�S��v�~�or�g���z�@�=��,-|�k�B���v�Q1
�go�ÒI�)�	2��P{���Ka��Y��c�xʛm�`��]-Pu�s�__*�[y�6y��c�ģ���ْ=G��P������f~R>$(��29��٧�OyMʟ$���r��FoB	��2�_�̂�}�t����ӽ�3�']���QC��|����V6JX����٦����&�"�9��+�8�W�E�Ʉ9yhA�V���ӾC���p;���7�#B7<�Ղm�,�����>�\|G��R>��9��/�K
d���bS�����<�)F��H�;���1|�}����A*{�Q��>�BNX?|)�O���^�g�j��?��5���D
�_ށ���Tv8�)�k/�vww���Lh'�3s��>�bWU.&��I��V�|,1#�ڑ4�D�D�/Ƥ9����d5_�1E������N���ߴ�o���\�V���W�<��{E������}߱[�ł��A�~	c�Wѻ��SD~NՊM)<B���U0s��Ϧ3<����d���r,��8!������<�O�֣.6�/މn%.���Hy��Ba-�a��#���*��YU	���*��/v@,��R���ŀm|���6<�(�hK��m6�΃u�R��5�A��H�LO����y�s���z�s�W��2����X��@n[G(_�?�,�lT�@~>���y���>�|&ε�Ɔk>��m}(���G�&M�۹��~i���5]��\Jo���	�Q�@��P�cj��Tf����s�c�L<�b/��5��!�b�|�Jx�t��ً�/�jDZ���fk;eT�������A��!����x��ua�`��������=؉^{�7��i��bY �R�|_���-R��~FC��c�Y{��#a� �k������Hsa�ouc����qt

��ʯ��b\��l|�	\pǐ
Wڧ�,,�����D��<>�C�F�g�T&z�����z��XM�ko2�%ՕzG��b���}�M������'[���A��`V�W���ly��s����l��7���=�N	���h��X*_����|�l_�������lg���Y"m|���Rf�`FX6�d�no�����"�s�V��O�B~�����K��a�����~���>�
:���)6��S�DL;���&;*�	�ч5&�@�j�ʒ�n�(�.SV���C��B�x������IH�c>}�8hu~y���Uol.�]5�G�W�>_��GD �9ʁ�Gx.%����P"����?ї���4�*k_��������j˒��輿�<�����SL H� ��09��QH��m���U^�I_������p����-��>IBg���j���Z��E���P��k���?�D�"�`*rn\�k�j&Y>7^O䲗��vC1|,	_��k.ՙA�t�	xv�J,�85�
�x	�_�欱	ɍ���	�bk���Ct4�WeZ_f,FŒ�{4��~����CL�-G��������\�-]P�m��=}bq�>H��C��'ې:�~|]:�~�_�_�li�x�J��"�'	����|��8|���
yq�2�d�����e������w�ڑ<�HG�`,�`pL`��r��
N��?�O�^ӥY���_��^=7��5��1�'�|\���o�����1���[`DtO�MZ�Wg��8(:������Çej��)��a�?���ɗ���O����_�U�>���x���q_>*o0*'�!���sg�!��k|�Y��O~|��=���d[�r�YW�Y���a��5[�Ov��W�i�֯0��H��=Nl��9_�O���g�����=Y�t�>}z���ɒ�_�.�&��ȋ�����Eup�5�����Iѧ3����� ��g*و�����\g;P�qJ~h5/���9����F�)��U�#N�;�h�֯�k��r/��蓗��Ëѧ�T(�-��/C�1_���(S����~u������������.S���G�u\�^���M��3:��V�n�^ɋ.�|G�����5�E|W>���~�<����[�u`�����6��q��ڴx���:"��Y�I����������ՉQ�>��dk	�@���Sn
q���}[�4#$���qM�Iat�+�P����X�Ge��#O}=�>�ٌ�q�>X��0|��Ͽs0��Ɇ=�|g��l��φC��¾DQ�/�AM�jYѸh<��!� ��)9M�I������^���=|N1�~~P�;XK�+�I	���ҫ��7��Ĭ�/>� ��v�qI�WRR�(u���h�_;h��ښP�A��N�Z7����dg?�]l3�
�v�������oW��K�����Kךw@�F��4�<�x�96%\P"�(��6�����ƻ_�?���?5��1�>�Q?�*>{�μ�cиn~/�r�?���裤Tq���`AO~=>d�����z&|6�T!}X����k��r%�Ь���A��i��~����!��r�3�&�m�m�m��oG卾�E�T��k Xa�(���n��<����*(����f��u�*?�=*ט�[�6��_�ܲj���{�*�w|qr�I��Ծ��Z�`�/�x�z��c���L6����>H.%B��<G���ڿl�xL�g�����7ׇ�V�.��v�����t.��$Ϛ�8M��gɢ�������ar>���޽�Wt6N!��E������t�T��ODޒ���^�*�ܺnɤ�
��4=6~m}��|n�_��4��Vy��>���O�G�o�s��<�j{�/W���i�0�n�M��l��'_:�L���|z#���~ˏ�J�9H��Z��j��?��&�6㞭3�K~��|VVސG��~�t}nڽ��\a��?S����+>�<}�w��� $���:y��8��	�����d�"̃��_���xo�����~-m�w�'e;y6�2/�w����=j>��W��А<wͦ�d���)Uw�9p�����	+��F�����X"��١W:pU��0�įA*�1�0Dh��+<�o�m–�f�l��>�o�����꼸�Cz�Y�vC�j���+X��-$�[C���G�"��Q|_��r�6��O8��rp�wGN|�ƻ���J��j\���돭C�Kqƭ��/�؁"�:��AH�~�>8�~�B���m�-���è^N%p�G���Ͼ����p.Ü�T�t��S���̘�
H�ٱ���=�a�˵G*��_�ߔ����N�D9�<GN���2����s��+��#�'�q�~�c�S�p _‘�`��~�MQ�	j��J|_�B�Ȇ��3�����V�����oA�tN�����Uq,��5<t.��3�~�0�@���'9<�Lo@�X���3_��ټUT�ݓ�_�1����*��Q������{��i.�|�DO�"s�6�S�'�n)r���7�	M�Q>��!�qh*��m�A�ת(~����r	��(H�0����O�cE�K[�	��_���}�`�~���A��ˉ�/�q�E��󼄒O9x����E�l�{ɝ�QE�͸D��W��t"��(\�>
�C�UX/vuCFy>"ŏ�>p|<`�G$�o�S��x�:�t]�J�DT~-,SK���4��_ȋ%��1�o�Ts��>}G���&9lFȵ���`ԉg��4h�M.{t6���'?(��8�@��>�Pr��T�+�u2SwMy%k��5��l�P�#�T�8�?��>"����dS�'>'�O~�=�<��i�*��C��t?3�/��Y�#?|e}� >�7��r�Q�ҵ%�J�Q��֜,Zȳ�N�	v�/��8F��/_��YQ���IJ��~R3�E��8����[n�+>u���'$?�
�J�i'ǝ)�.�|δV�3R�-
*�hųqe��)��<�<G?aX�N�y����_�$(8��m���� t��B��ϧ��˂J��t��,?��O�>������|�1e>.'���2��B���(���nC{%|�785��L�L#Aq;�2�-r�YWYL��N�"��*�E��'��ٝb�F�[۽�A��0⹋�g4F�)�}��[[�w؄������5�qX���݄ͅ�	�EPn��x�g��Y�N�q����rӜ��F"Ie٫�%>8x�>�п��A�]��dguX�/uF�����ր��gB���9�!����+b�>�	^�O8���*#��[;�&���X�,{Ə�.n
��<��������_�����������g68QO�|��L���ۼ<B�_�O�Y�G��Ç�
�l�	��Y���IV�n�����.V�n:��!7��Đ�C{f��ݶ1H���S,M.Jgg��#���
�u��
������J��dQ������ߙ՟=-Y��ez����}E
W����q�D��>���l�b��r���c"��ip,��g���a�������r��,8پ���w碋c�=�]
��
g	2�0����DQ ,�7Ot>�Yss:� �g��8f������O��^��_�/�3���C�į�W��p�L�&h��Z,(�M���O��!T���GF��S��͗l3�̷��UʋHU��B��?:����h��B����į�m����G���:��:��98�|9���'>��V�!��+��
�
��O��'���F��\��3���09�Y��^Z��Ǖ'*�����kח�D���d��� �����N&絘y18�Cp�<���a�<�	>�e
�
�o�,^�̝�?���K�:�ۙ_�4�^stoo�V��b�ϼ���f�)�ҽ����r�/��ڽ�0���2~�~�Y�w�4�d�>�_OhC���K�7�R�{��g���h��O����_���
K|��+/D>K������,�ϕ>߄��a�G�������\��;�+nD5{�������\at�^u4	ύ��������rDN���S���v4[L�X��|w�=�d�~��^�ۨk4��/o������vK�D����~K�Q&���~U�YQ@���s�?���_����&�d�k!m��ΐ:.����rpR����ʯ��D�������g����i\��F~_��_��=��gˋ�?-�����ߍ�k�����]>���+H�cnr>�ath|]�<l�Ge���'��S��u�;����U�gC��yX?��ؔ K7�&��56]�F�_~֨c��*���ř�¼�!����Aw����lf�Cƍ�F���%��'5>�į�sp�/��g���f����<+ׯ��|8Ї��|V���fa��Փn
�<���ϓÛq�~.��f}O~����UO�W��wO�
�����ڝf�Dp�sP���������_�־#:O>�"܌�o��Q���X��9S�8���x�)?���U���7�u��lށ��ӭ��N(~�MΎ�o���*ϵ��7�3����zu���z������~%������ӟs_z��7Z���|�^d��i���[�^�y9}6�V��'_�@}o_0�/.�裼���������7��f�~-�U��*ÿέ�������_˥v�߸��x�z~rx#?��x��dE��D��D���N�v������7׽!V���ͧ!3}88�7E��i|͍�J�(y�;�<�_	������>��ܣ�}��|�wl�uuq�bG��>e[�~Jn����<gظ��f�:|w�����|v��uxzrv�51���x3W{Bb>�OKS5���6ы_����?Ng���G���M
W����{b3����e`�	�����#��r�MH‡|~��֯�q�&���Y����"�,��r�}�^���M;�5~�$���h��s��u��+>��j/�<_��
���G;^���r��;�x%����O������Ư�v��Í�0y.��o�/yf�6�(w���qt�w�l�~>zx��k�tF�/��:z��K��Y(d>1�+���D&S������O�38�O���!�~y�T.l���Wc.D����q���@��%8��Ggt8��N�-��_N��~�G�ζ�k�$|�����s_�51|�&�1���P˨�Sy�5�^��pSP�37��pg�A����|D��(aC�w��
����5��S�
�N�N��%��5�ꇏ���T�/��%�st�>�����|��|g;��ξkׁ
�>�%?:ˇ�M)�ѴV��
���l�
i���6�Ub����Cp���*w���|�Z|:���)|&?�9��x�/��2�	���S@���x�y���(u���~�>������u��N�߾u��>~�4�Q��S~�Γ����=0���6_���{�޶μ�gy�X7ԯN�]�W�>�v��FN��.
&���YzUȟ����=����w�~�zj\D�Co��fw�0^��s����l���j�M}>=�A>�T?:�Ħ�����f%�6��}+�G���l��>��Y�t��WW|z�c����v�0|�'|֯���y��fwor�NMG�^�}�]�����g��F�ч��B�*��
`�3:��k��E���s��wbb{��Jg���:7�;_�n�#@U��g�H���]��_����O��⳩>�(����(��F;jW�ָ5��<�O8/��u�����r8����u�/��g�W�
�`��X$GG�:����i�|>�y�_T��|k��(���L����	�r`A �x"��������A4p�>�A�u��J�I�W=+?�̀�W�Yѡ�
&
�A�`|Q?�9�reW�"���	P�5Oمѳg��5��W��
�^.�;��C����'-������%��^R�s��1O�I�L�|&$���-HZ��&�G�\���`C��~maR
��ct��M�?��%��2��(��BVp���4aj�_k��F��,&0�/�>���9����2��t
>��g��/�#Hyp:KV�#�Ɯr�={���d=8�AKQU����*0�Bc��u��IU�l��(��{��"`�����>~xW�D���A�R��(�Q�I�`9�u�k�\hɉ���S>U�:s����}x��C�BS���O��
��_�47�<��E䊣>d;�A&��+2�!�1�����yp�bp��0H�a7T�K����=��<޽y�����ؙ0
��G��W�9A�XT���	��l�ɏj4�8�Hckh�+����\�j��K~�.k������
�a{�����!'���ʣ�W-�����/�y�y6�ӡn{��L�"|(��g��@��R�?���A��e�Y0Մ���[�
���5|��xnV��q�ck_v���>-n:ۀOJ3�Jo��P�L�H��c]k��QY?�1���SԷ�rv�
��m���3�SΥCr�aa큡v;E�U<���%d�^w����ݫ[~ߙ�3�����/��U�k����t��mA�f�P(�Z2�+&�`��J�Ա�=*�������<��w�� y�u��PC��r
����p�tZ����]�/�͖�(&��(6r|[>�Op����w
��0�*?b�#{��\��<��},��m6q��U83��7o�:|�C��Ӫ �y��t6~1�t�X��f��G�O���V���f['9
%6N�Ǭ�R�'7��Ư�$?�A'�����/���O�'~e����2��2����EyOp:�� ��J��
�ܛ����
�+I�H|��DV�����Dv�e�&���|u)�$���c���[N��/Q4Ϟ֢�s������l(*��P�O�v��O6� (|�uV��I�C׳۶
���`���j� �D+zf�����~_,���
~%!ߙӕ_���ѧ~u5�3]%�c��kp����P�P%|8|���s����4�����Ժ�^�>�����\�I?��S���з�ׯ�~={Nخ�Z�����#�iV�O+�^ܝO	��s��h���'a��6~��fgrr��3.O�q�5}b�Ӡc�.�T>����2���\���q�o�|�ٙ
T�Dv��租~4P`:9���+"�=��F�E7�6.��'��b���ո'������:z8�L?�{�Y�ag`?��G�j?]��P�]��W�B�γ�Z^�.�R8ɏusV�/���_��_��}'�g�?"�:VF~"����{4��G��)�Z�Ϟ�'9D7r�{'�o�.y.'Q!R��:_+��Ɠ��>�ɏnbH�Q��+�S��]�I$9��x�����[٤��\Z
t ���0y���S�vw�}ǯN>���z���{\F߹�'���J~�2�@�q������9�$����7=m�6�BՈH�ç��;����_�@�"��C�_ɲ����xY�|�YH$�
�ݙ7��W��~
_����?�ߛQ�ӫH�Y7�v��8X�ύ~N����Z������ݙ�Aѻ��爼4m�]k��2��h�<Cy뺰�g��)��GYλ�5�{=կ!o$Ѩ_��/��TSXQ}*de�i����'�H��p\�W���I����^녷:;G� g
��r$U��hz#���z�/��|�x��2Z��S�up&ϣ�<g�������g��W/'K�a��ͺ% ��Z��V�|Ag�O�
�w�C��t��3�$�g��D�Ek��|��g�1����_�|��+�z��4C���чg���k|�0|v'g�Ӎ���N�q:~�Vi�M_Ư��ߛu��+���~ž���i��sg����
��5Rg����/#�
��|�Y�|�цt��@���w��>���k�g����������I��=d�-[=f��<��}�sx�h>.#�5�̧�u]���+W|�+��c�'�\�;!���\Y���+�L���lK=o�5=�^ݾr���jw���~}&�ŶL��/�߇����2��_֦헙n,�2�����s��':��;�G��D%�����gO����[���8�=s���t�c]z�,�le*�g�lcz������'9/|n|S��t��O۫>���Ê�!q#�ۗ9E�������G��@��o��xE{;ꬽ��=k��&���m1 �8�P+a��]�C7��p��ޘ~�<ߑ�F��*|�C��@�y����t?ոH���j��DZ����Gi�N��?*��U����__�e|,�%v9鉏�U/���#�g/r��ƕ>p��|G�_E�韧?�a�:��^%BȐm���	`ƌ>��[�f�,>Jp4�l���84$?���Ws��4w��v���>�%?|��E��h7�����S�l}]ч�$��g�/��Ώ�H���]TR������gxB&&���:�\��*Gg�?�awz~�98�`�%��g�:�ç�^`�rB���n�w:�����C{���]<�Ħ��F&
>W}��3��ǂ+�����J9(/�%7A��,L�x���
�q��~}
^���B�P�*�Nr�Gq��2t���Lqz�?��r�$��J�t�k�^郏�o�Q?=�^�'�O~@�v*��O�+ׯ�����3:D��Ʌ>�gq3k_�TA��#��x2Hė��>=�ܸУ&��pB��@կ�4/���Ny�!�������PN��W£����G
ܡ�@v^C>Ujj+(@���'�O7f�c�`Vq�̊+��T�	p|GO����n{_~=��Mt��
�E�8��I��_�t�\�H)����z�<"w�{��RWf���O�l!i�tR����fC?�?})�7�gQ�8sr}T��#�*/�:���+Ϣ�*����·���U;"�<�_-���'cT���f�k���5�oG�S�J��$�
5��0,]�|�[?$�䂔��3�tj���0�_r�:Rʜ�o�J���ߒg�<ZN�l3:�pT=�~��h���Ѳ�E�Խ;�U�v�U���t��OI��t)��5NV�?f��fJ�Oe�ã��b��U,�k��aA�����
��/��gi�k�BƸ�_��|���II��I�E�O���ʣ���#.����Q`��_��>������dSM�]<Dx|~�����cR����7���sN���y�|���|����X��Q�J�"e�'��#�)?�
~��V�/�vg��*�?�RR���x'�?C���]<p�R,��]\�?T3��l����-�:>#�R�fі���`�j|��
��۟y�8�y����3��q�j.y�$ݥ��#TP��*����|S~{���
�Da��d��§� ��7�R��E��%W���s����~��[�.�BX����H\<)��'DmQ��O�Y����O��`Λ�
�V��K|e��)����)�۟�N�X�U	8Ԙ���M��C�$D|"����k�w��>�T�.B��F3�Ŋ�Ҧ��u�-B@�Z�Г�Lk�N<���T��C*������_��G�u&"y<k�WA�g�v���_���Z$4r��n4���G'��Z#�V���y�9̈�x'�1n:�1�-|�!���\8'~���Kq�0�Os>�o��s�ʗ/�՛��2�1iJ|Dԟ��E��UJ</�w�&�Sd��Ae��j.���BSJ���o�-wF^�?�%�1Q95F���	>�ۡ_�!%�Pk��ݙ��<x�"�w?Q?��BYgՄ��[��BH�=pK�P�_�l=Գ)�f��P��0���Z���L�^��M;^-D��>;+%�4P����x9���E�'}�[�t-E�q�c�??�_0/�
~�LдH�r�"�ڱ�^~r1�������丳[pv�Vak��ڷ1�m�x~����i��kP
���x��k�����G���*|pͶ�eS;^�{�-  3��oa�K�W�V:�X"	���dݎ_��[��N���m���s6 ���_J�����fv�o)د>
5|~y��d:;�%�%`jx�˛|�6�]�Wd���|!�Ew$^t�@p�Sj��cp���ղ�U�f���S2�Y�5�:e�t��4���w��?�'"��y[,�	[�B`~�tA��_�C��_;�ҋ�#/
Ў���r@b�/����5�9�;٩p�p�~�\�Wܾ�u��Q���ZC�tV��~�9��g�p"�<����Ǽ�ݣ����D��yV�_���:���\կ0���1���}�ᳳ�G�W�D�+ҩYEC��4��]���!��u5k7Rw&��F=�6�DE�l�u�>�c�F�?��%�ORwC�]�$8���ٚwJ���|&o�wv��$��S}"��y�C��㏴V�19�o�߼*�lʋ>�3:'�7t΀��������J�����k����$�L����,-c2躳pR��mT
	�t'J���+�ʱ�O?���#���^���<�p���?��3�?���r!Y���~�c����(��
c=y�l�+J��(�s����k���DWW�'�D���?Q�ŋ�S�*����ç9�k^�T����=��!���ǿ�6|>N"�`�c��f2΂ԇ�_�9���E��~��Ͽ�׏�A����q1�L���$����
���?��/;�w�����E����bg�wg�577�ɏ~��&��������{�y���Z�P�A>���[�'V��?�3~i���˺���/㒟�g��˓4?Ÿ�w�~*�h���G[��O�$-�2�1��;u����׸ o�%>������HZ�)_�B�n�٦�4f٧�!�2�>H.ޏ����OӢ��p;:�~��ᖠ�3+���Uz���k��>}8�����w�s�$������>��7����ʜQz�6�\�E=i��������֯�Ғ��ŎE�ի�z_	�f��o^��1�����u3��{�zi�؞�g��[+�ř��Y��OƬ�;��Z�}�^���<9P2����Ƀ'��3�xZ�y]_E�͏�nS�}S�Ysp�G��w�>PE��ϯ���L~r�"�?������S�����H��
���:�:��=���\K; �ٺ.��U(�Y\���[�c��#a6d�o�7��v.R��֙���z,�|
R���B.5��g�>��G�>_Ɩ4���8�l|�;�&�>L���U�����f\hq��<��k]�v�Z���>K�V+�~̂j�~Um��O��l��v=�ɗ%�:8
�����[g�ع�ri�<5o�;Wy��T�KW�xW��P�O�D����k�	����$�o���yt�ϔF�b����w�l���n���s��i��p|_����<��>�w_<8�_�����Ÿ�e���j7_������xO`§��W�\��y3��3�um�%
�������f���C���l;ǚ3��q9D���a��&*_}t��4����+�xw�FOv<�q֮���i���DeאR2�߯����B�y�5�'�y�v��;�̏�X���Ά��D�O^�}��X=ڸ��5$��;Ý��<���$S���]vֿ�/_��4U�ΰ�=\!��t�h�f��<k��3=j����IB��8זG���_���z
��v�W�鼠x̶.��y�ϛ���G��=���a�2��<�9�p������lS��~��	d�����2�~͕im�5�MX�x�<:��p��}�~A�A�hW7�����H���>1��K}�~%��
U�~9p $Ԋ�w��y�32`�c�L~ΑN���6�I;T�ýk|1k��9��K����3ur�U��N���O/EH97��:%tC|�dAt'��pI#�ePb'�!�Q�P���"$P��ۄ�$��
���a@��n�`.�q>p��,���?:��B����Y5�|�G᣾81�ȃ����j�m��CAL8��B�d�14w
s�*�s�b��L���K�Q�h�M���8����S���:p��x!?��Wz=�[�3tÂ9|��]೸;�}(����(9��
�97/�fn@��a����0<~���=��9XV����* �k���E�٣~�!R�2	�ے_�~%��A����攷%��08��_͑�v�����ۋs/��O���Gy7��ce	GЇ|ғ�lh�d�W�\�?t�jZ���<\��|��<M t�oM�]B~S'���v
��)�ѯg��W^�ƅ/(�DN�O>R���>�x���O|/��˅7/�~a�S�r�)o\c�^�_�&��^n��>�5�&N|��4_
#/|�C�������|b����Ї�"���}`촕9�xc����#>�Fޏ�_�?s������|>��Q��[��A`�Q���8w>�B���?����N��x`���6�-Fy���)�޾,���V>���m�OzR�X��R�jN�Mj~!�&�|~x���W�Do���|��/�U~�@��y�(��І�q�b�Qh���W&�P�C����,��Y��xZv��c��O�/����*V��R��‚�3.+��	�U?<p�'�b4���hԴ��~�Փ�GgD>xJ'}��2�	���*�E}	�r$��H_�k}2����#irȁV�~A��?'b��ˇ�,��TD��~��B����+�r6��3l�u��#���b�����3A���~���:&3a�(Q������6���^��/lC�2:���Cg8�yp��}���w�}����I܎�@E�]��k����)>G�\�����f&x�]cxqJ�=�'�_t�샏Po�b�{��GK;���F^�f����<��	9�����}�Z<�����c�=ӊ��ɋ���?�����g�^U�
����5YI�~C5(}���f9Τ�r�s��y�=�A���;/�&ߟ<~�'��l��t:/Q��^P��C�4�u���$=��o�j.{[#��ٍs�:�vĈ<�	�Zk/|!L���Э��r|�=�cx�&;|_�uJ��*?rQ�����Au��xg��b��
�cV�U�/_�`�A�㝏��Kq_�1A�S��ć��!�w{f�:;��I+��Q���x�_?�����rW��fDp�r�8�q&����`���&]��h�bv�~�@�L���O��w�<���ޫ׹?㸞�@{§�_X>D��8��8+;P$�b��p�F�u�-����:PB\{B�+���h߲M����ao.=��x�5A�d!��<�2����^N��V�g�O���d;��,��%�~ƥ�,�;8[�Z,��$?�UϰB�Z��3�_9�Լc��S6�*04d+_����A6��-|�{��γ��3"쾔:ʋ��}�*���_����:b�T߸6�TN�:�=�Cgh��эlg����u�r%�I1)�>�� Vx��D>7�hq����Y9���X�=�;G�~���h�G�.�1�=D�|S����~'���i���,�=���K��m4���="ۄe��F
�~x�p4�]���,J�7�H���(����
�xJ�H��{��۰�d�g�,gz�/��cǥ]J������=�������Y�[�`փ���S�M>%q��m	��c�^�V_��v�5��ל_ʁ��x=����y���RMLγA|i��Ot�o��g�7���. g^j��7[��c��@/�z��(�:�4w����y%;qdSk�(��l|i}4�'���S�6~���Ө}���n��3`��ԍY�=l��3�TY�Zt��x~�*w�����/賳��wٽ�ujp�C�p@O�id��Gm-Lt�|&W;��,a�aK��z�~��57��c��|J	
�>��o0n.Lߚ�X�6��+�r�U�|�+�l����k]�kŨ�1Ip|	N��'8^I�"b'-|���cX��vgr�WQ�����+��.~Xs�Ь:�	hC���?5~��ϖ��G(�����g��>�;ߝ���D�>H����Y�]|���<�����'�;�~k��F�����Qf��~�Dwn�8Y��VSM�xI���E-�t��C�E�>y'����o�b$�D�h=�q��qv��|,��lC���@SÂ�x?��5�8��]�O������M�<ʻb���_�o�NZ�A����}�ѓق�(gi�%��>�(i�|荷?��$�(�R�x'�_7�7䃓�T��?���~�iu�4�O㫓��s�Jt�Е8��$���Y��0>�#LǎX�<�O!�q����RJ�a.�o��i�~��a�#~MJ�Q	��wf)_���(�_��닌g
}�)>���q��'���H�=q~¯�����}�K4��GL9Ǯ��yv&�=}8M�������?i�Τ�s��J�8�����_�1�ieOq��s�M��韴D���و=VS!�O?����N�c�9�#HZv��÷L}�G)l�pm~��\Mj�/��<�����G'�̧>��lm*$�L�g���8=�<����8��ŗr��=m=S���tB���eɆv�D�-¿}Yg�u��B����a�7h�{�����~*�bC���>�WƸS�}$��[O�$�
N�����9�ׯ�74�+�������5�h���E���6y�n|���Yܝ|�oJ>F�g<i}�|>+W|4>A��l,�G�ٸ�_=�"���1���_ܸ�<߂�[���S��e�'|�޾��)h�i���o�)�n����+�����s�Z��0���/H6ޓC�9F����h���	r_�uZ�l��G���������m��çND�	��E?�z>~��g�;�28�򳓌/�?>V����_yM_����W���n�?�9d�h^S��@�_�c�J��i�
�ŝ
��tZ}��y��ӾT��*| �'_���%���Glb�F��8,S�)���OT\���m\4�?�y�Gq��fRn��T�J����ЁF��#��?���>�x
���~-�G�&�m�$�ؠ���
Ҝx0ޮr|tV�꘾�G�O������͛좬��1z�f@�;m�'�{�Of���$��!�����"�
=�Sy�	��G��(*o|a}�c�����5��;ix�ӫ�+v��G��r%}q��
����9�V�����7��\Y���O/_��Q�/�S�2�N��(�_��;%R���G4oN�|���8 ��콇/�Ƌ^�qq�p�I^;�/���)P���fQ�3�wg}�8�2�Z�7c[|���>^q���r��5i�����/|�r�������S�0�%?�D�|;��stx��8]8��@��3ו:�_�"]��v��y�r���)���9.2n�C�-��$Ç�>�4~���˹�S:�h6�ct��д�����CCQ�n�F�w��AY�Җ�
=��@���5�|�M4�\Blk�w��BN�� 5�V��÷	ցh6����	=!�O���4�ކ���p�'r������S�����7�� ���?X�u���i>-���9_���A�L��o��9*��]�ճ�{�{��~��-� ��>�5X��O򩾳��̦�~=�dL��
߁��tѭ�È@x����`�����`7�������cu�O������g�5Շr�Jֱ��w.I�Z_5e���N���(L�'�A���h�+��V9>�>d�۳*�/�����c�78�
_��-]Z$?�+��g9���x%�	�G��;�$�>g�x����uKB���ZF��T��1xǦբ��Lǘf\�h�[��SN�e9'�ѓ�
������/Yv�K}p?��~K �[+R�2*W��l.��j�J��o{7%�n�$kU��N����e��)z��y�
��0��-~��W>�#~�M
����'���_˗�<�7�zw��P��v��*��S®�g�oGqYθ&dY�6��4���%pIv·G}�׻���tA�$84�O�$��Y*������n��v��q'�j���>�5��U�E>��'��	�mI񉸿8���(�V'�
|<�ɔ���O�����,�>��Q���_��)2>ή�X��:����Q��#�&��9�>����P�fN8�?��U�`'�*'���c�0�p�S�^ཛྷ;\�#�芦�����8�;��#� ُ�9_�x?��Z����x���3W�'�(f'>1F�rfk|�Ex��	���>�N����ߓ������j�����/���_�������z���+¦����� 
 d�x�"����+�hep�������_�2=���_����_x��@-�Ŕ��O��/��&�������y|�'"��֘�����0�(�c�A}���v�{�~��/�~|��H��3���F���Ξ�4����g��'��P��� i7?�M��g���Y�χrt����st3��f5{4����1�Ё<�'|D��/^��rv���ٙ
Y}:�w�V
=�vG�g��B	Η�ة�&ג��Y��	/�YT�� �4+5i��Rnz�s�]�ҳ� �Gu��3��G�A&���I~^�d��=���{�w��E����"��D��G=��4:��C�z��E8�ñ�:+ʦ�i&<��vl3k~�ߌ1�_�h�q���'H�	�=$�t<Xg���Q~��fw�j`(t��۶�`γ1�4�岁8�9�x
���?�����ـ_�u�}χfg�3���F�ْ^��0�ĝ���@l%ѧ�¹Y=����w��8b`w�m(|�!�T>��_�il6�+����~�ֹ~A	>���SX��1��'8���ɦ���̶5���m�Z�M���zf.I࿔���	�5���َ���t�8���g�	������+�\��wO}���o\�)���!]��Wg���k�0�gS��?P�k��z���e^y��_7�b���3�f���a��NJ��荾�T�0��}��0�8��Y�ͦ}f��������d,G�_n�WxV~={�Y~Cx��Ka�C��C�4
�m�
��;����P%?�kܝ��ﬨmL���I?o��>�Ӑ\,��0<�K놏Ӈ��U���g�ڸ���0���Y'a��֯�_��.ԯ�I~�Q��q�7�':��oJ���:N��d9>��%ۭ��t�v}R21�_���'|Da�<�9��+�����(�>sk��ט�j���)������kz~�P�{|�Y���3�pŧ�T�,��'�':$���y�<����R7�l.λ�mJ�Qn�"t~�=�}���:~�s��8m���/(i��c��UU/��k���6�{g���|#�S�`����~�>|S��F'ϯ|YΠ
���E�lY��<_}F����]aOt��	����1����`�8��#"��0��Bq/���s[<��f#�|��&ϐߙw��|���8�����?8�g������NK������RKC���V5�O�}�ϕ�>˃^a����r�N�ɝ�
{����ި�ˡ3��r�S_��W�o���<�(Я� �sp�����>�D��b����ʓ��l��WoބWO �l��m�v�@%'�~U����K��ʓ��y���'~]�yn[��/x�ɕ�h�Ѱ���m�O�:O��/Q��_����_�R�:*|Fύ��ͫ
�8�h����3��z��'8�Otn���W��D�_�v7�_�m�\}��e�Wt������k�K>��U�ʧ|fO��w6_'σ�u���u_0
s���NV�8|c�U~�K�+���~p�3���<:O�|��[�M�6?V�|��ʼnԣ�'|�B�Y��hҗ�3a[���yK��$W{e�Ww���j������f�5�>�u���=��T�.\���n�'?7���r����l��������+�T88x��Ź�߃?8�7�ez,T����ʷ����O~6���T��Ƅj���5�::o���q>�%���?=9y���O��͏��_�:�Dp�L���n�	��ɞ����ռ3Sap&?7w�4]<�L�ԝ3&;[��˧amª̶̊���"dd��٣���&�=��5�'�v��~g�=\����~*������y�<RX���FPi\>���_��W�t��I/�>w���Vgf���¿�|M|@�
��-�	�i�*Gh�L�ΰ��3�$��ˏ|V�K��ӽ~�y�_��q�	�����t�S`Xt���2Nw���_�|A�|�PȠL�8��'&:��[�*H|;
:r���r	a�|�o�06p������8���!�����L�8�`�'��~A���|�Y�5�b�GF��y$�-�k=�P�
}����#
2�榆��n������1rZSB+���w���w
��J�Їt����v���W[9P>�d���r�_�@9����Q���,}�n�|g����킏>�k����cT��~�K���6��_��n���/���9��?���i0޻�h�ե��)|�����sr�sDC�����Wn�pf�>�y�߹;�(�t��9�k�x�.8P���/y�˩Qj|�o�'��8J��`�^q�����r��)����bj|A)|�j��2��VÍ��7����?�p�0B��5
��G75��y�C�����~@�
ҭ[�T^ϧf�ڦ���i�\��9�Uy�lv�� n�#�ã�3�s���W����g���I��i}>ߝ��E�=7��W��aM�6���ou�{���h\r�r��X��>��i]ȂJ.�y�%�"N�l}>q?�G�Ý�u���a���k���3�
|Z	G��+�-��<�T��j�
U�lU,+h���ɠ�
;�IVX�xk&7	#���h")�8�tvm><B�|������M�/�;I��s��r7���0��`�|�K
�]$�;b����O#[e�5�9�牾*�*�����S9>+�����夏.@���8�)<�x�E���S��>����%8ʁ�T�]������b�_��ר~�_������3c�Gp���/g�op��:�>�>�5|1�R98�8�Q�/�D��<������b[�K����5���j�����I;�y�����q�Zit��QH�RlC�U��>�5�����S�6<��Ec#���Ŝq��u�{CԴxZ���ۊi���~5��]��]9W���O�Gͣ_��XQr+�~�Yt��u���I)����]?:�p�`"�w�Y�w����z�>���x���	2���$�f蟺�5_�@�?�&NLі4
}.�k}�S�DK�(�?�}2=@�asꇏO�x:�)b������fj������s�P{�Z��/t&$ K
EJxHu�G�)]H1��M�g5��+�>���&�Q�W�;�(�F:�ZS�UV�K���=�u�<`.��W�y���4���d��W��S~��>r�X �|��]��.p�����G���IG���U�0����B��i���o���N��DhbI��X��*Z�O�B��o@�:�V� Rb��'pNu>|������`��TH�_������Chv��8��)oh��*U��� z}�/4�K�Cmx�:龏*_�w5M�����l܀x�H��ػL�
w	�.<�	��
p�ʔ�&��gSh5`Z�J��/O�JikO-��Q�5Y�5�b"�w���C���C�R�ͩ�c����.�ʋ�~�OfP Q�]
6��J~
����B�.�����[����O�0�{��e��J�q��`9�� N�P�>�LZQ)�;j,~���8z��v�bL�U���Vq�*o
7We�;�	�"��z��߇g��j�ݯ*�{�� �mR��DNK��v1B!�^<*�
�h%�M�Wl�E�)�q�q�[���έM�9Uyt�$�M{�+g�(�SH�I���D$��R����38|����riל���b�`����I?���ś)���"�򛽊��3|v��>�S��pW��[�����ם$?�X߅�)�,Sr{AW���2�l�L
}��i�/U6S&>��>��_����g�<��"�rr!f7�`9���+}�oo@E,=����3����^%;nopՇ�!�~��b��)ɛ{����([��5iߘmQ���?t�K�������f��EIg[Nם=���Y[�U(�W��r��,GD����M���F��ߟ=n͎+�s����E�~x��
�|f��x���#$�M��݇ޙ���p���[�R��HN�һ�'L' ����Yu��Kz��sgl��
��HlQ���0���y6ze(�4Jp�(��K>�]����-XF�k-[f�?s��32��I��
�v=�Cs�0:S��O��{�UT��5��Σ4��/M�:[n)C�qq��:��C��t�C���:>�ъ�J�|������S�*�	3!"p��w�i;��+�Ň�)�9[�bUkH�x����Ps��e��۟�Y#�x�������#������/�C~4��Y�B[�ظe�m@4M�C����M��u͡�_ـvv��/鍅BY��?b*�#t *�×�`Y��N?_�z>߯��/��"����[�������V���Kp�zU%���eŷ���܀�Q7�?�O�
�Q_| 6�\\���P�+
�GJ,�)���I��O:3Nw�5��3lM?��;3�w;�mr_h���
}���iJ`gl�^��EW2��?Z?��s�8��5�]Z%8��	vï��O�N��lӍ��,~�����O��V�$�8{m\���)��cY���+:_��w�_���Qo�`g�*�K>7r?�"E"�	��CN�G+��#��"��'�c�C�t�Z2$ϳi�i��-B�7/O���!s��6�7z��=��o�Y�|)������!��K��(�@�{c9qv�����Z!���f�>:����	
���^je�>�N�^��ģҟ�ѯ����m��y�z[gF��y>O�l��<{��r��>��ܖsjn����>uyqt���띜�S��&�~��z�&[�į]�z�8f?5/��b��1��vg�I���)��2�����_[m��3{���U����Y���ʼn�&	N�P��r�/�ݦ��5wj�':��Oj����=߳�Ln�����z���)ϰ
��N!>���J��>����>�'?�α;�B��P�Ǖ��͒vA�5*��%��n�Z6��c�_�3f�0d7XN���Է���|{9z�˽�+~Y���G��|�ǯ�';�@���H�}�����/��'�t֨]g�=�7ٝ�rJ�dm�~1����	|������+sd|g�Iu �O%߻�8.����ǥ���RO��Za��mL��;=\<��p�tA�|�ق��r� ��G���pf]���=wg��_$E�ƻ�%��,N�K��ޛ;�!?g�!u�A}4HH���MF�֋���[:����ǫ���s�9}k��4J�9�:}b���>�GM兎�|�' ����4*���1����q�31;-�k:&B�nq�����j7&�'�
�WNʗ�>�E���FK�ɳ���҇�&�ǧ<��3�3�O�&���\yV.�M���O��|�‡�N�&.���
�Ty]�k,Ӵ�!1<�/�
����	m.�^��[(k��M~�C�t3��':���"���G��ہ��F�M�>:�
��-a�O�ʼn	�'�(Ga=m�Z+���>�ۏgHD��q��De�;�մ��q)��6�3��z�����Y�xM��߃��L>
��W�ʍ}�"��O�G%(���T5�����Ne&,����Ge�W�g�)��Ձ�!��s{���w�K�
�TŗKz�"��*�]B�S<���+(���α��`X��s_z�+�&=I��6�?��&R��3�DB9�͏I�Q��miU.���v�I����.*[1�ɯ&�r�)}���{�j>���9ߋs��9� ��~�4G��Wly�%?O�_��!dN�ɳ36n}w�=}.7S�)>M��5�y@:3�޶���252�6�>*{
�{�M�?9����]±��͔Z����
����W�dZ?Ϧ9{�Z�'�4�v�\+N�J�������u����'��]���y�D<����� �
8�'A#���:
ɣ��
�r-���0%8F�|���c��j�Y��+<��ڳX*GU>@z�JC���O��9#�/tx)k���ְ��Y~4�Y���I���KB9���z�SĠ~��
N�|���� ��(��<��nx�]��İW���ѡ��/8� I�RVB~p(a�P�_�������_�c�q�E�',v�,-����u@����3�p@�ۊv�z��)	T��/Iu�e��\��|A�������X2}�7�M�7bg&�E͓e��7��>�O�Y@䨎����fIpr�>ݹ#���“��XKql;���f"��G���<g����=�㰑u�I '�m8@�
=�	T�ӿ�O�&ϟʏ�>�rC�3b�cla����_�n
%SF���:J��^��>��AQ?;�7mm��(Ϸ�B���~⣧യ�c
��`�%�M�b��z����W�{u<��й�H��Ԁ�Y�I�-�K~|���Y����,V�L���Q����D���d|Ň��E~�TSk�K�(���3N�W��&+�:����)!��-}���δH{���SUW��#~���r�dp����E�E=���m��U�~�&�\/�E��!�(F���P}�5��������9G�q~�3H4�Q:����=���ܽ%xR�n�o��8.s�-f~�J�(��I"�⿁_��Pq�ErD�i��Ӌ���O����r�ڧ̑�5���;b,%*2'?����{�Zx��[�g
�N���Q���_�M���7I�-�ŕ�<ˏ�]ȇ@����(��c[�ɅÄ~��j��,C����=d�ث��ٱ�_�����l�J�hf�b�BƣPU��^�/|7�Qn�r�<����p�����XL�w�1��LZ������bK���
��C�X>rT"���ҕ_�tgט���k�A�?8��*�?}�\�gg?�C~���S9�0ח�kʑ4�&��o���+Ř�x-���r(��o�,G���M
z|�M�Xgr��J�!�Y���	ɕ>d��I��5��<L���I��������;X��Ogҙr:x��?y�YM���5.���\ub��<�4�d� l�e�spta�eš��Y��y�F��<G�.�?���W���>�G�Ƶ����k��'Mu���
�F���*�ƣg�Ʈz���Hmvƶ���dM�	u�e����J�|\��
��D�W6��"QI`/G���xn�,�T�܌��Y<�0�i�N��������_J��=��y�q�㲛
���>�"IxW�}~E����m�6�->��M�zfҸ�??�C�X�N|_��Bu�����)���V�`Y����u���ẻ-8�`ފ��E�P��Ɨ���n"
����I3�r,�_;�I��ШB�I�v�@��d�J�4��� �����ʺ�Dꫂ>����v�w�<��
3E‶��	[}��I�r��;��>��:c�.��}s�?%b�A�|�9��L�e��w����yi��x3�䁔	�c��c�M�Ν�(:h�����W	��C�\�b����sI;���M/� �~����vC���tz>/�܏:��ǩ��#0��I�'h@��|���?��Ga��)M닟���Cn���S�5D߯��8��^V)���?���
�<ۖ3���}�,>���>E�0:�%�D�4�j%;pty�?��.BޢO���7]�3|���>�K}�r����8�+snX:��@	���3'���9�_����n�c~��g����
�����c�X�@@� �q}��<����MU*_�?�I~�r�2�۱x���#~Qɷպ��n>Ed]%���'_����|�cz�S�|I?,���N�v쭿�fF�0D����|v��SR�&]i� '�O�b�t�Y�5�Gp�w�����N���N5���o��g�_����SY�{�3�0��|?����'y�>7�Ո��F�g�҃>�y��
��k|T>$;�	N(
N��|�������7���m���o?8����o�M��u�Y�|o�|�O�Y�!��~̺�W��S�q��*��ԩőZ߄�>߃�y�>̘v���s�������ǯ��_���}n� 	��y\�y�9:��_���+W�O书���	���{�"�Ϻp�ߩ|������pty�]��������n�&}����'T�'j�$$���,>T��i�x���5}�~gu��`�`�p��XΦ~�q>%p�J�ú����c<��/�j�艖ʣύ��i��!����-�rg��)�H�0�|����<;�~�oA9�k�#k�u�nÞ
E���W:��n
�awPY{|�*�����S���𘏡�У�������לrρ��?{=��k������-Z	8�B�i�4��w���3��a���_���SND���B>�f��+~���xߢ{t��Ҭ0�����Ǣt<�Z��DR���+�ڏ�qN��O~u��-��?��#y8s�8@�\dCjV}Ҭ\e�M:��_���v�{V;��1"��t���W�,|l��K���7:t�-��$�W���u��,_��"W�χ��Z�/9�?~�?��M��c�§'�0H��#�>$�{��s���^0�������|�|�y��.|�%	^�����)JԮ��T9���K'th8ߌ��ɛ����-�L�
�/?�,|��/�
ŕWU9:�Or��%�3+~!���}
ń>�x���rBu�~���wg!��s��N���l;��z�8�GC�qq0�D���z�p�h��c�|��+���G�%:����J���u�8@�Sy~N�M��F,K����Ey������xz%8�@ap�=S\+��Ӡ�V�8 �"_�U�hl��W�_p��P*�~���Z��ߺ�}�_}q�|�
НcJ�-��Jp�h�!�n�)ЍO+��F��k�?�wԂ�m~
��h�/��Lp��.ߦ��U�+>jx�N30����G��^�N(G�&�ѳBJ��T��v�]Z[�?^	��R_�'>����9.ԯ�?S��_Wӝ�j�����Q�L�s�~�����3�I���'��Ä��/zU}�H���|�Ϯ�}v�lz�I�����9���2���">*?D;�`�.=^��}��t�6R~��O����W Q�>F�;8�1�
��*�s�:��*t馝,0B��\��.P�gk�4h9��G��̵��3����J�,|��d�2�i����} ,4Z	N�@������a3�MU8�8�:�)�Շ��ѳ�_�o��W�}9�~G-
-��S#��?���Q������+^��a�1�N���g�Fa��'f'�|���0��Z���[E:���3Rmb�`lE@Y� Jm�D=�S�w,�Y��9��:��>�G���2��8*<RRYp��A���P�
�������������S�N��*
ã����~Y4�)�����W#�(0 ��c���9���ć�t���Ccէmf]�`���Q�֠�k�&�"��
�?�<%P)'����F����)��^���5*���`�>;u��_k}J	��3�D@������ރ�8�O��<��9�(����J_Z�y�H��=!d8�d?���&�W����X|�]
���(�!��	�G|���y�iS�8 ���F�y����a��/�d�<�y@sC�b_,�q�85
�o&�[Y����(�>�$���5�I�u�I�.�7u�'�:���B����xHGӢyh����w��l�f/��!bv�������	�A�>��Ggˣ}��LݭI=j5�Z��|el���KT��y�!��'x.��r��<��������L����2%��x_��Iԉ�_�qMᤏE�P{��x��}Y‹�_6�}5��6��T\�L��l�ɦj��t�S��)&
�=�s೘���.ϵE�>�����
=�';Ey�;�S[�,Vr���r�g٘���y�2�����<_�h�R;3���]�{oFxN��@2̚���l�ڍ���lF��	Up�A�|�;����N�������(�c�,6�I�w� ���G��O���@�Ǘ���ެ{{&58���>�L��9�M9�Z�q�a6������m��r�m��8�I4�{'_�	�I��a.)n��헲�p6Y2\����_�Ӄ>�?��,�N��_��K̓��|�:��_��|�*��?g�	m��n�;@���m�q�[�E�jFg{uC 8�mY�_�S��&��]<�V��S���}���V�׺D}����b{g���o�Fw�;���ѧq���Y�BO]��Ϛܗ虃Kt��ᆵ��+=�͈�{��G,nP�>�P]��L\c�ǯ�Iv0>�ֵ�\�E��F���"~
���(��ZgT�~��O�)~��Ư����$���}�?�Ĭk.��Jѭ�V��Ƌ.T^��/�|W���ɗ/�/gM,0��zPm{�8�L�k�7_���%�3Fqb���$y�O��fu�<�CC�Yze�4��!)�[L���<�]>�c���������)�/|��v6�4�r�=�J��u゘��6�6%��~�O�^��gC�=�a��y�>��>��OxV��J��uκ�/E����p���`�)?�r�IoDς�<8����2
�/��L����:���8���A���'y��eR���ԝ��"?��ic��!���dIɖDe�9���֪~g���J,�5�x0���?�c���@��g�:�9<�WzXE�_��v���K3��MqYn����z�>�P̧�ѧ�;�����9�w[�~��0;T���5�#&T��Ie�,�G|�>�K�+�P���|�WV��ƻj���L<㓡��,n�갽��W�|>LKgs_?_�I�Ot3\��>��Z!��_I����g���������S�[o\C5^�?���kNC�$�Tb)=��ne�Q�y���jg��,��2���>�-^ �G<ᯑ�4���|y�	�c<-փ�d��NL"]g�Ƌ�����O�/�|�7�(�攩ׁ}w*T��>����ؚ/H�Y��z[�.>E�����i}�0o����I�n��zl�����,��5�:�{M�����i�}�>��R'��G��uN��|�7�G�/MO���}掰��
}҇��f�{�ظ�0�ç!s��=8�g�A�{�����M�'?��D��秈y�����c+6<9}���	�֥ѧ\Tj��=~����kt�>\��9�<ߌ��38�g(�~3.&����Ǝ4Us�O|��,.Zx�p�3����W����
}��᳜h�'��E#9��e���Q�-�`kp?�sO��k��Ao,�_9Ft��%?�K��Ôr+t;\�VV���K����7�sB�\sZ��>7���s#?cb�>�-�F�8��Oq,Jz������B�~�*;���ʂS|��`t�	I}�̀ç�E.�-~���&$}�߳x'��w��B�����c��p�*��'R��;�^>��֯� �rv$ң�!�W�aܸ(8��'�F�	4�>H6Nپ��;~��9g�*y�wpП�$-a
�H��\|t��Y����G��U�BGg��Y�h��Y�>��I�[�$����ʐ�Ɨ�d��x��F��Ý��r�b6L��\Hʅ���=0�O:��a������j���bqn��e�����
:�4�^8���|n�0���2@��:�J���7��AødA,��'ߔ�\}RO�,�>(�Ι[
���Ԫ�r!��u�r���9ɇ/��L�E�|�AZ����]C����8��c�����;�b?�_D:z�9��5
O(����	�Y4ڝ4��HC6�.H��(���-�ܹR���D��1��A9�����ߋ��|��#�r�����z�_���'~EO�P|/�/�!iqt@I��6:�78�O~�?'�ţRX�k����0~i����>G�w��c9��E��p���&h�p�_Ɣ:��sl¯�1���r�!�]���b�u�S8�y����I����G��׌)�S9z����a^��A�3��Jsr�n��F��_��>�N���f&���ʌV�П^�(�1�4�=�f��F�z��J�&/_9�8�$Rzr.4��|�Wp�q�
��\Z:})��M��W�\>4�l�"��⸴g�_�DQ�P��y3�&��=;X�����0/J�ޭ�(��_ȁfD������d��ջ$�JBa����ů_}�~�4�_����n�E���hO���x�~���������)��飯�!|�M�R2H�e��:-@��kB~�3���u�7|��X{X�g��=/2p���w�fx��.�-��z������j�|����7��T�/��rU*N��r����T���~�,�����r�b'��ʁ?:��Jt�ٯ��ѡ���D�s{C��W�J#��V|�/|AT=�� �u�K����9�3��BP��nF$�b�3��W����H�:�@@��P{:A���d,�?�4�>��N�CJ��Y��"���XHf.���1��_:�Ù���3��%*�ᖁ����I1����zt��aH�B�\N��
�>A���4��Y�I�"�9��O9QmU��V���2j�/W��"����g�<�>�t���9�H�8��]��'�8��/�ᠦ�x�<�w�3�ZV~���{��˲I�d�d,b���6��b��+�~A���w�yŃ�w�R�4����Y���\K�J���az�8��Y���؛��_,�Ϟ4���@S�އ����YQQH�$��{"�d�cưm	��,me�.��b~��.���}�~=�&��#�9�:��{��&�>[�5��5�����+|��5_��Y��mg��p�WN��fC}�]t�l8�#�2�P�S8&%��:ߚ�s/-:W�~E(ك�#�����Ir���G\t.�9	�^lM�_��0}R��y���s���'����)�g��D#�� ��k���Ryx�n�p��e`�_7�!�?~����/V�l�7�.�7�D��e��6=8+�n��~�M�G��S����k������sC��9�xF��̿�h�ذ�3|֯�ϖo{����O�����������b�׮���?7����Y�6���|}��bu*�09�!~U-~��o�������w�	��s�Q�3~���}���4~
N���W}R�����Ά>:���0~���<�����F~vO}6�� �|�|ӯ�\�<�X����Fo(N°�&�����O���׸?y�����	��M֮�7�n��*�`��^giu!87���%d����ѧqQ��{�󹙜���=���D��N~��G��n��Ư����l���G�m����+��3�oҧ�U�%J��g^>����3}�)NJ���3T����*ON:]}m�O|�T���͡9~]�g��o�\sOl^8�$�Β��A�K���襣v{�	�ތg��ʇi��B9M�/>"I	(����&��zu��u���x�3A}2A�|���i�[��_�c�纾J~jh���<8�kg{��u��<��^R}��yj7pիɏm�B鐩�{����X�����t��=V����d���f��/7z~�Y����>>KL�'���7]�Q�<O?���F?ߟ��o��L���S��-U�~-V���y9�
�n�s�J�O����jq��e,�+��ו>�}��t�˟���o�W��O�3���&�g��K�s}}n�����������z��u��u7p����u�|��1���i��>�|߾ct�~��smu�X<����c���>�=�(�&_�to\���o�;s���W��ݗ��5<oƅ���_����
�Ń	�����_�~�<�l\|�>W}X�?�W��S�٣<������<߯������g�xn���=a|����
>7�?�/Gp�7��;��?=7���ܗ�
���~�>7v������v���3:���j��k��	��i�]�|�N2|T�����k���k|��o|���}p�ǯ��<W|&�u��>�^:ssį#�L�:>��;�>�8�x����.Ň��o��{�1mU�����8;~�'�Qqz��y�?~�	`�?������)3l�M�s��/>1��/�~Q����
|�gt.�{g���ͪ_�>P
.�8�Qߝ�C��pf��?:�+�v�Z�g��ya�ہ�(�~���`M��y�H�F`g���QU�h>��b|��%=\ � ߷?���>^}��}����8�8�r�m��{�_s |�+�>э�A���Ch�cq�&'�Q�����/;w���|����=5
�kHZ���)Gg@2��#��FG
;�M��GgL�9f9���y��<)O�'�C��s|���d����G%wx���6@5N���8��Z�t�K�>?�{
��H���6�\����P��}�4g�����Ƒ�n�NEh����{N�=���O�)��)��!�~)^Hrx��4�_��k�|�Dg��s�+s9�
��}�U|��4��/~�vs7Q8�'���z�]c��H@�4�_�OΤ�aM�DTR�����ާs�׵�����<k����v�|����<8���Wx��.��G��Yo���=�g�k�_�}V���V�3��������s�l|�W|�p�2����U���>�=�/�鶎��7��\92j���f�j_(��C
Kj�������~�Ź��jw9������o�S|���$Z��D���D�ނF9��S������ϡA[D�|e�S)P�*0:�':�T�+��8:��]�>i����;����(��W>��o��A�~���rг��=��D�ʵ�#�{௦&�W�W^Y��9D�����-R!Ȕ��Qq&�#!C9j�^��ߝ��y��{����y����o
8�A��5eg�����K��N����4��X���>`Ñ���k�����RL����¿OAx�=�K�����cH�$ �����9��ﹶ�b��n�}�E��ª��ZF� *����^X�����A�K���.8��庆E*����2$I�l�,��R��r�PZ�->L��Q��~@�.Q�_h�Z_}���'v���÷��I(e3���=�X�x�s����W��Gh���g�!�����l����§ąG��.�G�r���?"����|p.�#\~��C����B�$�O���	���3y��?��&�ɏ%��
��#��"3,Fox�gB����(��a
�7�Cj7ؔ��x?�<.���d�x3����9�{ħQx��)��q��T�ē3��?���=��En��G��>�m�ş��̯��mڵ��>Wy��ߺ�b���}�й�
N��w�{�yN�M��˒=;�$��P�L~�츞�|�N�{r�'��#X���R&8%Ѥ�ړ�ʃ��WؙX������w9D}��N���Z�mh������
��r��D�JLaT��%I�Y�l�IN�ѭ�N���^qOE/���;R�
��X��l|������PE�bV������<|��}0����2�T3~�O���=�Ճ�q�ͳ��,���Y��Vy��o�)6/��u�=�
m͚?~�&?E
�_��\Qc�*��m����y��j{�t>�>�or�޾xB�>2���b���Y� 1���xA�l�$�b�B�|����a�oA�O@�
�l^��@�f�e�H��'�AT������-��-d����⻒����IN*�98�֔��^����
��g���2J�O�{�2���3�B��
��l��,wU�ʶ2Pߞ&�Z�-{>տ񕹡Oʭ��g-�K�>8;���Yj1~u��6c6��U�kLx�� �l�3HM�5+_2��*�?8A�� ݛ�x_΅�3ۺ��g�9F����t椦r��t6�w��c�<\>�.Io�l���6����!��C�UKޮ�ߝ%,Ғ'���D
^��ҏ�\d}OC�>+�x��K�����J}g�;��LH�uW���Yy��2샬Ʌ@��&?��x]p�%����{ �A�
.�Dg��=:�o�Z���"��qv����G
��-�O�%�G�����/�v���z��f}|�G��s\��`�f@R������Vy*Z}K�κ(��/��rV�W�P\���r"��w�'��L^Y�B��/�m�2QA	�~TP?y@��!|�	��	B�,�Di���H~�%�8�w0ߥ\EQ=#�5��;_����Ґ<Åt��L���*�y�3��<���v�>�3~u��3�/?]Ǘ_�ᆪn�O��MiC�hw3�����;+����,D�k����A�R��9�
�|諟� =��
����D	�@�k��a�T�>�r$y�N����o���9}ь���$��:ރQ�Z����e"���W=sxU>߅٪u=�>`�SM�|b����Y�!Ta��98�B��Yc����O���L�Ư+>�Smz��q�8�;���6
���'?�UBS![�\���vp�>}�W�J��~�_?_Ϟ�s�|
f��:���e
�,���>ʢ���;�[��
��H�|"�s�#�w³F����Ҽ��v�O~6�o��x�/��e��o��'���a�cTm�	����|;�4�&?��z�ޘ��_7|T�>���}Jt��YV�M����}Թ�'�Y�����ao5ޫ�On֫�����h���3#��[_"�7����3�Y��>��2���X5��)�������X���֫*Wr#?�a��gN;�O�jn���rz�~�����s��~��O��O�'�W~U��:<:'���Ǿ'?K��|�6/��g����1%��ut��ԕ>��=���G��|#ϐ��s�s�qd�5��˕�����o����}>�zq#�a�@3V�:��}.�ԛOn�#�^i��v- ⻅���=�Ðg����]��IO���2��>����O���������q�Jɉ O.�u�����)���z��L�;o7���0]�t�(e�V98_�N8�s��d���!���a�.|��J���sgؐOO~�St�r'�2�����G<Վ�����ۑ�7�NTG�E�Cib�~x�<ۭů�wV�<Ĕ@�G��_���/����0y�ή�8%�sS�ހ�r{!z���˵t��0NOA��r����ޝ����6rőͭA��⣏_�y1��D��(/���{��ͧj?��䇝6=�kK7}� �_4),���g��C~��霘�9��/��7�<\���G����w�fwJZ%�_e��uWp@�=E}�TJ��p�32w
5�qъt��X7�)��Oq\�ATN�n����!�|�
Ӟ/]��:��q|G��sLg�-�#!����簫��狦�}��$��>��}w���N8Z{qS�/p�A�&�p���2���-VX�y9�
;s����O�A��i�����ß��O���>�{z�yy�V�y�c.:}KP�W�;U�7"|���X	8m^�"`��g���.��Z'M��H�w���I�w���Q�?=�~9��=���!��v*B��fQ2_|�	5���?w�7|���(�N�.Z>ѧr��Gp���
|�Nא~�J��a��}��W��~��8�Ѣl���ʺİ�S6�gy��	|�h�U���>����p��
7�K�ӇL����K�>�d��\}��()��Sz����������XJ�dR�7?·�?t�o�=�sL��K��|��x����ݎp<�yթn>M͟�?|ZL2f-	���;ǿ�ʷ��	���W��Ӈ��H�t�'��S"��/N>.ʋ����>���&8�W.��x������B��/��G�ׯѧ�2N烥��񽵜t4q�+_.��0Z�P�~�|}��~aY�M�]�s��b�����A��ۃ�X��PE��GK�΅x�L@҅�������J!Lt����7<2$�q��s��{f
+�I�'E9�|�S'���H?�F�_6��;�FH��?Dٳk��qԋ�i�9ߚG/���)�./b�|x{�y<}�Q@�s�s����#�~�\� ���|�!.��ず��9��_ʵ�������
�G�u/6���&���?���r��Ƿ�Q>*���c�?����S2�:�Q�4�;V��a��[{q��1�bBg.(d}L�~�K��0�t>���Leu�Y��ӷ����hr�GN���К�o��)H���CB�hSHT���STA�>��fl%�6i�&Cjc�����]�"^�#�	���k,P=��*+nGqwqw�v`ij o���Gd͍q,���7�� �w�E�~#��[5AeΕ6fT�Dk�Gt(���V.l��>r}"p��|�1�a˯o�$�QxB1��1��ٓ/(sI���~Jx��X!�>�v�v�;;��h'd�� ���
�C��\�/1����z��92Y2�cs����r�O��>�&Oq}�P-�~����4���5P�]�hrE���l%��V�醶[N�6~�Ԁ�)q�����n�=�Lw����@�<�[�s�%7����E��ׯ>�9fz�XW��Y\ZƁ`C���m��n0B�x9�M��G!:�|�2&y8��ɶu�a*O���3���vt({�ˍB"�5Ӓ����=M�W_����c϶�6i�!a��{%� 9��ߞ��ߋ�I���G���z�Mn#���O��;���Tg)qlg�3yP~c�-����u�p��۳-O+{�GTh�{5ҍ���	I!��I?� �U(��Ç3GR�)�?	�߾y���D�fj[�,�	m�>�>���>r���8]����>�y�MJ�M썋���׳
�ζ�~�/j���/J��6��kg��_܋��Vً?��s��J&'Ǽ��D�A������q_J�3NW?!e�x��z�yfF���|�޿yK�!�,�ϵ�A�'`��}|��Ъz*�3����<�67��7~݌�鍩����j�׿�U�����g
��3�{p�������^/AL)(H��q�O?�T���7���a���?���|�V���n���,ȿ���ٞW�3yNjW߻�at����_P}���	�4�+�~��'?��,kJ◇�(�Th\��i���‘J�h��,���n>M�����O�%�޿18#�DC�`ݳ�،����Ͽ�bg�gaD������;~����vJ;V!?���(�y���:ߋ�
"$%?���׿���8|łdÉ�En���c�gn��r��욐�������j�g	i�)�Lː��	�"����W~5��_��3��"�g컷G���/^[u����J`��/?��5��Z�D���bw
���V�뢏ot�5�*�Y�rN��!���=}��KL	T>I���4��r9�S��\Q�;hk�����U?�P�"�Č>�?�i>R�k4ġ7��wk�/	U��Q�ή^�z���|s|%?_�]�n�o���<���
���s���ؽ����i��},��o�e�B��^q�=?��s	B�_~~�_>*3�m	Լ���s.�n~T�q:y��N~p- �54v��Ӈ�fr:P	y�O�>$u�}<��x?<�L��Y��W�=�C���{�`ţB�������)mo�/�W�g4�S�n�{�7��h��yꪟk�&N�{�7�r�?���Nڠ�ŻZT��?�@�s��A��aM��g�N��O@n���G���<q4�\f4�w�OL\N(f*�����7���F�!�j�O��KD����+����\�l};�l_��H>��~g��l<��1�9�EK��}��@�Y�����\���:��r�\�_79t����Fs6ү�S�`���
o�;����}vS�!�7�:uj=:+��
`p�}��u_�v����o���̎�G��|cX��q�'����[j���ʷ��+>IK�0�&�WyV>�ߣO��O��&�7���X�M��e��s]_M�
��39�ڑ_gp�vv#���Op*�>�_�q+��Prc�_�#u�|#?��K��Y��5
Yp�{��<�_B%���{���Ԭi8=�Ώ��r��A�_��S�-��� ����κة��{�_�W���3��M�6�~����Ǿ�p�^��Q����g�b�Giq��2�ɭխ_=����P��e>�g��
 vHO�c}�&�F��]�+�7�;�ݺ���G��}�w�2c�;��[LN�;-Bgß?��d�w߻�֝=H*�4��ӯ�
~Ǩ-��.d�_Z�rq��w�#�3%�9��3�ȐԊ��j4~]�*;$ B��Xy$%����W�11��r�9(B�/g�KPʉ�'�̶oK���1u��}_p�Į���CJ��oqG�����(q���3�M>d�=w�8G��<�v��|��N	��nӈE������p��ۄΠ�=|�K��X��Cޚ�Qy�#ɭ���u�Qy��	�;ۅ�E��/S�y��j�	|�
(�7.X�W�����}П�=�K�[=5}R��DT&���C~��׻��G���W>�:u/9��De��ѝ�^!OP�`TA��aB��
*|r����򙄃�$�'>�)��Ci�o�p�����8�ʯT����9N���Hl��btc�Fs�J;>=+/1DC&�]� ��x�DZ^TH�q�X��N��D�n�rl¯xvo�wq_T�yD��D��5_7\�t��4Mo�w��@҅F��lNQNNF'u�Ū�rt+G�oz�s��n���CrOyK>��5n
�Y���#�&���Wy�A�G����Y|��v��@���d�!��V��A���a�#��'7����(���1�Ky�T<���k�pX��Oq������U�E�>ᓈ����[\rK'��W"$'�<.}9��1��P������8"�ƷC}O~=)�����C�����_��ǯ�Д��x�Ǘ9�4ϴݚ�U������D��q_����̳n�`���������/���`����a��+CO>��N~9���/��r>Hg���59�l�{��*��Q$�r-�_>>gv�D��?�J>�1R�z����W9��!<��
>�Q���?կ_G��eD:Z��чՒ��]X�y�����|��r�E�ჹ�\Kr�=�k��.�^ky�}�$���#~x�?l�I���<���=��ʶW�,���G}�t�O ��#���8
#����P��	_�h���D��U�ϰ5���t�"�~��j��I��Ns�Ct:�`�%4��H�K�
[���^@7e��fL�ǜ�t���!7ΜG�Y�s�Cs����<�o���{M�@A�oT����r��@����~��pH۶C{����6���C�|��tى'��������;#9�$O�C���ō��)�}Q=��ϻW�E�\�q�������n��P�m��H�����}��[5F���
�H7�}���?�Z^$��Y_�Q��B��A�;G���1���2(:���w2`� ��;_�g���?O�p��K^n� ?����J�w�	���VTX\��/?UN2t��0鵄;}-���0�6����;����H�ʛ�F_����Xm;�2(<����ѯ�\g�~�s
�#�4�b�DJ$6uok0y��F7�r�
�n9�:�s���K��[MQ�F�'��O3���9��ڥ=�'�~�Q�	��=��͉�w�y#8�}
�㓭��'�1������������b��W����=���{fK�xL��ovh�ng�������wL���a�	�k��4%�mA�x<71���v�*7���7�Zr5۫�X�;{�_׼�?���h�BX�ɖ�W _�uw|!���>8�Y�!���$?�E�]<���41�П���c�D�V._�r̖�ٝc8s���l�v��w
?y�M�~u�v�k�$8韄p�����rC����h���,�>�'y(�W6z���
��״�]��9�x�������X��
B�F9�&Ϟ�O�D#t�T�?���}��	�rjD��/�~�d@��׳����O���W-{�_���9޻����L�1�����3��>�[�I��u�l&|�?�e�F���ʯ�'_��EC59j������X��|̀~���Y�4p��W��
���Y�Ư|�bn�v�?|棙M��
�uM�����
>��j�W>	�VDv^���g�_|�l�S�ѧ�V����e�%:̷��/��l�+,��o����7�t��{c;�H�g��\WX\y��Gg���qZgc�߈�&�
W���|�=z����nf�|��)UϘ�o#����x��l�g��4���%����:��uv[�
�x=�D�|��;���|��R���8�������c�Lvή�gć!d|N=N�jJ��r[g�z�2T������J�S��F�r.�졟��O�ǟ^僲ہ|&:S<��ÝE��p�K�U������Ҋ~�ϧ!�Ž��G�_�������$̧W�J/9K.��'�!2����J��)B�&?�`��7q>d#��P����m|ܨr��V�8�L/�,$�7���oAr�{��{����(g�4/���k�
���)�2�3ߋMFW��8²F����`�m���FI��rM(/����;�"��(���-G(�O���+�<��1i���֫�9Pn<�Ua!U�E
�iʼ�3��u�ǐ���>
���Fϐo�<�WڍYvv�W'�֤o[rO?�f=6}�����9_
�A����|��-�(�*��t��'�w

��t��K���ѧF]>%�$���x���c���ݫ�/R>�l0݁�g�Ц��Z���'��a�lԃO��7�6��3ל#��Ta"����J�jt���*?�����w>:�7���7�{�*�5~;�n}��XܬW��3y�|���Y��$�ںe���}����g�j.NC��$'7��p*h�MW��������+}³��p|��g�ڝ�����f��.����$B���P-�fr8!L�M��;���w|� �{ޣ�rc���z~���ɫ���-�.���u�'�a�#�kr����5=|
27�������)���4��v5?8���:�3�7�ܾ2���c���grU��Q~3�'?=�'��8��:^��&�o�_!� �
�տNF7�Ăʷ�~�n�\���.�.����&]p���:ߋ_�18�����Yz��Ư��;�k����%�7�L~��O�:�@�HO{���.\p�Q0(�dǽ�i�P�q�~f������;��o\n����w�}F��º�:���xR����Ʃʅ~��T���
��$WY��{Dg��_V�Y��,���B�ޕ�Qp�o�������f�^���I��;�
����ӳ�/~��<y#ce����� ��
:�_�G]�l�@ڼ_m��}z��S�G�!�\?�ED���WO�K���W|b�=��Xq�&T������ק��jzR�?�����P���O>�qIn#p����K�~�c�”�'�7��#�r?�����	�J�n!R�|�'I�&���������2��ϠPѳX��Pߋ3���6����-�Uބ��<\��FS�
�/�;��r���d��~Z�j�V��
�T�|S
��_z�1��o����_�ʫ���z^s�)4�TH�L.�hrج�$���}p�48��4�����r��Ņ�a���D��+
$<�t��Vz�S}��n�C��/y�n�|���nW߈�Kt+�T�n���>/�ꤲ������BZ=<QfA�Ư�$��gI�c.�).Q倆������>b�RbN��۪������?:'�[2��9�A�H$�KtN>��D]-�@6R���<����
8�
�Q���<Ņ�?��d}�l݉R�9���rC��Ϸ5�n����ĕ=�J�Ў�O�eY��v�KG4��=�]���U��냣$럷�ZE�TIwßZ���s{�^�����%jt4b�����>V�sRF�r>�C��5	�<��o�o�
�.K{���)��!���@XU�"���
���,���%JC�'��’�ҏ��3�,:�O���f�_�n>Z�ݕ�r>7u�?�Z��WOh��?<5F��]K��}͛|p���&�z�N�?ѹq����^��Q�TO`�����:h_tƚ�c,�P��v#���X3оMN·;_���.�쥝��Z@��x
8f���C� yv7���l�Ԝi�W�c�4hf���=�{{1��q_M�k&����F����qf�a�P��o��j�3L�=—ob=�
��A������+aC�+�M�`��yщ���uW��G쯤��+9"������ZS�M�޸O|m`�.Dx
�HJ�O{��Ң�ٔCO��fT�t�(��Je��a�d����c��J�1���u�T@������Q]���'Zb^ņ�z\���sk���M_���ڌѣO��$���o�|�����/�>�	��3�}�������pg�R�|�	t(f*���#o ��s<���%��SJ̷b��N���_���ʪ��M��Ǵ�>��T���a�?|k��Ͻ5�͓)*&s ���:w"�pʪU?�@�F��>���l���L†��ψ1i��Db�#ݪq�}���Q�?�rpq��$��c�[��"���ܝ�P��؞~�-��B}�:ߌ��y�h5omu��Fc�����dZ�A{`��Q{W�a;ƗKtw�q�m�ҍk�C�ڔÝ%aaS�mW����Ek]�˞��ˮ�Q�ɘ>oM�4�~P>�0Lhy�Խ\1���@|���~Ǫ��&�e�^L��I�GnJ�B�MH�����w��>���2�t�ܔ3�]��Y$�{��2�NY�C��p�G�<|�<��g��(�B��Ϩ����=�1>]k
>����%��_Z*���'=�hj1��	�ݿ�u8���[1J헽vp�)$�؇�>��Yu.�p���C~	|��+�?�I�h���'9����9|�%:'�������ۊW�Z{{�]0{d��g����b��S�cݩɻ��ie��ǫ˱�Y{����s�Ab���n��R/�bf�m�`�\Ӧ���#��X�Ȫ����<��[�P����Ȉ�^f��|�|\�ڕ;5�>�E%��=���_I_���ú�%*E��I���N9졁�ivd%\���#[�Nwo��|�/��3䐷�	8�j�k��;X1�eJC	�f�q��/B����Q#��g����(�>Nn.P^J� 髇Ϗ��<���$������.��_]��iǾO���i�P����/f�br��y��v8�������^<<|Z�Vk�y,���OQ-����l���V�ٓp&߻(-~���O�~Q,xw<Dg_�C㒹C$�=�9:������K���T'<��ٟ��|�)<%�r~��䁸��3�zW���[�0�DQ�|�<�IJ+L/0݇%Gaq���4�������9�a�#�����EΧ#Qn���&'���פ�	�uZ4c�Pm9|XoZg�ȕ�_tfW��s94�Ϝ��{���n�$�|֝G��V����G��4�������5���4��@�~�6-��gQ΄�p�%'T�I���|���e�j�1.��J5�����������m%da�bd�{~y!��;\�H=�̜�ǣ\&�q�T�����`�A�� J��"~I#���_�Q㪇��wG	����)k>��
����(���(�mo�	:艏z��W$���X���D���s�o�qkUź��K)ӽ�&���X�C��'	I��E�N�����ꋈN[����%��������3���K/��U��G�T�`&4��d�S��"��E��>F���N�O�{���J��j����v[t@�E�)�'%�ϩS����I>��7��"�ho\͒�����Ϫ�E�25H2�|����1.�'<!���);���J8�Qu^�?�����_-��';v�9���1�p���U�w|��p:��u�٪�C-إ%?s\�D�u��@�/���,��v���YB�A�	g�O�sz�CO=�N�Ϩ�"?�
��_��}K��
y��� -\�%��?��0�˙�th����\~f븵�x��
��i\�bA㪳F��n\��ݹ��W��t$?�n�_���1�b������i�*��Y&"}�K��Re���׺���ۻ�V��e7<�-�)�̧�A���ݞA�
~Q#��o'>❫�ښ��9�/��|�u��%�[�����*&��;̓n��g�?�����">)#-�}:�������k�G\�:��_��&���Wtp�תSu[�o�UL��#�r�(�/���ړp\�����YO�����A5�J��Q��>1%�	΢su����?�ۧ:�Z��jǧ��q/�g^���ƑB�cѧ��k�^·g�)w���§�Z��T�BCw+�N�����\�����1ԓ��G���%x8>:�Rq/d3j��ݧ�چ�[.r�ݙvҋ^q����ظVI�����1k�r�W�P"�xk��?4��[�6������z*�b�W<4�I��'�E{��w�E�)�v���C�Z�/�!>h�=B/�>�3�	Wv��N������ ��g^ҩ�r�>�o����O$/X�'/4��v��V-w�^���'(�����h\���#n��_=�6�>�m��o�t��?'?�3'G����u��L�PO%:S"�O��������i'�OT���Cݓ��z���3�_ƥ}���*?�4.x���
�v����3��p�#]��3%|t%��qco�It����o�^p���
oţV}�r�ߣ��������pZ�]wmt�Uu:W�f�8-o�+�'r�~��9�=#�E�E����KNCp=p�h���=��^c94���;�[Jq�_�m��>�;�b�3c���%���+r��@��{�	�z3�������p:���&�q�e	�����r�LFgܿͱK�I�~e�";S��A�r,t���*��6�~�X�~�n�z����%����;��^��5$��
�J���E��gvc��^wU7_���(�/2���Zg�黟W�f�����o���r���˻�a�G���,8,�i݉�J�kG1qU�5�E��?�^�Q:k���,��aA���t-ԥ~|��E�{�?�����k).������m�pm�A��q��yjؽ�ټ�[�����z��;1��~���p�;�3��<���6˾䡻�ps��+w��Oh\h
��VM�OS-L�K�!{��O�nj�m����{vl�/�w>�Lo�b3׌ŋ�?�t����:D�k��Y�|geӸ��^,JiʼK�Lut�y-y�>�q���;B�h�Iq/��Gf-�-]����fO:u��b�]�a������r.y��;�l��>����a��\�=�E�>	���u|����Uo��u�y��OS%C1���˔�t�oMn�g�v�\QI�B��lѡu�2�tk��=���9D��t_�9�o��;�̧�9E7�1�k|jX��sh�)&���%^���v�O�䫞�e��
�CI�X�`�L�A��5�
���ʩ#��G���@ot�?�=�?�s�?��*:�g֤�C���Ȇ� r9dr�V��P�r��K���
��Cu��j��'��9{~���wx��
�ڐ�/d������>狋���V	_��5� ��ՠ?���P��ӊ�_�6����ƞ�v48���\8�qb�Jt@7��Q���1-bT�����-�g�Q�:-�_0j�w����;萾�g�=��gt.
�Q�w�z]r���R�|p���ߥ��[˸i�Ƞ�q!���?�nJB�Y�,�jL��\N��
�L�`�8�u�oϱ��o�"9�z���	��3���.�f$}�=�h����ԛ�e�p�����p�{Suh�ts6��jZ�'xS�Q��g#'c�qL�,����y��	Il��u;����97Z>��� G>��I��z{z��^Ȭ7K��0�{e�!0固�H6���O�a<���g��ty�%讝�[ج����V�D���~��?m���M��ӄ\f���wi@+����+���n�s���5���Ŧ[:	��Iek̝t�	N�}���Ed|	�]���5*��u攘�<U_7�������[ba��.�jK�e��a���WrV��1�N|���:���~�[5~e��->�����{�!%���dg�В����.��/!��o.�П��Q���U�Շ�9�>�J������t���c�S���w%���Lcdo�>�W��A~�1��m�����l�џ�w������>�d;�)�+�$g=���wR�d�<�=�g�x?8/��Ze�78�IY��1m�ɚ�^��+Ow��'ä���z��y�28����˧>�����G��ȭ4�&��z-|����3��wS~��Xѡ��UC�aͿM��}ϐ�|���љ/��c^�u9~�S.B?4*�d��g>FH~�m�$��9������rO�|�l���|���^��=o�A�N�4ӷg�Ѹ�<��el�{��F��_�_�Nj�9fv�e�	����n����ߏ	gҹ�
��S�5�GL��_���,|p%�Ch��˄����l�q/|:3D~��M�D�uD��s\-\��]���2��b��,=��U�<�&�M���/p�裳(C�P��c%�����B)�tn�=l��<��B��>o�d�8�j>5s�q=IP_�O�J����﫢:#,|�'�ڻ��śXp���#'��T�����8�!8�}��}��r
�~���퍛�~e�����8뜢r��j}�Dg=���ᓾ�O��#l{ֺ�
0��n�"��qe��v��N~=�X��8|���t:���$��b�2*2���|>= U�	���-���S}/�j��-�K\��Qy�O�"Ϸ�!'љ����q��;V�ь�����9|��q�ޒ��wޝG�G��j-�E�[|W�Zp��P-=
|lot�V;yK/����E�n�����	��9�b�Y:��!�v/�h7|��g�j�
�O���7k��=�>�4@�s���'~�F>�}��E�m�z�c]�����+hg�3���]�+����#WU;�(Iߣ��n��{��������	�'NO��+�6�d��R�O�&���Ͽ�:|�{�
�,| X�H���
7�{�Q��ͭ�0�,�Ӟ�N���Sx������n7g��c�M>��%~M|F��ħ���_�8
y��y���g��ȏ��E'���)��L���l���5U�^L���=c�R[�����s�>6�¯��/���[�4^t���1t���w�x5�T�i�ɱ`5.~��o嘞���d%|�����M��J^�"�1%9�&)�3J~�u�z:���^�����$��G|����7]���#p�Q���?aNe-K��~�?���>~
vD�����u-��%�{��s��~�x
��X����_�?��?�+����p��}.�X��[��R�Y~�{��U��r�'z��V���3A_�c��4��c�V�0�
O�E���T�#�f�U;0��
�
��6�m��W�����{�����+S�B��t��_�]�:�j�W��!�""`��d��}%�����a���~М%<ѿ�y��z�06x�3xȹ��/L���,�~ȇ�$�:�C~QʛoXo��8{�~4�ɞ���G|�=��k����G����?��!�<펜��վCo��N��9��c?{�wg�S�V�{�����׽{������^W>r�G?XK���xc��L.���\���:7	O��N�^vH_�){>쪻�~�O��r�Ϳ�O�)_%����$睞9
�<�/�B��<�s��W���藣���*{�z��!����,��)����|��=���U9"�=<��D��pƙ�+�����#{���)2���c�*�$�"^h*�쨙/��:p#�_ջ����:R��3���[N�wx�8�Ѿ�>���ywz��fB����V�5��/�B��ʸ9�3v��+G��<����L_C'����"��j�o�\�'1FL�Ϳ��ad�<˧�c?��'}i)�j_���Ox⫡���m~s���.����#��t���ӳڳCې ��
O%�;��a\�ޜ冫�u 9�k\��v��3O��m�?�^�4nY���+z�+{}M�/�
��/^�T����f/z����$G�wo.�PP>�Qa��'zj_&P��
O���z빬u��Bv���=�6zn�̙����e%m&-zҋU&x�Zp�/��)�8�zB9
e�@N�SW,�i�vy{���~��}l���f��L�׹ґ�Þ�9K�+.�Ï���e�哾�^�+���r�NU<�ޢ�K`EO~����%��5G��>��5!!�u���^FE5�W�{�M(�X����9_���l'}U�9F��������G�����$}Y�B�%7_Y������:�%�)�]!U��Q����i〲�}��'�'�ĤwXzZ�AO�0-�$=tf��s������:��C]�'�^_˕,����q*�O�)xv�`uwo\����
��`+��uO��Y�]��y��H���t��Ư\^�2\z�=����~x������3�ip���ܨ><�x�L�u��7��΢�����)w�It�N�].�ԧl}����~\���uuM�39���Fu������o�������KKx��d�=�V�)M��O�ȯ��Ǣ�K���=�y
�["���m�4"� ;of�Pu��ɦ��p%�Z<<����%?ͮ&���_!�ܦ($��1At���r�5<uy�B�ʞ����ދ��j��C_ܨd����O猁���`���Ʈy=9L=���2C�x� D]�n\�*QV?�l$7�Cm�V;`�$ax���J%=K?��+"��7�I��x�b��%�'��6J^���Ȣ ��g�ܳ�����o�^F_#��7e?#�ɡ�U����
n��rw�qL>�Zo�����^r�8ok�g�9ߟZ෸���S�N{�x/���C�A_g���	����������A�Hv��E�+`Ϻ��_���؝}'�f/�3p㽮	����,K�J�`ݴսLNo�+;�-9�QyK�<�*8�b���n�d�`�+�����.���|��xiӂ��&/�}�S���������ok�iߣ�rhC�ݦ��f�у�5^*�cOw{yV�`x�d/_W^�N��එ��f�K_�E�
/Q�k�Rnc����iYi�xepjO	d��P��>=Oo�$���|%�m�����k/rP�eqx6����d�<�VZ��+��R^i΅g3$�eyTH	����
���@���z6.x��JY����z$uؗ��{Y:�~���.c�L�jc���|��)+�rPj_.�",���f��~����{�I�5�Q����/��Ʋ���*eH7	Xx��od~�@�
A'��X�bB�g�k�PŸ<G��qژ4��Q5�Y�}|!�y�����O3y��CRf��V����D�П=�lC�$t&����W��ْ4�ڇzs�_�9����s��}���e�Ǿ��=e�x����H�Z�r%zl���I�#7pt&O�?���^�&��ݟ!��N��W��7r�w9�� �a��O����5C����. S���x��+iC�x�(}e�C��C�D�F)��[F�ѳ�z�<�x(�l���(ݧ�9}���*�?��l�ݒ��RzϞ1kQ���Uu*"u��<��}mokܵ2�Pm3���'�r���7��牞���8�\�ƶ�uV-v}��W�'���=�T>=�o[%[�%��y�Qx�����\���Ѿ&�{�l~x��Ƃg3�X�;��n?��Ǭ��W����Jn��fϝxh%B��)�I���ZX$�/�ȉ�/LEO��1/J��4�jM��ҳ�O�R�n��k?x71G���'{<��)�=�@�ϫ�sJ� ����Չ�]�r�nn[�$���^��D���>���X��K�Ք/�%��9z�b�Aj�\ft|�?�P֊o���n��r���=C���i��v��rP<�?�a7#C�����,4���ZƬeC=ڃ�d��m9[����-z���'���Y����&���|�����AM+�F�.�������CV����t���/��\I'��e��.z��ß��_@9#I����/ip֤:�bH�=�k�|���lϟ�<���Wk���?뫯�g�f�_�g͢��?
Q�~ai�*9o�*�q��mO�ݥ/̎��3�3���v}e'{��%i���� ��0��`����豓�Z^��
����i1:{�uc�?ɇƞ;-�=o��;&�F�Spۮ�e�)�ŗcK&�]��/M_+��F��N`ȕ	��'x�-xM��D�6��/���tvk�{si��RE���g=�st�݁g�+��毿��,~��y[�;r�H��uһw����Ov��
���؝I_��Z$Z�z�=6r(Wf�#Mݔ|E���j^�_0��qz�T�s�'D�j3�����o�4#$��/f���3}mq_�nǞ�O�r������")<�Y��X�2�f�T*��e��<�!�h����T��!��C�^]Y/���K�/ij���?����;:�$���L�~}��1�r8���n> }1�r&ʑ�x �>}�gx�OvX?H�#��Qp��?��";l�d�'�h�&G�ٳ�=��wx�
�Wge��T_���]��/xʍ���=X���Ҟ��I��w�y�Z9Iw���D�Ĕ��|F_�<��'�;�t���U9*K�D�Ӫ}����^�P�#���v�~ķ]_厤��|����~�퍜�9�y���p�7�z>��]~�r��Iy�O~��Ξ}���arI��=G��Z���A�����Y�������L|�3��I��_���|�qǿ�r��Hk�
�2�ҤZ����ʮ���m������������臧���d��gr�g���O�ͿʵJ_=%�u���q|��go���V"N'�W��N�O��Α<��$�r{!�i������s~I�2:�s��)K��]��={o@v>�>�ri:swJ�Ӑ�w�v]H��,.�$;�wF�]����g�妌�
fo����{���S?U�P�'�m�����J_��������Ɩ���/^��y<6w�'7r���{���kƽCO��_��7}y��&�\��ՇH�&���w�J$�2nD_����{�<F��j����rn^�=zFΟ�W�3{h��|=�AH?���_ﳟ��n�zY�x�����AÂ���~����&��p�����d�ձ�}{T�����x�#��7����)FO%I�������K�GO�ܠ'��vc�o��Wz/g��s=M����'���0��c>칽�칣���j��(W&����/��S���S�������)�O��W����������_��%z�+z�����!ϳ��/���CB~�|暒��q��
=�O6C�����[Ô�sm ��CG<D?}%���T��N$���-�S��Y>Dݼ�M	�F�;�i�߃�M��ܚ�+;���j���g�d��?��}z���(}�s-�b��L�-��#����\�܅r2ʕa?��8H~W.W��\�%�y��U\U5�զG�����ZgR��)����_�$��W�=����=?a��^���13v�U����̕�|�ixM��7v�-��wJ�X�q�U�_��rj�ͬ8ƿ@�)���Q��O�u�Y�F-�A�~׻K� ��)���s9[�={�ov⍣/J�=z��#'��j��K��'��=��kKƞ�������ѓ܊�-nD�Q���{�TL�4ΤyߴDO������#��Ck�?��ڳ�r��@��Ŗ鏂����x|�S��!8z�;�{���W���.΄�C��
���—W�З�Epr�'����W?_�|��g�t�a���،~��ck}�ן�o>1�5�^z��)߬��k�=���ko><��)���W��J�����wH�1xGi|�
��_���{<��G{qr��,�����;4ߗp���D?`��1b
��y����,<SMM�l��ė��Q��{�-^�=�N�E���s��s������>����Np�����yڏ�G$%�$O,S���&χ���ގ
{��w��1��^���z�|�'ք	C��B8��W]ô��J���/�W�T0p?�-]���Teo= BX헜��F��|�W�A8�.:١C��v(�����5|P��\2<����	~����䋫]�TI�31���x|��_u��=��)��K"�3T�2q`��TNI�t��?�%�_{ �Uh!�A�Ė��g3���r����_�W��\הM�˕���k�t��ט��lM�W�w���0
�H�no�	}ͷ���VQ���M���^��S�,�y�ICO�=�v�
]��S�dW����k���9N���E0��ks)zا
�����)>��'�LH�1���E|d�	ר.�hP�K�Xv6�OlP�w'8Ð�Z�֘�ȁH)��(z6�"Lp�b�W^����
+C<}��_C�A"�����n�	�~~礴�!Sf:i��>�z)��9R���CH�vD�����px�Ԕ��+�:�Ƙ���|AO�$�—$2v�"��1 G��{�C~H��&�'�-9�LK���y��.�1D$C���˺#�;;�|��R��J����0FB���c��'<�@���2���������Ek�����Ur��8�L�ț����<�!;$�� �Z��j��yM���쭻$�p��7Ý���)���ED+ud�Cr��i3r��
���/Ѓ��<z���ُ�5�?A|���}�
w' 9���(�R+����<kr�J���3z�f�����g?\	\0~���o�
��j����x������P�b�$��{E�����S<�y�Ϩ�Ufm�W'�﵂��c��-��[q�_�z�w������{����G�U3h�Q$|ؽ�����)�_ɟ���Nk�0I�w�����:��<�T��jBZ$ç�|T�j�'z��_�z�?)���Bߚ&:9�sB�2����^�Cú��"�+�0���o|8t�I��f�^M�O���鮜�?y�˩���C/��d���Dr"&(�S�M;�`b��R���r�g��Wz	v�w0�~�?���O��@F'g��h߲{�����(�'gǒ��ꜧݝ�Ꮾ���ɣ(b~�||TJ��،��֎	���y�W��5a��E\4�K��WY����_uJ��p8�1!�<}��^`��ߋ�p�\�p�f�ּ� pu �#���H��'{�]H�h˺ӘkM�]���N|���=�+��ڷ�ػRޤW��O9(�?�vo���i=��`�
�������>�^��|w�p�k�D�n���:#��+~8��'��C?�NN�֎Eo��R���Jk����E`������&9�?��g{s\�޾�<�iRR|�n�s�nx�v���.j5���YO�JD.���-��0M��>�g~fw�p����'v��8��q2x�3����re��}���j]"�N)����{�Ez
K���%Q;c���9T ^�*�����",���Y�{��"�R�8^��ё?*���CU+(fV�^*��kM�c��Ln��
ݟ���Jj���c/�R�(��F��� �������kNJ�7������d�� �%^8�n9@�g�>��^;��ܶ}�M��ݩej\�s~����ǀ9��~�z]�.N.5���^sw�TĻJ�gU��Ý��b�xg�ΡAO $�7z�W�����	z����ގ��^���;}�xo��<k��ÿ���1��^{�?ǧK񊋾�‡���lΟ�y���鎧����{G�LN�'��t���/��
g�7t��;��;��<ڍS��	����c�讥���Cs?ї�r��u�.<��g�%��^@x��;��\�J�F��I��b�{�8%�Yz�ħ��@Z-�����gD���w���*7m<�yF�'���ק?3�Q-��rS�q���Ow{��+��cW�w[��>�Go\�e�݅t����o�甇q�	��v`���1��sze/_"QA����]���ڸ��E8�c����݊w�����O���{�$�s�b��G���4�ް����]T���<���~�x�����vn���UwV����,��x`���Zk�`��on��|p5[�Bt����ω�G�s^�đD�߿��k~x���ɪw8o���f`�i�O��|6��T ��r5�][NL���8s8/�ȓF��u8��������_T��L*'��9qF��w��x�w������4w����?OTǾ�v~��p٫����;#��D�.'fGrn;S�;��A<�Z|Mr~���Qr��=l����O�e�'쳗(���v��.����o���y���m�������sЫ4z���f~��75���DHt��X!z����
}����CzJ���4�Q�8������L�T�c|�	(L�3��~Hk��۟s�����o����>�O����9"�"'bL�3��$I�B7�����)���5��ɣ����� ��4O���OD�m�eI���X���<p�[0���$.���.!�s@zڽz��?��W*�'��k��qH��f8��C�v>� �·,#~������x��sY��stL���<g{(��c��?#�g�t�>�W@�Ж�z〹�2�0|��*C$���B;9�|��I/��||�:��v�B�жz�'�'�IN;W�O�0r�]���C��y0�/�%E��|�ar�:�Al�	s�u����:%$y&����a��"?OeN)΀�]jXoXc��6�V�jW��}~w
�?]��@�a�D�.ʟ�i� ��^�~�߲��L*������9�}��A��9�VqJ���4(�q�v祗W�(�b�>�����?7�q��9�E��]��?^\$�+;@�!�'s*?%d�0?L���}�DjT��RW~����u�?b��s�\��=aM�r82{q�}�\���T՛ki�ův��v���)'>Jv��^�N|��b/��V��S<v���/��C���C�'�^Ƀn�P��
��e��!LvW/|(E�ՋBx����.�LVN{1M��,��ýI>{�+#�M�4���!ϋO�$H�q��Ro�6�y�	�<�[��ۇ����է<��0�<���ŻZ�<��ex.'C��c]�y��"�u<>{�+]���O�x�0�iY�W�y�	����vG&��/�<������BO9� ��O�˟��vm����P��k7���佖?���-���Oj���D�cD/���?����g���W���t|p�j����t4��*]�"6��g�9'0�d/�b�3���	���-�u�ы��ž"�{4��#�zˉ����%~=Е<Do ��f��ꗜ�W��>�Fϟ��p~����-s���MB�׎{{e�tb����O�s�}����D��G�����Ow6u:8�W�!*To��K_��9��H�m{�^���ݛ��u����e(�^�'ί=b�߾�$�f��.�|d�Wt�>z�Rc�q���7�}�ri}��l;��B
�km����@R��b���w
�N�}�Y�����Ă�F�h��iԪ���$
���M�@�Kj�J��b�滄�n�0�c����}B�o[j�^y���&`��� Fo��<6j|�c|��p@��g>��f�p>�3�cĵ��[��	����9�?+z�T�O���{�pN�mMpߥ5�j�5��&u�Yv�z���q��g�pʳ6=��O�᰸Xs<?I��O>��z��*>��]����b���Q��:���]λf�=�<�|��s����gz=�i.t�v#>	�q�'�[�x��D}��U�[��%��'��a(����w\����Y\T�s���_?��o��\;��+���ۓO��?��s~r��i�-�~���hm�n'ѱ�.��W5�Og�>�\\�<���y��8?���+9�g8�kxf�>�L�۟o|"�ӟS��D�V������g���>p>(�I��9����ۜ�J�[/�ϟO{�g�B���w{��jN|�;��>��נ��g�ܵ񻮢sN���}�����/|������p����U��8���v3^��ͧ%�ar�z%d���<�=��֚�g�ޞ8�&O8��>'OLn{��}�ℛۮU:|�xW��5�?⫸�ɍ��i�?�5���n�uf�=ݩ�pv���<[#����?�'y��'��'߮�(�c�E����	=Fi�^g�f/��X�N|�����k�d8ۋ-WoN���~'��	��XDI���&�<��^�����y�Rz)��4��*�x����5y�ωO*�����^-�٥��?���^�+y����p��gߍ�|!�gqQ�'��'�|��gz��5��c'�x�������S��w����|���Y{8{���^��/�s��ˁ�^�}�L���y�i|��<�Y\�z�\��������/��x��B|�Y����<�|�9G�^�|��?�x5>��ϧ<(���ʍ�6/XN�_g�
��^�ce�|���}�-�^�=�O�'���X|g.NO{���<�~�ʼn��i��>�7�����=����ϭ�͟7�z������f"��/�;���x �)��u�/�^
ק���d��:��i=���=�5�g�����v���Oo{=�[b~�u�N������z��>�C~��
�{����~�\���%����!�#��?_��|��T���y��͟;�֯�?�|n{E��Wg����>����;h�p��I�<g�:>��h����眿��ڟ�o���k���-���������a|6/�R��7�A����a��<_��'>6�����η����Ϫ����n�O��^���FO��w��r���燝��o���ng��'z�C��O�����i���3��u�ы���t�G����\��9q8�c�+z����x�m䑳���L���g�3��i
��ǧ���?����3>��ɟ�>I�v={-���~����W�*�C�7�H
�vt?pӆ�"��Z��ɿ��<��>󉏊�+g���M��
W��a���?-N�ǧ��%y���">�`�Ӥ��v�>V}��c�'Oq�'>�
l_x��������g|z��'J���g�s�Tx~r�_|M��e���9{%��B���n|�IQE˧?�Y\��y��'��[�Z��~�����a>�\���W����g�V/zA����$w�i�W�g�O��|�wJ%C|��ﶦ�Zo?��kQF�<��}|���a����Cco_��?�|��KT�|��Kx5���>}�5=���t����D���
{�'����=����d̄�k*�B�p���d��$�+9k�R$��c���U�n�~����z�Ud�����?=�Sd�ʕ�ن��P�;�D>&������e�t�?��az.�j��[[o|D �:ᣫ�Ƒ29�j���ծ�cKJ)����C�m��CW�1���p?���
���rt�H^�<?y���?'�/�ʳ�cq=D���C|�2��71��].gJ�,#���%O١E�4���h��a� Ɍ	dM�t���f�E��!�z�FI{��$<�$_R�	dkt�9��{�w9V��d�yUj躝�&��ybGy����;R���INt��>-����Σ�D'������>�^r��>��,�/a��%�qlk�ġ����	3�`�u�3{�f��բ�ّ;L��L/���
�%�}̐��;��9(d�����ݻ?4���۳��q8
��W�e�������?teD�v���]��*�?"Q5�X)|Y��G�b�����|��9����{���/�dJ�ЗQ��wx�т�#U����_���3>���K�{��*�����$1�#I/�(/.�;ϲ�C~8�G��o��u�	 /
��J�'�
�Xq6��;'�?��Nğ�DH"uN���T���#�}xϩ��iy�nR�my�&?g8�}����x����<�.�Y5�	�aw���5�K!s�O8�c'^޾~�X|���<��t|����nu��c)� �tc�ٝ4�il)�?D_ol�{H��jm����'�TΕ��Ň<o�
]MS�ːɣ^��W��<~��\����kʦ��&'�֢G�3�]b��^���蠮����hL��BW^�ǧ=>��5}�&Z���Ι O�:�Q�*�M�����Z_>�<��U�O�ĩJ������Hn\�~@|�kN_�1�=�
�Q3�����98K)�
�λ�����<����L����-"�ೱq|�Ǿ�G�^����z��.B�jgˣ�g�d���C���^�m-��Ӝ<ʟ}�B	��?����Q�f�7��O_m�}�I�{yλoz����3|޼:�꺅����]�G�'�z]�(��77����¡|�H��I~yn?�sg/��oo��/4��+�Y|-.�3'�ܥ8�R�%?\����'������i�-��s���t�"���$d�*�k=� '|�`�g����U*����'��S+��8�ًݻk)���&�+����(�t=J��R���޾��_��}jw�#����{ƻ���Sx3ܻ	��}#1|t޼�
E��t�5t!ͼOY�~���|����^��z셎�OS�z-W�?����ۓ?c��1=;}�Ly���k°U8g_|Ƀ��9�Gs�����Z:o��>�����t�R
�9_gl�~��z��8�7���F8^���?���;��M��Q�o_��
�N��<����1�ɟ��a�<�uw}Q^uT�SZ^=�A�6P\�z�~�Z��ك�X���:?c������G��L�.�l�fv����y�O���^֘�����?�ˇ�N�)��핱Z�<?�á�<�_�x}���1?�$�����xr�v�G�<����Q��$�������NwwB�?�������·�^���cɟQ{�O�꜃�a|����X���͛�:w�,'�����y�����& ȣo�sO����x�3��F�p��g�q�����K��P6>�}�?>�z����_��×���WFTF?��k�s�)в�P���u^�ڝ�W��^O�<��s�a����S��ll"~)����/>��,kw�5���S|��^�qw0��O��7����K��o�&��v�մ�%d�k�g��qA|�;>k/��훿�w�`��.��w�����݁_^��F:��-^~>�鶇��HzL�����V���m������A�]���ނ�)�M�R������N��֨�\|��yJV���t�L��K��&��c��7�sv�<��Y;�\U�g�p�^�z�=���;94I���8]\$�X��9F���Z�����z���i�1�+�J��ȓ]j^�C|��'{.���^�w��xG���;GJ����E#��@L��ݳ�Z�镄�¤�jĄ9����ڋ��B�*nM"��ZW|�o--�7��.qx�nLD���~�;��;������/�_w�loYS��%�<��x�G�B	��K�''|���=^�?2��t@��^�g!��m�z騻C�?���wO�ਆs|ѡ���>�cL�j�¡=gɣ�a�^�s8v�V�H���E��0����y#)�M��Q��<�k���sqv!g箅0<�WΉ�C/r�n�5�d���9���
��9y�Gc8�����]��`R2|:�ް}w�O�J����2�y�	��s/���A�``U3�)<i�;Mv~��ӑg��<�7C��A$j��$O�ӳ��*?�w�2?A�H���M%�%|���0����~�Η˾����,}|8���	���bw��J���6�[�^|�ruy���;7�E���U��#gڣ)�7�(�w΄,�_��������ɟ��q:�f��1�O{�}��}�#�8C.![�' ��|�9O�a���ׇ�qe��Wk/:���{t���?�e`��΍�����lI~�<�d\Ó����o��^�֧���i�6���mj������ba��n�ӝJ���6�\���U~{��cf;|�b�v�6i��H>��ڋG/ϓ���u�wq1��?�Y^b�3�ͱ��;zZ�-_i]�UDr�$��ጞQ�~p#�"\O���y|�S�ϥ�x����((�^���H�ǿ<�^���W��	�I��P�Ww!f��t�V-T�.A$�pJ'>ݳ�޹2a�L��)偂(Q�)>ͅ�.3t||8xu}��9�o���>{���xd��U �J�o
�z�	�z���%�&�/�첽k�ٷ	��E������o��">�΄�xu�p�t�<�o>�KSϮ�#��9%x�n�W����w�	CGk�<g�_��Ν�	��t��D�\d��8!e~h�|��%�MD�_�f��Ǣ��<���
�g���J�gt��[�au���e婼��D8��'[t�3��Gv3�Ŕ������A�^Ƀ�@^NU�{e��<{�G��G�o�B�G����^��ëGϧ��*&S�G�٫���<�4|ȉ��y��e��D��|~j��������g�h�9��~��ø�:&�':����?�GL�	(�yW�v����B��s�RP�����pC����~F8�}�3�K��p0jƾ�4>,p����y��]�!�����p6˄��e��D<�Y�~ĕ���|U�C`�W7>l�DNQ���-l�>�����u�*�|L�Y����2�8���_	p�e�I�V>�4
�+��1�/�A_,\���`��՛��O����7��Nr�Pݭ���	`�ş�ȓ1d�/��4{�9�����H��m��늒��.z�j��6��N���~��9���t������:)��$�DW��Ȩ�R�Y�򟿯�`��wox�ǩZp�O:�
��/�vN�9V��PE��������������^�K� e�T�q���ٻ��+�l
y��<v_`�<_��z��W�џ<vq��Y�<���_�g��F��ʛ�@����mſ=���ݚ��B�;�?OH��c����K��I�B͎��}'O�b�pS�-=镜���������R�`Vo�f�H�h�y#( ��#a��^y ��N/�F�sc0F�P8H��i�����v~�~�/���>"y���D�C�p�op�u�s�����"'��q���0`�2>ٛr�=0ѫM��Ƀ�<����GyJݟ��X�>jߌ	�
)����o�C����e���I�X�c��Djkp~;y���}��'�	Dq��˰�W^�ߺ��]�%ҽ^~R���f��B�[n�MԺ���Ƀ���G��v�O}�O���,�K&H�k����\>ԅ�<^�4�/agzYSoO�G�%O�M������S8�R�E)\��ڶ�T�DJL@���L6ǁN�YOԶ<����fw�|����s�������IcB`���+����E{ԚC�d�����<ԡ0O8'��qv��u��W5}��τ����p�y�W/�@z/�UE��7�	�/{��K��gw�ZI#^�+_>AOz)�<�!5?>y�^e�5��O����q�'��?y�՛����^��|8s���j/䙻�.N�.��|�ħ��78|��q��<Tn���zù�¤x�^����]�}?�NHMm�҃D�������E�����T�޽�М>��E��>�<�E����>+�R��G����^�����B���$t��*��k����
b��bG���p�<{�?�Kxy/�ᯊ����;��ؽ�#����gڞB��L*��O�EM34Zl�NΟ
��:K���="���Q�^
{/r�ć=YFPv���Cݖ��pJȆ�M���M/=�����pA~�^�_���APE�#M�}�\�<��OG�F���<ם���<d��GW�;t(���~xW>��Jr$���}{Ѱ��<p|���P;sw'��L\����0��WM�}�a,���?���L��w|��%@
�Ӌ��pו���ٶc���̪�}����S�>��1��#>@�t�5����ঊ��^�	���1������S���ON>L>��Y\��������j3����k�����'��9������C�LJ�ʗ���Ņ<������1!������
F]�`~m��/�i�5��|qA�������!���^�Q>{����
��).j_O�pX���:�LH|��!?O_�6���?�q���pƿxO/8�'�~T��������Sޘ�d��	9�t�
.AO�����L�����~X�懲�};(d�v'z�U�K��c�]���Ẅ́��^���UރOٺ����r�jv',&�������s~��_�b�����ݟ�ϔvk���{L��^�*�����7t|ҋ<h�����#��+�'�}{�5���4�h�����LN�9F>�+y�q�y��ɓz������N��^����|�6}ӫ�Iٸm-�9�x�/^�x�^�i����Ç�q��Tˇ�k�‡�_���[�&F/N�a������>e�v�Xwߴ��v��и���9�Ə	�Wy#4����ޟɪ���Y���uኯ�{R�ɁM�o|�(m�4�A�_앜�k�n�ɟѵk��ӟ�k�E8wWT�բX����:��s�/<�I����=ٱ���ʟ{���+�T�wv�S�Ο^���4���n
@�j�e��ֽ4�
&�U�EOi;�:����pU���5{m1�d���/�v/�%g�ҁ�p�[�~�ɘ^����r���Rw�Ք�_��'��~��<�������'�ç3��n+m���/���#��y'�ҫ�g���W{J����Gm�0"y��ȓ�K��<{�����g?³}W*����,t��򺕦�1���9}���L���A��+{]�{�X���:���0Qq���\v��]�Y0b���0����~�3�p�������Ү
�w$y�<�/.���᤿�]�p���=�Ѳ|�>��Ӓ�Hou� �Ϻ����
x��p���aU�{�<�Eo�MPO�G�p�|�Se���O�Z��W�`N_tO{Y����ޝ�%���C~_���Cz�#y��w���6{a�>P_7��8�W|Q{8�g�����p�U�Uw$��x����n�6�VP����ߺ.����wY����yG���,̝tx�#��T]��T�e���70�'��oM�(h�CHt��u�d�P	�z��G)n0��}2]a �^�l��‡�w�Q�������R�|��]���Y�l�^��}m\�X�H�����m���O��]�3��4����K��nſ�{ʫ�g��-�a�3�$>�F�p�̭�����6���@f�=��	h�����ګ��������U��^?'��SQ��',?�#���.+�[�]�_�?}/{��Z��I��s�~�)?d/d|*v||"|���OT}��ҫ<��^�z�^T���_g	���'{���~�U�ڝ�y'>.��~��b����u��W�����UU��m��5c�<ތ�N���)�)��<'��:>�Z&U����T^�~�?�g�Y�p�J3��o7�?�n�8D�z|�m��_iE�۝�z��L������n(O���p�Z����Ձ�D	�O<M���:�zÇ?�w�|ګ>�%}%��v�(-���'9ە�^���ϭ�kW>>�"y����+���	?f��O�b�R򘈏���2��������d�p�]�}�:��&�91��?t*�w�n���<�.�vݷ��a��Oc[yL�q��l� �f��(�nr�H��H
dvWXJ����#]��}5��ϗ��`v����H��*ョ�ڟ���b�R�+>׬�
���j�K8�!.��9��v|\~�*�3�hO���z�<H�h����x~~U!op�@uSL$֯�����K[�Q��Z%�k����h�D>o�S�;_�Dh&�a3��i^i�t�UMz�5b�o
��0ad_=�ʷVaI�K�׆_F��׫C��P����Z��Ca��}��s�=}Ņ����M�V�����
�@c��݉��ym];���H��J t��V�B/!��ozć��t�����5r��no_��_���p �_�E���,���k�[[�/]5�!Z�.;A+b]���kt���ѹ������^��_��z5]��<%?��M�U�+me��%x�)�ç5W,x�z�i�Vt���ވ>�Ci	���Qs�\M��ŹQ�ᇄGU���+����鮲ݻL`�[�Y=�8>����?��EyD_qQ�r�Bvo�o�U^��A����~%�R������Ђ��z��ƹu�����"�G��G���6�98��@�����d~b���T�j��C�(ϙ�?:V���	~���jNOy�OtB�C~AN��	�
��>M\���D��5�T�Oaw�����LSΝ?HxBU��	z��)�o�����Z7���i�h����?�*�#g�/(���HɃ������@�=�שE���{�sl������^��^��XyMx��]�;>ꕌ�;{0���ݝk�J���kꇐ�ǧ��<�ӝ�V^�
:���gT���s����W�s�ħ=�m:��7�˯�s/'o���A6A���?z#�&F{�@���(<����j���g��|,�<��'|��%�'�S;�OC������o.A�Z��`���{�0�����v�_���>���^~�y|@�|���3��o�rʣ]�o�
��,JE�3��KrN�Ma�3y“_	����� ����J���X���<���{��/�Uo�$O}���H���G�/���������T>�ϟ� �u�M�Ɵ�$�ы��D}U��BE8�gq1<�#{-���d��H�&��3?<���
Y9~�i��#��}�z{�>>돵�I�%��$
��7��ރ�]Kɳ9��X^-���U	�V;ض��b��[�g~�rPƕ�������wF�=�UZd/F�*��#�fH"�W���X�E����<�8�����C_��~�I=|�5W�!}m���I
_���y:6*��t�x�}8Q��נT+h��8��Bl��#B�?�Ncѐ<����AUX�D�%B�-;/-ΞF;���/DS�����X��+:Twu���ϯ*�
s_�T�\q�Mx��ώ�k��G��)�oߟ�p��zu<��0�(!��nzE��<f��d����n2�*�w�e��~m�?�������G��$��RY��g�P�W�4>�?M������FDU`	�����L�=�?WK���y��Ӱ<e�9�I�έ<�]���gg����9{-���'p�<L��d���j����H[��•��E�G���Ct��Oh��<yf/��[��<+8>�L~|@�ȟK��Ǽ�{q>躑�����'*�m8�'�*�+?��]\t^f�{x��Lg7�
��)���3?�]g/�ҷ~�m��2b�J��� %��K�L�9�|*M.���<��^�3?!�v��4��
�*�����^~��G��]��� �+K��=��&k�,.|T��G��`���Q�{�"�#�}�c�è�4������1'Oze��<����tgq�o
��p�svϟ�oq=>�D��}W)�fy5��?p^\��������!{凵_��O�Z?�?ώn��^��b�w�{8w��n��v����}��S��9��@���Wz!����鼱����?�RP�h��c~[�s�W�upЙ�o�w���Ǽ�u0��5����9����20�Ӡ��gyiW�g�W5J|F���b�p6.�!���/��B�zkt�qjG�'wJ��^9F���VM�s��.������xbħz7���s�C�t�^(M�څ���x�	p��֚�^o��G+�*�%8]�3��U ՚U���=u���^�w:�
���Q��ֺ|p��|�����l�V��|�|��� R9r��^:����6�����5y�e�����JtNg�Q���NYTl�+�z�j��s;{*=�7����g_t]("�ћ�@t��
�
y��g!ܚ(|�Oy���ʟ�!�O���|�/���+��q!�͟s�6@p)�){���R�.�U�NY%��^�k8��<�?Tuq��,�<�{�?��3����=y\�e��ڭ�s�/��a���>�<�TH���lL�sqZ*x����}�uw��P��(X����|��xo/Z~H~v=V8��bkOϒg�w$�k{Gv'�p&9ˇ����Wt�yd�p����E1�o�Ԕ�d��f��-F/[#Gg/���8&�S�@zj��o�kà�yTV�Rx�����yy��;d�94�.��_ �'�a8���Z��ץ�FE�ќ��E%7)����I������)?�W�A՞�	����=�S)���Z�+��K��toBXJa;8{:߅[f��x�7���g��yҚ4>,��Fy�@�]�kz�����<�����<g~�<��g)>��Ur�O[0w8_��C�i�W~�1��۰�K�'�Z���8���'jzQRN*������J��v����	�s´�՜X�P-ͽ���I��D�L��'����:�`�k[�[����!�� B��#*PuHB�
ǿ����g��OR;�'�>��=,�.�`MF�F��Br�C%�ZO߇��;�^�k�|��!�=H�:�}��f�1a�C�b�(��\.��ѩ��3iv'�ǜ�7|�
��OU��S��'�~����z�~O5��<f3�M/���W�(L~����p�+�U�+�i����>�
�ן����m7� ��>�7�7��[g�4�����W���LS��|n����`�R�r�o���2�N��tn
5~y�s�^K�f�Ӵ#r�O01A�`
O�%dZx%
Կ��
���o��'k�p������2j�Þ$�ΰ����1g��N����UD�et�����9����L.������u�T]���B5:=�Wq
H�b�DW�B�p�B����W�T�T#�)��2ĕZcI�x�2zy�(����4��*O:�B^�E�"��M�4����q$�v�B�������c�����e4G�x����k���u"[G��е��x?����k�X���'�)3�'xX���2�on���w��p�n�e���٘�:L1�'�E0��s}�&΀�X��+V�ێè��jm�1)�������᜻O��m�X��@-cnn1}+�߶3�Л�JNOV��97�A?瞚�ƍ��۴�a�����k���3}=�IZL6=>�3y�'��1y�|�o�>Q'n�s��<�c�O����<���V�KR�gs��O|x>��}�C�G���^_<�f��o&Os�{�.<�'��D�}w�r�^���IWE�O�9��Ux�'k>�˚���?>酸���>�5�O����/9a�0��݃�;0��r��kv>'�����O�H��B=��,P8�'�_���⫳��K��<{09��0)>��=i��г;��xʯ|c�ӎݣ�<E_��?DI�m�)��;�s~������>�����
��$�0���k���o"������y�!����f��u^ƏO�o�Y��)�b�$�����gf����2�'z��γ���q@8/��� =���=�����<?��!�o�&|��݋���<������Xv<�y�?�S>�^�禪���3���+�?�Ӹ���<��Ε_\'O�:��.���0l��������/��??�?�?����RLNy�s�~v�����k.��+�sn}�nI���'����ԫ�o|��'>��ނ_��SO8�?�Qx|�3�����
R��������I�/A:���\��>>�p�ky���w������Qx��INOz-��q��2�琌�<p�<Yy��S/���<j���=|�#>O�R��_
���N�/�mNa��TP�<�ݓgqQ�/�ǿ<_K7{�CN_�^�'�ү��ɳ&�
��0�Q;X{���4	5>��΋�S��8�?�*����K|��#����f���3в����
��ק<g��x_��m���'?L��sv)Nç�=|6DJD~��sy5>���ݗ������3{�OŸ�p�Q*'���o�G~x�C_9�~��v������)��<���Ƴɓ�{N��?�ᅬ�<�x����Q�ϓg�<y֞>�gR9��'�_|m�i���%y����偧��W�ml|j�3�b�~o�sz��߳�Zި����+}��3�Y������*�.??�����=�m-��������vy��s��#����X���]~N����?'j��S�g|f��|��lg��Ӽ�ݗW�:�[�����s�e�~��_�{��_�?�gr*ܐa8��*�⳽�k�v��{�����Ա���?��E��y���_���Q�����ڝGv�p��k��sܽx_?3�+?9�����I���+p���N�I��#�?�6~�<���o�[y�߫w��֡�%�3Ϗ�y��y���.��^g�d�<����<���W.��G�3N�4	������7�~:OΛ����y�p8��	��o����?�G-���>ɿ���W��xN��'�Qzp>�����?4�yڽ�O<��QI���]?��X|�69W/Y��k_�=M���瑽���K����k���5�m,�<գ��<m|����^�|*O�c�K�� v~����z�8|p�g�?^���1OGP�Z~>y6��|�y*�!�מ�X�w��A:_$I�_z�>r·���ׂn8�W�6;��t^]�Εi��-t��e�*E��������)��>ߝ��L��C?}��e��=X��9U�ST~�,���i��v3>瞞��u������3�9�ǧ�?}�/~7^˾;gn����$��Q{�8������QB{r���s]ly��O��'��u=+��_�,_M��<{m3�q����<���i�j9�p|�ce�2�~v�ޔ��څ�W~rBgv����y�?�������F��3i�/��̷W8Γs�'J��sqq�}|��S�D�K��vS=���̣Άʏ���s=tM�dvG|$O~R�| >O�W�/������e|zN>�O��wy/��y�<�Y��Qr���!�3Ͷ�$a�G����I�������񏞼Pg�����K��Et�o=���C��sZft���;�A�o]���뤡x)�W̢
�=�&�#��|��>,�ߵo0��C�P����hT/�࿉q�&�c��uy���:����o��M#]
��J֧Z��٦�v#�[s�.Q�h���-T[l��:j-,�I#�+-mO��D��'�jmɁ^�f��������~{*���8B=[eT�l�\z�ޯ÷9�K����xΨ�؅A6�ه�ץ��@�t��$?&�/nWp�}0�LK}����2cwO�����<���P��"�����)N��\������:�|�����N-����z�v��N
�(w�^�x�'m�eD�/|��-�Y#����D�j��R�>4��c�G���6���z/���$Lj�X�+�����
����2tX���m����ѕ�Rv/wP�'2�z�̙h�^�Q���
kм<ל�u�M�+�?H��x����O:����\�W/^�(���>�#Ct���A��z��z�Ŀ8����C�I}��B����h�S����v�*�e�3�ϵ.?�������NH�]�3g��p�U��㠗ً�CNr��b��|�%O�ur���&��^ ���7?��?D�b�g�;ײR�;7h8x���.2a��x���fp��v��.�ll�v�qZU��G��+bR�UUMﵐӞ8����B&yT�#�<��኏z
90����ɣ<�z��@�y������ф������8���������O����g�P�f�ً�{-����$|��#�_S}���(>b�;�;��x$�z��'?��t�0���ٙ��S}8������u7J/7l�w�G�aʳc���1c
g�ó��3��u"*�6���ETv���\�����ò3�9�wN���?�HL��W/&>p`w%����5��/إ�~�O�^@���Ϧi��^��1��9C�P�����
�U��^5���'�aߚ���ty��Oz!I���*8���|��#ҫ�V��5y�p>�HI~�^�~8/������9���X�(�~��(o^��݂��Γg�����|8��#�[Dm��x�n/�b٫�k�ά������7>ٱ�`:�g�x������G���'y0ǧ#����gt^R��=�!�J���y�+ޣd�g8H\��s�S��^\�^>�>����sڝ���C��im�����^��W�Ƀ���m��Ƽ�J��N����Ν..��Q�Q\ �
�o�pƇ��?[;�8���G���"�+>�\��r}�|J������ƫ�/�:ۓ]޿�ɣ
��?Ы����6Wh�蕞��'�}���
�QmH�M˩
=��ќ��z��ů/e%�:yW�G�}ٽs>�,ޑ�ޅWz|�>�LX�����Gj#�L�L[c@|��uil|�mD/(����	g!e7�n@z]����_=�C�W/ߨ�ٜ����J�gΫx��=
��s�4���û��������*���^���Ͽ�f��j��?o^�ъ�3�s����պ��>���/^sgO?q/��Wo�Ep/Ol�}4?1�ӗC,.��O���O��|�q�:����̇���ZS�?���j�@�aY�ħ�F|�	u���N�Ϋ�f/�M���������i�
t|j��?�#.t�N{I9����+L�E.4��W�1�)���W�vYa|θH/|�O��8ŧ���U����R��KH��L\���4.�3Cwz���^�݇����#������i��=�ąպ�3?�F�<��s�8^�=��E���y�{>?����}�E�G���?�iy��CH���l����:)��l�/��}�={�'Όr������O���S�L/|����e���'�-��xY��}�Z&����ٞ��bC���i��!>�:����JU�gc�Ύg?��蝿�����'�`2�w�^���}ߞ���������/��{������������ćH�ѥ�s��p;�9��p>�b���^�cD��y]��wΌ��^�|@����f����z�	�G8o.>{!>��9�:���+x''������O�������qy�� �zk�ɳ��^�i�^��?�>䟾;'�i����Z��;�g�>��?��Y\Do�w��9"}˯�y7�o�R��=�}{?6��(���M�����^���\e��'�#��/~ÇgW��N��K�s�F�ӹ��Wz1�LJ�El;�S/��Q��8��:ۛ�1��� �|���0@�<|:_�]-�Ӹ":j&�̙�O����[~�^栘����C�wj��i��C繚�"j�&��Rv���ag �G��';��D%O|��r���q��@�?�1Թ��Ez-��r�p&�����L�O�ջ��]�+�O��^���(����U��+zq��j>!|:��>���i��i�(��W��xZ|���^��zɏ��d����s_�������w�`�pP~|�>Z�����������0��'��5����P�ħt�܅J�U�?���m�o����\t�<y([{�<�{�?}��!���)zz���wIv���VNe��Wm3�^�}��M��C��w�(���#SD��b�tQKaS:��O��\5��Z���R��҉��NȽM�����Ρ��|�E{�Z|l�Ir�S�=�S�jE�w��"D*p����h���:U�����^���y��U��a�>�:,G�m_&���v����9�Z���RL~�yH���{ ���;{�~凝m#��w"��ń"�>9��%G���pr�vw�^T�З�*���7%���)��2Ƀ��Q~{eʇ��Á��
�m� �@'?av���-�>� ��WH�h7p��k7��L��I�ο�j����a��Oٖ�Y�s�����I�����?g �
�z黽�]b��uF�vyUE U~�h�q�vLHO{�JP��*����R�8]���� �!�p��iwDgJ 6��;mM��ʪ�sG�Bx||�⫂+SZS��G�m$�p��;4?;ʢ���b��W�׵}����2_ν��}=a::Ka�ȭ�#)a���� 8O/_AM~C|����>�L�����܄�����l�2ɫ�<>�Aw>��s����f9a����l��1>���.�>��]f/�}Ѝ
)���k9g�	�?��<���<�������g���覸�Ɍ�6%�cU'�|K�A���wۃ�+縸�e{zL�Ew{-��x�d�1�Pqt�(���l� x�����&�
�t�Γ���GN�Oj5K`X@)�α��������ݬ�#ís�h������]䇋"��>?�k��+y��?����R�_RW���'��%6��Kq��G��.�y�v�k K���`���>_}��y����	���?]��n N=����:i�w�9��}��u�l����&��GAI/�X"U�9wIZ
2����6`H`x�:����{f2-pD�=IT���st
��Ku\�"�ON��3G�5�/�?�}�����o3;�$r�y6pŦ��-���m���!֢�Kq/>�j=�apW!}i봉���oe���]��cÍ�>�򕭹���Y
���xX3ӕz��^~kW#���K8u|����#u���<��w���G�.췛^7����(>�@o,��꺏]��'�?�0@g^�+� j�����[y�"g�����%����=E�ez�"������e�gӗbטD��d��9V�������	�������З<ʀS��c�={��?ի
ج�c���"���3�<B]��<�b�䁳����M����3�3:��;���f�r�?�.�^|�O�+�#r����{�=|���/�%O��??�� |�EΏ�߹�69�b|8�K��C�f/��@�4�%��'�����8
W�Q��c���S)��b,yT-L�J�@g�6L�T�
�/��������o��Ƈ�����+o�W8�}T��;|�:.��3�C�&�F��z�����n�(|����/
7#��E4R��O(k��Æ:'�Kp�y����SK\wG�?�#c�+9���>,��ߚky��
�
꒧;k�j��=?�aŸ
Ҙ�B�򹼚�I5�r�]U�3���A/ ����5��!<y�aw7�]��L&C�?9��5%�o�]�
tU�>��P\$O��K��T���+�^]�\��ù���ï�t���|�h�0�gw��G\�W~����O���j׌�>~���Y<�>���Q��<����������ޛA������U���9��������)ϵ�3{�M���:>���i����b�,\Ӌs���>�I��T����^��|��9�g/���	���T=�Ao$Ư���g��=>�����D�����1�S8�1'��ۍI��}s�)[���&�9�^fʾ�Έ
�O!��^��_�s������y���?wؽ|؟�D>������#.��E(#���'y��;剏z��^��
:�D	�x���0T��bc�����H-y��<���́L���&�����p�?�������gv�W������	�T�<,CzM��=�z���]	TH/L�}�LJ<�'��$������F%�|��™?�O�����Bhd�<��\��?�X�Y����'�x"y��m���v����p�
�>���<��SW��)|��\�i�{�ϳ�b�w�����X���<�,��N|�g|nf O�'{=��ʻ��.��X�$��9>���ɟݳ��y~>ًj�'�.�M`}�j�����ip�O텊��c���oA�e����<�+B�����g��+����zew~�O�W�9y�'>��������J��R�){Ȳ�º��nm��k8_�����+�g/8��@��!N>o^��p7��8M=�ũ�Hz�{�$��d/���E/K�Q0��y��|Mz\M�Ƨ8E�<�;�����ƈ�,�6��zڋ0>�������Zz�ⴸ �a���ZTzȇ����Ϡ��PM�b�5y����S��G��J���?y��.O��f�ϟ�=:W)O���A,�����(�'{���;>�3�GO��_W��<�~�"���<�V~���=�$�����Q|��������|�����	��)N>ɓ�v�`�j�ޝ�F8��ʟ'g��#|�~�^����Ry{Y�g����<�'����^��^����
�9�R<��7���pB\{����qA��I��<�k���SyF�4�'>��n��p~��R~|N�O����+���e���t�}ck��b�;yt���EW�?�G��щ��;��Ĺy�z��s7����M��'�ni*���5_7���g����Q�^�O�{y�sww�rwYfk>ʛz?��{G��=>���n��{
��o�uw�k�4�V>9�΀nͲ[ȟ<�v�i��tK�qa���
��H�j������D��I��h
s��]��/��99�c�G��FN�n/�$<:��]���Q�����	�����G������Ї����3�_�7���ܝ&�"4O���
���s�O�i9$�G�3�������'삞<���}���+����K\M/������'?>�CxU\�Ç����99z��<v�?d/_���"?�|�(>��G6rju�?�����Ç�5��<��U�Á����sAW;�?71�K��t�7�䟌�^����pn������k�l���i��	3
l�������합�!<K��5�#?&�mѐO�e��X�p�
z�g����>�.H�ԍ�{��x���0��_�t�s���]T�y�lק�Tv��ʣ��]�=��X�>yn�\q��TM'����ȉ(~ۻ��K4��0݇��(�4F�+�����^�����IK~�<�y�~�At|�7f/h�[De�j"�U"u=n~���T�&�ſ;w�\{���^�ǽ'�^���|dw��
�|qZ^jEM����ˏ�I��˯�^����C��_���q�ϟ��s����a�Qܵ��0����9��/G�&g�d_���;��D�y�s8g/�vw����@�O�Gv�?��7���&��镽���n��/J����Uw�%?��������M/.� �v�;=����Y$z��-|��dw��Ƀn$�I��z����k���EϷkm>�N���\~����^�/��W&��>I�H�a3y���0��c���>����LN�'�}|��ݒ^�@�����sڤU��u���I:��>�^�V��[`a��2��O�_�R�v�rh��y���ׅ�����;�|�<z�~��
4^�y�?��ν�{��_�V|�0�^���s��^��	��x��Njv��W�b���ޑ��y.>�S�����d�nb�ά���]��g8G�۽�����a�w��ES��~]�U�R盖�1~�돶,ުH������N9�����T�FFӆ�n�������7��ú�9����K�<:�f2:���dt��bn������"9��#��5Z�*yn�l�ɏ��M�����w��H0�3�ϱ!��[Dp��Š�/��;#T��"�jO?Ȩ�{<^����\ĵm�@}�ʹ/�%_k���j�Z���7��E��܊�l�KF���r�/Ř������{3��C^$-^#c�?n\^�b<_]=x���x<�)�ps›u!�7-;���������f�� �Ι��o��W-�m
�~P���8\#
�%�ޛ!�|eqU����w���1�J�(L�����7�'8H��;'Ca�u�Þ���z�T��z[�Q�OЕ�m����d��I����_�z��CgUÍ���=ctz9yKiL��᧚L�Ua,C/J���LI��/��C_F8"�
Ȏ`���7�%[_�wV?�B�s-��w���v�i��y/|��BN�;��W���z��	�K$-.������U�G��7�|�(��1���FJ��zdq�ޏH0w�Ny���E�4���
�x,>����e��{��8���HO_�
�G$�9�t`zUW{�N��t�m�������n�1|���]>��R��^0g���>��I/N[|��7����ׯ߲;�ݝ���%5��`V��v�*mrb�'�\\�K�l�
�ǟ���Ɣ�������R~�W��g�7 
g�E�"ޗ�L�#,���ޙ̜��,B@\���?_�z������1a��C����|�+��7|�.����_Dz���g����6�����O#���_��+��Ds|~hڑ�h"tBȃ�H��0?��ʟ��'>��5BrvNX��^��7y�6'K��~�MCdt��b%�0{������5zs��G�{�?��?�'��
��
z*܌���(|�Oy�=�p��{�u�����љ&>�۞��B��Syzۣ�Z|K�ph��H/_��*���p=q���Y|��MBvwr����W�t;�<�`+ur��8������<��^ƽ�a3��:x���������hv�����'��5��^�a��K���+�N��]F,^���B��g�?��+���?�>y髆��S\�o�C�nz5��Z'>��l���<�~U\�
>
�����3�av�����ѵw\�^�F�6C��������H�pV��Oz��6�3]�'z�SB {-o�t�9���"��WO�ʫ��N|W�}���LJ3�S�%#��q��
~�u��]o���y$Oy�3g���f�Ƨ�*�؈m�0�Sߠ#��/ּJK��L?�+��?�#�]{G䟟�х>�85^����`l���O��G��t#,y�.�?�-�º�ć�T ��r�c��A��Y]Ty�O���>����TF&�<�A�3y�����A�808�X7y���s��z�F����}Wٵ�����x螺���'s���M� >��o&C�
�øTֵTC�gv�ʫ懧8w~�_��tP>?������&Or"�����P��|���Q~�^���fk/�����|�ǧv��H�5y�?ݡy�gz5�-.�{��a��x����D��a8��S���8ϟ�$>*�_������
�����n��ݟ�������7¡sn�G�@���c��>��lq��ũ��G����懋/��=�2�Su�����?O�	��=��Q���<��Ƨ�s�w�k����|��s��:�z��1&F��p^|���{	�^�VAZ�8q^{���z�!y�ug����\�ɓ��~��'z��B]���k�j����?ϖ�<�p�,ϟ��G�����w�;$��(>�y屵;p�D�8�K�9??�����_�O�ڞ�Żb{k\��?��Jg��)�J���݇�Q�p�ۓOɿ����O8�,��^�Ӹ	tJ�==�e���q�kk�f��P��+�ƹd�%O����z���Oy��O��S{�Oq~�O��#>�g�$^*��Ὕ���g������?������{����X�|�2�.|��=?���[�����Oz��N��ңQ�ǿn����D�c��u^)��Q��z��5��53���_���G~�������ܚ����M�����P~�avG���A�EN����}�3ۻ�;y~��������H	����{c1GQ&d(u��^�[�w7���|q����g��c���E���}8�ψʷתs|�z�����ۛ�q�{y��u.�������B��?�ׯ�_���?������޵؉��ɿ�1{�FmO��
���W{��O�{:�+y�S�C�����O����=�_\����?^q��3��ɟ�3���Ց�Z����g|�����΋����o�;n��^�����0aÑ,H��
�Z���<��5��^�zm�&�����ʨ|�Ǟ^7|Lp9DI)e�)��W��녱�y
�5��BmӢ�CC��^���#6�Z�\����;Q��TIo����@���Dp�ΗΡ�X��W�R���+�v���UZ�iEr���L�!�چK�ބD�p�|����A������l�B{(=��">�
��2v�3�g����|�a���Gd�Νj��K��{J�}���x�5�ٗ��;6r��s�[yb|:�\I����;�x|�aUҹk�B���K�JND�F���}��y�'l�`8��;��%�l���>���#(�_h�z�
]��6B����p��|vF�V՝S�;�ʓ� &^�L�(��yM��e����	+v��7�����#{����(̵p_{4&�i�V�Q��Z��W���Og䣟vG�2be���������5�'�|���`��dxϧ�?����n��ugs}�(N�\�M:�������;�yI^>�|���/>����_}�����_����w
cb�K�����2��>�Ek�i��&��szEߞ�)�n���t�|����Ēת���W&�l����-���b��ӕ:����<�LO���%�-+���w��&��s�&��ԧi�����z�?�Z�_.����:��	K�����'��E�⓾װ�?����B��?�'Φ���p��?���I�'�\vk�JT�,����%�����ykF�,$�����!��zM�\{~�=Hѿ�>�_~չ���tRr&�5����nT�oyχ���+����m7�<�4��Y.Aie7�-�\~bF7��|��܇6�^�7dP
Su��$�Y��U�]y��)i�_q4����<o_�%k͌�x;}st|2����W�ٞ#\�!������r��^����-�k�"���i�a
;<���C�Wm9E�
�C��eO�N
��tl+�)��5>�G��6�]�^������
��Q2>;��^��>�?�9�?c���l�q�G��@�Z���W{���m�̒�P�ԩ@
J�ݝ�]��q���%�ש��ē�ߔS-�i/����J�j �ב��S�ͭ)<IS/:�sz�-��]byЗ�pvBz)?���L��x(_i#p<�����d��j�x�o���&�.�$�s
S�fFV����G���\�c��'9|ޓ�W�q��-�ϟ0M�aN'�*�ڑέ����3;��9�!=M���H���-�z�?�2�҇�_�!���ꌽ�h����)]��[ɟs�J�g�0v�T�G?�7Z�����TM;�>����9ҋ��� ��<�?�j�����;9�V�֙��-ҳWv�玈�݃r���'���W�����������u��4�=��;���W$�UT�sz�x�W�.<k����'�ɷʑ7�\yF�AN~�����G9M�yu�׌��^S���>y��
��P��8mT�5o���xV��
�SΊg���+<�p�sFN��>+�H;cw�F��Oz�^(������X9)���q��������y��bœ��('~l7Ng;�wV��Wrf��Wq��(���!?ϸ��μa���c��z�q*���Vi����Ad�_�g͇�/�S+��G�u@T�:�?��Y��x����T�e�G����Ӡ�v��M>�sX�W��x�?��!o���?�<�q����x��3/uL�j�5�ز�az��yR���^�O�$'<�Y��Or�Pq!��9�״{�C�C�'�>��l�<'�M9j?-�&�L��w�К7&�ݿ���o�������?�ox>�L9o��ħx�g�p�f~�}ͫ�8*~���^���9�3�%>޼n��s�#����9�畟���?����g/y^�Qk\؞?�`w#���s�z����_�����e����g��g�yx��,��R��7����3v�}�C����_����Ο�<k����`����y�|~�7�)./�v��u�y��v{N�c������б�jG>o?/O��b/f�ݣB\��j��Ҩ����ݳ���'�:g���am��3������������a��x���O�V{�o���Wӟ�Q։���^���}��:�yx�X���yȉ'~�S�5�<ӄ���7/��&��|�j�9����'-��_�SNy�`/�ׅ^��șk���j�~��IK~���*g�g[��^z��@<H���_!_q���+�GT�;�=���"��M?��C�H߉�c�ޞ���b_�f����<[�ʹ��?�����}ʼ/�ג�n��=Hy�yu��n����1�B�x����u�Wٛ<@������T�&�����L����C�H/Ϧ\�����{4W����3�O���oZ��h|:3��~K<�O�M{u�L��$�y�,H?�2�y�\"?�)~���^.�gN�q�o�k��g�O~�ٵ_ڱ�!�����o�,�xF��
�{�x�����sx&�ȑ^�|��e��8��&�?��Pv��9Afzٍ�?Z*<+?��@{��'�1��34���U��fT���"i�?�.��y#��t*?�L��=��O.'��{�g�Y���4�r�lL9�,���gׅ��� ?�����x'�����XP�V9!�]�S/q�Ǒ���xݼ���O���'�b����df~�g���̼���♶���&���ruO��٫C���[�m���
�'lj���M;w*ܓ����KhC�����bI6�
�~��a���Y_ù���d˾{�y�����w1�j<oTsmxe��q��v�ߥ>����.���?Ҋ��������1�t��w�2lK�4���p��c5r�5��l��W��EA�/��>�#�8��/���`T�>���|�	��Y��-b����?=�z0���
��C�YB�R�Yb�}}��
,`�K�j���lR���Y��`�&6���?�s�Wؓv��A5z1�g]6��Բluh�ﲺ �P���e�Ϭg4cܢ��˂����g���un��װ�D qB�1Qo���.�a������~���ԗUQ��<5B��}?zYwgO�8=��� ΰ�#����^����Ẍ���dm!��mߣ/�uyf�Q�����u�o=�b5�/��kc)i����ϱn]�i�����{@2�乯&&Tp8�X�!�G�<�&��堔#�����V��h����7
�����e\�܁��g�B9�3f`��w�*2�;Nͺ���(J��Vg�������T�$���× D�K]�^�:^�v�3�,���
A���x�}�u�D&K�Q��~X1v~�y\����B?����c���]/;�?�俛���1�G���3eLO�5�\���܃�ӎ���/��纎:�sC�6.�'d�x���8��F�B\a�)t��������ץ���p1�m���Ece⚥��őa��c"^��襓x]xƾ�_�#�i�|O����%�{6݄�R�]x����ǻ�[�K�Py7����&�?��.��.��?n����$�W�x����7���Y��_�77����2�ݧe����5qG?씽�#ǻ<���佩�����K!�aу��\����
����'���I���w�����5x&����Ń5⡑bzy�/-��ar��g�-�?�v��|���?D�^ʁw���(���ǀ�a�<[�F<���#?��ͽ�
���0��׫?;�ɟ�g#������
y�x
ԋ<�}�~>��67��%�y�8U/�8��?�_��i�j�S/?��)���қ�r��#���^��P�?�T���/�G�����qZ���7B�U�+����=��=s���)N��ß��O��߁�����څ���r��ˑ�>�N�:�Ev��yq��NM~8^���<l�i���8%�u��>��X������:%
��CR���l�8�PɁ�G�l��5��¬w_�|���9�g�͐?g�v{{�sS�=N������l{1i���?��i?m�ϟ��܋ݷ��}��O~���^[��β�^�q�H싾���~�|�q�ꅵon>z���'9�S�����.�Q'<�Ћ�JO����?�����x��^�������3zݪ���~Җ �}���������ŀ�a��I��H�ϦU�Zo��7%���ܱ<cw��g��]7F��
�~���'>�����Y`�����,�c�㝯��V�ۛ;��:<�#g�)��x�<�3B��U�
�;�b���U�X'��i�E�>�3r�C��9#���L9�A��5�(����^O��Żr£^9����g�@�p�Ņ�V��{r:�h�/�]��p�^ۋw{7�80�xA����B�zq��M�����}��c�����k��{�(g�7�"�8z}C���xi�b�8��#�3����<v�0�09�؝pz�?�G��c|����w�������H|�g'�)�������&(�ڞ?�?�<�~�@D�G<K��Γ���g�<|��z�'z,���q_�������v��}�<��1��G{��yO� g�S��{�*�܅%E�A�+}�(g�@E���q]����u�*�GU�F����9�7yN_͸�r�srֵ�L%�1�5�8Rn\�qW�}����~�J����r{KԴ�[xҋ���s
��c�ڭ�?��*'~r��Z�t>�臓���ܖ��.>5+��-�OBalI]
����A�:_Y=��7,������7��O�aڝo{���:.|{qE�m|�h�.��u�b�Ѻ����:!Ǻ�|��k{Y����z�������b})�p��������dt��hG>�^�Y�]<��yb�<Ϟ�!�;y�6�D�o��qVԺ8��sL&~Z�O#�?�"?\���
�
��wW��f������Bi�4�q���A�5"��?�ɟ����#��,^��ٵ��N{cq��F\Xy�ɭԍpM������٫v��v�O��l풟��j����^�����uM|��uQ!��3��G{_�sz�+N��7��8�8��i��坫w"�sG7ޓC#�s�Z̫��W��ǃ�iy!��w�z����� ��x�(�NF��=�A���|z�_�̇�G�춋�)z��y��s,9��u����<�F?A/Kz�r&DQ����;.�u�v�`�?�g�p�)L��?���b\���Җ%K������xv�}�,[�}~�_�=ZJ�ǬّQ,��7RE���yV�����~�o��[�^�R����}�9	v�v&'C�Y�kG��I���t�yj�~� ��L~[�k� n�Ú�B�������^*�Ƨ7��$:�`t�t��/�n	09pn�S�@����ܨsy��":'C���������0�����S�+�
�좿���+��B����N���?@��B��G�P�7?�ε٣� ڮZ�m��ozA��`�ωc�{}�S{Dl�ٶ�Q��2�P�����?���|����h�y���Gyv���_��U�+9+��7�j��*埼:��xXd�hs�����n��Db��J��@�Q�z�Gke��5��!>��v���m���i���{|���y	CW�
#'�
��1�]��U{�
����3������C�O��_�pៜ;��9غ3h�;�[Dw|C�0<���%�^m�Њ����4T�)�8�%�v/ۢ��h���F�ar����w���b�i+\�+|F��y����#OtUX�r�I�~��G��K)J@ގ�<p(亝��'�Xv�.��]
�ˇ���R��oO�Υ��G�uX�	�z�j��9���@Vo嗜`~�^/|��^�������������<�z�]�TD����i7|���~�~|�\z�m�#�9�Sy^�!���Fw:_��篓���ԁ#u%
����|�+���~1|������ʝ�J[�pO��t���MQ��Q��|��~V���_�G\��|,��t��n��4�u�v��O!�Ł�W~��Z���\�7��=��d�^�V�(MlH���K�Fp�rjq͡�_�9�5؇�?���9Ͼ���OVW��C?n�?��)}���&}X޹~�w��2��,>���y�N��Ub��B5�l�^؛\���a5�k2re�c޾|s��b��x��[둁BS�z�kֆt�@�����9����H�X�z�c�Ltm ���~wr��e#�C�X�Ur�H+oc����F;�a��d�6f�bnm"�1�zU���o���_���1�#NƇ�}�Y|V�9>�Z>��z[��O�#����A��E'��;^�:$�o�_������ok�]�=�ø��?����fH����3��^���_y0��d���g�Е'O|�vύ�'|\"ws�s986\��ZMs!�O|���%�C��ѳ�-{��o앜۾�5�g����&{m�G�g��A�3>�̷<��3��"�+>䔈�M3�7�p_>�'d����]*�:>��O���x��Q>%��ç���i�$������ޜ�3�ԋ�^��Or�ɓ�?�<����~�@k����v��]���mc�_;�,\0�o��9�y�w�����k��#z�o�D�z�����U�Q|D��/�ҋ
)y���Q�����W9R�?�y��y��AÌʷ�q��Z����g�v�O^���|�+�L3�7���
�CYU��}N�+�$�N1>��I/|��Z�8#'��~z�C|��~%ωO�ŭ�?z�7�\z]ߐs�a�p��c<T�a^{�n���$��`����R�'��?y���`ѳ�D��]>I/��>��#ϋ��O�Zv�0�p�|�_r��lܲ;c�cʫ7yZ�z�����G���_��L���?|6��������o���j��W�>�8zݟ�����>�ɹ��Jgʃ�1���'��w�ɂ��^K��{i�a�_��W�9|���ep��5�� �����='�>��ɟA����p^N���7���y�=|�7��|�s�������?�>�,���?�<V����[�����ݟ�a+��^o�\��?�e�@���E��{�'{ż���~�ӱ��	��#ϗϟ��0n����ƓO���O�">%��2.�H���v�Lph�;@H}�����s:�N�@�Ƀ���9�%��<�EԾ�y�I`��<����_���vv7
���k��Q��$zz)��'�Ͼ~}�y��z�tE�+��\�O����u|J~N��^�A7)�'y>}ʟ�}/>�U��k�9�Q�<蕷��������+>z����?�5��(,ݡq�v���z
��3��-�~�����nG�����	���38���c��h��e�}i�������'Ƀ�_~Ryv�?�u���&���<�!�)����O�l�v�c)2�g�BW>{�٘����|��c�f���a|��˗��~����[t�����٫�{�*>���.0Ǫ��r�j�g{���z��	��Q��s��ϑO�x·?��}���3���m�E$�����>&K�<�/7�0�$O��'���Kc�}�+���X����y�L����:+��^��
#����x޲�g�y^��~�g�!ϋ��R�(���9�k�pɃ^J��|�;M�^+{���U�߾�Ƈ�9}�NqcN�(���>Tn���u�%n��~9>�E)���z�S{/>{�b�r��}�G��d��Q�<�<�29����D*�<�w&�m˳�'(�A�!�������>	+�>q~8Y��~v��%��	�=�b�sf|8/Z�"O���G�i_���=� ���pV^�>��n��'|�_�������9? ?<�{qL�i_�yz��q��'�#>�#�ƽ�y�?��9O���x�K�ټ�?�|L�+�_���??��_���Ǚ��x�g�9{�?O��|���9�ٳR>>���?9�^�:8�����ONU�
�?��@�<���
6�� b;yH���B�ϓ�>���^����M��3iI�_yӐ�%�ܑ��m��7 ��_y�9H/��/��'}�K�B��Η����Aʁ�����]�O�Q�||��3|x[(�
�9���5�	����}��[q�+���L���CY*��O�j4�A��M.E|��mȾ���
ώ
�9���Z�n[{��0w{)ߕR�lg�SCE/��?�g�y��K��?�x�/�A�L���?���>�,}7n�Ut�pV/�ヨ<bw6�z��%��GON|П��	�z���=�c��F-ǰ7y�N��v�9zݦc�I�Ú�����Ŏr�j/蠛;Y�L>����3q�g8��|�s����䤗�p��E����!g�+�Z_��&���?�0G�m��w8kw�[�����P+�.�پ%���ԾC� ���-��(���M�wg�tD���Ŀ|[���ǘw�����\r�: %�B�8���W�n�ovI_�h����O���um��b���G�d�ľ�W�ћ���y�t���s�d8�����W2zA� й����
`�t8�Ư��+�~��h�s�N��>�#�)WF�p��~����;gB:��Z��O��C�[kY�'�&^u��<��G��?/7�5�f�ع&�7��.N.>��0(f��v�X�<�3�������O*7��΁��R�d��y�O	��~���Kq����a��P������wrI�G�֎�=9�^�'�rr����ɾ#a@�3��c���GbS�\ƿx8�T�
g�5��ԧ=��!�+�s�S�d髽�?*Ou�:���r���׎���O�k�kGy��
矃ϲ���1�~�ˏ��5.�N~	
��n�i��B�c�8�6�唄�k\�wǍ��}����[���I�<��o_Ɵ0�G�Wp�
[~|6�o��LL�T���S����3]ȏ��~(7���<��':� ���E�|*��M2?�v	~�.-�|��E��cP����w?LyN\&
}}���~ɾK�r����7��GO_8�R�G�}�s�yU{�\�r20oa}��'��y���������8��_n�>y@7�׮�!�C��5[�M�W����^Vc�.*۾������k�%�O��':&�e���_�?���.�c��Ϭ��G"=<Ё�v���I�;5h��wV�8���[�}��7?~�O���կ��v�G!H���ҹz�߸�����jG���|�?��']���կ7�,W�����<�ϙ�J�?|6���^P�Z������C[��9�uz���~˃(e���7����]�s�^�:{�?ۢ��g�p��%��l��A�&ڱ��t8��'��G����#�Ff�����c��W���oޔ�JË�מ��7���R��j/��ą�8H�r��b=���)y8,X_�V`��4���sK��U�j����>~"�ƞ�E�y���z�%�/��S�u�g�w�����jI�-���}!uE������Z��唘�z�����ā���
'h�ս�7�!�wC�6�a�BN��-��.�
��Ϫ8��R��� �F9.;qи����ް��z�!0��iQ�X)��m4�/�����5Q]@ ��o�~L�o9�_t]��@L��X\������'�g���#��R���q��r�{Ѐ�1��~�g��x�B��c9U���{����f/-
�E��N�K8�6����	y���.���A�W�b��g�}
�
��o���j�hh�I��-�v��>�!�ߣc�w�Ţ�>�E�s�Eat�O���ֹ>b�*8)B�j��k�+4���@Iq
�Ƀ��u9g�nE/�ޚ�1٢/���JG���3?���`�Zx���!�!��r�w�ϊ\�>�'��,�[���ui,�����_��`N7>����?���#g�|��"?;rQ���J�%���d��Tӿ%O�����}�^E��ՑG{l�	?t|��n~m�ZӘ���u|���\�WoK?�����څ�=y�I��ޘ̫�0?<�9ӓG������<�x���Ǽ�'�vM_)��b>�'�Z�W��^�"�S�-|���z��^�Ɓ{�"�^�q(���،��⤵��A|b/��A��@���LL����f������<��S�7��.�<�!?>���w�^o�|���<��ы� b������W��G�5(�|�}�7ª�+�ᓽ�d���<��Uz��(��y4$�|�73�����=���pB.Ԋ�������>�վ��l���)��B':"�C�v!|��g��S>ki�	�Gy�+y�UM��=>���e�v���7�h�[��g��_��%���Gp�](��r\Ľ�0>�.��ڃ3&L�<�şۻ�W�Aw<{v�}f��9?���o�]�Y#�G��@�<�R��oͺ��h�.�v��	���^��*�o�h�Da�f����@_�3��^��^띊�����K�7�6�v��x�����y�gѷ7o�fء����_��~u/��8oq�ˣ;�	Ξr)j)U�p��?&=��[3�_��3zC�f���L��g���:1�<g���=Ž)��zO�G+��ׇ�߳{{���O8w!�5~fLN�p���'y�|��]$�l]�1�r���c֣ ";n�o�����<8�	������C���� tw�_��<n�%2?�Ao
���Y!՘�$���+:��I<�%�?��{G�n�VӘ�F h��ٝ<�+�F�z��1*�
}�uo�cU����^�r�^�n?_�Z��F��W�������9��#�?���?���{t��!��;q�/�'Oا���<��<�Uz���v��m>o�g�R�=Ƨx�<��Z(>]�E��t�/>�O����I/Uħt�i�ً�Q�D(�؛�r��ʼ�'8�^�N�;���<b��A�[{G�_���0������qc�!8����o\gQ�?y�q]�D|b�V=��~⏀z���a�t�g�!���o���3�;�׽�_;�q����� y6��Ç��X}��y�^�#����3�Ct�]�>$���ܱ���8��T�5���ʤ�'y|�Ⱦ~�oჿvM8��L����+���<�w��k�EN��s<�#>�U�@[�Ŝ^��^��q�q|X�s�;&ό7j����s����'���W���q�]�pƫ��?����X�w�/p�{���r��Aϴ/��>��|���_q�L|>�ǎ�R��|���L��?��g����0�ϼiƇ��8�W��L�昜c��WrF���
��p�F�Ƀ�<i1�ˈ:�����~���s���|Y����(���E�'|���f<�^�v�bli�p-�n��}o���n�u�s�z�����L��CCt��ο<�����W�0�7��tM�!��l���%�A$�e/�*oj��:Y�D�$�,�<��O��s.t�|�
�ߞ��:����ȣ��P�w�Ħ�����\%��z��9��kbbh��Z�U$�SS�@�
�g�E��~�������ez+��{~�q�xqw�27�'
����Aҋ�PF�z;_�<;��n�gDS��N������ɚ�9
����u瘲��]��#|�i�q�1�s@rމ�;np����^^�rN�d�>�	�2=̔�y�8w�
}W|�3�&���&��p�����?������||��G����t/�k-�Yv7�ϟ�4��9��":>�M{7>n|&gEaz����?�����ruk ��"�Kzq�V�Ѵ��]����C%i�*`��y��;X|v#r!�o�Ӟ�	Fz��AT��&��F/|�3y8b��w���,�¹I:�"��"LN��[ 4������ ��B_�s�&L|�|��1�!{i��R~2�/>��\�Al|ϢǕ�_���]��ij���;�1�o�t�U���.q�ۮw���TZ�k�t���ʘ�I�ϴ�=�:y4mkM
N�e���l[�	���.�|����>����YD��N�k���I������r�Ү����w��e0�U������*���"S�[�Ez�!�^�����.�Q�K/l����b�����[�͇��'rD�3��/��������3(Å�ϖ�nÁ�7���M���L|�ڗ���dr��1z�w�^���Ѿ�c�@��>D�9(��2V��B��!C<m����ڗb����s���0��@2L�³Y/��ݞ�v�O�[�z�1iv�wF/Ʋ��sn���:��,�&�\��p���4N���W��r#���R���l�~�����|;y\Z�~~>��:/D7�Rq��
|:��sqv�[�Aa�b�%�7%!�s���7������i��*�8+�*��k��7+�94w�g��N��G!�b���Kt�տ$OAC�;��~*�����!5:�K��'$�ǂ)z���Iy{T�H\0��#��6��Ф#�~sE��×�s�_�-y�l|`��=��<B��B쒾�_�6>:$�)?���0�@��k���`�+2��l��}~ɯ��nyfu�;:�<���(�ߏ8����ߖ����7Q����=�=���oyޡ4C�+g�ξ�'}���w��63=��R/��{|�qV_�+�[�_B~N��!��MpU�Eim������X��%�i��^�]y�� m��a@q��y�R���_ΐ��p
$I���.�U��g���>{e��Ё�?�%�J`�8�<'t������ޞ�K n�|nҡ��������{bqso�
c?����w��=��}�bл��� �TQT/��ͅ��j��b��F�4z{qI&1�Gh�;��F�/;o�!�r��σ�7�����9P��啁�z;��J����9f�$����Ȫ�����b��	τw`���h�G�B=�]!��i�2}[H�n�*��k�#;�`'�S�ѫ��c�0y�g�.�r/F�X�:~4�U5v��*��D&Aʋ���0dc�#�2Ĝ�DνHDL���܈��0�wM��$
��>���hC��R��%*�3W0�g��wz.�ojƖGwN����V��y���U��]To�"T.dl�v�G4�0o�
�r� Z��?V~#$y�I%�vt������W]|�Is� �4�о���Z��r�7��^�rj�Q~������𝭷"~�y`����µ의�(0'��3W�M���~��{])yZ#kٚ�!�;9F��x�i|�0��o�Al�y|fAJa�¾���sŸ��ċak+��Uru*	����^㷋ϥ���L��l��=��ix-�
5��D�^��Z@*�(�Z����}����eef;��֚����3k������$�b���Wg��r�W�g���4��#�﯂W@E�Ū�M�
}">՛K�<��ۚݶ{x��s�х� �<�ʄgR3gDG�8�&|���f�p�ل�~����������Q8��S{'���I���#����z�NH#���Qi�rvl2�Y<��G\�akIM2�+�v��E��ܝk��쵀�L�����c���c>z���_|:�9&��i�5$g
�c�˝ʎy���?Z�1�ī�Oc�'ys��#�Y;~�>�C��[���f�r$�=㜳���߫N���)W� �Ag�����Q/�����3�g����
��?~4墭��?}�������N|�{�z�/�<�����Y��=�#�}Fn|@�񦗵ډ?���t}@��]��"�4�q=܇�k��8�vN����Oq@��bC!l~��1��ozO�)�i�3��x�N��5KCo��0V�&�j�����偾6�2�qĂqcv��~?cM�HȘu�Bj�d�$*m��V�ǐ���Q�r�������y� ��w��.��|�}�ٹ��|�����>m�r����'9_%�r��V�]��"J����?�;�[���'(M{�τ8�[#�.u�y��>���J/�H:��Xב��E�o��zJ7���@���heH|�<r7��
{�C˝��ʗ��XsV{��>g'�Wi�5�m�({I�����?մ������ٹA�����f���ř�˩f�T�ǧ�[�Kz�=y��z�@.];
���@w�ٙ�8���׿��!�皘�h�+��)�댴��+�>��?�Q��1�l��8�d}��<��[�_��\}���B]7��8�?�3���[;�~m��+���s9�+����}P��p*��i�ޯ3Ë>�ٓ��w
N�L�䙅�Y�?� &Ҵ���]m�z���j����R�/ث���:�;>�|�a�L�@lA�OA��<�
!Z���?�$��3q�£W{���j�A��ߏ�Uq>>1�^W;�$!���>�OƓ���<�T�'�O�?�Ct]��qovo���񱔝?�>ƥ���G�ӳ���ؽ�g��d��r��{��3}#
����|�q����y���^�sy���S�	�/�����z�Z��W:W�=?�gB���4���FTŒ���<|�?���E/��q2�O���^׹X0�/[tk-�1��;����-Y|r��w��e|~�#u���W|�i"�+?�+����^
{���k��A�]<����T7>��V�
�2J��ћ�)+/h�,�����j�I/2���W����q^�|'��vmI9?<��X,ywyql�'�����g�)9�:���D��g��W����*�av�G�g��?�R��k�F�����}Ņ�:e
�j�*w�����1�w�#��gۧ�&��80�`,����t�m�������<�o�_�IV�7����Ze��Zm9.oл��G�Ŵ�*:�ӵ�D�N�&�ߋz
�{Ay
&�}���}��DEo���s��ǫ�a���ZvqD����/�׺B�MNR��,�;�>��m��o*�[�g��L|f��%-Z'A4��z���SB�[j�Q���K���ô��1�DC���i��ȧ.,*�=Y����I|7x��>���f�gC���	?��X��}��]�w��0y�����<wֵ�~X;Ul��*�8g"|zw��V�0�'�k_޾:l(�Ω��!�U����NR��3�{oc٫�?m|���K�7�q�yn����+��g7���h>x�]���z!�I�]�s�z[�Jv��
>����$R�j�1�5��C{6�j4|�&d~��Ҵ��ڻ�����f��'�b1נ'O/�f��R�G���[ňT��:5t��Eۘ��𚩯#@zG��_T|���Wrn���%�qq��QH�	n��}�~_h�f)�m��)������u��B����m����)�6lZ��_��v+qmֱ�/Aa҃���K�B3��	P����C����K]{��JQ��]��}�w�ۨ�&{����^`����I|�g.��f�v�$A'�S�	ٹ)�!��<3�ܿ����
���{3�Hd�o8w�_�H�ݚgxw	�U�N�"�Wu�,���>m�8�Ԅ��<	�ͣ��\���Z8�g���c�C��yM���f.������K�
�o�m��V��ë9ӱ��n�:�e���~���E��[d3�� �§J_�)�����8D�9,�)L�v��"X�.<����v�O�5��'.�B�`P�]��y�e1�2��R�S��D2z��P�o�� Y\Mx�-�ڵ��Z�#|��n�ITzH���ZF<�
|U|��̵�O)�Ygz��~�V+f�9��n��4�GF�D��_�\y�w�N7O�����A�3��vQ�VJJj=��Or!#�d�ۻ@�.�0��t�	f݆A�7��/Ћ��`2v�/���r-<
��t�Y�H7�s�YW�����c_��ο�پ����)z%��'_4��(ǒ}�I5�C�srM�Aȱoȁ]�_v�+� ��ڿ��������s��b�(�:�K�+��f}rE�z6G�ؤV>y�W��0�.@��"�����Џ=�.5|&n��}M��D���u��֎�YM�DP�Y�:������V���~7y`6K7o��~9��I/��Ϣ�����]��Kzy�|�V��(��Ƕ	ъ������Z�H�*���ko�Uz먘-��7��Dz��gmȿ��Ҭ�#���8S	�� ��'�wȲ�d��E%��!<��[X:��wgӅJ�H�����^p�O;p����[t����q���`P�ǝ�v����+r..�b�#j|^��ښK�i߸a��q�No���*�ɷ�~ܗ���n�`���m�V��v�$pw���mϟX�Ծ�̻{Ƨ�Z:%(���������?�j��MT#�|N[���^/�󴚙<�~b.���!�4&��<o�����N��!�2{��j��2�,G��TU����U��Fd{��[��#�͍��w���M��c`d̉Z�E�=]	i�{{F��Ĥ5�-d��F�*�z�O��c?h�L"R}�ʕ)ڐÜG�
 ����1���N�ލ	�hv��V���<�p Z�K!jD����PX0�<��
9[`�l<_�ٞ������c
6g��_�}�ۘ��~;����B�~�K.�N:�n*R��k�wWZzA��%�[Pp�`����b$x
����}�����M~*c�
�;A
Ժ�TV��R{Y|Ε_�Ӿ�'y�%;סnC@��c.њ�u'�-/қ��Ղ[���BJ_�n`
9r�Y#�$ڗߐ�<�dz�G����_�����[\q��1zS���1�v�_e��0�G�o�w���B%��]}��j��F{b<���=�w�	��à/���X�E'|�_G�^�8�>�BH��E(d�3�NND�v�bza��M�yns��f�h>�%������'�a_�
��4��5>�������7���jDv1"h�5oA�e�{X�ŧ�k����"[��P�o��ɣs[_�Ϯ�F������,��9�����;���̢�);T���f
�[�R/���3����.W��蚞��yǜ�9d|
����O�P�Gg8��]Q�
~���3���5;{�>r~��bz���hv���=���X��ٻ8�+-˓<�{BV>y��]H�ꅏ��Z�ld�8�����'y�/��5��#{y�F|0�N�G~�v�ř���������$OGw�n��玧��x��?;K�<�r�/�-��󏏿����fn�!|j�|~7���ķ��d<�+�u��q�I�@vl�Z�_y/�<��3��n�ΑB��c�=��':2jڅ·�3���C����"�Vc��돏�]���;%��[C�E�Y;����s��]~�^��t�gO���^:I�۝kUK�m-��o�����U<�f�i��Du-��lt���{{��Q�$���rs�-��3�BN���&��OG�q�[�i��6�������w�S����=O��F�=+X����_���m�{�a=�U�ҳ|����ᘳ���D:G=��&�հ��U�w�z��Õ�#k����DEW��z���=#�w�.��X�q��|[���n�j�[e����Y$��'�QO��)c7k��/>�oΰ�h+r4K�Z��8�����oÙ�����l5�>�S��?�Ρ��)�L�?��U���C8�/w���,baS����ޞ��ǫ�N���izf����� �v�{Q?����M�{1J��W�{��}�N��u�g
�����:g���~5qF�z��"&���9eٱ &��"���^N�+���;��&|G��K$kC˓?�Wf�
0��0���<��j��A��7��@�����㳥�O�k^�L0D�Ar�;7br"ѓ��1��U?���K��M��t~Ƭ����}?�AN�UK�w>
>���Oҷ�ů:�b�:vL�m��F�3�;�8�E� r����y
���	�����aV(:n��v_�V{��}i��9{"�f��|��w�+����8ǧzˉ1�"g?AW+�����'g�9�����q�Q�G�#>���	�
��a�p'�*�v�jﷷş��5{l�R��U{|�}9��k� e���쵷~�x��Z��)H���ő�Q2�����7��ǘ �l�]�տhJ���?W;b���/+ל]����������{q�G��˹����A2��T���3��!��ѳc�G+��G �;����t- 2{�k��>�
��-:cI�࠼��v}��w�J|��k��+��9<�K��4���^�����Kγ���WfV�̺��y�ZR�����;�.O�oōc눣�!�lW��Ћ�3�ݹ��h������Ÿ�ğ�{����E����k������m�sqX{���Ϧ��\4���E�v�{������͛Ѓ"y�q�7bsӃz��!I2ArV>'T~�������9g�y�pȩ�O��H/�r�W(��wy6��_>�L��]m�tn�~|�T¹��c��>{�g|T�O��N��.�wKu���荣Zv�隆?��Cjsl:�;X��6������;E*�ث�bkS��ݳ_}��2X�+���5�$?4���>�'�^�y�'�L��ͺ�ր�(G�I_�>{���T�b��3��/&3Aہf�7��V��5�3H;���
}x�S�w���O�g�;��=�EG�]P�O3��a��X�� v>�s|����l�q��rv�]���'{-w���гW�a�l>��ev'<��+yK+I��C/��^s������l����0r�n$1�~��Q���_�ij�p�����tކ�MH��YzD�h���'�+��5��2>j&3�:䵦�;���J�D���_�,Wf�r��Ot��Rt�M�NP���(�rP�|�w[(+�q�r��[����&Ga�}0�w��<��q�R`�q�ii�c;�3Q��%25���(�ׯ��m�����_$|����;���	7J�a�/rӁ��P�R��k�����.��r���^[�վ6�N�4�9S��E�L� Ƨ����OZO��.zƝ�Z/�Aq���
�
D��<y�o��䐝��^q^�F��U�����v�]���е��]a|Z�J�5��^�Oq�8��D%�����]e��d���ex�H�Q�->�Һ��*wG���������A�:iB�A
��@/|j,gj�ʘ�k����mj ��FLN���7,I� ƶ�{rQO�ϵ��*���~)0�
�_>��
��[�ğ��})C���P*�ʽRz���M<�O�d���~���A��^�����|CT��o�[J����_Q�������O��8����ڴ'|Љ�O���-��^k�%��;���Qد+��'Ð�a"_��܄���(�����]��}-��@廅���tm��Anՙa�Ըo�����)�)�K4_"�W2𡚅��T��p.|�M|5��)L_Q����Q���Η�<r�5!�j~ع&�)�uKg���cg�Q��/����rkZ���Za�A�:tVWO+Ӻ����}��Rv�|������|�g�V2Y�%�ǟ�ڥ���<�nݷ����Bǧ�x��y��A@>�<.��DR�񤳹jg�{��]�Nj&{	�{��.�/Q����;����C�{���E�R��t1H$���Ǝ;��ߧ\4<:�m_�s9U�͟�[
{�����[��w�&W�Ǵ���/�M�(�͉�ř�G�}W^�{�Fq�-a�!>@S�8�ϾW>��8g��r���^���{1<��
+a�D������W��5���;�|n����@noZ��1"����d���m�}����Gq�L��Է�/o��.��k.D��*����[�7��N��^�vZ��v�e�0!$����#<�_��E.�<��e�G�ժ�i������{&�����Z�B\�M��� іh�p �x:��LL$�7��Ƿ���/�w>W=}7|z�FP|?���F�m�?kΉ�2�H��g�H�=�[�+�~�OQf�!mZ=��L�ϝ����=�ߴ���~~��
�OA����I�^�4���J�3$�����>���%���n����LT����6��ߺ�˦����ݑ���8����F��j-{mO7��/\��o4��z�iG��G�@/�I
 `�Mʟ����X���7��� �ڳ2�*�X�d��(/�i�{w�d(o�%:��
��~��!�;j���>-�T/��?z�nr>�U0�Th��񦼈�V��K�d��6<S��]rPwf��.��s:�
�z�?>��R~trJ$�a�ы<�9�����ޥ�-u$-�G{��x����ê9<��6�nD��Q��4۞^YV�$��P��g���.�jDPWK��\�7�Şᳯ�<��g_=bۇ�Ɲ�X��+�[A�r��H҅w"hr�(z��ې�<@�l=k[�U�K�^8�<��‡��;753��"{南porqu٤�z��f�����9՛-O>F~�w��iO�(���_7�D���_gL�;<ə��bMc�މۋ�p=���Vԫ�y7��#`㮩�éׇ��Q:|4��lϘ��
���Z+Y��[��_��3z������Ae�'jt�O�p莡�<#H;˴�{��G����UZz�O�`>�)ntj\���Nn���_�Y��'9�9>�1i�
b��J����Φ��'����'g״��pӾ�(?���N�<�ŗM�|�3>�[ə�e��k�8������j�h&�<�/G*��[k��!3�-9�'�󟑇?kzɃ�z������þ��/��Ov|��;�����o��/�?�	��=9�Ulr~hoR����%Q��՛}�;����W�I~RNU�Ң���sI8�?g�o��<�H����ˠs���^^|���(�3F�B�p�.N|�T9��`���=�g�cfκZpf�rF���lS�������-V�}϶�+A*���-Ň�.{�s�t&���'O�푈��5��#x����xj��C9��Oq̡���vM�
���=y�C�����s��+ �<Dn�
�O�/�����S��{���d;:H+gv��`�����{�����o=Ŝ�L�4���sv�^�A?ݙ�[|<�E~�0{�,�|�����ۚ�O��>�<^y|޼�1C~��Ib���f�k2j#]�b��'��-�{�w�1��Xl��;\���H9�A#���ޖ���pP���ז�.�0AO/"�&k�L\�s'�!g��$i	sʡYc~L|ȟ^�Ԧ�̥��<^�o����ȓ���EqC� ?bo�%��+?{���	>�O���{�u�ع���f0
�j����K���݄�S�p+Gʊ��U���A_��W�/ɩ0�p��Q�?��o�������Ozy�^yz���C~��e�O�K�>w�	g��?p.�E�f��������?!���ơ�eZ6q�7c�M�A��JS��<��זg����
巄ԍ��'{��������(@�N��rz��ܗSʎwc/����?����[v�
-B��Г�3Yv<׿��|�%�/���T[q��>���G�kMI��>�B�?�����<:����x��J������g���D'g���s�k/�ӹq�W�'7���c�Ο���b�YN/�=|���$y�F��5w*�zO4z{�.ɉ���������38o����G��K�Eώt�����˕g�'n�?�Z���0<�g5��{�@T;�ϊ3]'W;
��?�7O�qKq�4?E���������Ɨ�1Т{&'f�Y)>/r"d�����'�䜍�hG���91�U;s�F���qS�(���k�_�1���{ZPV�K���w^$z	ʫR��P��p�jz�ک�v㳄�ǭ���[t���g�1o�:j=����ŀ?z{�LH,�w�>g��/>mH��Lw�Ws����hϲ;G*ӡ��-7N3a��]��˼-K�r����C����h����s�[email protected]=�'�d7q|�
)a�b ³��Q�]��B����
�%{��
��m,��R)|�l|F����)f�O�=�C‚I�����<��_��wnM�D�Q�A��79�1~�?�5�.��>pK_�ӂR��O:�?���9s���b����n���,�^u��|�m�+H/�h�CT�ʗ�S�D�~T��=H�c‹�]d��M�q�>��v���͑=���`o�	�<�����]zN_���\�&����O�no�\
�)�J�j�+�ɉYt)h���(�s�X����np��ƧsD���8��`0��$;�GAIJ���AOߑg�/�Cq��,k��<����}�G�I�_���G�G9"S~~K~{�5!�g�n�78t�{�*�-8����k�w�f�"ҋ�w�����b�>��ƤV�^j�W_�ϖ�17�#�ԋ�ʘ�'����)�p:'��P(�pк|�'��;��NQ��K?���q�6�l�)���4��bG�f�)�jep�JU�<�&��w����Wo�ɸ�t�O�G�������Î������S��zT|n-�a�>���^��^�nܝk����	��v���7�/y�|*?����C~5�"a.ݞ=$}���ȟ�m��R�|̭��ʓ�?���@�7�ʕA/���m���?>���g�p[�ߺ��s��G�K�f��2w!���o�g)�Q�&��q�O�-d-���^�>+>�iE���������N���¾�&?W�#���0�ʣ<y��!�Di�C�����AQ&}ç;�pC�C�Ӵn�M_�̐�Ma�����V�UjW;o�<��g�L���zGΡ+3rR�܂Y��<�?}�	
�ϥ�c�M�C�W�<��>T}U��b��DH_�7������Ѝ��@/��
�t�<󟝽>���F���P|���U^����?[~��5��"��C݊3�[�셸�<�Ή�&���i�Gɢ�q%|�O�~��P
]����>�a-9�*�+��U�,;:%?�q���p����7�����Q^bRw4�������	^b~���y�0��\�
2�ϸ�ک��Q)|Ȧ���D�[0ʎ�^����E��7�V�"�֗���n�wv�pf,<Ȅ\���g��s�����.�-_�Ť�;0�^��3�
x
�|#$���=�-�֞���"SEÑV�w�=3*w��=PT�f�ib��ƪ<ݭ��]#к�w��|�כ���Ke��B.4{uUw�^|�ӹ�2o;�<F�rYt�NA]�M�;W�G�L�Ի���YiM�U��”�����c����!z��n�/�L�>���+3����3u�'�=���X�@�Xd��G~���ȳ�����Sw!�W�|�{Q�9yz����<ЛT8��}��P�7��DP��Oa��)����s��\�L�E�ok�`�e�.���
]��.j5��2��r,��]p,�z�����6�h���>��
�����;��7�-ؗ��4:?PxGd_������WW��rGxegđ��oΧ�9Qэp$(��~��m�\����Gw�Ȝ������q�Yg�Ӏ�@O��	�Ç�k.����0���D�!g'��T�߆j�՟6o�U\hƻ�R)�����cg�P�<L�+ћ��Ԕ69U�}��1ś����ȳ�|�˛N�R�F��������%'�/����^S��3��z�f����L�'��G���%��c�<�|�ќm���>���ӫsz�oM�c5�4��|"i=���55{��c!*���]�P��]�/��ͳ9D�z{����$OwO���M������ù�F;�����*0.z��́O�Ox��nj���x�xIy��]�Z�]P/a|�?<���{N�A��C�­�p�D{����ItI�m񩝂@y�9S��u�ي��'H[�3b3��".�{�
����j���6څ��5y��O�$�����"���sZ�so�t�7y|<�zUJ��,.1y8+��t.Ԇ.�+���;������	��|I���Q8>�:M�4n�9�K�k>����չ,��#Ť�:����Q�r�y�A�O{��J���c�2���zu���pW��p<k����;�0�B�#�ޤt�I��#:>.xR�;��ٌ�hwqU�@|�y���O�Y�e�e8��4��>��9�s�0U���3��]�����'�#��vql��^����ܩ�-��ΉI�c��|#���멝�G��'����^�u�^m{M�cDz�ok��Aj~2�+�3+�cyȃ����,��Mq�ڞa��H��(��a�w�[ӫXF�����ĎP�������Oߗc���:��3P��#��tl>�g&Q�iw��^�Q]��6��W��"�>�x���L)3{Qc`]t�\$�s/\�ngm���J\]S͡;|�<�ޡ�^��=������9��P{Y3�G�m��t���&廮���_�;?�<YJ���?��yf�k�n��еF��D�3���^��^݉�jם-��I���a{�G��w�S/D_�g�uʬ��_��N�[�����|�e���������-f�h��!g破�W�0��N��sPy|��{��<v�G�B�b���?��(����|��R�9z{�kX�O{��<>妨�s:l�����8��I��?zf�����ۻ�J� o&�����Q�%��;��;5��ş��|��_�R��(�:Tw����s&��a�s�9.���S����W����g;k�ʋ����5w�>�>Ǩ�?�۬'n�޹�%��������f��z�`5���eQ9||5g��[��%�^`\z%Ҍ��ʧT��czi�yz�E��|�ϻ^l�#Pn��Oz�Ƈ�6� �Ku�z~����j�����ҳ�
}��xNULJً
��&)�y~h��.��ϋ��?|F/ p��y�[�Uo8�?Zv����Y�оjzٝ���W8|f�^G����v��pK���Z��GAa�ᓾ��\��iʎ����S��Q���RpN��_%���#�Q/P���y�w���sv��`w�ŧ���ɣ��M�����p�W|�<���.c�i�яq>-����'������_M������O�>\}�p(�G��?��W;�?+?��鿊�MS�����g�c���G�_�7"�
��k��Q�<E�0]w~��9ll�{�1і�b���]�8��}�M�,��K�<���$��	v���k񯽷?�+��y����Qʱ�*��7N�?�{����Oo����Z�����1�5Е'O9F�eO��-C�|t�NǪ|*�����u���sX���B{�p�8�Gk�O�޹�5�
r���y�#��=�21qWH�Qy��mw\�;'!�4�A/�:��kxfO�u{��^K�t�Z��&�j�]{D�5y��	>p����U�/<Yq��=o{��
���sq�������U�"���#Y���A���K�'���3�t�/��]���'?9�|ړ�.�!Mۛvx�%Ze/_��=xKX�Nza�<Ł�R{�����݉�]����Tm	֢:����*�)��yv�Qm�|���U텾�o����[q�\�ڗ���.���~8�*]up�B���r�������e�Ył��%[�BW�<��ѺP��C_��Ƈ<��n�:��U�J�=}�8�jݩ�]����}�ݸ�}����k�$C�`T�����
�LPNpZ9L�r��o��-�D����v�����I���?�)~je�OY�EO��P՝7#�����M��=��w���5>���L�<�Cߦ$R��}�Γ����SY�w6+�
��;�p�?���>3�{Ň�����W���z��iR.��5Vܨ?�.f��mq8�w.�����3�Ө�?��v��Qun�H�Χ�x�B5b����0>���퍢�+��e��UJ`��9yp	7_u$!�Ɵ}[����sl��]1��D��}��0��P�d�Ž��:y�<�r�o��9g�K�鏀xq�����ձ��r�r������#�侰�pr�{������/���D��sGkY��i�j9:��F�d��S�5��퉫�W���oE%N�7,C�?y^(�s/���B�FĶ���o�mc���B�r_���k��D��fc"K�d/��	Άu~��*V��R޲ޖ'<;_�.}��ڹ5V=�Z(|���M91�5g���(/�@�*���(��7��\wf�yb�8Z����� ��C3�.��+~��W#&>�}䉞��}�B��c���{���B�������;��
>r%�D�֮�bo?5#�r��A>͗�?}��[��]h��l���y�,�k�q��q|��]��\�)/��w�WC>X��H���P����S�]���M���7Лb,3��k8���
�_�g�C~e}s⟝��7>|���[��^>�:��c[߽�\�q�?7Lb���u�|{��+���Cg5��_��.[�hȎ���g�2-#��c�|&�����#��y�#�:Y~�&u潟w�X����Ε�6#�*5�[ߞ9���w"�^�ӽ�8zd�׻Ӥ����X�6��Ч@�' dZ��4���K�a�I���������r*1�{��Q��Jb�?NT�͇�̪�]�%��Q����L�&�7I�}c-�,y6D�p���>+'f�O��p3�F����jw7��+}"�u�2�4�3����}�p;D�
~��O�np@T�(�}��P֧���Km��ܛkӛ2��m����m�7c��=:~��r#TMK��m��+��
	��#�)����+�z��c����Q��7^Yq
��ݳ�oZ+���ԙ����u�Ri;��Βbo��O�UN�5��h�#�>s�~��<�!uރz04�@�����#�pg�����s���QG�^��UE�G��^%'sY,h�3�ȃ��3z5���m?i���.��pNO����aD��v@�z#&Ҭy��>�m6��7�g�<��>Gy�SW`𙽊r˔O�H��m$�(�[�i���d�������.I�\��s�{���p�^���~��|�YK�Y>�O���SA�L�c���<Vzh�S���[��7�3�R�05t��'g¹gp���)�u
�"{wy�����G�Z��j}��W8O�ɟ<�=f�{��1��9���S�Or�7�y�Oq��sl_�"Ϭ�>��L�`���I��3|�E!�t��H�#D�8yzv�X�kmݞ����џ�d�7έe�%Y���^�����E_؏8���?���!9�����H��%>'y4����7����?�c�{u��s�0iG�iAG|�kpN��w�ޞ��+&��9�Ӊ����]�x���<c��^�s�[C����i���8�g�Ϗ�^~>zE<�[������k�'I�C{�8��\�9>��|��y�~��<������U�u�_�w��L���9�=E����o�)S�EGy�n��,�?�����ط�AHU�ׇ3;�9���^�Qo~5�v�/�J:�C�@;�G>#������#�����<G|h0{�G���?�_3RN�|{���Y��s�{e�W��Z���g�1�G��z�~����j��g_�/�?�y�u-���_�(�	0���/���
��F�?�/�㨩w�7���Pf���e4���<��$��{%O��T��gp�+\���<�c��s����gC��7�����r�f��<gڗ?�č�د������7V
�g��E{M�����/�ǣ��+x�^�/�w��)?�t�1>?�krL��n�3g^�^�9i�l�&S����v/t<�L�'�4ܞ�W'�O{�p��듾���9��r4�c�}�C�qx��e�Ͽ�8��y{o����|^��^�~v��C���?G��?g��|�q�<�'�Џ�1V��(�h�
�6.R��y��<���3�����z���9����?��̸�N\=�w�۫�8�>�V�'����:�,�L�F#�s|����p8���������	��}��&�j�/�y�N�я~�����^��3|f]+>��O�#O8�4e�w��o�Z���	>�xn����y�ϙ�O ��gO��c��ۄlI�ȣ��<Z�S���N���<e-�}'�f��+}����C��c?�\���
�+7����5I�+3�ħ�]�dO�:V��}^�w�ǻ�/��ϼ��l<6���<b���1��>�;�0��7Ƥ/�-ޚ]XX�8w�K�_7����+FN~GeL(��8�^���&��o�s���T}���*M�$�[vM���{&&��K�vR6W'?y�S�
~j����-e�\��yU+|w:�>�;��)^���g�L�c�~�E,�;7�xX��+�o}�R�[�&�0D/a/՜[�|p�ow������/:�	�+��S�h�е���+�o+�91�)$Q�W8�ߡ,~�M8"a�M����%H{�m�r	�l�<��<�������>ɓ��O��S{|�/3�մ�}u�Z���9Wl�W�sn�\�rY���9{uDV|:g+�OJ��*�?��ܚp^{���ܠ4}<y��x�3#ƟK�L��j�Ӆ/��O�-�=O�}�8�s�vr�_Bg��併/�����<��߬�����W�Ӿя��y�g�m��UL&w�h/�&����j�=���.�(��0�ħCXz�����(�O�^٫ʳ��}���jG��A�XJ���<�-�>���.��Ka3Q��]r	�����F[�~���e�7��&Iy}|HHR_�_�;�l���q���<ѕ:ȣp�;���wP�g�{�d/�t�_v�4__)�{M�~�����|�'��$�(��(��s��u_ML���;yS�+-��WN	��G������\���˕�?�,L��
m�~�]T��>�6�����'�U��MЫh8��M�:ƥ๾E�b�Wa�j#e����Q�]P歡^�m-�:��]����P��l��R�@��/s�F�׾苡���L��S#�KX<�<���̷`�O��#b���W\���E���0�u&k�ץ/@��Q�!��Xۓt���n~g�}V��]K��I���|��v2�2�/|}��E��ڢ�խ�A�il�{��*��W�Wl��U�9���`�e0���ZnJ:Ӷ9&o�^���^��j���1=>�iޜ��mc�|K(T��t�H��3L�6����Lyӝ�~��nSb]C�^Gn�	.���wü䙰K��r����'�Ma�}��¹q������C��)9�gx�<���i�4�|���g��`	J�=������
�N
�I�̘��KL$ �t�EQ/���������^�`�����b��O�dB �D�RrsYM��	f�������p>w�w��X�+L
6���Q���L��<:9�EjnH�����bҁE����~N�{x$d��T"�-#������`8���A�f�O1�!��[2WY�b˩�ny����3�Y;�����|�r���?q��dQ#$��Nʛ��^���Nh*�/|�?��k�P��j��(�ȍ�<��;)�9.LI�վ���DA
�`GN|�Cƽ�v���"�A�a��x:u|	��qs.��zw)��/��0������nd׾�O������:�;���le�\_�3s������.�?\,�Θ���a����[Ӭ}�#o����F/x�	ښ|�ɡ��ç�+�z�k�ϵw|���5��<8��ڨn=>�1T��6O�{�O�����U<�4ѕT�����y���ȓ^����|�sajn��^ȝ���a/8s��vAl�����}gv�N���<���^Z~��'y.큑S{?��S|K���#yL&3z�oo�}�z�}'Ù��Gn_�U�^�o�<�cw������0�����a�T��?P�^W���/2�I���cw
z��'ءk���Yx:*I���^���7tG��K;U/zܶ����1���G��?/o��T�v�(��W�;�iƲK���8���^���I��Etr6�W1y|-Nv�>���E�4�םb�\��g���U�%�k�$��_�CԐ�
�g�~(���],������8��l���K����;���?�d�^<D��=G}�������%����	������N~����0:��3�
	:��0�?\^��<���9y&�H�?rq^��|���~��ދ��w/..�`b\}��,��`���Ǻ�w�yî��\���qM��>�o�9��I���{�5{>�S�U�->��e�h_#�^=�j�Yy�\]}Ђa�鳻�B./�^��?���ō�J���������<
������F�j�7����cҍ-��pt�?����t�����e�����۟��k�ԕ(�ο]������O�D7�te
��xn�q��.���˗�o�7���������N�	zz�G|�<��X�<�N����|�?1������_�{�Ƀv��!��;���}`�g����`�p�v;}ɾV�2��7���[��G��������q�Ϸ�f��(l�5��ZK+i1��ν�RG���d;kt��\�[zm9�]�]��gYs���W�%�j_�h�����B�^><'|��zun�c��$W���G���F�y���ٴHM�/�5ɉ����Ș9yF@(ث"�f�p�ɺ�E�ƫ��f4ί�����O�H�d���<����s��f��������g�8q̌Y�l
��h��r���SyV�s>��t�y��#�{�_�g�)�g��֩��w��G�����<���s|�4y|�^�;s�
��9�`p�F�EȜ����:W�L�C�g�e���З���������J�O�;��T��	��(���m�Wv�?4��W{fO=�����Iu�W�:�s�Ω�\"�k�'w��:�vt�S�y9(���=�Ŀsq��)���cNX</��s;f{ {�s#g����}��.P����?��ܠ���3<ű�y:ڋ^����8���w��L���kG����{��F]�pvһ����q|��׾:G�8~6�F�'<'���赻闇���p�?�'6��ұ����ǿ�����0y�O���WG>�3��w�i�C_���xRn��E�y���q�턑��'�>�@pD�g��0�a�t�W�*9q�v:�yk_�c�XO8�����?��&?���38����f��u�m�!�B�ڠs,�6��F�2q���]\�u΃�B�'��?�|�t���8/���Ed�0�@ֻȉO���)���W8Ƀ�������ny���->��	qc����^'?����e�V�܋�!� �5v.E�W�.�;��~[򨷽"-÷4:O�1���p��~�LE�$R�U>ܠ��k�r���#*
���w�@�Ԙ�R���o ?�O�Á�A9�	��.輙�	�ǗSɳ�g�3|�K��%%� Sn��Z�D�g�'��}}9�yʙ�9of�!��v�K�m�W��?W�k�^���8�0� �.�c��$eG~��{վ?k_�g��O���?����_������&�2.H�;	�w���xb�<btv�2>�Ҹ��u��Ww��<�	�V��?����ɃOU�6���u�y;I����_�(m�]��>?��h�w��=�<��$�S|f���Ώ>z]�a�N.H���I���)F>q�Nq�R�*y~w��N#�n�G)�R�r��a��y��@�[����Zy�jo9՜�\������
|�u��~�<�Goh#��aw_�94L��V�03����䑦M���W�E�\�����mt�!a�t8Ъt�X����.�s|�����������7�m��L��"�sw�M���L�r��]̝k�~둧��~
���ǯ�ӹ2G|6�g�+��f�{��O�1��J��dw���٫��x&���Fx��ξ�=LO��L�T��Z|�m��L�?���?zA5�sn7V�5�Q8{U/Jg0������/~�Gw��&�Ą�I^�.�v7S�j|�.xw^KW	��|���!GVQ����n�d�
kSŊ�����-�p��V�
rJ�	 ����ž$xN|zg����3x�+�鮥����?�q�C��'9i���w������3��|�+�	�$��O�qG>�o�}��F1\:�}E��_v��
��}�s�P���W�sI����|/�tnG���w~����Gy��@���J|)}��?��|�U�t|r'���}�Ҵ���1�>�n���U���>����*g�r�m��l?�oAퟝ�SS4��U~�����!���'�C
и��V�����C����`�-�SŖ���/�%��:�By��}'+�t�u��t���:
k�X��8��ߨ��G��F����6?\�
�3���2#}<C
�ّK��`a����w�|�94ً��9O���eu?`a�Pt)�Г�A+�:D����بF�Wg�Ez���[�P{�&Q(��r	�ꩠsetWZ�:̩v���ut���ʪ7��4ӗ�]��S��ν����:����߿)���|����P�G׊+�
b����=@����g�6�/錾�5�<x)��L�?�:-g KO�n���F	��o+O��9� ���d�[uH�Y�W��c;ݏ;�m�Lð��;{��Y�e���7mᕽmr�HIT�-������.=�Lh��D@���(���xv�m�(�ʿ�T>;�û}���S��Z]b}{��(Mٞܬ��;�����ف�a�s�r)�2y����|�<���GzE7���R}q�W�Z8���+D��+>tG�-l_ᮼY"�Kf�^��OY��
�q"���i����e��K��wi���ʏ��8�5}6	F�������u(��M��aI����8aDL�J���ʔs��O�E7�tF͊W�.w^t��}W��&��2y�;��T9M���
Ek���c�i���4"c��w���p࿹n,���>��"y��(굦CY
p썏vt�s| �M|�}�����>"/>F�Bi���D�'9Ť��,����j�>|؋C<ʳ�?�y@�i�Ay�"|��3��7K+#����w�h���'?�{����m0��.:1B�b�Մ�L�B�VL�[��r)6�E�۽�}��
5�x�%�� ,Q�����?Z4yw0a^|�'O�<6�,[�zɯ�!g/��G��ӀN���j�y��<�.�L�����S��'�իa}��28�t��ʋį쾯+=ɹ����7v;��o�C����6��gP/�ȓ]�!gx.�z�W�O ��2ćF哧1��r��p�A?q��/Q��!;b��}����*�����+�4����S�z[Mɽ�֝P�3�dw|�ֿ����G?�?:��j��s�o�{3�o77ً_���b���[�o��{�@&8�á��v1��7���p^��f�_���l6��>�H��<�n�������qHz�3����D|���'�����Y��'��3���>cwr���Z���	�=yL&��@቏*�weD�;��v�!����,>;��<��%�o�j�Q/{�y�h���P;E�O�@3�*>O���g�v�!�ѽ��y�C9(��S|�����@g<Z|6��8�^��8>-��|��2���[���ʗO_��n��ۀ�	��)���Y�ထ������DO~x�� �I�q����Z�^�5Μ<Ƀ�q������z�)?������i<s�☑�������Sy�2'y�F�-��
gt|���������{}�_e����3|���ۘ)��㜧�4I��p�[��spnlFNt��\���,�������cm\����__��P<����o�C`|X��������1v����3��s���* �K��8��ޣ
���>~�z���a��~��/[��BpkJ
�X�M�i<�u����0����7n��ܚ�R�A>�n����^����������pF�\�}�C���V���[1�D�s����@=|8����^�^�*�������#^ȓ^ݩ�r^�E����W>V�~*|�g���}n�Ǐl�7��dby��J)������Y�
�;,ZIdG��Y�1~��$����.#ϴw�6��L����݁u�W8��7^�w�"�}ŵ�	��0z�@B��ѳ��;���;��?��i��;�j۽���[tL�^;5�>��!���{��6��f��,N��~��}U�!��m�,�����&����!��o��j{�p�(��Gy*����
O܃\=���M��g�������/��.�3��ll+�������>/ʹꗳ�F�_�'��?o�/|ȩ�6~��>�L�v��~��W>9+_?�<�[��r�v��}�C��>�95�h���p�h���ݏ��Nl����QC�d��)NjD���4%q>�^�s����n�����\��xl�<�8�'�Л���Gyq#�Q^��|�����=������G�c��Wq�
��v�i�L�:|��ǟ���mܒ���ܖk�S~[?��Ȇ���8�t
�r�o�k�MN'w���JqU�Ϲ&�st���=��]���||��]e�P�N��͡_4�.|<
&���<�o>?ѻ���H��!{��~�V��)�b��d/��}�?�@���w���3�vwթ�	`�0��2{�2
��oo�>��6���3�m��3P&�к��h�m�@Pn��F���O�o���o�#�¶R�ݙ7�
c/�a�=r�T�;JZ�aK{��k� ��=?4�2I��t�@pN/r�Kd����Y���O|�����⡻����Ĝ�<XU�^p�;/�gʏ��Q8�I�ih��|VJ�j-��ϾS��C��Ϟ7����P�6���	�I��;PГgpN���!?�|��y�����^ݥ©��ϓ�+h/���n�^핎�Vo�>f���d/|�hF1u��疞U���7W�!PR`E�REW/����;�/�U���Z�9ıp��Ł˾�g��&(�S���Z���;z�ST�O_����s��;TEmG�d������r� ���2DY�;��<41�Y�೏~A�w6�9D������7���?��uP��j�(ɃN���PD�	���iM��<&�#�9��4�:|�$�NA;~�[oH�#*�з�ё�?�+F��^��o�(��>�[�����p�~�Fr�r�~���δ��w�l���?��?!�ˡ��?�M�S{�#J�k.�:o�Q����З�
�Oz巯'W��������JZ[}I�O��0>�
�7���(��L֑�7|��~�7ꚋ�ܣ�n9�K�w�Pk,蘄'&ް�҉�O��t�Cq�<�G��.��W[Ž0��|�ƛ�R!y�oz҃��y�T<���:r�j�������
����y�u�g��1�X������9	M�vDU2|nOv��T/y�X�������ьʌ���\�[�����|v��fA���Nʡ��1萛�^/�����̫]��_�K<���Q_rny�8��߳;!��{�1T��,�&9t�*?w$%��+��!:��� `dLȃ��W��6\��awU����qy��=9�s�l��,�J/Œ����/������Gz�k_Α1�����U�����<(���&��'���<�ӭ�[/��m?��%����ɩ�S�\��W�b��V�[a����1���u�ɏ������Eպ�mR��n�Z�Ox�H�g�Fy!!�#�=PW�"��-��� w���9S�>geۅ��k�[��:�AWf�~��`�_��Q��K9%�L��v�W�_�o��e��E���~Ȏ��H�+�u�Z
9M�IH���9:y�y�Az��61:bC�r���]�v��.��lک�U����۾�G�׿��.ɃT�y�-�����ʯ�;�u{���O�����[�����@΁��m�j��3.o�<��P��/9�yvB-��E�ͯt<U��ĸ�(�}�&Y��W����p'�z`tG�[?|�v��������@�t��k�qw#��^}��Ʈ�i̴�5ilj/s��Q����r���'s��T$
��wWX���r���bf�����x���p�<����\�,�_12���@$f�}�[P�\���s���p��^�&�����{'�{E��3����Ga�+eW)�8&&̹��W%���'���X^|.?��Tb��W#��j�{�0b�2�3_�����o���{ȕ��{W�1䆘��\ƝRu#��k���R2� �<�����l�s+����Rz��{�Z��Wn�(drN�Y��ɔ��FƯ���6�ޘ��2�a�W�:�x�*
��*<��syu����hGZ��$���rez�V*V��+��<���1!�\4�h�[�7�|�9���Ə��W�J�P��-ﲻ
{��%��BC_*JL�<��'2�'VPK���}��Y��A�&ސ��]a����e�e�^����'�e�w�fCת���b� p�3�?��ݕ���	�l/Y�d��ǜܓg�ы�ߢcvssm1�{��g���?���z�G1��/k�^s
|�_����<V�O�^� R�1������=K��������	_uޑ��;�TI��QާiϜ;#����}�.T�<�,��">���騧�+����)�?:!}E~t5����Q�O�/&!���Gy���Z�9�Q�h0>�>�^�]�؋ew���{ߎ$�E���U���8�5��;b|�Fć0�z�E��<��Q��_�|��bn/$>�b��<���#R��_�?��Q�����n������rȣW�p��]�XnA9
�ތn�O97�if�^?�g�<ѕ�����S\EO����o|�s3k�`�W����|���Y�3>�~}�^�J�p{i��9�Ч}u�Zg�uGLkj5�m�1�	��o���yT���ӷcc��ʟu]�>�d(G�<��)G*�^�s|Ɵg��.�Ym�u�>���$�7������d��/|��'�]���K<1�/.�����O'ӦWgY7���^�󄄞<�1�:!�c@��1�
�A�G�
���}u������y�I�Ӕ�Eq�P��Ց:W3Ǣ��9+|�)����}-��ILB��/_?��cl�}��.o���p�x�������n��0��^��������O��������<��b�D��H��j̏�f�=q�"��d����'�G7A�EF���S�Z`N/|�@$x6�����~h=bpN��K����~d{�;����\�W��Cc)Q{�|ҋ�������<�t���6�������i?�3�����%�ur
;ۅ��Cc��ӧO���������m�a�a#t�*y������(�݅�.'Dž-tt
��y�	?����H�h*�A~���돽�ka���_��w��1��{]I|����:����=%X�,�/��y�y�o|���vI�)��ɭ������
��է����8���1�t~|�\�4pF�,����W�AĪ�|�9wx��������U?�
��*������'8�s��U�ʄCq,{��1����z�?��ʧ��?�ޙw#���Q8#Zd�co�?>�kZ�+�6�h�{��&{��'<Q{?�8�Q���<c/|O�Wk����:,�v4wQ�S壛�ʌ7�m��c���B��MZ.>֮#<�O�4Pg�Ň��Y{������^�箢rA�/����'��E9b�=N��W�F����I���8�^�J��ӣ��uș�D�����!���3��U���s�����t≒�1:VVzQ��k��O��mk��4~��ء=��{��Y��|��C����]*�O�k��y��ԡ��*�����Q�R^c�6���KS�8���Q�c��q�����ȓ�#��D�F�^�>�9����|�S?���x�ϵ�p���'����>J�+���:����Ջ�q�\�<�Sz��p�'b��?v�<J�8��"�	�S�����`���b�Z��/�\�`?��4��X�p^izU>|:�x^�^_�����<��]�Y�z�LJ�tU��va����Z��Z<T%�)��~����
���<�kv���º�r̉�z�6�z���s�T�<���������a�P.��qf���G����p���'��w�h�]k�O�ˋ�oNg����<|~�!潍�{��뵻�C���Jk3[~n/s��ꮏz��\�<�sI�e���c/t���x�Cq�g�[�AK��9��^7�������[���B{���
���?��m�1����-J����۷����O�Ƕ?�?y�Z���U�ç8�<i������Թ��/7ם�{��O��p>��|k|�
����BnS�N~.�:�U kD�عJ��z�gt������$�
f8�Nz+�yfpk�:��y�����w�ğ�<�ҏ�lVC����$�\7�K�U~e/<y�z�!�� m�^�_$O���ߕ�4���C�*�i�M��N����)��9@�w�:}}r���M$��8�Ϊ	���o��K����9���a��-G|l~�>����G<�fiyi�t�Z�@�_��5V�����	+4�
^+z�
����n����<��0�T)$Z�)��ʓF���sF�^����$TS�����}�1|�?��'����y���0����?gq�x��Y�����_-�'ך;�z�N�Y�AG��ج��T�8����p��{��/�#�R����;�ɚ�T-��5��grR���<
��3P���/���!_~^V�Z�Gș<�U~α &%l�b˕�	�O�y�p���_�����x���?��F�c��C6wiy�>"��Q�����n饽ù��&���Vز�:�w��:�5����A���wqO|��0D�` d������Һ+
s�:�w�^��v0�zCk��Ӹ��xL�L���3\::��<�q�3�=�G�8è���C~8d��{5�8�d�U�ֈ V���m�x��{;v_�Xx�	N�>�'UG���
n#O�i	��Q_��!��W1p��G>۟y�~�>]��;U����C*>Ʒʃ�/��+�7��)7�W�֐iSՌ��|סk�ԈT�`��Ό��(��U��(�J�o�O�Yb���	�&�N�N��o�?>Zt�b��Ÿ���F~�;�Jc�‰�<�]�l�F��/�s�����&b��dV�;�G��U*�G7�|��uv���5l���}1X�P��u_��Tk�����Gz���d�PY!k��<���ً†2M��+�jy�#�73A���F���I�~�f�6n���>�
>���Qn��d'�����R�eV�rye���7;�3:Ռ��;s��y��
���7ع��!s�y^�^��7ͮ�����.~��uz����ԙ?� �Y���׫��_�|G
��#E>}�t����:��l��%>z�ϯߨ��W:��<��f a~��q2�^f�Q����y��#7�Mf�<1��q�R�cM�ۻ���_��MIR�/_?_��Ƕϔc�2����}={���o�-��$���|�,S> ���Ct�����󖻊~�U��j̚ï�Nh�%���t$�uH6�j��F����r,U7@/a�^>|�Ŏ���^�i��O��d�}�lo�wbhWH���Ά%#l�>
8zY�i݃<<ط���,v��lŠ�퉡�e�R
^{�º��[��j���Я�~���?��h5��گ�(��	z-��G��v�2�d'��sj@�ӟk
Z��l��" �Ǒ����R����u�s��MS�%�=�L֚�i��	��%�Nv)Fs�__�^~xo��O{�|Ky�������9������<s�Uce��?�S.�,�׌��P�I�r0g[�0L�:�pr���
�����'4���	��<D�O%G������U��8__�o?ԞS;�'R��a�g��O���Ç<�Y���ə^�5_���=�N/x�X���<�<Q7��Z�k�ϲW��^MGマ��
���9c��\t��K��?��z�*�:�Vo~;�4�O��9v�^3V�ޫr�<r�[�iyP*�Q~��s��N�2A�z�=�{9
��O�g��s����d��#?VGy����=|�9�Y���S��U��?�3��U8�/^�����J��'�J�G�#>3W��<�o�'xG��|z�+��k��G����U��b�^9pz�L��[L�_��3}�<����~2|��'�'�b�@+�������;�f78�3�kB���������-�
��O�n�����$'!��]~>~8���9�_;9X�C&@��
��音"y��+�=�/�'|B8H����?u�s�kù���3y�^�?�a9�	�i�Q���N�ſz�����������VrΙ��.��ę����W��x�jv�ߦ^z
���c��$����>����w�L�%����I܎���a@4�W�����3����c����3|j��׿�/�s*��_�d|x�,���q/�ȃ��h�y�9�y�L�<��+9Ç^�yT��;&�<�-i;��K�}�79
����8��$�vdU��y�g�*f�5�����g���3�J��	VyԌK�5q���Tɭ�?�}���+��D����?�;�*����(9�����9�+|*?���x>�׿���bN�ϾC��c:i�'��aw��s��J���>���4;��g�4z�����c��u�'>Ǧ<�C���O{����1��8qp~��=�:�wz��4��S��zk�cǑ?y��3�?�<x�'O���y"��V|�(�>|��g��=�0ǟ�r�x�#�{�?)ϸ��xl���2�N�ԯ%�ث�vlU�>>�R��g�r�y�zɏ�qo�9����q��Cv��{����W�?�e��_�~��p��$���S.Q�3�S�8�
��;�<�7e���	��ؾ��Wg�sp>���F���y�A|��#�g���U����S��x5�	���v����g�/#��q��q����9y���O���d��"I��0���<�9>��<�O���s�g�>�g�y�[�4��>��?�W���E�
q�8���9�TG>λ���[���{�$g��Ӿj���C��O�H��w��PEO�8���}"����̢��S����}��w�f`�-c���������1�aq�f5�(��s.~��N��#���n��4>����'��D��r��O���<_;��V/��}�tr��Ծ&�Tw�:�{�����|s�O9I�/b��J��wļ�2�Z��3�w����SJ���ϝKQ�m�Kw(�H�1�?���u���g�r�rc�J�?���ݸ���'�\�����h���+�KGV{�'*���:�<�;Vc+��:G�@�sb�еnr�x��?��*���y�Pa��oo��rI��~b�$#̆�{��M,6;fpv���Q�a/)^I���7wz�M��|/�ˑ$r�tݾA;�����q�m��Y�;Bp����W>��C�����O�s��g�Ɲ*o���v�q��Y����<f��鈵���e�����)#c����?��t{�g�kS_?���Cv�;��Q=��e8�t���һI�y!v2��;��9U8�C���h�*���m]�h�4��`�"~�?q�v���V�Z��ܡ�]c����v]Nm/�������.���m��~�^�q�ސ�X�+�Ы���0��'����\�~�+!{�]{B�`��?V�Cr�9�^W��!�Ǔ��I��~
N�"
}eǘ�hg��KI�$��YO��Z������¸'j��3����f��x��A<���^�4�+��c�
��P�D�Ҥύ�9/�v@�<��'��D�}�x"���6��c���g�
}�������z�Q���r:�w蘌<��R㢑��?�3|f�ćs�.<�c��*��G�j�.�]�L�r�
��l����n��A��ֿ�����@�m�<��	m-:i��;��{�G�ާO��,����+B��_��ao؟�}�H�nԥ���8
��$�����:=�Wm`"{k���o,e_�|��Y��
�޹Mo�m�ӗ]�����/��+�*�u��+�U�	v�����5ܶ��N_��ґ���ȟߐ�7����5�P�z{�pF?]�����{]�?>o�^�����jߐ:c����Wu�>�`dѝSV��w-��?�_��|�=�q+y�s�	Cy{Q����t��b2���=��><r-�ֲ>޶W-�w�,6�g�g���JP����D���}��o������^�q�9�I�;2vy��e_����Z��.�O���~�Ǧ�w�] ���=0��;/^l_����ޜ�,V�,�"��;��)�;�f��jYKy�O������؝NܕGO�A���;hk3ػI�K�d��A�����AY
�7�c�u}�KR��4��o5�j��sY�$:��^�]�L	y���/��21玄�f�y���.q�j��b�;�V�rn�=�nfK�N�f/yU�F�얷�^�v�βc�=�*p���O�$�L/�p?M��ID�,ņ^��J��
�`}m7rRj�1>��`����ei����{%gAG:�
f����3�v��P�j����'���#����]�}�.�윛��FN�l�U#@���'��C�rzzU�<%6s8����Q
oe�!���n��0yP��z�s�~�77I[ҷ�O8���5a֑����;���8�B��S��"���<�i��8��;`�?Y�ݜ�31�5k��n��.I������l��nyȱ������E7?����y0�C)t�d_�����{K��{�ɸ�� ˨��1�t=��,���?���b}�*h/��0Z\�6	d�����#+��;e0�3���[ts��2Q/}�K{���H��+�p.��դ�nbC��E�*.
=>_?}���4�I���Wt_�"st��N��e�_�]^F���%��G4��o�U�D2��6�Jޟ1�	�O����|���}�'>g~x���T��^�8��$��i_�Ko�$Os9������?�?�=f���X�МM�d������<z�oɏ�2kU⏱89���6����f�"z�͞h�>$b����9�+��_e�����ݯ��v�
�3��Ԡ�-|Dh8��R�@/�-�	_�@���3v��Z{���?_���~�����?���y���8ӛ1	��Kn���O�<O�p�����3kj���ӎ��~>w`M��ɭ�sr�9��v�K�;�]$�Ll��k���z�]�G|�g�~�<v���rl_rF�+?�I������KK�]���?�S;�g�����.�;���Z'�[&�����ږ~�;��g�g��9y�λߙ��g�����_c��$!�G���k����~�����?;?k�}�G|f���.o{w��h����_������������ɶ;`�5F�q�v]���mt���������#PS�����g�|p�5�}M�W�!�)�ǟ�c�cP>�!{i�[���6�x��x}����a�E��ӵP�w]��޿���Ϭ�8!�^��[v\*�L������������;B2<k_Fv�w�W�՟�O����(?����ί>\��kt�U��{��E�+#�?����|����y��?;w�ϋ���`e͌?��F�������~�sk:8�S{���(|z�Rj��SO/?\;7�`����/�� 	��<�O_Ͱd	������ߖ�qË��a~��ҫ��bR��峌x�E[���p��hw|���G����o���__d���:�ݮ5A?�~����$+HɃ�]��|���ݱ2��Z�����P�jZ�i�r���H���T%�=9��3|��??���]?�ly�����_nb����/�$?��ݻ���e�=�s�i�9C���eP:��F��W����%��Z�#1�γ�+4������^�=�y��pjD{�"k9@��膇�W[����#U����/�o/j��^v�ss�q���go��}�pE��	g{��3}�Ù����Gv��.��;���H3����ً�k_����B��hA&z��[|�!e1�q��^�{���/��)�<�L;bw��yt���e_��,���\���
h��";wj���C��.ϣ��Ћ}��
{���L�y�k�����M�O����5���+�Vo|�qC���+��������f�����
�l|�c{��w��}
��ڻ]�8Ç�;���?��F�ڗG8�-7�v9�S�����w��m{�,y����׍U�ⰧJ���\�h���|�])�ϓ��l;h��ye.�o���_g��jG�:�����g<�z���ԙ��Z����/�2|�Gyj_�]LP�3C�q8bql�j�?��O����'�ȓ�dnj�����{�S��i�9;�29v=�(@o���{�\��^��ٟ}��ȏ޹�v}�S�iۃ�����Pk�Y���D4P���O`�+�Nҷk/�p���w���U/aT�)m<uj��)��W��[?��O)�g��#��L�T���3�>�5����Ԟ49U#�h��X��B_W�%{��?�����;'�1y�]�`�i�w7Gt�3(>~��3	fݯ8�.���Y��$�uo����
����!<�����w�ԛ]������<቞<إW�t�mz�Wt%�T�����ÿӜ��_�?M����H@��z��LTG��j�)�c5G�I������w�¹sR���l$#�_�C�̢V8�'�	gt8���z�n�H���Rsm{��|�mc;s�td>L�CP����ㆣ>�K[*�	>��X���uo��}v���ǒ&cQ��6�����s�2�1.W�'?yf���q�}v��?��ޗ!@�1ai��'��8v����N��ȍv�[");�,�/��3^dK�<��8�c�B�[5��#7����\�<f���g���ӵG�3z�%���$��O[ﳷM��n����Ϋ�4�൨wh%�����/���������1��n��Ŋ�Ňy�����9�7"�[�";�+�7�^�(d�O���c���Xze<���"��~
Y[����<J��0�k38�9�-y� ������F��L�%?>)���`�~Ƈ��=�[�2��T�hXI��Z��?�6�Ckb�w�V`��#�_�.�dW������/c5��̽�r�੼�"wAR�<��B������Y�x���r��>��\�Lk �se�;yP��)Ox������B�`%m���L��v	�����O9
bS�O�<��EZ��J�4��Jt�
z'C��E�O�$��WPX_�^ȳo,rxĵnR���w�CWd|T����ݯi��Y��,�Q��؇">��ohX7ݼ���|d�j���`L��TDZ�=�G_F�7�����X`T^Z����}��f]�,�>M{q"*>�#��, X�D��YZᦿ�^����EA�)|ЕĿs%!�<��D�$g��]�Ǒ�v9�=})���x`�;�:�W�}U�t&��3�鹧<yo�Ju�ۑ��Σ�G�g|ȳ��g5ه݂:z�R�}���=�`�ܩ$�J�i�sOA��qV6�z:���裩�#O��ҎE$σ��}<�nyn�ZJK�]o� >0Z�*�G�fo�?O�H�����5x6TZv����l}��+��ndz�5�k<�=�Iy�vI��a{�yH~|��mr9�r`��xc�����%������6��=��>��x�2ѓ'g�w���䴈iu �n�.��t�NT|����*�8�Cz�GˤD��>k��F�Ȝ�t�#?��F�ll����D����$�MV¨S+tbKg��-v��_�:��5w�����G��T��b4���:�4+�<V��H��0�e�e�I��m���j%5T^U���F��.���f��ѵJ�Tf5�='�*��k�X�nϣ�@�m�+O��X��CcaDL�l��?~�g��>8X\�m���O�9Q>��T�S�^��[��x���1�u��~�@zK20\�e��C�e�,t5�M����G'5>jg���)yHN�s�,�P�ш�6m����0�#z����xr_�V������T�e��	��:Կ���`뫃}(�W�p`�:�g�گ�ra&U4|>��]�W��;���7�.ez�7�X9���Њ��������
7��76�S�X����Lji-y��4���~z�/4K{��٨!W�~��e��'�S��!��Մκ��|�p��îv�������q�^�7����ʣ�':��[ُ�R���0j�N�:u�@_��|�?�X$&OzG�,O��I�
9/ʧ�,��~�=W/9��=��2�Y����d�I����Yc �=:�����j?kIb��}�LJ�O�?��wl4�<����f���6��U�B��9���6|ͱ��+����%�����w���1:Wi��[����U��C��=~j����>kү|���çk�L�ړnŊ4#��#9k�zgO�j�Ϯ�9�Ɠg�E��7�OK��s�w�ב�9���F����}w�9�*o}��:�)�LF���qy�����>��>t9�V��gr⟾����O��;cP�?�����G�^�j��W��Ffᩪ���L�����R��ޓ��T��K�BqRy���1>�>����>���_�R�<��C��GN���[����gU'�šrS�	m����EϞ��]eϊ���yʧ/���cp���§9s~����ao�P������+y�]�}�b�+�?����7�J�������B��!����}�Lї B���S��+yF���5v��e�<�U�.=?:ܒ�qH����x���C�X%��Ey���]r8�8�o���G�b���:>�I/�s|lqR�L8���$��S���Yϩ]�>�'�x^��t����0㟾�[���_�	9�4e7�|,?2җ{�<�9%�q�\�^y,v�3r�L	�����Ћ�~O{l� R����݋~�5�
�us���?ԛ<@hqB[Х�(��%^���	3L��s�)v�-zrj�����#�q�S�K�/��I�<&O�ly��1���4�yt��IziFnM�P�V��h�C�,����Ƈ�ƞG����̄�����1<�~(K�pv�$z��ѻ��7>9��H�~o�ߙ܁3|�^�v٠��̢�Ò������o늯v�(��f>G�p�s��#
g�4��9���Ҿ�E�r�g��C�����`�]�.{��w�I�s�p&�ا�C3�yeǴ,�h/<�٧�#��=]�M��9:��\L|����+5�ʟ��p�#��yj�ZN	:�O|�S����?��	������co���N�����d������GZ�ǟ���ϱg�g�sӍiW{�ُ��i�F6��Ɇ>���	���j���+��.��I��V�kb̌}}�ۤ/�]�N�%���)��?����o�ʣ'϶�?Џ��*�_|�c��n���|�L.c	�����Ǔ<�tbӃ�������/�93:>ѓ��b|�
�a�1��8��aϾ|�/��-��|~��>�_�;E7�ӗ��C_�<��PB����	��}�p���r��O+�<����>J��������Q���Ç��"}��F~����أ��:�+{����P��:��'�{8>ڕ_'�?�w|��{8���'ܲ���څO��\��~4�S.>>+W&|��_v8���U8۬�v����ljc��#�iU��)�O�O|�m:�%���y|�&����&��=���E_�>z>[_�W����w�g|�Z'Q�u�2f��O�pn{`�BRb�G�/�3K�9ס� �^y8�'!�5��w�ӱ�ϼ']�s>�^eУT�mW�J���.�N/���v?'��[؍�=�y%��̡{
�i1z~�U>�V�ϫ~y��/��$��~��vٛ��	�rY쥡{ژ�R{׻uν��z�}��?>�G�'}�]�cz7A�?y�G��O�&ESw���%��':b�I�F~ӱ��<��.�m]�ǒ��
����T�ן���k�gn�'<�GN��M�t@�O�j���,9�b���'�+o����g����j��y�I�줜��D��㯽��=>�hӕ��;$��Qy�������99�ǧ��.�k���+�~�?:9�/��_�S�g��;�s�\�ş�2r��<��\
J,��q�����8Ϯ�G��'���pF��6>j�|���w+�����+�+�m�;�”�KN齻���sN�[|�	��J�;�{7���A����Jx�Q�%��|����C��1���U}/9�<᯼��P��@	t�W��]N���/|�#��~
|v��a��O�����{�<~�si4�@����q���i�X��%�����_1�����`Q����
1|�D�#�n�|����m��z����e���6��o?�|�u�ɗ��j�-0��|�a��9�%J�Pi����C �Ur.8{{]?aQ�����x���3��%ᐜ�8��H�Q��@-�8�r�z��S�(dߙ��W�F'����g���A�'	�Gt�|@Vy� NzӽxB�����8�s[�t��'	�/A�����k}��Az$-d8]���UC�ӯ�Ri|�����Sq�2%�P�
�O����N��0>�����@�p�c���-���C��w�9�gi

�,�nT/��~�ɷ�d�{���̝7��w���|H���pl�c9
B��[��	!�#ʗ�`��z�d�ǣE[��_H�\��+諻lj��u��L?F��ܸ������	s!O�O��{��G��;ӏ�$�� n���dzQNa��>ϻ]+��#p�.�77Q~�=���Y�șiW�">�m��֮��ko�bϬt���p���H	�<}��7�˿0-?W��f�]��I��G�;>�t�8A��r�c�����=��_KT8���}T+��޾�z���ǽ���&A4�E8�.7 ����(�V��g�c�M���A�����/q���_&��mw�כȷ����R��؃��	>Q��E��>y�����𱘑*�}�yn�p�D��
��馼x����������_{��3�W�#��sb~N|^|ȿ��a�a"��>w?�U�X�����=.Eɳ��]Z���ٹz��O�X��
�g��-:|.p�F�8���1�i���'o߄h��S�$/8�Oׅ�5TS�T�Ε�>*��b��1j��k�$���QU;�]2�91-�ʬ��1C�o�X���|�C�����ۗ��k>�:�5A�IU�f�;x^|:��XG��u�Q�ȃ���>s�?�j�첼@�a����[o����Dv�z�e��O�?"z�h�MK֥^�P~_�U�_�1�F<��>���q�DZ�j>8�m|�ǀ��pv��B����m=OU��׃6���b�t��0��,\J��R~���L6%��?Py��_h5�e�wV*�m�q�
���->����k�XA����w��ȣ]P�9C脒���B�&ϙu��nW{��ڰDat�G�K��=�[|��4�ްQ���(��.������Ǟo��ҷ�ʹy"O����$�Ģb|�����5w�?\8r^��ox���ŧ������Z!;��[��u�$6,�L�}�)�`�m�e���svy���=�ʓ���~|��v�I�B���KN��Y@��N�"��<pV��r�n2���{U�j��/K�e=>�f���/X>�.W�Y���=]�����7\���v����I,��/u�X]���N�����Y�<Q��?�;3<}��[�;z�/���I��������ʷv���啣��ާ�g�Խu�c�* ��6�>�Lw�N�|���Y��=�b�y]U�Iy�rt���{>8$O|����,V>��s��������󴶈O�꽨������"���])3��ԏУcB��'y�!�O|�Kv=<�GN�5?��g5w�,��/���ϴ��ڹ�W��Q�b��<�yr����;97��t�K�3�`>��}�ϧ�z�;�,4k������&><&y|�l�r��ἧ5v�(�ב�83vX.N�Y��_s����S�U|y�˕!�~���n�4�L���rݪ�ɟ<�<|j�����<�k���=���ٛO8��^aA>�y��9�a�ż����:+�:������I���:/
�W�7����4�t�`���sr��w���N��Σ���~��׎�=�s�E��{9�k�Ki�?w>�vX�?�c˞�Ę�~z"��������.�G,��� X F'�a��yǞ��{<@�����5Q.�F_rG�\Zj�#uF{�
�s�3G��/8�Ed�n,8�c*x[}m���jW��剏�QЍ���7|l���[''潟�9:��;�6�O�P�0�����%�	�)��ϓ=Q���_�>X�L{K�����_؏h
O�l>�r��#����a�*Ig�3>��
�Vc�q�ϓ�̤j�j����dz��_������Qs�׎o�B���;��&�P��gi��f}�L�p�1�˿��lA&��$w�tP	"/[_��rV���C��6�s2����>��]����Υ`��������e��쉲&���?�w��څO{W����\ˈ����0�p�u���j�g� 'W&��ރ�Ko;��>�w�p����R���0fA��-y|5z�O�L�zGBi��Qo3�f���[�n���97Ӯ��s�����~.�4�&>���&_��]��3UO��6|��C���C]�З��k���%>ѓ��G�����]�[������k��[9�>�I'��;z9^3�*���/v�ƞ�_��x��rG��!��Kq5y��'���g�r?�"�U�x8�C���sAZ�Q@�l}]Ǟ+�E{o��3~1�����/:?�՜�	�&�{��|z>7rw��n|ƞ�㜑hB��pF�jp}�<3��������~��py����̛:�Hijї*���;�O|F��k%�����������+��Ot�çw0��y�O�풆�+�ş�3.���NT�>B���_��XM|>��ʅ���w�Ԯ�����g���>�;|g~}���s�Ck3~ο����IzO�敳�=w�e���k#n��' ��h�J���(M���t�6���4�����q�~'�bW�\w�7zݑEh>��������O�s�S~a���>��	�M�[t>幽�qZ2S~�%2 �.��g.�Fw��>۰n��oZĮ��HW�vk���Y����R��:?���D����s�3��b�G��w���9�^��z����%�t~��*�{��D�^�
��qC�4���@�>�]�]��)霆�+��+9��o�\]�y�S�c"lU���Z��={֮;�
|�J�P�:����|����4e#������}�s�L��H�g8ϝ��j�[t8Ϲ)-d[$w�(���/�M�~�eN>�G�>��6�堌�jL�u��媋��+1Kc'�u��=���=_��u����#�iyZ�(��~�K%��I�Y��{��zg}��٢>�R�:������5ƵS�čΉ�|�Se�_i%�=ڋ�t��7q��.��r�؉�O��#�~P�G�����=I\���O�BAt�j�~bT�H�&�U>��j�L�	��&>�LB�V�������<�D��pN�2�Y���h��_��\��ݣ��'�����3q�Q�	�bi9_��J��>
r�{%j�P��x�K4w�-{�O�2U�	�<�<_�=<���/�cUIU���Ӹe�!&�;��N�LJ����AOO�K�q��I��������)X->�<�S���F�m;�L�<�JԄ�&�r��|r�r��&^�+�x��]��=�xt���ޗ�?��I5�űA�\��B���O�����2Dϒݳs2��_�~�]���*��j�W�埠>�m�yf8�
����˃`��)���6BڲzL���
��ܷ��L~|�8[����} "�^�5��E)����ܦ��~�C8��޹3ш�X7�o)}p{�'ty�OכQ��j<;��M��%|C��O��.��<�-�g��q){�|�]�z����7��G孽pc�K_�T~�][����=�a94�{r�G�ʣ�O���Dc���aV���~6>?��@�E�g�'��E{[�Q�^���.q�t�I�����8��݉�[�Sߐjrh6�#�8L/Ӯ]^��t�nÿ~�����hCZ^��ԋ^����-��w��D��8Jy�(��q�"���g��|f���[�f�%]t���Ӟ]��W9g���O�<�p�<sGؾ�(y�C��nͫ�Z�ل�K�+"%}Ѡ�d̙�'{�G��Ka�x����^:�D�}���?��e�:��M�2Y:㾛�����G��k
33u2ا���+�I�gt��n�����3#.
��~�Ի�s9fo��Wv��{x����V����s�'
&�z{�*_�i6�pUո�!��W�v!*�nO�o�]��c;C��g��n~n��v���V���+�����iN���q�]�`���Q��J$�(�v���7�TƼ�4�lZ�*��z��i�'�?o�X_񮺧K��eU!:^I�i/}��~ۛ�v��dsu2U��䡮�km��z�93�1�Q1-dk�F_���0�:ϭ���Ѯ~���r�r��>�Ej/ع4�iy�c���q�!§��9��������*�l�l�-�?97s)�}iǘ����i�>��V�yZW����!������ut������)V&����_fb{�Ϝj����V�7(F��4�O��z8���s�zћe�#��2s&g8��3�'�e?|�����^��>V掻<�H�\k�O��}��!}�~�9���}p�O�>s��������cZF������&�sѳE�9��i�����jN>���&������S��v
n��3F8���g4�X�e>�ґ����}���y�?����q�yc��N�^�P����ެ��Xc�����J�#�}D����m���g�L/�k��_
���3�;|��O��F�g;o���
>㿳>ÿo�ۺxR��?���y�3g?=�3x����Iv�aw����?���Ƀ����C>����F��|�����!�g����f�+���Y_�- ~����t�5�ғ����:�Ԯ�������/��ǟ�>��e���<�q�^��=�����'=�ǧ����<8����=>�(�1O�|?�~޷�G�����z��;o��G�g��\~��;�S-ٳ�kWF�F�g�-�%^�
��|�rN���G�ʏ�S��s��z�$^|��ܮ�PN��M_��>3��9zw�Jz��կZ�ӝG-n��.l�'^)6����ql�$��3��̕o�n����Ǻ�I���[:p�s칒5
��y�����':���Ξ��y��}���33�?j�4z��}|.�j��;&b��_L1|�����Z<|7��v7A���'{���~��7�>ϙ��_�Yz����������|���2���>v�9��68O<{������t��}����y�_T/��'��N_f���g���g�~���?���#N������iW����s�L��3�����<>�����_�W��#��~��y�~f�f�?�z3^E��9>��x8>���c�ߏW��k�4��~f|8zɏ�?����s�x���p�v�����r�ޞ���}|�ſ��C����d�̴덾��O����cӮߍW��S+��L��zoW#�ķ���~�5q���Q>{��~=a�=>g�9�3���O�3r����]���b5|�q,V���;g;���ƹz�co�F�o��t�2<��<Nx���~�e����g>�׻��y�i[�N��5,>r&�%��y�G�s\J���'��v��u�3�߭S�޿&>�_��=���~y�{N��[g��������2�vn��ɴkʿ�ӻ���gF�����p�8�z3{����8��Q>>�o��O��%�UT�M� Z�-�=r���u�L��έ邘�o/���'�)yT�O�$��
���S|�nv1��	�g��a�<g;��{t�M���]��o���]&G�����)��s}n��Nq���	����H�_{=�HJ��N�?��a�D΍?$ht��,��_)�v���yB��z�2��e�x#$I�����K��*Kֶ�ə�s���s�7���\$�{}���<�!��Y�o�KyL|����@�0w{?{�҂Q�EH哼���s_��|��<�~:�&}�C&S@{��<�J��39��o�v�g��tW�I�wҹ2�C���GI�9/-��~��;��U�=�C�_K
P���ɝ������+�Sl�ܔ{�c�{����|b�Uwn��\�s�&�i첟�D)W&�;W�2*�GL(|�}��X���]U$>����K3˕I�_��n��G�*���/�wF�$\z*?{XQz�|<�b|bο��=���3�L��O��se�Ӫ�ٝSg>2|�տo������_�nc�~d���j�S~咞�5�alG���s���\�g��q.�%��y#����۴zO(�O�c��,��Fߧw�Ӻ�E�i���teʈ4��н����W�*ܯ���3l�"L���[�ǿ�C{җ]w���?�Юѻ�J/>+�nm�#Ӏb�897�5����*y,i�"|�'��p���]~�	ңi�ߒ_{�߱��N
�����vEz�$>���>�uhh��!��<��n}9l�{����ܗ��#[���9y�����"��7���l*_������g�Պ|
E{����~�[u#���e�s�?b�yk�z����&�+�M� <^W9.����m�]����v<O����(�B��?�->�TnWz���~���
w���?^�˴����;�Ŋ/,TAֹ/_�X�w/�(�?U04�I����#�/��=K���O�m����Gȯ>Dg��_��m�o��iP�K��?�ш�9s��؝;���.y��N{qY8�#�
�NE�ύz����9/��8�_Z��Wo
6̓߾�ϫz�\��j��G�}�9�Y&�����bA,<�W�A�%\�P$�;o�<�꾀�ޓ��B�	P��Kw�,%v���4@�}>�S�x�G,`Bb7�ב��9�+#'[&w�GP,���'��j&Z�u��v�0�a�k��+��ݺ]�At�����28�q��L��$��\�'R5$#X�@hy�Χ��Ovx페R^��)�w�Ϯ���[��։�N5�~�zх�.�����ҹ�}�ыv)�=��c�]����)�O��U��4)		*	w������
Y��^��_W�uO�‡=�ѥ1ɳ��]zI.�4�k�K������#����P3�
�Q/(��>bk��>9;;Dg��� ����C~f���sP�Ps9��=-|�'�M��i��������C��d��͝Z#k4<;���%����[p�+�{�}lm�X����c̣����ۇ�g��=��{<�{�P���< �����QhO�t�{峫�4��\��bE��}���O8_�v���:;�1y������">��q�!���	�x�N��4�cV�{S�=�yH��z�Sf����Q�,��|��a�
��q��W5�L��6�ٓ>�I��v�{8'��H��������=$Oz���Tiߒ�_OЇ�;S���U��v,���ꏒ';�U_��!y*��F�ձC���D��N�FA�G���?����·�_�����޴f������3��MD	�@�/� {&v�o~��?����v����猏v�?zƿ؏*,P*i�L�5>�hX��=��şιyÇ��UA~~A��YI�ڕޣ��x��Q��a�\�g�G�����y���>�<��<������wqR���?�^���E5z{~�'�4�M���ӟ޺C�l�Ɓ�$��3|��������m�ꮟ=�����)>�,�O�#o}-��d'U�D6z�w���M��Q��V8��;�"��c/=�={�[��.��P�}n��$j?9�3�>�Y�C\%yF_�O��o��.rz��z����xȓ_�^r����K~笈o{:�Is��u8|N�|n��
��-�U����|�_3�uN����/�Oz44��~�7����3.?�|���*�/?�÷ޭ�e?]�|ć�Z���C:��~����͇�x��a�O����s7�;���ͨ�%B��
�Ƭ����ӷ���dć���y��,>����z)�B�}n-5�������F��}��_?w�ߴ����@K�g��c�T��gmt�Y�5��=r{�K�FlA����r}>7#����;S�N���[��g?���_�'?����9�{|�>c�[���u�Ԑ{������y-s̒a�9�t������w*�=i��+g�'H/���]��b��9�x?��Ay�����۞{Mg��|�H���}���1��ߟ�+�,|FT/�xcӔ��>�$���):w��Ue���/x$�a�M7=	:��j��!�����+�-O�<	�Az�h;�=z7s�
���*y�ٳY����3���~:�3zgo��;q6��m}=&O���(�stf|�q��P��=�=y^jW�\��]�ï���h�u#��xl�'�������88{�)>��ȣ]��~�(y�%|�'���.�=��_�/�O�N�3����������>��i��O�&8��r�3�p�e�O_���~�?>g��G�	瓾�����w|лsj�g����_�E�O���>έɞO��x��<p��/��~��F��r!��gϹ��k�b���=>Q�f�]t!��5�5zϙ�*f�����;Q�����U���~���~�"
���O�:6������c��7���ve���y-�\�E�GSX0���nEG7'�?�!6�qԫ<>�.t;����:+oN��1��W���Ny_���>�O.B/�y�T�o;�kڥ���3�t�y�X|�9�{�ۄ����_v|��WiO��#wY�,���vzo��W�b�w^nF?ڃ?�����1�(?z���w����kgC��B8k�F�x����~0�T-��W?�!�'w0}i��z;�s��B���^F�p����2㖻<�����Snbw�=��>��S@'���y���ӝM]�ܡ<p|�&�_4qCGT/�؉�9W/��3�ѳ�r�8�����n���c�|��睨���!�ħ\�u������KQ�w����k	�m(r8�Ɵ�'��Y��#��:U/�G��O{oP�w�ww��,y��j�7�|�5X��=�r�:��ƒZ���g��9�恏^�W���1�[_uW>�nֹ)
��a��~^�cȺ�j����綟w܀O�N��)�-��lv�]�z��e����ο�86����~���I/�rny�
�ߵ�X
(�g�y�?zqu���i/�
/|�K������|��؝S�୅ֹ3�0ăaq�	)�c O;c+^�myȘ^Ԋ����6��g�K33�2������u�O6��ֆ�F|�ӧ[���<�=nl�س>�̼��oݩ����;�d��Av^��'!��_���\��]~����'?Z��%y�v�Jy��TV|ο,O����U�O[���gr����2Q
��r��Z�O�E9���9W�<���w0�Oo�<_(�{�ω��帔[@�W�G����'{����
s^�{#�K�=�]y����sA���b�e��݊�b�:O����\�+�?�'{�{>vȝ��e��u!J��
�W��D��q�^`׮��H���}��]^����r	\�ٸ%����������I�n�W~�����b�]���3�\޾���t�P�;����i½
��Κɳ�]��
�\�خc�[|X0N�ks����l�\'�ݾ�m�7%��������ԍ��TK�w�D�ꑫ|��xw�{/��|����5;L�y�!�L�-�+�εK�F�hs���_+���|����d)+݉��N�>X��w�A��ʟ�W�����#�;�Byx��B�~� N�h��<���q�������^zS�n/9����n������@���K�����˝�g���@�&wɃR��VN��oK����*��y��e(Vr��X�=ʓ��A���4J���;��.�Έ� U�v�RwU��-����#���e5-͈5כ�
V��Ch|�����N�#֏�J)�`;�_�Is�?���=(�n,�����zy�g��2YO|T�
a_�т�!����`u{!��E�M	EKj�;[���zMm�F�X��2���<p��:��\���R
�A�ן�6>�ʖ�����)j��h�F�}&;��9<���w ���(Hc�
�(4r��;s���茻�{����5R�q���4_a~S�UYd�E�9R�Y���QGR�����R�V{�<� 7Ӛ�r���1y��>�d�KxO$��	�^U��s�Z8�G��U�70��,5Vs����
�Nf��7y�?��ߵ֯I��U�Nv�m���
����R-��!m{��go�U*�>�d��ȃn�4�����K/���wW����௼d`44R���g?��=,�~o�G�(�m�ȿ�n��<�@T|�՛��n �ό
�Z�f��Е!����7�.
���
\r�w'u����Y{ÍQ	�vbѩ���7���E׮pFO~��6{Ա��LV8W�=���@�[��̈C��n�4tl��':|�6E���w��m*n9w:B�uv�]K�_��fპ�=yd�5+k+��HgSC��3q��9>�����Ww���F:1k���'>����.9%gy�K��+*�ǿ5�)ߝn����GON{��Q�4�B������\��'ς�����������4<ˡ!��D����S�z�t�S9�C�k׾k�:��o
�<�g�_}�t٭�6��"g9:]k���9¨W72|�KwF��^��ɯ�侠�y��a乜���r�6|�:�k�L�+�'�M��;���=D��Y�!g��YYSz��O�CU'�H����n[V�Cw茝����y�'y,�g�ݍ:v��Oq���'��tFv��:U���gt�O��^��:˓}*_V8�������=�#x�sOzg?���;�򻱫�O|F�+�����G|^Ov��<��ȣ<���O��A�!�9�	���$�7�[�?]��<尶;�=�O �z���Fq0�$:D�<����[;����.��\������ZG����g.p[��W�<��9���8`�e�Ξ��	=���f�ǝq�"��,|����o�7���/}�oj�<W��Ғ�<ODmm�d�����>�s��|��:���Yiz�]�e��<��`I�Ƀ�������駒��$���>|����&7G��|J_�Y8/��N���=>��)/��_mk� S��*pf'�G���| _�&|�%7)uI~�A/ō����_��f���[c-�o[_���ϖ��p�a��s2���N�
f
D���ڎ��L��vt9��s��޻ֿveo݁bxȴ�����<P�$�ʓ��9���?��uR�f��Ɠ<�7�����.��ხQ�'yZ�P>;��'z����3퍏������.tO���H�;����\>�MT�s-0ɏ^{�����?�f�Ry��ȟ��^�ùv�O��������|�_-�=�c���4>����Ov�8'W��*����̈́�����a'�{�7�shynɃ��»�������N� x�
��߸�9F���.|���mʣ'{��]��n�����Je�<�
�g���4_��Y|�"g|�����y��W�HљbN���g�[��O�۵A�U���ؕ�p��/��Ϟ�38�~�GՍ�+q,>]����?c��?�ʞ����b�$?ō�p䩿��<7�lr��kڅ>�C�%���rn�߷޻�����kܴk�rh��/�o���e���W�0�>׉��E�eq=9��l��;z���d?��� ���<Z|�]|6Ϋ�>?�rV�W�Y{ѳ+~ƞ+_���>�'��_�<|���%��������ǿ|���sȼ<�ʿ9?�+Su�T�ٿ&��~���~��l���a|���5�ͷ���O�QA�Y\���U����ñv�?��4bˉVʳ�k�.��F�큑�g�2�1y8�w<)��?�����g.鐸�2e�"�!p-�IH��H���<��
t��W��2�#^o��Q�O�s��U^�K�M���+�(���;��A����;0��kv���RJ�b�V@K/耶�~З@ɉ����m����?�t=|J�m<�Y��H|���]�0���@�=�O��-y��'>7\��'{@W��~p�r��*:<���.�V~�i�Е?���3�����q/�	�U~��§��!�ϑ����v�;:�F_�
�x���O_�}�EyAg���6��9n���?��@J��a?��7(��z����sG��:��/6���l���X�n!�y!�p��Ӄ'pL�n�՘���~�00эBP�����xQ�ds�y`��>�
��È�_wy�}��m:�^o>\�׳�v�q��KCQ:������{�n_y8���╯,_�y9�6�By�(�
��w&Q����0����kv���~l{.'o��pm�W�s^ݼC�����E(�O�J۞-�X�sP`����vA�c#]��x�P�{�i�>KNz9�Ө����;GH�W9j���>�U�.t`v'˼Q�Qo�,�����5?rV����*�/T���'p�0��v�V|��.��O_�)�QFvҞNv��=�	P_�<p��>�?x�c�(�2�ծm������N|�u:��/;~�����w~U�E�9W��v����Y| �]�k�E�l	]d���~��}gͷ����{�?r ��=����1�6�Qf����g'��� ��,�ه>�p>��^�$�\3���ݲ؅�W�.����i>�;�R���s��.n׬5��n7
�=^MK_�&��F��3>6����F�,�������Y����'ƛOz!a��C3|�G���LC��6����|���M\�;��ᦼ�J�>po?Ň]��C�}S8?���N�Z=���|��Fß�a?�_.
��� �8��9)�E��./|����_�|�r�����O|�.����.���ܹk�>�ڹ/kB���C����������c�ڵ�(~z�_������Liwӫ�x6�S��9p#t|F���f�]'�92b�#�[�U,}���Zv���o��q��⟾V�du~�_��Q
2��3�5չ2ϸ��]~}�P��u��Î'�/cx���s��ep.��~�G�Ǩ䫄��e��{��HQ�|
�m�tVO���
�^,0uCs]�uo��z
c֍G���[�mʪ�+ж�1�\�<Ey�����	! ry�kN�Π3�Y��ݯ�y�O�:}Pzs*���^{����ҒU�ӣ]���D�a-���Ӟ���'|82q��0U�Yͱ�@���b�m!1�O�!���D]���"��O�\unJgBvٛg����#�m'���p��`����nO�"h%��wJU��3'Ϯ��� ��V��8���7�$�&�N���i���п�
��ga��o�����[_�Ht�Ϭ_��E��ϛ�=��
7�����d��5�%$���)tLz��<�c�K���·?~���["��߹,`O��G��f�CL~t�г�9�,������^�t�rڔO;$�p�w=>��h?|�^v�\�Ġ�����Y��%�!�I��*��d���v5b��ڐEF|0i�Y��b�����V�iY��'9����H�:\��WJ1���Zv�%���f?\�= ~��c�rŰ:;�q�k´�8�.�yb=�S�H�k��¿���g?�<���d?^^)?����3�Bh�k���k�4���c9w��w�
��m��w�{aή4�hq��l�����}������9{��(�wd�Y�����ꅭ�AA�O_uA�U�Q�Ow��n;���j�\�G�����n=��w�ٜ5���;��T��͙Q��5w
L.K�#|F_B�Qy��/�c{>������T���K{��>�{w�To�S.��
g�?'
������Q>=b�OS2l�\��M���v�W��w�9��L�g}��V�kWP��>�G��V�bʷf|�1���jW~���Z��W�^��zkW����<�|�3�hѤ����?�g�"�,�3�����9|����HvW8|�JA,�|���/0�|�F��!z~Z|H�I�v1���/o����q���!�11�F��zdv�>[�s���Qb4���?G�6�d��p����q�Ӯ�>�]��(L�z
yV��ϼ/�0��4��\C>���[����i-ސ>�7��q��8n��jl�}�/����W{f���7n�}�>���E��s��'�۞�������',H{�S|�ծf<g�_hf�𓧎#}q-ts08�f?{��=�Ň<@�]��
q��_<�8��5;�yL�b�<j��c$U���-O��\�=�N��e�H?���Wy&�.��{�჏EA�S�U��L_�����+c�؝e��ˣ�p~��˴K��s�SE�C_w|�]��e�څ�8�F�*O�-�6Q�q�~������D-3>��<�G~�G���zM��0TO�Z?��i^��忐��yuq����/&�%O|zK�`D%�~����X铲|�rnC�����ˣ�z����F�����Z�˥��?&�f|L!
L���Q�t�&J���<}�g�;�ڛ��䡖���g����Ԝ�_��!��v��c?-�`R�����j���]���D���:����'�b
��������4�����������;����q���˙�k�\����U46w,n{�ټi����F�
q�z�8�`�4}�5��9��#!o�1q��c�
����I_��&F_��ˌ*}�f��qv��=�i8�kW��O	�[��C�ɭ)n�_�yo?�7a�;��3�j���r�������}���<>~r�G؎<����C���G��8�t4˄�{�G���9�:O��4vX�ZzϞ�]�7fܛ=�y������_���O�2���'y,��J-�k�t�K�u��ɉyqCy���~��s�M�ˮ����ڥe#<�+�=�?91��g���c�Z��0}��G/�ٞ�/��Tg�ȓ�=����ű�/��-١����v��3�G�+�8l��s�/�swҜ��g�wrAN��SŒ�#V��Q�k[{?�u����S�!*9�Ѩ�~0��ㅳ��Pm��ݿONF�G�}wR$O�.��W��yЅ��y�	@l=�c�oo�y�{�#.ЍL������ް��).��3�j�z~�~w�w��]5:�;_d��<�J�i�'�;Y��ؐ܂ϼ�t'�+���h�&{K���邛�{��w�/���xX������G7���o�b?s�������ɘW�R��-����y6�O�8���P��Yc���3�������1Wo��s��a@�LJ���γA��O���/��uM{�_���?xz2�e�3>����*թ~�a'���v�K�e�sx�����M<<�j��s�O�z��NS���M�~d�u^?�ٹ��S��Ppx<�C�����seF�L�}���S��O�cj5�(�����{Β�+6���!Ӂ�ʳ���ƞ-"�'����w?ݍ;�Ρa��ô�s�0i��>�{؏��I뇤dߛ���r �;�Oj��쿴T.D���qc����Q�j���=�������*�'����U'X��0��a��p�}��z�r��M'Bߕ���#O��ς'�p@U��a�������-̞�F�᫑�Azq�|�6b?�n��]ir��d�O��s��%K���Ӊ�����;{�>b93�����x�G����q����i{��)��S3y�P(,>���~s��L�Q4}�/�_L<�/Lw�6�f��$ ��1OOQ��1��AL��1�����w|~��"�������m��Hh0v��!>��!�˕_o؟�6��SHq��P9��?2�����z �';I�r
��6:�}�/�q����:���޹2���-Ha�zjo?�Q�	:k�.��m�qe!�Gݺ��͢ڬWg����ѧ]��w�@�S%x��tN|�o|0g��-��2�ˆb��Dǁ�r��+��{��6��k�r˩2�5W���y�E�K�\�e�$��
�z�-ϭWI�[�����]�ۯ�~VbM�}��?p^���T�uE}E/�7�q���>�'���>�[r�����=o�/?��q)N*E��LR�7�g���
�����Cg��PMg;c,йz�>��
	�r��o�C�H:�s>�p%�QtR��p�{��aǍ��#a^�>?:��qr�bg;�g��.�{�2�,�w��a|9C2���۴w��.���mo���G=�݊��g����ect�M�™^6��[����
u>>}v����:���Ctb+��i���}��>'���?Ww��ƍp<(�fm��}���ƞ�s��'�w��
���h)�_"%�h��%��8�]�\��{�@vw��<y�'2W)�q�E��a�-p�6�"�f��m��~����**[�i�vb�%S��G&-&<W��x;���s^oƥ��U���\>,���
��7ok]y��m���n�=�������pN��'|�� i�$ժC$^�5��+]SC4���A&g;m^AǼ�U2��0뫺~���dn��x�M����X^�z�(���|�<8ʇHԗne�Fl3�L�{�� ꯠ��W�H]�%B����xI퀿��?�V��d�.y�.'�s�`�L���y�q(gLgX�`��q���J8
Ag=M����9�&2�ܠ��/:��U蘙N��Lv���z9�
�d�|�/�J~�9�~ 8���>��]t�X0���e�E�40J�H��W>���:�|��~���:�����>c��̻cY�۠�(�⡍{S6�������''��j<]��ζ熜���x\/��Z��*q�1|�Sbh�F��Y�Q/��[�ٮg�7~���g�؉���B�'V]�U���b�X��uB��� �/�?��bK�T����<����[���+�<X�<d׋�Yp�k�[�L��=�s�tm�5Ew�H�]%D�_%���v{���I1<~��Q�y��֞���kC�=<�9NV�bI�Y��qܶ
̼d<���R3��F���-&Glxx��+q���>�6�/>Cߟc.:C&�0c~��6�[\,Ss��=M�'�����R>w���\S^Y{�˛���fZ&��<_;�e�e{�Y��i<}�����-�)Q1?��\�Ut�>�w�m|O�ܗ���R�U;i҇:VI��q�mǖZ�I����u�>re&I;p�޳��zU��
��08��P����kK~�j�b��gi��Yt�y'���I)8��I/Q�מ�e�S;{�����8](�����V��<o���~�;�鉿��w��W�$��¯�
��}�_�؍(c	}$�f�?|N\t`����|�d�\��2�e>K~f&D{�o�>XKX��u`7�Ӓ�?�ɏ�+$���I�C�����<cA�����/�k���\@v&�g>ͫ�<�_J�o���v�J�?�A��R{��qp�"�H�6��(������3��4~�e3=�_$�@�U7��?����d��bJ��㷬�?�N������[��\76f��.����HV��.F�>���Q��e���b^Ee�g�9��8!�L��kw�p*فlvk#/�f;_ۮ���;�[��A�y�~����s;=�R�{���mW���약~~E��ڽ� v�3�v[���k�����cߓ�r�?kB��e�Y��?�g�^���nH�ى���1��ٴ�ѿ/BR���~�%��'��EH‡A�Q5zN�K�\����c�ވ���c�'X��
S����|�>��o�웖L��敞‡�v�S-��e�!�x>sn�>z���?��J�>�c���"�����<m�=���߮5YBd�F��?�7/�}w����d���w�
�u{o����qD�b���Q��tL2^Ug��?�=9�"S��St��U#��(�?��j;�?�'|"Q�\���|7ߖ	C������r�C����|��t�o۫\���
��,����[���_q�~a~
���M��)�0��.�}�FO��A��%�-�.�3�|�鴝-1?�~|�_�uP��e��D����rc��a�ҹ��r��{�5�
&R ��'��˺܌B&��Z�*#�ѫ�rm=9����G]�w�`Mp���?��5�Nu/�{���rX����W�wO	:�\�
,P0Nz���R�y-9�H&��e�3֋��[��}�X9����
�h��W��$�D.���Ks��28�38Z���S�Vpz�?����&T9���ѿy-|J[�_�#��W�A#'��_g.��wp�;9Y�d��s�g�Vf|����q��nB�z����τd�i�s��M/���c{q&���H�,�lۮ�|�]|A��'��~YPӯ�ϊSt���_�m�~�׫ܔ巄Opֺ�sXM�s�q���c dcnt���v��%2O{��^�)..!��7��FN�=�_�g��	��^W�،#D�7�鸜��g���iL�;@)�!��/�eVl�ev><[�a���}o��Lg��<b�Ӗ��=��ӹY �	��G��n�������i���.;s��u@�g��;���K�x�ʌ�ĵ�3�k��c=��U����G���~���ד��U{�Me�|������ǻZ?3nZ�vTs!	#�*��<��Ŷ%��3�v�V�S ����r�{�</:C1:{ՙ%��k�X�R�_
�*Z���n��!(T,8�>��Hbt@h�~������OS�>�'����Wv���5pٱz�4����/RB���!�����M�r���&���E�
��\�����~�g�P�~K�SK��8��v��r���9�ؼ�4
:g�*C�rͧ=�x��U9�
=�N�{r8��y���6YqIO��0!Qrex�W�����~f>�����S�^�l��<��`wD��Ӗȟ�c{��/���BZ=˕��8�d��V��h�b��g�+���U�3���3)F	�0߸�t*#�T�}>��jz5����!��=L~&kߩ��g�
|yKn�Q3>�!L*S�Rs�~��E�%��N�QW�<�ߧ�t�֙J�6K*|�ëfj�BOo���+I��]r%�wF�c���k��!?�T�vk�Ύm�|��lմ�O��Me�?��3��z���T�F|�~�X|/�%�����٢��_�5���n��bD���>�x���^"5�u����7H��أ�(o	^N1�A螰�Uv��g9�l�
Y��P�����e��[�퓽V��&|�ω�s1$y�g~B��v��>�/m���'�Q�����?'���+~y����7c�bD�W�M,�,?���k8Z�xJ����ӿ�f�c��͸��C�oj���i�w�י���Y�w���#�ѓT}G��<D�o�1n���y>+�`�i�b��漪�a^�0z!*�:D2�l��\72'��Wb5�\�e��ѹ2s���ڈk	+W&���<�Op�?�pg�rl�����z�s���s(j���&�Z:\�T|��	P�_�U��۸j	�ճ�^q�����
:��G��_������Tll^**��`����
̩u
���%~9_.H!��i�Tr�����D۠�i�a0�E���jH���U8���k^vV�~�B��t�;��K�g;��pk>�IYF;�]	���XS�G�œW����Y�C{�)�ҭ<"��8�F�(���Pg;<����%�.:۪u��`?�8��2�N����|�KZ�8�E~����0B���L�.F�>�+��ɧqm�i�p��`�H�v�����-���g�W���nT!f'l�c��r��6����j���.z�Ԍ���5b�h����|�Hh%(sKJ�o&	�%�%6Tp�3wt���F�Iu��zZ����2� ���%}hjQ�ѓu���C@B_��qgUp�<t������؜W�R�Q
R�]c?��&�_C����&�����w&�����K�.��ߐq��‡� �4��k�*#=��n�e���|_
:ehXB2��$�&���/$�7/1�A�)�m��}��fw�r�, h���B׾M5�>�X�nSn�%�x>�9+��Xڋ�V2��	������q��<̿?1\���
=��!����|�x���@�℣�ָ�j;[��w�(�'�wQ���10�<��w��Q�����|�3��>�k�^��‡�kO�ds�"�:�W֪j��6�
Lp�^G2(i�Ђ�_�y��d���ټ���G���W>��[O6N�v�}Wd��6�(<9͢F(�W�3[t�y��a��c�J���Alo۫�i C�n��߲O��~ߟ�~�3�>bK�!6��d�����Ÿȑ���'� fV�:��C���l-/[ۺ?@�Ā^��%�`�0S.���m���EK�����Iz���ũ=w<|x���z�%�*�Q��}��>����,Vr������-|B��:�]Q��|݇�ݶ�a�)V���޻��_{Bq��U �pʙA���؞���j��\��1������~����s9s��4=b��Q@���j-��S1���=�9��\��?���4|�Ow�q-P-�ܗ6oM?�7��+~<�9�7A�}l��d��?:e�D'�������^(�Mѱ�?
��~�+�ykI(K�.l��.Lf/��d��[�.����zx>7�_�z���g�����:k�ڏ��2_>���[_*:;P�v$��>�F����_���\ϙߊ�K������E?�^��΁�Xm��_؟���sƏ���h�5~t�O��(/:�᫯�zxx�.�(�\�YOr���[���~�>�;g�vz̓k��\d�d/�U���0��|�E��gr�/ȕ��Q����׾_�3{)?��s��0{%�zT�Y�Qg<'=���g�*7����oTn�gy���G��r����<���Q>�nu�s=��s���j����ٯ�]���$�p8=�N����$ׅ�ѹ�D��%��"W�I��#襜�0�(���7�YR.ŗ_~9{)���L�3~��fr���~J+���8�����OxޚS�0?Ōg�\��g�'W�h����9�5y���(���
��v���}k<�k\|���ϙNA,;�>rP���^������S;>\)l�!<��O2��V�'�i�ߚG�DJ;���.�<�k�*�5�2Q�X�����r�nVt��<վO�����
?�>��6|�Z�K1Ʌ��<����.�ĕ`&D���+"�%��<\����C_�Z�F��&�>�A9�����YП�θ~w���ߩ�S���������!�گ�����a%���{�����?'{�����[ݙ�{'�#LuR��^^x�n���u��?�s��O��p����Fǿ��v����.�+7ꉎa��^��~���r��2�k/��sjO�8����g*%7=l|�t$ۢCu�'���إ].���O^��_���|z�c-�;�VT�ŀ��N�p�-����q����6p�cq?(�O�ԾW�oO�59��qL|�}��~8�S?7����.#�~���%�Ϟ�_Ň
�.q�ܾ��\I���Gv�^���Y�1�]���,N�}x^�����\�?
��N��D����Q�|�._�Fz^{�~xv7^޸���j����N�=�W����g����p���^In�/t�b�=�O����s?j���8.=O?�s�6��8���S�S��8�������r��d�ō���W�g�Z�o�>�.�yޯ���Y?g��υ�OηL��V&B���|�'W��y�
=���'/8��nv��ڋx��6��������y��
p��g</ίӘe/�Wgx޸l�Q�I	��e�}Ə�x���kx��s������ֆ'W���� 6=g��Wv��g���tM~���F�������=wϺA����)gb�2�?�K��aL�0�<���O�N��N�Lt&�����|����p��L.~J���&������.�Q�M7Ǹ���I�Pt���f�[�w�v�H'�?Žk�:�c����S�l���~����g&b�e��������G�Ã���f�mw>���3~"���u��Ov߹DF���G��Ɖ���W=����'QB�BF���Eձ���"��)�E�jA=��_�{���Lo�)!��,���䳟
?�q���o��/�!��x��a/ ��rq�5��~����������U���(vM�᧜����[�A�����|v	�Fy�R?��<��8b��@�sP�dz���+]��A��[׿S���d_8<�۬}��Z2|vK~�~�?*�~8��{Λ��s�9���H�)W�+���uڙ?껋�
�W{i���oˁ��a]�����xo~>��F�x�K��OZ׫�*�q]��\C�vsb�v���)^���t+t]�.P�D� ��j��ж1��Cy:e`-���|��_�N���C�s�����쎟�:o��%�Qy�A{J�P���ڻfw�CUL�4����=4�[��8�^8�m���r�:��݁�PP�������ϝ�$�K� ���B`��~%w0�Z)F=��m����D��qAx94ū,�wKP�(?��S;عhr)v�q�J��9��t�+�y�ma�~�!�1�(����^�;��\���w>���P����b�M�Ox����v˅�Wt�nD��wk+�����Ň:���?����y?O�������몟u(piQ�3?��|��j�6%�]��	�ޠ�:�ُ�s2���Dz�G�y=��߀��B�c��i���;
-|9/��'bRKQ�,���p0�w��g#�A�w��]:�;\0:��YϺ|��Q�������)	Xb��LL�E'?u)Ax~A�ݗb�p�9�!���P\��6nt��ZAq��o�!�:���7���C;���������`L���6�]8�P�<�����侐��LYnP��C��>ٱ;ŎO�%��ׁ�V�ҧ:�BB4,�ӫ�Ъ�a3�ϖd����c���[m=ya��-�R{��v>�)���7w
��ϥ���I׶\tbU�)��ϫl�en�D���y���W�v;eǾ��`�[F5ᩦ�����
U2;V�	���
�tO��+>�ݥlI��%:a�3��
��f˻'h�
F�}5�7�~���-�FIל��<���:�)�![�…���~�@���Տ�bP�����arG�%:�Y�L�h����V0ƪ-Qx����^�
�h�����PlCk�HԚ�C_�~�1���"}5؅#h��9�W�9zO=�����r�`Q_���o
�~�E���_����"~��Z�����ѯ�7�&Q�^3L�;����77�(��^y�n�<��ʑu���e�����I3���]{/i�0u?~�����Rm���C6Ş�c�W�Ǟ�r�ۤ��$?=�r���
��<{XP�R~���O�����v��=���k��,?V���B[k������Z��D�V[��8�d��5G��X�vO���&���ŏ8gT�Q�_�Rs�N�k�λz��}t�,)g�K9�W�r�7���d;C^|λ}��f���coF܎7��C.�&|�=3^+�E�ґ=�ߓl����G�̞�'{��G��b�e���ǟ�=}�C��7o�b��#�lT�Gǹ.�G'~�yʕ�.��S���I'�y�c��31��z���Y�r��^����������(�g�k1�El&>�ӻ�jVqc�3�)�SrM/�]a��W;>.8
v�L:R�[�����5���|a�������s��
��=`ݿ������3�W����(���E��\���נ3��:�P�����k�C���-�R| .>=�,������Yp�i?���y�+:�
K�G:r��w|�J�¹�K:�<�Q.�af6,�P�=n0�����������8����8Ǐh�g�=K���;
��J�k�w��d��I_���/�k������vr�r�	.��/�Q�p��ӻ�N���D�,�i�?���4�v�b�x8�yg?���������C
�Cܦ������b�҃��H��)���_����vB�AX�dl١�H!j��mtd���Ə�5��Q"��,��j�Y~��~�u��k�Ё���4��O��D��^�	H���R���:���W�[Z�)j�P.g�u
��G�+��uڹ��Jql��P_:�5�r�~��ݿԗ8[��}�3ǽ�G�Y���z�5���E��ߛ1n�q�e�?�����ry�	��ϕS��Kql��sOt�s����o���?/p��BF�w|����]�?�W�W�,�����R��qE��/0d����%�7��i�>�e%�����=���D�y�y}�t��y�xc���ż˒�K�}�g�;w�8�����sW��B$:uU�xc���gԨ���q�s��NB�e�ԩ�#��Ov(>�\z�N�q�v���Y�{�<e��<e�w�:�Q_����[����`r�5~�si<����Ǯm���i^9����ʵ�S|���~���\X��~*W���|�!�?�����[���O"�L��0e�aݏu�?w�W���͸a��ƥy�����?{�rM|v:�k�#ُ�w�z�`&d�ʵ�o�w�	�0lU��֣� /�����}[�r��=V^�
�,\p���I|�Y~���P�gK_�|->_X�j}�M�/o�5��Y����%�W:��'�����j��	G˜�9�u��~Zo�~5�v�:��I�����~�[_
R��K
����ó8�����{�v�:���G�(G�T�%&񡃤jϪa`���6������ޜ�3ׇC�ܝ���8���������7狏�)|�+@}�!3�C�r�8Z��7r�[p@.�>*�9.�[�< ��m��>�'s��}�P�uc�޹D�x�o�\>�nN����;��/�_,�EX�i��vYvk��/xD�l�:	F�Α9?�Q�8�n��w�ݭ��THc�!Ԣ���%��8��8|����/l�EE�ޜt��ԩ*�������|�2냏��HO|l��j�P�i��N�<s�N�ڗ�\�:�1]�|��=u�E=���Y�j�����n�e�E��q�jާ�}��*��t����V��ڳt���}x(����P��Lq)���v��+��qL�r'�t�9���ɡ!��w���=q�鞙j�Kl�.u��4쟝|�Ph�
?���xQ_��%R.�̭�޹f[�Gפi��b�Ͷ9n���4���}$v��z5>əS�o�Yz'����/���G8��X���
���E?ö��0k��s���c��ӗ�e�G�VZZ2��,w�q��yg1�]�
��=�B]���,g��\��9���ij`,���7��4A��\��XSj4����$(��羣,B7�?�S�ᒔ�#�|�
y�O��)
�uB!�iʄ]�']���ԇ��Kۭ�O�:��La�ء��œ��s�UG@�xq2.\����8"œ�f��?���[���+�B��vϜ@u8�4P��>�~��K�P��P�C���a� ۥ� U�.:�k��8�Q��Sy��_��5U�/����/(�;'�q��,S�� o�c���	oXO�o�3��)<��SM���$�Υ�8Y����>̷�@H!<�	���E�9��,k�(&���R�R0}�X��N��#��ϙ����t�&v+1����8���
'���lyD�R������\�l�"��3ڭ���g��srM�d&��#c��Q7���UQnJ����4xRn��j���
#'��O�m�G��`�rQ�����6b"��u���+�?n��]�<wgNDi�v!蚵�8�
yQVk����Wԇ�My�W�C���W����Q��c�1��H\��w�3�Oy����t�i
�Av�Ԩ�2��`C�+8o/ı)�E>{bʁ���D��1�B�]#vn�K�ę?�W������#.�`��_�HJ:��ևS?M��w��*��@g��[�����	�t ��E���J���=��L;���9w�D�@��];���'|R��Wħ��l���­MH�1�8K�@'��+����];�57x�G�=�ŧ�Mc�s�&�C?���1��=C
{��{���X�ŚBzW.� N~�O���5���ȏ��O���t���O~�+�NqC#��ֈ[���(�t<f�#I�sx���+7�.v�N���g<48܏�zW��1N�3΅f
p�ʞ��8	�%��g�-����l�����v3��Ź
��y�g��|.�$�?�����҂��{�ȷ|/?�Oz��Xu���Lj?�\3�0�1�@!{v"�\�3�.��i�-p����&���k�ُ���e���p���D'~����I�_���">;��5s/r��I�;?�k�K����i?��O����܈����u~�S{����q�~!�s�':���=c�K"|:�����K���O���ٟzgS�#��].�g�qv�7��J���%{^�U�0�E����t��~�Y����'K�	��O�,�aH���%>'�qc{��k���8ЙK�}��{���W~����W�w���O�%������/�'�1�:��=����Q��]_����b��]_�R�3���,e�ُ~>���ҿO{N���x5~������t�����Nz���B}5��Կ/����3�;�����>�ʞgJ����O�4K.x��Q�������>_�Q5�1ǫ���/�jϾq��_Xs�/�/����>��g�~��Iy�����ywSZ��
_ҙ�A�n��M}���)��w���K�j~��ϛ�z��Hj�w/��p�s���˕t*_������_��&%�|0|���Q�e���G���=�8K�~p�eܻ�#��e�n���k�f�ޜ/k'��d��s�~��������wף\[�W�C�Hܸ:׵����S�t�6�K|v{�Z}����s۽4���{��_�a�c=s���Ɛ��\Oy����g6817Z��yT{�:-o�yw��]Xo�B�p�Ό���u$���Ws�7c���/(�D'?��w��I[��~�Ր�"���\�45SmN~���?gW��Ѿ~��\�|�����l�w�������r���>B���@ٴ*�S>�S�C�-9w�t����P~�����8�vH�H��z�qt8��yʫ��*��ޖD�9AG���b{��LP�i�!�#8rM�i<|p�s�r9p>������t�� �'�P6�~�f,�����X'7-U�����-/��j�@9[T�#�ԗ��b�ѳ�B�`�|�?�$9w�c?��绦�1'����V�4Ǒ��m��El������V΁��}"q����8��vK���~e��ƍ��%�S�,a����8����Qd#�����&���a\��>9O	���fn�SW�J����8��1�I}�}�~�_�3S�s�G^(>�����r2�&D�.x���ܔ͇��/V:�e��>�
R.��-����E;I����W�ɏ�I�������|)���D:��/$�#��R8�������ٱ��g�,��L�񣝨/퍿{Ƶ�&��(��HU���5�̕ў�=��~�5~�X����m���\�L�ڛr��я��<w���c�{�I8T�䇿�2'���#����8�c��z��+��>y'�w�ޜB���y6d�@?<Ex�a ��O}������A_:C_�`���P�S��?���y,��q剿���I��tL܁y�ʼn~�Z���m�1GGo(7���H�޿��C�g�Ͽ��z!�F"�PBwzq0�5{���V�O�<�KRj����ޒ0E^$]��Q������!��Ĝ'份{�?�oR�r�dz�3��(�&oϦ�G��ψu2B�EX����/�@��O���Ϟr�cL�`����w�̠��1f�9��??�	u���Ę�d4>�,q�ϫ���$����0��\�X}������
�d/��ɞ���Pz��D*�p��H7$�
��Ic\il�*������n���1��:����f_���ϗ�{ގ�h��N}��P�,_�B_D3�>h��������y�"?��;�sr\��C��氟���s
�
j�)�C��e�b��:Ö1F�=P��Rf�!gы��)��>�p�s�p����� #�q�'΄~��"�7�ϋ��G�[|�"���}�|��������r��=��	7l��O�AX#��!���k���v՞��E(琎�T�;�5/-󤃾���/n�A���3cb�{���3��n�������%�<
�0FX��SrN�`M{6ýȨ"���L��Ϸk����i��� �Wӏ~�ON��h�������
&�a<�8�ǣ8���������0]>;gB}�wLw®Ɔ����s���7'<�AޗN�����=��k?�ߘX��-���+�s��K�d6�'���#~q2��g������I'|LrR.�q{	k�rQg��Fr����ȋOv5��Ψ���~Z"��Z%�`�V�?�A�Ùc<}��;�y�]�57Q��'�{Vs���]�]_y��I�������.���o��r^OB���O������$����q��D�������q^�'L1|��7��9�9�s�RI��K;l�3}Y�S}��/�M���3�'����b?��'�Ez鼁�4���'w}5��8'W+Y�]eϼ~�^��O��g��W8�_;?g��|�c{�;?�|�Y}>���~'6r�75k�8�����^�z�E�N�E_S����E_s-#:������B��r/������g��Z���>�
B�אAa�?�@7�]3S9���/���������Oε��q�t�\#�x
��g�z��ǯտ���
���oZڔ+:���yڏz��4W���$�0�_2��G�{��ԗty���7���r��f��0w0�W/��
�t�|?.�sq,;�O�K�I�s�,�~��汌[�Q��}�9�5�*Y��$���G
��^?ٿq5/��HǗ�7��_�at����J(���AF��\����O��/�뜟f继�v�#����/̸�n1��{X�����(�
�>���*~��}u�̓cdi���q��v�8����a��#�}�2�&��$�P�8�+�;�_�z=�P:��)kg����s�;�����fڿ���ݏ��;�J�i���Y7n�ˣ3�ΐΕ�W-�����\������Ô\�y�_H�¹.>˼���83�f���������mz�׌�s�x��8>,7|����<W�91������笯\��ǟ�����7(������l��^l����ώs�9�,.ŏt�ϓ�v��3�����K8+�W�x>|���?��y��/��r��v����Iv��a�g�	�����7���ާ����?�0��<f>o^�|p�n{�.^�wgA��K(�|�w�_�}���<�=�S��| ����h����%س���q�O2��o?��w��o���+0��a�q�\�̞Isy��ы��PN��g���e�˷�s�����Yٞ���0��dd?�vo��g�:�GYp�:�̳V)�#c���z�Z�p�n�̃��9�|���ϲ_�8������u��L�����t����!M�*�m��`��k�C���`�����R?�iZR�枽Ox�I!?o޿�P�9HſuuX�'lj�!�F��7��l�'3��z)L�寯��J�����/sn�A���׷�4��H�p�h�'�rz��y8�B_�������6�D.��8�#>��F�H�E��O}9����c7�s��Q8�+���*W �;���/�e;���}�c���O*�>k���;������/��:�����~#��~��
�����`�
�h|�/��!�|r:�W0�4���ϫ�����_��3w��>�N�:��>�۟�O�z�K�v8�f��&���u���'��	�����\v���^3&1v��?��h
�Mi�~�pn�a�:oB��	d�G�)�5y^"�����=-$:�3�KR�n�=#Q�{�V��=��@���׏�/��:����<�N�v��1��K�y�����
��H0��`��vŇ�Ş!�2��Η����M���3@ɌLٷ��Fc�<�����7e�+w�x�������#�,�/U<q���b��;�O�ߚ2P�B�?���*�	%�^f�4r�@Ó�'U�R|�rN�p��]݋�����Qp� ���\ҡ/�`9�l&՝\�"�����r{}����M���)'�����]p�

Z�]?dlP�}����*lpF�(O�`�I'�퉯�zC���8��Y7�Ŵ=�e�j��"�AN���80��Ch��
COZ!N5R�]y�H�����X:&�8�ay��<�X&i7�?�B
��>�A���S>��3��ٮ�X��J�\�2!�(�;��|�2��5��0�$'�%
x-�y�%�7~q}�%qx5��B������ēi�5��=@F	��zTκ�w� �yk:���-.�z��c�<�"���CC���@�_l�*�<�ƻ��(Ms���@�*�>���Y�=*�B����v!�\��"B�._�6�@4y�m�y�����oB�̏��1��x�A��ry���^? ��#�R�`��w��Bڥ�3����������i��6�B5��#��`B�	��OTj@i_U^�<2���9��N��D� ��%|ڄ�ټ{G.�y���}�)��!��QO[{t3��ԧ<}�m3"����
KY�8ry�r�$}��I��>��|>��s��;���~ϡ���#=?�ҩ��
?�9$_��;����*nI5&���t�W:"��Y�K_�8��|�J�S�G���B^���B��ױ�r��}Ϝcď"���{�k���'��3ôsc���c}
w��{8xNO�sK�>��(�t�����0����:�\�8<GG�T�z�B�i�L��r.�B'&������r(�_̽���3|&?R6tX�bRr���G��8Ǐ~��5�L����t��\�
��[���<e8�,� ��Χ]���C��N.�(�ߣ��,S.��:t�
~�?���96�S�w:��Ώ ��m���z�#�Ʊ]���9<>�C[��b?T������k��O�7|��;z����ޔ+W�<�O5O|n��Ϋ�Q����I����/����s{��-΋���|rM~��W=��=��V{�b�K&?Z�O�0:��!W톏���=r�8�?���kF�q���鎏���#?��Z_:��|����Փ"g���w���r�W�?�Giw:�+w9L~�'�X��ar-�.���D~�:ϯ�ŽV�\�G"���ҿzn{>�>���H�r�	���O�R��Q�W�%��tv�ŧ)��V.U�E��)��]:��ߙt�G:�Vz��Wz��7(O��?ُ��Q)r[�%?~e}�6���{C���ש�x�N:-1�rYy�?ُ���X(��6Aǰ��0q<�yb�o�|�@?x�G���ȑ���K��qU���e��<���0�w������%��=�h<�M~�s�x#���?>�Ot.�3�����lt�7�mg�����O�8f}�Q���~��k�U�n�q������q,~§�4��2���e�1LJ;?�{�:7n�/�;s<��v|�?�X?�ߧ6�������9}M��L���;o�N��|�ۥ�aN�[�g�Z�_�_h�Ϳ�����;���L{��>���_щ+sY<BcΗ/�w`>���@ǜ��M�_�r���sw|��K�p��8��~�O��<w����<w�YS� �����Q���d?�|��X�����߳g��ξ�"�:��I������S}�u����,�1�t��^��)�����U�k�L���K�P�a��v;���H��2{�$H�7�N���gv�/s>\�υ�|��z�����u<�s��8�e�Q~I_���R�z�tUs���(��%��������cy�-�9媾���-�]��1*~�C;�T��?~��ƹ�€�)��<��C>侰�{�
p�qο~������a��s���S�٬�z �٠3��z��ܬ���~zf�Q��2���<��F;�>"n���ON�4�ttM�t|$�ǡ�i��gw��r2xq��{���Ʒd���3o�!_��wN�^�4>}�:�vy��r�d��[H��B�oyv��=��=�\"^7�q����\�_�\&"@��MP�aϯ^C���%�\����|ȋ���@���.��\oZb��=���~Mo�>�
�	����P�؈�O���خ�3^�d&ѾO��W��K�D��2��<G�p���!�"L�?v����Gw�?X��׷߁P:#t��(]q�?���ԉ�S��&c�z�{���bޘ�q�3���=���g\u(���}�{$��Ǎ�y?�Wc�k��G\8�v��8i��?�s�{<���C�^{�N:e�CA:�EG4,7'���2������w�~�*��B��Q^��|^�9{k��'���<�r���y��+��Q�3d���R˞�i��C���VpiHܺ�LX�ާ�
�Ϳ��C�;��4���Β�����7(_�m�O�[�o�ޕ+�"xN�y-z(?�7{��xh����C�mR~��Oh�r�J.�0��x��F|=������ΩG*:�ijO����K���1�M��=lٓ�!�>��{G�w6J?�!w�j'��?��@�:,B����V�]K����.����E�ƬcT�@��ިo>�y4�ra��ye�pP_)�X��5E��?�O��é/� ��C_�O2���ѯ�������rϕ��wf��|��N��V|���o�D
����=�SH���pc�mp���gW�ũi��2b0L!W�(A3gP�Bp^"s���p}g���]r(�� ��8٫/���w04�9Ni8U�y6�a�fՓ�_���0�`X�XB����s'����@�w_Ȼ����4G$�P�3`䍤�������G�	7���\�D9�rv�C2�����F����s Nῤ�Ũ��>
�=H#��7�
�A
D��7���<<�5<�ğcī3��C�Ř�=��{�FΓr= ����;�|9�8v�nt�y�b\6���M�ć<%�2�U��<2���
"�_&I�|�_��x*>�_��h?��V�K���d:j����^p|�"���G��r��G�׼}����K�)��3J�9#��{�ĸ,����Xթ��ڙ֒W��=�$�aL>�ohbp�'�#��P�O!)��Bq�i��\+}�uS��]_�1,9"d&>��Rn��w���W�y�`��k�Ʃ�pû5#�8vq�T�K�"t�eY�����0E�HO#ʽ��c3ͷ����iW�B���������S�\="��rˈ�a�(SRǿ�?�8�y�!�I���<�։6wt8�ȓB&R���\��{�����Κze����8J� (�u�Ќ�O����HW&�c�'��D�O�\(9��0\�
*k'�=�`��C!�ř��9��O:�� '���y=B��v�Z�7�d������N8/z����3����X���/�ۮ��r1��P���!�Φ�Ҙ^���{�Q��<�yߍ��v�;�P�
���X��5!�yEȅ�L:S��i�*�`���~�0Y��[rŏ��/ؿ!�װ|�
6m���s-�J_�ĵB��Z�G�����a��R��9p�s��z�]!��v��
�`�Grɭtͅ�M�G��C֡7�Ϝ�ل�e���y�Ky�ɵ�\����X��s���nϔO{nB����rg �C�����%?�ႏs]XM;���?�(ce���I�;>ʕ��N:�I_�	���1.n��>��y>�;�@n�C<tN+q�ȏ���/�d�?���'����,�3騬�~<k}9�;��~2����n�4t�wڝ�U�k��V_ff��I�ǚK{��%}�[r�j(�ॾ@T�JF���d��ի���u�{�C��
�з�U��O�L�{���F�s��Y�B~�/?~��Ur-8���ׇ���r�<��\�o�؄�0�~��	/�"����#9Q
��[tԗ��ƍs�����խ��\7|@��߱+�L|��#4����sυ�*w��SN��H_|��ܛq����B���s��Uv�}ڡ�CefR�����K��<?I.�����%?�����9*���;@㯿�N�0ѣ��k~
aU?�"�/�W:^��\$��G�P}Q_}�|���s�g� ��B��>~g��F1�̉���~g�c��d�1x$���ܼ�_>��1N8�������ű���Z��R\R��S^��|��\�K��td��?�\�^�pƿp��T�9�Y���)4o�� ��0�Ve�R�Y������/�~k?/����F-���4�{r��9>�ǫʵ�s��4oܲ��v��B�ǥ|
��$�J~f����éY3�ď����5���y�s�G%6/(��~1�S�"|�9�_�t&?�ݹ��
�=���&�1Λ.�36�)W�j���c=����Y:ӿ��O�s�;�uM|���*�~t.�+)���Q/��j���.��O;�K��i'�]M���ޛ_l��}�/A��p�?�#����Ŵ+��?|��,�ѹ�~��:Ҏ���䧬#�/Xu�˹5̸��b�^Ӑ~]���f��{��Y�/��z��/Ÿu�oʫ�V�I_����i�����[�Ɖ6͹��F�6�ٜ��Έ����z�ݚ����#��&��|�5�:6玗K
�p��q^;=�0~�G�S�n����Q�C&�w������t���#?�!��5�sࢽi]L0�rI�Bd��c��.����sL!BZ~�ʡ4Be�|ˑ��C��+�x�\&Y�gL�'��q]�g���g��50�}��3�����q�J�W�/eO=
+���y2��vN�v���;���j3	��;P.��'�Sh��w���~
y՗8_��v�~�G,ۮ��P���.'���Z�����/�5���K��_pd;�b�� �r�9�
?e8������?��ps����y�6�Mح'�$h<���wU���(�h���#��;V�iD{3^a?��X������މc��A��N�l��:���c*��3`*�d�[ab
($�1�:��t�pӿ,�����3q�����Q1��s�?�����[�'�Mk��U��-�O�#D�O���ob�02��~h��5Q̃�
}����®��ߚH�ߴjW�MLT�NT��W�v��٫�[�~
�|
cvX"��t\��#>�#�|ޕ&)�}�8��ϲWJ}���=8����O�@����s����h~���젢4J���r����5�x��[iD�����`�����z��ĩ������~>�2gBKH_�g������ɿ}v|0~��~��Z[�sdA}�!:��E�r
��$6`g
���y�ڍ�������n�������h�3�$��0�R�+i���wl[>Y�1t�9��	����q`GzD;�ñ
�E��섯�A}�i�m����\�|��ɿ�{���\gN�y�	8#�!/1��+�!��l��u�OW/_�rVx(��yX�)/@z��)��A>�@.��w�����DS�~����W��
���C��}rk�O�u~u���_�}��9�>'n�Iޅ�,?::�4r�
|x�|��uYH@M�&��]��s�F4G��œ�t����>���'}y2��H5QhΓ����ج�p�i5�5SDU��c���g
����j��<-a$����,<}�%�m�2�@8
pC�=?��n}�3�Y�9����ߍ�x�a�l�H�ʁɿ0��T$?���7�{�
sΏ\��Qnz��Ht��Z�f�s1`�a�-dD6������1���X������ŗ�W����G`��'s�
�<�~��\0o����soOznf��BS��R��7s��N1a�5t(������M��C�Le�:�%�1v�_"6_�"J�ЗW	R�A�N�:��\'30�>5�����>�B0���v����*��_o��(�g�7l�/Z����£-�f=l!�I�T1�y}�`�L2*w������F�f��6�xŦnE��捲�FR*���{�|��G�.g��@���Oj�����z���r�!4j�|���1�%^Տ���޹���dru��t������i��7�`Hٱ���'�?�b�':��
8髁W�^�Gb����;q���Z�q�o�#K�3$��U_�ˀ�v�1H����z��z�<�n�j�=�\v��sP�C����%��᫉���g���;I�Nd�g�<4g�k��%h��S?���/��U`�m���3�2�N}��j>�Кr�'��5_�&��|�k͖ϴ��Kj�$�g��o�\:����=��sُt����iM}���{�KG�K��aE��%~�o�J.��R��)�*\�I�΅���#?���S~v:�ͩ�C�':���еl���_ű��0�u���Ϥ�y,~�\;>�O}ɏv(?VN_=���'���ޥ3�I�ɏ{����3������W�Wv>�W�8$����{�2�\���Z{��-�wh����ÞO��)�8���(:�k����O�3��Ft����m2;L����?���~0�`I?�ˑ:�;J����-�)��lz�K���Z��!M�X#��k��c}�~2��\�T輜���s����^�#?�#qq֨�Q|ؖ����q �z0��ܵ6s����ߝ�M��l4:�33 ؙ8+���N�N�+~:4*��q.?�}�G(�\��U����xG'��Odϼ�;�h���q��.����r�(/7f��Կ��ŷ].����_�,��~��ˎWɝb��xx��5t��ץ~0{��g�/�-��������v��S?8�rQȴ�{�N(N~~Q.�O�;C�1><���g�5�P�_�Otf\UY�{��]:���9�١�y�ޟ~���9��H(o���F'��Y�Z�B�����Ҹ�~�e���q���F��9��-�LJn��/�}ѻ�{��,q^���r5�X���U�TB�������.ru^l9"���O{���3���,�}|������t�y��4^�O�y�����n��O�Y�,8��\��5����2o��rw�D';���}��|��C��ѹ<����Ո*Ǐ:�ޗu�}=�r�K��N��~|n���b�c�t��9}���+}��J�I����^��c\��᜾�Ƴ���?�='Wt��-������vi=��i���%~�Ϻ\�"��U�b�/}��_=}���״{n�F|��+�|��T������c+��YT,\��6t���g:�[�y�ϋ�ԟ�S��J���5�;}�'��ޒ��CkY�3�#�m����C�6�%>ͳZ
�N�,P�>��cB�-і�����]7.>��_�񹝻�:�	��{��]~��&��&(��,_w��{**�\
���#
�-�tN�ON.�){�vsތ/A�tɽ��ׇ	���U^���Լ�����Ӱ,��5��`���?��
�E.��k�$��{>Ʊy]�!��-�p ���c�\X,�hb�t�ƌG�З���[�0����r�H���ţu����
��P��2P�'��)� >��]i�0�y%��2�)~hW~�Y�p�^y��F53���c��<�I�*~���8;��q!V��<C���ɨ�_��v�K��S��s�=���#�Y�#6ByE
�؜�`�����I��B��θ�qQ�2�7gK&Y&"w�H
?�!��v
K������C��Ce�S�&q4���(��}s;d��v���Jt R���>3�6k�:�F�I�_JG�����"K�s����[�<4I|�U|� �Q]��ى��Y�]�_X�'/wֿ��-���B"����S��?�5d��ܬ&��<���,�����y�C�|��%�(����f;�8.���@f��f_�5��3�E�B��u�M��F�"�t��;�(�J{�h�a�c�������f���ٿ������V���d����?�xPR�o�e� i���ۋ\9����H�F8Pn�,#� ��I~f�G�`R6��"�Y�>��G:�i�Fr��aĶ)/�����&��K{�^�xN���lw׮��ᲃ|�o���i1�9F�/�0/�����:�+�Cc���s*���r�}����8�P3NIEND�B`�PK
!<�T�L��7chrome/browser/content/browser/defaultthemes/4.icon.png�PNG


IHDR  ��`IDATxb�a-��86`� �|�-�֡���`:�,�G5H�%+��	K��Q0A���`��AH`�X�+4@�@Oi��-�Ŀ�2P�%a�����<h	�]eR5��.U�W���r:$~c}����lqb^,����U^�Ē�Ğ�	�;p,BX��
�`|�Xu�A$o됇�b�@S�{ޕ?խ�v�3y7��_�����ɟ�^K�\����ߎ��u��}���ي~NO
W5�+���CWKE��j2����[cJ߭2��K�&h��#���ώR�Ü�t0�=ȃ��2�Q��&�4e������<#q�2b_j*č�k�
�m��|�����m�0.М�ű�v�Rv|���Zf��ι䵌��D��Rmm/�nL�u}�m���1���-���f	�'��ݫ���c�l]w=�~)���p7�w��)�5i���Ģ� �mL�T`��5'}�_�"�h���Fꇾ��\�6E�@���M���G��z�ʯ�c�^;S���H�ߏ�\�wr|:k��q��m�;+̰4���l</�iu�]�j��G���}�C��\Q9{Zb�9
��7�2a��D��{��IEND�B`�PK
!<��Y0Y0:chrome/browser/content/browser/defaultthemes/4.preview.png�PNG


IHDR�d�oU�0 IDATxTV]r-M
s�Μ�=�X,�v�6�r��6��L�򠫒eY=UI��}���V��<��"n7~���5;^�K�����z�����u~]`�{���,4����vx�&лK=~�u������f���\�O��>�Kf��yp��_�ÿ�	��o
3�q]�7���ؾ��(eCӡ����,L��C#����8���>��xsAoČ)��Y�쓸ov;j����hē�����{����}����	�zsW!��MGNj��v�N܅ZQ~��E���@kzQ��Mcׄ'|�{d�.d�ܵ���b�v��k?����b��>�7sr5{�>'��?���
�Q;���8�lff��N��Y_��F��[C�۱������5郟���ή�x�]���[K�v�7{k�Vћf�?���iP����Ơ?Q�-��Op(?;��9���s�x�]�-������dL�_����������owvt�R���*���ƨ�����J{QC�<;{�F���Z'r���f�k)�|�+���'�1������3���Qk���_�>�cV�8':_�C���Ǯo_���,�D�?Gg� �~֦��4'����ӧ���`�Q�w��s��>7}�N �1v��G���U�K�t�����f�7x.�}�R��v\�x���io��ԹC��zGoW�c�����2�<��>�CЇ���Fi�97߱��V�Ǜ�ɜ�	����#ω��75�ȹ���d�@�K>�������r�;{�[��[.�E~��o\�~�ʇ���iJm
��*�>y!]3��;�x���:�#VK�̓����oj���
��v��\��c��}��e���o|���2wENf��
�	�m�����_W�'�j6��pW����s�
��5���9�+�
�ߍ>����Jk��ߏsԚk���j{'�1�	Q�<F)�܂|-k㊜�HK��qN+�
b�[ٸzh�<#䓷��5�5��ܳ�4j"3��.���;2L�/ȟIl��<�Gf�+?�6g�Fy83�3�������<�x3�	g��1�K��4=��<z��|��Xa��5{���n��z�c�yp�1g�d%ĥl��?'n�##5e9�:�,�a��8Jo|	C_��W�zֆ]�<}�z��7�^G"x��*?�_:��Y�aN⧓Oh��yv�-����v��9�|2O������8˝xLa�G�J�_>̣N�#��O�]wW�o��zޏ�L�O�T����]��uw.��9[�Jw�9��Ώ�N�?�]���;�]V FE�|�>��鳁���Թ�T�$&/�w'
�vYѷ!=5���b���͊nt�=oWo�kY\�S�Yzvu�����w��=��ϻ�Л�����]qw��R�w9��y��So�G�O�����.��}��|B���>��f<�>�h��f(��rtn�n|_�_�-���s�ѻ'�Q_�'�^��F
��� �l��>]A�hh�ò�\�lG��b!�я��D<�q�����l��gG�Qg-�?>ȼ�GC�ĉ��w��Oi651����<�]GͲ�I�؜}<��F�������^Kd�ƨ�u|fM��Ю�'���N�
M-�~ϊl
�yW�����^
��l fm%yd�n�luX���F�a>�c��Q|6w�vio�M��o�F���l�܀��>�����{sȇb}۹�I
{����{zf��o��?����^�8��Ɲ>�@FHc��v`i���<Oj�ߗ�Y�A_k�>�������i�bo���o�Ob�"�^��<�9뾹��>c��d�5�A��O�]�`��w�ڟ]�I��|��!�hV�p�L3�N�����	>q#�����®�����
�3<���X1�>��0���G���'rfP~jB��<R>��f�s&v�S��A
���	��ψ��[�[��G�]�]Q��yϺz���u*����d��u�W;��|{�5�>�<��O�P��!,�c���t㘺�zjp�s=�3?w{�.�y��1��]�{-m��v漐�Μ���T��~'p��e���!g�����&S<���Gdpoc����};���H��<�}|���R���M��Y��y�w��u��i̹�3�>�]����^jP
�P���?7�G������N��waN�p��zfo)}CϿ��1p�8q�=<+��O�~��>�C��c�y�l�����;���Ǫc@�"{g?��I<5�1F�[p4�D��ڄ�[)�@��avi���O���!��c�œ�6�A��ؘ#��0�2��_9i�`�1iOO�<<�+�>����`�Ȼ��qaE��?�Y+�h(�lKU�N��C�А������)~n�|n̶Zq�|joX]�[3}��p;Y����ȝ�g�
�����~��-&�������iU��7�j�[m���!���yZ���wBO�ޖ����>i5{+yD���ء!�U��g�^�H�Ov�_��`ɑކ�P�3��sH*�\�c��z���RQ��q��]�ǿ�C�Son�������CyM��)���{x�,3t�5�/�f��u��	��}t��Vx����"κ�j?����:�P©���Y���˚�b��ύ���d�Z�O��}6�/�[�{==�3\�\x�ܟ;����.�-�prH�=���#��)��w����d�;�4ܜZ�����i�>/$k��%y�
���:��ir����s��tM��u�lW�О×�o)D����
2�o��\��w>!k��73���S���O����@<��z�)�����{b�A�05N����늦F_pOֱ���a�Sj�����>�����[�sg6��fE?{�|��=s.Dk���a��9��\gr�̒���Ub1L9���g���囬��u)5�mM?>���[Y�\��wr栮�~����</J������|�Y&ˡ���u�rޠ�f�s���s���Bit z:����-~x��ו��y�����XG%�+<���Qќ��7��^�����e��3m�������ܿjz�Ll�FxZ������~]�?{�sI��.<�s��G�`�W�[��|]�
U�<;|���{��J�F����iM4�Y�O����
��G�L���[��{����� ���P_�h��ɹ�L��êhž���XE�C[��\}�*��?�{�s�dM37��gk���+��2�zESj}�~�����n�8�ײ�|ֲ���G����s��K�����g�p���lY�~�)O�w�-�\�,��?�w��d�{�g/�O��G��\�'���}a���v�o.ao^��)=���ڹ噋���Q���|K���bsGG��|����$+B_�s#멋���{�6[�en�<\�[�py��f�{N�g�+�'� ���`�����yS��}��/5�W��ߣ��!Li�돟x���7D�����a�_����,5��y:�?��4['ON��'���W��!2�0=�3�΀�uy�RX�w��X~�rO4%ŧrn4� N�2~^}�m�i������Q��Y�ցkkfot6�F�����޼���?�YK��~l�w�M�l]�7~�R�{W�mT��:W���qr/o����w�#Ϯk}���;~|?�w���'�<�a�k�y?�DU��|��y	�*%o�^O���'Y�w��=m���F�盠��2�����I>A�4Y�����:�s����
g��UK����1:����R~��I
T��/�̍��礒u.��Wz���l	��P{t��~2.iz��Se5[7�g�(���]Ka�K�/������H�d�7�1�,�r���r�8�{�V�ZU��Ң���+y	����z<�u�=�\�F��J��w�_.�s�u��"AZ{6�^����	I�o ��mX��`l�p�ݯ��a��K�#��<�&L/C����j������ȕ�W�~��}W̟�����]��w�0�g�	&��9ZJ(���mKb��3���������=[��wx�l�ÿq�ڇj�y�uɎh����1���ə�0��ɺ��Qw�u��ɽ~�X�����{zo���Vc���7-��c~�P}ʼn��9�������Ygec�b��!�����{z0�?J�A��;y���U=;�sl0Q��\د�~o�wx���y�w�����֮�7�%Э礆�g�ܣ��w"�'����e묘{��U{���=c�;{���}�xa��o&�̼c�e�� �gS,�֓��_<��<������y�d=�.~��<6�!/aZG�ケ��4fts��uZ���+�y�;~tn�Xs(/_6�`������k�rD�xu4�{xГ�?궖{���;��Y��sW�g.Z�>댤�E(솞up�⺞M�z��6��w�ϴg�q0G�Nx�N�y	�3�j�'�'nտ��B��E��M�G��ó��B��E\c�?����T�a�G�=]�o���?|�N}X��ܺ�^gx��?�
�Ч=ﲙtJ5�]�����m��je�o���yQO��7qs�W�����d	s�Om�;x�̼����6��3��z��<k�RN�����`��4%��.0�6ss^����X8�:��M�����y|�7��}�{9�9'�g��s#�Qo26�����xua{�^6���3�M	?{�-R�`���r^;�<��#{�x8�bg,����|L0�'i�Q���/��E}���.�9z�&��V�oX��Π�+,뙥��y��X_��5����ģX�˳�V.�/sN0�e���}.������no8���g�+VS�m�x�<�yasV���{n�Ƿݘ�s߻y�46��q��7�!j��Wta��y���>K�u
��6�<���K�Ro�>V.�s�#&��w��#o�;�5&�t�?"2����#�x<��0Ź^�07~N��k~xd����c��QC�9��5����	dV�6���J�d샹mX��1�5�@�yk.
�~DZ�o�y�!{}8_��֌���W,�5V��nUM&5��][;֠��ܣ��1����� �y�z�|0��]�6�͍��K�?^'
�í�dl�y'�6cwb��!��8�v���&�C���?F�'O׶�5�{"�1�z�$�9G�7�����"�����9Jϱ�:���ݢ���I[�>4�m��9^�q���0�T(�H��G����&L�bC���A^�=`Ds��:ҹ�\�"����j�~���;�������Or���q}�G��g<_��$&�߾��������z�'~z��P~k���eX���{�(n�E�?zF�c��:
�E��{�#�Ϋ��5��)�9���R���3�?���X�Y<g���'�s���c���s4ΐ��~ܫV~a�t�ޑt ���`|�6b��E���Z[/~i6OR�᚟�8/A�q,��m�x-e�)��ƶ�>�ACxC��<�+B<��	^��Vw��՞�bO�y0�0֜�T����X�S��_��L���Έ���!�9�k'U���FÞ�|r��|�����r'���C,~k�c�nO��Z�G߭�i�(������4����B'��DC��q����9��]��i�pD��,��g�}v��
A��:�?���D3q˯R��
���?��=���N1���������s�1�����{�#ϵ����]L�����ǃ	zv�1}�����Ϳj�]���`�]~s^���ω�=gO�A�X�[�0�b5���LM����j|Ɩ��4����}���� ���sw
?y,���A����mi�zj�xXs�JrwQ�6�/Z�8a�
�B��?�v�}�dݭ_r��g�A��=���Ɯ»�Q��W�/����=��5�/��Y��6��!��x�����^�y��e���ocN��:����f���=�[��C��ã����X�0Α�'���
�U�y4W��+G0U+��ѣ�x��9���<�M�Mq/x��Ss�M=��5�~1j-�X��1����9z�{쥺��c&�2�㖆���#蹣���MSgr�
������S]7c�x����Qx�bN��\Z:y�;a�wP�G��2o8<g��':z����L��<���=��G�n'~�ٌwM�_���?F�Ib��C���ӗ��7��5��������T��@#��:��P�cv'ak,���Hcj
��y�Xq�Z�湨s��a�������{�S�h�����_ﳃ?LG���,�g���c�)�ޫl��F�c�����:�_b�m���
�\���˘��=~+�C�7�J����z�E}6��ם���<")�m�m�0֩��;��=�7�9zͯ$�A}8��d��O鉧�gq�漜�֏��?6���,������ll�U�hI��ܽ h"2�h�n���Vn�}t*#Lvv��5}��(�� �n�#���oЊ��8�'��>�%y�O��t�y`7�51��^�����(w�\����o���w�"�#Ϗʝʩ�;/��Q�7�L���s��)�>f}��z'^��u�K%s���+T���K�wM9�s?��DM9�+o[p���cjwtz�u���V� G�׳G=.�(�;���6}�=�:}�vܜ���n[>ġ�/��￟�&1G��+b�1e8�q��O�U�9�;n��N?R'{��?�3��m.g?c�Q�l���ֵ�� �O���[�L�����^Tù|1�]\�6>��~;���.�?u����a�qg�U���M�x�v=W���:�x�>���Y���ϟ��]�7af�D'��<G�c�g��Ί[Q��8�ek�.���0yn��;���l|y���(���d�$g�v��F�.�.��{�=���&x�^�s����\�w��m˃'�_{��˘g?�VJԶ��v�$�L�+*jlY�]�9�)��y�ѷ�����rv�L�"����Drp֬�Tb���מ�0�eOE�c�l�*[Z�wFs�R����
E\Ƽ�lac�hkRY5��i���z�5:���١X5��-]�7�|F�|5�m1�iw���ZyL���uaSa�p�9�p�F�3���@V����wr�6������p��u��#��\_�����>��p�_y��XQl
���~�!��%��?�D:�n����b����{Ŗ.}�߼�i!tLB����*8aF>��7m����I����@gb��banC<kh�T��n�J}�4^g���^�|$�!��)�|_
�c=�ROp��������?r�;�fo���t���z��;����v�ܥ�\��m��#_��3r������8���,=�w?\*�pv���,
;d�7�>u�Y�N�/�?}_<���g�_:��O��s�䳟Ϫ剓���q�ޏ~��w��v3��|)n�g�dO�#g;��vw�:�-۾߾8l�]�&d�U`��"k�/�Ꮖ�5�����P_�hp���]��ܴ	Gud�5�I͡zשMm�[�`k\�Y�/�j��U�������g]�yV�����&��E|�˩�Y�r����H,/��u�cn��/^��:|�~W�+dō�
_^�n�c�{�p~V����,�6�x�GM��?үs��H����&Ϋ���p
�]~��Ϻ8O�j@{=a��!�i7f�4w�����]{q+i��QX��r�>�_����:�վCI�)��w��'���SI�v��\�8���3A����~qC��S|,T�k�Lﳙ��>�r��<M�lt��w0�\�(��
����Os�Y��$�P=gp�^���π��pH�����l��
3c%�[�n:����~�ŭ/�0� +�:�H���efC��^�L:���4�P���ܨ��Q5�S�30z	�Ms�A��}7�F��[{d�tS�k�p-ҷ;�֓��]�3�ҳ�r������3��y�料� 9���â����
�����zcK�lOpx�_������ğ�¹��(2���L�oN�n����5�Y&&�۴u5Ŗ)~1��L��M��|�1����[U~�o+������?���z����>���fz�Ξ�o�r�~r��;%�T���W]�����/��S�8sL�<���8}׸�J���"�����}|p�2�����Z�Q����׊�;V�<��ZJ���|��˶;1w��:o�Ma�Y��\��b����9�!mm��T��g������ߧD�������\��z-�ȯ)���W43�n�3��m�'r>ݫ�U���xn͕���<x��eK���]�9O=�7@�J���p��N��U�%~�z%�x&�z�7�GP�oX��U��T%_7�3���Ҭ��n�I������A�
g�8w���|�Y������ƪ�ٗ]��,Փ��E�#Q9���T�����n���sp�G�p����\�88���ϳ�Q�})]��S4�Vm�o��w�\��f
�7��_u�f
!n�X�_�ޟK�Z�?T��Cn�����(��OY�U?3��ի����������P��滒c�7߷���'7�H�&wa:���~�3rޯ��ę�
����o��8���J���N��gK�V�m�����0��u�,��|��V!�J� ���1���p�I�"2���W�
���?e�L|W��s9��W�:[���Po��}AF�w&kqW�}�w�~����YCKs��'ҝ�n=�7�[��������՚0��Q������;~I>�o����Z��g���o�3�����.���é���r��W�Nْ��������c�H\���\%��8�\%n���5��|����8Ri{�	s���Ϳ�uf^�8[g�m����u^�W�����O�| �b��@?���?���?Y�5x�d���I�5��'̞�jh�;��t�<�ڨ�������V�U�q/�~h�:e��ģ����ɳ�k�G?��ֻTY�v�@P���?����P��_<;����9tL�iBn5�iX|�?!��]��T~�8��;�|�]���i����o�o֙����W�c0�qd�k8w4x�
Ob����i}�{����N����v��q#���^���_s��g|^�}�+���/|A�_���U�x���2���J���2����җ����+��_��8'��$��J5G ����Up�N���������O��6���S*86zEN�]:�W$[1�=�
�w�	ȹ>^��M��	��M:�g��p�*��]�G�H�^�?b�r$N��Ϛ^~��>I���}��F����-��ڼ[���'�����:�ɺݚ�g})c�=�I'.�9Vo��YG6�s��:t֜2�tX(��WĨ������z*_j4(�O����;[=��ܿK=��<���������C���s�o�bG�ϸ���;�U�ڽ��>l������Oe�=����?��3���ļ9[(������Y'cS9.���d�$_*?e^�<�Σ5��f��f?�ﲻ�ӥ���'�ܼ����\��ʑ��!����ԩ3�~��=�?+>��+W�7�պ�ݜ�?�:"��g����U�m��s�����Y~M�٢H}ַ΁��懏��������P�����Qjٜ��#9��{��k��b�[���_�/��UܕR��92V�O>~�������ħ��\_8|8)u�6bb�z����^š;�{p@_��T�I}&L��y���*[�{�U�y��W���Ӏs�8���#>
\��ħ_�j>���˨�MyW���~���5S�w)5�R-���c�r���G���6��{ޱSߦۓ�1��n�g=�q�_�_k):lܒϸ����©��ܪ3T�{D�y՚��_Ő����[p�S�n�����:��7��&]@*�n�E�Ý��}Y��*�)À=�":�i���P�S�u�����}�+x�v�S�i=|�Tb]�,R���/Tz�2���b�ܔ�U[㻞u�>��f�
�x�J����{%�S����X�u00�S����
�շ�Qj�k�(����A:C���+P�_����R�}ٚ2�!��¯��QMwGGvQ2�n�M�I��S�\�t�
ò�:D}�/��R�����ӊt���/DŅSJA_��\�S
�U5z��ש��)���F9",���x/�ɡ�Cn]��i����!.b[�|�n��a�����s�{�w|�c���a���G���;�m�w~�X?��:Z�'y~��?�h�<��G�&y翭�8�n�O��9�泛��9~˯�=�O}�[]�K�R���K)s㐃?��ud�7����ç�}VH�?��sH޹����3s��S�����F��ɸ�z��5}ΰ�>&fP_k]�O+g?�mݥGY|�k�
?�#��(MϪV#/�
n������y��ٕ�w�XM��~;�ksj�^�Y�B|�O��M��7�pcqN�Y3�3Pm�J�����>d���8�L>��1s�	�~K��X�Ծ�=z�wպ�Q�l�9��o��u�ˀ�!�W��M;�'��Oθ�M?K�����)?J�����:��;϶#gؕ�ӕ�z�Ϡ��jI>�Y��x����D��ۇp�M}+�*>z�r�1I��#���)���%n�SekLy�O>�g3>�u�����+�ʣ`J8��	�3ԏ1���y�)_��l�� M�,�m�O��o|Q��x�f�tΟ~�#{E�������P|t~�{>�ULJ7�C>�g�:���u�
��RU�t&����3�0�5���G'�s8���E�kʽ¿nޱ�<br�@=�����kXW੕/���x��F'&6�g�Tr�騎?��(�x��"G����e�޸)����xa����ݤ��`Nr䲈:�t#Z��S3���A�#�|��Z��Ky��݈����v��ͳ<j}DAF���rJ�r��ؔ�b��ګF���{L������8��}8������/͡wNZ���_}c�Ďg����:�R���I*q���^8��f�w��u�l���^r����י㽾���
�*���a�g�\߾��m�ZA��?�g~M��짼s��q��g�H糦������y�X�!�G�����ms�z��ƹ������z�ʳ#���d��5�^<u'��UӤ~S���Ȭֵ��
޼��=m9|x����?��(|�c,Ի���-�?㐟�3�;9Bg������oΪ� t�څg����yp�^�ߟ���U��NW.�����Y���$�Z��x���0�!�t7忹����q�5{�9עo��<^{���;��኏r@�NT�
��P����z����J�\�Le���2�'he��{X3_���kH��^�\װ�\I8���"��Ͼ�ڣ�xJ��{6\c�j8���I��n�p<��u�
���A���L䥏�?eS��1����PM���e����mJ�4jbH��N|Z�:��w���ֱ��Y�n�-x��uy1����!�
���>�&��U�s�ř���Vt�Ț;�l+�Fl��[æ���/���ɯ�87�"xT�)w�±&��G1�¥o����8h���*q�����S����&PAtNs��`�n1��:�#��x��l��^���b����_�݇�9�ݵ�;�����A�����{¨l��Z�@tN�V����g��_�Y��'0���Cֽ�����wKS=�ٷ��s�9���y���׳�.�����9��_�h;�=�Խ��{g�>�I�3�/L�^��i7���*�������V�	W6�ɺ�]��hՎ]u?1D���k���f��ˑo���;f�:�gn-|�U�������jM�(��/q�/7�d�˗�z�S�ًj�2��x��g;5�K�y��wb�jw�
�����;��X��P����Y'����Z�O$�6u�G���J�{�S��W��\S5�L}��N�ߐ�/�|�n�S�=�o�mU�����u�n�q'�7](r��א_\V�DG��ss��6���������o�/���s_���o�d��C�b�׺��C������+u*�nQѩ�8U�m�ի��~H��=x *���֘����
y⼲v�v�r�pt8�<�5[O�
�E��3���F���|d+�9B��tڢ���l��B_��iW�+>%m	nS�%�a ���G��l�z=��}���V�Q��ݬ����X�f�K�ϔK[u��0�X��9��c��Q�:�7Bw�N���:��u���󥞥F�~�u�Ț�J��ol�����(O�!,��-�1�SM?!�i�D�j�ep�����?��޲	(����|<��Eh'��_��11����o(ұ ��ſ��f^W>����~{�m�ٻ>4x�ΧN�����@��z�;gH�_��Uo
0K��x���|�#��u��I���W��>:|t=b��9��upW��u'�G/�Ϻ|�O�~�v��!��Y���3��8�M:e�:/��[Y|���l�u���'��R�0b�������羃��P���=G�;�裣ٳ�@�eMdɏs��5A���#r�~��3�6^|N�?�_�W�|�~�M���8��9�w�G���#���=��~i�s�[_q�:
��h�LwMnf��A'�����Ŀᓾ��LlſI[C����~x�)VpγT���{�${�'N������:��CqV|
�iK��O�z�p��#s�u��9L:���)x���Ӵ.�܀�/#֬${���G����8(n�3�䗜_w�>K�)��d/��|�9��47�C(�O]�^��t�.���m�ǔ'M�K>U=�d�Ք��W>��m#R�7a����/�4�e��zbͭj��N^d=�F���_��a+{	E{��+�{ȯK�g��l����Qo9֊|��~�|����F��2�#{H�@��輺A��)��դ�ZV�{�w�0�:���k���ܣ<=<{�&�pptV�:�pT��*Y:�)��#���y�]�v���0���m���舐]ɷ0[E�<sX���\N���C��u��)�a���I<O����pfqÖ|��zy�Y������M���T����=�up�6�g޲���u���=�^S���
�G:g��l���s�^��R?9�����$/J��u��q�l�w}��o�w^�����q>��y��>�}�ϳK>������ۙc�3�}�}��������G�?�gR�c�'�n�k����}]�陟
��lM���2��^����/7�_�����ž�s��0o��1�b�ԅ3�>|���[C_~��c0�>����^m>���=4�=��᝞�9�Y��|GO�臵����ǵr@����g^�}~>�Ég�P.���O��o�����a!�<U��xV��~%�7�Ӟ�l!��N���^�Cn���j��qt�Ǖ�	p���-�K�Θ�>u��yV�3&���-�e�����[~Ղ]p���ʑ��[�K��>҉��<?��3�F��V��jӼ��w	�<u<<�F<�
���;1�r��37[ɹ>�K�Er����
u����y�8(�2��ԁ39l��=�1��g)|/���d��O�E|Ȼ;���.���B~
�X�u�bx�s8�tW�[pvT/'�S_v�_��Dq���j��W�ĺ�����!��:e�W���\N��=���f/W|����(��q�3�Jo��5�؍��n�oA����v;���Nq��F�O��EYTNp�:�Br�&>ۓ#��,���܆>����C�`����͹k��I���7
;p�=ql��1G|G��œs;���;�ә�;.j��0֛j}�AMN8�ь��{}�j����[9�X̘DK>�W;������ő��'�[3�2�<'��t��Y\:������g��ʹNL������{)�V��^<����w�$+�N����>�z�7�ϯI'��6s���x?+=[�|���e�������C�,y,9�n>S��3ך�����D��N�״����ϖ?qr=>t�F��mk�Α1��ϧ����+�K�r��Zms&X�l��5nɟ�3g��$���g=m���5sa����9G����X]�p֞~�%>��iw.�ם�9��rd`+�=9}��2���Ͻ�!Y8�.Ö�.C�����s��V�S�\��;.Y�	���\�'�pK{vqC���S�󓑧�[y䳲řË���3hs��(g����L��,�Q'σ����й6���\;�'f(���Zn�W�p�x��?s�|�l��|����:m����ٰ�VBgD�7�{�1ut&[�9�Uʻ��|x*�����a>ػ�-$��0���E���/����{��*ik���8k=�N����y���"L���ln��gJ�)����+��?���W�w���D�q���Y�p��y��`�-�w��F�2{�C������%�G���0�K�����9�#�<��7�]�R_�x�^7+��nΙ[����n:D9�K�W�?W�-���Z2���<�;�<Ǹ�Y���]����9�,f%�a+��~�U�k#�2,�u����������vS.��f}�f7:e�🚏�n�8e�4�r�&~���`��
�'ՖI���[�H�3
��f\�	f��ه~�\���rvoOb�Yj����J��"�)�[`���\��y\V8%��`XP�����Dd��2ggn{g�;?�/yջ��_�F�&}z�6����H0�V���M;/.d�O���+�*���gj�8+�=Y�lL���)�u�m����8����a
)������|�O�?�7<�;fDl_�|���p����D-������YG���Pf�ygX��up��:���Wmʩ�s��;GNن�|��d���e�Y�r��������/>^X׬�=q�K�֪Yc��Kyb�_|/t>PN͜��s�upF�S�W�77�lSž5��!�G��/��>��p���{xڵx�WUM<���~NM�'�2�Õ�澼��όs��_��}�~E�鋨�_�-kS����y��&|����%���2�U�,k�̯.3��d��YDy��|���¯������]��+��~��W�G��+�e��+nx�701��Ĺ,�Mqǯb����i��~E�����=���y���%�<�9~���l������
|�:~��q ��ѓ(c�E?w�o��b5�_�u�0��0W�-˚[���|��l�8��p����W3������h�܏ݽޔ��<��8��?�o�Ì|\�v��F�j-�-��:��$��gd>6��c/��/��<�XE���3t5��V��Pߟ%(P���m�C��o�2���ԸL}r���	���嫒�o��̾\Ĺ<��7���ӗ�Wy_J<&&��������~(�s=
��|���?�����]>��V�M�R��_5\e}�9E�l�/���u>fĹ�pnp��7`�sL��&��l��:uʈ`��~@q�%w�����yq��3�r�����[�y+��/��Y��?�5�_�)?���Wn޵�z����|����1�ٹ6��\~����I�-~�����8��п���᠖��=S�:[N�z�E���!�5�G����~��E�=G�x�O�����:�_�}y�XtFQ����bqHgd]���v��Y���1 *�X|"x��ɜ0o���k�f�{Y�d��\��fU
`f-�o�������)ϬM��T��N�l���a�8x�������/��ʑ�����y��^�C��"k�A.�����jG�g}<��_�����7�ȝ��@~d�Ƕ�N7l����ߩ��t,��US��<۪�K�T|�dݩ>�ĩSG��͝X��y�1_�:`�%��_F�S�3k�Sw�������Wr���P=]����>
Gܺ��\A'�1g�$ԫWߞ�����ǽ�YΞ�լ�	ŁZ��N��i��'��x�s��1�O3C��l�X�=K��+�}W���[��9�/v�z6k}ո�Z��*D_�;��٥_��C�Y���S��zL�%�v�n�{H.�CI��P��Vm�H����F�h�s���u}�r�\��=�m��ìR���y�Ë�D��Nت�U�S%ͅ�y�r3�fD�X4�i����]��Z�WڭE=sc�~���~v�V��/O-��g7ꍊoG�2q,J>[�&�z�:�k)5��-�1x�� Y|�����&[�n�����?%�|��?����E��_�ە�?���=�־���Y�.��8c�g�a����k������>�l9k�����9;"kY�3�~�/_ƖY��~��}��u�_ʵzV:�1L����_�>�l[�ۑ��:F�S��Ӧt���h�>LK}�$/�_�t����>�\���yyhXBs\W�:�{�>�ʵs=�����P<{%Ȭ+G��h<˞�_�k�zݥh6�y���0�h�T��Z�OK^�A��f��32�Y����Z�W�lZW&{��J3�ݬq����|֩�u?1��(}c�.9�\8=��d��.g}�R�i����������kV�����+s�;��<}ia�١8w����~��^��U	Gs��9�WZ��G����%>ԣ<�=<�t��I��u�^����s�z��RG�R��p����lDԠ�S�����g��cs�3r�œYέÆ��md�W��U�l�M�CyO��ȑ�P�﮺�~��Bvs����#��;v����&�r3�N�<gR����/��'��r(��a��W�&'<G�;��K|2�W�D��C^/�3c���N1�_�˯�וcȵ�Z��ߎ���r;5��?�7�b�ߏ��nS:�Q�k�3�Cu8�N�T�����&.���T�O>���N��p�(ҧn�ܟ��(Z���R��0;���ܬ�!�tԧ�^/k�k��z���S�u������s��+y}zT�7�!�؍���SI�ۛw�]��6��Ќ����G�r��Sv{����M��������`=�<1�[:�G�3���^w��߶����̵��[ҿ������ହ�_�g�����Op����=���!mE�����?t���7�#�7涵q������8����1�q�� ���Ӯ��pt$���A#u���f9SGG��?#Ǹ�О�F'�"��3�,|z��(>�yJVl����<�}�o�<}�/|r<�����=җм�\��I�毿��n�c��3����J'�����f��%(O�ϖ��ӈ?���L�����̳�Y����L��<��I�S�Z盜s��|N�9�g�s����h�r_���Nɀ>m���8�����<������/�ӼV����w#Lw�5�N���=k��]:�z9���u���7��8�=�گ��|�g�l*�6��pf��dLL8��,8U�!����#���{��W�=w�����}��"�}�zl�3_��u��M�Dq[3{��8v�����j�|o�h^�-��T�eQ}��#Y�s�0f.�w��(y��9ܐ]8�ճɞP)Cg�֛��/dq>��ހ|���n�=&��.��j}7��H3�U���pTO�̞s6UCe6��}d]{I������κ��C:�O�j�B�^�Yw���l�	_w2G}{e��Cލ�Yt��u?_5��O�?����Z<m)��:�ה��qh��E83���z��g��%��P@?+w�E��	��?{���n���_¸��N�4��<D���|2?��ӱr���1e|�Na�n|���|&���5�����ַ�O��̯�잣Ko��_)o}ֿ�>mm������ɻ�u>��Է%I����,κ�˛?v7N�߯t�0�p��em�c�u��s��v�T�cL�S�33t�W��7��3�'b݉{�Z��>i7�z�[�O��|c*��1�!�� Q_�A?��v�⮜龕f�<K�^yW8Q�ɸ�_s�;�G1pč�����?e/����[�'�}��K�M�N��sG�_�	�5#0�$��8��\�C5��;q�)���q�7�����Yͭ3���U�]�S�,�Y�b�s�H��%��o�޷<'��rT��8W�N��[�<��_~���͟=���C��mz�\q8�ע��\���Oz��?�|�}?Bq�p�W�w��zO���Κ�x�S�;�g�(n9@�n�-�	ʑ�'���G�׋l���gUO��ߎrg�Q�8u~��=pԿY�!�z�mj��Ϝ���Ӑ����Yr-���f�[_U�y��!�A�3���y���~+?���\ڗ�U	yh�P�h�r3��Ĺ�o'����ySbz�甽]�?��;�G�g�J�gF/˼{T+Ч��Y~�	����un0�Tf�z#��]�+��W���=�G{�(#.��<b�Q�l_��~��\J!���>�Z���N|T��5���r6A�E�q2/��k�Y�y9���D�z����<����R���W)�S�}�V���x���u��9�J.�� wR��͝�S>SG�U�?�?����oYE�o�\�]�qrĞ���5|�s�^�^c��j�Dn�d�|��xN�Ů��m�:�����`
g?�Z�Jy���z)%�Sg�L�pϟS�#�i��+��gܶ�����_[g�3�5�9�Y�	��/�)��k�ۊs�5]tq=ұ*�1� _��X{~&g↜~��?���}�ݓ�#n��yQ�)x����:�?����]ga*���ג�:��g���Z����\��8�{L�2m�±%�pRVl��-�1��D0u���	'��Ǜ�r��.���@�K�p�Χ�7�f��G�yn8��?�_��|*ﭹs������|[�(���(���1ㆬ�|‡�̊������N>���SuƸ�w������L+&�q �-�
g�qf-��ɚ2��e���5���U�_�OzHU��:>��u�|�w�����~���#L����t�]�{˟O,a���%��c�S��|�҉��0�?��g'��ٌ���8��u��":���iѩE�8��t��Y��3��R3>M��uF�gފ�‘�˯�Q��	A��W���6�6q�e~�G�o��?���?u�)T���zP���	3�jN��'(z�)���h5�r��!i#��U������T/A�:y���h�����|�Z�M|��	��p�8�@����nb�����;�-E�	���|����}�21Y��������Ū�D�x�_%J�y+���/a�����sܡ<�>r�x�����|6U2G��=ߛO��y�Z9�!�����?������yz�S���;��⃒�#�s}�'���rH��}��7��%sC���u��H���?���]+�f|�8����gO"�����MAϼp�Y�i���=]�����'��w>�U�K_�VMI�<�����Y�_��S�ܓ��je�WmJL�o.��{	�Rc���>��/c�a��Z_��E:Ä���y���n���ۆ��3�[_��iR���N��_�O����x�>���>�����YW
��:gԬ}}�h]���O��pd+�c �l��1�3�
p.��j֩��͉�Q-���Gq�}�;���z�<z���/b����sW�^��u�x��G(����ڋ�+qP
��R�Z�n��85��/����_�19�R��30�8��N�%�̸��8b��I�y���7'��.�/��x���ݤ�9�s]B���ɼ�k9��M猏u�Rz�6j�NG�V�֙[E�����>qV_��|������m+��ʗ��?B�ifON�M?�>���Q9�^~y��l�=��֯��ly����o��s6λs�ˠ`�s���ǘ���N��������s��γ�о��M��9�;�a��G=C��6���uPC�B-��y�u`N�=���´�=����<s��(������±ġW�N5�/Z�����'�lP����b�nӆa�����I�>9�۷�����O�c��_y+���K�?s�΅�Q���3?��<q\�'�g|�����[N�疾}��Wr�?�6�O����|6�㌢���
o�ϼ�9^u���t�a���?qT��џ�3��I��Y@���:o��=�p6�K2�Ƕ5�?u�w����e�����~;�j#��g�����d���y:O��G�wH��=5C���#�B�e~�K�'�_�E����K�얭ջNz�}�)���'z�G�0>�W[���o�\���s"|��'���I��
�<�+���������W�̝�,Y�7�j8�׍-�c���k(n�NHG늉�ռ��wj�jE�S�N��g���Ͱ5UK�~7Җj/�rR�X�Y��;
>��<�$��=��_S�&Ί��{����w.�D?���O�v�)�up���VH_yz�Zς<���y�
���	d��R��s�M��jֈ����xW��8���=ˮhfܢ�a���%�����qj6?y�p��B����`�U4�9r�QB�"���Hh˪��0ma�ȺY>�V�Y���0s��A�[L���K�O�~�fᆬ\��]&�)+��S4� �r9=�>�M��c�%��ʜ
u��@V�-��`�S-�u��Jf�^���LJ����}��™)�{��Xg#��2�¬(������ǠV���^�C�w�G�_u�����w�v>x���fc���=�K�_{^�Οup������Y����=����nL׿=�%�o��.߶v�YO+��Y����c~��p�Ώ>�=2n�1L>��a�8�����2�GC��:R���ï��G�2�l��)�Z=�w������+׮��f�20���������5�@Ǥ��Q����3>����5%��d�d>�O|��f���X�7�Z}W�����/8�9
��t&_�a���~��N��gҺ�<+�B�"�+��1�8���=��Ѳ;B���f�^{�;�S��C�p��;��]�LW<
����T��I���#g�;�¼|��LgǬ��WQ�5�.�A|��V���y��r��:K��A|��N�s�Kg��=k�=�9�k�􇤃����t>�~����Z^o�bY�h�U�cK~e�/��)�^꿓�f�9�x�H�K���q������������U����W�;��O���l��0��/%��B���}Ƀ���C�a���gpg[
�p�+���ir��1��݅6 ꚹp??̩ף����-�u��������Wc���ɋ�݉_���f����'����*��S�P�D� �v����^��x��b.^�r6��V�N�Ԓ5�Ǎ����xC�uV��{|�D�|_�.�Êߑw���)� g����a��ϩ�X��~�V+s1�����o�?�i���o1�^��._�"ŝ‡�A��ߥX7j�s���/�3�=|��o'��i��s���ȭM��X�~��[�믯�/g�|��z~��Cߧ��O����\���LE�gr��<����^�Ʃ{=s珳H����Ⱦ��/rp��8����O�n��|Ϲx�|����߉ۖ�P��}�V��=.����U|rvݽ���x2۞1��i�p�r��V�B�8�'C:����d���Wiu�8?L3���_���d��U�i�M��N�yrN�콣�]�|��6.�11g����Y��ؚyEvy��iK����\?��q�x�_��+�~��ӧls]u��,�3s0:����ԏ�ޚ�����+�~��g�壟?\=������V�T��_��|�o��A|.���8�&0�>�����_�	��G����t��w���c�]:gg�����o� ���ϚA螊f���oP�A8Z�C�6d����LTqP�����d8�,�l�Z��;Ω
[9s���Y3��l��^��D��O�%�_p:���8�V7�vTp�3��g��]���+���I�Xy?�z�x
��~K�g$ిyG����#1UC���j�M��ٳ��~�/ԓ�"r.�J���whƏ[�!��C�7c�^"�e��ZuѺ�`{Gd�`�N��3�PNm����LJ���J�����Z���֯��9�Ƿe�6���G?��SgL���w��Sb�:��TO�['7�S��P��Q��S?-x��g8�x�{}㯻���:e��WLek�j囜s�Z�YC�40�y��9�Of���_�_���V-:�88^79;����Z�}%������`
���w_䶛�\[:3q�3��l}Β'�3���O�\Z�<�u����ѳ��NK9�7�?m
�	�#��	�r������q2���o��+��tnq�3��g~�y������[Sb�	�}�\�~��Թ�#tn�cj�S��s�b^�}�2��*?���7e���;�8��k}AH���N͞3�)O�܋��Y���䃜���~낢�U��z��gS��opJ�SypJA߱۫|�1z�aL���o�~�Ps�~���Wl:����sɲ��I9�'�Б-c���%�ϳȯ�~���K�g2K����I��g��~j։�{���CD�)[���;��5��e��*�|yr�J9�3��/jVb.}���iMq�Z�C��Z�;s�'L��H<�ҙ�|�t�H�4XG����1�Q���r����,8�!�GΉ��]��^�P�E_�}���+Xo��y����=��}�'O��ݷ�3���٤?��8+����Ĝ���
�/���իo:K
�Ҏ�br��c�=zS�!�����G���&���s׽)�j�*g)v�\��5��=�'��>��<wևbrM��^K�5���l�װ1q�<���{�sV�:u�>ul���2eC���
eM�c�/�_�1�ſ����]�ə���9����ڧ8���k=[X�r/�H����7\�|wp���g�c������ �v��V�]{419���V�sza¬O�g��������_�:�yN���N^��ZW�(�G�R~���ܟ?˛��_���#o������?�Ly�	�Y8���v߶�y}��?�O���[����/��h]����������<��u.=�O�?9�O[�Y�N���ә��V6��,��8���o[����B�p˖��ew."�70\��C�M���D��X��¿��[�s�[��<tr���隆|M}�"��
�m�7���Q�%�@�m�K���4:���&�?zHm�V֑�g��z�}�8ʩ�ʳ�3
�Wl�9�9g�djA����;U��pN�l�
N�^1��pn�޹�i�>����)�k�EC��W���d]�:/�g����\�o�Z�_1��bब�8*�5��]����͞�&��V\��'�^o��z�{�u\��7��T�L��o[�ﳣj}tX�d�+�n��͞��� yZ�k�h�[��k�������_��~)wb �mt��p���e
�烖���8��8T7�#n�J��w1�'<�@8-�s]g�e��?X�u8ro�G�1��9�� ��)�	�)�q�G�|W��V�l�|� M�Q�'��r��s*��uw��z�>�Zw�'���1e��D�Sg�������zӹd�j��³��2:�e�/�D��ZK�DZ�h�GW�	p�����nG>�sV��g��O�L:c�~缟�>�%�	:��38<����ӪN�������<w��#ɣ2�o��|?��ܼu���~�O�]�*����gM��9?s؏|�9����.�|��_����?�?yf��-��v���_���|rHy�sƧ�j���O����yw�J��@�y��������z����r�l_v�+�1�+r���5z/�-����/����5���w]�<�w��G���Oy�j��g�p~R���v����
��Nb�܃���V�Y�� �=%e�p+>ԣ�s���J;>�x�g���Z�Nᴌ��buM����yK|�p��
�8�b�syiA��0������98����1D�Sۡ��3g��rUm=�Ir?��b{�-./��x��N͊N�����6�9�M���ě1l�y�� ��*����_9�ޒ��/8�L.$Ok]�¬�
Lw�	�Kx:���:L|���-pB�|R�#S�BM���.��F�9�8S[|����Qw����G1���z��'8���l��	J/�θ#��3&9���3™���mz��=�,��[8���{���.������ޏ�K�5���8��w��g&;��V���m�:׻��]�uZ�s��>�Σ�Zg�<{]���S7�O��哜�Pc��]��Y[p�p�N�=��v�k�����~���%�*y7�0�gjz�#&�: �s���?��{�*>�]��l�
�c+l��K�:1���r�P�`5®���/ʗ-��Y��I��%��~�T�o�Z+om��Z��Y֭�s�J5�\9��~����™�x�;珽���p���~]�Ȗ��J
�S�s�_|>�0Z|~�C�a�ҍ�����)5���oqP��������i�s�����jh���<��欳v�K�"w���[��Ot~�W��6�w3s	��!2�W���91�Rd֕��H��o�"|�L�&�8O�U�VS�ξk�g�H�e�tŇ>�tjb*>��c���g�9�\{�KΡW9|�?Y�o������꛻b(��p�:��b����mw�9���5eͽP�W���G3��fB�;�G_�詣���wֳfU��ȉ�8�օ�^����,��g
ʳ�s8�4:d�C�7>��\�F~1s�5�F��}�;rO����~CG$N�a=�yQ�|�ּ?�	�4�:F��L�U?Ig��Y#��G��	'�Y�N�|_�Ň�yv#���w�6��vY:�Y��8L|S�K92�}��Yf]�π]�]�_}{t�Uз��թ��2���Ɲ\�.}�!�A^/l̐��;W�u�
�l)�sS�2GZG�G�W���,��Fk�G�2��ϑ�]��P�����z��l�ӄ�|$�[w�޻m��m���/��20ቩ_��Q���-�u�~�R�H���lɜ
�Z�?���&_����-����Y,q�^�qȳK�3 �:����A��s�]G�^�y�3�s�T�R*���8�oW�1;��E�������7�8?j����D~�~{v�[��d^�y��yT|r�����Zg�O�,���ױ�z
'V\^3u����GHh]��9^8�;�����_?gN}�BuL�n�r>dt�Ѯ�g�|��/��-7l%����_�ќ��?Χ�v�	Z����<�+�8���V�{����l��c��K1L�~�NE�]�5e�|�S��љ�s�o&yZ��0}��1ɞ�Gg�׸�Տ�U;���er�j��G��e�hO��3��H��n��=��X=*s���P��_e�|��!_�l��
��;cN�)��3��`�3����=�������%�����z<8s,����p�[W�r�_�VRC+�:����
��FdW�UL�_ȵ�֫��˨�q�	x謹pl����:��>=	׾��g/l5�|_�QO�8�b�y�+^q�h��{οW�)Ϭ�&���s?T�9�*G*�&}px[;�^��3UЙ��/|�4s��Ct����gD��|�C>����8J���r���P�=�GUL���ٺ��7k�)zڝ7�FMp��NG#���y�#uzt��~UpF��N�ߌV����&vs>��vŧ��p4�ù$O��(�bh���C9c�r�e�+�yq��?�i�s_��ɶ�K#��-�٤�r8Jw����xV�)�!n���6a�=<|�p"`�=�k׺lUj�G8����vi~�(��^<����Qpq��:͖�I�:�l���/{vۏ㣗N�Q���G��O�,:u�SV��u������;�C�2'>{�Q�~���s��?׻��u�_�p��a�u�Ⱦr���A�ض.��)�i�ud��sfA�Q����g?���N}�P�{m㸸�7�������ǧ\{�~�E0���[�t��]8���T�Y��9�!��?�+�������bh䳭��g�>��貋��Ց�@S8ٛ��3vI�S���R_�Vl_�8v��Y�&[���/�S'ϣ�T|���J/�$&ڊ{��VG���N�a]��g{EG��ϸ���Y:ܐK���g�({����9}KG[q�"�5��X�3�q��z�j'~�Yq�+�|�|� >������YL�����>��Qm���{&�w�3&��E����I�<�9��;}��b]�1�9u��~�rv�`�:�:8M:#c��/��_�CE�'���{N!΃uT�ZH:�Y�*��?Җ�V�%���saJI����F�Ș�G��S��D�@FnȐ@J|��p���/n��y���}F��[��ݛ�
���0�8���ѿ�'{K�3n!t�/[���c�U�@8�#�}�����/��Pr���{<Y��ؔ���|.����/[�/�f:�Z3�-ԇ�Ʃ���M���g�|��5uH��7�U�n���@g�|4px;JA'����_��O����������7�1��9��v���2���?��<���9/�g��k����:	��3u|������ϳ�R�U������3M�����Kr޹�O�_D8�%�19�MD�E������Y�|P#��5�:�\��7A�V?c��������q���挙v_>��r����j*�8�������~}�S&�J�����u���8��U��ކ_�B&W�p&�s���U)�8w���iލy$�_��}�׍� �u�uȯU;������<U�vj>�iK{��19/BN�N�eW�C�ֻ����+8�E�g���}r>�/'έ֯�y��N_��j띩u�5�_���U�w��t����U����U3>78SG����:������Ŀ{�s}�*8w�������b/�v�_�%Opb��z���:ϩ�=�j2��V뉎�#>�R��̔�������HO�
��;b"��94v�����=����w���Q{)�N��J_�cp�sĸ�c�C�^_z7�y���zW���CC���P�k-3\h��,�V-���:��]U��Ho[VK�����{��&�`�e��:�%Ωs�(zOL{T�Ф�V�8#ߦ�i�$v�O�u�/���]~-��7�z��|֦~�R=��g���)զ��!ӈ�z�A������]��f�Fy}��){1q���ȵ�6e�T`�#b��c(s��y�%�B�¸U+y��Jg.���g��Vo`��~)�p�2�
̻Tv̜H�|F�/�U�x|�)y��j��ۥg�q�;-؁j�A�����8����#xD��1�WQ��^����/�u���g�/�]e��-O��w3�٭�q�So�Z�V��Y���
�$��ےݍ��
gɿ�l��?�z��L�q6~�^�o�'��?_���O>:�Arn}�=r��7�6�;:#������k]:���k��������p;]j�陵Wӳ�'q.*���Je�$Y�g�i���KS_1t����J΋'��H>׌?��Y{zA�C����)���8�^�>|�i��Bג���%}G�:�t�M��W���^���ս��y�+�A�Ot�7�b(��8��_>���o��-��Bg�겵�0e�kbf�w+�E��9�E�_q	�3�yO0����n�/p��R��\G�utj�gղ���v^�-����;?e���HGw��}�>��O��%qT�{wVdW5�]S��o��c2֞F��&N���X��a�Ca��M����-���!��F�]���F��+l�}f/N{sV��U�!�c�3ga���qr�?:�g��%7h�M[��Ő~ם>�H����	:<Z=�g>����s�!�����Y��:��B�n�W�l�[\�j��]:�Z�߱K��p���u�0��!_�'G_rDٍp�U��Q�H������LQ�M��t�Eu�M����{��W(w7޾jD4,��eʬk_�#�j�Zϰ��]��m�Ĩ�1�E �x����\b�r���©���Μ�O�g�Y2�%s�rF��gޕ�t�-��<�z	��*��O��G�R�1���!�-�O>�_8��#�Mۯk�<��]�j��z��ߌr؞��Cn�*Y��9:�#�i��p�>=������%�'��Y�����箚��h�+d�"_1č�5k����"�bl�Y"k,�L_���y�ߪ�Ʌ
�5_�䐵i�Mu�����?Б\�é���pT�Ϡ���kU�b�)��FҏA�^�[�)L8'�KG����-�0u[KG�f|�,n
�ħ�i�܄s
tza�v���2R'����K�͇��os|�}�X�_�:vGqK�#k�տ��NUb���o���5&��+ߍ��z�5����I�R��;�7<'��g�p��;��<�"ﺙ�D�w�zó�l�Nm�$�~eE>*Ύ�1�{�u��'g��e�)��Ź���s����>~8�|+Q��;$nĄ�F��ښ���"�=}�%7�6R��Vkh/x���R�4�wͼѹ�殍��n�z��������!V�Ys��"�)�u��+�߶�T�a%u\IR���#�G]3�R��jM;�0��ú�g����N���Ҫ��B�=r���Z�R���z/S6�B���n�)�rٻ���	#V��㫾?�#�3п�����_|�s��컔�9�3Dy�ڧ-8�5z9�׵^n#�lr���3��^Bm�<�o�ƠG����s��ߊ��T�w�9�J�^�o%L%�l�3��]�+5{�-֏�\q�����z�`�%k��A�9��|����u�f��g�/�wjԚg繥?�\���Ѻ���_�>+H=9������E��j�6��7�$�2��?�}����U�m��g���Ź�Z���r�'����c$�:U57|6�~�)Y8R�[��l���?��GOլzV��d��z�y ���U�.nu�ᶏ����>:���A:��wK��)���ӗߟ��{�>��!o���U�w��đ�Y����[��?�}��/�<���ի�V��N�_@����v��Y�������=}?�v,>���|��[:����!{Z�z�u׻Ï|���f��J�99���G��`v1��\9��j���59�G�s��s�?T����u�˖�*���2B~w@Z<�_W�}�,|���.��T�Iv���'1s�1�)�@I[�[���mŤ�.'���V�w�u`��/�tڀB4Ӻ�`1q�yv��)V�S��"_�t"9#���c��;8x��H|��j��o��;:���aJg�C�PLTC�ӊf�f�u�0�Z_:5}��
!�,>�F�c���@��Ulu��z}��?j}� �c���w��[�d��|p��n�!Ab��4�c�m�k4�U�p����q�,r��Z�W��Ç^ֱUEs�������w��R0g%z�}��9#�p)ߨ'�=���POU8��f������s����;������~������.�p��g�T�8~}̳m��s
G~��_��^��|>l	g��qzȯޟ��̘˴��H���}�,S�����M<���ٻ�/E��;&�)}g������紥�q���[g85.>*����
Ok6�����t��1��289��䜧�]�#nj�2�t����Gz�je/�d&�3�
��E.�Q</�N�z����i�M}�0;�8�uz0�kV��ψP�ۮ���߰��蘇|yףc�ݟh�{|�/f��Hq.�K|�]�ǯ8s�<Oށ���~x�=��9~�	���{H3�^­�X3x�Yɳ�{��W�"����G��3��pb�/��UU�I-���;���sn{�f���tB|��@%}�oԧr�j�I����8kz�Z���4�oqX�o�/�A���J�����Nҷ7�%�����}���
��qƮt�\�U��\~5���cʞ�S�u⬾r>[	!��K#λg�?:�Sg#g_})�BS�l�(��?�����@��:
Ls8/[�˯XrM�w�],޳�vj��f����8+���<�F�=�n��%���O����s�%�:�:�'V)�y�^�'�v	�q�pXu�v�˻]����U�Q=��>��@�W�g��=��ŕ��w���k�_Y������������w�>����~r��YQ>��pȏ�p�b��������)��&O�{�6aE��M����)7�1q��b�_��dڎ�s��prv3�$]�U{"�|���<DN�+��r��y��&z���gͧˁ�����Ϸ����钖��͵���|\��K������2�ݬ�1q�;��\t�
[�P��=e{��k^>������C�1�u8+H�&�93N�5�N�?x�}B�=����g��b�o�\3���̞s}�i�u�HV���ٳQ�TK�o�����W��l�8�qꬹ�,k�G�3W�����C:W���3x���ӊb�>��V��w���������T�d��+�]�;.���yOB��VƐud��H'�%H~e�b+�-3��<
9�����=W�tg;g�cq �:kr>��np>�8|�8�i�'<K�Ěr���ٷ�p�Z��仡Y��@�q��9^��b�;�u��|t�3�K�+�r���p�E��z�Յ�E�F$g�H.�M_�|�׽����
��8go��əO�~����ϟk�uE��{�-�W?�
�g���
�f��K�[�=�Xv#�liFB�)��\��c�`����%��}[3xp��՚�q�wQ��	��L��8݋���3�z�Vy�]�לU�5���M�u��C��:�⌬xZ�ܭ�y.�m��fᡚ؈�y+���ɖ{b�z1z'!�ZZ��h̹G�������Ц~q�,n%�[�!{0�����&�i�=��1�{�G���x��H�Cv��æ��u��;V�v'f;��_��jV���Y�U���S��-�	7�1����_����l��u*��~v�1��=���̋�O���c�g�M�ώ�����o���q���ca/l���R��ǒ_��/�8�W�
<�l}�y]ҙ8�p�<9h=����#ƴ��1髓'��珩�\�̿��,���S��u|��5u���Q��ik.���1��)=Tg�p�:�7�����i,�O5}g� >�(S���x��u��v��¿��R��Z���_ݝ�è��`~MLո^�����Y?�b�}]�uj�|�g�y��s�!�����y�?<'7p����
��?��Ql�6�՞���׹U&�3���/|m<?߇��9�V�������o�$K?ֶ�w�?#���8�Λԫ�щ玹a+"cX�!��%Y{'|p�+*1���L��O��Mn&�b�*�[%�����,���ˇ�5��w#Ё�:��9ԯ�U�,|�8�~�� V[G�Y��9m�7��J<]9;�Eg)�Y?�oz}��=�v�lE��b�.u��U[G�O�۬����sjX�b���'���|���wd����v���R��h��C�ܔ0᩟��ﭏ�r:�2��3FH��pC�|��٩�2����ߧ�'�O>ڋ�-n��1��7!>~�Xe���NE_��_a�8����٩Se��v�(S[��07��%f�]L8eL9m��)�;.8�8�11�_6����:F_�C����٦�8'N���T�S���s�5���)���6��^���8�^�'��Ä�-i�o>�'d��]2��'�-���,|�~��ԟ�!np�O{��zrF�3�z�|'�)�8gMo��s��Qx��L�%�tT�^K�;�!Vg�-�!�Sv������q7�G�G�s�O�}����u��Ug�n0�3o�CR���z�k�#���9s�~�M��s�
�6r?8o[a���|_w�*�<"����,��Ĕ�$��^��,_<���k�����t2ߗ�y��sH��v�g�<��c�,瘭���=�O�1��e�����A�:w�~C��Ep�RN-��]�+��>��d�ϴ����{~�#�gS8&�ۀ��!�
[C�J��/�>�2�!����=
�9���/>Y����Rj�}��/G'�Φڎ¤��u��OY�9�3�I�q'N���-�{a��,r����{�Ro�p��'��@=�~��~q^a]gS��G��̄���b��"�I{�e�I���\H�������r��Gh/��Q�"�y�yN�K�ȣ��r�1�%y3���>.�/�0�
�}m�
�0�����8�i���=�����~���֞~�>�'U8�_��J��K�%x6���/5k�ā_6�����Q|�����8:�ud�"���Ǚq6?��l<`f��ᣝ���•��FG�E�UK��{b����8��0�Խ��c>9���1��M��}���H��w�D�@.!p�3y�\����j�ݟ���_�)�օ?�g�9o�v4�D�g���i�r�|��U�w�k����(���3����
s�fo��%�/��WU�����u��X��O�jbz�����_%����ak�ׇ��ɻZG�6b��1uzڍ����^��p�!S>�0/s��_���?uFkS��L�Bv�yk���U����[�8&ț�[	�5q��6��r�ɻ�um~˲�4j�1�Ӂ<׳Vf]������i�	���ב�ӳ�D>�����-����7��![�m�-C?�K�-,�qGϞ.>�O݅_��o��L��'>�)���\W+�ܨ��Q�jk��E54�/��j1e���g�o�U0�&�D��E(g��
�lM7a��Z9�L���0e˵��X3�Uo��
ܨ��d�FκS3u暉��7�]��վ�L�;L�z�9��frF.�9�rX���#�y��Ы�1�X��iv8_1	�k��vU�)�!g]8<��L���ώ��A���,����ZQ|��/��[��7O������W�����(��;�����~�?�ݡub�O7xV+YS�s}�,�����m뮆��E�SC��8t&j!O���)�Q�OΎ�C_����1Y�y#7���z֬�3zG���r�Q7���gͧYg���8�Jg�3��h��߹��zg�+<׃���yB:ᪧ�.�GU=��N|�_�Z���;���W8E8�r��|`��ɧ�v��w�lS�7�ez��iE�}q�;(��_]��j�gS=V�UT�RSܞ%��Su�����[��at=�?r�=Q�Y׳Q�/K���}5c6�R��P��{'[��S��5�[�	VϠ�:��/�?U��S���E����A��N�P�S�xss�����lXXɻ#>���ȟ�ǯq�>�8�s�h��X�_�i1��݈�r�xN�>��sɔ
���1ף��{��p�Y��2q��Q�u�c�<������,q}ֿ��v��֋zE�N����Pݯ�td�?�g�;{�]Io�ߒ�5���U�Ok#k�:�r>;ƮS�K��S�#j���ɏ�s������l���;}e��·�Ϟ��Y�\����S��R���YdH6g}��:�
�\q���N͒5���P~���|�=þ��,�t֦��(���rH֬W�ͮ��-��w�4�l��5�F~�9׹r�&<��V&[��~M��o�rd	�W%k�[�-�C�}�D��pO��{'Ys��+����=9$73��{A�eO=1��O[V�,-k3:��r���3&)3e��������ɚ�����3:Ⱥ;֣��rLX7�*g�AwK�;��u��á�g�f���+/ڧ_6t�L5��k����p�M�����z��'�al��r
OlE�}����Gv����J���P|R�2�sf�N�5p�L�s}�!>���y7�
�?��u'�+~��:[y���e�w+ʯy���y&�aZW��Qr���KJ����of����]��d+١,_x�����
o�����֦��OY�X�3c2������=
(�8'qP/M�xQ�LZ8������a+�5��#u���(X.y��7�0�d��l�u�}-���x��'�Yu$7��ߔ�Z��]�K_��ס;��#|W?�gC=�f�l3�a���֒�0{ _���oV1����7*�f�_�N�����w���y�z��3�Z���z����-��7�h���UЯS�D\���F�/��݉?�\/i״^'�b5�|L���j���P?�LX'�&�%y�����5��u�jt�E^kd��fq]kS�����f�Ei6�g�5�YG���Y�k���g���m(��^��p^���-����p�1�׺8�y����ʯ��������n�= �c����3�AM9�V�|����D����<�5zH�0���:;|�Vc�7�Ĕ�޸l=B:�������};��E}���u�X��]}����֞��	�ٚ�kn}
/`N99OL|G�Jq~>���3�y����?Й���Y_�.��2>�m��4�O�k�/_[�:`N�[�����(�h�kϺ�]��ϻ�f�Y�����|4��ჿ�J�,>�����_~�jk�N����E�����S�x�����gŧ��I_:YC�!��-�3�w#ܵ�<[���p_�¿�푶�.�ٻ8�����'@'��>��7&��#U����G1��0�q��9���d�7<�}g�^��E�f����@'�LFUG�q\|"<u&�u/�H�mt�����y�3��Ż���Z����ӊ�1v��Ш`j!}u��yc���[=3�-�aƧg}<"g���м��f��R�����]u��Y���=�/�W�,|=�L�9��5��'�1����7��;
�ٳ��\��_��^�;���=H~Y������jk�mهH}�y�{��S6p�ٗ��@9�΅FX��&��gM��j�+{-{r�T��0�E>���3�f����
�rL}�����_K������u�L��'�5q(y�Qm�y��
zŰ+�G)~��7�p��p��?�׈�4k��~_r___D��s>C=�=�ʔSGu6:��"Q+7�Cn��7�y�f��,|ʬGw@��~>������Gu|�K��2�8�I��ҙ�П�cȖ|N���2��΁�Ę�E��Wd�7g}x��'�[�q�ů�����C�,Y8���g������)}g^8ut/��ҝ�-�����	�lI��S�T�b뜜˴5
v/�Ő�8��|)�z7�l�t�ؽ8?r�첕=�p�CV�:N�Y~���Rx�tO�+���N�ݏ�_J����q��ǀ�t��Jz���C6��Ď�d�yʊ�X��ԑ���>V�ʊs�����;k_g^^�����_�R����}���u�/�9gx��ȥ���mʁ���=5��+�g_}�<���|��`!9��	N嗇�U�㡼n&�%O�k���'X�xy]���
<L�fm�������s@n�_SG|���b9��ߠ�[X��GΩ�|\w b�)��.�3�^a�ĥ	g�2���7!r��y+��ӊ����w�gͭ��� W��&���q�B�B֌�[)�l�
Uwzl�}�&�]������s����C���Sr��G�|��j�GݼG=	�v��8�c�'��Y����j��_�.��P�ׄ�~�cᷨ���SG8n��l^u<�Mيi� ��8���o+�A.�}�	g�o���V:�Y�{W�PL�*A�A��Ù�%����#��Zً�;�p��L60�=��;�pc���}�ڭN����Y�ч��'�<�l�L��8��%O��浞���K��YH��[�AΚ?��=�ϸT�>���Λ��|�&]�J�{���~6�Ug�~���f™��VSʦש~��W����*�cW�<�41�����_J�>����7����1���E:S>Ϟ~�g���/|�rmɡ3Hmġ�88u-��&5q��2����7�ўGoվ��2�����=�R��j�b��9���
��1�~a��8�Noeڕ�us�[��%�s��:7<�l�y���p:�^����׽�����,,����$7������K_?�&y��YF�A�!m
�K����-��p��%n�����)���I�������u�LΦZ
̇���,yN����WO�{�pV~u��η��ܭ�X���x^S�,0�ʣ��:�y���!�'������&�iKv�?�U>.�J_�슛���%ѝ}��/��!�b�{^wL8���`}�|�?l<�s��o[qH�W¡Py���~F=�	��sǀ�r�8��"뮾jw�LS�qÖ�]��8m�Ψ�|6tn�a�띁�8�<uJ1pB���	�
%㠚��q��޺Q�\?��{�{a����W*8�m���n��f�����:����RO�OUY��򃺰�o~"�a�Yjh���c�<&�+�δ���3�г��
C�^�U�}?�����!�9�9�A:�kr��#��v�apVM�P-+������V�qT��N�c�[�x��9:�6�S.�	������X�����&�g��-��瀏q�@��C|ʉ-��b%>f�9�b�u��Q�����J�Y���a>�9��H���?���?�?�_�dYߪ���zN�3�j�Gd�H�Q_h�R�M�ܥz�g��g�mB�:F����sܧ���+7�٧z8�N�X�&�:��g���ğ2�\����y�~�NtT�d
Ȝ	�Y�(�◸�G�p��L*�h6�����q��Ε�	�?�򑘬���!Yv�G�i�����R<5Jb��x����7�ӱVT#f�{�
k�zð\7p,���
d��T�*�<�'����:�`.[é��x�։#�٦��:���'ݿk�!8�we�����Y�$���8��[��Q�p>��myᛸ�G7�x�Kꗏ[�7�S:�bn�8�������Y:�.�V�q�%;D%W��!Yy}>�7<�?��lQ��}�|����(Z�)�d�}'ǣ�uj�Dh=�
�xųh�dK�U�+H?T�g݌_Z/:{+竣��Y>�Y�����L����`b�=��ښ�~>[&��+>E�r$a*��/���ZV�±��T{9��t"�L7�Vʐ>�[�7{�7������#{֠�w)LtP|�?�Q�6՝�)�B:׋��хc��˯�h�[��y�S��������YtB�}������9��aǴU��t�j_������ݵ���J'��׹<�
[Z7��'��抧�Đ�z#��������~�FLœgɩ�W�1d69��3�!{$�8���p�Շ�{��?�eި�Y?i��
�g��[Oo�WA�Ӽr����ͳ�>����,e�:��
5���|���WNR�v�C��?��=��f��'��ND���'Y������V>K(�3����s}S��(�q<9�,C�`��_]��P^\�@����*�緻�z�}�l�T���Y�f��/dյ�)���
�`=q�]�T?�?�b�Y7�Y���r�N��C�b8#��<�ҵadղ�SSR�U��[ϖ�o}T���w�� iH��Z�M=[9+���3X�y���U��%��[�Y��qY}f�a�c��K���'��<X�����ny�9{��'�d0�������Ǥ��)�}f�_>*���X��7����}�`^�N���}D'�u�\|�!���8�Y�X��\�\��_�;��K�ݑ����8����LE{:3���U�G��]�ƛ�����IY|�<�5u��T�b�wؤ�S�b��vh��8kA���'���gA�/q����;���#��/���U�Sc�t&(r��%��o����Jg��ё����;�oY�!2�E��V/���'���7taFb�pEv�m�ˈ��
�f�s��߫��+����S[�N{��תwC:!}2����i�.�eL}��s=���p�ضB�2�u�E`.>��ߦ�us��8�U֑�����!���@b&g�nҏq�q�8���5���g�\�5�|6�(W����U��]��o��8�8��GOŴ��(�v>�!>��:L8�]��-\#���3w�R�c���Y7��>9U6�R��W<�������������l�+"޳pə۞��y���w�z�K��}j�Ҍ��͌M�����z��Y�K���r�V͗sk��A�w���R>��o�<���P5_����32��C�����uR�yW�-3j�wnN���ǀ��'�ID֔7����\�+Ο��^W
���t����z74#p��g�j��+�A�Z8P�9e	8�>�X�=�kν�i��}��v}�5�|\����8�3�_3~��u������r�d8�_�:��Yɲ��Q�gՅz��yMι���O�o����;|>��#p�k���p��[枱����8�S�=$'g��/u���}�n�L�MΊg�Z���=͙n��ᆘsnᇭ���]�w>s����*yv4����xQ-S����M���-_rV�cp�w=�j�s�"��y����Z_Lg���f��]�{���5G�Ht�@5}��%��3�}�l��8Լ+�=��ך=/�.�k=g����?t.�3~�-��)T7K��YO�޳��7�8`��Wf��띄ϛg�K>Z}r�n w|��Ŋ8����7şXg|&�Y[���̺���,c�'-�pJ�ʪY�D�\9{	�n��:2(pk�$d�[`n����M=�ujt�\�8��D��OL!���tQ]�ݵw'r���b^��_�&}�gb�Z����	��Ws �Vq���^�vy�ք't/�9|���]�spR�1u
����D	b��n�eȏ����\�)�b����S3���O��*'e�]�u�񙇐ɝ��Fn��c��ʋ�����g�^��=���ք����m|K��oǔ�]0ҡ����x�-
*��W�|n�B�]�|�}4t^Yg��s�HVL�����u��W����-��p����3Ɗ�>�`k���<�^P�?4{��5���}]k�]�k��/�˔o�V����򼏍�J������s���Jճ�ќm_�3�"|�4�
�lp��⬹캗 ̜[_�%mq���y�E:�ѳ����G�RG�ٳ��r/��rg�E
|4�d����[g��{	|9�:f�(�*}q.�8�&n��2��Z�8���X��%�N[ӵ�o�#}�5k�ϊϚ�+�yB�l��8��{`wrv�ɿeO<K|Nޫϑ��Kܔ��kǠ�׬�~�7ջ�|����d-�ߓ��^N���K�^�����A@'�����'78��ɳ��uvp^iW5��Ϫx��8�=���8��֟��u	������{_��>�+��ۛ��s�T���W���s��l~�Hgb�_8�m�"���>
8��^��0c���&yL�5˷Ⱥ�
��zhf�t��s��s���*�����ޝ5=w�U����k�\�ܦ���~����y<��ȥ����^�޵�9��ė�Qʒ{�oG<u�@��
�rQ��1)��@_~ٺK���)9�g�yp.�/�p������Oζ��ۣM��J�嫟]O��78�XS�����%gg潒;|�4���e}�9�>q��ݯ�O��y�����s�qL��{�s�?���y�t��j�)%m��G�V�V���`�֯�(�_���u�g�v��u�z6q6f��z�+��h�/�![�W�C���n���	��ō�+R'<g+�>u�ɓt�N�茡8[��޾{܂闌e�k#V>|�T����(��H0��f��w�ELJ-��.}��ٔz�1��q��z�j��������l�EG��Z���.�1e8�]aj|�:k�z��k�;�@|�-ο.��;��F�.>�������Y��%�݊O��v���so�mT��_c�-�LS'�.�������,��;g��G���t::~��}��'����Zd��c�%�_Ȋ��M[`��W�Ɨ���dr��w�Sw����8o��g�o���йa����c�p.*nz7�������4��G+������}����bx9�*�r 5��=�_,cw�:i©[ut��>�-x�3���o�S8֊�}��ꑰ.5�����Z���"Sv��/S����8S:s�-�0U��ͥ�s�[E�'�k��NMYk��7�sf/����U�&}����_t$�9��5/7,I^|�I�i�t���uU|n��7�uR08�S��5�r�{e��z���X������|G.Y����W��z׳�0��qM�:�K�X��bń����k��i���Dv|)5qj�o�������h�'�X�,nj�"�U)�5쥟כ�-���g͞�]�/�Cv+�ƙ��p(S&&���������{�.~�є�K�Dr�SʽsK������8O�`^�����?��¿��U��� �䧕�[��c��:mI�ܶ���![��ߵ�s�+?��O�����q��?uT�_#^�{�ȓ���Zى-!���s]�e���Pa���F������g���~E�f��U���	�k�΁��=S�v��w�6��Rͪs��[���`�?m���g��u�OnzV�t�G�UKQ�������=�Y'O���C>��0��]�mzN���g�Z�v�.�߯š�3M���1�
���֭�,�WO��2�ek����9�>��K�o[��E:�C>����ևd��;�N��<�u>�ܯ�^3s��$�C��+�v�aG9���S޾��4�d�د�Saw�X����-�s�r�Q��)_2�Ȫ�i�ɖdW�I��~�i�C���-�_>
��Yd�-�,��秐��I�Mf��~*	ξl5�s�TɊ!~��ǔY����M�—,߉I�|x6���(���,<t��Wv����^�Y8_�98�	�i��VIΎ_Ԕ��Q�G����n&'ȭ�;T��C�7�O{��K{J<�pz�]�P�*��sت�y6�R�ܧ��u��Y3��-9�o�����߼(����ҩ��������Ga��-�g9�1q�|o/�n/������m�"�z��/��g�|����M�˗����&_�VGq��[��x�s�3�IξκCP������~�<8���������r�����C	��N����;�R���#��x�Z���[O;U��)O�z�:����d�c��'�qu�M��{�\��A/���Z�Ϛ;et����1��~EG�3���?s��Q�w�~�i+�����O��5���u��q�=,����=!�G������V�;�o���p4�<w#�����M�J�K�p�1qT/N�o��;����ߵ`���:Xu3��g��v��w-��pvM?V��
����`@5eMy��׹�Ūݑ_���|�������I[;���K�Z�X����G~�γN>+[�scf��][�u<	8�q���✹�߲����Z�ڥ��"��E�g��q+wꬦ����O���q�z�>f���r���H_:� �|A~�3�l�w��ijQ���ܭ�������sN��q��8�R猬Ms�3>ʑ����q4ٺw�۪֥�4褿s��S���=�U��~�o�䢿q�8 �K�^l��?�{֣:�lE��T�X<�d�]}|7qϡg�0�����}���J5t��׳Y�Nu-b�>��J���r��|\�iK��l��l^5�r[�9X�]�K�˞AM��uo��
��pX>R?��U:�E�8�u��z=<��y��^2V�PǷگiUu��=䂌�Y�ޗe_����^C�}�r蝹�F+�p.�k��	�RN�WӧZU��Yd�[��9�c�m6u�El�����h�k��{�y�
;'�:Y�S��@�#{�>1��::M��GV�7A��A|�C��>:'e���G]ޑ�-���G>�~e_��q$��\�g�ߞ��H��q�(�����8��]w�[+v�e�0���n揩�p��^����G
���I���4W�r�����pS]�u��ީ��Ξ�ɺsᗺj��]K�\wO����쮕5�T}�칾j %�����Qn�W�r�{�/ U#J�_9S�S�&1It)OA<��[�y?��jMq���tjU�sT����>��z�(�W��'q�ZW�\=gmd���>9�C���x�+_f<Uo)��C��c�6|+�;�:=s�/l�ǡ��[��ܨ�Y0Cu�8����GU����[�E�Jf=���Gqn['T[+�����O���Qa�U�>�;:���E|�_�e�@Q}vd�}i�#��22nM����Ib�U7b����;:�+Og�ӊ�{�W�!{u�G�j.�׫��ew�n���S�C�M��L5Î!Y1�{�'OSO"l
C1I���z:�Ϻ�v��^��[�b
_�k3t�[�N}0s�OluW`�P�+|ոY���-��;��
�L����V�_��lSǦ�˵�st�K��I�T�K[���~g�_���e�m��-ׇl��ͨ�'Ŋ�q�+bꈿiv.|ٺ&a�5����\:M|��$X��F<y6�)e�a�$��O�_U�}n#��R��ώr�S83��Dm�M����9����㥙:�������V�Z>���f�9�/S�'O����ptV�=�G�yHSO�16>:�'�]����S�?�ڱ�捾jwtp[I�ǘ���>?}�w���yz�tt�۩z1��/��tV}�1�XwX���g����\�=*�m7��;�O~��̾Uc_���7g��C��?��&>��9�"9�]8�b=u����8K?�9����u�!\��Κ���l�鎼��S������kU��~��;�s�ik���ⰾˣ�y�F�C��_�hY�zl��g�:����i��tT���p�J΃���sb�Z����8=f.�1Q;^��yM
�a�B͕��:,�~��n(�*�5�qΜ�s3~�\.m0
������c�!9�*sU�3Z�S�νt>�1���K�un�wdܪt�}��/�0�_�B���n�	�~ö%ʻ�/��_{�ؾ��.S����Y_����WI�otFS.׳��r�q�[�����T�W1�KC��&��Q���v�H��E��y����^��Z3>�m_${L$QP� ��<���j�"��'GS/aZ
C��hrL��>d��`�Fr���Y�Ū�����8�>"��Y~��I���Bv���v��@��̅��h��OtT�6�ѝ8+��*��~7N��
}�+�L8˖k]~�����s
ā����~cJ:Z?�Č��%nZg����pB7�lʜQ��p3�񊏆-o�5g��e(�=T������aǞ�>������!��o��̯�!?Ma�ꨟ�l�n�a��϶%�����λ[�Z߹_�r`:8৭�)>ۯ�s@ں�������&����Χ-嘭s���SV|��-s�O>}��,��?v��tpl��.��7��3_{�>��|̉�J9�׳���#��P��w>۴_�܋�<}���[������~��z�Zջ�]��Wڪm�ND|d�:y�8�~��_H����p;[�q>�?��y�>�<.�`�J��]�G�y��״�l�5U\:��>��k\
Y�U��П�{/[�d<]��F������Q�-�ʵ_�ؾ\=�_/�>�#�hGއx�
}�'�w�H�*V�iF�OH�ž�:�L�����g��M���p��ġN�8A���O��:U�Y5����{^Ͽ�Qu��Z��J^N�&>�� G���iC�Ȋ�։�~nG�5<9���]��$�8��#s�į�}�����e�QH��~J����\<��-e�q��3��*n��	|Bg2ٚ���`^�� ϒon囮�Rs����^�|�h6�8sx�5�u0	�*��[{1�C�Ԏz {�9��:��4w���8�a׭\���mW��,�}�E��n���D��<[��ք͜��]7�U����G���`�y=��;r|��'�L�z�<���!�i���çNEG�L���x�2qԝ�*��3)�D-���b�����k.�
>|ʌ�#�Cb�w�S(��@����
S��W)}=����_���:�������zg�H�V��<uv���)7�O�O�{�K��ԩ|���3�&���^s��į��vYw�r��%/>�q��g�p|L���i��n��(8��|0u���L�=V������_�֗_���h���q�1��OV�)߷8�]>����r9ZU~%����:{��p*Z��_R���.S����CG�i�""m)O��R�κP��л����g�
�u�\�K��w����݉s�#s��3�ޭ�<�ӄ��S;�����"�)g��Gq�8�Ϸ�>Ӊ맆�:K�w�k��Ygr|��m� {/�Ѻ��{8����S����ш�:������C��?*���׌� ��;['����9N��OM>�J��I���� ��O��prac�pſ���;��FH|��(O��S��x�ߑ�܋�Ǫw���E�`�U�3��~�(��!S�6��C�b�f���?�����]����y��l50}*��b���ԯ��֍��ލ�����~o���܇�;��>W�D����~a=�ghK�I
Յ��#�7JLg��>�w�׳Zw4%���_:g��/���%gsh�'��c�r�<ԥf��8��F�F�D-ƺ9����/q>�:r�8�L��8���uq�:��܏-k��R9<���>�p��S�z!
~��y>P��R��.�����w&v����n�ğw>i����۔�7�{���_��g��|�s��Ƿ;ן�gֵ�S���%���ߪ]a��>?��;�|���9��9@�~���Z9[��ْ�8;/�/ p��[�3
�����u6f�o[;l��N��%D?�q��A|>1��-s@�o�m7u�R獳����E��/�>�@p�3���:�g��O3ߜZ�Tg5e��)o�/�v����r�r���k���<w��[}d�*�Z�V�;�Q)��pj������;��ʳ��++ǫ��&�'����>��
��7r�t�c���1�|>8�:���S>�>�}�"���M����W��ns��En��܌/S�(L�ɧ��Cq�<�֍8�����$�L�{�����W�������3�ɡ‘��Plodզ�����Nc����/�`Ͻ~���V��~R�Y���@G5��y¹��gg��8���r��ᇕ�Ëڷ<�<�<���}�goq��}�|���V�Vw�Ջ֝��}7~�3�|�K�|I����0���Է8�@yvy�{}L}��u�����O0yvLnV����(�M�6�Ϙ����:s`֑yW8�|`vr���d!��3{*�<�Wqׂ��������gξ������	���(1�|��>�J��!���\[J��
֙#��ו��l�H��SK���U�����l�aF��؄W.ǯ��$��7�����c��
�DM��U&N̕�%ۓg�7>���J��S��1�#�ևv���n���1e�PǟQ�|M�Ȇ��η|���b�M�m��]�u�9��o₃5d/s��S�g��Wv-�����ʅ��Zv鴥�{ܥ�n�d~r�֯��G�xGN��#Uý���F��2uh��x���V�7<���2�o>����~�[�#L�J����ŗ�:��#������p�������~�|��9��z����sO�g��X����%�N�Z׷u���b�1gp0Y��^��q&��Y�֘x���i8)����~��1��Y8��T ���������/����ʺj�?7���i|�Ⱥ��+���wP����ҸpD���$�Sgg�Y��><�W֔ZW���a=sI�qg���=����ć��cO���3vC�zi�]�i��}�?|ᐛ2;i�}���u��\#�8:�L����R�5�g��o��1�.�M�7>j���43
�E��G=�;Fվ�ڤC�/G���]m�q��H&�K/�&��3��f��x�"���c���#nJ&�>7mj�ê;۝뵰هG_w�p��i N+���
_r��
�:��w����q��J�y�p:r�t�k��ݓ����ó6�K+p��?_?���|��$n����g��<a���G.��=��Sώ���OC�S�J��Vx^�$/"+n'���}�VZ���¯Ǫ���7P<�A[�)
��M<5�W��|��+;��J��3�JΞ�Y�~���O�"����0+w)����,�L�TJ�3�����G��3��6��l�t�Y����a��3�tR�y�H��gO~��g��� ��y���Y��Ó�����]�������q~����iˢ��}�K�|>+���q�gL���܏,�����C�~>���?���³��������j��:��w���Yrc�~�qp��_���V&����\W��VϬ��+�9��K�r/m��Y/j���P\�wPs��U���L3���6ut`�ߙm��bv^ڹq�g�h�.���-
���'��\�~~i�;y�n��z�yp��k�����;��B�S���#:�9F���7|��U�7�������֧���3βp^Η	���B^G'�סg{갧S���T�嚵�Ĺo�C�w���g���q��D1���M��Ydz/�;|�w��g��ɿ�q`�}����0޶�z���ڴ�}�����r+>u�
���]��n��+:�)w*��OA�T�G�u>���8
8S�8�?>s�OU<�������S���{�.�?���Bi��v��fs]uyi{ƿq��'�5㿙�W��弿��c��~����0��]�Y>��g�/�z��a7��Կ��Sy�{�5���Ps�fxE�p�>�~�����~��ӱ�g�{�>��}:h�����L[�;a/t�é�U����ۤ7�SGw���3��{�O�8��y��[;б��=���X���C�
��ű\��s�S�Ͼ}����9S������gn��w�|���ظ.�i��©?t�!��~�ߒ�Q}l���i{L�׌���/h�S�vɅ�+������3�]Ƿ�糯N�^�~v���ί�>J���\?��Sg�Y:[v��w>�XN�/�����9w��u�Q���O���yDž���y����H>I�e�� ��+g��֠���O���sh��x��Y�1n0O�(V���h�� yԎ��ř:��ʮ�z;�j��uvT�85��/�'�K��ph���|?Zݜ�o�O��/[
��8Sg�׹�ל���'g��w^yW�ݏ%n��l�m�~�L|4�[^T��_s�f���q o�3���y���ޟ�R
�~{�I}�W�Zo�����1��YX׻��쾔�J�^�
ͤ5S������*�i��v'�ݓ��/׻D��ǶE�ܩ�V/��|i9㯥�>�_=�ζ:�=\�x��^����d ,�::w �s�U:���})���@�cv^���bȡ�R�t>���_��= �ۚ�"�X9(�~����[ڍ�Y�u=K�a���}��SPY��	b��,��p�ٴ%>��_B�j�ޝ�?qh����@��+m�ܪ�3�U878�����{.��\E���B9[|
��_pֳ�|�[(��%�y
�aF��δk!��@|��f����ό���y<Y2�w濕o�?�\�s������θѯ�9���z��γ��0�3Ak�gǞ�RJ�G�Sr��8H�k�I��s��_��o3��)��>~�?��Vy��F��c8�U�c�~���1s���#l��tj��?�X�\�k/>r��x�
j|lj�8|�Y8���S_�}e^O��s�3qؾG��S�Y��쥼���?{	�lq&�(WŴ{�Z��q�<[��]�R�tx����\��y��ML����א_f�|z'�<�>��u<��5/�ٜ�[���5圯�j�le��;z�����S�O�6e�\͌�M�<'������7��_���ЏiW|���dw�k�~ujz��M�j�gr�U���vͯ�{�5}ϼ0|�w��.t��{7���¿��s�f�⃎�+�~�‰��(��-��xV�Ǹg�N���@�__��4�S
<o���wŶ�v�������зx��dp��yZ��b�9E��8��^���r3�R׌���ݦ��%>����v���S�️�F'�Cg��8�o���f�O������z���H�}<gfz74�)���3�
l
�8�=G
C��[>�p�
]��l�!`�p)U#��jS����oJ����<�E���{u_ɧ�1�n͜�dLw!C�V���}�r�?k�f
=�sd����\�oOF�������S~)����v�^�OzJ�|�4�/ҧ�O
=��V�,��G�}�`�f��0��s�(��o6�б�V��M��y�A�i6r��u���au��1�_�:����%{��n�>�8SΙ�]f�xZ�V�r�=��mf��n�~.���b�fN��SSg�'�L����I�o�%��_G?�11m�2o=��>"��f�?d+k���yv����f�zߝ#���u;�ì<��<B�2���~E�_6u���q�m�����p�~	�ƹ��c�5u>b�g�e;���p㈳�և8D�l���'���%l�a![u�j�گw�k3�*ަg���i>���\?��E.ٜwg܄#p�O�R?�M8��
�7�};N8��_pV��z��wX��%�/��|������.y]yE8�k�,|��}G&�w�z�ð��2�C�_sꟹ�Z�?�<mQ��N��_|��g={���M����x��*V�<���܏��\��‘>|����je�������/}|��*F>��:�;�0[�}�v��ºl����a2_�?��K���g���§��s�H���Y��aL��~��%��bU��ʯ��S@Ȋ�\�)���罿r�ԩ�˹Vٍ�!����y�8�:8��p,�V�g>S���̩�E|
3~�2��w��:��v��ā��@���G��W)�Kg8���/lIg�T��SGt�Ʈak���Ghs_��iƾ�Jk=��6�Z}��,��P>�ۖ�|���H�Z�K:ϰ����!��G�oNL<�NV=���V~
�C{TP�(��\'�z��ލ��zh�L8epή�������~�[ܤC�U��w��ƦR��S�|���᠚�������D�Q��)���ϣ�d����������e�ĿM��J|���R��
�v�����ȳ��<��U�z}��?�簡��9f�,,H>�&�Q��n���v>N=;����y=s�'���ֺd�K'"vo<���V|�ٽ�_9���,>��C���|p�N�^ߜ#v>����O��-}+M~�����w���Cr�b�9c�'2u���'�^�z�9`>�9�l�͌s�;K�������?x�m��;��||�l���\�d�)��5;߿ƊCLD_8�������� ���F��D�(n�*��]����w�Թ>�4�w(���X�H^L�Ũ)�V������3��ϥ[��_�XԗEq��ə}�^����+����4���WW�
/�D8���йG���Ed����L�-��Um*��r��?Ǯ:5�wr�p�Z\�Y�C���w��&N�K�Mr-����n�ʣӗ�'s�b+�ѣ�zA=��@߮���aÖӺ�~存2q&$����W�/Uy�.�yEg�|�M>S�6����7�i*�[忩o�WN"��/���=��B�,�Y�����J-dKv9�&O��J#q2������g�u�c|��-���-Irs�<�"=�b:��/��������~�,�p��)���'�jS����58�OՇ�,^�8�)�_�C�9��Q�į����2��q�E��_5����2�s��̩þ�I�/��R;�p�o��SǢ���;�
��e�~X9ڱrj�YW|�֚���_�Q�*�G����[{�4�#3���剾݊'�O:�&��1.rs��#>3ז������ݽ7��+2�^����/�s�9���3���|&�r9"�L��e�#�K�l��?�����M�<�=��KD~�:x�7��櫎��Z9�|6����c�����x��������_>���8��U���s���,�k��>�N��\Y8ZWm1R���;���9~q�1���;7��q�N�Qm�����W+���aK�1�3t��h�"|9�~��5�Su��)=���W핶�#n�Y'��+�d�	KwS^����1e�3�N�A~�k�P�u��Avվ�k&�l��B���G�V��/B������S�M��{��:���<�'��{��w�Ȑ���s�e�P�tT�ѷ���s)'Q��/S�>��<["g
wqS�b��n7r��G>�a�K'&��}ɜ�G�ْ�B�������r|8�e��)���=톓�]��X=��b���p.�<�-�ʻ�/��<�����+��×���:oễ��|���By��Ĕ߹�tD�nz �o�>&����#~yܔ��5��E.�yS������:��<��+>�̳����9�;��T�ކ}�H��X�(w��w���>"2�i�L��#y�_�{�!.V�8�(�~�P]~��d���g�������=���t�����
}��A���f�p���)���>���#v�`˔���T�����"<���&[�4���Q���W"7q�-��jQ�q�8*���p0�YC����:�zص�*���~b�>�g��C����X>N91�3��Y���֗7��9�k����j���fMgy�~��kW����%���|y0[��<4�Z:��s�ww>�Q�3�n�-�ٶ���K'���I�w�
>����3�{+f��?�
�C�⮑yh���=Ɯm�8�|��;&�:�P/d��c_T��7װ��+��w�bd�~���#1�?yJ��|��;�0�d�_��ry����S�c��NR��&}�
��ڷiF��.|�WiW��Y1L�&�y$�ù�wD��">Cg p27��&N��p��
�z(&U9��o4q{��A���C�+ς�º�3��S/z!O��R��=|�Rm=��L�dR�����K��ꏕ��u��r_����?�z�Q��}��Z8�’O��K���w��;ׇ��O���q&���L�a�އ��1�:������eǚ+�����2ud+Fu=��`�}0�Y���ZW>ʗ��S�2����/{�Н�z�����`w�sn&/
_=�|�Bv���L��P�o��_�=�S��-�g���_���iQ����%7q`���S
�W�O�-s���0��cV�9�M��-g����W�Numh�Qω>lx���
ӷ�<]���w��An�U����g���D~S߼x(�h&M?��$�A-W������V�r(�O��>�����m��?�i1%zG�8w�ާ_h���B����?޹v���
g��P?���7h���!�k�P.�q���j��g�-�7:�����l����XO��z�_f:[��l����Wr��y&йd�Y�a^ۿ����]�ݟ_3�����~�G�ܮ��ck~��k�cf���<\8�S�G]���|�����7~��Է�-��0�1/߶~��}������Sѕ���|W��|>�J7ΎI���G��	�����
�|�;~_�Ek6��Cd.�=�5kO��u뼲�#<�ݜ���,��m�W1�l�5KV|�O��_p4/(%��W�9B�o߳����[�9�=ש��9����a�k�5�9�Ӭ�g@�	f�VK���K�]>(M����u�p�zSO�}�,��Θ�Q�jV�Un6pV���vꆉcԩ��g=�gK֦S綴�8pX�1mu�G��Ϟ��g
_�Ï��v�Y���׹�c�?��gr�Yt���᧾tyW>��/����^�p���"cյ���[�!�'g�{:��S�ğ�f!n�.|Bq����_u�����{�8��r����Η�]�l��K���H�wL�ګ�����|��z��p�����_�G��yѤa'�{�����3����{v�l�D�<�u��Kq&~���9�p8��_w "{op�5R�a%V߮����L�3y���p;�a�,��W��M�NJ���K�|P�|j&�<Ǻrj�ur^oOd�.�u��fԩ���R��W�K�fu꘾@1��Q8���J�`jk�o��g%|�c�U�y�iV\��>uJ�3��d��
9�/���<b�z߹�l�Dn_�]Ǜ&o�n����&Β{�lX�.�����ܟ�����K�&�J����W^D&�gNڹj�گS��H@__7�r��k�~��"��ߦ�^�Z6���Ү�?�"��O�Öp6���*}���}�1S_��>9H��4}�}��Y�����iK<����f��Y�d郟>�~>;���n잏�!���r)�+����ʩU��|�+���q:xv�����eO�kZgu�7�:O:ۉO8����n>�߳yp��|�z��S�uv�y���8��z�y>o�l���l����ى3c�g���%{�a�O�/���5u�A�~|��-�/�<���X��Y@{�ۘ���b�٣�ٹ�}��%�Ya/\�Fg��V�G�vp��=��Y=�/w��g�'O�����7�gb*_"랩�	S�;��Y�rq&�(>�a��Yr�qF�S�f�Yx-7�I�q��Ln5T�ֲ|�F�&�y���
|Է��m8{m�o� oM�)���w#��k)��yw�������N�b�_��jzG}�pf�<��Xu|Ɓ�!�ȣ���#�)�_�8����ܣ;�NL�3T��WH�q��C�N�P�+=8Ł�|�W�۞�Y���/�[�Z��g�J���?G�/Ur�5��NT���D%��|����M��D�_�徂�Δ��f�v�"~Yq�WN��ة�'��sU���/1���ٞ�+t�P{t)�>����.nC?��G}Tr�=���ݕk�;;�(~QS����Kg�@_�u�pJL�4Lw ��o��u��8�O|JW�<���Cw��r���9�Љ#Ǘ�Y�f�@ٛ��N=0&+��#��:����/�̋�~R�q�Z�w��J���#�K)�1vn�y��wy�gm��?�S��S����:39l�\j_o��m��Y�N�o>�/q�gꤼ���'�k8�jr��r����|�8���#Λ�P^<�s�����||�?�����g�Q��:�Nyݗ���\��b�=r�8{�wS�_�=J�%���_�g:r�S���w��\�L@-��=˞��:k~�3{/�{���Z?C71�
��$6�}%��L߇烘>�_�~�@�CΨ�]�68�β�g�<�`�|;0��QyZ�"��#K�~�E��߁N%��y>S���dWgw񤎿%�$&�u��uH��'R�T0��}wb��Jw6�	�?�b5���yH�#�g>�9���_��g;��F,LL�3>�u�f�孏_:��oߊ?5w���ώ>1LߏV�P�>��>�8(�7��Gg�X�`}�sn�gs���s�r�����Ljz{�<��E1KV^W
Ǯ�u���=��_C&��<o駁K��pt&�ߩ֭���w-e�MnK�)�oǗ;���y�7�u<og���1�~���y
y���Z5��X|z�eK}qN��nns����s�aM���_|�v4l��҅�~����>��]��KG���Ku���/s��b������Ǔ��x�a�������@��B�/���L�>6���a�D�͜=L<�{��&>C�>S����L�;T�+&Z?J��d��7���g����6z��y���-��(��D��O���y���g�37c/k����\򡣼��ף����}?�#b��̻�92�
�^g=������ҹ�5�y��㜱���f��7�����9){��|����������'甍\���w|�8����^�y�Q��>.����=�xH_9R:(|�{ƽ��N|�tj�_��p��9c(g�wC{*��`�B�j�U+��{�>CP�	�f.S5��E��_�o�6>��0�E�S��`�c�x�]��M����8��Iw���Ab���P��#����:��p�+��<��r<�}�s�ֳ�'��1e�,�$t��:Zώ�>�"��āwc(���ﰵ�����C��ʩ��nڭw�g��<���v���ʻzW�J|]�W��3ͷ�<�u�p�3>C��]�r��sg�oPY5��P|2'���i����y1���5��U�����gd��%�U7,S?|�-��c��z��{�#�S�A��3��\���}���H�~{��/�����L��n�P����]5%2�,���L�����0��}�d̫z	a�a8lh��wUnr� ����+�����K��F�8�z����g�_e�G{��5bL�V�v���(ס�M~6���u��>'~4�}~�cU�Oa_�K�k�ҿݩ���y��a��y��sa��O�?9��3� GZSϟ|�g�	_�_���sΠ:��R����?���@���_-�m�S�y=m���:��ο�@إ{%���[l7�}���n��U�����~w��O��^���rX֝�[n~���O���OyT����ym����uv��]+���>�������:Ԃ�xs���{G���4qW��q��|�ٷ�Χ��k���s�;�y�����ջJ�1�-�i[����g�W�R�Z����'���l��{�[Y��j��q�4��}�<�����Q���f��G�gX�:��!�_z��a��{�[5��q��yI_�c����+n]���q�8$�H��Oo���:��>��Ƕ�l)U�/���'�p�a��Tޒ���ֽ�U�z�X����xr~�fұ��Ƀ�̅�@?�n>�\�K��k�����ic�n��#�Fch�<�εAo��K��$��:�Ʉ�ܲ�b�j����p�[��ܔK��7��8zV9"��=k͞s��hR��|�Qrp��=!�ɖR����=��������-�ȅ�x(>���ɯ�ϗ����/�任�\,�{kF����Og�o�\���,���zP{�Z�m�)��jL�\�8k�RG��X���:��:��zVr���׬��U5%}ĉ�O�p��.1$πo���瞧40Ň��ǻ~-�&:�w#�{b��yX��й
nYg[���5����Ug+�럱>��y 
�*��<�ᇛ���_�{�����Z��`S�,�v�U��~��>V����7v�JÖ�6���ܤ c�zˆ�Q2VS'lꧏ�I=�J����*C
�<�s�)�t�{�p8G�����R�/�q~�*7��̅��u9���0�t>g�)o��S/���#/霪��⧭�=�U�&��8���3��"�ϻ�?���O���,���>t~��ڂ��js�9�߶��'}?g��9��'�}yn[Ԃ���aWyQ�(����8���iNV�^z��[=v�LMќu�����[dpTO�=���g�O�����$<F�W�ދf��Z�u����W��7�!��/�	���
78�0�@�>���Q?c��}c�D_��lU��/�剿��sL����ɡ;q�b���
w�?:�lA�|^�gCҊ�ڑkh=T+y5m5��3|��⌺�㋽cH͔gD���O��k�'7���ԙ8�_g�N�E��<-�|9J�s�o��a���l"\u<����Y�'�Ul��K:굨���qC�_�K���uW@3~Cg���߳�33ZS�����%7�TS_�83���mS��?ϩp֙U���S�W��_�����r�37u�}ʽ�9��G|(��[�Xܺ�"�Г��u�a]�.q���_nD\��Ȟ8�O�߶�ROQv3����P���P�/���s��9uW-���hԊS�g}r��9����Z�Ӗ���ܗ�2r�>�p�V>l��G�|��F�zQ�)�6�_�{������b]烗��W�.�W��^B8��F���b��.�:1�����ҁOmu��wU�GSn�ټ�%�3r~�;�|�G��rƑ��Z���q�z�~b��՚�Z?�xωs>ݐ��wO�Vޭ��[��9K����-���tn�ԭ��z)��k�o�O9g�[�g[����G����͋��~}r�8�H?��O����u����o9mɯ?g(����wc��?��ꗼ�{}�ʯ�{����3���Y�͞�{��1��3�������a���P��5e:KQǻ�_������NɁ��<R�]�8�O^^��ߍ�5qc��kݽH�m�u8T|��_���1q�Ň\8m�Y�__�s�߮�c;{m�m�-�4\�n�'�ӭ�Dz�17�}x�=.C�,S�3
s�b���Z8�p����Op�&�
v�_��e����qG�����|�w��9��7�=S�u�y>.���uoC~�{�Ź�2��g�1B|���pp���11�;���ó�}��g��a�o3�:?���ʝ�\9/�������|���y����B�xNx��f�n����cL��xa�+��9����\7f�ڻ�y<�Y35�^zƜY���2��N�Cr�7f�2�\U���T������
��֥C������	r9>�2�9C�)u�[wN��'�#u�A.ߋ���ަ�~��Og]�������Z��Usc��Y~�
n�^E�T��3o�;5wQN�����=��p��|�ڹ{^:�y|��/r�\_s�2���u����Uカ:|>���l���8�H�Դu��Dz�WO3p�_�O7�[�Yt�c���{2��v�����c�p�5;'���Жs���3_~����{�\���/̥s�S'sI�Lal[>���I�)��s�I���oΛg׿̗]��U׵�������Aߗ,����3�n�\�5u~��Í?��Ͻ/�}�7����z��_?�l_�Y���8;�n���[�'83uf��3�5�޾�|~�=�Y���S��N��M}�/��/�-��z�C���A���~g8�[�+�\�u�9q�O���6n�E���P��ș��ix�M�'����v�$�T�U�\:[��!�Q��@�9�:z�!�����S�������d.�x)�Ҧ_� �e1n�|\�F���#=��]�op��2B��O��&����d���^�d���=N���?Fp�k�y���\>xg�ۉsW��z��^��m2�k�鼧�|W�zg�2�gC饟^��r�B1lS��[����r6��i�}H���'7:�!8���A�����T����z+��<Ԇ/>��y����w�/Ͽ�V��:�R��&��:�u�E���v��9!���D_FH��s갎�.�x����?W9��|5����_�G������A�^��vKg����ۯ��ӝ��=�g+mL}{�n��Ԭ��1�E�����Aq8ߢܳ�(����=oQs�Gp:�봥8�?"������������z�'�/l�p;ó��E��Z'�Y}֕_]?��$���¬ng@�i����>[�GS������wև�[����q�li���DFcMZ�w����=��~�˕�z�u�g��9��>:�׳���T���ȣ��2��m}��{���O>�m
��O>���L9�ly�s���;�o����g�i��׳�!>�|�0������'�8�����|��'7��o��oW�J[�
�9Υ�|��ϗgܰPm݆�D�$GJ��O����}���˘0;ړ���C��Twz�׉_�
:�(G5x���:��V�ʑr@gf��/騆��>��yr���W��u��zqɃ|���rO�M��V���������u>nغHyw�'��O����y�9��D����u:f���5�⃿�ܪ�Z7��:ļ^�]r#������\�|���p|�$��ə�4�%?�^'��տ��k�g�'�+�+N��ױ��:k�7yN�	�t���N�Yc(�Wr�p�R��;}���+��/��~�/������=w��}��;`z����Ou�yzv�צ��	�l���5}G��(!96�K�?�.ж��l�:�ҽb厔�:�`7p���y��[Oc��h�
4<�2B��-�5��!������C���K^?ɿ�\7?��@�wO���[V�t����R�'O��p�Z��L��]ܮ�N���>jy�uj��j���#����ZX�ِ<�����e�׺��>��F�ЗN�Ư�/>��:�N-��o��dl�����������:���|s��ع!�{�ʟ95��.߹��\���;o���g���U���;��?u�}M�;w^�s�g���:8u��y&h��ý�C����ӿq�l���A}�i�����3G�Ϟ�J��߹-�ճ�����f��v�|G:0��ZS�Mp^C:�"hd}�� 8��O�\�uX��1\9��op��'�
��jt�M8���=j/�����N>��g����:F-���\�]�M%�dE��o}��A�z;�<3_�ݗ����l|濛|�>��)��_�3�:O�?�"Y:v��C�A������h��w�s�j��k�B~Y�A]ە4�j��`@��7��u���N]��{��O}��'O%d�N�=V[��Vٹ��*��J��3�):��W���/z'¹�����٨�ߥ}N+��A9�j4��7�c���3�q�T�����$f�3'T����Çs@ܬ�ߣ�A璀[���y�T�}i�*{1�9}�ù�j�y�o�~)	�u�
��)¡���	q��X��zռc�E��H�u�љ�g*�|py�'h�Z��¿��8�����y�0X�]�"H�DS��ܵ��M��:X9,�1�n.��ߵ/��ѝ�n�x��J�a��z�$�q�)R�gy(S6��M���Z�ɟ��[�_��.�`	�b6ש���֯��#��bs}�ѯ]��M�z��5�1vF��3ر%>ġ�]�K<;�F5�?B硩o�a܊OǼ�YM���j��?�_թc�uj��Y'�9��7��gϟs�	���9�?m��g.���{~�K�]#���G�
��\����?�?��?q�9&����`�ϸm�S�8[��Y�GL�O>y�a+���g�����,[���t�O���~�?�O��/sv�3���)��)�0�d���+��6��r�u�9?�<�Oz����[�<�(������ ���G�*�_L�-թ9��sL����7S���~�/[)�V������+��0�p�ֳ^��!�v�-z˪#���%��@[|��O��l)>]��G��Us������Z_s�ɬo>�u��.ي⣚�|��(��p���@̓]8�WX~匀�;���ubޯ^�����c}�W���/�	}}��ךCg������?1Dv�Q���U:�Y7�B.i���^��j�I�n���&��{d
-�������MvrǞ�{]g b���M�Ϩuƒ�g�ә�u�TNp`:�mLg]3���r��a]8Js�s���I����ݐ>�"c�\|���x���k�{~��RB���֓��":��=�x�Sq��n�r�K:��vç��`�|ɺ��~�f�gA?q��߽t�K���j�)��3�kIΗ;:;���>�M�C���
�W�d�!c�`��j��Z��[�f��?����u�\��̩��c�!�R�_��Z��~����'�9G��g���n�6�g����@��b�Y�������=/^F����<~�/��#u~��H�v�}�S:�8XG�'N�u�>@���#Y1����:�>���ڻ<[`��yD�u���ܳ}���+�W�H���#O}}�գ���q�'N��O�����ԑ>8���ӋO֑/�3�s��rF�����r�1�{��w��Q���N���
�p8��x����I��]��^�g�wO�~��
�iY5���/�?�A�ոwP�_�gM�Z^���E�r\ȅ~)�T��� �!���s�SaMπu�a,[��'��Qw�TS��ǰy>�����t�;
>/�m��eM3�qh0��d�>O-���G��,[U�N�[�yT�
/�wo#nz*:��o�0�ܶc�e�<��t�-�R�s�u�����U�G�f�w�l��3n��l��G����}��h���~��8��.ߋ��9}#@��:~�-����yN7�0Vo�%K_w����K�ǩ6��������}����v�J+}�^Ō�q�}1#oǔ5o���6ׯ�}'�>t����쩛�`lo5\����U�S���~�E3֩�W]�,�<N��$���{.On�ǘ䛵��}?����ͷ�~��![�%�)w�'�
��@����<�:�2����`]�s]~��&9{��a�~��5����EK�N�q���"�R?��\��B#�Г8��_�ᚩ�?���g�'O.v�<���-��f��w�vN=��G�{��܀�g�{)7�Y����T��fJ�^d����6��uM�ǔ/@������<_�s�'���|��v\�>�0M@��~�f�PhW���߱����s��%�||���'����!��à�4��wr��=���@&��{������tȵ�9�|`z%g��ߑ	���t=�&�8�:�
��E�Sn����ӎ�y
s����ѳ�����ut�Y��!O�5�/�L���+&.nǚ7�b�����'�i]u-Jx��sվ��G���*�(n
z7��R��<�٣��Ȟ�}��#_8�eLt������=�ȸ�>��b�8���!Lm�Ķb`�n4�W�qŧ6�𮭞�}r�8_G{��"���<�1i�	�rdΰ�/|�ټ�t����	�u�A������!4�}�)d�.(�0���@̱�݆��ҕ�yVE(��hF
s�巌!�w�9���;��+���R^ǁ��rR+ӵ��p���]g���O����I8�vl�/��a�h�!5����5�o��k�{7�"
J�RȻ��L|0�`�\g=������0�<�S|��8�e���=���Π���9�\�eKMd��R��~����S��,��<��݃wO�B�ZG[�$�����p�X�|?��—��(�F<���ɯ2B���lÄ�ހ��p!}�o�G}�/,�l��_����r�N��1#�/�#��Iv�٧�qPC�Q������\�^뙛3�I�ϩ��������������
d�N�������~���yԒ��-�,�����'u.�<&�MM6îo�X~��P���c�&��?y&���a�5�R�)G��6�}�.��%7���d��>J+��q#���_j�2��l	s��>���׸�s�U���i��?��#q����g����nb�<�l��T��S�M�ȯ�y����8`Z���gՂ�|��/��g��/lɮ84�]|R��g񡲸��s��z�3���C|�Տ�~j��~�Yw0w΃:;��\�ß�
�Ʌ�{(}
�;K�9L3�ɿ��^��A|�x������_�~
y�>����=����j�=e��a푾t05V�P���3��]�c(�Z'љ�4��O�m>�(��b��x��!�Z7�S��~�������dL�(>�S����[�fq��cP�\/����׍_����ٽ����Xr�Q������u�ӄ�:��|W�ӎ�������&�`�dr���-�z�/t�z�)����Y�)]��]�O����m�"yr@���P�*�p�m�4p޶N���ƴ�-���խG��js����z�4�=�y�^�=��h����c��r���o��r�^.�J99��?��4�$��r�G�v�,�Sg���t|�a�xZ�C+�'�2�K�^�E�w����!V1�Z�Qz�=�UJ;�)��B�.)V�H�\V��9,JǗ#�feƊ�B�s�����=�ȼ�s��o�����s�켮xI?k�K��<��\�C�V�,�C��|�9�o�E^�J��\����?zJs���8���7�+�9����/������?'�\��L��h
�T?�����~�Ύ���(�2mW�y�s�-l��4�*��u���^q�֪�w��,8�_-�r%Α8_~�
��-n����,~˘�ׅ�:'8�,���������pz�Z��<UC� �S��y�B�C}�J�K�Y�^�}����B]�¹ᯘ�d����YO���lV����/[�x��������=	l�"��H&g��>(�X'�k�JT��8S���g���Yy�V|��{p�ф���7�
���U�K�N�A_�=*��ʑ���bX�7����\c�u��Î�|��ޯ����O���U:�C���}��@U<�^�8�O��~m[ґ�����]'��p|je+]�S.ƾx���&���U��Y<åO�M�e:��{�z>ke��>�Cqh�:e>�{��S�c��8�#g�YL��Zw�c�'qF��)7~-S	������]�����V�Q]�m��Y��`Anֺ�!t���D�>��x��X|�E늡�q��� 7"�E֕���-�hxYs���8G�a���gl+�$�uW��Y:�m>�\��{*�fF���]�e�|
��Kgj��Tk���@+�Xy=z��Ne�?�>,�>����_��cD#�d����)c����.��]���>jer���|���>j�o�[@[�G��fYتUܦv�֍���w[�'�{�K����d�g��z���E���5�Iͤ�r��v�B-x�J>Sؽ�}vIy�p՗��WU7�v�5�x�Vx�3��RO���~��!Ϊ�,����A�?�U�z��&����w(V�бE.a��sgݜ�(��m���d�-�d�Uo�yĝ|��ħ���O��%�*[����}��7�F܂�,�-�5^�td�-�bx��֐�k�E2��;��'��j!��H�]�G�3&�Hι��u�|����w;X?��Ŭ�Tˢ�N�@�
‚�{'�楔���y�ѳ��^�3{$�w���3uvɼW|�g�؂�2yT~�p���w}��)ɐ�FϾy�s��qղ��g��V͍~�Q��]�X����>V�g?��^HG�����R�j���-��mw��o���ޫ����9R�봊-��1���Y|��;8f����i��pT��7���W�>A�lm��}�^С��_U����zU��Z�ܮ�jW�ȳ�f5�m�H�.�ʽU�VS��#�o@F�4�T[�Yz$��48��F�
���YO�^��1qv��-����#�lx>j����*LslY������:M�%}�bE��n�t.��ϸ��=1k�p�-J�f^�;�m�cA}q�3L�rX�U�!��cC�g�Z�?��Gi������="|
w#$��bU"�7
F���N��>��m�tt^D��Y�z��k=Ϲd�=�Oy��#{�Y��y}��w�s��Ǟ�]뒵���{���Pm&�p�_�!�Op����?d͹��?��[珺�թ-g횁1<ԌُJ|VU{]�������vy���� 5�Bf]�����pl�����z�)�_p��=�'�j�.��Y8���y����(t��N�P�t��C��8��u�iў���?|��lںS<��t���/u�yhո�o::�3�s���pX�“'��}�6��w����-;�O8�w&�z+��!|��'�d٥/�7�#{9k]�ϣ��1�S��wKU�-jS��|2��ܛ�.|a&Ι���
���޻�h���9�U�('�d�����+O�Öy�V���~xz�Q��GDS_T0�^:�ǚY*���FI���k�7����ѳM:������Ū�{n=��gos��(������Ϻ�߳���䁎�VL��Gq�/�0K���éS˒#��(�[�:Ys���f�;��x�-�Y8�eľ����sͪ�8���~+��u_�i9�V���\�p�Y:����>:�wn�0"�N�'�V��:�,��u�IL��&���S_�Z�S_r����⩚ۊ�!>�[0��ɮg�����!>��E�S7S`������*L���1����)���:���d���)�X{$�q�����=��ϟu�9�����5Ю]�G�J���QO_�t��l}�>q�Ì��������|�c8����3~�ۏ�&�goS���G���{6�O����v滒��sں��������������S˞�'�j���O͹9>��y�j���l�Xu?=�㑳�����c��}��Ǟ�{��p|����>�!�v�a�ȩ�Sֽ�xl�+kܜ�����U9s�n)o���w����I�ג�S��7洐�ƭ����3�w�G��.�'�w�X�wOe(��������k�W�>X���]~t4�7q���'꣤/E���W��<�ɟ�Z��6�G<�G��^�:����������j5�c{�pf_�>y?���B�o�֭sۥ��/��8�1�8�}��Cc]u��Y3���W���+}�h�:�E��={l��@��Rl�c�D��c��s�zB��:e��OqS/j�j�n�W��d��#�S_}t�вOZ/�O��H�=g1���p67գ��l�{�y�A_{m��
�O7q�HnY��W)�6lʦ<�,r9�C�/�iN�K���u�g�[��}Ӟ�n78�i������S��͵^z�۝�uYs�g�\Z�c�����`��Œ_�/�:\����-�S�_&�p+SvxF��>���?`Z���#>��~�y��!���"3�-g��d~�tz6qe}�'��߉���g���{��I��Y"�>}2������b���4g�������==��̗��_r޹�,٤߯�=S���yd��u�ė��e�u��𙳯̩�5�_g��[ק�W�Դ�p|��<�~�m}��z}���P
9^뽂�<w���u��s8�>O���օ���W���RB(��1ȵ�>Ĺ
��_�G��O��~�9��2>�p�5�A��f�|h�_��L��+���_�F���¯���r���lݞ�{/r^K�Y��PM��o+�X<�t�9)e��|F��:W�J����������9����r�|	�R��j�f�?�c���r���f�)Á�ҙ)��I��gy�����(�]��~�E�Z�7g���{��;#���xt�o������z4�RM1O���HLjМg�_�
)��9�br���nU���M<��0;�{�t$��
g���S4���zY�~�8�|O���g�t��2�SƖ⣻#Z�},��͚��W�n�*�Y�{ٜ�鹎Nf�xv�O�]������7�T��D��uS|��n૦W�8�~ںё,߉O�}��~O�e�������R_J�7��1�:�C�6���Җ�5��!��V�::�L8� ��c��aZ�u�X���>�gÖs}t����31�+��s�г&|��|6�	�V~�k�ڢ���}��a���+���+���A�L�p�2A�Vy��q�nv��(i+�a1�����Q�>�w���=���#�ϻ��Ӫɞ��\�p��OÅ_�Ky���`�y��l-��|���N��AĽl=O��x��Kߦ������Ƹ?l����o|^7:�j���C-�)�1�~�$g���_s}pw݊=������t��3ך��:�3ʹ��F[�5�0��T>_�i+�XYv9�Ԏ��_pst���Y�ϒ�#��>��U_Ӯ�n�ߗp�=�\��,��S'J�:�:�N1��E=l͕��sp؋o�|M�����m�<�f>�[LΡZӦ_����:א�b%�/�51Cg2|�~�z��u���_�ϲ��=.qCg��F�)�C'�‹MK��վ���G��/�}���:�<W�_�g�}H��ˑ�[��~���ze�'���G��g�~*n��z���N��VY1�䣟���:����W��|��.�٢��:q&V��M��#�����}����栽�Җ�*t��:E�eȧR�~���݉c�R-^���Nr��r��l�[S��]*���n�W}���M�?�����Gz	!���L�����\�n�7�e¡g���_s�(q�玨Y�MY��^���\1G�8���C:WL��Ⱥ�Lφ��~��j�[{���E�#1�S�|"��7Δ����l0�K�6��O����Q<m��:�D�vSM\�.�n˯S:��|��@�gM�O[�`]�lǰN�P�^|b��z_܈Ŀ��;��8�v��N�Kf����O�ڴ�U_�=���(6�}�^�j�cE�f7��R��~��,e��o�=��pF�O�<ڿ���k�)�����;���{?sa������^�r䗻�>ermQ�\8��C��
?��	�ܟ9R�V�\(}���	l���o�Â|�3�I�s�͙.1�|���s��3�6�����_�+	�|�:�~��	�"��d3t|�?!��r��A��:9`�8����/��@�4�F�?�K���V��+���G�_w5;�_��o�d�X�\`*��J�:�x�S������CY+of��V{}ɖp�)y�n��!�j8��[.����{�"�$�.|��O;�~j_姕�c�����z�.���g�W�8i/�����/�G�+�}�\�1��a�g#Ϝ�r�+($Xw�rS��f+�r��\�#��a�(��o�_��G�	8��#��~����C��p�.�1��a�R�f�M}�a���_!L��:�=�gLr���k������n||	�x�Y>2;ޓ�����3|��`FD��yqdL�~y1?�ֺW{��~7.���v�|�=��'�1�-��nS�3:�\���s"vڬS*���1B�?U#���7;eKum��]GzN�;���E��(™���ݼ�����f=T+G��p ����ow�ͻ&.�;��t�>��F,C�/�n>�ۚ�:g��\Ϲur>�sj٣
s���G_w�k�O�ٳ6�,�ϡ�u�|����+�9e��~կQT+39Ѽ�8�O�w�ZpJX�O3:��ɱU�֏��n�5��R�'�ϼ�p���ޙ��T��ʔL�p�>�$�q�`:g���c%q��Vб��c�c�B>���=CvKX��O�x�	Z�܆��[,�&}���^��ލV�b��]��0�e�+��s��O���x�}�P�'��2awW�L�|*�#�����F?�9���a]%��	a�N9��t����U߫�w�*ű��c��e�����8s����y��r}��&�Ĵ�8:/߾r��-x���&D��s�)k�:L�D�|S�z<:�c�kN�<#�g���)�լ&��E�|1u�]փ�m���?V;f�yRoL��xN���6�P�Y$���:���Іz-�a��p$/n%�&{Š6�2��q�t�Ϊ������a��t�0ה����]↿�#ߋ���̑�fZ��̵�G������kbŜo����yiPs�g�T8���ܜ��"`�r�:�NƧTC�ˊ���׌I-�>�좚>9d�����vTo���b���:^�ҏ��b�yT95�{��"~I?g��E9�2��<
cw��<���uΈ�O�b���+We͝1�6eP�_׺�\�z��X�#�w�C:Z7�
�댵j}�5z�bŹ��B�]��;m�ԟ9X|�&�q�ȗ�t�d�/�+���a�P����������%�G�Fm3�7=��UOۈ����E��d��U��L9�=��<[��' λV���&N�ᩯ�ۘBS�#���?8�_���l�F�!��g�4���#�b�g:�˪�
���g��!_�;R��·N3��'��b.[�z�}�*��_�A>2>�o�7�X3�|cj)>������~@ʡކ8���
����_T�VT��
y��^�}���޵C�}7�.��w��.�(��&����?�?,��uUv�9�+՘�J�6rI��S�={צy֩�r�����S:�ݧ��u�S{՚w��\�BG|�9�M�֗ݢ��K��?'��1݄�����sV()'�m8<�\_%\$���4_3�Ǫ9�.Y��S�L���������9t>;F���w-ebX�zQܖ����8W;f|���#��/�Ϛ�*ng����mk��U��_��Z�7�9g�n�A�_��L�r���V��	�H�^������s��K���I���\h��x��~Ǩ��[����4!���A�>���W�ϻo�#�d�ܓ�w�>�'��+���dK9~�2��K�D|�;υxr!ߐ3�=D�:t�B����W0��N���8ē��#q���~��w=����
>o��w�������R�z�����������O��ʋ�G礐縷S>-�s��gݩ:�ǝ�+n���OK-�g��X�|��b��F)�Ժj}�
�R](�[L�����yD��x雸�j�飾���5e�U��l>�:F�FT]�e����oͧ׼\��8����,Yq���|�ϳ��g혳���8w��]��~���rm����e+9k������۔qî�1�>׬�t0l��a�|�����A)����w�[ޓ��r�U�v���.�QZ}:W+���"�}�0������_b�&|j}�0�&q6�D|¼N�ڽL���Ur�
G����䯉��P�`�%�OٲQ5[q�nV�񛋳���e��տ�3~�Cu���L��������Ԇf��D�:a>�b��1�^�ȯ��l�������+�N��3m
��S硱����_�d�ꙋϵg��l�3�L��N���0�~	���>^�h�q�{<.ͤ��;w��N�5���i������<}?;����~ΧO����ď��OK~
�����4�U���<5�����-����3'%fh�O�ѼS<W�}��]�c�}���s�*�[<���k��6�g��Za,������d+�C�+>1>f̓��Ls��S�!�ۖfꊛ��;u���sƿ�_50�s�(���O[����b�z�cH��}��y�y>ՃI�ϱ�C�DrV���k��Lw��O��\�xƕ<�k#�U���gx�f�i��X�\)��a�'�ΰ]�3��>�o��?{3�%���
ω,��~���{�~�S�3H9��(�e�a{	���|O,g���K�%�̻#wa��9�8���<����AV�=m�y�����}��Oq�uy�P͹C��U˳Žl�ﲵj�Q�0�J�>�;>�~F��ٯ���NW�?�����t�|�h�f��sh9��g�2\3�ܳ�G��M0
[Q�)Q����c�P��_��׳��\γ�=L�����v���Xst�'OO�Ӗ�P-+�%�C�%7�Q|R?F(���a~��]�8ܑ}�)��@���E�b�̺����c�[�y&NbJ_=�_�k)g1��Z7��~7����"�	�ş8�s���`���83{�+�vXrZ�2Џ2��?Q�:�������ZJ�~��>?\�l���+�f"�`vnV���mJ�<3~�D�z1���a��@��>�S=��R�}|M/�Bg��\�~hF�sq�Yr)�c����͐?���oy�D��{�������ȯ�n��p�щi�E/�����#�+t*����5eӽ
�|�-0_�י&��������b�0�'�GO�>�ȋg�wj�2c��g��~�
�G���Q�Vb�oq.a�9�>�G8�z�y��p�eי�G:��}��S@�.��q�<�q��ԗ��8Cw�#�&�"93#/:{�~-�9�Y��t��֞*nUw-s.`�q#ך�����寽q�1��y4��>�ܯ<[X��1q苄���\ݦ9Δ_c�J|g��o�u���x8���3�Ͱ5�9G���}��Q��8���/�� ��z�S��G�N,��.��{\����SO��g��9������AN��X�g}�,>�l�>غ�+�3{-���dO��L�S�G��>zޏ�ᏼWآ�<��u��v��Y��C�9��>�z��>q�^3�f�(��㈬�U/Z	�iK>��@ǹ��s�n5��>}��j��i�ML��5̐��V=Sg8��li�ɇu����#��x��iV��p����e
΅�ߏ�f�C��_�~x)���R9j��s¿un�7�O�f]n<����b��x��0���Yj�2�z1�m�W�O5k(#�����X_�78ҧǃ<mE8�B���C3�3X'��'j�����dQ��#��;v�y�~�Q�XDm_^��U�O)�Q�e��Q���gv[�b�>
?�#���ۯ{���Bz�ǣ�V��P��=�->���\��YjIY�i�\�K�:.���21}�xC�M���8��r��	����Bib�R���ُ��e��D���>�^+r�8O=�>��&z�~��7���ج������~�u�c��9 &�S�K�ƩSKY��j�s��:{�f|7�u�8�F_yz0���Ŀ=��YjG�Y��Y�<��"�7�{��c�Z:غ��ӯkj٣bw����6�'q�
�|����)����r!n���߿o�~Hg��Y���"pC[�;&�-g�mc��ʣo�79	|��\���G1���{���og��%c��>�gd5"1D���هr��e�'>�p�RC>�(���3�U;�C@�
�;|䗸¹5�O�|�	�BO�:7�^�x�"9<ɯ�V��w�%>⩽��	Ŷ,���W������o�"�ʳ6��9���ʐ��|S}�}QO(����S7k�,��-�oD<�;x��#���B�W�o��^Q�w�]�
��g�(>>�黡�^�Ir̀r��K�e��9��-�G��>P�.����3���b��-�j�J
c��Ր����n�:�6u&���{�����
��!�0��w����3l<V�-L={�k��}Q����J�Oq�����;qX�+q�—-�NK_f|Љ�˫8L�'�Z�z�M�I��#��Ulʬ�
�O��W�񪛏�<����=�_Æ�B��U+��nƧ��a�Z���\�
n�ĩf���H9�c�q�8C�>9Ά�ž��玂8s,�[D���_t���W�6F�n�a�:�t.�o�l��80���ܯ�O���z�5(���6in������X���1�ު�O��se�2������NL�yo���k��6\�Y�n��;B5��9�$�s35���f����]�d�	�S^�J��1�M�#���L���;O�@'r}�������)CwN�ͤ~���4����P���A|X姡ڽjk�&F_<�g�G�T/_\uՁ݀ϭ8�wm=A�a&��jݜ���?�\T�����7�7r��odq��+�Y��+>qoY8��:k�\W*��晵��s�]�S8���YoeM�ZV��O�ʳ�{&ή5��U�eW��p|?X���];�6��I=��K��:��J�|�����̳��
���w�G�Y�
6�<-� (O�:�?��^�?��^A�ޜۮ\x����Q߾��Eg+�w/z��ϊsg�]{�ygoM��p�»�q(������C���(_ʮ����߹n!>���!s<��xڊ!�S�=�y�,T��_8�'���up�O]џ��)ZG^�y?q�����&���T[�㪧��e��u���Y_��f]v�=��0����<�.L�Q�9�L�p(��(�Q&��Q�3���s}�k�]g�9k�V,�x���o�3���"�YCd��
���GqG'{�Z_ۨ�w�^��8�����s�q�ޜ���JܝZ��s��8��ıZ�_[����ZW.7xʯթ�'��9�H���7|�t�F�FyM�wM?��^�Yè�]�e�>[���_��6��3���R$u|�#�8U�3�W��%�c�+���޾��
�e;纅RϦ�p�-/=��m��=�u}g��Z�C�"�r9:�[1��S�#ޘ+���MV.�+��5R��Di�௃�=g~�<Ɛ�����\v}S�]���j˪S�v��y6�.�1�gd���]C+�h�Y�R;.�>5���/�?�u7J��ά���{���砞�Yle���[�ׅ�K�|�'�>y&g��Y�w/rZ��1�v�UXϜ�-��o�	qV<-��;k��_”_��G�=�Cr���Xq����,����=m�[ʋ�-Wn3�Qt��ͺ�iPw�M|����a�d��4S�1�G��A>���Y�c�^�+����3k�ȷ�����ͪG��
d���y��%����>�i�Ї�о��NH'&������w1e�gޒ��_|�V.\��&����<
uy�-��s	|0�x�9�'�!������O��=�^�P�ّ�'V�s_�݆�5�S��{���=����g��f�`=���}�ێ��b�'�m%���꽹���~C>[T��ζz���L�z.�us�
��d#>5��>4G�������c�,�I���n����|4��v\�����b�-锬gR~ׯ���5�,>6"���,|>�72�C#��^W�k�y�@��S?�t��̻� ͹��#�Ϊ�K�V�!n��Σb>y��^g�M��Vl��گ*|�R�-��b�Sz��,g�}{�Y��й�wT��Nݿ�
<��	j��)7�!��4���V��A��Al>e�٘����`�: �-��U�̻l9�����)G6����ס�����ۆ�as=����|�p�Cj^�v8yW�4w_�t����s���m0�v=+>����~�r��ƴ�]G�����uA�>6����ΔjY[u6�̰��]�� ��l�}|/�i���{O|�8k�OqN99\c����H����İ��s����H9�t�	ʈ��>e��lc�g]N�K!�fG֦O|��?g�f�.�=�w�N�r�j��pS��đc��
վ����o�|<�{�ʸ�X|…y��������yFR��k#�y#�C��
pٍs��p�Uj�u#K?�>#���<�C�`a^ڋ��g��9��Lgՙc(>5�nX�0�ݯ�������K�8�<�s� ���q(E��f�{!�QV�˓�7�4~�����g��b��z�tV�1�^���C3�^4���0��g�"r�z�⣘�J�@Q���[�a�� |�
�gN���]}����g�n�B:��a/Z-E[a�'��'�h��X�n��7O5s�Ź�?<�����3B�b�s�3�R�N~j�KN�2�X���Jsn�tUkPl�Y�V�,R*C}o�D���a�[�5�hQ��:m�so��<��1{$�⬗k���W'��G�la�׋bX�s7�݅����3*�u�T|oŪ�={D�^��i�콧��+V��YU)ѳ��{�M�?T4˯$Ϝ�fp�s�ܬ�R����u�5����J�(娊��@��8E��6�s�{ee�[q����[w�[Q���K(�}���d��pJg�ʘ3��uμ�8��k��\ζ����Uky]ߚ�>k�Sv�/��_��8��3�/��?�)o�iwҽ�7�Z:S�Y��OC?�V��%�Z�̹$�y������o}�AgZK9ק�䳸)�5u���+�{���W�p\�r<���<1~��P�s�Jng:�8��'���܉�=✱rI�����}o�n�'}'��|�B^�k���;�rI-u�����Y:×���/0G�;&ڋe���p�G_�ɒgx���;:u��P��i�&�=�.|8���&}N�U�ϥ8k6�9�־��_�_�\�0s]���Y��T�?���������Snߺ؂����{��y�⃎p�|��8�C�+���=�߫�sM��>��V�}���3'V�]�\�����K��Y��K��S:ߊ��p'{�ή���,ҟr��&N�ϑ�ytX��i��W<-�ߑ�1��a�E�[�A|��Q�/���Z�s=��98[����o��,���w��MY���W�}ϞY��K��Ɗ�܍�zKϧB����y�r�y�9�t�q�¡_
f�2��|V<���.�����y��@<��W����g�p�����c5Tד|Ҽi�O��L�e�|9$�R-�X�׆�p8�j2��/�;��#~�)�S/W���N��I���H�u��j.�U��/���̵�����x���_����![�$Vz��զ~O����L���7��:�5��d�]�k������y�h��_���u�r�{��q쟻�{��}蛮e�^/5���x�R�;�ܸ��/�&�mK��H'�dUpB.������17�;1Z���+�ץ3?uxv�ֻ����������?�`��X��Д7g$���%�O�|�l�흫q����8�AgƓ�L[�q��<����'���2�q�/��<��r��땾�澠O�6��R��ٞ87~�	�1��qh�Sɽ�q����c�8G�N����ǞJ�UU��Mj[�:��7�[��Ί9��﵌�|$�"����gaއ�cׅ�N����[8#����u��^2&z7��a�|���Tr�wW�8'O}���8�Ό3:�����;����[�Z�6��7��o�a=c�*����x6޷@�������D�N��I�~�7yQ�C�g��ۯ|�u4#���/xN[R��m �[�Ls9�8���붵�K|���O�NƤK'��ʣ�O����ρ����1�_�m����[������Fб)�8L�{��'�܊�'9L�<8mD`��O��$��/���-��Y|�3|�<37��x�F����g�k/�%�V��ZG�n]qә��Λ�F�S*��U��1��k[�N���.5�ݐL����2�P�ޏ�L�%��Zq�)��C��;��č��;z�Ǭ->��|�)+c==;1�u��r>:�`��_�T�sQ.��Vʩ3�������ϝ�r=s�Z�Otؿ���K�"<ׅsԢ��^��<�U�j=qv����=UGJ���k�Ϻ8'�����'��ɼ����g��}�u!�I�K�N���q��\�ur�� �_�i�pN��܉<����g�/���c_v��ze���]8�Ȗ��o���y��/��}��7w ��?b���]���_���} gŕ�%�.̲�yj��g>��_����‰�X�-�E�-�}c���D��W���_w��h���onڣ.�.�'/��K�^�?9����/>��P��R]�糓>�ε;�f|r�.W
�p¯ā�X��7���K��Dz~;A!V��ᬆ�b�K����,��_�Cm؅��mՒ��_���69��d7��)��6����:8�_qV
]�r�)��r��}s����'����:4n8�?��{���^?u���􊁏����4��rsm`&%�ի>�>Ϊ�|����:8����Ȭ��F�J�Cg�uΣ �p�-V���8e�uޚ�`f��3��\���ic?�A6�ǦX�\�)�S��O�,�9{'3��~��Y��լ�'>8!̪�[�Zי29��o�L̖��B�s��k� �Z�q�u��Y����Y㺫'��zS==聗���˩��&L��୯�z ���j�Z�ξܧLL�!��9�g�����Io��w|&Oa�k}H�YIy��//�pǐ{|���k.[�Ǿ'l/_�|M�V�k���#|��%�ӮM��,7A��^�IEND�B`�PK
!<�T�R��9chrome/browser/content/browser/defaultthemes/5.header.png�PNG


IHDR��][��IDATx�ݱ�m7�{��oo��ة3ø� V��f]>���� 3��PMd�=?�����'2�����T@9�r*�����S(�Ȝ;S
�����x��
(�PN���Q�r*��
��r*��]��v���T����T�SXN�SXN�9w�9ww���C��S�SXNʩ> 2����~�Ƞ ���\��`9��T@9�r*�T@9�r*���'��2s��&{�.�j)�ʩ�S������Q@����O��������PMv�\դ�Q@t�~�\�˩,��S������Tʩ"����
��
@9@��_�S(���~rg��&�{.�j���'�X�'n����'2����~,�~`9@<<�����dΝPM��]�XN,�PN!���r*`9��T@9@��Od(�PN� n���/�SD@?���
@9x��]�~�S�PN��C@?q������r*����
�r*���sT��s@5����SXN,�(!�~�SPN��
@9PN��
 2��������S(�PN�S(�Ȝ;���������~�!�����
�r*�ʩ(����'n���]�~�v�����r*`9��T������������G,��SXN`9��
�r*���
��
@9@dXN`9PN��
@9PN��
�S(�PN�S(�����+%xIEND�B`�PK
!<*���7chrome/browser/content/browser/defaultthemes/5.icon.jpg���JFIF��C��  ��	
��!ST�������?�~5uTd�j��H��Q�����%Bs8\
��8\
��8\
��8\
���g7q�I���3�&swϤ���s>�!��u��F�*�mt��ʧ[]$n���K��PK
!<�Ě:chrome/browser/content/browser/defaultthemes/5.preview.jpg���JFIF��C��C���
��6
!1AQaq��"RS��#2bc�ђ��CB���?�G�G0�X�@�1�u�+J�{�;&�s$�T7x����b@�*T?l�'�����+J����U�۔�g���K��6��iQxCdծc@�J��v�W�H@J��
��qR11eiP�0�*��r���ҕ�)c9���t�*!�`욵�`�)P�a����
�h�P�A���n*F&"l�*�EWnR1�ZR��E,g [��:Ε�D=��V��e*�<\]�^1 M*�6X��H��M��B��h��m�F3�JTs���sYҴ���!��j�1� L�Cq����@+�$	��C��q�������_�pV6!�H�ql��NӁ��B����b�������Jtu[�mu��pS����
V���\�S���8��34,�2S���o��f�<��(��@���S��Z�{�k�LK���j�F�*���ٙ�dI��C��~
�4ة�g�G��,��B���]bb\��/�CU�6�W.��-(���"L�����n٦�O�?�?�� !`���������G9����ʹp�G9hqGvfhYd�GP� ߃v�6*x?�Q�n�$�GP�����X��:9�P�l��U˅:�f��X�C��2�k?���
/G�^�$�a�!S��v�`O"���mXp�Jd�b�\�ڝ�L��G�7�%�b�b��
��Y��?�*q}n�L	�DV1�a��nÉL��Cߋ���S�b)���������d�lU�Cԃ� ���� �����N/�ک�<H��:�1�`-�8q)���{�r_jvl@e2V1P�>Xl�M���z�s�6��dV0����U0'�X�Q�6��g%2V1~.B��N͈�J�#����
��V1Rx��,�J��8��j��"+�0�Հ��ĦJ�!���Q}�ٱ��X�}Cpp�a�]6<�Y��3s궮�g 
�7;��	$��é��۾�qVo����-�,huf��q�L3�Ha6J�:��O*���g 
�7;��	$��é��۾�qVo����-�,huf��q�L3�Ha6J�:��O*���g 
�7;��	$��é��۾�qVo����-�,huf��q�L3�Ha6J�:��O*���g 
�7;��	$��é��۾�qVo����-�,huf��q�L3�Ha6J�:��O*���g 
�7;��	$��é��۾�qVo����-�,hugL���a�fl�泞9>�d�Vs���V�3����2��.)VsϹ{|l�g=L�d/%��9>�d�Vs���V�3����2��.)VsϹ{|l�g=L�d/%��9>�d�Vs���V�3����2��.)VsϹ{|l�g=L�d/%��9>�d�Vs���V�3����2��.)VsϹ{|l�g=L�d/%��9>�d�Vs���V�3����2��.)VsϹ{|l�g#���Sa�# �(9������)����Z�ktB�<$��q�x3����$���0n7�����n�a��6��YC|A�VK�c�Zd���ּZ���	+/�{^�s6	+(�ۍ�w�$�����Xch͢�VP�`���֙+(�5��D+s�J�׃:m͂J�:���q]�I+(�f��3h���7�d�f<5�J�:�kŭ�
��ǵ�ΛG3`�����-��W|RJ�:��ņ6��+%e
�Y/�
i����Z�ktB�<$��q�x3����$���0n7�����j�
C:�y`���5Q�A��
�Հ�}n�RJ�z��u�l$�V�=������7�^YV,���#��2b����ݤ.6����7���P�I@��{��#�+�o���,Y+�G|d�XV�=��H\mI+�o�֡���XP��s�G�V��9yeXX�VԎ:�Ɋ���{�v��ڒV��?��Ca%����p����r�ʰ�d��u�`XP���!q�$���WZ��J`=C�M��!XQu�Wߌ��gs��T����;�t��S��{���"�Jbw6O{��t`ʪfw8﯍�	%1;��~�y6ILN��C�k�Y)�����yS%1;�'��p:0eU3;�w�������n�B<�$�'so��5Ĭ���q��<�����͓��82����;��lILN�7_�M�S�����VJbw8�{yDT�LN���q��UL����$�'sC�Џ&�)����wMq+%1;�w���"*d�'sd���F��'s��3`׵�^�}��8�x��n��-��^^#�OI�s�-����2z�߈�[|��
'�=��
�H/{ߔ��I�x�1(���K�	_Rz�ǿ֞-����OI�s�-����2z�߈�[|��
'�=��
�H/{ߔ��I�x�1(���K�	_Rz�ǿ֞-����OI�s�-����2z�߈�[|��
'�=��
�H/{ߔ��I�x�1(���K�	_Rz�ǿ֞-����OI�s�-����2z�߈�[|��
'�=��
�H/{ߔ��I�x�1(���K�	_Rz�ǿ֞-����OI�s�-����2z�߈�[|��
'�=��
�H/{ߔ��I�x�1(���K�	��PK
!<���)):chrome/browser/content/browser/defaultthemes/dark.icon.svg<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
  <path fill="#0c0c0d" d="M2 2h14v13H2z"/>
  <path fill="#323234" d="M16 2v13H2v15h28V2H16z"/>
  <rect x="1" y="1" width="30" height="30" rx="2" ry="2" fill="none" stroke="#08091a" stroke-opacity=".35" stroke-width="2"/>
  <circle cx="9.5" cy="22.5" r="6" fill="#474749" stroke="#08091a"/>
  <path d="M12.5 22H7.707l2.146-2.146a.5.5 0 0 0-.707-.707l-3 3a.5.5 0 0 0 0 .708l3 3a.5.5 0 1 0 .707-.707L7.707 23H12.5a.5.5 0 0 0 0-1z" fill="#f9f9fa" fill-opacity=".8"/>
  <path d="M20.5 20h4a.5.5 0 0 0 0-1h-4a.5.5 0 0 0 0 1zm4 2h-4a.5.5 0 0 0 0 1h4a.5.5 0 0 0 0-1zm0 3h-4a.5.5 0 0 0 0 1h4a.5.5 0 0 0 0-1z" fill="#f9f9fa" fill-opacity=".8"/>
  <path fill="#0a84ff" d="M16 2h14v1H16z"/>
  <path d="M26.354 8.646l-3.5-3.5a.5.5 0 0 0-.707 0l-3.5 3.5a.5.5 0 0 0 .707.707L20 8.707V11.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5V8.707l.646.646a.5.5 0 1 0 .707-.707zM24 11h-1V9h-1v2h-1V7.707l1.5-1.5 1.5 1.5z" fill="#f9f9fa" fill-opacity=".8"/>
  <path fill="#08091a" d="M15 2v12H2v1h14V2h-1z"/>
</svg>PK
!<.5��99;chrome/browser/content/browser/defaultthemes/light.icon.svg<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
  <path fill="#e1e1e5" d="M2 2h14v13H2z"/>
  <path fill="#f9f9fa" d="M16 2v13H2v15h28V2H16z"/>
  <rect x="1" y="1" width="30" height="30" rx="2" ry="2" fill="none" stroke="#08091a" stroke-opacity=".35" stroke-width="2"/>
  <circle cx="9.5" cy="22.5" r="6" fill="#fff" stroke="#adadb3"/>
  <path d="M12.5 22H7.707l2.146-2.146a.5.5 0 0 0-.707-.707l-3 3a.5.5 0 0 0 0 .708l3 3a.5.5 0 1 0 .707-.707L7.707 23H12.5a.5.5 0 0 0 0-1z" fill="#0c0c0d" fill-opacity=".8"/>
  <path d="M20.5 20h4a.5.5 0 0 0 0-1h-4a.5.5 0 0 0 0 1zm4 2h-4a.5.5 0 0 0 0 1h4a.5.5 0 0 0 0-1zm0 3h-4a.5.5 0 0 0 0 1h4a.5.5 0 0 0 0-1z" fill="#0c0c0d" fill-opacity=".8"/>
  <path fill="#0a84ff" d="M16 2h14v1H16z"/>
  <path d="M26.354 8.646l-3.5-3.5a.5.5 0 0 0-.707 0l-3.5 3.5a.5.5 0 0 0 .707.707L20 8.707V11.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5V8.707l.646.646a.5.5 0 1 0 .707-.707zM24 11h-1V9h-1v2h-1V7.707l1.5-1.5 1.5 1.5z" fill="#0c0c0d" fill-opacity=".8"/>
  <path fill="#08091a" fill-opacity=".25" d="M15 2v12H2v1h14V2h-1z"/>
</svg>PK
!<�ir|�e�eCchrome/browser/content/browser/downloads/allDownloadsViewOverlay.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 mozilla/browser-window */

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");

ChromeUtils.defineModuleGetter(this, "Downloads",
                               "resource://gre/modules/Downloads.jsm");
ChromeUtils.defineModuleGetter(this, "DownloadsCommon",
                               "resource:///modules/DownloadsCommon.jsm");
ChromeUtils.defineModuleGetter(this, "DownloadsViewUI",
                               "resource:///modules/DownloadsViewUI.jsm");
ChromeUtils.defineModuleGetter(this, "FileUtils",
                               "resource://gre/modules/FileUtils.jsm");
ChromeUtils.defineModuleGetter(this, "NetUtil",
                               "resource://gre/modules/NetUtil.jsm");
ChromeUtils.defineModuleGetter(this, "OS",
                               "resource://gre/modules/osfile.jsm");
ChromeUtils.defineModuleGetter(this, "RecentWindow",
                               "resource:///modules/RecentWindow.jsm");
ChromeUtils.defineModuleGetter(this, "Services",
                               "resource://gre/modules/Services.jsm");

/**
 * A download element shell is responsible for handling the commands and the
 * displayed data for a single download view element.
 *
 * The shell may contain a session download, a history download, or both.  When
 * both a history and a session download are present, the session download gets
 * priority and its information is displayed.
 *
 * On construction, a new richlistitem is created, and can be accessed through
 * the |element| getter. The shell doesn't insert the item in a richlistbox, the
 * caller must do it and remove the element when it's no longer needed.
 *
 * The caller is also responsible for forwarding status notifications, calling
 * the onChanged method.
 *
 * @param download
 *        The Download object from the DownloadHistoryList.
 */
function HistoryDownloadElementShell(download) {
  this._download = download;

  this.element = document.createElement("richlistitem");
  this.element._shell = this;

  this.element.classList.add("download");
  this.element.classList.add("download-state");
}

HistoryDownloadElementShell.prototype = {
  __proto__: DownloadsViewUI.DownloadElementShell.prototype,

  /**
   * Manages the "active" state of the shell.  By default all the shells are
   * inactive, thus their UI is not updated.  They must be activated when
   * entering the visible area.
   */
  ensureActive() {
    if (!this._active) {
      this._active = true;
      this.element.setAttribute("active", true);
      this.onChanged();
    }
  },
  get active() {
    return !!this._active;
  },

  /**
   * Overrides the base getter to return the Download or HistoryDownload object
   * for displaying information and executing commands in the user interface.
   */
  get download() {
    return this._download;
  },

  onStateChanged() {
    // Since the state changed, we may need to check the target file again.
    this._targetFileChecked = false;

    this._updateState();

    if (this.element.selected) {
      goUpdateDownloadCommands();
    } else {
      // If a state change occurs in an item that is not currently selected,
      // this is the only command that may be affected.
      goUpdateCommand("downloadsCmd_clearDownloads");
    }
  },

  onChanged() {
    // There is nothing to do if the item has always been invisible.
    if (!this.active) {
      return;
    }

    let newState = DownloadsCommon.stateOfDownload(this.download);
    if (this._downloadState !== newState) {
      this._downloadState = newState;
      this.onStateChanged();
    }

    // This cannot be placed within onStateChanged because
    // when a download goes from hasBlockedData to !hasBlockedData
    // it will still remain in the same state.
    this.element.classList.toggle("temporary-block",
                                  !!this.download.hasBlockedData);
    this._updateProgress();
  },
  _downloadState: null,

  isCommandEnabled(aCommand) {
    // The only valid command for inactive elements is cmd_delete.
    if (!this.active && aCommand != "cmd_delete") {
      return false;
    }
    return DownloadsViewUI.DownloadElementShell.prototype
                          .isCommandEnabled.call(this, aCommand);
  },

  downloadsCmd_unblock() {
    this.confirmUnblock(window, "unblock");
  },

  downloadsCmd_chooseUnblock() {
    this.confirmUnblock(window, "chooseUnblock");
  },

  downloadsCmd_chooseOpen() {
    this.confirmUnblock(window, "chooseOpen");
  },

  // Returns whether or not the download handled by this shell should
  // show up in the search results for the given term.  Both the display
  // name for the download and the url are searched.
  matchesSearchTerm(aTerm) {
    if (!aTerm) {
      return true;
    }
    aTerm = aTerm.toLowerCase();
    return this.displayName.toLowerCase().includes(aTerm) ||
           this.download.source.url.toLowerCase().includes(aTerm);
  },

  // Handles return keypress on the element (the keypress listener is
  // set in the DownloadsPlacesView object).
  doDefaultCommand() {
    let command = this.currentDefaultCommandName;
    if (command && this.isCommandEnabled(command)) {
      this.doCommand(command);
    }
  },

  /**
   * This method is called by the outer download view, after the controller
   * commands have already been updated. In case we did not check for the
   * existence of the target file already, we can do it now and then update
   * the commands as needed.
   */
  onSelect() {
    if (!this.active) {
      return;
    }

    // If this is a history download for which no target file information is
    // available, we cannot retrieve information about the target file.
    if (!this.download.target.path) {
      return;
    }

    // Start checking for existence.  This may be done twice if onSelect is
    // called again before the information is collected.
    if (!this._targetFileChecked) {
      this.download.refresh().catch(Cu.reportError).then(() => {
        // Do not try to check for existence again even if this failed.
        this._targetFileChecked = true;
      });
    }
  },
};

/**
 * Relays commands from the download.xml binding to the selected items.
 */
const DownloadsView = {
  onDownloadCommand(event, command) {
    goDoCommand(command);
  },

  onDownloadClick() {},
};

/**
 * A Downloads Places View is a places view designed to show a places query
 * for history downloads alongside the session downloads.
 *
 * As we don't use the places controller, some methods implemented by other
 * places views are not implemented by this view.
 *
 * A richlistitem in this view can represent either a past download or a session
 * download, or both. Session downloads are shown first in the view, and as long
 * as they exist they "collapses" their history "counterpart" (So we don't show two
 * items for every download).
 */
function DownloadsPlacesView(aRichListBox, aActive = true) {
  this._richlistbox = aRichListBox;
  this._richlistbox._placesView = this;
  window.controllers.insertControllerAt(0, this);

  // Map downloads to their element shells.
  this._viewItemsForDownloads = new WeakMap();

  this._searchTerm = "";

  this._active = aActive;

  // Register as a downloads view. The places data will be initialized by
  // the places setter.
  this._initiallySelectedElement = null;
  this._downloadsData = DownloadsCommon.getData(window.opener || window, true);
  this._downloadsData.addView(this);

  // Get the Download button out of the attention state since we're about to
  // view all downloads.
  DownloadsCommon.getIndicatorData(window).attention = DownloadsCommon.ATTENTION_NONE;

  // Make sure to unregister the view if the window is closed.
  window.addEventListener("unload", () => {
    window.controllers.removeController(this);
    this._downloadsData.removeView(this);
    this.result = null;
  }, true);
  // Resizing the window may change items visibility.
  window.addEventListener("resize", () => {
    this._ensureVisibleElementsAreActive();
  }, true);
}

DownloadsPlacesView.prototype = {
  __proto__: DownloadsViewUI.BaseView.prototype,

  get associatedElement() {
    return this._richlistbox;
  },

  get active() {
    return this._active;
  },
  set active(val) {
    this._active = val;
    if (this._active)
      this._ensureVisibleElementsAreActive();
    return this._active;
  },

  _ensureVisibleElementsAreActive() {
    if (!this.active || this._ensureVisibleTimer ||
        !this._richlistbox.firstChild) {
      return;
    }

    this._ensureVisibleTimer = setTimeout(() => {
      delete this._ensureVisibleTimer;
      if (!this._richlistbox.firstChild) {
        return;
      }

      let rlbRect = this._richlistbox.getBoundingClientRect();
      let winUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)
                           .getInterface(Ci.nsIDOMWindowUtils);
      let nodes = winUtils.nodesFromRect(rlbRect.left, rlbRect.top,
                                         0, rlbRect.width, rlbRect.height, 0,
                                         true, false);
      // nodesFromRect returns nodes in z-index order, and for the same z-index
      // sorts them in inverted DOM order, thus starting from the one that would
      // be on top.
      let firstVisibleNode, lastVisibleNode;
      for (let node of nodes) {
        if (node.localName === "richlistitem" && node._shell) {
          node._shell.ensureActive();
          // The first visible node is the last match.
          firstVisibleNode = node;
          // While the last visible node is the first match.
          if (!lastVisibleNode) {
            lastVisibleNode = node;
          }
        }
      }

      // Also activate the first invisible nodes in both boundaries (that is,
      // above and below the visible area) to ensure proper keyboard navigation
      // in both directions.
      let nodeBelowVisibleArea = lastVisibleNode && lastVisibleNode.nextSibling;
      if (nodeBelowVisibleArea && nodeBelowVisibleArea._shell) {
        nodeBelowVisibleArea._shell.ensureActive();
      }

      let nodeAboveVisibleArea = firstVisibleNode &&
                                 firstVisibleNode.previousSibling;
      if (nodeAboveVisibleArea && nodeAboveVisibleArea._shell) {
        nodeAboveVisibleArea._shell.ensureActive();
      }
    }, 10);
  },

  _place: "",
  get place() {
    return this._place;
  },
  set place(val) {
    if (this._place == val) {
      // XXXmano: places.js relies on this behavior (see Bug 822203).
      this.searchTerm = "";
    } else {
      this._place = val;
    }
  },

  get selectedNodes() {
      return Array.filter(this._richlistbox.selectedItems,
                          element => element._shell.download.placesNode);
  },

  get selectedNode() {
    let selectedNodes = this.selectedNodes;
    return selectedNodes.length == 1 ? selectedNodes[0] : null;
  },

  get hasSelection() {
    return this.selectedNodes.length > 0;
  },

  get controller() {
    return this._richlistbox.controller;
  },

  get searchTerm() {
    return this._searchTerm;
  },
  set searchTerm(aValue) {
    if (this._searchTerm != aValue) {
      for (let element of this._richlistbox.childNodes) {
        element.hidden = !element._shell.matchesSearchTerm(aValue);
      }
      this._ensureVisibleElementsAreActive();
    }
    return this._searchTerm = aValue;
  },

  /**
   * When the view loads, we want to select the first item.
   * However, because session downloads, for which the data is loaded
   * asynchronously, always come first in the list, and because the list
   * may (or may not) already contain history downloads at that point, it
   * turns out that by the time we can select the first item, the user may
   * have already started using the view.
   * To make things even more complicated, in other cases, the places data
   * may be loaded after the session downloads data.  Thus we cannot rely on
   * the order in which the data comes in.
   * We work around this by attempting to select the first element twice,
   * once after the places data is loaded and once when the session downloads
   * data is done loading.  However, if the selection has changed in-between,
   * we assume the user has already started using the view and give up.
   */
  _ensureInitialSelection() {
    // Either they're both null, or the selection has not changed in between.
    if (this._richlistbox.selectedItem == this._initiallySelectedElement) {
      let firstDownloadElement = this._richlistbox.firstChild;
      if (firstDownloadElement != this._initiallySelectedElement) {
        // We may be called before _ensureVisibleElementsAreActive,
        // or before the download binding is attached. Therefore, ensure the
        // first item is activated, and pass the item to the richlistbox
        // setters only at a point we know for sure the binding is attached.
        firstDownloadElement._shell.ensureActive();
        Services.tm.dispatchToMainThread(() => {
          this._richlistbox.selectedItem = firstDownloadElement;
          this._richlistbox.currentItem = firstDownloadElement;
          this._initiallySelectedElement = firstDownloadElement;
        });
      }
    }
  },

  /**
   * DocumentFragment object that contains all the new elements added during a
   * batch operation, or null if no batch is in progress.
   *
   * Since newest downloads are displayed at the top, elements are normally
   * prepended to the fragment, and then the fragment is prepended to the list.
   */
  batchFragment: null,

  onDownloadBatchStarting() {
    this.batchFragment = document.createDocumentFragment();

    this.oldSuppressOnSelect = this._richlistbox.suppressOnSelect;
    this._richlistbox.suppressOnSelect = true;
  },

  onDownloadBatchEnded() {
    this._richlistbox.suppressOnSelect = this.oldSuppressOnSelect;
    delete this.oldSuppressOnSelect;

    if (this.batchFragment.childElementCount) {
      this._prependBatchFragment();
    }
    this.batchFragment = null;

    this._ensureInitialSelection();
    this._ensureVisibleElementsAreActive();
    goUpdateDownloadCommands();
  },

  _prependBatchFragment() {
    // Workaround multiple reflows hang by removing the richlistbox
    // and adding it back when we're done.

    // Hack for bug 836283: reset xbl fields to their old values after the
    // binding is reattached to avoid breaking the selection state
    let xblFields = new Map();
    for (let key of Object.getOwnPropertyNames(this._richlistbox)) {
      let value = this._richlistbox[key];
      xblFields.set(key, value);
    }

    let oldActiveElement = document.activeElement;
    let parentNode = this._richlistbox.parentNode;
    let nextSibling = this._richlistbox.nextSibling;
    parentNode.removeChild(this._richlistbox);
    this._richlistbox.prepend(this.batchFragment);
    parentNode.insertBefore(this._richlistbox, nextSibling);
    if (oldActiveElement && oldActiveElement != document.activeElement) {
      oldActiveElement.focus();
    }

    for (let [key, value] of xblFields) {
      this._richlistbox[key] = value;
    }
  },

  onDownloadAdded(download, { insertBefore } = {}) {
    let shell = new HistoryDownloadElementShell(download);
    this._viewItemsForDownloads.set(download, shell);

    // Since newest downloads are displayed at the top, either prepend the new
    // element or insert it after the one indicated by the insertBefore option.
    if (insertBefore) {
      this._viewItemsForDownloads.get(insertBefore)
          .element.insertAdjacentElement("afterend", shell.element);
    } else {
      (this.batchFragment || this._richlistbox).prepend(shell.element);
    }

    if (this.searchTerm) {
      shell.element.hidden = !shell.matchesSearchTerm(this.searchTerm);
    }

    // Don't update commands and visible elements during a batch change.
    if (!this.batchFragment) {
      this._ensureVisibleElementsAreActive();
      goUpdateCommand("downloadsCmd_clearDownloads");
    }
  },

  onDownloadChanged(download) {
    this._viewItemsForDownloads.get(download).onChanged();
  },

  onDownloadRemoved(download) {
    let element = this._viewItemsForDownloads.get(download).element;

    // If the element was selected exclusively, select its next
    // sibling first, if not, try for previous sibling, if any.
    if ((element.nextSibling || element.previousSibling) &&
        this._richlistbox.selectedItems &&
        this._richlistbox.selectedItems.length == 1 &&
        this._richlistbox.selectedItems[0] == element) {
      this._richlistbox.selectItem(element.nextSibling ||
                                   element.previousSibling);
    }

    this._richlistbox.removeItemFromSelection(element);
    element.remove();

    // Don't update commands and visible elements during a batch change.
    if (!this.batchFragment) {
      this._ensureVisibleElementsAreActive();
      goUpdateCommand("downloadsCmd_clearDownloads");
    }
  },

  // nsIController
  supportsCommand(aCommand) {
    // Firstly, determine if this is a command that we can handle.
    if (!DownloadsViewUI.isCommandName(aCommand)) {
      return false;
    }
    if (!(aCommand in this) &&
        !(aCommand in HistoryDownloadElementShell.prototype)) {
      return false;
    }
    // If this function returns true, other controllers won't get a chance to
    // process the command even if isCommandEnabled returns false, so it's
    // important to check if the list is focused here to handle common commands
    // like copy and paste correctly. The clear downloads command, instead, is
    // specific to the downloads list but can be invoked from the toolbar, so we
    // can just return true unconditionally.
    return aCommand == "downloadsCmd_clearDownloads" ||
           document.activeElement == this._richlistbox;
  },

  // nsIController
  isCommandEnabled(aCommand) {
    switch (aCommand) {
      case "cmd_copy":
      case "downloadsCmd_openReferrer":
      case "downloadShowMenuItem":
        return this._richlistbox.selectedItems.length == 1;
      case "cmd_selectAll":
        return true;
      case "cmd_paste":
        return this._canDownloadClipboardURL();
      case "downloadsCmd_clearDownloads":
        return this.canClearDownloads(this._richlistbox);
      default:
        return Array.every(this._richlistbox.selectedItems,
                           element => element._shell.isCommandEnabled(aCommand));
    }
  },

  _copySelectedDownloadsToClipboard() {
    let urls = Array.map(this._richlistbox.selectedItems,
                         element => element._shell.download.source.url);

    Cc["@mozilla.org/widget/clipboardhelper;1"]
      .getService(Ci.nsIClipboardHelper)
      .copyString(urls.join("\n"));
  },

  _getURLFromClipboardData() {
    let trans = Cc["@mozilla.org/widget/transferable;1"].
                createInstance(Ci.nsITransferable);
    trans.init(null);

    let flavors = ["text/x-moz-url", "text/unicode"];
    flavors.forEach(trans.addDataFlavor);

    Services.clipboard.getData(trans, Services.clipboard.kGlobalClipboard);

    // Getting the data or creating the nsIURI might fail.
    try {
      let data = {};
      trans.getAnyTransferData({}, data, {});
      let [url, name] = data.value.QueryInterface(Ci.nsISupportsString)
                            .data.split("\n");
      if (url) {
        return [NetUtil.newURI(url).spec, name];
      }
    } catch (ex) {}

    return ["", ""];
  },

  _canDownloadClipboardURL() {
    let [url /* ,name */] = this._getURLFromClipboardData();
    return url != "";
  },

  _downloadURLFromClipboard() {
    let [url, name] = this._getURLFromClipboardData();
    let browserWin = RecentWindow.getMostRecentBrowserWindow();
    let initiatingDoc = browserWin ? browserWin.document : document;
    DownloadURL(url, name, initiatingDoc);
  },

  // nsIController
  doCommand(aCommand) {
    // Commands may be invoked with keyboard shortcuts even if disabled.
    if (!this.isCommandEnabled(aCommand)) {
      return;
    }

    // If this command is not selection-specific, execute it.
    if (aCommand in this) {
      this[aCommand]();
      return;
    }

    // Cloning the nodelist into an array to get a frozen list of selected items.
    // Otherwise, the selectedItems nodelist is live and doCommand may alter the
    // selection while we are trying to do one particular action, like removing
    // items from history.
    let selectedElements = [...this._richlistbox.selectedItems];
    for (let element of selectedElements) {
      element._shell.doCommand(aCommand);
    }
  },

  // nsIController
  onEvent() {},

  cmd_copy() {
    this._copySelectedDownloadsToClipboard();
  },

  cmd_selectAll() {
    this._richlistbox.selectAll();
  },

  cmd_paste() {
    this._downloadURLFromClipboard();
  },

  downloadsCmd_clearDownloads() {
    this._downloadsData.removeFinished();
    if (this._place) {
      Cc["@mozilla.org/browser/download-history;1"]
        .getService(Ci.nsIDownloadHistory)
        .removeAllDownloads();
    }
    // There may be no selection or focus change as a result
    // of these change, and we want the command updated immediately.
    goUpdateCommand("downloadsCmd_clearDownloads");
  },

  onContextMenu(aEvent) {
    let element = this._richlistbox.selectedItem;
    if (!element || !element._shell) {
      return false;
    }

    // Set the state attribute so that only the appropriate items are displayed.
    let contextMenu = document.getElementById("downloadsContextMenu");
    let download = element._shell.download;
    contextMenu.setAttribute("state",
                             DownloadsCommon.stateOfDownload(download));
    contextMenu.setAttribute("exists", "true");
    contextMenu.classList.toggle("temporary-block",
                                 !!download.hasBlockedData);

    if (!download.stopped) {
      // The hasPartialData property of a download may change at any time after
      // it has started, so ensure we update the related command now.
      goUpdateCommand("downloadsCmd_pauseResume");
    }

    return true;
  },

  onKeyPress(aEvent) {
    let selectedElements = this._richlistbox.selectedItems;
    if (aEvent.keyCode == KeyEvent.DOM_VK_RETURN) {
      // In the content tree, opening bookmarks by pressing return is only
      // supported when a single item is selected. To be consistent, do the
      // same here.
      if (selectedElements.length == 1) {
        let element = selectedElements[0];
        if (element._shell) {
          element._shell.doDefaultCommand();
        }
      }
    } else if (aEvent.charCode == " ".charCodeAt(0)) {
      // Pause/Resume every selected download
      for (let element of selectedElements) {
        if (element._shell.isCommandEnabled("downloadsCmd_pauseResume")) {
          element._shell.doCommand("downloadsCmd_pauseResume");
        }
      }
    }
  },

  onDoubleClick(aEvent) {
    if (aEvent.button != 0) {
      return;
    }

    let selectedElements = this._richlistbox.selectedItems;
    if (selectedElements.length != 1) {
      return;
    }

    let element = selectedElements[0];
    if (element._shell) {
      element._shell.doDefaultCommand();
    }
  },

  onScroll() {
    this._ensureVisibleElementsAreActive();
  },

  onSelect() {
    goUpdateDownloadCommands();

    let selectedElements = this._richlistbox.selectedItems;
    for (let elt of selectedElements) {
      if (elt._shell) {
        elt._shell.onSelect();
      }
    }
  },

  onDragStart(aEvent) {
    // TODO Bug 831358: Support d&d for multiple selection.
    // For now, we just drag the first element.
    let selectedItem = this._richlistbox.selectedItem;
    if (!selectedItem) {
      return;
    }

    let targetPath = selectedItem._shell.download.target.path;
    if (!targetPath) {
      return;
    }

    // We must check for existence synchronously because this is a DOM event.
    let file = new FileUtils.File(targetPath);
    if (!file.exists()) {
      return;
    }

    let dt = aEvent.dataTransfer;
    dt.mozSetDataAt("application/x-moz-file", file, 0);
    let url = Services.io.newFileURI(file).spec;
    dt.setData("text/uri-list", url);
    dt.setData("text/plain", url);
    dt.effectAllowed = "copyMove";
    dt.addElement(selectedItem);
  },

  onDragOver(aEvent) {
    let types = aEvent.dataTransfer.types;
    if (types.includes("text/uri-list") ||
        types.includes("text/x-moz-url") ||
        types.includes("text/plain")) {
      aEvent.preventDefault();
    }
  },

  onDrop(aEvent) {
    let dt = aEvent.dataTransfer;
    // If dragged item is from our source, do not try to
    // redownload already downloaded file.
    if (dt.mozGetDataAt("application/x-moz-file", 0)) {
      return;
    }

    let links = Services.droppedLinkHandler.dropLinks(aEvent);
    if (!links.length)
      return;
    let browserWin = RecentWindow.getMostRecentBrowserWindow();
    let initiatingDoc = browserWin ? browserWin.document : document;
    for (let link of links) {
      if (link.url.startsWith("about:"))
        continue;
      DownloadURL(link.url, link.name, initiatingDoc);
    }
  },
};

for (let methodName of ["load", "applyFilter", "selectNode", "selectItems"]) {
  DownloadsPlacesView.prototype[methodName] = function() {
    throw new Error("|" + methodName +
                    "| is not implemented by the downloads view.");
  };
}

function goUpdateDownloadCommands() {
  function updateCommandsForObject(object) {
    for (let name in object) {
      if (DownloadsViewUI.isCommandName(name)) {
        goUpdateCommand(name);
      }
    }
  }
  updateCommandsForObject(DownloadsPlacesView.prototype);
  updateCommandsForObject(HistoryDownloadElementShell.prototype);
}
PK
!<���XXDchrome/browser/content/browser/downloads/allDownloadsViewOverlay.xul<?xml version="1.0"?>


<?xml-stylesheet href="chrome://browser/content/downloads/downloads.css"?>
<?xml-stylesheet href="chrome://browser/skin/downloads/allDownloadsViewOverlay.css"?>

<!DOCTYPE overlay [
<!ENTITY % downloadsDTD SYSTEM "chrome://browser/locale/downloads/downloads.dtd">
%downloadsDTD;
]>

<!-- This overlay provides a downloads view that lists both session downloads,
     using the DownloadsView API, and history downloads, using places queries.
     The view also implements a command controller and a context menu for
     managing the downloads list.  In order to use this view:
     1. Apply this overlay to your window.
     2. Insert in all the overlay entry-points, namely:
        <richlistbox id="downloadsRichListBox"/>
        <commandset id="downloadCommands"/>
        <menupopup id="downloadsContextMenu"/>
    3. Make sure your window has the editMenuOverlay overlay applied,
       because the view implements cmd_copy and cmd_delete.
    4. Make sure your window has the globalOverlay.js script loaded.
    5. To initialize the view
        let view = new DownloadsPlacesView(document.getElementById("downloadsRichListBox"));
        // This is what the Places Library uses. It could be tweaked a bit as long as the
        // transition-type is set correctly
        view.place = "place:transition=7&sort=4";
-->
<overlay id="downloadsViewOverlay"
         xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

  <script type="application/javascript"
          src="chrome://browser/content/downloads/allDownloadsViewOverlay.js"/>
  <script type="application/javascript"
          src="chrome://global/content/contentAreaUtils.js"/>

  <richlistbox flex="1"
               seltype="multiple"
               id="downloadsRichListBox" context="downloadsContextMenu"
               onscroll="return this._placesView.onScroll();"
               onkeypress="return this._placesView.onKeyPress(event);"
               ondblclick="return this._placesView.onDoubleClick(event);"
               oncontextmenu="return this._placesView.onContextMenu(event);"
               ondragstart="this._placesView.onDragStart(event);"
               ondragover="this._placesView.onDragOver(event);"
               ondrop="this._placesView.onDrop(event);"
               onfocus="goUpdateDownloadCommands();"
               onselect="this._placesView.onSelect();"
               onblur="goUpdateDownloadCommands();"/>

  <commandset id="downloadCommands"
              commandupdater="true"
              events="focus,select,contextmenu"
              oncommandupdate="goUpdateDownloadCommands();">
    <command id="downloadsCmd_pauseResume"
             oncommand="goDoCommand('downloadsCmd_pauseResume')"/>
    <command id="downloadsCmd_cancel"
             oncommand="goDoCommand('downloadsCmd_cancel')"/>
    <command id="downloadsCmd_unblock"
             oncommand="goDoCommand('downloadsCmd_unblock')"/>
    <command id="downloadsCmd_chooseUnblock"
             oncommand="goDoCommand('downloadsCmd_chooseUnblock')"/>
    <command id="downloadsCmd_chooseOpen"
             oncommand="goDoCommand('downloadsCmd_chooseOpen')"/>
    <command id="downloadsCmd_confirmBlock"
             oncommand="goDoCommand('downloadsCmd_confirmBlock')"/>
    <command id="downloadsCmd_open"
             oncommand="goDoCommand('downloadsCmd_open')"/>
    <command id="downloadsCmd_show"
             oncommand="goDoCommand('downloadsCmd_show')"/>
    <command id="downloadsCmd_retry"
             oncommand="goDoCommand('downloadsCmd_retry')"/>
    <command id="downloadsCmd_openReferrer"
             oncommand="goDoCommand('downloadsCmd_openReferrer')"/>
    <command id="downloadsCmd_clearDownloads"
             oncommand="goDoCommand('downloadsCmd_clearDownloads')"/>
  </commandset>

  <menupopup id="downloadsContextMenu" class="download-state">
    <menuitem command="downloadsCmd_pauseResume"
              class="downloadPauseMenuItem"
              label="&cmd.pause.label;"
              accesskey="&cmd.pause.accesskey;"/>
    <menuitem command="downloadsCmd_pauseResume"
              class="downloadResumeMenuItem"
              label="&cmd.resume.label;"
              accesskey="&cmd.resume.accesskey;"/>
    <menuitem command="downloadsCmd_unblock"
              class="downloadUnblockMenuItem"
              label="&cmd.unblock2.label;"
              accesskey="&cmd.unblock2.accesskey;"/>
    <menuitem command="downloadsCmd_show"
              class="downloadShowMenuItem"
              label="&cmd.show.label;"
              accesskey="&cmd.show.accesskey;"
              />

    <menuseparator class="downloadCommandsSeparator"/>

    <menuitem command="downloadsCmd_openReferrer"
              label="&cmd.goToDownloadPage.label;"
              accesskey="&cmd.goToDownloadPage.accesskey;"/>
    <menuitem command="cmd_copy"
              label="&cmd.copyDownloadLink.label;"
              accesskey="&cmd.copyDownloadLink.accesskey;"/>

    <menuseparator/>

    <menuitem command="cmd_delete"
              class="downloadRemoveFromHistoryMenuItem"
              label="&cmd.removeFromHistory.label;"
              accesskey="&cmd.removeFromHistory.accesskey;"/>
    <menuitem command="downloadsCmd_clearDownloads"
              label="&cmd.clearDownloads.label;"
              accesskey="&cmd.clearDownloads.accesskey;"/>
  </menupopup>
</overlay>
PK
!<]"��YYEchrome/browser/content/browser/downloads/contentAreaDownloadsView.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#downloadsListEmptyDescription {
  display: none;
}

#downloadsRichListBox:empty + #downloadsListEmptyDescription {
  display: -moz-box;
}
PK
!<ޜPDchrome/browser/content/browser/downloads/contentAreaDownloadsView.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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-globals-from allDownloadsViewOverlay.js */

ChromeUtils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");

var ContentAreaDownloadsView = {
  init() {
    let view = new DownloadsPlacesView(document.getElementById("downloadsRichListBox"));
    // Do not display the Places downloads in private windows
    if (!PrivateBrowsingUtils.isContentWindowPrivate(window)) {
      view.place = "place:transition=7&sort=4";
    }
    // Set focus to Downloads list once it is created
    document.getElementById("downloadsRichListBox").focus();
  },
};
PK
!<<�CCEchrome/browser/content/browser/downloads/contentAreaDownloadsView.xul<?xml version="1.0"?>


<?xml-stylesheet href="chrome://global/skin/"?>
<?xml-stylesheet href="chrome://browser/content/downloads/contentAreaDownloadsView.css"?>
<?xml-stylesheet href="chrome://browser/skin/downloads/contentAreaDownloadsView.css"?>

<?xul-overlay href="chrome://browser/content/downloads/allDownloadsViewOverlay.xul"?>

<!DOCTYPE window [
<!ENTITY % downloadsDTD SYSTEM "chrome://browser/locale/downloads/downloads.dtd">
%downloadsDTD;
<!ENTITY % editMenuDTD SYSTEM "chrome://global/locale/editMenuOverlay.dtd">
%editMenuDTD;
]>

<window id="contentAreaDownloadsView"
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        title="&downloads.title;"
        onload="ContentAreaDownloadsView.init();">

  <script type="application/javascript"
          src="chrome://global/content/globalOverlay.js"/>
  <script type="application/javascript"
          src="chrome://browser/content/downloads/contentAreaDownloadsView.js"/>

<script type="application/javascript" src="chrome://global/content/editMenuOverlay.js"/>
<commandset id="editMenuCommands">
  <commandset id="editMenuCommandSetAll" commandupdater="true" events="focus,select"
              oncommandupdate="goUpdateGlobalEditMenuItems()"/>
  <commandset id="editMenuCommandSetUndo" commandupdater="true" events="undo"
              oncommandupdate="goUpdateUndoEditMenuItems()"/>
  <commandset id="editMenuCommandSetPaste" commandupdater="true" events="clipboard"
              oncommandupdate="goUpdatePasteMenuItems()"/>
  <command id="cmd_undo" oncommand="goDoCommand('cmd_undo')"/>
  <command id="cmd_redo" oncommand="goDoCommand('cmd_redo')"/>
  <command id="cmd_cut" oncommand="goDoCommand('cmd_cut')"/>
  <command id="cmd_copy" oncommand="goDoCommand('cmd_copy')"/>
  <command id="cmd_paste" oncommand="goDoCommand('cmd_paste')"/>
  <command id="cmd_delete" oncommand="goDoCommand('cmd_delete')"/>
  <command id="cmd_selectAll" oncommand="goDoCommand('cmd_selectAll')"/>
  <command id="cmd_switchTextDirection" oncommand="goDoCommand('cmd_switchTextDirection');"/>
</commandset>

  <!-- These key nodes are here only for show. The real bindings come from
       XBL, in platformHTMLBindings.xml. See bugs 57078 and 71779. -->

  <keyset id="editMenuKeys">
    <key id="key_undo" key="&undoCmd.key;" modifiers="accel" command="cmd_undo"/>
    <key id="key_redo" key="&undoCmd.key;" modifiers="accel,shift" command="cmd_redo"/>
    <key id="key_cut" key="&cutCmd.key;" modifiers="accel" command="cmd_cut"/>
    <key id="key_copy" key="&copyCmd.key;" modifiers="accel" command="cmd_copy"/>
    <key id="key_paste" key="&pasteCmd.key;" modifiers="accel" command="cmd_paste"/>
    <key id="key_delete" keycode="VK_DELETE" command="cmd_delete"/>
    <key id="key_selectAll" key="&selectAllCmd.key;" modifiers="accel" command="cmd_selectAll"/>
    <key id="key_find" key="&findCmd.key;" modifiers="accel" command="cmd_find"/>
    <key id="key_findAgain" key="&findAgainCmd.key;" modifiers="accel" command="cmd_findAgain"/>
    <key id="key_findPrevious" key="&findAgainCmd.key;" modifiers="shift,accel" command="cmd_findPrevious"/>
    <key id="key_findAgain2" keycode="&findAgainCmd.key2;" command="cmd_findAgain"/>
    <key id="key_findPrevious2" keycode="&findAgainCmd.key2;" modifiers="shift" command="cmd_findPrevious"/>
  </keyset>

  <stack flex="1">
    <richlistbox id="downloadsRichListBox"/>
    <description id="downloadsListEmptyDescription"
                 value="&downloadsListEmpty.label;"
                 mousethrough="always"/>
  </stack>
  <commandset id="downloadCommands"/>
  <menupopup id="downloadsContextMenu"/>
</window>
PK
!<��|��5chrome/browser/content/browser/downloads/download.xml<?xml version="1.0"?>
<!-- -*- Mode: HTML; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- -->
<!-- vim: set ts=2 et sw=2 tw=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/. -->

<!DOCTYPE bindings SYSTEM "chrome://browser/locale/downloads/downloads.dtd">

<bindings id="downloadBindings"
          xmlns="http://www.mozilla.org/xbl"
          xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
          xmlns:xbl="http://www.mozilla.org/xbl">

  <binding id="download"
           extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
    <content orient="horizontal"
             onclick="DownloadsView.onDownloadClick(event);">
      <xul:hbox class="downloadMainArea"
                flex="1"
                align="center">
        <xul:stack>
          <xul:image class="downloadTypeIcon"
                     validate="always"
                     xbl:inherits="src=image"/>
          <xul:image class="downloadBlockedBadge" />
        </xul:stack>
        <xul:vbox pack="center"
                  flex="1"
                  class="downloadContainer"
                  style="width: &downloadDetails.width;">
          <!-- We're letting localizers put a min-width in here primarily
               because of the downloads summary at the bottom of the list of
               download items. An element in the summary has the same min-width
               on a description, and we don't want the panel to change size if the
               summary isn't being displayed, so we ensure that items share the
               same minimum width.
               -->
          <xul:description class="downloadTarget"
                           crop="center"
                           style="min-width: &downloadsSummary.minWidth2;"
                           xbl:inherits="value=displayName,tooltiptext=displayName"/>
          <xul:progressmeter anonid="progressmeter"
                             class="downloadProgress"
                             min="0"
                             max="100"
                             xbl:inherits="mode=progressmode,value=progress,paused=progresspaused"/>
          <xul:description class="downloadDetails downloadDetailsNormal"
                           crop="end"
                           xbl:inherits="value=status"/>
          <xul:description class="downloadDetails downloadDetailsHover"
                           crop="end"
                           xbl:inherits="value=hoverStatus"/>
          <xul:description class="downloadDetails downloadDetailsFull"
                           crop="end"
                           xbl:inherits="value=fullStatus,tooltiptext=fullStatus"/>
          <xul:description class="downloadDetails downloadOpenFile"
                           crop="end"
                           value="&openFile.label;"/>
          <xul:description class="downloadDetails downloadShowMoreInfo"
                           crop="end"
                           value="&showMoreInformation.label;"/>
          <xul:stack class="downloadButtonLabels">
            <xul:description class="downloadDetails downloadShow"
                             crop="end"
                             value="&cmd.show.label;"
                             />
            <xul:description class="downloadDetails downloadCancel"
                             crop="end"
                             value="&cancelDownload.label;"/>
            <xul:description class="downloadDetails downloadRetry"
                             crop="end"
                             value="&retryDownload.label;"/>
          </xul:stack>
        </xul:vbox>
      </xul:hbox>
      <xul:toolbarseparator />
      <xul:stack class="downloadButtonArea">
        <xul:button class="downloadButton downloadCancel downloadIconCancel"
                    tooltiptext="&cmd.cancel.label;"
                    oncommand="DownloadsView.onDownloadCommand(event, 'downloadsCmd_cancel');"/>
        <xul:button class="downloadButton downloadRetry downloadIconRetry"
                    tooltiptext="&cmd.retry.label;"
                    oncommand="DownloadsView.onDownloadCommand(event, 'downloadsCmd_retry');"/>
        <xul:button class="downloadButton downloadShow downloadIconShow"
                    tooltiptext="&cmd.show.label;"
                    oncommand="DownloadsView.onDownloadCommand(event, 'downloadsCmd_show');"/>
        <xul:button class="downloadButton downloadConfirmBlock downloadIconCancel"
                    tooltiptext="&cmd.removeFile.label;"
                    oncommand="DownloadsView.onDownloadCommand(event, 'downloadsCmd_confirmBlock');"/>
        <xul:button class="downloadButton downloadChooseUnblock downloadIconShow"
                    tooltiptext="&cmd.chooseUnblock.label;"
                    oncommand="DownloadsView.onDownloadCommand(event, 'downloadsCmd_chooseUnblock');"/>
        <xul:button class="downloadButton downloadChooseOpen downloadIconShow"
                    tooltiptext="&cmd.chooseOpen.label;"
                    oncommand="DownloadsView.onDownloadCommand(event, 'downloadsCmd_chooseOpen');"/>
        <xul:button class="downloadButton downloadShowBlockedInfo"
                    tooltiptext="&cmd.chooseUnblock.label;"
                    oncommand="DownloadsView.onDownloadCommand(event, 'downloadsCmd_showBlockedInfo');"/>
      </xul:stack>
    </content>
  </binding>

  <binding id="download-subview-toolbarbutton"
           extends="chrome://global/content/bindings/button.xml#menu-button-base">
    <content>
      <children includes="observes|template|menupopup|panel|tooltip"/>
      <xul:image class="toolbarbutton-icon" xbl:inherits="validate,src=image,label,consumeanchor"/>
      <xul:vbox class="toolbarbutton-text" flex="1">
        <xul:label crop="end" xbl:inherits="value=label,accesskey,crop,wrap"/>
        <xul:label class="status-text status-full" crop="end" xbl:inherits="value=fullStatus"/>
        <xul:label class="status-text status-open" crop="end" xbl:inherits="value=openLabel"/>
        <xul:label class="status-text status-retry" crop="end" xbl:inherits="value=retryLabel"/>
        <xul:label class="status-text status-show" crop="end" xbl:inherits="value=showLabel"/>
      </xul:vbox>
      <xul:toolbarbutton anonid="button" class="action-button"/>
    </content>
  </binding>
</bindings>
PK
!<��N�N�5chrome/browser/content/browser/downloads/downloads.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=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/. */
/* eslint-env mozilla/browser-window */

/**
 * Handles the Downloads panel user interface for each browser window.
 *
 * This file includes the following constructors and global objects:
 *
 * DownloadsPanel
 * Main entry point for the downloads panel interface.
 *
 * DownloadsView
 * Builds and updates the downloads list widget, responding to changes in the
 * download state and real-time data.  In addition, handles part of the user
 * interaction events raised by the downloads list widget.
 *
 * DownloadsViewItem
 * Builds and updates a single item in the downloads list widget, responding to
 * changes in the download state and real-time data, and handles the user
 * interaction events related to a single item in the downloads list widgets.
 *
 * DownloadsViewController
 * Handles part of the user interaction events raised by the downloads list
 * widget, in particular the "commands" that apply to multiple items, and
 * dispatches the commands that apply to individual items.
 */

/**
 * A few words on focus and focusrings
 *
 * We do quite a few hacks in the Downloads Panel for focusrings. In fact, we
 * basically suppress most if not all XUL-level focusrings, and style/draw
 * them ourselves (using :focus instead of -moz-focusring). There are a few
 * reasons for this:
 *
 * 1) Richlists on OSX don't have focusrings; instead, they are shown as
 *    selected. This makes for some ambiguity when we have a focused/selected
 *    item in the list, and the mouse is hovering a completed download (which
 *    highlights).
 * 2) Windows doesn't show focusrings until after the first time that tab is
 *    pressed (and by then you're focusing the second item in the panel).
 * 3) Richlistbox sets -moz-focusring even when we select it with a mouse.
 *
 * In general, the desired behaviour is to focus the first item after pressing
 * tab/down, and show that focus with a ring. Then, if the mouse moves over
 * the panel, to hide that focus ring; essentially resetting us to the state
 * before pressing the key.
 *
 * We end up capturing the tab/down key events, and preventing their default
 * behaviour. We then set a "keyfocus" attribute on the panel, which allows
 * us to draw a ring around the currently focused element. If the panel is
 * closed or the mouse moves over the panel, we remove the attribute.
 */

"use strict";

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");

ChromeUtils.defineModuleGetter(this, "DownloadsViewUI",
                               "resource:///modules/DownloadsViewUI.jsm");
ChromeUtils.defineModuleGetter(this, "DownloadsSubview",
                               "resource:///modules/DownloadsSubview.jsm");
ChromeUtils.defineModuleGetter(this, "FileUtils",
                               "resource://gre/modules/FileUtils.jsm");
ChromeUtils.defineModuleGetter(this, "NetUtil",
                               "resource://gre/modules/NetUtil.jsm");
ChromeUtils.defineModuleGetter(this, "PlacesUtils",
                               "resource://gre/modules/PlacesUtils.jsm");
ChromeUtils.defineModuleGetter(this, "Services",
                               "resource://gre/modules/Services.jsm");

// DownloadsPanel

/**
 * Main entry point for the downloads panel interface.
 */
var DownloadsPanel = {
  // Initialization and termination

  /**
   * Internal state of the downloads panel, based on one of the kState
   * constants.  This is not the same state as the XUL panel element.
   */
  _state: 0,

  /** The panel is not linked to downloads data yet. */
  get kStateUninitialized() {
    return 0;
  },
  /** This object is linked to data, but the panel is invisible. */
  get kStateHidden() {
    return 1;
  },
  /** The panel will be shown as soon as possible. */
  get kStateWaitingData() {
    return 2;
  },
  /** The panel is open. */
  get kStateShown() {
    return 3;
  },

  /**
   * Starts loading the download data in background, without opening the panel.
   * Use showPanel instead to load the data and open the panel at the same time.
   */
  initialize() {
    DownloadsCommon.log("Attempting to initialize DownloadsPanel for a window.");
    if (this._state != this.kStateUninitialized) {
      DownloadsCommon.log("DownloadsPanel is already initialized.");
      return;
    }
    this._state = this.kStateHidden;

    window.addEventListener("unload", this.onWindowUnload);

    // Load and resume active downloads if required.  If there are downloads to
    // be shown in the panel, they will be loaded asynchronously.
    DownloadsCommon.initializeAllDataLinks();

    // Now that data loading has eventually started, load the required XUL
    // elements and initialize our views.

    this.panel.hidden = false;
    DownloadsViewController.initialize();
    DownloadsCommon.log("Attaching DownloadsView...");
    DownloadsCommon.getData(window).addView(DownloadsView);
    DownloadsCommon.getSummary(window, DownloadsView.kItemCountLimit)
                   .addView(DownloadsSummary);
    DownloadsCommon.log("DownloadsView attached - the panel for this window",
                        "should now see download items come in.");
    DownloadsPanel._attachEventListeners();
    DownloadsCommon.log("DownloadsPanel initialized.");
  },

  /**
   * Closes the downloads panel and frees the internal resources related to the
   * downloads.  The downloads panel can be reopened later, even after this
   * function has been called.
   */
  terminate() {
    DownloadsCommon.log("Attempting to terminate DownloadsPanel for a window.");
    if (this._state == this.kStateUninitialized) {
      DownloadsCommon.log("DownloadsPanel was never initialized. Nothing to do.");
      return;
    }

    window.removeEventListener("unload", this.onWindowUnload);

    // Ensure that the panel is closed before shutting down.
    this.hidePanel();

    DownloadsViewController.terminate();
    DownloadsCommon.getData(window).removeView(DownloadsView);
    DownloadsCommon.getSummary(window, DownloadsView.kItemCountLimit)
                   .removeView(DownloadsSummary);
    this._unattachEventListeners();

    this._state = this.kStateUninitialized;

    DownloadsSummary.active = false;
    DownloadsCommon.log("DownloadsPanel terminated.");
  },

  // Panel interface

  /**
   * Main panel element in the browser window.
   */
  get panel() {
    delete this.panel;
    return this.panel = document.getElementById("downloadsPanel");
  },

  /**
   * Starts opening the downloads panel interface, anchored to the downloads
   * button of the browser window.  The list of downloads to display is
   * initialized the first time this method is called, and the panel is shown
   * only when data is ready.
   */
  showPanel() {
    DownloadsCommon.log("Opening the downloads panel.");

    if (this.isPanelShowing) {
      DownloadsCommon.log("Panel is already showing - focusing instead.");
      this._focusPanel();
      return;
    }

    // As a belt-and-suspenders check, ensure the button is not hidden.
    DownloadsButton.unhide();

    this.initialize();
    // Delay displaying the panel because this function will sometimes be
    // called while another window is closing (like the window for selecting
    // whether to save or open the file), and that would cause the panel to
    // close immediately.
    setTimeout(() => this._openPopupIfDataReady(), 0);

    DownloadsCommon.log("Waiting for the downloads panel to appear.");
    this._state = this.kStateWaitingData;
  },

  /**
   * Hides the downloads panel, if visible, but keeps the internal state so that
   * the panel can be reopened quickly if required.
   */
  hidePanel() {
    DownloadsCommon.log("Closing the downloads panel.");

    if (!this.isPanelShowing) {
      DownloadsCommon.log("Downloads panel is not showing - nothing to do.");
      return;
    }

    PanelMultiView.hidePopup(this.panel);

    // Ensure that we allow the panel to be reopened.  Note that, if the popup
    // was open, then the onPopupHidden event handler has already updated the
    // current state, otherwise we must update the state ourselves.
    this._state = this.kStateHidden;
    DownloadsCommon.log("Downloads panel is now closed.");
  },

  /**
   * Indicates whether the panel is shown or will be shown.
   */
  get isPanelShowing() {
    return this._state == this.kStateWaitingData ||
           this._state == this.kStateShown;
  },

  /**
   * Returns whether the user has started keyboard navigation.
   */
  get keyFocusing() {
    return this.panel.hasAttribute("keyfocus");
  },

  /**
   * Set to true if the user has started keyboard navigation, and we should be
   * showing focusrings in the panel. Also adds a mousemove event handler to
   * the panel which disables keyFocusing.
   */
  set keyFocusing(aValue) {
    if (aValue) {
      this.panel.setAttribute("keyfocus", "true");
      this.panel.addEventListener("mousemove", this);
    } else {
      this.panel.removeAttribute("keyfocus");
      this.panel.removeEventListener("mousemove", this);
    }
    return aValue;
  },

  /**
   * Handles the mousemove event for the panel, which disables focusring
   * visualization.
   */
  handleEvent(aEvent) {
    switch (aEvent.type) {
      case "mousemove":
        this.keyFocusing = false;
        break;
      case "keydown":
        this._onKeyDown(aEvent);
        break;
      case "keypress":
        this._onKeyPress(aEvent);
        break;
    }
  },

  // Callback functions from DownloadsView

  /**
   * Called after data loading finished.
   */
  onViewLoadCompleted() {
    this._openPopupIfDataReady();
  },

  // User interface event functions

  onWindowUnload() {
    // This function is registered as an event listener, we can't use "this".
    DownloadsPanel.terminate();
  },

  onPopupShown(aEvent) {
    // Ignore events raised by nested popups.
    if (aEvent.target != aEvent.currentTarget) {
      return;
    }

    DownloadsCommon.log("Downloads panel has shown.");
    this._state = this.kStateShown;

    // Since at most one popup is open at any given time, we can set globally.
    DownloadsCommon.getIndicatorData(window).attentionSuppressed = true;

    // Ensure that the first item is selected when the panel is focused.
    if (DownloadsView.richListBox.itemCount > 0 &&
        DownloadsView.richListBox.selectedIndex == -1) {
      DownloadsView.richListBox.selectedIndex = 0;
    }

    this._focusPanel();
  },

  onPopupHidden(aEvent) {
    // Ignore events raised by nested popups.
    if (aEvent.target != aEvent.currentTarget) {
      return;
    }

    DownloadsCommon.log("Downloads panel has hidden.");

    // Removes the keyfocus attribute so that we stop handling keyboard
    // navigation.
    this.keyFocusing = false;

    // Since at most one popup is open at any given time, we can set globally.
    DownloadsCommon.getIndicatorData(window).attentionSuppressed = false;

    // Allow the anchor to be hidden.
    DownloadsButton.releaseAnchor();

    // Allow the panel to be reopened.
    this._state = this.kStateHidden;
  },

  // Related operations

  /**
   * Shows or focuses the user interface dedicated to downloads history.
   */
  showDownloadsHistory() {
    DownloadsCommon.log("Showing download history.");
    // Hide the panel before showing another window, otherwise focus will return
    // to the browser window when the panel closes automatically.
    this.hidePanel();

    BrowserDownloadsUI();
  },

  // Internal functions

  /**
   * Attach event listeners to a panel element. These listeners should be
   * removed in _unattachEventListeners. This is called automatically after the
   * panel has successfully loaded.
   */
  _attachEventListeners() {
    // Handle keydown to support accel-V.
    this.panel.addEventListener("keydown", this);
    // Handle keypress to be able to preventDefault() events before they reach
    // the richlistbox, for keyboard navigation.
    this.panel.addEventListener("keypress", this);
  },

  /**
   * Unattach event listeners that were added in _attachEventListeners. This
   * is called automatically on panel termination.
   */
  _unattachEventListeners() {
    this.panel.removeEventListener("keydown", this);
    this.panel.removeEventListener("keypress", this);
  },

  _onKeyPress(aEvent) {
    // Handle unmodified keys only.
    if (aEvent.altKey || aEvent.ctrlKey || aEvent.shiftKey || aEvent.metaKey) {
      return;
    }

    let richListBox = DownloadsView.richListBox;

    // If the user has pressed the tab, up, or down cursor key, start keyboard
    // navigation, thus enabling focusrings in the panel.  Keyboard navigation
    // is automatically disabled if the user moves the mouse on the panel, or
    // if the panel is closed.
    if ((aEvent.keyCode == aEvent.DOM_VK_TAB ||
         aEvent.keyCode == aEvent.DOM_VK_UP ||
         aEvent.keyCode == aEvent.DOM_VK_DOWN) &&
        !this.keyFocusing) {
      this.keyFocusing = true;
      // Ensure there's a selection, we will show the focus ring around it and
      // prevent the richlistbox from changing the selection.
      if (DownloadsView.richListBox.selectedIndex == -1) {
        DownloadsView.richListBox.selectedIndex = 0;
      }
      aEvent.preventDefault();
      return;
    }

    if (aEvent.keyCode == aEvent.DOM_VK_DOWN) {
      // If the last element in the list is selected, or the footer is already
      // focused, focus the footer.
      if (richListBox.selectedItem === richListBox.lastChild ||
          document.activeElement.parentNode.id === "downloadsFooter") {
        DownloadsFooter.focus();
        aEvent.preventDefault();
        return;
      }
    }

    // Pass keypress events to the richlistbox view when it's focused.
    if (document.activeElement === richListBox) {
      DownloadsView.onDownloadKeyPress(aEvent);
    }
  },

  /**
   * Keydown listener that listens for the keys to start key focusing, as well
   * as the the accel-V "paste" event, which initiates a file download if the
   * pasted item can be resolved to a URI.
   */
  _onKeyDown(aEvent) {
    // If the footer is focused and the downloads list has at least 1 element
    // in it, focus the last element in the list when going up.
    if (aEvent.keyCode == aEvent.DOM_VK_UP &&
        document.activeElement.parentNode.id === "downloadsFooter" &&
        DownloadsView.richListBox.firstChild) {
      DownloadsView.richListBox.focus();
      DownloadsView.richListBox.selectedItem = DownloadsView.richListBox.lastChild;
      aEvent.preventDefault();
      return;
    }

    let pasting = aEvent.keyCode == aEvent.DOM_VK_V &&
                  aEvent.getModifierState("Accel");

    if (!pasting) {
      return;
    }

    DownloadsCommon.log("Received a paste event.");

    let trans = Cc["@mozilla.org/widget/transferable;1"]
                  .createInstance(Ci.nsITransferable);
    trans.init(null);
    let flavors = ["text/x-moz-url", "text/unicode"];
    flavors.forEach(trans.addDataFlavor);
    Services.clipboard.getData(trans, Services.clipboard.kGlobalClipboard);
    // Getting the data or creating the nsIURI might fail
    try {
      let data = {};
      trans.getAnyTransferData({}, data, {});
      let [url, name] = data.value
                            .QueryInterface(Ci.nsISupportsString)
                            .data
                            .split("\n");
      if (!url) {
        return;
      }

      let uri = NetUtil.newURI(url);
      DownloadsCommon.log("Pasted URL seems valid. Starting download.");
      DownloadURL(uri.spec, name, document);
    } catch (ex) {}
  },

  /**
   * Move focus to the main element in the downloads panel, unless another
   * element in the panel is already focused.
   */
  _focusPanel() {
    // We may be invoked while the panel is still waiting to be shown.
    if (this._state != this.kStateShown) {
      return;
    }

    let element = document.commandDispatcher.focusedElement;
    while (element && element != this.panel) {
      element = element.parentNode;
    }
    if (!element) {
      if (DownloadsView.richListBox.itemCount > 0) {
        DownloadsView.richListBox.focus();
      } else {
        DownloadsFooter.focus();
      }
    }
  },

  /**
   * Opens the downloads panel when data is ready to be displayed.
   */
  _openPopupIfDataReady() {
    // We don't want to open the popup if we already displayed it, or if we are
    // still loading data.
    if (this._state != this.kStateWaitingData || DownloadsView.loading) {
      return;
    }

    // At this point, if the window is minimized, opening the panel could fail
    // without any notification, and there would be no way to either open or
    // close the panel any more.  To prevent this, check if the window is
    // minimized and in that case force the panel to the closed state.
    if (window.windowState == window.STATE_MINIMIZED) {
      this._state = this.kStateHidden;
      return;
    }

    // Ensure the anchor is visible.  If that is not possible, show the panel
    // anchored to the top area of the window, near the default anchor position.
    let anchor = DownloadsButton.getAnchor();

    if (!anchor) {
      DownloadsCommon.error("Downloads button cannot be found.");
      return;
    }

    let onBookmarksToolbar = !!anchor.closest("#PersonalToolbar");
    this.panel.classList.toggle("bookmarks-toolbar", onBookmarksToolbar);

    // When the panel is opened, we check if the target files of visible items
    // still exist, and update the allowed items interactions accordingly.  We
    // do these checks on a background thread, and don't prevent the panel to
    // be displayed while these checks are being performed.
    for (let viewItem of DownloadsView._visibleViewItems.values()) {
      viewItem.download.refresh().catch(Cu.reportError);
    }

    DownloadsCommon.log("Opening downloads panel popup.");
    PanelMultiView.openPopup(this.panel, anchor, "bottomcenter topright",
                             0, 0, false, null).catch(Cu.reportError);
  },
};

XPCOMUtils.defineConstant(this, "DownloadsPanel", DownloadsPanel);

// DownloadsView

/**
 * Builds and updates the downloads list widget, responding to changes in the
 * download state and real-time data.  In addition, handles part of the user
 * interaction events raised by the downloads list widget.
 */
var DownloadsView = {
  // Functions handling download items in the list

  /**
   * Maximum number of items shown by the list at any given time.
   */
  kItemCountLimit: 5,

  /**
   * Indicates whether there is an open contextMenu for a download item.
   */
  contextMenuOpen: false,

  /**
   * Indicates whether there is a DownloadsBlockedSubview open.
   */
  subViewOpen: false,

  /**
   * Indicates whether we are still loading downloads data asynchronously.
   */
  loading: false,

  /**
   * Ordered array of all Download objects.  We need to keep this array because
   * only a limited number of items are shown at once, and if an item that is
   * currently visible is removed from the list, we might need to take another
   * item from the array and make it appear at the bottom.
   */
  _downloads: [],

  /**
   * Associates the visible Download objects with their corresponding
   * DownloadsViewItem object.  There is a limited number of view items in the
   * panel at any given time.
   */
  _visibleViewItems: new Map(),

  /**
   * Called when the number of items in the list changes.
   */
  _itemCountChanged() {
    DownloadsCommon.log("The downloads item count has changed - we are tracking",
                        this._downloads.length, "downloads in total.");
    let count = this._downloads.length;
    let hiddenCount = count - this.kItemCountLimit;

    if (count > 0) {
      DownloadsCommon.log("Setting the panel's hasdownloads attribute to true.");
      DownloadsPanel.panel.setAttribute("hasdownloads", "true");
    } else {
      DownloadsCommon.log("Removing the panel's hasdownloads attribute.");
      DownloadsPanel.panel.removeAttribute("hasdownloads");
    }

    // If we've got some hidden downloads, we should activate the
    // DownloadsSummary. The DownloadsSummary will determine whether or not
    // it's appropriate to actually display the summary.
    DownloadsSummary.active = hiddenCount > 0;
  },

  /**
   * Element corresponding to the list of downloads.
   */
  get richListBox() {
    delete this.richListBox;
    return this.richListBox = document.getElementById("downloadsListBox");
  },

  /**
   * Element corresponding to the button for showing more downloads.
   */
  get downloadsHistory() {
    delete this.downloadsHistory;
    return this.downloadsHistory = document.getElementById("downloadsHistory");
  },

  // Callback functions from DownloadsData

  /**
   * Called before multiple downloads are about to be loaded.
   */
  onDownloadBatchStarting() {
    DownloadsCommon.log("onDownloadBatchStarting called for DownloadsView.");
    this.loading = true;
  },

  /**
   * Called after data loading finished.
   */
  onDownloadBatchEnded() {
    DownloadsCommon.log("onDownloadBatchEnded called for DownloadsView.");

    this.loading = false;

    // We suppressed item count change notifications during the batch load, at
    // this point we should just call the function once.
    this._itemCountChanged();

    // Notify the panel that all the initially available downloads have been
    // loaded.  This ensures that the interface is visible, if still required.
    DownloadsPanel.onViewLoadCompleted();
  },

  /**
   * Called when a new download data item is available, either during the
   * asynchronous data load or when a new download is started.
   *
   * @param aDownload
   *        Download object that was just added.
   */
  onDownloadAdded(download) {
    DownloadsCommon.log("A new download data item was added");

    this._downloads.unshift(download);

    // The newly added item is visible in the panel and we must add the
    // corresponding element. If the list overflows, remove the last item from
    // the panel to make room for the new one that we just added at the top.
    this._addViewItem(download, true);
    if (this._downloads.length > this.kItemCountLimit) {
      this._removeViewItem(this._downloads[this.kItemCountLimit]);
    }

    // For better performance during batch loads, don't update the count for
    // every item, because the interface won't be visible until load finishes.
    if (!this.loading) {
      this._itemCountChanged();
    }
  },

  onDownloadChanged(download) {
    let viewItem = this._visibleViewItems.get(download);
    if (viewItem) {
      viewItem.onChanged();
    }
  },

  /**
   * Called when a data item is removed.  Ensures that the widget associated
   * with the view item is removed from the user interface.
   *
   * @param download
   *        Download object that is being removed.
   */
  onDownloadRemoved(download) {
    DownloadsCommon.log("A download data item was removed.");

    let itemIndex = this._downloads.indexOf(download);
    this._downloads.splice(itemIndex, 1);

    if (itemIndex < this.kItemCountLimit) {
      // The item to remove is visible in the panel.
      this._removeViewItem(download);
      if (this._downloads.length >= this.kItemCountLimit) {
        // Reinsert the next item into the panel.
        this._addViewItem(this._downloads[this.kItemCountLimit - 1], false);
      }
    }

    this._itemCountChanged();
  },

  /**
   * Associates each richlistitem for a download with its corresponding
   * DownloadsViewItem object.
   */
  _itemsForElements: new Map(),

  itemForElement(element) {
    return this._itemsForElements.get(element);
  },

  /**
   * Creates a new view item associated with the specified data item, and adds
   * it to the top or the bottom of the list.
   */
  _addViewItem(download, aNewest) {
    DownloadsCommon.log("Adding a new DownloadsViewItem to the downloads list.",
                        "aNewest =", aNewest);

    let element = document.createElement("richlistitem");
    let viewItem = new DownloadsViewItem(download, element);
    this._visibleViewItems.set(download, viewItem);
    this._itemsForElements.set(element, viewItem);
    if (aNewest) {
      this.richListBox.insertBefore(element, this.richListBox.firstChild);
    } else {
      this.richListBox.appendChild(element);
    }
  },

  /**
   * Removes the view item associated with the specified data item.
   */
  _removeViewItem(download) {
    DownloadsCommon.log("Removing a DownloadsViewItem from the downloads list.");
    let element = this._visibleViewItems.get(download).element;
    let previousSelectedIndex = this.richListBox.selectedIndex;
    this.richListBox.removeChild(element);
    if (previousSelectedIndex != -1) {
      this.richListBox.selectedIndex = Math.min(previousSelectedIndex,
                                                this.richListBox.itemCount - 1);
    }
    this._visibleViewItems.delete(download);
    this._itemsForElements.delete(element);
  },

  // User interface event functions

  /**
   * Helper function to do commands on a specific download item.
   *
   * @param aEvent
   *        Event object for the event being handled.  If the event target is
   *        not a richlistitem that represents a download, this function will
   *        walk up the parent nodes until it finds a DOM node that is.
   * @param aCommand
   *        The command to be performed.
   */
  onDownloadCommand(aEvent, aCommand) {
    let target = aEvent.target;
    while (target.nodeName != "richlistitem") {
      target = target.parentNode;
    }
    DownloadsView.itemForElement(target).doCommand(aCommand);
  },

  onDownloadClick(aEvent) {
    // Handle primary clicks only, and exclude the action button.
    if (aEvent.button == 0 &&
        !aEvent.originalTarget.hasAttribute("oncommand")) {
      let target = aEvent.target;
      while (target.nodeName != "richlistitem") {
        target = target.parentNode;
      }
      let download = DownloadsView.itemForElement(target).download;
      if (download.hasBlockedData) {
        goDoCommand("downloadsCmd_showBlockedInfo");
      } else {
        goDoCommand("downloadsCmd_open");
      }
    }
  },

  /**
   * Handles keypress events on a download item.
   */
  onDownloadKeyPress(aEvent) {
    // Pressing the key on buttons should not invoke the action because the
    // event has already been handled by the button itself.
    if (aEvent.originalTarget.hasAttribute("command") ||
        aEvent.originalTarget.hasAttribute("oncommand")) {
      return;
    }

    if (aEvent.charCode == " ".charCodeAt(0)) {
      goDoCommand("downloadsCmd_pauseResume");
      return;
    }

    if (aEvent.keyCode == KeyEvent.DOM_VK_RETURN) {
      goDoCommand("downloadsCmd_doDefault");
    }
  },

  /**
   * Event handlers to keep track of context menu state (open/closed) for
   * download items.
   */
  onContextPopupShown(aEvent) {
    // Ignore events raised by nested popups.
    if (aEvent.target != aEvent.currentTarget) {
      return;
    }

    DownloadsCommon.log("Context menu has shown.");
    this.contextMenuOpen = true;
  },

  onContextPopupHidden(aEvent) {
    // Ignore events raised by nested popups.
    if (aEvent.target != aEvent.currentTarget) {
      return;
    }

    DownloadsCommon.log("Context menu has hidden.");
    this.contextMenuOpen = false;
  },

  /**
   * Mouse listeners to handle selection on hover.
   */
  onDownloadMouseOver(aEvent) {
    if (aEvent.originalTarget.classList.contains("downloadButton")) {
      aEvent.target.classList.add("downloadHoveringButton");

      let button = aEvent.originalTarget;
      let tooltip = button.getAttribute("tooltiptext");
      if (tooltip) {
        button.setAttribute("aria-label", tooltip);
        button.removeAttribute("tooltiptext");
      }
    }
    if (!(this.contextMenuOpen || this.subViewOpen) &&
        aEvent.target.parentNode == this.richListBox) {
      this.richListBox.selectedItem = aEvent.target;
    }
  },

  onDownloadMouseOut(aEvent) {
    if (aEvent.originalTarget.classList.contains("downloadButton")) {
      aEvent.target.classList.remove("downloadHoveringButton");
    }
    if (!(this.contextMenuOpen || this.subViewOpen) &&
        aEvent.target.parentNode == this.richListBox) {
      // If the destination element is outside of the richlistitem, clear the
      // selection.
      let element = aEvent.relatedTarget;
      while (element && element != aEvent.target) {
        element = element.parentNode;
      }
      if (!element) {
        this.richListBox.selectedIndex = -1;
      }
    }
  },

  onDownloadContextMenu(aEvent) {
    let element = this.richListBox.selectedItem;
    if (!element) {
      return;
    }

    DownloadsViewController.updateCommands();

    // Set the state attribute so that only the appropriate items are displayed.
    let contextMenu = document.getElementById("downloadsContextMenu");
    contextMenu.setAttribute("state", element.getAttribute("state"));
    if (element.hasAttribute("exists")) {
      contextMenu.setAttribute("exists", "true");
    } else {
      contextMenu.removeAttribute("exists");
    }
    contextMenu.classList.toggle("temporary-block",
                                 element.classList.contains("temporary-block"));
  },

  onDownloadDragStart(aEvent) {
    let element = this.richListBox.selectedItem;
    if (!element) {
      return;
    }

    // We must check for existence synchronously because this is a DOM event.
    let file = new FileUtils.File(DownloadsView.itemForElement(element)
                                               .download.target.path);
    if (!file.exists()) {
      return;
    }

    let dataTransfer = aEvent.dataTransfer;
    dataTransfer.mozSetDataAt("application/x-moz-file", file, 0);
    dataTransfer.effectAllowed = "copyMove";
    let spec = NetUtil.newURI(file).spec;
    dataTransfer.setData("text/uri-list", spec);
    dataTransfer.setData("text/plain", spec);
    dataTransfer.addElement(element);

    aEvent.stopPropagation();
  },
};

XPCOMUtils.defineConstant(this, "DownloadsView", DownloadsView);

// DownloadsViewItem

/**
 * Builds and updates a single item in the downloads list widget, responding to
 * changes in the download state and real-time data, and handles the user
 * interaction events related to a single item in the downloads list widgets.
 *
 * @param download
 *        Download object to be associated with the view item.
 * @param aElement
 *        XUL element corresponding to the single download item in the view.
 */
function DownloadsViewItem(download, aElement) {
  this.download = download;
  this.downloadState = DownloadsCommon.stateOfDownload(download);
  this.element = aElement;
  this.element._shell = this;

  this.element.setAttribute("type", "download");
  this.element.classList.add("download-state");

  this._updateState();
}

DownloadsViewItem.prototype = {
  __proto__: DownloadsViewUI.DownloadElementShell.prototype,

  /**
   * The XUL element corresponding to the associated richlistbox item.
   */
  _element: null,

  onChanged() {
    let newState = DownloadsCommon.stateOfDownload(this.download);
    if (this.downloadState != newState) {
      this.downloadState = newState;
      this._updateState();
    }
    this._updateProgress();
  },

  isCommandEnabled(aCommand) {
    switch (aCommand) {
      case "downloadsCmd_open": {
        if (!this.download.succeeded) {
          return false;
        }

        let file = new FileUtils.File(this.download.target.path);
        return file.exists();
      }
      case "downloadsCmd_show": {
        let file = new FileUtils.File(this.download.target.path);
        if (file.exists()) {
          return true;
        }

        if (!this.download.target.partFilePath) {
          return false;
        }

        let partFile = new FileUtils.File(this.download.target.partFilePath);
        return partFile.exists();
      }
      case "cmd_delete":
      case "downloadsCmd_copyLocation":
      case "downloadsCmd_doDefault":
        return true;
      case "downloadsCmd_showBlockedInfo":
        return this.download.hasBlockedData;
    }
    return DownloadsViewUI.DownloadElementShell.prototype
                          .isCommandEnabled.call(this, aCommand);
  },

  doCommand(aCommand) {
    if (this.isCommandEnabled(aCommand)) {
      this[aCommand]();
    }
  },

  // Item commands

  downloadsCmd_unblock() {
    DownloadsPanel.hidePanel();
    this.confirmUnblock(window, "unblock");
  },

  downloadsCmd_chooseUnblock() {
    DownloadsPanel.hidePanel();
    this.confirmUnblock(window, "chooseUnblock");
  },

  downloadsCmd_unblockAndOpen() {
    DownloadsPanel.hidePanel();
    this.unblockAndOpenDownload().catch(Cu.reportError);
  },

  downloadsCmd_open() {
    this.download.launch().catch(Cu.reportError);

    // We explicitly close the panel here to give the user the feedback that
    // their click has been received, and we're handling the action.
    // Otherwise, we'd have to wait for the file-type handler to execute
    // before the panel would close. This also helps to prevent the user from
    // accidentally opening a file several times.
    DownloadsPanel.hidePanel();
  },

  downloadsCmd_show() {
    let file = new FileUtils.File(this.download.target.path);
    DownloadsCommon.showDownloadedFile(file);

    // We explicitly close the panel here to give the user the feedback that
    // their click has been received, and we're handling the action.
    // Otherwise, we'd have to wait for the operating system file manager
    // window to open before the panel closed. This also helps to prevent the
    // user from opening the containing folder several times.
    DownloadsPanel.hidePanel();
  },

  downloadsCmd_showBlockedInfo() {
    DownloadsBlockedSubview.toggle(this.element,
                                   ...this.rawBlockedTitleAndDetails);
  },

  downloadsCmd_openReferrer() {
    openURL(this.download.source.referrer);
  },

  downloadsCmd_copyLocation() {
    let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"]
                    .getService(Ci.nsIClipboardHelper);
    clipboard.copyString(this.download.source.url);
  },

  downloadsCmd_doDefault() {
    let defaultCommand = this.currentDefaultCommandName;
    if (defaultCommand && this.isCommandEnabled(defaultCommand)) {
      this.doCommand(defaultCommand);
    }
  },
};

// DownloadsViewController

/**
 * Handles part of the user interaction events raised by the downloads list
 * widget, in particular the "commands" that apply to multiple items, and
 * dispatches the commands that apply to individual items.
 */
var DownloadsViewController = {
  // Initialization and termination

  initialize() {
    window.controllers.insertControllerAt(0, this);
  },

  terminate() {
    window.controllers.removeController(this);
  },

  // nsIController

  supportsCommand(aCommand) {
    if (aCommand === "downloadsCmd_clearList") {
      return true;
    }
    // Firstly, determine if this is a command that we can handle.
    if (!DownloadsViewUI.isCommandName(aCommand)) {
      return false;
    }
    if (!(aCommand in this) &&
        !(aCommand in DownloadsViewItem.prototype)) {
      return false;
    }
    // The currently supported commands depend on whether the blocked subview is
    // showing.  If it is, then take the following path.
    if (DownloadsView.subViewOpen) {
      let blockedSubviewCmds = [
        "downloadsCmd_unblockAndOpen",
        "cmd_delete",
      ];
      return blockedSubviewCmds.includes(aCommand);
    }
    // If the blocked subview is not showing, then determine if focus is on a
    // control in the downloads list.
    let element = document.commandDispatcher.focusedElement;
    while (element && element != DownloadsView.richListBox) {
      element = element.parentNode;
    }
    // We should handle the command only if the downloads list is among the
    // ancestors of the focused element.
    return !!element;
  },

  isCommandEnabled(aCommand) {
    // Handle commands that are not selection-specific.
    if (aCommand == "downloadsCmd_clearList") {
      return DownloadsCommon.getData(window).canRemoveFinished;
    }

    // Other commands are selection-specific.
    let element = DownloadsView.richListBox.selectedItem;
    return element && DownloadsView.itemForElement(element)
                                   .isCommandEnabled(aCommand);
  },

  doCommand(aCommand) {
    // If this command is not selection-specific, execute it.
    if (aCommand in this) {
      this[aCommand]();
      return;
    }

    // Other commands are selection-specific.
    let element = DownloadsView.richListBox.selectedItem;
    if (element) {
      // The doCommand function also checks if the command is enabled.
      DownloadsView.itemForElement(element).doCommand(aCommand);
    }
  },

  onEvent() {},

  // Other functions

  updateCommands() {
    function updateCommandsForObject(object) {
      for (let name in object) {
        if (DownloadsViewUI.isCommandName(name)) {
          goUpdateCommand(name);
        }
      }
    }
    updateCommandsForObject(this);
    updateCommandsForObject(DownloadsViewItem.prototype);
  },

  // Selection-independent commands

  downloadsCmd_clearList() {
    DownloadsCommon.getData(window).removeFinished();
  },
};

XPCOMUtils.defineConstant(this, "DownloadsViewController", DownloadsViewController);

// DownloadsSummary

/**
 * Manages the summary at the bottom of the downloads panel list if the number
 * of items in the list exceeds the panels limit.
 */
var DownloadsSummary = {

  /**
   * Sets the active state of the summary. When active, the summary subscribes
   * to the DownloadsCommon DownloadsSummaryData singleton.
   *
   * @param aActive
   *        Set to true to activate the summary.
   */
  set active(aActive) {
    if (aActive == this._active || !this._summaryNode) {
      return this._active;
    }
    if (aActive) {
      DownloadsCommon.getSummary(window, DownloadsView.kItemCountLimit)
                     .refreshView(this);
    } else {
      DownloadsFooter.showingSummary = false;
    }

    return this._active = aActive;
  },

  /**
   * Returns the active state of the downloads summary.
   */
  get active() {
    return this._active;
  },

  _active: false,

  /**
   * Sets whether or not we show the progress bar.
   *
   * @param aShowingProgress
   *        True if we should show the progress bar.
   */
  set showingProgress(aShowingProgress) {
    if (aShowingProgress) {
      this._summaryNode.setAttribute("inprogress", "true");
    } else {
      this._summaryNode.removeAttribute("inprogress");
    }
    // If progress isn't being shown, then we simply do not show the summary.
    return DownloadsFooter.showingSummary = aShowingProgress;
  },

  /**
   * Sets the amount of progress that is visible in the progress bar.
   *
   * @param aValue
   *        A value between 0 and 100 to represent the progress of the
   *        summarized downloads.
   */
  set percentComplete(aValue) {
    if (this._progressNode) {
      this._progressNode.setAttribute("value", aValue);
    }
    return aValue;
  },

  /**
   * Sets the description for the download summary.
   *
   * @param aValue
   *        A string representing the description of the summarized
   *        downloads.
   */
  set description(aValue) {
    if (this._descriptionNode) {
      this._descriptionNode.setAttribute("value", aValue);
      this._descriptionNode.setAttribute("tooltiptext", aValue);
    }
    return aValue;
  },

  /**
   * Sets the details for the download summary, such as the time remaining,
   * the amount of bytes transferred, etc.
   *
   * @param aValue
   *        A string representing the details of the summarized
   *        downloads.
   */
  set details(aValue) {
    if (this._detailsNode) {
      this._detailsNode.setAttribute("value", aValue);
      this._detailsNode.setAttribute("tooltiptext", aValue);
    }
    return aValue;
  },

  /**
   * Focuses the root element of the summary.
   */
  focus() {
    if (this._summaryNode) {
      this._summaryNode.focus();
    }
  },

  /**
   * Respond to keydown events on the Downloads Summary node.
   *
   * @param aEvent
   *        The keydown event being handled.
   */
  onKeyDown(aEvent) {
    if (aEvent.charCode == " ".charCodeAt(0) ||
        aEvent.keyCode == KeyEvent.DOM_VK_RETURN) {
      DownloadsPanel.showDownloadsHistory();
    }
  },

  /**
   * Respond to click events on the Downloads Summary node.
   *
   * @param aEvent
   *        The click event being handled.
   */
  onClick(aEvent) {
    DownloadsPanel.showDownloadsHistory();
  },

  /**
   * Element corresponding to the root of the downloads summary.
   */
  get _summaryNode() {
    let node = document.getElementById("downloadsSummary");
    if (!node) {
      return null;
    }
    delete this._summaryNode;
    return this._summaryNode = node;
  },

  /**
   * Element corresponding to the progress bar in the downloads summary.
   */
  get _progressNode() {
    let node = document.getElementById("downloadsSummaryProgress");
    if (!node) {
      return null;
    }
    delete this._progressNode;
    return this._progressNode = node;
  },

  /**
   * Element corresponding to the main description of the downloads
   * summary.
   */
  get _descriptionNode() {
    let node = document.getElementById("downloadsSummaryDescription");
    if (!node) {
      return null;
    }
    delete this._descriptionNode;
    return this._descriptionNode = node;
  },

  /**
   * Element corresponding to the secondary description of the downloads
   * summary.
   */
  get _detailsNode() {
    let node = document.getElementById("downloadsSummaryDetails");
    if (!node) {
      return null;
    }
    delete this._detailsNode;
    return this._detailsNode = node;
  }
};

XPCOMUtils.defineConstant(this, "DownloadsSummary", DownloadsSummary);

// DownloadsFooter

/**
 * Manages events sent to to the footer vbox, which contains both the
 * DownloadsSummary as well as the "Show All Downloads" button.
 */
var DownloadsFooter = {

  /**
   * Focuses the appropriate element within the footer. If the summary
   * is visible, focus it. If not, focus the "Show All Downloads"
   * button.
   */
  focus() {
    if (this._showingSummary) {
      DownloadsSummary.focus();
    } else {
      DownloadsView.downloadsHistory.focus();
    }
  },

  _showingSummary: false,

  /**
   * Sets whether or not the Downloads Summary should be displayed in the
   * footer. If not, the "Show All Downloads" button is shown instead.
   */
  set showingSummary(aValue) {
    if (this._footerNode) {
      if (aValue) {
        this._footerNode.setAttribute("showingsummary", "true");
      } else {
        this._footerNode.removeAttribute("showingsummary");
      }
      this._showingSummary = aValue;
    }
    return aValue;
  },

  /**
   * Element corresponding to the footer of the downloads panel.
   */
  get _footerNode() {
    let node = document.getElementById("downloadsFooter");
    if (!node) {
      return null;
    }
    delete this._footerNode;
    return this._footerNode = node;
  }
};

XPCOMUtils.defineConstant(this, "DownloadsFooter", DownloadsFooter);


// DownloadsBlockedSubview

/**
 * Manages the blocked subview that slides in when you click a blocked download.
 */
var DownloadsBlockedSubview = {
  /**
   * Elements in the subview.
   */
  get elements() {
    let idSuffixes = [
      "title",
      "details1",
      "details2",
      "openButton",
      "deleteButton",
    ];
    let elements = idSuffixes.reduce((memo, s) => {
      memo[s] = document.getElementById("downloadsPanel-blockedSubview-" + s);
      return memo;
    }, {});
    delete this.elements;
    return this.elements = elements;
  },

  /**
   * The blocked-download richlistitem element that was clicked to show the
   * subview.  If the subview is not showing, this is undefined.
   */
  element: undefined,

  /**
   * Slides in the blocked subview.
   *
   * @param element
   *        The blocked-download richlistitem element that was clicked.
   * @param title
   *        The title to show in the subview.
   * @param details
   *        An array of strings with information about the block.
   */
  toggle(element, title, details) {
    DownloadsView.subViewOpen = true;
    DownloadsViewController.updateCommands();

    let e = this.elements;
    let s = DownloadsCommon.strings;
    e.title.textContent = title;
    e.details1.textContent = details[0];
    e.details2.textContent = details[1];
    e.openButton.label = s.unblockButtonOpen;
    e.deleteButton.label = s.unblockButtonConfirmBlock;

    let verdict = element.getAttribute("verdict");
    this.subview.setAttribute("verdict", verdict);

    this.mainView.addEventListener("ViewShown", this);
    DownloadsPanel.panel.addEventListener("popuphidden", this);
    this.panelMultiView.showSubView(this.subview);

    // Without this, the mainView is more narrow than the panel once all
    // downloads are removed from the panel.
    this.mainView.style.minWidth = window.getComputedStyle(this.subview).width;
  },

  handleEvent(event) {
    // This is called when the main view is shown or the panel is hidden.
    DownloadsView.subViewOpen = false;
    this.mainView.removeEventListener("ViewShown", this);
    DownloadsPanel.panel.removeEventListener("popuphidden", this);
    // Focus the proper element if we're going back to the main panel.
    if (event.type == "ViewShown") {
      DownloadsPanel.showPanel();
    }
  },

  /**
   * Deletes the download and hides the entire panel.
   */
  confirmBlock() {
    goDoCommand("cmd_delete");
    DownloadsPanel.hidePanel();
  },
};

XPCOMUtils.defineLazyGetter(DownloadsBlockedSubview, "panelMultiView",
  () => document.getElementById("downloadsPanel-multiView"));
XPCOMUtils.defineLazyGetter(DownloadsBlockedSubview, "mainView",
  () => document.getElementById("downloadsPanel-mainView"));
XPCOMUtils.defineLazyGetter(DownloadsBlockedSubview, "subview",
  () => document.getElementById("downloadsPanel-blockedSubview"));

XPCOMUtils.defineConstant(this, "DownloadsBlockedSubview",
                          DownloadsBlockedSubview);
PK
!<>�`A6N6N5chrome/browser/content/browser/downloads/indicator.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=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/. */
/* eslint-env mozilla/browser-window */

/**
 * Handles the indicator that displays the progress of ongoing downloads, which
 * is also used as the anchor for the downloads panel.
 *
 * This module includes the following constructors and global objects:
 *
 * DownloadsButton
 * Main entry point for the downloads indicator.  Depending on how the toolbars
 * have been customized, this object determines if we should show a fully
 * functional indicator, a placeholder used during customization and in the
 * customization palette, or a neutral view as a temporary anchor for the
 * downloads panel.
 *
 * DownloadsIndicatorView
 * Builds and updates the actual downloads status widget, responding to changes
 * in the global status data, or provides a neutral view if the indicator is
 * removed from the toolbars and only used as a temporary anchor.  In addition,
 * handles the user interaction events raised by the widget.
 */

"use strict";

ChromeUtils.import("resource://gre/modules/AppConstants.jsm");

// DownloadsButton

/**
 * Main entry point for the downloads indicator.  Depending on how the toolbars
 * have been customized, this object determines if we should show a fully
 * functional indicator, a placeholder used during customization and in the
 * customization palette, or a neutral view as a temporary anchor for the
 * downloads panel.
 */
const DownloadsButton = {
  /**
   * Returns a reference to the downloads button position placeholder, or null
   * if not available because it has been removed from the toolbars.
   */
  get _placeholder() {
    return document.getElementById("downloads-button");
  },

  /**
   * Indicates whether toolbar customization is in progress.
   */
  _customizing: false,

  /**
   * This function is called asynchronously just after window initialization.
   *
   * NOTE: This function should limit the input/output it performs to improve
   *       startup time.
   */
  initializeIndicator() {
    DownloadsIndicatorView.ensureInitialized();
  },

  /**
   * Determines the position where the indicator should appear, and moves its
   * associated element to the new position.
   *
   * @return Anchor element, or null if the indicator is not visible.
   */
  _getAnchorInternal() {
    let indicator = DownloadsIndicatorView.indicator;
    if (!indicator) {
      // Exit now if the button is not in the document.
      return null;
    }

    indicator.open = this._anchorRequested;

    let widget = CustomizableUI.getWidget("downloads-button");
     // Determine if the indicator is located on an invisible toolbar.
     if (!isElementVisible(indicator.parentNode) &&
         widget.areaType == CustomizableUI.TYPE_TOOLBAR) {
       return null;
     }

    return DownloadsIndicatorView.indicatorAnchor;
  },

  /**
   * Indicates whether we should try and show the indicator temporarily as an
   * anchor for the panel, even if the indicator would be hidden by default.
   */
  _anchorRequested: false,

  /**
   * Ensures that there is an anchor available for the panel.
   *
   * @return Anchor element where the panel should be anchored, or null if an
   *         anchor is not available (for example because both the tab bar and
   *         the navigation bar are hidden).
   */
  getAnchor() {
    // Do not allow anchoring the panel to the element while customizing.
    if (this._customizing) {
      return null;
    }

    this._anchorRequested = true;
    return this._getAnchorInternal();
  },

  /**
   * Allows the temporary anchor to be hidden.
   */
  releaseAnchor() {
    this._anchorRequested = false;
    this._getAnchorInternal();
  },

  /**
   * Unhide the button. Generally, this only needs to use the placeholder.
   * However, when starting customize mode, if the button is in the palette,
   * we need to unhide it before customize mode is entered, otherwise it
   * gets ignored by customize mode. To do this, we pass true for
   * `includePalette`. We don't always look in the palette because it's
   * inefficient (compared to getElementById), shouldn't be necessary, and
   * if _placeholder returned the node even if in the palette, other checks
   * would break.
   *
   * @param includePalette  whether to search the palette, too. Defaults to false.
   */
  unhide(includePalette = false) {
    let button = this._placeholder;
    if (!button && includePalette) {
      button = gNavToolbox.palette.querySelector("#downloads-button");
    }
    if (button && button.hasAttribute("hidden")) {
      button.removeAttribute("hidden");
      if (this._navBar.contains(button)) {
        this._navBar.setAttribute("downloadsbuttonshown", "true");
      }
    }
  },

  hide() {
    let button = this._placeholder;
    if (this.autoHideDownloadsButton && button && button.closest("toolbar")) {
      DownloadsPanel.hidePanel();
      button.setAttribute("hidden", "true");
      this._navBar.removeAttribute("downloadsbuttonshown");
    }
  },

  startAutoHide() {
    if (DownloadsIndicatorView.hasDownloads) {
      this.unhide();
    } else {
      this.hide();
    }
  },

  checkForAutoHide() {
    let button = this._placeholder;
    if (!this._customizing && this.autoHideDownloadsButton &&
        button && button.closest("toolbar")) {
      this.startAutoHide();
    } else {
      this.unhide();
    }
  },

  // Callback from CustomizableUI when nodes get moved around.
  // We use this to track whether our node has moved somewhere
  // where we should (not) autohide it.
  onWidgetAfterDOMChange(node) {
    if (node == this._placeholder) {
      this.checkForAutoHide();
    }
  },

  /**
   * This function is called when toolbar customization starts.
   *
   * During customization, we never show the actual download progress indication
   * or the event notifications, but we show a neutral placeholder.  The neutral
   * placeholder is an ordinary button defined in the browser window that can be
   * moved freely between the toolbars and the customization palette.
   */
  onCustomizeStart(win) {
    if (win == window) {
      // Prevent the indicator from being displayed as a temporary anchor
      // during customization, even if requested using the getAnchor method.
      this._customizing = true;
      this._anchorRequested = false;
      this.unhide(true);
    }
  },

  onCustomizeEnd(win) {
    if (win == window) {
      this._customizing = false;
      this.checkForAutoHide();
      DownloadsIndicatorView.afterCustomize();
    }
  },

  init() {
    XPCOMUtils.defineLazyPreferenceGetter(
      this, "autoHideDownloadsButton", "browser.download.autohideButton",
      true, this.checkForAutoHide.bind(this));

    CustomizableUI.addListener(this);
    this.checkForAutoHide();
  },

  uninit() {
    CustomizableUI.removeListener(this);
  },

  get _tabsToolbar() {
    delete this._tabsToolbar;
    return this._tabsToolbar = document.getElementById("TabsToolbar");
  },

  get _navBar() {
    delete this._navBar;
    return this._navBar = document.getElementById("nav-bar");
  }
};

Object.defineProperty(this, "DownloadsButton", {
  value: DownloadsButton,
  enumerable: true,
  writable: false
});

// DownloadsIndicatorView

/**
 * Builds and updates the actual downloads status widget, responding to changes
 * in the global status data, or provides a neutral view if the indicator is
 * removed from the toolbars and only used as a temporary anchor.  In addition,
 * handles the user interaction events raised by the widget.
 */
const DownloadsIndicatorView = {
  /**
   * True when the view is connected with the underlying downloads data.
   */
  _initialized: false,

  /**
   * True when the user interface elements required to display the indicator
   * have finished loading in the browser window, and can be referenced.
   */
  _operational: false,

  /**
   * Prepares the downloads indicator to be displayed.
   */
  ensureInitialized() {
    if (this._initialized) {
      return;
    }
    this._initialized = true;

    window.addEventListener("unload", this.onWindowUnload);
    DownloadsCommon.getIndicatorData(window).addView(this);
  },

  /**
   * Frees the internal resources related to the indicator.
   */
  ensureTerminated() {
    if (!this._initialized) {
      return;
    }
    this._initialized = false;

    window.removeEventListener("unload", this.onWindowUnload);
    DownloadsCommon.getIndicatorData(window).removeView(this);

    // Reset the view properties, so that a neutral indicator is displayed if we
    // are visible only temporarily as an anchor.
    this.percentComplete = 0;
    this.attention = DownloadsCommon.ATTENTION_NONE;
  },

  /**
   * Ensures that the user interface elements required to display the indicator
   * are loaded.
   */
  _ensureOperational() {
    if (this._operational) {
      return;
    }

    // If we don't have a _placeholder, there's no chance that everything
    // will load correctly: bail (and don't set _operational to true!)
    if (!DownloadsButton._placeholder) {
      return;
    }

    this._operational = true;

    // If the view is initialized, we need to update the elements now that
    // they are finally available in the document.
    if (this._initialized) {
      DownloadsCommon.getIndicatorData(window).refreshView(this);
    }
  },

  // Direct control functions

  /**
   * Set to the type ("start" or "finish") when display of a notification is in-progress
   */
  _currentNotificationType: null,

  /**
   * Set to the type ("start" or "finish") when a notification arrives while we
   * are waiting for the timeout of the previous notification
   */
  _nextNotificationType: null,

  /**
   * Check if the panel containing aNode is open.
   * @param aNode
   *        the node whose panel we're interested in.
   */
  _isAncestorPanelOpen(aNode) {
    while (aNode && aNode.localName != "panel") {
      aNode = aNode.parentNode;
    }
    return aNode && aNode.state == "open";
  },

  /**
   * Display or enqueue a visual notification of a relevant event, like a new download.
   *
   * @param aType
   *        Set to "start" for new downloads, "finish" for completed downloads.
   */
  showEventNotification(aType) {
    if (!this._initialized) {
      return;
    }

    if (!DownloadsCommon.animateNotifications) {
      return;
    }

    // enqueue this notification while the current one is being displayed
    if (this._currentNotificationType) {
      // only queue up the notification if it is different to the current one
      if (this._currentNotificationType != aType) {
        this._nextNotificationType = aType;
      }
    } else {
      this._showNotification(aType);
    }
  },

  /**
   * If the status indicator is visible in its assigned position, shows for a
   * brief time a visual notification of a relevant event, like a new download.
   *
   * @param aType
   *        Set to "start" for new downloads, "finish" for completed downloads.
   */
  _showNotification(aType) {
    // No need to show visual notification if the panel is visible.
    if (DownloadsPanel.isPanelShowing) {
      return;
    }

    let anchor = DownloadsButton._placeholder;
    let widgetGroup = CustomizableUI.getWidget("downloads-button");
    let widget = widgetGroup.forWindow(window);
    if (widgetGroup.areaType == CustomizableUI.TYPE_MENU_PANEL) {
      if (anchor && this._isAncestorPanelOpen(anchor)) {
        // If the containing panel is open, don't do anything, because the
        // notification would appear under the open panel. See
        // https://bugzilla.mozilla.org/show_bug.cgi?id=984023
        return;
      }

      // Otherwise, try to use the anchor of the panel:
      anchor = widget.anchor;
    }
    if (!anchor || !isElementVisible(anchor.parentNode)) {
      // Our container isn't visible, so can't show the animation:
      return;
    }

    // The notification element is positioned to show in the same location as
    // the downloads button. It's not in the downloads button itself in order to
    // be able to anchor the notification elsewhere if required, and to ensure
    // the notification isn't clipped by overflow properties of the anchor's
    // container.
    // Note: no notifier animation for download finished in Photon
    let notifier = this.notifier;

    if (aType == "start") {
      // Show the notifier before measuring for size/placement. Being hidden by default
      // avoids the interference with scrolling/APZ when the notifier element is
      // tall enough to overlap the tabbrowser element
      notifier.removeAttribute("hidden");

      // the anchor height may vary if font-size is changed or
      // compact/tablet mode is selected so recalculate this each time
      let anchorRect = anchor.getBoundingClientRect();
      let notifierRect = notifier.getBoundingClientRect();
      let topDiff = anchorRect.top - notifierRect.top;
      let leftDiff = anchorRect.left - notifierRect.left;
      let heightDiff = anchorRect.height - notifierRect.height;
      let widthDiff = anchorRect.width - notifierRect.width;
      let translateX = (leftDiff + .5 * widthDiff) + "px";
      let translateY = (topDiff + .5 * heightDiff) + "px";
      notifier.style.transform = "translate(" + translateX + ", " + translateY + ")";
      notifier.setAttribute("notification", aType);
    }
    anchor.setAttribute("notification", aType);

    let animationDuration;
    // This value is determined by the overall duration of animation in CSS.
    animationDuration = aType == "start" ? 760 : 850;

    this._currentNotificationType = aType;

    setTimeout(() => {
      requestAnimationFrame(() => {
        notifier.setAttribute("hidden", "true");
        notifier.removeAttribute("notification");
        notifier.style.transform = "";
        anchor.removeAttribute("notification");

        requestAnimationFrame(() => {
          let nextType = this._nextNotificationType;
          this._currentNotificationType = null;
          this._nextNotificationType = null;
          if (nextType) {
            this._showNotification(nextType);
          }
        });
      });
    }, animationDuration);
  },

  // Callback functions from DownloadsIndicatorData

  /**
   * Indicates whether the indicator should be shown because there are some
   * downloads to be displayed.
   */
  set hasDownloads(aValue) {
    if (this._hasDownloads != aValue || (!this._operational && aValue)) {
      this._hasDownloads = aValue;

      // If there is at least one download, ensure that the view elements are
      // operational
      if (aValue) {
        DownloadsButton.unhide();
        this._ensureOperational();
      } else {
        DownloadsButton.checkForAutoHide();
      }
    }
    return aValue;
  },
  get hasDownloads() {
    return this._hasDownloads;
  },
  _hasDownloads: false,

  /**
   * Progress indication to display, from 0 to 100, or -1 if unknown.
   * Progress is not visible if the current progress is unknown.
   */
  set percentComplete(aValue) {
    if (!this._operational) {
      return this._percentComplete;
    }

    if (this._percentComplete !== aValue) {
      this._percentComplete = aValue;
      this._refreshAttention();

      if (this._percentComplete >= 0) {
        this.indicator.setAttribute("progress", "true");
        // For arrow type only:
        // We set animationDelay to a minus value (0s ~ -100s) to show the
        // corresponding frame needed for progress.
        this._progressIcon.style.animationDelay = (-this._percentComplete) + "s";
      } else {
        this.indicator.removeAttribute("progress");
        this._progressIcon.style.animationDelay = "1s";
      }
    }
    return aValue;
  },
  _percentComplete: null,

  /**
   * Set when the indicator should draw user attention to itself.
   */
  set attention(aValue) {
    if (!this._operational) {
      return this._attention;
    }
    if (this._attention != aValue) {
      this._attention = aValue;
      this._refreshAttention();
    }
    return this._attention;
  },

  _refreshAttention() {
    // Check if the downloads button is in the menu panel, to determine which
    // button needs to get a badge.
    let widgetGroup = CustomizableUI.getWidget("downloads-button");
    let inMenu = widgetGroup.areaType == CustomizableUI.TYPE_MENU_PANEL;

    // For arrow-Styled indicator, suppress success attention if we have
    // progress in toolbar
    let suppressAttention = !inMenu &&
      this._attention == DownloadsCommon.ATTENTION_SUCCESS &&
      this._percentComplete >= 0;

    if (suppressAttention || this._attention == DownloadsCommon.ATTENTION_NONE) {
      this.indicator.removeAttribute("attention");
    } else {
      this.indicator.setAttribute("attention", this._attention);
    }
  },
  _attention: DownloadsCommon.ATTENTION_NONE,

  // User interface event functions

  onWindowUnload() {
    // This function is registered as an event listener, we can't use "this".
    DownloadsIndicatorView.ensureTerminated();
  },

  onCommand(aEvent) {
    if (aEvent.type == "mousedown" && aEvent.button != 0) {
      return;
    }

    DownloadsPanel.showPanel();
    aEvent.stopPropagation();
  },

  onDragOver(aEvent) {
    browserDragAndDrop.dragOver(aEvent);
  },

  onDrop(aEvent) {
    let dt = aEvent.dataTransfer;
    // If dragged item is from our source, do not try to
    // redownload already downloaded file.
    if (dt.mozGetDataAt("application/x-moz-file", 0))
      return;

    let links = browserDragAndDrop.dropLinks(aEvent);
    if (!links.length)
      return;
    let sourceDoc = dt.mozSourceNode ? dt.mozSourceNode.ownerDocument : document;
    let handled = false;
    for (let link of links) {
      if (link.url.startsWith("about:"))
        continue;
      saveURL(link.url, link.name, null, true, true, null, sourceDoc);
      handled = true;
    }
    if (handled) {
      aEvent.preventDefault();
    }
  },

  _indicator: null,
  __progressIcon: null,

  /**
   * Returns a reference to the main indicator element, or null if the element
   * is not present in the browser window yet.
   */
  get indicator() {
    if (this._indicator) {
      return this._indicator;
    }

    let indicator = document.getElementById("downloads-button");
    if (!indicator || indicator.getAttribute("indicator") != "true") {
      return null;
    }

    return this._indicator = indicator;
  },

  get indicatorAnchor() {
    let widgetGroup = CustomizableUI.getWidget("downloads-button");
    if (widgetGroup.areaType == CustomizableUI.TYPE_MENU_PANEL) {
      let overflowIcon = widgetGroup.forWindow(window).anchor;
      return document.getAnonymousElementByAttribute(overflowIcon, "class", "toolbarbutton-icon");
    }

    return document.getAnonymousElementByAttribute(this.indicator, "class",
                                                   "toolbarbutton-badge-stack");
  },

  get _progressIcon() {
    return this.__progressIcon ||
      (this.__progressIcon = document.getElementById("downloads-indicator-progress-inner"));
  },

  get notifier() {
    return this._notifier ||
      (this._notifier = document.getElementById("downloads-notification-anchor"));
  },

  _onCustomizedAway() {
    this._indicator = null;
    this.__progressIcon = null;
  },

  afterCustomize() {
    // If the cached indicator is not the one currently in the document,
    // invalidate our references
    if (this._indicator != document.getElementById("downloads-button")) {
      this._onCustomizedAway();
      this._operational = false;
      this.ensureTerminated();
      this.ensureInitialized();
    }
  },
};

Object.defineProperty(this, "DownloadsIndicatorView", {
  value: DownloadsIndicatorView,
  enumerable: true,
  writable: false
});
PK
!<]\v��*�*/chrome/browser/content/browser/ext-bookmarks.js/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";

// The ext-* files are imported into the same scopes.
/* import-globals-from ext-browserAction.js */

ChromeUtils.import("resource://gre/modules/PlacesUtils.jsm");

const {
  TYPE_BOOKMARK,
  TYPE_FOLDER,
  TYPE_SEPARATOR,
} = PlacesUtils.bookmarks;

const BOOKMARKS_TYPES_TO_API_TYPES_MAP = new Map([
  [TYPE_BOOKMARK, "bookmark"],
  [TYPE_FOLDER, "folder"],
  [TYPE_SEPARATOR, "separator"],
]);

const BOOKMARK_SEPERATOR_URL = "data:";

XPCOMUtils.defineLazyGetter(this, "API_TYPES_TO_BOOKMARKS_TYPES_MAP", () => {
  let theMap = new Map();

  for (let [code, name] of BOOKMARKS_TYPES_TO_API_TYPES_MAP) {
    theMap.set(name, code);
  }
  return theMap;
});

let listenerCount = 0;

function getUrl(type, url) {
  switch (type) {
    case TYPE_BOOKMARK:
      return url;
    case TYPE_SEPARATOR:
      return BOOKMARK_SEPERATOR_URL;
    default:
      return undefined;
  }
}

const getTree = (rootGuid, onlyChildren) => {
  function convert(node, parent) {
    let treenode = {
      id: node.guid,
      title: PlacesUtils.bookmarks.getLocalizedTitle(node) || "",
      index: node.index,
      dateAdded: node.dateAdded / 1000,
      type: BOOKMARKS_TYPES_TO_API_TYPES_MAP.get(node.typeCode),
      url: getUrl(node.typeCode, node.uri),
    };

    if (parent && node.guid != PlacesUtils.bookmarks.rootGuid) {
      treenode.parentId = parent.guid;
    }

    if (node.typeCode == TYPE_FOLDER) {
      treenode.dateGroupModified = node.lastModified / 1000;

      if (!onlyChildren) {
        treenode.children = node.children
          ? node.children.map(child => convert(child, node))
          : [];
      }
    }

    return treenode;
  }

  return PlacesUtils.promiseBookmarksTree(rootGuid, {
    excludeItemsCallback: item => {
      return item.annos &&
             item.annos.find(a => a.name == PlacesUtils.EXCLUDE_FROM_BACKUP_ANNO);
    },
  }).then(root => {
    if (onlyChildren) {
      let children = root.children || [];
      return children.map(child => convert(child, root));
    }
    let treenode = convert(root, null);
    treenode.parentId = root.parentGuid;
    // It seems like the array always just contains the root node.
    return [treenode];
  }).catch(e => Promise.reject({message: e.message}));
};

const convertBookmarks = result => {
  let node = {
    id: result.guid,
    title: PlacesUtils.bookmarks.getLocalizedTitle(result) || "",
    index: result.index,
    dateAdded: result.dateAdded.getTime(),
    type: BOOKMARKS_TYPES_TO_API_TYPES_MAP.get(result.type),
    url: getUrl(result.type, result.url && result.url.href),
  };

  if (result.guid != PlacesUtils.bookmarks.rootGuid) {
    node.parentId = result.parentGuid;
  }

  if (result.type == TYPE_FOLDER) {
    node.dateGroupModified = result.lastModified.getTime();
  }

  return node;
};

let observer = new class extends EventEmitter {
  constructor() {
    super();

    this.skipTags = true;
    this.skipDescendantsOnItemRemoval = true;
  }

  onBeginUpdateBatch() {}
  onEndUpdateBatch() {}

  onItemAdded(id, parentId, index, itemType, uri, title, dateAdded, guid, parentGuid, source) {
    let bookmark = {
      id: guid,
      parentId: parentGuid,
      index,
      title,
      dateAdded: dateAdded / 1000,
      type: BOOKMARKS_TYPES_TO_API_TYPES_MAP.get(itemType),
      url: getUrl(itemType, uri && uri.spec),
    };

    if (itemType == TYPE_FOLDER) {
      bookmark.dateGroupModified = bookmark.dateAdded;
    }

    this.emit("created", bookmark);
  }

  onItemVisited() {}

  onItemMoved(id, oldParentId, oldIndex, newParentId, newIndex, itemType, guid, oldParentGuid, newParentGuid, source) {
    let info = {
      parentId: newParentGuid,
      index: newIndex,
      oldParentId: oldParentGuid,
      oldIndex,
    };
    this.emit("moved", {guid, info});
  }

  onItemRemoved(id, parentId, index, itemType, uri, guid, parentGuid, source) {
    let node = {
      id: guid,
      parentId: parentGuid,
      index,
      type: BOOKMARKS_TYPES_TO_API_TYPES_MAP.get(itemType),
      url: getUrl(itemType, uri && uri.spec),
    };

    this.emit("removed", {guid, info: {parentId: parentGuid, index, node}});
  }

  onItemChanged(id, prop, isAnno, val, lastMod, itemType, parentId, guid, parentGuid, oldVal, source) {
    let info = {};
    if (prop == "title") {
      info.title = val;
    } else if (prop == "uri") {
      info.url = val;
    } else {
      // Not defined yet.
      return;
    }

    this.emit("changed", {guid, info});
  }
}();

const decrementListeners = () => {
  listenerCount -= 1;
  if (!listenerCount) {
    PlacesUtils.bookmarks.removeObserver(observer);
  }
};

const incrementListeners = () => {
  listenerCount++;
  if (listenerCount == 1) {
    PlacesUtils.bookmarks.addObserver(observer);
  }
};

this.bookmarks = class extends ExtensionAPI {
  getAPI(context) {
    return {
      bookmarks: {
        async get(idOrIdList) {
          let list = Array.isArray(idOrIdList) ? idOrIdList : [idOrIdList];

          try {
            let bookmarks = [];
            for (let id of list) {
              let bookmark = await PlacesUtils.bookmarks.fetch({guid: id});
              if (!bookmark) {
                throw new Error("Bookmark not found");
              }
              bookmarks.push(convertBookmarks(bookmark));
            }
            return bookmarks;
          } catch (error) {
            return Promise.reject({message: error.message});
          }
        },

        getChildren: function(id) {
          // TODO: We should optimize this.
          return getTree(id, true);
        },

        getTree: function() {
          return getTree(PlacesUtils.bookmarks.rootGuid, false);
        },

        getSubTree: function(id) {
          return getTree(id, false);
        },

        search: function(query) {
          return PlacesUtils.bookmarks.search(query).then(result => result.map(convertBookmarks));
        },

        getRecent: function(numberOfItems) {
          return PlacesUtils.bookmarks.getRecent(numberOfItems).then(result => result.map(convertBookmarks));
        },

        create: function(bookmark) {
          let info = {
            title: bookmark.title || "",
          };

          info.type = API_TYPES_TO_BOOKMARKS_TYPES_MAP.get(bookmark.type);
          if (!info.type) {
            // If url is NULL or missing, it will be a folder.
            if (bookmark.url !== null) {
              info.type = TYPE_BOOKMARK;
            } else {
              info.type = TYPE_FOLDER;
            }
          }

          if (info.type === TYPE_BOOKMARK) {
            info.url = bookmark.url || "";
          }

          if (bookmark.index !== null) {
            info.index = bookmark.index;
          }

          if (bookmark.parentId !== null) {
            info.parentGuid = bookmark.parentId;
          } else {
            info.parentGuid = PlacesUtils.bookmarks.unfiledGuid;
          }

          try {
            return PlacesUtils.bookmarks.insert(info).then(convertBookmarks)
              .catch(error => Promise.reject({message: error.message}));
          } catch (e) {
            return Promise.reject({message: `Invalid bookmark: ${JSON.stringify(info)}`});
          }
        },

        move: function(id, destination) {
          let info = {
            guid: id,
          };

          if (destination.parentId !== null) {
            info.parentGuid = destination.parentId;
          }
          info.index = (destination.index === null) ?
            PlacesUtils.bookmarks.DEFAULT_INDEX : destination.index;

          try {
            return PlacesUtils.bookmarks.update(info).then(convertBookmarks)
              .catch(error => Promise.reject({message: error.message}));
          } catch (e) {
            return Promise.reject({message: `Invalid bookmark: ${JSON.stringify(info)}`});
          }
        },

        update: function(id, changes) {
          let info = {
            guid: id,
          };

          if (changes.title !== null) {
            info.title = changes.title;
          }
          if (changes.url !== null) {
            info.url = changes.url;
          }

          try {
            return PlacesUtils.bookmarks.update(info).then(convertBookmarks)
              .catch(error => Promise.reject({message: error.message}));
          } catch (e) {
            return Promise.reject({message: `Invalid bookmark: ${JSON.stringify(info)}`});
          }
        },

        remove: function(id) {
          let info = {
            guid: id,
          };

          // The API doesn't give you the old bookmark at the moment
          try {
            return PlacesUtils.bookmarks.remove(info, {preventRemovalOfNonEmptyFolders: true})
              .catch(error => Promise.reject({message: error.message}));
          } catch (e) {
            return Promise.reject({message: `Invalid bookmark: ${JSON.stringify(info)}`});
          }
        },

        removeTree: function(id) {
          let info = {
            guid: id,
          };

          try {
            return PlacesUtils.bookmarks.remove(info)
              .catch(error => Promise.reject({message: error.message}));
          } catch (e) {
            return Promise.reject({message: `Invalid bookmark: ${JSON.stringify(info)}`});
          }
        },

        onCreated: new EventManager(context, "bookmarks.onCreated", fire => {
          let listener = (event, bookmark) => {
            fire.sync(bookmark.id, bookmark);
          };

          observer.on("created", listener);
          incrementListeners();
          return () => {
            observer.off("created", listener);
            decrementListeners();
          };
        }).api(),

        onRemoved: new EventManager(context, "bookmarks.onRemoved", fire => {
          let listener = (event, data) => {
            fire.sync(data.guid, data.info);
          };

          observer.on("removed", listener);
          incrementListeners();
          return () => {
            observer.off("removed", listener);
            decrementListeners();
          };
        }).api(),

        onChanged: new EventManager(context, "bookmarks.onChanged", fire => {
          let listener = (event, data) => {
            fire.sync(data.guid, data.info);
          };

          observer.on("changed", listener);
          incrementListeners();
          return () => {
            observer.off("changed", listener);
            decrementListeners();
          };
        }).api(),

        onMoved: new EventManager(context, "bookmarks.onMoved", fire => {
          let listener = (event, data) => {
            fire.sync(data.guid, data.info);
          };

          observer.on("moved", listener);
          incrementListeners();
          return () => {
            observer.off("moved", listener);
            decrementListeners();
          };
        }).api(),
      },
    };
  }
};
PK
!<�}�BlBl-chrome/browser/content/browser/ext-browser.js/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";

// This file provides some useful code for the |tabs| and |windows|
// modules. All of the code is installed on |global|, which is a scope
// shared among the different ext-*.js scripts.

/* global EventEmitter:false, TabContext:false, WindowEventManager:false,
          makeWidgetId:false, tabTracker:true, windowTracker:true */
/* import-globals-from ../../../toolkit/components/extensions/ext-toolkit.js */

/* globals TabBase, WindowBase, TabTrackerBase, WindowTrackerBase, TabManagerBase, WindowManagerBase */

ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
                               "resource://gre/modules/PrivateBrowsingUtils.jsm");
ChromeUtils.defineModuleGetter(this, "RecentWindow",
                               "resource:///modules/RecentWindow.jsm");

var {
  ExtensionError,
  defineLazyGetter,
} = ExtensionUtils;

const READER_MODE_PREFIX = "about:reader";

let tabTracker;
let windowTracker;

// This function is pretty tightly tied to Extension.jsm.
// Its job is to fill in the |tab| property of the sender.
const getSender = (extension, target, sender) => {
  let tabId;
  if ("tabId" in sender) {
    // The message came from a privileged extension page running in a tab. In
    // that case, it should include a tabId property (which is filled in by the
    // page-open listener below).
    tabId = sender.tabId;
    delete sender.tabId;
  } else if (target instanceof Ci.nsIDOMXULElement ||
             ExtensionUtils.instanceOf(target, "HTMLIFrameElement")) {
    tabId = tabTracker.getBrowserData(target).tabId;
  }

  if (tabId) {
    let tab = extension.tabManager.get(tabId, null);
    if (tab) {
      sender.tab = tab.convert();
    }
  }
};

// Used by Extension.jsm
global.tabGetSender = getSender;

/* eslint-disable mozilla/balanced-listeners */
extensions.on("uninstalling", (msg, extension) => {
  if (extension.uninstallURL) {
    let browser = windowTracker.topWindow.gBrowser;
    browser.addTab(extension.uninstallURL, {relatedToCurrent: true});
  }
});

extensions.on("page-shutdown", (type, context) => {
  if (context.viewType == "tab") {
    if (context.extension.id !== context.xulBrowser.contentPrincipal.addonId) {
      // Only close extension tabs.
      // This check prevents about:addons from closing when it contains a
      // WebExtension as an embedded inline options page.
      return;
    }
    let {gBrowser} = context.xulBrowser.ownerGlobal;
    if (gBrowser) {
      let nativeTab = gBrowser.getTabForBrowser(context.xulBrowser);
      if (nativeTab) {
        gBrowser.removeTab(nativeTab);
      }
    }
  }
});
/* eslint-enable mozilla/balanced-listeners */

global.openOptionsPage = (extension) => {
  let window = windowTracker.topWindow;
  if (!window) {
    return Promise.reject({message: "No browser window available"});
  }

  if (extension.manifest.options_ui.open_in_tab) {
    window.switchToTabHavingURI(extension.manifest.options_ui.page, true, {
      triggeringPrincipal: extension.principal,
    });
    return Promise.resolve();
  }

  let viewId = `addons://detail/${encodeURIComponent(extension.id)}/preferences`;

  return window.BrowserOpenAddonsMgr(viewId);
};

global.makeWidgetId = id => {
  id = id.toLowerCase();
  // FIXME: This allows for collisions.
  return id.replace(/[^a-z0-9_-]/g, "_");
};

// Manages tab-specific context data, and dispatching tab select events
// across all windows.
global.TabContext = class extends EventEmitter {
  constructor(getDefaults, extension) {
    super();

    this.extension = extension;
    this.getDefaults = getDefaults;

    this.tabData = new WeakMap();

    windowTracker.addListener("progress", this);
    windowTracker.addListener("TabSelect", this);
  }

  get(nativeTab) {
    if (!this.tabData.has(nativeTab)) {
      this.tabData.set(nativeTab, this.getDefaults(nativeTab));
    }

    return this.tabData.get(nativeTab);
  }

  clear(nativeTab) {
    this.tabData.delete(nativeTab);
  }

  handleEvent(event) {
    if (event.type == "TabSelect") {
      let nativeTab = event.target;
      this.emit("tab-select", nativeTab);
      this.emit("location-change", nativeTab);
    }
  }

  onLocationChange(browser, webProgress, request, locationURI, flags) {
    let gBrowser = browser.ownerGlobal.gBrowser;
    if (browser === gBrowser.selectedBrowser) {
      let tab = gBrowser.getTabForBrowser(browser);
      // fromBrowse will be false in case of e.g. a hash change or history.pushState
      let fromBrowse = !(flags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT);
      this.emit("location-change", tab, fromBrowse);
    }
  }

  shutdown() {
    windowTracker.removeListener("progress", this);
    windowTracker.removeListener("TabSelect", this);
  }
};


class WindowTracker extends WindowTrackerBase {
  addProgressListener(window, listener) {
    window.gBrowser.addTabsProgressListener(listener);
  }

  removeProgressListener(window, listener) {
    window.gBrowser.removeTabsProgressListener(listener);
  }

  /**
   * @property {DOMWindow|null} topNormalWindow
   *        The currently active, or topmost, browser window, or null if no
   *        browser window is currently open.
   *        Will return the topmost "normal" (i.e., not popup) window.
   *        @readonly
   */
  get topNormalWindow() {
    return RecentWindow.getMostRecentBrowserWindow({allowPopups: false});
  }
}

/**
 * An event manager API provider which listens for a DOM event in any browser
 * window, and calls the given listener function whenever an event is received.
 * That listener function receives a `fire` object, which it can use to dispatch
 * events to the extension, and a DOM event object.
 *
 * @param {BaseContext} context
 *        The extension context which the event manager belongs to.
 * @param {string} name
 *        The API name of the event manager, e.g.,"runtime.onMessage".
 * @param {string} event
 *        The name of the DOM event to listen for.
 * @param {function} listener
 *        The listener function to call when a DOM event is received.
 */
global.WindowEventManager = class extends EventManager {
  constructor(context, name, event, listener) {
    super(context, name, fire => {
      let listener2 = listener.bind(null, fire);

      windowTracker.addListener(event, listener2);
      return () => {
        windowTracker.removeListener(event, listener2);
      };
    });
  }
};

class TabTracker extends TabTrackerBase {
  constructor() {
    super();

    this._tabs = new WeakMap();
    this._browsers = new WeakMap();
    this._tabIds = new Map();
    this._nextId = 1;

    this._handleTabDestroyed = this._handleTabDestroyed.bind(this);
  }

  init() {
    if (this.initialized) {
      return;
    }
    this.initialized = true;

    this.adoptedTabs = new WeakMap();

    this._handleWindowOpen = this._handleWindowOpen.bind(this);
    this._handleWindowClose = this._handleWindowClose.bind(this);

    windowTracker.addListener("TabClose", this);
    windowTracker.addListener("TabOpen", this);
    windowTracker.addListener("TabSelect", this);
    windowTracker.addOpenListener(this._handleWindowOpen);
    windowTracker.addCloseListener(this._handleWindowClose);

    Services.mm.addMessageListener("Reader:UpdateReaderButton", this);

    /* eslint-disable mozilla/balanced-listeners */
    this.on("tab-detached", this._handleTabDestroyed);
    this.on("tab-removed", this._handleTabDestroyed);
    /* eslint-enable mozilla/balanced-listeners */
  }

  getId(nativeTab) {
    let id = this._tabs.get(nativeTab);
    if (id) {
      return id;
    }

    this.init();

    id = this._nextId++;
    this.setId(nativeTab, id);
    return id;
  }

  getBrowserTabId(browser) {
    let id = this._browsers.get(browser);
    if (id) {
      return id;
    }

    let tab = browser.ownerGlobal.gBrowser.getTabForBrowser(browser);
    if (tab) {
      id = this.getId(tab);
      this._browsers.set(browser, id);
      return id;
    }
    return -1;
  }

  setId(nativeTab, id) {
    this._tabs.set(nativeTab, id);
    if (nativeTab.linkedBrowser) {
      this._browsers.set(nativeTab.linkedBrowser, id);
    }
    this._tabIds.set(id, nativeTab);
  }

  _handleTabDestroyed(event, {nativeTab}) {
    let id = this._tabs.get(nativeTab);
    if (id) {
      this._tabs.delete(nativeTab);
      if (this._tabIds.get(id) === nativeTab) {
        this._tabIds.delete(id);
      }
    }
  }

  /**
   * Returns the XUL <tab> element associated with the given tab ID. If no tab
   * with the given ID exists, and no default value is provided, an error is
   * raised, belonging to the scope of the given context.
   *
   * @param {integer} tabId
   *        The ID of the tab to retrieve.
   * @param {*} default_
   *        The value to return if no tab exists with the given ID.
   * @returns {Element<tab>}
   *        A XUL <tab> element.
   */
  getTab(tabId, default_ = undefined) {
    let nativeTab = this._tabIds.get(tabId);
    if (nativeTab) {
      return nativeTab;
    }
    if (default_ !== undefined) {
      return default_;
    }
    throw new ExtensionError(`Invalid tab ID: ${tabId}`);
  }

  /**
   * Sets the opener of `tab` to the ID `openerTab`. Both tabs must be in the
   * same window, or this function will throw a type error.
   *
   * @param {Element} tab The tab for which to set the owner.
   * @param {Element} openerTab The opener of <tab>.
   */
  setOpener(tab, openerTab) {
    if (tab.ownerDocument !== openerTab.ownerDocument) {
      throw new Error("Tab must be in the same window as its opener");
    }
    tab.openerTab = openerTab;
  }

  /**
   * @param {Event} event
   *        The DOM Event to handle.
   * @private
   */
  handleEvent(event) {
    let nativeTab = event.target;

    switch (event.type) {
      case "TabOpen":
        let {adoptedTab} = event.detail;
        if (adoptedTab) {
          this.adoptedTabs.set(adoptedTab, event.target);

          // This tab is being created to adopt a tab from a different window.
          // Copy the ID from the old tab to the new.
          this.setId(nativeTab, this.getId(adoptedTab));

          adoptedTab.linkedBrowser.messageManager.sendAsyncMessage("Extension:SetFrameData", {
            windowId: windowTracker.getId(nativeTab.ownerGlobal),
          });
        }

        // Save the current tab, since the newly-created tab will likely be
        // active by the time the promise below resolves and the event is
        // dispatched.
        let currentTab = nativeTab.ownerGlobal.gBrowser.selectedTab;

        // We need to delay sending this event until the next tick, since the
        // tab does not have its final index when the TabOpen event is dispatched.
        Promise.resolve().then(() => {
          if (event.detail.adoptedTab) {
            this.emitAttached(event.originalTarget);
          } else {
            this.emitCreated(event.originalTarget, currentTab);
          }
        });
        break;

      case "TabClose":
        let {adoptedBy} = event.detail;
        if (adoptedBy) {
          // This tab is being closed because it was adopted by a new window.
          // Copy its ID to the new tab, in case it was created as the first tab
          // of a new window, and did not have an `adoptedTab` detail when it was
          // opened.
          this.setId(adoptedBy, this.getId(nativeTab));

          this.emitDetached(nativeTab, adoptedBy);
        } else {
          this.emitRemoved(nativeTab, false);
        }
        break;

      case "TabSelect":
        // Because we are delaying calling emitCreated above, we also need to
        // delay sending this event because it shouldn't fire before onCreated.
        Promise.resolve().then(() => {
          this.emitActivated(nativeTab);
        });
        break;
    }
  }

  /**
   * @param {Object} message
   *        The message to handle.
   * @private
   */
  receiveMessage(message) {
    switch (message.name) {
      case "Reader:UpdateReaderButton":
        if (message.data && message.data.isArticle !== undefined) {
          this.emit("tab-isarticle", message);
        }
        break;
    }
  }

  /**
   * A private method which is called whenever a new browser window is opened,
   * and dispatches the necessary events for it.
   *
   * @param {DOMWindow} window
   *        The window being opened.
   * @private
   */
  _handleWindowOpen(window) {
    if (window.arguments && window.arguments[0] instanceof window.XULElement) {
      // If the first window argument is a XUL element, it means the
      // window is about to adopt a tab from another window to replace its
      // initial tab.
      //
      // Note that this event handler depends on running before the
      // delayed startup code in browser.js, which is currently triggered
      // by the first MozAfterPaint event. That code handles finally
      // adopting the tab, and clears it from the arguments list in the
      // process, so if we run later than it, we're too late.
      let nativeTab = window.arguments[0];
      let adoptedBy = window.gBrowser.tabs[0];

      this.adoptedTabs.set(nativeTab, adoptedBy);
      this.setId(adoptedBy, this.getId(nativeTab));

      // We need to be sure to fire this event after the onDetached event
      // for the original tab.
      let listener = (event, details) => {
        if (details.nativeTab === nativeTab) {
          this.off("tab-detached", listener);

          Promise.resolve().then(() => {
            this.emitAttached(details.adoptedBy);
          });
        }
      };

      this.on("tab-detached", listener);
    } else {
      for (let nativeTab of window.gBrowser.tabs) {
        this.emitCreated(nativeTab);
      }

      // emitActivated to trigger tab.onActivated/tab.onHighlighted for a newly opened window.
      this.emitActivated(window.gBrowser.tabs[0]);
    }
  }

  /**
   * A private method which is called whenever a browser window is closed,
   * and dispatches the necessary events for it.
   *
   * @param {DOMWindow} window
   *        The window being closed.
   * @private
   */
  _handleWindowClose(window) {
    for (let nativeTab of window.gBrowser.tabs) {
      if (this.adoptedTabs.has(nativeTab)) {
        this.emitDetached(nativeTab, this.adoptedTabs.get(nativeTab));
      } else {
        this.emitRemoved(nativeTab, true);
      }
    }
  }

  /**
   * Emits a "tab-activated" event for the given tab element.
   *
   * @param {NativeTab} nativeTab
   *        The tab element which has been activated.
   * @private
   */
  emitActivated(nativeTab) {
    this.emit("tab-activated", {
      tabId: this.getId(nativeTab),
      windowId: windowTracker.getId(nativeTab.ownerGlobal)});
  }

  /**
   * Emits a "tab-attached" event for the given tab element.
   *
   * @param {NativeTab} nativeTab
   *        The tab element in the window to which the tab is being attached.
   * @private
   */
  emitAttached(nativeTab) {
    let newWindowId = windowTracker.getId(nativeTab.ownerGlobal);
    let tabId = this.getId(nativeTab);

    this.emit("tab-attached", {nativeTab, tabId, newWindowId, newPosition: nativeTab._tPos});
  }

  /**
   * Emits a "tab-detached" event for the given tab element.
   *
   * @param {NativeTab} nativeTab
   *        The tab element in the window from which the tab is being detached.
   * @param {NativeTab} adoptedBy
   *        The tab element in the window to which detached tab is being moved,
   *        and will adopt this tab's contents.
   * @private
   */
  emitDetached(nativeTab, adoptedBy) {
    let oldWindowId = windowTracker.getId(nativeTab.ownerGlobal);
    let tabId = this.getId(nativeTab);

    this.emit("tab-detached", {nativeTab, adoptedBy, tabId, oldWindowId, oldPosition: nativeTab._tPos});
  }

  /**
   * Emits a "tab-created" event for the given tab element.
   *
   * @param {NativeTab} nativeTab
   *        The tab element which is being created.
   * @param {NativeTab} [currentTab]
   *        The tab element for the currently active tab.
   * @private
   */
  emitCreated(nativeTab, currentTab) {
    this.emit("tab-created", {nativeTab, currentTab});
  }

  /**
   * Emits a "tab-removed" event for the given tab element.
   *
   * @param {NativeTab} nativeTab
   *        The tab element which is being removed.
   * @param {boolean} isWindowClosing
   *        True if the tab is being removed because the browser window is
   *        closing.
   * @private
   */
  emitRemoved(nativeTab, isWindowClosing) {
    let windowId = windowTracker.getId(nativeTab.ownerGlobal);
    let tabId = this.getId(nativeTab);

    this.emit("tab-removed", {nativeTab, tabId, windowId, isWindowClosing});
  }

  getBrowserData(browser) {
    let {gBrowser} = browser.ownerGlobal;
    // Some non-browser windows have gBrowser but not getTabForBrowser!
    if (!gBrowser || !gBrowser.getTabForBrowser) {
      if (browser.ownerDocument.documentURI === "about:addons") {
        // When we're loaded into a <browser> inside about:addons, we need to go up
        // one more level.
        browser = browser.ownerDocument.docShell.chromeEventHandler;

        ({gBrowser} = browser.ownerGlobal);
      } else {
        return {
          tabId: -1,
          windowId: -1,
        };
      }
    }

    return {
      tabId: this.getBrowserTabId(browser),
      windowId: windowTracker.getId(browser.ownerGlobal),
    };
  }

  get activeTab() {
    let window = windowTracker.topWindow;
    if (window && window.gBrowser) {
      return window.gBrowser.selectedTab;
    }
    return null;
  }
}

windowTracker = new WindowTracker();
tabTracker = new TabTracker();

Object.assign(global, {tabTracker, windowTracker});

class Tab extends TabBase {
  get _favIconUrl() {
    return this.window.gBrowser.getIcon(this.nativeTab);
  }

  get audible() {
    return this.nativeTab.soundPlaying;
  }

  get browser() {
    return this.nativeTab.linkedBrowser;
  }

  get discarded() {
    return !this.nativeTab.linkedPanel;
  }

  get frameLoader() {
    // If we don't have a frameLoader yet, just return a dummy with no width and
    // height.
    return super.frameLoader || {lazyWidth: 0, lazyHeight: 0};
  }

  get hidden() {
    return this.nativeTab.hidden;
  }

  get sharingState() {
    return this.window.gBrowser.getTabSharingState(this.nativeTab);
  }

  get cookieStoreId() {
    return getCookieStoreIdForTab(this, this.nativeTab);
  }

  get openerTabId() {
    let opener = this.nativeTab.openerTab;
    if (opener && opener.parentNode && opener.ownerDocument == this.nativeTab.ownerDocument) {
      return tabTracker.getId(opener);
    }
    return null;
  }

  get height() {
    return this.frameLoader.lazyHeight;
  }

  get index() {
    return this.nativeTab._tPos;
  }

  get mutedInfo() {
    let {nativeTab} = this;

    let mutedInfo = {muted: nativeTab.muted};
    if (nativeTab.muteReason === null) {
      mutedInfo.reason = "user";
    } else if (nativeTab.muteReason) {
      mutedInfo.reason = "extension";
      mutedInfo.extensionId = nativeTab.muteReason;
    }

    return mutedInfo;
  }

  get lastAccessed() {
    return this.nativeTab.lastAccessed;
  }

  get pinned() {
    return this.nativeTab.pinned;
  }

  get active() {
    return this.nativeTab.selected;
  }

  get highlighted() {
    return this.nativeTab.selected;
  }

  get selected() {
    return this.nativeTab.selected;
  }

  get status() {
    if (this.nativeTab.getAttribute("busy") === "true") {
      return "loading";
    }
    return "complete";
  }

  get width() {
    return this.frameLoader.lazyWidth;
  }

  get window() {
    return this.nativeTab.ownerGlobal;
  }

  get windowId() {
    return windowTracker.getId(this.window);
  }

  get isArticle() {
    return this.nativeTab.linkedBrowser.isArticle;
  }

  get isInReaderMode() {
    return this.url && this.url.startsWith(READER_MODE_PREFIX);
  }

  /**
   * Converts session store data to an object compatible with the return value
   * of the convert() method, representing that data.
   *
   * @param {Extension} extension
   *        The extension for which to convert the data.
   * @param {Object} tabData
   *        Session store data for a closed tab, as returned by
   *        `SessionStore.getClosedTabData()`.
   * @param {DOMWindow} [window = null]
   *        The browser window which the tab belonged to before it was closed.
   *        May be null if the window the tab belonged to no longer exists.
   *
   * @returns {Object}
   * @static
   */
  static convertFromSessionStoreClosedData(extension, tabData, window = null) {
    let result = {
      sessionId: String(tabData.closedId),
      index: tabData.pos ? tabData.pos : 0,
      windowId: window && windowTracker.getId(window),
      highlighted: false,
      active: false,
      pinned: false,
      hidden: tabData.state ? tabData.state.hidden : tabData.hidden,
      incognito: Boolean(tabData.state && tabData.state.isPrivate),
      lastAccessed: tabData.state ? tabData.state.lastAccessed : tabData.lastAccessed,
    };

    if (extension.tabManager.hasTabPermission(tabData)) {
      let entries = tabData.state ? tabData.state.entries : tabData.entries;
      let lastTabIndex = tabData.state ? tabData.state.index : tabData.index;
      // We need to take lastTabIndex - 1 because the index in the tab data is
      // 1-based rather than 0-based.
      let entry = entries[lastTabIndex - 1];
      result.url = entry.url;
      result.title = entry.title;
      if (tabData.image) {
        result.favIconUrl = tabData.image;
      }
    }

    return result;
  }
}

class Window extends WindowBase {
  /**
   * Update the geometry of the browser window.
   *
   * @param {Object} options
   *        An object containing new values for the window's geometry.
   * @param {integer} [options.left]
   *        The new pixel distance of the left side of the browser window from
   *        the left of the screen.
   * @param {integer} [options.top]
   *        The new pixel distance of the top side of the browser window from
   *        the top of the screen.
   * @param {integer} [options.width]
   *        The new pixel width of the window.
   * @param {integer} [options.height]
   *        The new pixel height of the window.
   */
  updateGeometry(options) {
    let {window} = this;

    if (options.left !== null || options.top !== null) {
      let left = options.left !== null ? options.left : window.screenX;
      let top = options.top !== null ? options.top : window.screenY;
      window.moveTo(left, top);
    }

    if (options.width !== null || options.height !== null) {
      let width = options.width !== null ? options.width : window.outerWidth;
      let height = options.height !== null ? options.height : window.outerHeight;
      window.resizeTo(width, height);
    }
  }

  get _title() {
    return this.window.document.title;
  }

  setTitlePreface(titlePreface) {
    this.window.document.documentElement.setAttribute("titlepreface", titlePreface);
  }

  get focused() {
    return this.window.document.hasFocus();
  }

  get top() {
    return this.window.screenY;
  }

  get left() {
    return this.window.screenX;
  }

  get width() {
    return this.window.outerWidth;
  }

  get height() {
    return this.window.outerHeight;
  }

  get incognito() {
    return PrivateBrowsingUtils.isWindowPrivate(this.window);
  }

  get alwaysOnTop() {
    return this.xulWindow.zLevel >= Ci.nsIXULWindow.raisedZ;
  }

  get isLastFocused() {
    return this.window === windowTracker.topWindow;
  }

  static getState(window) {
    const STATES = {
      [window.STATE_MAXIMIZED]: "maximized",
      [window.STATE_MINIMIZED]: "minimized",
      [window.STATE_NORMAL]: "normal",
    };
    let state = STATES[window.windowState];
    if (window.fullScreen) {
      state = "fullscreen";
    }
    return state;
  }

  get state() {
    return Window.getState(this.window);
  }

  set state(state) {
    let {window} = this;
    if (state !== "fullscreen" && window.fullScreen) {
      window.fullScreen = false;
    }

    switch (state) {
      case "maximized":
        window.maximize();
        break;

      case "minimized":
      case "docked":
        window.minimize();
        break;

      case "normal":
        // Restore sometimes returns the window to its previous state, rather
        // than to the "normal" state, so it may need to be called anywhere from
        // zero to two times.
        window.restore();
        if (window.windowState !== window.STATE_NORMAL) {
          window.restore();
        }
        if (window.windowState !== window.STATE_NORMAL) {
          // And on OS-X, where normal vs. maximized is basically a heuristic,
          // we need to cheat.
          window.sizeToContent();
        }
        break;

      case "fullscreen":
        window.fullScreen = true;
        break;

      default:
        throw new Error(`Unexpected window state: ${state}`);
    }
  }

  * getTabs() {
    let {tabManager} = this.extension;

    for (let nativeTab of this.window.gBrowser.tabs) {
      yield tabManager.getWrapper(nativeTab);
    }
  }

  get activeTab() {
    let {tabManager} = this.extension;

    return tabManager.getWrapper(this.window.gBrowser.selectedTab);
  }

  /**
   * Converts session store data to an object compatible with the return value
   * of the convert() method, representing that data.
   *
   * @param {Extension} extension
   *        The extension for which to convert the data.
   * @param {Object} windowData
   *        Session store data for a closed window, as returned by
   *        `SessionStore.getClosedWindowData()`.
   *
   * @returns {Object}
   * @static
   */
  static convertFromSessionStoreClosedData(extension, windowData) {
    let result = {
      sessionId: String(windowData.closedId),
      focused: false,
      incognito: false,
      type: "normal", // this is always "normal" for a closed window
      // Surely this does not actually work?
      state: this.getState(windowData),
      alwaysOnTop: false,
    };

    if (windowData.tabs.length) {
      result.tabs = windowData.tabs.map(tabData => {
        return Tab.convertFromSessionStoreClosedData(extension, tabData);
      });
    }

    return result;
  }
}

Object.assign(global, {Tab, Window});

class TabManager extends TabManagerBase {
  get(tabId, default_ = undefined) {
    let nativeTab = tabTracker.getTab(tabId, default_);

    if (nativeTab) {
      return this.getWrapper(nativeTab);
    }
    return default_;
  }

  addActiveTabPermission(nativeTab = tabTracker.activeTab) {
    return super.addActiveTabPermission(nativeTab);
  }

  revokeActiveTabPermission(nativeTab = tabTracker.activeTab) {
    return super.revokeActiveTabPermission(nativeTab);
  }

  wrapTab(nativeTab) {
    return new Tab(this.extension, nativeTab, tabTracker.getId(nativeTab));
  }
}

class WindowManager extends WindowManagerBase {
  get(windowId, context) {
    let window = windowTracker.getWindow(windowId, context);

    return this.getWrapper(window);
  }

  * getAll() {
    for (let window of windowTracker.browserWindows()) {
      yield this.getWrapper(window);
    }
  }

  wrapWindow(window) {
    return new Window(this.extension, window, windowTracker.getId(window));
  }
}


extensions.on("startup", (type, extension) => { // eslint-disable-line mozilla/balanced-listeners
  defineLazyGetter(extension, "tabManager",
                   () => new TabManager(extension));
  defineLazyGetter(extension, "windowManager",
                   () => new WindowManager(extension));
});
PK
!<\j�п�/chrome/browser/content/browser/ext-browser.json{
  "bookmarks": {
    "url": "chrome://browser/content/ext-bookmarks.js",
    "schema": "chrome://browser/content/schemas/bookmarks.json",
    "scopes": ["addon_parent"],
    "paths": [
      ["bookmarks"]
    ]
  },
  "browserAction": {
    "url": "chrome://browser/content/ext-browserAction.js",
    "schema": "chrome://browser/content/schemas/browser_action.json",
    "scopes": ["addon_parent"],
    "manifest": ["browser_action"],
    "paths": [
      ["browserAction"]
    ]
  },
  "browsingData": {
    "url": "chrome://browser/content/ext-browsingData.js",
    "schema": "chrome://browser/content/schemas/browsing_data.json",
    "scopes": ["addon_parent"],
    "paths": [
      ["browsingData"]
    ]
  },
  "chrome_settings_overrides": {
    "url": "chrome://browser/content/ext-chrome-settings-overrides.js",
    "scopes": [],
    "events": ["update", "uninstall"],
    "schema": "chrome://browser/content/schemas/chrome_settings_overrides.json",
    "manifest": ["chrome_settings_overrides"]
  },
  "commands": {
    "url": "chrome://browser/content/ext-commands.js",
    "schema": "chrome://browser/content/schemas/commands.json",
    "scopes": ["addon_parent"],
    "events": ["uninstall"],
    "manifest": ["commands"],
    "paths": [
      ["commands"]
    ]
  },
  "devtools": {
    "url": "chrome://browser/content/ext-devtools.js",
    "schema": "chrome://browser/content/schemas/devtools.json",
    "scopes": ["devtools_parent"],
    "manifest": ["devtools_page"],
    "paths": [
      ["devtools"]
    ]
  },
  "devtools_inspectedWindow": {
    "url": "chrome://browser/content/ext-devtools-inspectedWindow.js",
    "schema": "chrome://browser/content/schemas/devtools_inspected_window.json",
    "scopes": ["devtools_parent"],
    "paths": [
      ["devtools", "inspectedWindow"]
    ]
  },
  "devtools_network": {
    "url": "chrome://browser/content/ext-devtools-network.js",
    "schema": "chrome://browser/content/schemas/devtools_network.json",
    "scopes": ["devtools_parent"],
    "paths": [
      ["devtools", "network"]
    ]
  },
  "devtools_panels": {
    "url": "chrome://browser/content/ext-devtools-panels.js",
    "schema": "chrome://browser/content/schemas/devtools_panels.json",
    "scopes": ["devtools_parent"],
    "paths": [
      ["devtools", "panels"]
    ]
  },
  "find": {
    "url": "chrome://browser/content/ext-find.js",
    "schema": "chrome://browser/content/schemas/find.json",
    "scopes": ["addon_parent"],
    "paths": [
      ["find"]
    ]
  },
  "history": {
    "url": "chrome://browser/content/ext-history.js",
    "schema": "chrome://browser/content/schemas/history.json",
    "scopes": ["addon_parent"],
    "paths": [
      ["history"]
    ]
  },
  "identity": {
    "url": "chrome://extensions/content/ext-identity.js",
    "schema": "chrome://extensions/content/schemas/identity.json",
    "scopes": ["addon_parent"],
    "paths": [
      ["identity"]
    ]
  },
  "menusInternal": {
    "url": "chrome://browser/content/ext-menus.js",
    "schema": "chrome://browser/content/schemas/menus.json",
    "scopes": ["addon_parent"],
    "paths": [
      ["contextMenus"],
      ["menus"],
      ["menusInternal"]
    ]
  },
  "omnibox": {
    "url": "chrome://browser/content/ext-omnibox.js",
    "schema": "chrome://browser/content/schemas/omnibox.json",
    "scopes": ["addon_parent"],
    "manifest": ["omnibox"],
    "paths": [
      ["omnibox"]
    ]
  },
  "pageAction": {
    "url": "chrome://browser/content/ext-pageAction.js",
    "schema": "chrome://browser/content/schemas/page_action.json",
    "scopes": ["addon_parent"],
    "manifest": ["page_action"],
    "paths": [
      ["pageAction"]
    ]
  },
  "pkcs11": {
    "url": "chrome://browser/content/ext-pkcs11.js",
    "schema": "chrome://browser/content/schemas/pkcs11.json",
    "scopes": ["addon_parent"],
    "paths": [
      ["pkcs11"]
    ]
  },
  "geckoProfiler": {
    "url": "chrome://browser/content/ext-geckoProfiler.js",
    "schema": "chrome://browser/content/schemas/geckoProfiler.json",
    "scopes": ["addon_parent"],
    "paths": [
      ["geckoProfiler"]
    ]
  },
  "sessions": {
    "url": "chrome://browser/content/ext-sessions.js",
    "schema": "chrome://browser/content/schemas/sessions.json",
    "scopes": ["addon_parent"],
    "paths": [
      ["sessions"]
    ]
  },
  "sidebarAction": {
    "url": "chrome://browser/content/ext-sidebarAction.js",
    "schema": "chrome://browser/content/schemas/sidebar_action.json",
    "scopes": ["addon_parent"],
    "manifest": ["sidebar_action"],
    "paths": [
      ["sidebarAction"]
    ]
  },
  "tabs": {
    "url": "chrome://browser/content/ext-tabs.js",
    "schema": "chrome://browser/content/schemas/tabs.json",
    "scopes": ["addon_parent"],
    "events": ["update", "disable"],
    "paths": [
      ["tabs"]
    ]
  },
  "urlOverrides": {
    "url": "chrome://browser/content/ext-url-overrides.js",
    "schema": "chrome://browser/content/schemas/url_overrides.json",
    "scopes": ["addon_parent"],
    "manifest": ["chrome_url_overrides"],
    "paths": [
      ["urlOverrides"]
    ]
  },
  "windows": {
    "url": "chrome://browser/content/ext-windows.js",
    "schema": "chrome://browser/content/schemas/windows.json",
    "scopes": ["addon_parent"],
    "paths": [
      ["windows"]
    ]
  }
}
PK
!<�b��XX3chrome/browser/content/browser/ext-browserAction.js/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";

/* exported browserActionFor, sidebarActionFor, pageActionFor */
/* global browserActionFor:false, sidebarActionFor:false, pageActionFor:false */

// The ext-* files are imported into the same scopes.
/* import-globals-from ext-browser.js */

ChromeUtils.defineModuleGetter(this, "CustomizableUI",
                               "resource:///modules/CustomizableUI.jsm");
ChromeUtils.defineModuleGetter(this, "clearTimeout",
                               "resource://gre/modules/Timer.jsm");
ChromeUtils.defineModuleGetter(this, "setTimeout",
                               "resource://gre/modules/Timer.jsm");
ChromeUtils.defineModuleGetter(this, "TelemetryStopwatch",
                               "resource://gre/modules/TelemetryStopwatch.jsm");
ChromeUtils.defineModuleGetter(this, "ViewPopup",
                               "resource:///modules/ExtensionPopups.jsm");

var {
  DefaultWeakMap,
} = ExtensionUtils;

ChromeUtils.import("resource://gre/modules/ExtensionParent.jsm");

var {
  IconDetails,
  StartupCache,
} = ExtensionParent;

var {
  ExtensionError,
} = ExtensionUtils;

Cu.importGlobalProperties(["InspectorUtils"]);

const POPUP_PRELOAD_TIMEOUT_MS = 200;
const POPUP_OPEN_MS_HISTOGRAM = "WEBEXT_BROWSERACTION_POPUP_OPEN_MS";
const POPUP_RESULT_HISTOGRAM = "WEBEXT_BROWSERACTION_POPUP_PRELOAD_RESULT_COUNT";

var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";

const isAncestorOrSelf = (target, node) => {
  for (; node; node = node.parentNode) {
    if (node === target) {
      return true;
    }
  }
  return false;
};

// WeakMap[Extension -> BrowserAction]
const browserActionMap = new WeakMap();

XPCOMUtils.defineLazyGetter(this, "browserAreas", () => {
  return {
    "navbar": CustomizableUI.AREA_NAVBAR,
    "menupanel": CustomizableUI.AREA_FIXED_OVERFLOW_PANEL,
    "tabstrip": CustomizableUI.AREA_TABSTRIP,
    "personaltoolbar": CustomizableUI.AREA_BOOKMARKS,
  };
});

this.browserAction = class extends ExtensionAPI {
  static for(extension) {
    return browserActionMap.get(extension);
  }

  async onManifestEntry(entryName) {
    let {extension} = this;

    let options = extension.manifest.browser_action;

    this.iconData = new DefaultWeakMap(icons => this.getIconData(icons));

    let widgetId = makeWidgetId(extension.id);
    this.id = `${widgetId}-browser-action`;
    this.viewId = `PanelUI-webext-${widgetId}-browser-action-view`;
    this.widget = null;

    this.pendingPopup = null;
    this.pendingPopupTimeout = null;
    this.eventQueue = [];

    this.tabManager = extension.tabManager;

    this.defaults = {
      enabled: true,
      title: options.default_title || extension.name,
      badgeText: "",
      badgeBackgroundColor: null,
      popup: options.default_popup || "",
      area: browserAreas[options.default_area || "navbar"],
    };
    this.globals = Object.create(this.defaults);

    this.browserStyle = options.browser_style || false;
    if (options.browser_style === null) {
      this.extension.logger.warn("Please specify whether you want browser_style " +
                                 "or not in your browser_action options.");
    }

    browserActionMap.set(extension, this);

    this.defaults.icon = await StartupCache.get(
      extension, ["browserAction", "default_icon"],
      () => IconDetails.normalize({
        path: options.default_icon,
        iconType: "browserAction",
        themeIcons: options.theme_icons,
      }, extension));

    this.iconData.set(
      this.defaults.icon,
      await StartupCache.get(
        extension, ["browserAction", "default_icon_data"],
        () => this.getIconData(this.defaults.icon)));

    this.tabContext = new TabContext(tab => Object.create(this.globals),
                                     extension);

    // eslint-disable-next-line mozilla/balanced-listeners
    this.tabContext.on("location-change", this.handleLocationChange.bind(this));

    this.build();
  }

  handleLocationChange(eventType, tab, fromBrowse) {
    if (fromBrowse) {
      this.tabContext.clear(tab);
      this.updateOnChange(tab);
    }
  }

  onShutdown(reason) {
    browserActionMap.delete(this.extension);

    this.tabContext.shutdown();
    CustomizableUI.destroyWidget(this.id);

    this.clearPopup();
  }

  build() {
    let widget = CustomizableUI.createWidget({
      id: this.id,
      viewId: this.viewId,
      type: "view",
      removable: true,
      label: this.defaults.title || this.extension.name,
      tooltiptext: this.defaults.title || "",
      defaultArea: this.defaults.area,

      // Don't attempt to load properties from the built-in widget string
      // bundle.
      localized: false,

      onBeforeCreated: document => {
        let view = document.createElementNS(XUL_NS, "panelview");
        view.id = this.viewId;
        view.setAttribute("flex", "1");
        view.setAttribute("extension", true);

        document.getElementById("appMenu-viewCache").appendChild(view);

        if (this.extension.hasPermission("menus") ||
            this.extension.hasPermission("contextMenus")) {
          document.addEventListener("popupshowing", this);
        }
      },

      onDestroyed: document => {
        document.removeEventListener("popupshowing", this);

        let view = document.getElementById(this.viewId);
        if (view) {
          this.clearPopup();
          CustomizableUI.hidePanelForNode(view);
          view.remove();
        }
      },

      onCreated: node => {
        node.classList.add("badged-button");
        node.classList.add("webextension-browser-action");
        node.setAttribute("constrain-size", "true");

        node.onmousedown = event => this.handleEvent(event);
        node.onmouseover = event => this.handleEvent(event);
        node.onmouseout = event => this.handleEvent(event);

        this.updateButton(node, this.globals, true);
      },

      onViewShowing: async event => {
        TelemetryStopwatch.start(POPUP_OPEN_MS_HISTOGRAM, this);
        let document = event.target.ownerDocument;
        let tabbrowser = document.defaultView.gBrowser;

        let tab = tabbrowser.selectedTab;
        let popupURL = this.getProperty(tab, "popup");
        this.tabManager.addActiveTabPermission(tab);

        // Popups are shown only if a popup URL is defined; otherwise
        // a "click" event is dispatched. This is done for compatibility with the
        // Google Chrome onClicked extension API.
        if (popupURL) {
          try {
            let popup = this.getPopup(document.defaultView, popupURL);
            let attachPromise = popup.attach(event.target);
            event.detail.addBlocker(attachPromise);
            await attachPromise;
            TelemetryStopwatch.finish(POPUP_OPEN_MS_HISTOGRAM, this);
            if (this.eventQueue.length) {
              let histogram = Services.telemetry.getHistogramById(POPUP_RESULT_HISTOGRAM);
              histogram.add("popupShown");
              this.eventQueue = [];
            }
          } catch (e) {
            TelemetryStopwatch.cancel(POPUP_OPEN_MS_HISTOGRAM, this);
            Cu.reportError(e);
            event.preventDefault();
          }
        } else {
          TelemetryStopwatch.cancel(POPUP_OPEN_MS_HISTOGRAM, this);
          // This isn't not a hack, but it seems to provide the correct behavior
          // with the fewest complications.
          event.preventDefault();
          this.emit("click", tabbrowser.selectedBrowser);
          // Ensure we close any popups this node was in:
          CustomizableUI.hidePanelForNode(event.target);
        }
      },
    });

    this.tabContext.on("tab-select", // eslint-disable-line mozilla/balanced-listeners
                       (evt, tab) => { this.updateWindow(tab.ownerGlobal); });

    this.widget = widget;
  }

  /**
   * Triggers this browser action for the given window, with the same effects as
   * if it were clicked by a user.
   *
   * This has no effect if the browser action is disabled for, or not
   * present in, the given window.
   *
   * @param {Window} window
   */
  async triggerAction(window) {
    let popup = ViewPopup.for(this.extension, window);
    if (popup) {
      popup.closePopup();
      return;
    }

    let widget = this.widget.forWindow(window);
    let tab = window.gBrowser.selectedTab;

    if (!widget || !this.getProperty(tab, "enabled")) {
      return;
    }

    // Popups are shown only if a popup URL is defined; otherwise
    // a "click" event is dispatched. This is done for compatibility with the
    // Google Chrome onClicked extension API.
    if (this.getProperty(tab, "popup")) {
      if (this.widget.areaType == CustomizableUI.TYPE_MENU_PANEL) {
        await window.document.getElementById("nav-bar").overflowable.show();
      }

      let event = new window.CustomEvent("command", {bubbles: true, cancelable: true});
      widget.node.dispatchEvent(event);
    } else {
      this.tabManager.addActiveTabPermission(tab);
      this.emit("click");
    }
  }

  handleEvent(event) {
    let button = event.target;
    let window = button.ownerGlobal;

    switch (event.type) {
      case "mousedown":
        if (event.button == 0) {
          // Begin pre-loading the browser for the popup, so it's more likely to
          // be ready by the time we get a complete click.
          let tab = window.gBrowser.selectedTab;
          let popupURL = this.getProperty(tab, "popup");
          let enabled = this.getProperty(tab, "enabled");

          if (popupURL && enabled && (this.pendingPopup || !ViewPopup.for(this.extension, window))) {
            this.eventQueue.push("Mousedown");
            // Add permission for the active tab so it will exist for the popup.
            // Store the tab to revoke the permission during clearPopup.
            if (!this.tabManager.hasActiveTabPermission(tab)) {
              this.tabManager.addActiveTabPermission(tab);
              this.tabToRevokeDuringClearPopup = tab;
            }

            this.pendingPopup = this.getPopup(window, popupURL);
            window.addEventListener("mouseup", this, true);
          } else {
            this.clearPopup();
          }
        }
        break;

      case "mouseup":
        if (event.button == 0) {
          this.clearPopupTimeout();
          // If we have a pending pre-loaded popup, cancel it after we've waited
          // long enough that we can be relatively certain it won't be opening.
          if (this.pendingPopup) {
            let node = window.gBrowser && this.widget.forWindow(window).node;
            if (isAncestorOrSelf(node, event.originalTarget)) {
              this.pendingPopupTimeout = setTimeout(() => this.clearPopup(),
                                                    POPUP_PRELOAD_TIMEOUT_MS);
            } else {
              this.clearPopup();
            }
          }
        }
        break;

      case "mouseover": {
        // Begin pre-loading the browser for the popup, so it's more likely to
        // be ready by the time we get a complete click.
        let tab = window.gBrowser.selectedTab;
        let popupURL = this.getProperty(tab, "popup");
        let enabled = this.getProperty(tab, "enabled");

        if (popupURL && enabled && (this.pendingPopup || !ViewPopup.for(this.extension, window))) {
          this.eventQueue.push("Hover");
          this.pendingPopup = this.getPopup(window, popupURL, true);
        }
        break;
      }

      case "mouseout":
        if (this.pendingPopup) {
          if (this.eventQueue.length) {
            let histogram = Services.telemetry.getHistogramById(POPUP_RESULT_HISTOGRAM);
            histogram.add(`clearAfter${this.eventQueue.pop()}`);
            this.eventQueue = [];
          }
          this.clearPopup();
        }
        break;


      case "popupshowing":
        const menu = event.target;
        const trigger = menu.triggerNode;
        const node = window.document.getElementById(this.id);
        const contexts = ["toolbar-context-menu", "customizationPanelItemContextMenu"];

        if (contexts.includes(menu.id) && node && isAncestorOrSelf(node, trigger)) {
          global.actionContextMenu({
            extension: this.extension,
            onBrowserAction: true,
            menu: menu,
          });
        }
        break;
    }
  }

  /**
   * Returns a potentially pre-loaded popup for the given URL in the given
   * window. If a matching pre-load popup already exists, returns that.
   * Otherwise, initializes a new one.
   *
   * If a pre-load popup exists which does not match, it is destroyed before a
   * new one is created.
   *
   * @param {Window} window
   *        The browser window in which to create the popup.
   * @param {string} popupURL
   *        The URL to load into the popup.
   * @param {boolean} [blockParser = false]
   *        True if the HTML parser should initially be blocked.
   * @returns {ViewPopup}
   */
  getPopup(window, popupURL, blockParser = false) {
    this.clearPopupTimeout();
    let {pendingPopup} = this;
    this.pendingPopup = null;

    if (pendingPopup) {
      if (pendingPopup.window === window && pendingPopup.popupURL === popupURL) {
        if (!blockParser) {
          pendingPopup.unblockParser();
        }

        return pendingPopup;
      }
      pendingPopup.destroy();
    }

    let fixedWidth = this.widget.areaType == CustomizableUI.TYPE_MENU_PANEL;
    return new ViewPopup(this.extension, window, popupURL, this.browserStyle, fixedWidth, blockParser);
  }

  /**
   * Clears any pending pre-loaded popup and related timeouts.
   */
  clearPopup() {
    this.clearPopupTimeout();
    if (this.pendingPopup) {
      if (this.tabToRevokeDuringClearPopup) {
        this.tabManager.revokeActiveTabPermission(this.tabToRevokeDuringClearPopup);
      }
      this.pendingPopup.destroy();
      this.pendingPopup = null;
    }
    this.tabToRevokeDuringClearPopup = null;
  }

  /**
   * Clears any pending timeouts to clear stale, pre-loaded popups.
   */
  clearPopupTimeout() {
    if (this.pendingPopup) {
      this.pendingPopup.window.removeEventListener("mouseup", this, true);
    }

    if (this.pendingPopupTimeout) {
      clearTimeout(this.pendingPopupTimeout);
      this.pendingPopupTimeout = null;
    }
  }

  // Update the toolbar button |node| with the tab context data
  // in |tabData|.
  updateButton(node, tabData, sync = false) {
    let title = tabData.title || this.extension.name;
    let callback = () => {
      node.setAttribute("tooltiptext", title);
      node.setAttribute("label", title);

      if (tabData.badgeText) {
        node.setAttribute("badge", tabData.badgeText);
      } else {
        node.removeAttribute("badge");
      }

      if (tabData.enabled) {
        node.removeAttribute("disabled");
      } else {
        node.setAttribute("disabled", "true");
      }

      let color = tabData.badgeBackgroundColor;
      if (color) {
        color = `rgba(${color[0]}, ${color[1]}, ${color[2]}, ${color[3] / 255})`;
        node.setAttribute("badgeStyle", `background-color: ${color};`);
      } else {
        node.removeAttribute("badgeStyle");
      }

      let {style, legacy} = this.iconData.get(tabData.icon);
      const LEGACY_CLASS = "toolbarbutton-legacy-addon";
      if (legacy) {
        node.classList.add(LEGACY_CLASS);
      } else {
        node.classList.remove(LEGACY_CLASS);
      }

      node.setAttribute("style", style);
    };
    if (sync) {
      callback();
    } else {
      node.ownerGlobal.requestAnimationFrame(callback);
    }
  }

  getIconData(icons) {
    let baseSize = 16;
    let {icon, size} = IconDetails.getPreferredIcon(icons, this.extension, baseSize);

    let legacy = false;

    // If the best available icon size is not divisible by 16, check if we have
    // an 18px icon to fall back to, and trim off the padding instead.
    if (size % 16 && typeof icon === "string" && !icon.endsWith(".svg")) {
      let result = IconDetails.getPreferredIcon(icons, this.extension, 18);

      if (result.size % 18 == 0) {
        baseSize = 18;
        icon = result.icon;
        legacy = true;
      }
    }

    let getIcon = (size, theme) => {
      let {icon} = IconDetails.getPreferredIcon(icons, this.extension, size);
      if (typeof icon === "object") {
        return IconDetails.escapeUrl(icon[theme]);
      }
      return IconDetails.escapeUrl(icon);
    };

    let getStyle = (name, size) => {
      return `
        --webextension-${name}: url("${getIcon(size, "default")}");
        --webextension-${name}-light: url("${getIcon(size, "light")}");
        --webextension-${name}-dark: url("${getIcon(size, "dark")}");
      `;
    };

    let style = `
      ${getStyle("menupanel-image", 32)}
      ${getStyle("menupanel-image-2x", 64)}
      ${getStyle("toolbar-image", baseSize)}
      ${getStyle("toolbar-image-2x", baseSize * 2)}
    `;

    return {style, legacy};
  }

  // Update the toolbar button for a given window.
  updateWindow(window) {
    let widget = this.widget.forWindow(window);
    if (widget) {
      let tab = window.gBrowser.selectedTab;
      this.updateButton(widget.node, this.tabContext.get(tab));
    }
  }

  // Update the toolbar button when the extension changes the icon,
  // title, badge, etc. If it only changes a parameter for a single
  // tab, |tab| will be that tab. Otherwise it will be null.
  updateOnChange(tab) {
    if (tab) {
      if (tab.selected) {
        this.updateWindow(tab.ownerGlobal);
      }
    } else {
      for (let window of windowTracker.browserWindows()) {
        this.updateWindow(window);
      }
    }
  }

  // tab is allowed to be null.
  // prop should be one of "icon", "title", "badgeText", "popup", or "badgeBackgroundColor".
  setProperty(tab, prop, value) {
    let values;
    if (tab == null) {
      values = this.globals;
    } else {
      values = this.tabContext.get(tab);
    }
    if (value == null) {
      delete values[prop];
    } else {
      values[prop] = value;
    }

    this.updateOnChange(tab);
  }

  // tab is allowed to be null.
  // prop should be one of "title", "badgeText", "popup", or "badgeBackgroundColor".
  getProperty(tab, prop) {
    if (tab == null) {
      return this.globals[prop];
    }
    return this.tabContext.get(tab)[prop];
  }

  getAPI(context) {
    let {extension} = context;
    let {tabManager} = extension;

    let browserAction = this;

    function getTab(tabId) {
      if (tabId !== null) {
        return tabTracker.getTab(tabId);
      }
      return null;
    }

    return {
      browserAction: {
        onClicked: new InputEventManager(context, "browserAction.onClicked", fire => {
          let listener = (event, browser) => {
            context.withPendingBrowser(browser, () =>
              fire.sync(tabManager.convert(tabTracker.activeTab)));
          };
          browserAction.on("click", listener);
          return () => {
            browserAction.off("click", listener);
          };
        }).api(),

        enable: function(tabId) {
          let tab = getTab(tabId);
          browserAction.setProperty(tab, "enabled", true);
        },

        disable: function(tabId) {
          let tab = getTab(tabId);
          browserAction.setProperty(tab, "enabled", false);
        },

        isEnabled: function(details) {
          let tab = getTab(details.tabId);
          return browserAction.getProperty(tab, "enabled");
        },

        setTitle: function(details) {
          let tab = getTab(details.tabId);

          browserAction.setProperty(tab, "title", details.title);
        },

        getTitle: function(details) {
          let tab = getTab(details.tabId);

          let title = browserAction.getProperty(tab, "title");
          return Promise.resolve(title);
        },

        setIcon: function(details) {
          let tab = getTab(details.tabId);

          details.iconType = "browserAction";

          let icon = IconDetails.normalize(details, extension, context);
          if (!Object.keys(icon).length) {
            icon = null;
          }
          browserAction.setProperty(tab, "icon", icon);
        },

        setBadgeText: function(details) {
          let tab = getTab(details.tabId);

          browserAction.setProperty(tab, "badgeText", details.text);
        },

        getBadgeText: function(details) {
          let tab = getTab(details.tabId);

          let text = browserAction.getProperty(tab, "badgeText");
          return Promise.resolve(text);
        },

        setPopup: function(details) {
          let tab = getTab(details.tabId);

          // Note: Chrome resolves arguments to setIcon relative to the calling
          // context, but resolves arguments to setPopup relative to the extension
          // root.
          // For internal consistency, we currently resolve both relative to the
          // calling context.
          let url = details.popup && context.uri.resolve(details.popup);
          if (url && !context.checkLoadURL(url)) {
            return Promise.reject({message: `Access denied for URL ${url}`});
          }
          browserAction.setProperty(tab, "popup", url);
        },

        getPopup: function(details) {
          let tab = getTab(details.tabId);

          let popup = browserAction.getProperty(tab, "popup");
          return Promise.resolve(popup);
        },

        setBadgeBackgroundColor: function(details) {
          let tab = getTab(details.tabId);
          let color = details.color;
          if (typeof color == "string") {
            let col = InspectorUtils.colorToRGBA(color);
            if (!col) {
              throw new ExtensionError(`Invalid badge background color: "${color}"`);
            }
            color = col && [col.r, col.g, col.b, Math.round(col.a * 255)];
          }
          browserAction.setProperty(tab, "badgeBackgroundColor", color);
        },

        getBadgeBackgroundColor: function(details, callback) {
          let tab = getTab(details.tabId);

          let color = browserAction.getProperty(tab, "badgeBackgroundColor");
          return Promise.resolve(color || [0xd9, 0, 0, 255]);
        },

        openPopup: function() {
          let window = windowTracker.topWindow;
          browserAction.triggerAction(window);
        },
      },
    };
  }
};

global.browserActionFor = this.browserAction.for;
PK
!<ON	��&�&2chrome/browser/content/browser/ext-browsingData.js/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";


ChromeUtils.defineModuleGetter(this, "PlacesUtils",
                               "resource://gre/modules/PlacesUtils.jsm");
ChromeUtils.defineModuleGetter(this, "Preferences",
                               "resource://gre/modules/Preferences.jsm");
ChromeUtils.defineModuleGetter(this, "Sanitizer",
                               "resource:///modules/Sanitizer.jsm");
ChromeUtils.defineModuleGetter(this, "Services",
                               "resource://gre/modules/Services.jsm");
ChromeUtils.defineModuleGetter(this, "setTimeout",
                               "resource://gre/modules/Timer.jsm");

XPCOMUtils.defineLazyServiceGetter(this, "serviceWorkerManager",
                                   "@mozilla.org/serviceworkers/manager;1",
                                   "nsIServiceWorkerManager");
XPCOMUtils.defineLazyServiceGetter(this, "quotaManagerService",
                                   "@mozilla.org/dom/quota-manager-service;1",
                                   "nsIQuotaManagerService");

/**
* A number of iterations after which to yield time back
* to the system.
*/
const YIELD_PERIOD = 10;

const makeRange = options => {
  return (options.since == null) ?
    null :
    [PlacesUtils.toPRTime(options.since), PlacesUtils.toPRTime(Date.now())];
};

const clearCache = () => {
  // Clearing the cache does not support timestamps.
  return Sanitizer.items.cache.clear();
};

const clearCookies = async function(options) {
  let cookieMgr = Services.cookies;
  // This code has been borrowed from Sanitizer.jsm.
  let yieldCounter = 0;

  if (options.since || options.hostnames) {
    // Iterate through the cookies and delete any created after our cutoff.
    for (const cookie of XPCOMUtils.IterSimpleEnumerator(cookieMgr.enumerator, Ci.nsICookie2)) {
      if ((!options.since || cookie.creationTime >= PlacesUtils.toPRTime(options.since)) &&
          (!options.hostnames || options.hostnames.includes(cookie.host.replace(/^\./, "")))) {
        // This cookie was created after our cutoff, clear it.
        cookieMgr.remove(cookie.host, cookie.name, cookie.path,
                         false, cookie.originAttributes);

        if (++yieldCounter % YIELD_PERIOD == 0) {
          await new Promise(resolve => setTimeout(resolve, 0)); // Don't block the main thread too long.
        }
      }
    }
  } else {
    // Remove everything.
    cookieMgr.removeAll();
  }
};

const clearDownloads = options => {
  return Sanitizer.items.downloads.clear(makeRange(options));
};

const clearFormData = options => {
  return Sanitizer.items.formdata.clear(makeRange(options));
};

const clearHistory = options => {
  return Sanitizer.items.history.clear(makeRange(options));
};

const clearIndexedDB = async function(options) {
  let promises = [];

  await new Promise(resolve => {
    quotaManagerService.getUsage(request => {
      if (request.resultCode != Cr.NS_OK) {
        // We are probably shutting down. We don't want to propagate the error,
        // rejecting the promise.
        resolve();
        return;
      }

      for (let item of request.result) {
        let principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(item.origin);
        let uri = principal.URI;
        if (uri.scheme == "http" || uri.scheme == "https" || uri.scheme == "file") {
          promises.push(new Promise(r => {
            let req = quotaManagerService.clearStoragesForPrincipal(principal, null, false);
            req.callback = () => { r(); };
          }));
        }
      }

      resolve();
    });
  });

  return Promise.all(promises);
};

const clearLocalStorage = async function(options) {
  if (options.since) {
    return Promise.reject(
      {message: "Firefox does not support clearing localStorage with 'since'."});
  }
  if (options.hostnames) {
    for (let hostname of options.hostnames) {
      Services.obs.notifyObservers(null, "extension:purge-localStorage", hostname);
    }
  } else {
    Services.obs.notifyObservers(null, "extension:purge-localStorage");
  }
};

const clearPasswords = async function(options) {
  let loginManager = Services.logins;
  let yieldCounter = 0;

  if (options.since) {
    // Iterate through the logins and delete any updated after our cutoff.
    let logins = loginManager.getAllLogins();
    for (let login of logins) {
      login.QueryInterface(Ci.nsILoginMetaInfo);
      if (login.timePasswordChanged >= options.since) {
        loginManager.removeLogin(login);
        if (++yieldCounter % YIELD_PERIOD == 0) {
          await new Promise(resolve => setTimeout(resolve, 0)); // Don't block the main thread too long.
        }
      }
    }
  } else {
    // Remove everything.
    loginManager.removeAllLogins();
  }
};

const clearPluginData = options => {
  return Sanitizer.items.pluginData.clear(makeRange(options));
};

const clearServiceWorkers = async function() {
  // Clearing service workers does not support timestamps.
  let yieldCounter = 0;

  // Iterate through the service workers and remove them.
  let serviceWorkers = serviceWorkerManager.getAllRegistrations();
  for (let i = 0; i < serviceWorkers.length; i++) {
    let sw = serviceWorkers.queryElementAt(i, Ci.nsIServiceWorkerRegistrationInfo);
    let host = sw.principal.URI.host;
    serviceWorkerManager.removeAndPropagate(host);
    if (++yieldCounter % YIELD_PERIOD == 0) {
      await new Promise(resolve => setTimeout(resolve, 0)); // Don't block the main thread too long.
    }
  }
};

const doRemoval = (options, dataToRemove, extension) => {
  if (options.originTypes &&
      (options.originTypes.protectedWeb || options.originTypes.extension)) {
    return Promise.reject(
      {message: "Firefox does not support protectedWeb or extension as originTypes."});
  }

  let removalPromises = [];
  let invalidDataTypes = [];
  for (let dataType in dataToRemove) {
    if (dataToRemove[dataType]) {
      switch (dataType) {
        case "cache":
          removalPromises.push(clearCache());
          break;
        case "cookies":
          removalPromises.push(clearCookies(options));
          break;
        case "downloads":
          removalPromises.push(clearDownloads(options));
          break;
        case "formData":
          removalPromises.push(clearFormData(options));
          break;
        case "history":
          removalPromises.push(clearHistory(options));
          break;
        case "indexedDB":
          removalPromises.push(clearIndexedDB(options));
          break;
        case "localStorage":
          removalPromises.push(clearLocalStorage(options));
          break;
        case "passwords":
          removalPromises.push(clearPasswords(options));
          break;
        case "pluginData":
          removalPromises.push(clearPluginData(options));
          break;
        case "serviceWorkers":
          removalPromises.push(clearServiceWorkers());
          break;
        default:
          invalidDataTypes.push(dataType);
      }
    }
  }
  if (extension && invalidDataTypes.length) {
    extension.logger.warn(
      `Firefox does not support dataTypes: ${invalidDataTypes.toString()}.`);
  }
  return Promise.all(removalPromises);
};

this.browsingData = class extends ExtensionAPI {
  getAPI(context) {
    let {extension} = context;
    return {
      browsingData: {
        settings() {
          const PREF_DOMAIN = "privacy.cpd.";
          // The following prefs are the only ones in Firefox that match corresponding
          // values used by Chrome when rerturning settings.
          const PREF_LIST = ["cache", "cookies", "history", "formdata", "downloads"];

          // since will be the start of what is returned by Sanitizer.getClearRange
          // divided by 1000 to convert to ms.
          // If Sanitizer.getClearRange returns undefined that means the range is
          // currently "Everything", so we should set since to 0.
          let clearRange = Sanitizer.getClearRange();
          let since = clearRange ? clearRange[0] / 1000 : 0;
          let options = {since};

          let dataToRemove = {};
          let dataRemovalPermitted = {};

          for (let item of PREF_LIST) {
            // The property formData needs a different case than the
            // formdata preference.
            const name = item === "formdata" ? "formData" : item;
            dataToRemove[name] = Preferences.get(`${PREF_DOMAIN}${item}`);
            // Firefox doesn't have the same concept of dataRemovalPermitted
            // as Chrome, so it will always be true.
            dataRemovalPermitted[name] = true;
          }

          return Promise.resolve({options, dataToRemove, dataRemovalPermitted});
        },
        remove(options, dataToRemove) {
          return doRemoval(options, dataToRemove, extension);
        },
        removeCache(options) {
          return doRemoval(options, {cache: true});
        },
        removeCookies(options) {
          return doRemoval(options, {cookies: true});
        },
        removeDownloads(options) {
          return doRemoval(options, {downloads: true});
        },
        removeFormData(options) {
          return doRemoval(options, {formData: true});
        },
        removeHistory(options) {
          return doRemoval(options, {history: true});
        },
        removeIndexedDB(options) {
          return doRemoval(options, {indexedDB: true});
        },
        removeLocalStorage(options) {
          return doRemoval(options, {localStorage: true});
        },
        removePasswords(options) {
          return doRemoval(options, {passwords: true});
        },
        removePluginData(options) {
          return doRemoval(options, {pluginData: true});
        },
      },
    };
  }
};
PK
!<�G'��/chrome/browser/content/browser/ext-c-browser.js"use strict";

extensions.registerModules({
  devtools: {
    url: "chrome://browser/content/ext-c-devtools.js",
    scopes: ["devtools_child"],
    paths: [
      ["devtools"],
    ],
  },
  devtools_inspectedWindow: {
    url: "chrome://browser/content/ext-c-devtools-inspectedWindow.js",
    scopes: ["devtools_child"],
    paths: [
      ["devtools", "inspectedWindow"],
    ],
  },
  devtools_panels: {
    url: "chrome://browser/content/ext-c-devtools-panels.js",
    scopes: ["devtools_child"],
    paths: [
      ["devtools", "panels"],
    ],
  },
  devtools_network: {
    url: "chrome://browser/content/ext-c-devtools-network.js",
    scopes: ["devtools_child"],
    paths: [
      ["devtools", "network"],
    ],
  },
  // Because of permissions, the module name must differ from both namespaces.
  menusInternal: {
    url: "chrome://browser/content/ext-c-menus.js",
    scopes: ["addon_child"],
    paths: [
      ["contextMenus"],
      ["menus"],
    ],
  },
  omnibox: {
    url: "chrome://browser/content/ext-c-omnibox.js",
    scopes: ["addon_child"],
    paths: [
      ["omnibox"],
    ],
  },
  tabs: {
    url: "chrome://browser/content/ext-c-tabs.js",
    scopes: ["addon_child"],
    paths: [
      ["tabs"],
    ],
  },
});
PK
!<!u�@chrome/browser/content/browser/ext-c-devtools-inspectedWindow.js/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";

this.devtools_inspectedWindow = class extends ExtensionAPI {
  getAPI(context) {
    // `devtoolsToolboxInfo` is received from the child process when the root devtools view
    // has been created, and every sub-frame of that top level devtools frame will
    // receive the same information when the context has been created from the
    // `ExtensionChild.createExtensionContext` method.
    let tabId = (context.devtoolsToolboxInfo &&
                 context.devtoolsToolboxInfo.inspectedWindowTabId);

    return {
      devtools: {
        inspectedWindow: {
          get tabId() {
            return tabId;
          },
        },
      },
    };
  }
};
PK
!<}��3��8chrome/browser/content/browser/ext-c-devtools-network.js/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";

// The ext-* files are imported into the same scopes.
/* import-globals-from ../../../toolkit/components/extensions/ext-c-toolkit.js */

/**
 * Responsible for fetching HTTP response content from the backend.
 *
 * @param {DevtoolsExtensionContext}
 *   A devtools extension context running in a child process.
 * @param {object} options
 */
class ChildNetworkResponseLoader {
  constructor(context, requestId) {
    this.context = context;
    this.requestId = requestId;
  }

  api() {
    const {context, requestId} = this;
    return {
      getContent(callback) {
        return context.childManager.callParentAsyncFunction(
          "devtools.network.Request.getContent",
          [requestId],
          callback);
      },
    };
  }
}

this.devtools_network = class extends ExtensionAPI {
  getAPI(context) {
    return {
      devtools: {
        network: {
          onRequestFinished: new EventManager(context, "devtools.network.onRequestFinished", fire => {
            let onFinished = (data) => {
              const loader = new ChildNetworkResponseLoader(context, data.requestId);
              const harEntry = {...data.harEntry, ...loader.api()};
              const result = Cu.cloneInto(harEntry, context.cloneScope, {
                cloneFunctions: true,
              });
              fire.asyncWithoutClone(result);
            };

            let parent = context.childManager.getParentEvent("devtools.network.onRequestFinished");
            parent.addListener(onFinished);
            return () => {
              parent.removeListener(onFinished);
            };
          }).api(),
        },
      },
    };
  }
};
PK
!<\y�k$k$7chrome/browser/content/browser/ext-c-devtools-panels.js/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";

// The ext-* files are imported into the same scopes.
/* import-globals-from ../../../toolkit/components/extensions/ext-c-toolkit.js */

ChromeUtils.defineModuleGetter(this, "ExtensionChildDevToolsUtils",
                               "resource://gre/modules/ExtensionChildDevToolsUtils.jsm");

var {
  promiseDocumentLoaded,
} = ExtensionUtils;

/**
 * Represents an addon devtools panel in the child process.
 *
 * @param {DevtoolsExtensionContext}
 *   A devtools extension context running in a child process.
 * @param {object} panelOptions
 * @param {string} panelOptions.id
 *   The id of the addon devtools panel registered in the main process.
 */
class ChildDevToolsPanel extends ExtensionUtils.EventEmitter {
  constructor(context, {id}) {
    super();

    this.context = context;
    this.context.callOnClose(this);

    this.id = id;
    this._panelContext = null;

    this.mm = context.messageManager;
    this.mm.addMessageListener("Extension:DevToolsPanelShown", this);
    this.mm.addMessageListener("Extension:DevToolsPanelHidden", this);
  }

  get panelContext() {
    if (this._panelContext) {
      return this._panelContext;
    }

    for (let view of this.context.extension.devtoolsViews) {
      if (view.viewType === "devtools_panel" &&
          view.devtoolsToolboxInfo.toolboxPanelId === this.id) {
        this._panelContext = view;

        // Reset the cached _panelContext property when the view is closed.
        view.callOnClose({
          close: () => {
            this._panelContext = null;
          },
        });
        return view;
      }
    }

    return null;
  }

  receiveMessage({name, data}) {
    // Filter out any message that is not related to the id of this
    // toolbox panel.
    if (!data || data.toolboxPanelId !== this.id) {
      return;
    }

    switch (name) {
      case "Extension:DevToolsPanelShown":
        // Filter out *Shown message received while the panel context do not yet
        // exist.
        if (!this.panelContext || !this.panelContext.contentWindow) {
          return;
        }
        this.onParentPanelShown();
        break;
      case "Extension:DevToolsPanelHidden":
        this.onParentPanelHidden();
        break;
    }
  }

  onParentPanelShown() {
    const {document} = this.panelContext.contentWindow;

    // Ensure that the onShown event is fired when the panel document has
    // been fully loaded.
    promiseDocumentLoaded(document).then(() => {
      this.emit("shown", this.panelContext.contentWindow);
    });
  }

  onParentPanelHidden() {
    this.emit("hidden");
  }

  api() {
    return {
      onShown: new EventManager(
        this.context, "devtoolsPanel.onShown", fire => {
          const listener = (eventName, panelContentWindow) => {
            fire.asyncWithoutClone(panelContentWindow);
          };
          this.on("shown", listener);
          return () => {
            this.off("shown", listener);
          };
        }).api(),

      onHidden: new EventManager(
        this.context, "devtoolsPanel.onHidden", fire => {
          const listener = () => {
            fire.async();
          };
          this.on("hidden", listener);
          return () => {
            this.off("hidden", listener);
          };
        }).api(),

      // TODO(rpl): onSearch event and createStatusBarButton method
    };
  }

  close() {
    this.mm.removeMessageListener("Extension:DevToolsPanelShown", this);
    this.mm.removeMessageListener("Extension:DevToolsPanelHidden", this);

    this._panelContext = null;
    this.context = null;
  }
}

/**
 * Represents an addon devtools inspector sidebar in the child process.
 *
 * @param {DevtoolsExtensionContext}
 *   A devtools extension context running in a child process.
 * @param {object} sidebarOptions
 * @param {string} sidebarOptions.id
 *   The id of the addon devtools sidebar registered in the main process.
 */
class ChildDevToolsInspectorSidebar extends ExtensionUtils.EventEmitter {
  constructor(context, {id}) {
    super();

    this.context = context;
    this.context.callOnClose(this);

    this.id = id;

    this.mm = context.messageManager;
    this.mm.addMessageListener("Extension:DevToolsInspectorSidebarShown", this);
    this.mm.addMessageListener("Extension:DevToolsInspectorSidebarHidden", this);
  }

  close() {
    this.mm.removeMessageListener("Extension:DevToolsInspectorSidebarShown", this);
    this.mm.removeMessageListener("Extension:DevToolsInspectorSidebarHidden", this);

    this.content = null;
  }

  receiveMessage({name, data}) {
    // Filter out any message that is not related to the id of this
    // toolbox panel.
    if (!data || data.inspectorSidebarId !== this.id) {
      return;
    }

    switch (name) {
      case "Extension:DevToolsInspectorSidebarShown":
        this.onParentSidebarShown();
        break;
      case "Extension:DevToolsInspectorSidebarHidden":
        this.onParentSidebarHidden();
        break;
    }
  }

  onParentSidebarShown() {
    // TODO: wait and emit sidebar contentWindow once sidebar.setPage is supported.
    this.emit("shown");
  }

  onParentSidebarHidden() {
    this.emit("hidden");
  }

  api() {
    const {context, id} = this;

    return {
      onShown: new EventManager(
        context, "devtoolsInspectorSidebar.onShown", fire => {
          const listener = (eventName, panelContentWindow) => {
            fire.asyncWithoutClone(panelContentWindow);
          };
          this.on("shown", listener);
          return () => {
            this.off("shown", listener);
          };
        }).api(),

      onHidden: new EventManager(
        context, "devtoolsInspectorSidebar.onHidden", fire => {
          const listener = () => {
            fire.async();
          };
          this.on("hidden", listener);
          return () => {
            this.off("hidden", listener);
          };
        }).api(),

      setObject(jsonObject, rootTitle) {
        return context.cloneScope.Promise.resolve().then(() => {
          return context.childManager.callParentAsyncFunction(
            "devtools.panels.elements.Sidebar.setObject",
            [id, jsonObject, rootTitle]
          );
        });
      },

      setExpression(evalExpression, rootTitle) {
        return context.cloneScope.Promise.resolve().then(() => {
          return context.childManager.callParentAsyncFunction(
            "devtools.panels.elements.Sidebar.setExpression",
            [id, evalExpression, rootTitle]
          );
        });
      },
    };
  }
}

this.devtools_panels = class extends ExtensionAPI {
  getAPI(context) {
    const themeChangeObserver = ExtensionChildDevToolsUtils.getThemeChangeObserver();

    return {
      devtools: {
        panels: {
          elements: {
            createSidebarPane(title) {
              // NOTE: this is needed to be able to return to the caller (the extension)
              // a promise object that it had the privileges to use (e.g. by marking this
              // method async we will return a promise object which can only be used by
              // chrome privileged code).
              return context.cloneScope.Promise.resolve().then(async () => {
                const sidebarId = await context.childManager.callParentAsyncFunction(
                  "devtools.panels.elements.createSidebarPane", [title]);

                const sidebar = new ChildDevToolsInspectorSidebar(context, {id: sidebarId});

                const sidebarAPI = Cu.cloneInto(sidebar.api(),
                                                context.cloneScope,
                                                {cloneFunctions: true});

                return sidebarAPI;
              });
            },
          },
          create(title, icon, url) {
            // NOTE: this is needed to be able to return to the caller (the extension)
            // a promise object that it had the privileges to use (e.g. by marking this
            // method async we will return a promise object which can only be used by
            // chrome privileged code).
            return context.cloneScope.Promise.resolve().then(async () => {
              const panelId = await context.childManager.callParentAsyncFunction(
                "devtools.panels.create", [title, icon, url]);

              const devtoolsPanel = new ChildDevToolsPanel(context, {id: panelId});

              const devtoolsPanelAPI = Cu.cloneInto(devtoolsPanel.api(),
                                                    context.cloneScope,
                                                    {cloneFunctions: true});
              return devtoolsPanelAPI;
            });
          },
          get themeName() {
            return themeChangeObserver.themeName;
          },
          onThemeChanged: new EventManager(
            context, "devtools.panels.onThemeChanged", fire => {
              const listener = (eventName, themeName) => {
                fire.async(themeName);
              };
              themeChangeObserver.on("themeChanged", listener);
              return () => {
                themeChangeObserver.off("themeChanged", listener);
              };
            }).api(),
        },
      },
    };
  }
};
PK
!<�@����0chrome/browser/content/browser/ext-c-devtools.js/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";


this.devtools = class extends ExtensionAPI {
  getAPI(context) {
    return {
      devtools: {},
    };
  }
};

PK
!<�z�ZZ-chrome/browser/content/browser/ext-c-menus.js/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";

// The ext-* files are imported into the same scopes.
/* import-globals-from ../../../toolkit/components/extensions/ext-c-toolkit.js */

var {
  withHandlingUserInput,
} = ExtensionUtils;

// If id is not specified for an item we use an integer.
// This ID need only be unique within a single addon. Since all addon code that
// can use this API runs in the same process, this local variable suffices.
var gNextMenuItemID = 0;

// Map[Extension -> Map[string or id, ContextMenusClickPropHandler]]
var gPropHandlers = new Map();

// The contextMenus API supports an "onclick" attribute in the create/update
// methods to register a callback. This class manages these onclick properties.
class ContextMenusClickPropHandler {
  constructor(context) {
    this.context = context;
    // Map[string or integer -> callback]
    this.onclickMap = new Map();
    this.dispatchEvent = this.dispatchEvent.bind(this);
  }

  // A listener on contextMenus.onClicked that forwards the event to the only
  // listener, if any.
  dispatchEvent(info, tab) {
    let onclick = this.onclickMap.get(info.menuItemId);
    if (onclick) {
      // No need for runSafe or anything because we are already being run inside
      // an event handler -- the event is just being forwarded to the actual
      // handler.
      onclick(info, tab);
    }
  }

  // Sets the `onclick` handler for the given menu item.
  // The `onclick` function MUST be owned by `this.context`.
  setListener(id, onclick) {
    if (this.onclickMap.size === 0) {
      this.context.childManager.getParentEvent("menusInternal.onClicked").addListener(this.dispatchEvent);
      this.context.callOnClose(this);
    }
    this.onclickMap.set(id, onclick);

    let propHandlerMap = gPropHandlers.get(this.context.extension);
    if (!propHandlerMap) {
      propHandlerMap = new Map();
    } else {
      // If the current callback was created in a different context, remove it
      // from the other context.
      let propHandler = propHandlerMap.get(id);
      if (propHandler && propHandler !== this) {
        propHandler.unsetListener(id);
      }
    }
    propHandlerMap.set(id, this);
    gPropHandlers.set(this.context.extension, propHandlerMap);
  }

  // Deletes the `onclick` handler for the given menu item.
  // The `onclick` function MUST be owned by `this.context`.
  unsetListener(id) {
    if (!this.onclickMap.delete(id)) {
      return;
    }
    if (this.onclickMap.size === 0) {
      this.context.childManager.getParentEvent("menusInternal.onClicked").removeListener(this.dispatchEvent);
      this.context.forgetOnClose(this);
    }
    let propHandlerMap = gPropHandlers.get(this.context.extension);
    propHandlerMap.delete(id);
    if (propHandlerMap.size === 0) {
      gPropHandlers.delete(this.context.extension);
    }
  }

  // Deletes the `onclick` handler for the given menu item, if any, regardless
  // of the context where it was created.
  unsetListenerFromAnyContext(id) {
    let propHandlerMap = gPropHandlers.get(this.context.extension);
    let propHandler = propHandlerMap && propHandlerMap.get(id);
    if (propHandler) {
      propHandler.unsetListener(id);
    }
  }

  // Remove all `onclick` handlers of the extension.
  deleteAllListenersFromExtension() {
    let propHandlerMap = gPropHandlers.get(this.context.extension);
    if (propHandlerMap) {
      for (let [id, propHandler] of propHandlerMap) {
        propHandler.unsetListener(id);
      }
    }
  }

  // Removes all `onclick` handlers from this context.
  close() {
    for (let id of this.onclickMap.keys()) {
      this.unsetListener(id);
    }
  }
}

this.menusInternal = class extends ExtensionAPI {
  getAPI(context) {
    let onClickedProp = new ContextMenusClickPropHandler(context);

    let api = {
      menus: {
        create(createProperties, callback) {
          if (createProperties.id === null) {
            createProperties.id = ++gNextMenuItemID;
          }
          let {onclick} = createProperties;
          delete createProperties.onclick;
          context.childManager.callParentAsyncFunction("menusInternal.create", [
            createProperties,
          ]).then(() => {
            if (onclick) {
              onClickedProp.setListener(createProperties.id, onclick);
            }
            if (callback) {
              callback();
            }
          });
          return createProperties.id;
        },

        update(id, updateProperties) {
          let {onclick} = updateProperties;
          delete updateProperties.onclick;
          return context.childManager.callParentAsyncFunction("menusInternal.update", [
            id,
            updateProperties,
          ]).then(() => {
            if (onclick) {
              onClickedProp.setListener(id, onclick);
            } else if (onclick === null) {
              onClickedProp.unsetListenerFromAnyContext(id);
            }
            // else onclick is not set so it should not be changed.
          });
        },

        remove(id) {
          onClickedProp.unsetListenerFromAnyContext(id);
          return context.childManager.callParentAsyncFunction("menusInternal.remove", [
            id,
          ]);
        },

        removeAll() {
          onClickedProp.deleteAllListenersFromExtension();

          return context.childManager.callParentAsyncFunction("menusInternal.removeAll", []);
        },

        onClicked: new EventManager(context, "menus.onClicked", fire => {
          let listener = (info, tab) => {
            withHandlingUserInput(context.contentWindow,
                                  () => fire.sync(info, tab));
          };

          let event = context.childManager.getParentEvent("menusInternal.onClicked");
          event.addListener(listener);
          return () => {
            event.removeListener(listener);
          };
        }).api(),
      },
    };

    const result = {};
    if (context.extension.hasPermission("menus")) {
      result.menus = api.menus;
    }
    if (context.extension.hasPermission("contextMenus")) {
      result.contextMenus = api.menus;
    }
    return result;
  }
};
PK
!<t<��/chrome/browser/content/browser/ext-c-omnibox.js/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";

// The ext-* files are imported into the same scopes.
/* import-globals-from ../../../toolkit/components/extensions/ext-c-toolkit.js */

this.omnibox = class extends ExtensionAPI {
  getAPI(context) {
    return {
      omnibox: {
        onInputChanged: new EventManager(context, "omnibox.onInputChanged", fire => {
          let listener = (text, id) => {
            fire.asyncWithoutClone(text, suggestions => {
              context.childManager.callParentFunctionNoReturn("omnibox.addSuggestions", [
                id,
                suggestions,
              ]);
            });
          };
          context.childManager.getParentEvent("omnibox.onInputChanged").addListener(listener);
          return () => {
            context.childManager.getParentEvent("omnibox.onInputChanged").removeListener(listener);
          };
        }).api(),
      },
    };
  }
};
PK
!<�����,chrome/browser/content/browser/ext-c-tabs.js/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";

this.tabs = class extends ExtensionAPI {
  getAPI(context) {
    return {
      tabs: {
        connect: function(tabId, connectInfo) {
          let name = "";
          if (connectInfo && connectInfo.name !== null) {
            name = connectInfo.name;
          }
          let recipient = {
            extensionId: context.extension.id,
            tabId,
          };
          if (connectInfo && connectInfo.frameId !== null) {
            recipient.frameId = connectInfo.frameId;
          }
          return context.messenger.connect(context.messageManager, name, recipient);
        },

        sendMessage: function(tabId, message, options, responseCallback) {
          let recipient = {
            extensionId: context.extension.id,
            tabId: tabId,
          };
          if (options && options.frameId !== null) {
            recipient.frameId = options.frameId;
          }
          return context.messenger.sendMessage(context.messageManager, message, recipient, responseCallback);
        },
      },
    };
  }
};
PK
!<��B��$�$?chrome/browser/content/browser/ext-chrome-settings-overrides.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 windowTracker */

"use strict";

ChromeUtils.defineModuleGetter(this, "ExtensionPreferencesManager",
                               "resource://gre/modules/ExtensionPreferencesManager.jsm");
ChromeUtils.defineModuleGetter(this, "ExtensionSettingsStore",
                               "resource://gre/modules/ExtensionSettingsStore.jsm");

const DEFAULT_SEARCH_STORE_TYPE = "default_search";
const DEFAULT_SEARCH_SETTING_NAME = "defaultSearch";
const ENGINE_ADDED_SETTING_NAME = "engineAdded";

// This promise is used to wait for the search service to be initialized.
// None of the code in this module requests that initialization. It is assumed
// that it is started at some point. If tests start to fail because this
// promise never resolves, that's likely the cause.
const searchInitialized = () => {
  if (Services.search.isInitialized) {
    return;
  }
  return new Promise(resolve => {
    const SEARCH_SERVICE_TOPIC = "browser-search-service";
    Services.obs.addObserver(function observer(subject, topic, data) {
      if (data != "init-complete") {
        return;
      }

      Services.obs.removeObserver(observer, SEARCH_SERVICE_TOPIC);
      resolve();
    }, SEARCH_SERVICE_TOPIC);
  });
};

this.chrome_settings_overrides = class extends ExtensionAPI {
  static async processDefaultSearchSetting(action, id) {
    await ExtensionSettingsStore.initialize();
    let item = ExtensionSettingsStore.getSetting(DEFAULT_SEARCH_STORE_TYPE, DEFAULT_SEARCH_SETTING_NAME);
    if (!item) {
      return;
    }
    if (Services.search.currentEngine.name != item.value &&
        Services.search.currentEngine.name != item.initialValue) {
      // The current engine is not the same as the value that the ExtensionSettingsStore has.
      // This means that the user changed the engine, so we shouldn't control it anymore.
      // Do nothing and remove our entry from the ExtensionSettingsStore.
      ExtensionSettingsStore.removeSetting(id, DEFAULT_SEARCH_STORE_TYPE, DEFAULT_SEARCH_SETTING_NAME);
      return;
    }
    item = ExtensionSettingsStore[action](id, DEFAULT_SEARCH_STORE_TYPE, DEFAULT_SEARCH_SETTING_NAME);
    if (item) {
      try {
        let engine = Services.search.getEngineByName(item.value || item.initialValue);
        if (engine) {
          Services.search.currentEngine = engine;
        }
      } catch (e) {
        Cu.reportError(e);
      }
    }
  }

  static async removeEngine(id) {
    await ExtensionSettingsStore.initialize();
    let item = await ExtensionSettingsStore.getSetting(
      DEFAULT_SEARCH_STORE_TYPE, ENGINE_ADDED_SETTING_NAME, id);
    if (item) {
      ExtensionSettingsStore.removeSetting(
        id, DEFAULT_SEARCH_STORE_TYPE, ENGINE_ADDED_SETTING_NAME);
      await searchInitialized();
      let engine = Services.search.getEngineByName(item.value);
      try {
        Services.search.removeEngine(engine);
      } catch (e) {
        Cu.reportError(e);
      }
    }
  }

  static removeSearchSettings(id) {
    this.processDefaultSearchSetting("removeSetting", id);
    this.removeEngine(id);
  }

  static onUninstall(id) {
    // Note: We do not have to deal with homepage here as it is managed by
    // the ExtensionPreferencesManager.
    this.removeSearchSettings(id);
  }

  static onUpdate(id, manifest) {
    let haveHomepage = manifest && manifest.chrome_settings_overrides &&
                       manifest.chrome_settings_overrides.homepage;
    if (!haveHomepage) {
      ExtensionPreferencesManager.removeSetting(id, "homepage_override");
    }

    let haveSearchProvider = manifest && manifest.chrome_settings_overrides &&
                             manifest.chrome_settings_overrides.search_provider;
    if (!haveSearchProvider) {
      this.removeSearchSettings(id);
    }
  }

  async onManifestEntry(entryName) {
    let {extension} = this;
    let {manifest} = extension;

    await ExtensionSettingsStore.initialize();
    if (manifest.chrome_settings_overrides.homepage) {
      ExtensionPreferencesManager.setSetting(extension.id, "homepage_override",
                                             manifest.chrome_settings_overrides.homepage);
    }
    if (manifest.chrome_settings_overrides.search_provider) {
      await searchInitialized();
      extension.callOnClose({
        close: () => {
          if (extension.shutdownReason == "ADDON_DISABLE") {
            chrome_settings_overrides.processDefaultSearchSetting("disable", extension.id);
            chrome_settings_overrides.removeEngine(extension.id);
          }
        },
      });

      let searchProvider = manifest.chrome_settings_overrides.search_provider;
      let engineName = searchProvider.name.trim();
      if (searchProvider.is_default) {
        let engine = Services.search.getEngineByName(engineName);
        if (engine && Services.search.getDefaultEngines().includes(engine)) {
          // Needs to be called every time to handle reenabling, but
          // only sets default for install or enable.
          await this.setDefault(engineName);
          // For built in search engines, we don't do anything further
          return;
        }
      }
      await this.addSearchEngine(searchProvider);
      if (searchProvider.is_default) {
        if (extension.startupReason === "ADDON_INSTALL") {
          // Don't ask if it already the current engine
          let engine = Services.search.getEngineByName(engineName);
          if (Services.search.currentEngine != engine) {
            let allow = await new Promise(resolve => {
              let subject = {
                wrappedJSObject: {
                  // This is a hack because we don't have the browser of
                  // the actual install. This means the popup might show
                  // in a different window. Will be addressed in a followup bug.
                  browser: windowTracker.topWindow.gBrowser.selectedBrowser,
                  name: this.extension.name,
                  icon: this.extension.iconURL,
                  currentEngine: Services.search.currentEngine.name,
                  newEngine: engineName,
                  resolve,
                },
              };
              Services.obs.notifyObservers(subject, "webextension-defaultsearch-prompt");
            });
            if (!allow) {
              return;
            }
          }
        }
        // Needs to be called every time to handle reenabling, but
        // only sets default for install or enable.
        await this.setDefault(engineName);
      } else if (ExtensionSettingsStore.hasSetting(extension.id,
                                                   DEFAULT_SEARCH_STORE_TYPE,
                                                   DEFAULT_SEARCH_SETTING_NAME)) {
        // is_default has been removed, but we still have a setting. Remove it.
        chrome_settings_overrides.processDefaultSearchSetting("removeSetting", extension.id);
      }
    }
  }

  async setDefault(engineName) {
    let {extension} = this;
    if (extension.startupReason === "ADDON_INSTALL") {
      let item = await ExtensionSettingsStore.addSetting(
        extension.id, DEFAULT_SEARCH_STORE_TYPE, DEFAULT_SEARCH_SETTING_NAME, engineName, () => {
          return Services.search.currentEngine.name;
        });
      Services.search.currentEngine = Services.search.getEngineByName(item.value);
    } else if (extension.startupReason === "ADDON_ENABLE") {
      chrome_settings_overrides.processDefaultSearchSetting("enable", extension.id);
    }
  }

  async addSearchEngine(searchProvider) {
    let {extension} = this;
    let isCurrent = false;
    let index = -1;
    if (extension.startupReason === "ADDON_UPGRADE") {
      let engines = Services.search.getEnginesByExtensionID(extension.id);
      if (engines.length > 0) {
        // There can be only one engine right now
        isCurrent = Services.search.currentEngine == engines[0];
        // Get position of engine and store it
        index = Services.search.getEngines().indexOf(engines[0]);
        Services.search.removeEngine(engines[0]);
      }
    }
    try {
      let params = {
        template: searchProvider.search_url,
        iconURL: searchProvider.favicon_url,
        alias: searchProvider.keyword,
        extensionID: extension.id,
        suggestURL: searchProvider.suggest_url,
        queryCharset: "UTF-8",
      };
      Services.search.addEngineWithDetails(searchProvider.name.trim(), params);
      await ExtensionSettingsStore.addSetting(
        extension.id, DEFAULT_SEARCH_STORE_TYPE, ENGINE_ADDED_SETTING_NAME,
        searchProvider.name.trim());
      if (extension.startupReason === "ADDON_UPGRADE") {
        let engine = Services.search.getEngineByName(searchProvider.name.trim());
        if (isCurrent) {
          Services.search.currentEngine = engine;
        }
        if (index != -1) {
          Services.search.moveEngine(engine, index);
        }
      }
    } catch (e) {
      Cu.reportError(e);
      return false;
    }
    return true;
  }
};

ExtensionPreferencesManager.addSetting("homepage_override", {
  prefNames: [
    "browser.startup.homepage",
  ],
  setCallback(value) {
    return {
      "browser.startup.homepage": value,
    };
  },
});
PK
!<�;r�0�0.chrome/browser/content/browser/ext-commands.js/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";

// The ext-* files are imported into the same scopes.
/* import-globals-from ext-browserAction.js */
/* import-globals-from ext-browser.js */

ChromeUtils.defineModuleGetter(this, "ExtensionParent",
                               "resource://gre/modules/ExtensionParent.jsm");
ChromeUtils.defineModuleGetter(this, "ExtensionSettingsStore",
                               "resource://gre/modules/ExtensionSettingsStore.jsm");

var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";

const EXECUTE_PAGE_ACTION = "_execute_page_action";
const EXECUTE_BROWSER_ACTION = "_execute_browser_action";
const EXECUTE_SIDEBAR_ACTION = "_execute_sidebar_action";

function normalizeShortcut(shortcut) {
  return shortcut ? shortcut.replace(/\s+/g, "") : null;
}

this.commands = class extends ExtensionAPI {
  static async onUninstall(extensionId) {
    // Cleanup the updated commands. In some cases the extension is installed
    // and uninstalled so quickly that `this.commands` hasn't loaded yet. To
    // handle that we need to make sure ExtensionSettingsStore is initialized
    // before we clean it up.
    await ExtensionSettingsStore.initialize();
    ExtensionSettingsStore
      .getAllForExtension(extensionId, "commands")
      .forEach(key => {
        ExtensionSettingsStore.removeSetting(extensionId, "commands", key);
      });
  }

  async onManifestEntry(entryName) {
    let {extension} = this;

    this.id = makeWidgetId(extension.id);
    this.windowOpenListener = null;

    // Map[{String} commandName -> {Object} commandProperties]
    this.manifestCommands = this.loadCommandsFromManifest(extension.manifest);

    this.commands = new Promise(async (resolve) => {
      // Deep copy the manifest commands to commands so we can keep the original
      // manifest commands and update commands as needed.
      let commands = new Map();
      this.manifestCommands.forEach((command, name) => {
        commands.set(name, {...command});
      });

      // Update the manifest commands with the persisted updates from
      // browser.commands.update().
      let savedCommands = await this.loadCommandsFromStorage(extension.id);
      savedCommands.forEach((update, name) => {
        let command = commands.get(name);
        if (command) {
          // We will only update commands, not add them.
          Object.assign(command, update);
        }
      });

      resolve(commands);
    });

    // WeakMap[Window -> <xul:keyset>]
    this.keysetsMap = new WeakMap();

    await this.register();
  }

  onShutdown(reason) {
    this.unregister();
  }

  registerKeys(commands) {
    for (let window of windowTracker.browserWindows()) {
      this.registerKeysToDocument(window, commands);
    }
  }

  /**
   * Registers the commands to all open windows and to any which
   * are later created.
   */
  async register() {
    let commands = await this.commands;
    this.registerKeys(commands);

    this.windowOpenListener = (window) => {
      if (!this.keysetsMap.has(window)) {
        this.registerKeysToDocument(window, commands);
      }
    };

    windowTracker.addOpenListener(this.windowOpenListener);
  }

  /**
   * Unregisters the commands from all open windows and stops commands
   * from being registered to windows which are later created.
   */
  unregister() {
    for (let window of windowTracker.browserWindows()) {
      if (this.keysetsMap.has(window)) {
        this.keysetsMap.get(window).remove();
      }
    }

    windowTracker.removeOpenListener(this.windowOpenListener);
  }

  /**
   * Creates a Map from commands for each command in the manifest.commands object.
   *
   * @param {Object} manifest The manifest JSON object.
   * @returns {Map<string, object>}
   */
  loadCommandsFromManifest(manifest) {
    let commands = new Map();
    // For Windows, chrome.runtime expects 'win' while chrome.commands
    // expects 'windows'.  We can special case this for now.
    let {PlatformInfo} = ExtensionParent;
    let os = PlatformInfo.os == "win" ? "windows" : PlatformInfo.os;
    for (let [name, command] of Object.entries(manifest.commands)) {
      let suggested_key = command.suggested_key || {};
      let shortcut = normalizeShortcut(suggested_key[os] || suggested_key.default);
      commands.set(name, {
        description: command.description,
        shortcut,
      });
    }
    return commands;
  }

  async loadCommandsFromStorage(extensionId) {
    await ExtensionSettingsStore.initialize();
    let names = ExtensionSettingsStore.getAllForExtension(extensionId, "commands");
    return names.reduce((map, name) => {
      let command = ExtensionSettingsStore.getSetting(
        "commands", name, extensionId).value;
      return map.set(name, command);
    }, new Map());
  }

  /**
   * Registers the commands to a document.
   * @param {ChromeWindow} window The XUL window to insert the Keyset.
   * @param {Map} commands The commands to be set.
   */
  registerKeysToDocument(window, commands) {
    let doc = window.document;
    let keyset = doc.createElementNS(XUL_NS, "keyset");
    keyset.id = `ext-keyset-id-${this.id}`;
    if (this.keysetsMap.has(window)) {
      this.keysetsMap.get(window).remove();
    }
    let sidebarKey;
    commands.forEach((command, name) => {
      if (command.shortcut) {
        let keyElement = this.buildKey(doc, name, command.shortcut);
        keyset.appendChild(keyElement);
        if (name == EXECUTE_SIDEBAR_ACTION) {
          sidebarKey = keyElement;
        }
      }
    });
    doc.documentElement.appendChild(keyset);
    if (sidebarKey) {
      window.SidebarUI.updateShortcut({key: sidebarKey});
    }
    this.keysetsMap.set(window, keyset);
  }

  /**
   * Builds a XUL Key element and attaches an onCommand listener which
   * emits a command event with the provided name when fired.
   *
   * @param {Document} doc The XUL document.
   * @param {string} name The name of the command.
   * @param {string} shortcut The shortcut provided in the manifest.
   * @see https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL/key
   *
   * @returns {Document} The newly created Key element.
   */
  buildKey(doc, name, shortcut) {
    let keyElement = this.buildKeyFromShortcut(doc, name, shortcut);

    // We need to have the attribute "oncommand" for the "command" listener to fire,
    // and it is currently ignored when set to the empty string.
    keyElement.setAttribute("oncommand", "//");

    /* eslint-disable mozilla/balanced-listeners */
    // We remove all references to the key elements when the extension is shutdown,
    // therefore the listeners for these elements will be garbage collected.
    keyElement.addEventListener("command", (event) => {
      let action;
      if (name == EXECUTE_PAGE_ACTION) {
        action = pageActionFor(this.extension);
      } else if (name == EXECUTE_BROWSER_ACTION) {
        action = browserActionFor(this.extension);
      } else if (name == EXECUTE_SIDEBAR_ACTION) {
        action = sidebarActionFor(this.extension);
      } else {
        this.extension.tabManager
            .addActiveTabPermission();
        this.emit("command", name);
        return;
      }
      if (action) {
        let win = event.target.ownerGlobal;
        action.triggerAction(win);
      }
    });
    /* eslint-enable mozilla/balanced-listeners */

    return keyElement;
  }

  /**
   * Builds a XUL Key element from the provided shortcut.
   *
   * @param {Document} doc The XUL document.
   * @param {string} name The name of the shortcut.
   * @param {string} shortcut The shortcut provided in the manifest.
   *
   * @see https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL/key
   * @returns {Document} The newly created Key element.
   */
  buildKeyFromShortcut(doc, name, shortcut) {
    let keyElement = doc.createElementNS(XUL_NS, "key");

    let parts = shortcut.split("+");

    // The key is always the last element.
    let chromeKey = parts.pop();

    // The modifiers are the remaining elements.
    keyElement.setAttribute("modifiers", this.getModifiersAttribute(parts));
    if (name == EXECUTE_SIDEBAR_ACTION) {
      let id = `ext-key-id-${this.id}-sidebar-action`;
      keyElement.setAttribute("id", id);
    }

    if (/^[A-Z]$/.test(chromeKey)) {
      // We use the key attribute for all single digits and characters.
      keyElement.setAttribute("key", chromeKey);
    } else {
      keyElement.setAttribute("keycode", this.getKeycodeAttribute(chromeKey));
      keyElement.setAttribute("event", "keydown");
    }

    return keyElement;
  }

  /**
   * Determines the corresponding XUL keycode from the given chrome key.
   *
   * For example:
   *
   *    input     |  output
   *    ---------------------------------------
   *    "PageUP"  |  "VK_PAGE_UP"
   *    "Delete"  |  "VK_DELETE"
   *
   * @param {string} chromeKey The chrome key (e.g. "PageUp", "Space", ...)
   * @returns {string} The constructed value for the Key's 'keycode' attribute.
   */
  getKeycodeAttribute(chromeKey) {
    if (/[0-9]/.test(chromeKey)) {
      return `VK_${chromeKey}`;
    }
    return `VK${chromeKey.replace(/([A-Z])/g, "_$&").toUpperCase()}`;
  }

  /**
   * Determines the corresponding XUL modifiers from the chrome modifiers.
   *
   * For example:
   *
   *    input             |   output
   *    ---------------------------------------
   *    ["Ctrl", "Shift"] |   "accel shift"
   *    ["MacCtrl"]       |   "control"
   *
   * @param {Array} chromeModifiers The array of chrome modifiers.
   * @returns {string} The constructed value for the Key's 'modifiers' attribute.
   */
  getModifiersAttribute(chromeModifiers) {
    let modifiersMap = {
      "Alt": "alt",
      "Command": "accel",
      "Ctrl": "accel",
      "MacCtrl": "control",
      "Shift": "shift",
    };
    return Array.from(chromeModifiers, modifier => {
      return modifiersMap[modifier];
    }).join(" ");
  }

  getAPI(context) {
    return {
      commands: {
        getAll: async () => {
          let commands = await this.commands;
          return Array.from(commands, ([name, command]) => {
            return ({
              name,
              description: command.description,
              shortcut: command.shortcut,
            });
          });
        },
        update: async ({name, description, shortcut}) => {
          let {extension} = this;
          let commands = await this.commands;
          let command = commands.get(name);

          if (!command) {
            throw new ExtensionError(`Unknown command "${name}"`);
          }

          // Only store the updates so manifest changes can take precedence
          // later.
          let previousUpdates = await ExtensionSettingsStore.getSetting(
            "commands", name, extension.id);
          let commandUpdates = (previousUpdates && previousUpdates.value) || {};

          if (description && description != command.description) {
            commandUpdates.description = description;
            command.description = description;
          }

          if (shortcut && shortcut != command.shortcut) {
            shortcut = normalizeShortcut(shortcut);
            commandUpdates.shortcut = shortcut;
            command.shortcut = shortcut;
          }

          await ExtensionSettingsStore.addSetting(
            extension.id, "commands", name, commandUpdates);

          this.registerKeys(commands);
        },
        reset: async (name) => {
          let {extension, manifestCommands} = this;
          let commands = await this.commands;
          let command = commands.get(name);

          if (!command) {
            throw new ExtensionError(`Unknown command "${name}"`);
          }

          let storedCommand = ExtensionSettingsStore.getSetting(
            "commands", name, extension.id);

          if (storedCommand && storedCommand.value) {
            commands.set(name, {...manifestCommands.get(name)});
            ExtensionSettingsStore.removeSetting(extension.id, "commands", name);
            this.registerKeys(commands);
          }
        },
        onCommand: new EventManager(context, "commands.onCommand", fire => {
          let listener = (eventName, commandName) => {
            fire.async(commandName);
          };
          this.on("command", listener);
          return () => {
            this.off("command", listener);
          };
        }).api(),
      },
    };
  }
};
PK
!<S����>chrome/browser/content/browser/ext-devtools-inspectedWindow.js/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";

// The ext-* files are imported into the same scopes.
/* import-globals-from ext-devtools.js */
/* import-globals-from ext-browser.js */

var {
  SpreadArgs,
} = ExtensionCommon;

this.devtools_inspectedWindow = class extends ExtensionAPI {
  getAPI(context) {
    // Lazily retrieved inspectedWindow actor front per child context.
    let waitForInspectedWindowFront;

    // TODO - Bug 1448878: retrive a more detailed callerInfo object,
    // like the filename and lineNumber of the actual extension called
    // in the child process.
    const callerInfo = {
      addonId: context.extension.id,
      url: context.extension.baseURI.spec,
    };

    return {
      devtools: {
        inspectedWindow: {
          async eval(expression, options) {
            if (!waitForInspectedWindowFront) {
              waitForInspectedWindowFront = getInspectedWindowFront(context);
            }

            const front = await waitForInspectedWindowFront;

            const evalOptions = Object.assign({}, options, getToolboxEvalOptions(context));

            const evalResult = await front.eval(callerInfo, expression, evalOptions);

            // TODO(rpl): check for additional undocumented behaviors on chrome
            // (e.g. if we should also print error to the console or set lastError?).
            return new SpreadArgs([evalResult.value, evalResult.exceptionInfo]);
          },
          async reload(options) {
            const {ignoreCache, userAgent, injectedScript} = options || {};

            if (!waitForInspectedWindowFront) {
              waitForInspectedWindowFront = getInspectedWindowFront(context);
            }

            const front = await waitForInspectedWindowFront;
            front.reload(callerInfo, {ignoreCache, userAgent, injectedScript});
          },
        },
      },
    };
  }
};
PK
!<��Pu		6chrome/browser/content/browser/ext-devtools-network.js/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";

// The ext-* files are imported into the same scopes.
/* import-globals-from ext-devtools.js */

var {
  SpreadArgs,
} = ExtensionCommon;

this.devtools_network = class extends ExtensionAPI {
  getAPI(context) {
    return {
      devtools: {
        network: {
          onNavigated: new EventManager(context, "devtools.onNavigated", fire => {
            let listener = (event, data) => {
              fire.async(data.url);
            };

            let targetPromise = getDevToolsTargetForContext(context);
            targetPromise.then(target => {
              target.on("navigate", listener);
            });
            return () => {
              targetPromise.then(target => {
                target.off("navigate", listener);
              });
            };
          }).api(),

          getHAR: function() {
            return context.devToolsToolbox.getHARFromNetMonitor();
          },

          onRequestFinished: new EventManager(context, "devtools.network.onRequestFinished", fire => {
            const listener = (data) => {
              fire.async(data);
            };

            const toolbox = context.devToolsToolbox;
            toolbox.addRequestFinishedListener(listener);

            return () => {
              toolbox.removeRequestFinishedListener(listener);
            };
          }).api(),

          // The following method is used internally to allow the request API
          // piece that is running in the child process to ask the parent process
          // to fetch response content from the back-end.
          Request: {
            async getContent(requestId) {
              return context.devToolsToolbox.fetchResponseContent(requestId)
                .then(({content}) => new SpreadArgs([content.text, content.mimeType]))
                .catch(err => {
                  const debugName = context.extension.policy.debugName;
                  const errorMsg = "Unexpected error while fetching response content";
                  Cu.reportError(`${debugName}: ${errorMsg} for ${requestId}: ${err}`);
                  throw new ExtensionError(errorMsg);
                });
            },
          },
        },
      },
    };
  }
};
PK
!<UEC�yPyP5chrome/browser/content/browser/ext-devtools-panels.js/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";

// The ext-* files are imported into the same scopes.
/* import-globals-from ext-devtools.js */
/* import-globals-from ext-browser.js */

ChromeUtils.import("resource://gre/modules/ExtensionParent.jsm");

ChromeUtils.defineModuleGetter(this, "E10SUtils",
                               "resource://gre/modules/E10SUtils.jsm");

var {
  IconDetails,
  watchExtensionProxyContextLoad,
} = ExtensionParent;

var {
  promiseEvent,
} = ExtensionUtils;

var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";

/**
 * Represents an addon devtools panel in the main process.
 *
 * @param {ExtensionChildProxyContext} context
 *        A devtools extension proxy context running in a main process.
 * @param {object} options
 * @param {string} options.id
 *        The id of the addon devtools panel.
 * @param {string} options.icon
 *        The icon of the addon devtools panel.
 * @param {string} options.title
 *        The title of the addon devtools panel.
 * @param {string} options.url
 *        The url of the addon devtools panel, relative to the extension base URL.
 */
class ParentDevToolsPanel {
  constructor(context, panelOptions) {
    const toolbox = context.devToolsToolbox;
    if (!toolbox) {
      // This should never happen when this constructor is called with a valid
      // devtools extension context.
      throw Error("Missing mandatory toolbox");
    }

    this.extension = context.extension;
    this.viewType = "devtools_panel";

    this.visible = false;
    this.toolbox = toolbox;

    this.context = context;

    this.panelOptions = panelOptions;

    this.context.callOnClose(this);

    this.id = this.panelOptions.id;

    this.onToolboxPanelSelect = this.onToolboxPanelSelect.bind(this);
    this.onToolboxHostWillChange = this.onToolboxHostWillChange.bind(this);
    this.onToolboxHostChanged = this.onToolboxHostChanged.bind(this);

    this.unwatchExtensionProxyContextLoad = null;
    this.waitTopLevelContext = new Promise(resolve => {
      this._resolveTopLevelContext = resolve;
    });

    // References to the panel browser XUL element and the toolbox window global which
    // contains the devtools panel UI.
    this.browser = null;
    this.browserContainerWindow = null;

    this.panelAdded = false;
    this.addPanel();
  }

  addPanel() {
    const {icon, title} = this.panelOptions;
    const extensionName = this.context.extension.name;

    this.toolbox.addAdditionalTool({
      id: this.id,
      url: "chrome://browser/content/webext-panels.xul",
      icon: icon,
      label: title,
      tooltip: `DevTools Panel added by "${extensionName}" add-on.`,
      visibilityswitch:  `devtools.webext-${this.id}.enabled`,
      isTargetSupported: target => target.isLocalTab,
      build: (window, toolbox) => {
        if (toolbox !== this.toolbox) {
          throw new Error("Unexpected toolbox received on addAdditionalTool build property");
        }

        const destroy = this.buildPanel(window);

        return {toolbox, destroy};
      },
    });

    this.panelAdded = true;
  }

  buildPanel(window) {
    const {toolbox} = this;

    this.createBrowserElement(window);

    // Store the last panel's container element (used to restore it when the toolbox
    // host is switched between docked and undocked).
    this.browserContainerWindow = window;

    toolbox.on("select", this.onToolboxPanelSelect);
    toolbox.on("host-will-change", this.onToolboxHostWillChange);
    toolbox.on("host-changed", this.onToolboxHostChanged);

    // Return a cleanup method that is when the panel is destroyed, e.g.
    // - when addon devtool panel has been disabled by the user from the toolbox preferences,
    //   its ParentDevToolsPanel instance is still valid, but the built devtools panel is removed from
    //   the toolbox (and re-built again if the user re-enables it from the toolbox preferences panel)
    // - when the creator context has been destroyed, the ParentDevToolsPanel close method is called,
    //   it removes the tool definition from the toolbox, which will call this destroy method.
    return () => {
      this.destroyBrowserElement();
      this.browserContainerWindow = null;
      toolbox.off("select", this.onToolboxPanelSelect);
      toolbox.off("host-will-change", this.onToolboxHostWillChange);
      toolbox.off("host-changed", this.onToolboxHostChanged);
    };
  }

  onToolboxHostWillChange() {
    // NOTE: Using a content iframe here breaks the devtools panel
    // switching between docked and undocked mode,
    // because of a swapFrameLoader exception (see bug 1075490),
    // destroy the browser and recreate it after the toolbox host has been
    // switched is a reasonable workaround to fix the issue on release and beta
    // Firefox versions (at least until the underlying bug can be fixed).
    if (this.browser) {
      // Fires a panel.onHidden event before destroying the browser element because
      // the toolbox hosts is changing.
      if (this.visible) {
        this.context.parentMessageManager.sendAsyncMessage("Extension:DevToolsPanelHidden", {
          toolboxPanelId: this.id,
        });
      }

      this.destroyBrowserElement();
    }
  }

  async onToolboxHostChanged() {
    if (this.browserContainerWindow) {
      this.createBrowserElement(this.browserContainerWindow);

      // Fires a panel.onShown event once the browser element has been recreated
      // after the toolbox hosts has been changed (needed to provide the new window
      // object to the extension page that has created the devtools panel).
      if (this.visible) {
        await this.waitTopLevelContext;

        this.context.parentMessageManager.sendAsyncMessage("Extension:DevToolsPanelShown", {
          toolboxPanelId: this.id,
        });
      }
    }
  }

  async onToolboxPanelSelect(what, id) {
    if (!this.waitTopLevelContext || !this.panelAdded) {
      return;
    }

    // Wait that the panel is fully loaded and emit show.
    await this.waitTopLevelContext;

    if (!this.visible && id === this.id) {
      this.visible = true;
    } else if (this.visible && id !== this.id) {
      this.visible = false;
    }

    const extensionMessage = `Extension:DevToolsPanel${this.visible ? "Shown" : "Hidden"}`;
    this.context.parentMessageManager.sendAsyncMessage(extensionMessage, {
      toolboxPanelId: this.id,
    });
  }

  close() {
    const {toolbox} = this;

    if (!toolbox) {
      throw new Error("Unable to destroy a closed devtools panel");
    }

    // Explicitly remove the panel if it is registered and the toolbox is not
    // closing itself.
    if (this.panelAdded && toolbox.isToolRegistered(this.id)) {
      toolbox.removeAdditionalTool(this.id);
    }

    this.waitTopLevelContext = null;
    this._resolveTopLevelContext = null;
    this.context = null;
    this.toolbox = null;
    this.browser = null;
    this.browserContainerWindow = null;
  }

  createBrowserElement(window) {
    const {toolbox} = this;
    const {extension} = this.context;
    const {url} = this.panelOptions;
    const {document} = window;

    // TODO Bug 1442601: Refactor ext-devtools-panels.js to reuse the helpers
    // functions defined in webext-panels.xul (e.g. create the browser element
    // using an helper function defined in webext-panels.js and shared with the
    // extension sidebar pages).
    let stack = document.getElementById("webext-panels-stack");
    if (!stack) {
      stack = document.createElementNS(XUL_NS, "stack");
      stack.setAttribute("flex", "1");
      stack.setAttribute("id", "webext-panels-stack");
      document.documentElement.appendChild(stack);
    }

    const browser = document.createElementNS(XUL_NS, "browser");
    browser.setAttribute("id", "webext-panels-browser");
    browser.setAttribute("type", "content");
    browser.setAttribute("disableglobalhistory", "true");
    browser.setAttribute("flex", "1");
    browser.setAttribute("class", "webextension-devtoolsPanel-browser");
    browser.setAttribute("webextension-view-type", "devtools_panel");
    // TODO Bug 1442604: Add additional tests for the select and autocompletion
    // popups used in an extension devtools panels (in oop and non-oop mode).
    browser.setAttribute("selectmenulist", "ContentSelectDropdown");
    browser.setAttribute("autocompletepopup", "PopupAutoComplete");

    // Ensure that the devtools panel browser is going to run in the same
    // process of the other extension pages from the same addon.
    browser.sameProcessAsFrameLoader = extension.groupFrameLoader;

    this.browser = browser;

    let awaitFrameLoader = Promise.resolve();
    if (extension.remote) {
      browser.setAttribute("remote", "true");
      browser.setAttribute("remoteType", E10SUtils.EXTENSION_REMOTE_TYPE);
      awaitFrameLoader = promiseEvent(browser, "XULFrameLoaderCreated");
    }

    let hasTopLevelContext = false;

    // Listening to new proxy contexts.
    this.unwatchExtensionProxyContextLoad = watchExtensionProxyContextLoad(this, context => {
      // Keep track of the toolbox and target associated to the context, which is
      // needed by the API methods implementation.
      context.devToolsToolbox = toolbox;

      if (!hasTopLevelContext) {
        hasTopLevelContext = true;

        // Resolve the promise when the root devtools_panel context has been created.
        awaitFrameLoader.then(() => this._resolveTopLevelContext(context));
      }
    });

    stack.appendChild(browser);

    extensions.emit("extension-browser-inserted", browser, {
      devtoolsToolboxInfo: {
        toolboxPanelId: this.id,
        inspectedWindowTabId: getTargetTabIdForToolbox(toolbox),
      },
    });

    browser.loadURIWithFlags(url, {
      triggeringPrincipal: extension.principal,
    });
  }

  destroyBrowserElement() {
    const {browser, unwatchExtensionProxyContextLoad} = this;
    if (unwatchExtensionProxyContextLoad) {
      this.unwatchExtensionProxyContextLoad = null;
      unwatchExtensionProxyContextLoad();
    }

    if (browser) {
      browser.remove();
      this.browser = null;
    }

    // If the panel has been removed or disabled (e.g. from the toolbox preferences
    // or during the toolbox switching between docked and undocked),
    // we need to re-initialize the waitTopLevelContext Promise.
    this.waitTopLevelContext = new Promise(resolve => {
      this._resolveTopLevelContext = resolve;
    });
  }
}

class DevToolsSelectionObserver extends EventEmitter {
  constructor(context) {
    if (!context.devToolsToolbox) {
      // This should never happen when this constructor is called with a valid
      // devtools extension context.
      throw Error("Missing mandatory toolbox");
    }

    super();
    context.callOnClose(this);

    this.toolbox = context.devToolsToolbox;
    this.onSelected = this.onSelected.bind(this);
    this.initialized = false;
  }

  on(...args) {
    this.lazyInit();
    super.on.apply(this, args);
  }

  once(...args) {
    this.lazyInit();
    super.once.apply(this, args);
  }

  async lazyInit() {
    if (!this.initialized) {
      this.initialized = true;
      this.toolbox.on("selection-changed", this.onSelected);
    }
  }

  close() {
    if (this.destroyed) {
      throw new Error("Unable to close a destroyed DevToolsSelectionObserver");
    }

    if (this.initialized) {
      this.toolbox.off("selection-changed", this.onSelected);
    }

    this.toolbox = null;
    this.destroyed = true;
  }

  onSelected(event) {
    this.emit("selectionChanged");
  }
}

/**
 * Represents an addon devtools inspector sidebar in the main process.
 *
 * @param {ExtensionChildProxyContext} context
 *        A devtools extension proxy context running in a main process.
 * @param {object} options
 * @param {string} options.id
 *        The id of the addon devtools sidebar.
 * @param {string} options.title
 *        The title of the addon devtools sidebar.
 */
class ParentDevToolsInspectorSidebar {
  constructor(context, sidebarOptions) {
    const toolbox = context.devToolsToolbox;
    if (!toolbox) {
      // This should never happen when this constructor is called with a valid
      // devtools extension context.
      throw Error("Missing mandatory toolbox");
    }

    this.extension = context.extension;
    this.visible = false;
    this.destroyed = false;

    this.toolbox = toolbox;
    this.context = context;
    this.sidebarOptions = sidebarOptions;

    this.context.callOnClose(this);

    this.id = this.sidebarOptions.id;
    this.onSidebarSelect = this.onSidebarSelect.bind(this);
    this.onSidebarCreated = this.onSidebarCreated.bind(this);

    this.toolbox.once(`extension-sidebar-created-${this.id}`, this.onSidebarCreated);
    this.toolbox.on(`inspector-sidebar-select`, this.onSidebarSelect);

    // Set by setObject if the sidebar has not been created yet.
    this._initializeSidebar = null;

    // Set by _updateLastObjectValueGrip to keep track of the last
    // object value grip (to release the previous selected actor
    // on the remote debugging server when the actor changes).
    this._lastObjectValueGrip = null;

    this.toolbox.registerInspectorExtensionSidebar(this.id, {
      title: sidebarOptions.title,
    });
  }

  close() {
    if (this.destroyed) {
      throw new Error("Unable to close a destroyed DevToolsSelectionObserver");
    }

    // Release the last selected actor on the remote debugging server.
    this._updateLastObjectValueGrip(null);

    this.toolbox.off(`extension-sidebar-created-${this.id}`, this.onSidebarCreated);
    this.toolbox.off(`inspector-sidebar-select`, this.onSidebarSelect);

    this.toolbox.unregisterInspectorExtensionSidebar(this.id);
    this.extensionSidebar = null;
    this._lazySidebarInit = null;

    this.destroyed = true;
  }

  onSidebarCreated(evt, sidebar) {
    this.extensionSidebar = sidebar;

    const {_lazySidebarInit} = this;
    this._lazySidebarInit = null;

    if (typeof _lazySidebarInit === "function") {
      _lazySidebarInit();
    }
  }

  onSidebarSelect(what, id) {
    if (!this.extensionSidebar) {
      return;
    }

    if (!this.visible && id === this.id) {
      // TODO: Wait for top level context if extension page
      this.visible = true;
      this.context.parentMessageManager.sendAsyncMessage("Extension:DevToolsInspectorSidebarShown", {
        inspectorSidebarId: this.id,
      });
    } else if (this.visible && id !== this.id) {
      this.visible = false;
      this.context.parentMessageManager.sendAsyncMessage("Extension:DevToolsInspectorSidebarHidden", {
        inspectorSidebarId: this.id,
      });
    }
  }

  setObject(object, rootTitle) {
    this._updateLastObjectValueGrip(null);

    // Nest the object inside an object, as the value of the `rootTitle` property.
    if (rootTitle) {
      object = {[rootTitle]: object};
    }

    if (this.extensionSidebar) {
      this.extensionSidebar.setObject(object);
    } else {
      // Defer the sidebar.setObject call.
      this._setLazySidebarInit(() => this.extensionSidebar.setObject(object));
    }
  }

  _setLazySidebarInit(cb) {
    this._lazySidebarInit = cb;
  }

  setObjectValueGrip(objectValueGrip, rootTitle) {
    this._updateLastObjectValueGrip(objectValueGrip);

    if (this.extensionSidebar) {
      this.extensionSidebar.setObjectValueGrip(objectValueGrip, rootTitle);
    } else {
      // Defer the sidebar.setObjectValueGrip call.
      this._setLazySidebarInit(() => {
        this.extensionSidebar.setObjectValueGrip(objectValueGrip, rootTitle);
      });
    }
  }

  _updateLastObjectValueGrip(newObjectValueGrip = null) {
    const {_lastObjectValueGrip} = this;

    this._lastObjectValueGrip = newObjectValueGrip;

    const oldActor = _lastObjectValueGrip && _lastObjectValueGrip.actor;
    const newActor = newObjectValueGrip && newObjectValueGrip.actor;

    // Release the previously active actor on the remote debugging server.
    if (oldActor && oldActor !== newActor) {
      this.toolbox.target.client.release(oldActor);
    }
  }
}

const sidebarsById = new Map();

this.devtools_panels = class extends ExtensionAPI {
  getAPI(context) {
    // Lazily retrieved inspectedWindow actor front per child context
    // (used by Sidebar.setExpression).
    let waitForInspectedWindowFront;

    // TODO - Bug 1448878: retrive a more detailed callerInfo object,
    // like the filename and lineNumber of the actual extension called
    // in the child process.
    const callerInfo = {
      addonId: context.extension.id,
      url: context.extension.baseURI.spec,
    };

    // An incremental "per context" id used in the generated devtools panel id.
    let nextPanelId = 0;

    const toolboxSelectionObserver = new DevToolsSelectionObserver(context);

    function newBasePanelId() {
      return `${context.extension.id}-${context.contextId}-${nextPanelId++}`;
    }

    return {
      devtools: {
        panels: {
          elements: {
            onSelectionChanged: new EventManager(
              context, "devtools.panels.elements.onSelectionChanged", fire => {
                const listener = (eventName) => {
                  fire.async();
                };
                toolboxSelectionObserver.on("selectionChanged", listener);
                return () => {
                  toolboxSelectionObserver.off("selectionChanged", listener);
                };
              }).api(),
            createSidebarPane(title) {
              const id = `devtools-inspector-sidebar-${makeWidgetId(newBasePanelId())}`;

              const parentSidebar = new ParentDevToolsInspectorSidebar(context, {title, id});
              sidebarsById.set(id, parentSidebar);

              context.callOnClose({
                close() {
                  sidebarsById.delete(id);
                },
              });

              // Resolved to the devtools sidebar id into the child addon process,
              // where it will be used to identify the messages related
              // to the panel API onShown/onHidden events.
              return Promise.resolve(id);
            },
            // The following methods are used internally to allow the sidebar API
            // piece that is running in the child process to asks the parent process
            // to execute the sidebar methods.
            Sidebar: {
              setObject(sidebarId, jsonObject, rootTitle) {
                const sidebar = sidebarsById.get(sidebarId);
                return sidebar.setObject(jsonObject, rootTitle);
              },
              async setExpression(sidebarId, evalExpression, rootTitle) {
                const sidebar = sidebarsById.get(sidebarId);

                if (!waitForInspectedWindowFront) {
                  waitForInspectedWindowFront = getInspectedWindowFront(context);
                }

                const front = await waitForInspectedWindowFront;
                const evalOptions = Object.assign({
                  evalResultAsGrip: true,
                }, getToolboxEvalOptions(context));
                const evalResult = await front.eval(callerInfo, evalExpression, evalOptions);

                let jsonObject;

                if (evalResult.exceptionInfo) {
                  jsonObject = evalResult.exceptionInfo;

                  return sidebar.setObject(jsonObject, rootTitle);
                }

                return sidebar.setObjectValueGrip(evalResult.valueGrip, rootTitle);
              },
            },
          },
          create(title, icon, url) {
            // Get a fallback icon from the manifest data.
            if (icon === "" && context.extension.manifest.icons) {
              const iconInfo = IconDetails.getPreferredIcon(context.extension.manifest.icons,
                                                            context.extension, 128);
              icon = iconInfo ? iconInfo.icon : "";
            }

            icon = context.extension.baseURI.resolve(icon);
            url = context.extension.baseURI.resolve(url);

            const id = `${makeWidgetId(newBasePanelId())}-devtools-panel`;

            new ParentDevToolsPanel(context, {title, icon, url, id});

            // Resolved to the devtools panel id into the child addon process,
            // where it will be used to identify the messages related
            // to the panel API onShown/onHidden events.
            return Promise.resolve(id);
          },
        },
      },
    };
  }
};
PK
!<�;Z22.chrome/browser/content/browser/ext-devtools.js/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";

/* exported getDevToolsTargetForContext, getInspectedWindowFront, getToolboxEvalOptions */
/* global getTargetTabIdForToolbox, getDevToolsTargetForContext, getInspectedWindowFront, getToolboxEvalOptions */

// The ext-* files are imported into the same scopes.
/* import-globals-from ext-browser.js */

/**
 * This module provides helpers used by the other specialized `ext-devtools-*.js` modules
 * and the implementation of the `devtools_page`.
 */

ChromeUtils.defineModuleGetter(this, "DevToolsShim",
                               "chrome://devtools-shim/content/DevToolsShim.jsm");

ChromeUtils.import("resource://gre/modules/ExtensionParent.jsm");

var {
  HiddenExtensionPage,
  watchExtensionProxyContextLoad,
} = ExtensionParent;

// Map[extension -> DevToolsPageDefinition]
let devtoolsPageDefinitionMap = new Map();

let initDevTools;

/**
 * Retrieve the devtools target for the devtools extension proxy context
 * (lazily cloned from the target of the toolbox associated to the context
 * the first time that it is accessed).
 *
 * @param {DevToolsExtensionPageContextParent} context
 *   A devtools extension proxy context.
 *
 * @returns {Promise<TabTarget>}
 *   The cloned devtools target associated to the context.
 */
global.getDevToolsTargetForContext = async (context) => {
  if (context.devToolsTarget) {
    await context.devToolsTarget.makeRemote();
    return context.devToolsTarget;
  }

  if (!context.devToolsToolbox || !context.devToolsToolbox.target) {
    throw new Error("Unable to get a TabTarget for a context not associated to any toolbox");
  }

  if (!context.devToolsToolbox.target.isLocalTab) {
    throw new Error("Unexpected target type: only local tabs are currently supported.");
  }

  const tab = context.devToolsToolbox.target.tab;
  context.devToolsTarget = DevToolsShim.createTargetForTab(tab);

  await context.devToolsTarget.makeRemote();

  return context.devToolsTarget;
};

/**
 * Retrieve the devtools target for the devtools extension proxy context
 * (lazily cloned from the target of the toolbox associated to the context
 * the first time that it is accessed).
 *
 * @param {Toolbox} toolbox
 *   A devtools toolbox instance.
 *
 * @returns {number}
 *   The corresponding WebExtensions tabId.
 */
global.getTargetTabIdForToolbox = (toolbox) => {
  let {target} = toolbox;

  if (!target.isLocalTab) {
    throw new Error("Unexpected target type: only local tabs are currently supported.");
  }

  let parentWindow = target.tab.linkedBrowser.ownerGlobal;
  let tab = parentWindow.gBrowser.getTabForBrowser(target.tab.linkedBrowser);

  return tabTracker.getId(tab);
};

// Create an InspectedWindowFront instance for a given context (used in devtoools.inspectedWindow.eval
// and in sidebar.setExpression API methods).
global.getInspectedWindowFront = async function(context) {
  // If there is not yet a front instance, then a lazily cloned target for the context is
  // retrieved using the DevtoolsParentContextsManager helper (which is an asynchronous operation,
  // because the first time that the target has been cloned, it is not ready to be used to create
  // the front instance until it is connected to the remote debugger successfully).
  const clonedTarget = await getDevToolsTargetForContext(context);
  return DevToolsShim.createWebExtensionInspectedWindowFront(clonedTarget);
};

// Get the WebExtensionInspectedWindowActor eval options (needed to provide the $0 and inspect
// binding provided to the evaluated js code).
global.getToolboxEvalOptions = function(context) {
  const options = {};
  const toolbox = context.devToolsToolbox;
  const selectedNode = toolbox.selection;

  if (selectedNode && selectedNode.nodeFront) {
    // If there is a selected node in the inspector, we hand over
    // its actor id to the eval request in order to provide the "$0" binding.
    options.toolboxSelectedNodeActorID = selectedNode.nodeFront.actorID;
  }

  // Provide the console actor ID to implement the "inspect" binding.
  options.toolboxConsoleActorID = toolbox.target.form.consoleActor;

  return options;
};

/**
 * The DevToolsPage represents the "devtools_page" related to a particular
 * Toolbox and WebExtension.
 *
 * The devtools_page contexts are invisible WebExtensions contexts, similar to the
 * background page, associated to a single developer toolbox (e.g. If an add-on
 * registers a devtools_page and the user opens 3 developer toolbox in 3 webpages,
 * 3 devtools_page contexts will be created for that add-on).
 *
 * @param {Extension}              extension
 *   The extension that owns the devtools_page.
 * @param {Object}                 options
 * @param {Toolbox}                options.toolbox
 *   The developer toolbox instance related to this devtools_page.
 * @param {string}                 options.url
 *   The path to the devtools page html page relative to the extension base URL.
 * @param {DevToolsPageDefinition} options.devToolsPageDefinition
 *   The instance of the devToolsPageDefinition class related to this DevToolsPage.
 */
class DevToolsPage extends HiddenExtensionPage {
  constructor(extension, options) {
    super(extension, "devtools_page");

    this.url = extension.baseURI.resolve(options.url);
    this.toolbox = options.toolbox;
    this.devToolsPageDefinition = options.devToolsPageDefinition;

    this.unwatchExtensionProxyContextLoad = null;

    this.waitForTopLevelContext = new Promise(resolve => {
      this.resolveTopLevelContext = resolve;
    });
  }

  async build() {
    await this.createBrowserElement();

    // Listening to new proxy contexts.
    this.unwatchExtensionProxyContextLoad = watchExtensionProxyContextLoad(this, context => {
      // Keep track of the toolbox and target associated to the context, which is
      // needed by the API methods implementation.
      context.devToolsToolbox = this.toolbox;

      if (!this.topLevelContext) {
        this.topLevelContext = context;

        // Ensure this devtools page is destroyed, when the top level context proxy is
        // closed.
        this.topLevelContext.callOnClose(this);

        this.resolveTopLevelContext(context);
      }
    });

    extensions.emit("extension-browser-inserted", this.browser, {
      devtoolsToolboxInfo: {
        inspectedWindowTabId: getTargetTabIdForToolbox(this.toolbox),
        themeName: DevToolsShim.getTheme(),
      },
    });

    this.browser.loadURIWithFlags(this.url, {
      triggeringPrincipal: this.extension.principal,
    });

    await this.waitForTopLevelContext;
  }

  close() {
    if (this.closed) {
      throw new Error("Unable to shutdown a closed DevToolsPage instance");
    }

    this.closed = true;

    // Unregister the devtools page instance from the devtools page definition.
    this.devToolsPageDefinition.forgetForTarget(this.toolbox.target);

    // Unregister it from the resources to cleanup when the context has been closed.
    if (this.topLevelContext) {
      this.topLevelContext.forgetOnClose(this);
    }

    // Stop watching for any new proxy contexts from the devtools page.
    if (this.unwatchExtensionProxyContextLoad) {
      this.unwatchExtensionProxyContextLoad();
      this.unwatchExtensionProxyContextLoad = null;
    }

    super.shutdown();
  }
}

/**
 * The DevToolsPageDefinitions class represents the "devtools_page" manifest property
 * of a WebExtension.
 *
 * A DevToolsPageDefinition instance is created automatically when a WebExtension
 * which contains the "devtools_page" manifest property has been loaded, and it is
 * automatically destroyed when the related WebExtension has been unloaded,
 * and so there will be at most one DevtoolsPageDefinition per add-on.
 *
 * Every time a developer tools toolbox is opened, the DevToolsPageDefinition creates
 * and keep track of a DevToolsPage instance (which represents the actual devtools_page
 * instance related to that particular toolbox).
 *
 * @param {Extension} extension
 *   The extension that owns the devtools_page.
 * @param {string}    url
 *   The path to the devtools page html page relative to the extension base URL.
 */
class DevToolsPageDefinition {
  constructor(extension, url) {
    initDevTools();

    this.url = url;
    this.extension = extension;

    // Map[TabTarget -> DevToolsPage]
    this.devtoolsPageForTarget = new Map();
  }

  onThemeChanged(evt, themeName) {
    Services.ppmm.broadcastAsyncMessage("Extension:DevToolsThemeChanged", {themeName});
  }

  buildForToolbox(toolbox) {
    if (this.devtoolsPageForTarget.has(toolbox.target)) {
      return Promise.reject(new Error("DevtoolsPage has been already created for this toolbox"));
    }

    const devtoolsPage = new DevToolsPage(this.extension, {
      toolbox, url: this.url, devToolsPageDefinition: this,
    });

    // If this is the first DevToolsPage, subscribe to the theme-changed event
    if (this.devtoolsPageForTarget.size === 0) {
      DevToolsShim.on("theme-changed", this.onThemeChanged);
    }
    this.devtoolsPageForTarget.set(toolbox.target, devtoolsPage);

    return devtoolsPage.build();
  }

  shutdownForTarget(target) {
    if (this.devtoolsPageForTarget.has(target)) {
      const devtoolsPage = this.devtoolsPageForTarget.get(target);
      devtoolsPage.close();

      // `devtoolsPage.close()` should remove the instance from the map,
      // raise an exception if it is still there.
      if (this.devtoolsPageForTarget.has(target)) {
        throw new Error(`Leaked DevToolsPage instance for target "${target.toString()}"`);
      }

      // If this was the last DevToolsPage, unsubscribe from the theme-changed event
      if (this.devtoolsPageForTarget.size === 0) {
        DevToolsShim.off("theme-changed", this.onThemeChanged);
      }
    }
  }

  forgetForTarget(target) {
    this.devtoolsPageForTarget.delete(target);
  }

  shutdown() {
    for (let target of this.devtoolsPageForTarget.keys()) {
      this.shutdownForTarget(target);
    }

    if (this.devtoolsPageForTarget.size > 0) {
      throw new Error(
        `Leaked ${this.devtoolsPageForTarget.size} DevToolsPage instances in devtoolsPageForTarget Map`
      );
    }
  }
}


let devToolsInitialized = false;
initDevTools = function() {
  if (devToolsInitialized) {
    return;
  }

  /* eslint-disable mozilla/balanced-listeners */
  // Create a devtools page context for a new opened toolbox,
  // based on the registered devtools_page definitions.
  DevToolsShim.on("toolbox-created", (evt, toolbox) => {
    if (!toolbox.target.isLocalTab) {
      // Only local tabs are currently supported (See Bug 1304378 for additional details
      // related to remote targets support).
      let msg = `Ignoring DevTools Toolbox for target "${toolbox.target.toString()}": ` +
                `"${toolbox.target.name}" ("${toolbox.target.url}"). ` +
                "Only local tab are currently supported by the WebExtensions DevTools API.";
      let scriptError = Cc["@mozilla.org/scripterror;1"].createInstance(Ci.nsIScriptError);
      scriptError.init(msg, null, null, null, null, Ci.nsIScriptError.warningFlag, "content javascript");
      Services.console.logMessage(scriptError);

      return;
    }

    for (let devtoolsPage of devtoolsPageDefinitionMap.values()) {
      devtoolsPage.buildForToolbox(toolbox);
    }
  });

  // Destroy a devtools page context for a destroyed toolbox,
  // based on the registered devtools_page definitions.
  DevToolsShim.on("toolbox-destroy", (evt, target) => {
    if (!target.isLocalTab) {
      // Only local tabs are currently supported (See Bug 1304378 for additional details
      // related to remote targets support).
      return;
    }

    for (let devtoolsPageDefinition of devtoolsPageDefinitionMap.values()) {
      devtoolsPageDefinition.shutdownForTarget(target);
    }
  });
  /* eslint-enable mozilla/balanced-listeners */

  devToolsInitialized = true;
};

this.devtools = class extends ExtensionAPI {
  onManifestEntry(entryName) {
    let {extension} = this;
    let {manifest} = extension;

    // Create and register a new devtools_page definition as specified in the
    // "devtools_page" property in the extension manifest.
    let devtoolsPageDefinition = new DevToolsPageDefinition(extension, manifest.devtools_page);
    devtoolsPageDefinitionMap.set(extension, devtoolsPageDefinition);
  }

  onShutdown(reason) {
    let {extension} = this;

    // Destroy the registered devtools_page definition on extension shutdown.
    if (devtoolsPageDefinitionMap.has(extension)) {
      devtoolsPageDefinitionMap.get(extension).shutdown();
      devtoolsPageDefinitionMap.delete(extension);
    }
  }

  getAPI(context) {
    return {
      devtools: {},
    };
  }
};
PK
!<HB`���*chrome/browser/content/browser/ext-find.js/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
/* global tabTracker */
"use strict";

/**
 * runFindOperation
 * Utility for `find` and `highlightResults`.
 *
 * @param {object} params - params to pass to message sender.
 * @param {string} message - identifying component of message name.
 *
 * @returns {Promise} a promise that will be resolved or rejected based on the
 *          data received by the message listener.
 */
function runFindOperation(params, message) {
  let {tabId} = params;
  let tab = tabId ? tabTracker.getTab(tabId) : tabTracker.activeTab;
  let browser = tab.linkedBrowser;
  let mm = browser.messageManager;
  tabId = tabId || tabTracker.getId(tab);

  // We disallow find in about: urls.
  if (tab.linkedBrowser.contentPrincipal.isSystemPrincipal ||
      (["about", "chrome", "resource"].includes(tab.linkedBrowser.currentURI.scheme) &&
      tab.linkedBrowser.currentURI.spec != "about:blank")) {
    return Promise.reject({message: `Unable to search: ${tabId}`});
  }

  return new Promise((resolve, reject) => {
    mm.addMessageListener(`ext-Finder:${message}Finished`, function messageListener(message) {
      mm.removeMessageListener(`ext-Finder:${message}Finished`, messageListener);
      switch (message.data) {
        case "Success":
          resolve();
          break;
        case "OutOfRange":
          reject({message: "index supplied was out of range"});
          break;
        case "NoResults":
          reject({message: "no search results to highlight"});
          break;
      }
      resolve(message.data);
    });
    mm.sendAsyncMessage(`ext-Finder:${message}`, params);
  });
}

this.find = class extends ExtensionAPI {
  getAPI(context) {
    return {
      find: {
        /**
         * browser.find.find
         * Searches document and its frames for a given queryphrase and stores all found
         * Range objects in an array accessible by other browser.find methods.
         *
         * @param {string} queryphrase - The string to search for.
         * @param {object} params optional - may contain any of the following properties,
         *   all of which are optional:
         *   {number} tabId - Tab to query.  Defaults to the active tab.
         *   {boolean} caseSensitive - Highlight only ranges with case sensitive match.
         *   {boolean} entireWord - Highlight only ranges that match entire word.
         *   {boolean} includeRangeData - Whether to return range data.
         *   {boolean} includeRectData - Whether to return rectangle data.
         *
         * @returns {object} data received by the message listener that includes:
         *   {number} count - number of results found.
         *   {array} rangeData (if opted) - serialized representation of ranges found.
         *   {array} rectData (if opted) - rect data of ranges found.
         */
        find(queryphrase, params) {
          params = params || {};
          params.queryphrase = queryphrase;
          return runFindOperation(params, "CollectResults");
        },

        /**
         * browser.find.highlightResults
         * Highlights range(s) found in previous browser.find.find.
         *
         * @param {object} params optional - may contain any of the following properties,
         *   all of which are optional:
         *   {number} rangeIndex - Found range to be highlighted. Default highlights all ranges.
         *   {number} tabId - Tab to highlight.  Defaults to the active tab.
         *   {boolean} noScroll - Don't scroll to highlighted item.
         *
         * @returns {string} - data received by the message listener that may be:
         *   "Success" - Highlighting succeeded.
         *   "OutOfRange" - The index supplied was out of range.
         *   "NoResults" - There were no search results to highlight.
         */
        highlightResults(params) {
          params = params || {};
          return runFindOperation(params, "HighlightResults");
        },

        /**
         * browser.find.removeHighlighting
         * Removes all hightlighting from previous search.
         *
         * @param {number} tabId optional
         *                 Tab to clear highlighting in.  Defaults to the active tab.
         */
        removeHighlighting(tabId) {
          let tab = tabId ? tabTracker.getTab(tabId) : tabTracker.activeTab;
          tab.linkedBrowser.messageManager.sendAsyncMessage("ext-Finder:clearHighlighting");
        },
      },
    };
  }
};
PK
!<�N��,�,3chrome/browser/content/browser/ext-geckoProfiler.js/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";

// The ext-* files are imported into the same scopes.
/* import-globals-from ../../../toolkit/components/extensions/ext-toolkit.js */

ChromeUtils.import("resource://gre/modules/Services.jsm");
Cu.importGlobalProperties(["TextEncoder", "TextDecoder"]);

ChromeUtils.defineModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
ChromeUtils.defineModuleGetter(this, "Subprocess", "resource://gre/modules/Subprocess.jsm");

const PREF_ASYNC_STACK = "javascript.options.asyncstack";
const PREF_SYMBOLS_URL = "extensions.geckoProfiler.symbols.url";
const PREF_GET_SYMBOL_RULES = "extensions.geckoProfiler.getSymbolRules";

const ASYNC_STACKS_ENABLED = Services.prefs.getBoolPref(PREF_ASYNC_STACK, false);

var {
  ExtensionError,
} = ExtensionUtils;

const parseSym = data => {
  const worker = new ChromeWorker("resource://app/modules/ParseBreakpadSymbols-worker.js");
  const promise = new Promise((resolve, reject) => {
    worker.onmessage = (e) => {
      if (e.data.error) {
        reject(e.data.error);
      } else {
        resolve(e.data.result);
      }
    };
  });
  worker.postMessage(data);
  return promise;
};

class NMParser {
  constructor() {
    this._worker = new ChromeWorker("resource://app/modules/ParseNMSymbols-worker.js");
  }

  consume(buffer) {
    this._worker.postMessage({buffer}, [buffer]);
  }

  finish() {
    const promise = new Promise((resolve, reject) => {
      this._worker.onmessage = (e) => {
        if (e.data.error) {
          reject(e.data.error);
        } else {
          resolve(e.data.result);
        }
      };
    });
    this._worker.postMessage({
      finish: true,
      isDarwin: Services.appinfo.OS === "Darwin",
    });
    return promise;
  }
}

class CppFiltParser {
  constructor() {
    this._worker = new ChromeWorker("resource://app/modules/ParseCppFiltSymbols-worker.js");
  }

  consume(buffer) {
    this._worker.postMessage({buffer}, [buffer]);
  }

  finish() {
    const promise = new Promise((resolve, reject) => {
      this._worker.onmessage = (e) => {
        if (e.data.error) {
          reject(e.data.error);
        } else {
          resolve(e.data.result);
        }
      };
    });
    this._worker.postMessage({
      finish: true,
    });
    return promise;
  }
}

const readAllData = async function(pipe, processData) {
  let data;
  while ((data = await pipe.read()) && data.byteLength) {
    processData(data);
  }
};

const spawnProcess = async function(name, cmdArgs, processData, stdin = null) {
  const opts = {
    command: await Subprocess.pathSearch(name),
    arguments: cmdArgs,
  };
  const proc = await Subprocess.call(opts);

  if (stdin) {
    const encoder = new TextEncoder("utf-8");
    proc.stdin.write(encoder.encode(stdin));
    proc.stdin.close();
  }

  await readAllData(proc.stdout, processData);
};

const getSymbolsFromNM = async function(path, arch) {
  const parser = new NMParser();

  const args = [path];
  if (Services.appinfo.OS === "Darwin") {
    args.unshift("-arch", arch);
  } else {
    // Mac's `nm` doesn't support the demangle option, so we have to
    // post-process the symbols with c++filt.
    args.unshift("--demangle");
  }

  await spawnProcess("nm", args, data => parser.consume(data));
  await spawnProcess("nm", ["-D", ...args], data => parser.consume(data));
  let result = await parser.finish();
  if (Services.appinfo.OS !== "Darwin") {
    return result;
  }

  const [addresses, symbolsJoinedBuffer] = result;
  const decoder = new TextDecoder();
  const symbolsJoined = decoder.decode(symbolsJoinedBuffer);
  const demangler = new CppFiltParser(addresses.length);
  await spawnProcess("c++filt", [], data => demangler.consume(data), symbolsJoined);
  const [newIndex, newBuffer] = await demangler.finish();
  return [addresses, newIndex, newBuffer];
};

const pathComponentsForSymbolFile = (debugName, breakpadId) => {
  const symName = debugName.replace(/(\.pdb)?$/, ".sym");
  return [debugName, breakpadId, symName];
};

const urlForSymFile = (debugName, breakpadId) => {
  const profilerSymbolsURL = Services.prefs.getCharPref(PREF_SYMBOLS_URL,
                                                        "http://symbols.mozilla.org/");
  return profilerSymbolsURL + pathComponentsForSymbolFile(debugName, breakpadId).join("/");
};

const getContainingObjdirDist = path => {
  let curPath = path;
  let curPathBasename = OS.Path.basename(curPath);
  while (curPathBasename) {
    if (curPathBasename === "dist") {
      return curPath;
    }
    const parentDirPath = OS.Path.dirname(curPath);
    if (curPathBasename === "bin") {
      return parentDirPath;
    }
    curPath = parentDirPath;
    curPathBasename = OS.Path.basename(curPath);
  }
  return null;
};

const filePathForSymFileInObjDir = (binaryPath, debugName, breakpadId) => {
  // `mach buildsymbols` generates symbol files located
  // at /path/to/objdir/dist/crashreporter-symbols/.
  const objDirDist = getContainingObjdirDist(binaryPath);
  if (!objDirDist) {
    return null;
  }
  return OS.Path.join(objDirDist,
                      "crashreporter-symbols",
                      ...pathComponentsForSymbolFile(debugName, breakpadId));
};

const symbolCache = new Map();

const primeSymbolStore = libs => {
  for (const {debugName, breakpadId, path, arch} of libs) {
    symbolCache.set(urlForSymFile(debugName, breakpadId), {path, arch});
  }
};

const isRunningObserver = {
  _observers: new Set(),

  observe(subject, topic, data) {
    switch (topic) {
      case "profiler-started":
      case "profiler-stopped":
        // Call observer(false) or observer(true), but do it through a promise
        // so that it's asynchronous.
        // We don't want it to be synchronous because of the observer call in
        // addObserver, which is asynchronous, and we want to get the ordering
        // right.
        const isRunningPromise = Promise.resolve(topic === "profiler-started");
        for (let observer of this._observers) {
          isRunningPromise.then(observer);
        }
        break;
    }
  },

  _startListening() {
    Services.obs.addObserver(this, "profiler-started");
    Services.obs.addObserver(this, "profiler-stopped");
  },

  _stopListening() {
    Services.obs.removeObserver(this, "profiler-started");
    Services.obs.removeObserver(this, "profiler-stopped");
  },

  addObserver(observer) {
    if (this._observers.size === 0) {
      this._startListening();
    }

    this._observers.add(observer);
    observer(Services.profiler.IsActive());
  },

  removeObserver(observer) {
    if (this._observers.delete(observer) && this._observers.size === 0) {
      this._stopListening();
    }
  },
};

this.geckoProfiler = class extends ExtensionAPI {
  getAPI(context) {
    return {
      geckoProfiler: {
        async start(options) {
          const {bufferSize, interval, features, threads} = options;

          Services.prefs.setBoolPref(PREF_ASYNC_STACK, false);
          if (threads) {
            Services.profiler.StartProfiler(bufferSize, interval, features, features.length, threads, threads.length);
          } else {
            Services.profiler.StartProfiler(bufferSize, interval, features, features.length);
          }
        },

        async stop() {
          if (ASYNC_STACKS_ENABLED !== null) {
            Services.prefs.setBoolPref(PREF_ASYNC_STACK, ASYNC_STACKS_ENABLED);
          }

          Services.profiler.StopProfiler();
        },

        async pause() {
          Services.profiler.PauseSampling();
        },

        async resume() {
          Services.profiler.ResumeSampling();
        },

        async getProfile() {
          if (!Services.profiler.IsActive()) {
            throw new ExtensionError("The profiler is stopped. " +
              "You need to start the profiler before you can capture a profile.");
          }

          return Services.profiler.getProfileDataAsync();
        },

        async getProfileAsArrayBuffer() {
          if (!Services.profiler.IsActive()) {
            throw new ExtensionError("The profiler is stopped. " +
              "You need to start the profiler before you can capture a profile.");
          }

          return Services.profiler.getProfileDataAsArrayBuffer();
        },

        async getSymbols(debugName, breakpadId) {
          if (symbolCache.size === 0) {
            primeSymbolStore(Services.profiler.sharedLibraries);
          }

          const cachedLibInfo = symbolCache.get(urlForSymFile(debugName, breakpadId));

          const symbolRules = Services.prefs.getCharPref(PREF_GET_SYMBOL_RULES, "localBreakpad,remoteBreakpad");
          const haveAbsolutePath = cachedLibInfo && OS.Path.split(cachedLibInfo.path).absolute;

          // We have multiple options for obtaining symbol information for the given
          // binary.
          //  "localBreakpad"  - Use existing symbol dumps stored in the object directory of a local
          //      Firefox build, generated using `mach buildsymbols` [requires path]
          //  "remoteBreakpad" - Use symbol dumps from the Mozilla symbol server [only requires
          //      debugName + breakpadId]
          //  "nm"             - Use the command line tool `nm` [linux/mac only, requires path]
          for (const rule of symbolRules.split(",")) {
            try {
              switch (rule) {
                case "localBreakpad":
                  if (haveAbsolutePath) {
                    const {path} = cachedLibInfo;
                    const filepath = filePathForSymFileInObjDir(path, debugName, breakpadId);
                    if (filepath) {
                      // NOTE: here and below, "return await" is used to ensure we catch any
                      // errors in the promise. A simple return would give the error to the
                      // caller.
                      return await parseSym({filepath});
                    }
                  }
                  break;
                case "remoteBreakpad":
                  const url = urlForSymFile(debugName, breakpadId);
                  return await parseSym({url});
                case "nm":
                  if (haveAbsolutePath) {
                    const {path, arch} = cachedLibInfo;
                    return await getSymbolsFromNM(path, arch);
                  }
                  break;
              }
            } catch (e) {
              // Each of our options can go wrong for a variety of reasons, so on failure
              // we will try the next one.
              // "localBreakpad" will fail if this is not a local build that's running from the object
              // directory or if the user hasn't run `mach buildsymbols` on it.
              // "remoteBreakpad" will fail if this is not an official mozilla build (e.g. Nightly) or a
              // known system library.
              // "nm" will fail if `nm` is not available.
            }
          }

          throw new Error(`Ran out of options to get symbols from library ${debugName} ${breakpadId}.`);
        },

        onRunning: new EventManager(context, "geckoProfiler.onRunning", fire => {
          isRunningObserver.addObserver(fire.async);
          return () => {
            isRunningObserver.removeObserver(fire.async);
          };
        }).api(),
      },
    };
  }
};
PK
!<�Yq��"�"-chrome/browser/content/browser/ext-history.js/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";

// The ext-* files are imported into the same scopes.
/* import-globals-from ext-browserAction.js */

ChromeUtils.defineModuleGetter(this, "PlacesUtils",
                               "resource://gre/modules/PlacesUtils.jsm");
ChromeUtils.defineModuleGetter(this, "Services",
                               "resource://gre/modules/Services.jsm");

var {
  normalizeTime,
} = ExtensionUtils;

let nsINavHistoryService = Ci.nsINavHistoryService;
const TRANSITION_TO_TRANSITION_TYPES_MAP = new Map([
  ["link", nsINavHistoryService.TRANSITION_LINK],
  ["typed", nsINavHistoryService.TRANSITION_TYPED],
  ["auto_bookmark", nsINavHistoryService.TRANSITION_BOOKMARK],
  ["auto_subframe", nsINavHistoryService.TRANSITION_EMBED],
  ["manual_subframe", nsINavHistoryService.TRANSITION_FRAMED_LINK],
  ["reload", nsINavHistoryService.TRANSITION_RELOAD],
]);

let TRANSITION_TYPE_TO_TRANSITIONS_MAP = new Map();
for (let [transition, transitionType] of TRANSITION_TO_TRANSITION_TYPES_MAP) {
  TRANSITION_TYPE_TO_TRANSITIONS_MAP.set(transitionType, transition);
}

const getTransitionType = transition => {
  // cannot set a default value for the transition argument as the framework sets it to null
  transition = transition || "link";
  let transitionType = TRANSITION_TO_TRANSITION_TYPES_MAP.get(transition);
  if (!transitionType) {
    throw new Error(`|${transition}| is not a supported transition for history`);
  }
  return transitionType;
};

const getTransition = transitionType => {
  return TRANSITION_TYPE_TO_TRANSITIONS_MAP.get(transitionType) || "link";
};

/*
 * Converts a nsINavHistoryResultNode into a HistoryItem
 *
 * https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsINavHistoryResultNode
 */
const convertNodeToHistoryItem = node => {
  return {
    id: node.pageGuid,
    url: node.uri,
    title: node.title,
    lastVisitTime: PlacesUtils.toDate(node.time).getTime(),
    visitCount: node.accessCount,
  };
};

/*
 * Converts a nsINavHistoryResultNode into a VisitItem
 *
 * https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsINavHistoryResultNode
 */
const convertNodeToVisitItem = node => {
  return {
    id: node.pageGuid,
    visitId: String(node.visitId),
    visitTime: PlacesUtils.toDate(node.time).getTime(),
    referringVisitId: String(node.fromVisitId),
    transition: getTransition(node.visitType),
  };
};

/*
 * Converts a nsINavHistoryContainerResultNode into an array of objects
 *
 * https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsINavHistoryContainerResultNode
 */
const convertNavHistoryContainerResultNode = (container, converter) => {
  let results = [];
  container.containerOpen = true;
  for (let i = 0; i < container.childCount; i++) {
    let node = container.getChild(i);
    results.push(converter(node));
  }
  container.containerOpen = false;
  return results;
};

var _observer;

const getHistoryObserver = () => {
  if (!_observer) {
    _observer = new class extends EventEmitter {
      onDeleteURI(uri, guid, reason) {
        this.emit("visitRemoved", {allHistory: false, urls: [uri.spec]});
      }
      onVisits(visits) {
        for (let visit of visits) {
          let data = {
            id: visit.guid,
            url: visit.uri.spec,
            title: visit.lastKnownTitle || "",
            lastVisitTime: visit.time / 1000,  // time from Places is microseconds,
            visitCount: visit.visitCount,
            typedCount: visit.typed,
          };
          this.emit("visited", data);
        }
      }
      onBeginUpdateBatch() {}
      onEndUpdateBatch() {}
      onTitleChanged(uri, title) {
        this.emit("titleChanged", {url: uri.spec, title: title});
      }
      onClearHistory() {
        this.emit("visitRemoved", {allHistory: true, urls: []});
      }
      onPageChanged() {}
      onFrecencyChanged() {}
      onManyFrecenciesChanged() {}
      onDeleteVisits(uri, time, guid, reason) {
        this.emit("visitRemoved", {allHistory: false, urls: [uri.spec]});
      }
    }();
    PlacesUtils.history.addObserver(_observer);
  }
  return _observer;
};

this.history = class extends ExtensionAPI {
  getAPI(context) {
    return {
      history: {
        addUrl: function(details) {
          let transition, date;
          try {
            transition = getTransitionType(details.transition);
          } catch (error) {
            return Promise.reject({message: error.message});
          }
          if (details.visitTime) {
            date = normalizeTime(details.visitTime);
          }
          let pageInfo = {
            title: details.title,
            url: details.url,
            visits: [
              {
                transition,
                date,
              },
            ],
          };
          try {
            return PlacesUtils.history.insert(pageInfo).then(() => undefined);
          } catch (error) {
            return Promise.reject({message: error.message});
          }
        },

        deleteAll: function() {
          return PlacesUtils.history.clear();
        },

        deleteRange: function(filter) {
          let newFilter = {
            beginDate: normalizeTime(filter.startTime),
            endDate: normalizeTime(filter.endTime),
          };
          // History.removeVisitsByFilter returns a boolean, but our API should return nothing
          return PlacesUtils.history.removeVisitsByFilter(newFilter).then(() => undefined);
        },

        deleteUrl: function(details) {
          let url = details.url;
          // History.remove returns a boolean, but our API should return nothing
          return PlacesUtils.history.remove(url).then(() => undefined);
        },

        search: function(query) {
          let beginTime = (query.startTime == null) ?
                            PlacesUtils.toPRTime(Date.now() - 24 * 60 * 60 * 1000) :
                            PlacesUtils.toPRTime(normalizeTime(query.startTime));
          let endTime = (query.endTime == null) ?
                          Number.MAX_VALUE :
                          PlacesUtils.toPRTime(normalizeTime(query.endTime));
          if (beginTime > endTime) {
            return Promise.reject({message: "The startTime cannot be after the endTime"});
          }

          let options = PlacesUtils.history.getNewQueryOptions();
          options.includeHidden = true;
          options.sortingMode = options.SORT_BY_DATE_DESCENDING;
          options.maxResults = query.maxResults || 100;

          let historyQuery = PlacesUtils.history.getNewQuery();
          historyQuery.searchTerms = query.text;
          historyQuery.beginTime = beginTime;
          historyQuery.endTime = endTime;
          let queryResult = PlacesUtils.history.executeQuery(historyQuery, options).root;
          let results = convertNavHistoryContainerResultNode(queryResult, convertNodeToHistoryItem);
          return Promise.resolve(results);
        },

        getVisits: function(details) {
          let url = details.url;
          if (!url) {
            return Promise.reject({message: "A URL must be provided for getVisits"});
          }

          let options = PlacesUtils.history.getNewQueryOptions();
          options.includeHidden = true;
          options.sortingMode = options.SORT_BY_DATE_DESCENDING;
          options.resultType = options.RESULTS_AS_VISIT;

          let historyQuery = PlacesUtils.history.getNewQuery();
          historyQuery.uri = Services.io.newURI(url);
          let queryResult = PlacesUtils.history.executeQuery(historyQuery, options).root;
          let results = convertNavHistoryContainerResultNode(queryResult, convertNodeToVisitItem);
          return Promise.resolve(results);
        },

        onVisited: new EventManager(context, "history.onVisited", fire => {
          let listener = (event, data) => {
            fire.sync(data);
          };

          getHistoryObserver().on("visited", listener);
          return () => {
            getHistoryObserver().off("visited", listener);
          };
        }).api(),

        onVisitRemoved: new EventManager(context, "history.onVisitRemoved", fire => {
          let listener = (event, data) => {
            fire.sync(data);
          };

          getHistoryObserver().on("visitRemoved", listener);
          return () => {
            getHistoryObserver().off("visitRemoved", listener);
          };
        }).api(),

        onTitleChanged: new EventManager(context, "history.onTitleChanged", fire => {
          let listener = (event, data) => {
            fire.sync(data);
          };

          getHistoryObserver().on("titleChanged", listener);
          return () => {
            getHistoryObserver().off("titleChanged", listener);
          };
        }).api(),
      },
    };
  }
};
PK
!<�g�uiui+chrome/browser/content/browser/ext-menus.js/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";

// The ext-* files are imported into the same scopes.
/* import-globals-from ext-browser.js */

ChromeUtils.import("resource://gre/modules/Services.jsm");

var {
  DefaultMap,
  ExtensionError,
} = ExtensionUtils;

ChromeUtils.import("resource://gre/modules/ExtensionParent.jsm");

var {
  IconDetails,
} = ExtensionParent;

const ACTION_MENU_TOP_LEVEL_LIMIT = 6;

// Map[Extension -> Map[ID -> MenuItem]]
// Note: we want to enumerate all the menu items so
// this cannot be a weak map.
var gMenuMap = new Map();

// Map[Extension -> MenuItem]
var gRootItems = new Map();

// Map[Extension -> ID[]]
// Menu IDs that were eligible for being shown in the current menu.
var gShownMenuItems = new DefaultMap(() => []);

// Set of extensions that are listening to onShown.
var gOnShownSubscribers = new Set();

// If id is not specified for an item we use an integer.
var gNextMenuItemID = 0;

// Used to assign unique names to radio groups.
var gNextRadioGroupID = 0;

// The max length of a menu item's label.
var gMaxLabelLength = 64;

var gMenuBuilder = {
  // When a new menu is opened, this function is called and
  // we populate the |xulMenu| with all the items from extensions
  // to be displayed. We always clear all the items again when
  // popuphidden fires.
  build(contextData) {
    let xulMenu = contextData.menu;
    xulMenu.addEventListener("popuphidden", this);
    this.xulMenu = xulMenu;
    for (let [, root] of gRootItems) {
      let rootElement = this.createTopLevelElement(root, contextData);
      if (rootElement) {
        this.appendTopLevelElement(rootElement);
      }
    }
    this.afterBuildingMenu(contextData);
  },

  // Builds a context menu for browserAction and pageAction buttons.
  buildActionContextMenu(contextData) {
    const {menu} = contextData;

    const root = gRootItems.get(contextData.extension);
    if (!root) {
      return;
    }

    const children = this.buildChildren(root, contextData);
    const visible = children.slice(0, ACTION_MENU_TOP_LEVEL_LIMIT);

    this.xulMenu = menu;
    menu.addEventListener("popuphidden", this);

    if (visible.length) {
      const separator = menu.ownerDocument.createElement("menuseparator");
      menu.insertBefore(separator, menu.firstChild);
      this.itemsToCleanUp.add(separator);

      for (const child of visible) {
        this.itemsToCleanUp.add(child);
        menu.insertBefore(child, separator);
      }
    }
    this.afterBuildingMenu(contextData);
  },

  buildElementWithChildren(item, contextData) {
    const element = this.buildSingleElement(item, contextData);
    const children = this.buildChildren(item, contextData);
    if (children.length) {
      element.firstChild.append(...children);
    }
    return element;
  },

  buildChildren(item, contextData) {
    let groupName;
    let children = [];
    for (let child of item.children) {
      if (child.type == "radio" && !child.groupName) {
        if (!groupName) {
          groupName = `webext-radio-group-${gNextRadioGroupID++}`;
        }
        child.groupName = groupName;
      } else {
        groupName = null;
      }

      if (child.enabledForContext(contextData)) {
        children.push(this.buildElementWithChildren(child, contextData));
      }
    }
    return children;
  },

  createTopLevelElement(root, contextData) {
    let rootElement = this.buildElementWithChildren(root, contextData);
    if (!rootElement.firstChild || !rootElement.firstChild.childNodes.length) {
      // If the root has no visible children, there is no reason to show
      // the root menu item itself either.
      return null;
    }
    rootElement.setAttribute("ext-type", "top-level-menu");
    rootElement = this.removeTopLevelMenuIfNeeded(rootElement);

    // Display the extension icon on the root element.
    if (root.extension.manifest.icons) {
      this.setMenuItemIcon(rootElement, root.extension, contextData, root.extension.manifest.icons);
    }
    return rootElement;
  },

  appendTopLevelElement(rootElement) {
    if (this.itemsToCleanUp.size === 0) {
      const separator = this.xulMenu.ownerDocument.createElement("menuseparator");
      this.itemsToCleanUp.add(separator);
      this.xulMenu.append(separator);
    }

    this.xulMenu.appendChild(rootElement);
    this.itemsToCleanUp.add(rootElement);
  },

  removeSeparatorIfNoTopLevelItems() {
    if (this.itemsToCleanUp.size === 1) {
      // Remove the separator if all extension menu items have disappeared.
      const separator = this.itemsToCleanUp.values().next().value;
      separator.remove();
      this.itemsToCleanUp.clear();
    }
  },

  removeTopLevelMenuIfNeeded(element) {
    // If there is only one visible top level element we don't need the
    // root menu element for the extension.
    let menuPopup = element.firstChild;
    if (menuPopup && menuPopup.childNodes.length == 1) {
      let onlyChild = menuPopup.firstChild;

      // Keep single checkbox items in the submenu on Linux since
      // the extension icon overlaps the checkbox otherwise.
      if (AppConstants.platform === "linux" && onlyChild.getAttribute("type") === "checkbox") {
        return element;
      }

      onlyChild.remove();
      return onlyChild;
    }

    return element;
  },

  buildSingleElement(item, contextData) {
    let doc = contextData.menu.ownerDocument;
    let element;
    if (item.children.length > 0) {
      element = this.createMenuElement(doc, item);
    } else if (item.type == "separator") {
      element = doc.createElement("menuseparator");
    } else {
      element = doc.createElement("menuitem");
    }

    return this.customizeElement(element, item, contextData);
  },

  createMenuElement(doc, item) {
    let element = doc.createElement("menu");
    // Menu elements need to have a menupopup child for its menu items.
    let menupopup = doc.createElement("menupopup");
    element.appendChild(menupopup);
    return element;
  },

  customizeElement(element, item, contextData) {
    let label = item.title;
    if (label) {
      if (contextData.isTextSelected && label.indexOf("%s") > -1) {
        let selection = contextData.selectionText.trim();
        // The rendering engine will truncate the title if it's longer than 64 characters.
        // But if it makes sense let's try truncate selection text only, to handle cases like
        // 'look up "%s" in MyDictionary' more elegantly.

        let codePointsToRemove = 0;

        let selectionArray = Array.from(selection);

        let completeLabelLength = label.length - 2 + selectionArray.length;
        if (completeLabelLength > gMaxLabelLength) {
          codePointsToRemove = completeLabelLength - gMaxLabelLength;
        }

        if (codePointsToRemove) {
          let ellipsis = "\u2026";
          try {
            ellipsis = Services.prefs.getComplexValue("intl.ellipsis",
                                                      Ci.nsIPrefLocalizedString).data;
          } catch (e) { }
          codePointsToRemove += 1;
          selection = selectionArray.slice(0, -codePointsToRemove).join("") + ellipsis;
        }

        label = label.replace(/%s/g, selection);
      }

      element.setAttribute("label", label);
    }

    element.setAttribute("id", item.elementId);

    if (item.icons) {
      this.setMenuItemIcon(element, item.extension, contextData, item.icons);
    }

    if (item.type == "checkbox") {
      element.setAttribute("type", "checkbox");
      if (item.checked) {
        element.setAttribute("checked", "true");
      }
    } else if (item.type == "radio") {
      element.setAttribute("type", "radio");
      element.setAttribute("name", item.groupName);
      if (item.checked) {
        element.setAttribute("checked", "true");
      }
    }

    if (!item.enabled) {
      element.setAttribute("disabled", "true");
    }

    element.addEventListener("command", event => { // eslint-disable-line mozilla/balanced-listeners
      if (event.target !== event.currentTarget) {
        return;
      }
      const wasChecked = item.checked;
      if (item.type == "checkbox") {
        item.checked = !item.checked;
      } else if (item.type == "radio") {
        // Deselect all radio items in the current radio group.
        for (let child of item.parent.children) {
          if (child.type == "radio" && child.groupName == item.groupName) {
            child.checked = false;
          }
        }
        // Select the clicked radio item.
        item.checked = true;
      }

      if (!contextData.onBookmark) {
        item.tabManager.addActiveTabPermission();
      }

      let tab = contextData.tab && item.tabManager.convert(contextData.tab);
      let info = item.getClickInfo(contextData, wasChecked);

      const map = {shiftKey: "Shift", altKey: "Alt", metaKey: "Command", ctrlKey: "Ctrl"};
      info.modifiers = Object.keys(map).filter(key => event[key]).map(key => map[key]);
      if (event.ctrlKey && AppConstants.platform === "macosx") {
        info.modifiers.push("MacCtrl");
      }

      // Allow menus to open various actions supported in webext prior
      // to notifying onclicked.
      let actionFor = {
        _execute_page_action: global.pageActionFor,
        _execute_browser_action: global.browserActionFor,
        _execute_sidebar_action: global.sidebarActionFor,
      }[item.command];
      if (actionFor) {
        let win = event.target.ownerGlobal;
        actionFor(item.extension).triggerAction(win);
      }

      item.extension.emit("webext-menu-menuitem-click", info, tab);
    });

    // Don't publish the ID of the root because the root element is
    // auto-generated.
    if (item.parent) {
      gShownMenuItems.get(item.extension).push(item.id);
    }

    return element;
  },

  setMenuItemIcon(element, extension, contextData, icons) {
    let parentWindow = contextData.menu.ownerGlobal;

    let {icon} = IconDetails.getPreferredIcon(icons, extension,
                                              16 * parentWindow.devicePixelRatio);

    // The extension icons in the manifest are not pre-resolved, since
    // they're sometimes used by the add-on manager when the extension is
    // not enabled, and its URLs are not resolvable.
    let resolvedURL = extension.baseURI.resolve(icon);

    if (element.localName == "menu") {
      element.setAttribute("class", "menu-iconic");
    } else if (element.localName == "menuitem") {
      element.setAttribute("class", "menuitem-iconic");
    }

    element.setAttribute("image", resolvedURL);
  },

  rebuildMenu(extension) {
    let {contextData} = this;
    if (!contextData) {
      // This happens if the menu is not visible.
      return;
    }

    if (!gShownMenuItems.has(extension)) {
      // The onShown event was not fired for the extension, so the extension
      // does not know that a menu is being shown, and therefore they should
      // not care whether the extension menu is updated.
      return;
    }

    if (contextData.onBrowserAction || contextData.onPageAction) {
      // The action menu can only have items from one extension, so remove all
      // items (including the separator) and rebuild the action menu (if any).
      for (let item of this.itemsToCleanUp) {
        item.remove();
      }
      this.itemsToCleanUp.clear();
      this.buildActionContextMenu(contextData);
      return;
    }

    // First find the one and only top-level menu item for the extension.
    let elementIdPrefix = `${makeWidgetId(extension.id)}-menuitem-`;
    let oldRoot = null;
    for (let item = this.xulMenu.lastElementChild; item !== null; item = item.previousElementSibling) {
      if (item.id && item.id.startsWith(elementIdPrefix)) {
        oldRoot = item;
        this.itemsToCleanUp.delete(oldRoot);
        break;
      }
    }

    let root = gRootItems.get(extension);
    let newRoot = root && this.createTopLevelElement(root, contextData);
    if (newRoot) {
      this.itemsToCleanUp.add(newRoot);
      if (oldRoot) {
        oldRoot.replaceWith(newRoot);
      } else {
        this.appendTopLevelElement(newRoot);
      }
    } else if (oldRoot) {
      oldRoot.remove();
      this.removeSeparatorIfNoTopLevelItems();
    }
  },

  afterBuildingMenu(contextData) {
    if (this.contextData) {
      // rebuildMenu can trigger us again, but the logic below should run only
      // once per open menu.
      return;
    }

    function dispatchOnShownEvent(extension) {
      // Note: gShownMenuItems is a DefaultMap, so .get(extension) causes the
      // extension to be stored in the map even if there are currently no
      // shown menu items. This ensures that the onHidden event can be fired
      // when the menu is closed.
      let menuIds = gShownMenuItems.get(extension);
      extension.emit("webext-menu-shown", menuIds, contextData);
    }

    if (contextData.onBrowserAction || contextData.onPageAction) {
      dispatchOnShownEvent(contextData.extension);
    } else {
      gOnShownSubscribers.forEach(dispatchOnShownEvent);
    }

    this.contextData = contextData;
  },

  handleEvent(event) {
    if (this.xulMenu != event.target || event.type != "popuphidden") {
      return;
    }

    delete this.xulMenu;
    delete this.contextData;

    let target = event.target;
    target.removeEventListener("popuphidden", this);
    for (let item of this.itemsToCleanUp) {
      item.remove();
    }
    this.itemsToCleanUp.clear();
    for (let extension of gShownMenuItems.keys()) {
      extension.emit("webext-menu-hidden");
    }
    gShownMenuItems.clear();
  },

  itemsToCleanUp: new Set(),
};

// Called from pageAction or browserAction popup.
global.actionContextMenu = function(contextData) {
  contextData.tab = tabTracker.activeTab;
  contextData.pageUrl = contextData.tab.linkedBrowser.currentURI.spec;
  gMenuBuilder.buildActionContextMenu(contextData);
};

const contextsMap = {
  onAudio: "audio",
  onEditable: "editable",
  inFrame: "frame",
  onImage: "image",
  onLink: "link",
  onPassword: "password",
  isTextSelected: "selection",
  onVideo: "video",

  onBookmark: "bookmark",
  onBrowserAction: "browser_action",
  onPageAction: "page_action",
  onTab: "tab",
  inToolsMenu: "tools_menu",
};

const getMenuContexts = contextData => {
  let contexts = new Set();

  for (const [key, value] of Object.entries(contextsMap)) {
    if (contextData[key]) {
      contexts.add(value);
    }
  }

  if (contexts.size === 0) {
    contexts.add("page");
  }

  // New non-content contexts supported in Firefox are not part of "all".
  if (!contextData.onBookmark && !contextData.onTab && !contextData.inToolsMenu) {
    contexts.add("all");
  }

  return contexts;
};

function addMenuEventInfo(info, contextData, includeSensitiveData) {
  if (contextData.onVideo) {
    info.mediaType = "video";
  } else if (contextData.onAudio) {
    info.mediaType = "audio";
  } else if (contextData.onImage) {
    info.mediaType = "image";
  }
  if (contextData.frameId !== undefined) {
    info.frameId = contextData.frameId;
  }
  if (contextData.onBookmark) {
    info.bookmarkId = contextData.bookmarkId;
  }
  info.editable = contextData.onEditable || false;
  if (includeSensitiveData) {
    if (contextData.onLink) {
      info.linkText = contextData.linkText;
      info.linkUrl = contextData.linkUrl;
    }
    if (contextData.onAudio || contextData.onImage || contextData.onVideo) {
      info.srcUrl = contextData.srcUrl;
    }
    if (!contextData.onBookmark) {
      info.pageUrl = contextData.pageUrl;
    }
    if (contextData.inFrame) {
      info.frameUrl = contextData.frameUrl;
    }
    if (contextData.isTextSelected) {
      info.selectionText = contextData.selectionText;
    }
  }
}

function MenuItem(extension, createProperties, isRoot = false) {
  this.extension = extension;
  this.children = [];
  this.parent = null;
  this.tabManager = extension.tabManager;

  this.setDefaults();
  this.setProps(createProperties);

  if (!this.hasOwnProperty("_id")) {
    this.id = gNextMenuItemID++;
  }
  // If the item is not the root and has no parent
  // it must be a child of the root.
  if (!isRoot && !this.parent) {
    this.root.addChild(this);
  }
}

MenuItem.prototype = {
  setProps(createProperties) {
    for (let propName in createProperties) {
      if (createProperties[propName] === null) {
        // Omitted optional argument.
        continue;
      }
      this[propName] = createProperties[propName];
    }

    if (createProperties.documentUrlPatterns != null) {
      this.documentUrlMatchPattern = new MatchPatternSet(this.documentUrlPatterns);
    }

    if (createProperties.targetUrlPatterns != null) {
      this.targetUrlMatchPattern = new MatchPatternSet(this.targetUrlPatterns);
    }

    // If a child MenuItem does not specify any contexts, then it should
    // inherit the contexts specified from its parent.
    if (createProperties.parentId && !createProperties.contexts) {
      this.contexts = this.parent.contexts;
    }
  },

  setDefaults() {
    this.setProps({
      type: "normal",
      checked: false,
      contexts: ["all"],
      enabled: true,
    });
  },

  set id(id) {
    if (this.hasOwnProperty("_id")) {
      throw new ExtensionError("ID of a MenuItem cannot be changed");
    }
    let isIdUsed = gMenuMap.get(this.extension).has(id);
    if (isIdUsed) {
      throw new ExtensionError(`ID already exists: ${id}`);
    }
    this._id = id;
  },

  get id() {
    return this._id;
  },

  get elementId() {
    let id = this.id;
    // If the ID is an integer, it is auto-generated and globally unique.
    // If the ID is a string, it is only unique within one extension and the
    // ID needs to be concatenated with the extension ID.
    if (typeof id !== "number") {
      // To avoid collisions with numeric IDs, add a prefix to string IDs.
      id = `_${id}`;
    }
    return `${makeWidgetId(this.extension.id)}-menuitem-${id}`;
  },

  ensureValidParentId(parentId) {
    if (parentId === undefined) {
      return;
    }
    let menuMap = gMenuMap.get(this.extension);
    if (!menuMap.has(parentId)) {
      throw new ExtensionError(`Could not find any MenuItem with id: ${parentId}`);
    }
    for (let item = menuMap.get(parentId); item; item = item.parent) {
      if (item === this) {
        throw new ExtensionError("MenuItem cannot be an ancestor (or self) of its new parent.");
      }
    }
  },

  set parentId(parentId) {
    this.ensureValidParentId(parentId);

    if (this.parent) {
      this.parent.detachChild(this);
    }

    if (parentId === undefined) {
      this.root.addChild(this);
    } else {
      let menuMap = gMenuMap.get(this.extension);
      menuMap.get(parentId).addChild(this);
    }
  },

  get parentId() {
    return this.parent ? this.parent.id : undefined;
  },

  addChild(child) {
    if (child.parent) {
      throw new Error("Child MenuItem already has a parent.");
    }
    this.children.push(child);
    child.parent = this;
  },

  detachChild(child) {
    let idx = this.children.indexOf(child);
    if (idx < 0) {
      throw new Error("Child MenuItem not found, it cannot be removed.");
    }
    this.children.splice(idx, 1);
    child.parent = null;
  },

  get root() {
    let extension = this.extension;
    if (!gRootItems.has(extension)) {
      let root = new MenuItem(extension,
                              {title: extension.name},
                              /* isRoot = */ true);
      gRootItems.set(extension, root);
    }

    return gRootItems.get(extension);
  },

  remove() {
    if (this.parent) {
      this.parent.detachChild(this);
    }
    let children = this.children.slice(0);
    for (let child of children) {
      child.remove();
    }

    let menuMap = gMenuMap.get(this.extension);
    menuMap.delete(this.id);
    if (this.root == this) {
      gRootItems.delete(this.extension);
    }
  },

  getClickInfo(contextData, wasChecked) {
    let info = {
      menuItemId: this.id,
    };
    if (this.parent) {
      info.parentMenuItemId = this.parentId;
    }

    addMenuEventInfo(info, contextData, true);

    if ((this.type === "checkbox") || (this.type === "radio")) {
      info.checked = this.checked;
      info.wasChecked = wasChecked;
    }

    return info;
  },

  enabledForContext(contextData) {
    let contexts = getMenuContexts(contextData);
    if (!this.contexts.some(n => contexts.has(n))) {
      return false;
    }

    if (contextData.onBookmark) {
      return this.extension.hasPermission("bookmarks");
    }

    let docPattern = this.documentUrlMatchPattern;
    let pageURI = Services.io.newURI(contextData[contextData.inFrame ? "frameUrl" : "pageUrl"]);
    if (docPattern && !docPattern.matches(pageURI)) {
      return false;
    }

    let targetPattern = this.targetUrlMatchPattern;
    if (targetPattern) {
      let targetUrls = [];
      if (contextData.onImage || contextData.onAudio || contextData.onVideo) {
        // TODO: double check if srcUrl is always set when we need it
        targetUrls.push(contextData.srcUrl);
      }
      if (contextData.onLink) {
        targetUrls.push(contextData.linkUrl);
      }
      if (!targetUrls.some(targetUrl => targetPattern.matches(Services.io.newURI(targetUrl)))) {
        return false;
      }
    }

    return true;
  },
};

// While any extensions are active, this Tracker registers to observe/listen
// for menu events from both Tools and context menus, both content and chrome.
const menuTracker = {
  menuIds: ["placesContext", "menu_ToolsPopup", "tabContextMenu"],

  register() {
    Services.obs.addObserver(this, "on-build-contextmenu");
    for (const window of windowTracker.browserWindows()) {
      this.onWindowOpen(window);
    }
    windowTracker.addOpenListener(this.onWindowOpen);
  },

  unregister() {
    Services.obs.removeObserver(this, "on-build-contextmenu");
    for (const window of windowTracker.browserWindows()) {
      for (const id of this.menuIds) {
        const menu = window.document.getElementById(id);
        menu.removeEventListener("popupshowing", this);
      }
    }
    windowTracker.removeOpenListener(this.onWindowOpen);
  },

  observe(subject, topic, data) {
    subject = subject.wrappedJSObject;
    gMenuBuilder.build(subject);
  },

  onWindowOpen(window) {
    for (const id of menuTracker.menuIds) {
      const menu = window.document.getElementById(id);
      menu.addEventListener("popupshowing", menuTracker);
    }
  },

  handleEvent(event) {
    const menu = event.target;
    if (menu.id === "placesContext") {
      const trigger = menu.triggerNode;
      if (!trigger._placesNode) {
        return;
      }

      gMenuBuilder.build({
        menu,
        bookmarkId: trigger._placesNode.bookmarkGuid,
        onBookmark: true,
      });
    }
    if (menu.id === "menu_ToolsPopup") {
      const tab = tabTracker.activeTab;
      const pageUrl = tab.linkedBrowser.currentURI.spec;
      gMenuBuilder.build({menu, tab, pageUrl, inToolsMenu: true});
    }
    if (menu.id === "tabContextMenu") {
      const trigger = menu.triggerNode;
      const tab = trigger.localName === "tab" ? trigger : tabTracker.activeTab;
      const pageUrl = tab.linkedBrowser.currentURI.spec;
      gMenuBuilder.build({menu, tab, pageUrl, onTab: true});
    }
  },
};

this.menusInternal = class extends ExtensionAPI {
  constructor(extension) {
    super(extension);

    if (!gMenuMap.size) {
      menuTracker.register();
    }
    gMenuMap.set(extension, new Map());
  }

  onShutdown(reason) {
    let {extension} = this;

    if (gMenuMap.has(extension)) {
      gMenuMap.delete(extension);
      gRootItems.delete(extension);
      gShownMenuItems.delete(extension);
      gOnShownSubscribers.delete(extension);
      if (!gMenuMap.size) {
        menuTracker.unregister();
      }
    }
  }

  getAPI(context) {
    let {extension} = context;

    const menus = {
      refresh() {
        gMenuBuilder.rebuildMenu(extension);
      },

      onShown: new EventManager(context, "menus.onShown", fire => {
        let listener = (event, menuIds, contextData) => {
          let info = {
            menuIds,
            contexts: Array.from(getMenuContexts(contextData)),
          };

          // The menus.onShown event is fired before the user has consciously
          // interacted with an extension, so we require permissions before
          // exposing sensitive contextual data.
          let includeSensitiveData =
            extension.tabManager.hasActiveTabPermission(contextData.tab) ||
            extension.whiteListedHosts.matches(contextData.inFrame ? contextData.frameUrl : contextData.pageUrl);

          addMenuEventInfo(info, contextData, includeSensitiveData);

          let tab = extension.tabManager.convert(contextData.tab);
          fire.sync(info, tab);
        };
        gOnShownSubscribers.add(extension);
        extension.on("webext-menu-shown", listener);
        return () => {
          gOnShownSubscribers.delete(extension);
          extension.off("webext-menu-shown", listener);
        };
      }).api(),
      onHidden: new EventManager(context, "menus.onHidden", fire => {
        let listener = () => {
          fire.sync();
        };
        extension.on("webext-menu-hidden", listener);
        return () => {
          extension.off("webext-menu-hidden", listener);
        };
      }).api(),
    };

    return {
      contextMenus: menus,
      menus,
      menusInternal: {
        create: function(createProperties) {
          // Note that the id is required by the schema. If the addon did not set
          // it, the implementation of menus.create in the child should
          // have added it.
          let menuItem = new MenuItem(extension, createProperties);
          gMenuMap.get(extension).set(menuItem.id, menuItem);
        },

        update: function(id, updateProperties) {
          let menuItem = gMenuMap.get(extension).get(id);
          if (menuItem) {
            menuItem.setProps(updateProperties);
          }
        },

        remove: function(id) {
          let menuItem = gMenuMap.get(extension).get(id);
          if (menuItem) {
            menuItem.remove();
          }
        },

        removeAll: function() {
          let root = gRootItems.get(extension);
          if (root) {
            root.remove();
          }
        },

        onClicked: new EventManager(context, "menusInternal.onClicked", fire => {
          let listener = (event, info, tab) => {
            let {linkedBrowser} = tab || tabTracker.activeTab;
            context.withPendingBrowser(linkedBrowser,
                                       () => fire.sync(info, tab));
          };

          extension.on("webext-menu-menuitem-click", listener);
          return () => {
            extension.off("webext-menu-menuitem-click", listener);
          };
        }).api(),
      },
    };
  }
};
PK
!<�ՙ��-chrome/browser/content/browser/ext-omnibox.js/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";

// The ext-* files are imported into the same scopes.
/* import-globals-from ../../../toolkit/components/extensions/ext-toolkit.js */

ChromeUtils.defineModuleGetter(this, "ExtensionSearchHandler",
                               "resource://gre/modules/ExtensionSearchHandler.jsm");

this.omnibox = class extends ExtensionAPI {
  onManifestEntry(entryName) {
    let {extension} = this;
    let {manifest} = extension;

    let keyword = manifest.omnibox.keyword;
    try {
      // This will throw if the keyword is already registered.
      ExtensionSearchHandler.registerKeyword(keyword, extension);
      this.keyword = keyword;
    } catch (e) {
      extension.manifestError(e.message);
    }
  }

  onShutdown(reason) {
    ExtensionSearchHandler.unregisterKeyword(this.keyword);
  }

  getAPI(context) {
    let {extension} = context;
    return {
      omnibox: {
        setDefaultSuggestion: (suggestion) => {
          try {
            // This will throw if the keyword failed to register.
            ExtensionSearchHandler.setDefaultSuggestion(this.keyword, suggestion);
          } catch (e) {
            return Promise.reject(e.message);
          }
        },

        onInputStarted: new EventManager(context, "omnibox.onInputStarted", fire => {
          let listener = (eventName) => {
            fire.sync();
          };
          extension.on(ExtensionSearchHandler.MSG_INPUT_STARTED, listener);
          return () => {
            extension.off(ExtensionSearchHandler.MSG_INPUT_STARTED, listener);
          };
        }).api(),

        onInputCancelled: new EventManager(context, "omnibox.onInputCancelled", fire => {
          let listener = (eventName) => {
            fire.sync();
          };
          extension.on(ExtensionSearchHandler.MSG_INPUT_CANCELLED, listener);
          return () => {
            extension.off(ExtensionSearchHandler.MSG_INPUT_CANCELLED, listener);
          };
        }).api(),

        onInputEntered: new EventManager(context, "omnibox.onInputEntered", fire => {
          let listener = (eventName, text, disposition) => {
            fire.sync(text, disposition);
          };
          extension.on(ExtensionSearchHandler.MSG_INPUT_ENTERED, listener);
          return () => {
            extension.off(ExtensionSearchHandler.MSG_INPUT_ENTERED, listener);
          };
        }).api(),

        // Internal APIs.
        addSuggestions: (id, suggestions) => {
          try {
            ExtensionSearchHandler.addSuggestions(this.keyword, id, suggestions);
          } catch (e) {
            // Silently fail because the extension developer can not know for sure if the user
            // has already invalidated the callback when asynchronously providing suggestions.
          }
        },

        onInputChanged: new EventManager(context, "omnibox.onInputChanged", fire => {
          let listener = (eventName, text, id) => {
            fire.sync(text, id);
          };
          extension.on(ExtensionSearchHandler.MSG_INPUT_CHANGED, listener);
          return () => {
            extension.off(ExtensionSearchHandler.MSG_INPUT_CHANGED, listener);
          };
        }).api(),
      },
    };
  }
};
PK
!<��7,�6�60chrome/browser/content/browser/ext-pageAction.js/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";

// The ext-* files are imported into the same scopes.
/* import-globals-from ext-browserAction.js */
/* import-globals-from ext-browser.js */

ChromeUtils.defineModuleGetter(this, "PageActions",
                               "resource:///modules/PageActions.jsm");
ChromeUtils.defineModuleGetter(this, "PanelPopup",
                               "resource:///modules/ExtensionPopups.jsm");
ChromeUtils.defineModuleGetter(this, "TelemetryStopwatch",
                               "resource://gre/modules/TelemetryStopwatch.jsm");


ChromeUtils.import("resource://gre/modules/ExtensionParent.jsm");

var {
  IconDetails,
  StartupCache,
} = ExtensionParent;

const popupOpenTimingHistogram = "WEBEXT_PAGEACTION_POPUP_OPEN_MS";

// WeakMap[Extension -> PageAction]
let pageActionMap = new WeakMap();

this.pageAction = class extends ExtensionAPI {
  static for(extension) {
    return pageActionMap.get(extension);
  }

  async onManifestEntry(entryName) {
    let {extension} = this;
    let options = extension.manifest.page_action;

    let widgetId = makeWidgetId(extension.id);
    this.id = widgetId + "-page-action";

    this.tabManager = extension.tabManager;

    // `show` can have three different values:
    // - `false`. This means the page action is not shown.
    //   It's set as default if show_matches is empty. Can also be set in a tab via
    //   `pageAction.hide(tabId)`, e.g. in order to override show_matches.
    // - `true`. This means the page action is shown.
    //   It's never set as default because <all_urls> doesn't really match all URLs
    //   (e.g. "about:" URLs). But can be set in a tab via `pageAction.show(tabId)`.
    // - `undefined`.
    //   This is the default value when there are some patterns in show_matches.
    //   Can't be set as a tab-specific value.
    let show, showMatches, hideMatches;
    let show_matches = options.show_matches || [];
    let hide_matches = options.hide_matches || [];
    if (!show_matches.length) {
      // Always hide by default. No need to do any pattern matching.
      show = false;
    } else {
      // Might show or hide depending on the URL. Enable pattern matching.
      showMatches = new MatchPatternSet(show_matches);
      hideMatches = new MatchPatternSet(hide_matches);
    }

    this.defaults = {
      show,
      showMatches,
      hideMatches,
      title: options.default_title || extension.name,
      popup: options.default_popup || "",
    };

    this.browserStyle = options.browser_style || false;
    if (options.browser_style === null) {
      this.extension.logger.warn("Please specify whether you want browser_style " +
                                 "or not in your page_action options.");
    }

    this.tabContext = new TabContext(tab => Object.create(this.defaults),
                                     extension);

    this.tabContext.on("location-change", this.handleLocationChange.bind(this)); // eslint-disable-line mozilla/balanced-listeners

    pageActionMap.set(extension, this);

    this.defaults.icon = await StartupCache.get(
      extension, ["pageAction", "default_icon"],
      () => IconDetails.normalize({path: options.default_icon}, extension));

    if (!this.browserPageAction) {
      this.browserPageAction = PageActions.addAction(new PageActions.Action({
        id: widgetId,
        extensionID: extension.id,
        title: this.defaults.title,
        iconURL: this.getIconData(this.defaults.icon),
        pinnedToUrlbar: true,
        disabled: !this.defaults.show,
        onCommand: (event, buttonNode) => {
          this.handleClick(event.target.ownerGlobal);
        },
        onBeforePlacedInWindow: browserWindow => {
          if (this.extension.hasPermission("menus") ||
              this.extension.hasPermission("contextMenus")) {
            browserWindow.document.addEventListener("popupshowing", this);
          }
        },
        onRemovedFromWindow: browserWindow => {
          browserWindow.document.removeEventListener("popupshowing", this);
        },
      }));

      // If the page action is only enabled in some URLs, do pattern matching in
      // the active tabs and update the button if necessary.
      if (show === undefined) {
        for (let window of windowTracker.browserWindows()) {
          let tab = window.gBrowser.selectedTab;
          if (this.isShown(tab)) {
            this.updateButton(window);
          }
        }
      }
    }
  }

  onShutdown(reason) {
    pageActionMap.delete(this.extension);

    this.tabContext.shutdown();

    // Removing the browser page action causes PageActions to forget about it
    // across app restarts, so don't remove it on app shutdown, but do remove
    // it on all other shutdowns since there's no guarantee the action will be
    // coming back.
    if (reason != "APP_SHUTDOWN" && this.browserPageAction) {
      this.browserPageAction.remove();
      this.browserPageAction = null;
    }
  }

  // Returns the value of the property |prop| for the given tab, where
  // |prop| is one of "show", "title", "icon", "popup".
  getProperty(tab, prop) {
    return this.tabContext.get(tab)[prop];
  }

  // Sets the value of the property |prop| for the given tab to the
  // given value, symmetrically to |getProperty|.
  //
  // If |tab| is currently selected, updates the page action button to
  // reflect the new value.
  setProperty(tab, prop, value) {
    if (value != null) {
      this.tabContext.get(tab)[prop] = value;
    } else {
      delete this.tabContext.get(tab)[prop];
    }

    if (tab.selected) {
      this.updateButton(tab.ownerGlobal);
    }
  }

  // Updates the page action button in the given window to reflect the
  // properties of the currently selected tab:
  //
  // Updates "tooltiptext" and "aria-label" to match "title" property.
  // Updates "image" to match the "icon" property.
  // Enables or disables the icon, based on the "show" and "patternMatching" properties.
  updateButton(window) {
    let tab = window.gBrowser.selectedTab;
    let tabData = this.tabContext.get(tab);
    let title = tabData.title || this.extension.name;
    this.browserPageAction.setTitle(title, window);
    this.browserPageAction.setTooltip(title, window);

    // At least one of "show" or "patternMatching" must be defined here.
    let {show = tabData.patternMatching} = tabData;
    this.browserPageAction.setDisabled(!show, window);

    let iconURL;
    if (typeof(tabData.icon) == "string") {
      iconURL = IconDetails.escapeUrl(tabData.icon);
    } else {
      iconURL = this.getIconData(tabData.icon);
    }
    this.browserPageAction.setIconURL(iconURL, window);
  }

  // Checks whether the tab action is shown when the specified tab becomes active.
  // Does pattern matching if necessary, and caches the result as a tab-specific value.
  // @param {XULElement} tab
  //        The tab to be checked
  // @param [optional] {boolean} ignoreCache
  //        If true, forces pattern matching to be reevaluated, even if there is a cached value.
  isShown(tab, ignoreCache = false) {
    let tabData = this.tabContext.get(tab);

    // If there is a "show" value, return it. Can be due to show(), hide() or empty show_matches.
    if (tabData.show !== undefined) {
      return tabData.show;
    }

    // Otherwise pattern matching must have been configured. Do it, caching the result.
    if (ignoreCache || tabData.patternMatching === undefined) {
      let uri = tab.linkedBrowser.currentURI;
      tabData.patternMatching = tabData.showMatches.matches(uri) && !tabData.hideMatches.matches(uri);
    }
    return tabData.patternMatching;
  }

  getIconData(icons) {
    let getIcon = size => {
      let {icon} = IconDetails.getPreferredIcon(icons, this.extension, size);
      // TODO: implement theme based icon for pageAction (Bug 1398156)
      return IconDetails.escapeUrl(icon);
    };
    return {
      "16": getIcon(16),
      "32": getIcon(32),
    };
  }

  /**
   * Triggers this page action for the given window, with the same effects as
   * if it were clicked by a user.
   *
   * This has no effect if the page action is hidden for the selected tab.
   *
   * @param {Window} window
   */
  triggerAction(window) {
    if (this.isShown(window.gBrowser.selectedTab)) {
      this.handleClick(window);
    }
  }

  handleEvent(event) {
    switch (event.type) {
      case "popupshowing":
        const menu = event.target;
        const trigger = menu.triggerNode;

        if (menu.id === "pageActionContextMenu" &&
            trigger &&
            trigger.getAttribute("actionid") === this.browserPageAction.id &&
            !this.browserPageAction.getDisabled(trigger.ownerGlobal)) {
          global.actionContextMenu({
            extension: this.extension,
            onPageAction: true,
            menu: menu,
          });
        }
        break;
    }
  }

  // Handles a click event on the page action button for the given
  // window.
  // If the page action has a |popup| property, a panel is opened to
  // that URL. Otherwise, a "click" event is emitted, and dispatched to
  // the any click listeners in the add-on.
  async handleClick(window) {
    TelemetryStopwatch.start(popupOpenTimingHistogram, this);
    let tab = window.gBrowser.selectedTab;
    let popupURL = this.tabContext.get(tab).popup;

    this.tabManager.addActiveTabPermission(tab);

    // If the widget has a popup URL defined, we open a popup, but do not
    // dispatch a click event to the extension.
    // If it has no popup URL defined, we dispatch a click event, but do not
    // open a popup.
    if (popupURL) {
      if (this.popupNode && this.popupNode.panel.state !== "closed") {
        // The panel is being toggled closed.
        TelemetryStopwatch.cancel(popupOpenTimingHistogram, this);
        window.BrowserPageActions.togglePanelForAction(this.browserPageAction,
                                                       this.popupNode.panel);
        return;
      }

      this.popupNode = new PanelPopup(this.extension, window.document, popupURL,
                                      this.browserStyle);
      // Remove popupNode when it is closed.
      this.popupNode.panel.addEventListener("popuphiding", () => {
        this.popupNode = undefined;
      }, {once: true});
      await this.popupNode.contentReady;
      window.BrowserPageActions.togglePanelForAction(this.browserPageAction,
                                                     this.popupNode.panel);
      TelemetryStopwatch.finish(popupOpenTimingHistogram, this);
    } else {
      TelemetryStopwatch.cancel(popupOpenTimingHistogram, this);
      this.emit("click", tab);
    }
  }

  handleLocationChange(eventType, tab, fromBrowse) {
    // fromBrowse can have three values:
    // - true: navigation occurred in the active tab
    // - false: the location changed in the active tab but no navigation occurred
    // - undefined: an inactive tab has become active
    if (fromBrowse === true) {
      this.tabContext.clear(tab);
    }

    // isShown will do pattern matching (if necessary) and store the result
    // so that updateButton knows whether the page action should be shown.
    this.isShown(tab, fromBrowse !== undefined);
    this.updateButton(tab.ownerGlobal);
  }

  getAPI(context) {
    let {extension} = context;

    const {tabManager} = extension;
    const pageAction = this;

    return {
      pageAction: {
        onClicked: new InputEventManager(context, "pageAction.onClicked", fire => {
          let listener = (evt, tab) => {
            context.withPendingBrowser(tab.linkedBrowser, () =>
              fire.sync(tabManager.convert(tab)));
          };

          pageAction.on("click", listener);
          return () => {
            pageAction.off("click", listener);
          };
        }).api(),

        show(tabId) {
          let tab = tabTracker.getTab(tabId);
          pageAction.setProperty(tab, "show", true);
        },

        hide(tabId) {
          let tab = tabTracker.getTab(tabId);
          pageAction.setProperty(tab, "show", false);
        },

        isShown(details) {
          let tab = tabTracker.getTab(details.tabId);
          return pageAction.isShown(tab);
        },

        setTitle(details) {
          let tab = tabTracker.getTab(details.tabId);
          pageAction.setProperty(tab, "title", details.title);
        },

        getTitle(details) {
          let tab = tabTracker.getTab(details.tabId);

          let title = pageAction.getProperty(tab, "title");
          return Promise.resolve(title);
        },

        setIcon(details) {
          let tab = tabTracker.getTab(details.tabId);

          let icon = IconDetails.normalize(details, extension, context);
          if (!Object.keys(icon).length) {
            icon = null;
          }
          pageAction.setProperty(tab, "icon", icon);
        },

        setPopup(details) {
          let tab = tabTracker.getTab(details.tabId);

          // Note: Chrome resolves arguments to setIcon relative to the calling
          // context, but resolves arguments to setPopup relative to the extension
          // root.
          // For internal consistency, we currently resolve both relative to the
          // calling context.
          let url = details.popup && context.uri.resolve(details.popup);
          if (url && !context.checkLoadURL(url)) {
            return Promise.reject({message: `Access denied for URL ${url}`});
          }
          pageAction.setProperty(tab, "popup", url);
        },

        getPopup(details) {
          let tab = tabTracker.getTab(details.tabId);

          let popup = pageAction.getProperty(tab, "popup");
          return Promise.resolve(popup);
        },

        openPopup: function() {
          let window = windowTracker.topWindow;
          pageAction.triggerAction(window);
        },
      },
    };
  }
};

global.pageActionFor = this.pageAction.for;
PK
!<��O(��,chrome/browser/content/browser/ext-pkcs11.js"use strict";

XPCOMUtils.defineLazyModuleGetters(this, {
  ctypes: "resource://gre/modules/ctypes.jsm",
  NativeManifests: "resource://gre/modules/NativeManifests.jsm",
  OS: "resource://gre/modules/osfile.jsm",
});

XPCOMUtils.defineLazyServiceGetter(this, "pkcs11db",
                                   "@mozilla.org/security/pkcs11moduledb;1",
                                   "nsIPKCS11ModuleDB");

var {DefaultMap} = ExtensionUtils;

const findModuleByPath = function(path) {
  let modules = pkcs11db.listModules();
  for (let module of XPCOMUtils.IterSimpleEnumerator(modules, Ci.nsIPKCS11Module)) {
    if (module && module.libName === path) {
      return module;
    }
  }
  return null;
};

this.pkcs11 = class extends ExtensionAPI {
  getAPI(context) {
    let manifestCache = new DefaultMap(async name => {
      let hostInfo = await NativeManifests.lookupManifest("pkcs11", name, context);
      if (hostInfo) {
        if (AppConstants.platform === "win") {
          hostInfo.manifest.path = OS.Path.join(OS.Path.dirname(hostInfo.path), hostInfo.manifest.path);
        }
        let manifestLib = OS.Path.basename(hostInfo.manifest.path);
        if (AppConstants.platform !== "linux") {
          manifestLib = manifestLib.toLowerCase(manifestLib);
        }
        if (manifestLib !== ctypes.libraryName("nssckbi")) {
          return hostInfo.manifest;
        }
      }
      return Promise.reject({message: `No such PKCS#11 module ${name}`});
    });
    return {
      pkcs11: {
        /**
          * Verify whether a given PKCS#11 module is installed.
          *
          * @param {string} name The name of the module, as specified in
          *                      the manifest file.
          * @returns {Promise} A Promise that resolves to true if the package
          *                    is installed, or false if it is not. May be
          *                    rejected if the module could not be found.
          */
        async isModuleInstalled(name) {
          let manifest = await manifestCache.get(name);
          return findModuleByPath(manifest.path) !== null;
        },
        /**
          * Install a PKCS#11 module
          *
          * @param {string} name The name of the module, as specified in
          *                      the manifest file.
          * @param {integer} [flags = 0] Any flags to be passed on to the
          *                              nsIPKCS11ModuleDB.addModule method
          * @returns {Promise} When the Promise resolves, the module will have
          *                    been installed. When it is rejected, the module
          *                    either is already installed or could not be
          *                    installed for some reason.
          */
        async installModule(name, flags = 0) {
          let manifest = await manifestCache.get(name);
          if (!manifest.description) {
            return Promise.reject({message: `The description field in the manifest for PKCS#11 module ${name} must have a value`});
          }
          pkcs11db.addModule(manifest.description, manifest.path, flags, 0);
        },
        /**
          * Uninstall a PKCS#11 module
          *
          * @param {string} name The name of the module, as specified in
          *                      the manifest file.
          * @returns {Promise}. When the Promise resolves, the module will have
          *                     been uninstalled. When it is rejected, the
          *                     module either was not installed or could not be
          *                     uninstalled for some reason.
          */
        async uninstallModule(name) {
          let manifest = await manifestCache.get(name);
          let module = findModuleByPath(manifest.path);
          if (!module) {
            return Promise.reject({message: `The PKCS#11 module ${name} is not loaded`});
          }
          pkcs11db.deleteModule(module.name);
        },
        /**
         * Get a list of slots for a given PKCS#11 module, with
         * information on the token (if any) in the slot.
         *
         * The PKCS#11 standard defines slots as an abstract concept
         * that may or may not have at most one token. In practice, when
         * using PKCS#11 for smartcards (the most likely use case of
         * PKCS#11 for Firefox), a slot corresponds to a cardreader, and
         * a token corresponds to a card.
         *
         * @param {string} name The name of the PKCS#11 module, as
         *                 specified in the manifest file.
         * @returns {Promise} A promise that resolves to an array of objects
         *                    with two properties.  The `name` object contains
         *                    the name of the slot; the `token` object is null
         *                    if there is no token in the slot, or is an object
         *                    describing various properties of the token if
         *                    there is.
         */
        async getModuleSlots(name) {
          let manifest = await manifestCache.get(name);
          let module = findModuleByPath(manifest.path);
          if (!module) {
            return Promise.reject({message: `The module ${name} is not installed`});
          }
          let rv = [];
          for (let slot of XPCOMUtils.IterSimpleEnumerator(module.listSlots(), Ci.nsIPKCS11Slot)) {
            let token = slot.getToken();
            let slotobj = {
              name: slot.name,
              token: null,
            };
            if (slot.status != 1 /* SLOT_NOT_PRESENT */) {
              slotobj.token = {
                name: token.tokenName,
                manufacturer: token.tokenManID,
                HWVersion: token.tokenHWVersion,
                FWVersion: token.tokenFWVersion,
                serial: token.tokenSerialNumber,
                isLoggedIn: token.isLoggedIn(),
              };
            }
            rv.push(slotobj);
          }
          return rv;
        },
      },
    };
  }
};
PK
!<C���.chrome/browser/content/browser/ext-sessions.js/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";

// The ext-* files are imported into the same scopes.
/* import-globals-from ext-browser.js */

var {
  ExtensionError,
  promiseObserved,
} = ExtensionUtils;

ChromeUtils.defineModuleGetter(this, "AddonManagerPrivate",
                               "resource://gre/modules/AddonManager.jsm");
ChromeUtils.defineModuleGetter(this, "SessionStore",
                               "resource:///modules/sessionstore/SessionStore.jsm");

const SS_ON_CLOSED_OBJECTS_CHANGED = "sessionstore-closed-objects-changed";

const getRecentlyClosed = (maxResults, extension) => {
  let recentlyClosed = [];

  // Get closed windows
  let closedWindowData = SessionStore.getClosedWindowData(false);
  for (let window of closedWindowData) {
    recentlyClosed.push({
      lastModified: window.closedAt,
      window: Window.convertFromSessionStoreClosedData(extension, window)});
  }

  // Get closed tabs
  for (let window of windowTracker.browserWindows()) {
    let closedTabData = SessionStore.getClosedTabData(window, false);
    for (let tab of closedTabData) {
      recentlyClosed.push({
        lastModified: tab.closedAt,
        tab: Tab.convertFromSessionStoreClosedData(extension, tab, window)});
    }
  }

  // Sort windows and tabs
  recentlyClosed.sort((a, b) => b.lastModified - a.lastModified);
  return recentlyClosed.slice(0, maxResults);
};

const createSession = async function createSession(restored, extension, sessionId) {
  if (!restored) {
    throw new ExtensionError(`Could not restore object using sessionId ${sessionId}.`);
  }
  let sessionObj = {lastModified: Date.now()};
  if (restored instanceof Ci.nsIDOMChromeWindow) {
    await promiseObserved("sessionstore-single-window-restored", subject => subject == restored);
    sessionObj.window = extension.windowManager.convert(restored, {populate: true});
    return sessionObj;
  }
  sessionObj.tab = extension.tabManager.convert(restored);
  return sessionObj;
};

const getEncodedKey = function getEncodedKey(extensionId, key) {
  // Throw if using a temporary extension id.
  if (AddonManagerPrivate.isTemporaryInstallID(extensionId)) {
    let message =
      "Sessions API storage methods will not work with a temporary addon ID. " +
      "Please add an explicit addon ID to your manifest.";
    throw new ExtensionError(message);
  }

  return `extension:${extensionId}:${key}`;
};

const getTabParams = function getTabParams(extensionId, key, id) {
  let encodedKey = getEncodedKey(extensionId, key);
  let tab = tabTracker.getTab(id);

  return {encodedKey, tab};
};

const getWindowParams = function getWindowParams(extensionId, key, id, context) {
  let encodedKey = getEncodedKey(extensionId, key);
  let win = windowTracker.getWindow(id, context);

  return {encodedKey, win};
};

this.sessions = class extends ExtensionAPI {
  getAPI(context) {
    let {extension} = context;
    return {
      sessions: {
        async getRecentlyClosed(filter) {
          await SessionStore.promiseInitialized;
          let maxResults = filter.maxResults == undefined ? this.MAX_SESSION_RESULTS : filter.maxResults;
          return getRecentlyClosed(maxResults, extension);
        },

        async forgetClosedTab(windowId, sessionId) {
          await SessionStore.promiseInitialized;
          let window = context.extension.windowManager.get(windowId).window;
          let closedTabData = SessionStore.getClosedTabData(window, false);

          let closedTabIndex = closedTabData.findIndex((closedTab) => {
            return closedTab.closedId === parseInt(sessionId, 10);
          });

          if (closedTabIndex < 0) {
            throw new ExtensionError(`Could not find closed tab using sessionId ${sessionId}.`);
          }

          SessionStore.forgetClosedTab(window, closedTabIndex);
        },

        async forgetClosedWindow(sessionId) {
          await SessionStore.promiseInitialized;
          let closedWindowData = SessionStore.getClosedWindowData(false);

          let closedWindowIndex = closedWindowData.findIndex((closedWindow) => {
            return closedWindow.closedId === parseInt(sessionId, 10);
          });

          if (closedWindowIndex < 0) {
            throw new ExtensionError(`Could not find closed window using sessionId ${sessionId}.`);
          }

          SessionStore.forgetClosedWindow(closedWindowIndex);
        },

        async restore(sessionId) {
          await SessionStore.promiseInitialized;
          let session, closedId;
          if (sessionId) {
            closedId = sessionId;
            session = SessionStore.undoCloseById(closedId);
          } else if (SessionStore.lastClosedObjectType == "window") {
            // If the most recently closed object is a window, just undo closing the most recent window.
            session = SessionStore.undoCloseWindow(0);
          } else {
            // It is a tab, and we cannot call SessionStore.undoCloseTab without a window,
            // so we must find the tab in which case we can just use its closedId.
            let recentlyClosedTabs = [];
            for (let window of windowTracker.browserWindows()) {
              let closedTabData = SessionStore.getClosedTabData(window, false);
              for (let tab of closedTabData) {
                recentlyClosedTabs.push(tab);
              }
            }

            // Sort the tabs.
            recentlyClosedTabs.sort((a, b) => b.closedAt - a.closedAt);

            // Use the closedId of the most recently closed tab to restore it.
            closedId = recentlyClosedTabs[0].closedId;
            session = SessionStore.undoCloseById(closedId);
          }
          return createSession(session, extension, closedId);
        },

        setTabValue(tabId, key, value) {
          let {tab, encodedKey} =
            getTabParams(extension.id, key, tabId);

          SessionStore.setTabValue(tab, encodedKey, JSON.stringify(value));
        },

        async getTabValue(tabId, key) {
          let {tab, encodedKey} =
            getTabParams(extension.id, key, tabId);

          let value = SessionStore.getTabValue(tab, encodedKey);
          if (value) {
            return JSON.parse(value);
          }

          return undefined;
        },

        removeTabValue(tabId, key) {
          let {tab, encodedKey} =
            getTabParams(extension.id, key, tabId);

          SessionStore.deleteTabValue(tab, encodedKey);
        },

        setWindowValue(windowId, key, value) {
          let {win, encodedKey} =
            getWindowParams(extension.id, key, windowId, context);

          SessionStore.setWindowValue(win, encodedKey, JSON.stringify(value));
        },

        async getWindowValue(windowId, key) {
          let {win, encodedKey} =
            getWindowParams(extension.id, key, windowId, context);

          let value = SessionStore.getWindowValue(win, encodedKey);
          if (value) {
            return JSON.parse(value);
          }

          return undefined;
        },

        removeWindowValue(windowId, key) {
          let {win, encodedKey} =
            getWindowParams(extension.id, key, windowId, context);

          SessionStore.deleteWindowValue(win, encodedKey);
        },

        onChanged: new EventManager(context, "sessions.onChanged", fire => {
          let observer = () => {
            fire.async();
          };

          Services.obs.addObserver(observer, SS_ON_CLOSED_OBJECTS_CHANGED);
          return () => {
            Services.obs.removeObserver(observer, SS_ON_CLOSED_OBJECTS_CHANGED);
          };
        }).api(),
      },
    };
  }
};
PK
!<%��H5H53chrome/browser/content/browser/ext-sidebarAction.js/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";

// The ext-* files are imported into the same scopes.
/* import-globals-from ext-browser.js */
/* globals WINDOW_ID_CURRENT */

ChromeUtils.import("resource://gre/modules/ExtensionParent.jsm");

var {
  IconDetails,
} = ExtensionParent;

var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";

// WeakMap[Extension -> SidebarAction]
let sidebarActionMap = new WeakMap();

const sidebarURL = "chrome://browser/content/webext-panels.xul";

/**
 * Responsible for the sidebar_action section of the manifest as well
 * as the associated sidebar browser.
 */
this.sidebarAction = class extends ExtensionAPI {
  static for(extension) {
    return sidebarActionMap.get(extension);
  }

  onManifestEntry(entryName) {
    let {extension} = this;

    extension.once("ready", this.onReady.bind(this));

    let options = extension.manifest.sidebar_action;

    // Add the extension to the sidebar menu.  The sidebar widget will copy
    // from that when it is viewed, so we shouldn't need to update that.
    let widgetId = makeWidgetId(extension.id);
    this.id = `${widgetId}-sidebar-action`;
    this.menuId = `menu_${this.id}`;
    this.buttonId = `button_${this.id}`;

    // We default browser_style to true because this is a new API and
    // we therefore don't need to worry about breaking existing add-ons.
    this.browserStyle = options.browser_style || options.browser_style === null;

    this.defaults = {
      enabled: true,
      title: options.default_title || extension.name,
      icon: IconDetails.normalize({path: options.default_icon}, extension),
      panel: options.default_panel || "",
    };
    this.globals = Object.create(this.defaults);

    this.tabContext = new TabContext(tab => Object.create(this.globals),
                                     extension);

    // We need to ensure our elements are available before session restore.
    this.windowOpenListener = (window) => {
      this.createMenuItem(window, this.globals);
    };
    windowTracker.addOpenListener(this.windowOpenListener);

    this.updateHeader = (event) => {
      let window = event.target.ownerGlobal;
      let details = this.tabContext.get(window.gBrowser.selectedTab);
      let header = window.document.getElementById("sidebar-switcher-target");
      if (window.SidebarUI.currentID === this.id) {
        this.setMenuIcon(header, details);
      }
    };

    this.windowCloseListener = (window) => {
      let header = window.document.getElementById("sidebar-switcher-target");
      if (header) {
        header.removeEventListener("SidebarShown", this.updateHeader);
      }
    };
    windowTracker.addCloseListener(this.windowCloseListener);

    sidebarActionMap.set(extension, this);
  }

  onReady() {
    this.build();
  }

  onShutdown(reason) {
    sidebarActionMap.delete(this.this);

    this.tabContext.shutdown();

    // Don't remove everything on app shutdown so session restore can handle
    // restoring open sidebars.
    if (reason === "APP_SHUTDOWN") {
      return;
    }

    for (let window of windowTracker.browserWindows()) {
      let {document, SidebarUI} = window;
      if (SidebarUI.currentID === this.id) {
        SidebarUI.hide();
      }
      let menu = document.getElementById(this.menuId);
      if (menu) {
        menu.remove();
      }
      let button = document.getElementById(this.buttonId);
      if (button) {
        button.remove();
      }
      let broadcaster = document.getElementById(this.id);
      if (broadcaster) {
        broadcaster.remove();
      }
      let header = document.getElementById("sidebar-switcher-target");
      header.removeEventListener("SidebarShown", this.updateHeader);
    }
    windowTracker.removeOpenListener(this.windowOpenListener);
    windowTracker.removeCloseListener(this.windowCloseListener);
  }

  build() {
    this.tabContext.on("tab-select", // eslint-disable-line mozilla/balanced-listeners
                       (evt, tab) => { this.updateWindow(tab.ownerGlobal); });

    let install = this.extension.startupReason === "ADDON_INSTALL";
    for (let window of windowTracker.browserWindows()) {
      this.updateWindow(window);
      let {SidebarUI} = window;
      if (install || SidebarUI.lastOpenedId == this.id) {
        SidebarUI.show(this.id);
      }
    }
  }

  createMenuItem(window, details) {
    let {document, SidebarUI} = window;

    // Use of the broadcaster allows browser-sidebar.js to properly manage the
    // checkmarks in the menus.
    let broadcaster = document.createElementNS(XUL_NS, "broadcaster");
    broadcaster.setAttribute("id", this.id);
    broadcaster.setAttribute("autoCheck", "false");
    broadcaster.setAttribute("type", "checkbox");
    broadcaster.setAttribute("group", "sidebar");
    broadcaster.setAttribute("label", details.title);
    broadcaster.setAttribute("sidebarurl", sidebarURL);
    broadcaster.setAttribute("panel", details.panel);
    if (this.browserStyle) {
      broadcaster.setAttribute("browserStyle", "true");
    }
    broadcaster.setAttribute("extensionId", this.extension.id);
    let id = `ext-key-id-${this.id}`;
    broadcaster.setAttribute("key", id);

    // oncommand gets attached to menuitem, so we use the observes attribute to
    // get the command id we pass to SidebarUI.
    broadcaster.setAttribute("oncommand", "SidebarUI.toggle(this.getAttribute('observes'))");

    let header = document.getElementById("sidebar-switcher-target");
    header.addEventListener("SidebarShown", this.updateHeader);

    // Insert a menuitem for View->Show Sidebars.
    let menuitem = document.createElementNS(XUL_NS, "menuitem");
    menuitem.setAttribute("id", this.menuId);
    menuitem.setAttribute("observes", this.id);
    menuitem.setAttribute("class", "menuitem-iconic webextension-menuitem");
    this.setMenuIcon(menuitem, details);

    // Insert a toolbarbutton for the sidebar dropdown selector.
    let toolbarbutton = document.createElementNS(XUL_NS, "toolbarbutton");
    toolbarbutton.setAttribute("id", this.buttonId);
    toolbarbutton.setAttribute("observes", this.id);
    toolbarbutton.setAttribute("class", "subviewbutton subviewbutton-iconic webextension-menuitem");
    this.setMenuIcon(toolbarbutton, details);

    document.getElementById("mainBroadcasterSet").appendChild(broadcaster);
    document.getElementById("viewSidebarMenu").appendChild(menuitem);
    let separator = document.getElementById("sidebar-extensions-separator");
    separator.parentNode.insertBefore(toolbarbutton, separator);
    SidebarUI.updateShortcut({button: toolbarbutton});

    return menuitem;
  }

  setMenuIcon(menuitem, details) {
    let getIcon = size => IconDetails.escapeUrl(
      IconDetails.getPreferredIcon(details.icon, this.extension, size).icon);

    menuitem.setAttribute("style", `
      --webextension-menuitem-image: url("${getIcon(16)}");
      --webextension-menuitem-image-2x: url("${getIcon(32)}");
    `);
  }

  /**
   * Update the broadcaster and menuitem `node` with the tab context data
   * in `tabData`.
   *
   * @param {ChromeWindow} window
   *        Browser chrome window.
   * @param {object} tabData
   *        Tab specific sidebar configuration.
   */
  updateButton(window, tabData) {
    let {document, SidebarUI} = window;
    let title = tabData.title || this.extension.name;
    let menu = document.getElementById(this.menuId);
    if (!menu) {
      menu = this.createMenuItem(window, tabData);
    }

    // Update the broadcaster first, it will update both menus.
    let broadcaster = document.getElementById(this.id);
    broadcaster.setAttribute("tooltiptext", title);
    broadcaster.setAttribute("label", title);

    let urlChanged = tabData.panel !== broadcaster.getAttribute("panel");
    if (urlChanged) {
      broadcaster.setAttribute("panel", tabData.panel);
    }

    this.setMenuIcon(menu, tabData);

    let button = document.getElementById(this.buttonId);
    this.setMenuIcon(button, tabData);

    // Update the sidebar if this extension is the current sidebar.
    if (SidebarUI.currentID === this.id) {
      SidebarUI.title = title;
      let header = document.getElementById("sidebar-switcher-target");
      this.setMenuIcon(header, tabData);
      if (SidebarUI.isOpen && urlChanged) {
        SidebarUI.show(this.id);
      }
    }
  }

  /**
   * Update the broadcaster and menuitem for a given window.
   *
   * @param {ChromeWindow} window
   *        Browser chrome window.
   */
  updateWindow(window) {
    let nativeTab = window.gBrowser.selectedTab;
    this.updateButton(window, this.tabContext.get(nativeTab));
  }

  /**
   * Update the broadcaster and menuitem when the extension changes the icon,
   * title, url, etc. If it only changes a parameter for a single
   * tab, `tab` will be that tab. Otherwise it will be null.
   *
   * @param {XULElement|null} nativeTab
   *        Browser tab, may be null.
   */
  updateOnChange(nativeTab) {
    if (nativeTab) {
      if (nativeTab.selected) {
        this.updateWindow(nativeTab.ownerGlobal);
      }
    } else {
      for (let window of windowTracker.browserWindows()) {
        this.updateWindow(window);
      }
    }
  }

  /**
   * Set a default or tab specific property.
   *
   * @param {XULElement|null} nativeTab
   *        Webextension tab object, may be null.
   * @param {string} prop
   *        String property to retrieve ["icon", "title", or "panel"].
   * @param {string} value
   *        Value for property.
   */
  setProperty(nativeTab, prop, value) {
    let values;
    if (nativeTab === null) {
      values = this.globals;
    } else {
      values = this.tabContext.get(nativeTab);
    }
    if (value === null) {
      delete values[prop];
    } else {
      values[prop] = value;
    }

    this.updateOnChange(nativeTab);
  }

  /**
   * Retrieve a property from the tab or globals if tab is null.
   *
   * @param {XULElement|null} nativeTab
   *        Browser tab object, may be null.
   * @param {string} prop
   *        String property to retrieve ["icon", "title", or "panel"]
   * @returns {string} value
   *          Value for prop.
   */
  getProperty(nativeTab, prop) {
    if (nativeTab === null) {
      return this.globals[prop];
    }
    return this.tabContext.get(nativeTab)[prop];
  }

  /**
   * Triggers this sidebar action for the given window, with the same effects as
   * if it were toggled via menu or toolbarbutton by a user.
   *
   * @param {ChromeWindow} window
   */
  triggerAction(window) {
    let {SidebarUI} = window;
    if (SidebarUI) {
      SidebarUI.toggle(this.id);
    }
  }

  /**
   * Opens this sidebar action for the given window.
   *
   * @param {ChromeWindow} window
   */
  open(window) {
    let {SidebarUI} = window;
    if (SidebarUI) {
      SidebarUI.show(this.id);
    }
  }

  /**
   * Closes this sidebar action for the given window if this sidebar action is open.
   *
   * @param {ChromeWindow} window
   */
  close(window) {
    if (this.isOpen(window)) {
      window.SidebarUI.hide();
    }
  }

  /**
   * Checks whether this sidebar action is open in the given window.
   *
   * @param {ChromeWindow} window
   * @returns {boolean}
   */
  isOpen(window) {
    let {SidebarUI} = window;
    return SidebarUI.isOpen && this.id == SidebarUI.currentID;
  }

  getAPI(context) {
    let {extension} = context;
    const sidebarAction = this;

    function getTab(tabId) {
      if (tabId !== null) {
        return tabTracker.getTab(tabId);
      }
      return null;
    }

    return {
      sidebarAction: {
        async setTitle(details) {
          let nativeTab = getTab(details.tabId);
          sidebarAction.setProperty(nativeTab, "title", details.title);
        },

        getTitle(details) {
          let nativeTab = getTab(details.tabId);

          let title = sidebarAction.getProperty(nativeTab, "title");
          return Promise.resolve(title);
        },

        async setIcon(details) {
          let nativeTab = getTab(details.tabId);

          let icon = IconDetails.normalize(details, extension, context);
          if (!Object.keys(icon).length) {
            icon = null;
          }
          sidebarAction.setProperty(nativeTab, "icon", icon);
        },

        async setPanel(details) {
          let nativeTab = getTab(details.tabId);

          let url;
          // Clear the url when given null or empty string.
          if (!details.panel) {
            url = null;
          } else {
            url = context.uri.resolve(details.panel);
            if (!context.checkLoadURL(url)) {
              return Promise.reject({message: `Access denied for URL ${url}`});
            }
          }

          sidebarAction.setProperty(nativeTab, "panel", url);
        },

        getPanel(details) {
          let nativeTab = getTab(details.tabId);

          let panel = sidebarAction.getProperty(nativeTab, "panel");
          return Promise.resolve(panel);
        },

        open() {
          let window = windowTracker.topWindow;
          sidebarAction.open(window);
        },

        close() {
          let window = windowTracker.topWindow;
          sidebarAction.close(window);
        },

        isOpen(details) {
          let {windowId} = details;
          if (windowId == null) {
            windowId = WINDOW_ID_CURRENT;
          }
          let window = windowTracker.getWindow(windowId, context);
          return sidebarAction.isOpen(window);
        },
      },
    };
  }
};

global.sidebarActionFor = this.sidebarAction.for;
PK
!<$ H�H�*chrome/browser/content/browser/ext-tabs.js/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";

// The ext-* files are imported into the same scopes.
/* import-globals-from ext-browser.js */

ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
                               "resource://gre/modules/PrivateBrowsingUtils.jsm");
ChromeUtils.defineModuleGetter(this, "PromiseUtils",
                               "resource://gre/modules/PromiseUtils.jsm");
ChromeUtils.defineModuleGetter(this, "Services",
                               "resource://gre/modules/Services.jsm");
ChromeUtils.defineModuleGetter(this, "SessionStore",
                               "resource:///modules/sessionstore/SessionStore.jsm");

XPCOMUtils.defineLazyGetter(this, "strBundle", function() {
  return Services.strings.createBundle("chrome://global/locale/extensions.properties");
});

var {
  ExtensionError,
} = ExtensionUtils;

const TABHIDE_PREFNAME = "extensions.webextensions.tabhide.enabled";

function showHiddenTabs(id) {
  let windowsEnum = Services.wm.getEnumerator("navigator:browser");
  while (windowsEnum.hasMoreElements()) {
    let win = windowsEnum.getNext();
    if (win.closed || !win.gBrowser) {
      continue;
    }

    for (let tab of win.gBrowser.tabs) {
      if (tab.hidden && tab.ownerGlobal &&
          SessionStore.getTabValue(tab, "hiddenBy") === id) {
        win.gBrowser.showTab(tab);
      }
    }
  }
}

let tabListener = {
  tabReadyInitialized: false,
  tabReadyPromises: new WeakMap(),
  initializingTabs: new WeakSet(),

  initTabReady() {
    if (!this.tabReadyInitialized) {
      windowTracker.addListener("progress", this);

      this.tabReadyInitialized = true;
    }
  },

  onLocationChange(browser, webProgress, request, locationURI, flags) {
    if (webProgress.isTopLevel) {
      let {gBrowser} = browser.ownerGlobal;
      let nativeTab = gBrowser.getTabForBrowser(browser);

      // Now we are certain that the first page in the tab was loaded.
      this.initializingTabs.delete(nativeTab);

      // browser.innerWindowID is now set, resolve the promises if any.
      let deferred = this.tabReadyPromises.get(nativeTab);
      if (deferred) {
        deferred.resolve(nativeTab);
        this.tabReadyPromises.delete(nativeTab);
      }
    }
  },

  /**
   * Returns a promise that resolves when the tab is ready.
   * Tabs created via the `tabs.create` method are "ready" once the location
   * changes to the requested URL. Other tabs are assumed to be ready once their
   * inner window ID is known.
   *
   * @param {XULElement} nativeTab The <tab> element.
   * @returns {Promise} Resolves with the given tab once ready.
   */
  awaitTabReady(nativeTab) {
    let deferred = this.tabReadyPromises.get(nativeTab);
    if (!deferred) {
      deferred = PromiseUtils.defer();
      if (!this.initializingTabs.has(nativeTab) &&
          (nativeTab.linkedBrowser.innerWindowID ||
           nativeTab.linkedBrowser.currentURI.spec === "about:blank")) {
        deferred.resolve(nativeTab);
      } else {
        this.initTabReady();
        this.tabReadyPromises.set(nativeTab, deferred);
      }
    }
    return deferred.promise;
  },
};

this.tabs = class extends ExtensionAPI {
  static onUpdate(id, manifest) {
    if (!manifest.permissions || !manifest.permissions.includes("tabHide")) {
      showHiddenTabs(id);
    }
  }

  static onDisable(id) {
    showHiddenTabs(id);
  }

  getAPI(context) {
    let {extension} = context;

    let {tabManager} = extension;

    function getTabOrActive(tabId) {
      if (tabId !== null) {
        return tabTracker.getTab(tabId);
      }
      return tabTracker.activeTab;
    }

    async function promiseTabWhenReady(tabId) {
      let tab;
      if (tabId !== null) {
        tab = tabManager.get(tabId);
      } else {
        tab = tabManager.getWrapper(tabTracker.activeTab);
      }

      await tabListener.awaitTabReady(tab.nativeTab);

      return tab;
    }

    let self = {
      tabs: {
        onActivated: new EventManager(context, "tabs.onActivated", fire => {
          let listener = (eventName, event) => {
            fire.async(event);
          };

          tabTracker.on("tab-activated", listener);
          return () => {
            tabTracker.off("tab-activated", listener);
          };
        }).api(),

        onCreated: new EventManager(context, "tabs.onCreated", fire => {
          let listener = (eventName, event) => {
            fire.async(tabManager.convert(event.nativeTab, event.currentTab));
          };

          tabTracker.on("tab-created", listener);
          return () => {
            tabTracker.off("tab-created", listener);
          };
        }).api(),

        /**
         * Since multiple tabs currently can't be highlighted, onHighlighted
         * essentially acts an alias for self.tabs.onActivated but returns
         * the tabId in an array to match the API.
         * @see  https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/Tabs/onHighlighted
        */
        onHighlighted: new EventManager(context, "tabs.onHighlighted", fire => {
          let listener = (eventName, event) => {
            fire.async({tabIds: [event.tabId], windowId: event.windowId});
          };

          tabTracker.on("tab-activated", listener);
          return () => {
            tabTracker.off("tab-activated", listener);
          };
        }).api(),

        onAttached: new EventManager(context, "tabs.onAttached", fire => {
          let listener = (eventName, event) => {
            fire.async(event.tabId, {newWindowId: event.newWindowId, newPosition: event.newPosition});
          };

          tabTracker.on("tab-attached", listener);
          return () => {
            tabTracker.off("tab-attached", listener);
          };
        }).api(),

        onDetached: new EventManager(context, "tabs.onDetached", fire => {
          let listener = (eventName, event) => {
            fire.async(event.tabId, {oldWindowId: event.oldWindowId, oldPosition: event.oldPosition});
          };

          tabTracker.on("tab-detached", listener);
          return () => {
            tabTracker.off("tab-detached", listener);
          };
        }).api(),

        onRemoved: new EventManager(context, "tabs.onRemoved", fire => {
          let listener = (eventName, event) => {
            fire.async(event.tabId, {windowId: event.windowId, isWindowClosing: event.isWindowClosing});
          };

          tabTracker.on("tab-removed", listener);
          return () => {
            tabTracker.off("tab-removed", listener);
          };
        }).api(),

        onReplaced: new EventManager(context, "tabs.onReplaced", fire => {
          return () => {};
        }).api(),

        onMoved: new EventManager(context, "tabs.onMoved", fire => {
          // There are certain circumstances where we need to ignore a move event.
          //
          // Namely, the first time the tab is moved after it's created, we need
          // to report the final position as the initial position in the tab's
          // onAttached or onCreated event. This is because most tabs are inserted
          // in a temporary location and then moved after the TabOpen event fires,
          // which generates a TabOpen event followed by a TabMove event, which
          // does not match the contract of our API.
          let ignoreNextMove = new WeakSet();

          let openListener = event => {
            ignoreNextMove.add(event.target);
            // Remove the tab from the set on the next tick, since it will already
            // have been moved by then.
            Promise.resolve().then(() => {
              ignoreNextMove.delete(event.target);
            });
          };

          let moveListener = event => {
            let nativeTab = event.originalTarget;

            if (ignoreNextMove.has(nativeTab)) {
              ignoreNextMove.delete(nativeTab);
              return;
            }

            fire.async(tabTracker.getId(nativeTab), {
              windowId: windowTracker.getId(nativeTab.ownerGlobal),
              fromIndex: event.detail,
              toIndex: nativeTab._tPos,
            });
          };

          windowTracker.addListener("TabMove", moveListener);
          windowTracker.addListener("TabOpen", openListener);
          return () => {
            windowTracker.removeListener("TabMove", moveListener);
            windowTracker.removeListener("TabOpen", openListener);
          };
        }).api(),

        onUpdated: new EventManager(context, "tabs.onUpdated", fire => {
          const restricted = ["url", "favIconUrl", "title"];

          function sanitize(extension, changeInfo) {
            let result = {};
            let nonempty = false;
            for (let prop in changeInfo) {
              if (extension.hasPermission("tabs") || !restricted.includes(prop)) {
                nonempty = true;
                result[prop] = changeInfo[prop];
              }
            }
            return [nonempty, result];
          }

          let fireForTab = (tab, changed) => {
            let [needed, changeInfo] = sanitize(extension, changed);
            if (needed) {
              fire.async(tab.id, changeInfo, tab.convert());
            }
          };

          let listener = event => {
            let needed = [];
            if (event.type == "TabAttrModified") {
              let changed = event.detail.changed;
              if (changed.includes("image")) {
                needed.push("favIconUrl");
              }
              if (changed.includes("muted")) {
                needed.push("mutedInfo");
              }
              if (changed.includes("soundplaying")) {
                needed.push("audible");
              }
              if (changed.includes("label")) {
                needed.push("title");
              }
              if (changed.includes("sharing")) {
                needed.push("sharingState");
              }
            } else if (event.type == "TabPinned") {
              needed.push("pinned");
            } else if (event.type == "TabUnpinned") {
              needed.push("pinned");
            } else if (event.type == "TabBrowserInserted" &&
                       !event.detail.insertedOnTabCreation) {
              needed.push("discarded");
            } else if (event.type == "TabBrowserDiscarded") {
              needed.push("discarded");
            } else if (event.type == "TabShow") {
              needed.push("hidden");
            } else if (event.type == "TabHide") {
              needed.push("hidden");
            }

            let tab = tabManager.getWrapper(event.originalTarget);
            let changeInfo = {};
            for (let prop of needed) {
              changeInfo[prop] = tab[prop];
            }

            fireForTab(tab, changeInfo);
          };

          let statusListener = ({browser, status, url}) => {
            let {gBrowser} = browser.ownerGlobal;
            let tabElem = gBrowser.getTabForBrowser(browser);
            if (tabElem) {
              let changed = {status};
              if (url) {
                changed.url = url;
              }

              fireForTab(tabManager.wrapTab(tabElem), changed);
            }
          };

          let isArticleChangeListener = (messageName, message) => {
            let {gBrowser} = message.target.ownerGlobal;
            let nativeTab = gBrowser.getTabForBrowser(message.target);

            if (nativeTab) {
              let tab = tabManager.getWrapper(nativeTab);
              fireForTab(tab, {isArticle: message.data.isArticle});
            }
          };

          windowTracker.addListener("status", statusListener);
          windowTracker.addListener("TabAttrModified", listener);
          windowTracker.addListener("TabPinned", listener);
          windowTracker.addListener("TabUnpinned", listener);
          windowTracker.addListener("TabBrowserInserted", listener);
          windowTracker.addListener("TabBrowserDiscarded", listener);
          windowTracker.addListener("TabShow", listener);
          windowTracker.addListener("TabHide", listener);

          tabTracker.on("tab-isarticle", isArticleChangeListener);

          return () => {
            windowTracker.removeListener("status", statusListener);
            windowTracker.removeListener("TabAttrModified", listener);
            windowTracker.removeListener("TabPinned", listener);
            windowTracker.removeListener("TabUnpinned", listener);
            windowTracker.removeListener("TabBrowserInserted", listener);
            windowTracker.removeListener("TabBrowserDiscarded", listener);
            windowTracker.removeListener("TabShow", listener);
            windowTracker.removeListener("TabHide", listener);
            tabTracker.off("tab-isarticle", isArticleChangeListener);
          };
        }).api(),

        create(createProperties) {
          return new Promise((resolve, reject) => {
            let window = createProperties.windowId !== null ?
              windowTracker.getWindow(createProperties.windowId, context) :
              windowTracker.topNormalWindow;

            if (!window.gBrowser) {
              let obs = (finishedWindow, topic, data) => {
                if (finishedWindow != window) {
                  return;
                }
                Services.obs.removeObserver(obs, "browser-delayed-startup-finished");
                resolve(window);
              };
              Services.obs.addObserver(obs, "browser-delayed-startup-finished");
            } else {
              resolve(window);
            }
          }).then(window => {
            let url;

            if (createProperties.url !== null) {
              url = context.uri.resolve(createProperties.url);

              if (!context.checkLoadURL(url, {dontReportErrors: true})) {
                return Promise.reject({message: `Illegal URL: ${url}`});
              }

              if (createProperties.openInReaderMode) {
                url = `about:reader?url=${encodeURIComponent(url)}`;
              }
            }

            if (createProperties.cookieStoreId && !extension.hasPermission("cookies")) {
              return Promise.reject({message: `No permission for cookieStoreId: ${createProperties.cookieStoreId}`});
            }

            let options = {};
            if (createProperties.cookieStoreId) {
              if (!global.isValidCookieStoreId(createProperties.cookieStoreId)) {
                return Promise.reject({message: `Illegal cookieStoreId: ${createProperties.cookieStoreId}`});
              }

              let privateWindow = PrivateBrowsingUtils.isBrowserPrivate(window.gBrowser);
              if (privateWindow && !global.isPrivateCookieStoreId(createProperties.cookieStoreId)) {
                return Promise.reject({message: `Illegal to set non-private cookieStoreId in a private window`});
              }

              if (!privateWindow && global.isPrivateCookieStoreId(createProperties.cookieStoreId)) {
                return Promise.reject({message: `Illegal to set private cookieStoreId in a non-private window`});
              }

              if (global.isContainerCookieStoreId(createProperties.cookieStoreId)) {
                let containerId = global.getContainerForCookieStoreId(createProperties.cookieStoreId);
                if (!containerId) {
                  return Promise.reject({message: `No cookie store exists with ID ${createProperties.cookieStoreId}`});
                }

                options.userContextId = containerId;
              }
            }

            // Make sure things like about:blank and data: URIs never inherit,
            // and instead always get a NullPrincipal.
            options.disallowInheritPrincipal = true;

            tabListener.initTabReady();
            let currentTab = window.gBrowser.selectedTab;

            if (createProperties.openerTabId !== null) {
              options.ownerTab = tabTracker.getTab(createProperties.openerTabId);
              options.openerBrowser = options.ownerTab.linkedBrowser;
              if (options.ownerTab.ownerGlobal !== window) {
                return Promise.reject({message: "Opener tab must be in the same window as the tab being created"});
              }
            }

            let nativeTab = window.gBrowser.addTab(url || window.BROWSER_NEW_TAB_URL, options);

            let active = true;
            if (createProperties.active !== null) {
              active = createProperties.active;
            }
            if (active) {
              window.gBrowser.selectedTab = nativeTab;
            }

            if (createProperties.index !== null) {
              window.gBrowser.moveTabTo(nativeTab, createProperties.index);
            }

            if (createProperties.pinned) {
              window.gBrowser.pinTab(nativeTab);
            }

            if (active && !url) {
              window.focusAndSelectUrlBar();
            }

            if (createProperties.url && createProperties.url !== window.BROWSER_NEW_TAB_URL) {
              // We can't wait for a location change event for about:newtab,
              // since it may be pre-rendered, in which case its initial
              // location change event has already fired.

              // Mark the tab as initializing, so that operations like
              // `executeScript` wait until the requested URL is loaded in
              // the tab before dispatching messages to the inner window
              // that contains the URL we're attempting to load.
              tabListener.initializingTabs.add(nativeTab);
            }

            return tabManager.convert(nativeTab, currentTab);
          });
        },

        async remove(tabs) {
          if (!Array.isArray(tabs)) {
            tabs = [tabs];
          }

          for (let tabId of tabs) {
            let nativeTab = tabTracker.getTab(tabId);
            nativeTab.ownerGlobal.gBrowser.removeTab(nativeTab);
          }
        },

        async discard(tabIds) {
          if (!Array.isArray(tabIds)) {
            tabIds = [tabIds];
          }
          let tabs = tabIds.map(tabId => tabTracker.getTab(tabId));

          for (let tab of tabs) {
            tab.ownerGlobal.gBrowser.discardBrowser(tab.linkedBrowser);
          }
        },

        async update(tabId, updateProperties) {
          let nativeTab = getTabOrActive(tabId);

          let tabbrowser = nativeTab.ownerGlobal.gBrowser;

          if (updateProperties.url !== null) {
            let url = context.uri.resolve(updateProperties.url);

            if (!context.checkLoadURL(url, {dontReportErrors: true})) {
              return Promise.reject({message: `Illegal URL: ${url}`});
            }

            let options = {
              flags: updateProperties.loadReplace
                      ? Ci.nsIWebNavigation.LOAD_FLAGS_REPLACE_HISTORY
                      : Ci.nsIWebNavigation.LOAD_FLAGS_NONE,
              triggeringPrincipal: context.principal,
            };
            nativeTab.linkedBrowser.loadURIWithFlags(url, options);
          }

          if (updateProperties.active !== null) {
            if (updateProperties.active) {
              tabbrowser.selectedTab = nativeTab;
            } else {
              // Not sure what to do here? Which tab should we select?
            }
          }
          if (updateProperties.muted !== null) {
            if (nativeTab.muted != updateProperties.muted) {
              nativeTab.toggleMuteAudio(extension.id);
            }
          }
          if (updateProperties.pinned !== null) {
            if (updateProperties.pinned) {
              tabbrowser.pinTab(nativeTab);
            } else {
              tabbrowser.unpinTab(nativeTab);
            }
          }
          if (updateProperties.openerTabId !== null) {
            let opener = tabTracker.getTab(updateProperties.openerTabId);
            if (opener.ownerDocument !== nativeTab.ownerDocument) {
              return Promise.reject({message: "Opener tab must be in the same window as the tab being updated"});
            }
            tabTracker.setOpener(nativeTab, opener);
          }

          return tabManager.convert(nativeTab);
        },

        async reload(tabId, reloadProperties) {
          let nativeTab = getTabOrActive(tabId);

          let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
          if (reloadProperties && reloadProperties.bypassCache) {
            flags |= Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE;
          }
          nativeTab.linkedBrowser.reloadWithFlags(flags);
        },

        async get(tabId) {
          return tabManager.get(tabId).convert();
        },

        getCurrent() {
          let tabData;
          if (context.tabId) {
            tabData = tabManager.get(context.tabId).convert();
          }
          return Promise.resolve(tabData);
        },

        async query(queryInfo) {
          if (!extension.hasPermission("tabs")) {
            if (queryInfo.url !== null || queryInfo.title !== null) {
              return Promise.reject({message: 'The "tabs" permission is required to use the query API with the "url" or "title" parameters'});
            }
          }

          queryInfo = Object.assign({}, queryInfo);

          if (queryInfo.url !== null) {
            queryInfo.url = new MatchPatternSet([].concat(queryInfo.url));
          }
          if (queryInfo.title !== null) {
            queryInfo.title = new MatchGlob(queryInfo.title);
          }

          return Array.from(tabManager.query(queryInfo, context),
                            tab => tab.convert());
        },

        async captureTab(tabId, options) {
          let nativeTab = getTabOrActive(tabId);
          await tabListener.awaitTabReady(nativeTab);

          let tab = tabManager.wrapTab(nativeTab);
          return tab.capture(context, options);
        },

        async captureVisibleTab(windowId, options) {
          let window = windowId == null ?
            windowTracker.topWindow :
            windowTracker.getWindow(windowId, context);

          let tab = tabManager.wrapTab(window.gBrowser.selectedTab);
          await tabListener.awaitTabReady(tab.nativeTab);

          return tab.capture(context, options);
        },

        async detectLanguage(tabId) {
          let tab = await promiseTabWhenReady(tabId);

          return tab.sendMessage(context, "Extension:DetectLanguage");
        },

        async executeScript(tabId, details) {
          let tab = await promiseTabWhenReady(tabId);

          return tab.executeScript(context, details);
        },

        async insertCSS(tabId, details) {
          let tab = await promiseTabWhenReady(tabId);

          return tab.insertCSS(context, details);
        },

        async removeCSS(tabId, details) {
          let tab = await promiseTabWhenReady(tabId);

          return tab.removeCSS(context, details);
        },

        async move(tabIds, moveProperties) {
          let tabsMoved = [];
          if (!Array.isArray(tabIds)) {
            tabIds = [tabIds];
          }

          let destinationWindow = null;
          if (moveProperties.windowId !== null) {
            destinationWindow = windowTracker.getWindow(moveProperties.windowId);
            // Fail on an invalid window.
            if (!destinationWindow) {
              return Promise.reject({message: `Invalid window ID: ${moveProperties.windowId}`});
            }
          }

          /*
            Indexes are maintained on a per window basis so that a call to
              move([tabA, tabB], {index: 0})
                -> tabA to 0, tabB to 1 if tabA and tabB are in the same window
              move([tabA, tabB], {index: 0})
                -> tabA to 0, tabB to 0 if tabA and tabB are in different windows
          */
          let indexMap = new Map();
          let lastInsertion = new Map();

          let tabs = tabIds.map(tabId => tabTracker.getTab(tabId));
          for (let nativeTab of tabs) {
            // If the window is not specified, use the window from the tab.
            let window = destinationWindow || nativeTab.ownerGlobal;
            let gBrowser = window.gBrowser;

            // If we are not moving the tab to a different window, and the window
            // only has one tab, do nothing.
            if (nativeTab.ownerGlobal == window && gBrowser.tabs.length === 1) {
              continue;
            }

            let insertionPoint = indexMap.get(window) || moveProperties.index;
            // If the index is -1 it should go to the end of the tabs.
            if (insertionPoint == -1) {
              insertionPoint = gBrowser.tabs.length;
            }

            // We can only move pinned tabs to a point within, or just after,
            // the current set of pinned tabs. Unpinned tabs, likewise, can only
            // be moved to a position after the current set of pinned tabs.
            // Attempts to move a tab to an illegal position are ignored.
            let numPinned = gBrowser._numPinnedTabs;
            let ok = nativeTab.pinned ? insertionPoint <= numPinned : insertionPoint >= numPinned;
            if (!ok) {
              continue;
            }

            // If this is not the first tab to be inserted into this window and
            // the insertion point is the same as the last insertion and
            // the tab is further to the right than the current insertion point
            // then you need to bump up the insertion point. See bug 1323311.
            if (lastInsertion.has(window) &&
                lastInsertion.get(window) === insertionPoint &&
                nativeTab._tPos > insertionPoint) {
              insertionPoint++;
              indexMap.set(window, insertionPoint);
            }

            if (nativeTab.ownerGlobal != window) {
              // If the window we are moving the tab in is different, then move the tab
              // to the new window.
              nativeTab = gBrowser.adoptTab(nativeTab, insertionPoint, false);
            } else {
              // If the window we are moving is the same, just move the tab.
              gBrowser.moveTabTo(nativeTab, insertionPoint);
            }
            lastInsertion.set(window, nativeTab._tPos);
            tabsMoved.push(nativeTab);
          }

          return tabsMoved.map(nativeTab => tabManager.convert(nativeTab));
        },

        duplicate(tabId) {
          let nativeTab = tabTracker.getTab(tabId);

          let gBrowser = nativeTab.ownerGlobal.gBrowser;
          let newTab = gBrowser.duplicateTab(nativeTab);

          return new Promise(resolve => {
            // We need to use SSTabRestoring because any attributes set before
            // are ignored. SSTabRestored is too late and results in a jump in
            // the UI. See http://bit.ly/session-store-api for more information.
            newTab.addEventListener("SSTabRestoring", function() {
              // As the tab is restoring, move it to the correct position.

              // Pinned tabs that are duplicated are inserted
              // after the existing pinned tab and pinned.
              if (nativeTab.pinned) {
                gBrowser.pinTab(newTab);
              }
              gBrowser.moveTabTo(newTab, nativeTab._tPos + 1);
            }, {once: true});

            newTab.addEventListener("SSTabRestored", function() {
              // Once it has been restored, select it and return the promise.
              gBrowser.selectedTab = newTab;

              resolve(tabManager.convert(newTab));
            }, {once: true});
          });
        },

        getZoom(tabId) {
          let nativeTab = getTabOrActive(tabId);

          let {ZoomManager} = nativeTab.ownerGlobal;
          let zoom = ZoomManager.getZoomForBrowser(nativeTab.linkedBrowser);

          return Promise.resolve(zoom);
        },

        setZoom(tabId, zoom) {
          let nativeTab = getTabOrActive(tabId);

          let {FullZoom, ZoomManager} = nativeTab.ownerGlobal;

          if (zoom === 0) {
            // A value of zero means use the default zoom factor.
            return FullZoom.reset(nativeTab.linkedBrowser);
          } else if (zoom >= ZoomManager.MIN && zoom <= ZoomManager.MAX) {
            FullZoom.setZoom(zoom, nativeTab.linkedBrowser);
          } else {
            return Promise.reject({
              message: `Zoom value ${zoom} out of range (must be between ${ZoomManager.MIN} and ${ZoomManager.MAX})`,
            });
          }

          return Promise.resolve();
        },

        _getZoomSettings(tabId) {
          let nativeTab = getTabOrActive(tabId);

          let {FullZoom} = nativeTab.ownerGlobal;

          return {
            mode: "automatic",
            scope: FullZoom.siteSpecific ? "per-origin" : "per-tab",
            defaultZoomFactor: 1,
          };
        },

        getZoomSettings(tabId) {
          return Promise.resolve(this._getZoomSettings(tabId));
        },

        setZoomSettings(tabId, settings) {
          let nativeTab = getTabOrActive(tabId);

          let currentSettings = this._getZoomSettings(tabTracker.getId(nativeTab));

          if (!Object.keys(settings).every(key => settings[key] === currentSettings[key])) {
            return Promise.reject(`Unsupported zoom settings: ${JSON.stringify(settings)}`);
          }
          return Promise.resolve();
        },

        onZoomChange: new EventManager(context, "tabs.onZoomChange", fire => {
          let getZoomLevel = browser => {
            let {ZoomManager} = browser.ownerGlobal;

            return ZoomManager.getZoomForBrowser(browser);
          };

          // Stores the last known zoom level for each tab's browser.
          // WeakMap[<browser> -> number]
          let zoomLevels = new WeakMap();

          // Store the zoom level for all existing tabs.
          for (let window of windowTracker.browserWindows()) {
            for (let nativeTab of window.gBrowser.tabs) {
              let browser = nativeTab.linkedBrowser;
              zoomLevels.set(browser, getZoomLevel(browser));
            }
          }

          let tabCreated = (eventName, event) => {
            let browser = event.nativeTab.linkedBrowser;
            zoomLevels.set(browser, getZoomLevel(browser));
          };


          let zoomListener = event => {
            let browser = event.originalTarget;

            // For non-remote browsers, this event is dispatched on the document
            // rather than on the <browser>.
            if (browser instanceof Ci.nsIDOMDocument) {
              browser = browser.docShell.chromeEventHandler;
            }

            let {gBrowser} = browser.ownerGlobal;
            let nativeTab = gBrowser.getTabForBrowser(browser);
            if (!nativeTab) {
              // We only care about zoom events in the top-level browser of a tab.
              return;
            }

            let oldZoomFactor = zoomLevels.get(browser);
            let newZoomFactor = getZoomLevel(browser);

            if (oldZoomFactor != newZoomFactor) {
              zoomLevels.set(browser, newZoomFactor);

              let tabId = tabTracker.getId(nativeTab);
              fire.async({
                tabId,
                oldZoomFactor,
                newZoomFactor,
                zoomSettings: self.tabs._getZoomSettings(tabId),
              });
            }
          };

          tabTracker.on("tab-attached", tabCreated);
          tabTracker.on("tab-created", tabCreated);

          windowTracker.addListener("FullZoomChange", zoomListener);
          windowTracker.addListener("TextZoomChange", zoomListener);
          return () => {
            tabTracker.off("tab-attached", tabCreated);
            tabTracker.off("tab-created", tabCreated);

            windowTracker.removeListener("FullZoomChange", zoomListener);
            windowTracker.removeListener("TextZoomChange", zoomListener);
          };
        }).api(),

        print() {
          let activeTab = getTabOrActive(null);
          let {PrintUtils} = activeTab.ownerGlobal;

          PrintUtils.printWindow(activeTab.linkedBrowser.outerWindowID, activeTab.linkedBrowser);
        },

        printPreview() {
          let activeTab = getTabOrActive(null);
          let {
            PrintUtils,
            PrintPreviewListener,
          } = activeTab.ownerGlobal;

          return new Promise((resolve, reject) => {
            let ppBrowser = PrintUtils._shouldSimplify ?
              PrintPreviewListener.getSimplifiedPrintPreviewBrowser() :
              PrintPreviewListener.getPrintPreviewBrowser();

            let mm = ppBrowser.messageManager;

            let onEntered = (message) => {
              mm.removeMessageListener("Printing:Preview:Entered", onEntered);
              if (message.data.failed) {
                reject({message: "Print preview failed"});
              }
              resolve();
            };

            mm.addMessageListener("Printing:Preview:Entered", onEntered);

            PrintUtils.printPreview(PrintPreviewListener);
          });
        },

        saveAsPDF(pageSettings) {
          let activeTab = getTabOrActive(null);
          let picker = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
          let title = strBundle.GetStringFromName("saveaspdf.saveasdialog.title");

          if (AppConstants.platform === "macosx") {
            return Promise.reject({message: "Not supported on Mac OS X"});
          }

          picker.init(activeTab.ownerGlobal, title, Ci.nsIFilePicker.modeSave);
          picker.appendFilter("PDF", "*.pdf");
          picker.defaultExtension = "pdf";
          picker.defaultString = activeTab.linkedBrowser.contentTitle + ".pdf";

          return new Promise(resolve => {
            picker.open(function(retval) {
              if (retval == 0 || retval == 2) {
                // OK clicked (retval == 0) or replace confirmed (retval == 2)

                // Workaround: When trying to replace an existing file that is open in another application (i.e. a locked file),
                // the print progress listener is never called. This workaround ensures that a correct status is always returned.
                try {
                  let fstream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream);
                  fstream.init(picker.file, 0x2A, 0o666, 0); // ioflags = write|create|truncate, file permissions = rw-rw-rw-
                  fstream.close();
                } catch (e) {
                  resolve(retval == 0 ? "not_saved" : "not_replaced");
                  return;
                }

                let psService = Cc["@mozilla.org/gfx/printsettings-service;1"].getService(Ci.nsIPrintSettingsService);
                let printSettings = psService.newPrintSettings;

                printSettings.printerName = "";
                printSettings.isInitializedFromPrinter = true;
                printSettings.isInitializedFromPrefs = true;

                printSettings.printToFile = true;
                printSettings.toFileName = picker.file.path;

                printSettings.printSilent = true;
                printSettings.showPrintProgress = false;

                printSettings.printFrameType = Ci.nsIPrintSettings.kFramesAsIs;
                printSettings.outputFormat = Ci.nsIPrintSettings.kOutputFormatPDF;

                if (pageSettings.paperSizeUnit !== null) {
                  printSettings.paperSizeUnit = pageSettings.paperSizeUnit;
                }
                if (pageSettings.paperWidth !== null) {
                  printSettings.paperWidth = pageSettings.paperWidth;
                }
                if (pageSettings.paperHeight !== null) {
                  printSettings.paperHeight = pageSettings.paperHeight;
                }
                if (pageSettings.orientation !== null) {
                  printSettings.orientation = pageSettings.orientation;
                }
                if (pageSettings.scaling !== null) {
                  printSettings.scaling = pageSettings.scaling;
                }
                if (pageSettings.shrinkToFit !== null) {
                  printSettings.shrinkToFit = pageSettings.shrinkToFit;
                }
                if (pageSettings.showBackgroundColors !== null) {
                  printSettings.printBGColors = pageSettings.showBackgroundColors;
                }
                if (pageSettings.showBackgroundImages !== null) {
                  printSettings.printBGImages = pageSettings.showBackgroundImages;
                }
                if (pageSettings.edgeLeft !== null) {
                  printSettings.edgeLeft = pageSettings.edgeLeft;
                }
                if (pageSettings.edgeRight !== null) {
                  printSettings.edgeRight = pageSettings.edgeRight;
                }
                if (pageSettings.edgeTop !== null) {
                  printSettings.edgeTop = pageSettings.edgeTop;
                }
                if (pageSettings.edgeBottom !== null) {
                  printSettings.edgeBottom = pageSettings.edgeBottom;
                }
                if (pageSettings.marginLeft !== null) {
                  printSettings.marginLeft = pageSettings.marginLeft;
                }
                if (pageSettings.marginRight !== null) {
                  printSettings.marginRight = pageSettings.marginRight;
                }
                if (pageSettings.marginTop !== null) {
                  printSettings.marginTop = pageSettings.marginTop;
                }
                if (pageSettings.marginBottom !== null) {
                  printSettings.marginBottom = pageSettings.marginBottom;
                }
                if (pageSettings.headerLeft !== null) {
                  printSettings.headerStrLeft = pageSettings.headerLeft;
                }
                if (pageSettings.headerCenter !== null) {
                  printSettings.headerStrCenter = pageSettings.headerCenter;
                }
                if (pageSettings.headerRight !== null) {
                  printSettings.headerStrRight = pageSettings.headerRight;
                }
                if (pageSettings.footerLeft !== null) {
                  printSettings.footerStrLeft = pageSettings.footerLeft;
                }
                if (pageSettings.footerCenter !== null) {
                  printSettings.footerStrCenter = pageSettings.footerCenter;
                }
                if (pageSettings.footerRight !== null) {
                  printSettings.footerStrRight = pageSettings.footerRight;
                }

                let printProgressListener = {
                  onLocationChange(webProgress, request, location, flags) { },
                  onProgressChange(webProgress, request, curSelfProgress, maxSelfProgress, curTotalProgress, maxTotalProgress) { },
                  onSecurityChange(webProgress, request, state) { },
                  onStateChange(webProgress, request, flags, status) {
                    if ((flags & Ci.nsIWebProgressListener.STATE_STOP) && (flags & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT)) {
                      resolve(retval == 0 ? "saved" : "replaced");
                    }
                  },
                  onStatusChange: function(webProgress, request, status, message) {
                    if (status != 0) {
                      resolve(retval == 0 ? "not_saved" : "not_replaced");
                    }
                  },
                  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener]),
                };

                activeTab.linkedBrowser.print(activeTab.linkedBrowser.outerWindowID, printSettings, printProgressListener);
              } else {
                // Cancel clicked (retval == 1)
                resolve("canceled");
              }
            });
          });
        },

        async toggleReaderMode(tabId) {
          let tab = await promiseTabWhenReady(tabId);
          if (!tab.isInReaderMode && !tab.isArticle) {
            throw new ExtensionError("The specified tab cannot be placed into reader mode.");
          }
          tab = getTabOrActive(tabId);

          tab.linkedBrowser.messageManager.sendAsyncMessage("Reader:ToggleReaderMode");
        },

        show(tabIds) {
          if (!Services.prefs.getBoolPref(TABHIDE_PREFNAME, false)) {
            throw new ExtensionError(`tabs.show is currently experimental and must be enabled with the ${TABHIDE_PREFNAME} preference.`);
          }

          if (!Array.isArray(tabIds)) {
            tabIds = [tabIds];
          }

          for (let tabId of tabIds) {
            let tab = tabTracker.getTab(tabId);
            if (tab.ownerGlobal) {
              tab.ownerGlobal.gBrowser.showTab(tab);
            }
          }
        },

        hide(tabIds) {
          if (!Services.prefs.getBoolPref(TABHIDE_PREFNAME, false)) {
            throw new ExtensionError(`tabs.hide is currently experimental and must be enabled with the ${TABHIDE_PREFNAME} preference.`);
          }

          if (!Array.isArray(tabIds)) {
            tabIds = [tabIds];
          }

          let hidden = [];
          let tabs = tabIds.map(tabId => tabTracker.getTab(tabId));
          for (let tab of tabs) {
            if (tab.ownerGlobal && !tab.hidden) {
              tab.ownerGlobal.gBrowser.hideTab(tab, extension.id);
              if (tab.hidden) {
                hidden.push(tabTracker.getId(tab));
              }
            }
          }
          return hidden;
        },
      },
    };
    return self;
  }
};
PK
!<N}���$�$3chrome/browser/content/browser/ext-url-overrides.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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-globals-from ext-browser.js */

"use strict";

ChromeUtils.defineModuleGetter(this, "AddonManager",
                               "resource://gre/modules/AddonManager.jsm");
ChromeUtils.defineModuleGetter(this, "BrowserUtils",
                               "resource://gre/modules/BrowserUtils.jsm");
ChromeUtils.defineModuleGetter(this, "CustomizableUI",
                               "resource:///modules/CustomizableUI.jsm");
ChromeUtils.defineModuleGetter(this, "ExtensionSettingsStore",
                               "resource://gre/modules/ExtensionSettingsStore.jsm");

XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
                                   "@mozilla.org/browser/aboutnewtab-service;1",
                                   "nsIAboutNewTabService");

const STORE_TYPE = "url_overrides";
const NEW_TAB_SETTING_NAME = "newTabURL";
const NEW_TAB_CONFIRMED_TYPE = "newTabNotification";
const PREF_BRANCH_INSTALLED_ADDON = "extensions.installedDistroAddon.";

XPCOMUtils.defineLazyGetter(this, "strBundle", function() {
  return Services.strings.createBundle("chrome://global/locale/extensions.properties");
});

function userWasNotified(extensionId) {
  let setting = ExtensionSettingsStore.getSetting(NEW_TAB_CONFIRMED_TYPE, extensionId);
  return setting && setting.value;
}

function getAddonDetails(doc, addon) {
  const defaultIcon = "chrome://mozapps/skin/extensions/extensionGeneric.svg";

  let image = doc.createElement("image");
  image.setAttribute("src", addon.iconURL || defaultIcon);
  image.classList.add("extension-controlled-icon");

  let addonDetails = doc.createDocumentFragment();
  addonDetails.appendChild(image);
  addonDetails.appendChild(doc.createTextNode(" " + addon.name));

  return addonDetails;
}

function replaceUrlInTab(gBrowser, tab, url) {
  let loaded = new Promise(resolve => {
    windowTracker.addListener("progress", {
      onLocationChange(browser, webProgress, request, locationURI, flags) {
        if (webProgress.isTopLevel
            && browser.ownerGlobal.gBrowser.getTabForBrowser(browser) == tab
            && locationURI.spec == url) {
          windowTracker.removeListener(this);
          resolve();
        }
      },
    });
  });
  gBrowser.loadURIWithFlags(
    url, {flags: Ci.nsIWebNavigation.LOAD_FLAGS_REPLACE_HISTORY});
  return loaded;
}

let gDistributionAddonsList;

function isDistributionAddon(id) {
  if (!gDistributionAddonsList) {
    gDistributionAddonsList = Services.prefs.getChildList(PREF_BRANCH_INSTALLED_ADDON)
                                      .map(id => id.replace(PREF_BRANCH_INSTALLED_ADDON, ""));
  }
  return gDistributionAddonsList.includes(id);
}

async function handleNewTabOpened() {
  // We don't need to open the doorhanger again until the controlling add-on changes.
  // eslint-disable-next-line no-use-before-define
  removeNewTabObserver();

  let item = ExtensionSettingsStore.getSetting(STORE_TYPE, NEW_TAB_SETTING_NAME);

  if (!item || !item.id || userWasNotified(item.id) || isDistributionAddon(item.id)) {
    return;
  }

  // Find the elements we need.
  let win = windowTracker.getCurrentWindow({});
  let doc = win.document;
  let panel = doc.getElementById("extension-notification-panel");
  let addon = await AddonManager.getAddonByID(item.id);

  let description = doc.getElementById("extension-new-tab-notification-description");
  while (description.firstChild) {
    description.firstChild.remove();
  }
  let message = strBundle.GetStringFromName("newTabControlled.message2");
  let addonDetails = getAddonDetails(doc, addon);
  description.appendChild(
    BrowserUtils.getLocalizedFragment(doc, message, addonDetails));

  // Add the Learn more link to the description.
  let link = doc.createElement("label");
  link.setAttribute("class", "learnMore text-link");
  link.href = Services.urlFormatter.formatURLPref("app.support.baseURL") + "extension-home";
  link.textContent = strBundle.GetStringFromName("newTabControlled.learnMore");
  description.appendChild(link);

  // Setup the command handler.
  let handleCommand = async (event) => {
    if (event.originalTarget.getAttribute("anonid") == "button") {
      // Main action is to keep changes.
      await ExtensionSettingsStore.addSetting(
        item.id, NEW_TAB_CONFIRMED_TYPE, item.id, true, () => false);
    } else {
      // Secondary action is to restore settings. Disabling an add-on should remove
      // the tabs that it has open, but we want to open the new New Tab in this tab.
      //   1. Replace the tab's URL with about:blank, wait for it to change
      //   2. Now that this tab isn't associated with the add-on, disable the add-on
      //   3. Replace the tab's URL with the new New Tab URL
      ExtensionSettingsStore.removeSetting(NEW_TAB_CONFIRMED_TYPE, item.id);
      let gBrowser = win.gBrowser;
      let tab = gBrowser.selectedTab;
      await replaceUrlInTab(gBrowser, tab, "about:blank");
      Services.obs.addObserver({
        async observe() {
          await replaceUrlInTab(gBrowser, tab, aboutNewTabService.newTabURL);
          handleNewTabOpened();
          Services.obs.removeObserver(this, "newtab-url-changed");
        },
      }, "newtab-url-changed");

      addon.userDisabled = true;
    }
    panel.hidePopup();
    win.gURLBar.focus();
  };
  panel.addEventListener("command", handleCommand);
  panel.addEventListener("popuphidden", () => {
    panel.removeEventListener("command", handleCommand);
  }, {once: true});

  // Look for a browserAction on the toolbar.
  let action = CustomizableUI.getWidget(
    `${global.makeWidgetId(item.id)}-browser-action`);
  if (action) {
    action = action.areaType == "toolbar" && action.forWindow(win).node;
  }

  // Anchor to a toolbar browserAction if found, otherwise use the menu button.
  let anchor = doc.getAnonymousElementByAttribute(
    action || doc.getElementById("PanelUI-menu-button"),
    "class", "toolbarbutton-icon");
  panel.hidden = false;
  panel.openPopup(anchor);
}

let newTabOpenedListener = {
  observe(subject, topic, data) {
    // Do this work in an idle callback to avoid interfering with new tab performance tracking.
    windowTracker
      .getCurrentWindow({})
      .requestIdleCallback(handleNewTabOpened);
  },
};

function removeNewTabObserver() {
  if (aboutNewTabService.willNotifyUser) {
    Services.obs.removeObserver(newTabOpenedListener, "browser-open-newtab-start");
    aboutNewTabService.willNotifyUser = false;
  }
}

function addNewTabObserver(extensionId) {
  if (!aboutNewTabService.willNotifyUser && extensionId && !userWasNotified(extensionId)) {
    Services.obs.addObserver(newTabOpenedListener, "browser-open-newtab-start");
    aboutNewTabService.willNotifyUser = true;
  }
}

function setNewTabURL(extensionId, url) {
  if (extensionId) {
    addNewTabObserver(extensionId);
  } else {
    removeNewTabObserver();
  }
  aboutNewTabService.newTabURL = url;
}

this.urlOverrides = class extends ExtensionAPI {
  processNewTabSetting(action) {
    let {extension} = this;
    let item = ExtensionSettingsStore[action](extension.id, STORE_TYPE, NEW_TAB_SETTING_NAME);
    if (item) {
      setNewTabURL(item.id, item.value || item.initialValue);
    }
  }

  async onManifestEntry(entryName) {
    let {extension} = this;
    let {manifest} = extension;

    await ExtensionSettingsStore.initialize();

    if (manifest.chrome_url_overrides.newtab) {
      // Set up the shutdown code for the setting.
      extension.callOnClose({
        close: () => {
          if (extension.shutdownReason == "ADDON_DISABLE"
              || extension.shutdownReason == "ADDON_UNINSTALL") {
            ExtensionSettingsStore.removeSetting(
              extension.id, NEW_TAB_CONFIRMED_TYPE, extension.id);
          }
          switch (extension.shutdownReason) {
            case "ADDON_DISABLE":
              this.processNewTabSetting("disable");
              break;

            // We can remove the setting on upgrade or downgrade because it will be
            // added back in when the manifest is re-read. This will cover the case
            // where a new version of an add-on removes the manifest key.
            case "ADDON_DOWNGRADE":
            case "ADDON_UPGRADE":
            case "ADDON_UNINSTALL":
              this.processNewTabSetting("removeSetting");
              break;
          }
        },
      });

      let url = extension.baseURI.resolve(manifest.chrome_url_overrides.newtab);

      let item = await ExtensionSettingsStore.addSetting(
        extension.id, STORE_TYPE, NEW_TAB_SETTING_NAME, url,
        () => aboutNewTabService.newTabURL);

      // If the extension was just re-enabled, change the setting to enabled.
      // This is required because addSetting above is used for both add and update.
      if (["ADDON_ENABLE", "ADDON_UPGRADE", "ADDON_DOWNGRADE"]
          .includes(extension.startupReason)) {
        item = ExtensionSettingsStore.enable(extension.id, STORE_TYPE, NEW_TAB_SETTING_NAME);
      }

      // Set the newTabURL to the current value of the setting.
      if (item) {
        setNewTabURL(item.id, item.value || item.initialValue);
      }
    }
  }
};
PK
!<0�_�&�&-chrome/browser/content/browser/ext-windows.js/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";

// The ext-* files are imported into the same scopes.
/* import-globals-from ext-browser.js */

XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
                                   "@mozilla.org/browser/aboutnewtab-service;1",
                                   "nsIAboutNewTabService");
ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
                               "resource://gre/modules/PrivateBrowsingUtils.jsm");

var {
  promiseObserved,
} = ExtensionUtils;

const onXULFrameLoaderCreated = ({target}) => {
  target.messageManager.sendAsyncMessage("AllowScriptsToClose", {});
};

this.windows = class extends ExtensionAPI {
  getAPI(context) {
    let {extension} = context;

    const {windowManager} = extension;

    return {
      windows: {
        onCreated:
        new WindowEventManager(context, "windows.onCreated", "domwindowopened", (fire, window) => {
          fire.async(windowManager.convert(window));
        }).api(),

        onRemoved:
        new WindowEventManager(context, "windows.onRemoved", "domwindowclosed", (fire, window) => {
          fire.async(windowTracker.getId(window));
        }).api(),

        onFocusChanged: new EventManager(context, "windows.onFocusChanged", fire => {
          // Keep track of the last windowId used to fire an onFocusChanged event
          let lastOnFocusChangedWindowId;

          let listener = event => {
            // Wait a tick to avoid firing a superfluous WINDOW_ID_NONE
            // event when switching focus between two Firefox windows.
            Promise.resolve().then(() => {
              let window = Services.focus.activeWindow;
              let windowId = window ? windowTracker.getId(window) : Window.WINDOW_ID_NONE;
              if (windowId !== lastOnFocusChangedWindowId) {
                fire.async(windowId);
                lastOnFocusChangedWindowId = windowId;
              }
            });
          };
          windowTracker.addListener("focus", listener);
          windowTracker.addListener("blur", listener);
          return () => {
            windowTracker.removeListener("focus", listener);
            windowTracker.removeListener("blur", listener);
          };
        }).api(),

        get: function(windowId, getInfo) {
          let window = windowTracker.getWindow(windowId, context);
          if (!window) {
            return Promise.reject({message: `Invalid window ID: ${windowId}`});
          }
          return Promise.resolve(windowManager.convert(window, getInfo));
        },

        getCurrent: function(getInfo) {
          let window = context.currentWindow || windowTracker.topWindow;
          return Promise.resolve(windowManager.convert(window, getInfo));
        },

        getLastFocused: function(getInfo) {
          let window = windowTracker.topWindow;
          return Promise.resolve(windowManager.convert(window, getInfo));
        },

        getAll: function(getInfo) {
          let doNotCheckTypes = getInfo === null || getInfo.windowTypes === null;

          function typeFilter(win) {
            return doNotCheckTypes || getInfo.windowTypes.includes(win.type);
          }

          let windows = Array.from(windowManager.getAll(), win => win.convert(getInfo))
                        .filter(typeFilter);

          return Promise.resolve(windows);
        },

        create: function(createData) {
          let needResize = (createData.left !== null || createData.top !== null ||
                            createData.width !== null || createData.height !== null);

          if (needResize) {
            if (createData.state !== null && createData.state != "normal") {
              return Promise.reject({message: `"state": "${createData.state}" may not be combined with "left", "top", "width", or "height"`});
            }
            createData.state = "normal";
          }

          function mkstr(s) {
            let result = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
            result.data = s;
            return result;
          }

          let args = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);

          if (createData.tabId !== null) {
            if (createData.url !== null) {
              return Promise.reject({message: "`tabId` may not be used in conjunction with `url`"});
            }

            if (createData.allowScriptsToClose) {
              return Promise.reject({message: "`tabId` may not be used in conjunction with `allowScriptsToClose`"});
            }

            let tab = tabTracker.getTab(createData.tabId);

            // Private browsing tabs can only be moved to private browsing
            // windows.
            let incognito = PrivateBrowsingUtils.isBrowserPrivate(tab.linkedBrowser);
            if (createData.incognito !== null && createData.incognito != incognito) {
              return Promise.reject({message: "`incognito` property must match the incognito state of tab"});
            }
            if (createData.incognito && !PrivateBrowsingUtils.enabled) {
              return Promise.reject({message: "`incognito` cannot be used if incognito mode is disabled"});
            }
            createData.incognito = incognito;

            args.appendElement(tab);
          } else if (createData.url !== null) {
            if (Array.isArray(createData.url)) {
              let array = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
              for (let url of createData.url) {
                array.appendElement(mkstr(url));
              }
              args.appendElement(array);
            } else {
              args.appendElement(mkstr(createData.url));
            }
          } else {
            args.appendElement(mkstr(aboutNewTabService.newTabURL));
          }

          let features = ["chrome"];

          if (createData.type === null || createData.type == "normal") {
            features.push("dialog=no", "all");
          } else {
            // All other types create "popup"-type windows by default.
            features.push("dialog", "resizable", "minimizable", "centerscreen", "titlebar", "close");
          }

          if (createData.incognito !== null) {
            if (createData.incognito) {
              features.push("private");
            } else {
              features.push("non-private");
            }
          }

          let {allowScriptsToClose, url} = createData;
          if (allowScriptsToClose === null) {
            allowScriptsToClose = typeof url === "string" && url.startsWith("moz-extension://");
          }

          let window = Services.ww.openWindow(null, "chrome://browser/content/browser.xul", "_blank",
                                              features.join(","), args);

          let win = windowManager.getWrapper(window);
          win.updateGeometry(createData);

          // TODO: focused, type

          return new Promise(resolve => {
            window.addEventListener("load", function() {
              if (["maximized", "normal"].includes(createData.state)) {
                window.document.documentElement.setAttribute("sizemode", createData.state);
              }
              resolve(promiseObserved("browser-delayed-startup-finished", win => win == window));
            }, {once: true});
          }).then(() => {
            // Some states only work after delayed-startup-finished
            if (["minimized", "fullscreen", "docked"].includes(createData.state)) {
              win.state = createData.state;
            }
            if (allowScriptsToClose) {
              for (let {linkedBrowser} of window.gBrowser.tabs) {
                onXULFrameLoaderCreated({target: linkedBrowser});
                // eslint-disable-next-line mozilla/balanced-listeners
                linkedBrowser.addEventListener("XULFrameLoaderCreated", onXULFrameLoaderCreated);
              }
            }
            if (createData.titlePreface !== null) {
              win.setTitlePreface(createData.titlePreface);
            }
            return win.convert({populate: true});
          });
        },

        update: function(windowId, updateInfo) {
          if (updateInfo.state !== null && updateInfo.state != "normal") {
            if (updateInfo.left !== null || updateInfo.top !== null ||
                updateInfo.width !== null || updateInfo.height !== null) {
              return Promise.reject({message: `"state": "${updateInfo.state}" may not be combined with "left", "top", "width", or "height"`});
            }
          }

          let win = windowManager.get(windowId, context);
          if (updateInfo.focused) {
            Services.focus.activeWindow = win.window;
          }

          if (updateInfo.state !== null) {
            win.state = updateInfo.state;
          }

          if (updateInfo.drawAttention) {
            // Bug 1257497 - Firefox can't cancel attention actions.
            win.window.getAttention();
          }

          win.updateGeometry(updateInfo);

          if (updateInfo.titlePreface !== null) {
            win.setTitlePreface(updateInfo.titlePreface);
            win.window.gBrowser.updateTitlebar();
          }

          // TODO: All the other properties, focused=false...

          return Promise.resolve(win.convert());
        },

        remove: function(windowId) {
          let window = windowTracker.getWindow(windowId, context);
          window.close();

          return new Promise(resolve => {
            let listener = () => {
              windowTracker.removeListener("domwindowclosed", listener);
              resolve();
            };
            windowTracker.addListener("domwindowclosed", listener);
          });
        },
      },
    };
  }
};
PK
!<טu�5�5,chrome/browser/content/browser/extension.css/* stylelint-disable property-no-vendor-prefix */
/* stylelint-disable property-no-vendor-prefix */

/* Global */
html,
body {
  background: transparent;
  box-sizing: border-box;
  color: #222426;
  cursor: default;
  display: flex;
  flex-direction: column;
  font: caption;
  margin: 0;
  padding: 0;
  -moz-user-select: none;
}

body * {
  box-sizing: border-box;
  text-align: start;
}

.browser-style {
  -moz-appearance: none;
  margin-bottom: 6px;
  text-align: left;
}

/* stylelint-disable property-no-vendor-prefix */
/* Buttons */
button.browser-style,
select.browser-style {
  background-color: #fbfbfb;
  border: 1px solid #b1b1b1;
  box-shadow: 0 0 0 0 transparent;
  font: caption;
  height: 24px;
  outline: 0 !important;
  padding: 0 8px 0;
  transition-duration: 250ms;
  transition-property: box-shadow, border;
}

select.browser-style {
  background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDE2IDE2Ij4KICA8cGF0aCBkPSJNOCwxMkwzLDcsNCw2bDQsNCw0LTQsMSwxWiIgZmlsbD0iIzZBNkE2QSIgLz4KPC9zdmc+Cg==);
  background-position: calc(100% - 4px) center;
  background-repeat: no-repeat;
  padding-inline-end: 24px;
  text-overflow: ellipsis;
}

label.browser-style-label {
  font: caption;
}

button.browser-style::-moz-focus-inner {
  border: 0;
  outline: 0;
}

/* Dropdowns */
select.browser-style {
  background-color: #fbfbfb;
  border: 1px solid #b1b1b1;
  box-shadow: 0 0 0 0 transparent;
  font: caption;
  height: 24px;
  outline: 0 !important;
  padding: 0 8px 0;
  transition-duration: 250ms;
  transition-property: box-shadow, border;
}

select.browser-style {
  background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDE2IDE2Ij4KICA8cGF0aCBkPSJNOCwxMkwzLDcsNCw2bDQsNCw0LTQsMSwxWiIgZmlsbD0iIzZBNkE2QSIgLz4KPC9zdmc+Cg==);
  background-position: calc(100% - 4px) center;
  background-repeat: no-repeat;
  padding-inline-end: 24px;
  text-overflow: ellipsis;
}

select.browser-style:-moz-focusring {
  color: transparent;
  text-shadow: 0 0 0 #000;
}

select.browser-style:-moz-focusring * {
  color: #000;
  text-shadow: none;
}

button.browser-style.hover,
select.browser-style.hover {
  background-color: #ebebeb;
  border: 1px solid #b1b1b1;
}

button.browser-style.pressed,
select.browser-style.pressed {
  background-color: #d4d4d4;
  border: 1px solid #858585;
}

button.browser-style.disabled,
select.browser-style.disabled {
  color: #999;
  opacity: .5;
}

button.browser-style.focused,
select.browser-style.focused {
  border-color: #fff;
  box-shadow: 0 0 0 2px rgba(97, 181, 255, 0.75);
}

button.browser-style.default {
  background-color: #0996f8;
  border-color: #0670cc;
  color: #fff;
}

button.browser-style.default.hover {
  background-color: #0670cc;
  border-color: #005bab;
}

button.browser-style.default.pressed {
  background-color: #005bab;
  border-color: #004480;
}

button.browser-style.default.focused {
  border-color: #fff;
}

/* Radio Buttons */
.browser-style > input[type="radio"] {
  display: none;
}

.browser-style > input[type="radio"] + label {
  -moz-user-select: none;
}

.browser-style > input[type="radio"] + label::before {
  background-color: #fff;
  background-position: center;
  border: 1px solid #b1b1b1;
  border-radius: 50%;
  content: "";
  display: inline-block;
  height: 16px;
  margin-right: 6px;
  vertical-align: text-top;
  width: 16px;
}

.browser-style > input[type="radio"]:hover + label::before,
.browser-style.hover > input[type="radio"]:not(active) + label::before {
  background-color: #fbfbfb;
  border-color: #b1b1b1;
}

.browser-style > input[type="radio"]:hover:active + label::before,
.browser-style.pressed > input[type="radio"]:not(active) + label::before {
  background-color: #ebebeb;
  border-color: #858585;
}

.browser-style > input[type="radio"]:checked + label::before {
  background-color: #0996f8;
  background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDE2IDE2Ij4KICA8Y2lyY2xlIGN4PSI4IiBjeT0iOCIgcj0iNCIgZmlsbD0iI2ZmZiIgLz4KPC9zdmc+Cg==);
  border-color: #0670cc;
}

.browser-style > input[type="radio"]:checked:hover + label::before,
.browser-style.hover > input[type="radio"]:checked:not(active) + label::before {
  background-color: #0670cc;
  border-color: #005bab;
}

.browser-style > input[type="radio"]:checked:hover:active + label::before,
.browser-style.pressed > input[type="radio"]:checked:not(active) + label::before {
  background-color: #005bab;
  border-color: #004480;
}

.browser-style.disabled > input[type="radio"] + label,
.browser-style.disabled > input[type="radio"]:hover + label,
.browser-style.disabled > input[type="radio"]:hover:active + label {
  color: #999;
  opacity: .5;
}

.browser-style.focused > input[type="radio"] + label::before {
  border-color: #0996f8;
  box-shadow: 0 0 0 2px rgba(97, 181, 255, 0.75);
}

.browser-style.focused > input[type="radio"]:checked + label::before {
  border-color: #fff;
}

/* Checkboxes */
.browser-style > input[type="checkbox"] {
  display: none;
}

.browser-style > input[type="checkbox"] + label {
  -moz-user-select: none;
}

.browser-style > input[type="checkbox"] + label::before {
  background-color: #fff;
  background-position: center;
  border: 1px solid #b1b1b1;
  content: "";
  display: inline-block;
  height: 16px;
  margin-right: 6px;
  vertical-align: text-top;
  width: 16px;
}

.browser-style > input[type="checkbox"]:hover + label::before,
.browser-style.hover > input[type="checkbox"]:not(active) + label::before {
  background-color: #fbfbfb;
  border-color: #b1b1b1;
}

.browser-style > input[type="checkbox"]:hover:active + label::before,
.browser-style.pressed > input[type="checkbox"]:not(active) + label::before {
  background-color: #ebebeb;
  border-color: #858585;
}

.browser-style > input[type="checkbox"]:checked + label::before {
  background-color: #0996f8;
  background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDE2IDE2Ij4KICA8cGF0aCBkPSJNNy43LDEyLjkgQzcuNCwxMy4zIDYuOCwxMy40IDYuNCwxMyBMMy4yLDkuOCBDMi44LDkuNCAyLjgsOC42IDMuMiw4LjIgQzMuNiw3LjggNC40LDcuOCA0LjgsOC4yIEw2LjksMTAuMyBMMTEuMSw0LjQgQzExLjUsMy45IDEyLjIsMy44IDEyLjcsNC4xIEMxMy4yLDQuNSAxMy4zLDUuMiAxMyw1LjcgTDcuNywxMi45IEw3LjcsMTIuOSBaIiBmaWxsPSIjZmZmIiAvPgo8L3N2Zz4K);
  border-color: #0670cc;
}

.browser-style > input[type="checkbox"]:checked:hover + label::before,
.browser-style.hover > input[type="checkbox"]:checked:not(active) + label::before {
  background-color: #0670cc;
  border-color: #005bab;
}

.browser-style > input[type="checkbox"]:checked:hover:active + label::before,
.browser-style.pressed > input[type="checkbox"]:checked:not(active) + label::before {
  background-color: #005bab;
  border-color: #004480;
}

.browser-style.disabled > input[type="checkbox"] + label,
.browser-style.disabled > input[type="checkbox"]:hover + label,
.browser-style.disabled > input[type="checkbox"]:hover:active + label {
  color: #999;
  opacity: .5;
}

.browser-style.focused > input[type="checkbox"] + label::before {
  border-color: #0996f8;
  box-shadow: 0 0 0 2px rgba(97, 181, 255, 0.75);
}

.browser-style.focused > input[type="checkbox"]:checked + label::before {
  border-color: #fff;
}

/* Expander Button */
button.browser-style.expander {
  background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDE2IDE2Ij4KICA8cGF0aCBkPSJNOCwxMkwzLDcsNCw2bDQsNCw0LTQsMSwxWiIgZmlsbD0iIzZBNkE2QSIgLz4KPC9zdmc+Cg==);
  background-position: center;
  background-repeat: no-repeat;
  height: 24px;
  padding: 0;
  width: 24px;
}

/* Interactive States */
button.browser-style:hover:not(.pressed):not(.disabled):not(.focused),
select.browser-style:hover:not(.pressed):not(.disabled):not(.focused) {
  background-color: #ebebeb;
  border: 1px solid #b1b1b1;
}

button.browser-style:hover:active:not(.hover):not(.disabled):not(.focused),
select.browser-style:hover:active:not(.hover):not(.disabled):not(.focused) {
  background-color: #d4d4d4;
  border: 1px solid #858585;
}

button.browser-style.default:hover:not(.pressed):not(.disabled):not(.focused) {
  background-color: #0670cc;
  border-color: #005bab;
}

button.browser-style.default:hover:active:not(.hover):not(.disabled):not(.focused) {
  background-color: #005bab;
  border-color: #004480;
}

button.browser-style:focus:not(.disabled) {
  border-color: #fff !important;
  box-shadow: 0 0 0 2px rgba(97, 181, 255, 0.75);
}

/* Fields */
.browser-style > input[type="text"],
textarea.browser-style {
  background-color: #fff;
  border: 1px solid #b1b1b1;
  box-shadow: 0 0 0 0 rgba(97, 181, 255, 0);
  font: caption;
  padding: 0 6px 0;
  transition-duration: 250ms;
  transition-property: box-shadow;
}

.browser-style > input[type="text"] {
  height: 24px;
}

.browser-style > input[type="text"].hover,
textarea.browser-style.hover {
  border: 1px solid #858585;
}

.browser-style > input[type="text"].disabled,
textarea.browser-style.disabled {
  color: #999;
  opacity: .5;
}

.browser-style > input[type="text"].focused,
textarea.browser-style.focused {
  border-color: #0996f8;
  box-shadow: 0 0 0 2px rgba(97, 181, 255, 0.75);
}

/* Interactive States */
.browser-style > input[type="text"]:not(disabled):hover,
textarea.browser-style:not(disabled):hover {
  border: 1px solid #858585;
}

.browser-style > input[type="text"]:focus,
.browser-style > input[type="text"]:focus:hover,
textarea.browser-style:focus,
textarea.browser-style:focus:hover {
  border-color: #0996f8;
  box-shadow: 0 0 0 2px rgba(97, 181, 255, 0.75);
}

/* stylelint-disable property-no-vendor-prefix */
.panel-section {
  display: flex;
  flex-direction: row;
}

.panel-section-separator {
  background-color: rgba(0, 0, 0, 0.15);
  min-height: 1px;
}

/* Panel Section - Header */
.panel-section-header {
  border-bottom: 1px solid rgba(0, 0, 0, 0.15);
  padding: 16px;
}

.panel-section-header > .icon-section-header {
  background-position: center center;
  background-repeat: no-repeat;
  height: 32px;
  margin-right: 16px;
  position: relative;
  width: 32px;
}

.panel-section-header > .text-section-header {
  align-self: center;
  font-size: 1.385em;
  font-weight: lighter;
}

/* Panel Section - List */
.panel-section-list {
  flex-direction: column;
  padding: 4px 0;
}

.panel-list-item {
  align-items: center;
  display: flex;
  flex-direction: row;
  height: 24px;
  padding: 0 16px;
}

.panel-list-item:not(.disabled):hover {
  background-color: rgba(0, 0, 0, 0.06);
  border-bottom: 1px solid rgba(0, 0, 0, 0.1);
  border-top: 1px solid rgba(0, 0, 0, 0.1);
}

.panel-list-item:not(.disabled):hover:active {
  background-color: rgba(0, 0, 0, 0.1);
}

.panel-list-item.disabled {
  color: #999;
}

.panel-list-item > .icon {
  flex-grow: 0;
  flex-shrink: 0;
}

.panel-list-item > .text {
  flex-grow: 10;
}

.panel-list-item > .text-shortcut {
  color: #808080;
  font-family: "Lucida Grande", caption;
  font-size: .847em;
  justify-content: flex-end;
}

.panel-section-list .panel-section-separator {
  margin: 4px 0;
}

/* Panel Section - Form Elements */
.panel-section-formElements {
  display: flex;
  flex-direction: column;
  padding: 16px;
}

.panel-formElements-item {
  align-items: center;
  display: flex;
  flex-direction: row;
  margin-bottom: 12px;
}

.panel-formElements-item:last-child {
  margin-bottom: 0;
}

.panel-formElements-item label {
  flex-shrink: 0;
  margin-right: 6px;
  text-align: right;
}

.panel-formElements-item input[type="text"],
.panel-formElements-item select.browser-style {
  flex-grow: 1;
}

/* Panel Section - Footer */
.panel-section-footer {
  background-color: rgba(0, 0, 0, 0.06);
  border-top: 1px solid rgba(0, 0, 0, 0.15);
  color: #1a1a1a;
  display: flex;
  flex-direction: row;
  height: 41px;
  margin-top: -1px;
  padding: 0;
}

.panel-section-footer-button {
  flex: 1 1 auto;
  height: 100%;
  margin: 0 -1px;
  padding: 12px;
  text-align: center;
}

.panel-section-footer-button > .text-shortcut {
  color: #808080;
  font-family: "Lucida Grande", caption;
  font-size: .847em;
}

.panel-section-footer-button:hover {
  background-color: rgba(0, 0, 0, 0.06);
}

.panel-section-footer-button:hover:active {
  background-color: rgba(0, 0, 0, 0.1);
}

.panel-section-footer-button.default {
  background-color: #0996f8;
  box-shadow: 0 1px 0 #0670cc inset;
  color: #fff;
}

.panel-section-footer-button.default:hover {
  background-color: #0670cc;
  box-shadow: 0 1px 0 #005bab inset;
}

.panel-section-footer-button.default:hover:active {
  background-color: #005bab;
  box-shadow: 0 1px 0 #004480 inset;
}

.panel-section-footer-separator {
  background-color: rgba(0, 0, 0, 0.1);
  width: 1px;
  z-index: 99;
}

/* Panel Section - Tabs */
.panel-section-tabs {
  color: #1a1a1a;
  display: flex;
  flex-direction: row;
  height: 41px;
  margin-bottom: -1px;
  padding: 0;
}

.panel-section-tabs-button {
  flex: 1 1 auto;
  height: 100%;
  margin: 0 -1px;
  padding: 12px;
  text-align: center;
}

.panel-section-tabs-button:hover {
  background-color: rgba(0, 0, 0, 0.06);
}

.panel-section-tabs-button:hover:active {
  background-color: rgba(0, 0, 0, 0.1);
}

.panel-section-tabs-button.select.browser-styleed {
  box-shadow: 0 -1px 0 #0670cc inset, 0 -4px 0 #0996f8 inset;
  color: #0996f8;
}

.panel-section-tabs-button.select.browser-styleed:hover {
  color: #0670cc;
}

.panel-section-tabs-separator {
  background-color: rgba(0, 0, 0, 0.1);
  width: 1px;
  z-index: 99;
}
PK
!<ҍ�Xuu,chrome/browser/content/browser/extension.svg<?xml version="1.0" encoding="utf-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 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
     width="64" height="64" viewBox="0 0 64 64">
  <defs>
    <style>
      .style-puzzle-piece {
        fill: url('#gradient-linear-puzzle-piece');
      }
    </style>
    <linearGradient id="gradient-linear-puzzle-piece" x1="0%" y1="0%" x2="0%" y2="100%">
      <stop offset="0%" stop-color="#66cc52" stop-opacity="1"/>
      <stop offset="100%" stop-color="#60bf4c" stop-opacity="1"/>
    </linearGradient>
  </defs>
  <path class="style-puzzle-piece" d="M42,62c2.2,0,4-1.8,4-4l0-14.2c0,0,0.4-3.7,2.8-3.7c2.4,0,2.2,3.9,6.7,3.9c2.3,0,6.2-1.2,6.2-8.2 c0-7-3.9-7.9-6.2-7.9c-4.5,0-4.3,3.7-6.7,3.7c-2.4,0-2.8-3.8-2.8-3.8V22c0-2.2-1.8-4-4-4H31.5c0,0-3.4-0.6-3.4-3 c0-2.4,3.8-2.6,3.8-7.1c0-2.3-1.3-5.9-8.3-5.9s-8,3.6-8,5.9c0,4.5,3.4,4.7,3.4,7.1c0,2.4-3.4,3-3.4,3H6c-2.2,0-4,1.8-4,4l0,7.8 c0,0-0.4,6,4.4,6c3.1,0,3.2-4.1,7.3-4.1c2,0,4,1.9,4,6c0,4.2-2,6.3-4,6.3c-4,0-4.2-4.1-7.3-4.1c-4.8,0-4.4,5.8-4.4,5.8L2,58 c0,2.2,1.8,4,4,4H19c0,0,6.3,0.4,6.3-4.4c0-3.1-4-3.6-4-7.7c0-2,2.2-4.5,6.4-4.5c4.2,0,6.6,2.5,6.6,4.5c0,4-3.9,4.6-3.9,7.7 c0,4.9,6.3,4.4,6.3,4.4H42z"/>
</svg>
PK
!<U�"�1chrome/browser/content/browser/feeds/subscribe.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

var SubscribeHandler = {
  /**
   * The nsIFeedWriter object that produces the UI
   */
  _feedWriter: null,

  init: function SH_init() {
    this._feedWriter = new BrowserFeedWriter();
  },

  writeContent: function SH_writeContent() {
    this._feedWriter.writeContent();
  },

  uninit: function SH_uninit() {
    this._feedWriter.close();
  }
};

SubscribeHandler.init();

window.onload = function() {
  SubscribeHandler.writeContent();
};

window.onunload = function() {
  SubscribeHandler.uninit();
};
PK
!<x���4chrome/browser/content/browser/feeds/subscribe.xhtml<?xml version="1.0" encoding="utf-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/. -->


<!DOCTYPE html [
  <!ENTITY % htmlDTD
    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "DTD/xhtml1-strict.dtd">
  %htmlDTD;
  <!ENTITY % globalDTD
    SYSTEM "chrome://global/locale/global.dtd">
  %globalDTD;
  <!ENTITY % feedDTD
    SYSTEM "chrome://browser/locale/feeds/subscribe.dtd">
  %feedDTD;
]>

<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>

<html id="feedHandler"
      xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Security-Policy" content="default-src chrome:; img-src *; media-src *" />
    <title>&feedPage.title;</title>
    <link rel="stylesheet"
          href="chrome://browser/skin/feeds/subscribe.css"
          type="text/css"
          media="all"/>
  </head>
  <body>
    <div id="feedHeaderContainer">
      <div id="feedHeader" dir="&locale.dir;">
        <div id="feedIntroText">
          <p id="feedSubscriptionInfo1" />
          <p id="feedSubscriptionInfo2" />
        </div>
        <div id="feedSubscribeLine">
          <label id="subscribeUsingDescription">
            <select id="handlersMenuList">
              <option id="liveBookmarksMenuItem" selected="true">&feedLiveBookmarks;</option>
              <option disabled="true">&#x2501;&#x2501;&#x2501;&#x2501;&#x2501;&#x2501;&#x2501;</option>
            </select>
          </label>
          <label id="checkboxText">
            <input type="checkbox" id="alwaysUse" class="alwaysUse" checked="false"/>
          </label>
          <button id="subscribeButton">&feedSubscribeNow;</button>
        </div>
      </div>
      <div id="feedHeaderContainerSpacer"/>
    </div>
    <div id="feedBody">
      <div id="feedTitle">
        <a id="feedTitleLink">
          <img id="feedTitleImage"/>
        </a>
        <div id="feedTitleContainer">
          <h1 id="feedTitleText"/>
          <h2 id="feedSubtitleText"/>
        </div>
      </div>
      <div id="feedContent"/>
    </div>
  </body>
  <script type="application/javascript" src="chrome://browser/content/feeds/subscribe.js"/>
</html>
PK
!<��c/chrome/browser/content/browser/hiddenWindow.xul<?xml version="1.0"?>

PK
!<���7��8chrome/browser/content/browser/history/history-panel.xul<?xml version="1.0"?> <!-- -*- Mode: xml; indent-tabs-mode: nil; -*- -->


<?xml-stylesheet href="chrome://browser/content/places/places.css"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/places/places.css"?>

<?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?>

<!DOCTYPE page [
<!ENTITY % placesDTD SYSTEM "chrome://browser/locale/places/places.dtd">
%placesDTD;
<!ENTITY % editMenuDTD SYSTEM "chrome://global/locale/editMenuOverlay.dtd">
%editMenuDTD;
]>

<!-- we need to keep id="history-panel" for upgrade and switching
     between versions of the browser -->

<page id="history-panel" orient="vertical"
      xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
      onload="HistorySidebarInit();"
      onunload="SidebarUtils.setMouseoverURL('');">

  <script type="application/javascript"
          src="chrome://browser/content/bookmarks/sidebarUtils.js"/>
  <script type="application/javascript"
          src="chrome://browser/content/places/history-panel.js"/>

<script type="application/javascript" src="chrome://global/content/editMenuOverlay.js"/>
<commandset id="editMenuCommands">
  <commandset id="editMenuCommandSetAll" commandupdater="true" events="focus,select"
              oncommandupdate="goUpdateGlobalEditMenuItems()"/>
  <commandset id="editMenuCommandSetUndo" commandupdater="true" events="undo"
              oncommandupdate="goUpdateUndoEditMenuItems()"/>
  <commandset id="editMenuCommandSetPaste" commandupdater="true" events="clipboard"
              oncommandupdate="goUpdatePasteMenuItems()"/>
  <command id="cmd_undo" oncommand="goDoCommand('cmd_undo')"/>
  <command id="cmd_redo" oncommand="goDoCommand('cmd_redo')"/>
  <command id="cmd_cut" oncommand="goDoCommand('cmd_cut')"/>
  <command id="cmd_copy" oncommand="goDoCommand('cmd_copy')"/>
  <command id="cmd_paste" oncommand="goDoCommand('cmd_paste')"/>
  <command id="cmd_delete" oncommand="goDoCommand('cmd_delete')"/>
  <command id="cmd_selectAll" oncommand="goDoCommand('cmd_selectAll')"/>
  <command id="cmd_switchTextDirection" oncommand="goDoCommand('cmd_switchTextDirection');"/>
</commandset>

  <commandset id="placesCommands"/>

  <!-- These key nodes are here only for show. The real bindings come from
       XBL, in platformHTMLBindings.xml. See bugs 57078 and 71779. -->

  <keyset id="editMenuKeys">
    <key id="key_undo" key="&undoCmd.key;" modifiers="accel" command="cmd_undo"/>
    <key id="key_redo" key="&undoCmd.key;" modifiers="accel,shift" command="cmd_redo"/>
    <key id="key_cut" key="&cutCmd.key;" modifiers="accel" command="cmd_cut"/>
    <key id="key_copy" key="&copyCmd.key;" modifiers="accel" command="cmd_copy"/>
    <key id="key_paste" key="&pasteCmd.key;" modifiers="accel" command="cmd_paste"/>
    <key id="key_delete" keycode="VK_DELETE" command="cmd_delete"/>
    <key id="key_selectAll" key="&selectAllCmd.key;" modifiers="accel" command="cmd_selectAll"/>
    <key id="key_find" key="&findCmd.key;" modifiers="accel" command="cmd_find"/>
    <key id="key_findAgain" key="&findAgainCmd.key;" modifiers="accel" command="cmd_findAgain"/>
    <key id="key_findPrevious" key="&findAgainCmd.key;" modifiers="shift,accel" command="cmd_findPrevious"/>
    <key id="key_findAgain2" keycode="&findAgainCmd.key2;" command="cmd_findAgain"/>
    <key id="key_findPrevious2" keycode="&findAgainCmd.key2;" modifiers="shift" command="cmd_findPrevious"/>
  </keyset>

  <!-- required to overlay the context menu -->
  <menupopup id="placesContext"/>

  <!-- Bookmarks and history tooltip -->
  <tooltip id="bhTooltip"/>

  <hbox id="sidebar-search-container">
    <textbox id="search-box" flex="1" type="search"
             placeholder="&historySearch.placeholder;"
             aria-controls="historyTree"
             oncommand="searchHistory(this.value);"/>
    <button id="viewButton" style="min-width:0px !important;" type="menu"
            label="&view.label;" accesskey="&view.accesskey;" selectedsort="day"
            persist="selectedsort">
      <menupopup>
        <menuitem id="bydayandsite" label="&byDayAndSite.label;"
                  accesskey="&byDayAndSite.accesskey;" type="radio"
                  oncommand="this.parentNode.parentNode.setAttribute('selectedsort', 'dayandsite'); GroupBy('dayandsite');"/>
        <menuitem id="bysite" label="&bySite.label;"
                  accesskey="&bySite.accesskey;" type="radio"
                  oncommand="this.parentNode.parentNode.setAttribute('selectedsort', 'site'); GroupBy('site');"/>
        <menuitem id="byday" label="&byDate.label;"
                  accesskey="&byDate.accesskey;"
                  type="radio"
                  oncommand="this.parentNode.parentNode.setAttribute('selectedsort', 'day'); GroupBy('day');"/>
        <menuitem id="byvisited" label="&byMostVisited.label;"
                  accesskey="&byMostVisited.accesskey;"
                  type="radio"
                  oncommand="this.parentNode.parentNode.setAttribute('selectedsort', 'visited'); GroupBy('visited');"/>
        <menuitem id="bylastvisited" label="&byLastVisited.label;"
                  accesskey="&byLastVisited.accesskey;"
                  type="radio"
                  oncommand="this.parentNode.parentNode.setAttribute('selectedsort', 'lastvisited'); GroupBy('lastvisited');"/>
      </menupopup>
    </button>
  </hbox>

  <tree id="historyTree"
        class="sidebar-placesTree"
        flex="1"
        type="places"
        context="placesContext"
        hidecolumnpicker="true"
        onkeypress="SidebarUtils.handleTreeKeyPress(event);"
        onclick="SidebarUtils.handleTreeClick(this, event, true);"
        onmousemove="SidebarUtils.handleTreeMouseMove(event);"
        onmouseout="SidebarUtils.setMouseoverURL('');">
    <treecols>
      <treecol id="title" flex="1" primary="true" hideheader="true"/>
    </treecols>
    <treechildren class="sidebar-placesTreechildren" flex="1" tooltip="bhTooltip"/>
  </tree>
</page>
PK
!<�K~qzEzEIchrome/browser/content/browser/illustrations/error-connection-failure.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="300" height="300" viewBox="0 0 300 300">
  <defs>
    <linearGradient id="a" x1="-300.021" y1="-272.736" x2="547.138" y2="574.423" gradientUnits="userSpaceOnUse">
      <stop offset="0" stop-color="#ccfbff"/>
      <stop offset="1" stop-color="#c9e4ff"/>
    </linearGradient>
    <linearGradient id="b" x1="-18.672" y1="23.78" x2="279.805" y2="322.256" gradientUnits="userSpaceOnUse">
      <stop offset="0" stop-color="#00c8d7"/>
      <stop offset="1" stop-color="#0a84ff"/>
    </linearGradient>
  </defs>
  <path d="M224.245 144.067h-10.733c.136.343.274.674.41 1h10.323a.5.5 0 0 0 0-1zm2.454-11.821a.5.5 0 0 0-.5-.5h-20.26c.373.357.727.688 1.065 1h19.2a.5.5 0 0 0 .496-.5zm8.546 11.821h-3a.5.5 0 1 0 0 1h3a.5.5 0 0 0 0-1zm5 0h-1a.5.5 0 1 0 0 1h1a.5.5 0 0 0 0-1zm-3.3-6.66h-25.78a12.767 12.767 0 0 1 .862 2h24.918a1 1 0 0 0 0-2zm20.422 6.66h-8.122a.5.5 0 1 0 0 1h8.122a.5.5 0 0 0 0-1z" fill="#eaeaee"/>
  <path d="M269.53 87.757h-24.236c-2.108-3.9-7.559-12.718-14.4-14.023-8.952-1.707-10.737 7.217-10.737 7.217s-5.949-15.468-21-13.419c-16.878 2.3-8.928 20.065-8.928 20.065h-25.408l8.181.159h-8.184a1 1 0 0 0 0 2H269.53a1 1 0 0 0 0-2z" fill="#fff"/>
  <path d="M118.373 63.908h-13.69c-1.129-2.112-4.19-7.156-8.057-7.894-4.978-.949-5.971 4.013-5.971 4.013s-3.309-8.6-11.68-7.462c-9.386 1.278-4.965 11.158-4.965 11.158H59.88l9.471.185h-9.212a1 1 0 0 0 0 2h58.233a1 1 0 1 0 0-2z" fill="#fff"/>
  <ellipse cx="143.566" cy="245.472" rx="55.042" ry="8.362" fill="#eaeaee"/>
  <path d="M102.31 121.507H60.818a1 1 0 0 0 0 2h41.492a1 1 0 1 0 0-2zM70.336 117.6H82.1a.5.5 0 0 0 0-1H70.336a.5.5 0 0 0 0 1z" fill="#eaeaee"/>
  <path d="M111.457 174.8h-78.3a1 1 0 0 0 0 2h78.3a1 1 0 1 0 0-2zm-26.742-3.793h1a.5.5 0 0 0 0-1h-1a.5.5 0 0 0 0 1zm10 0h3.1a.5.5 0 0 0 0-1h-3.1a.5.5 0 0 0 0 1zm-17 0h3a.5.5 0 0 0 0-1h-3a.5.5 0 0 0 0 1zm-20 0h12a.5.5 0 0 0 0-1h-12a.5.5 0 0 0 0 1z" fill="#eaeaee"/>
  <path d="M206.885 62.973l.045-.1c-.058.027-.063.059-.045.1z" fill="#fff"/>
  <path d="M77.937 214.941H39.95a1 1 0 1 1 0-2h37.987a1 1 0 1 1 0 2z" fill="#eaeaee"/>
  <path d="M258.931 214.941h-61.813a1 1 0 0 1 0-2h61.813a1 1 0 0 1 0 2z" fill="#eaeaee"/>
  <path d="M265.745 85.333h-3a.5.5 0 0 1 0-1h3a.5.5 0 0 1 0 1zm-11 0h-8.07a.5.5 0 0 1-.447-.277c-.007-.014-.724-1.425-1.979-3.342a.5.5 0 1 1 .837-.548c.393.6 1.444 2.293 1.888 3.167h7.772a.5.5 0 0 1 0 1zm-66.489-.712h-3a.5.5 0 0 1 0-1h3a.5.5 0 0 1 0 1zm-11 0h-12a.5.5 0 0 1 0-1h12a.5.5 0 0 1 0 1zM190.1 83.13a.5.5 0 0 1-.474-.339c-.1-.29-.2-.615-.31-.971a.5.5 0 1 1 .958-.287c.1.343.2.657.3.937a.5.5 0 0 1-.474.661zm30.5-5.156a.5.5 0 0 1-.467-.32 23.7 23.7 0 0 0-2.182-4.039.5.5 0 0 1 .834-.552 27.291 27.291 0 0 1 1.719 2.982 10.522 10.522 0 0 1 3.679-5.015.5.5 0 0 1 .571.82 10.181 10.181 0 0 0-3.665 5.721.5.5 0 0 1-.448.4zm18.345-2.964a.5.5 0 0 1-.339-.132q-.361-.333-.735-.651a.5.5 0 0 1 .647-.762q.39.331.765.678a.5.5 0 0 1-.339.868zm-49.923-1.725a.47.47 0 0 1-.09-.008.5.5 0 0 1-.4-.581c.792-4.351 3.544-7.229 8.18-8.556a.5.5 0 0 1 .275.962c-4.24 1.212-6.753 3.828-7.472 7.773a.5.5 0 0 1-.499.411zm45.893-1.218a.5.5 0 0 1-.237-.06 12.545 12.545 0 0 0-2.666-1.081.5.5 0 1 1 .261-.966 13.559 13.559 0 0 1 2.88 1.167.5.5 0 0 1-.238.94zM212.146 67.4a.5.5 0 0 1-.28-.086q-.4-.27-.82-.524a.5.5 0 1 1 .516-.856q.444.267.865.552a.5.5 0 0 1-.281.914zm-4.47-2.2a.5.5 0 0 1-.154-.024 16.724 16.724 0 0 0-2.832-.647.5.5 0 0 1 .137-.99 17.6 17.6 0 0 1 3 .686.5.5 0 0 1-.154.976z" fill="#eaeaee"/>
  <path d="M72.315 62.052h-12a.5.5 0 0 1 0-1h12a.5.5 0 0 1 0 1zm45.582-.184h-1.8a.5.5 0 0 1 0-1h1.8a.5.5 0 0 1 0 1zm-10.8 0h-1a.5.5 0 0 1 0-1h1a.5.5 0 0 1 0 1zm-3.491-2.881a.5.5 0 0 1-.39-.186 18.484 18.484 0 0 0-2-2.129.5.5 0 0 1 .668-.744A19.433 19.433 0 0 1 104 58.174a.5.5 0 0 1-.389.814zm-12.521-.631a.5.5 0 0 1-.466-.319 13.8 13.8 0 0 0-3.857-5.165.5.5 0 0 1 .623-.782 14.47 14.47 0 0 1 3.624 4.438A5.414 5.414 0 0 1 94.12 53.5a.5.5 0 1 1 .326.945 4.994 4.994 0 0 0-2.867 3.51.5.5 0 0 1-.49.401zm-17.637-2.037h-.051a.5.5 0 0 1-.447-.548 6.454 6.454 0 0 1 1.057-3.036.5.5 0 1 1 .824.566 5.46 5.46 0 0 0-.886 2.571.5.5 0 0 1-.497.447zm4.193-5.039a.5.5 0 0 1-.151-.977 10.27 10.27 0 0 1 1.017-.264.5.5 0 0 1 .2.979 9.033 9.033 0 0 0-.917.237.5.5 0 0 1-.148.025z" fill="#eaeaee"/>
  <path d="M216.326 144.72c-1.079-4.2-3.73-8.616-7.88-13.117a84.471 84.471 0 0 0-8.921-9.988 5.558 5.558 0 0 0-2.261-1.3c-7.177-6.885-21.972-19.819-32.5-20.546a27.625 27.625 0 0 0-1.889-.063 47.155 47.155 0 0 0-15.147 2.913l-5.114-1.4a5 5 0 0 0-6.142 3.5l-.969 3.545a68.668 68.668 0 0 0-7.844 5.3l-2.052-.048h-.116a5 5 0 0 0-5 4.884l-.042 1.829a45.575 45.575 0 0 0-5.648 7.656 5 5 0 0 0-2.515 5.326c-1.882 5.474-2.848 13.743-2.875 24.633a100.416 100.416 0 0 1-1.646 7.791l-4.279 2.594a6.312 6.312 0 0 0-2.125 8.657l1.516 2.5q-.375.778-.761 1.518l-4.037 1.24a6.312 6.312 0 0 0-4.176 7.877l.6 1.953q-.516.587-1.038 1.143l-1.616-.127a5.926 5.926 0 0 0-.5-.02 6.334 6.334 0 0 0-6.277 5.8l-.115 1.456c-2.741 2.043-5.415 4.547-5.415 9.117 0 6.528 10.272 10.568 13.648 11.9a68.955 68.955 0 0 0 24.224 4.781q.566 0 1.127-.012c.607 2.411 1.208 4.468 1.8 6.144a54.956 54.956 0 0 0 4.745 10.264c1.748 2.839 4.22 6.1 7.845 6.1a5.673 5.673 0 0 0 3.572-1.268c1.746-1.421 2.429-3.072 2.482-10.087a86.865 86.865 0 0 0 16.862 1.533 80.987 80.987 0 0 0 14.53-1.463c-.215 6.82.472 8.55 2.275 10.017a5.672 5.672 0 0 0 3.571 1.268c5.645 0 9.953-8.9 12.573-16.361a52.867 52.867 0 0 0 1.069-3.467c1.432-3.724 4.539-13.353 4.892-27.476.161-6.428 0-12.95-.488-19.419a7.56 7.56 0 0 0 1.131-4.949c6.461-1.2 10.808-3.919 15.1-9.34 3.4-4.308 8-14.308 5.826-22.788z" fill="#fff"/>
  <path d="M211.476 145.947c-1.092-4.252-4.261-8.341-6.9-11.168a78.846 78.846 0 0 0-8.482-9.533.649.649 0 0 0-.907.018c-10.173-10.009-23.029-19.987-30.783-20.521-4.534-.313-10.392.808-16.48 3.1l-6.645-1.817-1.521 5.561a63.823 63.823 0 0 0-10.4 7l-3.884-.09-.087 3.744a40.534 40.534 0 0 0-7.123 9.675l-1.083.338.36 1.153c-2.227 5.146-3.144 14.255-3.147 24.842a104.047 104.047 0 0 1-2.33 10.6l-6 3.638a1.452 1.452 0 0 0-.685 1.835l3.021 4.978a54.736 54.736 0 0 1-2.883 5.777l-5.979 1.836a1.305 1.305 0 0 0-.864 1.63l1.394 4.541a51.394 51.394 0 0 1-4.691 5.189l-3.93-.31a1.3 1.3 0 0 0-1.4 1.2l-.3 3.752c-3.258 2.357-5.218 3.541-5.218 6.422 0 4.144 20.8 13.416 37.9 11.39a88.878 88.878 0 0 0 2.615 9.761c2.852 8.125 6.861 14.032 8.287 12.871.675-.549.711-6.554.562-12.73.446.174 8.025 3.054 21.939 3.054a88.332 88.332 0 0 0 19.876-2.718c-.4 5.844-.626 11.816.085 12.394 1.426 1.161 5.418-4.745 8.269-12.871.357-1.018.7-2.159 1.031-3.377 1.276-3.253 4.307-12.338 4.649-26.035a191.885 191.885 0 0 0-.651-21.1 2.592 2.592 0 0 0-.475-4.743 128.961 128.961 0 0 0-.36-2.871 28.465 28.465 0 0 0 4.578-.19c6.147-.82 9.835-2.869 13.728-7.786 2.797-3.536 6.551-12.06 4.914-18.439z" fill="url(#a)"/>
  <path d="M193.99 124.155c.033.013.067.031.1.045-9.986-9.641-22.2-18.942-29.683-19.457-4.534-.313-10.392.808-16.48 3.1l-6.645-1.817-1.521 5.561a63.823 63.823 0 0 0-10.4 7l-3.884-.09-.087 3.744a40.534 40.534 0 0 0-7.123 9.675l-1.083.338.36 1.153c-2.227 5.146-3.144 14.255-3.147 24.842a109.944 109.944 0 0 1-.921 4.824c.168-.72.327-1.43.475-2.127.061 3.039.177 6.023.332 8.836-1.014 1.2-4.408 5.548-2.546 8.155a10.876 10.876 0 0 0 3.445 2.858c.069.589.138 1.155.209 1.681 1.015 7.544 4.11 29 6.408 38.3.207-.021.417-.036.623-.061a88.878 88.878 0 0 0 2.615 9.761c2.852 8.125 6.861 14.032 8.287 12.871.675-.549.711-6.554.562-12.73.038.015.145.054.283.1V222.3a46 46 0 0 0 27.634 3.451c15.651-3.043 21.467-14.705 22.172-22.607 1.194-13.372-1.415-19.893-1.415-19.893l6.558-2.852c-.011-.135-.022-.284-.034-.417a2.592 2.592 0 0 0-.475-4.743l-11.158 4.316s-.109-1.739-2.5-4.348c-3.174-3.462-10.521-6.355-11.428-6.7a84.091 84.091 0 0 0 24.884 5.065l-.109-.839c-10.706-.609-24.216-5.348-24.216-5.348s23.783 4.316 33.476-.761c6.847-3.587 11.086-13.369 9.456-21.846-1.731-9.023-13.024-20.623-13.024-20.623z" fill="#f9f9fa"/>
  <path d="M149.523 146.54l-5.537-1.957a1 1 0 0 0-1.219 1.406l1.99 3.81a1 1 0 0 0 1.349.423l3.547-1.853a1 1 0 0 0-.13-1.829z" fill="#fff"/>
  <g fill="url(#b)">
    <path d="M268.754 200.7h-.768c-17.152 0-33.29 0-42.937.322-8.333.277-16.774 1.994-16.787 4.736-.008 1.811 4.646 3.3 13.565 5.982 7.387 2.225 17.5 5.271 17.736 7.75a2.9 2.9 0 0 1-1.2 2.046c-2.984 2.772-11.281 5.909-19.089 4.588a25.1 25.1 0 0 1-12.468-6.4c-4.383-3.888-6.191-7.86-7.786-11.364-.619-1.359-1.2-2.643-1.9-3.837a22.145 22.145 0 0 0-5.357-6.126 194.068 194.068 0 0 0-.6-17.308 4.6 4.6 0 0 0-.2-6.523c.75-.036 1.463-.1 2.13-.188 6.749-.9 10.823-3.211 15.032-8.527 3.282-4.146 7.092-13.147 5.287-20.18-1.2-4.662-4.606-9.055-7.29-11.947a80.769 80.769 0 0 0-8.657-9.724 2.62 2.62 0 0 0-1.653-.713 133.306 133.306 0 0 0-15.775-13.264c-6.315-4.414-11.527-6.79-15.492-7.064a24.782 24.782 0 0 0-1.683-.056 45.119 45.119 0 0 0-15.027 3.055l-6.025-1.648a2 2 0 0 0-2.457 1.4l-1.3 4.745a65.633 65.633 0 0 0-9.391 6.332l-3.141-.073h-.047a2 2 0 0 0-2 1.953l-.069 2.965a42.572 42.572 0 0 0-6.522 8.837l-.3.093a2 2 0 0 0-1.312 2.506l.145.465c-1.993 5.12-3.01 13.443-3.023 24.766a102.594 102.594 0 0 1-2.05 9.47L105.03 171a3.309 3.309 0 0 0-1.114 4.538l2.345 3.87a56.333 56.333 0 0 1-2.1 4.209l-5.216 1.6a3.309 3.309 0 0 0-2.189 4.129l1.082 3.524a49.795 49.795 0 0 1-3.217 3.554l-3.023-.238a3.177 3.177 0 0 0-.267-.011 3.318 3.318 0 0 0-3.287 3.043l-.223 2.83c-2.965 2.13-5.3 3.833-5.3 7.5 0 2.65 3.4 5.1 6.95 6.946a89.372 89.372 0 0 0-1.57 3.365c-1.866 4.156-3.1 6.9-7.448 8.232-8.459 2.584-20.437.244-26.315-3.564-1.825-1.182-2.9-2.432-3.041-3.519-.264-2.111 5.188-4.187 9.167-5.7 3.919-1.492 6.152-2.394 6.47-3.56a1.66 1.66 0 0 0-.295-1.493C64.48 207.69 53.977 207.22 34 207.22a.5.5 0 0 0 0 1c12.5 0 29.627 0 31.645 2.642a.661.661 0 0 1 .125.623c-.2.732-3.339 1.928-5.861 2.888-5 1.9-10.165 3.871-9.8 6.761.176 1.405 1.382 2.869 3.489 4.234 4.275 2.77 11.581 4.841 18.68 4.841a29.2 29.2 0 0 0 8.471-1.16c4.784-1.462 6.161-4.531 8.068-8.778a89.007 89.007 0 0 1 1.552-3.326 54.676 54.676 0 0 0 3.9 1.709 65.842 65.842 0 0 0 23.126 4.572c1.177 0 2.336-.042 3.462-.126a80.823 80.823 0 0 0 2.292 8.265c.516 1.469 5.171 14.354 9.759 14.354a2.629 2.629 0 0 0 1.678-.595c.831-.677 1.5-1.223 1.353-11.536a76.69 76.69 0 0 0 19.885 2.308 86.392 86.392 0 0 0 17.71-2.167c-.585 10.114.149 10.711.989 11.395a2.628 2.628 0 0 0 1.677.595c4.572 0 9.226-12.885 9.742-14.354.347-.989.7-2.137 1.046-3.412 1.345-3.458 4.4-12.766 4.747-26.612.014-.552.01-1.108.019-1.662a20.857 20.857 0 0 1 4.507 5.34c.668 1.152 1.242 2.415 1.851 3.751 1.636 3.595 3.491 7.669 8.032 11.7a26.054 26.054 0 0 0 12.965 6.641 24.08 24.08 0 0 0 4.006.325c6.5 0 12.949-2.4 15.93-5.166a3.735 3.735 0 0 0 1.514-2.872c-.274-2.93-7.481-5.313-18.444-8.614-5.427-1.634-12.859-3.872-12.854-5.02.006-1.314 5.643-3.4 15.82-3.742 9.631-.321 25.785-.326 42.9-.321h.768a.5.5 0 0 0 0-1zM204.579 135c2.636 2.827 5.805 6.916 6.9 11.168 1.637 6.379-2.117 14.9-4.918 18.441-3.893 4.918-7.581 6.967-13.728 7.786-.7.093-1.432.142-2.179.172a.988.988 0 0 0-.354-.077 73.652 73.652 0 0 1-26.1-5.54 1 1 0 0 0-.826 1.821 74.46 74.46 0 0 0 25.115 5.664c.039.323.08.659.121 1.021a2.579 2.579 0 0 1 1.39 3.874.484.484 0 0 0-.243.11c-1.838 1.634-8.407 3.132-8.474 3.146a.5.5 0 0 0-.113.041l-2.2 1.1a.5.5 0 0 0-.274.5c.006.061.561 6.134-2.275 8.973a5.1 5.1 0 0 1-3.97 1.438l.82-4.449a.5.5 0 0 0-.383-.579l-2.605-.579c-.139-1-.37-4.182 1.943-5.917.976-.731 8.157-3.579 10.842-4.614a.5.5 0 0 0-.359-.933c-1 .386-9.818 3.8-11.082 4.747-3.262 2.447-2.312 7.043-2.27 7.237a.492.492 0 0 0 .107.2q-1.276-.217-2.6-.406c-.048-1.62-.626-3.782-2.151-4.88a3.922 3.922 0 0 0-3.715-.379.507.507 0 0 0-.086.039 5.413 5.413 0 0 0-2.65 4.454c0 2.46 1.4 5.3 3.473 5.614a5.187 5.187 0 0 0 .772.059 4.147 4.147 0 0 0 2.763-1 4.691 4.691 0 0 0 1.526-2.9q2.748.39 5.267.9l-.694 3.765a.5.5 0 0 0 .444.588q.446.043.87.043a6.04 6.04 0 0 0 4.449-1.735 6.78 6.78 0 0 0 .928-1.172 40.237 40.237 0 0 1 11.732 5.419q-.012 1.546-.051 3.143c-.342 13.7-3.374 22.782-4.649 26.035a48.73 48.73 0 0 1-1.031 3.377c-2.611 7.438-6.177 13.017-7.855 13.017a.639.639 0 0 1-.415-.146c-.669-.545-.51-5.868-.153-11.366a19.327 19.327 0 0 0 5.9-3.617.5.5 0 0 0-.717-.7 18.885 18.885 0 0 1-5.594 3.4 87.371 87.371 0 0 1-19.4 2.607c-11.281 0-18.4-1.892-20.923-2.7l-.26-6.109a.5.5 0 1 0-1 .043l.243 5.714c.149 6.176.113 12.181-.562 12.73a.639.639 0 0 1-.415.146c-1.681 0-5.261-5.579-7.872-13.017a88.882 88.882 0 0 1-2.615-9.761h-.005c-2.551-10.248-4.636-21.971-4.657-22.089a.5.5 0 1 0-.984.175c.021.118 2.091 11.756 4.633 22-1.324.122-2.662.2-4.013.2a63.607 63.607 0 0 1-26.1-6.059 44.608 44.608 0 0 1 6.949-9.4c9.072-9.555 18.343-13.569 25.331-15.21a6.619 6.619 0 0 0 2.526 3.172 5.232 5.232 0 0 0 2.774.9 4.77 4.77 0 0 0 3.579-1.983.5.5 0 0 0 .052-.57l-1.467-2.574q2.151-.118 4.317-.032c1.077 3.689 5.807 4.056 5.858 4.06h.033a.5.5 0 0 0 .464-.314 8.392 8.392 0 0 0 .432-1.968h2.691a.5.5 0 1 0 0-1h-2.606a18.073 18.073 0 0 0-.152-3.239h2.437a.5.5 0 0 0 0-1h-2.593a24.74 24.74 0 0 0-.512-2.243.5.5 0 0 0-.564-.359 11.053 11.053 0 0 0-3 1.03c-1.867.989-2.77 2.376-2.658 4.025a46.151 46.151 0 0 0-4.689.068l-.048-.084 2.933-.3a.5.5 0 0 0 .449-.484c.005-.2.1-4.931-2.952-6.689-1.677-.968-11.4-4.872-11.818-5.037a.5.5 0 0 0-.372.928c.1.041 10.074 4.043 11.69 4.976 2.05 1.183 2.388 4.223 2.442 5.359l-3.237.333a.5.5 0 0 0-.383.745l2.271 3.988c-1.076 1.186-2.506 2.067-4.831.615-3.425-2.138-3.069-8.563-3.019-9.262 0-.022.005-.044 0-.066v-.007a.5.5 0 0 0-.052-.266.985.985 0 0 0-.548-.63c-.037-.016-3.859-1.7-7.521-3.205-3.062-1.261-3.5-3.464-3.524-3.584a1 1 0 0 0-1.975.313c.021.138.568 3.4 4.738 5.121 3.645 1.5 7.452 3.176 7.488 3.191.056.024.2.091.35.158a17.069 17.069 0 0 0 .664 4.966c-7.122 1.693-16.53 5.791-25.719 15.47a45.518 45.518 0 0 0-7.118 9.64c-3.625-1.887-5.88-3.8-5.88-5.166 0-2.882 1.96-4.065 5.218-6.422l.3-3.752a1.306 1.306 0 0 1 1.293-1.2h.11l2.534.2a15.544 15.544 0 0 1-1.922 1.463.5.5 0 0 0 .545.839 15.693 15.693 0 0 0 2.715-2.2h.058a51.394 51.394 0 0 0 4.691-5.189l-1.394-4.541a1.305 1.305 0 0 1 .864-1.63l4.352-1.336-1.57 2.34a.5.5 0 1 0 .83.558l2.256-3.363.111-.034a59.576 59.576 0 0 0 3-6.032l-2.884-4.76a1.305 1.305 0 0 1 .439-1.792l4.593-2.784-.9 2.5a.5.5 0 0 0 .941.338l1.316-3.665.051-.031c.038-.137.069-.27.106-.406l.078-.218a.5.5 0 0 0 .027-.185 105.18 105.18 0 0 0 2.119-9.789c0-9.717.779-18.179 2.627-23.484l.683 1.709a.5.5 0 0 0 .929-.371l-1.144-2.862-.308-.986 1.083-.338a39.766 39.766 0 0 1 6.223-8.725 12.852 12.852 0 0 0 .241 1.72.5.5 0 0 0 .629.315.5.5 0 0 0 .32-.631 24.71 24.71 0 0 1-.287-2.484l.084-3.613 3.884.09a63.833 63.833 0 0 1 10.4-7l1.521-5.561 5.138 1.405-2.527 1.118a.5.5 0 1 0 .409.912l3.7-1.659a44.04 44.04 0 0 1 14.851-3.124q.8 0 1.545.052c7.754.535 20.609 10.512 30.783 20.521a.655.655 0 0 1 .465-.193.639.639 0 0 1 .442.176m0 0a78.848 78.848 0 0 1 8.484 9.535m-14.79 61.935a42.035 42.035 0 0 0-11.248-5.1 15.357 15.357 0 0 0 1.207-6.3.955.955 0 0 0 .1.016.989.989 0 0 0 .292-.044l9.149-2.784c.288 4.005.511 8.839.5 14.208z"/>
    <path d="M193.225 139.669a1.412 1.412 0 0 0 1.41-1.41V135.1a1.41 1.41 0 0 0-2.821 0v3.162a1.412 1.412 0 0 0 1.411 1.407z"/>
    <path d="M193.225 144.475a5.736 5.736 0 0 0 4.644-2.413 1 1 0 1 0-1.679-1.088 3.76 3.76 0 0 1-2.965 1.5 3.71 3.71 0 0 1-2.969-1.506 1 1 0 0 0-1.675 1.094 5.736 5.736 0 0 0 4.644 2.413z"/>
    <path d="M163.852 142.719a1 1 0 0 0 1.674-1.094 5.674 5.674 0 0 0-9.286 0 1 1 0 0 0 1.674 1.095 3.679 3.679 0 0 1 5.938 0z"/>
    <path d="M159.473 135.1v3.162c0 .036.008.07.011.106a6.569 6.569 0 0 1 2.8 0c0-.036.011-.07.011-.106V135.1a1.41 1.41 0 1 0-2.821 0z"/>
    <path d="M160.912 130.086a5.733 5.733 0 0 0 4.643-2.412 1 1 0 0 0-1.674-1.095 3.679 3.679 0 0 1-5.937 0 1 1 0 0 0-1.674 1.094 5.733 5.733 0 0 0 4.642 2.413z"/>
    <path d="M155.934 155.646a18.515 18.515 0 0 0-4.51-8.323c-5.144-5.145-12.507-4.867-12.817-4.857a1 1 0 0 0 .089 2 16.68 16.68 0 0 1 3.159.279 18.431 18.431 0 0 0 1.1 3.47 6.118 6.118 0 0 0-1.562.513 7.174 7.174 0 0 0-2.41 2.27.5.5 0 1 0 .84.542 6.254 6.254 0 0 1 2.014-1.917 5.319 5.319 0 0 1 1.6-.478 4.113 4.113 0 0 0 .928 1.091 6.669 6.669 0 0 0-.955 2.347 7.171 7.171 0 0 0 .372 3.29.5.5 0 0 0 .469.326.494.494 0 0 0 .174-.031.5.5 0 0 0 .295-.643 6.214 6.214 0 0 1-.326-2.76 5.734 5.734 0 0 1 .894-2.1 2.673 2.673 0 0 0 .474.054 9.686 9.686 0 0 0 4.458-1.746 16.947 16.947 0 0 1 3.75 7.1 1 1 0 0 0 1.956-.42zm-10.821-6.082c-1.027-.642-1.8-3.029-2.178-4.592a15.045 15.045 0 0 1 6.547 3.285c-1.517.943-3.507 1.843-4.37 1.307z"/>
    <path d="M193.839 116.06a1 1 0 0 0 .71-.3l2.533-2.554a1 1 0 1 0-1.42-1.408l-2.533 2.554a1 1 0 0 0 .71 1.7z"/>
    <path d="M199.708 117.691a1 1 0 0 0 .409-.088l3.512-1.576a1 1 0 0 0-.818-1.825l-3.512 1.576a1 1 0 0 0 .41 1.913z"/>
    <path d="M193.882 183.558a.5.5 0 0 0 .106.988.487.487 0 0 0 .105-.011 5.176 5.176 0 0 0 3-7.9.5.5 0 0 0-.807.591 4.178 4.178 0 0 1-2.41 6.327z"/>
    <path d="M195.435 187.387a.487.487 0 0 0 .105-.011 7.781 7.781 0 0 0 4.512-11.856.5.5 0 1 0-.807.591 6.884 6.884 0 0 1 .676 6.146 6.8 6.8 0 0 1-4.593 4.142.5.5 0 0 0 .105.989z"/>
    <path d="M127.087 198.745a5.134 5.134 0 0 0 1.54.239 5.254 5.254 0 0 0 3.143-1.063.5.5 0 0 0-.637-.771 4.178 4.178 0 0 1-6.463-2.021.5.5 0 0 0-.962.271 5.206 5.206 0 0 0 3.379 3.345z"/>
    <path d="M132.43 200.03a6.783 6.783 0 0 1-10.508-3.284.5.5 0 0 0-.963.27 7.773 7.773 0 0 0 12.108 3.785.5.5 0 0 0-.638-.771z"/>
  </g>
  <g fill="#f9f9fa">
    <path d="M160.366 188.859a.5.5 0 0 1-.43-.754l1.345-2.283a.5.5 0 1 1 .861.508l-1.345 2.283a.5.5 0 0 1-.431.246z"/>
    <path d="M161.086 192.527a.5.5 0 0 1-.43-.754l1.344-2.282a.5.5 0 1 1 .861.508l-1.345 2.282a.5.5 0 0 1-.43.246z"/>
  </g>
</svg>
PK
!<��m�VEVEDchrome/browser/content/browser/illustrations/error-malformed-url.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="300" height="300" viewBox="0 0 300 300">
  <defs>
    <linearGradient id="a" x1="-300.021" y1="-272.736" x2="547.138" y2="574.423" gradientUnits="userSpaceOnUse">
      <stop offset="0" stop-color="#ccfbff"/>
      <stop offset="1" stop-color="#c9e4ff"/>
    </linearGradient>
    <linearGradient id="b" x1="-18.672" y1="23.78" x2="279.805" y2="322.256" gradientUnits="userSpaceOnUse">
      <stop offset="0" stop-color="#00c8d7"/>
      <stop offset="1" stop-color="#0a84ff"/>
    </linearGradient>
  </defs>
  <path d="M73.275 165.84l63.117-26.74a1 1 0 0 0-.78-1.842L72.494 164a1 1 0 0 0 .78 1.842z" fill="#eaeaee"/>
  <path d="M110.05 144.74l15.834-6.708a.5.5 0 1 0-.39-.921l-15.834 6.708a.5.5 0 0 0 .39.921z" fill="#eaeaee"/>
  <path d="M59.35 176.038a.5.5 0 0 1 .265-.655l2.762-1.17a.5.5 0 1 1 .39.921l-2.762 1.17a.5.5 0 0 1-.655-.266zm10.128-4.291a.5.5 0 0 1 .265-.655l11.049-4.681a.5.5 0 1 1 .39.921l-11.049 4.681a.5.5 0 0 1-.654-.266zm19.336-8.192a.5.5 0 0 1 .265-.655l.921-.39a.5.5 0 1 1 .39.921l-.921.39a.5.5 0 0 1-.654-.266zm4.6-1.951a.5.5 0 0 1 .265-.655l2.762-1.17a.5.5 0 1 1 .39.921l-2.762 1.17a.5.5 0 0 1-.65-.27zm10.128-4.291a.5.5 0 0 1 .265-.655l11.049-4.681a.5.5 0 0 1 .39.921l-11.046 4.68a.5.5 0 0 1-.653-.265z" fill="#eaeaee"/>
  <path d="M103.245 194.26L208.809 161a1 1 0 0 0-.6-1.908l-105.565 33.26a1 1 0 1 0 .6 1.908z" fill="#eaeaee"/>
  <path d="M116 185l26.482-8.343a.5.5 0 1 0-.3-.954l-26.482 8.342a.5.5 0 0 0 .3.954z" fill="#eaeaee"/>
  <path d="M84.918 204.09a.5.5 0 0 1 .327-.627l11.445-3.606a.5.5 0 0 1 .3.954l-11.445 3.606a.5.5 0 0 1-.627-.327zm19.076-6.01a.5.5 0 0 1 .327-.627l2.861-.9a.5.5 0 1 1 .3.954l-2.861.9a.5.5 0 0 1-.627-.326zm6.677-2.1a.5.5 0 0 1 .327-.627l.954-.3a.5.5 0 1 1 .3.954l-.954.3a.5.5 0 0 1-.627-.33zm9.538-3a.5.5 0 0 1 .327-.627l11.445-3.606a.5.5 0 1 1 .3.954l-11.445 3.599a.5.5 0 0 1-.628-.327zm19.076-6.01a.5.5 0 0 1 .327-.627l2.861-.9a.5.5 0 1 1 .3.954l-2.861.9a.5.5 0 0 1-.628-.334zm6.677-2.1a.5.5 0 0 1 .327-.627l.954-.3a.5.5 0 1 1 .3.954l-.954.3a.5.5 0 0 1-.628-.337zm9.538-3a.5.5 0 0 1 .327-.627l11.445-3.606a.5.5 0 1 1 .3.954l-11.445 3.606a.5.5 0 0 1-.627-.342zm19.076-6.01a.5.5 0 0 1 .327-.627l2.861-.9a.5.5 0 0 1 .3.954l-2.861.9a.5.5 0 0 1-.629-.342zm6.677-2.1a.5.5 0 0 1 .327-.627l.954-.3a.5.5 0 1 1 .3.954l-.954.3a.5.5 0 0 1-.629-.345zm9.538-3a.5.5 0 0 1 .327-.627l11.445-3.606a.5.5 0 0 1 .3.954l-11.445 3.606a.5.5 0 0 1-.629-.35zm19.076-6.01a.5.5 0 0 1 .327-.627l2.861-.9a.5.5 0 0 1 .3.954l-2.861.9a.5.5 0 0 1-.629-.349zm6.677-2.1a.5.5 0 0 1 .327-.627l.954-.3a.5.5 0 0 1 .3.954l-.954.3a.5.5 0 0 1-.63-.352zm9.538-3a.5.5 0 0 1 .327-.627l11.445-3.606a.5.5 0 1 1 .3.954l-11.445 3.606a.5.5 0 0 1-.63-.357z" fill="#eaeaee"/>
  <path d="M50.883 109.437L73.366 97.57s-15.334-12.008-1.472-21.924C84.256 66.8 96.745 77.71 96.745 77.71s-2.589-8.73 6.13-11.4c8.6-2.633 20.994 6.885 20.994 6.885l19.577-10.334" fill="#fff"/>
  <path d="M141.872 58.947L136.085 62a.588.588 0 0 1-.549-1.04l5.788-3.055a.588.588 0 1 1 .549 1.04zm-15.152 8l-1.04.549a.588.588 0 0 1-.549-1.04l1.04-.549a.588.588 0 0 1 .549 1.04zM72.4 94.811l-1.674.884a.588.588 0 0 1-.549-1.04l.9-.478a24.222 24.222 0 0 1-.591-.543.588.588 0 1 1 .81-.853c.7.664 1.177 1.039 1.188 1.048a.588.588 0 0 1-.088.983zm-10 5.277l-12.485 6.59a.588.588 0 1 1-.549-1.04l12.485-6.59a.588.588 0 1 1 .549 1.04zm59.158-31.3a.588.588 0 0 1-.6-.026 45.206 45.206 0 0 0-3-1.776.588.588 0 1 1 .559-1.036 46.056 46.056 0 0 1 3.086 1.824.588.588 0 0 1-.045 1.014zM68.064 90.2a.588.588 0 0 1-.751-.176q-.348-.483-.681-1a.588.588 0 0 1 .993-.631c.208.328.423.643.642.946a.588.588 0 0 1-.133.821.666.666 0 0 1-.07.04zm25.892-16.6a.587.587 0 0 1-.6-.031c-.293-.2-.617-.4-.972-.62a.588.588 0 0 1 .611-1.006c.37.225.71.444 1.016.649a.588.588 0 0 1-.052 1.008zm15.815-10.047a.586.586 0 0 1-.405.053 14.722 14.722 0 0 0-7.557-.008 8.578 8.578 0 0 0-4.693 3.166.588.588 0 0 1-.966-.673 9.747 9.747 0 0 1 5.314-3.618 15.855 15.855 0 0 1 8.163-.013.588.588 0 0 1 .144 1.093zm-21.029 7.324a.587.587 0 0 1-.486.029 21.942 21.942 0 0 0-3.291-.988.588.588 0 0 1 .246-1.151 23.255 23.255 0 0 1 3.468 1.04.588.588 0 0 1 .063 1.07zm-23.055 8.291a.589.589 0 0 1-.827-.722 14.421 14.421 0 0 1 5.452-6.558 19.345 19.345 0 0 1 5.307-2.711.588.588 0 1 1 .361 1.123A18.263 18.263 0 0 0 71 72.844a13.284 13.284 0 0 0-5.031 6.006.59.59 0 0 1-.282.318z" fill="#eaeaee"/>
  <path d="M144.627 62.6l-92.656 48.906a1.176 1.176 0 0 1-1.1-2.081l92.656-48.907a1.176 1.176 0 1 1 1.1 2.081z" fill="#fff"/>
  <path d="M185.779 90.824l13.771 3.409s-1.925-10.7 7.531-9.678c8.434.909 9.583 10.091 9.583 10.091s2.165-4.6 6.788-2.471c4.558 2.1 6.186 10.666 6.186 10.666l11.991 2.968" fill="#fff"/>
  <path d="M200.337 92.789l-13.752-3.4a.59.59 0 0 1 .284-1.146l13.752 3.4a.59.59 0 0 1-.284 1.146zm42.412 10.311l-.529-.131a.59.59 0 1 1 .284-1.146l.529.131a.59.59 0 0 1-.284 1.146zm-5.113-1.266l-3.438-.851a.59.59 0 1 1 .284-1.146l3.438.851a.59.59 0 0 1-.284 1.146zm-20.166-8.627a.614.614 0 0 1-.445-.506l-.035-.222a.589.589 0 0 1 .4-.662 5.483 5.483 0 0 1 6-2.637 7.111 7.111 0 0 1 1.259.447 10.622 10.622 0 0 1 4.381 4.221.591.591 0 0 1-1 .626 9.484 9.484 0 0 0-3.872-3.774 5.935 5.935 0 0 0-1.05-.374c-3.3-.818-4.891 2.422-4.957 2.562a.592.592 0 0 1-.681.319zm-14.675-9.061a.591.591 0 0 1-.2-1.053 6.8 6.8 0 0 1 3.628-1.19.591.591 0 0 1 .07 1.179 5.632 5.632 0 0 0-3.009.97.589.589 0 0 1-.489.094zm9 .181a.6.6 0 0 1-.146-.057 9.055 9.055 0 0 0-1-.478.59.59 0 0 1-.324-.769.6.6 0 0 1 .769-.325 10.36 10.36 0 0 1 1.128.54.59.59 0 0 1-.428 1.089z" fill="#eaeaee"/>
  <path d="M242.217 107.236l-56.755-14.049a1.181 1.181 0 1 1 .567-2.292l56.755 14.049a1.181 1.181 0 0 1-.567 2.292z" fill="#fff"/>
  <ellipse cx="155.535" cy="245.952" rx="7.463" ry="43.55" transform="rotate(-86.464 155.535 245.951)" fill="#eaeaee"/>
  <path d="M112.695 226.207l-64.217-18.684a1 1 0 1 0-.559 1.92l64.217 18.684a1 1 0 1 0 .559-1.92z" fill="#eaeaee"/>
  <path d="M243.246 226.609L178.5 209.394a1 1 0 0 0-.514 1.933l64.751 17.215a1 1 0 1 0 .514-1.933z" fill="#eaeaee"/>
  <path d="M146.467 240.275a.789.789 0 0 0 .166-.037.82.82 0 0 1-.205.045z" fill="#fff"/>
  <g fill="#fff">
    <path d="M179.037 101.776l-.121-.547a12.937 12.937 0 0 0-.4-1.386 12.75 12.75 0 0 0-22.367-3.36l-4.583-.2a5 5 0 0 0-4.02-2.981 5 5 0 0 0-1.409-1.736l-.987 5.022-.259 1.327 1.261-.689 2.337-1.286.866 3.905 8.492.371.509-.8a9.751 9.751 0 0 1 16.444 10.478l-1.117 1.753a166 166 0 0 1 5.9 23.121c.32-.012 11.494 70.842 11.421 75.734a61.9 61.9 0 0 1-.1 10.1c-.433 3.64-1.661 13.195-5.4 14.023a2.76 2.76 0 0 1-1.855-.234c-.957-.484-1.864-1.833-3.817-11.386a123 123 0 0 1-15.17 4.294 149.57 149.57 0 0 1-17.448 2.843c2.283 9.563 1.761 10.211 1.121 11.019a2.76 2.76 0 0 1-1.582 1c-3.739.829-8.634-6.281-10.574-9.446a46.87 46.87 0 0 1-2.769-5.378c-2.744-3.417-5.519-2.72-2.775.881a47.78 47.78 0 0 0 2.805 5.4 38.26 38.26 0 0 0 6.116 7.863c2.261 2.07 4.879 3.528 7.652 2.913a5.81 5.81 0 0 0 3.292-2.061c.926-1.229 1.334-2.436 1.126-4.9a43.46 43.46 0 0 0-.916-5.379c4.453-.567 9.452-1.439 14.4-2.536 3.437-.762 7.923-1.869 12.292-3.277A61.92 61.92 0 0 0 179 232.33c.863 2.369 1.735 4.03 3.085 4.714a5.017 5.017 0 0 0 3.538.655c2.812-.623 4.751-1.742 6.522-6.337a46.05 46.05 0 0 0 1.841-10.27 75.3 75.3 0 0 0-.13-11.375c.029-4.493-.48-7.184-.945-13.441-1.184-16.126-9.59-59.447-11.018-64.876a168.439 168.439 0 0 0-5.141-20.053l.342-.537a12.8 12.8 0 0 0 1.475-3.236c.1-.361.189-.718.262-1.082a12.68 12.68 0 0 0 .206-4.716z"/>
    <path d="M134.237 228.656l-6.947-14.593c-1.686-6.992-20.874-79.082-21.813-86.213l-3.447-2.2a9.76 9.76 0 0 1 10.489-16.449l2.087 1.33c4.834-2.638 12.315-6.642 15.256-7.744 3.252-1.223 9.874-6.285 12.141-8.119L146 91.43c-.11-.078-.209-.158-.317-.227a5 5 0 0 0-5.729.4c-3.239 2.623-8.884 6.732-11.307 7.627-2.713 1.021-8.725 4.167-14.083 7.075l-.6-.38a12.76 12.76 0 1 0-13.679 21.468l2.254 1.436c.592 4.611 14.794 90.939 28.1 99.384z"/>
    <path d="M186.722 176.206s10.389-4.558 15.3-2.655c3.46 1.339 7.075 4.468 9.434 12.129s2.346 13.664-1.921 16.234-9.314 1.9-10.178-.979c-.621-2.067-2.547-9.158-2.547-9.158s-1.255-.941-6.281 1.683z"/>
    <path d="M116.553 182.33s-16.275-1.34-19.879 3.447c-2.232 2.964-4.4 7.12-3.154 15.039s3.888 13.307 8.85 13.744 9.2-2.381 8.716-5.346c-.349-2.129-1.73-9.347-1.73-9.347s.135 1.219 11.44 2.024z"/>
  </g>
  <path d="M205.432 180.5c-3.039-4.414-6.895-5.465-18.936.241-3.035-15.577-8.491-43.6-8.527-43.761a260.987 260.987 0 0 0-.7-3.767l-.039-.176-.958-4.325-.113-.508-.361-1.63-.307-1.386-.314-1.416q-.216-.976-.5-1.937l-.206-.927q-.369-1.342-.756-2.67a5 5 0 0 0-.285-.961q-.305-1.008-.618-2c-.065-.293-.179-.575-.275-.871-.3-.957-.6-1.833-.924-2.735l1.623-2.8a7.778 7.778 0 0 0-13.12-8.359l-.448.7a12.79 12.79 0 0 1 7.84 4.408.5.5 0 0 1-.755.659 11.81 11.81 0 0 0-7.682-4.115l-.084.131-11.187-.495-.052-.234-.059.013-.576-2.6-4.955 2.737.067-.343-.192.1 1.014-5.131c-1.3 1.047-8.6 6.812-12.5 8.282-3.064 1.15-11.506 5.715-16.035 8.2-4.027 2.582-4.287 5.938-4.3 6.074a.5.5 0 0 1-.391.455l-.137.03a.5.5 0 0 1-.474-.52c-.035-.156.269-3.778 4.383-6.615l-2.111-1.345a7.778 7.778 0 0 0-8.359 13.12l3.817 2.432.463.287c.312 3.3 1.05 7.88 2.09 13.129a244.916 244.916 0 0 0 2.3 10.359l.182.82s.136 11.739 6.835 36.611c-13.88-.9-19.023.179-20.366 4.046-1.625 4.678.566 13.9 1.116 15.346a4 4 0 0 0 4.6 2.486 4.046 4.046 0 0 0 .557-.167c2.064-.782 1.925-2.583 1.145-4.648-1.047-2.768-.811-7.074-.755-9.09 2.342-1.09 8.613-.388 16.016.208a437.29 437.29 0 0 0 5.028 15.915c3.14 9.547 6.214 11.174 9.089 14.624a46.19 46.19 0 0 0 2.75 5.383c2.74 4.412 5.31 7.181 7.025 8.174l.357.177.091.041.277.092.13.033.2.037.156-.035.137-.03a.821.821 0 0 0 .205-.045l.059-.013a.781.781 0 0 0 .214-.191c.465-.584-.541-5.4-1.791-10.532a17.821 17.821 0 0 1-5.563-1.625.5.5 0 1 1 .446-.9 17.649 17.649 0 0 0 4.866 1.482c2.018-.14 4.231-.364 6.637-.693l.185-.041 2.114-.3.989-.158 1.423-.234 1.45-.25.976-.216 1.836-.356.586-.13 2.119-.47.312-.069a115.067 115.067 0 0 0 17.231-4.884c2.187-1.13 5.312-3.021 5.347-3.049a.5.5 0 0 1 .518.858c-.136.081-2.713 1.625-4.879 2.8 1.044 5.218 2.365 11.073 3.045 11.414a.77.77 0 0 0 .476.069l.049-.011.215-.048.088-.019.205-.138.087-.07.2-.2.09-.1.216-.273.078-.109.228-.358.046-.072a15.559 15.559 0 0 0 1.319-3.293l-.019-.088.221-.807.033-.13.215-.877-.022-.1c.072-.323.143-.646.211-.989.213-1.071 1.234-3.087.359-4.558a61.5 61.5 0 0 0 .075-9.829l-.015-.068-.033-.658c.569-3.666-.534-19.041-.534-19.041l-.4-2.048c5.392-2.54 8.767-3.317 11.088-3.221.826 1.839 2.666 5.179 2.763 8.137.074 2.206.67 4.468 2.876 4.4a4 4 0 0 0 3.869-4.122c-.047-1.53-.432-9.45-3.24-13.53z" fill="url(#a)"/>
  <g fill="#f9f9fa">
    <path d="M120.69 115.886c-2.232 1.282-7.4 4.415-7.3 9.164s2.239 19.146 3.2 20.184 16.5-.365 17.218-1.631-1.208-31.981-1.974-32.714-8.428 3.437-11.144 4.997z"/>
    <path d="M173.771 187.946c-.254-1.838 4.982-9.811 7.446-13.441-2.179-10.881-6.769-31.937-7.541-34.944-1.738-10.523-4.47-21.185-8.722-28.908-2.181-3.961-12.142-6.956-23.757-5.16l-4.669 36.8s-.428 3.26-2.172 3.549c-1.431.237-13.05 3.558-17.183 4.745q.248.932.505 1.867c.248 1.63 4.281 18.045 7.083 28.711 4.165 2.817 11.809 8.193 12.283 10.016.429 1.652-2.666 8.234-4.839 12.552 2.917 7.157 5.781 12.57 7.815 14.158 12.135 9.6 40.827 2.219 43.492-11.964a69.862 69.862 0 0 0 .03-7.868c-3.884-3.376-9.553-8.541-9.771-10.113z"/>
  </g>
  <g fill="url(#b)">
    <path d="M124.228 131.126a1.4 1.4 0 0 0 1.064-1.67l-.679-3.066a1.4 1.4 0 1 0-2.734.606l.679 3.066a1.4 1.4 0 0 0 1.67 1.064z"/>
    <path d="M165.962 143.534a1.4 1.4 0 0 0 1.064-1.67l-.679-3.066a1.4 1.4 0 1 0-2.734.606l.679 3.066a1.4 1.4 0 0 0 1.67 1.064z"/>
    <path d="M125.543 137.062a5.7 5.7 0 0 0 3.981-3.341 1 1 0 1 0-1.876-.7 3.72 3.72 0 0 1-2.548 2.091 3.67 3.67 0 0 1-3.193-.819 1 1 0 0 0-1.4 1.428 5.7 5.7 0 0 0 5.036 1.341z"/>
    <path d="M167.278 149.47a5.7 5.7 0 0 0 3.991-3.343 1 1 0 0 0-1.866-.7 3.64 3.64 0 0 1-5.741 1.272 1 1 0 0 0-1.395 1.426 5.7 5.7 0 0 0 5.011 1.345z"/>
    <path d="M156.452 156.752a6.476 6.476 0 0 1-.522.387 6.69 6.69 0 0 1 .8 1.377 4.69 4.69 0 0 1-.525 4.333 1 1 0 0 0 1.592 1.21 6.72 6.72 0 0 0 .808-6.321 7.62 7.62 0 0 0-.365-.762 4.47 4.47 0 0 0 1.118.081 5 5 0 0 0 3.889-2.108 1 1 0 0 0-1.693-1.061 3.06 3.06 0 0 1-2.335 1.181 4.41 4.41 0 0 1-1.548-.31 6.4 6.4 0 0 1-.921 1.814 1.579 1.579 0 0 1-.298.179z"/>
    <path d="M155.906 157.107a6.476 6.476 0 0 0 .522-.387 1.58 1.58 0 0 0 .249-.184 6.4 6.4 0 0 0 .921-1.814 14.34 14.34 0 0 0 .973-5.9 2.88 2.88 0 0 0-1.181-2.149 1.75 1.75 0 0 0-1.792-.017c-2.144 1-6.864 6.325-7.348 10.764a1.52 1.52 0 0 0 .906 1.594c1.376.615 5.074-.928 6.75-1.907z"/>
    <path d="M205.286 200.2a5.653 5.653 0 0 0 4.378-4.134c.93-3.7-1.4-15.212-7.182-18.87-5.91-3.741-18 3.543-19.94 4.564-.488.258-.789.725-.531 1.213a1 1 0 0 0 1.35.418c14.7-7.753 16.768-5.262 18.106-4.473 4.967 2.926 7.036 13.563 6.257 16.658a3.561 3.561 0 0 1-3.3 2.686.794.794 0 0 1-.858-.791c-1.144-9.775-3.335-12.139-3.582-12.377a1 1 0 0 0-.744-.28c-.358.018-2.956-.343-13.509 4.457a.97.97 0 0 0-.37 1.365 1.049 1.049 0 0 0 1.365.369c7.878-3.365 10.1-3.936 12.094-4.148.54.877 1.925 3.726 2.759 10.845a2.777 2.777 0 0 0 2.714 2.555 4.132 4.132 0 0 0 .993-.057z"/>
    <path d="M122.9 161.253l-16.287 3.61a1 1 0 0 1-.433-1.953l16.287-3.61a1 1 0 0 1 .433 1.953z"/>
    <path d="M124.115 166.721l-16.287 3.61a1 1 0 1 1-.433-1.953l16.287-3.61a1 1 0 1 1 .433 1.953z"/>
    <path d="M125.219 171.7l-16.287 3.61a.5.5 0 1 1-.216-.976l16.284-3.61a.5.5 0 0 1 .216.976z"/>
    <path d="M177.123 139.793a1 1 0 0 1-1.06-1.514l8.964-14.069a1 1 0 0 1 1.687 1.075l-8.964 14.069a1 1 0 0 1-.627.439z"/>
    <path d="M105.051 211.452a5.653 5.653 0 0 1-5.63-2.134c-2.28-3.056-4.647-14.652-.62-20.181 3.171-4.352 22.929-3.128 25.11-2.93a1 1 0 0 1 .9 1.087 1.024 1.024 0 0 1-1.086.9c-22.257-.643-23.329 2.139-23.329 2.139-3.462 4.61-1.284 15.225.625 17.784a3.561 3.561 0 0 0 4.076 1.213.794.794 0 0 0 .488-1.06c-2.7-9.464-1.586-12.489-1.449-12.8a1 1 0 0 1 .58-.544c.337-.121 7.4-.753 19.966.848a1 1 0 1 1-.253 1.983c-10.344-1.318-16.621-1.676-18.541-1.106-.162 1.018-.346 4.18 1.62 11.072a2.777 2.777 0 0 1-1.524 3.4 4.132 4.132 0 0 1-.933.329z"/>
    <path d="M154.954 133.172s2.432 4.544 3.463 4.542 1.742-2.279 4.785-3.524c3.272-1.339 5.033.538 6.124-.251s1.2-5.459.718-6.368-3.7-2.42-8.672-.654-6.905 5.347-6.418 6.255z"/>
    <path d="M131.166 148.6a6.476 6.476 0 0 0 .643.1 1.58 1.58 0 0 0 .307.046 6.4 6.4 0 0 0 1.934-.631 14.34 14.34 0 0 0 4.857-3.481 2.88 2.88 0 0 0 .685-2.355 1.75 1.75 0 0 0-1.255-1.279c-2.225-.807-9.326-.381-12.807 2.416a1.52 1.52 0 0 0-.486 1.768c.538 1.397 4.244 2.916 6.122 3.416z"/>
    <path d="M131.973 147.394s-.186 6.672 2.354 8.45"/>
    <path d="M134.349 156.844a1 1 0 0 1-.595-.181c-2.9-2.028-2.8-8.559-2.781-9.3a.982.982 0 0 1 1.028-.972 1 1 0 0 1 .972 1.027c-.064 2.374.4 6.536 1.929 7.6a1 1 0 0 1-.553 1.819z"/>
    <path d="M191.146 210.564c-.657-8.673-1.389-15.793-2.2-22.113a70.69 70.69 0 0 0-1.943.918c.927 6.929 1.676 13.949 2.144 21.2l.015.068a61.494 61.494 0 0 1-.075 9.829c-.254 3.16-.151 3.467-.359 4.558-.067.343-.139.666-.211.989l.022.1-.215.877-.033.13-.221.807.019.088a15.562 15.562 0 0 1-1.319 3.293l-.046.072-.228.358-.078.109-.216.273-.09.1-.2.2-.087.07-.205.138-.088.019-.215.048-.049.011a.77.77 0 0 1-.476-.069c-.67-.343-2-6.187-3.045-11.414 2.134-1.139 4.734-2.719 4.879-2.8a.5.5 0 1 0-.518-.858s-3.162 1.909-5.347 3.049l-.156.035a113.632 113.632 0 0 1-16.147 4.695l-1.25.277-2.119.47-.586.13-1.837.356-.976.216-1.45.25-1.423.233-.989.158-2.114.3-.185.041c-2.4.326-4.649.559-6.637.693a17.65 17.65 0 0 1-4.866-1.482.5.5 0 1 0-.446.9 17.821 17.821 0 0 0 5.563 1.625c1.25 5.131 2.256 9.948 1.791 10.532a.769.769 0 0 1-.224.193l-.1.022a.784.784 0 0 1-.166.037l-.137.03-.156.035-.2-.037-.13-.033-.276-.092-.091-.041-.357-.177c-1.723-.98-4.285-3.762-7.025-8.174a46.184 46.184 0 0 1-2.75-5.383c-2.877-3.46-6.6-10.108-9.711-19.642q-1.77-5.725-3.244-10.808c-.206-.022-.41-.043-.606-.059-.655-.052-1.3-.1-1.936-.157 4.495 15.768 9.369 28.443 13.727 31.643a46.869 46.869 0 0 0 2.769 5.378c1.93 3.116 6.835 10.274 10.574 9.446a2.76 2.76 0 0 0 1.582-1c.64-.808 1.16-1.466-1.121-11.019a149.565 149.565 0 0 0 17.448-2.843 123 123 0 0 0 15.17-4.294c1.953 9.554 2.86 10.9 3.817 11.386a2.76 2.76 0 0 0 1.855.234c3.739-.829 5.571-8.783 5.4-14.023a61.9 61.9 0 0 0 .1-10.101zm-79.172-59.54l-.182-.82-2.3-10.359c-1.052-5.257-1.793-9.845-2.11-13.124l-.441-.281-3.817-2.432a7.778 7.778 0 1 1 8.359-13.12l2.111 1.345c-4.114 2.837-4.368 6.448-4.383 6.615a.5.5 0 0 0 .474.52l.137-.03a.5.5 0 0 0 .391-.455c-.032-.146.249-3.5 4.3-6.074 4.568-2.5 12.973-7.044 16.035-8.2 3.885-1.465 11.18-7.23 12.5-8.282l.2-.167-1.02 5.194-.067.343 4.955-2.737.059-.013.63 2.841 11.181.492.084-.131a11.81 11.81 0 0 1 7.682 4.115.5.5 0 1 0 .755-.659 12.79 12.79 0 0 0-7.84-4.408l.448-.7a7.778 7.778 0 1 1 13.12 8.359l-1.634 2.564c.311.894.616 1.81.924 2.735.065.293.188.573.275.871q.3.957.618 2a5 5 0 0 0 .285.961q.386 1.328.756 2.67l.206.927q.216.976.5 1.938l.314 1.416.307 1.386.361 1.63.112.508.959 4.325.039.176c2.917 16.877 6.122 32.387 8.549 48.11a99.288 99.288 0 0 1 1.969-.924c-2.147-13.977-4.762-25.188-7.888-44.349a179.865 179.865 0 0 0-6.07-24.087l1.117-1.753a9.751 9.751 0 0 0-16.447-10.479l-.509.8-8.492-.371-.859-3.91-2.337 1.286-1.261.689.259-1.327.987-5.022.076-.4-.3.24-3.993 3.241c-2.342 1.82-8.958 6.861-12.167 8.094-2.941 1.1-10.422 5.106-15.256 7.744l-2.087-1.33a9.76 9.76 0 1 0-11.04 16.1q.269.185.55.351l3.447 2.2A571.373 571.373 0 0 0 117.6 187.6q1.175.069 2.441.158c-6.999-25.479-8.067-36.735-8.067-36.735z"/>
  </g>
</svg>
PK
!<C�)�J�JGchrome/browser/content/browser/illustrations/error-server-not-found.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="300" height="300" viewBox="0 0 300 300">
  <defs>
    <linearGradient id="a" x1="-300.021" y1="-272.736" x2="547.138" y2="574.423" gradientUnits="userSpaceOnUse">
      <stop offset="0" stop-color="#ccfbff"/>
      <stop offset="1" stop-color="#c9e4ff"/>
    </linearGradient>
    <linearGradient id="b" x1="-18.672" y1="23.78" x2="279.805" y2="322.256" gradientUnits="userSpaceOnUse">
      <stop offset="0" stop-color="#00c8d7"/>
      <stop offset="1" stop-color="#0a84ff"/>
    </linearGradient>
  </defs>
  <path d="M224.245 144.067h-10.733c.136.343.274.674.41 1h10.323a.5.5 0 0 0 0-1zm2.454-11.821a.5.5 0 0 0-.5-.5h-20.26c.373.357.727.688 1.065 1h19.2a.5.5 0 0 0 .496-.5zm8.546 11.821h-3a.5.5 0 1 0 0 1h3a.5.5 0 0 0 0-1zm5 0h-1a.5.5 0 1 0 0 1h1a.5.5 0 0 0 0-1zm-3.3-6.66h-25.78a12.767 12.767 0 0 1 .862 2h24.918a1 1 0 0 0 0-2zm20.422 6.66h-8.122a.5.5 0 1 0 0 1h8.122a.5.5 0 0 0 0-1z" fill="#eaeaee"/>
  <path d="M269.53 87.757h-24.236c-2.108-3.9-7.559-12.718-14.4-14.023-8.952-1.707-10.737 7.217-10.737 7.217s-5.949-15.468-21-13.419c-16.878 2.3-8.928 20.065-8.928 20.065h-25.408l8.181.159h-8.184a1 1 0 0 0 0 2H269.53a1 1 0 0 0 0-2z" fill="#fff"/>
  <path d="M118.373 63.908h-13.69c-1.129-2.112-4.19-7.156-8.057-7.894-4.978-.949-5.971 4.013-5.971 4.013s-3.309-8.6-11.68-7.462c-9.386 1.278-4.965 11.158-4.965 11.158H59.88l9.471.185h-9.212a1 1 0 0 0 0 2h58.233a1 1 0 1 0 0-2z" fill="#fff"/>
  <path d="M102.31 121.507H60.818a1 1 0 0 0 0 2h41.492a1 1 0 1 0 0-2zM70.336 117.6H82.1a.5.5 0 0 0 0-1H70.336a.5.5 0 0 0 0 1z" fill="#eaeaee"/>
  <path d="M111.457 174.8h-78.3a1 1 0 0 0 0 2h78.3a1 1 0 1 0 0-2zm-26.742-3.793h1a.5.5 0 0 0 0-1h-1a.5.5 0 0 0 0 1zm10 0h3.1a.5.5 0 0 0 0-1h-3.1a.5.5 0 0 0 0 1zm-17 0h3a.5.5 0 0 0 0-1h-3a.5.5 0 0 0 0 1zm-20 0h12a.5.5 0 0 0 0-1h-12a.5.5 0 0 0 0 1z" fill="#eaeaee"/>
  <path d="M206.885 62.973l.045-.1c-.058.027-.063.059-.045.1z" fill="#fff"/>
  <path d="M77.937 214.941H39.95a1 1 0 1 1 0-2h37.987a1 1 0 1 1 0 2z" fill="#eaeaee"/>
  <path d="M258.931 214.941h-61.813a1 1 0 0 1 0-2h61.813a1 1 0 0 1 0 2z" fill="#eaeaee"/>
  <path d="M265.745 85.333h-3a.5.5 0 0 1 0-1h3a.5.5 0 0 1 0 1zm-11 0h-8.07a.5.5 0 0 1-.447-.277c-.007-.014-.724-1.425-1.979-3.342a.5.5 0 1 1 .837-.548c.393.6 1.444 2.293 1.888 3.167h7.772a.5.5 0 0 1 0 1zm-66.489-.712h-3a.5.5 0 0 1 0-1h3a.5.5 0 0 1 0 1zm-11 0h-12a.5.5 0 0 1 0-1h12a.5.5 0 0 1 0 1zM190.1 83.13a.5.5 0 0 1-.474-.339c-.1-.29-.2-.615-.31-.971a.5.5 0 1 1 .958-.287c.1.343.2.657.3.937a.5.5 0 0 1-.474.661zm30.5-5.156a.5.5 0 0 1-.467-.32 23.7 23.7 0 0 0-2.182-4.039.5.5 0 0 1 .834-.552 27.291 27.291 0 0 1 1.719 2.982 10.522 10.522 0 0 1 3.679-5.015.5.5 0 0 1 .571.82 10.181 10.181 0 0 0-3.665 5.721.5.5 0 0 1-.448.4zm18.345-2.964a.5.5 0 0 1-.339-.132q-.361-.333-.735-.651a.5.5 0 0 1 .647-.762q.39.331.765.678a.5.5 0 0 1-.339.868zm-49.923-1.725a.47.47 0 0 1-.09-.008.5.5 0 0 1-.4-.581c.792-4.351 3.544-7.229 8.18-8.556a.5.5 0 0 1 .275.962c-4.24 1.212-6.753 3.828-7.472 7.773a.5.5 0 0 1-.499.411zm45.893-1.218a.5.5 0 0 1-.237-.06 12.545 12.545 0 0 0-2.666-1.081.5.5 0 1 1 .261-.966 13.559 13.559 0 0 1 2.88 1.167.5.5 0 0 1-.238.94zM212.146 67.4a.5.5 0 0 1-.28-.086q-.4-.27-.82-.524a.5.5 0 1 1 .516-.856q.444.267.865.552a.5.5 0 0 1-.281.914zm-4.47-2.2a.5.5 0 0 1-.154-.024 16.724 16.724 0 0 0-2.832-.647.5.5 0 0 1 .137-.99 17.6 17.6 0 0 1 3 .686.5.5 0 0 1-.154.976z" fill="#eaeaee"/>
  <path d="M72.315 62.052h-12a.5.5 0 0 1 0-1h12a.5.5 0 0 1 0 1zm45.582-.184h-1.8a.5.5 0 0 1 0-1h1.8a.5.5 0 0 1 0 1zm-10.8 0h-1a.5.5 0 0 1 0-1h1a.5.5 0 0 1 0 1zm-3.491-2.881a.5.5 0 0 1-.39-.186 18.484 18.484 0 0 0-2-2.129.5.5 0 1 1 .668-.744A19.433 19.433 0 0 1 104 58.174a.5.5 0 0 1-.389.814zm-12.521-.631a.5.5 0 0 1-.466-.319 13.8 13.8 0 0 0-3.857-5.165.5.5 0 0 1 .623-.782 14.47 14.47 0 0 1 3.624 4.438A5.414 5.414 0 0 1 94.12 53.5a.5.5 0 0 1 .326.945 4.994 4.994 0 0 0-2.867 3.51.5.5 0 0 1-.49.401zm-17.637-2.037h-.051a.5.5 0 0 1-.447-.548 6.454 6.454 0 0 1 1.057-3.036.5.5 0 1 1 .824.566 5.46 5.46 0 0 0-.886 2.571.5.5 0 0 1-.497.447zm4.193-5.039a.5.5 0 0 1-.151-.977 10.27 10.27 0 0 1 1.017-.264.5.5 0 0 1 .2.979 9.033 9.033 0 0 0-.917.237.5.5 0 0 1-.148.025z" fill="#eaeaee"/>
  <ellipse cx="143.502" cy="244.914" rx="54.673" ry="8.306" fill="#eaeaee"/>
  <path d="M223.695 147.595a8.557 8.557 0 0 0-3.594-1.658v-.084a5 5 0 0 0-5.98-5.15 36.839 36.839 0 0 0-6.483-9.3 83.918 83.918 0 0 0-8.865-9.925 5.554 5.554 0 0 0-2.252-1.3c-7.134-6.844-21.832-19.69-32.3-20.412a27.308 27.308 0 0 0-1.879-.063A46.863 46.863 0 0 0 147.3 102.6l-5.07-1.386a5 5 0 0 0-6.142 3.5l-.959 3.508a68.223 68.223 0 0 0-7.774 5.252l-2.026-.047h-.116a5 5 0 0 0-5 4.884l-.042 1.8a45.3 45.3 0 0 0-5.6 7.593 5 5 0 0 0-2.508 5.313c-1.868 5.441-2.827 13.654-2.855 24.468-.454 2.653-1 5.24-1.63 7.721l-4.239 2.569a6.3 6.3 0 0 0-2.122 8.645l1.5 2.469q-.366.757-.741 1.478l-4 1.227a6.3 6.3 0 0 0-4.171 7.866l.59 1.923q-.5.569-1.007 1.109l-1.588-.122a5.857 5.857 0 0 0-.5-.02 6.325 6.325 0 0 0-6.268 5.8l-.113 1.431c-2.725 2.033-5.38 4.525-5.38 9.073 0 6.507 10.219 10.526 13.578 11.847a68.529 68.529 0 0 0 24.074 4.751q.549 0 1.093-.012c.6 2.385 1.2 4.42 1.778 6.08a54.622 54.622 0 0 0 4.716 10.2c1.741 2.827 4.2 6.072 7.821 6.072a5.668 5.668 0 0 0 3.569-1.267c1.74-1.416 2.422-3.057 2.477-10.005a86.376 86.376 0 0 0 16.716 1.516 80.415 80.415 0 0 0 14.4-1.446c-.21 6.755.477 8.474 2.273 9.935a5.668 5.668 0 0 0 3.568 1.267c5.627 0 9.914-8.85 12.52-16.273.379-1.079.76-2.326 1.133-3.705h-.008c1.555-5.314 12.415-46.933 15.771-59.81l9.2.076h.041q.235 0 .47-.022a13.639 13.639 0 0 0 8.795-4.994l.014.011c4.448-5.667 4.514-11.806.157-15.28z" fill="#fff"/>
  <g fill="url(#a)">
    <path d="M121.625 218.616c-4.1-12.832-7.76-39.924-7.746-61.081a103.51 103.51 0 0 1-2.321 10.573l-5.961 3.612a1.3 1.3 0 0 0-.437 1.779l2.865 4.729a59.209 59.209 0 0 1-2.977 5.993l-5.939 1.823a1.3 1.3 0 0 0-.858 1.619l1.385 4.511a51.054 51.054 0 0 1-4.661 5.155l-3.9-.308a1.3 1.3 0 0 0-1.394 1.19l-.294 3.726c-3.236 2.342-5.183 3.517-5.183 6.38 0 4.117 20.661 13.327 37.645 11.314-.078-.339-.149-.677-.224-1.015z"/>
    <path d="M186.98 169.883l-12.066-1.734s10.339 13.394 7.748 33.691c-1 7.817-6.478 19.433-22.024 22.456a45.69 45.69 0 0 1-27.449-3.428v8.583s7.584 3.05 21.835 3.05a87.741 87.741 0 0 0 19.743-2.7c-.4 5.8-.622 11.737.084 12.311 1.416 1.153 5.381-4.714 8.214-12.785a48.073 48.073 0 0 0 1.019-3.337c1.268-3.235 4.283-12.279 4.623-25.878a176.857 176.857 0 0 0-1.727-30.229z"/>
    <path d="M189.791 210.674l-4.457-18.074-8.517-31.348a1.288 1.288 0 0 0-1.554-.989l-20.588 10.535-2.94.327-15.735.134a1.287 1.287 0 0 0-1.177 1.782l6.114 14.682 4.476 18.806a1.278 1.278 0 0 0 .146.349l-1.614 14.85a1.287 1.287 0 0 0 1.3 1.426l18.745-.305q.061 0 .121-.008a1.288 1.288 0 0 0 .151-.026l24.607-10.476a1.287 1.287 0 0 0 .926-1.665z"/>
    <path d="M139.025 177.647a1.384 1.384 0 0 0 .773-1.1 1.407 1.407 0 0 0-1.269-1.533s-1.257-.638-4.768-.72a7.635 7.635 0 0 0-5.773 2.53 75.369 75.369 0 0 1-13.532-5.021 2.5 2.5 0 0 0-2.225 4.478 76.1 76.1 0 0 0 15.616 5.619c1.4 1.485 3.989 2.07 5.679 2.241.1.031 5.754.357 7.31-3.608.474-1.206-.654-2.449-1.811-2.886z"/>
    <path d="M212.017 152.78c2.378-2.584 6.425-3.047 8.227-1.609 2.856 2.278.577 6.3-.976 8.274-.044.09-3.736 4.39-7.671 2.758-1.2-.5-1.309-2.171-.821-3.307a1.384 1.384 0 0 1-1.327-.206 1.407 1.407 0 0 1-.223-1.978s.413-1.348 2.791-3.932z"/>
    <path d="M210.657 147.674a12.766 12.766 0 0 0-.357-2.321c-1.084-4.224-4.233-8.285-6.851-11.094a78.307 78.307 0 0 0-8.425-9.468.648.648 0 0 0-.887.944 80.955 80.955 0 0 1 7.823 8.7c1.579 2.767 3.417 6.488 3.862 9.489a17.1 17.1 0 0 1-1.055 7.18zm-56.427 14.1a1.3 1.3 0 0 0 .514 1.758c.142.078 2.046 1.108 5.194 2.412l2.974-1.612a63.892 63.892 0 0 1-6.924-3.073 1.3 1.3 0 0 0-1.758.517z"/>
  </g>
  <g fill="#f9f9fa">
    <path d="M164.107 222.841q-.06.007-.121.008l-18.745.305a1.287 1.287 0 0 1-1.3-1.426l1.614-14.85a1.28 1.28 0 0 1-.146-.349l-4.476-18.806-2.024-4.861a9.694 9.694 0 0 1-5.383 1.28c-1.69-.17-4.282-.756-5.679-2.241a70.415 70.415 0 0 1-13.1-4.449c1.326 16.092 3.99 32.149 6.873 41.164a93.749 93.749 0 0 0 2.817 10.711c2.833 8.071 6.815 13.938 8.231 12.785.67-.545.705-6.511.557-12.645l-.042-.016v-8.583a45.69 45.69 0 0 0 27.449 3.428 29.062 29.062 0 0 0 11.749-4.94l-8.127 3.46a1.29 1.29 0 0 1-.147.025z"/>
    <path d="M218.386 185.448c-.284-.616-4.741-17.387-4.741-17.387l.274-5.519a4.792 4.792 0 0 1-2.322-.339c-1.2-.5-1.309-2.171-.821-3.307a1.384 1.384 0 0 1-1.328-.206 1.407 1.407 0 0 1-.223-1.978s.413-1.348 2.791-3.932a7.392 7.392 0 0 1 2.474-1.729l.287-5.78-10.011 5.837a17.1 17.1 0 0 0 1.055-7.18c-.445-3-2.283-6.722-3.862-9.489a80.955 80.955 0 0 0-7.823-8.7.648.648 0 0 1-.028-.915s.011-.006.015-.011c-10.105-9.943-22.875-19.853-30.577-20.384-4.5-.311-10.323.8-16.37 3.082l-6.6-1.805-1.511 5.524a63.4 63.4 0 0 0-10.33 6.958l-3.858-.09-.086 3.719a40.259 40.259 0 0 0-7.075 9.61l-1.076.336.36 1.137c-3.164 7.31-3.661 22.679-2.656 38.859.037.016.076.024.113.043a75.369 75.369 0 0 0 13.532 5.021 6.434 6.434 0 0 1 .467-.48c.065-.061.135-.118.2-.177.1-.088.2-.175.314-.259l.3-.218a7.807 7.807 0 0 1 .651-.406c.045-.025.091-.048.137-.072a7.522 7.522 0 0 1 3.707-.919c.615.014 1.137.049 1.619.092l-.56-1.346a1.287 1.287 0 0 1 1.176-1.779l15.739-.139 2.94-.327 20.588-10.535a1.287 1.287 0 0 1 1.554.989l8.517 31.348 4.457 18.078a1.287 1.287 0 0 1-.926 1.665l-24.607 10.476a1.29 1.29 0 0 1-.151.026q-.06.007-.121.008l-18.745.305a1.287 1.287 0 0 1-1.3-1.426l1.614-14.85a1.28 1.28 0 0 1-.146-.349l-4.476-18.806-2.024-4.861a7.222 7.222 0 0 1-1.957.882l1.244 2.643 4.883 18.667-1.442 20.018 22.316.754 28.2-12.809 22.4-5.773 12.27-10s-8.157-11.179-8.441-11.795zm-58.447-19.5c-3.153-1.305-5.053-2.334-5.2-2.412a1.3 1.3 0 0 1 1.244-2.273 64.081 64.081 0 0 0 6.926 3.071z"/>
  </g>
  <path d="M148.2 151.572a1 1 0 0 0-.969-.513l-5.848.544a1 1 0 0 0-.518 1.788l3.4 2.625a1 1 0 0 0 .611.208 1.023 1.023 0 0 0 .128-.008 1 1 0 0 0 .664-.381l2.444-3.169a1 1 0 0 0 .088-1.094z" fill="#fff"/>
  <g fill="url(#a)">
    <path d="M215.942 188.189a.5.5 0 0 0-.643-.294c-.657.244-6.829 2.359-9.226 3.179a.485.485 0 0 0-.129.02l-.223.082-.3-.533-4.132-16.317c3.722-1.631 9.422-4.082 10-4.2a.5.5 0 0 0 .43-.551.507.507 0 0 0-.552-.442c-.612.068-7.152 2.91-10.007 4.159l-.978-16.222a.506.506 0 0 0-.529-.469.5.5 0 0 0-.469.529l1.027 16.881a.482.482 0 0 0 .005.087v.063l4.244 16.8a.5.5 0 0 0 .048.121l.252.452-14.371 5.266a.5.5 0 1 0 .345.938l14.53-5.331 7.075 12.687a.5.5 0 1 0 .873-.487l-6.987-12.529c2.259-.773 8.727-2.988 9.423-3.247a.5.5 0 0 0 .294-.642zm-17.866-13.124a.5.5 0 0 0-.645-.29l-12.685 4.811a.5.5 0 0 0 .177.967.49.49 0 0 0 .177-.033l12.685-4.811a.5.5 0 0 0 .29-.644z"/>
    <path d="M177.573 181.528a1.031 1.031 0 1 0 1.081.668 1.036 1.036 0 0 0-1.081-.668z"/>
  </g>
  <path d="M186.537 198.867a.5.5 0 0 0-.634-.312l-22.81 7.758-.215-2.424-3.513-18.32 22.327-4.386a.5.5 0 0 0-.192-.981l-22.373 4.4-4.2-13.34a.5.5 0 0 0-.954.3l4.153 13.167-15.205 1.277a.5.5 0 0 0 .042 1h.042l15.376-1.294 3.513 18.32.214 2.418c-3.146-.116-12.594-.432-13.583-.117a.5.5 0 0 0-.321.621.506.506 0 0 0 .623.333c.734-.214 8.379-.017 13.371.167l1.382 15.6a.5.5 0 0 0 .5.456h.044a.5.5 0 0 0 .454-.542l-1.386-15.624 23.041-7.837a.5.5 0 0 0 .304-.64z" fill="#f9f9fa"/>
  <g fill="url(#b)">
    <path d="M158.845 166.591l-3.592-1.575a1 1 0 0 1 .8-1.832l4.854 2.129z"/>
    <path d="M151.812 151.976a11.582 11.582 0 0 0-15.092 1.077 1 1 0 0 0 1.387 1.441 10.932 10.932 0 0 1 1.693-1.238c1.011 1.259 2.852 3.273 4.438 3.474a1.779 1.779 0 0 0 .228.014c1.905 0 3.843-2.924 4.537-4.082a11.155 11.155 0 0 1 1.621.922 1 1 0 0 0 1.189-1.608zm-7.448 3.762c-1.113-.141-2.654-1.735-3.671-2.971a9.546 9.546 0 0 1 4.815-1.067 9.091 9.091 0 0 1 2.56.576c-.968 1.589-2.592 3.611-3.705 3.461z"/>
    <path d="M195.7 141.2a5.614 5.614 0 0 0-9.187 0 1 1 0 1 0 1.679 1.088 3.7 3.7 0 0 1 2.915-1.475 3.648 3.648 0 0 1 2.918 1.48 1 1 0 0 0 1.675-1.093z"/>
    <path d="M155.081 127.311a1 1 0 0 0 1.383-.28 3.618 3.618 0 0 1 5.838 0 1 1 0 0 0 1.675-1.093 5.614 5.614 0 0 0-9.187 0 1 1 0 0 0 .291 1.373z"/>
    <path d="M189.934 134.871c0 .034-.01.066-.01.1v2.844a6.008 6.008 0 0 1 2.784.078v-2.922a1.378 1.378 0 0 0-.023-.227 5.846 5.846 0 0 0 2.993-2.135 1 1 0 0 0-1.678-1.094 3.617 3.617 0 0 1-5.838 0 1 1 0 0 0-1.674 1.094 5.755 5.755 0 0 0 3.446 2.262z"/>
    <path d="M158.008 134.971v3.152a5.941 5.941 0 0 1 2.781-.038v-3.114a1.392 1.392 0 1 0-2.784 0z"/>
    <path d="M159.4 139.021a5.674 5.674 0 0 0-4.593 2.387 1 1 0 0 0 1.674 1.094 3.618 3.618 0 0 1 5.838 0 1 1 0 0 0 1.675-1.093 5.674 5.674 0 0 0-4.594-2.388z"/>
    <path d="M180.388 181.389a2.938 2.938 0 1 0-4.66 3.254 4.326 4.326 0 0 0 .765.557l2.4 1.8a.562.562 0 0 0 .531.077h.008a.564.564 0 0 0 .35-.418l.577-3a3.9 3.9 0 0 0 .029-2.27zm-2.344 2.132a1.031 1.031 0 1 1 .61-1.325 1.034 1.034 0 0 1-.61 1.325z"/>
    <path d="M212.864 169.173a.64.64 0 0 0-.9-.158l-6.583 4.6-6.6 4.16-.891.437-6.69-16.391-6.419 2.481-1.271-5.447a.643.643 0 1 0-1.252.294l1.315 5.621-3.806 1.471v.01l-19.531 7.538 3.908 11.6-2.937 1.966-7.657 3.829-11.434.845a.8.8 0 0 0-.1.031l.3 1.246 11.461-.848.127-.01 7.955-3.983 2.715-1.815 8.963 26.609 13.988-4.649-.86-1.86 3.448-1.219 1.248-.441 5.962 3.956a.643.643 0 1 0 .71-1.073l-5.168-3.415.03-.01-1.152-2.5 13.594-5.606-6.957-17.044 1.032-.505 6.682-4.212 6.609-4.621a.642.642 0 0 0 .161-.887zm-9.2 26.575l-13.647 5.629.785 1.7-2.343-2.449-1.288-1.346-4.425-3.346L180.2 193l-.376-4.707a1.748 1.748 0 0 1-1.294-.042l.421 5.273 2.862 3.309 4.5 3.41 3.112 3.263.648.68-.228.081-.146.052-4.8 1.7.847 1.836-11.4 3.79-8.665-25.724 1.41-.943 5.921.554 2.9 2.118 1.361-.251-1.479-1.1a5.89 5.89 0 0 1-.9-.663 4.206 4.206 0 0 1-.783-.893l-.624-.456-6.729-.628-1.5 1-3.4-10.1 19.632-7.577v-.01l2.388-.923.438 1.874.528 4.731.565 3.225-1 2.954-2.623 1.281-.4.374a4.217 4.217 0 0 1 .218.466 3.835 3.835 0 0 1 .2.87l.634-.6 3.021-1.474 1.275-3.774-.616-3.508-.536-4.767-.5-2.123 5.41-2.091 6.246 15.3-10.983 5.386-4.378 1.031-.267 1.386 4.4-1.038.607-.143 11.1-5.432z"/>
    <path d="M211.383 147.927l2.9-1.77.039 5.23a.5.5 0 0 0 .5.5.5.5 0 0 0 .5-.5l-.046-6.113a.5.5 0 0 0-.76-.423l-3.655 2.229a.5.5 0 0 0 .521.854zm15.845 49.026l-8.383-11.723-4.7-17.252-.32-5.243a.51.51 0 0 0-.529-.469.5.5 0 0 0-.469.529l.323 5.294a.5.5 0 0 0 .017.1l4.733 17.391a.5.5 0 0 0 .076.16l8.163 11.414-11.821 9.633-22.291 5.745c-.015 0-.026.014-.04.02s-.028 0-.041.01l-28.095 12.761-21.68-.732 1.4-19.5a.506.506 0 0 0-.015-.163l-4.876-18.668a.537.537 0 0 0-.041-.1l-1.539-2.96a.5.5 0 0 0-.887.463l1.515 2.9 4.848 18.532-1.435 19.936a.5.5 0 0 0 .481.536l22.316.754h.017a.5.5 0 0 0 .207-.045l28.159-12.79 22.358-5.762a.505.505 0 0 0 .191-.1l12.27-10a.5.5 0 0 0 .088-.671zm-96.2-26.9l23.165-.486a.5.5 0 0 0 .228-.061l22.133-12 14.9-1.49h.011l7.36-.909a.5.5 0 0 0 .187-.062l8.711-4.962a.5.5 0 1 0-.5-.869l-8.624 4.913-7.249.9-15 1.5a.5.5 0 0 0-.188.058l-22.115 11.992-23.782.5a.5.5 0 0 0-.449.7l1.47 3.46a.5.5 0 0 0 .46.3.493.493 0 0 0 .2-.04.5.5 0 0 0 .265-.655z"/>
    <path d="M221.337 150.947c-.37-.407-2.546-2.481-6.929-.472a1 1 0 0 0 .832 1.818c3.251-1.489 4.553-.074 4.594-.03.024.03.064.072.091.1.076.074 1.82 1.86-.674 5.575-2.65 3.945-5.713 3.84-5.862 3.832a1 1 0 0 0-.142 1.995c.019 0 .085.006.193.006.877 0 4.494-.285 7.471-4.717 3.289-4.902.831-7.7.426-8.107zm-9.037 2.245a.5.5 0 1 0-.567-.823c-.354.244-3.456 2.447-3.149 4.537.13.889.826 1.577 2.071 2.049v2.479a.5.5 0 0 0 1 0V158.6a.5.5 0 0 0-.347-.476c-1.069-.345-1.652-.8-1.735-1.363-.183-1.25 1.962-3.041 2.727-3.569z"/>
    <path d="M141.324 179.661a1 1 0 0 0-1.361.384 5.382 5.382 0 0 1-3.357 2.765c-3.375.921-7.389-1.383-7.43-1.407a1.043 1.043 0 0 0-.494-.137 23.844 23.844 0 0 1-5.361-1.266 94.816 94.816 0 0 1-10.046-4.156 1 1 0 1 0-.87 1.8 94.837 94.837 0 0 0 10.318 4.263 28.613 28.613 0 0 0 5.662 1.344 14.825 14.825 0 0 0 6.505 1.775 8.516 8.516 0 0 0 2.233-.285 7.38 7.38 0 0 0 4.585-3.72 1 1 0 0 0-.384-1.36zm-13.077-2.407c1.673-1.014 6.485-3.407 8.631-2.308a2.26 2.26 0 0 1 1.111 2.027.5.5 0 0 0 .125.292l1.333 1.5a.5.5 0 1 0 .747-.664l-1.225-1.378a3.151 3.151 0 0 0-1.637-2.667c-3.076-1.572-9.338 2.182-9.6 2.343a.5.5 0 0 0 .519.855z"/>
    <path d="M187.079 215.322a62.9 62.9 0 0 1-2.99 10.65 48.268 48.268 0 0 1-1.024 3.355c-2.593 7.389-6.135 12.929-7.8 12.929a.634.634 0 0 1-.412-.145c-.706-.575-.484-6.506-.084-12.311a87.741 87.741 0 0 1-19.743 2.7c-11.364 0-18.463-1.931-20.878-2.715l-.237-5.569a.5.5 0 0 0-1 .043l.386 9.076c.038 4.572-.1 8.349-.622 8.777a.635.635 0 0 1-.412.145c-1.67 0-5.226-5.541-7.819-12.929a88.238 88.238 0 0 1-2.593-9.678v-.018c-2.557-10.229-4.4-21.578-4.427-21.725a.5.5 0 0 0-.984.174c.025.144 1.891 11.541 4.373 21.641a43.511 43.511 0 0 1-3.955.194c-15.782 0-32.651-7.878-32.651-11.6 0-2.862 1.947-4.038 5.183-6.379l.294-3.727a1.3 1.3 0 0 1 1.284-1.194h.109l2.658.209c-.932 1-1.8 1.954-1.856 2.019a.5.5 0 0 0 .741.672c.02-.022 1.7-1.876 2.915-3.143a51.528 51.528 0 0 0 4.106-4.6l-1.385-4.51a1.3 1.3 0 0 1 .858-1.619l4.541-1.395-1.472 2.6a.5.5 0 0 0 .871.492l2-3.526a59.168 59.168 0 0 0 2.977-5.992l-2.871-4.723a1.3 1.3 0 0 1 .436-1.78l4.685-2.84-.841 2.8a.5.5 0 0 0 .335.623.506.506 0 0 0 .144.021.5.5 0 0 0 .479-.356l1.159-3.861a103.318 103.318 0 0 0 2.315-10.528c0-9.635.771-18.028 2.6-23.3l.629 1.572a.5.5 0 0 0 .929-.371l-1.036-2.59-.354-1.132 1.076-.336a39.516 39.516 0 0 1 6.206-8.693 17.58 17.58 0 0 0 .293 2.241.5.5 0 0 0 .472.34.5.5 0 0 0 .477-.656c-.081-.25-.222-1.675-.361-3.33l.075-3.231 3.858.09a63.4 63.4 0 0 1 10.33-6.958l1.511-5.524 5.175 1.415-2.448 1.1a.5.5 0 1 0 .409.912l3.465-1.619a43.83 43.83 0 0 1 14.834-3.133q.79 0 1.535.051c7.7.531 20.471 10.442 30.576 20.384a.65.65 0 0 1 .461-.192.635.635 0 0 1 .439.175 78.316 78.316 0 0 1 8.426 9.469c2.618 2.808 5.766 6.87 6.851 11.093a12.766 12.766 0 0 1 .357 2.321l1.9-1.109a14.088 14.088 0 0 0-.321-1.71c-1.189-4.633-4.577-9-7.244-11.873a80.227 80.227 0 0 0-8.6-9.651 2.616 2.616 0 0 0-1.645-.711 132.405 132.405 0 0 0-15.667-13.173c-6.275-4.386-11.455-6.747-15.4-7.019-.538-.037-1.1-.056-1.673-.056a44.826 44.826 0 0 0-14.927 3.034l-5.98-1.635a2 2 0 0 0-2.457 1.4l-1.287 4.707a65.217 65.217 0 0 0-9.321 6.285l-3.115-.072h-.047a2 2 0 0 0-2 1.953l-.068 2.94a42.293 42.293 0 0 0-6.474 8.772l-.291.091a2 2 0 0 0-1.313 2.506l.143.457c-1.979 5.088-2.989 13.356-3 24.6a102.64 102.64 0 0 1-2.034 9.4l-5.279 3.2a3.3 3.3 0 0 0-1.111 4.526l2.326 3.838a57.63 57.63 0 0 1-2.077 4.169l-5.176 1.59a3.3 3.3 0 0 0-2.183 4.118l1.073 3.494a49.486 49.486 0 0 1-3.185 3.519l-3-.236a3.17 3.17 0 0 0-.266-.011 3.31 3.31 0 0 0-3.278 3.035l-.221 2.8c-2.946 2.116-5.262 3.81-5.262 7.461 0 3.883 7.315 7.34 11.675 9.055a65.414 65.414 0 0 0 22.976 4.543 46.33 46.33 0 0 0 3.429-.125 80.231 80.231 0 0 0 2.274 8.2c.513 1.46 5.14 14.267 9.706 14.267a2.624 2.624 0 0 0 1.675-.594c.828-.674 1.5-1.218 1.349-11.451a76.223 76.223 0 0 0 19.738 2.288 85.8 85.8 0 0 0 17.577-2.149c-.579 10.036.152 10.631.988 11.312a2.623 2.623 0 0 0 1.674.594c4.55 0 9.177-12.807 9.689-14.267.345-.983.694-2.123 1.039-3.39a67.385 67.385 0 0 0 3.306-12.285z"/>
  </g>
</svg>
PK
!<�OF�F�+chrome/browser/content/browser/license.html<!DOCTYPE HTML>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this file,
   - You can obtain one at http://mozilla.org/MPL/2.0/.  -->

<html lang="en">
  <head>
    <meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src chrome:; img-src chrome:" />
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
    <title>Licenses</title>
    <link rel="stylesheet" href="chrome://global/skin/in-content/info-pages.css" type="text/css">
    <link rel="stylesheet" href="chrome://global/skin/aboutLicense.css" type="text/css"/>
  </head>

  <body id="lic-info">
  <div class="license-header">
    <div>
      <h1><a id="top"></a>Licenses</h1>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/.  -->
    <p><b>Binaries</b> of this product have been made available to you by the
    <a href="http://www.mozilla.org/">Mozilla Project</a> under the Mozilla
    Public License 2.0 (MPL). <a href="about:rights">Know your rights</a>.</p>
    </div>
  </div>
  <div>
    <p>All of the <b>source code</b> to this product is
       available under licenses which are both
       <a href="http://www.gnu.org/philosophy/free-sw.html">free</a> and
       <a href="http://www.opensource.org/docs/definition.php">open source</a>.
       A URL identifying the specific source code used to create this copy can be found
       on the <a href="about:buildconfig">build configuration page</a>, and you can read
       <a href="https://developer.mozilla.org/en/Mozilla_Source_Code_%28Mercurial%29">instructions
       on how to download and build the code for yourself</a>.
    </p>

    <p>More specifically, most of the source code is available under the
       <a href="about:license#mpl">Mozilla Public License 2.0</a> (MPL).
       The MPL has a
       <a href="http://www.mozilla.org/MPL/2.0/FAQ.html">FAQ</a> to help
       you understand it. The remainder of the software which is not
       under the MPL is available under one of a variety of other
       free and open source licenses. Those that require reproduction
       of the license text in the distribution are given below.
       (Note: your copy of this product may not contain code covered by one
       or more of the licenses listed here, depending on the exact product
       and version you choose.)
    </p>

    <ul>
      <li><a href="about:license#mpl">Mozilla Public License 2.0</a>
      <br><br>
      </li>
      <li><a href="about:license#lgpl">GNU Lesser General Public License 2.1</a>
      <br><br>
      </li>
      <li><a href="about:license#lgpl-3.0">GNU Lesser General Public License 3.0</a>
      <br><br>
      </li>
      <li><a href="about:license#gpl-3.0">GNU General Public License 3.0</a>
      <br><br>
      </li>
      <li><a href="about:license#ACE">ACE License</a></li>
      <li><a href="about:license#acorn">acorn License</a></li>
      <li><a href="about:license#adobecmap">Adobe CMap License</a></li>
      <li><a href="about:license#android">Android Open Source License</a></li>
      <li><a href="about:license#angle">ANGLE License</a></li>
      <li><a href="about:license#agg">Anti-Grain Geometry Public License</a></li>
      <li><a href="about:license#apache">Apache License 2.0</a></li>
      <li><a href="about:license#apple">Apple License</a></li>
      <li><a href="about:license#apple-mozilla">Apple/Mozilla NPRuntime License</a></li>
      <li><a href="about:license#arm">ARM License</a></li>
      <li><a href="about:license#babel">Babel License</a></li>
      <li><a href="about:license#babylon">Babylon License</a></li>
      <li><a href="about:license#bincode">bincode License</a></li>
      <li><a href="about:license#bspatch">bspatch License</a></li>
      <li><a href="about:license#byteorder">byteorder License</a></li>
      <li><a href="about:license#cairo">Cairo Component Licenses</a></li>
      <li><a href="about:license#chromium">Chromium License</a></li>
      <li><a href="about:license#codemirror">CodeMirror License</a></li>
      <li><a href="about:license#cubic-bezier">cubic-bezier License</a></li>
      <li><a href="about:license#d3">D3 License</a></li>
      <li><a href="about:license#dagre-d3">Dagre-D3 License</a></li>
      <li><a href="about:license#diff">diff License</a></li>
      <li><a href="about:license#dtoa">dtoa License</a></li>
      <li><a href="about:license#fuzz-aldrin">fuzz-aldrin License</a></li>
      <li><a href="about:license#hunspell-nl">Dutch Spellchecking Dictionary License</a></li>
      <li><a href="about:license#emojione">EmojiOne License</a></li>
      <li><a href="about:license#hunspell-ee">Estonian Spellchecking Dictionary License</a></li>
      <li><a href="about:license#expat">Expat License</a></li>
      <li><a href="about:license#firebug">Firebug License</a></li>
      <li><a href="about:license#gdi32-sys">gdi32-sys License</a></li>
      <li><a href="about:license#gfx-font-list">gfxFontList License</a></li>
      <li><a href="about:license#google-bsd">Google BSD License</a></li>
      <li><a href="about:license#gears">Google Gears License</a></li>
      <li><a href="about:license#gears-istumbler">Google Gears/iStumbler License</a></li>
      <li><a href="about:license#vp8">Google VP8 License</a></li>
      <li><a href="about:license#gsl">GSL License</a></li>
      <li><a href="about:license#gyp">gyp License</a></li>
      <li><a href="about:license#halloc">halloc License</a></li>
      <li><a href="about:license#harfbuzz">HarfBuzz License</a></li>
      <li><a href="about:license#icu">ICU License</a></li>
      <li><a href="about:license#immutable">Immutable.js License</a></li>
      <li><a href="about:license#jpnic">Japan Network Information Center License</a></li>
      <li><a href="about:license#jsmn">JSMN License</a></li>
      <li><a href="about:license#jszip">JSZip License</a></li>
      <li><a href="about:license#jemalloc">jemalloc License</a></li>
      <li><a href="about:license#jquery">jQuery License</a></li>
      <li><a href="about:license#k_exp">k_exp License</a></li>
      <li><a href="about:license#kernel32-sys">kernel32-sys License</a></li>
      <li><a href="about:license#khronos">Khronos group License</a></li>
      <li><a href="about:license#kiss_fft">Kiss FFT License</a></li>
      <li><a href="about:license#lazy_static">lazy_static License</a></li>
      <li><a href="about:license#lcms">lcms License</a></li>
      <li><a href="about:license#libcubeb">libcubeb License</a></li>
      <li><a href="about:license#libevent">libevent License</a></li>
      <li><a href="about:license#libffi">libffi License</a></li>
      <li><a href="about:license#libjingle">libjingle License</a></li>
      <li><a href="about:license#libnestegg">libnestegg License</a></li>
      <li><a href="about:license#libsoundtouch">libsoundtouch License</a></li>
      <li><a href="about:license#libyuv">libyuv License</a></li>
      <li><a href="about:license#hunspell-lt">Lithuanian Spellchecking Dictionary License</a></li>
      <li><a href="about:license#lodash">lodash License</a></li>
      <li><a href="about:license#matches">matches License</a></li>
      <li><a href="about:license#microformatsshiv">MIT license — microformat-shiv</a></li>
      <li><a href="about:license#myspell">MySpell License</a></li>
      <li><a href="about:license#naturalSort">naturalSort License</a></li>
      <li><a href="about:license#nicer">nICEr License</a></li>
      <li><a href="about:license#node-md5">node-md5 License</a></li>
      <li><a href="about:license#node-properties">node-properties License</a></li>
      <li><a href="about:license#nom">nom License</a></li>
      <li><a href="about:license#nrappkit">nrappkit License</a></li>
      <li><a href="about:license#openaes">OpenAES License</a></li>
      <li><a href="about:license#openvision">OpenVision License</a></li>
      <li><a href="about:license#openvr">OpenVR License</a></li>
      <li><a href="about:license#ordered-float">ordered-float License</a></li>
      <li><a href="about:license#owning_ref">owning_ref License</a></li>
      <li><a href="about:license#pbkdf2-sha256">pbkdf2_sha256 License</a></li>
      <li><a href="about:license#pdfium">PDFium License</a></li>
      <li><a href="about:license#phf">phf, phf_codegen, phf_generator, phf_shared License</a></li>
      <li><a href="about:license#polymer">Polymer License</a></li>
      <li><a href="about:license#praton">praton License</a></li>
      <li><a href="about:license#praton1">praton and inet_ntop License</a></li>
      <li><a href="about:license#precomputed-hash">precomputed-hash License</a></li>
      <li><a href="about:license#prop-types">prop-types License</a></li>
      <li><a href="about:license#qcms">qcms License</a></li>
      <li><a href="about:license#qrcode-generator">QR Code Generator License</a></li>
      <li><a href="about:license#raven-js">Raven.js License</a></li>
      <li><a href="about:license#react">React License</a></li>
      <li><a href="about:license#react-mit">React MIT License</a></li>
      <li><a href="about:license#react-intl">React Intl License</a></li>
      <li><a href="about:license#react-redux">React-Redux License</a></li>
      <li><a href="about:license#xdg">Red Hat xdg_user_dir_lookup License</a></li>
      <li><a href="about:license#redox_syscall">redox_syscall License</a></li>
      <li><a href="about:license#redux">Redux License</a></li>
      <li><a href="about:license#reselect">Reselect License</a></li>
      <li><a href="about:license#hunspell-ru">Russian Spellchecking Dictionary License</a></li>
      <li><a href="about:license#sctp">SCTP Licenses</a></li>
      <li><a href="about:license#skia">Skia License</a></li>
      <li><a href="about:license#snappy">Snappy License</a></li>
      <li><a href="about:license#sprintf.js">sprintf.js License</a></li>
      <li><a href="about:license#sunsoft">SunSoft License</a></li>
      <li><a href="about:license#superfasthash">SuperFastHash License</a></li>
      <li><a href="about:license#synstructure">synstructure License</a></li>
      <li><a href="about:license#unicase">unicase License</a></li>
      <li><a href="about:license#unicode">Unicode License</a></li>
      <li><a href="about:license#ucal">University of California License</a></li>
      <li><a href="about:license#unreachable">unreachable License</a></li>
      <li><a href="about:license#hunspell-en-US">US English Spellchecking Dictionary Licenses</a></li>
      <li><a href="about:license#utf8-ranges">utf8-ranges License</a></li>
      <li><a href="about:license#v8">V8 License</a></li>
      <li><a href="about:license#validator">Validator License</a></li>
      <li><a href="about:license#void">void License</a></li>
      <li><a href="about:license#vtune">VTune License</a></li>
      <li><a href="about:license#webrtc">WebRTC License</a></li>
      <li><a href="about:license#x264">x264 License</a></li>
      <li><a href="about:license#xiph">Xiph.org Foundation License</a></li>
    </ul>

<br>

    <ul>
      <li><a href="about:license#other-notices">Other Required Notices</a>
      <li><a href="about:license#optional-notices">Optional Notices</a>
    </ul>

    </div>

    <hr>

  <h1 id="mpl">Mozilla Public License 2.0</h1>

  <h2 id="definitions">1. Definitions</h2>

  <dl>
    <dt>1.1. "Contributor"</dt>

    <dd>
      <p>means each individual or legal entity that creates, contributes to
      the creation of, or owns Covered Software.</p>
    </dd>

    <dt>1.2. "Contributor Version"</dt>

    <dd>
      <p>means the combination of the Contributions of others (if any) used
      by a Contributor and that particular Contributor's Contribution.</p>
    </dd>

    <dt>1.3. "Contribution"</dt>

    <dd>
      <p>means Covered Software of a particular Contributor.</p>
    </dd>

    <dt>1.4. "Covered Software"</dt>

    <dd>
      <p>means Source Code Form to which the initial Contributor has attached
      the notice in Exhibit A, the Executable Form of such Source Code Form,
      and Modifications of such Source Code Form, in each case including
      portions thereof.</p>
    </dd>

    <dt>1.5. "Incompatible With Secondary Licenses"</dt>

    <dd>
      <p>means</p>

      <ol type="a">
        <li>
          <p>that the initial Contributor has attached the notice described
          in Exhibit B to the Covered Software; or</p>
        </li>

        <li>
          <p>that the Covered Software was made available under the terms of
          version 1.1 or earlier of the License, but not also under the terms
          of a Secondary License.</p>
        </li>
      </ol>
    </dd>

    <dt>1.6. "Executable Form"</dt>

    <dd>
      <p>means any form of the work other than Source Code Form.</p>
    </dd>

    <dt>1.7. "Larger Work"</dt>

    <dd>
      <p>means a work that combines Covered Software with other material, in
      a separate file or files, that is not Covered Software.</p>
    </dd>

    <dt>1.8. "License"</dt>

    <dd>
      <p>means this document.</p>
    </dd>

    <dt>1.9. "Licensable"</dt>

    <dd>
      <p>means having the right to grant, to the maximum extent possible,
      whether at the time of the initial grant or subsequently, any and all
      of the rights conveyed by this License.</p>
    </dd>

    <dt>1.10. "Modifications"</dt>

    <dd>
      <p>means any of the following:</p>

      <ol type="a">
        <li>
          <p>any file in Source Code Form that results from an addition to,
          deletion from, or modification of the contents of Covered Software;
          or</p>
        </li>

        <li>
          <p>any new file in Source Code Form that contains any Covered
          Software.</p>
        </li>
      </ol>
    </dd>

    <dt>1.11. "Patent Claims" of a Contributor</dt>

    <dd>
      <p>means any patent claim(s), including without limitation, method,
      process, and apparatus claims, in any patent Licensable by such
      Contributor that would be infringed, but for the grant of the License,
      by the making, using, selling, offering for sale, having made, import,
      or transfer of either its Contributions or its Contributor Version.</p>
    </dd>

    <dt>1.12. "Secondary License"</dt>

    <dd>
      <p>means either the GNU General Public License, Version 2.0, the GNU
      Lesser General Public License, Version 2.1, the GNU Affero General
      Public License, Version 3.0, or any later versions of those
      licenses.</p>
    </dd>

    <dt>1.13. "Source Code Form"</dt>

    <dd>
      <p>means the form of the work preferred for making modifications.</p>
    </dd>

    <dt>1.14. "You" (or "Your")</dt>

    <dd>
      <p>means an individual or a legal entity exercising rights under this
      License. For legal entities, "You" includes any entity that controls,
      is controlled by, or is under common control with You. For purposes of
      this definition, "control" means (a) the power, direct or indirect, to
      cause the direction or management of such entity, whether by contract
      or otherwise, or (b) ownership of more than fifty percent (50%) of the
      outstanding shares or beneficial ownership of such entity.</p>
    </dd>
  </dl>

  <h2 id="license-grants-and-conditions">2. License Grants and
  Conditions</h2>

  <h3 id="grants">2.1. Grants</h3>

  <p>Each Contributor hereby grants You a world-wide, royalty-free,
  non-exclusive license:</p>

  <ol type="a">
    <li>
      <p>under intellectual property rights (other than patent or trademark)
      Licensable by such Contributor to use, reproduce, make available,
      modify, display, perform, distribute, and otherwise exploit its
      Contributions, either on an unmodified basis, with Modifications, or as
      part of a Larger Work; and</p>
    </li>

    <li>
      <p>under Patent Claims of such Contributor to make, use, sell, offer
      for sale, have made, import, and otherwise transfer either its
      Contributions or its Contributor Version.</p>
    </li>
  </ol>

  <h3 id="effective-date">2.2. Effective Date</h3>

  <p>The licenses granted in Section 2.1 with respect to any Contribution
  become effective for each Contribution on the date the Contributor first
  distributes such Contribution.</p>

  <h3 id="limitations-on-grant-scope">2.3. Limitations on Grant Scope</h3>

  <p>The licenses granted in this Section 2 are the only rights granted under
  this License. No additional rights or licenses will be implied from the
  distribution or licensing of Covered Software under this License.
  Notwithstanding Section 2.1(b) above, no patent license is granted by a
  Contributor:</p>

  <ol type="a">
    <li>
      <p>for any code that a Contributor has removed from Covered Software;
      or</p>
    </li>

    <li>
      <p>for infringements caused by: (i) Your and any other third party's
      modifications of Covered Software, or (ii) the combination of its
      Contributions with other software (except as part of its Contributor
      Version); or</p>
    </li>

    <li>
      <p>under Patent Claims infringed by Covered Software in the absence of
      its Contributions.</p>
    </li>
  </ol>

  <p>This License does not grant any rights in the trademarks, service marks,
  or logos of any Contributor (except as may be necessary to comply with the
  notice requirements in Section 3.4).</p>

  <h3 id="subsequent-licenses">2.4. Subsequent Licenses</h3>

  <p>No Contributor makes additional grants as a result of Your choice to
  distribute the Covered Software under a subsequent version of this License
  (see Section 10.2) or under the terms of a Secondary License (if permitted
  under the terms of Section 3.3).</p>

  <h3 id="representation">2.5. Representation</h3>

  <p>Each Contributor represents that the Contributor believes its
  Contributions are its original creation(s) or it has sufficient rights to
  grant the rights to its Contributions conveyed by this License.</p>

  <h3 id="fair-use">2.6. Fair Use</h3>

  <p>This License is not intended to limit any rights You have under
  applicable copyright doctrines of fair use, fair dealing, or other
  equivalents.</p>

  <h3 id="conditions">2.7. Conditions</h3>

  <p>Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
  in Section 2.1.</p>

  <h2 id="responsibilities">3. Responsibilities</h2>

  <h3 id="distribution-of-source-form">3.1. Distribution of Source Form</h3>

  <p>All distribution of Covered Software in Source Code Form, including any
  Modifications that You create or to which You contribute, must be under the
  terms of this License. You must inform recipients that the Source Code Form
  of the Covered Software is governed by the terms of this License, and how
  they can obtain a copy of this License. You may not attempt to alter or
  restrict the recipients' rights in the Source Code Form.</p>

  <h3 id="distribution-of-executable-form">3.2. Distribution of Executable
  Form</h3>

  <p>If You distribute Covered Software in Executable Form then:</p>

  <ol type="a">
    <li>
      <p>such Covered Software must also be made available in Source Code
      Form, as described in Section 3.1, and You must inform recipients of
      the Executable Form how they can obtain a copy of such Source Code Form
      by reasonable means in a timely manner, at a charge no more than the
      cost of distribution to the recipient; and</p>
    </li>

    <li>
      <p>You may distribute such Executable Form under the terms of this
      License, or sublicense it under different terms, provided that the
      license for the Executable Form does not attempt to limit or alter the
      recipients' rights in the Source Code Form under this License.</p>
    </li>
  </ol>

  <h3 id="distribution-of-a-larger-work">3.3. Distribution of a Larger
  Work</h3>

  <p>You may create and distribute a Larger Work under terms of Your choice,
  provided that You also comply with the requirements of this License for the
  Covered Software. If the Larger Work is a combination of Covered Software
  with a work governed by one or more Secondary Licenses, and the Covered
  Software is not Incompatible With Secondary Licenses, this License permits
  You to additionally distribute such Covered Software under the terms of
  such Secondary License(s), so that the recipient of the Larger Work may, at
  their option, further distribute the Covered Software under the terms of
  either this License or such Secondary License(s).</p>

  <h3 id="notices">3.4. Notices</h3>

  <p>You may not remove or alter the substance of any license notices
  (including copyright notices, patent notices, disclaimers of warranty, or
  limitations of liability) contained within the Source Code Form of the
  Covered Software, except that You may alter any license notices to the
  extent required to remedy known factual inaccuracies.</p>

  <h3 id="application-of-additional-terms">3.5. Application of Additional
  Terms</h3>

  <p>You may choose to offer, and to charge a fee for, warranty, support,
  indemnity or liability obligations to one or more recipients of Covered
  Software. However, You may do so only on Your own behalf, and not on behalf
  of any Contributor. You must make it absolutely clear that any such
  warranty, support, indemnity, or liability obligation is offered by You
  alone, and You hereby agree to indemnify every Contributor for any
  liability incurred by such Contributor as a result of warranty, support,
  indemnity or liability terms You offer. You may include additional
  disclaimers of warranty and limitations of liability specific to any
  jurisdiction.</p>

  <h2 id="inability-to-comply-due-to-statute-or-regulation">4. Inability to
  Comply Due to Statute or Regulation</h2>

  <p>If it is impossible for You to comply with any of the terms of this
  License with respect to some or all of the Covered Software due to statute,
  judicial order, or regulation then You must: (a) comply with the terms of
  this License to the maximum extent possible; and (b) describe the
  limitations and the code they affect. Such description must be placed in a
  text file included with all distributions of the Covered Software under
  this License. Except to the extent prohibited by statute or regulation,
  such description must be sufficiently detailed for a recipient of ordinary
  skill to be able to understand it.</p>

  <h2 id="termination">5. Termination</h2>

  <h3>5.1.</h3>

  <p>The rights granted under this License will terminate automatically
  if You fail to comply with any of its terms. However, if You become
  compliant, then the rights granted under this License from a particular
  Contributor are reinstated (a) provisionally, unless and until such
  Contributor explicitly and finally terminates Your grants, and (b) on an
  ongoing basis, if such Contributor fails to notify You of the
  non-compliance by some reasonable means prior to 60 days after You have
  come back into compliance. Moreover, Your grants from a particular
  Contributor are reinstated on an ongoing basis if such Contributor notifies
  You of the non-compliance by some reasonable means, this is the first time
  You have received notice of non-compliance with this License from such
  Contributor, and You become compliant prior to 30 days after Your receipt
  of the notice.</p>

  <h3>5.2.</h3>

  <p>If You initiate litigation against any entity by asserting a patent
  infringement claim (excluding declaratory judgment actions, counter-claims,
  and cross-claims) alleging that a Contributor Version directly or
  indirectly infringes any patent, then the rights granted to You by any and
  all Contributors for the Covered Software under Section 2.1 of this License
  shall terminate.</p>

  <h3>5.3.</h3>

  <p>In the event of termination under Sections 5.1 or 5.2 above, all
  end user license agreements (excluding distributors and resellers) which
  have been validly granted by You or Your distributors under this License
  prior to termination shall survive termination.</p>

  <h2 id="disclaimer-of-warranty">6. Disclaimer of Warranty</h2>

  <p><em>Covered Software is provided under this License on an "as is" basis,
  without warranty of any kind, either expressed, implied, or statutory,
  including, without limitation, warranties that the Covered Software is free
  of defects, merchantable, fit for a particular purpose or non-infringing.
  The entire risk as to the quality and performance of the Covered Software
  is with You. Should any Covered Software prove defective in any respect,
  You (not any Contributor) assume the cost of any necessary servicing,
  repair, or correction. This disclaimer of warranty constitutes an essential
  part of this License. No use of any Covered Software is authorized under
  this License except under this disclaimer.</em></p>

  <h2 id="limitation-of-liability">7. Limitation of Liability</h2>

  <p><em>Under no circumstances and under no legal theory, whether tort
  (including negligence), contract, or otherwise, shall any Contributor, or
  anyone who distributes Covered Software as permitted above, be liable to
  You for any direct, indirect, special, incidental, or consequential damages
  of any character including, without limitation, damages for lost profits,
  loss of goodwill, work stoppage, computer failure or malfunction, or any
  and all other commercial damages or losses, even if such party shall have
  been informed of the possibility of such damages. This limitation of
  liability shall not apply to liability for death or personal injury
  resulting from such party's negligence to the extent applicable law
  prohibits such limitation. Some jurisdictions do not allow the exclusion or
  limitation of incidental or consequential damages, so this exclusion and
  limitation may not apply to You.</em></p>

  <h2 id="litigation">8. Litigation</h2>

  <p>Any litigation relating to this License may be brought only in the
  courts of a jurisdiction where the defendant maintains its principal place
  of business and such litigation shall be governed by laws of that
  jurisdiction, without reference to its conflict-of-law provisions. Nothing
  in this Section shall prevent a party's ability to bring cross-claims or
  counter-claims.</p>

  <h2 id="miscellaneous">9. Miscellaneous</h2>

  <p>This License represents the complete agreement concerning the subject
  matter hereof. If any provision of this License is held to be
  unenforceable, such provision shall be reformed only to the extent
  necessary to make it enforceable. Any law or regulation which provides that
  the language of a contract shall be construed against the drafter shall not
  be used to construe this License against a Contributor.</p>

  <h2 id="versions-of-the-license">10. Versions of the License</h2>

  <h3 id="new-versions">10.1. New Versions</h3>

  <p>Mozilla Foundation is the license steward. Except as provided in Section
  10.3, no one other than the license steward has the right to modify or
  publish new versions of this License. Each version will be given a
  distinguishing version number.</p>

  <h3 id="effect-of-new-versions">10.2. Effect of New Versions</h3>

  <p>You may distribute the Covered Software under the terms of the version
  of the License under which You originally received the Covered Software, or
  under the terms of any subsequent version published by the license
  steward.</p>

  <h3 id="modified-versions">10.3. Modified Versions</h3>

  <p>If you create software not governed by this License, and you want to
  create a new license for such software, you may create and use a modified
  version of this License if you rename the license and remove any references
  to the name of the license steward (except to note that such modified
  license differs from this License).</p>

  <h3 id=
  "distributing-source-code-form-that-is-incompatible-with-secondary-licenses">
  10.4. Distributing Source Code Form that is Incompatible With Secondary
  Licenses</h3>

  <p>If You choose to distribute Source Code Form that is Incompatible With
  Secondary Licenses under the terms of this version of the License, the
  notice described in Exhibit B of this License must be attached.</p>

  <h2 id="exhibit-a---source-code-form-license-notice">Exhibit A - Source
  Code Form License Notice</h2>

  <blockquote>
    <p>This Source Code Form is subject to the terms of the Mozilla Public
    License, v. 2.0. If a copy of the MPL was not distributed with this file,
    You can obtain one at http://mozilla.org/MPL/2.0/.</p>
  </blockquote>

  <p>If it is not possible or desirable to put the notice in a particular
  file, then You may include the notice in a location (such as a LICENSE file
  in a relevant directory) where a recipient would be likely to look for such
  a notice.</p>

  <p>You may add additional accurate notices of copyright ownership.</p>

  <h2 id="exhibit-b---incompatible-with-secondary-licenses-notice">Exhibit B
  - "Incompatible With Secondary Licenses" Notice</h2>

  <blockquote>
    <p>This Source Code Form is "Incompatible With Secondary Licenses", as
    defined by the Mozilla Public License, v. 2.0.</p>
  </blockquote>


    <hr>

  <h1 id="lgpl">GNU Lesser General Public License 2.1</h1>

<p>This product contains code from the following LGPLed libraries:</p>

<ul>
<li><a href="http://www.surina.net/soundtouch/">libsoundtouch</a>
<li><a href="http://libav.org/">libav</a>
<li><a href="http://ffmpeg.org/">FFmpeg</a>
</ul>

<pre>
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]
</pre>

<h3><a id="SEC2">Preamble</a></h3>

<p>
  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
</p>
<p>
  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
</p>
<p>
  When we speak of free software, we are referring to freedom of use,
not price.  Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
</p>
<p>
  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
</p>
<p>
  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.
</p>
<p>
  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
</p>
<p>
  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
</p>
<p>
  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
</p>
<p>
  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
</p>
<p>
  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.
</p>
<p>
  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.
</p>
<p>
  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
</p>
<p>
  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
</p>
<p>
  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
</p>
<p>
  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
</p>

<h3><a id="SEC3">TERMS AND CONDITIONS FOR COPYING,
DISTRIBUTION AND MODIFICATION</a></h3>


<p>
<strong>0.</strong>
This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
</p>
<p>
  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
</p>
<p>
  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)
</p>
<p>
  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
</p>
<p>
  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
</p>
<p>
<strong>1.</strong>
You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
</p>
<p>
  You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
</p>
<p>
<strong>2.</strong>
You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
</p>

<ul>
  <li><strong>a)</strong>
       The modified work must itself be a software library.</li>
  <li><strong>b)</strong>
       You must cause the files modified to carry prominent notices
       stating that you changed the files and the date of any change.</li>

  <li><strong>c)</strong>
       You must cause the whole of the work to be licensed at no
       charge to all third parties under the terms of this License.</li>

  <li><strong>d)</strong>
       If a facility in the modified Library refers to a function or a
       table of data to be supplied by an application program that uses
       the facility, other than as an argument passed when the facility
       is invoked, then you must make a good faith effort to ensure that,
       in the event an application does not supply such function or
       table, the facility still operates, and performs whatever part of
       its purpose remains meaningful.
       <p>
       (For example, a function in a library to compute square roots has
       a purpose that is entirely well-defined independent of the
       application.  Therefore, Subsection 2d requires that any
       application-supplied function or table used by this function must
       be optional: if the application does not supply it, the square
       root function must still compute square roots.)</p></li>
</ul>

<p>
These requirements apply to the modified work as a whole.  If identifiable
sections of that work are not derived from the Library, and can be
reasonably considered independent and separate works in themselves, then
this License, and its terms, do not apply to those sections when you
distribute them as separate works.  But when you distribute the same
sections as part of a whole which is a work based on the Library, the
distribution of the whole must be on the terms of this License, whose
permissions for other licensees extend to the entire whole, and thus to
each and every part regardless of who wrote it.
</p>
<p>
Thus, it is not the intent of this section to claim rights or contest your
rights to work written entirely by you; rather, the intent is to exercise
the right to control the distribution of derivative or collective works
based on the Library.
</p>
<p>
In addition, mere aggregation of another work not based on the Library with
the Library (or with a work based on the Library) on a volume of a storage
or distribution medium does not bring the other work under the scope of
this License.
</p>
<p>
<strong>3.</strong>
You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.
</p>
<p>
  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
</p>
<p>
  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
</p>
<p>
<strong>4.</strong>
You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
</p>
<p>
  If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
</p>
<p>
<strong>5.</strong>
A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
</p>
<p>
  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
</p>
<p>
  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.
</p>
<p>
  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
</p>
<p>
  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
</p>
<p>
<strong>6.</strong>
As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
</p>
<p>
  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:
</p>

<ul>
    <li><strong>a)</strong> Accompany the work with the complete
    corresponding machine-readable source code for the Library
    including whatever changes were used in the work (which must be
    distributed under Sections 1 and 2 above); and, if the work is an
    executable linked with the Library, with the complete
    machine-readable "work that uses the Library", as object code
    and/or source code, so that the user can modify the Library and
    then relink to produce a modified executable containing the
    modified Library.  (It is understood that the user who changes the
    contents of definitions files in the Library will not necessarily
    be able to recompile the application to use the modified
    definitions.)</li>

    <li><strong>b)</strong> Use a suitable shared library mechanism
    for linking with the Library.  A suitable mechanism is one that
    (1) uses at run time a copy of the library already present on the
    user's computer system, rather than copying library functions into
    the executable, and (2) will operate properly with a modified
    version of the library, if the user installs one, as long as the
    modified version is interface-compatible with the version that the
    work was made with.</li>

    <li><strong>c)</strong> Accompany the work with a written offer,
    valid for at least three years, to give the same user the
    materials specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.</li>

    <li><strong>d)</strong> If distribution of the work is made by
    offering access to copy from a designated place, offer equivalent
    access to copy the above specified materials from the same
    place.</li>

    <li><strong>e)</strong> Verify that the user has already received
    a copy of these materials or that you have already sent this user
    a copy.</li>
</ul>

<p>
  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
</p>
<p>
  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
</p>
<p>
<strong>7.</strong> You may place library facilities that are a work
based on the Library side-by-side in a single library together with
other library facilities not covered by this License, and distribute
such a combined library, provided that the separate distribution of
the work based on the Library and of the other library facilities is
otherwise permitted, and provided that you do these two things:
</p>

<ul>
    <li><strong>a)</strong> Accompany the combined library with a copy
    of the same work based on the Library, uncombined with any other
    library facilities.  This must be distributed under the terms of
    the Sections above.</li>

    <li><strong>b)</strong> Give prominent notice with the combined
    library of the fact that part of it is a work based on the
    Library, and explaining where to find the accompanying uncombined
    form of the same work.</li>
</ul>

<p>
<strong>8.</strong> You may not copy, modify, sublicense, link with,
or distribute the Library except as expressly provided under this
License.  Any attempt otherwise to copy, modify, sublicense, link
with, or distribute the Library is void, and will automatically
terminate your rights under this License.  However, parties who have
received copies, or rights, from you under this License will not have
their licenses terminated so long as such parties remain in full
compliance.
</p>
<p>
<strong>9.</strong>
You are not required to accept this License, since you have not
signed it.  However, nothing else grants you permission to modify or
distribute the Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
</p>
<p>
<strong>10.</strong>
Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions.  You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
</p>
<p>
<strong>11.</strong>
If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
</p>
<p>
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
</p>
<p>
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices.  Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
</p>
<p>
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
</p>
<p>
<strong>12.</strong>
If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded.  In such case, this License incorporates the limitation as if
written in the body of this License.
</p>
<p>
<strong>13.</strong>
The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
</p>
<p>
Each version is given a distinguishing version number.  If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation.  If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
</p>
<p>
<strong>14.</strong>
If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission.  For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this.  Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
</p>
<p>
<strong>NO WARRANTY</strong>
</p>
<p>
<strong>15.</strong>
BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
</p>
<p>
<strong>16.</strong>
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
</p>

    <hr>

  <h1 id="lgpl-3.0">GNU Lesser General Public License 3.0</h1>

<p>Some versions of this product contains code from the following LGPLed libraries:</p>

<ul>
<li><a
href="https://addons.mozilla.org/en-US/firefox/addon/görans-hemmasnickrade-ordli/">Swedish dictionary</a>
</ul>

<pre>Copyright &copy; 2007 Free Software Foundation, Inc.
 &lt;<a href="http://fsf.org/">http://fsf.org/</a>&gt;

Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.</pre>

<p>This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.</p>

<h3><a id="section0">0. Additional Definitions</a></h3>

<p>As used herein, &ldquo;this License&rdquo; refers to version 3 of the GNU Lesser
General Public License, and the &ldquo;GNU GPL&rdquo; refers to version 3 of the GNU
General Public License.</p>

<p>&ldquo;The Library&rdquo; refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.</p>

<p>An &ldquo;Application&rdquo; is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.</p>

<p>A &ldquo;Combined Work&rdquo; is a work produced by combining or linking an
Application with the Library.  The particular version of the Library
with which the Combined Work was made is also called the &ldquo;Linked
Version&rdquo;.</p>

<p>The &ldquo;Minimal Corresponding Source&rdquo; for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.</p>

<p>The &ldquo;Corresponding Application Code&rdquo; for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.</p>

<h3><a id="section1">1. Exception to Section 3 of the GNU GPL.</a></h3>

<p>You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.</p>

<h3><a id="section2">2. Conveying Modified Versions.</a></h3>

<p>If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:</p>

<ul>
<li>a) under this License, provided that you make a good faith effort to
   ensure that, in the event an Application does not supply the
   function or data, the facility still operates, and performs
   whatever part of its purpose remains meaningful, or</li>

<li>b) under the GNU GPL, with none of the additional permissions of
   this License applicable to that copy.</li>
</ul>

<h3><a id="section3">3. Object Code Incorporating Material from Library Header Files.</a></h3>

<p>The object code form of an Application may incorporate material from
a header file that is part of the Library.  You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:</p>

<ul>
<li>a) Give prominent notice with each copy of the object code that the
   Library is used in it and that the Library and its use are
   covered by this License.</li>

<li>b) Accompany the object code with a copy of the GNU GPL and this license
   document.</li>
</ul>

<h3><a id="section4">4. Combined Works.</a></h3>

<p>You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:</p>

<ul>
<li>a) Give prominent notice with each copy of the Combined Work that
   the Library is used in it and that the Library and its use are
   covered by this License.</li>

<li>b) Accompany the Combined Work with a copy of the GNU GPL and this license
   document.</li>

<li>c) For a Combined Work that displays copyright notices during
   execution, include the copyright notice for the Library among
   these notices, as well as a reference directing the user to the
   copies of the GNU GPL and this license document.</li>

<li>d) Do one of the following:

<ul>
<li>0) Convey the Minimal Corresponding Source under the terms of this
       License, and the Corresponding Application Code in a form
       suitable for, and under terms that permit, the user to
       recombine or relink the Application with a modified version of
       the Linked Version to produce a modified Combined Work, in the
       manner specified by section 6 of the GNU GPL for conveying
       Corresponding Source.</li>

<li>1) Use a suitable shared library mechanism for linking with the
       Library.  A suitable mechanism is one that (a) uses at run time
       a copy of the Library already present on the user's computer
       system, and (b) will operate properly with a modified version
       of the Library that is interface-compatible with the Linked
       Version.</li>
</ul></li>

<li>e) Provide Installation Information, but only if you would otherwise
   be required to provide such information under section 6 of the
   GNU GPL, and only to the extent that such information is
   necessary to install and execute a modified version of the
   Combined Work produced by recombining or relinking the
   Application with a modified version of the Linked Version. (If
   you use option 4d0, the Installation Information must accompany
   the Minimal Corresponding Source and Corresponding Application
   Code. If you use option 4d1, you must provide the Installation
   Information in the manner specified by section 6 of the GNU GPL
   for conveying Corresponding Source.)</li>
</ul>

<h3><a id="section5">5. Combined Libraries.</a></h3>

<p>You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:</p>

<ul>
<li>a) Accompany the combined library with a copy of the same work based
   on the Library, uncombined with any other library facilities,
   conveyed under the terms of this License.</li>

<li>b) Give prominent notice with the combined library that part of it
   is a work based on the Library, and explaining where to find the
   accompanying uncombined form of the same work.</li>
</ul>

<h3><a id="section6">6. Revised Versions of the GNU Lesser General Public License.</a></h3>

<p>The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.</p>

<p>Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License &ldquo;or any later version&rdquo;
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.</p>

<p>If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.</p>


    <hr>


    <h1 id="gpl-3.0">GNU General Public License 3.0</h1>

    <p>This license does not apply to any of the code shipped with
    Firefox, but may apply to blocklists downloaded after installation
    for use with the tracking protection feature. Firefox and such
    blocklists are separate and independent works as described in
    Sections 5 and 6 of this license. Our blocklist is based on one
    originally written by Disconnect.me.</p>

<pre>Version 3, 29 June 2007

Copyright &copy; 2007 Free Software Foundation, Inc.
&lt;<a href="http://fsf.org/">http://fsf.org/</a>&gt;

Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.</pre>

<h2><a name="preamble"></a>Preamble</h2>

<p>The GNU General Public License is a free, copyleft license for
software and other kinds of works.</p>

<p>The licenses for most software and other practical works are designed
to take away your freedom to share and change the works.  By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.  We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors.  You can apply it to
your programs, too.</p>

<p>When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.</p>

<p>To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights.  Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.</p>

<p>For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received.  You must make sure that they, too, receive
or can get the source code.  And you must show them these terms so they
know their rights.</p>

<p>Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.</p>

<p>For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software.  For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.</p>

<p>Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so.  This is fundamentally incompatible with the aim of
protecting users' freedom to change the software.  The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable.  Therefore, we
have designed this version of the GPL to prohibit the practice for those
products.  If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.</p>

<p>Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary.  To prevent this, the GPL assures that
patents cannot be used to render the program non-free.</p>

<p>The precise terms and conditions for copying, distribution and
modification follow.</p>

<h2><a name="terms"></a>TERMS AND CONDITIONS</h2>

<h3><a name="section0"></a>0. Definitions.</h3>

<p>&ldquo;This License&rdquo; refers to version 3 of the GNU General Public License.</p>

<p>&ldquo;Copyright&rdquo; also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.</p>

<p>&ldquo;The Program&rdquo; refers to any copyrightable work licensed under this
License.  Each licensee is addressed as &ldquo;you&rdquo;.  &ldquo;Licensees&rdquo; and
&ldquo;recipients&rdquo; may be individuals or organizations.</p>

<p>To &ldquo;modify&rdquo; a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy.  The resulting work is called a &ldquo;modified version&rdquo; of the
earlier work or a work &ldquo;based on&rdquo; the earlier work.</p>

<p>A &ldquo;covered work&rdquo; means either the unmodified Program or a work based
on the Program.</p>

<p>To &ldquo;propagate&rdquo; a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy.  Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.</p>

<p>To &ldquo;convey&rdquo; a work means any kind of propagation that enables other
parties to make or receive copies.  Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.</p>

<p>An interactive user interface displays &ldquo;Appropriate Legal Notices&rdquo;
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License.  If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.</p>

<h3><a name="section1"></a>1. Source Code.</h3>

<p>The &ldquo;source code&rdquo; for a work means the preferred form of the work
for making modifications to it.  &ldquo;Object code&rdquo; means any non-source
form of a work.</p>

<p>A &ldquo;Standard Interface&rdquo; means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.</p>

<p>The &ldquo;System Libraries&rdquo; of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form.  A
&ldquo;Major Component&rdquo;, in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.</p>

<p>The &ldquo;Corresponding Source&rdquo; for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities.  However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work.  For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.</p>

<p>The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.</p>

<p>The Corresponding Source for a work in source code form is that
same work.</p>

<h3><a name="section2"></a>2. Basic Permissions.</h3>

<p>All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met.  This License explicitly affirms your unlimited
permission to run the unmodified Program.  The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work.  This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.</p>

<p>You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force.  You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright.  Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.</p>

<p>Conveying under any other circumstances is permitted solely under
the conditions stated below.  Sublicensing is not allowed; section 10
makes it unnecessary.</p>

<h3><a name="section3"></a>3. Protecting Users' Legal Rights From Anti-Circumvention Law.</h3>

<p>No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.</p>

<p>When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.</p>

<h3><a name="section4"></a>4. Conveying Verbatim Copies.</h3>

<p>You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.</p>

<p>You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.</p>

<h3><a name="section5"></a>5. Conveying Modified Source Versions.</h3>

<p>You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:</p>

<ul>
<li>a) The work must carry prominent notices stating that you modified
    it, and giving a relevant date.</li>

<li>b) The work must carry prominent notices stating that it is
    released under this License and any conditions added under section
    7.  This requirement modifies the requirement in section 4 to
    &ldquo;keep intact all notices&rdquo;.</li>

<li>c) You must license the entire work, as a whole, under this
    License to anyone who comes into possession of a copy.  This
    License will therefore apply, along with any applicable section 7
    additional terms, to the whole of the work, and all its parts,
    regardless of how they are packaged.  This License gives no
    permission to license the work in any other way, but it does not
    invalidate such permission if you have separately received it.</li>

<li>d) If the work has interactive user interfaces, each must display
    Appropriate Legal Notices; however, if the Program has interactive
    interfaces that do not display Appropriate Legal Notices, your
    work need not make them do so.</li>
</ul>

<p>A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
&ldquo;aggregate&rdquo; if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit.  Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.</p>

<h3><a name="section6"></a>6. Conveying Non-Source Forms.</h3>

<p>You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:</p>

<ul>
<li>a) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by the
    Corresponding Source fixed on a durable physical medium
    customarily used for software interchange.</li>

<li>b) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by a
    written offer, valid for at least three years and valid for as
    long as you offer spare parts or customer support for that product
    model, to give anyone who possesses the object code either (1) a
    copy of the Corresponding Source for all the software in the
    product that is covered by this License, on a durable physical
    medium customarily used for software interchange, for a price no
    more than your reasonable cost of physically performing this
    conveying of source, or (2) access to copy the
    Corresponding Source from a network server at no charge.</li>

<li>c) Convey individual copies of the object code with a copy of the
    written offer to provide the Corresponding Source.  This
    alternative is allowed only occasionally and noncommercially, and
    only if you received the object code with such an offer, in accord
    with subsection 6b.</li>

<li>d) Convey the object code by offering access from a designated
    place (gratis or for a charge), and offer equivalent access to the
    Corresponding Source in the same way through the same place at no
    further charge.  You need not require recipients to copy the
    Corresponding Source along with the object code.  If the place to
    copy the object code is a network server, the Corresponding Source
    may be on a different server (operated by you or a third party)
    that supports equivalent copying facilities, provided you maintain
    clear directions next to the object code saying where to find the
    Corresponding Source.  Regardless of what server hosts the
    Corresponding Source, you remain obligated to ensure that it is
    available for as long as needed to satisfy these requirements.</li>

<li>e) Convey the object code using peer-to-peer transmission, provided
    you inform other peers where the object code and Corresponding
    Source of the work are being offered to the general public at no
    charge under subsection 6d.</li>
</ul>

<p>A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.</p>

<p>A &ldquo;User Product&rdquo; is either (1) a &ldquo;consumer product&rdquo;, which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling.  In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage.  For a particular
product received by a particular user, &ldquo;normally used&rdquo; refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product.  A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.</p>

<p>&ldquo;Installation Information&rdquo; for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source.  The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.</p>

<p>If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information.  But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).</p>

<p>The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed.  Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.</p>

<p>Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.</p>

<h3><a name="section7"></a>7. Additional Terms.</h3>

<p>&ldquo;Additional permissions&rdquo; are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law.  If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.</p>

<p>When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it.  (Additional permissions may be written to require their own
removal in certain cases when you modify the work.)  You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.</p>

<p>Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:</p>

<ul>
<li>a) Disclaiming warranty or limiting liability differently from the
    terms of sections 15 and 16 of this License; or</li>

<li>b) Requiring preservation of specified reasonable legal notices or
    author attributions in that material or in the Appropriate Legal
    Notices displayed by works containing it; or</li>

<li>c) Prohibiting misrepresentation of the origin of that material, or
    requiring that modified versions of such material be marked in
    reasonable ways as different from the original version; or</li>

<li>d) Limiting the use for publicity purposes of names of licensors or
    authors of the material; or</li>

<li>e) Declining to grant rights under trademark law for use of some
    trade names, trademarks, or service marks; or</li>

<li>f) Requiring indemnification of licensors and authors of that
    material by anyone who conveys the material (or modified versions of
    it) with contractual assumptions of liability to the recipient, for
    any liability that these contractual assumptions directly impose on
    those licensors and authors.</li>
</ul>

<p>All other non-permissive additional terms are considered &ldquo;further
restrictions&rdquo; within the meaning of section 10.  If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term.  If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.</p>

<p>If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.</p>

<p>Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.</p>

<h3><a name="section8"></a>8. Termination.</h3>

<p>You may not propagate or modify a covered work except as expressly
provided under this License.  Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).</p>

<p>However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.</p>

<p>Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.</p>

<p>Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License.  If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.</p>

<h3><a name="section9"></a>9. Acceptance Not Required for Having Copies.</h3>

<p>You are not required to accept this License in order to receive or
run a copy of the Program.  Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance.  However,
nothing other than this License grants you permission to propagate or
modify any covered work.  These actions infringe copyright if you do
not accept this License.  Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.</p>

<h3><a name="section10"></a>10. Automatic Licensing of Downstream Recipients.</h3>

<p>Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License.  You are not responsible
for enforcing compliance by third parties with this License.</p>

<p>An &ldquo;entity transaction&rdquo; is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations.  If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.</p>

<p>You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License.  For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.</p>

<h3><a name="section11"></a>11. Patents.</h3>

<p>A &ldquo;contributor&rdquo; is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based.  The
work thus licensed is called the contributor's &ldquo;contributor version&rdquo;.</p>

<p>A contributor's &ldquo;essential patent claims&rdquo; are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version.  For
purposes of this definition, &ldquo;control&rdquo; includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.</p>

<p>Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.</p>

<p>In the following three paragraphs, a &ldquo;patent license&rdquo; is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement).  To &ldquo;grant&rdquo; such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.</p>

<p>If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients.  &ldquo;Knowingly relying&rdquo; means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.</p>

<p>If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.</p>

<p>A patent license is &ldquo;discriminatory&rdquo; if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License.  You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.</p>

<p>Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.</p>

<h3><a name="section12"></a>12. No Surrender of Others' Freedom.</h3>

<p>If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all.  For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.</p>

<h3><a name="section13"></a>13. Use with the GNU Affero General Public License.</h3>

<p>Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work.  The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.</p>

<h3><a name="section14"></a>14. Revised Versions of this License.</h3>

<p>The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.</p>

<p>Each version is given a distinguishing version number.  If the
Program specifies that a certain numbered version of the GNU General
Public License &ldquo;or any later version&rdquo; applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation.  If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.</p>

<p>If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.</p>

<p>Later license versions may give you additional or different
permissions.  However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.</p>

<h3><a name="section15"></a>15. Disclaimer of Warranty.</h3>

<p>THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM &ldquo;AS IS&rdquo; WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.</p>

<h3><a name="section16"></a>16. Limitation of Liability.</h3>

<p>IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.</p>

<h3><a name="section17"></a>17. Interpretation of Sections 15 and 16.</h3>

<p>If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.</p>

<p>END OF TERMS AND CONDITIONS</p>

<h2><a name="howto"></a>How to Apply These Terms to Your New Programs</h2>

<p>If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.</p>

<p>To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the &ldquo;copyright&rdquo; line and a pointer to where the full notice is found.</p>

<pre>    &lt;one line to give the program's name and a brief idea of what it does.&gt;
    Copyright (C) &lt;year&gt;  &lt;name of author&gt;

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see &lt;http://www.gnu.org/licenses/&gt;.
</pre>

<p>Also add information on how to contact you by electronic and paper mail.</p>

<p>If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:</p>

<pre>    &lt;program&gt;  Copyright (C) &lt;year&gt;  &lt;name of author&gt;
    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.
</pre>

<p>The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, your program's commands
might be different; for a GUI interface, you would use an &ldquo;about box&rdquo;.</p>

<p>You should also get your employer (if you work as a programmer) or school,
if any, to sign a &ldquo;copyright disclaimer&rdquo; for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
&lt;<a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>&gt;.</p>

<p>The GNU General Public License does not permit incorporating your program
into proprietary programs.  If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library.  If this is what you want to do, use the GNU Lesser General
Public License instead of this License.  But first, please read
&lt;<a href="http://www.gnu.org/philosophy/why-not-lgpl.html">http://www.gnu.org/philosophy/why-not-lgpl.html</a>&gt;.</p>

<hr>

    <h1><a id="ACE"></a>ACE License</h1>

    <p>This license applies to the file
    <code>media/webrtc/trunk/webrtc/system_wrappers/source/condition_variable_event_win.cc</code>.</p>

<pre>
ACE(TM), TAO(TM), CIAO(TM), DAnCE(TM), and CoSMIC(TM)
(henceforth referred to as "DOC software") are copyrighted by
Douglas C. Schmidt and his research group at Washington University,
University of California, Irvine, and Vanderbilt University,
Copyright (c) 1993-2009, all rights reserved.
Since DOC software is open-source, freely available software,
you are free to use, modify, copy, and distribute--perpetually and
irrevocably--the DOC software source code and object code produced
from the source, as well as copy and distribute modified versions of
this software. You must, however, include this copyright statement
along with any code built using DOC software that you release. No
copyright statement needs to be provided if you just ship binary
executables of your software products.
</pre>


    <hr>

    <h1><a id="adobecmap"></a>Adobe CMap License</h1>

    <p>This license applies to files in the directory
    <code>browser/extensions/pdfjs/content/web/cmaps/</code>.</p>

<pre>
Copyright 1990-2009 Adobe Systems Incorporated.
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 Adobe Systems Incorporated 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.
</pre>


    <hr>

    <h1><a id="android"></a>Android Open Source License</h1>

    <p>This license applies to various files in the Mozilla codebase,
    including those in the directory <code>gfx/skia/</code>.</p>
<!-- This is the wrong directory, what was intended? -->

<pre>
 Copyright 2009, The Android Open Source Project

 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.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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.
</pre>


    <hr>

    <h1><a id="angle"></a>ANGLE License</h1>

    <p>This license applies to files in the directory <code>gfx/angle/</code>.</p>

<pre>
Copyright (C) 2002-2010 The ANGLE Project 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 TransGaming Inc., Google Inc., 3DLabs Inc.
    Ltd., nor the names of their 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.
</pre>


    <hr>

    <h1><a id="agg"></a>Anti-Grain Geometry Public License</h1>

    <p>This license applies to files in the directory
    <span class="path">modules/pdfium/pdfium/third_party/agg23/</span>.</p>

<pre>
Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)

Permission to copy, use, modify, sell and distribute this software
is granted provided this copyright notice appears in all copies.
This software is provided "as is" without express or implied
warranty, and with no claim as to its suitability for any purpose.
</pre>


    <hr>

    <h1><a id="acorn"></a>acorn License</h1>

    <p>This license applies to all files in
      <code>devtools/shared/acorn</code>.
    </p>
<pre>
Copyright (C) 2012 by Marijn Haverbeke &lt;[email protected]&gt;

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.
</pre>


    <hr>

    <h1><a id="apache"></a>Apache License 2.0</h1>

    <p>This license applies to various files in the Mozilla codebase.</p>

<pre>
                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS
</pre>



    <hr>

    <h1><a id="apple"></a>Apple License</h1>

    <p>This license applies to certain files in the directories <code>dom/media/webaudio/blink</code>, and <code>widget/cocoa</code>.</p>

<pre>
Copyright (C) 2008, 2009 Apple 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:
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.

THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. 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.
</pre>


    <hr>

    <h1><a id="apple-mozilla"></a>Apple/Mozilla NPRuntime License</h1>

    <p>This license applies to the file
    <code>dom/plugins/base/npruntime.h</code>.</p>

<pre>
Copyright &copy; 2004, Apple Computer, Inc. and The 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 names of Apple Computer, Inc. ("Apple") or The Mozilla
Foundation ("Mozilla") nor the names of their contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.

THIS SOFTWARE IS PROVIDED BY APPLE, MOZILLA AND THEIR 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, MOZILLA OR
THEIR 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.
</pre>


    <hr>

    <h1><a id="arm"></a>ARM License</h1>

    <p>This license applies to files in the directory <code>js/src/jit/arm64/vixl/</code>.</p>

<pre>
Copyright 2013, ARM Limited
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 ARM Limited 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 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.
</pre>


    <hr>

    <h1><a id="babel"></a>Babel License</h1>

    <p>This license applies to this file in the directory
      <code>devtools/client/debugger/new/debugger.js/</code>.
    </p>

<pre>
Copyright (c) 2014-2017 Sebastian McKenzie <[email protected]>

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.
</pre>

    </hr>

    <h1><a id="babylon"></a>Babylon License</h1>

    <p>This license applies to this file in the directory
      <code>devtools/client/debugger/new/debugger.js/</code>.
    </p>

<pre>
Copyright (C) 2012-2014 by various contributors (see AUTHORS)

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.
</pre>


    <hr>

    <h1><a id="bincode"></a>bincode License</h1>

    <p>This license applies to files in the directory
    <code>third_party/rust/bincode</code>.</p>

<pre>
The MIT License (MIT)

Copyright (c) 2014 Ty Overby

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.
</pre>


    <hr>

    <h1><a id="bspatch"></a>bspatch License</h1>

    <p>This license applies to the files
    <code>toolkit/mozapps/update/updater/bspatch.cpp</code> and
    <code>toolkit/mozapps/update/updater/bspatch.h</code>.
    </p>

<pre>
Copyright 2003,2004 Colin Percival
All rights reserved

Redistribution and use in source and binary forms, with or without
modification, are permitted providing 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.

THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
</pre>


    <hr>

    <h1><a id="byteorder"></a>byteorder License</h1>

    <p>This license applies to files in the directory
    <code>third_party/rust/byteorder</code>.</p>

<pre>
The MIT License (MIT)

Copyright (c) 2015 Andrew Gallant

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.
</pre>


    <hr>

    <h1><a id="cairo"></a>Cairo Component Licenses</h1>

    <p>This license, with different copyright holders, applies to certain files
     in the directory <code>gfx/cairo/</code>. The copyright
     holders and the applicable ranges of dates are as follows:

    <ul>
<li>2004 Richard D. Worth
<li>2004, 2005 Red Hat, Inc.
<li>2003 USC, Information Sciences Institute
<li>2004 David Reveman
<li>2005 Novell, Inc.
<li>2004 David Reveman, Peter Nilsson
<li>2000 Keith Packard, member of The XFree86 Project, Inc.
<li>2005 Lars Knoll &amp; Zack Rusin, Trolltech
<li>1998, 2000, 2002, 2004 Keith Packard
<li>2004 Nicholas Miell
<li>2005 Trolltech AS
<li>2000 SuSE, Inc.
<li>2003 Carl Worth
<li>1987, 1988, 1989, 1998 The Open Group
<li>1987, 1988, 1989 Digital Equipment Corporation, Maynard, Massachusetts.
<li>1998 Keith Packard
<li>2003 Richard Henderson
    </ul>

<pre>
Copyright &copy; &lt;date&gt; &lt;copyright holder&gt;

Permission to use, copy, modify, distribute, and sell this software
and its documentation for any purpose is hereby granted without
fee, provided that the above copyright notice appear in all copies
and that both that copyright notice and this permission notice
appear in supporting documentation, and that the name of
&lt;copyright holder&gt; not be used in advertising or publicity pertaining to
distribution of the software without specific, written prior permission.
&lt;copyright holder&gt; makes no representations about the suitability of this
software for any purpose. It is provided "as is" without express or
implied warranty.

&lt;COPYRIGHT HOLDER&gt; DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
NO EVENT SHALL &lt;COPYRIGHT HOLDER&gt; BE LIABLE FOR ANY SPECIAL, INDIRECT OR
CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
</pre>


    <hr>

    <h1><a id="chromium"></a>Chromium License</h1>

    <p>This license applies to parts of the code in:</p>
    <ul>
        <li><code>browser/extensions/mortar/host/common/opengles2-utils.jsm</code></li>
        <li><code>editor/libeditor/EditorEventListener.cpp</code></li>
        <li><code>security/sandbox/</code></li>
        <li><code>widget/cocoa/GfxInfo.mm</code></li>
    </ul>
    <p>and also some files in these directories:</p>
    <ul>
        <li><code>browser/extensions/mortar/ppapi/</code></li>
        <li><code>dom/media/webspeech/recognition/</code></li>
        <li><code>dom/plugins/</code></li>
        <li><code>gfx/ots/</code></li>
        <li><code>gfx/ycbcr/</code></li>
        <li><code>ipc/chromium/</code></li>
        <li><code>modules/pdfium/pdfium/third_party/base/</code></li>
        <li><code>media/openmax_dl/</code></li>
        <li><code>toolkit/components/downloads/chromium/</code></li>
        <li><code>toolkit/components/url-classifier/chromium/</code></li>
        <li><code>tools/profiler/</code></li>
    </ul>

<pre>
Copyright (c) 2006-2016 The Chromium 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.
</pre>


    <hr>

    <h1><a id="codemirror"></a>CodeMirror License</h1>

    <p>This license applies to all files in
      <code>devtools/client/sourceeditor/codemirror</code> and
      to specified files in the <code>devtools/client/sourceeditor/test/</code>:
    </p>
    <ul>
      <li><code>cm_comment_test.js</code></li>
      <li><code>cm_driver.js</code></li>
      <li><code>cm_mode_javascript_test.js</code></li>
      <li><code>cm_mode_test.css</code></li>
      <li><code>cm_mode_test.js</code></li>
      <li><code>cm_test.js</code></li>
    </ul>
<pre>
Copyright (C) 2013 by Marijn Haverbeke <[email protected]>

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.
</pre>


    <hr>

    <h1><a id="cubic-bezier"></a>cubic-bezier License</h1>

    <p>This license applies to the file
    <code>devtools/client/shared/widgets/CubicBezierWidget.js
    </code>.</p>
<pre>
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.
</pre>


    <hr>

    <h1><a id="d3"></a>D3 License</h1>

    <p>This license applies to the file
      <code>devtools/client/shared/d3.js</code>.
    </p>
<pre>
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.
</pre>


    <hr>

    <h1><a id="dagre-d3"></a>Dagre-D3 License</h1>

    <p>This license applies to the file
      <code>devtools/client/webaudioeditor/lib/dagre-d3.js</code>.
    </p>
<pre>
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.
</pre>


<hr>

<h1><a id="diff"></a>diff License</h1>

<p>This license applies to the file
<code>devtools/client/inspector/markup/test/helper_diff.js</code>.</p>

<pre>
Copyright (c) 2014 Slava

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.
</pre>


<hr>

<h1><a id="dtoa"></a>dtoa License</h1>

<p>This license applies to the file
<code>nsprpub/pr/src/misc/dtoa.c</code>.</p>

<pre>
The author of this software is David M. Gay.

Copyright (c) 1991, 2000, 2001 by Lucent Technologies.

Permission to use, copy, modify, and distribute this software for any
purpose without fee is hereby granted, provided that this entire notice
is included in all copies of any software which is or includes a copy
or modification of this software and in all copies of the supporting
documentation for such software.

THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
WARRANTY.  IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY
REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
</pre>

    <hr>

    <h1><a id="fuzz-aldrin"></a>fuzz-aldrin License</h1>

    <p>This license applies to some of the code in
    <code>devtools/client/debugger/new/debugger.js</code>.</p>

<pre>
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.

</pre>


    <hr>

    <h1><a id="hunspell-nl"></a>Dutch Spellchecking Dictionary License</h1>

    <p>This license applies to the Dutch Spellchecking Dictionary. (This
      code only ships in some localized versions of this product.)</p>

<pre>
Copyright (c) 2006, 2007 OpenTaal
Copyright (c) 2001, 2002, 2003, 2005 Simon Brouwer e.a.
Copyright (c) 1996 Nederlandstalige Tex Gebruikersgroep

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 the OpenTaal, Simon Brouwer e.a., or Nederlandstalige Tex
Gebruikersgroep 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.
</pre>


    <hr>

    <h1><a id="emojione"></a>EmojiOne License</h1>

    <p>This license applies to the emoji art contained within the bundled
emoji font file.</p>

<pre>
Copyright (c) 2016 Ranks.com Inc.
Copyright (c) 2014 Twitter, Inc and other contributors.

Creative Commons Attribution 4.0 International (CC BY 4.0)

See https://creativecommons.org/licenses/by/4.0/legalcode or
for the human readable summary: https://creativecommons.org/licenses/by/4.0/

You are free to:

Share — copy and redistribute the material in any medium or format

Adapt — remix, transform, and build upon the material for any purpose, even commercially.

The licensor cannot revoke these freedoms as long as you follow the license terms.

Under the following terms:

Attribution — You must give appropriate credit, provide a link to the license,
and indicate if changes were made. You may do so in any reasonable manner,
but not in any way that suggests the licensor endorses you or your use.

No additional restrictions — You may not apply legal terms or technological
measures that legally restrict others from doing anything the license permits.

Notices:

You do not have to comply with the license for elements of the material in
the public domain or where your use is permitted by an applicable exception or
limitation. No warranties are given. The license may not give you all of the
permissions necessary for your intended use. For example, other rights such as
publicity, privacy, or moral rights may limit how you use the material.
</pre>


    <hr>

    <h1><a id="hunspell-ee"></a>Estonian Spellchecking Dictionary License</h1>

    <p>This license applies to precursor works to certain files which are
      part of the Estonian Spellchecking Dictionary. The
      shipped versions are under the GNU Lesser General Public License. (This
      code only ships in some localized versions of this product.)</p>

<pre>
Copyright © Institute of the Estonian Language

E-mail: [email protected]
URL: http://www.eki.ee/tarkvara/

The present Licence Agreement gives the user of this Software Product
(hereinafter: Product) the right to use the Product for whatever purpose
(incl. distribution, copying, altering, inclusion in other software, and
selling) on the following conditions:

1. The present Licence Agreement should belong unaltered to each copy ever
   made of this Product;
2. Neither the Institute of the Estonian Language (hereinafter: IEL) nor the
   author(s) of the Product will take responsibility for any detriment, direct
   or indirect, possibly ensuing from the application of the Product;
3. The IEL is ready to share the Product with other users as we wish to
   advance research on the Estonian language and to promote the use of
   Estonian in rapidly developing infotechnology, yet we refuse to bind
   ourselves to any further obligation, which means that the IEL is not
   obliged either to warrant the suitability of the Product for a specific
   purpose, to improve the software, or to provide a more detailed description
   of the underlying algorithms. (Which does not mean, though, that we may not
   do it.)

Notification Request:

As a courtesy, we would appreciate being informed whenever our linguistic
products are used to create derivative works. If you modify our software or
include it in other products, please inform us by sending e-mail to
[email protected] or by letter to

Institute of the Estonian Language
Roosikrantsi 6
10119 Tallinn
ESTONIA

Phone &amp; Fax: +372 6411443
</pre>



    <hr>

    <h1><a id="expat"></a>Expat License</h1>

    <p>This license applies to certain files in the directory
    <code>parser/expat/</code>.</p>

<pre>
Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd
                               and Clark Cooper
Copyright (c) 2001, 2002, 2003 Expat maintainers.

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.
</pre>



    <hr>


    <h1><a id="firebug"></a>Firebug License</h1>

    <p>This license applies to the code
    <code>devtools/shared/webconsole/network-helper.js</code>.</p>

<pre>
Copyright (c) 2007, Parakey Inc.
All rights reserved.

Redistribution and use of this software 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 Parakey Inc. nor the names of its
  contributors may be used to endorse or promote products
  derived from this software without specific prior
  written permission of Parakey Inc.

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.
</pre>


    <hr>

    <h1><a id="gdi32-sys"></a>gdi32-sys License</h1>

    <p>This license applies to files in the directory
    <code>third_party/rust/gdi32-sys</code>.</p>

<pre>
The MIT License (MIT)

Copyright (c) 2015 Peter Atashian

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.
</pre>


    <hr>

    <h1><a id="gfx-font-list"></a>gfxFontList License</h1>

    <p>This license applies to the files
    <code>gfx/thebes/gfxMacPlatformFontList.mm</code> and
    <code>gfx/thebes/gfxPlatformFontList.cpp</code>.
    </p>

<pre>
Copyright (C) 2006 Apple Computer, 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:

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 APPLE AND ITS 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.
</pre>



    <hr>

    <h1><a id="google-bsd"></a>Google BSD License</h1>

    <p>This license applies to files in the directories
    <code>toolkit/crashreporter/google-breakpad/</code>,
    <code>toolkit/components/protobuf/</code> and
    <code>devtools/client/netmonitor/src/utils/filter-text-utils.js.</code></p>

<pre>
Copyright (c) 2006, 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.
</pre>


    <hr>

    <h1><a id="vp8"></a>Google VP8 License</h1>

    <p>This license applies to certain files in the directory
    <code>media/libvpx</code>.</p>
<pre>
Copyright (c) 2010, 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 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.

Subject to the terms and conditions of the above License, Google
hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable (except as stated in this
section) patent license to make, have made, use, offer to sell, sell,
import, and otherwise transfer this implementation of VP8, where such
license applies only to those patent claims, both currently owned by
Google and acquired in the future, licensable by Google that are
necessarily infringed by this implementation of VP8. If You or your
agent or exclusive licensee institute or order or agree to the
institution of patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that this
implementation of VP8 or any code incorporated within this
implementation of VP8 constitutes direct or contributory patent
infringement, or inducement of patent infringement, then any rights
granted to You under this License for this implementation of VP8
shall terminate as of the date such litigation is filed.
</pre>

    <hr>

    <h1><a id="gears-istumbler"></a>Google Gears/iStumbler License</h1>

    <p>This license applies to the file
    <code>netwerk/wifi/osx_wifi.h</code>.</p>

<pre>
Copyright 2008, Google Inc.

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 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 AUTHOR ``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 AUTHOR 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.

The contents of this file are taken from Apple80211.h from the iStumbler
project (http://www.istumbler.net). This project is released under the BSD
license with the following restrictions.

Copyright (c) 02006, Alf Watt ([email protected]). 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 iStumbler 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.
</pre>

    <hr>

    <h1><a id="gsl"></a>GSL License</h1>

    <p>This license applies to <code>mfbt/Span.h</code> and
    <code>mfbt/tests/gtest/TestSpan.cpp</code>.</p>
    <!-- https://github.com/Microsoft/GSL/blob/3819df6e378ffccf0e29465afe99c3b324c2aa70/LICENSE -->
<pre>
Copyright (c) 2015 Microsoft Corporation. All rights reserved.

This code is licensed under the MIT License (MIT).

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.
</pre>


    <hr>

    <h1><a id="gyp"></a>gyp License</h1>

    <p>This license applies to certain files in the directory
    <code>media/webrtc/trunk/tools/gyp</code>.</p>
<pre>
Copyright (c) 2009 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.
</pre>


    <hr>

    <h1><a id="halloc"></a>halloc License</h1>

    <p>This license applies to certain files in the directory
    <code>media/libnestegg/src</code>.</p>
<pre>
Copyright (c) 2004-2010 Alex Pankratov. 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 the project 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.
</pre>

    <hr>

    <h1><a id="harfbuzz"></a>HarfBuzz License</h1>

    <p>This license, with different copyright holders, applies to the files in
    the directory <code>gfx/harfbuzz/</code>.
    The copyright holders and the applicable ranges of dates are as follows:</p>

    <ul>
        <li>1998-2004  David Turner and Werner Lemberg</li>
        <li>2004, 2007, 2008, 2009, 2010  Red Hat, Inc.</li>
        <li>2006  Behdad Esfahbod</li>
        <li>2007  Chris Wilson</li>
        <li>2009  Keith Stribley &lt;[email protected]&gt;</li>
        <li>2010  Mozilla Foundation</li>
    </ul>

<pre>
Copyright (C) &lt;date&gt; &lt;copyright holder&gt;

 This is part of HarfBuzz, an OpenType Layout engine library.

Permission is hereby granted, without written agreement and without
license or royalty fees, to use, copy, modify, and distribute this
software and its documentation for any purpose, provided that the
above copyright notice and the following two paragraphs appear in
all copies of this software.

IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.

THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
</pre>


    <hr>

    <h1><a id="icu"></a>ICU License</h1>

    <p>This license applies to some code in the
    <code>gfx/thebes</code> directory.</p>

<pre>
ICU License - ICU 1.8.1 and later

COPYRIGHT AND PERMISSION NOTICE

Copyright (c) 1995-2012 International Business Machines Corporation and
others

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, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, provided that the above copyright notice(s) and this
permission notice appear in all copies of the Software and that both the
above copyright notice(s) and this permission notice appear in supporting
documentation.

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 OF THIRD PARTY RIGHTS.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE
BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES,
OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.

Except as contained in this notice, the name of a copyright holder shall
not be used in advertising or otherwise to promote the sale, use or other
dealings in this Software without prior written authorization of the
copyright holder.
All trademarks and registered trademarks mentioned herein are the property
of their respective owners.
</pre>
    <hr>
    <h1><a id="immutable"></a>Immutable.js License</h1>

<pre>
BSD License

For Immutable JS software

Copyright (c) 2014-2015, Facebook, 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 Facebook 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.
</pre>

    <hr>

    <h1><a id="jpnic"></a>Japan Network Information Center License</h1>
    <p>This license applies to certain files in the
    directory <code>netwerk/dns/</code>.</p>
<pre>
Copyright (c) 2001,2002 Japan Network Information Center.
All rights reserved.

By using this file, you agree to the terms and conditions set forth below.

     LICENSE TERMS AND CONDITIONS

The following License Terms and Conditions apply, unless a different
license is obtained from Japan Network Information Center ("JPNIC"),
a Japanese association, Kokusai-Kougyou-Kanda Bldg 6F, 2-3-4 Uchi-Kanda,
Chiyoda-ku, Tokyo 101-0047, Japan.

1. Use, Modification and Redistribution (including distribution of any
   modified or derived work) in source and/or binary forms is permitted
   under this License Terms and Conditions.

2. Redistribution of source code must retain the copyright notices as they
   appear in each source code file, this License Terms and Conditions.

3. Redistribution in binary form must reproduce the Copyright Notice,
   this License Terms and Conditions, in the documentation and/or other
   materials provided with the distribution.  For the purposes of binary
   distribution the "Copyright Notice" refers to the following language:
   "Copyright (c) 2000-2002 Japan Network Information Center. All rights
   reserved."

4. The name of JPNIC may not be used to endorse or promote products
   derived from this Software without specific prior written approval of
   JPNIC.

5. Disclaimer/Limitation of Liability: THIS SOFTWARE IS PROVIDED BY JPNIC
   "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 JPNIC 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 DAMAGES.
</pre>

    <hr>

    <h1><a id="jsmn"></a>JSMN License</h1>

    <p>This license applies to files in the directories
    <code>browser/extensions/mortar/json/</code>.
    </p>

<pre>
Copyright (c) 2010 Serge A. Zaitsev
Copyright (c) 2015 Andreas Gal

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.
</pre>

    <hr>

    <h1><a id="jszip"></a>JSZip License</h1>

    <p>This license applies to the file
    <code>devtools/client/shared/vendor/jszip.js</code>.</p>

<pre>
Copyright (c) 2009-2016 Stuart Knightley, David Duponchel, Franz Buchinger, António Afonso

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.
</pre>

    <hr>

    <h1><a id="jemalloc"></a>jemalloc License</h1>

    <p>This license applies to portions of the files in the directory
    <code>memory/build/</code>.
    </p>

<pre>
Copyright (C) 2006-2008 Jason Evans &lt;[email protected]&gt;.
All rights reserved.
Copyright (C) 2007-2017 Mozilla Foundation.

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(s), this list of conditions and the following disclaimer as
   the first lines of this file unmodified other than the possible
   addition of one or more copyright notices.
2. Redistributions in binary form must reproduce the above copyright
   notice(s), this list of conditions and the following disclaimer in
   the documentation and/or other materials provided with the
   distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``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(S) 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.
</pre>

    <hr>

    <h1><a id="jquery"></a>jQuery License</h1>

    <p>This license applies to all copies of jQuery in the code.</p>

<pre>
Copyright (c) 2010 John Resig, http://jquery.com/

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.
</pre>

    <hr>

    <h1><a id="k_exp"></a>k_exp License</h1>

    <p>This license applies to the file
    <code>modules/fdlibm/src/k_exp.cpp</code>.
    </p>

<pre>
Copyright (c) 2011 David Schultz &lt;[email protected]&gt;
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.

THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
</pre>


    <hr>

    <h1><a id="kernel32-sys"></a>kernel32-sys License</h1>

    <p>This license applies to files in the directories
    <code>third_party/rust/kernel32-sys</code> and
    <code>third_party/rust/kernel32-sys-0.1.4</code>.</p>

<pre>
The MIT License (MIT)

Copyright (c) 2015 Peter Atashian

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.
</pre>


    <hr>

    <h1><a id="khronos"></a>Khronos group License</h1>

    <p>This license applies to the following files:</p>

    <ul>
      <li><code>openmax_dl/dl/api/omxtypes.h</code></li>
      <li><code>openmax_dl/dl/sp/api/omxSP.h</code></li>
    </ul>

    <p>and also some files in these directories:</p>

    <ul>
      <li><code>browser/extensions/mortar/ppapi/</code></li>
    </ul>

<pre>
Copyright 2005-2008 The Khronos Group Inc. All Rights Reserved.

These materials are protected by copyright laws and contain material
proprietary to the Khronos Group, Inc.  You may use these materials
for implementing Khronos specifications, without altering or removing
any trademark, copyright or other notice from the specification.

Khronos Group makes no, and expressly disclaims any, representations
or warranties, express or implied, regarding these materials, including,
without limitation, any implied warranties of merchantability or fitness
for a particular purpose or non-infringement of any intellectual property.
Khronos Group makes no, and expressly disclaims any, warranties, express
or implied, regarding the correctness, accuracy, completeness, timeliness,
and reliability of these materials.

Under no circumstances will the Khronos Group, or any of its Promoters,
Contributors or Members or their respective partners, officers, directors,
employees, agents or representatives be liable for any damages, whether
direct, indirect, special or consequential damages for lost revenues,
lost profits, or otherwise, arising from or in connection with these
materials.

Khronos and OpenMAX are trademarks of the Khronos Group Inc.
</pre>

    <hr>

    <h1><a id="kiss_fft"></a>Kiss FFT License</h1>

    <p>This license applies to files in the directory
      <code>media/kiss_fft/</code>.</p>

<pre>
Copyright (c) 2003-2010 Mark Borgerding

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 author nor the names of any 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.
</pre>

    <hr>

    <h1><a id="lazy_static"></a>lazy_static License</h1>

    <p>This license applies to files in the directories
    <code>third_party/rust/lazy_static</code> and
    <code>third_party/rust/lazy_static-0.1.6</code>.</p>

<pre>
The MIT License (MIT)

Copyright (c) 2014 Marvin Löbel

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.
</pre>


    <hr>

    <h1><a id="lcms"></a>lcms License</h1>

    <p>This license applies to files in the directory
      <span class="path">modules/pdfium/pdfium/third_party/lcms2-2.6/</span>.</p>

<pre>
Copyright (c) 1998-2014 Marti Maria Saguer

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.
</pre>

    <hr>


    <h1><a id="libcubeb"></a>libcubeb License</h1>

    <p class="correctme">This license applies to files in the directory
    <code>media/libcubeb</code>.
    </p>

<pre>
Copyright &copy; 2011 Mozilla Foundation

Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
</pre>

    <hr>

    <h1><a id="libevent"></a>libevent License</h1>

    <p>This license applies to files in the directory
    <code>ipc/chromium/src/third_party/libevent/</code>.
    </p>

<pre>
Copyright 2000-2002 Niels Provos &lt;[email protected]&gt;
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. The name of the author may not be used to endorse or promote products
   derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
</pre>


    <hr>

    <h1><a id="libffi"></a>libffi License</h1>

    <p>This license applies to files in the directory
    <code>js/src/ctypes/libffi/</code>.
    </p>

<pre>
libffi - Copyright (c) 1996-2008 Red Hat, Inc and others.
See source files for details.

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.
</pre>


    <hr>

    <h1><a id="libjingle"></a>libjingle License</h1>

    <p>This license applies to the following files:</p>
    <ul>
      <li><code>media/mtransport/sigslot.h</code></li>
      <li><code>media/mtransport/test/gtest_utils.h</code></li>
    </ul>

<pre>
Copyright (c) 2004--2005, 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.
    * The name of the author 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 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.
</pre>


    <hr>

    <h1><a id="libnestegg"></a>libnestegg License</h1>

    <p>This license applies to certain files in the directory
    <code>media/libnestegg</code>.
    </p>

<pre>
Copyright &copy; 2010 Mozilla Foundation

Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
</pre>

    <hr>

    <h1><a id="libsoundtouch"></a>libsoundtouch License</h1>

    <p>This license applies to certain files in the directory
    <code>media/libsoundtouch/src/</code>.
    </p>

<pre>
The SoundTouch Library Copyright &copy; Olli Parviainen 2001-2012

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
</pre>

    <hr>

    <h1><a id="libyuv"></a>libyuv License</h1>

    <p>This license applies to files in the directory
    <code>media/libyuv</code> except
    for the file <code>media/libyuv/source/x86inc.asm</code>.
    </p>

<pre>
Copyright (c) 2011, The LibYuv project 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 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.
</pre>

    <hr>

    <h1><a id="hunspell-lt"></a>Lithuanian Spellchecking Dictionary License</h1>

    <p>This license applies to the Lithuanian Spellchecking Dictionary. (This
      code only ships in some localized versions of this product.)</p>

<pre>
Copyright (c) 2000-2013, Albertas Agejevas 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:
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 the copyright holders 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 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 ALBERTAS AGEJEVAS 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.
</pre>

    <hr>

    <h1><a id="lodash"></a>License - lodash</h1>

    <p>This license applies to some of the code in
    <code>devtools/client/shared/vendor/lodash.js</code>.</p>

<pre>
Copyright JS Foundation and other contributors <https://js.foundation/>

Based on Underscore.js, copyright Jeremy Ashkenas,
DocumentCloud and Investigative Reporters & Editors <http://underscorejs.org/>

This software consists of voluntary contributions made by many
individuals. For exact contribution history, see the revision history
available at https://github.com/lodash/lodash

The following license applies to all parts of this software except as
documented below:

====

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.

====

Copyright and related rights for sample code are waived via CC0. Sample
code is defined as all source code displayed within the prose of the
documentation.

CC0: http://creativecommons.org/publicdomain/zero/1.0/

====

Files located in the node_modules and vendor directories are externally
maintained libraries used by this software which have their own
licenses; we recommend you read them, as their terms may differ from the
terms above.

</pre>


    <hr>

    <h1><a id="matches"></a>matches License</h1>

    <p>This license applies to files in the directory
    <code>third_party/rust/matches</code>.</p>

<pre>
Copyright (c) 2014-2016 Simon Sapin

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.
</pre>


    <hr>

    <h1><a id="microformatsshiv"></a>MIT license — microformat-shiv</h1>

    <p>This license applies to some files in the directory
    <code>toolkit/components/microformats</code>.</p>

<pre>
MIT license — microformat-shiv

Copyright (c) 2012-2013 Glenn Jones

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.
</pre>


    <hr>

    <h1><a id="myspell"></a>MySpell License</h1>

    <p>This license applies to some files in the directory
    <code>extensions/spellcheck/hunspell</code>.</p>

<pre>
Copyright 2002 Kevin B. Hendricks, Stratford, Ontario, Canada
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:

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. All modifications to the source code must be clearly marked as
   such.  Binary redistributions based on modified source code
   must be clearly marked as modified versions in the documentation
   and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY KEVIN B. HENDRICKS 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
KEVIN B. HENDRICKS 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.
</pre>


<hr>

<h1><a id="naturalSort"></a>naturalSort License</h1>

<p>This license applies to <code>devtools/client/shared/natural-sort.js</code>.</p>

<pre>
The MIT License (MIT)

Copyright (c) 2014 Gabriel Llamas

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.
</pre>

    <hr>

    <h1><a id="nicer"></a>nICEr License</h1>

    <p>This license applies to certain files in the directory
    <code>media/mtransport/third_party/nICEr</code>.</p>

<pre>
   Copyright (C) 2007, Adobe Systems Inc.
   Copyright (C) 2007-2008, Network Resonance, Inc.

Each source file bears an individual copyright notice.

The following license applies to this distribution as a whole.


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 Adobe Systems, Network Resonance 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.
</pre>


    <hr>

    <h1><a id="openaes"></a>OpenAES License</h1>

    <p>This license applies to certain files in the directory
    <code>media/gmp-clearkey/0.1/openaes</code>.
    </p>

<pre>
Copyright (c) 2012, Nabil S. Al Ramli, www.nalramli.com
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.

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.
</pre>


    <hr>

    <h1><a id="openvision"></a>OpenVision License</h1>

    <p>This license applies to the file
    <code>extensions/auth/gssapi.h</code>.</p>

<pre>
Copyright 1993 by OpenVision Technologies, Inc.

Permission to use, copy, modify, distribute, and sell this software
and its documentation for any purpose is hereby granted without fee,
provided that the above copyright notice appears in all copies and
that both that copyright notice and this permission notice appear in
supporting documentation, and that the name of OpenVision not be used
in advertising or publicity pertaining to distribution of the software
without specific, written prior permission. OpenVision makes no
representations about the suitability of this software for any
purpose.  It is provided "as is" without express or implied warranty.

OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
</pre>


    <hr>

    <h1><a id="openvr"></a>OpenVR License</h1>

    <p>This license applies to certain files in the directory
    <code>gfx/vr/openvr</code>.</p>
<pre>
Copyright (c) 2015, Valve Corporation
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 the copyright holder 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.
</pre>


<hr>

    <h1><a id="node-md5"></a>node-md5 License</h1>

    <p>This license applies to some of the code in
    <code>devtools/client/debugger/new/debugger.js</code>.</p>

<pre>
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.
</pre>

    <hr>

    <h1><a id="node-properties"></a>node-properties License</h1>

    <p>This license applies to
    <code>devtools/shared/node-properties/node-properties.js</code>.</p>

<pre>
The MIT License (MIT)

Copyright (c) 2014 Gabriel Llamas

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.
</pre>


    <hr>

    <h1><a id="nom"></a>nom License</h1>

    <p>This license applies to files in the directory
    <code>third_party/rust/nom</code>.</p>

<pre>
Copyright (c) 2015 Geoffroy Couprie

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.
</pre>


    <hr>

    <h1><a id="nrappkit"></a>nrappkit License</h1>

    <p>This license applies to certain files in the directory
    <code>media/mtransport/third_party/nrappkit</code>.</p>

<pre>
Copyright (C) 2001-2007, Network Resonance, 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:

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 Network Resonance, Inc. nor the name of any
   contributors to this software 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.
</pre>

    <p>This license applies to certain files in the directory
    <code>media/mtransport/third_party/nrappkit</code>.</p>

<pre>
Copyright (C) 1999-2003 RTFM, Inc.
All Rights Reserved

This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla
<[email protected]> and licensed by RTFM, Inc.

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. All advertising materials mentioning features or use of this software
   must display the following acknowledgement:

   This product includes software developed by Eric Rescorla for
   RTFM, Inc.

4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be
   used to endorse or promote products derived from this
   software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE ERIC RESCORLA AND RTFM ``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 REGENTS OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
oDAMAGES (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.
</pre>

   <p>Note that RTFM, Inc. has waived clause (3) above as of June 20, 2012
   for files appearing in this distribution. This waiver applies only to
   files included in this distribution. it does not apply to any other
   part of ssldump not included in this distribution.</p>

    <p>This license applies to the file <code>media/mtransport/third_party/nrappkit/src/port/generic/include/sys/queue.h</code>.</p>

<pre>
Copyright (c) 1991, 1993
	The Regents of the University of California.  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.
4. Neither the name of the University 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 REGENTS 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 REGENTS 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.
</pre>


  <p>This license applies to the file:
  <code>media/mtransport/third_party/nrappkit/src/util/util.c</code>.</p>

<pre>
Copyright (c) 1998 Todd C. Miller <[email protected]>
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. The name of the author may not be used to endorse or promote products
   derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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.
</pre>


    <hr>

    <h1><a id="praton"></a>praton License</h1>

    <p>This license applies to the file
    <code>nsprpub/pr/src/misc/praton.c</code>.</p>

<pre>
Copyright (c) 1983, 1990, 1993
   The Regents of the University of California.  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.
4. Neither the name of the University 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 REGENTS 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 REGENTS 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.


Portions Copyright (c) 1993 by Digital Equipment Corporation.

Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies, and that
the name of Digital Equipment Corporation not be used in advertising or
publicity pertaining to distribution of the document or software without
specific, written prior permission.

THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL DIGITAL EQUIPMENT
CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.
</pre>


    <hr>

    <h1><a id="praton1"></a>praton and inet_ntop License</h1>

    <p>This license applies to the files
    <code>nsprpub/pr/src/misc/praton.c</code> and
    <code>media/mtransport/third_party/nrappkit/src/util/util.c</code>.</p>

<pre>
Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
Portions Copyright (c) 1996-1999 by Internet Software Consortium.

Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
</pre>


    <hr>

    <h1><a id="ordered-float"></a>ordered-float License</h1>

    <p>This license applies to files in the directory
    <code>third_party/rust/ordered-float</code>.</p>

<pre>
The MIT License (MIT)

Copyright (c) 2015 Jonathan Reem

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.
</pre>


    <hr>

    <h1><a id="owning_ref"></a>owning_ref License</h1>

    <p>This license applies to files in the directory
    <code>third_party/rust/owning_ref</code>.</p>

<pre>
The MIT License (MIT)

Copyright (c) 2015 Marvin Löbel

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.
</pre>


    <hr>

    <h1><a id="pbkdf2-sha256"></a>pbkdf2_sha256 License</h1>

    <p>This license applies to the code
    <code>mozglue/android/pbkdf2_sha256.c</code> and
    <code>mozglue/android/pbkdf2_sha256.h</code>.
    </p>

<pre>
Copyright 2005,2007,2009 Colin Percival
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.

THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
</pre>

    <hr>

    <h1><a id="pdfium"></a>PDFium License</h1>

    <p>This license applies to files in the directory
      <span class="path">modules/pdfium/pdfium/</span>.
    </p>

<pre>
Copyright 2014 PDFium 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.
</pre>


    <hr>

    <h1><a id="phf"></a>phf, phf_codegen, phf_generator, phf_shared License</h1>

    <p>This license applies to files in the directories
    <code>third_party/rust/phf</code>,
    <code>third_party/rust/phf_codegen</code>,
    <code>third_party/rust/phf_generator</code>, and
    <code>third_party/rust/phf_shared</code>.</p>

<pre>
The MIT License (MIT)

Copyright (c) 2014 Steven Fackler

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.
</pre>


    <hr>

    <h1><a id="polymer"></a>Polymer License</h1>

    <p>This license applies to the file
    <code>toolkit/components/payments/res/vendor/custom-elements.min.js</code>.</p>

<pre>
Copyright (c) 2014 The Polymer 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.
</pre>


    <hr>

    <h1><a id="precomputed-hash"></a>precomputed-hash License</h1>

    <p>This license applies to files in the directory
    <code>third_party/rust/precomputed-hash</code>.</p>

<pre>
MIT License

Copyright (c) 2017 Emilio Cobos Álvarez

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.
</pre>


    <hr>

    <h1><a id="prop-types"></a>prop-types License</h1>

    <p>This license applies to the files
    <code>browser/extensions/activity-stream/vendor/prop-types*</code>.</p>

<pre>
MIT License

Copyright (c) 2013-present, Facebook, Inc.

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.
</pre>


    <hr>

    <h1><a id="qcms"></a>qcms License</h1>

    <p>This license applies to certain files in the directory
      <code>gfx/qcms/</code>.</p>
<pre>
Copyright (C) 2009 Mozilla Corporation
Copyright (C) 1998-2007 Marti Maria

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.
</pre>


    <hr>

    <h1><a id="qrcode-generator"></a>QR Code Generator License</h1>

    <p>This license applies to certain files in the directory
      <code>devtools/shared/qrcode/encoder/</code>.</p>
<pre>
Copyright (c) 2009 Kazuhiko Arase

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.
</pre>

    <hr>

    <h1><a id="raven-js"></a>Raven.js License</h1>

    <p>This license applies to the file
      <code>browser/extensions/screenshots/webextension/build/raven.js</code>.</p>
<pre>
Copyright (c) 2014 Matt Robenolt and other 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.

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.
</pre>

    <hr>

    <h1><a id="react"></a>React License</h1>

    <p>This license applies to various files in the Mozilla codebase.</p>

<pre>
Copyright (c) 2013-2015, Facebook, 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 Facebook 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.
</pre>

    <hr>

  <h1>
    <a id="react-mit"></a>React MIT License</h1>

  <p>This license applies to various files in the Mozilla codebase.</p>

  <pre>
MIT License

Copyright (c) 2013-present, Facebook, Inc.

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.
  </pre>

  <hr>

    <h1><a id="react-intl"></a>React Intl License</h1>

    <p>This license applies to the files
    <code>browser/extensions/activity-stream/vendor/react-intl.js</code>.</p>
<pre>
Copyright 2014 Yahoo 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 the Yahoo 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 YAHOO! INC. 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.
</pre>

    <hr>

    <h1><a id="react-redux"></a>React-Redux License</h1>

    <p>This license applies to the files
    <code>devtools/client/shared/vendor/react-redux.js</code> and
    <code>browser/extensions/activity-stream/vendor/react-redux.js</code>.</p>
<pre>
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.
</pre>

    <hr>

    <h1><a id="xdg"></a>Red Hat xdg_user_dir_lookup License</h1>

    <p>This license applies to the
    <code>xdg_user_dir_lookup</code> function in
    <code>xpcom/io/SpecialSystemDirectory.cpp</code>.</p>

<pre>
Copyright (c) 2007 Red Hat, Inc.

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.
</pre>


    <hr>

    <h1><a id="redox_syscall"></a>redox_syscall License</h1>

    <p>This license applies to files in the directory
    <code>third_party/rust/redox_syscall</code>.</p>

<pre>
MIT License

Copyright (c) 2016 Jeremy Soller

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.
</pre>


    <hr>

    <h1><a id="redux"></a>Redux License</h1>

    <p>This license applies to the file
    <code>devtools/client/shared/vendor/redux.js</code> and
    <code>browser/extensions/activity-stream/vendor/Redux.jsm</code>.</p>
<pre>
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.
</pre>

<hr>

<h1><a id="reselect"></a>Reselect License</h1>

<p>This license applies to the file
<code>devtools/client/shared/vendor/reselect.js</code>.</p>
<pre>
The MIT License (MIT)

Copyright (c) 2015-2016 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.
</pre>

    <hr>

    <h1><a id="hunspell-ru"></a>Russian Spellchecking Dictionary License</h1>

    <p>This license applies to the Russian Spellchecking Dictionary. (This
      code only ships in some localized versions of this product.)</p>

<pre>
* Copyright (c) 1997-2008, Alexander I. Lebedev

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.
* Modified versions must be clearly marked as such.
* The name of Alexander I. Lebedev 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 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.
</pre>

    <hr>

    <h1><a id="sctp"></a>SCTP Licenses</h1>

    <p>These licenses apply to certain files in the directory
    <code>netwerk/sctp/src/</code>.</p>

<pre>
Copyright (c) 2009-2010 Brad Penoff
Copyright (c) 2009-2010 Humaira Kamal
Copyright (c) 2011-2012 Irene Ruengeler
Copyright (c) 2010-2012, by Michael Tuexen. All rights reserved.
Copyright (c) 2010-2012, by Randall Stewart. All rights reserved.
Copyright (c) 2010-2012, by Robin Seggelmann. 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.

THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.

Copyright (c) 2001-2008, by Cisco Systems, Inc. All rights reserved.
Copyright (c) 2008-2012, by Randall Stewart. All rights reserved.
Copyright (c) 2008-2012, by Michael Tuexen. All rights reserved.
Copyright (c) 2008-2012, by Brad Penoff. All rights reserved.
Copyright (c) 1980, 1982, 1986, 1987, 1988, 1990, 1993
  The Regents of the University of California.
Copyright (c) 2005 Robert N. M. Watson All rights reserved.
Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
a) Redistributions of source code must retain the above copyright notice,
   this list of conditions and the following disclaimer.
b) 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.
c) Neither the name of Cisco Systems, Inc, the name of the university,
   the WIDE project, 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.
</pre>


    <hr>

    <h1><a id="skia"></a>Skia License</h1>

    <p>This license applies to certain files in the directory
    <code>gfx/skia/</code>.</p>

<pre>
Copyright (c) 2011 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.
</pre>

    <hr>

    <h1><a id="snappy"></a>Snappy License</h1>

    <p>This license applies to certain files in the directory
    <code>other-licenses/snappy/</code>.</p>

<pre>
Copyright 2011, 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.
</pre>

    <hr>

    <h1><a id="sprintf.js"></a>sprintf.js License</h1>

    <p>This license applies to
    <code>devtools/shared/sprintfjs/sprintf.js</code>.</p>

<pre>
Copyright (c) 2007-2016, Alexandru Marasteanu &lt;hello [at) alexei (dot] ro&gt;
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 this software 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 AUTHORS OR COPYRIGHT HOLDERS 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.
</pre>

    <hr>

    <h1><a id="sunsoft"></a>SunSoft License</h1>

    <p>This license applies to the
      <code>ICC_H</code> block in
      <code>gfx/qcms/qcms.h</code>.</p>

<pre>
Copyright (c) 1994-1996 SunSoft, Inc.

                    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 restrict-
ion, 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 NON-
INFRINGEMENT.  IN NO EVENT SHALL SUNSOFT, INC. OR ITS PARENT
COMPANY 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.

Except as contained in this notice, the name of SunSoft, Inc.
shall not be used in advertising or otherwise to promote the
sale, use or other dealings in this Software without written
authorization from SunSoft Inc.
</pre>


    <hr>

    <h1><a id="superfasthash"></a>SuperFastHash License</h1>

    <p>This license applies to files in the directory
      <code>security/sandbox/chromium/base/third_party/superfasthash/</code>.</p>

<pre>
Copyright (c) 2010, Paul Hsieh
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 my name, Paul Hsieh, nor the names of any other contributors to the
  code use 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 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.
</pre>


    <hr>

    <h1><a id="synstructure"></a>synstructure License</h1>

    <p>This license applies to files in the directory
    <code>third_party/rust/synstructure</code>.</p>

<pre>
The MIT License (MIT)

Copyright (c) 2016 Michael Layzell

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.
</pre>


    <hr>

    <h1><a id="unicase"></a>unicase License</h1>

    <p>This license applies to files in the directory
    <code>third_party/rust/unicase</code>.</p>

<pre>
Copyright (c) 2014-2015 Sean McArthur

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.
</pre>


    <hr>

    <h1><a id="unicode"></a>Unicode License</h1>

    <p>This license applies to files in the <code>intl/icu</code>
    and <code>intl/tzdata</code> directories and certain files in
    the <code>js/src/vm</code> directory.</p>
    </p>

<pre>
COPYRIGHT AND PERMISSION NOTICE

Copyright © 1991-2016 Unicode, Inc. All rights reserved.
Distributed under the Terms of Use in http://www.unicode.org/copyright.html.

Permission is hereby granted, free of charge, to any person obtaining
a copy of the Unicode data files and any associated documentation
(the "Data Files") or Unicode software and any associated documentation
(the "Software") to deal in the Data Files or Software
without restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, and/or sell copies of
the Data Files or Software, and to permit persons to whom the Data Files
or Software are furnished to do so, provided that either
(a) this copyright and permission notice appear with all copies
of the Data Files or Software, or
(b) this copyright and permission notice appear in associated
Documentation.

THE DATA FILES AND SOFTWARE ARE 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 OF THIRD PARTY RIGHTS.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS
NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL
DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THE DATA FILES OR SOFTWARE.

Except as contained in this notice, the name of a copyright holder
shall not be used in advertising or otherwise to promote the sale,
use or other dealings in these Data Files or Software without prior
written authorization of the copyright holder.

---------------------

Third-Party Software Licenses

This section contains third-party software notices and/or additional
terms for licensed third-party software components included within ICU
libraries.

1. ICU License - ICU 1.8.1 to ICU 57.1

COPYRIGHT AND PERMISSION NOTICE

Copyright (c) 1995-2016 International Business Machines Corporation and others
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, and/or sell copies of the Software, and to permit persons
to whom the Software is furnished to do so, provided that the above
copyright notice(s) and this permission notice appear in all copies of
the Software and that both the above copyright notice(s) and this
permission notice appear in supporting documentation.

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
OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY
SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER
RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

Except as contained in this notice, the name of a copyright holder
shall not be used in advertising or otherwise to promote the sale, use
or other dealings in this Software without prior written authorization
of the copyright holder.

All trademarks and registered trademarks mentioned herein are the
property of their respective owners.

2. Chinese/Japanese Word Break Dictionary Data (cjdict.txt)

 #     The Google Chrome software developed by Google is licensed under
 # the BSD license. Other software included in this distribution is
 # provided under other licenses, as set forth below.
 #
 #  The BSD License
 #  http://opensource.org/licenses/bsd-license.php
 #  Copyright (C) 2006-2008, 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.
 #
 #
 #  The word list in cjdict.txt are generated by combining three word lists
 # listed below with further processing for compound word breaking. The
 # frequency is generated with an iterative training against Google web
 # corpora.
 #
 #  * Libtabe (Chinese)
 #    - https://sourceforge.net/project/?group_id=1519
 #    - Its license terms and conditions are shown below.
 #
 #  * IPADIC (Japanese)
 #    - http://chasen.aist-nara.ac.jp/chasen/distribution.html
 #    - Its license terms and conditions are shown below.
 #
 #  ---------COPYING.libtabe ---- BEGIN--------------------
 #
 #  /*
 #   * Copyrighy (c) 1999 TaBE Project.
 #   * Copyright (c) 1999 Pai-Hsiang Hsiao.
 #   * 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 the TaBE Project 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
 #   * REGENTS 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.
 #   */
 #
 #  /*
 #   * Copyright (c) 1999 Computer Systems and Communication Lab,
 #   *                    Institute of Information Science, Academia
 #       *                    Sinica. 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 the Computer Systems and Communication Lab
 #   *   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
 #   * REGENTS 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.
 #   */
 #
 #  Copyright 1996 Chih-Hao Tsai @ Beckman Institute,
 #      University of Illinois
 #  [email protected]  http://casper.beckman.uiuc.edu/~c-tsai4
 #
 #  ---------------COPYING.libtabe-----END--------------------------------
 #
 #
 #  ---------------COPYING.ipadic-----BEGIN-------------------------------
 #
 #  Copyright 2000, 2001, 2002, 2003 Nara Institute of Science
 #  and Technology.  All Rights Reserved.
 #
 #  Use, reproduction, and distribution of this software is permitted.
 #  Any copy of this software, whether in its original form or modified,
 #  must include both the above copyright notice and the following
 #  paragraphs.
 #
 #  Nara Institute of Science and Technology (NAIST),
 #  the copyright holders, disclaims all warranties with regard to this
 #  software, including all implied warranties of merchantability and
 #  fitness, in no event shall NAIST be liable for
 #  any special, indirect or consequential damages or any damages
 #  whatsoever resulting from loss of use, data or profits, whether in an
 #  action of contract, negligence or other tortuous action, arising out
 #  of or in connection with the use or performance of this software.
 #
 #  A large portion of the dictionary entries
 #  originate from ICOT Free Software.  The following conditions for ICOT
 #  Free Software applies to the current dictionary as well.
 #
 #  Each User may also freely distribute the Program, whether in its
 #  original form or modified, to any third party or parties, PROVIDED
 #  that the provisions of Section 3 ("NO WARRANTY") will ALWAYS appear
 #  on, or be attached to, the Program, which is distributed substantially
 #  in the same form as set out herein and that such intended
 #  distribution, if actually made, will neither violate or otherwise
 #  contravene any of the laws and regulations of the countries having
 #  jurisdiction over the User or the intended distribution itself.
 #
 #  NO WARRANTY
 #
 #  The program was produced on an experimental basis in the course of the
 #  research and development conducted during the project and is provided
 #  to users as so produced on an experimental basis.  Accordingly, the
 #  program is provided without any warranty whatsoever, whether express,
 #  implied, statutory or otherwise.  The term "warranty" used herein
 #  includes, but is not limited to, any warranty of the quality,
 #  performance, merchantability and fitness for a particular purpose of
 #  the program and the nonexistence of any infringement or violation of
 #  any right of any third party.
 #
 #  Each user of the program will agree and understand, and be deemed to
 #  have agreed and understood, that there is no warranty whatsoever for
 #  the program and, accordingly, the entire risk arising from or
 #  otherwise connected with the program is assumed by the user.
 #
 #  Therefore, neither ICOT, the copyright holder, or any other
 #  organization that participated in or was otherwise related to the
 #  development of the program and their respective officials, directors,
 #  officers and other employees shall be held liable for any and all
 #  damages, including, without limitation, general, special, incidental
 #  and consequential damages, arising out of or otherwise in connection
 #  with the use or inability to use the program or any product, material
 #  or result produced or otherwise obtained by using the program,
 #  regardless of whether they have been advised of, or otherwise had
 #  knowledge of, the possibility of such damages at any time during the
 #  project or thereafter.  Each user will be deemed to have agreed to the
 #  foregoing by his or her commencement of use of the program.  The term
 #  "use" as used herein includes, but is not limited to, the use,
 #  modification, copying and distribution of the program and the
 #  production of secondary products from the program.
 #
 #  In the case where the program, whether in its original form or
 #  modified, was distributed or delivered to or received by a user from
 #  any person, organization or entity other than ICOT, unless it makes or
 #  grants independently of ICOT any specific warranty to the user in
 #  writing, such person, organization or entity, will also be exempted
 #  from and not be held liable to the user for any such damages as noted
 #  above as far as the program is concerned.
 #
 #  ---------------COPYING.ipadic-----END----------------------------------

3. Lao Word Break Dictionary Data (laodict.txt)

 #  Copyright (c) 2013 International Business Machines Corporation
 #  and others. All Rights Reserved.
 #
 # Project: http://code.google.com/p/lao-dictionary/
 # Dictionary: http://lao-dictionary.googlecode.com/git/Lao-Dictionary.txt
 # License: http://lao-dictionary.googlecode.com/git/Lao-Dictionary-LICENSE.txt
 #              (copied below)
 #
 #  This file is derived from the above dictionary, with slight
 #  modifications.
 #  ----------------------------------------------------------------------
 #  Copyright (C) 2013 Brian Eugene Wilson, Robert Martin Campbell.
 #  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.
 #
 #
 # 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.
 #  --------------------------------------------------------------------------

4. Burmese Word Break Dictionary Data (burmesedict.txt)

 #  Copyright (c) 2014 International Business Machines Corporation
 #  and others. All Rights Reserved.
 #
 #  This list is part of a project hosted at:
 #    github.com/kanyawtech/myanmar-karen-word-lists
 #
 #  --------------------------------------------------------------------------
 #  Copyright (c) 2013, LeRoy Benjamin Sharon
 #  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 Myanmar Karen Word Lists, 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.
 #  --------------------------------------------------------------------------

5. Time Zone Database

  ICU uses the public domain data and code derived from Time Zone
Database for its time zone support. The ownership of the TZ database
is explained in BCP 175: Procedure for Maintaining the Time Zone
Database section 7.

 # 7.  Database Ownership
 #
 #    The TZ database itself is not an IETF Contribution or an IETF
 #    document.  Rather it is a pre-existing and regularly updated work
 #    that is in the public domain, and is intended to remain in the
 #    public domain.  Therefore, BCPs 78 [RFC5378] and 79 [RFC3979] do
 #    not apply to the TZ Database or contributions that individuals make
 #    to it.  Should any claims be made and substantiated against the TZ
 #    Database, the organization that is providing the IANA
 #    Considerations defined in this RFC, under the memorandum of
 #    understanding with the IETF, currently ICANN, may act in accordance
 #    with all competent court orders.  No ownership claims will be made
 #    by ICANN or the IETF Trust on the database or the code.  Any person
 #    making a contribution to the database or code waives all rights to
 #    future claims in that contribution or in the TZ Database.</pre>


    <hr>

    <h1><a id="ucal"></a>University of California License</h1>

    <p>This license applies to the following files or, in the case of
    directories, certain files in those directories:</p>

    <ul>
      <li><code>dbm/</code></li>
      <li><code>db/mork/src/morkQuickSort.cpp</code></li>
      <li><code>xpcom/ds/nsQuickSort.cpp</code></li>
      <li><code>nsprpub/pr/src/misc/praton.c</code></li>
      <li><code>media/mtransport/third_party/nICEr/src/stun/addrs.c</code></li>
    </ul>

<pre>
Copyright (c) 1990, 1993
 The Regents of the University of California.  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 Deleted as of 22nd July 1999; see
    <a href="ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change">ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change</a>
    for details]
4. Neither the name of the University 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 REGENTS 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 REGENTS 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.
</pre>


    <hr>

    <h1><a id="unreachable"></a>unreachable License</h1>

    <p>This license applies to files in the directory
    <code>third_party/rust/unreachable</code>.</p>

<pre>
The MIT License (MIT)

Copyright (c) 2015 Jonathan Reem

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.
</pre>


    <hr>

    <h1><a id="hunspell-en-US"></a>US English Spellchecking Dictionary Licenses</h1>

    <p>These licenses apply to certain files in the directory
      <code>extensions/spellcheck/locales/en-US/hunspell/</code>. (This
      code only ships in some localized versions of this product.)</p>

<pre>
Different parts of the US English dictionary (SCOWL) are subject to the
following licenses as  shown below. For additional details, sources, credits,
and public domain references, see <a href="https://dxr.mozilla.org/mozilla-central/source/extensions/spellcheck/locales/en-US/hunspell/README_en_US.txt">README.txt</a>.

The collective work of the Spell Checking Oriented Word Lists (SCOWL) is under
the  following copyright:

Copyright 2000-2007 by Kevin Atkinson
Permission to use, copy, modify, distribute and sell these word lists, the
associated scripts, the output created from the scripts, and its documentation
for any purpose is hereby granted without fee, provided that the above
copyright notice appears in all copies and that both that copyright notice and
this permission notice appear in supporting documentation. Kevin Atkinson makes
no representations about the suitability of this array for any purpose. It is
provided  "as is" without express or implied warranty.

The WordNet database is under the following copyright:

This software and database is being provided to you, the LICENSEE, by Princeton
University under the following license.  By obtaining, using and/or copying
this software and database, you agree that you have read, understood, and will
comply with these terms and conditions:
Permission to use, copy, modify and distribute this software and database and
its documentation for any purpose and without fee or royalty is hereby granted,
provided that you agree to comply with the following copyright notice and
statements, including the disclaimer, and that the same appear on ALL copies of
the software, database and documentation, including modifications that you make
for internal use or for distribution.
WordNet 1.6 Copyright 1997 by Princeton University.  All rights reserved.
THIS SOFTWARE AND DATABASE IS PROVIDED "AS IS" AND PRINCETON UNIVERSITY
MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED.  BY WAY OF
EXAMPLE, BUT NOT LIMITATION, PRINCETON UNIVERSITY MAKES NO
REPRESENTATIONS OR WARRANTIES OF MERCHANT- ABILITY OR FITNESS FOR ANY
PARTICULAR PURPOSE OR THAT THE USE OF THE LICENSED SOFTWARE, DATABASE OR
DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS,
TRADEMARKS OR OTHER RIGHTS.
The name of Princeton University or Princeton may not be used in advertising or
publicity pertaining to distribution of the software and/or database.  Title to
copyright in this software, database and any associated documentation shall at
all times remain with Princeton University and LICENSEE agrees to preserve same.

The "UK Advanced Cryptics Dictionary" is under the following copyright:

Copyright (c) J Ross Beresford 1993-1999. All Rights Reserved.
The following restriction is placed on the use of this publication: if The UK
Advanced Cryptics Dictionary is used in a software package or redistributed in
any form, the copyright notice must be prominently displayed and the text of
this document must be included verbatim.   There are no other restrictions: I
would like to see the list distributed as widely as possible.

Various parts are under the Ispell copyright:

Copyright 1993, Geoff Kuenning, Granada Hills, CA
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. All modifications to the source code must be clearly marked as such.
Binary redistributions based on modified source code must be clearly marked as
modified versions in the documentation and/or other materials provided with
the distribution.
  (clause 4 removed with permission from Geoff Kuenning)
  5. The name of Geoff Kuenning may not be used to endorse or promote products
derived from this software without specific prior written permission.
 THIS SOFTWARE IS PROVIDED BY GEOFF KUENNING 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 GEOFF KUENNING 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.

Additional Contributors:

 Alan Beale &lt;[email protected]&gt;
 M Cooper &lt;[email protected]&gt;
</pre>


    <hr>

    <h1><a id="utf8-ranges"></a>utf8-ranges License</h1>

    <p>This license applies to files in the directory
    <code>third_party/rust/utf8-ranges</code>.</p>

<pre>
The MIT License (MIT)

Copyright (c) 2015 Andrew Gallant

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.
</pre>


    <hr>

    <h1><a id="void"></a>void License</h1>

    <p>This license applies to files in the directory
    <code>third_party/rust/void</code>.</p>

<pre>
The MIT License (MIT)

Copyright (c) 2015 Jonathan Reem

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.
</pre>


    <hr>

    <h1><a id="v8"></a>V8 License</h1>

    <p>This license applies to certain files in the directories
      <code>js/src/irregexp</code>,
      <code>js/src/builtin</code>,
      <code>js/src/jit/arm</code> and
      <code>js/src/jit/mips</code>.
    </p>
<pre>
Copyright 2006-2012 the V8 project 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.
</pre>

<hr>

<h1><a id="validator"></a>Validator License</h1>

<p>This license applies to certain files in the directory
  <code>devtools/shared/stringvalidator/</code>,
</p>
<pre>

Copyright (c) 2016 Chris O"Hara <[email protected]>

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.
</pre>

    <hr>


    <h1><a id="vtune"></a>VTune License</h1>

    <p>This license applies to certain files in the directory
    <code>js/src/vtune</code> and <code>tools/profiler/core/vtune</code>.</p>
<pre>
Copyright (c) 2011 Intel Corporation.
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 Intel Corporation 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.
</pre>


    <hr>

    <h1><a id="webrtc"></a>WebRTC License</h1>

    <p>This license applies to certain files in the directory
    <code>media/webrtc/trunk</code>.</p>
<pre>
Copyright (c) 2011, The WebRTC project 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 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.
</pre>


    <hr>

    <h1><a id="x264"></a>x264 License</h1>

    <p>This license applies to the file <code>
    media/webrtc/trunk/third_party/libyuv/source/x86inc.asm</code>.
    </p>

<pre>
Copyright (C) 2005-2012 x264 project

Authors: Loren Merritt <[email protected]>
         Anton Mitrofanov <[email protected]>
         Jason Garrett-Glaser <[email protected]>
         Henrik Gramner <[email protected]>

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
</pre>

    <hr>

    <h1><a id="xiph"></a>Xiph.org Foundation License</h1>

    <p>This license applies to files in the following directories
      with the specified copyright year ranges:</p>
    <ul>
      <li><code>media/libogg/</code>, 2002</li>
      <li><code>media/libtheora/</code>, 2002-2007</li>
      <li><code>media/libvorbis/</code>, 2002-2004</li>
      <li><code>media/libtremor/</code>, 2002-2010</li>
      <li><code>media/libspeex_resampler/</code>, 2002-2008</li>
    </ul>

<pre>
Copyright (c) &lt;year&gt;, Xiph.org Foundation

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 the Xiph.org Foundation 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 FOUNDATION
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.
</pre>


    <hr>

    <h1><a id="other-notices"></a>Other Required Notices</h1>

    <ul>
      <li>This software is based in part on the work of the Independent
          JPEG Group.</li>
      <li>Portions of the OS/2 and Android versions
          of this software are copyright &copy;1996-2012
          <a href="http://www.freetype.org/">The FreeType Project</a>.
          All rights reserved.</li>
    </ul>


    <hr>

    <h1><a id="optional-notices"></a>Optional Notices</h1>

    <p>Some permissive software licenses request but do not require an
    acknowledgement of the use of their software. We are very grateful
    to the following people and projects for their contributions to
    this product:</p>

    <ul>
      <li>The <a href="http://www.zlib.net/">zlib</a> compression library
          (Jean-loup Gailly, Mark Adler and team)</li>
      <li>The <a href="http://www.bzip.org/">bzip2</a> compression library
          (Julian Seward)</li>
      <li>The <a href="http://www.libpng.org/pub/png/">libpng</a> graphics library
          (Glenn Randers-Pehrson and team)</li>
      <li>The <a href="http://www.sqlite.org/">sqlite</a> database engine
          (D. Richard Hipp and team)</li>
      <li>The <a href="http://nsis.sourceforge.net/">Nullsoft Scriptable Install System</a>
          (Amir Szekely and team)</li>
      <li>The <a href="http://mattmccutchen.net/bigint/">C++ Big Integer Library</a>
          (Matt McCutchen)</li>
    </ul>





      <hr>

      <p><a href="about:license#top">Return to top</a>.</p>
    </div>
  </body>
</html>
PK
!<������Cchrome/browser/content/browser/microsoft-translator-attribution.png�PNG


IHDR�&Gs�sIDATx��S�\[��k�m۶mێ��2�m[׶۶���îU���]=��_�ᗙi�9��G�;r�I'Wýx'�*���t��C'�,�#����QS⌀�VZ�.d�8�qj@W�n��W6��P�	57C�w�E~w{!
1��B���p$Ĺ�۾�G�Sm�|�?�;TGaHV�����0h�嘀!h��!��0�!h������o���E�+#1q������A�"�a=��
�`~�#x�1%q$=��ެ��	APkQ2x#�`T�e+�%x&x��s�P��4�"�B؂cp�=�\(���_��,N���p�a��r2�P��]I�l0�qz`oc
a��#�E� 
q�c��ې��f�W\1lL޲�>���w�{��bH�d��y��(����`��1r�
gB��h��Is�t��3�^�(��0H���'��!���dens>��ӑ�,v��F(^�K0-��]�	�pW6'BQ���9s�������]V_�uP�� ��4�9L?(^�=��"􀘚�9�=��Q%�.	]A1�_�' �AL���!Y��g8�]�^A	��
4F-��
X�t�8$P+�q_�T^�
����'�
�ixb�9����wCrOV:^�
A"�p��=�~��(�
�dE��,���c0UP�SPǞ{&d?1jn�/Oz��y�~���:Lj���vǣ"��k�܏'�H�a�j��A�����d�I������K'����DHVF��	j�/3j��de$�Q�`��
f ����yx��b�AW�EI�s*b0>5�P�A��z�çf4�B$�b�ǧ���9�c�U�|jF��8��Q܇ߡ�)n�#x"2�U���n=����l{��ݧ�Dk�A�t�
j�A�40�|x?CX�g��7�xspY���v�&P)�`��
�M`�E/�$��s%��F|�S �^�����#��PS]�f7�!(ub,fC��#��F�8,�V��)�O���k_�4J"���<�����id#
}	<�����$�������4GIhn��]�
&��}(
�AP�A=\�����5&o6B�;n@���

��^��5xE!��R|�:Is(��6�E1�C܎�P��\�g��po޽xϹ��%�X{\�ƛX5�Qxj��nlEe\��Q1m���ct�t�ɑs�p�9%2��*��•4j*@<��9�	�c!����菉��&���P�9�A<s�CM��c,b	�F0w�a��k�y4�x;q�3�`�\1C͍�_%��|B�)^V����C"JA�JML#Hn3
�=���}��|��?Wf@�/�@͓)�	j�@bP�%�;qr&��IBNx���Ħ��O�`�@�?����́8���`��
"�`~���R��`9���D0�!1xñ28�;�`P��<1E��?֣=.I5��k���[��)���L�D68��L�6$�#�<Ih+&cfǂ1��)�aPg:��$�9��_�Ԃ9�渤&6��@0�7��q+p��e8�!蒉`��k�39�&L��֠��-_
��9c�AMc������,��L����SƔ��)8!�|)S���H��5�r0���<$�a�k	
�ƽ��A�,�h�y��S�yc&B�y9�o���ȕŪ}���ˆ�#�\�̱�	f�<�Pj�Cbp
ԬFN^
�oU!�?��b0�p$$��PsI,|5o�>�	����GaԼI �޼L�Y�� ��h�P0?��8���k���z/�ِ@	��-�yj�A�)15��8����P"�s4�Q
�C����C�"(`0�[��j�N�\
lB_TE��Rz,��s40��1zA����Z��O���w�Lk0[��&�㷬V���&�'�ԔJf���33����3/��`���`p4���\$�x����8��u��)�`�Fg�O0� ��#�D܀!X��؀%h\j����+Rϼ�N���Ԃ����`��x�Y'�c�[�yh��!x�/�g���o>�$X��1�-�R��1%��������?���&��eIEND�B`�PK
!<�����>�>5chrome/browser/content/browser/migration/migration.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 kIMig = Ci.nsIBrowserProfileMigrator;
const kIPStartup = Ci.nsIProfileStartup;

ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource:///modules/MigrationUtils.jsm");

var MigrationWizard = { /* exported MigrationWizard */
  _source: "",                  // Source Profile Migrator ContractID suffix
  _itemsFlags: kIMig.ALL,       // Selected Import Data Sources (16-bit bitfield)
  _selectedProfile: null,       // Selected Profile name to import from
  _wiz: null,
  _migrator: null,
  _autoMigrate: null,

  init() {
    let os = Services.obs;
    os.addObserver(this, "Migration:Started");
    os.addObserver(this, "Migration:ItemBeforeMigrate");
    os.addObserver(this, "Migration:ItemAfterMigrate");
    os.addObserver(this, "Migration:ItemError");
    os.addObserver(this, "Migration:Ended");

    this._wiz = document.documentElement;

    let args = window.arguments;
    let entryPointId = args[0] || MigrationUtils.MIGRATION_ENTRYPOINT_UNKNOWN;
    Services.telemetry.getHistogramById("FX_MIGRATION_ENTRY_POINT").add(entryPointId);
    this.isInitialMigration = entryPointId == MigrationUtils.MIGRATION_ENTRYPOINT_FIRSTRUN;

    if (args.length > 1) {
      this._source = args[1];
      this._migrator = args[2] instanceof kIMig ? args[2] : null;
      this._autoMigrate = args[3].QueryInterface(kIPStartup);
      this._skipImportSourcePage = args[4];
      if (this._migrator && args[5]) {
        let sourceProfiles = this.spinResolve(this._migrator.getSourceProfiles());
        this._selectedProfile = sourceProfiles.find(profile => profile.id == args[5]);
      }

      if (this._autoMigrate) {
        // Show the "nothing" option in the automigrate case to provide an
        // easily identifiable way to avoid migration and create a new profile.
        document.getElementById("nothing").hidden = false;
      }
    }

    this.onImportSourcePageShow();
  },

  uninit() {
    var os = Services.obs;
    os.removeObserver(this, "Migration:Started");
    os.removeObserver(this, "Migration:ItemBeforeMigrate");
    os.removeObserver(this, "Migration:ItemAfterMigrate");
    os.removeObserver(this, "Migration:ItemError");
    os.removeObserver(this, "Migration:Ended");
    MigrationUtils.finishMigration();
  },

  spinResolve(promise) {
    let canAdvance = this._wiz.canAdvance;
    let canRewind = this._wiz.canRewind;
    this._wiz.canAdvance = false;
    this._wiz.canRewind = false;
    let result = MigrationUtils.spinResolve(promise);
    this._wiz.canAdvance = canAdvance;
    this._wiz.canRewind = canRewind;
    return result;
  },

  onWizardCancel() {
    MigrationUtils.forceExitSpinResolve();
    return true;
  },

  // 1 - Import Source
  onImportSourcePageShow() {
    // Show warning message to close the selected browser when needed
    let toggleCloseBrowserWarning = () => {
      let visibility = "hidden";
      if (group.selectedItem.id != "nothing") {
        let migrator = this.spinResolve(MigrationUtils.getMigrator(group.selectedItem.id));
        visibility = migrator.sourceLocked ? "visible" : "hidden";
      }
      document.getElementById("closeSourceBrowser").style.visibility = visibility;
    };
    this._wiz.canRewind = false;

    var selectedMigrator = null;
    this._availableMigrators = [];

    // Figure out what source apps are are available to import from:
    var group = document.getElementById("importSourceGroup");
    for (var i = 0; i < group.childNodes.length; ++i) {
      var migratorKey = group.childNodes[i].id;
      if (migratorKey != "nothing") {
        var migrator = this.spinResolve(MigrationUtils.getMigrator(migratorKey));
        if (migrator) {
          // Save this as the first selectable item, if we don't already have
          // one, or if it is the migrator that was passed to us.
          if (!selectedMigrator || this._source == migratorKey)
            selectedMigrator = group.childNodes[i];
          this._availableMigrators.push([migratorKey, migrator]);
        } else {
          // Hide this option
          group.childNodes[i].hidden = true;
        }
      }
    }
    if (this.isInitialMigration) {
      Services.telemetry.getHistogramById("FX_STARTUP_MIGRATION_BROWSER_COUNT")
        .add(this._availableMigrators.length);
      let defaultBrowser = MigrationUtils.getMigratorKeyForDefaultBrowser();
      // This will record 0 for unknown default browser IDs.
      defaultBrowser = MigrationUtils.getSourceIdForTelemetry(defaultBrowser);
      Services.telemetry.getHistogramById("FX_STARTUP_MIGRATION_EXISTING_DEFAULT_BROWSER")
        .add(defaultBrowser);
    }

    group.addEventListener("command", toggleCloseBrowserWarning);

    if (selectedMigrator) {
      group.selectedItem = selectedMigrator;
      toggleCloseBrowserWarning();
    } else {
      // We didn't find a migrator, notify the user
      document.getElementById("noSources").hidden = false;

      this._wiz.canAdvance = false;

      document.getElementById("importBookmarks").hidden = true;
      document.getElementById("importAll").hidden = true;
    }

    // Advance to the next page if the caller told us to.
    if (this._migrator && this._skipImportSourcePage) {
      this._wiz.advance();
      this._wiz.canRewind = false;
    }
  },

  onImportSourcePageAdvanced() {
    var newSource = document.getElementById("importSourceGroup").selectedItem.id;

    if (newSource == "nothing") {
      // Need to do telemetry here because we're closing the dialog before we get to
      // do actual migration. For actual migration, this doesn't happen until after
      // migration takes place.
      Services.telemetry.getHistogramById("FX_MIGRATION_SOURCE_BROWSER")
                        .add(MigrationUtils.getSourceIdForTelemetry("nothing"));
      document.documentElement.cancel();
      return false;
    }

    if (!this._migrator || (newSource != this._source)) {
      // Create the migrator for the selected source.
      this._migrator = this.spinResolve(MigrationUtils.getMigrator(newSource));

      this._itemsFlags = kIMig.ALL;
      this._selectedProfile = null;
    }
    this._source = newSource;

    // check for more than one source profile
    var sourceProfiles = this.spinResolve(this._migrator.getSourceProfiles());
    if (this._skipImportSourcePage) {
      this._wiz.currentPage.next = "migrating";
    } else if (sourceProfiles && sourceProfiles.length > 1) {
      this._wiz.currentPage.next = "selectProfile";
    } else {
      if (this._autoMigrate)
        this._wiz.currentPage.next = "migrating";
      else
        this._wiz.currentPage.next = "importItems";

      if (sourceProfiles && sourceProfiles.length == 1)
        this._selectedProfile = sourceProfiles[0];
      else
        this._selectedProfile = null;
    }
    return undefined;
  },

  // 2 - [Profile Selection]
  onSelectProfilePageShow() {
    // Disabling this for now, since we ask about import sources in automigration
    // too and don't want to disable the back button
    // if (this._autoMigrate)
    //   document.documentElement.getButton("back").disabled = true;

    var profiles = document.getElementById("profiles");
    while (profiles.hasChildNodes())
      profiles.firstChild.remove();

    // Note that this block is still reached even if the user chose 'From File'
    // and we canceled the dialog.  When that happens, _migrator will be null.
    if (this._migrator) {
      var sourceProfiles = this.spinResolve(this._migrator.getSourceProfiles());

      for (let profile of sourceProfiles) {
        var item = document.createElement("radio");
        item.id = profile.id;
        item.setAttribute("label", profile.name);
        profiles.appendChild(item);
      }
    }

    profiles.selectedItem = this._selectedProfile ? document.getElementById(this._selectedProfile.id) : profiles.firstChild;
  },

  onSelectProfilePageRewound() {
    var profiles = document.getElementById("profiles");
    let sourceProfiles = this.spinResolve(this._migrator.getSourceProfiles());
    this._selectedProfile = sourceProfiles.find(
      profile => profile.id == profiles.selectedItem.id
    ) || null;
  },

  onSelectProfilePageAdvanced() {
    var profiles = document.getElementById("profiles");
    let sourceProfiles = this.spinResolve(this._migrator.getSourceProfiles());
    this._selectedProfile = sourceProfiles.find(
      profile => profile.id == profiles.selectedItem.id
    ) || null;

    // If we're automigrating or just doing bookmarks don't show the item selection page
    if (this._autoMigrate)
      this._wiz.currentPage.next = "migrating";
  },

  // 3 - ImportItems
  onImportItemsPageShow() {
    var dataSources = document.getElementById("dataSources");
    while (dataSources.hasChildNodes())
      dataSources.firstChild.remove();

    var items = this.spinResolve(this._migrator.getMigrateData(this._selectedProfile,
                                                               this._autoMigrate));
    for (var i = 0; i < 16; ++i) {
      var itemID = (items >> i) & 0x1 ? Math.pow(2, i) : 0;
      if (itemID > 0) {
        var checkbox = document.createElement("checkbox");
        checkbox.id = itemID;
        checkbox.setAttribute("label",
          MigrationUtils.getLocalizedString(itemID + "_" + this._source));
        dataSources.appendChild(checkbox);
        if (!this._itemsFlags || this._itemsFlags & itemID)
          checkbox.checked = true;
      }
    }
  },

  onImportItemsPageRewound() {
    this._wiz.canAdvance = true;
    this.onImportItemsPageAdvanced();
  },

  onImportItemsPageAdvanced() {
    var dataSources = document.getElementById("dataSources");
    this._itemsFlags = 0;
    for (var i = 0; i < dataSources.childNodes.length; ++i) {
      var checkbox = dataSources.childNodes[i];
      if (checkbox.localName == "checkbox" && checkbox.checked)
        this._itemsFlags |= parseInt(checkbox.id);
    }
  },

  onImportItemCommand() {
    var items = document.getElementById("dataSources");
    var checkboxes = items.getElementsByTagName("checkbox");

    var oneChecked = false;
    for (var i = 0; i < checkboxes.length; ++i) {
      if (checkboxes[i].checked) {
        oneChecked = true;
        break;
      }
    }

    this._wiz.canAdvance = oneChecked;
  },

  // 4 - Migrating
  onMigratingPageShow() {
    this._wiz.getButton("cancel").disabled = true;
    this._wiz.canRewind = false;
    this._wiz.canAdvance = false;

    // When automigrating, show all of the data that can be received from this source.
    if (this._autoMigrate) {
      this._itemsFlags =
        this.spinResolve(this._migrator.getMigrateData(this._selectedProfile,
                                                       this._autoMigrate));
    }

    this._listItems("migratingItems");
    setTimeout(() => this.onMigratingMigrate(), 0);
  },

  async onMigratingMigrate() {
    await this._migrator.migrate(this._itemsFlags, this._autoMigrate, this._selectedProfile);

    Services.telemetry.getHistogramById("FX_MIGRATION_SOURCE_BROWSER")
                      .add(MigrationUtils.getSourceIdForTelemetry(this._source));
    if (!this._autoMigrate) {
      let hist = Services.telemetry.getKeyedHistogramById("FX_MIGRATION_USAGE");
      let exp = 0;
      let items = this._itemsFlags;
      while (items) {
        if (items & 1) {
          hist.add(this._source, exp);
        }
        items = items >> 1;
        exp++;
      }
    }
  },

  _listItems(aID) {
    var items = document.getElementById(aID);
    while (items.hasChildNodes())
      items.firstChild.remove();

    var itemID;
    for (var i = 0; i < 16; ++i) {
      itemID = (this._itemsFlags >> i) & 0x1 ? Math.pow(2, i) : 0;
      if (itemID > 0) {
        var label = document.createElement("label");
        label.id = itemID + "_migrated";
        try {
          label.setAttribute("value",
            MigrationUtils.getLocalizedString(itemID + "_" + this._source));
          items.appendChild(label);
        } catch (e) {
          // if the block above throws, we've enumerated all the import data types we
          // currently support and are now just wasting time, break.
          break;
        }
      }
    }
  },

  observe(aSubject, aTopic, aData) {
    var label;
    switch (aTopic) {
      case "Migration:Started":
        break;
      case "Migration:ItemBeforeMigrate":
        label = document.getElementById(aData + "_migrated");
        if (label)
          label.setAttribute("style", "font-weight: bold");
        break;
      case "Migration:ItemAfterMigrate":
        label = document.getElementById(aData + "_migrated");
        if (label)
          label.removeAttribute("style");
        break;
      case "Migration:Ended":
        if (this.isInitialMigration) {
          // Ensure errors in reporting data recency do not affect the rest of the migration.
          try {
            this.reportDataRecencyTelemetry();
          } catch (ex) {
            Cu.reportError(ex);
          }
        }
        if (this._autoMigrate) {
          // We're done now.
          this._wiz.canAdvance = true;
          this._wiz.advance();

          setTimeout(close, 5000);
        } else {
          this._wiz.canAdvance = true;
          var nextButton = this._wiz.getButton("next");
          nextButton.click();
        }
        break;
      case "Migration:ItemError":
        let type = "undefined";
        let numericType = parseInt(aData);
        switch (numericType) {
          case Ci.nsIBrowserProfileMigrator.SETTINGS:
            type = "settings";
            break;
          case Ci.nsIBrowserProfileMigrator.COOKIES:
            type = "cookies";
            break;
          case Ci.nsIBrowserProfileMigrator.HISTORY:
            type = "history";
            break;
          case Ci.nsIBrowserProfileMigrator.FORMDATA:
            type = "form data";
            break;
          case Ci.nsIBrowserProfileMigrator.PASSWORDS:
            type = "passwords";
            break;
          case Ci.nsIBrowserProfileMigrator.BOOKMARKS:
            type = "bookmarks";
            break;
          case Ci.nsIBrowserProfileMigrator.OTHERDATA:
            type = "misc. data";
            break;
        }
        Services.console.logStringMessage("some " + type + " did not successfully migrate.");
        Services.telemetry.getKeyedHistogramById("FX_MIGRATION_ERRORS")
                          .add(this._source, Math.log2(numericType));
        break;
    }
  },

  onDonePageShow() {
    this._wiz.getButton("cancel").disabled = true;
    this._wiz.canRewind = false;
    this._listItems("doneItems");
  },

  reportDataRecencyTelemetry() {
    let histogram = Services.telemetry.getKeyedHistogramById("FX_STARTUP_MIGRATION_DATA_RECENCY");
    let lastUsedPromises = [];
    for (let [key, migrator] of this._availableMigrators) {
      // No block-scoped let in for...of loop conditions, so get the source:
      let localKey = key;
      lastUsedPromises.push(migrator.getLastUsedDate().then(date => {
        const ONE_YEAR = 24 * 365;
        let diffInHours = Math.round((Date.now() - date) / (60 * 60 * 1000));
        if (diffInHours > ONE_YEAR) {
          diffInHours = ONE_YEAR;
        }
        histogram.add(localKey, diffInHours);
        return [localKey, diffInHours];
      }));
    }
    Promise.all(lastUsedPromises).then(migratorUsedTimeDiff => {
      // Sort low to high.
      migratorUsedTimeDiff.sort(([keyA, diffA], [keyB, diffB]) => diffA - diffB); /* eslint no-unused-vars: off */
      let usedMostRecentBrowser = migratorUsedTimeDiff.length && this._source == migratorUsedTimeDiff[0][0];
      let usedRecentBrowser =
        Services.telemetry.getKeyedHistogramById("FX_STARTUP_MIGRATION_USED_RECENT_BROWSER");
      usedRecentBrowser.add(this._source, usedMostRecentBrowser);
    });
  },
};
PK
!<�"��6chrome/browser/content/browser/migration/migration.xul<?xml version="1.0"?>

<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>

<!DOCTYPE dialog SYSTEM "chrome://browser/locale/migration/migration.dtd" >

<wizard id="migrationWizard"
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        windowtype="Browser:MigrationWizard"
        title="&migrationWizard.title;"
        onload="MigrationWizard.init()"
        onunload="MigrationWizard.uninit()"
        style="width: 40em;"
        buttons="accept,cancel"
        branded="true"
        onwizardcancel="return MigrationWizard.onWizardCancel();">

  <script type="application/javascript" src="chrome://browser/content/migration/migration.js"/>

  <wizardpage id="importSource" pageid="importSource" next="selectProfile"
              label="&importSource.title;"
              onpageadvanced="return MigrationWizard.onImportSourcePageAdvanced();">
    <description id="importAll" control="importSourceGroup">&importFromUnix.label;</description>
    <description id="importBookmarks" control="importSourceGroup" hidden="true">&importFromBookmarks.label;</description>

    <radiogroup id="importSourceGroup" align="start">
      <radio id="firefox"   label="&importFromFirefox.label;"   accesskey="&importFromFirefox.accesskey;"/>
      <radio id="chrome"    label="&importFromChrome.label;"    accesskey="&importFromChrome.accesskey;"/>
      <radio id="chrome-beta" label="&importFromChromeBeta.label;" accesskey="&importFromChromeBeta.accesskey;"/>
      <radio id="chrome-dev"  label="&importFromChromeDev.label;"  accesskey="&importFromChromeDev.accesskey;"/>
      <radio id="chromium"  label="&importFromChromium.label;"  accesskey="&importFromChromium.accesskey;"/>
      <radio id="nothing"   label="&importFromNothing.label;"   accesskey="&importFromNothing.accesskey;" hidden="true"/>
    </radiogroup>
    <label id="noSources" hidden="true">&noMigrationSources.label;</label>
    <spacer flex="1"/>
    <description class="header" id="closeSourceBrowser" style="visibility:hidden">&closeSourceBrowser.label;</description>
  </wizardpage>

  <wizardpage id="selectProfile" pageid="selectProfile" label="&selectProfile.title;"
              next="importItems"
              onpageshow="return MigrationWizard.onSelectProfilePageShow();"
              onpagerewound="return MigrationWizard.onSelectProfilePageRewound();"
              onpageadvanced="return MigrationWizard.onSelectProfilePageAdvanced();">
    <description control="profiles">&selectProfile.label;</description>

    <radiogroup id="profiles" align="left"/>
  </wizardpage>

  <wizardpage id="importItems" pageid="importItems" label="&importItems.title;"
              next="migrating"
              onpageshow="return MigrationWizard.onImportItemsPageShow();"
              onpagerewound="return MigrationWizard.onImportItemsPageRewound();"
              onpageadvanced="return MigrationWizard.onImportItemsPageAdvanced();"
              oncommand="MigrationWizard.onImportItemCommand();">
    <description control="dataSources">&importItems.label;</description>

    <vbox id="dataSources" style="overflow: auto; -moz-appearance: listbox" align="left" flex="1" role="group"/>
  </wizardpage>

  <wizardpage id="migrating" pageid="migrating" label="&migrating.title;"
              next="done"
              onpageshow="MigrationWizard.onMigratingPageShow();">
    <description control="migratingItems">&migrating.label;</description>

    <vbox id="migratingItems" style="overflow: auto;" align="left" role="group"/>
  </wizardpage>

  <wizardpage id="done" pageid="done" label="&done.title;"
              onpageshow="MigrationWizard.onDonePageShow();">
    <description control="doneItems">&done.label;</description>

    <vbox id="doneItems" style="overflow: auto;" align="left" role="group"/>
  </wizardpage>

</wizard>

PK
!<�`P,��/chrome/browser/content/browser/nsContextMenu.js/* -*- tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=2 sw=2 sts=2 et tw=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/. */

ChromeUtils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
ChromeUtils.import("resource://gre/modules/BrowserUtils.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");

XPCOMUtils.defineLazyModuleGetters(this, {
  SpellCheckHelper: "resource://gre/modules/InlineSpellChecker.jsm",
  LoginHelper: "resource://gre/modules/LoginHelper.jsm",
  LoginManagerContextMenu: "resource://gre/modules/LoginManagerContextMenu.jsm",
  WebNavigationFrames: "resource://gre/modules/WebNavigationFrames.jsm",
  ContextualIdentityService: "resource://gre/modules/ContextualIdentityService.jsm",
  DevToolsShim: "chrome://devtools-shim/content/DevToolsShim.jsm",
  NetUtil: "resource://gre/modules/NetUtil.jsm",
});

var gContextMenuContentData = null;

function setContextMenuContentData(data) {
  gContextMenuContentData = data;
}

function openContextMenu(aMessage) {
  let data = aMessage.data;
  let browser = aMessage.target;
  let spellInfo = data.spellInfo;

  // ContextMenu.jsm sends us the target as a CPOW only so that
  // we can send that CPOW back down to the content process and
  // have it resolve to a DOM node. The parent should not attempt
  // to access any properties on this CPOW (in fact, doing so
  // will throw an exception).
  data.context.targetAsCPOW = aMessage.objects.targetAsCPOW;

  if (spellInfo) {
    spellInfo.target = aMessage.target.messageManager;
  }

  let documentURIObject = makeURI(data.docLocation,
                                  data.charSet,
                                  makeURI(data.baseURI));
  gContextMenuContentData = { context: data.context,
                              isRemote: data.isRemote,
                              popupNodeSelectors: data.popupNodeSelectors,
                              browser,
                              editFlags: data.editFlags,
                              spellInfo,
                              principal: data.principal,
                              customMenuItems: data.customMenuItems,
                              documentURIObject,
                              docLocation: data.docLocation,
                              charSet: data.charSet,
                              referrer: data.referrer,
                              referrerPolicy: data.referrerPolicy,
                              contentType: data.contentType,
                              contentDisposition: data.contentDisposition,
                              frameOuterWindowID: data.frameOuterWindowID,
                              selectionInfo: data.selectionInfo,
                              disableSetDesktopBackground: data.disableSetDesktopBg,
                              loginFillInfo: data.loginFillInfo,
                              parentAllowsMixedContent: data.parentAllowsMixedContent,
                              userContextId: data.userContextId,
                            };

  let popup = browser.ownerDocument.getElementById("contentAreaContextMenu");
  let context = gContextMenuContentData.context;

  // The event is a CPOW that can't be passed into the native openPopupAtScreen
  // function. Therefore we synthesize a new MouseEvent to propagate the
  // inputSource to the subsequently triggered popupshowing event.
  var newEvent = document.createEvent("MouseEvent");
  newEvent.initNSMouseEvent("contextmenu", true, true, null, 0, context.screenX, context.screenY,
                            0, 0, false, false, false, false, 0, null, 0, context.mozInputSource);

  popup.openPopupAtScreen(newEvent.screenX, newEvent.screenY, true, newEvent);
}

function nsContextMenu(aXulMenu, aIsShift) {
  this.shouldDisplay = true;
  this.initMenu(aXulMenu, aIsShift);
}

// Prototype for nsContextMenu "class."
nsContextMenu.prototype = {
  initMenu: function CM_initMenu(aXulMenu, aIsShift) {
    // Get contextual info.
    this.setContext();

    if (!this.shouldDisplay)
      return;

    this.hasPageMenu = false;
    this.isContentSelected = !this.selectionInfo.docSelectionIsCollapsed;
    if (!aIsShift) {
      if (this.isRemote) {
        this.hasPageMenu =
          PageMenuParent.addToPopup(gContextMenuContentData.customMenuItems,
                                    this.browser, aXulMenu);
      } else {
        this.hasPageMenu = PageMenuParent.buildAndAddToPopup(this.target, aXulMenu);
      }

      let subject = {
        menu: aXulMenu,
        tab: gBrowser ? gBrowser.getTabForBrowser(this.browser) : undefined,
        isContentSelected: this.isContentSelected,
        inFrame: this.inFrame,
        isTextSelected: this.isTextSelected,
        onTextInput: this.onTextInput,
        onLink: this.onLink,
        onImage: this.onImage,
        onVideo: this.onVideo,
        onAudio: this.onAudio,
        onCanvas: this.onCanvas,
        onEditable: this.onEditable,
        onSpellcheckable: this.onSpellcheckable,
        onPassword: this.onPassword,
        srcUrl: this.mediaURL,
        frameUrl: gContextMenuContentData ? gContextMenuContentData.docLocation : undefined,
        pageUrl: this.browser ? this.browser.currentURI.spec : undefined,
        linkText: this.linkTextStr,
        linkUrl: this.linkURL,
        selectionText: this.isTextSelected ? this.selectionInfo.fullText : undefined,
        frameId: this.frameOuterWindowID,
      };
      subject.wrappedJSObject = subject;
      Services.obs.notifyObservers(subject, "on-build-contextmenu");
    }

    this.isFrameImage = document.getElementById("isFrameImage");
    this.ellipsis = "\u2026";
    try {
      this.ellipsis = Services.prefs.getComplexValue("intl.ellipsis",
                                                     Ci.nsIPrefLocalizedString).data;
    } catch (e) { }

    // Reset after "on-build-contextmenu" notification in case selection was
    // changed during the notification.
    this.isContentSelected = !this.selectionInfo.docSelectionIsCollapsed;
    this.onPlainTextLink = false;

    let bookmarkPage = document.getElementById("context-bookmarkpage");
    if (bookmarkPage)
      BookmarkingUI.onCurrentPageContextPopupShowing();

    // Initialize (disable/remove) menu items.
    this.initItems();

    // Register this opening of the menu with telemetry:
    this._checkTelemetryForMenu(aXulMenu);
  },

  setContext() {
    let context = Object.create(null);
    this.isRemote = false;

    if (gContextMenuContentData) {
      context = gContextMenuContentData.context;
      gContextMenuContentData.context = null;
      this.isRemote = gContextMenuContentData.isRemote;
    }

    this.shouldDisplay = context.shouldDisplay;

    // Assign what's _possibly_ needed from `context` sent by ContextMenu.jsm
    // Keep this consistent with the similar code in ContextMenu's _setContext
    this.bgImageURL          = context.bgImageURL;
    this.imageDescURL        = context.imageDescURL;
    this.imageInfo           = context.imageInfo;
    this.mediaURL            = context.mediaURL;
    this.webExtBrowserType   = context.webExtBrowserType;

    this.canSpellCheck       = context.canSpellCheck;
    this.hasBGImage          = context.hasBGImage;
    this.hasMultipleBGImages = context.hasMultipleBGImages;
    this.isDesignMode        = context.isDesignMode;
    this.inFrame             = context.inFrame;
    this.inSrcdocFrame       = context.inSrcdocFrame;
    this.inSyntheticDoc      = context.inSyntheticDoc;
    this.inTabBrowser        = context.inTabBrowser;
    this.inWebExtBrowser     = context.inWebExtBrowser;

    this.link                = context.link;
    this.linkDownload        = context.linkDownload;
    this.linkHasNoReferrer   = context.linkHasNoReferrer;
    this.linkProtocol        = context.linkProtocol;
    this.linkTextStr         = context.linkTextStr;
    this.linkURL             = context.linkURL;
    this.linkURI             = this.getLinkURI(); // can't send; regenerate

    this.onAudio             = context.onAudio;
    this.onCanvas            = context.onCanvas;
    this.onCompletedImage    = context.onCompletedImage;
    this.onCTPPlugin         = context.onCTPPlugin;
    this.onDRMMedia          = context.onDRMMedia;
    this.onEditable          = context.onEditable;
    this.onImage             = context.onImage;
    this.onKeywordField      = context.onKeywordField;
    this.onLink              = context.onLink;
    this.onLoadedImage       = context.onLoadedImage;
    this.onMailtoLink        = context.onMailtoLink;
    this.onMathML            = context.onMathML;
    this.onMozExtLink        = context.onMozExtLink;
    this.onNumeric           = context.onNumeric;
    this.onPassword          = context.onPassword;
    this.onSaveableLink      = context.onSaveableLink;
    this.onSpellcheckable    = context.onSpellcheckable;
    this.onTextInput         = context.onTextInput;
    this.onVideo             = context.onVideo;

    this.target = this.isRemote ? context.target : document.popupNode;
    this.targetAsCPOW = context.targetAsCPOW;

    this.principal = context.principal;
    this.frameOuterWindowID = context.frameOuterWindowID;

    this.inSyntheticDoc = context.inSyntheticDoc;

    // Everything after this isn't sent directly from ContextMenu
    this.ownerDoc = this.target.ownerDocument;

    // Remember the CSS selectors corresponding to clicked node. gContextMenuContentData
    // can be null if the menu was triggered by tests in which case use an empty array.
    this.targetSelectors = gContextMenuContentData
                           ? gContextMenuContentData.popupNodeSelectors
                           : [];

    if (this.isRemote) {
      this.browser = gContextMenuContentData.browser;
      this.selectionInfo = gContextMenuContentData.selectionInfo;
    } else {
      this.browser = this.ownerDoc.defaultView
                         .QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsIWebNavigation)
                         .QueryInterface(Ci.nsIDocShell)
                         .chromeEventHandler;
      this.selectionInfo = BrowserUtils.getSelectionDetails(window);
    }

    this.textSelected      = this.selectionInfo.text;
    this.isTextSelected    = this.textSelected.length != 0;
    this.webExtBrowserType = this.browser.getAttribute("webextension-view-type");
    this.inWebExtBrowser   = !!this.webExtBrowserType;
    this.inTabBrowser      = this.browser.ownerGlobal.gBrowser ?
      !!this.browser.ownerGlobal.gBrowser.getTabForBrowser(this.browser) : false;

    if (context.shouldInitInlineSpellCheckerUINoChildren) {
      if (this.isRemote) {
        InlineSpellCheckerUI.initFromRemote(gContextMenuContentData.spellInfo);
      } else {
        InlineSpellCheckerUI.init(this.target.QueryInterface(Ci.nsIDOMNSEditableElement).editor);
        InlineSpellCheckerUI.initFromEvent(document.popupRangeParent,
                                           document.popupRangeOffset);
      }
    }

    if (context.shouldInitInlineSpellCheckerUIWithChildren) {
      if (this.isRemote) {
        InlineSpellCheckerUI.initFromRemote(gContextMenuContentData.spellInfo);
      } else {
        var targetWin = this.ownerDoc.defaultView;
        var editingSession = targetWin.QueryInterface(Ci.nsIInterfaceRequestor)
                                      .getInterface(Ci.nsIWebNavigation)
                                      .QueryInterface(Ci.nsIInterfaceRequestor)
                                      .getInterface(Ci.nsIEditingSession);

        InlineSpellCheckerUI.init(editingSession.getEditorForWindow(targetWin));
        InlineSpellCheckerUI.initFromEvent(document.popupRangeParent,
                                           document.popupRangeOffset);
      }

      let canSpell = InlineSpellCheckerUI.canSpellCheck && this.canSpellCheck;
      this.showItem("spell-check-enabled", canSpell);
      this.showItem("spell-separator", canSpell);
    }
  },  // setContext

  hiding: function CM_hiding() {
    if (this.browser) {
      this.browser.messageManager.sendAsyncMessage("ContextMenu:Hiding");
    }

    gContextMenuContentData = null;
    InlineSpellCheckerUI.clearSuggestionsFromMenu();
    InlineSpellCheckerUI.clearDictionaryListFromMenu();
    InlineSpellCheckerUI.uninit();
    if (Cu.isModuleLoaded("resource://gre/modules/LoginManagerContextMenu.jsm")) {
      LoginManagerContextMenu.clearLoginsFromMenu(document);
    }

    // This handler self-deletes, only run it if it is still there:
    if (this._onPopupHiding) {
      this._onPopupHiding();
    }
  },

  initItems: function CM_initItems() {
    this.initPageMenuSeparator();
    this.initOpenItems();
    this.initNavigationItems();
    this.initViewItems();
    this.initMiscItems();
    this.initSpellingItems();
    this.initSaveItems();
    this.initClipboardItems();
    this.initMediaPlayerItems();
    this.initLeaveDOMFullScreenItems();
    this.initClickToPlayItems();
    this.initPasswordManagerItems();
    this.initSyncItems();
  },

  initPageMenuSeparator: function CM_initPageMenuSeparator() {
    this.showItem("page-menu-separator", this.hasPageMenu);
  },

  initOpenItems: function CM_initOpenItems() {
    var isMailtoInternal = false;
    if (this.onMailtoLink) {
      var mailtoHandler = Cc["@mozilla.org/uriloader/external-protocol-service;1"].
                          getService(Ci.nsIExternalProtocolService).
                          getProtocolHandlerInfo("mailto");
      isMailtoInternal = (!mailtoHandler.alwaysAskBeforeHandling &&
                          mailtoHandler.preferredAction == Ci.nsIHandlerInfo.useHelperApp &&
                          (mailtoHandler.preferredApplicationHandler instanceof Ci.nsIWebHandlerApp));
    }

    if (this.isTextSelected && !this.onLink &&
        this.selectionInfo && this.selectionInfo.linkURL) {
      this.linkURL = this.selectionInfo.linkURL;
      try {
        this.linkURI = makeURI(this.linkURL);
      } catch (ex) {}

      this.linkTextStr = this.selectionInfo.linkText;
      this.onPlainTextLink = true;
    }

    var inContainer = false;
    if (gContextMenuContentData.userContextId) {
      inContainer = true;
      var item = document.getElementById("context-openlinkincontainertab");

      item.setAttribute("data-usercontextid", gContextMenuContentData.userContextId);

      var label =
        ContextualIdentityService.getUserContextLabel(gContextMenuContentData.userContextId);
      item.setAttribute("label",
         gBrowserBundle.formatStringFromName("userContextOpenLink.label",
                                             [label], 1));
    }

    var shouldShow = this.onSaveableLink || isMailtoInternal || this.onPlainTextLink;
    var isWindowPrivate = PrivateBrowsingUtils.isWindowPrivate(window);
    var showContainers = Services.prefs.getBoolPref("privacy.userContext.enabled");
    this.showItem("context-openlink", shouldShow && !isWindowPrivate);
    this.showItem("context-openlinkprivate", shouldShow && PrivateBrowsingUtils.enabled);
    this.showItem("context-openlinkintab", shouldShow && !inContainer);
    this.showItem("context-openlinkincontainertab", shouldShow && inContainer);
    this.showItem("context-openlinkinusercontext-menu", shouldShow && !isWindowPrivate && showContainers);
    this.showItem("context-openlinkincurrent", this.onPlainTextLink);
    this.showItem("context-sep-open", shouldShow);
  },

  initNavigationItems: function CM_initNavigationItems() {
    var shouldShow = !(this.isContentSelected || this.onLink || this.onImage ||
                       this.onCanvas || this.onVideo || this.onAudio ||
                       this.onTextInput) && this.inTabBrowser;
    this.showItem("context-navigation", shouldShow);
    this.showItem("context-sep-navigation", shouldShow);

    let stopped = XULBrowserWindow.stopCommand.getAttribute("disabled") == "true";

    let stopReloadItem = "";
    if (shouldShow || !this.inTabBrowser) {
      stopReloadItem = (stopped || !this.inTabBrowser) ? "reload" : "stop";
    }

    this.showItem("context-reload", stopReloadItem == "reload");
    this.showItem("context-stop", stopReloadItem == "stop");

    // XXX: Stop is determined in browser.js; the canStop broadcaster is broken
    // this.setItemAttrFromNode( "context-stop", "disabled", "canStop" );
  },

  initLeaveDOMFullScreenItems: function CM_initLeaveFullScreenItem() {
    // only show the option if the user is in DOM fullscreen
    var shouldShow = this.target.ownerDocument.fullscreen;
    this.showItem("context-leave-dom-fullscreen", shouldShow);

    // Explicitly show if in DOM fullscreen, but do not hide it has already been shown
    if (shouldShow)
        this.showItem("context-media-sep-commands", true);
  },

  initSaveItems: function CM_initSaveItems() {
    var shouldShow = !(this.onTextInput || this.onLink ||
                       this.isContentSelected || this.onImage ||
                       this.onCanvas || this.onVideo || this.onAudio);
    this.showItem("context-savepage", shouldShow);

    // Save link depends on whether we're in a link, or selected text matches valid URL pattern.
    this.showItem("context-savelink", this.onSaveableLink || this.onPlainTextLink);

    // Save image depends on having loaded its content, video and audio don't.
    this.showItem("context-saveimage", this.onLoadedImage || this.onCanvas);
    this.showItem("context-savevideo", this.onVideo);
    this.showItem("context-saveaudio", this.onAudio);
    this.showItem("context-video-saveimage", this.onVideo);
    this.setItemAttr("context-savevideo", "disabled", !this.mediaURL);
    this.setItemAttr("context-saveaudio", "disabled", !this.mediaURL);
    // Send media URL (but not for canvas, since it's a big data: URL)
    this.showItem("context-sendimage", this.onImage);
    this.showItem("context-sendvideo", this.onVideo);
    this.showItem("context-sendaudio", this.onAudio);
    let mediaIsBlob = this.mediaURL.startsWith("blob:");
    this.setItemAttr("context-sendvideo", "disabled", !this.mediaURL || mediaIsBlob);
    this.setItemAttr("context-sendaudio", "disabled", !this.mediaURL || mediaIsBlob);
  },

  initViewItems: function CM_initViewItems() {
    // View source is always OK, unless in directory listing.
    this.showItem("context-viewpartialsource-selection",
                  this.isContentSelected);
    this.showItem("context-viewpartialsource-mathml",
                  this.onMathML && !this.isContentSelected);

    var shouldShow = !(this.isContentSelected ||
                       this.onImage || this.onCanvas ||
                       this.onVideo || this.onAudio ||
                       this.onLink || this.onTextInput);

    var showInspect = this.inTabBrowser &&
                      Services.prefs.getBoolPref("devtools.inspector.enabled", true) &&
                      !Services.prefs.getBoolPref("devtools.policy.disabled", false);

    this.showItem("context-viewsource", shouldShow);
    this.showItem("context-viewinfo", shouldShow);
    // The page info is broken for WebExtension popups, as the browser is
    // destroyed when the popup is closed.
    this.setItemAttr("context-viewinfo", "disabled", this.webExtBrowserType === "popup");
    this.showItem("inspect-separator", showInspect);
    this.showItem("context-inspect", showInspect);

    this.showItem("context-sep-viewsource", shouldShow);

    // Set as Desktop background depends on whether an image was clicked on,
    // and only works if we have a shell service.
    var haveSetDesktopBackground = false;

    if (AppConstants.HAVE_SHELL_SERVICE &&
        Services.policies.isAllowed("setDesktopBackground")) {
      // Only enable Set as Desktop Background if we can get the shell service.
      var shell = getShellService();
      if (shell)
        haveSetDesktopBackground = shell.canSetDesktopBackground;
    }

    this.showItem("context-setDesktopBackground",
                  haveSetDesktopBackground && this.onLoadedImage);

    if (haveSetDesktopBackground && this.onLoadedImage) {
      document.getElementById("context-setDesktopBackground")
              .disabled = gContextMenuContentData.disableSetDesktopBackground;
    }

    // Reload image depends on an image that's not fully loaded
    this.showItem("context-reloadimage", (this.onImage && !this.onCompletedImage));

    // View image depends on having an image that's not standalone
    // (or is in a frame), or a canvas.
    this.showItem("context-viewimage", (this.onImage &&
                  (!this.inSyntheticDoc || this.inFrame)) || this.onCanvas);

    // View video depends on not having a standalone video.
    this.showItem("context-viewvideo", this.onVideo && (!this.inSyntheticDoc || this.inFrame));
    this.setItemAttr("context-viewvideo", "disabled", !this.mediaURL);

    // View background image depends on whether there is one, but don't make
    // background images of a stand-alone media document available.
    this.showItem("context-viewbgimage", shouldShow &&
                                         !this.hasMultipleBGImages &&
                                         !this.inSyntheticDoc);
    this.showItem("context-sep-viewbgimage", shouldShow &&
                                             !this.hasMultipleBGImages &&
                                             !this.inSyntheticDoc);
    document.getElementById("context-viewbgimage")
            .disabled = !this.hasBGImage;

    this.showItem("context-viewimageinfo", this.onImage);
    // The image info popup is broken for WebExtension popups, since the browser
    // is destroyed when the popup is closed.
    this.setItemAttr("context-viewimageinfo", "disabled", this.webExtBrowserType === "popup");
    this.showItem("context-viewimagedesc", this.onImage && this.imageDescURL !== "");
  },

  initMiscItems: function CM_initMiscItems() {
    // Use "Bookmark This Link" if on a link.
    let bookmarkPage = document.getElementById("context-bookmarkpage");
    this.showItem(bookmarkPage,
                  !(this.isContentSelected || this.onTextInput || this.onLink ||
                    this.onImage || this.onVideo || this.onAudio ||
                    this.onCanvas || this.inWebExtBrowser));
    bookmarkPage.setAttribute("tooltiptext", bookmarkPage.getAttribute("buttontooltiptext"));

    this.showItem("context-bookmarklink", (this.onLink && !this.onMailtoLink &&
                                           !this.onMozExtLink) || this.onPlainTextLink);
    this.showItem("context-keywordfield",
                  this.onTextInput && this.onKeywordField);
    this.showItem("frame", this.inFrame);

    let showSearchSelect = (this.isTextSelected || this.onLink) && !this.onImage;
    this.showItem("context-searchselect", showSearchSelect);
    if (showSearchSelect) {
      this.formatSearchContextItem();
    }

    // srcdoc cannot be opened separately due to concerns about web
    // content with about:srcdoc in location bar masquerading as trusted
    // chrome/addon content.
    // No need to also test for this.inFrame as this is checked in the parent
    // submenu.
    this.showItem("context-showonlythisframe", !this.inSrcdocFrame);
    this.showItem("context-openframeintab", !this.inSrcdocFrame);
    this.showItem("context-openframe", !this.inSrcdocFrame);
    this.showItem("context-bookmarkframe", !this.inSrcdocFrame);
    this.showItem("open-frame-sep", !this.inSrcdocFrame);

    this.showItem("frame-sep", this.inFrame && this.isTextSelected);

    // Hide menu entries for images, show otherwise
    if (this.inFrame) {
      if (BrowserUtils.mimeTypeIsTextBased(this.target.ownerDocument.contentType))
        this.isFrameImage.removeAttribute("hidden");
      else
        this.isFrameImage.setAttribute("hidden", "true");
    }

    // BiDi UI
    this.showItem("context-sep-bidi", !this.onNumeric && top.gBidiUI);
    this.showItem("context-bidi-text-direction-toggle",
                  this.onTextInput && !this.onNumeric && top.gBidiUI);
    this.showItem("context-bidi-page-direction-toggle",
                  !this.onTextInput && top.gBidiUI);
  },

  initSpellingItems() {
    var canSpell = InlineSpellCheckerUI.canSpellCheck &&
                   !InlineSpellCheckerUI.initialSpellCheckPending &&
                   this.canSpellCheck;
    let showDictionaries = canSpell && InlineSpellCheckerUI.enabled;
    var onMisspelling = InlineSpellCheckerUI.overMisspelling;
    var showUndo = canSpell && InlineSpellCheckerUI.canUndo();
    this.showItem("spell-check-enabled", canSpell);
    this.showItem("spell-separator", canSpell);
    document.getElementById("spell-check-enabled")
            .setAttribute("checked", canSpell && InlineSpellCheckerUI.enabled);

    this.showItem("spell-add-to-dictionary", onMisspelling);
    this.showItem("spell-undo-add-to-dictionary", showUndo);

    // suggestion list
    this.showItem("spell-suggestions-separator", onMisspelling || showUndo);
    if (onMisspelling) {
      var suggestionsSeparator =
        document.getElementById("spell-add-to-dictionary");
      var numsug =
        InlineSpellCheckerUI.addSuggestionsToMenu(suggestionsSeparator.parentNode,
                                                  suggestionsSeparator, 5);
      this.showItem("spell-no-suggestions", numsug == 0);
    } else {
      this.showItem("spell-no-suggestions", false);
    }

    // dictionary list
    this.showItem("spell-dictionaries", showDictionaries);
    if (canSpell) {
      var dictMenu = document.getElementById("spell-dictionaries-menu");
      var dictSep = document.getElementById("spell-language-separator");
      let count = InlineSpellCheckerUI.addDictionaryListToMenu(dictMenu, dictSep);
      this.showItem(dictSep, count > 0);
      this.showItem("spell-add-dictionaries-main", false);
    } else if (this.onSpellcheckable) {
      // when there is no spellchecker but we might be able to spellcheck
      // add the add to dictionaries item. This will ensure that people
      // with no dictionaries will be able to download them
      this.showItem("spell-language-separator", showDictionaries);
      this.showItem("spell-add-dictionaries-main", showDictionaries);
    } else {
      this.showItem("spell-add-dictionaries-main", false);
    }
  },

  initClipboardItems() {
    // Copy depends on whether there is selected text.
    // Enabling this context menu item is now done through the global
    // command updating system
    // this.setItemAttr( "context-copy", "disabled", !this.isTextSelected() );
    goUpdateGlobalEditMenuItems();

    this.showItem("context-undo", this.onTextInput);
    this.showItem("context-sep-undo", this.onTextInput);
    this.showItem("context-cut", this.onTextInput);
    this.showItem("context-copy",
                  this.isContentSelected || this.onTextInput);
    this.showItem("context-paste", this.onTextInput);
    this.showItem("context-delete", this.onTextInput);
    this.showItem("context-sep-paste", this.onTextInput);
    this.showItem("context-selectall", !(this.onLink || this.onImage ||
                                         this.onVideo || this.onAudio ||
                                         this.inSyntheticDoc) ||
                                       this.isDesignMode);
    this.showItem("context-sep-selectall", this.isContentSelected );

    // XXX dr
    // ------
    // nsDocumentViewer.cpp has code to determine whether we're
    // on a link or an image. we really ought to be using that...

    // Copy email link depends on whether we're on an email link.
    this.showItem("context-copyemail", this.onMailtoLink);

    // Copy link location depends on whether we're on a non-mailto link.
    this.showItem("context-copylink", this.onLink && !this.onMailtoLink);
    this.showItem("context-sep-copylink", this.onLink &&
                  (this.onImage || this.onVideo || this.onAudio));

    // Copy image contents depends on whether we're on an image.
    // Note: the element doesn't exist on all platforms, but showItem() takes
    // care of that by itself.
    this.showItem("context-copyimage-contents", this.onImage);

    // Copy image location depends on whether we're on an image.
    this.showItem("context-copyimage", this.onImage);
    this.showItem("context-copyvideourl", this.onVideo);
    this.showItem("context-copyaudiourl", this.onAudio);
    this.setItemAttr("context-copyvideourl", "disabled", !this.mediaURL);
    this.setItemAttr("context-copyaudiourl", "disabled", !this.mediaURL);
    this.showItem("context-sep-copyimage", this.onImage ||
                  this.onVideo || this.onAudio);
  },

  initMediaPlayerItems() {
    var onMedia = (this.onVideo || this.onAudio);
    // Several mutually exclusive items... play/pause, mute/unmute, show/hide
    this.showItem("context-media-play", onMedia && (this.target.paused || this.target.ended));
    this.showItem("context-media-pause", onMedia && !this.target.paused && !this.target.ended);
    this.showItem("context-media-mute", onMedia && !this.target.muted);
    this.showItem("context-media-unmute", onMedia && this.target.muted);
    this.showItem("context-media-playbackrate", onMedia && this.target.duration != Number.POSITIVE_INFINITY);
    this.showItem("context-media-loop", onMedia);
    this.showItem("context-media-showcontrols", onMedia && !this.target.controls);
    this.showItem("context-media-hidecontrols", this.target.controls && (this.onVideo || (this.onAudio && !this.inSyntheticDoc)));
    this.showItem("context-video-fullscreen", this.onVideo && !this.target.ownerDocument.fullscreen);
    this.showItem("context-media-eme-learnmore", this.onDRMMedia);
    this.showItem("context-media-eme-separator", this.onDRMMedia);

    // Disable them when there isn't a valid media source loaded.
    if (onMedia) {
      this.setItemAttr("context-media-playbackrate-050x", "checked", this.target.playbackRate == 0.5);
      this.setItemAttr("context-media-playbackrate-100x", "checked", this.target.playbackRate == 1.0);
      this.setItemAttr("context-media-playbackrate-125x", "checked", this.target.playbackRate == 1.25);
      this.setItemAttr("context-media-playbackrate-150x", "checked", this.target.playbackRate == 1.5);
      this.setItemAttr("context-media-playbackrate-200x", "checked", this.target.playbackRate == 2.0);
      this.setItemAttr("context-media-loop", "checked", this.target.loop);
      var hasError = this.target.error != null ||
                     this.target.networkState == this.target.NETWORK_NO_SOURCE;
      this.setItemAttr("context-media-play", "disabled", hasError);
      this.setItemAttr("context-media-pause", "disabled", hasError);
      this.setItemAttr("context-media-mute", "disabled", hasError);
      this.setItemAttr("context-media-unmute", "disabled", hasError);
      this.setItemAttr("context-media-playbackrate", "disabled", hasError);
      this.setItemAttr("context-media-playbackrate-050x", "disabled", hasError);
      this.setItemAttr("context-media-playbackrate-100x", "disabled", hasError);
      this.setItemAttr("context-media-playbackrate-125x", "disabled", hasError);
      this.setItemAttr("context-media-playbackrate-150x", "disabled", hasError);
      this.setItemAttr("context-media-playbackrate-200x", "disabled", hasError);
      this.setItemAttr("context-media-showcontrols", "disabled", hasError);
      this.setItemAttr("context-media-hidecontrols", "disabled", hasError);
      if (this.onVideo) {
        let canSaveSnapshot = !this.onDRMMedia && this.target.readyState >= this.target.HAVE_CURRENT_DATA;
        this.setItemAttr("context-video-saveimage", "disabled", !canSaveSnapshot);
        this.setItemAttr("context-video-fullscreen", "disabled", hasError);
      }
    }
    this.showItem("context-media-sep-commands", onMedia);
  },

  initClickToPlayItems() {
    this.showItem("context-ctp-play", this.onCTPPlugin);
    this.showItem("context-ctp-hide", this.onCTPPlugin);
    this.showItem("context-sep-ctp", this.onCTPPlugin);
  },

  initPasswordManagerItems() {
    let loginFillInfo = gContextMenuContentData && gContextMenuContentData.loginFillInfo;

    // If we could not find a password field we
    // don't want to show the form fill option.
    let showFill = loginFillInfo && loginFillInfo.passwordField.found;

    // Disable the fill option if the user has set a master password
    // or if the password field or target field are disabled.
    let disableFill = !loginFillInfo ||
                      !Services.logins ||
                      !Services.logins.isLoggedIn ||
                      loginFillInfo.passwordField.disabled ||
                      (!this.onPassword && loginFillInfo.usernameField.disabled);

    this.showItem("fill-login-separator", showFill);
    this.showItem("fill-login", showFill);
    this.setItemAttr("fill-login", "disabled", disableFill);

    // Set the correct label for the fill menu
    let fillMenu = document.getElementById("fill-login");
    if (this.onPassword) {
      fillMenu.setAttribute("label", fillMenu.getAttribute("label-password"));
      fillMenu.setAttribute("accesskey", fillMenu.getAttribute("accesskey-password"));
    } else {
      fillMenu.setAttribute("label", fillMenu.getAttribute("label-login"));
      fillMenu.setAttribute("accesskey", fillMenu.getAttribute("accesskey-login"));
    }

    if (!showFill || disableFill) {
      return;
    }
    let documentURI = gContextMenuContentData.documentURIObject;
    let fragment = LoginManagerContextMenu.addLoginsToMenu(this.targetAsCPOW, this.browser, documentURI);

    this.showItem("fill-login-no-logins", !fragment);

    if (!fragment) {
      return;
    }
    let popup = document.getElementById("fill-login-popup");
    let insertBeforeElement = document.getElementById("fill-login-no-logins");
    popup.insertBefore(fragment, insertBeforeElement);
  },

  initSyncItems() {
    gSync.updateContentContextMenu(this);
  },

  openPasswordManager() {
    LoginHelper.openPasswordManager(window, gContextMenuContentData.documentURIObject.host);
  },

  inspectNode() {
    return DevToolsShim.inspectNode(gBrowser.selectedTab, this.targetSelectors);
  },

  _openLinkInParameters(extra) {
    let params = { charset: gContextMenuContentData.charSet,
                   originPrincipal: this.principal,
                   triggeringPrincipal: this.principal,
                   referrerURI: gContextMenuContentData.documentURIObject,
                   referrerPolicy: gContextMenuContentData.referrerPolicy,
                   frameOuterWindowID: gContextMenuContentData.frameOuterWindowID,
                   noReferrer: this.linkHasNoReferrer || this.onPlainTextLink };
    for (let p in extra) {
      params[p] = extra[p];
    }

    if (!this.isRemote) {
      // Propagate the frameOuterWindowID value saved when
      // the context menu has been opened.
      params.frameOuterWindowID = this.frameOuterWindowID;
    }

    // If we want to change userContextId, we must be sure that we don't
    // propagate the referrer.
    if ("userContextId" in params &&
        params.userContextId != gContextMenuContentData.userContextId) {
      params.noReferrer = true;
    }

    return params;
  },

  // Open linked-to URL in a new window.
  openLink() {
    urlSecurityCheck(this.linkURL, this.principal);
    openLinkIn(this.linkURL, "window", this._openLinkInParameters());
  },

  // Open linked-to URL in a new private window.
  openLinkInPrivateWindow() {
    urlSecurityCheck(this.linkURL, this.principal);
    openLinkIn(this.linkURL, "window",
               this._openLinkInParameters({ private: true }));
  },

  // Open linked-to URL in a new tab.
  openLinkInTab(event) {
    urlSecurityCheck(this.linkURL, this.principal);
    let referrerURI = gContextMenuContentData.documentURIObject;

    // if its parent allows mixed content and the referring URI passes
    // a same origin check with the target URI, we can preserve the users
    // decision of disabling MCB on a page for it's child tabs.
    let persistAllowMixedContentInChildTab = false;

    if (gContextMenuContentData.parentAllowsMixedContent) {
      const sm = Services.scriptSecurityManager;
      try {
        let targetURI = this.linkURI;
        sm.checkSameOriginURI(referrerURI, targetURI, false);
        persistAllowMixedContentInChildTab = true;
      } catch (e) { }
    }

    let params = {
      allowMixedContent: persistAllowMixedContentInChildTab,
      userContextId: parseInt(event.target.getAttribute("data-usercontextid")),
    };

    openLinkIn(this.linkURL, "tab", this._openLinkInParameters(params));
  },

  // open URL in current tab
  openLinkInCurrent() {
    urlSecurityCheck(this.linkURL, this.principal);
    openLinkIn(this.linkURL, "current", this._openLinkInParameters());
  },

  // Open frame in a new tab.
  openFrameInTab() {
    let referrer = gContextMenuContentData.referrer;
    openLinkIn(gContextMenuContentData.docLocation, "tab",
               { charset: gContextMenuContentData.charSet,
                 referrerURI: referrer ? makeURI(referrer) : null });
  },

  // Reload clicked-in frame.
  reloadFrame(aEvent) {
    let forceReload = aEvent.shiftKey;
    this.browser.messageManager.sendAsyncMessage("ContextMenu:ReloadFrame",
                                                 null, { target: this.target, forceReload });
  },

  // Open clicked-in frame in its own window.
  openFrame() {
    let referrer = gContextMenuContentData.referrer;
    openLinkIn(gContextMenuContentData.docLocation, "window",
               { charset: gContextMenuContentData.charSet,
                 referrerURI: referrer ? makeURI(referrer) : null });
  },

  // Open clicked-in frame in the same window.
  showOnlyThisFrame() {
    urlSecurityCheck(gContextMenuContentData.docLocation,
                     this.browser.contentPrincipal,
                     Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
    let referrer = gContextMenuContentData.referrer;
    openUILinkIn(gContextMenuContentData.docLocation, "current",
                 { disallowInheritPrincipal: true,
                   referrerURI: referrer ? makeURI(referrer) : null });
  },

  reload(event) {
    BrowserReloadOrDuplicate(event);
  },

  // View Partial Source
  viewPartialSource(aContext) {
    let {browser} = this;
    let openSelectionFn = function() {
      let tabBrowser = gBrowser;
      // In the case of popups, we need to find a non-popup browser window.
      // We might also not have a tabBrowser reference (if this isn't in a
      // a tabbrowser scope) or might have a fake/stub tabbrowser reference
      // (in the sidebar). Deal with those cases:
      if (!tabBrowser || !tabBrowser.loadOneTab || !window.toolbar.visible) {
        // This returns only non-popup browser windows by default.
        let browserWindow = RecentWindow.getMostRecentBrowserWindow();
        tabBrowser = browserWindow.gBrowser;
      }
      let relatedToCurrent = gBrowser && gBrowser.selectedBrowser == browser;
      let tab = tabBrowser.loadOneTab("about:blank", {
        relatedToCurrent,
        inBackground: false,
        triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
      });
      return tabBrowser.getBrowserForTab(tab);
    };

    let target = aContext == "mathml" ? this.target : null;
    top.gViewSourceUtils.viewPartialSourceInBrowser(browser, target, openSelectionFn);
  },

  // Open new "view source" window with the frame's URL.
  viewFrameSource() {
    BrowserViewSourceOfDocument({
      browser: this.browser,
      URL: gContextMenuContentData.docLocation,
      outerWindowID: this.frameOuterWindowID,
    });
  },

  viewInfo() {
    BrowserPageInfo(gContextMenuContentData.docLocation, null, null, null, this.browser);
  },

  viewImageInfo() {
    BrowserPageInfo(gContextMenuContentData.docLocation, "mediaTab",
                    this.imageInfo, null, this.browser);
  },

  viewImageDesc(e) {
    urlSecurityCheck(this.imageDescURL,
                     this.browser.contentPrincipal,
                     Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
    openUILink(this.imageDescURL, e, { disallowInheritPrincipal: true,
                                       referrerURI: gContextMenuContentData.documentURIObject });
  },

  viewFrameInfo() {
    BrowserPageInfo(gContextMenuContentData.docLocation, null, null,
                    this.frameOuterWindowID, this.browser);
  },

  reloadImage() {
    urlSecurityCheck(this.mediaURL,
                     this.browser.contentPrincipal,
                     Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);

    this.browser.messageManager.sendAsyncMessage("ContextMenu:ReloadImage",
                                                 null, { target: this.target });
  },

  _canvasToBlobURL(target) {
    let mm = this.browser.messageManager;
    return new Promise(function(resolve) {
      mm.sendAsyncMessage("ContextMenu:Canvas:ToBlobURL", {}, { target });

      let onMessage = (message) => {
        mm.removeMessageListener("ContextMenu:Canvas:ToBlobURL:Result", onMessage);
        resolve(message.data.blobURL);
      };
      mm.addMessageListener("ContextMenu:Canvas:ToBlobURL:Result", onMessage);
    });
  },

  // Change current window to the URL of the image, video, or audio.
  viewMedia(e) {
    let referrerURI = gContextMenuContentData.documentURIObject;
    let systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
    if (this.onCanvas) {
      this._canvasToBlobURL(this.target).then(function(blobURL) {
        openUILink(blobURL, e, { disallowInheritPrincipal: true,
                                 referrerURI,
                                 triggeringPrincipal: systemPrincipal});
      }, Cu.reportError);
    } else {
      urlSecurityCheck(this.mediaURL,
                       this.browser.contentPrincipal,
                       Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
      openUILink(this.mediaURL, e, { disallowInheritPrincipal: true,
                                     referrerURI,
                                     forceAllowDataURI: true });
    }
  },

  saveVideoFrameAsImage() {
    let mm = this.browser.messageManager;
    let isPrivate = PrivateBrowsingUtils.isBrowserPrivate(this.browser);

    let name = "";
    if (this.mediaURL) {
      try {
        let uri = makeURI(this.mediaURL);
        let url = uri.QueryInterface(Ci.nsIURL);
        if (url.fileBaseName)
          name = decodeURI(url.fileBaseName) + ".jpg";
      } catch (e) { }
    }
    if (!name)
      name = "snapshot.jpg";

    mm.sendAsyncMessage("ContextMenu:SaveVideoFrameAsImage", {}, {
      target: this.target,
    });

    let onMessage = (message) => {
      mm.removeMessageListener("ContextMenu:SaveVideoFrameAsImage:Result", onMessage);
      let dataURL = message.data.dataURL;
      saveImageURL(dataURL, name, "SaveImageTitle", true, false,
                   document.documentURIObject, null, null, null,
                   isPrivate);
    };
    mm.addMessageListener("ContextMenu:SaveVideoFrameAsImage:Result", onMessage);
  },

  leaveDOMFullScreen() {
    document.exitFullscreen();
  },

  // Change current window to the URL of the background image.
  viewBGImage(e) {
    urlSecurityCheck(this.bgImageURL,
                     this.browser.contentPrincipal,
                     Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
    openUILink(this.bgImageURL, e, { disallowInheritPrincipal: true,
                                     referrerURI: gContextMenuContentData.documentURIObject });
  },

  setDesktopBackground() {
    let mm = this.browser.messageManager;

    mm.sendAsyncMessage("ContextMenu:SetAsDesktopBackground", null,
                        { target: this.target });

    let onMessage = (message) => {
      mm.removeMessageListener("ContextMenu:SetAsDesktopBackground:Result",
                               onMessage);

      if (message.data.disable ||
          !Services.policies.isAllowed("setDesktopBackground")) {
        return;
      }

      let image = document.createElementNS("http://www.w3.org/1999/xhtml", "img");
      image.src = message.data.dataUrl;
      let imageName = message.data.imageName;

      // Confirm since it's annoying if you hit this accidentally.
      const kDesktopBackgroundURL =
                    "chrome://browser/content/setDesktopBackground.xul";

      if (AppConstants.platform == "macosx") {
        // On Mac, the Set Desktop Background window is not modal.
        // Don't open more than one Set Desktop Background window.
        let dbWin = Services.wm.getMostRecentWindow("Shell:SetDesktopBackground");
        if (dbWin) {
          dbWin.gSetBackground.init(image, imageName);
          dbWin.focus();
        } else {
          openDialog(kDesktopBackgroundURL, "",
                     "centerscreen,chrome,dialog=no,dependent,resizable=no",
                     image, imageName);
        }
      } else {
        // On non-Mac platforms, the Set Wallpaper dialog is modal.
        openDialog(kDesktopBackgroundURL, "",
                   "centerscreen,chrome,dialog,modal,dependent",
                   image, imageName);
      }
    };

    mm.addMessageListener("ContextMenu:SetAsDesktopBackground:Result", onMessage);
  },

  // Save URL of clicked-on frame.
  saveFrame() {
    saveBrowser(this.browser, false, this.frameOuterWindowID);
  },

  // Helper function to wait for appropriate MIME-type headers and
  // then prompt the user with a file picker
  saveHelper(linkURL, linkText, dialogTitle, bypassCache, doc, docURI,
             windowID, linkDownload, isContentWindowPrivate) {
    // canonical def in nsURILoader.h
    const NS_ERROR_SAVE_LINK_AS_TIMEOUT = 0x805d0020;

    // an object to proxy the data through to
    // nsIExternalHelperAppService.doContent, which will wait for the
    // appropriate MIME-type headers and then prompt the user with a
    // file picker
    function saveAsListener() {}
    saveAsListener.prototype = {
      extListener: null,

      onStartRequest: function saveLinkAs_onStartRequest(aRequest, aContext) {

        // if the timer fired, the error status will have been caused by that,
        // and we'll be restarting in onStopRequest, so no reason to notify
        // the user
        if (aRequest.status == NS_ERROR_SAVE_LINK_AS_TIMEOUT)
          return;

        timer.cancel();

        // some other error occured; notify the user...
        if (!Components.isSuccessCode(aRequest.status)) {
          try {
            const bundle = Services.strings.createBundle(
                    "chrome://mozapps/locale/downloads/downloads.properties");

            const title = bundle.GetStringFromName("downloadErrorAlertTitle");
            const msg = bundle.GetStringFromName("downloadErrorGeneric");

            let window = Services.wm.getOuterWindowWithId(windowID);
            Services.prompt.alert(window, title, msg);
          } catch (ex) {}
          return;
        }

        let extHelperAppSvc =
          Cc["@mozilla.org/uriloader/external-helper-app-service;1"].
          getService(Ci.nsIExternalHelperAppService);
        let channel = aRequest.QueryInterface(Ci.nsIChannel);
        this.extListener =
          extHelperAppSvc.doContent(channel.contentType, aRequest,
                                    null, true, window);
        this.extListener.onStartRequest(aRequest, aContext);
      },

      onStopRequest: function saveLinkAs_onStopRequest(aRequest, aContext,
                                                       aStatusCode) {
        if (aStatusCode == NS_ERROR_SAVE_LINK_AS_TIMEOUT) {
          // do it the old fashioned way, which will pick the best filename
          // it can without waiting.
          saveURL(linkURL, linkText, dialogTitle, bypassCache, false, docURI,
                  doc, isContentWindowPrivate);
        }
        if (this.extListener)
          this.extListener.onStopRequest(aRequest, aContext, aStatusCode);
      },

      onDataAvailable: function saveLinkAs_onDataAvailable(aRequest, aContext,
                                                           aInputStream,
                                                           aOffset, aCount) {
        this.extListener.onDataAvailable(aRequest, aContext, aInputStream,
                                         aOffset, aCount);
      }
    };

    function callbacks() {}
    callbacks.prototype = {
      getInterface: function sLA_callbacks_getInterface(aIID) {
        if (aIID.equals(Ci.nsIAuthPrompt) || aIID.equals(Ci.nsIAuthPrompt2)) {
          // If the channel demands authentication prompt, we must cancel it
          // because the save-as-timer would expire and cancel the channel
          // before we get credentials from user.  Both authentication dialog
          // and save as dialog would appear on the screen as we fall back to
          // the old fashioned way after the timeout.
          timer.cancel();
          channel.cancel(NS_ERROR_SAVE_LINK_AS_TIMEOUT);
        }
        throw Cr.NS_ERROR_NO_INTERFACE;
      }
    };

    // if it we don't have the headers after a short time, the user
    // won't have received any feedback from their click.  that's bad.  so
    // we give up waiting for the filename.
    function timerCallback() {}
    timerCallback.prototype = {
      notify: function sLA_timer_notify(aTimer) {
        channel.cancel(NS_ERROR_SAVE_LINK_AS_TIMEOUT);
      }
    };

    // setting up a new channel for 'right click - save link as ...'
    var channel = NetUtil.newChannel({
                    uri: makeURI(linkURL),
                    loadingPrincipal: this.principal,
                    contentPolicyType: Ci.nsIContentPolicy.TYPE_SAVEAS_DOWNLOAD,
                    securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS,
                  });

    if (linkDownload)
      channel.contentDispositionFilename = linkDownload;
    if (channel instanceof Ci.nsIPrivateBrowsingChannel) {
      let docIsPrivate = PrivateBrowsingUtils.isBrowserPrivate(this.browser);
      channel.setPrivate(docIsPrivate);
    }
    channel.notificationCallbacks = new callbacks();

    let flags = Ci.nsIChannel.LOAD_CALL_CONTENT_SNIFFERS;

    if (bypassCache)
      flags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;

    if (channel instanceof Ci.nsICachingChannel)
      flags |= Ci.nsICachingChannel.LOAD_BYPASS_LOCAL_CACHE_IF_BUSY;

    channel.loadFlags |= flags;

    if (channel instanceof Ci.nsIHttpChannel) {
      channel.referrer = docURI;
      if (channel instanceof Ci.nsIHttpChannelInternal)
        channel.forceAllowThirdPartyCookie = true;
    }

    // fallback to the old way if we don't see the headers quickly
    var timeToWait =
      Services.prefs.getIntPref("browser.download.saveLinkAsFilenameTimeout");
    var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
    timer.initWithCallback(new timerCallback(), timeToWait,
                           timer.TYPE_ONE_SHOT);

    // kick off the channel with our proxy object as the listener
    channel.asyncOpen2(new saveAsListener());
  },

  // Save URL of clicked-on link.
  saveLink() {
    urlSecurityCheck(this.linkURL, this.principal);

    let isContentWindowPrivate = this.isRemote ? this.ownerDoc.isPrivate : undefined;
    this.saveHelper(this.linkURL, this.linkTextStr, null, true, this.ownerDoc,
                    gContextMenuContentData.documentURIObject,
                    this.frameOuterWindowID,
                    this.linkDownload,
                    isContentWindowPrivate);
  },

  // Backwards-compatibility wrapper
  saveImage() {
    if (this.onCanvas || this.onImage)
        this.saveMedia();
  },

  // Save URL of the clicked upon image, video, or audio.
  saveMedia() {
    let doc = this.ownerDoc;
    let isContentWindowPrivate = this.isRemote ? this.ownerDoc.isPrivate : undefined;
    let referrerURI = gContextMenuContentData.documentURIObject;
    let isPrivate = PrivateBrowsingUtils.isBrowserPrivate(this.browser);
    if (this.onCanvas) {
      // Bypass cache, since it's a data: URL.
      this._canvasToBlobURL(this.target).then(function(blobURL) {
        saveImageURL(blobURL, "canvas.png", "SaveImageTitle",
                     true, false, referrerURI, null, null, null,
                     isPrivate);
      }, Cu.reportError);
    } else if (this.onImage) {
      urlSecurityCheck(this.mediaURL, this.principal);
      saveImageURL(this.mediaURL, null, "SaveImageTitle", false,
                   false, referrerURI, null, gContextMenuContentData.contentType,
                   gContextMenuContentData.contentDisposition, isPrivate);
    } else if (this.onVideo || this.onAudio) {
      urlSecurityCheck(this.mediaURL, this.principal);
      var dialogTitle = this.onVideo ? "SaveVideoTitle" : "SaveAudioTitle";
      this.saveHelper(this.mediaURL, null, dialogTitle, false, doc, referrerURI,
                      this.frameOuterWindowID, "", isContentWindowPrivate);
    }
  },

  // Backwards-compatibility wrapper
  sendImage() {
    if (this.onCanvas || this.onImage)
        this.sendMedia();
  },

  sendMedia() {
    MailIntegration.sendMessage(this.mediaURL, "");
  },

  playPlugin() {
    gPluginHandler.contextMenuCommand(this.browser, this.target, "play");
  },

  hidePlugin() {
    gPluginHandler.contextMenuCommand(this.browser, this.target, "hide");
  },

  // Generate email address and put it on clipboard.
  copyEmail() {
    // Copy the comma-separated list of email addresses only.
    // There are other ways of embedding email addresses in a mailto:
    // link, but such complex parsing is beyond us.
    var url = this.linkURL;
    var qmark = url.indexOf("?");
    var addresses;

    // 7 == length of "mailto:"
    addresses = qmark > 7 ? url.substring(7, qmark) : url.substr(7);

    // Let's try to unescape it using a character set
    // in case the address is not ASCII.
    try {
      addresses = Services.textToSubURI.unEscapeURIForUI(gContextMenuContentData.charSet,
                                                         addresses);
    } catch (ex) {
      // Do nothing.
    }

    var clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].
                    getService(Ci.nsIClipboardHelper);
    clipboard.copyString(addresses);
  },

  copyLink() {
    // If we're in a view source tab, remove the view-source: prefix
    let linkURL = this.linkURL.replace(/^view-source:/, "");
    var clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].
                    getService(Ci.nsIClipboardHelper);
    clipboard.copyString(linkURL);
  },

  /**
   * Utilities
   */

  /**
   * Show/hide one item (specified via name or the item element itself).
   * If the element is not found, then this function finishes silently.
   *
   * @param {Element|String} aItemOrId The item element or the name of the element
   *                                   to show.
   * @param {Boolean} aShow Set to true to show the item, false to hide it.
   */
  showItem(aItemOrId, aShow) {
    var item = aItemOrId.constructor == String ?
      document.getElementById(aItemOrId) : aItemOrId;
    if (item)
      item.hidden = !aShow;
  },

  // Set given attribute of specified context-menu item.  If the
  // value is null, then it removes the attribute (which works
  // nicely for the disabled attribute).
  setItemAttr(aID, aAttr, aVal ) {
    var elem = document.getElementById(aID);
    if (elem) {
      if (aVal == null) {
        // null indicates attr should be removed.
        elem.removeAttribute(aAttr);
      } else {
        // Set attr=val.
        elem.setAttribute(aAttr, aVal);
      }
    }
  },

  // Set context menu attribute according to like attribute of another node
  // (such as a broadcaster).
  setItemAttrFromNode(aItem_id, aAttr, aOther_id) {
    var elem = document.getElementById(aOther_id);
    if (elem && elem.getAttribute(aAttr) == "true")
      this.setItemAttr(aItem_id, aAttr, "true");
    else
      this.setItemAttr(aItem_id, aAttr, null);
  },

  // Temporary workaround for DOM api not yet implemented by XUL nodes.
  cloneNode(aItem) {
    // Create another element like the one we're cloning.
    var node = document.createElement(aItem.tagName);

    // Copy attributes from argument item to the new one.
    var attrs = aItem.attributes;
    for (var i = 0; i < attrs.length; i++) {
      var attr = attrs.item(i);
      node.setAttribute(attr.nodeName, attr.nodeValue);
    }

    // Voila!
    return node;
  },

  getLinkURI() {
    try {
      return makeURI(this.linkURL);
    } catch (ex) {
     // e.g. empty URL string
    }

    return null;
  },

  // Kept for addon compat
  linkText() {
    return this.linkTextStr;
  },

  // Determines whether or not the separator with the specified ID should be
  // shown or not by determining if there are any non-hidden items between it
  // and the previous separator.
  shouldShowSeparator(aSeparatorID) {
    var separator = document.getElementById(aSeparatorID);
    if (separator) {
      var sibling = separator.previousSibling;
      while (sibling && sibling.localName != "menuseparator") {
        if (!sibling.hidden)
          return true;
        sibling = sibling.previousSibling;
      }
    }
    return false;
  },

  addDictionaries() {
    var uri = formatURL("browser.dictionaries.download.url", true);

    var locale = "-";
    try {
      locale = Services.prefs.getComplexValue("intl.accept_languages",
                                              Ci.nsIPrefLocalizedString).data;
    } catch (e) { }

    var version = "-";
    try {
      version = Services.appinfo.version;
    } catch (e) { }

    uri = uri.replace(/%LOCALE%/, escape(locale)).replace(/%VERSION%/, version);

    var newWindowPref = Services.prefs.getIntPref("browser.link.open_newwindow");
    var where = newWindowPref == 3 ? "tab" : "window";

    openUILinkIn(uri, where);
  },

  bookmarkThisPage: function CM_bookmarkThisPage() {
    window.top.PlacesCommandHook
              .bookmarkPage(this.browser, true)
              .catch(Cu.reportError);
  },

  bookmarkLink: function CM_bookmarkLink() {
    window.top.PlacesCommandHook.bookmarkLink(PlacesUtils.bookmarksMenuFolderId,
                                              this.linkURL, this.linkTextStr);
  },

  addBookmarkForFrame: function CM_addBookmarkForFrame() {
    let uri = gContextMenuContentData.documentURIObject;
    let mm = this.browser.messageManager;

    let onMessage = (message) => {
      mm.removeMessageListener("ContextMenu:BookmarkFrame:Result", onMessage);

      window.top.PlacesCommandHook.bookmarkLink(PlacesUtils.bookmarksMenuFolderId,
                                                uri.spec,
                                                message.data.title,
                                                message.data.description)
                                  .catch(Cu.reportError);
    };
    mm.addMessageListener("ContextMenu:BookmarkFrame:Result", onMessage);

    mm.sendAsyncMessage("ContextMenu:BookmarkFrame", null, { target: this.target });
  },

  savePageAs: function CM_savePageAs() {
    saveBrowser(this.browser);
  },

  printFrame: function CM_printFrame() {
    PrintUtils.printWindow(this.frameOuterWindowID, this.browser);
  },

  switchPageDirection: function CM_switchPageDirection() {
    this.browser.messageManager.sendAsyncMessage("SwitchDocumentDirection");
  },

  mediaCommand: function CM_mediaCommand(command, data) {
    let mm = this.browser.messageManager;
    let win = this.browser.ownerGlobal;
    let windowUtils = win.QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsIDOMWindowUtils);
    mm.sendAsyncMessage("ContextMenu:MediaCommand",
                        {command,
                         data,
                         handlingUserInput: windowUtils.isHandlingUserInput},
                        {element: this.target});
  },

  copyMediaLocation() {
    var clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].
                    getService(Ci.nsIClipboardHelper);
    clipboard.copyString(this.mediaURL);
  },

  drmLearnMore(aEvent) {
    let drmInfoURL = Services.urlFormatter.formatURLPref("app.support.baseURL") + "drm-content";
    let dest = whereToOpenLink(aEvent);
    // Don't ever want this to open in the same tab as it'll unload the
    // DRM'd video, which is going to be a bad idea in most cases.
    if (dest == "current") {
      dest = "tab";
    }
    openUILinkIn(drmInfoURL, dest);
  },

  get imageURL() {
    if (this.onImage)
      return this.mediaURL;
    return "";
  },

  // Formats the 'Search <engine> for "<selection or link text>"' context menu.
  formatSearchContextItem() {
    var menuItem = document.getElementById("context-searchselect");
    let selectedText = this.isTextSelected ? this.textSelected : this.linkTextStr;

    // Store searchTerms in context menu item so we know what to search onclick
    menuItem.searchTerms = selectedText;

    // Copied to alert.js' prefillAlertInfo().
    // If the JS character after our truncation point is a trail surrogate,
    // include it in the truncated string to avoid splitting a surrogate pair.
    if (selectedText.length > 15) {
      let truncLength = 15;
      let truncChar = selectedText[15].charCodeAt(0);
      if (truncChar >= 0xDC00 && truncChar <= 0xDFFF)
        truncLength++;
      selectedText = selectedText.substr(0, truncLength) + this.ellipsis;
    }

    // format "Search <engine> for <selection>" string to show in menu
    let engineName = Services.search.currentEngine.name;
    var menuLabel = gNavigatorBundle.getFormattedString("contextMenuSearch",
                                                        [engineName,
                                                         selectedText]);
    menuItem.label = menuLabel;
    menuItem.accessKey = gNavigatorBundle.getString("contextMenuSearch.accesskey");
  },

  _getTelemetryClickInfo(aXulMenu) {
    this._onPopupHiding = () => {
      aXulMenu.ownerDocument.removeEventListener("command", activationHandler, true);
      aXulMenu.removeEventListener("popuphiding", this._onPopupHiding, true);
      delete this._onPopupHiding;

      let eventKey = [
          this._telemetryPageContext,
          this._telemetryHadCustomItems ? "withcustom" : "withoutcustom"
      ];
      let target = this._telemetryClickID || "close-without-interaction";
      BrowserUITelemetry.registerContextMenuInteraction(eventKey, target);
    };
    let activationHandler = (e) => {
      // Deal with command events being routed to command elements; figure out
      // what triggered the event (which will have the right e.target)
      if (e.sourceEvent) {
        e = e.sourceEvent;
      }
      // Target should be in the menu (this catches using shortcuts for items
      // not in the menu while the menu is up)
      if (!aXulMenu.contains(e.target)) {
        return;
      }

      // Check if this is a page menu item:
      if (e.target.hasAttribute(PageMenuParent.GENERATEDITEMID_ATTR)) {
        this._telemetryClickID = "custom-page-item";
      } else {
        this._telemetryClickID = (e.target.id || "unknown").replace(/^context-/i, "");
      }
    };
    aXulMenu.ownerDocument.addEventListener("command", activationHandler, true);
    aXulMenu.addEventListener("popuphiding", this._onPopupHiding, true);
  },

  _getTelemetryPageContextInfo() {
    let rv = [];
    for (let k of ["isContentSelected", "onLink", "onImage", "onCanvas", "onVideo", "onAudio",
                   "onTextInput", "inWebExtBrowser", "inTabBrowser"]) {
      if (this[k]) {
        rv.push(k.replace(/^(?:is|on)(.)/, (match, firstLetter) => firstLetter.toLowerCase()));
      }
    }
    if (!rv.length) {
      rv.push("other");
    }

    return JSON.stringify(rv);
  },

  _checkTelemetryForMenu(aXulMenu) {
    this._telemetryClickID = null;
    this._telemetryPageContext = this._getTelemetryPageContextInfo();
    this._telemetryHadCustomItems = this.hasPageMenu;
    this._getTelemetryClickInfo(aXulMenu);
  },

  createContainerMenu(aEvent) {
    let createMenuOptions = {
      isContextMenu: true,
      excludeUserContextId: gContextMenuContentData.userContextId,
    };
    return createUserContextMenu(aEvent, createMenuOptions);
  },
};
PK
!<�=@�gg0chrome/browser/content/browser/pageinfo/feeds.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Via pageInfo.xul -> utilityOverlay.js
/* import-globals-from ../utilityOverlay.js */
/* import-globals-from ./pageInfo.js */

function initFeedTab(feeds) {
  for (const [name, type, url] of feeds) {
    addRow(name, type, url);
  }

  const feedListbox = document.getElementById("feedListbox");
  document.getElementById("feedTab").hidden = feedListbox.getRowCount() == 0;
}

function addRow(name, type, url) {
  const item = document.createElement("richlistitem");

  const top = document.createElement("hbox");
  top.setAttribute("flex", "1");
  item.appendChild(top);

  const bottom = document.createElement("hbox");
  bottom.setAttribute("flex", "1");
  item.appendChild(bottom);

  const nameLabel = document.createElement("label");
  nameLabel.className = "feedTitle";
  nameLabel.textContent = name;
  nameLabel.setAttribute("flex", "1");
  top.appendChild(nameLabel);

  const typeLabel = document.createElement("label");
  typeLabel.textContent = type;
  top.appendChild(typeLabel);

  const urlContainer = document.createElement("hbox");
  urlContainer.setAttribute("flex", "1");
  bottom.appendChild(urlContainer);

  const urlLabel = document.createElement("label");
  urlLabel.className = "text-link";
  urlLabel.textContent = url;
  urlLabel.setAttribute("tooltiptext", url);
  urlLabel.addEventListener("click", ev => openUILink(this.value, ev));
  urlContainer.appendChild(urlLabel);

  const subscribeButton = document.createElement("button");
  subscribeButton.className = "feed-subscribe";
  subscribeButton.addEventListener("click",
    () => openUILinkIn(url, "current", { ignoreAlt: true }));
  subscribeButton.setAttribute("label", gBundle.getString("feedSubscribe"));
  subscribeButton.setAttribute("accesskey", gBundle.getString("feedSubscribe.accesskey"));
  bottom.appendChild(subscribeButton);

  document.getElementById("feedListbox").appendChild(item);
}
PK
!<&s۞��4chrome/browser/content/browser/pageinfo/pageInfo.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#viewGroup > radio > .radio-label-box {
  -moz-box-orient: vertical;
  -moz-box-align: center;
}

/* Hide the radio button for the section headers */
#viewGroup > radio > .radio-check {
  display: none;
}

#feedListbox richlistitem {
  -moz-box-orient: vertical;
}

#feedListbox richlistitem:not([selected="true"]) .feed-subscribe {
  display: none;
}

groupbox[closed="true"] > .groupbox-body {
  visibility: collapse;
}

#thepreviewimage {
  display: block;
/* This following entry can be removed when Bug 522850 is fixed. */
  min-width: 1px;
}
PK
!<�4
�?�?�3chrome/browser/content/browser/pageinfo/pageInfo.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

ChromeUtils.import("resource://gre/modules/Services.jsm");

/* import-globals-from ../../../../toolkit/content/globalOverlay.js */
/* import-globals-from ../../../../toolkit/content/contentAreaUtils.js */
/* import-globals-from ../../../../toolkit/content/treeUtils.js */
/* import-globals-from feeds.js */
/* import-globals-from permissions.js */
/* import-globals-from security.js */

// define a js object to implement nsITreeView
function pageInfoTreeView(treeid, copycol) {
  // copycol is the index number for the column that we want to add to
  // the copy-n-paste buffer when the user hits accel-c
  this.treeid = treeid;
  this.copycol = copycol;
  this.rows = 0;
  this.tree = null;
  this.data = [ ];
  this.selection = null;
  this.sortcol = -1;
  this.sortdir = false;
}

pageInfoTreeView.prototype = {
  set rowCount(c) { throw "rowCount is a readonly property"; },
  get rowCount() { return this.rows; },

  setTree(tree) {
    this.tree = tree;
  },

  getCellText(row, column) {
    // row can be null, but js arrays are 0-indexed.
    // colidx cannot be null, but can be larger than the number
    // of columns in the array. In this case it's the fault of
    // whoever typoed while calling this function.
    return this.data[row][column.index] || "";
  },

  setCellValue(row, column, value) {
  },

  setCellText(row, column, value) {
    this.data[row][column.index] = value;
  },

  addRow(row) {
    this.rows = this.data.push(row);
    this.rowCountChanged(this.rows - 1, 1);
    if (this.selection.count == 0 && this.rowCount && !gImageElement) {
      this.selection.select(0);
    }
  },

  addRows(rows) {
    for (let row of rows) {
      this.addRow(row);
    }
  },

  rowCountChanged(index, count) {
    this.tree.rowCountChanged(index, count);
  },

  invalidate() {
    this.tree.invalidate();
  },

  clear() {
    if (this.tree)
      this.tree.rowCountChanged(0, -this.rows);
    this.rows = 0;
    this.data = [ ];
  },

  handleCopy(row) {
    return (row < 0 || this.copycol < 0) ? "" : (this.data[row][this.copycol] || "");
  },

  performActionOnRow(action, row) {
    if (action == "copy") {
      var data = this.handleCopy(row);
      this.tree.treeBody.parentNode.setAttribute("copybuffer", data);
    }
  },

  onPageMediaSort(columnname) {
    var tree = document.getElementById(this.treeid);
    var treecol = tree.columns.getNamedColumn(columnname);

    this.sortdir =
      gTreeUtils.sort(
        tree,
        this,
        this.data,
        treecol.index,
        function textComparator(a, b) { return (a || "").toLowerCase().localeCompare((b || "").toLowerCase()); },
        this.sortcol,
        this.sortdir
      );

    Array.forEach(tree.columns, function(col) {
      col.element.removeAttribute("sortActive");
      col.element.removeAttribute("sortDirection");
    });
    treecol.element.setAttribute("sortActive", "true");
    treecol.element.setAttribute("sortDirection", this.sortdir ?
                                                  "ascending" : "descending");

    this.sortcol = treecol.index;
  },

  getRowProperties(row) { return ""; },
  getCellProperties(row, column) { return ""; },
  getColumnProperties(column) { return ""; },
  isContainer(index) { return false; },
  isContainerOpen(index) { return false; },
  isSeparator(index) { return false; },
  isSorted() { return this.sortcol > -1; },
  canDrop(index, orientation) { return false; },
  drop(row, orientation) { return false; },
  getParentIndex(index) { return 0; },
  hasNextSibling(index, after) { return false; },
  getLevel(index) { return 0; },
  getImageSrc(row, column) { },
  getCellValue(row, column) { },
  toggleOpenState(index) { },
  cycleHeader(col) { },
  selectionChanged() { },
  cycleCell(row, column) { },
  isEditable(row, column) { return false; },
  isSelectable(row, column) { return false; },
  performAction(action) { },
  performActionOnCell(action, row, column) { }
};

// mmm, yummy. global variables.
var gDocInfo = null;
var gImageElement = null;

// column number to help using the data array
const COL_IMAGE_ADDRESS = 0;
const COL_IMAGE_TYPE    = 1;
const COL_IMAGE_SIZE    = 2;
const COL_IMAGE_ALT     = 3;
const COL_IMAGE_COUNT   = 4;
const COL_IMAGE_NODE    = 5;
const COL_IMAGE_BG      = 6;

// column number to copy from, second argument to pageInfoTreeView's constructor
const COPYCOL_NONE = -1;
const COPYCOL_META_CONTENT = 1;
const COPYCOL_IMAGE = COL_IMAGE_ADDRESS;

// one nsITreeView for each tree in the window
var gMetaView = new pageInfoTreeView("metatree", COPYCOL_META_CONTENT);
var gImageView = new pageInfoTreeView("imagetree", COPYCOL_IMAGE);

gImageView.getCellProperties = function(row, col) {
  var data = gImageView.data[row];
  var item = gImageView.data[row][COL_IMAGE_NODE];
  var props = "";
  if (!checkProtocol(data) ||
      item instanceof HTMLEmbedElement ||
      (item instanceof HTMLObjectElement && !item.type.startsWith("image/")))
    props += "broken";

  if (col.element.id == "image-address")
    props += " ltr";

  return props;
};

gImageView.getCellText = function(row, column) {
  var value = this.data[row][column.index];
  if (column.index == COL_IMAGE_SIZE) {
    if (value == -1) {
      return gStrings.unknown;
    }
    var kbSize = Number(Math.round(value / 1024 * 100) / 100);
    return gBundle.getFormattedString("mediaFileSize", [kbSize]);
  }
  return value || "";
};

gImageView.onPageMediaSort = function(columnname) {
  var tree = document.getElementById(this.treeid);
  var treecol = tree.columns.getNamedColumn(columnname);

  var comparator;
  var index = treecol.index;
  if (index == COL_IMAGE_SIZE || index == COL_IMAGE_COUNT) {
    comparator = function numComparator(a, b) { return a - b; };
  } else {
    comparator = function textComparator(a, b) { return (a || "").toLowerCase().localeCompare((b || "").toLowerCase()); };
  }

  this.sortdir =
    gTreeUtils.sort(
      tree,
      this,
      this.data,
      index,
      comparator,
      this.sortcol,
      this.sortdir
    );

  Array.forEach(tree.columns, function(col) {
    col.element.removeAttribute("sortActive");
    col.element.removeAttribute("sortDirection");
  });
  treecol.element.setAttribute("sortActive", "true");
  treecol.element.setAttribute("sortDirection", this.sortdir ?
                                                "ascending" : "descending");

  this.sortcol = index;
};

var gImageHash = { };

// localized strings (will be filled in when the document is loaded)
// this isn't all of them, these are just the ones that would otherwise have been loaded inside a loop
var gStrings = { };
var gBundle;

const PERMISSION_CONTRACTID     = "@mozilla.org/permissionmanager;1";
const PREFERENCES_CONTRACTID    = "@mozilla.org/preferences-service;1";

// a number of services I'll need later
// the cache services
const nsICacheStorageService = Ci.nsICacheStorageService;
const nsICacheStorage = Ci.nsICacheStorage;
const cacheService = Cc["@mozilla.org/netwerk/cache-storage-service;1"].getService(nsICacheStorageService);

var loadContextInfo = Services.loadContextInfo.fromLoadContext(
  window.QueryInterface(Ci.nsIInterfaceRequestor)
        .getInterface(Ci.nsIWebNavigation)
        .QueryInterface(Ci.nsILoadContext), false);
var diskStorage = cacheService.diskCacheStorage(loadContextInfo, false);

const nsICookiePermission  = Ci.nsICookiePermission;
const nsIPermissionManager = Ci.nsIPermissionManager;

const nsICertificateDialogs = Ci.nsICertificateDialogs;
const CERTIFICATEDIALOGS_CONTRACTID = "@mozilla.org/nsCertificateDialogs;1";

// clipboard helper
function getClipboardHelper() {
    try {
        return Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
    } catch (e) {
        // do nothing, later code will handle the error
        return null;
    }
}
const gClipboardHelper = getClipboardHelper();

// namespaces, don't need all of these yet...
const XLinkNS  = "http://www.w3.org/1999/xlink";
const XULNS    = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const XMLNS    = "http://www.w3.org/XML/1998/namespace";
const XHTMLNS  = "http://www.w3.org/1999/xhtml";
const XHTML2NS = "http://www.w3.org/2002/06/xhtml2";

const XHTMLNSre  = "^http\:\/\/www\.w3\.org\/1999\/xhtml$";
const XHTML2NSre = "^http\:\/\/www\.w3\.org\/2002\/06\/xhtml2$";
const XHTMLre = RegExp(XHTMLNSre + "|" + XHTML2NSre, "");

/* Overlays register functions here.
 * These arrays are used to hold callbacks that Page Info will call at
 * various stages. Use them by simply appending a function to them.
 * For example, add a function to onLoadRegistry by invoking
 *   "onLoadRegistry.push(XXXLoadFunc);"
 * The XXXLoadFunc should be unique to the overlay module, and will be
 * invoked as "XXXLoadFunc();"
 */

// These functions are called to build the data displayed in the Page Info window.
var onLoadRegistry = [ ];

// These functions are called to remove old data still displayed in
// the window when the document whose information is displayed
// changes. For example, at this time, the list of images of the Media
// tab is cleared.
var onResetRegistry = [ ];

// These functions are called once when all the elements in all of the target
// document (and all of its subframes, if any) have been processed
var onFinished = [ ];

// These functions are called once when the Page Info window is closed.
var onUnloadRegistry = [ ];

/* Called when PageInfo window is loaded.  Arguments are:
 *  window.arguments[0] - (optional) an object consisting of
 *                         - doc: (optional) document to use for source. if not provided,
 *                                the calling window's document will be used
 *                         - initialTab: (optional) id of the inital tab to display
 */
function onLoadPageInfo() {
  gBundle = document.getElementById("pageinfobundle");
  gStrings.unknown = gBundle.getString("unknown");
  gStrings.notSet = gBundle.getString("notset");
  gStrings.mediaImg = gBundle.getString("mediaImg");
  gStrings.mediaBGImg = gBundle.getString("mediaBGImg");
  gStrings.mediaBorderImg = gBundle.getString("mediaBorderImg");
  gStrings.mediaListImg = gBundle.getString("mediaListImg");
  gStrings.mediaCursor = gBundle.getString("mediaCursor");
  gStrings.mediaObject = gBundle.getString("mediaObject");
  gStrings.mediaEmbed = gBundle.getString("mediaEmbed");
  gStrings.mediaLink = gBundle.getString("mediaLink");
  gStrings.mediaInput = gBundle.getString("mediaInput");
  gStrings.mediaVideo = gBundle.getString("mediaVideo");
  gStrings.mediaAudio = gBundle.getString("mediaAudio");

  var args = "arguments" in window &&
             window.arguments.length >= 1 &&
             window.arguments[0];

  // init media view
  var imageTree = document.getElementById("imagetree");
  imageTree.view = gImageView;

  /* Select the requested tab, if the name is specified */
  loadTab(args);
  Services.obs.notifyObservers(window, "page-info-dialog-loaded");
}

function loadPageInfo(frameOuterWindowID, imageElement, browser) {
  browser = browser || window.opener.gBrowser.selectedBrowser;
  let mm = browser.messageManager;

  gStrings["application/rss+xml"]  = gBundle.getString("feedRss");
  gStrings["application/atom+xml"] = gBundle.getString("feedAtom");
  gStrings["text/xml"]             = gBundle.getString("feedXML");
  gStrings["application/xml"]      = gBundle.getString("feedXML");
  gStrings["application/rdf+xml"]  = gBundle.getString("feedXML");

  let imageInfo = imageElement;

  // Look for pageInfoListener in content.js. Sends message to listener with arguments.
  mm.sendAsyncMessage("PageInfo:getData", {strings: gStrings, frameOuterWindowID});

  let pageInfoData;

  // Get initial pageInfoData needed to display the general, feeds, permission and security tabs.
  mm.addMessageListener("PageInfo:data", function onmessage(message) {
    mm.removeMessageListener("PageInfo:data", onmessage);
    pageInfoData = message.data;
    let docInfo = pageInfoData.docInfo;
    let windowInfo = pageInfoData.windowInfo;
    let uri = makeURI(docInfo.documentURIObject.spec);
    let principal = docInfo.principal;
    gDocInfo = docInfo;

    gImageElement = imageInfo;

    var titleFormat = windowInfo.isTopWindow ? "pageInfo.page.title"
                                             : "pageInfo.frame.title";
    document.title = gBundle.getFormattedString(titleFormat, [docInfo.location]);

    document.getElementById("main-window").setAttribute("relatedUrl", docInfo.location);

    makeGeneralTab(pageInfoData.metaViewRows, docInfo);
    initFeedTab(pageInfoData.feeds);
    onLoadPermission(uri, principal);
    securityOnLoad(uri, windowInfo);
  });

  // Get the media elements from content script to setup the media tab.
  mm.addMessageListener("PageInfo:mediaData", function onmessage(message) {
    // Page info window was closed.
    if (window.closed) {
      mm.removeMessageListener("PageInfo:mediaData", onmessage);
      return;
    }

    // The page info media fetching has been completed.
    if (message.data.isComplete) {
      mm.removeMessageListener("PageInfo:mediaData", onmessage);
      onFinished.forEach(function(func) { func(pageInfoData); });
      return;
    }

    for (let item of message.data.mediaItems) {
      addImage(item);
    }

    selectImage();
  });

  /* Call registered overlay init functions */
  onLoadRegistry.forEach(function(func) { func(); });
}

function resetPageInfo(args) {
  /* Reset Meta tags part */
  gMetaView.clear();

  /* Reset Media tab */
  var mediaTab = document.getElementById("mediaTab");
  if (!mediaTab.hidden) {
    Services.obs.removeObserver(imagePermissionObserver, "perm-changed");
    mediaTab.hidden = true;
  }
  gImageView.clear();
  gImageHash = {};

  /* Reset Feeds Tab */
  var feedListbox = document.getElementById("feedListbox");
  while (feedListbox.firstChild)
    feedListbox.firstChild.remove();

  /* Call registered overlay reset functions */
  onResetRegistry.forEach(function(func) { func(); });

  /* Rebuild the data */
  loadTab(args);
}

function onUnloadPageInfo() {
  // Remove the observer, only if there is at least 1 image.
  if (!document.getElementById("mediaTab").hidden) {
    Services.obs.removeObserver(imagePermissionObserver, "perm-changed");
  }

  /* Call registered overlay unload functions */
  onUnloadRegistry.forEach(function(func) { func(); });
}

function doHelpButton() {
  const helpTopics = {
    "generalPanel":  "pageinfo_general",
    "mediaPanel":    "pageinfo_media",
    "feedPanel":     "pageinfo_feed",
    "permPanel":     "pageinfo_permissions",
    "securityPanel": "pageinfo_security"
  };

  var deck  = document.getElementById("mainDeck");
  var helpdoc = helpTopics[deck.selectedPanel.id] || "pageinfo_general";
  openHelpLink(helpdoc);
}

function showTab(id) {
  var deck  = document.getElementById("mainDeck");
  var pagel = document.getElementById(id + "Panel");
  deck.selectedPanel = pagel;
}

function loadTab(args) {
  // If the "View Image Info" context menu item was used, the related image
  // element is provided as an argument. This can't be a background image.
  let imageElement = args && args.imageElement;
  let frameOuterWindowID = args && args.frameOuterWindowID;
  let browser = args && args.browser;

  /* Load the page info */
  loadPageInfo(frameOuterWindowID, imageElement, browser);

  var initialTab = (args && args.initialTab) || "generalTab";
  var radioGroup = document.getElementById("viewGroup");
  initialTab = document.getElementById(initialTab) || document.getElementById("generalTab");
  radioGroup.selectedItem = initialTab;
  radioGroup.selectedItem.doCommand();
  radioGroup.focus();
}

function toggleGroupbox(id) {
  var elt = document.getElementById(id);
  if (elt.hasAttribute("closed")) {
    elt.removeAttribute("closed");
    if (elt.flexWhenOpened)
      elt.flex = elt.flexWhenOpened;
  } else {
    elt.setAttribute("closed", "true");
    if (elt.flex) {
      elt.flexWhenOpened = elt.flex;
      elt.flex = 0;
    }
  }
}

function openCacheEntry(key, cb) {
  var checkCacheListener = {
    onCacheEntryCheck(entry, appCache) {
      return Ci.nsICacheEntryOpenCallback.ENTRY_WANTED;
    },
    onCacheEntryAvailable(entry, isNew, appCache, status) {
      cb(entry);
    }
  };
  diskStorage.asyncOpenURI(Services.io.newURI(key), "", nsICacheStorage.OPEN_READONLY, checkCacheListener);
}

function makeGeneralTab(metaViewRows, docInfo) {
  var title = (docInfo.title) ? docInfo.title : gBundle.getString("noPageTitle");
  document.getElementById("titletext").value = title;

  var url = docInfo.location;
  setItemValue("urltext", url);

  var referrer = ("referrer" in docInfo && docInfo.referrer);
  setItemValue("refertext", referrer);

  var mode = ("compatMode" in docInfo && docInfo.compatMode == "BackCompat") ? "generalQuirksMode" : "generalStrictMode";
  document.getElementById("modetext").value = gBundle.getString(mode);

  // find out the mime type
  var mimeType = docInfo.contentType;
  setItemValue("typetext", mimeType);

  // get the document characterset
  var encoding = docInfo.characterSet;
  document.getElementById("encodingtext").value = encoding;

  let length = metaViewRows.length;

  var metaGroup = document.getElementById("metaTags");
  if (!length)
    metaGroup.collapsed = true;
  else {
    var metaTagsCaption = document.getElementById("metaTagsCaption");
    if (length == 1)
      metaTagsCaption.label = gBundle.getString("generalMetaTag");
    else
      metaTagsCaption.label = gBundle.getFormattedString("generalMetaTags", [length]);
    var metaTree = document.getElementById("metatree");
    metaTree.view = gMetaView;

    // Add the metaViewRows onto the general tab's meta info tree.
    gMetaView.addRows(metaViewRows);

    metaGroup.collapsed = false;
  }

  // get the date of last modification
  var modifiedText = formatDate(docInfo.lastModified, gStrings.notSet);
  document.getElementById("modifiedtext").value = modifiedText;

  // get cache info
  var cacheKey = url.replace(/#.*$/, "");
  openCacheEntry(cacheKey, function(cacheEntry) {
    var sizeText;
    if (cacheEntry) {
      var pageSize = cacheEntry.dataSize;
      var kbSize = formatNumber(Math.round(pageSize / 1024 * 100) / 100);
      sizeText = gBundle.getFormattedString("generalSize", [kbSize, formatNumber(pageSize)]);
    }
    setItemValue("sizetext", sizeText);
  });
}

function addImage(imageViewRow) {
  let [url, type, alt, elem, isBg] = imageViewRow;

  if (!url)
    return;

  if (!gImageHash.hasOwnProperty(url))
    gImageHash[url] = { };
  if (!gImageHash[url].hasOwnProperty(type))
    gImageHash[url][type] = { };
  if (!gImageHash[url][type].hasOwnProperty(alt)) {
    gImageHash[url][type][alt] = gImageView.data.length;
    var row = [url, type, -1, alt, 1, elem, isBg];
    gImageView.addRow(row);

    // Fill in cache data asynchronously
    openCacheEntry(url, function(cacheEntry) {
      // The data at row[2] corresponds to the data size.
      if (cacheEntry) {
        row[2] = cacheEntry.dataSize;
        // Invalidate the row to trigger a repaint.
        gImageView.tree.invalidateRow(gImageView.data.indexOf(row));
      }
    });

    // Add the observer, only once.
    if (gImageView.data.length == 1) {
      document.getElementById("mediaTab").hidden = false;
      Services.obs.addObserver(imagePermissionObserver, "perm-changed");
    }
  } else {
    var i = gImageHash[url][type][alt];
    gImageView.data[i][COL_IMAGE_COUNT]++;
    // The same image can occur several times on the page at different sizes.
    // If the "View Image Info" context menu item was used, ensure we select
    // the correct element.
    if (!gImageView.data[i][COL_IMAGE_BG] &&
        gImageElement && url == gImageElement.currentSrc &&
        gImageElement.width == elem.width &&
        gImageElement.height == elem.height &&
        gImageElement.imageText == elem.imageText) {
      gImageView.data[i][COL_IMAGE_NODE] = elem;
    }
  }
}

// Link Stuff
function openURL(target) {
  var url = target.parentNode.childNodes[2].value;
  window.open(url, "_blank", "chrome");
}

function onBeginLinkDrag(event, urlField, descField) {
  if (event.originalTarget.localName != "treechildren")
    return;

  var tree = event.target;
  if (!("treeBoxObject" in tree))
    tree = tree.parentNode;

  var row = tree.treeBoxObject.getRowAt(event.clientX, event.clientY);
  if (row == -1)
    return;

  // Adding URL flavor
  var col = tree.columns[urlField];
  var url = tree.view.getCellText(row, col);
  col = tree.columns[descField];
  var desc = tree.view.getCellText(row, col);

  var dt = event.dataTransfer;
  dt.setData("text/x-moz-url", url + "\n" + desc);
  dt.setData("text/url-list", url);
  dt.setData("text/plain", url);
}

// Image Stuff
function getSelectedRows(tree) {
  var start = { };
  var end   = { };
  var numRanges = tree.view.selection.getRangeCount();

  var rowArray = [ ];
  for (var t = 0; t < numRanges; t++) {
    tree.view.selection.getRangeAt(t, start, end);
    for (var v = start.value; v <= end.value; v++)
      rowArray.push(v);
  }

  return rowArray;
}

function getSelectedRow(tree) {
  var rows = getSelectedRows(tree);
  return (rows.length == 1) ? rows[0] : -1;
}

function selectSaveFolder(aCallback) {
  const nsIFile = Ci.nsIFile;
  const nsIFilePicker = Ci.nsIFilePicker;
  let titleText = gBundle.getString("mediaSelectFolder");
  let fp = Cc["@mozilla.org/filepicker;1"].
           createInstance(nsIFilePicker);
  let fpCallback = function fpCallback_done(aResult) {
    if (aResult == nsIFilePicker.returnOK) {
      aCallback(fp.file.QueryInterface(nsIFile));
    } else {
      aCallback(null);
    }
  };

  fp.init(window, titleText, nsIFilePicker.modeGetFolder);
  fp.appendFilters(nsIFilePicker.filterAll);
  try {
    let initialDir = Services.prefs.getComplexValue("browser.download.dir", nsIFile);
    if (initialDir) {
      fp.displayDirectory = initialDir;
    }
  } catch (ex) {
  }
  fp.open(fpCallback);
}

function saveMedia() {
  var tree = document.getElementById("imagetree");
  var rowArray = getSelectedRows(tree);
  if (rowArray.length == 1) {
    let row = rowArray[0];
    let item = gImageView.data[row][COL_IMAGE_NODE];
    let url = gImageView.data[row][COL_IMAGE_ADDRESS];

    if (url) {
      var titleKey = "SaveImageTitle";

      if (item instanceof HTMLVideoElement)
        titleKey = "SaveVideoTitle";
      else if (item instanceof HTMLAudioElement)
        titleKey = "SaveAudioTitle";

      saveURL(url, null, titleKey, false, false, makeURI(item.baseURI),
              null, gDocInfo.isContentWindowPrivate);
    }
  } else {
    selectSaveFolder(function(aDirectory) {
      if (aDirectory) {
        var saveAnImage = function(aURIString, aChosenData, aBaseURI) {
          uniqueFile(aChosenData.file);
          internalSave(aURIString, null, null, null, null, false, "SaveImageTitle",
                       aChosenData, aBaseURI, null, false, null, gDocInfo.isContentWindowPrivate);
        };

        for (var i = 0; i < rowArray.length; i++) {
          let v = rowArray[i];
          let dir = aDirectory.clone();
          let item = gImageView.data[v][COL_IMAGE_NODE];
          let uriString = gImageView.data[v][COL_IMAGE_ADDRESS];
          let uri = makeURI(uriString);

          try {
            uri.QueryInterface(Ci.nsIURL);
            dir.append(decodeURIComponent(uri.fileName));
          } catch (ex) {
            // data:/blob: uris
            // Supply a dummy filename, otherwise Download Manager
            // will try to delete the base directory on failure.
            dir.append(gImageView.data[v][COL_IMAGE_TYPE]);
          }

          if (i == 0) {
            saveAnImage(uriString, new AutoChosen(dir, uri), makeURI(item.baseURI));
          } else {
            // This delay is a hack which prevents the download manager
            // from opening many times. See bug 377339.
            setTimeout(saveAnImage, 200, uriString, new AutoChosen(dir, uri),
                       makeURI(item.baseURI));
          }
        }
      }
    });
  }
}

function onBlockImage() {
  var permissionManager = Cc[PERMISSION_CONTRACTID]
                            .getService(nsIPermissionManager);

  var checkbox = document.getElementById("blockImage");
  var uri = makeURI(document.getElementById("imageurltext").value);
  if (checkbox.checked)
    permissionManager.add(uri, "image", nsIPermissionManager.DENY_ACTION);
  else
    permissionManager.remove(uri, "image");
}

function onImageSelect() {
  var previewBox   = document.getElementById("mediaPreviewBox");
  var mediaSaveBox = document.getElementById("mediaSaveBox");
  var splitter     = document.getElementById("mediaSplitter");
  var tree = document.getElementById("imagetree");
  var count = tree.view.selection.count;
  if (count == 0) {
    previewBox.collapsed   = true;
    mediaSaveBox.collapsed = true;
    splitter.collapsed     = true;
    tree.flex = 1;
  } else if (count > 1) {
    splitter.collapsed     = true;
    previewBox.collapsed   = true;
    mediaSaveBox.collapsed = false;
    tree.flex = 1;
  } else {
    mediaSaveBox.collapsed = true;
    splitter.collapsed     = false;
    previewBox.collapsed   = false;
    tree.flex = 0;
    makePreview(getSelectedRows(tree)[0]);
  }
}

// Makes the media preview (image, video, etc) for the selected row on the media tab.
function makePreview(row) {
  var item = gImageView.data[row][COL_IMAGE_NODE];
  var url = gImageView.data[row][COL_IMAGE_ADDRESS];
  var isBG = gImageView.data[row][COL_IMAGE_BG];
  var isAudio = false;

  setItemValue("imageurltext", url);
  setItemValue("imagetext", item.imageText);
  setItemValue("imagelongdesctext", item.longDesc);

  // get cache info
  var cacheKey = url.replace(/#.*$/, "");
  openCacheEntry(cacheKey, function(cacheEntry) {
    // find out the file size
    var sizeText;
    if (cacheEntry) {
      let imageSize = cacheEntry.dataSize;
      var kbSize = Math.round(imageSize / 1024 * 100) / 100;
      sizeText = gBundle.getFormattedString("generalSize",
                                            [formatNumber(kbSize), formatNumber(imageSize)]);
    } else
      sizeText = gBundle.getString("mediaUnknownNotCached");
    setItemValue("imagesizetext", sizeText);

    var mimeType = item.mimeType || this.getContentTypeFromHeaders(cacheEntry);
    var numFrames = item.numFrames;

    var imageType;
    if (mimeType) {
      // We found the type, try to display it nicely
      let imageMimeType = /^image\/(.*)/i.exec(mimeType);
      if (imageMimeType) {
        imageType = imageMimeType[1].toUpperCase();
        if (numFrames > 1)
          imageType = gBundle.getFormattedString("mediaAnimatedImageType",
                                                 [imageType, numFrames]);
        else
          imageType = gBundle.getFormattedString("mediaImageType", [imageType]);
      } else {
        // the MIME type doesn't begin with image/, display the raw type
        imageType = mimeType;
      }
    } else {
      // We couldn't find the type, fall back to the value in the treeview
      imageType = gImageView.data[row][COL_IMAGE_TYPE];
    }
    setItemValue("imagetypetext", imageType);

    var imageContainer = document.getElementById("theimagecontainer");
    var oldImage = document.getElementById("thepreviewimage");

    var isProtocolAllowed = checkProtocol(gImageView.data[row]);

    var newImage = new Image;
    newImage.id = "thepreviewimage";
    var physWidth = 0, physHeight = 0;
    var width = 0, height = 0;

    let serial = Cc["@mozilla.org/network/serialization-helper;1"]
                   .getService(Ci.nsISerializationHelper);
    let triggeringPrinStr = serial.serializeToString(gDocInfo.principal);
    if ((item.HTMLLinkElement || item.HTMLInputElement ||
         item.HTMLImageElement || item.SVGImageElement ||
         (item.HTMLObjectElement && mimeType && mimeType.startsWith("image/")) ||
         isBG) && isProtocolAllowed) {
      // We need to wait for the image to finish loading before using width & height
      newImage.addEventListener("loadend", function() {
        physWidth = newImage.width || 0;
        physHeight = newImage.height || 0;

        // "width" and "height" attributes must be set to newImage,
        // even if there is no "width" or "height attribute in item;
        // otherwise, the preview image cannot be displayed correctly.
        // Since the image might have been loaded out-of-process, we expect
        // the item to tell us its width / height dimensions. Failing that
        // the item should tell us the natural dimensions of the image. Finally
        // failing that, we'll assume that the image was never loaded in the
        // other process (this can be true for favicons, for example), and so
        // we'll assume that we can use the natural dimensions of the newImage
        // we just created. If the natural dimensions of newImage are not known
        // then the image is probably broken.
        if (!isBG) {
          newImage.width = ("width" in item && item.width) || newImage.naturalWidth;
          newImage.height = ("height" in item && item.height) || newImage.naturalHeight;
        } else {
          // the Width and Height of an HTML tag should not be used for its background image
          // (for example, "table" can have "width" or "height" attributes)
          newImage.width = item.naturalWidth || newImage.naturalWidth;
          newImage.height = item.naturalHeight || newImage.naturalHeight;
        }

        if (item.SVGImageElement) {
          newImage.width = item.SVGImageElementWidth;
          newImage.height = item.SVGImageElementHeight;
        }

        width = newImage.width;
        height = newImage.height;

        document.getElementById("theimagecontainer").collapsed = false;
        document.getElementById("brokenimagecontainer").collapsed = true;

        let imageSize = "";
        if (url) {
          if (width != physWidth || height != physHeight) {
            imageSize = gBundle.getFormattedString("mediaDimensionsScaled",
                                                   [formatNumber(physWidth),
                                                    formatNumber(physHeight),
                                                    formatNumber(width),
                                                    formatNumber(height)]);
          } else {
            imageSize = gBundle.getFormattedString("mediaDimensions",
                                                   [formatNumber(width),
                                                    formatNumber(height)]);
          }
        }
        setItemValue("imagedimensiontext", imageSize);
      }, {once: true});

      newImage.setAttribute("triggeringprincipal", triggeringPrinStr);
      newImage.setAttribute("src", url);
    } else {
      // Handle the case where newImage is not used for width & height
      if (item.HTMLVideoElement && isProtocolAllowed) {
        newImage = document.createElementNS("http://www.w3.org/1999/xhtml", "video");
        newImage.id = "thepreviewimage";
        newImage.setAttribute("triggeringprincipal", triggeringPrinStr);
        newImage.src = url;
        newImage.controls = true;
        width = physWidth = item.videoWidth;
        height = physHeight = item.videoHeight;

        document.getElementById("theimagecontainer").collapsed = false;
        document.getElementById("brokenimagecontainer").collapsed = true;
      } else if (item.HTMLAudioElement && isProtocolAllowed) {
        newImage = new Audio;
        newImage.id = "thepreviewimage";
        newImage.setAttribute("triggeringprincipal", triggeringPrinStr);
        newImage.src = url;
        newImage.controls = true;
        isAudio = true;

        document.getElementById("theimagecontainer").collapsed = false;
        document.getElementById("brokenimagecontainer").collapsed = true;
      } else {
        // fallback image for protocols not allowed (e.g., javascript:)
        // or elements not [yet] handled (e.g., object, embed).
        document.getElementById("brokenimagecontainer").collapsed = false;
        document.getElementById("theimagecontainer").collapsed = true;
      }

      let imageSize = "";
      if (url && !isAudio) {
        imageSize = gBundle.getFormattedString("mediaDimensions",
                                               [formatNumber(width),
                                                formatNumber(height)]);
      }
      setItemValue("imagedimensiontext", imageSize);
    }

    makeBlockImage(url);

    imageContainer.removeChild(oldImage);
    imageContainer.appendChild(newImage);
  });
}

function makeBlockImage(url) {
  var permissionManager = Cc[PERMISSION_CONTRACTID]
                            .getService(nsIPermissionManager);

  var checkbox = document.getElementById("blockImage");
  var imagePref = Services.prefs.getIntPref("permissions.default.image");
  if (!(/^https?:/.test(url)) || imagePref == 2)
    // We can't block the images from this host because either is is not
    // for http(s) or we don't load images at all
    checkbox.hidden = true;
  else {
    var uri = makeURI(url);
    if (uri.host) {
      checkbox.hidden = false;
      checkbox.label = gBundle.getFormattedString("mediaBlockImage", [uri.host]);
      var perm = permissionManager.testPermission(uri, "image");
      checkbox.checked = perm == nsIPermissionManager.DENY_ACTION;
    } else
      checkbox.hidden = true;
  }
}

var imagePermissionObserver = {
  observe(aSubject, aTopic, aData) {
    if (document.getElementById("mediaPreviewBox").collapsed)
      return;

    if (aTopic == "perm-changed") {
      var permission = aSubject.QueryInterface(Ci.nsIPermission);
      if (permission.type == "image") {
        var imageTree = document.getElementById("imagetree");
        var row = getSelectedRow(imageTree);
        var url = gImageView.data[row][COL_IMAGE_ADDRESS];
        if (permission.matchesURI(makeURI(url), true)) {
          makeBlockImage(url);
        }
      }
    }
  }
};

function getContentTypeFromHeaders(cacheEntryDescriptor) {
  if (!cacheEntryDescriptor)
    return null;

  let headers = cacheEntryDescriptor.getMetaDataElement("response-head");
  let type = /^Content-Type:\s*(.*?)\s*(?:\;|$)/mi.exec(headers);
  return type && type[1];
}

function setItemValue(id, value) {
  var item = document.getElementById(id);
  if (value) {
    item.parentNode.collapsed = false;
    item.value = value;
  } else
    item.parentNode.collapsed = true;
}

function formatNumber(number) {
  return (+number).toLocaleString(); // coerce number to a numeric value before calling toLocaleString()
}

function formatDate(datestr, unknown) {
  var date = new Date(datestr);
  if (!date.valueOf())
    return unknown;

  const dateTimeFormatter = new Services.intl.DateTimeFormat(undefined, {
    dateStyle: "long", timeStyle: "long"
  });
  return dateTimeFormatter.format(date);
}

function doCopy() {
  if (!gClipboardHelper)
    return;

  var elem = document.commandDispatcher.focusedElement;

  if (elem && "treeBoxObject" in elem) {
    var view = elem.view;
    var selection = view.selection;
    var text = [], tmp = "";
    var min = {}, max = {};

    var count = selection.getRangeCount();

    for (var i = 0; i < count; i++) {
      selection.getRangeAt(i, min, max);

      for (var row = min.value; row <= max.value; row++) {
        view.performActionOnRow("copy", row);

        tmp = elem.getAttribute("copybuffer");
        if (tmp)
          text.push(tmp);
        elem.removeAttribute("copybuffer");
      }
    }
    gClipboardHelper.copyString(text.join("\n"));
  }
}

function doSelectAllMedia() {
  var tree = document.getElementById("imagetree");

  if (tree)
    tree.view.selection.selectAll();
}

function doSelectAll() {
  var elem = document.commandDispatcher.focusedElement;

  if (elem && "treeBoxObject" in elem)
    elem.view.selection.selectAll();
}

function selectImage() {
  if (!gImageElement)
    return;

  var tree = document.getElementById("imagetree");
  for (var i = 0; i < tree.view.rowCount; i++) {
    // If the image row element is the image selected from the "View Image Info" context menu item.
    let image = gImageView.data[i][COL_IMAGE_NODE];
    if (!gImageView.data[i][COL_IMAGE_BG] &&
        gImageElement.currentSrc == gImageView.data[i][COL_IMAGE_ADDRESS] &&
        gImageElement.width == image.width &&
        gImageElement.height == image.height &&
        gImageElement.imageText == image.imageText) {
      tree.view.selection.select(i);
      tree.treeBoxObject.ensureRowIsVisible(i);
      tree.focus();
      return;
    }
  }
}

function checkProtocol(img) {
  var url = img[COL_IMAGE_ADDRESS];
  return /^data:image\//i.test(url) ||
    /^(https?|ftp|file|about|chrome|resource):/.test(url);
}
PK
!<�֌)�F�F4chrome/browser/content/browser/pageinfo/pageInfo.xul<?xml version="1.0"?>

<?xml-stylesheet href="chrome://browser/content/pageinfo/pageInfo.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/pageInfo.css" type="text/css"?>

<!DOCTYPE window [
  <!ENTITY % pageInfoDTD SYSTEM "chrome://browser/locale/pageInfo.dtd">
  %pageInfoDTD;
]>


<window id="main-window"
  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
  windowtype="Browser:page-info"
  onload="onLoadPageInfo()"
  onunload="onUnloadPageInfo()"
  align="stretch"
  screenX="10" screenY="10"
  width="&pageInfoWindow.width;" height="&pageInfoWindow.height;"
  persist="screenX screenY width height sizemode">

  <script type="application/javascript" src="chrome://global/content/globalOverlay.js"/>
  <script type="application/javascript" src="chrome://global/content/contentAreaUtils.js"/>
  <script type="application/javascript" src="chrome://global/content/treeUtils.js"/>
  <script type="application/javascript" src="chrome://browser/content/pageinfo/pageInfo.js"/>
  <script type="application/javascript" src="chrome://browser/content/pageinfo/feeds.js"/>
  <script type="application/javascript" src="chrome://browser/content/pageinfo/permissions.js"/>
  <script type="application/javascript" src="chrome://browser/content/pageinfo/security.js"/>
  <script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>

  <stringbundleset id="pageinfobundleset">
    <stringbundle id="pageinfobundle" src="chrome://browser/locale/pageInfo.properties"/>
    <stringbundle id="pkiBundle" src="chrome://pippki/locale/pippki.properties"/>
    <stringbundle id="browserBundle" src="chrome://browser/locale/browser.properties"/>
  </stringbundleset>

  <commandset id="pageInfoCommandSet">
    <command id="cmd_close"     oncommand="window.close();"/>
    <command id="cmd_help"      oncommand="doHelpButton();"/>
    <command id="cmd_copy"      oncommand="doCopy();"/>
    <command id="cmd_selectall" oncommand="doSelectAll();"/>
  </commandset>

  <keyset id="pageInfoKeySet">
    <key key="&closeWindow.key;" modifiers="accel" command="cmd_close"/>
    <key keycode="VK_ESCAPE"                       command="cmd_close"/>
    <key keycode="VK_F1"                           command="cmd_help"/>
    <key key="&copy.key;"        modifiers="accel" command="cmd_copy"/>
    <key key="&selectall.key;"   modifiers="accel" command="cmd_selectall"/>
    <key key="&selectall.key;"   modifiers="alt"   command="cmd_selectall"/>
  </keyset>

  <menupopup id="picontext">
    <menuitem id="menu_selectall" label="&selectall.label;" command="cmd_selectall" accesskey="&selectall.accesskey;"/>
    <menuitem id="menu_copy"      label="&copy.label;"      command="cmd_copy"      accesskey="&copy.accesskey;"/>
  </menupopup>

  <vbox id="topBar">
    <radiogroup id="viewGroup" class="chromeclass-toolbar" orient="horizontal">
      <radio id="generalTab"  label="&generalTab;"  accesskey="&generalTab.accesskey;"
           oncommand="showTab('general');"/>
      <radio id="mediaTab"    label="&mediaTab;"    accesskey="&mediaTab.accesskey;"
           oncommand="showTab('media');" hidden="true"/>
      <radio id="feedTab"     label="&feedTab;"     accesskey="&feedTab.accesskey;"
           oncommand="showTab('feed');" hidden="true"/>
      <radio id="permTab"     label="&permTab;"     accesskey="&permTab.accesskey;"
           oncommand="showTab('perm');"/>
      <radio id="securityTab" label="&securityTab;" accesskey="&securityTab.accesskey;"
           oncommand="showTab('security');"/>
    </radiogroup>
  </vbox>

  <deck id="mainDeck" flex="1">
    <!-- General page information -->
    <vbox id="generalPanel">
      <grid id="generalGrid">
        <columns>
          <column/>
          <column class="gridSeparator"/>
          <column flex="1"/>
        </columns>
        <rows id="generalRows">
          <row id="generalTitle">
            <label control="titletext" value="&generalTitle;"/>
            <separator/>
            <textbox readonly="true" id="titletext"/>
          </row>
          <row id="generalURLRow">
            <label control="urltext" value="&generalURL;"/>
            <separator/>
            <textbox readonly="true" id="urltext"/>
          </row>
          <row id="generalSeparatorRow1">
            <separator class="thin"/>
          </row>
          <row id="generalTypeRow">
            <label control="typetext" value="&generalType;"/>
            <separator/>
            <textbox readonly="true" id="typetext"/>
          </row>
          <row id="generalModeRow">
            <label control="modetext" value="&generalMode;"/>
            <separator/>
            <textbox readonly="true" crop="end" id="modetext"/>
          </row>
          <row id="generalEncodingRow">
            <label control="encodingtext" value="&generalEncoding2;"/>
            <separator/>
            <textbox readonly="true" id="encodingtext"/>
          </row>
          <row id="generalSizeRow">
            <label control="sizetext" value="&generalSize;"/>
            <separator/>
            <textbox readonly="true" id="sizetext"/>
          </row>
          <row id="generalReferrerRow">
            <label control="refertext" value="&generalReferrer;"/>
            <separator/>
            <textbox readonly="true" id="refertext"/>
          </row>
          <row id="generalSeparatorRow2">
            <separator class="thin"/>
          </row>
          <row id="generalModifiedRow">
            <label control="modifiedtext" value="&generalModified;"/>
            <separator/>
            <textbox readonly="true" id="modifiedtext"/>
          </row>
        </rows>
      </grid>
      <separator class="thin"/>
      <groupbox id="metaTags" flex="1" class="collapsable treebox">
        <caption id="metaTagsCaption" onclick="toggleGroupbox('metaTags');"/>
        <tree id="metatree" flex="1" hidecolumnpicker="true" contextmenu="picontext">
          <treecols>
            <treecol id="meta-name"    label="&generalMetaName;"
                     persist="width" flex="1"
                     onclick="gMetaView.onPageMediaSort('meta-name');"/>
            <splitter class="tree-splitter"/>
            <treecol id="meta-content" label="&generalMetaContent;"
                     persist="width" flex="4"
                     onclick="gMetaView.onPageMediaSort('meta-content');"/>
          </treecols>
          <treechildren id="metatreechildren" flex="1"/>
        </tree>        
      </groupbox>
      <hbox pack="end">
        <button command="cmd_help" label="&helpButton.label;" dlgtype="help"/>
      </hbox>
    </vbox>

    <!-- Media information -->
    <vbox id="mediaPanel">
      <tree id="imagetree" onselect="onImageSelect();" contextmenu="picontext"
            ondragstart="onBeginLinkDrag(event,'image-address','image-alt')">
        <treecols>
          <treecol primary="true" persist="width" flex="10"
                        width="10" id="image-address" label="&mediaAddress;"
                        onclick="gImageView.onPageMediaSort('image-address');"/>
          <splitter class="tree-splitter"/>
          <treecol persist="hidden width" flex="2"
                        width="2"  id="image-type"    label="&mediaType;"
                        onclick="gImageView.onPageMediaSort('image-type');"/>
          <splitter class="tree-splitter"/>
          <treecol hidden="true" persist="hidden width" flex="2"
                        width="2"  id="image-size"  label="&mediaSize;" value="size"
                        onclick="gImageView.onPageMediaSort('image-size');"/>
          <splitter class="tree-splitter"/>
          <treecol hidden="true" persist="hidden width" flex="4"
                        width="4"  id="image-alt"    label="&mediaAltHeader;"
                        onclick="gImageView.onPageMediaSort('image-alt');"/>
          <splitter class="tree-splitter"/>
          <treecol hidden="true" persist="hidden width" flex="1"
                        width="1"  id="image-count"    label="&mediaCount;"
                        onclick="gImageView.onPageMediaSort('image-count');"/>
        </treecols>
        <treechildren id="imagetreechildren" flex="1"/>
      </tree>
      <splitter orient="vertical" id="mediaSplitter"/>
      <vbox flex="1" id="mediaPreviewBox" collapsed="true">
        <grid id="mediaGrid">
          <columns>
            <column id="mediaLabelColumn"/>
            <column class="gridSeparator"/>
            <column flex="1"/>
          </columns>
          <rows id="mediaRows">
            <row id="mediaLocationRow">
              <label control="imageurltext" value="&mediaLocation;"/>
              <separator/>
              <textbox readonly="true" id="imageurltext"/>
            </row>
            <row id="mediaTypeRow">
              <label control="imagetypetext" value="&generalType;"/>
              <separator/>
              <textbox readonly="true" id="imagetypetext"/>
            </row>
            <row id="mediaSizeRow">
              <label control="imagesizetext" value="&generalSize;"/>
              <separator/>
              <textbox readonly="true" id="imagesizetext"/>
            </row>
            <row id="mediaDimensionRow">
              <label control="imagedimensiontext" value="&mediaDimension;"/>
              <separator/>
              <textbox readonly="true" id="imagedimensiontext"/>
            </row>
            <row id="mediaTextRow">
              <label control="imagetext" value="&mediaText;"/>
              <separator/>
              <textbox readonly="true" id="imagetext"/>
            </row>
            <row id="mediaLongdescRow">
              <label control="imagelongdesctext" value="&mediaLongdesc;"/>
              <separator/>
              <textbox readonly="true" id="imagelongdesctext"/>
            </row>
          </rows>
        </grid>
        <hbox id="imageSaveBox" align="end">
          <vbox id="blockImageBox">
            <checkbox id="blockImage" hidden="true" oncommand="onBlockImage()"
                      accesskey="&mediaBlockImage.accesskey;"/>
            <label control="thepreviewimage" value="&mediaPreview;" class="header"/>
          </vbox>
          <spacer id="imageSaveBoxSpacer" flex="1"/>
          <button label="&selectall.label;" accesskey="&selectall.accesskey;"
                  id="selectallbutton"
                  oncommand="doSelectAllMedia();"/>
          <button label="&mediaSaveAs;" accesskey="&mediaSaveAs.accesskey;"
                  icon="save" id="imagesaveasbutton"
                  oncommand="saveMedia();"/>
        </hbox>
        <vbox id="imagecontainerbox" flex="1" pack="center">
          <hbox id="theimagecontainer" pack="center">
            <image id="thepreviewimage"/>
          </hbox>
          <hbox id="brokenimagecontainer" pack="center" collapsed="true">
            <image id="brokenimage" src="resource://gre-resources/broken-image.png"/>
          </hbox>
        </vbox>
      </vbox>
      <hbox id="mediaSaveBox" collapsed="true">
        <spacer id="mediaSaveBoxSpacer" flex="1"/>
        <button label="&mediaSaveAs;" accesskey="&mediaSaveAs2.accesskey;"
                icon="save" id="mediasaveasbutton"
                oncommand="saveMedia();"/>
      </hbox>
      <hbox pack="end">
        <button command="cmd_help" label="&helpButton.label;" dlgtype="help"/>
      </hbox>
    </vbox>

    <!-- Feeds -->
    <vbox id="feedPanel">
      <richlistbox id="feedListbox" flex="1"/>
    </vbox>

    <!-- Permissions -->
    <vbox id="permPanel">
      <hbox id="permHostBox">
        <label value="&permissionsFor;" control="hostText" />
        <textbox id="hostText" class="header" readonly="true"
                 crop="end" flex="1"/>
      </hbox>

      <vbox id="permList" flex="1"/>
      <hbox pack="end">
        <button command="cmd_help" label="&helpButton.label;" dlgtype="help"/>
      </hbox>
    </vbox>

    <!-- Security & Privacy -->
    <vbox id="securityPanel">
      <!-- Identity Section -->
      <groupbox id="security-identity-groupbox" flex="1">
        <caption id="security-identity" label="&securityView.identity.header;"/>
        <grid id="security-identity-grid" flex="1">
          <columns>
            <column/>
            <column flex="1"/>
          </columns>
          <rows id="security-identity-rows">
            <!-- Domain -->
            <row id="security-identity-domain-row">
              <label id="security-identity-domain-label"
                     class="fieldLabel"
                     value="&securityView.identity.domain;"
                     control="security-identity-domain-value"/>
              <textbox id="security-identity-domain-value"
                       class="fieldValue" readonly="true"/>
            </row>
            <!-- Owner -->
            <row id="security-identity-owner-row">
              <label id="security-identity-owner-label"
                     class="fieldLabel"
                     value="&securityView.identity.owner;"
                     control="security-identity-owner-value"/>
              <textbox id="security-identity-owner-value"
                       class="fieldValue" readonly="true"/>
            </row>
            <!-- Verifier -->
            <row id="security-identity-verifier-row">
              <label id="security-identity-verifier-label"
                     class="fieldLabel"
                     value="&securityView.identity.verifier;"
                     control="security-identity-verifier-value"/>
              <textbox id="security-identity-verifier-value"
                       class="fieldValue" readonly="true" />
            </row>
            <!-- Certificate Validity -->
            <row id="security-identity-validity-row">
              <label id="security-identity-validity-label"
                     class="fieldLabel"
                     value="&securityView.identity.validity;"
                     control="security-identity-validity-value"/>
              <textbox id="security-identity-validity-value"
                       class="fieldValue" readonly="true" />
            </row>
          </rows>
        </grid>
        <spacer flex="1"/>
        <!-- Cert button -->
        <hbox id="security-view-cert-box" pack="end">
          <button id="security-view-cert" label="&securityView.certView;"
                  accesskey="&securityView.accesskey;"
                  oncommand="security.viewCert();"/>
        </hbox>
      </groupbox>
      
      <!-- Privacy & History section -->
      <groupbox id="security-privacy-groupbox" flex="1">
        <caption id="security-privacy" label="&securityView.privacy.header;" />
        <grid id="security-privacy-grid">
          <columns>
            <column flex="1"/>
            <column flex="1"/>
          </columns>
          <rows id="security-privacy-rows">
            <!-- History -->
            <row id="security-privacy-history-row">
              <label id="security-privacy-history-label"
                           control="security-privacy-history-value"
                           class="fieldLabel">&securityView.privacy.history;</label>
              <textbox id="security-privacy-history-value"
                       class="fieldValue"
                       value="&securityView.unknown;"
                       readonly="true"/>
            </row>
            <!-- Cookies -->
            <row id="security-privacy-cookies-row">
              <label id="security-privacy-cookies-label"
                           control="security-privacy-cookies-value"
                           class="fieldLabel">&securityView.privacy.cookies;</label>
              <hbox id="security-privacy-cookies-box" align="center">
                <textbox id="security-privacy-cookies-value"
                         class="fieldValue"
                         value="&securityView.unknown;"
                         flex="1"
                         readonly="true"/>
                <button id="security-view-cookies"
                        label="&securityView.privacy.viewCookies;"
                        accesskey="&securityView.privacy.viewCookies.accessKey;"
                        oncommand="security.viewCookies();"/>
              </hbox>
            </row>
            <!-- Passwords -->
            <row id="security-privacy-passwords-row">
              <label id="security-privacy-passwords-label"
                            control="security-privacy-passwords-value"
                            class="fieldLabel">&securityView.privacy.passwords;</label>
              <hbox id="security-privacy-passwords-box" align="center">
                <textbox id="security-privacy-passwords-value"
                         class="fieldValue"
                         value="&securityView.unknown;"
                         flex="1"
                         readonly="true"/>
                <button id="security-view-password"
                        label="&securityView.privacy.viewPasswords;"
                        accesskey="&securityView.privacy.viewPasswords.accessKey;"
                        oncommand="security.viewPasswords();"/>
              </hbox>
            </row>
          </rows>
        </grid>
      </groupbox>
      
      <!-- Technical Details section -->
      <groupbox id="security-technical-groupbox" flex="1">
        <caption id="security-technical" label="&securityView.technical.header;" />
        <vbox id="security-technical-box" flex="1">
          <label id="security-technical-shortform" class="fieldValue"/>
          <description id="security-technical-longform1" class="fieldLabel"/>
          <description id="security-technical-longform2" class="fieldLabel"/>
          <description id="security-technical-certificate-transparency" class="fieldLabel"/>
        </vbox>
      </groupbox>
      <hbox pack="end">
        <button command="cmd_help" label="&helpButton.label;" dlgtype="help"/>
      </hbox>
    </vbox>
    <!-- Others added by overlay -->
  </deck>


</window>
PK
!<���

6chrome/browser/content/browser/pageinfo/permissions.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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-globals-from pageInfo.js */

ChromeUtils.import("resource:///modules/SitePermissions.jsm");
ChromeUtils.import("resource://gre/modules/BrowserUtils.jsm");

var gPermURI;
var gPermPrincipal;
var gUsageRequest;

// Array of permissionIDs sorted alphabetically by label.
var gPermissions = SitePermissions.listPermissions().sort((a, b) => {
  let firstLabel = SitePermissions.getPermissionLabel(a);
  let secondLabel = SitePermissions.getPermissionLabel(b);
  return firstLabel.localeCompare(secondLabel);
});

var permissionObserver = {
  observe(aSubject, aTopic, aData) {
    if (aTopic == "perm-changed") {
      var permission = aSubject.QueryInterface(Ci.nsIPermission);
      if (permission.matchesURI(gPermURI, true) && gPermissions.includes(permission.type)) {
          initRow(permission.type);
      }
    }
  }
};

function onLoadPermission(uri, principal) {
  var permTab = document.getElementById("permTab");
  if (SitePermissions.isSupportedURI(uri)) {
    gPermURI = uri;
    gPermPrincipal = principal;
    var hostText = document.getElementById("hostText");
    hostText.value = gPermURI.displayPrePath;

    for (var i of gPermissions)
      initRow(i);
    Services.obs.addObserver(permissionObserver, "perm-changed");
    onUnloadRegistry.push(onUnloadPermission);
    permTab.hidden = false;
  } else
    permTab.hidden = true;
}

function onUnloadPermission() {
  Services.obs.removeObserver(permissionObserver, "perm-changed");

  if (gUsageRequest) {
    gUsageRequest.cancel();
    gUsageRequest = null;
  }
}

function initRow(aPartId) {
  createRow(aPartId);

  var checkbox = document.getElementById(aPartId + "Def");
  var command  = document.getElementById("cmd_" + aPartId + "Toggle");
  var {state, scope} = SitePermissions.get(gPermURI, aPartId);
  let defaultState = SitePermissions.getDefault(aPartId);

  // When flash permission state is "Hide", we show it as "Always Ask" in page info.
  if (aPartId.startsWith("plugin") && state == SitePermissions.PROMPT_HIDE) {
    defaultState == SitePermissions.UNKNOWN ? state = defaultState : state = SitePermissions.PROMPT;
  }

  if (state != defaultState) {
    checkbox.checked = false;
    command.removeAttribute("disabled");
  } else {
    checkbox.checked = true;
    command.setAttribute("disabled", "true");
  }

  if (scope == SitePermissions.SCOPE_POLICY) {
    checkbox.setAttribute("disabled", "true");
    command.setAttribute("disabled", "true");
  }

  setRadioState(aPartId, state);
}

function createRow(aPartId) {
  let rowId = "perm-" + aPartId + "-row";
  if (document.getElementById(rowId))
    return;

  let commandId = "cmd_" + aPartId + "Toggle";
  let labelId = "perm-" + aPartId + "-label";
  let radiogroupId = aPartId + "RadioGroup";

  let command = document.createElement("command");
  command.setAttribute("id", commandId);
  command.setAttribute("oncommand", "onRadioClick('" + aPartId + "');");
  document.getElementById("pageInfoCommandSet").appendChild(command);

  let row = document.createElement("vbox");
  row.setAttribute("id", rowId);
  row.setAttribute("class", "permission");

  let label = document.createElement("label");
  label.setAttribute("id", labelId);
  label.setAttribute("control", radiogroupId);
  label.setAttribute("value", SitePermissions.getPermissionLabel(aPartId));
  label.setAttribute("class", "permissionLabel");
  row.appendChild(label);

  let controls = document.createElement("hbox");
  controls.setAttribute("role", "group");
  controls.setAttribute("aria-labelledby", labelId);

  let checkbox = document.createElement("checkbox");
  checkbox.setAttribute("id", aPartId + "Def");
  checkbox.setAttribute("oncommand", "onCheckboxClick('" + aPartId + "');");
  checkbox.setAttribute("label", gBundle.getString("permissions.useDefault"));
  controls.appendChild(checkbox);

  let spacer = document.createElement("spacer");
  spacer.setAttribute("flex", "1");
  controls.appendChild(spacer);

  let radiogroup = document.createElement("radiogroup");
  radiogroup.setAttribute("id", radiogroupId);
  radiogroup.setAttribute("orient", "horizontal");
  for (let state of SitePermissions.getAvailableStates(aPartId)) {
    let radio = document.createElement("radio");
    radio.setAttribute("id", aPartId + "#" + state);
    radio.setAttribute("label", SitePermissions.getMultichoiceStateLabel(state));
    radio.setAttribute("command", commandId);
    radiogroup.appendChild(radio);
  }
  controls.appendChild(radiogroup);

  row.appendChild(controls);

  document.getElementById("permList").appendChild(row);
}

function onCheckboxClick(aPartId) {
  var command  = document.getElementById("cmd_" + aPartId + "Toggle");
  var checkbox = document.getElementById(aPartId + "Def");
  if (checkbox.checked) {
    SitePermissions.remove(gPermURI, aPartId);
    command.setAttribute("disabled", "true");
    var perm = SitePermissions.getDefault(aPartId);
    setRadioState(aPartId, perm);
  } else {
    onRadioClick(aPartId);
    command.removeAttribute("disabled");
  }
}

function onRadioClick(aPartId) {
  var radioGroup = document.getElementById(aPartId + "RadioGroup");
  var id = radioGroup.selectedItem.id;
  var permission = parseInt(id.split("#")[1]);
  SitePermissions.set(gPermURI, aPartId, permission);
}

function setRadioState(aPartId, aValue) {
  var radio = document.getElementById(aPartId + "#" + aValue);
  if (radio) {
    radio.radioGroup.selectedItem = radio;
  }
}
PK
!<�k���,�,3chrome/browser/content/browser/pageinfo/security.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

ChromeUtils.import("resource://gre/modules/BrowserUtils.jsm");

/* import-globals-from pageInfo.js */

ChromeUtils.defineModuleGetter(this, "LoginHelper",
                               "resource://gre/modules/LoginHelper.jsm");
ChromeUtils.defineModuleGetter(this, "PluralForm",
                               "resource://gre/modules/PluralForm.jsm");

var security = {
  init(uri, windowInfo) {
    this.uri = uri;
    this.windowInfo = windowInfo;
  },

  // Display the server certificate (static)
  viewCert() {
    var cert = security._cert;
    viewCertHelper(window, cert);
  },

  _getSecurityInfo() {
    const nsISSLStatusProvider = Ci.nsISSLStatusProvider;
    const nsISSLStatus = Ci.nsISSLStatus;

    // We don't have separate info for a frame, return null until further notice
    // (see bug 138479)
    if (!this.windowInfo.isTopWindow)
      return null;

    var hostName = this.windowInfo.hostName;

    var ui = security._getSecurityUI();
    if (!ui)
      return null;

    var isBroken =
      (ui.state & Ci.nsIWebProgressListener.STATE_IS_BROKEN);
    var isMixed =
      (ui.state & (Ci.nsIWebProgressListener.STATE_LOADED_MIXED_ACTIVE_CONTENT |
                   Ci.nsIWebProgressListener.STATE_LOADED_MIXED_DISPLAY_CONTENT));
    var isInsecure =
      (ui.state & Ci.nsIWebProgressListener.STATE_IS_INSECURE);
    var isEV =
      (ui.state & Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL);
    ui.QueryInterface(nsISSLStatusProvider);
    var status = ui.SSLStatus;

    if (!isInsecure && status) {
      status.QueryInterface(nsISSLStatus);
      var cert = status.serverCert;
      var issuerName = cert.issuerOrganization || cert.issuerName;

      var retval = {
        hostName,
        cAName: issuerName,
        encryptionAlgorithm: undefined,
        encryptionStrength: undefined,
        version: undefined,
        isBroken,
        isMixed,
        isEV,
        cert,
        certificateTransparency: undefined
      };

      var version;
      try {
        retval.encryptionAlgorithm = status.cipherName;
        retval.encryptionStrength = status.secretKeyLength;
        version = status.protocolVersion;
      } catch (e) {
      }

      switch (version) {
        case nsISSLStatus.SSL_VERSION_3:
          retval.version = "SSL 3";
          break;
        case nsISSLStatus.TLS_VERSION_1:
          retval.version = "TLS 1.0";
          break;
        case nsISSLStatus.TLS_VERSION_1_1:
          retval.version = "TLS 1.1";
          break;
        case nsISSLStatus.TLS_VERSION_1_2:
          retval.version = "TLS 1.2";
          break;
        case nsISSLStatus.TLS_VERSION_1_3:
          retval.version = "TLS 1.3";
          break;
      }

      // Select the status text to display for Certificate Transparency.
      // Since we do not yet enforce the CT Policy on secure connections,
      // we must not complain on policy discompliance (it might be viewed
      // as a security issue by the user).
      switch (status.certificateTransparencyStatus) {
        case nsISSLStatus.CERTIFICATE_TRANSPARENCY_NOT_APPLICABLE:
        case nsISSLStatus.CERTIFICATE_TRANSPARENCY_POLICY_NOT_ENOUGH_SCTS:
        case nsISSLStatus.CERTIFICATE_TRANSPARENCY_POLICY_NOT_DIVERSE_SCTS:
          retval.certificateTransparency = null;
          break;
        case nsISSLStatus.CERTIFICATE_TRANSPARENCY_POLICY_COMPLIANT:
          retval.certificateTransparency = "Compliant";
          break;
      }

      return retval;
    }
    return {
      hostName,
      cAName: "",
      encryptionAlgorithm: "",
      encryptionStrength: 0,
      version: "",
      isBroken,
      isMixed,
      isEV,
      cert: null,
      certificateTransparency: null
    };
  },

  // Find the secureBrowserUI object (if present)
  _getSecurityUI() {
    if (window.opener.gBrowser)
      return window.opener.gBrowser.securityUI;
    return null;
  },

  /**
   * Open the cookie manager window
   */
  viewCookies() {
    var win = Services.wm.getMostRecentWindow("Browser:Cookies");

    var eTLD;
    try {
      eTLD = Services.eTLD.getBaseDomain(this.uri);
    } catch (e) {
      // getBaseDomain will fail if the host is an IP address or is empty
      eTLD = this.uri.asciiHost;
    }

    if (win) {
      win.gCookiesWindow.setFilter(eTLD);
      win.focus();
    } else
      window.openDialog("chrome://browser/content/preferences/cookies.xul",
                        "Browser:Cookies", "", {filterString: eTLD});
  },

  /**
   * Open the login manager window
   */
  viewPasswords() {
    LoginHelper.openPasswordManager(window, this._getSecurityInfo().hostName);
  },

  _cert: null
};

function securityOnLoad(uri, windowInfo) {
  security.init(uri, windowInfo);

  var info = security._getSecurityInfo();
  if (!info || uri.scheme === "about") {
    document.getElementById("securityTab").hidden = true;
    return;
  }
  document.getElementById("securityTab").hidden = false;

  const pageInfoBundle = document.getElementById("pageinfobundle");

  /* Set Identity section text */
  setText("security-identity-domain-value", info.hostName);

  var owner, verifier, validity;
  if (info.cert && !info.isBroken) {
    validity = info.cert.validity.notAfterLocalDay;

    // Try to pull out meaningful values.  Technically these fields are optional
    // so we'll employ fallbacks where appropriate.  The EV spec states that Org
    // fields must be specified for subject and issuer so that case is simpler.
    if (info.isEV) {
      owner = info.cert.organization;
      verifier = info.cAName;
    } else {
      // Technically, a non-EV cert might specify an owner in the O field or not,
      // depending on the CA's issuing policies.  However we don't have any programmatic
      // way to tell those apart, and no policy way to establish which organization
      // vetting standards are good enough (that's what EV is for) so we default to
      // treating these certs as domain-validated only.
      owner = pageInfoBundle.getString("securityNoOwner");
      verifier = info.cAName || info.cert.issuerCommonName || info.cert.issuerName;
    }
  } else {
    // We don't have valid identity credentials.
    owner = pageInfoBundle.getString("securityNoOwner");
    verifier = pageInfoBundle.getString("notset");
  }

  setText("security-identity-owner-value", owner);
  setText("security-identity-verifier-value", verifier);
  if (validity) {
    setText("security-identity-validity-value", validity);
  } else {
    document.getElementById("security-identity-validity-row").hidden = true;
  }

  /* Manage the View Cert button*/
  var viewCert = document.getElementById("security-view-cert");
  if (info.cert) {
    security._cert = info.cert;
    viewCert.collapsed = false;
  } else
    viewCert.collapsed = true;

  /* Set Privacy & History section text */
  var yesStr = pageInfoBundle.getString("yes");
  var noStr = pageInfoBundle.getString("no");

  setText("security-privacy-cookies-value",
          hostHasCookies(uri) ? yesStr : noStr);
  setText("security-privacy-passwords-value",
          realmHasPasswords(uri) ? yesStr : noStr);

  var visitCount = previousVisitCount(info.hostName);

  let visitCountStr = visitCount > 0
    ? PluralForm.get(visitCount, pageInfoBundle.getString("securityVisitsNumber"))
        .replace("#1", visitCount.toLocaleString())
    : pageInfoBundle.getString("securityNoVisits");
  setText("security-privacy-history-value", visitCountStr);

  /* Set the Technical Detail section messages */
  const pkiBundle = document.getElementById("pkiBundle");
  var hdr;
  var msg1;
  var msg2;

  if (info.isBroken) {
    if (info.isMixed) {
      hdr = pkiBundle.getString("pageInfo_MixedContent");
      msg1 = pkiBundle.getString("pageInfo_MixedContent2");
    } else {
      hdr = pkiBundle.getFormattedString("pageInfo_BrokenEncryption",
                                         [info.encryptionAlgorithm,
                                          info.encryptionStrength + "",
                                          info.version]);
      msg1 = pkiBundle.getString("pageInfo_WeakCipher");
    }
    msg2 = pkiBundle.getString("pageInfo_Privacy_None2");
  } else if (info.encryptionStrength > 0) {
    hdr = pkiBundle.getFormattedString("pageInfo_EncryptionWithBitsAndProtocol",
                                       [info.encryptionAlgorithm,
                                        info.encryptionStrength + "",
                                        info.version]);
    msg1 = pkiBundle.getString("pageInfo_Privacy_Encrypted1");
    msg2 = pkiBundle.getString("pageInfo_Privacy_Encrypted2");
    security._cert = info.cert;
  } else {
    hdr = pkiBundle.getString("pageInfo_NoEncryption");
    if (info.hostName != null)
      msg1 = pkiBundle.getFormattedString("pageInfo_Privacy_None1", [info.hostName]);
    else
      msg1 = pkiBundle.getString("pageInfo_Privacy_None4");
    msg2 = pkiBundle.getString("pageInfo_Privacy_None2");
  }
  setText("security-technical-shortform", hdr);
  setText("security-technical-longform1", msg1);
  setText("security-technical-longform2", msg2);

  const ctStatus =
    document.getElementById("security-technical-certificate-transparency");
  if (info.certificateTransparency) {
    ctStatus.hidden = false;
    ctStatus.value = pkiBundle.getString(
      "pageInfo_CertificateTransparency_" + info.certificateTransparency);
  } else {
    ctStatus.hidden = true;
  }
}

function setText(id, value) {
  var element = document.getElementById(id);
  if (!element)
    return;
  if (element.localName == "textbox" || element.localName == "label")
    element.value = value;
  else {
    if (element.hasChildNodes())
      element.firstChild.remove();
    var textNode = document.createTextNode(value);
    element.appendChild(textNode);
  }
}

function viewCertHelper(parent, cert) {
  if (!cert)
    return;

  var cd = Cc[CERTIFICATEDIALOGS_CONTRACTID].getService(nsICertificateDialogs);
  cd.viewCert(parent, cert);
}

/**
 * Return true iff we have cookies for uri
 */
function hostHasCookies(uri) {
  return Services.cookies.countCookiesFromHost(uri.asciiHost) > 0;
}

/**
 * Return true iff realm (proto://host:port) (extracted from uri) has
 * saved passwords
 */
function realmHasPasswords(uri) {
  return Services.logins.countLogins(uri.prePath, "", "") > 0;
}

/**
 * Return the number of previous visits recorded for host before today.
 *
 * @param host - the domain name to look for in history
 */
function previousVisitCount(host, endTimeReference) {
  if (!host)
    return false;

  var historyService = Cc["@mozilla.org/browser/nav-history-service;1"]
                         .getService(Ci.nsINavHistoryService);

  var options = historyService.getNewQueryOptions();
  options.resultType = options.RESULTS_AS_VISIT;

  // Search for visits to this host before today
  var query = historyService.getNewQuery();
  query.endTimeReference = query.TIME_RELATIVE_TODAY;
  query.endTime = 0;
  query.domain = host;

  var result = historyService.executeQuery(query, options);
  result.root.containerOpen = true;
  var cc = result.root.childCount;
  result.root.containerOpen = false;
  return cc;
}
PK
!<�XdޣH�H;chrome/browser/content/browser/places/bookmarkProperties.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
 * The panel is initialized based on data given in the js object passed
 * as window.arguments[0]. The object must have the following fields set:
 *   @ action (String). Possible values:
 *     - "add" - for adding a new item.
 *       @ type (String). Possible values:
 *         - "bookmark"
 *           @ loadBookmarkInSidebar - optional, the default state for the
 *             "Load this bookmark in the sidebar" field.
 *         - "folder"
 *           @ URIList (Array of nsIURI objects) - optional, list of uris to
 *             be bookmarked under the new folder.
 *         - "livemark"
 *       @ uri (nsIURI object) - optional, the default uri for the new item.
 *         The property is not used for the "folder with items" type.
 *       @ title (String) - optional, the default title for the new item.
 *       @ description (String) - optional, the default description for the new
 *         item.
 *       @ defaultInsertionPoint (InsertionPoint JS object) - optional, the
 *         default insertion point for the new item.
 *       @ keyword (String) - optional, the default keyword for the new item.
 *       @ postData (String) - optional, POST data to accompany the keyword.
 *       @ charSet (String) - optional, character-set to accompany the keyword.
 *      Notes:
 *        1) If |uri| is set for a bookmark/livemark item and |title| isn't,
 *           the dialog will query the history tables for the title associated
 *           with the given uri. If the dialog is set to adding a folder with
 *           bookmark items under it (see URIList), a default static title is
 *           used ("[Folder Name]").
 *        2) The index field of the default insertion point is ignored if
 *           the folder picker is shown.
 *     - "edit" - for editing a bookmark item or a folder.
 *       @ type (String). Possible values:
 *         - "bookmark"
 *           @ node (an nsINavHistoryResultNode object) - a node representing
 *             the bookmark.
 *         - "folder" (also applies to livemarks)
 *           @ node (an nsINavHistoryResultNode object) - a node representing
 *             the folder.
 *   @ hiddenRows (Strings array) - optional, list of rows to be hidden
 *     regardless of the item edited or added by the dialog.
 *     Possible values:
 *     - "title"
 *     - "location"
 *     - "description"
 *     - "keyword"
 *     - "tags"
 *     - "loadInSidebar"
 *     - "folderPicker" - hides both the tree and the menu.
 *
 * window.arguments[0].performed is set to true if any transaction has
 * been performed by the dialog.
 */

/* import-globals-from editBookmarkOverlay.js */

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
                               "resource://gre/modules/PrivateBrowsingUtils.jsm");

const BOOKMARK_ITEM = 0;
const BOOKMARK_FOLDER = 1;
const LIVEMARK_CONTAINER = 2;

const ACTION_EDIT = 0;
const ACTION_ADD = 1;

var elementsHeight = new Map();

var BookmarkPropertiesPanel = {

  /** UI Text Strings */
  __strings: null,
  get _strings() {
    if (!this.__strings) {
      this.__strings = document.getElementById("stringBundle");
    }
    return this.__strings;
  },

  _action: null,
  _itemType: null,
  _itemId: -1,
  _uri: null,
  _loadInSidebar: false,
  _title: "",
  _description: "",
  _URIs: [],
  _keyword: "",
  _postData: null,
  _charSet: "",
  _feedURI: null,
  _siteURI: null,

  _defaultInsertionPoint: null,
  _hiddenRows: [],

  /**
   * This method returns the correct label for the dialog's "accept"
   * button based on the variant of the dialog.
   */
  _getAcceptLabel: function BPP__getAcceptLabel() {
    if (this._action == ACTION_ADD) {
      if (this._URIs.length)
        return this._strings.getString("dialogAcceptLabelAddMulti");

      if (this._itemType == LIVEMARK_CONTAINER)
        return this._strings.getString("dialogAcceptLabelAddLivemark");

      if (this._dummyItem || this._loadInSidebar)
        return this._strings.getString("dialogAcceptLabelAddItem");

      return this._strings.getString("dialogAcceptLabelSaveItem");
    }
    return this._strings.getString("dialogAcceptLabelEdit");
  },

  /**
   * This method returns the correct title for the current variant
   * of this dialog.
   */
  _getDialogTitle: function BPP__getDialogTitle() {
    if (this._action == ACTION_ADD) {
      if (this._itemType == BOOKMARK_ITEM)
        return this._strings.getString("dialogTitleAddBookmark");
      if (this._itemType == LIVEMARK_CONTAINER)
        return this._strings.getString("dialogTitleAddLivemark");

      // add folder
      NS_ASSERT(this._itemType == BOOKMARK_FOLDER, "Unknown item type");
      if (this._URIs.length)
        return this._strings.getString("dialogTitleAddMulti");

      return this._strings.getString("dialogTitleAddFolder");
    }
    if (this._action == ACTION_EDIT) {
      return this._strings.getFormattedString("dialogTitleEdit", [this._title]);
    }
    return "";
  },

  /**
   * Determines the initial data for the item edited or added by this dialog
   */
  async _determineItemInfo() {
    let dialogInfo = window.arguments[0];
    this._action = dialogInfo.action == "add" ? ACTION_ADD : ACTION_EDIT;
    this._hiddenRows = dialogInfo.hiddenRows ? dialogInfo.hiddenRows : [];
    if (this._action == ACTION_ADD) {
      NS_ASSERT("type" in dialogInfo, "missing type property for add action");

      if ("title" in dialogInfo)
        this._title = dialogInfo.title;

      if ("defaultInsertionPoint" in dialogInfo) {
        this._defaultInsertionPoint = dialogInfo.defaultInsertionPoint;
      } else {
        this._defaultInsertionPoint =
          new PlacesInsertionPoint({
            parentId: PlacesUtils.bookmarksMenuFolderId,
            parentGuid: PlacesUtils.bookmarks.menuGuid
          });
      }

      switch (dialogInfo.type) {
        case "bookmark":
          this._itemType = BOOKMARK_ITEM;
          if ("uri" in dialogInfo) {
            NS_ASSERT(dialogInfo.uri instanceof Ci.nsIURI,
                      "uri property should be a uri object");
            this._uri = dialogInfo.uri;
            if (typeof(this._title) != "string") {
              this._title = await PlacesUtils.history.fetch(this._uri) ||
                            this._uri.spec;
            }
          } else {
            this._uri = Services.io.newURI("about:blank");
            this._title = this._strings.getString("newBookmarkDefault");
            this._dummyItem = true;
          }

          if ("loadBookmarkInSidebar" in dialogInfo)
            this._loadInSidebar = dialogInfo.loadBookmarkInSidebar;

          if ("keyword" in dialogInfo) {
            this._keyword = dialogInfo.keyword;
            this._isAddKeywordDialog = true;
            if ("postData" in dialogInfo)
              this._postData = dialogInfo.postData;
            if ("charSet" in dialogInfo)
              this._charSet = dialogInfo.charSet;
          }
          break;

        case "folder":
          this._itemType = BOOKMARK_FOLDER;
          if (!this._title) {
            if ("URIList" in dialogInfo) {
              this._title = this._strings.getString("bookmarkAllTabsDefault");
              this._URIs = dialogInfo.URIList;
            } else
              this._title = this._strings.getString("newFolderDefault");
              this._dummyItem = true;
          }
          break;

        case "livemark":
          this._itemType = LIVEMARK_CONTAINER;
          if ("feedURI" in dialogInfo)
            this._feedURI = dialogInfo.feedURI;
          if ("siteURI" in dialogInfo)
            this._siteURI = dialogInfo.siteURI;

          if (!this._title) {
            if (this._feedURI) {
              this._title = await PlacesUtils.history.fetch(this._feedURI) ||
                            this._feedURI.spec;
            } else
              this._title = this._strings.getString("newLivemarkDefault");
          }
      }

      if ("description" in dialogInfo)
        this._description = dialogInfo.description;
    } else { // edit
      this._node = dialogInfo.node;
      this._title = this._node.title;
      if (PlacesUtils.nodeIsFolder(this._node))
        this._itemType = BOOKMARK_FOLDER;
      else if (PlacesUtils.nodeIsURI(this._node))
        this._itemType = BOOKMARK_ITEM;
    }
  },

  /**
   * This method should be called by the onload of the Bookmark Properties
   * dialog to initialize the state of the panel.
   */
  async onDialogLoad() {
    await this._determineItemInfo();

    document.title = this._getDialogTitle();

    // Disable the buttons until we have all the information required.
    let acceptButton = document.documentElement.getButton("accept");
    acceptButton.disabled = true;

    // Allow initialization to complete in a truely async manner so that we're
    // not blocking the main thread.
    this._initDialog().catch(ex => {
      Cu.reportError(`Failed to initialize dialog: ${ex}`);
    });
  },

  /**
   * Initializes the dialog, gathering the required bookmark data. This function
   * will enable the accept button (if appropraite) when it is complete.
   */
  async _initDialog() {
    let acceptButton = document.documentElement.getButton("accept");
    acceptButton.label = this._getAcceptLabel();
    let acceptButtonDisabled = false;

    // Do not use sizeToContent, otherwise, due to bug 90276, the dialog will
    // grow at every opening.
    // Since elements can be uncollapsed asynchronously, we must observe their
    // mutations and resize the dialog using a cached element size.
    this._height = window.outerHeight;
    this._mutationObserver = new MutationObserver(mutations => {
      for (let mutation of mutations) {
        let target = mutation.target;
        let id = target.id;
        if (!/^editBMPanel_.*(Row|Checkbox)$/.test(id))
          continue;

        let collapsed = target.getAttribute("collapsed") === "true";
        let wasCollapsed = mutation.oldValue === "true";
        if (collapsed == wasCollapsed)
          continue;

        if (collapsed) {
          this._height -= elementsHeight.get(id);
          elementsHeight.delete(id);
        } else {
          elementsHeight.set(id, target.boxObject.height);
          this._height += elementsHeight.get(id);
        }
        window.resizeTo(window.outerWidth, this._height);
      }
    });

    this._mutationObserver.observe(document,
                                   { subtree: true,
                                     attributeOldValue: true,
                                     attributeFilter: ["collapsed"] });

    // Some controls are flexible and we want to update their cached size when
    // the dialog is resized.
    window.addEventListener("resize", this);

    switch (this._action) {
      case ACTION_EDIT:
        gEditItemOverlay.initPanel({ node: this._node,
                                     hiddenRows: this._hiddenRows,
                                     focusedElement: "first" });
        acceptButtonDisabled = gEditItemOverlay.readOnly;
        break;
      case ACTION_ADD:
        this._node = await this._promiseNewItem();
        // Edit the new item
        gEditItemOverlay.initPanel({ node: this._node,
                                     hiddenRows: this._hiddenRows,
                                     postData: this._postData,
                                     focusedElement: "first" });

        // Empty location field if the uri is about:blank, this way inserting a new
        // url will be easier for the user, Accept button will be automatically
        // disabled by the input listener until the user fills the field.
        let locationField = this._element("locationField");
        if (locationField.value == "about:blank")
          locationField.value = "";

        // if this is an uri related dialog disable accept button until
        // the user fills an uri value.
        if (this._itemType == BOOKMARK_ITEM)
          acceptButtonDisabled = !this._inputIsValid();
        break;
    }

    if (!gEditItemOverlay.readOnly) {
      // Listen on uri fields to enable accept button if input is valid
      if (this._itemType == BOOKMARK_ITEM) {
        this._element("locationField")
            .addEventListener("input", this);
        if (this._isAddKeywordDialog) {
          this._element("keywordField")
              .addEventListener("input", this);
        }
      }
    }
    // Only enable the accept button once we've finished everything.
    acceptButton.disabled = acceptButtonDisabled;
  },

  // nsIDOMEventListener
  handleEvent: function BPP_handleEvent(aEvent) {
    var target = aEvent.target;
    switch (aEvent.type) {
      case "input":
        if (target.id == "editBMPanel_locationField" ||
            target.id == "editBMPanel_keywordField") {
          // Check uri fields to enable accept button if input is valid
          document.documentElement
                  .getButton("accept").disabled = !this._inputIsValid();
        }
        break;
      case "resize":
        for (let [id, oldHeight] of elementsHeight) {
          let newHeight = document.getElementById(id).boxObject.height;
          this._height += -oldHeight + newHeight;
          elementsHeight.set(id, newHeight);
        }
        break;
    }
  },

  // nsISupports
  QueryInterface: function BPP_QueryInterface(aIID) {
    if (aIID.equals(Ci.nsIDOMEventListener) ||
        aIID.equals(Ci.nsISupports))
      return this;

    throw Cr.NS_NOINTERFACE;
  },

  _element: function BPP__element(aID) {
    return document.getElementById("editBMPanel_" + aID);
  },

  onDialogUnload() {
    // gEditItemOverlay does not exist anymore here, so don't rely on it.
    this._mutationObserver.disconnect();
    delete this._mutationObserver;

    window.removeEventListener("resize", this);

    // Calling removeEventListener with arguments which do not identify any
    // currently registered EventListener on the EventTarget has no effect.
    this._element("locationField")
        .removeEventListener("input", this);
  },

  onDialogAccept() {
    // We must blur current focused element to save its changes correctly
    document.commandDispatcher.focusedElement.blur();
    // We have to uninit the panel first, otherwise late changes could force it
    // to commit more transactions.
    gEditItemOverlay.uninitPanel(true);
    window.arguments[0].performed = true;
  },

  onDialogCancel() {
    // We have to uninit the panel first, otherwise late changes could force it
    // to commit more transactions.
    gEditItemOverlay.uninitPanel(true);
    window.arguments[0].performed = false;
  },

  /**
   * This method checks to see if the input fields are in a valid state.
   *
   * @returns  true if the input is valid, false otherwise
   */
  _inputIsValid: function BPP__inputIsValid() {
    if (this._itemType == BOOKMARK_ITEM &&
        !this._containsValidURI("locationField"))
      return false;
    if (this._isAddKeywordDialog && !this._element("keywordField").value.length)
      return false;

    return true;
  },

  /**
   * Determines whether the XUL textbox with the given ID contains a
   * string that can be converted into an nsIURI.
   *
   * @param aTextboxID
   *        the ID of the textbox element whose contents we'll test
   *
   * @returns true if the textbox contains a valid URI string, false otherwise
   */
  _containsValidURI: function BPP__containsValidURI(aTextboxID) {
    try {
      var value = this._element(aTextboxID).value;
      if (value) {
        PlacesUIUtils.createFixedURI(value);
        return true;
      }
    } catch (e) { }
    return false;
  },

  /**
   * [New Item Mode] Get the insertion point details for the new item, given
   * dialog state and opening arguments.
   *
   * The container-identifier and insertion-index are returned separately in
   * the form of [containerIdentifier, insertionIndex]
   */
  async _getInsertionPointDetails() {
    return [
      this._defaultInsertionPoint.itemId,
      await this._defaultInsertionPoint.getIndex(),
      this._defaultInsertionPoint.guid,
    ];
  },

  async _promiseNewItem() {
    let [containerId, index, parentGuid] = await this._getInsertionPointDetails();
    let annotations = [];
    if (this._description) {
      annotations.push({ name: PlacesUIUtils.DESCRIPTION_ANNO,
                         value: this._description });
    }
    if (this._loadInSidebar) {
      annotations.push({ name: PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO,
                         value: true });
    }

    let itemGuid;
    let info = { parentGuid, index, title: this._title, annotations };
    if (this._itemType == BOOKMARK_ITEM) {
      info.url = this._uri;
      if (this._keyword)
        info.keyword = this._keyword;
      if (this._postData)
        info.postData = this._postData;

      if (this._charSet && !PrivateBrowsingUtils.isWindowPrivate(window))
        PlacesUtils.setCharsetForURI(this._uri, this._charSet);

      itemGuid = await PlacesTransactions.NewBookmark(info).transact();
    } else if (this._itemType == LIVEMARK_CONTAINER) {
      info.feedUrl = this._feedURI;
      if (this._siteURI)
        info.siteUrl = this._siteURI;

      itemGuid = await PlacesTransactions.NewLivemark(info).transact();
    } else if (this._itemType == BOOKMARK_FOLDER) {
      // NewFolder requires a url rather than uri.
      info.children = this._URIs.map(item => {
        return { url: item.uri, title: item.title };
      });
      itemGuid = await PlacesTransactions.NewFolder(info).transact();
    } else {
      throw new Error(`unexpected value for _itemType:  ${this._itemType}`);
    }

    this._itemGuid = itemGuid;
    this._itemId = await PlacesUtils.promiseItemId(itemGuid);
    return Object.freeze({
      itemId: this._itemId,
      bookmarkGuid: this._itemGuid,
      title: this._title,
      uri: this._uri ? this._uri.spec : "",
      type: this._itemType == BOOKMARK_ITEM ?
              Ci.nsINavHistoryResultNode.RESULT_TYPE_URI :
              Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER,
      parent: {
        itemId: containerId,
        bookmarkGuid: parentGuid,
        type: Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER
      }
    });
  }
};
PK
!<Fz���<chrome/browser/content/browser/places/bookmarkProperties.xul<?xml version="1.0"?>

<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<?xml-stylesheet href="chrome://global/skin/"?>
<?xml-stylesheet href="chrome://browser/skin/places/editBookmarkOverlay.css"?>
<?xml-stylesheet href="chrome://browser/skin/places/places.css"?>
<?xml-stylesheet href="chrome://browser/content/places/places.css"?>

<?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?>
<?xul-overlay href="chrome://browser/content/places/editBookmarkOverlay.xul"?>

<!DOCTYPE dialog [
  <!ENTITY % editBookmarkOverlayDTD SYSTEM "chrome://browser/locale/places/editBookmarkOverlay.dtd">
  %editBookmarkOverlayDTD;
]>

<dialog id="bookmarkproperties"
        buttons="accept, cancel"
        buttoniconaccept="save"
        ondialogaccept="BookmarkPropertiesPanel.onDialogAccept();"
        ondialogcancel="BookmarkPropertiesPanel.onDialogCancel();"
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        onload="BookmarkPropertiesPanel.onDialogLoad();"
        onunload="BookmarkPropertiesPanel.onDialogUnload();"
        style="min-width: 30em;"
        persist="screenX screenY width">

  <stringbundleset id="stringbundleset">
    <stringbundle id="stringBundle"
                  src="chrome://browser/locale/places/bookmarkProperties.properties"/>
  </stringbundleset>

  <script type="application/javascript"
          src="chrome://browser/content/places/editBookmarkOverlay.js"/>
  <script type="application/javascript"
          src="chrome://browser/content/places/bookmarkProperties.js"/>

<vbox id="editBookmarkPanelContent"/>

</dialog>
PK
!<Fz���=chrome/browser/content/browser/places/bookmarkProperties2.xul<?xml version="1.0"?>

<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<?xml-stylesheet href="chrome://global/skin/"?>
<?xml-stylesheet href="chrome://browser/skin/places/editBookmarkOverlay.css"?>
<?xml-stylesheet href="chrome://browser/skin/places/places.css"?>
<?xml-stylesheet href="chrome://browser/content/places/places.css"?>

<?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?>
<?xul-overlay href="chrome://browser/content/places/editBookmarkOverlay.xul"?>

<!DOCTYPE dialog [
  <!ENTITY % editBookmarkOverlayDTD SYSTEM "chrome://browser/locale/places/editBookmarkOverlay.dtd">
  %editBookmarkOverlayDTD;
]>

<dialog id="bookmarkproperties"
        buttons="accept, cancel"
        buttoniconaccept="save"
        ondialogaccept="BookmarkPropertiesPanel.onDialogAccept();"
        ondialogcancel="BookmarkPropertiesPanel.onDialogCancel();"
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        onload="BookmarkPropertiesPanel.onDialogLoad();"
        onunload="BookmarkPropertiesPanel.onDialogUnload();"
        style="min-width: 30em;"
        persist="screenX screenY width">

  <stringbundleset id="stringbundleset">
    <stringbundle id="stringBundle"
                  src="chrome://browser/locale/places/bookmarkProperties.properties"/>
  </stringbundleset>

  <script type="application/javascript"
          src="chrome://browser/content/places/editBookmarkOverlay.js"/>
  <script type="application/javascript"
          src="chrome://browser/content/places/bookmarkProperties.js"/>

<vbox id="editBookmarkPanelContent"/>

</dialog>
PK
!<:w�^�<�<;chrome/browser/content/browser/places/browserPlacesViews.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 mozilla/browser-window */

/**
 * The base view implements everything that's common to the toolbar and
 * menu views.
 */
function PlacesViewBase(aPlace, aOptions = {}) {
  if ("rootElt" in aOptions)
    this._rootElt = aOptions.rootElt;
  if ("viewElt" in aOptions)
    this._viewElt = aOptions.viewElt;
  this.options = aOptions;
  this._controller = new PlacesController(this);
  this.place = aPlace;
  this._viewElt.controllers.appendController(this._controller);
}

PlacesViewBase.prototype = {
  // The xul element that holds the entire view.
  _viewElt: null,
  get viewElt() {
    return this._viewElt;
  },

  get associatedElement() {
    return this._viewElt;
  },

  get controllers() {
    return this._viewElt.controllers;
  },

  // The xul element that represents the root container.
  _rootElt: null,

  // Set to true for views that are represented by native widgets (i.e.
  // the native mac menu).
  _nativeView: false,

  QueryInterface: XPCOMUtils.generateQI(
    [Ci.nsINavHistoryResultObserver,
     Ci.nsISupportsWeakReference]),

  _place: "",
  get place() {
    return this._place;
  },
  set place(val) {
    this._place = val;

    let history = PlacesUtils.history;
    let queries = { }, options = { };
    history.queryStringToQueries(val, queries, { }, options);
    if (!queries.value.length)
      queries.value = [history.getNewQuery()];

    let result = history.executeQueries(queries.value, queries.value.length,
                                        options.value);
    result.addObserver(this);
    return val;
  },

  _result: null,
  get result() {
    return this._result;
  },
  set result(val) {
    if (this._result == val)
      return val;

    if (this._result) {
      this._result.removeObserver(this);
      this._resultNode.containerOpen = false;
    }

    if (this._rootElt.localName == "menupopup")
      this._rootElt._built = false;

    this._result = val;
    if (val) {
      this._resultNode = val.root;
      this._rootElt._placesNode = this._resultNode;
      this._domNodes = new Map();
      this._domNodes.set(this._resultNode, this._rootElt);

      // This calls _rebuild through invalidateContainer.
      this._resultNode.containerOpen = true;
    } else {
      this._resultNode = null;
      delete this._domNodes;
    }

    return val;
  },

  _options: null,
  get options() {
    return this._options;
  },
  set options(val) {
    if (!val)
      val = {};

    if (!("extraClasses" in val))
      val.extraClasses = {};
    this._options = val;

    return val;
  },

  /**
   * Gets the DOM node used for the given places node.
   *
   * @param aPlacesNode
   *        a places result node.
   * @param aAllowMissing
   *        whether the node may be missing
   * @throws if there is no DOM node set for aPlacesNode.
   */
  _getDOMNodeForPlacesNode:
  function PVB__getDOMNodeForPlacesNode(aPlacesNode, aAllowMissing = false) {
    let node = this._domNodes.get(aPlacesNode, null);
    if (!node && !aAllowMissing) {
      throw new Error("No DOM node set for aPlacesNode.\nnode.type: " +
                      aPlacesNode.type + ". node.parent: " + aPlacesNode);
    }
    return node;
  },

  get controller() {
    return this._controller;
  },

  get selType() {
    return "single";
  },
  selectItems() { },
  selectAll() { },

  get selectedNode() {
    if (this._contextMenuShown) {
      let anchor = this._contextMenuShown.triggerNode;
      if (!anchor)
        return null;

      if (anchor._placesNode)
        return this._rootElt == anchor ? null : anchor._placesNode;

      anchor = anchor.parentNode;
      return this._rootElt == anchor ? null : (anchor._placesNode || null);
    }
    return null;
  },

  get hasSelection() {
    return this.selectedNode != null;
  },

  get selectedNodes() {
    let selectedNode = this.selectedNode;
    return selectedNode ? [selectedNode] : [];
  },

  get removableSelectionRanges() {
    // On static content the current selectedNode would be the selection's
    // parent node. We don't want to allow removing a node when the
    // selection is not explicit.
    if (document.popupNode &&
        (document.popupNode == "menupopup" || !document.popupNode._placesNode))
      return [];

    return [this.selectedNodes];
  },

  get draggableSelection() {
    return [this._draggedElt];
  },

  get insertionPoint() {
    // There is no insertion point for history queries, so bail out now and
    // save a lot of work when updating commands.
    let resultNode = this._resultNode;
    if (PlacesUtils.nodeIsQuery(resultNode) &&
        PlacesUtils.asQuery(resultNode).queryOptions.queryType ==
          Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY)
      return null;

    // By default, the insertion point is at the top level, at the end.
    let index = PlacesUtils.bookmarks.DEFAULT_INDEX;
    let container = this._resultNode;
    let orientation = Ci.nsITreeView.DROP_BEFORE;
    let tagName = null;

    let selectedNode = this.selectedNode;
    if (selectedNode) {
      let popup = document.popupNode;
      if (!popup._placesNode || popup._placesNode == this._resultNode ||
          popup._placesNode.itemId == -1 || !selectedNode.parent) {
        // If a static menuitem is selected, or if the root node is selected,
        // the insertion point is inside the folder, at the end.
        container = selectedNode;
        orientation = Ci.nsITreeView.DROP_ON;
      } else {
        // In all other cases the insertion point is before that node.
        container = selectedNode.parent;
        index = container.getChildIndex(selectedNode);
        if (PlacesUtils.nodeIsTagQuery(container)) {
          tagName = container.title;
          // TODO (Bug 1160193): properly support dropping on a tag root.
          if (!tagName)
            return null;
        }
      }
    }

    if (this.controller.disallowInsertion(container))
      return null;

    return new PlacesInsertionPoint({
      parentId: PlacesUtils.getConcreteItemId(container),
      parentGuid: PlacesUtils.getConcreteItemGuid(container),
      index, orientation, tagName
    });
  },

  buildContextMenu: function PVB_buildContextMenu(aPopup) {
    this._contextMenuShown = aPopup;
    window.updateCommands("places");
    return this.controller.buildContextMenu(aPopup);
  },

  destroyContextMenu: function PVB_destroyContextMenu(aPopup) {
    this._contextMenuShown = null;
  },

  clearAllContents(aPopup) {
    let kid = aPopup.firstChild;
    while (kid) {
      let next = kid.nextSibling;
      if (!kid.classList.contains("panel-header")) {
        kid.remove();
      }
      kid = next;
    }
    aPopup._emptyMenuitem = aPopup._startMarker = aPopup._endMarker = null;
  },

  _cleanPopup: function PVB_cleanPopup(aPopup, aDelay) {
    // Ensure markers are here when `invalidateContainer` is called before the
    // popup is shown, which may the case for panelviews, for example.
    this._ensureMarkers(aPopup);
    // Remove Places nodes from the popup.
    let child = aPopup._startMarker;
    while (child.nextSibling != aPopup._endMarker) {
      let sibling = child.nextSibling;
      if (sibling._placesNode && !aDelay) {
        aPopup.removeChild(sibling);
      } else if (sibling._placesNode && aDelay) {
        // HACK (bug 733419): the popups originating from the OS X native
        // menubar don't live-update while open, thus we don't clean it
        // until the next popupshowing, to avoid zombie menuitems.
        if (!aPopup._delayedRemovals)
          aPopup._delayedRemovals = [];
        aPopup._delayedRemovals.push(sibling);
        child = child.nextSibling;
      } else {
        child = child.nextSibling;
      }
    }
  },

  _rebuildPopup: function PVB__rebuildPopup(aPopup) {
    let resultNode = aPopup._placesNode;
    if (!resultNode.containerOpen)
      return;

    if (this.controller.hasCachedLivemarkInfo(resultNode)) {
      this._setEmptyPopupStatus(aPopup, false);
      aPopup._built = true;
      this._populateLivemarkPopup(aPopup);
      return;
    }

    this._cleanPopup(aPopup);

    let cc = resultNode.childCount;
    if (cc > 0) {
      this._setEmptyPopupStatus(aPopup, false);
      let fragment = document.createDocumentFragment();
      for (let i = 0; i < cc; ++i) {
        let child = resultNode.getChild(i);
        this._insertNewItemToPopup(child, fragment);
      }
      aPopup.insertBefore(fragment, aPopup._endMarker);
    } else {
      this._setEmptyPopupStatus(aPopup, true);
    }
    aPopup._built = true;
  },

  _removeChild: function PVB__removeChild(aChild) {
    // If document.popupNode pointed to this child, null it out,
    // otherwise controller's command-updating may rely on the removed
    // item still being "selected".
    if (document.popupNode == aChild)
      document.popupNode = null;

    aChild.remove();
  },

  _setEmptyPopupStatus:
  function PVB__setEmptyPopupStatus(aPopup, aEmpty) {
    if (!aPopup._emptyMenuitem) {
      let label = PlacesUIUtils.getString("bookmarksMenuEmptyFolder");
      aPopup._emptyMenuitem = document.createElement("menuitem");
      aPopup._emptyMenuitem.setAttribute("label", label);
      aPopup._emptyMenuitem.setAttribute("disabled", true);
      aPopup._emptyMenuitem.className = "bookmark-item";
      if (typeof this.options.extraClasses.entry == "string")
        aPopup._emptyMenuitem.classList.add(this.options.extraClasses.entry);
    }

    if (aEmpty) {
      aPopup.setAttribute("emptyplacesresult", "true");
      // Don't add the menuitem if there is static content.
      if (!aPopup._startMarker.previousSibling &&
          !aPopup._endMarker.nextSibling)
        aPopup.insertBefore(aPopup._emptyMenuitem, aPopup._endMarker);
    } else {
      aPopup.removeAttribute("emptyplacesresult");
      try {
        aPopup.removeChild(aPopup._emptyMenuitem);
      } catch (ex) {}
    }
  },

  _createDOMNodeForPlacesNode:
  function PVB__createDOMNodeForPlacesNode(aPlacesNode) {
    this._domNodes.delete(aPlacesNode);

    let element;
    let type = aPlacesNode.type;
    if (type == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR) {
      element = document.createElement("menuseparator");
      element.setAttribute("class", "small-separator");
    } else {
      let itemId = aPlacesNode.itemId;
      if (type == Ci.nsINavHistoryResultNode.RESULT_TYPE_URI) {
        element = document.createElement("menuitem");
        element.className = "menuitem-iconic bookmark-item menuitem-with-favicon";
        element.setAttribute("scheme",
                             PlacesUIUtils.guessUrlSchemeForUI(aPlacesNode.uri));
      } else if (PlacesUtils.containerTypes.includes(type)) {
        element = document.createElement("menu");
        element.setAttribute("container", "true");

        if (aPlacesNode.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_QUERY) {
          element.setAttribute("query", "true");
          if (PlacesUtils.nodeIsTagQuery(aPlacesNode))
            element.setAttribute("tagContainer", "true");
          else if (PlacesUtils.nodeIsDay(aPlacesNode))
            element.setAttribute("dayContainer", "true");
          else if (PlacesUtils.nodeIsHost(aPlacesNode))
            element.setAttribute("hostContainer", "true");
        } else if (itemId != -1) {
          PlacesUtils.livemarks.getLivemark({ id: itemId })
            .then(aLivemark => {
              element.setAttribute("livemark", "true");
              if (AppConstants.platform === "macosx") {
                // OS X native menubar doesn't track list-style-images since
                // it doesn't have a frame (bug 733415).  Thus enforce updating.
                element.setAttribute("image", "");
                element.removeAttribute("image");
              }
              this.controller.cacheLivemarkInfo(aPlacesNode, aLivemark);
            }, () => undefined);
        }

        let popup = document.createElement("menupopup");
        popup._placesNode = PlacesUtils.asContainer(aPlacesNode);

        if (!this._nativeView) {
          popup.setAttribute("placespopup", "true");
        }

        element.appendChild(popup);
        element.className = "menu-iconic bookmark-item";
        if (typeof this.options.extraClasses.entry == "string") {
          element.classList.add(this.options.extraClasses.entry);
        }

        this._domNodes.set(aPlacesNode, popup);
      } else
        throw "Unexpected node";

      element.setAttribute("label", PlacesUIUtils.getBestTitle(aPlacesNode));

      let icon = aPlacesNode.icon;
      if (icon)
        element.setAttribute("image", icon);
    }

    element._placesNode = aPlacesNode;
    if (!this._domNodes.has(aPlacesNode))
      this._domNodes.set(aPlacesNode, element);

    return element;
  },

  _insertNewItemToPopup:
  function PVB__insertNewItemToPopup(aNewChild, aInsertionNode, aBefore = null) {
    let element = this._createDOMNodeForPlacesNode(aNewChild);

    if (element.localName == "menuitem" || element.localName == "menu") {
      if (typeof this.options.extraClasses.entry == "string")
        element.classList.add(this.options.extraClasses.entry);
    }

    aInsertionNode.insertBefore(element, aBefore);
    return element;
  },

  _setLivemarkSiteURIMenuItem:
  function PVB__setLivemarkSiteURIMenuItem(aPopup) {
    let livemarkInfo = this.controller.getCachedLivemarkInfo(aPopup._placesNode);
    let siteUrl = livemarkInfo && livemarkInfo.siteURI ?
                  livemarkInfo.siteURI.spec : null;
    if (!siteUrl && aPopup._siteURIMenuitem) {
      aPopup.removeChild(aPopup._siteURIMenuitem);
      aPopup._siteURIMenuitem = null;
      aPopup.removeChild(aPopup._siteURIMenuseparator);
      aPopup._siteURIMenuseparator = null;
    } else if (siteUrl && !aPopup._siteURIMenuitem) {
      // Add "Open (Feed Name)" menuitem.
      aPopup._siteURIMenuitem = document.createElement("menuitem");
      aPopup._siteURIMenuitem.className = "openlivemarksite-menuitem";
      if (typeof this.options.extraClasses.entry == "string") {
        aPopup._siteURIMenuitem.classList.add(this.options.extraClasses.entry);
      }
      aPopup._siteURIMenuitem.setAttribute("targetURI", siteUrl);
      aPopup._siteURIMenuitem.setAttribute("oncommand",
        "openUILink(this.getAttribute('targetURI'), event);");

      // If a user middle-clicks this item we serve the oncommand event.
      // We are using checkForMiddleClick because of Bug 246720.
      // Note: stopPropagation is needed to avoid serving middle-click
      // with BT_onClick that would open all items in tabs.
      aPopup._siteURIMenuitem.setAttribute("onclick",
        "checkForMiddleClick(this, event); event.stopPropagation();");
      let label =
        PlacesUIUtils.getFormattedString("menuOpenLivemarkOrigin.label",
                                         [aPopup.parentNode.getAttribute("label")]);
      aPopup._siteURIMenuitem.setAttribute("label", label);
      aPopup.insertBefore(aPopup._siteURIMenuitem, aPopup._startMarker);

      aPopup._siteURIMenuseparator = document.createElement("menuseparator");
      aPopup.insertBefore(aPopup._siteURIMenuseparator, aPopup._startMarker);
    }
  },

  /**
   * Add, update or remove the livemark status menuitem.
   * @param aPopup
   *        The livemark container popup
   * @param aStatus
   *        The livemark status
   */
  _setLivemarkStatusMenuItem:
  function PVB_setLivemarkStatusMenuItem(aPopup, aStatus) {
    let statusMenuitem = aPopup._statusMenuitem;
    if (!statusMenuitem) {
      // Create the status menuitem and cache it in the popup object.
      statusMenuitem = document.createElement("menuitem");
      statusMenuitem.className = "livemarkstatus-menuitem";
      if (typeof this.options.extraClasses.entry == "string") {
        statusMenuitem.classList.add(this.options.extraClasses.entry);
      }
      statusMenuitem.setAttribute("disabled", true);
      aPopup._statusMenuitem = statusMenuitem;
    }

    if (aStatus == Ci.mozILivemark.STATUS_LOADING ||
        aStatus == Ci.mozILivemark.STATUS_FAILED) {
      // Status has changed, update the cached status menuitem.
      let stringId = aStatus == Ci.mozILivemark.STATUS_LOADING ?
                       "bookmarksLivemarkLoading" : "bookmarksLivemarkFailed";
      statusMenuitem.setAttribute("label", PlacesUIUtils.getString(stringId));
      if (aPopup._startMarker.nextSibling != statusMenuitem)
        aPopup.insertBefore(statusMenuitem, aPopup._startMarker.nextSibling);
    } else if (aPopup._statusMenuitem.parentNode == aPopup) {
      // The livemark has finished loading.
      aPopup.removeChild(aPopup._statusMenuitem);
    }
  },

  toggleCutNode: function PVB_toggleCutNode(aPlacesNode, aValue) {
    let elt = this._getDOMNodeForPlacesNode(aPlacesNode);

    // We may get the popup for menus, but we need the menu itself.
    if (elt.localName == "menupopup")
      elt = elt.parentNode;
    if (aValue)
      elt.setAttribute("cutting", "true");
    else
      elt.removeAttribute("cutting");
  },

  nodeURIChanged: function PVB_nodeURIChanged(aPlacesNode, aURIString) {
    let elt = this._getDOMNodeForPlacesNode(aPlacesNode);

    // Here we need the <menu>.
    if (elt.localName == "menupopup")
      elt = elt.parentNode;

    elt.setAttribute("scheme", PlacesUIUtils.guessUrlSchemeForUI(aPlacesNode.uri));
  },

  nodeIconChanged: function PVB_nodeIconChanged(aPlacesNode) {
    let elt = this._getDOMNodeForPlacesNode(aPlacesNode);

    // There's no UI representation for the root node, thus there's nothing to
    // be done when the icon changes.
    if (elt == this._rootElt)
      return;

    // Here we need the <menu>.
    if (elt.localName == "menupopup") {
      elt = elt.parentNode;
    }
    // We must remove and reset the attribute to force an update.
    elt.removeAttribute("image");
    elt.setAttribute("image", aPlacesNode.icon);
  },

  nodeAnnotationChanged:
  function PVB_nodeAnnotationChanged(aPlacesNode, aAnno) {
    let elt = this._getDOMNodeForPlacesNode(aPlacesNode);

    // All livemarks have a feedURI, so use it as our indicator of a livemark
    // being modified.
    if (aAnno == PlacesUtils.LMANNO_FEEDURI) {
      let menu = elt.parentNode;
      if (!menu.hasAttribute("livemark")) {
        menu.setAttribute("livemark", "true");
        if (AppConstants.platform === "macosx") {
          // OS X native menubar doesn't track list-style-images since
          // it doesn't have a frame (bug 733415).  Thus enforce updating.
          menu.setAttribute("image", "");
          menu.removeAttribute("image");
        }
      }

      PlacesUtils.livemarks.getLivemark({ id: aPlacesNode.itemId })
        .then(aLivemark => {
          // Controller will use this to build the meta data for the node.
          this.controller.cacheLivemarkInfo(aPlacesNode, aLivemark);
          this.invalidateContainer(aPlacesNode);
        }, () => undefined);
    }
  },

  nodeTitleChanged:
  function PVB_nodeTitleChanged(aPlacesNode, aNewTitle) {
    let elt = this._getDOMNodeForPlacesNode(aPlacesNode);

    // There's no UI representation for the root node, thus there's
    // nothing to be done when the title changes.
    if (elt == this._rootElt)
      return;

    // Here we need the <menu>.
    if (elt.localName == "menupopup")
      elt = elt.parentNode;

    if (!aNewTitle && elt.localName != "toolbarbutton") {
      // Many users consider toolbars as shortcuts containers, so explicitly
      // allow empty labels on toolbarbuttons.  For any other element try to be
      // smarter, guessing a title from the uri.
      elt.setAttribute("label", PlacesUIUtils.getBestTitle(aPlacesNode));
    } else {
      elt.setAttribute("label", aNewTitle);
    }
  },

  nodeRemoved:
  function PVB_nodeRemoved(aParentPlacesNode, aPlacesNode, aIndex) {
    let parentElt = this._getDOMNodeForPlacesNode(aParentPlacesNode);
    let elt = this._getDOMNodeForPlacesNode(aPlacesNode);

    // Here we need the <menu>.
    if (elt.localName == "menupopup")
      elt = elt.parentNode;

    if (parentElt._built) {
      parentElt.removeChild(elt);

      // Figure out if we need to show the "<Empty>" menu-item.
      // TODO Bug 517701: This doesn't seem to handle the case of an empty
      // root.
      if (parentElt._startMarker.nextSibling == parentElt._endMarker)
        this._setEmptyPopupStatus(parentElt, true);
    }
  },

  nodeHistoryDetailsChanged:
  function PVB_nodeHistoryDetailsChanged(aPlacesNode, aTime, aCount) {
    if (aPlacesNode.parent &&
        this.controller.hasCachedLivemarkInfo(aPlacesNode.parent)) {
      // Find the node in the parent.
      let popup = this._getDOMNodeForPlacesNode(aPlacesNode.parent);
      for (let child = popup._startMarker.nextSibling;
           child != popup._endMarker;
           child = child.nextSibling) {
        if (child._placesNode && child._placesNode.uri == aPlacesNode.uri) {
          if (aPlacesNode.accessCount)
            child.setAttribute("visited", "true");
          else
            child.removeAttribute("visited");
          break;
        }
      }
    }
  },

  nodeTagsChanged() { },
  nodeDateAddedChanged() { },
  nodeLastModifiedChanged() { },
  nodeKeywordChanged() { },
  sortingChanged() { },
  batching() { },

  nodeInserted:
  function PVB_nodeInserted(aParentPlacesNode, aPlacesNode, aIndex) {
    let parentElt = this._getDOMNodeForPlacesNode(aParentPlacesNode);
    if (!parentElt._built)
      return;

    let index = Array.prototype.indexOf.call(parentElt.childNodes, parentElt._startMarker) +
                aIndex + 1;
    this._insertNewItemToPopup(aPlacesNode, parentElt,
                               parentElt.childNodes[index] || parentElt._endMarker);
    this._setEmptyPopupStatus(parentElt, false);
  },

  nodeMoved:
  function PBV_nodeMoved(aPlacesNode,
                         aOldParentPlacesNode, aOldIndex,
                         aNewParentPlacesNode, aNewIndex) {
    // Note: the current implementation of moveItem does not actually
    // use this notification when the item in question is moved from one
    // folder to another.  Instead, it calls nodeRemoved and nodeInserted
    // for the two folders.  Thus, we can assume old-parent == new-parent.
    let elt = this._getDOMNodeForPlacesNode(aPlacesNode);

    // Here we need the <menu>.
    if (elt.localName == "menupopup")
      elt = elt.parentNode;

    // If our root node is a folder, it might be moved. There's nothing
    // we need to do in that case.
    if (elt == this._rootElt)
      return;

    let parentElt = this._getDOMNodeForPlacesNode(aNewParentPlacesNode);
    if (parentElt._built) {
      // Move the node.
      parentElt.removeChild(elt);
      let index = Array.prototype.indexOf.call(parentElt.childNodes, parentElt._startMarker) +
                  aNewIndex + 1;
      parentElt.insertBefore(elt, parentElt.childNodes[index]);
    }
  },

  containerStateChanged:
  function PVB_containerStateChanged(aPlacesNode, aOldState, aNewState) {
    if (aNewState == Ci.nsINavHistoryContainerResultNode.STATE_OPENED ||
        aNewState == Ci.nsINavHistoryContainerResultNode.STATE_CLOSED) {
      this.invalidateContainer(aPlacesNode);

      if (PlacesUtils.nodeIsFolder(aPlacesNode)) {
        let queryOptions = PlacesUtils.asQuery(this._result.root).queryOptions;
        if (queryOptions.excludeItems) {
          return;
        }

        PlacesUtils.livemarks.getLivemark({ id: aPlacesNode.itemId })
          .then(aLivemark => {
            if (!this.controller) {
              // We might have been destroyed in the interim...
              return;
            }
            let shouldInvalidate =
              !this.controller.hasCachedLivemarkInfo(aPlacesNode);
            this.controller.cacheLivemarkInfo(aPlacesNode, aLivemark);
            if (aNewState == Ci.nsINavHistoryContainerResultNode.STATE_OPENED) {
              aLivemark.registerForUpdates(aPlacesNode, this);
              // Prioritize the current livemark.
              aLivemark.reload();
              PlacesUtils.livemarks.reloadLivemarks();
              if (shouldInvalidate)
                this.invalidateContainer(aPlacesNode);
            } else {
              aLivemark.unregisterForUpdates(aPlacesNode);
            }
          }, () => undefined);
      }
    }
  },

  _populateLivemarkPopup: function PVB__populateLivemarkPopup(aPopup) {
    this._setLivemarkSiteURIMenuItem(aPopup);
    // Show the loading status only if there are no entries yet.
    if (aPopup._startMarker.nextSibling == aPopup._endMarker)
      this._setLivemarkStatusMenuItem(aPopup, Ci.mozILivemark.STATUS_LOADING);

    PlacesUtils.livemarks.getLivemark({ id: aPopup._placesNode.itemId })
      .then(aLivemark => {
        let placesNode = aPopup._placesNode;
        if (!placesNode.containerOpen)
          return;

        if (aLivemark.status != Ci.mozILivemark.STATUS_LOADING)
          this._setLivemarkStatusMenuItem(aPopup, aLivemark.status);
        this._cleanPopup(aPopup,
          this._nativeView && aPopup.parentNode.hasAttribute("open"));

        let children = aLivemark.getNodesForContainer(placesNode);
        for (let i = 0; i < children.length; i++) {
          let child = children[i];
          this.nodeInserted(placesNode, child, i);
          if (child.accessCount)
            this._getDOMNodeForPlacesNode(child).setAttribute("visited", true);
          else
            this._getDOMNodeForPlacesNode(child).removeAttribute("visited");
        }
      }, Cu.reportError);
  },

  /**
   * Checks whether the popup associated with the provided element is open.
   * This method may be overridden by classes that extend this base class.
   *
   * @param  {nsIDOMElement} elt
   * @return {Boolean}
   */
  _isPopupOpen(elt) {
    return !!elt.parentNode.open;
  },

  invalidateContainer: function PVB_invalidateContainer(aPlacesNode) {
    let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
    elt._built = false;

    // If the menupopup is open we should live-update it.
    if (this._isPopupOpen(elt))
      this._rebuildPopup(elt);
  },

  uninit: function PVB_uninit() {
    if (this._result) {
      this._result.removeObserver(this);
      this._resultNode.containerOpen = false;
      this._resultNode = null;
      this._result = null;
    }

    if (this._controller) {
      this._controller.terminate();
      // Removing the controller will fail if it is already no longer there.
      // This can happen if the view element was removed/reinserted without
      // our knowledge. There is no way to check for that having happened
      // without the possibility of an exception. :-(
      try {
        this._viewElt.controllers.removeController(this._controller);
      } catch (ex) {
      } finally {
        this._controller = null;
      }
    }

    delete this._viewElt._placesView;
  },

  get isRTL() {
    if ("_isRTL" in this)
      return this._isRTL;

    return this._isRTL = document.defaultView
                                 .getComputedStyle(this.viewElt)
                                 .direction == "rtl";
  },

  get ownerWindow() {
    return window;
  },

  /**
   * Adds an "Open All in Tabs" menuitem to the bottom of the popup.
   * @param aPopup
   *        a Places popup.
   */
  _mayAddCommandsItems: function PVB__mayAddCommandsItems(aPopup) {
    // The command items are never added to the root popup.
    if (aPopup == this._rootElt)
      return;

    let hasMultipleURIs = false;

    // Check if the popup contains at least 2 menuitems with places nodes.
    // We don't currently support opening multiple uri nodes when they are not
    // populated by the result.
    if (aPopup._placesNode.childCount > 0) {
      let currentChild = aPopup.firstChild;
      let numURINodes = 0;
      while (currentChild) {
        if (currentChild.localName == "menuitem" && currentChild._placesNode) {
          if (++numURINodes == 2)
            break;
        }
        currentChild = currentChild.nextSibling;
      }
      hasMultipleURIs = numURINodes > 1;
    }

    let isLiveMark = false;
    if (this.controller.hasCachedLivemarkInfo(aPopup._placesNode)) {
      hasMultipleURIs = true;
      isLiveMark = true;
    }

    if (!hasMultipleURIs) {
      aPopup.setAttribute("singleitempopup", "true");
    } else {
      aPopup.removeAttribute("singleitempopup");
    }

    if (!hasMultipleURIs) {
      // We don't have to show any option.
      if (aPopup._endOptOpenAllInTabs) {
        aPopup.removeChild(aPopup._endOptOpenAllInTabs);
        aPopup._endOptOpenAllInTabs = null;

        aPopup.removeChild(aPopup._endOptSeparator);
        aPopup._endOptSeparator = null;
      }
    } else if (!aPopup._endOptOpenAllInTabs) {
      // Create a separator before options.
      aPopup._endOptSeparator = document.createElement("menuseparator");
      aPopup._endOptSeparator.className = "bookmarks-actions-menuseparator";
      aPopup.appendChild(aPopup._endOptSeparator);

      // Add the "Open All in Tabs" menuitem.
      aPopup._endOptOpenAllInTabs = document.createElement("menuitem");
      aPopup._endOptOpenAllInTabs.className = "openintabs-menuitem";

      if (typeof this.options.extraClasses.entry == "string")
        aPopup._endOptOpenAllInTabs.classList.add(this.options.extraClasses.entry);
      if (typeof this.options.extraClasses.footer == "string")
        aPopup._endOptOpenAllInTabs.classList.add(this.options.extraClasses.footer);

      if (isLiveMark) {
        aPopup._endOptOpenAllInTabs.setAttribute("oncommand",
          "PlacesUIUtils.openLiveMarkNodesInTabs(this.parentNode._placesNode, event, " +
                                                 "PlacesUIUtils.getViewForNode(this));");
      } else {
        aPopup._endOptOpenAllInTabs.setAttribute("oncommand",
          "PlacesUIUtils.openContainerNodeInTabs(this.parentNode._placesNode, event, " +
                                                 "PlacesUIUtils.getViewForNode(this));");
      }
      aPopup._endOptOpenAllInTabs.setAttribute("onclick",
        "checkForMiddleClick(this, event); event.stopPropagation();");
      aPopup._endOptOpenAllInTabs.setAttribute("label",
        gNavigatorBundle.getString("menuOpenAllInTabs.label"));
      aPopup.appendChild(aPopup._endOptOpenAllInTabs);
    }
  },

  _ensureMarkers: function PVB__ensureMarkers(aPopup) {
    if (aPopup._startMarker)
      return;

    // _startMarker is an hidden menuseparator that lives before places nodes.
    aPopup._startMarker = document.createElement("menuseparator");
    aPopup._startMarker.hidden = true;
    aPopup.insertBefore(aPopup._startMarker, aPopup.firstChild);

    // _endMarker is a DOM node that lives after places nodes, specified with
    // the 'insertionPoint' option or will be a hidden menuseparator.
    let node = this.options.insertionPoint ?
               aPopup.querySelector(this.options.insertionPoint) : null;
    if (node) {
      aPopup._endMarker = node;
    } else {
      aPopup._endMarker = document.createElement("menuseparator");
      aPopup._endMarker.hidden = true;
    }
    aPopup.appendChild(aPopup._endMarker);

    // Move the markers to the right position.
    let firstNonStaticNodeFound = false;
    for (let i = 0; i < aPopup.childNodes.length; i++) {
      let child = aPopup.childNodes[i];
      // Menus that have static content at the end, but are initially empty,
      // use a special "builder" attribute to figure out where to start
      // inserting places nodes.
      if (child.getAttribute("builder") == "end") {
        aPopup.insertBefore(aPopup._endMarker, child);
        break;
      }

      if (child._placesNode && !firstNonStaticNodeFound) {
        firstNonStaticNodeFound = true;
        aPopup.insertBefore(aPopup._startMarker, child);
      }
    }
    if (!firstNonStaticNodeFound) {
      aPopup.insertBefore(aPopup._startMarker, aPopup._endMarker);
    }
  },

  _onPopupShowing: function PVB__onPopupShowing(aEvent) {
    // Avoid handling popupshowing of inner views.
    let popup = aEvent.originalTarget;

    this._ensureMarkers(popup);

    // Remove any delayed element, see _cleanPopup for details.
    if ("_delayedRemovals" in popup) {
      while (popup._delayedRemovals.length > 0) {
        popup.removeChild(popup._delayedRemovals.shift());
      }
    }

    if (popup._placesNode && PlacesUIUtils.getViewForNode(popup) == this) {
      if (this.controller.hasCachedLivemarkInfo(popup._placesNode)) {
        Services.telemetry.scalarAdd("browser.feeds.livebookmark_opened", 1);
      }
      if (!popup._placesNode.containerOpen)
        popup._placesNode.containerOpen = true;
      if (!popup._built)
        this._rebuildPopup(popup);

      this._mayAddCommandsItems(popup);
    }
  },

  _addEventListeners:
  function PVB__addEventListeners(aObject, aEventNames, aCapturing = false) {
    for (let i = 0; i < aEventNames.length; i++) {
      aObject.addEventListener(aEventNames[i], this, aCapturing);
    }
  },

  _removeEventListeners:
  function PVB__removeEventListeners(aObject, aEventNames, aCapturing = false) {
    for (let i = 0; i < aEventNames.length; i++) {
      aObject.removeEventListener(aEventNames[i], this, aCapturing);
    }
  },
};

function PlacesToolbar(aPlace) {
  let startTime = Date.now();
  // Add some smart getters for our elements.
  let thisView = this;
  [
    ["_viewElt",              "PlacesToolbar"],
    ["_rootElt",              "PlacesToolbarItems"],
    ["_dropIndicator",        "PlacesToolbarDropIndicator"],
    ["_chevron",              "PlacesChevron"],
    ["_chevronPopup",         "PlacesChevronPopup"]
  ].forEach(function(elementGlobal) {
    let [name, id] = elementGlobal;
    thisView.__defineGetter__(name, function() {
      let element = document.getElementById(id);
      if (!element)
        return null;

      delete thisView[name];
      return thisView[name] = element;
    });
  });

  this._viewElt._placesView = this;

  this._addEventListeners(this._viewElt, this._cbEvents, false);
  this._addEventListeners(this._rootElt, ["popupshowing", "popuphidden"], true);
  this._addEventListeners(this._rootElt, ["overflow", "underflow"], true);
  this._addEventListeners(window, ["resize", "unload"], false);

  // If personal-bookmarks has been dragged to the tabs toolbar,
  // we have to track addition and removals of tabs, to properly
  // recalculate the available space for bookmarks.
  // TODO (bug 734730): Use a performant mutation listener when available.
  if (this._viewElt.parentNode.parentNode == document.getElementById("TabsToolbar")) {
    this._addEventListeners(gBrowser.tabContainer, ["TabOpen", "TabClose"], false);
  }

  PlacesViewBase.call(this, aPlace);

  Services.telemetry.getHistogramById("FX_BOOKMARKS_TOOLBAR_INIT_MS")
                    .add(Date.now() - startTime);
}

PlacesToolbar.prototype = {
  __proto__: PlacesViewBase.prototype,

  _cbEvents: ["dragstart", "dragover", "dragexit", "dragend", "drop",
              "mousemove", "mouseover", "mouseout"],

  QueryInterface: function PT_QueryInterface(aIID) {
    if (aIID.equals(Ci.nsIDOMEventListener) ||
        aIID.equals(Ci.nsITimerCallback))
      return this;

    return PlacesViewBase.prototype.QueryInterface.apply(this, arguments);
  },

  uninit: function PT_uninit() {
    this._removeEventListeners(this._viewElt, this._cbEvents, false);
    this._removeEventListeners(this._rootElt, ["popupshowing", "popuphidden"],
                               true);
    this._removeEventListeners(this._rootElt, ["overflow", "underflow"], true);
    this._removeEventListeners(window, ["resize", "unload"], false);
    this._removeEventListeners(gBrowser.tabContainer, ["TabOpen", "TabClose"], false);

    if (this._chevron._placesView) {
      this._chevron._placesView.uninit();
    }

    PlacesViewBase.prototype.uninit.apply(this, arguments);
  },

  _openedMenuButton: null,
  _allowPopupShowing: true,

  _rebuild: function PT__rebuild() {
    // Clear out references to existing nodes, since they will be removed
    // and re-added.
    if (this._overFolder.elt)
      this._clearOverFolder();

    this._openedMenuButton = null;
    while (this._rootElt.hasChildNodes()) {
      this._rootElt.firstChild.remove();
    }

    let fragment = document.createDocumentFragment();
    let cc = this._resultNode.childCount;
    if (cc > 0) {
      // There could be a lot of nodes, but we only want to build the ones that
      // are likely to be shown, not all of them. Then we'll lazily create the
      // missing nodes when needed.
      // We don't want to cause reflows at every node insertion to calculate
      // a precise size, thus we guess a size from the first node.
      let button = this._insertNewItem(this._resultNode.getChild(0),
                                       this._rootElt);
      requestAnimationFrame(() => {
        // May have been destroyed in the meanwhile.
        if (!this._resultNode || !this._rootElt)
          return;
        // We assume a button with just the icon will be more or less a square,
        // then compensate the measurement error by considering a larger screen
        // width. Moreover the window could be bigger than the screen.
        let size = button.clientHeight;
        let limit = Math.min(cc, parseInt((window.screen.width * 1.5) / size));
        for (let i = 1; i < limit; ++i) {
          this._insertNewItem(this._resultNode.getChild(i), fragment);
        }
        this._rootElt.appendChild(fragment);

        this.updateNodesVisibility();
      });
    }

    if (this._chevronPopup.hasAttribute("type")) {
      // Chevron has already been initialized, but since we are forcing
      // a rebuild of the toolbar, it has to be rebuilt.
      // Otherwise, it will be initialized when the toolbar overflows.
      this._chevronPopup.place = this.place;
    }
  },

  _insertNewItem:
  function PT__insertNewItem(aChild, aInsertionNode, aBefore = null) {
    this._domNodes.delete(aChild);

    let type = aChild.type;
    let button;
    if (type == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR) {
      button = document.createElement("toolbarseparator");
    } else {
      button = document.createElement("toolbarbutton");
      button.className = "bookmark-item";
      button.setAttribute("label", aChild.title || "");

      if (PlacesUtils.containerTypes.includes(type)) {
        button.setAttribute("type", "menu");
        button.setAttribute("container", "true");

        if (PlacesUtils.nodeIsQuery(aChild)) {
          button.setAttribute("query", "true");
          if (PlacesUtils.nodeIsTagQuery(aChild))
            button.setAttribute("tagContainer", "true");
        } else if (PlacesUtils.nodeIsFolder(aChild)) {
          PlacesUtils.livemarks.getLivemark({ id: aChild.itemId })
            .then(aLivemark => {
              button.setAttribute("livemark", "true");
              this.controller.cacheLivemarkInfo(aChild, aLivemark);
            }, () => undefined);
        }

        let popup = document.createElement("menupopup");
        popup.setAttribute("placespopup", "true");
        button.appendChild(popup);
        popup._placesNode = PlacesUtils.asContainer(aChild);
        popup.setAttribute("context", "placesContext");

        this._domNodes.set(aChild, popup);
      } else if (PlacesUtils.nodeIsURI(aChild)) {
        button.setAttribute("scheme",
                            PlacesUIUtils.guessUrlSchemeForUI(aChild.uri));
      }
    }

    button._placesNode = aChild;
    if (!this._domNodes.has(aChild))
      this._domNodes.set(aChild, button);

    if (aBefore)
      aInsertionNode.insertBefore(button, aBefore);
    else
      aInsertionNode.appendChild(button);
    return button;
  },

  _updateChevronPopupNodesVisibility:
  function PT__updateChevronPopupNodesVisibility() {
    // Note the toolbar by default builds less nodes than the chevron popup.
    for (let toolbarNode = this._rootElt.firstChild,
         node = this._chevronPopup._startMarker.nextSibling;
         toolbarNode && node;
         toolbarNode = toolbarNode.nextSibling, node = node.nextSibling) {
      node.hidden = toolbarNode.style.visibility != "hidden";
    }
  },

  _onChevronPopupShowing:
  function PT__onChevronPopupShowing(aEvent) {
    // Handle popupshowing only for the chevron popup, not for nested ones.
    if (aEvent.target != this._chevronPopup)
      return;

    if (!this._chevron._placesView)
      this._chevron._placesView = new PlacesMenu(aEvent, this.place);

    this._updateChevronPopupNodesVisibility();
  },

  handleEvent: function PT_handleEvent(aEvent) {
    switch (aEvent.type) {
      case "unload":
        this.uninit();
        break;
      case "resize":
        // This handler updates nodes visibility in both the toolbar
        // and the chevron popup when a window resize does not change
        // the overflow status of the toolbar.
        if (aEvent.target == aEvent.currentTarget) {
          this.updateNodesVisibility();
        }
        break;
      case "overflow":
        if (!this._isOverflowStateEventRelevant(aEvent))
          return;
        this._onOverflow();
        break;
      case "underflow":
        if (!this._isOverflowStateEventRelevant(aEvent))
          return;
        this._onUnderflow();
        break;
      case "TabOpen":
      case "TabClose":
        this.updateNodesVisibility();
        break;
      case "dragstart":
        this._onDragStart(aEvent);
        break;
      case "dragover":
        this._onDragOver(aEvent);
        break;
      case "dragexit":
        this._onDragExit(aEvent);
        break;
      case "dragend":
        this._onDragEnd(aEvent);
        break;
      case "drop":
        this._onDrop(aEvent);
        break;
      case "mouseover":
        this._onMouseOver(aEvent);
        break;
      case "mousemove":
        this._onMouseMove(aEvent);
        break;
      case "mouseout":
        this._onMouseOut(aEvent);
        break;
      case "popupshowing":
        this._onPopupShowing(aEvent);
        break;
      case "popuphidden":
        this._onPopupHidden(aEvent);
        break;
      default:
        throw "Trying to handle unexpected event.";
    }
  },

  _isOverflowStateEventRelevant: function PT_isOverflowStateEventRelevant(aEvent) {
    // Ignore events not aimed at ourselves, as well as purely vertical ones:
    return aEvent.target == aEvent.currentTarget && aEvent.detail > 0;
  },

  _onOverflow: function PT_onOverflow() {
    // Attach the popup binding to the chevron popup if it has not yet
    // been initialized.
    if (!this._chevronPopup.hasAttribute("type")) {
      this._chevronPopup.setAttribute("place", this.place);
      this._chevronPopup.setAttribute("type", "places");
    }
    this._chevron.collapsed = false;
    this.updateNodesVisibility();
  },

  _onUnderflow: function PT_onUnderflow() {
    this.updateNodesVisibility();
    this._chevron.collapsed = true;
  },

  updateNodesVisibility: function PT_updateNodesVisibility() {
    // Update the chevron on a timer.  This will avoid repeated work when
    // lot of changes happen in a small timeframe.
    if (this._updateNodesVisibilityTimer)
      this._updateNodesVisibilityTimer.cancel();

    this._updateNodesVisibilityTimer = this._setTimer(100);
  },

  _updateNodesVisibilityTimerCallback: function PT__updateNodesVisibilityTimerCallback() {
    let scrollRect = this._rootElt.getBoundingClientRect();
    let childOverflowed = false;
    for (let child of this._rootElt.childNodes) {
      // Once a child overflows, all the next ones will.
      if (!childOverflowed) {
        let childRect = child.getBoundingClientRect();
        childOverflowed = this.isRTL ? (childRect.left < scrollRect.left)
                                     : (childRect.right > scrollRect.right);
      }

      if (childOverflowed) {
        child.removeAttribute("image");
        child.style.visibility = "hidden";
      } else {
        let icon = child._placesNode.icon;
        if (icon)
          child.setAttribute("image", icon);
        child.style.visibility = "visible";
      }
    }

    // We rebuild the chevron on popupShowing, so if it is open
    // we must update it.
    if (!this._chevron.collapsed && this._chevron.open)
      this._updateChevronPopupNodesVisibility();
    let event = new CustomEvent("BookmarksToolbarVisibilityUpdated", {bubbles: true});
    this._viewElt.dispatchEvent(event);
  },

  nodeInserted:
  function PT_nodeInserted(aParentPlacesNode, aPlacesNode, aIndex) {
    let parentElt = this._getDOMNodeForPlacesNode(aParentPlacesNode);
    if (parentElt == this._rootElt) { // Node is on the toolbar.
      let children = this._rootElt.childNodes;
      // Nothing to do if it's a never-visible node, but note it's possible
      // we are appending.
      if (aIndex > children.length)
        return;

      // Note that childCount is already accounting for the node being added,
      // thus we must subtract one node from it.
      if (this._resultNode.childCount - 1 > children.length) {
        if (aIndex == children.length) {
          // If we didn't build all the nodes and new node is being appended,
          // we can skip it as well.
          return;
        }
        // Keep the number of built nodes consistent.
        this._rootElt.removeChild(this._rootElt.lastChild);
      }

      let button = this._insertNewItem(aPlacesNode, this._rootElt,
                                       children[aIndex] || null);
      let prevSiblingOverflowed = aIndex > 0 && aIndex <= children.length &&
                                  children[aIndex - 1].style.visibility == "hidden";
      if (prevSiblingOverflowed) {
        button.style.visibility = "hidden";
      } else {
        let icon = aPlacesNode.icon;
        if (icon)
          button.setAttribute("image", icon);
        this.updateNodesVisibility();
      }
      return;
    }

    PlacesViewBase.prototype.nodeInserted.apply(this, arguments);
  },

  nodeRemoved:
  function PT_nodeRemoved(aParentPlacesNode, aPlacesNode, aIndex) {
    let parentElt = this._getDOMNodeForPlacesNode(aParentPlacesNode);
    if (parentElt == this._rootElt) { // Node is on the toolbar.
      let elt = this._getDOMNodeForPlacesNode(aPlacesNode, true);
      // Nothing to do if it's a never-visible node.
      if (!elt)
        return;

      // Here we need the <menu>.
      if (elt.localName == "menupopup")
        elt = elt.parentNode;

      let overflowed = elt.style.visibility == "hidden";
      this._removeChild(elt);
      if (this._resultNode.childCount > this._rootElt.childNodes.length) {
        // A new node should be built to keep a coherent number of children.
        this._insertNewItem(this._resultNode.getChild(this._rootElt.childNodes.length),
                            this._rootElt);
      }
      if (!overflowed)
        this.updateNodesVisibility();
      return;
    }

    PlacesViewBase.prototype.nodeRemoved.apply(this, arguments);
  },

  nodeMoved:
  function PT_nodeMoved(aPlacesNode,
                        aOldParentPlacesNode, aOldIndex,
                        aNewParentPlacesNode, aNewIndex) {
    let parentElt = this._getDOMNodeForPlacesNode(aNewParentPlacesNode);
    if (parentElt == this._rootElt) { // Node is on the toolbar.
      // Do nothing if the node will never be visible.
      let lastBuiltIndex = this._rootElt.childNodes.length - 1;
      if (aOldIndex > lastBuiltIndex && aNewIndex > lastBuiltIndex + 1)
        return;

      let elt = this._getDOMNodeForPlacesNode(aPlacesNode, true);
      if (elt) {
        // Here we need the <menu>.
        if (elt.localName == "menupopup")
          elt = elt.parentNode;
        this._removeChild(elt);
      }

      if (aNewIndex > lastBuiltIndex + 1) {
        if (this._resultNode.childCount > this._rootElt.childNodes.length) {
          // If the element was built and becomes non built, another node should
          // be built to keep a coherent number of children.
          this._insertNewItem(this._resultNode.getChild(this._rootElt.childNodes.length),
                              this._rootElt);
        }
        return;
      }

      if (!elt) {
        // The node has not been inserted yet, so we must create it.
        elt = this._insertNewItem(aPlacesNode, this._rootElt, this._rootElt.childNodes[aNewIndex]);
        let icon = aPlacesNode.icon;
        if (icon)
          elt.setAttribute("image", icon);
      } else {
        this._rootElt.insertBefore(elt, this._rootElt.childNodes[aNewIndex]);
      }

      // The chevron view may get nodeMoved after the toolbar.  In such a case,
      // we should ensure (by manually swapping menuitems) that the actual nodes
      // are in the final position before updateNodesVisibility tries to update
      // their visibility, or the chevron may go out of sync.
      // Luckily updateNodesVisibility runs on a timer, so, by the time it updates
      // nodes, the menu has already handled the notification.

      this.updateNodesVisibility();
      return;
    }

    PlacesViewBase.prototype.nodeMoved.apply(this, arguments);
  },

  nodeAnnotationChanged:
  function PT_nodeAnnotationChanged(aPlacesNode, aAnno) {
    let elt = this._getDOMNodeForPlacesNode(aPlacesNode, true);
    // Nothing to do if it's a never-visible node.
    if (!elt || elt == this._rootElt)
      return;

    // We're notified for the menupopup, not the containing toolbarbutton.
    if (elt.localName == "menupopup")
      elt = elt.parentNode;

    if (elt.parentNode == this._rootElt) { // Node is on the toolbar.
      // All livemarks have a feedURI, so use it as our indicator.
      if (aAnno == PlacesUtils.LMANNO_FEEDURI) {
        elt.setAttribute("livemark", true);

        PlacesUtils.livemarks.getLivemark({ id: aPlacesNode.itemId })
          .then(aLivemark => {
            this.controller.cacheLivemarkInfo(aPlacesNode, aLivemark);
            this.invalidateContainer(aPlacesNode);
          }, Cu.reportError);
      }
    } else {
      // Node is in a submenu.
      PlacesViewBase.prototype.nodeAnnotationChanged.apply(this, arguments);
    }
  },

  nodeTitleChanged: function PT_nodeTitleChanged(aPlacesNode, aNewTitle) {
    let elt = this._getDOMNodeForPlacesNode(aPlacesNode, true);

    // Nothing to do if it's a never-visible node.
    if (!elt || elt == this._rootElt)
      return;

    PlacesViewBase.prototype.nodeTitleChanged.apply(this, arguments);

    // Here we need the <menu>.
    if (elt.localName == "menupopup")
      elt = elt.parentNode;

    if (elt.parentNode == this._rootElt) { // Node is on the toolbar.
      if (elt.style.visibility != "hidden")
        this.updateNodesVisibility();
    }
  },

  invalidateContainer: function PT_invalidateContainer(aPlacesNode) {
    let elt = this._getDOMNodeForPlacesNode(aPlacesNode, true);
    // Nothing to do if it's a never-visible node.
    if (!elt)
      return;

    if (elt == this._rootElt) {
      // Container is the toolbar itself.
      this._rebuild();
      return;
    }

    PlacesViewBase.prototype.invalidateContainer.apply(this, arguments);
  },

  _overFolder: { elt: null,
                 openTimer: null,
                 hoverTime: 350,
                 closeTimer: null },

  _clearOverFolder: function PT__clearOverFolder() {
    // The mouse is no longer dragging over the stored menubutton.
    // Close the menubutton, clear out drag styles, and clear all
    // timers for opening/closing it.
    if (this._overFolder.elt && this._overFolder.elt.lastChild) {
      if (!this._overFolder.elt.lastChild.hasAttribute("dragover")) {
        this._overFolder.elt.lastChild.hidePopup();
      }
      this._overFolder.elt.removeAttribute("dragover");
      this._overFolder.elt = null;
    }
    if (this._overFolder.openTimer) {
      this._overFolder.openTimer.cancel();
      this._overFolder.openTimer = null;
    }
    if (this._overFolder.closeTimer) {
      this._overFolder.closeTimer.cancel();
      this._overFolder.closeTimer = null;
    }
  },

  /**
   * This function returns information about where to drop when dragging over
   * the toolbar.  The returned object has the following properties:
   * - ip: the insertion point for the bookmarks service.
   * - beforeIndex: child index to drop before, for the drop indicator.
   * - folderElt: the folder to drop into, if applicable.
   */
  _getDropPoint: function PT__getDropPoint(aEvent) {
    if (!PlacesUtils.nodeIsFolder(this._resultNode))
      return null;

    let dropPoint = { ip: null, beforeIndex: null, folderElt: null };
    let elt = aEvent.target;
    if (elt._placesNode && elt != this._rootElt &&
        elt.localName != "menupopup") {
      let eltRect = elt.getBoundingClientRect();
      let eltIndex = Array.prototype.indexOf.call(this._rootElt.childNodes, elt);
      if (PlacesUtils.nodeIsFolder(elt._placesNode) &&
          !PlacesUIUtils.isFolderReadOnly(elt._placesNode, this)) {
        // This is a folder.
        // If we are in the middle of it, drop inside it.
        // Otherwise, drop before it, with regards to RTL mode.
        let threshold = eltRect.width * 0.25;
        if (this.isRTL ? (aEvent.clientX > eltRect.right - threshold)
                       : (aEvent.clientX < eltRect.left + threshold)) {
          // Drop before this folder.
          dropPoint.ip =
            new PlacesInsertionPoint({
              parentId: PlacesUtils.getConcreteItemId(this._resultNode),
              parentGuid: PlacesUtils.getConcreteItemGuid(this._resultNode),
              index: eltIndex,
              orientation: Ci.nsITreeView.DROP_BEFORE
            });
          dropPoint.beforeIndex = eltIndex;
        } else if (this.isRTL ? (aEvent.clientX > eltRect.left + threshold)
                            : (aEvent.clientX < eltRect.right - threshold)) {
          // Drop inside this folder.
          let tagName = PlacesUtils.nodeIsTagQuery(elt._placesNode) ?
                        elt._placesNode.title : null;
          dropPoint.ip =
            new PlacesInsertionPoint({
              parentId: PlacesUtils.getConcreteItemId(elt._placesNode),
              parentGuid: PlacesUtils.getConcreteItemGuid(elt._placesNode),
              tagName
            });
          dropPoint.beforeIndex = eltIndex;
          dropPoint.folderElt = elt;
        } else {
          // Drop after this folder.
          let beforeIndex =
            (eltIndex == this._rootElt.childNodes.length - 1) ?
            -1 : eltIndex + 1;

          dropPoint.ip =
            new PlacesInsertionPoint({
              parentId: PlacesUtils.getConcreteItemId(this._resultNode),
              parentGuid: PlacesUtils.getConcreteItemGuid(this._resultNode),
              index: beforeIndex,
              orientation: Ci.nsITreeView.DROP_BEFORE
            });
          dropPoint.beforeIndex = beforeIndex;
        }
      } else {
        // This is a non-folder node or a read-only folder.
        // Drop before it with regards to RTL mode.
        let threshold = eltRect.width * 0.5;
        if (this.isRTL ? (aEvent.clientX > eltRect.left + threshold)
                       : (aEvent.clientX < eltRect.left + threshold)) {
          // Drop before this bookmark.
          dropPoint.ip =
            new PlacesInsertionPoint({
              parentId: PlacesUtils.getConcreteItemId(this._resultNode),
              parentGuid: PlacesUtils.getConcreteItemGuid(this._resultNode),
              index: eltIndex,
              orientation: Ci.nsITreeView.DROP_BEFORE
            });
          dropPoint.beforeIndex = eltIndex;
        } else {
          // Drop after this bookmark.
          let beforeIndex =
            eltIndex == this._rootElt.childNodes.length - 1 ?
            -1 : eltIndex + 1;
          dropPoint.ip =
            new PlacesInsertionPoint({
              parentId: PlacesUtils.getConcreteItemId(this._resultNode),
              parentGuid: PlacesUtils.getConcreteItemGuid(this._resultNode),
              index: beforeIndex,
              orientation: Ci.nsITreeView.DROP_BEFORE
            });
          dropPoint.beforeIndex = beforeIndex;
        }
      }
    } else {
      // We are most likely dragging on the empty area of the
      // toolbar, we should drop after the last node.
      dropPoint.ip =
        new PlacesInsertionPoint({
          parentId: PlacesUtils.getConcreteItemId(this._resultNode),
          parentGuid: PlacesUtils.getConcreteItemGuid(this._resultNode),
          orientation: Ci.nsITreeView.DROP_BEFORE
        });
      dropPoint.beforeIndex = -1;
    }

    return dropPoint;
  },

  _setTimer: function PT_setTimer(aTime) {
    let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
    timer.initWithCallback(this, aTime, timer.TYPE_ONE_SHOT);
    return timer;
  },

  notify: function PT_notify(aTimer) {
    if (aTimer == this._updateNodesVisibilityTimer) {
      this._updateNodesVisibilityTimer = null;
      // Bug 1440070: This should use promiseDocumentFlushed, so that
      // _updateNodesVisibilityTimerCallback can use getBoundsWithoutFlush.
      window.requestAnimationFrame(this._updateNodesVisibilityTimerCallback.bind(this));
    } else if (aTimer == this._ibTimer) {
      // * Timer to turn off indicator bar.
      this._dropIndicator.collapsed = true;
      this._ibTimer = null;
    } else if (aTimer == this._overFolder.openTimer) {
      // * Timer to open a menubutton that's being dragged over.
      // Set the autoopen attribute on the folder's menupopup so that
      // the menu will automatically close when the mouse drags off of it.
      this._overFolder.elt.lastChild.setAttribute("autoopened", "true");
      this._overFolder.elt.open = true;
      this._overFolder.openTimer = null;
    } else if (aTimer == this._overFolder.closeTimer) {
      // * Timer to close a menubutton that's been dragged off of.
      // Close the menubutton if we are not dragging over it or one of
      // its children.  The autoopened attribute will let the menu know to
      // close later if the menu is still being dragged over.
      let currentPlacesNode = PlacesControllerDragHelper.currentDropTarget;
      let inHierarchy = false;
      while (currentPlacesNode) {
        if (currentPlacesNode == this._rootElt) {
          inHierarchy = true;
          break;
        }
        currentPlacesNode = currentPlacesNode.parentNode;
      }
      // The _clearOverFolder() function will close the menu for
      // _overFolder.elt.  So null it out if we don't want to close it.
      if (inHierarchy)
        this._overFolder.elt = null;

      // Clear out the folder and all associated timers.
      this._clearOverFolder();
    }
  },

  _onMouseOver: function PT__onMouseOver(aEvent) {
    let button = aEvent.target;
    if (button.parentNode == this._rootElt && button._placesNode &&
        PlacesUtils.nodeIsURI(button._placesNode))
      window.XULBrowserWindow.setOverLink(aEvent.target._placesNode.uri, null);
  },

  _onMouseOut: function PT__onMouseOut(aEvent) {
    window.XULBrowserWindow.setOverLink("", null);
  },

  _cleanupDragDetails: function PT__cleanupDragDetails() {
    // Called on dragend and drop.
    PlacesControllerDragHelper.currentDropTarget = null;
    this._draggedElt = null;
    if (this._ibTimer)
      this._ibTimer.cancel();

    this._dropIndicator.collapsed = true;
  },

  _onDragStart: function PT__onDragStart(aEvent) {
    // Sub menus have their own d&d handlers.
    let draggedElt = aEvent.target;
    if (draggedElt.parentNode != this._rootElt || !draggedElt._placesNode)
      return;

    if (draggedElt.localName == "toolbarbutton" &&
        draggedElt.getAttribute("type") == "menu") {
      // If the drag gesture on a container is toward down we open instead
      // of dragging.
      let translateY = this._cachedMouseMoveEvent.clientY - aEvent.clientY;
      let translateX = this._cachedMouseMoveEvent.clientX - aEvent.clientX;
      if ((translateY) >= Math.abs(translateX / 2)) {
        // Don't start the drag.
        aEvent.preventDefault();
        // Open the menu.
        draggedElt.open = true;
        return;
      }

      // If the menu is open, close it.
      if (draggedElt.open) {
        draggedElt.lastChild.hidePopup();
        draggedElt.open = false;
      }
    }

    // Activate the view and cache the dragged element.
    this._draggedElt = draggedElt._placesNode;
    this._rootElt.focus();

    this._controller.setDataTransfer(aEvent);
    aEvent.stopPropagation();
  },

  _onDragOver: function PT__onDragOver(aEvent) {
    // Cache the dataTransfer
    PlacesControllerDragHelper.currentDropTarget = aEvent.target;
    let dt = aEvent.dataTransfer;

    let dropPoint = this._getDropPoint(aEvent);
    if (!dropPoint || !dropPoint.ip ||
        !PlacesControllerDragHelper.canDrop(dropPoint.ip, dt)) {
      this._dropIndicator.collapsed = true;
      aEvent.stopPropagation();
      return;
    }

    if (this._ibTimer) {
      this._ibTimer.cancel();
      this._ibTimer = null;
    }

    if (dropPoint.folderElt || aEvent.originalTarget == this._chevron) {
      // Dropping over a menubutton or chevron button.
      // Set styles and timer to open relative menupopup.
      let overElt = dropPoint.folderElt || this._chevron;
      if (this._overFolder.elt != overElt) {
        this._clearOverFolder();
        this._overFolder.elt = overElt;
        this._overFolder.openTimer = this._setTimer(this._overFolder.hoverTime);
      }
      if (!this._overFolder.elt.hasAttribute("dragover"))
        this._overFolder.elt.setAttribute("dragover", "true");

      this._dropIndicator.collapsed = true;
    } else {
      // Dragging over a normal toolbarbutton,
      // show indicator bar and move it to the appropriate drop point.
      let ind = this._dropIndicator;
      ind.parentNode.collapsed = false;
      let halfInd = ind.clientWidth / 2;
      let translateX;
      if (this.isRTL) {
        halfInd = Math.ceil(halfInd);
        translateX = 0 - this._rootElt.getBoundingClientRect().right - halfInd;
        if (this._rootElt.firstChild) {
          if (dropPoint.beforeIndex == -1)
            translateX += this._rootElt.lastChild.getBoundingClientRect().left;
          else {
            translateX += this._rootElt.childNodes[dropPoint.beforeIndex]
                              .getBoundingClientRect().right;
          }
        }
      } else {
        halfInd = Math.floor(halfInd);
        translateX = 0 - this._rootElt.getBoundingClientRect().left +
                     halfInd;
        if (this._rootElt.firstChild) {
          if (dropPoint.beforeIndex == -1)
            translateX += this._rootElt.lastChild.getBoundingClientRect().right;
          else {
            translateX += this._rootElt.childNodes[dropPoint.beforeIndex]
                              .getBoundingClientRect().left;
          }
        }
      }

      ind.style.transform = "translate(" + Math.round(translateX) + "px)";
      ind.style.marginInlineStart = (-ind.clientWidth) + "px";
      ind.collapsed = false;

      // Clear out old folder information.
      this._clearOverFolder();
    }

    aEvent.preventDefault();
    aEvent.stopPropagation();
  },

  _onDrop: function PT__onDrop(aEvent) {
    PlacesControllerDragHelper.currentDropTarget = aEvent.target;

    let dropPoint = this._getDropPoint(aEvent);
    if (dropPoint && dropPoint.ip) {
      PlacesControllerDragHelper.onDrop(dropPoint.ip, aEvent.dataTransfer)
                                .catch(Cu.reportError);
      aEvent.preventDefault();
    }

    this._cleanupDragDetails();
    aEvent.stopPropagation();
  },

  _onDragExit: function PT__onDragExit(aEvent) {
    PlacesControllerDragHelper.currentDropTarget = null;

    // Set timer to turn off indicator bar (if we turn it off
    // here, dragenter might be called immediately after, creating
    // flicker).
    if (this._ibTimer)
      this._ibTimer.cancel();
    this._ibTimer = this._setTimer(10);

    // If we hovered over a folder, close it now.
    if (this._overFolder.elt)
        this._overFolder.closeTimer = this._setTimer(this._overFolder.hoverTime);
  },

  _onDragEnd: function PT_onDragEnd(aEvent) {
    this._cleanupDragDetails();
  },

  _onPopupShowing: function PT__onPopupShowing(aEvent) {
    if (!this._allowPopupShowing) {
      this._allowPopupShowing = true;
      aEvent.preventDefault();
      return;
    }

    let parent = aEvent.target.parentNode;
    if (parent.localName == "toolbarbutton")
      this._openedMenuButton = parent;

    PlacesViewBase.prototype._onPopupShowing.apply(this, arguments);
  },

  _onPopupHidden: function PT__onPopupHidden(aEvent) {
    let popup = aEvent.target;
    let placesNode = popup._placesNode;
    // Avoid handling popuphidden of inner views
    if (placesNode && PlacesUIUtils.getViewForNode(popup) == this) {
      // UI performance: folder queries are cheap, keep the resultnode open
      // so we don't rebuild its contents whenever the popup is reopened.
      // Though, we want to always close feed containers so their expiration
      // status will be checked at next opening.
      if (!PlacesUtils.nodeIsFolder(placesNode) ||
          this.controller.hasCachedLivemarkInfo(placesNode)) {
        placesNode.containerOpen = false;
      }
    }

    let parent = popup.parentNode;
    if (parent.localName == "toolbarbutton") {
      this._openedMenuButton = null;
      // Clear the dragover attribute if present, if we are dragging into a
      // folder in the hierachy of current opened popup we don't clear
      // this attribute on clearOverFolder.  See Notify for closeTimer.
      if (parent.hasAttribute("dragover"))
        parent.removeAttribute("dragover");
    }
  },

  _onMouseMove: function PT__onMouseMove(aEvent) {
    // Used in dragStart to prevent dragging folders when dragging down.
    this._cachedMouseMoveEvent = aEvent;

    if (this._openedMenuButton == null ||
        PlacesControllerDragHelper.getSession())
      return;

    let target = aEvent.originalTarget;
    if (this._openedMenuButton != target &&
        target.localName == "toolbarbutton" &&
        target.type == "menu") {
      this._openedMenuButton.open = false;
      target.open = true;
    }
  }
};

/**
 * View for Places menus.  This object should be created during the first
 * popupshowing that's dispatched on the menu.
 */
function PlacesMenu(aPopupShowingEvent, aPlace, aOptions) {
  this._rootElt = aPopupShowingEvent.target; // <menupopup>
  this._viewElt = this._rootElt.parentNode; // <menu>
  this._viewElt._placesView = this;
  this._addEventListeners(this._rootElt, ["popupshowing", "popuphidden"], true);
  this._addEventListeners(window, ["unload"], false);

  if (AppConstants.platform === "macosx") {
    // Must walk up to support views in sub-menus, like Bookmarks Toolbar menu.
    for (let elt = this._viewElt.parentNode; elt; elt = elt.parentNode) {
      if (elt.localName == "menubar") {
        this._nativeView = true;
        break;
      }
    }
  }

  PlacesViewBase.call(this, aPlace, aOptions);
  this._onPopupShowing(aPopupShowingEvent);
}

PlacesMenu.prototype = {
  __proto__: PlacesViewBase.prototype,

  QueryInterface: function PM_QueryInterface(aIID) {
    if (aIID.equals(Ci.nsIDOMEventListener))
      return this;

    return PlacesViewBase.prototype.QueryInterface.apply(this, arguments);
  },

  _removeChild: function PM_removeChild(aChild) {
    PlacesViewBase.prototype._removeChild.apply(this, arguments);
  },

  uninit: function PM_uninit() {
    this._removeEventListeners(this._rootElt, ["popupshowing", "popuphidden"],
                               true);
    this._removeEventListeners(window, ["unload"], false);

    PlacesViewBase.prototype.uninit.apply(this, arguments);
  },

  handleEvent: function PM_handleEvent(aEvent) {
    switch (aEvent.type) {
      case "unload":
        this.uninit();
        break;
      case "popupshowing":
        this._onPopupShowing(aEvent);
        break;
      case "popuphidden":
        this._onPopupHidden(aEvent);
        break;
    }
  },

  _onPopupHidden: function PM__onPopupHidden(aEvent) {
    // Avoid handling popuphidden of inner views.
    let popup = aEvent.originalTarget;
    let placesNode = popup._placesNode;
    if (!placesNode || PlacesUIUtils.getViewForNode(popup) != this)
      return;

    // UI performance: folder queries are cheap, keep the resultnode open
    // so we don't rebuild its contents whenever the popup is reopened.
    // Though, we want to always close feed containers so their expiration
    // status will be checked at next opening.
    if (!PlacesUtils.nodeIsFolder(placesNode) ||
        this.controller.hasCachedLivemarkInfo(placesNode))
      placesNode.containerOpen = false;

    // The autoopened attribute is set for folders which have been
    // automatically opened when dragged over.  Turn off this attribute
    // when the folder closes because it is no longer applicable.
    popup.removeAttribute("autoopened");
    popup.removeAttribute("dragstart");
  }
};

function PlacesPanelMenuView(aPlace, aViewId, aRootId, aOptions) {
  this._viewElt = document.getElementById(aViewId);
  this._rootElt = document.getElementById(aRootId);
  this._viewElt._placesView = this;
  this.options = aOptions;

  PlacesViewBase.call(this, aPlace, aOptions);
}

PlacesPanelMenuView.prototype = {
  __proto__: PlacesViewBase.prototype,

  QueryInterface: function PAMV_QueryInterface(aIID) {
    return PlacesViewBase.prototype.QueryInterface.apply(this, arguments);
  },

  uninit: function PAMV_uninit() {
    PlacesViewBase.prototype.uninit.apply(this, arguments);
  },

  _insertNewItem:
  function PAMV__insertNewItem(aChild, aInsertionNode, aBefore = null) {
    this._domNodes.delete(aChild);

    let type = aChild.type;
    let button;
    if (type == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR) {
      button = document.createElement("toolbarseparator");
      button.setAttribute("class", "small-separator");
    } else {
      button = document.createElement("toolbarbutton");
      button.className = "bookmark-item";
      if (typeof this.options.extraClasses.entry == "string")
        button.classList.add(this.options.extraClasses.entry);
      button.setAttribute("label", aChild.title || "");
      let icon = aChild.icon;
      if (icon)
        button.setAttribute("image", icon);

      if (PlacesUtils.containerTypes.includes(type)) {
        button.setAttribute("container", "true");

        if (PlacesUtils.nodeIsQuery(aChild)) {
          button.setAttribute("query", "true");
          if (PlacesUtils.nodeIsTagQuery(aChild))
            button.setAttribute("tagContainer", "true");
        } else if (PlacesUtils.nodeIsFolder(aChild)) {
          PlacesUtils.livemarks.getLivemark({ id: aChild.itemId })
            .then(aLivemark => {
              button.setAttribute("livemark", "true");
              this.controller.cacheLivemarkInfo(aChild, aLivemark);
            }, () => undefined);
        }
      } else if (PlacesUtils.nodeIsURI(aChild)) {
        button.setAttribute("scheme",
                            PlacesUIUtils.guessUrlSchemeForUI(aChild.uri));
      }
    }

    button._placesNode = aChild;
    if (!this._domNodes.has(aChild))
      this._domNodes.set(aChild, button);

    aInsertionNode.insertBefore(button, aBefore);
    return button;
  },

  nodeInserted:
  function PAMV_nodeInserted(aParentPlacesNode, aPlacesNode, aIndex) {
    let parentElt = this._getDOMNodeForPlacesNode(aParentPlacesNode);
    if (parentElt != this._rootElt)
      return;

    let children = this._rootElt.childNodes;
    this._insertNewItem(aPlacesNode, this._rootElt,
      aIndex < children.length ? children[aIndex] : null);
  },

  nodeRemoved:
  function PAMV_nodeRemoved(aParentPlacesNode, aPlacesNode, aIndex) {
    let parentElt = this._getDOMNodeForPlacesNode(aParentPlacesNode);
    if (parentElt != this._rootElt)
      return;

    let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
    this._removeChild(elt);
  },

  nodeMoved:
  function PAMV_nodeMoved(aPlacesNode,
                          aOldParentPlacesNode, aOldIndex,
                          aNewParentPlacesNode, aNewIndex) {
    let parentElt = this._getDOMNodeForPlacesNode(aNewParentPlacesNode);
    if (parentElt != this._rootElt)
      return;

    let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
    this._removeChild(elt);
    this._rootElt.insertBefore(elt, this._rootElt.childNodes[aNewIndex]);
  },

  nodeAnnotationChanged:
  function PAMV_nodeAnnotationChanged(aPlacesNode, aAnno) {
    let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
    // There's no UI representation for the root node.
    if (elt == this._rootElt)
      return;

    if (elt.parentNode != this._rootElt)
      return;

    // All livemarks have a feedURI, so use it as our indicator.
    if (aAnno == PlacesUtils.LMANNO_FEEDURI) {
      elt.setAttribute("livemark", true);

      PlacesUtils.livemarks.getLivemark({ id: aPlacesNode.itemId })
        .then(aLivemark => {
          this.controller.cacheLivemarkInfo(aPlacesNode, aLivemark);
          this.invalidateContainer(aPlacesNode);
        }, Cu.reportError);
    }
  },

  nodeTitleChanged: function PAMV_nodeTitleChanged(aPlacesNode, aNewTitle) {
    let elt = this._getDOMNodeForPlacesNode(aPlacesNode);

    // There's no UI representation for the root node.
    if (elt == this._rootElt)
      return;

    PlacesViewBase.prototype.nodeTitleChanged.apply(this, arguments);
  },

  invalidateContainer: function PAMV_invalidateContainer(aPlacesNode) {
    let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
    if (elt != this._rootElt)
      return;

    // Container is the toolbar itself.
    while (this._rootElt.hasChildNodes()) {
      this._rootElt.firstChild.remove();
    }

    let fragment = document.createDocumentFragment();
    for (let i = 0; i < this._resultNode.childCount; ++i) {
      this._insertNewItem(this._resultNode.getChild(i), fragment);
    }
    this._rootElt.appendChild(fragment);
  }
};

this.PlacesPanelview = class extends PlacesViewBase {
  constructor(container, panelview, place, options = {}) {
    options.rootElt = container;
    options.viewElt = panelview;
    super(place, options);
    this._viewElt._placesView = this;
    // We're simulating a popup show, because a panelview may only be shown when
    // its containing popup is already shown.
    this._onPopupShowing({ originalTarget: this._rootElt });
    this._addEventListeners(window, ["unload"]);
    this._rootElt.setAttribute("context", "placesContext");
  }

  get events() {
    if (this._events)
      return this._events;
    return this._events = ["click", "command", "dragend", "dragstart", "ViewHiding", "ViewShown"];
  }

  handleEvent(event) {
    switch (event.type) {
      case "click":
        // For middle clicks, fall through to the command handler.
        if (event.button != 1) {
          break;
        }
      case "command":
        this._onCommand(event);
        break;
      case "dragend":
        this._onDragEnd(event);
        break;
      case "dragstart":
        this._onDragStart(event);
        break;
      case "unload":
        this.uninit(event);
        break;
      case "ViewHiding":
        this._onPopupHidden(event);
        break;
      case "ViewShown":
        this._onViewShown(event);
        break;
    }
  }

  _onCommand(event) {
    let button = event.originalTarget;
    if (!button._placesNode)
      return;

    let modifKey = AppConstants.platform === "macosx" ? event.metaKey
                                                      : event.ctrlKey;
    if (!PlacesUIUtils.openInTabClosesMenu && modifKey) {
      // If 'Recent Bookmarks' in Bookmarks Panel.
      if (button.parentNode.id == "panelMenu_bookmarksMenu") {
        button.setAttribute("closemenu", "none");
      }
    } else {
      button.removeAttribute("closemenu");
    }
    PlacesUIUtils.openNodeWithEvent(button._placesNode, event);
    // Unlike left-click, middle-click requires manual menu closing.
    if (button.parentNode.id != "panelMenu_bookmarksMenu" ||
        (event.type == "click" && event.button == 1 && PlacesUIUtils.openInTabClosesMenu)) {
      this.panelMultiView.closest("panel").hidePopup();
    }
  }

  _onDragEnd() {
    this._draggedElt = null;
  }

  _onDragStart(event) {
    let draggedElt = event.originalTarget;
    if (draggedElt.parentNode != this._rootElt || !draggedElt._placesNode)
      return;

    // Activate the view and cache the dragged element.
    this._draggedElt = draggedElt._placesNode;
    this._rootElt.focus();

    this._controller.setDataTransfer(event);
    event.stopPropagation();
  }

  uninit(event) {
    this._removeEventListeners(this.panelMultiView, this.events);
    this._removeEventListeners(window, ["unload"]);
    delete this.panelMultiView;
    super.uninit(event);
  }

  _createDOMNodeForPlacesNode(placesNode) {
    this._domNodes.delete(placesNode);

    let element;
    let type = placesNode.type;
    if (type == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR) {
      element = document.createElement("toolbarseparator");
    } else {
      if (type != Ci.nsINavHistoryResultNode.RESULT_TYPE_URI)
        throw "Unexpected node";

      element = document.createElement("toolbarbutton");
      element.classList.add("subviewbutton", "subviewbutton-iconic", "bookmark-item");
      element.setAttribute("scheme", PlacesUIUtils.guessUrlSchemeForUI(placesNode.uri));
      element.setAttribute("label", PlacesUIUtils.getBestTitle(placesNode));

      let icon = placesNode.icon;
      if (icon)
        element.setAttribute("image", icon);
    }

    element._placesNode = placesNode;
    if (!this._domNodes.has(placesNode))
      this._domNodes.set(placesNode, element);

    return element;
  }

  _setEmptyPopupStatus(panelview, empty = false) {
    if (!panelview._emptyMenuitem) {
      let label = PlacesUIUtils.getString("bookmarksMenuEmptyFolder");
      panelview._emptyMenuitem = document.createElement("toolbarbutton");
      panelview._emptyMenuitem.setAttribute("label", label);
      panelview._emptyMenuitem.setAttribute("disabled", true);
      panelview._emptyMenuitem.className = "subviewbutton";
      if (typeof this.options.extraClasses.entry == "string")
        panelview._emptyMenuitem.classList.add(this.options.extraClasses.entry);
    }

    if (empty) {
      panelview.setAttribute("emptyplacesresult", "true");
      // Don't add the menuitem if there is static content.
      // We also support external usage for custom crafted panels - which'll have
      // no markers present.
      if (!panelview._startMarker ||
          (!panelview._startMarker.previousSibling && !panelview._endMarker.nextSibling)) {
        panelview.insertBefore(panelview._emptyMenuitem, panelview._endMarker);
      }
    } else {
      panelview.removeAttribute("emptyplacesresult");
      try {
        panelview.removeChild(panelview._emptyMenuitem);
      } catch (ex) {}
    }
  }

  _isPopupOpen() {
    return PanelView.forNode(this._viewElt).active;
  }

  _onPopupHidden(event) {
    let panelview = event.originalTarget;
    let placesNode = panelview._placesNode;
    // Avoid handling ViewHiding of inner views
    if (placesNode && PlacesUIUtils.getViewForNode(panelview) == this) {
      // UI performance: folder queries are cheap, keep the resultnode open
      // so we don't rebuild its contents whenever the popup is reopened.
      // Though, we want to always close feed containers so their expiration
      // status will be checked at next opening.
      if (!PlacesUtils.nodeIsFolder(placesNode) ||
          this.controller.hasCachedLivemarkInfo(placesNode)) {
        placesNode.containerOpen = false;
      }
    }
  }

  _onPopupShowing(event) {
    // If the event came from the root element, this is the first time
    // we ever get here.
    if (event.originalTarget == this._rootElt) {
      // Start listening for events from all panels inside the panelmultiview.
      this.panelMultiView = this._viewElt.panelMultiView;
      this._addEventListeners(this.panelMultiView, this.events);
    }
    super._onPopupShowing(event);
  }

  _onViewShown(event) {
    if (event.originalTarget != this._viewElt)
      return;

    // Because PanelMultiView reparents the panelview internally, the controller
    // may get lost. In that case we'll append it again, because we certainly
    // need it later!
    if (!this.controllers.getControllerCount() && this._controller)
      this.controllers.appendController(this._controller);
  }
};
PK
!<TC#�#�3chrome/browser/content/browser/places/controller.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
 * Represents an insertion point within a container where we can insert
 * items.
 * @param {object} an object containing the following properties:
 *   - parentId
 *     The identifier of the parent container
 *   - parentGuid
 *     The unique identifier of the parent container
 *   - index
 *     The index within the container where to insert, defaults to appending
 *   - orientation
 *     The orientation of the insertion. NOTE: the adjustments to the
 *     insertion point to accommodate the orientation should be done by
 *     the person who constructs the IP, not the user. The orientation
 *     is provided for informational purposes only! Defaults to DROP_ON.
 *   - tagName
 *     The tag name if this IP is set to a tag, null otherwise.
 *   - dropNearNode
 *     When defined index will be calculated based on this node
 */
function PlacesInsertionPoint({ parentId, parentGuid,
                                index = PlacesUtils.bookmarks.DEFAULT_INDEX,
                                orientation = Ci.nsITreeView.DROP_ON,
                                tagName = null,
                                dropNearNode = null }) {
  this.itemId = parentId;
  this.guid = parentGuid;
  this._index = index;
  this.orientation = orientation;
  this.tagName = tagName;
  this.dropNearNode = dropNearNode;
}

PlacesInsertionPoint.prototype = {
  set index(val) {
    return this._index = val;
  },

  async getIndex() {
    if (this.dropNearNode) {
      // If dropNearNode is set up we must calculate the index of the item near
      // which we will drop.
      let index = (await PlacesUtils.bookmarks.fetch(this.dropNearNode.bookmarkGuid)).index;
      return this.orientation == Ci.nsITreeView.DROP_BEFORE ? index : index + 1;
    }
    return this._index;
  },

  get isTag() {
    return typeof(this.tagName) == "string";
  }
};

/**
 * Places Controller
 */

function PlacesController(aView) {
  this._view = aView;
  XPCOMUtils.defineLazyServiceGetter(this, "clipboard",
                                     "@mozilla.org/widget/clipboard;1",
                                     "nsIClipboard");
  XPCOMUtils.defineLazyGetter(this, "profileName", function() {
    return Services.dirsvc.get("ProfD", Ci.nsIFile).leafName;
  });

  this._cachedLivemarkInfoObjects = new Map();
}

PlacesController.prototype = {
  /**
   * The places view.
   */
  _view: null,

  // This is used in certain views to disable user actions on the places tree
  // views. This avoids accidental deletion/modification when the user is not
  // actually organising the trees.
  disableUserActions: false,

  QueryInterface: XPCOMUtils.generateQI([
    Ci.nsIClipboardOwner
  ]),

  // nsIClipboardOwner
  LosingOwnership: function PC_LosingOwnership(aXferable) {
    this.cutNodes = [];
  },

  terminate: function PC_terminate() {
    this._releaseClipboardOwnership();
  },

  supportsCommand: function PC_supportsCommand(aCommand) {
    if (this.disableUserActions) {
      return false;
    }
    // Non-Places specific commands that we also support
    switch (aCommand) {
    case "cmd_undo":
    case "cmd_redo":
    case "cmd_cut":
    case "cmd_copy":
    case "cmd_paste":
    case "cmd_delete":
    case "cmd_selectAll":
      return true;
    }

    // All other Places Commands are prefixed with "placesCmd_" ... this
    // filters out other commands that we do _not_ support (see 329587).
    const CMD_PREFIX = "placesCmd_";
    return (aCommand.substr(0, CMD_PREFIX.length) == CMD_PREFIX);
  },

  isCommandEnabled: function PC_isCommandEnabled(aCommand) {
    switch (aCommand) {
    case "cmd_undo":
      return PlacesTransactions.topUndoEntry != null;
    case "cmd_redo":
      return PlacesTransactions.topRedoEntry != null;
    case "cmd_cut":
    case "placesCmd_cut":
      for (let node of this._view.selectedNodes) {
        // If selection includes history nodes or tags-as-bookmark, disallow
        // cutting.
        if (node.itemId == -1 ||
            (node.parent && PlacesUtils.nodeIsTagQuery(node.parent))) {
          return false;
        }
      }
      // Otherwise fall through the cmd_delete check.
    case "cmd_delete":
    case "placesCmd_delete":
    case "placesCmd_deleteDataHost":
      return this._hasRemovableSelection();
    case "cmd_copy":
    case "placesCmd_copy":
      return this._view.hasSelection;
    case "cmd_paste":
    case "placesCmd_paste":
      return this._canInsert(true) && this._isClipboardDataPasteable();
    case "cmd_selectAll":
      if (this._view.selType != "single") {
        let rootNode = this._view.result.root;
        if (rootNode.containerOpen && rootNode.childCount > 0)
          return true;
      }
      return false;
    case "placesCmd_open":
    case "placesCmd_open:window":
    case "placesCmd_open:privatewindow":
    case "placesCmd_open:tab": {
      let selectedNode = this._view.selectedNode;
      return selectedNode && PlacesUtils.nodeIsURI(selectedNode);
    }
    case "placesCmd_new:folder":
      return this._canInsert();
    case "placesCmd_new:bookmark":
      return this._canInsert();
    case "placesCmd_new:separator":
      return this._canInsert() &&
             !PlacesUtils.asQuery(this._view.result.root).queryOptions.excludeItems &&
             this._view.result.sortingMode ==
                 Ci.nsINavHistoryQueryOptions.SORT_BY_NONE;
    case "placesCmd_show:info": {
      let selectedNode = this._view.selectedNode;
      return selectedNode && PlacesUtils.getConcreteItemId(selectedNode) != -1;
    }
    case "placesCmd_reload": {
      // Livemark containers
      let selectedNode = this._view.selectedNode;
      return selectedNode && this.hasCachedLivemarkInfo(selectedNode);
    }
    case "placesCmd_sortBy:name": {
      let selectedNode = this._view.selectedNode;
      return selectedNode &&
             PlacesUtils.nodeIsFolder(selectedNode) &&
             !PlacesUIUtils.isFolderReadOnly(selectedNode, this._view) &&
             this._view.result.sortingMode ==
                 Ci.nsINavHistoryQueryOptions.SORT_BY_NONE;
    }
    case "placesCmd_createBookmark":
      var node = this._view.selectedNode;
      return node && PlacesUtils.nodeIsURI(node) && node.itemId == -1;
    default:
      return false;
    }
  },

  doCommand: function PC_doCommand(aCommand) {
    switch (aCommand) {
    case "cmd_undo":
      PlacesTransactions.undo().catch(Cu.reportError);
      break;
    case "cmd_redo":
      PlacesTransactions.redo().catch(Cu.reportError);
      break;
    case "cmd_cut":
    case "placesCmd_cut":
      this.cut();
      break;
    case "cmd_copy":
    case "placesCmd_copy":
      this.copy();
      break;
    case "cmd_paste":
    case "placesCmd_paste":
      this.paste().catch(Cu.reportError);
      break;
    case "cmd_delete":
    case "placesCmd_delete":
      this.remove("Remove Selection").catch(Cu.reportError);
      break;
    case "placesCmd_deleteDataHost":
      var host;
      if (PlacesUtils.nodeIsHost(this._view.selectedNode)) {
        var queries = this._view.selectedNode.getQueries();
        host = queries[0].domain;
      } else
        host = Services.io.newURI(this._view.selectedNode.uri).host;
      ForgetAboutSite.removeDataFromDomain(host)
                     .catch(Cu.reportError);
      break;
    case "cmd_selectAll":
      this.selectAll();
      break;
    case "placesCmd_open":
      PlacesUIUtils.openNodeIn(this._view.selectedNode, "current", this._view);
      break;
    case "placesCmd_open:window":
      PlacesUIUtils.openNodeIn(this._view.selectedNode, "window", this._view);
      break;
    case "placesCmd_open:privatewindow":
      PlacesUIUtils.openNodeIn(this._view.selectedNode, "window", this._view, true);
      break;
    case "placesCmd_open:tab":
      PlacesUIUtils.openNodeIn(this._view.selectedNode, "tab", this._view);
      break;
    case "placesCmd_new:folder":
      this.newItem("folder").catch(Cu.reportError);
      break;
    case "placesCmd_new:bookmark":
      this.newItem("bookmark").catch(Cu.reportError);
      break;
    case "placesCmd_new:separator":
      this.newSeparator().catch(Cu.reportError);
      break;
    case "placesCmd_show:info":
      this.showBookmarkPropertiesForSelection();
      break;
    case "placesCmd_reload":
      this.reloadSelectedLivemark();
      break;
    case "placesCmd_sortBy:name":
      this.sortFolderByName().catch(Cu.reportError);
      break;
    case "placesCmd_createBookmark":
      let node = this._view.selectedNode;
      PlacesUIUtils.showBookmarkDialog({ action: "add",
                                         type: "bookmark",
                                         hiddenRows: [ "description",
                                                        "keyword",
                                                        "location",
                                                        "loadInSidebar" ],
                                         uri: Services.io.newURI(node.uri),
                                         title: node.title
                                       }, window.top);
      break;
    }
  },

  onEvent: function PC_onEvent(eventName) { },


  /**
   * Determine whether or not the selection can be removed, either by the
   * delete or cut operations based on whether or not any of its contents
   * are non-removable. We don't need to worry about recursion here since it
   * is a policy decision that a removable item not be placed inside a non-
   * removable item.
   *
   * @return true if all nodes in the selection can be removed,
   *         false otherwise.
   */
  _hasRemovableSelection() {
    var ranges = this._view.removableSelectionRanges;
    if (!ranges.length)
      return false;

    var root = this._view.result.root;

    for (var j = 0; j < ranges.length; j++) {
      var nodes = ranges[j];
      for (var i = 0; i < nodes.length; ++i) {
        // Disallow removing the view's root node
        if (nodes[i] == root)
          return false;

        if (!PlacesUIUtils.canUserRemove(nodes[i], this._view))
          return false;
      }
    }

    return true;
  },

  /**
   * Determines whether or not nodes can be inserted relative to the selection.
   */
  _canInsert: function PC__canInsert(isPaste) {
    var ip = this._view.insertionPoint;
    return ip != null && (isPaste || !ip.isTag);
  },

  /**
   * Looks at the data on the clipboard to see if it is paste-able.
   * Paste-able data is:
   *   - in a format that the view can receive
   * @return true if: - clipboard data is of a TYPE_X_MOZ_PLACE_* flavor,
   *                  - clipboard data is of type TEXT_UNICODE and
   *                    is a valid URI.
   */
  _isClipboardDataPasteable: function PC__isClipboardDataPasteable() {
    // if the clipboard contains TYPE_X_MOZ_PLACE_* data, it is definitely
    // pasteable, with no need to unwrap all the nodes.

    var flavors = PlacesUIUtils.PLACES_FLAVORS;
    var clipboard = this.clipboard;
    var hasPlacesData =
      clipboard.hasDataMatchingFlavors(flavors, flavors.length,
                                       Ci.nsIClipboard.kGlobalClipboard);
    if (hasPlacesData)
      return this._view.insertionPoint != null;

    // if the clipboard doesn't have TYPE_X_MOZ_PLACE_* data, we also allow
    // pasting of valid "text/unicode" and "text/x-moz-url" data
    var xferable = Cc["@mozilla.org/widget/transferable;1"].
                   createInstance(Ci.nsITransferable);
    xferable.init(null);

    xferable.addDataFlavor(PlacesUtils.TYPE_X_MOZ_URL);
    xferable.addDataFlavor(PlacesUtils.TYPE_UNICODE);
    clipboard.getData(xferable, Ci.nsIClipboard.kGlobalClipboard);

    try {
      // getAnyTransferData will throw if no data is available.
      var data = { }, type = { };
      xferable.getAnyTransferData(type, data, { });
      data = data.value.QueryInterface(Ci.nsISupportsString).data;
      if (type.value != PlacesUtils.TYPE_X_MOZ_URL &&
          type.value != PlacesUtils.TYPE_UNICODE)
        return false;

      // unwrapNodes() will throw if the data blob is malformed.
      PlacesUtils.unwrapNodes(data, type.value);
      return this._view.insertionPoint != null;
    } catch (e) {
      // getAnyTransferData or unwrapNodes failed
      return false;
    }
  },

  /**
   * Gathers information about the selected nodes according to the following
   * rules:
   *    "link"              node is a URI
   *    "bookmark"          node is a bookmark
   *    "livemarkChild"     node is a child of a livemark
   *    "tagChild"          node is a child of a tag
   *    "folder"            node is a folder
   *    "query"             node is a query
   *    "separator"         node is a separator line
   *    "host"              node is a host
   *
   * @return an array of objects corresponding the selected nodes. Each
   *         object has each of the properties above set if its corresponding
   *         node matches the rule. In addition, the annotations names for each
   *         node are set on its corresponding object as properties.
   * Notes:
   *   1) This can be slow, so don't call it anywhere performance critical!
   */
  _buildSelectionMetadata: function PC__buildSelectionMetadata() {
    var metadata = [];
    var nodes = this._view.selectedNodes;

    for (var i = 0; i < nodes.length; i++) {
      var nodeData = {};
      var node = nodes[i];
      var nodeType = node.type;
      var uri = null;

      // We don't use the nodeIs* methods here to avoid going through the type
      // property way too often
      switch (nodeType) {
        case Ci.nsINavHistoryResultNode.RESULT_TYPE_QUERY:
          nodeData.query = true;
          if (node.parent) {
            switch (PlacesUtils.asQuery(node.parent).queryOptions.resultType) {
              case Ci.nsINavHistoryQueryOptions.RESULTS_AS_SITE_QUERY:
                nodeData.host = true;
                break;
              case Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_SITE_QUERY:
              case Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_QUERY:
                nodeData.day = true;
                break;
            }
          }
          break;
        case Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER:
        case Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT:
          nodeData.folder = true;
          break;
        case Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR:
          nodeData.separator = true;
          break;
        case Ci.nsINavHistoryResultNode.RESULT_TYPE_URI:
          nodeData.link = true;
          uri = Services.io.newURI(node.uri);
          if (PlacesUtils.nodeIsBookmark(node)) {
            nodeData.bookmark = true;
            var parentNode = node.parent;
            if (parentNode) {
              if (PlacesUtils.nodeIsTagQuery(parentNode))
                nodeData.tagChild = true;
              else if (this.hasCachedLivemarkInfo(parentNode))
                nodeData.livemarkChild = true;
            }
          }
          break;
      }

      // annotations
      if (uri) {
        let names = PlacesUtils.annotations.getPageAnnotationNames(uri);
        for (let j = 0; j < names.length; ++j)
          nodeData[names[j]] = true;
      }

      // For items also include the item-specific annotations
      if (node.itemId != -1) {
        let names = PlacesUtils.annotations
                               .getItemAnnotationNames(node.itemId);
        for (let j = 0; j < names.length; ++j)
          nodeData[names[j]] = true;
      }
      metadata.push(nodeData);
    }

    return metadata;
  },

  /**
   * Determines if a context-menu item should be shown
   * @param   aMenuItem
   *          the context menu item
   * @param   aMetaData
   *          meta data about the selection
   * @return true if the conditions (see buildContextMenu) are satisfied
   *         and the item can be displayed, false otherwise.
   */
  _shouldShowMenuItem: function PC__shouldShowMenuItem(aMenuItem, aMetaData) {
    if (aMenuItem.hasAttribute("hideifprivatebrowsing") && !PrivateBrowsingUtils.enabled) {
      return false;
    }

    var selectiontype = aMenuItem.getAttribute("selectiontype");
    if (!selectiontype) {
      selectiontype = "single|multiple";
    }
    var selectionTypes = selectiontype.split("|");
    if (selectionTypes.includes("any")) {
      return true;
    }
    var count = aMetaData.length;
    if (count > 1 && !selectionTypes.includes("multiple"))
      return false;
    if (count == 1 && !selectionTypes.includes("single"))
      return false;
    // NB: if there is no selection, we show the item if and only if
    // the selectiontype includes 'none' - the metadata list will be
    // empty so none of the other criteria will apply anyway.
    if (count == 0)
      return selectionTypes.includes("none");

    var forceHideAttr = aMenuItem.getAttribute("forcehideselection");
    if (forceHideAttr) {
      var forceHideRules = forceHideAttr.split("|");
      for (let i = 0; i < aMetaData.length; ++i) {
        for (let j = 0; j < forceHideRules.length; ++j) {
          if (forceHideRules[j] in aMetaData[i])
            return false;
        }
      }
    }

    var selectionAttr = aMenuItem.getAttribute("selection");
    if (!selectionAttr) {
      return !aMenuItem.hidden;
    }

    if (selectionAttr == "any")
      return true;

    var showRules = selectionAttr.split("|");
    var anyMatched = false;
    function metaDataNodeMatches(metaDataNode, rules) {
      for (var i = 0; i < rules.length; i++) {
        if (rules[i] in metaDataNode)
          return true;
      }
      return false;
    }

    for (var i = 0; i < aMetaData.length; ++i) {
      if (metaDataNodeMatches(aMetaData[i], showRules))
        anyMatched = true;
      else
        return false;
    }
    return anyMatched;
  },

  /**
   * Detects information (meta-data rules) about the current selection in the
   * view (see _buildSelectionMetadata) and sets the visibility state for each
   * of the menu-items in the given popup with the following rules applied:
   *  0) The "ignoreitem" attribute may be set to "true" for this code not to
   *     handle that menuitem.
   *  1) The "selectiontype" attribute may be set on a menu-item to "single"
   *     if the menu-item should be visible only if there is a single node
   *     selected, or to "multiple" if the menu-item should be visible only if
   *     multiple nodes are selected, or to "none" if the menuitems should be
   *     visible for if there are no selected nodes, or to a |-separated
   *     combination of these.
   *     If the attribute is not set or set to an invalid value, the menu-item
   *     may be visible irrespective of the selection.
   *  2) The "selection" attribute may be set on a menu-item to the various
   *     meta-data rules for which it may be visible. The rules should be
   *     separated with the | character.
   *  3) A menu-item may be visible only if at least one of the rules set in
   *     its selection attribute apply to each of the selected nodes in the
   *     view.
   *  4) The "forcehideselection" attribute may be set on a menu-item to rules
   *     for which it should be hidden. This attribute takes priority over the
   *     selection attribute. A menu-item would be hidden if at least one of the
   *     given rules apply to one of the selected nodes. The rules should be
   *     separated with the | character.
   *  5) The "hideifnoinsertionpoint" attribute may be set on a menu-item to
   *     true if it should be hidden when there's no insertion point
   *  6) The visibility state of a menu-item is unchanged if none of these
   *     attribute are set.
   *  7) These attributes should not be set on separators for which the
   *     visibility state is "auto-detected."
   *  8) The "hideifprivatebrowsing" attribute may be set on a menu-item to
   *     true if it should be hidden inside the private browsing mode
   * @param   aPopup
   *          The menupopup to build children into.
   * @return true if at least one item is visible, false otherwise.
   */
  buildContextMenu: function PC_buildContextMenu(aPopup) {
    var metadata = this._buildSelectionMetadata();
    var ip = this._view.insertionPoint;
    var noIp = !ip || ip.isTag;

    var separator = null;
    var visibleItemsBeforeSep = false;
    var usableItemCount = 0;
    for (var i = 0; i < aPopup.childNodes.length; ++i) {
      var item = aPopup.childNodes[i];
      if (item.getAttribute("ignoreitem") == "true") {
        continue;
      }
      if (item.localName != "menuseparator") {
        // We allow pasting into tag containers, so special case that.
        var hideIfNoIP = item.getAttribute("hideifnoinsertionpoint") == "true" &&
                         noIp && !(ip && ip.isTag && item.id == "placesContext_paste");
        var hideIfPrivate = item.getAttribute("hideifprivatebrowsing") == "true" &&
                            PrivateBrowsingUtils.isWindowPrivate(window);
        var shouldHideItem = hideIfNoIP || hideIfPrivate ||
                             !this._shouldShowMenuItem(item, metadata);
        item.hidden = item.disabled = shouldHideItem;

        if (!item.hidden) {
          visibleItemsBeforeSep = true;
          usableItemCount++;

          // Show the separator above the menu-item if any
          if (separator) {
            separator.hidden = false;
            separator = null;
          }
        }
      } else { // menuseparator
        // Initially hide it. It will be unhidden if there will be at least one
        // visible menu-item above and below it.
        item.hidden = true;

        // We won't show the separator at all if no items are visible above it
        if (visibleItemsBeforeSep)
          separator = item;

        // New separator, count again:
        visibleItemsBeforeSep = false;
      }
    }

    // Set Open Folder/Links In Tabs items enabled state if they're visible
    if (usableItemCount > 0) {
      var openContainerInTabsItem = document.getElementById("placesContext_openContainer:tabs");
      if (!openContainerInTabsItem.hidden) {
        var containerToUse = this._view.selectedNode || this._view.result.root;
        if (PlacesUtils.nodeIsContainer(containerToUse)) {
          if (!PlacesUtils.hasChildURIs(containerToUse)) {
            openContainerInTabsItem.disabled = true;
            // Ensure that we don't display the menu if nothing is enabled:
            usableItemCount--;
          }
        }
      }
    }

    // Make sure to display the correct string when multiple pages are selected.
    let stringId = metadata.length === 1 ? "SinglePage" : "MultiplePages";

    let deleteHistoryItem = document.getElementById("placesContext_delete_history");
    deleteHistoryItem.label = PlacesUIUtils.getString(`cmd.delete${stringId}.label`);
    deleteHistoryItem.accessKey = PlacesUIUtils.getString(`cmd.delete${stringId}.accesskey`);

    let createBookmarkItem = document.getElementById("placesContext_createBookmark");
    createBookmarkItem.label = PlacesUIUtils.getString(`cmd.bookmark${stringId}.label`);
    createBookmarkItem.accessKey = PlacesUIUtils.getString(`cmd.bookmark${stringId}.accesskey`);

    return usableItemCount > 0;
  },

  /**
   * Select all links in the current view.
   */
  selectAll: function PC_selectAll() {
    this._view.selectAll();
  },

  /**
   * Opens the bookmark properties for the selected URI Node.
   */
  showBookmarkPropertiesForSelection() {
    let node = this._view.selectedNode;
    if (!node)
      return;

    PlacesUIUtils.showBookmarkDialog({ action: "edit",
                                       node,
                                       hiddenRows: [ "folderPicker" ]
                                     }, window.top);
  },

  /**
   * This method can be run on a URI parameter to ensure that it didn't
   * receive a string instead of an nsIURI object.
   */
  _assertURINotString: function PC__assertURINotString(value) {
    NS_ASSERT((typeof(value) == "object") && !(value instanceof String),
           "This method should be passed a URI as a nsIURI object, not as a string.");
  },

  /**
   * Reloads the selected livemark if any.
   */
  reloadSelectedLivemark: function PC_reloadSelectedLivemark() {
    var selectedNode = this._view.selectedNode;
    if (selectedNode) {
      let itemId = selectedNode.itemId;
      PlacesUtils.livemarks.getLivemark({ id: itemId })
        .then(aLivemark => {
          aLivemark.reload(true);
        }, Cu.reportError);
    }
  },

  /**
   * Opens the links in the selected folder, or the selected links in new tabs.
   */
  openSelectionInTabs: function PC_openLinksInTabs(aEvent) {
    var node = this._view.selectedNode;
    var nodes = this._view.selectedNodes;
    // In the case of no selection, open the root node:
    if (!node && !nodes.length) {
      node = this._view.result.root;
    }
    if (node && PlacesUtils.nodeIsContainer(node))
      PlacesUIUtils.openContainerNodeInTabs(node, aEvent, this._view);
    else
      PlacesUIUtils.openURINodesInTabs(nodes, aEvent, this._view);
  },

  /**
   * Shows the Add Bookmark UI for the current insertion point.
   *
   * @param aType
   *        the type of the new item (bookmark/livemark/folder)
   */
  async newItem(aType) {
    let ip = this._view.insertionPoint;
    if (!ip)
      throw Cr.NS_ERROR_NOT_AVAILABLE;

    let performed =
      PlacesUIUtils.showBookmarkDialog({ action: "add",
                                         type: aType,
                                         defaultInsertionPoint: ip,
                                         hiddenRows: [ "folderPicker" ]
                                       }, window.top);
    if (performed) {
      // Select the new item.
      // TODO (Bug 1425555): When we remove places transactions, we might be
      // able to improve showBookmarkDialog to return the guid direct, and
      // avoid the fetch.
      let insertedNode = await PlacesUtils.bookmarks.fetch({
        parentGuid: ip.guid,
        index: await ip.getIndex()
      });

      this._view.selectItems([insertedNode.guid], false);
    }
  },

  /**
   * Create a new Bookmark separator somewhere.
   */
  async newSeparator() {
    var ip = this._view.insertionPoint;
    if (!ip)
      throw Cr.NS_ERROR_NOT_AVAILABLE;

    let index = await ip.getIndex();
    let txn = PlacesTransactions.NewSeparator({ parentGuid: ip.guid, index });
    let guid = await txn.transact();
    // Select the new item.
    this._view.selectItems([guid], false);
  },

  /**
   * Sort the selected folder by name
   */
  async sortFolderByName() {
    let guid = PlacesUtils.getConcreteItemGuid(this._view.selectedNode);
    await PlacesTransactions.SortByName(guid).transact();
  },

  /**
   * Walk the list of folders we're removing in this delete operation, and
   * see if the selected node specified is already implicitly being removed
   * because it is a child of that folder.
   * @param   node
   *          Node to check for containment.
   * @param   pastFolders
   *          List of folders the calling function has already traversed
   * @return true if the node should be skipped, false otherwise.
   */
  _shouldSkipNode: function PC_shouldSkipNode(node, pastFolders) {
    /**
     * Determines if a node is contained by another node within a resultset.
     * @param   node
     *          The node to check for containment for
     * @param   parent
     *          The parent container to check for containment in
     * @return true if node is a member of parent's children, false otherwise.
     */
    function isNodeContainedBy(parent) {
      var cursor = node.parent;
      while (cursor) {
        if (cursor == parent)
          return true;
        cursor = cursor.parent;
      }
      return false;
    }

    for (var j = 0; j < pastFolders.length; ++j) {
      if (isNodeContainedBy(pastFolders[j]))
        return true;
    }
    return false;
  },

  /**
   * Creates a set of transactions for the removal of a range of items.
   * A range is an array of adjacent nodes in a view.
   * @param   [in] range
   *          An array of nodes to remove. Should all be adjacent.
   * @param   [out] transactions
   *          An array of transactions.
   * @param   [optional] removedFolders
   *          An array of folder nodes that have already been removed.
   * @return {Integer} The total number of items affected.
   */
  async _removeRange(range, transactions, removedFolders) {
    NS_ASSERT(transactions instanceof Array, "Must pass a transactions array");
    if (!removedFolders)
      removedFolders = [];

    let bmGuidsToRemove = [];
    let totalItems = 0;

    for (var i = 0; i < range.length; ++i) {
      var node = range[i];
      if (this._shouldSkipNode(node, removedFolders))
        continue;

      totalItems++;

      if (PlacesUtils.nodeIsTagQuery(node.parent)) {
        // This is a uri node inside a tag container.  It needs a special
        // untag transaction.
        let tag = node.parent.title;
        if (!tag) {
          // TODO: Bug 1432405 Try using getConcreteItemGuid.
          let tagItemId = PlacesUtils.getConcreteItemId(node.parent);
          let tagGuid = await PlacesUtils.promiseItemGuid(tagItemId);
          tag = (await PlacesUtils.bookmarks.fetch(tagGuid)).title;
        }
        transactions.push(PlacesTransactions.Untag({ urls: [node.uri], tag }));
      } else if (PlacesUtils.nodeIsTagQuery(node) && node.parent &&
               PlacesUtils.nodeIsQuery(node.parent) &&
               PlacesUtils.asQuery(node.parent).queryOptions.resultType ==
                 Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_QUERY) {
        // This is a tag container.
        // Untag all URIs tagged with this tag only if the tag container is
        // child of the "Tags" query in the library, in all other places we
        // must only remove the query node.
        let tag = node.title;
        let URIs = PlacesUtils.tagging.getURIsForTag(tag);
        transactions.push(PlacesTransactions.Untag({ tag, urls: URIs }));
      } else if (PlacesUtils.nodeIsURI(node) &&
               PlacesUtils.nodeIsQuery(node.parent) &&
               PlacesUtils.asQuery(node.parent).queryOptions.queryType ==
                 Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY) {
        // This is a uri node inside an history query.
        PlacesUtils.history.remove(node.uri).catch(Cu.reportError);
        // History deletes are not undoable, so we don't have a transaction.
      } else if (node.itemId == -1 &&
               PlacesUtils.nodeIsQuery(node) &&
               PlacesUtils.asQuery(node).queryOptions.queryType ==
                 Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY) {
        // This is a dynamically generated history query, like queries
        // grouped by site, time or both.  Dynamically generated queries don't
        // have an itemId even if they are descendants of a bookmark.
        this._removeHistoryContainer(node);
        // History deletes are not undoable, so we don't have a transaction.
      } else {
        // This is a common bookmark item.
        if (PlacesUtils.nodeIsFolder(node)) {
          // If this is a folder we add it to our array of folders, used
          // to skip nodes that are children of an already removed folder.
          removedFolders.push(node);
        }
        bmGuidsToRemove.push(node.bookmarkGuid);
      }
    }
    if (bmGuidsToRemove.length) {
      transactions.push(PlacesTransactions.Remove({ guids: bmGuidsToRemove }));
    }
    return totalItems;
  },

  /**
   * Removes the set of selected ranges from bookmarks.
   * @param   txnName
   *          See |remove|.
   */
  async _removeRowsFromBookmarks(txnName) {
    let ranges = this._view.removableSelectionRanges;
    let transactions = [];
    let removedFolders = [];
    let totalItems = 0;

    for (let range of ranges) {
      totalItems += await this._removeRange(range, transactions, removedFolders);
    }

    if (transactions.length > 0) {
      await PlacesUIUtils.batchUpdatesForNode(this._view.result, totalItems, async () => {
        await PlacesTransactions.batch(transactions);
      });
    }
  },

  /**
   * Removes the set of selected ranges from history, asynchronously.
   *
   * @note history deletes are not undoable.
   */
  _removeRowsFromHistory: function PC__removeRowsFromHistory() {
    let nodes = this._view.selectedNodes;
    let URIs = new Set();
    for (let i = 0; i < nodes.length; ++i) {
      let node = nodes[i];
      if (PlacesUtils.nodeIsURI(node)) {
        URIs.add(node.uri);
      } else if (PlacesUtils.nodeIsQuery(node) &&
               PlacesUtils.asQuery(node).queryOptions.queryType ==
                 Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY) {
        this._removeHistoryContainer(node);
      }
    }

    PlacesUtils.history.remove([...URIs]).catch(Cu.reportError);
  },

  /**
   * Removes history visits for an history container node.
   * @param   [in] aContainerNode
   *          The container node to remove.
   *
   * @note history deletes are not undoable.
   */
  _removeHistoryContainer: function PC__removeHistoryContainer(aContainerNode) {
    if (PlacesUtils.nodeIsHost(aContainerNode)) {
      // Site container.
      PlacesUtils.history.removePagesFromHost(aContainerNode.title, true);
    } else if (PlacesUtils.nodeIsDay(aContainerNode)) {
      // Day container.
      let query = aContainerNode.getQueries()[0];
      let beginTime = query.beginTime;
      let endTime = query.endTime;
      NS_ASSERT(query && beginTime && endTime,
                "A valid date container query should exist!");
      // We want to exclude beginTime from the removal because
      // removePagesByTimeframe includes both extremes, while date containers
      // exclude the lower extreme.  So, if we would not exclude it, we would
      // end up removing more history than requested.
      PlacesUtils.history.removePagesByTimeframe(beginTime + 1, endTime);
    }
  },

  /**
   * Removes the selection
   * @param   aTxnName
   *          A name for the transaction if this is being performed
   *          as part of another operation.
   */
  async remove(aTxnName) {
    if (!this._hasRemovableSelection())
      return;

    NS_ASSERT(aTxnName !== undefined, "Must supply Transaction Name");

    var root = this._view.result.root;

    if (PlacesUtils.nodeIsFolder(root)) {
      await this._removeRowsFromBookmarks(aTxnName);
    } else if (PlacesUtils.nodeIsQuery(root)) {
      var queryType = PlacesUtils.asQuery(root).queryOptions.queryType;
      if (queryType == Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS) {
        await this._removeRowsFromBookmarks(aTxnName);
      } else if (queryType == Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY) {
        this._removeRowsFromHistory();
      } else {
        NS_ASSERT(false, "implement support for QUERY_TYPE_UNIFIED");
      }
    } else
      NS_ASSERT(false, "unexpected root");
  },

  /**
   * Fills a DataTransfer object with the content of the selection that can be
   * dropped elsewhere.
   * @param   aEvent
   *          The dragstart event.
   */
  setDataTransfer: function PC_setDataTransfer(aEvent) {
    let dt = aEvent.dataTransfer;

    let result = this._view.result;
    let didSuppressNotifications = result.suppressNotifications;
    if (!didSuppressNotifications)
      result.suppressNotifications = true;

    function addData(type, index, feedURI) {
      let wrapNode = PlacesUtils.wrapNode(node, type, feedURI);
      dt.mozSetDataAt(type, wrapNode, index);
    }

    function addURIData(index, feedURI) {
      addData(PlacesUtils.TYPE_X_MOZ_URL, index, feedURI);
      addData(PlacesUtils.TYPE_UNICODE, index, feedURI);
      addData(PlacesUtils.TYPE_HTML, index, feedURI);
    }

    try {
      let nodes = this._view.draggableSelection;
      for (let i = 0; i < nodes.length; ++i) {
        var node = nodes[i];

        // This order is _important_! It controls how this and other
        // applications select data to be inserted based on type.
        addData(PlacesUtils.TYPE_X_MOZ_PLACE, i);

        // Drop the feed uri for livemark containers
        let livemarkInfo = this.getCachedLivemarkInfo(node);
        if (livemarkInfo) {
          addURIData(i, livemarkInfo.feedURI.spec);
        } else if (node.uri) {
          addURIData(i);
        }
      }
    } finally {
      if (!didSuppressNotifications)
        result.suppressNotifications = false;
    }
  },

  get clipboardAction() {
    let action = {};
    let actionOwner;
    try {
      let xferable = Cc["@mozilla.org/widget/transferable;1"].
                     createInstance(Ci.nsITransferable);
      xferable.init(null);
      xferable.addDataFlavor(PlacesUtils.TYPE_X_MOZ_PLACE_ACTION);
      this.clipboard.getData(xferable, Ci.nsIClipboard.kGlobalClipboard);
      xferable.getTransferData(PlacesUtils.TYPE_X_MOZ_PLACE_ACTION, action, {});
      [action, actionOwner] =
        action.value.QueryInterface(Ci.nsISupportsString).data.split(",");
    } catch (ex) {
      // Paste from external sources don't have any associated action, just
      // fallback to a copy action.
      return "copy";
    }
    // For cuts also check who inited the action, since cuts across different
    // instances should instead be handled as copies (The sources are not
    // available for this instance).
    if (action == "cut" && actionOwner != this.profileName)
      action = "copy";

    return action;
  },

  _releaseClipboardOwnership: function PC__releaseClipboardOwnership() {
    if (this.cutNodes.length > 0) {
      // This clears the logical clipboard, doesn't remove data.
      this.clipboard.emptyClipboard(Ci.nsIClipboard.kGlobalClipboard);
    }
  },

  _clearClipboard: function PC__clearClipboard() {
    let xferable = Cc["@mozilla.org/widget/transferable;1"].
                   createInstance(Ci.nsITransferable);
    xferable.init(null);
    // Empty transferables may cause crashes, so just add an unknown type.
    const TYPE = "text/x-moz-place-empty";
    xferable.addDataFlavor(TYPE);
    xferable.setTransferData(TYPE, PlacesUtils.toISupportsString(""), 0);
    this.clipboard.setData(xferable, null, Ci.nsIClipboard.kGlobalClipboard);
  },

  _populateClipboard: function PC__populateClipboard(aNodes, aAction) {
    // This order is _important_! It controls how this and other applications
    // select data to be inserted based on type.
    let contents = [
      { type: PlacesUtils.TYPE_X_MOZ_PLACE, entries: [] },
      { type: PlacesUtils.TYPE_X_MOZ_URL, entries: [] },
      { type: PlacesUtils.TYPE_HTML, entries: [] },
      { type: PlacesUtils.TYPE_UNICODE, entries: [] },
    ];

    // Avoid handling descendants of a copied node, the transactions take care
    // of them automatically.
    let copiedFolders = [];
    aNodes.forEach(function(node) {
      if (this._shouldSkipNode(node, copiedFolders))
        return;
      if (PlacesUtils.nodeIsFolder(node))
        copiedFolders.push(node);

      let livemarkInfo = this.getCachedLivemarkInfo(node);
      let feedURI = livemarkInfo && livemarkInfo.feedURI.spec;

      contents.forEach(function(content) {
        content.entries.push(
          PlacesUtils.wrapNode(node, content.type, feedURI)
        );
      });
    }, this);

    function addData(type, data) {
      xferable.addDataFlavor(type);
      xferable.setTransferData(type, PlacesUtils.toISupportsString(data),
                               data.length * 2);
    }

    let xferable = Cc["@mozilla.org/widget/transferable;1"].
                   createInstance(Ci.nsITransferable);
    xferable.init(null);
    let hasData = false;
    // This order matters here!  It controls how this and other applications
    // select data to be inserted based on type.
    contents.forEach(function(content) {
      if (content.entries.length > 0) {
        hasData = true;
        let glue =
          content.type == PlacesUtils.TYPE_X_MOZ_PLACE ? "," : PlacesUtils.endl;
        addData(content.type, content.entries.join(glue));
      }
    });

    // Track the exected action in the xferable.  This must be the last flavor
    // since it's the least preferred one.
    // Enqueue a unique instance identifier to distinguish operations across
    // concurrent instances of the application.
    addData(PlacesUtils.TYPE_X_MOZ_PLACE_ACTION, aAction + "," + this.profileName);

    if (hasData) {
      this.clipboard.setData(xferable,
                             this.cutNodes.length > 0 ? this : null,
                             Ci.nsIClipboard.kGlobalClipboard);
    }
  },

  _cutNodes: [],
  get cutNodes() {
    return this._cutNodes;
  },
  set cutNodes(aNodes) {
    let self = this;
    function updateCutNodes(aValue) {
      self._cutNodes.forEach(function(aNode) {
        self._view.toggleCutNode(aNode, aValue);
      });
    }

    updateCutNodes(false);
    this._cutNodes = aNodes;
    updateCutNodes(true);
    return aNodes;
  },

  /**
   * Copy Bookmarks and Folders to the clipboard
   */
  copy: function PC_copy() {
    let result = this._view.result;
    let didSuppressNotifications = result.suppressNotifications;
    if (!didSuppressNotifications)
      result.suppressNotifications = true;
    try {
      this._populateClipboard(this._view.selectedNodes, "copy");
    } finally {
      if (!didSuppressNotifications)
        result.suppressNotifications = false;
    }
  },

  /**
   * Cut Bookmarks and Folders to the clipboard
   */
  cut: function PC_cut() {
    let result = this._view.result;
    let didSuppressNotifications = result.suppressNotifications;
    if (!didSuppressNotifications)
      result.suppressNotifications = true;
    try {
      this._populateClipboard(this._view.selectedNodes, "cut");
      this.cutNodes = this._view.selectedNodes;
    } finally {
      if (!didSuppressNotifications)
        result.suppressNotifications = false;
    }
  },

  /**
   * Paste Bookmarks and Folders from the clipboard
   */
  async paste() {
    // No reason to proceed if there isn't a valid insertion point.
    let ip = this._view.insertionPoint;
    if (!ip)
      throw Cr.NS_ERROR_NOT_AVAILABLE;

    let action = this.clipboardAction;

    let xferable = Cc["@mozilla.org/widget/transferable;1"].
                   createInstance(Ci.nsITransferable);
    xferable.init(null);
    // This order matters here!  It controls the preferred flavors for this
    // paste operation.
    [ PlacesUtils.TYPE_X_MOZ_PLACE,
      PlacesUtils.TYPE_X_MOZ_URL,
      PlacesUtils.TYPE_UNICODE,
    ].forEach(type => xferable.addDataFlavor(type));

    this.clipboard.getData(xferable, Ci.nsIClipboard.kGlobalClipboard);

    // Now get the clipboard contents, in the best available flavor.
    let data = {}, type = {}, items = [];
    try {
      xferable.getAnyTransferData(type, data, {});
      data = data.value.QueryInterface(Ci.nsISupportsString).data;
      type = type.value;
      items = PlacesUtils.unwrapNodes(data, type);
    } catch (ex) {
      // No supported data exists or nodes unwrap failed, just bail out.
      return;
    }

    let doCopy = action == "copy";
    let itemsToSelect = await PlacesUIUtils.handleTransferItems(items, ip, doCopy, this._view);

    // Cut/past operations are not repeatable, so clear the clipboard.
    if (action == "cut") {
      this._clearClipboard();
    }

    if (itemsToSelect.length > 0)
      this._view.selectItems(itemsToSelect, false);
  },

  /**
   * Cache the livemark info for a node.  This allows the controller and the
   * views to treat the given node as a livemark.
   * @param aNode
   *        a places result node.
   * @param aLivemarkInfo
   *        a mozILivemarkInfo object.
   */
  cacheLivemarkInfo: function PC_cacheLivemarkInfo(aNode, aLivemarkInfo) {
    this._cachedLivemarkInfoObjects.set(aNode, aLivemarkInfo);
  },

  /**
   * Returns whether or not there's cached mozILivemarkInfo object for a node.
   * @param aNode
   *        a places result node.
   * @return true if there's a cached mozILivemarkInfo object for
   *         aNode, false otherwise.
   */
  hasCachedLivemarkInfo: function PC_hasCachedLivemarkInfo(aNode) {
    return this._cachedLivemarkInfoObjects.has(aNode);
  },

  /**
   * Returns the cached livemark info for a node, if set by cacheLivemarkInfo,
   * null otherwise.
   * @param aNode
   *        a places result node.
   * @return the mozILivemarkInfo object for aNode, if set, null otherwise.
   */
  getCachedLivemarkInfo: function PC_getCachedLivemarkInfo(aNode) {
    return this._cachedLivemarkInfoObjects.get(aNode, null);
  },

  /**
   * Checks if we can insert into a container.
   * @param   container
   *          The container were we are want to drop
   */
  disallowInsertion(container) {
    NS_ASSERT(container, "empty container");
    // Allow dropping into Tag containers and editable folders.
    return !PlacesUtils.nodeIsTagQuery(container) &&
           (!PlacesUtils.nodeIsFolder(container) ||
            PlacesUIUtils.isFolderReadOnly(container, this._view));
  },

  /**
   * Determines if a node can be moved.
   *
   * @param   aNode
   *          A nsINavHistoryResultNode node.
   * @return True if the node can be moved, false otherwise.
   */
  canMoveNode(node) {
    // Only bookmark items are movable.
    if (node.itemId == -1)
      return false;

    // Once tags and bookmarked are divorced, the tag-query check should be
    // removed.
    let parentNode = node.parent;
    return parentNode != null &&
           PlacesUtils.nodeIsFolder(parentNode) &&
           !PlacesUIUtils.isFolderReadOnly(parentNode, this._view) &&
           !PlacesUtils.nodeIsTagQuery(parentNode);
  },
};

/**
 * Handles drag and drop operations for views. Note that this is view agnostic!
 * You should not use PlacesController._view within these methods, since
 * the view that the item(s) have been dropped on was not necessarily active.
 * Drop functions are passed the view that is being dropped on.
 */
var PlacesControllerDragHelper = {
  /**
   * DOM Element currently being dragged over
   */
  currentDropTarget: null,

  /**
   * Determines if the mouse is currently being dragged over a child node of
   * this menu. This is necessary so that the menu doesn't close while the
   * mouse is dragging over one of its submenus
   * @param   node
   *          The container node
   * @return true if the user is dragging over a node within the hierarchy of
   *         the container, false otherwise.
   */
  draggingOverChildNode: function PCDH_draggingOverChildNode(node) {
    let currentNode = this.currentDropTarget;
    while (currentNode) {
      if (currentNode == node)
        return true;
      currentNode = currentNode.parentNode;
    }
    return false;
  },

  /**
   * @return The current active drag session. Returns null if there is none.
   */
  getSession: function PCDH__getSession() {
    return this.dragService.getCurrentSession();
  },

  /**
   * Extract the first accepted flavor from a list of flavors.
   * @param aFlavors
   *        The flavors list of type DOMStringList.
   */
  getFirstValidFlavor: function PCDH_getFirstValidFlavor(aFlavors) {
    for (let i = 0; i < aFlavors.length; i++) {
      if (PlacesUIUtils.SUPPORTED_FLAVORS.includes(aFlavors[i]))
        return aFlavors[i];
    }

    // If no supported flavor is found, check if data includes text/plain
    // contents.  If so, request them as text/unicode, a conversion will happen
    // automatically.
    if (aFlavors.contains("text/plain")) {
        return PlacesUtils.TYPE_UNICODE;
    }

    return null;
  },

  /**
   * Determines whether or not the data currently being dragged can be dropped
   * on a places view.
   * @param ip
   *        The insertion point where the items should be dropped.
   */
  canDrop: function PCDH_canDrop(ip, dt) {
    let dropCount = dt.mozItemCount;

    // Check every dragged item.
    for (let i = 0; i < dropCount; i++) {
      let flavor = this.getFirstValidFlavor(dt.mozTypesAt(i));
      if (!flavor)
        return false;

      // Urls can be dropped on any insertionpoint.
      // XXXmano: remember that this method is called for each dragover event!
      // Thus we shouldn't use unwrapNodes here at all if possible.
      // I think it would be OK to accept bogus data here (e.g. text which was
      // somehow wrapped as TAB_DROP_TYPE, this is not in our control, and
      // will just case the actual drop to be a no-op), and only rule out valid
      // expected cases, which are either unsupported flavors, or items which
      // cannot be dropped in the current insertionpoint. The last case will
      // likely force us to use unwrapNodes for the private data types of
      // places.
      if (flavor == TAB_DROP_TYPE)
        continue;

      let data = dt.mozGetDataAt(flavor, i);
      let nodes;
      try {
        nodes = PlacesUtils.unwrapNodes(data, flavor);
      } catch (e) {
        return false;
      }

      for (let dragged of nodes) {
        // Only bookmarks and urls can be dropped into tag containers.
        if (ip.isTag &&
            dragged.type != PlacesUtils.TYPE_X_MOZ_URL &&
            (dragged.type != PlacesUtils.TYPE_X_MOZ_PLACE ||
             (dragged.uri && dragged.uri.startsWith("place:")) ))
          return false;

        // The following loop disallows the dropping of a folder on itself or
        // on any of its descendants.
        if (dragged.type == PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER ||
            (dragged.uri && dragged.uri.startsWith("place:")) ) {
          let parentId = ip.itemId;
          while (parentId != PlacesUtils.placesRootId) {
            if (dragged.concreteId == parentId || dragged.id == parentId)
              return false;
            parentId = PlacesUtils.bookmarks.getFolderIdForItem(parentId);
          }
        }
      }
    }
    return true;
  },

  /**
   * Handles the drop of one or more items onto a view.
   *
   * @param {Object} insertionPoint The insertion point where the items should
   *                                be dropped.
   * @param {Object} dt             The dataTransfer information for the drop.
   * @param {Object} view           The view or the tree element. This allows
   *                                batching to take place.
   */
  async onDrop(insertionPoint, dt, view) {
    let doCopy = ["copy", "link"].includes(dt.dropEffect);

    let dropCount = dt.mozItemCount;

    // Following flavors may contain duplicated data.
    let duplicable = new Map();
    duplicable.set(PlacesUtils.TYPE_UNICODE, new Set());
    duplicable.set(PlacesUtils.TYPE_X_MOZ_URL, new Set());

    // Collect all data from the DataTransfer before processing it, as the
    // DataTransfer is only valid during the synchronous handling of the `drop`
    // event handler callback.
    let nodes = [];
    for (let i = 0; i < dropCount; ++i) {
      let flavor = this.getFirstValidFlavor(dt.mozTypesAt(i));
      if (!flavor)
        return;

      let data = dt.mozGetDataAt(flavor, i);
      if (duplicable.has(flavor)) {
        let handled = duplicable.get(flavor);
        if (handled.has(data))
          continue;
        handled.add(data);
      }

      if (flavor != TAB_DROP_TYPE) {
        nodes = [...nodes, ...PlacesUtils.unwrapNodes(data, flavor)];
      } else if (data instanceof XULElement && data.localName == "tab" &&
               data.ownerGlobal.isChromeWindow) {
        let uri = data.linkedBrowser.currentURI;
        let spec = uri ? uri.spec : "about:blank";
        nodes.push({
          uri: spec,
          title: data.label,
          type: PlacesUtils.TYPE_X_MOZ_URL
        });
      } else {
        throw new Error("bogus data was passed as a tab");
      }
    }

    await PlacesUIUtils.handleTransferItems(nodes, insertionPoint, doCopy, view);
  },
};

XPCOMUtils.defineLazyServiceGetter(PlacesControllerDragHelper, "dragService",
                                   "@mozilla.org/widget/dragservice;1",
                                   "nsIDragService");
PK
!<�1}ff>chrome/browser/content/browser/places/downloadsViewOverlay.xul<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<?xul-overlay href="chrome://browser/content/downloads/allDownloadsViewOverlay.xul"?>

<!DOCTYPE overlay [
<!ENTITY % downloadsDTD SYSTEM "chrome://browser/locale/downloads/downloads.dtd">
%downloadsDTD;
]>

<overlay id="downloadsViewOverlay"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

  <script type="application/javascript"><![CDATA[
    const DOWNLOADS_QUERY = "place:transition=" +
      Ci.nsINavHistoryService.TRANSITION_DOWNLOAD +
      "&sort=" +
      Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING;

    ContentArea.setContentViewForQueryString(DOWNLOADS_QUERY,
      () => new DownloadsPlacesView(document.getElementById("downloadsRichListBox"), false),
      { showDetailsPane: false,
        toolbarSet: "back-button, forward-button, organizeButton, clearDownloadsButton, libraryToolbarSpacer, searchFilter" });
  ]]></script>

  <window id="places">
    <commandset id="downloadCommands"/>
    <menupopup id="downloadsContextMenu"/>
  </window>

  <deck id="placesViewsDeck">
    <richlistbox id="downloadsRichListBox"/>
  </deck>

  <toolbar id="placesToolbar">
    <toolbarbutton id="clearDownloadsButton"
                   insertbefore="libraryToolbarSpacer"
                   label="&clearDownloadsButton.label;"
                   command="downloadsCmd_clearDownloads"
                   tooltiptext="&clearDownloadsButton.tooltip;"/>
  </toolbar>

</overlay>
PK
!<AE
�
�<chrome/browser/content/browser/places/editBookmarkOverlay.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");

const LAST_USED_ANNO = "bookmarkPropertiesDialog/folderLastUsed";
const MAX_FOLDER_ITEM_IN_MENU_LIST = 5;

var gEditItemOverlay = {
  _observersAdded: false,
  _staticFoldersListBuilt: false,

  _paneInfo: null,
  _setPaneInfo(aInitInfo) {
    if (!aInitInfo)
      return this._paneInfo = null;

    if ("uris" in aInitInfo && "node" in aInitInfo)
      throw new Error("ambiguous pane info");
    if (!("uris" in aInitInfo) && !("node" in aInitInfo))
      throw new Error("Neither node nor uris set for pane info");

    // We either pass a node or uris.
    let node = "node" in aInitInfo ? aInitInfo.node : null;

    // Since there's no true UI for folder shortcuts (they show up just as their target
    // folders), when the pane shows for them it's opened in read-only mode, showing the
    // properties of the target folder.
    let itemId = node ? node.itemId : -1;
    let itemGuid = node ? PlacesUtils.getConcreteItemGuid(node) : null;
    let isItem = itemId != -1;
    let isFolderShortcut = isItem &&
                           node.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT;
    let isTag = node && PlacesUtils.nodeIsTagQuery(node);
    if (isTag) {
      itemId = PlacesUtils.getConcreteItemId(node);
      // For now we don't have access to the item guid synchronously for tags,
      // so we'll need to fetch it later.
    }
    let isURI = node && PlacesUtils.nodeIsURI(node);
    let uri = isURI ? Services.io.newURI(node.uri) : null;
    let title = node ? node.title : null;
    let isBookmark = isItem && isURI;
    let bulkTagging = !node;
    let uris = bulkTagging ? aInitInfo.uris : null;
    let visibleRows = new Set();
    let isParentReadOnly = false;
    let postData = aInitInfo.postData;
    let parentId = -1;
    let parentGuid = null;

    if (node && isItem) {
      if (!node.parent || (node.parent.itemId > 0 && !node.parent.bookmarkGuid)) {
        throw new Error("Cannot use an incomplete node to initialize the edit bookmark panel");
      }
      let parent = node.parent;
      isParentReadOnly = !PlacesUtils.nodeIsFolder(parent);
      if (!isParentReadOnly) {
        let folderId = PlacesUtils.getConcreteItemId(parent);
        isParentReadOnly = folderId == PlacesUtils.placesRootId ||
                           (!("get" in Object.getOwnPropertyDescriptor(PlacesUIUtils, "leftPaneFolderId")) &&
                            (folderId == PlacesUIUtils.leftPaneFolderId));
      }
      parentId = parent.itemId;
      parentGuid = parent.bookmarkGuid;
    }

    let focusedElement = aInitInfo.focusedElement;
    let onPanelReady = aInitInfo.onPanelReady;

    return this._paneInfo = { itemId, itemGuid, parentId, parentGuid, isItem,
                              isURI, uri, title,
                              isBookmark, isFolderShortcut, isParentReadOnly,
                              bulkTagging, uris,
                              visibleRows, postData, isTag, focusedElement,
                              onPanelReady };
  },

  get initialized() {
    return this._paneInfo != null;
  },

  // Backwards-compatibility getters
  get itemId() {
    if (!this.initialized || this._paneInfo.bulkTagging)
      return -1;
    return this._paneInfo.itemId;
  },

  get uri() {
    if (!this.initialized)
      return null;
    if (this._paneInfo.bulkTagging)
      return this._paneInfo.uris[0];
    return this._paneInfo.uri;
  },

  get multiEdit() {
    return this.initialized && this._paneInfo.bulkTagging;
  },

  // Check if the pane is initialized to show only read-only fields.
  get readOnly() {
    // TODO (Bug 1120314): Folder shortcuts are currently read-only due to some
    // quirky implementation details (the most important being the "smart"
    // semantics of node.title that makes hard to edit the right entry).
    // This pane is read-only if:
    //  * the panel is not initialized
    //  * the node is a folder shortcut
    //  * the node is not bookmarked and not a tag container
    //  * the node is child of a read-only container and is not a bookmarked
    //    URI nor a tag container
    return !this.initialized ||
           this._paneInfo.isFolderShortcut ||
           (!this._paneInfo.isItem && !this._paneInfo.isTag) ||
           (this._paneInfo.isParentReadOnly && !this._paneInfo.isBookmark && !this._paneInfo.isTag);
  },

  // the first field which was edited after this panel was initialized for
  // a certain item
  _firstEditedField: "",

  _initNamePicker() {
    if (this._paneInfo.bulkTagging)
      throw new Error("_initNamePicker called unexpectedly");

    // title may by null, which, for us, is the same as an empty string.
    this._initTextField(this._namePicker, this._paneInfo.title || "");
  },

  _initLocationField() {
    if (!this._paneInfo.isURI)
      throw new Error("_initLocationField called unexpectedly");
    this._initTextField(this._locationField, this._paneInfo.uri.spec);
  },

  _initDescriptionField() {
    if (!this._paneInfo.isItem)
      throw new Error("_initDescriptionField called unexpectedly");

    this._initTextField(this._descriptionField,
                        PlacesUIUtils.getItemDescription(this._paneInfo.itemId));
  },

  async _initKeywordField(newKeyword = "") {
    if (!this._paneInfo.isBookmark) {
      throw new Error("_initKeywordField called unexpectedly");
    }

    // Reset the field status synchronously now, eventually we'll reinit it
    // later if we find an existing keyword. This way we can ensure to be in a
    // consistent status when reusing the panel across different bookmarks.
    this._keyword = newKeyword;
    this._initTextField(this._keywordField, newKeyword);

    if (!newKeyword) {
      let entries = [];
      await PlacesUtils.keywords.fetch({ url: this._paneInfo.uri.spec },
                                       e => entries.push(e));
      if (entries.length > 0) {
        // We show an existing keyword if either POST data was not provided, or
        // if the POST data is the same.
        let existingKeyword = entries[0].keyword;
        let postData = this._paneInfo.postData;
        if (postData) {
          let sameEntry = entries.find(e => e.postData === postData);
          existingKeyword = sameEntry ? sameEntry.keyword : "";
        }
        if (existingKeyword) {
          this._keyword = existingKeyword;
          // Update the text field to the existing keyword.
          this._initTextField(this._keywordField, this._keyword);
        }
      }
    }
  },

  _initLoadInSidebar() {
    if (!this._paneInfo.isBookmark)
      throw new Error("_initLoadInSidebar called unexpectedly");

    this._loadInSidebarCheckbox.checked =
      PlacesUtils.annotations.itemHasAnnotation(
        this._paneInfo.itemId, PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO);
  },

  /**
   * Initialize the panel.
   *
   * @param aInfo
   *        An object having:
   *        1. one of the following properties:
   *        - node: either a result node or a node-like object representing the
   *          item to be edited. A node-like object must have the following
   *          properties (with values that match exactly those a result node
   *          would have): itemId, bookmarkGuid, uri, title, type.
   *        - uris: an array of uris for bulk tagging.
   *
   *        2. any of the following optional properties:
   *          - hiddenRows (Strings array): list of rows to be hidden regardless
   *            of the item edited. Possible values: "title", "location",
   *            "description", "keyword", "loadInSidebar", "feedLocation",
   *            "siteLocation", folderPicker"
   */
  initPanel(aInfo) {
    if (typeof(aInfo) != "object" || aInfo === null)
      throw new Error("aInfo must be an object.");
    if ("node" in aInfo) {
      try {
        aInfo.node.type;
      } catch (e) {
        // If the lazy loader for |type| generates an exception, it means that
        // this bookmark could not be loaded. This sometimes happens when tests
        // create a bookmark by clicking the bookmark star, then try to cleanup
        // before the bookmark panel has finished opening. Either way, if we
        // cannot retrieve the bookmark information, we cannot open the panel.
        return;
      }
    }

    // For sanity ensure that the implementer has uninited the panel before
    // trying to init it again, or we could end up leaking due to observers.
    if (this.initialized)
      this.uninitPanel(false);

    let { parentId, isItem, isURI,
          isBookmark, bulkTagging, uris,
          visibleRows, focusedElement,
          onPanelReady } = this._setPaneInfo(aInfo);

    let showOrCollapse =
      (rowId, isAppropriateForInput, nameInHiddenRows = null) => {
        let visible = isAppropriateForInput;
        if (visible && "hiddenRows" in aInfo && nameInHiddenRows)
          visible &= !aInfo.hiddenRows.includes(nameInHiddenRows);
        if (visible)
          visibleRows.add(rowId);
        return !(this._element(rowId).collapsed = !visible);
      };

    if (showOrCollapse("nameRow", !bulkTagging, "name")) {
      this._initNamePicker();
      this._namePicker.readOnly = this.readOnly;
    }

    // In some cases we want to hide the location field, since it's not
    // human-readable, but we still want to initialize it.
    showOrCollapse("locationRow", isURI, "location");
    if (isURI) {
      this._initLocationField();
      this._locationField.readOnly = this.readOnly;
    }

    // hide the description field for
    if (showOrCollapse("descriptionRow", isItem && !this.readOnly,
                       "description")) {
      this._initDescriptionField();
      this._descriptionField.readOnly = this.readOnly;
    }

    if (showOrCollapse("keywordRow", isBookmark, "keyword")) {
      this._initKeywordField().catch(Cu.reportError);
      this._keywordField.readOnly = this.readOnly;
    }

    // Collapse the tag selector if the item does not accept tags.
    if (showOrCollapse("tagsRow", isURI || bulkTagging, "tags"))
      this._initTagsField();
    else if (!this._element("tagsSelectorRow").collapsed)
      this.toggleTagsSelector();

    // Load in sidebar.
    if (showOrCollapse("loadInSidebarCheckbox", isBookmark, "loadInSidebar")) {
      this._initLoadInSidebar();
    }

    // Folder picker.
    // Technically we should check that the item is not moveable, but that's
    // not cheap (we don't always have the parent), and there's no use case for
    // this (it's only the Star UI that shows the folderPicker)
    if (showOrCollapse("folderRow", isItem, "folderPicker")) {
      this._initFolderMenuList(parentId).catch(Cu.reportError);
    }

    // Selection count.
    if (showOrCollapse("selectionCount", bulkTagging)) {
      this._element("itemsCountText").value =
        PlacesUIUtils.getPluralString("detailsPane.itemsCountLabel",
                                      uris.length,
                                      [uris.length]);
    }

    // Observe changes.
    if (!this._observersAdded) {
      PlacesUtils.bookmarks.addObserver(this);
      window.addEventListener("unload", this);
      this._observersAdded = true;
    }

    let focusElement = () => {
      // The focusedElement possible values are:
      //  * preferred: focus the field that the user touched first the last
      //    time the pane was shown (either namePicker or tagsField)
      //  * first: focus the first non collapsed textbox
      // Note: since all controls are collapsed by default, we don't get the
      // default XUL dialog behavior, that selects the first control, so we set
      // the focus explicitly.
      let elt;
      if (focusedElement === "preferred") {
        elt = this._element(Services.prefs.getCharPref("browser.bookmarks.editDialog.firstEditField"));
      } else if (focusedElement === "first") {
        elt = document.querySelector("textbox:not([collapsed=true])");
      }
      if (elt) {
        elt.focus();
        elt.select();
      }
    };

    if (onPanelReady) {
      onPanelReady(focusElement);
    } else {
      focusElement();
    }
  },

  /**
   * Finds tags that are in common among this._currentInfo.uris;
   */
  _getCommonTags() {
    if ("_cachedCommonTags" in this._paneInfo)
      return this._paneInfo._cachedCommonTags;

    let uris = [...this._paneInfo.uris];
    let firstURI = uris.shift();
    let commonTags = new Set(PlacesUtils.tagging.getTagsForURI(firstURI));
    if (commonTags.size == 0)
      return this._cachedCommonTags = [];

    for (let uri of uris) {
      let curentURITags = PlacesUtils.tagging.getTagsForURI(uri);
      for (let tag of commonTags) {
        if (!curentURITags.includes(tag)) {
          commonTags.delete(tag);
          if (commonTags.size == 0)
            return this._paneInfo.cachedCommonTags = [];
        }
      }
    }
    return this._paneInfo._cachedCommonTags = [...commonTags];
  },

  _initTextField(aElement, aValue) {
    if (aElement.value != aValue) {
      aElement.value = aValue;

      // Clear the editor's undo stack
      let transactionManager;
      try {
        transactionManager = aElement.editor.transactionManager;
      } catch (e) {
        // When retrieving the transaction manager, editor may be null resulting
        // in a TypeError. Additionally, the transaction manager may not
        // exist yet, which causes access to it to throw NS_ERROR_FAILURE.
        // In either event, the transaction manager doesn't exist it, so we
        // don't need to worry about clearing it.
        if (!(e instanceof TypeError) && e.result != Cr.NS_ERROR_FAILURE) {
          throw e;
        }
      }
      if (transactionManager) {
        transactionManager.clear();
      }
    }
  },

  /**
   * Appends a menu-item representing a bookmarks folder to a menu-popup.
   * @param aMenupopup
   *        The popup to which the menu-item should be added.
   * @param aFolderId
   *        The identifier of the bookmarks folder.
   * @param aTitle
   *        The title to use as a label.
   * @return the new menu item.
   */
  _appendFolderItemToMenupopup(aMenupopup, aFolderId, aTitle) {
    // First make sure the folders-separator is visible
    this._element("foldersSeparator").hidden = false;

    var folderMenuItem = document.createElement("menuitem");
    var folderTitle = aTitle;
    folderMenuItem.folderId = aFolderId;
    folderMenuItem.setAttribute("label", folderTitle);
    folderMenuItem.className = "menuitem-iconic folder-icon";
    aMenupopup.appendChild(folderMenuItem);
    return folderMenuItem;
  },

  async _initFolderMenuList(aSelectedFolder) {
    // clean up first
    var menupopup = this._folderMenuList.menupopup;
    while (menupopup.childNodes.length > 6)
      menupopup.removeChild(menupopup.lastChild);

    // Build the static list
    if (!this._staticFoldersListBuilt) {
      let unfiledItem = this._element("unfiledRootItem");
      unfiledItem.label = PlacesUtils.getString("OtherBookmarksFolderTitle");
      unfiledItem.folderId = PlacesUtils.unfiledBookmarksFolderId;
      let bmMenuItem = this._element("bmRootItem");
      bmMenuItem.label = PlacesUtils.getString("BookmarksMenuFolderTitle");
      bmMenuItem.folderId = PlacesUtils.bookmarksMenuFolderId;
      let toolbarItem = this._element("toolbarFolderItem");
      toolbarItem.label = PlacesUtils.getString("BookmarksToolbarFolderTitle");
      toolbarItem.folderId = PlacesUtils.toolbarFolderId;
      this._staticFoldersListBuilt = true;
    }

    // List of recently used folders:
    var folderIds =
      PlacesUtils.annotations.getItemsWithAnnotation(LAST_USED_ANNO);

    /**
     * The value of the LAST_USED_ANNO annotation is the time (in the form of
     * Date.getTime) at which the folder has been last used.
     *
     * First we build the annotated folders array, each item has both the
     * folder identifier and the time at which it was last-used by this dialog
     * set. Then we sort it descendingly based on the time field.
     */
    this._recentFolders = [];
    for (let folderId of folderIds) {
      var lastUsed =
        PlacesUtils.annotations.getItemAnnotation(folderId, LAST_USED_ANNO);
      let guid = await PlacesUtils.promiseItemGuid(folderId);
      let bm = await PlacesUtils.bookmarks.fetch(guid);
      // Since this could be a root mobile folder, we should get the proper
      // title.
      let title = PlacesUtils.bookmarks.getLocalizedTitle(bm);
      this._recentFolders.push({ folderId, guid, title, lastUsed });
    }
    this._recentFolders.sort(function(a, b) {
      if (b.lastUsed < a.lastUsed)
        return -1;
      if (b.lastUsed > a.lastUsed)
        return 1;
      return 0;
    });

    var numberOfItems = Math.min(MAX_FOLDER_ITEM_IN_MENU_LIST,
                                 this._recentFolders.length);
    for (let i = 0; i < numberOfItems; i++) {
      await this._appendFolderItemToMenupopup(menupopup,
                                              this._recentFolders[i].folderId,
                                              this._recentFolders[i].title);
    }

    let selectedFolderGuid = await PlacesUtils.promiseItemGuid(aSelectedFolder);
    let title = (await PlacesUtils.bookmarks.fetch(selectedFolderGuid)).title;
    var defaultItem = this._getFolderMenuItem(aSelectedFolder, title);
    this._folderMenuList.selectedItem = defaultItem;

    // Set a selectedIndex attribute to show special icons
    this._folderMenuList.setAttribute("selectedIndex",
                                      this._folderMenuList.selectedIndex);

    // Hide the folders-separator if no folder is annotated as recently-used
    this._element("foldersSeparator").hidden = (menupopup.childNodes.length <= 6);
    this._folderMenuList.disabled = this.readOnly;
  },

  QueryInterface:
  XPCOMUtils.generateQI([Ci.nsIDOMEventListener,
                         Ci.nsINavBookmarkObserver]),

  _element(aID) {
    return document.getElementById("editBMPanel_" + aID);
  },

  uninitPanel(aHideCollapsibleElements) {
    if (aHideCollapsibleElements) {
      // Hide the folder tree if it was previously visible.
      var folderTreeRow = this._element("folderTreeRow");
      if (!folderTreeRow.collapsed)
        this.toggleFolderTreeVisibility();

      // Hide the tag selector if it was previously visible.
      var tagsSelectorRow = this._element("tagsSelectorRow");
      if (!tagsSelectorRow.collapsed)
        this.toggleTagsSelector();
    }

    if (this._observersAdded) {
      PlacesUtils.bookmarks.removeObserver(this);
      this._observersAdded = false;
    }

    this._setPaneInfo(null);
    this._firstEditedField = "";
  },

  onTagsFieldChange() {
    // Check for _paneInfo existing as the dialog may be closing but receiving
    // async updates from unresolved promises.
    if (this._paneInfo &&
        (this._paneInfo.isURI || this._paneInfo.bulkTagging)) {
      this._updateTags().then(
        anyChanges => {
          // Check _paneInfo here as we might be closing the dialog.
          if (anyChanges && this._paneInfo)
            this._mayUpdateFirstEditField("tagsField");
        }, Cu.reportError);
    }
  },

  /**
   * For a given array of currently-set tags and the tags-input-field
   * value, returns which tags should be removed and which should be added in
   * the form of { removedTags: [...], newTags: [...] }.
   */
  _getTagsChanges(aCurrentTags) {
    let inputTags = this._getTagsArrayFromTagsInputField();

    // Optimize the trivial cases (which are actually the most common).
    if (inputTags.length == 0 && aCurrentTags.length == 0)
      return { newTags: [], removedTags: [] };
    if (inputTags.length == 0)
      return { newTags: [], removedTags: aCurrentTags };
    if (aCurrentTags.length == 0)
      return { newTags: inputTags, removedTags: [] };

    // Do not remove tags that may be reinserted with a different
    // case, since the tagging service may handle those more efficiently.
    let lcInputTags = inputTags.map(t => t.toLowerCase());
    let removedTags = aCurrentTags.filter(t => !lcInputTags.includes(t.toLowerCase()));
    let newTags = inputTags.filter(t => !aCurrentTags.includes(t));
    return { removedTags, newTags };
  },

  // Adds and removes tags for one or more uris.
  _setTagsFromTagsInputField(aCurrentTags, aURIs) {
    let { removedTags, newTags } = this._getTagsChanges(aCurrentTags);
    if (removedTags.length + newTags.length == 0)
      return false;

    let setTags = async function() {
      if (removedTags.length > 0) {
        await PlacesTransactions.Untag({ urls: aURIs, tags: removedTags })
                                .transact();
      }
      if (newTags.length > 0) {
        await PlacesTransactions.Tag({ urls: aURIs, tags: newTags })
                                .transact();
      }
    };

    // Only in the library info-pane it's safe (and necessary) to batch these.
    // TODO bug 1093030: cleanup this mess when the bookmarksProperties dialog
    // and star UI code don't "run a batch in the background".
    if (window.document.documentElement.id == "places")
      PlacesTransactions.batch(setTags).catch(Cu.reportError);
    else
      setTags().catch(Cu.reportError);
    return true;
  },

  async _updateTags() {
    let uris = this._paneInfo.bulkTagging ?
                 this._paneInfo.uris : [this._paneInfo.uri];
    let currentTags = this._paneInfo.bulkTagging ?
                        await this._getCommonTags() :
                        PlacesUtils.tagging.getTagsForURI(uris[0]);
    let anyChanges = this._setTagsFromTagsInputField(currentTags, uris);
    if (!anyChanges)
      return false;

    // The panel could have been closed in the meanwhile.
    if (!this._paneInfo)
      return false;

    // Ensure the tagsField is in sync, clean it up from empty tags
    currentTags = this._paneInfo.bulkTagging ?
                    this._getCommonTags() :
                    PlacesUtils.tagging.getTagsForURI(this._paneInfo.uri);
    this._initTextField(this._tagsField, currentTags.join(", "), false);
    return true;
  },

  /**
   * Stores the first-edit field for this dialog, if the passed-in field
   * is indeed the first edited field
   * @param aNewField
   *        the id of the field that may be set (without the "editBMPanel_"
   *        prefix)
   */
  _mayUpdateFirstEditField(aNewField) {
    // * The first-edit-field behavior is not applied in the multi-edit case
    // * if this._firstEditedField is already set, this is not the first field,
    //   so there's nothing to do
    if (this._paneInfo.bulkTagging || this._firstEditedField)
      return;

    this._firstEditedField = aNewField;

    // set the pref
    Services.prefs.setCharPref("browser.bookmarks.editDialog.firstEditField", aNewField);
  },

  async onNamePickerChange() {
    if (this.readOnly || !(this._paneInfo.isItem || this._paneInfo.isTag))
      return;

    // Here we update either the item title or its cached static title
    let newTitle = this._namePicker.value;
    if (!newTitle && this._paneInfo.isTag) {
      // We don't allow setting an empty title for a tag, restore the old one.
      this._initNamePicker();
    } else {
      this._mayUpdateFirstEditField("namePicker");

      let guid = this._paneInfo.isTag
                  ? (await PlacesUtils.promiseItemGuid(this._paneInfo.itemId))
                  : this._paneInfo.itemGuid;
      await PlacesTransactions.EditTitle({ guid, title: newTitle }).transact();
    }
  },

  onDescriptionFieldChange() {
    if (this.readOnly || !this._paneInfo.isItem)
      return;

    let description = this._element("descriptionField").value;
    if (description != PlacesUIUtils.getItemDescription(this._paneInfo.itemId)) {
      let annotation =
        { name: PlacesUIUtils.DESCRIPTION_ANNO, value: description };
      let guid = this._paneInfo.itemGuid;
      PlacesTransactions.Annotate({ guid, annotation })
                        .transact().catch(Cu.reportError);
    }
  },

  onLocationFieldChange() {
    if (this.readOnly || !this._paneInfo.isBookmark)
      return;

    let newURI;
    try {
      newURI = PlacesUIUtils.createFixedURI(this._locationField.value);
    } catch (ex) {
      // TODO: Bug 1089141 - Provide some feedback about the invalid url.
      return;
    }

    if (this._paneInfo.uri.equals(newURI))
      return;

    let guid = this._paneInfo.itemGuid;
    PlacesTransactions.EditUrl({ guid, url: newURI })
                      .transact().catch(Cu.reportError);
  },

  onKeywordFieldChange() {
    if (this.readOnly || !this._paneInfo.isBookmark)
      return;

    let oldKeyword = this._keyword;
    let keyword = this._keyword = this._keywordField.value;
    let postData = this._paneInfo.postData;
    let guid = this._paneInfo.itemGuid;
    PlacesTransactions.EditKeyword({ guid, keyword, postData, oldKeyword })
                      .transact().catch(Cu.reportError);
  },

  onLoadInSidebarCheckboxCommand() {
    if (!this.initialized || !this._paneInfo.isBookmark)
      return;

    let annotation = { name: PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO };
    if (this._loadInSidebarCheckbox.checked)
      annotation.value = true;

    let guid = this._paneInfo.itemGuid;
    PlacesTransactions.Annotate({ guid, annotation })
                      .transact().catch(Cu.reportError);
  },

  toggleFolderTreeVisibility() {
    var expander = this._element("foldersExpander");
    var folderTreeRow = this._element("folderTreeRow");
    if (!folderTreeRow.collapsed) {
      expander.className = "expander-down";
      expander.setAttribute("tooltiptext",
                            expander.getAttribute("tooltiptextdown"));
      folderTreeRow.collapsed = true;
      this._element("chooseFolderSeparator").hidden =
        this._element("chooseFolderMenuItem").hidden = false;
    } else {
      expander.className = "expander-up";
      expander.setAttribute("tooltiptext",
                            expander.getAttribute("tooltiptextup"));
      folderTreeRow.collapsed = false;

      // XXXmano: Ideally we would only do this once, but for some odd reason,
      // the editable mode set on this tree, together with its collapsed state
      // breaks the view.
      const FOLDER_TREE_PLACE_URI =
        "place:excludeItems=1&excludeQueries=1&excludeReadOnlyFolders=1&type=" +
        Ci.nsINavHistoryQueryOptions.RESULTS_AS_ROOTS_QUERY;
      this._folderTree.place = FOLDER_TREE_PLACE_URI;

      this._element("chooseFolderSeparator").hidden =
        this._element("chooseFolderMenuItem").hidden = true;
      this._folderTree.selectItems([this._paneInfo.parentGuid]);
      this._folderTree.focus();
    }
  },

  /**
   * Get the corresponding menu-item in the folder-menu-list for a bookmarks
   * folder if such an item exists. Otherwise, this creates a menu-item for the
   * folder. If the items-count limit (see MAX_FOLDERS_IN_MENU_LIST) is reached,
   * the new item replaces the last menu-item.
   * @param aFolderId
   *        The identifier of the bookmarks folder.
   * @param aTitle
   *        The title to use in case of menuitem creation.
   * @return handle to the menuitem.
   */
  _getFolderMenuItem(aFolderId, aTitle) {
    let menupopup = this._folderMenuList.menupopup;
    let menuItem = Array.prototype.find.call(
      menupopup.childNodes, item => item.folderId === aFolderId);
    if (menuItem !== undefined)
      return menuItem;

    // 3 special folders + separator + folder-items-count limit
    if (menupopup.childNodes.length == 4 + MAX_FOLDER_ITEM_IN_MENU_LIST)
      menupopup.removeChild(menupopup.lastChild);

    return this._appendFolderItemToMenupopup(menupopup, aFolderId, aTitle);
  },

  async onFolderMenuListCommand(aEvent) {
    // Check for _paneInfo existing as the dialog may be closing but receiving
    // async updates from unresolved promises.
    if (!this._paneInfo) {
      return;
    }
    // Set a selectedIndex attribute to show special icons
    this._folderMenuList.setAttribute("selectedIndex",
                                      this._folderMenuList.selectedIndex);

    if (aEvent.target.id == "editBMPanel_chooseFolderMenuItem") {
      // reset the selection back to where it was and expand the tree
      // (this menu-item is hidden when the tree is already visible
      let item = this._getFolderMenuItem(this._paneInfo.parentId,
                                         this._paneInfo.title);
      this._folderMenuList.selectedItem = item;
      // XXXmano HACK: setTimeout 100, otherwise focus goes back to the
      // menulist right away
      setTimeout(() => this.toggleFolderTreeVisibility(), 100);
      return;
    }

    // Move the item
    let containerId = this._folderMenuList.selectedItem.folderId;
    if (this._paneInfo.parentId != containerId &&
        this._paneInfo.itemId != containerId) {
      let newParentGuid = await PlacesUtils.promiseItemGuid(containerId);
      let guid = this._paneInfo.itemGuid;
      await PlacesTransactions.Move({ guid, newParentGuid }).transact();

      // Mark the containing folder as recently-used if it isn't in the
      // static list
      if (containerId != PlacesUtils.unfiledBookmarksFolderId &&
          containerId != PlacesUtils.toolbarFolderId &&
          containerId != PlacesUtils.bookmarksMenuFolderId) {
        this._markFolderAsRecentlyUsed(containerId)
            .catch(Cu.reportError);
      }

      // Auto-show the bookmarks toolbar when adding / moving an item there.
      if (containerId == PlacesUtils.toolbarFolderId) {
        Services.obs.notifyObservers(null, "autoshow-bookmarks-toolbar");
      }
    }

    // Update folder-tree selection
    var folderTreeRow = this._element("folderTreeRow");
    if (!folderTreeRow.collapsed) {
      var selectedNode = this._folderTree.selectedNode;
      if (!selectedNode ||
          PlacesUtils.getConcreteItemId(selectedNode) != containerId)
        this._folderTree.selectItems([containerId]);
    }
  },

  onFolderTreeSelect() {
    var selectedNode = this._folderTree.selectedNode;

    // Disable the "New Folder" button if we cannot create a new folder
    this._element("newFolderButton")
        .disabled = !this._folderTree.insertionPoint || !selectedNode;

    if (!selectedNode)
      return;

    var folderId = PlacesUtils.getConcreteItemId(selectedNode);
    if (this._folderMenuList.selectedItem.folderId == folderId)
      return;

    var folderItem = this._getFolderMenuItem(folderId, selectedNode.title);
    this._folderMenuList.selectedItem = folderItem;
    folderItem.doCommand();
  },

  async _markFolderAsRecentlyUsed(aFolderId) {
    // Expire old unused recent folders.
    let guids = [];
    while (this._recentFolders.length > MAX_FOLDER_ITEM_IN_MENU_LIST) {
      let folderId = this._recentFolders.pop().folderId;
      let guid = await PlacesUtils.promiseItemGuid(folderId);
      guids.push(guid);
    }
    if (guids.length > 0) {
      let annotation = this._getLastUsedAnnotationObject(false);
      PlacesTransactions.Annotate({ guids, annotation  })
                        .transact().catch(Cu.reportError);
    }

    // Mark folder as recently used
    let annotation = this._getLastUsedAnnotationObject(true);
    let guid = await PlacesUtils.promiseItemGuid(aFolderId);
    PlacesTransactions.Annotate({ guid, annotation })
                      .transact().catch(Cu.reportError);
  },

  /**
   * Returns an object which could then be used to set/unset the
   * LAST_USED_ANNO annotation for a folder.
   *
   * @param aLastUsed
   *        Whether to set or unset the LAST_USED_ANNO annotation.
   * @returns an object representing the annotation which could then be used
   *          with the transaction manager.
   */
  _getLastUsedAnnotationObject(aLastUsed) {
    return { name: LAST_USED_ANNO,
             value: aLastUsed ? new Date().getTime() : null };
  },

  _rebuildTagsSelectorList() {
    let tagsSelector = this._element("tagsSelector");
    let tagsSelectorRow = this._element("tagsSelectorRow");
    if (tagsSelectorRow.collapsed)
      return;

    // Save the current scroll position and restore it after the rebuild.
    let firstIndex = tagsSelector.getIndexOfFirstVisibleRow();
    let selectedIndex = tagsSelector.selectedIndex;
    let selectedTag = selectedIndex >= 0 ? tagsSelector.selectedItem.label
                                         : null;

    while (tagsSelector.hasChildNodes()) {
      tagsSelector.removeChild(tagsSelector.lastChild);
    }

    let tagsInField = this._getTagsArrayFromTagsInputField();
    let allTags = PlacesUtils.tagging.allTags;
    for (let tag of allTags) {
      let elt = document.createElement("listitem");
      elt.setAttribute("type", "checkbox");
      elt.setAttribute("label", tag);
      if (tagsInField.includes(tag))
        elt.setAttribute("checked", "true");
      tagsSelector.appendChild(elt);
      if (selectedTag === tag)
        selectedIndex = tagsSelector.getIndexOfItem(elt);
    }

    // Restore position.
    // The listbox allows to scroll only if the required offset doesn't
    // overflow its capacity, thus need to adjust the index for removals.
    firstIndex =
      Math.min(firstIndex,
               tagsSelector.itemCount - tagsSelector.getNumberOfVisibleRows());
    tagsSelector.scrollToIndex(firstIndex);
    if (selectedIndex >= 0 && tagsSelector.itemCount > 0) {
      selectedIndex = Math.min(selectedIndex, tagsSelector.itemCount - 1);
      tagsSelector.selectedIndex = selectedIndex;
      tagsSelector.ensureIndexIsVisible(selectedIndex);
    }
  },

  toggleTagsSelector() {
    var tagsSelector = this._element("tagsSelector");
    var tagsSelectorRow = this._element("tagsSelectorRow");
    var expander = this._element("tagsSelectorExpander");
    if (tagsSelectorRow.collapsed) {
      expander.className = "expander-up";
      expander.setAttribute("tooltiptext",
                            expander.getAttribute("tooltiptextup"));
      tagsSelectorRow.collapsed = false;
      this._rebuildTagsSelectorList();

      // This is a no-op if we've added the listener.
      tagsSelector.addEventListener("CheckboxStateChange", this);
    } else {
      expander.className = "expander-down";
      expander.setAttribute("tooltiptext",
                            expander.getAttribute("tooltiptextdown"));
      tagsSelectorRow.collapsed = true;
    }
  },

  /**
   * Splits "tagsField" element value, returning an array of valid tag strings.
   *
   * @return Array of tag strings found in the field value.
   */
  _getTagsArrayFromTagsInputField() {
    let tags = this._element("tagsField").value;
    return tags.trim()
               .split(/\s*,\s*/) // Split on commas and remove spaces.
               .filter(tag => tag.length > 0); // Kill empty tags.
  },

  async newFolder() {
    let ip = this._folderTree.insertionPoint;

    // default to the bookmarks menu folder
    if (!ip) {
      ip = new PlacesInsertionPoint({
        parentId: PlacesUtils.bookmarksMenuFolderId,
        parentGuid: PlacesUtils.bookmarks.menuGuid
      });
    }

    // XXXmano: add a separate "New Folder" string at some point...
    let title = this._element("newFolderButton").label;
    await PlacesTransactions.NewFolder({ parentGuid: ip.guid, title,
                                         index: await ip.getIndex() })
                            .transact().catch(Cu.reportError);

    this._folderTree.focus();
    this._folderTree.selectItems([ip.itemId]);
    PlacesUtils.asContainer(this._folderTree.selectedNode).containerOpen = true;
    this._folderTree.selectItems([this._lastNewItem]);
    this._folderTree.startEditing(this._folderTree.view.selection.currentIndex,
                                  this._folderTree.columns.getFirstColumn());
  },

  // nsIDOMEventListener
  handleEvent(aEvent) {
    switch (aEvent.type) {
    case "CheckboxStateChange":
      // Update the tags field when items are checked/unchecked in the listbox
      let tags = this._getTagsArrayFromTagsInputField();
      let tagCheckbox = aEvent.target;

      let curTagIndex = tags.indexOf(tagCheckbox.label);
      let tagsSelector = this._element("tagsSelector");
      tagsSelector.selectedItem = tagCheckbox;

      if (tagCheckbox.checked) {
        if (curTagIndex == -1)
          tags.push(tagCheckbox.label);
      } else if (curTagIndex != -1) {
        tags.splice(curTagIndex, 1);
      }
      this._element("tagsField").value = tags.join(", ");
      this._updateTags();
      break;
    case "unload":
      this.uninitPanel(false);
      break;
    }
  },

  _initTagsField() {
    let tags;
    if (this._paneInfo.isURI)
      tags = PlacesUtils.tagging.getTagsForURI(this._paneInfo.uri);
    else if (this._paneInfo.bulkTagging)
      tags = this._getCommonTags();
    else
      throw new Error("_promiseTagsStr called unexpectedly");

    this._initTextField(this._tagsField, tags.join(", "));
  },

  async _onTagsChange(guid, changedURI = null) {
    let paneInfo = this._paneInfo;
    let updateTagsField = false;
    if (paneInfo.isURI) {
      if (paneInfo.isBookmark && guid == paneInfo.itemGuid) {
        updateTagsField = true;
      } else if (!paneInfo.isBookmark) {
        if (!changedURI) {
          let href = (await PlacesUtils.bookmarks.fetch(guid)).url.href;
          changedURI = Services.io.newURI(href);
        }
        updateTagsField = changedURI.equals(paneInfo.uri);
      }
    } else if (paneInfo.bulkTagging) {
      if (!changedURI) {
        let href = (await PlacesUtils.bookmarks.fetch(guid)).url.href;
        changedURI = Services.io.newURI(href);
      }
      if (paneInfo.uris.some(uri => uri.equals(changedURI))) {
        updateTagsField = true;
        delete this._paneInfo._cachedCommonTags;
      }
    } else {
      throw new Error("_onTagsChange called unexpectedly");
    }

    if (updateTagsField) {
      this._initTagsField();
      // Any tags change should be reflected in the tags selector.
      if (this._element("tagsSelector")) {
        this._rebuildTagsSelectorList();
      }
    }
  },

  _onItemTitleChange(aItemId, aNewTitle) {
    if (aItemId == this._paneInfo.itemId) {
      this._paneInfo.title = aNewTitle;
      this._initTextField(this._namePicker, aNewTitle);
    } else if (this._paneInfo.visibleRows.has("folderRow")) {
      // If the title of a folder which is listed within the folders
      // menulist has been changed, we need to update the label of its
      // representing element.
      let menupopup = this._folderMenuList.menupopup;
      for (let menuitem of menupopup.childNodes) {
        if ("folderId" in menuitem && menuitem.folderId == aItemId) {
          menuitem.label = aNewTitle;
          break;
        }
      }
    }
    // We need to also update title of recent folders.
    if (this._recentFolders) {
      for (let folder of this._recentFolders) {
        if (folder.folderId == aItemId) {
          folder.title = aNewTitle;
          break;
        }
      }
    }
  },

  // nsINavBookmarkObserver
  onItemChanged(aItemId, aProperty, aIsAnnotationProperty, aValue,
                aLastModified, aItemType, aParentId, aGuid) {
    if (aProperty == "tags" && this._paneInfo.visibleRows.has("tagsRow")) {
      this._onTagsChange(aGuid).catch(Cu.reportError);
      return;
    }
    if (aProperty == "title" && (this._paneInfo.isItem || this._paneInfo.isTag)) {
      // This also updates titles of folders in the folder menu list.
      this._onItemTitleChange(aItemId, aValue);
      return;
    }

    if (!this._paneInfo.isItem || this._paneInfo.itemId != aItemId) {
      return;
    }

    switch (aProperty) {
    case "uri":
      let newURI = Services.io.newURI(aValue);
      if (!newURI.equals(this._paneInfo.uri)) {
        this._paneInfo.uri = newURI;
        if (this._paneInfo.visibleRows.has("locationRow"))
          this._initLocationField();

        if (this._paneInfo.visibleRows.has("tagsRow")) {
          delete this._paneInfo._cachedCommonTags;
          this._onTagsChange(aGuid, newURI).catch(Cu.reportError);
        }
      }
      break;
    case "keyword":
      if (this._paneInfo.visibleRows.has("keywordRow"))
        this._initKeywordField(aValue).catch(Cu.reportError);
      break;
    case PlacesUIUtils.DESCRIPTION_ANNO:
      if (this._paneInfo.visibleRows.has("descriptionRow"))
        this._initDescriptionField();
      break;
    case PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO:
      if (this._paneInfo.visibleRows.has("loadInSidebarCheckbox"))
        this._initLoadInSidebar();
      break;
    }
  },

  onItemMoved(id, oldParentId, oldIndex, newParentId, newIndex, type, guid,
              oldParentGuid, newParentGuid) {
    if (!this._paneInfo.isItem || this._paneInfo.itemId != id) {
      return;
    }

    this._paneInfo.parentId = newParentId;
    this._paneInfo.parentGuid = newParentGuid;

    if (!this._paneInfo.visibleRows.has("folderRow") ||
        newParentId == this._folderMenuList.selectedItem.folderId) {
      return;
    }

    // Just setting selectItem _does not_ trigger oncommand, so we don't
    // recurse.
    PlacesUtils.bookmarks.fetch(newParentGuid).then(bm => {
      this._folderMenuList.selectedItem = this._getFolderMenuItem(newParentId,
                                                                  bm.title);
    });
  },

  onItemAdded(aItemId, aParentId, aIndex, aItemType, aURI) {
    this._lastNewItem = aItemId;
  },

  onItemRemoved() { },
  onBeginUpdateBatch() { },
  onEndUpdateBatch() { },
  onItemVisited() { },
};


for (let elt of ["folderMenuList", "folderTree", "namePicker",
                 "locationField", "descriptionField", "keywordField",
                 "tagsField", "loadInSidebarCheckbox"]) {
  let eltScoped = elt;
  XPCOMUtils.defineLazyGetter(gEditItemOverlay, `_${eltScoped}`,
                              () => gEditItemOverlay._element(eltScoped));
}
PK
!<&z
�| | =chrome/browser/content/browser/places/editBookmarkOverlay.xul<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 overlay [
<!ENTITY % editBookmarkOverlayDTD SYSTEM "chrome://browser/locale/places/editBookmarkOverlay.dtd">
%editBookmarkOverlayDTD;
]>

<?xml-stylesheet href="chrome://browser/skin/places/editBookmarkOverlay.css"?>
<?xml-stylesheet href="chrome://browser/skin/places/places.css"?>

<overlay id="editBookmarkOverlay"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

  <vbox id="editBookmarkPanelContent" flex="1">
    <hbox id="editBMPanel_selectionCount" pack="center">
      <label id="editBMPanel_itemsCountText"/>
    </hbox>

    <grid id="editBookmarkPanelGrid" flex="1">
      <columns id="editBMPanel_columns">
        <column id="editBMPanel_labelColumn" />
        <column flex="1" id="editBMPanel_editColumn" />
      </columns>
      <rows id="editBMPanel_rows">
        <row id="editBMPanel_nameRow"
             align="center"
             collapsed="true">
          <label value="&editBookmarkOverlay.name.label;"
                 class="editBMPanel_rowLabel"
                 accesskey="&editBookmarkOverlay.name.accesskey;"
                 control="editBMPanel_namePicker"/>
          <textbox id="editBMPanel_namePicker"
                   onchange="gEditItemOverlay.onNamePickerChange().catch(Components.utils.reportError);"/>
        </row>

        <row id="editBMPanel_locationRow"
             align="center"
             collapsed="true">
          <label value="&editBookmarkOverlay.location.label;"
                 class="editBMPanel_rowLabel"
                 accesskey="&editBookmarkOverlay.location.accesskey;"
                 control="editBMPanel_locationField"/>
          <textbox id="editBMPanel_locationField"
                   class="uri-element"
                   onchange="gEditItemOverlay.onLocationFieldChange();"/>
        </row>

        <row id="editBMPanel_folderRow"
             align="center"
             collapsed="true">
          <label value="&editBookmarkOverlay.folder.label;"
                 class="editBMPanel_rowLabel"
                 control="editBMPanel_folderMenuList"/>
          <hbox flex="1" align="center">
            <menulist id="editBMPanel_folderMenuList"
                      class="folder-icon"
                      flex="1"
                      oncommand="gEditItemOverlay.onFolderMenuListCommand(event).catch(Components.utils.reportError);">
              <menupopup>
                <!-- Static item for special folders -->
                <menuitem id="editBMPanel_toolbarFolderItem"
                          class="menuitem-iconic folder-icon"/>
                <menuitem id="editBMPanel_bmRootItem"
                          class="menuitem-iconic folder-icon"/>
                <menuitem id="editBMPanel_unfiledRootItem"
                          class="menuitem-iconic folder-icon"/>
                <menuseparator id="editBMPanel_chooseFolderSeparator"/>
                <menuitem id="editBMPanel_chooseFolderMenuItem"
                          label="&editBookmarkOverlay.choose.label;"
                          class="menuitem-iconic folder-icon"/>
                <menuseparator id="editBMPanel_foldersSeparator" hidden="true"/>
              </menupopup>
            </menulist>
            <button id="editBMPanel_foldersExpander"
                    class="expander-down"
                    tooltiptext="&editBookmarkOverlay.foldersExpanderDown.tooltip;"
                    tooltiptextdown="&editBookmarkOverlay.foldersExpanderDown.tooltip;"
                    tooltiptextup="&editBookmarkOverlay.expanderUp.tooltip;"
                    oncommand="gEditItemOverlay.toggleFolderTreeVisibility();"/>
          </hbox>
        </row>

        <row id="editBMPanel_folderTreeRow"
             collapsed="true"
             flex="1">
          <spacer/>
          <vbox flex="1">
            <tree id="editBMPanel_folderTree"
                  flex="1"
                  class="placesTree"
                  type="places"
                  height="150"
                  minheight="150"
                  editable="true"
                  onselect="gEditItemOverlay.onFolderTreeSelect();"
                  disableUserActions="true"
                  hidecolumnpicker="true">
              <treecols>
                <treecol anonid="title" flex="1" primary="true" hideheader="true"/>
              </treecols>
              <treechildren flex="1"/>
            </tree>

            <hbox id="editBMPanel_newFolderBox">
              <button label="&editBookmarkOverlay.newFolderButton.label;"
                      id="editBMPanel_newFolderButton"
                      accesskey="&editBookmarkOverlay.newFolderButton.accesskey;"
                      oncommand="gEditItemOverlay.newFolder().catch(Components.utils.reportError);"/>
            </hbox>
          </vbox>
        </row>

        <row id="editBMPanel_tagsRow"
             align="center"
             collapsed="true">
          <label value="&editBookmarkOverlay.tags.label;"
                 class="editBMPanel_rowLabel"
                 accesskey="&editBookmarkOverlay.tags.accesskey;"
                 control="editBMPanel_tagsField"/>
          <hbox flex="1" align="center">
            <textbox id="editBMPanel_tagsField"
                     type="autocomplete"
                     flex="1"
                     autocompletesearch="places-tag-autocomplete"
                     autocompletepopup="PopupAutoComplete"
                     completedefaultindex="true"
                     tabscrolling="true"
                     placeholder="&editBookmarkOverlay.tagsEmptyDesc.label;"
                     onchange="gEditItemOverlay.onTagsFieldChange();"/>
            <button id="editBMPanel_tagsSelectorExpander"
                    class="expander-down"
                    tooltiptext="&editBookmarkOverlay.tagsExpanderDown.tooltip;"
                    tooltiptextdown="&editBookmarkOverlay.tagsExpanderDown.tooltip;"
                    tooltiptextup="&editBookmarkOverlay.expanderUp.tooltip;"
                    oncommand="gEditItemOverlay.toggleTagsSelector();"/>
          </hbox>
        </row>

        <row id="editBMPanel_tagsSelectorRow"
             align="center"
             collapsed="true">
          <spacer/>
          <listbox id="editBMPanel_tagsSelector"
                   height="150"/>
        </row>

        <row id="editBMPanel_keywordRow"
             align="center"
             collapsed="true">
          <observes element="additionalInfoBroadcaster" attribute="hidden"/>
          <label value="&editBookmarkOverlay.keyword.label;"
                 class="editBMPanel_rowLabel"
                 accesskey="&editBookmarkOverlay.keyword.accesskey;"
                 control="editBMPanel_keywordField"/>
          <textbox id="editBMPanel_keywordField"
                   onchange="gEditItemOverlay.onKeywordFieldChange();"/>
        </row>

        <row id="editBMPanel_descriptionRow"
             collapsed="true">
          <observes element="additionalInfoBroadcaster" attribute="hidden"/>
          <label value="&editBookmarkOverlay.description.label;"
                 class="editBMPanel_rowLabel"
                 accesskey="&editBookmarkOverlay.description.accesskey;"
                 control="editBMPanel_descriptionField"/>
          <textbox id="editBMPanel_descriptionField"
                   multiline="true"
                   rows="4"
                   onchange="gEditItemOverlay.onDescriptionFieldChange();"/>
        </row>
      </rows>
    </grid>

    <checkbox id="editBMPanel_loadInSidebarCheckbox"
              collapsed="true"
              label="&editBookmarkOverlay.loadInSidebar.label;"
              accesskey="&editBookmarkOverlay.loadInSidebar.accesskey;"
              oncommand="gEditItemOverlay.onLoadInSidebarCheckboxCommand();">
      <observes element="additionalInfoBroadcaster" attribute="hidden"/>
    </checkbox>

    <!-- If the ids are changing or additional fields are being added, be sure
         to sync the values in places.js -->
    <broadcaster id="additionalInfoBroadcaster"/>
  </vbox>
</overlay>
PK
!<�y?��6chrome/browser/content/browser/places/history-panel.js/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

ChromeUtils.import("resource://gre/modules/TelemetryStopwatch.jsm");

var gHistoryTree;
var gSearchBox;
var gHistoryGrouping = "";
var gSearching = false;

function HistorySidebarInit() {
  gHistoryTree = document.getElementById("historyTree");
  gSearchBox = document.getElementById("search-box");

  gHistoryGrouping = document.getElementById("viewButton").
                              getAttribute("selectedsort");

  if (gHistoryGrouping == "site")
    document.getElementById("bysite").setAttribute("checked", "true");
  else if (gHistoryGrouping == "visited")
    document.getElementById("byvisited").setAttribute("checked", "true");
  else if (gHistoryGrouping == "lastvisited")
    document.getElementById("bylastvisited").setAttribute("checked", "true");
  else if (gHistoryGrouping == "dayandsite")
    document.getElementById("bydayandsite").setAttribute("checked", "true");
  else
    document.getElementById("byday").setAttribute("checked", "true");

  searchHistory("");
}

function GroupBy(groupingType) {
  gHistoryGrouping = groupingType;
  searchHistory(gSearchBox.value);
}

function searchHistory(aInput) {
  var query = PlacesUtils.history.getNewQuery();
  var options = PlacesUtils.history.getNewQueryOptions();

  const NHQO = Ci.nsINavHistoryQueryOptions;
  var sortingMode;
  var resultType;

  switch (gHistoryGrouping) {
    case "visited":
      resultType = NHQO.RESULTS_AS_URI;
      sortingMode = NHQO.SORT_BY_VISITCOUNT_DESCENDING;
      break;
    case "lastvisited":
      resultType = NHQO.RESULTS_AS_URI;
      sortingMode = NHQO.SORT_BY_DATE_DESCENDING;
      break;
    case "dayandsite":
      resultType = NHQO.RESULTS_AS_DATE_SITE_QUERY;
      break;
    case "site":
      resultType = NHQO.RESULTS_AS_SITE_QUERY;
      sortingMode = NHQO.SORT_BY_TITLE_ASCENDING;
      break;
    case "day":
    default:
      resultType = NHQO.RESULTS_AS_DATE_QUERY;
      break;
  }

  if (aInput) {
    query.searchTerms = aInput;
    if (gHistoryGrouping != "visited" && gHistoryGrouping != "lastvisited") {
      sortingMode = NHQO.SORT_BY_FRECENCY_DESCENDING;
      resultType = NHQO.RESULTS_AS_URI;
    }
  }

  options.sortingMode = sortingMode;
  options.resultType = resultType;
  options.includeHidden = !!aInput;

  if (gHistoryGrouping == "lastvisited")
    this.TelemetryStopwatch.start("HISTORY_LASTVISITED_TREE_QUERY_TIME_MS");

  // call load() on the tree manually
  // instead of setting the place attribute in history-panel.xul
  // otherwise, we will end up calling load() twice
  gHistoryTree.load([query], options);

  if (gHistoryGrouping == "lastvisited")
    this.TelemetryStopwatch.finish("HISTORY_LASTVISITED_TREE_QUERY_TIME_MS");
}

window.addEventListener("SidebarFocused",
                        () => gSearchBox.focus());
PK
!<���i`i`.chrome/browser/content/browser/places/menu.xml<?xml version="1.0"?>

<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<bindings id="placesMenuBindings"
          xmlns="http://www.mozilla.org/xbl"
          xmlns:xbl="http://www.mozilla.org/xbl"
          xmlns:html="http://www.w3.org/1999/xhtml"
          xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

  <binding id="places-popup-base"
           extends="chrome://global/content/bindings/popup.xml#popup">
    <content>
      <xul:hbox flex="1">
        <xul:vbox class="menupopup-drop-indicator-bar" hidden="true">
          <xul:image class="menupopup-drop-indicator" mousethrough="always"/>
        </xul:vbox>
        <xul:arrowscrollbox class="popup-internal-box" flex="1" orient="vertical"
                            smoothscroll="false">
          <children/>
        </xul:arrowscrollbox>
      </xul:hbox>
    </content>

    <implementation>

      <field name="AppConstants" readonly="true">
        (ChromeUtils.import("resource://gre/modules/AppConstants.jsm", {})).AppConstants;
      </field>

      <field name="_indicatorBar">
        document.getAnonymousElementByAttribute(this, "class",
                                                "menupopup-drop-indicator-bar");
      </field>

      <field name="_scrollBox">
        document.getAnonymousElementByAttribute(this, "class",
                                                "popup-internal-box");
      </field>

      <!-- This is the view that manage the popup -->
      <field name="_rootView">PlacesUIUtils.getViewForNode(this);</field>

      <!-- Check if we should hide the drop indicator for the target -->
      <method name="_hideDropIndicator">
        <parameter name="aEvent"/>
        <body><![CDATA[
          let target = aEvent.target;

          // Don't draw the drop indicator outside of markers or if current
          // node is not a Places node.
          let betweenMarkers =
            (this._startMarker.compareDocumentPosition(target) & Node.DOCUMENT_POSITION_FOLLOWING) &&
            (this._endMarker.compareDocumentPosition(target) & Node.DOCUMENT_POSITION_PRECEDING);

          // Hide the dropmarker if current node is not a Places node.
          return !(target && target._placesNode && betweenMarkers);
        ]]></body>
      </method>

      <!-- This function returns information about where to drop when
           dragging over this popup insertion point -->
      <method name="_getDropPoint">
        <parameter name="aEvent"/>
          <body><![CDATA[
            // Can't drop if the menu isn't a folder
            let resultNode = this._placesNode;

            if (!PlacesUtils.nodeIsFolder(resultNode) ||
                this._rootView.controller.disallowInsertion(resultNode)) {
              return null;
            }

            var dropPoint = { ip: null, folderElt: null };

            // The element we are dragging over
            let elt = aEvent.target;
            if (elt.localName == "menupopup")
              elt = elt.parentNode;

            // Calculate positions taking care of arrowscrollbox
            let scrollbox = this._scrollBox;
            let eventY = aEvent.layerY + (scrollbox.boxObject.y - this.boxObject.y);
            let scrollboxOffset = scrollbox.scrollBoxObject.y -
                                  (scrollbox.boxObject.y - this.boxObject.y);
            let eltY = elt.boxObject.y - scrollboxOffset;
            let eltHeight = elt.boxObject.height;

            if (!elt._placesNode) {
              // If we are dragging over a non places node drop at the end.
              dropPoint.ip = new PlacesInsertionPoint({
                parentId: PlacesUtils.getConcreteItemId(resultNode),
                parentGuid: PlacesUtils.getConcreteItemGuid(resultNode)
              });
              // We can set folderElt if we are dropping over a static menu that
              // has an internal placespopup.
              let isMenu = elt.localName == "menu" ||
                 (elt.localName == "toolbarbutton" &&
                  elt.getAttribute("type") == "menu");
              if (isMenu && elt.lastChild &&
                  elt.lastChild.hasAttribute("placespopup"))
                dropPoint.folderElt = elt;
              return dropPoint;
            }

            let tagName = PlacesUtils.nodeIsTagQuery(elt._placesNode) ?
                            elt._placesNode.title : null;
            if ((PlacesUtils.nodeIsFolder(elt._placesNode) &&
                 !PlacesUIUtils.isFolderReadOnly(elt._placesNode, this._rootView)) ||
                PlacesUtils.nodeIsTagQuery(elt._placesNode)) {
              // This is a folder or a tag container.
              if (eventY - eltY < eltHeight * 0.20) {
                // If mouse is in the top part of the element, drop above folder.
                dropPoint.ip = new PlacesInsertionPoint({
                  parentId: PlacesUtils.getConcreteItemId(resultNode),
                  parentGuid: PlacesUtils.getConcreteItemGuid(resultNode),
                  orientation: Ci.nsITreeView.DROP_BEFORE,
                  tagName,
                  dropNearNode: elt._placesNode
                });
                return dropPoint;
              } else if (eventY - eltY < eltHeight * 0.80) {
                // If mouse is in the middle of the element, drop inside folder.
                dropPoint.ip = new PlacesInsertionPoint({
                  parentId: PlacesUtils.getConcreteItemId(elt._placesNode),
                  parentGuid: PlacesUtils.getConcreteItemGuid(elt._placesNode),
                  tagName
                });
                dropPoint.folderElt = elt;
                return dropPoint;
              }
            } else if (eventY - eltY <= eltHeight / 2) {
              // This is a non-folder node or a readonly folder.
              // If the mouse is above the middle, drop above this item.
              dropPoint.ip = new PlacesInsertionPoint({
                parentId: PlacesUtils.getConcreteItemId(resultNode),
                parentGuid: PlacesUtils.getConcreteItemGuid(resultNode),
                orientation: Ci.nsITreeView.DROP_BEFORE,
                tagName,
                dropNearNode: elt._placesNode
              });
              return dropPoint;
            }

            // Drop below the item.
            dropPoint.ip = new PlacesInsertionPoint({
              parentId: PlacesUtils.getConcreteItemId(resultNode),
              parentGuid: PlacesUtils.getConcreteItemGuid(resultNode),
              orientation: Ci.nsITreeView.DROP_AFTER,
              tagName,
              dropNearNode: elt._placesNode,
            });
            return dropPoint;
        ]]></body>
      </method>

      <!-- Sub-menus should be opened when the mouse drags over them, and closed
           when the mouse drags off.  The overFolder object manages opening and
           closing of folders when the mouse hovers. -->
      <field name="_overFolder"><![CDATA[({
        _self: this,
        _folder: {elt: null,
                  openTimer: null,
                  hoverTime: 350,
                  closeTimer: null},
        _closeMenuTimer: null,

        get elt() {
          return this._folder.elt;
        },
        set elt(val) {
          return this._folder.elt = val;
        },

        get openTimer() {
          return this._folder.openTimer;
        },
        set openTimer(val) {
          return this._folder.openTimer = val;
        },

        get hoverTime() {
          return this._folder.hoverTime;
        },
        set hoverTime(val) {
          return this._folder.hoverTime = val;
        },

        get closeTimer() {
          return this._folder.closeTimer;
        },
        set closeTimer(val) {
          return this._folder.closeTimer = val;
        },

        get closeMenuTimer() {
          return this._closeMenuTimer;
        },
        set closeMenuTimer(val) {
          return this._closeMenuTimer = val;
        },

        setTimer: function OF__setTimer(aTime) {
          var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
          timer.initWithCallback(this, aTime, timer.TYPE_ONE_SHOT);
          return timer;
        },

        notify: function OF__notify(aTimer) {
          // Function to process all timer notifications.

          if (aTimer == this._folder.openTimer) {
            // Timer to open a submenu that's being dragged over.
            this._folder.elt.lastChild.setAttribute("autoopened", "true");
            this._folder.elt.lastChild.showPopup(this._folder.elt);
            this._folder.openTimer = null;
          } else if (aTimer == this._folder.closeTimer) {
            // Timer to close a submenu that's been dragged off of.
            // Only close the submenu if the mouse isn't being dragged over any
            // of its child menus.
            var draggingOverChild = PlacesControllerDragHelper
                                    .draggingOverChildNode(this._folder.elt);
            if (draggingOverChild)
              this._folder.elt = null;
            this.clear();

            // Close any parent folders which aren't being dragged over.
            // (This is necessary because of the above code that keeps a folder
            // open while its children are being dragged over.)
            if (!draggingOverChild)
              this.closeParentMenus();
          } else if (aTimer == this.closeMenuTimer) {
            // Timer to close this menu after the drag exit.
            var popup = this._self;
            // if we are no more dragging we can leave the menu open to allow
            // for better D&D bookmark organization
            if (PlacesControllerDragHelper.getSession() &&
                !PlacesControllerDragHelper.draggingOverChildNode(popup.parentNode)) {
              popup.hidePopup();
              // Close any parent menus that aren't being dragged over;
              // otherwise they'll stay open because they couldn't close
              // while this menu was being dragged over.
              this.closeParentMenus();
            }
            this._closeMenuTimer = null;
          }
        },

        //  Helper function to close all parent menus of this menu,
        //  as long as none of the parent's children are currently being
        //  dragged over.
        closeParentMenus: function OF__closeParentMenus() {
          var popup = this._self;
          var parent = popup.parentNode;
          while (parent) {
            if (parent.localName == "menupopup" && parent._placesNode) {
              if (PlacesControllerDragHelper.draggingOverChildNode(parent.parentNode))
                break;
              parent.hidePopup();
            }
            parent = parent.parentNode;
          }
        },

        //  The mouse is no longer dragging over the stored menubutton.
        //  Close the menubutton, clear out drag styles, and clear all
        //  timers for opening/closing it.
        clear: function OF__clear() {
          if (this._folder.elt && this._folder.elt.lastChild) {
            if (!this._folder.elt.lastChild.hasAttribute("dragover"))
              this._folder.elt.lastChild.hidePopup();
            // remove menuactive style
            this._folder.elt.removeAttribute("_moz-menuactive");
            this._folder.elt = null;
          }
          if (this._folder.openTimer) {
            this._folder.openTimer.cancel();
            this._folder.openTimer = null;
          }
          if (this._folder.closeTimer) {
            this._folder.closeTimer.cancel();
            this._folder.closeTimer = null;
          }
        }
      })]]></field>

      <method name="_cleanupDragDetails">
        <body><![CDATA[
          // Called on dragend and drop.
          PlacesControllerDragHelper.currentDropTarget = null;
          this._rootView._draggedElt = null;
          this.removeAttribute("dragover");
          this.removeAttribute("dragstart");
          this._indicatorBar.hidden = true;
        ]]></body>
      </method>

    </implementation>

    <handlers>
      <handler event="DOMMenuItemActive"><![CDATA[
        let elt = event.target;
        if (elt.parentNode != this)
          return;

        if (this.AppConstants.platform === "macosx") {
          // XXX: The following check is a temporary hack until bug 420033 is
          // resolved.
          let parentElt = elt.parent;
          while (parentElt) {
            if (parentElt.id == "bookmarksMenuPopup" ||
                parentElt.id == "goPopup")
              return;

            parentElt = parentElt.parentNode;
          }
        }

        if (window.XULBrowserWindow) {
          let placesNode = elt._placesNode;

          var linkURI;
          if (placesNode && PlacesUtils.nodeIsURI(placesNode))
            linkURI = placesNode.uri;
          else if (elt.hasAttribute("targetURI"))
            linkURI = elt.getAttribute("targetURI");

          if (linkURI)
            window.XULBrowserWindow.setOverLink(linkURI, null);
        }
      ]]></handler>

      <handler event="DOMMenuItemInactive"><![CDATA[
        let elt = event.target;
        if (elt.parentNode != this)
          return;

        if (window.XULBrowserWindow)
          window.XULBrowserWindow.setOverLink("", null);
      ]]></handler>

      <handler event="dragstart"><![CDATA[
        let elt = event.target;
        if (!elt._placesNode)
          return;

        let draggedElt = elt._placesNode;

        // Force a copy action if parent node is a query or we are dragging a
        // not-removable node.
        if (!this._rootView.controller.canMoveNode(draggedElt))
          event.dataTransfer.effectAllowed = "copyLink";

        // Activate the view and cache the dragged element.
        this._rootView._draggedElt = draggedElt;
        this._rootView.controller.setDataTransfer(event);
        this.setAttribute("dragstart", "true");
        event.stopPropagation();
      ]]></handler>

      <handler event="drop"><![CDATA[
        PlacesControllerDragHelper.currentDropTarget = event.target;

        let dropPoint = this._getDropPoint(event);
        if (dropPoint && dropPoint.ip) {
          PlacesControllerDragHelper.onDrop(dropPoint.ip, event.dataTransfer)
                                    .catch(Cu.reportError);
          event.preventDefault();
        }

        this._cleanupDragDetails();
        event.stopPropagation();
      ]]></handler>

      <handler event="dragover"><![CDATA[
        PlacesControllerDragHelper.currentDropTarget = event.target;
        let dt = event.dataTransfer;

        let dropPoint = this._getDropPoint(event);
        if (!dropPoint || !dropPoint.ip ||
            !PlacesControllerDragHelper.canDrop(dropPoint.ip, dt)) {
          this._indicatorBar.hidden = true;
          event.stopPropagation();
          return;
        }

        // Mark this popup as being dragged over.
        this.setAttribute("dragover", "true");

        if (dropPoint.folderElt) {
          // We are dragging over a folder.
          // _overFolder should take the care of opening it on a timer.
          if (this._overFolder.elt &&
              this._overFolder.elt != dropPoint.folderElt) {
            // We are dragging over a new folder, let's clear old values
            this._overFolder.clear();
          }
          if (!this._overFolder.elt) {
            this._overFolder.elt = dropPoint.folderElt;
            // Create the timer to open this folder.
            this._overFolder.openTimer = this._overFolder
                                             .setTimer(this._overFolder.hoverTime);
          }
          // Since we are dropping into a folder set the corresponding style.
          dropPoint.folderElt.setAttribute("_moz-menuactive", true);
        } else {
          // We are not dragging over a folder.
          // Clear out old _overFolder information.
          this._overFolder.clear();
        }

        // Autoscroll the popup strip if we drag over the scroll buttons.
        let anonid = event.originalTarget.getAttribute("anonid");
        let scrollDir = 0;
        if (anonid == "scrollbutton-up") {
          scrollDir = -1;
        } else if (anonid == "scrollbutton-down") {
          scrollDir = 1;
        }
        if (scrollDir != 0) {
          this._scrollBox.scrollByIndex(scrollDir, true);
        }

        // Check if we should hide the drop indicator for this target.
        if (dropPoint.folderElt || this._hideDropIndicator(event)) {
          this._indicatorBar.hidden = true;
          event.preventDefault();
          event.stopPropagation();
          return;
        }

        // We should display the drop indicator relative to the arrowscrollbox.
        let sbo = this._scrollBox.scrollBoxObject;
        let newMarginTop = 0;
        if (scrollDir == 0) {
          let elt = this.firstChild;
          while (elt && event.screenY > elt.boxObject.screenY +
                                        elt.boxObject.height / 2)
            elt = elt.nextSibling;
          newMarginTop = elt ? elt.boxObject.screenY - sbo.screenY :
                               sbo.height;
        } else if (scrollDir == 1)
          newMarginTop = sbo.height;

        // Set the new marginTop based on arrowscrollbox.
        newMarginTop += sbo.y - this._scrollBox.boxObject.y;
        this._indicatorBar.firstChild.style.marginTop = newMarginTop + "px";
        this._indicatorBar.hidden = false;

        event.preventDefault();
        event.stopPropagation();
      ]]></handler>

      <handler event="dragexit"><![CDATA[
        PlacesControllerDragHelper.currentDropTarget = null;
        this.removeAttribute("dragover");

        // If we have not moved to a valid new target clear the drop indicator
        // this happens when moving out of the popup.
        let target = event.relatedTarget;
        if (!target || !this.contains(target))
          this._indicatorBar.hidden = true;

        // Close any folder being hovered over
        if (this._overFolder.elt) {
          this._overFolder.closeTimer = this._overFolder
                                            .setTimer(this._overFolder.hoverTime);
        }

        // The autoopened attribute is set when this folder was automatically
        // opened after the user dragged over it.  If this attribute is set,
        // auto-close the folder on drag exit.
        // We should also try to close this popup if the drag has started
        // from here, the timer will check if we are dragging over a child.
        if (this.hasAttribute("autoopened") ||
            this.hasAttribute("dragstart")) {
          this._overFolder.closeMenuTimer = this._overFolder
                                                .setTimer(this._overFolder.hoverTime);
        }

        event.stopPropagation();
      ]]></handler>

      <handler event="dragend"><![CDATA[
        this._cleanupDragDetails();
      ]]></handler>

    </handlers>
  </binding>

  <!-- Most of this is copied from the arrowpanel binding in popup.xml -->
  <binding id="places-popup-arrow"
           extends="chrome://browser/content/places/menu.xml#places-popup-base">
    <content flip="both" side="top" position="bottomcenter topright">
      <xul:vbox anonid="container" class="panel-arrowcontainer" flex="1"
               xbl:inherits="side,panelopen">
        <xul:box anonid="arrowbox" class="panel-arrowbox">
          <xul:image anonid="arrow" class="panel-arrow" xbl:inherits="side"/>
        </xul:box>
        <xul:box class="panel-arrowcontent" xbl:inherits="side,align,dir,orient,pack" flex="1">
          <xul:vbox class="menupopup-drop-indicator-bar" hidden="true">
            <xul:image class="menupopup-drop-indicator" mousethrough="always"/>
          </xul:vbox>
          <xul:arrowscrollbox class="popup-internal-box" flex="1" orient="vertical"
                              smoothscroll="false">
            <children/>
          </xul:arrowscrollbox>
        </xul:box>
      </xul:vbox>
    </content>

    <implementation>
      <constructor><![CDATA[
        this.style.pointerEvents = "none";
      ]]></constructor>
      <method name="adjustArrowPosition">
        <body><![CDATA[
          var arrow = document.getAnonymousElementByAttribute(this, "anonid", "arrow");

          var anchor = this.anchorNode;
          if (!anchor) {
            arrow.hidden = true;
            return;
          }

          var container = document.getAnonymousElementByAttribute(this, "anonid", "container");
          var arrowbox = document.getAnonymousElementByAttribute(this, "anonid", "arrowbox");

          var position = this.alignmentPosition;
          var offset = this.alignmentOffset;

          this.setAttribute("arrowposition", position);

          // if this panel has a "sliding" arrow, we may have previously set margins...
          arrowbox.style.removeProperty("transform");
          if (position.indexOf("start_") == 0 || position.indexOf("end_") == 0) {
            container.orient = "horizontal";
            arrowbox.orient = "vertical";
            if (position.indexOf("_after") > 0) {
              arrowbox.pack = "end";
            } else {
              arrowbox.pack = "start";
            }
            arrowbox.style.transform = "translate(0, " + -offset + "px)";

            // The assigned side stays the same regardless of direction.
            var isRTL = (window.getComputedStyle(this).direction == "rtl");

            if (position.indexOf("start_") == 0) {
              container.dir = "reverse";
              this.setAttribute("side", isRTL ? "left" : "right");
            } else {
              container.dir = "";
              this.setAttribute("side", isRTL ? "right" : "left");
            }
          } else if (position.indexOf("before_") == 0 || position.indexOf("after_") == 0) {
            container.orient = "";
            arrowbox.orient = "";
            if (position.indexOf("_end") > 0) {
              arrowbox.pack = "end";
            } else {
              arrowbox.pack = "start";
            }
            arrowbox.style.transform = "translate(" + -offset + "px, 0)";

            if (position.indexOf("before_") == 0) {
              container.dir = "reverse";
              this.setAttribute("side", "bottom");
            } else {
              container.dir = "";
              this.setAttribute("side", "top");
            }
          }

          arrow.hidden = false;
        ]]></body>
      </method>
    </implementation>

    <handlers>
      <handler event="popupshowing" phase="target"><![CDATA[
        this.adjustArrowPosition();
        this.setAttribute("animate", "open");
      ]]></handler>
      <handler event="popupshown" phase="target"><![CDATA[
        this.setAttribute("panelopen", "true");
        let disablePointerEvents;
        if (!this.hasAttribute("disablepointereventsfortransition")) {
          let container = document.getAnonymousElementByAttribute(this, "anonid", "container");
          let cs = getComputedStyle(container);
          let transitionProp = cs.transitionProperty;
          let transitionTime = parseFloat(cs.transitionDuration);
          disablePointerEvents = (transitionProp.includes("transform") ||
                                  transitionProp == "all") &&
                                 transitionTime > 0;
          this.setAttribute("disablepointereventsfortransition", disablePointerEvents);
        } else {
          disablePointerEvents = this.getAttribute("disablepointereventsfortransition") == "true";
        }
        if (!disablePointerEvents) {
          this.style.removeProperty("pointer-events");
        }
      ]]></handler>
      <handler event="transitionend"><![CDATA[
        if (event.originalTarget.getAttribute("anonid") == "container" &&
            (event.propertyName == "transform" || event.propertyName == "-moz-window-transform")) {
          this.style.removeProperty("pointer-events");
        }
      ]]></handler>
      <handler event="popuphiding" phase="target"><![CDATA[
        this.setAttribute("animate", "cancel");
      ]]></handler>
      <handler event="popuphidden" phase="target"><![CDATA[
        this.removeAttribute("panelopen");
        if (this.getAttribute("disablepointereventsfortransition") == "true") {
          this.style.pointerEvents = "none";
        }
        this.removeAttribute("animate");
      ]]></handler>
    </handlers>
  </binding>
</bindings>
PK
!<&���3chrome/browser/content/browser/places/organizer.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#searchFilter {
  width: 23em;
}
PK
!<�8���/chrome/browser/content/browser/places/places.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* import-globals-from editBookmarkOverlay.js */
// Via downloadsViewOverlay.xul -> allDownloadsViewOverlay.xul
/* import-globals-from ../../../../toolkit/content/contentAreaUtils.js */

ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/TelemetryStopwatch.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.defineModuleGetter(this, "MigrationUtils",
                               "resource:///modules/MigrationUtils.jsm");
ChromeUtils.defineModuleGetter(this, "BookmarkJSONUtils",
                               "resource://gre/modules/BookmarkJSONUtils.jsm");
ChromeUtils.defineModuleGetter(this, "PlacesBackups",
                               "resource://gre/modules/PlacesBackups.jsm");
ChromeUtils.defineModuleGetter(this, "DownloadUtils",
                               "resource://gre/modules/DownloadUtils.jsm");

const RESTORE_FILEPICKER_FILTER_EXT = "*.json;*.jsonlz4";
const HISTORY_LIBRARY_SEARCH_TELEMETRY = "PLACES_HISTORY_LIBRARY_SEARCH_TIME_MS";

var PlacesOrganizer = {
  _places: null,

  // IDs of fields from editBookmarkOverlay that should be hidden when infoBox
  // is minimal. IDs should be kept in sync with the IDs of the elements
  // observing additionalInfoBroadcaster.
  _additionalInfoFields: [
    "editBMPanel_descriptionRow",
    "editBMPanel_loadInSidebarCheckbox",
    "editBMPanel_keywordRow",
  ],

  _initFolderTree() {
    var leftPaneRoot = PlacesUIUtils.leftPaneFolderId;
    this._places.place = "place:excludeItems=1&expandQueries=0&folder=" + leftPaneRoot;
  },

  /**
   * Selects a left pane built-in item.
   *
   * @param {String} item The built-in item to select, may be one of (case sensitive):
   *                      AllBookmarks, BookmarksMenu, BookmarksToolbar,
   *                      History, Downloads, Tags, UnfiledBookmarks.
   */
  selectLeftPaneBuiltIn(item) {
    switch (item) {
      case "AllBookmarks":
      case "History":
      case "Downloads":
      case "Tags": {
        var itemId = PlacesUIUtils.leftPaneQueries[item];
        this._places.selectItems([itemId]);
        // Forcefully expand all-bookmarks
        if (item == "AllBookmarks" || item == "History")
          PlacesUtils.asContainer(this._places.selectedNode).containerOpen = true;
        break;
      }
      case "BookmarksMenu":
        this.selectLeftPaneContainerByHierarchy([
          PlacesUIUtils.leftPaneQueries.AllBookmarks,
          PlacesUtils.bookmarks.virtualMenuGuid
        ]);
        break;
      case "BookmarksToolbar":
        this.selectLeftPaneContainerByHierarchy([
          PlacesUIUtils.leftPaneQueries.AllBookmarks,
          PlacesUtils.bookmarks.virtualToolbarGuid
        ]);
        break;
      case "UnfiledBookmarks":
        this.selectLeftPaneContainerByHierarchy([
          PlacesUIUtils.leftPaneQueries.AllBookmarks,
          PlacesUtils.bookmarks.virtualUnfiledGuid
        ]);
        break;
      default:
        throw new Error(`Unrecognized item ${item} passed to selectLeftPaneRootItem`);
    }
  },

  /**
   * Opens a given hierarchy in the left pane, stopping at the last reachable
   * container. Note: item ids should be considered deprecated.
   *
   * @param aHierarchy A single container or an array of containers, sorted from
   *                   the outmost to the innermost in the hierarchy. Each
   *                   container may be either an item id, a Places URI string,
   *                   or a named query, like:
   *                   "BookmarksMenu", "BookmarksToolbar", "UnfiledBookmarks", "AllBookmarks".
   * @see PlacesUIUtils.leftPaneQueries for supported named queries.
   */
  selectLeftPaneContainerByHierarchy(aHierarchy) {
    if (!aHierarchy)
      throw new Error("Containers hierarchy not specified");
    let hierarchy = [].concat(aHierarchy);
    let selectWasSuppressed = this._places.view.selection.selectEventsSuppressed;
    if (!selectWasSuppressed)
      this._places.view.selection.selectEventsSuppressed = true;
    try {
      for (let container of hierarchy) {
        switch (typeof container) {
          case "number":
            this._places.selectItems([container], false);
            break;
          case "string":
            try {
              this.selectLeftPaneBuiltIn(container);
            } catch (ex) {
              if (container.substr(0, 6) == "place:") {
                this._places.selectPlaceURI(container);
              } else {
                // May be a guid.
                this._places.selectItems([container], false);
              }
            }
            break;
          default:
            throw new Error("Invalid container type found: " + container);
        }
        PlacesUtils.asContainer(this._places.selectedNode).containerOpen = true;
      }
    } finally {
      if (!selectWasSuppressed)
        this._places.view.selection.selectEventsSuppressed = false;
    }
  },

  init: function PO_init() {
    ContentArea.init();

    this._places = document.getElementById("placesList");
    this._initFolderTree();

    var leftPaneSelection = "AllBookmarks"; // default to all-bookmarks
    if (window.arguments && window.arguments[0])
      leftPaneSelection = window.arguments[0];

    this.selectLeftPaneContainerByHierarchy(leftPaneSelection);
    if (leftPaneSelection === "History") {
      let historyNode = this._places.selectedNode;
      if (historyNode.childCount > 0)
        this._places.selectNode(historyNode.getChild(0));
    }

    // clear the back-stack
    this._backHistory.splice(0, this._backHistory.length);
    document.getElementById("OrganizerCommand:Back").setAttribute("disabled", true);

    // Set up the search UI.
    PlacesSearchBox.init();

    window.addEventListener("AppCommand", this, true);

    if (AppConstants.platform === "macosx") {
      // 1. Map Edit->Find command to OrganizerCommand_find:all.  Need to map
      // both the menuitem and the Find key.
      let findMenuItem = document.getElementById("menu_find");
      findMenuItem.setAttribute("command", "OrganizerCommand_find:all");
      let findKey = document.getElementById("key_find");
      findKey.setAttribute("command", "OrganizerCommand_find:all");

      // 2. Disable some keybindings from browser.xul
      let elements = ["cmd_handleBackspace", "cmd_handleShiftBackspace"];
      for (let i = 0; i < elements.length; i++) {
        document.getElementById(elements[i]).setAttribute("disabled", "true");
      }
    }

    // remove the "Properties" context-menu item, we've our own details pane
    document.getElementById("placesContext")
            .removeChild(document.getElementById("placesContext_show:info"));

    if (!Services.policies.isAllowed("profileImport")) {
      document.getElementById("OrganizerCommand_browserImport").setAttribute("disabled", true);
    }

    ContentArea.focus();
  },

  QueryInterface: function PO_QueryInterface(aIID) {
    if (aIID.equals(Ci.nsIDOMEventListener) ||
        aIID.equals(Ci.nsISupports))
      return this;

    throw Cr.NS_NOINTERFACE;
  },

  handleEvent: function PO_handleEvent(aEvent) {
    if (aEvent.type != "AppCommand")
      return;

    aEvent.stopPropagation();
    switch (aEvent.command) {
      case "Back":
        if (this._backHistory.length > 0)
          this.back();
        break;
      case "Forward":
        if (this._forwardHistory.length > 0)
          this.forward();
        break;
      case "Search":
        PlacesSearchBox.findAll();
        break;
    }
  },

  destroy: function PO_destroy() {
  },

  _location: null,
  get location() {
    return this._location;
  },

  set location(aLocation) {
    if (!aLocation || this._location == aLocation)
      return aLocation;

    if (this.location) {
      this._backHistory.unshift(this.location);
      this._forwardHistory.splice(0, this._forwardHistory.length);
    }

    this._location = aLocation;
    this._places.selectPlaceURI(aLocation);

    if (!this._places.hasSelection) {
      // If no node was found for the given place: uri, just load it directly
      ContentArea.currentPlace = aLocation;
    }
    this.updateDetailsPane();

    // update navigation commands
    if (this._backHistory.length == 0)
      document.getElementById("OrganizerCommand:Back").setAttribute("disabled", true);
    else
      document.getElementById("OrganizerCommand:Back").removeAttribute("disabled");
    if (this._forwardHistory.length == 0)
      document.getElementById("OrganizerCommand:Forward").setAttribute("disabled", true);
    else
      document.getElementById("OrganizerCommand:Forward").removeAttribute("disabled");

    return aLocation;
  },

  _backHistory: [],
  _forwardHistory: [],

  back: function PO_back() {
    this._forwardHistory.unshift(this.location);
    var historyEntry = this._backHistory.shift();
    this._location = null;
    this.location = historyEntry;
  },
  forward: function PO_forward() {
    this._backHistory.unshift(this.location);
    var historyEntry = this._forwardHistory.shift();
    this._location = null;
    this.location = historyEntry;
  },

  /**
   * Called when a place folder is selected in the left pane.
   * @param   resetSearchBox
   *          true if the search box should also be reset, false otherwise.
   *          The search box should be reset when a new folder in the left
   *          pane is selected; the search scope and text need to be cleared in
   *          preparation for the new folder.  Note that if the user manually
   *          resets the search box, either by clicking its reset button or by
   *          deleting its text, this will be false.
   */
  _cachedLeftPaneSelectedURI: null,
  onPlaceSelected: function PO_onPlaceSelected(resetSearchBox) {
    // Don't change the right-hand pane contents when there's no selection.
    if (!this._places.hasSelection)
      return;

    let node = this._places.selectedNode;
    let placeURI = node.uri;

    // If either the place of the content tree in the right pane has changed or
    // the user cleared the search box, update the place, hide the search UI,
    // and update the back/forward buttons by setting location.
    if (ContentArea.currentPlace != placeURI || !resetSearchBox) {
      ContentArea.currentPlace = placeURI;
      this.location = placeURI;
    }

    // When we invalidate a container we use suppressSelectionEvent, when it is
    // unset a select event is fired, in many cases the selection did not really
    // change, so we should check for it, and return early in such a case. Note
    // that we cannot return any earlier than this point, because when
    // !resetSearchBox, we need to update location and hide the UI as above,
    // even though the selection has not changed.
    if (placeURI == this._cachedLeftPaneSelectedURI)
      return;
    this._cachedLeftPaneSelectedURI = placeURI;

    // At this point, resetSearchBox is true, because the left pane selection
    // has changed; otherwise we would have returned earlier.

    PlacesSearchBox.searchFilter.reset();
    this._setSearchScopeForNode(node);
    this.updateDetailsPane();
  },

  /**
   * Sets the search scope based on aNode's properties.
   * @param   aNode
   *          the node to set up scope from
   */
  _setSearchScopeForNode: function PO__setScopeForNode(aNode) {
    let itemId = aNode.itemId;

    if (PlacesUtils.nodeIsHistoryContainer(aNode) ||
        itemId == PlacesUIUtils.leftPaneQueries.History) {
      PlacesQueryBuilder.setScope("history");
    } else if (itemId == PlacesUIUtils.leftPaneQueries.Downloads) {
      PlacesQueryBuilder.setScope("downloads");
    } else {
      // Default to All Bookmarks for all other nodes, per bug 469437.
      PlacesQueryBuilder.setScope("bookmarks");
    }
  },

  /**
   * Handle clicks on the places list.
   * Single Left click, right click or modified click do not result in any
   * special action, since they're related to selection.
   * @param   aEvent
   *          The mouse event.
   */
  onPlacesListClick: function PO_onPlacesListClick(aEvent) {
    // Only handle clicks on tree children.
    if (aEvent.target.localName != "treechildren")
      return;

    let node = this._places.selectedNode;
    if (node) {
      let middleClick = aEvent.button == 1 && aEvent.detail == 1;
      if (middleClick && PlacesUtils.nodeIsContainer(node)) {
        // The command execution function will take care of seeing if the
        // selection is a folder or a different container type, and will
        // load its contents in tabs.
        PlacesUIUtils.openContainerNodeInTabs(node, aEvent, this._places);
      }
    }
  },

  /**
   * Handle focus changes on the places list and the current content view.
   */
  updateDetailsPane: function PO_updateDetailsPane() {
    if (!ContentArea.currentViewOptions.showDetailsPane)
      return;
    let view = PlacesUIUtils.getViewForNode(document.activeElement);
    if (view) {
      let selectedNodes = view.selectedNode ?
                          [view.selectedNode] : view.selectedNodes;
      this._fillDetailsPane(selectedNodes);
    }
  },

  openFlatContainer: function PO_openFlatContainerFlatContainer(aContainer) {
    if (aContainer.itemId != -1) {
      PlacesUtils.asContainer(this._places.selectedNode).containerOpen = true;
      this._places.selectItems([aContainer.itemId], false);
    } else if (PlacesUtils.nodeIsQuery(aContainer)) {
      this._places.selectPlaceURI(aContainer.uri);
    }
  },

  /**
   * Returns the options associated with the query currently loaded in the
   * main places pane.
   */
  getCurrentOptions: function PO_getCurrentOptions() {
    return PlacesUtils.asQuery(ContentArea.currentView.result.root).queryOptions;
  },

  /**
   * Returns the queries associated with the query currently loaded in the
   * main places pane.
   */
  getCurrentQueries: function PO_getCurrentQueries() {
    return PlacesUtils.asQuery(ContentArea.currentView.result.root).getQueries();
  },

  /**
   * Show the migration wizard for importing passwords,
   * cookies, history, preferences, and bookmarks.
   */
  importFromBrowser: function PO_importFromBrowser() {
    // We pass in the type of source we're using for use in telemetry:
    MigrationUtils.showMigrationWizard(window, [MigrationUtils.MIGRATION_ENTRYPOINT_PLACES]);
  },

  /**
   * Open a file-picker and import the selected file into the bookmarks store
   */
  importFromFile: function PO_importFromFile() {
    let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
    let fpCallback = function fpCallback_done(aResult) {
      if (aResult != Ci.nsIFilePicker.returnCancel && fp.fileURL) {
        ChromeUtils.import("resource://gre/modules/BookmarkHTMLUtils.jsm");
        BookmarkHTMLUtils.importFromURL(fp.fileURL.spec, false)
                         .catch(Cu.reportError);
      }
    };

    fp.init(window, PlacesUIUtils.getString("SelectImport"),
            Ci.nsIFilePicker.modeOpen);
    fp.appendFilters(Ci.nsIFilePicker.filterHTML);
    fp.open(fpCallback);
  },

  /**
   * Allows simple exporting of bookmarks.
   */
  exportBookmarks: function PO_exportBookmarks() {
    let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
    let fpCallback = function fpCallback_done(aResult) {
      if (aResult != Ci.nsIFilePicker.returnCancel) {
        ChromeUtils.import("resource://gre/modules/BookmarkHTMLUtils.jsm");
        BookmarkHTMLUtils.exportToFile(fp.file.path)
                         .catch(Cu.reportError);
      }
    };

    fp.init(window, PlacesUIUtils.getString("EnterExport"),
            Ci.nsIFilePicker.modeSave);
    fp.appendFilters(Ci.nsIFilePicker.filterHTML);
    fp.defaultString = "bookmarks.html";
    fp.open(fpCallback);
  },

  /**
   * Populates the restore menu with the dates of the backups available.
   */
  populateRestoreMenu: function PO_populateRestoreMenu() {
    let restorePopup = document.getElementById("fileRestorePopup");

    const dtOptions = {
      dateStyle: "long"
    };
    let dateFormatter = new Services.intl.DateTimeFormat(undefined, dtOptions);

    // Remove existing menu items.  Last item is the restoreFromFile item.
    while (restorePopup.childNodes.length > 1)
      restorePopup.firstChild.remove();

    (async function() {
      let backupFiles = await PlacesBackups.getBackupFiles();
      if (backupFiles.length == 0)
        return;

      // Populate menu with backups.
      for (let i = 0; i < backupFiles.length; i++) {
        let fileSize = (await OS.File.stat(backupFiles[i])).size;
        let [size, unit] = DownloadUtils.convertByteUnits(fileSize);
        let sizeString = PlacesUtils.getFormattedString("backupFileSizeText",
                                                        [size, unit]);
        let sizeInfo;
        let bookmarkCount = PlacesBackups.getBookmarkCountForFile(backupFiles[i]);
        if (bookmarkCount != null) {
          sizeInfo = " (" + sizeString + " - " +
                     PlacesUIUtils.getPluralString("detailsPane.itemsCountLabel",
                                                   bookmarkCount,
                                                   [bookmarkCount]) +
                     ")";
        } else {
          sizeInfo = " (" + sizeString + ")";
        }

        let backupDate = PlacesBackups.getDateForFile(backupFiles[i]);
        let m = restorePopup.insertBefore(document.createElement("menuitem"),
                                          document.getElementById("restoreFromFile"));
        m.setAttribute("label", dateFormatter.format(backupDate) + sizeInfo);
        m.setAttribute("value", OS.Path.basename(backupFiles[i]));
        m.setAttribute("oncommand",
                       "PlacesOrganizer.onRestoreMenuItemClick(this);");
      }

      // Add the restoreFromFile item.
      restorePopup.insertBefore(document.createElement("menuseparator"),
                                document.getElementById("restoreFromFile"));
    })();
  },

  /**
   * Called when a menuitem is selected from the restore menu.
   */
  async onRestoreMenuItemClick(aMenuItem) {
    let backupName = aMenuItem.getAttribute("value");
    let backupFilePaths = await PlacesBackups.getBackupFiles();
    for (let backupFilePath of backupFilePaths) {
      if (OS.Path.basename(backupFilePath) == backupName) {
        PlacesOrganizer.restoreBookmarksFromFile(backupFilePath);
        break;
      }
    }
  },

  /**
   * Called when 'Choose File...' is selected from the restore menu.
   * Prompts for a file and restores bookmarks to those in the file.
   */
  onRestoreBookmarksFromFile: function PO_onRestoreBookmarksFromFile() {
    let backupsDir = Services.dirsvc.get("Desk", Ci.nsIFile);
    let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
    let fpCallback = aResult => {
      if (aResult != Ci.nsIFilePicker.returnCancel) {
        this.restoreBookmarksFromFile(fp.file.path);
      }
    };

    fp.init(window, PlacesUIUtils.getString("bookmarksRestoreTitle"),
            Ci.nsIFilePicker.modeOpen);
    fp.appendFilter(PlacesUIUtils.getString("bookmarksRestoreFilterName"),
                    RESTORE_FILEPICKER_FILTER_EXT);
    fp.appendFilters(Ci.nsIFilePicker.filterAll);
    fp.displayDirectory = backupsDir;
    fp.open(fpCallback);
  },

  /**
   * Restores bookmarks from a JSON file.
   */
  restoreBookmarksFromFile: function PO_restoreBookmarksFromFile(aFilePath) {
    // check file extension
    if (!aFilePath.toLowerCase().endsWith("json") &&
        !aFilePath.toLowerCase().endsWith("jsonlz4")) {
      this._showErrorAlert(PlacesUIUtils.getString("bookmarksRestoreFormatError"));
      return;
    }

    // confirm ok to delete existing bookmarks
    if (!Services.prompt.confirm(null,
           PlacesUIUtils.getString("bookmarksRestoreAlertTitle"),
           PlacesUIUtils.getString("bookmarksRestoreAlert")))
      return;

    (async function() {
      try {
        await BookmarkJSONUtils.importFromFile(aFilePath, true);
      } catch (ex) {
        PlacesOrganizer._showErrorAlert(PlacesUIUtils.getString("bookmarksRestoreParseError"));
      }
    })();
  },

  _showErrorAlert: function PO__showErrorAlert(aMsg) {
    var brandShortName = document.getElementById("brandStrings").
                                  getString("brandShortName");

    Services.prompt.alert(window, brandShortName, aMsg);
  },

  /**
   * Backup bookmarks to desktop, auto-generate a filename with a date.
   * The file is a JSON serialization of bookmarks, tags and any annotations
   * of those items.
   */
  backupBookmarks: function PO_backupBookmarks() {
    let backupsDir = Services.dirsvc.get("Desk", Ci.nsIFile);
    let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
    let fpCallback = function fpCallback_done(aResult) {
      if (aResult != Ci.nsIFilePicker.returnCancel) {
        // There is no OS.File version of the filepicker yet (Bug 937812).
        PlacesBackups.saveBookmarksToJSONFile(fp.file.path)
                     .catch(Cu.reportError);
      }
    };

    fp.init(window, PlacesUIUtils.getString("bookmarksBackupTitle"),
            Ci.nsIFilePicker.modeSave);
    fp.appendFilter(PlacesUIUtils.getString("bookmarksRestoreFilterName"),
                    RESTORE_FILEPICKER_FILTER_EXT);
    fp.defaultString = PlacesBackups.getFilenameForDate();
    fp.defaultExtension = "json";
    fp.displayDirectory = backupsDir;
    fp.open(fpCallback);
  },

  _detectAndSetDetailsPaneMinimalState:
  function PO__detectAndSetDetailsPaneMinimalState(aNode) {
    /**
     * The details of simple folder-items (as opposed to livemarks) or the
     * of livemark-children are not likely to fill the infoBox anyway,
     * thus we remove the "More/Less" button and show all details.
     *
     * the wasminimal attribute here is used to persist the "more/less"
     * state in a bookmark->folder->bookmark scenario.
     */
    var infoBox = document.getElementById("infoBox");
    var infoBoxExpanderWrapper = document.getElementById("infoBoxExpanderWrapper");
    var additionalInfoBroadcaster = document.getElementById("additionalInfoBroadcaster");

    if (!aNode) {
      infoBoxExpanderWrapper.hidden = true;
      return;
    }
    if (aNode.itemId != -1 &&
        PlacesUtils.nodeIsFolder(aNode) && !aNode._feedURI) {
      if (infoBox.getAttribute("minimal") == "true")
        infoBox.setAttribute("wasminimal", "true");
      infoBox.removeAttribute("minimal");
      infoBoxExpanderWrapper.hidden = true;
    } else {
      if (infoBox.getAttribute("wasminimal") == "true")
        infoBox.setAttribute("minimal", "true");
      infoBox.removeAttribute("wasminimal");
      infoBoxExpanderWrapper.hidden =
        this._additionalInfoFields.every(id =>
          document.getElementById(id).collapsed);
    }
    additionalInfoBroadcaster.hidden = infoBox.getAttribute("minimal") == "true";
  },

  // NOT YET USED
  updateThumbnailProportions: function PO_updateThumbnailProportions() {
    var previewBox = document.getElementById("previewBox");
    var canvas = document.getElementById("itemThumbnail");
    var height = previewBox.boxObject.height;
    var width = height * (screen.width / screen.height);
    canvas.width = width;
    canvas.height = height;
  },

  _fillDetailsPane: function PO__fillDetailsPane(aNodeList) {
    var infoBox = document.getElementById("infoBox");
    var detailsDeck = document.getElementById("detailsDeck");

    // Make sure the infoBox UI is visible if we need to use it, we hide it
    // below when we don't.
    infoBox.hidden = false;
    let selectedNode = aNodeList.length == 1 ? aNodeList[0] : null;

    // If a textbox within a panel is focused, force-blur it so its contents
    // are saved
    if (gEditItemOverlay.itemId != -1) {
      var focusedElement = document.commandDispatcher.focusedElement;
      if ((focusedElement instanceof HTMLInputElement ||
           focusedElement instanceof HTMLTextAreaElement) &&
          /^editBMPanel.*/.test(focusedElement.parentNode.parentNode.id))
        focusedElement.blur();

      // don't update the panel if we are already editing this node unless we're
      // in multi-edit mode
      if (selectedNode) {
        let concreteId = PlacesUtils.getConcreteItemId(selectedNode);
        var nodeIsSame = gEditItemOverlay.itemId == selectedNode.itemId ||
                         gEditItemOverlay.itemId == concreteId ||
                         (selectedNode.itemId == -1 && gEditItemOverlay.uri &&
                          gEditItemOverlay.uri == selectedNode.uri);
        if (nodeIsSame && detailsDeck.selectedIndex == 1 &&
            !gEditItemOverlay.multiEdit)
          return;
      }
    }

    // Clean up the panel before initing it again.
    gEditItemOverlay.uninitPanel(false);

    if (selectedNode && !PlacesUtils.nodeIsSeparator(selectedNode)) {
      detailsDeck.selectedIndex = 1;

      gEditItemOverlay.initPanel({ node: selectedNode,
                                   hiddenRows: ["folderPicker"] });

      this._detectAndSetDetailsPaneMinimalState(selectedNode);
    } else if (!selectedNode && aNodeList[0]) {
      if (aNodeList.every(PlacesUtils.nodeIsURI)) {
        let uris = aNodeList.map(node => Services.io.newURI(node.uri));
        detailsDeck.selectedIndex = 1;
        gEditItemOverlay.initPanel({ uris,
                                     hiddenRows: ["folderPicker",
                                                  "loadInSidebar",
                                                  "location",
                                                  "keyword",
                                                  "description",
                                                  "name"]});
        this._detectAndSetDetailsPaneMinimalState(selectedNode);
      } else {
        detailsDeck.selectedIndex = 0;
        let selectItemDesc = document.getElementById("selectItemDescription");
        let itemsCountLabel = document.getElementById("itemsCountText");
        selectItemDesc.hidden = false;
        itemsCountLabel.value =
          PlacesUIUtils.getPluralString("detailsPane.itemsCountLabel",
                                        aNodeList.length, [aNodeList.length]);
        infoBox.hidden = true;
      }
    } else {
      detailsDeck.selectedIndex = 0;
      infoBox.hidden = true;
      let selectItemDesc = document.getElementById("selectItemDescription");
      let itemsCountLabel = document.getElementById("itemsCountText");
      let itemsCount = 0;
      if (ContentArea.currentView.result) {
        let rootNode = ContentArea.currentView.result.root;
        if (rootNode.containerOpen)
          itemsCount = rootNode.childCount;
      }
      if (itemsCount == 0) {
        selectItemDesc.hidden = true;
        itemsCountLabel.value = PlacesUIUtils.getString("detailsPane.noItems");
      } else {
        selectItemDesc.hidden = false;
        itemsCountLabel.value =
          PlacesUIUtils.getPluralString("detailsPane.itemsCountLabel",
                                        itemsCount, [itemsCount]);
      }
    }
  },

  // NOT YET USED
  _updateThumbnail: function PO__updateThumbnail() {
    var bo = document.getElementById("previewBox").boxObject;
    var width  = bo.width;
    var height = bo.height;

    var canvas = document.getElementById("itemThumbnail");
    var ctx = canvas.getContext("2d");
    var notAvailableText = canvas.getAttribute("notavailabletext");
    ctx.save();
    ctx.fillStyle = "-moz-Dialog";
    ctx.fillRect(0, 0, width, height);
    ctx.translate(width / 2, height / 2);

    ctx.fillStyle = "GrayText";
    ctx.mozTextStyle = "12pt sans serif";
    var len = ctx.mozMeasureText(notAvailableText);
    ctx.translate(-len / 2, 0);
    ctx.mozDrawText(notAvailableText);
    ctx.restore();
  },

  toggleAdditionalInfoFields: function PO_toggleAdditionalInfoFields() {
    var infoBox = document.getElementById("infoBox");
    var infoBoxExpander = document.getElementById("infoBoxExpander");
    var infoBoxExpanderLabel = document.getElementById("infoBoxExpanderLabel");
    var additionalInfoBroadcaster = document.getElementById("additionalInfoBroadcaster");

    if (infoBox.getAttribute("minimal") == "true") {
      infoBox.removeAttribute("minimal");
      infoBoxExpanderLabel.value = infoBoxExpanderLabel.getAttribute("lesslabel");
      infoBoxExpanderLabel.accessKey = infoBoxExpanderLabel.getAttribute("lessaccesskey");
      infoBoxExpander.className = "expander-up";
      additionalInfoBroadcaster.removeAttribute("hidden");
    } else {
      infoBox.setAttribute("minimal", "true");
      infoBoxExpanderLabel.value = infoBoxExpanderLabel.getAttribute("morelabel");
      infoBoxExpanderLabel.accessKey = infoBoxExpanderLabel.getAttribute("moreaccesskey");
      infoBoxExpander.className = "expander-down";
      additionalInfoBroadcaster.setAttribute("hidden", "true");
    }
  },
};

/**
 * A set of utilities relating to search within Bookmarks and History.
 */
var PlacesSearchBox = {

  /**
   * The Search text field
   */
  get searchFilter() {
    return document.getElementById("searchFilter");
  },

  /**
   * Folders to include when searching.
   */
  _folders: [],
  get folders() {
    if (this._folders.length == 0) {
      this._folders.push(PlacesUtils.bookmarksMenuFolderId,
                         PlacesUtils.unfiledBookmarksFolderId,
                         PlacesUtils.toolbarFolderId,
                         PlacesUtils.mobileFolderId);
    }
    return this._folders;
  },
  set folders(aFolders) {
    this._folders = aFolders;
    return aFolders;
  },

  /**
   * Run a search for the specified text, over the collection specified by
   * the dropdown arrow. The default is all bookmarks, but can be
   * localized to the active collection.
   * @param   filterString
   *          The text to search for.
   */
  search: function PSB_search(filterString) {
    var PO = PlacesOrganizer;
    // If the user empties the search box manually, reset it and load all
    // contents of the current scope.
    // XXX this might be to jumpy, maybe should search for "", so results
    // are ungrouped, and search box not reset
    if (filterString == "") {
      PO.onPlaceSelected(false);
      return;
    }

    let currentView = ContentArea.currentView;

    // Search according to the current scope, which was set by
    // PQB_setScope()
    switch (PlacesSearchBox.filterCollection) {
      case "bookmarks":
        currentView.applyFilter(filterString, this.folders);
        break;
      case "history": {
        let currentOptions = PO.getCurrentOptions();
        if (currentOptions.queryType != Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY) {
          let query = PlacesUtils.history.getNewQuery();
          query.searchTerms = filterString;
          let options = currentOptions.clone();
          // Make sure we're getting uri results.
          options.resultType = currentOptions.RESULTS_AS_URI;
          options.queryType = Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY;
          options.includeHidden = true;
          currentView.load([query], options);
        } else {
          TelemetryStopwatch.start(HISTORY_LIBRARY_SEARCH_TELEMETRY);
          currentView.applyFilter(filterString, null, true);
          TelemetryStopwatch.finish(HISTORY_LIBRARY_SEARCH_TELEMETRY);
        }
        break;
      }
      case "downloads": {
        // The new downloads view doesn't use places for searching downloads.
        currentView.searchTerm = filterString;
        break;
      }
      default:
        throw "Invalid filterCollection on search";
    }

    // Update the details panel
    PlacesOrganizer.updateDetailsPane();
  },

  /**
   * Finds across all history, downloads or all bookmarks.
   */
  findAll: function PSB_findAll() {
    switch (this.filterCollection) {
      case "history":
        PlacesQueryBuilder.setScope("history");
        break;
      case "downloads":
        PlacesQueryBuilder.setScope("downloads");
        break;
      default:
        PlacesQueryBuilder.setScope("bookmarks");
        break;
    }
    this.focus();
  },

  /**
   * Updates the display with the title of the current collection.
   * @param   aTitle
   *          The title of the current collection.
   */
  updateCollectionTitle: function PSB_updateCollectionTitle(aTitle) {
    let title = "";
    switch (this.filterCollection) {
      case "history":
        title = PlacesUIUtils.getString("searchHistory");
        break;
      case "downloads":
        title = PlacesUIUtils.getString("searchDownloads");
        break;
      default:
        title = PlacesUIUtils.getString("searchBookmarks");
    }
    this.searchFilter.placeholder = title;
  },

  /**
   * Gets/sets the active collection from the dropdown menu.
   */
  get filterCollection() {
    return this.searchFilter.getAttribute("collection");
  },
  set filterCollection(collectionName) {
    if (collectionName == this.filterCollection)
      return collectionName;

    this.searchFilter.setAttribute("collection", collectionName);
    this.updateCollectionTitle();

    return collectionName;
  },

  /**
   * Focus the search box
   */
  focus: function PSB_focus() {
    this.searchFilter.focus();
  },

  /**
   * Set up the gray text in the search bar as the Places View loads.
   */
  init: function PSB_init() {
    if (Services.prefs.getBoolPref("browser.urlbar.clickSelectsAll", false)) {
      this.searchFilter.setAttribute("clickSelectsAll", true);
    }
    this.updateCollectionTitle();
  },

  /**
   * Gets or sets the text shown in the Places Search Box
   */
  get value() {
    return this.searchFilter.value;
  },
  set value(value) {
    return this.searchFilter.value = value;
  },
};

/**
 * Functions and data for advanced query builder
 */
var PlacesQueryBuilder = {

  queries: [],
  queryOptions: null,

  /**
   * Sets the search scope.  This can be called when no search is active, and
   * in that case, when the user does begin a search aScope will be used (see
   * PSB_search()).  If there is an active search, it's performed again to
   * update the content tree.
   * @param   aScope
   *          The search scope: "bookmarks", "collection", "downloads" or
   *          "history".
   */
  setScope: function PQB_setScope(aScope) {
    // Determine filterCollection, folders, and scopeButtonId based on aScope.
    var filterCollection;
    var folders = [];
    switch (aScope) {
      case "history":
        filterCollection = "history";
        break;
      case "bookmarks":
        filterCollection = "bookmarks";
        folders.push(PlacesUtils.bookmarksMenuFolderId,
                     PlacesUtils.toolbarFolderId,
                     PlacesUtils.unfiledBookmarksFolderId,
                     PlacesUtils.mobileFolderId);
        break;
      case "downloads":
        filterCollection = "downloads";
        break;
      default:
        throw "Invalid search scope";
    }

    // Update the search box.  Re-search if there's an active search.
    PlacesSearchBox.filterCollection = filterCollection;
    PlacesSearchBox.folders = folders;
    var searchStr = PlacesSearchBox.searchFilter.value;
    if (searchStr)
      PlacesSearchBox.search(searchStr);
  }
};

/**
 * Population and commands for the View Menu.
 */
var ViewMenu = {
  /**
   * Removes content generated previously from a menupopup.
   * @param   popup
   *          The popup that contains the previously generated content.
   * @param   startID
   *          The id attribute of an element that is the start of the
   *          dynamically generated region - remove elements after this
   *          item only.
   *          Must be contained by popup. Can be null (in which case the
   *          contents of popup are removed).
   * @param   endID
   *          The id attribute of an element that is the end of the
   *          dynamically generated region - remove elements up to this
   *          item only.
   *          Must be contained by popup. Can be null (in which case all
   *          items until the end of the popup will be removed). Ignored
   *          if startID is null.
   * @returns The element for the caller to insert new items before,
   *          null if the caller should just append to the popup.
   */
  _clean: function VM__clean(popup, startID, endID) {
    if (endID)
      NS_ASSERT(startID, "meaningless to have valid endID and null startID");
    if (startID) {
      var startElement = document.getElementById(startID);
      NS_ASSERT(startElement.parentNode ==
                popup, "startElement is not in popup");
      NS_ASSERT(startElement,
                "startID does not correspond to an existing element");
      var endElement = null;
      if (endID) {
        endElement = document.getElementById(endID);
        NS_ASSERT(endElement.parentNode == popup,
                  "endElement is not in popup");
        NS_ASSERT(endElement,
                  "endID does not correspond to an existing element");
      }
      while (startElement.nextSibling != endElement)
        popup.removeChild(startElement.nextSibling);
      return endElement;
    }
    while (popup.hasChildNodes()) {
      popup.firstChild.remove();
    }
    return null;
  },

  /**
   * Fills a menupopup with a list of columns
   * @param   event
   *          The popupshowing event that invoked this function.
   * @param   startID
   *          see _clean
   * @param   endID
   *          see _clean
   * @param   type
   *          the type of the menuitem, e.g. "radio" or "checkbox".
   *          Can be null (no-type).
   *          Checkboxes are checked if the column is visible.
   * @param   propertyPrefix
   *          If propertyPrefix is non-null:
   *          propertyPrefix + column ID + ".label" will be used to get the
   *          localized label string.
   *          propertyPrefix + column ID + ".accesskey" will be used to get the
   *          localized accesskey.
   *          If propertyPrefix is null, the column label is used as label and
   *          no accesskey is assigned.
   */
  fillWithColumns: function VM_fillWithColumns(event, startID, endID, type, propertyPrefix) {
    var popup = event.target;
    var pivot = this._clean(popup, startID, endID);

    var content = document.getElementById("placeContent");
    var columns = content.columns;
    for (var i = 0; i < columns.count; ++i) {
      var column = columns.getColumnAt(i).element;
      var menuitem = document.createElement("menuitem");
      menuitem.id = "menucol_" + column.id;
      menuitem.column = column;
      var label = column.getAttribute("label");
      if (propertyPrefix) {
        var menuitemPrefix = propertyPrefix;
        // for string properties, use "name" as the id, instead of "title"
        // see bug #386287 for details
        var columnId = column.getAttribute("anonid");
        menuitemPrefix += columnId == "title" ? "name" : columnId;
        label = PlacesUIUtils.getString(menuitemPrefix + ".label");
        var accesskey = PlacesUIUtils.getString(menuitemPrefix + ".accesskey");
        menuitem.setAttribute("accesskey", accesskey);
      }
      menuitem.setAttribute("label", label);
      if (type == "radio") {
        menuitem.setAttribute("type", "radio");
        menuitem.setAttribute("name", "columns");
        // This column is the sort key. Its item is checked.
        if (column.getAttribute("sortDirection") != "") {
          menuitem.setAttribute("checked", "true");
        }
      } else if (type == "checkbox") {
        menuitem.setAttribute("type", "checkbox");
        // Cannot uncheck the primary column.
        if (column.getAttribute("primary") == "true")
          menuitem.setAttribute("disabled", "true");
        // Items for visible columns are checked.
        if (!column.hidden)
          menuitem.setAttribute("checked", "true");
      }
      if (pivot)
        popup.insertBefore(menuitem, pivot);
      else
        popup.appendChild(menuitem);
    }
    event.stopPropagation();
  },

  /**
   * Set up the content of the view menu.
   */
  populateSortMenu: function VM_populateSortMenu(event) {
    this.fillWithColumns(event, "viewUnsorted", "directionSeparator", "radio", "view.sortBy.1.");

    var sortColumn = this._getSortColumn();
    var viewSortAscending = document.getElementById("viewSortAscending");
    var viewSortDescending = document.getElementById("viewSortDescending");
    // We need to remove an existing checked attribute because the unsorted
    // menu item is not rebuilt every time we open the menu like the others.
    var viewUnsorted = document.getElementById("viewUnsorted");
    if (!sortColumn) {
      viewSortAscending.removeAttribute("checked");
      viewSortDescending.removeAttribute("checked");
      viewUnsorted.setAttribute("checked", "true");
    } else if (sortColumn.getAttribute("sortDirection") == "ascending") {
      viewSortAscending.setAttribute("checked", "true");
      viewSortDescending.removeAttribute("checked");
      viewUnsorted.removeAttribute("checked");
    } else if (sortColumn.getAttribute("sortDirection") == "descending") {
      viewSortDescending.setAttribute("checked", "true");
      viewSortAscending.removeAttribute("checked");
      viewUnsorted.removeAttribute("checked");
    }
  },

  /**
   * Shows/Hides a tree column.
   * @param   element
   *          The menuitem element for the column
   */
  showHideColumn: function VM_showHideColumn(element) {
    var column = element.column;

    var splitter = column.nextSibling;
    if (splitter && splitter.localName != "splitter")
      splitter = null;

    if (element.getAttribute("checked") == "true") {
      column.setAttribute("hidden", "false");
      if (splitter)
        splitter.removeAttribute("hidden");
    } else {
      column.setAttribute("hidden", "true");
      if (splitter)
        splitter.setAttribute("hidden", "true");
    }
  },

  /**
   * Gets the last column that was sorted.
   * @returns  the currently sorted column, null if there is no sorted column.
   */
  _getSortColumn: function VM__getSortColumn() {
    var content = document.getElementById("placeContent");
    var cols = content.columns;
    for (var i = 0; i < cols.count; ++i) {
      var column = cols.getColumnAt(i).element;
      var sortDirection = column.getAttribute("sortDirection");
      if (sortDirection == "ascending" || sortDirection == "descending")
        return column;
    }
    return null;
  },

  /**
   * Sorts the view by the specified column.
   * @param   aColumn
   *          The colum that is the sort key. Can be null - the
   *          current sort column or the title column will be used.
   * @param   aDirection
   *          The direction to sort - "ascending" or "descending".
   *          Can be null - the last direction or descending will be used.
   *
   * If both aColumnID and aDirection are null, the view will be unsorted.
   */
  setSortColumn: function VM_setSortColumn(aColumn, aDirection) {
    var result = document.getElementById("placeContent").result;
    if (!aColumn && !aDirection) {
      result.sortingMode = Ci.nsINavHistoryQueryOptions.SORT_BY_NONE;
      return;
    }

    var columnId;
    if (aColumn) {
      columnId = aColumn.getAttribute("anonid");
      if (!aDirection) {
        let sortColumn = this._getSortColumn();
        if (sortColumn)
          aDirection = sortColumn.getAttribute("sortDirection");
      }
    } else {
      let sortColumn = this._getSortColumn();
      columnId = sortColumn ? sortColumn.getAttribute("anonid") : "title";
    }

    // This maps the possible values of columnId (i.e., anonid's of treecols in
    // placeContent) to the default sortingMode and sortingAnnotation values for
    // each column.
    //   key:  Sort key in the name of one of the
    //         nsINavHistoryQueryOptions.SORT_BY_* constants
    //   dir:  Default sort direction to use if none has been specified
    //   anno: The annotation to sort by, if key is "ANNOTATION"
    var colLookupTable = {
      title:        { key: "TITLE",        dir: "ascending"  },
      tags:         { key: "TAGS",         dir: "ascending"  },
      url:          { key: "URI",          dir: "ascending"  },
      date:         { key: "DATE",         dir: "descending" },
      visitCount:   { key: "VISITCOUNT",   dir: "descending" },
      dateAdded:    { key: "DATEADDED",    dir: "descending" },
      lastModified: { key: "LASTMODIFIED", dir: "descending" },
      description:  { key: "ANNOTATION",
                      dir: "ascending",
                      anno: PlacesUIUtils.DESCRIPTION_ANNO }
    };

    // Make sure we have a valid column.
    if (!colLookupTable.hasOwnProperty(columnId))
      throw new Error("Invalid column");

    // Use a default sort direction if none has been specified.  If aDirection
    // is invalid, result.sortingMode will be undefined, which has the effect
    // of unsorting the tree.
    aDirection = (aDirection || colLookupTable[columnId].dir).toUpperCase();

    var sortConst = "SORT_BY_" + colLookupTable[columnId].key + "_" + aDirection;
    result.sortingAnnotation = colLookupTable[columnId].anno || "";
    result.sortingMode = Ci.nsINavHistoryQueryOptions[sortConst];
  }
};

var ContentArea = {
  _specialViews: new Map(),

  init: function CA_init() {
    this._deck = document.getElementById("placesViewsDeck");
    this._toolbar = document.getElementById("placesToolbar");
    ContentTree.init();
    this._setupView();
  },

  /**
   * Gets the content view to be used for loading the given query.
   * If a custom view was set by setContentViewForQueryString, that
   * view would be returned, else the default tree view is returned
   *
   * @param aQueryString
   *        a query string
   * @return the view to be used for loading aQueryString.
   */
  getContentViewForQueryString:
  function CA_getContentViewForQueryString(aQueryString) {
    try {
      if (this._specialViews.has(aQueryString)) {
        let { view, options } = this._specialViews.get(aQueryString);
        if (typeof view == "function") {
          view = view();
          this._specialViews.set(aQueryString, { view, options });
        }
        return view;
      }
    } catch (ex) {
      Cu.reportError(ex);
    }
    return ContentTree.view;
  },

  /**
   * Sets a custom view to be used rather than the default places tree
   * whenever the given query is selected in the left pane.
   * @param aQueryString
   *        a query string
   * @param aView
   *        Either the custom view or a function that will return the view
   *        the first (and only) time it's called.
   * @param [optional] aOptions
   *        Object defining special options for the view.
   * @see ContentTree.viewOptions for supported options and default values.
   */
  setContentViewForQueryString:
  function CA_setContentViewForQueryString(aQueryString, aView, aOptions) {
    if (!aQueryString ||
        typeof aView != "object" && typeof aView != "function")
      throw new Error("Invalid arguments");

    this._specialViews.set(aQueryString, { view: aView,
                                           options: aOptions || {} });
  },

  get currentView() {
    return PlacesUIUtils.getViewForNode(this._deck.selectedPanel);
  },
  set currentView(aNewView) {
    let oldView = this.currentView;
    if (oldView != aNewView) {
      this._deck.selectedPanel = aNewView.associatedElement;

      // If the content area inactivated view was focused, move focus
      // to the new view.
      if (document.activeElement == oldView.associatedElement)
        aNewView.associatedElement.focus();
    }
    return aNewView;
  },

  get currentPlace() {
    return this.currentView.place;
  },
  set currentPlace(aQueryString) {
    let oldView = this.currentView;
    let newView = this.getContentViewForQueryString(aQueryString);
    newView.place = aQueryString;
    if (oldView != newView) {
      oldView.active = false;
      this.currentView = newView;
      this._setupView();
      newView.active = true;
    }
    return aQueryString;
  },

  /**
   * Applies view options.
   */
  _setupView: function CA__setupView() {
    let options = this.currentViewOptions;

    // showDetailsPane.
    let detailsDeck = document.getElementById("detailsDeck");
    detailsDeck.hidden = !options.showDetailsPane;

    // toolbarSet.
    for (let elt of this._toolbar.childNodes) {
      // On Windows and Linux the menu buttons are menus wrapped in a menubar.
      if (elt.id == "placesMenu") {
        for (let menuElt of elt.childNodes) {
          menuElt.hidden = !options.toolbarSet.includes(menuElt.id);
        }
      } else {
        elt.hidden = !options.toolbarSet.includes(elt.id);
      }
    }
  },

  /**
   * Options for the current view.
   *
   * @see ContentTree.viewOptions for supported options and default values.
   */
  get currentViewOptions() {
    // Use ContentTree options as default.
    let viewOptions = ContentTree.viewOptions;
    if (this._specialViews.has(this.currentPlace)) {
      let { options } = this._specialViews.get(this.currentPlace);
      for (let option in options) {
        viewOptions[option] = options[option];
      }
    }
    return viewOptions;
  },

  focus() {
    this._deck.selectedPanel.focus();
  }
};

var ContentTree = {
  init: function CT_init() {
    this._view = document.getElementById("placeContent");
  },

  get view() {
    return this._view;
  },

  get viewOptions() {
    return Object.seal({
      showDetailsPane: true,
      toolbarSet: "back-button, forward-button, organizeButton, viewMenu, maintenanceButton, libraryToolbarSpacer, searchFilter"
    });
  },

  openSelectedNode: function CT_openSelectedNode(aEvent) {
    let view = this.view;
    PlacesUIUtils.openNodeWithEvent(view.selectedNode, aEvent);
  },

  onClick: function CT_onClick(aEvent) {
    let node = this.view.selectedNode;
    if (node) {
      let doubleClick = aEvent.button == 0 && aEvent.detail == 2;
      let middleClick = aEvent.button == 1 && aEvent.detail == 1;
      if (PlacesUtils.nodeIsURI(node) && (doubleClick || middleClick)) {
        // Open associated uri in the browser.
        this.openSelectedNode(aEvent);
      } else if (middleClick && PlacesUtils.nodeIsContainer(node)) {
        // The command execution function will take care of seeing if the
        // selection is a folder or a different container type, and will
        // load its contents in tabs.
        PlacesUIUtils.openContainerNodeInTabs(node, aEvent, this.view);
      }
    }
  },

  onKeyPress: function CT_onKeyPress(aEvent) {
    if (aEvent.keyCode == KeyEvent.DOM_VK_RETURN)
      this.openSelectedNode(aEvent);
  }
};
PK
!<�sSOIOI0chrome/browser/content/browser/places/places.xul<?xml version="1.0"?>


<?xml-stylesheet href="chrome://browser/content/places/places.css"?>
<?xml-stylesheet href="chrome://browser/content/places/organizer.css"?>

<?xml-stylesheet href="chrome://global/skin/"?>
<?xml-stylesheet href="chrome://browser/skin/places/places.css"?>
<?xml-stylesheet href="chrome://browser/skin/places/organizer.css"?>

<?xul-overlay href="chrome://browser/content/places/editBookmarkOverlay.xul"?>

<?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?>

<!DOCTYPE window [
<!ENTITY % placesDTD SYSTEM "chrome://browser/locale/places/places.dtd">
%placesDTD;
<!ENTITY % editMenuOverlayDTD SYSTEM "chrome://global/locale/editMenuOverlay.dtd">
%editMenuOverlayDTD;
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
%browserDTD;
]>

<window id="places"
        title="&places.library.title;"
        windowtype="Places:Organizer"
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        xmlns:html="http://www.w3.org/1999/xhtml"
        onload="PlacesOrganizer.init();"
        onunload="PlacesOrganizer.destroy();"
        width="&places.library.width;" height="&places.library.height;"
        screenX="10" screenY="10"
        toggletoolbar="true"
        persist="width height screenX screenY sizemode">

  <script type="application/javascript"
          src="chrome://browser/content/places/places.js"/>
  <!-- On Mac, these are included via macBrowserOverlay.xul -> browser.js -> defineLazyScriptGetter -->
  <script type="application/javascript"
          src="chrome://browser/content/places/editBookmarkOverlay.js"/>

  <stringbundleset id="placesStringSet">
    <stringbundle id="brandStrings" src="chrome://branding/locale/brand.properties"/>
  </stringbundleset>


<script type="application/javascript" src="chrome://global/content/editMenuOverlay.js"/>
<commandset id="editMenuCommands">
  <commandset id="editMenuCommandSetAll" commandupdater="true" events="focus,select"
              oncommandupdate="goUpdateGlobalEditMenuItems()"/>
  <commandset id="editMenuCommandSetUndo" commandupdater="true" events="undo"
              oncommandupdate="goUpdateUndoEditMenuItems()"/>
  <commandset id="editMenuCommandSetPaste" commandupdater="true" events="clipboard"
              oncommandupdate="goUpdatePasteMenuItems()"/>
  <command id="cmd_undo" oncommand="goDoCommand('cmd_undo')"/>
  <command id="cmd_redo" oncommand="goDoCommand('cmd_redo')"/>
  <command id="cmd_cut" oncommand="goDoCommand('cmd_cut')"/>
  <command id="cmd_copy" oncommand="goDoCommand('cmd_copy')"/>
  <command id="cmd_paste" oncommand="goDoCommand('cmd_paste')"/>
  <command id="cmd_delete" oncommand="goDoCommand('cmd_delete')"/>
  <command id="cmd_selectAll" oncommand="goDoCommand('cmd_selectAll')"/>
  <command id="cmd_switchTextDirection" oncommand="goDoCommand('cmd_switchTextDirection');"/>
</commandset>
  <commandset id="placesCommands"/>

  <commandset id="organizerCommandSet">
    <command id="OrganizerCommand_find:all"
             oncommand="PlacesSearchBox.findAll();"/>
    <command id="OrganizerCommand_export"
             oncommand="PlacesOrganizer.exportBookmarks();"/>
    <command id="OrganizerCommand_import"
             oncommand="PlacesOrganizer.importFromFile();"/>
    <command id="OrganizerCommand_browserImport"
             oncommand="PlacesOrganizer.importFromBrowser();"/>
    <command id="OrganizerCommand_backup"
             oncommand="PlacesOrganizer.backupBookmarks();"/>
    <command id="OrganizerCommand_restoreFromFile"
             oncommand="PlacesOrganizer.onRestoreBookmarksFromFile();"/>
    <command id="OrganizerCommand_search:save"
             oncommand="PlacesOrganizer.saveSearch();"/>
    <command id="OrganizerCommand_search:moreCriteria"
             oncommand="PlacesQueryBuilder.addRow();"/>
    <command id="OrganizerCommand:Back"
             oncommand="PlacesOrganizer.back();"/>
    <command id="OrganizerCommand:Forward"
             oncommand="PlacesOrganizer.forward();"/>
  </commandset>


  <keyset id="placesOrganizerKeyset">
    <!-- Instantiation Keys -->
    <key id="placesKey_close" key="&cmd.close.key;" modifiers="accel"
         oncommand="close();"/>

    <!-- Command Keys -->
    <key id="placesKey_find:all"
         command="OrganizerCommand_find:all"
         key="&cmd.find.key;"
         modifiers="accel"/>

    <!-- Back/Forward Keys Support -->
    <key id="placesKey_goBackKb"
         keycode="VK_LEFT"
         command="OrganizerCommand:Back"
         modifiers="alt"/>
    <key id="placesKey_goForwardKb"
         keycode="VK_RIGHT"
         command="OrganizerCommand:Forward"
         modifiers="alt"/>
    <key id="placesKey_goBackKb2"
         key="&goBackCmd.commandKey;"
         command="OrganizerCommand:Back"
         modifiers="accel"/>
    <key id="placesKey_goForwardKb2"
         key="&goForwardCmd.commandKey;"
         command="OrganizerCommand:Forward"
         modifiers="accel"/>
  </keyset>

  <!-- These key nodes are here only for show. The real bindings come from
       XBL, in platformHTMLBindings.xml. See bugs 57078 and 71779. -->

  <keyset id="editMenuKeys">
    <key id="key_undo" key="&undoCmd.key;" modifiers="accel" command="cmd_undo"/>
    <key id="key_redo" key="&undoCmd.key;" modifiers="accel,shift" command="cmd_redo"/>
    <key id="key_cut" key="&cutCmd.key;" modifiers="accel" command="cmd_cut"/>
    <key id="key_copy" key="&copyCmd.key;" modifiers="accel" command="cmd_copy"/>
    <key id="key_paste" key="&pasteCmd.key;" modifiers="accel" command="cmd_paste"/>
    <key id="key_delete" keycode="VK_DELETE" command="cmd_delete"/>
    <key id="key_selectAll" key="&selectAllCmd.key;" modifiers="accel" command="cmd_selectAll"/>
    <key id="key_find" key="&findCmd.key;" modifiers="accel" command="cmd_find"/>
    <key id="key_findAgain" key="&findAgainCmd.key;" modifiers="accel" command="cmd_findAgain"/>
    <key id="key_findPrevious" key="&findAgainCmd.key;" modifiers="shift,accel" command="cmd_findPrevious"/>
    <key id="key_findAgain2" keycode="&findAgainCmd.key2;" command="cmd_findAgain"/>
    <key id="key_findPrevious2" keycode="&findAgainCmd.key2;" modifiers="shift" command="cmd_findPrevious"/>
  </keyset>

  <popupset id="placesPopupset">
    <menupopup id="placesContext"/>
    <menupopup id="placesColumnsContext"
               onpopupshowing="ViewMenu.fillWithColumns(event, null, null, 'checkbox', null);"
               oncommand="ViewMenu.showHideColumn(event.target); event.stopPropagation();"/>
  </popupset>

  <toolbox id="placesToolbox">
    <toolbar class="chromeclass-toolbar" id="placesToolbar" align="center">
      <toolbarbutton id="back-button"
                     command="OrganizerCommand:Back"
                     tooltiptext="&backButton.tooltip;"
                     disabled="true"/>

      <toolbarbutton id="forward-button"
                     command="OrganizerCommand:Forward"
                     tooltiptext="&forwardButton.tooltip;"
                     disabled="true"/>

      <menubar id="placesMenu">
        <menu accesskey="&organize.accesskey;" class="menu-iconic"
              id="organizeButton" label="&organize.label;"
              tooltiptext="&organize.tooltip;">
          <menupopup id="organizeButtonPopup">
            <menuitem id="newbookmark"
                      command="placesCmd_new:bookmark"
                      label="&cmd.new_bookmark.label;"
                      accesskey="&cmd.new_bookmark.accesskey;"/>
            <menuitem id="newfolder"
                      command="placesCmd_new:folder"
                      label="&cmd.new_folder.label;"
                      accesskey="&cmd.new_folder.accesskey;"/>
            <menuitem id="newseparator"
                      command="placesCmd_new:separator"
                      label="&cmd.new_separator.label;"
                      accesskey="&cmd.new_separator.accesskey;"/>

            <menuseparator id="orgUndoSeparator"/>

            <menuitem id="orgUndo"
                      command="cmd_undo"
                      label="&undoCmd.label;"
                      key="key_undo"
                      accesskey="&undoCmd.accesskey;"/>
            <menuitem id="orgRedo"
                      command="cmd_redo"
                      label="&redoCmd.label;"
                      key="key_redo"
                      accesskey="&redoCmd.accesskey;"/>

            <menuseparator id="orgCutSeparator"/>

            <menuitem id="orgCut"
                      command="cmd_cut"
                      label="&cutCmd.label;"
                      key="key_cut"
                      accesskey="&cutCmd.accesskey;"
                      selection="separator|link|folder|mixed"/>
            <menuitem id="orgCopy"
                      command="cmd_copy"
                      label="&copyCmd.label;"
                      key="key_copy"
                      accesskey="&copyCmd.accesskey;"
                      selection="separator|link|folder|mixed"/>
            <menuitem id="orgPaste"
                      command="cmd_paste"
                      label="&pasteCmd.label;"
                      key="key_paste"
                      accesskey="&pasteCmd.accesskey;"
                      selection="mutable"/>
            <menuitem id="orgDelete"
                      command="cmd_delete"
                      label="&deleteCmd.label;"
                      key="key_delete"
                      accesskey="&deleteCmd.accesskey;"/>

            <menuseparator id="selectAllSeparator"/>

            <menuitem id="orgSelectAll"
                      command="cmd_selectAll"
                      label="&selectAllCmd.label;"
                      key="key_selectAll"
                      accesskey="&selectAllCmd.accesskey;"/>

            <menuseparator id="orgCloseSeparator"/>

            <menuitem id="orgClose"
                      key="placesKey_close"
                      label="&file.close.label;"
                      accesskey="&file.close.accesskey;"
                      oncommand="close();"/>
          </menupopup>
        </menu>
        <menu accesskey="&views.accesskey;" class="menu-iconic"
              id="viewMenu" label="&views.label;"
              tooltiptext="&views.tooltip;">
          <menupopup id="viewMenuPopup">

            <menu id="viewColumns"
                  label="&view.columns.label;" accesskey="&view.columns.accesskey;">
              <menupopup onpopupshowing="ViewMenu.fillWithColumns(event, null, null, 'checkbox', null);"
                         oncommand="ViewMenu.showHideColumn(event.target); event.stopPropagation();"/>
            </menu>

            <menu id="viewSort" label="&view.sort.label;"
                  accesskey="&view.sort.accesskey;">
              <menupopup onpopupshowing="ViewMenu.populateSortMenu(event);"
                         oncommand="ViewMenu.setSortColumn(event.target.column, null);">
                <menuitem id="viewUnsorted" type="radio" name="columns"
                          label="&view.unsorted.label;" accesskey="&view.unsorted.accesskey;"
                          oncommand="ViewMenu.setSortColumn(null, null);"/>
                <menuseparator id="directionSeparator"/>
                <menuitem id="viewSortAscending" type="radio" name="direction"
                          label="&view.sortAscending.label;" accesskey="&view.sortAscending.accesskey;"
                          oncommand="ViewMenu.setSortColumn(null, 'ascending'); event.stopPropagation();"/>
                <menuitem id="viewSortDescending" type="radio" name="direction"
                          label="&view.sortDescending.label;" accesskey="&view.sortDescending.accesskey;"
                          oncommand="ViewMenu.setSortColumn(null, 'descending'); event.stopPropagation();"/>
              </menupopup>
            </menu>
          </menupopup>
        </menu>
        <menu accesskey="&maintenance.accesskey;" class="menu-iconic"
              id="maintenanceButton" label="&maintenance.label;"
              tooltiptext="&maintenance.tooltip;">
          <menupopup id="maintenanceButtonPopup">
            <menuitem id="backupBookmarks"
                      command="OrganizerCommand_backup"
                      label="&cmd.backup.label;"
                      accesskey="&cmd.backup.accesskey;"/>
            <menu id="fileRestoreMenu" label="&cmd.restore2.label;"
                      accesskey="&cmd.restore2.accesskey;">
              <menupopup id="fileRestorePopup" onpopupshowing="PlacesOrganizer.populateRestoreMenu();">
                <menuitem id="restoreFromFile"
                          command="OrganizerCommand_restoreFromFile"
                          label="&cmd.restoreFromFile.label;"
                          accesskey="&cmd.restoreFromFile.accesskey;"/>
              </menupopup>
            </menu>
            <menuseparator/>
            <menuitem id="fileImport"
                      command="OrganizerCommand_import"
                      label="&importBookmarksFromHTML.label;"
                      accesskey="&importBookmarksFromHTML.accesskey;"/>
            <menuitem id="fileExport"
                      command="OrganizerCommand_export"
                      label="&exportBookmarksToHTML.label;"
                      accesskey="&exportBookmarksToHTML.accesskey;"/>
            <menuseparator/>
            <menuitem id="browserImport"
                      command="OrganizerCommand_browserImport"
                      label="&importOtherBrowser.label;"
                      accesskey="&importOtherBrowser.accesskey;"/>
          </menupopup>
        </menu>
      </menubar>

      <spacer id="libraryToolbarSpacer" flex="1"/>

      <textbox id="searchFilter"
               type="search"
               aria-controls="placeContent"
               oncommand="PlacesSearchBox.search(this.value);"
               collection="bookmarks">
      </textbox>
    </toolbar>
  </toolbox>

  <hbox flex="1" id="placesView">
    <tree id="placesList"
          class="plain placesTree"
          type="places"
          hidecolumnpicker="true" context="placesContext"
          onselect="PlacesOrganizer.onPlaceSelected(true);"
          onclick="PlacesOrganizer.onPlacesListClick(event);"
          onfocus="PlacesOrganizer.updateDetailsPane(event);"
          seltype="single"
          persist="width"
          width="200"
          minwidth="100"
          maxwidth="400">
      <treecols>
        <treecol anonid="title" flex="1" primary="true" hideheader="true"/>
      </treecols>
      <treechildren flex="1"/>
    </tree>
    <splitter collapse="none" persist="state"></splitter>
    <vbox id="contentView" flex="4">
      <deck id="placesViewsDeck"
            selectedIndex="0"
            flex="1">
        <tree id="placeContent"
              class="plain placesTree"
              context="placesContext"
              hidecolumnpicker="true"
              flex="1"
              type="places"
              flatList="true"
              selectfirstnode="true"
              enableColumnDrag="true"
              onfocus="PlacesOrganizer.updateDetailsPane(event)"
              onselect="PlacesOrganizer.updateDetailsPane(event)"
              onkeypress="ContentTree.onKeyPress(event);"
              onopenflatcontainer="PlacesOrganizer.openFlatContainer(aContainer);">
          <treecols id="placeContentColumns" context="placesColumnsContext">
            <treecol label="&col.name.label;" id="placesContentTitle" anonid="title" flex="5" primary="true" ordinal="1"
                      persist="width hidden ordinal sortActive sortDirection"/>
            <splitter class="tree-splitter"/>
            <treecol label="&col.tags.label;" id="placesContentTags" anonid="tags" flex="2"
                      persist="width hidden ordinal sortActive sortDirection"/>
            <splitter class="tree-splitter"/>
            <treecol label="&col.url.label;" id="placesContentUrl" anonid="url" flex="5"
                      persist="width hidden ordinal sortActive sortDirection"/>
            <splitter class="tree-splitter"/>
            <treecol label="&col.mostrecentvisit.label;" id="placesContentDate" anonid="date" flex="1" hidden="true"
                      persist="width hidden ordinal sortActive sortDirection"/>
            <splitter class="tree-splitter"/>
            <treecol label="&col.visitcount.label;" id="placesContentVisitCount" anonid="visitCount" flex="1" hidden="true"
                      persist="width hidden ordinal sortActive sortDirection"/>
            <splitter class="tree-splitter"/>
            <treecol label="&col.description.label;" id="placesContentDescription" anonid="description" flex="1" hidden="true"
                      persist="width hidden ordinal sortActive sortDirection"/>
            <splitter class="tree-splitter"/>
            <treecol label="&col.dateadded.label;" id="placesContentDateAdded" anonid="dateAdded" flex="1" hidden="true"
                      persist="width hidden ordinal sortActive sortDirection"/>
            <splitter class="tree-splitter"/>
            <treecol label="&col.lastmodified.label;" id="placesContentLastModified" anonid="lastModified" flex="1" hidden="true"
                      persist="width hidden ordinal sortActive sortDirection"/>
          </treecols>
          <treechildren flex="1" onclick="ContentTree.onClick(event);"/>
        </tree>
      </deck>
      <deck id="detailsDeck" style="height: 11em;">
        <vbox id="itemsCountBox" align="center">
          <spacer flex="3"/>
          <label id="itemsCountText"/>
          <spacer flex="1"/>
          <description id="selectItemDescription">
              &detailsPane.selectAnItemText.description;
          </description>
          <spacer flex="3"/>
        </vbox>
        <vbox id="infoBox" minimal="true">
          <vbox id="editBookmarkPanelContent" flex="1"/>
          <hbox id="infoBoxExpanderWrapper" align="center">

            <button type="image" id="infoBoxExpander"
                    class="expander-down"
                    oncommand="PlacesOrganizer.toggleAdditionalInfoFields();"
                    observes="paneElementsBroadcaster"/>

            <label id="infoBoxExpanderLabel"
                    lesslabel="&detailsPane.less.label;"
                    lessaccesskey="&detailsPane.less.accesskey;"
                    morelabel="&detailsPane.more.label;"
                    moreaccesskey="&detailsPane.more.accesskey;"
                    value="&detailsPane.more.label;"
                    accesskey="&detailsPane.more.accesskey;"
                    control="infoBoxExpander"/>

          </hbox>
        </vbox>
      </deck>
    </vbox>
  </hbox>
</window>
PK
!<q�H�X*X*7chrome/browser/content/browser/places/placesOverlay.xul<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 overlay [
<!ENTITY % placesDTD SYSTEM "chrome://browser/locale/places/places.dtd">
%placesDTD;
<!ENTITY % editMenuOverlayDTD SYSTEM "chrome://global/locale/editMenuOverlay.dtd">
%editMenuOverlayDTD;
]>

<overlay id="placesOverlay"
         xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

  <script type="application/javascript"
          src="chrome://global/content/globalOverlay.js"/>
  <script type="application/javascript"
          src="chrome://browser/content/utilityOverlay.js"/>
  <script type="application/javascript"><![CDATA[
    ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
    ChromeUtils.defineModuleGetter(window,
      "PlacesUtils", "resource://gre/modules/PlacesUtils.jsm");
    ChromeUtils.defineModuleGetter(window,
      "PlacesUIUtils", "resource:///modules/PlacesUIUtils.jsm");
    ChromeUtils.defineModuleGetter(window,
      "PlacesTransactions", "resource://gre/modules/PlacesTransactions.jsm");
    ChromeUtils.defineModuleGetter(window,
      "ForgetAboutSite", "resource://gre/modules/ForgetAboutSite.jsm");

    XPCOMUtils.defineLazyScriptGetter(window, "PlacesTreeView",
      "chrome://browser/content/places/treeView.js");
    XPCOMUtils.defineLazyScriptGetter(window,
      ["PlacesInsertionPoint", "PlacesController", "PlacesControllerDragHelper"],
      "chrome://browser/content/places/controller.js");
  ]]></script>

  <!-- Bookmarks and history tooltip -->
  <tooltip id="bhTooltip" noautohide="true"
           onpopupshowing="return window.top.BookmarksEventHandler.fillInBHTooltip(document, event)">
    <vbox id="bhTooltipTextBox" flex="1">
      <label id="bhtTitleText" class="tooltip-label" />
      <label id="bhtUrlText" crop="center" class="tooltip-label uri-element" />
    </vbox>
  </tooltip>

  <commandset id="placesCommands"
              commandupdater="true"
              events="focus,sort,places"
              oncommandupdate="PlacesUIUtils.updateCommands(window);">
    <command id="placesCmd_open"
             oncommand="PlacesUIUtils.doCommand(window, 'placesCmd_open');"/>
    <command id="placesCmd_open:window"
             oncommand="PlacesUIUtils.doCommand(window, 'placesCmd_open:window');"/>
    <command id="placesCmd_open:privatewindow"
             oncommand="PlacesUIUtils.doCommand(window, 'placesCmd_open:privatewindow');"/>
    <command id="placesCmd_open:tab"
             oncommand="PlacesUIUtils.doCommand(window, 'placesCmd_open:tab');"/>

    <command id="placesCmd_new:bookmark"
             oncommand="PlacesUIUtils.doCommand(window, 'placesCmd_new:bookmark');"/>
    <command id="placesCmd_new:folder"
             oncommand="PlacesUIUtils.doCommand(window, 'placesCmd_new:folder');"/>
    <command id="placesCmd_new:separator"
             oncommand="PlacesUIUtils.doCommand(window, 'placesCmd_new:separator');"/>
    <command id="placesCmd_show:info"
             oncommand="PlacesUIUtils.doCommand(window, 'placesCmd_show:info');"/>
    <command id="placesCmd_rename"
             oncommand="PlacesUIUtils.doCommand(window, 'placesCmd_show:info');"
             observes="placesCmd_show:info"/>
    <command id="placesCmd_reload"
             oncommand="PlacesUIUtils.doCommand(window, 'placesCmd_reload');"/>
    <command id="placesCmd_sortBy:name"
             oncommand="PlacesUIUtils.doCommand(window, 'placesCmd_sortBy:name');"/>
    <command id="placesCmd_deleteDataHost"
             oncommand="PlacesUIUtils.doCommand(window, 'placesCmd_deleteDataHost');"/>
    <command id="placesCmd_createBookmark"
             oncommand="PlacesUIUtils.doCommand(window, 'placesCmd_createBookmark');"/>

    <!-- Special versions of cut/copy/paste/delete which check for an open context menu. -->
    <command id="placesCmd_cut"
             oncommand="PlacesUIUtils.doCommand(window, 'placesCmd_cut');"/>
    <command id="placesCmd_copy"
             oncommand="PlacesUIUtils.doCommand(window, 'placesCmd_copy');"/>
    <command id="placesCmd_paste"
             oncommand="PlacesUIUtils.doCommand(window, 'placesCmd_paste');"/>
    <command id="placesCmd_delete"
             oncommand="PlacesUIUtils.doCommand(window, 'placesCmd_delete');"/>
  </commandset>

  <menupopup id="placesContext"
             onpopupshowing="this._view = PlacesUIUtils.getViewForNode(document.popupNode);
                             if (!PlacesUIUtils.openInTabClosesMenu) {
                               document.getElementById ('placesContext_open:newtab')
                               .setAttribute('closemenu', 'single');
                             }
                             return this._view.buildContextMenu(this);"
             onpopuphiding="this._view.destroyContextMenu();">
    <menuitem id="placesContext_open"
              command="placesCmd_open"
              label="&cmd.open.label;"
              accesskey="&cmd.open.accesskey;"
              default="true"
              selectiontype="single"
              selection="link"/>
    <menuitem id="placesContext_open:newtab"
              command="placesCmd_open:tab"
              label="&cmd.open_tab.label;"
              accesskey="&cmd.open_tab.accesskey;"
              selectiontype="single"
              selection="link"/>
    <menuitem id="placesContext_openContainer:tabs"
              oncommand="var view = PlacesUIUtils.getViewForNode(document.popupNode);
                         view.controller.openSelectionInTabs(event);"
              onclick="checkForMiddleClick(this, event);"
              label="&cmd.open_all_in_tabs.label;"
              accesskey="&cmd.open_all_in_tabs.accesskey;"
              selectiontype="single|none"
              selection="folder|host|query"/>
    <menuitem id="placesContext_openLinks:tabs"
              oncommand="var view = PlacesUIUtils.getViewForNode(document.popupNode);
                         view.controller.openSelectionInTabs(event);"
              onclick="checkForMiddleClick(this, event);"
              label="&cmd.open_all_in_tabs.label;"
              accesskey="&cmd.open_all_in_tabs.accesskey;"
              selectiontype="multiple"
              selection="link"/>
    <menuitem id="placesContext_open:newwindow"
              command="placesCmd_open:window"
              label="&cmd.open_window.label;"
              accesskey="&cmd.open_window.accesskey;"
              selectiontype="single"
              selection="link"/>
    <menuitem id="placesContext_open:newprivatewindow"
              command="placesCmd_open:privatewindow"
              label="&cmd.open_private_window.label;"
              accesskey="&cmd.open_private_window.accesskey;"
              selectiontype="single"
              selection="link"
              hideifprivatebrowsing="true"/>
    <menuseparator id="placesContext_openSeparator"/>
    <menuitem id="placesContext_new:bookmark"
              command="placesCmd_new:bookmark"
              label="&cmd.new_bookmark.label;"
              accesskey="&cmd.new_bookmark.accesskey;"
              selectiontype="any"
              hideifnoinsertionpoint="true"/>
    <menuitem id="placesContext_new:folder"
              command="placesCmd_new:folder"
              label="&cmd.new_folder.label;"
              accesskey="&cmd.context_new_folder.accesskey;"
              selectiontype="any"
              hideifnoinsertionpoint="true"/>
    <menuitem id="placesContext_new:separator"
              command="placesCmd_new:separator"
              label="&cmd.new_separator.label;"
              accesskey="&cmd.new_separator.accesskey;"
              closemenu="single"
              selectiontype="any"
              hideifnoinsertionpoint="true"/>
    <menuseparator id="placesContext_newSeparator"/>
    <menuitem id="placesContext_createBookmark"
              command="placesCmd_createBookmark"
              selection="link"
              forcehideselection="bookmark|tagChild"/>
    <menuitem id="placesContext_cut"
              command="placesCmd_cut"
              label="&cutCmd.label;"
              accesskey="&cutCmd.accesskey;"
              closemenu="single"
              selection="bookmark|folder|separator|query"
              forcehideselection="tagChild|livemarkChild"/>
    <menuitem id="placesContext_copy"
              command="placesCmd_copy"
              label="&copyCmd.label;"
              closemenu="single"
              accesskey="&copyCmd.accesskey;"
              selection="any"/>
    <menuitem id="placesContext_paste"
              command="placesCmd_paste"
              label="&pasteCmd.label;"
              closemenu="single"
              accesskey="&pasteCmd.accesskey;"
              selectiontype="any"
              hideifnoinsertionpoint="true"/>
    <menuseparator id="placesContext_editSeparator"/>
    <menuitem id="placesContext_delete"
              command="placesCmd_delete"
              label="&deleteCmd.label;"
              accesskey="&deleteCmd.accesskey;"
              closemenu="single"
              selection="bookmark|tagChild|folder|query|dynamiccontainer|separator|host"/>
    <menuitem id="placesContext_delete_history"
              command="placesCmd_delete"
              closemenu="single"
              selection="link"
              forcehideselection="bookmark"/>
    <menuitem id="placesContext_deleteHost"
              command="placesCmd_deleteDataHost"
              label="&cmd.deleteDomainData.label;"
              accesskey="&cmd.deleteDomainData.accesskey;"
              closemenu="single"
              selection="link|host"
              selectiontype="single"
              forcehideselection="bookmark"/>
    <menuseparator id="placesContext_deleteSeparator"/>
    <menuitem id="placesContext_sortBy:name"
              command="placesCmd_sortBy:name"
              label="&cmd.sortby_name.label;"
              accesskey="&cmd.context_sortby_name.accesskey;"
              closemenu="single"
              selection="folder"/>
    <menuitem id="placesContext_reload"
              command="placesCmd_reload"
              label="&cmd.reloadLivebookmark.label;"
              accesskey="&cmd.reloadLivebookmark.accesskey;"
              closemenu="single"
              selection="livemark/feedURI"/>
    <menuseparator id="placesContext_sortSeparator"/>
    <menuitem id="placesContext_show:info"
              command="placesCmd_show:info"
              label="&cmd.properties.label;"
              accesskey="&cmd.properties.accesskey;"
              selection="bookmark|folder|query"
              forcehideselection="livemarkChild"/>
  </menupopup>

</overlay>
PK
!<��q`bxbx.chrome/browser/content/browser/places/tree.xml<?xml version="1.0"?>

<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<bindings id="placesTreeBindings"
          xmlns="http://www.mozilla.org/xbl"
          xmlns:xbl="http://www.mozilla.org/xbl"
          xmlns:html="http://www.w3.org/1999/xhtml"
          xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

  <binding id="places-tree" extends="chrome://global/content/bindings/tree.xml#tree">
    <implementation>
      <constructor><![CDATA[
        // Force an initial build.
        if (this.place)
          this.place = this.place;
      ]]></constructor>

      <destructor><![CDATA[
        // Break the treeviewer->result->treeviewer cycle.
        // Note: unsetting the result's viewer also unsets
        // the viewer's reference to our treeBoxObject.
        var result = this.result;
        if (result) {
          result.root.containerOpen = false;
        }

        // Unregister the controllber before unlinking the view, otherwise it
        // may still try to update commands on a view with a null result.
        if (this._controller) {
          this._controller.terminate();
          this.controllers.removeController(this._controller);
        }

        if (this.view) {
          this.view.uninit();
        }
        this.view = null;
      ]]></destructor>

      <property name="controller"
                readonly="true"
                onget="return this._controller"/>

      <property name="disableUserActions"
                onget="return this.getAttribute('disableUserActions') == 'true';"
                onset="if (val) this.setAttribute('disableUserActions', 'true');
                       else this.removeAttribute('disableUserActions'); return val;"/>

      <!-- overriding -->
      <property name="view">
        <getter><![CDATA[
          try {
            return this.treeBoxObject.view.wrappedJSObject || null;
          } catch (e) {
            return null;
          }
        ]]></getter>
        <setter><![CDATA[
          return this.treeBoxObject.view = val;
        ]]></setter>
      </property>

      <property name="associatedElement"
                readonly="true"
                onget="return this"/>

      <method name="applyFilter">
        <parameter name="filterString"/>
        <parameter name="folderRestrict"/>
        <parameter name="includeHidden"/>
        <body><![CDATA[
          // preserve grouping
          var queryNode = PlacesUtils.asQuery(this.result.root);
          var options = queryNode.queryOptions.clone();

          // Make sure we're getting uri results.
          // We do not yet support searching into grouped queries or into
          // tag containers, so we must fall to the default case.
          if (PlacesUtils.nodeIsHistoryContainer(queryNode) ||
              options.resultType == options.RESULTS_AS_TAG_QUERY ||
              options.resultType == options.RESULTS_AS_TAG_CONTENTS ||
              options.resultType == options.RESULTS_AS_ROOTS_QUERY)
            options.resultType = options.RESULTS_AS_URI;

          var query = PlacesUtils.history.getNewQuery();
          query.searchTerms = filterString;

          if (folderRestrict) {
            query.setFolders(folderRestrict, folderRestrict.length);
            options.queryType = options.QUERY_TYPE_BOOKMARKS;
          }

          options.includeHidden = !!includeHidden;

          this.load([query], options);
        ]]></body>
      </method>

      <method name="load">
        <parameter name="queries"/>
        <parameter name="options"/>
        <body><![CDATA[
          let result = PlacesUtils.history
                                  .executeQueries(queries, queries.length,
                                                  options);
          let callback;
          if (this.flatList) {
            let onOpenFlatContainer = this.onOpenFlatContainer;
            if (onOpenFlatContainer)
              callback = new Function("aContainer", onOpenFlatContainer);
          }

          if (!this._controller) {
            this._controller = new PlacesController(this);
            this._controller.disableUserActions = this.disableUserActions;
            this.controllers.appendController(this._controller);
          }

          let treeView = new PlacesTreeView(this.flatList, callback, this._controller);

          // Observer removal is done within the view itself.  When the tree
          // goes away, treeboxobject calls view.setTree(null), which then
          // calls removeObserver.
          result.addObserver(treeView);
          this.view = treeView;

          if (this.getAttribute("selectfirstnode") == "true" && treeView.rowCount > 0) {
            treeView.selection.select(0);
          }

          this._cachedInsertionPoint = undefined;
        ]]></body>
      </method>

      <property name="flatList">
        <getter><![CDATA[
          return this.getAttribute("flatList") == "true";
        ]]></getter>
        <setter><![CDATA[
          if (this.flatList != val) {
            this.setAttribute("flatList", val);
            // reload with the last place set
            if (this.place)
              this.place = this.place;
          }
          return val;
        ]]></setter>
      </property>

      <property name="onOpenFlatContainer">
        <getter><![CDATA[
          return this.getAttribute("onopenflatcontainer");
        ]]></getter>
        <setter><![CDATA[
          if (this.onOpenFlatContainer != val) {
            this.setAttribute("onopenflatcontainer", val);
            // reload with the last place set
            if (this.place)
              this.place = this.place;
          }
          return val;
        ]]></setter>
      </property>

      <!--
        Causes a particular node represented by the specified placeURI to be
        selected in the tree. All containers above the node in the hierarchy
        will be opened, so that the node is visible.
        -->
      <method name="selectPlaceURI">
        <parameter name="placeURI"/>
        <body><![CDATA[
          // Do nothing if a node matching the given uri is already selected
          if (this.hasSelection && this.selectedNode.uri == placeURI)
            return;

          function findNode(container, nodesURIChecked) {
            var containerURI = container.uri;
            if (containerURI == placeURI)
              return container;
            if (nodesURIChecked.includes(containerURI))
              return null;

            // never check the contents of the same query
            nodesURIChecked.push(containerURI);

            var wasOpen = container.containerOpen;
            if (!wasOpen)
              container.containerOpen = true;
            for (var i = 0; i < container.childCount; ++i) {
              var child = container.getChild(i);
              var childURI = child.uri;
              if (childURI == placeURI)
                return child;
              else if (PlacesUtils.nodeIsContainer(child)) {
                var nested = findNode(PlacesUtils.asContainer(child), nodesURIChecked);
                if (nested)
                  return nested;
              }
            }

            if (!wasOpen)
              container.containerOpen = false;

            return null;
          }

          var container = this.result.root;
          NS_ASSERT(container, "No result, cannot select place URI!");
          if (!container)
            return;

          var child = findNode(container, []);
          if (child)
            this.selectNode(child);
          else {
            // If the specified child could not be located, clear the selection
            var selection = this.view.selection;
            selection.clearSelection();
          }
        ]]></body>
      </method>

      <!--
        Causes a particular node to be selected in the tree, resulting in all
        containers above the node in the hierarchy to be opened, so that the
        node is visible.
        -->
      <method name="selectNode">
        <parameter name="node"/>
        <body><![CDATA[
          var view = this.view;

          var parent = node.parent;
          if (parent && !parent.containerOpen) {
            // Build a list of all of the nodes that are the parent of this one
            // in the result.
            var parents = [];
            var root = this.result.root;
            while (parent && parent != root) {
              parents.push(parent);
              parent = parent.parent;
            }

            // Walk the list backwards (opening from the root of the hierarchy)
            // opening each folder as we go.
            for (var i = parents.length - 1; i >= 0; --i) {
              let index = view.treeIndexForNode(parents[i]);
              if (index != -1 &&
                  view.isContainer(index) && !view.isContainerOpen(index))
                view.toggleOpenState(index);
            }
            // Select the specified node...
          }

          let index = view.treeIndexForNode(node);
          if (index == -1)
            return;

          view.selection.select(index);
          // ... and ensure it's visible, not scrolled off somewhere.
          this.treeBoxObject.ensureRowIsVisible(index);
        ]]></body>
      </method>

      <!-- nsIPlacesView -->
      <property name="result">
        <getter><![CDATA[
          try {
            return this.view.QueryInterface(Ci.nsINavHistoryResultObserver).result;
          } catch (e) {
            return null;
          }
        ]]></getter>
      </property>

      <!-- nsIPlacesView -->
      <property name="place">
        <getter><![CDATA[
          return this.getAttribute("place");
        ]]></getter>
        <setter><![CDATA[
          this.setAttribute("place", val);

          var queriesRef = { };
          var queryCountRef = { };
          var optionsRef = { };
          PlacesUtils.history.queryStringToQueries(val, queriesRef, queryCountRef, optionsRef);
          if (queryCountRef.value == 0)
            queriesRef.value = [PlacesUtils.history.getNewQuery()];
          if (!optionsRef.value)
            optionsRef.value = PlacesUtils.history.getNewQueryOptions();

          this.load(queriesRef.value, optionsRef.value);

          return val;
        ]]></setter>
      </property>

      <!-- nsIPlacesView -->
      <property name="hasSelection">
        <getter><![CDATA[
          return this.view && this.view.selection.count >= 1;
        ]]></getter>
      </property>

      <!-- nsIPlacesView -->
      <property name="selectedNodes">
        <getter><![CDATA[
          let nodes = [];
          if (!this.hasSelection)
            return nodes;

          let selection = this.view.selection;
          let rc = selection.getRangeCount();
          let resultview = this.view;
          for (let i = 0; i < rc; ++i) {
            let min = { }, max = { };
            selection.getRangeAt(i, min, max);
            for (let j = min.value; j <= max.value; ++j) {
              nodes.push(resultview.nodeForTreeIndex(j));
            }
          }
          return nodes;
        ]]></getter>
      </property>

      <method name="toggleCutNode">
        <parameter name="aNode"/>
        <parameter name="aValue"/>
        <body><![CDATA[
          this.view.toggleCutNode(aNode, aValue);
        ]]></body>
      </method>

      <!-- nsIPlacesView -->
      <property name="removableSelectionRanges">
        <getter><![CDATA[
          // This property exists in addition to selectedNodes because it
          // encodes selection ranges (which only occur in list views) into
          // the return value. For each removed range, the index at which items
          // will be re-inserted upon the remove transaction being performed is
          // the first index of the range, so that the view updates correctly.
          //
          // For example, if we remove rows 2,3,4 and 7,8 from a list, when we
          // undo that operation, if we insert what was at row 3 at row 3 again,
          // it will show up _after_ the item that was at row 5. So we need to
          // insert all items at row 2, and the tree view will update correctly.
          //
          // Also, this function collapses the selection to remove redundant
          // data, e.g. when deleting this selection:
          //
          //      http://www.foo.com/
          //  (-) Some Folder
          //        http://www.bar.com/
          //
          // ... returning http://www.bar.com/ as part of the selection is
          // redundant because it is implied by removing "Some Folder". We
          // filter out all such redundancies since some partial amount of
          // the folder's children may be selected.
          //
          let nodes = [];
          if (!this.hasSelection)
            return nodes;

          var selection = this.view.selection;
          var rc = selection.getRangeCount();
          var resultview = this.view;
          // This list is kept independently of the range selected (i.e. OUTSIDE
          // the for loop) since the row index of a container is unique for the
          // entire view, and we could have some really wacky selection and we
          // don't want to blow up.
          var containers = { };
          for (var i = 0; i < rc; ++i) {
            var range = [];
            var min = { }, max = { };
            selection.getRangeAt(i, min, max);

            for (var j = min.value; j <= max.value; ++j) {
              if (this.view.isContainer(j))
                containers[j] = true;
              if (!(this.view.getParentIndex(j) in containers))
                range.push(resultview.nodeForTreeIndex(j));
            }
            nodes.push(range);
          }
          return nodes;
        ]]></getter>
      </property>

      <!-- nsIPlacesView -->
      <property name="draggableSelection"
                onget="return this.selectedNodes"/>

      <!-- nsIPlacesView -->
      <property name="selectedNode">
        <getter><![CDATA[
          var view = this.view;
          if (!view || view.selection.count != 1)
            return null;

          var selection = view.selection;
          var min = { }, max = { };
          selection.getRangeAt(0, min, max);

          return this.view.nodeForTreeIndex(min.value);
        ]]></getter>
      </property>

      <!-- nsIPlacesView -->
      <property name="insertionPoint">
        <getter><![CDATA[
          // invalidated on selection and focus changes
          if (this._cachedInsertionPoint !== undefined)
            return this._cachedInsertionPoint;

          // there is no insertion point for history queries
          // so bail out now and save a lot of work when updating commands
          var resultNode = this.result.root;
          if (PlacesUtils.nodeIsQuery(resultNode) &&
              PlacesUtils.asQuery(resultNode).queryOptions.queryType ==
                Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY)
              return this._cachedInsertionPoint = null;

          var orientation = Ci.nsITreeView.DROP_BEFORE;
          // If there is no selection, insert at the end of the container.
          if (!this.hasSelection) {
            var index = this.view.rowCount - 1;
            this._cachedInsertionPoint =
              this._getInsertionPoint(index, orientation);
            return this._cachedInsertionPoint;
          }

          // This is a two-part process. The first part is determining the drop
          // orientation.
          // * The default orientation is to drop _before_ the selected item.
          // * If the selected item is a container, the default orientation
          //   is to drop _into_ that container.
          //
          // Warning: It may be tempting to use tree indexes in this code, but
          //          you must not, since the tree is nested and as your tree
          //          index may change when folders before you are opened and
          //          closed. You must convert your tree index to a node, and
          //          then use getChildIndex to find your absolute index in
          //          the parent container instead.
          //
          var resultView = this.view;
          var selection = resultView.selection;
          var rc = selection.getRangeCount();
          var min = { }, max = { };
          selection.getRangeAt(rc - 1, min, max);

          // If the sole selection is a container, and we are not in
          // a flatlist, insert into it.
          // Note that this only applies to _single_ selections,
          // if the last element within a multi-selection is a
          // container, insert _adjacent_ to the selection.
          //
          // If the sole selection is the bookmarks toolbar folder, we insert
          // into it even if it is not opened
          if (selection.count == 1 && resultView.isContainer(max.value) &&
              !this.flatList)
            orientation = Ci.nsITreeView.DROP_ON;

          this._cachedInsertionPoint =
            this._getInsertionPoint(max.value, orientation);
          return this._cachedInsertionPoint;
        ]]></getter>
      </property>

      <method name="_getInsertionPoint">
        <parameter name="index"/>
        <parameter name="orientation"/>
        <body><![CDATA[
          var result = this.result;
          var resultview = this.view;
          var container = result.root;
          var dropNearNode = null;
          NS_ASSERT(container, "null container");
          // When there's no selection, assume the container is the container
          // the view is populated from (i.e. the result's itemId).
          if (index != -1) {
            var lastSelected = resultview.nodeForTreeIndex(index);
            if (resultview.isContainer(index) && orientation == Ci.nsITreeView.DROP_ON) {
              // If the last selected item is an open container, append _into_
              // it, rather than insert adjacent to it.
              container = lastSelected;
              index = -1;
            } else if (lastSelected.containerOpen &&
                       orientation == Ci.nsITreeView.DROP_AFTER &&
                       lastSelected.hasChildren) {
             // If the last selected item is an open container and the user is
             // trying to drag into it as a first item, really insert into it.
             container = lastSelected;
             orientation = Ci.nsITreeView.DROP_ON;
             index = 0;
            } else {
              // Use the last-selected node's container.
              container = lastSelected.parent;

              // See comment in the treeView.js's copy of this method
              if (!container || !container.containerOpen)
                return null;

              // Avoid the potentially expensive call to getChildIndex
              // if we know this container doesn't allow insertion
              if (this.controller.disallowInsertion(container))
                return null;

              var queryOptions = PlacesUtils.asQuery(result.root).queryOptions;
              if (queryOptions.sortingMode !=
                    Ci.nsINavHistoryQueryOptions.SORT_BY_NONE) {
                // If we are within a sorted view, insert at the end
                index = -1;
              } else if (queryOptions.excludeItems ||
                         queryOptions.excludeQueries ||
                         queryOptions.excludeReadOnlyFolders) {
                // Some item may be invisible, insert near last selected one.
                // We don't replace index here to avoid requests to the db,
                // instead it will be calculated later by the controller.
                index = -1;
                dropNearNode = lastSelected;
              } else {
                var lsi = container.getChildIndex(lastSelected);
                index = orientation == Ci.nsITreeView.DROP_BEFORE ? lsi : lsi + 1;
              }
            }
          }

          if (this.controller.disallowInsertion(container))
            return null;

          // TODO (Bug 1160193): properly support dropping on a tag root.
          let tagName = null;
          if (PlacesUtils.nodeIsTagQuery(container)) {
            tagName = container.title;
            if (!tagName)
              return null;
          }

          return new PlacesInsertionPoint({
            parentId: PlacesUtils.getConcreteItemId(container),
            parentGuid: PlacesUtils.getConcreteItemGuid(container),
            index, orientation, tagName, dropNearNode
          });
        ]]></body>
      </method>

      <!-- nsIPlacesView -->
      <method name="selectAll">
        <body><![CDATA[
          this.view.selection.selectAll();
        ]]></body>
      </method>

      <!-- This method will select the first node in the tree that matches
           each given item guid. It will open any folder nodes that it needs
           to in order to show the selected items.
           Note: An array of ids or guids (or a mixture) may be passed as aIDs.
           Passing IDs should be considered deprecated.
      -->
      <method name="selectItems">
        <parameter name="aIDs"/>
        <parameter name="aOpenContainers"/>
        <body><![CDATA[
          // Never open containers in flat lists.
          if (this.flatList)
            aOpenContainers = false;
          // By default, we do search and select within containers which were
          // closed (note that containers in which nodes were not found are
          // closed).
          if (aOpenContainers === undefined)
            aOpenContainers = true;

          var ids = aIDs; // don't manipulate the caller's array

          // Array of nodes found by findNodes which are to be selected
          var nodes = [];

          // Array of nodes found by findNodes which should be opened
          var nodesToOpen = [];

          // A set of GUIDs of container-nodes that were previously searched,
          // and thus shouldn't be searched again. This is empty at the initial
          // start of the recursion and gets filled in as the recursion
          // progresses.
          var checkedGuidsSet = new Set();

          /**
           * Recursively search through a node's children for items
           * with the given IDs. When a matching item is found, remove its ID
           * from the IDs array, and add the found node to the nodes dictionary.
           *
           * NOTE: This method will leave open any node that had matching items
           * in its subtree.
           */
          function findNodes(node) {
            var foundOne = false;
            // See if node matches an ID we wanted; add to results.
            // For simple folder queries, check both itemId and the concrete
            // item id.
            var index = ids.indexOf(node.itemId);
            if (index == -1 &&
                node.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT)
              index = ids.indexOf(PlacesUtils.asQuery(node).folderItemId);

            if (index == -1) {
              index = ids.indexOf(node.bookmarkGuid);
              if (index == -1) {
                let concreteGuid = PlacesUtils.getConcreteItemGuid(node);
                if (concreteGuid != node.bookmarkGuid) {
                  index = ids.indexOf(concreteGuid);
                }
              }
            }

            if (index != -1) {
              nodes.push(node);
              foundOne = true;
              ids.splice(index, 1);
            }

            var concreteGuid = PlacesUtils.getConcreteItemGuid(node);
            if (ids.length == 0 || !PlacesUtils.nodeIsContainer(node) ||
                checkedGuidsSet.has(concreteGuid))
              return foundOne;

            // Only follow a query if it has been been explicitly opened by the
            // caller. We support the "AllBookmarks" case to allow callers to
            // specify just the top-level bookmark folders.
            let shouldOpen = aOpenContainers && (PlacesUtils.nodeIsFolder(node) ||
              (PlacesUtils.nodeIsQuery(node) && node.itemId == PlacesUIUtils.leftPaneQueries.AllBookmarks));

            PlacesUtils.asContainer(node);
            if (!node.containerOpen && !shouldOpen)
              return foundOne;

            checkedGuidsSet.add(concreteGuid);

            // Remember the beginning state so that we can re-close
            // this node if we don't find any additional results here.
            var previousOpenness = node.containerOpen;
            node.containerOpen = true;
            for (var child = 0; child < node.childCount && ids.length > 0; child++) {
              var childNode = node.getChild(child);
              var found = findNodes(childNode);
              if (!foundOne)
                foundOne = found;
            }

            // If we didn't find any additional matches in this node's
            // subtree, revert the node to its previous openness.
            if (foundOne)
              nodesToOpen.unshift(node);
            node.containerOpen = previousOpenness;
            return foundOne;
          }

          // Disable notifications while looking for nodes.
          let result = this.result;
          let didSuppressNotifications = result.suppressNotifications;
          if (!didSuppressNotifications)
            result.suppressNotifications = true;
          try {
            findNodes(this.result.root);
          } finally {
            if (!didSuppressNotifications)
              result.suppressNotifications = false;
          }

          // For all the nodes we've found, highlight the corresponding
          // index in the tree.
          var resultview = this.view;
          var selection = this.view.selection;
          selection.selectEventsSuppressed = true;
          selection.clearSelection();
          // Open nodes containing found items
          for (let i = 0; i < nodesToOpen.length; i++) {
            nodesToOpen[i].containerOpen = true;
          }
          for (let i = 0; i < nodes.length; i++) {
            var index = resultview.treeIndexForNode(nodes[i]);
            if (index == -1)
              continue;
            selection.rangedSelect(index, index, true);
          }
          selection.selectEventsSuppressed = false;
        ]]></body>
      </method>

      <field name="_contextMenuShown">false</field>

      <method name="buildContextMenu">
        <parameter name="aPopup"/>
        <body><![CDATA[
          this._contextMenuShown = true;
          return this.controller.buildContextMenu(aPopup);
        ]]></body>
      </method>

      <method name="destroyContextMenu">
        <parameter name="aPopup"/>
          this._contextMenuShown = false;
        <body/>
      </method>

      <property name="ownerWindow"
                readonly="true"
                onget="return window;"/>

      <field name="_active">true</field>
      <property name="active"
                onget="return this._active"
                onset="return this._active = val"/>

    </implementation>
    <handlers>
      <handler event="focus"><![CDATA[
        this._cachedInsertionPoint = undefined;

        // See select handler. We need the sidebar's places commandset to be
        // updated as well
        document.commandDispatcher.updateCommands("focus");
      ]]></handler>
      <handler event="select"><![CDATA[
        this._cachedInsertionPoint = undefined;

        // This additional complexity is here for the sidebars
        var win = window;
        while (true) {
          win.document.commandDispatcher.updateCommands("focus");
          if (win == window.top)
            break;

          win = win.parent;
        }
      ]]></handler>

      <handler event="dragstart"><![CDATA[
        if (event.target.localName != "treechildren")
          return;

        if (this.disableUserActions) {
          event.preventDefault();
          event.stopPropagation();
          return;
        }

        let nodes = this.selectedNodes;
        for (let i = 0; i < nodes.length; i++) {
          let node = nodes[i];

          // Disallow dragging the root node of a tree.
          if (!node.parent) {
            event.preventDefault();
            event.stopPropagation();
            return;
          }

          // If this node is child of a readonly container (e.g. a livemark)
          // or cannot be moved, we must force a copy.
          if (!this.controller.canMoveNode(node)) {
            event.dataTransfer.effectAllowed = "copyLink";
            break;
          }
        }

        this._controller.setDataTransfer(event);
        event.stopPropagation();
      ]]></handler>

      <handler event="dragover"><![CDATA[
        if (event.target.localName != "treechildren")
          return;

        let cell = this.treeBoxObject.getCellAt(event.clientX, event.clientY);
        let node = cell.row != -1 ?
                   this.view.nodeForTreeIndex(cell.row) :
                   this.result.root;
        // cache the dropTarget for the view
        PlacesControllerDragHelper.currentDropTarget = node;

        // We have to calculate the orientation since view.canDrop will use
        // it and we want to be consistent with the dropfeedback.
        let tbo = this.treeBoxObject;
        let rowHeight = tbo.rowHeight;
        let eventY = event.clientY - tbo.treeBody.boxObject.y -
                     rowHeight * (cell.row - tbo.getFirstVisibleRow());

        let orientation = Ci.nsITreeView.DROP_BEFORE;

        if (cell.row == -1) {
          // If the row is not valid we try to insert inside the resultNode.
          orientation = Ci.nsITreeView.DROP_ON;
        } else if (PlacesUtils.nodeIsContainer(node) &&
                 eventY > rowHeight * 0.75) {
          // If we are below the 75% of a container the treeview we try
          // to drop after the node.
          orientation = Ci.nsITreeView.DROP_AFTER;
        } else if (PlacesUtils.nodeIsContainer(node) &&
                 eventY > rowHeight * 0.25) {
          // If we are below the 25% of a container the treeview we try
          // to drop inside the node.
          orientation = Ci.nsITreeView.DROP_ON;
        }

        if (!this.view.canDrop(cell.row, orientation, event.dataTransfer))
          return;

        event.preventDefault();
        event.stopPropagation();
      ]]></handler>

      <handler event="dragend"><![CDATA[
        PlacesControllerDragHelper.currentDropTarget = null;
      ]]></handler>

    </handlers>
  </binding>

</bindings>
PK
!<��g1chrome/browser/content/browser/places/treeView.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");

const PTV_interfaces = [Ci.nsITreeView,
                        Ci.nsINavHistoryResultObserver,
                        Ci.nsISupportsWeakReference];

/**
 * This returns the key for any node/details object.
 *
 * @param nodeOrDetails
 *        A node, or an object containing the following properties:
 *        - uri
 *        - time
 *        - itemId
 *        In case any of these is missing, an empty string will be returned. This is
 *        to facilitate easy delete statements which occur due to assignment to items in `this._rows`,
 *        since the item we are deleting may be undefined in the array.
 *
 * @return key or empty string.
 */
function makeNodeDetailsKey(nodeOrDetails) {
  if (nodeOrDetails &&
      typeof nodeOrDetails === "object" &&
      "uri" in nodeOrDetails &&
      "time" in nodeOrDetails &&
      "itemId" in nodeOrDetails) {
    return `${nodeOrDetails.uri}*${nodeOrDetails.time}*${nodeOrDetails.itemId}`;
  }
  return "";
}

function PlacesTreeView(aFlatList, aOnOpenFlatContainer, aController) {
  this._tree = null;
  this._result = null;
  this._selection = null;
  this._rootNode = null;
  this._rows = [];
  this._flatList = aFlatList;
  this._nodeDetails = new Map();
  this._openContainerCallback = aOnOpenFlatContainer;
  this._controller = aController;
}

PlacesTreeView.prototype = {
  get wrappedJSObject() {
    return this;
  },

  __xulStore: null,
  get _xulStore() {
    if (!this.__xulStore) {
      this.__xulStore = Cc["@mozilla.org/xul/xulstore;1"].getService(Ci.nsIXULStore);
    }
    return this.__xulStore;
  },

  QueryInterface: XPCOMUtils.generateQI(PTV_interfaces),

  // Bug 761494:
  // ----------
  // Some addons use methods from nsINavHistoryResultObserver and
  // nsINavHistoryResultTreeViewer, without QIing to these interfaces first.
  // That's not a problem when the view is retrieved through the
  // <tree>.view getter (which returns the wrappedJSObject of this object),
  // it raises an issue when the view retrieved through the treeBoxObject.view
  // getter.  Thus, to avoid breaking addons, the interfaces are prefetched.
  classInfo: XPCOMUtils.generateCI({ interfaces: PTV_interfaces }),

  /**
   * This is called once both the result and the tree are set.
   */
  _finishInit: function PTV__finishInit() {
    let selection = this.selection;
    if (selection)
      selection.selectEventsSuppressed = true;

    if (!this._rootNode.containerOpen) {
      // This triggers containerStateChanged which then builds the visible
      // section.
      this._rootNode.containerOpen = true;
    } else
      this.invalidateContainer(this._rootNode);

    // "Activate" the sorting column and update commands.
    this.sortingChanged(this._result.sortingMode);

    if (selection)
      selection.selectEventsSuppressed = false;
  },

  uninit() {
    if (this._editingObservers) {
      for (let observer of this._editingObservers.values()) {
        observer.disconnect();
      }
      delete this._editingObservers;
    }
  },

  /**
   * Plain Container: container result nodes which may never include sub
   * hierarchies.
   *
   * When the rows array is constructed, we don't set the children of plain
   * containers.  Instead, we keep placeholders for these children.  We then
   * build these children lazily as the tree asks us for information about each
   * row.  Luckily, the tree doesn't ask about rows outside the visible area.
   *
   * @see _getNodeForRow and _getRowForNode for the actual magic.
   *
   * @note It's guaranteed that all containers are listed in the rows
   * elements array.  It's also guaranteed that separators (if they're not
   * filtered, see below) are listed in the visible elements array, because
   * bookmark folders are never built lazily, as described above.
   *
   * @param aContainer
   *        A container result node.
   *
   * @return true if aContainer is a plain container, false otherwise.
   */
  _isPlainContainer: function PTV__isPlainContainer(aContainer) {
    // Livemarks are always plain containers.
    if (this._controller.hasCachedLivemarkInfo(aContainer))
      return true;

    // We don't know enough about non-query containers.
    if (!(aContainer instanceof Ci.nsINavHistoryQueryResultNode))
      return false;

    switch (aContainer.queryOptions.resultType) {
      case Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_QUERY:
      case Ci.nsINavHistoryQueryOptions.RESULTS_AS_SITE_QUERY:
      case Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_SITE_QUERY:
      case Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_QUERY:
      case Ci.nsINavHistoryQueryOptions.RESULTS_AS_ROOTS_QUERY:
        return false;
    }

    // If it's a folder, it's not a plain container.
    let nodeType = aContainer.type;
    return nodeType != Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER &&
           nodeType != Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT;
  },

  /**
   * Gets the row number for a given node.  Assumes that the given node is
   * visible (i.e. it's not an obsolete node).
   *
   * @param aNode
   *        A result node.  Do not pass an obsolete node, or any
   *        node which isn't supposed to be in the tree (e.g. separators in
   *        sorted trees).
   * @param [optional] aForceBuild
   *        @see _isPlainContainer.
   *        If true, the row will be computed even if the node still isn't set
   *        in our rows array.
   * @param [optional] aParentRow
   *        The row of aNode's parent. Ignored for the root node.
   * @param [optional] aNodeIndex
   *        The index of aNode in its parent.  Only used if aParentRow is
   *        set too.
   *
   * @throws if aNode is invisible.
   * @note If aParentRow and aNodeIndex are passed and parent is a plain
   * container, this method will just return a calculated row value, without
   * making assumptions on existence of the node at that position.
   * @return aNode's row if it's in the rows list or if aForceBuild is set, -1
   *         otherwise.
   */
  _getRowForNode:
  function PTV__getRowForNode(aNode, aForceBuild, aParentRow, aNodeIndex) {
    if (aNode == this._rootNode)
      throw new Error("The root node is never visible");

    // A node is removed form the view either if it has no parent or if its
    // root-ancestor is not the root node (in which case that's the node
    // for which nodeRemoved was called).
    let ancestors = Array.from(PlacesUtils.nodeAncestors(aNode));
    if (ancestors.length == 0 ||
        ancestors[ancestors.length - 1] != this._rootNode) {
      throw new Error("Removed node passed to _getRowForNode");
    }

    // Ensure that the entire chain is open, otherwise that node is invisible.
    for (let ancestor of ancestors) {
      if (!ancestor.containerOpen)
        throw new Error("Invisible node passed to _getRowForNode");
    }

    // Non-plain containers are initially built with their contents.
    let parent = aNode.parent;
    let parentIsPlain = this._isPlainContainer(parent);
    if (!parentIsPlain) {
      if (parent == this._rootNode) {
        return this._rows.indexOf(aNode);
      }

      return this._rows.indexOf(aNode, aParentRow);
    }

    let row = -1;
    let useNodeIndex = typeof(aNodeIndex) == "number";
    if (parent == this._rootNode) {
      row = useNodeIndex ? aNodeIndex : this._rootNode.getChildIndex(aNode);
    } else if (useNodeIndex && typeof(aParentRow) == "number") {
      // If we have both the row of the parent node, and the node's index, we
      // can avoid searching the rows array if the parent is a plain container.
      row = aParentRow + aNodeIndex + 1;
    } else {
      // Look for the node in the nodes array.  Start the search at the parent
      // row.  If the parent row isn't passed, we'll pass undefined to indexOf,
      // which is fine.
      row = this._rows.indexOf(aNode, aParentRow);
      if (row == -1 && aForceBuild) {
        let parentRow = typeof(aParentRow) == "number" ? aParentRow
                                                       : this._getRowForNode(parent);
        row = parentRow + parent.getChildIndex(aNode) + 1;
      }
    }

    if (row != -1) {
      this._nodeDetails.delete(makeNodeDetailsKey(this._rows[row]));
      this._nodeDetails.set(makeNodeDetailsKey(aNode), aNode);
      this._rows[row] = aNode;
    }

    return row;
  },

  /**
   * Given a row, finds and returns the parent details of the associated node.
   *
   * @param aChildRow
   *        Row number.
   * @return [parentNode, parentRow]
   */
  _getParentByChildRow: function PTV__getParentByChildRow(aChildRow) {
    let node = this._getNodeForRow(aChildRow);
    let parent = (node === null) ? this._rootNode : node.parent;

    // The root node is never visible
    if (parent == this._rootNode)
      return [this._rootNode, -1];

    let parentRow = this._rows.lastIndexOf(parent, aChildRow - 1);
    return [parent, parentRow];
  },

  /**
   * Gets the node at a given row.
   */
  _getNodeForRow: function PTV__getNodeForRow(aRow) {
    if (aRow < 0) {
      return null;
    }

    let node = this._rows[aRow];
    if (node !== undefined)
      return node;

    // Find the nearest node.
    let rowNode, row;
    for (let i = aRow - 1; i >= 0 && rowNode === undefined; i--) {
      rowNode = this._rows[i];
      row = i;
    }

    // If there's no container prior to the given row, it's a child of
    // the root node (remember: all containers are listed in the rows array).
    if (!rowNode) {
      let newNode = this._rootNode.getChild(aRow);
      this._nodeDetails.delete(makeNodeDetailsKey(this._rows[aRow]));
      this._nodeDetails.set(makeNodeDetailsKey(newNode), newNode);
      return this._rows[aRow] = newNode;
    }

    // Unset elements may exist only in plain containers.  Thus, if the nearest
    // node is a container, it's the row's parent, otherwise, it's a sibling.
    if (rowNode instanceof Ci.nsINavHistoryContainerResultNode) {
      let newNode = rowNode.getChild(aRow - row - 1);
      this._nodeDetails.delete(makeNodeDetailsKey(this._rows[aRow]));
      this._nodeDetails.set(makeNodeDetailsKey(newNode), newNode);
      return this._rows[aRow] = newNode;
    }

    let [parent, parentRow] = this._getParentByChildRow(row);
    let newNode = parent.getChild(aRow - parentRow - 1);
    this._nodeDetails.delete(makeNodeDetailsKey(this._rows[aRow]));
    this._nodeDetails.set(makeNodeDetailsKey(newNode), newNode);
    return this._rows[aRow] = newNode;
  },

  /**
   * This takes a container and recursively appends our rows array per its
   * contents.  Assumes that the rows arrays has no rows for the given
   * container.
   *
   * @param [in] aContainer
   *        A container result node.
   * @param [in] aFirstChildRow
   *        The first row at which nodes may be inserted to the row array.
   *        In other words, that's aContainer's row + 1.
   * @param [out] aToOpen
   *        An array of containers to open once the build is done.
   *
   * @return the number of rows which were inserted.
   */
  _buildVisibleSection:
  function PTV__buildVisibleSection(aContainer, aFirstChildRow, aToOpen) {
    // There's nothing to do if the container is closed.
    if (!aContainer.containerOpen)
      return 0;

    // Inserting the new elements into the rows array in one shot (by
    // Array.prototype.concat) is faster than resizing the array (by splice) on each loop
    // iteration.
    let cc = aContainer.childCount;
    let newElements = new Array(cc);
    // We need to clean up the node details from aFirstChildRow + 1 to the end of rows.
    for (let i = aFirstChildRow + 1; i < this._rows.length; i++) {
      this._nodeDetails.delete(makeNodeDetailsKey(this._rows[i]));
    }
    this._rows = this._rows.splice(0, aFirstChildRow)
                     .concat(newElements, this._rows);

    if (this._isPlainContainer(aContainer))
      return cc;

    let sortingMode = this._result.sortingMode;

    let rowsInserted = 0;
    for (let i = 0; i < cc; i++) {
      let curChild = aContainer.getChild(i);
      let curChildType = curChild.type;

      let row = aFirstChildRow + rowsInserted;

      // Don't display separators when sorted.
      if (curChildType == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR) {
        if (sortingMode != Ci.nsINavHistoryQueryOptions.SORT_BY_NONE) {
          // Remove the element for the filtered separator.
          // Notice that the rows array was initially resized to include all
          // children.
          this._nodeDetails.delete(makeNodeDetailsKey(this._rows[row]));
          this._rows.splice(row, 1);
          continue;
        }
      }

      this._nodeDetails.delete(makeNodeDetailsKey(this._rows[row]));
      this._nodeDetails.set(makeNodeDetailsKey(curChild), curChild);
      this._rows[row] = curChild;
      rowsInserted++;

      // Recursively do containers.
      if (!this._flatList &&
          curChild instanceof Ci.nsINavHistoryContainerResultNode &&
          !this._controller.hasCachedLivemarkInfo(curChild)) {
        let uri = curChild.uri;
        let isopen = false;

        if (uri) {
          let val = this._xulStore.getValue(document.documentURI, uri, "open");
          isopen = (val == "true");
        }

        if (isopen != curChild.containerOpen)
          aToOpen.push(curChild);
        else if (curChild.containerOpen && curChild.childCount > 0)
          rowsInserted += this._buildVisibleSection(curChild, row + 1, aToOpen);
      }
    }

    return rowsInserted;
  },

  /**
   * This counts how many rows a node takes in the tree.  For containers it
   * will count the node itself plus any child node following it.
   */
  _countVisibleRowsForNodeAtRow:
  function PTV__countVisibleRowsForNodeAtRow(aNodeRow) {
    let node = this._rows[aNodeRow];

    // If it's not listed yet, we know that it's a leaf node (instanceof also
    // null-checks).
    if (!(node instanceof Ci.nsINavHistoryContainerResultNode))
      return 1;

    let outerLevel = node.indentLevel;
    for (let i = aNodeRow + 1; i < this._rows.length; i++) {
      let rowNode = this._rows[i];
      if (rowNode && rowNode.indentLevel <= outerLevel)
        return i - aNodeRow;
    }

    // This node plus its children take up the bottom of the list.
    return this._rows.length - aNodeRow;
  },

  _getSelectedNodesInRange:
  function PTV__getSelectedNodesInRange(aFirstRow, aLastRow) {
    let selection = this.selection;
    let rc = selection.getRangeCount();
    if (rc == 0)
      return [];

    // The visible-area borders are needed for checking whether a
    // selected row is also visible.
    let firstVisibleRow = this._tree.getFirstVisibleRow();
    let lastVisibleRow = this._tree.getLastVisibleRow();

    let nodesInfo = [];
    for (let rangeIndex = 0; rangeIndex < rc; rangeIndex++) {
      let min = { }, max = { };
      selection.getRangeAt(rangeIndex, min, max);

      // If this range does not overlap the replaced chunk, we don't need to
      // persist the selection.
      if (max.value < aFirstRow || min.value > aLastRow)
        continue;

      let firstRow = Math.max(min.value, aFirstRow);
      let lastRow = Math.min(max.value, aLastRow);
      for (let i = firstRow; i <= lastRow; i++) {
        nodesInfo.push({
          node: this._rows[i],
          oldRow: i,
          wasVisible: i >= firstVisibleRow && i <= lastVisibleRow
        });
      }
    }

    return nodesInfo;
  },

  /**
   * Tries to find an equivalent node for a node which was removed.  We first
   * look for the original node, in case it was just relocated.  Then, if we
   * that node was not found, we look for a node that has the same itemId, uri
   * and time values.
   *
   * @param aUpdatedContainer
   *        An ancestor of the node which was removed.  It does not have to be
   *        its direct parent.
   * @param aOldNode
   *        The node which was removed.
   *
   * @return the row number of an equivalent node for aOldOne, if one was
   *         found, -1 otherwise.
   */
  _getNewRowForRemovedNode:
  function PTV__getNewRowForRemovedNode(aUpdatedContainer, aOldNode) {
    let parent = aOldNode.parent;
    if (parent) {
      // If the node's parent is still set, the node is not obsolete
      // and we should just find out its new position.
      // However, if any of the node's ancestor is closed, the node is
      // invisible.
      let ancestors = PlacesUtils.nodeAncestors(aOldNode);
      for (let ancestor of ancestors) {
        if (!ancestor.containerOpen) {
          return -1;
        }
      }

      return this._getRowForNode(aOldNode, true);
    }

    // There's a broken edge case here.
    // If a visit appears in two queries, and the second one was
    // the old node, we'll select the first one after refresh.  There's
    // nothing we could do about that, because aOldNode.parent is
    // gone by the time invalidateContainer is called.
    let newNode = this._nodeDetails.get(makeNodeDetailsKey(aOldNode));

    if (!newNode)
      return -1;

    return this._getRowForNode(newNode, true);
  },

  /**
   * Restores a given selection state as near as possible to the original
   * selection state.
   *
   * @param aNodesInfo
   *        The persisted selection state as returned by
   *        _getSelectedNodesInRange.
   * @param aUpdatedContainer
   *        The container which was updated.
   */
  _restoreSelection:
  function PTV__restoreSelection(aNodesInfo, aUpdatedContainer) {
    if (aNodesInfo.length == 0)
      return;

    let selection = this.selection;

    // Attempt to ensure that previously-visible selection will be visible
    // if it's re-selected.  However, we can only ensure that for one row.
    let scrollToRow = -1;
    for (let i = 0; i < aNodesInfo.length; i++) {
      let nodeInfo = aNodesInfo[i];
      let row = this._getNewRowForRemovedNode(aUpdatedContainer,
                                              nodeInfo.node);
      // Select the found node, if any.
      if (row != -1) {
        selection.rangedSelect(row, row, true);
        if (nodeInfo.wasVisible && scrollToRow == -1)
          scrollToRow = row;
      }
    }

    // If only one node was previously selected and there's no selection now,
    // select the node at its old row, if any.
    if (aNodesInfo.length == 1 && selection.count == 0) {
      let row = Math.min(aNodesInfo[0].oldRow, this._rows.length - 1);
      if (row != -1) {
        selection.rangedSelect(row, row, true);
        if (aNodesInfo[0].wasVisible && scrollToRow == -1)
          scrollToRow = aNodesInfo[0].oldRow;
      }
    }

    if (scrollToRow != -1)
      this._tree.ensureRowIsVisible(scrollToRow);
  },

  _convertPRTimeToString: function PTV__convertPRTimeToString(aTime) {
    const MS_PER_MINUTE = 60000;
    const MS_PER_DAY = 86400000;
    let timeMs = aTime / 1000; // PRTime is in microseconds

    // Date is calculated starting from midnight, so the modulo with a day are
    // milliseconds from today's midnight.
    // getTimezoneOffset corrects that based on local time, notice midnight
    // can have a different offset during DST-change days.
    let dateObj = new Date();
    let now = dateObj.getTime() - dateObj.getTimezoneOffset() * MS_PER_MINUTE;
    let midnight = now - (now % MS_PER_DAY);
    midnight += new Date(midnight).getTimezoneOffset() * MS_PER_MINUTE;

    let timeObj = new Date(timeMs);
    return timeMs >= midnight ? this._todayFormatter.format(timeObj)
                              : this._dateFormatter.format(timeObj);
  },

  // We use a different formatter for times within the current day,
  // so we cache both a "today" formatter and a general date formatter.
  __todayFormatter: null,
  get _todayFormatter() {
    if (!this.__todayFormatter) {
      const dtOptions = { timeStyle: "short" };
      this.__todayFormatter = new Services.intl.DateTimeFormat(undefined, dtOptions);
    }
    return this.__todayFormatter;
  },

  __dateFormatter: null,
  get _dateFormatter() {
    if (!this.__dateFormatter) {
      const dtOptions = {
        dateStyle: "short",
        timeStyle: "short"
      };
      this.__dateFormatter = new Services.intl.DateTimeFormat(undefined, dtOptions);
    }
    return this.__dateFormatter;
  },

  COLUMN_TYPE_UNKNOWN: 0,
  COLUMN_TYPE_TITLE: 1,
  COLUMN_TYPE_URI: 2,
  COLUMN_TYPE_DATE: 3,
  COLUMN_TYPE_VISITCOUNT: 4,
  COLUMN_TYPE_DESCRIPTION: 5,
  COLUMN_TYPE_DATEADDED: 6,
  COLUMN_TYPE_LASTMODIFIED: 7,
  COLUMN_TYPE_TAGS: 8,

  _getColumnType: function PTV__getColumnType(aColumn) {
    let columnType = aColumn.element.getAttribute("anonid") || aColumn.id;

    switch (columnType) {
      case "title":
        return this.COLUMN_TYPE_TITLE;
      case "url":
        return this.COLUMN_TYPE_URI;
      case "date":
        return this.COLUMN_TYPE_DATE;
      case "visitCount":
        return this.COLUMN_TYPE_VISITCOUNT;
      case "description":
        return this.COLUMN_TYPE_DESCRIPTION;
      case "dateAdded":
        return this.COLUMN_TYPE_DATEADDED;
      case "lastModified":
        return this.COLUMN_TYPE_LASTMODIFIED;
      case "tags":
        return this.COLUMN_TYPE_TAGS;
    }
    return this.COLUMN_TYPE_UNKNOWN;
  },

  _sortTypeToColumnType: function PTV__sortTypeToColumnType(aSortType) {
    switch (aSortType) {
      case Ci.nsINavHistoryQueryOptions.SORT_BY_TITLE_ASCENDING:
        return [this.COLUMN_TYPE_TITLE, false];
      case Ci.nsINavHistoryQueryOptions.SORT_BY_TITLE_DESCENDING:
        return [this.COLUMN_TYPE_TITLE, true];
      case Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_ASCENDING:
        return [this.COLUMN_TYPE_DATE, false];
      case Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING:
        return [this.COLUMN_TYPE_DATE, true];
      case Ci.nsINavHistoryQueryOptions.SORT_BY_URI_ASCENDING:
        return [this.COLUMN_TYPE_URI, false];
      case Ci.nsINavHistoryQueryOptions.SORT_BY_URI_DESCENDING:
        return [this.COLUMN_TYPE_URI, true];
      case Ci.nsINavHistoryQueryOptions.SORT_BY_VISITCOUNT_ASCENDING:
        return [this.COLUMN_TYPE_VISITCOUNT, false];
      case Ci.nsINavHistoryQueryOptions.SORT_BY_VISITCOUNT_DESCENDING:
        return [this.COLUMN_TYPE_VISITCOUNT, true];
      case Ci.nsINavHistoryQueryOptions.SORT_BY_ANNOTATION_ASCENDING:
        if (this._result.sortingAnnotation == PlacesUIUtils.DESCRIPTION_ANNO)
          return [this.COLUMN_TYPE_DESCRIPTION, false];
        break;
      case Ci.nsINavHistoryQueryOptions.SORT_BY_ANNOTATION_DESCENDING:
        if (this._result.sortingAnnotation == PlacesUIUtils.DESCRIPTION_ANNO)
          return [this.COLUMN_TYPE_DESCRIPTION, true];
      case Ci.nsINavHistoryQueryOptions.SORT_BY_DATEADDED_ASCENDING:
        return [this.COLUMN_TYPE_DATEADDED, false];
      case Ci.nsINavHistoryQueryOptions.SORT_BY_DATEADDED_DESCENDING:
        return [this.COLUMN_TYPE_DATEADDED, true];
      case Ci.nsINavHistoryQueryOptions.SORT_BY_LASTMODIFIED_ASCENDING:
        return [this.COLUMN_TYPE_LASTMODIFIED, false];
      case Ci.nsINavHistoryQueryOptions.SORT_BY_LASTMODIFIED_DESCENDING:
        return [this.COLUMN_TYPE_LASTMODIFIED, true];
      case Ci.nsINavHistoryQueryOptions.SORT_BY_TAGS_ASCENDING:
        return [this.COLUMN_TYPE_TAGS, false];
      case Ci.nsINavHistoryQueryOptions.SORT_BY_TAGS_DESCENDING:
        return [this.COLUMN_TYPE_TAGS, true];
    }
    return [this.COLUMN_TYPE_UNKNOWN, false];
  },

  // nsINavHistoryResultObserver
  nodeInserted: function PTV_nodeInserted(aParentNode, aNode, aNewIndex) {
    NS_ASSERT(this._result, "Got a notification but have no result!");
    if (!this._tree || !this._result)
      return;

    // Bail out for hidden separators.
    if (PlacesUtils.nodeIsSeparator(aNode) && this.isSorted())
      return;

    let parentRow;
    if (aParentNode != this._rootNode) {
      parentRow = this._getRowForNode(aParentNode);

      // Update parent when inserting the first item, since twisty has changed.
      if (aParentNode.childCount == 1)
        this._tree.invalidateRow(parentRow);
    }

    // Compute the new row number of the node.
    let row = -1;
    let cc = aParentNode.childCount;
    if (aNewIndex == 0 || this._isPlainContainer(aParentNode) || cc == 0) {
      // We don't need to worry about sub hierarchies of the parent node
      // if it's a plain container, or if the new node is its first child.
      if (aParentNode == this._rootNode)
        row = aNewIndex;
      else
        row = parentRow + aNewIndex + 1;
    } else {
      // Here, we try to find the next visible element in the child list so we
      // can set the new visible index to be right before that.  Note that we
      // have to search down instead of up, because some siblings could have
      // children themselves that would be in the way.
      let separatorsAreHidden = PlacesUtils.nodeIsSeparator(aNode) &&
                                this.isSorted();
      for (let i = aNewIndex + 1; i < cc; i++) {
        let node = aParentNode.getChild(i);
        if (!separatorsAreHidden || PlacesUtils.nodeIsSeparator(node)) {
          // The children have not been shifted so the next item will have what
          // should be our index.
          row = this._getRowForNode(node, false, parentRow, i);
          break;
        }
      }
      if (row < 0) {
        // At the end of the child list without finding a visible sibling. This
        // is a little harder because we don't know how many rows the last item
        // in our list takes up (it could be a container with many children).
        let prevChild = aParentNode.getChild(aNewIndex - 1);
        let prevIndex = this._getRowForNode(prevChild, false, parentRow,
                                            aNewIndex - 1);
        row = prevIndex + this._countVisibleRowsForNodeAtRow(prevIndex);
      }
    }

    this._nodeDetails.set(makeNodeDetailsKey(aNode), aNode);
    this._rows.splice(row, 0, aNode);
    this._tree.rowCountChanged(row, 1);

    if (PlacesUtils.nodeIsContainer(aNode) &&
        PlacesUtils.asContainer(aNode).containerOpen) {
      this.invalidateContainer(aNode);
    }
  },

  /**
   * THIS FUNCTION DOES NOT HANDLE cases where a collapsed node is being
   * removed but the node it is collapsed with is not being removed (this then
   * just swap out the removee with its collapsing partner). The only time
   * when we really remove things is when deleting URIs, which will apply to
   * all collapsees. This function is called sometimes when resorting items.
   * However, we won't do this when sorted by date because dates will never
   * change for visits, and date sorting is the only time things are collapsed.
   */
  nodeRemoved: function PTV_nodeRemoved(aParentNode, aNode, aOldIndex) {
    NS_ASSERT(this._result, "Got a notification but have no result!");
    if (!this._tree || !this._result)
      return;

    // XXX bug 517701: We don't know what to do when the root node is removed.
    if (aNode == this._rootNode)
      throw Cr.NS_ERROR_NOT_IMPLEMENTED;

    // Bail out for hidden separators.
    if (PlacesUtils.nodeIsSeparator(aNode) && this.isSorted())
      return;

    let parentRow = aParentNode == this._rootNode ?
                    undefined : this._getRowForNode(aParentNode, true);
    let oldRow = this._getRowForNode(aNode, true, parentRow, aOldIndex);
    if (oldRow < 0)
      throw Cr.NS_ERROR_UNEXPECTED;

    // If the node was exclusively selected, the node next to it will be
    // selected.
    let selectNext = false;
    let selection = this.selection;
    if (selection.getRangeCount() == 1) {
      let min = { }, max = { };
      selection.getRangeAt(0, min, max);
      if (min.value == max.value &&
          this.nodeForTreeIndex(min.value) == aNode)
        selectNext = true;
    }

    // Remove the node and its children, if any.
    let count = this._countVisibleRowsForNodeAtRow(oldRow);
    for (let splicedNode of this._rows.splice(oldRow, count)) {
      this._nodeDetails.delete(makeNodeDetailsKey(splicedNode));
    }
    this._tree.rowCountChanged(oldRow, -count);

    // Redraw the parent if its twisty state has changed.
    if (aParentNode != this._rootNode && !aParentNode.hasChildren) {
      parentRow = oldRow - 1;
      this._tree.invalidateRow(parentRow);
    }

    // Restore selection if the node was exclusively selected.
    if (!selectNext)
      return;

    // Restore selection.
    let rowToSelect = Math.min(oldRow, this._rows.length - 1);
    if (rowToSelect != -1)
      this.selection.rangedSelect(rowToSelect, rowToSelect, true);
  },

  nodeMoved:
  function PTV_nodeMoved(aNode, aOldParent, aOldIndex, aNewParent, aNewIndex) {
    NS_ASSERT(this._result, "Got a notification but have no result!");
    if (!this._tree || !this._result)
      return;

    // Bail out for hidden separators.
    if (PlacesUtils.nodeIsSeparator(aNode) && this.isSorted())
      return;

    // Note that at this point the node has already been moved by the backend,
    // so we must give hints to _getRowForNode to get the old row position.
    let oldParentRow = aOldParent == this._rootNode ?
                         undefined : this._getRowForNode(aOldParent, true);
    let oldRow = this._getRowForNode(aNode, true, oldParentRow, aOldIndex);
    if (oldRow < 0)
      throw Cr.NS_ERROR_UNEXPECTED;

    // If this node is a container it could take up more than one row.
    let count = this._countVisibleRowsForNodeAtRow(oldRow);

    // Persist selection state.
    let nodesToReselect =
      this._getSelectedNodesInRange(oldRow, oldRow + count);
    if (nodesToReselect.length > 0)
      this.selection.selectEventsSuppressed = true;

    // Redraw the parent if its twisty state has changed.
    if (aOldParent != this._rootNode && !aOldParent.hasChildren) {
      let parentRow = oldRow - 1;
      this._tree.invalidateRow(parentRow);
    }

    // Remove node and its children, if any, from the old position.
    for (let splicedNode of this._rows.splice(oldRow, count)) {
      this._nodeDetails.delete(makeNodeDetailsKey(splicedNode));
    }
    this._tree.rowCountChanged(oldRow, -count);

    // Insert the node into the new position.
    this.nodeInserted(aNewParent, aNode, aNewIndex);

    // Restore selection.
    if (nodesToReselect.length > 0) {
      this._restoreSelection(nodesToReselect, aNewParent);
      this.selection.selectEventsSuppressed = false;
    }
  },

  _invalidateCellValue: function PTV__invalidateCellValue(aNode,
                                                          aColumnType) {
    NS_ASSERT(this._result, "Got a notification but have no result!");
    if (!this._tree || !this._result)
      return;

    // Nothing to do for the root node.
    if (aNode == this._rootNode)
      return;

    let row = this._getRowForNode(aNode);
    if (row == -1)
      return;

    let column = this._findColumnByType(aColumnType);
    if (column && !column.element.hidden) {
      if (aColumnType == this.COLUMN_TYPE_TITLE)
        this._tree.removeImageCacheEntry(row, column);
      this._tree.invalidateCell(row, column);
    }

    // Last modified time is altered for almost all node changes.
    if (aColumnType != this.COLUMN_TYPE_LASTMODIFIED) {
      let lastModifiedColumn =
        this._findColumnByType(this.COLUMN_TYPE_LASTMODIFIED);
      if (lastModifiedColumn && !lastModifiedColumn.hidden)
        this._tree.invalidateCell(row, lastModifiedColumn);
    }
  },

  _populateLivemarkContainer: function PTV__populateLivemarkContainer(aNode) {
    PlacesUtils.livemarks.getLivemark({ id: aNode.itemId })
      .then(aLivemark => {
        let placesNode = aNode;
        // Need to check containerOpen since getLivemark is async.
        if (!placesNode.containerOpen)
          return;

        let children = aLivemark.getNodesForContainer(placesNode);
        for (let i = 0; i < children.length; i++) {
          let child = children[i];
          this.nodeInserted(placesNode, child, i);
        }
      }, Cu.reportError);
  },

  nodeTitleChanged: function PTV_nodeTitleChanged(aNode, aNewTitle) {
    this._invalidateCellValue(aNode, this.COLUMN_TYPE_TITLE);
  },

  nodeURIChanged: function PTV_nodeURIChanged(aNode, aOldURI) {
    this._nodeDetails.delete(makeNodeDetailsKey({uri: aOldURI,
                                                 itemId: aNode.itemId,
                                                 time: aNode.time}));
    this._nodeDetails.set(makeNodeDetailsKey(aNode), aNode);
    this._invalidateCellValue(aNode, this.COLUMN_TYPE_URI);
  },

  nodeIconChanged: function PTV_nodeIconChanged(aNode) {
    this._invalidateCellValue(aNode, this.COLUMN_TYPE_TITLE);
  },

  nodeHistoryDetailsChanged:
  function PTV_nodeHistoryDetailsChanged(aNode, aOldVisitDate,
                                         aOldVisitCount) {
    this._nodeDetails.delete(makeNodeDetailsKey({uri: aNode.uri,
                                                 itemId: aNode.itemId,
                                                 time: aOldVisitDate}));
    this._nodeDetails.set(makeNodeDetailsKey(aNode), aNode);
    if (aNode.parent && this._controller.hasCachedLivemarkInfo(aNode.parent)) {
      // Find the node in the parent.
      let parentRow = this._flatList ? 0 : this._getRowForNode(aNode.parent);
      for (let i = parentRow; i < this._rows.length; i++) {
        let child = this.nodeForTreeIndex(i);
        if (child.uri == aNode.uri) {
          this._cellProperties.delete(child);
          this._invalidateCellValue(child, this.COLUMN_TYPE_TITLE);
          break;
        }
      }
      return;
    }

    this._invalidateCellValue(aNode, this.COLUMN_TYPE_DATE);
    this._invalidateCellValue(aNode, this.COLUMN_TYPE_VISITCOUNT);
  },

  nodeTagsChanged: function PTV_nodeTagsChanged(aNode) {
    this._invalidateCellValue(aNode, this.COLUMN_TYPE_TAGS);
  },

  nodeKeywordChanged(aNode, aNewKeyword) {},

  nodeAnnotationChanged: function PTV_nodeAnnotationChanged(aNode, aAnno) {
    if (aAnno == PlacesUIUtils.DESCRIPTION_ANNO) {
      this._invalidateCellValue(aNode, this.COLUMN_TYPE_DESCRIPTION);
    } else if (aAnno == PlacesUtils.LMANNO_FEEDURI) {
      PlacesUtils.livemarks.getLivemark({ id: aNode.itemId })
        .then(aLivemark => {
          this._controller.cacheLivemarkInfo(aNode, aLivemark);
          let properties = this._cellProperties.get(aNode);
          this._cellProperties.set(aNode, properties += " livemark");
          // The livemark attribute is set as a cell property on the title cell.
          this._invalidateCellValue(aNode, this.COLUMN_TYPE_TITLE);
        }, Cu.reportError);
    }
  },

  nodeDateAddedChanged: function PTV_nodeDateAddedChanged(aNode, aNewValue) {
    this._invalidateCellValue(aNode, this.COLUMN_TYPE_DATEADDED);
  },

  nodeLastModifiedChanged:
  function PTV_nodeLastModifiedChanged(aNode, aNewValue) {
    this._invalidateCellValue(aNode, this.COLUMN_TYPE_LASTMODIFIED);
  },

  containerStateChanged:
  function PTV_containerStateChanged(aNode, aOldState, aNewState) {
    this.invalidateContainer(aNode);

    if (PlacesUtils.nodeIsFolder(aNode) ||
        (this._flatList && aNode == this._rootNode)) {
      let queryOptions = PlacesUtils.asQuery(this._rootNode).queryOptions;
      if (queryOptions.excludeItems) {
        return;
      }
      if (aNode.itemId != -1) { // run when there's a valid node id
        PlacesUtils.livemarks.getLivemark({ id: aNode.itemId })
          .then(aLivemark => {
            let shouldInvalidate =
              !this._controller.hasCachedLivemarkInfo(aNode);
            this._controller.cacheLivemarkInfo(aNode, aLivemark);
            if (aNewState == Ci.nsINavHistoryContainerResultNode.STATE_OPENED) {
              aLivemark.registerForUpdates(aNode, this);
              // Prioritize the current livemark.
              aLivemark.reload();
              PlacesUtils.livemarks.reloadLivemarks();
              if (shouldInvalidate)
                this.invalidateContainer(aNode);
            } else {
              aLivemark.unregisterForUpdates(aNode);
            }
          }, () => undefined);
      }
    }
  },

  invalidateContainer: function PTV_invalidateContainer(aContainer) {
    NS_ASSERT(this._result, "Need to have a result to update");
    if (!this._tree)
      return;

    // If we are currently editing, don't invalidate the container until we
    // finish.
    if (this._tree.element.getAttribute("editing")) {
      if (!this._editingObservers) {
        this._editingObservers = new Map();
      }
      if (!this._editingObservers.has(aContainer)) {
        let mutationObserver = new MutationObserver(() => {
          Services.tm.dispatchToMainThread(
            () => this.invalidateContainer(aContainer));
          let observer = this._editingObservers.get(aContainer);
          observer.disconnect();
          this._editingObservers.delete(aContainer);
        });

        mutationObserver.observe(this._tree.element, {
          attributes: true,
          attributeFilter: ["editing"],
        });

        this._editingObservers.set(aContainer, mutationObserver);
      }
      return;
    }

    let startReplacement, replaceCount;
    if (aContainer == this._rootNode) {
      startReplacement = 0;
      replaceCount = this._rows.length;

      // If the root node is now closed, the tree is empty.
      if (!this._rootNode.containerOpen) {
        this._nodeDetails.clear();
        this._rows = [];
        if (replaceCount)
          this._tree.rowCountChanged(startReplacement, -replaceCount);

        return;
      }
    } else {
      // Update the twisty state.
      let row = this._getRowForNode(aContainer);
      this._tree.invalidateRow(row);

      // We don't replace the container node itself, so we should decrease the
      // replaceCount by 1.
      startReplacement = row + 1;
      replaceCount = this._countVisibleRowsForNodeAtRow(row) - 1;
    }

    // Persist selection state.
    let nodesToReselect =
      this._getSelectedNodesInRange(startReplacement,
                                    startReplacement + replaceCount);

    // Now update the number of elements.
    this.selection.selectEventsSuppressed = true;

    // First remove the old elements
    for (let splicedNode of this._rows.splice(startReplacement, replaceCount)) {
      this._nodeDetails.delete(makeNodeDetailsKey(splicedNode));
    }

    // If the container is now closed, we're done.
    if (!aContainer.containerOpen) {
      let oldSelectionCount = this.selection.count;
      if (replaceCount)
        this._tree.rowCountChanged(startReplacement, -replaceCount);

      // Select the row next to the closed container if any of its
      // children were selected, and nothing else is selected.
      if (nodesToReselect.length > 0 &&
          nodesToReselect.length == oldSelectionCount) {
        this.selection.rangedSelect(startReplacement, startReplacement, true);
        this._tree.ensureRowIsVisible(startReplacement);
      }

      this.selection.selectEventsSuppressed = false;
      return;
    }

    // Otherwise, start a batch first.
    this._tree.beginUpdateBatch();
    if (replaceCount)
      this._tree.rowCountChanged(startReplacement, -replaceCount);

    let toOpenElements = [];
    let elementsAddedCount = this._buildVisibleSection(aContainer,
                                                       startReplacement,
                                                       toOpenElements);
    if (elementsAddedCount)
      this._tree.rowCountChanged(startReplacement, elementsAddedCount);

    if (!this._flatList) {
      // Now, open any containers that were persisted.
      for (let i = 0; i < toOpenElements.length; i++) {
        let item = toOpenElements[i];
        let parent = item.parent;

        // Avoid recursively opening containers.
        while (parent) {
          if (parent.uri == item.uri)
            break;
          parent = parent.parent;
        }

        // If we don't have a parent, we made it all the way to the root
        // and didn't find a match, so we can open our item.
        if (!parent && !item.containerOpen)
          item.containerOpen = true;
      }
    }

    if (this._controller.hasCachedLivemarkInfo(aContainer)) {
      let queryOptions = PlacesUtils.asQuery(this._result.root).queryOptions;
      if (!queryOptions.excludeItems) {
        this._populateLivemarkContainer(aContainer);
      }
    }

    this._tree.endUpdateBatch();

    // Restore selection.
    this._restoreSelection(nodesToReselect, aContainer);
    this.selection.selectEventsSuppressed = false;
  },

  _columns: [],
  _findColumnByType: function PTV__findColumnByType(aColumnType) {
    if (this._columns[aColumnType])
      return this._columns[aColumnType];

    let columns = this._tree.columns;
    let colCount = columns.count;
    for (let i = 0; i < colCount; i++) {
      let column = columns.getColumnAt(i);
      let columnType = this._getColumnType(column);
      this._columns[columnType] = column;
      if (columnType == aColumnType)
        return column;
    }

    // That's completely valid.  Most of our trees actually include just the
    // title column.
    return null;
  },

  sortingChanged: function PTV__sortingChanged(aSortingMode) {
    if (!this._tree || !this._result)
      return;

    // Depending on the sort mode, certain commands may be disabled.
    window.updateCommands("sort");

    let columns = this._tree.columns;

    // Clear old sorting indicator.
    let sortedColumn = columns.getSortedColumn();
    if (sortedColumn)
      sortedColumn.element.removeAttribute("sortDirection");

    // Set new sorting indicator by looking through all columns for ours.
    if (aSortingMode == Ci.nsINavHistoryQueryOptions.SORT_BY_NONE)
      return;

    let [desiredColumn, desiredIsDescending] =
      this._sortTypeToColumnType(aSortingMode);
    let column = this._findColumnByType(desiredColumn);
    if (column) {
      let sortDir = desiredIsDescending ? "descending" : "ascending";
      column.element.setAttribute("sortDirection", sortDir);
    }
  },

  _inBatchMode: false,
  batching: function PTV__batching(aToggleMode) {
    if (this._inBatchMode != aToggleMode) {
      this._inBatchMode = this.selection.selectEventsSuppressed = aToggleMode;
      if (this._inBatchMode) {
        this._tree.beginUpdateBatch();
      } else {
        this._tree.endUpdateBatch();
      }
    }
  },

  get result() {
    return this._result;
  },
  set result(val) {
    if (this._result) {
      this._result.removeObserver(this);
      this._rootNode.containerOpen = false;
    }

    if (val) {
      this._result = val;
      this._rootNode = this._result.root;
      this._cellProperties = new Map();
      this._cuttingNodes = new Set();
    } else if (this._result) {
      delete this._result;
      delete this._rootNode;
      delete this._cellProperties;
      delete this._cuttingNodes;
    }

    // If the tree is not set yet, setTree will call finishInit.
    if (this._tree && val)
      this._finishInit();

    return val;
  },

  /**
   * This allows you to get at the real node for a given row index. This is
   * only valid when a tree is attached.
   *
   * @param {Integer} aIndex The index for the node to get.
   * @return {Ci.nsINavHistoryResultNode} The node.
   * @throws Cr.NS_ERROR_INVALID_ARG if the index is greater than the number of
   *                                 rows.
   */
  nodeForTreeIndex(aIndex) {
    if (aIndex > this._rows.length)
      throw Cr.NS_ERROR_INVALID_ARG;

    return this._getNodeForRow(aIndex);
  },

  /**
   * Reverse of nodeForTreeIndex, returns the row index for a given result node.
   * The node should be part of the tree.
   *
   * @param {Ci.nsINavHistoryResultNode} aNode The node to look for in the tree.
   * @returns {Integer} The found index, or -1 if the item is not visible or not found.
   */
  treeIndexForNode(aNode) {
    // The API allows passing invisible nodes.
    try {
      return this._getRowForNode(aNode, true);
    } catch (ex) { }

    return -1;
  },

  // nsITreeView
  get rowCount() {
    return this._rows.length;
  },
  get selection() {
    return this._selection;
  },
  set selection(val) {
    this._selection = val;
  },

  getRowProperties() { return ""; },

  getCellProperties:
  function PTV_getCellProperties(aRow, aColumn) {
    // for anonid-trees, we need to add the column-type manually
    var props = "";
    let columnType = aColumn.element.getAttribute("anonid");
    if (columnType)
      props += columnType;
    else
      columnType = aColumn.id;

    // Set the "ltr" property on url cells
    if (columnType == "url")
      props += " ltr";

    if (columnType != "title")
      return props;

    let node = this._getNodeForRow(aRow);

    if (this._cuttingNodes.has(node)) {
      props += " cutting";
    }

    let properties = this._cellProperties.get(node);
    if (properties === undefined) {
      properties = "";
      let itemId = node.itemId;
      let nodeType = node.type;
      if (PlacesUtils.containerTypes.includes(nodeType)) {
        if (nodeType == Ci.nsINavHistoryResultNode.RESULT_TYPE_QUERY) {
          properties += " query";
          if (PlacesUtils.nodeIsTagQuery(node))
            properties += " tagContainer";
          else if (PlacesUtils.nodeIsDay(node))
            properties += " dayContainer";
          else if (PlacesUtils.nodeIsHost(node))
            properties += " hostContainer";
        } else if (nodeType == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER ||
                 nodeType == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT) {
          if (itemId != -1) {
            if (this._controller.hasCachedLivemarkInfo(node)) {
              properties += " livemark";
            } else {
              PlacesUtils.livemarks.getLivemark({ id: itemId })
                .then(aLivemark => {
                  this._controller.cacheLivemarkInfo(node, aLivemark);
                  let livemarkProps = this._cellProperties.get(node);
                  this._cellProperties.set(node, livemarkProps += " livemark");
                  // The livemark attribute is set as a cell property on the title cell.
                  this._invalidateCellValue(node, this.COLUMN_TYPE_TITLE);
                }, () => undefined);
            }
          }
        }

        if (itemId == -1) {
          switch (node.bookmarkGuid) {
          case PlacesUtils.bookmarks.virtualToolbarGuid:
            properties += ` queryFolder_${PlacesUtils.bookmarks.toolbarGuid}`;
            break;
          case PlacesUtils.bookmarks.virtualMenuGuid:
            properties += ` queryFolder_${PlacesUtils.bookmarks.menuGuid}`;
            break;
          case PlacesUtils.bookmarks.virtualUnfiledGuid:
            properties += ` queryFolder_${PlacesUtils.bookmarks.unfiledGuid}`;
            break;
          }
        } else {
          let queryName = PlacesUIUtils.getLeftPaneQueryNameFromId(itemId);
          if (queryName)
            properties += " OrganizerQuery_" + queryName;
        }
      } else if (nodeType == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR)
        properties += " separator";
      else if (PlacesUtils.nodeIsURI(node)) {
        properties += " " + PlacesUIUtils.guessUrlSchemeForUI(node.uri);

        if (this._controller.hasCachedLivemarkInfo(node.parent)) {
          properties += " livemarkItem";
          if (node.accessCount) {
            properties += " visited";
          }
        }
      }

      this._cellProperties.set(node, properties);
    }

    return props + " " + properties;
  },

  getColumnProperties(aColumn) { return ""; },

  isContainer: function PTV_isContainer(aRow) {
    // Only leaf nodes aren't listed in the rows array.
    let node = this._rows[aRow];
    if (node === undefined || !PlacesUtils.nodeIsContainer(node))
      return false;

    // Flat-lists may ignore expandQueries and other query options when
    // they are asked to open a container.
    if (this._flatList)
      return true;

    // Treat non-expandable childless queries as non-containers, unless they
    // are tags.
    if (PlacesUtils.nodeIsQuery(node) && !PlacesUtils.nodeIsTagQuery(node)) {
      PlacesUtils.asQuery(node);
      return node.queryOptions.expandQueries || node.hasChildren;
    }
    return true;
  },

  isContainerOpen: function PTV_isContainerOpen(aRow) {
    if (this._flatList)
      return false;

    // All containers are listed in the rows array.
    return this._rows[aRow].containerOpen;
  },

  isContainerEmpty: function PTV_isContainerEmpty(aRow) {
    if (this._flatList)
      return true;

    let node = this._rows[aRow];
    if (this._controller.hasCachedLivemarkInfo(node)) {
      let queryOptions = PlacesUtils.asQuery(this._result.root).queryOptions;
      return queryOptions.excludeItems;
    }

    // All containers are listed in the rows array.
    return !node.hasChildren;
  },

  isSeparator: function PTV_isSeparator(aRow) {
    // All separators are listed in the rows array.
    let node = this._rows[aRow];
    return node && PlacesUtils.nodeIsSeparator(node);
  },

  isSorted: function PTV_isSorted() {
    return this._result.sortingMode !=
           Ci.nsINavHistoryQueryOptions.SORT_BY_NONE;
  },

  canDrop: function PTV_canDrop(aRow, aOrientation, aDataTransfer) {
    if (!this._result)
      throw Cr.NS_ERROR_UNEXPECTED;

    if (this._controller.disableUserActions) {
      return false;
    }

    // Drop position into a sorted treeview would be wrong.
    if (this.isSorted())
      return false;

    let ip = this._getInsertionPoint(aRow, aOrientation);
    return ip && PlacesControllerDragHelper.canDrop(ip, aDataTransfer);
  },

  _getInsertionPoint: function PTV__getInsertionPoint(index, orientation) {
    let container = this._result.root;
    let dropNearNode = null;
    // When there's no selection, assume the container is the container
    // the view is populated from (i.e. the result's itemId).
    if (index != -1) {
      let lastSelected = this.nodeForTreeIndex(index);
      if (this.isContainer(index) && orientation == Ci.nsITreeView.DROP_ON) {
        // If the last selected item is an open container, append _into_
        // it, rather than insert adjacent to it.
        container = lastSelected;
        index = -1;
      } else if (lastSelected.containerOpen &&
               orientation == Ci.nsITreeView.DROP_AFTER &&
               lastSelected.hasChildren) {
        // If the last selected node is an open container and the user is
        // trying to drag into it as a first node, really insert into it.
        container = lastSelected;
        orientation = Ci.nsITreeView.DROP_ON;
        index = 0;
      } else {
        // Use the last-selected node's container.
        container = lastSelected.parent;

        // During its Drag & Drop operation, the tree code closes-and-opens
        // containers very often (part of the XUL "spring-loaded folders"
        // implementation).  And in certain cases, we may reach a closed
        // container here.  However, we can simply bail out when this happens,
        // because we would then be back here in less than a millisecond, when
        // the container had been reopened.
        if (!container || !container.containerOpen)
          return null;

        // Avoid the potentially expensive call to getChildIndex
        // if we know this container doesn't allow insertion.
        if (this._controller.disallowInsertion(container))
          return null;

        let queryOptions = PlacesUtils.asQuery(this._result.root).queryOptions;
        if (queryOptions.sortingMode !=
              Ci.nsINavHistoryQueryOptions.SORT_BY_NONE) {
          // If we are within a sorted view, insert at the end.
          index = -1;
        } else if (queryOptions.excludeItems ||
                 queryOptions.excludeQueries ||
                 queryOptions.excludeReadOnlyFolders) {
          // Some item may be invisible, insert near last selected one.
          // We don't replace index here to avoid requests to the db,
          // instead it will be calculated later by the controller.
          index = -1;
          dropNearNode = lastSelected;
        } else {
          let lsi = container.getChildIndex(lastSelected);
          index = orientation == Ci.nsITreeView.DROP_BEFORE ? lsi : lsi + 1;
        }
      }
    }

    if (this._controller.disallowInsertion(container))
      return null;

    // TODO (Bug 1160193): properly support dropping on a tag root.
    let tagName = null;
    if (PlacesUtils.nodeIsTagQuery(container)) {
      tagName = container.title;
      if (!tagName)
        return null;
    }

    return new PlacesInsertionPoint({
      parentId: PlacesUtils.getConcreteItemId(container),
      parentGuid: PlacesUtils.getConcreteItemGuid(container),
      index, orientation, tagName, dropNearNode
    });
  },

  drop: function PTV_drop(aRow, aOrientation, aDataTransfer) {
    if (this._controller.disableUserActions) {
      return;
    }

    // We are responsible for translating the |index| and |orientation|
    // parameters into a container id and index within the container,
    // since this information is specific to the tree view.
    let ip = this._getInsertionPoint(aRow, aOrientation);
    if (ip) {
      PlacesControllerDragHelper.onDrop(ip, aDataTransfer, this._tree.element)
                                .catch(Cu.reportError)
                                .then(() => {
                                  // We should only clear the drop target once
                                  // the onDrop is complete, as it is an async function.
                                  PlacesControllerDragHelper.currentDropTarget = null;
                                });
    }
  },

  getParentIndex: function PTV_getParentIndex(aRow) {
    let [, parentRow] = this._getParentByChildRow(aRow);
    return parentRow;
  },

  hasNextSibling: function PTV_hasNextSibling(aRow, aAfterIndex) {
    if (aRow == this._rows.length - 1) {
      // The last row has no sibling.
      return false;
    }

    let node = this._rows[aRow];
    if (node === undefined || this._isPlainContainer(node.parent)) {
      // The node is a child of a plain container.
      // If the next row is either unset or has the same parent,
      // it's a sibling.
      let nextNode = this._rows[aRow + 1];
      return (nextNode == undefined || nextNode.parent == node.parent);
    }

    let thisLevel = node.indentLevel;
    for (let i = aAfterIndex + 1; i < this._rows.length; ++i) {
      let rowNode = this._getNodeForRow(i);
      let nextLevel = rowNode.indentLevel;
      if (nextLevel == thisLevel)
        return true;
      if (nextLevel < thisLevel)
        break;
    }

    return false;
  },

  getLevel(aRow) {
    return this._getNodeForRow(aRow).indentLevel;
  },

  getImageSrc: function PTV_getImageSrc(aRow, aColumn) {
    // Only the title column has an image.
    if (this._getColumnType(aColumn) != this.COLUMN_TYPE_TITLE)
      return "";

    let node = this._getNodeForRow(aRow);
    return node.icon;
  },

  getCellValue(aRow, aColumn) { },

  getCellText: function PTV_getCellText(aRow, aColumn) {
    let node = this._getNodeForRow(aRow);
    switch (this._getColumnType(aColumn)) {
      case this.COLUMN_TYPE_TITLE:
        // normally, this is just the title, but we don't want empty items in
        // the tree view so return a special string if the title is empty.
        // Do it here so that callers can still get at the 0 length title
        // if they go through the "result" API.
        if (PlacesUtils.nodeIsSeparator(node))
          return "";
        return PlacesUIUtils.getBestTitle(node, true);
      case this.COLUMN_TYPE_TAGS:
        return node.tags;
      case this.COLUMN_TYPE_URI:
        if (PlacesUtils.nodeIsURI(node))
          return node.uri;
        return "";
      case this.COLUMN_TYPE_DATE:
        let nodeTime = node.time;
        if (nodeTime == 0 || !PlacesUtils.nodeIsURI(node)) {
          // hosts and days shouldn't have a value for the date column.
          // Actually, you could argue this point, but looking at the
          // results, seeing the most recently visited date is not what
          // I expect, and gives me no information I know how to use.
          // Only show this for URI-based items.
          return "";
        }

        return this._convertPRTimeToString(nodeTime);
      case this.COLUMN_TYPE_VISITCOUNT:
        return node.accessCount;
      case this.COLUMN_TYPE_DESCRIPTION:
        if (node.itemId != -1) {
          try {
            return PlacesUtils.annotations.
                               getItemAnnotation(node.itemId, PlacesUIUtils.DESCRIPTION_ANNO);
          } catch (ex) { /* has no description */ }
        }
        return "";
      case this.COLUMN_TYPE_DATEADDED:
        if (node.dateAdded)
          return this._convertPRTimeToString(node.dateAdded);
        return "";
      case this.COLUMN_TYPE_LASTMODIFIED:
        if (node.lastModified)
          return this._convertPRTimeToString(node.lastModified);
        return "";
    }
    return "";
  },

  setTree: function PTV_setTree(aTree) {
    // If we are replacing the tree during a batch, there is a concrete risk
    // that the treeView goes out of sync, thus it's safer to end the batch now.
    // This is a no-op if we are not batching.
    this.batching(false);

    let hasOldTree = this._tree != null;
    this._tree = aTree;

    if (this._result) {
      if (hasOldTree) {
        // detach from result when we are detaching from the tree.
        // This breaks the reference cycle between us and the result.
        if (!aTree) {
          this._result.removeObserver(this);
          this._rootNode.containerOpen = false;
        }
      }
      if (aTree)
        this._finishInit();
    }
  },

  toggleOpenState: function PTV_toggleOpenState(aRow) {
    if (!this._result)
      throw Cr.NS_ERROR_UNEXPECTED;

    let node = this._rows[aRow];
    if (this._flatList && this._openContainerCallback) {
      this._openContainerCallback(node);
      return;
    }

    // Persist containers open status, but never persist livemarks.
    if (!this._controller.hasCachedLivemarkInfo(node)) {
      let uri = node.uri;

      if (uri) {
        let docURI = document.documentURI;

        if (node.containerOpen) {
          this._xulStore.removeValue(docURI, uri, "open");
        } else {
          this._xulStore.setValue(docURI, uri, "open", "true");
        }
      }
    }

    node.containerOpen = !node.containerOpen;
  },

  cycleHeader: function PTV_cycleHeader(aColumn) {
    if (!this._result)
      throw Cr.NS_ERROR_UNEXPECTED;

    // Sometimes you want a tri-state sorting, and sometimes you don't. This
    // rule allows tri-state sorting when the root node is a folder. This will
    // catch the most common cases. When you are looking at folders, you want
    // the third state to reset the sorting to the natural bookmark order. When
    // you are looking at history, that third state has no meaning so we try
    // to disallow it.
    //
    // The problem occurs when you have a query that results in bookmark
    // folders. One example of this is the subscriptions view. In these cases,
    // this rule doesn't allow you to sort those sub-folders by their natural
    // order.
    let allowTriState = PlacesUtils.nodeIsFolder(this._result.root);

    let oldSort = this._result.sortingMode;
    let oldSortingAnnotation = this._result.sortingAnnotation;
    let newSort;
    let newSortingAnnotation = "";
    const NHQO = Ci.nsINavHistoryQueryOptions;
    switch (this._getColumnType(aColumn)) {
      case this.COLUMN_TYPE_TITLE:
        if (oldSort == NHQO.SORT_BY_TITLE_ASCENDING)
          newSort = NHQO.SORT_BY_TITLE_DESCENDING;
        else if (allowTriState && oldSort == NHQO.SORT_BY_TITLE_DESCENDING)
          newSort = NHQO.SORT_BY_NONE;
        else
          newSort = NHQO.SORT_BY_TITLE_ASCENDING;

        break;
      case this.COLUMN_TYPE_URI:
        if (oldSort == NHQO.SORT_BY_URI_ASCENDING)
          newSort = NHQO.SORT_BY_URI_DESCENDING;
        else if (allowTriState && oldSort == NHQO.SORT_BY_URI_DESCENDING)
          newSort = NHQO.SORT_BY_NONE;
        else
          newSort = NHQO.SORT_BY_URI_ASCENDING;

        break;
      case this.COLUMN_TYPE_DATE:
        if (oldSort == NHQO.SORT_BY_DATE_ASCENDING)
          newSort = NHQO.SORT_BY_DATE_DESCENDING;
        else if (allowTriState &&
                 oldSort == NHQO.SORT_BY_DATE_DESCENDING)
          newSort = NHQO.SORT_BY_NONE;
        else
          newSort = NHQO.SORT_BY_DATE_ASCENDING;

        break;
      case this.COLUMN_TYPE_VISITCOUNT:
        // visit count default is unusual because we sort by descending
        // by default because you are most likely to be looking for
        // highly visited sites when you click it
        if (oldSort == NHQO.SORT_BY_VISITCOUNT_DESCENDING)
          newSort = NHQO.SORT_BY_VISITCOUNT_ASCENDING;
        else if (allowTriState && oldSort == NHQO.SORT_BY_VISITCOUNT_ASCENDING)
          newSort = NHQO.SORT_BY_NONE;
        else
          newSort = NHQO.SORT_BY_VISITCOUNT_DESCENDING;

        break;
      case this.COLUMN_TYPE_DESCRIPTION:
        if (oldSort == NHQO.SORT_BY_ANNOTATION_ASCENDING &&
            oldSortingAnnotation == PlacesUIUtils.DESCRIPTION_ANNO) {
          newSort = NHQO.SORT_BY_ANNOTATION_DESCENDING;
          newSortingAnnotation = PlacesUIUtils.DESCRIPTION_ANNO;
        } else if (allowTriState &&
                 oldSort == NHQO.SORT_BY_ANNOTATION_DESCENDING &&
                 oldSortingAnnotation == PlacesUIUtils.DESCRIPTION_ANNO)
          newSort = NHQO.SORT_BY_NONE;
        else {
          newSort = NHQO.SORT_BY_ANNOTATION_ASCENDING;
          newSortingAnnotation = PlacesUIUtils.DESCRIPTION_ANNO;
        }

        break;
      case this.COLUMN_TYPE_DATEADDED:
        if (oldSort == NHQO.SORT_BY_DATEADDED_ASCENDING)
          newSort = NHQO.SORT_BY_DATEADDED_DESCENDING;
        else if (allowTriState &&
                 oldSort == NHQO.SORT_BY_DATEADDED_DESCENDING)
          newSort = NHQO.SORT_BY_NONE;
        else
          newSort = NHQO.SORT_BY_DATEADDED_ASCENDING;

        break;
      case this.COLUMN_TYPE_LASTMODIFIED:
        if (oldSort == NHQO.SORT_BY_LASTMODIFIED_ASCENDING)
          newSort = NHQO.SORT_BY_LASTMODIFIED_DESCENDING;
        else if (allowTriState &&
                 oldSort == NHQO.SORT_BY_LASTMODIFIED_DESCENDING)
          newSort = NHQO.SORT_BY_NONE;
        else
          newSort = NHQO.SORT_BY_LASTMODIFIED_ASCENDING;

        break;
      case this.COLUMN_TYPE_TAGS:
        if (oldSort == NHQO.SORT_BY_TAGS_ASCENDING)
          newSort = NHQO.SORT_BY_TAGS_DESCENDING;
        else if (allowTriState && oldSort == NHQO.SORT_BY_TAGS_DESCENDING)
          newSort = NHQO.SORT_BY_NONE;
        else
          newSort = NHQO.SORT_BY_TAGS_ASCENDING;

        break;
      default:
        throw Cr.NS_ERROR_INVALID_ARG;
    }
    this._result.sortingAnnotation = newSortingAnnotation;
    this._result.sortingMode = newSort;
  },

  isEditable: function PTV_isEditable(aRow, aColumn) {
    // At this point we only support editing the title field.
    if (aColumn.index != 0)
      return false;

    let node = this._rows[aRow];
    if (!node) {
      Cu.reportError("isEditable called for an unbuilt row.");
      return false;
    }
    let itemGuid = node.bookmarkGuid;

    // Only bookmark-nodes are editable.  Fortunately, this checks also takes
    // care of livemark children.
    if (itemGuid == "")
      return false;

    // The following items are also not editable, even though they are bookmark
    // items.
    // * places-roots
    // * the left pane special folders and queries (those are place: uri
    //   bookmarks)
    // * separators
    //
    // Note that concrete itemIds aren't used intentionally.  For example, we
    // have no reason to disallow renaming a shortcut to the Bookmarks Toolbar,
    // except for the one under All Bookmarks.
    if (PlacesUtils.nodeIsSeparator(node) || PlacesUtils.isRootItem(itemGuid) ||
        PlacesUtils.isQueryGeneratedFolder(itemGuid))
      return false;

    let parentId = PlacesUtils.getConcreteItemId(node.parent);
    if (parentId == PlacesUIUtils.leftPaneFolderId) {
      // Note that the for the time being this is the check that actually
      // blocks renaming places "roots", and not the isRootItem check above.
      // That's because places root are only exposed through folder shortcuts
      // descendants of the left pane folder.
      return false;
    }

    return true;
  },

  setCellText: function PTV_setCellText(aRow, aColumn, aText) {
    // We may only get here if the cell is editable.
    let node = this._rows[aRow];
    if (node.title != aText) {
      PlacesTransactions.EditTitle({ guid: node.bookmarkGuid, title: aText })
                        .transact().catch(Cu.reportError);
    }
  },

  toggleCutNode: function PTV_toggleCutNode(aNode, aValue) {
    let currentVal = this._cuttingNodes.has(aNode);
    if (currentVal != aValue) {
      if (aValue)
        this._cuttingNodes.add(aNode);
      else
        this._cuttingNodes.delete(aNode);

      this._invalidateCellValue(aNode, this.COLUMN_TYPE_TITLE);
    }
  },

  selectionChanged() { },
  cycleCell(aRow, aColumn) { },
  isSelectable(aRow, aColumn) { return false; },
  performAction(aAction) { },
  performActionOnRow(aAction, aRow) { },
  performActionOnCell(aAction, aRow, aColumn) { }
};
PK
!<<�r((@chrome/browser/content/browser/preferences/applicationManager.js// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.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-globals-from in-content/main.js */

var gAppManagerDialog = {
  _removed: [],

  init: function appManager_init() {
    this.handlerInfo = window.arguments[0];
    Services.scriptloader.loadSubScript("chrome://browser/content/preferences/in-content/main.js",
      window);
    var pane = gMainPane;
    var bundle = document.getElementById("appManagerBundle");
    var contentText;
    if (this.handlerInfo.type == TYPE_MAYBE_FEED)
      contentText = bundle.getString("handleWebFeeds");
    else {
      var description = pane._describeType(this.handlerInfo);
      var key =
        (this.handlerInfo.wrappedHandlerInfo instanceof Ci.nsIMIMEInfo) ? "handleFile"
                                                                        : "handleProtocol";
        contentText = bundle.getFormattedString(key, [description]);
    }
    contentText = bundle.getFormattedString("descriptionApplications", [contentText]);
    document.getElementById("appDescription").textContent = contentText;

    var list = document.getElementById("appList");
    var apps = this.handlerInfo.possibleApplicationHandlers.enumerate();
    while (apps.hasMoreElements()) {
      let app = apps.getNext();
      if (!pane.isValidHandlerApp(app))
        continue;

      app.QueryInterface(Ci.nsIHandlerApp);
      var item = list.appendItem(app.name);
      item.setAttribute("image", pane._getIconURLForHandlerApp(app));
      item.className = "listitem-iconic";
      item.app = app;
    }

    list.selectedIndex = 0;
  },

  onOK: function appManager_onOK() {
    if (!this._removed.length) {
      // return early to avoid calling the |store| method.
      return;
    }

    for (var i = 0; i < this._removed.length; ++i)
      this.handlerInfo.removePossibleApplicationHandler(this._removed[i]);

    this.handlerInfo.store();
  },

  onCancel: function appManager_onCancel() {
    // do nothing
  },

  remove: function appManager_remove() {
    var list = document.getElementById("appList");
    this._removed.push(list.selectedItem.app);
    var index = list.selectedIndex;
    list.removeItemAt(index);
    if (list.getRowCount() == 0) {
      // The list is now empty, make the bottom part disappear
      document.getElementById("appDetails").hidden = true;
    } else {
      // Select the item at the same index, if we removed the last
      // item of the list, select the previous item
      if (index == list.getRowCount())
        --index;
      list.selectedIndex = index;
    }
  },

  onSelect: function appManager_onSelect() {
    var list = document.getElementById("appList");
    if (!list.selectedItem) {
      document.getElementById("remove").disabled = true;
      return;
    }
    document.getElementById("remove").disabled = false;
    var app = list.selectedItem.app;
    var address = "";
    if (app instanceof Ci.nsILocalHandlerApp)
      address = app.executable.path;
    else if (app instanceof Ci.nsIWebHandlerApp)
      address = app.uriTemplate;
    else if (app instanceof Ci.nsIWebContentHandlerInfo)
      address = app.uri;
    document.getElementById("appLocation").value = address;
    var bundle = document.getElementById("appManagerBundle");
    var appType = app instanceof Ci.nsILocalHandlerApp ? "descriptionLocalApp"
                                                       : "descriptionWebApp";
    document.getElementById("appType").value = bundle.getString(appType);
  }
};
PK
!<��D�++Achrome/browser/content/browser/preferences/applicationManager.xul<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<?xml-stylesheet href="chrome://global/skin/"?>

<!DOCTYPE dialog SYSTEM "chrome://browser/locale/preferences/applicationManager.dtd">

<dialog id="appManager"
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        buttons="accept,cancel"
        onload="gAppManagerDialog.init();"
        ondialogaccept="gAppManagerDialog.onOK();"
        ondialogcancel="gAppManagerDialog.onCancel();"
        title="&appManager.title;"
        style="&appManager.style;"
        persist="screenX screenY">

  <script type="application/javascript"
          src="chrome://browser/content/utilityOverlay.js"/>
  <script type="application/javascript"
          src="chrome://global/content/preferencesBindings.js"/>
  <script type="application/javascript"
          src="chrome://browser/content/preferences/applicationManager.js"/>

  <commandset id="appManagerCommandSet">
    <command id="cmd_remove"
             oncommand="gAppManagerDialog.remove();"
             disabled="true"/>
  </commandset>

  <keyset id="appManagerKeyset">
    <key id="delete" keycode="VK_DELETE" command="cmd_remove"/>
  </keyset>

  <stringbundleset id="appManagerBundleset">
    <stringbundle id="appManagerBundle"
                  src="chrome://browser/locale/preferences/applicationManager.properties"/>
  </stringbundleset>

  <description id="appDescription"/>
  <separator class="thin"/>
  <hbox flex="1">
    <listbox id="appList" onselect="gAppManagerDialog.onSelect();" flex="1"/>
    <vbox>
      <button id="remove"
              label="&remove.label;"
              accesskey="&remove.accesskey;"
              command="cmd_remove"/>
      <spacer flex="1"/>
    </vbox>
  </hbox>
  <vbox id="appDetails">
    <separator class="thin"/>
    <label id="appType"/>
    <textbox id="appLocation" readonly="true" class="plain"/>
  </vbox>
</dialog>
PK
!<%e�SS8chrome/browser/content/browser/preferences/blocklists.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

ChromeUtils.import("resource://gre/modules/Services.jsm");
const BASE_LIST_ID = "base";
const CONTENT_LIST_ID = "content";
const TRACK_SUFFIX = "-track-digest256";
const TRACKING_TABLE_PREF = "urlclassifier.trackingTable";
const LISTS_PREF_BRANCH = "browser.safebrowsing.provider.mozilla.lists.";

var gBlocklistManager = {
  _type: "",
  _blockLists: [],
  _brandShortName: null,
  _bundle: null,
  _tree: null,

  _view: {
    _rowCount: 0,
    get rowCount() {
      return this._rowCount;
    },
    getCellText(row, column) {
      if (column.id == "listCol") {
        let list = gBlocklistManager._blockLists[row];
        let desc = list.description ? list.description : "";
        let text = gBlocklistManager._bundle.getFormattedString("mozNameTemplate",
                                                                [list.name, desc]);
        return text;
      }
      return "";
    },

    isSeparator(index) { return false; },
    isSorted() { return false; },
    isContainer(index) { return false; },
    setTree(tree) {},
    getImageSrc(row, column) {},
    getCellValue(row, column) {
      if (column.id == "selectionCol")
        return gBlocklistManager._blockLists[row].selected;
      return undefined;
    },
    cycleHeader(column) {},
    getRowProperties(row) { return ""; },
    getColumnProperties(column) { return ""; },
    getCellProperties(row, column) {
      if (column.id == "selectionCol") {
        return "checkmark";
      }

      return "";
    }
  },

  onWindowKeyPress(event) {
    if (event.keyCode == KeyEvent.DOM_VK_ESCAPE) {
      window.close();
    } else if (event.keyCode == KeyEvent.DOM_VK_RETURN) {
      gBlocklistManager.onApplyChanges();
    }
  },

  onLoad() {
    this._bundle = document.getElementById("bundlePreferences");
    let params = window.arguments[0];
    this.init(params);
  },

  init(params) {
    if (this._type) {
      // reusing an open dialog, clear the old observer
      this.uninit();
    }

    this._type = "tracking";
    this._brandShortName = params.brandShortName;

    let blocklistsText = document.getElementById("blocklistsText");
    while (blocklistsText.hasChildNodes()) {
      blocklistsText.firstChild.remove();
    }
    blocklistsText.appendChild(document.createTextNode(params.introText));

    document.title = params.windowTitle;

    this._loadBlockLists();
  },

  uninit() {},

  onListSelected() {
    for (let list of this._blockLists) {
      list.selected = false;
    }
    this._blockLists[this._tree.currentIndex].selected = true;

    this._updateTree();
  },

  onApplyChanges() {
    let activeList = this._getActiveList();
    let selected = null;
    for (let list of this._blockLists) {
      if (list.selected) {
        selected = list;
        break;
      }
    }

    if (activeList !== selected.id) {
      let trackingTable = Services.prefs.getCharPref(TRACKING_TABLE_PREF);
      if (selected.id != CONTENT_LIST_ID) {
        trackingTable = trackingTable.replace("," + CONTENT_LIST_ID + TRACK_SUFFIX, "");
      } else {
        trackingTable += "," + CONTENT_LIST_ID + TRACK_SUFFIX;
      }
      Services.prefs.setCharPref(TRACKING_TABLE_PREF, trackingTable);

      // Force an update after changing the tracking protection table.
      let listmanager = Cc["@mozilla.org/url-classifier/listmanager;1"]
                        .getService(Ci.nsIUrlListManager);
      if (listmanager) {
        listmanager.forceUpdates(trackingTable);
      }
    }

    window.close();
  },

  _loadBlockLists() {
    this._blockLists = [];

    // Load blocklists into a table.
    let branch = Services.prefs.getBranch(LISTS_PREF_BRANCH);
    let itemArray = branch.getChildList("");
    for (let itemName of itemArray) {
      try {
        this._createOrUpdateBlockList(itemName);
      } catch (e) {
        // Ignore bogus or missing list name.
        continue;
      }
    }

    this._updateTree();
  },

  _createOrUpdateBlockList(itemName) {
    let branch = Services.prefs.getBranch(LISTS_PREF_BRANCH);
    let key = branch.getCharPref(itemName);
    let value = this._bundle.getString(key);

    let suffix = itemName.slice(itemName.lastIndexOf("."));
    let id = itemName.replace(suffix, "");
    let list = this._blockLists.find(el => el.id === id);
    if (!list) {
      list = { id };
      this._blockLists.push(list);
    }
    list.selected = this._getActiveList() === id;

    // Get the property name from the suffix (e.g. ".name" -> "name").
    let prop = suffix.slice(1);
    list[prop] = value;

    return list;
  },

  _updateTree() {
    this._tree = document.getElementById("blocklistsTree");
    this._view._rowCount = this._blockLists.length;
    this._tree.view = this._view;
  },

  _getActiveList() {
    let trackingTable = Services.prefs.getCharPref(TRACKING_TABLE_PREF);
    return trackingTable.includes(CONTENT_LIST_ID) ? CONTENT_LIST_ID : BASE_LIST_ID;
  }
};

function initWithParams(params) {
  gBlocklistManager.init(params);
}
PK
!<L�g��9chrome/browser/content/browser/preferences/blocklists.xul<?xml version="1.0"?>

<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css" type="text/css"?>

<!DOCTYPE dialog SYSTEM "chrome://browser/locale/preferences/blocklists.dtd" >

<window id="BlocklistsDialog" class="windowDialog"
        windowtype="Browser:Blocklists"
        title="&window.title;"
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        style="width: &window.width;;"
        onload="gBlocklistManager.onLoad();"
        onunload="gBlocklistManager.uninit();"
        persist="screenX screenY width height"
        onkeypress="gBlocklistManager.onWindowKeyPress(event);">

  <script src="chrome://global/content/treeUtils.js"/>
  <script src="chrome://browser/content/preferences/blocklists.js"/>

  <stringbundle id="bundlePreferences"
                src="chrome://browser/locale/preferences/preferences.properties"/>

  <keyset>
    <key key="&windowClose.key;" modifiers="accel" oncommand="window.close();"/>
  </keyset>

  <vbox class="contentPane largeDialogContainer" flex="1">
    <description id="blocklistsText" control="url"/>
    <separator class="thin"/>
    <tree id="blocklistsTree" flex="1" style="height: 18em;"
          hidecolumnpicker="true"
          onselect="gBlocklistManager.onListSelected();">
      <treecols>
        <treecol id="selectionCol" label="" flex="1" sortable="false"
                 type="checkbox"/>
        <treecol id="listCol" label="&treehead.list.label;" flex="80"
                 sortable="false"/>
      </treecols>
      <treechildren/>
    </tree>
  </vbox>
  <vbox>
    <spacer flex="1"/>
    <hbox class="actionButtons" align="right" flex="1">
      <button oncommand="close();" icon="close"
              label="&button.cancel.label;" accesskey="&button.cancel.accesskey;" />
      <button id="btnApplyChanges" oncommand="gBlocklistManager.onApplyChanges();" icon="save"
              label="&button.ok.label;" accesskey="&button.ok.accesskey;"/>
    </hbox>
  </vbox>
</window>
PK
!<Qێ���<chrome/browser/content/browser/preferences/clearSiteData.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

.options-container {
  background-color: var(--in-content-box-background);
  border: 1px solid var(--in-content-box-border-color);
  border-radius: 2px;
  color: var(--in-content-text-color);
  padding: 0.5em;
}

.option {
  padding-bottom: 16px;
}

.option-description {
  color: #737373;
}
PK
!<i/S�||;chrome/browser/content/browser/preferences/clearSiteData.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
ChromeUtils.import("resource://gre/modules/DownloadUtils.jsm");
ChromeUtils.import("resource:///modules/SiteDataManager.jsm");

var gClearSiteDataDialog = {
  _clearSiteDataCheckbox: null,
  _clearCacheCheckbox: null,
  _clearButton: null,

  init() {
    this._bundle = Services.strings
      .createBundle("chrome://browser/locale/preferences/clearSiteData.properties");

    SiteDataManager.getTotalUsage().then(bytes => {
      // Size is an array of amount and unit, e.g. [20, "MB"].
      let size = DownloadUtils.convertByteUnits(bytes);
      document.getElementById("clearSiteDataLabel").value =
        this._bundle.formatStringFromName("clearSiteDataWithEstimates.label", size, 2);
    });
    SiteDataManager.getCacheSize().then(bytes => {
      // Size is an array of amount and unit, e.g. [20, "MB"].
      let size = DownloadUtils.convertByteUnits(bytes);
      document.getElementById("clearCacheLabel").value =
        this._bundle.formatStringFromName("clearCacheWithEstimates.label", size, 2);
    });

    this._clearButton = document.getElementById("clearButton");
    this._cancelButton = document.getElementById("cancelButton");
    this._clearSiteDataCheckbox = document.getElementById("clearSiteData");
    this._clearCacheCheckbox = document.getElementById("clearCache");

    window.addEventListener("keypress", this.onWindowKeyPress);

    this._cancelButton.addEventListener("command", window.close);
    this._clearButton.addEventListener("command", () => this.onClear());

    this._clearSiteDataCheckbox.addEventListener("command", e => this.onCheckboxCommand(e));
    this._clearCacheCheckbox.addEventListener("command", e => this.onCheckboxCommand(e));
  },

  onWindowKeyPress(event) {
    if (event.keyCode == KeyEvent.DOM_VK_ESCAPE)
      window.close();
  },

  onCheckboxCommand(event) {
    this._clearButton.disabled =
      !(this._clearSiteDataCheckbox.checked || this._clearCacheCheckbox.checked);
  },

  onClear() {
    let allowed = true;

    if (this._clearCacheCheckbox.checked && allowed) {
      SiteDataManager.removeCache();
      // If we're not clearing site data, we need to tell the
      // SiteDataManager to signal that it's updating.
      if (!this._clearSiteDataCheckbox.checked) {
        SiteDataManager.updateSites();
      }
    }

    if (this._clearSiteDataCheckbox.checked) {
      allowed = SiteDataManager.promptSiteDataRemoval(window);
      if (allowed) {
        SiteDataManager.removeSiteData();
      }
    }

    if (allowed) {
      window.close();
    }
  },
};

window.addEventListener("load", () => gClearSiteDataDialog.init());
PK
!<wފ

<chrome/browser/content/browser/preferences/clearSiteData.xul<?xml version="1.0"?>

<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/preferences/clearSiteData.css" type="text/css"?>

<!DOCTYPE dialog [
 <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
 <!ENTITY % clearSiteDataDTD SYSTEM "chrome://browser/locale/preferences/clearSiteData.dtd">
 %brandDTD;
 %clearSiteDataDTD;
]>

<window id="ClearSiteDataDialog" class="windowDialog"
        windowtype="Browser:ClearSiteData"
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        style="width: &window.width;;"
        title="&window.title;"
        persist="screenX screenY width height">

  <script src="chrome://browser/content/preferences/clearSiteData.js"/>

  <stringbundle id="bundlePreferences"
                src="chrome://browser/locale/preferences/preferences.properties"/>

  <keyset>
    <key key="&windowClose.key;" modifiers="accel" oncommand="window.close();"/>
  </keyset>

  <vbox class="contentPane largeDialogContainer" flex="1">
    <description control="url">&window.description;</description>
    <separator class="thin"/>
    <vbox class="options-container">
      <hbox class="option">
        <checkbox id="clearSiteData" checked="true"
                  accesskey="&clearSiteData.accesskey;" />
        <vbox>
          <label for="clearSiteData" id="clearSiteDataLabel" value="&clearSiteData.label;" />
          <description class="option-description">&clearSiteData.description;</description>
        </vbox>
      </hbox>
      <hbox class="option">
        <checkbox id="clearCache" checked="true"
                  accesskey="&clearCache.accesskey;" />
        <vbox>
          <label for="clearCache" id="clearCacheLabel" value="&clearCache.label;" />
          <description class="option-description">&clearCache.description;</description>
        </vbox>
      </hbox>
    </vbox>
  </vbox>
  <vbox>
    <hbox class="actionButtons" align="right" flex="1">
      <button id="cancelButton" icon="close"
              label="&button.cancel.label;" accesskey="&button.cancel.accesskey;" />
      <button id="clearButton" icon="save"
              label="&button.clear.label;" accesskey="&button.clear.accesskey;"/>
    </hbox>
  </vbox>
</window>
PK
!<��c���4chrome/browser/content/browser/preferences/colors.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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-globals-from ../../../toolkit/content/preferencesBindings.js */

Preferences.addAll([
  { id: "browser.display.document_color_use", type: "int" },
  { id: "browser.anchor_color", type: "string" },
  { id: "browser.visited_color", type: "string" },
  { id: "browser.underline_anchors", type: "bool" },
  { id: "browser.display.foreground_color", type: "string" },
  { id: "browser.display.background_color", type: "string" },
  { id: "browser.display.use_system_colors", type: "bool" },
]);
PK
!<��3���5chrome/browser/content/browser/preferences/colors.xul<?xml version="1.0"?>


<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css"?>

<!DOCTYPE dialog [
  <!ENTITY % preferencesDTD SYSTEM "chrome://global/locale/preferences.dtd">
  %preferencesDTD;
  <!ENTITY % colorsDTD SYSTEM "chrome://browser/locale/preferences/colors.dtd">
  %colorsDTD;
]>

<dialog id="ColorsDialog" type="child" class="prefwindow"
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        title="&colorsDialog.title;"
        buttons="accept,cancel,help"
        persist="lastSelected screenX screenY"
        closebuttonlabel="&preferencesCloseButton.label;"
        closebuttonaccesskey="&preferencesCloseButton.accesskey;"
        role="dialog"
        helpTopic="prefs-fonts-and-colors"
        ondialoghelp="openPrefsHelp()"
        style="width: &window.width; !important;">

  <script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>
  <script type="application/javascript" src="chrome://global/content/preferencesBindings.js"/>

  <keyset>
    <key key="&windowClose.key;" modifiers="accel" oncommand="Preferences.close(event)"/>
  </keyset>

  <vbox id="ColorsDialogPane" class="prefpane">

    <script type="application/javascript" src="chrome://browser/content/preferences/colors.js"/>

    <hbox>
      <groupbox flex="1">
        <caption><label>&color;</label></caption>
        <hbox align="center">
          <label accesskey="&textColor2.accesskey;" control="foregroundtextmenu">&textColor2.label;</label>
          <spacer flex="1"/>
          <colorpicker type="button" id="foregroundtextmenu" palettename="standard"
                       preference="browser.display.foreground_color"/>
        </hbox>
        <hbox align="center" style="margin-top: 5px">
          <label accesskey="&backgroundColor2.accesskey;" control="backgroundmenu">&backgroundColor2.label;</label>
          <spacer flex="1"/>
          <colorpicker type="button" id="backgroundmenu" palettename="standard"
                       preference="browser.display.background_color"/>
        </hbox>
        <separator class="thin"/>
        <hbox align="center">
          <checkbox id="browserUseSystemColors" label="&useSystemColors.label;" accesskey="&useSystemColors.accesskey;"
                    preference="browser.display.use_system_colors"/>
        </hbox>
      </groupbox>

      <groupbox flex="1">
        <caption><label>&links;</label></caption>
        <hbox align="center">
          <label accesskey="&linkColor2.accesskey;" control="unvisitedlinkmenu">&linkColor2.label;</label>
          <spacer flex="1"/>
          <colorpicker type="button" id="unvisitedlinkmenu" palettename="standard"
                       preference="browser.anchor_color"/>
        </hbox>
        <hbox align="center" style="margin-top: 5px">
          <label accesskey="&visitedLinkColor2.accesskey;" control="visitedlinkmenu">&visitedLinkColor2.label;</label>
          <spacer flex="1"/>
          <colorpicker type="button" id="visitedlinkmenu" palettename="standard"
                       preference="browser.visited_color"/>
        </hbox>
        <separator class="thin"/>
        <hbox align="center">
          <checkbox id="browserUnderlineAnchors" label="&underlineLinks.label;" accesskey="&underlineLinks.accesskey;"
                    preference="browser.underline_anchors"/>
        </hbox>
      </groupbox>
    </hbox>
    <vbox>
      <label accesskey="&overrideDefaultPageColors2.accesskey;"
             control="useDocumentColors">&overrideDefaultPageColors2.label;</label>
      <hbox>
        <menulist id="useDocumentColors" preference="browser.display.document_color_use" flex="1">
          <menupopup>
            <menuitem label="&overrideDefaultPageColors.always.label;"
                      value="2" id="documentColorAlways"/>
            <menuitem label="&overrideDefaultPageColors.auto.label;"
                      value="0" id="documentColorAutomatic"/>
            <menuitem label="&overrideDefaultPageColors.never.label;"
                      value="1" id="documentColorNever"/>
          </menupopup>
        </menulist>
      </hbox>
    </vbox>
  </vbox>
</dialog>
PK
!<]W}�{-{-8chrome/browser/content/browser/preferences/connection.js/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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-globals-from ../../base/content/utilityOverlay.js */
/* import-globals-from ../../../toolkit/content/preferencesBindings.js */
/* import-globals-from in-content/extensionControlled.js */

Preferences.addAll([
  // Add network.proxy.autoconfig_url before network.proxy.type so they're
  // both initialized when network.proxy.type initialization triggers a call to
  // gConnectionsDialog.updateReloadButton().
  { id: "network.proxy.autoconfig_url", type: "string" },
  { id: "network.proxy.type", type: "int" },
  { id: "network.proxy.http", type: "string" },
  { id: "network.proxy.http_port", type: "int" },
  { id: "network.proxy.ftp", type: "string" },
  { id: "network.proxy.ftp_port", type: "int" },
  { id: "network.proxy.ssl", type: "string" },
  { id: "network.proxy.ssl_port", type: "int" },
  { id: "network.proxy.socks", type: "string" },
  { id: "network.proxy.socks_port", type: "int" },
  { id: "network.proxy.socks_version", type: "int" },
  { id: "network.proxy.socks_remote_dns", type: "bool" },
  { id: "network.proxy.no_proxies_on", type: "string" },
  { id: "network.proxy.share_proxy_settings", type: "bool" },
  { id: "signon.autologin.proxy", type: "bool" },
  { id: "pref.advanced.proxies.disable_button.reload", type: "bool" },
  { id: "network.proxy.backup.ftp", type: "string" },
  { id: "network.proxy.backup.ftp_port", type: "int" },
  { id: "network.proxy.backup.ssl", type: "string" },
  { id: "network.proxy.backup.ssl_port", type: "int" },
  { id: "network.proxy.backup.socks", type: "string" },
  { id: "network.proxy.backup.socks_port", type: "int" },
]);

window.addEventListener("DOMContentLoaded", () => {
  Preferences.get("network.proxy.type").on("change",
    gConnectionsDialog.proxyTypeChanged.bind(gConnectionsDialog));
  Preferences.get("network.proxy.socks_version").on("change",
    gConnectionsDialog.updateDNSPref.bind(gConnectionsDialog));

  document
    .getElementById("disableProxyExtension")
    .addEventListener(
      "command", makeDisableControllingExtension(
        PREF_SETTING_TYPE, PROXY_KEY).bind(gConnectionsDialog));
  gConnectionsDialog.updateProxySettingsUI();
  initializeProxyUI(gConnectionsDialog);
}, { once: true, capture: true });

var gConnectionsDialog = {
  beforeAccept() {
    var proxyTypePref = Preferences.get("network.proxy.type");
    if (proxyTypePref.value == 2) {
      this.doAutoconfigURLFixup();
      return true;
    }

    if (proxyTypePref.value != 1)
      return true;

    var httpProxyURLPref = Preferences.get("network.proxy.http");
    var httpProxyPortPref = Preferences.get("network.proxy.http_port");
    var shareProxiesPref = Preferences.get("network.proxy.share_proxy_settings");

    // If the port is 0 and the proxy server is specified, focus on the port and cancel submission.
    for (let prefName of ["http", "ssl", "ftp", "socks"]) {
      let proxyPortPref = Preferences.get("network.proxy." + prefName + "_port");
      let proxyPref = Preferences.get("network.proxy." + prefName);
      // Only worry about ports which are currently active. If the share option is on, then ignore
      // all ports except the HTTP port
      if (proxyPref.value != "" && proxyPortPref.value == 0 &&
            (prefName == "http" || !shareProxiesPref.value)) {
        document.getElementById("networkProxy" + prefName.toUpperCase() + "_Port").focus();
        return false;
      }
    }

    // In the case of a shared proxy preference, backup the current values and update with the HTTP value
    if (shareProxiesPref.value) {
      var proxyPrefs = ["ssl", "ftp", "socks"];
      for (var i = 0; i < proxyPrefs.length; ++i) {
        var proxyServerURLPref = Preferences.get("network.proxy." + proxyPrefs[i]);
        var proxyPortPref = Preferences.get("network.proxy." + proxyPrefs[i] + "_port");
        var backupServerURLPref = Preferences.get("network.proxy.backup." + proxyPrefs[i]);
        var backupPortPref = Preferences.get("network.proxy.backup." + proxyPrefs[i] + "_port");
        backupServerURLPref.value = backupServerURLPref.value || proxyServerURLPref.value;
        backupPortPref.value = backupPortPref.value || proxyPortPref.value;
        proxyServerURLPref.value = httpProxyURLPref.value;
        proxyPortPref.value = httpProxyPortPref.value;
      }
    }

    this.sanitizeNoProxiesPref();

    return true;
  },

  checkForSystemProxy() {
    if ("@mozilla.org/system-proxy-settings;1" in Cc)
      document.getElementById("systemPref").removeAttribute("hidden");
  },

  proxyTypeChanged() {
    var proxyTypePref = Preferences.get("network.proxy.type");

    // Update http
    var httpProxyURLPref = Preferences.get("network.proxy.http");
    httpProxyURLPref.disabled = proxyTypePref.value != 1;
    var httpProxyPortPref = Preferences.get("network.proxy.http_port");
    httpProxyPortPref.disabled = proxyTypePref.value != 1;

    // Now update the other protocols
    this.updateProtocolPrefs();

    var shareProxiesPref = Preferences.get("network.proxy.share_proxy_settings");
    shareProxiesPref.disabled = proxyTypePref.value != 1;
    var autologinProxyPref = Preferences.get("signon.autologin.proxy");
    autologinProxyPref.disabled = proxyTypePref.value == 0;
    var noProxiesPref = Preferences.get("network.proxy.no_proxies_on");
    noProxiesPref.disabled = proxyTypePref.value != 1;

    var autoconfigURLPref = Preferences.get("network.proxy.autoconfig_url");
    autoconfigURLPref.disabled = proxyTypePref.value != 2;

    this.updateReloadButton();
  },

  updateDNSPref() {
    var socksVersionPref = Preferences.get("network.proxy.socks_version");
    var socksDNSPref = Preferences.get("network.proxy.socks_remote_dns");
    var proxyTypePref = Preferences.get("network.proxy.type");
    var isDefinitelySocks4 = !socksVersionPref.disabled && socksVersionPref.value == 4;
    socksDNSPref.disabled = (isDefinitelySocks4 || proxyTypePref.value == 0);
    return undefined;
  },

  updateReloadButton() {
    // Disable the "Reload PAC" button if the selected proxy type is not PAC or
    // if the current value of the PAC textbox does not match the value stored
    // in prefs.  Likewise, disable the reload button if PAC is not configured
    // in prefs.

    var typedURL = document.getElementById("networkProxyAutoconfigURL").value;
    var proxyTypeCur = Preferences.get("network.proxy.type").value;

    var pacURL = Services.prefs.getCharPref("network.proxy.autoconfig_url");
    var proxyType = Services.prefs.getIntPref("network.proxy.type");

    var disableReloadPref =
        Preferences.get("pref.advanced.proxies.disable_button.reload");
    disableReloadPref.disabled =
        (proxyTypeCur != 2 || proxyType != 2 || typedURL != pacURL);
  },

  readProxyType() {
    this.proxyTypeChanged();
    return undefined;
  },

  updateProtocolPrefs() {
    var proxyTypePref = Preferences.get("network.proxy.type");
    var shareProxiesPref = Preferences.get("network.proxy.share_proxy_settings");
    var proxyPrefs = ["ssl", "ftp", "socks"];
    for (var i = 0; i < proxyPrefs.length; ++i) {
      var proxyServerURLPref = Preferences.get("network.proxy." + proxyPrefs[i]);
      var proxyPortPref = Preferences.get("network.proxy." + proxyPrefs[i] + "_port");

      // Restore previous per-proxy custom settings, if present.
      if (!shareProxiesPref.value) {
        var backupServerURLPref = Preferences.get("network.proxy.backup." + proxyPrefs[i]);
        var backupPortPref = Preferences.get("network.proxy.backup." + proxyPrefs[i] + "_port");
        if (backupServerURLPref.hasUserValue) {
          proxyServerURLPref.value = backupServerURLPref.value;
          backupServerURLPref.reset();
        }
        if (backupPortPref.hasUserValue) {
          proxyPortPref.value = backupPortPref.value;
          backupPortPref.reset();
        }
      }

      proxyServerURLPref.updateElements();
      proxyPortPref.updateElements();
      proxyServerURLPref.disabled = proxyTypePref.value != 1 || shareProxiesPref.value;
      proxyPortPref.disabled = proxyServerURLPref.disabled;
    }
    var socksVersionPref = Preferences.get("network.proxy.socks_version");
    socksVersionPref.disabled = proxyTypePref.value != 1 || shareProxiesPref.value;
    this.updateDNSPref();
    return undefined;
  },

  readProxyProtocolPref(aProtocol, aIsPort) {
    var shareProxiesPref = Preferences.get("network.proxy.share_proxy_settings");
    if (shareProxiesPref.value) {
      var pref = Preferences.get("network.proxy.http" + (aIsPort ? "_port" : ""));
      return pref.value;
    }

    var backupPref = Preferences.get("network.proxy.backup." + aProtocol + (aIsPort ? "_port" : ""));
    return backupPref.hasUserValue ? backupPref.value : undefined;
  },

  reloadPAC() {
    Cc["@mozilla.org/network/protocol-proxy-service;1"].
        getService().reloadPAC();
  },

  doAutoconfigURLFixup() {
    var autoURL = document.getElementById("networkProxyAutoconfigURL");
    var autoURLPref = Preferences.get("network.proxy.autoconfig_url");
    try {
      autoURLPref.value = autoURL.value =
        Services.uriFixup.createFixupURI(autoURL.value, 0).spec;
    } catch (ex) {}
  },

  sanitizeNoProxiesPref() {
    var noProxiesPref = Preferences.get("network.proxy.no_proxies_on");
    // replace substrings of ; and \n with commas if they're neither immediately
    // preceded nor followed by a valid separator character
    noProxiesPref.value = noProxiesPref.value.replace(/([^, \n;])[;\n]+(?![,\n;])/g, "$1,");
    // replace any remaining ; and \n since some may follow commas, etc.
    noProxiesPref.value = noProxiesPref.value.replace(/[;\n]/g, "");
  },

  readHTTPProxyServer() {
    var shareProxiesPref = Preferences.get("network.proxy.share_proxy_settings");
    if (shareProxiesPref.value)
      this.updateProtocolPrefs();
    return undefined;
  },

  readHTTPProxyPort() {
    var shareProxiesPref = Preferences.get("network.proxy.share_proxy_settings");
    if (shareProxiesPref.value)
      this.updateProtocolPrefs();
    return undefined;
  },

  getProxyControls() {
    let controlGroup = document.getElementById("networkProxyType");
    return [
      ...controlGroup.querySelectorAll(":scope > radio"),
      ...controlGroup.querySelectorAll("label"),
      ...controlGroup.querySelectorAll("textbox"),
      ...controlGroup.querySelectorAll("checkbox"),
      ...document.querySelectorAll("#networkProxySOCKSVersion > radio"),
      ...document.querySelectorAll("#ConnectionsDialogPane > checkbox"),
    ];
  },

  // Update the UI to show/hide the extension controlled message for
  // proxy settings.
  async updateProxySettingsUI() {
    let isLocked = API_PROXY_PREFS.some(
      pref => Services.prefs.prefIsLocked(pref));

    function setInputsDisabledState(isControlled) {
      let disabled = isLocked || isControlled;
      for (let element of gConnectionsDialog.getProxyControls()) {
        element.disabled = disabled;
      }
      if (!isLocked) {
        gConnectionsDialog.proxyTypeChanged();
      }
    }

    if (isLocked) {
      // An extension can't control this setting if any pref is locked.
      hideControllingExtension(PROXY_KEY);
      setInputsDisabledState(false);
    } else {
      handleControllingExtension(PREF_SETTING_TYPE, PROXY_KEY, "extensionControlled.proxyConfig")
        .then(setInputsDisabledState);
    }
  }
};
PK
!<�3��$#$#9chrome/browser/content/browser/preferences/connection.xul<?xml version="1.0"?>

<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 dialog [
  <!ENTITY % preferencesDTD SYSTEM "chrome://global/locale/preferences.dtd">
  %preferencesDTD;
  <!ENTITY % connectionDTD SYSTEM "chrome://browser/locale/preferences/connection.dtd">
  %connectionDTD;
  <!ENTITY % mainDTD SYSTEM "chrome://browser/locale/preferences/main.dtd">
  %mainDTD;
]>

<?xml-stylesheet href="chrome://global/skin/"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css"?>

<dialog id="ConnectionsDialog" type="child" class="prefwindow"
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        title="&connectionsDialog.title;"
        buttons="accept,cancel,help"
        persist="lastSelected screenX screenY"
        closebuttonlabel="&preferencesCloseButton.label;"
        closebuttonaccesskey="&preferencesCloseButton.accesskey;"
        role="dialog"
        onbeforeaccept="return gConnectionsDialog.beforeAccept();"
        onload="gConnectionsDialog.checkForSystemProxy();"
        helpTopic="prefs-connection-settings"
        ondialoghelp="openPrefsHelp()"

        style="width: &window.width2; !important;">

  <script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>
  <script type="application/javascript" src="chrome://global/content/preferencesBindings.js"/>
  <script type="application/javascript" src="chrome://browser/content/preferences/in-content/extensionControlled.js"/>

  <keyset>
    <key key="&windowClose.key;" modifiers="accel" oncommand="Preferences.close(event)"/>
  </keyset>

  <vbox id="ConnectionsDialogPane" class="prefpane largeDialogContainer">

    <stringbundle id="bundlePreferences" src="chrome://browser/locale/preferences/preferences.properties"/>
    <stringbundle id="bundleBrand" src="chrome://branding/locale/brand.properties"/>
    <script type="application/javascript" src="chrome://browser/content/preferences/connection.js"/>

    <hbox id="proxyExtensionContent" align="top" hidden="true">
      <description control="disableProxyExtension" flex="1" />
      <button id="disableProxyExtension"
              class="extension-controlled-button accessory-button"
              label="&disableExtension.label;" />
    </hbox>

    <groupbox>
      <caption><label>&proxyTitle.label2;</label></caption>

      <radiogroup id="networkProxyType" preference="network.proxy.type"
                  onsyncfrompreference="return gConnectionsDialog.readProxyType();">
        <radio value="0" label="&noProxyTypeRadio.label;" accesskey="&noProxyTypeRadio.accesskey;"/>
        <radio value="4" label="&WPADTypeRadio.label;" accesskey="&WPADTypeRadio.accesskey;"/>
        <radio value="5" label="&systemTypeRadio.label;" accesskey="&systemTypeRadio.accesskey;" id="systemPref" hidden="true"/>
        <radio value="1" label="&manualTypeRadio2.label;" accesskey="&manualTypeRadio2.accesskey;"/>
        <grid class="indent" flex="1">
          <columns>
            <column/>
            <column flex="1"/>
          </columns>
          <rows>
            <row align="center">
              <hbox pack="end">
                <label accesskey="&http2.accesskey;" control="networkProxyHTTP">&http2.label;</label>
              </hbox>
              <hbox align="center">
                <textbox id="networkProxyHTTP" flex="1"
                         preference="network.proxy.http" onsyncfrompreference="return gConnectionsDialog.readHTTPProxyServer();"/>
                <label accesskey="&HTTPport.accesskey;" control="networkProxyHTTP_Port">&port2.label;</label>
                <textbox id="networkProxyHTTP_Port" class="proxy-port-input" type="number" max="65535" hidespinbuttons="true"
                         preference="network.proxy.http_port" onsyncfrompreference="return gConnectionsDialog.readHTTPProxyPort();"/>
              </hbox>
            </row>
            <row>
              <hbox/>
              <hbox>
                <checkbox id="shareAllProxies" label="&shareproxy.label;" accesskey="&shareproxy.accesskey;"
                          preference="network.proxy.share_proxy_settings"
                          onsyncfrompreference="return gConnectionsDialog.updateProtocolPrefs();"/>
              </hbox>
            </row>
            <row align="center">
              <hbox pack="end">
                <label accesskey="&ssl2.accesskey;" control="networkProxySSL">&ssl2.label;</label>
              </hbox>
              <hbox align="center">
                <textbox id="networkProxySSL" flex="1" preference="network.proxy.ssl"
                         onsyncfrompreference="return gConnectionsDialog.readProxyProtocolPref('ssl', false);"/>
                <label accesskey="&SSLport.accesskey;" control="networkProxySSL_Port">&port2.label;</label>
                <textbox id="networkProxySSL_Port" class="proxy-port-input" type="number" max="65535" size="5" preference="network.proxy.ssl_port"
                         hidespinbuttons="true" onsyncfrompreference="return gConnectionsDialog.readProxyProtocolPref('ssl', true);"/>
              </hbox>
            </row>
            <row align="center">
              <hbox pack="end">
                <label accesskey="&ftp2.accesskey;" control="networkProxyFTP">&ftp2.label;</label>
              </hbox>
              <hbox align="center">
                <textbox id="networkProxyFTP" flex="1" preference="network.proxy.ftp"
                         onsyncfrompreference="return gConnectionsDialog.readProxyProtocolPref('ftp', false);"/>
                <label accesskey="&FTPport.accesskey;" control="networkProxyFTP_Port">&port2.label;</label>
                <textbox id="networkProxyFTP_Port" class="proxy-port-input" type="number" max="65535" size="5" preference="network.proxy.ftp_port"
                         hidespinbuttons="true" onsyncfrompreference="return gConnectionsDialog.readProxyProtocolPref('ftp', true);"/>
              </hbox>
            </row>
            <row align="center">
              <hbox pack="end">
                <label accesskey="&socks2.accesskey;" control="networkProxySOCKS">&socks2.label;</label>
              </hbox>
              <hbox align="center">
                <textbox id="networkProxySOCKS" flex="1" preference="network.proxy.socks"
                         onsyncfrompreference="return gConnectionsDialog.readProxyProtocolPref('socks', false);"/>
                <label accesskey="&SOCKSport.accesskey;" control="networkProxySOCKS_Port">&port2.label;</label>
                <textbox id="networkProxySOCKS_Port" class="proxy-port-input" type="number" max="65535" size="5" preference="network.proxy.socks_port"
                         hidespinbuttons="true" onsyncfrompreference="return gConnectionsDialog.readProxyProtocolPref('socks', true);"/>
              </hbox>
            </row>
            <row>
              <spacer/>
              <box pack="start">
              <radiogroup id="networkProxySOCKSVersion" orient="horizontal"
                          preference="network.proxy.socks_version">
                <radio id="networkProxySOCKSVersion4" value="4" label="&socks4.label;" accesskey="&socks4.accesskey;" />
                <radio id="networkProxySOCKSVersion5" value="5" label="&socks5.label;" accesskey="&socks5.accesskey;" />
              </radiogroup>
              </box>
            </row>
            <label accesskey="&noproxy2.accesskey;" control="networkProxyNone">&noproxy2.label;</label>
            <textbox id="networkProxyNone" preference="network.proxy.no_proxies_on" multiline="true" rows="2"/>
            <label control="networkProxyNone">&noproxyExplain.label;</label>
          </rows>
        </grid>
        <radio value="2" label="&autoTypeRadio2.label;" accesskey="&autoTypeRadio2.accesskey;"/>
        <hbox class="indent" flex="1" align="center">
          <textbox id="networkProxyAutoconfigURL" flex="1" preference="network.proxy.autoconfig_url"
                   oninput="gConnectionsDialog.updateReloadButton();"/>
          <button id="autoReload" icon="refresh"
                  label="&reload.label;" accesskey="&reload.accesskey;"
                  oncommand="gConnectionsDialog.reloadPAC();"
                  preference="pref.advanced.proxies.disable_button.reload"/>
        </hbox>
      </radiogroup>
    </groupbox>
    <separator class="thin"/>
    <checkbox id="autologinProxy"
              label="&autologinproxy.label;"
              accesskey="&autologinproxy.accesskey;"
              preference="signon.autologin.proxy"
              tooltiptext="&autologinproxy.tooltip;"/>
    <checkbox id="networkProxySOCKSRemoteDNS"  preference="network.proxy.socks_remote_dns" label="&socksRemoteDNS.label2;" accesskey="&socksRemoteDNS.accesskey;" />
    <separator/>
  </vbox>
</dialog>
PK
!<���^^8chrome/browser/content/browser/preferences/containers.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/ContextualIdentityService.jsm");

const containersBundle = Services.strings.createBundle("chrome://browser/locale/preferences/containers.properties");

const HTMLNS = "http://www.w3.org/1999/xhtml";

let gContainersManager = {
  icons: [
    "fingerprint",
    "briefcase",
    "dollar",
    "cart",
    "circle",
    "gift",
    "vacation",
    "food",
    "fruit",
    "pet",
    "tree",
    "chill"
  ],

  colors: [
    "blue",
    "turquoise",
    "green",
    "yellow",
    "orange",
    "red",
    "pink",
    "purple"
  ],

  onLoad() {
    let params = window.arguments[0] || {};
    this.init(params);
  },

  init(aParams) {
    this.userContextId = aParams.userContextId || null;
    this.identity = aParams.identity;

    if (aParams.windowTitle) {
      document.title = aParams.windowTitle;
    }

    const iconWrapper = document.getElementById("iconWrapper");
    iconWrapper.appendChild(this.createIconButtons());

    const colorWrapper = document.getElementById("colorWrapper");
    colorWrapper.appendChild(this.createColorSwatches());

    if (this.identity.name) {
      const name = document.getElementById("name");
      name.value = this.identity.name;
      this.checkForm();
    }

    this.setLabelsMinWidth();

    // This is to prevent layout jank caused by the svgs and outlines rendering at different times
    document.getElementById("containers-content").removeAttribute("hidden");
  },

  setLabelsMinWidth() {
    const labelMinWidth = containersBundle.GetStringFromName("containers.labelMinWidth");
    const labels = [
      document.getElementById("nameLabel"),
      document.getElementById("iconLabel"),
      document.getElementById("colorLabel")
    ];
    for (let label of labels) {
      label.style.minWidth = labelMinWidth;
    }
  },

  uninit() {
  },

  // Check if name string as to if the form can be submitted
  checkForm() {
    const name = document.getElementById("name");
    let btnApplyChanges = document.getElementById("btnApplyChanges");
    if (!name.value) {
      btnApplyChanges.setAttribute("disabled", true);
    } else {
      btnApplyChanges.removeAttribute("disabled");
    }
  },

  createIconButtons(defaultIcon) {
    let radiogroup = document.createElement("radiogroup");
    radiogroup.setAttribute("id", "icon");
    radiogroup.className = "icon-buttons radio-buttons";

    for (let icon of this.icons) {
      let iconSwatch = document.createElement("radio");
      iconSwatch.id = "iconbutton-" + icon;
      iconSwatch.name = "icon";
      iconSwatch.type = "radio";
      iconSwatch.value = icon;

      if (this.identity.icon && this.identity.icon == icon) {
        iconSwatch.setAttribute("selected", true);
      }

      iconSwatch.setAttribute("label",
        containersBundle.GetStringFromName(`containers.${icon}.label`));
      let iconElement = document.createElement("hbox");
      iconElement.className = "userContext-icon";
      iconElement.setAttribute("data-identity-icon", icon);

      iconSwatch.appendChild(iconElement);
      radiogroup.appendChild(iconSwatch);
    }

    return radiogroup;
  },

  createColorSwatches(defaultColor) {
    let radiogroup = document.createElement("radiogroup");
    radiogroup.setAttribute("id", "color");
    radiogroup.className = "radio-buttons";

    for (let color of this.colors) {
      let colorSwatch = document.createElement("radio");
      colorSwatch.id = "colorswatch-" + color;
      colorSwatch.name = "color";
      colorSwatch.type = "radio";
      colorSwatch.value = color;

      if (this.identity.color && this.identity.color == color) {
        colorSwatch.setAttribute("selected", true);
      }

      colorSwatch.setAttribute("label",
        containersBundle.GetStringFromName(`containers.${color}.label`));
      let iconElement = document.createElement("hbox");
      iconElement.className = "userContext-icon";
      iconElement.setAttribute("data-identity-icon", "circle");
      iconElement.setAttribute("data-identity-color", color);

      colorSwatch.appendChild(iconElement);
      radiogroup.appendChild(colorSwatch);
    }
    return radiogroup;
  },

  onApplyChanges() {
    let icon = document.getElementById("icon").value;
    let color = document.getElementById("color").value;
    let name = document.getElementById("name").value;

    if (!this.icons.includes(icon)) {
      throw "Internal error. The icon value doesn't match.";
    }

    if (!this.colors.includes(color)) {
      throw "Internal error. The color value doesn't match.";
    }

    if (this.userContextId) {
      ContextualIdentityService.update(this.userContextId,
        name,
        icon,
        color);
    } else {
      ContextualIdentityService.create(name,
        icon,
        color);
    }
    window.parent.location.reload();
  },

  onWindowKeyPress(aEvent) {
    if (aEvent.keyCode == KeyEvent.DOM_VK_ESCAPE)
      window.close();
  }
};
PK
!<�߀��9chrome/browser/content/browser/preferences/containers.xul<?xml version="1.0"?>

<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/containers.css" type="text/css"?>

<!DOCTYPE dialog SYSTEM "chrome://browser/locale/preferences/containers.dtd" >

<window id="ContainersDialog" class="windowDialog"
        windowtype="Browser:Permissions"
        title="&window.title;"
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        style="width: &window.width;;"
        onload="gContainersManager.onLoad();"
        onunload="gContainersManager.uninit();"
        persist="screenX screenY width height"
        onkeypress="gContainersManager.onWindowKeyPress(event);">

  <script src="chrome://global/content/treeUtils.js"/>
  <script src="chrome://browser/content/preferences/containers.js"/>

  <stringbundle id="bundlePreferences"
                src="chrome://browser/locale/preferences/preferences.properties"/>

  <keyset>
    <key key="&windowClose.key;" modifiers="accel" oncommand="window.close();"/>
  </keyset>

  <vbox class="contentPane largeDialogContainer" flex="1" hidden="true" id="containers-content">
    <hbox align="start">
      <label id="nameLabel" control="name" accesskey="&name2.accesskey;">&name2.label;</label>
      <textbox id="name" placeholder="&name.placeholder;" flex="1" oninput="gContainersManager.checkForm();" />
    </hbox>
    <hbox align="center" id="iconWrapper">
      <label id="iconLabel" control="icon" accesskey="&icon2.accesskey;">&icon2.label;</label>
    </hbox>
    <hbox align="center" id="colorWrapper">
      <label id="colorLabel" control="color" accesskey="&color2.accesskey;">&color2.label;</label>
    </hbox>
  </vbox>
  <vbox>
    <hbox class="actionButtons" align="right" flex="1">
      <button id="btnApplyChanges" disabled="true" oncommand="gContainersManager.onApplyChanges();" icon="save"
              label="&button.ok.label;" accesskey="&button.ok.accesskey;"/>
    </hbox>
  </vbox>
</window>
PK
!<mu��5chrome/browser/content/browser/preferences/cookies.js/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 nsICookie = Ci.nsICookie;

ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
ChromeUtils.import("resource://gre/modules/PluralForm.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");

ChromeUtils.defineModuleGetter(this, "ContextualIdentityService",
                               "resource://gre/modules/ContextualIdentityService.jsm");

var gCookiesWindow = {
  _hosts: {},
  _hostOrder: [],
  _tree: null,
  _bundle: null,

  init() {
    Services.obs.addObserver(this, "cookie-changed");
    Services.obs.addObserver(this, "perm-changed");

    this._bundle = document.getElementById("bundlePreferences");
    this._tree = document.getElementById("cookiesList");

    let removeAllCookies = document.getElementById("removeAllCookies");
    removeAllCookies.setAttribute("accesskey", this._bundle.getString("removeAllCookies.accesskey"));
    let removeSelectedCookies = document.getElementById("removeSelectedCookies");
    removeSelectedCookies.setAttribute("accesskey", this._bundle.getString("removeSelectedCookies.accesskey"));

    this._populateList(true);

    document.getElementById("filter").focus();

    if (!Services.prefs.getBoolPref("privacy.userContext.enabled")) {
      document.getElementById("userContextRow").hidden = true;
    }
  },

  uninit() {
    Services.obs.removeObserver(this, "cookie-changed");
    Services.obs.removeObserver(this, "perm-changed");
  },

  _populateList(aInitialLoad) {
    this._loadCookies();
    this._tree.view = this._view;
    if (aInitialLoad)
      this.sort("rawHost");
    if (this._view.rowCount > 0)
      this._tree.view.selection.select(0);

    if (aInitialLoad) {
      if ("arguments" in window &&
          window.arguments[0] &&
          window.arguments[0].filterString)
        this.setFilter(window.arguments[0].filterString);
    } else if (document.getElementById("filter").value != "") {
      this.filter();
    }

    this._updateRemoveAllButton();

    this._saveState();
  },

  _cookieEquals(aCookieA, aCookieB, aStrippedHost) {
    return aCookieA.rawHost == aStrippedHost &&
           aCookieA.name == aCookieB.name &&
           aCookieA.path == aCookieB.path &&
           ChromeUtils.isOriginAttributesEqual(aCookieA.originAttributes,
                                               aCookieB.originAttributes);
  },

  _isPrivateCookie(cookie) {
    let { userContextId } = cookie.originAttributes;
    // A private cookie is when its userContextId points to a private identity.
    return userContextId && !ContextualIdentityService.getPublicIdentityFromId(userContextId);
  },

  observe(aCookie, aTopic, aData) {
    if (aTopic != "cookie-changed")
      return;

    if (aCookie instanceof Ci.nsICookie) {
      if (this._isPrivateCookie(aCookie)) {
        return;
      }

      var strippedHost = this._makeStrippedHost(aCookie.host);
      if (aData == "changed")
        this._handleCookieChanged(aCookie, strippedHost);
      else if (aData == "added")
        this._handleCookieAdded(aCookie, strippedHost);
    } else if (aData == "cleared") {
      this._hosts = {};
      this._hostOrder = [];

      var oldRowCount = this._view._rowCount;
      this._view._rowCount = 0;
      this._tree.treeBoxObject.rowCountChanged(0, -oldRowCount);
      this._view.selection.clearSelection();
      this._updateRemoveAllButton();
    } else if (aData == "reload") {
      // first, clear any existing entries
      this.observe(aCookie, aTopic, "cleared");

      // then, reload the list
      this._populateList(false);
    }

    // We don't yet handle aData == "deleted" - it's a less common case
    // and is rather complicated as selection tracking is difficult
  },

  _handleCookieChanged(changedCookie, strippedHost) {
    var rowIndex = 0;
    var cookieItem = null;
    if (!this._view._filtered) {
      for (let host of this._hostOrder) {
        ++rowIndex;
        var hostItem = this._hosts[host];
        if (host == strippedHost) {
          // Host matches, look for the cookie within this Host collection
          // and update its data
          for (let currCookie of hostItem.cookies) {
            ++rowIndex;
            if (this._cookieEquals(currCookie, changedCookie, strippedHost)) {
              currCookie.value    = changedCookie.value;
              currCookie.isSecure = changedCookie.isSecure;
              currCookie.isDomain = changedCookie.isDomain;
              currCookie.expires  = changedCookie.expires;
              cookieItem = currCookie;
              break;
            }
          }
        } else if (hostItem.open)
          rowIndex += hostItem.cookies.length;
      }
    } else {
      // Just walk the filter list to find the item. It doesn't matter that
      // we don't update the main Host collection when we do this, because
      // when the filter is reset the Host collection is rebuilt anyway.
      for (let currCookie of this._view._filterSet) {
        if (this._cookieEquals(currCookie, changedCookie, strippedHost)) {
          currCookie.value    = changedCookie.value;
          currCookie.isSecure = changedCookie.isSecure;
          currCookie.isDomain = changedCookie.isDomain;
          currCookie.expires  = changedCookie.expires;
          cookieItem = currCookie;
          break;
        }
      }
    }

    // Make sure the tree display is up to date...
    this._tree.treeBoxObject.invalidateRow(rowIndex);
    // ... and if the cookie is selected, update the displayed metadata too
    if (cookieItem != null && this._view.selection.currentIndex == rowIndex)
      this._updateCookieData(cookieItem);
  },

  _handleCookieAdded(changedCookie, strippedHost) {
    var rowCountImpact = 0;
    var addedHost = { value: 0 };
    this._addCookie(strippedHost, changedCookie, addedHost);
    if (!this._view._filtered) {
      // The Host collection for this cookie already exists, and it's not open,
      // so don't increment the rowCountImpact becaues the user is not going to
      // see the additional rows as they're hidden.
      if (addedHost.value || this._hosts[strippedHost].open)
        ++rowCountImpact;
    } else {
      // We're in search mode, and the cookie being added matches
      // the search condition, so add it to the list.
      var c = this._makeCookieObject(strippedHost, changedCookie);
      if (this._cookieMatchesFilter(c)) {
        this._view._filterSet.push(this._makeCookieObject(strippedHost, changedCookie));
        ++rowCountImpact;
      }
    }
    // Now update the tree display at the end (we could/should re run the sort
    // if any to get the position correct.)
    var oldRowCount = this._rowCount;
    this._view._rowCount += rowCountImpact;
    this._tree.treeBoxObject.rowCountChanged(oldRowCount - 1, rowCountImpact);

    this._updateRemoveAllButton();
  },

  _view: {
    _filtered: false,
    _filterSet: [],
    _filterValue: "",
    _rowCount: 0,
    _cacheValid: 0,
    _cacheItems: [],
    get rowCount() {
      return this._rowCount;
    },

    _getItemAtIndex(aIndex) {
      if (this._filtered)
        return this._filterSet[aIndex];

      var start = 0;
      var count = 0, hostIndex = 0;

      var cacheIndex = Math.min(this._cacheValid, aIndex);
      if (cacheIndex > 0) {
        var cacheItem = this._cacheItems[cacheIndex];
        start = cacheItem.start;
        count = hostIndex = cacheItem.count;
      }

      for (let i = start; i < gCookiesWindow._hostOrder.length; ++i) { // var host in gCookiesWindow._hosts) {
        let currHost = gCookiesWindow._hosts[gCookiesWindow._hostOrder[i]];// gCookiesWindow._hosts[host];
        if (!currHost) continue;
        if (count == aIndex)
          return currHost;
        hostIndex = count;

        var cacheEntry = { "start": i, "count": count };
        var cacheStart = count;

        if (currHost.open) {
          if (count < aIndex && aIndex <= (count + currHost.cookies.length)) {
            // We are looking for an entry within this host's children,
            // enumerate them looking for the index.
            ++count;
            for (let cookie of currHost.cookies) {
              if (count == aIndex) {
                cookie.parentIndex = hostIndex;
                return cookie;
              }
              ++count;
            }
          } else {
            // A host entry was open, but we weren't looking for an index
            // within that host entry's children, so skip forward over the
            // entry's children. We need to add one to increment for the
            // host value too.
            count += currHost.cookies.length + 1;
          }
        } else
          ++count;

        for (let k = cacheStart; k < count; k++)
          this._cacheItems[k] = cacheEntry;
        this._cacheValid = count - 1;
      }
      return null;
    },

    _removeItemAtIndex(aIndex, aCount) {
      let removeCount = aCount === undefined ? 1 : aCount;
      if (this._filtered) {
        // remove the cookies from the unfiltered set so that they
        // don't reappear when the filter is changed. See bug 410863.
        for (let i = aIndex; i < aIndex + removeCount; ++i) {
          let item = this._filterSet[i];
          let parent = gCookiesWindow._hosts[item.rawHost];
          for (let j = 0; j < parent.cookies.length; ++j) {
            if (item == parent.cookies[j]) {
              parent.cookies.splice(j, 1);
              break;
            }
          }
        }
        this._filterSet.splice(aIndex, removeCount);
        return;
      }

      let item = this._getItemAtIndex(aIndex);
      if (!item) return;
      this._invalidateCache(aIndex - 1);
      if (item.container) {
        gCookiesWindow._hosts[item.rawHost] = null;
      } else {
        let parent = this._getItemAtIndex(item.parentIndex);
        for (let i = 0; i < parent.cookies.length; ++i) {
          let cookie = parent.cookies[i];
          if (item.rawHost == cookie.rawHost &&
              item.name == cookie.name &&
              item.path == cookie.path &&
              ChromeUtils.isOriginAttributesEqual(item.originAttributes,
                                                  cookie.originAttributes)) {
            parent.cookies.splice(i, removeCount);
          }
        }
      }
    },

    _invalidateCache(aIndex) {
      this._cacheValid = Math.min(this._cacheValid, aIndex);
    },

    getCellText(aIndex, aColumn) {
      if (!this._filtered) {
        var item = this._getItemAtIndex(aIndex);
        if (!item)
          return "";
        if (aColumn.id == "domainCol")
          return item.rawHost;
        else if (aColumn.id == "nameCol")
          return item.name;
      } else if (aColumn.id == "domainCol") {
        return this._filterSet[aIndex].rawHost;
      } else if (aColumn.id == "nameCol") {
        return this._filterSet[aIndex].name;
      }
      return "";
    },

    _selection: null,
    get selection() { return this._selection; },
    set selection(val) { this._selection = val; return val; },
    getRowProperties(aIndex) { return ""; },
    getCellProperties(aIndex, aColumn) { return ""; },
    getColumnProperties(aColumn) { return ""; },
    isContainer(aIndex) {
      if (!this._filtered) {
        var item = this._getItemAtIndex(aIndex);
        if (!item) return false;
        return item.container;
      }
      return false;
    },
    isContainerOpen(aIndex) {
      if (!this._filtered) {
        var item = this._getItemAtIndex(aIndex);
        if (!item) return false;
        return item.open;
      }
      return false;
    },
    isContainerEmpty(aIndex) {
      if (!this._filtered) {
        var item = this._getItemAtIndex(aIndex);
        if (!item) return false;
        return item.cookies.length == 0;
      }
      return false;
    },
    isSeparator(aIndex) { return false; },
    isSorted(aIndex) { return false; },
    canDrop(aIndex, aOrientation) { return false; },
    drop(aIndex, aOrientation) {},
    getParentIndex(aIndex) {
      if (!this._filtered) {
        var item = this._getItemAtIndex(aIndex);
        // If an item has no parent index (i.e. it is at the top level) this
        // function MUST return -1 otherwise we will go into an infinite loop.
        // Containers are always top level items in the cookies tree, so make
        // sure to return the appropriate value here.
        if (!item || item.container) return -1;
        return item.parentIndex;
      }
      return -1;
    },
    hasNextSibling(aParentIndex, aIndex) {
      if (!this._filtered) {
        // |aParentIndex| appears to be bogus, but we can get the real
        // parent index by getting the entry for |aIndex| and reading the
        // parentIndex field.
        // The index of the last item in this host collection is the
        // index of the parent + the size of the host collection, and
        // aIndex has a next sibling if it is less than this value.
        var item = this._getItemAtIndex(aIndex);
        if (item) {
          if (item.container) {
            for (let i = aIndex + 1; i < this.rowCount; ++i) {
              var subsequent = this._getItemAtIndex(i);
              if (subsequent.container)
                return true;
            }
            return false;
          }
          var parent = this._getItemAtIndex(item.parentIndex);
          if (parent && parent.container)
            return aIndex < item.parentIndex + parent.cookies.length;
        }
      }
      return aIndex < this.rowCount - 1;
    },
    hasPreviousSibling(aIndex) {
      if (!this._filtered) {
        var item = this._getItemAtIndex(aIndex);
        if (!item) return false;
        var parent = this._getItemAtIndex(item.parentIndex);
        if (parent && parent.container)
          return aIndex > item.parentIndex + 1;
      }
      return aIndex > 0;
    },
    getLevel(aIndex) {
      if (!this._filtered) {
        var item = this._getItemAtIndex(aIndex);
        if (!item) return 0;
        return item.level;
      }
      return 0;
    },
    getImageSrc(aIndex, aColumn) {},
    getCellValue(aIndex, aColumn) {},
    setTree(aTree) {},
    toggleOpenState(aIndex) {
      if (!this._filtered) {
        var item = this._getItemAtIndex(aIndex);
        if (!item) return;
        this._invalidateCache(aIndex);
        var multiplier = item.open ? -1 : 1;
        var delta = multiplier * item.cookies.length;
        this._rowCount += delta;
        item.open = !item.open;
        gCookiesWindow._tree.treeBoxObject.rowCountChanged(aIndex + 1, delta);
        gCookiesWindow._tree.treeBoxObject.invalidateRow(aIndex);
      }
    },
    cycleHeader(aColumn) {},
    selectionChanged() {},
    cycleCell(aIndex, aColumn) {},
    isEditable(aIndex, aColumn) {
      return false;
    },
    isSelectable(aIndex, aColumn) {
      return false;
    },
    setCellValue(aIndex, aColumn, aValue) {},
    setCellText(aIndex, aColumn, aValue) {},
    performAction(aAction) {},
    performActionOnRow(aAction, aIndex) {},
    performActionOnCell(aAction, aindex, aColumn) {}
  },

  _makeStrippedHost(aHost) {
    var formattedHost = aHost.charAt(0) == "." ? aHost.substring(1, aHost.length) : aHost;
    return formattedHost.substring(0, 4) == "www." ? formattedHost.substring(4, formattedHost.length) : formattedHost;
  },

  _addCookie(aStrippedHost, aCookie, aHostCount) {
    if (!(aStrippedHost in this._hosts) || !this._hosts[aStrippedHost]) {
      this._hosts[aStrippedHost] = { cookies: [],
                                     rawHost: aStrippedHost,
                                     level: 0,
                                     open: false,
                                     container: true };
      this._hostOrder.push(aStrippedHost);
      ++aHostCount.value;
    }

    var c = this._makeCookieObject(aStrippedHost, aCookie);
    this._hosts[aStrippedHost].cookies.push(c);
  },

  _makeCookieObject(aStrippedHost, aCookie) {
    var c = { name: aCookie.name,
              value: aCookie.value,
              isDomain: aCookie.isDomain,
              host: aCookie.host,
              rawHost: aStrippedHost,
              path: aCookie.path,
              isSecure: aCookie.isSecure,
              expires: aCookie.expires,
              level: 1,
              container: false,
              originAttributes: aCookie.originAttributes };
    return c;
  },

  _loadCookies() {
    var e = Services.cookies.enumerator;
    var hostCount = { value: 0 };
    this._hosts = {};
    this._hostOrder = [];
    while (e.hasMoreElements()) {
      var cookie = e.getNext();
      if (cookie && cookie instanceof Ci.nsICookie) {
        if (this._isPrivateCookie(cookie)) {
          continue;
        }

        var strippedHost = this._makeStrippedHost(cookie.host);
        this._addCookie(strippedHost, cookie, hostCount);
      } else
        break;
    }
    this._view._rowCount = hostCount.value;
  },

  formatExpiresString(aExpires) {
    if (aExpires) {
      var date = new Date(1000 * aExpires);
      const dateTimeFormatter = new Services.intl.DateTimeFormat(undefined, {
        dateStyle: "long", timeStyle: "long"
      });
      return dateTimeFormatter.format(date);
    }
    return this._bundle.getString("expireAtEndOfSession");
  },

  _getUserContextString(aUserContextId) {
    if (parseInt(aUserContextId) == 0) {
      return this._bundle.getString("defaultUserContextLabel");
    }

    return ContextualIdentityService.getUserContextLabel(aUserContextId);
  },

  _updateCookieData(aItem) {
    var seln = this._view.selection;
    var ids = ["name", "value", "host", "path", "isSecure", "expires", "userContext"];
    var properties;

    if (aItem && !aItem.container && seln.count > 0) {
      properties = { name: aItem.name, value: aItem.value, host: aItem.host,
                     path: aItem.path, expires: this.formatExpiresString(aItem.expires),
                     isDomain: aItem.isDomain ? this._bundle.getString("domainColon")
                                              : this._bundle.getString("hostColon"),
                     isSecure: aItem.isSecure ? this._bundle.getString("forSecureOnly")
                                              : this._bundle.getString("forAnyConnection"),
                     userContext: this._getUserContextString(aItem.originAttributes.userContextId) };
      for (let id of ids) {
        document.getElementById(id).disabled = false;
      }
    } else {
      var noneSelected = this._bundle.getString("noCookieSelected");
      properties = { name: noneSelected, value: noneSelected, host: noneSelected,
                     path: noneSelected, expires: noneSelected,
                     isSecure: noneSelected, userContext: noneSelected };
      for (let id of ids) {
        document.getElementById(id).disabled = true;
      }
    }
    for (let property in properties)
      document.getElementById(property).value = properties[property];
  },

  onCookieSelected() {
    var item;
    var seln = this._tree.view.selection;
    if (!this._view._filtered)
      item = this._view._getItemAtIndex(seln.currentIndex);
    else
      item = this._view._filterSet[seln.currentIndex];

    this._updateCookieData(item);

    var rangeCount = seln.getRangeCount();
    var selectedCookieCount = 0;
    for (let i = 0; i < rangeCount; ++i) {
      var min = {}; var max = {};
      seln.getRangeAt(i, min, max);
      for (let j = min.value; j <= max.value; ++j) {
        item = this._view._getItemAtIndex(j);
        if (!item) continue;
        if (item.container)
          selectedCookieCount += item.cookies.length;
        else if (!item.container)
          ++selectedCookieCount;
      }
    }

    let buttonLabel = this._bundle.getString("removeSelectedCookies.label");
    let removeSelectedCookies = document.getElementById("removeSelectedCookies");
    removeSelectedCookies.label = PluralForm.get(selectedCookieCount, buttonLabel)
                                            .replace("#1", selectedCookieCount);

    removeSelectedCookies.disabled = !(seln.count > 0);
  },

  performDeletion: function gCookiesWindow_performDeletion(deleteItems) {
    var blockFutureCookies = false;
    if (Services.prefs.prefHasUserValue("network.cookie.blockFutureCookies"))
      blockFutureCookies = Services.prefs.getBoolPref("network.cookie.blockFutureCookies");
    for (let item of deleteItems) {
      Services.cookies.remove(item.host, item.name, item.path,
                      blockFutureCookies, item.originAttributes);
    }
  },

  deleteCookie() {
    // Selection Notes
    // - Selection always moves to *NEXT* adjacent item unless item
    //   is last child at a given level in which case it moves to *PREVIOUS*
    //   item
    //
    // Selection Cases (Somewhat Complicated)
    //
    // 1) Single cookie selected, host has single child
    //    v cnn.com
    //    //// cnn.com ///////////// goksdjf@ ////
    //    > atwola.com
    //
    //    Before SelectedIndex: 1   Before RowCount: 3
    //    After  SelectedIndex: 0   After  RowCount: 1
    //
    // 2) Host selected, host open
    //    v goats.com ////////////////////////////
    //         goats.com             sldkkfjl
    //         goat.scom             flksj133
    //    > atwola.com
    //
    //    Before SelectedIndex: 0   Before RowCount: 4
    //    After  SelectedIndex: 0   After  RowCount: 1
    //
    // 3) Host selected, host closed
    //    > goats.com ////////////////////////////
    //    > atwola.com
    //
    //    Before SelectedIndex: 0   Before RowCount: 2
    //    After  SelectedIndex: 0   After  RowCount: 1
    //
    // 4) Single cookie selected, host has many children
    //    v goats.com
    //         goats.com             sldkkfjl
    //    //// goats.com /////////// flksjl33 ////
    //    > atwola.com
    //
    //    Before SelectedIndex: 2   Before RowCount: 4
    //    After  SelectedIndex: 1   After  RowCount: 3
    //
    // 5) Single cookie selected, host has many children
    //    v goats.com
    //    //// goats.com /////////// flksjl33 ////
    //         goats.com             sldkkfjl
    //    > atwola.com
    //
    //    Before SelectedIndex: 1   Before RowCount: 4
    //    After  SelectedIndex: 1   After  RowCount: 3
    var seln = this._view.selection;
    var tbo = this._tree.treeBoxObject;

    if (seln.count < 1) return;

    var nextSelected = 0;
    var rowCountImpact = 0;
    var deleteItems = [];
    if (!this._view._filtered) {
      var ci = seln.currentIndex;
      nextSelected = ci;
      var invalidateRow = -1;
      var item = this._view._getItemAtIndex(ci);
      if (item.container) {
        rowCountImpact -= (item.open ? item.cookies.length : 0) + 1;
        deleteItems = deleteItems.concat(item.cookies);
        if (!this._view.hasNextSibling(-1, ci))
          --nextSelected;
        this._view._removeItemAtIndex(ci);
      } else {
        var parent = this._view._getItemAtIndex(item.parentIndex);
        --rowCountImpact;
        if (parent.cookies.length == 1) {
          --rowCountImpact;
          deleteItems.push(item);
          if (!this._view.hasNextSibling(-1, ci))
            --nextSelected;
          if (!this._view.hasNextSibling(-1, item.parentIndex))
            --nextSelected;
          this._view._removeItemAtIndex(item.parentIndex);
          invalidateRow = item.parentIndex;
        } else {
          deleteItems.push(item);
          if (!this._view.hasNextSibling(-1, ci))
            --nextSelected;
          this._view._removeItemAtIndex(ci);
        }
      }
      this._view._rowCount += rowCountImpact;
      tbo.rowCountChanged(ci, rowCountImpact);
      if (invalidateRow != -1)
        tbo.invalidateRow(invalidateRow);
    } else {
      var rangeCount = seln.getRangeCount();
      // Traverse backwards through selections to avoid messing
      // up the indices when they are deleted.
      // See bug 388079.
      for (let i = rangeCount - 1; i >= 0; --i) {
        var min = {}; var max = {};
        seln.getRangeAt(i, min, max);
        nextSelected = min.value;
        for (let j = min.value; j <= max.value; ++j) {
          deleteItems.push(this._view._getItemAtIndex(j));
          if (!this._view.hasNextSibling(-1, max.value))
            --nextSelected;
        }
        var delta = max.value - min.value + 1;
        this._view._removeItemAtIndex(min.value, delta);
        rowCountImpact = -1 * delta;
        this._view._rowCount += rowCountImpact;
        tbo.rowCountChanged(min.value, rowCountImpact);
      }
    }

    this.performDeletion(deleteItems);

    if (nextSelected < 0)
      seln.clearSelection();
    else {
      seln.select(nextSelected);
      this._tree.focus();
    }
  },

  deleteAllCookies() {
    if (this._view._filtered) {
      var rowCount = this._view.rowCount;
      var deleteItems = [];
      for (let index = 0; index < rowCount; index++) {
        deleteItems.push(this._view._getItemAtIndex(index));
      }
      this._view._removeItemAtIndex(0, rowCount);
      this._view._rowCount = 0;
      this._tree.treeBoxObject.rowCountChanged(0, -rowCount);
      this.performDeletion(deleteItems);
    } else {
      Services.cookies.removeAll();
    }
    this._updateRemoveAllButton();
    this.focusFilterBox();
  },

  onCookieKeyPress(aEvent) {
    if (aEvent.keyCode == KeyEvent.DOM_VK_DELETE ||
        (AppConstants.platform == "macosx" &&
        aEvent.keyCode == KeyEvent.DOM_VK_BACK_SPACE)) {
      this.deleteCookie();
      aEvent.preventDefault();
    } else if (aEvent.getModifierState("Accel") &&
               document.getElementById("key_selectAll")
                       .getAttribute("key")
                       .toLocaleLowerCase()
                       .charCodeAt(0) == aEvent.charCode) {
      let view = gCookiesWindow._view;
      view.selection.selectAll();
      aEvent.preventDefault();
    }
  },

  _lastSortProperty: "",
  _lastSortAscending: false,
  sort(aProperty) {
    var ascending = (aProperty == this._lastSortProperty) ? !this._lastSortAscending : true;
    // Sort the Non-Filtered Host Collections
    if (aProperty == "rawHost") {
      function sortByHost(a, b) {
        return a.toLowerCase().localeCompare(b.toLowerCase());
      }
      this._hostOrder.sort(sortByHost);
      if (!ascending)
        this._hostOrder.reverse();
    }

    function sortByProperty(a, b) {
      return a[aProperty].toLowerCase().localeCompare(b[aProperty].toLowerCase());
    }
    for (let host in this._hosts) {
      var cookies = this._hosts[host].cookies;
      cookies.sort(sortByProperty);
      if (!ascending)
        cookies.reverse();
    }
    // Sort the Filtered List, if in Filtered mode
    if (this._view._filtered) {
      this._view._filterSet.sort(sortByProperty);
      if (!ascending)
        this._view._filterSet.reverse();
    }

    // Adjust the Sort Indicator
    var domainCol = document.getElementById("domainCol");
    var nameCol = document.getElementById("nameCol");
    var sortOrderString = ascending ? "ascending" : "descending";
    if (aProperty == "rawHost") {
      domainCol.setAttribute("sortDirection", sortOrderString);
      nameCol.removeAttribute("sortDirection");
    } else {
      nameCol.setAttribute("sortDirection", sortOrderString);
      domainCol.removeAttribute("sortDirection");
    }

    this._view._invalidateCache(0);
    this._view.selection.clearSelection();
    this._view.selection.select(0);
    this._tree.treeBoxObject.invalidate();
    this._tree.treeBoxObject.ensureRowIsVisible(0);

    this._lastSortAscending = ascending;
    this._lastSortProperty = aProperty;
  },

  clearFilter() {
    // Revert to single-select in the tree
    this._tree.setAttribute("seltype", "single");

    // Clear the Tree Display
    this._view._filtered = false;
    this._view._rowCount = 0;
    this._tree.treeBoxObject.rowCountChanged(0, -this._view._filterSet.length);
    this._view._filterSet = [];

    // Just reload the list to make sure deletions are respected
    this._loadCookies();
    this._tree.view = this._view;

    // Restore sort order
    var sortby = this._lastSortProperty;
    if (sortby == "") {
      this._lastSortAscending = false;
      this.sort("rawHost");
    } else {
      this._lastSortAscending = !this._lastSortAscending;
      this.sort(sortby);
    }

    // Restore open state
    for (let openIndex of this._openIndices) {
      this._view.toggleOpenState(openIndex);
    }
    this._openIndices = [];

    // Restore selection
    this._view.selection.clearSelection();
    for (let range of this._lastSelectedRanges) {
      this._view.selection.rangedSelect(range.min, range.max, true);
    }
    this._lastSelectedRanges = [];

    document.getElementById("cookiesIntro").value = this._bundle.getString("cookiesAll");
    this._updateRemoveAllButton();
  },

  _cookieMatchesFilter(aCookie) {
    return aCookie.rawHost.includes(this._view._filterValue) ||
           aCookie.name.includes(this._view._filterValue) ||
           aCookie.value.includes(this._view._filterValue);
  },

  _filterCookies(aFilterValue) {
    this._view._filterValue = aFilterValue;
    var cookies = [];
    for (let i = 0; i < gCookiesWindow._hostOrder.length; ++i) { // var host in gCookiesWindow._hosts) {
      let currHost = gCookiesWindow._hosts[gCookiesWindow._hostOrder[i]]; // gCookiesWindow._hosts[host];
      if (!currHost) continue;
      for (let cookie of currHost.cookies) {
        if (this._cookieMatchesFilter(cookie))
          cookies.push(cookie);
      }
    }
    return cookies;
  },

  _lastSelectedRanges: [],
  _openIndices: [],
  _saveState() {
    // Save selection
    var seln = this._view.selection;
    this._lastSelectedRanges = [];
    var rangeCount = seln.getRangeCount();
    for (let i = 0; i < rangeCount; ++i) {
      var min = {}; var max = {};
      seln.getRangeAt(i, min, max);
      this._lastSelectedRanges.push({ min: min.value, max: max.value });
    }

    // Save open states
    this._openIndices = [];
    for (let i = 0; i < this._view.rowCount; ++i) {
      var item = this._view._getItemAtIndex(i);
      if (item && item.container && item.open)
        this._openIndices.push(i);
    }
  },

  _updateRemoveAllButton: function gCookiesWindow__updateRemoveAllButton() {
    let removeAllCookies = document.getElementById("removeAllCookies");
    removeAllCookies.disabled = this._view._rowCount == 0;

    let labelStringID = "removeAllCookies.label";
    let accessKeyStringID = "removeAllCookies.accesskey";
    if (this._view._filtered) {
      labelStringID = "removeAllShownCookies.label";
      accessKeyStringID = "removeAllShownCookies.accesskey";
    }
    removeAllCookies.setAttribute("label", this._bundle.getString(labelStringID));
    removeAllCookies.setAttribute("accesskey", this._bundle.getString(accessKeyStringID));
  },

  filter() {
    var filter = document.getElementById("filter").value;
    if (filter == "") {
      gCookiesWindow.clearFilter();
      return;
    }
    var view = gCookiesWindow._view;
    view._filterSet = gCookiesWindow._filterCookies(filter);
    if (!view._filtered) {
      // Save Display Info for the Non-Filtered mode when we first
      // enter Filtered mode.
      gCookiesWindow._saveState();
      view._filtered = true;
    }
    // Move to multi-select in the tree
    gCookiesWindow._tree.setAttribute("seltype", "multiple");

    // Clear the display
    var oldCount = view._rowCount;
    view._rowCount = 0;
    gCookiesWindow._tree.treeBoxObject.rowCountChanged(0, -oldCount);
    // Set up the filtered display
    view._rowCount = view._filterSet.length;
    gCookiesWindow._tree.treeBoxObject.rowCountChanged(0, view.rowCount);

    // if the view is not empty then select the first item
    if (view.rowCount > 0)
      view.selection.select(0);

    document.getElementById("cookiesIntro").value = gCookiesWindow._bundle.getString("cookiesFiltered");
    this._updateRemoveAllButton();
  },

  setFilter(aFilterString) {
    document.getElementById("filter").value = aFilterString;
    this.filter();
  },

  focusFilterBox() {
    var filter = document.getElementById("filter");
    filter.focus();
    filter.select();
  },

  onWindowKeyPress(aEvent) {
    if (aEvent.keyCode == KeyEvent.DOM_VK_ESCAPE)
      window.close();
  }
};
PK
!<����oo6chrome/browser/content/browser/preferences/cookies.xul<?xml version="1.0"?>


<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css" type="text/css"?>

<!DOCTYPE dialog [
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
%browserDTD;
<!ENTITY % cookiesDTD SYSTEM "chrome://browser/locale/preferences/cookies.dtd">
%cookiesDTD;
]>

<window id="CookiesDialog" windowtype="Browser:Cookies"
        class="windowDialog" title="&window.title;"
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        style="width: &window.width;;"
        onload="gCookiesWindow.init();"
        onunload="gCookiesWindow.uninit();"
        persist="screenX screenY width height"
        onkeypress="gCookiesWindow.onWindowKeyPress(event);">

  <script src="chrome://browser/content/preferences/cookies.js"/>

  <stringbundle id="bundlePreferences"
                src="chrome://browser/locale/preferences/preferences.properties"/>

  <keyset>
    <key key="&windowClose.key;" modifiers="accel" oncommand="window.close();"/>
    <key key="&focusSearch1.key;" modifiers="accel" oncommand="gCookiesWindow.focusFilterBox();"/>
    <key id="key_selectAll" key="&selectAllCmd.key;" modifiers="accel"/>
  </keyset>

  <vbox flex="1" class="contentPane largeDialogContainer">
    <hbox align="center">
      <textbox type="search" id="filter" flex="1"
               aria-controls="cookiesList"
               oncommand="gCookiesWindow.filter();"
               placeholder="&searchFilter.label;"
               accesskey="&searchFilter.accesskey;"/>
    </hbox>
    <separator class="thin"/>
    <label control="cookiesList" id="cookiesIntro">&cookiesonsystem2.label;</label>
    <separator class="thin"/>
    <tree id="cookiesList" flex="1" style="height: 10em;"
          onkeypress="gCookiesWindow.onCookieKeyPress(event)"
          onselect="gCookiesWindow.onCookieSelected();"
          hidecolumnpicker="true" seltype="single">
      <treecols>
        <treecol id="domainCol" label="&cookiedomain.label;" flex="2" primary="true"
                 persist="width" onclick="gCookiesWindow.sort('rawHost');"/>
        <splitter class="tree-splitter"/>
        <treecol id="nameCol" label="&cookiename.label;" flex="1"
                 persist="width"
                 onclick="gCookiesWindow.sort('name');"/>
      </treecols>
      <treechildren id="cookiesChildren"/>
    </tree>
    <hbox id="cookieInfoBox">
      <grid flex="1" id="cookieInfoGrid">
        <columns>
          <column/>
          <column flex="1"/>
        </columns>
        <rows>
          <row align="center">
            <hbox pack="end"><label id="nameLabel" control="name">&props.name.label;</label></hbox>
            <textbox id="name" readonly="true" class="plain"/>
          </row>
          <row align="center">
            <hbox pack="end"><label id="valueLabel" control="value">&props.value.label;</label></hbox>
            <textbox id="value" readonly="true" class="plain"/>
          </row>
          <row align="center">
            <hbox pack="end"><label id="isDomain" control="host">&props.domain.label;</label></hbox>
            <textbox id="host" readonly="true" class="plain"/>
          </row>
          <row align="center">
            <hbox pack="end"><label id="pathLabel" control="path">&props.path.label;</label></hbox>
            <textbox id="path" readonly="true" class="plain"/>
          </row>
          <row align="center">
            <hbox pack="end"><label id="isSecureLabel" control="isSecure">&props.secure.label;</label></hbox>
            <textbox id="isSecure" readonly="true" class="plain"/>
          </row>
          <row align="center">
            <hbox pack="end"><label id="expiresLabel" control="expires">&props.expires.label;</label></hbox>
            <textbox id="expires" readonly="true" class="plain"/>
          </row>
          <row align="center" id="userContextRow">
            <hbox pack="end"><label id="userContextLabel" control="userContext">&props.container.label;</label></hbox>
            <textbox id="userContext" readonly="true" class="plain"/>
          </row>
        </rows>
      </grid>
    </hbox>
  </vbox>
  <hbox align="end">
    <hbox class="actionButtons" flex="1">
      <button id="removeSelectedCookies" disabled="true" icon="clear"
              oncommand="gCookiesWindow.deleteCookie();"/>
      <button id="removeAllCookies" disabled="true" icon="clear"
              oncommand="gCookiesWindow.deleteAllCookies();"/>
      <spacer flex="1"/>
      <button oncommand="close();" icon="close"
              label="&button.close.label;" accesskey="&button.close.accesskey;"/>
    </hbox>
  </hbox>
</window>
PK
!<�_	3chrome/browser/content/browser/preferences/fonts.js/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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-globals-from ../../../toolkit/mozapps/preferences/fontbuilder.js */

// browser.display.languageList LOCK ALL when LOCKED

const kDefaultFontType          = "font.default.%LANG%";
const kFontNameFmtSerif         = "font.name.serif.%LANG%";
const kFontNameFmtSansSerif     = "font.name.sans-serif.%LANG%";
const kFontNameFmtMonospace     = "font.name.monospace.%LANG%";
const kFontNameListFmtSerif     = "font.name-list.serif.%LANG%";
const kFontNameListFmtSansSerif = "font.name-list.sans-serif.%LANG%";
const kFontNameListFmtMonospace = "font.name-list.monospace.%LANG%";
const kFontSizeFmtVariable      = "font.size.variable.%LANG%";
const kFontSizeFmtFixed         = "font.size.fixed.%LANG%";
const kFontMinSizeFmt           = "font.minimum-size.%LANG%";

Preferences.addAll([
  { id: "font.language.group", type: "wstring" },
  { id: "browser.display.use_document_fonts", type: "int" },
  { id: "intl.charset.fallback.override", type: "string" },
]);

var gFontsDialog = {
  _selectLanguageGroupPromise: Promise.resolve(),

  _selectLanguageGroup(aLanguageGroup) {
    this._selectLanguageGroupPromise = (async () => {
      // Avoid overlapping language group selections by awaiting the resolution
      // of the previous one.  We do this because this function is re-entrant,
      // as inserting <preference> elements into the DOM sometimes triggers a call
      // back into this function.  And since this function is also asynchronous,
      // that call can enter this function before the previous run has completed,
      // which would corrupt the font menulists.  Awaiting the previous call's
      // resolution avoids that fate.
      await this._selectLanguageGroupPromise;

      var prefs = [
        { format: kDefaultFontType,          type: "string",   element: "defaultFontType", fonttype: null     },
        { format: kFontNameFmtSerif,         type: "fontname", element: "serif",      fonttype: "serif"       },
        { format: kFontNameFmtSansSerif,     type: "fontname", element: "sans-serif", fonttype: "sans-serif"  },
        { format: kFontNameFmtMonospace,     type: "fontname", element: "monospace",  fonttype: "monospace"   },
        { format: kFontNameListFmtSerif,     type: "unichar",  element: null,         fonttype: "serif"       },
        { format: kFontNameListFmtSansSerif, type: "unichar",  element: null,         fonttype: "sans-serif"  },
        { format: kFontNameListFmtMonospace, type: "unichar",  element: null,         fonttype: "monospace"   },
        { format: kFontSizeFmtVariable,      type: "int",      element: "sizeVar",    fonttype: null          },
        { format: kFontSizeFmtFixed,         type: "int",      element: "sizeMono",   fonttype: null          },
        { format: kFontMinSizeFmt,           type: "int",      element: "minSize",    fonttype: null          }
      ];
      for (var i = 0; i < prefs.length; ++i) {
        var name = prefs[i].format.replace(/%LANG%/, aLanguageGroup);
        var preference = Preferences.get(name);
        if (!preference) {
          preference = Preferences.add({ id: name, type: prefs[i].type });
        }

        if (!prefs[i].element)
          continue;

        var element = document.getElementById(prefs[i].element);
        if (element) {
          element.setAttribute("preference", preference.id);

          if (prefs[i].fonttype)
            await FontBuilder.buildFontList(aLanguageGroup, prefs[i].fonttype, element);

          preference.setElementValue(element);
        }
      }
    })()
      .catch(Cu.reportError);
  },

  readFontLanguageGroup() {
    var languagePref = Preferences.get("font.language.group");
    this._selectLanguageGroup(languagePref.value);
    return undefined;
  },

  readUseDocumentFonts() {
    var preference = Preferences.get("browser.display.use_document_fonts");
    return preference.value == 1;
  },

  writeUseDocumentFonts() {
    var useDocumentFonts = document.getElementById("useDocumentFonts");
    return useDocumentFonts.checked ? 1 : 0;
  },

  onBeforeAccept() {
    // It would be good if we could avoid touching languages the pref pages won't use, but
    // unfortunately the language group APIs (deducing language groups from language codes)
    // are C++ - only. So we just check all the things the user touched:
    // Don't care about anything up to 24px, or if this value is the same as set previously:
    let preferences = Preferences.getAll().filter(pref => {
      return pref.id.includes("font.minimum-size") && pref.value > 24 && pref.value != pref.valueFromPreferences;
    });
    if (!preferences.length) {
      return true;
    }

    let strings = document.getElementById("bundlePreferences");
    let title = strings.getString("veryLargeMinimumFontTitle");
    let confirmLabel = strings.getString("acceptVeryLargeMinimumFont");
    let warningMessage = strings.getString("veryLargeMinimumFontWarning");
    let {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm", {});
    let flags = Services.prompt.BUTTON_POS_1 * Services.prompt.BUTTON_TITLE_CANCEL |
                Services.prompt.BUTTON_POS_0 * Services.prompt.BUTTON_TITLE_IS_STRING |
                Services.prompt.BUTTON_POS_1_DEFAULT;
    let buttonChosen = Services.prompt.confirmEx(window, title, warningMessage, flags, confirmLabel, null, "", "", {});
    return buttonChosen == 0;
  },
};
PK
!<��`�;<;<4chrome/browser/content/browser/preferences/fonts.xul<?xml version="1.0"?>

<!-- -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- -->
<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css"?>

<!DOCTYPE dialog [
  <!ENTITY % preferencesDTD SYSTEM "chrome://global/locale/preferences.dtd">
  %preferencesDTD;
  <!ENTITY % fontsDTD SYSTEM "chrome://browser/locale/preferences/fonts.dtd">
  %fontsDTD;
]>

<dialog id="FontsDialog" type="child" class="prefwindow"
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        title="&fontsDialog.title;"
        buttons="accept,cancel,help"
        persist="lastSelected screenX screenY"
        closebuttonlabel="&preferencesCloseButton.label;"
        closebuttonaccesskey="&preferencesCloseButton.accesskey;"
        role="dialog"
        helpTopic="prefs-fonts-and-colors"
        ondialoghelp="openPrefsHelp()"
        onbeforeaccept="return gFontsDialog.onBeforeAccept();">

  <script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>
  <script type="application/javascript" src="chrome://global/content/preferencesBindings.js"/>

  <keyset>
    <key key="&windowClose.key;" modifiers="accel" oncommand="Preferences.close(event)"/>
  </keyset>

  <vbox id="FontsDialogPane" class="prefpane largeDialogContainer">

    <stringbundle id="bundlePreferences" src="chrome://browser/locale/preferences/preferences.properties"/>
    <script type="application/javascript" src="chrome://mozapps/content/preferences/fontbuilder.js"/>
    <script type="application/javascript" src="chrome://browser/content/preferences/fonts.js"/>

    <!-- Fonts for: [ Language ] -->
    <groupbox>
      <caption>
        <hbox align="center">
          <label accesskey="&fonts.accesskey;" control="selectLangs">&fonts.label;</label>
        </hbox>
        <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
        <hbox>
          <menulist id="selectLangs" preference="font.language.group"
                    onsyncfrompreference="return gFontsDialog.readFontLanguageGroup();">
            <menupopup>
              <menuitem value="ar"              label="&font.langGroup.arabic;"/>
              <menuitem value="x-armn"          label="&font.langGroup.armenian;"/>
              <menuitem value="x-beng"          label="&font.langGroup.bengali;"/>
              <menuitem value="zh-CN"           label="&font.langGroup.simpl-chinese;"/>
              <menuitem value="zh-HK"           label="&font.langGroup.trad-chinese-hk;"/>
              <menuitem value="zh-TW"           label="&font.langGroup.trad-chinese;"/>
              <menuitem value="x-cyrillic"      label="&font.langGroup.cyrillic;"/>
              <menuitem value="x-devanagari"    label="&font.langGroup.devanagari;"/>
              <menuitem value="x-ethi"          label="&font.langGroup.ethiopic;"/>
              <menuitem value="x-geor"          label="&font.langGroup.georgian;"/>
              <menuitem value="el"              label="&font.langGroup.el;"/>
              <menuitem value="x-gujr"          label="&font.langGroup.gujarati;"/>
              <menuitem value="x-guru"          label="&font.langGroup.gurmukhi;"/>
              <menuitem value="he"              label="&font.langGroup.hebrew;"/>
              <menuitem value="ja"              label="&font.langGroup.japanese;"/>
              <menuitem value="x-knda"          label="&font.langGroup.kannada;"/>
              <menuitem value="x-khmr"          label="&font.langGroup.khmer;"/>
              <menuitem value="ko"              label="&font.langGroup.korean;"/>
              <menuitem value="x-western"       label="&font.langGroup.latin;"/>
              <menuitem value="x-mlym"          label="&font.langGroup.malayalam;"/>
              <menuitem value="x-math"          label="&font.langGroup.math;"/>
              <menuitem value="x-orya"          label="&font.langGroup.odia;"/>
              <menuitem value="x-sinh"          label="&font.langGroup.sinhala;"/>
              <menuitem value="x-tamil"         label="&font.langGroup.tamil;"/>
              <menuitem value="x-telu"          label="&font.langGroup.telugu;"/>
              <menuitem value="th"              label="&font.langGroup.thai;"/>
              <menuitem value="x-tibt"          label="&font.langGroup.tibetan;"/>
              <menuitem value="x-cans"          label="&font.langGroup.canadian;"/>
              <menuitem value="x-unicode"       label="&font.langGroup.other;"/>
            </menupopup>
          </menulist>
        </hbox>
      </caption>

      <grid>
        <columns>
          <column/>
          <column flex="1"/>
          <column/>
          <column/>
        </columns>

        <rows>
          <row>
            <separator class="thin"/>
          </row>

          <row align="center">
            <hbox align="center" pack="end">
              <label accesskey="&proportional2.accesskey;" control="defaultFontType">&proportional2.label;</label>
            </hbox>
            <!-- This <hbox> is needed to position search tooltips correctly. -->
            <hbox>
              <menulist id="defaultFontType" flex="1" style="width: 0px;">
                <menupopup>
                  <menuitem value="serif" label="&useDefaultFontSerif.label;"/>
                  <menuitem value="sans-serif" label="&useDefaultFontSansSerif.label;"/>
                </menupopup>
              </menulist>
            </hbox>
            <hbox align="center" pack="end">
              <label accesskey="&sizeProportional.accesskey;"
                     control="sizeVar">&size2.label;</label>
            </hbox>
            <!-- This <hbox> is needed to position search tooltips correctly. -->
            <hbox>
              <menulist id="sizeVar" delayprefsave="true">
                <menupopup>
                  <menuitem value="9" label="9"/>
                  <menuitem value="10" label="10"/>
                  <menuitem value="11" label="11"/>
                  <menuitem value="12" label="12"/>
                  <menuitem value="13" label="13"/>
                  <menuitem value="14" label="14"/>
                  <menuitem value="15" label="15"/>
                  <menuitem value="16" label="16"/>
                  <menuitem value="17" label="17"/>
                  <menuitem value="18" label="18"/>
                  <menuitem value="20" label="20"/>
                  <menuitem value="22" label="22"/>
                  <menuitem value="24" label="24"/>
                  <menuitem value="26" label="26"/>
                  <menuitem value="28" label="28"/>
                  <menuitem value="30" label="30"/>
                  <menuitem value="32" label="32"/>
                  <menuitem value="34" label="34"/>
                  <menuitem value="36" label="36"/>
                  <menuitem value="40" label="40"/>
                  <menuitem value="44" label="44"/>
                  <menuitem value="48" label="48"/>
                  <menuitem value="56" label="56"/>
                  <menuitem value="64" label="64"/>
                  <menuitem value="72" label="72"/>
                </menupopup>
              </menulist>
            </hbox>
          </row>
          <row align="center">
            <hbox align="center" pack="end">
              <label accesskey="&serif2.accesskey;" control="serif">&serif2.label;</label>
            </hbox>
            <hbox>
              <menulist id="serif" flex="1" style="width: 0px;" delayprefsave="true"
                        onsyncfrompreference="return FontBuilder.readFontSelection(this);"/>
            </hbox>
            <spacer/>
          </row>
          <row align="center">
            <hbox align="center" pack="end">
              <label accesskey="&sans-serif2.accesskey;" control="sans-serif">&sans-serif2.label;</label>
            </hbox>
            <hbox>
              <menulist id="sans-serif" flex="1" style="width: 0px;" delayprefsave="true"
                        onsyncfrompreference="return FontBuilder.readFontSelection(this);"/>
            </hbox>
            <spacer/>
          </row>
          <row align="center">
            <hbox align="center" pack="end">
              <label accesskey="&monospace2.accesskey;" control="monospace">&monospace2.label;</label>
            </hbox>
            <hbox>
              <menulist id="monospace" flex="1" style="width: 0px;" crop="right" delayprefsave="true"
                        onsyncfrompreference="return FontBuilder.readFontSelection(this);"/>
            </hbox>
            <hbox align="center" pack="end">
              <label accesskey="&sizeMonospace.accesskey;"
                     control="sizeMono">&size2.label;</label>
            </hbox>
            <hbox>
              <menulist id="sizeMono" delayprefsave="true">
                <menupopup>
                  <menuitem value="9" label="9"/>
                  <menuitem value="10" label="10"/>
                  <menuitem value="11" label="11"/>
                  <menuitem value="12" label="12"/>
                  <menuitem value="13" label="13"/>
                  <menuitem value="14" label="14"/>
                  <menuitem value="15" label="15"/>
                  <menuitem value="16" label="16"/>
                  <menuitem value="17" label="17"/>
                  <menuitem value="18" label="18"/>
                  <menuitem value="20" label="20"/>
                  <menuitem value="22" label="22"/>
                  <menuitem value="24" label="24"/>
                  <menuitem value="26" label="26"/>
                  <menuitem value="28" label="28"/>
                  <menuitem value="30" label="30"/>
                  <menuitem value="32" label="32"/>
                  <menuitem value="34" label="34"/>
                  <menuitem value="36" label="36"/>
                  <menuitem value="40" label="40"/>
                  <menuitem value="44" label="44"/>
                  <menuitem value="48" label="48"/>
                  <menuitem value="56" label="56"/>
                  <menuitem value="64" label="64"/>
                  <menuitem value="72" label="72"/>
                </menupopup>
              </menulist>
            </hbox>
          </row>
        </rows>
      </grid>
      <separator class="thin"/>
      <hbox flex="1">
        <spacer flex="1"/>
        <hbox align="center" pack="end">
          <label accesskey="&minSize2.accesskey;" control="minSize">&minSize2.label;</label>
          <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
          <hbox>
            <menulist id="minSize">
              <menupopup>
                <menuitem value="0" label="&minSize.none;"/>
                <menuitem value="9" label="9"/>
                <menuitem value="10" label="10"/>
                <menuitem value="11" label="11"/>
                <menuitem value="12" label="12"/>
                <menuitem value="13" label="13"/>
                <menuitem value="14" label="14"/>
                <menuitem value="15" label="15"/>
                <menuitem value="16" label="16"/>
                <menuitem value="17" label="17"/>
                <menuitem value="18" label="18"/>
                <menuitem value="20" label="20"/>
                <menuitem value="22" label="22"/>
                <menuitem value="24" label="24"/>
                <menuitem value="26" label="26"/>
                <menuitem value="28" label="28"/>
                <menuitem value="30" label="30"/>
                <menuitem value="32" label="32"/>
                <menuitem value="34" label="34"/>
                <menuitem value="36" label="36"/>
                <menuitem value="40" label="40"/>
                <menuitem value="44" label="44"/>
                <menuitem value="48" label="48"/>
                <menuitem value="56" label="56"/>
                <menuitem value="64" label="64"/>
                <menuitem value="72" label="72"/>
              </menupopup>
            </menulist>
          </hbox>
        </hbox>
      </hbox>
      <separator/>
      <separator class="groove"/>
      <hbox>
        <checkbox id="useDocumentFonts"
                  label="&allowPagesToUseOwn.label;" accesskey="&allowPagesToUseOwn.accesskey;"
                  preference="browser.display.use_document_fonts"
                  onsyncfrompreference="return gFontsDialog.readUseDocumentFonts();"
                  onsynctopreference="return gFontsDialog.writeUseDocumentFonts();"/>
      </hbox>
    </groupbox>

    <!-- Text Encoding -->
    <groupbox>
      <caption><label>&languages.customize.Fallback2.grouplabel;</label></caption>
      <description>&languages.customize.Fallback2.desc;</description>
      <hbox align="center">
        <label accesskey="&languages.customize.Fallback3.accesskey;"
               control="DefaultCharsetList">&languages.customize.Fallback3.label;</label>
        <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
        <hbox>
          <menulist id="DefaultCharsetList" preference="intl.charset.fallback.override">
            <menupopup>
              <menuitem label="&languages.customize.Fallback.auto;"        value=""/>
              <menuitem label="&languages.customize.Fallback.arabic;"      value="windows-1256"/>
              <menuitem label="&languages.customize.Fallback.baltic;"      value="windows-1257"/>
              <menuitem label="&languages.customize.Fallback.ceiso;"       value="ISO-8859-2"/>
              <menuitem label="&languages.customize.Fallback.cewindows;"   value="windows-1250"/>
              <!-- Using gbk instead of GBK for compat with previously-stored prefs.
                  The value gets normalized in dom/encoding/FallbackEncoding.cpp. -->
              <menuitem label="&languages.customize.Fallback.simplified;"  value="gbk"/>
              <menuitem label="&languages.customize.Fallback.traditional;" value="Big5"/>
              <menuitem label="&languages.customize.Fallback.cyrillic;"    value="windows-1251"/>
              <menuitem label="&languages.customize.Fallback.greek;"       value="ISO-8859-7"/>
              <menuitem label="&languages.customize.Fallback.hebrew;"      value="windows-1255"/>
              <menuitem label="&languages.customize.Fallback.japanese;"    value="Shift_JIS"/>
              <menuitem label="&languages.customize.Fallback.korean;"      value="EUC-KR"/>
              <menuitem label="&languages.customize.Fallback.thai;"        value="windows-874"/>
              <menuitem label="&languages.customize.Fallback.turkish;"     value="windows-1254"/>
              <menuitem label="&languages.customize.Fallback.vietnamese;"  value="windows-1258"/>
              <menuitem label="&languages.customize.Fallback.other;"       value="windows-1252"/>
            </menupopup>
          </menulist>
        </hbox>
      </hbox>
    </groupbox>
  </vbox>
</dialog>
PK
!<�/��7chrome/browser/content/browser/preferences/handlers.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#handlersView > richlistitem {
  -moz-binding: url("chrome://browser/content/preferences/handlers.xml#handler");
}

#containersView > richlistitem {
  -moz-binding: url("chrome://browser/content/preferences/handlers.xml#container");
}

/**
 * Make the icons appear.
 * Note: we display the icon box for every item whether or not it has an icon
 * so the labels of all the items align vertically.
 */
.actionsMenu > menupopup > menuitem > .menu-iconic-left {
  display: -moz-box;
  min-width: 16px;
}

listitem.offlineapp {
  -moz-binding: url("chrome://browser/content/preferences/handlers.xml#offlineapp");
}

/* Apply crisp rendering for favicons at exactly 2dppx resolution */
@media (resolution: 2dppx) {
  #handlersView > richlistitem,
  .actionsMenu > menupopup > menuitem > .menu-iconic-left {
    image-rendering: -moz-crisp-edges;
  }
}
PK
!<�l���7chrome/browser/content/browser/preferences/handlers.xml<?xml version="1.0"?>

<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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-globals-from in-content/main.js -->

<!DOCTYPE overlay [
  <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
  <!ENTITY % containersDTD SYSTEM "chrome://browser/locale/preferences/containers.dtd">
  %brandDTD;
  %containersDTD;
]>

<bindings id="handlerBindings"
          xmlns="http://www.mozilla.org/xbl"
          xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
          xmlns:xbl="http://www.mozilla.org/xbl">

  <binding id="handler" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
    <implementation>
      <property name="type" readonly="true">
        <getter>
          return this.getAttribute("type");
        </getter>
      </property>
      <!-- Overriding listitem -->
      <property name="selected" onget="return this.getAttribute('selected') == 'true';">
        <setter><![CDATA[
          if (val) {
            this.setAttribute("selected", "true");
            gMainPane.rebuildActionsMenu();
          } else {
            this.removeAttribute("selected");
          }

          document.getAnonymousElementByAttribute(this, "anonid", "selected").setAttribute("hidden", !val);
          document.getAnonymousElementByAttribute(this, "anonid", "not-selected").setAttribute("hidden", !!val);

          return val;
        ]]></setter>
      </property>
    </implementation>
    <content>
      <xul:hbox flex="1" equalsize="always">
        <xul:hbox flex="1" align="center" xbl:inherits="tooltiptext=typeDescription">
          <xul:image src="moz-icon://goat?size=16" class="typeIcon"
                     xbl:inherits="src=typeIcon" height="16" width="16"/>
          <xul:label flex="1" crop="end" xbl:inherits="value=typeDescription"/>
        </xul:hbox>
        <xul:hbox anonid="not-selected" flex="1" align="center" xbl:inherits="tooltiptext=actionDescription">
          <xul:image xbl:inherits="src=actionIcon" height="16" width="16" class="actionIcon"/>
          <xul:label flex="1" crop="end" xbl:inherits="value=actionDescription"/>
        </xul:hbox>
        <xul:hbox hidden="true" anonid="selected" flex="1">
          <xul:menulist class="actionsMenu" flex="1" crop="end" selectedIndex="1"
                        xbl:inherits="tooltiptext=actionDescription"
                        oncommand="gMainPane.onSelectAction(event.originalTarget)">
            <xul:menupopup/>
          </xul:menulist>
        </xul:hbox>
      </xul:hbox>
    </content>
  </binding>

  <binding id="container">
    <content>
      <xul:hbox flex="1" equalsize="always">
        <xul:hbox flex="1" align="center">
          <xul:hbox xbl:inherits="data-identity-icon=containerIcon,data-identity-color=containerColor" height="24" width="24" class="userContext-icon"/>
          <xul:label flex="1" crop="end" xbl:inherits="xbl:text=containerName,highlightable"/>
        </xul:hbox>
        <xul:hbox flex="1" align="right">
          <xul:button anonid="preferencesButton"
                      label="&preferencesButton.label;"
                      xbl:inherits="value=userContextId"
                      oncommand="gContainersPane.onPreferenceCommand(event.originalTarget)">
          </xul:button>
          <xul:button anonid="removeButton"
                      label="&removeButton.label;"
                      xbl:inherits="value=userContextId"
                      oncommand="gContainersPane.onRemoveCommand(event.originalTarget)">
          </xul:button>
        </xul:hbox>
      </xul:hbox>
    </content>
  </binding>

  <binding id="offlineapp"
	   extends="chrome://global/content/bindings/listbox.xml#listitem">
    <content>
      <children>
	<xul:listcell xbl:inherits="label=origin"/>
	<xul:listcell xbl:inherits="label=usage"/>
      </children>
    </content>
  </binding>

</bindings>
PK
!<Q���..Cchrome/browser/content/browser/preferences/in-content/containers.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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-globals-from preferences.js */

ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
ChromeUtils.import("resource://gre/modules/ContextualIdentityService.jsm");

const containersBundle = Services.strings.createBundle("chrome://browser/locale/preferences/containers.properties");

const defaultContainerIcon = "fingerprint";
const defaultContainerColor = "blue";

let gContainersPane = {

  init() {
    this._list = document.getElementById("containersView");

    document.getElementById("backContainersLink").addEventListener("click", function(event) {
      if (event.button == 0) {
        gotoPref("general");
      }
    });

    this._rebuildView();
  },

  _rebuildView() {
    const containers = ContextualIdentityService.getPublicIdentities();
    while (this._list.firstChild) {
      this._list.firstChild.remove();
    }
    for (let container of containers) {
      let item = document.createElement("richlistitem");
      item.setAttribute("containerName", ContextualIdentityService.getUserContextLabel(container.userContextId));
      item.setAttribute("containerIcon", container.icon);
      item.setAttribute("containerColor", container.color);
      item.setAttribute("userContextId", container.userContextId);

      this._list.appendChild(item);
    }
  },

  async onRemoveCommand(button) {
    let userContextId = parseInt(button.getAttribute("value"), 10);

    let count = ContextualIdentityService.countContainerTabs(userContextId);
    if (count > 0) {
      let bundlePreferences = document.getElementById("bundlePreferences");

      let title = bundlePreferences.getString("removeContainerAlertTitle");
      let message = PluralForm.get(count, bundlePreferences.getString("removeContainerMsg"))
                              .replace("#S", count);
      let okButton = bundlePreferences.getString("removeContainerOkButton");
      let cancelButton = bundlePreferences.getString("removeContainerButton2");

      let buttonFlags = (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_0) +
                        (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_1);

      let rv = Services.prompt.confirmEx(window, title, message, buttonFlags,
                                         okButton, cancelButton, null, null, {});
      if (rv != 0) {
        return;
      }

      await ContextualIdentityService.closeContainerTabs(userContextId);
    }

    ContextualIdentityService.remove(userContextId);
    this._rebuildView();
  },

  onPreferenceCommand(button) {
    this.openPreferenceDialog(button.getAttribute("value"));
  },

  onAddButtonCommand(button) {
    this.openPreferenceDialog(null);
  },

  openPreferenceDialog(userContextId) {
    let identity = {
      name: "",
      icon: defaultContainerIcon,
      color: defaultContainerColor
    };
    let title;
    if (userContextId) {
      identity = ContextualIdentityService.getPublicIdentityFromId(userContextId);
      // This is required to get the translation string from defaults
      identity.name = ContextualIdentityService.getUserContextLabel(identity.userContextId);
      title = containersBundle.formatStringFromName("containers.updateContainerTitle", [identity.name], 1);
    }

    const params = { userContextId, identity, windowTitle: title };
    gSubDialog.open("chrome://browser/content/preferences/containers.xul",
                     null, params);
  }

};
PK
!<��^��Lchrome/browser/content/browser/preferences/in-content/extensionControlled.js/* - This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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-globals-from preferences.js */

"use strict";

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");

ChromeUtils.defineModuleGetter(this, "AddonManager",
                                  "resource://gre/modules/AddonManager.jsm");
ChromeUtils.defineModuleGetter(this, "BrowserUtils",
                                  "resource://gre/modules/BrowserUtils.jsm");
ChromeUtils.defineModuleGetter(this, "DeferredTask",
                                  "resource://gre/modules/DeferredTask.jsm");
ChromeUtils.defineModuleGetter(this, "ExtensionSettingsStore",
                                  "resource://gre/modules/ExtensionSettingsStore.jsm");

XPCOMUtils.defineLazyPreferenceGetter(this, "trackingprotectionUiEnabled",
                                      "privacy.trackingprotection.ui.enabled");

const PREF_SETTING_TYPE = "prefs";
const PROXY_KEY = "proxy.settings";
const API_PROXY_PREFS = [
  "network.proxy.type",
  "network.proxy.http",
  "network.proxy.http_port",
  "network.proxy.share_proxy_settings",
  "network.proxy.ftp",
  "network.proxy.ftp_port",
  "network.proxy.ssl",
  "network.proxy.ssl_port",
  "network.proxy.socks",
  "network.proxy.socks_port",
  "network.proxy.socks_version",
  "network.proxy.socks_remote_dns",
  "network.proxy.no_proxies_on",
  "network.proxy.autoconfig_url",
  "signon.autologin.proxy",
];

let extensionControlledContentIds = {
  "privacy.containers": "browserContainersExtensionContent",
  "homepage_override": "browserHomePageExtensionContent",
  "newTabURL": "browserNewTabExtensionContent",
  "defaultSearch": "browserDefaultSearchExtensionContent",
  "proxy.settings": "proxyExtensionContent",
  get "websites.trackingProtectionMode"() {
    return {
      button: "trackingProtectionExtensionContentButton",
      section:
        trackingprotectionUiEnabled ?
          "trackingProtectionExtensionContentLabel" :
          "trackingProtectionPBMExtensionContentLabel",
    };
  }
};

function getExtensionControlledArgs(settingName) {
  switch (settingName) {
    case "proxy.settings":
      return [document.getElementById("bundleBrand").getString("brandShortName")];
    default:
      return [];
  }
}

let extensionControlledIds = {};

/**
  * Check if a pref is being managed by an extension.
  */
async function getControllingExtensionInfo(type, settingName) {
  await ExtensionSettingsStore.initialize();
  return ExtensionSettingsStore.getSetting(type, settingName);
}

function getControllingExtensionEls(settingName) {
  let idInfo = extensionControlledContentIds[settingName];
  let section = document.getElementById(idInfo.section || idInfo);
  let button = idInfo.button ?
    document.getElementById(idInfo.button) :
    section.querySelector("button");
  return {
    section,
    button,
    description: section.querySelector("description"),
  };
}

async function getControllingExtension(type, settingName) {
  let info = await getControllingExtensionInfo(type, settingName);
  let addon = info && info.id
    && await AddonManager.getAddonByID(info.id);
  return addon;
}

async function handleControllingExtension(type, settingName, stringId) {
  let addon = await getControllingExtension(type, settingName);

  // Sometimes the ExtensionSettingsStore gets in a bad state where it thinks
  // an extension is controlling a setting but the extension has been uninstalled
  // outside of the regular lifecycle. If the extension isn't currently installed
  // then we should treat the setting as not being controlled.
  // See https://bugzilla.mozilla.org/show_bug.cgi?id=1411046 for an example.
  if (addon) {
    extensionControlledIds[settingName] = addon.id;
    showControllingExtension(settingName, addon, stringId);
  } else {
    let elements = getControllingExtensionEls(settingName);
    if (extensionControlledIds[settingName]
        && !document.hidden
        && elements.button) {
      showEnableExtensionMessage(settingName);
    } else {
      hideControllingExtension(settingName);
    }
    delete extensionControlledIds[settingName];
  }

  return !!addon;
}

function getControllingExtensionFragment(stringId, addon, ...extraArgs) {
  let msg = document.getElementById("bundlePreferences").getString(stringId);
  let image = document.createElement("image");
  const defaultIcon = "chrome://mozapps/skin/extensions/extensionGeneric.svg";
  image.setAttribute("src", addon.iconURL || defaultIcon);
  image.classList.add("extension-controlled-icon");
  let addonBit = document.createDocumentFragment();
  addonBit.appendChild(image);
  addonBit.appendChild(document.createTextNode(" " + addon.name));
  return BrowserUtils.getLocalizedFragment(document, msg, addonBit, ...extraArgs);
}

async function showControllingExtension(
  settingName, addon, stringId = `extensionControlled.${settingName}`) {
  // Tell the user what extension is controlling the setting.
  let elements = getControllingExtensionEls(settingName);
  let extraArgs = getExtensionControlledArgs(settingName);

  elements.section.classList.remove("extension-controlled-disabled");
  let description = elements.description;

  // Remove the old content from the description.
  while (description.firstChild) {
    description.firstChild.remove();
  }

  let fragment = getControllingExtensionFragment(
    stringId, addon, ...extraArgs);
  description.appendChild(fragment);

  if (elements.button) {
    elements.button.hidden = false;
  }

  // Show the controlling extension row and hide the old label.
  elements.section.hidden = false;
}

function hideControllingExtension(settingName) {
  let elements = getControllingExtensionEls(settingName);
  elements.section.hidden = true;
  if (elements.button) {
    elements.button.hidden = true;
  }
}

function showEnableExtensionMessage(settingName) {
  let elements = getControllingExtensionEls(settingName);

  elements.button.hidden = true;
  elements.section.classList.add("extension-controlled-disabled");
  let icon = url => {
    let img = document.createElement("image");
    img.src = url;
    img.className = "extension-controlled-icon";
    return img;
  };
  let addonIcon = icon("chrome://mozapps/skin/extensions/extensionGeneric-16.svg");
  let toolbarIcon = icon("chrome://browser/skin/menu.svg");
  let message = document.getElementById("bundlePreferences")
                        .getString("extensionControlled.enable");
  let frag = BrowserUtils.getLocalizedFragment(document, message, addonIcon, toolbarIcon);
  elements.description.innerHTML = "";
  elements.description.appendChild(frag);
  let dismissButton = document.createElement("image");
  dismissButton.setAttribute("class", "extension-controlled-icon close-icon");
  dismissButton.addEventListener("click", function dismissHandler() {
    hideControllingExtension(settingName);
    dismissButton.removeEventListener("click", dismissHandler);
  });
  elements.description.appendChild(dismissButton);
}

function makeDisableControllingExtension(type, settingName) {
  return async function disableExtension() {
    let {id} = await getControllingExtensionInfo(type, settingName);
    let addon = await AddonManager.getAddonByID(id);
    addon.userDisabled = true;
  };
}

function initializeProxyUI(container) {
  let deferredUpdate = new DeferredTask(() => {
    container.updateProxySettingsUI();
  }, 10);
  let proxyObserver = {
    observe: (subject, topic, data) => {
      if (API_PROXY_PREFS.includes(data)) {
        deferredUpdate.arm();
      }
    },
  };
  Services.prefs.addObserver("", proxyObserver);
  window.addEventListener("unload", () => {
    Services.prefs.removeObserver("", proxyObserver);
  });
}
PK
!<��fLLCchrome/browser/content/browser/preferences/in-content/findInPage.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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-globals-from extensionControlled.js */
/* import-globals-from preferences.js */

var gSearchResultsPane = {
  listSearchTooltips: new Set(),
  listSearchMenuitemIndicators: new Set(),
  searchInput: null,
  inited: false,

  init() {
    if (this.inited) {
      return;
    }
    this.inited = true;
    this.searchInput = document.getElementById("searchInput");
    this.searchInput.hidden = !Services.prefs.getBoolPref("browser.preferences.search");
    if (!this.searchInput.hidden) {
      this.searchInput.addEventListener("input", this);
      this.searchInput.addEventListener("command", this);
      window.addEventListener("DOMContentLoaded", () => {
        this.searchInput.focus();
      });
      // Initialize other panes in an idle callback.
      window.requestIdleCallback(() => this.initializeCategories());
    }
    let strings = this.strings;
    this.searchInput.placeholder = AppConstants.platform == "win" ?
      strings.getString("searchInput.labelWin") :
      strings.getString("searchInput.labelUnix");
  },

  handleEvent(event) {
    // Ensure categories are initialized if idle callback didn't run sooo enough.
    this.initializeCategories();
    this.searchFunction(event);
  },

  /**
   * Check that the text content contains the query string.
   *
   * @param String content
   *    the text content to be searched
   * @param String query
   *    the query string
   * @returns boolean
   *    true when the text content contains the query string else false
   */
  queryMatchesContent(content, query) {
    if (!content || !query) {
      return false;
    }
    return content.toLowerCase().includes(query.toLowerCase());
  },

  categoriesInitialized: false,

  /**
   * Will attempt to initialize all uninitialized categories
   */
  initializeCategories() {
    //  Initializing all the JS for all the tabs
    if (!this.categoriesInitialized) {
      this.categoriesInitialized = true;
      // Each element of gCategoryInits is a name
      for (let [/* name */, category] of gCategoryInits) {
        if (!category.inited) {
          category.init();
        }
      }
    }
  },

  /**
   * Finds and returns text nodes within node and all descendants
   * Iterates through all the sibilings of the node object and adds the sibilings
   * to an array if sibiling is a TEXT_NODE else checks the text nodes with in current node
   * Source - http://stackoverflow.com/questions/10730309/find-all-text-nodes-in-html-page
   *
   * @param Node nodeObject
   *    DOM element
   * @returns array of text nodes
   */
  textNodeDescendants(node) {
    if (!node) {
      return [];
    }
    let all = [];
    for (node = node.firstChild; node; node = node.nextSibling) {
      if (node.nodeType === node.TEXT_NODE) {
        all.push(node);
      } else {
        all = all.concat(this.textNodeDescendants(node));
      }
    }
    return all;
  },

  /**
   * This function is used to find words contained within the text nodes.
   * We pass in the textNodes because they contain the text to be highlighted.
   * We pass in the nodeSizes to tell exactly where highlighting need be done.
   * When creating the range for highlighting, if the nodes are section is split
   * by an access key, it is important to have the size of each of the nodes summed.
   * @param Array textNodes
   *    List of DOM elements
   * @param Array nodeSizes
   *    Running size of text nodes. This will contain the same number of elements as textNodes.
   *    The first element is the size of first textNode element.
   *    For any nodes after, they will contain the summation of the nodes thus far in the array.
   *    Example:
   *    textNodes = [[This is ], [a], [n example]]
   *    nodeSizes = [[8], [9], [18]]
   *    This is used to determine the offset when highlighting
   * @param String textSearch
   *    Concatination of textNodes's text content
   *    Example:
   *    textNodes = [[This is ], [a], [n example]]
   *    nodeSizes = "This is an example"
   *    This is used when executing the regular expression
   * @param String searchPhrase
   *    word or words to search for
   * @returns boolean
   *      Returns true when atleast one instance of search phrase is found, otherwise false
   */
  highlightMatches(textNodes, nodeSizes, textSearch, searchPhrase) {
    if (!searchPhrase) {
      return false;
    }

    let indices = [];
    let i = -1;
    while ((i = textSearch.indexOf(searchPhrase, i + 1)) >= 0) {
      indices.push(i);
    }

    // Looping through each spot the searchPhrase is found in the concatenated string
    for (let startValue of indices) {
      let endValue = startValue + searchPhrase.length;
      let startNode = null;
      let endNode = null;
      let nodeStartIndex = null;

      // Determining the start and end node to highlight from
      for (let index = 0; index < nodeSizes.length; index++) {
        let lengthNodes = nodeSizes[index];
        // Determining the start node
        if (!startNode && lengthNodes >= startValue) {
          startNode = textNodes[index];
          nodeStartIndex = index;
          // Calculating the offset when found query is not in the first node
          if (index > 0) {
            startValue -= nodeSizes[index - 1];
          }
        }
        // Determining the end node
        if (!endNode && lengthNodes >= endValue) {
          endNode = textNodes[index];
          // Calculating the offset when endNode is different from startNode
          // or when endNode is not the first node
          if (index != nodeStartIndex || index > 0 ) {
            endValue -= nodeSizes[index - 1];
          }
        }
      }
      let range = document.createRange();
      range.setStart(startNode, startValue);
      range.setEnd(endNode, endValue);
      this.getFindSelection(startNode.ownerGlobal).addRange(range);
    }

    return indices.length > 0;
  },

  /**
   * Get the selection instance from given window
   *
   * @param Object win
   *   The window object points to frame's window
   */
  getFindSelection(win) {
    // Yuck. See bug 138068.
    let docShell = win.QueryInterface(Ci.nsIInterfaceRequestor)
                      .getInterface(Ci.nsIWebNavigation)
                      .QueryInterface(Ci.nsIDocShell);

    let controller = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                              .getInterface(Ci.nsISelectionDisplay)
                              .QueryInterface(Ci.nsISelectionController);

    let selection = controller.getSelection(Ci.nsISelectionController.SELECTION_FIND);
    selection.setColors("currentColor", "#ffe900", "currentColor", "#003eaa");

    return selection;
  },

  get strings() {
    delete this.strings;
    return this.strings = document.getElementById("searchResultBundle");
  },

  /**
   * Shows or hides content according to search input
   *
   * @param String event
   *    to search for filted query in
   */
  async searchFunction(event) {
    let query = event.target.value.trim().toLowerCase();
    if (this.query == query) {
      return;
    }

    let subQuery = this.query && query.includes(this.query);
    this.query = query;

    this.getFindSelection(window).removeAllRanges();
    this.removeAllSearchTooltips();
    this.removeAllSearchMenuitemIndicators();

    // Clear telemetry request if user types very frequently.
    if (this.telemetryTimer) {
      clearTimeout(this.telemetryTimer);
    }

    let srHeader = document.getElementById("header-searchResults");

    if (this.query) {
      // Showing the Search Results Tag
      gotoPref("paneSearchResults");

      let resultsFound = false;

      // Building the range for highlighted areas
      let rootPreferencesChildren = document
        .querySelectorAll("#mainPrefPane > *:not([data-hidden-from-search])");

      // Show all second level headers in search result
      for (let element of document.querySelectorAll("caption.search-header")) {
        element.hidden = false;
      }

      if (subQuery) {
        // Since the previous query is a subset of the current query,
        // there is no need to check elements that is hidden already.
        rootPreferencesChildren =
          Array.prototype.filter.call(rootPreferencesChildren, el => !el.hidden);
      }

      // Mark all the children to check be visible to bind JS, Access Keys, etc,
      // but don't really show them by setting their visibility to hidden in CSS.
      for (let i = 0; i < rootPreferencesChildren.length; i++) {
        rootPreferencesChildren[i].hidden = false;
        rootPreferencesChildren[i].classList.add("visually-hidden");
      }

      let ts = performance.now();
      let FRAME_THRESHOLD = 1000 / 60;

      // Showing or Hiding specific section depending on if words in query are found
      for (let i = 0; i < rootPreferencesChildren.length; i++) {
        if (performance.now() - ts > FRAME_THRESHOLD) {
          // Creating tooltips for all the instances found
          for (let anchorNode of this.listSearchTooltips) {
            this.createSearchTooltip(anchorNode, this.query);
          }
          // It hides Search Results header so turning it on
          srHeader.hidden = false;
          srHeader.classList.remove("visually-hidden");
          ts = await new Promise(resolve => window.requestAnimationFrame(resolve));
          if (query !== this.query) {
            return;
          }
        }

        rootPreferencesChildren[i].classList.remove("visually-hidden");
        if (!rootPreferencesChildren[i].classList.contains("header") &&
            !rootPreferencesChildren[i].classList.contains("subcategory") &&
            !rootPreferencesChildren[i].classList.contains("no-results-message") &&
            this.searchWithinNode(rootPreferencesChildren[i], this.query)) {
          rootPreferencesChildren[i].hidden = false;
          resultsFound = true;
        } else {
          rootPreferencesChildren[i].hidden = true;
        }
      }
      // It hides Search Results header so turning it on
      srHeader.hidden = false;
      srHeader.classList.remove("visually-hidden");

      if (!resultsFound) {
        let noResultsEl = document.querySelector(".no-results-message");
        noResultsEl.setAttribute("query", this.query);
        noResultsEl.hidden = false;

        let strings = this.strings;

        document.getElementById("sorry-message").textContent = AppConstants.platform == "win" ?
          strings.getFormattedString("searchResults.sorryMessageWin", [this.query]) :
          strings.getFormattedString("searchResults.sorryMessageUnix", [this.query]);
        let helpUrl = Services.urlFormatter.formatURLPref("app.support.baseURL") + "preferences";
        let brandName = document.getElementById("bundleBrand").getString("brandShortName");
        let helpString = strings.getString("searchResults.needHelp3");
        let helpContainer = document.getElementById("need-help");
        let link = document.createElement("label");
        link.className = "text-link";
        link.setAttribute("href", helpUrl);
        link.textContent = strings.getFormattedString("searchResults.needHelpSupportLink", [brandName]);

        helpContainer.innerHTML = "";
        let fragment = BrowserUtils.getLocalizedFragment(document, helpString, link);
        helpContainer.appendChild(fragment);
      } else {
        // Creating tooltips for all the instances found
        for (let anchorNode of this.listSearchTooltips) {
          this.createSearchTooltip(anchorNode, this.query);
        }

        // Implant search telemetry probe after user stops typing for a while
        if (this.query.length >= 2) {
          this.telemetryTimer = setTimeout(() => {
            Services.telemetry.keyedScalarAdd("preferences.search_query", this.query, 1);
          }, 1000);
        }
      }
    } else {
      document.getElementById("sorry-message").textContent = "";
      // Going back to General when cleared
      gotoPref("paneGeneral");

      // Hide some special second level headers in normal view
      for (let element of document.querySelectorAll("caption.search-header")) {
        element.hidden = true;
      }
    }

    window.dispatchEvent(new CustomEvent("PreferencesSearchCompleted", { detail: query }));
  },

  /**
   * Finding leaf nodes and checking their content for words to search,
   * It is a recursive function
   *
   * @param Node nodeObject
   *    DOM Element
   * @param String searchPhrase
   * @returns boolean
   *    Returns true when found in at least one childNode, false otherwise
   */
  searchWithinNode(nodeObject, searchPhrase) {
    let matchesFound = false;
    if (nodeObject.childElementCount == 0 ||
        nodeObject.tagName == "label" ||
        nodeObject.tagName == "description" ||
        nodeObject.tagName == "menulist") {
      let simpleTextNodes = this.textNodeDescendants(nodeObject);
      for (let node of simpleTextNodes) {
        let result = this.highlightMatches([node], [node.length], node.textContent.toLowerCase(), searchPhrase);
        matchesFound = matchesFound || result;
      }

      // Collecting data from boxObject / label / description
      let nodeSizes = [];
      let allNodeText = "";
      let runningSize = 0;
      let accessKeyTextNodes = this.textNodeDescendants(nodeObject.boxObject);

      if (nodeObject.tagName == "label" || nodeObject.tagName == "description") {
        accessKeyTextNodes.push(...this.textNodeDescendants(nodeObject));
      }

      for (let node of accessKeyTextNodes) {
        runningSize += node.textContent.length;
        allNodeText += node.textContent;
        nodeSizes.push(runningSize);
      }

      // Access key are presented
      let complexTextNodesResult = this.highlightMatches(accessKeyTextNodes, nodeSizes, allNodeText.toLowerCase(), searchPhrase);

      // Searching some elements, such as xul:button, have a 'label' attribute that contains the user-visible text.
      let labelResult = this.queryMatchesContent(nodeObject.getAttribute("label"), searchPhrase);

      // Searching some elements, such as xul:label, store their user-visible text in a "value" attribute.
      // Value will be skipped for menuitem since value in menuitem could represent index number to distinct each item.
      let valueResult = nodeObject.tagName !== "menuitem" ?
        this.queryMatchesContent(nodeObject.getAttribute("value"), searchPhrase) : false;

      // Searching some elements, such as xul:button, buttons to open subdialogs.
      let keywordsResult = this.queryMatchesContent(nodeObject.getAttribute("searchkeywords"), searchPhrase);

      // Creating tooltips for buttons
      if (keywordsResult && (nodeObject.tagName === "button" || nodeObject.tagName == "menulist")) {
        this.listSearchTooltips.add(nodeObject);
      }

      if (keywordsResult && nodeObject.tagName === "menuitem") {
        nodeObject.setAttribute("indicator", "true");
        this.listSearchMenuitemIndicators.add(nodeObject);
        let menulist = nodeObject.closest("menulist");

        menulist.setAttribute("indicator", "true");
        this.listSearchMenuitemIndicators.add(menulist);
      }

      if ((nodeObject.tagName == "button" ||
           nodeObject.tagName == "menulist" ||
           nodeObject.tagName == "menuitem") &&
           (labelResult || valueResult || keywordsResult)) {
        nodeObject.setAttribute("highlightable", "true");
      }

      matchesFound = matchesFound || complexTextNodesResult || labelResult || valueResult || keywordsResult;
    }

    // Should not search unselected child nodes of a <xul:deck> element
    // except the "historyPane" <xul:deck> element.
    if (nodeObject.tagName == "deck" && nodeObject.id != "historyPane") {
      let index = nodeObject.selectedIndex;
      if (index != -1) {
        let result = this.searchChildNodeIfVisible(nodeObject, index, searchPhrase);
        matchesFound = matchesFound || result;
      }
    } else {
      for (let i = 0; i < nodeObject.childNodes.length; i++) {
        let result = this.searchChildNodeIfVisible(nodeObject, i, searchPhrase);
        matchesFound = matchesFound || result;
      }
    }
    return matchesFound;
  },

  /**
   * Search for a phrase within a child node if it is visible.
   *
   * @param Node nodeObject
   *    The parent DOM Element
   * @param Number index
   *    The index for the childNode
   * @param String searchPhrase
   * @returns boolean
   *    Returns true when found the specific childNode, false otherwise
   */
  searchChildNodeIfVisible(nodeObject, index, searchPhrase) {
    let result = false;
    if (!nodeObject.childNodes[index].hidden && nodeObject.getAttribute("data-hidden-from-search") !== "true") {
      result = this.searchWithinNode(nodeObject.childNodes[index], searchPhrase);
      // Creating tooltips for menulist element
      if (result && nodeObject.tagName === "menulist") {
        this.listSearchTooltips.add(nodeObject);
      }
    }
    return result;
  },

  /**
   * Inserting a div structure infront of the DOM element matched textContent.
   * Then calculation the offsets to position the tooltip in the correct place.
   *
   * @param Node anchorNode
   *    DOM Element
   * @param String query
   *    Word or words that are being searched for
   */
  createSearchTooltip(anchorNode, query) {
    if (anchorNode.tooltipNode) {
      return;
    }
    let searchTooltip = anchorNode.ownerDocument.createElement("span");
    let searchTooltipText = anchorNode.ownerDocument.createElement("span");
    searchTooltip.setAttribute("class", "search-tooltip");
    searchTooltipText.textContent = query;
    searchTooltip.appendChild(searchTooltipText);

    // Set tooltipNode property to track corresponded tooltip node.
    anchorNode.tooltipNode = searchTooltip;
    anchorNode.parentElement.classList.add("search-tooltip-parent");
    anchorNode.parentElement.appendChild(searchTooltip);

    this.calculateTooltipPosition(anchorNode);
  },

  calculateTooltipPosition(anchorNode) {
    let searchTooltip = anchorNode.tooltipNode;
    // In order to get the up-to-date position of each of the nodes that we're
    // putting tooltips on, we have to flush layout intentionally, and that
    // this is the result of a XUL limitation (bug 1363730).
    let tooltipRect = searchTooltip.getBoundingClientRect();
    searchTooltip.style.setProperty("left", `calc(50% - ${(tooltipRect.width / 2)}px)`);
  },

  /**
   * Remove all search tooltips that were created.
   */
  removeAllSearchTooltips() {
    let searchTooltips = Array.from(document.querySelectorAll(".search-tooltip"));
    for (let searchTooltip of searchTooltips) {
      searchTooltip.parentElement.classList.remove("search-tooltip-parent");
      searchTooltip.remove();
    }
    for (let anchorNode of this.listSearchTooltips) {
      anchorNode.tooltipNode.remove();
      anchorNode.tooltipNode = null;
    }
    this.listSearchTooltips.clear();
  },

  /**
   * Remove all indicators on menuitem.
   */
  removeAllSearchMenuitemIndicators() {
    for (let node of this.listSearchMenuitemIndicators) {
      node.removeAttribute("indicator");
    }
    this.listSearchMenuitemIndicators.clear();
  }
};
PK
!<n���AA=chrome/browser/content/browser/preferences/in-content/main.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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-globals-from extensionControlled.js */
/* import-globals-from preferences.js */
/* import-globals-from ../../../../toolkit/mozapps/preferences/fontbuilder.js */
/* import-globals-from ../../../base/content/aboutDialog-appUpdater.js */

ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/Downloads.jsm");
ChromeUtils.import("resource://gre/modules/FileUtils.jsm");
ChromeUtils.import("resource:///modules/ShellService.jsm");
ChromeUtils.import("resource:///modules/TransientPrefs.jsm");
ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
ChromeUtils.import("resource://gre/modules/DownloadUtils.jsm");
ChromeUtils.defineModuleGetter(this, "CloudStorage",
  "resource://gre/modules/CloudStorage.jsm");

// Constants & Enumeration Values
const TYPE_MAYBE_FEED = "application/vnd.mozilla.maybe.feed";
const TYPE_MAYBE_VIDEO_FEED = "application/vnd.mozilla.maybe.video.feed";
const TYPE_MAYBE_AUDIO_FEED = "application/vnd.mozilla.maybe.audio.feed";
const TYPE_PDF = "application/pdf";

const PREF_PDFJS_DISABLED = "pdfjs.disabled";
const TOPIC_PDFJS_HANDLER_CHANGED = "pdfjs:handlerChanged";

const PREF_DISABLED_PLUGIN_TYPES = "plugin.disable_full_page_plugin_for_types";

// Pref for when containers is being controlled
const PREF_CONTAINERS_EXTENSION = "privacy.userContext.extension";

// Preferences that affect which entries to show in the list.
const PREF_SHOW_PLUGINS_IN_LIST = "browser.download.show_plugins_in_list";
const PREF_HIDE_PLUGINS_WITHOUT_EXTENSIONS =
  "browser.download.hide_plugins_without_extensions";

// Strings to identify ExtensionSettingsStore overrides
const CONTAINERS_KEY = "privacy.containers";
const HOMEPAGE_OVERRIDE_KEY = "homepage_override";
const URL_OVERRIDES_TYPE = "url_overrides";
const NEW_TAB_KEY = "newTabURL";
const NEW_TAB_STRING_ID = "extensionControlled.newTabURL2";

/*
 * Preferences where we store handling information about the feed type.
 *
 * browser.feeds.handler
 * - "bookmarks", "reader" (clarified further using the .default preference),
 *   or "ask" -- indicates the default handler being used to process feeds;
 *   "bookmarks" is obsolete; to specify that the handler is bookmarks,
 *   set browser.feeds.handler.default to "bookmarks";
 *
 * browser.feeds.handler.default
 * - "bookmarks", "client" or "web" -- indicates the chosen feed reader used
 *   to display feeds, either transiently (i.e., when the "use as default"
 *   checkbox is unchecked, corresponds to when browser.feeds.handler=="ask")
 *   or more permanently (i.e., the item displayed in the dropdown in Feeds
 *   preferences)
 *
 * browser.feeds.handler.webservice
 * - the URL of the currently selected web service used to read feeds
 *
 * browser.feeds.handlers.application
 * - nsIFile, stores the current client-side feed reading app if one has
 *   been chosen
 */
const PREF_FEED_SELECTED_APP = "browser.feeds.handlers.application";
const PREF_FEED_SELECTED_WEB = "browser.feeds.handlers.webservice";
const PREF_FEED_SELECTED_ACTION = "browser.feeds.handler";
const PREF_FEED_SELECTED_READER = "browser.feeds.handler.default";

const PREF_VIDEO_FEED_SELECTED_APP = "browser.videoFeeds.handlers.application";
const PREF_VIDEO_FEED_SELECTED_WEB = "browser.videoFeeds.handlers.webservice";
const PREF_VIDEO_FEED_SELECTED_ACTION = "browser.videoFeeds.handler";
const PREF_VIDEO_FEED_SELECTED_READER = "browser.videoFeeds.handler.default";

const PREF_AUDIO_FEED_SELECTED_APP = "browser.audioFeeds.handlers.application";
const PREF_AUDIO_FEED_SELECTED_WEB = "browser.audioFeeds.handlers.webservice";
const PREF_AUDIO_FEED_SELECTED_ACTION = "browser.audioFeeds.handler";
const PREF_AUDIO_FEED_SELECTED_READER = "browser.audioFeeds.handler.default";

// The nsHandlerInfoAction enumeration values in nsIHandlerInfo identify
// the actions the application can take with content of various types.
// But since nsIHandlerInfo doesn't support plugins, there's no value
// identifying the "use plugin" action, so we use this constant instead.
const kActionUsePlugin = 5;

const ICON_URL_APP = AppConstants.platform == "linux" ?
  "moz-icon://dummy.exe?size=16" :
  "chrome://browser/skin/preferences/application.png";

// For CSS. Can be one of "ask", "save", "plugin" or "feed". If absent, the icon URL
// was set by us to a custom handler icon and CSS should not try to override it.
const APP_ICON_ATTR_NAME = "appHandlerIcon";

ChromeUtils.defineModuleGetter(this, "OS",
  "resource://gre/modules/osfile.jsm");

if (AppConstants.MOZ_DEV_EDITION) {
  ChromeUtils.defineModuleGetter(this, "fxAccounts",
    "resource://gre/modules/FxAccounts.jsm");
  ChromeUtils.defineModuleGetter(this, "FxAccounts",
    "resource://gre/modules/FxAccounts.jsm");
}

Preferences.addAll([
  // Startup
  { id: "browser.startup.page", type: "int" },
  { id: "browser.startup.homepage", type: "wstring" },

  { id: "pref.browser.homepage.disable_button.current_page", type: "bool" },
  { id: "pref.browser.homepage.disable_button.bookmark_page", type: "bool" },
  { id: "pref.browser.homepage.disable_button.restore_default", type: "bool" },

  { id: "browser.privatebrowsing.autostart", type: "bool" },

  // Downloads
  { id: "browser.download.useDownloadDir", type: "bool" },
  { id: "browser.download.folderList", type: "int" },
  { id: "browser.download.dir", type: "file" },

  /* Tab preferences
  Preferences:

  browser.link.open_newwindow
      1 opens such links in the most recent window or tab,
      2 opens such links in a new window,
      3 opens such links in a new tab
  browser.tabs.loadInBackground
  - true if display should switch to a new tab which has been opened from a
    link, false if display shouldn't switch
  browser.tabs.warnOnClose
  - true if when closing a window with multiple tabs the user is warned and
    allowed to cancel the action, false to just close the window
  browser.tabs.warnOnOpen
  - true if the user should be warned if he attempts to open a lot of tabs at
    once (e.g. a large folder of bookmarks), false otherwise
  browser.taskbar.previews.enable
  - true if tabs are to be shown in the Windows 7 taskbar
  */

  { id: "browser.link.open_newwindow", type: "int" },
  { id: "browser.tabs.loadInBackground", type: "bool", inverted: true },
  { id: "browser.tabs.warnOnClose", type: "bool" },
  { id: "browser.tabs.warnOnOpen", type: "bool" },
  { id: "browser.sessionstore.restore_on_demand", type: "bool" },
  { id: "browser.ctrlTab.previews", type: "bool" },

  // Fonts
  { id: "font.language.group", type: "wstring" },

  // Languages
  { id: "browser.translation.detectLanguage", type: "bool" },

  // General tab

  /* Accessibility
   * accessibility.browsewithcaret
     - true enables keyboard navigation and selection within web pages using a
       visible caret, false uses normal keyboard navigation with no caret
   * accessibility.typeaheadfind
     - when set to true, typing outside text areas and input boxes will
       automatically start searching for what's typed within the current
       document; when set to false, no search action happens */
  { id: "accessibility.browsewithcaret", type: "bool" },
  { id: "accessibility.typeaheadfind", type: "bool" },
  { id: "accessibility.blockautorefresh", type: "bool" },

  /* Browsing
   * general.autoScroll
     - when set to true, clicking the scroll wheel on the mouse activates a
       mouse mode where moving the mouse down scrolls the document downward with
       speed correlated with the distance of the cursor from the original
       position at which the click occurred (and likewise with movement upward);
       if false, this behavior is disabled
   * general.smoothScroll
     - set to true to enable finer page scrolling than line-by-line on page-up,
       page-down, and other such page movements */
  { id: "general.autoScroll", type: "bool" },
  { id: "general.smoothScroll", type: "bool" },
  { id: "layout.spellcheckDefault", type: "int" },

  { id: "browser.preferences.defaultPerformanceSettings.enabled", type: "bool" },
  { id: "dom.ipc.processCount", type: "int" },
  { id: "dom.ipc.processCount.web", type: "int" },
  { id: "layers.acceleration.disabled", type: "bool", inverted: true },

  // Files and Applications
  { id: "browser.feeds.handler", type: "string" },
  { id: "browser.feeds.handler.default", type: "string" },
  { id: "browser.feeds.handlers.application", type: "file" },
  { id: "browser.feeds.handlers.webservice", type: "string" },

  { id: "browser.videoFeeds.handler", type: "string" },
  { id: "browser.videoFeeds.handler.default", type: "string" },
  { id: "browser.videoFeeds.handlers.application", type: "file" },
  { id: "browser.videoFeeds.handlers.webservice", type: "string" },

  { id: "browser.audioFeeds.handler", type: "string" },
  { id: "browser.audioFeeds.handler.default", type: "string" },
  { id: "browser.audioFeeds.handlers.application", type: "file" },
  { id: "browser.audioFeeds.handlers.webservice", type: "string" },

  { id: "pref.downloads.disable_button.edit_actions", type: "bool" },

  // DRM content
  { id: "media.eme.enabled", type: "bool" },

  // Update
  { id: "browser.preferences.advanced.selectedTabIndex", type: "int" },
  { id: "browser.search.update", type: "bool" },

  { id: "privacy.userContext.enabled", type: "bool" },
]);

if (AppConstants.HAVE_SHELL_SERVICE) {
  Preferences.addAll([
    { id: "browser.shell.checkDefaultBrowser", type: "bool" },
    { id: "pref.general.disable_button.default_browser", type: "bool" },
  ]);
}

if (AppConstants.platform === "win") {
  Preferences.addAll([
    { id: "browser.taskbar.previews.enable", type: "bool" },
    { id: "ui.osk.enabled", type: "bool" },
  ]);
}

if (AppConstants.MOZ_UPDATER) {
  Preferences.addAll([
    { id: "app.update.enabled", type: "bool" },
    { id: "app.update.auto", type: "bool" },
    { id: "app.update.disable_button.showUpdateHistory", type: "bool" },
  ]);

  if (AppConstants.MOZ_MAINTENANCE_SERVICE) {
    Preferences.addAll([
      { id: "app.update.service.enabled", type: "bool" },
    ]);
  }
}

// A promise that resolves when the list of application handlers is loaded.
// We store this in a global so tests can await it.
var promiseLoadHandlersList;

var gMainPane = {
  // The set of types the app knows how to handle.  A hash of HandlerInfoWrapper
  // objects, indexed by type.
  _handledTypes: {},

  // The list of types we can show, sorted by the sort column/direction.
  // An array of HandlerInfoWrapper objects.  We build this list when we first
  // load the data and then rebuild it when users change a pref that affects
  // what types we can show or change the sort column/direction.
  // Note: this isn't necessarily the list of types we *will* show; if the user
  // provides a filter string, we'll only show the subset of types in this list
  // that match that string.
  _visibleTypes: [],

  // A count of the number of times each visible type description appears.
  // We use these counts to determine whether or not to annotate descriptions
  // with their types to distinguish duplicate descriptions from each other.
  // A hash of integer counts, indexed by string description.
  _visibleTypeDescriptionCount: {},


  // Convenience & Performance Shortcuts

  get _brandShortName() {
    delete this._brandShortName;
    return this._brandShortName = document.getElementById("bundleBrand").getString("brandShortName");
  },

  get _prefsBundle() {
    delete this._prefsBundle;
    return this._prefsBundle = document.getElementById("bundlePreferences");
  },

  get _list() {
    delete this._list;
    return this._list = document.getElementById("handlersView");
  },

  get _filter() {
    delete this._filter;
    return this._filter = document.getElementById("filter");
  },

  _mimeSvc: Cc["@mozilla.org/mime;1"].
    getService(Ci.nsIMIMEService),

  _helperAppSvc: Cc["@mozilla.org/uriloader/external-helper-app-service;1"].
    getService(Ci.nsIExternalHelperAppService),

  _handlerSvc: Cc["@mozilla.org/uriloader/handler-service;1"].
    getService(Ci.nsIHandlerService),

  _backoffIndex: 0,

  /**
   * Initialization of this.
   */
  init() {
    function setEventListener(aId, aEventType, aCallback) {
      document.getElementById(aId)
        .addEventListener(aEventType, aCallback.bind(gMainPane));
    }

    if (AppConstants.HAVE_SHELL_SERVICE) {
      this.updateSetDefaultBrowser();
      let win = Services.wm.getMostRecentWindow("navigator:browser");

      // Exponential backoff mechanism will delay the polling times if user doesn't
      // trigger SetDefaultBrowser for a long time.
      let backoffTimes = [1000, 1000, 1000, 1000, 2000, 2000, 2000, 5000, 5000, 10000];

      let pollForDefaultBrowser = () => {
        let uri = win.gBrowser.currentURI.spec;

        if ((uri == "about:preferences" || uri == "about:preferences#general") &&
          document.visibilityState == "visible") {
          this.updateSetDefaultBrowser();
        }

        // approximately a "requestIdleInterval"
        window.setTimeout(() => {
          window.requestIdleCallback(pollForDefaultBrowser);
        }, backoffTimes[this._backoffIndex + 1 < backoffTimes.length ?
          this._backoffIndex++ : backoffTimes.length - 1]);
      };

      window.setTimeout(() => {
        window.requestIdleCallback(pollForDefaultBrowser);
      }, backoffTimes[this._backoffIndex]);
    }

    this.initBrowserContainers();
    this.buildContentProcessCountMenuList();

    let performanceSettingsLink = document.getElementById("performanceSettingsLearnMore");
    let performanceSettingsUrl = Services.urlFormatter.formatURLPref("app.support.baseURL") + "performance";
    performanceSettingsLink.setAttribute("href", performanceSettingsUrl);

    this.updateDefaultPerformanceSettingsPref();

    let defaultPerformancePref =
      Preferences.get("browser.preferences.defaultPerformanceSettings.enabled");
    defaultPerformancePref.on("change", () => {
      this.updatePerformanceSettingsBox({ duringChangeEvent: true });
    });
    this.updatePerformanceSettingsBox({ duringChangeEvent: false });

    // set up the "use current page" label-changing listener
    this._updateUseCurrentButton();
    window.addEventListener("focus", this._updateUseCurrentButton.bind(this));

    this.updateBrowserStartupLastSession();

    handleControllingExtension(
      URL_OVERRIDES_TYPE, NEW_TAB_KEY, NEW_TAB_STRING_ID);
    let newTabObserver = {
      observe(subject, topic, data) {
        handleControllingExtension(
          URL_OVERRIDES_TYPE, NEW_TAB_KEY, NEW_TAB_STRING_ID);
      },
    };
    Services.obs.addObserver(newTabObserver, "newtab-url-changed");
    window.addEventListener("unload", () => {
      Services.obs.removeObserver(newTabObserver, "newtab-url-changed");
    });

    let connectionSettingsLink = document.getElementById("connectionSettingsLearnMore");
    let connectionSettingsUrl = Services.urlFormatter.formatURLPref("app.support.baseURL") +
                                "prefs-connection-settings";
    connectionSettingsLink.setAttribute("href", connectionSettingsUrl);
    this.updateProxySettingsUI();
    initializeProxyUI(gMainPane);

    if (AppConstants.platform == "win") {
      // Functionality for "Show tabs in taskbar" on Windows 7 and up.
      try {
        let ver = parseFloat(Services.sysinfo.getProperty("version"));
        let showTabsInTaskbar = document.getElementById("showTabsInTaskbar");
        showTabsInTaskbar.hidden = ver < 6.1;
      } catch (ex) { }
    }

    // The "closing multiple tabs" and "opening multiple tabs might slow down
    // &brandShortName;" warnings provide options for not showing these
    // warnings again. When the user disabled them, we provide checkboxes to
    // re-enable the warnings.
    if (!TransientPrefs.prefShouldBeVisible("browser.tabs.warnOnClose"))
      document.getElementById("warnCloseMultiple").hidden = true;
    if (!TransientPrefs.prefShouldBeVisible("browser.tabs.warnOnOpen"))
      document.getElementById("warnOpenMany").hidden = true;

    Preferences.get("browser.privatebrowsing.autostart").on("change",
      gMainPane.updateBrowserStartupLastSession.bind(gMainPane));
    if (AppConstants.HAVE_SHELL_SERVICE) {
      setEventListener("setDefaultButton", "command",
        gMainPane.setDefaultBrowser);
    }
    setEventListener("useCurrent", "command",
      gMainPane.setHomePageToCurrent);
    setEventListener("useBookmark", "command",
      gMainPane.setHomePageToBookmark);
    setEventListener("restoreDefaultHomePage", "command",
      gMainPane.restoreDefaultHomePage);
    setEventListener("disableHomePageExtension", "command",
                     makeDisableControllingExtension(PREF_SETTING_TYPE, HOMEPAGE_OVERRIDE_KEY));
    setEventListener("disableContainersExtension", "command",
                     makeDisableControllingExtension(PREF_SETTING_TYPE, CONTAINERS_KEY));
    setEventListener("disableNewTabExtension", "command",
                     makeDisableControllingExtension(URL_OVERRIDES_TYPE, NEW_TAB_KEY));
    setEventListener("chooseLanguage", "command",
      gMainPane.showLanguages);
    setEventListener("translationAttributionImage", "click",
      gMainPane.openTranslationProviderAttribution);
    setEventListener("translateButton", "command",
      gMainPane.showTranslationExceptions);
    Preferences.get("font.language.group").on("change",
      gMainPane._rebuildFonts.bind(gMainPane));
    setEventListener("advancedFonts", "command",
      gMainPane.configureFonts);
    setEventListener("colors", "command",
      gMainPane.configureColors);
    Preferences.get("layers.acceleration.disabled").on("change",
      gMainPane.updateHardwareAcceleration.bind(gMainPane));
    setEventListener("connectionSettings", "command",
      gMainPane.showConnections);
    setEventListener("browserContainersCheckbox", "command",
      gMainPane.checkBrowserContainers);
    setEventListener("browserContainersSettings", "command",
      gMainPane.showContainerSettings);
    setEventListener("browserHomePage", "input",
      gMainPane.onBrowserHomePageChange);

    // Initializes the fonts dropdowns displayed in this pane.
    this._rebuildFonts();

    this.updateOnScreenKeyboardVisibility();

    // Show translation preferences if we may:
    const prefName = "browser.translation.ui.show";
    if (Services.prefs.getBoolPref(prefName)) {
      let row = document.getElementById("translationBox");
      row.removeAttribute("hidden");
      // Showing attribution only for Bing Translator.
      ChromeUtils.import("resource:///modules/translation/Translation.jsm");
      if (Translation.translationEngine == "bing") {
        document.getElementById("bingAttribution").removeAttribute("hidden");
      }
    }

    if (AppConstants.MOZ_DEV_EDITION) {
      let uAppData = OS.Constants.Path.userApplicationDataDir;
      let ignoreSeparateProfile = OS.Path.join(uAppData, "ignore-dev-edition-profile");

      setEventListener("separateProfileMode", "command", gMainPane.separateProfileModeChange);
      let separateProfileModeCheckbox = document.getElementById("separateProfileMode");
      setEventListener("getStarted", "click", gMainPane.onGetStarted);

      OS.File.stat(ignoreSeparateProfile).then(() => separateProfileModeCheckbox.checked = false,
        () => separateProfileModeCheckbox.checked = true);

      if (Services.prefs.getBoolPref("identity.fxaccounts.enabled")) {
        document.getElementById("sync-dev-edition-root").hidden = false;
        fxAccounts.getSignedInUser().then(data => {
          document.getElementById("getStarted").selectedIndex = data ? 1 : 0;
        }).catch(Cu.reportError);
      }
    }

    // Initialize the Firefox Updates section.
    let version = AppConstants.MOZ_APP_VERSION_DISPLAY;

    // Include the build ID if this is an "a#" (nightly) build
    if (/a\d+$/.test(version)) {
      let buildID = Services.appinfo.appBuildID;
      let year = buildID.slice(0, 4);
      let month = buildID.slice(4, 6);
      let day = buildID.slice(6, 8);
      version += ` (${year}-${month}-${day})`;
    }

    // Append "(32-bit)" or "(64-bit)" build architecture to the version number:
    let bundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
    let archResource = Services.appinfo.is64Bit
      ? "aboutDialog.architecture.sixtyFourBit"
      : "aboutDialog.architecture.thirtyTwoBit";
    let arch = bundle.GetStringFromName(archResource);
    version += ` (${arch})`;

    document.l10n.setAttributes(
      document.getElementById("updateAppInfo"),
      "update-application-info",
      { version }
    );

    // Show a release notes link if we have a URL.
    let relNotesLink = document.getElementById("releasenotes");
    let relNotesPrefType = Services.prefs.getPrefType("app.releaseNotesURL");
    if (relNotesPrefType != Services.prefs.PREF_INVALID) {
      let relNotesURL = Services.urlFormatter.formatURLPref("app.releaseNotesURL");
      if (relNotesURL != "about:blank") {
        relNotesLink.href = relNotesURL;
        relNotesLink.hidden = false;
      }
    }

    let distroId = Services.prefs.getCharPref("distribution.id", "");
    if (distroId) {
      let distroString = distroId;

      let distroVersion = Services.prefs.getCharPref("distribution.version", "");
      if (distroVersion) {
        distroString += " - " + distroVersion;
      }

      let distroIdField = document.getElementById("distributionId");
      distroIdField.value = distroString;
      distroIdField.hidden = false;

      let distroAbout = Services.prefs.getStringPref("distribution.about", "");
      if (distroAbout) {
        let distroField = document.getElementById("distribution");
        distroField.value = distroAbout;
        distroField.hidden = false;
      }
    }

    if (AppConstants.MOZ_UPDATER) {
      gAppUpdater = new appUpdater();
      let onUnload = () => {
        window.removeEventListener("unload", onUnload);
        Services.prefs.removeObserver("app.update.", this);
      };
      window.addEventListener("unload", onUnload);
      Services.prefs.addObserver("app.update.", this);
      this.updateReadPrefs();
      setEventListener("updateRadioGroup", "command",
        gMainPane.updateWritePrefs);
      setEventListener("showUpdateHistory", "command",
        gMainPane.showUpdates);
    }

    // Initilize Application section.

    // Observe preferences that influence what we display so we can rebuild
    // the view when they change.
    Services.prefs.addObserver(PREF_SHOW_PLUGINS_IN_LIST, this);
    Services.prefs.addObserver(PREF_HIDE_PLUGINS_WITHOUT_EXTENSIONS, this);
    Services.prefs.addObserver(PREF_FEED_SELECTED_APP, this);
    Services.prefs.addObserver(PREF_FEED_SELECTED_WEB, this);
    Services.prefs.addObserver(PREF_FEED_SELECTED_ACTION, this);
    Services.prefs.addObserver(PREF_FEED_SELECTED_READER, this);

    Services.prefs.addObserver(PREF_VIDEO_FEED_SELECTED_APP, this);
    Services.prefs.addObserver(PREF_VIDEO_FEED_SELECTED_WEB, this);
    Services.prefs.addObserver(PREF_VIDEO_FEED_SELECTED_ACTION, this);
    Services.prefs.addObserver(PREF_VIDEO_FEED_SELECTED_READER, this);

    Services.prefs.addObserver(PREF_AUDIO_FEED_SELECTED_APP, this);
    Services.prefs.addObserver(PREF_AUDIO_FEED_SELECTED_WEB, this);
    Services.prefs.addObserver(PREF_AUDIO_FEED_SELECTED_ACTION, this);
    Services.prefs.addObserver(PREF_AUDIO_FEED_SELECTED_READER, this);

    setEventListener("filter", "command", gMainPane.filter);
    setEventListener("handlersView", "select",
      gMainPane.onSelectionChanged);
    setEventListener("typeColumn", "click", gMainPane.sort);
    setEventListener("actionColumn", "click", gMainPane.sort);
    setEventListener("chooseFolder", "command", gMainPane.chooseFolder);
    setEventListener("saveWhere", "command", gMainPane.handleSaveToCommand);
    Preferences.get("browser.download.dir").on("change",
      gMainPane.displayDownloadDirPref.bind(gMainPane));

    // Listen for window unload so we can remove our preference observers.
    window.addEventListener("unload", this);

    // Figure out how we should be sorting the list.  We persist sort settings
    // across sessions, so we can't assume the default sort column/direction.
    // XXX should we be using the XUL sort service instead?
    if (document.getElementById("actionColumn").hasAttribute("sortDirection")) {
      this._sortColumn = document.getElementById("actionColumn");
      // The typeColumn element always has a sortDirection attribute,
      // either because it was persisted or because the default value
      // from the xul file was used.  If we are sorting on the other
      // column, we should remove it.
      document.getElementById("typeColumn").removeAttribute("sortDirection");
    } else {
      this._sortColumn = document.getElementById("typeColumn");
    }

    let browserBundle = document.getElementById("browserBundle");
    appendSearchKeywords("browserContainersSettings", [
      browserBundle.getString("userContextPersonal.label"),
      browserBundle.getString("userContextWork.label"),
      browserBundle.getString("userContextBanking.label"),
      browserBundle.getString("userContextShopping.label"),
    ]);

    // Notify observers that the UI is now ready
    Services.obs.notifyObservers(window, "main-pane-loaded");
  },

  preInit() {
    promiseLoadHandlersList = new Promise((resolve, reject) => {
      // Load the data and build the list of handlers for applications pane.
      // By doing this after pageshow, we ensure it doesn't delay painting
      // of the preferences page.
      window.addEventListener("pageshow", async () => {
        try {
          this._loadData();
          await this._rebuildVisibleTypes();
          this._sortVisibleTypes();
          this._rebuildView();
          resolve();
        } catch (ex) {
          reject(ex);
        }
      }, { once: true });
    });
  },

  // CONTAINERS

  /*
   * preferences:
   *
   * privacy.userContext.enabled
   * - true if containers is enabled
   */

  /**
   * Enables/disables the Settings button used to configure containers
   */
  readBrowserContainersCheckbox() {
    const pref = Preferences.get("privacy.userContext.enabled");
    const settings = document.getElementById("browserContainersSettings");

    settings.disabled = !pref.value;
    const containersEnabled = Services.prefs.getBoolPref("privacy.userContext.enabled");
    const containersCheckbox = document.getElementById("browserContainersCheckbox");
    containersCheckbox.checked = containersEnabled;
    handleControllingExtension(PREF_SETTING_TYPE, CONTAINERS_KEY)
      .then((isControlled) => {
        containersCheckbox.disabled = isControlled;
      });
  },

  /**
   * Show the Containers UI depending on the privacy.userContext.ui.enabled pref.
   */
  initBrowserContainers() {
    if (!Services.prefs.getBoolPref("privacy.userContext.ui.enabled")) {
      // The browserContainersGroup element has its own internal padding that
      // is visible even if the browserContainersbox is visible, so hide the whole
      // groupbox if the feature is disabled to prevent a gap in the preferences.
      document.getElementById("browserContainersbox").setAttribute("data-hidden-from-search", "true");
      return;
    }
    Services.prefs.addObserver(PREF_CONTAINERS_EXTENSION, this);

    const link = document.getElementById("browserContainersLearnMore");
    link.href = Services.urlFormatter.formatURLPref("app.support.baseURL") + "containers";

    document.getElementById("browserContainersbox").hidden = false;
    this.readBrowserContainersCheckbox();
  },

  async separateProfileModeChange() {
    if (AppConstants.MOZ_DEV_EDITION) {
      function quitApp() {
        Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestartNotSameProfile);
      }
      function revertCheckbox(error) {
        separateProfileModeCheckbox.checked = !separateProfileModeCheckbox.checked;
        if (error) {
          Cu.reportError("Failed to toggle separate profile mode: " + error);
        }
      }
      function createOrRemoveSpecialDevEditionFile(onSuccess) {
        let uAppData = OS.Constants.Path.userApplicationDataDir;
        let ignoreSeparateProfile = OS.Path.join(uAppData, "ignore-dev-edition-profile");

        if (separateProfileModeCheckbox.checked) {
          OS.File.remove(ignoreSeparateProfile).then(onSuccess, revertCheckbox);
        } else {
          OS.File.writeAtomic(ignoreSeparateProfile, new Uint8Array()).then(onSuccess, revertCheckbox);
        }
      }

      let separateProfileModeCheckbox = document.getElementById("separateProfileMode");
      let button_index = await confirmRestartPrompt(separateProfileModeCheckbox.checked,
        0, false, true);
      switch (button_index) {
        case CONFIRM_RESTART_PROMPT_CANCEL:
          revertCheckbox();
          return;
        case CONFIRM_RESTART_PROMPT_RESTART_NOW:
          let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]
            .createInstance(Ci.nsISupportsPRBool);
          Services.obs.notifyObservers(cancelQuit, "quit-application-requested",
            "restart");
          if (!cancelQuit.data) {
            createOrRemoveSpecialDevEditionFile(quitApp);
            return;
          }

          // Revert the checkbox in case we didn't quit
          revertCheckbox();
          return;
        case CONFIRM_RESTART_PROMPT_RESTART_LATER:
          createOrRemoveSpecialDevEditionFile();
      }
    }
  },

  async onGetStarted(aEvent) {
    if (!AppConstants.MOZ_DEV_EDITION) {
      return;
    }
    const win = Services.wm.getMostRecentWindow("navigator:browser");
    if (!win) {
      return;
    }
    const user = await fxAccounts.getSignedInUser();
    if (user) {
      // We have a user, open Sync preferences in the same tab
      win.openUILinkIn("about:preferences#sync", "current");
      return;
    }
    let url = await FxAccounts.config.promiseSignInURI("dev-edition-setup");
    let accountsTab = win.gBrowser.addTab(url);
    win.gBrowser.selectedTab = accountsTab;
  },

  // HOME PAGE

  /*
   * Preferences:
   *
   * browser.startup.homepage
   * - the user's home page, as a string; if the home page is a set of tabs,
   *   this will be those URLs separated by the pipe character "|"
   * browser.startup.page
   * - what page(s) to show when the user starts the application, as an integer:
   *
   *     0: a blank page
   *     1: the home page (as set by the browser.startup.homepage pref)
   *     2: the last page the user visited (DEPRECATED)
   *     3: windows and tabs from the last session (a.k.a. session restore)
   *
   *   The deprecated option is not exposed in UI; however, if the user has it
   *   selected and doesn't change the UI for this preference, the deprecated
   *   option is preserved.
   */

  syncFromHomePref() {
    let homePref = Preferences.get("browser.startup.homepage");

    // Set the "Use Current Page(s)" button's text and enabled state.
    this._updateUseCurrentButton();

    function setInputDisabledStates(isControlled) {
      let tabCount = this._getTabsForHomePage().length;

      // Disable or enable the inputs based on if this is controlled by an extension.
      document.querySelectorAll("#browserHomePage, .homepage-button")
        .forEach((element) => {
          let pref = element.getAttribute("preference");

          let isDisabled = Preferences.get(pref).locked || isControlled;
          if (pref == "pref.browser.disable_button.current_page") {
            // Special case for current_page to disable it if tabCount is 0
            isDisabled = isDisabled || tabCount < 1;
          }

          element.disabled = isDisabled;
        });
    }

    if (homePref.locked) {
      // An extension can't control these settings if they're locked.
      hideControllingExtension(HOMEPAGE_OVERRIDE_KEY);
      setInputDisabledStates.call(this, false);
    } else {
      // Asynchronously update the extension controlled UI.
      handleControllingExtension(
        PREF_SETTING_TYPE, HOMEPAGE_OVERRIDE_KEY, "extensionControlled.homepage_override2")
        .then(setInputDisabledStates.bind(this));
    }

    // If the pref is set to about:home or about:newtab, set the value to ""
    // to show the placeholder text (about:home title) rather than
    // exposing those URLs to users.
    let defaultBranch = Services.prefs.getDefaultBranch("");
    let defaultValue = defaultBranch.getComplexValue("browser.startup.homepage",
      Ci.nsIPrefLocalizedString).data;
    let currentValue = homePref.value.toLowerCase();
    if (currentValue == "about:home" ||
      (currentValue == defaultValue && currentValue == "about:newtab")) {
      return "";
    }

    // If the pref is actually "", show about:blank.  The actual home page
    // loading code treats them the same, and we don't want the placeholder text
    // to be shown.
    if (homePref.value == "")
      return "about:blank";

    // Otherwise, show the actual pref value.
    return undefined;
  },

  syncToHomePref(value) {
    // If the value is "", use about:home.
    if (value == "")
      return "about:home";

    // Otherwise, use the actual textbox value.
    return undefined;
  },

  /**
   * Sets the home page to the current displayed page (or frontmost tab, if the
   * most recent browser window contains multiple tabs), updating preference
   * window UI to reflect this.
   */
  setHomePageToCurrent() {
    let homePage = Preferences.get("browser.startup.homepage");
    let tabs = this._getTabsForHomePage();
    function getTabURI(t) {
      return t.linkedBrowser.currentURI.spec;
    }

    // FIXME Bug 244192: using dangerous "|" joiner!
    if (tabs.length) {
      homePage.value = tabs.map(getTabURI).join("|");
    }

    Services.telemetry.scalarAdd("preferences.use_current_page", 1);
  },

  /**
   * Displays a dialog in which the user can select a bookmark to use as home
   * page.  If the user selects a bookmark, that bookmark's name is displayed in
   * UI and the bookmark's address is stored to the home page preference.
   */
  setHomePageToBookmark() {
    var rv = { urls: null, names: null };
    gSubDialog.open("chrome://browser/content/preferences/selectBookmark.xul",
      "resizable=yes, modal=yes", rv,
      this._setHomePageToBookmarkClosed.bind(this, rv));
    Services.telemetry.scalarAdd("preferences.use_bookmark", 1);
  },

  onBrowserHomePageChange() {
    if (this.telemetryHomePageTimer) {
      clearTimeout(this.telemetryHomePageTimer);
    }
    let browserHomePage = document.querySelector("#browserHomePage").value;
    // The length of the home page URL string should be more then four,
    // and it should contain at least one ".", for example, "https://mozilla.org".
    if (browserHomePage.length > 4 && browserHomePage.includes(".")) {
      this.telemetryHomePageTimer = setTimeout(() => {
        let homePageNumber = browserHomePage.split("|").length;
        Services.telemetry.scalarAdd("preferences.browser_home_page_change", 1);
        Services.telemetry.keyedScalarAdd("preferences.browser_home_page_count", homePageNumber, 1);
      }, 3000);
    }
  },

  _setHomePageToBookmarkClosed(rv, aEvent) {
    if (aEvent.detail.button != "accept")
      return;
    if (rv.urls && rv.names) {
      var homePage = Preferences.get("browser.startup.homepage");

      // XXX still using dangerous "|" joiner!
      homePage.value = rv.urls.join("|");
    }
  },

  /**
   * Switches the "Use Current Page" button between its singular and plural
   * forms.
   */
  async _updateUseCurrentButton() {
    let useCurrent = document.getElementById("useCurrent");
    let tabs = this._getTabsForHomePage();

    const tabCount = tabs.length;

    document.l10n.setAttributes(useCurrent, "use-current-pages", { tabCount });

    // If the homepage is controlled by an extension then you can't use this.
    if (await getControllingExtensionInfo(PREF_SETTING_TYPE, HOMEPAGE_OVERRIDE_KEY)) {
      return;
    }

    // In this case, the button's disabled state is set by preferences.xml.
    let prefName = "pref.browser.homepage.disable_button.current_page";
    if (Preferences.get(prefName).locked)
      return;

    useCurrent.disabled = tabCount < 1;
  },

  _getTabsForHomePage() {
    var tabs = [];
    var win = Services.wm.getMostRecentWindow("navigator:browser");

    if (win && win.document.documentElement
      .getAttribute("windowtype") == "navigator:browser") {
      // We should only include visible & non-pinned tabs

      tabs = win.gBrowser.visibleTabs.slice(win.gBrowser._numPinnedTabs);
      tabs = tabs.filter(this.isNotAboutPreferences);
      // XXX: Bug 1441637 - Fix tabbrowser to report tab.closing before it blurs it
      tabs = tabs.filter(tab => !tab.closing);
    }

    return tabs;
  },

  /**
   * Check to see if a tab is not about:preferences
   */
  isNotAboutPreferences(aElement, aIndex, aArray) {
    return !aElement.linkedBrowser.currentURI.spec.startsWith("about:preferences");
  },

  /**
   * Restores the default home page as the user's home page.
   */
  restoreDefaultHomePage() {
    var homePage = Preferences.get("browser.startup.homepage");
    homePage.value = homePage.defaultValue;
  },

  /**
   * Utility function to enable/disable the button specified by aButtonID based
   * on the value of the Boolean preference specified by aPreferenceID.
   */
  updateButtons(aButtonID, aPreferenceID) {
    var button = document.getElementById(aButtonID);
    var preference = Preferences.get(aPreferenceID);
    button.disabled = !preference.value;
    return undefined;
  },

  /**
   * Hide/show the "Show my windows and tabs from last time" option based
   * on the value of the browser.privatebrowsing.autostart pref.
   */
  updateBrowserStartupLastSession() {
    let pbAutoStartPref = Preferences.get("browser.privatebrowsing.autostart");
    let startupPref = Preferences.get("browser.startup.page");
    let group = document.getElementById("browserStartupPage");
    let option = document.getElementById("browserStartupLastSession");
    if (pbAutoStartPref.value) {
      option.setAttribute("disabled", "true");
      if (option.selected) {
        group.selectedItem = document.getElementById("browserStartupHomePage");
      }
    } else {
      option.removeAttribute("disabled");
      startupPref.updateElements(); // select the correct radio in the startup group
    }
  },

  // TABS

  /*
   * Preferences:
   *
   * browser.link.open_newwindow - int
   *   Determines where links targeting new windows should open.
   *   Values:
   *     1 - Open in the current window or tab.
   *     2 - Open in a new window.
   *     3 - Open in a new tab in the most recent window.
   * browser.tabs.loadInBackground - bool
   *   True - Whether browser should switch to a new tab opened from a link.
   * browser.tabs.warnOnClose - bool
   *   True - If when closing a window with multiple tabs the user is warned and
   *          allowed to cancel the action, false to just close the window.
   * browser.tabs.warnOnOpen - bool
   *   True - Whether the user should be warned when trying to open a lot of
   *          tabs at once (e.g. a large folder of bookmarks), allowing to
   *          cancel the action.
   * browser.taskbar.previews.enable - bool
   *   True - Tabs are to be shown in Windows 7 taskbar.
   *   False - Only the window is to be shown in Windows 7 taskbar.
   */

  /**
   * Determines where a link which opens a new window will open.
   *
   * @returns |true| if such links should be opened in new tabs
   */
  readLinkTarget() {
    var openNewWindow = Preferences.get("browser.link.open_newwindow");
    return openNewWindow.value != 2;
  },

  /**
   * Determines where a link which opens a new window will open.
   *
   * @returns 2 if such links should be opened in new windows,
   *          3 if such links should be opened in new tabs
   */
  writeLinkTarget() {
    var linkTargeting = document.getElementById("linkTargeting");
    return linkTargeting.checked ? 3 : 2;
  },
  /*
   * Preferences:
   *
   * browser.shell.checkDefault
   * - true if a default-browser check (and prompt to make it so if necessary)
   *   occurs at startup, false otherwise
   */

  /**
   * Show button for setting browser as default browser or information that
   * browser is already the default browser.
   */
  updateSetDefaultBrowser() {
    if (AppConstants.HAVE_SHELL_SERVICE) {
      let shellSvc = getShellService();
      let defaultBrowserBox = document.getElementById("defaultBrowserBox");
      if (!shellSvc) {
        defaultBrowserBox.hidden = true;
        return;
      }
      let setDefaultPane = document.getElementById("setDefaultPane");
      let isDefault = shellSvc.isDefaultBrowser(false, true);
      setDefaultPane.selectedIndex = isDefault ? 1 : 0;
      let alwaysCheck = document.getElementById("alwaysCheckDefault");
      alwaysCheck.disabled = alwaysCheck.disabled ||
        isDefault && alwaysCheck.checked;
    }
  },

  /**
   * Set browser as the operating system default browser.
   */
  setDefaultBrowser() {
    if (AppConstants.HAVE_SHELL_SERVICE) {
      let alwaysCheckPref = Preferences.get("browser.shell.checkDefaultBrowser");
      alwaysCheckPref.value = true;

      // Reset exponential backoff delay time in order to do visual update in pollForDefaultBrowser.
      this._backoffIndex = 0;

      let shellSvc = getShellService();
      if (!shellSvc)
        return;
      try {
        shellSvc.setDefaultBrowser(true, false);
      } catch (ex) {
        Cu.reportError(ex);
        return;
      }

      let selectedIndex = shellSvc.isDefaultBrowser(false, true) ? 1 : 0;
      document.getElementById("setDefaultPane").selectedIndex = selectedIndex;
    }
  },

  /**
   * Shows a dialog in which the preferred language for web content may be set.
   */
  showLanguages() {
    gSubDialog.open("chrome://browser/content/preferences/languages.xul");
  },

  /**
   * Displays the translation exceptions dialog where specific site and language
   * translation preferences can be set.
   */
  showTranslationExceptions() {
    gSubDialog.open("chrome://browser/content/preferences/translation.xul");
  },

  openTranslationProviderAttribution() {
    ChromeUtils.import("resource:///modules/translation/Translation.jsm");
    Translation.openProviderAttribution();
  },

  /**
   * Displays the fonts dialog, where web page font names and sizes can be
   * configured.
   */
  configureFonts() {
    gSubDialog.open("chrome://browser/content/preferences/fonts.xul", "resizable=no");
  },

  /**
   * Displays the colors dialog, where default web page/link/etc. colors can be
   * configured.
   */
  configureColors() {
    gSubDialog.open("chrome://browser/content/preferences/colors.xul", "resizable=no");
  },

  // NETWORK
  /**
   * Displays a dialog in which proxy settings may be changed.
   */
  showConnections() {
    gSubDialog.open("chrome://browser/content/preferences/connection.xul",
                    null, null, this.updateProxySettingsUI.bind(this));
  },

  // Update the UI to show the proper description depending on whether an
  // extension is in control or not.
  async updateProxySettingsUI() {
    let controllingExtension = await getControllingExtension(PREF_SETTING_TYPE, PROXY_KEY);
    let fragment = controllingExtension ?
      getControllingExtensionFragment(
        "extensionControlled.proxyConfig", controllingExtension, this._brandShortName) :
      BrowserUtils.getLocalizedFragment(
        document,
        this._prefsBundle.getString("connectionDesc.label"),
        this._brandShortName);
    let description = document.getElementById("connectionSettingsDescription");

    // Remove the old content from the description.
    while (description.firstChild) {
      description.firstChild.remove();
    }

    description.appendChild(fragment);
  },

  async checkBrowserContainers(event) {
    let checkbox = document.getElementById("browserContainersCheckbox");
    if (checkbox.checked) {
      Services.prefs.setBoolPref("privacy.userContext.enabled", true);
      return;
    }

    let count = ContextualIdentityService.countContainerTabs();
    if (count == 0) {
      Services.prefs.setBoolPref("privacy.userContext.enabled", false);
      return;
    }

    let [
      title, message, okButton, cancelButton
    ] = await document.l10n.formatValues([
      ["containers-disable-alert-title"],
      ["containers-disable-alert-desc", { tabCount: count }],
      ["containers-disable-alert-ok-button", { tabCount: count }],
      ["containers-disable-alert-cancel-button"]
    ]);

    let buttonFlags = (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_0) +
      (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_1);

    let rv = Services.prompt.confirmEx(window, title, message, buttonFlags,
      okButton, cancelButton, null, null, {});
    if (rv == 0) {
      Services.prefs.setBoolPref("privacy.userContext.enabled", false);
      return;
    }

    checkbox.checked = true;
  },

  /**
   * Displays container panel for customising and adding containers.
   */
  showContainerSettings() {
    gotoPref("containers");
  },

  /**
   * ui.osk.enabled
   * - when set to true, subject to other conditions, we may sometimes invoke
   *   an on-screen keyboard when a text input is focused.
   *   (Currently Windows-only, and depending on prefs, may be Windows-8-only)
   */
  updateOnScreenKeyboardVisibility() {
    if (AppConstants.platform == "win") {
      let minVersion = Services.prefs.getBoolPref("ui.osk.require_win10") ? 10 : 6.2;
      if (Services.vc.compare(Services.sysinfo.getProperty("version"), minVersion) >= 0) {
        document.getElementById("useOnScreenKeyboard").hidden = false;
      }
    }
  },

  updateHardwareAcceleration() {
    // Placeholder for restart on change
  },

  // FONTS

  /**
   * Populates the default font list in UI.
   */
  _rebuildFonts() {
    var langGroupPref = Preferences.get("font.language.group");
    var isSerif = this._readDefaultFontTypeForLanguage(langGroupPref.value) == "serif";
    this._selectDefaultLanguageGroup(langGroupPref.value, isSerif);
  },

  /**
   * Returns the type of the current default font for the language denoted by
   * aLanguageGroup.
   */
  _readDefaultFontTypeForLanguage(aLanguageGroup) {
    const kDefaultFontType = "font.default.%LANG%";
    var defaultFontTypePref = kDefaultFontType.replace(/%LANG%/, aLanguageGroup);
    var preference = Preferences.get(defaultFontTypePref);
    if (!preference) {
      preference = Preferences.add({ id: defaultFontTypePref, type: "string" });
      preference.on("change", gMainPane._rebuildFonts.bind(gMainPane));
    }
    return preference.value;
  },

  _selectDefaultLanguageGroupPromise: Promise.resolve(),

  _selectDefaultLanguageGroup(aLanguageGroup, aIsSerif) {
    this._selectDefaultLanguageGroupPromise = (async () => {
      // Avoid overlapping language group selections by awaiting the resolution
      // of the previous one.  We do this because this function is re-entrant,
      // as inserting <preference> elements into the DOM sometimes triggers a call
      // back into this function.  And since this function is also asynchronous,
      // that call can enter this function before the previous run has completed,
      // which would corrupt the font menulists.  Awaiting the previous call's
      // resolution avoids that fate.
      await this._selectDefaultLanguageGroupPromise;

      const kFontNameFmtSerif = "font.name.serif.%LANG%";
      const kFontNameFmtSansSerif = "font.name.sans-serif.%LANG%";
      const kFontNameListFmtSerif = "font.name-list.serif.%LANG%";
      const kFontNameListFmtSansSerif = "font.name-list.sans-serif.%LANG%";
      const kFontSizeFmtVariable = "font.size.variable.%LANG%";

      var prefs = [{
        format: aIsSerif ? kFontNameFmtSerif : kFontNameFmtSansSerif,
        type: "fontname",
        element: "defaultFont",
        fonttype: aIsSerif ? "serif" : "sans-serif"
      },
      {
        format: aIsSerif ? kFontNameListFmtSerif : kFontNameListFmtSansSerif,
        type: "unichar",
        element: null,
        fonttype: aIsSerif ? "serif" : "sans-serif"
      },
      {
        format: kFontSizeFmtVariable,
        type: "int",
        element: "defaultFontSize",
        fonttype: null
      }];
      for (var i = 0; i < prefs.length; ++i) {
        var preference = Preferences.get(prefs[i].format.replace(/%LANG%/, aLanguageGroup));
        if (!preference) {
          var name = prefs[i].format.replace(/%LANG%/, aLanguageGroup);
          preference = Preferences.add({ id: name, type: prefs[i].type });
        }

        if (!prefs[i].element)
          continue;

        var element = document.getElementById(prefs[i].element);
        if (element) {
          element.setAttribute("preference", preference.id);

          if (prefs[i].fonttype)
            await FontBuilder.buildFontList(aLanguageGroup, prefs[i].fonttype, element);

          preference.setElementValue(element);
        }
      }
    })()
      .catch(Cu.reportError);
  },

  /**
   * Stores the original value of the spellchecking preference to enable proper
   * restoration if unchanged (since we're mapping a tristate onto a checkbox).
   */
  _storedSpellCheck: 0,

  /**
   * Returns true if any spellchecking is enabled and false otherwise, caching
   * the current value to enable proper pref restoration if the checkbox is
   * never changed.
   *
   * layout.spellcheckDefault
   * - an integer:
   *     0  disables spellchecking
   *     1  enables spellchecking, but only for multiline text fields
   *     2  enables spellchecking for all text fields
   */
  readCheckSpelling() {
    var pref = Preferences.get("layout.spellcheckDefault");
    this._storedSpellCheck = pref.value;

    return (pref.value != 0);
  },

  /**
   * Returns the value of the spellchecking preference represented by UI,
   * preserving the preference's "hidden" value if the preference is
   * unchanged and represents a value not strictly allowed in UI.
   */
  writeCheckSpelling() {
    var checkbox = document.getElementById("checkSpelling");
    if (checkbox.checked) {
      if (this._storedSpellCheck == 2) {
        return 2;
      }
      return 1;
    }
    return 0;
  },

  updateDefaultPerformanceSettingsPref() {
    let defaultPerformancePref =
      Preferences.get("browser.preferences.defaultPerformanceSettings.enabled");
    let processCountPref = Preferences.get("dom.ipc.processCount");
    let accelerationPref = Preferences.get("layers.acceleration.disabled");
    if (processCountPref.value != processCountPref.defaultValue ||
      accelerationPref.value != accelerationPref.defaultValue) {
      defaultPerformancePref.value = false;
    }
  },

  updatePerformanceSettingsBox({ duringChangeEvent }) {
    let defaultPerformancePref =
      Preferences.get("browser.preferences.defaultPerformanceSettings.enabled");
    let performanceSettings = document.getElementById("performanceSettings");
    let processCountPref = Preferences.get("dom.ipc.processCount");
    if (defaultPerformancePref.value) {
      let accelerationPref = Preferences.get("layers.acceleration.disabled");
      // Unset the value so process count will be decided by the platform.
      processCountPref.value = processCountPref.defaultValue;
      accelerationPref.value = accelerationPref.defaultValue;
      performanceSettings.hidden = true;
    } else {
      performanceSettings.hidden = false;
    }
  },

  buildContentProcessCountMenuList() {
    if (Services.appinfo.browserTabsRemoteAutostart) {
      let processCountPref = Preferences.get("dom.ipc.processCount");
      let defaultProcessCount = processCountPref.defaultValue;

      let contentProcessCount =
        document.querySelector(`#contentProcessCount > menupopup >
                                menuitem[value="${defaultProcessCount}"]`);

      document.l10n.setAttributes(
        contentProcessCount,
        "performance-default-content-process-count",
        { num: defaultProcessCount });

      document.getElementById("limitContentProcess").disabled = false;
      document.getElementById("contentProcessCount").disabled = false;
      document.getElementById("contentProcessCountEnabledDescription").hidden = false;
      document.getElementById("contentProcessCountDisabledDescription").hidden = true;
    } else {
      document.getElementById("limitContentProcess").disabled = true;
      document.getElementById("contentProcessCount").disabled = true;
      document.getElementById("contentProcessCountEnabledDescription").hidden = true;
      document.getElementById("contentProcessCountDisabledDescription").hidden = false;
    }
  },

  /*
   * Preferences:
   *
   * app.update.enabled
   * - true if updates to the application are enabled, false otherwise
   * app.update.auto
   * - true if updates should be automatically downloaded and installed and
   * false if the user should be asked what he wants to do when an update is
   * available
   * extensions.update.enabled
   * - true if updates to extensions and themes are enabled, false otherwise
   * browser.search.update
   * - true if updates to search engines are enabled, false otherwise
   */

  /**
   * Selects the item of the radiogroup based on the pref values and locked
   * states.
   *
   * UI state matrix for update preference conditions
   *
   * UI Components:                              Preferences
   * Radiogroup                                  i   = app.update.enabled
   *                                             ii  = app.update.auto
   *
   * Disabled states:
   * Element           pref  value  locked  disabled
   * radiogroup        i     t/f    f       false
   *                   i     t/f    *t*     *true*
   *                   ii    t/f    f       false
   *                   ii    t/f    *t*     *true*
   */
  updateReadPrefs() {
    if (AppConstants.MOZ_UPDATER) {
      var enabledPref = Preferences.get("app.update.enabled");
      var autoPref = Preferences.get("app.update.auto");
      let disabledByPolicy = Services.policies &&
                             !Services.policies.isAllowed("appUpdate");
      var radiogroup = document.getElementById("updateRadioGroup");

      if (!enabledPref.value || disabledByPolicy) // Don't care for autoPref.value in this case.
        radiogroup.value = "manual"; // 3. Never check for updates.
      else if (autoPref.value) // enabledPref.value && autoPref.value
        radiogroup.value = "auto"; // 1. Automatically install updates
      else // enabledPref.value && !autoPref.value
        radiogroup.value = "checkOnly"; // 2. Check, but let me choose

      var canCheck = Cc["@mozilla.org/updates/update-service;1"].
        getService(Ci.nsIApplicationUpdateService).
        canCheckForUpdates;
      // canCheck is false if the enabledPref is false and locked,
      // or the binary platform or OS version is not known.
      // A locked pref is sufficient to disable the radiogroup.
      radiogroup.disabled = !canCheck ||
                            enabledPref.locked ||
                            autoPref.locked ||
                            disabledByPolicy;

      if (AppConstants.MOZ_MAINTENANCE_SERVICE) {
        // Check to see if the maintenance service is installed.
        // If it is don't show the preference at all.
        var installed;
        try {
          var wrk = Cc["@mozilla.org/windows-registry-key;1"]
            .createInstance(Ci.nsIWindowsRegKey);
          wrk.open(wrk.ROOT_KEY_LOCAL_MACHINE,
            "SOFTWARE\\Mozilla\\MaintenanceService",
            wrk.ACCESS_READ | wrk.WOW64_64);
          installed = wrk.readIntValue("Installed");
          wrk.close();
        } catch (e) {
        }
        if (installed != 1) {
          document.getElementById("useService").hidden = true;
        }
      }
    }
  },

  /**
   * Sets the pref values based on the selected item of the radiogroup.
   */
  updateWritePrefs() {
    let disabledByPolicy = Services.policies &&
                           !Services.policies.isAllowed("appUpdate");
    if (AppConstants.MOZ_UPDATER && !disabledByPolicy) {
      var enabledPref = Preferences.get("app.update.enabled");
      var autoPref = Preferences.get("app.update.auto");
      var radiogroup = document.getElementById("updateRadioGroup");
      switch (radiogroup.value) {
        case "auto": // 1. Automatically install updates for Desktop only
          enabledPref.value = true;
          autoPref.value = true;
          break;
        case "checkOnly": // 2. Check, but let me choose
          enabledPref.value = true;
          autoPref.value = false;
          break;
        case "manual": // 3. Never check for updates.
          enabledPref.value = false;
          autoPref.value = false;
      }
    }
  },

  /**
   * Displays the history of installed updates.
   */
  showUpdates() {
    gSubDialog.open("chrome://mozapps/content/update/history.xul");
  },

  destroy() {
    window.removeEventListener("unload", this);
    Services.prefs.removeObserver(PREF_SHOW_PLUGINS_IN_LIST, this);
    Services.prefs.removeObserver(PREF_HIDE_PLUGINS_WITHOUT_EXTENSIONS, this);
    Services.prefs.removeObserver(PREF_FEED_SELECTED_APP, this);
    Services.prefs.removeObserver(PREF_FEED_SELECTED_WEB, this);
    Services.prefs.removeObserver(PREF_FEED_SELECTED_ACTION, this);
    Services.prefs.removeObserver(PREF_FEED_SELECTED_READER, this);

    Services.prefs.removeObserver(PREF_VIDEO_FEED_SELECTED_APP, this);
    Services.prefs.removeObserver(PREF_VIDEO_FEED_SELECTED_WEB, this);
    Services.prefs.removeObserver(PREF_VIDEO_FEED_SELECTED_ACTION, this);
    Services.prefs.removeObserver(PREF_VIDEO_FEED_SELECTED_READER, this);

    Services.prefs.removeObserver(PREF_AUDIO_FEED_SELECTED_APP, this);
    Services.prefs.removeObserver(PREF_AUDIO_FEED_SELECTED_WEB, this);
    Services.prefs.removeObserver(PREF_AUDIO_FEED_SELECTED_ACTION, this);
    Services.prefs.removeObserver(PREF_AUDIO_FEED_SELECTED_READER, this);

    Services.prefs.removeObserver(PREF_CONTAINERS_EXTENSION, this);
  },


  // nsISupports

  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsIDOMEventListener]),

  // nsIObserver

  async observe(aSubject, aTopic, aData) {
    if (aTopic == "nsPref:changed") {
      if (aData == PREF_CONTAINERS_EXTENSION) {
        this.readBrowserContainersCheckbox();
        return;
      }
      // Rebuild the list when there are changes to preferences that influence
      // whether or not to show certain entries in the list.
      if (!this._storingAction) {
        // These two prefs alter the list of visible types, so we have to rebuild
        // that list when they change.
        if (aData == PREF_SHOW_PLUGINS_IN_LIST ||
          aData == PREF_HIDE_PLUGINS_WITHOUT_EXTENSIONS) {
          await this._rebuildVisibleTypes();
          this._sortVisibleTypes();
        }

        // All the prefs we observe can affect what we display, so we rebuild
        // the view when any of them changes.
        this._rebuildView();
      }
      if (AppConstants.MOZ_UPDATER) {
        this.updateReadPrefs();
      }
    }
  },


  // nsIDOMEventListener

  handleEvent(aEvent) {
    if (aEvent.type == "unload") {
      this.destroy();
    }
  },


  // Composed Model Construction

  _loadData() {
    this._loadFeedHandler();
    this._loadInternalHandlers();
    this._loadPluginHandlers();
    this._loadApplicationHandlers();
  },

  _loadFeedHandler() {
    this._handledTypes[TYPE_MAYBE_FEED] = feedHandlerInfo;
    feedHandlerInfo.handledOnlyByPlugin = false;

    this._handledTypes[TYPE_MAYBE_VIDEO_FEED] = videoFeedHandlerInfo;
    videoFeedHandlerInfo.handledOnlyByPlugin = false;

    this._handledTypes[TYPE_MAYBE_AUDIO_FEED] = audioFeedHandlerInfo;
    audioFeedHandlerInfo.handledOnlyByPlugin = false;
  },

  /**
   * Load higher level internal handlers so they can be turned on/off in the
   * applications menu.
   */
  _loadInternalHandlers() {
    var internalHandlers = [pdfHandlerInfo];
    for (let internalHandler of internalHandlers) {
      if (internalHandler.enabled) {
        this._handledTypes[internalHandler.type] = internalHandler;
      }
    }
  },

  /**
   * Load the set of handlers defined by plugins.
   *
   * Note: if there's more than one plugin for a given MIME type, we assume
   * the last one is the one that the application will use.  That may not be
   * correct, but it's how we've been doing it for years.
   *
   * Perhaps we should instead query navigator.mimeTypes for the set of types
   * supported by the application and then get the plugin from each MIME type's
   * enabledPlugin property.  But if there's a plugin for a type, we need
   * to know about it even if it isn't enabled, since we're going to give
   * the user an option to enable it.
   *
   * Also note that enabledPlugin does not get updated when
   * plugin.disable_full_page_plugin_for_types changes, so even if we could use
   * enabledPlugin to get the plugin that would be used, we'd still need to
   * check the pref ourselves to find out if it's enabled.
   */
  _loadPluginHandlers() {
    "use strict";

    let mimeTypes = navigator.mimeTypes;

    for (let mimeType of mimeTypes) {
      let handlerInfoWrapper;
      if (mimeType.type in this._handledTypes) {
        handlerInfoWrapper = this._handledTypes[mimeType.type];
      } else {
        let wrappedHandlerInfo =
          this._mimeSvc.getFromTypeAndExtension(mimeType.type, null);
        handlerInfoWrapper = new HandlerInfoWrapper(mimeType.type, wrappedHandlerInfo);
        handlerInfoWrapper.handledOnlyByPlugin = true;
        this._handledTypes[mimeType.type] = handlerInfoWrapper;
      }
      handlerInfoWrapper.pluginName = mimeType.enabledPlugin.name;
    }
  },

  /**
   * Load the set of handlers defined by the application datastore.
   */
  _loadApplicationHandlers() {
    var wrappedHandlerInfos = this._handlerSvc.enumerate();
    while (wrappedHandlerInfos.hasMoreElements()) {
      let wrappedHandlerInfo =
        wrappedHandlerInfos.getNext().QueryInterface(Ci.nsIHandlerInfo);
      let type = wrappedHandlerInfo.type;

      let handlerInfoWrapper;
      if (type in this._handledTypes)
        handlerInfoWrapper = this._handledTypes[type];
      else {
        handlerInfoWrapper = new HandlerInfoWrapper(type, wrappedHandlerInfo);
        this._handledTypes[type] = handlerInfoWrapper;
      }

      handlerInfoWrapper.handledOnlyByPlugin = false;
    }
  },


  // View Construction

  async _rebuildVisibleTypes() {
    // Reset the list of visible types and the visible type description counts.
    this._visibleTypes = [];
    this._visibleTypeDescriptionCount = {};

    // Get the preferences that help determine what types to show.
    var showPlugins = Services.prefs.getBoolPref(PREF_SHOW_PLUGINS_IN_LIST);
    var hidePluginsWithoutExtensions =
      Services.prefs.getBoolPref(PREF_HIDE_PLUGINS_WITHOUT_EXTENSIONS);

    for (let type in this._handledTypes) {
      // Yield before processing each handler info object to avoid monopolizing
      // the main thread, as the objects are retrieved lazily, and retrieval
      // can be expensive on Windows.
      await new Promise(resolve => Services.tm.dispatchToMainThread(resolve));

      let handlerInfo = this._handledTypes[type];

      // Hide plugins without associated extensions if so prefed so we don't
      // show a whole bunch of obscure types handled by plugins on Mac.
      // Note: though protocol types don't have extensions, we still show them;
      // the pref is only meant to be applied to MIME types, since plugins are
      // only associated with MIME types.
      // FIXME: should we also check the "suffixes" property of the plugin?
      // Filed as bug 395135.
      if (hidePluginsWithoutExtensions && handlerInfo.handledOnlyByPlugin &&
        handlerInfo.wrappedHandlerInfo instanceof Ci.nsIMIMEInfo &&
        !handlerInfo.primaryExtension)
        continue;

      // Hide types handled only by plugins if so prefed.
      if (handlerInfo.handledOnlyByPlugin && !showPlugins)
        continue;

      // We couldn't find any reason to exclude the type, so include it.
      this._visibleTypes.push(handlerInfo);

      if (handlerInfo.description in this._visibleTypeDescriptionCount)
        this._visibleTypeDescriptionCount[handlerInfo.description]++;
      else
        this._visibleTypeDescriptionCount[handlerInfo.description] = 1;
    }
  },

  _rebuildView() {
    // Clear the list of entries.
    while (this._list.childNodes.length > 1)
      this._list.removeChild(this._list.lastChild);

    var visibleTypes = this._visibleTypes;

    // If the user is filtering the list, then only show matching types.
    if (this._filter.value)
      visibleTypes = visibleTypes.filter(this._matchesFilter, this);

    for (let visibleType of visibleTypes) {
      let item = document.createElement("richlistitem");
      item.setAttribute("type", visibleType.type);
      item.setAttribute("typeDescription", this._describeType(visibleType));
      if (visibleType.smallIcon)
        item.setAttribute("typeIcon", visibleType.smallIcon);
      item.setAttribute("actionDescription",
        this._describePreferredAction(visibleType));

      if (!this._setIconClassForPreferredAction(visibleType, item)) {
        item.setAttribute("actionIcon",
          this._getIconURLForPreferredAction(visibleType));
      }

      this._list.appendChild(item);
    }

    this._selectLastSelectedType();
  },

  _matchesFilter(aType) {
    var filterValue = this._filter.value.toLowerCase();
    return this._describeType(aType).toLowerCase().includes(filterValue) ||
      this._describePreferredAction(aType).toLowerCase().includes(filterValue);
  },

  /**
   * Describe, in a human-readable fashion, the type represented by the given
   * handler info object.  Normally this is just the description provided by
   * the info object, but if more than one object presents the same description,
   * then we annotate the duplicate descriptions with the type itself to help
   * users distinguish between those types.
   *
   * @param aHandlerInfo {nsIHandlerInfo} the type being described
   * @returns {string} a description of the type
   */
  _describeType(aHandlerInfo) {
    if (this._visibleTypeDescriptionCount[aHandlerInfo.description] > 1)
      return this._prefsBundle.getFormattedString("typeDescriptionWithType",
        [aHandlerInfo.description,
        aHandlerInfo.type]);

    return aHandlerInfo.description;
  },

  /**
   * Describe, in a human-readable fashion, the preferred action to take on
   * the type represented by the given handler info object.
   *
   * XXX Should this be part of the HandlerInfoWrapper interface?  It would
   * violate the separation of model and view, but it might make more sense
   * nonetheless (f.e. it would make sortTypes easier).
   *
   * @param aHandlerInfo {nsIHandlerInfo} the type whose preferred action
   *                                      is being described
   * @returns {string} a description of the action
   */
  _describePreferredAction(aHandlerInfo) {
    // alwaysAskBeforeHandling overrides the preferred action, so if that flag
    // is set, then describe that behavior instead.  For most types, this is
    // the "alwaysAsk" string, but for the feed type we show something special.
    if (aHandlerInfo.alwaysAskBeforeHandling) {
      if (isFeedType(aHandlerInfo.type))
        return this._prefsBundle.getFormattedString("previewInApp",
          [this._brandShortName]);
      return this._prefsBundle.getString("alwaysAsk");
    }

    switch (aHandlerInfo.preferredAction) {
      case Ci.nsIHandlerInfo.saveToDisk:
        return this._prefsBundle.getString("saveFile");

      case Ci.nsIHandlerInfo.useHelperApp:
        var preferredApp = aHandlerInfo.preferredApplicationHandler;
        var name;
        if (preferredApp instanceof Ci.nsILocalHandlerApp)
          name = getFileDisplayName(preferredApp.executable);
        else
          name = preferredApp.name;
        return this._prefsBundle.getFormattedString("useApp", [name]);

      case Ci.nsIHandlerInfo.handleInternally:
        // For the feed type, handleInternally means live bookmarks.
        if (isFeedType(aHandlerInfo.type)) {
          return this._prefsBundle.getFormattedString("addLiveBookmarksInApp",
            [this._brandShortName]);
        }

        if (aHandlerInfo instanceof InternalHandlerInfoWrapper) {
          return this._prefsBundle.getFormattedString("previewInApp",
            [this._brandShortName]);
        }

        // For other types, handleInternally looks like either useHelperApp
        // or useSystemDefault depending on whether or not there's a preferred
        // handler app.
        if (this.isValidHandlerApp(aHandlerInfo.preferredApplicationHandler))
          return aHandlerInfo.preferredApplicationHandler.name;

        return aHandlerInfo.defaultDescription;

      // XXX Why don't we say the app will handle the type internally?
      // Is it because the app can't actually do that?  But if that's true,
      // then why would a preferredAction ever get set to this value
      // in the first place?

      case Ci.nsIHandlerInfo.useSystemDefault:
        return this._prefsBundle.getFormattedString("useDefault",
          [aHandlerInfo.defaultDescription]);

      case kActionUsePlugin:
        return this._prefsBundle.getFormattedString("usePluginIn",
          [aHandlerInfo.pluginName,
          this._brandShortName]);
      default:
        throw new Error(`Unexpected preferredAction: ${aHandlerInfo.preferredAction}`);
    }
  },

  _selectLastSelectedType() {
    // If the list is disabled by the pref.downloads.disable_button.edit_actions
    // preference being locked, then don't select the type, as that would cause
    // it to appear selected, with a different background and an actions menu
    // that makes it seem like you can choose an action for the type.
    if (this._list.disabled)
      return;

    var lastSelectedType = this._list.getAttribute("lastSelectedType");
    if (!lastSelectedType)
      return;

    var item = this._list.getElementsByAttribute("type", lastSelectedType)[0];
    if (!item)
      return;

    this._list.selectedItem = item;
  },

  /**
   * Whether or not the given handler app is valid.
   *
   * @param aHandlerApp {nsIHandlerApp} the handler app in question
   *
   * @returns {boolean} whether or not it's valid
   */
  isValidHandlerApp(aHandlerApp) {
    if (!aHandlerApp)
      return false;

    if (aHandlerApp instanceof Ci.nsILocalHandlerApp)
      return this._isValidHandlerExecutable(aHandlerApp.executable);

    if (aHandlerApp instanceof Ci.nsIWebHandlerApp)
      return aHandlerApp.uriTemplate;

    if (aHandlerApp instanceof Ci.nsIWebContentHandlerInfo)
      return aHandlerApp.uri;

    if (aHandlerApp instanceof Ci.nsIGIOMimeApp)
      return aHandlerApp.command;

    return false;
  },

  _isValidHandlerExecutable(aExecutable) {
    let leafName;
    if (AppConstants.platform == "win") {
      leafName = `${AppConstants.MOZ_APP_NAME}.exe`;
    } else if (AppConstants.platform == "macosx") {
      leafName = AppConstants.MOZ_MACBUNDLE_NAME;
    } else {
      leafName = `${AppConstants.MOZ_APP_NAME}-bin`;
    }
    return aExecutable &&
      aExecutable.exists() &&
      aExecutable.isExecutable() &&
      // XXXben - we need to compare this with the running instance executable
      //          just don't know how to do that via script...
      // XXXmano TBD: can probably add this to nsIShellService
      aExecutable.leafName != leafName;
  },

  /**
   * Rebuild the actions menu for the selected entry.  Gets called by
   * the richlistitem constructor when an entry in the list gets selected.
   */
  rebuildActionsMenu() {
    var typeItem = this._list.selectedItem;
    var handlerInfo = this._handledTypes[typeItem.type];
    var menu =
      document.getAnonymousElementByAttribute(typeItem, "class", "actionsMenu");
    var menuPopup = menu.menupopup;

    // Clear out existing items.
    while (menuPopup.hasChildNodes())
      menuPopup.removeChild(menuPopup.lastChild);

    let internalMenuItem;
    // Add the "Preview in Firefox" option for optional internal handlers.
    if (handlerInfo instanceof InternalHandlerInfoWrapper) {
      internalMenuItem = document.createElement("menuitem");
      internalMenuItem.setAttribute("action", Ci.nsIHandlerInfo.handleInternally);
      let label = this._prefsBundle.getFormattedString("previewInApp",
        [this._brandShortName]);
      internalMenuItem.setAttribute("label", label);
      internalMenuItem.setAttribute("tooltiptext", label);
      internalMenuItem.setAttribute(APP_ICON_ATTR_NAME, "ask");
      menuPopup.appendChild(internalMenuItem);
    }

    {
      var askMenuItem = document.createElement("menuitem");
      askMenuItem.setAttribute("action", Ci.nsIHandlerInfo.alwaysAsk);
      let label;
      if (isFeedType(handlerInfo.type))
        label = this._prefsBundle.getFormattedString("previewInApp",
          [this._brandShortName]);
      else
        label = this._prefsBundle.getString("alwaysAsk");
      askMenuItem.setAttribute("label", label);
      askMenuItem.setAttribute("tooltiptext", label);
      askMenuItem.setAttribute(APP_ICON_ATTR_NAME, "ask");
      menuPopup.appendChild(askMenuItem);
    }

    // Create a menu item for saving to disk.
    // Note: this option isn't available to protocol types, since we don't know
    // what it means to save a URL having a certain scheme to disk, nor is it
    // available to feeds, since the feed code doesn't implement the capability.
    if ((handlerInfo.wrappedHandlerInfo instanceof Ci.nsIMIMEInfo) &&
      !isFeedType(handlerInfo.type)) {
      var saveMenuItem = document.createElement("menuitem");
      saveMenuItem.setAttribute("action", Ci.nsIHandlerInfo.saveToDisk);
      let label = this._prefsBundle.getString("saveFile");
      saveMenuItem.setAttribute("label", label);
      saveMenuItem.setAttribute("tooltiptext", label);
      saveMenuItem.setAttribute(APP_ICON_ATTR_NAME, "save");
      menuPopup.appendChild(saveMenuItem);
    }

    // If this is the feed type, add a Live Bookmarks item.
    if (isFeedType(handlerInfo.type)) {
      internalMenuItem = document.createElement("menuitem");
      internalMenuItem.setAttribute("action", Ci.nsIHandlerInfo.handleInternally);
      let label = this._prefsBundle.getFormattedString("addLiveBookmarksInApp",
        [this._brandShortName]);
      internalMenuItem.setAttribute("label", label);
      internalMenuItem.setAttribute("tooltiptext", label);
      internalMenuItem.setAttribute(APP_ICON_ATTR_NAME, "feed");
      menuPopup.appendChild(internalMenuItem);
    }

    // Add a separator to distinguish these items from the helper app items
    // that follow them.
    let menuseparator = document.createElement("menuseparator");
    menuPopup.appendChild(menuseparator);

    // Create a menu item for the OS default application, if any.
    if (handlerInfo.hasDefaultHandler) {
      var defaultMenuItem = document.createElement("menuitem");
      defaultMenuItem.setAttribute("action", Ci.nsIHandlerInfo.useSystemDefault);
      let label = this._prefsBundle.getFormattedString("useDefault",
        [handlerInfo.defaultDescription]);
      defaultMenuItem.setAttribute("label", label);
      defaultMenuItem.setAttribute("tooltiptext", handlerInfo.defaultDescription);
      defaultMenuItem.setAttribute("image", this._getIconURLForSystemDefault(handlerInfo));

      menuPopup.appendChild(defaultMenuItem);
    }

    // Create menu items for possible handlers.
    let preferredApp = handlerInfo.preferredApplicationHandler;
    let possibleApps = handlerInfo.possibleApplicationHandlers.enumerate();
    var possibleAppMenuItems = [];
    while (possibleApps.hasMoreElements()) {
      let possibleApp = possibleApps.getNext();
      if (!this.isValidHandlerApp(possibleApp))
        continue;

      let menuItem = document.createElement("menuitem");
      menuItem.setAttribute("action", Ci.nsIHandlerInfo.useHelperApp);
      let label;
      if (possibleApp instanceof Ci.nsILocalHandlerApp)
        label = getFileDisplayName(possibleApp.executable);
      else
        label = possibleApp.name;
      label = this._prefsBundle.getFormattedString("useApp", [label]);
      menuItem.setAttribute("label", label);
      menuItem.setAttribute("tooltiptext", label);
      menuItem.setAttribute("image", this._getIconURLForHandlerApp(possibleApp));

      // Attach the handler app object to the menu item so we can use it
      // to make changes to the datastore when the user selects the item.
      menuItem.handlerApp = possibleApp;

      menuPopup.appendChild(menuItem);
      possibleAppMenuItems.push(menuItem);
    }
    // Add gio handlers
    if (Cc["@mozilla.org/gio-service;1"]) {
      let gIOSvc = Cc["@mozilla.org/gio-service;1"].
                   getService(Ci.nsIGIOService);
      var gioApps = gIOSvc.getAppsForURIScheme(typeItem.type);
      let enumerator = gioApps.enumerate();
      let possibleHandlers = handlerInfo.possibleApplicationHandlers;
      while (enumerator.hasMoreElements()) {
        let handler = enumerator.getNext().QueryInterface(Ci.nsIHandlerApp);
        // OS handler share the same name, it's most likely the same app, skipping...
        if (handler.name == handlerInfo.defaultDescription) {
          continue;
        }
        // Check if the handler is already in possibleHandlers
        let appAlreadyInHandlers = false;
        for (let i = possibleHandlers.length - 1; i >= 0; --i) {
          let app = possibleHandlers.queryElementAt(i, Ci.nsIHandlerApp);
          // nsGIOMimeApp::Equals is able to compare with nsILocalHandlerApp
          if (handler.equals(app)) {
            appAlreadyInHandlers = true;
            break;
          }
        }
        if (!appAlreadyInHandlers) {
          let menuItem = document.createElement("menuitem");
          menuItem.setAttribute("action", Ci.nsIHandlerInfo.useHelperApp);
          let label = this._prefsBundle.getFormattedString("useApp", [handler.name]);
          menuItem.setAttribute("label", label);
          menuItem.setAttribute("tooltiptext", label);
          menuItem.setAttribute("image", this._getIconURLForHandlerApp(handler));

          // Attach the handler app object to the menu item so we can use it
          // to make changes to the datastore when the user selects the item.
          menuItem.handlerApp = handler;

          menuPopup.appendChild(menuItem);
          possibleAppMenuItems.push(menuItem);
        }
      }
    }

    // Create a menu item for the plugin.
    if (handlerInfo.pluginName) {
      var pluginMenuItem = document.createElement("menuitem");
      pluginMenuItem.setAttribute("action", kActionUsePlugin);
      let label = this._prefsBundle.getFormattedString("usePluginIn",
        [handlerInfo.pluginName,
        this._brandShortName]);
      pluginMenuItem.setAttribute("label", label);
      pluginMenuItem.setAttribute("tooltiptext", label);
      pluginMenuItem.setAttribute(APP_ICON_ATTR_NAME, "plugin");
      menuPopup.appendChild(pluginMenuItem);
    }

    // Create a menu item for selecting a local application.
    let canOpenWithOtherApp = true;
    if (AppConstants.platform == "win") {
      // On Windows, selecting an application to open another application
      // would be meaningless so we special case executables.
      let executableType = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService)
        .getTypeFromExtension("exe");
      canOpenWithOtherApp = handlerInfo.type != executableType;
    }
    if (canOpenWithOtherApp) {
      let menuItem = document.createElement("menuitem");
      menuItem.className = "choose-app-item";
      menuItem.addEventListener("command", function(e) {
        gMainPane.chooseApp(e);
      });
      let label = this._prefsBundle.getString("useOtherApp");
      menuItem.setAttribute("label", label);
      menuItem.setAttribute("tooltiptext", label);
      menuPopup.appendChild(menuItem);
    }

    // Create a menu item for managing applications.
    if (possibleAppMenuItems.length) {
      let menuItem = document.createElement("menuseparator");
      menuPopup.appendChild(menuItem);
      menuItem = document.createElement("menuitem");
      menuItem.className = "manage-app-item";
      menuItem.addEventListener("command", function(e) {
        gMainPane.manageApp(e);
      });
      menuItem.setAttribute("label", this._prefsBundle.getString("manageApp"));
      menuPopup.appendChild(menuItem);
    }

    // Select the item corresponding to the preferred action.  If the always
    // ask flag is set, it overrides the preferred action.  Otherwise we pick
    // the item identified by the preferred action (when the preferred action
    // is to use a helper app, we have to pick the specific helper app item).
    if (handlerInfo.alwaysAskBeforeHandling)
      menu.selectedItem = askMenuItem;
    else switch (handlerInfo.preferredAction) {
      case Ci.nsIHandlerInfo.handleInternally:
        if (internalMenuItem) {
          menu.selectedItem = internalMenuItem;
        } else {
          Cu.reportError("No menu item defined to set!");
        }
        break;
      case Ci.nsIHandlerInfo.useSystemDefault:
        menu.selectedItem = defaultMenuItem;
        break;
      case Ci.nsIHandlerInfo.useHelperApp:
        if (preferredApp)
          menu.selectedItem =
            possibleAppMenuItems.filter(v => v.handlerApp.equals(preferredApp))[0];
        break;
      case kActionUsePlugin:
        menu.selectedItem = pluginMenuItem;
        break;
      case Ci.nsIHandlerInfo.saveToDisk:
        menu.selectedItem = saveMenuItem;
        break;
    }
  },


  // Sorting & Filtering

  _sortColumn: null,

  /**
   * Sort the list when the user clicks on a column header.
   */
  sort(event) {
    var column = event.target;

    // If the user clicked on a new sort column, remove the direction indicator
    // from the old column.
    if (this._sortColumn && this._sortColumn != column)
      this._sortColumn.removeAttribute("sortDirection");

    this._sortColumn = column;

    // Set (or switch) the sort direction indicator.
    if (column.getAttribute("sortDirection") == "ascending")
      column.setAttribute("sortDirection", "descending");
    else
      column.setAttribute("sortDirection", "ascending");

    this._sortVisibleTypes();
    this._rebuildView();
  },

  /**
   * Sort the list of visible types by the current sort column/direction.
   */
  _sortVisibleTypes() {
    if (!this._sortColumn)
      return;

    var t = this;

    function sortByType(a, b) {
      return t._describeType(a).toLowerCase().
        localeCompare(t._describeType(b).toLowerCase());
    }

    function sortByAction(a, b) {
      return t._describePreferredAction(a).toLowerCase().
        localeCompare(t._describePreferredAction(b).toLowerCase());
    }

    switch (this._sortColumn.getAttribute("value")) {
      case "type":
        this._visibleTypes.sort(sortByType);
        break;
      case "action":
        this._visibleTypes.sort(sortByAction);
        break;
    }

    if (this._sortColumn.getAttribute("sortDirection") == "descending")
      this._visibleTypes.reverse();
  },

  /**
   * Filter the list when the user enters a filter term into the filter field.
   */
  filter() {
    this._rebuildView();
  },

  focusFilterBox() {
    this._filter.focus();
    this._filter.select();
  },


  // Changes

  onSelectAction(aActionItem) {
    this._storingAction = true;

    try {
      this._storeAction(aActionItem);
    } finally {
      this._storingAction = false;
    }
  },

  _storeAction(aActionItem) {
    var typeItem = this._list.selectedItem;
    var handlerInfo = this._handledTypes[typeItem.type];

    let action = parseInt(aActionItem.getAttribute("action"));

    // Set the plugin state if we're enabling or disabling a plugin.
    if (action == kActionUsePlugin)
      handlerInfo.enablePluginType();
    else if (handlerInfo.pluginName && !handlerInfo.isDisabledPluginType)
      handlerInfo.disablePluginType();

    // Set the preferred application handler.
    // We leave the existing preferred app in the list when we set
    // the preferred action to something other than useHelperApp so that
    // legacy datastores that don't have the preferred app in the list
    // of possible apps still include the preferred app in the list of apps
    // the user can choose to handle the type.
    if (action == Ci.nsIHandlerInfo.useHelperApp)
      handlerInfo.preferredApplicationHandler = aActionItem.handlerApp;

    // Set the "always ask" flag.
    if (action == Ci.nsIHandlerInfo.alwaysAsk)
      handlerInfo.alwaysAskBeforeHandling = true;
    else
      handlerInfo.alwaysAskBeforeHandling = false;

    // Set the preferred action.
    handlerInfo.preferredAction = action;

    handlerInfo.store();

    // Make sure the handler info object is flagged to indicate that there is
    // now some user configuration for the type.
    handlerInfo.handledOnlyByPlugin = false;

    // Update the action label and image to reflect the new preferred action.
    typeItem.setAttribute("actionDescription",
      this._describePreferredAction(handlerInfo));
    if (!this._setIconClassForPreferredAction(handlerInfo, typeItem)) {
      typeItem.setAttribute("actionIcon",
        this._getIconURLForPreferredAction(handlerInfo));
    }
  },

  manageApp(aEvent) {
    // Don't let the normal "on select action" handler get this event,
    // as we handle it specially ourselves.
    aEvent.stopPropagation();

    var typeItem = this._list.selectedItem;
    var handlerInfo = this._handledTypes[typeItem.type];

    let onComplete = () => {
      // Rebuild the actions menu so that we revert to the previous selection,
      // or "Always ask" if the previous default application has been removed
      this.rebuildActionsMenu();

      // update the richlistitem too. Will be visible when selecting another row
      typeItem.setAttribute("actionDescription",
        this._describePreferredAction(handlerInfo));
      if (!this._setIconClassForPreferredAction(handlerInfo, typeItem)) {
        typeItem.setAttribute("actionIcon",
          this._getIconURLForPreferredAction(handlerInfo));
      }
    };

    gSubDialog.open("chrome://browser/content/preferences/applicationManager.xul",
      "resizable=no", handlerInfo, onComplete);

  },

  chooseApp(aEvent) {
    // Don't let the normal "on select action" handler get this event,
    // as we handle it specially ourselves.
    aEvent.stopPropagation();

    var handlerApp;
    let chooseAppCallback = aHandlerApp => {
      // Rebuild the actions menu whether the user picked an app or canceled.
      // If they picked an app, we want to add the app to the menu and select it.
      // If they canceled, we want to go back to their previous selection.
      this.rebuildActionsMenu();

      // If the user picked a new app from the menu, select it.
      if (aHandlerApp) {
        let typeItem = this._list.selectedItem;
        let actionsMenu =
          document.getAnonymousElementByAttribute(typeItem, "class", "actionsMenu");
        let menuItems = actionsMenu.menupopup.childNodes;
        for (let i = 0; i < menuItems.length; i++) {
          let menuItem = menuItems[i];
          if (menuItem.handlerApp && menuItem.handlerApp.equals(aHandlerApp)) {
            actionsMenu.selectedIndex = i;
            this.onSelectAction(menuItem);
            break;
          }
        }
      }
    };

    if (AppConstants.platform == "win") {
      var params = {};
      var handlerInfo = this._handledTypes[this._list.selectedItem.type];

      if (isFeedType(handlerInfo.type)) {
        // MIME info will be null, create a temp object.
        params.mimeInfo = this._mimeSvc.getFromTypeAndExtension(handlerInfo.type,
          handlerInfo.primaryExtension);
      } else {
        params.mimeInfo = handlerInfo.wrappedHandlerInfo;
      }

      params.title = this._prefsBundle.getString("fpTitleChooseApp");
      params.description = handlerInfo.description;
      params.filename = null;
      params.handlerApp = null;

      let onAppSelected = () => {
        if (this.isValidHandlerApp(params.handlerApp)) {
          handlerApp = params.handlerApp;

          // Add the app to the type's list of possible handlers.
          handlerInfo.addPossibleApplicationHandler(handlerApp);
        }

        chooseAppCallback(handlerApp);
      };

      gSubDialog.open("chrome://global/content/appPicker.xul",
        null, params, onAppSelected);
    } else {
      let winTitle = this._prefsBundle.getString("fpTitleChooseApp");
      let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
      let fpCallback = aResult => {
        if (aResult == Ci.nsIFilePicker.returnOK && fp.file &&
          this._isValidHandlerExecutable(fp.file)) {
          handlerApp = Cc["@mozilla.org/uriloader/local-handler-app;1"].
            createInstance(Ci.nsILocalHandlerApp);
          handlerApp.name = getFileDisplayName(fp.file);
          handlerApp.executable = fp.file;

          // Add the app to the type's list of possible handlers.
          let handler = this._handledTypes[this._list.selectedItem.type];
          handler.addPossibleApplicationHandler(handlerApp);

          chooseAppCallback(handlerApp);
        }
      };

      // Prompt the user to pick an app.  If they pick one, and it's a valid
      // selection, then add it to the list of possible handlers.
      fp.init(window, winTitle, Ci.nsIFilePicker.modeOpen);
      fp.appendFilters(Ci.nsIFilePicker.filterApps);
      fp.open(fpCallback);
    }
  },

  // Mark which item in the list was last selected so we can reselect it
  // when we rebuild the list or when the user returns to the prefpane.
  onSelectionChanged() {
    if (this._list.selectedItem)
      this._list.setAttribute("lastSelectedType",
        this._list.selectedItem.getAttribute("type"));
  },

  _setIconClassForPreferredAction(aHandlerInfo, aElement) {
    // If this returns true, the attribute that CSS sniffs for was set to something
    // so you shouldn't manually set an icon URI.
    // This removes the existing actionIcon attribute if any, even if returning false.
    aElement.removeAttribute("actionIcon");

    if (aHandlerInfo.alwaysAskBeforeHandling) {
      aElement.setAttribute(APP_ICON_ATTR_NAME, "ask");
      return true;
    }

    switch (aHandlerInfo.preferredAction) {
      case Ci.nsIHandlerInfo.saveToDisk:
        aElement.setAttribute(APP_ICON_ATTR_NAME, "save");
        return true;

      case Ci.nsIHandlerInfo.handleInternally:
        if (isFeedType(aHandlerInfo.type)) {
          aElement.setAttribute(APP_ICON_ATTR_NAME, "feed");
          return true;
        } else if (aHandlerInfo instanceof InternalHandlerInfoWrapper) {
          aElement.setAttribute(APP_ICON_ATTR_NAME, "ask");
          return true;
        }
        break;

      case kActionUsePlugin:
        aElement.setAttribute(APP_ICON_ATTR_NAME, "plugin");
        return true;
    }
    aElement.removeAttribute(APP_ICON_ATTR_NAME);
    return false;
  },

  _getIconURLForPreferredAction(aHandlerInfo) {
    switch (aHandlerInfo.preferredAction) {
      case Ci.nsIHandlerInfo.useSystemDefault:
        return this._getIconURLForSystemDefault(aHandlerInfo);

      case Ci.nsIHandlerInfo.useHelperApp:
        let preferredApp = aHandlerInfo.preferredApplicationHandler;
        if (this.isValidHandlerApp(preferredApp))
          return this._getIconURLForHandlerApp(preferredApp);
      // Explicit fall-through

      // This should never happen, but if preferredAction is set to some weird
      // value, then fall back to the generic application icon.
      default:
        return ICON_URL_APP;
    }
  },

  _getIconURLForHandlerApp(aHandlerApp) {
    if (aHandlerApp instanceof Ci.nsILocalHandlerApp)
      return this._getIconURLForFile(aHandlerApp.executable);

    if (aHandlerApp instanceof Ci.nsIWebHandlerApp)
      return this._getIconURLForWebApp(aHandlerApp.uriTemplate);

    if (aHandlerApp instanceof Ci.nsIWebContentHandlerInfo)
      return this._getIconURLForWebApp(aHandlerApp.uri);

    // We know nothing about other kinds of handler apps.
    return "";
  },

  _getIconURLForFile(aFile) {
    var fph = Services.io.getProtocolHandler("file").
      QueryInterface(Ci.nsIFileProtocolHandler);
    var urlSpec = fph.getURLSpecFromFile(aFile);

    return "moz-icon://" + urlSpec + "?size=16";
  },

  _getIconURLForWebApp(aWebAppURITemplate) {
    var uri = Services.io.newURI(aWebAppURITemplate);

    // Unfortunately we can't use the favicon service to get the favicon,
    // because the service looks in the annotations table for a record with
    // the exact URL we give it, and users won't have such records for URLs
    // they don't visit, and users won't visit the web app's URL template,
    // they'll only visit URLs derived from that template (i.e. with %s
    // in the template replaced by the URL of the content being handled).

    if (/^https?$/.test(uri.scheme) && Services.prefs.getBoolPref("browser.chrome.favicons"))
      return uri.prePath + "/favicon.ico";

    return "";
  },

  _getIconURLForSystemDefault(aHandlerInfo) {
    // Handler info objects for MIME types on some OSes implement a property bag
    // interface from which we can get an icon for the default app, so if we're
    // dealing with a MIME type on one of those OSes, then try to get the icon.
    if ("wrappedHandlerInfo" in aHandlerInfo) {
      let wrappedHandlerInfo = aHandlerInfo.wrappedHandlerInfo;

      if (wrappedHandlerInfo instanceof Ci.nsIMIMEInfo &&
        wrappedHandlerInfo instanceof Ci.nsIPropertyBag) {
        try {
          let url = wrappedHandlerInfo.getProperty("defaultApplicationIconURL");
          if (url)
            return url + "?size=16";
        } catch (ex) { }
      }
    }

    // If this isn't a MIME type object on an OS that supports retrieving
    // the icon, or if we couldn't retrieve the icon for some other reason,
    // then use a generic icon.
    return ICON_URL_APP;
  },

  // DOWNLOADS

  /*
   * Preferences:
   *
   * browser.download.useDownloadDir - bool
   *   True - Save files directly to the folder configured via the
   *   browser.download.folderList preference.
   *   False - Always ask the user where to save a file and default to
   *   browser.download.lastDir when displaying a folder picker dialog.
   * browser.download.dir - local file handle
   *   A local folder the user may have selected for downloaded files to be
   *   saved. Migration of other browser settings may also set this path.
   *   This folder is enabled when folderList equals 2.
   * browser.download.lastDir - local file handle
   *   May contain the last folder path accessed when the user browsed
   *   via the file save-as dialog. (see contentAreaUtils.js)
   * browser.download.folderList - int
   *   Indicates the location users wish to save downloaded files too.
   *   It is also used to display special file labels when the default
   *   download location is either the Desktop or the Downloads folder.
   *   Values:
   *     0 - The desktop is the default download location.
   *     1 - The system's downloads folder is the default download location.
   *     2 - The default download location is elsewhere as specified in
   *         browser.download.dir.
   *     3 - The default download location is elsewhere as specified by
   *         cloud storage API getDownloadFolder
   * browser.download.downloadDir
   *   deprecated.
   * browser.download.defaultFolder
   *   deprecated.
   */

  /**
   * Enables/disables the folder field and Browse button based on whether a
   * default download directory is being used.
   */
  readUseDownloadDir() {
    var downloadFolder = document.getElementById("downloadFolder");
    var chooseFolder = document.getElementById("chooseFolder");
    var preference = Preferences.get("browser.download.useDownloadDir");
    downloadFolder.disabled = !preference.value || preference.locked;
    chooseFolder.disabled = !preference.value || preference.locked;

    this.readCloudStorage().catch(Cu.reportError);
    // don't override the preference's value in UI
    return undefined;
  },

  /**
   * Show/Hide the cloud storage radio button with provider name as label if
   * cloud storage provider is in use.
   * Select cloud storage radio button if browser.download.useDownloadDir is true
   * and browser.download.folderList has value 3. Enables/disables the folder field
   * and Browse button if cloud storage radio button is selected.
   *
   */
  async readCloudStorage() {
    // Get preferred provider in use display name
    let providerDisplayName = await CloudStorage.getProviderIfInUse();
    if (providerDisplayName) {
      // Show cloud storage radio button with provider name in label
      let saveToCloudRadio = document.getElementById("saveToCloud");
      let cloudStrings = Services.strings.createBundle("resource://cloudstorage/preferences.properties");
      saveToCloudRadio.label = cloudStrings.formatStringFromName("saveFilesToCloudStorage",
        [providerDisplayName], 1);
      saveToCloudRadio.hidden = false;

      let useDownloadDirPref = Preferences.get("browser.download.useDownloadDir");
      let folderListPref = Preferences.get("browser.download.folderList");

      // Check if useDownloadDir is true and folderListPref is set to Cloud Storage value 3
      // before selecting cloudStorageradio button. Disable folder field and Browse button if
      // 'Save to Cloud Storage Provider' radio option is selected
      if (useDownloadDirPref.value && folderListPref.value === 3) {
        document.getElementById("saveWhere").selectedItem = saveToCloudRadio;
        document.getElementById("downloadFolder").disabled = true;
        document.getElementById("chooseFolder").disabled = true;
      }
    }
  },

  /**
   * Handle clicks to 'Save To <custom path> or <system default downloads>' and
   * 'Save to <cloud storage provider>' if cloud storage radio button is displayed in UI.
   * Sets browser.download.folderList value and Enables/disables the folder field and Browse
   * button based on option selected.
   */
  handleSaveToCommand(event) {
    return this.handleSaveToCommandTask(event).catch(Cu.reportError);
  },
  async handleSaveToCommandTask(event) {
    if (event.target.id !== "saveToCloud" && event.target.id !== "saveTo") {
      return;
    }
    // Check if Save To Cloud Storage Provider radio option is displayed in UI
    // before continuing.
    let saveToCloudRadio = document.getElementById("saveToCloud");
    if (!saveToCloudRadio.hidden) {
      // When switching between SaveTo and SaveToCloud radio button
      // with useDownloadDirPref value true, if selectedIndex is other than
      // SaveTo radio button disable downloadFolder filefield and chooseFolder button
      let saveWhere = document.getElementById("saveWhere");
      let useDownloadDirPref = Preferences.get("browser.download.useDownloadDir");
      if (useDownloadDirPref.value) {
        let downloadFolder = document.getElementById("downloadFolder");
        let chooseFolder = document.getElementById("chooseFolder");
        downloadFolder.disabled = saveWhere.selectedIndex || useDownloadDirPref.locked;
        chooseFolder.disabled = saveWhere.selectedIndex || useDownloadDirPref.locked;
      }

      // Set folderListPref value depending on radio option
      // selected. folderListPref should be set to 3 if Save To Cloud Storage Provider
      // option is selected. If user switch back to 'Save To' custom path or system
      // default Downloads, check pref 'browser.download.dir' before setting respective
      // folderListPref value. If currentDirPref is unspecified folderList should
      // default to 1
      let folderListPref = Preferences.get("browser.download.folderList");
      let saveTo = document.getElementById("saveTo");
      if (saveWhere.selectedItem == saveToCloudRadio) {
        folderListPref.value = 3;
      } else if (saveWhere.selectedItem == saveTo) {
        let currentDirPref = Preferences.get("browser.download.dir");
        folderListPref.value = currentDirPref.value ? await this._folderToIndex(currentDirPref.value) : 1;
      }
    }
  },

  /**
   * Displays a file picker in which the user can choose the location where
   * downloads are automatically saved, updating preferences and UI in
   * response to the choice, if one is made.
   */
  chooseFolder() {
    return this.chooseFolderTask().catch(Cu.reportError);
  },
  async chooseFolderTask() {
    let bundlePreferences = document.getElementById("bundlePreferences");
    let title = bundlePreferences.getString("chooseDownloadFolderTitle");
    let folderListPref = Preferences.get("browser.download.folderList");
    let currentDirPref = await this._indexToFolder(folderListPref.value);
    let defDownloads = await this._indexToFolder(1);
    let fp = Cc["@mozilla.org/filepicker;1"].
      createInstance(Ci.nsIFilePicker);

    fp.init(window, title, Ci.nsIFilePicker.modeGetFolder);
    fp.appendFilters(Ci.nsIFilePicker.filterAll);
    // First try to open what's currently configured
    if (currentDirPref && currentDirPref.exists()) {
      fp.displayDirectory = currentDirPref;
    } else if (defDownloads && defDownloads.exists()) {
      // Try the system's download dir
      fp.displayDirectory = defDownloads;
    } else {
      // Fall back to Desktop
      fp.displayDirectory = await this._indexToFolder(0);
    }

    let result = await new Promise(resolve => fp.open(resolve));
    if (result != Ci.nsIFilePicker.returnOK) {
      return;
    }

    let downloadDirPref = Preferences.get("browser.download.dir");
    downloadDirPref.value = fp.file;
    folderListPref.value = await this._folderToIndex(fp.file);
    // Note, the real prefs will not be updated yet, so dnld manager's
    // userDownloadsDirectory may not return the right folder after
    // this code executes. displayDownloadDirPref will be called on
    // the assignment above to update the UI.
  },

  /**
   * Initializes the download folder display settings based on the user's
   * preferences.
   */
  displayDownloadDirPref() {
    this.displayDownloadDirPrefTask().catch(Cu.reportError);

    // don't override the preference's value in UI
    return undefined;
  },

  async displayDownloadDirPrefTask() {
    var folderListPref = Preferences.get("browser.download.folderList");
    var bundlePreferences = document.getElementById("bundlePreferences");
    var downloadFolder = document.getElementById("downloadFolder");
    var currentDirPref = Preferences.get("browser.download.dir");

    // Used in defining the correct path to the folder icon.
    var fph = Services.io.getProtocolHandler("file")
      .QueryInterface(Ci.nsIFileProtocolHandler);
    var iconUrlSpec;

    let folderIndex = folderListPref.value;
    if (folderIndex == 3) {
      // When user has selected cloud storage, use value in currentDirPref to
      // compute index to display download folder label and icon to avoid
      // displaying blank downloadFolder label and icon on load of preferences UI
      // Set folderIndex to 1 if currentDirPref is unspecified
      folderIndex = currentDirPref.value ? await this._folderToIndex(currentDirPref.value) : 1;
    }

    // Display a 'pretty' label or the path in the UI.
    if (folderIndex == 2) {
      // Custom path selected and is configured
      downloadFolder.label = this._getDisplayNameOfFile(currentDirPref.value);
      iconUrlSpec = fph.getURLSpecFromFile(currentDirPref.value);
    } else if (folderIndex == 1) {
      // 'Downloads'
      downloadFolder.label = bundlePreferences.getString("downloadsFolderName");
      iconUrlSpec = fph.getURLSpecFromFile(await this._indexToFolder(1));
    } else {
      // 'Desktop'
      downloadFolder.label = bundlePreferences.getString("desktopFolderName");
      iconUrlSpec = fph.getURLSpecFromFile(await this._getDownloadsFolder("Desktop"));
    }
    downloadFolder.image = "moz-icon://" + iconUrlSpec + "?size=16";
  },

  /**
   * Returns the textual path of a folder in readable form.
   */
  _getDisplayNameOfFile(aFolder) {
    // TODO: would like to add support for 'Downloads on Macintosh HD'
    //       for OS X users.
    return aFolder ? aFolder.path : "";
  },

  /**
   * Returns the Downloads folder.  If aFolder is "Desktop", then the Downloads
   * folder returned is the desktop folder; otherwise, it is a folder whose name
   * indicates that it is a download folder and whose path is as determined by
   * the XPCOM directory service via the download manager's attribute
   * defaultDownloadsDirectory.
   *
   * @throws if aFolder is not "Desktop" or "Downloads"
   */
  async _getDownloadsFolder(aFolder) {
    switch (aFolder) {
      case "Desktop":
        return Services.dirsvc.get("Desk", Ci.nsIFile);
      case "Downloads":
        let downloadsDir = await Downloads.getSystemDownloadsDirectory();
        return new FileUtils.File(downloadsDir);
    }
    throw "ASSERTION FAILED: folder type should be 'Desktop' or 'Downloads'";
  },

  /**
   * Determines the type of the given folder.
   *
   * @param   aFolder
   *          the folder whose type is to be determined
   * @returns integer
   *          0 if aFolder is the Desktop or is unspecified,
   *          1 if aFolder is the Downloads folder,
   *          2 otherwise
   */
  async _folderToIndex(aFolder) {
    if (!aFolder || aFolder.equals(await this._getDownloadsFolder("Desktop")))
      return 0;
    else if (aFolder.equals(await this._getDownloadsFolder("Downloads")))
      return 1;
    return 2;
  },

  /**
   * Converts an integer into the corresponding folder.
   *
   * @param   aIndex
   *          an integer
   * @returns the Desktop folder if aIndex == 0,
   *          the Downloads folder if aIndex == 1,
   *          the folder stored in browser.download.dir
   */
  _indexToFolder(aIndex) {
    switch (aIndex) {
      case 0:
        return this._getDownloadsFolder("Desktop");
      case 1:
        return this._getDownloadsFolder("Downloads");
    }
    var currentDirPref = Preferences.get("browser.download.dir");
    return currentDirPref.value;
  }
};

// Utilities

function getFileDisplayName(file) {
  if (AppConstants.platform == "win") {
    if (file instanceof Ci.nsILocalFileWin) {
      try {
        return file.getVersionInfoField("FileDescription");
      } catch (e) { }
    }
  }
  if (AppConstants.platform == "macosx") {
    if (file instanceof Ci.nsILocalFileMac) {
      try {
        return file.bundleDisplayName;
      } catch (e) { }
    }
  }
  return file.leafName;
}

function getLocalHandlerApp(aFile) {
  var localHandlerApp = Cc["@mozilla.org/uriloader/local-handler-app;1"].
    createInstance(Ci.nsILocalHandlerApp);
  localHandlerApp.name = getFileDisplayName(aFile);
  localHandlerApp.executable = aFile;

  return localHandlerApp;
}

/**
 * An enumeration of items in a JS array.
 *
 * FIXME: use ArrayConverter once it lands (bug 380839).
 *
 * @constructor
 */
function ArrayEnumerator(aItems) {
  this._index = 0;
  this._contents = aItems;
}

ArrayEnumerator.prototype = {
  _index: 0,

  hasMoreElements() {
    return this._index < this._contents.length;
  },

  getNext() {
    return this._contents[this._index++];
  }
};

function isFeedType(t) {
  return t == TYPE_MAYBE_FEED || t == TYPE_MAYBE_VIDEO_FEED || t == TYPE_MAYBE_AUDIO_FEED;
}

// HandlerInfoWrapper

/**
 * This object wraps nsIHandlerInfo with some additional functionality
 * the Applications prefpane needs to display and allow modification of
 * the list of handled types.
 *
 * We create an instance of this wrapper for each entry we might display
 * in the prefpane, and we compose the instances from various sources,
 * including plugins and the handler service.
 *
 * We don't implement all the original nsIHandlerInfo functionality,
 * just the stuff that the prefpane needs.
 *
 * In theory, all of the custom functionality in this wrapper should get
 * pushed down into nsIHandlerInfo eventually.
 */
function HandlerInfoWrapper(aType, aHandlerInfo) {
  this._type = aType;
  this.wrappedHandlerInfo = aHandlerInfo;
}

HandlerInfoWrapper.prototype = {
  // The wrapped nsIHandlerInfo object.  In general, this object is private,
  // but there are a couple cases where callers access it directly for things
  // we haven't (yet?) implemented, so we make it a public property.
  wrappedHandlerInfo: null,


  // Convenience Utils

  _handlerSvc: Cc["@mozilla.org/uriloader/handler-service;1"].
    getService(Ci.nsIHandlerService),

  _categoryMgr: Cc["@mozilla.org/categorymanager;1"].
    getService(Ci.nsICategoryManager),

  element(aID) {
    return document.getElementById(aID);
  },


  // nsIHandlerInfo

  // The MIME type or protocol scheme.
  _type: null,
  get type() {
    return this._type;
  },

  get description() {
    if (this.wrappedHandlerInfo.description)
      return this.wrappedHandlerInfo.description;

    if (this.primaryExtension) {
      var extension = this.primaryExtension.toUpperCase();
      return this.element("bundlePreferences").getFormattedString("fileEnding",
        [extension]);
    }

    return this.type;
  },

  get preferredApplicationHandler() {
    return this.wrappedHandlerInfo.preferredApplicationHandler;
  },

  set preferredApplicationHandler(aNewValue) {
    this.wrappedHandlerInfo.preferredApplicationHandler = aNewValue;

    // Make sure the preferred handler is in the set of possible handlers.
    if (aNewValue)
      this.addPossibleApplicationHandler(aNewValue);
  },

  get possibleApplicationHandlers() {
    return this.wrappedHandlerInfo.possibleApplicationHandlers;
  },

  addPossibleApplicationHandler(aNewHandler) {
    var possibleApps = this.possibleApplicationHandlers.enumerate();
    while (possibleApps.hasMoreElements()) {
      if (possibleApps.getNext().equals(aNewHandler))
        return;
    }
    this.possibleApplicationHandlers.appendElement(aNewHandler);
  },

  removePossibleApplicationHandler(aHandler) {
    var defaultApp = this.preferredApplicationHandler;
    if (defaultApp && aHandler.equals(defaultApp)) {
      // If the app we remove was the default app, we must make sure
      // it won't be used anymore
      this.alwaysAskBeforeHandling = true;
      this.preferredApplicationHandler = null;
    }

    var handlers = this.possibleApplicationHandlers;
    for (var i = 0; i < handlers.length; ++i) {
      var handler = handlers.queryElementAt(i, Ci.nsIHandlerApp);
      if (handler.equals(aHandler)) {
        handlers.removeElementAt(i);
        break;
      }
    }
  },

  get hasDefaultHandler() {
    return this.wrappedHandlerInfo.hasDefaultHandler;
  },

  get defaultDescription() {
    return this.wrappedHandlerInfo.defaultDescription;
  },

  // What to do with content of this type.
  get preferredAction() {
    // If we have an enabled plugin, then the action is to use that plugin.
    if (this.pluginName && !this.isDisabledPluginType)
      return kActionUsePlugin;

    // If the action is to use a helper app, but we don't have a preferred
    // handler app, then switch to using the system default, if any; otherwise
    // fall back to saving to disk, which is the default action in nsMIMEInfo.
    // Note: "save to disk" is an invalid value for protocol info objects,
    // but the alwaysAskBeforeHandling getter will detect that situation
    // and always return true in that case to override this invalid value.
    if (this.wrappedHandlerInfo.preferredAction == Ci.nsIHandlerInfo.useHelperApp &&
      !gMainPane.isValidHandlerApp(this.preferredApplicationHandler)) {
      if (this.wrappedHandlerInfo.hasDefaultHandler)
        return Ci.nsIHandlerInfo.useSystemDefault;
      return Ci.nsIHandlerInfo.saveToDisk;
    }

    return this.wrappedHandlerInfo.preferredAction;
  },

  set preferredAction(aNewValue) {
    // If the action is to use the plugin,
    // we must set the preferred action to "save to disk".
    // But only if it's not currently the preferred action.
    if ((aNewValue == kActionUsePlugin) &&
      (this.preferredAction != Ci.nsIHandlerInfo.saveToDisk)) {
      aNewValue = Ci.nsIHandlerInfo.saveToDisk;
    }

    // We don't modify the preferred action if the new action is to use a plugin
    // because handler info objects don't understand our custom "use plugin"
    // value.  Also, leaving it untouched means that we can automatically revert
    // to the old setting if the user ever removes the plugin.

    if (aNewValue != kActionUsePlugin)
      this.wrappedHandlerInfo.preferredAction = aNewValue;
  },

  get alwaysAskBeforeHandling() {
    // If this type is handled only by a plugin, we can't trust the value
    // in the handler info object, since it'll be a default based on the absence
    // of any user configuration, and the default in that case is to always ask,
    // even though we never ask for content handled by a plugin, so special case
    // plugin-handled types by returning false here.
    if (this.pluginName && this.handledOnlyByPlugin)
      return false;

    // If this is a protocol type and the preferred action is "save to disk",
    // which is invalid for such types, then return true here to override that
    // action.  This could happen when the preferred action is to use a helper
    // app, but the preferredApplicationHandler is invalid, and there isn't
    // a default handler, so the preferredAction getter returns save to disk
    // instead.
    if (!(this.wrappedHandlerInfo instanceof Ci.nsIMIMEInfo) &&
      this.preferredAction == Ci.nsIHandlerInfo.saveToDisk)
      return true;

    return this.wrappedHandlerInfo.alwaysAskBeforeHandling;
  },

  set alwaysAskBeforeHandling(aNewValue) {
    this.wrappedHandlerInfo.alwaysAskBeforeHandling = aNewValue;
  },


  // nsIMIMEInfo

  // The primary file extension associated with this type, if any.
  //
  // XXX Plugin objects contain an array of MimeType objects with "suffixes"
  // properties; if this object has an associated plugin, shouldn't we check
  // those properties for an extension?
  get primaryExtension() {
    try {
      if (this.wrappedHandlerInfo instanceof Ci.nsIMIMEInfo &&
        this.wrappedHandlerInfo.primaryExtension)
        return this.wrappedHandlerInfo.primaryExtension;
    } catch (ex) { }

    return null;
  },


  // Plugin Handling

  // A plugin that can handle this type, if any.
  //
  // Note: just because we have one doesn't mean it *will* handle the type.
  // That depends on whether or not the type is in the list of types for which
  // plugin handling is disabled.
  plugin: null,

  // Whether or not this type is only handled by a plugin or is also handled
  // by some user-configured action as specified in the handler info object.
  //
  // Note: we can't just check if there's a handler info object for this type,
  // because OS and user configuration is mixed up in the handler info object,
  // so we always need to retrieve it for the OS info and can't tell whether
  // it represents only OS-default information or user-configured information.
  //
  // FIXME: once handler info records are broken up into OS-provided records
  // and user-configured records, stop using this boolean flag and simply
  // check for the presence of a user-configured record to determine whether
  // or not this type is only handled by a plugin.  Filed as bug 395142.
  handledOnlyByPlugin: undefined,

  get isDisabledPluginType() {
    return this._getDisabledPluginTypes().includes(this.type);
  },

  _getDisabledPluginTypes() {
    var types = "";

    if (Services.prefs.prefHasUserValue(PREF_DISABLED_PLUGIN_TYPES))
      types = Services.prefs.getCharPref(PREF_DISABLED_PLUGIN_TYPES);

    // Only split if the string isn't empty so we don't end up with an array
    // containing a single empty string.
    if (types != "")
      return types.split(",");

    return [];
  },

  disablePluginType() {
    var disabledPluginTypes = this._getDisabledPluginTypes();

    if (!disabledPluginTypes.includes(this.type))
      disabledPluginTypes.push(this.type);

    Services.prefs.setCharPref(PREF_DISABLED_PLUGIN_TYPES,
      disabledPluginTypes.join(","));

    // Update the category manager so existing browser windows update.
    this._categoryMgr.deleteCategoryEntry("Gecko-Content-Viewers",
      this.type,
      false);
  },

  enablePluginType() {
    var disabledPluginTypes = this._getDisabledPluginTypes();

    var type = this.type;
    disabledPluginTypes = disabledPluginTypes.filter(v => v != type);

    Services.prefs.setCharPref(PREF_DISABLED_PLUGIN_TYPES,
      disabledPluginTypes.join(","));

    // Update the category manager so existing browser windows update.
    this._categoryMgr.
      addCategoryEntry("Gecko-Content-Viewers",
      this.type,
      "@mozilla.org/content/plugin/document-loader-factory;1",
      false,
      true);
  },


  // Storage

  store() {
    this._handlerSvc.store(this.wrappedHandlerInfo);
  },


  // Icons

  get smallIcon() {
    return this._getIcon(16);
  },

  _getIcon(aSize) {
    if (this.primaryExtension)
      return "moz-icon://goat." + this.primaryExtension + "?size=" + aSize;

    if (this.wrappedHandlerInfo instanceof Ci.nsIMIMEInfo)
      return "moz-icon://goat?size=" + aSize + "&contentType=" + this.type;

    // FIXME: consider returning some generic icon when we can't get a URL for
    // one (for example in the case of protocol schemes).  Filed as bug 395141.
    return null;
  }

};


// Feed Handler Info

/**
 * This object implements nsIHandlerInfo for the feed types.  It's a separate
 * object because we currently store handling information for the feed type
 * in a set of preferences rather than the nsIHandlerService-managed datastore.
 *
 * This object inherits from HandlerInfoWrapper in order to get functionality
 * that isn't special to the feed type.
 *
 * XXX Should we inherit from HandlerInfoWrapper?  After all, we override
 * most of that wrapper's properties and methods, and we have to dance around
 * the fact that the wrapper expects to have a wrappedHandlerInfo, which we
 * don't provide.
 */

function FeedHandlerInfo(aMIMEType) {
  HandlerInfoWrapper.call(this, aMIMEType, null);
}

FeedHandlerInfo.prototype = {
  __proto__: HandlerInfoWrapper.prototype,

  // Convenience Utils

  _converterSvc:
  Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"].
    getService(Ci.nsIWebContentConverterService),

  _shellSvc: AppConstants.HAVE_SHELL_SERVICE ? getShellService() : null,

  // nsIHandlerInfo

  get description() {
    return this.element("bundlePreferences").getString(this._appPrefLabel);
  },

  get preferredApplicationHandler() {
    switch (Preferences.get(this._prefSelectedReader).value) {
      case "client":
        var file = Preferences.get(this._prefSelectedApp).value;
        if (file)
          return getLocalHandlerApp(file);

        return null;

      case "web":
        var uri = Preferences.get(this._prefSelectedWeb).value;
        if (!uri)
          return null;
        return this._converterSvc.getWebContentHandlerByURI(this.type, uri);

      case "bookmarks":
      default:
        // When the pref is set to bookmarks, we handle feeds internally,
        // we don't forward them to a local or web handler app, so there is
        // no preferred handler.
        return null;
    }
  },

  set preferredApplicationHandler(aNewValue) {
    if (aNewValue instanceof Ci.nsILocalHandlerApp) {
      Preferences.get(this._prefSelectedApp).value = aNewValue.executable;
      Preferences.get(this._prefSelectedReader).value = "client";
    } else if (aNewValue instanceof Ci.nsIWebContentHandlerInfo) {
      Preferences.get(this._prefSelectedWeb).value = aNewValue.uri;
      Preferences.get(this._prefSelectedReader).value = "web";
      // Make the web handler be the new "auto handler" for feeds.
      // Note: we don't have to unregister the auto handler when the user picks
      // a non-web handler (local app, Live Bookmarks, etc.) because the service
      // only uses the "auto handler" when the selected reader is a web handler.
      // We also don't have to unregister it when the user turns on "always ask"
      // (i.e. preview in browser), since that also overrides the auto handler.
      this._converterSvc.setAutoHandler(this.type, aNewValue);
    }
  },

  _possibleApplicationHandlers: null,

  get possibleApplicationHandlers() {
    if (this._possibleApplicationHandlers)
      return this._possibleApplicationHandlers;

    // A minimal implementation of nsIMutableArray.  It only supports the two
    // methods its callers invoke, namely appendElement and nsIArray::enumerate.
    this._possibleApplicationHandlers = {
      _inner: [],
      _removed: [],

      QueryInterface(aIID) {
        if (aIID.equals(Ci.nsIMutableArray) ||
          aIID.equals(Ci.nsIArray) ||
          aIID.equals(Ci.nsISupports))
          return this;

        throw Cr.NS_ERROR_NO_INTERFACE;
      },

      get length() {
        return this._inner.length;
      },

      enumerate() {
        return new ArrayEnumerator(this._inner);
      },

      appendElement(aHandlerApp, aWeak) {
        this._inner.push(aHandlerApp);
      },

      removeElementAt(aIndex) {
        this._removed.push(this._inner[aIndex]);
        this._inner.splice(aIndex, 1);
      },

      queryElementAt(aIndex, aInterface) {
        return this._inner[aIndex].QueryInterface(aInterface);
      }
    };

    // Add the selected local app if it's different from the OS default handler.
    // Unlike for other types, we can store only one local app at a time for the
    // feed type, since we store it in a preference that historically stores
    // only a single path.  But we display all the local apps the user chooses
    // while the prefpane is open, only dropping the list when the user closes
    // the prefpane, for maximum usability and consistency with other types.
    var preferredAppFile = Preferences.get(this._prefSelectedApp).value;
    if (preferredAppFile) {
      let preferredApp = getLocalHandlerApp(preferredAppFile);
      let defaultApp = this._defaultApplicationHandler;
      if (!defaultApp || !defaultApp.equals(preferredApp))
        this._possibleApplicationHandlers.appendElement(preferredApp);
    }

    // Add the registered web handlers.  There can be any number of these.
    var webHandlers = this._converterSvc.getContentHandlers(this.type);
    for (let webHandler of webHandlers)
      this._possibleApplicationHandlers.appendElement(webHandler);

    return this._possibleApplicationHandlers;
  },

  __defaultApplicationHandler: undefined,
  get _defaultApplicationHandler() {
    if (typeof this.__defaultApplicationHandler != "undefined")
      return this.__defaultApplicationHandler;

    var defaultFeedReader = null;
    if (AppConstants.HAVE_SHELL_SERVICE) {
      try {
        defaultFeedReader = this._shellSvc.defaultFeedReader;
      } catch (ex) {
        // no default reader or _shellSvc is null
      }
    }

    if (defaultFeedReader) {
      let handlerApp = Cc["@mozilla.org/uriloader/local-handler-app;1"].
        createInstance(Ci.nsIHandlerApp);
      handlerApp.name = getFileDisplayName(defaultFeedReader);
      handlerApp.QueryInterface(Ci.nsILocalHandlerApp);
      handlerApp.executable = defaultFeedReader;

      this.__defaultApplicationHandler = handlerApp;
    } else {
      this.__defaultApplicationHandler = null;
    }

    return this.__defaultApplicationHandler;
  },

  get hasDefaultHandler() {
    if (AppConstants.HAVE_SHELL_SERVICE) {
      try {
        if (this._shellSvc.defaultFeedReader)
          return true;
      } catch (ex) {
        // no default reader or _shellSvc is null
      }
    }

    return false;
  },

  get defaultDescription() {
    if (this.hasDefaultHandler)
      return this._defaultApplicationHandler.name;

    // Should we instead return null?
    return "";
  },

  // What to do with content of this type.
  get preferredAction() {
    switch (Preferences.get(this._prefSelectedAction).value) {

      case "bookmarks":
        return Ci.nsIHandlerInfo.handleInternally;

      case "reader": {
        let preferredApp = this.preferredApplicationHandler;
        let defaultApp = this._defaultApplicationHandler;

        // If we have a valid preferred app, return useSystemDefault if it's
        // the default app; otherwise return useHelperApp.
        if (gMainPane.isValidHandlerApp(preferredApp)) {
          if (defaultApp && defaultApp.equals(preferredApp))
            return Ci.nsIHandlerInfo.useSystemDefault;

          return Ci.nsIHandlerInfo.useHelperApp;
        }

        // The pref is set to "reader", but we don't have a valid preferred app.
        // What do we do now?  Not sure this is the best option (perhaps we
        // should direct the user to the default app, if any), but for now let's
        // direct the user to live bookmarks.
        return Ci.nsIHandlerInfo.handleInternally;
      }

      // If the action is "ask", then alwaysAskBeforeHandling will override
      // the action, so it doesn't matter what we say it is, it just has to be
      // something that doesn't cause the controller to hide the type.
      case "ask":
      default:
        return Ci.nsIHandlerInfo.handleInternally;
    }
  },

  set preferredAction(aNewValue) {
    switch (aNewValue) {

      case Ci.nsIHandlerInfo.handleInternally:
        Preferences.get(this._prefSelectedReader).value = "bookmarks";
        break;

      case Ci.nsIHandlerInfo.useHelperApp:
        Preferences.get(this._prefSelectedAction).value = "reader";
        // The controller has already set preferredApplicationHandler
        // to the new helper app.
        break;

      case Ci.nsIHandlerInfo.useSystemDefault:
        Preferences.get(this._prefSelectedAction).value = "reader";
        this.preferredApplicationHandler = this._defaultApplicationHandler;
        break;
    }
  },

  get alwaysAskBeforeHandling() {
    return Preferences.get(this._prefSelectedAction).value == "ask";
  },

  set alwaysAskBeforeHandling(aNewValue) {
    if (aNewValue)
      Preferences.get(this._prefSelectedAction).value = "ask";
    else
      Preferences.get(this._prefSelectedAction).value = "reader";
  },

  // Whether or not we are currently storing the action selected by the user.
  // We use this to suppress notification-triggered updates to the list when
  // we make changes that may spawn such updates, specifically when we change
  // the action for the feed type, which results in feed preference updates,
  // which spawn "pref changed" notifications that would otherwise cause us
  // to rebuild the view unnecessarily.
  _storingAction: false,


  // nsIMIMEInfo

  get primaryExtension() {
    return "xml";
  },


  // Storage

  // Changes to the preferred action and handler take effect immediately
  // (we write them out to the preferences right as they happen),
  // so we when the controller calls store() after modifying the handlers,
  // the only thing we need to store is the removal of possible handlers
  // XXX Should we hold off on making the changes until this method gets called?
  store() {
    for (let app of this._possibleApplicationHandlers._removed) {
      if (app instanceof Ci.nsILocalHandlerApp) {
        let pref = Preferences.get(PREF_FEED_SELECTED_APP);
        var preferredAppFile = pref.value;
        if (preferredAppFile) {
          let preferredApp = getLocalHandlerApp(preferredAppFile);
          if (app.equals(preferredApp))
            pref.reset();
        }
      } else {
        app.QueryInterface(Ci.nsIWebContentHandlerInfo);
        this._converterSvc.removeContentHandler(app.contentType, app.uri);
      }
    }
    this._possibleApplicationHandlers._removed = [];
  },


  // Icons

  get smallIcon() {
    return this._smallIcon;
  }

};

var feedHandlerInfo = {
  __proto__: new FeedHandlerInfo(TYPE_MAYBE_FEED),
  _prefSelectedApp: PREF_FEED_SELECTED_APP,
  _prefSelectedWeb: PREF_FEED_SELECTED_WEB,
  _prefSelectedAction: PREF_FEED_SELECTED_ACTION,
  _prefSelectedReader: PREF_FEED_SELECTED_READER,
  _smallIcon: "chrome://browser/skin/feeds/feedIcon16.png",
  _appPrefLabel: "webFeed"
};

var videoFeedHandlerInfo = {
  __proto__: new FeedHandlerInfo(TYPE_MAYBE_VIDEO_FEED),
  _prefSelectedApp: PREF_VIDEO_FEED_SELECTED_APP,
  _prefSelectedWeb: PREF_VIDEO_FEED_SELECTED_WEB,
  _prefSelectedAction: PREF_VIDEO_FEED_SELECTED_ACTION,
  _prefSelectedReader: PREF_VIDEO_FEED_SELECTED_READER,
  _smallIcon: "chrome://browser/skin/feeds/videoFeedIcon16.png",
  _appPrefLabel: "videoPodcastFeed"
};

var audioFeedHandlerInfo = {
  __proto__: new FeedHandlerInfo(TYPE_MAYBE_AUDIO_FEED),
  _prefSelectedApp: PREF_AUDIO_FEED_SELECTED_APP,
  _prefSelectedWeb: PREF_AUDIO_FEED_SELECTED_WEB,
  _prefSelectedAction: PREF_AUDIO_FEED_SELECTED_ACTION,
  _prefSelectedReader: PREF_AUDIO_FEED_SELECTED_READER,
  _smallIcon: "chrome://browser/skin/feeds/audioFeedIcon16.png",
  _appPrefLabel: "audioPodcastFeed"
};

/**
 * InternalHandlerInfoWrapper provides a basic mechanism to create an internal
 * mime type handler that can be enabled/disabled in the applications preference
 * menu.
 */
function InternalHandlerInfoWrapper(aMIMEType) {
  var mimeSvc = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
  var handlerInfo = mimeSvc.getFromTypeAndExtension(aMIMEType, null);

  HandlerInfoWrapper.call(this, aMIMEType, handlerInfo);
}

InternalHandlerInfoWrapper.prototype = {
  __proto__: HandlerInfoWrapper.prototype,

  // Override store so we so we can notify any code listening for registration
  // or unregistration of this handler.
  store() {
    HandlerInfoWrapper.prototype.store.call(this);
    Services.obs.notifyObservers(null, this._handlerChanged);
  },

  get enabled() {
    throw Cr.NS_ERROR_NOT_IMPLEMENTED;
  },

  get description() {
    return this.element("bundlePreferences").getString(this._appPrefLabel);
  }
};

var pdfHandlerInfo = {
  __proto__: new InternalHandlerInfoWrapper(TYPE_PDF),
  _handlerChanged: TOPIC_PDFJS_HANDLER_CHANGED,
  _appPrefLabel: "portableDocumentFormat",
  get enabled() {
    return !Services.prefs.getBoolPref(PREF_PDFJS_DISABLED) &&
           Services.policies.isAllowed("PDF.js");
  },
};
PK
!<�!_Y�9�9Dchrome/browser/content/browser/preferences/in-content/preferences.js/* - This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 globals from the files imported by the .xul files.
/* import-globals-from subdialogs.js */
/* import-globals-from main.js */
/* import-globals-from search.js */
/* import-globals-from containers.js */
/* import-globals-from privacy.js */
/* import-globals-from sync.js */
/* import-globals-from findInPage.js */
/* import-globals-from ../../../base/content/utilityOverlay.js */
/* import-globals-from ../../../../toolkit/content/preferencesBindings.js */

"use strict";

ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/AppConstants.jsm");

ChromeUtils.defineModuleGetter(this, "formAutofillParent",
                               "resource://formautofill/FormAutofillParent.jsm");

var gLastHash = "";

var gCategoryInits = new Map();
function init_category_if_required(category) {
  let categoryInfo = gCategoryInits.get(category);
  if (!categoryInfo) {
    throw "Unknown in-content prefs category! Can't init " + category;
  }
  if (categoryInfo.inited) {
    return;
  }
  categoryInfo.init();
}

function register_module(categoryName, categoryObject) {
  gCategoryInits.set(categoryName, {
    inited: false,
    init() {
      categoryObject.init();
      this.inited = true;
    }
  });
}

document.addEventListener("DOMContentLoaded", init_all, {once: true});

function init_all() {
  Preferences.forceEnableInstantApply();

  gSubDialog.init();
  register_module("paneGeneral", gMainPane);
  register_module("paneSearch", gSearchPane);
  register_module("panePrivacy", gPrivacyPane);
  register_module("paneContainers", gContainersPane);
  if (Services.prefs.getBoolPref("identity.fxaccounts.enabled")) {
    document.getElementById("category-sync").hidden = false;
    document.getElementById("weavePrefsDeck").removeAttribute("data-hidden-from-search");
    register_module("paneSync", gSyncPane);
  }
  register_module("paneSearchResults", gSearchResultsPane);
  gSearchResultsPane.init();
  gMainPane.preInit();

  let categories = document.getElementById("categories");
  categories.addEventListener("select", event => gotoPref(event.target.value));

  document.documentElement.addEventListener("keydown", function(event) {
    if (event.keyCode == KeyEvent.DOM_VK_TAB) {
      categories.setAttribute("keyboard-navigation", "true");
    }
  });
  categories.addEventListener("mousedown", function() {
    this.removeAttribute("keyboard-navigation");
  });

  maybeDisplayPoliciesNotice();

  window.addEventListener("hashchange", onHashChange);
  gotoPref();

  let helpButton = document.querySelector(".help-button > .text-link");
  let helpUrl = Services.urlFormatter.formatURLPref("app.support.baseURL") + "preferences";
  helpButton.setAttribute("href", helpUrl);

  document.dispatchEvent(new CustomEvent("Initialized", {
    "bubbles": true,
    "cancelable": true
  }));
}

function telemetryBucketForCategory(category) {
  category = category.toLowerCase();
  switch (category) {
    case "containers":
    case "general":
    case "privacy":
    case "search":
    case "sync":
    case "searchresults":
      return category;
    default:
      return "unknown";
  }
}

function onHashChange() {
  gotoPref();
}

function gotoPref(aCategory) {
  let categories = document.getElementById("categories");
  const kDefaultCategoryInternalName = "paneGeneral";
  const kDefaultCategory = "general";
  let hash = document.location.hash;

  let category = aCategory || hash.substr(1) || kDefaultCategoryInternalName;
  let breakIndex = category.indexOf("-");
  // Subcategories allow for selecting smaller sections of the preferences
  // until proper search support is enabled (bug 1353954).
  let subcategory = breakIndex != -1 && category.substring(breakIndex + 1);
  if (subcategory) {
    category = category.substring(0, breakIndex);
  }
  category = friendlyPrefCategoryNameToInternalName(category);
  if (category != "paneSearchResults") {
    gSearchResultsPane.query = null;
    gSearchResultsPane.searchInput.value = "";
    gSearchResultsPane.getFindSelection(window).removeAllRanges();
    gSearchResultsPane.removeAllSearchTooltips();
    gSearchResultsPane.removeAllSearchMenuitemIndicators();
  } else if (!gSearchResultsPane.searchInput.value) {
    // Something tried to send us to the search results pane without
    // a query string. Default to the General pane instead.
    category = kDefaultCategoryInternalName;
    document.location.hash = kDefaultCategory;
    gSearchResultsPane.query = null;
  }

  // Updating the hash (below) or changing the selected category
  // will re-enter gotoPref.
  if (gLastHash == category && !subcategory)
    return;

  let item;
  if (category != "paneSearchResults") {
    item = categories.querySelector(".category[value=" + category + "]");
    if (!item) {
      category = kDefaultCategoryInternalName;
      item = categories.querySelector(".category[value=" + category + "]");
    }
  }

  try {
    init_category_if_required(category);
  } catch (ex) {
    Cu.reportError("Error initializing preference category " + category + ": " + ex);
    throw ex;
  }

  let friendlyName = internalPrefCategoryNameToFriendlyName(category);
  if (gLastHash || category != kDefaultCategoryInternalName || subcategory) {
    document.location.hash = friendlyName;
  }
  // Need to set the gLastHash before setting categories.selectedItem since
  // the categories 'select' event will re-enter the gotoPref codepath.
  gLastHash = category;
  if (item) {
    categories.selectedItem = item;
  } else {
    categories.clearSelection();
  }
  window.history.replaceState(category, document.title);
  search(category, "data-category");

  let mainContent = document.querySelector(".main-content");
  mainContent.scrollTop = 0;

  spotlight(subcategory);

  Services.telemetry
          .getHistogramById("FX_PREFERENCES_CATEGORY_OPENED_V2")
          .add(telemetryBucketForCategory(friendlyName));
}

function search(aQuery, aAttribute) {
  let mainPrefPane = document.getElementById("mainPrefPane");
  let elements = mainPrefPane.children;
  for (let element of elements) {
    // If the "data-hidden-from-search" is "true", the
    // element will not get considered during search. This
    // should only be used when an element is still under
    // development and should not be shown for any reason.
    if (element.getAttribute("data-hidden-from-search") != "true" ||
        element.getAttribute("data-subpanel") == "true") {
      let attributeValue = element.getAttribute(aAttribute);
      if (attributeValue == aQuery) {
        element.hidden = false;
      } else {
        element.hidden = true;
      }
    }
    element.classList.remove("visually-hidden");
  }

  let keysets = mainPrefPane.getElementsByTagName("keyset");
  for (let element of keysets) {
    let attributeValue = element.getAttribute(aAttribute);
    if (attributeValue == aQuery)
      element.removeAttribute("disabled");
    else
      element.setAttribute("disabled", true);
  }
}

async function spotlight(subcategory) {
  let highlightedElements = document.querySelectorAll(".spotlight");
  if (highlightedElements.length) {
    for (let element of highlightedElements) {
      element.classList.remove("spotlight");
    }
  }
  if (subcategory) {
    if (!gSearchResultsPane.categoriesInitialized) {
      await waitForSystemAddonInjectionsFinished([{
        isGoingToInject: formAutofillParent.initialized,
        elementId: "formAutofillGroup",
      }]);
    }
    scrollAndHighlight(subcategory);
  }

  /**
   * Wait for system addons finished their dom injections.
   * @param {Array} addons - The system addon information array.
   * For example, the element is looked like
   * { isGoingToInject: true, elementId: "formAutofillGroup" }.
   * The `isGoingToInject` means the system addon will be visible or not,
   * and the `elementId` means the id of the element will be injected into the dom
   * if the `isGoingToInject` is true.
   * @returns {Promise} Will resolve once all injections are finished.
   */
  function waitForSystemAddonInjectionsFinished(addons) {
    return new Promise(resolve => {
      let elementIdSet = new Set();
      for (let addon of addons) {
        if (addon.isGoingToInject) {
          elementIdSet.add(addon.elementId);
        }
      }
      if (elementIdSet.size) {
        let observer = new MutationObserver(mutations => {
          for (let mutation of mutations) {
            for (let node of mutation.addedNodes) {
              elementIdSet.delete(node.id);
              if (elementIdSet.size === 0) {
                observer.disconnect();
                resolve();
              }
            }
          }
        });
        let mainContent = document.querySelector(".main-content");
        observer.observe(mainContent, {childList: true, subtree: true});
        // Disconnect the mutation observer once there is any user input.
        mainContent.addEventListener("scroll", disconnectMutationObserver);
        window.addEventListener("mousedown", disconnectMutationObserver);
        window.addEventListener("keydown", disconnectMutationObserver);
        function disconnectMutationObserver() {
          mainContent.removeEventListener("scroll", disconnectMutationObserver);
          window.removeEventListener("mousedown", disconnectMutationObserver);
          window.removeEventListener("keydown", disconnectMutationObserver);
          observer.disconnect();
        }
      } else {
        resolve();
      }
    });
  }
}

function scrollAndHighlight(subcategory) {
  let element = document.querySelector(`[data-subcategory="${subcategory}"]`);
  if (element) {
    let header = getClosestDisplayedHeader(element);
    scrollContentTo(header);
    element.classList.add("spotlight");
  }
}

/**
 * If there is no visible second level header it will return first level header,
 * otherwise return second level header.
 * @returns {Element} - The closest displayed header.
 */
function getClosestDisplayedHeader(element) {
  let header = element.closest("groupbox");
  let searchHeader = header.querySelector("caption.search-header");
  if (searchHeader && searchHeader.hidden &&
      header.previousSibling.classList.contains("subcategory")) {
    header = header.previousSibling;
  }
  return header;
}

function scrollContentTo(element) {
  const STICKY_CONTAINER_HEIGHT = document.querySelector(".sticky-container").clientHeight;
  let mainContent = document.querySelector(".main-content");
  let top = element.getBoundingClientRect().top - STICKY_CONTAINER_HEIGHT;
  mainContent.scroll({
    top,
    behavior: "smooth",
  });
}

function friendlyPrefCategoryNameToInternalName(aName) {
  if (aName.startsWith("pane"))
    return aName;
  return "pane" + aName.substring(0, 1).toUpperCase() + aName.substr(1);
}

// This function is duplicated inside of utilityOverlay.js's openPreferences.
function internalPrefCategoryNameToFriendlyName(aName) {
  return (aName || "").replace(/^pane./, function(toReplace) { return toReplace[4].toLowerCase(); });
}

// Put up a confirm dialog with "ok to restart", "revert without restarting"
// and "restart later" buttons and returns the index of the button chosen.
// We can choose not to display the "restart later", or "revert" buttons,
// altough the later still lets us revert by using the escape key.
//
// The constants are useful to interpret the return value of the function.
const CONFIRM_RESTART_PROMPT_RESTART_NOW = 0;
const CONFIRM_RESTART_PROMPT_CANCEL = 1;
const CONFIRM_RESTART_PROMPT_RESTART_LATER = 2;
async function confirmRestartPrompt(aRestartToEnable, aDefaultButtonIndex,
                                    aWantRevertAsCancelButton,
                                    aWantRestartLaterButton) {
  let [
    msg, title, restartButtonText, noRestartButtonText, restartLaterButtonText
  ] = await document.l10n.formatValues([
    [aRestartToEnable ?
      "feature-enable-requires-restart" : "feature-disable-requires-restart"],
    ["should-restart-title"],
    ["should-restart-ok"],
    ["cancel-no-restart-button"],
    ["restart-later"],
  ]);

  // Set up the first (index 0) button:
  let buttonFlags = (Services.prompt.BUTTON_POS_0 *
                     Services.prompt.BUTTON_TITLE_IS_STRING);


  // Set up the second (index 1) button:
  if (aWantRevertAsCancelButton) {
    buttonFlags += (Services.prompt.BUTTON_POS_1 *
                    Services.prompt.BUTTON_TITLE_IS_STRING);
  } else {
    noRestartButtonText = null;
    buttonFlags += (Services.prompt.BUTTON_POS_1 *
                    Services.prompt.BUTTON_TITLE_CANCEL);
  }

  // Set up the third (index 2) button:
  if (aWantRestartLaterButton) {
    buttonFlags += (Services.prompt.BUTTON_POS_2 *
                    Services.prompt.BUTTON_TITLE_IS_STRING);
  } else {
    restartLaterButtonText = null;
  }

  switch (aDefaultButtonIndex) {
    case 0:
      buttonFlags += Services.prompt.BUTTON_POS_0_DEFAULT;
      break;
    case 1:
      buttonFlags += Services.prompt.BUTTON_POS_1_DEFAULT;
      break;
    case 2:
      buttonFlags += Services.prompt.BUTTON_POS_2_DEFAULT;
      break;
    default:
      break;
  }

  let buttonIndex = Services.prompt.confirmEx(window, title, msg, buttonFlags,
                                              restartButtonText, noRestartButtonText,
                                              restartLaterButtonText, null, {});

  // If we have the second confirmation dialog for restart, see if the user
  // cancels out at that point.
  if (buttonIndex == CONFIRM_RESTART_PROMPT_RESTART_NOW) {
    let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]
                       .createInstance(Ci.nsISupportsPRBool);
    Services.obs.notifyObservers(cancelQuit, "quit-application-requested",
                                  "restart");
    if (cancelQuit.data) {
      buttonIndex = CONFIRM_RESTART_PROMPT_CANCEL;
    }
  }
  return buttonIndex;
}

// This function is used to append search keywords found
// in the related subdialog to the button that will activate the subdialog.
function appendSearchKeywords(aId, keywords) {
  let element = document.getElementById(aId);
  let searchKeywords = element.getAttribute("searchkeywords");
  if (searchKeywords) {
    keywords.push(searchKeywords);
  }
  element.setAttribute("searchkeywords", keywords.join(" "));
}

function maybeDisplayPoliciesNotice() {
  if (Services.policies.status == Services.policies.ACTIVE) {
    document.getElementById("policies-container").removeAttribute("hidden");
  }
}
PK
!<�y�fMfMEchrome/browser/content/browser/preferences/in-content/preferences.xul<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this file,
   - You can obtain one at http://mozilla.org/MPL/2.0/. -->

<?xml-stylesheet href="chrome://global/skin/global.css"?>

<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css"?>
<?xml-stylesheet href="chrome://global/skin/in-content/common.css"?>
<?xml-stylesheet
  href="chrome://browser/skin/preferences/in-content/preferences.css"?>
<?xml-stylesheet
  href="chrome://browser/content/preferences/handlers.css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/applications.css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/in-content/search.css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/in-content/containers.css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/in-content/privacy.css"?>

<!DOCTYPE page [
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
<!ENTITY % globalPreferencesDTD SYSTEM "chrome://global/locale/preferences.dtd">
<!ENTITY % preferencesDTD SYSTEM
  "chrome://browser/locale/preferences/preferences.dtd">
<!ENTITY % selectBookmarkDTD SYSTEM
  "chrome://browser/locale/preferences/selectBookmark.dtd">
<!ENTITY % languagesDTD SYSTEM "chrome://browser/locale/preferences/languages.dtd">
<!ENTITY % fontDTD SYSTEM "chrome://browser/locale/preferences/fonts.dtd">
<!ENTITY % colorsDTD SYSTEM "chrome://browser/locale/preferences/colors.dtd">
<!ENTITY % permissionsDTD SYSTEM "chrome://browser/locale/preferences/permissions.dtd">
<!ENTITY % passwordManagerDTD SYSTEM "chrome://passwordmgr/locale/passwordManager.dtd">
<!ENTITY % historyDTD SYSTEM "chrome://mozapps/locale/update/history.dtd">
<!ENTITY % certManagerDTD SYSTEM "chrome://pippki/locale/certManager.dtd">
<!ENTITY % deviceManangerDTD SYSTEM "chrome://pippki/locale/deviceManager.dtd">
<!ENTITY % connectionDTD SYSTEM "chrome://browser/locale/preferences/connection.dtd">
<!ENTITY % siteDataSettingsDTD SYSTEM
  "chrome://browser/locale/preferences/siteDataSettings.dtd" >
<!ENTITY % clearSiteDataDTD SYSTEM
  "chrome://browser/locale/preferences/clearSiteData.dtd" >
<!ENTITY % privacyDTD SYSTEM "chrome://browser/locale/preferences/privacy.dtd">
<!ENTITY % searchDTD SYSTEM "chrome://browser/locale/preferences/search.dtd">
<!ENTITY % syncBrandDTD SYSTEM "chrome://browser/locale/syncBrand.dtd">
<!ENTITY % syncDTD SYSTEM "chrome://browser/locale/preferences/sync.dtd">
<!ENTITY % securityDTD SYSTEM
  "chrome://browser/locale/preferences/security.dtd">
<!ENTITY % containersDTD SYSTEM
  "chrome://browser/locale/preferences/containers.dtd">
<!ENTITY % sanitizeDTD SYSTEM "chrome://browser/locale/sanitize.dtd">
<!ENTITY % mainDTD SYSTEM "chrome://browser/locale/preferences/main.dtd">
<!ENTITY % aboutHomeDTD SYSTEM "chrome://browser/locale/aboutHome.dtd">
<!ENTITY % contentDTD SYSTEM "chrome://browser/locale/preferences/content.dtd">
<!ENTITY % advancedDTD SYSTEM
  "chrome://browser/locale/preferences/advanced.dtd">
<!ENTITY % aboutDialogDTD SYSTEM "chrome://browser/locale/aboutDialog.dtd" >
%aboutDialogDTD;
%brandDTD;
%globalPreferencesDTD;
%preferencesDTD;
%selectBookmarkDTD;
%languagesDTD;
%fontDTD;
%colorsDTD;
%permissionsDTD;
%passwordManagerDTD;
%historyDTD;
%certManagerDTD;
%deviceManangerDTD;
%connectionDTD;
%siteDataSettingsDTD;
%clearSiteDataDTD;
%privacyDTD;
%searchDTD;
%syncBrandDTD;
%syncDTD;
%securityDTD;
%containersDTD;
%sanitizeDTD;
%mainDTD;
%aboutHomeDTD;
%contentDTD;
%advancedDTD;
]>


<page xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
      xmlns:html="http://www.w3.org/1999/xhtml"
      disablefastfind="true"
      data-l10n-id="pref-page"
      data-l10n-attrs="title">

  <link rel="localization" href="branding/brand.ftl"/>
  <link rel="localization" href="browser/branding/sync-brand.ftl"/>
  <link rel="localization" href="browser/preferences/preferences.ftl"/>
  <script type="text/javascript" src="chrome://global/content/l10n.js"></script>

  <html:link rel="shortcut icon"
              href="chrome://browser/skin/settings.svg"/>

  <script type="application/javascript"
          src="chrome://browser/content/utilityOverlay.js"/>
  <script type="application/javascript"
          src="chrome://global/content/preferencesBindings.js"/>
  <script type="application/javascript"
          src="chrome://browser/content/preferences/in-content/preferences.js"/>
  <script type="application/javascript"
          src="chrome://browser/content/preferences/in-content/extensionControlled.js"/>
  <script src="chrome://browser/content/preferences/in-content/findInPage.js"/>
  <script src="chrome://browser/content/preferences/in-content/subdialogs.js"/>

  <stringbundle id="bundleBrand"
                src="chrome://branding/locale/brand.properties"/>
  <stringbundle id="bundlePreferences"
                src="chrome://browser/locale/preferences/preferences.properties"/>
  <stringbundle id="pkiBundle"
                src="chrome://pippki/locale/pippki.properties"/>
  <stringbundle id="browserBundle"
                src="chrome://browser/locale/browser.properties"/>

  <stringbundleset id="appManagerBundleset">
    <stringbundle id="appManagerBundle"
                  src="chrome://browser/locale/preferences/applicationManager.properties"/>
  </stringbundleset>

  <stack flex="1">
  <hbox flex="1">

    <vbox class="navigation">
      <!-- category list -->
      <richlistbox id="categories">
        <richlistitem id="category-general"
                      class="category"
                      value="paneGeneral"
                      helpTopic="prefs-main"
                      data-l10n-id="category-general"
                      data-l10n-attrs="tooltiptext"
                      align="center">
          <image class="category-icon"/>
          <label class="category-name" flex="1" data-l10n-id="pane-general-title"></label>
        </richlistitem>

        <richlistitem id="category-search"
                      class="category"
                      value="paneSearch"
                      helpTopic="prefs-search"
                      data-l10n-id="category-search"
                      data-l10n-attrs="tooltiptext"
                      align="center">
          <image class="category-icon"/>
          <label class="category-name" flex="1" data-l10n-id="pane-search-title"></label>
        </richlistitem>

        <richlistitem id="category-containers"
                      class="category"
                      value="paneContainers"
                      helpTopic="prefs-containers"
                      hidden="true"/>

        <richlistitem id="category-privacy"
                      class="category"
                      value="panePrivacy"
                      helpTopic="prefs-privacy"
                      data-l10n-id="category-privacy"
                      data-l10n-attrs="tooltiptext"
                      align="center">
          <image class="category-icon"/>
          <label class="category-name" flex="1" data-l10n-id="pane-privacy-title"></label>
        </richlistitem>

        <richlistitem id="category-sync"
                      class="category"
                      hidden="true"
                      value="paneSync"
                      helpTopic="prefs-weave"
                      data-l10n-id="category-sync"
                      data-l10n-attrs="tooltiptext"
                      align="center">
          <image class="category-icon"/>
          <label class="category-name" flex="1" data-l10n-id="pane-sync-title"></label>
        </richlistitem>
      </richlistbox>

      <spacer flex="1"/>

      <hbox class="help-button" pack="center">
        <label class="text-link">
          <hbox align="center">
            <image class="help-icon"/>
              <label class="help-label" flex="1" data-l10n-id="help-button-label"></label>
          </hbox>
        </label>
      </hbox>
    </vbox>

    <keyset>
      <key data-l10n-id="focus-search" key="" modifiers="accel" id="focusSearch1" oncommand="gSearchResultsPane.searchInput.focus();"/>
    </keyset>

    <vbox class="main-content" flex="1" align="start">
      <vbox class="pane-container">
        <hbox class="sticky-container" pack="end" align="top">
          <hbox id="policies-container" align="stretch" flex="1" hidden="true">
            <hbox align="top">
              <image class="info-icon"></image>
            </hbox>
            <hbox align="center" flex="1">
              <label class="policies-label" flex="1" data-l10n-id="policies-notice"></label>
            </hbox>
          </hbox>
          <textbox
            type="search" id="searchInput"
            data-l10n-id="search-input"
            data-l10n-attrs="style"
            hidden="true" clickSelectsAll="true"/>
        </hbox>
        <vbox id="mainPrefPane" class="prefpane prefwindow">
<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this file,
   - You can obtain one at http://mozilla.org/MPL/2.0/. -->

<stringbundle id="searchResultBundle" src="chrome://browser/locale/preferences/preferences.properties"/>

<hbox id="header-searchResults"
      class="subcategory"
      hidden="true"
      data-category="paneSearchResults">
  <label class="header-name" flex="1">&paneSearchResults.title;</label>
</hbox>

<groupbox class="no-results-message" data-category="paneSearchResults" hidden="true">
  <vbox class="no-results-container">
    <label id="sorry-message"></label>
    <label id="need-help"></label>
  </vbox>
  <vbox class="no-results-container" align="center">
    <image></image>
  </vbox>
</groupbox>

<!-- General panel -->

<script type="application/javascript"
        src="chrome://browser/content/preferences/in-content/main.js"/>

  <script type="application/javascript" src="chrome://browser/content/aboutDialog-appUpdater.js"/>

<script type="application/javascript"
        src="chrome://mozapps/content/preferences/fontbuilder.js"/>

<stringbundle id="bundlePreferences" src="chrome://browser/locale/preferences.properties"/>

<hbox id="generalCategory"
      class="subcategory"
      hidden="true"
      data-category="paneGeneral">
      <label class="header-name" flex="1" data-l10n-id="pane-general-title"/>
</hbox>

<!-- Startup -->
<groupbox id="startupGroup"
          data-category="paneGeneral"
          hidden="true">
  <caption><label data-l10n-id="startup-header"/></caption>


  <vbox id="defaultBrowserBox">
    <checkbox id="alwaysCheckDefault" preference="browser.shell.checkDefaultBrowser"
              data-l10n-id="always-check-default"/>
    <deck id="setDefaultPane">
      <hbox align="center" class="indent">
        <image class="face-sad"/>
        <label id="isNotDefaultLabel" flex="1" data-l10n-id="is-not-default"/>
        <button id="setDefaultButton"
                class="accessory-button"
                data-l10n-id="set-as-my-default-browser"
                preference="pref.general.disable_button.default_browser"/>
      </hbox>
      <hbox align="center" class="indent">
        <image class="face-smile"/>
        <label id="isDefaultLabel" flex="1" data-l10n-id="is-default"/>
      </hbox>
    </deck>
  </vbox>

  <vbox id="startupPageBox">
    <label data-l10n-id="startup-page"
           control="browserStartupPage"/>
    <radiogroup id="browserStartupPage"
                preference="browser.startup.page">
      <radio data-l10n-id="startup-user-homepage"
             value="1"
             id="browserStartupHomePage"/>
      <radio data-l10n-id="startup-blank-page"
             value="0"
             id="browserStartupBlank"/>
      <radio data-l10n-id="startup-prev-session"
             value="3"
             id="browserStartupLastSession"/>
    </radiogroup>
    <hbox id="browserNewTabExtensionContent" align="center" hidden="true">
      <description control="disableNewTabExtension" flex="1" />
      <button id="disableNewTabExtension"
              class="extension-controlled-button accessory-button"
              data-l10n-id="disable-extension" />
    </hbox>
  </vbox>
</groupbox>

<!-- Home Page -->
<groupbox id="homepageGroup"
          data-category="paneGeneral"
          hidden="true">
  <caption><label data-l10n-id="home-page-header"/></caption>

  <hbox id="browserHomePageExtensionContent" align="center" hidden="true">
    <description control="disableHomePageExtension" flex="1" />
    <button id="disableHomePageExtension"
            class="extension-controlled-button accessory-button"
            data-l10n-id="disable-extension" />
  </hbox>

  <vbox>
    <textbox id="browserHomePage"
             class="uri-element"
             type="autocomplete"
             autocompletesearch="unifiedcomplete"
             onsyncfrompreference="return gMainPane.syncFromHomePref();"
             onsynctopreference="return gMainPane.syncToHomePref(this.value);"
             placeholder="&abouthome.pageTitle;"
             preference="browser.startup.homepage"/>
  </vbox>

  <hbox class="homepage-buttons">
    <button id="useCurrent"
            flex="1"
            class="homepage-button"
            data-l10n-id="use-current-pages"
            data-l10n-args='{"tabCount": 0}'
            disabled="true"
            preference="pref.browser.homepage.disable_button.current_page"/>
    <button id="useBookmark"
            flex="1"
            class="homepage-button"
            data-l10n-id="choose-bookmark"
            preference="pref.browser.homepage.disable_button.bookmark_page"
            searchkeywords="&selectBookmark.title; &selectBookmark.label;"/>
    <button id="restoreDefaultHomePage"
            flex="1"
            class="homepage-button"
            data-l10n-id="restore-default"
            preference="pref.browser.homepage.disable_button.restore_default"/>
  </hbox>
</groupbox>

<!-- Tab preferences -->
<groupbox data-category="paneGeneral"
          hidden="true">
    <caption><label data-l10n-id="tabs-group-header"/></caption>

    <checkbox id="ctrlTabRecentlyUsedOrder" data-l10n-id="ctrl-tab-recently-used-order"
              preference="browser.ctrlTab.previews"/>

    <checkbox id="linkTargeting" data-l10n-id="open-new-link-as-tabs"
              preference="browser.link.open_newwindow"
              onsyncfrompreference="return gMainPane.readLinkTarget();"
              onsynctopreference="return gMainPane.writeLinkTarget();"/>

    <checkbox id="warnCloseMultiple" data-l10n-id="warn-on-close-multiple-tabs"
              preference="browser.tabs.warnOnClose"/>

    <checkbox id="warnOpenMany" data-l10n-id="warn-on-open-many-tabs"
              preference="browser.tabs.warnOnOpen"/>

    <checkbox id="switchToNewTabs" data-l10n-id="switch-links-to-new-tabs"
              preference="browser.tabs.loadInBackground"/>


    <vbox id="browserContainersbox" hidden="true">
      <hbox id="browserContainersExtensionContent" align="center">
        <description control="disableContainersExtension" flex="1" />
        <button id="disableContainersExtension"
                class="extension-controlled-button accessory-button"
                data-l10n-id="disable-extension" />
      </hbox>
      <hbox align="center">
        <checkbox id="browserContainersCheckbox"
                  class="tail-with-learn-more"
                  data-l10n-id="browser-containers-enabled"
                  preference="privacy.userContext.enabled"
                  onsyncfrompreference="return gMainPane.readBrowserContainersCheckbox();"/>
        <label id="browserContainersLearnMore" class="learnMore text-link" data-l10n-id="browser-containers-learn-more"/>
        <spacer flex="1"/>
        <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
        <hbox>
          <button id="browserContainersSettings"
                  class="accessory-button"
                  data-l10n-id="browser-containers-settings"
                  searchkeywords="&addButton.label;
                                  &preferencesButton.label;
                                  &removeButton.label;"/>
        </hbox>
      </hbox>
    </vbox>
</groupbox>

<hbox id="languageAndAppearanceCategory"
      class="subcategory"
      hidden="true"
      data-category="paneGeneral">
  <label class="header-name" flex="1" data-l10n-id="language-and-appearance-header"/>
</hbox>

<!-- Fonts and Colors -->
<groupbox id="fontsGroup" data-category="paneGeneral" hidden="true">
  <caption><label data-l10n-id="fonts-and-colors-header"/></caption>

  <vbox>
    <hbox id="fontSettings">
      <hbox align="center" flex="1">
        <label control="defaultFont" data-l10n-id="default-font"/>
        <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
        <hbox flex="1">
          <menulist id="defaultFont" flex="1" delayprefsave="true" onsyncfrompreference="return FontBuilder.readFontSelection(this);"/>
        </hbox>
        <label id="defaultFontSizeLabel" control="defaultFontSize" data-l10n-id="default-font-size"></label>
        <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
        <hbox>
          <menulist id="defaultFontSize" delayprefsave="true">
            <menupopup>
              <menuitem value="9" label="9"/>
              <menuitem value="10" label="10"/>
              <menuitem value="11" label="11"/>
              <menuitem value="12" label="12"/>
              <menuitem value="13" label="13"/>
              <menuitem value="14" label="14"/>
              <menuitem value="15" label="15"/>
              <menuitem value="16" label="16"/>
              <menuitem value="17" label="17"/>
              <menuitem value="18" label="18"/>
              <menuitem value="20" label="20"/>
              <menuitem value="22" label="22"/>
              <menuitem value="24" label="24"/>
              <menuitem value="26" label="26"/>
              <menuitem value="28" label="28"/>
              <menuitem value="30" label="30"/>
              <menuitem value="32" label="32"/>
              <menuitem value="34" label="34"/>
              <menuitem value="36" label="36"/>
              <menuitem value="40" label="40"/>
              <menuitem value="44" label="44"/>
              <menuitem value="48" label="48"/>
              <menuitem value="56" label="56"/>
              <menuitem value="64" label="64"/>
              <menuitem value="72" label="72"/>
            </menupopup>
          </menulist>
        </hbox>
      </hbox>

      <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
      <hbox>
        <button id="advancedFonts"
                class="accessory-button"
                icon="select-font"
                data-l10n-id="advanced-fonts"
                searchkeywords="&fontsDialog.title;
                                &fonts.label;
                                &size2.label;
                                &proportional2.label;
                                &serif2.label;
                                &sans-serif2.label;
                                &monospace2.label;
                                &font.langGroup.latin;
                                &font.langGroup.japanese;
                                &font.langGroup.trad-chinese;
                                &font.langGroup.simpl-chinese;
                                &font.langGroup.trad-chinese-hk;
                                &font.langGroup.korean;
                                &font.langGroup.cyrillic;
                                &font.langGroup.el;
                                &font.langGroup.other;
                                &font.langGroup.thai;
                                &font.langGroup.hebrew;
                                &font.langGroup.arabic;
                                &font.langGroup.devanagari;
                                &font.langGroup.tamil;
                                &font.langGroup.armenian;
                                &font.langGroup.bengali;
                                &font.langGroup.canadian;
                                &font.langGroup.ethiopic;
                                &font.langGroup.georgian;
                                &font.langGroup.gujarati;
                                &font.langGroup.gurmukhi;
                                &font.langGroup.khmer;
                                &font.langGroup.malayalam;
                                &font.langGroup.math;
                                &font.langGroup.odia;
                                &font.langGroup.telugu;
                                &font.langGroup.kannada;
                                &font.langGroup.sinhala;
                                &font.langGroup.tibetan;
                                &minSize2.label;
                                &minSize.none;
                                &useDefaultFontSerif.label;
                                &useDefaultFontSansSerif.label;
                                &allowPagesToUseOwn.label;
                                &languages.customize.Fallback2.grouplabel;
                                &languages.customize.Fallback3.label;
                                &languages.customize.Fallback2.desc;
                                &languages.customize.Fallback.auto;
                                &languages.customize.Fallback.arabic;
                                &languages.customize.Fallback.baltic;
                                &languages.customize.Fallback.ceiso;
                                &languages.customize.Fallback.cewindows;
                                &languages.customize.Fallback.simplified;
                                &languages.customize.Fallback.traditional;
                                &languages.customize.Fallback.cyrillic;
                                &languages.customize.Fallback.greek;
                                &languages.customize.Fallback.hebrew;
                                &languages.customize.Fallback.japanese;
                                &languages.customize.Fallback.korean;
                                &languages.customize.Fallback.thai;
                                &languages.customize.Fallback.turkish;
                                &languages.customize.Fallback.vietnamese;
                                &languages.customize.Fallback.other;"/>
      </hbox>
    </hbox>
    <hbox id="colorsSettings">
      <spacer flex="1" />
      <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
      <hbox>
        <button id="colors"
                class="accessory-button"
                icon="select-color"
                data-l10n-id="colors-settings"
                searchkeywords="&overrideDefaultPageColors2.label;
                                &overrideDefaultPageColors.always.label;
                                &overrideDefaultPageColors.auto.label;
                                &overrideDefaultPageColors.never.label;
                                &color;
                                &textColor2.label;
                                &backgroundColor2.label;
                                &useSystemColors.label;
                                &underlineLinks.label;
                                &links;
                                &linkColor2.label;
                                &visitedLinkColor2.label;"/>
      </hbox>
    </hbox>
  </vbox>
</groupbox>

<!-- Languages -->
<groupbox id="languagesGroup" data-category="paneGeneral" hidden="true">
  <caption><label data-l10n-id="language-header"/></caption>

  <hbox id="languagesBox" align="center">
    <description flex="1" control="chooseLanguage" data-l10n-id="choose-language-description"/>
    <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
    <hbox>
      <button id="chooseLanguage"
              class="accessory-button"
              data-l10n-id="choose-button"
              searchkeywords="&languages.customize.Header;
                              &languages.customize2.description;
                              &languages.customize.moveUp.label;
                              &languages.customize.moveDown.label;
                              &languages.customize.deleteButton.label;
                              &languages.customize.selectLanguage.label;
                              &languages.customize.addButton.label;"/>
    </hbox>
  </hbox>

  <hbox id="translationBox" hidden="true">
    <hbox align="center" flex="1">
      <checkbox id="translate" preference="browser.translation.detectLanguage"
                data-l10n-id="translate-web-pages"
                onsyncfrompreference="return gMainPane.updateButtons('translateButton',
                                              'browser.translation.detectLanguage');"/>
      <hbox id="bingAttribution" hidden="true" align="center">
        <label>&translation.options.attribution.beforeLogo;</label>
        <separator orient="vertical" class="thin"/>
        <image id="translationAttributionImage" aria-label="Microsoft Translator"
               src="chrome://browser/content/microsoft-translator-attribution.png"/>
        <separator orient="vertical" class="thin"/>
        <label>&translation.options.attribution.afterLogo;</label>
      </hbox>
    </hbox>
    <button id="translateButton"
            class="accessory-button"
            data-l10n-id="translate-exceptions"/>
  </hbox>
  <checkbox id="checkSpelling"
          data-l10n-id="check-user-spelling"
          onsyncfrompreference="return gMainPane.readCheckSpelling();"
          onsynctopreference="return gMainPane.writeCheckSpelling();"
          preference="layout.spellcheckDefault"/>
</groupbox>

<!-- Files and Applications -->
<hbox id="filesAndApplicationsCategory"
      class="subcategory"
      hidden="true"
      data-category="paneGeneral">
  <label class="header-name" flex="1" data-l10n-id="files-and-applications-title"/>
</hbox>

<!--Downloads-->
<groupbox id="downloadsGroup" data-category="paneGeneral" hidden="true">
  <caption><label data-l10n-id="download-header"/></caption>

  <radiogroup id="saveWhere"
              preference="browser.download.useDownloadDir"
              onsyncfrompreference="return gMainPane.readUseDownloadDir();">
    <hbox id="saveToRow">
      <radio id="saveTo"
            value="true"
            data-l10n-id="download-save-to"
            aria-labelledby="saveTo downloadFolder"/>
      <filefield id="downloadFolder"
                flex="1"
                preference="browser.download.folderList"
                preference-editable="true"
                aria-labelledby="saveTo"
                onsyncfrompreference="return gMainPane.displayDownloadDirPref();"/>
      <button id="chooseFolder"
              data-l10n-id="download-choose-folder"
      />
    </hbox>
    <!-- Additional radio button added to support CloudStorage - Bug 1357171 -->
    <radio id="saveToCloud"
          value="true"
          hidden="true"/>
    <radio id="alwaysAsk"
          value="false"
          data-l10n-id="download-always-ask-where"/>
  </radiogroup>
</groupbox>

<groupbox id="applicationsGroup" data-category="paneGeneral" hidden="true">
  <caption><label data-l10n-id="applications-header"/></caption>
  <description data-l10n-id="applications-description"/>
  <textbox id="filter" flex="1"
           type="search"
           data-l10n-id="applications-filter"
           aria-controls="handlersView"/>

  <richlistbox id="handlersView" orient="vertical" persist="lastSelectedType"
               preference="pref.downloads.disable_button.edit_actions"
               flex="1">
    <listheader equalsize="always">
        <treecol id="typeColumn" data-l10n-id="applications-type-column" value="type"
                 persist="sortDirection"
                 flex="1" sortDirection="ascending"/>
        <treecol id="actionColumn" data-l10n-id="applications-action-column" value="action"
                 persist="sortDirection"
                 flex="1"/>
    </listheader>
  </richlistbox>
</groupbox>


<!-- DRM Content -->
<groupbox id="drmGroup" data-category="paneGeneral" data-subcategory="drm" hidden="true">
  <caption><label data-l10n-id="drm-content-header"/></caption>
  <grid id="contentGrid2">
    <columns>
      <column flex="1"/>
      <column/>
    </columns>
    <rows id="contentRows-2">
      <row id="playDRMContentRow">
        <hbox align="center">
          <checkbox id="playDRMContent" preference="media.eme.enabled"
                    class="tail-with-learn-more" data-l10n-id="play-drm-content" />
          <label id="playDRMContentLink" class="learnMore text-link" data-l10n-id="play-drm-content-learn-more"/>
        </hbox>
      </row>
    </rows>
  </grid>
</groupbox>

  <stringbundle id="bundleShell" src="chrome://browser/locale/shellservice.properties"/>
  <stringbundle id="bundleBrand" src="chrome://branding/locale/brand.properties"/>

<hbox id="updatesCategory"
      class="subcategory"
      hidden="true"
      data-category="paneGeneral">
  <label class="header-name" flex="1" data-l10n-id="update-application-title"/>
</hbox>

<!-- Update -->
<groupbox id="updateApp" data-category="paneGeneral" hidden="true">
  <caption class="search-header" hidden="true"><label data-l10n-id="update-application-title"/></caption>

  <label data-l10n-id="update-application-description"/>
  <hbox align="center">
    <vbox flex="1">
      <description id="updateAppInfo">
        <html:a id="releasenotes" class="learnMore text-link" hidden="true"/>
      </description>
      <description id="distribution" class="text-blurb" hidden="true"/>
      <description id="distributionId" class="text-blurb" hidden="true"/>
    </vbox>
    <spacer flex="1"/>
    <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
    <vbox>
      <button id="showUpdateHistory"
              class="accessory-button"
              data-l10n-id="update-history"
              preference="app.update.disable_button.showUpdateHistory"
              searchkeywords="&history.title; &history2.intro;"/>
    </vbox>
  </hbox>
  <vbox id="updateBox">
    <deck id="updateDeck" orient="vertical">
      <hbox id="checkForUpdates" align="start">
        <spacer flex="1"/>
        <button id="checkForUpdatesButton"
                label="&update.checkForUpdatesButton.label;"
                accesskey="&update.checkForUpdatesButton.accesskey;"
                oncommand="gAppUpdater.checkForUpdates();"/>
      </hbox>
      <hbox id="downloadAndInstall" align="start">
        <spacer flex="1"/>
        <button id="downloadAndInstallButton"
                oncommand="gAppUpdater.startDownload();"/>
                <!-- label and accesskey will be filled by JS -->
      </hbox>
      <hbox id="apply" align="start">
        <spacer flex="1"/>
        <button id="updateButton"
                label="&update.updateButton.label3;"
                accesskey="&update.updateButton.accesskey;"
                oncommand="gAppUpdater.buttonRestartAfterDownload();"/>
      </hbox>
      <hbox id="checkingForUpdates" align="start">
        <image class="update-throbber"/><label>&update.checkingForUpdates;</label>
        <spacer flex="1"/>
        <button label="&update.checkForUpdatesButton.label;"
                accesskey="&update.checkForUpdatesButton.accesskey;"
                disabled="true"/>
      </hbox>
      <hbox id="downloading" align="start">
        <image class="update-throbber"/><label>&update.downloading.start;</label><label id="downloadStatus"/><label>&update.downloading.end;</label>
      </hbox>
      <hbox id="applying" align="start">
        <image class="update-throbber"/><label>&update.applying;</label>
      </hbox>
      <hbox id="downloadFailed" align="start">
        <label>&update.failed.start;</label><label id="failedLink" class="text-link">&update.failed.linkText;</label><label>&update.failed.end;</label>
        <spacer flex="1"/>
        <button label="&update.checkForUpdatesButton.label;"
                accesskey="&update.checkForUpdatesButton.accesskey;"
                oncommand="gAppUpdater.checkForUpdates();"/>
      </hbox>
      <hbox id="adminDisabled" align="start">
        <label>&update.adminDisabled;</label>
        <spacer flex="1"/>
        <button label="&update.checkForUpdatesButton.label;"
                accesskey="&update.checkForUpdatesButton.accesskey;"
                disabled="true"/>
      </hbox>
      <hbox id="noUpdatesFound" align="start">
        <image class="face-smile"/>
        <label>&update.noUpdatesFound;</label>
        <spacer flex="1"/>
        <button label="&update.checkForUpdatesButton.label;"
                accesskey="&update.checkForUpdatesButton.accesskey;"
                oncommand="gAppUpdater.checkForUpdates();"/>
      </hbox>
      <hbox id="otherInstanceHandlingUpdates" align="start">
        <label>&update.otherInstanceHandlingUpdates;</label>
        <spacer flex="1"/>
        <button label="&update.checkForUpdatesButton.label;"
                accesskey="&update.checkForUpdatesButton.accesskey;"
                disabled="true"/>
      </hbox>
      <hbox id="manualUpdate" align="start">
        <image class="face-sad"/>
        <description flex="1">
          <label>&update.manual.start;</label><label id="manualLink" class="text-link"/><label>&update.manual.end;</label>
        </description>
        <spacer flex="1"/>
        <button label="&update.checkForUpdatesButton.label;"
                accesskey="&update.checkForUpdatesButton.accesskey;"
                disabled="true"/>
      </hbox>
      <hbox id="unsupportedSystem" align="start">
        <description flex="1">
          <label>&update.unsupported.start;</label><label id="unsupportedLink" class="learnMore text-link">&update.unsupported.linkText;</label><label>&update.unsupported.end;</label>
        </description>
        <spacer flex="1"/>
        <button label="&update.checkForUpdatesButton.label;"
                accesskey="&update.checkForUpdatesButton.accesskey;"
                disabled="true"/>
      </hbox>
      <hbox id="restarting" align="start">
        <image class="update-throbber"/><label>&update.restarting;</label>
        <spacer flex="1"/>
        <button label="&update.updateButton.label3;"
                accesskey="&update.updateButton.accesskey;"
                disabled="true"/>
      </hbox>
    </deck>
  </vbox>

  <description data-l10n-id="update-application-allow-description"></description>
  <radiogroup id="updateRadioGroup">
    <radio id="autoDesktop"
           value="auto"
           data-l10n-id="update-application-auto"/>
    <radio value="checkOnly"
           data-l10n-id="update-application-check-choose"/>
    <radio value="manual"
           data-l10n-id="update-application-manual"/>
  </radiogroup>
  <checkbox id="enableSearchUpdate"
            data-l10n-id="update-enable-search-update"
            preference="browser.search.update"/>
</groupbox>

<hbox id="performanceCategory"
      class="subcategory"
      hidden="true"
      data-category="paneGeneral">
  <label class="header-name" flex="1" data-l10n-id="performance-title"/>
</hbox>

<!-- Performance -->
<groupbox id="performanceGroup" data-category="paneGeneral" hidden="true">
  <caption class="search-header" hidden="true"><label data-l10n-id="performance-title"/></caption>

  <hbox align="center">
    <checkbox id="useRecommendedPerformanceSettings"
              class="tail-with-learn-more"
              data-l10n-id="performance-use-recommended-settings-checkbox"
              preference="browser.preferences.defaultPerformanceSettings.enabled"/>
    <label id="performanceSettingsLearnMore" class="learnMore text-link" data-l10n-id="performance-settings-learn-more"/>
  </hbox>
  <description class="indent tip-caption" data-l10n-id="performance-use-recommended-settings-desc"/>

  <vbox id="performanceSettings" class="indent" hidden="true">
    <checkbox id="allowHWAccel"
              data-l10n-id="performance-allow-hw-accel"
              preference="layers.acceleration.disabled"/>
    <hbox align="center">
      <label id="limitContentProcess" data-l10n-id="performance-limit-content-process-option" control="contentProcessCount"/>
      <menulist id="contentProcessCount" preference="dom.ipc.processCount">
        <menupopup>
          <menuitem label="1" value="1"/>
          <menuitem label="2" value="2"/>
          <menuitem label="3" value="3"/>
          <menuitem label="4" value="4"/>
          <menuitem label="5" value="5"/>
          <menuitem label="6" value="6"/>
          <menuitem label="7" value="7"/>
        </menupopup>
      </menulist>
    </hbox>
    <description id="contentProcessCountEnabledDescription" class="tip-caption" data-l10n-id="performance-limit-content-process-enabled-desc"/>
    <description id="contentProcessCountDisabledDescription" class="tip-caption" data-l10n-id="performance-limit-content-process-disabled-desc">
      <html:a class="text-link" href="https://wiki.mozilla.org/Electrolysis"/>
    </description>
  </vbox>
</groupbox>

<hbox id="browsingCategory"
      class="subcategory"
      hidden="true"
      data-category="paneGeneral">
  <label class="header-name" flex="1" data-l10n-id="browsing-title"/>
</hbox>

<!-- Browsing -->
<groupbox id="browsingGroup" data-category="paneGeneral" hidden="true">
  <caption class="search-header" hidden="true"><label data-l10n-id="browsing-title"/></caption>

  <checkbox id="useAutoScroll"
            data-l10n-id="browsing-use-autoscroll"
            preference="general.autoScroll"/>
  <checkbox id="useSmoothScrolling"
            data-l10n-id="browsing-use-smooth-scrolling"
            preference="general.smoothScroll"/>

  <checkbox id="useCursorNavigation"
            data-l10n-id="browsing-use-cursor-navigation"
            preference="accessibility.browsewithcaret"/>
  <checkbox id="searchStartTyping"
            data-l10n-id="browsing-search-on-start-typing"
            preference="accessibility.typeaheadfind"/>
</groupbox>

<hbox id="networkProxyCategory"
      class="subcategory"
      hidden="true"
      data-category="paneGeneral">
  <label class="header-name" flex="1" data-l10n-id="network-proxy-title"/>
</hbox>

<!-- Network Proxy-->
<groupbox id="connectionGroup" data-category="paneGeneral" hidden="true">
  <caption class="search-header" hidden="true"><label data-l10n-id="network-proxy-title"/></caption>

  <hbox align="center">
    <hbox align="center" flex="1">
      <description id="connectionSettingsDescription" control="connectionSettings"/>
      <spacer width="5"/>
      <label id="connectionSettingsLearnMore" class="learnMore text-link"
        data-l10n-id="network-proxy-connection-learn-more">
      </label>
      <separator orient="vertical"/>
    </hbox>

    <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
    <hbox>
      <button id="connectionSettings"
              class="accessory-button"
              icon="network"
              data-l10n-id="network-proxy-connection-settings"
              searchkeywords="&connectionsDialog.title;
                              &noProxyTypeRadio.label;
                              &WPADTypeRadio.label;
                              &systemTypeRadio.label;
                              &manualTypeRadio2.label;
                              &http2.label;
                              &ssl2.label;
                              &ftp2.label;
                              &port2.label;
                              &socks2.label;
                              &socks4.label;
                              &socks5.label;
                              &noproxy2.label;
                              &noproxyExplain.label;
                              &shareproxy.label;
                              &autoTypeRadio2.label;
                              &reload.label;
                              &autologinproxy.label;
                              &socksRemoteDNS.label2;"/>
    </hbox>
  </hbox>
</groupbox>
    <script type="application/javascript"
            src="chrome://browser/content/preferences/in-content/search.js"/>

    <stringbundle id="engineManagerBundle" src="chrome://browser/locale/engineManager.properties"/>

    <hbox id="searchCategory"
          class="subcategory"
          hidden="true"
          data-category="paneSearch">
      <label class="header-name" flex="1">&paneSearch.title;</label>
    </hbox>

    <groupbox id="searchbarGroup" data-category="paneSearch">
      <caption><label id="searchbarLabel">&searchBar.label;</label></caption>
      <radiogroup id="searchBarVisibleGroup" aria-labelledby="searchbarLabel" preference="browser.search.widget.inNavBar">
        <radio id="searchBarHiddenRadio" value="false" label="&searchBar.hidden.label;"/>
        <image class="searchBarImage searchBarHiddenImage" role="presentation"/>
        <radio id="searchBarShownRadio" value="true" label="&searchBar.shown.label;"/>
        <image class="searchBarImage searchBarShownImage" role="presentation"/>
      </radiogroup>
    </groupbox>

    <!-- Default Search Engine -->
    <groupbox id="defaultEngineGroup" data-category="paneSearch">
      <caption><label>&defaultSearchEngine.label;</label></caption>
      <description>&chooseYourDefaultSearchEngine2.label;</description>

      <hbox id="browserDefaultSearchExtensionContent" align="center" hidden="true">
        <description control="disableDefaultSearchExtension" flex="1"/>
      </hbox>

      <hbox>
        <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
        <hbox>
          <menulist id="defaultEngine">
            <menupopup/>
          </menulist>
        </hbox>
      </hbox>

      <checkbox id="suggestionsInSearchFieldsCheckbox"
                label="&provideSearchSuggestions.label;"
                accesskey="&provideSearchSuggestions.accesskey;"
                preference="browser.search.suggest.enabled"/>
      <vbox class="indent">
        <checkbox id="urlBarSuggestion" label="&showURLBarSuggestions2.label;"
                  accesskey="&showURLBarSuggestions2.accesskey;"/>
        <checkbox id="showSearchSuggestionsFirstCheckbox"
                  label="&showSearchSuggestionsAboveHistory.label;"/>
        <hbox id="urlBarSuggestionPermanentPBLabel"
              align="center" class="indent">
          <label flex="1">&urlBarSuggestionsPermanentPB.label;</label>
        </hbox>
      </vbox>
    </groupbox>

    <groupbox id="oneClickSearchProvidersGroup" data-category="paneSearch">
      <caption><label>&oneClickSearchEngines.label;</label></caption>
      <description>&chooseWhichOneToDisplay2.label;</description>

      <tree id="engineList" flex="1" rows="8" hidecolumnpicker="true" editable="true"
            seltype="single" allowunderflowscroll="true">
        <treechildren id="engineChildren" flex="1"/>
        <treecols>
          <treecol id="engineShown" type="checkbox" editable="true" sortable="false"/>
          <treecol id="engineName" flex="4" label="&engineNameColumn.label;" sortable="false"/>
          <treecol id="engineKeyword" flex="1" label="&engineKeywordColumn.label;" editable="true"
                   sortable="false"/>
        </treecols>
      </tree>

      <hbox>
        <button id="restoreDefaultSearchEngines"
                label="&restoreDefaultSearchEngines.label;"
                accesskey="&restoreDefaultSearchEngines.accesskey;"
                />
        <spacer flex="1"/>
        <button id="removeEngineButton"
                class="searchEngineAction"
                label="&removeEngine.label;"
                accesskey="&removeEngine.accesskey;"
                disabled="true"
                />
      </hbox>
      <hbox id="addEnginesBox" pack="start">
        <label id="addEngines" class="text-link">&findMoreSearchEngines.label;</label>
      </hbox>
    </groupbox>

<!-- Privacy panel -->

<script type="application/javascript"
        src="chrome://browser/content/preferences/in-content/privacy.js"/>
<stringbundle id="bundlePreferences" src="chrome://browser/locale/preferences/preferences.properties"/>
<stringbundle id="signonBundle" src="chrome://passwordmgr/locale/passwordmgr.properties"/>

<hbox id="browserPrivacyCategory"
      class="subcategory"
      hidden="true"
      data-category="panePrivacy">
  <label class="header-name" flex="1">&browserPrivacy.label;</label>
</hbox>

<!-- Passwords -->
<groupbox id="passwordsGroup" orient="vertical" data-category="panePrivacy" hidden="true">
  <caption><label>&formsAndPasswords.label;</label></caption>

  <vbox id="passwordSettings">
    <hbox id="savePasswordsBox">
      <checkbox id="savePasswords"
                label="&rememberLogins2.label;" accesskey="&rememberLogins2.accesskey;"
                preference="signon.rememberSignons"
                onsyncfrompreference="return gPrivacyPane.readSavePasswords();"
                flex="1" />
      <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
      <hbox>
        <button id="passwordExceptions"
                class="accessory-button"
                label="&passwordExceptions.label;"
                accesskey="&passwordExceptions.accesskey;"
                preference="pref.privacy.disable_button.view_passwords_exceptions"
                searchkeywords="&address2.label;"/>
      </hbox>
    </hbox>
    <hbox id="showPasswordBox" pack="end">
      <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
      <hbox>
        <button id="showPasswords"
                class="accessory-button"
                label="&savedLogins.label;" accesskey="&savedLogins.accesskey;"
                preference="pref.privacy.disable_button.view_passwords"
                searchkeywords="&savedLogins.title;"/>
      </hbox>
    </hbox>
  </vbox>
  <hbox id="masterPasswordRow">
    <checkbox id="useMasterPassword"
              label="&useMasterPassword.label;"
              accesskey="&useMasterPassword.accesskey;"
              flex="1"/>
    <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
    <hbox>
      <button id="changeMasterPassword"
              class="accessory-button"
              label="&changeMasterPassword.label;"
              accesskey="&changeMasterPassword.accesskey;"/>
    </hbox>
  </hbox>
</groupbox>

<!-- History -->
<groupbox id="historyGroup" data-category="panePrivacy" hidden="true">
  <caption><label>&history.label;</label></caption>
  <hbox align="center">
    <label id="historyModeLabel"
           control="historyMode"
           accesskey="&historyHeader2.pre.accesskey;">&historyHeader2.pre.label;
    </label>
    <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
    <hbox>
      <menulist id="historyMode">
        <menupopup>
          <menuitem label="&historyHeader.remember.label;" value="remember" searchkeywords="&rememberDescription1.label;"/>
          <menuitem label="&historyHeader.dontremember.label;" value="dontremember" searchkeywords="&dontrememberDescription.label;"/>
          <menuitem label="&historyHeader.custom.label;" value="custom" searchkeywords="&privateBrowsingPermanent2.label;
                                                                                        &rememberHistory2.label;
                                                                                        &rememberSearchForm.label;
                                                                                        &clearOnClose.label;
                                                                                        &clearOnCloseSettings.label;"/>
        </menupopup>
      </menulist>
    </hbox>
    <label>&historyHeader.post.label;</label>
  </hbox>
  <hbox>
    <deck id="historyPane" flex="1">
      <vbox id="historyRememberPane">
        <hbox align="center" flex="1">
          <vbox flex="1">
            <description class="description-with-side-element">&rememberDescription1.label;</description>
          </vbox>
        </hbox>
      </vbox>
      <vbox id="historyDontRememberPane">
        <hbox align="center" flex="1">
          <vbox flex="1">
            <description class="description-with-side-element">&dontrememberDescription.label;</description>
          </vbox>
        </hbox>
      </vbox>
      <vbox id="historyCustomPane">
        <vbox>
          <checkbox id="privateBrowsingAutoStart"
                    label="&privateBrowsingPermanent2.label;"
                    accesskey="&privateBrowsingPermanent2.accesskey;"
                    preference="browser.privatebrowsing.autostart"/>
          <vbox class="indent">
            <checkbox id="rememberHistory"
                      label="&rememberHistory2.label;"
                      accesskey="&rememberHistory2.accesskey;"
                      preference="places.history.enabled"/>
            <checkbox id="rememberForms"
                      label="&rememberSearchForm.label;"
                      accesskey="&rememberSearchForm.accesskey;"
                      preference="browser.formfill.enable"/>
            <hbox id="clearDataBox"
                  align="center">
              <checkbox id="alwaysClear"
                        preference="privacy.sanitize.sanitizeOnShutdown"
                        label="&clearOnClose.label;"
                        accesskey="&clearOnClose.accesskey;"
                        flex="1" />
              <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
              <hbox>
                <button id="clearDataSettings"
                        class="accessory-button"
                        label="&clearOnCloseSettings.label;"
                        accesskey="&clearOnCloseSettings.accesskey;"
                        searchkeywords="&clearDataSettings3.label;
                                        &historySection.label;
                                        &itemHistoryAndDownloads.label;
                                        &itemCookies.label;
                                        &itemActiveLogins.label;
                                        &itemCache.label;
                                        &itemFormSearchHistory.label;
                                        &dataSection.label;
                                        &itemSitePreferences.label;
                                        &itemOfflineApps.label;"/>
              </hbox>
            </hbox>
          </vbox>
        </vbox>
      </vbox>
    </deck>
    <vbox align="end">
      <button id="clearHistoryButton"
              class="accessory-button"
              icon="clear"
              label="&clearHistoryButton.label;"
              accesskey="&clearHistoryButton.accesskey;"/>
    </vbox>
  </hbox>
</groupbox>

<!-- Site Data -->
<groupbox id="siteDataGroup" hidden="true" data-category="panePrivacy" data-hidden-from-search="true">
  <caption><label>&siteData1.label;</label></caption>

  <hbox data-subcategory="sitedata" align="baseline">
    <vbox flex="1">
      <description class="description-with-side-element" flex="1">
        <html:span id="totalSiteDataSize" class="tail-with-learn-more"></html:span>
        <label id="siteDataLearnMoreLink" class="learnMore text-link">&siteDataLearnMoreLink.label;</label>
      </description>
      <radiogroup id="acceptCookies"
                  preference="network.cookie.cookieBehavior"
                  onsyncfrompreference="return gPrivacyPane.readAcceptCookies();"
                  onsynctopreference="return gPrivacyPane.writeAcceptCookies();">
        <hbox id="cookiesBox">
          <radio label="&acceptCookies4.label;"
                 value="0"
                 accesskey="&acceptCookies4.accesskey;"
                 flex="1" />
        </hbox>
        <hbox id="keepRow"
              class="indent"
              align="center">
          <label id="keepUntil"
                 control="keepCookiesUntil"
                 accesskey="&keepUntil2.accesskey;">&keepUntil2.label;</label>
          <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
          <hbox>
            <menulist id="keepCookiesUntil"
                      onsyncfrompreference="return gPrivacyPane.readKeepCookiesUntil();"
                      preference="network.cookie.lifetimePolicy">
              <menupopup>
                <menuitem label="&expire.label;" value="0"/>
                <menuitem label="&close.label;" value="2"/>
              </menupopup>
            </menulist>
          </hbox>
        </hbox>
        <hbox id="acceptThirdPartyRow"
              class="indent"
              align="center">
          <label id="acceptThirdPartyLabel" control="acceptThirdPartyMenu"
                 accesskey="&acceptThirdParty3.pre.accesskey;">&acceptThirdParty3.pre.label;</label>
          <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
          <hbox>
            <menulist id="acceptThirdPartyMenu" preference="network.cookie.cookieBehavior"
            onsyncfrompreference="return gPrivacyPane.readAcceptThirdPartyCookies();"
            onsynctopreference="return gPrivacyPane.writeAcceptThirdPartyCookies();">
              <menupopup>
                <menuitem label="&acceptThirdParty.always.label;" value="always"/>
                <menuitem label="&acceptThirdParty.visited.label;" value="visited"/>
                <menuitem label="&acceptThirdParty.never.label;" value="never"/>
              </menupopup>
            </menulist>
          </hbox>
        </hbox>
        <radio label="&blockCookies.label;"
               value="2"
               accesskey="&blockCookies.accesskey;"
               flex="1" />
      </radiogroup>
    </vbox>
    <vbox>
      <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
      <hbox>
        <button id="clearSiteDataButton"
            class="accessory-button"
            icon="clear"
            searchkeywords="&clearSiteData.label;
                            &clearCache.label;"
            label="&clearSiteData1.label;" accesskey="&clearSiteData1.accesskey;"/>
      </hbox>
      <hbox>
        <button id="siteDataSettings"
                class="accessory-button"
                label="&siteDataSettings1.label;"
                accesskey="&siteDataSettings1.accesskey;"
                searchkeywords="&window2.title;
                                &hostCol.label;
                                &cookiesCol.label;
                                &usageCol.label;"/>
      </hbox>
      <hbox>
        <button id="cookieExceptions"
                class="accessory-button"
                label="&cookieExceptions.label;" accesskey="&cookieExceptions.accesskey;"
                preference="pref.privacy.disable_button.cookie_exceptions"
                searchkeywords="&address2.label;
                                &block.label;
                                &session.label;
                                &allow.label;
                                &removepermission2.label;
                                &removeallpermissions2.label;
                                &button.cancel.label;
                                &button.ok.label;"/>
      </hbox>
    </vbox>
  </hbox>
</groupbox>

<!-- Address Bar -->
<groupbox id="locationBarGroup"
          data-category="panePrivacy"
          hidden="true">
  <caption><label>&addressBar.label;</label></caption>
  <label id="locationBarSuggestionLabel">&addressBar.suggest.label;</label>
  <checkbox id="historySuggestion" label="&locbar.history2.label;"
            accesskey="&locbar.history2.accesskey;"
            preference="browser.urlbar.suggest.history"/>
  <checkbox id="bookmarkSuggestion" label="&locbar.bookmarks.label;"
            accesskey="&locbar.bookmarks.accesskey;"
            preference="browser.urlbar.suggest.bookmark"/>
  <checkbox id="openpageSuggestion" label="&locbar.openpage.label;"
            accesskey="&locbar.openpage.accesskey;"
            preference="browser.urlbar.suggest.openpage"/>
  <label class="text-link" id="openSearchEnginePreferences">
    &suggestionSettings2.label;
  </label>
</groupbox>

<!-- Tracking -->
<groupbox id="trackingGroup" data-category="panePrivacy" hidden="true">
  <caption><label>&trackingProtectionHeader2.label;</label></caption>
  <vbox>
    <hbox align="start">
      <vbox flex="1">
        <description>
          &trackingProtection3.description;
          <label id="trackingProtectionLearnMore" class="learnMore text-link">&trackingProtectionLearnMore2.label;</label>
        </description>
      </vbox>
      <spacer flex="1"/>
    </hbox>
    <hbox>
      <vbox id="trackingProtectionBox" flex="1" hidden="true">
        <vbox>
          <hbox id="trackingProtectionExtensionContentLabel" align="center" hidden="true">
            <description control="disableTrackingProtectionExtension" flex="1"/>
          </hbox>
          <vbox>
            <description id="trackingProtectionDesc"
                         control="trackingProtectionRadioGroup">
               <label>&trackingProtection3.radioGroupLabel;</label>
            </description>
            <radiogroup id="trackingProtectionRadioGroup" aria-labelledby="trackingProtectionDesc">
              <radio value="always"
                     label="&trackingProtectionAlways.label;"
                     accesskey="&trackingProtectionAlways.accesskey;"/>
              <radio value="private"
                     label="&trackingProtectionPrivate.label;"
                     accesskey="&trackingProtectionPrivate.accesskey;"/>
              <radio value="never"
                     label="&trackingProtectionNever.label;"
                     accesskey="&trackingProtectionNever.accesskey;"/>
            </radiogroup>
          </vbox>
        </vbox>
      </vbox>
      <vbox id="trackingProtectionPBMBox" flex="1">
        <hbox id="trackingProtectionPBMExtensionContentLabel" align="center" hidden="true">
          <description control="disableTrackingProtectionExtension" flex="1"/>
        </hbox>
        <hbox align="start">
          <checkbox id="trackingProtectionPBM"
                    preference="privacy.trackingprotection.pbmode.enabled"
                    accesskey="&trackingProtectionPBM6.accesskey;"/>
          <label id="trackingProtectionPBMLabel" flex="1">&trackingProtectionPBM6.label;</label>
        </hbox>
      </vbox>
      <vbox id="trackingProtectionAdvancedSettings">
        <hbox id="trackingProtectionExtensionContentButton" hidden="true">
          <button id="disableTrackingProtectionExtension"
                  class="extension-controlled-button accessory-button"
                  flex="1"
                  label="&disableExtension.label;"/>
        </hbox>
        <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
        <hbox>
          <button id="trackingProtectionExceptions"
                  class="accessory-button"
                  flex="1"
                  hidden="true"
                  label="&trackingProtectionExceptions.label;"
                  accesskey="&trackingProtectionExceptions.accesskey;"
                  preference="pref.privacy.disable_button.tracking_protection_exceptions"
                  searchkeywords="&removepermission2.label;
                                  &removeallpermissions2.label;
                                  &button.cancel.label;
                                  &button.ok.label;"/>
        </hbox>
        <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
        <hbox>
          <button id="changeBlockList"
                  class="accessory-button"
                  flex="1"
                  label="&changeBlockList2.label;"
                  accesskey="&changeBlockList2.accesskey;"
                  preference="pref.privacy.disable_button.change_blocklist"
                  searchkeywords="&button.cancel.label; &button.ok.label;"/>
        </hbox>
      </vbox>
    </hbox>
    <vbox id="doNotTrackLearnMoreBox">
      <label><label class="tail-with-learn-more" data-l10n-id="do-not-track-description"></label><label
      class="learnMore text-link" href="https://www.mozilla.org/dnt"
      data-l10n-id="do-not-track-learn-more"></label></label>
      <radiogroup id="doNotTrackRadioGroup" aria-labelledby="doNotTrackDesc" preference="privacy.donottrackheader.enabled">
        <radio value="false" data-l10n-id="do-not-track-option-default"/>
        <radio value="true" data-l10n-id="do-not-track-option-always"/>
      </radiogroup>
    </vbox>
  </vbox>
</groupbox>

<hbox id="permissionsCategory"
      class="subcategory"
      hidden="true"
      data-category="panePrivacy">
  <label class="header-name" flex="1">&permissions.label;</label>
</hbox>

<!-- Permissions -->
<groupbox id="permissionsGroup" data-category="panePrivacy" hidden="true">
  <caption class="search-header" hidden="true"><label>&permissions.label;</label></caption>

  <grid>
    <columns>
      <column flex="1"/>
      <column/>
    </columns>
    <rows>
      <row id="locationSettingsRow" align="center">
        <description flex="1">
          <image class="geo-icon permission-icon" />
          <separator orient="vertical" class="thin"/>
          <label id="locationPermissionsLabel">&locationPermissions.label;</label>
        </description>
        <hbox pack="end">
          <button id="locationSettingsButton"
                  class="accessory-button"
                  label="&locationSettingsButton.label;"
                  accesskey="&locationSettingsButton.accesskey;"
                  searchkeywords="&removepermission2.label;
                                  &removeallpermissions2.label;
                                  &button.cancel.label;
                                  &button.ok.label;"/>
        </hbox>
      </row>

      <row id="cameraSettingsRow" align="center">
        <description flex="1">
          <image class="camera-icon permission-icon" />
          <separator orient="vertical" class="thin"/>
          <label id="cameraPermissionsLabel">&cameraPermissions.label;</label>
        </description>
        <hbox pack="end">
          <button id="cameraSettingsButton"
                  class="accessory-button"
                  label="&cameraSettingsButton.label;"
                  accesskey="&cameraSettingsButton.accesskey;"
                  searchkeywords="&removepermission2.label;
                                  &removeallpermissions2.label;
                                  &button.cancel.label;
                                  &button.ok.label;"/>
        </hbox>
      </row>

      <row id="microphoneSettingsRow" align="center">
        <description flex="1">
          <image class="microphone-icon permission-icon" />
          <separator orient="vertical" class="thin"/>
          <label id="microphonePermissionsLabel">&microphonePermissions.label;</label>
        </description>
        <hbox pack="end">
          <button id="microphoneSettingsButton"
                  class="accessory-button"
                  label="&microphoneSettingsButton.label;"
                  accesskey="&microphoneSettingsButton.accesskey;"
                  searchkeywords="&removepermission2.label;
                                  &removeallpermissions2.label;
                                  &button.cancel.label;
                                  &button.ok.label;"/>
        </hbox>
      </row>

      <row id="notificationSettingsRow" align="center">
        <description flex="1">
          <image class="desktop-notification-icon permission-icon" />
          <separator orient="vertical" class="thin"/>
          <label id="notificationPermissionsLabel" class="tail-with-learn-more">&notificationPermissions.label;</label>
          <label id="notificationPermissionsLearnMore"
                 class="learnMore text-link">&notificationPermissionsLearnMore.label;</label>
        </description>
        <hbox pack="end">
          <button id="notificationSettingsButton"
                  class="accessory-button"
                  label="&notificationSettingsButton.label;"
                  accesskey="&notificationSettingsButton.accesskey;"
                  searchkeywords="&removepermission2.label;
                                  &removeallpermissions2.label;
                                  &button.cancel.label;
                                  &button.ok.label;"/>
        </hbox>
      </row>
    </rows>
  </grid>

  <vbox id="notificationsDoNotDisturbBox" hidden="true">
    <checkbox id="notificationsDoNotDisturb" class="indent"/>
  </vbox>

  <separator flex="1"/>

  <hbox align="start">
    <checkbox id="popupPolicy" preference="dom.disable_open_during_load"
              label="&blockPopups.label;" accesskey="&blockPopups.accesskey;"
              onsyncfrompreference="return gPrivacyPane.updateButtons('popupPolicyButton',
                                         'dom.disable_open_during_load');"
              flex="1" />
    <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
    <hbox>
      <button id="popupPolicyButton"
              class="accessory-button"
              label="&popupExceptions.label;"
              accesskey="&popupExceptions.accesskey;"
              searchkeywords="&address2.label; &button.cancel.label; &button.ok.label;"/>
    </hbox>
  </hbox>

  <hbox id="addonInstallBox">
    <checkbox id="warnAddonInstall"
              label="&warnOnAddonInstall2.label;"
              accesskey="&warnOnAddonInstall2.accesskey;"
              preference="xpinstall.whitelist.required"
              onsyncfrompreference="return gPrivacyPane.readWarnAddonInstall();"
              flex="1" />
    <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
    <hbox>
      <button id="addonExceptions"
              class="accessory-button"
              label="&addonExceptions.label;"
              accesskey="&addonExceptions.accesskey;"
              searchkeywords="&address2.label;
                              &allow.label;
                              &removepermission2.label;
                              &removeallpermissions2.label;
                              &button.cancel.label;
                              &button.ok.label;"/>
    </hbox>
  </hbox>

  <vbox id="a11yPermissionsBox">
    <hbox flex="1" align="center">
      <checkbox id="a11yPrivacyCheckbox" class="tail-with-learn-more"
                label="&a11yPrivacy.checkbox.label;"
                accesskey="&a11yPrivacy.checkbox.accesskey;"
                oncommand="return gPrivacyPane.updateA11yPrefs(this.checked)"/>
      <label id="a11yLearnMoreLink" class="learnMore text-link"
              value="&a11yPrivacy.learnmore.label;"></label>
    </hbox>
  </vbox>
</groupbox>

<!-- Firefox Data Collection and Use -->
<hbox id="dataCollectionCategory"
      class="subcategory"
      hidden="true"
      data-category="panePrivacy">
  <label class="header-name" flex="1">&dataCollection.label;</label>
</hbox>

<groupbox id="dataCollectionGroup" data-category="panePrivacy" hidden="true">
  <caption class="search-header" hidden="true"><label>&dataCollection.label;</label></caption>

  <description>
    <label class="tail-with-learn-more">&dataCollectionDesc.label;</label><label id="dataCollectionPrivacyNotice" class="learnMore text-link">&dataCollectionPrivacyNotice.label;</label>
  </description>
  <vbox data-subcategory="reports">
    <description flex="1">
      <checkbox id="submitHealthReportBox" label="&enableHealthReport2.label;"
                class="tail-with-learn-more"
                accesskey="&enableHealthReport2.accesskey;"/>
      <label id="FHRLearnMore"
             class="learnMore text-link">&healthReportLearnMore.label;</label>
    </description>


    <hbox align="center">
      <checkbox id="automaticallySubmitCrashesBox"
                class="tail-with-learn-more"
                preference="browser.crashReports.unsubmittedCheck.autoSubmit2"
                label="&sendBackloggedCrashReports.label;"
                accesskey="&sendBackloggedCrashReports.accesskey;"
                flex="1"/>
      <label id="crashReporterLearnMore"
             class="learnMore text-link">&crashReporterLearnMore.label;</label>
    </hbox>
  </vbox>
</groupbox>

<hbox id="securityCategory"
      class="subcategory"
      hidden="true"
      data-category="panePrivacy">
  <label class="header-name" flex="1">&security.label;</label>
</hbox>

<!-- addons, forgery (phishing) UI Security -->
<groupbox id="browsingProtectionGroup" data-category="panePrivacy" hidden="true">
  <caption><label>&browsingProtection.label;</label></caption>
  <hbox align = "center">
    <checkbox id="enableSafeBrowsing"
              label="&enableSafeBrowsing.label;"
              class="tail-with-learn-more"
              accesskey="&enableSafeBrowsing.accesskey;" />
    <label id="enableSafeBrowsingLearnMore"
           class="learnMore text-link">&enableSafeBrowsingLearnMore.label;</label>
  </hbox>
  <vbox class="indent">
    <checkbox id="blockDownloads"
              label="&blockDownloads.label;"
              accesskey="&blockDownloads.accesskey;" />
    <checkbox id="blockUncommonUnwanted"
              label="&blockUncommonAndUnwanted.label;"
              accesskey="&blockUncommonAndUnwanted.accesskey;" />
  </vbox>
</groupbox>

<!-- Certificates -->
<groupbox id="certSelection" data-category="panePrivacy" hidden="true">
  <caption><label>&certificateTab.label;</label></caption>
  <description id="CertSelectionDesc" control="certSelection">&certPersonal2.description;</description>

  <!--
    The values on these radio buttons may look like l10n issues, but
    they're not - this preference uses *those strings* as its values.
    I KID YOU NOT.
  -->
  <radiogroup id="certSelection"
              preftype="string"
              preference="security.default_personal_cert"
              aria-labelledby="CertSelectionDesc">
    <radio label="&selectCerts.auto;"
           accesskey="&selectCerts.auto.accesskey;"
           value="Select Automatically"/>
    <radio label="&selectCerts.ask;"
           accesskey="&selectCerts.ask.accesskey;"
           value="Ask Every Time"/>
  </radiogroup>
  <hbox align="start">
    <checkbox id="enableOCSP"
              label="&enableOCSP.label;"
              accesskey="&enableOCSP.accesskey;"
              onsyncfrompreference="return gPrivacyPane.readEnableOCSP();"
              onsynctopreference="return gPrivacyPane.writeEnableOCSP();"
              preference="security.OCSP.enabled"
              flex="1" />
    <vbox>
      <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
      <hbox pack="end">
        <button id="viewCertificatesButton"
                class="accessory-button"
                label="&viewCerts2.label;"
                accesskey="&viewCerts2.accesskey;"
                preference="security.disable_button.openCertManager"
                searchkeywords="&certmgr.tab.mine;
                                &certmgr.tab.others2;
                                &certmgr.tab.websites3;
                                &certmgr.tab.ca;
                                &certmgr.mine2;
                                &certmgr.others2;
                                &certmgr.websites3;
                                &certmgr.cas2;
                                &certmgr.certname;
                                &certmgr.tokenname;
                                &certmgr.view2.label;
                                &certmgr.export.label;
                                &certmgr.delete2.label;"/>
      </hbox>
      <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
      <hbox pack="end">
        <button id="viewSecurityDevicesButton"
                class="accessory-button"
                label="&viewSecurityDevices2.label;"
                accesskey="&viewSecurityDevices2.accesskey;"
                preference="security.disable_button.openDeviceManager"
                searchkeywords="&devmgr.title;
                                &devmgr.devlist.label;
                                &devmgr.details.title;
                                &devmgr.details.title2;
                                &devmgr.button.login.label;
                                &devmgr.button.logout.label;
                                &devmgr.button.changepw.label;
                                &devmgr.button.load.label;
                                &devmgr.button.unload.label;"/>
      </hbox>
    </vbox>
  </hbox>
</groupbox>

<!-- Containers panel -->

<script type="application/javascript"
        src="chrome://browser/content/preferences/in-content/containers.js"/>

<hbox hidden="true"
      class="container-header-links"
      data-category="paneContainers">
  <label class="text-link" id="backContainersLink">&backLink2.label;</label>
</hbox>

<hbox id="header-containers"
      class="header"
      hidden="true"
      data-category="paneContainers">
  <label class="header-name" flex="1">&paneContainers.title;</label>
</hbox>

<!-- Containers -->
<groupbox id="browserContainersGroupPane" data-category="paneContainers" hidden="true"
          data-hidden-from-search="true" data-subpanel="true">
  <vbox id="browserContainersbox">

    <richlistbox id="containersView" orient="vertical" persist="lastSelectedType"
                 flex="1">
    </richlistbox>
  </vbox>
  <vbox>
    <hbox flex="1">
      <button id="containersAdd" oncommand="gContainersPane.onAddButtonCommand();" accesskey="&addButton.accesskey;" label="&addButton.label;"/>
    </hbox>
  </vbox>
</groupbox>

<!-- Sync panel -->

<script type="application/javascript"
        src="chrome://browser/content/preferences/in-content/sync.js"/>

<hbox id="firefoxAccountCategory"
      class="subcategory"
      hidden="true"
      data-category="paneSync">
  <label class="header-name" flex="1">&paneSync1.title;</label>
</hbox>

<deck id="weavePrefsDeck" data-category="paneSync" hidden="true"
      data-hidden-from-search="true">
  <groupbox id="noFxaAccount">
    <hbox>
      <vbox flex="1">
        <caption><label id="noFxaCaption">&signedOut.caption;</label></caption>
        <description id="noFxaDescription" flex="1">&signedOut.description;</description>
      </vbox>
      <vbox>
        <image class="fxaSyncIllustration"/>
      </vbox>
    </hbox>
    <hbox id="fxaNoLoginStatus" align="center" flex="1">
      <vbox>
        <image class="fxaProfileImage"/>
      </vbox>
      <vbox flex="1">
        <hbox align="center" flex="1">
          <hbox align="center" flex="1">
            <caption><label id="signedOutAccountBoxTitle">&signedOut.accountBox.title;</label></caption>
          </hbox>
          <button id="noFxaSignIn"
                  class="accessory-button"
                  label="&signedOut.accountBox.signin2;"
                  accesskey="&signedOut.accountBox.signin2.accesskey;"/>
        </hbox>
        <hbox align="center" flex="1">
          <html:a id="noFxaSignUp"
                  class="openLink"
                  accesskey="&signedOut.accountBox.create2.accesskey;">&signedOut.accountBox.create2;</html:a>
        </hbox>
      </vbox>
    </hbox>
    <label class="fxaMobilePromo">
        &mobilePromo3.start;<!-- We put these comments to avoid inserting white spaces
        --><image class="androidLink"></image><label id="fxaMobilePromo-android"
                  class="text-link"><!--
        -->&mobilePromo3.androidLink;</label><!--
        -->&mobilePromo3.iOSBefore;<!--
        --><image class="iOSLink"></image><label id="fxaMobilePromo-ios"
                  class="text-link"><!--
        -->&mobilePromo3.iOSLink;</label><!--
        -->&mobilePromo3.end;
    </label>
  </groupbox>

  <vbox id="hasFxaAccount">
    <hbox>
      <vbox id="fxaContentWrapper" flex="1">
        <groupbox id="fxaGroup">
          <caption class="search-header" hidden="true"><label>&paneSync1.title;</label></caption>

          <deck id="fxaLoginStatus" flex="1">

            <!-- logged in and verified and all is good -->
            <hbox id="fxaLoginVerified" align="center" flex="1">
              <image class="fxaProfileImage actionable"
                     role="button"
                     onclick="gSyncPane.openChangeProfileImage(event);"
                     onkeypress="gSyncPane.openChangeProfileImage(event);"
                     tooltiptext="&profilePicture.tooltip;"/>
              <vbox flex="1" pack="center">
                <hbox flex="1" align="baseline">
                  <caption><label id="fxaDisplayName" hidden="true"/></caption>
                  <label class="fxaEmailAddress" flex="1" crop="end"/>
                  <button id="fxaUnlinkButton"
                          class="accessory-button"
                          label="&disconnect3.label;"
                          accesskey="&disconnect3.accesskey;"/>
                </hbox>
                <hbox>
                  <html:a id="verifiedManage" class="openLink"
                          accesskey="&verifiedManage.accesskey;"
                          onkeypress="gSyncPane.openManageFirefoxAccount(event);">&verifiedManage.label;</html:a>
                </hbox>
              </vbox>
            </hbox>

            <!-- logged in to an unverified account -->
            <hbox id="fxaLoginUnverified">
              <vbox>
                <image class="fxaProfileImage"/>
              </vbox>
              <vbox flex="1" pack="center">
                <hbox>
                  <image class="fxaLoginRejectedWarning"/>
                  <description flex="1">
                    &signedInUnverified.beforename.label;
                    <label class="fxaEmailAddress"/>
                    &signedInUnverified.aftername.label;
                  </description>
                </hbox>
                <hbox class="fxaAccountBoxButtons">
                  <button id="verifyFxaAccount" label="&resendVerification.label;" accesskey="&resendVerification.accesskey;"></button>
                  <button id="unverifiedUnlinkFxaAccount" label="&cancelSetup.label;" accesskey="&cancelSetup.accesskey;"></button>
                </hbox>
              </vbox>
            </hbox>

            <!-- logged in locally but server rejected credentials -->
            <hbox id="fxaLoginRejected">
              <vbox>
                <image class="fxaProfileImage"/>
              </vbox>
              <vbox flex="1" pack="center">
                <hbox>
                  <image class="fxaLoginRejectedWarning"/>
                  <description flex="1">
                    &signedInLoginFailure.beforename.label;
                    <label class="fxaEmailAddress"/>
                    &signedInLoginFailure.aftername.label;
                  </description>
                </hbox>
                <hbox class="fxaAccountBoxButtons">
                  <button id="rejectReSignIn" label="&signIn.label;" accesskey="&signIn.accesskey;"></button>
                  <button id="rejectUnlinkFxaAccount" label="&cancelSetup.label;" accesskey="&cancelSetup.accesskey;"></button>
                </hbox>
              </vbox>
            </hbox>
          </deck>
        </groupbox>
        <groupbox id="syncOptions">
          <caption><label>&signedIn.settings.label;</label></caption>
          <description>&signedIn.settings.description;</description>
          <hbox id="fxaSyncEngines">
            <vbox flex="1">
              <!-- by design, no tooltip for bookmarks or history -->
              <checkbox label="&engine.bookmarks.label;"
                        accesskey="&engine.bookmarks.accesskey;"
                        preference="engine.bookmarks"/>
              <checkbox label="&engine.history.label;"
                        accesskey="&engine.history.accesskey;"
                        preference="engine.history"/>
              <checkbox label="&engine.tabs.label2;"
                        tooltiptext="&engine.tabs.title;"
                        accesskey="&engine.tabs.accesskey;"
                        preference="engine.tabs"/>
              <checkbox label="&engine.logins.label;"
                        tooltiptext="&engine.logins.title;"
                        accesskey="&engine.logins.accesskey;"
                        preference="engine.passwords"/>
            </vbox>
            <vbox flex="1">
              <checkbox label="&engine.addresses.label;"
                        accesskey="&engine.addresses.accesskey;"
                        tooltiptext="&engine.addresses.title;"
                        preference="engine.addresses"/>
              <checkbox label="&engine.creditcards.label;"
                        tooltiptext="&engine.creditcards.title;"
                        accesskey="&engine.creditcards.accesskey;"
                        preference="engine.creditcards"/>
              <checkbox label="&engine.addons.label;"
                        tooltiptext="&engine.addons.title;"
                        accesskey="&engine.addons.accesskey;"
                        preference="engine.addons"/>
              <checkbox
                        label="&engine.prefs.label;"
                        accesskey="&engine.prefs.accesskey;"
                        tooltiptext="&engine.prefs.title;"
                        preference="engine.prefs"/>
            </vbox>
            <spacer/>
          </hbox>
        </groupbox>
      </vbox>
    </hbox>
    <groupbox>
      <caption>
        <label control="fxaSyncComputerName">
          &fxaSyncDeviceName.label;
        </label>
      </caption>
      <hbox id="fxaDeviceName">
        <textbox id="fxaSyncComputerName" flex="1" disabled="true"/>
        <button id="fxaChangeDeviceName"
                label="&changeSyncDeviceName2.label;"
                accesskey="&changeSyncDeviceName2.accesskey;"/>
        <button id="fxaCancelChangeDeviceName"
                label="&cancelChangeSyncDeviceName.label;"
                accesskey="&cancelChangeSyncDeviceName.accesskey;"
                hidden="true"/>
        <button id="fxaSaveChangeDeviceName"
                label="&saveChangeSyncDeviceName.label;"
                accesskey="&saveChangeSyncDeviceName.accesskey;"
                hidden="true"/>
      </hbox>
    </groupbox>
    <vbox align="start">
      <label id="mobilePromo-singledevice"
             class="text-link fxaMobilePromo">&mobilepromo.singledevice;</label>
      <label id="mobilePromo-multidevice"
             class="text-link fxaMobilePromo">&mobilepromo.multidevice;</label>
    </vbox>
    <vbox id="tosPP-small" align="start">
      <label id="tosPP-small-ToS" class="text-link">
        &prefs.tosLink.label;
      </label>
      <label id="tosPP-small-PP" class="text-link">
        &fxaPrivacyNotice.link.label;
      </label>
    </vbox>
  </vbox>
</deck>
        </vbox>
      </vbox>
    </vbox>
  </hbox>

  <stack id="dialogStack" hidden="true"/>
  <vbox id="dialogTemplate" class="dialogOverlay" align="center" pack="center" topmost="true" hidden="true">
    <groupbox class="dialogBox"
              orient="vertical"
              pack="end"
              role="dialog"
              aria-labelledby="dialogTitle">
      <caption flex="1" align="center">
        <label class="dialogTitle" flex="1"></label>
        <button class="dialogClose close-icon"
                data-l10n-id="close-button"/>
      </caption>
      <browser class="dialogFrame"
               autoscroll="false"
               disablehistory="true"/>
    </groupbox>
  </vbox>
  </stack>
</page>
PK
!<�.F߼���@chrome/browser/content/browser/preferences/in-content/privacy.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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-globals-from extensionControlled.js */
/* import-globals-from preferences.js */

/* FIXME: ESlint globals workaround should be removed once bug 1395426 gets fixed */
/* globals DownloadUtils, LoadContextInfo */

ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
ChromeUtils.import("resource://gre/modules/PluralForm.jsm");

ChromeUtils.defineModuleGetter(this, "PluralForm",
  "resource://gre/modules/PluralForm.jsm");
ChromeUtils.defineModuleGetter(this, "LoginHelper",
  "resource://gre/modules/LoginHelper.jsm");
ChromeUtils.defineModuleGetter(this, "SiteDataManager",
  "resource:///modules/SiteDataManager.jsm");

XPCOMUtils.defineLazyPreferenceGetter(this, "trackingprotectionUiEnabled",
                                      "privacy.trackingprotection.ui.enabled");

ChromeUtils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");

const PREF_UPLOAD_ENABLED = "datareporting.healthreport.uploadEnabled";

const TRACKING_PROTECTION_KEY = "websites.trackingProtectionMode";
const TRACKING_PROTECTION_PREFS = ["privacy.trackingprotection.enabled",
                                   "privacy.trackingprotection.pbmode.enabled"];

XPCOMUtils.defineLazyGetter(this, "AlertsServiceDND", function() {
  try {
    let alertsService = Cc["@mozilla.org/alerts-service;1"]
      .getService(Ci.nsIAlertsService)
      .QueryInterface(Ci.nsIAlertsDoNotDisturb);
    // This will throw if manualDoNotDisturb isn't implemented.
    alertsService.manualDoNotDisturb;
    return alertsService;
  } catch (ex) {
    return undefined;
  }
});

Preferences.addAll([
  // Tracking
  { id: "privacy.trackingprotection.enabled", type: "bool" },
  { id: "privacy.trackingprotection.pbmode.enabled", type: "bool" },

  // Button prefs
  { id: "pref.privacy.disable_button.cookie_exceptions", type: "bool" },
  { id: "pref.privacy.disable_button.view_cookies", type: "bool" },
  { id: "pref.privacy.disable_button.change_blocklist", type: "bool" },
  { id: "pref.privacy.disable_button.tracking_protection_exceptions", type: "bool" },

  // Location Bar
  { id: "browser.urlbar.autocomplete.enabled", type: "bool" },
  { id: "browser.urlbar.suggest.bookmark", type: "bool" },
  { id: "browser.urlbar.suggest.history", type: "bool" },
  { id: "browser.urlbar.suggest.openpage", type: "bool" },

  // History
  { id: "places.history.enabled", type: "bool" },
  { id: "browser.formfill.enable", type: "bool" },
  { id: "privacy.history.custom", type: "bool" },
  // Cookies
  { id: "network.cookie.cookieBehavior", type: "int" },
  { id: "network.cookie.lifetimePolicy", type: "int" },
  { id: "network.cookie.blockFutureCookies", type: "bool" },
  // Clear Private Data
  { id: "privacy.sanitize.sanitizeOnShutdown", type: "bool" },
  { id: "privacy.sanitize.timeSpan", type: "int" },
  // Do not track
  { id: "privacy.donottrackheader.enabled", type: "bool" },

  // Popups
  { id: "dom.disable_open_during_load", type: "bool" },
  // Passwords
  { id: "signon.rememberSignons", type: "bool" },

  // Buttons
  { id: "pref.privacy.disable_button.view_passwords", type: "bool" },
  { id: "pref.privacy.disable_button.view_passwords_exceptions", type: "bool" },

  /* Certificates tab
   * security.default_personal_cert
   *   - a string:
   *       "Select Automatically"   select a certificate automatically when a site
   *                                requests one
   *       "Ask Every Time"         present a dialog to the user so he can select
   *                                the certificate to use on a site which
   *                                requests one
   */
  { id: "security.default_personal_cert", type: "string" },

  { id: "security.disable_button.openCertManager", type: "bool" },

  { id: "security.disable_button.openDeviceManager", type: "bool" },

  { id: "security.OCSP.enabled", type: "int" },

  // Add-ons, malware, phishing
  { id: "xpinstall.whitelist.required", type: "bool" },

  { id: "browser.safebrowsing.malware.enabled", type: "bool" },
  { id: "browser.safebrowsing.phishing.enabled", type: "bool" },

  { id: "browser.safebrowsing.downloads.enabled", type: "bool" },

  { id: "urlclassifier.malwareTable", type: "string" },

  { id: "browser.safebrowsing.downloads.remote.block_potentially_unwanted", type: "bool" },
  { id: "browser.safebrowsing.downloads.remote.block_uncommon", type: "bool" },
]);

// Data Choices tab
if (AppConstants.NIGHTLY_BUILD) {
  Preferences.add({ id: "browser.chrome.errorReporter.enabled", type: "bool" });
}
if (AppConstants.MOZ_CRASHREPORTER) {
  Preferences.add({ id: "browser.crashReports.unsubmittedCheck.autoSubmit2", type: "bool" });
}

var gPrivacyPane = {
  _pane: null,

  /**
   * Whether the prompt to restart Firefox should appear when changing the autostart pref.
   */
  _shouldPromptForRestart: true,

  /**
   * Show the Tracking Protection UI depending on the
   * privacy.trackingprotection.ui.enabled pref, and linkify its Learn More link
   */
  _initTrackingProtection() {
    if (!trackingprotectionUiEnabled) {
      return;
    }

    let link = document.getElementById("trackingProtectionLearnMore");
    let url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "tracking-protection";
    link.setAttribute("href", url);

    this.trackingProtectionReadPrefs();

    document.getElementById("trackingProtectionExceptions").hidden = false;
    document.getElementById("trackingProtectionBox").hidden = false;
    document.getElementById("trackingProtectionPBMBox").hidden = true;
  },

  /**
   * Linkify the Learn More link of the Private Browsing Mode Tracking
   * Protection UI.
   */
  _initTrackingProtectionPBM() {
    if (trackingprotectionUiEnabled) {
      return;
    }

    let link = document.getElementById("trackingProtectionLearnMore");
    let url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "tracking-protection-pbm";
    link.setAttribute("href", url);

    this._updateTrackingProtectionUI();
  },

  /**
   * Update the tracking protection UI to deal with extension control.
   */
  _updateTrackingProtectionUI() {
    let isLocked = TRACKING_PROTECTION_PREFS.some(
      pref => Services.prefs.prefIsLocked(pref));

    function setInputsDisabledState(isControlled) {
      let disabled = isLocked || isControlled;
      if (trackingprotectionUiEnabled) {
        document.querySelectorAll("#trackingProtectionRadioGroup > radio")
          .forEach((element) => {
            element.disabled = disabled;
          });
        document.querySelector("#trackingProtectionDesc > label")
          .disabled = disabled;
      } else {
        document.getElementById("trackingProtectionPBM").disabled = disabled;
        document.getElementById("trackingProtectionPBMLabel")
          .disabled = disabled;
      }
    }

    if (isLocked) {
      // An extension can't control this setting if either pref is locked.
      hideControllingExtension(TRACKING_PROTECTION_KEY);
      setInputsDisabledState(false);
    } else {
      handleControllingExtension(
        PREF_SETTING_TYPE,
        TRACKING_PROTECTION_KEY)
          .then(setInputsDisabledState);
    }
  },

  /**
   * Set up handlers for showing and hiding controlling extension info
   * for tracking protection.
   */
  _initTrackingProtectionExtensionControl() {
    let trackingProtectionObserver = {
      observe(subject, topic, data) {
        gPrivacyPane._updateTrackingProtectionUI();
      },
    };

    for (let pref of TRACKING_PROTECTION_PREFS) {
      Services.prefs.addObserver(pref, trackingProtectionObserver);
    }
    window.addEventListener("unload", () => {
      for (let pref of TRACKING_PROTECTION_PREFS) {
        Services.prefs.removeObserver(pref, trackingProtectionObserver);
      }
    });
  },

  /**
   * Initialize autocomplete to ensure prefs are in sync.
   */
  _initAutocomplete() {
    Cc["@mozilla.org/autocomplete/search;1?name=unifiedcomplete"]
      .getService(Ci.mozIPlacesAutoComplete);
  },

  /**
   * Sets up the UI for the number of days of history to keep, and updates the
   * label of the "Clear Now..." button.
   */
  init() {
    function setEventListener(aId, aEventType, aCallback) {
      document.getElementById(aId)
        .addEventListener(aEventType, aCallback.bind(gPrivacyPane));
    }

    this._updateSanitizeSettingsButton();
    this.initializeHistoryMode();
    this.updateHistoryModePane();
    this.updatePrivacyMicroControls();
    this.initAutoStartPrivateBrowsingReverter();
    this._initTrackingProtection();
    this._initTrackingProtectionPBM();
    this._initTrackingProtectionExtensionControl();
    this._initAutocomplete();

    Preferences.get("privacy.sanitize.sanitizeOnShutdown").on("change",
      gPrivacyPane._updateSanitizeSettingsButton.bind(gPrivacyPane));
    Preferences.get("browser.privatebrowsing.autostart").on("change",
      gPrivacyPane.updatePrivacyMicroControls.bind(gPrivacyPane));
    Preferences.get("privacy.trackingprotection.enabled").on("change",
      gPrivacyPane.trackingProtectionReadPrefs.bind(gPrivacyPane));
    Preferences.get("privacy.trackingprotection.pbmode.enabled").on("change",
      gPrivacyPane.trackingProtectionReadPrefs.bind(gPrivacyPane));
    setEventListener("historyMode", "command", function() {
      gPrivacyPane.updateHistoryModePane();
      gPrivacyPane.updateHistoryModePrefs();
      gPrivacyPane.updatePrivacyMicroControls();
      gPrivacyPane.updateAutostart();
    });
    setEventListener("clearHistoryButton", "command", function() {
      let historyMode = document.getElementById("historyMode");
      // Select "everything" in the clear history dialog if the
      // user has set their history mode to never remember history.
      gPrivacyPane.clearPrivateDataNow(historyMode.value == "dontremember");
    });
    setEventListener("openSearchEnginePreferences", "click", function(event) {
      if (event.button == 0) {
        gotoPref("search");
      }
      return false;
    });
    setEventListener("privateBrowsingAutoStart", "command",
      gPrivacyPane.updateAutostart);
    setEventListener("cookieExceptions", "command",
      gPrivacyPane.showCookieExceptions);
    setEventListener("clearDataSettings", "command",
      gPrivacyPane.showClearPrivateDataSettings);
    setEventListener("disableTrackingProtectionExtension", "command",
      makeDisableControllingExtension(
        PREF_SETTING_TYPE, TRACKING_PROTECTION_KEY));
    setEventListener("trackingProtectionRadioGroup", "command",
      gPrivacyPane.trackingProtectionWritePrefs);
    setEventListener("trackingProtectionExceptions", "command",
      gPrivacyPane.showTrackingProtectionExceptions);
    setEventListener("changeBlockList", "command",
      gPrivacyPane.showBlockLists);
    setEventListener("passwordExceptions", "command",
      gPrivacyPane.showPasswordExceptions);
    setEventListener("useMasterPassword", "command",
      gPrivacyPane.updateMasterPasswordButton);
    setEventListener("changeMasterPassword", "command",
      gPrivacyPane.changeMasterPassword);
    setEventListener("showPasswords", "command",
      gPrivacyPane.showPasswords);
    setEventListener("addonExceptions", "command",
      gPrivacyPane.showAddonExceptions);
    setEventListener("viewCertificatesButton", "command",
      gPrivacyPane.showCertificates);
    setEventListener("viewSecurityDevicesButton", "command",
      gPrivacyPane.showSecurityDevices);

    this._pane = document.getElementById("panePrivacy");
    this._initMasterPasswordUI();
    this._initSafeBrowsing();

    setEventListener("notificationSettingsButton", "command",
      gPrivacyPane.showNotificationExceptions);
    setEventListener("locationSettingsButton", "command",
      gPrivacyPane.showLocationExceptions);
    setEventListener("cameraSettingsButton", "command",
      gPrivacyPane.showCameraExceptions);
    setEventListener("microphoneSettingsButton", "command",
      gPrivacyPane.showMicrophoneExceptions);
    setEventListener("popupPolicyButton", "command",
      gPrivacyPane.showPopupExceptions);
    setEventListener("notificationsDoNotDisturb", "command",
      gPrivacyPane.toggleDoNotDisturbNotifications);

    let bundlePrefs = document.getElementById("bundlePreferences");

    if (AlertsServiceDND) {
      let notificationsDoNotDisturbBox =
        document.getElementById("notificationsDoNotDisturbBox");
      notificationsDoNotDisturbBox.removeAttribute("hidden");
      let checkbox = document.getElementById("notificationsDoNotDisturb");
      let brandName = document.getElementById("bundleBrand")
        .getString("brandShortName");
      checkbox.setAttribute("label",
        bundlePrefs.getFormattedString("pauseNotifications.label",
          [brandName]));
      checkbox.setAttribute("accesskey", bundlePrefs.getString("pauseNotifications.accesskey"));
      if (AlertsServiceDND.manualDoNotDisturb) {
        let notificationsDoNotDisturb =
          document.getElementById("notificationsDoNotDisturb");
        notificationsDoNotDisturb.setAttribute("checked", true);
      }
    }

    if (Services.prefs.getBoolPref("browser.storageManager.enabled")) {
      Services.obs.addObserver(this, "sitedatamanager:sites-updated");
      Services.obs.addObserver(this, "sitedatamanager:updating-sites");
      let unload = () => {
        window.removeEventListener("unload", unload);
        Services.obs.removeObserver(this, "sitedatamanager:sites-updated");
        Services.obs.removeObserver(this, "sitedatamanager:updating-sites");
      };
      window.addEventListener("unload", unload);
      SiteDataManager.updateSites();
      setEventListener("clearSiteDataButton", "command",
        gPrivacyPane.clearSiteData);
      setEventListener("siteDataSettings", "command",
        gPrivacyPane.showSiteDataSettings);
      let url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "storage-permissions";
      document.getElementById("siteDataLearnMoreLink").setAttribute("href", url);
      let siteDataGroup = document.getElementById("siteDataGroup");
      siteDataGroup.removeAttribute("data-hidden-from-search");
    }

    let notificationInfoURL =
      Services.urlFormatter.formatURLPref("app.support.baseURL") + "push";
    document.getElementById("notificationPermissionsLearnMore").setAttribute("href",
      notificationInfoURL);
    let drmInfoURL =
      Services.urlFormatter.formatURLPref("app.support.baseURL") + "drm-content";
    document.getElementById("playDRMContentLink").setAttribute("href", drmInfoURL);
    let emeUIEnabled = Services.prefs.getBoolPref("browser.eme.ui.enabled");
    // Force-disable/hide on WinXP:
    if (navigator.platform.toLowerCase().startsWith("win")) {
      emeUIEnabled = emeUIEnabled && parseFloat(Services.sysinfo.get("version")) >= 6;
    }
    if (!emeUIEnabled) {
      // Don't want to rely on .hidden for the toplevel groupbox because
      // of the pane hiding/showing code potentially interfering:
      document.getElementById("drmGroup").setAttribute("style", "display: none !important");
    }

    if (AppConstants.MOZ_DATA_REPORTING) {
      this.initDataCollection();
      if (AppConstants.NIGHTLY_BUILD) {
        this.initCollectBrowserErrors();
      }
      if (AppConstants.MOZ_CRASHREPORTER) {
        this.initSubmitCrashes();
      }
      this.initSubmitHealthReport();
      setEventListener("submitHealthReportBox", "command",
        gPrivacyPane.updateSubmitHealthReport);
    }
    this._initA11yState();
    let signonBundle = document.getElementById("signonBundle");
    let pkiBundle = document.getElementById("pkiBundle");
    appendSearchKeywords("passwordExceptions", [
      bundlePrefs.getString("savedLoginsExceptions_title"),
      bundlePrefs.getString("savedLoginsExceptions_desc3"),
    ]);
    appendSearchKeywords("showPasswords", [
      signonBundle.getString("loginsDescriptionAll2"),
    ]);
    appendSearchKeywords("cookieExceptions", [
      bundlePrefs.getString("cookiepermissionstext1"),
    ]);
    appendSearchKeywords("trackingProtectionExceptions", [
      bundlePrefs.getString("trackingprotectionpermissionstitle"),
      bundlePrefs.getString("trackingprotectionpermissionstext2"),
    ]);
    appendSearchKeywords("changeBlockList", [
      bundlePrefs.getString("blockliststitle"),
      bundlePrefs.getString("blockliststext"),
    ]);
    appendSearchKeywords("popupPolicyButton", [
      bundlePrefs.getString("popuppermissionstitle2"),
      bundlePrefs.getString("popuppermissionstext"),
    ]);
    appendSearchKeywords("notificationSettingsButton", [
      bundlePrefs.getString("notificationspermissionstitle2"),
      bundlePrefs.getString("notificationspermissionstext6"),
      bundlePrefs.getString("notificationspermissionsdisablelabel"),
      bundlePrefs.getString("notificationspermissionsdisabledescription"),
    ]);
    appendSearchKeywords("locationSettingsButton", [
      bundlePrefs.getString("locationpermissionstitle"),
      bundlePrefs.getString("locationpermissionstext2"),
      bundlePrefs.getString("locationpermissionsdisablelabel"),
      bundlePrefs.getString("locationpermissionsdisabledescription"),
    ]);
    appendSearchKeywords("cameraSettingsButton", [
      bundlePrefs.getString("camerapermissionstitle"),
      bundlePrefs.getString("camerapermissionstext2"),
      bundlePrefs.getString("camerapermissionsdisablelabel"),
      bundlePrefs.getString("camerapermissionsdisabledescription"),
    ]);
    appendSearchKeywords("microphoneSettingsButton", [
      bundlePrefs.getString("microphonepermissionstitle"),
      bundlePrefs.getString("microphonepermissionstext2"),
      bundlePrefs.getString("microphonepermissionsdisablelabel"),
      bundlePrefs.getString("microphonepermissionsdisabledescription"),
    ]);
    appendSearchKeywords("addonExceptions", [
      bundlePrefs.getString("addons_permissions_title2"),
      bundlePrefs.getString("addonspermissionstext"),
    ]);
    appendSearchKeywords("viewSecurityDevicesButton", [
      pkiBundle.getString("enable_fips"),
    ]);
    appendSearchKeywords("siteDataSettings", [
      bundlePrefs.getString("siteDataSettings3.description"),
      bundlePrefs.getString("removeAllCookies.label"),
      bundlePrefs.getString("removeSelectedCookies.label"),
    ]);

    if (!PrivateBrowsingUtils.enabled) {
      document.getElementById("privateBrowsingAutoStart").hidden = true;
      document.querySelector("menuitem[value='dontremember']").hidden = true;
    }

    // Notify observers that the UI is now ready
    Services.obs.notifyObservers(window, "privacy-pane-loaded");
  },

  // TRACKING PROTECTION MODE

  /**
   * Selects the right item of the Tracking Protection radiogroup.
   */
  trackingProtectionReadPrefs() {
    let enabledPref = Preferences.get("privacy.trackingprotection.enabled");
    let pbmPref = Preferences.get("privacy.trackingprotection.pbmode.enabled");
    let radiogroup = document.getElementById("trackingProtectionRadioGroup");

    this._updateTrackingProtectionUI();

    // Global enable takes precedence over enabled in Private Browsing.
    if (enabledPref.value) {
      radiogroup.value = "always";
    } else if (pbmPref.value) {
      radiogroup.value = "private";
    } else {
      radiogroup.value = "never";
    }
  },

  /**
   * Sets the pref values based on the selected item of the radiogroup.
   */
  trackingProtectionWritePrefs() {
    let enabledPref = Preferences.get("privacy.trackingprotection.enabled");
    let pbmPref = Preferences.get("privacy.trackingprotection.pbmode.enabled");
    let radiogroup = document.getElementById("trackingProtectionRadioGroup");

    switch (radiogroup.value) {
      case "always":
        enabledPref.value = true;
        pbmPref.value = true;
        break;
      case "private":
        enabledPref.value = false;
        pbmPref.value = true;
        break;
      case "never":
        enabledPref.value = false;
        pbmPref.value = false;
        break;
    }
  },

  // HISTORY MODE

  /**
   * The list of preferences which affect the initial history mode settings.
   * If the auto start private browsing mode pref is active, the initial
   * history mode would be set to "Don't remember anything".
   * If ALL of these preferences are set to the values that correspond
   * to keeping some part of history, and the auto-start
   * private browsing mode is not active, the initial history mode would be
   * set to "Remember everything".
   * Otherwise, the initial history mode would be set to "Custom".
   *
   * Extensions adding their own preferences can set values here if needed.
   */
  prefsForKeepingHistory: {
    "places.history.enabled": true, // History is enabled
    "browser.formfill.enable": true, // Form information is saved
    "privacy.sanitize.sanitizeOnShutdown": false, // Private date is NOT cleared on shutdown
  },

  /**
   * The list of control IDs which are dependent on the auto-start private
   * browsing setting, such that in "Custom" mode they would be disabled if
   * the auto-start private browsing checkbox is checked, and enabled otherwise.
   *
   * Extensions adding their own controls can append their IDs to this array if needed.
   */
  dependentControls: [
    "rememberHistory",
    "rememberForms",
    "alwaysClear",
    "clearDataSettings"
  ],

  /**
   * Check whether preferences values are set to keep history
   *
   * @param aPrefs an array of pref names to check for
   * @returns boolean true if all of the prefs are set to keep history,
   *                  false otherwise
   */
  _checkHistoryValues(aPrefs) {
    for (let pref of Object.keys(aPrefs)) {
      if (Preferences.get(pref).value != aPrefs[pref])
        return false;
    }
    return true;
  },

  /**
   * Initialize the history mode menulist based on the privacy preferences
   */
  initializeHistoryMode() {
    let mode;
    let getVal = aPref => Preferences.get(aPref).value;

    if (getVal("privacy.history.custom"))
      mode = "custom";
    else if (this._checkHistoryValues(this.prefsForKeepingHistory)) {
      if (getVal("browser.privatebrowsing.autostart"))
        mode = "dontremember";
      else
        mode = "remember";
    } else
      mode = "custom";

    document.getElementById("historyMode").value = mode;
  },

  /**
   * Update the selected pane based on the history mode menulist
   */
  updateHistoryModePane() {
    let selectedIndex = -1;
    switch (document.getElementById("historyMode").value) {
      case "remember":
        selectedIndex = 0;
        break;
      case "dontremember":
        selectedIndex = 1;
        break;
      case "custom":
        selectedIndex = 2;
        break;
    }
    document.getElementById("historyPane").selectedIndex = selectedIndex;
    Preferences.get("privacy.history.custom").value = selectedIndex == 2;
  },

  /**
   * Update the private browsing auto-start pref and the history mode
   * micro-management prefs based on the history mode menulist
   */
  updateHistoryModePrefs() {
    let pref = Preferences.get("browser.privatebrowsing.autostart");
    switch (document.getElementById("historyMode").value) {
      case "remember":
        if (pref.value)
          pref.value = false;

        // select the remember history option if needed
        Preferences.get("places.history.enabled").value = true;

        // select the remember forms history option
        Preferences.get("browser.formfill.enable").value = true;

        // select the clear on close option
        Preferences.get("privacy.sanitize.sanitizeOnShutdown").value = false;
        break;
      case "dontremember":
        if (!pref.value)
          pref.value = true;
        break;
    }
  },

  /**
   * Update the privacy micro-management controls based on the
   * value of the private browsing auto-start preference.
   */
  updatePrivacyMicroControls() {
    // Set "Keep cookies until..." to "I close Nightly" and disable the setting
    // when we're in auto private mode (or reset it back otherwise).
    document.getElementById("keepCookiesUntil").value = this.readKeepCookiesUntil();
    this.readAcceptCookies();

    if (document.getElementById("historyMode").value == "custom") {
      let disabled = Preferences.get("browser.privatebrowsing.autostart").value;
      this.dependentControls.forEach(function(aElement) {
        let control = document.getElementById(aElement);
        let preferenceId = control.getAttribute("preference");
        if (!preferenceId) {
          let dependentControlId = control.getAttribute("control");
          if (dependentControlId) {
            let dependentControl = document.getElementById(dependentControlId);
            preferenceId = dependentControl.getAttribute("preference");
          }
        }

        let preference = preferenceId ? Preferences.get(preferenceId) : {};
        control.disabled = disabled || preference.locked;
      });

      // adjust the checked state of the sanitizeOnShutdown checkbox
      document.getElementById("alwaysClear").checked = disabled ? false :
        Preferences.get("privacy.sanitize.sanitizeOnShutdown").value;

      // adjust the checked state of the remember history checkboxes
      document.getElementById("rememberHistory").checked = disabled ? false :
        Preferences.get("places.history.enabled").value;
      document.getElementById("rememberForms").checked = disabled ? false :
        Preferences.get("browser.formfill.enable").value;

      if (!disabled) {
        // adjust the Settings button for sanitizeOnShutdown
        this._updateSanitizeSettingsButton();
      }
    }
  },

  // CLEAR PRIVATE DATA

  /*
   * Preferences:
   *
   * privacy.sanitize.sanitizeOnShutdown
   * - true if the user's private data is cleared on startup according to the
   *   Clear Private Data settings, false otherwise
   */

  /**
   * Displays the Clear Private Data settings dialog.
   */
  showClearPrivateDataSettings() {
    gSubDialog.open("chrome://browser/content/preferences/sanitize.xul", "resizable=no");
  },


  /**
   * Displays a dialog from which individual parts of private data may be
   * cleared.
   */
  clearPrivateDataNow(aClearEverything) {
    var ts = Preferences.get("privacy.sanitize.timeSpan");
    var timeSpanOrig = ts.value;

    if (aClearEverything) {
      ts.value = 0;
    }

    gSubDialog.open("chrome://browser/content/sanitize.xul", "resizable=no", null, () => {
      // reset the timeSpan pref
      if (aClearEverything) {
        ts.value = timeSpanOrig;
      }

      Services.obs.notifyObservers(null, "clear-private-data");
    });
  },

  /**
   * Enables or disables the "Settings..." button depending
   * on the privacy.sanitize.sanitizeOnShutdown preference value
   */
  _updateSanitizeSettingsButton() {
    var settingsButton = document.getElementById("clearDataSettings");
    var sanitizeOnShutdownPref = Preferences.get("privacy.sanitize.sanitizeOnShutdown");

    settingsButton.disabled = !sanitizeOnShutdownPref.value;
  },

  toggleDoNotDisturbNotifications(event) {
    AlertsServiceDND.manualDoNotDisturb = event.target.checked;
  },

  // PRIVATE BROWSING

  /**
   * Initialize the starting state for the auto-start private browsing mode pref reverter.
   */
  initAutoStartPrivateBrowsingReverter() {
    let mode = document.getElementById("historyMode");
    let autoStart = document.getElementById("privateBrowsingAutoStart");
    this._lastMode = mode.selectedIndex;
    this._lastCheckState = autoStart.hasAttribute("checked");
  },

  _lastMode: null,
  _lastCheckState: null,
  async updateAutostart() {
    let mode = document.getElementById("historyMode");
    let autoStart = document.getElementById("privateBrowsingAutoStart");
    let pref = Preferences.get("browser.privatebrowsing.autostart");
    if ((mode.value == "custom" && this._lastCheckState == autoStart.checked) ||
      (mode.value == "remember" && !this._lastCheckState) ||
      (mode.value == "dontremember" && this._lastCheckState)) {
      // These are all no-op changes, so we don't need to prompt.
      this._lastMode = mode.selectedIndex;
      this._lastCheckState = autoStart.hasAttribute("checked");
      return;
    }

    if (!this._shouldPromptForRestart) {
      // We're performing a revert. Just let it happen.
      return;
    }

    let buttonIndex = await confirmRestartPrompt(autoStart.checked, 1,
      true, false);
    if (buttonIndex == CONFIRM_RESTART_PROMPT_RESTART_NOW) {
      pref.value = autoStart.hasAttribute("checked");
      Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart);
      return;
    }

    this._shouldPromptForRestart = false;

    if (this._lastCheckState) {
      autoStart.checked = "checked";
    } else {
      autoStart.removeAttribute("checked");
    }
    pref.value = autoStart.hasAttribute("checked");
    mode.selectedIndex = this._lastMode;
    mode.doCommand();

    this._shouldPromptForRestart = true;
  },

  /**
   * Displays fine-grained, per-site preferences for tracking protection.
   */
  showTrackingProtectionExceptions() {
    let bundlePreferences = document.getElementById("bundlePreferences");
    let params = {
      permissionType: "trackingprotection",
      hideStatusColumn: true,
      windowTitle: bundlePreferences.getString("trackingprotectionpermissionstitle"),
      introText: bundlePreferences.getString("trackingprotectionpermissionstext2"),
    };
    gSubDialog.open("chrome://browser/content/preferences/permissions.xul",
      null, params);
  },

  /**
   * Displays the available block lists for tracking protection.
   */
  showBlockLists() {
    var bundlePreferences = document.getElementById("bundlePreferences");
    let brandName = document.getElementById("bundleBrand")
      .getString("brandShortName");
    var params = {
      brandShortName: brandName,
      windowTitle: bundlePreferences.getString("blockliststitle"),
      introText: bundlePreferences.getString("blockliststext")
    };
    gSubDialog.open("chrome://browser/content/preferences/blocklists.xul",
      null, params);
  },

  // COOKIES AND SITE DATA

  /*
   * Preferences:
   *
   * network.cookie.cookieBehavior
   * - determines how the browser should handle cookies:
   *     0   means enable all cookies
   *     1   means reject all third party cookies
   *     2   means disable all cookies
   *     3   means reject third party cookies unless at least one is already set for the eTLD
   *         see netwerk/cookie/src/nsCookieService.cpp for details
   * network.cookie.lifetimePolicy
   * - determines how long cookies are stored:
   *     0   means keep cookies until they expire
   *     2   means keep cookies until the browser is closed
   */

  readKeepCookiesUntil() {
    let privateBrowsing = Preferences.get("browser.privatebrowsing.autostart").value;
    if (privateBrowsing) {
      return "2";
    }

    let lifetimePolicy = Preferences.get("network.cookie.lifetimePolicy").value;
    if (lifetimePolicy != Ci.nsICookieService.ACCEPT_NORMALLY &&
      lifetimePolicy != Ci.nsICookieService.ACCEPT_SESSION &&
      lifetimePolicy != Ci.nsICookieService.ACCEPT_FOR_N_DAYS) {
      return Ci.nsICookieService.ACCEPT_NORMALLY;
    }

    return lifetimePolicy;
  },

  /**
   * Reads the network.cookie.cookieBehavior preference value and
   * enables/disables the rest of the cookie UI accordingly.
   *
   * Returns "0" if cookies are accepted and "2" if they are entirely disabled.
   */
  readAcceptCookies() {
    let pref = Preferences.get("network.cookie.cookieBehavior");
    let acceptThirdPartyLabel = document.getElementById("acceptThirdPartyLabel");
    let acceptThirdPartyMenu = document.getElementById("acceptThirdPartyMenu");
    let keepUntilLabel = document.getElementById("keepUntil");
    let keepUntilMenu = document.getElementById("keepCookiesUntil");

    // enable the rest of the UI for anything other than "disable all cookies"
    let acceptCookies = (pref.value != 2);
    let cookieBehaviorLocked = Services.prefs.prefIsLocked("network.cookie.cookieBehavior");
    const acceptThirdPartyControlsDisabled = !acceptCookies || cookieBehaviorLocked;

    acceptThirdPartyLabel.disabled = acceptThirdPartyMenu.disabled = acceptThirdPartyControlsDisabled;

    let privateBrowsing = Preferences.get("browser.privatebrowsing.autostart").value;
    let cookieExpirationLocked = Services.prefs.prefIsLocked("network.cookie.lifetimePolicy");
    const keepUntilControlsDisabled = privateBrowsing || !acceptCookies || cookieExpirationLocked;
    keepUntilLabel.disabled = keepUntilMenu.disabled = keepUntilControlsDisabled;

    // Our top-level setting is a radiogroup that only sets "enable all"
    // and "disable all", so convert the pref value accordingly.
    return acceptCookies ? "0" : "2";
  },

  /**
   * Updates the "accept third party cookies" menu based on whether the
   * "accept cookies" or "block cookies" radio buttons are selected.
   */
  writeAcceptCookies() {
    var accept = document.getElementById("acceptCookies");
    var acceptThirdPartyMenu = document.getElementById("acceptThirdPartyMenu");

    // if we're enabling cookies, automatically select 'accept third party always'
    if (accept.value == "0")
      acceptThirdPartyMenu.selectedIndex = 0;

    return parseInt(accept.value, 10);
  },

  /**
   * Converts between network.cookie.cookieBehavior and the third-party cookie UI
   */
  readAcceptThirdPartyCookies() {
    var pref = Preferences.get("network.cookie.cookieBehavior");
    switch (pref.value) {
      case 0:
        return "always";
      case 1:
        return "never";
      case 2:
        return "never";
      case 3:
        return "visited";
      default:
        return undefined;
    }
  },

  writeAcceptThirdPartyCookies() {
    var accept = document.getElementById("acceptThirdPartyMenu").selectedItem;
    switch (accept.value) {
      case "always":
        return 0;
      case "visited":
        return 3;
      case "never":
        return 1;
      default:
        return undefined;
    }
  },

  /**
   * Displays fine-grained, per-site preferences for cookies.
   */
  showCookieExceptions() {
    var bundlePreferences = document.getElementById("bundlePreferences");
    var params = {
      blockVisible: true,
      sessionVisible: true,
      allowVisible: true,
      prefilledHost: "",
      permissionType: "cookie",
      windowTitle: bundlePreferences.getString("cookiepermissionstitle1"),
      introText: bundlePreferences.getString("cookiepermissionstext1")
    };
    gSubDialog.open("chrome://browser/content/preferences/permissions.xul",
      null, params);
  },

  showSiteDataSettings() {
    gSubDialog.open("chrome://browser/content/preferences/siteDataSettings.xul");
  },

  toggleSiteData(shouldShow) {
    let clearButton = document.getElementById("clearSiteDataButton");
    let settingsButton = document.getElementById("siteDataSettings");
    clearButton.disabled = !shouldShow;
    settingsButton.disabled = !shouldShow;
  },

  showSiteDataLoading() {
    let totalSiteDataSizeLabel = document.getElementById("totalSiteDataSize");
    let prefStrBundle = document.getElementById("bundlePreferences");
    totalSiteDataSizeLabel.textContent = prefStrBundle.getString("loadingSiteDataSize1");
  },

  updateTotalDataSizeLabel(siteDataUsage) {
    SiteDataManager.getCacheSize().then(function(cacheUsage) {
      let prefStrBundle = document.getElementById("bundlePreferences");
      let totalSiteDataSizeLabel = document.getElementById("totalSiteDataSize");
      let totalUsage = siteDataUsage + cacheUsage;
      let size = DownloadUtils.convertByteUnits(totalUsage);
      totalSiteDataSizeLabel.textContent = prefStrBundle.getFormattedString("totalSiteDataSize2", size);
    });
  },

  clearSiteData() {
    gSubDialog.open("chrome://browser/content/preferences/clearSiteData.xul");
  },

  // GEOLOCATION

  /**
   * Displays the location exceptions dialog where specific site location
   * preferences can be set.
   */
  showLocationExceptions() {
    let bundlePreferences = document.getElementById("bundlePreferences");
    let params = { permissionType: "geo" };
    params.windowTitle = bundlePreferences.getString("locationpermissionstitle");
    params.introText = bundlePreferences.getString("locationpermissionstext2");
    params.disablePermissionsLabel = bundlePreferences.getString("locationpermissionsdisablelabel");
    params.disablePermissionsDescription = bundlePreferences.getString("locationpermissionsdisabledescription");

    gSubDialog.open("chrome://browser/content/preferences/sitePermissions.xul",
      "resizable=yes", params);
  },

  // CAMERA

  /**
   * Displays the camera exceptions dialog where specific site camera
   * preferences can be set.
   */
  showCameraExceptions() {
    let bundlePreferences = document.getElementById("bundlePreferences");
    let params = { permissionType: "camera" };
    params.windowTitle = bundlePreferences.getString("camerapermissionstitle");
    params.introText = bundlePreferences.getString("camerapermissionstext2");
    params.disablePermissionsLabel = bundlePreferences.getString("camerapermissionsdisablelabel");
    params.disablePermissionsDescription = bundlePreferences.getString("camerapermissionsdisabledescription");

    gSubDialog.open("chrome://browser/content/preferences/sitePermissions.xul",
      "resizable=yes", params);
  },

  // MICROPHONE

  /**
   * Displays the microphone exceptions dialog where specific site microphone
   * preferences can be set.
   */
  showMicrophoneExceptions() {
    let bundlePreferences = document.getElementById("bundlePreferences");
    let params = { permissionType: "microphone" };
    params.windowTitle = bundlePreferences.getString("microphonepermissionstitle");
    params.introText = bundlePreferences.getString("microphonepermissionstext2");
    params.disablePermissionsLabel = bundlePreferences.getString("microphonepermissionsdisablelabel");
    params.disablePermissionsDescription = bundlePreferences.getString("microphonepermissionsdisabledescription");

    gSubDialog.open("chrome://browser/content/preferences/sitePermissions.xul",
      "resizable=yes", params);
  },

  // NOTIFICATIONS

  /**
   * Displays the notifications exceptions dialog where specific site notification
   * preferences can be set.
   */
  showNotificationExceptions() {
    let bundlePreferences = document.getElementById("bundlePreferences");
    let params = { permissionType: "desktop-notification" };
    params.windowTitle = bundlePreferences.getString("notificationspermissionstitle2");
    params.introText = bundlePreferences.getString("notificationspermissionstext6");
    params.disablePermissionsLabel = bundlePreferences.getString("notificationspermissionsdisablelabel");
    params.disablePermissionsDescription = bundlePreferences.getString("notificationspermissionsdisabledescription");

    gSubDialog.open("chrome://browser/content/preferences/sitePermissions.xul",
      "resizable=yes", params);

    try {
      Services.telemetry
        .getHistogramById("WEB_NOTIFICATION_EXCEPTIONS_OPENED").add();
    } catch (e) { }
  },


  // POP-UPS

  /**
   * Displays the popup exceptions dialog where specific site popup preferences
   * can be set.
   */
  showPopupExceptions() {
    var bundlePreferences = document.getElementById("bundlePreferences");
    var params = {
      blockVisible: false, sessionVisible: false, allowVisible: true,
      prefilledHost: "", permissionType: "popup"
    };
    params.windowTitle = bundlePreferences.getString("popuppermissionstitle2");
    params.introText = bundlePreferences.getString("popuppermissionstext");

    gSubDialog.open("chrome://browser/content/preferences/permissions.xul",
      "resizable=yes", params);
  },

  // UTILITY FUNCTIONS

  /**
   * Utility function to enable/disable the button specified by aButtonID based
   * on the value of the Boolean preference specified by aPreferenceID.
   */
  updateButtons(aButtonID, aPreferenceID) {
    var button = document.getElementById(aButtonID);
    var preference = Preferences.get(aPreferenceID);
    button.disabled = !preference.value;
    return undefined;
  },

  // BEGIN UI CODE

  /*
   * Preferences:
   *
   * dom.disable_open_during_load
   * - true if popups are blocked by default, false otherwise
   */

  // POP-UPS

  /**
   * Displays a dialog in which the user can view and modify the list of sites
   * where passwords are never saved.
   */
  showPasswordExceptions() {
    var bundlePrefs = document.getElementById("bundlePreferences");
    var params = {
      blockVisible: true,
      sessionVisible: false,
      allowVisible: false,
      hideStatusColumn: true,
      prefilledHost: "",
      permissionType: "login-saving",
      windowTitle: bundlePrefs.getString("savedLoginsExceptions_title"),
      introText: bundlePrefs.getString("savedLoginsExceptions_desc3")
    };

    gSubDialog.open("chrome://browser/content/preferences/permissions.xul",
      null, params);
  },

  /**
   * Initializes master password UI: the "use master password" checkbox, selects
   * the master password button to show, and enables/disables it as necessary.
   * The master password is controlled by various bits of NSS functionality, so
   * the UI for it can't be controlled by the normal preference bindings.
   */
  _initMasterPasswordUI() {
    var noMP = !LoginHelper.isMasterPasswordSet();

    var button = document.getElementById("changeMasterPassword");
    button.disabled = noMP;

    var checkbox = document.getElementById("useMasterPassword");
    checkbox.checked = !noMP;
    checkbox.disabled = noMP && !Services.policies.isAllowed("createMasterPassword");
  },

  /**
   * Enables/disables the master password button depending on the state of the
   * "use master password" checkbox, and prompts for master password removal if
   * one is set.
   */
  updateMasterPasswordButton() {
    var checkbox = document.getElementById("useMasterPassword");
    var button = document.getElementById("changeMasterPassword");
    button.disabled = !checkbox.checked;

    // unchecking the checkbox should try to immediately remove the master
    // password, because it's impossible to non-destructively remove the master
    // password used to encrypt all the passwords without providing it (by
    // design), and it would be extremely odd to pop up that dialog when the
    // user closes the prefwindow and saves his settings
    if (!checkbox.checked)
      this._removeMasterPassword();
    else
      this.changeMasterPassword();

    this._initMasterPasswordUI();
  },

  /**
   * Displays the "remove master password" dialog to allow the user to remove
   * the current master password.  When the dialog is dismissed, master password
   * UI is automatically updated.
   */
  _removeMasterPassword() {
    var secmodDB = Cc["@mozilla.org/security/pkcs11moduledb;1"].
      getService(Ci.nsIPKCS11ModuleDB);
    if (secmodDB.isFIPSEnabled) {
      var bundle = document.getElementById("bundlePreferences");
      Services.prompt.alert(window,
        bundle.getString("pw_change_failed_title"),
        bundle.getString("pw_change2empty_in_fips_mode"));
      this._initMasterPasswordUI();
    } else {
      gSubDialog.open("chrome://mozapps/content/preferences/removemp.xul",
        null, null, this._initMasterPasswordUI.bind(this));
    }
  },

  /**
   * Displays a dialog in which the master password may be changed.
   */
  changeMasterPassword() {
    gSubDialog.open("chrome://mozapps/content/preferences/changemp.xul",
      "resizable=no", null, this._initMasterPasswordUI.bind(this));
  },

  /**
 * Shows the sites where the user has saved passwords and the associated login
 * information.
 */
  showPasswords() {
    gSubDialog.open("chrome://passwordmgr/content/passwordManager.xul");
  },

  /**
   * Enables/disables the Exceptions button used to configure sites where
   * passwords are never saved. When browser is set to start in Private
   * Browsing mode, the "Remember passwords" UI is useless, so we disable it.
   */
  readSavePasswords() {
    var pref = Preferences.get("signon.rememberSignons");
    var excepts = document.getElementById("passwordExceptions");

    if (PrivateBrowsingUtils.permanentPrivateBrowsing) {
      document.getElementById("savePasswords").disabled = true;
      excepts.disabled = true;
      return false;
    }
    excepts.disabled = !pref.value;
    // don't override pref value in UI
    return undefined;
  },

  /**
   * Enables/disables the add-ons Exceptions button depending on whether
   * or not add-on installation warnings are displayed.
   */
  readWarnAddonInstall() {
    var warn = Preferences.get("xpinstall.whitelist.required");
    var exceptions = document.getElementById("addonExceptions");

    exceptions.disabled = !warn.value;

    // don't override the preference value
    return undefined;
  },

  _initSafeBrowsing() {
    let enableSafeBrowsing = document.getElementById("enableSafeBrowsing");
    let blockDownloads = document.getElementById("blockDownloads");
    let blockUncommonUnwanted = document.getElementById("blockUncommonUnwanted");

    let safeBrowsingPhishingPref = Preferences.get("browser.safebrowsing.phishing.enabled");
    let safeBrowsingMalwarePref = Preferences.get("browser.safebrowsing.malware.enabled");

    let blockDownloadsPref = Preferences.get("browser.safebrowsing.downloads.enabled");
    let malwareTable = Preferences.get("urlclassifier.malwareTable");

    let blockUnwantedPref = Preferences.get("browser.safebrowsing.downloads.remote.block_potentially_unwanted");
    let blockUncommonPref = Preferences.get("browser.safebrowsing.downloads.remote.block_uncommon");

    let learnMoreLink = document.getElementById("enableSafeBrowsingLearnMore");
    let phishingUrl = Services.urlFormatter.formatURLPref("app.support.baseURL") + "phishing-malware";
    learnMoreLink.setAttribute("href", phishingUrl);

    enableSafeBrowsing.addEventListener("command", function() {
      safeBrowsingPhishingPref.value = enableSafeBrowsing.checked;
      safeBrowsingMalwarePref.value = enableSafeBrowsing.checked;

      if (enableSafeBrowsing.checked) {
        if (blockDownloads) {
          blockDownloads.removeAttribute("disabled");
          if (blockDownloads.checked) {
            blockUncommonUnwanted.removeAttribute("disabled");
          }
        } else {
          blockUncommonUnwanted.removeAttribute("disabled");
        }
      } else {
        if (blockDownloads) {
          blockDownloads.setAttribute("disabled", "true");
        }
        blockUncommonUnwanted.setAttribute("disabled", "true");
      }
    });

    if (blockDownloads) {
      blockDownloads.addEventListener("command", function() {
        blockDownloadsPref.value = blockDownloads.checked;
        if (blockDownloads.checked) {
          blockUncommonUnwanted.removeAttribute("disabled");
        } else {
          blockUncommonUnwanted.setAttribute("disabled", "true");
        }
      });
    }

    blockUncommonUnwanted.addEventListener("command", function() {
      blockUnwantedPref.value = blockUncommonUnwanted.checked;
      blockUncommonPref.value = blockUncommonUnwanted.checked;

      let malware = malwareTable.value
        .split(",")
        .filter(x => x !== "goog-unwanted-proto" &&
                     x !== "goog-unwanted-shavar" &&
                     x !== "test-unwanted-simple");

      if (blockUncommonUnwanted.checked) {
        if (malware.includes("goog-malware-shavar")) {
          malware.push("goog-unwanted-shavar");
        } else {
          malware.push("goog-unwanted-proto");
        }

        malware.push("test-unwanted-simple");
      }

      // sort alphabetically to keep the pref consistent
      malware.sort();

      malwareTable.value = malware.join(",");

      // Force an update after changing the malware table.
      let listmanager = Cc["@mozilla.org/url-classifier/listmanager;1"]
                        .getService(Ci.nsIUrlListManager);
      if (listmanager) {
        listmanager.forceUpdates(malwareTable.value);
      }
    });

    // set initial values

    enableSafeBrowsing.checked = safeBrowsingPhishingPref.value && safeBrowsingMalwarePref.value;
    if (!enableSafeBrowsing.checked) {
      if (blockDownloads) {
        blockDownloads.setAttribute("disabled", "true");
      }

      blockUncommonUnwanted.setAttribute("disabled", "true");
    }

    if (blockDownloads) {
      blockDownloads.checked = blockDownloadsPref.value;
      if (!blockDownloadsPref.value) {
        blockUncommonUnwanted.setAttribute("disabled", "true");
      }
    }

    blockUncommonUnwanted.checked = blockUnwantedPref.value && blockUncommonPref.value;
  },

  /**
   * Displays the exceptions lists for add-on installation warnings.
   */
  showAddonExceptions() {
    var bundlePrefs = document.getElementById("bundlePreferences");

    var params = this._addonParams;
    if (!params.windowTitle || !params.introText) {
      params.windowTitle = bundlePrefs.getString("addons_permissions_title2");
      params.introText = bundlePrefs.getString("addonspermissionstext");
    }

    gSubDialog.open("chrome://browser/content/preferences/permissions.xul",
      null, params);
  },

  /**
   * Parameters for the add-on install permissions dialog.
   */
  _addonParams:
  {
    blockVisible: false,
    sessionVisible: false,
    allowVisible: true,
    prefilledHost: "",
    permissionType: "install"
  },

  /**
   * readEnableOCSP is used by the preferences UI to determine whether or not
   * the checkbox for OCSP fetching should be checked (it returns true if it
   * should be checked and false otherwise). The about:config preference
   * "security.OCSP.enabled" is an integer rather than a boolean, so it can't be
   * directly mapped from {true,false} to {checked,unchecked}. The possible
   * values for "security.OCSP.enabled" are:
   * 0: fetching is disabled
   * 1: fetch for all certificates
   * 2: fetch only for EV certificates
   * Hence, if "security.OCSP.enabled" is non-zero, the checkbox should be
   * checked. Otherwise, it should be unchecked.
   */
  readEnableOCSP() {
    var preference = Preferences.get("security.OCSP.enabled");
    // This is the case if the preference is the default value.
    if (preference.value === undefined) {
      return true;
    }
    return preference.value != 0;
  },

  /**
   * writeEnableOCSP is used by the preferences UI to map the checked/unchecked
   * state of the OCSP fetching checkbox to the value that the preference
   * "security.OCSP.enabled" should be set to (it returns that value). See the
   * readEnableOCSP documentation for more background. We unfortunately don't
   * have enough information to map from {true,false} to all possible values for
   * "security.OCSP.enabled", but a reasonable alternative is to map from
   * {true,false} to {<the default value>,0}. That is, if the box is checked,
   * "security.OCSP.enabled" will be set to whatever default it should be, given
   * the platform and channel. If the box is unchecked, the preference will be
   * set to 0. Obviously this won't work if the default is 0, so we will have to
   * revisit this if we ever set it to 0.
   */
  writeEnableOCSP() {
    var checkbox = document.getElementById("enableOCSP");
    var defaults = Services.prefs.getDefaultBranch(null);
    var defaultValue = defaults.getIntPref("security.OCSP.enabled");
    return checkbox.checked ? defaultValue : 0;
  },

  /**
   * Displays the user's certificates and associated options.
   */
  showCertificates() {
    gSubDialog.open("chrome://pippki/content/certManager.xul");
  },

  /**
   * Displays a dialog from which the user can manage his security devices.
   */
  showSecurityDevices() {
    gSubDialog.open("chrome://pippki/content/device_manager.xul");
  },

  initDataCollection() {
    this._setupLearnMoreLink("toolkit.datacollection.infoURL",
      "dataCollectionPrivacyNotice");
  },

  initCollectBrowserErrors() {
    this._setupLearnMoreLink("browser.chrome.errorReporter.infoURL",
      "collectBrowserErrorsLearnMore");
  },

  initSubmitCrashes() {
    this._setupLearnMoreLink("toolkit.crashreporter.infoURL",
      "crashReporterLearnMore");
  },

  /**
   * Set up or hide the Learn More links for various data collection options
   */
  _setupLearnMoreLink(pref, element) {
    // set up the Learn More link with the correct URL
    let url = Services.urlFormatter.formatURLPref(pref);
    let el = document.getElementById(element);

    if (url) {
      el.setAttribute("href", url);
    } else {
      el.setAttribute("hidden", "true");
    }
  },

  /**
   * Initialize the health report service reference and checkbox.
   */
  initSubmitHealthReport() {
    this._setupLearnMoreLink("datareporting.healthreport.infoURL", "FHRLearnMore");

    let checkbox = document.getElementById("submitHealthReportBox");

    // Telemetry is only sending data if MOZ_TELEMETRY_REPORTING is defined.
    // We still want to display the preferences panel if that's not the case, but
    // we want it to be disabled and unchecked.
    if (Services.prefs.prefIsLocked(PREF_UPLOAD_ENABLED) ||
      !AppConstants.MOZ_TELEMETRY_REPORTING) {
      checkbox.setAttribute("disabled", "true");
      return;
    }

    checkbox.checked = Services.prefs.getBoolPref(PREF_UPLOAD_ENABLED) &&
      AppConstants.MOZ_TELEMETRY_REPORTING;
  },

  /**
   * Update the health report preference with state from checkbox.
   */
  updateSubmitHealthReport() {
    let checkbox = document.getElementById("submitHealthReportBox");
    Services.prefs.setBoolPref(PREF_UPLOAD_ENABLED, checkbox.checked);
  },

  observe(aSubject, aTopic, aData) {
    switch (aTopic) {
      case "sitedatamanager:updating-sites":
        // While updating, we want to disable this section and display loading message until updated
        this.toggleSiteData(false);
        this.showSiteDataLoading();
        break;

      case "sitedatamanager:sites-updated":
        this.toggleSiteData(true);
        SiteDataManager.getTotalUsage()
          .then(this.updateTotalDataSizeLabel.bind(this));
        break;
    }
  },

  // Accessibility checkbox helpers
  _initA11yState() {
    this._initA11yString();
    let checkbox = document.getElementById("a11yPrivacyCheckbox");
    switch (Services.prefs.getIntPref("accessibility.force_disabled")) {
      case 1: // access blocked
        checkbox.checked = true;
        break;
      case -1: // a11y is forced on for testing
      case 0: // access allowed
        checkbox.checked = false;
        break;
    }
  },

  _initA11yString() {
    let a11yLearnMoreLink =
      Services.urlFormatter.formatURLPref("accessibility.support.url");
    document.getElementById("a11yLearnMoreLink")
      .setAttribute("href", a11yLearnMoreLink);
  },

  async updateA11yPrefs(checked) {
    let buttonIndex = await confirmRestartPrompt(checked, 0, true, false);
    if (buttonIndex == CONFIRM_RESTART_PROMPT_RESTART_NOW) {
      Services.prefs.setIntPref("accessibility.force_disabled", checked ? 1 : 0);
      Services.telemetry.scalarSet("preferences.prevent_accessibility_services", true);
      Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart);
    }

    // Revert the checkbox in case we didn't quit
    document.getElementById("a11yPrivacyCheckbox").checked = !checked;
  }
};
PK
!<Y��z�X�X?chrome/browser/content/browser/preferences/in-content/search.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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-globals-from extensionControlled.js */
/* import-globals-from preferences.js */

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.defineModuleGetter(this, "PlacesUtils",
                               "resource://gre/modules/PlacesUtils.jsm");
ChromeUtils.defineModuleGetter(this, "ExtensionSettingsStore",
                               "resource://gre/modules/ExtensionSettingsStore.jsm");

Preferences.addAll([
  { id: "browser.search.suggest.enabled", type: "bool" },
  { id: "browser.urlbar.suggest.searches", type: "bool" },
  { id: "browser.search.hiddenOneOffs", type: "unichar" },
  { id: "browser.search.widget.inNavBar", type: "bool" },
  { id: "browser.urlbar.matchBuckets", type: "string" },
]);

const ENGINE_FLAVOR = "text/x-moz-search-engine";
const SEARCH_TYPE = "default_search";
const SEARCH_KEY = "defaultSearch";

var gEngineView = null;

var gSearchPane = {

  /**
   * Initialize autocomplete to ensure prefs are in sync.
   */
  _initAutocomplete() {
    Cc["@mozilla.org/autocomplete/search;1?name=unifiedcomplete"]
      .getService(Ci.mozIPlacesAutoComplete);
  },

  init() {
    gEngineView = new EngineView(new EngineStore());
    document.getElementById("engineList").view = gEngineView;
    this.buildDefaultEngineDropDown();

    if (Services.policies &&
        !Services.policies.isAllowed("installSearchEngine")) {
      document.getElementById("addEnginesBox").hidden = true;
    } else {
      let addEnginesLink = document.getElementById("addEngines");
      let searchEnginesURL = Services.wm.getMostRecentWindow("navigator:browser")
                                        .BrowserSearch.searchEnginesURL;
      addEnginesLink.setAttribute("href", searchEnginesURL);
    }

    window.addEventListener("click", this);
    window.addEventListener("command", this);
    window.addEventListener("dragstart", this);
    window.addEventListener("keypress", this);
    window.addEventListener("select", this);
    window.addEventListener("blur", this, true);

    Services.obs.addObserver(this, "browser-search-engine-modified");
    window.addEventListener("unload", () => {
      Services.obs.removeObserver(this, "browser-search-engine-modified");
    });

    this._initAutocomplete();

    let suggestsPref = Preferences.get("browser.search.suggest.enabled");
    let urlbarSuggestsPref = Preferences.get("browser.urlbar.suggest.searches");
    let updateSuggestionCheckboxes = this._updateSuggestionCheckboxes.bind(this);
    suggestsPref.on("change", updateSuggestionCheckboxes);
    urlbarSuggestsPref.on("change", updateSuggestionCheckboxes);
    let urlbarSuggests = document.getElementById("urlBarSuggestion");
    urlbarSuggests.addEventListener("command", () => {
      urlbarSuggestsPref.value = urlbarSuggests.checked;
    });

    this._initShowSearchSuggestionsFirst();
    this._updateSuggestionCheckboxes();
  },

  _initShowSearchSuggestionsFirst() {
    this._urlbarSuggestionsPosPref = Preferences.get("browser.urlbar.matchBuckets");
    let checkbox =
      document.getElementById("showSearchSuggestionsFirstCheckbox");

    this._urlbarSuggestionsPosPref.on("change", () => {
      this._syncFromShowSearchSuggestionsFirstPref(checkbox);
    });
    this._syncFromShowSearchSuggestionsFirstPref(checkbox);

    checkbox.addEventListener("command", () => {
      this._syncToShowSearchSuggestionsFirstPref(checkbox.checked);
    });
  },

  _syncFromShowSearchSuggestionsFirstPref(checkbox) {
    if (!this._urlbarSuggestionsPosPref.value) {
      // The pref is cleared, meaning search suggestions are shown first.
      checkbox.checked = true;
      return;
    }
    // The pref has a value.  If the first bucket in the pref is search
    // suggestions, then check the checkbox.
    let buckets = PlacesUtils.convertMatchBucketsStringToArray(this._urlbarSuggestionsPosPref.value);
    checkbox.checked = buckets[0] && buckets[0][0] == "suggestion";
  },

  _syncToShowSearchSuggestionsFirstPref(checked) {
    if (checked) {
      // Show search suggestions first, so clear the pref since that's the
      // default.
      this._urlbarSuggestionsPosPref.reset();
      return;
    }
    // Show history first.
    this._urlbarSuggestionsPosPref.value = "general:5,suggestion:Infinity";
  },

  _updateSuggestionCheckboxes() {
    let suggestsPref = Preferences.get("browser.search.suggest.enabled");
    let permanentPB =
      Services.prefs.getBoolPref("browser.privatebrowsing.autostart");
    let urlbarSuggests = document.getElementById("urlBarSuggestion");
    let positionCheckbox =
      document.getElementById("showSearchSuggestionsFirstCheckbox");

    urlbarSuggests.disabled = !suggestsPref.value || permanentPB;

    let urlbarSuggestsPref = Preferences.get("browser.urlbar.suggest.searches");
    urlbarSuggests.checked = urlbarSuggestsPref.value;
    if (urlbarSuggests.disabled) {
      urlbarSuggests.checked = false;
    }

    if (urlbarSuggests.checked) {
      positionCheckbox.disabled = false;
      this._syncFromShowSearchSuggestionsFirstPref(positionCheckbox);
    } else {
      positionCheckbox.disabled = true;
      positionCheckbox.checked = false;
    }

    let permanentPBLabel =
      document.getElementById("urlBarSuggestionPermanentPBLabel");
    permanentPBLabel.hidden = urlbarSuggests.hidden || !permanentPB;
  },

  buildDefaultEngineDropDown() {
    // This is called each time something affects the list of engines.
    let list = document.getElementById("defaultEngine");
    // Set selection to the current default engine.
    let currentEngine = Services.search.currentEngine.name;

    // If the current engine isn't in the list any more, select the first item.
    let engines = gEngineView._engineStore._engines;
    if (!engines.some(e => e.name == currentEngine))
      currentEngine = engines[0].name;

    // Now clean-up and rebuild the list.
    list.removeAllItems();
    gEngineView._engineStore._engines.forEach(e => {
      let item = list.appendItem(e.name);
      item.setAttribute("class", "menuitem-iconic searchengine-menuitem menuitem-with-favicon");
      if (e.iconURI) {
        item.setAttribute("image", e.iconURI.spec);
      }
      item.engine = e;
      if (e.name == currentEngine)
        list.selectedItem = item;
    });

    handleControllingExtension(SEARCH_TYPE, SEARCH_KEY);
    let searchEngineListener = {
      observe(subject, topic, data) {
        handleControllingExtension(SEARCH_TYPE, SEARCH_KEY);
      },
    };
    Services.obs.addObserver(searchEngineListener, "browser-search-engine-modified");
    window.addEventListener("unload", () => {
      Services.obs.removeObserver(searchEngineListener, "browser-search-engine-modified");
    });
  },

  handleEvent(aEvent) {
    switch (aEvent.type) {
      case "click":
        if (aEvent.target.id != "engineChildren" &&
            !aEvent.target.classList.contains("searchEngineAction")) {
          let engineList = document.getElementById("engineList");
          // We don't want to toggle off selection while editing keyword
          // so proceed only when the input field is hidden.
          // We need to check that engineList.view is defined here
          // because the "click" event listener is on <window> and the
          // view might have been destroyed if the pane has been navigated
          // away from.
          if (engineList.inputField.hidden && engineList.view) {
            let selection = engineList.view.selection;
            if (selection.count > 0) {
              selection.toggleSelect(selection.currentIndex);
            }
            engineList.blur();
          }
        }
        break;
      case "command":
        switch (aEvent.target.id) {
          case "":
            if (aEvent.target.parentNode &&
                aEvent.target.parentNode.parentNode &&
                aEvent.target.parentNode.parentNode.id == "defaultEngine") {
              gSearchPane.setDefaultEngine();
            }
            break;
          case "restoreDefaultSearchEngines":
            gSearchPane.onRestoreDefaults();
            break;
          case "removeEngineButton":
            Services.search.removeEngine(gEngineView.selectedEngine.originalEngine);
            break;
        }
        break;
      case "dragstart":
        if (aEvent.target.id == "engineChildren") {
          onDragEngineStart(aEvent);
        }
        break;
      case "keypress":
        if (aEvent.target.id == "engineList") {
          gSearchPane.onTreeKeyPress(aEvent);
        }
        break;
      case "select":
        if (aEvent.target.id == "engineList") {
          gSearchPane.onTreeSelect();
        }
        break;
      case "blur":
        if (aEvent.target.id == "engineList" &&
            aEvent.target.inputField == document.getBindingParent(aEvent.originalTarget)) {
          gSearchPane.onInputBlur();
        }
        break;
    }
  },

  observe(aEngine, aTopic, aVerb) {
    if (aTopic == "browser-search-engine-modified") {
      aEngine.QueryInterface(Ci.nsISearchEngine);
      switch (aVerb) {
      case "engine-added":
        gEngineView._engineStore.addEngine(aEngine);
        gEngineView.rowCountChanged(gEngineView.lastIndex, 1);
        gSearchPane.buildDefaultEngineDropDown();
        break;
      case "engine-changed":
        gEngineView._engineStore.reloadIcons();
        gEngineView.invalidate();
        break;
      case "engine-removed":
        gSearchPane.remove(aEngine);
        break;
      case "engine-current":
        // If the user is going through the drop down using up/down keys, the
        // dropdown may still be open (eg. on Windows) when engine-current is
        // fired, so rebuilding the list unconditionally would get in the way.
        let selectedEngine =
          document.getElementById("defaultEngine").selectedItem.engine;
        if (selectedEngine.name != aEngine.name)
          gSearchPane.buildDefaultEngineDropDown();
        break;
      case "engine-default":
        // Not relevant
        break;
      }
    }
  },

  onInputBlur(aEvent) {
    let tree = document.getElementById("engineList");
    if (!tree.hasAttribute("editing"))
      return;

    // Accept input unless discarded.
    let accept = aEvent.charCode != KeyEvent.DOM_VK_ESCAPE;
    tree.stopEditing(accept);
  },

  onTreeSelect() {
    document.getElementById("removeEngineButton").disabled =
      !gEngineView.isEngineSelectedAndRemovable();
  },

  onTreeKeyPress(aEvent) {
    let index = gEngineView.selectedIndex;
    let tree = document.getElementById("engineList");
    if (tree.hasAttribute("editing"))
      return;

    if (aEvent.charCode == KeyEvent.DOM_VK_SPACE) {
      // Space toggles the checkbox.
      let newValue = !gEngineView._engineStore.engines[index].shown;
      gEngineView.setCellValue(index, tree.columns.getFirstColumn(),
                               newValue.toString());
      // Prevent page from scrolling on the space key.
      aEvent.preventDefault();
    } else {
      let isMac = Services.appinfo.OS == "Darwin";
      if ((isMac && aEvent.keyCode == KeyEvent.DOM_VK_RETURN) ||
          (!isMac && aEvent.keyCode == KeyEvent.DOM_VK_F2)) {
        tree.startEditing(index, tree.columns.getLastColumn());
      } else if (aEvent.keyCode == KeyEvent.DOM_VK_DELETE ||
                 (isMac && aEvent.shiftKey &&
                  aEvent.keyCode == KeyEvent.DOM_VK_BACK_SPACE &&
                  gEngineView.isEngineSelectedAndRemovable())) {
        // Delete and Shift+Backspace (Mac) removes selected engine.
        Services.search.removeEngine(gEngineView.selectedEngine.originalEngine);
     }
    }
  },

  onRestoreDefaults() {
    let num = gEngineView._engineStore.restoreDefaultEngines();
    gEngineView.rowCountChanged(0, num);
    gEngineView.invalidate();
  },

  showRestoreDefaults(aEnable) {
    document.getElementById("restoreDefaultSearchEngines").disabled = !aEnable;
  },

  remove(aEngine) {
    let index = gEngineView._engineStore.removeEngine(aEngine);
    gEngineView.rowCountChanged(index, -1);
    gEngineView.invalidate();
    gEngineView.selection.select(Math.min(index, gEngineView.lastIndex));
    gEngineView.ensureRowIsVisible(gEngineView.currentIndex);
    document.getElementById("engineList").focus();
  },

  async editKeyword(aEngine, aNewKeyword) {
    let keyword = aNewKeyword.trim();
    if (keyword) {
      let eduplicate = false;
      let dupName = "";

      // Check for duplicates in Places keywords.
      let bduplicate = !!(await PlacesUtils.keywords.fetch(keyword));

      // Check for duplicates in changes we haven't committed yet
      let engines = gEngineView._engineStore.engines;
      let lc_keyword = keyword.toLocaleLowerCase();
      for (let engine of engines) {
        if (engine.alias &&
            engine.alias.toLocaleLowerCase() == lc_keyword &&
            engine.name != aEngine.name) {
          eduplicate = true;
          dupName = engine.name;
          break;
        }
      }

      // Notify the user if they have chosen an existing engine/bookmark keyword
      if (eduplicate || bduplicate) {
        let strings = document.getElementById("engineManagerBundle");
        let dtitle = strings.getString("duplicateTitle");
        let bmsg = strings.getString("duplicateBookmarkMsg");
        let emsg = strings.getFormattedString("duplicateEngineMsg", [dupName]);

        Services.prompt.alert(window, dtitle, eduplicate ? emsg : bmsg);
        return false;
      }
    }

    gEngineView._engineStore.changeEngine(aEngine, "alias", keyword);
    gEngineView.invalidate();
    return true;
  },

  saveOneClickEnginesList() {
    let hiddenList = [];
    for (let engine of gEngineView._engineStore.engines) {
      if (!engine.shown)
        hiddenList.push(engine.name);
    }
    Preferences.get("browser.search.hiddenOneOffs").value =
      hiddenList.join(",");
  },

  setDefaultEngine() {
    Services.search.currentEngine =
      document.getElementById("defaultEngine").selectedItem.engine;
    ExtensionSettingsStore.setByUser(SEARCH_TYPE, SEARCH_KEY);
  }
};

function onDragEngineStart(event) {
  var selectedIndex = gEngineView.selectedIndex;
  var tree = document.getElementById("engineList");
  var row = { }, col = { }, child = { };
  tree.treeBoxObject.getCellAt(event.clientX, event.clientY, row, col, child);
  if (selectedIndex >= 0 && !gEngineView.isCheckBox(row.value, col.value)) {
    event.dataTransfer.setData(ENGINE_FLAVOR, selectedIndex.toString());
    event.dataTransfer.effectAllowed = "move";
  }
}


function EngineStore() {
  let pref = Preferences.get("browser.search.hiddenOneOffs").value;
  this.hiddenList = pref ? pref.split(",") : [];

  this._engines = Services.search.getVisibleEngines().map(this._cloneEngine, this);
  this._defaultEngines = Services.search.getDefaultEngines().map(this._cloneEngine, this);

  // check if we need to disable the restore defaults button
  var someHidden = this._defaultEngines.some(e => e.hidden);
  gSearchPane.showRestoreDefaults(someHidden);
}
EngineStore.prototype = {
  _engines: null,
  _defaultEngines: null,

  get engines() {
    return this._engines;
  },
  set engines(val) {
    this._engines = val;
    return val;
  },

  _getIndexForEngine(aEngine) {
    return this._engines.indexOf(aEngine);
  },

  _getEngineByName(aName) {
    return this._engines.find(engine => engine.name == aName);
  },

  _cloneEngine(aEngine) {
    var clonedObj = {};
    for (var i in aEngine)
      clonedObj[i] = aEngine[i];
    clonedObj.originalEngine = aEngine;
    clonedObj.shown = !this.hiddenList.includes(clonedObj.name);
    return clonedObj;
  },

  // Callback for Array's some(). A thisObj must be passed to some()
  _isSameEngine(aEngineClone) {
    return aEngineClone.originalEngine == this.originalEngine;
  },

  addEngine(aEngine) {
    this._engines.push(this._cloneEngine(aEngine));
  },

  moveEngine(aEngine, aNewIndex) {
    if (aNewIndex < 0 || aNewIndex > this._engines.length - 1)
      throw new Error("ES_moveEngine: invalid aNewIndex!");
    var index = this._getIndexForEngine(aEngine);
    if (index == -1)
      throw new Error("ES_moveEngine: invalid engine?");

    if (index == aNewIndex)
      return; // nothing to do

    // Move the engine in our internal store
    var removedEngine = this._engines.splice(index, 1)[0];
    this._engines.splice(aNewIndex, 0, removedEngine);

    Services.search.moveEngine(aEngine.originalEngine, aNewIndex);
  },

  removeEngine(aEngine) {
    if (this._engines.length == 1) {
      throw new Error("Cannot remove last engine!");
    }

    let engineName = aEngine.name;
    let index = this._engines.findIndex(element => element.name == engineName);

    if (index == -1)
      throw new Error("invalid engine?");

    let removedEngine = this._engines.splice(index, 1)[0];

    if (this._defaultEngines.some(this._isSameEngine, removedEngine))
      gSearchPane.showRestoreDefaults(true);
    gSearchPane.buildDefaultEngineDropDown();
    return index;
  },

  restoreDefaultEngines() {
    var added = 0;

    for (var i = 0; i < this._defaultEngines.length; ++i) {
      var e = this._defaultEngines[i];

      // If the engine is already in the list, just move it.
      if (this._engines.some(this._isSameEngine, e)) {
        this.moveEngine(this._getEngineByName(e.name), i);
      } else {
        // Otherwise, add it back to our internal store

        // The search service removes the alias when an engine is hidden,
        // so clear any alias we may have cached before unhiding the engine.
        e.alias = "";

        this._engines.splice(i, 0, e);
        let engine = e.originalEngine;
        engine.hidden = false;
        Services.search.moveEngine(engine, i);
        added++;
      }
    }
    Services.search.resetToOriginalDefaultEngine();
    gSearchPane.showRestoreDefaults(false);
    gSearchPane.buildDefaultEngineDropDown();
    return added;
  },

  changeEngine(aEngine, aProp, aNewValue) {
    var index = this._getIndexForEngine(aEngine);
    if (index == -1)
      throw new Error("invalid engine?");

    this._engines[index][aProp] = aNewValue;
    aEngine.originalEngine[aProp] = aNewValue;
  },

  reloadIcons() {
    this._engines.forEach(function(e) {
      e.uri = e.originalEngine.uri;
    });
  }
};

function EngineView(aEngineStore) {
  this._engineStore = aEngineStore;
}
EngineView.prototype = {
  _engineStore: null,
  tree: null,

  get lastIndex() {
    return this.rowCount - 1;
  },
  get selectedIndex() {
    var seln = this.selection;
    if (seln.getRangeCount() > 0) {
      var min = {};
      seln.getRangeAt(0, min, {});
      return min.value;
    }
    return -1;
  },
  get selectedEngine() {
    return this._engineStore.engines[this.selectedIndex];
  },

  // Helpers
  rowCountChanged(index, count) {
    this.tree.rowCountChanged(index, count);
  },

  invalidate() {
    this.tree.invalidate();
  },

  ensureRowIsVisible(index) {
    this.tree.ensureRowIsVisible(index);
  },

  getSourceIndexFromDrag(dataTransfer) {
    return parseInt(dataTransfer.getData(ENGINE_FLAVOR));
  },

  isCheckBox(index, column) {
    return column.id == "engineShown";
  },

  isEngineSelectedAndRemovable() {
    return this.selectedIndex != -1 && this.lastIndex != 0;
  },

  // nsITreeView
  get rowCount() {
    return this._engineStore.engines.length;
  },

  getImageSrc(index, column) {
    if (column.id == "engineName") {
      if (this._engineStore.engines[index].iconURI)
        return this._engineStore.engines[index].iconURI.spec;

      if (window.devicePixelRatio > 1)
        return "chrome://browser/skin/[email protected]";
      return "chrome://browser/skin/search-engine-placeholder.png";
    }

    return "";
  },

  getCellText(index, column) {
    if (column.id == "engineName")
      return this._engineStore.engines[index].name;
    else if (column.id == "engineKeyword")
      return this._engineStore.engines[index].alias;
    return "";
  },

  setTree(tree) {
    this.tree = tree;
  },

  canDrop(targetIndex, orientation, dataTransfer) {
    var sourceIndex = this.getSourceIndexFromDrag(dataTransfer);
    return (sourceIndex != -1 &&
            sourceIndex != targetIndex &&
            sourceIndex != targetIndex + orientation);
  },

  drop(dropIndex, orientation, dataTransfer) {
    var sourceIndex = this.getSourceIndexFromDrag(dataTransfer);
    var sourceEngine = this._engineStore.engines[sourceIndex];

    const nsITreeView = Ci.nsITreeView;
    if (dropIndex > sourceIndex) {
      if (orientation == nsITreeView.DROP_BEFORE)
        dropIndex--;
    } else if (orientation == nsITreeView.DROP_AFTER) {
      dropIndex++;
    }

    this._engineStore.moveEngine(sourceEngine, dropIndex);
    gSearchPane.showRestoreDefaults(true);
    gSearchPane.buildDefaultEngineDropDown();

    // Redraw, and adjust selection
    this.invalidate();
    this.selection.select(dropIndex);
  },

  selection: null,
  getRowProperties(index) { return ""; },
  getCellProperties(index, column) { return ""; },
  getColumnProperties(column) { return ""; },
  isContainer(index) { return false; },
  isContainerOpen(index) { return false; },
  isContainerEmpty(index) { return false; },
  isSeparator(index) { return false; },
  isSorted(index) { return false; },
  getParentIndex(index) { return -1; },
  hasNextSibling(parentIndex, index) { return false; },
  getLevel(index) { return 0; },
  getCellValue(index, column) {
    if (column.id == "engineShown")
      return this._engineStore.engines[index].shown;
    return undefined;
  },
  toggleOpenState(index) { },
  cycleHeader(column) { },
  selectionChanged() { },
  cycleCell(row, column) { },
  isEditable(index, column) { return column.id != "engineName"; },
  isSelectable(index, column) { return false; },
  setCellValue(index, column, value) {
    if (column.id == "engineShown") {
      this._engineStore.engines[index].shown = value == "true";
      gEngineView.invalidate();
      gSearchPane.saveOneClickEnginesList();
    }
  },
  setCellText(index, column, value) {
    if (column.id == "engineKeyword") {
      gSearchPane.editKeyword(this._engineStore.engines[index], value)
                 .then(valid => {
        if (!valid)
          document.getElementById("engineList").startEditing(index, column);
      });
    }
  },
  performAction(action) { },
  performActionOnRow(action, index) { },
  performActionOnCell(action, index, column) { }
};
PK
!<Z)���W�WCchrome/browser/content/browser/preferences/in-content/subdialogs.js/* - This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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-globals-from ../../../base/content/utilityOverlay.js */
/* import-globals-from preferences.js */

"use strict";

/**
 * SubDialog constructor creates a new subdialog from a template and appends
 * it to the parentElement.
 * @param {DOMNode} template: The template is copied to create a new dialog.
 * @param {DOMNode} parentElement: New dialog is appended onto parentElement.
 * @param {String}  id: A unique identifier for the dialog.
 */
function SubDialog({template, parentElement, id}) {
  this._id = id;

  this._overlay = template.cloneNode(true);
  this._frame = this._overlay.querySelector(".dialogFrame");
  this._box = this._overlay.querySelector(".dialogBox");
  this._closeButton = this._overlay.querySelector(".dialogClose");
  this._titleElement = this._overlay.querySelector(".dialogTitle");

  this._overlay.id = `dialogOverlay-${id}`;
  this._frame.setAttribute("name", `dialogFrame-${id}`);
  this._frameCreated = new Promise(resolve => {
    this._frame.addEventListener("load", resolve, {once: true});
  });

  parentElement.appendChild(this._overlay);
  this._overlay.hidden = false;
}

SubDialog.prototype = {
  _closingCallback: null,
  _closingEvent: null,
  _isClosing: false,
  _frame: null,
  _frameCreated: null,
  _overlay: null,
  _box: null,
  _openedURL: null,
  _injectedStyleSheets: [
    "chrome://browser/skin/preferences/preferences.css",
    "chrome://global/skin/in-content/common.css",
    "chrome://browser/skin/preferences/in-content/preferences.css",
    "chrome://browser/skin/preferences/in-content/dialog.css",
  ],
  _resizeObserver: null,
  _template: null,
  _id: null,
  _titleElement: null,
  _closeButton: null,

  updateTitle(aEvent) {
    if (aEvent.target != this._frame.contentDocument)
      return;
    this._titleElement.textContent = this._frame.contentDocument.title;
  },

  injectXMLStylesheet(aStylesheetURL) {
    let contentStylesheet = this._frame.contentDocument.createProcessingInstruction(
      "xml-stylesheet",
      'href="' + aStylesheetURL + '" type="text/css"'
    );
    this._frame.contentDocument.insertBefore(contentStylesheet,
                                             this._frame.contentDocument.documentElement);
  },

  async open(aURL, aFeatures = null, aParams = null, aClosingCallback = null) {
    // Wait until frame is ready to prevent browser crash in tests
    await this._frameCreated;

    if (!this._frame.contentWindow) {
      // Given the binding constructor execution is asynchronous, and "load"
      // event can be dispatched before the browser element is shown, the
      // browser binding might not be constructed at this point.  Forcibly
      // construct the frame and construct the binding.
      // FIXME: Remove this (bug 1437247)
      this._frame.getBoundingClientRect();
    }

    // If we're open on some (other) URL or we're closing, open when closing has finished.
    if (this._openedURL || this._isClosing) {
      if (!this._isClosing) {
        this.close();
      }
      let args = Array.from(arguments);
      this._closingPromise.then(() => {
        this.open.apply(this, args);
      });
      return;
    }
    this._addDialogEventListeners();

    let features = (aFeatures ? aFeatures + "," : "") + "resizable,dialog=no,centerscreen";
    let dialog = window.openDialog(aURL, `dialogFrame-${this._id}`, features, aParams);
    if (aClosingCallback) {
      this._closingCallback = aClosingCallback.bind(dialog);
    }

    this._closingEvent = null;
    this._isClosing = false;
    this._openedURL = aURL;

    features = features.replace(/,/g, "&");
    let featureParams = new URLSearchParams(features.toLowerCase());
    this._box.setAttribute("resizable", featureParams.has("resizable") &&
                                        featureParams.get("resizable") != "no" &&
                                        featureParams.get("resizable") != "0");
  },

  close(aEvent = null) {
    if (this._isClosing) {
      return;
    }
    this._isClosing = true;
    this._closingPromise = new Promise(resolve => {
      this._resolveClosePromise = resolve;
    });

    if (this._closingCallback) {
      try {
        this._closingCallback.call(null, aEvent);
      } catch (ex) {
        Cu.reportError(ex);
      }
      this._closingCallback = null;
    }

    this._removeDialogEventListeners();

    this._overlay.style.visibility = "";
    // Clear the sizing inline styles.
    this._frame.removeAttribute("style");
    // Clear the sizing attributes
    this._box.removeAttribute("width");
    this._box.removeAttribute("height");
    this._box.style.removeProperty("min-height");
    this._box.style.removeProperty("min-width");

    this._overlay.dispatchEvent(new CustomEvent("dialogclose", {
      bubbles: true,
      detail: { dialog: this },
    }));

    setTimeout(() => {
      // Unload the dialog after the event listeners run so that the load of about:blank isn't
      // cancelled by the ESC <key>.
      let onBlankLoad = e => {
        if (this._frame.contentWindow.location.href == "about:blank") {
          this._frame.removeEventListener("load", onBlankLoad);
          // We're now officially done closing, so update the state to reflect that.
          this._openedURL = null;
          this._isClosing = false;
          this._resolveClosePromise();
        }
      };
      this._frame.addEventListener("load", onBlankLoad);
      this._frame.loadURI("about:blank");
    }, 0);
  },

  handleEvent(aEvent) {
    switch (aEvent.type) {
      case "click":
        // Close the dialog if the user clicked the overlay background, just
        // like when the user presses the ESC key (case "command" below).
        if (aEvent.target === this._overlay) {
          this._frame.contentWindow.close();
        }
        break;
      case "command":
        this._frame.contentWindow.close();
        break;
      case "dialogclosing":
        this._onDialogClosing(aEvent);
        break;
      case "DOMTitleChanged":
        this.updateTitle(aEvent);
        break;
      case "DOMFrameContentLoaded":
        this._onContentLoaded(aEvent);
        break;
      case "load":
        this._onLoad(aEvent);
        break;
      case "unload":
        this._onUnload(aEvent);
        break;
      case "keydown":
        this._onKeyDown(aEvent);
        break;
      case "focus":
        this._onParentWinFocus(aEvent);
        break;
    }
  },

  /* Private methods */

  _onUnload(aEvent) {
    if (aEvent.target.location.href == this._openedURL) {
      this._frame.contentWindow.close();
    }
  },

  _onContentLoaded(aEvent) {
    if (aEvent.target != this._frame || aEvent.target.contentWindow.location == "about:blank") {
      return;
    }

    for (let styleSheetURL of this._injectedStyleSheets) {
      this.injectXMLStylesheet(styleSheetURL);
    }

    // Provide the ability for the dialog to know that it is being loaded "in-content".
    this._frame.contentDocument.documentElement.setAttribute("subdialog", "true");

    this._frame.contentWindow.addEventListener("dialogclosing", this);

    let oldResizeBy = this._frame.contentWindow.resizeBy;
    this._frame.contentWindow.resizeBy = (resizeByWidth, resizeByHeight) => {
      // Only handle resizeByHeight currently.
      let frameHeight = this._frame.clientHeight;
      let boxMinHeight = parseFloat(getComputedStyle(this._box).minHeight, 10);

      this._frame.style.height = (frameHeight + resizeByHeight) + "px";
      this._box.style.minHeight = (boxMinHeight + resizeByHeight) + "px";

      oldResizeBy.call(this._frame.contentWindow, resizeByWidth, resizeByHeight);
    };

    // Make window.close calls work like dialog closing.
    let oldClose = this._frame.contentWindow.close;
    this._frame.contentWindow.close = () => {
      var closingEvent = this._closingEvent;
      if (!closingEvent) {
        closingEvent = new CustomEvent("dialogclosing", {
          bubbles: true,
          detail: { button: null },
        });

        this._frame.contentWindow.dispatchEvent(closingEvent);
      }

      this.close(closingEvent);
      oldClose.call(this._frame.contentWindow);
    };

    // XXX: Hack to make focus during the dialog's load functions work. Make the element visible
    // sooner in DOMContentLoaded but mostly invisible instead of changing visibility just before
    // the dialog's load event.
    this._overlay.style.visibility = "visible";
    this._overlay.style.opacity = "0.01";
  },

  _onLoad(aEvent) {
    if (aEvent.target.contentWindow.location == "about:blank") {
      return;
    }

    // Do this on load to wait for the CSS to load and apply before calculating the size.
    let docEl = this._frame.contentDocument.documentElement;

    let groupBoxTitle = document.getAnonymousElementByAttribute(this._box, "class", "groupbox-title");
    let groupBoxTitleHeight = groupBoxTitle.clientHeight +
                              parseFloat(getComputedStyle(groupBoxTitle).borderBottomWidth);

    let groupBoxBody = document.getAnonymousElementByAttribute(this._box, "class", "groupbox-body");
    // These are deduced from styles which we don't change, so it's safe to get them now:
    let boxVerticalPadding = 2 * parseFloat(getComputedStyle(groupBoxBody).paddingTop);
    let boxHorizontalPadding = 2 * parseFloat(getComputedStyle(groupBoxBody).paddingLeft);
    let boxHorizontalBorder = 2 * parseFloat(getComputedStyle(this._box).borderLeftWidth);
    let boxVerticalBorder = 2 * parseFloat(getComputedStyle(this._box).borderTopWidth);

    // The difference between the frame and box shouldn't change, either:
    let boxRect = this._box.getBoundingClientRect();
    let frameRect = this._frame.getBoundingClientRect();
    let frameSizeDifference = (frameRect.top - boxRect.top) + (boxRect.bottom - frameRect.bottom);

    // Then determine and set a bunch of width stuff:
    let frameMinWidth = docEl.style.width || docEl.scrollWidth + "px";
    let frameWidth = docEl.getAttribute("width") ? docEl.getAttribute("width") + "px" :
                     frameMinWidth;
    this._frame.style.width = frameWidth;
    this._box.style.minWidth = "calc(" +
                               (boxHorizontalBorder + boxHorizontalPadding) +
                               "px + " + frameMinWidth + ")";

    // Now do the same but for the height. We need to do this afterwards because otherwise
    // XUL assumes we'll optimize for height and gives us "wrong" values which then are no
    // longer correct after we set the width:
    let frameMinHeight = docEl.style.height || docEl.scrollHeight + "px";
    let frameHeight = docEl.getAttribute("height") ? docEl.getAttribute("height") + "px" :
                                                     frameMinHeight;

    // Now check if the frame height we calculated is possible at this window size,
    // accounting for titlebar, padding/border and some spacing.
    let maxHeight = window.innerHeight - frameSizeDifference - 30;
    // Do this with a frame height in pixels...
    let comparisonFrameHeight;
    if (frameHeight.endsWith("em")) {
      let fontSize = parseFloat(getComputedStyle(this._frame).fontSize);
      comparisonFrameHeight = parseFloat(frameHeight, 10) * fontSize;
    } else if (frameHeight.endsWith("px")) {
      comparisonFrameHeight = parseFloat(frameHeight, 10);
    } else {
      Cu.reportError("This dialog (" + this._frame.contentWindow.location.href + ") " +
                     "set a height in non-px-non-em units ('" + frameHeight + "'), " +
                     "which is likely to lead to bad sizing in in-content preferences. " +
                     "Please consider changing this.");
      comparisonFrameHeight = parseFloat(frameHeight);
    }

    if (comparisonFrameHeight > maxHeight) {
      // If the height is bigger than that of the window, we should let the contents scroll:
      frameHeight = maxHeight + "px";
      frameMinHeight = maxHeight + "px";
      let containers = this._frame.contentDocument.querySelectorAll(".largeDialogContainer");
      for (let container of containers) {
        container.classList.add("doScroll");
      }
    }

    this._frame.style.height = frameHeight;
    this._box.style.minHeight = "calc(" +
                                (boxVerticalBorder + groupBoxTitleHeight + boxVerticalPadding) +
                                "px + " + frameMinHeight + ")";

    this._overlay.dispatchEvent(new CustomEvent("dialogopen", {
      bubbles: true,
      detail: { dialog: this },
    }));
    this._overlay.style.visibility = "visible";
    this._overlay.style.opacity = ""; // XXX: focus hack continued from _onContentLoaded

    if (this._box.getAttribute("resizable") == "true") {
      this._onResize = this._onResize.bind(this);
      this._resizeObserver = new MutationObserver(this._onResize);
      this._resizeObserver.observe(this._box, {attributes: true});
    }

    this._trapFocus();

    // Search within main document and highlight matched keyword.
    gSearchResultsPane.searchWithinNode(this._titleElement, gSearchResultsPane.query);

    // Search within sub-dialog document and highlight matched keyword.
    gSearchResultsPane.searchWithinNode(this._frame.contentDocument.firstElementChild,
      gSearchResultsPane.query);

    // Creating tooltips for all the instances found
    for (let node of gSearchResultsPane.listSearchTooltips) {
      if (!node.tooltipNode) {
        gSearchResultsPane.createSearchTooltip(node, gSearchResultsPane.query);
      }
    }
  },

  _onResize(mutations) {
    let frame = this._frame;
    // The width and height styles are needed for the initial
    // layout of the frame, but afterward they need to be removed
    // or their presence will restrict the contents of the <browser>
    // from resizing to a smaller size.
    frame.style.removeProperty("width");
    frame.style.removeProperty("height");

    let docEl = frame.contentDocument.documentElement;
    let persistedAttributes = docEl.getAttribute("persist");
    if (!persistedAttributes ||
        (!persistedAttributes.includes("width") &&
         !persistedAttributes.includes("height"))) {
      return;
    }

    for (let mutation of mutations) {
      if (mutation.attributeName == "width") {
        docEl.setAttribute("width", docEl.scrollWidth);
      } else if (mutation.attributeName == "height") {
        docEl.setAttribute("height", docEl.scrollHeight);
      }
    }
  },

  _onDialogClosing(aEvent) {
    this._frame.contentWindow.removeEventListener("dialogclosing", this);
    this._closingEvent = aEvent;
  },

  _onKeyDown(aEvent) {
    if (aEvent.currentTarget == window && aEvent.keyCode == aEvent.DOM_VK_ESCAPE &&
        !aEvent.defaultPrevented) {
      this.close(aEvent);
      return;
    }
    if (aEvent.keyCode != aEvent.DOM_VK_TAB ||
        aEvent.ctrlKey || aEvent.altKey || aEvent.metaKey) {
      return;
    }

    let fm = Services.focus;

    let isLastFocusableElement = el => {
      // XXXgijs unfortunately there is no way to get the last focusable element without asking
      // the focus manager to move focus to it.
      let rv = el == fm.moveFocus(this._frame.contentWindow, null, fm.MOVEFOCUS_LAST, 0);
      fm.setFocus(el, 0);
      return rv;
    };

    let forward = !aEvent.shiftKey;
    // check if focus is leaving the frame (incl. the close button):
    if ((aEvent.target == this._closeButton && !forward) ||
        (isLastFocusableElement(aEvent.originalTarget) && forward)) {
      aEvent.preventDefault();
      aEvent.stopImmediatePropagation();
      let parentWin = this._getBrowser().ownerGlobal;
      if (forward) {
        fm.moveFocus(parentWin, null, fm.MOVEFOCUS_FIRST, fm.FLAG_BYKEY);
      } else {
        // Somehow, moving back 'past' the opening doc is not trivial. Cheat by doing it in 2 steps:
        fm.moveFocus(window, null, fm.MOVEFOCUS_ROOT, fm.FLAG_BYKEY);
        fm.moveFocus(parentWin, null, fm.MOVEFOCUS_BACKWARD, fm.FLAG_BYKEY);
      }
    }
  },

  _onParentWinFocus(aEvent) {
    // Explicitly check for the focus target of |window| to avoid triggering this when the window
    // is refocused
    if (aEvent.target != this._closeButton && aEvent.target != window) {
      this._closeButton.focus();
    }
  },

  _addDialogEventListeners() {
    // Make the close button work.
    this._closeButton.addEventListener("command", this);

    // DOMTitleChanged isn't fired on the frame, only on the chromeEventHandler
    let chromeBrowser = this._getBrowser();
    chromeBrowser.addEventListener("DOMTitleChanged", this, true);

    // Similarly DOMFrameContentLoaded only fires on the top window
    window.addEventListener("DOMFrameContentLoaded", this, true);

    // Wait for the stylesheets injected during DOMContentLoaded to load before showing the dialog
    // otherwise there is a flicker of the stylesheet applying.
    this._frame.addEventListener("load", this);

    chromeBrowser.addEventListener("unload", this, true);

    // Ensure we get <esc> keypresses even if nothing in the subdialog is focusable
    // (happens on OS X when only text inputs and lists are focusable, and
    //  the subdialog only has checkboxes/radiobuttons/buttons)
    window.addEventListener("keydown", this, true);

    this._overlay.addEventListener("click", this, true);
  },

  _removeDialogEventListeners() {
    let chromeBrowser = this._getBrowser();
    chromeBrowser.removeEventListener("DOMTitleChanged", this, true);
    chromeBrowser.removeEventListener("unload", this, true);

    this._closeButton.removeEventListener("command", this);

    window.removeEventListener("DOMFrameContentLoaded", this, true);
    this._frame.removeEventListener("load", this);
    this._frame.contentWindow.removeEventListener("dialogclosing", this);
    window.removeEventListener("keydown", this, true);

    this._overlay.removeEventListener("click", this, true);

    if (this._resizeObserver) {
      this._resizeObserver.disconnect();
      this._resizeObserver = null;
    }
    this._untrapFocus();
  },

  _trapFocus() {
    let fm = Services.focus;
    fm.moveFocus(this._frame.contentWindow, null, fm.MOVEFOCUS_FIRST, 0);
    this._frame.contentDocument.addEventListener("keydown", this, true);
    this._closeButton.addEventListener("keydown", this);

    window.addEventListener("focus", this, true);
  },

  _untrapFocus() {
    this._frame.contentDocument.removeEventListener("keydown", this, true);
    this._closeButton.removeEventListener("keydown", this);
    window.removeEventListener("focus", this);
  },

  _getBrowser() {
    return window.QueryInterface(Ci.nsIInterfaceRequestor)
                 .getInterface(Ci.nsIWebNavigation)
                 .QueryInterface(Ci.nsIDocShell)
                 .chromeEventHandler;
  },
};

var gSubDialog = {
  /**
   * New dialogs are stacked on top of the existing ones, and they are pushed
   * to the end of the _dialogs array.
   * @type {Array}
   */
  _dialogs: [],
  _dialogStack: null,
  _dialogTemplate: null,
  _nextDialogID: 0,
  _preloadDialog: null,
  get _topDialog() {
    return this._dialogs.length > 0 ? this._dialogs[this._dialogs.length - 1] : undefined;
  },

  init() {
    this._dialogStack = document.getElementById("dialogStack");
    this._dialogTemplate = document.getElementById("dialogTemplate");
    this._preloadDialog = new SubDialog({template: this._dialogTemplate,
                                         parentElement: this._dialogStack,
                                         id: this._nextDialogID++});
  },

  open(aURL, aFeatures = null, aParams = null, aClosingCallback = null) {
    // If we're already open/opening on this URL, do nothing.
    if (this._topDialog && this._topDialog._openedURL == aURL) {
      return;
    }

    if (this._dialogs.length == 0) {
      // When opening the first dialog, show the dialog stack to make sure
      // the browser binding can be constructed.
      this._dialogStack.hidden = false;
    }

    this._preloadDialog.open(aURL, aFeatures, aParams, aClosingCallback);
    this._dialogs.push(this._preloadDialog);
    this._preloadDialog = new SubDialog({template: this._dialogTemplate,
                                         parentElement: this._dialogStack,
                                         id: this._nextDialogID++});

    if (this._dialogs.length == 1) {
      this._ensureStackEventListeners();
    }
  },

  close() {
    this._topDialog.close();
  },

  handleEvent(aEvent) {
    switch (aEvent.type) {
      case "dialogopen": {
        this._onDialogOpen();
        break;
      }
      case "dialogclose": {
        this._onDialogClose(aEvent.detail.dialog);
        break;
      }
    }
  },

  _onDialogOpen() {
    let lowerDialog = this._dialogs.length > 1 ? this._dialogs[this._dialogs.length - 2] : undefined;
    if (lowerDialog) {
      lowerDialog._overlay.removeAttribute("topmost");
      lowerDialog._removeDialogEventListeners();
    }
  },

  _onDialogClose(dialog) {
    let fm = Services.focus;
    if (this._topDialog == dialog) {
      // XXX: When a top-most dialog is closed, we reuse the closed dialog and
      //      remove the preloadDialog. This is a temporary solution before we
      //      rewrite all the test cases in Bug 1359023.
      this._preloadDialog._overlay.remove();
      this._preloadDialog = this._dialogs.pop();
    } else {
      dialog._overlay.remove();
      this._dialogs.splice(this._dialogs.indexOf(dialog), 1);
    }

    if (this._topDialog) {
      fm.moveFocus(this._topDialog._frame.contentWindow, null, fm.MOVEFOCUS_FIRST, fm.FLAG_BYKEY);
      this._topDialog._overlay.setAttribute("topmost", true);
      this._topDialog._addDialogEventListeners();
    } else {
      fm.moveFocus(window, null, fm.MOVEFOCUS_ROOT, fm.FLAG_BYKEY);
      this._dialogStack.hidden = true;
      this._removeStackEventListeners();
    }
  },

  _ensureStackEventListeners() {
    this._dialogStack.addEventListener("dialogopen", this);
    this._dialogStack.addEventListener("dialogclose", this);
  },

  _removeStackEventListeners() {
    this._dialogStack.removeEventListener("dialogopen", this);
    this._dialogStack.removeEventListener("dialogclose", this);
  },
};
PK
!<T���H�H=chrome/browser/content/browser/preferences/in-content/sync.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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-globals-from preferences.js */

ChromeUtils.import("resource://services-sync/main.js");
ChromeUtils.import("resource://gre/modules/FxAccounts.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");

XPCOMUtils.defineLazyGetter(this, "FxAccountsCommon", function() {
  return ChromeUtils.import("resource://gre/modules/FxAccountsCommon.js", {});
});

ChromeUtils.defineModuleGetter(this, "UIState",
  "resource://services-sync/UIState.jsm");

const FXA_PAGE_LOGGED_OUT = 0;
const FXA_PAGE_LOGGED_IN = 1;

// Indexes into the "login status" deck.
// We are in a successful verified state - everything should work!
const FXA_LOGIN_VERIFIED = 0;
// We have logged in to an unverified account.
const FXA_LOGIN_UNVERIFIED = 1;
// We are logged in locally, but the server rejected our credentials.
const FXA_LOGIN_FAILED = 2;

Preferences.addAll([
  { id: "engine.addons", name: "services.sync.engine.addons", type: "bool" },
  { id: "engine.bookmarks", name: "services.sync.engine.bookmarks", type: "bool" },
  { id: "engine.history", name: "services.sync.engine.history", type: "bool" },
  { id: "engine.tabs", name: "services.sync.engine.tabs", type: "bool" },
  { id: "engine.prefs", name: "services.sync.engine.prefs", type: "bool" },
  { id: "engine.passwords", name: "services.sync.engine.passwords", type: "bool" },
  { id: "engine.addresses", name: "services.sync.engine.addresses", type: "bool" },
  { id: "engine.creditcards", name: "services.sync.engine.creditcards", type: "bool" },
]);

var gSyncPane = {
  get page() {
    return document.getElementById("weavePrefsDeck").selectedIndex;
  },

  set page(val) {
    document.getElementById("weavePrefsDeck").selectedIndex = val;
  },

  init() {
    this._setupEventListeners();
    this._adjustForPrefs();

    // If the Service hasn't finished initializing, wait for it.
    let xps = Cc["@mozilla.org/weave/service;1"]
      .getService(Ci.nsISupports)
      .wrappedJSObject;

    if (xps.ready) {
      this._init();
      return;
    }

    // it may take some time before we can determine what provider to use
    // and the state of that provider, so show the "please wait" page.
    this._showLoadPage(xps);

    let onUnload = function() {
      window.removeEventListener("unload", onUnload);
      try {
        Services.obs.removeObserver(onReady, "weave:service:ready");
      } catch (e) { }
    };

    let onReady = () => {
      Services.obs.removeObserver(onReady, "weave:service:ready");
      window.removeEventListener("unload", onUnload);
      this._init();
    };

    Services.obs.addObserver(onReady, "weave:service:ready");
    window.addEventListener("unload", onUnload);

    xps.ensureLoaded();
  },

  // make whatever tweaks we need based on preferences.
  _adjustForPrefs() {
    // These 2 engines are unique in that there are prefs that make the
    // entire engine unavailable (which is distinct from "disabled").
    let enginePrefs = [
      ["services.sync.engine.addresses.available", "engine.addresses"],
      ["services.sync.engine.creditcards.available", "engine.creditcards"],
    ];
    let numHidden = 0;
    for (let [availablePref, prefName] of enginePrefs) {
      if (!Services.prefs.getBoolPref(availablePref)) {
        let checkbox = document.querySelector("[preference=\"" + prefName + "\"]");
        checkbox.hidden = true;
        numHidden += 1;
      }
    }
    // If we hid both, the list of prefs is unbalanced, so move "history" to
    // the second column. (If we only moved one, it's still unbalanced, but
    // there's an odd number of engines so that can't be avoided)
    if (numHidden == 2) {
      let history = document.querySelector("[preference=\"engine.history\"]");
      let addons = document.querySelector("[preference=\"engine.addons\"]");
      addons.parentNode.insertBefore(history, addons);
    }
  },

  _showLoadPage(xps) {
    let username = Services.prefs.getCharPref("services.sync.username", "");
    if (!username) {
      this.page = FXA_PAGE_LOGGED_OUT;
      return;
    }

    // Use cached values while we wait for the up-to-date values
    let cachedComputerName = Services.prefs.getCharPref("services.sync.client.name", "");
    document.querySelector(".fxaEmailAddress").value = username;
    this._populateComputerName(cachedComputerName);
    this.page = FXA_PAGE_LOGGED_IN;
  },

  _init() {
    // Add the observers now and remove them on unload
    // XXXzpao This should use Services.obs.* but Weave's Obs does nice handling
    //        of `this`. Fix in a followup. (bug 583347)
    Weave.Svc.Obs.add(UIState.ON_UPDATE, this.updateWeavePrefs, this);

    window.addEventListener("unload", () => {
      Weave.Svc.Obs.remove(UIState.ON_UPDATE, this.updateWeavePrefs, this);
    });

    XPCOMUtils.defineLazyGetter(this, "_accountsStringBundle", () => {
      return Services.strings.createBundle("chrome://browser/locale/accounts.properties");
    });

    // Links for mobile devices before the user is logged in.
    let url = Services.prefs.getCharPref("identity.mobilepromo.android") + "sync-preferences";
    document.getElementById("fxaMobilePromo-android").setAttribute("href", url);
    url = Services.prefs.getCharPref("identity.mobilepromo.ios") + "sync-preferences";
    document.getElementById("fxaMobilePromo-ios").setAttribute("href", url);

    // Links for mobile devices shown after the user is logged in.
    FxAccounts.config.promiseConnectDeviceURI(this._getEntryPoint()).then(connectURI => {
      document.getElementById("mobilePromo-singledevice").setAttribute("href", connectURI);
    });

    FxAccounts.config.promiseManageDevicesURI(this._getEntryPoint()).then(manageURI => {
      document.getElementById("mobilePromo-multidevice").setAttribute("href", manageURI);
    });

    document.getElementById("tosPP-small-ToS").setAttribute("href", Weave.Svc.Prefs.get("fxa.termsURL"));
    document.getElementById("tosPP-small-PP").setAttribute("href", Weave.Svc.Prefs.get("fxa.privacyURL"));

    FxAccounts.config.promiseSignUpURI(this._getEntryPoint()).then(signUpURI => {
      document.getElementById("noFxaSignUp").setAttribute("href", signUpURI);
    });

    this.updateWeavePrefs();

    // Notify observers that the UI is now ready
    Services.obs.notifyObservers(window, "sync-pane-loaded");
  },

  _toggleComputerNameControls(editMode) {
    let textbox = document.getElementById("fxaSyncComputerName");
    textbox.disabled = !editMode;
    document.getElementById("fxaChangeDeviceName").hidden = editMode;
    document.getElementById("fxaCancelChangeDeviceName").hidden = !editMode;
    document.getElementById("fxaSaveChangeDeviceName").hidden = !editMode;
  },

  _focusComputerNameTextbox() {
    let textbox = document.getElementById("fxaSyncComputerName");
    let valLength = textbox.value.length;
    textbox.focus();
    textbox.setSelectionRange(valLength, valLength);
  },

  _blurComputerNameTextbox() {
    document.getElementById("fxaSyncComputerName").blur();
  },

  _focusAfterComputerNameTextbox() {
    // Focus the most appropriate element that's *not* the "computer name" box.
    Services.focus.moveFocus(window,
      document.getElementById("fxaSyncComputerName"),
      Services.focus.MOVEFOCUS_FORWARD, 0);
  },

  _updateComputerNameValue(save) {
    if (save) {
      let textbox = document.getElementById("fxaSyncComputerName");
      Weave.Service.clientsEngine.localName = textbox.value;
    }
    this._populateComputerName(Weave.Service.clientsEngine.localName);
  },

  _setupEventListeners() {
    function setEventListener(aId, aEventType, aCallback) {
      document.getElementById(aId)
        .addEventListener(aEventType, aCallback.bind(gSyncPane));
    }

    setEventListener("fxaChangeDeviceName", "command", function() {
      this._toggleComputerNameControls(true);
      this._focusComputerNameTextbox();
    });
    setEventListener("fxaCancelChangeDeviceName", "command", function() {
      // We explicitly blur the textbox because of bug 75324, then after
      // changing the state of the buttons, force focus to whatever the focus
      // manager thinks should be next (which on the mac, depends on an OSX
      // keyboard access preference)
      this._blurComputerNameTextbox();
      this._toggleComputerNameControls(false);
      this._updateComputerNameValue(false);
      this._focusAfterComputerNameTextbox();
    });
    setEventListener("fxaSaveChangeDeviceName", "command", function() {
      // Work around bug 75324 - see above.
      this._blurComputerNameTextbox();
      this._toggleComputerNameControls(false);
      this._updateComputerNameValue(true);
      this._focusAfterComputerNameTextbox();
    });
    setEventListener("noFxaSignIn", "command", function() {
      gSyncPane.signIn();
      return false;
    });
    setEventListener("fxaUnlinkButton", "command", function() {
      gSyncPane.unlinkFirefoxAccount(true);
    });
    setEventListener("verifyFxaAccount", "command",
      gSyncPane.verifyFirefoxAccount);
    setEventListener("unverifiedUnlinkFxaAccount", "command", function() {
      /* no warning as account can't have previously synced */
      gSyncPane.unlinkFirefoxAccount(false);
    });
    setEventListener("rejectReSignIn", "command",
      gSyncPane.reSignIn);
    setEventListener("rejectUnlinkFxaAccount", "command", function() {
      gSyncPane.unlinkFirefoxAccount(true);
    });
    setEventListener("fxaSyncComputerName", "keypress", function(e) {
      if (e.keyCode == KeyEvent.DOM_VK_RETURN) {
        document.getElementById("fxaSaveChangeDeviceName").click();
      } else if (e.keyCode == KeyEvent.DOM_VK_ESCAPE) {
        document.getElementById("fxaCancelChangeDeviceName").click();
      }
    });
  },

  updateWeavePrefs() {
    let service = Cc["@mozilla.org/weave/service;1"]
      .getService(Ci.nsISupports)
      .wrappedJSObject;

    let displayNameLabel = document.getElementById("fxaDisplayName");
    let fxaEmailAddressLabels = document.querySelectorAll(".fxaEmailAddress");
    displayNameLabel.hidden = true;

    // determine the fxa status...
    this._showLoadPage(service);

    let state = UIState.get();
    if (state.status == UIState.STATUS_NOT_CONFIGURED) {
      this.page = FXA_PAGE_LOGGED_OUT;
      return;
    }
    this.page = FXA_PAGE_LOGGED_IN;
    // We are logged in locally, but maybe we are in a state where the
    // server rejected our credentials (eg, password changed on the server)
    let fxaLoginStatus = document.getElementById("fxaLoginStatus");
    let syncReady = false; // Is sync able to actually sync?
    // We need to check error states that need a re-authenticate to resolve
    // themselves first.
    if (state.status == UIState.STATUS_LOGIN_FAILED) {
      fxaLoginStatus.selectedIndex = FXA_LOGIN_FAILED;
    } else if (state.status == UIState.STATUS_NOT_VERIFIED) {
      fxaLoginStatus.selectedIndex = FXA_LOGIN_UNVERIFIED;
    } else {
      // We must be golden (or in an error state we expect to magically
      // resolve itself)
      fxaLoginStatus.selectedIndex = FXA_LOGIN_VERIFIED;
      syncReady = true;
    }
    fxaEmailAddressLabels.forEach((label) => {
      label.value = state.email;
    });
    this._populateComputerName(Weave.Service.clientsEngine.localName);
    let engines = document.getElementById("fxaSyncEngines");
    for (let checkbox of engines.querySelectorAll("checkbox")) {
      checkbox.disabled = !syncReady;
    }
    document.getElementById("fxaChangeDeviceName").disabled = !syncReady;

    // Clear the profile image (if any) of the previously logged in account.
    document.querySelector("#fxaLoginVerified > .fxaProfileImage").style.removeProperty("list-style-image");

    if (state.displayName) {
      fxaLoginStatus.setAttribute("hasName", true);
      displayNameLabel.hidden = false;
      displayNameLabel.textContent = state.displayName;
    } else {
      fxaLoginStatus.removeAttribute("hasName");
    }
    if (state.avatarURL) {
      let bgImage = "url(\"" + state.avatarURL + "\")";
      let profileImageElement = document.querySelector("#fxaLoginVerified > .fxaProfileImage");
      profileImageElement.style.listStyleImage = bgImage;

      let img = new Image();
      img.onerror = () => {
        // Clear the image if it has trouble loading. Since this callback is asynchronous
        // we check to make sure the image is still the same before we clear it.
        if (profileImageElement.style.listStyleImage === bgImage) {
          profileImageElement.style.removeProperty("list-style-image");
        }
      };
      img.src = state.avatarURL;
    }
    // The "manage account" link embeds the uid, so we need to update this
    // if the account state changes.
    FxAccounts.config.promiseManageURI(this._getEntryPoint()).then(accountsManageURI => {
      document.getElementById("verifiedManage").setAttribute("href", accountsManageURI);
    });
    let isUnverified = state.status == UIState.STATUS_NOT_VERIFIED;
    // The mobile promo links - which one is shown depends on the number of devices.
    let isMultiDevice = Weave.Service.clientsEngine.stats.numClients > 1;
    document.getElementById("mobilePromo-singledevice").hidden = isUnverified || isMultiDevice;
    document.getElementById("mobilePromo-multidevice").hidden = isUnverified || !isMultiDevice;
  },

  _getEntryPoint() {
    let params = new URLSearchParams(document.URL.split("#")[0].split("?")[1] || "");
    return params.get("entrypoint") || "preferences";
  },

  openContentInBrowser(url, options) {
    let win = Services.wm.getMostRecentWindow("navigator:browser");
    if (!win) {
      openUILinkIn(url, "tab");
      return;
    }
    win.switchToTabHavingURI(url, true, options);
  },

  // Replace the current tab with the specified URL.
  replaceTabWithUrl(url) {
    // Get the <browser> element hosting us.
    let browser = window.QueryInterface(Ci.nsIInterfaceRequestor)
      .getInterface(Ci.nsIWebNavigation)
      .QueryInterface(Ci.nsIDocShell)
      .chromeEventHandler;
    // And tell it to load our URL.
    browser.loadURI(url);
  },

  async signIn() {
    const url = await FxAccounts.config.promiseSignInURI(this._getEntryPoint());
    this.replaceTabWithUrl(url);
  },

  async reSignIn() {
    // There's a bit of an edge-case here - we might be forcing reauth when we've
    // lost the FxA account data - in which case we'll not get a URL as the re-auth
    // URL embeds account info and the server endpoint complains if we don't
    // supply it - So we just use the regular "sign in" URL in that case.
    let entryPoint = this._getEntryPoint();
    const url = (await FxAccounts.config.promiseForceSigninURI(entryPoint)) ||
                (await FxAccounts.config.promiseSignInURI(entryPoint));
    this.replaceTabWithUrl(url);
  },


  clickOrSpaceOrEnterPressed(event) {
    // Note: charCode is deprecated, but 'char' not yet implemented.
    // Replace charCode with char when implemented, see Bug 680830
    return ((event.type == "click" && event.button == 0) ||
      (event.type == "keypress" &&
        (event.charCode == KeyEvent.DOM_VK_SPACE || event.keyCode == KeyEvent.DOM_VK_RETURN)));
  },

  openChangeProfileImage(event) {
    if (this.clickOrSpaceOrEnterPressed(event)) {
      FxAccounts.config.promiseChangeAvatarURI(this._getEntryPoint())
        .then(url => {
          this.openContentInBrowser(url, {
            replaceQueryString: true,
            triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
          });
        });
      // Prevent page from scrolling on the space key.
      event.preventDefault();
    }
  },

  openManageFirefoxAccount(event) {
    if (this.clickOrSpaceOrEnterPressed(event)) {
      this.manageFirefoxAccount();
      // Prevent page from scrolling on the space key.
      event.preventDefault();
    }
  },

  manageFirefoxAccount() {
    FxAccounts.config.promiseManageURI(this._getEntryPoint())
      .then(url => {
        this.openContentInBrowser(url, {
          replaceQueryString: true,
          triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
        });
      });
  },

  verifyFirefoxAccount() {
    let showVerifyNotification = (data) => {
      let isError = !data;
      let maybeNot = isError ? "Not" : "";
      let sb = this._accountsStringBundle;
      let title = sb.GetStringFromName("verification" + maybeNot + "SentTitle");
      let email = !isError && data ? data.email : "";
      let body = sb.formatStringFromName("verification" + maybeNot + "SentBody", [email], 1);
      new Notification(title, { body });
    };

    let onError = () => {
      showVerifyNotification();
    };

    let onSuccess = data => {
      if (data) {
        showVerifyNotification(data);
      } else {
        onError();
      }
    };

    fxAccounts.resendVerificationEmail()
      .then(fxAccounts.getSignedInUser, onError)
      .then(onSuccess, onError);
  },

  unlinkFirefoxAccount(confirm) {
    if (confirm) {
      let sb = Services.strings.createBundle("chrome://browser/locale/syncSetup.properties");
      let disconnectLabel = sb.GetStringFromName("disconnect.label");
      let title = sb.GetStringFromName("disconnect.verify.title");
      let body = sb.GetStringFromName("disconnect.verify.bodyHeading") +
        "\n\n" +
        sb.GetStringFromName("disconnect.verify.bodyText");
      let ps = Services.prompt;
      let buttonFlags = (ps.BUTTON_POS_0 * ps.BUTTON_TITLE_IS_STRING) +
        (ps.BUTTON_POS_1 * ps.BUTTON_TITLE_CANCEL) +
        ps.BUTTON_POS_1_DEFAULT;

      let factory = Cc["@mozilla.org/prompter;1"]
        .getService(Ci.nsIPromptFactory);
      let prompt = factory.getPrompt(window, Ci.nsIPrompt);
      let bag = prompt.QueryInterface(Ci.nsIWritablePropertyBag2);
      bag.setPropertyAsBool("allowTabModal", true);

      let pressed = prompt.confirmEx(title, body, buttonFlags,
        disconnectLabel, null, null, null, {});

      if (pressed != 0) { // 0 is the "continue" button
        return;
      }
    }
    fxAccounts.signOut().then(() => {
      this.updateWeavePrefs();
    });
  },

  _populateComputerName(value) {
    let textbox = document.getElementById("fxaSyncComputerName");
    if (!textbox.hasAttribute("placeholder")) {
      textbox.setAttribute("placeholder",
        Weave.Utils.getDefaultDeviceName());
    }
    textbox.value = value;
  },
};
PK
!<��	�k/k/7chrome/browser/content/browser/preferences/languages.js/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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-globals-from ../../../toolkit/content/preferencesBindings.js */

ChromeUtils.import("resource://gre/modules/Services.jsm");

Preferences.addAll([
  { id: "intl.accept_languages", type: "wstring" },
  { id: "pref.browser.language.disable_button.up", type: "bool" },
  { id: "pref.browser.language.disable_button.down", type: "bool" },
  { id: "pref.browser.language.disable_button.remove", type: "bool" },
  { id: "privacy.spoof_english", type: "int" },
]);

var gLanguagesDialog = {

  _availableLanguagesList: [],
  _acceptLanguages: { },

  _selectedItemID: null,

  init() {
    if (!this._availableLanguagesList.length)
      this._loadAvailableLanguages();
  },

  // Ugly hack used to trigger extra reflow in order to work around XUL bug 1194844;
  // see bug 1194346.
  forceReflow() {
    this._activeLanguages.style.fontKerning = "none";
    setTimeout(() => {
      this._activeLanguages.style.removeProperty("font-kerning");
    }, 0);
  },

  get _activeLanguages() {
    return document.getElementById("activeLanguages");
  },

  get _availableLanguages() {
    return document.getElementById("availableLanguages");
  },

  _loadAvailableLanguages() {
    // This is a parser for: resource://gre/res/language.properties
    // The file is formatted like so:
    // ab[-cd].accept=true|false
    //  ab = language
    //  cd = region
    var bundleAccepted    = document.getElementById("bundleAccepted");
    var bundleRegions     = document.getElementById("bundleRegions");
    var bundleLanguages   = document.getElementById("bundleLanguages");
    var bundlePreferences = document.getElementById("bundlePreferences");

    function LanguageInfo(aName, aABCD, aIsVisible) {
      this.name = aName;
      this.abcd = aABCD;
      this.isVisible = aIsVisible;
    }

    // 1) Read the available languages out of language.properties
    var strings = bundleAccepted.strings;
    while (strings.hasMoreElements()) {
      var currString = strings.getNext();
      if (!(currString instanceof Ci.nsIPropertyElement))
        break;

      var property = currString.key.split("."); // ab[-cd].accept
      if (property[1] == "accept") {
        var abCD = property[0];
        var abCDPairs = abCD.split("-"); // ab[-cd]
        var useABCDFormat = abCDPairs.length > 1;
        var ab = useABCDFormat ? abCDPairs[0] : abCD;
        var cd = useABCDFormat ? abCDPairs[1] : "";
        if (ab) {
          var language = "";
          try {
            language = bundleLanguages.getString(ab);
          } catch (e) { continue; }

          var region = "";
          if (useABCDFormat) {
            try {
              region = bundleRegions.getString(cd);
            } catch (e) { continue; }
          }

          var name = "";
          if (useABCDFormat)
            name = bundlePreferences.getFormattedString("languageRegionCodeFormat",
                                                        [language, region, abCD]);
          else
            name = bundlePreferences.getFormattedString("languageCodeFormat",
                                                        [language, abCD]);

          if (name && abCD) {
            var isVisible = currString.value == "true" &&
                            (!(abCD in this._acceptLanguages) || !this._acceptLanguages[abCD]);
            var li = new LanguageInfo(name, abCD, isVisible);
            this._availableLanguagesList.push(li);
          }
        }
      }
    }
    this._buildAvailableLanguageList();
  },

  _buildAvailableLanguageList() {
    var availableLanguagesPopup = document.getElementById("availableLanguagesPopup");
    while (availableLanguagesPopup.hasChildNodes())
      availableLanguagesPopup.firstChild.remove();

    // Sort the list of languages by name
    this._availableLanguagesList.sort(function(a, b) {
                                        return a.name.localeCompare(b.name);
                                      });

    // Load the UI with the data
    for (var i = 0; i < this._availableLanguagesList.length; ++i) {
      var abCD = this._availableLanguagesList[i].abcd;
      if (this._availableLanguagesList[i].isVisible &&
          (!(abCD in this._acceptLanguages) || !this._acceptLanguages[abCD])) {
        var menuitem = document.createElement("menuitem");
        menuitem.id = this._availableLanguagesList[i].abcd;
        availableLanguagesPopup.appendChild(menuitem);
        menuitem.setAttribute("label", this._availableLanguagesList[i].name);
      }
    }
  },

  readAcceptLanguages() {
    while (this._activeLanguages.hasChildNodes())
      this._activeLanguages.firstChild.remove();

    var selectedIndex = 0;
    var preference = Preferences.get("intl.accept_languages");
    if (preference.value == "")
      return undefined;
    var languages = preference.value.toLowerCase().split(/\s*,\s*/);
    for (var i = 0; i < languages.length; ++i) {
      var name = this._getLanguageName(languages[i]);
      if (!name)
        name = "[" + languages[i] + "]";
      var listitem = document.createElement("listitem");
      listitem.id = languages[i];
      if (languages[i] == this._selectedItemID)
        selectedIndex = i;
      this._activeLanguages.appendChild(listitem);
      listitem.setAttribute("label", name);

      // Hash this language as an "Active" language so we don't
      // show it in the list that can be added.
      this._acceptLanguages[languages[i]] = true;
    }

    if (this._activeLanguages.childNodes.length > 0) {
      this._activeLanguages.ensureIndexIsVisible(selectedIndex);
      this._activeLanguages.selectedIndex = selectedIndex;
    }

    // Update states of accept-language list and buttons according to
    // privacy.resistFingerprinting and privacy.spoof_english.
    this.readSpoofEnglish();

    return undefined;
  },

  writeAcceptLanguages() {
    return undefined;
  },

  onAvailableLanguageSelect() {
    var availableLanguages = this._availableLanguages;
    var addButton = document.getElementById("addButton");
    addButton.disabled = availableLanguages.disabled ||
                         availableLanguages.selectedIndex < 0;

    this._availableLanguages.removeAttribute("accesskey");
  },

  addLanguage() {
    var selectedID = this._availableLanguages.selectedItem.id;
    var preference = Preferences.get("intl.accept_languages");
    var arrayOfPrefs = preference.value.toLowerCase().split(/\s*,\s*/);
    for (var i = 0; i < arrayOfPrefs.length; ++i ) {
      if (arrayOfPrefs[i] == selectedID)
        return;
    }

    this._selectedItemID = selectedID;

    if (preference.value == "")
      preference.value = selectedID;
    else {
      arrayOfPrefs.unshift(selectedID);
      preference.value = arrayOfPrefs.join(",");
    }

    this._acceptLanguages[selectedID] = true;
    this._availableLanguages.selectedItem = null;

    // Rebuild the available list with the added item removed...
    this._buildAvailableLanguageList();

    this._availableLanguages.setAttribute("label", this._availableLanguages.getAttribute("label2"));
  },

  removeLanguage() {
    // Build the new preference value string.
    var languagesArray = [];
    for (var i = 0; i < this._activeLanguages.childNodes.length; ++i) {
      var item = this._activeLanguages.childNodes[i];
      if (!item.selected)
        languagesArray.push(item.id);
      else
        this._acceptLanguages[item.id] = false;
    }
    var string = languagesArray.join(",");

    // Get the item to select after the remove operation completes.
    var selection = this._activeLanguages.selectedItems;
    var lastSelected = selection[selection.length - 1];
    var selectItem = lastSelected.nextSibling || lastSelected.previousSibling;
    selectItem = selectItem ? selectItem.id : null;

    this._selectedItemID = selectItem;

    // Update the preference and force a UI rebuild
    var preference = Preferences.get("intl.accept_languages");
    preference.value = string;

    this._buildAvailableLanguageList();
  },

  _getLanguageName(aABCD) {
    if (!this._availableLanguagesList.length)
      this._loadAvailableLanguages();
    for (var i = 0; i < this._availableLanguagesList.length; ++i) {
      if (aABCD == this._availableLanguagesList[i].abcd)
        return this._availableLanguagesList[i].name;
    }
    return "";
  },

  moveUp() {
    var selectedItem = this._activeLanguages.selectedItems[0];
    var previousItem = selectedItem.previousSibling;

    var string = "";
    for (var i = 0; i < this._activeLanguages.childNodes.length; ++i) {
      var item = this._activeLanguages.childNodes[i];
      string += (i == 0 ? "" : ",");
      if (item.id == previousItem.id)
        string += selectedItem.id;
      else if (item.id == selectedItem.id)
        string += previousItem.id;
      else
        string += item.id;
    }

    this._selectedItemID = selectedItem.id;

    // Update the preference and force a UI rebuild
    var preference = Preferences.get("intl.accept_languages");
    preference.value = string;
  },

  moveDown() {
    var selectedItem = this._activeLanguages.selectedItems[0];
    var nextItem = selectedItem.nextSibling;

    var string = "";
    for (var i = 0; i < this._activeLanguages.childNodes.length; ++i) {
      var item = this._activeLanguages.childNodes[i];
      string += (i == 0 ? "" : ",");
      if (item.id == nextItem.id)
        string += selectedItem.id;
      else if (item.id == selectedItem.id)
        string += nextItem.id;
      else
        string += item.id;
    }

    this._selectedItemID = selectedItem.id;

    // Update the preference and force a UI rebuild
    var preference = Preferences.get("intl.accept_languages");
    preference.value = string;
  },

  onLanguageSelect() {
    var upButton = document.getElementById("up");
    var downButton = document.getElementById("down");
    var removeButton = document.getElementById("remove");
    switch (this._activeLanguages.selectedCount) {
    case 0:
      upButton.disabled = downButton.disabled = removeButton.disabled = true;
      break;
    case 1:
      upButton.disabled = this._activeLanguages.selectedIndex == 0;
      downButton.disabled = this._activeLanguages.selectedIndex == this._activeLanguages.childNodes.length - 1;
      removeButton.disabled = false;
      break;
    default:
      upButton.disabled = true;
      downButton.disabled = true;
      removeButton.disabled = false;
    }
  },

  readSpoofEnglish() {
    var checkbox = document.getElementById("spoofEnglish");
    var resistFingerprinting = Services.prefs.getBoolPref("privacy.resistFingerprinting");
    if (!resistFingerprinting) {
      checkbox.hidden = true;
      return false;
    }

    var spoofEnglish = Preferences.get("privacy.spoof_english").value;
    var activeLanguages = this._activeLanguages;
    var availableLanguages = this._availableLanguages;
    checkbox.hidden = false;
    switch (spoofEnglish) {
    case 1: // don't spoof intl.accept_languages
      activeLanguages.disabled = false;
      activeLanguages.selectItem(activeLanguages.firstChild);
      availableLanguages.disabled = false;
      this.onAvailableLanguageSelect();
      return false;
    case 2: // spoof intl.accept_languages
      activeLanguages.clearSelection();
      activeLanguages.disabled = true;
      availableLanguages.disabled = true;
      this.onAvailableLanguageSelect();
      return true;
    default: // will prompt for spoofing intl.accept_languages if resisting fingerprinting
      return false;
    }
  },

  writeSpoofEnglish() {
    return document.getElementById("spoofEnglish").checked ? 2 : 1;
  }
};

// These focus and resize handlers hack around XUL bug 1194844
// by triggering extra reflow (see bug 1194346).
window.addEventListener("focus", () => gLanguagesDialog.forceReflow());
window.addEventListener("resize", () => gLanguagesDialog.forceReflow());
PK
!<��|8chrome/browser/content/browser/preferences/languages.xul<?xml version="1.0"?>

<!-- -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- -->
<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 dialog [
  <!ENTITY % preferencesDTD SYSTEM "chrome://global/locale/preferences.dtd">
  %preferencesDTD;
  <!ENTITY % languagesDTD SYSTEM "chrome://browser/locale/preferences/languages.dtd">
  %languagesDTD;
]>

<?xml-stylesheet href="chrome://global/skin/"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css"?>

<dialog id="LanguagesDialog" type="child" class="prefwindow"
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        title="&languages.customize.Header;"
        buttons="accept,cancel,help"
        persist="lastSelected screenX screenY"
        closebuttonlabel="&preferencesCloseButton.label;"
        closebuttonaccesskey="&preferencesCloseButton.accesskey;"
        role="dialog"
        onload="gLanguagesDialog.init();"
        helpTopic="prefs-languages"
        ondialoghelp="openPrefsHelp()"
        style="width: &window.width;">

  <script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>
  <script type="application/javascript" src="chrome://global/content/preferencesBindings.js"/>
  <script type="application/javascript" src="chrome://browser/content/preferences/languages.js"/>

  <keyset>
    <key key="&windowClose.key;" modifiers="accel" oncommand="Preferences.close(event)"/>
  </keyset>

  <vbox id="LanguagesDialogPane" class="prefpane largeDialogContainer">

    <stringbundleset id="languageSet">
      <stringbundle id="bundleRegions"      src="chrome://global/locale/regionNames.properties"/>
      <stringbundle id="bundleLanguages"    src="chrome://global/locale/languageNames.properties"/>
      <stringbundle id="bundlePreferences"  src="chrome://browser/locale/preferences/preferences.properties"/>
      <stringbundle id="bundleAccepted"     src="resource://gre/res/language.properties"/>
    </stringbundleset>

    <description>&languages.customize2.description;</description>
    <checkbox id="spoofEnglish"
              label="&languages.customize.spoofEnglish;"
              preference="privacy.spoof_english"
              onsyncfrompreference="return gLanguagesDialog.readSpoofEnglish();"
              onsynctopreference="return gLanguagesDialog.writeSpoofEnglish();"/>
    <grid flex="1">
      <columns>
        <column flex="1"/>
        <column/>
      </columns>
      <rows>
        <row flex="1">
          <listbox id="activeLanguages" flex="1" rows="6"
                    seltype="multiple" onselect="gLanguagesDialog.onLanguageSelect();"
                    preference="intl.accept_languages"
                    onsyncfrompreference="return gLanguagesDialog.readAcceptLanguages();"
                    onsynctopreference="return gLanguagesDialog.writeAcceptLanguages();"/>
          <vbox>
            <button id="up" class="up" oncommand="gLanguagesDialog.moveUp();" disabled="true"
                    label="&languages.customize.moveUp.label;"
                    accesskey="&languages.customize.moveUp.accesskey;"
                    preference="pref.browser.language.disable_button.up"/>
            <button id="down" class="down" oncommand="gLanguagesDialog.moveDown();" disabled="true"
                    label="&languages.customize.moveDown.label;"
                    accesskey="&languages.customize.moveDown.accesskey;"
                    preference="pref.browser.language.disable_button.down"/>
            <button id="remove" oncommand="gLanguagesDialog.removeLanguage();" disabled="true"
                    label="&languages.customize.deleteButton.label;"
                    accesskey="&languages.customize.deleteButton.accesskey;"
                    preference="pref.browser.language.disable_button.remove"/>
          </vbox>
        </row>
        <row>
          <separator class="thin"/>
        </row>
        <row>
          <!-- This <vbox> is needed to position search tooltips correctly. -->
          <vbox>
            <menulist id="availableLanguages" oncommand="gLanguagesDialog.onAvailableLanguageSelect();"
                      label="&languages.customize.selectLanguage.label;"
                      label2="&languages.customize.selectLanguage.label;">
              <menupopup id="availableLanguagesPopup"/>
            </menulist>
          </vbox>
          <button id="addButton" oncommand="gLanguagesDialog.addLanguage();" disabled="true"
                  label="&languages.customize.addButton.label;"
                  accesskey="&languages.customize.addButton.accesskey;"/>
        </row>
      </rows>
    </grid>
    <separator/>
    <separator/>
  </vbox>
</dialog>
PK
!<�h�8�89chrome/browser/content/browser/preferences/permissions.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Imported via permissions.xul.
/* import-globals-from ../../../toolkit/content/treeUtils.js */

ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/AppConstants.jsm");

const nsIPermissionManager = Ci.nsIPermissionManager;
const nsICookiePermission = Ci.nsICookiePermission;

const NOTIFICATION_FLUSH_PERMISSIONS = "flush-pending-permissions";

function Permission(principal, type, capability) {
  this.principal = principal;
  this.origin = principal.origin;
  this.type = type;
  this.capability = capability;
}

var gPermissionManager = {
  _type: "",
  _permissions: [],
  _permissionsToAdd: new Map(),
  _permissionsToDelete: new Map(),
  _bundle: null,
  _tree: null,
  _observerRemoved: false,

  _view: {
    _rowCount: 0,
    get rowCount() {
      return this._rowCount;
    },
    getCellText(aRow, aColumn) {
      if (aColumn.id == "siteCol")
        return gPermissionManager._permissions[aRow].origin;
      else if (aColumn.id == "statusCol")
        return gPermissionManager._permissions[aRow].capability;
      return "";
    },

    isSeparator(aIndex) { return false; },
    isSorted() { return false; },
    isContainer(aIndex) { return false; },
    setTree(aTree) {},
    getImageSrc(aRow, aColumn) {},
    getCellValue(aRow, aColumn) {},
    cycleHeader(column) {},
    getRowProperties(row) { return ""; },
    getColumnProperties(column) { return ""; },
    getCellProperties(row, column) {
      if (column.element.getAttribute("id") == "siteCol")
        return "ltr";

      return "";
    }
  },

  _getCapabilityString(aCapability) {
    var stringKey = null;
    switch (aCapability) {
    case nsIPermissionManager.ALLOW_ACTION:
      stringKey = "can";
      break;
    case nsIPermissionManager.DENY_ACTION:
      stringKey = "cannot";
      break;
    case nsICookiePermission.ACCESS_ALLOW_FIRST_PARTY_ONLY:
      stringKey = "canAccessFirstParty";
      break;
    case nsICookiePermission.ACCESS_SESSION:
      stringKey = "canSession";
      break;
    }
    return this._bundle.getString(stringKey);
  },

  addPermission(aCapability) {
    var textbox = document.getElementById("url");
    var input_url = textbox.value.replace(/^\s*/, ""); // trim any leading space
    let principal;
    try {
      // The origin accessor on the principal object will throw if the
      // principal doesn't have a canonical origin representation. This will
      // help catch cases where the URI parser parsed something like
      // `localhost:8080` as having the scheme `localhost`, rather than being
      // an invalid URI. A canonical origin representation is required by the
      // permission manager for storage, so this won't prevent any valid
      // permissions from being entered by the user.
      let uri;
      try {
        uri = Services.io.newURI(input_url);
        principal = Services.scriptSecurityManager.createCodebasePrincipal(uri, {});
        if (principal.origin.startsWith("moz-nullprincipal:")) {
          throw "Null principal";
        }
      } catch (ex) {
        uri = Services.io.newURI("http://" + input_url);
        principal = Services.scriptSecurityManager.createCodebasePrincipal(uri, {});
        // If we have ended up with an unknown scheme, the following will throw.
        principal.origin;
      }
    } catch (ex) {
      var message = this._bundle.getString("invalidURI");
      var title = this._bundle.getString("invalidURITitle");
      Services.prompt.alert(window, title, message);
      return;
    }

    var capabilityString = this._getCapabilityString(aCapability);

    // check whether the permission already exists, if not, add it
    let permissionExists = false;
    let capabilityExists = false;
    for (var i = 0; i < this._permissions.length; ++i) {
      if (this._permissions[i].principal.equals(principal)) {
        permissionExists = true;
        capabilityExists = this._permissions[i].capability == capabilityString;
        if (!capabilityExists) {
          this._permissions[i].capability = capabilityString;
        }
        break;
      }
    }

    let permissionParams = {principal, type: this._type, capability: aCapability};
    if (!permissionExists) {
      this._permissionsToAdd.set(principal.origin, permissionParams);
      this._addPermission(permissionParams);
    } else if (!capabilityExists) {
      this._permissionsToAdd.set(principal.origin, permissionParams);
      this._handleCapabilityChange();
    }

    textbox.value = "";
    textbox.focus();

    // covers a case where the site exists already, so the buttons don't disable
    this.onHostInput(textbox);

    // enable "remove all" button as needed
    document.getElementById("removeAllPermissions").disabled = this._permissions.length == 0;
  },

  _removePermission(aPermission) {
    this._removePermissionFromList(aPermission.principal);

    // If this permission was added during this session, let's remove
    // it from the pending adds list to prevent calls to the
    // permission manager.
    let isNewPermission = this._permissionsToAdd.delete(aPermission.principal.origin);

    if (!isNewPermission) {
      this._permissionsToDelete.set(aPermission.principal.origin, aPermission);
    }

  },

  _handleCapabilityChange() {
    // Re-do the sort, if the status changed from Block to Allow
    // or vice versa, since if we're sorted on status, we may no
    // longer be in order.
    if (this._lastPermissionSortColumn == "statusCol") {
      this._resortPermissions();
    }
    this._tree.treeBoxObject.invalidate();
  },

  _addPermission(aPermission) {
    this._addPermissionToList(aPermission);
    ++this._view._rowCount;
    this._tree.treeBoxObject.rowCountChanged(this._view.rowCount - 1, 1);
    // Re-do the sort, since we inserted this new item at the end.
    this._resortPermissions();
  },

  _resortPermissions() {
    gTreeUtils.sort(this._tree, this._view, this._permissions,
                    this._lastPermissionSortColumn,
                    this._permissionsComparator,
                    this._lastPermissionSortColumn,
                    !this._lastPermissionSortAscending); // keep sort direction
  },

  onHostInput(aSiteField) {
    document.getElementById("btnSession").disabled = !aSiteField.value;
    document.getElementById("btnBlock").disabled = !aSiteField.value;
    document.getElementById("btnAllow").disabled = !aSiteField.value;
  },

  onWindowKeyPress(aEvent) {
    if (aEvent.keyCode == KeyEvent.DOM_VK_ESCAPE)
      window.close();
  },

  onHostKeyPress(aEvent) {
    if (aEvent.keyCode == KeyEvent.DOM_VK_RETURN)
      document.getElementById("btnAllow").click();
  },

  onLoad() {
    this._bundle = document.getElementById("bundlePreferences");
    var params = window.arguments[0];
    this.init(params);
  },

  init(aParams) {
    if (this._type) {
      // reusing an open dialog, clear the old observer
      this.uninit();
    }

    this._type = aParams.permissionType;
    this._manageCapability = aParams.manageCapability;

    var permissionsText = document.getElementById("permissionsText");
    while (permissionsText.hasChildNodes())
      permissionsText.firstChild.remove();
    permissionsText.appendChild(document.createTextNode(aParams.introText));

    document.title = aParams.windowTitle;

    document.getElementById("btnBlock").hidden    = !aParams.blockVisible;
    document.getElementById("btnSession").hidden  = !aParams.sessionVisible;
    document.getElementById("btnAllow").hidden    = !aParams.allowVisible;

    var urlFieldVisible = (aParams.blockVisible || aParams.sessionVisible || aParams.allowVisible);

    var urlField = document.getElementById("url");
    urlField.value = aParams.prefilledHost;
    urlField.hidden = !urlFieldVisible;

    this.onHostInput(urlField);

    var urlLabel = document.getElementById("urlLabel");
    urlLabel.hidden = !urlFieldVisible;

    if (aParams.hideStatusColumn) {
      document.getElementById("statusCol").hidden = true;
    }

    let treecols = document.getElementsByTagName("treecols")[0];
    treecols.addEventListener("click", event => {
      if (event.target.nodeName != "treecol" || event.button != 0) {
        return;
      }

      let sortField = event.target.getAttribute("data-field-name");
      if (!sortField) {
        return;
      }

      gPermissionManager.onPermissionSort(sortField);
    });

    Services.obs.notifyObservers(null, NOTIFICATION_FLUSH_PERMISSIONS, this._type);
    Services.obs.addObserver(this, "perm-changed");

    this._loadPermissions();

    urlField.focus();
  },

  uninit() {
    if (!this._observerRemoved) {
      Services.obs.removeObserver(this, "perm-changed");

      this._observerRemoved = true;
    }
  },

  observe(aSubject, aTopic, aData) {
    if (aTopic == "perm-changed") {
      var permission = aSubject.QueryInterface(Ci.nsIPermission);

      // Ignore unrelated permission types.
      if (permission.type != this._type)
        return;

      if (aData == "added") {
        this._addPermission(permission);
      } else if (aData == "changed") {
        for (var i = 0; i < this._permissions.length; ++i) {
          if (permission.matches(this._permissions[i].principal, true)) {
            this._permissions[i].capability = this._getCapabilityString(permission.capability);
            break;
          }
        }
        this._handleCapabilityChange();
      } else if (aData == "deleted") {
        this._removePermissionFromList(permission.principal);
      }
    }
  },

  onPermissionSelected() {
    var hasSelection = this._tree.view.selection.count > 0;
    var hasRows = this._tree.view.rowCount > 0;
    document.getElementById("removePermission").disabled = !hasRows || !hasSelection;
    document.getElementById("removeAllPermissions").disabled = !hasRows;
  },

  onPermissionDeleted() {
    if (!this._view.rowCount)
      return;
    var removedPermissions = [];
    gTreeUtils.deleteSelectedItems(this._tree, this._view, this._permissions, removedPermissions);
    for (var i = 0; i < removedPermissions.length; ++i) {
      var p = removedPermissions[i];
      this._removePermission(p);
    }
    document.getElementById("removePermission").disabled = !this._permissions.length;
    document.getElementById("removeAllPermissions").disabled = !this._permissions.length;
  },

  onAllPermissionsDeleted() {
    if (!this._view.rowCount)
      return;
    var removedPermissions = [];
    gTreeUtils.deleteAll(this._tree, this._view, this._permissions, removedPermissions);
    for (var i = 0; i < removedPermissions.length; ++i) {
      var p = removedPermissions[i];
      this._removePermission(p);
    }
    document.getElementById("removePermission").disabled = true;
    document.getElementById("removeAllPermissions").disabled = true;
  },

  onPermissionKeyPress(aEvent) {
    if (aEvent.keyCode == KeyEvent.DOM_VK_DELETE) {
      this.onPermissionDeleted();
    } else if (AppConstants.platform == "macosx" &&
               aEvent.keyCode == KeyEvent.DOM_VK_BACK_SPACE) {
      this.onPermissionDeleted();
      aEvent.preventDefault();
    }
  },

  _lastPermissionSortColumn: "",
  _lastPermissionSortAscending: false,
  _permissionsComparator(a, b) {
    return a.toLowerCase().localeCompare(b.toLowerCase());
  },


  onPermissionSort(aColumn) {
    this._lastPermissionSortAscending = gTreeUtils.sort(this._tree,
                                                        this._view,
                                                        this._permissions,
                                                        aColumn,
                                                        this._permissionsComparator,
                                                        this._lastPermissionSortColumn,
                                                        this._lastPermissionSortAscending);
    this._lastPermissionSortColumn = aColumn;
  },

  onApplyChanges() {
    // Stop observing permission changes since we are about
    // to write out the pending adds/deletes and don't need
    // to update the UI
    this.uninit();

    for (let permissionParams of this._permissionsToAdd.values()) {
      Services.perms.addFromPrincipal(permissionParams.principal, permissionParams.type, permissionParams.capability);
    }

    for (let p of this._permissionsToDelete.values()) {
      Services.perms.removeFromPrincipal(p.principal, p.type);
    }

    window.close();
  },

  _loadPermissions() {
    this._tree = document.getElementById("permissionsTree");
    this._permissions = [];

    // load permissions into a table
    var enumerator = Services.perms.enumerator;
    while (enumerator.hasMoreElements()) {
      var nextPermission = enumerator.getNext().QueryInterface(Ci.nsIPermission);
      this._addPermissionToList(nextPermission);
    }

    this._view._rowCount = this._permissions.length;

    // sort and display the table
    this._tree.view = this._view;
    this.onPermissionSort("origin");

    // disable "remove all" button if there are none
    document.getElementById("removeAllPermissions").disabled = this._permissions.length == 0;
  },

  _addPermissionToList(aPermission) {
    if (aPermission.type == this._type &&
        (!this._manageCapability ||
         (aPermission.capability == this._manageCapability))) {

      var principal = aPermission.principal;
      var capabilityString = this._getCapabilityString(aPermission.capability);
      var p = new Permission(principal,
                             aPermission.type,
                             capabilityString);
      this._permissions.push(p);
    }
  },

  _removePermissionFromList(aPrincipal) {
    for (let i = 0; i < this._permissions.length; ++i) {
      if (this._permissions[i].principal.equals(aPrincipal)) {
        this._permissions.splice(i, 1);
        this._view._rowCount--;
        this._tree.treeBoxObject.rowCountChanged(this._view.rowCount - 1, -1);
        this._tree.treeBoxObject.invalidate();
        break;
      }
    }
  },

  setOrigin(aOrigin) {
    document.getElementById("url").value = aOrigin;
  }
};

function setOrigin(aOrigin) {
  gPermissionManager.setOrigin(aOrigin);
}

function initWithParams(aParams) {
  gPermissionManager.init(aParams);
}
PK
!<>�����:chrome/browser/content/browser/preferences/permissions.xul<?xml version="1.0"?>

<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css" type="text/css"?>

<!DOCTYPE dialog SYSTEM "chrome://browser/locale/preferences/permissions.dtd" >

<window id="PermissionsDialog" class="windowDialog"
        windowtype="Browser:Permissions"
        title="&window.title;"
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        style="width: &window.width;;"
        onload="gPermissionManager.onLoad();"
        onunload="gPermissionManager.uninit();"
        persist="screenX screenY width height"
        onkeypress="gPermissionManager.onWindowKeyPress(event);">

  <script src="chrome://global/content/treeUtils.js"/>
  <script src="chrome://browser/content/preferences/permissions.js"/>

  <stringbundle id="bundlePreferences"
                src="chrome://browser/locale/preferences/preferences.properties"/>

  <keyset>
    <key key="&windowClose.key;" modifiers="accel" oncommand="window.close();"/>
  </keyset>

  <vbox class="contentPane largeDialogContainer" flex="1">
    <description id="permissionsText" control="url"/>
    <separator class="thin"/>
    <label id="urlLabel" control="url" accesskey="&address2.accesskey;">&address2.label;</label>
    <hbox align="start">
      <textbox id="url" flex="1"
               oninput="gPermissionManager.onHostInput(event.target);"
               onkeypress="gPermissionManager.onHostKeyPress(event);"/>
    </hbox>
    <hbox pack="end">
      <button id="btnBlock" disabled="true" label="&block.label;" accesskey="&block.accesskey;"
              oncommand="gPermissionManager.addPermission(nsIPermissionManager.DENY_ACTION);"/>
      <button id="btnSession" disabled="true" label="&session.label;" accesskey="&session.accesskey;"
              oncommand="gPermissionManager.addPermission(nsICookiePermission.ACCESS_SESSION);"/>
      <button id="btnAllow" disabled="true" label="&allow.label;" default="true" accesskey="&allow.accesskey;"
              oncommand="gPermissionManager.addPermission(nsIPermissionManager.ALLOW_ACTION);"/>
    </hbox>
    <separator class="thin"/>
    <tree id="permissionsTree" flex="1" style="height: 18em;"
          hidecolumnpicker="true"
          onkeypress="gPermissionManager.onPermissionKeyPress(event)"
          onselect="gPermissionManager.onPermissionSelected();">
      <treecols>
        <treecol id="siteCol" label="&treehead.sitename2.label;" flex="3"
                 data-field-name="origin" persist="width"/>
        <splitter class="tree-splitter"/>
        <treecol id="statusCol" label="&treehead.status.label;" flex="1"
                 data-field-name="capability" persist="width"/>
      </treecols>
      <treechildren/>
    </tree>
  </vbox>
  <vbox>
    <hbox class="actionButtons" align="left" flex="1">
      <button id="removePermission" disabled="true"
              accesskey="&removepermission2.accesskey;"
              icon="remove" label="&removepermission2.label;"
              oncommand="gPermissionManager.onPermissionDeleted();"/>
      <button id="removeAllPermissions"
              icon="clear" label="&removeallpermissions2.label;"
              accesskey="&removeallpermissions2.accesskey;"
              oncommand="gPermissionManager.onAllPermissionsDeleted();"/>
    </hbox>
    <spacer flex="1"/>
    <hbox class="actionButtons" align="right" flex="1">
      <button oncommand="close();" icon="close"
              label="&button.cancel.label;" accesskey="&button.cancel.accesskey;" />
      <button id="btnApplyChanges" oncommand="gPermissionManager.onApplyChanges();" icon="save"
              label="&button.ok.label;" accesskey="&button.ok.accesskey;"/>
    </hbox>
  </vbox>
</window>
PK
!<Z\���6chrome/browser/content/browser/preferences/sanitize.js/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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-globals-from ../../../toolkit/content/preferencesBindings.js */

Preferences.addAll([
  { id: "privacy.clearOnShutdown.history", type: "bool" },
  { id: "privacy.clearOnShutdown.formdata", type: "bool" },
  { id: "privacy.clearOnShutdown.downloads", type: "bool" },
  { id: "privacy.clearOnShutdown.cookies", type: "bool" },
  { id: "privacy.clearOnShutdown.cache", type: "bool" },
  { id: "privacy.clearOnShutdown.offlineApps", type: "bool" },
  { id: "privacy.clearOnShutdown.sessions", type: "bool" },
  { id: "privacy.clearOnShutdown.siteSettings", type: "bool" },
]);

var gSanitizeDialog = Object.freeze({
  init() {
    this.onClearHistoryChanged();

    Preferences.get("privacy.clearOnShutdown.history").on("change", this.onClearHistoryChanged.bind(this));
  },

  onClearHistoryChanged() {
    let downloadsPref = Preferences.get("privacy.clearOnShutdown.downloads");
    let historyPref = Preferences.get("privacy.clearOnShutdown.history");
    downloadsPref.value = historyPref.value;
  }
});
PK
!<?z.}TT7chrome/browser/content/browser/preferences/sanitize.xul<?xml version="1.0"?>

<!-- -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- -->
<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<?xml-stylesheet href="chrome://global/skin/"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css"?>

<!DOCTYPE dialog [
  <!ENTITY % preferencesDTD SYSTEM "chrome://global/locale/preferences.dtd">
  %preferencesDTD;
  <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
  %brandDTD;
  <!ENTITY % sanitizeDTD SYSTEM "chrome://browser/locale/sanitize.dtd">
  %sanitizeDTD;
]>

<dialog id="SanitizeDialog" type="child" class="prefwindow"
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        buttons="accept,cancel,help"
        persist="lastSelected screenX screenY"
        closebuttonlabel="&preferencesCloseButton.label;"
        closebuttonaccesskey="&preferencesCloseButton.accesskey;"
        role="dialog"
        ondialoghelp="openPrefsHelp()"
        style="width: &sanitizePrefs2.modal.width;"
        title="&sanitizePrefs2.title;"
        helpTopic="prefs-clear-private-data"
        onload="gSanitizeDialog.init();">

  <script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>
  <script type="application/javascript" src="chrome://global/content/preferencesBindings.js"/>

  <keyset>
    <key key="&windowClose.key;" modifiers="accel" oncommand="Preferences.close(event)"/>
  </keyset>

  <vbox id="SanitizeDialogPane" class="prefpane">

    <script type="application/javascript" src="chrome://browser/content/preferences/sanitize.js"/>

    <description>&clearDataSettings3.label;</description>

    <groupbox orient="horizontal">
      <caption><label>&historySection.label;</label></caption>
      <grid flex="1">
        <columns>
          <column style="width: &sanitizePrefs2.column.width;"/>
          <column flex="1"/>
        </columns>
        <rows>
          <row>
            <checkbox label="&itemHistoryAndDownloads.label;"
                      accesskey="&itemHistoryAndDownloads.accesskey;"
                      preference="privacy.clearOnShutdown.history"/>
            <checkbox label="&itemCookies.label;"
                      accesskey="&itemCookies.accesskey;"
                      preference="privacy.clearOnShutdown.cookies"/>
          </row>
          <row>
            <checkbox label="&itemActiveLogins.label;"
                      accesskey="&itemActiveLogins.accesskey;"
                      preference="privacy.clearOnShutdown.sessions"/>
            <checkbox label="&itemCache.label;"
                      accesskey="&itemCache.accesskey;"
                      preference="privacy.clearOnShutdown.cache"/>
          </row>
          <row>
            <checkbox label="&itemFormSearchHistory.label;"
                      accesskey="&itemFormSearchHistory.accesskey;"
                      preference="privacy.clearOnShutdown.formdata"/>
          </row>
        </rows>
      </grid>
    </groupbox>
    <groupbox orient="horizontal">
      <caption><label>&dataSection.label;</label></caption>
      <grid flex="1">
        <columns>
          <column style="width: &sanitizePrefs2.column.width;"/>
          <column flex="1"/>
        </columns>
        <rows>
          <row>
            <checkbox label="&itemSitePreferences.label;"
                      accesskey="&itemSitePreferences.accesskey;"
                      preference="privacy.clearOnShutdown.siteSettings"/>
            <checkbox label="&itemOfflineApps.label;"
                      accesskey="&itemOfflineApps.accesskey;"
                      preference="privacy.clearOnShutdown.offlineApps"/>
          </row>
        </rows>
      </grid>
    </groupbox>
  </vbox>
</dialog>
PK
!<��`�<chrome/browser/content/browser/preferences/selectBookmark.js//* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* eslint-env mozilla/places-overlay */

/**
 * SelectBookmarkDialog controls the user interface for the "Use Bookmark for
 * Home Page" dialog.
 *
 * The caller (gMainPane.setHomePageToBookmark in main.js) invokes this dialog
 * with a single argument - a reference to an object with a .urls property and
 * a .names property.  This dialog is responsible for updating the contents of
 * the .urls property with an array of URLs to use as home pages and for
 * updating the .names property with an array of names for those URLs before it
 * closes.
 */
var SelectBookmarkDialog = {
  init: function SBD_init() {
    document.getElementById("bookmarks").place =
      "place:type=" + Ci.nsINavHistoryQueryOptions.RESULTS_AS_ROOTS_QUERY;

    // Initial update of the OK button.
    this.selectionChanged();
  },

  /**
   * Update the disabled state of the OK button as the user changes the
   * selection within the view.
   */
  selectionChanged: function SBD_selectionChanged() {
    var accept = document.documentElement.getButton("accept");
    var bookmarks = document.getElementById("bookmarks");
    var disableAcceptButton = true;
    if (bookmarks.hasSelection) {
      if (!PlacesUtils.nodeIsSeparator(bookmarks.selectedNode))
        disableAcceptButton = false;
    }
    accept.disabled = disableAcceptButton;
  },

  onItemDblClick: function SBD_onItemDblClick() {
    var bookmarks = document.getElementById("bookmarks");
    var selectedNode = bookmarks.selectedNode;
    if (selectedNode && PlacesUtils.nodeIsURI(selectedNode)) {
      /**
       * The user has double clicked on a tree row that is a link. Take this to
       * mean that they want that link to be their homepage, and close the dialog.
       */
      document.documentElement.getButton("accept").click();
    }
  },

  /**
   * User accepts their selection. Set all the selected URLs or the contents
   * of the selected folder as the list of homepages.
   */
  accept: function SBD_accept() {
    var bookmarks = document.getElementById("bookmarks");
    NS_ASSERT(bookmarks.hasSelection,
              "Should not be able to accept dialog if there is no selected URL!");
    var urls = [];
    var names = [];
    var selectedNode = bookmarks.selectedNode;
    if (PlacesUtils.nodeIsFolder(selectedNode)) {
      var contents = PlacesUtils.getFolderContents(selectedNode.itemId).root;
      var cc = contents.childCount;
      for (var i = 0; i < cc; ++i) {
        var node = contents.getChild(i);
        if (PlacesUtils.nodeIsURI(node)) {
          urls.push(node.uri);
          names.push(node.title);
        }
      }
      contents.containerOpen = false;
    } else {
      urls.push(selectedNode.uri);
      names.push(selectedNode.title);
    }
    window.arguments[0].urls = urls;
    window.arguments[0].names = names;
  }
};
PK
!<^��NN=chrome/browser/content/browser/preferences/selectBookmark.xul<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->


<?xml-stylesheet href="chrome://browser/content/places/places.css"?>

<?xml-stylesheet href="chrome://global/skin/"?>
<?xml-stylesheet href="chrome://browser/skin/places/places.css"?>

<?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?>

<!DOCTYPE dialog SYSTEM "chrome://browser/locale/preferences/selectBookmark.dtd">

<dialog id="selectBookmarkDialog"
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        title="&selectBookmark.title;" style="width: 32em;"
        persist="screenX screenY width height" screenX="24" screenY="24"
        onload="SelectBookmarkDialog.init();"
        ondialogaccept="SelectBookmarkDialog.accept();">

  <script type="application/javascript"
          src="chrome://browser/content/preferences/selectBookmark.js"/>

  <description>&selectBookmark.label;</description>

  <separator class="thin"/>

  <tree id="bookmarks" flex="1" type="places"
        style="height: 15em;"
        hidecolumnpicker="true"
        seltype="single"
        ondblclick="SelectBookmarkDialog.onItemDblClick();"
        onselect="SelectBookmarkDialog.selectionChanged();"
        disableUserActions="true">
    <treecols>
      <treecol id="title" flex="1" primary="true" hideheader="true"/>
    </treecols>
    <treechildren id="bookmarksChildren" flex="1"/>
  </tree>

  <separator class="thin"/>

</dialog>
PK
!<��5``Dchrome/browser/content/browser/preferences/siteDataRemoveSelected.js/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

let gSiteDataRemoveSelected = {

  init() {
    let bundlePreferences = document.getElementById("bundlePreferences");
    let acceptBtn = document.getElementById("SiteDataRemoveSelectedDialog")
                            .getButton("accept");
    acceptBtn.label = bundlePreferences.getString("acceptRemove");

    let hosts = window.arguments[0].hosts;
    hosts.sort();
    let tree = document.getElementById("sitesTree");
    this._view._hosts = hosts;
    tree.view = this._view;
  },

  ondialogaccept() {
    window.arguments[0].allowed = true;
  },

  ondialogcancel() {
    window.arguments[0].allowed = false;
  },

  _view: {
    _hosts: null,

    get rowCount() {
      return this._hosts.length;
    },
    getCellText(index, column) {
      return this._hosts[index];
    },
    getLevel(index) {
      return 0;
    },
  },
};
PK
!<�Echrome/browser/content/browser/preferences/siteDataRemoveSelected.xul<?xml version="1.0"?>

<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/preferences/siteDataSettings.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/in-content/siteDataSettings.css" type="text/css"?>

<!DOCTYPE dialog SYSTEM "chrome://browser/locale/preferences/siteDataSettings.dtd" >

<dialog id="SiteDataRemoveSelectedDialog"
        windowtype="Browser:SiteDataRemoveSelected"
        width="500"
        title="&removingDialog1.title;"
        onload="gSiteDataRemoveSelected.init();"
        ondialogaccept="gSiteDataRemoveSelected.ondialogaccept(); return true;"
        ondialogcancel="gSiteDataRemoveSelected.ondialogcancel(); return true;"
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

  <script src="chrome://browser/content/preferences/siteDataRemoveSelected.js"/>

  <stringbundle id="bundlePreferences"
                src="chrome://browser/locale/preferences/preferences.properties"/>

  <vbox id="contentContainer">
    <hbox flex="1">
      <vbox>
        <image class="question-icon"/>
      </vbox>
      <vbox flex="1">
        <!-- Only show this label on OS X because of no dialog title -->
        <label id="removing-label"
               hidden="true"
        >&removingDialog1.title;</label>
        <separator class="thin"/>
        <description id="removing-description">&removingSelected1.description;</description>
      </vbox>
    </hbox>

    <separator />

    <vbox flex="1">
      <label>&siteTree3.label;</label>
      <separator class="thin"/>
      <tree id="sitesTree" flex="1" seltype="single" hidecolumnpicker="true">
        <treecols>
          <treecol primary="true" flex="1" hideheader="true"/>
        </treecols>
        <treechildren />
      </tree>
    </vbox>
  </vbox>

</dialog>
PK
!<FO�w>&>&>chrome/browser/content/browser/preferences/siteDataSettings.js/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");

ChromeUtils.defineModuleGetter(this, "SiteDataManager",
                               "resource:///modules/SiteDataManager.jsm");
ChromeUtils.defineModuleGetter(this, "DownloadUtils",
                               "resource://gre/modules/DownloadUtils.jsm");

"use strict";

let gSiteDataSettings = {

  // Array of metadata of sites. Each array element is object holding:
  // - uri: uri of site; instance of nsIURI
  // - baseDomain: base domain of the site
  // - cookies: array of cookies of that site
  // - usage: disk usage which site uses
  // - userAction: "remove" or "update-permission"; the action user wants to take.
  _sites: null,

  _list: null,
  _searchBox: null,
  _prefStrBundle: null,

  _createSiteListItem(site) {
    let item = document.createElement("richlistitem");
    item.setAttribute("host", site.host);
    let container = document.createElement("hbox");

    // Creates a new column item with the specified relative width.
    function addColumnItem(value, flexWidth) {
      let box = document.createElement("hbox");
      box.className = "item-box";
      box.setAttribute("flex", flexWidth);
      let label = document.createElement("label");
      label.setAttribute("crop", "end");
      if (value) {
        box.setAttribute("tooltiptext", value);
        label.setAttribute("value", value);
      }
      box.appendChild(label);
      container.appendChild(box);
    }

    // Add "Host" column.
    addColumnItem(site.host, "4");

    // Add "Cookies" column.
    addColumnItem(site.cookies.length, "1");

    // Add "Storage" column
    if (site.usage > 0 || site.persisted) {
      let size = DownloadUtils.convertByteUnits(site.usage);
      let strName = site.persisted ? "siteUsagePersistent" : "siteUsage";
      addColumnItem(this._prefStrBundle.getFormattedString(strName, size), "2");
    } else {
      // Pass null to avoid showing "0KB" when there is no site data stored.
      addColumnItem(null, "2");
    }

    // Add "Last Used" column.
    addColumnItem(site.lastAccessed > 0 ?
      this._formatter.format(site.lastAccessed) : null, "2");

    item.appendChild(container);
    return item;
  },

  init() {
    function setEventListener(id, eventType, callback) {
      document.getElementById(id)
              .addEventListener(eventType, callback.bind(gSiteDataSettings));
    }

    this._formatter = new Services.intl.DateTimeFormat(undefined, {
      dateStyle: "short", timeStyle: "short",
    });

    this._list = document.getElementById("sitesList");
    this._searchBox = document.getElementById("searchBox");
    this._prefStrBundle = document.getElementById("bundlePreferences");
    SiteDataManager.getSites().then(sites => {
      this._sites = sites;
      let sortCol = document.querySelector("treecol[data-isCurrentSortCol=true]");
      this._sortSites(this._sites, sortCol);
      this._buildSitesList(this._sites);
      Services.obs.notifyObservers(null, "sitedata-settings-init");
    });

    let brandShortName = document.getElementById("bundle_brand").getString("brandShortName");
    let settingsDescription = document.getElementById("settingsDescription");
    settingsDescription.textContent = this._prefStrBundle.getFormattedString("siteDataSettings3.description", [brandShortName]);

    setEventListener("sitesList", "select", this.onSelect);
    setEventListener("hostCol", "click", this.onClickTreeCol);
    setEventListener("usageCol", "click", this.onClickTreeCol);
    setEventListener("lastAccessedCol", "click", this.onClickTreeCol);
    setEventListener("cookiesCol", "click", this.onClickTreeCol);
    setEventListener("cancel", "command", this.close);
    setEventListener("save", "command", this.saveChanges);
    setEventListener("searchBox", "command", this.onCommandSearch);
    setEventListener("removeAll", "command", this.onClickRemoveAll);
    setEventListener("removeSelected", "command", this.onClickRemoveSelected);
  },

  _updateButtonsState() {
    let items = this._list.getElementsByTagName("richlistitem");
    let removeSelectedBtn = document.getElementById("removeSelected");
    let removeAllBtn = document.getElementById("removeAll");
    removeSelectedBtn.disabled = this._list.selectedItems.length == 0;
    removeAllBtn.disabled = items.length == 0;

    let removeAllBtnLabelStringID = "removeAllSiteData.label";
    let removeAllBtnAccesskeyStringID = "removeAllSiteData.accesskey";
    if (this._searchBox.value) {
      removeAllBtnLabelStringID = "removeAllSiteDataShown.label";
      removeAllBtnAccesskeyStringID = "removeAllSiteDataShown.accesskey";
    }
    removeAllBtn.setAttribute("label", this._prefStrBundle.getString(removeAllBtnLabelStringID));
    removeAllBtn.setAttribute("accesskey", this._prefStrBundle.getString(removeAllBtnAccesskeyStringID));
  },

  /**
   * @param sites {Array}
   * @param col {XULElement} the <treecol> being sorted on
   */
  _sortSites(sites, col) {
    let isCurrentSortCol = col.getAttribute("data-isCurrentSortCol");
    let sortDirection = col.getAttribute("data-last-sortDirection") || "ascending";
    if (isCurrentSortCol) {
      // Sort on the current column, flip the sorting direction
      sortDirection = sortDirection === "ascending" ? "descending" : "ascending";
    }

    let sortFunc = null;
    switch (col.id) {
      case "hostCol":
        sortFunc = (a, b) => {
          let aHost = a.baseDomain.toLowerCase();
          let bHost = b.baseDomain.toLowerCase();
          return aHost.localeCompare(bHost);
        };
        break;

      case "cookiesCol":
        sortFunc = (a, b) => a.cookies.length - b.cookies.length;
        break;

      case "usageCol":
        sortFunc = (a, b) => a.usage - b.usage;
        break;

      case "lastAccessedCol":
        sortFunc = (a, b) => a.lastAccessed - b.lastAccessed;
        break;
    }
    if (sortDirection === "descending") {
      sites.sort((a, b) => sortFunc(b, a));
    } else {
      sites.sort(sortFunc);
    }

    let cols = this._list.querySelectorAll("treecol");
    cols.forEach(c => {
      c.removeAttribute("sortDirection");
      c.removeAttribute("data-isCurrentSortCol");
    });
    col.setAttribute("data-isCurrentSortCol", true);
    col.setAttribute("sortDirection", sortDirection);
    col.setAttribute("data-last-sortDirection", sortDirection);
  },

  /**
   * @param sites {Array} array of metadata of sites
   */
  _buildSitesList(sites) {
    // Clear old entries.
    let oldItems = this._list.querySelectorAll("richlistitem");
    for (let item of oldItems) {
      item.remove();
    }

    let keyword = this._searchBox.value.toLowerCase().trim();
    for (let site of sites) {
      let host = site.host;
      if (keyword && !host.includes(keyword)) {
        continue;
      }

      if (site.userAction === "remove") {
        continue;
      }

      let item = this._createSiteListItem(site);
      this._list.appendChild(item);
    }
    this._updateButtonsState();
  },

  _removeSiteItems(items) {
    for (let i = items.length - 1; i >= 0; --i) {
      let item = items[i];
      let host = item.getAttribute("host");
      let siteForHost = this._sites.find(site => site.host == host);
      if (siteForHost) {
        siteForHost.userAction = "remove";
      }
      item.remove();
    }
    this._updateButtonsState();
  },

  saveChanges() {
    // Tracks whether the user confirmed their decision.
    let allowed = false;

    let removals = this._sites
      .filter(site => site.userAction == "remove")
      .map(site => site.host);

    if (removals.length > 0) {
      if (this._sites.length == removals.length) {
        allowed = SiteDataManager.promptSiteDataRemoval(window);
        if (allowed) {
          SiteDataManager.removeAll();
        }
      } else {
        let args = {
          hosts: removals,
          allowed: false
        };
        let features = "centerscreen,chrome,modal,resizable=no";
        window.openDialog("chrome://browser/content/preferences/siteDataRemoveSelected.xul", "", features, args);
        allowed = args.allowed;
        if (allowed) {
          try {
            SiteDataManager.remove(removals);
          } catch (e) {
            // Hit error, maybe remove unknown site.
            // Let's print out the error, then proceed to close this settings dialog.
            // When we next open again we will once more get sites from the SiteDataManager and refresh the list.
            Cu.reportError(e);
          }
        }
      }
    }

    // If the user cancelled the confirm dialog keep the site data window open,
    // they can still press cancel again to exit.
    if (allowed) {
      this.close();
    }
  },

  close() {
    window.close();
  },

  onClickTreeCol(e) {
    this._sortSites(this._sites, e.target);
    this._buildSitesList(this._sites);
    this._list.clearSelection();
  },

  onCommandSearch() {
    this._buildSitesList(this._sites);
    this._list.clearSelection();
  },

  onClickRemoveSelected() {
    this._removeSiteItems(this._list.selectedItems);
    this._list.clearSelection();
  },

  onClickRemoveAll() {
    let siteItems = this._list.getElementsByTagName("richlistitem");
    if (siteItems.length > 0) {
      this._removeSiteItems(siteItems);
    }
  },

  onKeyPress(e) {
    if (e.keyCode == KeyEvent.DOM_VK_ESCAPE) {
      this.close();
    }
  },

  onSelect() {
    this._updateButtonsState();
  }
};
PK
!<0�>�{
{
?chrome/browser/content/browser/preferences/siteDataSettings.xul<?xml version="1.0"?>

<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/preferences/siteDataSettings.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/in-content/siteDataSettings.css" type="text/css"?>

<!DOCTYPE dialog SYSTEM "chrome://browser/locale/preferences/siteDataSettings.dtd" >

<window id="SiteDataSettingsDialog" windowtype="Browser:SiteDataSettings"
        class="windowDialog" title="&window2.title;"
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        style="width: 45em;"
        onload="gSiteDataSettings.init();"
        onkeypress="gSiteDataSettings.onKeyPress(event);"
        persist="screenX screenY width height">

  <script src="chrome://browser/content/preferences/siteDataSettings.js"/>

  <stringbundle id="bundlePreferences"
                src="chrome://browser/locale/preferences/preferences.properties"/>
  <stringbundle id="bundle_brand" src="chrome://branding/locale/brand.properties"/>

  <vbox flex="1">
    <description id="settingsDescription"></description>
    <separator class="thin"/>

    <hbox id="searchBoxContainer">
      <textbox id="searchBox" type="search" flex="1"
        placeholder="&searchTextboxPlaceHolder;" accesskey="&searchTextboxPlaceHolder.accesskey;"/>
    </hbox>
    <separator class="thin"/>

    <richlistbox seltype="multiple" id="sitesList" orient="vertical" flex="1">
      <listheader>
        <treecol flex="4" width="50" label="&hostCol.label;" id="hostCol"/>
        <treecol flex="1" width="50" label="&cookiesCol.label;" id="cookiesCol"/>
        <!-- Sorted by usage so the user can quickly see which sites use the most data. -->
        <treecol flex="2" width="50" label="&usageCol.label;" id="usageCol" data-isCurrentSortCol="true"/>
        <treecol flex="2" width="50" label="&lastAccessedCol.label;" id="lastAccessedCol" />
      </listheader>
    </richlistbox>
  </vbox>

  <hbox align="start">
    <button id="removeSelected" label="&removeSelected.label;" accesskey="&removeSelected.accesskey;"/>
    <button id="removeAll"/>
  </hbox>

  <vbox align="end">
    <hbox>
        <button id="cancel" label="&cancel.label;" accesskey="&cancel.accesskey;"/>
        <button id="save" label="&save.label;" accesskey="&save.accesskey;"/>
    </hbox>
  </vbox>

</window>
PK
!<J*�N��>chrome/browser/content/browser/preferences/sitePermissions.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

.website-name {
  padding-inline-start: 7px;
  -moz-box-align: center;
}

#permissionsBox {
  min-height: 18em;
}

#siteCol,
#statusCol,
#permissionsBox > richlistitem {
  min-height: 35px;
}

.website-status {
  margin: 1px;
  margin-inline-end: 5px;
}

#permissionsDisableDescription {
  margin-inline-start: 32px;
  color: #737373;
  line-height: 110%;
}

#permissionsDisableCheckbox {
  margin-inline-start: 4px;
  padding-top: 10px;
}
PK
!<RkF/F/=chrome/browser/content/browser/preferences/sitePermissions.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
ChromeUtils.import("resource:///modules/SitePermissions.jsm");

function Permission(principal, type, capability, capabilityString) {
  this.principal = principal;
  this.origin = principal.origin;
  this.type = type;
  this.capability = capability;
  this.capabilityString = capabilityString;
}

const PERMISSION_STATES = [SitePermissions.ALLOW, SitePermissions.BLOCK, SitePermissions.PROMPT];

var gSitePermissionsManager = {
  _type: "",
  _isObserving: false,
  _permissions: new Map(),
  _permissionsToChange: new Map(),
  _permissionsToDelete: new Map(),
  _list: null,
  _bundle: null,
  _removeButton: null,
  _removeAllButton: null,
  _searchBox: null,
  _checkbox: null,
  _currentDefaultPermissionsState: null,
  _defaultPermissionStatePrefName: null,

  onLoad() {
    let params = window.arguments[0];
    this.init(params);
  },

  init(params) {
    if (!this._isObserving) {
      Services.obs.addObserver(this, "perm-changed");
      this._isObserving = true;
    }

    this._bundle = document.getElementById("bundlePreferences");
    this._type = params.permissionType;
    this._list = document.getElementById("permissionsBox");
    this._removeButton = document.getElementById("removePermission");
    this._removeAllButton = document.getElementById("removeAllPermissions");
    this._searchBox = document.getElementById("searchBox");
    this._checkbox = document.getElementById("permissionsDisableCheckbox");

    let permissionsText = document.getElementById("permissionsText");
    while (permissionsText.hasChildNodes())
      permissionsText.firstChild.remove();
    permissionsText.appendChild(document.createTextNode(params.introText));

    this._checkbox.setAttribute("label", params.disablePermissionsLabel);
    let permissionsDisableDescription = document.getElementById("permissionsDisableDescription");
    permissionsDisableDescription.appendChild(document.createTextNode(params.disablePermissionsDescription));

    document.title = params.windowTitle;

    // Initialize the checkbox state.
    this._defaultPermissionStatePrefName = "permissions.default." + this._type;
    let pref = Services.prefs.getPrefType(this._defaultPermissionStatePrefName);
    if (pref != Services.prefs.PREF_INVALID) {
      this._currentDefaultPermissionsState = Services.prefs.getIntPref(this._defaultPermissionStatePrefName);
    }

    if (this._currentDefaultPermissionsState === null) {
      this._checkbox.setAttribute("hidden", true);
      permissionsDisableDescription.setAttribute("hidden", true);
    } else if (this._currentDefaultPermissionsState == SitePermissions.BLOCK) {
      this._checkbox.checked = true;
    } else {
      this._checkbox.checked = false;
    }

    this._loadPermissions();
    this.buildPermissionsList();

    this._searchBox.focus();
  },

  uninit() {
    if (this._isObserving) {
      Services.obs.removeObserver(this, "perm-changed");
      this._isObserving = false;
    }
  },

  observe(subject, topic, data) {
    if (topic !== "perm-changed")
      return;

    let permission = subject.QueryInterface(Ci.nsIPermission);

    // Ignore unrelated permission types and permissions with unknown states.
    if (permission.type !== this._type || !PERMISSION_STATES.includes(permission.capability))
      return;

    if (data == "added") {
      this._addPermissionToList(permission);
      this.buildPermissionsList();
    } else if (data == "changed") {
      let p = this._permissions.get(permission.principal.origin);
      p.capability = permission.capability;
      p.capabilityString = this._getCapabilityString(permission.capability);
      this._handleCapabilityChange(p);
      this.buildPermissionsList();
    } else if (data == "deleted") {
      this._removePermissionFromList(permission.principal.origin);
    }
  },

  _handleCapabilityChange(perm) {
    let permissionlistitem = document.getElementsByAttribute("origin", perm.origin)[0];
    let menulist = permissionlistitem.getElementsByTagName("menulist")[0];
    menulist.selectedItem =
      menulist.getElementsByAttribute("value", perm.capability)[0];
  },

  _getCapabilityString(capability) {
    let stringKey = null;
    switch (capability) {
    case Services.perms.ALLOW_ACTION:
      stringKey = "can";
      break;
    case Services.perms.DENY_ACTION:
      stringKey = "cannot";
      break;
    case Services.perms.PROMPT_ACTION:
      stringKey = "prompt";
      break;
    }
    return this._bundle.getString(stringKey);
  },

  _addPermissionToList(perm) {
    // Ignore unrelated permission types and permissions with unknown states.
    if (perm.type !== this._type || !PERMISSION_STATES.includes(perm.capability))
      return;
    let capabilityString = this._getCapabilityString(perm.capability);
    let p = new Permission(perm.principal, perm.type, perm.capability,
                           capabilityString);
    this._permissions.set(p.origin, p);
  },

  _removePermissionFromList(origin) {
    this._permissions.delete(origin);
    let permissionlistitem = document.getElementsByAttribute("origin", origin)[0];
    this._list.removeItemAt(this._list.getIndexOfItem(permissionlistitem));
  },

  _loadPermissions() {
    // load permissions into a table.
    let enumerator = Services.perms.enumerator;
    while (enumerator.hasMoreElements()) {
      let nextPermission = enumerator.getNext().QueryInterface(Ci.nsIPermission);
      this._addPermissionToList(nextPermission);
    }
  },

  _createPermissionListItem(permission) {
    let richlistitem = document.createElement("richlistitem");
    richlistitem.setAttribute("origin", permission.origin);
    let row = document.createElement("hbox");
    row.setAttribute("flex", "1");

    let hbox = document.createElement("hbox");
    let website = document.createElement("label");
    website.setAttribute("value", permission.origin);
    website.setAttribute("width", "50");
    hbox.setAttribute("class", "website-name");
    hbox.setAttribute("flex", "3");
    hbox.appendChild(website);

    let menulist = document.createElement("menulist");
    let menupopup = document.createElement("menupopup");
    menulist.setAttribute("flex", "1");
    menulist.setAttribute("width", "50");
    menulist.setAttribute("class", "website-status");
    menulist.appendChild(menupopup);
    let states = SitePermissions.getAvailableStates(permission.type);
    for (let state of states) {
      // Work around the (rare) edge case when a user has changed their
      // default permission type back to UNKNOWN while still having a
      // PROMPT permission set for an origin.
      if (state == SitePermissions.UNKNOWN &&
          permission.capability == SitePermissions.PROMPT) {
        state = SitePermissions.PROMPT;
      } else if (state == SitePermissions.UNKNOWN) {
        continue;
      }
      let m = document.createElement("menuitem");
      m.setAttribute("label", this._getCapabilityString(state));
      m.setAttribute("value", state);
      menupopup.appendChild(m);
    }
    menulist.value = permission.capability;

    menulist.addEventListener("select", () => {
      this.onPermissionChange(permission, Number(menulist.selectedItem.value));
    });

    row.appendChild(hbox);
    row.appendChild(menulist);
    richlistitem.appendChild(row);
    this._list.appendChild(richlistitem);
  },

  onWindowKeyPress(event) {
    if (event.keyCode == KeyEvent.DOM_VK_ESCAPE)
      window.close();
  },

  onPermissionKeyPress(event) {
    if (!this._list.selectedItem)
      return;

    if (event.keyCode == KeyEvent.DOM_VK_DELETE ||
       (AppConstants.platform == "macosx" &&
        event.keyCode == KeyEvent.DOM_VK_BACK_SPACE)) {
      this.onPermissionDelete();
      event.preventDefault();
    }
  },

  _setRemoveButtonState() {
    if (!this._list)
      return;

    let hasSelection = this._list.selectedIndex >= 0;
    let hasRows = this._list.itemCount > 0;
    this._removeButton.disabled = !hasSelection;
    this._removeAllButton.disabled = !hasRows;
  },

  onPermissionDelete() {
    let richlistitem = this._list.selectedItem;
    let origin = richlistitem.getAttribute("origin");
    let permission = this._permissions.get(origin);

    this._removePermissionFromList(origin);
    this._permissionsToDelete.set(permission.origin, permission);

    this._setRemoveButtonState();
  },

  onAllPermissionsDelete() {
    for (let permission of this._permissions.values()) {
      this._removePermissionFromList(permission.origin);
      this._permissionsToDelete.set(permission.origin, permission);
    }

    this._setRemoveButtonState();
  },

  onPermissionSelect() {
    this._setRemoveButtonState();
  },

  onPermissionChange(perm, capability) {
    let p = this._permissions.get(perm.origin);
    if (p.capability == capability)
      return;
    p.capability = capability;
    p.capabilityString = this._getCapabilityString(capability);
    this._permissionsToChange.set(p.origin, p);

    // enable "remove all" button as needed
    this._setRemoveButtonState();
  },

  onApplyChanges() {
    // Stop observing permission changes since we are about
    // to write out the pending adds/deletes and don't need
    // to update the UI
    this.uninit();

    for (let p of this._permissionsToChange.values()) {
      let uri = Services.io.newURI(p.origin);
      SitePermissions.set(uri, p.type, p.capability);
    }

    for (let p of this._permissionsToDelete.values()) {
      let uri = Services.io.newURI(p.origin);
      SitePermissions.remove(uri, p.type);
    }

    if (this._checkbox.checked) {
      Services.prefs.setIntPref(this._defaultPermissionStatePrefName, SitePermissions.BLOCK);
    } else if (this._currentDefaultPermissionsState == SitePermissions.BLOCK) {
      Services.prefs.setIntPref(this._defaultPermissionStatePrefName, SitePermissions.UNKNOWN);
    }

    window.close();
  },

  buildPermissionsList(sortCol) {
    // Clear old entries.
    let oldItems = this._list.querySelectorAll("richlistitem");
    for (let item of oldItems) {
      item.remove();
    }

    // Sort permissions.
    let sortedPermissions = this._sortPermissions(sortCol);

    let keyword = this._searchBox.value.toLowerCase().trim();
    for (let permission of sortedPermissions) {
      if (keyword && !permission.origin.includes(keyword)) {
        continue;
      }

      this._createPermissionListItem(permission);
    }

    this._setRemoveButtonState();
  },

  _sortPermissions(column) {
    let permissions = Array.from(this._permissions.values());
    let sortDirection;

    if (!column) {
      column = document.querySelector("treecol[data-isCurrentSortCol=true]");
      sortDirection = column.getAttribute("data-last-sortDirection") || "ascending";
    } else {
      sortDirection = column.getAttribute("data-last-sortDirection");
      sortDirection = sortDirection === "ascending" ? "descending" : "ascending";
    }

    let sortFunc = null;
    switch (column.id) {
      case "siteCol":
        sortFunc = (a, b) => {
          return a.origin.localeCompare(b.origin);
        };
        break;

      case "statusCol":
        sortFunc = (a, b) => {
          return a.capabilityString.localeCompare(b.capabilityString);
        };
        break;
    }

    if (sortDirection === "descending") {
      permissions.sort((a, b) => sortFunc(b, a));
    } else {
      permissions.sort(sortFunc);
    }

    let cols = this._list.querySelectorAll("treecol");
    cols.forEach(c => {
      c.removeAttribute("data-isCurrentSortCol");
      c.removeAttribute("sortDirection");
    });
    column.setAttribute("data-isCurrentSortCol", "true");
    column.setAttribute("sortDirection", sortDirection);
    column.setAttribute("data-last-sortDirection", sortDirection);

    return permissions;
  },
};
PK
!<�48>chrome/browser/content/browser/preferences/sitePermissions.xul<?xml version="1.0"?>

<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/preferences/sitePermissions.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css" type="text/css"?>

<!DOCTYPE dialog SYSTEM "chrome://browser/locale/preferences/permissions.dtd" >

<window id="SitePermissionsDialog" class="windowDialog"
        windowtype="Browser:SitePermissions"
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        style="width: &window.width;;"
        onload="gSitePermissionsManager.onLoad();"
        onunload="gSitePermissionsManager.uninit();"
        persist="screenX screenY width height"
        onkeypress="gSitePermissionsManager.onWindowKeyPress(event);">

  <script src="chrome://browser/content/preferences/sitePermissions.js"/>

  <stringbundle id="bundlePreferences"
                src="chrome://browser/locale/preferences/preferences.properties"/>

  <keyset>
    <key key="&windowClose.key;" modifiers="accel" oncommand="window.close();"/>
  </keyset>

  <vbox class="contentPane largeDialogContainer" flex="1">
    <description id="permissionsText" control="url"/>
    <separator class="thin"/>
    <hbox align="start">
      <textbox id="searchBox" flex="1" placeholder="&searchbox.placeholder;"
               type="search" oncommand="gSitePermissionsManager.buildPermissionsList();"/>
    </hbox>
    <separator class="thin"/>
    <richlistbox id="permissionsBox" selected="false"
                 hidecolumnpicker="true" flex="1"
                 onkeypress="gSitePermissionsManager.onPermissionKeyPress(event);"
                 onselect="gSitePermissionsManager.onPermissionSelect();">
      <listheader>
        <treecol id="siteCol" label="&treehead.sitename2.label;" flex="3"
                 persist="width" width="50"
                 onclick="gSitePermissionsManager.buildPermissionsList(event.target)"/>
        <treecol id="statusCol" label="&treehead.status.label;" flex="1"
                 persist="width" width="50" data-isCurrentSortCol="true"
                 onclick="gSitePermissionsManager.buildPermissionsList(event.target);"/>
      </listheader>
    </richlistbox>
  </vbox>
  <vbox>
    <hbox class="actionButtons" align="left" flex="1">
      <button id="removePermission" disabled="true"
              accesskey="&removepermission2.accesskey;"
              icon="remove" label="&removepermission2.label;"
              oncommand="gSitePermissionsManager.onPermissionDelete();"/>
      <button id="removeAllPermissions"
              icon="clear" label="&removeallpermissions2.label;"
              accesskey="&removeallpermissions2.accesskey;"
              oncommand="gSitePermissionsManager.onAllPermissionsDelete();"/>
    </hbox>
    <spacer flex="1"/>
    <checkbox id="permissionsDisableCheckbox"/>
    <description id="permissionsDisableDescription"/>
    <spacer flex="1"/>
    <hbox class="actionButtons" align="right" flex="1">
      <button oncommand="close();" icon="close" id="cancel"
              label="&button.cancel.label;" accesskey="&button.cancel.accesskey;" />
      <button id="btnApplyChanges" oncommand="gSitePermissionsManager.onApplyChanges();" icon="save"
              label="&button.ok.label;" accesskey="&button.ok.accesskey;"/>
    </hbox>
  </vbox>
</window>
PK
!<ӧR��9chrome/browser/content/browser/preferences/translation.js/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");

XPCOMUtils.defineLazyGetter(this, "gLangBundle", () =>
  Services.strings.createBundle("chrome://global/locale/languageNames.properties"));

const kPermissionType = "translate";
const kLanguagesPref = "browser.translation.neverForLanguages";

function Tree(aId, aData) {
  this._data = aData;
  this._tree = document.getElementById(aId);
  this._tree.view = this;
}

Tree.prototype = {
  get boxObject() {
    return this._tree.treeBoxObject;
  },
  get isEmpty() {
    return !this._data.length;
  },
  get hasSelection() {
    return this.selection.count > 0;
  },
  getSelectedItems() {
    let result = [];

    let rc = this.selection.getRangeCount();
    for (let i = 0; i < rc; ++i) {
      let min = {}, max = {};
      this.selection.getRangeAt(i, min, max);
      for (let j = min.value; j <= max.value; ++j)
        result.push(this._data[j]);
    }

    return result;
  },

  // nsITreeView implementation
  get rowCount() {
    return this._data.length;
  },
  getCellText(aRow, aColumn) {
    return this._data[aRow];
  },
  isSeparator(aIndex) {
    return false;
  },
  isSorted() {
    return false;
  },
  isContainer(aIndex) {
    return false;
  },
  setTree(aTree) {},
  getImageSrc(aRow, aColumn) {},
  getCellValue(aRow, aColumn) {},
  cycleHeader(column) {},
  getRowProperties(row) {
    return "";
  },
  getColumnProperties(column) {
    return "";
  },
  getCellProperties(row, column) {
    return "";
  },
  QueryInterface: XPCOMUtils.generateQI([Ci.nsITreeView])
};

function Lang(aCode) {
  this.langCode = aCode;
  this._label = gLangBundle.GetStringFromName(aCode);
}

Lang.prototype = {
  toString() {
    return this._label;
  }
};

var gTranslationExceptions = {
  onLoad() {
    if (this._siteTree) {
      // Re-using an open dialog, clear the old observers.
      this.uninit();
    }

    // Load site permissions into an array.
    this._sites = [];
    let enumerator = Services.perms.enumerator;
    while (enumerator.hasMoreElements()) {
      let perm = enumerator.getNext().QueryInterface(Ci.nsIPermission);

      if (perm.type == kPermissionType &&
          perm.capability == Services.perms.DENY_ACTION) {
        this._sites.push(perm.principal.origin);
      }
    }
    Services.obs.addObserver(this, "perm-changed");
    this._sites.sort();

    this._siteTree = new Tree("sitesTree", this._sites);
    this.onSiteSelected();

    this._langs = this.getLanguageExceptions();
    Services.prefs.addObserver(kLanguagesPref, this);
    this._langTree = new Tree("languagesTree", this._langs);
    this.onLanguageSelected();
  },

  // Get the list of languages we don't translate as an array.
  getLanguageExceptions() {
    let langs = Services.prefs.getCharPref(kLanguagesPref);
    if (!langs)
      return [];

    let result = langs.split(",").map(code => new Lang(code));
    result.sort();

    return result;
  },

  observe(aSubject, aTopic, aData) {
    if (aTopic == "perm-changed") {
      if (aData == "cleared") {
        if (!this._sites.length)
          return;
        let removed = this._sites.splice(0, this._sites.length);
        this._siteTree.boxObject.rowCountChanged(0, -removed.length);
      } else {
        let perm = aSubject.QueryInterface(Ci.nsIPermission);
        if (perm.type != kPermissionType)
          return;

        if (aData == "added") {
          if (perm.capability != Services.perms.DENY_ACTION)
            return;
          this._sites.push(perm.principal.origin);
          this._sites.sort();
          let boxObject = this._siteTree.boxObject;
          boxObject.rowCountChanged(0, 1);
          boxObject.invalidate();
        } else if (aData == "deleted") {
          let index = this._sites.indexOf(perm.principal.origin);
          if (index == -1)
            return;
          this._sites.splice(index, 1);
          this._siteTree.boxObject.rowCountChanged(index, -1);
          this.onSiteSelected();
          return;
        }
      }
      this.onSiteSelected();
    } else if (aTopic == "nsPref:changed") {
      this._langs = this.getLanguageExceptions();
      let change = this._langs.length - this._langTree.rowCount;
      this._langTree._data = this._langs;
      let boxObject = this._langTree.boxObject;
      if (change)
        boxObject.rowCountChanged(0, change);
      boxObject.invalidate();
      this.onLanguageSelected();
    }
  },

  _handleButtonDisabling(aTree, aIdPart) {
    let empty = aTree.isEmpty;
    document.getElementById("removeAll" + aIdPart + "s").disabled = empty;
    document.getElementById("remove" + aIdPart).disabled =
      empty || !aTree.hasSelection;
  },

  onLanguageSelected() {
    this._handleButtonDisabling(this._langTree, "Language");
  },

  onSiteSelected() {
    this._handleButtonDisabling(this._siteTree, "Site");
  },

  onLanguageDeleted() {
    let langs = Services.prefs.getCharPref(kLanguagesPref);
    if (!langs)
      return;

    let removed = this._langTree.getSelectedItems().map(l => l.langCode);

    langs = langs.split(",").filter(l => !removed.includes(l));
    Services.prefs.setCharPref(kLanguagesPref, langs.join(","));
  },

  onAllLanguagesDeleted() {
    Services.prefs.setCharPref(kLanguagesPref, "");
  },

  onSiteDeleted() {
    let removedSites = this._siteTree.getSelectedItems();
    for (let origin of removedSites) {
      let principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(origin);
      Services.perms.removeFromPrincipal(principal, kPermissionType);
    }
  },

  onAllSitesDeleted() {
    if (this._siteTree.isEmpty)
      return;

    let removedSites = this._sites.splice(0, this._sites.length);
    this._siteTree.boxObject.rowCountChanged(0, -removedSites.length);

    for (let origin of removedSites) {
      let principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(origin);
      Services.perms.removeFromPrincipal(principal, kPermissionType);
    }

    this.onSiteSelected();
  },

  onSiteKeyPress(aEvent) {
    if (aEvent.keyCode == KeyEvent.DOM_VK_DELETE)
      this.onSiteDeleted();
  },

  onLanguageKeyPress(aEvent) {
    if (aEvent.keyCode == KeyEvent.DOM_VK_DELETE)
      this.onLanguageDeleted();
  },

  onWindowKeyPress(aEvent) {
    if (aEvent.keyCode == KeyEvent.DOM_VK_ESCAPE)
      window.close();
  },

  uninit() {
    Services.obs.removeObserver(this, "perm-changed");
    Services.prefs.removeObserver(kLanguagesPref, this);
  }
};
PK
!<����:chrome/browser/content/browser/preferences/translation.xul<?xml version="1.0"?>

<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css" type="text/css"?>

<!DOCTYPE dialog SYSTEM "chrome://browser/locale/preferences/translation.dtd">

<window id="TranslationDialog" class="windowDialog"
        windowtype="Browser:TranslationExceptions"
        title="&window.title;"
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        style="width: &window.width;;"
        onload="gTranslationExceptions.onLoad();"
        onunload="gTranslationExceptions.uninit();"
        persist="screenX screenY width height"
        onkeypress="gTranslationExceptions.onWindowKeyPress(event);">

  <script src="chrome://browser/content/preferences/translation.js"/>

  <stringbundle id="bundlePreferences"
                src="chrome://browser/locale/preferences/preferences.properties"/>

  <keyset>
    <key key="&windowClose.key;" modifiers="accel" oncommand="window.close();"/>
  </keyset>

  <vbox class="largeDialogContainer">
    <vbox class="contentPane" flex="1">
      <label id="languagesLabel" control="permissionsTree">&noTranslationForLanguages2.label;</label>
      <separator class="thin"/>
      <tree id="languagesTree" flex="1" style="height: 12em;"
            hidecolumnpicker="true"
            onkeypress="gTranslationExceptions.onLanguageKeyPress(event)"
            onselect="gTranslationExceptions.onLanguageSelected();">
        <treecols>
          <treecol id="languageCol" label="&treehead.languageName.label;" flex="1"/>
        </treecols>
        <treechildren/>
      </tree>
    </vbox>
    <hbox align="end">
      <hbox class="actionButtons" flex="1">
        <button id="removeLanguage" disabled="true"
                accesskey="&removeLanguage.accesskey;"
                icon="remove" label="&removeLanguage.label;"
                oncommand="gTranslationExceptions.onLanguageDeleted();"/>
        <button id="removeAllLanguages"
                icon="clear" label="&removeAllLanguages.label;"
                accesskey="&removeAllLanguages.accesskey;"
                oncommand="gTranslationExceptions.onAllLanguagesDeleted();"/>
        <spacer flex="1"/>
      </hbox>
    </hbox>
    <separator/>
    <vbox class="contentPane" flex="1">
      <label id="languagesLabel" control="permissionsTree">&noTranslationForSites2.label;</label>
      <separator class="thin"/>
      <tree id="sitesTree" flex="1" style="height: 12em;"
            hidecolumnpicker="true"
            onkeypress="gTranslationExceptions.onSiteKeyPress(event)"
            onselect="gTranslationExceptions.onSiteSelected();">
        <treecols>
          <treecol id="siteCol" label="&treehead.siteName2.label;" flex="1"/>
        </treecols>
        <treechildren/>
      </tree>
    </vbox>
  </vbox>
  <hbox align="end">
    <hbox class="actionButtons" flex="1">
      <button id="removeSite" disabled="true"
              accesskey="&removeSite.accesskey;"
              icon="remove" label="&removeSite.label;"
              oncommand="gTranslationExceptions.onSiteDeleted();"/>
      <button id="removeAllSites"
              icon="clear" label="&removeAllSites.label;"
              accesskey="&removeAllSites.accesskey;"
              oncommand="gTranslationExceptions.onAllSitesDeleted();"/>
      <spacer flex="1"/>
      <button oncommand="close();" icon="close"
              label="&button.close.label;" accesskey="&button.close.accesskey;"/>
    </hbox>
  </hbox>
</window>
PK
!<� Q��(chrome/browser/content/browser/robot.ico�PNG


IHDR�aacTL�m�fcTL.����޺�IDAT8����
� D9F��\S����\H
�.[G.�`r�5"|�FEϋd皇�a�����u��B�9gSC�V�%@Dp�D�ڵ ��p��q��8�Z
H>��S���H>`��,Pf��L�bh+
��#b���}׀�-h�L�kԢQ&����ED}���^�e���f�r|Wz�
P�ԹΗfcTL��gܖ*fdAT8�c`XAss�b�pjީ��Y61ڹ`�r��X_��fcTL�
�fdAT8�c`�`��'�fcTL��;}@fdAT8�c`�`��b�p���0c�j�S��n������j�.F; ���Q0
`�4+~��pfcTL�
���fdAT8�c`�`�3�7�fcTL	��ޟ�*fdAT
8�c`XAss�b�pjީ��Y61ڹ`�r��A��zfcTL�
HLYfdAT8�c`�`�ڳ:ffcTL
��>#@fdAT8�c`�`��b�p���0c�j�S��n������j�.F; ���Q0
`�4+&�
fcTL�
��fdAT8�c`�`�)8fcTL��Z�*fdAT8�c`XAss�b�pjީ��Y61ڹ`�r��k��fcTL���3fdAT8�c`�`��P�fcTL��I�I@fdAT8�c`�`��b�p���0c�j�S��n������j�.F; ���Q0
`�4+Ϝ�fcTL��(�fdAT8�c`�`� ��fcTL���*fdAT8�c`XAss�b�pjީ��Y61ڹ`�r��r2VlfcTL�:�fdAT8�c`�`���lfcTL���o@fdAT8�c`�`��b�p���0c�j�S��n������j�.F; ���Q0
`�4+�w�fcTL�fk�fdAT 8�c`�`��\fcTL!���*fdAT"8�c`XAss�b�pjީ��Y61ڹ`�r��?2��IEND�B`�PK
!<]����+chrome/browser/content/browser/safeMode.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

#resetProfileFooter {
  font-weight: bold;
}

PK
!<eT.�9	9	*chrome/browser/content/browser/safeMode.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

ChromeUtils.import("resource://gre/modules/Services.jsm");

const appStartup = Services.startup;

ChromeUtils.import("resource://gre/modules/ResetProfile.jsm");

var defaultToReset = false;

function restartApp() {
  appStartup.quit(appStartup.eForceQuit | appStartup.eRestart);
}

function resetProfile() {
  // Set the reset profile environment variable.
  let env = Cc["@mozilla.org/process/environment;1"]
              .getService(Ci.nsIEnvironment);
  env.set("MOZ_RESET_PROFILE_RESTART", "1");
}

function showResetDialog() {
  // Prompt the user to confirm the reset.
  let retVals = {
    reset: false,
  };
  window.openDialog("chrome://global/content/resetProfile.xul", null,
                    "chrome,modal,centerscreen,titlebar,dialog=yes", retVals);
  if (!retVals.reset)
    return;
  resetProfile();
  restartApp();
}

function onDefaultButton() {
  if (defaultToReset) {
    // Restart to reset the profile.
    resetProfile();
    restartApp();
    // Return false to prevent starting into safe mode while restarting.
    return false;
  }
  // Continue in safe mode. No restart needed.
  return true;
}

function onCancel() {
  appStartup.quit(appStartup.eForceQuit);
}

function onExtra1() {
  if (defaultToReset) {
    // Continue in safe mode
    window.close();
    return true;
  }
  // The reset dialog will handle starting the reset process if the user confirms.
  showResetDialog();
  return false;
}

function onLoad() {
  if (appStartup.automaticSafeModeNecessary) {
    document.getElementById("autoSafeMode").hidden = false;
    document.getElementById("safeMode").hidden = true;
    if (ResetProfile.resetSupported()) {
      document.getElementById("resetProfile").hidden = false;
    } else {
      // Hide the reset button is it's not supported.
      document.documentElement.getButton("extra1").hidden = true;
    }
  } else if (!ResetProfile.resetSupported()) {
    // Hide the reset button and text if it's not supported.
    document.documentElement.getButton("extra1").hidden = true;
    document.getElementById("resetProfileInstead").hidden = true;
  }
}
PK
!<�&_��+chrome/browser/content/browser/safeMode.xul<?xml version="1.0"?>

<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 prefwindow [
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
%brandDTD;
<!ENTITY % safeModeDTD SYSTEM "chrome://browser/locale/safeMode.dtd" >
%safeModeDTD;
<!ENTITY % resetProfileDTD SYSTEM "chrome://global/locale/resetProfile.dtd" >
%resetProfileDTD;
]>

<?xml-stylesheet href="chrome://global/skin/"?>
<?xml-stylesheet href="chrome://browser/content/safeMode.css"?>

<dialog id="safeModeDialog"
            xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
            title="&safeModeDialog.title;"
            buttons="accept,extra1"
            buttonlabelaccept="&startSafeMode.label;"
            buttonlabelextra1="&refreshProfile.label;"
            maxwidth="&window.maxWidth;"
            ondialogaccept="return onDefaultButton()"
            ondialogcancel="onCancel();"
            ondialogextra1="return onExtra1()"
            onload="onLoad()">

  <script type="application/javascript" src="chrome://browser/content/safeMode.js"/>

  <vbox id="autoSafeMode" hidden="true">
    <description>&autoSafeModeDescription3.label;</description>
  </vbox>

  <vbox id="safeMode">
    <label>&safeModeDescription3.label;</label>
    <separator class="thin"/>
    <label>&safeModeDescription4.label;</label>
    <separator class="thin"/>
    <label id="resetProfileInstead">&refreshProfileInstead.label;</label>
  </vbox>

  <vbox id="resetProfile" hidden="true">
    <label id="resetProfileInstead">&refreshProfileInstead.label;</label>
  </vbox>

  <separator class="thin"/>
</dialog>
PK
!<�E��+chrome/browser/content/browser/sanitize.xul<?xml version="1.0"?>

<!-- -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- -->
<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<?xml-stylesheet href="chrome://global/skin/"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css"?>
<?xml-stylesheet href="chrome://browser/skin/sanitizeDialog.css"?>


<?xml-stylesheet href="chrome://browser/content/sanitizeDialog.css"?>

<!DOCTYPE dialog [
  <!ENTITY % preferencesDTD SYSTEM "chrome://global/locale/preferences.dtd">
  <!ENTITY % globalKeysDTD SYSTEM "chrome://global/locale/globalKeys.dtd">
  <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
  <!ENTITY % sanitizeDTD SYSTEM "chrome://browser/locale/sanitize.dtd">
  %preferencesDTD;
  %globalKeysDTD;
  %brandDTD;
  %sanitizeDTD;
]>

<dialog id="SanitizeDialog" class="prefwindow" type="child"
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        buttons="accept,cancel"
        persist="lastSelected screenX screenY"
        closebuttonlabel="&preferencesCloseButton.label;"
        closebuttonaccesskey="&preferencesCloseButton.accesskey;"
        role="dialog"
        title="&sanitizeDialog2.title;"
        noneverythingtitle="&sanitizeDialog2.title;"
        style="width: &sanitizeDialog2.width;;"
        onload="gSanitizePromptDialog.init();"
        ondialogaccept="return gSanitizePromptDialog.sanitize();">

  <vbox id="SanitizeDialogPane" class="prefpane">
    <stringbundle id="bundleBrowser"
                  src="chrome://browser/locale/browser.properties"/>

    <script type="application/javascript"
            src="chrome://global/content/preferencesBindings.js"/>
    <script type="application/javascript"
            src="chrome://browser/content/sanitizeDialog.js"/>

    <hbox id="SanitizeDurationBox" align="center">
      <label value="&clearTimeDuration.label;"
             accesskey="&clearTimeDuration.accesskey;"
             control="sanitizeDurationChoice"
             id="sanitizeDurationLabel"/>
      <menulist id="sanitizeDurationChoice"
                preference="privacy.sanitize.timeSpan"
                onselect="gSanitizePromptDialog.selectByTimespan();"
                flex="1">
        <menupopup id="sanitizeDurationPopup">
          <menuitem label="&clearTimeDuration.lastHour;" value="1"/>
          <menuitem label="&clearTimeDuration.last2Hours;" value="2"/>
          <menuitem label="&clearTimeDuration.last4Hours;" value="3"/>
          <menuitem label="&clearTimeDuration.today;" value="4"/>
          <menuseparator/>
          <menuitem label="&clearTimeDuration.everything;" value="0"/>
        </menupopup>
      </menulist>
      <label id="sanitizeDurationSuffixLabel"
             value="&clearTimeDuration.suffix;"/>
    </hbox>

    <separator class="thin"/>


      <vbox id="sanitizeEverythingWarningBox">
        <spacer flex="1"/>
        <hbox align="center">
          <image id="sanitizeEverythingWarningIcon"/>
          <vbox id="sanitizeEverythingWarningDescBox" flex="1">
            <description id="sanitizeEverythingWarning"/>
            <description id="sanitizeEverythingUndoWarning">&sanitizeEverythingUndoWarning;</description>
          </vbox>
        </hbox>
        <spacer flex="1"/>
      </vbox>


    <separator class="thin"/>

    <hbox id="detailsExpanderWrapper" align="center">
      <button type="image"
              id="detailsExpander"
              class="expander-down"
              persist="class"
              oncommand="gSanitizePromptDialog.toggleItemList();"/>
      <label id="detailsExpanderLabel"
             value="&detailsProgressiveDisclosure.label;"
             accesskey="&detailsProgressiveDisclosure.accesskey;"
             control="detailsExpander"/>
    </hbox>
    <listbox id="itemList" rows="7" collapsed="true" persist="collapsed">
      <listitem label="&itemHistoryAndDownloads.label;"
                type="checkbox"
                accesskey="&itemHistoryAndDownloads.accesskey;"
                preference="privacy.cpd.history"
                onsyncfrompreference="return gSanitizePromptDialog.onReadGeneric();"/>
      <listitem label="&itemFormSearchHistory.label;"
                type="checkbox"
                accesskey="&itemFormSearchHistory.accesskey;"
                preference="privacy.cpd.formdata"
                onsyncfrompreference="return gSanitizePromptDialog.onReadGeneric();"/>
      <listitem label="&itemCookies.label;"
                type="checkbox"
                accesskey="&itemCookies.accesskey;"
                preference="privacy.cpd.cookies"
                onsyncfrompreference="return gSanitizePromptDialog.onReadGeneric();"/>
      <listitem label="&itemCache.label;"
                type="checkbox"
                accesskey="&itemCache.accesskey;"
                preference="privacy.cpd.cache"
                onsyncfrompreference="return gSanitizePromptDialog.onReadGeneric();"/>
      <listitem label="&itemActiveLogins.label;"
                type="checkbox"
                accesskey="&itemActiveLogins.accesskey;"
                preference="privacy.cpd.sessions"
                onsyncfrompreference="return gSanitizePromptDialog.onReadGeneric();"/>
      <listitem label="&itemOfflineApps.label;"
                type="checkbox"
                accesskey="&itemOfflineApps.accesskey;"
                preference="privacy.cpd.offlineApps"
                onsyncfrompreference="return gSanitizePromptDialog.onReadGeneric();"/>
      <listitem label="&itemSitePreferences.label;"
                type="checkbox"
                accesskey="&itemSitePreferences.accesskey;"
                preference="privacy.cpd.siteSettings"
                noduration="true"
                onsyncfrompreference="return gSanitizePromptDialog.onReadGeneric();"/>
    </listbox>

  </vbox>
</dialog>
PK
!<��P<<1chrome/browser/content/browser/sanitizeDialog.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* Places tree */

#placesTreechildren {
  -moz-user-focus: normal;
}

#placesTreechildren::-moz-tree-cell(grippyRow),
#placesTreechildren::-moz-tree-cell-text(grippyRow),
#placesTreechildren::-moz-tree-image(grippyRow) {
  cursor: grab;
}


/* Sanitize everything warnings */

#sanitizeEverythingWarning,
#sanitizeEverythingUndoWarning {
  white-space: pre-wrap;
}
PK
!<���%  0chrome/browser/content/browser/sanitizeDialog.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* import-globals-from ../../../toolkit/content/preferencesBindings.js */

var {Sanitizer} = ChromeUtils.import("resource:///modules/Sanitizer.jsm", {});

Preferences.addAll([
  { id: "privacy.cpd.history", type: "bool" },
  { id: "privacy.cpd.formdata", type: "bool" },
  { id: "privacy.cpd.downloads", type: "bool", disabled: true },
  { id: "privacy.cpd.cookies", type: "bool" },
  { id: "privacy.cpd.cache", type: "bool" },
  { id: "privacy.cpd.sessions", type: "bool" },
  { id: "privacy.cpd.offlineApps", type: "bool" },
  { id: "privacy.cpd.siteSettings", type: "bool" },
  { id: "privacy.sanitize.timeSpan", type: "int" },
]);

var gSanitizePromptDialog = {

  get bundleBrowser() {
    if (!this._bundleBrowser)
      this._bundleBrowser = document.getElementById("bundleBrowser");
    return this._bundleBrowser;
  },

  get selectedTimespan() {
    var durList = document.getElementById("sanitizeDurationChoice");
    return parseInt(durList.value);
  },

  get warningBox() {
    return document.getElementById("sanitizeEverythingWarningBox");
  },

  init() {
    // This is used by selectByTimespan() to determine if the window has loaded.
    this._inited = true;

    document.documentElement.getButton("accept").label =
      this.bundleBrowser.getString("sanitizeButtonOK");

    if (this.selectedTimespan === Sanitizer.TIMESPAN_EVERYTHING) {
      this.prepareWarning();
      this.warningBox.hidden = false;
      document.title =
        this.bundleBrowser.getString("sanitizeDialog2.everything.title");
    } else
      this.warningBox.hidden = true;
  },

  selectByTimespan() {
    // This method is the onselect handler for the duration dropdown.  As a
    // result it's called a couple of times before onload calls init().
    if (!this._inited)
      return;

    var warningBox = this.warningBox;

    // If clearing everything
    if (this.selectedTimespan === Sanitizer.TIMESPAN_EVERYTHING) {
      this.prepareWarning();
      if (warningBox.hidden) {
        warningBox.hidden = false;
        window.resizeBy(0, warningBox.boxObject.height);
      }
      window.document.title =
        this.bundleBrowser.getString("sanitizeDialog2.everything.title");
      return;
    }

    // If clearing a specific time range
    if (!warningBox.hidden) {
      window.resizeBy(0, -warningBox.boxObject.height);
      warningBox.hidden = true;
    }
    window.document.title =
      window.document.documentElement.getAttribute("noneverythingtitle");
  },

  sanitize() {
    // Update pref values before handing off to the sanitizer (bug 453440)
    this.updatePrefs();

    // As the sanitize is async, we disable the buttons, update the label on
    // the 'accept' button to indicate things are happening and return false -
    // once the async operation completes (either with or without errors)
    // we close the window.
    let docElt = document.documentElement;
    let acceptButton = docElt.getButton("accept");
    acceptButton.disabled = true;
    acceptButton.setAttribute("label",
                              this.bundleBrowser.getString("sanitizeButtonClearing"));
    docElt.getButton("cancel").disabled = true;

    try {
      let range = Sanitizer.getClearRange(this.selectedTimespan);
      let options = {
        ignoreTimespan: !range,
        range,
      };
      Sanitizer.sanitize(null, options)
        .catch(Cu.reportError)
        .then(() => window.close())
        .catch(Cu.reportError);
      return false;
    } catch (er) {
      Cu.reportError("Exception during sanitize: " + er);
      return true; // We *do* want to close immediately on error.
    }
  },

  /**
   * If the panel that displays a warning when the duration is "Everything" is
   * not set up, sets it up.  Otherwise does nothing.
   *
   * @param aDontShowItemList Whether only the warning message should be updated.
   *                          True means the item list visibility status should not
   *                          be changed.
   */
  prepareWarning(aDontShowItemList) {
    // If the date and time-aware locale warning string is ever used again,
    // initialize it here.  Currently we use the no-visits warning string,
    // which does not include date and time.  See bug 480169 comment 48.

    var warningStringID;
    if (this.hasNonSelectedItems()) {
      warningStringID = "sanitizeSelectedWarning";
      if (!aDontShowItemList)
        this.showItemList();
    } else {
      warningStringID = "sanitizeEverythingWarning2";
    }

    var warningDesc = document.getElementById("sanitizeEverythingWarning");
    warningDesc.textContent =
      this.bundleBrowser.getString(warningStringID);
  },

  /**
   * Return the boolean prefs that enable/disable clearing of various kinds
   * of history.  The only pref this excludes is privacy.sanitize.timeSpan.
   */
  _getItemPrefs() {
    return Preferences.getAll().filter(p => p.id !== "privacy.sanitize.timeSpan");
  },

  /**
   * Called when the value of a preference element is synced from the actual
   * pref.  Enables or disables the OK button appropriately.
   */
  onReadGeneric() {
    // Find any other pref that's checked and enabled (except for
    // privacy.sanitize.timeSpan, which doesn't affect the button's status).
    var found = this._getItemPrefs().some(pref => !!pref.value && !pref.disabled);

    try {
      document.documentElement.getButton("accept").disabled = !found;
    } catch (e) { }

    // Update the warning prompt if needed
    this.prepareWarning(true);

    return undefined;
  },

  /**
   * Sanitizer.prototype.sanitize() requires the prefs to be up-to-date.
   * Because the type of this prefwindow is "child" -- and that's needed because
   * without it the dialog has no OK and Cancel buttons -- the prefs are not
   * updated on dialogaccept.  We must therefore manually set the prefs
   * from their corresponding preference elements.
   */
  updatePrefs() {
    Services.prefs.setIntPref(Sanitizer.PREF_TIMESPAN, this.selectedTimespan);

    // Keep the pref for the download history in sync with the history pref.
    Preferences.get("privacy.cpd.downloads").value =
      Preferences.get("privacy.cpd.history").value;

    // Now manually set the prefs from their corresponding preference
    // elements.
    var prefs = this._getItemPrefs();
    for (let i = 0; i < prefs.length; ++i) {
      var p = prefs[i];
      Services.prefs.setBoolPref(p.name, p.value);
    }
  },

  /**
   * Check if all of the history items have been selected like the default status.
   */
  hasNonSelectedItems() {
    let checkboxes = document.querySelectorAll("#itemList > [preference]");
    for (let i = 0; i < checkboxes.length; ++i) {
      let pref = Preferences.get(checkboxes[i].getAttribute("preference"));
      if (!pref.value)
        return true;
    }
    return false;
  },

  /**
   * Show the history items list.
   */
  showItemList() {
    var itemList = document.getElementById("itemList");
    var expanderButton = document.getElementById("detailsExpander");

    if (itemList.collapsed) {
      expanderButton.className = "expander-up";
      itemList.setAttribute("collapsed", "false");
      if (document.documentElement.boxObject.height)
        window.resizeBy(0, itemList.boxObject.height);
    }
  },

  /**
   * Hide the history items list.
   */
  hideItemList() {
    var itemList = document.getElementById("itemList");
    var expanderButton = document.getElementById("detailsExpander");

    if (!itemList.collapsed) {
      expanderButton.className = "expander-down";
      window.resizeBy(0, -itemList.boxObject.height);
      itemList.setAttribute("collapsed", "true");
    }
  },

  /**
   * Called by the item list expander button to toggle the list's visibility.
   */
  toggleItemList() {
    var itemList = document.getElementById("itemList");

    if (itemList.collapsed)
      this.showItemList();
    else
      this.hideItemList();
  }


};
PK
!<��dtoFoF5chrome/browser/content/browser/schemas/bookmarks.json// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

[
  {
    "namespace": "manifest",
    "types": [
      {
        "$extend": "OptionalPermission",
        "choices": [{
          "type": "string",
          "enum": [
            "bookmarks"
          ]
        }]
      }
    ]
  },
  {
    "namespace": "bookmarks",
    "description": "Use the <code>browser.bookmarks</code> API to create, organize, and otherwise manipulate bookmarks. Also see $(topic:override)[Override Pages], which you can use to create a custom Bookmark Manager page.",
    "permissions": ["bookmarks"],
    "types": [
      {
        "id": "BookmarkTreeNodeUnmodifiable",
        "type": "string",
        "enum": ["managed"],
        "description": "Indicates the reason why this node is unmodifiable. The <var>managed</var> value indicates that this node was configured by the system administrator or by the custodian of a supervised user. Omitted if the node can be modified by the user and the extension (default)."
      },
      {
        "id": "BookmarkTreeNodeType",
        "type": "string",
        "enum": ["bookmark", "folder", "separator"],
        "description": "Indicates the type of a BookmarkTreeNode, which can be one of bookmark, folder or separator."
      },
      {
        "id": "BookmarkTreeNode",
        "type": "object",
        "description": "A node (either a bookmark or a folder) in the bookmark tree.  Child nodes are ordered within their parent folder.",
        "properties": {
          "id": {
            "type": "string",
            "description": "The unique identifier for the node. IDs are unique within the current profile, and they remain valid even after the browser is restarted."
          },
          "parentId": {
            "type": "string",
            "optional": true,
            "description": "The <code>id</code> of the parent folder.  Omitted for the root node."
          },
          "index": {
            "type": "integer",
            "optional": true,
            "description": "The 0-based position of this node within its parent folder."
          },
          "url": {
            "type": "string",
            "optional": true,
            "description": "The URL navigated to when a user clicks the bookmark. Omitted for folders."
          },
          "title": {
            "type": "string",
            "description": "The text displayed for the node."
          },
          "dateAdded": {
            "type": "number",
            "optional": true,
            "description": "When this node was created, in milliseconds since the epoch (<code>new Date(dateAdded)</code>)."
          },
          "dateGroupModified": {
            "type": "number",
            "optional": true,
            "description": "When the contents of this folder last changed, in milliseconds since the epoch."
          },
          "unmodifiable": {
            "$ref": "BookmarkTreeNodeUnmodifiable",
            "optional": true,
            "description": "Indicates the reason why this node is unmodifiable. The <var>managed</var> value indicates that this node was configured by the system administrator or by the custodian of a supervised user. Omitted if the node can be modified by the user and the extension (default)."
          },
          "type": {
            "$ref": "BookmarkTreeNodeType",
            "optional": true,
            "description": "Indicates the type of the BookmarkTreeNode, which can be one of bookmark, folder or separator."
          },
          "children": {
            "type": "array",
            "optional": true,
            "items": { "$ref": "BookmarkTreeNode" },
            "description": "An ordered list of children of this node."
          }
        }
      },
      {
        "id": "CreateDetails",
        "description": "Object passed to the create() function.",
        "type": "object",
        "properties": {
          "parentId": {
            "type": "string",
            "optional": true,
            "description": "Defaults to the Other Bookmarks folder."
          },
          "index": {
            "type": "integer",
            "minimum": 0,
            "optional": true
          },
          "title": {
            "type": "string",
            "optional": true
          },
          "url": {
            "type": "string",
            "optional": true
          },
          "type": {
            "$ref": "BookmarkTreeNodeType",
            "optional": true,
            "description": "Indicates the type of BookmarkTreeNode to create, which can be one of bookmark, folder or separator."
          }
        }
      }
    ],
    "functions": [
      {
        "name": "get",
        "type": "function",
        "description": "Retrieves the specified BookmarkTreeNode(s).",
        "async": "callback",
        "parameters": [
          {
            "name": "idOrIdList",
            "description": "A single string-valued id, or an array of string-valued ids",
            "choices": [
              {
                "type": "string"
              },
              {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "minItems": 1
              }
            ]
          },
          {
            "type": "function",
            "name": "callback",
            "parameters": [
              {
                "name": "results",
                "type": "array",
                "items": { "$ref": "BookmarkTreeNode" }
              }
            ]
          }
        ]
      },
      {
        "name": "getChildren",
        "type": "function",
        "description": "Retrieves the children of the specified BookmarkTreeNode id.",
        "async": "callback",
        "parameters": [
          {
            "type": "string",
            "name": "id"
          },
          {
            "type": "function",
            "name": "callback",
            "parameters": [
              {
                "name": "results",
                "type": "array",
                "items": { "$ref": "BookmarkTreeNode"}
              }
            ]
          }
        ]
      },
      {
        "name": "getRecent",
        "type": "function",
        "description": "Retrieves the recently added bookmarks.",
        "async": "callback",
        "parameters": [
          {
            "type": "integer",
            "minimum": 1,
            "name": "numberOfItems",
            "description": "The maximum number of items to return."
          },
          {
            "type": "function",
            "name": "callback",
            "parameters": [
              {
                "name": "results",
                "type": "array",
                "items": { "$ref": "BookmarkTreeNode" }
              }
            ]
          }
        ]
      },
      {
        "name": "getTree",
        "type": "function",
        "description": "Retrieves the entire Bookmarks hierarchy.",
        "async": "callback",
        "parameters": [
          {
            "type": "function",
            "name": "callback",
            "parameters": [
              {
                "name": "results",
                "type": "array",
                "items": { "$ref": "BookmarkTreeNode" }
              }
            ]
          }
        ]
      },
      {
        "name": "getSubTree",
        "type": "function",
        "description": "Retrieves part of the Bookmarks hierarchy, starting at the specified node.",
        "async": "callback",
        "parameters": [
          {
            "type": "string",
            "name": "id",
            "description": "The ID of the root of the subtree to retrieve."
          },
          {
            "type": "function",
            "name": "callback",
            "parameters": [
              {
                "name": "results",
                "type": "array",
                "items": { "$ref": "BookmarkTreeNode" }
              }
            ]
          }
        ]
      },
      {
        "name": "search",
        "type": "function",
        "description": "Searches for BookmarkTreeNodes matching the given query. Queries specified with an object produce BookmarkTreeNodes matching all specified properties.",
        "async": "callback",
        "parameters": [
          {
            "name": "query",
            "description": "Either a string of words and quoted phrases that are matched against bookmark URLs and titles, or an object. If an object, the properties <code>query</code>, <code>url</code>, and <code>title</code> may be specified and bookmarks matching all specified properties will be produced.",
            "choices": [
              {
                "type": "string",
                "description": "A string of words and quoted phrases that are matched against bookmark URLs and titles."
              },
              {
                "type": "object",
                "description": "An object specifying properties and values to match when searching. Produces bookmarks matching all properties.",
                "properties": {
                  "query": {
                    "type": "string",
                    "optional": true,
                    "description": "A string of words and quoted phrases that are matched against bookmark URLs and titles."
                  },
                  "url": {
                    "type": "string",
                    "format": "url",
                    "optional": true,
                    "description": "The URL of the bookmark; matches verbatim. Note that folders have no URL."
                  },
                  "title": {
                    "type": "string",
                    "optional": true,
                    "description": "The title of the bookmark; matches verbatim."
                  }
                }
              }
            ]
          },
          {
            "type": "function",
            "name": "callback",
            "parameters": [
              {
                "name": "results",
                "type": "array",
                "items": { "$ref": "BookmarkTreeNode" }
              }
            ]
          }
        ]
      },
      {
        "name": "create",
        "type": "function",
        "description": "Creates a bookmark or folder under the specified parentId.  If url is NULL or missing, it will be a folder.",
        "async": "callback",
        "parameters": [
          {
            "$ref": "CreateDetails",
            "name": "bookmark"
          },
          {
            "type": "function",
            "name": "callback",
            "optional": true,
            "parameters": [
              {
                "name": "result",
                "$ref": "BookmarkTreeNode"
              }
            ]
          }
        ]
      },
      {
        "name": "move",
        "type": "function",
        "description": "Moves the specified BookmarkTreeNode to the provided location.",
        "async": "callback",
        "parameters": [
          {
            "type": "string",
            "name": "id"
          },
          {
            "type": "object",
            "name": "destination",
            "properties": {
              "parentId": {
                "type": "string",
                "optional": true
              },
              "index": {
                "type": "integer",
                "minimum": 0,
                "optional": true
              }
            }
          },
          {
            "type": "function",
            "name": "callback",
            "optional": true,
            "parameters": [
              {
                "name": "result",
                "$ref": "BookmarkTreeNode"
              }
            ]
          }
        ]
      },
      {
        "name": "update",
        "type": "function",
        "description": "Updates the properties of a bookmark or folder. Specify only the properties that you want to change; unspecified properties will be left unchanged.  <b>Note:</b> Currently, only 'title' and 'url' are supported.",
        "async": "callback",
        "parameters": [
          {
            "type": "string",
            "name": "id"
          },
          {
            "type": "object",
            "name": "changes",
            "properties": {
              "title": {
                "type": "string",
                "optional": true
              },
              "url": {
                "type": "string",
                "optional": true
              }
            }
          },
          {
            "type": "function",
            "name": "callback",
            "optional": true,
            "parameters": [
              {
                "name": "result",
                "$ref": "BookmarkTreeNode"
              }
            ]
          }
        ]
      },
      {
        "name": "remove",
        "type": "function",
        "description": "Removes a bookmark or an empty bookmark folder.",
        "async": "callback",
        "parameters": [
          {
            "type": "string",
            "name": "id"
          },
          {
            "type": "function",
            "name": "callback",
            "optional": true,
            "parameters": []
          }
        ]
      },
      {
        "name": "removeTree",
        "type": "function",
        "description": "Recursively removes a bookmark folder.",
        "async": "callback",
        "parameters": [
          {
            "type": "string",
            "name": "id"
          },
          {
            "type": "function",
            "name": "callback",
            "optional": true,
            "parameters": []
          }
        ]
      },
      {
        "name": "import",
        "unsupported": true,
        "type": "function",
        "description": "Imports bookmarks from an html bookmark file",
        "async": "callback",
        "parameters": [
          {
            "type": "function",
            "name": "callback",
            "optional": true,
            "parameters": []
          }
        ]
      },
      {
        "name": "export",
        "unsupported": true,
        "type": "function",
        "description": "Exports bookmarks to an html bookmark file",
        "async": "callback",
        "parameters": [
          {
            "type": "function",
            "name": "callback",
            "optional": true,
            "parameters": []
          }
        ]
      }
    ],
    "events": [
      {
        "name": "onCreated",
        "type": "function",
        "description": "Fired when a bookmark or folder is created.",
        "parameters": [
          {
            "type": "string",
            "name": "id"
          },
          {
            "$ref": "BookmarkTreeNode",
            "name": "bookmark"
          }
        ]
      },
      {
        "name": "onRemoved",
        "type": "function",
        "description": "Fired when a bookmark or folder is removed.  When a folder is removed recursively, a single notification is fired for the folder, and none for its contents.",
        "parameters": [
          {
            "type": "string",
            "name": "id"
          },
          {
            "type": "object",
            "name": "removeInfo",
            "properties": {
              "parentId": { "type": "string" },
              "index": { "type": "integer" },
              "node": { "$ref": "BookmarkTreeNode" }
            }
          }
        ]
      },
      {
        "name": "onChanged",
        "type": "function",
        "description": "Fired when a bookmark or folder changes.  <b>Note:</b> Currently, only title and url changes trigger this.",
        "parameters": [
          {
            "type": "string",
            "name": "id"
          },
          {
            "type": "object",
            "name": "changeInfo",
            "properties": {
              "title": { "type": "string" },
              "url": {
                "type": "string",
                "optional": true
              }
            }
          }
        ]
      },
      {
        "name": "onMoved",
        "type": "function",
        "description": "Fired when a bookmark or folder is moved to a different parent folder.",
        "parameters": [
          {
            "type": "string",
            "name": "id"
          },
          {
            "type": "object",
            "name": "moveInfo",
            "properties": {
              "parentId": { "type": "string" },
              "index": { "type": "integer" },
              "oldParentId": { "type": "string" },
              "oldIndex": { "type": "integer" }
            }
          }
        ]
      },
      {
        "name": "onChildrenReordered",
        "unsupported": true,
        "type": "function",
        "description": "Fired when the children of a folder have changed their order due to the order being sorted in the UI.  This is not called as a result of a move().",
        "parameters": [
          {
            "type": "string",
            "name": "id"
          },
          {
            "type": "object",
            "name": "reorderInfo",
            "properties": {
              "childIds": {
                "type": "array",
                "items": { "type": "string" }
              }
            }
          }
        ]
      },
      {
        "name": "onImportBegan",
        "unsupported": true,
        "type": "function",
        "description": "Fired when a bookmark import session is begun.  Expensive observers should ignore onCreated updates until onImportEnded is fired.  Observers should still handle other notifications immediately.",
        "parameters": []
      },
      {
        "name": "onImportEnded",
        "unsupported": true,
        "type": "function",
        "description": "Fired when a bookmark import session is ended.",
        "parameters": []
      }
    ]
  }
]
PK
!<u���==:chrome/browser/content/browser/schemas/browser_action.json// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

[
  {
    "namespace": "manifest",
    "types": [
      {
        "$extend": "WebExtensionManifest",
        "properties": {
          "browser_action": {
            "type": "object",
            "additionalProperties": { "$ref": "UnrecognizedProperty" },
            "properties": {
              "default_title": {
                "type": "string",
                "optional": true,
                "preprocess": "localize"
              },
              "default_icon": {
                "$ref": "IconPath",
                "optional": true
              },
              "theme_icons": {
                "type": "array",
                "optional": true,
                "minItems": 1,
                "items": { "$ref": "ThemeIcons" },
                "description": "Specifies icons to use for dark and light themes"
              },
              "default_popup": {
                "type": "string",
                "format": "relativeUrl",
                "optional": true,
                "preprocess": "localize"
              },
              "browser_style": {
                "type": "boolean",
                "optional": true
              },
              "default_area": {
                "description": "Defines the location the browserAction will appear by default.  The default location is navbar.",
                "type": "string",
                "enum": ["navbar", "menupanel", "tabstrip", "personaltoolbar"],
                "optional": true
              }
            },
            "optional": true
          }
        }
      }
    ]
  },
  {
    "namespace": "browserAction",
    "description": "Use browser actions to put icons in the main browser toolbar, to the right of the address bar. In addition to its icon, a browser action can also have a tooltip, a badge, and a popup.",
    "permissions": ["manifest:browser_action"],
    "types": [
      {
        "id": "ColorArray",
        "type": "array",
        "items": {
          "type": "integer",
          "minimum": 0,
          "maximum": 255
        },
        "minItems": 4,
        "maxItems": 4
      },
      {
        "id": "ImageDataType",
        "type": "object",
        "isInstanceOf": "ImageData",
        "additionalProperties": { "type": "any" },
        "postprocess": "convertImageDataToURL",
        "description": "Pixel data for an image. Must be an ImageData object (for example, from a <code>canvas</code> element)."
      }
    ],
    "functions": [
      {
        "name": "setTitle",
        "type": "function",
        "description": "Sets the title of the browser action. This shows up in the tooltip.",
        "async": "callback",
        "parameters": [
          {
            "name": "details",
            "type": "object",
            "properties": {
              "title": {
                "choices": [
                  {"type": "string"},
                  {"type": "null"}
                ],
                "description": "The string the browser action should display when moused over."
              },
              "tabId": {
                "type": "integer",
                "optional": true,
                "description": "Limits the change to when a particular tab is selected. Automatically resets when the tab is closed."
              }
            }
          },
          {
            "type": "function",
            "name": "callback",
            "optional": true,
            "parameters": []
          }
        ]
      },
      {
        "name": "getTitle",
        "type": "function",
        "description": "Gets the title of the browser action.",
        "async": "callback",
        "parameters": [
          {
            "name": "details",
            "type": "object",
            "properties": {
              "tabId": {
                "type": "integer",
                "optional": true,
                "description": "Specify the tab to get the title from. If no tab is specified, the non-tab-specific title is returned."
              }
            }
          },
          {
            "type": "function",
            "name": "callback",
            "parameters": [
              {
                "name": "result",
                "type": "string"
              }
            ]
          }
        ]
      },
      {
        "name": "setIcon",
        "type": "function",
        "description": "Sets the icon for the browser action. The icon can be specified either as the path to an image file or as the pixel data from a canvas element, or as dictionary of either one of those. Either the <b>path</b> or the <b>imageData</b> property must be specified.",
        "async": "callback",
        "parameters": [
          {
            "name": "details",
            "type": "object",
            "properties": {
              "imageData": {
                "choices": [
                  { "$ref": "ImageDataType" },
                  {
                    "type": "object",
                    "patternProperties": {
                      "^[1-9]\\d*$": {"$ref": "ImageDataType"}
                    }
                  }
                ],
                "optional": true,
                "description": "Either an ImageData object or a dictionary {size -> ImageData} representing icon to be set. If the icon is specified as a dictionary, the actual image to be used is chosen depending on screen's pixel density. If the number of image pixels that fit into one screen space unit equals <code>scale</code>, then image with size <code>scale</code> * 19 will be selected. Initially only scales 1 and 2 will be supported. At least one image must be specified. Note that 'details.imageData = foo' is equivalent to 'details.imageData = {'19': foo}'"
              },
              "path": {
                "choices": [
                  { "type": "string" },
                  {
                    "type": "object",
                    "patternProperties": {
                      "^[1-9]\\d*$": { "type": "string" }
                    }
                  }
                ],
                "optional": true,
                "description": "Either a relative image path or a dictionary {size -> relative image path} pointing to icon to be set. If the icon is specified as a dictionary, the actual image to be used is chosen depending on screen's pixel density. If the number of image pixels that fit into one screen space unit equals <code>scale</code>, then image with size <code>scale</code> * 19 will be selected. Initially only scales 1 and 2 will be supported. At least one image must be specified. Note that 'details.path = foo' is equivalent to 'details.imageData = {'19': foo}'"
              },
              "tabId": {
                "type": "integer",
                "optional": true,
                "description": "Limits the change to when a particular tab is selected. Automatically resets when the tab is closed."
              }
            }
          },
          {
            "type": "function",
            "name": "callback",
            "optional": true,
            "parameters": []
          }
        ]
      },
      {
        "name": "setPopup",
        "type": "function",
        "description": "Sets the html document to be opened as a popup when the user clicks on the browser action's icon.",
        "async": "callback",
        "parameters": [
          {
            "name": "details",
            "type": "object",
            "properties": {
              "tabId": {
                "type": "integer",
                "optional": true,
                "minimum": 0,
                "description": "Limits the change to when a particular tab is selected. Automatically resets when the tab is closed."
              },
              "popup": {
                "choices": [
                  {"type": "string"},
                  {"type": "null"}
                ],
                "description": "The html file to show in a popup.  If set to the empty string (''), no popup is shown."
              }
            }
          },
          {
            "type": "function",
            "name": "callback",
            "optional": true,
            "parameters": []
          }
        ]
      },
      {
        "name": "getPopup",
        "type": "function",
        "description": "Gets the html document set as the popup for this browser action.",
        "async": "callback",
        "parameters": [
          {
            "name": "details",
            "type": "object",
            "properties": {
              "tabId": {
                "type": "integer",
                "optional": true,
                "description": "Specify the tab to get the popup from. If no tab is specified, the non-tab-specific popup is returned."
              }
            }
          },
          {
            "type": "function",
            "name": "callback",
            "parameters": [
              {
                "name": "result",
                "type": "string"
              }
            ]
          }
        ]
      },
      {
        "name": "setBadgeText",
        "type": "function",
        "description": "Sets the badge text for the browser action. The badge is displayed on top of the icon.",
        "async": "callback",
        "parameters": [
          {
            "name": "details",
            "type": "object",
            "properties": {
              "text": {
                "choices": [
                  {"type": "string"},
                  {"type": "null"}
                ],
                "description": "Any number of characters can be passed, but only about four can fit in the space."
              },
              "tabId": {
                "type": "integer",
                "optional": true,
                "description": "Limits the change to when a particular tab is selected. Automatically resets when the tab is closed."
              }
            }
          },
          {
            "type": "function",
            "name": "callback",
            "optional": true,
            "parameters": []
          }
        ]
      },
      {
        "name": "getBadgeText",
        "type": "function",
        "description": "Gets the badge text of the browser action. If no tab is specified, the non-tab-specific badge text is returned.",
        "async": "callback",
        "parameters": [
          {
            "name": "details",
            "type": "object",
            "properties": {
              "tabId": {
                "type": "integer",
                "optional": true,
                "description": "Specify the tab to get the badge text from. If no tab is specified, the non-tab-specific badge text is returned."
              }
            }
          },
          {
            "type": "function",
            "name": "callback",
            "parameters": [
              {
                "name": "result",
                "type": "string"
              }
            ]
          }
        ]
      },
      {
        "name": "setBadgeBackgroundColor",
        "type": "function",
        "description": "Sets the background color for the badge.",
        "async": "callback",
        "parameters": [
          {
            "name": "details",
            "type": "object",
            "properties": {
              "color": {
                "description": "An array of four integers in the range [0,255] that make up the RGBA color of the badge. For example, opaque red is <code>[255, 0, 0, 255]</code>. Can also be a string with a CSS value, with opaque red being <code>#FF0000</code> or <code>#F00</code>.",
                "choices": [
                  {"type": "string"},
                  {"$ref": "ColorArray"},
                  {"type": "null"}
                ]
              },
              "tabId": {
                "type": "integer",
                "optional": true,
                "description": "Limits the change to when a particular tab is selected. Automatically resets when the tab is closed."
              }
            }
          },
          {
            "type": "function",
            "name": "callback",
            "optional": true,
            "parameters": []
          }
        ]
      },
      {
        "name": "getBadgeBackgroundColor",
        "type": "function",
        "description": "Gets the background color of the browser action.",
        "async": "callback",
        "parameters": [
          {
            "name": "details",
            "type": "object",
            "properties": {
              "tabId": {
                "type": "integer",
                "optional": true,
                "description": "Specify the tab to get the badge background color from. If no tab is specified, the non-tab-specific badge background color is returned."
              }
            }
          },
          {
            "type": "function",
            "name": "callback",
            "parameters": [
              {
                "name": "result",
                "$ref": "ColorArray"
              }
            ]
          }
        ]
      },
      {
        "name": "enable",
        "type": "function",
        "description": "Enables the browser action for a tab. By default, browser actions are enabled.",
        "async": "callback",
        "parameters": [
          {
            "type": "integer",
            "optional": true,
            "name": "tabId",
            "minimum": 0,
            "description": "The id of the tab for which you want to modify the browser action."
          },
          {
            "type": "function",
            "name": "callback",
            "optional": true,
            "parameters": []
          }
        ]
      },
      {
        "name": "disable",
        "type": "function",
        "description": "Disables the browser action for a tab.",
        "async": "callback",
        "parameters": [
          {
            "type": "integer",
            "optional": true,
            "name": "tabId",
            "minimum": 0,
            "description": "The id of the tab for which you want to modify the browser action."
          },
          {
            "type": "function",
            "name": "callback",
            "optional": true,
            "parameters": []
          }
        ]
      },
      {
        "name": "isEnabled",
        "type": "function",
        "description": "Checks whether the browser action is enabled.",
        "async": true,
        "parameters": [
          {
            "name": "details",
            "type": "object",
            "properties": {
              "tabId": {
                "type": "integer",
                "optional": true,
                "description": "Specify the tab to get the enabledness from. If no tab is specified, the non-tab-specific enabledness is returned."
              }
            }
          }
        ]
      },
      {
        "name": "openPopup",
        "type": "function",
        "requireUserInput": true,
        "description": "Opens the extension popup window in the active window.",
        "async": true,
        "parameters": []
      }
    ],
    "events": [
      {
        "name": "onClicked",
        "type": "function",
        "description": "Fired when a browser action icon is clicked.  This event will not fire if the browser action has a popup.",
        "parameters": [
          {
            "name": "tab",
            "$ref": "tabs.Tab"
          }
        ]
      }
    ]
  }
]
PK
!<�k+E�3�39chrome/browser/content/browser/schemas/browsing_data.json// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

[
  {
    "namespace": "manifest",
    "types": [
      {
        "$extend": "Permission",
        "choices": [{
          "type": "string",
          "enum": [
            "browsingData"
          ]
        }]
      }
    ]
  },
  {
    "namespace": "browsingData",
    "description": "Use the <code>chrome.browsingData</code> API to remove browsing data from a user's local profile.",
    "permissions": ["browsingData"],
    "types": [
      {
        "id": "RemovalOptions",
        "type": "object",
        "description": "Options that determine exactly what data will be removed.",
        "properties": {
          "since": {
            "$ref": "extensionTypes.Date",
            "optional": true,
            "description": "Remove data accumulated on or after this date, represented in milliseconds since the epoch (accessible via the <code>getTime</code> method of the JavaScript <code>Date</code> object). If absent, defaults to 0 (which would remove all browsing data)."
          },
          "hostnames": {
            "type": "array",
            "items": {"type": "string", "format": "hostname"},
            "optional": true,
            "description": "Only remove data associated with these hostnames (only applies to cookies and localStorage)."
          },
          "originTypes": {
            "type": "object",
            "optional": true,
            "description": "An object whose properties specify which origin types ought to be cleared. If this object isn't specified, it defaults to clearing only \"unprotected\" origins. Please ensure that you <em>really</em> want to remove application data before adding 'protectedWeb' or 'extensions'.",
            "properties": {
              "unprotectedWeb": {
                "type": "boolean",
                "optional": true,
                "description": "Normal websites."
              },
              "protectedWeb": {
                "type": "boolean",
                "optional": true,
                "description": "Websites that have been installed as hosted applications (be careful!)."
              },
              "extension": {
                "type": "boolean",
                "optional": true,
                "description": "Extensions and packaged applications a user has installed (be _really_ careful!)."
              }
            }
          }
        }
      },
      {
        "id": "DataTypeSet",
        "type": "object",
        "description": "A set of data types. Missing data types are interpreted as <code>false</code>.",
        "properties": {
          "cache": {
            "type": "boolean",
            "optional": true,
            "description": "The browser's cache. Note: when removing data, this clears the <em>entire</em> cache: it is not limited to the range you specify."
          },
          "cookies": {
            "type": "boolean",
            "optional": true,
            "description": "The browser's cookies."
          },
          "downloads": {
            "type": "boolean",
            "optional": true,
            "description": "The browser's download list."
          },
          "formData": {
            "type": "boolean",
            "optional": true,
            "description": "The browser's stored form data."
          },
          "history": {
            "type": "boolean",
            "optional": true,
            "description": "The browser's history."
          },
          "indexedDB": {
            "type": "boolean",
            "optional": true,
            "description": "Websites' IndexedDB data."
          },
          "localStorage": {
            "type": "boolean",
            "optional": true,
            "description": "Websites' local storage data."
          },
          "serverBoundCertificates": {
            "type": "boolean",
            "optional": true,
            "description": "Server-bound certificates."
          },
          "passwords": {
            "type": "boolean",
            "optional": true,
            "description": "Stored passwords."
          },
          "pluginData": {
            "type": "boolean",
            "optional": true,
            "description": "Plugins' data."
          },
          "serviceWorkers": {
            "type": "boolean",
            "optional": true,
            "description": "Service Workers."
          }
        }
      }
    ],
    "functions": [
      {
        "name": "settings",
        "description": "Reports which types of data are currently selected in the 'Clear browsing data' settings UI.  Note: some of the data types included in this API are not available in the settings UI, and some UI settings control more than one data type listed here.",
        "type": "function",
        "async": "callback",
        "parameters": [
          {
            "name": "callback",
            "type": "function",
            "parameters": [
              {
                "name": "result",
                "type": "object",
                "properties": {
                  "options": {
                    "$ref": "RemovalOptions"
                  },
                  "dataToRemove": {
                    "$ref": "DataTypeSet",
                    "description": "All of the types will be present in the result, with values of <code>true</code> if they are both selected to be removed and permitted to be removed, otherwise <code>false</code>."
                  },
                  "dataRemovalPermitted": {
                    "$ref": "DataTypeSet",
                    "description": "All of the types will be present in the result, with values of <code>true</code> if they are permitted to be removed (e.g., by enterprise policy) and <code>false</code> if not."
                  }
                }
              }
            ]
          }
        ]
      },
      {
        "name": "remove",
        "description": "Clears various types of browsing data stored in a user's profile.",
        "type": "function",
        "async": "callback",
        "parameters": [
          {
            "$ref": "RemovalOptions",
            "name": "options"
          },
          {
            "name": "dataToRemove",
            "$ref": "DataTypeSet",
            "description": "The set of data types to remove."
          },
          {
            "name": "callback",
            "type": "function",
            "description": "Called when deletion has completed.",
            "optional": true,
            "parameters": []
          }
        ]
      },
      {
        "name": "removeAppcache",
        "description": "Clears websites' appcache data.",
        "type": "function",
        "async": "callback",
        "unsupported": true,
        "parameters": [
          {
            "$ref": "RemovalOptions",
            "name": "options"
          },
          {
            "name": "callback",
            "type": "function",
            "description": "Called when websites' appcache data has been cleared.",
            "optional": true,
            "parameters": []
          }
        ]
      },
      {
        "name": "removeCache",
        "description": "Clears the browser's cache.",
        "type": "function",
        "async": "callback",
        "parameters": [
          {
            "$ref": "RemovalOptions",
            "name": "options"
          },
          {
            "name": "callback",
            "type": "function",
            "description": "Called when the browser's cache has been cleared.",
            "optional": true,
            "parameters": []
          }
        ]
      },
      {
        "name": "removeCookies",
        "description": "Clears the browser's cookies and server-bound certificates modified within a particular timeframe.",
        "type": "function",
        "async": "callback",
        "parameters": [
          {
            "$ref": "RemovalOptions",
            "name": "options"
          },
          {
            "name": "callback",
            "type": "function",
            "description": "Called when the browser's cookies and server-bound certificates have been cleared.",
            "optional": true,
            "parameters": []
          }
        ]
      },
      {
        "name": "removeDownloads",
        "description": "Clears the browser's list of downloaded files (<em>not</em> the downloaded files themselves).",
        "type": "function",
        "async": "callback",
        "parameters": [
          {
            "$ref": "RemovalOptions",
            "name": "options"
          },
          {
            "name": "callback",
            "type": "function",
            "description": "Called when the browser's list of downloaded files has been cleared.",
            "optional": true,
            "parameters": []
          }
        ]
      },
      {
        "name": "removeFileSystems",
        "description": "Clears websites' file system data.",
        "type": "function",
        "async": "callback",
        "unsupported": true,
        "parameters": [
          {
            "$ref": "RemovalOptions",
            "name": "options"
          },
          {
            "name": "callback",
            "type": "function",
            "description": "Called when websites' file systems have been cleared.",
            "optional": true,
            "parameters": []
          }
        ]
      },
      {
        "name": "removeFormData",
        "description": "Clears the browser's stored form data (autofill).",
        "type": "function",
        "async": "callback",
        "parameters": [
          {
            "$ref": "RemovalOptions",
            "name": "options"
          },
          {
            "name": "callback",
            "type": "function",
            "description": "Called when the browser's form data has been cleared.",
            "optional": true,
            "parameters": []
          }
        ]
      },
      {
        "name": "removeHistory",
        "description": "Clears the browser's history.",
        "type": "function",
        "async": "callback",
        "parameters": [
          {
            "$ref": "RemovalOptions",
            "name": "options"
          },
          {
            "name": "callback",
            "type": "function",
            "description": "Called when the browser's history has cleared.",
            "optional": true,
            "parameters": []
          }
        ]
      },
      {
        "name": "removeIndexedDB",
        "description": "Clears websites' IndexedDB data.",
        "type": "function",
        "async": "callback",
        "unsupported": true,
        "parameters": [
          {
            "$ref": "RemovalOptions",
            "name": "options"
          },
          {
            "name": "callback",
            "type": "function",
            "description": "Called when websites' IndexedDB data has been cleared.",
            "optional": true,
            "parameters": []
          }
        ]
      },
      {
        "name": "removeLocalStorage",
        "description": "Clears websites' local storage data.",
        "type": "function",
        "async": "callback",
        "parameters": [
          {
            "$ref": "RemovalOptions",
            "name": "options"
          },
          {
            "name": "callback",
            "type": "function",
            "description": "Called when websites' local storage has been cleared.",
            "optional": true,
            "parameters": []
          }
        ]
      },
      {
        "name": "removePluginData",
        "description": "Clears plugins' data.",
        "type": "function",
        "async": "callback",
        "parameters": [
          {
            "$ref": "RemovalOptions",
            "name": "options"
          },
          {
            "name": "callback",
            "type": "function",
            "description": "Called when plugins' data has been cleared.",
            "optional": true,
            "parameters": []
          }
        ]
      },
      {
        "name": "removePasswords",
        "description": "Clears the browser's stored passwords.",
        "type": "function",
        "async": "callback",
        "parameters": [
          {
            "$ref": "RemovalOptions",
            "name": "options"
          },
          {
            "name": "callback",
            "type": "function",
            "description": "Called when the browser's passwords have been cleared.",
            "optional": true,
            "parameters": []
          }
        ]
      },
      {
        "name": "removeWebSQL",
        "description": "Clears websites' WebSQL data.",
        "type": "function",
        "async": "callback",
        "unsupported": true,
        "parameters": [
          {
            "$ref": "RemovalOptions",
            "name": "options"
          },
          {
            "name": "callback",
            "type": "function",
            "description": "Called when websites' WebSQL databases have been cleared.",
            "optional": true,
            "parameters": []
          }
        ]
      }
    ]
  }
]
PK
!<�&6�ccEchrome/browser/content/browser/schemas/chrome_settings_overrides.json[
  {
    "namespace": "manifest",
    "types": [
      {
        "$extend": "WebExtensionManifest",
        "properties": {
          "chrome_settings_overrides": {
            "type": "object",
            "optional": true,
            "additionalProperties": { "$ref": "UnrecognizedProperty" },
            "properties": {
              "homepage": {
                "type": "string",
                "format": "homepageUrl",
                "optional": true,
                "preprocess": "localize"
              },
             "search_provider": {
                "type": "object",
                "optional": true,
                "additionalProperties": { "$ref": "UnrecognizedProperty" },
                "properties": {
                  "name": {
                    "type": "string",
                    "preprocess": "localize"
                  },
                  "keyword": {
                    "type": "string",
                    "optional": true,
                    "preprocess": "localize"
                  },
                  "search_url": {
                    "type": "string",
                    "format": "url",
                    "pattern": "^https://.*$",
                    "preprocess": "localize"
                  },
                  "favicon_url": {
                    "type": "string",
                    "optional": true,
                    "format": "url",
                    "preprocess": "localize"
                  },
                  "suggest_url": {
                    "type": "string",
                    "optional": true,
                    "format": "url",
                    "preprocess": "localize"
                  },
                  "instant_url": {
                    "type": "string",
                    "optional": true,
                    "format": "url",
                    "preprocess": "localize",
                    "deprecated": "Unsupported on Firefox at this time."
                  },
                  "image_url": {
                    "type": "string",
                    "optional": true,
                    "format": "url",
                    "preprocess": "localize",
                    "deprecated": "Unsupported on Firefox at this time."
                  },
                  "search_url_post_params": {
                    "type": "string",
                    "optional": true,
                    "preprocess": "localize",
                    "deprecated": "Unsupported on Firefox at this time."
                  },
                  "instant_url_post_params": {
                    "type": "string",
                    "optional": true,
                    "preprocess": "localize",
                    "deprecated": "Unsupported on Firefox at this time."
                  },
                  "image_url_post_params": {
                    "type": "string",
                    "optional": true,
                    "preprocess": "localize",
                    "deprecated": "Unsupported on Firefox at this time."
                  },
                  "alternate_urls": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "url",
                      "preprocess": "localize"
                    },
                    "optional": true,
                    "deprecated": "Unsupported on Firefox at this time."
                  },
                  "prepopulated_id": {
                    "type": "integer",
                    "optional": true,
                    "deprecated": "Unsupported on Firefox."
                  },
                  "is_default": {
                    "type": "boolean",
                    "optional": true,
                    "description": "Sets the default engine to a built-in engine only."
                  }
                }
              }
            }
          }
        }
      }
    ]
  }
]
PK
!<bi�٢�4chrome/browser/content/browser/schemas/commands.json// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

[
  {
    "namespace": "manifest",
    "types": [
     {
        "id": "KeyName",
        "choices": [
          {
            "type": "string",
            "pattern": "^\\s*(Alt|Ctrl|Command|MacCtrl)\\s*\\+\\s*(Shift\\s*\\+\\s*)?([A-Z0-9]|Comma|Period|Home|End|PageUp|PageDown|Space|Insert|Delete|Up|Down|Left|Right)\\s*$"
          },
          {
            "type": "string",
            "pattern": "^\\s*((Alt|Ctrl|Command|MacCtrl)\\s*\\+\\s*)?(Shift\\s*\\+\\s*)?(F[1-9]|F1[0-2])\\s*$"
          },
          {
            "type": "string",
            "pattern": "^(MediaNextTrack|MediaPlayPause|MediaPrevTrack|MediaStop)$"
          }
        ]
      },
      {
        "$extend": "WebExtensionManifest",
        "properties": {
          "commands": {
            "type": "object",
            "optional": true,
            "additionalProperties": {
              "type": "object",
              "additionalProperties": { "$ref": "UnrecognizedProperty" },
              "properties": {
                "suggested_key": {
                  "type": "object",
                  "optional": true,
                  "properties": {
                    "default": {
                      "$ref": "KeyName",
                      "optional": true
                    },
                    "mac": {
                      "$ref": "KeyName",
                      "optional": true
                    },
                    "linux": {
                      "$ref": "KeyName",
                      "optional": true
                    },
                    "windows": {
                      "$ref": "KeyName",
                      "optional": true
                    },
                    "chromeos": {
                      "type": "string",
                      "optional": true
                    },
                    "android": {
                      "type": "string",
                      "optional": true
                    },
                    "ios": {
                      "type": "string",
                      "optional": true
                    },
                    "additionalProperties": {
                      "type": "string",
                      "deprecated": "Unknown platform name",
                      "optional": true
                    }
                  }
                },
                "description": {
                  "type": "string",
                  "optional": true
                }
              }
            }
          }
        }
      }
    ]
  },
  {
    "namespace": "commands",
    "description": "Use the commands API to add keyboard shortcuts that trigger actions in your extension, for example, an action to open the browser action or send a command to the xtension.",
    "permissions": ["manifest:commands"],
    "types": [
      {
        "id": "Command",
        "type": "object",
        "properties": {
          "name":        {
            "type": "string",
            "optional": true,
            "description": "The name of the Extension Command"
          },
          "description": {
            "type": "string",
            "optional": true,
            "description": "The Extension Command description"
          },
          "shortcut": {
            "type": "string",
            "optional": true,
            "description": "The shortcut active for this command, or blank if not active."
          }
        }
      }
    ],
    "events": [
      {
        "name": "onCommand",
        "description": "Fired when a registered command is activated using a keyboard shortcut.",
        "type": "function",
        "parameters": [
          {
            "name": "command",
            "type": "string"
          }
        ]
      }
    ],
    "functions": [
      {
        "name": "update",
        "type": "function",
        "async": true,
        "description": "Update the details of an already defined command.",
        "parameters": [
          {
            "type": "object",
            "name": "detail",
            "description": "The new description for the command.",
            "properties": {
              "name": {
                "type": "string",
                "description": "The name of the command."
                },
              "description": {
                "type": "string",
                "optional": true,
                "description": "The new description for the command."
              },
              "shortcut": {
                "$ref": "manifest.KeyName",
                "optional": true
              }
            }
          }
        ]
      },
      {
        "name": "reset",
        "type": "function",
        "async": true,
        "description": "Reset a command's details to what is specified in the manifest.",
        "parameters": [
          {
            "type": "string",
            "name": "name",
            "description": "The name of the command."
          }
        ]
      },
      {
        "name": "getAll",
        "type": "function",
        "async": "callback",
        "description": "Returns all the registered extension commands for this extension and their shortcut (if active).",
        "parameters": [
          {
            "type": "function",
            "name": "callback",
            "optional": true,
            "parameters": [
              {
                "name": "commands",
                "type": "array",
                "items": {
                  "$ref": "Command"
                }
              }
            ],
            "description": "Called to return the registered commands."
          }
        ]
      }
    ]
  }
]
PK
!<�6kk4chrome/browser/content/browser/schemas/devtools.json[
  {
    "namespace": "manifest",
    "types": [
      {
        "$extend": "WebExtensionManifest",
        "properties": {
          "devtools_page": {
            "$ref": "ExtensionURL",
            "optional": true
          }
        }
      },
      {
        "$extend": "Permission",
        "choices": [{
          "type": "string",
          "enum": [
            "devtools"
          ]
        }]
      }
    ]
  },
  {
    "namespace": "devtools",
    "permissions": ["manifest:devtools_page"],
    "allowedContexts": ["devtools", "devtools_only"],
    "defaultContexts": ["devtools", "devtools_only"]
  }
]
PK
!<�����/�/Echrome/browser/content/browser/schemas/devtools_inspected_window.json// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

[
  {
    "namespace": "devtools.inspectedWindow",
    "allowedContexts": ["devtools", "devtools_only"],
    "defaultContexts": ["devtools", "devtools_only"],
    "description": "Use the <code>chrome.devtools.inspectedWindow</code> API to interact with the inspected window: obtain the tab ID for the inspected page, evaluate the code in the context of the inspected window, reload the page, or obtain the list of resources within the page.",
    "nocompile": true,
    "types": [
      {
        "id": "Resource",
        "type": "object",
        "description": "A resource within the inspected page, such as a document, a script, or an image.",
        "properties": {
          "url": {
            "type": "string",
            "description": "The URL of the resource."
          }
        },
        "functions": [
          {
            "name": "getContent",
            "unsupported": true,
            "type": "function",
            "async": "callback",
            "description": "Gets the content of the resource.",
            "parameters": [
              {
                "name": "callback",
                "type": "function",
                "description": "A function that receives resource content when the request completes.",
                "parameters": [
                  {
                    "name": "content",
                    "type": "string",
                    "description": "Content of the resource (potentially encoded)."
                  },
                  {
                    "name": "encoding",
                    "type": "string",
                    "description": "Empty if content is not encoded, encoding name otherwise. Currently, only base64 is supported."
                  }
                ]
              }
            ]
          },
          {
            "name": "setContent",
            "unsupported": true,
            "type": "function",
            "async": "callback",
            "description": "Sets the content of the resource.",
            "parameters": [
              {
                "name": "content",
                "type": "string",
                "description": "New content of the resource. Only resources with the text type are currently supported."
              },
              {
                "name": "commit",
                "type": "boolean",
                "description": "True if the user has finished editing the resource, and the new content of the resource should be persisted; false if this is a minor change sent in progress of the user editing the resource."
              },
              {
                "name": "callback",
                "type": "function",
                "description": "A function called upon request completion.",
                "optional": true,
                "parameters": [
                  {
                    "name": "error",
                    "type": "object",
                    "additionalProperties": {"type": "any"},
                    "optional": true,
                    "description": "Set to undefined if the resource content was set successfully; describes error otherwise."
                  }
                ]
              }
            ]
          }
        ]
      }
    ],
    "properties": {
      "tabId": {
        "description": "The ID of the tab being inspected. This ID may be used with chrome.tabs.* API.",
        "type": "integer"
      }
    },
    "functions": [
      {
        "name": "eval",
        "type": "function",
        "description": "Evaluates a JavaScript expression in the context of the main frame of the inspected page. The expression must evaluate to a JSON-compliant object, otherwise an exception is thrown. The eval function can report either a DevTools-side error or a JavaScript exception that occurs during evaluation. In either case, the <code>result</code> parameter of the callback is <code>undefined</code>. In the case of a DevTools-side error, the <code>isException</code> parameter is non-null and has <code>isError</code> set to true and <code>code</code> set to an error code. In the case of a JavaScript error, <code>isException</code> is set to true and <code>value</code> is set to the string value of thrown object.",
        "async": "callback",
        "parameters": [
          {
            "name": "expression",
            "type": "string",
            "description": "An expression to evaluate."
          },
          {
            "name": "options",
            "type": "object",
            "optional": true,
            "description": "The options parameter can contain one or more options.",
            "properties": {
              "frameURL": {
                "type": "string",
                "unsupported": true,
                "optional": true,
                "description": "If specified, the expression is evaluated on the iframe whose URL matches the one specified. By default, the expression is evaluated in the top frame of the inspected page."
              },
              "useContentScriptContext": {
                "type": "boolean",
                "unsupported": true,
                "optional": true,
                "description": "Evaluate the expression in the context of the content script of the calling extension, provided that the content script is already injected into the inspected page. If not, the expression is not evaluated and the callback is invoked with the exception parameter set to an object that has the <code>isError</code> field set to true and the <code>code</code> field set to <code>E_NOTFOUND</code>."
              },
              "contextSecurityOrigin": {
                "type": "string",
                "unsupported": true,
                "optional": true,
                "description": "Evaluate the expression in the context of a content script of an extension that matches the specified origin. If given, contextSecurityOrigin overrides the 'true' setting on userContentScriptContext."
              }
            }
          },
          {
            "name": "callback",
            "type": "function",
            "description": "A function called when evaluation completes.",
            "optional": true,
            "parameters": [
              {
                "name": "result",
                "type": "any",
                "description": "The result of evaluation."
              },
              {
                "name": "exceptionInfo",
                "type": "object",
                "optional": true,
                "description": "An object providing details if an exception occurred while evaluating the expression.",
                "properties": {
                  "isError": {
                    "type": "boolean",
                    "description": "Set if the error occurred on the DevTools side before the expression is evaluated."
                  },
                  "code": {
                    "type": "string",
                    "description": "Set if the error occurred on the DevTools side before the expression is evaluated."
                  },
                  "description": {
                    "type": "string",
                    "description": "Set if the error occurred on the DevTools side before the expression is evaluated."
                  },
                  "details": {
                    "type": "array",
                    "items": { "type": "any" },
                    "description": "Set if the error occurred on the DevTools side before the expression is evaluated, contains the array of the values that may be substituted into the description string to provide more information about the cause of the error."
                  },
                  "isException": {
                    "type": "boolean",
                    "description": "Set if the evaluated code produces an unhandled exception."
                  },
                  "value": {
                    "type": "string",
                    "description": "Set if the evaluated code produces an unhandled exception."
                  }
                }
              }
            ]
          }
        ]
      },
      {
        "name": "reload",
        "type": "function",
        "description": "Reloads the inspected page.",
        "parameters": [
          {
            "type": "object",
            "name": "reloadOptions",
            "optional": true,
            "properties": {
              "ignoreCache": {
                "type": "boolean",
                "optional": true,
                "description": "When true, the loader will bypass the cache for all inspected page resources loaded before the <code>load</code> event is fired. The effect is similar to pressing Ctrl+Shift+R in the inspected window or within the Developer Tools window."
              },
              "userAgent": {
                "type": "string",
                "optional": true,
                "description": "If specified, the string will override the value of the <code>User-Agent</code> HTTP header that's sent while loading the resources of the inspected page. The string will also override the value of the <code>navigator.userAgent</code> property that's returned to any scripts that are running within the inspected page."
              },
              "injectedScript": {
                "type": "string",
                "optional": true,
                "description": "If specified, the script will be injected into every frame of the inspected page immediately upon load, before any of the frame's scripts. The script will not be injected after subsequent reloads&mdash;for example, if the user presses Ctrl+R."
              },
              "preprocessorScript": {
                "unsupported": true,
                "type": "string",
                "deprecated": "Please avoid using this parameter, it will be removed soon.",
                "optional": true,
                "description": "If specified, this script evaluates into a function that accepts three string arguments: the source to preprocess, the URL of the source, and a function name if the source is an DOM event handler. The preprocessorerScript function should return a string to be compiled by Chrome in place of the input source. In the case that the source is a DOM event handler, the returned source must compile to a single JS function."
              }
            }
          }
        ]
      },
      {
        "name": "getResources",
        "unsupported": true,
        "type": "function",
        "description": "Retrieves the list of resources from the inspected page.",
        "unsupported": true,
        "async": "callback",
        "parameters": [
          {
            "name": "callback",
            "type": "function",
            "description": "A function that receives the list of resources when the request completes.",
            "parameters": [
              {
                "name": "resources",
                "type": "array",
                "items": { "$ref": "Resource" },
                "description": "The resources within the page."
              }
            ]
          }
        ]
      }
    ],
    "events": [
      {
        "name": "onResourceAdded",
        "unsupported": true,
        "type": "function",
        "description": "Fired when a new resource is added to the inspected page.",
        "parameters": [
          {
            "name": "resource",
            "$ref": "Resource"
          }
        ]
      },
      {
        "name": "onResourceContentCommitted",
        "unsupported": true,
        "type": "function",
        "description": "Fired when a new revision of the resource is committed (e.g. user saves an edited version of the resource in the Developer Tools).",
        "parameters": [
          {
            "name": "resource",
            "$ref": "Resource"
          },
          {
            "name": "content",
            "type": "string",
            "description": "New content of the resource."
          }
        ]
      }
    ]
  }
]
PK
!<,�c}}<chrome/browser/content/browser/schemas/devtools_network.json// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

[
  {
    "namespace": "devtools.network",
    "allowedContexts": ["devtools", "devtools_only"],
    "defaultContexts": ["devtools", "devtools_only"],
    "description": "Use the <code>chrome.devtools.network</code> API to retrieve the information about network requests displayed by the Developer Tools in the Network panel.",
    "types": [
      {
        "id": "Request",
        "type": "object",
        "description": "Represents a network request for a document resource (script, image and so on). See HAR Specification for reference.",
        "functions": [
          {
            "name": "getContent",
            "type": "function",
            "description": "Returns content of the response body.",
            "async": "callback",
            "parameters": [
              {
                "name": "callback",
                "type": "function",
                "description": "A function that receives the response body when the request completes.",
                "parameters": [
                  {
                    "name": "content",
                    "type": "string",
                    "description": "Content of the response body (potentially encoded)."
                  },
                  {
                    "name": "encoding",
                    "type": "string",
                    "description": "Empty if content is not encoded, encoding name otherwise. Currently, only base64 is supported."
                  }
                ]
              }
            ]
          }
        ]
      }
    ],
    "functions": [
      {
        "name": "getHAR",
        "type": "function",
        "description": "Returns HAR log that contains all known network requests.",
        "async": "callback",
        "parameters": [
          {
            "name": "callback",
            "type": "function",
            "description": "A function that receives the HAR log when the request completes.",
            "parameters": [
              {
                "name": "harLog",
                "type": "object",
                "additionalProperties": {"type": "any"},
                "description": "A HAR log. See HAR specification for details."
              }
            ]
          }
        ]
      }
    ],
    "events": [
      {
        "name": "onRequestFinished",
        "type": "function",
        "description": "Fired when a network request is finished and all request data are available.",
        "parameters": [
          {
            "name": "request",
            "$ref": "Request",
            "description": "Description of a network request in the form of a HAR entry. See HAR specification for details."
          }
        ]
      },
      {
        "name": "onNavigated",
        "type": "function",
        "description": "Fired when the inspected window navigates to a new page.",
        "parameters": [
          {
            "name": "url",
            "type": "string",
            "description": "URL of the new page."
          }
        ]
      }
    ]
  }
]
PK
!<�T�b�:�:;chrome/browser/content/browser/schemas/devtools_panels.json// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

[
  {
    "namespace": "devtools.panels",
    "allowedContexts": ["devtools", "devtools_only"],
    "defaultContexts": ["devtools", "devtools_only"],
    "description": "Use the <code>chrome.devtools.panels</code> API to integrate your extension into Developer Tools window UI: create your own panels, access existing panels, and add sidebars.",
    "nocompile": true,
    "types": [
      {
        "id": "ElementsPanel",
        "type": "object",
        "description": "Represents the Elements panel.",
        "events": [
          {
            "name": "onSelectionChanged",
            "type": "function",
            "description": "Fired when an object is selected in the panel."
          }
        ],
        "functions": [
          {
            "name": "createSidebarPane",
            "async": "callback",
            "type": "function",
            "description": "Creates a pane within panel's sidebar.",
            "parameters": [
              {
                "name": "title",
                "type": "string",
                "description": "Text that is displayed in sidebar caption."
              },
              {
                "name": "callback",
                "type": "function",
                "description": "A callback invoked when the sidebar is created.",
                "optional": true,
                "parameters": [
                  {
                    "name": "result",
                    "description": "An ExtensionSidebarPane object for created sidebar pane.",
                    "$ref": "ExtensionSidebarPane"
                  }
                ]
              }
            ]
          }
        ]
      },
      {
        "id": "SourcesPanel",
        "type": "object",
        "description": "Represents the Sources panel.",
        "events": [
          {
            "name": "onSelectionChanged",
            "unsupported": true,
            "description": "Fired when an object is selected in the panel."
          }
        ],
        "functions": [
          {
            "name": "createSidebarPane",
            "unsupported": true,
            "type": "function",
            "description": "Creates a pane within panel's sidebar.",
            "parameters": [
              {
                "name": "title",
                "type": "string",
                "description": "Text that is displayed in sidebar caption."
              },
              {
                "name": "callback",
                "type": "function",
                "description": "A callback invoked when the sidebar is created.",
                "optional": true,
                "parameters": [
                  {
                    "name": "result",
                    "description": "An ExtensionSidebarPane object for created sidebar pane.",
                    "$ref": "ExtensionSidebarPane"
                  }
                ]
              }
            ]
          }
        ]
      },
      {
        "id": "ExtensionPanel",
        "type": "object",
        "description": "Represents a panel created by extension.",
        "functions": [
          {
            "name": "createStatusBarButton",
            "unsupported": true,
            "description": "Appends a button to the status bar of the panel.",
            "type": "function",
            "parameters": [
              {
                "name": "iconPath",
                "type": "string",
                "description": "Path to the icon of the button. The file should contain a 64x24-pixel image composed of two 32x24 icons. The left icon is used when the button is inactive; the right icon is displayed when the button is pressed."
              },
              {
                "name": "tooltipText",
                "type": "string",
                "description": "Text shown as a tooltip when user hovers the mouse over the button."
              },
              {
                "name": "disabled",
                "type": "boolean",
                "description": "Whether the button is disabled."
              }
            ],
            "returns": { "$ref": "Button" }
          }
        ],
        "events": [
          {
            "name": "onSearch",
            "unsupported": true,
            "description": "Fired upon a search action (start of a new search, search result navigation, or search being canceled).",
            "parameters": [
              {
                "name": "action",
                "type": "string",
                "description": "Type of search action being performed."
              },
              {
                "name": "queryString",
                "type": "string",
                "optional": true,
                "description": "Query string (only for 'performSearch')."
              }
            ]
          },
          {
            "name": "onShown",
            "type": "function",
            "description": "Fired when the user switches to the panel.",
            "parameters": [
              {
                "name": "window",
                "type": "object",
                "isInstanceOf": "global",
                "additionalProperties": { "type": "any" },
                "description": "The JavaScript <code>window</code> object of panel's page."
              }
            ]
          },
          {
            "name": "onHidden",
            "type": "function",
            "description": "Fired when the user switches away from the panel."
          }
        ]
      },
      {
        "id": "ExtensionSidebarPane",
        "type": "object",
        "description": "A sidebar created by the extension.",
        "functions": [
          {
            "name": "setHeight",
            "unsupported": true,
            "type": "function",
            "description": "Sets the height of the sidebar.",
            "parameters": [
              {
                "name": "height",
                "type": "string",
                "description": "A CSS-like size specification, such as <code>'100px'</code> or <code>'12ex'</code>."
              }
            ]
          },
          {
            "name": "setExpression",
            "async": "callback",
            "type": "function",
            "description": "Sets an expression that is evaluated within the inspected page. The result is displayed in the sidebar pane.",
            "parameters": [
              {
                "name": "expression",
                "type": "string",
                "description": "An expression to be evaluated in context of the inspected page. JavaScript objects and DOM nodes are displayed in an expandable tree similar to the console/watch."
              },
              {
                "name": "rootTitle",
                "type": "string",
                "optional": true,
                "description": "An optional title for the root of the expression tree."
              },
              {
                "name": "callback",
                "type": "function",
                "optional": true,
                "description": "A callback invoked after the sidebar pane is updated with the expression evaluation results."
              }
            ]
          },
          {
            "name": "setObject",
            "async": "callback",
            "type": "function",
            "description": "Sets a JSON-compliant object to be displayed in the sidebar pane.",
            "parameters": [
              {
                "name": "jsonObject",
                "type": "string",
                "description": "An object to be displayed in context of the inspected page. Evaluated in the context of the caller (API client)."
              },
              {
                "name": "rootTitle",
                "type": "string",
                "optional": true,
                "description": "An optional title for the root of the expression tree."
              },
              {
                "name": "callback",
                "type": "function",
                "optional": true,
                "description": "A callback invoked after the sidebar is updated with the object."
              }
            ]
          },
          {
            "name": "setPage",
            "unsupported": true,
            "type": "function",
            "description": "Sets an HTML page to be displayed in the sidebar pane.",
            "parameters": [
              {
                "name": "path",
                "type": "string",
                "description": "Relative path of an extension page to display within the sidebar."
              }
            ]
          }
        ],
        "events": [
          {
            "name": "onShown",
            "type": "function",
            "description": "Fired when the sidebar pane becomes visible as a result of user switching to the panel that hosts it.",
            "parameters": [
              {
                "name": "window",
                "type": "object",
                "isInstanceOf": "global",
                "additionalProperties": { "type": "any" },
                "description": "The JavaScript <code>window</code> object of the sidebar page, if one was set with the <code>setPage()</code> method."
              }
            ]
          },
          {
            "name": "onHidden",
            "type": "function",
            "description": "Fired when the sidebar pane becomes hidden as a result of the user switching away from the panel that hosts the sidebar pane."
          }
        ]
      },
      {
        "id": "Button",
        "type": "object",
        "description": "A button created by the extension.",
        "functions": [
          {
            "name": "update",
            "unsupported": true,
            "type": "function",
            "description": "Updates the attributes of the button. If some of the arguments are omitted or <code>null</code>, the corresponding attributes are not updated.",
            "parameters": [
              {
                "name": "iconPath",
                "type": "string",
                "optional": true,
                "description": "Path to the new icon of the button."
              },
              {
                "name": "tooltipText",
                "type": "string",
                "optional": true,
                "description": "Text shown as a tooltip when user hovers the mouse over the button."
              },
              {
                "name": "disabled",
                "type": "boolean",
                "optional": true,
                "description": "Whether the button is disabled."
              }
            ]
          }
        ],
        "events": [
          {
            "name": "onClicked",
            "unsupported": true,
            "type": "function",
            "description": "Fired when the button is clicked."
          }
        ]
      }
    ],
    "properties": {
      "elements": {
        "$ref": "ElementsPanel",
        "description": "Elements panel."
      },
      "sources": {
        "$ref": "SourcesPanel",
        "description": "Sources panel."
      },
      "themeName": {
        "type": "string",
        "description": "The name of the current devtools theme."
      }
    },
    "functions": [
      {
        "name": "create",
        "type": "function",
        "description": "Creates an extension panel.",
        "async": "callback",
        "parameters": [
          {
            "name": "title",
            "type": "string",
            "description": "Title that is displayed next to the extension icon in the Developer Tools toolbar."
          },
          {
            "name": "iconPath",
            "description": "Path of the panel's icon relative to the extension directory, or an empty string to use the default extension icon as the panel icon.",
            "choices": [
              {"type": "string", "enum": [""]},
              {"$ref": "manifest.ExtensionURL"}
            ]
          },
          {
            "name": "pagePath",
            "$ref": "manifest.ExtensionURL",
            "description": "Path of the panel's HTML page relative to the extension directory."
          },
          {
            "name": "callback",
            "type": "function",
            "optional": true,
            "description": "A function that is called when the panel is created.",
            "parameters": [
              {
                "name": "panel",
                "description": "An ExtensionPanel object representing the created panel.",
                "$ref": "ExtensionPanel"
              }
            ]
          }
        ]
      },
      {
        "name": "setOpenResourceHandler",
        "unsupported": true,
        "type": "function",
        "description": "Specifies the function to be called when the user clicks a resource link in the Developer Tools window. To unset the handler, either call the method with no parameters or pass null as the parameter.",
        "async": "callback",
        "parameters": [
          {
            "name": "callback",
            "type": "function",
            "optional": true,
            "description": "A function that is called when the user clicks on a valid resource link in Developer Tools window. Note that if the user clicks an invalid URL or an XHR, this function is not called.",
            "parameters": [
              {
                "name": "resource",
                "$ref": "devtools.inspectedWindow.Resource",
                "description": "A $(ref:devtools.inspectedWindow.Resource) object for the resource that was clicked."
              }
            ]
          }
        ]
      },
      {
        "name": "openResource",
        "unsupported": true,
        "type": "function",
        "description": "Requests DevTools to open a URL in a Developer Tools panel.",
        "async": "callback",
        "parameters": [
          {
            "name": "url",
            "type": "string",
            "description": "The URL of the resource to open."
          },
          {
            "name": "lineNumber",
            "type": "integer",
            "description": "Specifies the line number to scroll to when the resource is loaded."
          },
          {
            "name": "callback",
            "type": "function",
            "optional": true,
            "description": "A function that is called when the resource has been successfully loaded."
          }
        ]
      }
    ],
    "events": [
      {
        "name": "onThemeChanged",
        "type": "function",
        "description": "Fired when the devtools theme changes.",
        "parameters": [
          {
            "name": "themeName",
            "type": "string",
            "description": "The name of the current devtools theme."
          }
        ]
      }
    ]
  }
]
PK
!<mI�aa0chrome/browser/content/browser/schemas/find.json// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

[
  {
    "namespace": "manifest",
    "types": [
      {
        "$extend": "OptionalPermission",
        "choices": [{
          "type": "string",
          "enum": [
            "find"
          ]
        }]
      }
    ]
  },
  {
    "namespace": "find",
    "description": "Use the <code>browser.find</code> API to interact with the browser's <code>Find</code> interface.",
    "permissions": ["find"],
    "functions": [
      {
        "name": "find",
        "type": "function",
        "async": true,
        "description": "Search for text in document and store found ranges in array, in document order.",
        "parameters": [
          {
            "name": "queryphrase",
            "type": "string",
            "description": "The string to search for."
          },
          {
            "name": "params",
            "type": "object",
            "description": "Search parameters.",
            "optional": true,
            "properties": {
              "tabId": {
                "type": "integer",
                "description": "Tab to query. Defaults to the active tab.",
                "optional": true,
                "minimum": 0
              },
              "caseSensitive": {
                "type": "boolean",
                "description": "Find only ranges with case sensitive match.",
                "optional": true
              },
              "entireWord": {
                "type": "boolean",
                "description": "Find only ranges that match entire word.",
                "optional": true
              },
              "includeRectData": {
                "description": "Return rectangle data which describes visual position of search results.",
                "type": "boolean",
                "optional": true
              },
              "includeRangeData": {
                "description": "Return range data which provides range data in a serializable form.",
                "type": "boolean",
                "optional": true
              }
            }
          }
        ]
      },
      {
        "name": "highlightResults",
        "type": "function",
        "async": true,
        "description": "Highlight a range",
        "parameters": [
          {
            "name": "params",
            "type": "object",
            "description": "highlightResults parameters",
            "optional": true,
            "properties": {
              "rangeIndex": {
                "type": "integer",
                "description": "Found range to be highlighted. Default highlights all ranges.",
                "minimum": 0,
                "optional": true
              },
              "tabId": {
                "type": "integer",
                "description": "Tab to highlight. Defaults to the active tab.",
                "minimum": 0,
                "optional": true
              },
              "noScroll": {
                "type": "boolean",
                "description": "Don't scroll to highlighted item.",
                "optional": true
              }
            }
          }
        ]
      },
      {
        "name": "removeHighlighting",
        "type": "function",
        "async": true,
        "description": "Remove all highlighting from previous searches.",
        "parameters": [
          {
            "name": "tabId",
            "type": "integer",
            "description": "Tab to highlight. Defaults to the active tab.",
            "optional": true
          }
        ]
      }
    ]
  }
]
PK
!<{f��\\9chrome/browser/content/browser/schemas/geckoProfiler.json[
  {
    "namespace": "manifest",
    "types": [
      {
        "$extend": "Permission",
        "choices": [{
          "type": "string",
          "enum": [
            "geckoProfiler"
          ]
        }]
      }
    ]
  },
  {
    "namespace": "geckoProfiler",
    "description": "Exposes the browser's profiler.",

    "permissions": ["geckoProfiler"],
    "types": [
      {
        "id": "ProfilerFeature",
        "type": "string",
        "enum": [
          "java",
          "js",
          "leaf",
          "mainthreadio",
          "memory",
          "privacy",
          "restyle",
          "stackwalk",
          "tasktracer",
          "threads"
        ]
      }
    ],
    "functions": [
      {
       "name": "start",
       "type": "function",
       "description": "Starts the profiler with the specified settings.",
       "async": true,
       "parameters": [
          {
            "name": "settings",
            "type": "object",
            "properties": {
              "bufferSize": {
                "type": "integer",
                "minimum": 0,
                "description": "The size in bytes of the buffer used to store profiling data. A larger value allows capturing a profile that covers a greater amount of time."
              },
              "interval": {
                "type": "number",
                "description": "Interval in milliseconds between samples of profiling data. A smaller value will increase the detail of the profiles captured."
              },
              "features": {
                "type": "array",
                "description": "A list of active features for the profiler.",
                "items": {
                  "$ref": "ProfilerFeature"
                }
              },
              "threads": {
                "type": "array",
                "description": "A list of thread names for which to capture profiles.",
                "optional": true,
                "items": {
                  "type": "string"
                }
              }
            }
          }
        ]
      },
      {
        "name": "stop",
        "type": "function",
        "description": "Stops the profiler and discards any captured profile data.",
        "async": true,
        "parameters": []
      },
      {
        "name": "pause",
        "type": "function",
        "description": "Pauses the profiler, keeping any profile data that is already written.",
        "async": true,
        "parameters": []
      },
      {
        "name": "resume",
        "type": "function",
        "description": "Resumes the profiler with the settings that were initially used to start it.",
        "async": true,
        "parameters": []
      },
      {
        "name": "getProfile",
        "type": "function",
        "description": "Gathers the profile data from the current profiling session.",
        "async": true,
        "parameters": []
      },
      {
        "name": "getProfileAsArrayBuffer",
        "type": "function",
        "description": "Gathers the profile data from the current profiling session. The returned promise resolves to an array buffer that contains a JSON string.",
        "async": true,
        "parameters": []
      },
      {
        "name": "getSymbols",
        "type": "function",
        "description": "Gets the debug symbols for a particular library.",
        "async": true,
        "parameters": [
          {
            "type": "string",
            "name": "debugName",
            "description": "The name of the library's debug file. For example, 'xul.pdb"
          },
          {
            "type": "string",
            "name": "breakpadId",
            "description": "The Breakpad ID of the library"
          }
        ]
      }
    ],
    "events": [
      {
        "name": "onRunning",
        "type": "function",
        "description": "Fires when the profiler starts/stops running.",
        "parameters": [
          {
            "name": "isRunning",
            "type": "boolean",
            "description": "Whether the profiler is running or not. Pausing the profiler will not affect this value."
          }
        ]
      }
    ]
  }
]
PK
!<j����)�)3chrome/browser/content/browser/schemas/history.json// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

[
  {
    "namespace": "manifest",
    "types": [
      {
        "$extend": "OptionalPermission",
        "choices": [{
          "type": "string",
          "enum": [
            "history"
          ]
        }]
      }
    ]
  },
  {
    "namespace": "history",
    "description": "Use the <code>browser.history</code> API to interact with the browser's record of visited pages. You can add, remove, and query for URLs in the browser's history. To override the history page with your own version, see $(topic:override)[Override Pages].",
    "permissions": ["history"],
    "types": [
      {
        "id": "TransitionType",
        "type": "string",
        "enum": ["link", "typed", "auto_bookmark", "auto_subframe", "manual_subframe", "generated", "auto_toplevel", "form_submit", "reload", "keyword", "keyword_generated"],
        "description": "The $(topic:transition-types)[transition type] for this visit from its referrer."
      },
      {
        "id": "HistoryItem",
        "type": "object",
        "description": "An object encapsulating one result of a history query.",
        "properties": {
          "id": {
            "type": "string",
            "description": "The unique identifier for the item."
          },
          "url": {
            "type": "string",
            "optional": true,
            "description": "The URL navigated to by a user."
          },
          "title": {
            "type": "string",
            "optional": true,
            "description": "The title of the page when it was last loaded."
          },
          "lastVisitTime": {
            "type": "number",
            "optional": true,
            "description": "When this page was last loaded, represented in milliseconds since the epoch."
          },
          "visitCount": {
            "type": "integer",
            "optional": true,
            "description": "The number of times the user has navigated to this page."
          },
          "typedCount": {
            "type": "integer",
            "optional": true,
            "description": "The number of times the user has navigated to this page by typing in the address."
          }
        }
      },
      {
        "id": "VisitItem",
        "type": "object",
        "description": "An object encapsulating one visit to a URL.",
        "properties": {
          "id": {
            "type": "string",
            "description": "The unique identifier for the item."
          },
          "visitId": {
            "type": "string",
            "description": "The unique identifier for this visit."
          },
          "visitTime": {
            "type": "number",
            "optional": true,
            "description": "When this visit occurred, represented in milliseconds since the epoch."
          },
          "referringVisitId": {
            "type": "string",
            "description": "The visit ID of the referrer."
          },
          "transition": {
            "$ref": "TransitionType",
            "description": "The $(topic:transition-types)[transition type] for this visit from its referrer."
          }
        }
      }
    ],
    "functions": [
      {
        "name": "search",
        "type": "function",
        "description": "Searches the history for the last visit time of each page matching the query.",
        "async": "callback",
        "parameters": [
          {
            "name": "query",
            "type": "object",
            "properties": {
              "text": {
                "type": "string",
                "description": "A free-text query to the history service.  Leave empty to retrieve all pages."
              },
              "startTime": {
                "$ref": "extensionTypes.Date",
                "optional": true,
                "description": "Limit results to those visited after this date. If not specified, this defaults to 24 hours in the past."
              },
              "endTime": {
                "$ref": "extensionTypes.Date",
                "optional": true,
                "description": "Limit results to those visited before this date."
              },
              "maxResults": {
                "type": "integer",
                "optional": true,
                "minimum": 1,
                "description": "The maximum number of results to retrieve.  Defaults to 100."
              }
            }
          },
          {
            "name": "callback",
            "type": "function",
            "parameters": [
              {
                "name": "results",
                "type": "array",
                "items": {
                  "$ref": "HistoryItem"
                }
              }
            ]
          }
        ]
      },
      {
        "name": "getVisits",
        "type": "function",
        "description": "Retrieves information about visits to a URL.",
        "async": "callback",
        "parameters": [
          {
            "name": "details",
            "type": "object",
            "properties": {
              "url": {
                "type": "string",
                "description": "The URL for which to retrieve visit information.  It must be in the format as returned from a call to history.search."
              }
            }
          },
          {
            "name": "callback",
            "type": "function",
            "parameters": [
              {
                "name": "results",
                "type": "array",
                "items": {
                  "$ref": "VisitItem"
                }
              }
            ]
          }
        ]
      },
      {
        "name": "addUrl",
        "type": "function",
        "description": "Adds a URL to the history with a default visitTime of the current time and a default $(topic:transition-types)[transition type] of \"link\".",
        "async": "callback",
        "parameters": [
          {
            "name": "details",
            "type": "object",
            "properties": {
              "url": {
                "type": "string",
                "description": "The URL to add. Must be a valid URL that can be added to history."
              },
              "title": {
                "type": "string",
                "optional": true,
                "description": "The title of the page."
              },
              "transition": {
                "$ref": "TransitionType",
                "optional": true,
                "description": "The $(topic:transition-types)[transition type] for this visit from its referrer."
              },
              "visitTime": {
                "$ref": "extensionTypes.Date",
                "optional": true,
                "description": "The date when this visit occurred."
              }
            }
          },
          {
            "name": "callback",
            "type": "function",
            "optional": true,
            "parameters": []
          }
        ]
      },
      {
        "name": "deleteUrl",
        "type": "function",
        "description": "Removes all occurrences of the given URL from the history.",
        "async": "callback",
        "parameters": [
          {
            "name": "details",
            "type": "object",
            "properties": {
              "url": {
                "type": "string",
                "description": "The URL to remove."
              }
            }
          },
          {
            "name": "callback",
            "type": "function",
            "optional": true,
            "parameters": []
          }
        ]
      },
      {
        "name": "deleteRange",
        "type": "function",
        "description": "Removes all items within the specified date range from the history.  Pages will not be removed from the history unless all visits fall within the range.",
        "async": "callback",
        "parameters": [
          {
            "name": "range",
            "type": "object",
            "properties": {
              "startTime": {
                "$ref": "extensionTypes.Date",
                "description": "Items added to history after this date."
              },
              "endTime": {
                "$ref": "extensionTypes.Date",
                "description": "Items added to history before this date."
              }
            }
          },
          {
            "name": "callback",
            "type": "function",
            "parameters": []
          }
        ]
      },
      {
        "name": "deleteAll",
        "type": "function",
        "description": "Deletes all items from the history.",
        "async": "callback",
        "parameters": [
          {
            "name": "callback",
            "type": "function",
            "parameters": []
          }
        ]
      }
    ],
    "events": [
      {
        "name": "onVisited",
        "type": "function",
        "description": "Fired when a URL is visited, providing the HistoryItem data for that URL.  This event fires before the page has loaded.",
        "parameters": [
          {
            "name": "result",
            "$ref": "HistoryItem"
          }
        ]
      },
      {
        "name": "onVisitRemoved",
        "type": "function",
        "description": "Fired when one or more URLs are removed from the history service.  When all visits have been removed the URL is purged from history.",
        "parameters": [
          {
            "name": "removed",
            "type": "object",
            "properties": {
              "allHistory": {
                "type": "boolean",
                "description": "True if all history was removed.  If true, then urls will be empty."
              },
              "urls": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          }
        ]
      },
      {
        "name": "onTitleChanged",
        "type": "function",
        "description": "Fired when the title of a URL is changed in the browser history.",
        "parameters": [
          {
            "name": "changed",
            "type": "object",
            "properties": {
              "url": {
                "type": "string",
                "description": "The URL for which the title has changed"
              },
              "title": {
                "type": "string",
                "description": "The new title for the URL."
              }
            }
          }
        ]
      }
    ]
  }
]
PK
!<b�g�G�G1chrome/browser/content/browser/schemas/menus.json// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

[
  {
    "namespace": "manifest",
    "types": [
      {
        "$extend": "Permission",
        "choices": [{
          "type": "string",
          "enum": [
            "menus",
            "contextMenus"
          ]
        }]
      }
    ]
  },
  {
    "namespace": "contextMenus",
    "permissions": ["contextMenus"],
    "description": "Use the browser.contextMenus API to add items to the browser's context menu. You can choose what types of objects your context menu additions apply to, such as images, hyperlinks, and pages.",
    "$import": "menus",
    "types": [
      {
        "id": "ContextType",
        "type": "string",
        "enum": ["all", "page", "frame", "selection", "link", "editable", "password", "image", "video", "audio", "launcher", "bookmark", "browser_action", "page_action", "tab"],
        "description": "The different contexts a menu can appear in. Specifying 'all' is equivalent to the combination of all other contexts except for 'tab' and 'tools_menu'."
      }
    ]
  },
  {
    "namespace": "menus",
    "permissions": ["menus"],
    "description": "Use the browser.menus API to add items to the browser's menus. You can choose what types of objects your context menu additions apply to, such as images, hyperlinks, and pages.",
    "properties": {
      "ACTION_MENU_TOP_LEVEL_LIMIT": {
        "value": 6,
        "description": "The maximum number of top level extension items that can be added to an extension action context menu. Any items beyond this limit will be ignored."
      }
    },
    "types": [
      {
        "id": "ContextType",
        "type": "string",
        "enum": ["all", "page", "frame", "selection", "link", "editable", "password", "image", "video", "audio", "launcher", "bookmark", "browser_action", "page_action", "tab", "tools_menu"],
        "description": "The different contexts a menu can appear in. Specifying 'all' is equivalent to the combination of all other contexts except for 'tab' and 'tools_menu'."
      },
      {
        "id": "ItemType",
        "type": "string",
        "enum": ["normal", "checkbox", "radio", "separator"],
        "description": "The type of menu item."
      },
      {
        "id": "OnClickData",
        "type": "object",
        "description": "Information sent when a context menu item is clicked.",
        "properties": {
          "menuItemId": {
            "choices": [
              { "type": "integer" },
              { "type": "string" }
            ],
            "description": "The ID of the menu item that was clicked."
          },
          "parentMenuItemId": {
            "choices": [
              { "type": "integer" },
              { "type": "string" }
            ],
            "optional": true,
            "description": "The parent ID, if any, for the item clicked."
          },
          "mediaType": {
            "type": "string",
            "optional": true,
            "description": "One of 'image', 'video', or 'audio' if the context menu was activated on one of these types of elements."
          },
          "linkText": {
            "type": "string",
            "optional": true,
            "description": "If the element is a link, the text of that link."
          },
          "linkUrl": {
            "type": "string",
            "optional": true,
            "description": "If the element is a link, the URL it points to."
          },
          "srcUrl": {
            "type": "string",
            "optional": true,
            "description": "Will be present for elements with a 'src' URL."
          },
          "pageUrl": {
            "type": "string",
            "optional": true,
            "description": "The URL of the page where the menu item was clicked. This property is not set if the click occured in a context where there is no current page, such as in a launcher context menu."
          },
          "frameUrl": {
            "type": "string",
            "optional": true,
            "description": " The URL of the frame of the element where the context menu was clicked, if it was in a frame."
          },
          "selectionText": {
            "type": "string",
            "optional": true,
            "description": "The text for the context selection, if any."
          },
          "editable": {
            "type": "boolean",
            "description": "A flag indicating whether the element is editable (text input, textarea, etc.)."
          },
          "wasChecked": {
            "type": "boolean",
            "optional": true,
            "description": "A flag indicating the state of a checkbox or radio item before it was clicked."
          },
          "checked": {
            "type": "boolean",
            "optional": true,
            "description": "A flag indicating the state of a checkbox or radio item after it is clicked."
          },
          "bookmarkId": {
            "type": "string",
            "description": "The id of the bookmark where the context menu was clicked, if it was on a bookmark."
          },
          "modifiers": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": ["Shift", "Alt", "Command", "Ctrl", "MacCtrl"]
            },
            "description": "An array of keyboard modifiers that were held while the menu item was clicked."
          }
        }
      }
    ],
    "functions": [
      {
        "name": "create",
        "type": "function",
        "description": "Creates a new context menu item. Note that if an error occurs during creation, you may not find out until the creation callback fires (the details will be in $(ref:runtime.lastError)).",
        "returns": {
          "choices": [
            { "type": "integer" },
            { "type": "string" }
          ],
          "description": "The ID of the newly created item."
        },
        "parameters": [
          {
            "type": "object",
            "name": "createProperties",
            "properties": {
              "type": {
                "$ref": "ItemType",
                "optional": true,
                "description": "The type of menu item. Defaults to 'normal' if not specified."
              },
              "id": {
                "type": "string",
                "optional": true,
                "description": "The unique ID to assign to this item. Mandatory for event pages. Cannot be the same as another ID for this extension."
              },
              "icons": {
                "type": "object",
                "optional" : true,
                "patternProperties" : {
                  "^[1-9]\\d*$": { "type" : "string" }
                }
              },
              "title": {
                "type": "string",
                "optional": true,
                "description": "The text to be displayed in the item; this is <em>required</em> unless <code>type</code> is 'separator'. When the context is 'selection', you can use <code>%s</code> within the string to show the selected text. For example, if this parameter's value is \"Translate '%s' to Pig Latin\" and the user selects the word \"cool\", the context menu item for the selection is \"Translate 'cool' to Pig Latin\"."
              },
              "checked": {
                "type": "boolean",
                "optional": true,
                "description": "The initial state of a checkbox or radio item: true for selected and false for unselected. Only one radio item can be selected at a time in a given group of radio items."
              },
              "contexts": {
                "type": "array",
                "items": {
                  "$ref": "ContextType"
                },
                "minItems": 1,
                "optional": true,
                "description": "List of contexts this menu item will appear in. Defaults to ['page'] if not specified."
              },
              "onclick": {
                "type": "function",
                "optional": true,
                "description": "A function that will be called back when the menu item is clicked. Event pages cannot use this; instead, they should register a listener for $(ref:contextMenus.onClicked).",
                "parameters": [
                  {
                    "name": "info",
                    "$ref": "contextMenusInternal.OnClickData",
                    "description": "Information about the item clicked and the context where the click happened."
                  },
                  {
                    "name": "tab",
                    "$ref": "tabs.Tab",
                    "description": "The details of the tab where the click took place. Note: this parameter only present for extensions."
                  }
                ]
              },
              "parentId": {
                "choices": [
                  { "type": "integer" },
                  { "type": "string" }
                ],
                "optional": true,
                "description": "The ID of a parent menu item; this makes the item a child of a previously added item."
              },
              "documentUrlPatterns": {
                "type": "array",
                "items": {"type": "string"},
                "optional": true,
                "description": "Lets you restrict the item to apply only to documents whose URL matches one of the given patterns. (This applies to frames as well.) For details on the format of a pattern, see $(topic:match_patterns)[Match Patterns]."
              },
              "targetUrlPatterns": {
                "type": "array",
                "items": {"type": "string"},
                "optional": true,
                "description": "Similar to documentUrlPatterns, but lets you filter based on the src attribute of img/audio/video tags and the href of anchor tags."
              },
              "enabled": {
                "type": "boolean",
                "optional": true,
                "description": "Whether this context menu item is enabled or disabled. Defaults to true."
              },
              "command": {
                "type": "string",
                "optional": true,
                "description": "Specifies a command to issue for the context click.  Currently supports internal commands _execute_page_action, _execute_browser_action and _execute_sidebar_action."
              }
            }
          },
          {
            "type": "function",
            "name": "callback",
            "optional": true,
            "description": "Called when the item has been created in the browser. If there were any problems creating the item, details will be available in $(ref:runtime.lastError).",
            "parameters": []
          }
        ]
      },
      {
        "name": "update",
        "type": "function",
        "description": "Updates a previously created context menu item.",
        "async": "callback",
        "parameters": [
          {
            "choices": [
              { "type": "integer" },
              { "type": "string" }
            ],
            "name": "id",
            "description": "The ID of the item to update."
          },
          {
            "type": "object",
            "name": "updateProperties",
            "description": "The properties to update. Accepts the same values as the create function.",
            "properties": {
              "type": {
                "$ref": "ItemType",
                "optional": true
              },
              "title": {
                "type": "string",
                "optional": true
              },
              "checked": {
                "type": "boolean",
                "optional": true
              },
              "contexts": {
                "type": "array",
                "items": {
                  "$ref": "ContextType"
                },
                "minItems": 1,
                "optional": true
              },
              "onclick": {
                "type": "function",
                "optional": "omit-key-if-missing",
                "parameters": [
                  {
                    "name": "info",
                    "$ref": "menusInternal.OnClickData"
                  },
                  {
                    "name": "tab",
                    "$ref": "tabs.Tab",
                    "description": "The details of the tab where the click took place. Note: this parameter only present for extensions."
                  }
                ]
              },
              "parentId": {
                "choices": [
                  { "type": "integer" },
                  { "type": "string" }
                ],
                "optional": true,
                "description": "Note: You cannot change an item to be a child of one of its own descendants."
              },
              "documentUrlPatterns": {
                "type": "array",
                "items": {"type": "string"},
                "optional": true
              },
              "targetUrlPatterns": {
                "type": "array",
                "items": {"type": "string"},
                "optional": true
              },
              "enabled": {
                "type": "boolean",
                "optional": true
              }
            }
          },
          {
            "type": "function",
            "name": "callback",
            "optional": true,
            "parameters": [],
            "description": "Called when the context menu has been updated."
          }
        ]
      },
      {
        "name": "remove",
        "type": "function",
        "description": "Removes a context menu item.",
        "async": "callback",
        "parameters": [
          {
            "choices": [
              { "type": "integer" },
              { "type": "string" }
            ],
            "name": "menuItemId",
            "description": "The ID of the context menu item to remove."
          },
          {
            "type": "function",
            "name": "callback",
            "optional": true,
            "parameters": [],
            "description": "Called when the context menu has been removed."
          }
        ]
      },
      {
        "name": "removeAll",
        "type": "function",
        "description": "Removes all context menu items added by this extension.",
        "async": "callback",
        "parameters": [
          {
            "type": "function",
            "name": "callback",
            "optional": true,
            "parameters": [],
            "description": "Called when removal is complete."
          }
        ]
      },
      {
        "name": "refresh",
        "type": "function",
        "description": "Updates the extension items in the shown menu, including changes that have been made since the menu was shown. Has no effect if the menu is hidden. Rebuilding a shown menu is an expensive operation, only invoke this method when necessary.",
        "async": true,
        "parameters": []
      }
    ],
    "events": [
      {
        "name": "onClicked",
        "type": "function",
        "description": "Fired when a context menu item is clicked.",
        "parameters": [
          {
            "name": "info",
            "$ref": "OnClickData",
            "description": "Information about the item clicked and the context where the click happened."
          },
          {
            "name": "tab",
            "$ref": "tabs.Tab",
            "description": "The details of the tab where the click took place. If the click did not take place in a tab, this parameter will be missing.",
            "optional": true
          }
        ]
      },
      {
        "name": "onShown",
        "type": "function",
        "description": "Fired when a menu is shown. The extension can add, modify or remove menu items and call menus.refresh() to update the menu.",
        "parameters": [
          {
            "name": "info",
            "type": "object",
            "description": "Information about the context of the menu action and the created menu items. For more information about each property, see contextMenusInternal.OnClickData. The following properties are only set if the extension has host permissions for the given context: linkUrl, linkText, srcUrl, pageUrl, frameUrl, selectionText.",
            "properties": {
              "menuIds": {
                "description": "A list of IDs of the menu items that were shown.",
                "type": "array",
                "items": {
                  "choices": [
                    { "type": "integer" },
                    { "type": "string" }
                  ]
                }
              },
              "contexts": {
                "description": "A list of all contexts that apply to the menu.",
                "type": "array",
                "items": {"$ref": "ContextType"}
              },
              "editable": {
                "type": "boolean"
              },
              "mediaType": {
                "type": "string",
                "optional": true
              },
              "linkUrl": {
                "type": "string",
                "optional": true
              },
              "linkText": {
                "type": "string",
                "optional": true
              },
              "srcUrl": {
                "type": "string",
                "optional": true
              },
              "pageUrl": {
                "type": "string",
                "optional": true
              },
              "frameUrl": {
                "type": "string",
                "optional": true
              },
              "selectionText": {
                "type": "string",
                "optional": true
              }
            }
          },
          {
            "name": "tab",
            "$ref": "tabs.Tab",
            "description": "The details of the tab where the menu was opened."
          }
        ]
      },
      {
        "name": "onHidden",
        "type": "function",
        "description": "Fired when a menu is hidden. This event is only fired if onShown has fired before.",
        "parameters": []
      }
    ]
  }
]
PK
!<���w��:chrome/browser/content/browser/schemas/menus_internal.json// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

[
  {
    "namespace": "menusInternal",
    "allowedContexts": ["addon_parent_only"],
    "description": "Use the <code>browser.contextMenus</code> API to add items to the browser's context menu. You can choose what types of objects your context menu additions apply to, such as images, hyperlinks, and pages.",
    "types": [
      {
        "id": "OnClickData",
        "type": "object",
        "description": "Information sent when a context menu item is clicked.",
        "properties": {
          "menuItemId": {
            "choices": [
              { "type": "integer" },
              { "type": "string" }
            ],
            "description": "The ID of the menu item that was clicked."
          },
          "parentMenuItemId": {
            "choices": [
              { "type": "integer" },
              { "type": "string" }
            ],
            "optional": true,
            "description": "The parent ID, if any, for the item clicked."
          },
          "mediaType": {
            "type": "string",
            "optional": true,
            "description": "One of 'image', 'video', or 'audio' if the context menu was activated on one of these types of elements."
          },
          "linkUrl": {
            "type": "string",
            "optional": true,
            "description": "If the element is a link, the URL it points to."
          },
          "srcUrl": {
            "type": "string",
            "optional": true,
            "description": "Will be present for elements with a 'src' URL."
          },
          "pageUrl": {
            "type": "string",
            "optional": true,
            "description": "The URL of the page where the menu item was clicked. This property is not set if the click occured in a context where there is no current page, such as in a launcher context menu."
          },
          "frameUrl": {
            "type": "string",
            "optional": true,
            "description": " The URL of the frame of the element where the context menu was clicked, if it was in a frame."
          },
          "selectionText": {
            "type": "string",
            "optional": true,
            "description": "The text for the context selection, if any."
          },
          "editable": {
            "type": "boolean",
            "description": "A flag indicating whether the element is editable (text input, textarea, etc.)."
          },
          "wasChecked": {
            "type": "boolean",
            "optional": true,
            "description": "A flag indicating the state of a checkbox or radio item before it was clicked."
          },
          "checked": {
            "type": "boolean",
            "optional": true,
            "description": "A flag indicating the state of a checkbox or radio item after it is clicked."
          }
        }
      }
    ]
  }
]
PK
!<'2����3chrome/browser/content/browser/schemas/omnibox.json// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
[
  {
    "namespace": "manifest",
    "types": [
      {
        "$extend": "WebExtensionManifest",
        "properties": {
          "omnibox": {
            "type": "object",
            "additionalProperties": { "$ref": "UnrecognizedProperty" },
            "properties": {
              "keyword": {
                "type": "string",
                "pattern": "^[^?\\s:]([^\\s:]*[^/\\s:])?$"
              }
            },
            "optional": true
          }
        }
      }
    ]
  },
  {
    "namespace": "omnibox",
    "description": "The omnibox API allows you to register a keyword with Firefox's address bar.",
    "permissions": ["manifest:omnibox"],
    "types": [
      {
        "id": "DescriptionStyleType",
        "type": "string",
        "description": "The style type.",
        "enum": ["url", "match", "dim"]
      },
      {
        "id": "OnInputEnteredDisposition",
        "type": "string",
        "enum": ["currentTab", "newForegroundTab", "newBackgroundTab"],
        "description": "The window disposition for the omnibox query. This is the recommended context to display results. For example, if the omnibox command is to navigate to a certain URL, a disposition of 'newForegroundTab' means the navigation should take place in a new selected tab."
      },
      {
        "id": "SuggestResult",
        "type": "object",
        "description": "A suggest result.",
        "properties": {
          "content": {
            "type": "string",
            "minLength": 1,
            "description": "The text that is put into the URL bar, and that is sent to the extension when the user chooses this entry."
          },
          "description": {
            "type": "string",
            "minLength": 1,
            "description": "The text that is displayed in the URL dropdown. Can contain XML-style markup for styling. The supported tags are 'url' (for a literal URL), 'match' (for highlighting text that matched what the user's query), and 'dim' (for dim helper text). The styles can be nested, eg. <dim><match>dimmed match</match></dim>. You must escape the five predefined entities to display them as text: stackoverflow.com/a/1091953/89484 "
          },
          "descriptionStyles": {
            "optional": true,
            "unsupported": true,
            "type": "array",
            "description": "An array of style ranges for the description, as provided by the extension.",
            "items": {
              "type": "object",
              "description": "The style ranges for the description, as provided by the extension.",
              "properties": {
                "offset": { "type": "integer" },
                "type": { "description": "The style type", "$ref": "DescriptionStyleType"},
                "length": { "type": "integer", "optional": true }
              }
            }
          },
          "descriptionStylesRaw": {
            "optional": true,
            "unsupported": true,
            "type": "array",
            "description": "An array of style ranges for the description, as provided by ToValue().",
            "items": {
              "type": "object",
              "description": "The style ranges for the description, as provided by ToValue().",
              "properties": {
                "offset": { "type": "integer" },
                "type": { "type": "integer" }
              }
            }
          }
        }
      },
      {
        "id": "DefaultSuggestResult",
        "type": "object",
        "description": "A suggest result.",
        "properties": {
          "description": {
            "type": "string",
            "minLength": 1,
            "description": "The text that is displayed in the URL dropdown."
          },
          "descriptionStyles": {
            "optional": true,
            "unsupported": true,
            "type": "array",
            "description": "An array of style ranges for the description, as provided by the extension.",
            "items": {
              "type": "object",
              "description": "The style ranges for the description, as provided by the extension.",
              "properties": {
                "offset": { "type": "integer" },
                "type": { "description": "The style type", "$ref": "DescriptionStyleType"},
                "length": { "type": "integer", "optional": true }
              }
            }
          },
          "descriptionStylesRaw": {
            "optional": true,
            "unsupported": true,
            "type": "array",
            "description": "An array of style ranges for the description, as provided by ToValue().",
            "items": {
              "type": "object",
              "description": "The style ranges for the description, as provided by ToValue().",
              "properties": {
                "offset": { "type": "integer" },
                "type": { "type": "integer" }
              }
            }
          }
        }
      }
    ],
    "functions": [
      {
        "name": "setDefaultSuggestion",
        "type": "function",
        "description": "Sets the description and styling for the default suggestion. The default suggestion is the text that is displayed in the first suggestion row underneath the URL bar.",
        "parameters": [
          {
            "name": "suggestion",
            "$ref": "DefaultSuggestResult",
            "description": "A partial SuggestResult object, without the 'content' parameter."
          }
        ]
      }
    ],
    "events": [
      {
        "name": "onInputStarted",
        "type": "function",
        "description": "User has started a keyword input session by typing the extension's keyword. This is guaranteed to be sent exactly once per input session, and before any onInputChanged events.",
        "parameters": []
      },
      {
        "name": "onInputChanged",
        "type": "function",
        "description": "User has changed what is typed into the omnibox.",
        "parameters": [
          {
            "type": "string",
            "name": "text"
          },
          {
            "name": "suggest",
            "type": "function",
            "description": "A callback passed to the onInputChanged event used for sending suggestions back to the browser.",
            "parameters": [
              {
                "name": "suggestResults",
                "type": "array",
                "description": "Array of suggest results",
                "items": {
                  "$ref": "SuggestResult"
                }
              }
            ]
          }
        ]
      },
      {
        "name": "onInputEntered",
        "type": "function",
        "description": "User has accepted what is typed into the omnibox.",
        "parameters": [
          {
            "type": "string",
            "name": "text"
          },
          {
            "name": "disposition",
            "$ref": "OnInputEnteredDisposition"
          }
        ]
      },
      {
        "name": "onInputCancelled",
        "type": "function",
        "description": "User has ended the keyword input session without accepting the input.",
        "parameters": []
      }
    ]
  }
]
PK
!<���l�&�&7chrome/browser/content/browser/schemas/page_action.json// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

[
  {
    "namespace": "manifest",
    "types": [
      {
        "$extend": "WebExtensionManifest",
        "properties": {
          "page_action": {
            "type": "object",
            "additionalProperties": { "$ref": "UnrecognizedProperty" },
            "properties": {
              "default_title": {
                "type": "string",
                "optional": true,
                "preprocess": "localize"
              },
              "default_icon": {
                "$ref": "IconPath",
                "optional": true
              },
              "default_popup": {
                "type": "string",
                "format": "relativeUrl",
                "optional": true,
                "preprocess": "localize"
              },
              "browser_style": {
                "type": "boolean",
                "optional": true
              },
              "show_matches": {
                "type": "array",
                "optional": true,
                "minItems": 1,
                "items": { "$ref": "MatchPattern" }
              },
              "hide_matches": {
                "type": "array",
                "optional": true,
                "minItems": 1,
                "items": { "$ref": "MatchPatternRestricted" }
              }
            },
            "optional": true
          }
        }
      }
    ]
  },
  {
    "namespace": "pageAction",
    "description": "Use the <code>browser.pageAction</code> API to put icons inside the address bar. Page actions represent actions that can be taken on the current page, but that aren't applicable to all pages.",
    "permissions": ["manifest:page_action"],
    "types": [
      {
        "id": "ImageDataType",
        "type": "object",
        "isInstanceOf": "ImageData",
        "additionalProperties": { "type": "any" },
        "postprocess": "convertImageDataToURL",
        "description": "Pixel data for an image. Must be an ImageData object (for example, from a <code>canvas</code> element)."
      }
    ],
    "functions": [
      {
        "name": "show",
        "type": "function",
        "async": "callback",
        "description": "Shows the page action. The page action is shown whenever the tab is selected.",
        "parameters": [
          {"type": "integer", "name": "tabId", "minimum": 0, "description": "The id of the tab for which you want to modify the page action."},
          {
            "type": "function",
            "name": "callback",
            "optional": true,
            "parameters": []
          }
        ]
      },
      {
        "name": "hide",
        "type": "function",
        "async": "callback",
        "description": "Hides the page action.",
        "parameters": [
          {"type": "integer", "name": "tabId", "minimum": 0, "description": "The id of the tab for which you want to modify the page action."},
          {
            "type": "function",
            "name": "callback",
            "optional": true,
            "parameters": []
          }
        ]
      },
      {
        "name": "isShown",
        "type": "function",
        "description": "Checks whether the page action is shown.",
        "async": true,
        "parameters": [
          {
            "name": "details",
            "type": "object",
            "properties": {
              "tabId": {
                "type": "integer",
                "description": "Specify the tab to get the shownness from."
              }
            }
          }
        ]
      },
      {
        "name": "setTitle",
        "type": "function",
        "description": "Sets the title of the page action. This is displayed in a tooltip over the page action.",
        "parameters": [
          {
            "name": "details",
            "type": "object",
            "properties": {
              "tabId": {"type": "integer", "minimum": 0, "description": "The id of the tab for which you want to modify the page action."},
              "title": {
                "choices": [
                  {"type": "string"},
                  {"type": "null"}
                ],
                "description": "The tooltip string."
              }
            }
          }
        ]
      },
      {
        "name": "getTitle",
        "type": "function",
        "description": "Gets the title of the page action.",
        "async": "callback",
        "parameters": [
          {
            "name": "details",
            "type": "object",
            "properties": {
              "tabId": {
                "type": "integer",
                "description": "Specify the tab to get the title from."
              }
            }
          },
          {
            "type": "function",
            "name": "callback",
            "parameters": [
              {
                "name": "result",
                "type": "string"
              }
            ]
          }
        ]
      },
      {
        "name": "setIcon",
        "type": "function",
        "description": "Sets the icon for the page action. The icon can be specified either as the path to an image file or as the pixel data from a canvas element, or as dictionary of either one of those. Either the <b>path</b> or the <b>imageData</b> property must be specified.",
        "async": "callback",
        "parameters": [
          {
            "name": "details",
            "type": "object",
            "properties": {
              "tabId": {"type": "integer", "minimum": 0, "description": "The id of the tab for which you want to modify the page action."},
              "imageData": {
                "choices": [
                  { "$ref": "ImageDataType" },
                  {
                    "type": "object",
                    "patternProperties": {
                      "^[1-9]\\d*$": {"$ref": "ImageDataType"}
                    }
                  }
                ],
                "optional": true,
                "description": "Either an ImageData object or a dictionary {size -> ImageData} representing icon to be set. If the icon is specified as a dictionary, the actual image to be used is chosen depending on screen's pixel density. If the number of image pixels that fit into one screen space unit equals <code>scale</code>, then image with size <code>scale</code> * 19 will be selected. Initially only scales 1 and 2 will be supported. At least one image must be specified. Note that 'details.imageData = foo' is equivalent to 'details.imageData = {'19': foo}'"
              },
              "path": {
                "choices": [
                  { "type": "string" },
                  {
                    "type": "object",
                    "patternProperties": {
                      "^[1-9]\\d*$": { "type": "string" }
                    }
                  }
                ],
                "optional": true,
                "description": "Either a relative image path or a dictionary {size -> relative image path} pointing to icon to be set. If the icon is specified as a dictionary, the actual image to be used is chosen depending on screen's pixel density. If the number of image pixels that fit into one screen space unit equals <code>scale</code>, then image with size <code>scale</code> * 19 will be selected. Initially only scales 1 and 2 will be supported. At least one image must be specified. Note that 'details.path = foo' is equivalent to 'details.imageData = {'19': foo}'"
              }
            }
          },
          {
            "type": "function",
            "name": "callback",
            "optional": true,
            "parameters": []
          }
        ]
      },
      {
        "name": "setPopup",
        "type": "function",
        "async": true,
        "description": "Sets the html document to be opened as a popup when the user clicks on the page action's icon.",
        "parameters": [
          {
            "name": "details",
            "type": "object",
            "properties": {
              "tabId": {"type": "integer", "minimum": 0, "description": "The id of the tab for which you want to modify the page action."},
              "popup": {
                "choices": [
                  {"type": "string"},
                  {"type": "null"}
                ],
                "description": "The html file to show in a popup.  If set to the empty string (''), no popup is shown."
              }
            }
          }
        ]
      },
      {
        "name": "getPopup",
        "type": "function",
        "description": "Gets the html document set as the popup for this page action.",
        "async": "callback",
        "parameters": [
          {
            "name": "details",
            "type": "object",
            "properties": {
              "tabId": {
                "type": "integer",
                "description": "Specify the tab to get the popup from."
              }
            }
          },
          {
            "type": "function",
            "name": "callback",
            "parameters": [
              {
                "name": "result",
                "type": "string"
              }
            ]
          }
        ]
      },
      {
        "name": "openPopup",
        "type": "function",
        "requireUserInput": true,
        "description": "Opens the extension page action in the active window.",
        "async": true,
        "parameters": []
      }
    ],
    "events": [
      {
        "name": "onClicked",
        "type": "function",
        "description": "Fired when a page action icon is clicked.  This event will not fire if the page action has a popup.",
        "parameters": [
          {
            "name": "tab",
            "$ref": "tabs.Tab"
          }
        ]
      }
    ]
  }
]
PK
!<�ZQC��2chrome/browser/content/browser/schemas/pkcs11.json[
  {
    "namespace": "manifest",
    "types": [
      {
        "$extend": "Permission",
        "choices": [{
          "type": "string",
          "enum": [
            "pkcs11"
          ]
        }]
      }
    ]
  },
  {
    "namespace": "pkcs11",
    "description": "PKCS#11 module management API",
    "permissions": ["pkcs11"],
    "functions": [
      {
        "name": "isModuleInstalled",
        "type": "function",
        "description": "checks whether a PKCS#11 module, given by name, is installed",
        "async": true,
        "parameters": [
          {
            "name": "name",
            "type": "string"
          }
        ]
      },
      {
        "name": "installModule",
        "type": "function",
        "description": "Install a PKCS#11 module with a given name",
        "async": true,
        "parameters": [
          {
            "name": "name",
            "type": "string"
          },
          {
            "name": "flags",
            "type": "integer",
            "optional": true
          }
        ]
      },
      {
        "name": "uninstallModule",
        "type": "function",
        "description": "Remove an installed PKCS#11 module from firefox",
        "async": true,
        "parameters": [
          {
            "name": "name",
            "type": "string"
          }
        ]
      },
      {
        "name": "getModuleSlots",
        "type": "function",
        "description": "Enumerate a module's slots, each with their name and whether a token is present",
        "async": true,
        "parameters": [
          {
            "name": "name",
            "type": "string"
          }
        ]
      }
    ]
  }
]
PK
!<O�t��&�&4chrome/browser/content/browser/schemas/sessions.json// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

[
  {
    "namespace": "manifest",
    "types": [
      {
        "$extend": "Permission",
        "choices": [{
          "type": "string",
          "enum": [
            "sessions"
          ]
        }]
      }
    ]
  },
  {
    "namespace": "sessions",
    "description": "Use the <code>chrome.sessions</code> API to query and restore tabs and windows from a browsing session.",
    "permissions": ["sessions"],
    "types": [
      {
        "id": "Filter",
        "type": "object",
        "properties": {
          "maxResults": {
            "type": "integer",
            "minimum": 0,
            "maximum": 25,
            "optional": true,
            "description": "The maximum number of entries to be fetched in the requested list. Omit this parameter to fetch the maximum number of entries ($(ref:sessions.MAX_SESSION_RESULTS))."
          }
        }
      },
      {
        "id": "Session",
        "type": "object",
        "properties": {
          "lastModified": {"type": "integer", "description": "The time when the window or tab was closed or modified, represented in milliseconds since the epoch."},
          "tab": {"$ref": "tabs.Tab", "optional": true, "description": "The $(ref:tabs.Tab), if this entry describes a tab. Either this or $(ref:sessions.Session.window) will be set."},
          "window": {"$ref": "windows.Window", "optional": true, "description": "The $(ref:windows.Window), if this entry describes a window. Either this or $(ref:sessions.Session.tab) will be set."}
        }
      },
      {
        "id": "Device",
        "type": "object",
        "properties": {
          "info": {"type": "string"},
          "deviceName": {"type": "string", "description": "The name of the foreign device."},
          "sessions": {"type": "array", "items": {"$ref": "Session"}, "description": "A list of open window sessions for the foreign device, sorted from most recently to least recently modified session."}
        }
      }
    ],
    "functions": [
      {
        "name": "forgetClosedTab",
        "type": "function",
        "description": "Forget a recently closed tab.",
        "async": true,
        "parameters": [
          {
            "name": "windowId",
            "type": "integer",
            "description": "The windowId of the window to which the recently closed tab to be forgotten belongs."
          },
          {
            "name": "sessionId",
            "type": "string",
            "description": "The sessionId (closedId) of the recently closed tab to be forgotten."
          }
        ]
      },
      {
        "name": "forgetClosedWindow",
        "type": "function",
        "description": "Forget a recently closed window.",
        "async": true,
        "parameters": [
          {
            "name": "sessionId",
            "type": "string",
            "description": "The sessionId (closedId) of the recently closed window to be forgotten."
          }
        ]
      },
      {
        "name": "getRecentlyClosed",
        "type": "function",
        "description": "Gets the list of recently closed tabs and/or windows.",
        "async": "callback",
        "parameters": [
          {
            "$ref": "Filter",
            "name": "filter",
            "optional": true,
            "default": {}
          },
          {
            "type": "function",
            "name": "callback",
            "parameters": [
              {
                "name": "sessions", "type": "array", "items": { "$ref": "Session" }, "description": "The list of closed entries in reverse order that they were closed (the most recently closed tab or window will be at index <code>0</code>). The entries may contain either tabs or windows."
              }
            ]
          }
        ]
      },
      {
        "name": "getDevices",
        "unsupported": true,
        "type": "function",
        "description": "Retrieves all devices with synced sessions.",
        "async": "callback",
        "parameters": [
          {
            "$ref": "Filter",
            "name": "filter",
            "optional": true
          },
          {
            "type": "function",
            "name": "callback",
            "parameters": [
              {
                "name": "devices", "type": "array", "items": { "$ref": "Device" }, "description": "The list of $(ref:sessions.Device) objects for each synced session, sorted in order from device with most recently modified session to device with least recently modified session. $(ref:tabs.Tab) objects are sorted by recency in the $(ref:windows.Window) of the $(ref:sessions.Session) objects."
              }
            ]
          }
        ]
      },
      {
        "name": "restore",
        "type": "function",
        "description": "Reopens a $(ref:windows.Window) or $(ref:tabs.Tab), with an optional callback to run when the entry has been restored.",
        "async": "callback",
        "parameters": [
          {
            "type": "string",
            "name": "sessionId",
            "optional": true,
            "description": "The $(ref:windows.Window.sessionId), or $(ref:tabs.Tab.sessionId) to restore. If this parameter is not specified, the most recently closed session is restored."
          },
          {
            "type": "function",
            "name": "callback",
            "optional": true,
            "parameters": [
              {
                "$ref": "Session",
                "name": "restoredSession",
                "description": "A $(ref:sessions.Session) containing the restored $(ref:windows.Window) or $(ref:tabs.Tab) object."
              }
            ]
          }
        ]
      },
      {
        "name": "setTabValue",
        "type": "function",
        "description": "Set a key/value pair on a given tab.",
        "async": true,
        "parameters": [
          {
            "type": "integer",
            "minimum": 0,
            "name": "tabId",
            "description": "The id of the tab that the key/value pair is being set on."
          },
          {
            "type": "string",
            "name": "key",
            "description": "The key which corresponds to the value being set."
          },
          {
            "type": "any",
            "name": "value",
            "description": "The value being set."
          }
        ]
      },
      {
        "name": "getTabValue",
        "type": "function",
        "description": "Retrieve a value that was set for a given key on a given tab.",
        "async": true,
        "parameters": [
          {
            "type": "integer",
            "minimum": 0,
            "name": "tabId",
            "description": "The id of the tab whose value is being retrieved from."
          },
          {
            "type": "string",
            "name": "key",
            "description": "The key which corresponds to the value."
          }
        ]
      },
      {
        "name": "removeTabValue",
        "type": "function",
        "description": "Remove a key/value pair that was set on a given tab.",
        "async": true,
        "parameters": [
          {
            "type": "integer",
            "minimum": 0,
            "name": "tabId",
            "description": "The id of the tab whose key/value pair is being removed."
          },
          {
            "type": "string",
            "name": "key",
            "description": "The key which corresponds to the value."
          }
        ]
      },
      {
        "name": "setWindowValue",
        "type": "function",
        "description": "Set a key/value pair on a given window.",
        "async": true,
        "parameters": [
          {
            "type": "integer",
            "minimum": -2,
            "name": "windowId",
            "description": "The id of the window that the key/value pair is being set on."
          },
          {
            "type": "string",
            "name": "key",
            "description": "The key which corresponds to the value being set."
          },
          {
            "type": "any",
            "name": "value",
            "description": "The value being set."
          }
        ]
      },
      {
        "name": "getWindowValue",
        "type": "function",
        "description": "Retrieve a value that was set for a given key on a given window.",
        "async": true,
        "parameters": [
          {
            "type": "integer",
            "minimum": -2,
            "name": "windowId",
            "description": "The id of the window whose value is being retrieved from."
          },
          {
            "type": "string",
            "name": "key",
            "description": "The key which corresponds to the value."
          }
        ]
      },
      {
        "name": "removeWindowValue",
        "type": "function",
        "description": "Remove a key/value pair that was set on a given window.",
        "async": true,
        "parameters": [
          {
            "type": "integer",
            "minimum": -2,
            "name": "windowId",
            "description": "The id of the window whose key/value pair is being removed."
          },
          {
            "type": "string",
            "name": "key",
            "description": "The key which corresponds to the value."
          }
        ]
      }
    ],
    "events": [
      {
        "name": "onChanged",
        "description": "Fired when recently closed tabs and/or windows are changed. This event does not monitor synced sessions changes.",
        "type": "function"
      }
    ],
    "properties": {
      "MAX_SESSION_RESULTS": {
        "value": 25,
        "description": "The maximum number of $(ref:sessions.Session) that will be included in a requested list."
      }
    }
  }
]
PK
!<䀭� � :chrome/browser/content/browser/schemas/sidebar_action.json/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

[
  {
    "namespace": "manifest",
    "types": [
      {
        "$extend": "WebExtensionManifest",
        "properties": {
          "sidebar_action": {
            "type": "object",
            "additionalProperties": { "$ref": "UnrecognizedProperty" },
            "properties": {
              "default_title": {
                "type": "string",
                "optional": true,
                "preprocess": "localize"
              },
              "default_icon": {
                "$ref": "IconPath",
                "optional": true
              },
              "browser_style": {
                "type": "boolean",
                "optional": true
              },
              "default_panel": {
                "type": "string",
                "format": "strictRelativeUrl",
                "preprocess": "localize"
              }
            },
            "optional": true
          }
        }
      }
    ]
  },
  {
    "namespace": "sidebarAction",
    "description": "Use sidebar actions to add a sidebar to Firefox.",
    "permissions": ["manifest:sidebar_action"],
    "types": [
      {
        "id": "ImageDataType",
        "type": "object",
        "isInstanceOf": "ImageData",
        "additionalProperties": { "type": "any" },
        "postprocess": "convertImageDataToURL",
        "description": "Pixel data for an image. Must be an ImageData object (for example, from a <code>canvas</code> element)."
      }
    ],
    "functions": [
      {
        "name": "setTitle",
        "type": "function",
        "description": "Sets the title of the sidebar action. This shows up in the tooltip.",
        "async": true,
        "parameters": [
          {
            "name": "details",
            "type": "object",
            "properties": {
              "title": {
                "choices": [
                  {"type": "string"},
                  {"type": "null"}
                ],
                "description": "The string the sidebar action should display when moused over."
              },
              "tabId": {
                "type": "integer",
                "optional": true,
                "description": "Sets the sidebar title for the tab specified by tabId. Automatically resets when the tab is closed."
              }
            }
          }
        ]
      },
      {
        "name": "getTitle",
        "type": "function",
        "description": "Gets the title of the sidebar action.",
        "async": true,
        "parameters": [
          {
            "name": "details",
            "type": "object",
            "properties": {
              "tabId": {
                "type": "integer",
                "optional": true,
                "description": "Specify the tab to get the title from. If no tab is specified, the non-tab-specific title is returned."
              }
            }
          }
        ]
      },
      {
        "name": "setIcon",
        "type": "function",
        "description": "Sets the icon for the sidebar action. The icon can be specified either as the path to an image file or as the pixel data from a canvas element, or as dictionary of either one of those. Either the <strong>path</strong> or the <strong>imageData</strong> property must be specified.",
        "async": true,
        "parameters": [
          {
            "name": "details",
            "type": "object",
            "properties": {
              "imageData": {
                "choices": [
                  { "$ref": "ImageDataType" },
                  {
                    "type": "object",
                    "patternProperties": {
                      "^[1-9]\\d*$": { "$ref": "ImageDataType" }
                    },
                    "additionalProperties": false
                  }
                ],
                "optional": true,
                "description": "Either an ImageData object or a dictionary {size -> ImageData} representing icon to be set. If the icon is specified as a dictionary, the actual image to be used is chosen depending on screen's pixel density. If the number of image pixels that fit into one screen space unit equals <code>scale</code>, then image with size <code>scale</code> * 19 will be selected. Initially only scales 1 and 2 will be supported. At least one image must be specified. Note that 'details.imageData = foo' is equivalent to 'details.imageData = {'19': foo}'"
              },
              "path": {
                "choices": [
                  { "type": "string" },
                  {
                    "type": "object",
                    "additionalProperties": {"type": "string"}
                  }
                ],
                "optional": true,
                "description": "Either a relative image path or a dictionary {size -> relative image path} pointing to icon to be set. If the icon is specified as a dictionary, the actual image to be used is chosen depending on screen's pixel density. If the number of image pixels that fit into one screen space unit equals <code>scale</code>, then image with size <code>scale</code> * 19 will be selected. Initially only scales 1 and 2 will be supported. At least one image must be specified. Note that 'details.path = foo' is equivalent to 'details.imageData = {'19': foo}'"
              },
              "tabId": {
                "type": "integer",
                "optional": true,
                "description": "Sets the sidebar icon for the tab specified by tabId. Automatically resets when the tab is closed."
              }
            }
          }
        ]
      },
      {
        "name": "setPanel",
        "type": "function",
        "description": "Sets the url to the html document to be opened in the sidebar when the user clicks on the sidebar action's icon.",
        "async": true,
        "parameters": [
          {
            "name": "details",
            "type": "object",
            "properties": {
              "tabId": {
                "type": "integer",
                "optional": true,
                "minimum": 0,
                "description": "Sets the sidebar url for the tab specified by tabId. Automatically resets when the tab is closed."
              },
              "panel": {
                "choices": [
                  {"type": "string"},
                  {"type": "null"}
                ],
                "description": "The url to the html file to show in a sidebar.  If set to the empty string (''), no sidebar is shown."
              }
            }
          }
        ]
      },
      {
        "name": "getPanel",
        "type": "function",
        "description": "Gets the url to the html document set as the panel for this sidebar action.",
        "async": true,
        "parameters": [
          {
            "name": "details",
            "type": "object",
            "properties": {
              "tabId": {
                "type": "integer",
                "optional": true,
                "description": "Specify the tab to get the sidebar from. If no tab is specified, the non-tab-specific sidebar is returned."
              }
            }
          }
        ]
      },
      {
        "name": "open",
        "type": "function",
        "requireUserInput": true,
        "description": "Opens the extension sidebar in the active window.",
        "async": true,
        "parameters": []
      },
      {
        "name": "close",
        "type": "function",
        "requireUserInput": true,
        "description": "Closes the extension sidebar in the active window if the sidebar belongs to the extension.",
        "async": true,
        "parameters": []
      },
      {
        "name": "isOpen",
        "type": "function",
        "description": "Checks whether the sidebar action is open.",
        "async": true,
        "parameters": [
          {
            "name": "details",
            "type": "object",
            "properties": {
              "windowId": {
                "type": "integer",
                "minimum": -2,
                "optional": true,
                "description": "Specify the window to get the openness from."
              }
            }
          }
        ]
      }
    ]
  }
]
PK
!<��X����0chrome/browser/content/browser/schemas/tabs.json// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

[
  {
    "namespace": "manifest",
    "types": [
      {
        "$extend": "OptionalPermission",
        "choices": [{
          "type": "string",
          "enum": [
            "activeTab",
            "tabs",
            "tabHide"
          ]
        }]
      }
    ]
  },
  {
    "namespace": "tabs",
    "description": "Use the <code>browser.tabs</code> API to interact with the browser's tab system. You can use this API to create, modify, and rearrange tabs in the browser.",
    "types": [
      { "id": "MutedInfoReason",
        "type": "string",
        "description": "An event that caused a muted state change.",
        "enum": [
          {"name": "user", "description": "A user input action has set/overridden the muted state."},
          {"name": "capture", "description": "Tab capture started, forcing a muted state change."},
          {"name": "extension", "description": "An extension, identified by the extensionId field, set the muted state."}
        ]
      },
      {
        "id": "MutedInfo",
        "type": "object",
        "description": "Tab muted state and the reason for the last state change.",
        "properties": {
          "muted": {
            "type": "boolean",
            "description": "Whether the tab is prevented from playing sound (but hasn't necessarily recently produced sound). Equivalent to whether the muted audio indicator is showing."
          },
          "reason": {
            "$ref": "MutedInfoReason",
            "optional": true,
            "description": "The reason the tab was muted or unmuted. Not set if the tab's mute state has never been changed."
          },
          "extensionId": {
            "type": "string",
            "optional": true,
            "description": "The ID of the extension that changed the muted state. Not set if an extension was not the reason the muted state last changed."
          }
        }
      },
      {
        "id": "SharingState",
        "type": "object",
        "description": "Tab sharing state for screen, microphone and camera.",
        "properties": {
          "screen": {
            "type": "string",
            "optional": true,
            "description": "If the tab is sharing the screen the value will be one of \"Screen\", \"Window\", or \"Application\", or undefined if not screen sharing."
          },
          "camera": {
            "type": "boolean",
            "description": "True if the tab is using the camera."
          },
          "microphone": {
            "type": "boolean",
            "description": "True if the tab is using the microphone."
          }
        }
      },
      {
        "id": "Tab",
        "type": "object",
        "properties": {
          "id": {"type": "integer", "minimum": -1, "optional": true, "description": "The ID of the tab. Tab IDs are unique within a browser session. Under some circumstances a Tab may not be assigned an ID, for example when querying foreign tabs using the $(ref:sessions) API, in which case a session ID may be present. Tab ID can also be set to $(ref:tabs.TAB_ID_NONE) for apps and devtools windows."},
          "index": {"type": "integer", "minimum": -1, "description": "The zero-based index of the tab within its window."},
          "windowId": {"type": "integer", "optional": true, "minimum": 0, "description": "The ID of the window the tab is contained within."},
          "openerTabId": {"type": "integer", "minimum": 0, "optional": true, "description": "The ID of the tab that opened this tab, if any. This property is only present if the opener tab still exists."},
          "selected": {"type": "boolean", "description": "Whether the tab is selected.", "deprecated": "Please use $(ref:tabs.Tab.highlighted).", "unsupported": true},
          "highlighted": {"type": "boolean", "description": "Whether the tab is highlighted. Works as an alias of active"},
          "active": {"type": "boolean", "description": "Whether the tab is active in its window. (Does not necessarily mean the window is focused.)"},
          "pinned": {"type": "boolean", "description": "Whether the tab is pinned."},
          "lastAccessed": {"type": "integer", "optional": true, "description": "The last time the tab was accessed as the number of milliseconds since epoch."},
          "audible": {"type": "boolean", "optional": true, "description": "Whether the tab has produced sound over the past couple of seconds (but it might not be heard if also muted). Equivalent to whether the speaker audio indicator is showing."},
          "mutedInfo": {"$ref": "MutedInfo", "optional": true, "description": "Current tab muted state and the reason for the last state change."},
          "url": {"type": "string", "optional": true, "permissions": ["tabs"], "description": "The URL the tab is displaying. This property is only present if the extension's manifest includes the <code>\"tabs\"</code> permission."},
          "title": {"type": "string", "optional": true, "permissions": ["tabs"], "description": "The title of the tab. This property is only present if the extension's manifest includes the <code>\"tabs\"</code> permission."},
          "favIconUrl": {"type": "string", "optional": true, "permissions": ["tabs"], "description": "The URL of the tab's favicon. This property is only present if the extension's manifest includes the <code>\"tabs\"</code> permission. It may also be an empty string if the tab is loading."},
          "status": {"type": "string", "optional": true, "description": "Either <em>loading</em> or <em>complete</em>."},
          "discarded": {"type": "boolean", "optional": true, "description": "True while the tab is not loaded with content."},
          "incognito": {"type": "boolean", "description": "Whether the tab is in an incognito window."},
          "width": {"type": "integer", "optional": true, "description": "The width of the tab in pixels."},
          "height": {"type": "integer", "optional": true, "description": "The height of the tab in pixels."},
          "hidden": {"type": "boolean", "optional": true, "description": "True if the tab is hidden."},
          "sessionId": {"type": "string", "optional": true, "description": "The session ID used to uniquely identify a Tab obtained from the $(ref:sessions) API."},
          "cookieStoreId": {"type": "string", "optional": true, "description": "The CookieStoreId used for the tab."},
          "isArticle": {"type": "boolean", "optional": true, "description": "Whether the document in the tab can be rendered in reader mode."},
          "isInReaderMode": {"type": "boolean", "optional": true, "description": "Whether the document in the tab is being rendered in reader mode."},
          "sharingState": {"$ref": "SharingState", "optional": true, "description": "Current tab sharing state for screen, microphone and camera."}
        }
      },
      {
        "id": "ZoomSettingsMode",
        "type": "string",
        "description": "Defines how zoom changes are handled, i.e. which entity is responsible for the actual scaling of the page; defaults to <code>automatic</code>.",
        "enum": [
          {
            "name": "automatic",
            "description": "Zoom changes are handled automatically by the browser."
          },
          {
            "name": "manual",
            "description": "Overrides the automatic handling of zoom changes. The <code>onZoomChange</code> event will still be dispatched, and it is the responsibility of the extension to listen for this event and manually scale the page. This mode does not support <code>per-origin</code> zooming, and will thus ignore the <code>scope</code> zoom setting and assume <code>per-tab</code>."
          },
          {
            "name": "disabled",
            "description": "Disables all zooming in the tab. The tab will revert to the default zoom level, and all attempted zoom changes will be ignored."
          }
        ]
      },
      {
        "id": "ZoomSettingsScope",
        "type": "string",
        "description": "Defines whether zoom changes will persist for the page's origin, or only take effect in this tab; defaults to <code>per-origin</code> when in <code>automatic</code> mode, and <code>per-tab</code> otherwise.",
        "enum": [
          {
            "name": "per-origin",
            "description": "Zoom changes will persist in the zoomed page's origin, i.e. all other tabs navigated to that same origin will be zoomed as well. Moreover, <code>per-origin</code> zoom changes are saved with the origin, meaning that when navigating to other pages in the same origin, they will all be zoomed to the same zoom factor. The <code>per-origin</code> scope is only available in the <code>automatic</code> mode."
          },
          {
            "name": "per-tab",
            "description": "Zoom changes only take effect in this tab, and zoom changes in other tabs will not affect the zooming of this tab. Also, <code>per-tab</code> zoom changes are reset on navigation; navigating a tab will always load pages with their <code>per-origin</code> zoom factors."
          }
        ]
      },
      {
        "id": "ZoomSettings",
        "type": "object",
        "description": "Defines how zoom changes in a tab are handled and at what scope.",
        "properties": {
          "mode": {
            "$ref": "ZoomSettingsMode",
            "description": "Defines how zoom changes are handled, i.e. which entity is responsible for the actual scaling of the page; defaults to <code>automatic</code>.",
            "optional": true
          },
          "scope": {
            "$ref": "ZoomSettingsScope",
            "description": "Defines whether zoom changes will persist for the page's origin, or only take effect in this tab; defaults to <code>per-origin</code> when in <code>automatic</code> mode, and <code>per-tab</code> otherwise.",
            "optional": true
          },
          "defaultZoomFactor": {
            "type": "number",
            "optional": true,
            "description": "Used to return the default zoom level for the current tab in calls to tabs.getZoomSettings."
          }
        }
      },
      {
        "id": "PageSettings",
        "type": "object",
        "description": "The page settings including: orientation, scale, background, margins, headers, footers.",
        "properties": {
          "paperSizeUnit": {
            "type": "integer",
            "optional": true,
            "description": "The page size unit: 0 = inches, 1 = millimeters. Default: 0."
          },
          "paperWidth": {
            "type": "number",
            "optional": true,
            "description": "The paper width in paper size units. Default: 8.5."
          },
          "paperHeight": {
            "type": "number",
            "optional": true,
            "description": "The paper height in paper size units. Default: 11.0."
          },
          "orientation": {
            "type": "integer",
            "optional": true,
            "description": "The page content orientation: 0 = portrait, 1 = landscape. Default: 0."
          },
          "scaling": {
            "type": "number",
            "optional": true,
            "description": "The page content scaling factor: 1.0 = 100% = normal size. Default: 1.0."
          },
          "shrinkToFit": {
            "type": "boolean",
            "optional": true,
            "description": "Whether the page content should shrink to fit the page width (overrides scaling). Default: true."
          },
          "showBackgroundColors": {
            "type": "boolean",
            "optional": true,
            "description": "Whether the page background colors should be shown. Default: false."
          },
          "showBackgroundImages": {
            "type": "boolean",
            "optional": true,
            "description": "Whether the page background images should be shown. Default: false."
          },
          "edgeLeft": {
            "type": "number",
            "optional": true,
            "description": "The spacing between the left header/footer and the left edge of the paper (inches). Default: 0."
          },
          "edgeRight": {
            "type": "number",
            "optional": true,
            "description": "The spacing between the right header/footer and the right edge of the paper (inches). Default: 0."
          },
          "edgeTop": {
            "type": "number",
            "optional": true,
            "description": "The spacing between the top of the headers and the top edge of the paper (inches). Default: 0"
          },
          "edgeBottom": {
            "type": "number",
            "optional": true,
            "description": "The spacing between the bottom of the footers and the bottom edge of the paper (inches). Default: 0."
          },
          "marginLeft": {
            "type": "number",
            "optional": true,
            "description": "The margin between the page content and the left edge of the paper (inches). Default: 0.5."
          },
          "marginRight": {
            "type": "number",
            "optional": true,
            "description": "The margin between the page content and the right edge of the paper (inches). Default: 0.5."
          },
          "marginTop": {
            "type": "number",
            "optional": true,
            "description": "The margin between the page content and the top edge of the paper (inches). Default: 0.5."
          },
          "marginBottom": {
            "type": "number",
            "optional": true,
            "description": "The margin between the page content and the bottom edge of the paper (inches). Default: 0.5."
          },
          "headerLeft": {
            "type": "string",
            "optional": true,
            "description": "The text for the page's left header. Default: '&T'."
          },
          "headerCenter": {
            "type": "string",
            "optional": true,
            "description": "The text for the page's center header. Default: ''."
          },
          "headerRight": {
            "type": "string",
            "optional": true,
            "description": "The text for the page's right header. Default: '&U'."
          },
          "footerLeft": {
            "type": "string",
            "optional": true,
            "description": "The text for the page's left footer. Default: '&PT'."
          },
          "footerCenter": {
            "type": "string",
            "optional": true,
            "description": "The text for the page's center footer. Default: ''."
          },
          "footerRight": {
            "type": "string",
            "optional": true,
            "description": "The text for the page's right footer. Default: '&D'."
          }
        }
      },
      {
        "id": "TabStatus",
        "type": "string",
        "enum": ["loading", "complete"],
        "description": "Whether the tabs have completed loading."
      },
      {
        "id": "WindowType",
        "type": "string",
        "enum": ["normal", "popup", "panel", "app", "devtools"],
        "description": "The type of window."
      }
    ],
    "properties": {
      "TAB_ID_NONE": {
        "value": -1,
        "description": "An ID which represents the absence of a browser tab."
      }
    },
    "functions": [
      {
        "name": "get",
        "type": "function",
        "description": "Retrieves details about the specified tab.",
        "async": "callback",
        "parameters": [
          {
            "type": "integer",
            "name": "tabId",
            "minimum": 0
          },
          {
            "type": "function",
            "name": "callback",
            "parameters": [
              {"name": "tab", "$ref": "Tab"}
            ]
          }
        ]
      },
      {
        "name": "getCurrent",
        "type": "function",
        "description": "Gets the tab that this script call is being made from. May be undefined if called from a non-tab context (for example: a background page or popup view).",
        "async": "callback",
        "parameters": [
          {
            "type": "function",
            "name": "callback",
            "parameters": [
              {
                "name": "tab",
                "$ref": "Tab",
                "optional": true
              }
            ]
          }
        ]
      },
      {
        "name": "connect",
        "type": "function",
        "description": "Connects to the content script(s) in the specified tab. The $(ref:runtime.onConnect) event is fired in each content script running in the specified tab for the current extension. For more details, see $(topic:messaging)[Content Script Messaging].",
        "parameters": [
          {
            "type": "integer",
            "name": "tabId",
            "minimum": 0
          },
          {
            "type": "object",
            "name": "connectInfo",
            "properties": {
              "name": { "type": "string", "optional": true, "description": "Will be passed into onConnect for content scripts that are listening for the connection event." },
              "frameId": {
                "type": "integer",
                "optional": true,
                "minimum": 0,
                "description": "Open a port to a specific $(topic:frame_ids)[frame] identified by <code>frameId</code> instead of all frames in the tab."
              }
            },
            "optional": true
          }
        ],
        "returns": {
          "$ref": "runtime.Port",
          "description": "A port that can be used to communicate with the content scripts running in the specified tab. The port's $(ref:runtime.Port) event is fired if the tab closes or does not exist. "
        }
      },
      {
        "name": "sendRequest",
        "deprecated": "Please use $(ref:runtime.sendMessage).",
        "unsupported": true,
        "type": "function",
        "description": "Sends a single request to the content script(s) in the specified tab, with an optional callback to run when a response is sent back.  The $(ref:extension.onRequest) event is fired in each content script running in the specified tab for the current extension.",
        "parameters": [
          {
            "type": "integer",
            "name": "tabId",
            "minimum": 0
          },
          {
            "type": "any",
            "name": "request"
          },
          {
            "type": "function",
            "name": "responseCallback",
            "optional": true,
            "parameters": [
              {
                "name": "response",
                "type": "any",
                "description": "The JSON response object sent by the handler of the request. If an error occurs while connecting to the specified tab, the callback will be called with no arguments and $(ref:runtime.lastError) will be set to the error message."
              }
            ]
          }
        ]
      },
      {
        "name": "sendMessage",
        "type": "function",
        "description": "Sends a single message to the content script(s) in the specified tab, with an optional callback to run when a response is sent back.  The $(ref:runtime.onMessage) event is fired in each content script running in the specified tab for the current extension.",
        "async": "responseCallback",
        "parameters": [
          {
            "type": "integer",
            "name": "tabId",
            "minimum": 0
          },
          {
            "type": "any",
            "name": "message"
          },
          {
            "type": "object",
            "name": "options",
            "properties": {
              "frameId": {
                "type": "integer",
                "optional": true,
                "minimum": 0,
                "description": "Send a message to a specific $(topic:frame_ids)[frame] identified by <code>frameId</code> instead of all frames in the tab."
              }
            },
            "optional": true
          },
          {
            "type": "function",
            "name": "responseCallback",
            "optional": true,
            "parameters": [
              {
                "name": "response",
                "type": "any",
                "description": "The JSON response object sent by the handler of the message. If an error occurs while connecting to the specified tab, the callback will be called with no arguments and $(ref:runtime.lastError) will be set to the error message."
              }
            ]
          }
        ]
      },
      {
        "name": "getSelected",
        "deprecated": "Please use $(ref:tabs.query) <code>{active: true}</code>.",
        "unsupported": true,
        "type": "function",
        "description": "Gets the tab that is selected in the specified window.",
        "async": "callback",
        "parameters": [
          {
            "type": "integer",
            "name": "windowId",
            "minimum": -2,
            "optional": true,
            "description": "Defaults to the $(topic:current-window)[current window]."
          },
          {
            "type": "function",
            "name": "callback",
            "parameters": [
              {"name": "tab", "$ref": "Tab"}
            ]
          }
        ]
      },
      {
        "name": "getAllInWindow",
        "deprecated": "Please use $(ref:tabs.query) <code>{windowId: windowId}</code>.",
        "unsupported": true,
        "type": "function",
        "description": "Gets details about all tabs in the specified window.",
        "async": "callback",
        "parameters": [
          {
            "type": "integer",
            "name": "windowId",
            "minimum": -2,
            "optional": true,
            "description": "Defaults to the $(topic:current-window)[current window]."
            },
          {
            "type": "function",
            "name": "callback",
            "parameters": [
              {"name": "tabs", "type": "array", "items": { "$ref": "Tab" } }
            ]
          }
        ]
      },
      {
        "name": "create",
        "type": "function",
        "description": "Creates a new tab.",
        "async": "callback",
        "parameters": [
          {
            "type": "object",
            "name": "createProperties",
            "properties": {
              "windowId": {
                "type": "integer",
                "minimum": -2,
                "optional": true,
                "description": "The window to create the new tab in. Defaults to the $(topic:current-window)[current window]."
              },
              "index": {
                "type": "integer",
                "minimum": 0,
                "optional": true,
                "description": "The position the tab should take in the window. The provided value will be clamped to between zero and the number of tabs in the window."
              },
              "url": {
                "type": "string",
                "optional": true,
                "description": "The URL to navigate the tab to initially. Fully-qualified URLs must include a scheme (i.e. 'http://www.google.com', not 'www.google.com'). Relative URLs will be relative to the current page within the extension. Defaults to the New Tab Page."
              },
              "active": {
                "type": "boolean",
                "optional": true,
                "description": "Whether the tab should become the active tab in the window. Does not affect whether the window is focused (see $(ref:windows.update)). Defaults to <var>true</var>."
              },
              "selected": {
                "deprecated": "Please use <em>active</em>.",
                "unsupported": true,
                "type": "boolean",
                "optional": true,
                "description": "Whether the tab should become the selected tab in the window. Defaults to <var>true</var>"
              },
              "pinned": {
                "type": "boolean",
                "optional": true,
                "description": "Whether the tab should be pinned. Defaults to <var>false</var>"
              },
              "openerTabId": {
                "type": "integer",
                "minimum": 0,
                "optional": true,
                "description": "The ID of the tab that opened this tab. If specified, the opener tab must be in the same window as the newly created tab."
              },
              "cookieStoreId": {
                "type": "string",
                "optional": true,
                "description": "The CookieStoreId for the tab that opened this tab."
              },
              "openInReaderMode": {
                "type": "boolean",
                "optional": true, "description": "Whether the document in the tab should be opened in reader mode."}
            }
          },
          {
            "type": "function",
            "name": "callback",
            "optional": true,
            "parameters": [
              {
                "name": "tab",
                "$ref": "Tab",
                "optional": true,
                "description": "Details about the created tab. Will contain the ID of the new tab."
              }
            ]
          }
        ]
      },
      {
        "name": "duplicate",
        "type": "function",
        "description": "Duplicates a tab.",
        "async": "callback",
        "parameters": [
          {
            "type": "integer",
            "name": "tabId",
            "minimum": 0,
            "description": "The ID of the tab which is to be duplicated."
          },
          {
            "type": "function",
            "name": "callback",
            "optional": true,
            "parameters": [
              {
                "name": "tab",
                "optional": true,
                "description": "Details about the duplicated tab. The $(ref:tabs.Tab) object doesn't contain <code>url</code>, <code>title</code> and <code>favIconUrl</code> if the <code>\"tabs\"</code> permission has not been requested.",
                "$ref": "Tab"
              }
            ]
          }
        ]
      },
      {
        "name": "query",
        "type": "function",
        "description": "Gets all tabs that have the specified properties, or all tabs if no properties are specified.",
        "async": "callback",
        "parameters": [
          {
            "type": "object",
            "name": "queryInfo",
            "properties": {
              "active": {
                "type": "boolean",
                "optional": true,
                "description": "Whether the tabs are active in their windows."
              },
              "pinned": {
                "type": "boolean",
                "optional": true,
                "description": "Whether the tabs are pinned."
              },
              "audible": {
                "type": "boolean",
                "optional": true,
                "description": "Whether the tabs are audible."
              },
              "muted": {
                "type": "boolean",
                "optional": true,
                "description": "Whether the tabs are muted."
              },
              "highlighted": {
                "type": "boolean",
                "optional": true,
                "description": "Whether the tabs are highlighted.  Works as an alias of active."
              },
              "currentWindow": {
                "type": "boolean",
                "optional": true,
                "description": "Whether the tabs are in the $(topic:current-window)[current window]."
              },
              "lastFocusedWindow": {
                "type": "boolean",
                "optional": true,
                "description": "Whether the tabs are in the last focused window."
              },
              "status": {
                "$ref": "TabStatus",
                "optional": true,
                "description": "Whether the tabs have completed loading."
              },
              "discarded": {
                "type": "boolean",
                "optional": true,
                "description": "True while the tabs are not loaded with content."
              },
              "hidden": {
                "type": "boolean",
                "optional": true,
                "description": "True while the tabs are hidden."
              },
              "title": {
                "type": "string",
                "optional": true,
                "description": "Match page titles against a pattern."
              },
              "url": {
                "choices": [
                  {"type": "string"},
                  {"type": "array", "items": {"type": "string"}}
                ],
                "optional": true,
                "description": "Match tabs against one or more $(topic:match_patterns)[URL patterns]. Note that fragment identifiers are not matched."
              },
              "windowId": {
                "type": "integer",
                "optional": true,
                "minimum": -2,
                "description": "The ID of the parent window, or $(ref:windows.WINDOW_ID_CURRENT) for the $(topic:current-window)[current window]."
              },
              "windowType": {
                "$ref": "WindowType",
                "optional": true,
                "description": "The type of window the tabs are in."
              },
              "index": {
                "type": "integer",
                "optional": true,
                "minimum": 0,
                "description": "The position of the tabs within their windows."
              },
              "cookieStoreId": {
                "type": "string",
                "optional": true,
                "description": "The CookieStoreId used for the tab."
              },
              "openerTabId": {
                "type": "integer",
                "minimum": 0,
                "optional": true,
                "description": "The ID of the tab that opened this tab. If specified, the opener tab must be in the same window as this tab."
              },
              "screen": {
                "choices": [
                  {"type": "string", "enum": ["Screen", "Window", "Application"]},
                  {"type": "boolean"}
                ],
                "optional": true,
                "description": "True for any screen sharing, or a string to specify type of screen sharing."
              },
              "camera": {
                "type": "boolean",
                "optional": true,
                "description": "True if the tab is using the camera."
              },
              "microphone": {
                "type": "boolean",
                "optional": true,
                "description": "True if the tab is using the microphone."
              }
            }
          },
          {
            "type": "function",
            "name": "callback",
            "parameters": [
              {
                "name": "result",
                "type": "array",
                "items": {
                  "$ref": "Tab"
                }
              }
            ]
          }
        ]
      },
      {
        "name": "highlight",
        "type": "function",
        "description": "Highlights the given tabs.",
        "async": "callback",
        "unsupported": "true",
        "parameters": [
          {
            "type": "object",
            "name": "highlightInfo",
            "properties": {
               "windowId": {
                 "type": "integer",
                 "optional": true,
                 "description": "The window that contains the tabs.",
                 "minimum": -2
               },
               "tabs": {
                 "description": "One or more tab indices to highlight.",
                 "choices": [
                   {"type": "array", "items": {"type": "integer", "minimum": 0}},
                   {"type": "integer"}
                 ]
               }
             }
           },
           {
             "type": "function",
             "name": "callback",
             "optional": true,
             "parameters": [
               {
                 "name": "window",
                 "$ref": "windows.Window",
                 "description": "Contains details about the window whose tabs were highlighted."
               }
             ]
           }
        ]
      },
      {
        "name": "update",
        "type": "function",
        "description": "Modifies the properties of a tab. Properties that are not specified in <var>updateProperties</var> are not modified.",
        "async": "callback",
        "parameters": [
          {
            "type": "integer",
            "name": "tabId",
            "minimum": 0,
            "optional": true,
            "description": "Defaults to the selected tab of the $(topic:current-window)[current window]."
          },
          {
            "type": "object",
            "name": "updateProperties",
            "properties": {
              "url": {
                "type": "string",
                "optional": true,
                "description": "A URL to navigate the tab to."
              },
              "active": {
                "type": "boolean",
                "optional": true,
                "description": "Whether the tab should be active. Does not affect whether the window is focused (see $(ref:windows.update))."
              },
              "highlighted": {
                "unsupported": true,
                "type": "boolean",
                "optional": true,
                "description": "Adds or removes the tab from the current selection."
              },
              "selected": {
                "unsupported": true,
                "deprecated": "Please use <em>highlighted</em>.",
                "type": "boolean",
                "optional": true,
                "description": "Whether the tab should be selected."
              },
              "pinned": {
                "type": "boolean",
                "optional": true,
                "description": "Whether the tab should be pinned."
              },
              "muted": {
                "type": "boolean",
                "optional": true,
                "description": "Whether the tab should be muted."
              },
              "openerTabId": {
                "type": "integer",
                "minimum": 0,
                "optional": true,
                "description": "The ID of the tab that opened this tab. If specified, the opener tab must be in the same window as this tab."
              },
              "loadReplace": {
                "type": "boolean",
                "optional": true,
                "description": "Whether the load should replace the current history entry for the tab."
              }
            }
          },
          {
            "type": "function",
            "name": "callback",
            "optional": true,
            "parameters": [
              {
                "name": "tab",
                "$ref": "Tab",
                "optional": true,
                "description": "Details about the updated tab. The $(ref:tabs.Tab) object doesn't contain <code>url</code>, <code>title</code> and <code>favIconUrl</code> if the <code>\"tabs\"</code> permission has not been requested."
              }
            ]
          }
        ]
      },
      {
        "name": "move",
        "type": "function",
        "description": "Moves one or more tabs to a new position within its window, or to a new window. Note that tabs can only be moved to and from normal (window.type === \"normal\") windows.",
        "async": "callback",
        "parameters": [
          {
            "name": "tabIds",
            "description": "The tab or list of tabs to move.",
            "choices": [
              {"type": "integer", "minimum": 0},
              {"type": "array", "items": {"type": "integer", "minimum": 0}}
            ]
          },
          {
            "type": "object",
            "name": "moveProperties",
            "properties": {
              "windowId": {
                "type": "integer",
                "minimum": -2,
                "optional": true,
                "description": "Defaults to the window the tab is currently in."
              },
              "index": {
                "type": "integer",
                "minimum": -1,
                "description": "The position to move the window to. -1 will place the tab at the end of the window."
              }
            }
          },
          {
            "type": "function",
            "name": "callback",
            "optional": true,
            "parameters": [
              {
                "name": "tabs",
                "description": "Details about the moved tabs.",
                "choices": [
                  {"$ref": "Tab"},
                  {"type": "array", "items": {"$ref": "Tab"}}
                ]
              }
            ]
          }
        ]
      },
      {
        "name": "reload",
        "type": "function",
        "description": "Reload a tab.",
        "async": "callback",
        "parameters": [
          {
            "type": "integer",
            "name": "tabId",
            "minimum": 0,
            "optional": true,
            "description": "The ID of the tab to reload; defaults to the selected tab of the current window."
          },
          {
            "type": "object",
            "name": "reloadProperties",
            "optional": true,
            "properties": {
              "bypassCache": {
                "type": "boolean",
                "optional": true,
                "description": "Whether using any local cache. Default is false."
              }
            }
          },
          {
            "type": "function",
            "name": "callback",
            "optional": true,
            "parameters": []
          }
        ]
      },
      {
        "name": "remove",
        "type": "function",
        "description": "Closes one or more tabs.",
        "async": "callback",
        "parameters": [
          {
            "name": "tabIds",
            "description": "The tab or list of tabs to close.",
            "choices": [
              {"type": "integer", "minimum": 0},
              {"type": "array", "items": {"type": "integer", "minimum": 0}}
            ]
          },
          {
            "type": "function",
            "name": "callback",
            "optional": true,
            "parameters": []
          }
        ]
      },
      {
        "name": "discard",
        "type": "function",
        "description": "discards one or more tabs.",
        "async": true,
        "parameters": [
          {
            "name": "tabIds",
            "description": "The tab or list of tabs to discard.",
            "choices": [
              {"type": "integer", "minimum": 0},
              {"type": "array", "items": {"type": "integer", "minimum": 0}}
            ]
          }
        ]
      },
      {
        "name": "detectLanguage",
        "type": "function",
        "description": "Detects the primary language of the content in a tab.",
        "async": "callback",
        "parameters": [
          {
            "type": "integer",
            "name": "tabId",
            "minimum": 0,
            "optional": true,
            "description": "Defaults to the active tab of the $(topic:current-window)[current window]."
          },
          {
            "type": "function",
            "name": "callback",
            "parameters": [
              {
                "type": "string",
                "name": "language",
                "description": "An ISO language code such as <code>en</code> or <code>fr</code>. For a complete list of languages supported by this method, see <a href='http://src.chromium.org/viewvc/chrome/trunk/src/third_party/cld/languages/internal/languages.cc'>kLanguageInfoTable</a>. The 2nd to 4th columns will be checked and the first non-NULL value will be returned except for Simplified Chinese for which zh-CN will be returned. For an unknown language, <code>und</code> will be returned."
              }
            ]
          }
        ]
      },
      {
        "name": "toggleReaderMode",
        "type": "function",
        "description": "Toggles reader mode for the document in the tab.",
        "async": true,
        "parameters": [
          {
            "type": "integer",
            "name": "tabId",
            "minimum": 0,
            "optional": true,
            "description": "Defaults to the active tab of the $(topic:current-window)[current window]."
          }
        ]
      },
      {
        "name": "captureTab",
        "type": "function",
        "description": "Captures the visible area of a specified tab. You must have $(topic:declare_permissions)[&lt;all_urls&gt;] permission to use this method.",
        "permissions": ["<all_urls>"],
        "async": true,
        "parameters": [
          {
            "type": "integer",
            "name": "tabId",
            "minimum": 0,
            "optional": true,
            "description": "The tab to capture. Defaults to the active tab of the current window."
          },
          {
            "$ref": "extensionTypes.ImageDetails",
            "name": "options",
            "optional": true
          }
        ]
      },
      {
        "name": "captureVisibleTab",
        "type": "function",
        "description": "Captures the visible area of the currently active tab in the specified window. You must have $(topic:declare_permissions)[&lt;all_urls&gt;] permission to use this method.",
        "permissions": ["<all_urls>"],
        "async": "callback",
        "parameters": [
          {
            "type": "integer",
            "name": "windowId",
            "minimum": -2,
            "optional": true,
            "description": "The target window. Defaults to the $(topic:current-window)[current window]."
          },
          {
            "$ref": "extensionTypes.ImageDetails",
            "name": "options",
            "optional": true
          },
          {
            "type": "function",
            "name": "callback",
            "parameters": [
              {
                "type": "string",
                "name": "dataUrl",
                "description": "A data URL which encodes an image of the visible area of the captured tab. May be assigned to the 'src' property of an HTML Image element for display."
              }
            ]
          }
        ]
      },
      {
        "name": "executeScript",
        "type": "function",
        "description": "Injects JavaScript code into a page. For details, see the $(topic:content_scripts)[programmatic injection] section of the content scripts doc.",
        "async": "callback",
        "parameters": [
          {
            "type": "integer",
            "name": "tabId",
            "minimum": 0,
            "optional": true,
            "description": "The ID of the tab in which to run the script; defaults to the active tab of the current window."
          },
          {
            "$ref": "extensionTypes.InjectDetails",
            "name": "details",
            "description": "Details of the script to run."
          },
          {
            "type": "function",
            "name": "callback",
            "optional": true,
            "description": "Called after all the JavaScript has been executed.",
            "parameters": [
              {
                "name": "result",
                "optional": true,
                "type": "array",
                "items": {"type": "any"},
                "description": "The result of the script in every injected frame."
              }
            ]
          }
        ]
      },
      {
        "name": "insertCSS",
        "type": "function",
        "description": "Injects CSS into a page. For details, see the $(topic:content_scripts)[programmatic injection] section of the content scripts doc.",
        "async": "callback",
        "parameters": [
          {
            "type": "integer",
            "name": "tabId",
            "minimum": 0,
            "optional": true,
            "description": "The ID of the tab in which to insert the CSS; defaults to the active tab of the current window."
          },
          {
            "$ref": "extensionTypes.InjectDetails",
            "name": "details",
            "description": "Details of the CSS text to insert."
          },
          {
            "type": "function",
            "name": "callback",
            "optional": true,
            "description": "Called when all the CSS has been inserted.",
            "parameters": []
          }
        ]
      },
      {
        "name": "removeCSS",
        "type": "function",
        "description": "Removes injected CSS from a page. For details, see the $(topic:content_scripts)[programmatic injection] section of the content scripts doc.",
        "async": "callback",
        "parameters": [
          {
            "type": "integer",
            "name": "tabId",
            "minimum": 0,
            "optional": true,
            "description": "The ID of the tab from which to remove the injected CSS; defaults to the active tab of the current window."
          },
          {
            "$ref": "extensionTypes.InjectDetails",
            "name": "details",
            "description": "Details of the CSS text to remove."
          },
          {
            "type": "function",
            "name": "callback",
            "optional": true,
            "description": "Called when all the CSS has been removed.",
            "parameters": []
          }
        ]
      },
      {
        "name": "setZoom",
        "type": "function",
        "description": "Zooms a specified tab.",
        "async": "callback",
        "parameters": [
          {
            "type": "integer",
            "name": "tabId",
            "minimum": 0,
            "optional": true,
            "description": "The ID of the tab to zoom; defaults to the active tab of the current window."
          },
          {
            "type": "number",
            "name": "zoomFactor",
            "description": "The new zoom factor. Use a value of 0 here to set the tab to its current default zoom factor. Values greater than zero specify a (possibly non-default) zoom factor for the tab."
          },
          {
            "type": "function",
            "name": "callback",
            "optional": true,
            "description": "Called after the zoom factor has been changed.",
            "parameters": []
          }
        ]
      },
      {
        "name": "getZoom",
        "type": "function",
        "description": "Gets the current zoom factor of a specified tab.",
        "async": "callback",
        "parameters": [
          {
            "type": "integer",
            "name": "tabId",
            "minimum": 0,
            "optional": true,
            "description": "The ID of the tab to get the current zoom factor from; defaults to the active tab of the current window."
          },
          {
            "type": "function",
            "name": "callback",
            "description": "Called with the tab's current zoom factor after it has been fetched.",
            "parameters": [
              {
                "type": "number",
                "name": "zoomFactor",
                "description": "The tab's current zoom factor."
              }
            ]
          }
        ]
      },
      {
        "name": "setZoomSettings",
        "type": "function",
        "description": "Sets the zoom settings for a specified tab, which define how zoom changes are handled. These settings are reset to defaults upon navigating the tab.",
        "async": "callback",
        "parameters": [
          {
            "type": "integer",
            "name": "tabId",
            "optional": true,
            "minimum": 0,
            "description": "The ID of the tab to change the zoom settings for; defaults to the active tab of the current window."
          },
          {
            "$ref": "ZoomSettings",
            "name": "zoomSettings",
            "description": "Defines how zoom changes are handled and at what scope."
          },
          {
            "type": "function",
            "name": "callback",
            "optional": true,
            "description": "Called after the zoom settings have been changed.",
            "parameters": []
          }
        ]
      },
      {
        "name": "getZoomSettings",
        "type": "function",
        "description": "Gets the current zoom settings of a specified tab.",
        "async": "callback",
        "parameters": [
          {
            "type": "integer",
            "name": "tabId",
            "optional": true,
            "minimum": 0,
            "description": "The ID of the tab to get the current zoom settings from; defaults to the active tab of the current window."
          },
          {
            "type": "function",
            "name": "callback",
            "description": "Called with the tab's current zoom settings.",
            "parameters": [
              {
                "$ref": "ZoomSettings",
                "name": "zoomSettings",
                "description": "The tab's current zoom settings."
              }
            ]
          }
        ]
      },
      {
        "name": "print",
        "type": "function",
        "description": "Prints page in active tab.",
        "parameters": []
      },
      {
        "name": "printPreview",
        "type": "function",
        "description": "Shows print preview for page in active tab.",
        "async": "callback",
        "parameters": [
          {
            "type": "function",
            "name": "callback",
            "optional": true,
            "description": "Called after print preview entered.",
            "parameters": []
          }
        ]
      },
      {
        "name": "saveAsPDF",
        "type": "function",
        "description": "Saves page in active tab as a PDF file.",
        "async": "callback",
        "parameters": [
          {
            "$ref": "PageSettings",
            "name": "pageSettings",
            "description": "The page settings used to save the PDF file."
          },
          {
            "type": "function",
            "name": "callback",
            "optional": true,
            "description": "Called after save as dialog closed.",
            "parameters": [
              {
                "type": "string",
                "name": "status",
                "description": "Save status: saved, replaced, canceled, not_saved, not_replaced."
              }
            ]
          }
        ]
      },
      {
        "name": "show",
        "type": "function",
        "description": "Shows one or more tabs.",
        "permissions": ["tabHide"],
        "async": true,
        "parameters": [
          {
            "name": "tabIds",
            "description": "The TAB ID or list of TAB IDs to show.",
            "choices": [
              {"type": "integer", "minimum": 0},
              {"type": "array", "items": {"type": "integer", "minimum": 0}}
            ]
          }
        ]
      },
      {
        "name": "hide",
        "type": "function",
        "description": "Hides one or more tabs. The <code>\"tabHide\"</code> permission is required to hide tabs.  Not all tabs are hidable.  Returns an array of hidden tabs.",
        "permissions": ["tabHide"],
        "async": true,
        "parameters": [
          {
            "name": "tabIds",
            "description": "The TAB ID or list of TAB IDs to hide.",
            "choices": [
              {"type": "integer", "minimum": 0},
              {"type": "array", "items": {"type": "integer", "minimum": 0}}
            ]
          }
        ]
      }
    ],
    "events": [
      {
        "name": "onCreated",
        "type": "function",
        "description": "Fired when a tab is created. Note that the tab's URL may not be set at the time this event fired, but you can listen to onUpdated events to be notified when a URL is set.",
        "parameters": [
          {
            "$ref": "Tab",
            "name": "tab",
            "description": "Details of the tab that was created."
          }
        ]
      },
      {
        "name": "onUpdated",
        "type": "function",
        "description": "Fired when a tab is updated.",
        "parameters": [
          {"type": "integer", "name": "tabId", "minimum": 0},
          {
            "type": "object",
            "name": "changeInfo",
            "description": "Lists the changes to the state of the tab that was updated.",
            "properties": {
              "status": {
                "type": "string",
                "optional": true,
                "description": "The status of the tab. Can be either <em>loading</em> or <em>complete</em>."
              },
              "discarded": {
                "type": "boolean",
                "optional": true,
                "description": "True while the tab is not loaded with content."
              },
              "url": {
                "type": "string",
                "optional": true,
                "description": "The tab's URL if it has changed."
              },
              "pinned": {
                "type": "boolean",
                "optional": true,
                "description": "The tab's new pinned state."
              },
              "audible": {
                "type": "boolean",
                "optional": true,
                "description": "The tab's new audible state."
              },
              "mutedInfo": {
                "$ref": "MutedInfo",
                "optional": true,
                "description": "The tab's new muted state and the reason for the change."
              },
              "favIconUrl": {
                "type": "string",
                "optional": true,
                "description": "The tab's new favicon URL."
              }
            }
          },
          {
            "$ref": "Tab",
            "name": "tab",
            "description": "Gives the state of the tab that was updated."
          }
        ]
      },
      {
        "name": "onMoved",
        "type": "function",
        "description": "Fired when a tab is moved within a window. Only one move event is fired, representing the tab the user directly moved. Move events are not fired for the other tabs that must move in response. This event is not fired when a tab is moved between windows. For that, see $(ref:tabs.onDetached).",
        "parameters": [
          {"type": "integer", "name": "tabId", "minimum": 0},
          {
            "type": "object",
            "name": "moveInfo",
            "properties": {
              "windowId": {"type": "integer", "minimum": 0},
              "fromIndex": {"type": "integer", "minimum": 0},
              "toIndex": {"type": "integer", "minimum": 0}
            }
          }
        ]
      },
      {
        "name": "onSelectionChanged",
        "deprecated": "Please use $(ref:tabs.onActivated).",
        "unsupported": true,
        "type": "function",
        "description": "Fires when the selected tab in a window changes.",
        "parameters": [
          {
            "type": "integer",
            "name": "tabId",
            "minimum": 0,
            "description": "The ID of the tab that has become active."
          },
          {
            "type": "object",
            "name": "selectInfo",
            "properties": {
              "windowId": {
                "type": "integer",
                "minimum": 0,
                "description": "The ID of the window the selected tab changed inside of."
              }
            }
          }
        ]
      },
      {
        "name": "onActiveChanged",
        "deprecated": "Please use $(ref:tabs.onActivated).",
        "unsupported": true,
        "type": "function",
        "description": "Fires when the selected tab in a window changes. Note that the tab's URL may not be set at the time this event fired, but you can listen to $(ref:tabs.onUpdated) events to be notified when a URL is set.",
        "parameters": [
          {
            "type": "integer",
            "name": "tabId",
            "minimum": 0,
            "description": "The ID of the tab that has become active."
          },
          {
            "type": "object",
            "name": "selectInfo",
            "properties": {
              "windowId": {
                "type": "integer",
                "minimum": 0,
                "description": "The ID of the window the selected tab changed inside of."
              }
            }
          }
        ]
      },
      {
        "name": "onActivated",
        "type": "function",
        "description": "Fires when the active tab in a window changes. Note that the tab's URL may not be set at the time this event fired, but you can listen to onUpdated events to be notified when a URL is set.",
        "parameters": [
          {
            "type": "object",
            "name": "activeInfo",
            "properties": {
              "tabId": {
                "type": "integer",
                "minimum": 0,
                "description": "The ID of the tab that has become active."
              },
              "windowId": {
                "type": "integer",
                "minimum": 0,
                "description": "The ID of the window the active tab changed inside of."
              }
            }
          }
        ]
      },
      {
        "name": "onHighlightChanged",
        "deprecated": "Please use $(ref:tabs.onHighlighted).",
        "unsupported": true,
        "type": "function",
        "description": "Fired when the highlighted or selected tabs in a window changes.",
        "parameters": [
          {
            "type": "object",
            "name": "selectInfo",
            "properties": {
              "windowId": {
                "type": "integer",
                "minimum": 0,
                "description": "The window whose tabs changed."
              },
              "tabIds": {
                "type": "array",
                "items": {"type": "integer", "minimum": 0},
                "description": "All highlighted tabs in the window."
              }
            }
          }
        ]
      },
      {
        "name": "onHighlighted",
        "type": "function",
        "description": "Fired when the highlighted or selected tabs in a window changes.",
        "parameters": [
          {
            "type": "object",
            "name": "highlightInfo",
            "properties": {
              "windowId": {
                "type": "integer",
                "minimum": 0,
                "description": "The window whose tabs changed."
              },
              "tabIds": {
                "type": "array",
                "items": {"type": "integer", "minimum": 0},
                "description": "All highlighted tabs in the window."
              }
            }
          }
        ]
      },
      {
        "name": "onDetached",
        "type": "function",
        "description": "Fired when a tab is detached from a window, for example because it is being moved between windows.",
        "parameters": [
          {"type": "integer", "name": "tabId", "minimum": 0},
          {
            "type": "object",
            "name": "detachInfo",
            "properties": {
              "oldWindowId": {"type": "integer", "minimum": 0},
              "oldPosition": {"type": "integer", "minimum": 0}
            }
          }
        ]
      },
      {
        "name": "onAttached",
        "type": "function",
        "description": "Fired when a tab is attached to a window, for example because it was moved between windows.",
        "parameters": [
          {"type": "integer", "name": "tabId", "minimum": 0},
          {
            "type": "object",
            "name": "attachInfo",
            "properties": {
              "newWindowId": {"type": "integer", "minimum": 0},
              "newPosition": {"type": "integer", "minimum": 0}
            }
          }
        ]
      },
      {
        "name": "onRemoved",
        "type": "function",
        "description": "Fired when a tab is closed.",
        "parameters": [
          {"type": "integer", "name": "tabId", "minimum": 0},
          {
            "type": "object",
            "name": "removeInfo",
            "properties": {
              "windowId": {"type": "integer", "minimum": 0, "description": "The window whose tab is closed." },
              "isWindowClosing": {"type": "boolean", "description": "True when the tab is being closed because its window is being closed." }
            }
          }
        ]
      },
      {
        "name": "onReplaced",
        "type": "function",
        "description": "Fired when a tab is replaced with another tab due to prerendering or instant.",
        "parameters": [
          {"type": "integer", "name": "addedTabId", "minimum": 0},
          {"type": "integer", "name": "removedTabId", "minimum": 0}
        ]
      },
      {
        "name": "onZoomChange",
        "type": "function",
        "description": "Fired when a tab is zoomed.",
        "parameters": [{
          "type": "object",
          "name": "ZoomChangeInfo",
          "properties": {
            "tabId": {"type": "integer", "minimum": 0},
            "oldZoomFactor": {"type": "number"},
            "newZoomFactor": {"type": "number"},
            "zoomSettings": {"$ref": "ZoomSettings"}
          }
        }]
      }
    ]
  }
]
PK
!<_%��WW9chrome/browser/content/browser/schemas/url_overrides.json[
  {
    "namespace": "manifest",
    "types": [
      {
        "$extend": "WebExtensionManifest",
        "properties": {
          "chrome_url_overrides": {
            "type": "object",
            "optional": true,
            "properties": {
              "newtab": {
                "$ref": "ExtensionURL",
                "optional": true,
                "preprocess": "localize"
              },
              "bookmarks": {
                "unsupported": true,
                "$ref": "ExtensionURL",
                "optional": true,
                "preprocess": "localize"
              },
              "history": {
                "unsupported": true,
                "$ref": "ExtensionURL",
                "optional": true,
                "preprocess": "localize"
              }
            }
          }
        }
      }
    ]
  }
]PK
!<����U�U3chrome/browser/content/browser/schemas/windows.json// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

[
  {
    "namespace": "windows",
    "description": "Use the <code>browser.windows</code> API to interact with browser windows. You can use this API to create, modify, and rearrange windows in the browser.",
    "types": [
      {
        "id": "WindowType",
        "type": "string",
        "description": "The type of browser window this is. Under some circumstances a Window may not be assigned type property, for example when querying closed windows from the $(ref:sessions) API.",
        "enum": ["normal", "popup", "panel", "app", "devtools"]
      },
      {
        "id": "WindowState",
        "type": "string",
        "description": "The state of this browser window. Under some circumstances a Window may not be assigned state property, for example when querying closed windows from the $(ref:sessions) API.",
        "enum": ["normal", "minimized", "maximized", "fullscreen", "docked"]
      },
      {
        "id": "Window",
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "optional": true,
            "minimum": 0,
            "description": "The ID of the window. Window IDs are unique within a browser session. Under some circumstances a Window may not be assigned an ID, for example when querying windows using the $(ref:sessions) API, in which case a session ID may be present."
          },
          "focused": {
            "type": "boolean",
            "description": "Whether the window is currently the focused window."
          },
          "top": {
            "type": "integer",
            "optional": true,
            "description": "The offset of the window from the top edge of the screen in pixels. Under some circumstances a Window may not be assigned top property, for example when querying closed windows from the $(ref:sessions) API."
          },
          "left": {
            "type": "integer",
            "optional": true,
            "description": "The offset of the window from the left edge of the screen in pixels. Under some circumstances a Window may not be assigned left property, for example when querying closed windows from the $(ref:sessions) API."
          },
          "width": {
            "type": "integer",
            "optional": true,
            "description": "The width of the window, including the frame, in pixels. Under some circumstances a Window may not be assigned width property, for example when querying closed windows from the $(ref:sessions) API."
          },
          "height": {
            "type": "integer",
            "optional": true,
            "description": "The height of the window, including the frame, in pixels. Under some circumstances a Window may not be assigned height property, for example when querying closed windows from the $(ref:sessions) API."
          },
          "tabs": {
            "type": "array",
            "items": { "$ref": "tabs.Tab" },
            "optional": true,
            "description": "Array of $(ref:tabs.Tab) objects representing the current tabs in the window."
          },
          "incognito": {
            "type": "boolean",
            "description": "Whether the window is incognito."
          },
          "type": {
            "$ref": "WindowType",
            "optional": true,
            "description": "The type of browser window this is."
          },
          "state": {
            "$ref": "WindowState",
            "optional": true,
            "description": "The state of this browser window."
          },
          "alwaysOnTop": {
            "type": "boolean",
            "description": "Whether the window is set to be always on top."
          },
          "sessionId": {
            "type": "string",
            "optional": true,
            "description": "The session ID used to uniquely identify a Window obtained from the $(ref:sessions) API."
          },
          "title": {
            "type": "string",
            "optional": true,
            "description": "The title of the window. Read-only."
          }
        }
      },
      {
        "id": "CreateType",
        "type": "string",
        "description": "Specifies what type of browser window to create. The 'panel' and 'detached_panel' types create a popup unless the '--enable-panels' flag is set.",
        "enum": ["normal", "popup", "panel", "detached_panel"]
      }
    ],
    "properties": {
      "WINDOW_ID_NONE": {
        "value": -1,
        "description": "The windowId value that represents the absence of a browser window."
      },
      "WINDOW_ID_CURRENT": {
        "value": -2,
        "description": "The windowId value that represents the $(topic:current-window)[current window]."
      }
    },
    "functions": [
      {
        "name": "get",
        "type": "function",
        "description": "Gets details about a window.",
        "async": "callback",
        "parameters": [
          {
            "type": "integer",
            "name": "windowId",
            "minimum": -2
          },
          {
            "type": "object",
            "name": "getInfo",
            "optional": true,
            "description": "",
            "properties": {
              "populate": {
                "type": "boolean",
                "optional": true,
                "description": "If true, the $(ref:windows.Window) object will have a <var>tabs</var> property that contains a list of the $(ref:tabs.Tab) objects. The <code>Tab</code> objects only contain the <code>url</code>, <code>title</code> and <code>favIconUrl</code> properties if the extension's manifest file includes the <code>\"tabs\"</code> permission."
              },
              "windowTypes": {
                "type": "array",
                "items": {
                  "$ref": "WindowType"
                },
                "optional": true,
                "description": "If set, the $(ref:windows.Window) returned will be filtered based on its type. If unset the default filter is set to <code>['app', 'normal', 'panel', 'popup']</code>, with <code>'app'</code> and <code>'panel'</code> window types limited to the extension's own windows."
              }
            }
          },
          {
            "type": "function",
            "name": "callback",
            "parameters": [
              {
                "name": "window",
                "$ref": "Window"
              }
            ]
          }
        ]
      },
      {
        "name": "getCurrent",
        "type": "function",
        "description": "Gets the $(topic:current-window)[current window].",
        "async": "callback",
        "parameters": [
          {
            "type": "object",
            "name": "getInfo",
            "optional": true,
            "description": "",
            "properties": {
              "populate": {
                "type": "boolean",
                "optional": true,
                "description": "If true, the $(ref:windows.Window) object will have a <var>tabs</var> property that contains a list of the $(ref:tabs.Tab) objects. The <code>Tab</code> objects only contain the <code>url</code>, <code>title</code> and <code>favIconUrl</code> properties if the extension's manifest file includes the <code>\"tabs\"</code> permission."
              },
              "windowTypes": {
                "type": "array",
                "items": { "$ref": "WindowType" },
                "optional": true,
                "description": "If set, the $(ref:windows.Window) returned will be filtered based on its type. If unset the default filter is set to <code>['app', 'normal', 'panel', 'popup']</code>, with <code>'app'</code> and <code>'panel'</code> window types limited to the extension's own windows."
              }
            }
          },
          {
            "type": "function",
            "name": "callback",
            "parameters": [
              {
                "name": "window",
                "$ref": "Window"
              }
            ]
          }
        ]
      },
      {
        "name": "getLastFocused",
        "type": "function",
        "description": "Gets the window that was most recently focused &mdash; typically the window 'on top'.",
        "async": "callback",
        "parameters": [
          {
            "type": "object",
            "name": "getInfo",
            "optional": true,
            "description": "",
            "properties": {
              "populate": {
                "type": "boolean",
                "optional": true,
                "description": "If true, the $(ref:windows.Window) object will have a <var>tabs</var> property that contains a list of the $(ref:tabs.Tab) objects. The <code>Tab</code> objects only contain the <code>url</code>, <code>title</code> and <code>favIconUrl</code> properties if the extension's manifest file includes the <code>\"tabs\"</code> permission."
              },
              "windowTypes": {
                "type": "array",
                "items": { "$ref": "WindowType" },
                "optional": true,
                "description": "If set, the $(ref:windows.Window) returned will be filtered based on its type. If unset the default filter is set to <code>['app', 'normal', 'panel', 'popup']</code>, with <code>'app'</code> and <code>'panel'</code> window types limited to the extension's own windows."
              }
            }
          },
          {
            "type": "function",
            "name": "callback",
            "parameters": [
              {
                "name": "window",
                "$ref": "Window"
              }
            ]
          }
        ]
      },
      {
        "name": "getAll",
        "type": "function",
        "description": "Gets all windows.",
        "async": "callback",
        "parameters": [
          {
            "type": "object",
            "name": "getInfo",
            "optional": true,
            "description": "",
            "properties": {
              "populate": {
                "type": "boolean",
                "optional": true,
                "description": "If true, each $(ref:windows.Window) object will have a <var>tabs</var> property that contains a list of the $(ref:tabs.Tab) objects for that window. The <code>Tab</code> objects only contain the <code>url</code>, <code>title</code> and <code>favIconUrl</code> properties if the extension's manifest file includes the <code>\"tabs\"</code> permission."
              },
              "windowTypes": {
                "type": "array",
                "items": { "$ref": "WindowType" },
                "optional": true,
                "description": "If set, the $(ref:windows.Window) returned will be filtered based on its type. If unset the default filter is set to <code>['app', 'normal', 'panel', 'popup']</code>, with <code>'app'</code> and <code>'panel'</code> window types limited to the extension's own windows."
              }
            }
          },
          {
            "type": "function",
            "name": "callback",
            "parameters": [
              {
                "name": "windows",
                "type": "array",
                "items": { "$ref": "Window" }
              }
            ]
          }
        ]
      },
      {
        "name": "create",
        "type": "function",
        "description": "Creates (opens) a new browser with any optional sizing, position or default URL provided.",
        "async": "callback",
        "parameters": [
          {
            "type": "object",
            "name": "createData",
            "optional": true,
            "default": {},
            "properties": {
              "url": {
                "description": "A URL or array of URLs to open as tabs in the window. Fully-qualified URLs must include a scheme (i.e. 'http://www.google.com', not 'www.google.com'). Relative URLs will be relative to the current page within the extension. Defaults to the New Tab Page.",
                "optional": true,
                "choices": [
                  { "type": "string", "format": "relativeUrl" },
                  {
                    "type": "array",
                    "items": { "type": "string", "format": "relativeUrl" }
                  }
                ]
              },
              "tabId": {
                "type": "integer",
                "minimum": 0,
                "optional": true,
                "description": "The id of the tab for which you want to adopt to the new window."
              },
              "left": {
                "type": "integer",
                "optional": true,
                "description": "The number of pixels to position the new window from the left edge of the screen. If not specified, the new window is offset naturally from the last focused window. This value is ignored for panels."
              },
              "top": {
                "type": "integer",
                "optional": true,
                "description": "The number of pixels to position the new window from the top edge of the screen. If not specified, the new window is offset naturally from the last focused window. This value is ignored for panels."
              },
              "width": {
                "type": "integer",
                "minimum": 0,
                "optional": true,
                "description": "The width in pixels of the new window, including the frame. If not specified defaults to a natural width."
              },
              "height": {
                "type": "integer",
                "minimum": 0,
                "optional": true,
                "description": "The height in pixels of the new window, including the frame. If not specified defaults to a natural height."
              },
              "focused": {
                "unsupported": true,
                "type": "boolean",
                "optional": true,
                "description": "If true, opens an active window. If false, opens an inactive window."
              },
              "incognito": {
                "type": "boolean",
                "optional": true,
                "description": "Whether the new window should be an incognito window."
              },
              "type": {
                "$ref": "CreateType",
                "optional": true,
                "description": "Specifies what type of browser window to create. The 'panel' and 'detached_panel' types create a popup unless the '--enable-panels' flag is set."
              },
              "state": {
                "$ref": "WindowState",
                "optional": true,
                "description": "The initial state of the window. The 'minimized', 'maximized' and 'fullscreen' states cannot be combined with 'left', 'top', 'width' or 'height'."
              },
              "allowScriptsToClose": {
                "type": "boolean",
                "optional": true,
                "description": "Allow scripts to close the window."
              },
              "titlePreface": {
                "type": "string",
                "optional": true,
                "description": "A string to add to the beginning of the window title."
              }
            },
            "optional": true
          },
          {
            "type": "function",
            "name": "callback",
            "optional": true,
            "parameters": [
              {
                "name": "window",
                "$ref": "Window",
                "description": "Contains details about the created window.",
                "optional": true
              }
            ]
          }
        ]
      },
      {
        "name": "update",
        "type": "function",
        "description": "Updates the properties of a window. Specify only the properties that you want to change; unspecified properties will be left unchanged.",
        "async": "callback",
        "parameters": [
          {
            "type": "integer",
            "name": "windowId",
            "minimum": -2
          },
          {
            "type": "object",
            "name": "updateInfo",
            "properties": {
              "left": {
                "type": "integer",
                "optional": true,
                "description": "The offset from the left edge of the screen to move the window to in pixels. This value is ignored for panels."
              },
              "top": {
                "type": "integer",
                "optional": true,
                "description": "The offset from the top edge of the screen to move the window to in pixels. This value is ignored for panels."
              },
              "width": {
                "type": "integer",
                "minimum": 0,
                "optional": true,
                "description": "The width to resize the window to in pixels. This value is ignored for panels."
              },
              "height": {
                "type": "integer",
                "minimum": 0,
                "optional": true,
                "description": "The height to resize the window to in pixels. This value is ignored for panels."
              },
              "focused": {
                "type": "boolean",
                "optional": true,
                "description": "If true, brings the window to the front. If false, brings the next window in the z-order to the front."
              },
              "drawAttention": {
                "type": "boolean",
                "optional": true,
                "description": "If true, causes the window to be displayed in a manner that draws the user's attention to the window, without changing the focused window. The effect lasts until the user changes focus to the window. This option has no effect if the window already has focus. Set to false to cancel a previous draw attention request."
              },
              "state": {
                "$ref": "WindowState",
                "optional": true,
                "description": "The new state of the window. The 'minimized', 'maximized' and 'fullscreen' states cannot be combined with 'left', 'top', 'width' or 'height'."
              },
              "titlePreface": {
                "type": "string",
                "optional": true,
                "description": "A string to add to the beginning of the window title."
              }
            }
          },
          {
            "type": "function",
            "name": "callback",
            "optional": true,
            "parameters": [
              {
                "name": "window",
                "$ref": "Window"
              }
            ]
          }
        ]
      },
      {
        "name": "remove",
        "type": "function",
        "description": "Removes (closes) a window, and all the tabs inside it.",
        "async": "callback",
        "parameters": [
          {
            "type": "integer",
            "name": "windowId",
            "minimum": 0
          },
          {
            "type": "function",
            "name": "callback",
            "optional": true,
            "parameters": []
          }
        ]
      }
    ],
    "events": [
      {
        "name": "onCreated",
        "type": "function",
        "description": "Fired when a window is created.",
        "filters": [
          {
            "name": "windowTypes",
            "type": "array",
            "items": { "$ref": "WindowType" },
            "description": "Conditions that the window's type being created must satisfy. By default it will satisfy <code>['app', 'normal', 'panel', 'popup']</code>, with <code>'app'</code> and <code>'panel'</code> window types limited to the extension's own windows."
          }
        ],
        "parameters": [
          {
            "$ref": "Window",
            "name": "window",
            "description": "Details of the window that was created."
          }
        ]
      },
      {
        "name": "onRemoved",
        "type": "function",
        "description": "Fired when a window is removed (closed).",
        "filters": [
          {
            "name": "windowTypes",
            "type": "array",
            "items": { "$ref": "WindowType" },
            "description": "Conditions that the window's type being removed must satisfy. By default it will satisfy <code>['app', 'normal', 'panel', 'popup']</code>, with <code>'app'</code> and <code>'panel'</code> window types limited to the extension's own windows."
          }
        ],
        "parameters": [
          {
            "type": "integer",
            "name": "windowId",
            "minimum": 0,
            "description": "ID of the removed window."
          }
        ]
      },
      {
        "name": "onFocusChanged",
        "type": "function",
        "description": "Fired when the currently focused window changes. Will be $(ref:windows.WINDOW_ID_NONE) if all browser windows have lost focus. Note: On some Linux window managers, WINDOW_ID_NONE will always be sent immediately preceding a switch from one browser window to another.",
        "filters": [
          {
            "name": "windowTypes",
            "type": "array",
            "items": { "$ref": "WindowType" },
            "description": "Conditions that the window's type being removed must satisfy. By default it will satisfy <code>['app', 'normal', 'panel', 'popup']</code>, with <code>'app'</code> and <code>'panel'</code> window types limited to the extension's own windows."
          }
        ],
        "parameters": [
          {
            "type": "integer",
            "name": "windowId",
            "minimum": -1,
            "description": "ID of the newly focused window."
          }
        ]
      }
    ]
  }
]
PK
!<�;Y=�y�y0chrome/browser/content/browser/search/search.xml<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!-- XULCommandEvent is a specialised global. -->
<!-- global XULCommandEvent -->

<!DOCTYPE bindings [
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
%browserDTD;
]>

<bindings id="SearchBindings"
          xmlns="http://www.mozilla.org/xbl"
          xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
          xmlns:xbl="http://www.mozilla.org/xbl">

  <binding id="searchbar">
    <resources>
      <stylesheet src="chrome://browser/content/search/searchbarBindings.css"/>
      <stylesheet src="chrome://browser/skin/searchbar.css"/>
    </resources>
    <content>
      <xul:stringbundle src="chrome://browser/locale/search.properties"
                        anonid="searchbar-stringbundle"/>
      <!--
      There is a dependency between "maxrows" attribute and
      "SuggestAutoComplete._historyLimit" (nsSearchSuggestions.js). Changing
      one of them requires changing the other one.
      -->
      <xul:textbox class="searchbar-textbox"
                   anonid="searchbar-textbox"
                   type="autocomplete"
                   inputtype="search"
                   placeholder="&searchInput.placeholder;"
                   flex="1"
                   autocompletepopup="PopupSearchAutoComplete"
                   autocompletesearch="search-autocomplete"
                   autocompletesearchparam="searchbar-history"
                   maxrows="10"
                   completeselectedindex="true"
                   minresultsforpopup="0"
                   xbl:inherits="disabled,disableautocomplete,searchengine,src,newlines">
        <!--
        Empty <box> to properly position the icon within the autocomplete
        binding's anonymous children (the autocomplete binding positions <box>
        children differently)
        -->
        <xul:box>
          <xul:hbox class="searchbar-search-button"
                    anonid="searchbar-search-button"
                    xbl:inherits="addengines"
                    tooltiptext="&searchIcon.tooltip;">
            <xul:image class="searchbar-search-icon"/>
            <xul:image class="searchbar-search-icon-overlay"/>
          </xul:hbox>
        </xul:box>
        <xul:hbox class="search-go-container">
          <xul:image class="search-go-button urlbar-icon" hidden="true"
                     anonid="search-go-button"
                     onclick="handleSearchCommand(event);"
                     tooltiptext="&contentSearchSubmit.tooltip;"/>
        </xul:hbox>
      </xul:textbox>
    </content>

    <implementation implements="nsIObserver">
      <constructor><![CDATA[
        if (this.parentNode.parentNode.localName == "toolbarpaletteitem")
          return;

        Services.obs.addObserver(this, "browser-search-engine-modified");

        this._initialized = true;

        (window.delayedStartupPromise || Promise.resolve()).then(() => {
          window.requestIdleCallback(() => {
            Services.search.init(aStatus => {
              // Bail out if the binding's been destroyed
              if (!this._initialized)
                return;

              if (Components.isSuccessCode(aStatus)) {
                // Refresh the display (updating icon, etc)
                this.updateDisplay();
                BrowserSearch.updateOpenSearchBadge();
              } else {
                Cu.reportError("Cannot initialize search service, bailing out: " + aStatus);
              }
            });
          });
        });

        // Wait until the popupshowing event to avoid forcing immediate
        // attachment of the search-one-offs binding.
        this.textbox.popup.addEventListener("popupshowing", () => {
          let oneOffButtons = this.textbox.popup.oneOffButtons;
          // Some accessibility tests create their own <searchbar> that doesn't
          // use the popup binding below, so null-check oneOffButtons.
          if (oneOffButtons) {
            oneOffButtons.telemetryOrigin = "searchbar";
            // Set .textbox first, since the popup setter will cause
            // a _rebuild call that uses it.
            oneOffButtons.textbox = this.textbox;
            oneOffButtons.popup = this.textbox.popup;
          }
        }, {capturing: true, once: true});
      ]]></constructor>

      <destructor><![CDATA[
        this.destroy();
      ]]></destructor>

      <method name="destroy">
        <body><![CDATA[
        if (this._initialized) {
          this._initialized = false;

          Services.obs.removeObserver(this, "browser-search-engine-modified");
        }

        // Make sure to break the cycle from _textbox to us. Otherwise we leak
        // the world. But make sure it's actually pointing to us.
        // Also make sure the textbox has ever been constructed, otherwise the
        // _textbox getter will cause the textbox constructor to run, add an
        // observer, and leak the world too.
        if (this._textboxInitialized && this._textbox.mController.input == this)
          this._textbox.mController.input = null;
        ]]></body>
      </method>

      <field name="_ignoreFocus">false</field>
      <field name="_clickClosedPopup">false</field>
      <field name="_stringBundle">document.getAnonymousElementByAttribute(this,
          "anonid", "searchbar-stringbundle");</field>
      <field name="_textboxInitialized">false</field>
      <field name="_textbox">document.getAnonymousElementByAttribute(this,
          "anonid", "searchbar-textbox");</field>
      <field name="_engines">null</field>
      <field name="FormHistory" readonly="true">
        (ChromeUtils.import("resource://gre/modules/FormHistory.jsm", {})).FormHistory;
      </field>

      <property name="engines" readonly="true">
        <getter><![CDATA[
          if (!this._engines)
            this._engines = Services.search.getVisibleEngines();
          return this._engines;
        ]]></getter>
      </property>

      <property name="currentEngine">
        <setter><![CDATA[
          Services.search.currentEngine = val;
          return val;
        ]]></setter>
        <getter><![CDATA[
          var currentEngine = Services.search.currentEngine;
          // Return a dummy engine if there is no currentEngine
          return currentEngine || {name: "", uri: null};
        ]]></getter>
      </property>

      <!-- textbox is used by sanitize.js to clear the undo history when
           clearing form information. -->
      <property name="textbox" readonly="true"
                onget="return this._textbox;"/>

      <property name="value" onget="return this._textbox.value;"
                             onset="return this._textbox.value = val;"/>

      <method name="focus">
        <body><![CDATA[
          this._textbox.focus();
        ]]></body>
      </method>

      <method name="select">
        <body><![CDATA[
          this._textbox.select();
        ]]></body>
      </method>

      <method name="observe">
        <parameter name="aEngine"/>
        <parameter name="aTopic"/>
        <parameter name="aVerb"/>
        <body><![CDATA[
          if (aTopic == "browser-search-engine-modified") {
            switch (aVerb) {
            case "engine-removed":
              this.offerNewEngine(aEngine);
              break;
            case "engine-added":
              this.hideNewEngine(aEngine);
              break;
            case "engine-changed":
              // An engine was removed (or hidden) or added, or an icon was
              // changed.  Do nothing special.
            }

            // Make sure the engine list is refetched next time it's needed
            this._engines = null;

            // Update the popup header and update the display after any modification.
            this._textbox.popup.updateHeader();
            this.updateDisplay();
          }
        ]]></body>
      </method>

      <!-- There are two seaprate lists of search engines, whose uses intersect
      in this file.  The search service (nsIBrowserSearchService and
      nsSearchService.js) maintains a list of Engine objects which is used to
      populate the searchbox list of available engines and to perform queries.
      That list is accessed here via this.SearchService, and it's that sort of
      Engine that is passed to this binding's observer as aEngine.

      In addition, browser.js fills two lists of autodetected search engines
      (browser.engines and browser.hiddenEngines) as properties of
      mCurrentBrowser.  Those lists contain unnamed JS objects of the form
      { uri:, title:, icon: }, and that's what the searchbar uses to determine
      whether to show any "Add <EngineName>" menu items in the drop-down.

      The two types of engines are currently related by their identifying
      titles (the Engine object's 'name'), although that may change; see bug
      335102.  -->

      <!-- If the engine that was just removed from the searchbox list was
      autodetected on this page, move it to each browser's active list so it
      will be offered to be added again. -->
      <method name="offerNewEngine">
        <parameter name="aEngine"/>
        <body><![CDATA[
          for (let browser of gBrowser.browsers) {
            if (browser.hiddenEngines) {
              // XXX This will need to be changed when engines are identified by
              // URL rather than title; see bug 335102.
              var removeTitle = aEngine.wrappedJSObject.name;
              for (var i = 0; i < browser.hiddenEngines.length; i++) {
                if (browser.hiddenEngines[i].title == removeTitle) {
                  if (!browser.engines)
                    browser.engines = [];
                  browser.engines.push(browser.hiddenEngines[i]);
                  browser.hiddenEngines.splice(i, 1);
                  break;
                }
              }
            }
          }
          BrowserSearch.updateOpenSearchBadge();
        ]]></body>
      </method>

      <!-- If the engine that was just added to the searchbox list was
      autodetected on this page, move it to each browser's hidden list so it is
      no longer offered to be added. -->
      <method name="hideNewEngine">
        <parameter name="aEngine"/>
        <body><![CDATA[
          for (let browser of gBrowser.browsers) {
            if (browser.engines) {
              // XXX This will need to be changed when engines are identified by
              // URL rather than title; see bug 335102.
              var removeTitle = aEngine.wrappedJSObject.name;
              for (var i = 0; i < browser.engines.length; i++) {
                if (browser.engines[i].title == removeTitle) {
                  if (!browser.hiddenEngines)
                    browser.hiddenEngines = [];
                  browser.hiddenEngines.push(browser.engines[i]);
                  browser.engines.splice(i, 1);
                  break;
                }
              }
            }
          }
          BrowserSearch.updateOpenSearchBadge();
        ]]></body>
      </method>

      <method name="setIcon">
        <parameter name="element"/>
        <parameter name="uri"/>
        <body><![CDATA[
          element.setAttribute("src", uri);
        ]]></body>
      </method>

      <method name="updateDisplay">
        <body><![CDATA[
          var uri = this.currentEngine.iconURI;
          this.setIcon(this, uri ? uri.spec : "");

          var name = this.currentEngine.name;
          var text = this._stringBundle.getFormattedString("searchtip", [name]);
          this._textbox.label = text;
          this._textbox.tooltipText = text;
        ]]></body>
      </method>

      <method name="updateGoButtonVisibility">
        <body><![CDATA[
          document.getAnonymousElementByAttribute(this, "anonid",
                                                  "search-go-button")
                  .hidden = !this._textbox.value;
        ]]></body>
      </method>

      <method name="openSuggestionsPanel">
        <parameter name="aShowOnlySettingsIfEmpty"/>
        <body><![CDATA[
          if (this._textbox.open)
            return;

          this._textbox.showHistoryPopup();

          if (this._textbox.value) {
            // showHistoryPopup does a startSearch("") call, ensure the
            // controller handles the text from the input box instead:
            this._textbox.mController.handleText();
          } else if (aShowOnlySettingsIfEmpty) {
            this.setAttribute("showonlysettings", "true");
          }
        ]]></body>
      </method>

      <method name="selectEngine">
        <parameter name="aEvent"/>
        <parameter name="isNextEngine"/>
        <body><![CDATA[
          // Find the new index
          var newIndex = this.engines.indexOf(this.currentEngine);
          newIndex += isNextEngine ? 1 : -1;

          if (newIndex >= 0 && newIndex < this.engines.length) {
            this.currentEngine = this.engines[newIndex];
          }

          aEvent.preventDefault();
          aEvent.stopPropagation();

          this.openSuggestionsPanel();
        ]]></body>
      </method>

      <method name="handleSearchCommand">
        <parameter name="aEvent"/>
        <parameter name="aEngine"/>
        <parameter name="aForceNewTab"/>
        <body><![CDATA[
          var where = "current";
          let params;

          // Open ctrl/cmd clicks on one-off buttons in a new background tab.
          if (aEvent && aEvent.originalTarget.getAttribute("anonid") == "search-go-button") {
            if (aEvent.button == 2)
              return;
            where = whereToOpenLink(aEvent, false, true);
          } else if (aForceNewTab) {
            where = "tab";
            if (Services.prefs.getBoolPref("browser.tabs.loadInBackground"))
              where += "-background";
          } else {
            var newTabPref = Services.prefs.getBoolPref("browser.search.openintab");
            if (((aEvent instanceof KeyboardEvent) && aEvent.altKey) ^ newTabPref)
              where = "tab";
            if ((aEvent instanceof MouseEvent) &&
                (aEvent.button == 1 || aEvent.getModifierState("Accel"))) {
              where = "tab";
              params = {
                inBackground: true,
              };
            }
          }

          this.handleSearchCommandWhere(aEvent, aEngine, where, params);
        ]]></body>
      </method>

      <method name="handleSearchCommandWhere">
        <parameter name="aEvent"/>
        <parameter name="aEngine"/>
        <parameter name="aWhere"/>
        <parameter name="aParams"/>
        <body><![CDATA[
          var textBox = this._textbox;
          var textValue = textBox.value;

          let selection = this.telemetrySearchDetails;
          let oneOffRecorded = false;

          BrowserUsageTelemetry.recordSearchbarSelectedResultMethod(
            aEvent,
            selection ? selection.index : -1
          );

          if (!selection || (selection.index == -1)) {
            oneOffRecorded = this.textbox.popup.oneOffButtons
                                 .maybeRecordTelemetry(aEvent, aWhere, aParams);
            if (!oneOffRecorded) {
              let source = "unknown";
              let type = "unknown";
              let target = aEvent.originalTarget;
              if (aEvent instanceof KeyboardEvent) {
                type = "key";
              } else if (aEvent instanceof MouseEvent) {
                type = "mouse";
                if (target.classList.contains("search-panel-header") ||
                    target.parentNode.classList.contains("search-panel-header")) {
                  source = "header";
                }
              } else if (aEvent instanceof XULCommandEvent) {
                if (target.getAttribute("anonid") == "paste-and-search") {
                  source = "paste";
                }
              }
              if (!aEngine) {
                aEngine = this.currentEngine;
              }
              BrowserSearch.recordOneoffSearchInTelemetry(aEngine, source, type,
                                                          aWhere);
            }
          }

          // This is a one-off search only if oneOffRecorded is true.
          this.doSearch(textValue, aWhere, aEngine, aParams, oneOffRecorded);

          if (aWhere == "tab" && aParams && aParams.inBackground)
            this.focus();
        ]]></body>
      </method>

      <method name="doSearch">
        <parameter name="aData"/>
        <parameter name="aWhere"/>
        <parameter name="aEngine"/>
        <parameter name="aParams"/>
        <parameter name="aOneOff"/>
        <body><![CDATA[
          var textBox = this._textbox;

          // Save the current value in the form history
          if (aData && !PrivateBrowsingUtils.isWindowPrivate(window) && this.FormHistory.enabled) {
            this.FormHistory.update(
              { op: "bump",
                fieldname: textBox.getAttribute("autocompletesearchparam"),
                value: aData },
              { handleError(aError) {
                  Cu.reportError("Saving search to form history failed: " + aError.message);
              }});
          }

          let engine = aEngine || this.currentEngine;
          var submission = engine.getSubmission(aData, null, "searchbar");
          let telemetrySearchDetails = this.telemetrySearchDetails;
          this.telemetrySearchDetails = null;
          if (telemetrySearchDetails && telemetrySearchDetails.index == -1) {
            telemetrySearchDetails = null;
          }
          // If we hit here, we come either from a one-off, a plain search or a suggestion.
          const details = {
            isOneOff: aOneOff,
            isSuggestion: (!aOneOff && telemetrySearchDetails),
            selection: telemetrySearchDetails
          };
          BrowserSearch.recordSearchInTelemetry(engine, "searchbar", details);
          // null parameter below specifies HTML response for search
          let params = {
            postData: submission.postData,
          };
          if (aParams) {
            for (let key in aParams) {
              params[key] = aParams[key];
            }
          }
          openUILinkIn(submission.uri.spec, aWhere, params);
        ]]></body>
      </method>
    </implementation>

    <handlers>
      <handler event="command"><![CDATA[
        const target = event.originalTarget;
        if (target.engine) {
          this.currentEngine = target.engine;
        } else if (target.classList.contains("addengine-item")) {
          // Select the installed engine if the installation succeeds
          var installCallback = {
            onSuccess: engine => this.currentEngine = engine
          };
          Services.search.addEngine(target.getAttribute("uri"), null,
                                    target.getAttribute("src"), false,
                                    installCallback);
        } else
          return;

        this.focus();
        this.select();
      ]]></handler>

      <handler event="DOMMouseScroll"
               phase="capturing"
               modifiers="accel"
               action="this.selectEngine(event, (event.detail > 0));"/>

      <handler event="input" action="this.updateGoButtonVisibility();"/>
      <handler event="drop" action="this.updateGoButtonVisibility();"/>

      <handler event="blur">
      <![CDATA[
        // If the input field is still focused then a different window has
        // received focus, ignore the next focus event.
        this._ignoreFocus = (document.activeElement == this._textbox.inputField);
      ]]></handler>

      <handler event="focus">
      <![CDATA[
        // Speculatively connect to the current engine's search URI (and
        // suggest URI, if different) to reduce request latency
        this.currentEngine.speculativeConnect({window,
                                               originAttributes: gBrowser.contentPrincipal
                                                                         .originAttributes});

        if (this._ignoreFocus) {
          // This window has been re-focused, don't show the suggestions
          this._ignoreFocus = false;
          return;
        }

        // Don't open the suggestions if there is no text in the textbox.
        if (!this._textbox.value)
          return;

        // Don't open the suggestions if the mouse was used to focus the
        // textbox, that will be taken care of in the click handler.
        if (Services.focus.getLastFocusMethod(window) & Services.focus.FLAG_BYMOUSE)
          return;

        this.openSuggestionsPanel();
      ]]></handler>

      <handler event="mousedown" phase="capturing">
      <![CDATA[
        if (event.originalTarget.getAttribute("anonid") == "searchbar-search-button") {
          this._clickClosedPopup = this._textbox.popup._isHiding;
        }
      ]]></handler>

      <handler event="mousedown" button="0">
      <![CDATA[
        // Ignore clicks on the search go button.
        if (event.originalTarget.getAttribute("anonid") == "search-go-button") {
          return;
        }

        let isIconClick = event.originalTarget.getAttribute("anonid") == "searchbar-search-button";

        // Ignore clicks on the icon if they were made to close the popup
        if (isIconClick && this._clickClosedPopup) {
          return;
        }

        // Open the suggestions whenever clicking on the search icon or if there
        // is text in the textbox.
        if (isIconClick || this._textbox.value) {
          this.openSuggestionsPanel(true);
        }
      ]]></handler>

    </handlers>
  </binding>

  <binding id="searchbar-textbox"
      extends="chrome://global/content/bindings/autocomplete.xml#autocomplete">
    <implementation>
      <constructor><![CDATA[
        if (document.getBindingParent(this).parentNode.parentNode.localName ==
            "toolbarpaletteitem")
          return;

        if (Services.prefs.getBoolPref("browser.urlbar.clickSelectsAll"))
          this.setAttribute("clickSelectsAll", true);

        var textBox = document.getAnonymousElementByAttribute(this,
                                              "anonid", "textbox-input-box");
        var cxmenu = document.getAnonymousElementByAttribute(textBox,
                                          "anonid", "input-box-contextmenu");
        cxmenu.addEventListener("popupshowing",
                                () => { this.initContextMenu(cxmenu); },
                                {capturing: true, once: true});

        this.setAttribute("aria-owns", this.popup.id);
        document.getBindingParent(this)._textboxInitialized = true;
      ]]></constructor>

      <destructor><![CDATA[
        // If the context menu has never been opened, there won't be anything
        // to remove here.
        // Also, XBL and the customize toolbar code sometimes interact poorly.
        try {
          this.controllers.removeController(this.searchbarController);
        } catch (ex) { }
      ]]></destructor>

      // Add items to context menu and attach controller to handle them the
      // first time the context menu is opened.
      <method name="initContextMenu">
        <parameter name="aMenu"/>
        <body><![CDATA[
          const kXULNS =
            "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
          let stringBundle = document.getBindingParent(this)._stringBundle;

          let pasteAndSearch, suggestMenuItem;
          let element, label, akey;

          element = document.createElementNS(kXULNS, "menuseparator");
          aMenu.appendChild(element);

          let insertLocation = aMenu.firstChild;
          while (insertLocation.nextSibling &&
                 insertLocation.getAttribute("cmd") != "cmd_paste")
            insertLocation = insertLocation.nextSibling;
          if (insertLocation) {
            element = document.createElementNS(kXULNS, "menuitem");
            label = stringBundle.getString("cmd_pasteAndSearch");
            element.setAttribute("label", label);
            element.setAttribute("anonid", "paste-and-search");
            element.setAttribute("oncommand", "BrowserSearch.pasteAndSearch(event)");
            aMenu.insertBefore(element, insertLocation.nextSibling);
            pasteAndSearch = element;
          }

          element = document.createElementNS(kXULNS, "menuitem");
          label = stringBundle.getString("cmd_clearHistory");
          akey = stringBundle.getString("cmd_clearHistory_accesskey");
          element.setAttribute("label", label);
          element.setAttribute("accesskey", akey);
          element.setAttribute("cmd", "cmd_clearhistory");
          aMenu.appendChild(element);

          element = document.createElementNS(kXULNS, "menuitem");
          label = stringBundle.getString("cmd_showSuggestions");
          akey = stringBundle.getString("cmd_showSuggestions_accesskey");
          element.setAttribute("anonid", "toggle-suggest-item");
          element.setAttribute("label", label);
          element.setAttribute("accesskey", akey);
          element.setAttribute("cmd", "cmd_togglesuggest");
          element.setAttribute("type", "checkbox");
          element.setAttribute("autocheck", "false");
          suggestMenuItem = element;
          aMenu.appendChild(element);

          if (AppConstants.platform == "macosx") {
            this.addEventListener("keypress", aEvent => {
              if (aEvent.keyCode == KeyEvent.DOM_VK_F4)
                this.openSearch();
            }, true);
          }

          this.controllers.appendController(this.searchbarController);

          let onpopupshowing = function() {
            BrowserSearch.searchBar._textbox.closePopup();
            if (suggestMenuItem) {
              let enabled =
                Services.prefs.getBoolPref("browser.search.suggest.enabled");
              suggestMenuItem.setAttribute("checked", enabled);
            }

            if (!pasteAndSearch)
              return;
            let controller = document.commandDispatcher.getControllerForCommand("cmd_paste");
            let enabled = controller.isCommandEnabled("cmd_paste");
            if (enabled)
              pasteAndSearch.removeAttribute("disabled");
            else
              pasteAndSearch.setAttribute("disabled", "true");
          };
          aMenu.addEventListener("popupshowing", onpopupshowing);
          onpopupshowing();
        ]]></body>
      </method>

      <!--
        This overrides the searchParam property in autocomplete.xml.  We're
        hijacking this property as a vehicle for delivering the privacy
        information about the window into the guts of nsSearchSuggestions.

        Note that the setter is the same as the parent.  We were not sure whether
        we can override just the getter.  If that proves to be the case, the setter
        can be removed.
      -->
      <property name="searchParam"
                onget="return this.getAttribute('autocompletesearchparam') +
                       (PrivateBrowsingUtils.isWindowPrivate(window) ? '|private' : '');"
                onset="this.setAttribute('autocompletesearchparam', val); return val;"/>

      <!-- This is implemented so that when textbox.value is set directly (e.g.,
           by tests), the one-off query is updated. -->
      <method name="onBeforeValueSet">
        <parameter name="aValue"/>
        <body><![CDATA[
          this.popup.oneOffButtons.query = aValue;
          return aValue;
        ]]></body>
      </method>

      <!--
        This method overrides the autocomplete binding's openPopup (essentially
        duplicating the logic from the autocomplete popup binding's
        openAutocompletePopup method), modifying it so that the popup is aligned with
        the inner textbox, but sized to not extend beyond the search bar border.
      -->
      <method name="openPopup">
        <body><![CDATA[
          // Entering customization mode after the search bar had focus causes
          // the popup to appear again, due to focus returning after the
          // hamburger panel closes. Don't open in that spurious event.
          if (document.documentElement.getAttribute("customizing") == "true") {
            return;
          }

          var popup = this.popup;
          if (!popup.mPopupOpen) {
            // Initially the panel used for the searchbar (PopupSearchAutoComplete
            // in browser.xul) is hidden to avoid impacting startup / new
            // window performance. The base binding's openPopup would normally
            // call the overriden openAutocompletePopup in
            // browser-search-autocomplete-result-popup binding to unhide the popup,
            // but since we're overriding openPopup we need to unhide the panel
            // ourselves.
            popup.hidden = false;

            // Don't roll up on mouse click in the anchor for the search UI.
            if (popup.id == "PopupSearchAutoComplete") {
              popup.setAttribute("norolluponanchor", "true");
            }

            popup.mInput = this;
            // clear any previous selection, see bugs 400671 and 488357
            popup.selectedIndex = -1;

            document.popupNode = null;

            const isRTL = getComputedStyle(this, "").direction == "rtl";

            var outerRect = this.getBoundingClientRect();
            var innerRect = this.inputField.getBoundingClientRect();
            let width = isRTL ?
                        innerRect.right - outerRect.left :
                        outerRect.right - innerRect.left;
            popup.setAttribute("width", width > 100 ? width : 100);

            // invalidate() depends on the width attribute
            popup._invalidate();

            var yOffset = outerRect.bottom - innerRect.bottom;
            popup.openPopup(this.inputField, "after_start", 0, yOffset, false, false);
          }
        ]]></body>
      </method>

      <method name="openSearch">
        <body>
          <![CDATA[
            if (!this.popupOpen) {
              document.getBindingParent(this).openSuggestionsPanel();
              return false;
            }
            return true;
          ]]>
        </body>
      </method>

      <method name="handleEnter">
        <parameter name="event"/>
        <body><![CDATA[
          // Toggle the open state of the add-engine menu button if it's
          // selected.  We're using handleEnter for this instead of listening
          // for the command event because a command event isn't fired.
          if (this.selectedButton &&
              this.selectedButton.getAttribute("anonid") ==
                "addengine-menu-button") {
            this.selectedButton.open = !this.selectedButton.open;
            return true;
          }
          // Otherwise, "call super": do what the autocomplete binding's
          // handleEnter implementation does.
          return this.mController.handleEnter(false, event || null);
        ]]></body>
      </method>

      <!-- override |onTextEntered| in autocomplete.xml -->
      <method name="onTextEntered">
        <parameter name="aEvent"/>
        <body><![CDATA[
          let engine;
          let oneOff = this.selectedButton;
          if (oneOff) {
            if (!oneOff.engine) {
              oneOff.doCommand();
              return;
            }
            engine = oneOff.engine;
          }
          if (this._selectionDetails) {
            BrowserSearch.searchBar.telemetrySearchDetails = this._selectionDetails;
            this._selectionDetails = null;
          }
          document.getBindingParent(this).handleSearchCommand(aEvent, engine);
        ]]></body>
      </method>

      <property name="selectedButton">
        <getter><![CDATA[
          return this.popup.oneOffButtons.selectedButton;
        ]]></getter>
        <setter><![CDATA[
          return this.popup.oneOffButtons.selectedButton = val;
        ]]></setter>
      </property>

      <method name="handleKeyboardNavigation">
        <parameter name="aEvent"/>
        <body><![CDATA[
          let popup = this.popup;
          if (!popup.popupOpen)
            return;

          // accel + up/down changes the default engine and shouldn't affect
          // the selection on the one-off buttons.
          if (aEvent.getModifierState("Accel"))
            return;

          let suggestionsHidden =
            popup.richlistbox.getAttribute("collapsed") == "true";
          let numItems = suggestionsHidden ? 0 : this.popup.matchCount;
          this.popup.oneOffButtons.handleKeyPress(aEvent, numItems, true);
        ]]></body>
      </method>

      <!-- nsIController -->
      <field name="searchbarController" readonly="true"><![CDATA[({
        _self: this,
        supportsCommand(aCommand) {
          return aCommand == "cmd_clearhistory" ||
                 aCommand == "cmd_togglesuggest";
        },

        isCommandEnabled(aCommand) {
          return true;
        },

        doCommand(aCommand) {
          switch (aCommand) {
            case "cmd_clearhistory":
              var param = this._self.getAttribute("autocompletesearchparam");

              BrowserSearch.searchBar.FormHistory.update({ op: "remove", fieldname: param }, null);
              this._self.value = "";
              break;
            case "cmd_togglesuggest":
              let enabled =
                Services.prefs.getBoolPref("browser.search.suggest.enabled");
              Services.prefs.setBoolPref("browser.search.suggest.enabled",
                                         !enabled);
              break;
            default:
              // do nothing with unrecognized command
          }
        }
      })]]></field>
    </implementation>

    <handlers>
      <handler event="input"><![CDATA[
        this.popup.removeAttribute("showonlysettings");
      ]]></handler>

      <handler event="keypress" phase="capturing"
               action="return this.handleKeyboardNavigation(event);"/>

      <handler event="keypress" keycode="VK_UP" modifiers="accel"
               phase="capturing"
               action="document.getBindingParent(this).selectEngine(event, false);"/>

      <handler event="keypress" keycode="VK_DOWN" modifiers="accel"
               phase="capturing"
               action="document.getBindingParent(this).selectEngine(event, true);"/>

      <handler event="keypress" keycode="VK_DOWN" modifiers="alt"
               phase="capturing"
               action="return this.openSearch();"/>

      <handler event="keypress" keycode="VK_UP" modifiers="alt"
               phase="capturing"
               action="return this.openSearch();"/>

      <handler event="dragover">
      <![CDATA[
        var types = event.dataTransfer.types;
        if (types.includes("text/plain") || types.includes("text/x-moz-text-internal"))
          event.preventDefault();
      ]]>
      </handler>

      <handler event="drop">
      <![CDATA[
        var dataTransfer = event.dataTransfer;
        var data = dataTransfer.getData("text/plain");
        if (!data)
          data = dataTransfer.getData("text/x-moz-text-internal");
        if (data) {
          event.preventDefault();
          this.value = data;
          document.getBindingParent(this).openSuggestionsPanel();
        }
      ]]>
      </handler>

    </handlers>
  </binding>

  <binding id="browser-search-autocomplete-result-popup" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete-rich-result-popup">
    <resources>
      <stylesheet src="chrome://browser/content/search/searchbarBindings.css"/>
      <stylesheet src="chrome://browser/skin/searchbar.css"/>
    </resources>
    <content ignorekeys="true" level="top" consumeoutsideclicks="never">
      <xul:hbox anonid="searchbar-engine" xbl:inherits="showonlysettings"
                class="search-panel-header search-panel-current-engine">
        <xul:image class="searchbar-engine-image" xbl:inherits="src"/>
        <xul:label anonid="searchbar-engine-name" flex="1" crop="end"
                   role="presentation"/>
      </xul:hbox>
      <xul:richlistbox anonid="richlistbox" class="autocomplete-richlistbox search-panel-tree" flex="1"/>
      <xul:vbox anonid="search-one-off-buttons" class="search-one-offs"/>
    </content>
    <implementation>
      <method name="openAutocompletePopup">
        <parameter name="aInput"/>
        <parameter name="aElement"/>
        <body><![CDATA[
          // initially the panel is hidden
          // to avoid impacting startup / new window performance
          aInput.popup.hidden = false;

          // this method is defined on the base binding
          this._openAutocompletePopup(aInput, aElement);
        ]]></body>
      </method>

      <method name="onPopupClick">
        <parameter name="aEvent"/>
        <body><![CDATA[
          // Ignore all right-clicks
          if (aEvent.button == 2)
            return;

          var searchBar = BrowserSearch.searchBar;
          var popupForSearchBar = searchBar && searchBar.textbox == this.mInput;
          if (popupForSearchBar) {
            searchBar.telemetrySearchDetails = {
              index: this.selectedIndex,
              kind: "mouse"
            };
          }

          // Check for unmodified left-click, and use default behavior
          if (aEvent.button == 0 && !aEvent.shiftKey && !aEvent.ctrlKey &&
              !aEvent.altKey && !aEvent.metaKey) {
            this.input.controller.handleEnter(true, aEvent);
            return;
          }

          // Check for middle-click or modified clicks on the search bar
          if (popupForSearchBar) {
            BrowserUsageTelemetry.recordSearchbarSelectedResultMethod(
              aEvent,
              this.selectedIndex
            );

            // Handle search bar popup clicks
            var search = this.input.controller.getValueAt(this.selectedIndex);

            // open the search results according to the clicking subtlety
            var where = whereToOpenLink(aEvent, false, true);
            let params = {};

            // But open ctrl/cmd clicks on autocomplete items in a new background tab.
            let modifier = AppConstants.platform == "macosx" ?
                           aEvent.metaKey :
                           aEvent.ctrlKey;
            if (where == "tab" && (aEvent instanceof MouseEvent) &&
                (aEvent.button == 1 || modifier))
              params.inBackground = true;

            // leave the popup open for background tab loads
            if (!(where == "tab" && params.inBackground)) {
              // close the autocomplete popup and revert the entered search term
              this.closePopup();
              this.input.controller.handleEscape();
            }

            searchBar.doSearch(search, where, null, params);
            if (where == "tab" && params.inBackground)
              searchBar.focus();
            else
              searchBar.value = search;
          }
        ]]></body>
      </method>

      <!-- Popup rollup is triggered by native events before the mousedown event
           reaches the DOM. The will be set to true by the popuphiding event and
           false after the mousedown event has been triggered to detect what
           caused rollup. -->
      <field name="_isHiding">false</field>
      <field name="_bundle">null</field>
      <property name="bundle" readonly="true">
        <getter>
          <![CDATA[
            if (!this._bundle) {
              const kBundleURI = "chrome://browser/locale/search.properties";
              this._bundle = Services.strings.createBundle(kBundleURI);
            }
            return this._bundle;
          ]]>
        </getter>
      </property>

      <field name="oneOffButtons" readonly="true">
        document.getAnonymousElementByAttribute(this, "anonid",
                                                "search-one-off-buttons");
      </field>

      <method name="updateHeader">
        <body><![CDATA[
          let currentEngine = Services.search.currentEngine;
          let uri = currentEngine.iconURI;
          if (uri) {
            this.setAttribute("src", uri.spec);
          } else {
            // If the default has just been changed to a provider without icon,
            // avoid showing the icon of the previous default provider.
            this.removeAttribute("src");
          }

          let headerText = this.bundle.formatStringFromName("searchHeader",
                                                            [currentEngine.name], 1);
          document.getAnonymousElementByAttribute(this, "anonid", "searchbar-engine-name")
                  .setAttribute("value", headerText);
          document.getAnonymousElementByAttribute(this, "anonid", "searchbar-engine")
                  .engine = currentEngine;
        ]]></body>
      </method>

      <!-- This is called when a one-off is clicked and when "search in new tab"
           is selected from a one-off context menu. -->
      <method name="handleOneOffSearch">
        <parameter name="event"/>
        <parameter name="engine"/>
        <parameter name="where"/>
        <parameter name="params"/>
        <body><![CDATA[
          let searchbar = document.getElementById("searchbar");
          searchbar.handleSearchCommandWhere(event, engine, where, params);
        ]]></body>
      </method>
    </implementation>

    <handlers>
      <handler event="popupshowing"><![CDATA[
        // Force the panel to have the width of the searchbar rather than
        // the width of the textfield.
        let DOMUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)
                             .getInterface(Ci.nsIDOMWindowUtils);
        let textboxRect = DOMUtils.getBoundsWithoutFlushing(this.mInput);
        let inputRect = DOMUtils.getBoundsWithoutFlushing(this.mInput.inputField);

        // Ensure the panel is wide enough to fit at least 3 engines.
        let minWidth = Math.max(textboxRect.width,
                                this.oneOffButtons.buttonWidth * 3);
        this.style.minWidth = Math.round(minWidth) + "px";
        // Alignment of the panel with the searchbar is obtained with negative
        // margins.
        this.style.marginLeft = (textboxRect.left - inputRect.left) + "px";
        // This second margin is needed when the direction is reversed,
        // eg. when using command+shift+X.
        this.style.marginRight = (inputRect.right - textboxRect.right) + "px";

        // First handle deciding if we are showing the reduced version of the
        // popup containing only the preferences button. We do this if the
        // glass icon has been clicked if the text field is empty.
        let searchbar = document.getElementById("searchbar");
        if (searchbar.hasAttribute("showonlysettings")) {
          searchbar.removeAttribute("showonlysettings");
          this.setAttribute("showonlysettings", "true");

          // Setting this with an xbl-inherited attribute gets overridden the
          // second time the user clicks the glass icon for some reason...
          this.richlistbox.collapsed = true;
        } else {
          this.removeAttribute("showonlysettings");
          // Uncollapse as long as we have a view which has >= 1 row.
          // The autocomplete binding itself will take care of uncollapsing later,
          // if we currently have no rows but end up having some in the future
          // when the search string changes
          this.richlistbox.collapsed = (this.matchCount == 0);
        }

        // Show the current default engine in the top header of the panel.
        this.updateHeader();
      ]]></handler>

      <handler event="popuphiding"><![CDATA[
        this._isHiding = true;
        Services.tm.dispatchToMainThread(() => {
          this._isHiding = false;
        });
      ]]></handler>

      <!-- This handles clicks on the topmost "Foo Search" header in the
           popup (hbox[anonid="searchbar-engine"]). -->
      <handler event="click"><![CDATA[
        if (event.button == 2) {
          // Ignore right clicks.
          return;
        }
        let button = event.originalTarget;
        let engine = button.parentNode.engine;
        if (!engine) {
          return;
        }
        this.oneOffButtons.handleSearchCommand(event, engine);
      ]]></handler>
    </handlers>

  </binding>

  <binding id="search-one-offs">
    <content context="_child">
      <xul:deck anonid="search-panel-one-offs-header"
                selectedIndex="0"
                class="search-panel-header search-panel-current-input">
        <xul:label anonid="searchbar-oneoffheader-search"
                   value="&searchWithHeader.label;"/>
        <xul:hbox anonid="search-panel-searchforwith"
                  class="search-panel-current-input">
          <xul:label anonid="searchbar-oneoffheader-before"
                     value="&searchFor.label;"/>
          <xul:label anonid="searchbar-oneoffheader-searchtext"
                     class="search-panel-input-value"
                     flex="1"
                     crop="end"/>
          <xul:label anonid="searchbar-oneoffheader-after"
                     flex="10000"
                     value="&searchWith.label;"/>
        </xul:hbox>
        <xul:hbox anonid="search-panel-searchonengine"
                  class="search-panel-current-input">
          <xul:label anonid="searchbar-oneoffheader-beforeengine"
                     value="&search.label;"/>
          <xul:label anonid="searchbar-oneoffheader-engine"
                     class="search-panel-input-value"
                     flex="1"
                     crop="end"/>
          <xul:label anonid="searchbar-oneoffheader-afterengine"
                     flex="10000"
                     value="&searchAfter.label;"/>
        </xul:hbox>
      </xul:deck>
      <xul:description anonid="search-panel-one-offs"
                       role="group"
                       class="search-panel-one-offs"
                       xbl:inherits="compact">
        <xul:button anonid="search-settings-compact"
                    oncommand="showSettings();"
                    class="searchbar-engine-one-off-item search-setting-button-compact"
                    tooltiptext="&changeSearchSettings.tooltip;"
                    xbl:inherits="compact"/>
      </xul:description>
      <xul:vbox anonid="add-engines" class="search-add-engines"/>
      <xul:button anonid="search-settings"
                  oncommand="showSettings();"
                  class="search-setting-button search-panel-header"
                  label="&changeSearchSettings.button;"
                  xbl:inherits="compact"/>
      <xul:menupopup anonid="search-one-offs-context-menu">
        <xul:menuitem anonid="search-one-offs-context-open-in-new-tab"
                      label="&searchInNewTab.label;"
                      accesskey="&searchInNewTab.accesskey;"/>
        <xul:menuitem anonid="search-one-offs-context-set-default"
                      label="&searchSetAsDefault.label;"
                      accesskey="&searchSetAsDefault.accesskey;"/>
      </xul:menupopup>
    </content>

    <implementation implements="nsIDOMEventListener,nsIObserver,nsIWeakReference">

      <!-- Width in pixels of the one-off buttons.  49px is the min-width of
           each search engine button, adapt this const when changing the css.
           It's actually 48px + 1px of right border. -->
      <property name="buttonWidth" readonly="true" onget="return 49;"/>

      <field name="_popup">null</field>

      <!-- The popup that contains the one-offs.  This is required, so it should
           never be null or undefined, except possibly before the one-offs are
           used. -->
      <property name="popup">
        <getter><![CDATA[
          return this._popup;
        ]]></getter>
        <setter><![CDATA[
          let events = [
            "popupshowing",
            "popuphidden",
          ];
          if (this._popup) {
            for (let event of events) {
              this._popup.removeEventListener(event, this);
            }
          }
          if (val) {
            for (let event of events) {
              val.addEventListener(event, this);
            }
          }
          this._popup = val;

          // If the popup is already open, rebuild the one-offs now.  The
          // popup may be opening, so check that the state is not closed
          // instead of checking popupOpen.
          if (val && val.state != "closed") {
            this._rebuild();
          }
          return val;
        ]]></setter>
      </property>

      <field name="_textbox">null</field>
      <field name="_textboxWidth">0</field>

      <!-- The textbox associated with the one-offs.  Set this to a textbox to
           automatically keep the related one-offs UI up to date.  Otherwise you
           can leave it null/undefined, and in that case you should update the
           query property manually. -->
      <property name="textbox">
        <getter><![CDATA[
          return this._textbox;
        ]]></getter>
        <setter><![CDATA[
          if (this._textbox) {
            this._textbox.removeEventListener("input", this);
          }
          if (val) {
            val.addEventListener("input", this);
          }
          return this._textbox = val;
        ]]></setter>
      </property>

      <!-- Set this to a string that identifies your one-offs consumer.  It'll
           be appended to telemetry recorded with maybeRecordTelemetry(). -->
      <field name="telemetryOrigin">""</field>

      <field name="_query">""</field>

      <!-- The query string currently shown in the one-offs.  If the textbox
           property is non-null, then this is automatically updated on
           input. -->
      <property name="query">
        <getter><![CDATA[
          return this._query;
        ]]></getter>
        <setter><![CDATA[
          this._query = val;
          if (this.popup && this.popup.popupOpen) {
            this._updateAfterQueryChanged();
          }
          return val;
        ]]></setter>
      </property>

      <field name="_selectedButton">null</field>

      <!-- The selected one-off, a xul:button, including the add-engine button
           and the search-settings button.  Null if no one-off is selected. -->
      <property name="selectedButton">
        <getter><![CDATA[
          return this._selectedButton;
        ]]></getter>
        <setter><![CDATA[
          if (val && val.classList.contains("dummy")) {
            // Never select dummy buttons.
            val = null;
          }
          if (this._selectedButton) {
            this._selectedButton.removeAttribute("selected");
          }
          if (val) {
            val.setAttribute("selected", "true");
          }
          this._selectedButton = val;
          this._updateStateForButton(null);
          if (val && !val.engine) {
            // If the button doesn't have an engine, then clear the popup's
            // selection to indicate that pressing Return while the button is
            // selected will do the button's command, not search.
            this.popup.selectedIndex = -1;
          }
          let event = document.createEvent("Events");
          event.initEvent("SelectedOneOffButtonChanged", true, false);
          this.dispatchEvent(event);
          return val;
        ]]></setter>
      </property>

      <!-- The index of the selected one-off, including the add-engine button
           and the search-settings button.  -1 if no one-off is selected. -->
      <property name="selectedButtonIndex">
        <getter><![CDATA[
          let buttons = this.getSelectableButtons(true);
          for (let i = 0; i < buttons.length; i++) {
            if (buttons[i] == this._selectedButton) {
              return i;
            }
          }
          return -1;
        ]]></getter>
        <setter><![CDATA[
          let buttons = this.getSelectableButtons(true);
          this.selectedButton = buttons[val];
          return val;
        ]]></setter>
      </property>

      <property name="compact" readonly="true">
        <getter><![CDATA[
          return this.getAttribute("compact") == "true";
        ]]></getter>
      </property>

      <field name="buttons" readonly="true">
        document.getAnonymousElementByAttribute(this, "anonid", "search-panel-one-offs");
      </field>
      <field name="header" readonly="true">
        document.getAnonymousElementByAttribute(this, "anonid", "search-panel-one-offs-header");
      </field>
      <field name="addEngines" readonly="true">
        document.getAnonymousElementByAttribute(this, "anonid", "add-engines");
      </field>
      <field name="settingsButton" readonly="true">
        document.getAnonymousElementByAttribute(this, "anonid", "search-settings");
      </field>
      <field name="settingsButtonCompact" readonly="true">
        document.getAnonymousElementByAttribute(this, "anonid", "search-settings-compact");
      </field>

      <field name="_bundle">null</field>

      <property name="bundle" readonly="true">
        <getter><![CDATA[
          if (!this._bundle) {
            const kBundleURI = "chrome://browser/locale/search.properties";
            this._bundle = Services.strings.createBundle(kBundleURI);
          }
          return this._bundle;
        ]]></getter>
      </property>

      <!-- When a context menu is opened on a one-off button, this is set to the
           engine of that button for use with the context menu actions. -->
      <field name="_contextEngine">null</field>

      <constructor><![CDATA[
        // Prevent popup events from the context menu from reaching the autocomplete
        // binding (or other listeners).
        let menu = document.getAnonymousElementByAttribute(this, "anonid", "search-one-offs-context-menu");
        let listener = aEvent => aEvent.stopPropagation();
        menu.addEventListener("popupshowing", listener);
        menu.addEventListener("popuphiding", listener);
        menu.addEventListener("popupshown", aEvent => {
          this._ignoreMouseEvents = true;
          aEvent.stopPropagation();
        });
        menu.addEventListener("popuphidden", aEvent => {
          this._ignoreMouseEvents = false;
          aEvent.stopPropagation();
        });

        // Add weak referenced observers to invalidate our cached list of engines.
        Services.prefs.addObserver("browser.search.hiddenOneOffs", this, true);
        Services.obs.addObserver(this, "browser-search-engine-modified", true);

        // Rebuild the buttons when the theme changes.  See bug 1357800 for
        // details.  Summary: On Linux, switching between themes can cause a row
        // of buttons to disappear.
        Services.obs.addObserver(this, "lightweight-theme-changed", true);
      ]]></constructor>

      <!-- This handles events outside the one-off buttons, like on the popup
           and textbox. -->
      <method name="handleEvent">
        <parameter name="event"/>
        <body><![CDATA[
          switch (event.type) {
            case "input":
              // Allow the consumer's input to override its value property with
              // a oneOffSearchQuery property.  That way if the value is not
              // actually what the user typed (e.g., it's autofilled, or it's a
              // mozaction URI), the consumer has some way of providing it.
              this.query = event.target.oneOffSearchQuery || event.target.value;
              break;
            case "popupshowing":
              this._rebuild();
              break;
            case "popuphidden":
              Services.tm.dispatchToMainThread(() => {
                this.selectedButton = null;
                this._contextEngine = null;
              });
              break;
          }
        ]]></body>
      </method>

      <method name="observe">
        <parameter name="aEngine"/>
        <parameter name="aTopic"/>
        <parameter name="aData"/>
        <body><![CDATA[
          // Make sure the engine list is refetched next time it's needed.
          this._engines = null;
        ]]></body>
      </method>

      <method name="showSettings">
        <body><![CDATA[
          BrowserUITelemetry.countSearchSettingsEvent(this.telemetryOrigin);

          openPreferences("paneSearch", {origin: "contentSearch"});

          // If the preference tab was already selected, the panel doesn't
          // close itself automatically.
          this.popup.hidePopup();
        ]]></body>
      </method>

      <!-- Updates the parts of the UI that show the query string. -->
      <method name="_updateAfterQueryChanged">
        <body><![CDATA[
          let headerSearchText =
            document.getAnonymousElementByAttribute(this, "anonid",
                                                    "searchbar-oneoffheader-searchtext");
          headerSearchText.setAttribute("value", this.query);
          let groupText;
          let isOneOffSelected =
            this.selectedButton &&
            this.selectedButton.classList.contains("searchbar-engine-one-off-item");
          // Typing de-selects the settings or opensearch buttons at the bottom
          // of the search panel, as typing shows the user intends to search.
          if (this.selectedButton && !isOneOffSelected)
            this.selectedButton = null;
          if (this.query) {
            groupText = headerSearchText.previousSibling.value +
                        '"' + headerSearchText.value + '"' +
                        headerSearchText.nextSibling.value;
            if (!isOneOffSelected)
              this.header.selectedIndex = 1;
          } else {
            let noSearchHeader =
              document.getAnonymousElementByAttribute(this, "anonid",
                                                      "searchbar-oneoffheader-search");
            groupText = noSearchHeader.value;
            if (!isOneOffSelected)
              this.header.selectedIndex = 0;
          }
          this.buttons.setAttribute("aria-label", groupText);
        ]]></body>
      </method>

      <field name="_engines">null</field>
      <property name="engines" readonly="true">
        <getter><![CDATA[
          if (this._engines)
            return this._engines;
          let currentEngineNameToIgnore;
          if (!this.getAttribute("includecurrentengine"))
            currentEngineNameToIgnore = Services.search.currentEngine.name;

          let pref = Services.prefs.getStringPref("browser.search.hiddenOneOffs");
          let hiddenList = pref ? pref.split(",") : [];

          this._engines = Services.search.getVisibleEngines().filter(e => {
            let name = e.name;
            return (!currentEngineNameToIgnore ||
                    name != currentEngineNameToIgnore) &&
                   !hiddenList.includes(name);
          });

          return this._engines;
        ]]></getter>
      </property>

      <!-- Builds all the UI. -->
      <method name="_rebuild">
        <body><![CDATA[
          // Update the 'Search for <keywords> with:" header.
          this._updateAfterQueryChanged();

          // Handle opensearch items. This needs to be done before building the
          // list of one off providers, as that code will return early if all the
          // alternative engines are hidden.
          // Skip this in compact mode, ie. for the urlbar.
          if (!this.compact)
            this._rebuildAddEngineList();

          // Check if the one-off buttons really need to be rebuilt.
          if (this._textbox) {
            // We can't get a reliable value for the popup width without flushing,
            // but the popup width won't change if the textbox width doesn't.
            let DOMUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)
                                 .getInterface(Ci.nsIDOMWindowUtils);
            let textboxWidth =
              DOMUtils.getBoundsWithoutFlushing(this._textbox).width;
            // We can return early if neither the list of engines nor the panel
            // width has changed.
            if (this._engines && this._textboxWidth == textboxWidth) {
              return;
            }
            this._textboxWidth = textboxWidth;
          }

          // Finally, build the list of one-off buttons.
          while (this.buttons.firstChild != this.settingsButtonCompact)
            this.buttons.firstChild.remove();
          // Remove the trailing empty text node introduced by the binding's
          // content markup above.
          if (this.settingsButtonCompact.nextSibling)
            this.settingsButtonCompact.nextSibling.remove();

          let engines = this.engines;
          let oneOffCount = engines.length;
          let collapsed = !oneOffCount ||
                          (oneOffCount == 1 && engines[0].name == Services.search.currentEngine.name);

          // header is a xul:deck so collapsed doesn't work on it, see bug 589569.
          this.header.hidden = this.buttons.collapsed = collapsed;

          if (collapsed)
            return;

          let panelWidth = parseInt(this.popup.clientWidth);

          // There's one weird thing to guard against: when layout pixels
          // aren't an integral multiple of device pixels, the last button
          // of each row sometimes gets pushed to the next row, depending on the
          // panel and button widths.
          // This is likely because the clientWidth getter rounds the value, but
          // the panel's border width is not an integer.
          // As a workaround, decrement the width if the scale is not an integer.
          let scale = window.QueryInterface(Ci.nsIInterfaceRequestor)
                            .getInterface(Ci.nsIDOMWindowUtils)
                            .screenPixelsPerCSSPixel;
          if (Math.floor(scale) != scale) {
            --panelWidth;
          }

          // The + 1 is because the last button doesn't have a right border.
          let enginesPerRow = Math.floor((panelWidth + 1) / this.buttonWidth);
          let buttonWidth = Math.floor(panelWidth / enginesPerRow);
          // There will be an emtpy area of:
          //   panelWidth - enginesPerRow * buttonWidth  px
          // at the end of each row.

          // If the <description> tag with the list of search engines doesn't have
          // a fixed height, the panel will be sized incorrectly, causing the bottom
          // of the suggestion <tree> to be hidden.
          if (this.compact)
            ++oneOffCount;
          let rowCount = Math.ceil(oneOffCount / enginesPerRow);
          let height = rowCount * 33; // 32px per row, 1px border.
          this.buttons.setAttribute("height", height + "px");

          // Ensure we can refer to the settings buttons by ID:
          let origin = this.telemetryOrigin;
          this.settingsButton.id = origin + "-anon-search-settings";
          this.settingsButtonCompact.id = origin + "-anon-search-settings-compact";

          const kXULNS =
            "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";

          let dummyItems = enginesPerRow - (oneOffCount % enginesPerRow || enginesPerRow);
          for (let i = 0; i < engines.length; ++i) {
            let engine = engines[i];
            let button = document.createElementNS(kXULNS, "button");
            button.id = this._buttonIDForEngine(engine);
            let uri = "chrome://browser/skin/search-engine-placeholder.png";
            if (engine.iconURI) {
              uri = engine.iconURI.spec;
            }
            button.setAttribute("image", uri);
            button.setAttribute("class", "searchbar-engine-one-off-item");
            button.setAttribute("tooltiptext", engine.name);
            button.setAttribute("width", buttonWidth);
            button.engine = engine;

            if ((i + 1) % enginesPerRow == 0)
              button.classList.add("last-of-row");

            if (i + 1 == engines.length)
              button.classList.add("last-engine");

            if (i >= oneOffCount + dummyItems - enginesPerRow)
              button.classList.add("last-row");

            this.buttons.insertBefore(button, this.settingsButtonCompact);
          }

          let hasDummyItems = !!dummyItems;
          while (dummyItems) {
            let button = document.createElementNS(kXULNS, "button");
            button.setAttribute("class", "searchbar-engine-one-off-item dummy last-row");
            button.setAttribute("width", buttonWidth);

            if (!--dummyItems)
              button.classList.add("last-of-row");

            this.buttons.insertBefore(button, this.settingsButtonCompact);
          }

          if (this.compact) {
            this.settingsButtonCompact.setAttribute("width", buttonWidth);
            if (rowCount == 1 && hasDummyItems) {
              // When there's only one row, make the compact settings button
              // hug the right edge of the panel.  It may not due to the panel's
              // width not being an integral multiple of the button width.  (See
              // the "There will be an emtpy area" comment above.)  Increase the
              // width of the last dummy item by the remainder.
              let remainder = panelWidth - (enginesPerRow * buttonWidth);
              let width = remainder + buttonWidth;
              let lastDummyItem = this.settingsButtonCompact.previousSibling;
              lastDummyItem.setAttribute("width", width);
            }
          }
        ]]></body>
      </method>

      <!-- If a page offers more than this number of engines, the add-engines
           menu button is shown, instead of showing the engines directly in the
           popup. -->
      <field name="_addEngineMenuThreshold">5</field>

      <method name="_rebuildAddEngineList">
        <body><![CDATA[
        let list = this.addEngines;
        while (list.firstChild) {
          list.firstChild.remove();
        }

        // Add a button for each engine that the page in the selected browser
        // offers, except when there are too many offered engines.
        // The popup isn't designed to handle too many (by scrolling for
        // example), so a page could break the popup by offering too many.
        // Instead, add a single menu button with a submenu of all the engines.

        if (!gBrowser.selectedBrowser.engines) {
          return;
        }

        const kXULNS =
          "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";

        let engines = gBrowser.selectedBrowser.engines;
        let tooManyEngines = engines.length > this._addEngineMenuThreshold;

        if (tooManyEngines) {
          // Make the top-level menu button.
          let button = document.createElementNS(kXULNS, "toolbarbutton");
          list.appendChild(button);
          button.classList.add("addengine-item", "badged-button");
          button.setAttribute("anonid", "addengine-menu-button");
          button.setAttribute("type", "menu");
          button.setAttribute("label",
            this.bundle.GetStringFromName("cmd_addFoundEngineMenu"));
          button.setAttribute("crop", "end");
          button.setAttribute("pack", "start");

          // Set the menu button's image to the image of the first engine.  The
          // offered engines may have differing images, so there's no perfect
          // choice here.
          let engine = engines[0];
          if (engine.icon) {
            button.setAttribute("image", engine.icon);
          }

          // Now make the button's child menupopup.
          list = document.createElementNS(kXULNS, "menupopup");
          button.appendChild(list);
          list.setAttribute("anonid", "addengine-menu");
          list.setAttribute("position", "topright topleft");

          // Events from child menupopups bubble up to the autocomplete binding,
          // which breaks it, so prevent these events from propagating.
          let suppressEventTypes = [
            "popupshowing",
            "popuphiding",
            "popupshown",
            "popuphidden",
          ];
          for (let type of suppressEventTypes) {
            list.addEventListener(type, event => {
              event.stopPropagation();
            });
          }
        }

        // Finally, add the engines to the list.  If there aren't too many
        // engines, the list is the add-engines vbox.  Otherwise it's the
        // menupopup created earlier.  In the latter case, create menuitem
        // elements instead of buttons, because buttons don't get keyboard
        // handling for free inside menupopups.
        let eltType = tooManyEngines ? "menuitem" : "toolbarbutton";
        for (let engine of engines) {
          let button = document.createElementNS(kXULNS, eltType);
          button.classList.add("addengine-item");
          if (!tooManyEngines) {
            button.classList.add("badged-button");
          }
          button.id = this.telemetryOrigin + "-add-engine-" +
                      this._fixUpEngineNameForID(engine.title);
          let label = this.bundle.formatStringFromName("cmd_addFoundEngine",
                                                       [engine.title], 1);
          button.setAttribute("label", label);
          button.setAttribute("crop", "end");
          button.setAttribute("tooltiptext", engine.title + "\n" + engine.uri);
          button.setAttribute("uri", engine.uri);
          button.setAttribute("title", engine.title);
          if (engine.icon) {
            button.setAttribute("image", engine.icon);
          }
          if (tooManyEngines) {
            button.classList.add("menuitem-iconic");
          } else {
            button.setAttribute("pack", "start");
          }
          list.appendChild(button);
        }
        ]]></body>
      </method>

      <method name="_buttonIDForEngine">
        <parameter name="engine"/>
        <body><![CDATA[
          return this.telemetryOrigin + "-engine-one-off-item-" +
                 this._fixUpEngineNameForID(engine.name);
        ]]></body>
      </method>

      <method name="_fixUpEngineNameForID">
        <parameter name="name"/>
        <body><![CDATA[
          return name.replace(/ /g, "-");
        ]]></body>
      </method>

      <method name="_buttonForEngine">
        <parameter name="engine"/>
        <body><![CDATA[
          return document.getElementById(this._buttonIDForEngine(engine));
        ]]></body>
      </method>

      <!--
        Updates the popup and textbox for the currently selected or moused-over
        button.

        @param mousedOverButton
               The currently moused-over button, or null if there isn't one.
      -->
      <method name="_updateStateForButton">
        <parameter name="mousedOverButton"/>
        <body><![CDATA[
          let button = mousedOverButton;

          // Ignore dummy buttons.
          if (button && button.classList.contains("dummy")) {
            button = null;
          }

          // If there's no moused-over button, then the one-offs should reflect
          // the selected button, if any.
          button = button || this.selectedButton;

          if (!button) {
            this.header.selectedIndex = this.query ? 1 : 0;
            if (this.textbox) {
              this.textbox.removeAttribute("aria-activedescendant");
            }
            return;
          }

          if (button.classList.contains("searchbar-engine-one-off-item") &&
              button.engine) {
            let headerEngineText =
              document.getAnonymousElementByAttribute(this, "anonid",
                                                      "searchbar-oneoffheader-engine");
            this.header.selectedIndex = 2;
            headerEngineText.value = button.engine.name;
          } else {
            this.header.selectedIndex = this.query ? 1 : 0;
          }
          if (this.textbox) {
            this.textbox.setAttribute("aria-activedescendant", button.id);
          }
        ]]></body>
      </method>

      <method name="getSelectableButtons">
        <parameter name="aIncludeNonEngineButtons"/>
        <body><![CDATA[
          let buttons = [];
          for (let oneOff = this.buttons.firstChild; oneOff; oneOff = oneOff.nextSibling) {
            // oneOff may be a text node since the list xul:description contains
            // whitespace and the compact settings button.  See the markup
            // above.  _rebuild removes text nodes, but it may not have been
            // called yet (because e.g. the popup hasn't been opened yet).
            if (oneOff.nodeType == Node.ELEMENT_NODE) {
              if (oneOff.classList.contains("dummy") ||
                  oneOff.classList.contains("search-setting-button-compact"))
                break;
              buttons.push(oneOff);
            }
          }

          if (aIncludeNonEngineButtons) {
            for (let addEngine = this.addEngines.firstChild; addEngine; addEngine = addEngine.nextSibling) {
              buttons.push(addEngine);
            }
            buttons.push(this.compact ? this.settingsButtonCompact : this.settingsButton);
          }

          return buttons;
        ]]></body>
      </method>

      <method name="handleSearchCommand">
        <parameter name="aEvent"/>
        <parameter name="aEngine"/>
        <parameter name="aForceNewTab"/>
        <body><![CDATA[
          let where = "current";
          let params;

          // Open ctrl/cmd clicks on one-off buttons in a new background tab.
          if (aForceNewTab) {
            where = "tab";
            if (Services.prefs.getBoolPref("browser.tabs.loadInBackground")) {
              params = {
                inBackground: true,
              };
            }
          } else {
            var newTabPref = Services.prefs.getBoolPref("browser.search.openintab");
            if (((aEvent instanceof KeyboardEvent) && aEvent.altKey) ^ newTabPref)
              where = "tab";
            if ((aEvent instanceof MouseEvent) &&
                (aEvent.button == 1 || aEvent.getModifierState("Accel"))) {
              where = "tab";
              params = {
                inBackground: true,
              };
            }
          }

          this.popup.handleOneOffSearch(aEvent, aEngine, where, params);
        ]]></body>
      </method>

      <!--
        Increments or decrements the index of the currently selected one-off.

        @param aForward
               If true, the index is incremented, and if false, the index is
               decremented.
        @param aIncludeNonEngineButtons
               If true, non-dummy buttons that do not have engines are included.
               These buttons include the OpenSearch and settings buttons.  For
               example, if the currently selected button is an engine button,
               the next button is the settings button, and you pass true for
               aForward, then passing true for this value would cause the
               settings to be selected.  Passing false for this value would
               cause the selection to clear or wrap around, depending on what
               value you passed for the aWrapAround parameter.
        @param aWrapAround
               If true, the selection wraps around between the first and last
               buttons.
        @return True if the selection can continue to advance after this method
                returns and false if not.
      -->
      <method name="advanceSelection">
        <parameter name="aForward"/>
        <parameter name="aIncludeNonEngineButtons"/>
        <parameter name="aWrapAround"/>
        <body><![CDATA[
          let buttons = this.getSelectableButtons(aIncludeNonEngineButtons);
          let index;
          if (this.selectedButton) {
            let inc = aForward ? 1 : -1;
            let oldIndex = buttons.indexOf(this.selectedButton);
            index = ((oldIndex + inc) + buttons.length) % buttons.length;
            if (!aWrapAround &&
                ((aForward && index <= oldIndex) ||
                 (!aForward && oldIndex <= index))) {
              // The index has wrapped around, but wrapping around isn't
              // allowed.
              index = -1;
            }
          } else {
            index = aForward ? 0 : buttons.length - 1;
          }
          this.selectedButton = index < 0 ? null : buttons[index];
        ]]></body>
      </method>

      <!--
        This handles key presses specific to the one-off buttons like Tab and
        Alt+Up/Down, and Up/Down keys within the buttons.  Since one-off buttons
        are always used in conjunction with a list of some sort (in this.popup),
        it also handles Up/Down keys that cross the boundaries between list
        items and the one-off buttons.

        If this method handles the key press, then event.defaultPrevented will
        be true when it returns.

        @param event
               The key event.
        @param numListItems
               The number of items in the list.  The reason that this is a
               parameter at all is that the list may contain items at the end
               that should be ignored, depending on the consumer.  That's true
               for the urlbar for example.
        @param allowEmptySelection
               Pass true if it's OK that neither the list nor the one-off
               buttons contains a selection.  Pass false if either the list or
               the one-off buttons (or both) should always contain a selection.
        @param textboxUserValue
               When the last list item is selected and the user presses Down,
               the first one-off becomes selected and the textbox value is
               restored to the value that the user typed.  Pass that value here.
               However, if you pass true for allowEmptySelection, you don't need
               to pass anything for this parameter.  (Pass undefined or null.)
      -->
      <method name="handleKeyPress">
        <parameter name="event"/>
        <parameter name="numListItems"/>
        <parameter name="allowEmptySelection"/>
        <parameter name="textboxUserValue"/>
        <body><![CDATA[
          if (!this.popup) {
            return;
          }
          let handled = this._handleKeyPress(event, numListItems,
                                             allowEmptySelection,
                                             textboxUserValue);
          if (handled) {
            event.preventDefault();
            event.stopPropagation();
          }
        ]]></body>
      </method>

      <method name="_handleKeyPress">
        <parameter name="event"/>
        <parameter name="numListItems"/>
        <parameter name="allowEmptySelection"/>
        <parameter name="textboxUserValue"/>
        <body><![CDATA[
          if (this.compact && this.buttons.collapsed)
            return false;
          if (event.keyCode == KeyEvent.DOM_VK_RIGHT &&
              this.selectedButton &&
              this.selectedButton.getAttribute("anonid") ==
                "addengine-menu-button") {
            // If the add-engine overflow menu item is selected and the user
            // presses the right arrow key, open the submenu.  Unfortunately
            // handling the left arrow key -- to close the popup -- isn't
            // straightforward.  Once the popup is open, it consumes all key
            // events.  Setting ignorekeys=handled on it doesn't help, since the
            // popup handles all arrow keys.  Setting ignorekeys=true on it does
            // mean that the popup no longer consumes the left arrow key, but
            // then it no longer handles up/down keys to select items in the
            // popup.
            this.selectedButton.open = true;
            return true;
          }

          // Handle the Tab key, but only if non-Shift modifiers aren't also
          // pressed to avoid clobbering other shortcuts (like the Alt+Tab
          // browser tab switcher).  The reason this uses getModifierState() and
          // checks for "AltGraph" is that when you press Shift-Alt-Tab,
          // event.altKey is actually false for some reason, at least on macOS.
          // getModifierState("Alt") is also false, but "AltGraph" is true.
          if (event.keyCode == KeyEvent.DOM_VK_TAB &&
              !event.getModifierState("Alt") &&
              !event.getModifierState("AltGraph") &&
              !event.getModifierState("Control") &&
              !event.getModifierState("Meta")) {
            if (this.getAttribute("disabletab") == "true" ||
                (event.shiftKey &&
                  this.selectedButtonIndex <= 0) ||
                (!event.shiftKey &&
                 this.selectedButtonIndex ==
                   this.getSelectableButtons(true).length - 1)) {
              this.selectedButton = null;
              return false;
            }
            this.popup.selectedIndex = -1;
            this.advanceSelection(!event.shiftKey, true, false);
            return !!this.selectedButton;
          }

          if (event.keyCode == KeyboardEvent.DOM_VK_UP) {
            if (event.altKey) {
              // Keep the currently selected result in the list (if any) as a
              // secondary "alt" selection and move the selection up within the
              // buttons.
              this.advanceSelection(false, false, false);
              return true;
            }
            if (numListItems == 0) {
              this.advanceSelection(false, true, false);
              return true;
            }
            if (this.popup.selectedIndex > 0) {
              // Moving up within the list.  The autocomplete controller should
              // handle this case.  A button may be selected, so null it.
              this.selectedButton = null;
              return false;
            }
            if (this.popup.selectedIndex == 0) {
              // Moving up from the top of the list.
              if (allowEmptySelection) {
                // Let the autocomplete controller remove selection in the list
                // and revert the typed text in the textbox.
                return false;
              }
              // Wrap selection around to the last button.
              if (this.textbox && typeof(textboxUserValue) == "string") {
                this.textbox.value = textboxUserValue;
              }
              this.advanceSelection(false, true, true);
              return true;
            }
            if (!this.selectedButton) {
              // Moving up from no selection in the list or the buttons, back
              // down to the last button.
              this.advanceSelection(false, true, true);
              return true;
            }
            if (this.selectedButtonIndex == 0) {
              // Moving up from the buttons to the bottom of the list.
              this.selectedButton = null;
              return false;
            }
            // Moving up/left within the buttons.
            this.advanceSelection(false, true, false);
            return true;
          }

          if (event.keyCode == KeyboardEvent.DOM_VK_DOWN) {
            if (event.altKey) {
              // Keep the currently selected result in the list (if any) as a
              // secondary "alt" selection and move the selection down within
              // the buttons.
              this.advanceSelection(true, false, false);
              return true;
            }
            if (numListItems == 0) {
              this.advanceSelection(true, true, false);
              return true;
            }
            if (this.popup.selectedIndex >= 0 &&
                this.popup.selectedIndex < numListItems - 1) {
              // Moving down within the list.  The autocomplete controller
              // should handle this case.  A button may be selected, so null it.
              this.selectedButton = null;
              return false;
            }
            if (this.popup.selectedIndex == numListItems - 1) {
              // Moving down from the last item in the list to the buttons.
              this.selectedButtonIndex = 0;
              if (allowEmptySelection) {
                // Let the autocomplete controller remove selection in the list
                // and revert the typed text in the textbox.
                return false;
              }
              if (this.textbox && typeof(textboxUserValue) == "string") {
                this.textbox.value = textboxUserValue;
              }
              this.popup.selectedIndex = -1;
              return true;
            }
            if (this.selectedButton) {
              let buttons = this.getSelectableButtons(true);
              if (this.selectedButtonIndex == buttons.length - 1) {
                // Moving down from the buttons back up to the top of the list.
                this.selectedButton = null;
                if (allowEmptySelection) {
                  // Prevent the selection from wrapping around to the top of
                  // the list by returning true, since the list currently has no
                  // selection.  Nothing should be selected after handling this
                  // Down key.
                  return true;
                }
                return false;
              }
              // Moving down/right within the buttons.
              this.advanceSelection(true, true, false);
              return true;
            }
            return false;
          }

          if (event.keyCode == KeyboardEvent.DOM_VK_LEFT) {
            if (this.selectedButton &&
                (this.compact || this.selectedButton.engine)) {
              // Moving left within the buttons.
              this.advanceSelection(false, this.compact, true);
              return true;
            }
            return false;
          }

          if (event.keyCode == KeyboardEvent.DOM_VK_RIGHT) {
            if (this.selectedButton &&
                (this.compact || this.selectedButton.engine)) {
              // Moving right within the buttons.
              this.advanceSelection(true, this.compact, true);
              return true;
            }
            return false;
          }

          return false;
        ]]></body>
      </method>

      <!--
        If the given event is related to the one-offs, this method records
        one-off telemetry for it.  this.telemetryOrigin will be appended to the
        computed source, so make sure you set that first.

        @param aEvent
               An event, like a click on a one-off button.
        @param aOpenUILinkWhere
               The "where" passed to openUILink.
        @param aOpenUILinkParams
               The "params" passed to openUILink.
        @return True if telemetry was recorded and false if not.
      -->
      <method name="maybeRecordTelemetry">
        <parameter name="aEvent"/>
        <parameter name="aOpenUILinkWhere"/>
        <parameter name="aOpenUILinkParams"/>
        <body><![CDATA[
          if (!aEvent) {
            return false;
          }

          let source = null;
          let type = "unknown";
          let engine = null;
          let target = aEvent.originalTarget;

          if (aEvent instanceof KeyboardEvent) {
            type = "key";
            if (this.selectedButton) {
              source = "oneoff";
              engine = this.selectedButton.engine;
            }
          } else if (aEvent instanceof MouseEvent) {
            type = "mouse";
            if (target.classList.contains("searchbar-engine-one-off-item")) {
              source = "oneoff";
              engine = target.engine;
            }
          } else if ((aEvent instanceof XULCommandEvent) &&
                     target.getAttribute("anonid") ==
                       "search-one-offs-context-open-in-new-tab") {
            source = "oneoff-context";
            engine = this._contextEngine;
          }

          if (!source) {
            return false;
          }

          if (this.telemetryOrigin) {
            source += "-" + this.telemetryOrigin;
          }

          let tabBackground = aOpenUILinkWhere == "tab" &&
                              aOpenUILinkParams &&
                              aOpenUILinkParams.inBackground;
          let where = tabBackground ? "tab-background" : aOpenUILinkWhere;
          BrowserSearch.recordOneoffSearchInTelemetry(engine, source, type,
                                                      where);
          return true;
        ]]></body>
      </method>

      <!-- All this stuff is to make the add-engines menu button behave like an
           actual menu.  The add-engines menu button is shown when there are
           many engines offered by the current site. -->
      <field name="_addEngineMenuTimeoutMs">200</field>
      <field name="_addEngineMenuTimeout">null</field>
      <field name="_addEngineMenuShouldBeOpen">false</field>

      <method name="_resetAddEngineMenuTimeout">
        <body><![CDATA[
        if (this._addEngineMenuTimeout) {
          clearTimeout(this._addEngineMenuTimeout);
        }
        this._addEngineMenuTimeout = setTimeout(() => {
          delete this._addEngineMenuTimeout;
          let button = document.getAnonymousElementByAttribute(
            this, "anonid", "addengine-menu-button"
          );
          button.open = this._addEngineMenuShouldBeOpen;
        }, this._addEngineMenuTimeoutMs);
        ]]></body>
      </method>

    </implementation>

    <handlers>

      <handler event="mousedown"><![CDATA[
        let target = event.originalTarget;
        if (target.getAttribute("anonid") == "addengine-menu-button") {
          return;
        }
        // Required to receive click events from the buttons on Linux.
        event.preventDefault();
      ]]></handler>

      <handler event="mousemove"><![CDATA[
        let target = event.originalTarget;

        // Handle mouseover on the add-engine menu button and its popup items.
        if (target.getAttribute("anonid") == "addengine-menu-button" ||
            (target.localName == "menuitem" &&
             target.classList.contains("addengine-item"))) {
          let menuButton = document.getAnonymousElementByAttribute(
            this, "anonid", "addengine-menu-button"
          );
          this._updateStateForButton(menuButton);
          this._addEngineMenuShouldBeOpen = true;
          this._resetAddEngineMenuTimeout();
          return;
        }

        if (target.localName != "button")
          return;

        // Ignore mouse events when the context menu is open.
         if (this._ignoreMouseEvents)
           return;

        let isOneOff =
          target.classList.contains("searchbar-engine-one-off-item") &&
          !target.classList.contains("dummy");
        if (isOneOff ||
            target.classList.contains("addengine-item") ||
            target.classList.contains("search-setting-button")) {
          this._updateStateForButton(target);
        }
      ]]></handler>

      <handler event="mouseout"><![CDATA[

        let target = event.originalTarget;

        // Handle mouseout on the add-engine menu button and its popup items.
        if (target.getAttribute("anonid") == "addengine-menu-button" ||
            (target.localName == "menuitem" &&
             target.classList.contains("addengine-item"))) {
          this._updateStateForButton(null);
          this._addEngineMenuShouldBeOpen = false;
          this._resetAddEngineMenuTimeout();
          return;
        }

        if (target.localName != "button") {
          return;
        }

        // Don't update the mouseover state if the context menu is open.
        if (this._ignoreMouseEvents)
          return;

        this._updateStateForButton(null);
      ]]></handler>

      <handler event="click"><![CDATA[
        if (event.button == 2)
          return; // ignore right clicks.

        let button = event.originalTarget;
        let engine = button.engine;

        if (!engine)
          return;

        // Select the clicked button so that consumers can easily tell which
        // button was acted on.
        this.selectedButton = button;
        this.handleSearchCommand(event, engine);
      ]]></handler>

      <handler event="command"><![CDATA[
        let target = event.originalTarget;
        if (target.classList.contains("addengine-item")) {
          // On success, hide the panel and tell event listeners to reshow it to
          // show the new engine.
          let installCallback = {
            onSuccess: engine => {
              this._rebuild();
            },
            onError(errorCode) {
              if (errorCode != Ci.nsISearchInstallCallback.ERROR_DUPLICATE_ENGINE) {
                // Download error is shown by the search service
                return;
              }
              const kSearchBundleURI = "chrome://global/locale/search/search.properties";
              let searchBundle = Services.strings.createBundle(kSearchBundleURI);
              let brandBundle = document.getElementById("bundle_brand");
              let brandName = brandBundle.getString("brandShortName");
              let title = searchBundle.GetStringFromName("error_invalid_engine_title");
              let text = searchBundle.formatStringFromName("error_duplicate_engine_msg",
                                                           [brandName, target.getAttribute("uri")], 2);
              Services.prompt.QueryInterface(Ci.nsIPromptFactory);
              let prompt = Services.prompt.getPrompt(gBrowser.contentWindow, Ci.nsIPrompt);
              prompt.QueryInterface(Ci.nsIWritablePropertyBag2);
              prompt.setPropertyAsBool("allowTabModal", true);
              prompt.alert(title, text);
            }
          };
          Services.search.addEngine(target.getAttribute("uri"), null,
                                    target.getAttribute("image"), false,
                                    installCallback);
        }
        let anonid = target.getAttribute("anonid");
        if (anonid == "search-one-offs-context-open-in-new-tab") {
          // Select the context-clicked button so that consumers can easily
          // tell which button was acted on.
          this.selectedButton = this._buttonForEngine(this._contextEngine);
          this.handleSearchCommand(event, this._contextEngine, true);
        }
        if (anonid == "search-one-offs-context-set-default") {
          let currentEngine = Services.search.currentEngine;

          if (!this.getAttribute("includecurrentengine")) {
            // Make the target button of the context menu reflect the current
            // search engine first. Doing this as opposed to rebuilding all the
            // one-off buttons avoids flicker.
            let button = this._buttonForEngine(this._contextEngine);
            button.id = this._buttonIDForEngine(currentEngine);
            let uri = "chrome://browser/skin/search-engine-placeholder.png";
            if (currentEngine.iconURI)
              uri = currentEngine.iconURI.spec;
            button.setAttribute("image", uri);
            button.setAttribute("tooltiptext", currentEngine.name);
            button.engine = currentEngine;
          }

          Services.search.currentEngine = this._contextEngine;
        }
      ]]></handler>

      <handler event="contextmenu"><![CDATA[
        let target = event.originalTarget;
        // Prevent the context menu from appearing except on the one off buttons.
        if (!target.classList.contains("searchbar-engine-one-off-item") ||
            target.classList.contains("dummy")) {
          event.preventDefault();
          return;
        }
        document.getAnonymousElementByAttribute(this, "anonid", "search-one-offs-context-set-default")
                .setAttribute("disabled", target.engine == Services.search.currentEngine);

        this._contextEngine = target.engine;
      ]]></handler>
    </handlers>

  </binding>

</bindings>
PK
!<�T���4chrome/browser/content/browser/search/searchReset.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

ChromeUtils.import("resource://gre/modules/Services.jsm");

const TELEMETRY_RESULT_ENUM = {
  RESTORED_DEFAULT: 0,
  KEPT_CURRENT: 1,
  CHANGED_ENGINE: 2,
  CLOSED_PAGE: 3,
  OPENED_SETTINGS: 4
};

window.onload = function() {
  let defaultEngine = document.getElementById("defaultEngine");
  let originalDefault = Services.search.originalDefaultEngine;
  defaultEngine.textContent = originalDefault.name;
  defaultEngine.style.backgroundImage =
    'url("' + originalDefault.iconURI.spec + '")';

  document.getElementById("searchResetChangeEngine").focus();
  window.addEventListener("unload", recordPageClosed);
  document.getElementById("linkSettingsPage")
          .addEventListener("click", openingSettings);
};

function doSearch() {
  let queryString = "";
  let purpose = "";
  let params = window.location.href.match(/^about:searchreset\?([^#]*)/);
  if (params) {
    params = params[1].split("&");
    for (let param of params) {
      if (param.startsWith("data="))
        queryString = decodeURIComponent(param.slice(5));
      else if (param.startsWith("purpose="))
        purpose = param.slice(8);
    }
  }

  let engine = Services.search.currentEngine;
  let submission = engine.getSubmission(queryString, null, purpose);

  window.removeEventListener("unload", recordPageClosed);

  let win = window.QueryInterface(Ci.nsIInterfaceRequestor)
                  .getInterface(Ci.nsIWebNavigation)
                  .QueryInterface(Ci.nsIDocShellTreeItem)
                  .rootTreeItem
                  .QueryInterface(Ci.nsIInterfaceRequestor)
                  .getInterface(Ci.nsIDOMWindow);
  win.openUILinkIn(submission.uri.spec, "current", false, submission.postData);
}

function openingSettings() {
  record(TELEMETRY_RESULT_ENUM.OPENED_SETTINGS);
  savePref("customized");
  window.removeEventListener("unload", recordPageClosed);
}

function savePref(value) {
  const statusPref = "browser.search.reset.status";
  if (Services.prefs.getCharPref(statusPref, "") == "pending")
    Services.prefs.setCharPref(statusPref, value);
}

function record(result) {
  Services.telemetry.getHistogramById("SEARCH_RESET_RESULT").add(result);
}

function keepCurrentEngine() {
  // Calling the currentEngine setter will force a correct loadPathHash to be
  // written for this engine, so that we don't prompt the user again.
  Services.search.currentEngine = Services.search.currentEngine;
  record(TELEMETRY_RESULT_ENUM.KEPT_CURRENT);
  savePref("declined");
  doSearch();
}

function changeSearchEngine() {
  let engine = Services.search.originalDefaultEngine;
  if (engine.hidden)
    engine.hidden = false;
  Services.search.currentEngine = engine;

  record(TELEMETRY_RESULT_ENUM.RESTORED_DEFAULT);
  savePref("accepted");

  doSearch();
}

function recordPageClosed() {
  record(TELEMETRY_RESULT_ENUM.CLOSED_PAGE);
}
PK
!<\��d		7chrome/browser/content/browser/search/searchReset.xhtml<?xml version="1.0" encoding="UTF-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/. -->

<!DOCTYPE html [
  <!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
  %htmlDTD;
  <!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
  %globalDTD;
  <!ENTITY % searchresetDTD SYSTEM "chrome://browser/locale/aboutSearchReset.dtd">
  %searchresetDTD;
  <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
  %brandDTD;
]>

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
  <head>
    <title>&searchreset.tabtitle;</title>
    <link rel="stylesheet" type="text/css" media="all"
          href="chrome://global/skin/in-content/info-pages.css"/>
    <link rel="stylesheet" type="text/css" media="all"
          href="chrome://browser/skin/searchReset.css"/>
    <link rel="icon" type="image/png"
          href="chrome://browser/skin/favicon-search-16.svg"/>

    <script type="application/javascript"
            src="chrome://browser/content/search/searchReset.js"/>
  </head>

  <body dir="&locale.dir;">

    <div class="container">
      <div class="title">
        <h1 class="title-text">&searchreset.pageTitle;</h1>
      </div>

      <div class="description">
        <p>&searchreset.pageInfo1;</p>
        <p>&searchreset.selector.label;<span id="defaultEngine"/></p>

        <p>&searchreset.beforelink.pageInfo2;<a id="linkSettingsPage" href="about:preferences#search">&searchreset.link.pageInfo2;</a>&searchreset.afterlink.pageInfo2;</p>
      </div>

      <div class="button-container">
        <xul:button id="searchResetKeepCurrent"
                    label="&searchreset.noChangeButton;"
                    accesskey="&searchreset.noChangeButton.access;"
                    oncommand="keepCurrentEngine();"/>
        <xul:button class="primary"
                    id="searchResetChangeEngine"
                    label="&searchreset.changeEngineButton;"
                    accesskey="&searchreset.changeEngineButton.access;"
                    oncommand="changeSearchEngine();"/>
      </div>
    </div>

  </body>
</html>
PK
!<
0���6chrome/browser/content/browser/setDesktopBackground.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");

var gSetBackground = {
  _position: AppConstants.platform == "macosx" ? "STRETCH" : "",
  _backgroundColor: AppConstants.platform != "macosx" ? 0 : undefined,
  _screenWidth: 0,
  _screenHeight: 0,
  _image: null,
  _canvas: null,
  _imageName: null,

  get _shell() {
    return Cc["@mozilla.org/browser/shell-service;1"]
             .getService(Ci.nsIShellService);
  },

  load() {
    this._canvas = document.getElementById("screen");
    this._screenWidth = screen.width;
    this._screenHeight = screen.height;
    if (AppConstants.platform == "macosx") {
      document.documentElement.getButton("accept").hidden = true;
    }
    if (this._screenWidth / this._screenHeight >= 1.6)
      document.getElementById("monitor").setAttribute("aspectratio", "16:10");

    if (AppConstants.platform == "win") {
      // Hide fill + fit options if < Win7 since they don't work.
      var version = Services.sysinfo.getProperty("version");
      var isWindows7OrHigher = (parseFloat(version) >= 6.1);
      if (!isWindows7OrHigher) {
        document.getElementById("fillPosition").hidden = true;
        document.getElementById("fitPosition").hidden = true;
      }
    }

    // make sure that the correct dimensions will be used
    setTimeout(function(self) {
      self.init(window.arguments[0], window.arguments[1]);
    }, 0, this);
  },

  init(aImage, aImageName) {
    this._image = aImage;
    this._imageName = aImageName;

    // set the size of the coordinate space
    this._canvas.width = this._canvas.clientWidth;
    this._canvas.height = this._canvas.clientHeight;

    var ctx = this._canvas.getContext("2d");
    ctx.scale(this._canvas.clientWidth / this._screenWidth, this._canvas.clientHeight / this._screenHeight);

    if (AppConstants.platform != "macosx") {
      this._initColor();
    } else {
      // Make sure to reset the button state in case the user has already
      // set an image as their desktop background.
      var setDesktopBackground = document.getElementById("setDesktopBackground");
      setDesktopBackground.hidden = false;
      var bundle = document.getElementById("backgroundBundle");
      setDesktopBackground.label = bundle.getString("DesktopBackgroundSet");
      setDesktopBackground.disabled = false;

      document.getElementById("showDesktopPreferences").hidden = true;
    }
    this.updatePosition();
  },

  setDesktopBackground() {
    if (AppConstants.platform != "macosx") {
      document.persist("menuPosition", "value");
      this._shell.desktopBackgroundColor = this._hexStringToLong(this._backgroundColor);
    } else {
      Services.obs.addObserver(this, "shell:desktop-background-changed");

      var bundle = document.getElementById("backgroundBundle");
      var setDesktopBackground = document.getElementById("setDesktopBackground");
      setDesktopBackground.disabled = true;
      setDesktopBackground.label = bundle.getString("DesktopBackgroundDownloading");
    }
    this._shell.setDesktopBackground(
      this._image,
      Ci.nsIShellService["BACKGROUND_" + this._position],
      this._imageName);
  },

  updatePosition() {
    var ctx = this._canvas.getContext("2d");
    ctx.clearRect(0, 0, this._screenWidth, this._screenHeight);

    if (AppConstants.platform != "macosx") {
      this._position = document.getElementById("menuPosition").value;
    }

    switch (this._position) {
      case "TILE":
        ctx.save();
        ctx.fillStyle = ctx.createPattern(this._image, "repeat");
        ctx.fillRect(0, 0, this._screenWidth, this._screenHeight);
        ctx.restore();
        break;
      case "STRETCH":
        ctx.drawImage(this._image, 0, 0, this._screenWidth, this._screenHeight);
        break;
      case "CENTER": {
        let x = (this._screenWidth - this._image.naturalWidth) / 2;
        let y = (this._screenHeight - this._image.naturalHeight) / 2;
        ctx.drawImage(this._image, x, y);
        break;
      }
      case "FILL": {
        // Try maxing width first, overflow height.
        let widthRatio = this._screenWidth / this._image.naturalWidth;
        let width = this._image.naturalWidth * widthRatio;
        let height = this._image.naturalHeight * widthRatio;
        if (height < this._screenHeight) {
          // Height less than screen, max height and overflow width.
          let heightRatio = this._screenHeight / this._image.naturalHeight;
          width = this._image.naturalWidth * heightRatio;
          height = this._image.naturalHeight * heightRatio;
        }
        let x = (this._screenWidth - width) / 2;
        let y = (this._screenHeight - height) / 2;
        ctx.drawImage(this._image, x, y, width, height);
        break;
      }
      case "FIT": {
        // Try maxing width first, top and bottom borders.
        let widthRatio = this._screenWidth / this._image.naturalWidth;
        let width = this._image.naturalWidth * widthRatio;
        let height = this._image.naturalHeight * widthRatio;
        let x = 0;
        let y = (this._screenHeight - height) / 2;
        if (height > this._screenHeight) {
          // Height overflow, maximise height, side borders.
          let heightRatio = this._screenHeight / this._image.naturalHeight;
          width = this._image.naturalWidth * heightRatio;
          height = this._image.naturalHeight * heightRatio;
          x = (this._screenWidth - width) / 2;
          y = 0;
        }
        ctx.drawImage(this._image, x, y, width, height);
        break;
      }
    }
  }
};

if (AppConstants.platform != "macosx") {
  gSetBackground._initColor = function() {
    var color = this._shell.desktopBackgroundColor;

    const rMask = 4294901760;
    const gMask = 65280;
    const bMask = 255;
    var r = (color & rMask) >> 16;
    var g = (color & gMask) >> 8;
    var b = (color & bMask);
    this.updateColor(this._rgbToHex(r, g, b));

    var colorpicker = document.getElementById("desktopColor");
    colorpicker.color = this._backgroundColor;
  };

  gSetBackground.updateColor = function(aColor) {
    this._backgroundColor = aColor;
    this._canvas.style.backgroundColor = aColor;
  };

  // Converts a color string in the format "#RRGGBB" to an integer.
  gSetBackground._hexStringToLong = function(aString) {
    return parseInt(aString.substring(1, 3), 16) << 16 |
           parseInt(aString.substring(3, 5), 16) << 8 |
           parseInt(aString.substring(5, 7), 16);
  };

  gSetBackground._rgbToHex = function(aR, aG, aB) {
    return "#" + [aR, aG, aB].map(aInt => aInt.toString(16).replace(/^(.)$/, "0$1"))
                             .join("").toUpperCase();
  };
} else {
  gSetBackground.observe = function(aSubject, aTopic, aData) {
    if (aTopic == "shell:desktop-background-changed") {
      document.getElementById("setDesktopBackground").hidden = true;
      document.getElementById("showDesktopPreferences").hidden = false;

      Services.obs.removeObserver(this, "shell:desktop-background-changed");
    }
  };

  gSetBackground.showDesktopPrefs = function() {
    this._shell.openApplication(Ci.nsIMacShellService.APPLICATION_DESKTOP);
  };
}
PK
!<qz		7chrome/browser/content/browser/setDesktopBackground.xul<?xml version="1.0"?> <!-- -*- Mode: HTML -*- --> 


<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> 
<?xml-stylesheet href="chrome://browser/skin/setDesktopBackground.css" type="text/css"?>


<!DOCTYPE dialog [
<!ENTITY % setDesktopBackgroundDTD SYSTEM "chrome://browser/locale/setDesktopBackground.dtd">
%setDesktopBackgroundDTD;
]>


<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        xmlns:html="http://www.w3.org/1999/xhtml"
        windowtype="Shell:SetDesktopBackground"
        buttons="accept,cancel"
        buttonlabelaccept="&setDesktopBackground.title;"
        onload="gSetBackground.load();"
        ondialogaccept="gSetBackground.setDesktopBackground();"
        title="&setDesktopBackground.title;"
        style="width: 30em;">

    <stringbundle id="backgroundBundle"
                  src="chrome://browser/locale/shellservice.properties"/>
    <script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>
    <script type="application/javascript" src="chrome://browser/content/setDesktopBackground.js"/>
    <script type="application/javascript" src="chrome://global/content/contentAreaUtils.js"/>

    <hbox align="center">
      <label value="&position.label;"/>
      <menulist id="menuPosition"
                label="&position.label;" 
                oncommand="gSetBackground.updatePosition();">
        <menupopup>
          <menuitem label="&center.label;"  value="CENTER"/>
          <menuitem label="&tile.label;"    value="TILE"/>
          <menuitem label="&stretch.label;" value="STRETCH"/>
          <menuitem label="&fill.label;"    value="FILL" id="fillPosition"/>
          <menuitem label="&fit.label;"     value="FIT"  id="fitPosition"/>
        </menupopup>
      </menulist>
      <spacer flex="1"/>
      <label value="&color.label;"/>
      <colorpicker id="desktopColor"
                   type="button" 
                   onchange="gSetBackground.updateColor(this.color);"/> 
    </hbox>
    <groupbox align="center">
      <caption label="&preview.label;"/>
      <stack>
        <!-- if width and height are not present, they default to 300x150 and stretch the stack -->
        <html:canvas id="screen" width="1" height="1"/>
        <image id="monitor"/>
      </stack>
    </groupbox>
    


</dialog>
PK
!<�.����/chrome/browser/content/browser/static-robot.png�PNG


IHDR���R$PLTE���������͕���?�-���f666ccc�uvJtRNS@��fjIDATxc`` `T C������p�1�B\S����d�6Gv #,�5�(rq�)1\�v�Y+g�t,�Z`�K�]�@�� ���VI��� ��vBj��!�IEND�B`�PK
!<�Ӻ`4chrome/browser/content/browser/syncedtabs/sidebar.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://services-sync/SyncedTabs.jsm");
ChromeUtils.import("resource:///modules/syncedtabs/SyncedTabsDeckComponent.js");

ChromeUtils.defineModuleGetter(this, "fxAccounts",
                               "resource://gre/modules/FxAccounts.jsm");

var syncedTabsDeckComponent = new SyncedTabsDeckComponent({window, SyncedTabs, fxAccounts});

let onLoaded = () => {
  syncedTabsDeckComponent.init();
  document.getElementById("template-container").appendChild(syncedTabsDeckComponent.container);
};

let onUnloaded = () => {
  removeEventListener("DOMContentLoaded", onLoaded);
  removeEventListener("unload", onUnloaded);
  syncedTabsDeckComponent.uninit();
};

addEventListener("DOMContentLoaded", onLoaded);
addEventListener("unload", onUnloaded);
PK
!<����7chrome/browser/content/browser/syncedtabs/sidebar.xhtml<?xml version="1.0" encoding="UTF-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/. -->

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" [
  <!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
  %browserDTD;
  <!ENTITY % globalDTD
    SYSTEM "chrome://global/locale/global.dtd">
  %globalDTD;
  <!ENTITY % syncBrandDTD
    SYSTEM "chrome://browser/locale/syncBrand.dtd">
  %syncBrandDTD;
]>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
  <head>
    <script src="chrome://browser/content/syncedtabs/sidebar.js" type="application/javascript"></script>
    <script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>

    <link rel="stylesheet" type="text/css" media="all" href="chrome://browser/skin/syncedtabs/sidebar.css"/>
    <link rel="stylesheet" type="text/css" media="all" href="chrome://global/skin/"/>
    <link rel="stylesheet" type="text/css" media="all" href="chrome://global/skin/textbox.css"/>
    <link rel="stylesheet" type="text/css" media="all" href="chrome://browser/content/browser.css"/>
    <title>&syncedTabs.sidebar.label;</title>
  </head>

  <body dir="&locale.dir;" role="application">
    <template id="client-template">
      <div class="item client" role="option" tabindex="-1">
        <div class="item-title-container">
          <div class="item-twisty-container"></div>
          <div class="item-icon-container"></div>
          <p class="item-title"></p>
        </div>
        <div class="item-tabs-list"></div>
      </div>
    </template>
    <template id="empty-client-template">
      <div class="item empty client" role="option" tabindex="-1">
        <div class="item-title-container">
          <div class="item-twisty-container"></div>
          <div class="item-icon-container"></div>
          <p class="item-title"></p>
        </div>
        <div class="item-tabs-list">
          <div class="item empty" role="option" tabindex="-1">
            <div class="item-title-container">
              <div class="item-icon-container"></div>
              <p class="item-title">&syncedTabs.sidebar.notabs.label;</p>
            </div>
          </div>
        </div>
      </div>
    </template>
    <template id="tab-template">
      <div class="item tab" role="option" tabindex="-1">
        <div class="item-title-container">
          <div class="item-icon-container"></div>
          <p class="item-title"></p>
        </div>
      </div>
    </template>

    <template id="tabs-container-template">
      <div class="tabs-container">
        <div class="list" role="listbox"></div>
      </div>
    </template>

    <template id="deck-template">
      <div class="deck">
        <div class="tabs-fetching sync-state">
          <!-- Show intentionally blank panel, see bug 1239845 -->
        </div>
        <div class="notAuthedInfo sync-state">
          <div class="syncIllustration"></div>
          <p class="instructions">&syncedTabs.sidebar.notsignedin.label;</p>
          <button class="button sync-prefs">&fxaSignIn.label;</button>
        </div>
        <div class="reauth sync-state">
          <div class="syncIllustrationIssue"></div>
          <p class="instructions">&syncedTabs.sidebar.notsignedin.label;</p>
          <button class="button sync-prefs">&fxaSignIn.label;</button>
        </div>
        <div class="unverified sync-state">
          <div class="syncIllustrationIssue"></div>
          <p class="instructions">&syncedTabs.sidebar.unverified.label;</p>
          <button class="button sync-prefs">&syncedTabs.sidebar.openprefs.label;</button>
        </div>
        <div class="singleDeviceInfo sync-state">
          <div class="syncIllustrationIssue"></div>
          <p class="instructions">&syncedTabs.sidebar.noclients.subtitle;</p>
          <button class="button connect-device">&syncedTabs.sidebar.connectAnotherDevice;</button>
        </div>
        <div class="tabs-disabled sync-state">
          <div class="syncIllustrationIssue"></div>
          <p class="instructions">&syncedTabs.sidebar.tabsnotsyncing.label;</p>
          <button class="button sync-prefs">&syncedTabs.sidebar.openprefs.label;</button>
        </div>
      </div>
    </template>

    <div class="content-container">
      <!-- the non-scrollable header -->
      <div class="content-header">
        <div class="sidebar-search-container tabs-container sync-state">
          <div class="search-box">
            <div class="textbox-input-box">
              <input type="text"
                     class="tabsFilter textbox-input"
                     placeholder="&syncedTabs.sidebar.searchPlaceholder;"
                     tabindex="1"/>
              <div class="textbox-search-icons">
                <a class="textbox-search-clear"></a>
              </div>
            </div>
          </div>
        </div>
      </div>
      <!-- the scrollable content area where our templates are inserted -->
      <div id="template-container" class="content-scrollable" tabindex="-1">
      </div>
    </div>
  </body>
</html>
PK
!<�]�1q1q-chrome/browser/content/browser/tab-content.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* This content script contains code that requires a tab browser. */

/* eslint-env mozilla/frame-script */

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");

ChromeUtils.defineModuleGetter(this, "E10SUtils",
  "resource://gre/modules/E10SUtils.jsm");
ChromeUtils.defineModuleGetter(this, "BrowserUtils",
  "resource://gre/modules/BrowserUtils.jsm");
ChromeUtils.defineModuleGetter(this, "Utils",
  "resource://gre/modules/sessionstore/Utils.jsm");
ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
  "resource://gre/modules/PrivateBrowsingUtils.jsm");
ChromeUtils.defineModuleGetter(this, "AboutReader",
  "resource://gre/modules/AboutReader.jsm");
ChromeUtils.defineModuleGetter(this, "ReaderMode",
  "resource://gre/modules/ReaderMode.jsm");
XPCOMUtils.defineLazyGetter(this, "SimpleServiceDiscovery", function() {
  let ssdp = ChromeUtils.import("resource://gre/modules/SimpleServiceDiscovery.jsm", {}).SimpleServiceDiscovery;
  // Register targets
  ssdp.registerDevice({
    id: "roku:ecp",
    target: "roku:ecp",
    factory(aService) {
      ChromeUtils.import("resource://gre/modules/RokuApp.jsm");
      return new RokuApp(aService);
    },
    types: ["video/mp4"],
    extensions: ["mp4"]
  });
  return ssdp;
});

// TabChildGlobal
var global = this;


addEventListener("MozDOMPointerLock:Entered", function(aEvent) {
  sendAsyncMessage("PointerLock:Entered", {
    originNoSuffix: aEvent.target.nodePrincipal.originNoSuffix
  });
});

addEventListener("MozDOMPointerLock:Exited", function(aEvent) {
  sendAsyncMessage("PointerLock:Exited");
});


addMessageListener("Browser:HideSessionRestoreButton", function(message) {
  // Hide session restore button on about:home
  let doc = content.document;
  let container;
  if (doc.documentURI.toLowerCase() == "about:home" &&
      (container = doc.getElementById("sessionRestoreContainer"))) {
    container.hidden = true;
  }
});


addMessageListener("Browser:Reload", function(message) {
  /* First, we'll try to use the session history object to reload so
   * that framesets are handled properly. If we're in a special
   * window (such as view-source) that has no session history, fall
   * back on using the web navigation's reload method.
   */

  let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
  try {
    let sh = webNav.sessionHistory;
    if (sh)
      webNav = sh.QueryInterface(Ci.nsIWebNavigation);
  } catch (e) {
  }

  let reloadFlags = message.data.flags;
  try {
    E10SUtils.wrapHandlingUserInput(content, message.data.handlingUserInput,
                                    () => webNav.reload(reloadFlags));
  } catch (e) {
  }
});

addMessageListener("MixedContent:ReenableProtection", function() {
  docShell.mixedContentChannel = null;
});

var AboutHomeListener = {
  init(chromeGlobal) {
    chromeGlobal.addEventListener("AboutHomeLoad", this, false, true);
  },

  get isAboutHome() {
    return content.document.documentURI.toLowerCase() == "about:home";
  },

  handleEvent(aEvent) {
    if (!this.isAboutHome) {
      return;
    }
    switch (aEvent.type) {
      case "AboutHomeLoad":
        this.onPageLoad();
        break;
      case "click":
        this.onClick(aEvent);
        break;
      case "pagehide":
        this.onPageHide(aEvent);
        break;
    }
  },

  receiveMessage(aMessage) {
    if (!this.isAboutHome) {
      return;
    }
    switch (aMessage.name) {
      case "AboutHome:Update":
        this.onUpdate(aMessage.data);
        break;
    }
  },

  onUpdate(aData) {
    let doc = content.document;
    if (aData.showRestoreLastSession && !PrivateBrowsingUtils.isContentWindowPrivate(content))
      doc.getElementById("launcher").setAttribute("session", "true");

    // Inject search engine and snippets URL.
    let docElt = doc.documentElement;
    // Set snippetsVersion last, which triggers to show the snippets when it's set.
    docElt.setAttribute("snippetsURL", aData.snippetsURL);
    if (aData.showKnowYourRights)
      docElt.setAttribute("showKnowYourRights", "true");
    docElt.setAttribute("snippetsVersion", aData.snippetsVersion);
  },

  onPageLoad() {
    addMessageListener("AboutHome:Update", this);
    addEventListener("click", this, true);
    addEventListener("pagehide", this, true);

    sendAsyncMessage("AboutHome:MaybeShowMigrateMessage");
    sendAsyncMessage("AboutHome:RequestUpdate");
  },

  onClick(aEvent) {
    if (!aEvent.isTrusted || // Don't trust synthetic events
        aEvent.button == 2 || aEvent.target.localName != "button") {
      return;
    }

    let originalTarget = aEvent.originalTarget;
    let ownerDoc = originalTarget.ownerDocument;
    if (ownerDoc.documentURI != "about:home") {
      // This shouldn't happen, but we're being defensive.
      return;
    }

    let elmId = originalTarget.getAttribute("id");

    switch (elmId) {
      case "restorePreviousSession":
        sendAsyncMessage("AboutHome:RestorePreviousSession");
        ownerDoc.getElementById("launcher").removeAttribute("session");
        break;

      case "downloads":
        sendAsyncMessage("AboutHome:Downloads");
        break;

      case "bookmarks":
        sendAsyncMessage("AboutHome:Bookmarks");
        break;

      case "history":
        sendAsyncMessage("AboutHome:History");
        break;

      case "addons":
        sendAsyncMessage("AboutHome:Addons");
        break;

      case "sync":
        sendAsyncMessage("AboutHome:Sync");
        break;

      case "settings":
        sendAsyncMessage("AboutHome:Settings");
        break;
    }
  },

  onPageHide(aEvent) {
    if (aEvent.target.defaultView.frameElement) {
      return;
    }
    removeMessageListener("AboutHome:Update", this);
    removeEventListener("click", this, true);
    removeEventListener("pagehide", this, true);
  },
};
AboutHomeListener.init(this);

var AboutReaderListener = {

  _articlePromise: null,

  _isLeavingReaderableReaderMode: false,

  init() {
    addEventListener("AboutReaderContentLoaded", this, false, true);
    addEventListener("DOMContentLoaded", this, false);
    addEventListener("pageshow", this, false);
    addEventListener("pagehide", this, false);
    addMessageListener("Reader:ToggleReaderMode", this);
    addMessageListener("Reader:PushState", this);
  },

  receiveMessage(message) {
    switch (message.name) {
      case "Reader:ToggleReaderMode":
        if (!this.isAboutReader) {
          this._articlePromise = ReaderMode.parseDocument(content.document).catch(Cu.reportError);
          ReaderMode.enterReaderMode(docShell, content);
        } else {
          this._isLeavingReaderableReaderMode = this.isReaderableAboutReader;
          ReaderMode.leaveReaderMode(docShell, content);
        }
        break;

      case "Reader:PushState":
        this.updateReaderButton(!!(message.data && message.data.isArticle));
        break;
    }
  },

  get isAboutReader() {
    if (!content) {
      return false;
    }
    return content.document.documentURI.startsWith("about:reader");
  },

  get isReaderableAboutReader() {
    return this.isAboutReader &&
      !content.document.documentElement.dataset.isError;
  },

  handleEvent(aEvent) {
    if (aEvent.originalTarget.defaultView != content) {
      return;
    }

    switch (aEvent.type) {
      case "AboutReaderContentLoaded":
        if (!this.isAboutReader) {
          return;
        }

        if (content.document.body) {
          // Update the toolbar icon to show the "reader active" icon.
          sendAsyncMessage("Reader:UpdateReaderButton");
          new AboutReader(global, content, this._articlePromise);
          this._articlePromise = null;
        }
        break;

      case "pagehide":
        this.cancelPotentialPendingReadabilityCheck();
        // this._isLeavingReaderableReaderMode is used here to keep the Reader Mode icon
        // visible in the location bar when transitioning from reader-mode page
        // back to the readable source page.
        sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: this._isLeavingReaderableReaderMode });
        if (this._isLeavingReaderableReaderMode) {
          this._isLeavingReaderableReaderMode = false;
        }
        break;

      case "pageshow":
        // If a page is loaded from the bfcache, we won't get a "DOMContentLoaded"
        // event, so we need to rely on "pageshow" in this case.
        if (aEvent.persisted) {
          this.updateReaderButton();
        }
        break;
      case "DOMContentLoaded":
        this.updateReaderButton();
        break;

    }
  },

  /**
   * NB: this function will update the state of the reader button asynchronously
   * after the next mozAfterPaint call (assuming reader mode is enabled and
   * this is a suitable document). Calling it on things which won't be
   * painted is not going to work.
   */
  updateReaderButton(forceNonArticle) {
    if (!ReaderMode.isEnabledForParseOnLoad || this.isAboutReader ||
        !content || !(content.document instanceof content.HTMLDocument) ||
        content.document.mozSyntheticDocument) {
      return;
    }

    this.scheduleReadabilityCheckPostPaint(forceNonArticle);
  },

  cancelPotentialPendingReadabilityCheck() {
    if (this._pendingReadabilityCheck) {
      removeEventListener("MozAfterPaint", this._pendingReadabilityCheck);
      delete this._pendingReadabilityCheck;
    }
  },

  scheduleReadabilityCheckPostPaint(forceNonArticle) {
    if (this._pendingReadabilityCheck) {
      // We need to stop this check before we re-add one because we don't know
      // if forceNonArticle was true or false last time.
      this.cancelPotentialPendingReadabilityCheck();
    }
    this._pendingReadabilityCheck = this.onPaintWhenWaitedFor.bind(this, forceNonArticle);
    addEventListener("MozAfterPaint", this._pendingReadabilityCheck);
  },

  onPaintWhenWaitedFor(forceNonArticle, event) {
    // In non-e10s, we'll get called for paints other than ours, and so it's
    // possible that this page hasn't been laid out yet, in which case we
    // should wait until we get an event that does relate to our layout. We
    // determine whether any of our content got painted by checking if there
    // are any painted rects.
    if (!event.clientRects.length) {
      return;
    }

    this.cancelPotentialPendingReadabilityCheck();
    // Only send updates when there are articles; there's no point updating with
    // |false| all the time.
    if (ReaderMode.isProbablyReaderable(content.document)) {
      sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: true });
    } else if (forceNonArticle) {
      sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: false });
    }
  },
};
AboutReaderListener.init();


var ContentSearchMediator = {

  whitelist: new Set([
    "about:home",
    "about:newtab",
  ]),

  init(chromeGlobal) {
    chromeGlobal.addEventListener("ContentSearchClient", this, true, true);
    addMessageListener("ContentSearch", this);
  },

  handleEvent(event) {
    if (this._contentWhitelisted) {
      this._sendMsg(event.detail.type, event.detail.data);
    }
  },

  receiveMessage(msg) {
    if (msg.data.type == "AddToWhitelist") {
      for (let uri of msg.data.data) {
        this.whitelist.add(uri);
      }
      this._sendMsg("AddToWhitelistAck");
      return;
    }
    if (this._contentWhitelisted) {
      this._fireEvent(msg.data.type, msg.data.data);
    }
  },

  get _contentWhitelisted() {
    return this.whitelist.has(content.document.documentURI);
  },

  _sendMsg(type, data = null) {
    sendAsyncMessage("ContentSearch", {
      type,
      data,
    });
  },

  _fireEvent(type, data = null) {
    let event = Cu.cloneInto({
      detail: {
        type,
        data,
      },
    }, content);
    content.dispatchEvent(new content.CustomEvent("ContentSearchService",
                                                  event));
  },
};
ContentSearchMediator.init(this);

var PageStyleHandler = {
  init() {
    addMessageListener("PageStyle:Switch", this);
    addMessageListener("PageStyle:Disable", this);
    addEventListener("pageshow", () => this.sendStyleSheetInfo());
  },

  get markupDocumentViewer() {
    return docShell.contentViewer;
  },

  sendStyleSheetInfo() {
    let filteredStyleSheets = this._filterStyleSheets(this.getAllStyleSheets());

    sendAsyncMessage("PageStyle:StyleSheets", {
      filteredStyleSheets,
      authorStyleDisabled: this.markupDocumentViewer.authorStyleDisabled,
      preferredStyleSheetSet: content.document.preferredStyleSheetSet
    });
  },

  getAllStyleSheets(frameset = content) {
    let selfSheets = Array.slice(frameset.document.styleSheets);
    let subSheets = Array.map(frameset.frames, frame => this.getAllStyleSheets(frame));
    return selfSheets.concat(...subSheets);
  },

  receiveMessage(msg) {
    switch (msg.name) {
      case "PageStyle:Switch":
        this.markupDocumentViewer.authorStyleDisabled = false;
        this._stylesheetSwitchAll(content, msg.data.title);
        break;

      case "PageStyle:Disable":
        this.markupDocumentViewer.authorStyleDisabled = true;
        break;
    }

    this.sendStyleSheetInfo();
  },

  _stylesheetSwitchAll(frameset, title) {
    if (!title || this._stylesheetInFrame(frameset, title)) {
      this._stylesheetSwitchFrame(frameset, title);
    }

    for (let i = 0; i < frameset.frames.length; i++) {
      // Recurse into sub-frames.
      this._stylesheetSwitchAll(frameset.frames[i], title);
    }
  },

  _stylesheetSwitchFrame(frame, title) {
    var docStyleSheets = frame.document.styleSheets;

    for (let i = 0; i < docStyleSheets.length; ++i) {
      let docStyleSheet = docStyleSheets[i];
      if (docStyleSheet.title) {
        docStyleSheet.disabled = (docStyleSheet.title != title);
      } else if (docStyleSheet.disabled) {
        docStyleSheet.disabled = false;
      }
    }
  },

  _stylesheetInFrame(frame, title) {
    return Array.some(frame.document.styleSheets, (styleSheet) => styleSheet.title == title);
  },

  _filterStyleSheets(styleSheets) {
    let result = [];

    for (let currentStyleSheet of styleSheets) {
      if (!currentStyleSheet.title)
        continue;

      // Skip any stylesheets that don't match the screen media type.
      if (currentStyleSheet.media.length > 0) {
        let mediaQueryList = currentStyleSheet.media.mediaText;
        if (!content.matchMedia(mediaQueryList).matches) {
          continue;
        }
      }

      let URI;
      try {
        if (!currentStyleSheet.ownerNode ||
            // special-case style nodes, which have no href
            currentStyleSheet.ownerNode.nodeName.toLowerCase() != "style") {
          URI = Services.io.newURI(currentStyleSheet.href);
        }
      } catch (e) {
        if (e.result != Cr.NS_ERROR_MALFORMED_URI) {
          throw e;
        }
        continue;
      }

      // We won't send data URIs all of the way up to the parent, as these
      // can be arbitrarily large.
      let sentURI = (!URI || URI.scheme == "data") ? null : URI.spec;

      result.push({
        title: currentStyleSheet.title,
        disabled: currentStyleSheet.disabled,
        href: sentURI,
      });
    }

    return result;
  },
};
PageStyleHandler.init();

// Keep a reference to the translation content handler to avoid it it being GC'ed.
var trHandler = null;
if (Services.prefs.getBoolPref("browser.translation.detectLanguage")) {
  ChromeUtils.import("resource:///modules/translation/TranslationContentHandler.jsm");
  trHandler = new TranslationContentHandler(global, docShell);
}

function gKeywordURIFixup(fixupInfo) {
  fixupInfo.QueryInterface(Ci.nsIURIFixupInfo);
  if (!fixupInfo.consumer) {
    return;
  }

  // Ignore info from other docshells
  let parent = fixupInfo.consumer.QueryInterface(Ci.nsIDocShellTreeItem).sameTypeRootTreeItem;
  if (parent != docShell)
    return;

  let data = {};
  for (let f of Object.keys(fixupInfo)) {
    if (f == "consumer" || typeof fixupInfo[f] == "function")
      continue;

    if (fixupInfo[f] && fixupInfo[f] instanceof Ci.nsIURI) {
      data[f] = fixupInfo[f].spec;
    } else {
      data[f] = fixupInfo[f];
    }
  }

  sendAsyncMessage("Browser:URIFixup", data);
}
Services.obs.addObserver(gKeywordURIFixup, "keyword-uri-fixup");
addEventListener("unload", () => {
  Services.obs.removeObserver(gKeywordURIFixup, "keyword-uri-fixup");
}, false);

addMessageListener("Browser:AppTab", function(message) {
  if (docShell) {
    docShell.isAppTab = message.data.isAppTab;
  }
});

var WebBrowserChrome = {
  onBeforeLinkTraversal(originalTarget, linkURI, linkNode, isAppTab) {
    return BrowserUtils.onBeforeLinkTraversal(originalTarget, linkURI, linkNode, isAppTab);
  },

  // Check whether this URI should load in the current process
  shouldLoadURI(aDocShell, aURI, aReferrer, aHasPostData, aTriggeringPrincipal) {
    if (!E10SUtils.shouldLoadURI(aDocShell, aURI, aReferrer, aHasPostData)) {
      E10SUtils.redirectLoad(aDocShell, aURI, aReferrer, aTriggeringPrincipal, false);
      return false;
    }

    return true;
  },

  shouldLoadURIInThisProcess(aURI) {
    return E10SUtils.shouldLoadURIInThisProcess(aURI);
  },

  // Try to reload the currently active or currently loading page in a new process.
  reloadInFreshProcess(aDocShell, aURI, aReferrer, aTriggeringPrincipal, aLoadFlags) {
    E10SUtils.redirectLoad(aDocShell, aURI, aReferrer, aTriggeringPrincipal, true, aLoadFlags);
    return true;
  }
};

if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
  let tabchild = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsITabChild);
  tabchild.webBrowserChrome = WebBrowserChrome;
}


var DOMFullscreenHandler = {

  init() {
    addMessageListener("DOMFullscreen:Entered", this);
    addMessageListener("DOMFullscreen:CleanUp", this);
    addEventListener("MozDOMFullscreen:Request", this);
    addEventListener("MozDOMFullscreen:Entered", this);
    addEventListener("MozDOMFullscreen:NewOrigin", this);
    addEventListener("MozDOMFullscreen:Exit", this);
    addEventListener("MozDOMFullscreen:Exited", this);
  },

  get _windowUtils() {
    if (!content) {
      return null;
    }
    return content.QueryInterface(Ci.nsIInterfaceRequestor)
                  .getInterface(Ci.nsIDOMWindowUtils);
  },

  receiveMessage(aMessage) {
    let windowUtils = this._windowUtils;
    switch (aMessage.name) {
      case "DOMFullscreen:Entered": {
        this._lastTransactionId = windowUtils.lastTransactionId;
        if (!windowUtils.handleFullscreenRequests() &&
            !content.document.fullscreenElement) {
          // If we don't actually have any pending fullscreen request
          // to handle, neither we have been in fullscreen, tell the
          // parent to just exit.
          sendAsyncMessage("DOMFullscreen:Exit");
        }
        break;
      }
      case "DOMFullscreen:CleanUp": {
        // If we've exited fullscreen at this point, no need to record
        // transaction id or call exit fullscreen. This is especially
        // important for non-e10s, since in that case, it is possible
        // that no more paint would be triggered after this point.
        if (content.document.fullscreenElement && windowUtils) {
          this._lastTransactionId = windowUtils.lastTransactionId;
          windowUtils.exitFullscreen();
        }
        break;
      }
    }
  },

  handleEvent(aEvent) {
    switch (aEvent.type) {
      case "MozDOMFullscreen:Request": {
        sendAsyncMessage("DOMFullscreen:Request");
        break;
      }
      case "MozDOMFullscreen:NewOrigin": {
        sendAsyncMessage("DOMFullscreen:NewOrigin", {
          originNoSuffix: aEvent.target.nodePrincipal.originNoSuffix,
        });
        break;
      }
      case "MozDOMFullscreen:Exit": {
        sendAsyncMessage("DOMFullscreen:Exit");
        break;
      }
      case "MozDOMFullscreen:Entered":
      case "MozDOMFullscreen:Exited": {
        addEventListener("MozAfterPaint", this);
        if (!content || !content.document.fullscreenElement) {
          // If we receive any fullscreen change event, and find we are
          // actually not in fullscreen, also ask the parent to exit to
          // ensure that the parent always exits fullscreen when we do.
          sendAsyncMessage("DOMFullscreen:Exit");
        }
        break;
      }
      case "MozAfterPaint": {
        // Only send Painted signal after we actually finish painting
        // the transition for the fullscreen change.
        // Note that this._lastTransactionId is not set when in non-e10s
        // mode, so we need to check that explicitly.
        if (!this._lastTransactionId ||
            aEvent.transactionId > this._lastTransactionId) {
          removeEventListener("MozAfterPaint", this);
          sendAsyncMessage("DOMFullscreen:Painted");
        }
        break;
      }
    }
  }
};
DOMFullscreenHandler.init();

var RefreshBlocker = {
  PREF: "accessibility.blockautorefresh",

  // Bug 1247100 - When a refresh is caused by an HTTP header,
  // onRefreshAttempted will be fired before onLocationChange.
  // When a refresh is caused by a <meta> tag in the document,
  // onRefreshAttempted will be fired after onLocationChange.
  //
  // We only ever want to send a message to the parent after
  // onLocationChange has fired, since the parent uses the
  // onLocationChange update to clear transient notifications.
  // Sending the message before onLocationChange will result in
  // us creating the notification, and then clearing it very
  // soon after.
  //
  // To account for both cases (onRefreshAttempted before
  // onLocationChange, and onRefreshAttempted after onLocationChange),
  // we'll hold a mapping of DOM Windows that we see get
  // sent through both onLocationChange and onRefreshAttempted.
  // When either run, they'll check the WeakMap for the existence
  // of the DOM Window. If it doesn't exist, it'll add it. If
  // it finds it, it'll know that it's safe to send the message
  // to the parent, since we know that both have fired.
  //
  // The DOM Window is removed from blockedWindows when we notice
  // the nsIWebProgress change state to STATE_STOP for the
  // STATE_IS_WINDOW case.
  //
  // DOM Windows are mapped to a JS object that contains the data
  // to be sent to the parent to show the notification. Since that
  // data is only known when onRefreshAttempted is fired, it's only
  // ever stashed in the map if onRefreshAttempted fires first -
  // otherwise, null is set as the value of the mapping.
  blockedWindows: new WeakMap(),

  init() {
    if (Services.prefs.getBoolPref(this.PREF)) {
      this.enable();
    }

    Services.prefs.addObserver(this.PREF, this);
  },

  uninit() {
    if (Services.prefs.getBoolPref(this.PREF)) {
      this.disable();
    }

    Services.prefs.removeObserver(this.PREF, this);
  },

  observe(subject, topic, data) {
    if (topic == "nsPref:changed" && data == this.PREF) {
      if (Services.prefs.getBoolPref(this.PREF)) {
        this.enable();
      } else {
        this.disable();
      }
    }
  },

  enable() {
    this._filter = Cc["@mozilla.org/appshell/component/browser-status-filter;1"]
                     .createInstance(Ci.nsIWebProgress);
    this._filter.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_ALL);
    this._filter.target = tabEventTarget;

    let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                              .getInterface(Ci.nsIWebProgress);
    webProgress.addProgressListener(this._filter, Ci.nsIWebProgress.NOTIFY_ALL);

    addMessageListener("RefreshBlocker:Refresh", this);
  },

  disable() {
    let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                              .getInterface(Ci.nsIWebProgress);
    webProgress.removeProgressListener(this._filter);

    this._filter.removeProgressListener(this);
    this._filter = null;

    removeMessageListener("RefreshBlocker:Refresh", this);
  },

  send(data) {
    sendAsyncMessage("RefreshBlocker:Blocked", data);
  },

  /**
   * Notices when the nsIWebProgress transitions to STATE_STOP for
   * the STATE_IS_WINDOW case, which will clear any mappings from
   * blockedWindows.
   */
  onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
    if (aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW &&
        aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
      this.blockedWindows.delete(aWebProgress.DOMWindow);
    }
  },

  /**
   * Notices when the location has changed. If, when running,
   * onRefreshAttempted has already fired for this DOM Window, will
   * send the appropriate refresh blocked data to the parent.
   */
  onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {
    let win = aWebProgress.DOMWindow;
    if (this.blockedWindows.has(win)) {
      let data = this.blockedWindows.get(win);
      if (data) {
        // We saw onRefreshAttempted before onLocationChange, so
        // send the message to the parent to show the notification.
        this.send(data);
      }
    } else {
      this.blockedWindows.set(win, null);
    }
  },

  /**
   * Notices when a refresh / reload was attempted. If, when running,
   * onLocationChange has not yet run, will stash the appropriate data
   * into the blockedWindows map to be sent when onLocationChange fires.
   */
  onRefreshAttempted(aWebProgress, aURI, aDelay, aSameURI) {
    let win = aWebProgress.DOMWindow;
    let outerWindowID = win.QueryInterface(Ci.nsIInterfaceRequestor)
                           .getInterface(Ci.nsIDOMWindowUtils)
                           .outerWindowID;

    let data = {
      URI: aURI.spec,
      delay: aDelay,
      sameURI: aSameURI,
      outerWindowID,
    };

    if (this.blockedWindows.has(win)) {
      // onLocationChange must have fired before, so we can tell the
      // parent to show the notification.
      this.send(data);
    } else {
      // onLocationChange hasn't fired yet, so stash the data in the
      // map so that onLocationChange can send it when it fires.
      this.blockedWindows.set(win, data);
    }

    return false;
  },

  receiveMessage(message) {
    let data = message.data;

    if (message.name == "RefreshBlocker:Refresh") {
      let win = Services.wm.getOuterWindowWithId(data.outerWindowID);
      let refreshURI = win.QueryInterface(Ci.nsIInterfaceRequestor)
                          .getInterface(Ci.nsIDocShell)
                          .QueryInterface(Ci.nsIRefreshURI);

      let URI = Services.io.newURI(data.URI);

      refreshURI.forceRefreshURI(URI, null, data.delay, true);
    }
  },

  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener2,
                                         Ci.nsIWebProgressListener,
                                         Ci.nsISupportsWeakReference,
                                         Ci.nsISupports]),
};

RefreshBlocker.init();

var UserContextIdNotifier = {
  init() {
    addEventListener("DOMWindowCreated", this);
  },

  uninit() {
    removeEventListener("DOMWindowCreated", this);
  },

  handleEvent(aEvent) {
    // When the window is created, we want to inform the tabbrowser about
    // the userContextId in use in order to update the UI correctly.
    // Just because we cannot change the userContextId from an active docShell,
    // we don't need to check DOMContentLoaded again.
    this.uninit();

    // We use the docShell because content.document can have been loaded before
    // setting the originAttributes.
    let loadContext = docShell.QueryInterface(Ci.nsILoadContext);
    let userContextId = loadContext.originAttributes.userContextId;

    sendAsyncMessage("Browser:WindowCreated", { userContextId });
  }
};

UserContextIdNotifier.init();

Services.obs.notifyObservers(this, "tab-content-frameloader-created");

addEventListener("unload", () => {
  RefreshBlocker.uninit();
});

addMessageListener("AllowScriptsToClose", () => {
  content.QueryInterface(Ci.nsIInterfaceRequestor)
         .getInterface(Ci.nsIDOMWindowUtils)
         .allowScriptsToClose();
});

addEventListener("MozAfterPaint", function onFirstPaint() {
  removeEventListener("MozAfterPaint", onFirstPaint);
  sendAsyncMessage("Browser:FirstPaint");
});

// Remove this once bug 1397365 is fixed.
addEventListener("MozAfterPaint", function onFirstNonBlankPaint() {
  if (content.document.documentURI == "about:blank" && !content.opener)
    return;
  removeEventListener("MozAfterPaint", onFirstNonBlankPaint);
  sendAsyncMessage("Browser:FirstNonBlankPaint");
});
PK
!<�It�j�j,chrome/browser/content/browser/tabbrowser.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* eslint-env mozilla/browser-window */

window._gBrowser = {
  init() {
    ChromeUtils.defineModuleGetter(this, "AsyncTabSwitcher",
      "resource:///modules/AsyncTabSwitcher.jsm");

    XPCOMUtils.defineLazyServiceGetters(this, {
      _unifiedComplete: ["@mozilla.org/autocomplete/search;1?name=unifiedcomplete", "mozIPlacesAutoComplete"],
      serializationHelper: ["@mozilla.org/network/serialization-helper;1", "nsISerializationHelper"],
      mURIFixup: ["@mozilla.org/docshell/urifixup;1", "nsIURIFixup"],
    });

    Services.obs.addObserver(this, "contextual-identity-updated");

    Services.els.addSystemEventListener(document, "keydown", this, false);
    if (AppConstants.platform == "macosx") {
      Services.els.addSystemEventListener(document, "keypress", this, false);
    }
    window.addEventListener("sizemodechange", this);
    window.addEventListener("occlusionstatechange", this);

    this._setupInitialBrowserAndTab();

    // Hook up the event listeners to the first browser
    var tabListener = new TabProgressListener(this.mCurrentTab, this.mCurrentBrowser, true, false);
    const nsIWebProgress = Ci.nsIWebProgress;
    const filter = Cc["@mozilla.org/appshell/component/browser-status-filter;1"]
      .createInstance(nsIWebProgress);
    filter.addProgressListener(tabListener, nsIWebProgress.NOTIFY_ALL);
    this._tabListeners.set(this.mCurrentTab, tabListener);
    this._tabFilters.set(this.mCurrentTab, filter);
    this.webProgress.addProgressListener(filter, nsIWebProgress.NOTIFY_ALL);

    if (Services.prefs.getBoolPref("browser.display.use_system_colors")) {
      this.mPanelContainer.style.backgroundColor = "-moz-default-background-color";
    } else if (Services.prefs.getIntPref("browser.display.document_color_use") == 2) {
      this.mPanelContainer.style.backgroundColor =
        Services.prefs.getCharPref("browser.display.background_color");
    }

    let messageManager = window.getGroupMessageManager("browsers");

    let remote = window.QueryInterface(Ci.nsIInterfaceRequestor)
                       .getInterface(Ci.nsIWebNavigation)
                       .QueryInterface(Ci.nsILoadContext)
                       .useRemoteTabs;
    if (remote) {
      messageManager.addMessageListener("DOMTitleChanged", this);
      messageManager.addMessageListener("DOMWindowClose", this);
      window.messageManager.addMessageListener("contextmenu", this);
      messageManager.addMessageListener("Browser:Init", this);

      // If this window has remote tabs, switch to our tabpanels fork
      // which does asynchronous tab switching.
      this.mPanelContainer.classList.add("tabbrowser-tabpanels");
    } else {
      this._outerWindowIDBrowserMap.set(this.mCurrentBrowser.outerWindowID,
        this.mCurrentBrowser);
    }
    messageManager.addMessageListener("DOMWindowFocus", this);
    messageManager.addMessageListener("RefreshBlocker:Blocked", this);
    messageManager.addMessageListener("Browser:WindowCreated", this);

    // To correctly handle keypresses for potential FindAsYouType, while
    // the tab's find bar is not yet initialized.
    this._findAsYouType = Services.prefs.getBoolPref("accessibility.typeaheadfind");
    Services.prefs.addObserver("accessibility.typeaheadfind", this);
    messageManager.addMessageListener("Findbar:Keypress", this);

    XPCOMUtils.defineLazyPreferenceGetter(this, "animationsEnabled",
      "toolkit.cosmeticAnimations.enabled", true);
    XPCOMUtils.defineLazyPreferenceGetter(this, "schedulePressureDefaultCount",
      "browser.schedulePressure.defaultCount", 3);
    XPCOMUtils.defineLazyPreferenceGetter(this, "tabWarmingEnabled",
      "browser.tabs.remote.warmup.enabled", false);
    XPCOMUtils.defineLazyPreferenceGetter(this, "tabWarmingMax",
      "browser.tabs.remote.warmup.maxTabs", 3);
    XPCOMUtils.defineLazyPreferenceGetter(this, "tabWarmingUnloadDelay" /* ms */,
      "browser.tabs.remote.warmup.unloadDelayMs", 2000);

    this._setupEventListeners();
  },

  ownerGlobal: window,

  ownerDocument: document,

  closingTabsEnum: { ALL: 0, OTHER: 1, TO_END: 2 },

  _visibleTabs: null,

  _lastRelatedTabMap: new WeakMap(),

  mProgressListeners: [],

  mTabsProgressListeners: [],

  _tabListeners: new Map(),

  _tabFilters: new Map(),

  mIsBusy: false,

  _outerWindowIDBrowserMap: new Map(),

  arrowKeysShouldWrap: AppConstants == "macosx",

  _autoScrollPopup: null,

  _previewMode: false,

  _lastFindValue: "",

  _contentWaitingCount: 0,

  tabAnimationsInProgress: 0,

  _XUL_NS: "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",

  /**
   * Binding from browser to tab
   */
  _tabForBrowser: new WeakMap(),

  /**
   * Holds a unique ID for the tab change that's currently being timed.
   * Used to make sure that multiple, rapid tab switches do not try to
   * create overlapping timers.
   */
  _tabSwitchID: null,

  _preloadedBrowser: null,

  /**
   * `_createLazyBrowser` will define properties on the unbound lazy browser
   * which correspond to properties defined in XBL which will be bound to
   * the browser when it is inserted into the document.  If any of these
   * properties are accessed by consumers, `_insertBrowser` is called and
   * the browser is inserted to ensure that things don't break.  This list
   * provides the names of properties that may be called while the browser
   * is in its unbound (lazy) state.
   */
  _browserBindingProperties: [
    "canGoBack", "canGoForward", "goBack", "goForward", "permitUnload",
    "reload", "reloadWithFlags", "stop", "loadURI", "loadURIWithFlags",
    "goHome", "homePage", "gotoIndex", "currentURI", "documentURI",
    "preferences", "imageDocument", "isRemoteBrowser", "messageManager",
    "getTabBrowser", "finder", "fastFind", "sessionHistory", "contentTitle",
    "characterSet", "fullZoom", "textZoom", "webProgress",
    "addProgressListener", "removeProgressListener", "audioPlaybackStarted",
    "audioPlaybackStopped", "pauseMedia", "stopMedia",
    "resumeMedia", "mute", "unmute", "blockedPopups", "lastURI",
    "purgeSessionHistory", "stopScroll", "startScroll",
    "userTypedValue", "userTypedClear", "mediaBlocked",
    "didStartLoadSinceLastUserTyping"
  ],

  _removingTabs: [],

  /**
   * Tab close requests are ignored if the window is closing anyway,
   * e.g. when holding Ctrl+W.
   */
  _windowIsClosing: false,

  /**
   * This defines a proxy which allows us to access browsers by
   * index without actually creating a full array of browsers.
   */
  browsers: new Proxy([], {
    has: (target, name) => {
      if (typeof name == "string" && Number.isInteger(parseInt(name))) {
        return (name in gBrowser.tabs);
      }
      return false;
    },
    get: (target, name) => {
      if (name == "length") {
        return gBrowser.tabs.length;
      }
      if (typeof name == "string" && Number.isInteger(parseInt(name))) {
        if (!(name in gBrowser.tabs)) {
          return undefined;
        }
        return gBrowser.tabs[name].linkedBrowser;
      }
      return target[name];
    }
  }),

  /**
   * List of browsers whose docshells must be active in order for print preview
   * to work.
   */
  _printPreviewBrowsers: new Set(),

  _switcher: null,

  _soundPlayingAttrRemovalTimer: 0,

  _hoverTabTimer: null,

  get tabContainer() {
    delete this.tabContainer;
    return this.tabContainer = document.getElementById("tabbrowser-tabs");
  },

  get tabs() {
    delete this.tabs;
    return this.tabs = this.tabContainer.childNodes;
  },

  get tabbox() {
    delete this.tabbox;
    return this.tabbox = document.getElementById("tabbrowser-tabbox");
  },

  get mPanelContainer() {
    delete this.mPanelContainer;
    return this.mPanelContainer = document.getElementById("tabbrowser-tabpanels");
  },

  get addEventListener() {
    delete this.addEventListener;
    return this.addEventListener = this.mPanelContainer.addEventListener.bind(this.mPanelContainer);
  },

  get removeEventListener() {
    delete this.removeEventListener;
    return this.removeEventListener = this.mPanelContainer.removeEventListener.bind(this.mPanelContainer);
  },

  get dispatchEvent() {
    delete this.dispatchEvent;
    return this.dispatchEvent = this.mPanelContainer.dispatchEvent.bind(this.mPanelContainer);
  },

  get visibleTabs() {
    if (!this._visibleTabs)
      this._visibleTabs = Array.filter(this.tabs,
        tab => !tab.hidden && !tab.closing);
    return this._visibleTabs;
  },

  get _numPinnedTabs() {
    for (var i = 0; i < this.tabs.length; i++) {
      if (!this.tabs[i].pinned)
        break;
    }
    return i;
  },

  get popupAnchor() {
    if (this.mCurrentTab._popupAnchor) {
      return this.mCurrentTab._popupAnchor;
    }
    let stack = this.mCurrentBrowser.parentNode;
    // Create an anchor for the popup
    let popupAnchor = document.createElementNS(this._XUL_NS, "hbox");
    popupAnchor.className = "popup-anchor";
    popupAnchor.hidden = true;
    stack.appendChild(popupAnchor);
    return this.mCurrentTab._popupAnchor = popupAnchor;
  },

  set selectedTab(val) {
    if (gNavToolbox.collapsed && !this._allowTabChange) {
      return this.tabbox.selectedTab;
    }
    // Update the tab
    this.tabbox.selectedTab = val;
    return val;
  },

  get selectedTab() {
    return this.mCurrentTab;
  },

  get selectedBrowser() {
    return this.mCurrentBrowser;
  },

  get initialBrowser() {
    delete this.initialBrowser;
    return this.initialBrowser = document.getElementById("tabbrowser-initialBrowser");
  },

  _setupInitialBrowserAndTab() {
    let browser = this.initialBrowser;
    this.mCurrentBrowser = browser;

    browser.permanentKey = {};
    browser.droppedLinkHandler = handleDroppedLink;

    this._autoScrollPopup = browser._createAutoScrollPopup();
    this._autoScrollPopup.id = "autoscroller";
    document.getElementById("mainPopupSet").appendChild(this._autoScrollPopup);
    browser.setAttribute("autoscrollpopup", this._autoScrollPopup.id);

    let tab = this.tabs[0];
    this.mCurrentTab = tab;

    let uniqueId = this._generateUniquePanelID();
    this.mPanelContainer.childNodes[0].id = uniqueId;
    tab.linkedPanel = uniqueId;
    tab.permanentKey = browser.permanentKey;
    tab._tPos = 0;
    tab._fullyOpen = true;
    tab.linkedBrowser = browser;
    this._tabForBrowser.set(browser, tab);
  },

  /**
   * BEGIN FORWARDED BROWSER PROPERTIES.  IF YOU ADD A PROPERTY TO THE BROWSER ELEMENT
   * MAKE SURE TO ADD IT HERE AS WELL.
   */
  get canGoBack() {
    return this.mCurrentBrowser.canGoBack;
  },

  get canGoForward() {
    return this.mCurrentBrowser.canGoForward;
  },

  goBack() {
    return this.mCurrentBrowser.goBack();
  },

  goForward() {
    return this.mCurrentBrowser.goForward();
  },

  reload() {
    return this.mCurrentBrowser.reload();
  },

  reloadWithFlags(aFlags) {
    return this.mCurrentBrowser.reloadWithFlags(aFlags);
  },

  stop() {
    return this.mCurrentBrowser.stop();
  },

  /**
   * throws exception for unknown schemes
   */
  loadURI(aURI, aReferrerURI, aCharset) {
    return this.mCurrentBrowser.loadURI(aURI, aReferrerURI, aCharset);
  },

  /**
   * throws exception for unknown schemes
   */
  loadURIWithFlags(aURI, aFlags, aReferrerURI, aCharset, aPostData) {
    // Note - the callee understands both:
    // (a) loadURIWithFlags(aURI, aFlags, ...)
    // (b) loadURIWithFlags(aURI, { flags: aFlags, ... })
    // Forwarding it as (a) here actually supports both (a) and (b),
    // so you can call us either way too.
    return this.mCurrentBrowser.loadURIWithFlags(aURI, aFlags, aReferrerURI, aCharset, aPostData);
  },

  goHome() {
    return this.mCurrentBrowser.goHome();
  },

  gotoIndex(aIndex) {
    return this.mCurrentBrowser.gotoIndex(aIndex);
  },

  set homePage(val) {
    this.mCurrentBrowser.homePage = val;
    return val;
  },

  get homePage() {
    return this.mCurrentBrowser.homePage;
  },

  get currentURI() {
    return this.mCurrentBrowser.currentURI;
  },

  get finder() {
    return this.mCurrentBrowser.finder;
  },

  get docShell() {
    return this.mCurrentBrowser.docShell;
  },

  get webNavigation() {
    return this.mCurrentBrowser.webNavigation;
  },

  get webBrowserFind() {
    return this.mCurrentBrowser.webBrowserFind;
  },

  get webProgress() {
    return this.mCurrentBrowser.webProgress;
  },

  get contentWindow() {
    return this.mCurrentBrowser.contentWindow;
  },

  get contentWindowAsCPOW() {
    return this.mCurrentBrowser.contentWindowAsCPOW;
  },

  get sessionHistory() {
    return this.mCurrentBrowser.sessionHistory;
  },

  get markupDocumentViewer() {
    return this.mCurrentBrowser.markupDocumentViewer;
  },

  get contentDocument() {
    return this.mCurrentBrowser.contentDocument;
  },

  get contentDocumentAsCPOW() {
    return this.mCurrentBrowser.contentDocumentAsCPOW;
  },

  get contentTitle() {
    return this.mCurrentBrowser.contentTitle;
  },

  get contentPrincipal() {
    return this.mCurrentBrowser.contentPrincipal;
  },

  get securityUI() {
    return this.mCurrentBrowser.securityUI;
  },

  set fullZoom(val) {
    this.mCurrentBrowser.fullZoom = val;
  },

  get fullZoom() {
    return this.mCurrentBrowser.fullZoom;
  },

  set textZoom(val) {
    this.mCurrentBrowser.textZoom = val;
  },

  get textZoom() {
    return this.mCurrentBrowser.textZoom;
  },

  get isSyntheticDocument() {
    return this.mCurrentBrowser.isSyntheticDocument;
  },

  set userTypedValue(val) {
    return this.mCurrentBrowser.userTypedValue = val;
  },

  get userTypedValue() {
    return this.mCurrentBrowser.userTypedValue;
  },

  isFindBarInitialized(aTab) {
    return (aTab || this.selectedTab)._findBar != undefined;
  },

  getFindBar(aTab) {
    if (!aTab)
      aTab = this.selectedTab;

    if (aTab._findBar)
      return aTab._findBar;

    let findBar = document.createElementNS(this._XUL_NS, "findbar");
    let browser = this.getBrowserForTab(aTab);
    let browserContainer = this.getBrowserContainer(browser);
    browserContainer.appendChild(findBar);

    // Force a style flush to ensure that our binding is attached.
    findBar.clientTop;

    findBar.browser = browser;
    findBar._findField.value = this._lastFindValue;

    aTab._findBar = findBar;

    let event = document.createEvent("Events");
    event.initEvent("TabFindInitialized", true, false);
    aTab.dispatchEvent(event);

    return findBar;
  },

  getStatusPanel() {
    if (!this._statusPanel) {
      this._statusPanel = document.createElementNS(this._XUL_NS, "statuspanel");
      this._statusPanel.setAttribute("inactive", "true");
      this._statusPanel.setAttribute("layer", "true");
      this._appendStatusPanel();
    }
    return this._statusPanel;
  },

  _appendStatusPanel() {
    if (this._statusPanel) {
      let browser = this.selectedBrowser;
      let browserContainer = this.getBrowserContainer(browser);
      browserContainer.insertBefore(this._statusPanel, browser.parentNode.nextSibling);
    }
  },

  pinTab(aTab) {
    if (aTab.pinned)
      return;

    if (aTab.hidden)
      this.showTab(aTab);

    this.moveTabTo(aTab, this._numPinnedTabs);
    aTab.setAttribute("pinned", "true");
    this.tabContainer._unlockTabSizing();
    this.tabContainer._positionPinnedTabs();
    this.tabContainer._updateCloseButtons();

    this.getBrowserForTab(aTab).messageManager.sendAsyncMessage("Browser:AppTab", { isAppTab: true });

    let event = document.createEvent("Events");
    event.initEvent("TabPinned", true, false);
    aTab.dispatchEvent(event);
  },

  unpinTab(aTab) {
    if (!aTab.pinned)
      return;

    this.moveTabTo(aTab, this._numPinnedTabs - 1);
    aTab.removeAttribute("pinned");
    aTab.style.marginInlineStart = "";
    this.tabContainer._unlockTabSizing();
    this.tabContainer._positionPinnedTabs();
    this.tabContainer._updateCloseButtons();

    this.getBrowserForTab(aTab).messageManager.sendAsyncMessage("Browser:AppTab", { isAppTab: false });

    let event = document.createEvent("Events");
    event.initEvent("TabUnpinned", true, false);
    aTab.dispatchEvent(event);
  },

  previewTab(aTab, aCallback) {
    let currentTab = this.selectedTab;
    try {
      // Suppress focus, ownership and selected tab changes
      this._previewMode = true;
      this.selectedTab = aTab;
      aCallback();
    } finally {
      this.selectedTab = currentTab;
      this._previewMode = false;
    }
  },

  syncThrobberAnimations(aTab) {
    aTab.ownerGlobal.promiseDocumentFlushed(() => {
      if (!aTab.parentNode) {
        return;
      }

      const animations =
        Array.from(aTab.parentNode.getElementsByTagName("tab"))
        .map(tab => {
          const throbber =
            document.getAnonymousElementByAttribute(tab, "anonid", "tab-throbber");
          return throbber ? throbber.getAnimations({ subtree: true }) : [];
        })
        .reduce((a, b) => a.concat(b))
        .filter(anim =>
          anim instanceof CSSAnimation &&
          (anim.animationName === "tab-throbber-animation" ||
            anim.animationName === "tab-throbber-animation-rtl") &&
          (anim.playState === "running" || anim.playState === "pending"));

      // Synchronize with the oldest running animation, if any.
      const firstStartTime = Math.min(
        ...animations.map(anim => anim.startTime === null ? Infinity : anim.startTime)
      );
      if (firstStartTime === Infinity) {
        return;
      }
      requestAnimationFrame(() => {
        for (let animation of animations) {
          // If |animation| has been cancelled since this rAF callback
          // was scheduled we don't want to set its startTime since
          // that would restart it. We check for a cancelled animation
          // by looking for a null currentTime rather than checking
          // the playState, since reading the playState of
          // a CSSAnimation object will flush style.
          if (animation.currentTime !== null) {
            animation.startTime = firstStartTime;
          }
        }
      });
    });
  },

  getBrowserAtIndex(aIndex) {
    return this.browsers[aIndex];
  },

  getBrowserIndexForDocument(aDocument) {
    var tab = this._getTabForContentWindow(aDocument.defaultView);
    return tab ? tab._tPos : -1;
  },

  getBrowserForDocument(aDocument) {
    var tab = this._getTabForContentWindow(aDocument.defaultView);
    return tab ? tab.linkedBrowser : null;
  },

  getBrowserForContentWindow(aWindow) {
    var tab = this._getTabForContentWindow(aWindow);
    return tab ? tab.linkedBrowser : null;
  },

  getBrowserForOuterWindowID(aID) {
    return this._outerWindowIDBrowserMap.get(aID);
  },

  _getTabForContentWindow(aWindow) {
    // When not using remote browsers, we can take a fast path by getting
    // directly from the content window to the browser without looping
    // over all browsers.
    if (!gMultiProcessBrowser) {
      let browser = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                           .getInterface(Ci.nsIWebNavigation)
                           .QueryInterface(Ci.nsIDocShell)
                           .chromeEventHandler;
      return this.getTabForBrowser(browser);
    }

    for (let i = 0; i < this.browsers.length; i++) {
      // NB: We use contentWindowAsCPOW so that this code works both
      // for remote browsers as well. aWindow may be a CPOW.
      if (this.browsers[i].contentWindowAsCPOW == aWindow)
        return this.tabs[i];
    }
    return null;
  },

  getTabForBrowser(aBrowser) {
    return this._tabForBrowser.get(aBrowser);
  },

  getNotificationBox(aBrowser) {
    return this.getSidebarContainer(aBrowser).parentNode;
  },

  getSidebarContainer(aBrowser) {
    return this.getBrowserContainer(aBrowser).parentNode;
  },

  getBrowserContainer(aBrowser) {
    return (aBrowser || this.mCurrentBrowser).parentNode.parentNode;
  },

  getTabModalPromptBox(aBrowser) {
    let browser = (aBrowser || this.mCurrentBrowser);
    if (!browser.tabModalPromptBox) {
      browser.tabModalPromptBox = new TabModalPromptBox(browser);
    }
    return browser.tabModalPromptBox;
  },

  getTabFromAudioEvent(aEvent) {
    if (!Services.prefs.getBoolPref("browser.tabs.showAudioPlayingIcon") ||
      !aEvent.isTrusted) {
      return null;
    }

    var browser = aEvent.originalTarget;
    var tab = this.getTabForBrowser(browser);
    return tab;
  },

  _callProgressListeners(aBrowser, aMethod, aArguments, aCallGlobalListeners, aCallTabsListeners) {
    var rv = true;

    function callListeners(listeners, args) {
      for (let p of listeners) {
        if (aMethod in p) {
          try {
            if (!p[aMethod].apply(p, args))
              rv = false;
          } catch (e) {
            // don't inhibit other listeners
            Cu.reportError(e);
          }
        }
      }
    }

    if (!aBrowser)
      aBrowser = this.mCurrentBrowser;

    // eslint-disable-next-line mozilla/no-compare-against-boolean-literals
    if (aCallGlobalListeners != false &&
        aBrowser == this.mCurrentBrowser) {
      callListeners(this.mProgressListeners, aArguments);
    }

    // eslint-disable-next-line mozilla/no-compare-against-boolean-literals
    if (aCallTabsListeners != false) {
      aArguments.unshift(aBrowser);

      callListeners(this.mTabsProgressListeners, aArguments);
    }

    return rv;
  },

  /**
   * Determine if a URI is an about: page pointing to a local resource.
   */
  _isLocalAboutURI(aURI, aResolvedURI) {
    if (!aURI.schemeIs("about")) {
      return false;
    }

    // Specially handle about:blank as local
    if (aURI.pathQueryRef === "blank") {
      return true;
    }

    try {
      // Use the passed in resolvedURI if we have one
      const resolvedURI = aResolvedURI || Services.io.newChannelFromURI2(
        aURI,
        null, // loadingNode
        Services.scriptSecurityManager.getSystemPrincipal(), // loadingPrincipal
        null, // triggeringPrincipal
        Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL, // securityFlags
        Ci.nsIContentPolicy.TYPE_OTHER // contentPolicyType
      ).URI;
      return resolvedURI.schemeIs("jar") || resolvedURI.schemeIs("file");
    } catch (ex) {
      // aURI might be invalid.
      return false;
    }
  },

  storeIcon(aBrowser, aURI, aLoadingPrincipal, aRequestContextID) {
    try {
      if (!(aURI instanceof Ci.nsIURI)) {
        aURI = makeURI(aURI);
      }
      PlacesUIUtils.loadFavicon(aBrowser, aLoadingPrincipal, aURI, aRequestContextID);
    } catch (ex) {
      Cu.reportError(ex);
    }
  },

  setIcon(aTab, aURI, aLoadingPrincipal, aRequestContextID) {
    let browser = this.getBrowserForTab(aTab);
    browser.mIconURL = aURI instanceof Ci.nsIURI ? aURI.spec : aURI;
    let loadingPrincipal = aLoadingPrincipal ||
      Services.scriptSecurityManager.getSystemPrincipal();
    let requestContextID = aRequestContextID || 0;
    let sizedIconUrl = browser.mIconURL || "";
    if (sizedIconUrl != aTab.getAttribute("image")) {
      if (sizedIconUrl) {
        if (!browser.mIconLoadingPrincipal ||
          !browser.mIconLoadingPrincipal.equals(loadingPrincipal)) {
          aTab.setAttribute("iconloadingprincipal",
            this.serializationHelper.serializeToString(loadingPrincipal));
          aTab.setAttribute("requestcontextid", requestContextID);
          browser.mIconLoadingPrincipal = loadingPrincipal;
        }
        aTab.setAttribute("image", sizedIconUrl);
      } else {
        aTab.removeAttribute("iconloadingprincipal");
        delete browser.mIconLoadingPrincipal;
        aTab.removeAttribute("image");
      }
      this._tabAttrModified(aTab, ["image"]);
    }

    this._callProgressListeners(browser, "onLinkIconAvailable", [browser.mIconURL]);
  },

  getIcon(aTab) {
    let browser = aTab ? this.getBrowserForTab(aTab) : this.selectedBrowser;
    return browser.mIconURL;
  },

  setPageInfo(aURL, aDescription, aPreviewImage) {
    if (aURL) {
      let pageInfo = { url: aURL, description: aDescription, previewImageURL: aPreviewImage };
      PlacesUtils.history.update(pageInfo).catch(Cu.reportError);
    }
  },

  shouldLoadFavIcon(aURI) {
    return (aURI &&
            Services.prefs.getBoolPref("browser.chrome.site_icons") &&
            Services.prefs.getBoolPref("browser.chrome.favicons") &&
            ("schemeIs" in aURI) && (aURI.schemeIs("http") || aURI.schemeIs("https")));
  },

  useDefaultIcon(aTab) {
    let browser = this.getBrowserForTab(aTab);
    let documentURI = browser.documentURI;
    let requestContextID = browser.contentRequestContextID;
    let loadingPrincipal = browser.contentPrincipal;
    let icon = null;

    if (browser.imageDocument) {
      if (Services.prefs.getBoolPref("browser.chrome.site_icons")) {
        let sz = Services.prefs.getIntPref("browser.chrome.image_icons.max_size");
        if (browser.imageDocument.width <= sz &&
            browser.imageDocument.height <= sz) {
          // Don't try to store the icon in Places, regardless it would
          // be skipped (see Bug 403651).
          icon = browser.currentURI;
        }
      }
    }

    // Use documentURIObject in the check for shouldLoadFavIcon so that we
    // do the right thing with about:-style error pages.  Bug 453442
    if (!icon && this.shouldLoadFavIcon(documentURI)) {
      let url = documentURI.prePath + "/favicon.ico";
      if (!this.isFailedIcon(url)) {
        icon = url;
        this.storeIcon(browser, icon, loadingPrincipal, requestContextID);
      }
    }

    this.setIcon(aTab, icon, loadingPrincipal, requestContextID);
  },

  isFailedIcon(aURI) {
    if (!(aURI instanceof Ci.nsIURI))
      aURI = makeURI(aURI);
    return PlacesUtils.favicons.isFailedFavicon(aURI);
  },

  getWindowTitleForBrowser(aBrowser) {
    var newTitle = "";
    var docElement = document.documentElement;
    var sep = docElement.getAttribute("titlemenuseparator");
    let tab = this.getTabForBrowser(aBrowser);
    let docTitle;

    if (tab._labelIsContentTitle) {
      // Strip out any null bytes in the content title, since the
      // underlying widget implementations of nsWindow::SetTitle pass
      // null-terminated strings to system APIs.
      docTitle = tab.getAttribute("label").replace(/\0/g, "");
    }

    if (!docTitle)
      docTitle = docElement.getAttribute("titledefault");

    var modifier = docElement.getAttribute("titlemodifier");
    if (docTitle) {
      newTitle += docElement.getAttribute("titlepreface");
      newTitle += docTitle;
      if (modifier)
        newTitle += sep;
    }
    newTitle += modifier;

    // If location bar is hidden and the URL type supports a host,
    // add the scheme and host to the title to prevent spoofing.
    // XXX https://bugzilla.mozilla.org/show_bug.cgi?id=22183#c239
    try {
      if (docElement.getAttribute("chromehidden").includes("location")) {
        var uri = this.mURIFixup.createExposableURI(
          aBrowser.currentURI);
        if (uri.scheme == "about")
          newTitle = uri.spec + sep + newTitle;
        else
          newTitle = uri.prePath + sep + newTitle;
      }
    } catch (e) {}

    return newTitle;
  },

  updateTitlebar() {
    document.title = this.getWindowTitleForBrowser(this.mCurrentBrowser);
  },

  updateCurrentBrowser(aForceUpdate) {
    var newBrowser = this.getBrowserAtIndex(this.tabContainer.selectedIndex);
    if (this.mCurrentBrowser == newBrowser && !aForceUpdate)
      return;

    if (!aForceUpdate) {
      document.commandDispatcher.lock();

      TelemetryStopwatch.start("FX_TAB_SWITCH_UPDATE_MS");
      if (!gMultiProcessBrowser) {
        // old way of measuring tab paint which is not valid with e10s.
        // Waiting until the next MozAfterPaint ensures that we capture
        // the time it takes to paint, upload the textures to the compositor,
        // and then composite.
        if (this._tabSwitchID) {
          TelemetryStopwatch.cancel("FX_TAB_SWITCH_TOTAL_MS");
        }

        let tabSwitchID = Symbol();

        TelemetryStopwatch.start("FX_TAB_SWITCH_TOTAL_MS");
        this._tabSwitchID = tabSwitchID;

        let onMozAfterPaint = () => {
          if (this._tabSwitchID === tabSwitchID) {
            TelemetryStopwatch.finish("FX_TAB_SWITCH_TOTAL_MS");
            this._tabSwitchID = null;
          }
          window.removeEventListener("MozAfterPaint", onMozAfterPaint);
        };
        window.addEventListener("MozAfterPaint", onMozAfterPaint);
      }
    }

    var oldTab = this.mCurrentTab;

    // Preview mode should not reset the owner
    if (!this._previewMode && !oldTab.selected)
      oldTab.owner = null;

    let lastRelatedTab = this._lastRelatedTabMap.get(oldTab);
    if (lastRelatedTab) {
      if (!lastRelatedTab.selected)
        lastRelatedTab.owner = null;
    }
    this._lastRelatedTabMap = new WeakMap();

    var oldBrowser = this.mCurrentBrowser;

    if (!gMultiProcessBrowser) {
      oldBrowser.removeAttribute("primary");
      oldBrowser.docShellIsActive = false;
      newBrowser.setAttribute("primary", "true");
      newBrowser.docShellIsActive =
        (window.windowState != window.STATE_MINIMIZED &&
          !window.isFullyOccluded);
    }

    var updateBlockedPopups = false;
    if ((oldBrowser.blockedPopups && !newBrowser.blockedPopups) ||
      (!oldBrowser.blockedPopups && newBrowser.blockedPopups))
      updateBlockedPopups = true;

    this.mCurrentBrowser = newBrowser;
    this.mCurrentTab = this.tabContainer.selectedItem;
    this.showTab(this.mCurrentTab);

    gURLBar.setAttribute("switchingtabs", "true");
    window.addEventListener("MozAfterPaint", function() {
      gURLBar.removeAttribute("switchingtabs");
    }, { once: true });

    this._appendStatusPanel();

    if (updateBlockedPopups)
      this.mCurrentBrowser.updateBlockedPopups();

    // Update the URL bar.
    var loc = this.mCurrentBrowser.currentURI;

    var webProgress = this.mCurrentBrowser.webProgress;
    var securityUI = this.mCurrentBrowser.securityUI;

    this._callProgressListeners(null, "onLocationChange",
                                [webProgress, null, loc, 0],
                                true, false);

    if (securityUI) {
      // Include the true final argument to indicate that this event is
      // simulated (instead of being observed by the webProgressListener).
      this._callProgressListeners(null, "onSecurityChange",
                                  [webProgress, null, securityUI.state, true],
                                  true, false);
    }

    var listener = this._tabListeners.get(this.mCurrentTab);
    if (listener && listener.mStateFlags) {
      this._callProgressListeners(null, "onUpdateCurrentBrowser",
                                  [listener.mStateFlags, listener.mStatus,
                                  listener.mMessage, listener.mTotalProgress],
                                  true, false);
    }

    if (!this._previewMode) {
      this.mCurrentTab.updateLastAccessed();
      this.mCurrentTab.removeAttribute("unread");
      oldTab.updateLastAccessed();

      let oldFindBar = oldTab._findBar;
      if (oldFindBar &&
          oldFindBar.findMode == oldFindBar.FIND_NORMAL &&
          !oldFindBar.hidden)
        this._lastFindValue = oldFindBar._findField.value;

      this.updateTitlebar();

      this.mCurrentTab.removeAttribute("titlechanged");
      this.mCurrentTab.removeAttribute("attention");

      // The tab has been selected, it's not unselected anymore.
      // (1) Call the current tab's finishUnselectedTabHoverTimer()
      //     to save a telemetry record.
      // (2) Call the current browser's unselectedTabHover() with false
      //     to dispatch an event.
      this.mCurrentTab.finishUnselectedTabHoverTimer();
      this.mCurrentBrowser.unselectedTabHover(false);
    }

    // If the new tab is busy, and our current state is not busy, then
    // we need to fire a start to all progress listeners.
    const nsIWebProgressListener = Ci.nsIWebProgressListener;
    if (this.mCurrentTab.hasAttribute("busy") && !this.mIsBusy) {
      this.mIsBusy = true;
      this._callProgressListeners(null, "onStateChange",
                                  [webProgress, null,
                                  nsIWebProgressListener.STATE_START |
                                  nsIWebProgressListener.STATE_IS_NETWORK, 0],
                                  true, false);
    }

    // If the new tab is not busy, and our current state is busy, then
    // we need to fire a stop to all progress listeners.
    if (!this.mCurrentTab.hasAttribute("busy") && this.mIsBusy) {
      this.mIsBusy = false;
      this._callProgressListeners(null, "onStateChange",
                                  [webProgress, null,
                                  nsIWebProgressListener.STATE_STOP |
                                  nsIWebProgressListener.STATE_IS_NETWORK, 0],
                                  true, false);
    }

    // TabSelect events are suppressed during preview mode to avoid confusing extensions and other bits of code
    // that might rely upon the other changes suppressed.
    // Focus is suppressed in the event that the main browser window is minimized - focusing a tab would restore the window
    if (!this._previewMode) {
      // We've selected the new tab, so go ahead and notify listeners.
      let event = new CustomEvent("TabSelect", {
        bubbles: true,
        cancelable: false,
        detail: {
          previousTab: oldTab
        }
      });
      this.mCurrentTab.dispatchEvent(event);

      this._tabAttrModified(oldTab, ["selected"]);
      this._tabAttrModified(this.mCurrentTab, ["selected"]);

      if (oldBrowser != newBrowser &&
          oldBrowser.getInPermitUnload) {
        oldBrowser.getInPermitUnload(inPermitUnload => {
          if (!inPermitUnload) {
            return;
          }
          // Since the user is switching away from a tab that has
          // a beforeunload prompt active, we remove the prompt.
          // This prevents confusing user flows like the following:
          //   1. User attempts to close Firefox
          //   2. User switches tabs (ingoring a beforeunload prompt)
          //   3. User returns to tab, presses "Leave page"
          let promptBox = this.getTabModalPromptBox(oldBrowser);
          let prompts = promptBox.listPrompts();
          // There might not be any prompts here if the tab was closed
          // while in an onbeforeunload prompt, which will have
          // destroyed aforementioned prompt already, so check there's
          // something to remove, first:
          if (prompts.length) {
            // NB: This code assumes that the beforeunload prompt
            //     is the top-most prompt on the tab.
            prompts[prompts.length - 1].abortPrompt();
          }
        });
      }

      if (!gMultiProcessBrowser) {
        this._adjustFocusBeforeTabSwitch(oldTab, this.mCurrentTab);
        this._adjustFocusAfterTabSwitch(this.mCurrentTab);
      }
    }

    updateUserContextUIIndicator();
    gIdentityHandler.updateSharingIndicator();

    this.tabContainer._setPositionalAttributes();

    // Enable touch events to start a native dragging
    // session to allow the user to easily drag the selected tab.
    // This is currently only supported on Windows.
    oldTab.removeAttribute("touchdownstartsdrag");
    this.mCurrentTab.setAttribute("touchdownstartsdrag", "true");

    if (!gMultiProcessBrowser) {
      document.commandDispatcher.unlock();

      let event = new CustomEvent("TabSwitchDone", {
        bubbles: true,
        cancelable: true
      });
      this.dispatchEvent(event);
    }

    if (!aForceUpdate)
      TelemetryStopwatch.finish("FX_TAB_SWITCH_UPDATE_MS");
  },

  _adjustFocusBeforeTabSwitch(oldTab, newTab) {
    if (this._previewMode) {
      return;
    }

    let oldBrowser = oldTab.linkedBrowser;
    let newBrowser = newTab.linkedBrowser;

    oldBrowser._urlbarFocused = (gURLBar && gURLBar.focused);

    if (this.isFindBarInitialized(oldTab)) {
      let findBar = this.getFindBar(oldTab);
      oldTab._findBarFocused = (!findBar.hidden &&
        findBar._findField.getAttribute("focused") == "true");
    }

    let activeEl = document.activeElement;
    // If focus is on the old tab, move it to the new tab.
    if (activeEl == oldTab) {
      newTab.focus();
    } else if (gMultiProcessBrowser && activeEl != newBrowser && activeEl != newTab) {
      // In e10s, if focus isn't already in the tabstrip or on the new browser,
      // and the new browser's previous focus wasn't in the url bar but focus is
      // there now, we need to adjust focus further.
      let keepFocusOnUrlBar = newBrowser &&
        newBrowser._urlbarFocused &&
        gURLBar &&
        gURLBar.focused;
      if (!keepFocusOnUrlBar) {
        // Clear focus so that _adjustFocusAfterTabSwitch can detect if
        // some element has been focused and respect that.
        document.activeElement.blur();
      }
    }
  },

  _adjustFocusAfterTabSwitch(newTab) {
    // Don't steal focus from the tab bar.
    if (document.activeElement == newTab)
      return;

    let newBrowser = this.getBrowserForTab(newTab);

    // If there's a tabmodal prompt showing, focus it.
    if (newBrowser.hasAttribute("tabmodalPromptShowing")) {
      let prompts = newBrowser.parentNode.getElementsByTagNameNS(this._XUL_NS, "tabmodalprompt");
      let prompt = prompts[prompts.length - 1];
      prompt.Dialog.setDefaultFocus();
      return;
    }

    // Focus the location bar if it was previously focused for that tab.
    // In full screen mode, only bother making the location bar visible
    // if the tab is a blank one.
    if (newBrowser._urlbarFocused && gURLBar) {
      // Explicitly close the popup if the URL bar retains focus
      gURLBar.closePopup();

      // If the user happened to type into the URL bar for this browser
      // by the time we got here, focusing will cause the text to be
      // selected which could cause them to overwrite what they've
      // already typed in.
      if (gURLBar.focused && newBrowser.userTypedValue) {
        return;
      }

      if (!window.fullScreen || isTabEmpty(newTab)) {
        focusAndSelectUrlBar();
        return;
      }
    }

    // Focus the find bar if it was previously focused for that tab.
    if (gFindBarInitialized && !gFindBar.hidden &&
        this.selectedTab._findBarFocused) {
      gFindBar._findField.focus();
      return;
    }

    // Don't focus the content area if something has been focused after the
    // tab switch was initiated.
    if (gMultiProcessBrowser &&
        document.activeElement != document.documentElement)
      return;

    // We're now committed to focusing the content area.
    let fm = Services.focus;
    let focusFlags = fm.FLAG_NOSCROLL;

    if (!gMultiProcessBrowser) {
      let newFocusedElement = fm.getFocusedElementForWindow(window.content, true, {});

      // for anchors, use FLAG_SHOWRING so that it is clear what link was
      // last clicked when switching back to that tab
      if (newFocusedElement &&
          (newFocusedElement instanceof HTMLAnchorElement ||
            newFocusedElement.getAttributeNS("http://www.w3.org/1999/xlink", "type") == "simple"))
        focusFlags |= fm.FLAG_SHOWRING;
    }

    fm.setFocus(newBrowser, focusFlags);
  },

  _tabAttrModified(aTab, aChanged) {
    if (aTab.closing)
      return;

    let event = new CustomEvent("TabAttrModified", {
      bubbles: true,
      cancelable: false,
      detail: {
        changed: aChanged,
      }
    });
    aTab.dispatchEvent(event);
  },

  setBrowserSharing(aBrowser, aState) {
    let tab = this.getTabForBrowser(aBrowser);
    if (!tab)
      return;

    if (aState.sharing) {
      tab._sharingState = aState;
      if (aState.paused) {
        tab.removeAttribute("sharing");
      } else {
        tab.setAttribute("sharing", aState.sharing);
      }
    } else {
      tab._sharingState = null;
      tab.removeAttribute("sharing");
    }
    this._tabAttrModified(tab, ["sharing"]);

    if (aBrowser == this.mCurrentBrowser)
      gIdentityHandler.updateSharingIndicator();
  },

  getTabSharingState(aTab) {
    // Normalize the state object for consumers (ie.extensions).
    let state = Object.assign({}, aTab._sharingState);
    return {
      camera: !!state.camera,
      microphone: !!state.microphone,
      screen: state.screen && state.screen.replace("Paused", ""),
    };
  },

  setInitialTabTitle(aTab, aTitle, aOptions) {
    if (aTitle) {
      if (!aTab.getAttribute("label")) {
        aTab._labelIsInitialTitle = true;
      }

      this._setTabLabel(aTab, aTitle, aOptions);
    }
  },

  setTabTitle(aTab) {
    var browser = this.getBrowserForTab(aTab);
    var title = browser.contentTitle;

    // Don't replace an initially set label with the URL while the tab
    // is loading.
    if (aTab._labelIsInitialTitle) {
      if (!title) {
        return false;
      }
      delete aTab._labelIsInitialTitle;
    }

    let isContentTitle = false;
    if (title) {
      isContentTitle = true;
    } else if (aTab.hasAttribute("customizemode")) {
      let brandBundle = document.getElementById("bundle_brand");
      let brandShortName = brandBundle.getString("brandShortName");
      title = gNavigatorBundle.getFormattedString("customizeMode.tabTitle",
                                                  [brandShortName]);
      isContentTitle = true;
    } else {
      if (browser.currentURI.displaySpec) {
        try {
          title = this.mURIFixup.createExposableURI(browser.currentURI).displaySpec;
        } catch (ex) {
          title = browser.currentURI.displaySpec;
        }
      }

      if (title && !isBlankPageURL(title)) {
        // At this point, we now have a URI.
        // Let's try to unescape it using a character set
        // in case the URI is not ASCII.
        try {
          // If it's a long data: URI that uses base64 encoding, truncate to
          // a reasonable length rather than trying to display the entire thing.
          // We can't shorten arbitrary URIs like this, as bidi etc might mean
          // we need the trailing characters for display. But a base64-encoded
          // data-URI is plain ASCII, so this is OK for tab-title display.
          // (See bug 1408854.)
          if (title.length > 500 && title.match(/^data:[^,]+;base64,/)) {
            title = title.substring(0, 500) + "\u2026";
          } else {
            var characterSet = browser.characterSet;
            title = Services.textToSubURI.unEscapeNonAsciiURI(characterSet, title);
          }
        } catch (ex) { /* Do nothing. */ }
      } else {
        // Still no title? Fall back to our untitled string.
        title = gTabBrowserBundle.GetStringFromName("tabs.emptyTabTitle");
      }
    }

    return this._setTabLabel(aTab, title, { isContentTitle });
  },

  _setTabLabel(aTab, aLabel, aOptions) {
    if (!aLabel) {
      return false;
    }

    aTab._fullLabel = aLabel;

    aOptions = aOptions || {};
    if (!aOptions.isContentTitle) {
      // Remove protocol and "www."
      if (!("_regex_shortenURLForTabLabel" in this)) {
        this._regex_shortenURLForTabLabel = /^[^:]+:\/\/(?:www\.)?/;
      }
      aLabel = aLabel.replace(this._regex_shortenURLForTabLabel, "");
    }

    aTab._labelIsContentTitle = aOptions.isContentTitle;

    if (aTab.getAttribute("label") == aLabel) {
      return false;
    }

    let dwu = window.QueryInterface(Ci.nsIInterfaceRequestor)
                    .getInterface(Ci.nsIDOMWindowUtils);
    let isRTL = dwu.getDirectionFromText(aLabel) == Ci.nsIDOMWindowUtils.DIRECTION_RTL;

    aTab.setAttribute("label", aLabel);
    aTab.setAttribute("labeldirection", isRTL ? "rtl" : "ltr");

    // Dispatch TabAttrModified event unless we're setting the label
    // before the TabOpen event was dispatched.
    if (!aOptions.beforeTabOpen) {
      this._tabAttrModified(aTab, ["label"]);
    }

    if (aTab.selected) {
      this.updateTitlebar();
    }

    return true;
  },

  loadOneTab(aURI, aReferrerURI, aCharset, aPostData, aLoadInBackground, aAllowThirdPartyFixup) {
    var aTriggeringPrincipal;
    var aReferrerPolicy;
    var aFromExternal;
    var aRelatedToCurrent;
    var aAllowMixedContent;
    var aSkipAnimation;
    var aForceNotRemote;
    var aPreferredRemoteType;
    var aNoReferrer;
    var aUserContextId;
    var aSameProcessAsFrameLoader;
    var aOriginPrincipal;
    var aOpener;
    var aOpenerBrowser;
    var aCreateLazyBrowser;
    var aNextTabParentId;
    var aFocusUrlBar;
    var aName;
    if (arguments.length == 2 &&
        typeof arguments[1] == "object" &&
        !(arguments[1] instanceof Ci.nsIURI)) {
      let params = arguments[1];
      aTriggeringPrincipal = params.triggeringPrincipal;
      aReferrerURI = params.referrerURI;
      aReferrerPolicy = params.referrerPolicy;
      aCharset = params.charset;
      aPostData = params.postData;
      aLoadInBackground = params.inBackground;
      aAllowThirdPartyFixup = params.allowThirdPartyFixup;
      aFromExternal = params.fromExternal;
      aRelatedToCurrent = params.relatedToCurrent;
      aAllowMixedContent = params.allowMixedContent;
      aSkipAnimation = params.skipAnimation;
      aForceNotRemote = params.forceNotRemote;
      aPreferredRemoteType = params.preferredRemoteType;
      aNoReferrer = params.noReferrer;
      aUserContextId = params.userContextId;
      aSameProcessAsFrameLoader = params.sameProcessAsFrameLoader;
      aOriginPrincipal = params.originPrincipal;
      aOpener = params.opener;
      aOpenerBrowser = params.openerBrowser;
      aCreateLazyBrowser = params.createLazyBrowser;
      aNextTabParentId = params.nextTabParentId;
      aFocusUrlBar = params.focusUrlBar;
      aName = params.name;
    }

    var bgLoad = (aLoadInBackground != null) ? aLoadInBackground :
      Services.prefs.getBoolPref("browser.tabs.loadInBackground");
    var owner = bgLoad ? null : this.selectedTab;

    var tab = this.addTab(aURI, {
      triggeringPrincipal: aTriggeringPrincipal,
      referrerURI: aReferrerURI,
      referrerPolicy: aReferrerPolicy,
      charset: aCharset,
      postData: aPostData,
      ownerTab: owner,
      allowThirdPartyFixup: aAllowThirdPartyFixup,
      fromExternal: aFromExternal,
      relatedToCurrent: aRelatedToCurrent,
      skipAnimation: aSkipAnimation,
      allowMixedContent: aAllowMixedContent,
      forceNotRemote: aForceNotRemote,
      createLazyBrowser: aCreateLazyBrowser,
      preferredRemoteType: aPreferredRemoteType,
      noReferrer: aNoReferrer,
      userContextId: aUserContextId,
      originPrincipal: aOriginPrincipal,
      sameProcessAsFrameLoader: aSameProcessAsFrameLoader,
      opener: aOpener,
      openerBrowser: aOpenerBrowser,
      nextTabParentId: aNextTabParentId,
      focusUrlBar: aFocusUrlBar,
      name: aName
    });
    if (!bgLoad)
      this.selectedTab = tab;

    return tab;
  },

  loadTabs(aURIs, aLoadInBackground, aReplace) {
    let aTriggeringPrincipal;
    let aAllowThirdPartyFixup;
    let aTargetTab;
    let aNewIndex = -1;
    let aPostDatas = [];
    let aUserContextId;
    if (arguments.length == 2 &&
        typeof arguments[1] == "object") {
      let params = arguments[1];
      aLoadInBackground = params.inBackground;
      aReplace = params.replace;
      aAllowThirdPartyFixup = params.allowThirdPartyFixup;
      aTargetTab = params.targetTab;
      aNewIndex = typeof params.newIndex === "number" ?
        params.newIndex : aNewIndex;
      aPostDatas = params.postDatas || aPostDatas;
      aUserContextId = params.userContextId;
      aTriggeringPrincipal = params.triggeringPrincipal;
    }

    if (!aURIs.length)
      return;

    // The tab selected after this new tab is closed (i.e. the new tab's
    // "owner") is the next adjacent tab (i.e. not the previously viewed tab)
    // when several urls are opened here (i.e. closing the first should select
    // the next of many URLs opened) or if the pref to have UI links opened in
    // the background is set (i.e. the link is not being opened modally)
    //
    // i.e.
    //    Number of URLs    Load UI Links in BG       Focus Last Viewed?
    //    == 1              false                     YES
    //    == 1              true                      NO
    //    > 1               false/true                NO
    var multiple = aURIs.length > 1;
    var owner = multiple || aLoadInBackground ? null : this.selectedTab;
    var firstTabAdded = null;
    var targetTabIndex = -1;

    if (aReplace) {
      let browser;
      if (aTargetTab) {
        browser = this.getBrowserForTab(aTargetTab);
        targetTabIndex = aTargetTab._tPos;
      } else {
        browser = this.mCurrentBrowser;
        targetTabIndex = this.tabContainer.selectedIndex;
      }
      let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
      if (aAllowThirdPartyFixup) {
        flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP |
          Ci.nsIWebNavigation.LOAD_FLAGS_FIXUP_SCHEME_TYPOS;
      }
      try {
        browser.loadURIWithFlags(aURIs[0], {
          flags,
          postData: aPostDatas[0],
          triggeringPrincipal: aTriggeringPrincipal,
        });
      } catch (e) {
        // Ignore failure in case a URI is wrong, so we can continue
        // opening the next ones.
      }
    } else {
      firstTabAdded = this.addTab(aURIs[0], {
        ownerTab: owner,
        skipAnimation: multiple,
        allowThirdPartyFixup: aAllowThirdPartyFixup,
        postData: aPostDatas[0],
        userContextId: aUserContextId,
        triggeringPrincipal: aTriggeringPrincipal,
      });
      if (aNewIndex !== -1) {
        this.moveTabTo(firstTabAdded, aNewIndex);
        targetTabIndex = firstTabAdded._tPos;
      }
    }

    let tabNum = targetTabIndex;
    for (let i = 1; i < aURIs.length; ++i) {
      let tab = this.addTab(aURIs[i], {
        skipAnimation: true,
        allowThirdPartyFixup: aAllowThirdPartyFixup,
        postData: aPostDatas[i],
        userContextId: aUserContextId,
        triggeringPrincipal: aTriggeringPrincipal,
      });
      if (targetTabIndex !== -1)
        this.moveTabTo(tab, ++tabNum);
    }

    if (firstTabAdded && !aLoadInBackground) {
      this.selectedTab = firstTabAdded;
    }
  },

  updateBrowserRemoteness(aBrowser, aShouldBeRemote, aOptions) {
    aOptions = aOptions || {};
    let isRemote = aBrowser.getAttribute("remote") == "true";

    if (!gMultiProcessBrowser && aShouldBeRemote) {
      throw new Error("Cannot switch to remote browser in a window " +
        "without the remote tabs load context.");
    }

    // Default values for remoteType
    if (!aOptions.remoteType) {
      aOptions.remoteType = aShouldBeRemote ? E10SUtils.DEFAULT_REMOTE_TYPE : E10SUtils.NOT_REMOTE;
    }

    // If we are passed an opener, we must be making the browser non-remote, and
    // if the browser is _currently_ non-remote, we need the openers to match,
    // because it is already too late to change it.
    if (aOptions.opener) {
      if (aShouldBeRemote) {
        throw new Error("Cannot set an opener on a browser which should be remote!");
      }
      if (!isRemote && aBrowser.contentWindow.opener != aOptions.opener) {
        throw new Error("Cannot change opener on an already non-remote browser!");
      }
    }

    // Abort if we're not going to change anything
    let currentRemoteType = aBrowser.getAttribute("remoteType");
    if (isRemote == aShouldBeRemote && !aOptions.newFrameloader &&
        (!isRemote || currentRemoteType == aOptions.remoteType)) {
      return false;
    }

    let tab = this.getTabForBrowser(aBrowser);
    // aBrowser needs to be inserted now if it hasn't been already.
    this._insertBrowser(tab);

    let evt = document.createEvent("Events");
    evt.initEvent("BeforeTabRemotenessChange", true, false);
    tab.dispatchEvent(evt);

    let wasActive = document.activeElement == aBrowser;

    // Unmap the old outerWindowID.
    this._outerWindowIDBrowserMap.delete(aBrowser.outerWindowID);

    // Unhook our progress listener.
    let filter = this._tabFilters.get(tab);
    let listener = this._tabListeners.get(tab);
    aBrowser.webProgress.removeProgressListener(filter);
    filter.removeProgressListener(listener);

    // We'll be creating a new listener, so destroy the old one.
    listener.destroy();

    let oldUserTypedValue = aBrowser.userTypedValue;
    let hadStartedLoad = aBrowser.didStartLoadSinceLastUserTyping();

    // Make sure the browser is destroyed so it unregisters from observer notifications
    aBrowser.destroy();

    // Make sure to restore the original droppedLinkHandler and
    // sameProcessAsFrameLoader.
    let droppedLinkHandler = aBrowser.droppedLinkHandler;
    let sameProcessAsFrameLoader = aBrowser.sameProcessAsFrameLoader;

    // Change the "remote" attribute.
    let parent = aBrowser.parentNode;
    parent.removeChild(aBrowser);
    if (aShouldBeRemote) {
      aBrowser.setAttribute("remote", "true");
      aBrowser.setAttribute("remoteType", aOptions.remoteType);
    } else {
      aBrowser.setAttribute("remote", "false");
      aBrowser.removeAttribute("remoteType");
    }

    // NB: This works with the hack in the browser constructor that
    // turns this normal property into a field.
    if (aOptions.sameProcessAsFrameLoader) {
      // Always set sameProcessAsFrameLoader when passed in aOptions.
      aBrowser.sameProcessAsFrameLoader = aOptions.sameProcessAsFrameLoader;
    } else if (!aShouldBeRemote || currentRemoteType == aOptions.remoteType) {
      // Only copy existing sameProcessAsFrameLoader when not switching
      // remote type otherwise it would stop the switch.
      aBrowser.sameProcessAsFrameLoader = sameProcessAsFrameLoader;
    }

    if (aOptions.opener) {
      // Set the opener window on the browser, such that when the frame
      // loader is created the opener is set correctly.
      aBrowser.presetOpenerWindow(aOptions.opener);
    }

    parent.appendChild(aBrowser);

    aBrowser.userTypedValue = oldUserTypedValue;
    if (hadStartedLoad) {
      aBrowser.urlbarChangeTracker.startedLoad();
    }

    aBrowser.droppedLinkHandler = droppedLinkHandler;

    // Switching a browser's remoteness will create a new frameLoader.
    // As frameLoaders start out with an active docShell we have to
    // deactivate it if this is not the selected tab's browser or the
    // browser window is minimized.
    aBrowser.docShellIsActive = this.shouldActivateDocShell(aBrowser);

    // Create a new tab progress listener for the new browser we just injected,
    // since tab progress listeners have logic for handling the initial about:blank
    // load
    listener = new TabProgressListener(tab, aBrowser, true, false);
    this._tabListeners.set(tab, listener);
    filter.addProgressListener(listener, Ci.nsIWebProgress.NOTIFY_ALL);

    // Restore the progress listener.
    aBrowser.webProgress.addProgressListener(filter, Ci.nsIWebProgress.NOTIFY_ALL);

    // Restore the securityUI state.
    let securityUI = aBrowser.securityUI;
    let state = securityUI ? securityUI.state :
      Ci.nsIWebProgressListener.STATE_IS_INSECURE;
    // Include the true final argument to indicate that this event is
    // simulated (instead of being observed by the webProgressListener).
    this._callProgressListeners(aBrowser, "onSecurityChange",
                                [aBrowser.webProgress, null, state, true],
                                true, false);

    if (aShouldBeRemote) {
      // Switching the browser to be remote will connect to a new child
      // process so the browser can no longer be considered to be
      // crashed.
      tab.removeAttribute("crashed");
    } else {
      aBrowser.messageManager.sendAsyncMessage("Browser:AppTab", { isAppTab: tab.pinned });

      // Register the new outerWindowID.
      this._outerWindowIDBrowserMap.set(aBrowser.outerWindowID, aBrowser);
    }

    if (wasActive)
      aBrowser.focus();

    // If the findbar has been initialised, reset its browser reference.
    if (this.isFindBarInitialized(tab)) {
      this.getFindBar(tab).browser = aBrowser;
    }

    evt = document.createEvent("Events");
    evt.initEvent("TabRemotenessChange", true, false);
    tab.dispatchEvent(evt);

    return true;
  },

  updateBrowserRemotenessByURL(aBrowser, aURL, aOptions) {
    aOptions = aOptions || {};

    if (!gMultiProcessBrowser)
      return this.updateBrowserRemoteness(aBrowser, false);

    let currentRemoteType = aBrowser.getAttribute("remoteType") || null;

    aOptions.remoteType =
      E10SUtils.getRemoteTypeForURI(aURL,
        gMultiProcessBrowser,
        currentRemoteType,
        aBrowser.currentURI);

    // If this URL can't load in the current browser then flip it to the
    // correct type.
    if (currentRemoteType != aOptions.remoteType ||
      aOptions.newFrameloader) {
      let remote = aOptions.remoteType != E10SUtils.NOT_REMOTE;
      return this.updateBrowserRemoteness(aBrowser, remote, aOptions);
    }

    return false;
  },

  removePreloadedBrowser() {
    if (!this._isPreloadingEnabled()) {
      return;
    }

    let browser = this._getPreloadedBrowser();

    if (browser) {
      browser.remove();
    }
  },

  _getPreloadedBrowser() {
    if (!this._isPreloadingEnabled()) {
      return null;
    }

    // The preloaded browser might be null.
    let browser = this._preloadedBrowser;

    // Consume the browser.
    this._preloadedBrowser = null;

    // Attach the nsIFormFillController now that we know the browser
    // will be used. If we do that before and the preloaded browser
    // won't be consumed until shutdown then we leak a docShell.
    // Also, we do not need to take care of attaching nsIFormFillControllers
    // in the case that the browser is remote, as remote browsers take
    // care of that themselves.
    if (browser) {
      browser.setAttribute("preloadedState", "consumed");
      browser.setAttribute("autocompletepopup", "PopupAutoComplete");
    }

    return browser;
  },

  _isPreloadingEnabled() {
    // Preloading for the newtab page is enabled when the pref is true
    // and the URL is "about:newtab". We do not support preloading for
    // custom newtab URLs.
    return Services.prefs.getBoolPref("browser.newtab.preload") &&
      !aboutNewTabService.overridden;
  },

  _createPreloadBrowser() {
    // Do nothing if we have a preloaded browser already
    // or preloading of newtab pages is disabled.
    if (this._preloadedBrowser || !this._isPreloadingEnabled()) {
      return;
    }

    let remoteType =
      E10SUtils.getRemoteTypeForURI(BROWSER_NEW_TAB_URL,
        gMultiProcessBrowser);
    let browser = this._createBrowser({ isPreloadBrowser: true, remoteType });
    this._preloadedBrowser = browser;

    let notificationbox = this.getNotificationBox(browser);
    this.mPanelContainer.appendChild(notificationbox);

    if (remoteType != E10SUtils.NOT_REMOTE) {
      // For remote browsers, we need to make sure that the webProgress is
      // instantiated, otherwise the parent won't get informed about the state
      // of the preloaded browser until it gets attached to a tab.
      browser.webProgress;
    }

    browser.loadURI(BROWSER_NEW_TAB_URL);
    browser.docShellIsActive = false;
    browser.renderLayers = true;
    browser._urlbarFocused = true;

    // Make sure the preloaded browser is loaded with desired zoom level
    let tabURI = Services.io.newURI(BROWSER_NEW_TAB_URL);
    FullZoom.onLocationChange(tabURI, false, browser);
  },

  _createBrowser(aParams) {
    // Supported parameters:
    // userContextId, remote, remoteType, isPreloadBrowser,
    // uriIsAboutBlank, sameProcessAsFrameLoader

    let b = document.createElementNS(this._XUL_NS, "browser");
    b.permanentKey = {};
    b.setAttribute("type", "content");
    b.setAttribute("message", "true");
    b.setAttribute("messagemanagergroup", "browsers");
    b.setAttribute("contextmenu", "contentAreaContextMenu");
    b.setAttribute("tooltip", "aHTMLTooltip");

    if (aParams.userContextId) {
      b.setAttribute("usercontextid", aParams.userContextId);
    }

    // remote parameter used by some addons, use default in this case.
    if (aParams.remote && !aParams.remoteType) {
      aParams.remoteType = E10SUtils.DEFAULT_REMOTE_TYPE;
    }

    if (aParams.remoteType) {
      b.setAttribute("remoteType", aParams.remoteType);
      b.setAttribute("remote", "true");
    }

    if (aParams.openerWindow) {
      if (aParams.remoteType) {
        throw new Error("Cannot set opener window on a remote browser!");
      }
      b.presetOpenerWindow(aParams.openerWindow);
    }

    if (!aParams.isPreloadBrowser) {
      b.setAttribute("autocompletepopup", "PopupAutoComplete");
    }

    /*
     * This attribute is meant to describe if the browser is the
     * preloaded browser. There are 2 defined states: "preloaded" or
     * "consumed". The order of events goes as follows:
     *   1. The preloaded browser is created and the 'preloadedState'
     *      attribute for that browser is set to "preloaded".
     *   2. When a new tab is opened and it is time to show that
     *      preloaded browser, the 'preloadedState' attribute for that
     *      browser is set to "consumed"
     *   3. When we then navigate away from about:newtab, the "consumed"
     *      browsers will attempt to switch to a new content process,
     *      therefore the 'preloadedState' attribute is removed from
     *      that browser altogether
     * See more details on Bug 1420285.
     */
    if (aParams.isPreloadBrowser) {
      b.setAttribute("preloadedState", "preloaded");
    }

    b.setAttribute("selectmenulist", "ContentSelectDropdown");

    b.setAttribute("datetimepicker", "DateTimePickerPanel");

    b.setAttribute("autoscrollpopup", this._autoScrollPopup.id);

    if (aParams.nextTabParentId) {
      if (!aParams.remoteType) {
        throw new Error("Cannot have nextTabParentId without a remoteType");
      }
      // Gecko is going to read this attribute and use it.
      b.setAttribute("nextTabParentId", aParams.nextTabParentId.toString());
    }

    if (aParams.sameProcessAsFrameLoader) {
      b.sameProcessAsFrameLoader = aParams.sameProcessAsFrameLoader;
    }

    // This will be used by gecko to control the name of the opened
    // window.
    if (aParams.name) {
      // XXX: The `name` property is special in HTML and XUL. Should
      // we use a different attribute name for this?
      b.setAttribute("name", aParams.name);
    }

    // Create the browserStack container
    let stack = document.createElementNS(this._XUL_NS, "stack");
    stack.className = "browserStack";
    stack.appendChild(b);
    stack.setAttribute("flex", "1");

    // Create the browserContainer
    let browserContainer = document.createElementNS(this._XUL_NS, "vbox");
    browserContainer.className = "browserContainer";
    browserContainer.appendChild(stack);
    browserContainer.setAttribute("flex", "1");

    // Create the sidebar container
    let browserSidebarContainer = document.createElementNS(this._XUL_NS, "hbox");
    browserSidebarContainer.className = "browserSidebarContainer";
    browserSidebarContainer.appendChild(browserContainer);
    browserSidebarContainer.setAttribute("flex", "1");

    // Add the Message and the Browser to the box
    let notificationbox = document.createElementNS(this._XUL_NS, "notificationbox");
    notificationbox.setAttribute("flex", "1");
    notificationbox.setAttribute("notificationside", "top");
    notificationbox.appendChild(browserSidebarContainer);

    // Prevent the superfluous initial load of a blank document
    // if we're going to load something other than about:blank.
    if (!aParams.uriIsAboutBlank) {
      b.setAttribute("nodefaultsrc", "true");
    }

    return b;
  },

  _createLazyBrowser(aTab) {
    let browser = aTab.linkedBrowser;

    let names = this._browserBindingProperties;

    for (let i = 0; i < names.length; i++) {
      let name = names[i];
      let getter;
      let setter;
      switch (name) {
        case "audioMuted":
          getter = () => false;
          break;
        case "contentTitle":
          getter = () => SessionStore.getLazyTabValue(aTab, "title");
          break;
        case "currentURI":
          getter = () => {
            let url = SessionStore.getLazyTabValue(aTab, "url");
            return Services.io.newURI(url);
          };
          break;
        case "didStartLoadSinceLastUserTyping":
          getter = () => () => false;
          break;
        case "fullZoom":
        case "textZoom":
          getter = () => 1;
          break;
        case "getTabBrowser":
          getter = () => () => this;
          break;
        case "isRemoteBrowser":
          getter = () => browser.getAttribute("remote") == "true";
          break;
        case "permitUnload":
          getter = () => () => ({ permitUnload: true, timedOut: false });
          break;
        case "reload":
        case "reloadWithFlags":
          getter = () =>
            params => {
              // Wait for load handler to be instantiated before
              // initializing the reload.
              aTab.addEventListener("SSTabRestoring", () => {
                browser[name](params);
              }, { once: true });
              gBrowser._insertBrowser(aTab);
            };
          break;
        case "resumeMedia":
          getter = () =>
            () => {
              // No need to insert a browser, so we just call the browser's
              // method.
              aTab.addEventListener("SSTabRestoring", () => {
                browser[name]();
              }, { once: true });
            };
          break;
        case "userTypedValue":
        case "userTypedClear":
        case "mediaBlocked":
          getter = () => SessionStore.getLazyTabValue(aTab, name);
          break;
        default:
          getter = () => {
            if (AppConstants.NIGHTLY_BUILD) {
              let message =
                `[bug 1345098] Lazy browser prematurely inserted via '${name}' property access:\n`;
              console.log(message + new Error().stack);
            }
            this._insertBrowser(aTab);
            return browser[name];
          };
          setter = value => {
            if (AppConstants.NIGHTLY_BUILD) {
              let message =
                `[bug 1345098] Lazy browser prematurely inserted via '${name}' property access:\n`;
              console.log(message + new Error().stack);
            }
            this._insertBrowser(aTab);
            return browser[name] = value;
          };
      }
      Object.defineProperty(browser, name, {
        get: getter,
        set: setter,
        configurable: true,
        enumerable: true
      });
    }
  },

  _insertBrowser(aTab, aInsertedOnTabCreation) {
    "use strict";

    // If browser is already inserted or window is closed don't do anything.
    if (aTab.linkedPanel || window.closed) {
      return;
    }

    let browser = aTab.linkedBrowser;

    // If browser is a lazy browser, delete the substitute properties.
    if (this._browserBindingProperties[0] in browser) {
      for (let name of this._browserBindingProperties) {
        delete browser[name];
      }
    }

    let { uriIsAboutBlank, remoteType, usingPreloadedContent } =
    aTab._browserParams;
    delete aTab._browserParams;

    let notificationbox = this.getNotificationBox(browser);
    let uniqueId = this._generateUniquePanelID();
    notificationbox.id = uniqueId;
    aTab.linkedPanel = uniqueId;

    // Inject the <browser> into the DOM if necessary.
    if (!notificationbox.parentNode) {
      // NB: this appendChild call causes us to run constructors for the
      // browser element, which fires off a bunch of notifications. Some
      // of those notifications can cause code to run that inspects our
      // state, so it is important that the tab element is fully
      // initialized by this point.
      this.mPanelContainer.appendChild(notificationbox);
    }

    // wire up a progress listener for the new browser object.
    let tabListener = new TabProgressListener(aTab, browser, uriIsAboutBlank, usingPreloadedContent);
    const filter = Cc["@mozilla.org/appshell/component/browser-status-filter;1"]
      .createInstance(Ci.nsIWebProgress);
    filter.addProgressListener(tabListener, Ci.nsIWebProgress.NOTIFY_ALL);
    browser.webProgress.addProgressListener(filter, Ci.nsIWebProgress.NOTIFY_ALL);
    this._tabListeners.set(aTab, tabListener);
    this._tabFilters.set(aTab, filter);

    browser.droppedLinkHandler = handleDroppedLink;

    // Most of the time, we start our browser's docShells out as inactive,
    // and then maintain activeness in the tab switcher. Preloaded about:newtab's
    // are already created with their docShell's as inactive, but then explicitly
    // render their layers to ensure that we can switch to them quickly. We avoid
    // setting docShellIsActive to false again in this case, since that'd cause
    // the layers for the preloaded tab to be dropped, and we'd see a flash
    // of empty content instead.
    //
    // So for all browsers except for the preloaded case, we set the browser
    // docShell to inactive.
    if (!usingPreloadedContent) {
      browser.docShellIsActive = false;
    }

    // When addTab() is called with an URL that is not "about:blank" we
    // set the "nodefaultsrc" attribute that prevents a frameLoader
    // from being created as soon as the linked <browser> is inserted
    // into the DOM. We thus have to register the new outerWindowID
    // for non-remote browsers after we have called browser.loadURI().
    if (remoteType == E10SUtils.NOT_REMOTE) {
      this._outerWindowIDBrowserMap.set(browser.outerWindowID, browser);
    }

    var evt = new CustomEvent("TabBrowserInserted", { bubbles: true, detail: { insertedOnTabCreation: aInsertedOnTabCreation } });
    aTab.dispatchEvent(evt);
  },

  discardBrowser(aBrowser, aForceDiscard) {
    "use strict";

    let tab = this.getTabForBrowser(aBrowser);

    let permitUnloadFlags = aForceDiscard ? aBrowser.dontPromptAndUnload : aBrowser.dontPromptAndDontUnload;

    if (!tab ||
      tab.selected ||
      tab.closing ||
      this._windowIsClosing ||
      !aBrowser.isConnected ||
      !aBrowser.isRemoteBrowser ||
      !aBrowser.permitUnload(permitUnloadFlags).permitUnload) {
      return;
    }

    // Set browser parameters for when browser is restored.  Also remove
    // listeners and set up lazy restore data in SessionStore. This must
    // be done before aBrowser is destroyed and removed from the document.
    tab._browserParams = {
      uriIsAboutBlank: aBrowser.currentURI.spec == "about:blank",
      remoteType: aBrowser.remoteType,
      usingPreloadedContent: false
    };

    SessionStore.resetBrowserToLazyState(tab);

    this._outerWindowIDBrowserMap.delete(aBrowser.outerWindowID);

    // Remove the tab's filter and progress listener.
    let filter = this._tabFilters.get(tab);
    let listener = this._tabListeners.get(tab);
    aBrowser.webProgress.removeProgressListener(filter);
    filter.removeProgressListener(listener);
    listener.destroy();

    this._tabListeners.delete(tab);
    this._tabFilters.delete(tab);

    // Reset the findbar and remove it if it is attached to the tab.
    if (tab._findBar) {
      tab._findBar.close(true);
      tab._findBar.remove();
      delete tab._findBar;
    }

    aBrowser.destroy();

    let notificationbox = this.getNotificationBox(aBrowser);
    this.mPanelContainer.removeChild(notificationbox);
    tab.removeAttribute("linkedpanel");

    this._createLazyBrowser(tab);

    let evt = new CustomEvent("TabBrowserDiscarded", { bubbles: true });
    tab.dispatchEvent(evt);
  },

  // eslint-disable-next-line complexity
  addTab(aURI, aReferrerURI, aCharset, aPostData, aOwner, aAllowThirdPartyFixup) {
    "use strict";

    var aTriggeringPrincipal;
    var aReferrerPolicy;
    var aFromExternal;
    var aRelatedToCurrent;
    var aSkipAnimation;
    var aAllowMixedContent;
    var aForceNotRemote;
    var aPreferredRemoteType;
    var aNoReferrer;
    var aUserContextId;
    var aEventDetail;
    var aSameProcessAsFrameLoader;
    var aOriginPrincipal;
    var aDisallowInheritPrincipal;
    var aOpener;
    var aOpenerBrowser;
    var aCreateLazyBrowser;
    var aSkipBackgroundNotify;
    var aNextTabParentId;
    var aNoInitialLabel;
    var aFocusUrlBar;
    var aName;
    if (arguments.length == 2 &&
        typeof arguments[1] == "object" &&
        !(arguments[1] instanceof Ci.nsIURI)) {
      let params = arguments[1];
      aTriggeringPrincipal = params.triggeringPrincipal;
      aReferrerURI = params.referrerURI;
      aReferrerPolicy = params.referrerPolicy;
      aCharset = params.charset;
      aPostData = params.postData;
      aOwner = params.ownerTab;
      aAllowThirdPartyFixup = params.allowThirdPartyFixup;
      aFromExternal = params.fromExternal;
      aRelatedToCurrent = params.relatedToCurrent;
      aSkipAnimation = params.skipAnimation;
      aAllowMixedContent = params.allowMixedContent;
      aForceNotRemote = params.forceNotRemote;
      aPreferredRemoteType = params.preferredRemoteType;
      aNoReferrer = params.noReferrer;
      aUserContextId = params.userContextId;
      aEventDetail = params.eventDetail;
      aSameProcessAsFrameLoader = params.sameProcessAsFrameLoader;
      aOriginPrincipal = params.originPrincipal;
      aDisallowInheritPrincipal = params.disallowInheritPrincipal;
      aOpener = params.opener;
      aOpenerBrowser = params.openerBrowser;
      aCreateLazyBrowser = params.createLazyBrowser;
      aSkipBackgroundNotify = params.skipBackgroundNotify;
      aNextTabParentId = params.nextTabParentId;
      aNoInitialLabel = params.noInitialLabel;
      aFocusUrlBar = params.focusUrlBar;
      aName = params.name;
    }

    // if we're adding tabs, we're past interrupt mode, ditch the owner
    if (this.mCurrentTab.owner)
      this.mCurrentTab.owner = null;

    // Find the tab that opened this one, if any. This is used for
    // determining positioning, and inherited attributes such as the
    // user context ID.
    //
    // If we have a browser opener (which is usually the browser
    // element from a remote window.open() call), use that.
    //
    // Otherwise, if the tab is related to the current tab (e.g.,
    // because it was opened by a link click), use the selected tab as
    // the owner. If aReferrerURI is set, and we don't have an
    // explicit relatedToCurrent arg, we assume that the tab is
    // related to the current tab, since aReferrerURI is null or
    // undefined if the tab is opened from an external application or
    // bookmark (i.e. somewhere other than an existing tab).
    let relatedToCurrent = aRelatedToCurrent == null ? !!aReferrerURI : aRelatedToCurrent;
    let openerTab = ((aOpenerBrowser && this.getTabForBrowser(aOpenerBrowser)) ||
      (relatedToCurrent && this.selectedTab));

    var t = document.createElementNS(this._XUL_NS, "tab");

    t.openerTab = openerTab;

    aURI = aURI || "about:blank";
    let aURIObject = null;
    try {
      aURIObject = Services.io.newURI(aURI);
    } catch (ex) { /* we'll try to fix up this URL later */ }

    let lazyBrowserURI;
    if (aCreateLazyBrowser && aURI != "about:blank") {
      lazyBrowserURI = aURIObject;
      aURI = "about:blank";
    }

    var uriIsAboutBlank = aURI == "about:blank";

    if (!aNoInitialLabel) {
      if (isBlankPageURL(aURI)) {
        t.setAttribute("label", gTabBrowserBundle.GetStringFromName("tabs.emptyTabTitle"));
      } else {
        // Set URL as label so that the tab isn't empty initially.
        this.setInitialTabTitle(t, aURI, { beforeTabOpen: true });
      }
    }

    // Related tab inherits current tab's user context unless a different
    // usercontextid is specified
    if (aUserContextId == null && openerTab) {
      aUserContextId = openerTab.getAttribute("usercontextid") || 0;
    }

    if (aUserContextId) {
      t.setAttribute("usercontextid", aUserContextId);
      ContextualIdentityService.setTabStyle(t);
    }

    t.setAttribute("onerror", "this.removeAttribute('image');");

    if (aSkipBackgroundNotify) {
      t.setAttribute("skipbackgroundnotify", true);
    }

    t.className = "tabbrowser-tab";

    this.tabContainer._unlockTabSizing();

    // When overflowing, new tabs are scrolled into view smoothly, which
    // doesn't go well together with the width transition. So we skip the
    // transition in that case.
    let animate = !aSkipAnimation &&
      this.tabContainer.getAttribute("overflow") != "true" &&
      this.animationsEnabled;
    if (!animate) {
      t.setAttribute("fadein", "true");

      // Call _handleNewTab asynchronously as it needs to know if the
      // new tab is selected.
      setTimeout(function(tabContainer) {
        tabContainer._handleNewTab(t);
      }, 0, this.tabContainer);
    }

    // invalidate cache
    this._visibleTabs = null;

    this.tabContainer.appendChild(t);

    let usingPreloadedContent = false;
    let b;

    try {
      // If this new tab is owned by another, assert that relationship
      if (aOwner)
        t.owner = aOwner;

      var position = this.tabs.length - 1;
      t._tPos = position;
      this.tabContainer._setPositionalAttributes();

      this.tabContainer.updateVisibility();

      // If we don't have a preferred remote type, and we have a remote
      // opener, use the opener's remote type.
      if (!aPreferredRemoteType && aOpenerBrowser) {
        aPreferredRemoteType = aOpenerBrowser.remoteType;
      }

      // If URI is about:blank and we don't have a preferred remote type,
      // then we need to use the referrer, if we have one, to get the
      // correct remote type for the new tab.
      if (uriIsAboutBlank && !aPreferredRemoteType && aReferrerURI) {
        aPreferredRemoteType =
          E10SUtils.getRemoteTypeForURI(aReferrerURI.spec,
            gMultiProcessBrowser);
      }

      let remoteType =
        aForceNotRemote ? E10SUtils.NOT_REMOTE :
        E10SUtils.getRemoteTypeForURI(aURI, gMultiProcessBrowser,
          aPreferredRemoteType);

      // If we open a new tab with the newtab URL in the default
      // userContext, check if there is a preloaded browser ready.
      // Private windows are not included because both the label and the
      // icon for the tab would be set incorrectly (see bug 1195981).
      if (aURI == BROWSER_NEW_TAB_URL &&
          !aUserContextId &&
          !PrivateBrowsingUtils.isWindowPrivate(window)) {
        b = this._getPreloadedBrowser();
        if (b) {
          usingPreloadedContent = true;
        }
      }

      if (!b) {
        // No preloaded browser found, create one.
        b = this._createBrowser({
          remoteType,
          uriIsAboutBlank,
          userContextId: aUserContextId,
          sameProcessAsFrameLoader: aSameProcessAsFrameLoader,
          openerWindow: aOpener,
          nextTabParentId: aNextTabParentId,
          name: aName
        });
      }

      t.linkedBrowser = b;

      if (aFocusUrlBar) {
        b._urlbarFocused = true;
      }

      this._tabForBrowser.set(b, t);
      t.permanentKey = b.permanentKey;
      t._browserParams = {
        uriIsAboutBlank,
        remoteType,
        usingPreloadedContent
      };

      // If the caller opts in, create a lazy browser.
      if (aCreateLazyBrowser) {
        this._createLazyBrowser(t);

        if (lazyBrowserURI) {
          // Lazy browser must be explicitly registered so tab will appear as
          // a switch-to-tab candidate in autocomplete.
          this._unifiedComplete.registerOpenPage(lazyBrowserURI, aUserContextId);
          b.registeredOpenURI = lazyBrowserURI;
        }
      } else {
        this._insertBrowser(t, true);
      }
    } catch (e) {
      Cu.reportError("Failed to create tab");
      Cu.reportError(e);
      t.remove();
      if (t.linkedBrowser) {
        this._tabFilters.delete(t);
        this._tabListeners.delete(t);
        let notificationbox = this.getNotificationBox(t.linkedBrowser);
        notificationbox.remove();
      }
      throw e;
    }

    // Hack to ensure that the about:newtab favicon is loaded
    // instantaneously, to avoid flickering and improve perceived performance.
    if (aURI == "about:newtab") {
      this.setIcon(t, "chrome://branding/content/icon32.png");
    } else if (aURI == "about:privatebrowsing") {
      this.setIcon(t, "chrome://browser/skin/privatebrowsing/favicon.svg");
    }

    // Dispatch a new tab notification.  We do this once we're
    // entirely done, so that things are in a consistent state
    // even if the event listener opens or closes tabs.
    var detail = aEventDetail || {};
    var evt = new CustomEvent("TabOpen", { bubbles: true, detail });
    t.dispatchEvent(evt);

    if (!usingPreloadedContent && aOriginPrincipal && aURI) {
      let { URI_INHERITS_SECURITY_CONTEXT } = Ci.nsIProtocolHandler;
      // Unless we know for sure we're not inheriting principals,
      // force the about:blank viewer to have the right principal:
      if (!aURIObject ||
        (doGetProtocolFlags(aURIObject) & URI_INHERITS_SECURITY_CONTEXT)) {
        b.createAboutBlankContentViewer(aOriginPrincipal);
      }
    }

    // If we didn't swap docShells with a preloaded browser
    // then let's just continue loading the page normally.
    if (!usingPreloadedContent && (!uriIsAboutBlank || aDisallowInheritPrincipal)) {
      // pretend the user typed this so it'll be available till
      // the document successfully loads
      if (aURI && !gInitialPages.includes(aURI))
        b.userTypedValue = aURI;

      let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
      if (aAllowThirdPartyFixup) {
        flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
        flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FIXUP_SCHEME_TYPOS;
      }
      if (aFromExternal)
        flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL;
      if (aAllowMixedContent)
        flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_MIXED_CONTENT;
      if (aDisallowInheritPrincipal)
        flags |= Ci.nsIWebNavigation.LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL;
      try {
        b.loadURIWithFlags(aURI, {
          flags,
          triggeringPrincipal: aTriggeringPrincipal,
          referrerURI: aNoReferrer ? null : aReferrerURI,
          referrerPolicy: aReferrerPolicy,
          charset: aCharset,
          postData: aPostData,
        });
      } catch (ex) {
        Cu.reportError(ex);
      }
    }

    // If we're opening a tab related to the an existing tab, move it
    // to a position after that tab.
    if (openerTab &&
        Services.prefs.getBoolPref("browser.tabs.insertRelatedAfterCurrent")) {

      let lastRelatedTab = this._lastRelatedTabMap.get(openerTab);
      let newTabPos = (lastRelatedTab || openerTab)._tPos + 1;
      if (lastRelatedTab)
        lastRelatedTab.owner = null;
      else
        t.owner = openerTab;
      this.moveTabTo(t, newTabPos, true);
      this._lastRelatedTabMap.set(openerTab, t);
    }

    // This field is updated regardless if we actually animate
    // since it's important that we keep this count correct in all cases.
    this.tabAnimationsInProgress++;

    if (animate) {
      requestAnimationFrame(function() {
        // kick the animation off
        t.setAttribute("fadein", "true");
      });
    }

    return t;
  },

  warnAboutClosingTabs(aCloseTabs, aTab) {
    var tabsToClose;
    switch (aCloseTabs) {
      case this.closingTabsEnum.ALL:
        tabsToClose = this.tabs.length - this._removingTabs.length -
          gBrowser._numPinnedTabs;
        break;
      case this.closingTabsEnum.OTHER:
        tabsToClose = this.visibleTabs.length - 1 - gBrowser._numPinnedTabs;
        break;
      case this.closingTabsEnum.TO_END:
        if (!aTab)
          throw new Error("Required argument missing: aTab");

        tabsToClose = this.getTabsToTheEndFrom(aTab).length;
        break;
      default:
        throw new Error("Invalid argument: " + aCloseTabs);
    }

    if (tabsToClose <= 1)
      return true;

    const pref = aCloseTabs == this.closingTabsEnum.ALL ?
      "browser.tabs.warnOnClose" : "browser.tabs.warnOnCloseOtherTabs";
    var shouldPrompt = Services.prefs.getBoolPref(pref);
    if (!shouldPrompt)
      return true;

    var ps = Services.prompt;

    // default to true: if it were false, we wouldn't get this far
    var warnOnClose = { value: true };

    // focus the window before prompting.
    // this will raise any minimized window, which will
    // make it obvious which window the prompt is for and will
    // solve the problem of windows "obscuring" the prompt.
    // see bug #350299 for more details
    window.focus();
    var warningMessage =
      PluralForm.get(tabsToClose, gTabBrowserBundle.GetStringFromName("tabs.closeWarningMultiple"))
      .replace("#1", tabsToClose);
    var buttonPressed =
      ps.confirmEx(window,
        gTabBrowserBundle.GetStringFromName("tabs.closeWarningTitle"),
        warningMessage,
        (ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_0) +
        (ps.BUTTON_TITLE_CANCEL * ps.BUTTON_POS_1),
        gTabBrowserBundle.GetStringFromName("tabs.closeButtonMultiple"),
        null, null,
        aCloseTabs == this.closingTabsEnum.ALL ?
        gTabBrowserBundle.GetStringFromName("tabs.closeWarningPromptMe") : null,
        warnOnClose);
    var reallyClose = (buttonPressed == 0);

    // don't set the pref unless they press OK and it's false
    if (aCloseTabs == this.closingTabsEnum.ALL && reallyClose && !warnOnClose.value)
      Services.prefs.setBoolPref(pref, false);

    return reallyClose;
  },

  getTabsToTheEndFrom(aTab) {
    let tabsToEnd = [];
    let tabs = this.visibleTabs;
    for (let i = tabs.length - 1; i >= 0; --i) {
      if (tabs[i] == aTab || tabs[i].pinned) {
        break;
      }
      tabsToEnd.push(tabs[i]);
    }
    return tabsToEnd;
  },

  removeTabsToTheEndFrom(aTab, aParams) {
    if (!this.warnAboutClosingTabs(this.closingTabsEnum.TO_END, aTab))
      return;

    let removeTab = tab => {
      // Avoid changing the selected browser several times.
      if (tab.selected)
        this.selectedTab = aTab;

      this.removeTab(tab, aParams);
    };

    let tabs = this.getTabsToTheEndFrom(aTab);
    let tabsWithBeforeUnload = [];
    for (let i = tabs.length - 1; i >= 0; --i) {
      let tab = tabs[i];
      if (this._hasBeforeUnload(tab))
        tabsWithBeforeUnload.push(tab);
      else
        removeTab(tab);
    }
    tabsWithBeforeUnload.forEach(removeTab);
  },

  removeAllTabsBut(aTab) {
    if (!this.warnAboutClosingTabs(this.closingTabsEnum.OTHER)) {
      return;
    }

    let tabs = this.visibleTabs.reverse();
    this.selectedTab = aTab;

    let tabsWithBeforeUnload = [];
    for (let i = tabs.length - 1; i >= 0; --i) {
      let tab = tabs[i];
      if (tab != aTab && !tab.pinned) {
        if (this._hasBeforeUnload(tab))
          tabsWithBeforeUnload.push(tab);
        else
          this.removeTab(tab, { animate: true });
      }
    }
    for (let tab of tabsWithBeforeUnload) {
      this.removeTab(tab, { animate: true });
    }
  },

  removeCurrentTab(aParams) {
    this.removeTab(this.mCurrentTab, aParams);
  },

  removeTab(aTab, aParams) {
    if (aParams) {
      var animate = aParams.animate;
      var byMouse = aParams.byMouse;
      var skipPermitUnload = aParams.skipPermitUnload;
    }

    // Telemetry stopwatches may already be running if removeTab gets
    // called again for an already closing tab.
    if (!TelemetryStopwatch.running("FX_TAB_CLOSE_TIME_ANIM_MS", aTab) &&
        !TelemetryStopwatch.running("FX_TAB_CLOSE_TIME_NO_ANIM_MS", aTab)) {
      // Speculatevely start both stopwatches now. We'll cancel one of
      // the two later depending on whether we're animating.
      TelemetryStopwatch.start("FX_TAB_CLOSE_TIME_ANIM_MS", aTab);
      TelemetryStopwatch.start("FX_TAB_CLOSE_TIME_NO_ANIM_MS", aTab);
    }
    window.maybeRecordAbandonmentTelemetry(aTab, "tabClosed");

    // Handle requests for synchronously removing an already
    // asynchronously closing tab.
    if (!animate &&
        aTab.closing) {
      this._endRemoveTab(aTab);
      return;
    }

    var isLastTab = (this.tabs.length - this._removingTabs.length == 1);

    if (!this._beginRemoveTab(aTab, null, null, true, skipPermitUnload)) {
      TelemetryStopwatch.cancel("FX_TAB_CLOSE_TIME_ANIM_MS", aTab);
      TelemetryStopwatch.cancel("FX_TAB_CLOSE_TIME_NO_ANIM_MS", aTab);
      return;
    }

    if (!aTab.pinned && !aTab.hidden && aTab._fullyOpen && byMouse)
      this.tabContainer._lockTabSizing(aTab);
    else
      this.tabContainer._unlockTabSizing();

    if (!animate /* the caller didn't opt in */ ||
      isLastTab ||
      aTab.pinned ||
      aTab.hidden ||
      this._removingTabs.length > 3 /* don't want lots of concurrent animations */ ||
      aTab.getAttribute("fadein") != "true" /* fade-in transition hasn't been triggered yet */ ||
      window.getComputedStyle(aTab).maxWidth == "0.1px" /* fade-in transition hasn't moved yet */ ||
      !this.animationsEnabled) {
      // We're not animating, so we can cancel the animation stopwatch.
      TelemetryStopwatch.cancel("FX_TAB_CLOSE_TIME_ANIM_MS", aTab);
      this._endRemoveTab(aTab);
      return;
    }

    // We're animating, so we can cancel the non-animation stopwatch.
    TelemetryStopwatch.cancel("FX_TAB_CLOSE_TIME_NO_ANIM_MS", aTab);

    aTab.style.maxWidth = ""; // ensure that fade-out transition happens
    aTab.removeAttribute("fadein");
    aTab.removeAttribute("bursting");

    setTimeout(function(tab, tabbrowser) {
      if (tab.parentNode &&
          window.getComputedStyle(tab).maxWidth == "0.1px") {
        NS_ASSERT(false, "Giving up waiting for the tab closing animation to finish (bug 608589)");
        tabbrowser._endRemoveTab(tab);
      }
    }, 3000, aTab, this);
  },

  _hasBeforeUnload(aTab) {
    let browser = aTab.linkedBrowser;
    return browser.isRemoteBrowser && browser.frameLoader &&
           browser.frameLoader.tabParent &&
           browser.frameLoader.tabParent.hasBeforeUnload;
  },

  _beginRemoveTab(aTab, aAdoptedByTab, aCloseWindowWithLastTab, aCloseWindowFastpath, aSkipPermitUnload) {
    if (aTab.closing ||
      this._windowIsClosing)
      return false;

    var browser = this.getBrowserForTab(aTab);
    if (!aSkipPermitUnload && !aAdoptedByTab &&
        aTab.linkedPanel && !aTab._pendingPermitUnload &&
        (!browser.isRemoteBrowser || this._hasBeforeUnload(aTab))) {
      TelemetryStopwatch.start("FX_TAB_CLOSE_PERMIT_UNLOAD_TIME_MS", aTab);

      // We need to block while calling permitUnload() because it
      // processes the event queue and may lead to another removeTab()
      // call before permitUnload() returns.
      aTab._pendingPermitUnload = true;
      let { permitUnload, timedOut } = browser.permitUnload();
      delete aTab._pendingPermitUnload;

      TelemetryStopwatch.finish("FX_TAB_CLOSE_PERMIT_UNLOAD_TIME_MS", aTab);

      // If we were closed during onbeforeunload, we return false now
      // so we don't (try to) close the same tab again. Of course, we
      // also stop if the unload was cancelled by the user:
      if (aTab.closing || (!timedOut && !permitUnload)) {
        return false;
      }
    }

    this._blurTab(aTab);

    var closeWindow = false;
    var newTab = false;
    if (this.tabs.length - this._removingTabs.length == 1) {
      closeWindow = aCloseWindowWithLastTab != null ? aCloseWindowWithLastTab :
        !window.toolbar.visible ||
        Services.prefs.getBoolPref("browser.tabs.closeWindowWithLastTab");

      if (closeWindow) {
        // We've already called beforeunload on all the relevant tabs if we get here,
        // so avoid calling it again:
        window.skipNextCanClose = true;
      }

      // Closing the tab and replacing it with a blank one is notably slower
      // than closing the window right away. If the caller opts in, take
      // the fast path.
      if (closeWindow &&
          aCloseWindowFastpath &&
          this._removingTabs.length == 0) {
        // This call actually closes the window, unless the user
        // cancels the operation.  We are finished here in both cases.
        this._windowIsClosing = window.closeWindow(true, window.warnAboutClosingWindow);
        return false;
      }

      newTab = true;
    }
    aTab._endRemoveArgs = [closeWindow, newTab];

    // swapBrowsersAndCloseOther will take care of closing the window without animation.
    if (closeWindow && aAdoptedByTab) {
      // Remove the tab's filter to avoid leaking.
      if (aTab.linkedPanel) {
        this._tabFilters.delete(aTab);
      }
      return true;
    }

    if (!aTab._fullyOpen) {
      // If the opening tab animation hasn't finished before we start closing the
      // tab, decrement the animation count since _handleNewTab will not get called.
      this.tabAnimationsInProgress--;
    }

    this.tabAnimationsInProgress++;

    // Mute audio immediately to improve perceived speed of tab closure.
    if (!aAdoptedByTab && aTab.hasAttribute("soundplaying")) {
      // Don't persist the muted state as this wasn't a user action.
      // This lets undo-close-tab return it to an unmuted state.
      aTab.linkedBrowser.mute(true);
    }

    aTab.closing = true;
    this._removingTabs.push(aTab);
    this._visibleTabs = null; // invalidate cache

    // Invalidate hovered tab state tracking for this closing tab.
    if (this.tabContainer._hoveredTab == aTab)
      aTab._mouseleave();

    if (newTab)
      this.addTab(BROWSER_NEW_TAB_URL, { skipAnimation: true });
    else
      this.tabContainer.updateVisibility();

    // We're committed to closing the tab now.
    // Dispatch a notification.
    // We dispatch it before any teardown so that event listeners can
    // inspect the tab that's about to close.
    var evt = new CustomEvent("TabClose", { bubbles: true, detail: { adoptedBy: aAdoptedByTab } });
    aTab.dispatchEvent(evt);

    if (aTab.linkedPanel) {
      if (!aAdoptedByTab && !gMultiProcessBrowser) {
        // Prevent this tab from showing further dialogs, since we're closing it
        var windowUtils = browser.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                                               .getInterface(Ci.nsIDOMWindowUtils);
        windowUtils.disableDialogs();
      }

      // Remove the tab's filter and progress listener.
      const filter = this._tabFilters.get(aTab);

      browser.webProgress.removeProgressListener(filter);

      const listener = this._tabListeners.get(aTab);
      filter.removeProgressListener(listener);
      listener.destroy();
    }

    if (browser.registeredOpenURI && !aAdoptedByTab) {
      this._unifiedComplete.unregisterOpenPage(browser.registeredOpenURI,
        browser.getAttribute("usercontextid") || 0);
      delete browser.registeredOpenURI;
    }

    // We are no longer the primary content area.
    browser.removeAttribute("primary");

    // Remove this tab as the owner of any other tabs, since it's going away.
    for (let tab of this.tabs) {
      if ("owner" in tab && tab.owner == aTab)
        // |tab| is a child of the tab we're removing, make it an orphan
        tab.owner = null;
    }

    return true;
  },

  _endRemoveTab(aTab) {
    if (!aTab || !aTab._endRemoveArgs)
      return;

    var [aCloseWindow, aNewTab] = aTab._endRemoveArgs;
    aTab._endRemoveArgs = null;

    if (this._windowIsClosing) {
      aCloseWindow = false;
      aNewTab = false;
    }

    this.tabAnimationsInProgress--;

    this._lastRelatedTabMap = new WeakMap();

    // update the UI early for responsiveness
    aTab.collapsed = true;
    this._blurTab(aTab);

    this._removingTabs.splice(this._removingTabs.indexOf(aTab), 1);

    if (aCloseWindow) {
      this._windowIsClosing = true;
      while (this._removingTabs.length)
        this._endRemoveTab(this._removingTabs[0]);
    } else if (!this._windowIsClosing) {
      if (aNewTab)
        focusAndSelectUrlBar();

      // workaround for bug 345399
      this.tabContainer.arrowScrollbox._updateScrollButtonsDisabledState();
    }

    // We're going to remove the tab and the browser now.
    this._tabFilters.delete(aTab);
    this._tabListeners.delete(aTab);

    var browser = this.getBrowserForTab(aTab);

    if (aTab.linkedPanel) {
      this._outerWindowIDBrowserMap.delete(browser.outerWindowID);

      // Because of the way XBL works (fields just set JS
      // properties on the element) and the code we have in place
      // to preserve the JS objects for any elements that have
      // JS properties set on them, the browser element won't be
      // destroyed until the document goes away.  So we force a
      // cleanup ourselves.
      // This has to happen before we remove the child so that the
      // XBL implementation of nsIObserver still works.
      browser.destroy();
    }

    var wasPinned = aTab.pinned;

    // Remove the tab ...
    this.tabContainer.removeChild(aTab);

    // ... and fix up the _tPos properties immediately.
    for (let i = aTab._tPos; i < this.tabs.length; i++)
      this.tabs[i]._tPos = i;

    if (!this._windowIsClosing) {
      if (wasPinned)
        this.tabContainer._positionPinnedTabs();

      // update tab close buttons state
      this.tabContainer._updateCloseButtons();

      setTimeout(function(tabs) {
        tabs._lastTabClosedByMouse = false;
      }, 0, this.tabContainer);
    }

    // update tab positional properties and attributes
    this.selectedTab._selected = true;
    this.tabContainer._setPositionalAttributes();

    // Removing the panel requires fixing up selectedPanel immediately
    // (see below), which would be hindered by the potentially expensive
    // browser removal. So we remove the browser and the panel in two
    // steps.

    var panel = this.getNotificationBox(browser);

    // In the multi-process case, it's possible an asynchronous tab switch
    // is still underway. If so, then it's possible that the last visible
    // browser is the one we're in the process of removing. There's the
    // risk of displaying preloaded browsers that are at the end of the
    // deck if we remove the browser before the switch is complete, so
    // we alert the switcher in order to show a spinner instead.
    if (this._switcher) {
      this._switcher.onTabRemoved(aTab);
    }

    // This will unload the document. An unload handler could remove
    // dependant tabs, so it's important that the tabbrowser is now in
    // a consistent state (tab removed, tab positions updated, etc.).
    browser.remove();

    // Release the browser in case something is erroneously holding a
    // reference to the tab after its removal.
    this._tabForBrowser.delete(aTab.linkedBrowser);
    aTab.linkedBrowser = null;

    panel.remove();

    // closeWindow might wait an arbitrary length of time if we're supposed
    // to warn about closing the window, so we'll just stop the tab close
    // stopwatches here instead.
    TelemetryStopwatch.finish("FX_TAB_CLOSE_TIME_ANIM_MS", aTab,
      true /* aCanceledOkay */ );
    TelemetryStopwatch.finish("FX_TAB_CLOSE_TIME_NO_ANIM_MS", aTab,
      true /* aCanceledOkay */ );

    if (aCloseWindow)
      this._windowIsClosing = closeWindow(true, window.warnAboutClosingWindow);
  },

  _findTabToBlurTo(aTab) {
    if (!aTab.selected) {
      return null;
    }

    if (aTab.owner &&
        !aTab.owner.hidden &&
        !aTab.owner.closing &&
        Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose")) {
      return aTab.owner;
    }

    // Switch to a visible tab unless there aren't any others remaining
    let remainingTabs = this.visibleTabs;
    let numTabs = remainingTabs.length;
    if (numTabs == 0 || numTabs == 1 && remainingTabs[0] == aTab) {
      remainingTabs = Array.filter(this.tabs, function(tab) {
        return !tab.closing;
      }, this);
    }

    // Try to find a remaining tab that comes after the given tab
    let tab = aTab;
    do {
      tab = tab.nextSibling;
    } while (tab && !remainingTabs.includes(tab));

    if (!tab) {
      tab = aTab;

      do {
        tab = tab.previousSibling;
      } while (tab && !remainingTabs.includes(tab));
    }

    return tab;
  },

  _blurTab(aTab) {
    this.selectedTab = this._findTabToBlurTo(aTab);
  },

  swapBrowsersAndCloseOther(aOurTab, aOtherTab) {
    // Do not allow transfering a private tab to a non-private window
    // and vice versa.
    if (PrivateBrowsingUtils.isWindowPrivate(window) !=
      PrivateBrowsingUtils.isWindowPrivate(aOtherTab.ownerGlobal))
      return;

    let ourBrowser = this.getBrowserForTab(aOurTab);
    let otherBrowser = aOtherTab.linkedBrowser;

    // Can't swap between chrome and content processes.
    if (ourBrowser.isRemoteBrowser != otherBrowser.isRemoteBrowser)
      return;

    // Keep the userContextId if set on other browser
    if (otherBrowser.hasAttribute("usercontextid")) {
      ourBrowser.setAttribute("usercontextid", otherBrowser.getAttribute("usercontextid"));
    }

    // That's gBrowser for the other window, not the tab's browser!
    var remoteBrowser = aOtherTab.ownerGlobal.gBrowser;
    var isPending = aOtherTab.hasAttribute("pending");

    let otherTabListener = remoteBrowser._tabListeners.get(aOtherTab);
    let stateFlags = otherTabListener.mStateFlags;

    // Expedite the removal of the icon if it was already scheduled.
    if (aOtherTab._soundPlayingAttrRemovalTimer) {
      clearTimeout(aOtherTab._soundPlayingAttrRemovalTimer);
      aOtherTab._soundPlayingAttrRemovalTimer = 0;
      aOtherTab.removeAttribute("soundplaying");
      remoteBrowser._tabAttrModified(aOtherTab, ["soundplaying"]);
    }

    // First, start teardown of the other browser.  Make sure to not
    // fire the beforeunload event in the process.  Close the other
    // window if this was its last tab.
    if (!remoteBrowser._beginRemoveTab(aOtherTab, aOurTab, true))
      return;

    // If this is the last tab of the window, hide the window
    // immediately without animation before the docshell swap, to avoid
    // about:blank being painted.
    let [closeWindow] = aOtherTab._endRemoveArgs;
    if (closeWindow) {
      let win = aOtherTab.ownerGlobal;
      let dwu = win.QueryInterface(Ci.nsIInterfaceRequestor)
                   .getInterface(Ci.nsIDOMWindowUtils);
      dwu.suppressAnimation(true);
      // Only suppressing window animations isn't enough to avoid
      // an empty content area being painted.
      let baseWin = win.QueryInterface(Ci.nsIInterfaceRequestor)
                       .getInterface(Ci.nsIDocShell)
                       .QueryInterface(Ci.nsIDocShellTreeItem)
                       .treeOwner
                       .QueryInterface(Ci.nsIBaseWindow);
      baseWin.visibility = false;
    }

    let modifiedAttrs = [];
    if (aOtherTab.hasAttribute("muted")) {
      aOurTab.setAttribute("muted", "true");
      aOurTab.muteReason = aOtherTab.muteReason;
      ourBrowser.mute();
      modifiedAttrs.push("muted");
    }
    if (aOtherTab.hasAttribute("soundplaying")) {
      aOurTab.setAttribute("soundplaying", "true");
      modifiedAttrs.push("soundplaying");
    }
    if (aOtherTab.hasAttribute("usercontextid")) {
      aOurTab.setUserContextId(aOtherTab.getAttribute("usercontextid"));
      modifiedAttrs.push("usercontextid");
    }
    if (aOtherTab.hasAttribute("sharing")) {
      aOurTab.setAttribute("sharing", aOtherTab.getAttribute("sharing"));
      modifiedAttrs.push("sharing");
      aOurTab._sharingState = aOtherTab._sharingState;
      webrtcUI.swapBrowserForNotification(otherBrowser, ourBrowser);
    }

    SitePermissions.copyTemporaryPermissions(otherBrowser, ourBrowser);

    // If the other tab is pending (i.e. has not been restored, yet)
    // then do not switch docShells but retrieve the other tab's state
    // and apply it to our tab.
    if (isPending) {
      SessionStore.setTabState(aOurTab, SessionStore.getTabState(aOtherTab));

      // Make sure to unregister any open URIs.
      this._swapRegisteredOpenURIs(ourBrowser, otherBrowser);
    } else {
      // Workarounds for bug 458697
      // Icon might have been set on DOMLinkAdded, don't override that.
      if (!ourBrowser.mIconURL && otherBrowser.mIconURL)
        this.setIcon(aOurTab, otherBrowser.mIconURL, otherBrowser.contentPrincipal, otherBrowser.contentRequestContextID);
      var isBusy = aOtherTab.hasAttribute("busy");
      if (isBusy) {
        aOurTab.setAttribute("busy", "true");
        modifiedAttrs.push("busy");
        if (aOurTab.selected)
          this.mIsBusy = true;
      }

      this._swapBrowserDocShells(aOurTab, otherBrowser, Ci.nsIBrowser.SWAP_DEFAULT, stateFlags);
    }

    // Unregister the previously opened URI
    if (otherBrowser.registeredOpenURI) {
      this._unifiedComplete.unregisterOpenPage(otherBrowser.registeredOpenURI,
        otherBrowser.getAttribute("usercontextid") || 0);
      delete otherBrowser.registeredOpenURI;
    }

    // Handle findbar data (if any)
    let otherFindBar = aOtherTab._findBar;
    if (otherFindBar &&
        otherFindBar.findMode == otherFindBar.FIND_NORMAL) {
      let ourFindBar = this.getFindBar(aOurTab);
      ourFindBar._findField.value = otherFindBar._findField.value;
      if (!otherFindBar.hidden)
        ourFindBar.onFindCommand();
    }

    // Finish tearing down the tab that's going away.
    if (closeWindow) {
      aOtherTab.ownerGlobal.close();
    } else {
      remoteBrowser._endRemoveTab(aOtherTab);
    }

    this.setTabTitle(aOurTab);

    // If the tab was already selected (this happpens in the scenario
    // of replaceTabWithWindow), notify onLocationChange, etc.
    if (aOurTab.selected)
      this.updateCurrentBrowser(true);

    if (modifiedAttrs.length) {
      this._tabAttrModified(aOurTab, modifiedAttrs);
    }
  },

  swapBrowsers(aOurTab, aOtherTab, aFlags) {
    let otherBrowser = aOtherTab.linkedBrowser;
    let otherTabBrowser = otherBrowser.getTabBrowser();

    // We aren't closing the other tab so, we also need to swap its tablisteners.
    let filter = otherTabBrowser._tabFilters.get(aOtherTab);
    let tabListener = otherTabBrowser._tabListeners.get(aOtherTab);
    otherBrowser.webProgress.removeProgressListener(filter);
    filter.removeProgressListener(tabListener);

    // Perform the docshell swap through the common mechanism.
    this._swapBrowserDocShells(aOurTab, otherBrowser, aFlags);

    // Restore the listeners for the swapped in tab.
    tabListener = new otherTabBrowser.ownerGlobal.TabProgressListener(aOtherTab, otherBrowser, false, false);
    otherTabBrowser._tabListeners.set(aOtherTab, tabListener);

    const notifyAll = Ci.nsIWebProgress.NOTIFY_ALL;
    filter.addProgressListener(tabListener, notifyAll);
    otherBrowser.webProgress.addProgressListener(filter, notifyAll);
  },

  _swapBrowserDocShells(aOurTab, aOtherBrowser, aFlags, aStateFlags) {
    // aOurTab's browser needs to be inserted now if it hasn't already.
    this._insertBrowser(aOurTab);

    // Unhook our progress listener
    const filter = this._tabFilters.get(aOurTab);
    let tabListener = this._tabListeners.get(aOurTab);
    let ourBrowser = this.getBrowserForTab(aOurTab);
    ourBrowser.webProgress.removeProgressListener(filter);
    filter.removeProgressListener(tabListener);

    // Make sure to unregister any open URIs.
    this._swapRegisteredOpenURIs(ourBrowser, aOtherBrowser);

    // Unmap old outerWindowIDs.
    this._outerWindowIDBrowserMap.delete(ourBrowser.outerWindowID);
    let remoteBrowser = aOtherBrowser.ownerGlobal.gBrowser;
    if (remoteBrowser) {
      remoteBrowser._outerWindowIDBrowserMap.delete(aOtherBrowser.outerWindowID);
    }

    // If switcher is active, it will intercept swap events and
    // react as needed.
    if (!this._switcher) {
      aOtherBrowser.docShellIsActive = this.shouldActivateDocShell(ourBrowser);
    }

    // Swap the docshells
    ourBrowser.swapDocShells(aOtherBrowser);

    if (ourBrowser.isRemoteBrowser) {
      // Switch outerWindowIDs for remote browsers.
      let ourOuterWindowID = ourBrowser._outerWindowID;
      ourBrowser._outerWindowID = aOtherBrowser._outerWindowID;
      aOtherBrowser._outerWindowID = ourOuterWindowID;
    }

    // Register new outerWindowIDs.
    this._outerWindowIDBrowserMap.set(ourBrowser.outerWindowID, ourBrowser);
    if (remoteBrowser) {
      remoteBrowser._outerWindowIDBrowserMap.set(aOtherBrowser.outerWindowID, aOtherBrowser);
    }

    if (!(aFlags & Ci.nsIBrowser.SWAP_KEEP_PERMANENT_KEY)) {
      // Swap permanentKey properties.
      let ourPermanentKey = ourBrowser.permanentKey;
      ourBrowser.permanentKey = aOtherBrowser.permanentKey;
      aOtherBrowser.permanentKey = ourPermanentKey;
      aOurTab.permanentKey = ourBrowser.permanentKey;
      if (remoteBrowser) {
        let otherTab = remoteBrowser.getTabForBrowser(aOtherBrowser);
        if (otherTab) {
          otherTab.permanentKey = aOtherBrowser.permanentKey;
        }
      }
    }

    // Restore the progress listener
    tabListener = new TabProgressListener(aOurTab, ourBrowser, false, false, aStateFlags);
    this._tabListeners.set(aOurTab, tabListener);

    const notifyAll = Ci.nsIWebProgress.NOTIFY_ALL;
    filter.addProgressListener(tabListener, notifyAll);
    ourBrowser.webProgress.addProgressListener(filter, notifyAll);
  },

  _swapRegisteredOpenURIs(aOurBrowser, aOtherBrowser) {
    // Swap the registeredOpenURI properties of the two browsers
    let tmp = aOurBrowser.registeredOpenURI;
    delete aOurBrowser.registeredOpenURI;
    if (aOtherBrowser.registeredOpenURI) {
      aOurBrowser.registeredOpenURI = aOtherBrowser.registeredOpenURI;
      delete aOtherBrowser.registeredOpenURI;
    }
    if (tmp) {
      aOtherBrowser.registeredOpenURI = tmp;
    }
  },

  reloadAllTabs() {
    let tabs = this.visibleTabs;
    let l = tabs.length;
    for (var i = 0; i < l; i++) {
      try {
        this.getBrowserForTab(tabs[i]).reload();
      } catch (e) {
        // ignore failure to reload so others will be reloaded
      }
    }
  },

  reloadTab(aTab) {
    let browser = this.getBrowserForTab(aTab);
    // Reset temporary permissions on the current tab. This is done here
    // because we only want to reset permissions on user reload.
    SitePermissions.clearTemporaryPermissions(browser);
    browser.reload();
  },

  addProgressListener(aListener) {
    if (arguments.length != 1) {
      Cu.reportError("gBrowser.addProgressListener was " +
        "called with a second argument, " +
        "which is not supported. See bug " +
        "608628. Call stack: " + new Error().stack);
    }

    this.mProgressListeners.push(aListener);
  },

  removeProgressListener(aListener) {
    this.mProgressListeners =
      this.mProgressListeners.filter(l => l != aListener);
  },

  addTabsProgressListener(aListener) {
    this.mTabsProgressListeners.push(aListener);
  },

  removeTabsProgressListener(aListener) {
    this.mTabsProgressListeners =
      this.mTabsProgressListeners.filter(l => l != aListener);
  },

  getBrowserForTab(aTab) {
    return aTab.linkedBrowser;
  },

  showOnlyTheseTabs(aTabs) {
    for (let tab of this.tabs) {
      if (!aTabs.includes(tab))
        this.hideTab(tab);
      else
        this.showTab(tab);
    }

    this.tabContainer._handleTabSelect(true);
  },

  showTab(aTab) {
    if (aTab.hidden) {
      aTab.removeAttribute("hidden");
      this._visibleTabs = null; // invalidate cache

      this.tabContainer._updateCloseButtons();

      this.tabContainer._setPositionalAttributes();

      let event = document.createEvent("Events");
      event.initEvent("TabShow", true, false);
      aTab.dispatchEvent(event);
      SessionStore.deleteTabValue(aTab, "hiddenBy");
    }
  },

  hideTab(aTab, aSource) {
    if (!aTab.hidden && !aTab.pinned && !aTab.selected &&
        !aTab.closing && !aTab._sharingState) {
      aTab.setAttribute("hidden", "true");
      this._visibleTabs = null; // invalidate cache

      this.tabContainer._updateCloseButtons();

      this.tabContainer._setPositionalAttributes();

      let event = document.createEvent("Events");
      event.initEvent("TabHide", true, false);
      aTab.dispatchEvent(event);
      if (aSource) {
        SessionStore.setTabValue(aTab, "hiddenBy", aSource);
      }
    }
  },

  selectTabAtIndex(aIndex, aEvent) {
    let tabs = this.visibleTabs;

    // count backwards for aIndex < 0
    if (aIndex < 0) {
      aIndex += tabs.length;
      // clamp at index 0 if still negative.
      if (aIndex < 0)
        aIndex = 0;
    } else if (aIndex >= tabs.length) {
      // clamp at right-most tab if out of range.
      aIndex = tabs.length - 1;
    }

    this.selectedTab = tabs[aIndex];

    if (aEvent) {
      aEvent.preventDefault();
      aEvent.stopPropagation();
    }
  },

  /**
   * Moves a tab to a new browser window, unless it's already the only tab
   * in the current window, in which case this will do nothing.
   */
  replaceTabWithWindow(aTab, aOptions) {
    if (this.tabs.length == 1)
      return null;

    var options = "chrome,dialog=no,all";
    for (var name in aOptions)
      options += "," + name + "=" + aOptions[name];

    // Play the tab closing animation to give immediate feedback while
    // waiting for the new window to appear.
    // content area when the docshells are swapped.
    if (this.animationsEnabled) {
      aTab.style.maxWidth = ""; // ensure that fade-out transition happens
      aTab.removeAttribute("fadein");
    }

    // tell a new window to take the "dropped" tab
    return window.openDialog(getBrowserURL(), "_blank", options, aTab);
  },

  moveTabTo(aTab, aIndex, aKeepRelatedTabs) {
    var oldPosition = aTab._tPos;
    if (oldPosition == aIndex)
      return;

    // Don't allow mixing pinned and unpinned tabs.
    if (aTab.pinned)
      aIndex = Math.min(aIndex, this._numPinnedTabs - 1);
    else
      aIndex = Math.max(aIndex, this._numPinnedTabs);
    if (oldPosition == aIndex)
      return;

    if (!aKeepRelatedTabs) {
      this._lastRelatedTabMap = new WeakMap();
    }

    let wasFocused = (document.activeElement == this.mCurrentTab);

    aIndex = aIndex < aTab._tPos ? aIndex : aIndex + 1;

    // invalidate cache
    this._visibleTabs = null;

    // use .item() instead of [] because dragging to the end of the strip goes out of
    // bounds: .item() returns null (so it acts like appendChild), but [] throws
    this.tabContainer.insertBefore(aTab, this.tabs.item(aIndex));

    for (let i = 0; i < this.tabs.length; i++) {
      this.tabs[i]._tPos = i;
      this.tabs[i]._selected = false;
    }

    // If we're in the midst of an async tab switch while calling
    // moveTabTo, we can get into a case where _visuallySelected
    // is set to true on two different tabs.
    //
    // What we want to do in moveTabTo is to remove logical selection
    // from all tabs, and then re-add logical selection to mCurrentTab
    // (and visual selection as well if we're not running with e10s, which
    // setting _selected will do automatically).
    //
    // If we're running with e10s, then the visual selection will not
    // be changed, which is fine, since if we weren't in the midst of a
    // tab switch, the previously visually selected tab should still be
    // correct, and if we are in the midst of a tab switch, then the async
    // tab switcher will set the visually selected tab once the tab switch
    // has completed.
    this.mCurrentTab._selected = true;

    if (wasFocused)
      this.mCurrentTab.focus();

    this.tabContainer._handleTabSelect(true);

    if (aTab.pinned)
      this.tabContainer._positionPinnedTabs();

    this.tabContainer._setPositionalAttributes();

    var evt = document.createEvent("UIEvents");
    evt.initUIEvent("TabMove", true, false, window, oldPosition);
    aTab.dispatchEvent(evt);
  },

  moveTabForward() {
    let nextTab = this.mCurrentTab.nextSibling;
    while (nextTab && nextTab.hidden)
      nextTab = nextTab.nextSibling;

    if (nextTab)
      this.moveTabTo(this.mCurrentTab, nextTab._tPos);
    else if (this.arrowKeysShouldWrap)
      this.moveTabToStart();
  },

  /**
   * Adopts a tab from another browser window, and inserts it at aIndex
   */
  adoptTab(aTab, aIndex, aSelectTab) {
    // Swap the dropped tab with a new one we create and then close
    // it in the other window (making it seem to have moved between
    // windows). We also ensure that the tab we create to swap into has
    // the same remote type and process as the one we're swapping in.
    // This makes sure we don't get a short-lived process for the new tab.
    let linkedBrowser = aTab.linkedBrowser;
    let params = {
      eventDetail: { adoptedTab: aTab },
      preferredRemoteType: linkedBrowser.remoteType,
      sameProcessAsFrameLoader: linkedBrowser.frameLoader,
      skipAnimation: true
    };
    if (aTab.hasAttribute("usercontextid")) {
      // new tab must have the same usercontextid as the old one
      params.userContextId = aTab.getAttribute("usercontextid");
    }
    let newTab = this.addTab("about:blank", params);
    let newBrowser = this.getBrowserForTab(newTab);

    // Stop the about:blank load.
    newBrowser.stop();
    // Make sure it has a docshell.
    newBrowser.docShell;

    let numPinned = this._numPinnedTabs;
    if (aIndex < numPinned || (aTab.pinned && aIndex == numPinned)) {
      this.pinTab(newTab);
    }

    this.moveTabTo(newTab, aIndex);

    // We need to select the tab before calling swapBrowsersAndCloseOther
    // so that window.content in chrome windows points to the right tab
    // when pagehide/show events are fired. This is no longer necessary
    // for any exiting browser code, but it may be necessary for add-on
    // compatibility.
    if (aSelectTab) {
      this.selectedTab = newTab;
    }

    aTab.parentNode._finishAnimateTabMove();
    this.swapBrowsersAndCloseOther(newTab, aTab);

    if (aSelectTab) {
      // Call updateCurrentBrowser to make sure the URL bar is up to date
      // for our new tab after we've done swapBrowsersAndCloseOther.
      this.updateCurrentBrowser(true);
    }

    return newTab;
  },

  moveTabBackward() {
    let previousTab = this.mCurrentTab.previousSibling;
    while (previousTab && previousTab.hidden)
      previousTab = previousTab.previousSibling;

    if (previousTab)
      this.moveTabTo(this.mCurrentTab, previousTab._tPos);
    else if (this.arrowKeysShouldWrap)
      this.moveTabToEnd();
  },

  moveTabToStart() {
    var tabPos = this.mCurrentTab._tPos;
    if (tabPos > 0)
      this.moveTabTo(this.mCurrentTab, 0);
  },

  moveTabToEnd() {
    var tabPos = this.mCurrentTab._tPos;
    if (tabPos < this.browsers.length - 1)
      this.moveTabTo(this.mCurrentTab, this.browsers.length - 1);
  },

  moveTabOver(aEvent) {
    let direction = window.getComputedStyle(document.documentElement).direction;
    if ((direction == "ltr" && aEvent.keyCode == KeyEvent.DOM_VK_RIGHT) ||
        (direction == "rtl" && aEvent.keyCode == KeyEvent.DOM_VK_LEFT)) {
      this.moveTabForward();
    } else {
      this.moveTabBackward();
    }
  },

  /**
   * @param   aTab
   *          Can be from a different window as well
   * @param   aRestoreTabImmediately
   *          Can defer loading of the tab contents
   */
  duplicateTab(aTab, aRestoreTabImmediately) {
    return SessionStore.duplicateTab(window, aTab, 0, aRestoreTabImmediately);
  },

  activateBrowserForPrintPreview(aBrowser) {
    this._printPreviewBrowsers.add(aBrowser);
    if (this._switcher) {
      this._switcher.activateBrowserForPrintPreview(aBrowser);
    }
    aBrowser.docShellIsActive = true;
  },

  deactivatePrintPreviewBrowsers() {
    let browsers = this._printPreviewBrowsers;
    this._printPreviewBrowsers = new Set();
    for (let browser of browsers) {
      browser.docShellIsActive = this.shouldActivateDocShell(browser);
    }
  },

  /**
   * Returns true if a given browser's docshell should be active.
   */
  shouldActivateDocShell(aBrowser) {
    if (this._switcher) {
      return this._switcher.shouldActivateDocShell(aBrowser);
    }
    return (aBrowser == this.selectedBrowser &&
            window.windowState != window.STATE_MINIMIZED &&
            !window.isFullyOccluded) ||
            this._printPreviewBrowsers.has(aBrowser);
  },

  _getSwitcher() {
    if (!this._switcher) {
      this._switcher = new this.AsyncTabSwitcher(this);
    }
    return this._switcher;
  },

  warmupTab(aTab) {
    if (gMultiProcessBrowser) {
      this._getSwitcher().warmupTab(aTab);
    }
  },

  _handleKeyDownEvent(aEvent) {
    if (!aEvent.isTrusted) {
      // Don't let untrusted events mess with tabs.
      return;
    }

    if (aEvent.altKey)
      return;

    // Don't check if the event was already consumed because tab
    // navigation should always work for better user experience.

    if (aEvent.ctrlKey && aEvent.shiftKey && !aEvent.metaKey) {
      switch (aEvent.keyCode) {
        case aEvent.DOM_VK_PAGE_UP:
          this.moveTabBackward();
          aEvent.preventDefault();
          return;
        case aEvent.DOM_VK_PAGE_DOWN:
          this.moveTabForward();
          aEvent.preventDefault();
          return;
      }
    }

    if (AppConstants.platform != "macosx") {
      if (aEvent.ctrlKey && !aEvent.shiftKey && !aEvent.metaKey &&
          aEvent.keyCode == KeyEvent.DOM_VK_F4 &&
          !this.mCurrentTab.pinned) {
        this.removeCurrentTab({ animate: true });
        aEvent.preventDefault();
      }
    }
  },

  _handleKeyPressEventMac(aEvent) {
    if (!aEvent.isTrusted) {
      // Don't let untrusted events mess with tabs.
      return;
    }

    if (aEvent.altKey)
      return;

    if (AppConstants.platform == "macosx") {
      if (!aEvent.metaKey)
        return;

      var offset = 1;
      switch (aEvent.charCode) {
        case "}".charCodeAt(0):
          offset = -1;
        case "{".charCodeAt(0):
          if (window.getComputedStyle(document.documentElement).direction == "ltr")
            offset *= -1;
          this.tabContainer.advanceSelectedTab(offset, true);
          aEvent.preventDefault();
      }
    }
  },

  createTooltip(event) {
    event.stopPropagation();
    var tab = document.tooltipNode;
    if (tab.localName != "tab") {
      event.preventDefault();
      return;
    }

    let stringWithShortcut = (stringId, keyElemId) => {
      let keyElem = document.getElementById(keyElemId);
      let shortcut = ShortcutUtils.prettifyShortcut(keyElem);
      return gTabBrowserBundle.formatStringFromName(stringId, [shortcut], 1);
    };

    var label;
    if (tab.mOverCloseButton) {
      label = tab.selected ?
        stringWithShortcut("tabs.closeSelectedTab.tooltip", "key_close") :
        gTabBrowserBundle.GetStringFromName("tabs.closeTab.tooltip");
    } else if (tab._overPlayingIcon) {
      let stringID;
      if (tab.selected) {
        stringID = tab.linkedBrowser.audioMuted ?
          "tabs.unmuteAudio.tooltip" :
          "tabs.muteAudio.tooltip";
        label = stringWithShortcut(stringID, "key_toggleMute");
      } else {
        if (tab.hasAttribute("activemedia-blocked")) {
          stringID = "tabs.unblockAudio.tooltip";
        } else {
          stringID = tab.linkedBrowser.audioMuted ?
            "tabs.unmuteAudio.background.tooltip" :
            "tabs.muteAudio.background.tooltip";
        }

        label = gTabBrowserBundle.GetStringFromName(stringID);
      }
    } else {
      label = tab._fullLabel || tab.getAttribute("label");
      if (AppConstants.NIGHTLY_BUILD &&
          tab.linkedBrowser &&
          tab.linkedBrowser.isRemoteBrowser &&
          tab.linkedBrowser.frameLoader) {
        label += " (pid " + tab.linkedBrowser.frameLoader.tabParent.osPid + ")";
      }
      if (tab.userContextId) {
        label = gTabBrowserBundle.formatStringFromName("tabs.containers.tooltip", [label, ContextualIdentityService.getUserContextLabel(tab.userContextId)], 2);
      }
    }

    event.target.setAttribute("label", label);
  },

  handleEvent(aEvent) {
    switch (aEvent.type) {
      case "keydown":
        this._handleKeyDownEvent(aEvent);
        break;
      case "keypress":
        this._handleKeyPressEventMac(aEvent);
        break;
      case "sizemodechange":
      case "occlusionstatechange":
        if (aEvent.target == window && !this._switcher) {
          this.mCurrentBrowser.preserveLayers(
            window.windowState == window.STATE_MINIMIZED || window.isFullyOccluded);
          this.mCurrentBrowser.docShellIsActive = this.shouldActivateDocShell(this.mCurrentBrowser);
        }
        break;
    }
  },

  receiveMessage(aMessage) {
    let data = aMessage.data;
    let browser = aMessage.target;

    switch (aMessage.name) {
      case "DOMTitleChanged":
      {
        let tab = this.getTabForBrowser(browser);
        if (!tab || tab.hasAttribute("pending"))
          return undefined;
        let titleChanged = this.setTabTitle(tab);
        if (titleChanged && !tab.selected && !tab.hasAttribute("busy"))
          tab.setAttribute("titlechanged", "true");
        break;
      }
      case "DOMWindowClose":
      {
        if (this.tabs.length == 1) {
          // We already did PermitUnload in the content process
          // for this tab (the only one in the window). So we don't
          // need to do it again for any tabs.
          window.skipNextCanClose = true;
          window.close();
          return undefined;
        }

        let tab = this.getTabForBrowser(browser);
        if (tab) {
          // Skip running PermitUnload since it already happened in
          // the content process.
          this.removeTab(tab, { skipPermitUnload: true });
        }
        break;
      }
      case "contextmenu":
      {
        openContextMenu(aMessage);
        break;
      }
      case "DOMWindowFocus":
      {
        let tab = this.getTabForBrowser(browser);
        if (!tab)
          return undefined;
        this.selectedTab = tab;
        window.focus();
        break;
      }
      case "Browser:Init":
      {
        let tab = this.getTabForBrowser(browser);
        if (!tab)
          return undefined;

        this._outerWindowIDBrowserMap.set(browser.outerWindowID, browser);
        browser.messageManager.sendAsyncMessage("Browser:AppTab", { isAppTab: tab.pinned });
        break;
      }
      case "Browser:WindowCreated":
      {
        let tab = this.getTabForBrowser(browser);
        if (tab && data.userContextId) {
          ContextualIdentityService.telemetry(data.userContextId);
          tab.setUserContextId(data.userContextId);
        }

        // We don't want to update the container icon and identifier if
        // this is not the selected browser.
        if (browser == gBrowser.selectedBrowser) {
          updateUserContextUIIndicator();
        }

        break;
      }
      case "Findbar:Keypress":
      {
        let tab = this.getTabForBrowser(browser);
        // If the find bar for this tab is not yet alive, only initialize
        // it if there's a possibility FindAsYouType will be used.
        // There's no point in doing it for most random keypresses.
        if (!this.isFindBarInitialized(tab) &&
            data.shouldFastFind) {
          let shouldFastFind = this._findAsYouType;
          if (!shouldFastFind) {
            // Please keep in sync with toolkit/content/widgets/findbar.xml
            const FAYT_LINKS_KEY = "'";
            const FAYT_TEXT_KEY = "/";
            let charCode = data.fakeEvent.charCode;
            let key = charCode ? String.fromCharCode(charCode) : null;
            shouldFastFind = key == FAYT_LINKS_KEY || key == FAYT_TEXT_KEY;
          }
          if (shouldFastFind) {
            // Make sure we return the result.
            return this.getFindBar(tab).receiveMessage(aMessage);
          }
        }
        break;
      }
      case "RefreshBlocker:Blocked":
      {
        // The data object is expected to contain the following properties:
        //  - URI (string)
        //     The URI that a page is attempting to refresh or redirect to.
        //  - delay (int)
        //     The delay (in milliseconds) before the page was going to
        //     reload or redirect.
        //  - sameURI (bool)
        //     true if we're refreshing the page. false if we're redirecting.
        //  - outerWindowID (int)
        //     The outerWindowID of the frame that requested the refresh or
        //     redirect.

        let brandBundle = document.getElementById("bundle_brand");
        let brandShortName = brandBundle.getString("brandShortName");
        let message =
          gNavigatorBundle.getFormattedString("refreshBlocked." +
                                              (data.sameURI ? "refreshLabel"
                                                            : "redirectLabel"),
                                              [brandShortName]);

        let notificationBox = this.getNotificationBox(browser);
        let notification = notificationBox.getNotificationWithValue("refresh-blocked");

        if (notification) {
          notification.label = message;
        } else {
          let refreshButtonText =
            gNavigatorBundle.getString("refreshBlocked.goButton");
          let refreshButtonAccesskey =
            gNavigatorBundle.getString("refreshBlocked.goButton.accesskey");

          let buttons = [{
            label: refreshButtonText,
            accessKey: refreshButtonAccesskey,
            callback() {
              if (browser.messageManager) {
                browser.messageManager.sendAsyncMessage("RefreshBlocker:Refresh", data);
              }
            }
          }];

          notificationBox.appendNotification(message, "refresh-blocked",
            "chrome://browser/skin/notification-icons/popup.svg",
            notificationBox.PRIORITY_INFO_MEDIUM,
            buttons);
        }
        break;
      }
    }
    return undefined;
  },

  observe(aSubject, aTopic, aData) {
    switch (aTopic) {
      case "contextual-identity-updated":
      {
        for (let tab of this.tabs) {
          if (tab.getAttribute("usercontextid") == aData) {
            ContextualIdentityService.setTabStyle(tab);
          }
        }
        break;
      }
      case "nsPref:changed":
      {
        // This is the only pref observed.
        this._findAsYouType = Services.prefs.getBoolPref("accessibility.typeaheadfind");
        break;
      }
    }
  },

  _generateUniquePanelID() {
    if (!this._uniquePanelIDCounter) {
      this._uniquePanelIDCounter = 0;
    }

    let outerID = window.QueryInterface(Ci.nsIInterfaceRequestor)
                        .getInterface(Ci.nsIDOMWindowUtils)
                        .outerWindowID;

    // We want panel IDs to be globally unique, that's why we include the
    // window ID. We switched to a monotonic counter as Date.now() lead
    // to random failures because of colliding IDs.
    return "panel-" + outerID + "-" + (++this._uniquePanelIDCounter);
  },

  destroy() {
    Services.obs.removeObserver(this, "contextual-identity-updated");

    for (let tab of this.tabs) {
      let browser = tab.linkedBrowser;
      if (browser.registeredOpenURI) {
        this._unifiedComplete.unregisterOpenPage(browser.registeredOpenURI,
          browser.getAttribute("usercontextid") || 0);
        delete browser.registeredOpenURI;
      }

      let filter = this._tabFilters.get(tab);
      if (filter) {
        browser.webProgress.removeProgressListener(filter);

        let listener = this._tabListeners.get(tab);
        if (listener) {
          filter.removeProgressListener(listener);
          listener.destroy();
        }

        this._tabFilters.delete(tab);
        this._tabListeners.delete(tab);
      }
    }
    const nsIEventListenerService =
      Ci.nsIEventListenerService;
    let els = Cc["@mozilla.org/eventlistenerservice;1"]
      .getService(nsIEventListenerService);
    els.removeSystemEventListener(document, "keydown", this, false);
    if (AppConstants.platform == "macosx") {
      els.removeSystemEventListener(document, "keypress", this, false);
    }
    window.removeEventListener("sizemodechange", this);
    window.removeEventListener("occlusionstatechange", this);

    if (gMultiProcessBrowser) {
      let messageManager = window.getGroupMessageManager("browsers");
      messageManager.removeMessageListener("DOMTitleChanged", this);
      window.messageManager.removeMessageListener("contextmenu", this);

      if (this._switcher) {
        this._switcher.destroy();
      }
    }

    Services.prefs.removeObserver("accessibility.typeaheadfind", this);
  },

  _setupEventListeners() {
    this.addEventListener("DOMWindowClose", (event) => {
      if (!event.isTrusted)
        return;

      if (this.tabs.length == 1) {
        // We already did PermitUnload in nsGlobalWindow::Close
        // for this tab. There are no other tabs we need to do
        // PermitUnload for.
        window.skipNextCanClose = true;
        return;
      }

      var tab = this._getTabForContentWindow(event.target);
      if (tab) {
        // Skip running PermitUnload since it already happened.
        this.removeTab(tab, { skipPermitUnload: true });
        event.preventDefault();
      }
    }, true);

    this.addEventListener("DOMWillOpenModalDialog", (event) => {
      if (!event.isTrusted)
        return;

      let targetIsWindow = event.target instanceof Window;

      // We're about to open a modal dialog, so figure out for which tab:
      // If this is a same-process modal dialog, then we're given its DOM
      // window as the event's target. For remote dialogs, we're given the
      // browser, but that's in the originalTarget and not the target,
      // because it's across the tabbrowser's XBL boundary.
      let tabForEvent = targetIsWindow ?
        this._getTabForContentWindow(event.target.top) :
        this.getTabForBrowser(event.originalTarget);

      // Focus window for beforeunload dialog so it is seen but don't
      // steal focus from other applications.
      if (event.detail &&
          event.detail.tabPrompt &&
          event.detail.inPermitUnload &&
          Services.focus.activeWindow)
        window.focus();

      // Don't need to act if the tab is already selected or if there isn't
      // a tab for the event (e.g. for the webextensions options_ui remote
      // browsers embedded in the "about:addons" page):
      if (!tabForEvent || tabForEvent.selected)
        return;

      // We always switch tabs for beforeunload tab-modal prompts.
      if (event.detail &&
          event.detail.tabPrompt &&
          !event.detail.inPermitUnload) {
        let docPrincipal = targetIsWindow ? event.target.document.nodePrincipal : null;
        // At least one of these should/will be non-null:
        let promptPrincipal = event.detail.promptPrincipal || docPrincipal ||
          tabForEvent.linkedBrowser.contentPrincipal;
        // For null principals, we bail immediately and don't show the checkbox:
        if (!promptPrincipal || promptPrincipal.isNullPrincipal) {
          tabForEvent.setAttribute("attention", "true");
          return;
        }

        // For non-system/expanded principals, we bail and show the checkbox
        if (promptPrincipal.URI &&
            !Services.scriptSecurityManager.isSystemPrincipal(promptPrincipal)) {
          let permission = Services.perms.testPermissionFromPrincipal(promptPrincipal,
            "focus-tab-by-prompt");
          if (permission != Services.perms.ALLOW_ACTION) {
            // Tell the prompt box we want to show the user a checkbox:
            let tabPrompt = this.getTabModalPromptBox(tabForEvent.linkedBrowser);
            tabPrompt.onNextPromptShowAllowFocusCheckboxFor(promptPrincipal);
            tabForEvent.setAttribute("attention", "true");
            return;
          }
        }
        // ... so system and expanded principals, as well as permitted "normal"
        // URI-based principals, always get to steal focus for the tab when prompting.
      }

      // If permissions/origins dictate so, bring tab to the front.
      this.selectedTab = tabForEvent;
    }, true);

    this.addEventListener("DOMTitleChanged", (event) => {
      if (!event.isTrusted)
        return;

      var contentWin = event.target.defaultView;
      if (contentWin != contentWin.top)
        return;

      var tab = this._getTabForContentWindow(contentWin);
      if (!tab || tab.hasAttribute("pending"))
        return;

      var titleChanged = this.setTabTitle(tab);
      if (titleChanged && !tab.selected && !tab.hasAttribute("busy"))
        tab.setAttribute("titlechanged", "true");
    });

    this.addEventListener("oop-browser-crashed", (event) => {
      if (!event.isTrusted)
        return;

      let browser = event.originalTarget;

      // Preloaded browsers do not actually have any tabs. If one crashes,
      // it should be released and removed.
      if (browser === this._preloadedBrowser) {
        this.removePreloadedBrowser();
        return;
      }

      let icon = browser.mIconURL;
      let tab = this.getTabForBrowser(browser);

      if (this.selectedBrowser == browser) {
        TabCrashHandler.onSelectedBrowserCrash(browser);
      } else {
        this.updateBrowserRemoteness(browser, false);
        SessionStore.reviveCrashedTab(tab);
      }

      tab.removeAttribute("soundplaying");
      this.setIcon(tab, icon, browser.contentPrincipal, browser.contentRequestContextID);
    });

    this.addEventListener("DOMAudioPlaybackStarted", (event) => {
      var tab = this.getTabFromAudioEvent(event);
      if (!tab) {
        return;
      }

      clearTimeout(tab._soundPlayingAttrRemovalTimer);
      tab._soundPlayingAttrRemovalTimer = 0;

      let modifiedAttrs = [];
      if (tab.hasAttribute("soundplaying-scheduledremoval")) {
        tab.removeAttribute("soundplaying-scheduledremoval");
        modifiedAttrs.push("soundplaying-scheduledremoval");
      }

      if (!tab.hasAttribute("soundplaying")) {
        tab.setAttribute("soundplaying", true);
        modifiedAttrs.push("soundplaying");
      }

      if (modifiedAttrs.length) {
        // Flush style so that the opacity takes effect immediately, in
        // case the media is stopped before the style flushes naturally.
        getComputedStyle(tab).opacity;
      }

      this._tabAttrModified(tab, modifiedAttrs);
    });

    this.addEventListener("DOMAudioPlaybackStopped", (event) => {
      var tab = this.getTabFromAudioEvent(event);
      if (!tab) {
        return;
      }

      if (tab.hasAttribute("soundplaying")) {
        let removalDelay = Services.prefs.getIntPref("browser.tabs.delayHidingAudioPlayingIconMS");

        tab.style.setProperty("--soundplaying-removal-delay", `${removalDelay - 300}ms`);
        tab.setAttribute("soundplaying-scheduledremoval", "true");
        this._tabAttrModified(tab, ["soundplaying-scheduledremoval"]);

        tab._soundPlayingAttrRemovalTimer = setTimeout(() => {
          tab.removeAttribute("soundplaying-scheduledremoval");
          tab.removeAttribute("soundplaying");
          this._tabAttrModified(tab, ["soundplaying", "soundplaying-scheduledremoval"]);
        }, removalDelay);
      }
    });

    this.addEventListener("DOMAudioPlaybackBlockStarted", (event) => {
      var tab = this.getTabFromAudioEvent(event);
      if (!tab) {
        return;
      }

      if (!tab.hasAttribute("activemedia-blocked")) {
        tab.setAttribute("activemedia-blocked", true);
        this._tabAttrModified(tab, ["activemedia-blocked"]);
        tab.startMediaBlockTimer();
      }
    });

    this.addEventListener("DOMAudioPlaybackBlockStopped", (event) => {
      var tab = this.getTabFromAudioEvent(event);
      if (!tab) {
        return;
      }

      if (tab.hasAttribute("activemedia-blocked")) {
        tab.removeAttribute("activemedia-blocked");
        this._tabAttrModified(tab, ["activemedia-blocked"]);
        let hist = Services.telemetry.getHistogramById("TAB_AUDIO_INDICATOR_USED");
        hist.add(2 /* unblockByVisitingTab */ );
        tab.finishMediaBlockTimer();
      }
    });
  },
};

/**
 * A web progress listener object definition for a given tab.
 */
class TabProgressListener {
  constructor(aTab, aBrowser, aStartsBlank, aWasPreloadedBrowser, aOrigStateFlags) {
    let stateFlags = aOrigStateFlags || 0;
    // Initialize mStateFlags to non-zero e.g. when creating a progress
    // listener for preloaded browsers as there was no progress listener
    // around when the content started loading. If the content didn't
    // quite finish loading yet, mStateFlags will very soon be overridden
    // with the correct value and end up at STATE_STOP again.
    if (aWasPreloadedBrowser) {
      stateFlags = Ci.nsIWebProgressListener.STATE_STOP |
        Ci.nsIWebProgressListener.STATE_IS_REQUEST;
    }

    this.mTab = aTab;
    this.mBrowser = aBrowser;
    this.mBlank = aStartsBlank;

    // cache flags for correct status UI update after tab switching
    this.mStateFlags = stateFlags;
    this.mStatus = 0;
    this.mMessage = "";
    this.mTotalProgress = 0;

    // count of open requests (should always be 0 or 1)
    this.mRequestCount = 0;
  }

  destroy() {
    delete this.mTab;
    delete this.mBrowser;
  }

  _callProgressListeners() {
    Array.unshift(arguments, this.mBrowser);
    return gBrowser._callProgressListeners.apply(gBrowser, arguments);
  }

  _shouldShowProgress(aRequest) {
    if (this.mBlank)
      return false;

    // Don't show progress indicators in tabs for about: URIs
    // pointing to local resources.
    if ((aRequest instanceof Ci.nsIChannel) &&
        gBrowser._isLocalAboutURI(aRequest.originalURI, aRequest.URI)) {
      return false;
    }

    return true;
  }

  _isForInitialAboutBlank(aWebProgress, aStateFlags, aLocation) {
    if (!this.mBlank || !aWebProgress.isTopLevel) {
      return false;
    }

    // If the state has STATE_STOP, and no requests were in flight, then this
    // must be the initial "stop" for the initial about:blank document.
    const nsIWebProgressListener = Ci.nsIWebProgressListener;
    if (aStateFlags & nsIWebProgressListener.STATE_STOP &&
        this.mRequestCount == 0 &&
        !aLocation) {
      return true;
    }

    let location = aLocation ? aLocation.spec : "";
    return location == "about:blank";
  }

  onProgressChange(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress,
                   aCurTotalProgress, aMaxTotalProgress) {
    this.mTotalProgress = aMaxTotalProgress ? aCurTotalProgress / aMaxTotalProgress : 0;

    if (!this._shouldShowProgress(aRequest))
      return;

    if (this.mTotalProgress && this.mTab.hasAttribute("busy"))
      this.mTab.setAttribute("progress", "true");

    this._callProgressListeners("onProgressChange",
                                [aWebProgress, aRequest,
                                 aCurSelfProgress, aMaxSelfProgress,
                                 aCurTotalProgress, aMaxTotalProgress]);
  }

  onProgressChange64(aWebProgress, aRequest, aCurSelfProgress,
                     aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress) {
    return this.onProgressChange(aWebProgress, aRequest,
      aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress,
      aMaxTotalProgress);
  }

  /* eslint-disable complexity */
  onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
    if (!aRequest)
      return;

    const nsIWebProgressListener = Ci.nsIWebProgressListener;
    const nsIChannel = Ci.nsIChannel;
    let location, originalLocation;
    try {
      aRequest.QueryInterface(nsIChannel);
      location = aRequest.URI;
      originalLocation = aRequest.originalURI;
    } catch (ex) {}

    let ignoreBlank = this._isForInitialAboutBlank(aWebProgress, aStateFlags,
      location);

    // If we were ignoring some messages about the initial about:blank, and we
    // got the STATE_STOP for it, we'll want to pay attention to those messages
    // from here forward. Similarly, if we conclude that this state change
    // is one that we shouldn't be ignoring, then stop ignoring.
    if ((ignoreBlank &&
        aStateFlags & nsIWebProgressListener.STATE_STOP &&
        aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) ||
        !ignoreBlank && this.mBlank) {
      this.mBlank = false;
    }

    if (aStateFlags & nsIWebProgressListener.STATE_START) {
      this.mRequestCount++;
    } else if (aStateFlags & nsIWebProgressListener.STATE_STOP) {
      const NS_ERROR_UNKNOWN_HOST = 2152398878;
      if (--this.mRequestCount > 0 && aStatus == NS_ERROR_UNKNOWN_HOST) {
        // to prevent bug 235825: wait for the request handled
        // by the automatic keyword resolver
        return;
      }
      // since we (try to) only handle STATE_STOP of the last request,
      // the count of open requests should now be 0
      this.mRequestCount = 0;
    }

    if (aStateFlags & nsIWebProgressListener.STATE_START &&
        aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
      if (aWebProgress.isTopLevel) {
        // Need to use originalLocation rather than location because things
        // like about:home and about:privatebrowsing arrive with nsIRequest
        // pointing to their resolved jar: or file: URIs.
        if (!(originalLocation && gInitialPages.includes(originalLocation.spec) &&
            originalLocation != "about:blank" &&
            this.mBrowser.initialPageLoadedFromURLBar != originalLocation.spec &&
            this.mBrowser.currentURI && this.mBrowser.currentURI.spec == "about:blank")) {
          // Indicating that we started a load will allow the location
          // bar to be cleared when the load finishes.
          // In order to not overwrite user-typed content, we avoid it
          // (see if condition above) in a very specific case:
          // If the load is of an 'initial' page (e.g. about:privatebrowsing,
          // about:newtab, etc.), was not explicitly typed in the location
          // bar by the user, is not about:blank (because about:blank can be
          // loaded by websites under their principal), and the current
          // page in the browser is about:blank (indicating it is a newly
          // created or re-created browser, e.g. because it just switched
          // remoteness or is a new tab/window).
          this.mBrowser.urlbarChangeTracker.startedLoad();
        }
        delete this.mBrowser.initialPageLoadedFromURLBar;
        // If the browser is loading it must not be crashed anymore
        this.mTab.removeAttribute("crashed");
      }

      if (this._shouldShowProgress(aRequest)) {
        if (!(aStateFlags & nsIWebProgressListener.STATE_RESTORING) &&
            aWebProgress && aWebProgress.isTopLevel) {
          this.mTab.setAttribute("busy", "true");
          this.mTab._notselectedsinceload = !this.mTab.selected;
          SchedulePressure.startMonitoring(window, {
            highPressureFn() {
              // Only switch back to the SVG loading indicator after getting
              // three consecutive low pressure callbacks. Used to prevent
              // switching quickly between the SVG and APNG loading indicators.
              gBrowser.tabContainer._schedulePressureCount = gBrowser.schedulePressureDefaultCount;
              gBrowser.tabContainer.setAttribute("schedulepressure", "true");
            },
            lowPressureFn() {
              if (!gBrowser.tabContainer._schedulePressureCount ||
                --gBrowser.tabContainer._schedulePressureCount <= 0) {
                gBrowser.tabContainer.removeAttribute("schedulepressure");
              }

              // If tabs are closed while they are loading we need to
              // stop monitoring schedule pressure. We don't stop monitoring
              // during high pressure times because we want to eventually
              // return to the SVG tab loading animations.
              let continueMonitoring = true;
              if (!document.querySelector(".tabbrowser-tab[busy]")) {
                SchedulePressure.stopMonitoring(window);
                continueMonitoring = false;
              }
              return { continueMonitoring };
            },
          });
          gBrowser.syncThrobberAnimations(this.mTab);
        }

        if (this.mTab.selected) {
          gBrowser.mIsBusy = true;
        }
      }
    } else if (aStateFlags & nsIWebProgressListener.STATE_STOP &&
               aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {

      if (this.mTab.hasAttribute("busy")) {
        this.mTab.removeAttribute("busy");
        if (!document.querySelector(".tabbrowser-tab[busy]")) {
          SchedulePressure.stopMonitoring(window);
          gBrowser.tabContainer.removeAttribute("schedulepressure");
        }

        // Only animate the "burst" indicating the page has loaded if
        // the top-level page is the one that finished loading.
        if (aWebProgress.isTopLevel && !aWebProgress.isLoadingDocument &&
            Components.isSuccessCode(aStatus) &&
            !gBrowser.tabAnimationsInProgress &&
            Services.prefs.getBoolPref("toolkit.cosmeticAnimations.enabled")) {
          if (this.mTab._notselectedsinceload) {
            this.mTab.setAttribute("notselectedsinceload", "true");
          } else {
            this.mTab.removeAttribute("notselectedsinceload");
          }

          this.mTab.setAttribute("bursting", "true");
        }

        gBrowser._tabAttrModified(this.mTab, ["busy"]);
        if (!this.mTab.selected)
          this.mTab.setAttribute("unread", "true");
      }
      this.mTab.removeAttribute("progress");

      if (aWebProgress.isTopLevel) {
        let isSuccessful = Components.isSuccessCode(aStatus);
        if (!isSuccessful && !isTabEmpty(this.mTab)) {
          // Restore the current document's location in case the
          // request was stopped (possibly from a content script)
          // before the location changed.

          this.mBrowser.userTypedValue = null;

          let inLoadURI = this.mBrowser.inLoadURI;
          if (this.mTab.selected && gURLBar && !inLoadURI) {
            URLBarSetURI();
          }
        } else if (isSuccessful) {
          this.mBrowser.urlbarChangeTracker.finishedLoad();
        }

        // Ignore initial about:blank to prevent flickering.
        if (!this.mBrowser.mIconURL && !ignoreBlank) {
          // Don't switch to the default icon on about:home or about:newtab,
          // since these pages get their favicon set in browser code to
          // improve perceived performance.
          let isNewTab = originalLocation &&
            (originalLocation.spec == "about:newtab" ||
              originalLocation.spec == "about:privatebrowsing" ||
              originalLocation.spec == "about:home");
          if (!isNewTab) {
            gBrowser.useDefaultIcon(this.mTab);
          }
        }
      }

      // For keyword URIs clear the user typed value since they will be changed into real URIs
      if (location.scheme == "keyword")
        this.mBrowser.userTypedValue = null;

      if (this.mTab.selected)
        gBrowser.mIsBusy = false;
    }

    if (ignoreBlank) {
      this._callProgressListeners("onUpdateCurrentBrowser",
                                  [aStateFlags, aStatus, "", 0],
                                  true, false);
    } else {
      this._callProgressListeners("onStateChange",
                                  [aWebProgress, aRequest, aStateFlags, aStatus],
                                  true, false);
    }

    this._callProgressListeners("onStateChange",
                                [aWebProgress, aRequest, aStateFlags, aStatus],
                                false);

    if (aStateFlags & (nsIWebProgressListener.STATE_START |
        nsIWebProgressListener.STATE_STOP)) {
      // reset cached temporary values at beginning and end
      this.mMessage = "";
      this.mTotalProgress = 0;
    }
    this.mStateFlags = aStateFlags;
    this.mStatus = aStatus;
  }
  /* eslint-enable complexity */

  onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {
    // OnLocationChange is called for both the top-level content
    // and the subframes.
    let topLevel = aWebProgress.isTopLevel;

    if (topLevel) {
      let isSameDocument = !!(aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT);
      // We need to clear the typed value
      // if the document failed to load, to make sure the urlbar reflects the
      // failed URI (particularly for SSL errors). However, don't clear the value
      // if the error page's URI is about:blank, because that causes complete
      // loss of urlbar contents for invalid URI errors (see bug 867957).
      // Another reason to clear the userTypedValue is if this was an anchor
      // navigation initiated by the user.
      if (this.mBrowser.didStartLoadSinceLastUserTyping() ||
          ((aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE) &&
            aLocation.spec != "about:blank") ||
          (isSameDocument && this.mBrowser.inLoadURI)) {
        this.mBrowser.userTypedValue = null;
      }

      // If the tab has been set to "busy" outside the stateChange
      // handler below (e.g. by sessionStore.navigateAndRestore), and
      // the load results in an error page, it's possible that there
      // isn't any (STATE_IS_NETWORK & STATE_STOP) state to cause busy
      // attribute being removed. In this case we should remove the
      // attribute here.
      if ((aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE) &&
          this.mTab.hasAttribute("busy")) {
        this.mTab.removeAttribute("busy");
        gBrowser._tabAttrModified(this.mTab, ["busy"]);
      }

      // If the browser was playing audio, we should remove the playing state.
      if (this.mTab.hasAttribute("soundplaying") && !isSameDocument) {
        clearTimeout(this.mTab._soundPlayingAttrRemovalTimer);
        this.mTab._soundPlayingAttrRemovalTimer = 0;
        this.mTab.removeAttribute("soundplaying");
        gBrowser._tabAttrModified(this.mTab, ["soundplaying"]);
      }

      // If the browser was previously muted, we should restore the muted state.
      if (this.mTab.hasAttribute("muted")) {
        this.mTab.linkedBrowser.mute();
      }

      if (gBrowser.isFindBarInitialized(this.mTab)) {
        let findBar = gBrowser.getFindBar(this.mTab);

        // Close the Find toolbar if we're in old-style TAF mode
        if (findBar.findMode != findBar.FIND_NORMAL) {
          findBar.close();
        }
      }

      gBrowser.setTabTitle(this.mTab);

      // Don't clear the favicon if this tab is in the pending
      // state, as SessionStore will have set the icon for us even
      // though we're pointed at an about:blank. Also don't clear it
      // if onLocationChange was triggered by a pushState or a
      // replaceState (bug 550565) or a hash change (bug 408415).
      if (!this.mTab.hasAttribute("pending") &&
          aWebProgress.isLoadingDocument &&
          !isSameDocument) {
        this.mBrowser.mIconURL = null;
      }

      let userContextId = this.mBrowser.getAttribute("usercontextid") || 0;
      if (this.mBrowser.registeredOpenURI) {
        gBrowser._unifiedComplete
          .unregisterOpenPage(this.mBrowser.registeredOpenURI, userContextId);
        delete this.mBrowser.registeredOpenURI;
      }
      // Tabs in private windows aren't registered as "Open" so
      // that they don't appear as switch-to-tab candidates.
      if (!isBlankPageURL(aLocation.spec) &&
          (!PrivateBrowsingUtils.isWindowPrivate(window) ||
            PrivateBrowsingUtils.permanentPrivateBrowsing)) {
        gBrowser._unifiedComplete.registerOpenPage(aLocation, userContextId);
        this.mBrowser.registeredOpenURI = aLocation;
      }
    }

    if (!this.mBlank) {
      this._callProgressListeners("onLocationChange",
                                  [aWebProgress, aRequest, aLocation, aFlags]);
    }

    if (topLevel) {
      this.mBrowser.lastURI = aLocation;
      this.mBrowser.lastLocationChange = Date.now();
    }
  }

  onStatusChange(aWebProgress, aRequest, aStatus, aMessage) {
    if (this.mBlank)
      return;

    this._callProgressListeners("onStatusChange",
                                [aWebProgress, aRequest, aStatus, aMessage]);

    this.mMessage = aMessage;
  }

  onSecurityChange(aWebProgress, aRequest, aState) {
    this._callProgressListeners("onSecurityChange",
                                [aWebProgress, aRequest, aState]);
  }

  onRefreshAttempted(aWebProgress, aURI, aDelay, aSameURI) {
    return this._callProgressListeners("onRefreshAttempted",
                                       [aWebProgress, aURI, aDelay, aSameURI]);
  }

  QueryInterface(aIID) {
    if (aIID.equals(Ci.nsIWebProgressListener) ||
        aIID.equals(Ci.nsIWebProgressListener2) ||
        aIID.equals(Ci.nsISupportsWeakReference) ||
        aIID.equals(Ci.nsISupports))
      return this;
    throw Cr.NS_NOINTERFACE;
  }
}

PK
!<y�||i|i-chrome/browser/content/browser/tabbrowser.xml<?xml version="1.0"?>

<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<bindings id="tabBrowserBindings"
          xmlns="http://www.mozilla.org/xbl"
          xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
          xmlns:xbl="http://www.mozilla.org/xbl">

  <binding id="tabbrowser-arrowscrollbox" extends="chrome://global/content/bindings/scrollbox.xml#arrowscrollbox-clicktoscroll">
    <implementation>
      <!-- Override scrollbox.xml method, since our scrollbox's children are
           inherited from the binding parent -->
      <method name="_getScrollableElements">
        <body><![CDATA[
          return Array.filter(document.getBindingParent(this).childNodes,
                              this._canScrollToElement, this);
        ]]></body>
      </method>
      <method name="_canScrollToElement">
        <parameter name="tab"/>
        <body><![CDATA[
          return !tab._pinnedUnscrollable && !tab.hidden;
        ]]></body>
      </method>
    </implementation>

    <handlers>
      <handler event="underflow" phase="capturing"><![CDATA[
        // Ignore underflow events:
        // - from nested scrollable elements
        // - for vertical orientation
        // - corresponding to an overflow event that we ignored
        let tabs = document.getBindingParent(this);
        if (event.originalTarget != this._scrollbox ||
            event.detail == 0 ||
            !tabs.hasAttribute("overflow")) {
          return;
        }

        tabs.removeAttribute("overflow");

        if (tabs._lastTabClosedByMouse) {
          tabs._expandSpacerBy(this._scrollButtonDown.clientWidth);
        }

        for (let tab of Array.from(gBrowser._removingTabs)) {
          gBrowser.removeTab(tab);
        }

        tabs._positionPinnedTabs();
      ]]></handler>
      <handler event="overflow"><![CDATA[
        // Ignore overflow events:
        // - from nested scrollable elements
        // - for vertical orientation
        // - when the window is tiny initially
        if (event.originalTarget != this._scrollbox ||
            event.detail == 0 ||
            window.outerWidth <= 1) {
          return;
        }

        var tabs = document.getBindingParent(this);
        tabs.setAttribute("overflow", "true");
        tabs._positionPinnedTabs();
        tabs._handleTabSelect(true);
      ]]></handler>
    </handlers>
  </binding>

  <binding id="tabbrowser-tabs"
           extends="chrome://global/content/bindings/tabbox.xml#tabs">
    <content>
      <xul:hbox class="tab-drop-indicator-box">
        <xul:image class="tab-drop-indicator" anonid="tab-drop-indicator" collapsed="true"/>
      </xul:hbox>
      <xul:arrowscrollbox anonid="arrowscrollbox" orient="horizontal" flex="1"
                          style="min-width: 1px;"
                          class="tabbrowser-arrowscrollbox">
<!--
 This is a hack to circumvent bug 472020, otherwise the tabs show up on the
 right of the newtab button.
-->
        <children includes="tab"/>
<!--
  This is to ensure anything extensions put here will go before the newtab
  button, necessary due to the previous hack.
-->
        <children/>
        <xul:toolbarbutton class="tabs-newtab-button toolbarbutton-1"
                           anonid="tabs-newtab-button"
                           command="cmd_newNavigatorTab"
                           onclick="checkForMiddleClick(this, event);"
                           tooltip="dynamic-shortcut-tooltip"/>
        <xul:hbox class="restore-tabs-button-wrapper"
                  anonid="restore-tabs-button-wrapper">
          <xul:toolbarbutton anonid="restore-tabs-button"
                             class="restore-tabs-button"
                             onclick="SessionStore.restoreLastSession();"/>
        </xul:hbox>

        <xul:spacer class="closing-tabs-spacer" anonid="closing-tabs-spacer"
                    style="width: 0;"/>
      </xul:arrowscrollbox>
    </content>

    <implementation implements="nsIDOMEventListener, nsIObserver">
      <constructor>
        <![CDATA[
          this._tabClipWidth = Services.prefs.getIntPref("browser.tabs.tabClipWidth");

          let { restoreTabsButton } = this;
          restoreTabsButton.setAttribute("label", gTabBrowserBundle.GetStringFromName("tabs.restoreLastTabs"));

          var tab = this.firstChild;
          tab.label = gTabBrowserBundle.GetStringFromName("tabs.emptyTabTitle");
          tab.setAttribute("onerror", "this.removeAttribute('image');");

          window.addEventListener("resize", this);
          window.addEventListener("DOMContentLoaded", this);

          Services.prefs.addObserver("privacy.userContext", this);
          this.observe(null, "nsPref:changed", "privacy.userContext.enabled");

          XPCOMUtils.defineLazyPreferenceGetter(this, "_tabMinWidthPref",
            "browser.tabs.tabMinWidth", null,
            (pref, prevValue, newValue) => this._tabMinWidth = newValue,
            newValue => {
              const LIMIT = 50;
              return Math.max(newValue, LIMIT);
            },
          );

          this._tabMinWidth = this._tabMinWidthPref;

          this._setPositionalAttributes();

          CustomizableUI.addListener(this);
          this._updateNewTabVisibility();
        ]]>
      </constructor>

      <destructor>
        <![CDATA[
          Services.prefs.removeObserver("privacy.userContext", this);

          CustomizableUI.removeListener(this);
        ]]>
      </destructor>

      <field name="tabbox" readonly="true">
        document.getElementById("tabbrowser-tabbox");
      </field>

      <field name="contextMenu" readonly="true">
        document.getElementById("tabContextMenu");
      </field>

      <field name="arrowScrollbox">
        document.getAnonymousElementByAttribute(this, "anonid", "arrowscrollbox");
      </field>

      <field name="_firstTab">null</field>
      <field name="_lastTab">null</field>
      <field name="_beforeSelectedTab">null</field>
      <field name="_beforeHoveredTab">null</field>
      <field name="_afterHoveredTab">null</field>
      <field name="_hoveredTab">null</field>
      <field name="restoreTabsButton">
        document.getAnonymousElementByAttribute(this, "anonid", "restore-tabs-button");
      </field>
      <field name="_restoreTabsButtonWrapperWidth">0</field>
      <field name="windowUtils">
        window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
      </field>

      <property name="_tabMinWidth">
        <setter>
          this.style.setProperty("--tab-min-width", val + "px");
          return val;
        </setter>
      </property>

      <property name="restoreTabsButtonWrapperWidth" readonly="true">
        <getter>
          if (!this._restoreTabsButtonWrapperWidth) {
            this._restoreTabsButtonWrapperWidth = this.windowUtils
              .getBoundsWithoutFlushing(this.restoreTabsButton.parentNode)
              .width;
          }
          return this._restoreTabsButtonWrapperWidth;
        </getter>
      </property>

      <method name="updateSessionRestoreVisibility">
        <body><![CDATA[
          let {restoreTabsButton, restoreTabsButtonWrapperWidth, windowUtils} = this;
          let restoreTabsButtonWrapper = restoreTabsButton.parentNode;

          if (!restoreTabsButtonWrapper.getAttribute("session-exists")) {
            restoreTabsButtonWrapper.removeAttribute("shown");
            return;
          }

          let arrowScrollboxWidth = this.arrowScrollbox.clientWidth;

          let newTabButton = document.getAnonymousElementByAttribute(
            this, "anonid", "tabs-newtab-button");

          // If there are no pinned tabs it will multiply by 0 and result in 0
          let pinnedTabsWidth = windowUtils.getBoundsWithoutFlushing(this.firstChild).width * this._lastNumPinned;

          let numUnpinnedTabs = this.childNodes.length - this._lastNumPinned;
          let unpinnedTabsWidth = windowUtils.getBoundsWithoutFlushing(this.lastChild).width * numUnpinnedTabs;

          let tabbarUsedSpace = pinnedTabsWidth + unpinnedTabsWidth
            + windowUtils.getBoundsWithoutFlushing(newTabButton).width;

          // Subtract the elements' widths from the available space to ensure
          // that showing the restoreTabsButton won't cause any overflow.
          if (arrowScrollboxWidth - tabbarUsedSpace > restoreTabsButtonWrapperWidth) {
            restoreTabsButtonWrapper.setAttribute("shown", "true");
          } else {
            restoreTabsButtonWrapper.removeAttribute("shown");
          }
        ]]></body>
      </method>

      <method name="observe">
        <parameter name="aSubject"/>
        <parameter name="aTopic"/>
        <parameter name="aData"/>
        <body><![CDATA[
          switch (aTopic) {
            case "nsPref:changed":
              // This is has to deal with changes in
              // privacy.userContext.enabled and
              // privacy.userContext.longPressBehavior.
              let containersEnabled = Services.prefs.getBoolPref("privacy.userContext.enabled")
                                        && !PrivateBrowsingUtils.isWindowPrivate(window);

              // This pref won't change so often, so just recreate the menu.
              let longPressBehavior = Services.prefs.getIntPref("privacy.userContext.longPressBehavior");

              // If longPressBehavior pref is set to 0 (or any invalid value)
              // long press menu is disabled.
              if (containersEnabled && (longPressBehavior <= 0 || longPressBehavior > 2)) {
                containersEnabled = false;
              }

              const newTab = document.getElementById("new-tab-button");
              const newTab2 = document.getAnonymousElementByAttribute(this, "anonid", "tabs-newtab-button");

              for (let parent of [newTab, newTab2]) {
                if (!parent)
                  continue;

                gClickAndHoldListenersOnElement.remove(parent);
                parent.removeAttribute("type");
                if (parent.firstChild) {
                  parent.firstChild.remove();
                }

                if (containersEnabled) {
                  let popup = document.createElementNS(
                                "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
                                "menupopup");
                  if (parent.id) {
                    popup.id = "newtab-popup";
                  } else {
                    popup.setAttribute("anonid", "newtab-popup");
                  }
                  popup.className = "new-tab-popup";
                  popup.setAttribute("position", "after_end");
                  parent.appendChild(popup);

                  // longPressBehavior == 2 means that the menu is shown after X
                  // millisecs. Otherwise, with 1, the menu is open immediatelly.
                  if (longPressBehavior == 2) {
                    gClickAndHoldListenersOnElement.add(parent);
                  }

                  parent.setAttribute("type", "menu");
                }
              }

              break;
          }
        ]]></body>
      </method>

      <property name="_isCustomizing" readonly="true">
        <getter><![CDATA[
          return document.documentElement.getAttribute("customizing") == "true";
        ]]></getter>
      </property>

      <method name="_getVisibleTabs">
        <body><![CDATA[
          // Cannot access gBrowser before it's initialized.
          if (!gBrowser) {
            return [ this.firstChild ];
          }

          return gBrowser.visibleTabs;
        ]]></body>
      </method>

      <method name="_setPositionalAttributes">
        <body><![CDATA[
          let visibleTabs = this._getVisibleTabs();
          if (!visibleTabs.length) {
            return;
          }
          let selectedIndex = visibleTabs.indexOf(this.selectedItem);

          if (this._beforeSelectedTab) {
            this._beforeSelectedTab.removeAttribute("beforeselected-visible");
          }

          if (this.selectedItem.closing || selectedIndex == 0) {
            this._beforeSelectedTab = null;
          } else {
            let beforeSelectedTab = visibleTabs[selectedIndex - 1];
            let separatedByScrollButton = this.getAttribute("overflow") == "true" &&
              beforeSelectedTab.pinned && !this.selectedItem.pinned;
            if (!separatedByScrollButton) {
              this._beforeSelectedTab = beforeSelectedTab;
              this._beforeSelectedTab.setAttribute("beforeselected-visible",
                                                   "true");
            }
          }

          if (this._firstTab)
            this._firstTab.removeAttribute("first-visible-tab");
          this._firstTab = visibleTabs[0];
          this._firstTab.setAttribute("first-visible-tab", "true");
          if (this._lastTab)
            this._lastTab.removeAttribute("last-visible-tab");
          this._lastTab = visibleTabs[visibleTabs.length - 1];
          this._lastTab.setAttribute("last-visible-tab", "true");

          let hoveredTab = this._hoveredTab;
          if (hoveredTab) {
            hoveredTab._mouseleave();
          }
          hoveredTab = this.querySelector("tab:hover");
          if (hoveredTab) {
            hoveredTab._mouseenter();
          }
        ]]></body>
      </method>

      <field name="_blockDblClick">false</field>

      <field name="_tabDropIndicator">
        document.getAnonymousElementByAttribute(this, "anonid", "tab-drop-indicator");
      </field>

      <field name="_dragOverDelay">350</field>
      <field name="_dragTime">0</field>

      <field name="_container" readonly="true"><![CDATA[
        this.parentNode && this.parentNode.localName == "toolbar" ? this.parentNode : this;
      ]]></field>

      <field name="_propagatedVisibilityOnce">false</field>

      <property name="visible"
                onget="return !this._container.collapsed;">
        <setter><![CDATA[
          if (val == this.visible &&
              this._propagatedVisibilityOnce)
            return val;

          this._container.collapsed = !val;

          this._propagateVisibility();
          this._propagatedVisibilityOnce = true;

          return val;
        ]]></setter>
      </property>

      <method name="_propagateVisibility">
        <body><![CDATA[
          let visible = this.visible;

          document.getElementById("menu_closeWindow").hidden = !visible;
          document.getElementById("menu_close").setAttribute("label",
            gTabBrowserBundle.GetStringFromName(visible ? "tabs.closeTab" : "tabs.close"));

          TabsInTitlebar.allowedBy("tabs-visible", visible);
        ]]></body>
      </method>

      <method name="updateVisibility">
        <body><![CDATA[
          if (this.childNodes.length - gBrowser._removingTabs.length == 1) {
            this.visible = window.toolbar.visible;
          } else {
            this.visible = true;
          }
        ]]></body>
      </method>

      <field name="_closeButtonsUpdatePending">false</field>
      <method name="_updateCloseButtons">
        <body><![CDATA[
          // If we're overflowing, tabs are at their minimum widths.
          if (this.getAttribute("overflow") == "true") {
            this.setAttribute("closebuttons", "activetab");
            return;
          }

          if (this._closeButtonsUpdatePending) {
            return;
          }
          this._closeButtonsUpdatePending = true;

          // Wait until after the next paint to get current layout data from
          // getBoundsWithoutFlushing.
          window.requestAnimationFrame(() => {
            window.requestAnimationFrame(() => {
              this._closeButtonsUpdatePending = false;

              // The scrollbox may have started overflowing since we checked
              // overflow earlier, so check again.
              if (this.getAttribute("overflow") == "true") {
                this.setAttribute("closebuttons", "activetab");
                return;
              }

              // Check if tab widths are below the threshold where we want to
              // remove close buttons from background tabs so that people don't
              // accidentally close tabs by selecting them.
              let rect = ele => {
                return window.QueryInterface(Ci.nsIInterfaceRequestor)
                             .getInterface(Ci.nsIDOMWindowUtils)
                             .getBoundsWithoutFlushing(ele);
              };
              let tab = this._getVisibleTabs()[gBrowser._numPinnedTabs];
              if (tab && rect(tab).width <= this._tabClipWidth) {
                this.setAttribute("closebuttons", "activetab");
              } else {
                this.removeAttribute("closebuttons");
              }
            });
          });
        ]]></body>
      </method>

      <method name="_handleTabSelect">
        <parameter name="aInstant"/>
        <body><![CDATA[
          if (this.getAttribute("overflow") == "true")
            this.arrowScrollbox.ensureElementIsVisible(this.selectedItem, aInstant);

          this.selectedItem._notselectedsinceload = false;
        ]]></body>
      </method>

      <field name="_closingTabsSpacer">
        document.getAnonymousElementByAttribute(this, "anonid", "closing-tabs-spacer");
      </field>

      <field name="_tabDefaultMaxWidth">NaN</field>
      <field name="_lastTabClosedByMouse">false</field>
      <field name="_hasTabTempMaxWidth">false</field>

      <!-- Try to keep the active tab's close button under the mouse cursor -->
      <method name="_lockTabSizing">
        <parameter name="aTab"/>
        <body><![CDATA[
          let tabs = this._getVisibleTabs();
          if (!tabs.length) {
            return;
          }

          var isEndTab = (aTab._tPos > tabs[tabs.length - 1]._tPos);
          var tabWidth = aTab.getBoundingClientRect().width;

          if (!this._tabDefaultMaxWidth) {
            this._tabDefaultMaxWidth =
              parseFloat(window.getComputedStyle(aTab).maxWidth);
          }
          this._lastTabClosedByMouse = true;

          if (this.getAttribute("overflow") == "true") {
            // Don't need to do anything if we're in overflow mode and aren't scrolled
            // all the way to the right, or if we're closing the last tab.
            if (isEndTab || !this.arrowScrollbox._scrollButtonDown.disabled) {
              return;
            }
            // If the tab has an owner that will become the active tab, the owner will
            // be to the left of it, so we actually want the left tab to slide over.
            // This can't be done as easily in non-overflow mode, so we don't bother.
            if (aTab.owner) {
              return;
            }
            this._expandSpacerBy(tabWidth);
          } else { // non-overflow mode
            // Locking is neither in effect nor needed, so let tabs expand normally.
            if (isEndTab && !this._hasTabTempMaxWidth) {
              return;
            }
            let numPinned = gBrowser._numPinnedTabs;
            // Force tabs to stay the same width, unless we're closing the last tab,
            // which case we need to let them expand just enough so that the overall
            // tabbar width is the same.
            if (isEndTab) {
              let numNormalTabs = tabs.length - numPinned;
              tabWidth = tabWidth * (numNormalTabs + 1) / numNormalTabs;
              if (tabWidth > this._tabDefaultMaxWidth) {
                tabWidth = this._tabDefaultMaxWidth;
              }
            }
            tabWidth += "px";
            for (let i = numPinned; i < tabs.length; i++) {
              let tab = tabs[i];
              tab.style.setProperty("max-width", tabWidth, "important");
              if (!isEndTab) { // keep tabs the same width
                tab.style.transition = "none";
                tab.clientTop; // flush styles to skip animation; see bug 649247
                tab.style.transition = "";
              }
            }
            this._hasTabTempMaxWidth = true;
            gBrowser.addEventListener("mousemove", this);
            window.addEventListener("mouseout", this);
          }
        ]]></body>
      </method>

      <method name="_expandSpacerBy">
        <parameter name="pixels"/>
        <body><![CDATA[
          let spacer = this._closingTabsSpacer;
          spacer.style.width = parseFloat(spacer.style.width) + pixels + "px";
          this.setAttribute("using-closing-tabs-spacer", "true");
          gBrowser.addEventListener("mousemove", this);
          window.addEventListener("mouseout", this);
        ]]></body>
      </method>

      <method name="_unlockTabSizing">
        <body><![CDATA[
          gBrowser.removeEventListener("mousemove", this);
          window.removeEventListener("mouseout", this);

          if (this._hasTabTempMaxWidth) {
            this._hasTabTempMaxWidth = false;
            let tabs = this._getVisibleTabs();
            for (let i = 0; i < tabs.length; i++) {
              tabs[i].style.maxWidth = "";
            }
          }

          if (this.hasAttribute("using-closing-tabs-spacer")) {
            this.removeAttribute("using-closing-tabs-spacer");
            this._closingTabsSpacer.style.width = 0;
          }
        ]]></body>
      </method>

      <method name="uiDensityChanged">
        <body><![CDATA[
          this._positionPinnedTabs();
          this._updateCloseButtons();
          this._handleTabSelect(true);
        ]]></body>
      </method>

      <field name="_lastNumPinned">0</field>
      <field name="_pinnedTabsLayoutCache">null</field>
      <method name="_positionPinnedTabs">
        <body><![CDATA[
          let numPinned = gBrowser._numPinnedTabs;
          let doPosition = this.getAttribute("overflow") == "true" &&
                           this._getVisibleTabs().length > numPinned &&
                           numPinned > 0;

          if (doPosition) {
            this.setAttribute("positionpinnedtabs", "true");

            let layoutData = this._pinnedTabsLayoutCache;
            let uiDensity = document.documentElement.getAttribute("uidensity");
            if (!layoutData ||
                layoutData.uiDensity != uiDensity) {
              let arrowScrollbox = this.arrowScrollbox;
              layoutData = this._pinnedTabsLayoutCache = {
                uiDensity,
                pinnedTabWidth: this.childNodes[0].getBoundingClientRect().width,
                scrollButtonWidth: arrowScrollbox._scrollButtonDown.getBoundingClientRect().width
              };
            }

            let width = 0;
            for (let i = numPinned - 1; i >= 0; i--) {
              let tab = this.childNodes[i];
              width += layoutData.pinnedTabWidth;
              tab.style.marginInlineStart = -(width + layoutData.scrollButtonWidth) + "px";
              tab._pinnedUnscrollable = true;
            }
            this.style.paddingInlineStart = width + "px";
          } else {
            this.removeAttribute("positionpinnedtabs");

            for (let i = 0; i < numPinned; i++) {
              let tab = this.childNodes[i];
              tab.style.marginInlineStart = "";
              tab._pinnedUnscrollable = false;
            }

            this.style.paddingInlineStart = "";
          }

          if (this._lastNumPinned != numPinned) {
            this._lastNumPinned = numPinned;
            this._handleTabSelect(true);
          }
        ]]></body>
      </method>

      <method name="_animateTabMove">
        <parameter name="event"/>
        <body><![CDATA[
          let draggedTab = event.dataTransfer.mozGetDataAt(TAB_DROP_TYPE, 0);

          if (this.getAttribute("movingtab") != "true") {
            this.setAttribute("movingtab", "true");
            this.parentNode.setAttribute("movingtab", "true");
            this.selectedItem = draggedTab;
          }

          if (!("animLastScreenX" in draggedTab._dragData))
            draggedTab._dragData.animLastScreenX = draggedTab._dragData.screenX;

          let screenX = event.screenX;
          if (screenX == draggedTab._dragData.animLastScreenX)
            return;

          draggedTab._dragData.animLastScreenX = screenX;

          let rtl = (window.getComputedStyle(this).direction == "rtl");
          let pinned = draggedTab.pinned;
          let numPinned = gBrowser._numPinnedTabs;
          let tabs = this._getVisibleTabs()
                         .slice(pinned ? 0 : numPinned,
                                pinned ? numPinned : undefined);
          if (rtl) {
            tabs.reverse();
          }
          let tabWidth = draggedTab.getBoundingClientRect().width;
          draggedTab._dragData.tabWidth = tabWidth;

          // Move the dragged tab based on the mouse position.

          let leftTab = tabs[0];
          let rightTab = tabs[tabs.length - 1];
          let tabScreenX = draggedTab.boxObject.screenX;
          let translateX = screenX - draggedTab._dragData.screenX;
          if (!pinned) {
            translateX += this.arrowScrollbox._scrollbox.scrollLeft - draggedTab._dragData.scrollX;
          }
          let leftBound = leftTab.boxObject.screenX - tabScreenX;
          let rightBound = (rightTab.boxObject.screenX + rightTab.boxObject.width) -
                           (tabScreenX + tabWidth);
          translateX = Math.max(translateX, leftBound);
          translateX = Math.min(translateX, rightBound);
          draggedTab.style.transform = "translateX(" + translateX + "px)";
          draggedTab._dragData.translateX = translateX;

          // Determine what tab we're dragging over.
          // * Point of reference is the center of the dragged tab. If that
          //   point touches a background tab, the dragged tab would take that
          //   tab's position when dropped.
          // * We're doing a binary search in order to reduce the amount of
          //   tabs we need to check.

          let tabCenter = tabScreenX + translateX + tabWidth / 2;
          let newIndex = -1;
          let oldIndex = "animDropIndex" in draggedTab._dragData ?
                         draggedTab._dragData.animDropIndex : draggedTab._tPos;
          let low = 0;
          let high = tabs.length - 1;
          while (low <= high) {
            let mid = Math.floor((low + high) / 2);
            if (tabs[mid] == draggedTab &&
                ++mid > high)
              break;
            let boxObject = tabs[mid].boxObject;
            screenX = boxObject.screenX + getTabShift(tabs[mid], oldIndex);
            if (screenX > tabCenter) {
              high = mid - 1;
            } else if (screenX + boxObject.width < tabCenter) {
              low = mid + 1;
            } else {
              newIndex = tabs[mid]._tPos;
              break;
            }
          }
          if (newIndex >= oldIndex)
            newIndex++;
          if (newIndex < 0 || newIndex == oldIndex)
            return;
          draggedTab._dragData.animDropIndex = newIndex;

          // Shift background tabs to leave a gap where the dragged tab
          // would currently be dropped.

          for (let tab of tabs) {
            if (tab != draggedTab) {
              let shift = getTabShift(tab, newIndex);
              tab.style.transform = shift ? "translateX(" + shift + "px)" : "";
            }
          }

          function getTabShift(tab, dropIndex) {
            if (tab._tPos < draggedTab._tPos && tab._tPos >= dropIndex)
              return rtl ? -tabWidth : tabWidth;
            if (tab._tPos > draggedTab._tPos && tab._tPos < dropIndex)
              return rtl ? tabWidth : -tabWidth;
            return 0;
          }
        ]]></body>
      </method>

      <method name="_finishAnimateTabMove">
        <body><![CDATA[
          if (this.getAttribute("movingtab") != "true") {
            return;
          }

          for (let tab of this._getVisibleTabs()) {
            tab.style.transform = "";
          }

          this.removeAttribute("movingtab");
          this.parentNode.removeAttribute("movingtab");

          this._handleTabSelect();
        ]]></body>
      </method>

      <method name="handleEvent">
        <parameter name="aEvent"/>
        <body><![CDATA[
          switch (aEvent.type) {
            case "DOMContentLoaded":
              this.updateVisibility();
              TabsInTitlebar.init();
              break;
            case "resize":
              if (aEvent.target != window)
                break;

              TabsInTitlebar.updateAppearance();
              this._updateCloseButtons();
              this._handleTabSelect(true);
              this.updateSessionRestoreVisibility();
              break;
            case "mouseout":
              // If the "related target" (the node to which the pointer went) is not
              // a child of the current document, the mouse just left the window.
              let relatedTarget = aEvent.relatedTarget;
              if (relatedTarget && relatedTarget.ownerDocument == document)
                break;
            case "mousemove":
              if (document.getElementById("tabContextMenu").state != "open")
                this._unlockTabSizing();
              break;
          }
        ]]></body>
      </method>

      <field name="_animateElement">
        this.arrowScrollbox._scrollButtonDown;
      </field>

      <method name="_notifyBackgroundTab">
        <parameter name="aTab"/>
        <body><![CDATA[
          if (aTab.pinned || aTab.hidden)
            return;

          var scrollRect = this.arrowScrollbox.scrollClientRect;
          var tab = aTab.getBoundingClientRect();

          // DOMRect left/right properties are immutable.
          tab = {left: tab.left, right: tab.right};

          // Is the new tab already completely visible?
          if (scrollRect.left <= tab.left && tab.right <= scrollRect.right)
            return;

          if (this.arrowScrollbox.smoothScroll) {
            let selected = !this.selectedItem.pinned &&
                           this.selectedItem.getBoundingClientRect();

            // Can we make both the new tab and the selected tab completely visible?
            if (!selected ||
                Math.max(tab.right - selected.left, selected.right - tab.left) <=
                  scrollRect.width) {
              this.arrowScrollbox.ensureElementIsVisible(aTab);
              return;
            }

            this.arrowScrollbox.scrollByPixels(this.arrowScrollbox._isRTLScrollbox ?
                                                 selected.right - scrollRect.right :
                                                 selected.left - scrollRect.left);
          }

          if (!this._animateElement.hasAttribute("highlight")) {
            this._animateElement.setAttribute("highlight", "true");
            setTimeout(function(ele) {
              ele.removeAttribute("highlight");
            }, 150, this._animateElement);
          }
        ]]></body>
      </method>

      <method name="_getDragTargetTab">
        <parameter name="event"/>
        <parameter name="isLink"/>
        <body><![CDATA[
          let tab = event.target.localName == "tab" ? event.target : null;
          if (tab && isLink) {
            let boxObject = tab.boxObject;
            if (event.screenX < boxObject.screenX + boxObject.width * .25 ||
                event.screenX > boxObject.screenX + boxObject.width * .75)
              return null;
          }
          return tab;
        ]]></body>
      </method>

      <method name="_getDropIndex">
        <parameter name="event"/>
        <parameter name="isLink"/>
        <body><![CDATA[
          var tabs = this.childNodes;
          var tab = this._getDragTargetTab(event, isLink);
          if (window.getComputedStyle(this).direction == "ltr") {
            for (let i = tab ? tab._tPos : 0; i < tabs.length; i++)
              if (event.screenX < tabs[i].boxObject.screenX + tabs[i].boxObject.width / 2)
                return i;
          } else {
            for (let i = tab ? tab._tPos : 0; i < tabs.length; i++)
              if (event.screenX > tabs[i].boxObject.screenX + tabs[i].boxObject.width / 2)
                return i;
          }
          return tabs.length;
        ]]></body>
      </method>

      <method name="_getDropEffectForTabDrag">
        <parameter name="event"/>
        <body><![CDATA[
          var dt = event.dataTransfer;
          if (dt.mozItemCount == 1) {
            var types = dt.mozTypesAt(0);
            // tabs are always added as the first type
            if (types[0] == TAB_DROP_TYPE) {
              let sourceNode = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
              if (sourceNode instanceof XULElement &&
                  sourceNode.localName == "tab" &&
                  sourceNode.ownerGlobal.isChromeWindow &&
                  sourceNode.ownerDocument.documentElement.getAttribute("windowtype") == "navigator:browser" &&
                  sourceNode.ownerGlobal.gBrowser.tabContainer == sourceNode.parentNode) {
                // Do not allow transfering a private tab to a non-private window
                // and vice versa.
                if (PrivateBrowsingUtils.isWindowPrivate(window) !=
                    PrivateBrowsingUtils.isWindowPrivate(sourceNode.ownerGlobal))
                  return "none";

                if (window.gMultiProcessBrowser !=
                    sourceNode.ownerGlobal.gMultiProcessBrowser)
                  return "none";

                return dt.dropEffect == "copy" ? "copy" : "move";
              }
            }
          }

          if (browserDragAndDrop.canDropLink(event)) {
            return "link";
          }
          return "none";
        ]]></body>
      </method>

      <method name="_handleNewTab">
        <parameter name="tab"/>
        <body><![CDATA[
          if (tab.parentNode != this) {
            return;
          }
          tab._fullyOpen = true;
          gBrowser.tabAnimationsInProgress--;

          this._updateCloseButtons();

          if (tab.getAttribute("selected") == "true") {
            this._handleTabSelect();
          } else if (!tab.hasAttribute("skipbackgroundnotify")) {
            this._notifyBackgroundTab(tab);
          }

          // XXXmano: this is a temporary workaround for bug 345399
          // We need to manually update the scroll buttons disabled state
          // if a tab was inserted to the overflow area or removed from it
          // without any scrolling and when the tabbar has already
          // overflowed.
          this.arrowScrollbox._updateScrollButtonsDisabledState();

          // Preload the next about:newtab if there isn't one already.
          gBrowser._createPreloadBrowser();
        ]]></body>
      </method>

      <method name="_canAdvanceToTab">
        <parameter name="aTab"/>
        <body>
        <![CDATA[
          return !aTab.closing;
        ]]>
        </body>
      </method>

      <method name="getRelatedElement">
        <parameter name="aTab"/>
        <body>
        <![CDATA[
          if (!aTab) {
            return null;
          }

          // Cannot access gBrowser before it's initialized.
          if (!gBrowser) {
            return this.tabbox.tabpanels.firstChild;
          }

          // If the tab's browser is lazy, we need to `_insertBrowser` in order
          // to have a linkedPanel.  This will also serve to bind the browser
          // and make it ready to use when the tab is selected.
          gBrowser._insertBrowser(aTab);
          return document.getElementById(aTab.linkedPanel);
        ]]>
        </body>
      </method>

      <method name="_updateNewTabVisibility">
        <body><![CDATA[
          // Helper functions to help deal with customize mode wrapping some items
          let wrap = n => n.parentNode.localName == "toolbarpaletteitem" ? n.parentNode : n;
          let unwrap = n => n && n.localName == "toolbarpaletteitem" ? n.firstElementChild : n;

          let sib = this;
          do {
            sib = unwrap(wrap(sib).nextElementSibling);
          } while (sib && sib.hidden);

          const kAttr = "hasadjacentnewtabbutton";
          if (sib && sib.id == "new-tab-button") {
            this.setAttribute(kAttr, "true");
          } else {
            this.removeAttribute(kAttr);
          }
        ]]></body>
      </method>

      <method name="onWidgetAfterDOMChange">
        <parameter name="aNode"/>
        <parameter name="aNextNode"/>
        <parameter name="aContainer"/>
        <body><![CDATA[
          if (aContainer.ownerDocument == document &&
              aContainer.id == "TabsToolbar") {
            this._updateNewTabVisibility();
          }
        ]]></body>
      </method>

      <method name="onAreaNodeRegistered">
        <parameter name="aArea"/>
        <parameter name="aContainer"/>
        <body><![CDATA[
          if (aContainer.ownerDocument == document &&
              aArea == "TabsToolbar") {
            this._updateNewTabVisibility();
          }
        ]]></body>
      </method>

      <method name="onAreaReset">
        <parameter name="aArea"/>
        <parameter name="aContainer"/>
        <body><![CDATA[
          this.onAreaNodeRegistered(aArea, aContainer);
        ]]></body>
      </method>
    </implementation>

    <handlers>
      <handler event="TabSelect" action="this._handleTabSelect();"/>

      <handler event="transitionend"><![CDATA[
        if (event.propertyName != "max-width") {
          return;
        }

        var tab = event.target;

        if (tab.getAttribute("fadein") == "true") {
          if (tab._fullyOpen) {
            this._updateCloseButtons();
          } else {
            this._handleNewTab(tab);
          }
        } else if (tab.closing) {
          gBrowser._endRemoveTab(tab);
        }
      ]]></handler>

      <handler event="dblclick"><![CDATA[
        // When the tabbar has an unified appearance with the titlebar
        // and menubar, a double-click in it should have the same behavior
        // as double-clicking the titlebar
        if (TabsInTitlebar.enabled || this.parentNode._dragBindingAlive)
          return;

        if (event.button != 0 ||
            event.originalTarget.localName != "box")
          return;

        if (!this._blockDblClick)
          BrowserOpenTab();

        event.preventDefault();
      ]]></handler>

      <handler event="click" button="0" phase="capturing"><![CDATA[
        /* Catches extra clicks meant for the in-tab close button.
         * Placed here to avoid leaking (a temporary handler added from the
         * in-tab close button binding would close over the tab and leak it
         * until the handler itself was removed). (bug 897751)
         *
         * The only sequence in which a second click event (i.e. dblclik)
         * can be dispatched on an in-tab close button is when it is shown
         * after the first click (i.e. the first click event was dispatched
         * on the tab). This happens when we show the close button only on
         * the active tab. (bug 352021)
         * The only sequence in which a third click event can be dispatched
         * on an in-tab close button is when the tab was opened with a
         * double click on the tabbar. (bug 378344)
         * In both cases, it is most likely that the close button area has
         * been accidentally clicked, therefore we do not close the tab.
         *
         * We don't want to ignore processing of more than one click event,
         * though, since the user might actually be repeatedly clicking to
         * close many tabs at once.
         */
        let target = event.originalTarget;
        if (target.classList.contains("tab-close-button")) {
          // We preemptively set this to allow the closing-multiple-tabs-
          // in-a-row case.
          if (this._blockDblClick) {
            target._ignoredCloseButtonClicks = true;
          } else if (event.detail > 1 && !target._ignoredCloseButtonClicks) {
            target._ignoredCloseButtonClicks = true;
            event.stopPropagation();
            return;
          } else {
            // Reset the "ignored click" flag
            target._ignoredCloseButtonClicks = false;
          }
        }

        /* Protects from close-tab-button errant doubleclick:
         * Since we're removing the event target, if the user
         * double-clicks the button, the dblclick event will be dispatched
         * with the tabbar as its event target (and explicit/originalTarget),
         * which treats that as a mouse gesture for opening a new tab.
         * In this context, we're manually blocking the dblclick event.
         */
        if (this._blockDblClick) {
          if (!("_clickedTabBarOnce" in this)) {
            this._clickedTabBarOnce = true;
            return;
          }
          delete this._clickedTabBarOnce;
          this._blockDblClick = false;
        }
      ]]></handler>

      <handler event="click"><![CDATA[
        if (event.button != 1) {
          return;
        }

        if (event.target.localName == "tab") {
          gBrowser.removeTab(event.target, {
            animate: true,
            byMouse: event.mozInputSource == MouseEvent.MOZ_SOURCE_MOUSE,
          });
        } else if (event.originalTarget.localName == "box") {
          // The user middleclicked an open space on the tabstrip. This could
          // be because they intend to open a new tab, but it could also be
          // because they just removed a tab and they now middleclicked on the
          // resulting space while that tab is closing. In that case, we don't
          // want to open a tab. So if we're removing one or more tabs, and
          // the tab click is before the end of the last visible tab, we do
          // nothing.
          if (gBrowser._removingTabs.length) {
            let visibleTabs = this._getVisibleTabs();
            let ltr = (window.getComputedStyle(this).direction == "ltr");
            let lastTab = visibleTabs[visibleTabs.length - 1];
            let endOfTab = lastTab.getBoundingClientRect()[ltr ? "right" : "left"];
            if ((ltr && event.clientX > endOfTab) ||
                (!ltr && event.clientX < endOfTab)) {
              BrowserOpenTab();
            }
          } else {
            BrowserOpenTab();
          }
        } else {
          return;
        }

        event.stopPropagation();
      ]]></handler>

      <handler event="keydown" group="system"><![CDATA[
        if (event.altKey || event.shiftKey)
          return;

        let wrongModifiers;
        if (AppConstants.platform == "macosx") {
          wrongModifiers = !event.metaKey;
        } else {
          wrongModifiers = !event.ctrlKey || event.metaKey;
        }

        if (wrongModifiers)
          return;

        // Don't check if the event was already consumed because tab navigation
        // should work always for better user experience.

        switch (event.keyCode) {
          case KeyEvent.DOM_VK_UP:
            gBrowser.moveTabBackward();
            break;
          case KeyEvent.DOM_VK_DOWN:
            gBrowser.moveTabForward();
            break;
          case KeyEvent.DOM_VK_RIGHT:
          case KeyEvent.DOM_VK_LEFT:
            gBrowser.moveTabOver(event);
            break;
          case KeyEvent.DOM_VK_HOME:
            gBrowser.moveTabToStart();
            break;
          case KeyEvent.DOM_VK_END:
            gBrowser.moveTabToEnd();
            break;
          default:
            // Consume the keydown event for the above keyboard
            // shortcuts only.
            return;
        }
        event.preventDefault();
      ]]></handler>

      <handler event="dragstart"><![CDATA[
        var tab = this._getDragTargetTab(event, false);
        if (!tab || this._isCustomizing)
          return;

        let dt = event.dataTransfer;
        dt.mozSetDataAt(TAB_DROP_TYPE, tab, 0);
        let browser = tab.linkedBrowser;

        // We must not set text/x-moz-url or text/plain data here,
        // otherwise trying to deatch the tab by dropping it on the desktop
        // may result in an "internet shortcut"
        dt.mozSetDataAt("text/x-moz-text-internal", browser.currentURI.spec, 0);

        // Set the cursor to an arrow during tab drags.
        dt.mozCursor = "default";

        // Set the tab as the source of the drag, which ensures we have a stable
        // node to deliver the `dragend` event.  See bug 1345473.
        dt.addElement(tab);

        // Create a canvas to which we capture the current tab.
        // Until canvas is HiDPI-aware (bug 780362), we need to scale the desired
        // canvas size (in CSS pixels) to the window's backing resolution in order
        // to get a full-resolution drag image for use on HiDPI displays.
        let windowUtils = window.getInterface(Ci.nsIDOMWindowUtils);
        let scale = windowUtils.screenPixelsPerCSSPixel / windowUtils.fullZoom;
        let canvas = this._dndCanvas;
        if (!canvas) {
          this._dndCanvas = canvas =
            document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
          canvas.style.width = "100%";
          canvas.style.height = "100%";
          canvas.mozOpaque = true;
        }

        canvas.width = 160 * scale;
        canvas.height = 90 * scale;
        let toDrag = canvas;
        let dragImageOffset = -16;
        if (gMultiProcessBrowser) {
          var context = canvas.getContext("2d");
          context.fillStyle = "white";
          context.fillRect(0, 0, canvas.width, canvas.height);

          let captureListener;
          let platform = AppConstants.platform;
          // On Windows and Mac we can update the drag image during a drag
          // using updateDragImage. On Linux, we can use a panel.
          if (platform == "win" || platform == "macosx") {
            captureListener = function() {
              dt.updateDragImage(canvas, dragImageOffset, dragImageOffset);
            };
          } else {
            // Create a panel to use it in setDragImage
            // which will tell xul to render a panel that follows
            // the pointer while a dnd session is on.
            if (!this._dndPanel) {
              this._dndCanvas = canvas;
              this._dndPanel = document.createElement("panel");
              this._dndPanel.className = "dragfeedback-tab";
              this._dndPanel.setAttribute("type", "drag");
              let wrapper = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
              wrapper.style.width = "160px";
              wrapper.style.height = "90px";
              wrapper.appendChild(canvas);
              this._dndPanel.appendChild(wrapper);
              document.documentElement.appendChild(this._dndPanel);
            }
            toDrag = this._dndPanel;
          }
          // PageThumb is async with e10s but that's fine
          // since we can update the image during the dnd.
          PageThumbs.captureToCanvas(browser, canvas, captureListener);
        } else {
          // For the non e10s case we can just use PageThumbs
          // sync, so let's use the canvas for setDragImage.
          PageThumbs.captureToCanvas(browser, canvas);
          dragImageOffset = dragImageOffset * scale;
        }
        dt.setDragImage(toDrag, dragImageOffset, dragImageOffset);

        // _dragData.offsetX/Y give the coordinates that the mouse should be
        // positioned relative to the corner of the new window created upon
        // dragend such that the mouse appears to have the same position
        // relative to the corner of the dragged tab.
        function clientX(ele) {
          return ele.getBoundingClientRect().left;
        }
        let tabOffsetX = clientX(tab) - clientX(this);
        tab._dragData = {
          offsetX: event.screenX - window.screenX - tabOffsetX,
          offsetY: event.screenY - window.screenY,
          scrollX: this.arrowScrollbox._scrollbox.scrollLeft,
          screenX: event.screenX
        };

        event.stopPropagation();
      ]]></handler>

      <handler event="dragover"><![CDATA[
        var effects = this._getDropEffectForTabDrag(event);

        var ind = this._tabDropIndicator;
        if (effects == "" || effects == "none") {
          ind.collapsed = true;
          return;
        }
        event.preventDefault();
        event.stopPropagation();

        var arrowScrollbox = this.arrowScrollbox;
        var ltr = (window.getComputedStyle(this).direction == "ltr");

        // autoscroll the tab strip if we drag over the scroll
        // buttons, even if we aren't dragging a tab, but then
        // return to avoid drawing the drop indicator
        var pixelsToScroll = 0;
        if (this.getAttribute("overflow") == "true") {
          var targetAnonid = event.originalTarget.getAttribute("anonid");
          switch (targetAnonid) {
            case "scrollbutton-up":
              pixelsToScroll = arrowScrollbox.scrollIncrement * -1;
              break;
            case "scrollbutton-down":
              pixelsToScroll = arrowScrollbox.scrollIncrement;
              break;
          }
          if (pixelsToScroll)
            arrowScrollbox.scrollByPixels((ltr ? 1 : -1) * pixelsToScroll, true);
        }

        if (effects == "move" &&
            this == event.dataTransfer.mozGetDataAt(TAB_DROP_TYPE, 0).parentNode) {
          ind.collapsed = true;
          this._animateTabMove(event);
          return;
        }

        this._finishAnimateTabMove();

        if (effects == "link") {
          let tab = this._getDragTargetTab(event, true);
          if (tab) {
            if (!this._dragTime)
              this._dragTime = Date.now();
            if (Date.now() >= this._dragTime + this._dragOverDelay)
              this.selectedItem = tab;
            ind.collapsed = true;
            return;
          }
        }

        var rect = arrowScrollbox.getBoundingClientRect();
        var newMargin;
        if (pixelsToScroll) {
          // if we are scrolling, put the drop indicator at the edge
          // so that it doesn't jump while scrolling
          let scrollRect = arrowScrollbox.scrollClientRect;
          let minMargin = scrollRect.left - rect.left;
          let maxMargin = Math.min(minMargin + scrollRect.width,
                                   scrollRect.right);
          if (!ltr)
            [minMargin, maxMargin] = [this.clientWidth - maxMargin,
                                      this.clientWidth - minMargin];
          newMargin = (pixelsToScroll > 0) ? maxMargin : minMargin;
        } else {
          let newIndex = this._getDropIndex(event, effects == "link");
          if (newIndex == this.childNodes.length) {
            let tabRect = this.childNodes[newIndex - 1].getBoundingClientRect();
            if (ltr)
              newMargin = tabRect.right - rect.left;
            else
              newMargin = rect.right - tabRect.left;
          } else {
            let tabRect = this.childNodes[newIndex].getBoundingClientRect();
            if (ltr)
              newMargin = tabRect.left - rect.left;
            else
              newMargin = rect.right - tabRect.right;
          }
        }

        ind.collapsed = false;

        newMargin += ind.clientWidth / 2;
        if (!ltr)
          newMargin *= -1;

        ind.style.transform = "translate(" + Math.round(newMargin) + "px)";
        ind.style.marginInlineStart = (-ind.clientWidth) + "px";
      ]]></handler>

      <handler event="drop"><![CDATA[
        var dt = event.dataTransfer;
        var dropEffect = dt.dropEffect;
        var draggedTab;
        if (dt.mozTypesAt(0)[0] == TAB_DROP_TYPE) { // tab copy or move
          draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
          // not our drop then
          if (!draggedTab)
            return;
        }

        this._tabDropIndicator.collapsed = true;
        event.stopPropagation();
        if (draggedTab && dropEffect == "copy") {
          // copy the dropped tab (wherever it's from)
          let newIndex = this._getDropIndex(event, false);
          let newTab = gBrowser.duplicateTab(draggedTab);
          gBrowser.moveTabTo(newTab, newIndex);
          if (draggedTab.parentNode != this || event.shiftKey) {
            this.selectedItem = newTab;
          }
        } else if (draggedTab && draggedTab.parentNode == this) {
          let oldTranslateX = Math.round(draggedTab._dragData.translateX);
          let tabWidth = Math.round(draggedTab._dragData.tabWidth);
          let translateOffset = oldTranslateX % tabWidth;
          let newTranslateX = oldTranslateX - translateOffset;
          if (oldTranslateX > 0 && translateOffset > tabWidth / 2) {
            newTranslateX += tabWidth;
          } else if (oldTranslateX < 0 && -translateOffset > tabWidth / 2) {
            newTranslateX -= tabWidth;
          }

          let dropIndex = "animDropIndex" in draggedTab._dragData &&
                          draggedTab._dragData.animDropIndex;
          if (dropIndex && dropIndex > draggedTab._tPos)
            dropIndex--;

          let animate = gBrowser.animationsEnabled;
          if (oldTranslateX && oldTranslateX != newTranslateX && animate) {
            draggedTab.setAttribute("tabdrop-samewindow", "true");
            draggedTab.style.transform = "translateX(" + newTranslateX + "px)";
            let onTransitionEnd = transitionendEvent => {
              if (transitionendEvent.propertyName != "transform" ||
                  transitionendEvent.originalTarget != draggedTab) {
                return;
              }
              draggedTab.removeEventListener("transitionend", onTransitionEnd);

              draggedTab.removeAttribute("tabdrop-samewindow");

              this._finishAnimateTabMove();
              if (dropIndex !== false) {
                gBrowser.moveTabTo(draggedTab, dropIndex);
              }

              gBrowser.syncThrobberAnimations(draggedTab);
            };
            draggedTab.addEventListener("transitionend", onTransitionEnd);
          } else {
            this._finishAnimateTabMove();
            if (dropIndex !== false) {
              gBrowser.moveTabTo(draggedTab, dropIndex);
            }
          }
        } else if (draggedTab) {
          let newIndex = this._getDropIndex(event, false);
          gBrowser.adoptTab(draggedTab, newIndex, true);
        } else {
          // Pass true to disallow dropping javascript: or data: urls
          let links;
          try {
            links = browserDragAndDrop.dropLinks(event, true);
          } catch (ex) {}

          if (!links || links.length === 0)
            return;

          let inBackground = Services.prefs.getBoolPref("browser.tabs.loadInBackground");
          if (event.shiftKey)
            inBackground = !inBackground;

          let targetTab = this._getDragTargetTab(event, true);
          let userContextId = this.selectedItem.getAttribute("usercontextid");
          let replace = !!targetTab;
          let newIndex = this._getDropIndex(event, true);
          let urls = links.map(link => link.url);

          let triggeringPrincipal = browserDragAndDrop.getTriggeringPrincipal(event);

          (async () => {
            if (urls.length >= Services.prefs.getIntPref("browser.tabs.maxOpenBeforeWarn")) {
              // Sync dialog cannot be used inside drop event handler.
              let answer = await OpenInTabsUtils.promiseConfirmOpenInTabs(urls.length,
                                                                          window);
              if (!answer) {
                return;
              }
            }

            gBrowser.loadTabs(urls, {
              inBackground,
              replace,
              allowThirdPartyFixup: true,
              targetTab,
              newIndex,
              userContextId,
              triggeringPrincipal,
            });
          })();
        }

        if (draggedTab) {
          delete draggedTab._dragData;
        }
      ]]></handler>

      <handler event="dragend"><![CDATA[
        var dt = event.dataTransfer;
        var draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0);

        // Prevent this code from running if a tabdrop animation is
        // running since calling _finishAnimateTabMove would clear
        // any CSS transition that is running.
        if (draggedTab.hasAttribute("tabdrop-samewindow"))
          return;

        this._finishAnimateTabMove();

        if (dt.mozUserCancelled || dt.dropEffect != "none" || this._isCustomizing) {
          delete draggedTab._dragData;
          return;
        }

        // Disable detach within the browser toolbox
        var eX = event.screenX;
        var eY = event.screenY;
        var wX = window.screenX;
        // check if the drop point is horizontally within the window
        if (eX > wX && eX < (wX + window.outerWidth)) {
          let bo = this.arrowScrollbox.boxObject;
          // also avoid detaching if the the tab was dropped too close to
          // the tabbar (half a tab)
          let endScreenY = bo.screenY + 1.5 * bo.height;
          if (eY < endScreenY && eY > window.screenY)
            return;
        }

        // screen.availLeft et. al. only check the screen that this window is on,
        // but we want to look at the screen the tab is being dropped onto.
        var screen = Cc["@mozilla.org/gfx/screenmanager;1"]
                       .getService(Ci.nsIScreenManager)
                       .screenForRect(eX, eY, 1, 1);
        var fullX = {}, fullY = {}, fullWidth = {}, fullHeight = {};
        var availX = {}, availY = {}, availWidth = {}, availHeight = {};
        // get full screen rect and available rect, both in desktop pix
        screen.GetRectDisplayPix(fullX, fullY, fullWidth, fullHeight);
        screen.GetAvailRectDisplayPix(availX, availY, availWidth, availHeight);

        // scale factor to convert desktop pixels to CSS px
        var scaleFactor =
          screen.contentsScaleFactor / screen.defaultCSSScaleFactor;
        // synchronize CSS-px top-left coordinates with the screen's desktop-px
        // coordinates, to ensure uniqueness across multiple screens
        // (compare the equivalent adjustments in nsGlobalWindow::GetScreenXY()
        // and related methods)
        availX.value = (availX.value - fullX.value) * scaleFactor + fullX.value;
        availY.value = (availY.value - fullY.value) * scaleFactor + fullY.value;
        availWidth.value *= scaleFactor;
        availHeight.value *= scaleFactor;

        // ensure new window entirely within screen
        var winWidth = Math.min(window.outerWidth, availWidth.value);
        var winHeight = Math.min(window.outerHeight, availHeight.value);
        var left = Math.min(Math.max(eX - draggedTab._dragData.offsetX, availX.value),
                            availX.value + availWidth.value - winWidth);
        var top = Math.min(Math.max(eY - draggedTab._dragData.offsetY, availY.value),
                           availY.value + availHeight.value - winHeight);

        delete draggedTab._dragData;

        if (gBrowser.tabs.length == 1) {
          // resize _before_ move to ensure the window fits the new screen.  if
          // the window is too large for its screen, the window manager may do
          // automatic repositioning.
          window.resizeTo(winWidth, winHeight);
          window.moveTo(left, top);
          window.focus();
        } else {
          let props = { screenX: left, screenY: top, suppressanimation: 1 };
          if (AppConstants.platform != "win") {
            props.outerWidth = winWidth;
            props.outerHeight = winHeight;
          }
          gBrowser.replaceTabWithWindow(draggedTab, props);
        }
        event.stopPropagation();
      ]]></handler>

      <handler event="dragexit"><![CDATA[
        this._dragTime = 0;

        // This does not work at all (see bug 458613)
        var target = event.relatedTarget;
        while (target && target != this)
          target = target.parentNode;
        if (target)
          return;

        this._tabDropIndicator.collapsed = true;
        event.stopPropagation();
      ]]></handler>
    </handlers>
  </binding>

  <binding id="tabbrowser-tab" display="xul:hbox"
           extends="chrome://global/content/bindings/tabbox.xml#tab">
    <content context="tabContextMenu">
      <xul:stack class="tab-stack" flex="1">
        <xul:vbox xbl:inherits="selected=visuallyselected,fadein"
                  class="tab-background">
          <xul:hbox xbl:inherits="selected=visuallyselected"
                    class="tab-line"/>
          <xul:spacer flex="1"/>
          <xul:hbox class="tab-bottom-line"/>
        </xul:vbox>
        <xul:hbox xbl:inherits="pinned,bursting,notselectedsinceload"
                  anonid="tab-loading-burst"
                  class="tab-loading-burst"/>
        <xul:hbox xbl:inherits="pinned,selected=visuallyselected,titlechanged,attention"
                  class="tab-content" align="center">
          <xul:hbox xbl:inherits="fadein,pinned,busy,progress,selected=visuallyselected"
                    anonid="tab-throbber"
                    class="tab-throbber"
                    layer="true"/>
          <xul:image xbl:inherits="fadein,pinned,busy,progress,selected=visuallyselected"
                     class="tab-throbber-fallback"
                     role="presentation"
                     layer="true"/>
          <xul:image xbl:inherits="src=image,triggeringprincipal=iconloadingprincipal,requestcontextid,fadein,pinned,selected=visuallyselected,busy,crashed,sharing"
                     anonid="tab-icon-image"
                     class="tab-icon-image"
                     validate="never"
                     role="presentation"/>
          <xul:image xbl:inherits="sharing,selected=visuallyselected,pinned"
                     anonid="sharing-icon"
                     class="tab-sharing-icon-overlay"
                     role="presentation"/>
          <xul:image xbl:inherits="crashed,busy,soundplaying,soundplaying-scheduledremoval,pinned,muted,blocked,selected=visuallyselected,activemedia-blocked"
                     anonid="overlay-icon"
                     class="tab-icon-overlay"
                     role="presentation"/>
          <xul:hbox class="tab-label-container"
                    xbl:inherits="pinned,selected=visuallyselected,labeldirection"
                    onoverflow="this.setAttribute('textoverflow', 'true');"
                    onunderflow="this.removeAttribute('textoverflow');"
                    flex="1">
            <xul:label class="tab-text tab-label"
                       xbl:inherits="xbl:text=label,accesskey,fadein,pinned,selected=visuallyselected,attention"
                       role="presentation"/>
          </xul:hbox>
          <xul:image xbl:inherits="soundplaying,soundplaying-scheduledremoval,pinned,muted,blocked,selected=visuallyselected,activemedia-blocked"
                     anonid="soundplaying-icon"
                     class="tab-icon-sound"
                     role="presentation"/>
          <xul:image anonid="close-button"
                     xbl:inherits="fadein,pinned,selected=visuallyselected"
                     class="tab-close-button close-icon"
                     role="presentation"/>
        </xul:hbox>
      </xul:stack>
    </content>

    <implementation>
      <constructor><![CDATA[
        if (!("_lastAccessed" in this)) {
          this.updateLastAccessed();
        }
      ]]></constructor>

      <property name="_visuallySelected">
        <setter>
          <![CDATA[
          if (val == (this.getAttribute("visuallyselected") == "true")) {
            return val;
          }

          if (val) {
            this.setAttribute("visuallyselected", "true");
          } else {
            this.removeAttribute("visuallyselected");
          }
          gBrowser._tabAttrModified(this, ["visuallyselected"]);

          return val;
          ]]>
        </setter>
      </property>

      <property name="_selected">
        <setter>
          <![CDATA[
          // in e10s we want to only pseudo-select a tab before its rendering is done, so that
          // the rest of the system knows that the tab is selected, but we don't want to update its
          // visual status to selected until after we receive confirmation that its content has painted.
          if (val)
            this.setAttribute("selected", "true");
          else
            this.removeAttribute("selected");

          // If we're non-e10s we should update the visual selection as well at the same time,
          // *or* if we're e10s and the visually selected tab isn't changing, in which case the
          // tab switcher code won't run and update anything else (like the before- and after-
          // selected attributes).
          if (!gMultiProcessBrowser || (val && this.hasAttribute("visuallyselected"))) {
            this._visuallySelected = val;
          }

          return val;
        ]]>
        </setter>
      </property>

      <property name="pinned" readonly="true">
        <getter>
          return this.getAttribute("pinned") == "true";
        </getter>
      </property>
      <property name="hidden" readonly="true">
        <getter>
          return this.getAttribute("hidden") == "true";
        </getter>
      </property>
      <property name="muted" readonly="true">
        <getter>
          return this.getAttribute("muted") == "true";
        </getter>
      </property>
      <!--
      Describes how the tab ended up in this mute state. May be any of:

       - undefined: The tabs mute state has never changed.
       - null: The mute state was last changed through the UI.
       - Any string: The ID was changed through an extension API. The string
                     must be the ID of the extension which changed it.
      -->
      <field name="muteReason">undefined</field>

      <property name="userContextId" readonly="true">
        <getter>
          return this.hasAttribute("usercontextid")
                   ? parseInt(this.getAttribute("usercontextid"))
                   : 0;
        </getter>
      </property>

      <property name="soundPlaying" readonly="true">
        <getter>
          return this.getAttribute("soundplaying") == "true";
        </getter>
      </property>

      <property name="activeMediaBlocked" readonly="true">
        <getter>
          return this.getAttribute("activemedia-blocked") == "true";
        </getter>
      </property>

      <property name="lastAccessed">
        <getter>
          return this._lastAccessed == Infinity ? Date.now() : this._lastAccessed;
        </getter>
      </property>
      <method name="updateLastAccessed">
        <parameter name="aDate"/>
        <body><![CDATA[
          this._lastAccessed = this.selected ? Infinity : (aDate || Date.now());
        ]]></body>
      </method>

      <field name="mOverCloseButton">false</field>
      <property name="_overPlayingIcon" readonly="true">
        <getter><![CDATA[
          let iconVisible = this.hasAttribute("soundplaying") ||
                            this.hasAttribute("muted") ||
                            this.hasAttribute("activemedia-blocked");
          let soundPlayingIcon =
            document.getAnonymousElementByAttribute(this, "anonid", "soundplaying-icon");
          let overlayIcon =
            document.getAnonymousElementByAttribute(this, "anonid", "overlay-icon");

          return soundPlayingIcon && soundPlayingIcon.matches(":hover") ||
                 (overlayIcon && overlayIcon.matches(":hover") && iconVisible);
        ]]></getter>
      </property>
      <field name="mCorrespondingMenuitem">null</field>

      <!--
      While it would make sense to track this in a field, the field will get nuked
      once the node is gone from the DOM, which causes us to think the tab is not
      closed, which causes us to make wrong decisions. So we use an expando instead.
      <field name="closing">false</field>
      -->

      <method name="_mouseenter">
        <body><![CDATA[
          if (this.hidden || this.closing) {
            return;
          }

          let tabContainer = this.parentNode;
          let visibleTabs = tabContainer._getVisibleTabs();
          let tabIndex = visibleTabs.indexOf(this);

          if (this.selected)
            tabContainer._handleTabSelect();

          if (tabIndex == 0) {
            tabContainer._beforeHoveredTab = null;
          } else {
            let candidate = visibleTabs[tabIndex - 1];
            let separatedByScrollButton =
              tabContainer.getAttribute("overflow") == "true" &&
              candidate.pinned && !this.pinned;
            if (!candidate.selected && !separatedByScrollButton) {
              tabContainer._beforeHoveredTab = candidate;
              candidate.setAttribute("beforehovered", "true");
            }
          }

          if (tabIndex == visibleTabs.length - 1) {
            tabContainer._afterHoveredTab = null;
          } else {
            let candidate = visibleTabs[tabIndex + 1];
            if (!candidate.selected) {
              tabContainer._afterHoveredTab = candidate;
              candidate.setAttribute("afterhovered", "true");
            }
          }

          tabContainer._hoveredTab = this;
          if (this.linkedPanel && !this.selected) {
            this.linkedBrowser.unselectedTabHover(true);
            this.startUnselectedTabHoverTimer();
          }

          // Prepare connection to host beforehand.
          SessionStore.speculativeConnectOnTabHover(this);

          let tabToWarm = this;
          if (this.mOverCloseButton) {
            tabToWarm = gBrowser._findTabToBlurTo(this);
          }
          gBrowser.warmupTab(tabToWarm);
        ]]></body>
      </method>

      <method name="_mouseleave">
        <body><![CDATA[
          let tabContainer = this.parentNode;
          if (tabContainer._beforeHoveredTab) {
            tabContainer._beforeHoveredTab.removeAttribute("beforehovered");
            tabContainer._beforeHoveredTab = null;
          }
          if (tabContainer._afterHoveredTab) {
            tabContainer._afterHoveredTab.removeAttribute("afterhovered");
            tabContainer._afterHoveredTab = null;
          }

          tabContainer._hoveredTab = null;
          if (this.linkedPanel && !this.selected) {
            this.linkedBrowser.unselectedTabHover(false);
            this.cancelUnselectedTabHoverTimer();
          }
        ]]></body>
      </method>

      <method name="startUnselectedTabHoverTimer">
        <body><![CDATA[
          // Only record data when we need to.
          if (!this.linkedBrowser.shouldHandleUnselectedTabHover) {
            return;
          }

          if (!TelemetryStopwatch.running("HOVER_UNTIL_UNSELECTED_TAB_OPENED", this)) {
            TelemetryStopwatch.start("HOVER_UNTIL_UNSELECTED_TAB_OPENED", this);
          }

          if (this._hoverTabTimer) {
            clearTimeout(this._hoverTabTimer);
            this._hoverTabTimer = null;
          }
        ]]></body>
      </method>

      <method name="cancelUnselectedTabHoverTimer">
        <body><![CDATA[
          // Since we're listening "mouseout" event, instead of "mouseleave".
          // Every time the cursor is moving from the tab to its child node (icon),
          // it would dispatch "mouseout"(for tab) first and then dispatch
          // "mouseover" (for icon, eg: close button, speaker icon) soon.
          // It causes we would cancel present TelemetryStopwatch immediately
          // when cursor is moving on the icon, and then start a new one.
          // In order to avoid this situation, we could delay cancellation and
          // remove it if we get "mouseover" within very short period.
          this._hoverTabTimer = setTimeout(() => {
            if (TelemetryStopwatch.running("HOVER_UNTIL_UNSELECTED_TAB_OPENED", this)) {
              TelemetryStopwatch.cancel("HOVER_UNTIL_UNSELECTED_TAB_OPENED", this);
            }
          }, 100);
        ]]></body>
      </method>

      <method name="finishUnselectedTabHoverTimer">
        <body><![CDATA[
          // Stop timer when the tab is opened.
          if (TelemetryStopwatch.running("HOVER_UNTIL_UNSELECTED_TAB_OPENED", this)) {
            TelemetryStopwatch.finish("HOVER_UNTIL_UNSELECTED_TAB_OPENED", this);
          }
        ]]></body>
      </method>

      <method name="startMediaBlockTimer">
        <body><![CDATA[
          TelemetryStopwatch.start("TAB_MEDIA_BLOCKING_TIME_MS", this);
        ]]></body>
      </method>

       <method name="finishMediaBlockTimer">
        <body><![CDATA[
          TelemetryStopwatch.finish("TAB_MEDIA_BLOCKING_TIME_MS", this);
        ]]></body>
      </method>

      <method name="toggleMuteAudio">
        <parameter name="aMuteReason"/>
        <body>
        <![CDATA[
          // Do not attempt to toggle mute state if browser is lazy.
          if (!this.linkedPanel) {
            return;
          }

          let browser = this.linkedBrowser;
          let modifiedAttrs = [];
          let hist = Services.telemetry.getHistogramById("TAB_AUDIO_INDICATOR_USED");

          if (this.hasAttribute("activemedia-blocked")) {
            this.removeAttribute("activemedia-blocked");
            modifiedAttrs.push("activemedia-blocked");

            browser.resumeMedia();
            hist.add(3 /* unblockByClickingIcon */);
            this.finishMediaBlockTimer();
          } else {
            if (browser.audioMuted) {
              browser.unmute();
              this.removeAttribute("muted");
              BrowserUITelemetry.countTabMutingEvent("unmute", aMuteReason);
              hist.add(1 /* unmute */);
            } else {
              browser.mute();
              this.setAttribute("muted", "true");
              BrowserUITelemetry.countTabMutingEvent("mute", aMuteReason);
              hist.add(0 /* mute */);
            }
            this.muteReason = aMuteReason || null;
            modifiedAttrs.push("muted");
          }
          gBrowser._tabAttrModified(this, modifiedAttrs);
        ]]>
        </body>
      </method>

      <method name="setUserContextId">
        <parameter name="aUserContextId"/>
        <body>
        <![CDATA[
          if (aUserContextId) {
            if (this.linkedBrowser) {
              this.linkedBrowser.setAttribute("usercontextid", aUserContextId);
            }
            this.setAttribute("usercontextid", aUserContextId);
          } else {
            if (this.linkedBrowser) {
              this.linkedBrowser.removeAttribute("usercontextid");
            }
            this.removeAttribute("usercontextid");
          }

          ContextualIdentityService.setTabStyle(this);
        ]]>
        </body>
      </method>
    </implementation>

    <handlers>
      <handler event="mouseover"><![CDATA[
        if (event.originalTarget.getAttribute("anonid") == "close-button") {
          this.mOverCloseButton = true;
        }

        this._mouseenter();
      ]]></handler>
      <handler event="mouseout"><![CDATA[
        if (event.originalTarget.getAttribute("anonid") == "close-button") {
          this.mOverCloseButton = false;
        }

        this._mouseleave();
      ]]></handler>

      <handler event="dragstart" phase="capturing">
        this.style.MozUserFocus = "";
      </handler>

      <handler event="dragstart"><![CDATA[
        if (this.mOverCloseButton) {
          event.stopPropagation();
        }
      ]]></handler>

      <handler event="mousedown" phase="capturing">
      <![CDATA[
        if (this.selected) {
          this.style.MozUserFocus = "ignore";
        } else if (this.mOverCloseButton ||
                   this._overPlayingIcon) {
          // Prevent tabbox.xml from selecting the tab.
          event.stopPropagation();
        }
      ]]>
      </handler>
      <handler event="mouseup">
        this.style.MozUserFocus = "";
      </handler>

      <handler event="click" button="0"><![CDATA[
        if (this._overPlayingIcon) {
          this.toggleMuteAudio();
          return;
        }

        if (event.originalTarget.getAttribute("anonid") == "close-button") {
          gBrowser.removeTab(this, {
            animate: true,
            byMouse: event.mozInputSource == MouseEvent.MOZ_SOURCE_MOUSE,
          });
          // This enables double-click protection for the tab container
          // (see tabbrowser-tabs 'click' handler).
          gBrowser.tabContainer._blockDblClick = true;
        }
      ]]></handler>

      <handler event="dblclick" button="0" phase="capturing"><![CDATA[
        // for the one-close-button case
        if (event.originalTarget.getAttribute("anonid") == "close-button") {
          event.stopPropagation();
        }
      ]]></handler>

      <handler event="animationend">
      <![CDATA[
        if (event.originalTarget.getAttribute("anonid") == "tab-loading-burst") {
          this.removeAttribute("bursting");
        }
      ]]>
      </handler>
    </handlers>
  </binding>

  <binding id="tabbrowser-alltabs-popup"
           extends="chrome://global/content/bindings/popup.xml#popup">
    <implementation implements="nsIDOMEventListener">
      <method name="_tabOnAttrModified">
        <parameter name="aEvent"/>
        <body><![CDATA[
          var tab = aEvent.target;
          if (tab.mCorrespondingMenuitem)
            this._setMenuitemAttributes(tab.mCorrespondingMenuitem, tab);
        ]]></body>
      </method>

      <method name="_tabOnTabClose">
        <parameter name="aEvent"/>
        <body><![CDATA[
          var tab = aEvent.target;
          if (tab.mCorrespondingMenuitem)
            this.removeChild(tab.mCorrespondingMenuitem);
        ]]></body>
      </method>

      <method name="handleEvent">
        <parameter name="aEvent"/>
        <body><![CDATA[
          switch (aEvent.type) {
            case "TabAttrModified":
              this._tabOnAttrModified(aEvent);
              break;
            case "TabClose":
              this._tabOnTabClose(aEvent);
              break;
          }
        ]]></body>
      </method>

      <method name="_updateTabsVisibilityStatus">
        <body><![CDATA[
          var tabContainer = gBrowser.tabContainer;
          // We don't want menu item decoration unless there is overflow.
          if (tabContainer.getAttribute("overflow") != "true") {
            return;
          }

          let windowUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)
                                  .getInterface(Ci.nsIDOMWindowUtils);
          let arrowScrollboxRect = windowUtils.getBoundsWithoutFlushing(tabContainer.arrowScrollbox);
          for (let menuitem of this.childNodes) {
            let curTab = menuitem.tab;
            if (!curTab) {
              // "Undo close tab", menuseparator, or entries put here by addons.
              continue;
            }
            let curTabRect = windowUtils.getBoundsWithoutFlushing(curTab);
            if (curTabRect.left >= arrowScrollboxRect.left &&
                curTabRect.right <= arrowScrollboxRect.right) {
              menuitem.setAttribute("tabIsVisible", "true");
            } else {
              menuitem.removeAttribute("tabIsVisible");
            }
          }
        ]]></body>
      </method>

      <method name="_createTabMenuItem">
        <parameter name="aTab"/>
        <body><![CDATA[
          var menuItem = document.createElementNS(
            "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
            "menuitem");

          menuItem.setAttribute("class", "menuitem-iconic alltabs-item menuitem-with-favicon");

          this._setMenuitemAttributes(menuItem, aTab);

          aTab.mCorrespondingMenuitem = menuItem;
          menuItem.tab = aTab;

          this.appendChild(menuItem);
        ]]></body>
      </method>

      <method name="_setMenuitemAttributes">
        <parameter name="aMenuitem"/>
        <parameter name="aTab"/>
        <body><![CDATA[
          aMenuitem.setAttribute("label", aTab.label);
          aMenuitem.setAttribute("crop", "end");

          if (aTab.hasAttribute("busy")) {
            aMenuitem.setAttribute("busy", aTab.getAttribute("busy"));
            aMenuitem.removeAttribute("iconloadingprincipal");
            aMenuitem.removeAttribute("image");
          } else {
            aMenuitem.setAttribute("iconloadingprincipal", aTab.getAttribute("iconloadingprincipal"));
            aMenuitem.setAttribute("image", aTab.getAttribute("image"));
            aMenuitem.removeAttribute("busy");
          }

          if (aTab.hasAttribute("pending"))
            aMenuitem.setAttribute("pending", aTab.getAttribute("pending"));
          else
            aMenuitem.removeAttribute("pending");

          if (aTab.selected)
            aMenuitem.setAttribute("selected", "true");
          else
            aMenuitem.removeAttribute("selected");

          function addEndImage() {
            let endImage = document.createElement("image");
            endImage.setAttribute("class", "alltabs-endimage");
            let endImageContainer = document.createElement("hbox");
            endImageContainer.setAttribute("align", "center");
            endImageContainer.setAttribute("pack", "center");
            endImageContainer.appendChild(endImage);
            aMenuitem.appendChild(endImageContainer);
            return endImage;
          }

          if (aMenuitem.firstChild)
            aMenuitem.firstChild.remove();
          if (aTab.hasAttribute("muted"))
            addEndImage().setAttribute("muted", "true");
          else if (aTab.hasAttribute("soundplaying"))
            addEndImage().setAttribute("soundplaying", "true");
        ]]></body>
      </method>
    </implementation>

    <handlers>
      <handler event="popupshowing">
      <![CDATA[
        if (event.target.getAttribute("id") == "alltabs_containersMenuTab") {
          createUserContextMenu(event, {useAccessKeys: false});
          return;
        }

        let containersEnabled = Services.prefs.getBoolPref("privacy.userContext.enabled");

        if (event.target.getAttribute("anonid") == "newtab-popup" ||
            event.target.id == "newtab-popup") {
          createUserContextMenu(event, {
            useAccessKeys: false,
            showDefaultTab: Services.prefs.getIntPref("privacy.userContext.longPressBehavior") == 1
          });
        } else {
          document.getElementById("alltabs-popup-separator-1").hidden = !containersEnabled;
          let containersTab = document.getElementById("alltabs_containersTab");

          containersTab.hidden = !containersEnabled;
          if (PrivateBrowsingUtils.isWindowPrivate(window)) {
            containersTab.setAttribute("disabled", "true");
          }

          document.getElementById("alltabs_undoCloseTab").disabled =
            SessionStore.getClosedTabCount(window) == 0;

          var tabcontainer = gBrowser.tabContainer;

          // Listen for changes in the tab bar.
          tabcontainer.addEventListener("TabAttrModified", this);
          tabcontainer.addEventListener("TabClose", this);

          let tabs = gBrowser.visibleTabs;
          for (var i = 0; i < tabs.length; i++) {
            if (!tabs[i].pinned)
              this._createTabMenuItem(tabs[i]);
          }
          this._updateTabsVisibilityStatus();
        }
      ]]></handler>

      <handler event="popuphidden">
      <![CDATA[
        if (event.target.getAttribute("id") == "alltabs_containersMenuTab") {
          return;
        }

        // clear out the menu popup and remove the listeners
        for (let i = this.childNodes.length - 1; i > 0; i--) {
          let menuItem = this.childNodes[i];
          if (menuItem.tab) {
            menuItem.tab.mCorrespondingMenuitem = null;
            this.removeChild(menuItem);
          }
          if (menuItem.hasAttribute("usercontextid")) {
            this.removeChild(menuItem);
          }
        }
        var tabcontainer = gBrowser.tabContainer;
        tabcontainer.removeEventListener("TabAttrModified", this);
        tabcontainer.removeEventListener("TabClose", this);
      ]]></handler>

      <handler event="DOMMenuItemActive">
      <![CDATA[
        var tab = event.target.tab;
        if (tab) {
          let overLink = tab.linkedBrowser.currentURI.displaySpec;
          if (overLink == "about:blank")
            overLink = "";
          XULBrowserWindow.setOverLink(overLink, null);
        }
      ]]></handler>

      <handler event="DOMMenuItemInactive">
      <![CDATA[
        XULBrowserWindow.setOverLink("", null);
      ]]></handler>

      <handler event="command"><![CDATA[
        if (event.target.tab) {
          if (gBrowser.selectedTab != event.target.tab) {
            gBrowser.selectedTab = event.target.tab;
          } else {
            gBrowser.tabContainer._handleTabSelect();
          }
        }
      ]]></handler>

    </handlers>
  </binding>

  <binding id="statuspanel" display="xul:hbox">
    <content>
      <xul:hbox class="statuspanel-inner">
        <xul:label class="statuspanel-label"
                   role="status"
                   aria-live="off"
                   xbl:inherits="value=label,crop,mirror"
                   flex="1"
                   crop="end"/>
      </xul:hbox>
    </content>

    <implementation implements="nsIDOMEventListener">
      <constructor><![CDATA[
        window.addEventListener("resize", this);
      ]]></constructor>

      <destructor><![CDATA[
        window.removeEventListener("resize", this);
        MousePosTracker.removeListener(this);
      ]]></destructor>

      <property name="label">
        <setter><![CDATA[
          if (!this.label) {
            this.removeAttribute("mirror");
            this.removeAttribute("sizelimit");
          }

          if (this.getAttribute("type") == "status" &&
              this.getAttribute("previoustype") == "status") {
            // Before updating the label, set the panel's current width as its
            // min-width to let the panel grow but not shrink and prevent
            // unnecessary flicker while loading pages. We only care about the
            // panel's width once it has been painted, so we can do this
            // without flushing layout.
            this.style.minWidth =
              window.QueryInterface(Ci.nsIInterfaceRequestor)
                    .getInterface(Ci.nsIDOMWindowUtils)
                    .getBoundsWithoutFlushing(this).width + "px";
          } else {
            this.style.minWidth = "";
          }

          if (val) {
            this.setAttribute("label", val);
            this.removeAttribute("inactive");
            this._mouseTargetRect = null;
            MousePosTracker.addListener(this);
          } else {
            this.setAttribute("inactive", "true");
            MousePosTracker.removeListener(this);
          }

          return val;
        ]]></setter>
        <getter>
          return this.hasAttribute("inactive") ? "" : this.getAttribute("label");
        </getter>
      </property>

      <method name="getMouseTargetRect">
        <body><![CDATA[
          if (!this._mouseTargetRect) {
            this._calcMouseTargetRect();
          }
          return this._mouseTargetRect;
        ]]></body>
      </method>

      <method name="onMouseEnter">
        <body>
          this._mirror();
        </body>
      </method>

      <method name="onMouseLeave">
        <body>
          this._mirror();
        </body>
      </method>

      <method name="handleEvent">
        <parameter name="event"/>
        <body><![CDATA[
          if (!this.label)
            return;

          switch (event.type) {
            case "resize":
              this._mouseTargetRect = null;
              break;
          }
        ]]></body>
      </method>

      <method name="_calcMouseTargetRect">
        <body><![CDATA[
          let container = this.parentNode;
          let alignRight = (getComputedStyle(container).direction == "rtl");
          let panelRect = this.getBoundingClientRect();
          let containerRect = container.getBoundingClientRect();

          this._mouseTargetRect = {
            top:    panelRect.top,
            bottom: panelRect.bottom,
            left:   alignRight ? containerRect.right - panelRect.width : containerRect.left,
            right:  alignRight ? containerRect.right : containerRect.left + panelRect.width
          };
        ]]></body>
      </method>

      <method name="_mirror">
        <body>
          if (this.hasAttribute("mirror"))
            this.removeAttribute("mirror");
          else
            this.setAttribute("mirror", "true");

          if (!this.hasAttribute("sizelimit")) {
            this.setAttribute("sizelimit", "true");
            this._mouseTargetRect = null;
          }
        </body>
      </method>
    </implementation>
  </binding>

  <binding id="tabbrowser-tabpanels"
           extends="chrome://global/content/bindings/tabbox.xml#tabpanels">
    <implementation>
      <field name="_selectedIndex">0</field>

      <property name="selectedIndex">
        <getter>
        <![CDATA[
          return this._selectedIndex;
        ]]>
        </getter>

        <setter>
        <![CDATA[
          if (val < 0 || val >= this.childNodes.length)
            return val;

          let toTab = this.getRelatedElement(this.childNodes[val]);

          gBrowser._getSwitcher().requestTab(toTab);

          var panel = this._selectedPanel;
          var newPanel = this.childNodes[val];
          this._selectedPanel = newPanel;
          if (this._selectedPanel != panel) {
            var event = document.createEvent("Events");
            event.initEvent("select", true, true);
            this.dispatchEvent(event);

            this._selectedIndex = val;
          }

          return val;
        ]]>
        </setter>
      </property>
    </implementation>
  </binding>

  <binding id="tabbrowser-browser"
           extends="chrome://global/content/bindings/browser.xml#browser">
    <implementation>
      <field name="tabModalPromptBox">null</field>

      <!-- throws exception for unknown schemes -->
      <method name="loadURIWithFlags">
        <parameter name="aURI"/>
        <parameter name="aFlags"/>
        <parameter name="aReferrerURI"/>
        <parameter name="aCharset"/>
        <parameter name="aPostData"/>
        <body>
          <![CDATA[
            var params = arguments[1];
            if (typeof(params) == "number") {
              params = {
                flags: aFlags,
                referrerURI: aReferrerURI,
                charset: aCharset,
                postData: aPostData,
              };
            }
            _loadURIWithFlags(this, aURI, params);
          ]]>
        </body>
      </method>
    </implementation>
  </binding>

  <binding id="tabbrowser-remote-browser"
           extends="chrome://global/content/bindings/remote-browser.xml#remote-browser">
    <implementation>
      <field name="tabModalPromptBox">null</field>

      <!-- throws exception for unknown schemes -->
      <method name="loadURIWithFlags">
        <parameter name="aURI"/>
        <parameter name="aFlags"/>
        <parameter name="aReferrerURI"/>
        <parameter name="aCharset"/>
        <parameter name="aPostData"/>
        <body>
          <![CDATA[
            var params = arguments[1];
            if (typeof(params) == "number") {
              params = {
                flags: aFlags,
                referrerURI: aReferrerURI,
                charset: aCharset,
                postData: aPostData,
              };
            }
            _loadURIWithFlags(this, aURI, params);
          ]]>
        </body>
      </method>
    </implementation>
  </binding>

</bindings>
PK
!<hIu�K�K6chrome/browser/content/browser/translation-infobar.xml<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 bindings [
<!ENTITY % notificationDTD SYSTEM "chrome://global/locale/notification.dtd">
%notificationDTD;
<!ENTITY % translationDTD SYSTEM "chrome://browser/locale/translation.dtd" >
%translationDTD;
]>

<bindings id="translationBindings"
          xmlns="http://www.mozilla.org/xbl"
          xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
          xmlns:xbl="http://www.mozilla.org/xbl">
  <binding id="translationbar" extends="chrome://global/content/bindings/notification.xml#notification">
    <resources>
      <stylesheet src="chrome://global/skin/notification.css"/>
    </resources>
    <content>
      <xul:hbox anonid="details" align="center" flex="1">
        <xul:image class="translate-infobar-element messageImage"
                   anonid="messageImage"/>
        <xul:panel anonid="welcomePanel" class="translation-welcome-panel"
                   type="arrow" align="start">
          <xul:image class="translation-welcome-logo"/>
          <xul:vbox flex="1" class="translation-welcome-content">
            <xul:description class="translation-welcome-headline"
                             anonid="welcomeHeadline"/>
            <xul:description class="translation-welcome-body" anonid="welcomeBody"/>
            <xul:hbox align="center">
              <xul:label anonid="learnMore" class="plain text-link"
                         onclick="openUILinkIn('https://support.mozilla.org/kb/automatic-translation', 'tab'); this.parentNode.parentNode.parentNode.hidePopup();"/>
              <xul:spacer flex="1"/>
              <xul:button class="translate-infobar-element" anonid="thanksButton"
                          onclick="this.parentNode.parentNode.parentNode.hidePopup();"/>
            </xul:hbox>
          </xul:vbox>
        </xul:panel>
        <xul:deck anonid="translationStates" selectedIndex="0">

          <!-- offer to translate -->
          <xul:hbox class="translate-offer-box" align="center">
            <xul:label class="translate-infobar-element" value="&translation.thisPageIsIn.label;"/>
            <xul:menulist class="translate-infobar-element" anonid="detectedLanguage">
              <xul:menupopup/>
            </xul:menulist>
            <xul:label class="translate-infobar-element" value="&translation.translateThisPage.label;"/>
            <xul:button class="translate-infobar-element"
                        label="&translation.translate.button;"
                        anonid="translate"
                        oncommand="document.getBindingParent(this).translate();"/>
            <xul:button class="translate-infobar-element"
                        label="&translation.notNow.button;" anonid="notNow"
                        oncommand="document.getBindingParent(this).closeCommand();"/>
          </xul:hbox>

          <!-- translating -->
          <xul:vbox class="translating-box" pack="center">
            <xul:label class="translate-infobar-element"
                       value="&translation.translatingContent.label;"/>
          </xul:vbox>

          <!-- translated -->
          <xul:hbox class="translated-box" align="center">
            <xul:label class="translate-infobar-element"
                       value="&translation.translatedFrom.label;"/>
            <xul:menulist class="translate-infobar-element"
                          anonid="fromLanguage"
                          oncommand="document.getBindingParent(this).translate()">
              <xul:menupopup/>
            </xul:menulist>
            <xul:label class="translate-infobar-element"
                       value="&translation.translatedTo.label;"/>
            <xul:menulist class="translate-infobar-element"
                          anonid="toLanguage"
                          oncommand="document.getBindingParent(this).translate()">
              <xul:menupopup/>
            </xul:menulist>
            <xul:label class="translate-infobar-element"
                       value="&translation.translatedToSuffix.label;"/>
            <xul:button anonid="showOriginal"
                        class="translate-infobar-element"
                        label="&translation.showOriginal.button;"
                        oncommand="document.getBindingParent(this).showOriginal();"/>
            <xul:button anonid="showTranslation"
                        class="translate-infobar-element"
                        label="&translation.showTranslation.button;"
                        oncommand="document.getBindingParent(this).showTranslation();"/>
          </xul:hbox>

          <!-- error -->
          <xul:hbox class="translation-error" align="center">
            <xul:label class="translate-infobar-element"
                       value="&translation.errorTranslating.label;"/>
            <xul:button class="translate-infobar-element"
                        label="&translation.tryAgain.button;"
                        anonid="tryAgain"
                        oncommand="document.getBindingParent(this).translate();"/>
          </xul:hbox>

          <!-- unavailable -->
          <xul:vbox class="translation-unavailable" pack="center">
            <xul:label class="translate-infobar-element"
                       value="&translation.serviceUnavailable.label;"/>
          </xul:vbox>

        </xul:deck>
        <xul:spacer flex="1"/>

        <xul:button type="menu"
                    class="translate-infobar-element options-menu-button"
                    anonid="options"
                    label="&translation.options.menu;">
          <xul:menupopup class="translation-menupopup cui-widget-panel cui-widget-panelview
                                cui-widget-panelWithFooter PanelUI-subView"
                         onpopupshowing="document.getBindingParent(this).optionsShowing();">
            <xul:menuitem anonid="neverForLanguage"
                          oncommand="document.getBindingParent(this).neverForLanguage();"/>
            <xul:menuitem anonid="neverForSite"
                          oncommand="document.getBindingParent(this).neverForSite();"
                          label="&translation.options.neverForSite.label;"
                          accesskey="&translation.options.neverForSite.accesskey;"/>
            <xul:menuseparator/>
            <xul:menuitem oncommand="openPreferences('paneGeneral', {origin:'translationInfobar'});"
                          label="&translation.options.preferences.label;"
                          accesskey="&translation.options.preferences.accesskey;"/>
            <xul:menuitem class="subviewbutton panel-subview-footer"
                          oncommand="document.getBindingParent(this).openProviderAttribution();">
              <xul:deck anonid="translationEngine" selectedIndex="0">
                <xul:hbox class="translation-attribution">
                  <xul:label>&translation.options.attribution.beforeLogo;</xul:label>
                  <xul:image src="chrome://browser/content/microsoft-translator-attribution.png"
                             aria-label="Microsoft Translator"/>
                  <xul:label>&translation.options.attribution.afterLogo;</xul:label>
                </xul:hbox>
                <xul:label class="translation-attribution">&translation.options.attribution.yandexTranslate;</xul:label>
              </xul:deck>
            </xul:menuitem>
          </xul:menupopup>
        </xul:button>

      </xul:hbox>
      <xul:toolbarbutton ondblclick="event.stopPropagation();"
                         anonid="closeButton"
                         class="messageCloseButton close-icon tabbable"
                         xbl:inherits="hidden=hideclose"
                         tooltiptext="&closeNotification.tooltip;"
                         oncommand="document.getBindingParent(this).closeCommand();"/>
    </content>
    <implementation>
      <property name="state"
                onget="return this._getAnonElt('translationStates').selectedIndex;">
        <setter>
          <![CDATA[
          let deck = this._getAnonElt("translationStates");

          let activeElt = document.activeElement;
          if (activeElt && deck.contains(activeElt))
            activeElt.blur();

          let stateName;
          for (let name of ["OFFER", "TRANSLATING", "TRANSLATED", "ERROR"]) {
            if (Translation["STATE_" + name] == val) {
              stateName = name.toLowerCase();
              break;
            }
          }
          this.setAttribute("state", stateName);

          if (val == Translation.STATE_TRANSLATED)
            this._handleButtonHiding();

          deck.selectedIndex = val;
          ]]>
        </setter>
      </property>

      <method name="init">
        <parameter name="aTranslation"/>
        <body>
          <![CDATA[
            this.translation = aTranslation;
            let bundle = Services.strings.createBundle("chrome://global/locale/languageNames.properties");
            let sortByLocalizedName = function(aList) {
              return aList.map(code => [code, bundle.GetStringFromName(code)])
                          .sort((a, b) => a[1].localeCompare(b[1]));
            };

            // Fill the lists of supported source languages.
            let detectedLanguage = this._getAnonElt("detectedLanguage");
            let fromLanguage = this._getAnonElt("fromLanguage");
            let sourceLanguages =
              sortByLocalizedName(Translation.supportedSourceLanguages);
            for (let [code, name] of sourceLanguages) {
              detectedLanguage.appendItem(name, code);
              fromLanguage.appendItem(name, code);
            }
            detectedLanguage.value = this.translation.detectedLanguage;

            // translatedFrom is only set if we have already translated this page.
            if (aTranslation.translatedFrom)
              fromLanguage.value = aTranslation.translatedFrom;

            // Fill the list of supported target languages.
            let toLanguage = this._getAnonElt("toLanguage");
            let targetLanguages =
              sortByLocalizedName(Translation.supportedTargetLanguages);
            for (let [code, name] of targetLanguages)
              toLanguage.appendItem(name, code);

            if (aTranslation.translatedTo)
              toLanguage.value = aTranslation.translatedTo;

            if (aTranslation.state)
              this.state = aTranslation.state;

            // Show attribution for the preferred translator.
            let engineIndex = Object.keys(Translation.supportedEngines)
              .indexOf(Translation.translationEngine);
            if (engineIndex != -1) {
              this._getAnonElt("translationEngine").selectedIndex = engineIndex;
            }

            const kWelcomePref = "browser.translation.ui.welcomeMessageShown";
            if (Services.prefs.prefHasUserValue(kWelcomePref) ||
                this.translation.browser != gBrowser.selectedBrowser)
              return;

            this.addEventListener("transitionend", function() {
              // These strings are hardcoded because they need to reach beta
              // without riding the trains.
              let localizedStrings = {
                en: ["Hey look! It's something new!",
                     "Now the Web is even more accessible with our new in-page translation feature. Click the translate button to try it!",
                     "Learn more.",
                     "Thanks"],
                "es-AR": ["\xA1Mir\xE1! \xA1Hay algo nuevo!",
                          "Ahora la web es a\xFAn m\xE1s accesible con nuestra nueva funcionalidad de traducci\xF3n integrada. \xA1Hac\xE9 clic en el bot\xF3n traducir para probarla!",
                          "Conoc\xE9 m\xE1s.",
                          "Gracias"],
                "es-ES": ["\xA1Mira! \xA1Hay algo nuevo!",
                          "Con la nueva funcionalidad de traducci\xF3n integrada, ahora la Web es a\xFAn m\xE1s accesible. \xA1Pulsa el bot\xF3n Traducir y pru\xE9bala!",
                          "M\xE1s informaci\xF3n.",
                          "Gracias"],
                pl: ["Sp\xF3jrz tutaj! To co\u015B nowego!",
                     "Sie\u0107 sta\u0142a si\u0119 w\u0142a\u015Bnie jeszcze bardziej dost\u0119pna dzi\u0119ki opcji bezpo\u015Bredniego t\u0142umaczenia stron. Kliknij przycisk t\u0142umaczenia, aby spr\xF3bowa\u0107!",
                     "Dowiedz si\u0119 wi\u0119cej",
                     "Dzi\u0119kuj\u0119"],
                tr: ["Bak\u0131n, burada yeni bir \u015Fey var!",
                     "Yeni sayfa i\xE7i \xE7eviri \xF6zelli\u011Fimiz sayesinde Web art\u0131k \xE7ok daha anla\u015F\u0131l\u0131r olacak. Denemek i\xE7in \xC7evir d\xFC\u011Fmesine t\u0131klay\u0131n!",
                     "Daha fazla bilgi al\u0131n.",
                     "Te\u015Fekk\xFCrler"],
                vi: ["Nh\xECn n\xE0y! \u0110\u1ED3 m\u1EDBi!",
                     "Gi\u1EDD \u0111\xE2y ch\xFAng ta c\xF3 th\u1EC3 ti\u1EBFp c\u1EADn web d\u1EC5 d\xE0ng h\u01A1n n\u1EEFa v\u1EDBi t\xEDnh n\u0103ng d\u1ECBch ngay trong trang.  Hay nh\u1EA5n n\xFAt d\u1ECBch \u0111\u1EC3 th\u1EED!",
                     "T\xECm hi\u1EC3u th\xEAm.",
                     "C\u1EA3m \u01A1n"]
              };

              let locale = Services.locale.getAppLocaleAsLangTag();
              if (!(locale in localizedStrings))
                locale = "en";
              let strings = localizedStrings[locale];

              this._getAnonElt("welcomeHeadline").setAttribute("value", strings[0]);
              this._getAnonElt("welcomeBody").textContent = strings[1];
              this._getAnonElt("learnMore").setAttribute("value", strings[2]);
              this._getAnonElt("thanksButton").setAttribute("label", strings[3]);

              let panel = this._getAnonElt("welcomePanel");
              panel.openPopup(this._getAnonElt("messageImage"),
                              "bottomcenter topleft");

              Services.prefs.setBoolPref(kWelcomePref, true);
            }, {once: true});
          ]]>
        </body>
      </method>

      <method name="_getAnonElt">
        <parameter name="aAnonId"/>
        <body>
          return document.getAnonymousElementByAttribute(this, "anonid", aAnonId);
        </body>
      </method>

      <method name="translate">
        <body>
          <![CDATA[
            if (this.state == Translation.STATE_OFFER) {
              this._getAnonElt("fromLanguage").value =
                this._getAnonElt("detectedLanguage").value;
              this._getAnonElt("toLanguage").value =
                Translation.defaultTargetLanguage;
            }

            this.translation.translate(this._getAnonElt("fromLanguage").value,
                                       this._getAnonElt("toLanguage").value);
          ]]>
        </body>
      </method>

      <!-- To be called when the infobar should be closed per user's wish (e.g.
           by clicking the notification's close button -->
      <method name="closeCommand">
        <body>
          <![CDATA[
            this.close();
            this.translation.infobarClosed();
          ]]>
        </body>
      </method>
      <method name="_handleButtonHiding">
        <body>
          <![CDATA[
            let originalShown = this.translation.originalShown;
            this._getAnonElt("showOriginal").hidden = originalShown;
            this._getAnonElt("showTranslation").hidden = !originalShown;
          ]]>
        </body>
      </method>

      <method name="showOriginal">
        <body>
          <![CDATA[
            this.translation.showOriginalContent();
            this._handleButtonHiding();
          ]]>
        </body>
      </method>

      <method name="showTranslation">
        <body>
          <![CDATA[
            this.translation.showTranslatedContent();
            this._handleButtonHiding();
          ]]>
        </body>
      </method>

      <method name="optionsShowing">
        <body>
          <![CDATA[
            // Get the source language name.
            let lang;
            if (this.state == Translation.STATE_OFFER)
              lang = this._getAnonElt("detectedLanguage").value;
            else {
              lang = this._getAnonElt("fromLanguage").value;

              // If we have never attempted to translate the page before the
              // service became unavailable, "fromLanguage" isn't set.
              if (!lang && this.state == Translation.STATE_UNAVAILABLE)
                lang = this.translation.detectedLanguage;
            }

            let langBundle =
              Services.strings.createBundle("chrome://global/locale/languageNames.properties");
            let langName = langBundle.GetStringFromName(lang);

            // Set the label and accesskey on the menuitem.
            let bundle =
              Services.strings.createBundle("chrome://browser/locale/translation.properties");
            let item = this._getAnonElt("neverForLanguage");
            const kStrId = "translation.options.neverForLanguage";
            item.setAttribute("label",
                              bundle.formatStringFromName(kStrId + ".label",
                                                          [langName], 1));
            item.setAttribute("accesskey",
                              bundle.GetStringFromName(kStrId + ".accesskey"));
            item.langCode = lang;

            // We may need to disable the menuitems if they have already been used.
            // Check if translation is already disabled for this language:
            let neverForLangs =
              Services.prefs.getCharPref("browser.translation.neverForLanguages");
            item.disabled = neverForLangs.split(",").includes(lang);

            // Check if translation is disabled for the domain:
            let uri = this.translation.browser.currentURI;
            let perms = Services.perms;
            item = this._getAnonElt("neverForSite");
            item.disabled =
              perms.testExactPermission(uri, "translate") == perms.DENY_ACTION;
          ]]>
        </body>
      </method>

      <method name="neverForLanguage">
        <body>
          <![CDATA[
            const kPrefName = "browser.translation.neverForLanguages";

            let val = Services.prefs.getCharPref(kPrefName);
            if (val)
              val += ",";
            val += this._getAnonElt("neverForLanguage").langCode;

            Services.prefs.setCharPref(kPrefName, val);

            this.closeCommand();
          ]]>
        </body>
      </method>

      <method name="neverForSite">
        <body>
          <![CDATA[
            let uri = this.translation.browser.currentURI;
            let perms = Services.perms;
            perms.add(uri, "translate", perms.DENY_ACTION);

            this.closeCommand();
          ]]>
        </body>
      </method>

      <method name="openProviderAttribution">
        <body>
          <![CDATA[
            Translation.openProviderAttribution();
          ]]>
        </body>
      </method>

    </implementation>
  </binding>
</bindings>
PK
!<�Ne����1chrome/browser/content/browser/urlbarBindings.xml<?xml version="1.0"?>

<!--
-*- Mode: HTML -*-
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.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 bindings [
<!ENTITY % notificationDTD SYSTEM "chrome://global/locale/notification.dtd">
%notificationDTD;
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
%browserDTD;
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
%brandDTD;
]>

<bindings id="urlbarBindings" xmlns="http://www.mozilla.org/xbl"
          xmlns:html="http://www.w3.org/1999/xhtml"
          xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
          xmlns:xbl="http://www.mozilla.org/xbl">

  <binding id="urlbar" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete">

    <content sizetopopup="pref">
      <xul:hbox flex="1" class="urlbar-textbox-container">
        <children includes="image|deck|stack|box"/>
        <xul:hbox anonid="textbox-input-box"
                  class="textbox-input-box urlbar-input-box"
                  flex="1" xbl:inherits="tooltiptext=inputtooltiptext">
          <children/>
          <html:input anonid="input"
                      class="autocomplete-textbox urlbar-input textbox-input"
                      allowevents="true"
                      inputmode="mozAwesomebar"
                      xbl:inherits="tooltiptext=inputtooltiptext,value,maxlength,disabled,size,readonly,placeholder,tabindex,accesskey,focused,textoverflow"/>
        </xul:hbox>
        <xul:image anonid="urlbar-go-button"
                   class="urlbar-go-button urlbar-icon"
                   onclick="gURLBar.handleCommand(event);"
                   tooltiptext="&goEndCap.tooltip;"
                   xbl:inherits="pageproxystate,parentfocused=focused,usertyping"/>
        <xul:dropmarker anonid="historydropmarker"
                        class="urlbar-history-dropmarker urlbar-icon chromeclass-toolbar-additional"
                        tooltiptext="&urlbar.openHistoryPopup.tooltip;"
                        allowevents="true"
                        xbl:inherits="open,parentfocused=focused,usertyping"/>
        <children includes="hbox"/>
      </xul:hbox>
      <xul:popupset anonid="popupset"
                    class="autocomplete-result-popupset"/>
      <children includes="toolbarbutton"/>
    </content>

    <implementation implements="nsIObserver, nsIDOMEventListener">
      <field name="ExtensionSearchHandler" readonly="true">
        (ChromeUtils.import("resource://gre/modules/ExtensionSearchHandler.jsm", {})).ExtensionSearchHandler;
      </field>

      <constructor><![CDATA[
        this._prefs = Cc["@mozilla.org/preferences-service;1"]
                        .getService(Ci.nsIPrefService)
                        .getBranch("browser.urlbar.");
        this._prefs.addObserver("", this);

        this._defaultPrefs = Cc["@mozilla.org/preferences-service;1"]
                               .getService(Ci.nsIPrefService)
                               .getDefaultBranch("browser.urlbar.");

        Services.prefs.addObserver("browser.search.suggest.enabled", this);
        this.browserSearchSuggestEnabled = Services.prefs.getBoolPref("browser.search.suggest.enabled");

        this.openInTab = this._prefs.getBoolPref("openintab");
        this.clickSelectsAll = this._prefs.getBoolPref("clickSelectsAll");
        this.doubleClickSelectsAll = this._prefs.getBoolPref("doubleClickSelectsAll");
        this.completeDefaultIndex = this._prefs.getBoolPref("autoFill");
        this.speculativeConnectEnabled = this._prefs.getBoolPref("speculativeConnect.enabled");
        this.urlbarSearchSuggestEnabled = this._prefs.getBoolPref("suggest.searches");
        this.timeout = this._prefs.getIntPref("delay");
        this._formattingEnabled = this._prefs.getBoolPref("formatting.enabled");
        this._mayTrimURLs = this._prefs.getBoolPref("trimURLs");
        this._adoptIntoActiveWindow = this._prefs.getBoolPref("switchTabs.adoptIntoActiveWindow");
        this.inputField.controllers.insertControllerAt(0, this._copyCutController);
        this.inputField.addEventListener("paste", this);
        this.inputField.addEventListener("mousedown", this);
        this.inputField.addEventListener("mousemove", this);
        this.inputField.addEventListener("mouseout", this);
        this.inputField.addEventListener("overflow", this);
        this.inputField.addEventListener("underflow", this);

        var textBox = document.getAnonymousElementByAttribute(this,
                                                "anonid", "textbox-input-box");
        var cxmenu = document.getAnonymousElementByAttribute(textBox,
                                            "anonid", "input-box-contextmenu");
        var pasteAndGo;
        cxmenu.addEventListener("popupshowing", function() {
          if (!pasteAndGo)
            return;
          var controller = document.commandDispatcher.getControllerForCommand("cmd_paste");
          var enabled = controller.isCommandEnabled("cmd_paste");
          if (enabled)
            pasteAndGo.removeAttribute("disabled");
          else
            pasteAndGo.setAttribute("disabled", "true");
        });

        var insertLocation = cxmenu.firstChild;
        while (insertLocation.nextSibling &&
               insertLocation.getAttribute("cmd") != "cmd_paste")
          insertLocation = insertLocation.nextSibling;
        if (insertLocation) {
          pasteAndGo = document.createElement("menuitem");
          let label = Services.strings.createBundle("chrome://browser/locale/browser.properties").
                                   GetStringFromName("pasteAndGo.label");
          pasteAndGo.setAttribute("label", label);
          pasteAndGo.setAttribute("anonid", "paste-and-go");
          pasteAndGo.setAttribute("oncommand",
              "gURLBar.select(); goDoCommand('cmd_paste'); gURLBar.handleCommand();");
          cxmenu.insertBefore(pasteAndGo, insertLocation.nextSibling);
        }

        this.popup.addEventListener("popupshowing", () => {
          this._enableOrDisableOneOffSearches();
        }, {capturing: true, once: true});

        // history dropmarker open state
        this.popup.addEventListener("popupshowing", () => {
          this.setAttribute("open", "true");
        });
        this.popup.addEventListener("popuphidden", () => {
          requestAnimationFrame(() => {
            this.removeAttribute("open");
          });
        });
      ]]></constructor>

      <destructor><![CDATA[
        // Somehow, it's possible for the XBL destructor to fire without the
        // constructor ever having fired. Fix:
        if (!this._prefs) {
          return;
        }
        this._prefs.removeObserver("", this);
        this._prefs = null;
        Services.prefs.removeObserver("browser.search.suggest.enabled", this);
        this.inputField.controllers.removeController(this._copyCutController);
        this.inputField.removeEventListener("paste", this);
        this.inputField.removeEventListener("mousedown", this);
        this.inputField.removeEventListener("mousemove", this);
        this.inputField.removeEventListener("mouseout", this);
        this.inputField.removeEventListener("overflow", this);
        this.inputField.removeEventListener("underflow", this);

        if (this._deferredKeyEventTimeout) {
          clearTimeout(this._deferredKeyEventTimeout);
          this._deferredKeyEventTimeout = null;
        }

        // Null out the one-offs' popup and textbox so that it cleans up its
        // internal state for both.  Most importantly, it removes the event
        // listeners that it added to both.
        this.popup.oneOffSearchButtons.popup = null;
        this.popup.oneOffSearchButtons.textbox = null;
      ]]></destructor>

      <field name="goButton">
        document.getAnonymousElementByAttribute(this, "anonid", "urlbar-go-button");
      </field>

      <field name="_value">""</field>
      <field name="gotResultForCurrentQuery">false</field>

      <!--
        This is set around HandleHenter so it can be used in handleCommand.
        It is also used to track whether we must handle a delayed handleEnter,
        by checking if it has been cleared.
      -->
      <field name="handleEnterInstance">null</field>

      <!--
        For performance reasons we want to limit the size of the text runs we
        build and show to the user.
      -->
      <field name="textRunsMaxLen">255</field>

      <!--
        Since we never want scrollbars, we always use the maxResults value.
      -->
      <property name="maxRows"
                onget="return this.popup.maxResults;"/>

      <!--
        Set by focusAndSelectUrlBar to indicate whether the next focus event was
        initiated by an explicit user action. See the "focus" handler below.
      -->
      <field name="userInitiatedFocus">false</field>

      <!--
        onBeforeValueGet is called by the base-binding's .value getter.
        It can return an object with a "value" property, to override the
        return value of the getter.
      -->
      <method name="onBeforeValueGet">
        <body><![CDATA[
          return { value: this._value };
        ]]></body>
      </method>

      <!--
        onBeforeValueSet is called by the base-binding's .value setter.
        It should return the value that the setter should use.
      -->
      <method name="onBeforeValueSet">
        <parameter name="aValue"/>
        <body><![CDATA[
          this._value = aValue;
          var returnValue = aValue;
          var action = this._parseActionUrl(aValue);

          if (action) {
            switch (action.type) {
              case "switchtab": // Fall through.
              case "remotetab": // Fall through.
              case "visiturl": {
                returnValue = action.params.displayUrl;
                break;
              }
              case "keyword": // Fall through.
              case "searchengine": {
                returnValue = action.params.input;
                break;
              }
              case "extension": {
                returnValue = action.params.content;
                break;
              }
            }
          } else {
            let originalUrl = ReaderMode.getOriginalUrlObjectForDisplay(aValue);
            if (originalUrl) {
              returnValue = originalUrl.displaySpec;
            }
          }

          // Set the actiontype only if the user is not overriding actions.
          if (action && this._pressedNoActionKeys.size == 0) {
            this.setAttribute("actiontype", action.type);
          } else {
            this.removeAttribute("actiontype");
          }
          return returnValue;
        ]]></body>
      </method>

      <method name="onKeyPress">
        <parameter name="aEvent"/>
        <body><![CDATA[
          switch (aEvent.keyCode) {
            case KeyEvent.DOM_VK_LEFT:
            case KeyEvent.DOM_VK_RIGHT:
            case KeyEvent.DOM_VK_HOME:
              // Reset the selected index so that nsAutoCompleteController
              // simply closes the popup without trying to fill anything.
              this.popup.selectedIndex = -1;
              break;
            case KeyEvent.DOM_VK_TAB:
              this.userSelectionBehavior = "tab";
              break;
            case KeyEvent.DOM_VK_UP:
            case KeyEvent.DOM_VK_DOWN:
            case KeyEvent.DOM_VK_PAGE_UP:
            case KeyEvent.DOM_VK_PAGE_DOWN:
              if (this.userSelectionBehavior != "tab")
                this.userSelectionBehavior = "arrow";
              break;
          }
          if (!this.popup.disableKeyNavigation) {
            if (this._shouldDeferKeyEvent(aEvent)) {
              this._deferKeyEvent(aEvent, "onKeyPress");
              return false;
            }
            if (this.popup.popupOpen && this.popup.handleKeyPress(aEvent)) {
              return true;
            }
          }
          return this.handleKeyPress(aEvent);
        ]]></body>
      </method>

      <!--
        Search results arrive asynchronously, which means that keypresses may
        arrive before results do and therefore not have the effect the user
        intends.  That's especially likely to happen with the down arrow and
        enter keys due to the one-off search buttons: if the user very quickly
        pastes something in the input, presses the down arrow key, and then hits
        enter, they are probably expecting to visit the first result.  But if
        there are no results, then pressing down and enter will trigger the
        first one-off button.  To prevent that undesirable behavior, certain
        keys are buffered and deferred until more results arrive, at which time
        they're replayed.

        @param  event
                The key event that should maybe be deferred.
        @return True if the event should be deferred, false if not.
       -->
      <method name="_shouldDeferKeyEvent">
        <parameter name="event"/>
        <body><![CDATA[
          // If any event has been deferred for this search, then defer all
          // subsequent events so that the user does not experience any
          // keypresses out of order.  All events will be replayed when this
          // timeout fires.
          if (this._deferredKeyEventTimeout) {
            return true;
          }

          // At this point, no events have been deferred for this search, and we
          // need to decide whether `event` is the first one that should be.

          if (!this._keyCodesToDefer.has(event.keyCode)) {
            // Not a key that should trigger deferring.
            return false;
          }

          let waitedLongEnough =
            this._searchStartDate + this._deferredKeyEventTimeoutMs <= Cu.now();
          if (waitedLongEnough) {
            // This is a key that we would defer, but enough time has passed
            // since the start of the search that we don't want to block the
            // user's keypresses anymore.
            return false;
          }

          if (event.keyCode == KeyEvent.DOM_VK_TAB && !this.popupOpen) {
            // The popup is closed and the user pressed the Tab key.  The
            // focus should move out of the urlbar immediately.
            return false;
          }

          return !this._safeToPlayDeferredKeyEvent(event);
        ]]></body>
      </method>

      <!--
        Returns true if the given deferred key event can be played now without
        possibly surprising the user.  This depends on the state of the popup,
        its results, and the type of keypress.  Use this method only after
        determining that the event should be deferred, or after it's already
        been deferred and you want to know if it can be played now.

        @param  event
                The key event.
        @return True if the event can be played, false if not.
      -->
      <method name="_safeToPlayDeferredKeyEvent">
        <parameter name="event"/>
        <body><![CDATA[
          if (!this.gotResultForCurrentQuery || !this.popupOpen) {
            // We're still waiting on the first result, or the popup hasn't
            // opened yet, so not safe.
            return false;
          }

          let maxResultsRemaining =
            this.popup.maxResults - this.popup.matchCount;
          if (maxResultsRemaining == 0) {
            // The popup can't possibly have any more results, so there's no
            // need to defer any event now.
            return true;
          }

          if (event.keyCode == KeyEvent.DOM_VK_DOWN) {
            // Don't play the event if the last result is selected so that the
            // user doesn't accidentally arrow down into the one-off buttons
            // when they didn't mean to.
            let lastResultSelected =
              this.popup.selectedIndex + 1 == this.popup.matchCount;
            return !lastResultSelected;
          }

          return true;
        ]]></body>
      </method>

      <!--
        Adds a key event to the deferred event queue.

        @param event
               The key event to defer.
        @param methodName
               The name of the method on `this` to call.  It's expected to take
               a single argument, the event.
      -->
      <method name="_deferKeyEvent">
        <parameter name="event"/>
        <parameter name="methodName"/>
        <body><![CDATA[
          // Somehow event.defaultPrevented ends up true for deferred events.
          // autocomplete ignores defaultPrevented events, which means it would
          // ignore replayed deferred events if we didn't tell it to bypass
          // defaultPrevented.  That's the purpose of this expando.  If we could
          // figure out what's setting defaultPrevented and prevent it, then we
          // could get rid of this.
          event.urlbarDeferred = true;

          this._deferredKeyEventQueue.push({
            methodName,
            event,
            searchString: this.mController.searchString,
          });

          if (!this._deferredKeyEventTimeout) {
            // Start the timeout that will unconditionally replay all deferred
            // events when it fires so that, after a certain point, we don't
            // keep blocking the user's keypresses when nothing else has caused
            // the events to be replayed.  Do not check whether it's safe to
            // replay the events because otherwise it may look like we ignored
            // the user's input.
            let elapsed = Cu.now() - this._searchStartDate;
            let remaining = this._deferredKeyEventTimeoutMs - elapsed;
            if (remaining <= 0) {
              this.replayAllDeferredKeyEvents();
            } else {
              this._deferredKeyEventTimeout = setTimeout(() => {
                this.replayAllDeferredKeyEvents();
                this._deferredKeyEventTimeout = null;
              }, remaining);
            }
          }
        ]]></body>
      </method>

      <!-- The enter key is always deferred, so it's not included here. -->
      <field name="_keyCodesToDefer">new Set([
        KeyboardEvent.DOM_VK_DOWN,
        KeyboardEvent.DOM_VK_TAB,
      ])</field>
      <field name="_deferredKeyEventQueue">[]</field>
      <field name="_deferredKeyEventTimeout">null</field>
      <field name="_deferredKeyEventTimeoutMs">200</field>
      <field name="_searchStartDate">0</field>

      <method name="replaySafeDeferredKeyEvents">
        <body><![CDATA[
          if (!this._deferredKeyEventQueue.length) {
            return;
          }
          let instance = this._deferredKeyEventQueue[0];
          if (!this._safeToPlayDeferredKeyEvent(instance.event)) {
            return;
          }
          this._deferredKeyEventQueue.shift();
          this._replayKeyEventInstance(instance);
          Services.tm.dispatchToMainThread(() => {
            this.replaySafeDeferredKeyEvents();
          });
        ]]></body>
      </method>

      <!--
        Unconditionally replays all deferred key events.  This does not check
        whether it's safe to replay the events; use replaySafeDeferredKeyEvents
        for that.  Use this method when you must replay all events so that it
        does not appear that we ignored the user's input.
      -->
      <method name="replayAllDeferredKeyEvents">
        <body><![CDATA[
          let instance = this._deferredKeyEventQueue.shift();
          if (!instance) {
            return;
          }
          this._replayKeyEventInstance(instance);
          Services.tm.dispatchToMainThread(() => {
            this.replayAllDeferredKeyEvents();
          });
        ]]></body>
      </method>

      <method name="_replayKeyEventInstance">
        <parameter name="instance"/>
        <body><![CDATA[
          // Safety check: handle only if the search string didn't change.
          if (this.mController.searchString == instance.searchString) {
            this[instance.methodName](instance.event);
          }
        ]]></body>
      </method>

      <field name="_mayTrimURLs">true</field>
      <method name="trimValue">
        <parameter name="aURL"/>
        <body><![CDATA[
          // This method must not modify the given URL such that calling
          // nsIURIFixup::createFixupURI with the result will produce a different URI.
          return this._mayTrimURLs ? trimURL(aURL) : aURL;
        ]]></body>
      </method>

      <field name="_formattingEnabled">true</field>
      <method name="formatValue">
        <body><![CDATA[
          if (!this._formattingEnabled || !this.editor)
            return;

          let controller = this.editor.selectionController;
          let strikeOut = controller.getSelection(controller.SELECTION_URLSTRIKEOUT);
          strikeOut.removeAllRanges();

          let selection = controller.getSelection(controller.SELECTION_URLSECONDARY);
          selection.removeAllRanges();

          if (this.focused)
            return;

          let textNode = this.editor.rootElement.firstChild;
          let value = textNode.textContent;
          if (!value)
            return;

          // Get the URL from the fixup service:
          let flags = Services.uriFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS |
                      Services.uriFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP;
          let uriInfo;
          try {
            uriInfo = Services.uriFixup.getFixupURIInfo(value, flags);
          } catch (ex) {}
          // Ignore if we couldn't make a URI out of this, the URI resulted in a search,
          // or the URI has a non-http(s)/ftp protocol.
          if (!uriInfo ||
              !uriInfo.fixedURI ||
              uriInfo.keywordProviderName ||
              !["http", "https", "ftp"].includes(uriInfo.fixedURI.scheme)) {
            return;
          }

          // If we trimmed off the http scheme, ensure we stick it back on before
          // trying to figure out what domain we're accessing, so we don't get
          // confused by user:pass@host http URLs. We later use
          // trimmedLength to ensure we don't count the length of a trimmed protocol
          // when determining which parts of the URL to highlight as "preDomain".
          let trimmedLength = 0;
          if (uriInfo.fixedURI.scheme == "http" && !value.startsWith("http://")) {
            value = "http://" + value;
            trimmedLength = "http://".length;
          }

          let matchedURL = value.match(/^((?:[a-z]+:\/\/)(?:[^\/#?]+@)?)(\S+?)(?::\d+)?\s*(?:[\/#?]|$)/);
          if (!matchedURL)
            return;

          // Strike out the "https" part if mixed active content is loaded.
          if (this.getAttribute("pageproxystate") == "valid" &&
              value.startsWith("https:") &&
              gBrowser.securityUI.state &
                Ci.nsIWebProgressListener.STATE_LOADED_MIXED_ACTIVE_CONTENT) {
            let range = document.createRange();
            range.setStart(textNode, 0);
            range.setEnd(textNode, 5);
            strikeOut.addRange(range);
          }

          let [, preDomain, domain] = matchedURL;
          let baseDomain = domain;
          let subDomain = "";
          try {
            baseDomain = Services.eTLD.getBaseDomainFromHost(uriInfo.fixedURI.host);
            if (!domain.endsWith(baseDomain)) {
              // getBaseDomainFromHost converts its resultant to ACE.
              let IDNService = Cc["@mozilla.org/network/idn-service;1"]
                               .getService(Ci.nsIIDNService);
              baseDomain = IDNService.convertACEtoUTF8(baseDomain);
            }
          } catch (e) {}
          if (baseDomain != domain) {
            subDomain = domain.slice(0, -baseDomain.length);
          }

          let rangeLength = preDomain.length + subDomain.length - trimmedLength;
          if (rangeLength) {
            let range = document.createRange();
            range.setStart(textNode, 0);
            range.setEnd(textNode, rangeLength);
            selection.addRange(range);
          }

          let startRest = preDomain.length + domain.length - trimmedLength;
          if (startRest < value.length - trimmedLength) {
            let range = document.createRange();
            range.setStart(textNode, startRest);
            range.setEnd(textNode, value.length - trimmedLength);
            selection.addRange(range);
          }
        ]]></body>
      </method>

      <method name="handleRevert">
        <body><![CDATA[
          var isScrolling = this.popupOpen;

          gBrowser.userTypedValue = null;

          // don't revert to last valid url unless page is NOT loading
          // and user is NOT key-scrolling through autocomplete list
          if (!XULBrowserWindow.isBusy && !isScrolling) {
            URLBarSetURI();

            // If the value isn't empty and the urlbar has focus, select the value.
            if (this.value && this.hasAttribute("focused"))
              this.select();
          }

          // tell widget to revert to last typed text only if the user
          // was scrolling when they hit escape
          return !isScrolling;
        ]]></body>
      </method>

      <method name="_whereToOpen">
        <parameter name="event"/>
        <body><![CDATA[
          let isMouseEvent = event instanceof MouseEvent;
          let reuseEmpty = !isMouseEvent;
          let where = undefined;
          if (isMouseEvent) {
            where = whereToOpenLink(event, false, false);
          } else {
            let altEnter = event && event.altKey;
            where = altEnter ? "tab" : "current";
          }
          if (this.openInTab) {
            if (where == "current") {
              where = "tab";
            } else if (where == "tab") {
              where = "current";
            }
            reuseEmpty = true;
          }
          if (where == "tab" && reuseEmpty && isTabEmpty(gBrowser.selectedTab)) {
            where = "current";
          }
          return where;
        ]]></body>
      </method>

      <!--
        This is ultimately called by the autocomplete controller as the result
        of handleEnter when the Return key is pressed in the textbox.  Since
        onPopupClick also calls handleEnter, this is also called as a result in
        that case.

        @param event
               The event that triggered the command.
        @param openUILinkWhere
               Optional.  The "where" to pass to openUILinkIn.  This method
               computes the appropriate "where" given the event, but you can
               use this to override it.
        @param openUILinkParams
               Optional.  The parameters to pass to openUILinkIn.  As with
               "where", this method computes the appropriate parameters, but
               any parameters you supply here will override those.
      -->
      <method name="handleCommand">
        <parameter name="event"/>
        <parameter name="openUILinkWhere"/>
        <parameter name="openUILinkParams"/>
        <parameter name="triggeringPrincipal"/>
        <body><![CDATA[
          let isMouseEvent = event instanceof MouseEvent;
          if (isMouseEvent && event.button == 2) {
            // Do nothing for right clicks.
            return;
          }

          BrowserUsageTelemetry.recordUrlbarSelectedResultMethod(
            event, this.userSelectionBehavior);

          // Determine whether to use the selected one-off search button.  In
          // one-off search buttons parlance, "selected" means that the button
          // has been navigated to via the keyboard.  So we want to use it if
          // the triggering event is not a mouse click -- i.e., it's a Return
          // key -- or if the one-off was mouse-clicked.
          let selectedOneOff = this.popup.oneOffSearchButtons.selectedButton;
          if (selectedOneOff &&
              isMouseEvent &&
              event.originalTarget != selectedOneOff) {
            selectedOneOff = null;
          }

          // Do the command of the selected one-off if it's not an engine.
          if (selectedOneOff && !selectedOneOff.engine) {
            selectedOneOff.doCommand();
            return;
          }

          let where = openUILinkWhere || this._whereToOpen(event);

          let url = this.value;
          if (!url) {
            return;
          }

          let mayInheritPrincipal = false;
          let postData = null;
          let browser = gBrowser.selectedBrowser;
          let action = this._parseActionUrl(url);

          if (selectedOneOff && selectedOneOff.engine) {
            // If there's a selected one-off button then load a search using
            // the one-off's engine.
            [url, postData] =
              this._parseAndRecordSearchEngineLoad(selectedOneOff.engine,
                                                   this.oneOffSearchQuery,
                                                   event, where,
                                                   openUILinkParams);
          } else if (action) {
            switch (action.type) {
              case "visiturl":
                // Unifiedcomplete uses fixupURI to tell if something is a visit
                // or a search, and passes out the fixedURI as the url param.
                // By using that uri we would end up passing a different string
                // to the docshell that may run a different not-found heuristic.
                // For example, "mozilla/run" would be fixed by unifiedcomplete
                // to "http://mozilla/run". The docshell, once it can't resolve
                // mozilla, would note the string has a scheme, and try to load
                // http://mozilla.com/run instead of searching "mozilla/run".
                // So, if we have the original input at hand, we pass it through
                // and let the docshell handle it.
                if (action.params.input) {
                  url = action.params.input;
                  break;
                }
                url = action.params.url;
                break;
              case "remotetab":
                url = action.params.url;
                break;
              case "keyword":
                if (action.params.postData) {
                  postData = getPostDataStream(action.params.postData);
                }
                mayInheritPrincipal = true;
                url = action.params.url;
                break;
              case "switchtab":
                url = action.params.url;
                if (this.hasAttribute("actiontype")) {
                  this.handleRevert();
                  let prevTab = gBrowser.selectedTab;
                  let loadOpts = {
                    adoptIntoActiveWindow: this._adoptIntoActiveWindow,
                  };

                  if (switchToTabHavingURI(url, false, loadOpts) &&
                      isTabEmpty(prevTab)) {
                    gBrowser.removeTab(prevTab);
                  }
                  return;
                }
                break;
              case "searchengine":
                if (selectedOneOff && selectedOneOff.engine) {
                  // Replace the engine with the selected one-off engine.
                  action.params.engineName = selectedOneOff.engine.name;
                }
                const actionDetails = {
                  isSuggestion: !!action.params.searchSuggestion,
                  isAlias: !!action.params.alias
                };
                [url, postData] = this._parseAndRecordSearchEngineLoad(
                  action.params.engineName,
                  action.params.searchSuggestion || action.params.searchQuery,
                  event,
                  where,
                  openUILinkParams,
                  actionDetails
                );
                break;
              case "extension":
                this.handleRevert();
                // Give the extension control of handling the command.
                let searchString = action.params.content;
                let keyword = action.params.keyword;
                this.ExtensionSearchHandler.handleInputEntered(keyword, searchString, where);
                return;
            }
          } else {
            // This is a fallback for add-ons and old testing code that directly
            // set value and try to confirm it. UnifiedComplete should always
            // resolve to a valid url.
            try {
              new URL(url);
            } catch (ex) {
              let lastLocationChange = browser.lastLocationChange;
              getShortcutOrURIAndPostData(url).then(data => {
                if (where != "current" ||
                    browser.lastLocationChange == lastLocationChange) {
                  this._loadURL(data.url, browser, data.postData, where,
                                openUILinkParams, data.mayInheritPrincipal,
                                triggeringPrincipal);
                }
              });
              return;
            }
          }

          this._loadURL(url, browser, postData, where, openUILinkParams,
                        mayInheritPrincipal, triggeringPrincipal);
        ]]></body>
      </method>

      <property name="oneOffSearchQuery">
        <getter><![CDATA[
          // If the user has selected a search suggestion, chances are they
          // want to use the one off search engine to search for that suggestion,
          // not the string that they manually entered into the location bar.
          let action = this._parseActionUrl(this.value);
          if (action && action.type == "searchengine") {
            return action.params.input;
          }
          // this.textValue may be an autofilled string.  Search only with the
          // portion that the user typed, if any, by preferring the autocomplete
          // controller's searchString (including handleEnterInstance.searchString).
          return this.handleEnterSearchString ||
                 this.mController.searchString ||
                 this.textValue;
        ]]></getter>
      </property>

      <method name="_loadURL">
        <parameter name="url"/>
        <parameter name="browser"/>
        <parameter name="postData"/>
        <parameter name="openUILinkWhere"/>
        <parameter name="openUILinkParams"/>
        <parameter name="mayInheritPrincipal"/>
        <parameter name="triggeringPrincipal"/>
        <body><![CDATA[
          this.value = url;
          browser.userTypedValue = url;
          if (gInitialPages.includes(url)) {
            browser.initialPageLoadedFromURLBar = url;
          }
          try {
            addToUrlbarHistory(url);
          } catch (ex) {
            // Things may go wrong when adding url to session history,
            // but don't let that interfere with the loading of the url.
            Cu.reportError(ex);
          }

          let params = {
            postData,
            allowThirdPartyFixup: true,
            triggeringPrincipal,
          };
          if (openUILinkWhere == "current") {
            params.targetBrowser = browser;
            params.indicateErrorPageLoad = true;
            params.allowPinnedTabHostChange = true;
            params.disallowInheritPrincipal = !mayInheritPrincipal;
            params.allowPopups = url.startsWith("javascript:");
          } else {
            params.initiatingDoc = document;
          }

          if (openUILinkParams) {
            for (let key in openUILinkParams) {
              params[key] = openUILinkParams[key];
            }
          }

          // Focus the content area before triggering loads, since if the load
          // occurs in a new tab, we want focus to be restored to the content
          // area when the current tab is re-selected.
          browser.focus();

          if (openUILinkWhere != "current") {
            this.handleRevert();
          }

          try {
            openUILinkIn(url, openUILinkWhere, params);
          } catch (ex) {
            // This load can throw an exception in certain cases, which means
            // we'll want to replace the URL with the loaded URL:
            if (ex.result != Cr.NS_ERROR_LOAD_SHOWED_ERRORPAGE) {
              this.handleRevert();
            }
          }

          if (openUILinkWhere == "current") {
            // Ensure the start of the URL is visible for usability reasons.
            this.selectionStart = this.selectionEnd = 0;
          }
        ]]></body>
      </method>

      <method name="_parseAndRecordSearchEngineLoad">
        <parameter name="engineOrEngineName"/>
        <parameter name="query"/>
        <parameter name="event"/>
        <parameter name="openUILinkWhere"/>
        <parameter name="openUILinkParams"/>
        <parameter name="searchActionDetails"/>
        <body><![CDATA[
          let engine =
            typeof(engineOrEngineName) == "string" ?
              Services.search.getEngineByName(engineOrEngineName) :
              engineOrEngineName;
          let isOneOff = this.popup.oneOffSearchButtons
              .maybeRecordTelemetry(event, openUILinkWhere, openUILinkParams);
          // Infer the type of the event which triggered the search.
          let eventType = "unknown";
          if (event instanceof KeyboardEvent) {
            eventType = "key";
          } else if (event instanceof MouseEvent) {
            eventType = "mouse";
          }
          // Augment the search action details object.
          let details = searchActionDetails || {};
          details.isOneOff = isOneOff;
          details.type = eventType;

          BrowserSearch.recordSearchInTelemetry(engine, "urlbar", details);
          let submission = engine.getSubmission(query, null, "keyword");
          return [submission.uri.spec, submission.postData];
        ]]></body>
      </method>

      <method name="maybeCanonizeURL">
        <parameter name="aTriggeringEvent"/>
        <parameter name="aUrl"/>
        <body><![CDATA[
          // Only add the suffix when the URL bar value isn't already "URL-like",
          // and only if we get a keyboard event, to match user expectations.
          if (!/^\s*[^.:\/\s]+(?:\/.*|\s*)$/i.test(aUrl) ||
              !(aTriggeringEvent instanceof KeyboardEvent)) {
            return;
          }

          let url = aUrl;
          let accel = AppConstants.platform == "macosx" ?
                      aTriggeringEvent.metaKey :
                      aTriggeringEvent.ctrlKey;
          let shift = aTriggeringEvent.shiftKey;
          let suffix = "";

          switch (true) {
            case (accel && shift):
              suffix = ".org/";
              break;
            case (shift):
              suffix = ".net/";
              break;
            case (accel):
              try {
                suffix = Services.prefs.getCharPref("browser.fixup.alternate.suffix");
                if (suffix.charAt(suffix.length - 1) != "/")
                  suffix += "/";
              } catch (e) {
                suffix = ".com/";
              }
              break;
          }

          if (!suffix)
            return;

          // trim leading/trailing spaces (bug 233205)
          url = url.trim();

          // Tack www. and suffix on.  If user has appended directories, insert
          // suffix before them (bug 279035).  Be careful not to get two slashes.
          let firstSlash = url.indexOf("/");
          if (firstSlash >= 0) {
            url = url.substring(0, firstSlash) + suffix +
                  url.substring(firstSlash + 1);
          } else {
            url = url + suffix;
          }

          this.popup.overrideValue = "http://www." + url;
        ]]></body>
      </method>

      <method name="_initURLTooltip">
        <body><![CDATA[
          if (this.focused || !this.hasAttribute("textoverflow"))
            return;
          this.inputField.setAttribute("tooltiptext", this.value);
        ]]></body>
      </method>

      <method name="_hideURLTooltip">
        <body><![CDATA[
          this.inputField.removeAttribute("tooltiptext");
        ]]></body>
      </method>

      <!-- Returns:
           null if there's a security issue and we should do nothing.
           a URL object if there is one that we're OK with loading,
           a text value otherwise.
           -->
      <method name="_getDroppableItem">
        <parameter name="aEvent"/>
        <body><![CDATA[
          let links;
          try {
            links = browserDragAndDrop.dropLinks(aEvent);
          } catch (ex) {
            // this is possibly a security exception, in which case we should return
            // null. Always return null because we can't *know* what exception is
            // being returned.
            return null;
          }
          // The URL bar automatically handles inputs with newline characters,
          // so we can get away with treating text/x-moz-url flavours as text/plain.
          if (links.length > 0 && links[0].url) {
            let triggeringPrincipal = browserDragAndDrop.getTriggeringPrincipal(aEvent);
            aEvent.preventDefault();
            let url = links[0].url;
            let strippedURL = stripUnsafeProtocolOnPaste(url);
            if (strippedURL != url) {
              aEvent.stopImmediatePropagation();
              return null;
            }
            let urlObj;
            try {
              // If this throws, urlSecurityCheck would also throw, as that's what it
              // does with things that don't pass the IO service's newURI constructor
              // without fixup. It's conceivable we may want to relax this check in
              // the future (so e.g. www.foo.com gets fixed up), but not right now.
              urlObj = new URL(url);
              // If we succeed, try to pass security checks. If this works, return the
              // URL object. If the *security checks* fail, return null.
              try {
                urlSecurityCheck(url,
                                 triggeringPrincipal,
                                 Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL);
                return urlObj;
              } catch (ex) {
                return null;
              }
            } catch (ex) {
              // We couldn't make a URL out of this. Continue on, and return text below.
            }
          }
          return aEvent.dataTransfer.getData("text/unicode");
        ]]></body>
      </method>

      <method name="onDragOver">
        <parameter name="aEvent"/>
        <body><![CDATA[
          if (!this._getDroppableItem(aEvent)) {
            aEvent.dataTransfer.dropEffect = "none";
          }
        ]]></body>
      </method>

      <method name="onDrop">
        <parameter name="aEvent"/>
        <body><![CDATA[
          let droppedItem = this._getDroppableItem(aEvent);
          if (droppedItem) {
            let triggeringPrincipal = browserDragAndDrop.getTriggeringPrincipal(aEvent);
            this.value = droppedItem instanceof URL ? droppedItem.href : droppedItem;
            SetPageProxyState("invalid");
            this.focus();
            this.handleCommand(null, undefined, undefined, triggeringPrincipal);
            // Force not showing the dropped URI immediately.
            gBrowser.userTypedValue = null;
            URLBarSetURI();
          }
        ]]></body>
      </method>

      <method name="makeURIReadable">
        <parameter name="aURI"/>
        <body>
          <![CDATA[
            // Avoid copying 'about:reader?url=', and always provide the original URI:
            // Reader mode ensures we call createExposableURI itself.
            let readerStrippedURI = ReaderMode.getOriginalUrlObjectForDisplay(aURI.displaySpec);
            if (readerStrippedURI) {
              aURI = readerStrippedURI;
            } else {
              // Only copy exposable URIs
              try {
                aURI = Services.uriFixup.createExposableURI(aURI);
              } catch (ex) {}
            }
            return aURI;
          ]]>
        </body>
      </method>

      <method name="_getSelectedValueForClipboard">
        <body><![CDATA[
          // Grab the actual input field's value, not our value, which could
          // include "moz-action:".
          var inputVal = this.inputField.value;
          let selection = this.editor.selection;
          const flags = Ci.nsIDocumentEncoder.OutputPreformatted |
                        Ci.nsIDocumentEncoder.OutputRaw;
          let selectedVal = selection.QueryInterface(Ci.nsISelectionPrivate)
                                     .toStringWithFormat("text/plain", flags, 0);

          // Handle multiple-range selection as a string for simplicity.
          if (selection.rangeCount > 1) {
             return selectedVal;
          }

          // If the selection doesn't start at the beginning or doesn't span the
          // full domain or the URL bar is modified or there is no text at all,
          // nothing else to do here.
          if (this.selectionStart > 0 || this.valueIsTyped || selectedVal == "")
            return selectedVal;
          // The selection doesn't span the full domain if it doesn't contain a slash and is
          // followed by some character other than a slash.
          if (!selectedVal.includes("/")) {
            let remainder = inputVal.replace(selectedVal, "");
            if (remainder != "" && remainder[0] != "/")
              return selectedVal;
          }

          // If the value was filled by a search suggestion, just return it.
          let action = this._parseActionUrl(this.value);
          if (action && action.type == "searchengine")
            return selectedVal;

          let uriFixup = Cc["@mozilla.org/docshell/urifixup;1"].getService(Ci.nsIURIFixup);

          let uri;
          if (this.getAttribute("pageproxystate") == "valid") {
            uri = gBrowser.currentURI;
          } else {
            // We're dealing with an autocompleted value, create a new URI from that.
            try {
              uri = uriFixup.createFixupURI(inputVal, Ci.nsIURIFixup.FIXUP_FLAG_NONE);
            } catch (e) {}
            if (!uri)
              return selectedVal;
          }

          uri = this.makeURIReadable(uri);

          // If the entire URL is selected, just use the actual loaded URI,
          // unless we want a decoded URI, or it's a data: or javascript: URI,
          // since those are hard to read when encoded.
          if (inputVal == selectedVal &&
              !uri.schemeIs("javascript") && !uri.schemeIs("data") &&
              !Services.prefs.getBoolPref("browser.urlbar.decodeURLsOnCopy")) {
            return uri.displaySpec;
          }

          // Just the beginning of the URL is selected, or we want a decoded
          // url. First check for a trimmed value.
          let spec = uri.displaySpec;
          let trimmedSpec = this.trimValue(spec);
          if (spec != trimmedSpec) {
            // Prepend the portion that trimValue removed from the beginning.
            // This assumes trimValue will only truncate the URL at
            // the beginning or end (or both).
            let trimmedSegments = spec.split(trimmedSpec);
            selectedVal = trimmedSegments[0] + selectedVal;
          }

          return selectedVal;
        ]]></body>
      </method>

      <field name="_copyCutController"><![CDATA[
        ({
          urlbar: this,
          doCommand(aCommand) {
            var urlbar = this.urlbar;
            var val = urlbar._getSelectedValueForClipboard();
            if (!val)
              return;

            if (aCommand == "cmd_cut" && this.isCommandEnabled(aCommand)) {
              let start = urlbar.selectionStart;
              let end = urlbar.selectionEnd;
              urlbar.inputField.value = urlbar.inputField.value.substring(0, start) +
                                        urlbar.inputField.value.substring(end);
              urlbar.selectionStart = urlbar.selectionEnd = start;

              let event = document.createEvent("UIEvents");
              event.initUIEvent("input", true, false, window, 0);
              urlbar.dispatchEvent(event);

              SetPageProxyState("invalid");
            }

            Cc["@mozilla.org/widget/clipboardhelper;1"]
              .getService(Ci.nsIClipboardHelper)
              .copyString(val);
          },
          supportsCommand(aCommand) {
            switch (aCommand) {
              case "cmd_copy":
              case "cmd_cut":
                return true;
            }
            return false;
          },
          isCommandEnabled(aCommand) {
            return this.supportsCommand(aCommand) &&
                   (aCommand != "cmd_cut" || !this.urlbar.readOnly) &&
                   this.urlbar.selectionStart < this.urlbar.selectionEnd;
          },
          onEvent(aEventName) {}
        })
      ]]></field>

      <method name="observe">
        <parameter name="aSubject"/>
        <parameter name="aTopic"/>
        <parameter name="aData"/>
        <body><![CDATA[
          if (aTopic == "nsPref:changed") {
            switch (aData) {
              case "clickSelectsAll":
              case "doubleClickSelectsAll":
                this[aData] = this._prefs.getBoolPref(aData);
                break;
              case "autoFill":
                this.completeDefaultIndex = this._prefs.getBoolPref(aData);
                break;
              case "delay":
                this.timeout = this._prefs.getIntPref(aData);
                break;
              case "formatting.enabled":
                this._formattingEnabled = this._prefs.getBoolPref(aData);
                break;
              case "speculativeConnect.enabled":
                this.speculativeConnectEnabled = this._prefs.getBoolPref(aData);
                break;
              case "openintab":
                this.openInTab = this._prefs.getBoolPref(aData);
                break;
              case "browser.search.suggest.enabled":
                this.browserSearchSuggestEnabled = Services.prefs.getBoolPref(aData);
                break;
              case "suggest.searches":
                this.urlbarSearchSuggestEnabled = this._prefs.getBoolPref(aData);
              case "userMadeSearchSuggestionsChoice":
                // Mirror the value for future use, see the comment in the
                // binding's constructor.
                this._prefs.setBoolPref("searchSuggestionsChoice",
                  this.urlbarSearchSuggestEnabled);
                // Clear the cached value to allow changing conditions in tests.
                delete this._whichSearchSuggestionsNotification;
                break;
              case "trimURLs":
                this._mayTrimURLs = this._prefs.getBoolPref(aData);
                break;
              case "oneOffSearches":
                this._enableOrDisableOneOffSearches();
                break;
              case "maxRichResults":
                this.popup.maxResults = this._prefs.getIntPref(aData);
                break;
              case "switchTabs.adoptIntoActiveWindow":
                this._adoptIntoActiveWindow =
                  this._prefs.getBoolPref("switchTabs.adoptIntoActiveWindow");
                break;
            }
          }
        ]]></body>
      </method>

      <method name="_enableOrDisableOneOffSearches">
        <body><![CDATA[
          let enable = this._prefs.getBoolPref("oneOffSearches");
          this.popup.enableOneOffSearches(enable);
        ]]></body>
      </method>

      <method name="handleEvent">
        <parameter name="aEvent"/>
        <body><![CDATA[
          switch (aEvent.type) {
            case "paste":
              let originalPasteData = aEvent.clipboardData.getData("text/plain");
              if (!originalPasteData) {
                return;
              }

              let oldValue = this.inputField.value;
              let oldStart = oldValue.substring(0, this.inputField.selectionStart);
              // If there is already non-whitespace content in the URL bar
              // preceding the pasted content, it's not necessary to check
              // protocols used by the pasted content:
              if (oldStart.trim()) {
                return;
              }
              let oldEnd = oldValue.substring(this.inputField.selectionEnd);

              let pasteData = stripUnsafeProtocolOnPaste(originalPasteData);
              if (originalPasteData != pasteData) {
                // Unfortunately we're not allowed to set the bits being pasted
                // so cancel this event:
                aEvent.preventDefault();
                aEvent.stopImmediatePropagation();

                this.inputField.value = oldStart + pasteData + oldEnd;
                // Fix up cursor/selection:
                let newCursorPos = oldStart.length + pasteData.length;
                this.inputField.selectionStart = newCursorPos;
                this.inputField.selectionEnd = newCursorPos;
              }
              break;
            case "mousedown":
              if (this.doubleClickSelectsAll &&
                  aEvent.button == 0 && aEvent.detail == 2) {
                this.editor.selectAll();
                aEvent.preventDefault();
              }
              break;
            case "mousemove":
              this._initURLTooltip();
              break;
            case "mouseout":
              this._hideURLTooltip();
              break;
            case "overflow":
              if (!this.value) {
                // We initially get a spurious overflow event from the
                // anonymous div containing the placeholder text; bail out.
                break;
              }
              this.setAttribute("textoverflow", "true");
              break;
            case "underflow":
              this.removeAttribute("textoverflow");
              this._hideURLTooltip();
              break;
            case "TabSelect":
              // The autocomplete controller uses heuristic on some internal caches
              // to handle cases like backspace, autofill or repeated searches.
              // Ensure to clear those internal caches when switching tabs.
              this.controller.resetInternalState();
              break;
          }
        ]]></body>
      </method>

      <!--
        onBeforeTextValueSet is called by the base-binding's .textValue getter.
        It should return the value that the getter should use.
      -->
      <method name="onBeforeTextValueGet">
        <body><![CDATA[
          return { value: this.inputField.value };
        ]]></body>
      </method>

      <!--
        onBeforeTextValueSet is called by the base-binding's .textValue setter.
        It should return the value that the setter should use.
      -->
      <method name="onBeforeTextValueSet">
        <parameter name="aValue"/>
        <body><![CDATA[
          let val = aValue;
          let uri;
          try {
            uri = makeURI(val);
          } catch (ex) {}

          if (uri) {
            // Do not touch moz-action URIs at all.  They depend on being
            // properly encoded and decoded and will break if decoded
            // unexpectedly.
            if (!this._parseActionUrl(val)) {
              val = losslessDecodeURI(uri);
            }
          }

          return val;
        ]]></body>
      </method>

      <method name="_parseActionUrl">
        <parameter name="aUrl"/>
        <body><![CDATA[
          const MOZ_ACTION_REGEX = /^moz-action:([^,]+),(.*)$/;
          if (!MOZ_ACTION_REGEX.test(aUrl))
            return null;

          // URL is in the format moz-action:ACTION,PARAMS
          // Where PARAMS is a JSON encoded object.
          let [, type, params] = aUrl.match(MOZ_ACTION_REGEX);

          let action = {
            type,
          };

          action.params = JSON.parse(params);
          for (let key in action.params) {
            action.params[key] = decodeURIComponent(action.params[key]);
          }

          if ("url" in action.params) {
            let uri;
            try {
              uri = makeURI(action.params.url);
              action.params.displayUrl = losslessDecodeURI(uri);
            } catch (e) {
              action.params.displayUrl = action.params.url;
            }
          }

          return action;
        ]]></body>
      </method>

      <property name="_noActionKeys" readonly="true">
        <getter><![CDATA[
          if (!this.__noActionKeys) {
            this.__noActionKeys = new Set([
              KeyEvent.DOM_VK_ALT,
              KeyEvent.DOM_VK_SHIFT,
            ]);
            let modifier = AppConstants.platform == "macosx" ?
                           KeyEvent.DOM_VK_META :
                           KeyEvent.DOM_VK_CONTROL;
            this.__noActionKeys.add(modifier);
          }
          return this.__noActionKeys;
        ]]></getter>
      </property>

      <field name="_pressedNoActionKeys"><![CDATA[
        new Set()
      ]]></field>

      <method name="_clearNoActions">
        <parameter name="aURL"/>
        <body><![CDATA[
          this._pressedNoActionKeys.clear();
          this.popup.removeAttribute("noactions");
          let action = this._parseActionUrl(this._value);
          if (action)
            this.setAttribute("actiontype", action.type);
        ]]></body>
      </method>

      <method name="onInput">
        <parameter name="aEvent"/>
        <body><![CDATA[
          if (!this.mIgnoreInput && this.mController.input == this) {
            this._value = this.inputField.value;
            gBrowser.userTypedValue = this.value;
            this.valueIsTyped = true;
            if (this.inputField.value) {
              this.setAttribute("usertyping", "true");
            } else {
              this.removeAttribute("usertyping");
            }
            // Only wait for a result when we are sure to get one.  In some
            // cases, like when pasting the same exact text, we may not fire
            // a new search and we won't get a result.
            if (this.mController.handleText()) {
              this.gotResultForCurrentQuery = false;
              this._searchStartDate = Cu.now();
              this._deferredKeyEventQueue = [];
              if (this._deferredKeyEventTimeout) {
                clearTimeout(this._deferredKeyEventTimeout);
                this._deferredKeyEventTimeout = null;
              }
            }
          }
          this.resetActionType();
        ]]></body>
      </method>

      <method name="handleEnter">
        <parameter name="event"/>
        <body><![CDATA[
          // We need to ensure we're using a selected autocomplete result.
          // A result should automatically be selected by default,
          // however autocomplete is async and therefore we may not
          // have a result set relating to the current input yet. If that
          // happens, we need to mark that when the first result does get added,
          // it needs to be handled as if enter was pressed with that first
          // result selected.
          // If anything other than the default (first) result is selected, then
          // it must have been manually selected by the human. We let this
          // explicit choice be used, even if it may be related to a previous
          // input.
          // However, if the default result is automatically selected, we
          // ensure that it corresponds to the current input.

          // Store the current search string so it can be used in handleCommand,
          // which will be called as a result of mController.handleEnter().
          this.handleEnterSearchString = this.mController.searchString;

          if (!this._deferredKeyEventQueue.length &&
              (this.popup.selectedIndex != 0 || this.gotResultForCurrentQuery)) {
            let canonizeValue = this.value;
            if (event.shiftKey || (AppConstants.platform === "macosx" ?
                                   event.metaKey :
                                   event.ctrlKey)) {
              let action = this._parseActionUrl(canonizeValue);
              if (action && "searchSuggestion" in action.params) {
                canonizeValue = action.params.searchSuggestion;
              } else if (this.popup.selectedIndex === 0 &&
                         this.mController.getStyleAt(0).includes("autofill")) {
                canonizeValue = this.handleEnterSearchString;
              }
            }
            this.maybeCanonizeURL(event, canonizeValue);
            let handled = this.mController.handleEnter(false, event);
            this.handleEnterSearchString = null;
            this.popup.overrideValue = null;
            return handled;
          }

          // Defer the event until the first non-heuristic result comes in.
          this._deferKeyEvent(event, "handleEnter");
          return false;
        ]]></body>
      </method>

      <method name="handleDelete">
        <body><![CDATA[
          // If the heuristic result is selected, then the autocomplete
          // controller's handleDelete implementation will remove it, which is
          // not what we want.  So in that case, call handleText so it acts as
          // a backspace on the text value instead of removing the result.
          if (this.popup.selectedIndex == 0 &&
              this.popup._isFirstResultHeuristic) {
            this.mController.handleText();
            return false;
          }
          return this.mController.handleDelete();
        ]]></body>
      </method>

      <property name="_userMadeSearchSuggestionsChoice" readonly="true">
        <getter><![CDATA[
          return this._prefs.getBoolPref("userMadeSearchSuggestionsChoice") ||
                 this._defaultPrefs.getBoolPref("suggest.searches") != this._prefs.getBoolPref("suggest.searches");
        ]]></getter>
      </property>

      <property name="whichSearchSuggestionsNotification" readonly="true">
        <getter><![CDATA[
          // Once we return "none" once, we'll always return "none".
          // If available, use the cached value, rather than running all of the
          // checks again at every locationbar focus.
          if (this._whichSearchSuggestionsNotification) {
            return this._whichSearchSuggestionsNotification;
          }

          if (this.browserSearchSuggestEnabled && !this.inPrivateContext &&
              // In any case, if the user made a choice we should not nag him.
              !this._userMadeSearchSuggestionsChoice) {
            if (this._defaultPrefs.getBoolPref("suggest.searches") &&
                this.urlbarSearchSuggestEnabled && // Has not been switched off.
                this._prefs.getIntPref("timesBeforeHidingSuggestionsHint")) {
              return "opt-out";
            }
          }
          return this._whichSearchSuggestionsNotification = "none";
        ]]></getter>
      </property>

      <method name="updateSearchSuggestionsNotificationImpressions">
        <parameter name="whichNotification"/>
        <body><![CDATA[
          if (whichNotification == "none") {
            throw new Error("Unexpected notification type");
          }

          let remaining = this._prefs.getIntPref("timesBeforeHidingSuggestionsHint");
          if (remaining > 0) {
            this._prefs.setIntPref("timesBeforeHidingSuggestionsHint", remaining - 1);
          }
        ]]></body>
      </method>

      <method name="maybeShowSearchSuggestionsNotificationOnFocus">
        <parameter name="mouseFocused"/>
        <body><![CDATA[
          let whichNotification = this.whichSearchSuggestionsNotification;
          if (this._showSearchSuggestionNotificationOnMouseFocus &&
              mouseFocused) {
            // Force showing the opt-out notification.
            this._whichSearchSuggestionsNotification = whichNotification = "opt-out";
          }
          if (whichNotification == "opt-out") {
            try {
              this.popup.openAutocompletePopup(this, this);
            } finally {
              if (mouseFocused) {
                delete this._whichSearchSuggestionsNotification;
                this._showSearchSuggestionNotificationOnMouseFocus = false;
              }
            }
          }
        ]]></body>
      </method>
    </implementation>

    <handlers>
      <handler event="keydown"><![CDATA[
        if (this._noActionKeys.has(event.keyCode) &&
            this.popup.selectedIndex >= 0 &&
            !this._pressedNoActionKeys.has(event.keyCode)) {
          if (this._pressedNoActionKeys.size == 0) {
            this.popup.setAttribute("noactions", "true");
            this.removeAttribute("actiontype");
          }
          this._pressedNoActionKeys.add(event.keyCode);
        }
      ]]></handler>

      <handler event="keyup"><![CDATA[
        if (this._noActionKeys.has(event.keyCode) &&
            this._pressedNoActionKeys.has(event.keyCode)) {
          this._pressedNoActionKeys.delete(event.keyCode);
          if (this._pressedNoActionKeys.size == 0)
            this._clearNoActions();
        }
      ]]></handler>

      <handler event="mousedown"><![CDATA[
        if (event.button == 0) {
          if (event.originalTarget.getAttribute("anonid") == "historydropmarker") {
            this.toggleHistoryPopup();
          }

          // Eventually show the opt-out notification even if the location bar is
          // empty, focused, and the user clicks on it.
          if (this.focused && this.textValue == "") {
            this.maybeShowSearchSuggestionsNotificationOnFocus(true);
          }
        }
      ]]></handler>

      <handler event="focus"><![CDATA[
        if (event.originalTarget == this.inputField) {
          this._hideURLTooltip();
          this.formatValue();
          if (this.getAttribute("pageproxystate") != "valid") {
            UpdatePopupNotificationsVisibility();
          }

          // We show the opt-out notification when the mouse/keyboard focus the
          // urlbar, but in any case we want to enforce at least one
          // notification when the user focuses it with the mouse.
          let whichNotification = this.whichSearchSuggestionsNotification;
          if (whichNotification == "opt-out" &&
              this._showSearchSuggestionNotificationOnMouseFocus === undefined) {
            this._showSearchSuggestionNotificationOnMouseFocus = true;
          }

          // Check whether the focus change came from a keyboard/mouse action.
          let focusMethod = Services.focus.getLastFocusMethod(window);
          // If it's a focus started by code and the primary user intention was
          // not to go to the location bar, don't show a notification.
          if (!focusMethod && !this.userInitiatedFocus) {
            return;
          }

          let mouseFocused = !!(focusMethod & Services.focus.FLAG_BYMOUSE);
          this.maybeShowSearchSuggestionsNotificationOnFocus(mouseFocused);
        }
      ]]></handler>

      <handler event="blur"><![CDATA[
        if (event.originalTarget == this.inputField) {
          this._clearNoActions();
          this.formatValue();
          if (this.getAttribute("pageproxystate") != "valid") {
            UpdatePopupNotificationsVisibility();
          }
        }
        if (this.ExtensionSearchHandler.hasActiveInputSession()) {
          this.ExtensionSearchHandler.handleInputCancelled();
        }
        if (this._deferredKeyEventTimeout) {
          clearTimeout(this._deferredKeyEventTimeout);
          this._deferredKeyEventTimeout = null;
        }
        this._deferredKeyEventQueue = [];
      ]]></handler>

      <handler event="dragstart" phase="capturing"><![CDATA[
        // Drag only if the gesture starts from the input field.
        if (this.inputField != event.originalTarget &&
            !(this.inputField.compareDocumentPosition(event.originalTarget) &
              Node.DOCUMENT_POSITION_CONTAINED_BY))
          return;

        // Drag only if the entire value is selected and it's a valid URI.
        var isFullSelection = this.selectionStart == 0 &&
                              this.selectionEnd == this.textLength;
        if (!isFullSelection ||
            this.getAttribute("pageproxystate") != "valid")
          return;

        var urlString = gBrowser.selectedBrowser.currentURI.displaySpec;
        var title = gBrowser.selectedBrowser.contentTitle || urlString;
        var htmlString = "<a href=\"" + urlString + "\">" + urlString + "</a>";

        var dt = event.dataTransfer;
        dt.setData("text/x-moz-url", urlString + "\n" + title);
        dt.setData("text/unicode", urlString);
        dt.setData("text/html", htmlString);

        dt.effectAllowed = "copyLink";
        event.stopPropagation();
      ]]></handler>

      <handler event="dragover" phase="capturing" action="this.onDragOver(event, this);"/>
      <handler event="drop" phase="capturing" action="this.onDrop(event, this);"/>
      <handler event="select"><![CDATA[
        if (!Cc["@mozilla.org/widget/clipboard;1"]
               .getService(Ci.nsIClipboard)
               .supportsSelectionClipboard())
          return;

        if (!window.QueryInterface(Ci.nsIInterfaceRequestor)
                   .getInterface(Ci.nsIDOMWindowUtils)
                   .isHandlingUserInput)
          return;

        var val = this._getSelectedValueForClipboard();
        if (!val)
          return;

        Cc["@mozilla.org/widget/clipboardhelper;1"]
          .getService(Ci.nsIClipboardHelper)
          .copyStringToClipboard(val, Ci.nsIClipboard.kSelectionClipboard);
      ]]></handler>
    </handlers>

  </binding>

  <binding id="urlbar-rich-result-popup" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete-rich-result-popup">

    <resources>
      <stylesheet src="chrome://browser/content/search/searchbarBindings.css"/>
      <stylesheet src="chrome://browser/skin/searchbar.css"/>
    </resources>

    <content ignorekeys="true" level="top" consumeoutsideclicks="never"
             aria-owns="richlistbox">
      <xul:deck anonid="search-suggestions-notification"
                align="center"
                role="alert"
                selectedIndex="0">
        <!-- OPT-OUT -->
        <xul:hbox flex="1" align="center" anonid="search-suggestions-opt-out">
          <xul:image class="ac-site-icon" type="searchengine"/>
          <xul:hbox anonid="search-suggestions-hint-typing">
            <xul:description class="ac-title-text">&brandShortName;</xul:description>
          </xul:hbox>
          <xul:hbox anonid="search-suggestions-hint-box" flex="1">
            <xul:description id="search-suggestions-hint">
              <html:span class="prefix">&#x1f4a1; &urlbar.searchSuggestionsNotification.hintPrefix;</html:span>
              <html:span>&urlbar.searchSuggestionsNotification.hint;</html:span>
            </xul:description>
          </xul:hbox>
          <xul:label id="search-suggestions-change-settings"
                     class="text-link"
                     role="link"
                     value="&urlbar.searchSuggestionsNotification.changeSettingsUnix;"
                     accesskey="&urlbar.searchSuggestionsNotification.changeSettingsUnix.accesskey;"
                     onclick="openPreferences('paneSearch', {origin: 'searchChangeSettings'});"
                     control="search-suggestions-change-settings"/>
        </xul:hbox>
      </xul:deck>
      <xul:richlistbox anonid="richlistbox" class="autocomplete-richlistbox"
                       flex="1"/>
      <xul:hbox anonid="footer">
        <children/>
        <xul:vbox anonid="one-off-search-buttons"
                  class="search-one-offs"
                  compact="true"
                  includecurrentengine="true"
                  disabletab="true"
                  flex="1"/>
      </xul:hbox>
    </content>

    <implementation>
      <field name="DOMWindowUtils">
        window.QueryInterface(Ci.nsIInterfaceRequestor)
              .getInterface(Ci.nsIDOMWindowUtils);
      </field>

      <field name="_maxResults">0</field>

      <field name="_bundle" readonly="true">
        Cc["@mozilla.org/intl/stringbundle;1"].
          getService(Ci.nsIStringBundleService).
          createBundle("chrome://browser/locale/places/places.properties");
      </field>

      <field name="searchSuggestionsNotification" readonly="true">
        document.getAnonymousElementByAttribute(
          this, "anonid", "search-suggestions-notification"
        );
      </field>

      <field name="footer" readonly="true">
        document.getAnonymousElementByAttribute(this, "anonid", "footer");
      </field>

      <field name="oneOffSearchButtons" readonly="true">
        document.getAnonymousElementByAttribute(this, "anonid",
                                                "one-off-search-buttons");
      </field>

      <field name="_oneOffSearchesEnabled">false</field>

      <field name="_overrideValue">null</field>
      <property name="overrideValue"
                onget="return this._overrideValue;"
                onset="this._overrideValue = val; return val;"/>

      <method name="onPopupClick">
        <parameter name="aEvent"/>
        <body><![CDATA[
          if (aEvent.button == 2) {
            // Ignore right-clicks.
            return;
          }
          // Otherwise "call super" -- do what autocomplete-base-popup does.
          this.input.controller.handleEnter(true, aEvent);
        ]]></body>
      </method>

      <method name="enableOneOffSearches">
        <parameter name="enable"/>
        <body><![CDATA[
          this._oneOffSearchesEnabled = enable;
          if (enable) {
            this.oneOffSearchButtons.telemetryOrigin = "urlbar";
            this.oneOffSearchButtons.style.display = "-moz-box";
            // Set .textbox first, since the popup setter will cause
            // a _rebuild call that uses it.
            this.oneOffSearchButtons.textbox = this.input;
            this.oneOffSearchButtons.popup = this;
          } else {
            this.oneOffSearchButtons.telemetryOrigin = null;
            this.oneOffSearchButtons.style.display = "none";
            this.oneOffSearchButtons.textbox = null;
            this.oneOffSearchButtons.popup = null;
          }
        ]]></body>
      </method>

      <!-- Override this so that navigating between items results in an item
           always being selected. -->
      <method name="getNextIndex">
        <parameter name="reverse"/>
        <parameter name="amount"/>
        <parameter name="index"/>
        <parameter name="maxRow"/>
        <body><![CDATA[
          if (maxRow < 0)
            return -1;

          let newIndex = index + (reverse ? -1 : 1) * amount;

          // We only want to wrap if navigation is in any direction by one item,
          // otherwise we clamp to one end of the list.
          // ie, hitting page-down will only cause is to wrap if we're already
          // at one end of the list.

          // Allow the selection to be removed if the first result is not a
          // heuristic result.
          if (!this._isFirstResultHeuristic) {
            if (reverse && index == -1 || newIndex > maxRow && index != maxRow)
              newIndex = maxRow;
            else if (!reverse && index == -1 || newIndex < 0 && index != 0)
              newIndex = 0;

            if (newIndex < 0 && index == 0 || newIndex > maxRow && index == maxRow)
              newIndex = -1;

            return newIndex;
          }

          // Otherwise do not allow the selection to be removed.
          if (newIndex < 0) {
            newIndex = index > 0 ? 0 : maxRow;
          } else if (newIndex > maxRow) {
            newIndex = index < maxRow ? maxRow : 0;
          }
          return newIndex;
        ]]></body>
      </method>

      <property name="_isFirstResultHeuristic" readonly="true">
        <getter>
          <![CDATA[
            // The popup usually has a special "heuristic" first result (added
            // by UnifiedComplete.js) that is automatically selected when the
            // popup opens.
            return this.input.mController.matchCount > 0 &&
                   this.input.mController
                             .getStyleAt(0)
                             .split(/\s+/).indexOf("heuristic") > 0;
          ]]>
        </getter>
      </property>

      <property name="maxResults">
        <getter>
          <![CDATA[
            if (!this._maxResults) {
              this._maxResults = Services.prefs.getIntPref("browser.urlbar.maxRichResults");
            }
            return this._maxResults;
          ]]>
        </getter>
        <setter>
          <![CDATA[
            return this._maxResults = parseInt(val);
          ]]>
        </setter>
      </property>

      <!-- This is set either to undefined or to a new object containing
           { start, end } margin values in pixels. These are used to align the
           results to the input field. -->
      <property name="margins"
                onget="return this._margins;">
        <setter>
          <![CDATA[
          this._margins = val;

          if (val) {
            /* eslint-disable no-multi-spaces */
            let paddingInCSS =
                3   // .autocomplete-richlistbox padding-left/right
              + 6   // .ac-site-icon margin-inline-start
              + 16  // .ac-site-icon width
              + 6;  // .ac-site-icon margin-inline-end
            /* eslint-enable no-multi-spaces */
            let actualVal = Math.round(val.start) - paddingInCSS;
            let actualValEnd = Math.round(val.end);
            this.style.setProperty("--item-padding-start", actualVal + "px");
            this.style.setProperty("--item-padding-end", actualValEnd + "px");
          } else {
            this.style.removeProperty("--item-padding-start");
            this.style.removeProperty("--item-padding-end");
          }

          return val;
          ]]>
        </setter>
      </property>

      <method name="openAutocompletePopup">
        <parameter name="aInput"/>
        <parameter name="aElement"/>
        <body>
          <![CDATA[
          // initially the panel is hidden
          // to avoid impacting startup / new window performance
          aInput.popup.hidden = false;

          this._openAutocompletePopup(aInput, aElement);
          ]]>
        </body>
      </method>

      <method name="_openAutocompletePopup">
        <parameter name="aInput"/>
        <parameter name="aElement"/>
        <body><![CDATA[
          if (this.mPopupOpen) {
            return;
          }

          // Set the direction of the popup based on the textbox (bug 649840).
          // getComputedStyle causes a layout flush, so avoid calling it if a
          // direction has already been set.
          if (!this.style.direction) {
            this.style.direction =
              aElement.ownerGlobal.getComputedStyle(aElement).direction;
          }
          let popupDirection = this.style.direction;

          // Make the popup span the width of the window.  First, set its width.
          let documentRect =
            this.DOMWindowUtils
                .getBoundsWithoutFlushing(window.document.documentElement);
          let width = documentRect.right - documentRect.left;
          this.setAttribute("width", width);

          // Now make its starting margin negative so that its leading edge
          // aligns with the window border.
          let elementRect =
            this.DOMWindowUtils.getBoundsWithoutFlushing(aElement);
          if (popupDirection == "rtl") {
            let offset = elementRect.right - documentRect.right;
            this.style.marginRight = offset + "px";
          } else {
            let offset = documentRect.left - elementRect.left;
            this.style.marginLeft = offset + "px";
          }

          // Keep the popup items' site icons aligned with the urlbar's identity
          // icon if it's not too far from the edge of the window.  We define
          // "too far" as "more than 30% of the window's width AND more than
          // 250px".  Do this *before* adding any items because when the new
          // value of the margins are different from the previous value, over-
          // and underflow must be handled for each item already in the popup.
          let needsHandleOverUnderflow = false;
          let boundToCheck = popupDirection == "rtl" ? "right" : "left";
          let inputRect = this.DOMWindowUtils.getBoundsWithoutFlushing(aInput);
          let startOffset = Math.abs(inputRect[boundToCheck] - documentRect[boundToCheck]);
          let alignSiteIcons = startOffset / width <= 0.3 || startOffset <= 250;
          if (alignSiteIcons) {
            // Calculate the end margin if we have a start margin.
            let boundToCheckEnd = popupDirection == "rtl" ? "left" : "right";
            let endOffset = Math.abs(inputRect[boundToCheckEnd] -
                                     documentRect[boundToCheckEnd]);
            if (endOffset > startOffset * 2) {
              // Provide more space when aligning would result in an unbalanced
              // margin. This allows the location bar to be moved to the start
              // of the navigation toolbar to reclaim space for results.
              endOffset = startOffset;
            }
            let identityIcon = document.getElementById("identity-icon");
            let identityRect =
              this.DOMWindowUtils.getBoundsWithoutFlushing(identityIcon);
            let start = popupDirection == "rtl" ?
                        documentRect.right - identityRect.right :
                        identityRect.left;
            if (!this.margins || start != this.margins.start ||
                                 endOffset != this.margins.end ||
                                 width != this.margins.width) {
              this.margins = { start, end: endOffset, width };
              needsHandleOverUnderflow = true;
            }
          } else if (this.margins) {
            // Reset the alignment so that the site icons are positioned
            // according to whatever's in the CSS.
            this.margins = undefined;
            needsHandleOverUnderflow = true;
          }

          // Now that the margins have been set, start adding items (via
          // _invalidate).
          this.mInput = aInput;
          this.input.controller.setInitiallySelectedIndex(this._isFirstResultHeuristic ? 0 : -1);
          this.input.userSelectionBehavior = "none";
          this._invalidate();

          try {
            let whichNotification = aInput.whichSearchSuggestionsNotification;
            if (whichNotification != "none") {
              // Update the impressions count on real popupshown, since there's
              // no guarantee openPopup will be respected by the platform.
              // Though, we must ensure the handled event is the expected one.
              let impressionId = this._searchSuggestionsImpressionId = {};
              this.addEventListener("popupshown", () => {
                if (this._searchSuggestionsImpressionId == impressionId)
                  aInput.updateSearchSuggestionsNotificationImpressions(whichNotification);
              }, {once: true});
              this._showSearchSuggestionsNotification(whichNotification, popupDirection);
            } else if (this.classList.contains("showSearchSuggestionsNotification")) {
              this._hideSearchSuggestionsNotification();
            }
          } catch (ex) {
            // Not critical for the urlbar functionality, just report the error.
            Cu.reportError(ex);
          }

          // Position the popup below the navbar.  To get the y-coordinate,
          // which is an offset from the bottom of the input, subtract the
          // bottom of the navbar from the buttom of the input.
          let yOffset = Math.round(
            this.DOMWindowUtils.getBoundsWithoutFlushing(document.getElementById("nav-bar")).bottom -
            this.DOMWindowUtils.getBoundsWithoutFlushing(aInput).bottom);

          this.openPopup(aElement, "after_start", 0, yOffset, false, false);

          // Do this immediately after we've requested the popup to open. This
          // will cause sync reflows but prevents flickering.
          if (needsHandleOverUnderflow) {
            for (let item of this.richlistbox.childNodes) {
              item.handleOverUnderflow();
            }
          }
        ]]></body>
      </method>

      <method name="_showSearchSuggestionsNotification">
        <parameter name="whichNotification"/>
        <parameter name="popupDirection"/>
        <body>
          <![CDATA[
          if (whichNotification == "opt-out") {
            if (this.margins) {
              this.searchSuggestionsNotification.style.paddingInlineStart =
                this.margins.start + "px";
            } else {
              this.searchSuggestionsNotification.style.removeProperty("padding-inline-start");
            }

            // We want to animate the opt-out hint only once.
            if (!this._firstSearchSuggestionsNotification) {
              this._firstSearchSuggestionsNotification = true;
              this.searchSuggestionsNotification.setAttribute("animate", "true");
            }
          }

          this.searchSuggestionsNotification.setAttribute("aria-describedby",
                                                          "search-suggestions-hint");

          // With the notification shown, the listbox's height can sometimes be
          // too small when it's flexed, as it normally is.  Also, it can start
          // out slightly scrolled down.  Both problems appear together, most
          // often when the popup is very narrow and the notification's text
          // must wrap.  Work around them by removing the flex.
          //
          // But without flexing the listbox, the listbox's height animation
          // sometimes fails to complete, leaving the popup too tall.  Work
          // around that problem by disabling the listbox animation.
          this.richlistbox.flex = 0;
          this.setAttribute("dontanimate", "true");

          this.classList.add("showSearchSuggestionsNotification");
          // Don't show the one-off buttons if we are showing onboarding and
          // there's no result, since it would be ugly and pointless.
          this.footer.collapsed = this.matchCount == 0;
          this.input.tabScrolling = this.matchCount != 0;

          // This event allows accessibility APIs to see the notification.
          if (!this.popupOpen) {
            let event = document.createEvent("Events");
            event.initEvent("AlertActive", true, true);
            this.searchSuggestionsNotification.dispatchEvent(event);
          }
          ]]>
        </body>
      </method>

      <method name="_hideSearchSuggestionsNotification">
        <body>
          <![CDATA[
          this.classList.remove("showSearchSuggestionsNotification");
          this.richlistbox.flex = 1;
          this.removeAttribute("dontanimate");
          this.searchSuggestionsNotification.removeAttribute("animate");
          if (this.matchCount) {
            // Update popup height.
            this._invalidate();
          } else {
            this.closePopup();
          }
          ]]>
        </body>
      </method>

      <method name="_selectedOneOffChanged">
        <body><![CDATA[
          // Update all searchengine result items to use the newly selected
          // engine.
          for (let item of this.richlistbox.childNodes) {
            if (item.collapsed) {
              break;
            }
            let url = item.getAttribute("url");
            if (url) {
              let action = item._parseActionUrl(url);
              if (action && action.type == "searchengine") {
                item._adjustAcItem();
              }
            }
          }
        ]]></body>
      </method>

      <!-- This handles keypress changes to the selection among the one-off
           search buttons and between the one-offs and the listbox.  It returns
           true if the keypress was consumed and false if not. -->
      <method name="handleKeyPress">
        <parameter name="aEvent"/>
        <body><![CDATA[
          this.oneOffSearchButtons.handleKeyPress(aEvent, this.matchCount,
                                                  !this._isFirstResultHeuristic,
                                                  gBrowser.userTypedValue);
          return aEvent.defaultPrevented && !aEvent.urlbarDeferred;
        ]]></body>
      </method>

      <!-- This is called when a one-off is clicked and when "search in new tab"
           is selected from a one-off context menu. -->
      <method name="handleOneOffSearch">
        <parameter name="event"/>
        <parameter name="engine"/>
        <parameter name="where"/>
        <parameter name="params"/>
        <body><![CDATA[
          this.input.handleCommand(event, where, params);
        ]]></body>
      </method>

      <!-- Result listitems call this to determine which search engine they
           should show in their labels and include in their url attributes. -->
      <property name="overrideSearchEngineName" readonly="true">
        <getter><![CDATA[
          let button = this.oneOffSearchButtons.selectedButton;
          return button && button.engine && button.engine.name;
        ]]></getter>
      </property>

      <method name="createResultLabel">
        <parameter name="item"/>
        <parameter name="proposedLabel"/>
        <body>
          <![CDATA[
            let parts = [proposedLabel];

            let action = this.input._parseActionUrl(item.getAttribute("url"));
            if (action) {
              switch (action.type) {
              case "searchengine":
                parts = [
                  action.params.searchSuggestion || action.params.searchQuery,
                  action.params.engineName,
                ];
                break;
              case "switchtab":
              case "remotetab":
                parts = [
                  item.getAttribute("title"),
                  item.getAttribute("displayurl"),
                ];
                break;
              }
            }

            let types = item.getAttribute("type").split(/\s+/);
            let type = types.find(t => t != "action" && t != "heuristic");
            try {
              // Some types intentionally do not map to strings, which is not
              // an error.
              parts.push(this._bundle.GetStringFromName(type + "ResultLabel"));
            } catch (e) {}

            return parts.filter(str => str).join(" ");
          ]]>
        </body>
      </method>

      <method name="maybeSetupSpeculativeConnect">
        <parameter name="aUriString"/>
        <body><![CDATA[
          try {
            let uri = makeURI(aUriString);
            Services.io.speculativeConnect2(uri, gBrowser.contentPrincipal, null);
          } catch (ex) {
            // Can't setup speculative connection for this uri string for some
            // reason, just ignore it.
          }
        ]]></body>
      </method>

      <method name="onResultsAdded">
        <body>
          <![CDATA[
            // If nothing is selected yet, select the first result if it is a
            // pre-selected "heuristic" result.  (See UnifiedComplete.js.)
            if (this.selectedIndex == -1 && this._isFirstResultHeuristic) {
              // Don't fire DOMMenuItemActive so that screen readers still see
              // the input as being focused.
              this.richlistbox.suppressMenuItemEvent = true;
              this.input.controller.setInitiallySelectedIndex(0);
              this.richlistbox.suppressMenuItemEvent = false;
            }
            // If this is the first time we get the result from the current
            // search and we are not in the private context, we can speculatively
            // connect to the intended site as a performance optimization.
            if (!this.input.gotResultForCurrentQuery &&
                this.input.speculativeConnectEnabled &&
                !this.input.inPrivateContext &&
                this.input.mController.matchCount > 0) {
              let firstStyle = this.input.mController.getStyleAt(0);
              if (firstStyle.includes("autofill")) {
                let uri = this.input.mController.getFinalCompleteValueAt(0);
                // "http" will be stripped out, but other scheme won't.
                if (!uri.includes("://")) {
                  uri = "http://" + uri;
                }
                this.maybeSetupSpeculativeConnect(uri);
              } else if (firstStyle.includes("searchengine") &&
                         this.input.browserSearchSuggestEnabled &&
                         this.input.urlbarSearchSuggestEnabled) {
                // Preconnect to the current search engine only if the search
                // suggestions are enabled.
                let engine = Services.search.currentEngine;
                engine.speculativeConnect({window,
                                           originAttributes: gBrowser.contentPrincipal.originAttributes});
              }
            }

            // When a result is present the footer should always be visible.
            this.footer.collapsed = false;
            this.input.tabScrolling = true;

            this.input.gotResultForCurrentQuery = true;
            this.input.replaySafeDeferredKeyEvents();
          ]]>
        </body>
      </method>

      <method name="_onSearchBegin">
        <body><![CDATA[
          // Set the selected index to 0 (heuristic) until a result comes back
          // and we can evaluate it better.
          //
          // This is required to properly manage delayed handleEnter:
          // 1. if a search starts we set selectedIndex to 0 here, and it will
          //    be updated by onResultsAdded. Since selectedIndex is 0,
          //    handleEnter will delay the action if a result didn't arrive yet.
          // 2. if a search doesn't start (for example if autocomplete is
          //    disabled), this won't be called, and the selectedIndex will be
          //    the default -1 value. Then handleEnter will know it should not
          //    delay the action, cause a result wont't ever arrive.
          this.input.controller.setInitiallySelectedIndex(0);

          // Since we are starting a new search, reset the currently selected
          // one-off button, to cover those cases where the oneoff buttons
          // binding won't receive an actual DOM event. For example, a search
          // could be started without an actual input event, and the popup may
          // not have been closed from the previous search.
          this.oneOffSearchButtons.selectedButton = null;
        ]]></body>
      </method>

      <field name="_addonIframe">null</field>
      <field name="_addonIframeOwner">null</field>
      <field name="_addonIframeOverriddenFunctionsByName">{}</field>

      <!-- These methods must be overridden and properly handled by the API
           runtime so that it doesn't break the popup.  If any of these methods
           is not overridden, then initAddonIframe should throw. -->
      <field name="_addonIframeOverrideFunctionNames">[
        "_invalidate",
      ]</field>

      <field name="_addonIframeHiddenAnonids">[
        "search-suggestions-notification",
        "richlistbox",
        "one-off-search-buttons",
      ]</field>
      <field name="_addonIframeHiddenDisplaysByAnonid">{}</field>

      <method name="initAddonIframe">
        <parameter name="owner"/>
        <parameter name="overrides"/>
        <body><![CDATA[
          if (this._addonIframeOwner) {
            // Another add-on has already requested the iframe.  Return null to
            // signal to the calling add-on that it should not take over the
            // popup.  First add-on wins for now.
            return null;
          }
          // Make sure all overrides are provided before doing anything.
          for (let name of this._addonIframeOverrideFunctionNames) {
            if (typeof(overrides[name]) != "function") {
              throw new Error(
                "Override for method '" + name + "' must be given"
              );
            }
          }
          // OK, insert the iframe.
          this._addonIframeOwner = owner;
          this._addonIframe = this._makeAddonIframe();
          this._addonIframeOverriddenFunctionsByName = {};
          for (let name of this._addonIframeOverrideFunctionNames) {
            this._addonIframeOverriddenFunctionsByName[name] = this[name];
            this[name] = overrides[name];
          }
          return this._addonIframe;
        ]]></body>
      </method>

      <method name="destroyAddonIframe">
        <parameter name="owner"/>
        <body><![CDATA[
          if (this._addonIframeOwner != owner) {
            throw new Error("You're not the iframe owner");
          }
          this._addonIframeOwner = null;
          this._addonIframe.remove();
          this._addonIframe = null;
          for (let anonid of this._addonIframeHiddenAnonids) {
            let child = document.getAnonymousElementByAttribute(
              this, "anonid", anonid
            );
            child.style.display =
              this._addonIframeHiddenDisplaysByAnonid[anonid];
          }
          for (let name in this._addonIframeOverriddenFunctionsByName) {
            this[name] = this._addonIframeOverriddenFunctionsByName[name];
          }
          this._addonIframeOverriddenFunctionsByName = {};
        ]]></body>
      </method>

      <method name="_makeAddonIframe">
        <body><![CDATA[
          this._addonIframeHiddenDisplaysByAnonid = {};
          for (let anonid of this._addonIframeHiddenAnonids) {
            let child = document.getAnonymousElementByAttribute(
              this, "anonid", anonid
            );
            this._addonIframeHiddenDisplaysByAnonid[anonid] =
              child.style.display;
            child.style.display = "none";
          }
          let XUL_NS =
            "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
          let iframe = document.createElementNS(XUL_NS, "iframe");
          iframe.setAttribute("type", "content");
          iframe.setAttribute("flex", "1");
          iframe.style.transition = "height 100ms";
          this.appendChild(iframe);
          return iframe;
        ]]></body>
      </method>

    </implementation>
    <handlers>

      <handler event="SelectedOneOffButtonChanged"><![CDATA[
        this._selectedOneOffChanged();
      ]]></handler>

      <handler event="mousedown"><![CDATA[
        // Required to make the xul:label.text-link elements in the search
        // suggestions notification work correctly when clicked on Linux.
        // This is copied from the mousedown handler in
        // browser-search-autocomplete-result-popup, which apparently had a
        // similar problem.
        event.preventDefault();

        if (!this.input.speculativeConnectEnabled) {
          return;
        }
        if (event.button == 2) {
          // Ignore right-clicks.
          return;
        }
        // Ensure the user is clicking on an url instead of other buttons
        // on the popup.
        let elt = event.originalTarget;
        while (elt && elt.localName != "richlistitem" && elt != this) {
          elt = elt.parentNode;
        }
        if (!elt || elt.localName != "richlistitem") {
          return;
        }
        // The user might click on a ghost entry which was removed because of
        // the coming new results.
        if (this.input.controller.matchCount <= this.selectedIndex) {
          return;
        }

        let url = this.input.controller.getFinalCompleteValueAt(this.selectedIndex);

        // Whitelist the cases that we want to speculative connect, and ignore
        // other moz-action uris or fancy protocols.
        // Note that it's likely we've speculatively connected to the first
        // url because it is a heuristic "autofill" result (see bug 1348275).
        // "moz-action:searchengine" is also the same case. (see bug 1355443)
        // So we won't duplicate the effort here.
        if (url.startsWith("http") && this.selectedIndex > 0) {
          this.maybeSetupSpeculativeConnect(url);
        } else if (url.startsWith("moz-action:remotetab")) {
          // URL is in the format moz-action:ACTION,PARAMS
          // Where PARAMS is a JSON encoded object.
          const MOZ_ACTION_REGEX = /^moz-action:([^,]+),(.*)$/;
          if (!MOZ_ACTION_REGEX.test(url))
            return;

          let params = JSON.parse(url.match(MOZ_ACTION_REGEX)[2]);
          if (params.url) {
            this.maybeSetupSpeculativeConnect(decodeURIComponent(params.url));
          }
        }

      ]]></handler>

    </handlers>
  </binding>

  <binding id="addon-progress-notification" extends="chrome://global/content/bindings/notification.xml#popup-notification">
    <implementation>
      <constructor><![CDATA[
        if (!this.notification)
          return;

        this.notification.options.installs.forEach(function(aInstall) {
          aInstall.addListener(this);
        }, this);

        // Calling updateProgress can sometimes cause this notification to be
        // removed in the middle of refreshing the notification panel which
        // makes the panel get refreshed again. Just initialise to the
        // undetermined state and then schedule a proper check at the next
        // opportunity
        this.setProgress(0, -1);
        this._updateProgressTimeout = setTimeout(this.updateProgress.bind(this), 0);
      ]]></constructor>

      <destructor><![CDATA[
        this.destroy();
      ]]></destructor>

      <field name="progressmeter" readonly="true">
        document.getElementById("addon-progress-notification-progressmeter");
      </field>
      <field name="progresstext" readonly="true">
        document.getElementById("addon-progress-notification-progresstext");
      </field>
      <property name="DownloadUtils" readonly="true">
        <getter><![CDATA[
          let module = {};
          ChromeUtils.import("resource://gre/modules/DownloadUtils.jsm", module);
          Object.defineProperty(this, "DownloadUtils", {
            configurable: true,
            enumerable: true,
            writable: true,
            value: module.DownloadUtils
          });
          return module.DownloadUtils;
        ]]></getter>
      </property>

      <method name="destroy">
        <body><![CDATA[
          if (!this.notification)
            return;

          this.notification.options.installs.forEach(function(aInstall) {
            aInstall.removeListener(this);
          }, this);
          clearTimeout(this._updateProgressTimeout);
        ]]></body>
      </method>

      <method name="setProgress">
        <parameter name="aProgress"/>
        <parameter name="aMaxProgress"/>
        <body><![CDATA[
          if (aMaxProgress == -1) {
            this.progressmeter.setAttribute("mode", "undetermined");
          } else {
            this.progressmeter.setAttribute("mode", "determined");
            this.progressmeter.setAttribute("value", (aProgress * 100) / aMaxProgress);
          }

          let now = Date.now();

          if (!this.notification.lastUpdate) {
            this.notification.lastUpdate = now;
            this.notification.lastProgress = aProgress;
            return;
          }

          let delta = now - this.notification.lastUpdate;
          if ((delta < 400) && (aProgress < aMaxProgress))
            return;

          delta /= 1000;

          // This algorithm is the same used by the downloads code.
          let speed = (aProgress - this.notification.lastProgress) / delta;
          if (this.notification.speed)
            speed = speed * 0.9 + this.notification.speed * 0.1;

          this.notification.lastUpdate = now;
          this.notification.lastProgress = aProgress;
          this.notification.speed = speed;

          let status = null;
          [status, this.notification.last] = this.DownloadUtils.getDownloadStatus(aProgress, aMaxProgress, speed, this.notification.last);
          this.progresstext.setAttribute("value", status);
          this.progresstext.setAttribute("tooltiptext", status);
        ]]></body>
      </method>

      <method name="cancel">
        <body><![CDATA[
          let installs = this.notification.options.installs;
          installs.forEach(function(aInstall) {
            try {
              aInstall.cancel();
            } catch (e) {
              // Cancel will throw if the download has already failed
            }
          }, this);

          PopupNotifications.remove(this.notification);
        ]]></body>
      </method>

      <method name="updateProgress">
        <body><![CDATA[
          if (!this.notification)
            return;

          let downloadingCount = 0;
          let progress = 0;
          let maxProgress = 0;

          this.notification.options.installs.forEach(function(aInstall) {
            if (aInstall.maxProgress == -1)
              maxProgress = -1;
            progress += aInstall.progress;
            if (maxProgress >= 0)
              maxProgress += aInstall.maxProgress;
            if (aInstall.state < AddonManager.STATE_DOWNLOADED)
              downloadingCount++;
          });

          if (downloadingCount == 0) {
            this.destroy();
            if (Services.prefs.getBoolPref("xpinstall.customConfirmationUI", false)) {
              this.progressmeter.setAttribute("mode", "undetermined");
              let status = gNavigatorBundle.getString("addonDownloadVerifying");
              this.progresstext.setAttribute("value", status);
              this.progresstext.setAttribute("tooltiptext", status);
            } else {
              PopupNotifications.remove(this.notification);
            }
          } else {
            this.setProgress(progress, maxProgress);
          }
        ]]></body>
      </method>

      <method name="onDownloadProgress">
        <body><![CDATA[
          this.updateProgress();
        ]]></body>
      </method>

      <method name="onDownloadFailed">
        <body><![CDATA[
          this.updateProgress();
        ]]></body>
      </method>

      <method name="onDownloadCancelled">
        <body><![CDATA[
          this.updateProgress();
        ]]></body>
      </method>

      <method name="onDownloadEnded">
        <body><![CDATA[
          this.updateProgress();
        ]]></body>
      </method>
    </implementation>
  </binding>
</bindings>
PK
!<n�p1�1�0chrome/browser/content/browser/utilityOverlay.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Services = object with smart getters for common XPCOM services
ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");

ChromeUtils.defineModuleGetter(this, "RecentWindow",
                               "resource:///modules/RecentWindow.jsm");

ChromeUtils.defineModuleGetter(this, "ShellService",
                               "resource:///modules/ShellService.jsm");

ChromeUtils.defineModuleGetter(this, "ContextualIdentityService",
                               "resource://gre/modules/ContextualIdentityService.jsm");

XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
                                   "@mozilla.org/browser/aboutnewtab-service;1",
                                   "nsIAboutNewTabService");

Object.defineProperty(this, "BROWSER_NEW_TAB_URL", {
  configurable: true,
  enumerable: true,
  get() {
    if (PrivateBrowsingUtils.isWindowPrivate(window) &&
        !PrivateBrowsingUtils.permanentPrivateBrowsing &&
        !aboutNewTabService.overridden) {
      return "about:privatebrowsing";
    }
    return aboutNewTabService.newTabURL;
  },
});

var TAB_DROP_TYPE = "application/x-moz-tabbrowser-tab";

var gBidiUI = false;

/**
 * Determines whether the given url is considered a special URL for new tabs.
 */
function isBlankPageURL(aURL) {
  return aURL == "about:blank" ||
         aURL == "about:home" ||
         aURL == BROWSER_NEW_TAB_URL;
}

function getBrowserURL() {
  return "chrome://browser/content/browser.xul";
}

function getTopWin(skipPopups) {
  // If this is called in a browser window, use that window regardless of
  // whether it's the frontmost window, since commands can be executed in
  // background windows (bug 626148).
  if (top.document.documentElement.getAttribute("windowtype") == "navigator:browser" &&
      (!skipPopups || top.toolbar.visible))
    return top;

  let isPrivate = PrivateBrowsingUtils.isWindowPrivate(window);
  return RecentWindow.getMostRecentBrowserWindow({private: isPrivate,
                                                  allowPopups: !skipPopups});
}

function getBoolPref(prefname, def) {
  try {
    return Services.prefs.getBoolPref(prefname);
  } catch (er) {
    return def;
  }
}

function doGetProtocolFlags(aURI) {
  let handler = Services.io.getProtocolHandler(aURI.scheme);
  // see DoGetProtocolFlags in nsIProtocolHandler.idl
  return handler instanceof Ci.nsIProtocolHandlerWithDynamicFlags ?
         handler.QueryInterface(Ci.nsIProtocolHandlerWithDynamicFlags).getFlagsForURI(aURI) :
         handler.protocolFlags;
}

/**
 * openUILink handles clicks on UI elements that cause URLs to load.
 *
 * As the third argument, you may pass an object with the same properties as
 * accepted by openUILinkIn, plus "ignoreButton" and "ignoreAlt".
 *
 * @param url {string}
 * @param event {Event | Object} Event or JSON object representing an Event
 * @param {Boolean | Object} aIgnoreButton
 * @param {Boolean} aIgnoreButton
 * @param {Boolean} aIgnoreAlt
 * @param {Boolean} aAllowThirdPartyFixup
 * @param {Object} aPostData
 * @param {nsIURI} aReferrerURI
 */
function openUILink(url, event, aIgnoreButton, aIgnoreAlt, aAllowThirdPartyFixup,
                    aPostData, aReferrerURI) {
  let params;

  if (aIgnoreButton && typeof aIgnoreButton == "object") {
    params = aIgnoreButton;

    // don't forward "ignoreButton" and "ignoreAlt" to openUILinkIn
    aIgnoreButton = params.ignoreButton;
    aIgnoreAlt = params.ignoreAlt;
    delete params.ignoreButton;
    delete params.ignoreAlt;
  } else {
    params = {
      allowThirdPartyFixup: aAllowThirdPartyFixup,
      postData: aPostData,
      referrerURI: aReferrerURI,
      referrerPolicy: Ci.nsIHttpChannel.REFERRER_POLICY_UNSET,
      initiatingDoc: event ? event.target.ownerDocument : null,
    };
  }

  let where = whereToOpenLink(event, aIgnoreButton, aIgnoreAlt);
  openUILinkIn(url, where, params);
}


/**
 * whereToOpenLink() looks at an event to decide where to open a link.
 *
 * The event may be a mouse event (click, double-click, middle-click) or keypress event (enter).
 *
 * On Windows, the modifiers are:
 * Ctrl        new tab, selected
 * Shift       new window
 * Ctrl+Shift  new tab, in background
 * Alt         save
 *
 * Middle-clicking is the same as Ctrl+clicking (it opens a new tab).
 *
 * Exceptions:
 * - Alt is ignored for menu items selected using the keyboard so you don't accidentally save stuff.
 *    (Currently, the Alt isn't sent here at all for menu items, but that will change in bug 126189.)
 * - Alt is hard to use in context menus, because pressing Alt closes the menu.
 * - Alt can't be used on the bookmarks toolbar because Alt is used for "treat this as something draggable".
 * - The button is ignored for the middle-click-paste-URL feature, since it's always a middle-click.
 *
 * @param e {Event|Object} Event or JSON Object
 * @param ignoreButton {Boolean}
 * @param ignoreAlt {Boolean}
 * @returns {"current" | "tabshifted" | "tab" | "save" | "window"}
 */
function whereToOpenLink(e, ignoreButton, ignoreAlt) {
  // This method must treat a null event like a left click without modifier keys (i.e.
  // e = { shiftKey:false, ctrlKey:false, metaKey:false, altKey:false, button:0 })
  // for compatibility purposes.
  if (!e)
    return "current";

  var shift = e.shiftKey;
  var ctrl =  e.ctrlKey;
  var meta =  e.metaKey;
  var alt  =  e.altKey && !ignoreAlt;

  // ignoreButton allows "middle-click paste" to use function without always opening in a new window.
  var middle = !ignoreButton && e.button == 1;
  var middleUsesTabs = getBoolPref("browser.tabs.opentabfor.middleclick", true);

  // Don't do anything special with right-mouse clicks.  They're probably clicks on context menu items.

  var metaKey = AppConstants.platform == "macosx" ? meta : ctrl;
  if (metaKey || (middle && middleUsesTabs))
    return shift ? "tabshifted" : "tab";

  if (alt && getBoolPref("browser.altClickSave", false))
    return "save";

  if (shift || (middle && !middleUsesTabs))
    return "window";

  return "current";
}

/* openUILinkIn opens a URL in a place specified by the parameter |where|.
 *
 * |where| can be:
 *  "current"     current tab            (if there aren't any browser windows, then in a new window instead)
 *  "tab"         new tab                (if there aren't any browser windows, then in a new window instead)
 *  "tabshifted"  same as "tab" but in background if default is to select new tabs, and vice versa
 *  "window"      new window
 *  "save"        save to disk (with no filename hint!)
 *
 * aAllowThirdPartyFixup controls whether third party services such as Google's
 * I Feel Lucky are allowed to interpret this URL. This parameter may be
 * undefined, which is treated as false.
 *
 * Instead of aAllowThirdPartyFixup, you may also pass an object with any of
 * these properties:
 *   allowThirdPartyFixup (boolean)
 *   postData             (nsIInputStream)
 *   referrerURI          (nsIURI)
 *   relatedToCurrent     (boolean)
 *   skipTabAnimation     (boolean)
 *   allowPinnedTabHostChange (boolean)
 *   allowPopups          (boolean)
 *   userContextId        (unsigned int)
 *   targetBrowser        (XUL browser)
 */
function openUILinkIn(url, where, aAllowThirdPartyFixup, aPostData, aReferrerURI) {
  var params;

  if (arguments.length == 3 && typeof arguments[2] == "object") {
    params = aAllowThirdPartyFixup;
  } else {
    params = {
      allowThirdPartyFixup: aAllowThirdPartyFixup,
      postData: aPostData,
      referrerURI: aReferrerURI,
      referrerPolicy: Ci.nsIHttpChannel.REFERRER_POLICY_UNSET,
    };
  }

  params.fromChrome = true;

  openLinkIn(url, where, params);
}

/* eslint-disable complexity */
function openLinkIn(url, where, params) {
  if (!where || !url)
    return;

  var aFromChrome           = params.fromChrome;
  var aAllowThirdPartyFixup = params.allowThirdPartyFixup;
  var aPostData             = params.postData;
  var aCharset              = params.charset;
  var aReferrerURI          = params.referrerURI;
  var aReferrerPolicy       = ("referrerPolicy" in params ?
      params.referrerPolicy : Ci.nsIHttpChannel.REFERRER_POLICY_UNSET);
  var aRelatedToCurrent     = params.relatedToCurrent;
  var aAllowMixedContent    = params.allowMixedContent;
  var aForceAllowDataURI    = params.forceAllowDataURI;
  var aInBackground         = params.inBackground;
  var aDisallowInheritPrincipal = params.disallowInheritPrincipal;
  var aInitiatingDoc        = params.initiatingDoc;
  var aIsPrivate            = params.private;
  var aSkipTabAnimation     = params.skipTabAnimation;
  var aAllowPinnedTabHostChange = !!params.allowPinnedTabHostChange;
  var aNoReferrer           = params.noReferrer;
  var aAllowPopups          = !!params.allowPopups;
  var aUserContextId        = params.userContextId;
  var aIndicateErrorPageLoad = params.indicateErrorPageLoad;
  var aPrincipal            = params.originPrincipal;
  var aTriggeringPrincipal  = params.triggeringPrincipal;
  var aForceAboutBlankViewerInCurrent =
      params.forceAboutBlankViewerInCurrent;

  if (where == "save") {
    // TODO(1073187): propagate referrerPolicy.

    // ContentClick.jsm passes isContentWindowPrivate for saveURL instead of passing a CPOW initiatingDoc
    if ("isContentWindowPrivate" in params) {
      saveURL(url, null, null, true, true, aNoReferrer ? null : aReferrerURI, null, params.isContentWindowPrivate);
    } else {
      if (!aInitiatingDoc) {
        Cu.reportError("openUILink/openLinkIn was called with " +
          "where == 'save' but without initiatingDoc.  See bug 814264.");
        return;
      }
      saveURL(url, null, null, true, true, aNoReferrer ? null : aReferrerURI, aInitiatingDoc);
    }
    return;
  }

  // Establish which window we'll load the link in.
  let w;
  if (where == "current" && params.targetBrowser) {
    w = params.targetBrowser.ownerGlobal;
  } else {
    w = getTopWin();
  }
  // We don't want to open tabs in popups, so try to find a non-popup window in
  // that case.
  if ((where == "tab" || where == "tabshifted") &&
      w && !w.toolbar.visible) {
    w = getTopWin(true);
    aRelatedToCurrent = false;
  }

  // Teach the principal about the right OA to use, e.g. in case when
  // opening a link in a new private window, or in a new container tab.
  // Please note we do not have to do that for SystemPrincipals and we
  // can not do it for NullPrincipals since NullPrincipals are only
  // identical if they actually are the same object (See Bug: 1346759)
  function useOAForPrincipal(principal) {
    if (principal && principal.isCodebasePrincipal) {
      let attrs = {
        userContextId: aUserContextId,
        privateBrowsingId: aIsPrivate || (w && PrivateBrowsingUtils.isWindowPrivate(w)),
      };
      return Services.scriptSecurityManager.createCodebasePrincipal(principal.URI, attrs);
    }
    return principal;
  }
  aPrincipal = useOAForPrincipal(aPrincipal);
  aTriggeringPrincipal = useOAForPrincipal(aTriggeringPrincipal);

  if (!w || where == "window") {
    let features = "chrome,dialog=no,all";
    if (aIsPrivate) {
      features += ",private";
      // To prevent regular browsing data from leaking to private browsing sites,
      // strip the referrer when opening a new private window. (See Bug: 1409226)
      aNoReferrer = true;
    }

    // This propagates to window.arguments.
    var sa = Cc["@mozilla.org/array;1"].
             createInstance(Ci.nsIMutableArray);

    var wuri = Cc["@mozilla.org/supports-string;1"].
               createInstance(Ci.nsISupportsString);
    wuri.data = url;

    let charset = null;
    if (aCharset) {
      charset = Cc["@mozilla.org/supports-string;1"]
                  .createInstance(Ci.nsISupportsString);
      charset.data = "charset=" + aCharset;
    }

    var allowThirdPartyFixupSupports = Cc["@mozilla.org/supports-PRBool;1"].
                                       createInstance(Ci.nsISupportsPRBool);
    allowThirdPartyFixupSupports.data = aAllowThirdPartyFixup;

    var referrerURISupports = null;
    if (aReferrerURI && !aNoReferrer) {
      referrerURISupports = Cc["@mozilla.org/supports-string;1"].
                            createInstance(Ci.nsISupportsString);
      referrerURISupports.data = aReferrerURI.spec;
    }

    var referrerPolicySupports = Cc["@mozilla.org/supports-PRUint32;1"].
                                 createInstance(Ci.nsISupportsPRUint32);
    referrerPolicySupports.data = aReferrerPolicy;

    var userContextIdSupports = Cc["@mozilla.org/supports-PRUint32;1"].
                                 createInstance(Ci.nsISupportsPRUint32);
    userContextIdSupports.data = aUserContextId;

    sa.appendElement(wuri);
    sa.appendElement(charset);
    sa.appendElement(referrerURISupports);
    sa.appendElement(aPostData);
    sa.appendElement(allowThirdPartyFixupSupports);
    sa.appendElement(referrerPolicySupports);
    sa.appendElement(userContextIdSupports);
    sa.appendElement(aPrincipal);
    sa.appendElement(aTriggeringPrincipal);

    const sourceWindow = (w || window);
    let win;
    if (params.frameOuterWindowID != undefined && sourceWindow) {
      // Only notify it as a WebExtensions' webNavigation.onCreatedNavigationTarget
      // event if it contains the expected frameOuterWindowID params.
      // (e.g. we should not notify it as a onCreatedNavigationTarget if the user is
      // opening a new window using the keyboard shortcut).
      const sourceTabBrowser = sourceWindow.gBrowser.selectedBrowser;
      let delayedStartupObserver = aSubject => {
        if (aSubject == win) {
          Services.obs.removeObserver(delayedStartupObserver, "browser-delayed-startup-finished");
          Services.obs.notifyObservers({
            wrappedJSObject: {
              url,
              createdTabBrowser: win.gBrowser.selectedBrowser,
              sourceTabBrowser,
              sourceFrameOuterWindowID: params.frameOuterWindowID,
            },
          }, "webNavigation-createdNavigationTarget");
        }
      };
      Services.obs.addObserver(delayedStartupObserver, "browser-delayed-startup-finished");
    }
    win = Services.ww.openWindow(sourceWindow, getBrowserURL(), null, features, sa);
    return;
  }

  // We're now committed to loading the link in an existing browser window.

  // Raise the target window before loading the URI, since loading it may
  // result in a new frontmost window (e.g. "javascript:window.open('');").
  w.focus();

  let targetBrowser;
  let loadInBackground;
  let uriObj;

  if (where == "current") {
    targetBrowser = params.targetBrowser || w.gBrowser.selectedBrowser;
    loadInBackground = false;

    try {
      uriObj = Services.io.newURI(url);
    } catch (e) {}

    if (w.gBrowser.getTabForBrowser(targetBrowser).pinned &&
        !aAllowPinnedTabHostChange) {
      try {
        // nsIURI.host can throw for non-nsStandardURL nsIURIs.
        if (!uriObj || (!uriObj.schemeIs("javascript") &&
                        targetBrowser.currentURI.host != uriObj.host)) {
          where = "tab";
          loadInBackground = false;
        }
      } catch (err) {
        where = "tab";
        loadInBackground = false;
      }
    }
  } else {
    // 'where' is "tab" or "tabshifted", so we'll load the link in a new tab.
    loadInBackground = aInBackground;
    if (loadInBackground == null) {
      loadInBackground =
        aFromChrome ? false : getBoolPref("browser.tabs.loadInBackground");
    }
  }

  let focusUrlBar = false;

  switch (where) {
  case "current":
    let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;

    if (aAllowThirdPartyFixup) {
      flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
      flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FIXUP_SCHEME_TYPOS;
    }

    // LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL isn't supported for javascript URIs,
    // i.e. it causes them not to load at all. Callers should strip
    // "javascript:" from pasted strings to protect users from malicious URIs
    // (see stripUnsafeProtocolOnPaste).
    if (aDisallowInheritPrincipal && !(uriObj && uriObj.schemeIs("javascript"))) {
      flags |= Ci.nsIWebNavigation.LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL;
    }

    if (aAllowPopups) {
      flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_POPUPS;
    }
    if (aIndicateErrorPageLoad) {
      flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ERROR_LOAD_CHANGES_RV;
    }
    if (aForceAllowDataURI) {
      flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FORCE_ALLOW_DATA_URI;
    }

    let {URI_INHERITS_SECURITY_CONTEXT} = Ci.nsIProtocolHandler;
    if (aForceAboutBlankViewerInCurrent &&
        (!uriObj ||
         (doGetProtocolFlags(uriObj) & URI_INHERITS_SECURITY_CONTEXT))) {
      // Unless we know for sure we're not inheriting principals,
      // force the about:blank viewer to have the right principal:
      targetBrowser.createAboutBlankContentViewer(aPrincipal);
    }

    targetBrowser.loadURIWithFlags(url, {
      triggeringPrincipal: aTriggeringPrincipal,
      flags,
      referrerURI: aNoReferrer ? null : aReferrerURI,
      referrerPolicy: aReferrerPolicy,
      postData: aPostData,
      userContextId: aUserContextId
    });

    // Don't focus the content area if focus is in the address bar and we're
    // loading the New Tab page.
    focusUrlBar = w.document.activeElement == w.gURLBar.inputField &&
                  w.isBlankPageURL(url);
    break;
  case "tabshifted":
    loadInBackground = !loadInBackground;
    // fall through
  case "tab":
    focusUrlBar = !loadInBackground && w.isBlankPageURL(url)
      && !aboutNewTabService.willNotifyUser;

    let tabUsedForLoad = w.gBrowser.loadOneTab(url, {
      referrerURI: aReferrerURI,
      referrerPolicy: aReferrerPolicy,
      charset: aCharset,
      postData: aPostData,
      inBackground: loadInBackground,
      allowThirdPartyFixup: aAllowThirdPartyFixup,
      relatedToCurrent: aRelatedToCurrent,
      skipAnimation: aSkipTabAnimation,
      allowMixedContent: aAllowMixedContent,
      noReferrer: aNoReferrer,
      userContextId: aUserContextId,
      originPrincipal: aPrincipal,
      triggeringPrincipal: aTriggeringPrincipal,
      focusUrlBar,
    });
    targetBrowser = tabUsedForLoad.linkedBrowser;

    if (params.frameOuterWindowID != undefined && w) {
      // Only notify it as a WebExtensions' webNavigation.onCreatedNavigationTarget
      // event if it contains the expected frameOuterWindowID params.
      // (e.g. we should not notify it as a onCreatedNavigationTarget if the user is
      // opening a new tab using the keyboard shortcut).
      Services.obs.notifyObservers({
        wrappedJSObject: {
          url,
          createdTabBrowser: targetBrowser,
          sourceTabBrowser: w.gBrowser.selectedBrowser,
          sourceFrameOuterWindowID: params.frameOuterWindowID,
        },
      }, "webNavigation-createdNavigationTarget");
    }
    break;
  }

  if (!focusUrlBar && targetBrowser == w.gBrowser.selectedBrowser) {
    // Focus the content, but only if the browser used for the load is selected.
    targetBrowser.focus();
  }
}

// Used as an onclick handler for UI elements with link-like behavior.
// e.g. onclick="checkForMiddleClick(this, event);"
function checkForMiddleClick(node, event) {
  // We should be using the disabled property here instead of the attribute,
  // but some elements that this function is used with don't support it (e.g.
  // menuitem).
  if (node.getAttribute("disabled") == "true")
    return; // Do nothing

  if (event.button == 1) {
    /* Execute the node's oncommand or command.
     *
     * XXX: we should use node.oncommand(event) once bug 246720 is fixed.
     */
    var target = node.hasAttribute("oncommand") ? node :
                 node.ownerDocument.getElementById(node.getAttribute("command"));
    var fn = new Function("event", target.getAttribute("oncommand"));
    fn.call(target, event);

    // If the middle-click was on part of a menu, close the menu.
    // (Menus close automatically with left-click but not with middle-click.)
    closeMenus(event.target);
  }
}

// Populate a menu with user-context menu items. This method should be called
// by onpopupshowing passing the event as first argument.
function createUserContextMenu(event, {
                                        isContextMenu = false,
                                        excludeUserContextId = 0,
                                        showDefaultTab = false,
                                        useAccessKeys = true
                                      } = {}) {
  while (event.target.hasChildNodes()) {
    event.target.firstChild.remove();
  }

  let bundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
  let docfrag = document.createDocumentFragment();

  // If we are excluding a userContextId, we want to add a 'no-container' item.
  if (excludeUserContextId || showDefaultTab) {
    let menuitem = document.createElement("menuitem");
    menuitem.setAttribute("data-usercontextid", "0");
    menuitem.setAttribute("label", bundle.GetStringFromName("userContextNone.label"));
    menuitem.setAttribute("accesskey", bundle.GetStringFromName("userContextNone.accesskey"));

    // We don't set an oncommand/command attribute because if we have
    // to exclude a userContextId we are generating the contextMenu and
    // isContextMenu will be true.

    docfrag.appendChild(menuitem);

    let menuseparator = document.createElement("menuseparator");
    docfrag.appendChild(menuseparator);
  }

  ContextualIdentityService.getPublicIdentities().forEach(identity => {
    if (identity.userContextId == excludeUserContextId) {
      return;
    }

    let menuitem = document.createElement("menuitem");
    menuitem.setAttribute("data-usercontextid", identity.userContextId);
    menuitem.setAttribute("label", ContextualIdentityService.getUserContextLabel(identity.userContextId));

    if (identity.accessKey && useAccessKeys) {
      menuitem.setAttribute("accesskey", bundle.GetStringFromName(identity.accessKey));
    }

    menuitem.classList.add("menuitem-iconic");
    menuitem.setAttribute("data-identity-color", identity.color);

    if (!isContextMenu) {
      menuitem.setAttribute("command", "Browser:NewUserContextTab");
    }

    menuitem.setAttribute("data-identity-icon", identity.icon);

    docfrag.appendChild(menuitem);
  });

  if (!isContextMenu) {
    docfrag.appendChild(document.createElement("menuseparator"));

    let menuitem = document.createElement("menuitem");
    menuitem.setAttribute("label",
                          bundle.GetStringFromName("userContext.aboutPage.label"));
    if (useAccessKeys) {
      menuitem.setAttribute("accesskey",
                            bundle.GetStringFromName("userContext.aboutPage.accesskey"));
    }
    menuitem.setAttribute("command", "Browser:OpenAboutContainers");
    docfrag.appendChild(menuitem);
  }

  event.target.appendChild(docfrag);
  return true;
}

// Closes all popups that are ancestors of the node.
function closeMenus(node) {
  if ("tagName" in node) {
    if (node.namespaceURI == "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
    && (node.tagName == "menupopup" || node.tagName == "popup"))
      node.hidePopup();

    closeMenus(node.parentNode);
  }
}

/** This function takes in a key element and compares it to the keys pressed during an event.
 *
 * @param aEvent
 *        The KeyboardEvent event you want to compare against your key.
 *
 * @param aKey
 *        The <key> element checked to see if it was called in aEvent.
 *        For example, aKey can be a variable set to document.getElementById("key_close")
 *        to check if the close command key was pressed in aEvent.
*/
function eventMatchesKey(aEvent, aKey) {
  let keyPressed = aKey.getAttribute("key").toLowerCase();
  let keyModifiers = aKey.getAttribute("modifiers");
  let modifiers = ["Alt", "Control", "Meta", "Shift"];

  if (aEvent.key != keyPressed) {
    return false;
  }
  let eventModifiers = modifiers.filter(modifier => aEvent.getModifierState(modifier));
  // Check if aEvent has a modifier and aKey doesn't
  if (eventModifiers.length > 0 && keyModifiers.length == 0) {
     return false;
  }
  // Check whether aKey's modifiers match aEvent's modifiers
  if (keyModifiers) {
    keyModifiers = keyModifiers.split(/[\s,]+/);
    // Capitalize first letter of aKey's modifers to compare to aEvent's modifier
    keyModifiers.forEach(function(modifier, index) {
      if (modifier == "accel") {
        keyModifiers[index] = AppConstants.platform == "macosx" ? "Meta" : "Control";
      } else {
        keyModifiers[index] = modifier[0].toUpperCase() + modifier.slice(1);
      }
    });
    return modifiers.every(modifier => keyModifiers.includes(modifier) == aEvent.getModifierState(modifier));
  }
  return true;
}

// Gather all descendent text under given document node.
function gatherTextUnder(root) {
  var text = "";
  var node = root.firstChild;
  var depth = 1;
  while ( node && depth > 0 ) {
    // See if this node is text.
    if ( node.nodeType == Node.TEXT_NODE ) {
      // Add this text to our collection.
      text += " " + node.data;
    } else if ( node instanceof HTMLImageElement) {
      // If it has an "alt" attribute, add that.
      var altText = node.getAttribute( "alt" );
      if ( altText && altText != "" ) {
        text += " " + altText;
      }
    }
    // Find next node to test.
    // First, see if this node has children.
    if ( node.hasChildNodes() ) {
      // Go to first child.
      node = node.firstChild;
      depth++;
    } else {
      // No children, try next sibling (or parent next sibling).
      while ( depth > 0 && !node.nextSibling ) {
        node = node.parentNode;
        depth--;
      }
      if ( node.nextSibling ) {
        node = node.nextSibling;
      }
    }
  }
  // Strip leading and tailing whitespace.
  text = text.trim();
  // Compress remaining whitespace.
  text = text.replace( /\s+/g, " " );
  return text;
}

// This function exists for legacy reasons.
function getShellService() {
  return ShellService;
}

function isBidiEnabled() {
  // first check the pref.
  if (getBoolPref("bidi.browser.ui", false))
    return true;

  // now see if the app locale is an RTL one.
  const isRTL = Services.locale.isAppLocaleRTL;

  if (isRTL) {
    Services.prefs.setBoolPref("bidi.browser.ui", true);
  }
  return isRTL;
}

function openAboutDialog() {
  var enumerator = Services.wm.getEnumerator("Browser:About");
  while (enumerator.hasMoreElements()) {
    // Only open one about window (Bug 599573)
    let win = enumerator.getNext();
    if (win.closed) {
      continue;
    }
    win.focus();
    return;
  }

  var features = "chrome,";
  if (AppConstants.platform == "win") {
    features += "centerscreen,dependent";
  } else if (AppConstants.platform == "macosx") {
    features += "resizable=no,minimizable=no";
  } else {
    features += "centerscreen,dependent,dialog=no";
  }

  window.openDialog("chrome://browser/content/aboutDialog.xul", "", features);
}

function openPreferences(paneID, extraArgs) {
  let histogram = Services.telemetry.getHistogramById("FX_PREFERENCES_OPENED_VIA");
  if (extraArgs && extraArgs.origin) {
    histogram.add(extraArgs.origin);
  } else {
    histogram.add("other");
  }

  // This function is duplicated from preferences.js.
  function internalPrefCategoryNameToFriendlyName(aName) {
    return (aName || "").replace(/^pane./, function(toReplace) { return toReplace[4].toLowerCase(); });
  }

  let win = Services.wm.getMostRecentWindow("navigator:browser");
  let friendlyCategoryName = internalPrefCategoryNameToFriendlyName(paneID);
  let params;
  if (extraArgs && extraArgs.urlParams) {
    params = new URLSearchParams();
    let urlParams = extraArgs.urlParams;
    for (let name in urlParams) {
      if (urlParams[name] !== undefined) {
        params.set(name, urlParams[name]);
      }
    }
  }
  let preferencesURL = "about:preferences" + (params ? "?" + params : "") +
    (friendlyCategoryName ? "#" + friendlyCategoryName : "");
  let newLoad = true;
  let browser = null;
  if (!win) {
    let windowArguments = Cc["@mozilla.org/array;1"]
      .createInstance(Ci.nsIMutableArray);
    let supportsStringPrefURL = Cc["@mozilla.org/supports-string;1"]
      .createInstance(Ci.nsISupportsString);
    supportsStringPrefURL.data = preferencesURL;
    windowArguments.appendElement(supportsStringPrefURL);

    win = Services.ww.openWindow(null, Services.prefs.getCharPref("browser.chromeURL"),
      "_blank", "chrome,dialog=no,all", windowArguments);
  } else {
    let shouldReplaceFragment = friendlyCategoryName ? "whenComparingAndReplace" : "whenComparing";
    newLoad = !win.switchToTabHavingURI(preferencesURL, true, {
      ignoreFragment: shouldReplaceFragment,
      replaceQueryString: true,
      triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
    });
    browser = win.gBrowser.selectedBrowser;
  }

  if (newLoad) {
    Services.obs.addObserver(function panesLoadedObs(prefWin, topic, data) {
      if (!browser) {
        browser = win.gBrowser.selectedBrowser;
      }
      if (prefWin != browser.contentWindow) {
        return;
      }
      Services.obs.removeObserver(panesLoadedObs, "sync-pane-loaded");
    }, "sync-pane-loaded");
  } else if (paneID) {
    browser.contentWindow.gotoPref(paneID);
  }
}

/**
 * Opens the troubleshooting information (about:support) page for this version
 * of the application.
 */
function openTroubleshootingPage() {
  openUILinkIn("about:support", "tab");
}

/**
 * Opens the feedback page for this version of the application.
 */
function openFeedbackPage() {
  var url = Services.urlFormatter.formatURLPref("app.feedback.baseURL");
  openUILinkIn(url, "tab");
}

function openTourPage() {
  let scope = {};
  ChromeUtils.import("resource:///modules/UITour.jsm", scope);
  openUILinkIn(scope.UITour.url, "tab");
}

function buildHelpMenu() {
  document.getElementById("feedbackPage")
          .disabled = !Services.policies.isAllowed("feedbackCommands");

  document.getElementById("helpSafeMode")
          .disabled = !Services.policies.isAllowed("safeMode");

  // Enable/disable the "Report Web Forgery" menu item.
  if (typeof gSafeBrowsing != "undefined") {
    gSafeBrowsing.setReportPhishingMenu();
  }
}

function isElementVisible(aElement) {
  if (!aElement)
    return false;

  // If aElement or a direct or indirect parent is hidden or collapsed,
  // height, width or both will be 0.
  var bo = aElement.boxObject;
  return (bo.height > 0 && bo.width > 0);
}

function makeURLAbsolute(aBase, aUrl) {
  // Note:  makeURI() will throw if aUri is not a valid URI
  return makeURI(aUrl, null, makeURI(aBase)).spec;
}

/**
 * openNewTabWith: opens a new tab with the given URL.
 *
 * @param aURL
 *        The URL to open (as a string).
 * @param aShiftKey
 *        True if shift key is held.  This value is used for the purpose of
 *        determining whether to open in the background.
 * @param aParams
 *        parameters passed to openLinkIn
 */
function openNewTabWith(aURL, aShiftKey, aParams = {}) {

  // As in openNewWindowWith(), we want to pass the charset of the
  // current document over to a new tab.
  if (document.documentElement.getAttribute("windowtype") == "navigator:browser")
    aParams.charset = gBrowser.selectedBrowser.characterSet;

  openLinkIn(aURL, aShiftKey ? "tabshifted" : "tab", aParams);
}

/**
 * openNewWindowWith: opens a new window with the given URL.
 * See openNewTabWith for parameters.
 */
function openNewWindowWith(aURL, aParams = {}) {
  // Extract the current charset menu setting from the current document and
  // use it to initialize the new browser window...
  if (document.documentElement.getAttribute("windowtype") == "navigator:browser")
    aParams.charset = gBrowser.selectedBrowser.characterSet;

  openLinkIn(aURL, "window", aParams);
}

function getHelpLinkURL(aHelpTopic) {
  var url = Services.urlFormatter.formatURLPref("app.support.baseURL");
  return url + aHelpTopic;
}

// aCalledFromModal is optional
function openHelpLink(aHelpTopic, aCalledFromModal, aWhere) {
  var url = getHelpLinkURL(aHelpTopic);
  var where = aWhere;
  if (!aWhere)
    where = aCalledFromModal ? "window" : "tab";

  openUILinkIn(url, where);
}

function openPrefsHelp() {
  // non-instant apply prefwindows are usually modal, so we can't open in the topmost window,
  // since its probably behind the window.
  var instantApply = getBoolPref("browser.preferences.instantApply");

  var helpTopic = document.documentElement.getAttribute("helpTopic");
  openHelpLink(helpTopic, !instantApply);
}

function trimURL(aURL) {
  // This function must not modify the given URL such that calling
  // nsIURIFixup::createFixupURI with the result will produce a different URI.

  // remove single trailing slash for http/https/ftp URLs
  let url = aURL.replace(/^((?:http|https|ftp):\/\/[^/]+)\/$/, "$1");

  // remove http://
  if (!url.startsWith("http://")) {
    return url;
  }
  let urlWithoutProtocol = url.substring(7);

  let flags = Services.uriFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP |
              Services.uriFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS;
  let fixedUpURL, expectedURLSpec;
  try {
    fixedUpURL = Services.uriFixup.createFixupURI(urlWithoutProtocol, flags);
    expectedURLSpec = makeURI(aURL).displaySpec;
  } catch (ex) {
    return url;
  }
  if (fixedUpURL.displaySpec == expectedURLSpec) {
    return urlWithoutProtocol;
  }
  return url;
}
PK
!<'��g
g
,chrome/browser/content/browser/web-panels.js/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Via web-panels.xul
/* import-globals-from browser.js */

const NS_ERROR_MODULE_NETWORK = 2152398848;
const NS_NET_STATUS_READ_FROM = NS_ERROR_MODULE_NETWORK + 8;
const NS_NET_STATUS_WROTE_TO  = NS_ERROR_MODULE_NETWORK + 9;

function getPanelBrowser() {
    return document.getElementById("web-panels-browser");
}

var panelProgressListener = {
    onProgressChange(aWebProgress, aRequest,
                                aCurSelfProgress, aMaxSelfProgress,
                                aCurTotalProgress, aMaxTotalProgress) {
    },

    onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
        if (!aRequest)
          return;

        // ignore local/resource:/chrome: files
        if (aStatus == NS_NET_STATUS_READ_FROM || aStatus == NS_NET_STATUS_WROTE_TO)
           return;

        if (aStateFlags & Ci.nsIWebProgressListener.STATE_START &&
            aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) {
            window.parent.document.getElementById("sidebar-throbber").setAttribute("loading", "true");
        } else if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
                aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) {
            window.parent.document.getElementById("sidebar-throbber").removeAttribute("loading");
        }
    },

    onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {
        UpdateBackForwardCommands(getPanelBrowser().webNavigation);
    },

    onStatusChange(aWebProgress, aRequest, aStatus, aMessage) {
    },

    onSecurityChange(aWebProgress, aRequest, aState) {
    },

    QueryInterface(aIID) {
        if (aIID.equals(Ci.nsIWebProgressListener) ||
            aIID.equals(Ci.nsISupportsWeakReference) ||
            aIID.equals(Ci.nsISupports))
            return this;
        throw Cr.NS_NOINTERFACE;
    }
};

var gLoadFired = false;
function loadWebPanel(aURI) {
    var panelBrowser = getPanelBrowser();
    if (gLoadFired) {
        panelBrowser.webNavigation
                    .loadURI(aURI, nsIWebNavigation.LOAD_FLAGS_NONE,
                             null, null, null);
    }
    panelBrowser.setAttribute("cachedurl", aURI);
}

function load() {
    var panelBrowser = getPanelBrowser();
    panelBrowser.webProgress.addProgressListener(panelProgressListener,
                                                 Ci.nsIWebProgress.NOTIFY_ALL);
    panelBrowser.messageManager.loadFrameScript("chrome://browser/content/content.js", true);
    var cachedurl = panelBrowser.getAttribute("cachedurl");
    if (cachedurl) {
        panelBrowser.webNavigation
                    .loadURI(cachedurl, nsIWebNavigation.LOAD_FLAGS_NONE, null,
                             null, null);
    }

    gLoadFired = true;
}

function unload() {
    getPanelBrowser().webProgress.removeProgressListener(panelProgressListener);
}

function PanelBrowserStop() {
    getPanelBrowser().webNavigation.stop(nsIWebNavigation.STOP_ALL);
}

function PanelBrowserReload() {
    getPanelBrowser().webNavigation
                     .sessionHistory
                     .QueryInterface(nsIWebNavigation)
                     .reload(nsIWebNavigation.LOAD_FLAGS_NONE);
}
PK
!<��H��f�f-chrome/browser/content/browser/web-panels.xul<?xml version="1.0"?>


<?xml-stylesheet href="chrome://browser/skin/" type="text/css"?> 
<?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?>

<!DOCTYPE page [
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
%browserDTD;
<!ENTITY % textcontextDTD SYSTEM "chrome://global/locale/textcontext.dtd">
%textcontextDTD;
]>

<page id="webpanels-window"
        xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        onload="load()" onunload="unload()">
  <script type="application/javascript" src="chrome://global/content/contentAreaUtils.js"/>
  <script type="application/javascript" src="chrome://browser/content/browser.js"/>
  <script type="application/javascript" src="chrome://browser/content/browser-places.js"/>
  <script type="application/javascript" src="chrome://browser/content/web-panels.js"/>

  <broadcasterset id="mainBroadcasterSet">
    <broadcaster id="isFrameImage"/>
  </broadcasterset>

  <commandset id="mainCommandset">
    <command id="Browser:Back"
             oncommand="getPanelBrowser().webNavigation.goBack();"
             disabled="true"/>
    <command id="Browser:Forward"
             oncommand="getPanelBrowser().webNavigation.goForward();"
             disabled="true"/>
    <command id="Browser:Stop" oncommand="PanelBrowserStop();"/>
    <command id="Browser:Reload" oncommand="PanelBrowserReload();"/>
  </commandset>

  <popupset id="mainPopupSet">
    <tooltip id="aHTMLTooltip" page="true"/>
    <menupopup id="contentAreaContextMenu" pagemenu="start"
               onpopupshowing="if (event.target != this)
                                 return true;
                               gContextMenu = new nsContextMenu(this, event.shiftKey);
                               if (gContextMenu.shouldDisplay)
                                 document.popupNode = this.triggerNode;
                               return gContextMenu.shouldDisplay;"
               onpopuphiding="if (event.target != this)
                                return;
                              gContextMenu.hiding();
                              gContextMenu = null;">


      <menugroup id="context-navigation">
        <menuitem id="context-back"
                  class="menuitem-iconic"
                  tooltiptext="&backButton.tooltip;"
                  aria-label="&backCmd.label;"
                  command="Browser:BackOrBackDuplicate"
                  accesskey="&backCmd.accesskey;"
                  onclick="checkForMiddleClick(this, event);"/>
        <menuitem id="context-forward"
                  class="menuitem-iconic"
                  tooltiptext="&forwardButton.tooltip;"
                  aria-label="&forwardCmd.label;"
                  command="Browser:ForwardOrForwardDuplicate"
                  accesskey="&forwardCmd.accesskey;"
                  onclick="checkForMiddleClick(this, event);"/>
        <menuitem id="context-reload"
                  class="menuitem-iconic"
                  tooltip="dynamic-shortcut-tooltip"
                  aria-label="&reloadCmd.label;"
                  oncommand="gContextMenu.reload(event);"
                  accesskey="&reloadCmd.accesskey;"
                  onclick="checkForMiddleClick(this, event);"/>
        <menuitem id="context-stop"
                  class="menuitem-iconic"
                  tooltip="dynamic-shortcut-tooltip"
                  aria-label="&stopCmd.label;"
                  accesskey="&stopCmd.accesskey;"
                  command="Browser:Stop"/>
        <menuitem id="context-bookmarkpage"
                  class="menuitem-iconic"
                  observes="bookmarkThisPageBroadcaster"
                  aria-label="&bookmarkPageCmd2.label;"
                  accesskey="&bookmarkPageCmd2.accesskey;"
                  oncommand="gContextMenu.bookmarkThisPage();"/>
      </menugroup>
      <menuseparator id="context-sep-navigation"/>
      <menuseparator id="page-menu-separator"/>
      <menuitem id="spell-no-suggestions"
                disabled="true"
                label="&spellNoSuggestions.label;"/>
      <menuitem id="spell-add-to-dictionary"
                label="&spellAddToDictionary.label;"
                accesskey="&spellAddToDictionary.accesskey;"
                oncommand="InlineSpellCheckerUI.addToDictionary();"/>
      <menuitem id="spell-undo-add-to-dictionary"
                label="&spellUndoAddToDictionary.label;"
                accesskey="&spellUndoAddToDictionary.accesskey;"
                oncommand="InlineSpellCheckerUI.undoAddToDictionary();" />
      <menuseparator id="spell-suggestions-separator"/>
      <menuitem id="context-openlinkincurrent"
                label="&openLinkCmdInCurrent.label;"
                accesskey="&openLinkCmdInCurrent.accesskey;"
                oncommand="gContextMenu.openLinkInCurrent();"/>
      <menuitem id="context-openlinkincontainertab"
                accesskey="&openLinkCmdInTab.accesskey;"
                oncommand="gContextMenu.openLinkInTab(event);"/>
      <menuitem id="context-openlinkintab"
                label="&openLinkCmdInTab.label;"
                accesskey="&openLinkCmdInTab.accesskey;"
                data-usercontextid="0"
                oncommand="gContextMenu.openLinkInTab(event);"/>

      <menu id="context-openlinkinusercontext-menu"
            label="&openLinkCmdInContainerTab.label;"
            accesskey="&openLinkCmdInContainerTab.accesskey;"
            hidden="true">
        <menupopup oncommand="gContextMenu.openLinkInTab(event);"
                   onpopupshowing="return gContextMenu.createContainerMenu(event);" />
      </menu>

      <menuitem id="context-openlink"
                label="&openLinkCmd.label;"
                accesskey="&openLinkCmd.accesskey;"
                oncommand="gContextMenu.openLink();"/>
      <menuitem id="context-openlinkprivate"
                label="&openLinkInPrivateWindowCmd.label;"
                accesskey="&openLinkInPrivateWindowCmd.accesskey;"
                oncommand="gContextMenu.openLinkInPrivateWindow();"/>
      <menuseparator id="context-sep-open"/>
      <menuitem id="context-bookmarklink"
                label="&bookmarkThisLinkCmd.label;"
                accesskey="&bookmarkThisLinkCmd.accesskey;"
                oncommand="gContextMenu.bookmarkLink();"/>
      <menuitem id="context-savelink"
                label="&saveLinkCmd.label;"
                accesskey="&saveLinkCmd.accesskey;"
                oncommand="gContextMenu.saveLink();"/>
      <menuitem id="context-copyemail"
                label="&copyEmailCmd.label;"
                accesskey="&copyEmailCmd.accesskey;"
                oncommand="gContextMenu.copyEmail();"/>
      <menuitem id="context-copylink"
                label="&copyLinkCmd.label;"
                accesskey="&copyLinkCmd.accesskey;"
                oncommand="gContextMenu.copyLink();"/>
      <menuseparator id="context-sep-copylink"/>
      <menuitem id="context-media-play"
                label="&mediaPlay.label;"
                accesskey="&mediaPlay.accesskey;"
                oncommand="gContextMenu.mediaCommand('play');"/>
      <menuitem id="context-media-pause"
                label="&mediaPause.label;"
                accesskey="&mediaPause.accesskey;"
                oncommand="gContextMenu.mediaCommand('pause');"/>
      <menuitem id="context-media-mute"
                label="&mediaMute.label;"
                accesskey="&mediaMute.accesskey;"
                oncommand="gContextMenu.mediaCommand('mute');"/>
      <menuitem id="context-media-unmute"
                label="&mediaUnmute.label;"
                accesskey="&mediaUnmute.accesskey;"
                oncommand="gContextMenu.mediaCommand('unmute');"/>
      <menu id="context-media-playbackrate" label="&mediaPlaybackRate2.label;" accesskey="&mediaPlaybackRate2.accesskey;">
        <menupopup>
          <menuitem id="context-media-playbackrate-050x"
                    label="&mediaPlaybackRate050x2.label;"
                    accesskey="&mediaPlaybackRate050x2.accesskey;"
                    type="radio"
                    name="playbackrate"
                    oncommand="gContextMenu.mediaCommand('playbackRate', 0.5);"/>
          <menuitem id="context-media-playbackrate-100x"
                    label="&mediaPlaybackRate100x2.label;"
                    accesskey="&mediaPlaybackRate100x2.accesskey;"
                    type="radio"
                    name="playbackrate"
                    checked="true"
                    oncommand="gContextMenu.mediaCommand('playbackRate', 1.0);"/>
          <menuitem id="context-media-playbackrate-125x"
                    label="&mediaPlaybackRate125x2.label;"
                    accesskey="&mediaPlaybackRate125x2.accesskey;"
                    type="radio"
                    name="playbackrate"
                    oncommand="gContextMenu.mediaCommand('playbackRate', 1.25);"/>
          <menuitem id="context-media-playbackrate-150x"
                    label="&mediaPlaybackRate150x2.label;"
                    accesskey="&mediaPlaybackRate150x2.accesskey;"
                    type="radio"
                    name="playbackrate"
                    oncommand="gContextMenu.mediaCommand('playbackRate', 1.5);"/>
          <menuitem id="context-media-playbackrate-200x"
                    label="&mediaPlaybackRate200x2.label;"
                    accesskey="&mediaPlaybackRate200x2.accesskey;"
                    type="radio"
                    name="playbackrate"
                    oncommand="gContextMenu.mediaCommand('playbackRate', 2.0);"/>
        </menupopup>
      </menu>
      <menuitem id="context-media-loop"
                label="&mediaLoop.label;"
                accesskey="&mediaLoop.accesskey;"
                type="checkbox"
                oncommand="gContextMenu.mediaCommand('loop');"/>
      <menuitem id="context-media-showcontrols"
                label="&mediaShowControls.label;"
                accesskey="&mediaShowControls.accesskey;"
                oncommand="gContextMenu.mediaCommand('showcontrols');"/>
      <menuitem id="context-media-hidecontrols"
                label="&mediaHideControls.label;"
                accesskey="&mediaHideControls.accesskey;"
                oncommand="gContextMenu.mediaCommand('hidecontrols');"/>
      <menuitem id="context-video-fullscreen"
                accesskey="&videoFullScreen.accesskey;"
                label="&videoFullScreen.label;"
                oncommand="gContextMenu.mediaCommand('fullscreen');"/>
      <menuitem id="context-leave-dom-fullscreen"
                accesskey="&leaveDOMFullScreen.accesskey;"
                label="&leaveDOMFullScreen.label;"
                oncommand="gContextMenu.leaveDOMFullScreen();"/>
      <menuseparator id="context-media-sep-commands"/>
      <menuitem id="context-reloadimage"
                label="&reloadImageCmd.label;"
                accesskey="&reloadImageCmd.accesskey;"
                oncommand="gContextMenu.reloadImage();"/>
      <menuitem id="context-viewimage"
                label="&viewImageCmd.label;"
                accesskey="&viewImageCmd.accesskey;"
                oncommand="gContextMenu.viewMedia(event);"
                onclick="checkForMiddleClick(this, event);"/>
      <menuitem id="context-viewvideo"
                label="&viewVideoCmd.label;"
                accesskey="&viewVideoCmd.accesskey;"
                oncommand="gContextMenu.viewMedia(event);"
                onclick="checkForMiddleClick(this, event);"/>
      <menuitem id="context-copyimage-contents"
                label="&copyImageContentsCmd.label;"
                accesskey="&copyImageContentsCmd.accesskey;"
                oncommand="goDoCommand('cmd_copyImage');"/>
      <menuitem id="context-copyimage"
                label="&copyImageCmd.label;"
                accesskey="&copyImageCmd.accesskey;"
                oncommand="gContextMenu.copyMediaLocation();"/>
      <menuitem id="context-copyvideourl"
                label="&copyVideoURLCmd.label;"
                accesskey="&copyVideoURLCmd.accesskey;"
                oncommand="gContextMenu.copyMediaLocation();"/>
      <menuitem id="context-copyaudiourl"
                label="&copyAudioURLCmd.label;"
                accesskey="&copyAudioURLCmd.accesskey;"
                oncommand="gContextMenu.copyMediaLocation();"/>
      <menuseparator id="context-sep-copyimage"/>
      <menuitem id="context-saveimage"
                label="&saveImageCmd.label;"
                accesskey="&saveImageCmd.accesskey;"
                oncommand="gContextMenu.saveMedia();"/>
      <menuitem id="context-sendimage"
                label="&emailImageCmd.label;"
                accesskey="&emailImageCmd.accesskey;"
                oncommand="gContextMenu.sendMedia();"/>
      <menuitem id="context-setDesktopBackground"
                label="&setDesktopBackgroundCmd.label;"
                accesskey="&setDesktopBackgroundCmd.accesskey;"
                oncommand="gContextMenu.setDesktopBackground();"/>
      <menuitem id="context-viewimageinfo"
                label="&viewImageInfoCmd.label;"
                accesskey="&viewImageInfoCmd.accesskey;"
                oncommand="gContextMenu.viewImageInfo();"/>
      <menuitem id="context-viewimagedesc"
                label="&viewImageDescCmd.label;"
                accesskey="&viewImageDescCmd.accesskey;"
                oncommand="gContextMenu.viewImageDesc(event);"
                onclick="checkForMiddleClick(this, event);"/>
      <menuitem id="context-savevideo"
                label="&saveVideoCmd.label;"
                accesskey="&saveVideoCmd.accesskey;"
                oncommand="gContextMenu.saveMedia();"/>
      <menuitem id="context-saveaudio"
                label="&saveAudioCmd.label;"
                accesskey="&saveAudioCmd.accesskey;"
                oncommand="gContextMenu.saveMedia();"/>
      <menuitem id="context-video-saveimage"
                accesskey="&videoSaveImage.accesskey;"
                label="&videoSaveImage.label;"
                oncommand="gContextMenu.saveVideoFrameAsImage();"/>
      <menuitem id="context-sendvideo"
                label="&emailVideoCmd.label;"
                accesskey="&emailVideoCmd.accesskey;"
                oncommand="gContextMenu.sendMedia();"/>
      <menuitem id="context-sendaudio"
                label="&emailAudioCmd.label;"
                accesskey="&emailAudioCmd.accesskey;"
                oncommand="gContextMenu.sendMedia();"/>
      <menuitem id="context-ctp-play"
                label="&playPluginCmd.label;"
                accesskey="&playPluginCmd.accesskey;"
                oncommand="gContextMenu.playPlugin();"/>
      <menuitem id="context-ctp-hide"
                label="&hidePluginCmd.label;"
                accesskey="&hidePluginCmd.accesskey;"
                oncommand="gContextMenu.hidePlugin();"/>
      <menuseparator id="context-sep-ctp"/>
      <menuitem id="context-savepage"
                label="&savePageCmd.label;"
                accesskey="&savePageCmd.accesskey2;"
                oncommand="gContextMenu.savePageAs();"/>
      <menuseparator id="context-sep-sendpagetodevice" class="sync-ui-item"
                     hidden="true"/>
      <menu id="context-sendpagetodevice"
            class="sync-ui-item"
            label="&sendPageToDevice.label;"
            accesskey="&sendPageToDevice.accesskey;"
            hidden="true">
        <menupopup id="context-sendpagetodevice-popup"
                   onpopupshowing="(() => { let browser = gBrowser || getPanelBrowser(); gSync.populateSendTabToDevicesMenu(event.target, browser.currentURI.spec, browser.contentTitle); })()"/>
      </menu>
      <menuseparator id="context-sep-viewbgimage"/>
      <menuitem id="context-viewbgimage"
                label="&viewBGImageCmd.label;"
                accesskey="&viewBGImageCmd.accesskey;"
                oncommand="gContextMenu.viewBGImage(event);"
                onclick="checkForMiddleClick(this, event);"/>
      <menuitem id="context-undo"
                label="&undoCmd.label;"
                accesskey="&undoCmd.accesskey;"
                command="cmd_undo"/>
      <menuseparator id="context-sep-undo"/>
      <menuitem id="context-cut"
                label="&cutCmd.label;"
                accesskey="&cutCmd.accesskey;"
                command="cmd_cut"/>
      <menuitem id="context-copy"
                label="&copyCmd.label;"
                accesskey="&copyCmd.accesskey;"
                command="cmd_copy"/>
      <menuitem id="context-paste"
                label="&pasteCmd.label;"
                accesskey="&pasteCmd.accesskey;"
                command="cmd_paste"/>
      <menuitem id="context-delete"
                label="&deleteCmd.label;"
                accesskey="&deleteCmd.accesskey;"
                command="cmd_delete"/>
      <menuseparator id="context-sep-paste"/>
      <menuitem id="context-selectall"
                label="&selectAllCmd.label;"
                accesskey="&selectAllCmd.accesskey;"
                command="cmd_selectAll"/>
      <menuseparator id="context-sep-selectall"/>
      <menuitem id="context-keywordfield"
                label="&keywordfield.label;"
                accesskey="&keywordfield.accesskey;"
                oncommand="AddKeywordForSearchField();"/>
      <menuitem id="context-searchselect"
                oncommand="BrowserSearch.loadSearchFromContext(this.searchTerms);"/>
      <menuseparator id="context-sep-sendlinktodevice" class="sync-ui-item"
                     hidden="true"/>
      <menu id="context-sendlinktodevice"
            class="sync-ui-item"
            label="&sendLinkToDevice.label;"
            accesskey="&sendLinkToDevice.accesskey;"
            hidden="true">
        <menupopup id="context-sendlinktodevice-popup"
                   onpopupshowing="gSync.populateSendTabToDevicesMenu(event.target, gContextMenu.linkURL, gContextMenu.linkTextStr);"/>
      </menu>
      <menuseparator id="frame-sep"/>
      <menu id="frame" label="&thisFrameMenu.label;" accesskey="&thisFrameMenu.accesskey;">
        <menupopup>
          <menuitem id="context-showonlythisframe"
                    label="&showOnlyThisFrameCmd.label;"
                    accesskey="&showOnlyThisFrameCmd.accesskey;"
                    oncommand="gContextMenu.showOnlyThisFrame();"/>
          <menuitem id="context-openframeintab"
                    label="&openFrameCmdInTab.label;"
                    accesskey="&openFrameCmdInTab.accesskey;"
                    oncommand="gContextMenu.openFrameInTab();"/>
          <menuitem id="context-openframe"
                    label="&openFrameCmd.label;"
                    accesskey="&openFrameCmd.accesskey;"
                    oncommand="gContextMenu.openFrame();"/>
          <menuseparator id="open-frame-sep"/>
          <menuitem id="context-reloadframe"
                    label="&reloadFrameCmd.label;"
                    accesskey="&reloadFrameCmd.accesskey;"
                    oncommand="gContextMenu.reloadFrame(event);"/>
          <menuseparator/>
          <menuitem id="context-bookmarkframe"
                    label="&bookmarkThisFrameCmd.label;"
                    accesskey="&bookmarkThisFrameCmd.accesskey;"
                    oncommand="gContextMenu.addBookmarkForFrame();"/>
          <menuitem id="context-saveframe"
                    label="&saveFrameCmd.label;"
                    accesskey="&saveFrameCmd.accesskey;"
                    oncommand="gContextMenu.saveFrame();"/>
          <menuseparator/>
          <menuitem id="context-printframe"
                    label="&printFrameCmd.label;"
                    accesskey="&printFrameCmd.accesskey;"
                    oncommand="gContextMenu.printFrame();"/>
          <menuseparator/>
          <menuitem id="context-viewframesource"
                    label="&viewFrameSourceCmd.label;"
                    accesskey="&viewFrameSourceCmd.accesskey;"
                    oncommand="gContextMenu.viewFrameSource();"
                    observes="isFrameImage"/>
          <menuitem id="context-viewframeinfo"
                    label="&viewFrameInfoCmd.label;"
                    accesskey="&viewFrameInfoCmd.accesskey;"
                    oncommand="gContextMenu.viewFrameInfo();"/>
        </menupopup>
      </menu>
      <menuitem id="context-viewpartialsource-selection"
                label="&viewPartialSourceForSelectionCmd.label;"
                accesskey="&viewPartialSourceCmd.accesskey;"
                oncommand="gContextMenu.viewPartialSource('selection');"
                observes="isImage"/>
      <menuitem id="context-viewpartialsource-mathml"
                label="&viewPartialSourceForMathMLCmd.label;"
                accesskey="&viewPartialSourceCmd.accesskey;"
                oncommand="gContextMenu.viewPartialSource('mathml');"
                observes="isImage"/>
      <menuseparator id="context-sep-viewsource"/>
      <menuitem id="context-viewsource"
                label="&viewPageSourceCmd.label;"
                accesskey="&viewPageSourceCmd.accesskey;"
                oncommand="BrowserViewSource(gContextMenu.browser);"
                observes="canViewSource"/>
      <menuitem id="context-viewinfo"
                label="&viewPageInfoCmd.label;"
                accesskey="&viewPageInfoCmd.accesskey;"
                oncommand="gContextMenu.viewInfo();"/>
      <menuseparator id="spell-separator"/>
      <menuitem id="spell-check-enabled"
                label="&spellCheckToggle.label;"
                type="checkbox"
                accesskey="&spellCheckToggle.accesskey;"
                oncommand="InlineSpellCheckerUI.toggleEnabled(window);"/>
      <menuitem id="spell-add-dictionaries-main"
                label="&spellAddDictionaries.label;"
                accesskey="&spellAddDictionaries.accesskey;"
                oncommand="gContextMenu.addDictionaries();"/>
      <menu id="spell-dictionaries"
            label="&spellDictionaries.label;"
            accesskey="&spellDictionaries.accesskey;">
          <menupopup id="spell-dictionaries-menu">
              <menuseparator id="spell-language-separator"/>
              <menuitem id="spell-add-dictionaries"
                        label="&spellAddDictionaries.label;"
                        accesskey="&spellAddDictionaries.accesskey;"
                        oncommand="gContextMenu.addDictionaries();"/>
          </menupopup>
      </menu>
      <menuseparator hidden="true" id="context-sep-bidi"/>
      <menuitem hidden="true" id="context-bidi-text-direction-toggle"
                label="&bidiSwitchTextDirectionItem.label;"
                accesskey="&bidiSwitchTextDirectionItem.accesskey;"
                command="cmd_switchTextDirection"/>
      <menuitem hidden="true" id="context-bidi-page-direction-toggle"
                label="&bidiSwitchPageDirectionItem.label;"
                accesskey="&bidiSwitchPageDirectionItem.accesskey;"
                oncommand="gContextMenu.switchPageDirection();"/>
      <menuseparator id="fill-login-separator" hidden="true"/>
      <menu id="fill-login"
            label="&fillLoginMenu.label;"
            label-login="&fillLoginMenu.label;"
            label-password="&fillPasswordMenu.label;"
            label-username="&fillUsernameMenu.label;"
            accesskey="&fillLoginMenu.accesskey;"
            accesskey-login="&fillLoginMenu.accesskey;"
            accesskey-password="&fillPasswordMenu.accesskey;"
            accesskey-username="&fillUsernameMenu.accesskey;"
            hidden="true">
        <menupopup id="fill-login-popup">
          <menuitem id="fill-login-no-logins"
                    label="&noLoginSuggestions.label;"
                    disabled="true"
                    hidden="true"/>
          <menuseparator id="saved-logins-separator"/>
          <menuitem id="fill-login-saved-passwords"
                    label="&viewSavedLogins.label;"
                    oncommand="gContextMenu.openPasswordManager();"/>
        </menupopup>
      </menu>
      <menuseparator id="inspect-separator" hidden="true"/>
      <menuitem id="context-inspect"
                hidden="true"
                label="&inspectContextMenu.label;"
                accesskey="&inspectContextMenu.accesskey;"
                oncommand="gContextMenu.inspectNode();"/>
      <menuseparator id="context-media-eme-separator" hidden="true"/>
      <menuitem id="context-media-eme-learnmore"
                class="menuitem-iconic"
                hidden="true"
                label="&emeLearnMoreContextMenu.label;"
                accesskey="&emeLearnMoreContextMenu.accesskey;"
                oncommand="gContextMenu.drmLearnMore(event);"
                onclick="checkForMiddleClick(this, event);"/>
    </menupopup>
  </popupset>

<script type="application/javascript" src="chrome://global/content/editMenuOverlay.js"/>
<commandset id="editMenuCommands">
  <commandset id="editMenuCommandSetAll" commandupdater="true" events="focus,select"
              oncommandupdate="goUpdateGlobalEditMenuItems()"/>
  <commandset id="editMenuCommandSetUndo" commandupdater="true" events="undo"
              oncommandupdate="goUpdateUndoEditMenuItems()"/>
  <commandset id="editMenuCommandSetPaste" commandupdater="true" events="clipboard"
              oncommandupdate="goUpdatePasteMenuItems()"/>
  <command id="cmd_undo" oncommand="goDoCommand('cmd_undo')"/>
  <command id="cmd_redo" oncommand="goDoCommand('cmd_redo')"/>
  <command id="cmd_cut" oncommand="goDoCommand('cmd_cut')"/>
  <command id="cmd_copy" oncommand="goDoCommand('cmd_copy')"/>
  <command id="cmd_paste" oncommand="goDoCommand('cmd_paste')"/>
  <command id="cmd_delete" oncommand="goDoCommand('cmd_delete')"/>
  <command id="cmd_selectAll" oncommand="goDoCommand('cmd_selectAll')"/>
  <command id="cmd_switchTextDirection" oncommand="goDoCommand('cmd_switchTextDirection');"/>
</commandset>
  <browser id="web-panels-browser" persist="cachedurl" type="content" flex="1"
           context="contentAreaContextMenu" tooltip="aHTMLTooltip"
           onclick="window.parent.contentAreaClick(event, true);"/>
</page>
PK
!<��zz/chrome/browser/content/browser/webext-panels.js/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Via webext-panels.xul
/* import-globals-from browser.js */
/* import-globals-from nsContextMenu.js */

ChromeUtils.defineModuleGetter(this, "ExtensionParent",
                               "resource://gre/modules/ExtensionParent.jsm");

ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/ExtensionUtils.jsm");

var {
  promiseEvent,
} = ExtensionUtils;

const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";

function getBrowser(sidebar) {
  let browser = document.getElementById("webext-panels-browser");
  if (browser) {
    return Promise.resolve(browser);
  }

  let stack = document.createElementNS(XUL_NS, "stack");
  stack.setAttribute("flex", "1");

  browser = document.createElementNS(XUL_NS, "browser");
  browser.setAttribute("id", "webext-panels-browser");
  browser.setAttribute("type", "content");
  browser.setAttribute("flex", "1");
  browser.setAttribute("disableglobalhistory", "true");
  browser.setAttribute("webextension-view-type", "sidebar");
  browser.setAttribute("context", "contentAreaContextMenu");
  browser.setAttribute("tooltip", "aHTMLTooltip");
  browser.setAttribute("autocompletepopup", "PopupAutoComplete");
  browser.setAttribute("selectmenulist", "ContentSelectDropdown");
  browser.setAttribute("onclick", "window.parent.contentAreaClick(event, true);");

  let readyPromise;
  if (sidebar.remote) {
    browser.setAttribute("remote", "true");
    browser.setAttribute("remoteType",
                         E10SUtils.getRemoteTypeForURI(sidebar.uri, true,
                                                       E10SUtils.EXTENSION_REMOTE_TYPE));
    readyPromise = promiseEvent(browser, "XULFrameLoaderCreated");

    window.messageManager.addMessageListener("contextmenu", openContextMenu);
    window.addEventListener("unload", () => {
      window.messageManager.removeMessageListener("contextmenu", openContextMenu);
    }, {once: true});
  } else {
    readyPromise = Promise.resolve();
  }

  stack.appendChild(browser);
  document.documentElement.appendChild(stack);

  return readyPromise.then(() => {
    browser.messageManager.loadFrameScript("chrome://browser/content/content.js", false);
    ExtensionParent.apiManager.emit("extension-browser-inserted", browser);

    if (sidebar.browserStyle) {
      browser.messageManager.loadFrameScript(
        "chrome://extensions/content/ext-browser-content.js", false);

      browser.messageManager.sendAsyncMessage("Extension:InitBrowser", {
        stylesheets: ExtensionParent.extensionStylesheets,
      });
    }
    return browser;
  });
}

// Stub tabbrowser implementation for use by the tab-modal alert code.
var gBrowser = {
  getTabForBrowser(browser) {
    return null;
  },

  getTabModalPromptBox(browser) {
    if (!browser.tabModalPromptBox) {
      browser.tabModalPromptBox = new TabModalPromptBox(browser);
    }
    return browser.tabModalPromptBox;
  },
};

function loadPanel(extensionId, extensionUrl, browserStyle) {
  let browserEl = document.getElementById("webext-panels-browser");
  if (browserEl) {
    if (browserEl.currentURI.spec === extensionUrl) {
      return;
    }
    // Forces runtime disconnect.  Remove the stack (parent).
    browserEl.parentNode.remove();
  }

  let policy = WebExtensionPolicy.getByID(extensionId);
  let sidebar = {
    uri: extensionUrl,
    remote: policy.extension.remote,
    browserStyle,
  };
  getBrowser(sidebar).then(browser => {
    let uri = Services.io.newURI(policy.getURL());
    let triggeringPrincipal = Services.scriptSecurityManager.createCodebasePrincipal(uri, {});
    browser.loadURIWithFlags(extensionUrl, {triggeringPrincipal});
  });
}
PK
!<��ii0chrome/browser/content/browser/webext-panels.xul<?xml version="1.0"?>


<?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/usercontext/usercontext.css" type="text/css"?>
<?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?>

<!DOCTYPE page [
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
%browserDTD;
<!ENTITY % textcontextDTD SYSTEM "chrome://global/locale/textcontext.dtd">
%textcontextDTD;
]>

<page id="webextpanels-window"
        xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
  <script type="application/javascript" src="chrome://global/content/contentAreaUtils.js"/>
  <script type="application/javascript" src="chrome://browser/content/browser.js"/>
  <script type="application/javascript" src="chrome://browser/content/browser-places.js"/>
  <script type="application/javascript" src="chrome://browser/content/webext-panels.js"/>

  <broadcasterset id="mainBroadcasterSet">
    <broadcaster id="isFrameImage"/>
  </broadcasterset>

  <commandset id="mainCommandset">
    <command id="Browser:Back"
             oncommand="getPanelBrowser().webNavigation.goBack();"
             disabled="true"/>
    <command id="Browser:Forward"
             oncommand="getPanelBrowser().webNavigation.goForward();"
             disabled="true"/>
    <command id="Browser:Stop" oncommand="PanelBrowserStop();"/>
    <command id="Browser:Reload" oncommand="PanelBrowserReload();"/>
  </commandset>

  <popupset id="mainPopupSet">
    <tooltip id="aHTMLTooltip" page="true"/>

    <panel type="autocomplete-richlistbox"
           id="PopupAutoComplete"
           noautofocus="true"
           hidden="true"
           overflowpadding="4"
           norolluponanchor="true" />

    <menupopup id="contentAreaContextMenu" pagemenu="start"
               onpopupshowing="if (event.target != this)
                                 return true;
                               gContextMenu = new nsContextMenu(this, event.shiftKey);
                               if (gContextMenu.shouldDisplay)
                                 document.popupNode = this.triggerNode;
                               return gContextMenu.shouldDisplay;"
               onpopuphiding="if (event.target != this)
                                return;
                              gContextMenu.hiding();
                              gContextMenu = null;">


      <menugroup id="context-navigation">
        <menuitem id="context-back"
                  class="menuitem-iconic"
                  tooltiptext="&backButton.tooltip;"
                  aria-label="&backCmd.label;"
                  command="Browser:BackOrBackDuplicate"
                  accesskey="&backCmd.accesskey;"
                  onclick="checkForMiddleClick(this, event);"/>
        <menuitem id="context-forward"
                  class="menuitem-iconic"
                  tooltiptext="&forwardButton.tooltip;"
                  aria-label="&forwardCmd.label;"
                  command="Browser:ForwardOrForwardDuplicate"
                  accesskey="&forwardCmd.accesskey;"
                  onclick="checkForMiddleClick(this, event);"/>
        <menuitem id="context-reload"
                  class="menuitem-iconic"
                  tooltip="dynamic-shortcut-tooltip"
                  aria-label="&reloadCmd.label;"
                  oncommand="gContextMenu.reload(event);"
                  accesskey="&reloadCmd.accesskey;"
                  onclick="checkForMiddleClick(this, event);"/>
        <menuitem id="context-stop"
                  class="menuitem-iconic"
                  tooltip="dynamic-shortcut-tooltip"
                  aria-label="&stopCmd.label;"
                  accesskey="&stopCmd.accesskey;"
                  command="Browser:Stop"/>
        <menuitem id="context-bookmarkpage"
                  class="menuitem-iconic"
                  observes="bookmarkThisPageBroadcaster"
                  aria-label="&bookmarkPageCmd2.label;"
                  accesskey="&bookmarkPageCmd2.accesskey;"
                  oncommand="gContextMenu.bookmarkThisPage();"/>
      </menugroup>
      <menuseparator id="context-sep-navigation"/>
      <menuseparator id="page-menu-separator"/>
      <menuitem id="spell-no-suggestions"
                disabled="true"
                label="&spellNoSuggestions.label;"/>
      <menuitem id="spell-add-to-dictionary"
                label="&spellAddToDictionary.label;"
                accesskey="&spellAddToDictionary.accesskey;"
                oncommand="InlineSpellCheckerUI.addToDictionary();"/>
      <menuitem id="spell-undo-add-to-dictionary"
                label="&spellUndoAddToDictionary.label;"
                accesskey="&spellUndoAddToDictionary.accesskey;"
                oncommand="InlineSpellCheckerUI.undoAddToDictionary();" />
      <menuseparator id="spell-suggestions-separator"/>
      <menuitem id="context-openlinkincurrent"
                label="&openLinkCmdInCurrent.label;"
                accesskey="&openLinkCmdInCurrent.accesskey;"
                oncommand="gContextMenu.openLinkInCurrent();"/>
      <menuitem id="context-openlinkincontainertab"
                accesskey="&openLinkCmdInTab.accesskey;"
                oncommand="gContextMenu.openLinkInTab(event);"/>
      <menuitem id="context-openlinkintab"
                label="&openLinkCmdInTab.label;"
                accesskey="&openLinkCmdInTab.accesskey;"
                data-usercontextid="0"
                oncommand="gContextMenu.openLinkInTab(event);"/>

      <menu id="context-openlinkinusercontext-menu"
            label="&openLinkCmdInContainerTab.label;"
            accesskey="&openLinkCmdInContainerTab.accesskey;"
            hidden="true">
        <menupopup oncommand="gContextMenu.openLinkInTab(event);"
                   onpopupshowing="return gContextMenu.createContainerMenu(event);" />
      </menu>

      <menuitem id="context-openlink"
                label="&openLinkCmd.label;"
                accesskey="&openLinkCmd.accesskey;"
                oncommand="gContextMenu.openLink();"/>
      <menuitem id="context-openlinkprivate"
                label="&openLinkInPrivateWindowCmd.label;"
                accesskey="&openLinkInPrivateWindowCmd.accesskey;"
                oncommand="gContextMenu.openLinkInPrivateWindow();"/>
      <menuseparator id="context-sep-open"/>
      <menuitem id="context-bookmarklink"
                label="&bookmarkThisLinkCmd.label;"
                accesskey="&bookmarkThisLinkCmd.accesskey;"
                oncommand="gContextMenu.bookmarkLink();"/>
      <menuitem id="context-savelink"
                label="&saveLinkCmd.label;"
                accesskey="&saveLinkCmd.accesskey;"
                oncommand="gContextMenu.saveLink();"/>
      <menuitem id="context-copyemail"
                label="&copyEmailCmd.label;"
                accesskey="&copyEmailCmd.accesskey;"
                oncommand="gContextMenu.copyEmail();"/>
      <menuitem id="context-copylink"
                label="&copyLinkCmd.label;"
                accesskey="&copyLinkCmd.accesskey;"
                oncommand="gContextMenu.copyLink();"/>
      <menuseparator id="context-sep-copylink"/>
      <menuitem id="context-media-play"
                label="&mediaPlay.label;"
                accesskey="&mediaPlay.accesskey;"
                oncommand="gContextMenu.mediaCommand('play');"/>
      <menuitem id="context-media-pause"
                label="&mediaPause.label;"
                accesskey="&mediaPause.accesskey;"
                oncommand="gContextMenu.mediaCommand('pause');"/>
      <menuitem id="context-media-mute"
                label="&mediaMute.label;"
                accesskey="&mediaMute.accesskey;"
                oncommand="gContextMenu.mediaCommand('mute');"/>
      <menuitem id="context-media-unmute"
                label="&mediaUnmute.label;"
                accesskey="&mediaUnmute.accesskey;"
                oncommand="gContextMenu.mediaCommand('unmute');"/>
      <menu id="context-media-playbackrate" label="&mediaPlaybackRate2.label;" accesskey="&mediaPlaybackRate2.accesskey;">
        <menupopup>
          <menuitem id="context-media-playbackrate-050x"
                    label="&mediaPlaybackRate050x2.label;"
                    accesskey="&mediaPlaybackRate050x2.accesskey;"
                    type="radio"
                    name="playbackrate"
                    oncommand="gContextMenu.mediaCommand('playbackRate', 0.5);"/>
          <menuitem id="context-media-playbackrate-100x"
                    label="&mediaPlaybackRate100x2.label;"
                    accesskey="&mediaPlaybackRate100x2.accesskey;"
                    type="radio"
                    name="playbackrate"
                    checked="true"
                    oncommand="gContextMenu.mediaCommand('playbackRate', 1.0);"/>
          <menuitem id="context-media-playbackrate-125x"
                    label="&mediaPlaybackRate125x2.label;"
                    accesskey="&mediaPlaybackRate125x2.accesskey;"
                    type="radio"
                    name="playbackrate"
                    oncommand="gContextMenu.mediaCommand('playbackRate', 1.25);"/>
          <menuitem id="context-media-playbackrate-150x"
                    label="&mediaPlaybackRate150x2.label;"
                    accesskey="&mediaPlaybackRate150x2.accesskey;"
                    type="radio"
                    name="playbackrate"
                    oncommand="gContextMenu.mediaCommand('playbackRate', 1.5);"/>
          <menuitem id="context-media-playbackrate-200x"
                    label="&mediaPlaybackRate200x2.label;"
                    accesskey="&mediaPlaybackRate200x2.accesskey;"
                    type="radio"
                    name="playbackrate"
                    oncommand="gContextMenu.mediaCommand('playbackRate', 2.0);"/>
        </menupopup>
      </menu>
      <menuitem id="context-media-loop"
                label="&mediaLoop.label;"
                accesskey="&mediaLoop.accesskey;"
                type="checkbox"
                oncommand="gContextMenu.mediaCommand('loop');"/>
      <menuitem id="context-media-showcontrols"
                label="&mediaShowControls.label;"
                accesskey="&mediaShowControls.accesskey;"
                oncommand="gContextMenu.mediaCommand('showcontrols');"/>
      <menuitem id="context-media-hidecontrols"
                label="&mediaHideControls.label;"
                accesskey="&mediaHideControls.accesskey;"
                oncommand="gContextMenu.mediaCommand('hidecontrols');"/>
      <menuitem id="context-video-fullscreen"
                accesskey="&videoFullScreen.accesskey;"
                label="&videoFullScreen.label;"
                oncommand="gContextMenu.mediaCommand('fullscreen');"/>
      <menuitem id="context-leave-dom-fullscreen"
                accesskey="&leaveDOMFullScreen.accesskey;"
                label="&leaveDOMFullScreen.label;"
                oncommand="gContextMenu.leaveDOMFullScreen();"/>
      <menuseparator id="context-media-sep-commands"/>
      <menuitem id="context-reloadimage"
                label="&reloadImageCmd.label;"
                accesskey="&reloadImageCmd.accesskey;"
                oncommand="gContextMenu.reloadImage();"/>
      <menuitem id="context-viewimage"
                label="&viewImageCmd.label;"
                accesskey="&viewImageCmd.accesskey;"
                oncommand="gContextMenu.viewMedia(event);"
                onclick="checkForMiddleClick(this, event);"/>
      <menuitem id="context-viewvideo"
                label="&viewVideoCmd.label;"
                accesskey="&viewVideoCmd.accesskey;"
                oncommand="gContextMenu.viewMedia(event);"
                onclick="checkForMiddleClick(this, event);"/>
      <menuitem id="context-copyimage-contents"
                label="&copyImageContentsCmd.label;"
                accesskey="&copyImageContentsCmd.accesskey;"
                oncommand="goDoCommand('cmd_copyImage');"/>
      <menuitem id="context-copyimage"
                label="&copyImageCmd.label;"
                accesskey="&copyImageCmd.accesskey;"
                oncommand="gContextMenu.copyMediaLocation();"/>
      <menuitem id="context-copyvideourl"
                label="&copyVideoURLCmd.label;"
                accesskey="&copyVideoURLCmd.accesskey;"
                oncommand="gContextMenu.copyMediaLocation();"/>
      <menuitem id="context-copyaudiourl"
                label="&copyAudioURLCmd.label;"
                accesskey="&copyAudioURLCmd.accesskey;"
                oncommand="gContextMenu.copyMediaLocation();"/>
      <menuseparator id="context-sep-copyimage"/>
      <menuitem id="context-saveimage"
                label="&saveImageCmd.label;"
                accesskey="&saveImageCmd.accesskey;"
                oncommand="gContextMenu.saveMedia();"/>
      <menuitem id="context-sendimage"
                label="&emailImageCmd.label;"
                accesskey="&emailImageCmd.accesskey;"
                oncommand="gContextMenu.sendMedia();"/>
      <menuitem id="context-setDesktopBackground"
                label="&setDesktopBackgroundCmd.label;"
                accesskey="&setDesktopBackgroundCmd.accesskey;"
                oncommand="gContextMenu.setDesktopBackground();"/>
      <menuitem id="context-viewimageinfo"
                label="&viewImageInfoCmd.label;"
                accesskey="&viewImageInfoCmd.accesskey;"
                oncommand="gContextMenu.viewImageInfo();"/>
      <menuitem id="context-viewimagedesc"
                label="&viewImageDescCmd.label;"
                accesskey="&viewImageDescCmd.accesskey;"
                oncommand="gContextMenu.viewImageDesc(event);"
                onclick="checkForMiddleClick(this, event);"/>
      <menuitem id="context-savevideo"
                label="&saveVideoCmd.label;"
                accesskey="&saveVideoCmd.accesskey;"
                oncommand="gContextMenu.saveMedia();"/>
      <menuitem id="context-saveaudio"
                label="&saveAudioCmd.label;"
                accesskey="&saveAudioCmd.accesskey;"
                oncommand="gContextMenu.saveMedia();"/>
      <menuitem id="context-video-saveimage"
                accesskey="&videoSaveImage.accesskey;"
                label="&videoSaveImage.label;"
                oncommand="gContextMenu.saveVideoFrameAsImage();"/>
      <menuitem id="context-sendvideo"
                label="&emailVideoCmd.label;"
                accesskey="&emailVideoCmd.accesskey;"
                oncommand="gContextMenu.sendMedia();"/>
      <menuitem id="context-sendaudio"
                label="&emailAudioCmd.label;"
                accesskey="&emailAudioCmd.accesskey;"
                oncommand="gContextMenu.sendMedia();"/>
      <menuitem id="context-ctp-play"
                label="&playPluginCmd.label;"
                accesskey="&playPluginCmd.accesskey;"
                oncommand="gContextMenu.playPlugin();"/>
      <menuitem id="context-ctp-hide"
                label="&hidePluginCmd.label;"
                accesskey="&hidePluginCmd.accesskey;"
                oncommand="gContextMenu.hidePlugin();"/>
      <menuseparator id="context-sep-ctp"/>
      <menuitem id="context-savepage"
                label="&savePageCmd.label;"
                accesskey="&savePageCmd.accesskey2;"
                oncommand="gContextMenu.savePageAs();"/>
      <menuseparator id="context-sep-sendpagetodevice" class="sync-ui-item"
                     hidden="true"/>
      <menu id="context-sendpagetodevice"
            class="sync-ui-item"
            label="&sendPageToDevice.label;"
            accesskey="&sendPageToDevice.accesskey;"
            hidden="true">
        <menupopup id="context-sendpagetodevice-popup"
                   onpopupshowing="(() => { let browser = gBrowser || getPanelBrowser(); gSync.populateSendTabToDevicesMenu(event.target, browser.currentURI.spec, browser.contentTitle); })()"/>
      </menu>
      <menuseparator id="context-sep-viewbgimage"/>
      <menuitem id="context-viewbgimage"
                label="&viewBGImageCmd.label;"
                accesskey="&viewBGImageCmd.accesskey;"
                oncommand="gContextMenu.viewBGImage(event);"
                onclick="checkForMiddleClick(this, event);"/>
      <menuitem id="context-undo"
                label="&undoCmd.label;"
                accesskey="&undoCmd.accesskey;"
                command="cmd_undo"/>
      <menuseparator id="context-sep-undo"/>
      <menuitem id="context-cut"
                label="&cutCmd.label;"
                accesskey="&cutCmd.accesskey;"
                command="cmd_cut"/>
      <menuitem id="context-copy"
                label="&copyCmd.label;"
                accesskey="&copyCmd.accesskey;"
                command="cmd_copy"/>
      <menuitem id="context-paste"
                label="&pasteCmd.label;"
                accesskey="&pasteCmd.accesskey;"
                command="cmd_paste"/>
      <menuitem id="context-delete"
                label="&deleteCmd.label;"
                accesskey="&deleteCmd.accesskey;"
                command="cmd_delete"/>
      <menuseparator id="context-sep-paste"/>
      <menuitem id="context-selectall"
                label="&selectAllCmd.label;"
                accesskey="&selectAllCmd.accesskey;"
                command="cmd_selectAll"/>
      <menuseparator id="context-sep-selectall"/>
      <menuitem id="context-keywordfield"
                label="&keywordfield.label;"
                accesskey="&keywordfield.accesskey;"
                oncommand="AddKeywordForSearchField();"/>
      <menuitem id="context-searchselect"
                oncommand="BrowserSearch.loadSearchFromContext(this.searchTerms);"/>
      <menuseparator id="context-sep-sendlinktodevice" class="sync-ui-item"
                     hidden="true"/>
      <menu id="context-sendlinktodevice"
            class="sync-ui-item"
            label="&sendLinkToDevice.label;"
            accesskey="&sendLinkToDevice.accesskey;"
            hidden="true">
        <menupopup id="context-sendlinktodevice-popup"
                   onpopupshowing="gSync.populateSendTabToDevicesMenu(event.target, gContextMenu.linkURL, gContextMenu.linkTextStr);"/>
      </menu>
      <menuseparator id="frame-sep"/>
      <menu id="frame" label="&thisFrameMenu.label;" accesskey="&thisFrameMenu.accesskey;">
        <menupopup>
          <menuitem id="context-showonlythisframe"
                    label="&showOnlyThisFrameCmd.label;"
                    accesskey="&showOnlyThisFrameCmd.accesskey;"
                    oncommand="gContextMenu.showOnlyThisFrame();"/>
          <menuitem id="context-openframeintab"
                    label="&openFrameCmdInTab.label;"
                    accesskey="&openFrameCmdInTab.accesskey;"
                    oncommand="gContextMenu.openFrameInTab();"/>
          <menuitem id="context-openframe"
                    label="&openFrameCmd.label;"
                    accesskey="&openFrameCmd.accesskey;"
                    oncommand="gContextMenu.openFrame();"/>
          <menuseparator id="open-frame-sep"/>
          <menuitem id="context-reloadframe"
                    label="&reloadFrameCmd.label;"
                    accesskey="&reloadFrameCmd.accesskey;"
                    oncommand="gContextMenu.reloadFrame(event);"/>
          <menuseparator/>
          <menuitem id="context-bookmarkframe"
                    label="&bookmarkThisFrameCmd.label;"
                    accesskey="&bookmarkThisFrameCmd.accesskey;"
                    oncommand="gContextMenu.addBookmarkForFrame();"/>
          <menuitem id="context-saveframe"
                    label="&saveFrameCmd.label;"
                    accesskey="&saveFrameCmd.accesskey;"
                    oncommand="gContextMenu.saveFrame();"/>
          <menuseparator/>
          <menuitem id="context-printframe"
                    label="&printFrameCmd.label;"
                    accesskey="&printFrameCmd.accesskey;"
                    oncommand="gContextMenu.printFrame();"/>
          <menuseparator/>
          <menuitem id="context-viewframesource"
                    label="&viewFrameSourceCmd.label;"
                    accesskey="&viewFrameSourceCmd.accesskey;"
                    oncommand="gContextMenu.viewFrameSource();"
                    observes="isFrameImage"/>
          <menuitem id="context-viewframeinfo"
                    label="&viewFrameInfoCmd.label;"
                    accesskey="&viewFrameInfoCmd.accesskey;"
                    oncommand="gContextMenu.viewFrameInfo();"/>
        </menupopup>
      </menu>
      <menuitem id="context-viewpartialsource-selection"
                label="&viewPartialSourceForSelectionCmd.label;"
                accesskey="&viewPartialSourceCmd.accesskey;"
                oncommand="gContextMenu.viewPartialSource('selection');"
                observes="isImage"/>
      <menuitem id="context-viewpartialsource-mathml"
                label="&viewPartialSourceForMathMLCmd.label;"
                accesskey="&viewPartialSourceCmd.accesskey;"
                oncommand="gContextMenu.viewPartialSource('mathml');"
                observes="isImage"/>
      <menuseparator id="context-sep-viewsource"/>
      <menuitem id="context-viewsource"
                label="&viewPageSourceCmd.label;"
                accesskey="&viewPageSourceCmd.accesskey;"
                oncommand="BrowserViewSource(gContextMenu.browser);"
                observes="canViewSource"/>
      <menuitem id="context-viewinfo"
                label="&viewPageInfoCmd.label;"
                accesskey="&viewPageInfoCmd.accesskey;"
                oncommand="gContextMenu.viewInfo();"/>
      <menuseparator id="spell-separator"/>
      <menuitem id="spell-check-enabled"
                label="&spellCheckToggle.label;"
                type="checkbox"
                accesskey="&spellCheckToggle.accesskey;"
                oncommand="InlineSpellCheckerUI.toggleEnabled(window);"/>
      <menuitem id="spell-add-dictionaries-main"
                label="&spellAddDictionaries.label;"
                accesskey="&spellAddDictionaries.accesskey;"
                oncommand="gContextMenu.addDictionaries();"/>
      <menu id="spell-dictionaries"
            label="&spellDictionaries.label;"
            accesskey="&spellDictionaries.accesskey;">
          <menupopup id="spell-dictionaries-menu">
              <menuseparator id="spell-language-separator"/>
              <menuitem id="spell-add-dictionaries"
                        label="&spellAddDictionaries.label;"
                        accesskey="&spellAddDictionaries.accesskey;"
                        oncommand="gContextMenu.addDictionaries();"/>
          </menupopup>
      </menu>
      <menuseparator hidden="true" id="context-sep-bidi"/>
      <menuitem hidden="true" id="context-bidi-text-direction-toggle"
                label="&bidiSwitchTextDirectionItem.label;"
                accesskey="&bidiSwitchTextDirectionItem.accesskey;"
                command="cmd_switchTextDirection"/>
      <menuitem hidden="true" id="context-bidi-page-direction-toggle"
                label="&bidiSwitchPageDirectionItem.label;"
                accesskey="&bidiSwitchPageDirectionItem.accesskey;"
                oncommand="gContextMenu.switchPageDirection();"/>
      <menuseparator id="fill-login-separator" hidden="true"/>
      <menu id="fill-login"
            label="&fillLoginMenu.label;"
            label-login="&fillLoginMenu.label;"
            label-password="&fillPasswordMenu.label;"
            label-username="&fillUsernameMenu.label;"
            accesskey="&fillLoginMenu.accesskey;"
            accesskey-login="&fillLoginMenu.accesskey;"
            accesskey-password="&fillPasswordMenu.accesskey;"
            accesskey-username="&fillUsernameMenu.accesskey;"
            hidden="true">
        <menupopup id="fill-login-popup">
          <menuitem id="fill-login-no-logins"
                    label="&noLoginSuggestions.label;"
                    disabled="true"
                    hidden="true"/>
          <menuseparator id="saved-logins-separator"/>
          <menuitem id="fill-login-saved-passwords"
                    label="&viewSavedLogins.label;"
                    oncommand="gContextMenu.openPasswordManager();"/>
        </menupopup>
      </menu>
      <menuseparator id="inspect-separator" hidden="true"/>
      <menuitem id="context-inspect"
                hidden="true"
                label="&inspectContextMenu.label;"
                accesskey="&inspectContextMenu.accesskey;"
                oncommand="gContextMenu.inspectNode();"/>
      <menuseparator id="context-media-eme-separator" hidden="true"/>
      <menuitem id="context-media-eme-learnmore"
                class="menuitem-iconic"
                hidden="true"
                label="&emeLearnMoreContextMenu.label;"
                accesskey="&emeLearnMoreContextMenu.accesskey;"
                oncommand="gContextMenu.drmLearnMore(event);"
                onclick="checkForMiddleClick(this, event);"/>
    </menupopup>

    <!-- for select dropdowns. The menupopup is what shows the list of options,
         and the popuponly menulist makes things like the menuactive attributes
         work correctly on the menupopup. ContentSelectDropdown expects the
         popuponly menulist to be its immediate parent. -->
    <menulist popuponly="true" id="ContentSelectDropdown" hidden="true">
      <menupopup rolluponmousewheel="true"
                 activateontab="true" position="after_start"
                 level="parent"
        />
    </menulist>
  </popupset>

<script type="application/javascript" src="chrome://global/content/editMenuOverlay.js"/>
<commandset id="editMenuCommands">
  <commandset id="editMenuCommandSetAll" commandupdater="true" events="focus,select"
              oncommandupdate="goUpdateGlobalEditMenuItems()"/>
  <commandset id="editMenuCommandSetUndo" commandupdater="true" events="undo"
              oncommandupdate="goUpdateUndoEditMenuItems()"/>
  <commandset id="editMenuCommandSetPaste" commandupdater="true" events="clipboard"
              oncommandupdate="goUpdatePasteMenuItems()"/>
  <command id="cmd_undo" oncommand="goDoCommand('cmd_undo')"/>
  <command id="cmd_redo" oncommand="goDoCommand('cmd_redo')"/>
  <command id="cmd_cut" oncommand="goDoCommand('cmd_cut')"/>
  <command id="cmd_copy" oncommand="goDoCommand('cmd_copy')"/>
  <command id="cmd_paste" oncommand="goDoCommand('cmd_paste')"/>
  <command id="cmd_delete" oncommand="goDoCommand('cmd_delete')"/>
  <command id="cmd_selectAll" oncommand="goDoCommand('cmd_selectAll')"/>
  <command id="cmd_switchTextDirection" oncommand="goDoCommand('cmd_switchTextDirection');"/>
</commandset>
</page>
PK
!<rg˜��1chrome/browser/content/browser/webrtcIndicator.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource:///modules/webrtcUI.jsm");

const BUNDLE_URL = "chrome://browser/locale/webrtcIndicator.properties";
var gStringBundle;

function init(event) {
  gStringBundle = Services.strings.createBundle(BUNDLE_URL);

  let brand = Services.strings.createBundle("chrome://branding/locale/brand.properties");
  let brandShortName = brand.GetStringFromName("brandShortName");
  document.title =
    gStringBundle.formatStringFromName("webrtcIndicator.windowtitle",
                                       [brandShortName], 1);

  for (let id of ["audioVideoButton", "screenSharePopup"]) {
    let popup = document.getElementById(id);
    popup.addEventListener("popupshowing", onPopupMenuShowing);
    popup.addEventListener("popuphiding", onPopupMenuHiding);
    popup.addEventListener("command", onPopupMenuCommand);
  }

  let fxButton = document.getElementById("firefoxButton");
  fxButton.addEventListener("click", onFirefoxButtonClick);
  fxButton.addEventListener("mousedown", PositionHandler);

  updateIndicatorState();

  // Alert accessibility implementations stuff just changed. We only need to do
  // this initially, because changes after this will automatically fire alert
  // events if things change materially.
  let ev = new CustomEvent("AlertActive", {bubbles: true, cancelable: true});
  document.documentElement.dispatchEvent(ev);
}

function updateIndicatorState() {
  // If gStringBundle isn't set, the window hasn't finished loading.
  if (!gStringBundle)
    return;

  updateWindowAttr("sharingvideo", webrtcUI.showCameraIndicator);
  updateWindowAttr("sharingaudio", webrtcUI.showMicrophoneIndicator);
  updateWindowAttr("sharingscreen", webrtcUI.showScreenSharingIndicator);

  // Camera and microphone button tooltip.
  let shareTypes = [];
  if (webrtcUI.showCameraIndicator)
    shareTypes.push("Camera");
  if (webrtcUI.showMicrophoneIndicator)
    shareTypes.push("Microphone");

  let audioVideoButton = document.getElementById("audioVideoButton");
  if (shareTypes.length) {
    let stringId = "webrtcIndicator.sharing" + shareTypes.join("And") + ".tooltip";
    audioVideoButton.setAttribute("tooltiptext",
                                   gStringBundle.GetStringFromName(stringId));
  } else {
    audioVideoButton.removeAttribute("tooltiptext");
  }

  // Screen sharing button tooltip.
  let screenShareButton = document.getElementById("screenShareButton");
  if (webrtcUI.showScreenSharingIndicator) {
    let stringId = "webrtcIndicator.sharing" +
      webrtcUI.showScreenSharingIndicator + ".tooltip";
    screenShareButton.setAttribute("tooltiptext",
                                    gStringBundle.GetStringFromName(stringId));
  } else {
    screenShareButton.removeAttribute("tooltiptext");
  }

  // Resize and ensure the window position is correct
  // (sizeToContent messes with our position).
  window.sizeToContent();
  PositionHandler.adjustPosition();
}

function updateWindowAttr(attr, value) {
  let docEl = document.documentElement;
  if (value)
    docEl.setAttribute(attr, "true");
  else
    docEl.removeAttribute(attr);
}

function onPopupMenuShowing(event) {
  let popup = event.target;

  let activeStreams;
  if (popup.getAttribute("type") == "Devices")
    activeStreams = webrtcUI.getActiveStreams(true, true, false);
  else
    activeStreams = webrtcUI.getActiveStreams(false, false, true);

  if (activeStreams.length == 1) {
    webrtcUI.showSharingDoorhanger(activeStreams[0]);
    event.preventDefault();
    return;
  }

  for (let stream of activeStreams) {
    let item = document.createElement("menuitem");
    item.setAttribute("label", stream.browser.contentTitle || stream.uri);
    item.setAttribute("tooltiptext", stream.uri);
    item.stream = stream;
    popup.appendChild(item);
  }
}

function onPopupMenuHiding(event) {
  let popup = event.target;
  while (popup.firstChild)
    popup.firstChild.remove();
}

function onPopupMenuCommand(event) {
  webrtcUI.showSharingDoorhanger(event.target.stream);
}

function onFirefoxButtonClick(event) {
  event.target.blur();
  let activeStreams = webrtcUI.getActiveStreams(true, true, true);
  activeStreams[0].browser.ownerGlobal.focus();
}

var PositionHandler = {
  positionCustomized: false,
  threshold: 10,
  adjustPosition() {
    if (!this.positionCustomized) {
      // Center the window horizontally on the screen (not the available area).
      // Until we have moved the window to y=0, 'screen.width' may give a value
      // for a secondary screen, so use values from the screen manager instead.
      let primaryScreen = Cc["@mozilla.org/gfx/screenmanager;1"]
                            .getService(Ci.nsIScreenManager)
                            .primaryScreen;
      let widthDevPix = {};
      primaryScreen.GetRect({}, {}, widthDevPix, {});
      let availTopDevPix = {};
      primaryScreen.GetAvailRect({}, availTopDevPix, {}, {});
      let scaleFactor = primaryScreen.defaultCSSScaleFactor;
      let widthCss = widthDevPix.value / scaleFactor;
      window.moveTo((widthCss - document.documentElement.clientWidth) / 2,
                    availTopDevPix.value / scaleFactor);
    } else {
      // This will ensure we're at y=0.
      this.setXPosition(window.screenX);
    }
  },
  setXPosition(desiredX) {
    // Ensure the indicator isn't moved outside the available area of the screen.
    desiredX = Math.max(desiredX, screen.availLeft);
    let maxX =
      screen.availLeft + screen.availWidth - document.documentElement.clientWidth;
    window.moveTo(Math.min(desiredX, maxX), screen.availTop);
  },
  handleEvent(aEvent) {
    switch (aEvent.type) {
      case "mousedown":
        if (aEvent.button != 0 || aEvent.defaultPrevented)
          return;

        this._startMouseX = aEvent.screenX;
        this._startWindowX = window.screenX;
        this._deltaX = this._startMouseX - this._startWindowX;

        window.addEventListener("mousemove", this);
        window.addEventListener("mouseup", this);
        break;

      case "mousemove":
        let moveOffset = Math.abs(aEvent.screenX - this._startMouseX);
        if (this._dragFullyStarted || moveOffset > this.threshold) {
          this.setXPosition(aEvent.screenX - this._deltaX);
          this._dragFullyStarted = true;
        }
        break;

      case "mouseup":
        this._dragFullyStarted = false;
        window.removeEventListener("mousemove", this);
        window.removeEventListener("mouseup", this);
        this.positionCustomized =
          Math.abs(this._startWindowX - window.screenX) >= this.threshold;
        break;
    }
  }
};
PK
!<4����2chrome/browser/content/browser/webrtcIndicator.xul<?xml version="1.0"?>


<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/webRTC-indicator.css" type="text/css"?>

<!DOCTYPE window>

<window xmlns:html="http://www.w3.org/1999/xhtml"
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        id="webrtcIndicator"
        role="alert"
        windowtype="Browser:WebRTCGlobalIndicator"
        onload="init(event);"
        sizemode="normal"
        hidechrome="true"
        orient="horizontal"
        >
  <script type="application/javascript" src="chrome://browser/content/webrtcIndicator.js"/>

  <button id="firefoxButton"/>
  <button id="audioVideoButton" type="menu">
    <menupopup id="audioVideoPopup" type="Devices"/>
  </button>
  <separator id="shareSeparator"/>
  <button id="screenShareButton" type="menu">
    <menupopup id="screenSharePopup" type="Screen"/>
  </button>
</window>
PK
!<V
�
��chrome/browser/content/cart.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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"
     width="32" height="32" viewBox="0 0 32 32">
  <path fill="context-fill" fill-rule="evenodd"
        d="M20.8195396,14 L15.1804604,14 L15.1804604,14 L15.8471271,18 L20.1528729,18 L20.8195396,14 Z M22.8471271,14 L27.6125741,14 L27.6125741,14 L26.2792408,18 L22.1804604,18 L22.8471271,14 Z M21.1528729,12 L14.8471271,12 L14.8471271,12 L14.1804604,8 L21.8195396,8 L21.1528729,12 Z M23.1804604,12 L28.2792408,12 L28.2792408,12 L29.6125741,8 L23.8471271,8 L23.1804604,12 Z M13.1528729,14 L8.47703296,14 L10.077033,18 L10.077033,18 L13.8195396,18 L13.1528729,14 Z M12.8195396,12 L7.67703296,12 L6.07703296,8 L12.1528729,8 L12.8195396,12 L12.8195396,12 Z M31.7207592,8 L32,8 L32,6 L31,6 L5.27703296,6 L5.27703296,6 L4,2.8074176 L4,2 L3,2 L1,2 L0,2 L0,4 L1,4 L2.32296704,4 L9.78931928,22.6658806 L9.78931928,22.6658806 C8.71085924,23.3823847 8,24.6081773 8,26 C8,28.209139 9.790861,30 12,30 C14.209139,30 16,28.209139 16,26 C16,25.2714257 15.8052114,24.5883467 15.4648712,24 L22.5351288,24 C22.1947886,24.5883467 22,25.2714257 22,26 C22,28.209139 23.790861,30 26,30 C28.209139,30 30,28.209139 30,26 C30,23.790861 28.209139,22 26,22 L11.677033,22 L10.877033,20 L27,20 L28,20 L28,19.1622777 L31.7207592,8 L31.7207592,8 Z M26,28 C27.1045695,28 28,27.1045695 28,26 C28,24.8954305 27.1045695,24 26,24 C24.8954305,24 24,24.8954305 24,26 C24,27.1045695 24.8954305,28 26,28 Z M12,28 C13.1045695,28 14,27.1045695 14,26 C14,24.8954305 13.1045695,24 12,24 C10.8954305,24 10,24.8954305 10,26 C10,27.1045695 10.8954305,28 12,28 Z"/>
</svg>

PK
!<��Z99 chrome/browser/content/chill.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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"
     width="32" height="32" viewBox="0 0 32 32">
  <path fill="context-fill" d="M9.1,18.5l-5.7,5.9C3.2,23.8,3,23.3,3,22.6c0-2.5,2-4.4,4.4-4.4C7.8,18.1,8.5,18.3,9.1,18.5 M26.5,18.5l-5.7,5.9
  c-0.2-0.5-0.4-1.1-0.4-1.8c0-2.5,2-4.4,4.4-4.4C25.4,18.1,26,18.3,26.5,18.5 M24.7,2L24.7,2c-0.7,0-1.4,0.7-1.4,1.4s0.7,1.4,1.4,1.4
  c2.5,0,4.4,2,4.4,4.4v7.6c-1.6-1.2-3.6-1.8-5.5-1.4c-2.1,0.4-3.9,1.6-5,3.4c-1.6-1.2-3.9-1.2-5.5,0c-1.1-1.8-2.8-3-5-3.4
  c-2-0.4-3.9,0.2-5.5,1.4V9.2c0-2.5,2-4.4,4.4-4.4c0.5,0,0.9-0.4,1.2-0.7c0.2-0.4,0.2-0.9,0-1.4C8.2,2.3,7.6,2,7.1,2
  C3.2,2,0,5.2,0,9.2v13.5C0,26.7,3.2,30,7.1,30l0,0c3.9,0,7.1-3.2,7.1-7.3c0-0.2,0-0.4,0-0.5c0.2-0.9,0.9-1.4,1.8-1.4
  s1.6,0.5,1.8,1.4v0.2c0,0.2,0,0.2,0,0.4c0,2,0.7,3.7,2.1,5c1.4,1.4,3,2.1,5,2.1l0,0c2,0,3.6-0.7,5-2.1c1.4-1.2,2.1-3.2,2.1-5V9.2
  C32,5.2,28.8,2,24.7,2"/>
</svg>

PK
!<I�}�ll!chrome/browser/content/circle.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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"
     width="32" height="32" viewBox="0 0 32 32">
  <circle fill="context-fill" cx="16" cy="16" r="16"/>
</svg>

PK
!<$����!chrome/browser/content/dollar.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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"
     width="32" height="32" viewBox="0 0 32 32">
  <path fill="context-fill" d="M17.3857868,14.0527919 C14.2304569,13.0862944 13.4913706,12.4609137 13.4913706,11.0964467 C13.4913706,9.61827411 14.7137056,8.85076142 16.4192893,8.85076142 C17.9827411,8.85076142 19.3187817,9.33401015 20.5979695,10.4994924 L22.4456853,8.42436548 C21.1664975,7.20203046 19.3187819,6.26535905 17,6.00952148 L17,2 L15,2 L15,6.00952148 C12.3827412,6.43591742 9.76751269,8.53807107 9.76751269,11.3238579 C9.76751269,14.1664975 11.4730964,15.786802 15.4812183,17.0091371 C18.4375635,17.9187817 19.2335025,18.6294416 19.2335025,20.2213198 C19.2335025,22.0690355 17.7553299,23.035533 15.7370558,23.035533 C13.7756345,23.035533 12.2406091,22.3248731 10.9329949,21.1025381 L9,23.2345178 C10.4213198,24.6274112 12.8659899,25.8324934 15,26.0030518 L15,30 L17,30 L17,26.0030518 C20.7116753,25.4060974 22.9857868,22.893401 22.9857868,20.022335 C22.9857868,16.4690355 20.7116751,15.1045685 17.3857868,14.0527919 Z"/>
</svg>

PK
!<���!%
%
&chrome/browser/content/fingerprint.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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"
     width="32" height="32" viewBox="0 0 32 32">
  <path fill="context-fill" d="M7.17741905,12 C7.10965537,12 7.041327,11.9953181 6.97243393,11.985018 C6.33263187,11.8918489 5.90515601,11.3862071 6.01809547,10.8552833 C7.41798011,4.26321358 12.2613889,2.57493207 15.0238882,2.15590491 C19.6448063,1.45690206 24.3408291,3.21541158 25.8344535,5.29743816 C26.1664955,5.76047488 25.9835336,6.35881757 25.4244832,6.63364321 C24.8654329,6.9098734 24.1437497,6.75583996 23.8122724,6.29327142 C22.8923805,5.01043967 19.1749781,3.51130562 15.4479759,4.07406612 C12.8080159,4.474834 9.43056132,6.03623689 8.33561323,11.1942506 C8.23453242,11.666651 7.73816348,12 7.17741905,12 Z M16.63127,26 C16.1452186,26 15.6509104,25.9658335 15.147795,25.8938767 C10.637921,25.257137 6.71207921,21.8114952 6.01575422,17.8807924 C5.91171832,17.2932317 6.33391695,16.7382846 6.95813239,16.6404441 C7.58454965,16.5343208 8.17298555,16.9406954 8.27757192,17.5272206 C8.80876054,20.5255916 11.9766264,23.26409 15.4885263,23.7610576 C17.3975027,24.02766 20.959494,23.8221432 23.3220449,19.3789425 C24.4625867,17.2331815 23.0049831,11.881462 19.9521622,9.34692739 C18.2380468,7.92384005 16.4573263,7.76905536 14.6628445,8.89499751 C13.26469,9.77142052 11.8070864,12.2857658 11.8665355,14.6287608 C11.9127737,16.4835887 12.8386382,17.9325598 14.6171568,18.9363308 C15.2210054,19.2764429 16.9411759,19.4933486 17.9424527,18.8296898 C18.7257495,18.3104622 18.9591422,17.2761485 18.6365758,15.7583267 C18.3822659,14.5650869 17.2219077,12.4452096 16.6664991,12.3711821 C16.6692513,12.3722175 16.4666841,12.4312324 16.1276041,12.9095636 C15.8545786,13.2936782 15.58981,14.7297074 15.9476054,15.3581643 C16.0142104,15.4761941 16.0725586,15.5465978 16.3202632,15.5465978 C16.9532859,15.5465978 17.46686,16.0290705 17.46686,16.6249139 C17.46686,17.2207573 16.9543868,17.7042653 16.3213641,17.7042653 C15.2644914,17.7042653 14.4140391,17.2336992 13.9268868,16.3774655 C13.1083609,14.9388479 13.5536787,12.6548678 14.2202791,11.7137354 C15.2540327,10.2564816 16.3631986,10.1151564 17.1123672,10.2564816 C19.7066595,10.7389543 20.8763754,15.2908666 20.8857331,15.3359043 C21.5303153,18.3648181 20.3594985,19.8665919 19.264094,20.593407 C17.4151172,21.8192603 14.6920186,21.493643 13.4380832,20.7859819 C10.3280151,19.0310652 9.62013053,16.497566 9.5744428,14.6805283 C9.49022326,11.3643051 11.4779146,8.30018945 13.391845,7.10021984 C16.0417332,5.43848454 18.9877658,5.66781436 21.4714167,7.72919442 C25.1176276,10.7565552 27.0871539,17.1229168 25.3746898,20.3433702 C23.4326862,23.9950465 20.2983981,26 16.63127,26 Z M16.0845157,30 C14.9348455,30 13.9050564,29.8557557 13.0394288,29.6610017 C10.2114238,29.0257442 7.58700058,27.4599412 6.18892823,25.5735955 C5.84440518,25.1078371 5.98426642,24.4803503 6.50105099,24.1700066 C7.01675554,23.8596629 7.71552172,23.986423 8.06112477,24.4507244 C9.89498097,26.9252176 15.9397944,29.9781448 22.2508301,26.1937972 C22.7676147,25.8844249 23.4658409,26.0087566 23.8109039,26.474515 C24.155427,26.9397877 24.0161057,27.5672745 23.4993212,27.8776182 C20.7987573,29.4963593 18.2315746,30 16.0845157,30 Z"/>
</svg>

PK
!<*8���chrome/browser/content/food.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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"
     width="32" height="32" viewBox="0 0 32 32">
  <path fill="context-fill" d="M14.1,0.9v5.3h-1.4V0.9c0-1.1-1.4-1.1-1.4,0v5.3h-1.2V0.9c0-1.1-1.4-1.1-1.4,0v5.3H7.2V0.9c0-1.2-1.6-1.1-1.6,0
  v10.4c0,1.8,1.2,3,2.8,3v15.2c0,1.6,1.1,2.5,2.1,2.5s2.1-0.9,2.1-2.5V14.3c1.6,0,2.8-1.4,2.8-2.8V0.9C15.6-0.4,14.1-0.2,14.1,0.9z
   M19.8,3.7v25.8c0,3.2,4.2,3.2,4.2,0V17.1h2.3V3.7C26.5-1.2,19.8-1.2,19.8,3.7z"/>
</svg>

PK
!<{�L�� chrome/browser/content/fruit.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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"
     width="32" height="32" viewBox="0 0 32 32">
  <path fill="context-fill" d="M16.5,8c-2.1-0.9-3.9-1.2-6.6-0.9C4.6,8,1.8,12.6,1.8,18c0,5.9,4.8,14,9.8,14c1.6,0,3.9-1.2,4.4-1.2
  c0.5,0,2.8,1.2,4.4,1.2c5,0,9.8-8.4,9.8-14c0-5.9-3.2-10.8-9.8-10.8C19,7.1,17.8,7.5,16.5,8z M11.7,0c1.1,0.2,3.2,0.9,4.1,2.3
  c0.9,1.4,0.5,3.6,0.2,4.6c-1.2-0.2-3.2-0.7-4.1-2.3C11,3.2,11.4,1.1,11.7,0L11.7,0z"/>
</svg>

PK
!<d�4``chrome/browser/content/gift.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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"
     width="32" height="32" viewBox="0 0 32 32">
  <path fill="context-fill" d="M30.3,8.1h-4.5V8c0.7-0.7,1.3-1.9,1.3-3.2c0-2.6-2.1-4.7-4.9-4.7c-1.5,0-4.5,1.5-6.4,3.4C14,1.6,11,0.1,9.5,0.1
  c-2.6,0-4.9,2.1-4.9,4.7C4.7,6.1,5.2,7.2,6,8H1.7C0.6,8,0,8.7,0,9.6v4.5c0,0.2,0.2,0.4,0.4,0.4h13.8V9.6h3.2v4.9h14.2
  c0.2,0,0.4-0.2,0.4-0.4V9.6C32,8.7,31.4,8.1,30.3,8.1z M9.5,6.5C8.6,6.5,8,5.9,8,4.8s0.6-1.5,1.5-1.5s3.7,1.9,4.7,2.8
  C13.7,6.3,9.5,6.5,9.5,6.5z M22.3,6.5c0,0-4.1-0.2-4.7-0.4c0.9-1.1,3.7-2.8,4.7-2.8S24,3.8,24,4.8S23.2,6.5,22.3,6.5z M1.7,17.7
  h12.7v14.2H1.7V17.7z M17.6,17.7h12.7v14.2H17.6V17.7z"/>
</svg>

PK
!<.b&I��chrome/browser/content/pet.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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"
     width="32" height="32" viewBox="0 0 32 32">
  <path fill="context-fill" d="M28.5,8.1c0-1.1-1-1.9-2.1-2.4V3.7c-0.2-0.2-0.3-0.3-0.6-0.3c-0.6,0-1.1,0.8-1.3,2.1c-0.2,0-0.3,0-0.5,0l0,0
  c0-0.2,0-0.3-0.2-0.5c-0.3-1.1-0.8-1.9-1.3-2.6C22,2.6,21.7,3.2,21.7,4L22,6.3c-0.3,0.2-0.6,0.3-1,0.6l-3.5,3.7l0,0
  c0,0-6.3-0.8-10.9,0.2c-0.6,0-1,0.2-1.1,0.3c-0.5,0.2-0.8,0.3-1.1,0.6c-1.1-0.8-2.2-2.1-3.2-4c0-0.3-0.5-0.5-0.8-0.5s-0.5,0.6-0.3,1
  c0.8,2.1,2.1,3.5,3.4,4.5c-0.5,0.5-0.8,1-1,1.6c0,0-0.3,2.2-0.3,5.5l1.4,8c0,1,0.8,1.8,1.9,1.8c1,0,1.9-0.8,1.9-1.8V23l0.5-1.3h8.8
  l0.8,1.3v4.7c0,1,0.8,1.8,1.9,1.8c1,0,1.6-0.6,1.8-1.4l0,0l1.9-9l0,0l2.1-6.4h3c3.4,0,3.7-2.9,3.7-2.9L28.5,8.1z"/>
</svg>

PK
!<�h���chrome/browser/content/tree.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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"
     width="32" height="32" viewBox="0 0 32 32">
  <path fill="context-fill" d="M0.7,18c0,4.9,3.6,8.8,8.1,9.5v4.3c0.2,0,3.2,0,3.2,0v-4.3c1.8-0.4,3.6-1.1,4.9-2.5c0.2-0.2,0.2-0.2,0.2-0.5
  c-0.2-0.4-0.2-1.1-0.2-1.6c0-2,0.2-4.9,1.6-7.9c0,0,0.9-1.6,0.7-1.8C18,7.2,14.4,0,10.4,0C5,0,0.7,12.6,0.7,18z M18.3,22.8
  c0,3.1,2.2,5.6,4.9,6.3V32h3.2v-2.9c2.7-0.7,4.9-3.2,4.9-6.3c0-3.6-2.9-12.9-6.5-12.9S18.3,19.2,18.3,22.8z"/>
</svg>

PK
!<�E��#chrome/browser/content/vacation.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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"
     width="32" height="32" viewBox="0 0 32 32">
  <path fill="context-fill" d="M3.6,27l-2.5-1.8L0.8,25c-0.7-0.4-0.7-1.2-0.4-2c0.4-0.5,1.1-0.7,1.6-0.5l3.6,1.2c0-0.4,0.2-0.9,0.4-1.4
  c0.2-0.7,0.5-1.6,1.1-2.3c0.2-0.4,0.5-0.7,0.7-1.2c0.2-0.4,0.5-0.9,0.9-1.2c0.4-0.9,1.1-1.6,1.8-2.5c0.7-0.9,1.4-1.6,2.3-2.5
  c0.4-0.4,0.9-0.7,1.2-1.2c0.4-0.2,0.9-0.7,1.2-1.1c0.2-0.2,0.2-0.2,0.4-0.4L3.1,7.3c-0.2,0-0.2,0-0.4,0l-2,0.9
  C0.2,8.3-0.3,7.6,0.2,7.1l2-2C2.4,5,2.4,5,2.5,5h17.9c0.5-0.5,1.2-1.1,1.8-1.6c0.7-0.7,1.4-1.2,2.1-1.8c0.4-0.4,0.7-0.5,1.1-0.7
  c0.4-0.2,0.7-0.4,1.1-0.5c0.5,0,0.9-0.2,1.2-0.2s0.7,0,1.1,0s0.7,0,1.1,0c0.4,0,0.5,0,0.7,0.2c0.5,0,0.7,0.2,0.7,0.2
  c0.2,0,0.2,0.2,0.4,0.4c0,0,0,0.4,0.2,0.7c0,0.2,0,0.5,0.2,0.7c0,0.4,0,0.7,0,1.1s0,0.7,0,1.1c0,0.4,0,0.9-0.2,1.2
  c-0.2,0.4-0.4,0.7-0.5,1.1c-0.2,0.4-0.5,0.7-0.7,1.1c-0.5,0.7-1.1,1.4-1.8,2.1c-0.4,0.4-0.7,0.7-1.1,1.1v17.8c0,0.2,0,0.4-0.2,0.4
  l-2,2c-0.5,0.5-1.2,0-1.1-0.5l0.7-2c0-0.2,0-0.2,0-0.4L22.8,16c-0.4,0.4-0.7,0.7-0.9,0.9c-0.4,0.4-0.7,0.9-1.2,1.2
  c-0.4,0.4-0.7,0.9-1.2,1.2c-0.7,0.9-1.6,1.6-2.5,2.3c-0.9,0.7-1.6,1.4-2.5,2c-0.4,0.4-0.9,0.5-1.2,0.9c-0.4,0.2-0.7,0.5-1.2,0.7
  c-0.7,0.4-1.6,0.7-2.3,1.1c-0.4,0.2-0.7,0.2-1.2,0.4L9.6,30c0.4,0.7,0,1.4-0.7,1.8c-0.5,0.2-1.2,0-1.6-0.5l-0.2-0.4l-1.8-2.3
  c-0.2,0-0.2,0-0.4,0.2c-0.4,0-0.5,0.2-0.7,0.2s-0.2,0-0.4,0c-0.2,0-0.2,0-0.4,0c-0.2,0-0.4,0-0.5,0c-0.2,0-0.2,0-0.2,0s0,0,0-0.2
  c0-0.2,0-0.2,0-0.5c0-0.2,0-0.2,0-0.4c0-0.2,0-0.2,0-0.4C3.4,27.5,3.4,27.3,3.6,27L3.6,27z M5.7,28.4L5.7,28.4L5.7,28.4L5.7,28.4z"
  />
</svg>

PK
!<�"�"gg5chrome/browser/skin/classic/browser/aboutNetError.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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://browser/skin/error-pages.css");

:root {
  --exception-button-container-background: #F5F5F7;
}

body.captiveportal .title {
  background-image: url("wifi.svg");
}

body.certerror .title {
  background-image: url("cert-error.svg");
}

body.blocked .title {
  background-image: url("chrome://global/skin/icons/blocked.svg");
}

#errorContainer {
  display: none;
}

/* Pressing the retry button will cause the cursor to flicker from a pointer to
 * not-allowed. Override the disabled cursor behaviour since we will never show
 * the button disabled as the initial state. */
button:disabled {
  cursor: pointer;
}

#prefChangeContainer {
  display: none;
}

#learnMoreContainer {
  display: none;
}

#certErrorAndCaptivePortalButtonContainer {
  display: none;
}

body:not(.neterror) #certErrorAndCaptivePortalButtonContainer {
  display: flex;
}

body:not(.neterror) #netErrorButtonContainer {
  display: none;
}

#errorTryAgain {
  margin-top: 1.2em;
}

#advancedButton {
  display: none;
}

body.captiveportal #returnButton {
  display: none;
}

body:not(.captiveportal) #openPortalLoginPageButton {
  display: none;
}

#openPortalLoginPageButton {
  margin-inline-start: 0;
}

body:not(.neterror) #advancedButton {
  display: block;
}

#certificateErrorReporting {
  display: none;
}

#advancedPanelContainer {
  position: absolute;
  width: 100%;
  left: 0;
}

.advanced-panel {
  /* Hidden until the link is clicked */
  display: none;
  background-color: white;
  border: 1px lightgray solid;
  margin: 48px auto;
  min-width: var(--in-content-container-min-width);
  max-width: var(--in-content-container-max-width);
}

#overrideWeakCryptoPanel {
  display: none;
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: space-between;
  align-content: space-between;
  align-items: flex-start;
  margin-top: 1em;
}

span#hostname {
  font-weight: bold;
}

#automaticallyReportInFuture {
  cursor: pointer;
}

#errorCode:not([href]) {
  color: var(--in-content-page-color);
  cursor: text;
  text-decoration: none;
}

#errorCode[href] {
  white-space: nowrap;
}

#badCertTechnicalInfo {
  margin: 3em;
  overflow: auto;
  white-space: pre-wrap;
}

#certificateErrorReporting {
  display: none;
}

#certificateErrorDebugInformation {
  display: none;
  background-color: var(--in-content-box-background-hover) !important;
  border-top: 1px solid var(--in-content-border-color);
  position: absolute;
  width: 100%;
  padding: 1em 17.5%;
  box-sizing: border-box;
}

#certificateErrorText {
  font-family: monospace;
  white-space: pre-wrap;
  padding: 1em 0;
}

#cert_domain_link:not([href]) {
  color: var(--in-content-page-color);
  text-decoration: none;
}

.exceptionDialogButtonContainer {
  background-color: var(--exception-button-container-background);
  display: flex;
  justify-content: end;
  padding: 10px;
}

.exceptionDialogButtonContainer[hidden] {
  display: none;
}

.illustrated #errorPageContainer {
  min-height: 300px;
  display: flex;
  flex-direction: column;
  background-position: left center;
}

.illustrated[dir="rtl"] #errorPageContainer {
  background-position: right center;
}

.illustrated .title {
  background: none;
  padding-inline-start: 0;
  margin-inline-start: 0;
}

.illustrated #text-container {
  margin: auto;
  padding-inline-start: 38%;
}

.illustrated #errorPageContainer {
  background-repeat: no-repeat;
  background-size: 38%;
}

.connectionFailure #errorPageContainer,
.netInterrupt #errorPageContainer,
.netTimeout #errorPageContainer,
.netReset #errorPageContainer,
.netOffline #errorPageContainer {
  background-image: url("chrome://browser/content/illustrations/error-connection-failure.svg");
}

.dnsNotFound #errorPageContainer {
  background-image: url("chrome://browser/content/illustrations/error-server-not-found.svg");
}

.malformedURI #errorPageContainer {
  background-image: url("chrome://browser/content/illustrations/error-malformed-url.svg");
}

.malformedURI #errorTryAgain {
  display: none;
}
PK
!<�x�())Gchrome/browser/skin/classic/browser/aboutSessionRestore-window-icon.png�PNG


IHDR�a�IDATxb���?%�r�����|����_��bafdP�d޷qZ�3H�ԦX�O�m+;Cn�b'����_{M��Z�p6��÷/�H�W�zF0�EG�7C��
C��B(��`o���p�.���/�7��H�h�$d���~w��2�����&�Rl�AD_3L�T'�I@T�7��HJLRZ��ݎ��b�Z~@${��[��"�z��}�ܼ�k�&�����A����al.RIEND�B`�PK
!<����;chrome/browser/skin/classic/browser/aboutSessionRestore.css
.description-wrapper {
  background-image: url("chrome://browser/skin/illustrations/error-session-restore.svg");
  background-repeat: no-repeat;
  background-size: 38%;
  padding-inline-start: 38%;
  background-position: left center;
  min-height: 300px;
}

.title {
  background-image: none;
}

body[dir="rtl"] .description-wrapper {
  background-position: right center;
}
body[dir="rtl"] #tabsToggle:after {
  background-position: left center;
}

#tabsToggle {
  cursor: pointer;
  color: #0a84ff;
  -moz-context-properties: fill;
  fill: currentColor;
  border: none;
  padding: 0;
  margin: 0;
  padding-inline-end: 45px;
  font: inherit;
  position: relative;
}

#tabsToggle:after {
  content: "";
  position: absolute;
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
  background-image: url("chrome://global/skin/icons/arrow-dropdown-16.svg");
  background-repeat: no-repeat;
  background-size: 20px;
  background-position: right center;
}

#tabsToggle:not(.show-tabs):after {
  transform: scaleY(-1);
}

#tabsToggle:hover {
  background-color: transparent;
}

#tabsToggle.show-tabs > #hideTabs,
#tabsToggle:not(.show-tabs) > #showTabs {
  display: none;
}

.tree-container:not(.expanded) {
  visibility: collapse;
}

.button-container {
  text-align: end;
}

treechildren::-moz-tree-image(icon),
treechildren::-moz-tree-image(noicon) {
  padding-right: 2px;
  margin: 0 2px;
  width: 16px;
  height: 16px;
}

treechildren::-moz-tree-image(noicon) {
  list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.svg");
  -moz-context-properties: fill;
  fill: currentColor;
}
treechildren::-moz-tree-image(container, noicon) {
  list-style-image: url("chrome://browser/skin/aboutSessionRestore-window-icon.png");
}

treechildren::-moz-tree-image(checked) {
  list-style-image: url("chrome://global/skin/in-content/check.svg");
  -moz-context-properties: fill, stroke;
  fill: #2292d0;
  stroke: none;
}
treechildren::-moz-tree-image(checked, selected) {
  fill: white;
  stroke: #0095dd;
}

treechildren::-moz-tree-image(partial) {
  list-style-image: url("chrome://global/skin/in-content/check-partial.svg");
  -moz-context-properties: fill, stroke;
  fill: #2292d0;
  stroke: none;
}
treechildren::-moz-tree-image(partial, selected) {
  fill: white;
  stroke: #0095dd;
}
PK
!<e��gg7chrome/browser/skin/classic/browser/aboutTabCrashed.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

body {
  font-size: 1.25rem;
}

.title {
  background-image: url("chrome://browser/skin/tab-crashed.svg");
}

.title > h1 {
  /**
   * Add commentary?
   */
  padding-right: 14px;
}

#reportSent {
  font-weight: bold;
}

#reportBox {
  background-color: var(--in-content-box-background-hover);
  margin: 24px 0;
  padding: 14px;
  border: 1px solid var(--in-content-box-border-color);
  border-radius: 2px;
}

#reportBox > h2:first-child {
  margin-top: 0;
}

#crash-reporter-title {
  font-weight: bold;
  margin: 0 0 14px 0;
}

input[type="text"],
textarea {
  width: 100%;
  box-sizing: border-box;
  resize: none;
}

input[type="text"],
input[type="checkbox"] {
  -moz-margin-start: 0px;
}

#options {
  list-style: none;
  margin-inline-start: 0;
}

#options > li,
#email {
  margin-top: 14px;
}

.checkbox-with-label {
  display: flex;
}

.checkbox-with-label > label {
  margin-top: auto;
  margin-bottom: auto;
}
PK
!<�4Om$$8chrome/browser/skin/classic/browser/aboutWelcomeBack.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

.title {
  background-image: url("chrome://browser/skin/welcome-back.svg");
}

.radioRestoreContainer:not(:last-child) {
  margin-bottom: 0.2em;
}

/* tablist starts out hidden, but JS may make it visible in response to
   clicks on the radio buttons by setting an "available" attribute.
*/
.tree-container:not([available]) {
  display: none;
}

treechildren::-moz-tree-image(icon),
treechildren::-moz-tree-image(noicon) {
  padding-right: 2px;
  margin: 0 2px;
  width: 16px;
  height: 16px;
}

treechildren::-moz-tree-image(noicon) {
  list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.svg");
  -moz-context-properties: fill;
  fill: currentColor;
}
treechildren::-moz-tree-image(container, noicon) {
  list-style-image: url("chrome://browser/skin/aboutSessionRestore-window-icon.png");
}

treechildren::-moz-tree-image(checked) {
  list-style-image: url("chrome://global/skin/in-content/check.svg");
  -moz-context-properties: fill, stroke;
  fill: #2292d0;
  stroke: none;
}
treechildren::-moz-tree-image(checked, selected) {
  fill: white;
  stroke: #0095dd;
}

treechildren::-moz-tree-image(partial) {
  list-style-image: url("chrome://global/skin/in-content/check-partial.svg");
  -moz-context-properties: fill, stroke;
  fill: #2292d0;
  stroke: none;
}
treechildren::-moz-tree-image(partial, selected) {
  fill: white;
  stroke: #0095dd;
}
PK
!<����<chrome/browser/skin/classic/browser/accessibility-active.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="24px" height="24px">
<path fill="#008EA4" fill-opacity="0.9886" d="M12,24L12,24C5.4,24,0,18.6,0,12l0,0C0,5.4,5.4,0,12,0l0,0c6.6,0,12,5.4,12,12l0,0  C24,18.6,18.6,24,12,24z"/>
<g>
	<circle fill="#FFFFFF" cx="12" cy="6" r="2"/>
	<path fill="#FFFFFF" d="M18.1,8.5h-3.6l0,0h-5l0,0H6c-0.6,0-1,0.4-1,1s0.4,1,1,1h3.5v0.6l0,0v7.8c0,0.6,0.4,1.1,1,1.1s1-0.5,1-1.1   v-4.1h1v4.1c0,0.6,0.4,1.1,1,1.1s1-0.5,1-1.1v-4.1l0,0v-4.5H18c0.6,0,1-0.4,1-1C19,8.8,18.6,8.5,18.1,8.5z"/>
</g>
</svg>PK
!<ց��5chrome/browser/skin/classic/browser/accessibility.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="24px" height="24px">
<path fill="#00C8D7" fill-opacity="0.9886" d="M12,24L12,24C5.4,24,0,18.6,0,12l0,0C0,5.4,5.4,0,12,0l0,0c6.6,0,12,5.4,12,12l0,0  C24,18.6,18.6,24,12,24z"/>
<g>
	<circle fill="#FFFFFF" cx="12" cy="6" r="2"/>
	<path fill="#FFFFFF" d="M18.1,8.5h-3.6l0,0h-5l0,0H6c-0.6,0-1,0.4-1,1c0,0.6,0.4,1,1,1h3.5v0.6l0,0v7.8c0,0.6,0.4,1.1,1,1.1s1-0.5,1-1.1
  v-4.1h1v4.1c0,0.6,0.4,1.1,1,1.1s1-0.5,1-1.1v-4.1l0,0v-4.5H18c0.6,0,1-0.4,1-1C19,8.8,18.6,8.5,18.1,8.5z"/>
</g>
</svg>
PK
!<Hv����Dchrome/browser/skin/classic/browser/addons/addon-install-blocked.svg<?xml version="1.0" encoding="utf-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 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
     width="64" height="64" viewBox="0 0 64 64">
  <defs>
    <style>
      .style-puzzle-piece {
        fill: url('#gradient-linear-puzzle-piece');
      }
      .style-badge-shadow {
        fill: #0d131a;
        fill-opacity: .15;
      }
      .style-badge-background {
        fill: #fff;
      }
      .style-badge-inside {
        fill: #e62117;
      }
      .style-badge-icon {
        fill: #fff;
      }
    </style>
    <linearGradient id="gradient-linear-puzzle-piece" x1="0%" y1="0%" x2="0%" y2="100%">
      <stop offset="0%" stop-color="#999999" stop-opacity="1"/>
      <stop offset="100%" stop-color="#8c8c8c" stop-opacity="1"/>
    </linearGradient>
  </defs>
  <path class="style-puzzle-piece" d="M42,62c2.2,0,4-1.8,4-4l0-14.2c0,0,0.4-3.7,2.8-3.7c2.4,0,2.2,3.9,6.7,3.9c2.3,0,6.2-1.2,6.2-8.2 c0-7-3.9-7.9-6.2-7.9c-4.5,0-4.3,3.7-6.7,3.7c-2.4,0-2.8-3.8-2.8-3.8V22c0-2.2-1.8-4-4-4H31.5c0,0-3.4-0.6-3.4-3 c0-2.4,3.8-2.6,3.8-7.1c0-2.3-1.3-5.9-8.3-5.9s-8,3.6-8,5.9c0,4.5,3.4,4.7,3.4,7.1c0,2.4-3.4,3-3.4,3H6c-2.2,0-4,1.8-4,4l0,7.8 c0,0-0.4,6,4.4,6c3.1,0,3.2-4.1,7.3-4.1c2,0,4,1.9,4,6c0,4.2-2,6.3-4,6.3c-4,0-4.2-4.1-7.3-4.1c-4.8,0-4.4,5.8-4.4,5.8L2,58 c0,2.2,1.8,4,4,4H19c0,0,6.3,0.4,6.3-4.4c0-3.1-4-3.6-4-7.7c0-2,2.2-4.5,6.4-4.5c4.2,0,6.6,2.5,6.6,4.5c0,4-3.9,4.6-3.9,7.7 c0,4.9,6.3,4.4,6.3,4.4H42z"/>
  <svg width="32" height="32" x="32" y="0">
    <ellipse class="style-badge-shadow"     rx="14" ry="15" cx="16" cy="17" />
    <circle  class="style-badge-background" r="15"  cy="15" cx="16" />
    <circle  class="style-badge-inside"     r="12"  cy="15" cx="16" />
    <rect    class="style-badge-icon"       x="9" y="13" width="14" height="4" rx="1" ry="1" />
  </svg>
</svg>
PK
!<ҍ�XuuDchrome/browser/skin/classic/browser/addons/addon-install-confirm.svg<?xml version="1.0" encoding="utf-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 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
     width="64" height="64" viewBox="0 0 64 64">
  <defs>
    <style>
      .style-puzzle-piece {
        fill: url('#gradient-linear-puzzle-piece');
      }
    </style>
    <linearGradient id="gradient-linear-puzzle-piece" x1="0%" y1="0%" x2="0%" y2="100%">
      <stop offset="0%" stop-color="#66cc52" stop-opacity="1"/>
      <stop offset="100%" stop-color="#60bf4c" stop-opacity="1"/>
    </linearGradient>
  </defs>
  <path class="style-puzzle-piece" d="M42,62c2.2,0,4-1.8,4-4l0-14.2c0,0,0.4-3.7,2.8-3.7c2.4,0,2.2,3.9,6.7,3.9c2.3,0,6.2-1.2,6.2-8.2 c0-7-3.9-7.9-6.2-7.9c-4.5,0-4.3,3.7-6.7,3.7c-2.4,0-2.8-3.8-2.8-3.8V22c0-2.2-1.8-4-4-4H31.5c0,0-3.4-0.6-3.4-3 c0-2.4,3.8-2.6,3.8-7.1c0-2.3-1.3-5.9-8.3-5.9s-8,3.6-8,5.9c0,4.5,3.4,4.7,3.4,7.1c0,2.4-3.4,3-3.4,3H6c-2.2,0-4,1.8-4,4l0,7.8 c0,0-0.4,6,4.4,6c3.1,0,3.2-4.1,7.3-4.1c2,0,4,1.9,4,6c0,4.2-2,6.3-4,6.3c-4,0-4.2-4.1-7.3-4.1c-4.8,0-4.4,5.8-4.4,5.8L2,58 c0,2.2,1.8,4,4,4H19c0,0,6.3,0.4,6.3-4.4c0-3.1-4-3.6-4-7.7c0-2,2.2-4.5,6.4-4.5c4.2,0,6.6,2.5,6.6,4.5c0,4-3.9,4.6-3.9,7.7 c0,4.9,6.3,4.4,6.3,4.4H42z"/>
</svg>
PK
!<"�[ggHchrome/browser/skin/classic/browser/addons/addon-install-downloading.svg<?xml version="1.0" encoding="utf-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 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
     width="64" height="64" viewBox="0 0 64 64">
  <defs>
    <style>
      .style-puzzle-piece {
        fill: url('#gradient-linear-puzzle-piece');
      }
      .style-badge-shadow {
        fill: #0d131a;
        fill-opacity: .15;
      }
      .style-badge-background {
        fill: #fff;
      }
      .style-badge-inside {
        fill: #55cc3d;
      }
      .style-badge-icon {
        fill: #fff;
      }
    </style>
    <linearGradient id="gradient-linear-puzzle-piece" x1="0%" y1="0%" x2="0%" y2="100%">
      <stop offset="0%" stop-color="#66cc52" stop-opacity="1"/>
      <stop offset="100%" stop-color="#60bf4c" stop-opacity="1"/>
    </linearGradient>
  </defs>
  <path class="style-puzzle-piece" d="M42,62c2.2,0,4-1.8,4-4l0-14.2c0,0,0.4-3.7,2.8-3.7c2.4,0,2.2,3.9,6.7,3.9c2.3,0,6.2-1.2,6.2-8.2 c0-7-3.9-7.9-6.2-7.9c-4.5,0-4.3,3.7-6.7,3.7c-2.4,0-2.8-3.8-2.8-3.8V22c0-2.2-1.8-4-4-4H31.5c0,0-3.4-0.6-3.4-3 c0-2.4,3.8-2.6,3.8-7.1c0-2.3-1.3-5.9-8.3-5.9s-8,3.6-8,5.9c0,4.5,3.4,4.7,3.4,7.1c0,2.4-3.4,3-3.4,3H6c-2.2,0-4,1.8-4,4l0,7.8 c0,0-0.4,6,4.4,6c3.1,0,3.2-4.1,7.3-4.1c2,0,4,1.9,4,6c0,4.2-2,6.3-4,6.3c-4,0-4.2-4.1-7.3-4.1c-4.8,0-4.4,5.8-4.4,5.8L2,58 c0,2.2,1.8,4,4,4H19c0,0,6.3,0.4,6.3-4.4c0-3.1-4-3.6-4-7.7c0-2,2.2-4.5,6.4-4.5c4.2,0,6.6,2.5,6.6,4.5c0,4-3.9,4.6-3.9,7.7 c0,4.9,6.3,4.4,6.3,4.4H42z"/>
  <svg width="32" height="32" x="32" y="0">
    <ellipse class="style-badge-shadow"     rx="14" ry="15" cx="16" cy="17" />
    <circle  class="style-badge-background" r="15"  cy="15" cx="16" />
    <circle  class="style-badge-inside"     r="12"  cy="15" cx="16" />
    <path    class="style-badge-icon" d="M22.7,16.1l-5.6,5.5C16.8,21.9,16.4,22,16,22c-0.4,0-0.7-0.1-1-0.4 l-5.6-5.5C8.8,15.5,8.9,15,9.8,15l3.2,0V9c0-0.6,0.5-1,1.1-1h4c0.6,0,1,0.4,1,1v6h3.2C23.1,15,23.3,15.5,22.7,16.1z"/>
  </svg>
</svg>
PK
!<HQ2��Bchrome/browser/skin/classic/browser/addons/addon-install-error.svg<?xml version="1.0" encoding="utf-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 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
     width="64" height="64" viewBox="0 0 64 64">
  <defs>
    <style>
      .style-puzzle-piece {
        fill: url('#gradient-linear-puzzle-piece');
      }
      .style-badge-shadow {
        fill: #0d131a;
        fill-opacity: .15;
      }
      .style-badge-background {
        fill: #fff;
      }
      .style-badge-inside {
        fill: #e62117;
      }
      .style-badge-icon {
        fill: #fff;
      }
    </style>
    <linearGradient id="gradient-linear-puzzle-piece" x1="0%" y1="0%" x2="0%" y2="100%">
      <stop offset="0%" stop-color="#999999" stop-opacity="1"/>
      <stop offset="100%" stop-color="#8c8c8c" stop-opacity="1"/>
    </linearGradient>
  </defs>
  <path class="style-puzzle-piece" d="M42,62c2.2,0,4-1.8,4-4l0-14.2c0,0,0.4-3.7,2.8-3.7c2.4,0,2.2,3.9,6.7,3.9c2.3,0,6.2-1.2,6.2-8.2 c0-7-3.9-7.9-6.2-7.9c-4.5,0-4.3,3.7-6.7,3.7c-2.4,0-2.8-3.8-2.8-3.8V22c0-2.2-1.8-4-4-4H31.5c0,0-3.4-0.6-3.4-3 c0-2.4,3.8-2.6,3.8-7.1c0-2.3-1.3-5.9-8.3-5.9s-8,3.6-8,5.9c0,4.5,3.4,4.7,3.4,7.1c0,2.4-3.4,3-3.4,3H6c-2.2,0-4,1.8-4,4l0,7.8 c0,0-0.4,6,4.4,6c3.1,0,3.2-4.1,7.3-4.1c2,0,4,1.9,4,6c0,4.2-2,6.3-4,6.3c-4,0-4.2-4.1-7.3-4.1c-4.8,0-4.4,5.8-4.4,5.8L2,58 c0,2.2,1.8,4,4,4H19c0,0,6.3,0.4,6.3-4.4c0-3.1-4-3.6-4-7.7c0-2,2.2-4.5,6.4-4.5c4.2,0,6.6,2.5,6.6,4.5c0,4-3.9,4.6-3.9,7.7 c0,4.9,6.3,4.4,6.3,4.4H42z"/>
  <svg width="32" height="32" x="32" y="0">
    <ellipse class="style-badge-shadow"     rx="14" ry="15" cx="16" cy="17" />
    <circle  class="style-badge-background" r="15"  cy="15" cx="16" />
    <circle  class="style-badge-inside"     r="12"  cy="15" cx="16" />
    <path    class="style-badge-icon" d="M14.9,16.2c0,0,0.1,0.8,1.1,0.8c1,0,1.1-0.8,1.1-0.8 s0.7-3.5,0.8-5.2C18,9.3,18.4,7,16,7s-2,2.4-1.9,4C14.2,12.7,14.9,16.2,14.9,16.2z M16,19c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2 c1.1,0,2-0.9,2-2C18,19.9,17.1,19,16,19z" />
  </svg>
</svg>
PK
!<���Fchrome/browser/skin/classic/browser/addons/addon-install-installed.svg<?xml version="1.0" encoding="utf-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 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
     width="64" height="64" viewBox="0 0 64 64">
  <defs>
    <style>
      .style-puzzle-piece {
        fill: url('#gradient-linear-puzzle-piece');
      }
      .style-badge-shadow {
        fill: #0d131a;
        fill-opacity: .15;
      }
      .style-badge-background {
        fill: #fff;
      }
      .style-badge-inside {
        fill: #55cc3d;
      }
      .style-badge-icon {
        fill: #fff;
      }
    </style>
    <linearGradient id="gradient-linear-puzzle-piece" x1="0%" y1="0%" x2="0%" y2="100%">
      <stop offset="0%" stop-color="#66cc52" stop-opacity="1"/>
      <stop offset="100%" stop-color="#60bf4c" stop-opacity="1"/>
    </linearGradient>
  </defs>
  <path class="style-puzzle-piece" d="M42,62c2.2,0,4-1.8,4-4l0-14.2c0,0,0.4-3.7,2.8-3.7c2.4,0,2.2,3.9,6.7,3.9c2.3,0,6.2-1.2,6.2-8.2 c0-7-3.9-7.9-6.2-7.9c-4.5,0-4.3,3.7-6.7,3.7c-2.4,0-2.8-3.8-2.8-3.8V22c0-2.2-1.8-4-4-4H31.5c0,0-3.4-0.6-3.4-3 c0-2.4,3.8-2.6,3.8-7.1c0-2.3-1.3-5.9-8.3-5.9s-8,3.6-8,5.9c0,4.5,3.4,4.7,3.4,7.1c0,2.4-3.4,3-3.4,3H6c-2.2,0-4,1.8-4,4l0,7.8 c0,0-0.4,6,4.4,6c3.1,0,3.2-4.1,7.3-4.1c2,0,4,1.9,4,6c0,4.2-2,6.3-4,6.3c-4,0-4.2-4.1-7.3-4.1c-4.8,0-4.4,5.8-4.4,5.8L2,58 c0,2.2,1.8,4,4,4H19c0,0,6.3,0.4,6.3-4.4c0-3.1-4-3.6-4-7.7c0-2,2.2-4.5,6.4-4.5c4.2,0,6.6,2.5,6.6,4.5c0,4-3.9,4.6-3.9,7.7 c0,4.9,6.3,4.4,6.3,4.4H42z"/>
  <svg width="32" height="32" x="32" y="0">
    <ellipse class="style-badge-shadow"     rx="14" ry="15" cx="16" cy="17" />
    <circle  class="style-badge-background" r="15"  cy="15" cx="16" />
    <circle  class="style-badge-inside"     r="12"  cy="15" cx="16" />
    <path    class="style-badge-icon" d="M22.8,12.3c0,0-6.7,8.1-6.9,8.3c-0.4,0.5-1.5,0.3-1.7,0 c-0.2-0.3-5-5.8-5-5.8c-0.3-0.3-0.3-0.7,0-1l1-1c0.4-0.4,0.9,0,1.2,0.3c0.3,0.4,3.4,3.8,3.4,3.8s5.2-6.1,5.4-6.4 c0.5-0.8,1.6-0.8,1.9-0.5l0.7,0.6C23.1,11.1,23.1,12,22.8,12.3z" />
  </svg>
</svg>
PK
!<,�X

Dchrome/browser/skin/classic/browser/addons/addon-install-restart.svg<?xml version="1.0" encoding="utf-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 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
     width="64" height="64" viewBox="0 0 64 64">
  <defs>
    <style>
      .style-puzzle-piece {
        fill: url('#gradient-linear-puzzle-piece');
        fill-opacity: .25;
      }
      .style-puzzle-piece-outline {
        fill: none;
        stroke-width: 2;
        stroke: #52b33e;
        stroke-dasharray: 4 2;
      }
      .style-badge-shadow {
        fill: #0d131a;
        fill-opacity: .15;
      }
      .style-badge-background {
        fill: #fff;
      }
      .style-badge-inside {
        fill: #00a1e5;
      }
      .style-badge-icon {
        fill: #fff;
      }
    </style>
    <linearGradient id="gradient-linear-puzzle-piece" x1="0%" y1="0%" x2="0%" y2="100%">
      <stop offset="0%" stop-color="#66cc52" stop-opacity="1"/>
      <stop offset="100%" stop-color="#60bf4c" stop-opacity="1"/>
    </linearGradient>
  </defs>
  <path class="style-puzzle-piece" d="M42,62c2.2,0,4-1.8,4-4l0-14.2c0,0,0.4-3.7,2.8-3.7c2.4,0,2.2,3.9,6.7,3.9c2.3,0,6.2-1.2,6.2-8.2 c0-7-3.9-7.9-6.2-7.9c-4.5,0-4.3,3.7-6.7,3.7c-2.4,0-2.8-3.8-2.8-3.8V22c0-2.2-1.8-4-4-4H31.5c0,0-3.4-0.6-3.4-3 c0-2.4,3.8-2.6,3.8-7.1c0-2.3-1.3-5.9-8.3-5.9s-8,3.6-8,5.9c0,4.5,3.4,4.7,3.4,7.1c0,2.4-3.4,3-3.4,3H6c-2.2,0-4,1.8-4,4l0,7.8 c0,0-0.4,6,4.4,6c3.1,0,3.2-4.1,7.3-4.1c2,0,4,1.9,4,6c0,4.2-2,6.3-4,6.3c-4,0-4.2-4.1-7.3-4.1c-4.8,0-4.4,5.8-4.4,5.8L2,58 c0,2.2,1.8,4,4,4H19c0,0,6.3,0.4,6.3-4.4c0-3.1-4-3.6-4-7.7c0-2,2.2-4.5,6.4-4.5c4.2,0,6.6,2.5,6.6,4.5c0,4-3.9,4.6-3.9,7.7 c0,4.9,6.3,4.4,6.3,4.4H42z"/>
  <path class="style-puzzle-piece-outline" d="M23.6,3c6.3,0,7.3,3,7.3,4.9c0,2.2-1,3.1-2,4c-0.8,0.8-1.8,1.6-1.8,3.1c0,2.6,2.7,3.7,4.3,4l0.1,0h0.1H42 c1.7,0,3,1.4,3,3v5.8v0l0,0c0.2,1.7,1.2,4.7,3.8,4.7c1.5,0,2.3-0.9,3-1.8c0.8-1,1.6-1.9,3.7-1.9c3.5,0,5.2,2.2,5.2,6.9 c0,6.2-3.2,7.2-5.2,7.2c-2.1,0-2.9-1-3.7-2c-0.7-0.9-1.5-1.9-3-1.9c-2.6,0-3.6,2.9-3.8,4.6l0,0l0,0L45,58c0,1.6-1.3,3-3,3h-5.2l0,0 l0,0c0,0-0.1,0-0.3,0c-4.5,0-4.9-2.4-4.9-3.4c0-1,0.5-1.6,1.5-2.6c1.1-1.1,2.4-2.5,2.4-5.1c0-3.3-3.9-5.5-7.6-5.5 c-4.6,0-7.4,2.8-7.4,5.5c0,2.6,1.4,4,2.5,5.1c1,1,1.5,1.6,1.5,2.6c0,3.1-3.4,3.4-4.9,3.4c-0.2,0-0.3,0-0.3,0l0,0h0H6 c-1.6,0-3-1.3-3-3l0-12.2l0,0l0,0c0,0-0.1-2.5,1.1-3.9c0.6-0.6,1.3-0.9,2.3-0.9c0.9,0,1.5,0.5,2.3,1.5c1,1.2,2.3,2.6,4.9,2.6 c3.3,0,5-3.6,5-7.3c0-3.4-1.6-7-5-7c-2.6,0-3.9,1.4-4.9,2.6c-0.9,1-1.4,1.5-2.3,1.5c-1,0-1.7-0.3-2.3-0.9C2.8,32.6,3,29.9,3,29.9 l0,0l0,0L3,22c0-1.7,1.3-3,3-3h9.7h0.1l0.1,0c1.6-0.3,4.3-1.4,4.3-4c0-1.4-0.9-2.3-1.6-3.1c-0.9-1-1.8-1.9-1.8-4.1 C16.6,4.6,18.9,3,23.6,3"/>
  <svg width="32" height="32" x="32" y="0">
    <ellipse class="style-badge-shadow"     rx="14" ry="15" cx="16" cy="17" />
    <circle  class="style-badge-background" r="15"  cy="15" cx="16" />
    <circle  class="style-badge-inside"     r="12"  cy="15" cx="16" />
    <path    class="style-badge-icon" d="M21,15h-6l2.4-2.4c-0.6-0.4-1.2-0.6-1.9-0.6c-2,0-3.5,1.6-3.5,3.5 c0,2,1.6,3.5,3.5,3.5c1,0,2-0.5,2.6-1.2l1.7,1c-1,1.3-2.6,2.1-4.3,2.1c-3,0-5.5-2.5-5.5-5.5c0-3,2.5-5.5,5.5-5.5 c1.3,0,2.4,0.4,3.3,1.2L21,9V15z"/>
  </svg>
</svg>
PK
!<�1�x�	�	Dchrome/browser/skin/classic/browser/addons/addon-install-warning.svg<?xml version="1.0" encoding="utf-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 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
     width="64" height="64" viewBox="0 0 64 64">
  <defs>
    <style>
      .style-puzzle-piece {
        fill: url('#gradient-linear-puzzle-piece');
      }
      .style-badge-shadow {
        fill: #0d131a;
        fill-opacity: .15;
      }
      .style-badge-background {
        fill: #fff;
      }
      .style-badge-inside {
        fill: #ffcd02;
      }
      .style-badge-icon {
        fill: #fff;
      }
    </style>
    <linearGradient id="gradient-linear-puzzle-piece" x1="0%" y1="0%" x2="0%" y2="100%">
      <stop offset="0%" stop-color="#999999" stop-opacity="1"/>
      <stop offset="100%" stop-color="#8c8c8c" stop-opacity="1"/>
    </linearGradient>
  </defs>
  <path class="style-puzzle-piece" d="M42,62c2.2,0,4-1.8,4-4l0-14.2c0,0,0.4-3.7,2.8-3.7c2.4,0,2.2,3.9,6.7,3.9c2.3,0,6.2-1.2,6.2-8.2 c0-7-3.9-7.9-6.2-7.9c-4.5,0-4.3,3.7-6.7,3.7c-2.4,0-2.8-3.8-2.8-3.8V22c0-2.2-1.8-4-4-4H31.5c0,0-3.4-0.6-3.4-3 c0-2.4,3.8-2.6,3.8-7.1c0-2.3-1.3-5.9-8.3-5.9s-8,3.6-8,5.9c0,4.5,3.4,4.7,3.4,7.1c0,2.4-3.4,3-3.4,3H6c-2.2,0-4,1.8-4,4l0,7.8 c0,0-0.4,6,4.4,6c3.1,0,3.2-4.1,7.3-4.1c2,0,4,1.9,4,6c0,4.2-2,6.3-4,6.3c-4,0-4.2-4.1-7.3-4.1c-4.8,0-4.4,5.8-4.4,5.8L2,58 c0,2.2,1.8,4,4,4H19c0,0,6.3,0.4,6.3-4.4c0-3.1-4-3.6-4-7.7c0-2,2.2-4.5,6.4-4.5c4.2,0,6.6,2.5,6.6,4.5c0,4-3.9,4.6-3.9,7.7 c0,4.9,6.3,4.4,6.3,4.4H42z"/>
  <svg width="32" height="32" x="32" y="0">
    <path class="style-badge-shadow" d="M29.5,25.8L18.7,4c-0.6-1.2-1.6-2-2.7-2c-1.1,0-2.1,0.7-2.7,2L2.5,25.8 c-0.6,1.2-0.6,2.5-0.1,3.6C2.9,30.4,4,31,5.2,31h21.6c1.2,0,2.3-0.6,2.8-1.6C30.2,28.4,30.1,27.1,29.5,25.8z" />
    <path class="style-badge-background" d="M16,0c-1.7,0-3.2,1-4.1,2.7L1.7,21.9c-0.9,1.7-0.9,3.4,0,4.8C2.5,28.2,4.1,29,5.9,29H26 c1.9,0,3.4-0.8,4.3-2.2c0.9-1.4,0.8-3.2,0-4.8L20.1,2.7C19.2,1,17.7,0,16,0L16,0z" />
    <path class="style-badge-inside" d="M5.9,26c-1.7,0-2.4-1.2-1.6-2.7L14.6,4.1c0.8-1.5,2.1-1.5,2.8,0l10.3,19.3 c0.8,1.5,0.1,2.7-1.6,2.7H5.9z" />
    <path class="style-badge-icon" d="M14.9,17.6c0,0,0.1,0.7,1.1,0.7c1,0,1.1-0.7,1.1-0.7 s0.7-2.9,0.8-4.2c0.1-1.3,0.5-3.2-1.9-3.2c-2.4,0-2,1.9-1.9,3.2C14.2,14.8,14.9,17.6,14.9,17.6z M16,20c-1.1,0-2,0.9-2,2 c0,1.1,0.9,2,2,2c1.1,0,2-0.9,2-2C18,20.9,17.1,20,16,20z" />
  </svg>
</svg>
PK
!<�ee/chrome/browser/skin/classic/browser/back-12.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="12" height="12" viewBox="0 0 12 12">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M 4.748 6 L 7.966 2.781 C 8.367 2.365 8.169 1.672 7.609 1.532 C 7.358 1.47 7.092 1.54 6.906 1.72 L 3.158 5.47 C 2.865 5.762 2.865 6.237 3.158 6.53 L 6.906 10.279 C 7.321 10.68 8.015 10.481 8.155 9.921 C 8.217 9.67 8.146 9.405 7.966 9.219 Z"/>
</svg>
PK
!<�����3chrome/browser/skin/classic/browser/blockedSite.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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://browser/skin/error-pages.css");

html {
  background-color: #A4000F;
}

body {
  color: white;
}

.title {
  background-image: url("chrome://global/skin/icons/blocked.svg");
  fill: currentColor;
  -moz-context-properties: fill;
}

.title-text {
  color: white;
}

.button-container button {
  background-color: transparent;
  color: white;
  border: 1px solid white;
  margin-inline-end: 0;
  margin-top: 1.5em;
}

.button-container button:hover {
  background-color: #5a0002;
}

.button-container button:hover:active {
  background-color: #3e0200;
}

#goBackButton {
  color: black;
  background-color: white;
}

#goBackButton:hover {
  background-color: white;
  opacity: 0.8;
}

#goBackButton:active {
  background-color: white;
  opacity: 0.7;
}

#advisory_provider {
  color: white;
  text-decoration: underline;
}

#errorDescriptionContainer {
  position: absolute;
  margin: 48px auto;
}

.error-description {
  min-width: var(--in-content-container-min-width);
  max-width: var(--in-content-container-max-width);
  color: black;
  background-color: white;
}

.error-description > p:first-child {
  padding: 3.5em 3.5em 1em 3.5em;
}

.error-description > p:last-child {
  padding: 0 3.5em 3.5em 3.5em;
}

.error-description #ignore_warning_link,
.error-description a:-moz-any(:link, :visited) {
  cursor: pointer;
  text-decoration: underline;
  color: black;
}

a:not(:link) {
  color: black;
  text-decoration: none;
  cursor: auto;
}

.sitename {
  font-weight: bold;
}
PK
!<:f�[����:chrome/browser/skin/classic/browser/bookmark-animation.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="660" height="33">
  <svg>
    <defs>
      <mask id="b" mask-type="alpha">
        <path fill="context-stroke" d="M0 0h320v240h-320z" transform="matrix(0 0 0 0 16.352 17.593)"/>
      </mask>
      <mask id="a" mask-type="alpha">
        <path fill="context-stroke" d="M0 0h320v240h-320z" transform="matrix(0 0 0 0 16.463 17.517)"/>
      </mask>
    </defs>
    <path fill="none" stroke="context-fill" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.584" d="M16.457 9.307l1.71 3.363 0.722 1.422 1.57 0.267 3.653 0.624 -2.66 2.897 -1.03 1.12 0.23 1.506 0.587 3.824 -3.32 -1.762 -1.496 -0.792 -1.49 0.8 -3.225 1.73 0.584 -3.798 0.233 -1.514 -1.043 -1.124 -2.676 -2.882 3.718 -0.624 1.613 -0.272 0.713 -1.472 1.606 -3.31"/>
    <g mask="url(#a)">
      <path fill="#FFF" d="M16.456 9.271l1.716 3.376 0.725 1.427 1.577 0.269 3.666 0.626 -2.67 2.907 -1.033 1.125 0.23 1.512 0.589 3.838 -3.333 -1.768 -1.5 -0.796 -1.496 0.803 -3.238 1.737 0.586 -3.813 0.234 -1.52 -1.047 -1.128 -2.686 -2.892 3.732 -0.627 1.619 -0.273 0.716 -1.477 1.612 -3.324"/>
      <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.59" d="M16.456 9.271l1.716 3.376 0.725 1.427 1.577 0.269 3.666 0.626 -2.67 2.907 -1.033 1.125 0.23 1.512 0.589 3.838 -3.333 -1.768 -1.5 -0.796 -1.496 0.803 -3.238 1.737 0.586 -3.813 0.234 -1.52 -1.047 -1.128 -2.686 -2.892 3.732 -0.627 1.619 -0.273 0.716 -1.477 1.612 -3.324"/>
    </g>
    <g mask="url(#b)" opacity=".08">
      <path fill="context-fill" fill-opacity="context-fill-opacity" d="M16.502 10.083l2.255 4.57 5.044 0.734 -3.65 3.557 0.862 5.023 -4.511 -2.371 -4.512 2.371 0.862 -5.023 -3.65 -3.557 5.044 -0.733 2.256 -4.57z"/>
      <path fill="none" d="M16.502 10.083l2.255 4.57 5.044 0.734 -3.65 3.557 0.862 5.023 -4.511 -2.371 -4.512 2.371 0.862 -5.023 -3.65 -3.557 5.044 -0.733 2.256 -4.57z"/>
    </g>
  </svg>
  <svg x="33">
    <defs>
      <mask id="d" mask-type="alpha">
        <path fill="context-stroke" d="M0 0h320v240h-320z" transform="matrix(.04 0 0 .04 9.957 11.594)"/>
      </mask>
      <mask id="c" mask-type="alpha">
        <path fill="context-stroke" d="M0 0h320v240h-320z" transform="matrix(.04 0 0 .04 10.127 12.218)"/>
      </mask>
    </defs>
    <path fill="none" stroke="context-fill" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.584" d="M16.457 9.307l1.71 3.363 0.722 1.422 1.57 0.267 3.653 0.624 -2.66 2.897 -1.03 1.12 0.23 1.506 0.587 3.824 -3.32 -1.762 -1.496 -0.792 -1.49 0.8 -3.225 1.73 0.584 -3.798 0.233 -1.514 -1.043 -1.124 -2.676 -2.882 3.718 -0.624 1.613 -0.272 0.713 -1.472 1.606 -3.31"/>
    <g mask="url(#c)">
      <path fill="#FFF" d="M16.457 9.283l1.712 3.37 0.724 1.425 1.575 0.268 3.66 0.626 -2.665 2.902 -1.032 1.124 0.23 1.509 0.588 3.831 -3.328 -1.765 -1.498 -0.794 -1.493 0.802 -3.233 1.734 0.585 -3.807 0.233 -1.517 -1.045 -1.127 -2.681 -2.887 3.726 -0.626 1.616 -0.272 0.715 -1.476 1.609 -3.318"/>
      <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.587" d="M16.457 9.283l1.712 3.37 0.724 1.425 1.575 0.268 3.66 0.626 -2.665 2.902 -1.032 1.124 0.23 1.509 0.588 3.831 -3.328 -1.765 -1.498 -0.794 -1.493 0.802 -3.233 1.734 0.585 -3.807 0.233 -1.517 -1.045 -1.127 -2.681 -2.887 3.726 -0.626 1.616 -0.272 0.715 -1.476 1.609 -3.318"/>
    </g>
    <g mask="url(#d)" opacity=".08">
      <path fill="context-stroke" d="M16.502 10.094l2.252 4.563 5.036 0.732 -3.644 3.552 0.86 5.015 -4.504 -2.368 -4.504 2.368 0.86 -5.015 -3.644 -3.552 5.036 -0.732 2.252 -4.563z"/>
      <path fill="none" d="M16.502 10.094l2.252 4.563 5.036 0.732 -3.644 3.552 0.86 5.015 -4.504 -2.368 -4.504 2.368 0.86 -5.015 -3.644 -3.552 5.036 -0.732 2.252 -4.563z"/>
    </g>
  </svg>
  <svg x="66">
    <defs>
      <mask id="f" mask-type="alpha">
        <path fill="context-stroke" d="M0 0h320v240h-320z" transform="matrix(.08 0 0 .08 3.562 5.594)"/>
      </mask>
      <mask id="e" mask-type="alpha">
        <path fill="context-stroke" d="M0 0h320v240h-320z" transform="matrix(.08 0 0 .08 3.791 6.92)"/>
      </mask>
    </defs>
    <path fill="none" stroke="context-fill" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.584" d="M16.457 9.307l1.71 3.363 0.722 1.422 1.57 0.267 3.653 0.624 -2.66 2.897 -1.03 1.12 0.23 1.506 0.587 3.824 -3.32 -1.762 -1.496 -0.792 -1.49 0.8 -3.225 1.73 0.584 -3.798 0.233 -1.514 -1.043 -1.124 -2.676 -2.882 3.718 -0.624 1.613 -0.272 0.713 -1.472 1.606 -3.31"/>
    <g mask="url(#e)">
      <path fill="#FFF" d="M16.457 9.308l1.708 3.359 0.721 1.42 1.569 0.267 3.649 0.624 -2.657 2.892 -1.029 1.12 0.23 1.504 0.585 3.819 -3.316 -1.76 -1.493 -0.791 -1.488 0.799 -3.223 1.729 0.584 -3.794 0.232 -1.513 -1.041 -1.123 -2.673 -2.878 3.714 -0.624 1.61 -0.27 0.713 -1.471 1.604 -3.307"/>
      <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.582" d="M16.457 9.308l1.708 3.359 0.721 1.42 1.569 0.267 3.649 0.624 -2.657 2.892 -1.029 1.12 0.23 1.504 0.585 3.819 -3.316 -1.76 -1.493 -0.791 -1.488 0.799 -3.223 1.729 0.584 -3.794 0.232 -1.513 -1.041 -1.123 -2.673 -2.878 3.714 -0.624 1.61 -0.27 0.713 -1.471 1.604 -3.307"/>
    </g>
    <g mask="url(#f)" opacity=".387">
      <path fill="context-stroke" d="M16.502 10.116l2.245 4.548 5.019 0.729 -3.632 3.54 0.858 4.999 -4.49 -2.36 -4.489 2.36 0.858 -4.999 -3.632 -3.54 5.019 -0.73 2.244 -4.547z"/>
      <path fill="none" d="M16.502 10.116l2.245 4.548 5.019 0.729 -3.632 3.54 0.858 4.999 -4.49 -2.36 -4.489 2.36 0.858 -4.999 -3.632 -3.54 5.019 -0.73 2.244 -4.547z"/>
    </g>
  </svg>
  <svg x="99">
    <defs>
      <mask id="h" mask-type="alpha">
        <path fill="context-stroke" d="M0 0h320v240h-320z" transform="matrix(.12 0 0 .12 -2.833 -.405)"/>
      </mask>
      <mask id="g" mask-type="alpha">
        <path fill="context-stroke" d="M0 0h320v240h-320z" transform="matrix(.12 0 0 .12 -2.545 1.62)"/>
      </mask>
    </defs>
    <path fill="none" stroke="context-fill" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.584" d="M16.457 9.307l1.71 3.363 0.722 1.422 1.57 0.267 3.653 0.624 -2.66 2.897 -1.03 1.12 0.23 1.506 0.587 3.824 -3.32 -1.762 -1.496 -0.792 -1.49 0.8 -3.225 1.73 0.584 -3.798 0.233 -1.514 -1.043 -1.124 -2.676 -2.882 3.718 -0.624 1.613 -0.272 0.713 -1.472 1.606 -3.31"/>
    <g mask="url(#g)">
      <path fill="#FFF" d="M16.458 9.336l1.7 3.347 0.72 1.414 1.562 0.266 3.635 0.621 -2.646 2.882 -1.025 1.115 0.229 1.498 0.583 3.804 -3.304 -1.752 -1.487 -0.789 -1.482 0.796 -3.21 1.722 0.58 -3.78 0.233 -1.506 -1.038 -1.118 -2.662 -2.867 3.699 -0.622 1.604 -0.27 0.71 -1.464 1.598 -3.295"/>
      <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.576" d="M16.458 9.336l1.7 3.347 0.72 1.414 1.562 0.266 3.635 0.621 -2.646 2.882 -1.025 1.115 0.229 1.498 0.583 3.804 -3.304 -1.752 -1.487 -0.789 -1.482 0.796 -3.21 1.722 0.58 -3.78 0.233 -1.506 -1.038 -1.118 -2.662 -2.867 3.699 -0.622 1.604 -0.27 0.71 -1.464 1.598 -3.295"/>
    </g>
    <g mask="url(#h)" opacity=".693">
      <path fill="context-stroke" d="M16.503 10.141l2.236 4.53 5 0.727 -3.618 3.526 0.854 4.98 -4.472 -2.351 -4.471 2.35 0.854 -4.979 -3.618 -3.526 5 -0.726 2.235 -4.53z"/>
      <path fill="none" d="M16.503 10.141l2.236 4.53 5 0.727 -3.618 3.526 0.854 4.98 -4.472 -2.351 -4.471 2.35 0.854 -4.979 -3.618 -3.526 5 -0.726 2.235 -4.53z"/>
    </g>
  </svg>
  <svg x="132">
    <defs>
      <mask id="j" mask-type="alpha">
        <path fill="context-stroke" d="M0 0h320v240h-320z" transform="matrix(.16 0 0 .16 -9.228 -6.404)"/>
      </mask>
      <mask id="i" mask-type="alpha">
        <path fill="context-stroke" d="M0 0h320v240h-320z" transform="matrix(.16 0 0 .16 -8.88 -3.678)"/>
      </mask>
    </defs>
    <g mask="url(#i)">
      <path fill="#FFF" d="M16.459 9.355l1.696 3.338 0.717 1.41 1.559 0.266 3.625 0.62 -2.64 2.874 -1.022 1.112 0.229 1.495 0.581 3.794 -3.295 -1.748 -1.484 -0.787 -1.478 0.794 -3.201 1.718 0.58 -3.77 0.23 -1.503 -1.035 -1.115 -2.655 -2.86 3.69 -0.62 1.6 -0.269 0.708 -1.46 1.593 -3.287"/>
      <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.572" d="M16.459 9.355l1.696 3.338 0.717 1.41 1.559 0.266 3.625 0.62 -2.64 2.874 -1.022 1.112 0.229 1.495 0.581 3.794 -3.295 -1.748 -1.484 -0.787 -1.478 0.794 -3.201 1.718 0.58 -3.77 0.23 -1.503 -1.035 -1.115 -2.655 -2.86 3.69 -0.62 1.6 -0.269 0.708 -1.46 1.593 -3.287"/>
    </g>
    <g mask="url(#j)">
      <path fill="context-stroke" d="M16.504 10.158l2.23 4.519 4.986 0.724 -3.608 3.518 0.852 4.966 -4.46 -2.345 -4.46 2.345 0.851 -4.966 -3.608 -3.518 4.987 -0.724 2.23 -4.519z"/>
      <path fill="none" d="M16.504 10.158l2.23 4.519 4.986 0.724 -3.608 3.518 0.852 4.966 -4.46 -2.345 -4.46 2.345 0.851 -4.966 -3.608 -3.518 4.987 -0.724 2.23 -4.519z"/>
    </g>
  </svg>
  <svg x="165">
    <defs>
      <mask id="l" mask-type="alpha">
        <path fill="context-stroke" d="M0 0h320v240h-320z" transform="matrix(.2 0 0 .2 -15.623 -12.404)"/>
      </mask>
      <mask id="k" mask-type="alpha">
        <path fill="context-stroke" d="M0 0h320v240h-320z" transform="matrix(.2 0 0 .2 -15.216 -8.977)"/>
      </mask>
    </defs>
    <g mask="url(#k)">
      <path fill="#FFF" d="M16.46 9.4l1.686 3.318 0.712 1.402 1.55 0.264 3.602 0.615 -2.623 2.856 -1.016 1.106 0.227 1.485 0.578 3.77 -3.275 -1.737 -1.474 -0.781 -1.469 0.789 -3.181 1.707 0.575 -3.746 0.23 -1.494 -1.028 -1.109 -2.639 -2.841 3.667 -0.616 1.59 -0.268 0.703 -1.452 1.584 -3.265"/>
      <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.562" d="M16.46 9.4l1.686 3.318 0.712 1.402 1.55 0.264 3.602 0.615 -2.623 2.856 -1.016 1.106 0.227 1.485 0.578 3.77 -3.275 -1.737 -1.474 -0.781 -1.469 0.789 -3.181 1.707 0.575 -3.746 0.23 -1.494 -1.028 -1.109 -2.639 -2.841 3.667 -0.616 1.59 -0.268 0.703 -1.452 1.584 -3.265"/>
    </g>
    <g mask="url(#l)">
      <path fill="context-stroke" d="M16.505 10.199l2.216 4.49 4.956 0.72 -3.586 3.496 0.846 4.935 -4.432 -2.33 -4.432 2.33 0.846 -4.935 -3.586 -3.496 4.956 -0.72 2.216 -4.49z"/>
      <path fill="none" d="M16.505 10.199l2.216 4.49 4.956 0.72 -3.586 3.496 0.846 4.935 -4.432 -2.33 -4.432 2.33 0.846 -4.935 -3.586 -3.496 4.956 -0.72 2.216 -4.49z"/>
    </g>
  </svg>
  <svg x="198">
    <defs>
      <mask id="n" mask-type="alpha">
        <path fill="context-stroke" d="M0 0h320v240h-320z" transform="matrix(.2 0 0 .2 -15.623 -12.404)"/>
      </mask>
      <mask id="m" mask-type="alpha">
        <path fill="context-stroke" d="M0 0h320v240h-320z" transform="matrix(.2 0 0 .2 -15.216 -8.977)"/>
      </mask>
    </defs>
    <g mask="url(#m)">
      <path fill="#FFF" d="M16.457 9.299l1.71 3.363 0.722 1.422 1.57 0.267 3.653 0.625 -2.66 2.896 -1.03 1.12 0.23 1.506 0.587 3.824 -3.32 -1.762 -1.496 -0.792 -1.49 0.8 -3.225 1.73 0.584 -3.798 0.233 -1.514 -1.043 -1.124 -2.676 -2.882 3.718 -0.624 1.613 -0.272 0.713 -1.472 1.606 -3.31"/>
      <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.584" d="M16.457 9.299l1.71 3.363 0.722 1.422 1.57 0.267 3.653 0.625 -2.66 2.896 -1.03 1.12 0.23 1.506 0.587 3.824 -3.32 -1.762 -1.496 -0.792 -1.49 0.8 -3.225 1.73 0.584 -3.798 0.233 -1.514 -1.043 -1.124 -2.676 -2.882 3.718 -0.624 1.613 -0.272 0.713 -1.472 1.606 -3.31"/>
    </g>
    <g mask="url(#n)">
      <path fill="context-stroke" d="M16.502 10.108l2.247 4.553 5.025 0.73 -3.636 3.545 0.858 5.004 -4.494 -2.363 -4.494 2.363 0.858 -5.004 -3.636 -3.545 5.025 -0.73 2.247 -4.553z"/>
      <path fill="none" d="M16.502 10.108l2.247 4.553 5.025 0.73 -3.636 3.545 0.858 5.004 -4.494 -2.363 -4.494 2.363 0.858 -5.004 -3.636 -3.545 5.025 -0.73 2.247 -4.553z"/>
    </g>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M4.273 14.274l-0.235 -0.115"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M4.273 14.274l-0.235 -0.115"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M28.738 14.306l0.234 -0.115"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M28.738 14.306l0.234 -0.115"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M24.168 28.806l0.168 0.2"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M24.168 28.806l0.168 0.2"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M8.816 28.826l-0.168 0.2"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M8.816 28.826l-0.168 0.2"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M16.559 4.97v-0.26"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M16.559 4.97v-0.26"/>
  </svg>
  <svg x="231">
    <defs>
      <mask id="p" mask-type="alpha">
        <path fill="context-stroke" d="M0 0h320v240h-320z" transform="matrix(.2 0 0 .2 -15.623 -12.404)"/>
      </mask>
      <mask id="o" mask-type="alpha">
        <path fill="context-stroke" d="M0 0h320v240h-320z" transform="matrix(.2 0 0 .2 -15.216 -8.977)"/>
      </mask>
    </defs>
    <g mask="url(#o)">
      <path fill="#FFF" d="M16.448 9.017l1.774 3.492 0.75 1.475 1.63 0.278 3.792 0.648 -2.76 3.007 -1.07 1.163 0.24 1.563 0.608 3.97 -3.447 -1.83 -1.552 -0.822 -1.546 0.83 -3.35 1.797 0.607 -3.943 0.241 -1.572 -1.082 -1.167 -2.777 -2.99 3.859 -0.65 1.674 -0.281 0.74 -1.528 1.667 -3.437"/>
      <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.644" d="M16.448 9.017l1.774 3.492 0.75 1.475 1.63 0.278 3.792 0.648 -2.76 3.007 -1.07 1.163 0.24 1.563 0.608 3.97 -3.447 -1.83 -1.552 -0.822 -1.546 0.83 -3.35 1.797 0.607 -3.943 0.241 -1.572 -1.082 -1.167 -2.777 -2.99 3.859 -0.65 1.674 -0.281 0.74 -1.528 1.667 -3.437"/>
    </g>
    <g mask="url(#p)">
      <path fill="context-stroke" d="M16.495 9.857l2.332 4.727 5.217 0.758 -3.775 3.68 0.891 5.194 -4.665 -2.453 -4.666 2.453 0.891 -5.195 -3.774 -3.68 5.216 -0.757 2.333 -4.727z"/>
      <path fill="none" d="M16.495 9.857l2.332 4.727 5.217 0.758 -3.775 3.68 0.891 5.194 -4.665 -2.453 -4.666 2.453 0.891 -5.195 -3.774 -3.68 5.216 -0.757 2.333 -4.727z"/>
    </g>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M4.273 14.274l-0.772 -0.377"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M4.273 14.274l-0.772 -0.377"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M28.738 14.306s0.33 -0.16 0.772 -0.377"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M28.738 14.306s0.33 -0.16 0.772 -0.377"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M24.168 28.806l0.552 0.658"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M24.168 28.806l0.552 0.658"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M8.816 28.826l-0.552 0.657"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M8.816 28.826l-0.552 0.657"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M16.559 4.97v-0.859"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M16.559 4.97v-0.859"/>
  </svg>
  <svg x="264">
    <defs>
      <mask id="r" mask-type="alpha">
        <path fill="context-stroke" d="M0 0h320v240h-320z" transform="matrix(.2 0 0 .2 -15.623 -12.404)"/>
      </mask>
      <mask id="q" mask-type="alpha">
        <path fill="context-stroke" d="M0 0h320v240h-320z" transform="matrix(.2 0 0 .2 -15.216 -8.977)"/>
      </mask>
    </defs>
    <g mask="url(#q)">
      <path fill="#FFF" d="M16.437 8.692l1.85 3.64 0.781 1.538 1.7 0.29 3.953 0.675 -2.878 3.133 -1.115 1.213 0.25 1.63 0.633 4.137 -3.593 -1.906 -1.617 -0.858 -1.612 0.866 -3.491 1.873 0.632 -4.11 0.252 -1.639 -1.129 -1.216 -2.895 -3.118 4.023 -0.676 1.745 -0.294 0.772 -1.593 1.737 -3.583"/>
      <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.714" d="M16.437 8.692l1.85 3.64 0.781 1.538 1.7 0.29 3.953 0.675 -2.878 3.133 -1.115 1.213 0.25 1.63 0.633 4.137 -3.593 -1.906 -1.617 -0.858 -1.612 0.866 -3.491 1.873 0.632 -4.11 0.252 -1.639 -1.129 -1.216 -2.895 -3.118 4.023 -0.676 1.745 -0.294 0.772 -1.593 1.737 -3.583"/>
    </g>
    <g mask="url(#r)">
      <path fill="context-stroke" d="M16.486 9.568l2.432 4.927 5.437 0.79 -3.935 3.835 0.93 5.415 -4.864 -2.557 -4.863 2.557 0.929 -5.415 -3.935 -3.835 5.437 -0.79 2.432 -4.927z"/>
      <path fill="none" d="M16.486 9.568l2.432 4.927 5.437 0.79 -3.935 3.835 0.93 5.415 -4.864 -2.557 -4.863 2.557 0.929 -5.415 -3.935 -3.835 5.437 -0.79 2.432 -4.927z"/>
    </g>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M4.273 14.274l-1.48 -0.722"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M4.273 14.274l-1.48 -0.722"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M28.738 14.306l1.48 -0.722"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M28.738 14.306l1.48 -0.722"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M24.168 28.806l1.058 1.26"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M24.168 28.806l1.058 1.26"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M8.816 28.826l-1.059 1.26"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M8.816 28.826l-1.059 1.26"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M16.559 4.97v-1.646"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M16.559 4.97v-1.646"/>
  </svg>
  <svg x="297">
    <defs>
      <mask id="t" mask-type="alpha">
        <path fill="context-stroke" d="M0 0h320v240h-320z" transform="matrix(.2 0 0 .2 -15.623 -12.404)"/>
      </mask>
      <mask id="s" mask-type="alpha">
        <path fill="context-stroke" d="M0 0h320v240h-320z" transform="matrix(.2 0 0 .2 -15.216 -8.977)"/>
      </mask>
    </defs>
    <g mask="url(#s)">
      <path fill="#FFF" d="M16.426 8.351l1.928 3.795 0.815 1.603 1.772 0.302 4.121 0.705 -3 3.267 -1.162 1.264 0.26 1.7 0.66 4.313 -3.746 -1.987 -1.686 -0.895 -1.68 0.903 -3.64 1.952 0.659 -4.285 0.262 -1.708 -1.176 -1.268 -3.018 -3.251 4.194 -0.705 1.819 -0.306 0.805 -1.66 1.811 -3.736"/>
      <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.787" d="M16.426 8.351l1.928 3.795 0.815 1.603 1.772 0.302 4.121 0.705 -3 3.267 -1.162 1.264 0.26 1.7 0.66 4.313 -3.746 -1.987 -1.686 -0.895 -1.68 0.903 -3.64 1.952 0.659 -4.285 0.262 -1.708 -1.176 -1.268 -3.018 -3.251 4.194 -0.705 1.819 -0.306 0.805 -1.66 1.811 -3.736"/>
    </g>
    <g mask="url(#t)">
      <path fill="context-stroke" d="M16.477 9.264l2.535 5.137 5.669 0.824 -4.102 3.998 0.968 5.646 -5.07 -2.666 -5.07 2.666 0.968 -5.646 -4.102 -3.998 5.669 -0.824 2.535 -5.137z"/>
      <path fill="none" d="M16.477 9.264l2.535 5.137 5.669 0.824 -4.102 3.998 0.968 5.646 -5.07 -2.666 -5.07 2.666 0.968 -5.646 -4.102 -3.998 5.669 -0.824 2.535 -5.137z"/>
    </g>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M4.273 14.274l-2.281 -1.113"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M4.273 14.274l-2.281 -1.113"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M28.738 14.306l2.28 -1.112"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M28.738 14.306l2.28 -1.112"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M24.168 28.806l1.631 1.944"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M24.168 28.806l1.631 1.944"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M8.816 28.826l-1.631 1.943"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M8.816 28.826l-1.631 1.943"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M16.559 4.97v-2.537"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M16.559 4.97v-2.537"/>
  </svg>
  <svg x="330">
    <defs>
      <mask id="v" mask-type="alpha">
        <g clip-path="url(#f10_BkV3WrqkR1)" transform="matrix(.2 0 0 .2 -15.623 -12.404)">
          <path fill="context-stroke" d="M0 0h320v240h-320z"/>
        </g>
      </mask>
      <mask id="u" mask-type="alpha">
        <g clip-path="url(#f10_4P5xKEAlF3)" transform="matrix(.2 0 0 .2 -15.216 -8.977)">
          <path fill="context-stroke" d="M0 0h320v240h-320z"/>
        </g>
      </mask>
    </defs>
    <g mask="url(#u)">
      <path fill="#FFF" d="M16.415 8.021l2.005 3.945 0.847 1.667 1.842 0.314 4.284 0.732 -3.119 3.397 -1.208 1.314 0.27 1.766 0.687 4.484 -3.894 -2.065 -1.753 -0.93 -1.747 0.938 -3.784 2.03 0.685 -4.455 0.273 -1.776 -1.223 -1.318 -3.138 -3.38 4.36 -0.732 1.891 -0.318 0.837 -1.727 1.883 -3.883"/>
      <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.858" d="M16.415 8.021l2.005 3.945 0.847 1.667 1.842 0.314 4.284 0.732 -3.119 3.397 -1.208 1.314 0.27 1.766 0.687 4.484 -3.894 -2.065 -1.753 -0.93 -1.747 0.938 -3.784 2.03 0.685 -4.455 0.273 -1.776 -1.223 -1.318 -3.138 -3.38 4.36 -0.732 1.891 -0.318 0.837 -1.727 1.883 -3.883"/>
    </g>
    <g mask="url(#v)">
      <path fill="context-stroke" d="M16.468 8.97l2.636 5.34 5.893 0.857 -4.265 4.156 1.007 5.87 -5.27 -2.771 -5.272 2.77 1.007 -5.869 -4.265 -4.156 5.894 -0.857 2.635 -5.34z"/>
      <path fill="none" d="M16.468 8.97l2.636 5.34 5.893 0.857 -4.265 4.156 1.007 5.87 -5.27 -2.771 -5.272 2.77 1.007 -5.869 -4.265 -4.156 5.894 -0.857 2.635 -5.34z"/>
    </g>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M4.273 14.274l-3.056 -1.49"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M4.273 14.274l-3.056 -1.49"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M28.738 14.306l3.056 -1.49"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M28.738 14.306l3.056 -1.49"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M24.168 28.806l2.186 2.605"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M24.168 28.806l2.186 2.605"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M8.816 28.826l-2.186 2.604"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M8.816 28.826l-2.186 2.604"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M16.559 4.97v-3.4"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M16.559 4.97v-3.4"/>
  </svg>
  <svg x="363">
    <defs>
      <mask id="x" mask-type="alpha">
        <g clip-path="url(#f11_BkV3WrqkR1)" transform="matrix(.2 0 0 .2 -15.623 -12.404)">
          <path fill="context-stroke" d="M0 0h320v240h-320z"/>
        </g>
      </mask>
      <mask id="w" mask-type="alpha">
        <g clip-path="url(#f11_4P5xKEAlF3)" transform="matrix(.2 0 0 .2 -15.216 -8.977)">
          <path fill="context-stroke" d="M0 0h320v240h-320z"/>
        </g>
      </mask>
    </defs>
    <g mask="url(#w)">
      <path fill="#FFF" d="M16.406 7.738l2.07 4.074 0.875 1.721 1.902 0.324 4.424 0.756 -3.221 3.508 -1.248 1.357 0.28 1.824 0.709 4.63 -4.022 -2.133 -1.81 -0.96 -1.804 0.97 -3.907 2.095 0.707 -4.6 0.282 -1.834 -1.263 -1.361 -3.24 -3.49 4.502 -0.757 1.953 -0.328 0.864 -1.783 1.945 -4.01"/>
      <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.918" d="M16.406 7.738l2.07 4.074 0.875 1.721 1.902 0.324 4.424 0.756 -3.221 3.508 -1.248 1.357 0.28 1.824 0.709 4.63 -4.022 -2.133 -1.81 -0.96 -1.804 0.97 -3.907 2.095 0.707 -4.6 0.282 -1.834 -1.263 -1.361 -3.24 -3.49 4.502 -0.757 1.953 -0.328 0.864 -1.783 1.945 -4.01"/>
    </g>
    <g mask="url(#x)">
      <path fill="context-stroke" d="M16.46 8.718l2.722 5.515 6.086 0.884 -4.404 4.292 1.04 6.061 -5.443 -2.861 -5.444 2.861 1.04 -6.06 -4.404 -4.293 6.086 -0.884 2.722 -5.515z"/>
      <path fill="none" d="M16.46 8.718l2.722 5.515 6.086 0.884 -4.404 4.292 1.04 6.061 -5.443 -2.861 -5.444 2.861 1.04 -6.06 -4.404 -4.293 6.086 -0.884 2.722 -5.515z"/>
    </g>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M4.145 14.211l-2.928 -1.428"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M4.145 14.211l-2.928 -1.428"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M28.866 14.243l2.928 -1.428"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M28.866 14.243l2.928 -1.428"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M24.26 28.915l2.094 2.496"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M24.26 28.915l2.094 2.496"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M8.724 28.935l-2.094 2.495"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M8.724 28.935l-2.094 2.495"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M16.559 4.828v-3.258"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M16.559 4.828v-3.258"/>
  </svg>
  <svg x="396">
    <defs>
      <mask id="z" mask-type="alpha">
        <g clip-path="url(#f12_BkV3WrqkR1)" transform="matrix(.2 0 0 .2 -15.623 -12.404)">
          <path fill="context-stroke" d="M0 0h320v240h-320z"/>
        </g>
      </mask>
      <mask id="y" mask-type="alpha">
        <g clip-path="url(#f12_4P5xKEAlF3)" transform="matrix(.2 0 0 .2 -15.216 -8.977)">
          <path fill="context-stroke" d="M0 0h320v240h-320z"/>
        </g>
      </mask>
    </defs>
    <g mask="url(#y)">
      <path fill="#FFF" d="M16.401 7.59l2.104 4.141 0.89 1.75 1.933 0.33 4.497 0.768 -3.274 3.565 -1.269 1.38 0.284 1.854 0.721 4.707 -4.087 -2.169 -1.84 -0.975 -1.834 0.984 -3.972 2.131 0.72 -4.676 0.286 -1.864 -1.284 -1.384 -3.293 -3.547 4.576 -0.77 1.985 -0.333 0.878 -1.812 1.977 -4.077"/>
      <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.95" d="M16.401 7.59l2.104 4.141 0.89 1.75 1.933 0.33 4.497 0.768 -3.274 3.565 -1.269 1.38 0.284 1.854 0.721 4.707 -4.087 -2.169 -1.84 -0.975 -1.834 0.984 -3.972 2.131 0.72 -4.676 0.286 -1.864 -1.284 -1.384 -3.293 -3.547 4.576 -0.77 1.985 -0.333 0.878 -1.812 1.977 -4.077"/>
    </g>
    <g mask="url(#z)">
      <path fill="context-stroke" d="M16.457 8.587l2.766 5.605 6.186 0.899 -4.476 4.363 1.056 6.16 -5.532 -2.908 -5.533 2.909 1.057 -6.161 -4.476 -4.363 6.185 -0.899 2.767 -5.605z"/>
      <path fill="none" d="M16.457 8.587l2.766 5.605 6.186 0.899 -4.476 4.363 1.056 6.16 -5.532 -2.908 -5.533 2.909 1.057 -6.161 -4.476 -4.363 6.185 -0.899 2.767 -5.605z"/>
    </g>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M3.835 14.06l-2.618 -1.277"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M3.835 14.06l-2.618 -1.277"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M29.176 14.092l2.618 -1.277"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M29.176 14.092l2.618 -1.277"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M24.481 29.18l1.873 2.23"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M24.481 29.18l1.873 2.23"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M8.502 29.199l-1.872 2.231"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M8.502 29.199l-1.872 2.231"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M16.559 4.483v-2.913"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M16.559 4.483v-2.913"/>
  </svg>
  <svg x="429">
    <defs>
      <mask id="B" mask-type="alpha">
        <g clip-path="url(#f13_BkV3WrqkR1)" transform="matrix(.2 0 0 .2 -15.623 -12.404)">
          <path fill="context-stroke" d="M0 0h320v240h-320z"/>
        </g>
      </mask>
      <mask id="A" mask-type="alpha">
        <g clip-path="url(#f13_4P5xKEAlF3)" transform="matrix(.2 0 0 .2 -15.216 -8.977)">
          <path fill="context-stroke" d="M0 0h320v240h-320z"/>
        </g>
      </mask>
    </defs>
    <g mask="url(#A)">
      <path fill="#FFF" d="M16.406 7.738l2.07 4.074 0.875 1.721 1.902 0.324 4.424 0.756 -3.221 3.508 -1.248 1.357 0.28 1.824 0.709 4.63 -4.022 -2.133 -1.81 -0.96 -1.804 0.97 -3.907 2.096 0.707 -4.6 0.282 -1.835 -1.263 -1.361 -3.24 -3.49 4.502 -0.757 1.953 -0.328 0.864 -1.783 1.945 -4.01"/>
      <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.918" d="M16.406 7.738l2.07 4.074 0.875 1.721 1.902 0.324 4.424 0.756 -3.221 3.508 -1.248 1.357 0.28 1.824 0.709 4.63 -4.022 -2.133 -1.81 -0.96 -1.804 0.97 -3.907 2.096 0.707 -4.6 0.282 -1.835 -1.263 -1.361 -3.24 -3.49 4.502 -0.757 1.953 -0.328 0.864 -1.783 1.945 -4.01"/>
    </g>
    <g mask="url(#B)">
      <path fill="context-stroke" d="M16.46 8.718l2.722 5.515 6.086 0.884 -4.404 4.292 1.04 6.061 -5.444 -2.861 -5.443 2.861 1.04 -6.06 -4.404 -4.293 6.086 -0.884 2.721 -5.515z"/>
      <path fill="none" d="M16.46 8.718l2.722 5.515 6.086 0.884 -4.404 4.292 1.04 6.061 -5.444 -2.861 -5.443 2.861 1.04 -6.06 -4.404 -4.293 6.086 -0.884 2.721 -5.515z"/>
    </g>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M3.408 13.852l-2.191 -1.069"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M3.408 13.852l-2.191 -1.069"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M29.602 13.884l2.192 -1.069"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M29.602 13.884l2.192 -1.069"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M24.786 29.543l1.568 1.868"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M24.786 29.543l1.568 1.868"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M8.197 29.563l-1.567 1.867"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M8.197 29.563l-1.567 1.867"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M16.559 4.008v-2.438"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M16.559 4.008v-2.438"/>
  </svg>
  <svg x="462">
    <defs>
      <mask id="D" mask-type="alpha">
        <g clip-path="url(#f14_BkV3WrqkR1)" transform="matrix(.2 0 0 .2 -15.623 -12.404)">
          <path fill="context-stroke" d="M0 0h320v240h-320z"/>
        </g>
      </mask>
      <mask id="C" mask-type="alpha">
        <g clip-path="url(#f14_4P5xKEAlF3)" transform="matrix(.2 0 0 .2 -15.216 -8.977)">
          <path fill="context-stroke" d="M0 0h320v240h-320z"/>
        </g>
      </mask>
    </defs>
    <g mask="url(#C)">
      <path fill="#FFF" d="M16.413 7.97l2.017 3.968 0.852 1.677 1.853 0.316 4.31 0.736 -3.138 3.417 -1.216 1.322 0.272 1.777 0.691 4.51 -3.917 -2.078 -1.763 -0.935 -1.758 0.944 -3.805 2.042 0.688 -4.482 0.275 -1.786 -1.23 -1.326 -3.156 -3.4 4.386 -0.736 1.902 -0.32 0.841 -1.737 1.895 -3.906"/>
      <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.869" d="M16.413 7.97l2.017 3.968 0.852 1.677 1.853 0.316 4.31 0.736 -3.138 3.417 -1.216 1.322 0.272 1.777 0.691 4.51 -3.917 -2.078 -1.763 -0.935 -1.758 0.944 -3.805 2.042 0.688 -4.482 0.275 -1.786 -1.23 -1.326 -3.156 -3.4 4.386 -0.736 1.902 -0.32 0.841 -1.737 1.895 -3.906"/>
    </g>
    <g mask="url(#D)">
      <path fill="context-stroke" d="M16.467 8.925l2.65 5.371 5.928 0.862 -4.289 4.18 1.013 5.905 -5.302 -2.788 -5.302 2.788 1.012 -5.904 -4.289 -4.181 5.928 -0.862 2.65 -5.371z"/>
      <path fill="none" d="M16.467 8.925l2.65 5.371 5.928 0.862 -4.289 4.18 1.013 5.905 -5.302 -2.788 -5.302 2.788 1.012 -5.904 -4.289 -4.181 5.928 -0.862 2.65 -5.371z"/>
    </g>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M2.901 13.605l-1.684 -0.822"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M2.901 13.605l-1.684 -0.822"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M30.109 13.637l1.685 -0.822"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M30.109 13.637l1.685 -0.822"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M25.149 29.975l1.205 1.436"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M25.149 29.975l1.205 1.436"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M7.835 29.994l-1.205 1.436"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M7.835 29.994l-1.205 1.436"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M16.559 3.445v-1.875"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M16.559 3.445v-1.875"/>
  </svg>
  <svg x="495">
    <defs>
      <mask id="F" mask-type="alpha">
        <g clip-path="url(#f15_BkV3WrqkR1)" transform="matrix(.2 0 0 .2 -15.623 -12.404)">
          <path fill="context-stroke" d="M0 0h320v240h-320z"/>
        </g>
      </mask>
      <mask id="E" mask-type="alpha">
        <g clip-path="url(#f15_4P5xKEAlF3)" transform="matrix(.2 0 0 .2 -15.216 -8.977)">
          <path fill="context-stroke" d="M0 0h320v240h-320z"/>
        </g>
      </mask>
    </defs>
    <g mask="url(#E)">
      <path fill="#FFF" d="M16.422 8.223l1.958 3.853 0.827 1.628 1.8 0.307 4.184 0.715 -3.047 3.317 -1.18 1.284 0.264 1.725 0.671 4.38 -3.804 -2.018 -1.712 -0.908 -1.706 0.917 -3.696 1.982 0.67 -4.35 0.266 -1.736 -1.195 -1.287 -3.064 -3.3 4.258 -0.716 1.848 -0.311 0.817 -1.686 1.839 -3.793"/>
      <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.814" d="M16.422 8.223l1.958 3.853 0.827 1.628 1.8 0.307 4.184 0.715 -3.047 3.317 -1.18 1.284 0.264 1.725 0.671 4.38 -3.804 -2.018 -1.712 -0.908 -1.706 0.917 -3.696 1.982 0.67 -4.35 0.266 -1.736 -1.195 -1.287 -3.064 -3.3 4.258 -0.716 1.848 -0.311 0.817 -1.686 1.839 -3.793"/>
    </g>
    <g mask="url(#F)">
      <path fill="context-stroke" d="M16.473 9.15l2.575 5.216 5.755 0.836 -4.165 4.06 0.984 5.733 -5.149 -2.707 -5.148 2.707 0.984 -5.733 -4.165 -4.06 5.755 -0.836 2.574 -5.216z"/>
      <path fill="none" d="M16.473 9.15l2.575 5.216 5.755 0.836 -4.165 4.06 0.984 5.733 -5.149 -2.707 -5.148 2.707 0.984 -5.733 -4.165 -4.06 5.755 -0.836 2.574 -5.216z"/>
    </g>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M2.342 13.332l-1.125 -0.549"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M2.342 13.332l-1.125 -0.549"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M30.668 13.364l1.126 -0.549"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M30.668 13.364l1.126 -0.549"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M25.549 30.451l0.805 0.96"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M25.549 30.451l0.805 0.96"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M7.435 30.47l-0.805 0.96"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M7.435 30.47l-0.805 0.96"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M16.559 2.822v-1.252"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M16.559 2.822v-1.252"/>
  </svg>
  <svg x="528">
    <defs>
      <mask id="H" mask-type="alpha">
        <g clip-path="url(#f16_BkV3WrqkR1)" transform="matrix(.2 0 0 .2 -15.623 -12.404)">
          <path fill="context-stroke" d="M0 0h320v240h-320z"/>
        </g>
      </mask>
      <mask id="G" mask-type="alpha">
        <g clip-path="url(#f16_4P5xKEAlF3)" transform="matrix(.2 0 0 .2 -15.216 -8.977)">
          <path fill="context-stroke" d="M0 0h320v240h-320z"/>
        </g>
      </mask>
    </defs>
    <g mask="url(#G)">
      <path fill="#FFF" d="M16.43 8.48l1.899 3.736 0.802 1.579 1.745 0.297 4.057 0.693 -2.954 3.217 -1.144 1.245 0.255 1.673 0.651 4.247 -3.688 -1.957 -1.66 -0.88 -1.655 0.888 -3.583 1.923 0.649 -4.22 0.258 -1.681 -1.158 -1.249 -2.972 -3.2 4.13 -0.694 1.79 -0.301 0.793 -1.636 1.783 -3.677"/>
      <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.759" d="M16.43 8.48l1.899 3.736 0.802 1.579 1.745 0.297 4.057 0.693 -2.954 3.217 -1.144 1.245 0.255 1.673 0.651 4.247 -3.688 -1.957 -1.66 -0.88 -1.655 0.888 -3.583 1.923 0.649 -4.22 0.258 -1.681 -1.158 -1.249 -2.972 -3.2 4.13 -0.694 1.79 -0.301 0.793 -1.636 1.783 -3.677"/>
    </g>
    <g mask="url(#H)">
      <path fill="context-stroke" d="M16.48 9.38l2.496 5.056 5.581 0.811 -4.038 3.937 0.953 5.559 -4.992 -2.625 -4.992 2.625 0.954 -5.559 -4.039 -3.937 5.581 -0.81 2.496 -5.058z"/>
      <path fill="none" d="M16.48 9.38l2.496 5.056 5.581 0.811 -4.038 3.937 0.953 5.559 -4.992 -2.625 -4.992 2.625 0.954 -5.559 -4.039 -3.937 5.581 -0.81 2.496 -5.058z"/>
    </g>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M1.758 13.047l-0.541 -0.264"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M1.758 13.047l-0.541 -0.264"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M31.253 13.08l0.54 -0.265"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M31.253 13.08l0.54 -0.265"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M25.967 30.95l0.387 0.46"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M25.967 30.95l0.387 0.46"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M7.017 30.97l-0.387 0.46"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M7.017 30.97l-0.387 0.46"/>
    <path fill="context-fill" fill-opacity="context-fill-opacity" d="M16.559 2.172v-0.602"/>
    <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-width="1.6" d="M16.559 2.172v-0.602"/>
  </svg>
  <svg x="561">
    <defs>
      <mask id="J" mask-type="alpha">
        <g clip-path="url(#f17_BkV3WrqkR1)" transform="matrix(.2 0 0 .2 -15.623 -12.404)">
          <path fill="context-stroke" d="M0 0h320v240h-320z"/>
        </g>
      </mask>
      <mask id="I" mask-type="alpha">
        <g clip-path="url(#f17_4P5xKEAlF3)" transform="matrix(.2 0 0 .2 -15.216 -8.977)">
          <path fill="context-stroke" d="M0 0h320v240h-320z"/>
        </g>
      </mask>
    </defs>
    <g mask="url(#I)">
      <path fill="#FFF" d="M16.438 8.731l1.84 3.622 0.779 1.53 1.69 0.289 3.934 0.672 -2.864 3.118 -1.109 1.207 0.248 1.622 0.631 4.116 -3.575 -1.896 -1.61 -0.854 -1.604 0.862 -3.473 1.863 0.628 -4.09 0.251 -1.63 -1.123 -1.21 -2.88 -3.103 4.002 -0.673 1.737 -0.292 0.768 -1.585 1.729 -3.565"/>
      <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.706" d="M16.438 8.731l1.84 3.622 0.779 1.53 1.69 0.289 3.934 0.672 -2.864 3.118 -1.109 1.207 0.248 1.622 0.631 4.116 -3.575 -1.896 -1.61 -0.854 -1.604 0.862 -3.473 1.863 0.628 -4.09 0.251 -1.63 -1.123 -1.21 -2.88 -3.103 4.002 -0.673 1.737 -0.292 0.768 -1.585 1.729 -3.565"/>
    </g>
    <g mask="url(#J)">
      <path fill="context-stroke" d="M16.487 9.603l2.42 4.902 5.41 0.787 -3.915 3.816 0.924 5.388 -4.839 -2.544 -4.84 2.544 0.925 -5.388 -3.915 -3.816 5.41 -0.787 2.42 -4.902z"/>
      <path fill="none" d="M16.487 9.603l2.42 4.902 5.41 0.787 -3.915 3.816 0.924 5.388 -4.839 -2.544 -4.84 2.544 0.925 -5.388 -3.915 -3.816 5.41 -0.787 2.42 -4.902z"/>
    </g>
  </svg>
  <svg x="594">
    <defs>
      <mask id="L" mask-type="alpha">
        <g clip-path="url(#f18_BkV3WrqkR1)" transform="matrix(.2 0 0 .2 -15.623 -12.404)">
          <path fill="context-stroke" d="M0 0h320v240h-320z"/>
        </g>
      </mask>
      <mask id="K" mask-type="alpha">
        <g clip-path="url(#f18_4P5xKEAlF3)" transform="matrix(.2 0 0 .2 -15.216 -8.977)">
          <path fill="context-stroke" d="M0 0h320v240h-320z"/>
        </g>
      </mask>
    </defs>
    <g mask="url(#K)">
      <path fill="#FFF" d="M16.446 8.966l1.786 3.515 0.755 1.485 1.642 0.28 3.817 0.652 -2.78 3.027 -1.076 1.171 0.24 1.574 0.613 3.996 -3.47 -1.841 -1.562 -0.828 -1.557 0.836 -3.371 1.808 0.61 -3.97 0.243 -1.582 -1.09 -1.175 -2.795 -3.01 3.885 -0.654 1.685 -0.283 0.745 -1.539 1.679 -3.46"/>
      <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.655" d="M16.446 8.966l1.786 3.515 0.755 1.485 1.642 0.28 3.817 0.652 -2.78 3.027 -1.076 1.171 0.24 1.574 0.613 3.996 -3.47 -1.841 -1.562 -0.828 -1.557 0.836 -3.371 1.808 0.61 -3.97 0.243 -1.582 -1.09 -1.175 -2.795 -3.01 3.885 -0.654 1.685 -0.283 0.745 -1.539 1.679 -3.46"/>
    </g>
    <g mask="url(#L)">
      <path fill="context-stroke" d="M16.493 9.811l2.349 4.759 5.25 0.763 -3.799 3.704 0.897 5.23 -4.697 -2.47 -4.696 2.47 0.897 -5.23 -3.8 -3.704 5.25 -0.763 2.35 -4.759z"/>
      <path fill="none" d="M16.493 9.811l2.349 4.759 5.25 0.763 -3.799 3.704 0.897 5.23 -4.697 -2.47 -4.696 2.47 0.897 -5.23 -3.8 -3.704 5.25 -0.763 2.35 -4.759z"/>
    </g>
  </svg>
  <svg x="627">
    <defs>
      <mask id="N" mask-type="alpha">
        <g clip-path="url(#f19_BkV3WrqkR1)" transform="matrix(.2 0 0 .2 -15.623 -12.404)">
          <path fill="context-stroke" d="M0 0h320v240h-320z"/>
        </g>
      </mask>
      <mask id="M" mask-type="alpha">
        <g clip-path="url(#f19_4P5xKEAlF3)" transform="matrix(.2 0 0 .2 -15.216 -8.977)">
          <path fill="context-stroke" d="M0 0h320v240h-320z"/>
        </g>
      </mask>
    </defs>
    <g mask="url(#M)">
      <path fill="#FFF" d="M16.453 9.164l1.74 3.425 0.735 1.447 1.6 0.273 3.719 0.635 -2.708 2.95 -1.049 1.14 0.235 1.534 0.596 3.893 -3.38 -1.794 -1.523 -0.807 -1.516 0.815 -3.285 1.762 0.595 -3.868 0.237 -1.542 -1.062 -1.144 -2.724 -2.934 3.785 -0.636 1.642 -0.276 0.726 -1.499 1.635 -3.371"/>
      <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.613" d="M16.453 9.164l1.74 3.425 0.735 1.447 1.6 0.273 3.719 0.635 -2.708 2.95 -1.049 1.14 0.235 1.534 0.596 3.893 -3.38 -1.794 -1.523 -0.807 -1.516 0.815 -3.285 1.762 0.595 -3.868 0.237 -1.542 -1.062 -1.144 -2.724 -2.934 3.785 -0.636 1.642 -0.276 0.726 -1.499 1.635 -3.371"/>
    </g>
    <g mask="url(#N)">
      <path fill="context-stroke" d="M16.499 9.988l2.288 4.636 5.116 0.744 -3.702 3.608 0.874 5.096 -4.576 -2.406 -4.576 2.406 0.874 -5.096 -3.702 -3.608 5.116 -0.744 2.288 -4.636z"/>
      <path fill="none" d="M16.499 9.988l2.288 4.636 5.116 0.744 -3.702 3.608 0.874 5.096 -4.576 -2.406 -4.576 2.406 0.874 -5.096 -3.702 -3.608 5.116 -0.744 2.288 -4.636z"/>
    </g>
  </svg>
  <svg x="660">
    <defs>
      <mask id="P" mask-type="alpha">
        <g clip-path="url(#f20_BkV3WrqkR1)" transform="matrix(0 0 0 0 16.352 17.593)">
          <path fill="context-stroke" d="M0 0h320v240h-320z"/>
        </g>
      </mask>
      <mask id="O" mask-type="alpha">
        <g clip-path="url(#f20_4P5xKEAlF3)" transform="matrix(0 0 0 0 16.463 17.517)">
          <path fill="context-stroke" d="M0 0h320v240h-320z"/>
        </g>
      </mask>
    </defs>
    <path fill="none" stroke="context-fill" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.584" d="M16.457 9.307l1.71 3.363 0.722 1.422 1.57 0.267 3.653 0.624 -2.66 2.897 -1.03 1.12 0.23 1.506 0.587 3.824 -3.32 -1.762 -1.496 -0.792 -1.49 0.8 -3.225 1.73 0.584 -3.798 0.233 -1.514 -1.043 -1.124 -2.676 -2.882 3.718 -0.624 1.613 -0.272 0.713 -1.472 1.606 -3.31" display="block"/>
    <g mask="url(#O)">
      <path fill="#FFF" d="M16.456 9.271l1.716 3.376 0.725 1.427 1.577 0.269 3.666 0.626 -2.67 2.907 -1.033 1.125 0.23 1.512 0.589 3.838 -3.333 -1.768 -1.5 -0.796 -1.496 0.803 -3.238 1.737 0.586 -3.813 0.234 -1.52 -1.047 -1.128 -2.686 -2.892 3.732 -0.627 1.619 -0.273 0.716 -1.477 1.612 -3.324"/>
      <path fill="none" stroke="context-stroke" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.59" d="M16.456 9.271l1.716 3.376 0.725 1.427 1.577 0.269 3.666 0.626 -2.67 2.907 -1.033 1.125 0.23 1.512 0.589 3.838 -3.333 -1.768 -1.5 -0.796 -1.496 0.803 -3.238 1.737 0.586 -3.813 0.234 -1.52 -1.047 -1.128 -2.686 -2.892 3.732 -0.627 1.619 -0.273 0.716 -1.477 1.612 -3.324"/>
    </g>
    <g mask="url(#P)" opacity=".08">
      <path fill="context-fill" fill-opacity="context-fill-opacity" d="M16.502 10.083l2.255 4.57 5.044 0.734 -3.65 3.557 0.862 5.023 -4.511 -2.371 -4.512 2.371 0.862 -5.023 -3.65 -3.557 5.044 -0.733 2.256 -4.57z"/>
      <path fill="none" d="M16.502 10.083l2.255 4.57 5.044 0.734 -3.65 3.557 0.862 5.023 -4.511 -2.371 -4.512 2.371 0.862 -5.023 -3.65 -3.557 5.044 -0.733 2.256 -4.57z"/>
    </g>
  </svg>
</svg>
PK
!<�V~l__=chrome/browser/skin/classic/browser/bookmark-star-on-tray.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M14 16H2a1 1 0 0 1-1-1v-2a1 1 0 0 1 2 0v1h10v-1a1 1 0 0 1 2 0v2a1 1 0 0 1-1 1zM13.961 4.282a.9.9 0 0 0-.723-.609l-3.063-.445L8.805.456a.934.934 0 0 0-1.605 0L5.83 3.228l-3.063.445A.893.893 0 0 0 2.27 5.2l2.217 2.156-.523 3.044a.894.894 0 0 0 1.3.942L8 9.907l2.74 1.439a.888.888 0 0 0 .416.1.9.9 0 0 0 .526-.172.893.893 0 0 0 .355-.874l-.522-3.047 2.22-2.153a.893.893 0 0 0 .226-.918zm-4.367 2.45l.376 2.189L8 7.888 6.035 8.921l.376-2.189-1.592-1.55 2.2-.319L8 2.872l.983 1.991 2.2.319z"/>
</svg>PK
!<a��`||0chrome/browser/skin/classic/browser/bookmark.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M15.845 6.064A1.1 1.1 0 0 0 15 5.331L10.911 4.6 8.985.735a1.1 1.1 0 0 0-1.969 0L5.089 4.6l-4.081.729a1.1 1.1 0 0 0-.615 1.834L3.32 10.31l-.609 4.36a1.1 1.1 0 0 0 1.6 1.127L8 13.873l3.69 1.927a1.1 1.1 0 0 0 1.6-1.127l-.61-4.363 2.926-3.146a1.1 1.1 0 0 0 .239-1.1z"/>
</svg>
PK
!<�Fn��9chrome/browser/skin/classic/browser/bookmarks-toolbar.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16" fill="context-fill" fill-opacity="context-fill-opacity">
  <path d="M13 1H3a3 3 0 0 0-3 3v8a3 3 0 0 0 3 3h11a2 2 0 0 0 2-2V4a3 3 0 0 0-3-3zm1 11a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1z"/>
  <path d="M9.466 6.269l-.964-1.934-.185-.305-.662.028-1.101 2.211-2.039.364-.329.084-.184.613L5.67 9.123 5.365 11.3l-.023.351.552.356 2.116-1.102 1.844.96.319.138.525-.395-.347-2.485 1.462-1.573.214-.268-.227-.596-2.334-.417z"/>
</svg>
PK
!<[�FJ��2chrome/browser/skin/classic/browser/cert-error.svg<?xml version="1.0" encoding="utf-8"?>

<svg version="1.1"
     xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink"
     width="45"
     height="45"
     viewBox="0 0 45 45">

  <style>
    .icon-default {
      fill: #999;
    }
  </style>

  <defs>
    <rect id="shape-lock-clasp-outer" x="8" y="2" width="28" height="40" rx="14" ry="14" />
    <rect id="shape-lock-clasp-inner" x="14" y="8" width="16" height="28" rx="8" ry="8" />
    <rect id="shape-lock-base" x="4" y="18" width="36" height="24" rx="3" ry="3" />

    <mask id="mask-clasp-cutout">
      <rect width="48" height="48" fill="#000" />
      <use xlink:href="#shape-lock-clasp-outer" fill="#fff" />
      <use xlink:href="#shape-lock-clasp-inner" fill="#000" />
      <line x1="4" y1="38" x2="41" y2="3" stroke="#000" stroke-width="5.5" />
      <line x1="4" y1="46" x2="41" y2="11" stroke="#000" stroke-width="5.5" />
      <rect x="4" y="18" width="36" height="26" rx="6" ry="6" />
    </mask>

    <mask id="mask-base-cutout">
      <rect width="45" height="45" fill="#000" />
      <use xlink:href="#shape-lock-base" fill="#fff" />
      <line x1="2.5" y1="41.5" x2="41" y2="5" stroke="#000" stroke-width="8.5" />
    </mask>
  </defs>

  <use xlink:href="#shape-lock-clasp-outer" mask="url(#mask-clasp-cutout)" fill="#999" />
  <use xlink:href="#shape-lock-base" mask="url(#mask-base-cutout)" fill="#999" />

  <line x1="2.5" y1="41.5" x2="41" y2="5" stroke="#d92d21" stroke-width="5.5" />

</svg>
PK
!<{���II9chrome/browser/skin/classic/browser/characterEncoding.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M13 15H3a2.006 2.006 0 0 1-2-2V3a2.006 2.006 0 0 1 2-2h10a2.006 2.006 0 0 1 2 2v10a2.006 2.006 0 0 1-2 2zm0-10a2.946 2.946 0 0 0-3-3H6a2.946 2.946 0 0 0-3 3v4a2.946 2.946 0 0 0 3 3h4c1.7 0 3-.3 3-2zm-3 3.2a2.769 2.769 0 0 0 .9-.1c.3-.1.5-.2.8-.3v.8a6.89 6.89 0 0 0-.8.3 2.22 2.22 0 0 1-.9.1 2.149 2.149 0 0 1-2.1-1.2 3.819 3.819 0 0 1-.9.9 2.663 2.663 0 0 1-1.2.3 1.728 1.728 0 0 1-1.3-.5A1.248 1.248 0 0 1 4 7.3 1.486 1.486 0 0 1 4.6 6a3.312 3.312 0 0 1 1.9-.5h.9v-.4a1.327 1.327 0 0 0-.3-1c-.1-.3-.4-.4-.8-.4a3.429 3.429 0 0 0-1.6.4l-.2-.6a3.919 3.919 0 0 1 .9-.4c.3 0 .6-.1 1-.1a3.6 3.6 0 0 1 1.1.2 1.7 1.7 0 0 1 .6.8 1.575 1.575 0 0 1 .7-.7 1.689 1.689 0 0 1 1-.3 1.865 1.865 0 0 1 1.6.7 2.883 2.883 0 0 1 .6 1.9v.6H8.4c0 1.4.6 2 1.6 2zM7.5 6.1h-.8a2.42 2.42 0 0 0-1.4.3.975.975 0 0 0-.4.9.779.779 0 0 0 .3.7.844.844 0 0 0 .7.2 1.594 1.594 0 0 0 1.2-.4 1.7 1.7 0 0 0 .4-1.3zm3.6-.6a2.269 2.269 0 0 0-.3-1.3.975.975 0 0 0-.9-.4 1.284 1.284 0 0 0-1 .4 2.226 2.226 0 0 0-.5 1.3z"/>
</svg>
PK
!<�V���7chrome/browser/skin/classic/browser/check-animation.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="266" height="14">
  <svg x="14">
    <defs>
      <mask id="b">
        <path fill="#fff" stroke="#fff" stroke-width="1.881" d="M-2.91 5.508v7.07h4.488v-7.07H-2.91"/>
      </mask>
    </defs>
    <path fill="#FFF" d="M6 14a.997.997 0 0 1-.707-.293l-3-3a1 1 0 0 1 1.414-1.414l2.157 2.157 6.316-9.023a1 1 0 0 1 1.64 1.146l-7 10a1 1 0 0 1-.733.427A1.262 1.262 0 0 1 6 14z" mask="url(#b)" transform="translate(-.938 -.938)"/>
  </svg>
  <svg x="28">
    <defs>
      <mask id="c">
        <path fill="#fff" stroke="#fff" stroke-width="3.877" d="M-2.91 5.508v7.07h4.488v-7.07H-2.91"/>
      </mask>
    </defs>
    <path fill="#FFF" d="M6 14a.997.997 0 0 1-.707-.293l-3-3a1 1 0 0 1 1.414-1.414l2.157 2.157 6.316-9.023a1 1 0 0 1 1.64 1.146l-7 10a1 1 0 0 1-.733.427A1.262 1.262 0 0 1 6 14z" mask="url(#c)" transform="translate(-.938 -.938)"/>
  </svg>
  <svg x="42">
    <defs>
      <mask id="d">
        <path fill="#fff" stroke="#fff" stroke-width="5.732" d="M-2.91 5.508v7.07h4.488v-7.07H-2.91"/>
      </mask>
    </defs>
    <path fill="#FFF" d="M6 14a.997.997 0 0 1-.707-.293l-3-3a1 1 0 0 1 1.414-1.414l2.157 2.157 6.316-9.023a1 1 0 0 1 1.64 1.146l-7 10a1 1 0 0 1-.733.427A1.262 1.262 0 0 1 6 14z" mask="url(#d)" transform="translate(-.938 -.938)"/>
  </svg>
  <svg x="56">
    <defs>
      <mask id="e">
        <path fill="#fff" stroke="#fff" stroke-width="7.323" d="M-2.91 5.508v7.07h4.488v-7.07H-2.91"/>
      </mask>
    </defs>
    <path fill="#FFF" d="M6 14a.997.997 0 0 1-.707-.293l-3-3a1 1 0 0 1 1.414-1.414l2.157 2.157 6.316-9.023a1 1 0 0 1 1.64 1.146l-7 10a1 1 0 0 1-.733.427A1.262 1.262 0 0 1 6 14z" mask="url(#e)" transform="translate(-.938 -.938)"/>
  </svg>
  <svg x="70">
    <defs>
      <mask id="f">
        <path fill="#fff" stroke="#fff" stroke-width="8.501" d="M-2.91 5.508v7.07h4.488v-7.07H-2.91"/>
      </mask>
    </defs>
    <path fill="#FFF" d="M6 14a.997.997 0 0 1-.707-.293l-3-3a1 1 0 0 1 1.414-1.414l2.157 2.157 6.316-9.023a1 1 0 0 1 1.64 1.146l-7 10a1 1 0 0 1-.733.427A1.262 1.262 0 0 1 6 14z" mask="url(#f)" transform="translate(-.938 -.938)"/>
  </svg>
  <svg x="84">
    <defs>
      <mask id="g">
        <path fill="#fff" stroke="#fff" stroke-width="9" d="M-2.91 5.508v7.07h4.488v-7.07H-2.91"/>
      </mask>
    </defs>
    <path fill="#FFF" d="M6 14a.997.997 0 0 1-.707-.293l-3-3a1 1 0 0 1 1.414-1.414l2.157 2.157 6.316-9.023a1 1 0 0 1 1.64 1.146l-7 10a1 1 0 0 1-.733.427A1.262 1.262 0 0 1 6 14z" mask="url(#g)" transform="translate(-.938 -.938)"/>
  </svg>
  <svg x="98">
    <defs>
      <mask id="h">
        <path fill="#fff" stroke="#fff" stroke-width="13.388" d="M-2.91 5.508v7.07h4.488v-7.07H-2.91"/>
      </mask>
    </defs>
    <path fill="#FFF" d="M6 14a.997.997 0 0 1-.707-.293l-3-3a1 1 0 0 1 1.414-1.414l2.157 2.157 6.316-9.023a1 1 0 0 1 1.64 1.146l-7 10a1 1 0 0 1-.733.427A1.262 1.262 0 0 1 6 14z" mask="url(#h)" transform="translate(-1.19 -1.19) scale(1.03134)"/>
  </svg>
  <svg x="112">
    <defs>
      <mask id="i">
        <path fill="#fff" stroke="#fff" stroke-width="18.046" d="M-2.91 5.508v7.07h4.488v-7.07H-2.91"/>
      </mask>
    </defs>
    <path fill="#FFF" d="M6 14a.997.997 0 0 1-.707-.293l-3-3a1 1 0 0 1 1.414-1.414l2.157 2.157 6.316-9.023a1 1 0 0 1 1.64 1.146l-7 10a1 1 0 0 1-.733.427A1.262 1.262 0 0 1 6 14z" mask="url(#i)" transform="translate(-1.455 -1.455) scale(1.0646)"/>
  </svg>
  <svg x="126">
    <defs>
      <mask id="j">
        <path fill="#fff" stroke="#fff" stroke-width="22.375" d="M-2.91 5.508v7.07h4.488v-7.07H-2.91"/>
      </mask>
    </defs>
    <path fill="#FFF" d="M6 14a.997.997 0 0 1-.707-.293l-3-3a1 1 0 0 1 1.414-1.414l2.157 2.157 6.316-9.023a1 1 0 0 1 1.64 1.146l-7 10a1 1 0 0 1-.733.427A1.262 1.262 0 0 1 6 14z" mask="url(#j)" transform="translate(-1.702 -1.702) scale(1.09553)"/>
  </svg>
  <svg x="140">
    <defs>
      <mask id="k">
        <path fill="#fff" stroke="#fff" stroke-width="26.086" d="M-2.91 5.508v7.07h4.488v-7.07H-2.91"/>
      </mask>
    </defs>
    <path fill="#FFF" d="M6 14a.997.997 0 0 1-.707-.293l-3-3a1 1 0 0 1 1.414-1.414l2.157 2.157 6.316-9.023a1 1 0 0 1 1.64 1.146l-7 10a1 1 0 0 1-.733.427A1.262 1.262 0 0 1 6 14z" mask="url(#k)" transform="translate(-1.914 -1.914) scale(1.12205)"/>
  </svg>
  <svg x="154">
    <defs>
      <mask id="l">
        <path fill="#fff" stroke="#fff" stroke-width="28.836" d="M-2.91 5.508v7.07h4.488v-7.07H-2.91"/>
      </mask>
    </defs>
    <path fill="#FFF" d="M6 14a.997.997 0 0 1-.707-.293l-3-3a1 1 0 0 1 1.414-1.414l2.157 2.157 6.316-9.023a1 1 0 0 1 1.64 1.146l-7 10a1 1 0 0 1-.733.427A1.262 1.262 0 0 1 6 14z" mask="url(#l)" transform="translate(-2.07 -2.07) scale(1.14168)"/>
  </svg>
  <svg x="168">
    <defs>
      <mask id="m">
        <path fill="#fff" stroke="#fff" stroke-width="30" d="M-2.91 5.508v7.07h4.488v-7.07H-2.91"/>
      </mask>
    </defs>
    <path fill="#FFF" d="M6 14a.997.997 0 0 1-.707-.293l-3-3a1 1 0 0 1 1.414-1.414l2.157 2.157 6.316-9.023a1 1 0 0 1 1.64 1.146l-7 10a1 1 0 0 1-.733.427A1.262 1.262 0 0 1 6 14z" mask="url(#m)" transform="matrix(1.15 0 0 1.15 -2.14 -2.14)"/>
  </svg>
  <svg x="182">
    <defs>
      <mask id="n">
        <path fill="#fff" stroke="#fff" stroke-width="30" d="M-2.91 5.508v7.07h4.488v-7.07H-2.91"/>
      </mask>
    </defs>
    <path fill="#FFF" d="M6 14a.997.997 0 0 1-.707-.293l-3-3a1 1 0 0 1 1.414-1.414l2.157 2.157 6.316-9.023a1 1 0 0 1 1.64 1.146l-7 10a1 1 0 0 1-.733.427A1.262 1.262 0 0 1 6 14z" mask="url(#n)" transform="matrix(1.12 0 0 1.12 -1.89 -1.89)"/>
  </svg>
  <svg x="196">
    <defs>
      <mask id="o">
        <path fill="#fff" stroke="#fff" stroke-width="30" d="M-2.91 5.508v7.07h4.488v-7.07H-2.91"/>
      </mask>
    </defs>
    <path fill="#FFF" d="M6 14a.997.997 0 0 1-.707-.293l-3-3a1 1 0 0 1 1.414-1.414l2.157 2.157 6.316-9.023a1 1 0 0 1 1.64 1.146l-7 10a1 1 0 0 1-.733.427A1.262 1.262 0 0 1 6 14z" mask="url(#o)" transform="matrix(1.1 0 0 1.1 -1.62 -1.62)"/>
  </svg>
  <svg x="210">
    <defs>
      <mask id="p">
        <path fill="#fff" stroke="#fff" stroke-width="30" d="M-2.91 5.508v7.07h4.488v-7.07H-2.91"/>
      </mask>
    </defs>
    <path fill="#FFF" d="M6 14a.997.997 0 0 1-.707-.293l-3-3a1 1 0 0 1 1.414-1.414l2.157 2.157 6.316-9.023a1 1 0 0 1 1.64 1.146l-7 10a1 1 0 0 1-.733.427A1.262 1.262 0 0 1 6 14z" mask="url(#p)" transform="matrix(1.05 0 0 1.05 -1.37 -1.37)"/>
  </svg>
  <svg x="224">
    <defs>
      <mask id="q">
        <path fill="#fff" stroke="#fff" stroke-width="30" d="M-2.91 5.508v7.07h4.488v-7.07H-2.91"/>
      </mask>
    </defs>
    <path fill="#FFF" d="M6 14a.997.997 0 0 1-.707-.293l-3-3a1 1 0 0 1 1.414-1.414l2.157 2.157 6.316-9.023a1 1 0 0 1 1.64 1.146l-7 10a1 1 0 0 1-.733.427A1.262 1.262 0 0 1 6 14z" mask="url(#q)" transform="matrix(1.03 0 0 1.03 -1.16 -1.16)"/>
  </svg>
  <svg x="238">
    <defs>
      <mask id="r">
        <path fill="#fff" stroke="#fff" stroke-width="30" d="M-2.91 5.508v7.07h4.488v-7.07H-2.91"/>
      </mask>
    </defs>
    <path fill="#FFF" d="M6 14a.997.997 0 0 1-.707-.293l-3-3a1 1 0 0 1 1.414-1.414l2.157 2.157 6.316-9.023a1 1 0 0 1 1.64 1.146l-7 10a1 1 0 0 1-.733.427A1.262 1.262 0 0 1 6 14z" mask="url(#r)" transform="matrix(1 0 0 1 -1 -1)"/>
  </svg>
  <svg x="252">
    <defs>
      <mask id="s">
        <path fill="#fff" stroke="#fff" stroke-width="30" d="M-2.91 5.508v7.07h4.488v-7.07H-2.91"/>
      </mask>
    </defs>
    <path fill="#FFF" d="M6 14a.997.997 0 0 1-.707-.293l-3-3a1 1 0 0 1 1.414-1.414l2.157 2.157 6.316-9.023a1 1 0 0 1 1.64 1.146l-7 10a1 1 0 0 1-.733.427A1.262 1.262 0 0 1 6 14z" mask="url(#s)" transform="translate(-.94 -.94)"/>
  </svg>
</svg>
PK
!<tK�-chrome/browser/skin/classic/browser/check.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M6 14a1 1 0 0 1-.707-.293l-3-3a1 1 0 0 1 1.414-1.414l2.157 2.157 6.316-9.023a1 1 0 0 1 1.639 1.146l-7 10a1 1 0 0 1-.732.427A.863.863 0 0 1 6 14z"/>
</svg>
PK
!<��
�'I'I9chrome/browser/skin/classic/browser/chevron-animation.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="1278" height="36" fill="context-fill" fill-opacity="context-fill-opacity">
  <svg>
    <path d="M9.7 17.3l-5-5a1 1 0 1 0-1.4 1.4L7.6 18l-4.3 4.3a1 1 0 0 0 0 1.4 1 1 0 0 0 1.4 0l5-5a1 1 0 0 0 0-1.4zm6 0l-5-5a1 1 0 1 0-1.4 1.4l4.3 4.3-4.3 4.3a1 1 0 0 0 0 1.4 1 1 0 0 0 1.4 0l5-5a1 1 0 0 0 0-1.4z"/>
  </svg>
  <svg x="18">
    <path d="M9.7 17.3l-5-5a1 1 0 1 0-1.4 1.3L7.6 18l-4.4 4.2a1 1 0 0 0 0 1.4 1 1 0 0 0 1.4 0l5.1-4.9a1 1 0 0 0 0-1.4zm6 0l-5-5a1 1 0 1 0-1.4 1.4l4.3 4.3-4.4 4.3a1 1 0 0 0 0 1.4 1 1 0 0 0 1.4 0l5.1-5a1 1 0 0 0 0-1.3z"/>
  </svg>
  <svg x="36">
    <path d="M9.7 17.3L5 12.1a1 1 0 1 0-1.4 1.4l4 4.4-4.4 4.2a1 1 0 0 0 0 1.4 1 1 0 0 0 1.4 0l5.2-4.8a1 1 0 0 0 0-1.4zm6 .3L11 12.4a1 1 0 1 0-1.4 1.3l4 4.5-4.4 4.1a1 1 0 0 0 0 1.4 1 1 0 0 0 1.4 0l5.2-4.7a1 1 0 0 0 0-1.4z"/>
  </svg>
  <svg x="54">
    <path d="M9.8 17.4l-4.6-5.5a1 1 0 1 0-1.5 1.3L7.6 18l-4.7 3.9a1 1 0 0 0-.1 1.4 1 1 0 0 0 1.4.1l5.4-4.5a1 1 0 0 0 .2-1.4zm6 .5l-4.6-5.4a1 1 0 1 0-1.5 1.2l3.9 4.7-4.7 3.9a1 1 0 0 0-.1 1.4 1 1 0 0 0 1.4.2l5.4-4.6a1 1 0 0 0 .1-1.4z"/>
  </svg>
  <svg x="72">
    <path d="M9.8 17.4l-4.2-5.7A1 1 0 1 0 4 13l3.6 4.9-4.9 3.6a1 1 0 0 0-.2 1.4 1 1 0 0 0 1.4.2l5.7-4.2a1 1 0 0 0 .2-1.4zm6 .9l-4.3-5.7a1 1 0 1 0-1.6 1.2l3.6 4.9-4.8 3.6a1 1 0 0 0-.3 1.4 1 1 0 0 0 1.4.2l5.7-4.2a1 1 0 0 0 .2-1.4z"/>
  </svg>
  <svg x="90">
    <path d="M9.8 17.5l-3.7-6a1 1 0 1 0-1.7 1l3.2 5.2-5.1 3.2a1 1 0 0 0-.4 1.4 1 1 0 0 0 1.4.3l6-3.8a1 1 0 0 0 .3-1.3zm5.9 1.3l-3.8-6a1 1 0 1 0-1.7 1l3.3 5.2-5.2 3.2a1 1 0 0 0-.3 1.4 1 1 0 0 0 1.4.4l6-3.8a1 1 0 0 0 .3-1.4z"/>
  </svg>
  <svg x="108">
    <path d="M9.9 17.5l-3.2-6.2a1 1 0 1 0-1.8.9l2.8 5.4-5.4 2.7a1 1 0 0 0-.5 1.4A1 1 0 0 0 3 22l6.6-3a1 1 0 0 0 .4-1.4zm5.7 1.9L12.4 13a1 1 0 1 0-1.8.9l2.8 5.4L8 22.2a1 1 0 0 0-.5 1.3 1 1 0 0 0 1.3.5l6.4-3.3a1 1 0 0 0 .4-1.3z"/>
  </svg>
  <svg x="126">
    <path d="M10 17.6l-2.7-6.5a1 1 0 1 0-1.8.7l2.2 5.6-5.6 2.3a1 1 0 0 0-.6 1.3 1 1 0 0 0 1.3.6l6.6-2.7a1 1 0 0 0 .5-1.3zm5.4 2.4l-2.6-6.6a1 1 0 1 0-1.8.8l2.2 5.6-5.6 2.3a1 1 0 0 0-.6 1.2 1 1 0 0 0 1.3.6l6.6-2.6a1 1 0 0 0 .5-1.3z"/>
  </svg>
  <svg x="144">
    <path d="M10 17.7L8 11a1 1 0 1 0-1.9.6l1.7 5.8L1.9 19a1 1 0 0 0-.7 1.2 1 1 0 0 0 1.2.7l6.9-2a1 1 0 0 0 .7-1.2zm5.2 3l-2-6.8a1 1 0 1 0-1.8.5l1.6 5.8L7.2 22a1 1 0 0 0-.8 1.2 1 1 0 0 0 1.3.7l6.8-2a1 1 0 0 0 .7-1.1z"/>
  </svg>
  <svg x="162">
    <path d="M10 17.8l-1.2-7a1 1 0 1 0-2 .4l1 6-6 1a1 1 0 0 0-.8 1.1 1 1 0 0 0 1.2.9l7-1.2a1 1 0 0 0 .8-1.2zm4.9 3.5l-1.2-7a1 1 0 1 0-2 .4l1 6-6 1a1 1 0 0 0-.8 1.1 1 1 0 0 0 1.1.8l7-1.1a1 1 0 0 0 .9-1.2z"/>
  </svg>
  <svg x="180">
    <path d="M10 18l-.4-7.1a1 1 0 1 0-2 .1l.3 6-6 .4a1 1 0 0 0-1 1 1 1 0 0 0 1 1H2l7-.4a1 1 0 0 0 1-1zm4.5 4l-.4-7.1a1 1 0 1 0-2 0l.3 6.2-6 .3a1 1 0 0 0-1 1 1 1 0 0 0 1 1l7.1-.4a1 1 0 0 0 1-1z"/>
  </svg>
  <svg x="198">
    <path d="M10 18l.4-7a1 1 0 1 0-2-.1l-.3 6-6.1-.3a1 1 0 0 0-1 1 1 1 0 0 0 .8 1L9 19a1 1 0 0 0 1.1-1zm4 4.5l.4-7a1 1 0 1 0-2-.1l-.3 6-6.1-.3a1 1 0 0 0-1 .9 1 1 0 0 0 .8 1l7.1.5a1 1 0 0 0 1.1-1z"/>
  </svg>
  <svg x="216">
    <path d="M10 18.2l1.2-7a1 1 0 1 0-2-.3l-1 6-6-1a1 1 0 0 0-1.2.7 1 1 0 0 0 .8 1.2l7 1.2a1 1 0 0 0 1.2-.8zm3.5 4.9l1.1-7a1 1 0 1 0-2-.3l-1 6-6-1a1 1 0 0 0-1 .7 1 1 0 0 0 .7 1.2l7 1.2a1 1 0 0 0 1.2-.8z"/>
  </svg>
  <svg x="234">
    <path d="M10 18.3l1.9-6.8a1 1 0 1 0-2-.6l-1.6 5.9L2.5 15a1 1 0 0 0-1.3.7A1 1 0 0 0 2 17l6.8 2a1 1 0 0 0 1.3-.7zm2.9 5.2l2-6.8a1 1 0 1 0-2-.5L11.2 22l-5.8-1.6a1 1 0 0 0-1.2.6 1 1 0 0 0 .6 1.3l6.9 1.9a1 1 0 0 0 1.2-.7z"/>
  </svg>
  <svg x="252">
    <path d="M10 18.4l2.5-6.6a1 1 0 1 0-1.8-.7l-2.3 5.6-5.6-2.2a1 1 0 0 0-1.3.5 1 1 0 0 0 .5 1.3L8.7 19a1 1 0 0 0 1.3-.5zm2.3 5.5l2.6-6.6a1 1 0 1 0-1.8-.7l-2.3 5.6L5.2 20a1 1 0 0 0-1.3.5 1 1 0 0 0 .5 1.3l6.6 2.6a1 1 0 0 0 1.3-.5z"/>
  </svg>
  <svg x="270">
    <path d="M9.9 18.5l3.2-6.3a1 1 0 1 0-1.8-1l-2.7 5.5-5.4-2.8a1 1 0 0 0-1.4.4 1 1 0 0 0 .4 1.3L8.6 19a1 1 0 0 0 1.4-.4zm1.8 5.7l3.3-6.3a1 1 0 1 0-1.8-1l-2.8 5.5L5 19.6a1 1 0 0 0-1.3.4 1 1 0 0 0 .4 1.4l6.3 3.2a1 1 0 0 0 1.3-.4z"/>
  </svg>
  <svg x="288">
    <path d="M9.8 18.5l3.8-6a1 1 0 1 0-1.7-1l-3.2 5.1-5.2-3.2a1 1 0 0 0-1.3.3 1 1 0 0 0 .2 1.4l6 3.7a1 1 0 0 0 1.4-.3zm1.4 5.9l3.7-6a1 1 0 1 0-1.7-1l-3.2 5-5-3.1a1 1 0 0 0-1.4.3 1 1 0 0 0 .3 1.4l6 3.8a1 1 0 0 0 1.4-.3z"/>
  </svg>
  <svg x="306">
    <path d="M9.8 18.6l4.2-5.7a1 1 0 1 0-1.6-1.2l-3.6 4.9L3.9 13a1 1 0 0 0-1.4.2 1 1 0 0 0 .2 1.4l5.7 4.2a1 1 0 0 0 1.4-.2zm.9 6l4.2-5.8a1 1 0 1 0-1.6-1.1l-3.6 4.8L4.8 19a1 1 0 0 0-1.4.2 1 1 0 0 0 .2 1.4l5.7 4.2a1 1 0 0 0 1.4-.2z"/>
  </svg>
  <svg x="324">
    <path d="M9.8 18.6l4.5-5.4a1 1 0 1 0-1.5-1.3l-4 4.7-4.6-4a1 1 0 0 0-1.4.2 1 1 0 0 0 .1 1.4l5.5 4.6a1 1 0 0 0 1.4-.2zm.5 6l4.5-5.4a1 1 0 1 0-1.5-1.3l-3.9 4.7-4.7-4a1 1 0 0 0-1.4.2 1 1 0 0 0 .1 1.4L9 24.7a1 1 0 0 0 1.4 0z"/>
  </svg>
  <svg x="342">
    <path d="M9.7 18.7l4.8-5.2a1 1 0 1 0-1.4-1.4l-4.2 4.5-4.4-4.1a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5.2 4.8a1 1 0 0 0 1.4 0zm.3 6l4.8-5.2a1 1 0 1 0-1.5-1.4l-4.1 4.5-4.5-4.1a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5.3 4.8a1 1 0 0 0 1.4 0z"/>
  </svg>
  <svg x="360">
    <path d="M9.7 18.7l5-5a1 1 0 1 0-1.5-1.5L9 16.6l-4.4-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5.1 5a1 1 0 0 0 1.4 0zm0 6l5-5a1 1 0 1 0-1.4-1.5L9 22.6l-4.3-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.5 0z"/>
  </svg>
  <svg x="378">
    <path d="M9.7 18.7l5-5a1 1 0 1 0-1.4-1.4L9 16.6l-4.3-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.4 0zm0 6l5-5a1 1 0 1 0-1.4-1.4L9 22.6l-4.3-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.4 0z"/>
  </svg>
  <svg x="396">
    <path d="M9.7 18.8l5-5a1 1 0 1 0-1.4-1.4L9 16.7l-4.3-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.4 0zm0 6l5-5a1 1 0 1 0-1.4-1.4L9 22.7l-4.3-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.4 0z"/>
  </svg>
  <svg x="414">
    <path d="M9.7 19l5-5a1 1 0 1 0-1.4-1.4L9 17l-4.3-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.4 0zm0 6l5-5a1 1 0 1 0-1.4-1.4L9 23l-4.3-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.4 0z"/>
  </svg>
  <svg x="432">
    <path d="M9.7 19.3l5-5a1 1 0 1 0-1.4-1.4L9 17.2 4.7 13a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.4 0zm0 6l5-5a1 1 0 1 0-1.4-1.4L9 23.2 4.7 19a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.4 0z"/>
  </svg>
  <svg x="450">
    <path d="M9.7 19.7l5-5a1 1 0 1 0-1.4-1.4L9 17.6l-4.3-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.4 0zm0 6l5-5a1 1 0 1 0-1.4-1.4L9 23.6l-4.3-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.4 0z"/>
  </svg>
  <svg x="468">
    <path d="M9.7 20l5-5a1 1 0 1 0-1.4-1.3L9 18l-4.3-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.3l5 5a1 1 0 0 0 1.4 0zm0 6l5-5a1 1 0 1 0-1.4-1.3L9 24l-4.3-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.3l5 5a1 1 0 0 0 1.4 0z"/>
  </svg>
  <svg x="486">
    <path d="M9.7 20.4l5-5a1 1 0 1 0-1.4-1.4L9 18.3 4.7 14a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.4 0zm0 6l5-5a1 1 0 1 0-1.4-1.4L9 24.3 4.7 20a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.4 0z"/>
  </svg>
  <svg x="504">
    <path d="M9.7 20.6l5.1-5a1 1 0 0 0 0-1.4 1 1 0 0 0-1.4 0L9 18.5l-4.4-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.5 0zm0 6l5.1-5a1 1 0 0 0 0-1.4 1 1 0 0 0-1.4 0L9 24.5l-4.4-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.5 0z"/>
  </svg>
  <svg x="522">
    <path d="M9.7 20.7l5.3-5a1 1 0 0 0 0-1.4 1 1 0 0 0-1.5 0L9 18.6l-4.5-4.3a1 1 0 0 0-1.5 0 1 1 0 0 0 0 1.4l5.3 5a1 1 0 0 0 1.4 0zm0 6l5.3-5a1 1 0 0 0 0-1.4 1 1 0 0 0-1.5 0L9 24.6l-4.5-4.3a1 1 0 0 0-1.5 0 1 1 0 0 0 0 1.4l5.3 5a1 1 0 0 0 1.4 0z"/>
  </svg>
  <svg x="540">
    <path d="M9.8 20.9l5.3-5c.4-.4.4-1 0-1.5-.4-.3-1.1-.3-1.5 0L9 18.7l-4.6-4.3c-.4-.4-1-.4-1.5 0a1 1 0 0 0 0 1.4l5.3 5c.5.4 1.1.4 1.6 0zm0 6l5.3-5c.4-.4.4-1 0-1.5-.4-.3-1.1-.3-1.5 0L9 24.7l-4.6-4.3c-.4-.4-1-.4-1.5 0a1 1 0 0 0 0 1.4l5.3 5c.5.4 1.1.4 1.6 0z"/>
  </svg>
  <svg x="558">
    <path d="M9.8 21.2l5.4-5c.5-.4.4-1 0-1.4-.4-.4-1-.4-1.5 0L9 19.1l-4.7-4.3a1 1 0 0 0-1.5 0 1 1 0 0 0 0 1.4l5.4 5c.5.4 1.1.4 1.6 0zm0 6l5.4-5c.5-.4.4-1 0-1.4-.4-.4-1-.4-1.5 0L9 25.1l-4.7-4.3a1 1 0 0 0-1.5 0 1 1 0 0 0 0 1.4l5.4 5c.5.4 1.1.4 1.6 0z"/>
  </svg>
  <svg x="576">
    <path d="M9.8 21.7l5.6-5c.4-.4.4-1 0-1.4-.5-.4-1.2-.4-1.6 0L9 19.6l-4.8-4.3c-.4-.4-1.1-.4-1.6 0a1 1 0 0 0 0 1.4l5.6 5c.4.4 1.2.4 1.6 0zm0 6l5.6-5c.4-.4.4-1 0-1.4-.5-.4-1.2-.4-1.6 0L9 25.6l-4.8-4.3c-.4-.4-1.1-.4-1.6 0a1 1 0 0 0 0 1.4l5.6 5c.4.4 1.2.4 1.6 0z"/>
  </svg>
  <svg x="594">
    <path d="M9.8 22.2l5.7-5c.5-.4.5-1 0-1.4-.4-.4-1.1-.4-1.6 0L9 20l-5-4.3c-.4-.4-1-.4-1.5 0a1 1 0 0 0 0 1.4l5.7 5c.4.4 1.2.4 1.6 0zm0 6l5.7-5c.5-.4.5-1 0-1.4-.4-.4-1.1-.4-1.6 0L9 26l-5-4.3c-.4-.4-1-.4-1.5 0a1 1 0 0 0 0 1.4l5.7 5c.4.4 1.2.4 1.6 0z"/>
  </svg>
  <svg x="612">
    <path d="M9.8 22.6l5.9-5c.4-.4.4-1 0-1.5-.5-.3-1.2-.3-1.7 0l-5 4.3-5-4.3c-.5-.4-1.2-.4-1.7 0a1 1 0 0 0 0 1.4l5.9 5c.4.4 1.2.4 1.6 0zm0 6l5.9-5c.4-.4.4-1 0-1.5-.5-.3-1.2-.3-1.7 0l-5 4.3-5-4.3c-.5-.4-1.2-.4-1.7 0a1 1 0 0 0 0 1.4l5.9 5c.4.4 1.2.4 1.6 0z"/>
  </svg>
  <svg x="630">
    <path d="M9.8 22.7l6-5c.4-.4.4-1 0-1.4-.5-.4-1.2-.4-1.7 0L9 20.6l-5.1-4.3c-.5-.4-1.2-.4-1.7 0s-.5 1 0 1.4l6 5c.4.4 1.2.4 1.6 0zm0 6l6-5c.4-.4.4-1 0-1.4-.5-.4-1.2-.4-1.7 0L9 26.6l-5.1-4.3c-.5-.4-1.2-.4-1.7 0s-.5 1 0 1.4l6 5c.4.4 1.2.4 1.6 0z"/>
  </svg>
  <svg x="648">
    <path d="M9.9 22.3l6-5c.5-.4.5-1 0-1.4-.5-.4-1.2-.4-1.7 0L9 20.2l-5.2-4.3c-.5-.4-1.2-.4-1.7 0-.5.3-.5 1 0 1.4l6 5c.5.4 1.3.4 1.8 0zm0 6l6-5c.5-.4.5-1 0-1.4-.5-.4-1.2-.4-1.7 0L9 26.2l-5.2-4.3c-.5-.4-1.2-.4-1.7 0-.5.3-.5 1 0 1.4l6 5c.5.4 1.3.4 1.8 0z"/>
  </svg>
  <svg x="666">
    <path d="M9.9 21.3l6.1-5c.5-.4.5-1 0-1.4-.5-.4-1.3-.4-1.7 0L9 19.2l-5.3-4.3c-.4-.4-1.2-.4-1.7 0-.5.3-.5 1 0 1.4l6.1 5c.5.4 1.3.4 1.8 0zm0 6l6.1-5c.5-.4.5-1 0-1.4-.5-.4-1.3-.4-1.7 0L9 25.2l-5.3-4.3c-.4-.4-1.2-.4-1.7 0-.5.3-.5 1 0 1.4l6.1 5c.5.4 1.3.4 1.8 0z"/>
  </svg>
  <svg x="684">
    <path d="M9.9 20.1l6.2-5c.4-.4.4-1 0-1.4-.5-.4-1.3-.4-1.8 0L9 18l-5.3-4.3c-.5-.4-1.3-.4-1.8 0s-.5 1 0 1.4l6.2 5c.5.4 1.3.4 1.8 0zm0 6l6.2-5c.4-.4.4-1 0-1.4-.5-.4-1.3-.4-1.8 0L9 24l-5.3-4.3c-.5-.4-1.3-.4-1.8 0s-.5 1 0 1.4l6.2 5c.5.4 1.3.4 1.8 0z"/>
  </svg>
  <svg x="702">
    <path d="M9.9 19.1l6.2-5c.5-.4.5-1 0-1.4-.5-.4-1.3-.4-1.7 0L9 17l-5.4-4.3c-.4-.4-1.2-.4-1.7 0s-.5 1 0 1.4l6.2 5c.5.4 1.3.4 1.8 0zm0 6l6.2-5c.5-.4.5-1 0-1.4-.5-.4-1.3-.4-1.7 0L9 23l-5.4-4.3c-.4-.4-1.2-.4-1.7 0s-.5 1 0 1.4l6.2 5c.5.4 1.3.4 1.8 0z"/>
  </svg>
  <svg x="720">
    <path d="M9.9 18.7l6.2-5c.5-.4.5-1 0-1.4-.5-.4-1.2-.4-1.7 0L9 16.6l-5.4-4.3c-.4-.4-1.2-.4-1.7 0s-.5 1 0 1.4l6.2 5c.5.4 1.3.4 1.8 0zm0 6l6.2-5c.5-.4.5-1 0-1.4-.5-.4-1.2-.4-1.7 0L9 22.6l-5.4-4.3c-.4-.4-1.2-.4-1.7 0s-.5 1 0 1.4l6.2 5c.5.4 1.3.4 1.8 0z"/>
  </svg>
  <svg x="738">
    <path d="M9.9 18.7l6-5c.5-.4.5-1 0-1.4-.4-.4-1.2-.4-1.7 0L9 16.6l-5.2-4.3c-.5-.4-1.3-.4-1.8 0-.4.4-.5 1 0 1.4l6.1 5c.5.4 1.3.4 1.8 0zm0 6l6-5c.5-.4.5-1 0-1.4-.4-.4-1.2-.4-1.7 0L9 22.6l-5.2-4.3c-.5-.4-1.3-.4-1.8 0-.4.4-.5 1 0 1.4l6.1 5c.5.4 1.3.4 1.8 0z"/>
  </svg>
  <svg x="756">
    <path d="M9.8 18.7l6-5c.4-.4.4-1 0-1.4-.5-.4-1.3-.4-1.7 0L9 16.6l-5-4.3c-.5-.4-1.3-.4-1.8 0a1 1 0 0 0 0 1.4l6 5c.4.4 1.2.4 1.6 0zm0 6l6-5c.4-.4.4-1 0-1.4-.5-.4-1.3-.4-1.7 0L9 22.6l-5-4.3c-.5-.4-1.3-.4-1.8 0a1 1 0 0 0 0 1.4l6 5c.4.4 1.2.4 1.6 0z"/>
  </svg>
  <svg x="774">
    <path d="M9.8 18.7l5.8-5c.4-.4.4-1 0-1.4-.5-.4-1.2-.4-1.7 0L9 16.6l-5-4.3c-.4-.4-1.1-.4-1.6 0a1 1 0 0 0 0 1.4l5.8 5c.4.4 1.2.4 1.6 0zm0 6l5.8-5c.4-.4.4-1 0-1.4-.5-.4-1.2-.4-1.7 0L9 22.6l-5-4.3c-.4-.4-1.1-.4-1.6 0a1 1 0 0 0 0 1.4l5.8 5c.4.4 1.2.4 1.6 0z"/>
  </svg>
  <svg x="792">
    <path d="M9.8 18.7l5.5-5c.5-.4.5-1 0-1.4a1 1 0 0 0-1.5 0L9 16.6l-4.8-4.3a1 1 0 0 0-1.5 0 1 1 0 0 0 0 1.4l5.5 5c.4.4 1.2.4 1.6 0zm0 6l5.5-5c.5-.4.5-1 0-1.4a1 1 0 0 0-1.5 0L9 22.6l-4.8-4.3a1 1 0 0 0-1.5 0 1 1 0 0 0 0 1.4l5.5 5c.4.4 1.2.4 1.6 0z"/>
  </svg>
  <svg x="810">
    <path d="M9.8 18.7l5.3-5c.5-.4.4-1 0-1.4-.4-.4-1-.4-1.5 0L9 16.6l-4.6-4.3a1 1 0 0 0-1.5 0 1 1 0 0 0 0 1.4l5.3 5c.5.4 1.1.4 1.6 0zm0 6l5.3-5c.5-.4.4-1 0-1.4-.4-.4-1-.4-1.5 0L9 22.6l-4.6-4.3a1 1 0 0 0-1.5 0 1 1 0 0 0 0 1.4l5.3 5c.5.4 1.1.4 1.6 0z"/>
  </svg>
  <svg x="828">
    <path d="M9.7 18.7l5.3-5a1 1 0 0 0 0-1.4 1 1 0 0 0-1.5 0L9 16.6l-4.5-4.3a1 1 0 0 0-1.5 0 1 1 0 0 0 0 1.4l5.3 5a1 1 0 0 0 1.4 0zm0 6l5.3-5a1 1 0 0 0 0-1.4 1 1 0 0 0-1.5 0L9 22.6l-4.5-4.3a1 1 0 0 0-1.5 0 1 1 0 0 0 0 1.4l5.3 5a1 1 0 0 0 1.4 0z"/>
  </svg>
  <svg x="846">
    <path d="M9.7 18.7l5.1-5a1 1 0 0 0 0-1.4 1 1 0 0 0-1.4 0L9 16.6l-4.4-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.5 0zm0 6l5.1-5a1 1 0 0 0 0-1.4 1 1 0 0 0-1.4 0L9 22.6l-4.4-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.5 0z"/>
  </svg>
  <svg x="864">
    <path d="M9.7 18.7l5-5a1 1 0 0 0 0-1.4 1 1 0 0 0-1.4 0L9 16.6l-4.3-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.4 0zm0 6l5-5a1 1 0 0 0 0-1.4 1 1 0 0 0-1.4 0L9 22.6l-4.3-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.4 0z"/>
  </svg>
  <svg x="882">
    <path d="M9.7 18.7l5-5a1 1 0 1 0-1.4-1.4L9 16.6l-4.3-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.4 0zm0 6l5-5a1 1 0 1 0-1.4-1.4L9 22.6l-4.3-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.4 0z"/>
  </svg>
  <svg x="900">
    <path d="M9.7 18.7l5-5a1 1 0 1 0-1.4-1.4L9 16.6l-4.3-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.4 0zm0 6l5-5a1 1 0 1 0-1.4-1.4L9 22.6l-4.3-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.4 0z"/>
  </svg>
  <svg x="918">
    <path d="M9.7 18.7l5-5a1 1 0 1 0-1.4-1.4L9 16.6l-4.3-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.4 0zm0 6l5-5a1 1 0 1 0-1.4-1.4L9 22.6l-4.3-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.4 0z"/>
  </svg>
  <svg x="936">
    <path d="M9.7 18.7l5-5a1 1 0 1 0-1.4-1.4L9 16.6l-4.3-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.4 0zm0 6l5-5a1 1 0 1 0-1.4-1.4L9 22.6l-4.3-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.4 0z"/>
  </svg>
  <svg x="954">
    <path d="M9.7 18.7l5-5a1 1 0 1 0-1.4-1.4L9 16.6l-4.3-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.4 0zm0 6l5-5a1 1 0 1 0-1.4-1.4L9 22.6l-4.3-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.4 0z"/>
  </svg>
  <svg x="972">
    <path d="M9.7 18.7l5-5a1 1 0 1 0-1.4-1.4L9 16.6l-4.3-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.4 0zm0 6l5-5a1 1 0 1 0-1.4-1.4L9 22.6l-4.3-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.4 0z"/>
  </svg>
  <svg x="990">
    <path d="M9.7 18.7l5-5a1 1 0 1 0-1.4-1.4L9 16.6l-4.3-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.4 0zm0 6l5-5a1 1 0 1 0-1.4-1.4L9 22.6l-4.3-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.4 0z"/>
  </svg>
  <svg x="1008">
    <path d="M9.7 18.7l5-5a1 1 0 1 0-1.4-1.4L9 16.6l-4.3-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.4 0zm0 6l5-5a1 1 0 1 0-1.4-1.4L9 22.6l-4.3-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.4 0z"/>
  </svg>
  <svg x="1026">
    <path d="M9.7 18.7l5-5a1 1 0 1 0-1.4-1.4L9 16.6l-4.3-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.4 0zm0 6l5-5a1 1 0 1 0-1.4-1.4L9 22.6l-4.3-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.4 0z"/>
  </svg>
  <svg x="1044">
    <path d="M9.7 18.7l5-5a1 1 0 1 0-1.4-1.4L9 16.6l-4.3-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.4 0zm0 6l5-5a1 1 0 1 0-1.4-1.4L9 22.6l-4.3-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.4 0z"/>
  </svg>
  <svg x="1062">
    <path d="M9.7 18.7l5-5a1 1 0 1 0-1.4-1.4L9 16.6l-4.3-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.4 0zm0 6l5-5a1 1 0 1 0-1.4-1.4L9 22.6l-4.3-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.4 0z"/>
  </svg>
  <svg x="1080">
    <path d="M9.7 18.7l5-5a1 1 0 1 0-1.4-1.4L9 16.6l-4.3-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.4 0zm0 6l5-5a1 1 0 1 0-1.4-1.4L9 22.6l-4.3-4.3a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.4 0z"/>
  </svg>
  <svg x="1098">
    <path d="M9.7 18.7l4.8-5.2A1 1 0 1 0 13 12l-4 4.5-4.5-4.1a1 1 0 0 0-1.5 0A1 1 0 0 0 3 14h.1l5.2 4.8a1 1 0 0 0 1.4 0zm.3 6l4.8-5.2a1 1 0 1 0-1.5-1.4l-4.1 4.5-4.5-4.1a1 1 0 0 0-1.4 0 1 1 0 0 0 0 1.4l5.3 4.8a1 1 0 0 0 1.4 0z"/>
  </svg>
  <svg x="1116">
    <path d="M9.8 18.6l4.1-5.8a1 1 0 1 0-1.6-1.1l-3.5 5-5-3.6a1 1 0 0 0-1.4.2 1 1 0 0 0 .2 1.4l5.8 4.1a1 1 0 0 0 1.4-.2zm1 5.9l4.1-5.7a1 1 0 1 0-1.6-1.2l-3.6 5L4.8 19a1 1 0 0 0-1.4.2 1 1 0 0 0 .2 1.4l5.8 4.1a1 1 0 0 0 1.4-.2z"/>
  </svg>
  <svg x="1134">
    <path d="M9.9 18.4l3-6.3a1 1 0 1 0-1.8-1l-2.6 5.6L3.1 14a1 1 0 0 0-1.4.5 1 1 0 0 0 .5 1.3L8.6 19a1 1 0 0 0 1.3-.5zm2 5.7l3-6.4a1 1 0 1 0-1.8-.9l-2.6 5.5L5 19.7a1 1 0 0 0-1.3.5 1 1 0 0 0 .5 1.3l6.4 3a1 1 0 0 0 1.3-.4z"/>
  </svg>
  <svg x="1152">
    <path d="M10 18.2l1.6-6.8a1 1 0 1 0-2-.5l-1.3 5.9-6-1.4a1 1 0 0 0-1.2.7 1 1 0 0 0 .8 1.2L8.8 19a1 1 0 0 0 1.2-.8zm3.1 5.1l1.7-6.8a1 1 0 1 0-2-.5l-1.4 5.9-5.9-1.4a1 1 0 0 0-1.2.7 1 1 0 0 0 .7 1.2l7 1.7a1 1 0 0 0 1.1-.8z"/>
  </svg>
  <svg x="1170">
    <path d="M10 18v-7a1 1 0 1 0-2 0v6H2a1 1 0 0 0-1 1 1 1 0 0 0 .9 1H9a1 1 0 0 0 1-1zm4.2 4.2v-7a1 1 0 1 0-2 0v6h-6a1 1 0 0 0-1 1 1 1 0 0 0 1 1h7a1 1 0 0 0 1-1z"/>
  </svg>
  <svg x="1188">
    <path d="M10 17.8l-1.7-7a1 1 0 1 0-1.9.5l1.4 6-6 1.4a1 1 0 0 0-.7 1.1 1 1 0 0 0 1.2.8l7-1.6a1 1 0 0 0 .7-1.2zm5 3.1L13.6 14a1 1 0 1 0-2 .5l1.4 6-6 1.3a1 1 0 0 0-.7 1.2 1 1 0 0 0 1.2.8l7-1.7A1 1 0 0 0 15 21z"/>
  </svg>
  <svg x="1206">
    <path d="M9.9 17.6l-3-6.4A1 1 0 1 0 5 12l2.7 5.4L2.2 20a1 1 0 0 0-.5 1.4A1 1 0 0 0 3 22l6.4-3.1a1 1 0 0 0 .5-1.3zm5.7 2l-3-6.4a1 1 0 1 0-1.9.8l2.6 5.5-5.5 2.6a1 1 0 0 0-.4 1.4 1 1 0 0 0 1.3.5l6.4-3.1a1 1 0 0 0 .5-1.3z"/>
  </svg>
  <svg x="1224">
    <path d="M9.8 17.4l-4.1-5.7A1 1 0 1 0 4 12.8l3.5 5-5 3.5a1 1 0 0 0-.2 1.4 1 1 0 0 0 1.4.3l5.8-4.2a1 1 0 0 0 .2-1.4zm6 1l-4.2-5.8a1 1 0 1 0-1.6 1.2l3.5 5-5 3.5a1 1 0 0 0-.2 1.4 1 1 0 0 0 1.4.2l5.8-4.1a1 1 0 0 0 .2-1.4z"/>
  </svg>
  <svg x="1242">
    <path d="M9.7 17.3L5 12.1a1 1 0 1 0-1.5 1.4l4 4.4L3.2 22a1 1 0 0 0 0 1.4 1 1 0 0 0 1.3.1l5.3-4.8a1 1 0 0 0 0-1.4zm6 .3L11 12.4a1 1 0 1 0-1.5 1.3l4 4.5-4.4 4.1a1 1 0 0 0 0 1.4 1 1 0 0 0 1.3.1l5.3-4.8a1 1 0 0 0 0-1.4z"/>
  </svg>
  <svg x="1260">
    <path d="M9.7 17.3l-5-5a1 1 0 1 0-1.4 1.4L7.6 18l-4.3 4.3a1 1 0 0 0 0 1.4 1 1 0 0 0 1.4 0l5-5a1 1 0 0 0 0-1.4zm6 0l-5-5a1 1 0 1 0-1.4 1.4l4.3 4.3-4.3 4.3a1 1 0 0 0 0 1.4 1 1 0 0 0 1.4 0l5-5a1 1 0 0 0 0-1.4z"/>
  </svg>
  <svg x="1278">
    <path d="M9.7 17.3l-5-5a1 1 0 1 0-1.4 1.4L7.6 18l-4.3 4.3a1 1 0 0 0 0 1.4 1 1 0 0 0 1.4 0l5-5a1 1 0 0 0 0-1.4zm6 0l-5-5a1 1 0 1 0-1.4 1.4l4.3 4.3-4.3 4.3a1 1 0 0 0 0 1.4 1 1 0 0 0 1.4 0l5-5a1 1 0 0 0 0-1.4z"/>
  </svg>
</svg>
PK
!< B|R@@4chrome/browser/skin/classic/browser/compacttheme.css

/* compacttheme.css is loaded in browser.xul after browser.css when it is
   preffed on.  The bulk of the styling is here in the shared file, but
   there are overrides for each platform in their compacttheme.css files. */

:root:-moz-lwtheme {
  --toolbar-bgcolor: var(--chrome-secondary-background-color);
  --toolbar-non-lwt-bgcolor: var(--toolbar-bgcolor);
  --toolbar-non-lwt-textcolor: var(--chrome-color);
  --toolbar-non-lwt-bgimage: none;

  --toolbarbutton-icon-fill-opacity: .7;
}

:root:-moz-lwtheme-brighttext {
  /* Chrome */
  --chrome-background-color: hsl(240, 5%, 5%);
  --chrome-color: rgb(249, 249, 250);
  --chrome-secondary-background-color: hsl(240, 1%, 20%);
  --toolbox-border-bottom-color: hsla(240, 5%, 5%, .1);
  --chrome-nav-bar-separator-color: rgba(0,0,0,.2);
  --chrome-nav-buttons-background: hsla(240, 5%, 5%, .1);
  --chrome-nav-buttons-hover-background: hsla(240, 5%, 5%, .15);
  --chrome-nav-bar-controls-border-color: hsla(240, 5%, 5%, .3);
  --chrome-selection-color: #fff;
  --chrome-selection-background-color: #5675B9;

  /* Url and search bars */
  --url-and-searchbar-background-color: rgb(71, 71, 73);
  --url-and-searchbar-color: var(--chrome-color);
  --urlbar-separator-color: #5F6670;
}

:root:-moz-lwtheme-darktext {
  --url-and-searchbar-background-color: #fff;

  --chrome-background-color: #E3E4E6;
  --chrome-color: #18191a;
  --chrome-secondary-background-color: #f5f6f7;
  --toolbox-border-bottom-color: #cccccc;
  --chrome-nav-bar-separator-color: #B6B6B8;
  --chrome-nav-buttons-background: #ffffff; /* --theme-body-background */
  --chrome-nav-buttons-hover-background: #DADBDB;
  --chrome-nav-bar-controls-border-color: #ccc;
  --chrome-selection-color: #f5f7fa;
  --chrome-selection-background-color: #4c9ed9;
}

#tabbrowser-tabs:-moz-lwtheme {
  --tab-line-color: #0a84ff;
}

.toolbarbutton-animatable-box[brighttext],
toolbar[brighttext] .toolbarbutton-animatable-box,
toolbar[brighttext] .toolbarbutton-1 {
  fill: rgb(249, 249, 250);
}

#urlbar ::-moz-selection,
#navigator-toolbox .searchbar-textbox ::-moz-selection,
.browserContainer > findbar ::-moz-selection {
  background-color: var(--chrome-selection-background-color);
  color: var(--chrome-selection-color);
}

/* Change the base colors for the browser chrome */

#TabsToolbar,
#navigator-toolbox {
  background-color: var(--chrome-background-color);
  color: var(--chrome-color);
}

#navigator-toolbox > toolbar:not(#TabsToolbar):not(#toolbar-menubar),
.browserContainer > findbar,
#browser-bottombox {
  background-color: var(--chrome-secondary-background-color) !important;
  background-image: none !important;
  color: var(--chrome-color);
}

/* Default findbar text color doesn't look good - Bug 1125677 */
.browserContainer > findbar .findbar-find-status,
.browserContainer > findbar .found-matches,
.browserContainer > findbar .findbar-button {
  color: inherit;
}

/* URL bar and search bar*/
#urlbar:not([focused="true"]),
.searchbar-textbox:not([focused="true"]) {
  border-color: var(--chrome-nav-bar-controls-border-color);
}

#urlbar[pageproxystate="valid"] > #identity-box.verifiedIdentity > #identity-icon-labels:-moz-lwtheme-brighttext {
  color: #30e60b;
}

#urlbar-zoom-button:-moz-lwtheme-brighttext:hover {
  background-color: rgba(255,255,255,.2);
}

#urlbar-zoom-button:-moz-lwtheme-brighttext:hover:active {
  background-color: rgba(255,255,255,.3);
}

/* The menubar and tabs toolbar should match the devedition theme */
#TabsToolbar,
#toolbar-menubar {
  -moz-appearance: none !important;
}
#main-menubar {
  color: var(--chrome-color);
}
#main-menubar > menu:not([open]) {
  color: inherit;
}


PK
!<�� ��Fchrome/browser/skin/classic/browser/connection-mixed-active-loaded.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path d="M12.5 6.984h-.484L4 15h8.5a1.5 1.5 0 0 0 1.5-1.5V8.484a1.5 1.5 0 0 0-1.5-1.5zm-6.5 0V5a2 2 0 0 1 4 0v1l1.892-1.892A4 4 0 0 0 4 5v1.984h-.5a1.5 1.5 0 0 0-1.5 1.5V13.5a1.483 1.483 0 0 0 .07.43l6.946-6.946z" fill="context-fill" fill-opacity="context-fill-opacity"/>
  <path d="M2 15a1 1 0 0 1-.707-1.707l12-12a1 1 0 0 1 1.414 1.414l-12 12A1 1 0 0 1 2 15z" fill="#ff0039"/>
</svg>
PK
!<�A��

Gchrome/browser/skin/classic/browser/connection-mixed-passive-loaded.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path d="M8 1a4 4 0 0 0-4 4v1.984h-.5a1.5 1.5 0 0 0-1.5 1.5V13.5A1.5 1.5 0 0 0 3.5 15h2.535a2.274 2.274 0 0 1 .207-1.318L9.43 7.27a2.266 2.266 0 0 1 .2-.286H6V5a2 2 0 0 1 4 0v1.568A2.255 2.255 0 0 1 11.478 6a2.283 2.283 0 0 1 .522.073V5a4 4 0 0 0-4-4z" fill="context-fill" fill-opacity="context-fill-opacity"/>
  <path d="M15.818 14.127l-3.189-6.411a1.285 1.285 0 0 0-2.3 0l-3.192 6.411A1.294 1.294 0 0 0 8.289 16h6.377a1.294 1.294 0 0 0 1.152-1.873z" fill="#ffbf00"/>
  <path d="M11.478 8a.272.272 0 0 1 .256.161l3.188 6.412a.291.291 0 0 1-.013.291.275.275 0 0 1-.243.137H8.289a.275.275 0 0 1-.243-.137.29.29 0 0 1-.013-.291l3.188-6.412A.272.272 0 0 1 11.478 8m0-1a1.272 1.272 0 0 0-1.152.716l-3.189 6.411A1.294 1.294 0 0 0 8.289 16h6.377a1.294 1.294 0 0 0 1.152-1.873l-3.189-6.411A1.272 1.272 0 0 0 11.478 7z" fill="#d76e00" opacity=".35"/>
  <path d="M11.5 12a.5.5 0 0 0 .5-.5v-2a.5.5 0 0 0-1 0v2a.5.5 0 0 0 .5.5zm0 .809a.691.691 0 1 0 .691.691.691.691 0 0 0-.691-.691z" fill="#fff"/>
</svg>
PK
!<���_��9chrome/browser/skin/classic/browser/connection-secure.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" d="M12,7 L13,7 C13.5522847,7 14,7.44771525 14,8 L14,14 C14,14.5522847 13.5522847,15 13,15 L3,15 C2.44771525,15 2,14.5522847 2,14 L2,8 C2,7.44771525 2.44771525,7 3,7 L4,7 L4,5.00032973 C4,2.79202307 5.79321704,1 8,1 C10.2075938,1 12,2.79481161 12,5.00032973 L12,7 Z M10,7 L10,5.00032973 C10,3.89878113 9.10242341,3 8,3 C6.89748845,3 6,3.89689088 6,5.00032973 L6,7 L10,7 Z"/>
</svg>
PK
!<�����Echrome/browser/skin/classic/browser/controlcenter/conn-not-secure.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="24" height="24">
  <defs>
    <mask id="mask-globe">
      <circle fill="#fff" cx="12" cy="12" r="11"/>
      <path transform="translate(1 1)" fill="#000" d="M8.41648275,2.92371996 C8.38948313,2.92220329 8.35948356,2.92371996 8.33398393,2.93433671 C8.32798401,2.93737006 8.32048413,2.94343677 8.31448421,2.95102016 C8.32348407,2.95102016 8.33398393,2.95102016 8.34148382,2.94950348 C8.36848344,2.94343677 8.38948313,2.92523664 8.41648275,2.92371996 L8.41648275,2.92371996 L8.41648275,2.92371996 Z M8.45098225,4.22654587 C8.48548176,4.18256223 8.40298294,4.14616197 8.35648361,4.14919533 C8.36848344,4.09611162 8.43598246,4.06881142 8.41798272,4.00207762 C8.40148297,3.93231045 8.31748416,3.94596055 8.27248482,3.98387749 C8.23198539,4.01876107 8.21098569,4.07942816 8.17648618,4.12037845 C8.15698646,4.14312862 8.12098698,4.150712 8.10748718,4.1780122 C8.09548735,4.20379571 8.11048713,4.24777936 8.10898715,4.27659623 C8.16448635,4.28417962 8.22298552,4.26749616 8.26348494,4.22806255 L8.29048456,4.21592913 C8.28448464,4.22047916 8.28148468,4.23109591 8.27848473,4.23716262 C8.30548433,4.27052952 8.42548262,4.26142946 8.45098225,4.22654587 L8.45098225,4.22654587 L8.45098225,4.22654587 Z M8.50798144,2.36558267 C8.50498148,2.44748325 8.58448033,2.45658332 8.64297949,2.48843354 C8.62497975,2.53545054 8.56048068,2.53393387 8.53048111,2.5718508 C8.49448162,2.6188678 8.56048068,2.65981809 8.59348021,2.68105158 C8.65797929,2.72048519 8.6219798,2.76598551 8.60997998,2.82210258 C8.59198024,2.9009698 8.7614778,2.87973632 8.79897727,2.87821964 C8.86347635,2.87518629 8.96547488,2.88580303 9.02697399,2.85546948 C9.09297304,2.81906922 9.12747255,2.73868532 9.19647157,2.6992517 C9.25347076,2.6658848 9.33446958,2.64768467 9.39746869,2.67043483 C9.46346774,2.693185 9.45596785,2.77811893 9.50996707,2.81451919 C9.57296618,2.85850283 9.64196518,2.87215293 9.6959644,2.8069358 C9.73046391,2.76598551 9.80696281,2.71441848 9.80996278,2.67043483 C9.81596269,2.59308428 9.83846236,2.53241719 9.92396114,2.51573374 C9.99296016,2.50208364 9.97796036,2.56881745 10.0259597,2.58095087 C10.1324581,2.60825106 10.1834574,2.28368209 10.2989558,2.37923277 C10.3259554,2.40198293 10.3334553,2.48995022 10.3799546,2.48236683 C10.4279539,2.47478345 10.4294539,2.40501628 10.4804532,2.40349961 C10.4969529,2.45051661 10.3919544,2.50815035 10.3784546,2.55971738 C10.4429537,2.50663367 10.4744532,2.51421706 10.5479522,2.50663367 C10.5674519,2.55668403 10.423454,2.63858461 10.3859545,2.64616799 C10.3334553,2.65981809 10.3019557,2.62948454 10.2614563,2.65830141 C10.2299568,2.6795349 10.1864574,2.67801822 10.1489579,2.68105158 C10.0964587,2.68711829 9.99746008,2.75688545 9.99896007,2.81451919 C9.99896007,2.83726935 10.0169598,2.88883638 9.99746008,2.90703651 C9.97946034,2.92675332 9.93596097,2.90855319 9.93146103,2.88883638 C9.88646168,2.95557019 9.82796252,2.83878603 9.79046305,2.93282003 C9.85046218,2.9479868 9.9059614,3.00562055 9.97346042,3.022304 C10.0394595,3.03898745 10.1054585,3.0556709 10.1699576,3.07387103 C10.279456,3.10723793 10.4429537,2.975287 10.5269525,2.91158654 C10.6064514,2.85243612 10.7069499,2.71896851 10.7279496,2.62341783 C10.7519493,2.51876709 10.8659476,2.39894957 10.841948,2.29581551 C10.8209483,2.19874815 10.8059485,2.15324783 10.9154469,2.11836425 C10.9619463,2.10319748 11.0744446,2.07893064 11.0909444,2.02584693 C11.1149441,1.9469797 10.8674476,1.96821318 10.8299482,1.95001306 C10.7054499,1.89389599 10.6514507,1.83171222 10.5074528,1.88782928 C10.4324539,1.91664615 10.3589549,1.94091299 10.280956,1.96214647 C10.2404566,1.97276322 10.1999572,1.97579657 10.1774575,2.01068015 C10.1684576,2.02433025 10.1564578,2.03494699 10.141458,2.04253038 C10.0769589,2.06831389 10.1564578,1.94546302 10.1639577,1.93787964 C10.1849574,1.9136128 10.2194569,1.8392956 10.1519579,1.85597906 C10.0529593,1.87872922 9.98096031,2.03039696 9.87446184,2.03798035 C9.79346301,2.04404706 9.81896264,1.97276322 9.83996235,1.93181293 C9.88046177,1.85749573 9.76496342,1.84839567 9.71396414,1.84839567 C9.64196518,1.84839567 9.58796595,1.88934596 9.51896694,1.89692935 C9.45446788,1.90299606 9.37946895,1.91512947 9.31496987,1.9136128 C9.18597171,1.90906277 9.10047295,1.98489664 8.97297477,1.94242967 C8.8379767,1.89844602 8.69247879,2.01068015 8.56198067,2.02584693 C8.51848128,2.03191364 8.45548219,2.02281357 8.43748245,2.0743806 C8.42248266,2.11684757 8.43748245,2.1820647 8.47048197,2.21391493 L8.48098182,2.20481486 C8.45248223,2.23363173 8.44948228,2.27458203 8.40748288,2.2897488 C8.36698346,2.3033989 8.32648404,2.35648261 8.30548433,2.39288286 C8.28898458,2.42018306 8.24248525,2.53393387 8.32348407,2.47478345 C8.38198324,2.4307998 8.41498277,2.34889922 8.50798144,2.36558267 L8.50798144,2.36558267 L8.50798144,2.36558267 Z M3.65405101,9.54705029 C3.516053,9.44694958 3.20255749,9.42268274 3.24155692,9.18911442 C3.26555658,9.04654674 3.41255447,8.93582929 3.53105277,8.86606213 C3.68705055,8.77506148 3.86104805,8.77202812 4.03654553,8.7871949 C4.07854493,8.79174493 4.15804379,8.78416154 4.18054347,8.81752845 C4.19254329,8.8342119 4.21654295,8.84482864 4.23604266,8.85089535 C4.282542,8.86454545 4.33054131,8.86606213 4.37854064,8.87516219 C4.4505396,8.88881229 4.50153886,8.95099606 4.57353783,8.89942903 C4.65603664,8.84179529 4.66953646,8.82966187 4.77003502,8.84179529 C4.86003373,8.85241203 4.91403295,8.78416154 4.98903188,8.79022825 C5.01303154,8.79174493 5.03403123,8.79629496 5.05203097,8.80387835 C5.05953087,8.77809483 5.07003071,8.75534467 5.08653048,8.75079464 C5.12402995,8.7401779 5.20652875,8.83117854 5.2455282,8.83876193 C5.34452678,8.85999542 5.33852687,8.79022825 5.34602676,8.71894441 C5.39552604,8.70984435 5.4195257,8.77809483 5.46452506,8.73562787 C5.46302509,8.74927796 5.47202495,8.77051145 5.47202495,8.78416154 C5.48102483,8.79022825 5.49152468,8.79022825 5.50052454,8.78264487 C5.50502448,8.77506148 5.50652446,8.76747809 5.50352451,8.75837803 C5.52752416,8.76596142 5.53952399,8.74927796 5.54252394,8.72046109 C5.56052368,8.72197777 5.58902327,8.71136103 5.60702302,8.71439438 C5.62052282,8.66282735 5.6505224,8.59457687 5.61302293,8.54755987 C5.62202281,8.54604319 5.63252265,8.54300983 5.6430225,8.54149316 C5.6430225,8.48992612 5.67752201,8.46717596 5.67902198,8.42622567 C5.62502276,8.41864228 5.56502362,8.42167564 5.50952442,8.42319232 C5.54252394,8.39285877 5.62202281,8.32612496 5.6295227,8.28517467 C5.64452249,8.21237415 5.55002384,8.16839051 5.55752373,8.0819399 C5.56652359,8.12744022 5.61752287,8.23209096 5.66552218,8.24725773 C5.77352063,8.28365799 5.74202108,8.1759739 5.74952097,8.11985683 C5.77652059,7.94240557 5.93551831,8.08952328 5.93851826,8.18204061 C5.9850176,8.07587319 6.10051595,8.19417402 6.0480167,8.28517467 C6.02251706,8.32915832 5.98801756,8.31247486 6.01951711,8.37010861 C6.04201679,8.4110589 6.07501631,8.41257557 6.12301562,8.40195883 C6.13501544,8.37769199 6.1455153,8.3503918 6.1455153,8.32157493 C6.22801412,8.29579141 6.28201334,8.39437545 6.23401403,8.45352586 C6.29551316,8.42015896 6.35851224,8.38830874 6.4245113,8.37162528 C6.38551186,8.23512432 6.34501244,8.1016567 6.37051207,7.95605567 C6.37651198,7.92420544 6.38101192,7.88780518 6.40501158,7.86353835 C6.43501115,7.83168812 6.39751169,7.84382154 6.39451173,7.82258806 C6.38551186,7.7588876 6.45301089,7.69367047 6.47851052,7.63603673 C6.40801154,7.62086996 6.47251061,7.48133563 6.51900994,7.4570688 C6.57000922,7.43128528 6.72000706,7.47375225 6.73050691,7.43280196 C6.76050648,7.44948541 6.78900608,7.47375225 6.82500556,7.47375225 C6.90750438,7.47526892 6.96300358,7.4767856 7.02000276,7.54351941 C7.04850236,7.57840299 7.09050176,7.65575354 7.14150103,7.66182025 C7.14000104,7.71945399 7.20600009,7.76192096 7.13700109,7.81045464 C7.08300187,7.84837157 7.00800294,7.83927151 6.98550326,7.91207202 C6.97050348,7.95757235 6.92700409,7.97880583 6.99300314,8.01065606 C7.02150274,8.02582283 7.05750222,8.02885619 7.08900178,8.02885619 C7.09650167,8.07283983 7.11900135,8.13199025 7.17600052,8.12440686 C7.28699894,8.11075677 7.31099859,7.97122244 7.39949732,7.92420544 C7.54349525,7.84837157 7.52099559,8.1759739 7.6454938,8.09407331 C7.67549337,8.07435651 7.67549337,7.99093925 7.69049315,7.95908902 C7.72049272,7.89235522 7.75499223,7.82410473 7.79849161,7.76495431 C7.85399081,7.68912044 7.92298983,7.61025322 7.89899017,7.51166918 C7.88549035,7.4570688 7.78049187,7.43431863 7.73249254,7.3964017 C7.67549337,7.3493847 7.61999416,7.29933434 7.58549466,7.23411721 C7.56449496,7.1946836 7.55249513,7.1810335 7.58549466,7.1613167 C7.60499438,7.15069995 7.59899447,7.12946647 7.59149458,7.1142997 C7.5554951,7.03846583 7.45799649,6.90348153 7.59449453,6.85646453 C7.6244941,6.84584779 7.6874932,6.74878044 7.69199314,6.7108635 C7.69949303,6.64716305 7.60349441,6.58952931 7.64699378,6.52582885 C7.67849332,6.47881185 7.75799219,6.44847831 7.79849161,6.40146131 C7.81799133,6.37871114 7.84049101,6.35899434 7.87049058,6.35292763 C7.87199055,6.32714411 7.87799047,6.29832724 7.90049015,6.28164379 C7.93648963,6.25434359 7.99348882,6.26799369 8.03548821,6.25434359 C8.10448721,6.23311011 8.13448678,6.16182627 8.18848602,6.12239266 C8.23348537,6.08902576 8.28448464,6.10115917 8.33248395,6.07689234 C8.35798358,6.06475892 8.36848344,6.03745872 8.39398307,6.0253253 C8.45698217,5.99499175 8.52898113,6.04352543 8.55748073,6.09660914 C8.62497975,6.22401005 8.70897855,6.42117811 8.9009758,6.36961108 C8.97897468,6.3483776 9.03747384,6.28467714 9.0614735,6.21187663 C9.08397318,6.14210947 9.05697356,6.08447572 9.0614735,6.01622524 C9.06747341,5.89489104 9.18597171,5.8175405 9.19947152,5.69772298 C9.10947281,5.69923966 9.15597215,5.64312259 9.12597258,5.59155556 C9.09297304,5.53392182 9.01797413,5.57638878 8.96847483,5.56728872 C9.0194741,5.44443785 9.0194741,5.40197088 8.89947583,5.34282046 C8.84697658,5.31703694 8.75997782,5.19570275 8.7194784,5.20025278 C8.75247793,5.15475246 8.83047681,5.23058633 8.85447647,5.25181982 C8.90697572,5.30187017 8.95347505,5.32462033 9.02697399,5.33068704 C9.0059743,5.29883682 8.99547445,5.24120307 9.00897425,5.20480282 C9.02247405,5.17143591 8.98947453,5.13655233 8.99247448,5.09408536 C9.07647329,5.20176946 9.06297347,5.32462033 9.11097278,5.44292117 C9.13047252,5.49297153 9.1799718,5.52633843 9.20097151,5.57790546 C9.22797111,5.64312259 9.20997137,5.64160591 9.26697056,5.68103953 C9.30147007,5.70530637 9.31346989,5.74777333 9.31946981,5.78569027 C9.32996965,5.85242408 9.35396932,5.82360721 9.3899688,5.86152414 C9.41096849,5.8842743 9.46496771,5.88730766 9.45446788,5.93432466 C9.44696797,5.96769156 9.42446829,5.99499175 9.41996837,6.02987534 C9.40646855,6.12997605 9.60296575,5.99347508 9.62246546,5.97982498 C9.66446486,5.94797475 9.73346387,5.94190804 9.76646339,5.90399111 C9.8009629,5.8645575 9.79346301,5.80692375 9.82946249,5.7705235 C9.87446184,5.7235065 9.91796123,5.75535672 9.97196045,5.74625666 C10.0349596,5.73715659 10.0889588,5.68710624 10.1354581,5.6491893 C10.2344567,5.56577204 10.2974558,5.4747714 10.3799546,5.37922072 C10.3439551,5.38832078 10.2209569,5.47780475 10.213457,5.39438749 C10.1669577,5.39438749 10.0589592,5.38680411 10.0424594,5.33523707 C10.0289596,5.29732014 10.0334596,5.25485317 10.0334596,5.21693623 C10.0319596,5.17598594 9.9824603,5.18963604 9.94946077,5.16840256 C9.88346172,5.12593559 9.85046218,5.04706836 9.77996321,5.0106681 C9.66746482,4.95151768 9.59996578,4.85596701 9.53246675,4.75434962 C9.49346731,4.6951992 9.35546929,4.57538168 9.36596914,4.50409784 C9.37196906,4.45708084 9.41096849,4.40703049 9.40796854,4.36001349 C9.40646855,4.31754652 9.37346903,4.29479636 9.37796897,4.24777936 C9.38246889,4.19317897 9.25047079,4.09762829 9.36596914,4.08701155 C9.40196863,4.0839782 9.40796854,4.03999455 9.44846795,4.01572771 C9.49346731,3.98842752 9.48296745,3.96416068 9.53246675,3.97781078 C9.61196561,4.00207762 9.66746482,3.91411032 9.72146405,3.86709332 C9.8144627,3.78519274 9.66596483,3.78367607 9.65846494,3.72149229 C9.65096506,3.65930852 9.61496557,3.61380819 9.60446572,3.54100768 C9.59846581,3.48792397 9.54896651,3.50915745 9.52046693,3.52280755 C9.48146748,3.54100768 9.44246803,3.51370748 9.40496858,3.5061241 C9.37046907,3.49854071 9.34196947,3.44090697 9.29997008,3.4591071 C9.26847053,3.47427387 9.26997051,3.51219081 9.22497116,3.50764077 C9.19197163,3.50460742 9.17097194,3.47275719 9.1379724,3.46669048 C9.08697313,3.46062377 9.13197249,3.50915745 9.0749733,3.51370748 C9.0344739,3.51674084 8.90397575,3.46214045 8.9009758,3.51370748 C8.86047638,3.44394032 8.84397661,3.56224116 8.80047724,3.57437458 C8.75247793,3.58802468 8.70147866,3.57589126 8.65347935,3.59409139 C8.54848085,3.63655836 8.58298036,3.7412091 8.68347892,3.75789255 C8.76447776,3.77002597 8.65947926,3.82614303 8.68647888,3.88074342 C8.71047854,3.9292771 8.71797842,3.962644 8.77047767,3.98236081 C8.85747643,4.01421103 8.95047509,4.03999455 8.92047552,4.15222868 C8.88147609,4.291763 8.78397748,4.42371394 8.64747943,4.48741439 C8.51698131,4.54808149 8.48098182,4.38276365 8.38198324,4.34029668 C8.32048413,4.31451317 8.25148511,4.32361323 8.18698603,4.33119662 C8.17648618,4.34788007 8.27698474,4.37973029 8.29348451,4.40854717 C8.32348407,4.46769759 8.24098526,4.4601142 8.23498534,4.50561452 C8.22898543,4.54353146 8.17948614,4.57083165 8.20648576,4.60874859 C8.17798617,4.57386501 8.12248696,4.62088201 8.10298724,4.64211549 C8.07448765,4.67244904 8.08048756,4.69216584 8.09248739,4.73008278 C8.11648704,4.80743333 7.99948873,4.88781723 7.93048971,4.87871717 C7.87199055,4.8696171 7.81649135,4.87416714 7.76099214,4.84686694 C7.69499309,4.81501672 7.71749277,4.83473352 7.70399297,4.76041633 C7.69049315,4.69064917 7.59449453,4.66031562 7.65149372,4.57386501 C7.69199314,4.51016455 7.67249341,4.51623126 7.66499352,4.45556417 C7.65599366,4.39186371 7.68449323,4.38276365 7.73549251,4.37366358 C7.79099171,4.36304684 7.81499136,4.26446281 7.8479909,4.21744581 C7.85549078,4.20682907 7.88999029,4.11886178 7.83599107,4.14161194 C7.8059915,4.15526204 7.82849118,4.19317897 7.78199184,4.19924568 C7.74899231,4.20531239 7.71599279,4.18256223 7.68149329,4.18256223 C7.64249384,4.18256223 7.60199442,4.20227904 7.56599493,4.1780122 C7.58399467,4.15677871 7.71299283,4.05061129 7.61099429,4.02937781 C7.57049487,4.02027774 7.60349441,4.08701155 7.54949518,4.07791149 C7.53899533,4.1309952 7.47449625,4.12644516 7.44299671,4.15981207 C7.45649651,4.103695 7.57499481,4.06122803 7.53599536,4.01572771 C7.62149415,3.94141052 7.63949389,3.92776042 7.52399554,3.88681013 C7.34249815,3.82310968 7.35599795,3.63655836 7.48049617,3.52432423 C7.59449453,3.42119016 7.78199184,3.2907559 7.89299026,3.46214045 C8.01148856,3.64565842 8.08648747,3.51219081 8.18398608,3.39388997 C8.15098655,3.38023987 8.18098612,3.37113981 8.17048627,3.33018951 C8.06248782,3.37417316 7.9664892,3.23463884 8.03998814,3.15122158 C8.0849875,3.10117122 8.15398651,3.11482132 8.21398565,3.09965455 C8.2664849,3.08600445 8.31448421,3.03443742 8.33698389,2.98590374 C8.29198453,2.99803716 8.29798444,2.97073696 8.31448421,2.95102016 C8.28748459,2.9479868 8.25748502,2.93585338 8.23498534,2.92827 C8.17048627,2.90551983 8.1749862,2.85546948 8.1059872,2.84636942 C7.94248954,2.82210258 8.27698474,2.63403458 8.11348709,2.63403458 C8.06098784,2.6325179 8.01448851,2.55365067 7.97548908,2.56881745 C7.94848946,2.57943419 7.94098957,2.60066767 7.90799003,2.58701758 C7.88549035,2.57791751 7.85849075,2.5582007 7.83149113,2.57336748 C7.77299197,2.60976774 7.76099214,2.56578409 7.70399297,2.58246754 C7.65749363,2.59763432 7.63199399,2.64313464 7.57799476,2.63100122 C7.63199399,2.5582007 7.69799305,2.49753361 7.74599236,2.42169974 C7.77749191,2.3701327 7.81649135,2.3231157 7.8689906,2.29126548 C7.89749018,2.27458203 7.97848902,2.25789857 7.98298896,2.21846496 C7.99048885,2.15324783 7.94998943,2.15931454 7.90499008,2.18509806 C7.78799176,2.25486522 7.66649349,2.32918241 7.55249513,2.40349961 C7.48349612,2.44748325 7.42949689,2.48540019 7.34399812,2.47326677 C7.27799907,2.46265003 7.25099945,2.53545054 7.1985002,2.52938383 C7.17300057,2.42776645 6.61050864,2.79631906 6.54150962,2.82816929 C6.4320112,2.87670296 6.30901296,2.96012022 6.19201463,2.98893709 C6.14401532,3.00107051 6.04351676,3.11330464 6.04951668,2.98893709 C5.98951754,2.98135371 5.94451818,3.0420208 5.90401876,3.07235435 C5.84701958,3.116338 5.77952054,3.14363819 5.71802143,3.18155513 C5.58602333,3.26648906 5.46152511,3.36810645 5.33852687,3.46365713 C5.22152854,3.55465777 5.10453022,3.66082519 4.98153198,3.74272577 C4.93953258,3.77154265 4.7850348,3.85040987 4.78953474,3.90956029 C4.89903316,3.93079378 5.27252782,3.46365713 5.3775263,3.58954135 C5.40452592,3.62139158 5.2185286,3.71542558 5.18852901,3.73362571 C5.16302938,3.74727581 5.13302981,3.74575913 5.10753018,3.75940923 C5.07453065,3.77912603 5.05353096,3.81249294 5.0220314,3.83372642 C4.93803261,3.88681013 4.86753362,3.95506061 4.8060345,4.03089449 C4.76253512,4.08701155 4.73103557,4.15981207 4.68303626,4.2113791 C4.69053615,4.15677871 4.6800363,4.1173451 4.68153629,4.06426139 C4.62003716,4.103695 4.59453753,4.17042881 4.50903876,4.15222868 C4.43103987,4.13402855 4.36954076,4.21289578 4.31404156,4.25536275 C4.1850434,4.35394678 4.09804465,4.46314755 3.9900462,4.57841504 C3.93004706,4.64363217 3.86554798,4.68913249 3.82804851,4.76951639 C3.78754911,4.85596701 3.73054992,4.93331756 3.67805067,5.01370146 C3.57755211,5.16081917 3.46205377,5.29580346 3.36305519,5.44292117 C3.16055809,5.74473998 3.02706,6.09054243 2.86056239,6.41207805 C2.77506361,6.57891256 2.6925648,6.74271373 2.65806529,6.92926505 C2.62806573,7.09003286 2.62656574,7.25383402 2.6295657,7.41763518 C2.72106438,7.34635134 2.71656446,7.49498573 2.6925648,7.53896938 C2.65806529,7.60721986 2.64456548,7.68457041 2.63406564,7.76040428 C2.61906585,7.85898831 2.60106611,7.95757235 2.60106611,8.05767306 C2.60106611,8.14260699 2.5740665,8.22147422 2.57256651,8.3033748 C2.54106697,8.27910796 2.60856599,8.1759739 2.54856686,8.19114067 C2.51856729,8.19872406 2.51706731,8.2427077 2.50956742,8.26545786 C2.48556777,8.34432509 2.3790693,8.3367417 2.36256953,8.42622567 C2.35356965,8.48082606 2.34756974,8.51267628 2.31157026,8.55665993 C2.28307066,8.59002683 2.31007028,8.60519361 2.31607019,8.63856051 C2.32957,8.71742774 2.22757146,8.82662851 2.25457108,8.89032896 C2.28007071,8.95251274 2.26207097,9.0222799 2.29057056,9.08143032 C2.30557034,9.11176387 2.34006985,9.15119748 2.32657005,9.18911442 C2.26207097,9.20124784 2.34456979,9.34988222 2.3505697,9.39538255 C2.35956957,9.46969974 2.42556862,9.70326807 2.4975676,9.73663497 C2.5875663,9.87465261 2.71056454,10.0672706 2.86956225,10.1370378 C2.98206064,10.1855715 3.02256006,10.0399705 3.08555916,9.97930336 C3.16505802,9.90043613 3.26855654,9.85038578 3.37355504,9.81398552 C3.46205377,9.78213529 3.83254845,9.67445119 3.65405101,9.54705029 L3.65405101,9.54705029 L3.65405101,9.54705029 Z M3.81004877,14.5293356 C3.82804851,14.4990021 3.81454871,14.4216515 3.78154919,14.395868 C3.70205032,14.3291342 3.67055078,14.4868687 3.72155004,14.5338857 C3.74104977,14.583936 3.78754911,14.5672526 3.81004877,14.5293356 L3.81004877,14.5293356 L3.81004877,14.5293356 Z M4.1355441,9.9429031 C4.11304444,9.92318629 4.09654467,9.93531971 4.0950447,9.89436942 C4.09654467,9.85796916 4.09954462,9.788202 4.05154531,9.83218565 C4.03804551,9.83673568 4.05604525,9.84431907 4.03654553,9.85190245 C4.02304572,9.85645249 4.01254588,9.84583574 4.003546,9.84128571 C3.97804637,9.83066897 3.96304658,9.82915229 3.94354686,9.85645249 C3.93004706,9.87465261 3.93004706,9.8958861 3.90754738,9.91105287 L3.86854793,9.92470297 C3.85504813,9.929253 3.81604868,9.9565532 3.81454871,9.97171997 C3.8085488,9.99447013 3.8430483,10.0111536 3.86704796,10.0172203 C3.88654768,10.0308704 3.91204732,10.0430038 3.93154704,10.0566539 C3.95104675,10.070304 3.98254631,10.0945708 4.00654597,10.1006376 C4.06054519,10.1309711 4.14454398,10.164338 4.18654338,10.1006376 C4.19854321,10.0763707 4.20754309,10.0581706 4.19104332,10.0369371 C4.17604353,10.0141869 4.15204387,10.0081202 4.14454398,9.9899201 C4.13854407,9.97020329 4.15504384,9.95806987 4.1355441,9.9429031 L4.1355441,9.9429031 L4.1355441,9.9429031 Z M11.3489407,11.0879946 C11.3204411,11.0895112 11.2694418,11.1077114 11.2469422,11.1274282 C11.2019428,11.1668618 11.2979414,11.1896119 11.3354409,11.2002287 C11.3774403,11.2244955 11.4374394,11.2366289 11.4779389,11.2608958 C11.5124384,11.2866793 11.536438,11.3215629 11.5754375,11.3382463 C11.6234368,11.3609965 11.6894358,11.3716132 11.7419351,11.3852633 C11.7644348,11.3928467 11.7989343,11.39133 11.8289338,11.3973968 C11.8619334,11.4171136 11.8769331,11.4474471 11.9039328,11.4686806 C11.9504321,11.5111476 12.0164312,11.5217643 12.0779303,11.5187309 C12.1364294,11.5247977 12.1799288,11.5354144 12.232428,11.5217643 C12.2924272,11.5065975 12.3344266,11.5369311 12.3899258,11.5369311 C12.4124255,11.5369311 12.4349251,11.5187309 12.4559248,11.5202476 C12.4844244,11.5202476 12.4874244,11.532381 12.5009242,11.5566479 C12.5249239,11.5915315 12.586423,11.6446152 12.6299223,11.6461318 C12.656922,11.6461318 12.6779217,11.6415818 12.7004213,11.6491652 C12.725921,11.664332 12.7364208,11.664332 12.7544206,11.6794988 C12.7859201,11.6931489 12.8129197,11.7022489 12.8264195,11.7265158 C12.8504192,11.7689827 12.8489192,11.8159997 12.8864187,11.8493666 C12.9119183,11.8690834 12.9389179,11.8903169 12.9659175,11.9100337 C12.9839173,11.9267172 12.9689175,11.9236838 12.9959171,11.9236838 C13.0109169,11.9267172 13.0394165,11.9267172 13.0589162,11.9206505 C13.1339151,11.9161004 13.0934157,11.809933 13.0694161,11.7750494 C13.0544163,11.7447159 13.0409165,11.720449 13.0469164,11.6916322 C13.0514163,11.6567486 13.0679161,11.6324818 13.0454164,11.6036649 C13.0334166,11.5854648 13.0169168,11.5763647 13.000417,11.5672646 C12.9914172,11.5551312 12.9869172,11.5429978 12.9764174,11.5247977 C12.9539177,11.4959808 12.9104183,11.4868807 12.8819187,11.4595805 C12.8339194,11.4110468 12.8084198,11.3412797 12.7469207,11.2957794 C12.7139211,11.2760626 12.6839216,11.2912293 12.6434222,11.2745459 C12.6269224,11.2639291 12.6179225,11.2533124 12.5939229,11.245729 C12.5714232,11.2381456 12.5519235,11.2426957 12.5309238,11.241179 C12.4874244,11.2381456 12.4499249,11.2002287 12.4079255,11.203262 C12.3614262,11.2093288 12.3524263,11.2593791 12.3254267,11.2866793 C12.3014271,11.3063961 12.2744274,11.3063961 12.2654276,11.2745459 C12.2624276,11.2335956 12.2774274,11.2093288 12.2984271,11.1835452 C12.3314266,11.1486617 12.2984271,11.1289448 12.2549277,11.1259115 C12.1994285,11.1259115 12.1889287,11.1698951 12.166429,11.2169121 C12.1304295,11.2669625 12.1079298,11.2320789 12.0569306,11.2244955 C12.0224311,11.2260122 11.9984314,11.2396623 11.9654319,11.2260122 C11.9429322,11.2184288 11.9384323,11.2002287 11.9219325,11.1896119 C11.8964329,11.1759618 11.8769331,11.1805119 11.8589334,11.1926453 C11.8334338,11.198712 11.8334338,11.198712 11.8079341,11.1850619 C11.7854345,11.1759618 11.7794345,11.1577617 11.750935,11.151695 C11.7059356,11.1425949 11.6594363,11.1805119 11.6219368,11.1698951 C11.605437,11.1607951 11.5919372,11.1365282 11.5694376,11.1289448 C11.5439379,11.1137781 11.5484378,11.1274282 11.5319381,11.1456283 C11.5034385,11.1729285 11.4644391,11.1820286 11.4374394,11.1607951 C11.4029399,11.1365282 11.39994,11.0955779 11.3489407,11.0879946 L11.3489407,11.0879946 L11.3489407,11.0879946 Z M13.0619162,12.8807073 C13.0859158,12.8776739 13.0979156,12.8534071 13.1189153,12.8564404 C13.142915,12.8518904 13.1309152,12.8761572 13.1474149,12.891324 C13.1624147,12.9049741 13.1774145,12.9049741 13.1924143,12.9049741 C13.2194139,12.9095241 13.2719132,12.9125575 13.282413,12.8882907 C13.2944128,12.8503737 13.2314137,12.8427903 13.214914,12.8124568 C13.1999142,12.7684731 13.2344137,12.7260062 13.2464135,12.6880892 C13.2644132,12.6365222 13.1999142,12.613772 13.2059141,12.5728218 C13.2044141,12.5288381 13.2344137,12.5136713 13.2254138,12.4712044 C13.2179139,12.4393541 13.1909143,12.4044706 13.1714146,12.3817204 C13.1534148,12.3574536 13.1189153,12.3347034 13.1219153,12.2998198 C13.1249153,12.2634196 13.1909143,12.2649362 13.1594148,12.2209526 C13.141415,12.1830357 13.0934157,12.190619 13.0499163,12.1845523 C13.0349165,12.1845523 13.0199168,12.186069 13.004917,12.1709022 C12.9914172,12.1496688 12.9974171,12.139052 12.9974171,12.1238852 C12.9884172,12.0829349 12.9569177,12.0677682 12.9209182,12.0510847 C12.9089183,12.045018 12.8909186,12.0374346 12.8834187,12.0192345 C12.8774188,12.0010344 12.8954185,11.9949677 12.8894186,11.9782842 C12.8699189,11.9388506 12.7979199,11.993451 12.7679204,11.9798009 C12.7469207,11.9767675 12.7499206,11.9570507 12.7364208,11.9358172 L12.7004213,11.9191338 C12.6509221,11.8963836 12.6314223,11.9373339 12.6404222,11.9782842 C12.6629219,12.0708015 12.7304209,12.1314686 12.722921,12.2239859 C12.727421,12.2619029 12.7334209,12.280103 12.7469207,12.3149866 C12.7589205,12.365037 12.7724203,12.3893038 12.7499206,12.4378375 C12.7139211,12.4651377 12.7469207,12.4985046 12.7589205,12.5333881 C12.7664204,12.5804051 12.7784202,12.6168054 12.7769202,12.6668558 C12.7679204,12.7578564 12.7394208,12.8473404 12.7469207,12.9398577 C12.7514206,12.9777746 12.7484206,13.0126582 12.7619205,13.0490585 C12.7679204,13.0960755 12.8054198,13.1127589 12.8444193,13.1385424 C12.8819187,13.1703927 13.0559162,13.2750434 13.004917,13.1476425 C12.9899172,13.1188256 12.9659175,13.0778753 12.9584176,13.0445084 C12.9464178,13.0096249 12.9884172,12.985358 12.9899172,12.9489578 C12.9959171,12.9080075 12.9644176,12.8943574 13.0229167,12.8837406 C13.0349165,12.8746406 13.0529163,12.8837406 13.0619162,12.8807073 L13.0619162,12.8807073 L13.0619162,12.8807073 Z M11.2529421,1.95152973 C11.2994414,1.94091299 11.3459408,1.95456309 11.3894401,1.93787964 C11.4119398,1.92877957 11.4854388,1.90299606 11.4809388,1.87266251 C11.4734389,1.81654544 11.2349423,1.8529457 11.1959429,1.86962915 C11.1839431,1.90451273 11.2199426,1.93181293 11.2514421,1.94091299 C11.2514421,1.94394635 11.2529421,1.94849638 11.2529421,1.95152973 L11.2529421,1.95152973 L11.2529421,1.95152973 Z M12.3989257,12.0950684 C12.3899258,12.0753516 12.3989257,12.0571514 12.3989257,12.0374346 C12.3944257,12.0055844 12.3854259,11.9964843 12.3899258,11.9631174 C12.4004256,11.9449173 12.4004256,11.9161004 12.3959257,11.8933503 C12.3869258,11.8751501 12.3719261,11.8599834 12.3584262,11.8463333 C12.3584262,11.8372332 12.3539263,11.8220664 12.3434265,11.8129664 C12.3254267,11.7947662 12.305927,11.8220664 12.2864273,11.8311665 C12.2714275,11.8448166 12.2429279,11.8539167 12.238428,11.8690834 C12.2234282,11.8918336 12.232428,11.9100337 12.232428,11.9297505 L12.236928,11.9525007 C12.2039285,11.9858676 12.235428,12.0738349 12.2309281,12.1072018 C12.2309281,12.1451187 12.1769288,12.2512861 12.2489278,12.2194359 C12.2684275,12.2103359 12.2834273,12.1966858 12.3014271,12.1875857 C12.3254267,12.1739356 12.3554263,12.1739356 12.3839259,12.1648355 C12.3929257,12.1648355 12.445425,12.1602855 12.445425,12.1511854 C12.446925,12.1314686 12.4034256,12.1178185 12.3989257,12.0950684 L12.3989257,12.0950684 L12.3989257,12.0950684 Z M11.0444451,14.0121486 C11.1044442,14.0409655 11.2094427,13.9833318 11.2664419,13.9651316 C11.3384409,13.9423815 11.4524392,13.8665476 11.5259382,13.9090146 C11.5559377,13.925698 11.5709375,13.9605816 11.6024371,13.9742317 C11.6414365,13.9909152 11.6879359,13.9757484 11.7269353,13.9666483 C11.7674347,13.9575482 11.813934,13.9514815 11.8514335,13.9332814 C11.884433,13.916598 11.9054327,13.8892978 11.9339323,13.8680643 C12.0074313,13.8119472 12.0704304,13.8650309 12.1514292,13.8528975 C12.1979285,13.8468308 12.2414279,13.8240806 12.2864273,13.8119472 C12.3194268,13.8043638 12.377926,13.8043638 12.4034256,13.780097 C12.4319252,13.7527968 12.4184254,13.6936464 12.4184254,13.6587628 C12.4169254,13.6117458 12.4199254,13.5632121 12.4034256,13.5192285 C12.3704261,13.4327779 12.2504278,13.3311605 12.374926,13.26291 C12.3989257,13.1112422 12.2219282,13.1370258 12.1709289,13.027825 C12.1379294,12.9565411 12.1274295,12.9019408 12.0329309,12.8943574 C11.953432,12.886774 11.9054327,12.929241 11.8364337,12.9565411 C11.7584348,12.985358 11.6984357,12.9595745 11.6309367,12.9216576 C11.5904373,12.8989074 11.5049385,12.8458237 11.4884387,12.9155909 C11.4734389,12.976258 11.5319381,13.0354084 11.4839388,13.0900088 C11.4419394,13.1385424 11.3684404,13.1597759 11.3084413,13.173426 C11.1779432,13.2007262 11.0744446,13.3008269 10.979946,13.3872775 L10.9904458,13.3963776 C10.9529464,13.3948609 10.8974472,13.4949616 10.8959472,13.5252952 C10.910947,13.5298452 10.9244468,13.5343952 10.9409466,13.5389453 C10.9394466,13.5905123 10.9979457,13.5556287 11.0024457,13.5192285 C11.0144455,13.5222618 11.0264453,13.5298452 11.0384452,13.5313619 C11.048945,13.5343952 11.0714447,13.5328786 11.0804446,13.5374286 C11.1059442,13.5480453 11.1104441,13.5723122 11.1419437,13.5753455 C11.1239439,13.6542128 11.1404437,13.73763 11.1029442,13.8119472 C11.0789446,13.8574475 10.9574463,13.9696817 11.0444451,14.0121486 L11.0444451,14.0121486 L11.0444451,14.0121486 Z M11.6639362,2.34586586 C11.7089356,2.39439954 11.7644348,2.40804964 11.7539349,2.48388351 C11.8109341,2.4914669 11.8469336,2.51270038 11.8799331,2.4641667 C11.9009328,2.43383315 11.9324323,2.40956632 11.9669319,2.3974329 C12.0089312,2.38074944 12.1814288,2.38226612 12.1739289,2.45354996 C12.1694289,2.48843354 12.1484293,2.51876709 12.1424293,2.55365067 C12.1349295,2.60218435 12.1874287,2.56730077 12.2099284,2.57943419 C12.1844287,2.59763432 12.1529292,2.60825106 12.1214296,2.61583445 C12.1349295,2.62493451 12.1439293,2.63706793 12.1454293,2.6522347 C12.1064299,2.66133477 12.0869301,2.77053554 12.0179311,2.79176903 C11.9759317,2.80541912 11.9144326,2.77660225 11.8724332,2.77205222 C11.8229339,2.76598551 11.7854345,2.75081874 11.7359352,2.74778538 C11.6879359,2.74475203 11.7269353,2.68105158 11.6669361,2.693185 C11.6564363,2.73565196 11.677436,2.84333606 11.6849359,2.88580303 C11.6924358,2.93888674 11.7374351,2.96922029 11.7899344,2.97832035 C11.8634333,2.99045377 11.8979328,3.01472061 11.9609319,3.05112087 C12.0104312,3.07842106 12.0659304,3.06173761 12.1199297,3.06628764 C12.1559291,3.069321 12.1859287,3.08297109 12.2129283,3.10572126 C12.2069284,3.12240471 12.1934286,3.1497049 12.2024285,3.16790503 C12.2129283,3.19217187 12.2894272,3.16487168 12.307427,3.163355 C12.3614262,3.15728829 12.4124255,3.09813787 12.4634247,3.10572126 C12.4829245,3.10875461 12.5729232,3.1360548 12.5684232,3.15880497 C12.5204239,3.13908816 12.4889244,3.19823858 12.4514249,3.16638835 C12.4184254,3.13757148 12.3299267,3.16183832 12.3869258,3.20278861 C12.3914258,3.20733864 12.4064255,3.32108945 12.4064255,3.33322287 C12.4019256,3.37568984 12.3254267,3.42119016 12.3314266,3.44849035 C12.3419265,3.45000703 12.4109255,3.45455706 12.4259253,3.46820716 C12.4304252,3.45000703 12.4169254,3.44242364 12.4589248,3.43029022 C12.4904244,3.42119016 12.5279238,3.41815681 12.5594234,3.43332358 C12.5714232,3.48640729 12.5324238,3.54100768 12.6059227,3.52735758 C12.6749217,3.51370748 12.7049213,3.55920781 12.7769202,3.51219081 C12.8219196,3.48489061 12.8699189,3.48944064 12.9089183,3.52887426 C12.9614176,3.58044129 12.8549191,3.65172513 12.9164182,3.70177548 C12.9404179,3.72149229 12.9599176,3.78215939 12.9839173,3.79277613 C13.000417,3.80035952 13.0904157,3.76850929 13.1069155,3.7609259 C13.1354151,3.81249294 13.1609147,3.73362571 13.1834144,3.72907568 C13.1924143,3.69570877 13.2344137,3.65779184 13.2749131,3.65324181 C13.3349122,3.6471751 13.3364122,3.65779184 13.3769116,3.68660871 C13.4954099,3.76850929 13.4789102,3.56830787 13.5404093,3.51674084 C13.6514077,3.42574019 13.7069069,3.33928958 13.7834058,3.22250542 C13.8434049,3.12847142 13.9274038,3.10572126 14.0339022,3.0890378 C14.117901,3.07538771 14.2468992,3.0556709 14.2813987,2.96467025 C14.3218981,2.85850283 14.2243995,2.80086909 14.1389007,2.76901887 C14.0429021,2.73565196 13.9349036,2.6992517 13.976903,2.57791751 C14.0264023,2.43686651 13.982903,2.35496593 13.8329051,2.30946561 C13.5164096,2.21088157 13.2314137,2.04556373 12.9089183,1.95456309 C12.6239224,1.87417918 12.3359266,1.84536231 12.0434308,1.83019554 C11.9129326,1.78469522 11.6369366,1.78014518 11.5574377,1.89086264 C11.5064384,1.96214647 11.5709375,2.02433025 11.5649376,2.09864744 C11.5574377,2.18813141 11.6009371,2.27913206 11.6639362,2.34586586 L11.6639362,2.34586586 L11.6639362,2.34586586 Z M16.6423649,16.4069823 L16.6408648,16.4054657 C16.6453648,16.4130491 16.6423649,16.4266991 16.6438648,16.4373159 C16.702364,16.4373159 16.7278637,16.4903996 16.7908628,16.4706828 C16.8553617,16.4524827 16.8928612,16.3902989 16.841862,16.3387319 C16.7968627,16.2947482 16.7578632,16.2568313 16.6918641,16.2689647 C16.6138653,16.2841315 16.6303649,16.3463152 16.6423649,16.4069823 L16.6423649,16.4069823 L16.6423649,16.4069823 Z M18.6808357,14.3791846 C18.6778357,14.3655345 18.6748358,14.3534011 18.6718357,14.339751 C18.6103366,14.3215508 18.5713371,14.3852513 18.514338,14.3382343 C18.4048396,14.4125515 18.5218379,14.5596692 18.3418405,14.5505691 C18.37334,14.5884861 18.3703401,14.630953 18.3553403,14.6749367 C18.3328406,14.7431872 18.3148408,14.7371204 18.2683415,14.7462205 C18.1708429,14.7613873 18.1243436,14.7007202 18.094344,14.6188196 C17.9983455,14.621853 17.8663472,14.7704874 17.7913484,14.8175044 C17.7718486,14.8281211 17.7373491,14.8599713 17.7163495,14.8736214 C17.6998497,14.8827215 17.6593503,14.9024383 17.6383505,14.9145717 C17.5873513,14.9403552 17.4778528,14.9752388 17.4718529,15.0328725 C17.4463533,15.0283225 17.4073539,15.0434893 17.3818543,15.0404559 C17.3728543,15.0525894 17.3728543,15.0662394 17.3818543,15.0798895 C17.4988526,15.0996064 17.5603516,15.0601727 17.6593503,15.0177058 C17.7628488,14.9706888 17.8738472,14.9813055 17.9713458,14.9449053 C18.0178451,14.9282218 18.0193451,14.8766548 18.095844,14.9069883 C18.1288435,14.9221551 18.167843,14.9706888 18.1753429,15.0040557 C18.1903427,15.0798895 18.1108438,15.1921237 18.0313449,15.1966737 C18.0118452,15.1496567 18.0403448,15.101123 18.0493446,15.0632061 C17.9443462,15.0283225 17.7718486,15.1769569 17.743349,15.2649242 C17.8513475,15.2876744 17.8963469,15.4454088 17.8378476,15.5318595 C17.818348,15.5530929 17.7958484,15.5803931 17.7583489,15.5925266 C17.6968497,15.6107267 17.6683501,15.5546096 17.608351,15.5985933 C17.5303522,15.6577437 17.6158509,15.8200282 17.5708515,15.9110288 C17.5363521,15.980796 17.4778528,16.0065795 17.4283536,16.0551132 C17.395354,16.0899968 17.3758544,16.1279137 17.330855,16.1582472 C17.2723558,16.1976809 17.1298578,16.2826148 17.1418577,16.3645154 C17.2708559,16.408499 17.537852,16.1840308 17.6488504,16.1097136 C17.7193494,16.0626966 17.7628488,15.989896 17.8348477,15.942879 C17.9158466,15.8928287 18.0223451,15.8670452 18.0748442,15.7775612 C18.1048439,15.7259942 18.0808441,15.6804939 18.098844,15.6289268 C18.1153438,15.5834265 18.1468433,15.5682597 18.1768428,15.5333761 C18.2323421,15.4666423 18.2833413,15.4454088 18.3463403,15.3908084 C18.4243393,15.3210413 18.4063395,15.2118405 18.442339,15.1193232 C18.4738386,15.0389393 18.5353378,14.9767555 18.5788371,14.9009216 C18.6463362,14.7811041 18.8233336,14.4959687 18.7483346,14.3609844 C18.7303349,14.3761512 18.6973353,14.3716012 18.6808357,14.3791846 L18.6808357,14.3791846 L18.6808357,14.3791846 Z M20.2123137,10.498007 C20.1733142,10.4252065 20.2318134,10.215905 20.2318134,10.1294544 C20.2303134,9.97020329 20.182314,9.85038578 20.1598145,9.70175139 C20.1463146,9.56221706 20.1268149,9.19214777 20.1778142,9.06626355 C20.2498131,8.88881229 19.8988181,8.58851016 19.8793184,8.39589212 C19.8613187,8.22905761 19.7668201,8.07132315 19.630322,7.97577248 C19.5748228,7.93482218 19.4548246,7.38426828 19.3843255,7.41005179 C19.349826,7.42673525 19.421825,7.54806944 19.4173251,7.58598638 C19.3978254,7.71945399 19.3303263,7.58598638 19.2598273,7.61631992 C19.1293292,7.66940363 18.9913311,7.79528786 18.9748315,7.92420544 C18.9133323,8.40195883 18.5743371,7.90752199 18.6013368,7.88780518 C18.6763357,7.83017144 18.7033352,7.84837157 18.7918339,7.83623815 C18.8893327,7.80135457 18.7378349,7.73158741 18.8908326,7.71642064 C18.8548332,7.6178366 18.935832,7.58598638 18.8968325,7.50863583 C18.8383334,7.39488502 18.7963339,7.40853512 18.8548332,7.28568425 C18.8803327,7.21895044 18.727335,7.00813228 18.7153352,6.92774837 C18.7033352,6.84888115 18.7018353,6.74574708 18.6913354,6.65777979 C18.6853355,6.60166273 18.7768342,6.54857902 18.7603344,6.50459537 C18.7573345,6.3483776 18.7918339,6.1800264 18.7363348,6.02835866 C18.6973353,5.92522459 18.6523361,5.78872362 18.586337,5.70075633 C18.5638373,5.67042279 18.4933383,5.51420501 18.4858384,5.47022136 C18.4678387,5.37315401 18.4243393,5.40955427 18.3703401,5.37163733 C18.3403404,5.3337204 18.2053424,5.20783617 18.1663429,5.18963604 C18.1303435,5.17295259 17.8693472,4.93483423 17.8633473,4.91511743 C17.8408476,4.84686694 17.6938498,4.79378323 17.7103496,4.71946604 C17.7343492,4.60723191 17.3428548,4.31602984 17.2333564,4.29631304 C17.1628574,4.28417962 17.4493532,4.62694872 17.4478533,4.61936533 C17.4523532,4.63908213 17.6323506,4.86810043 17.6323506,4.86810043 C17.6728501,4.88175052 17.7718486,5.16233585 17.7688487,5.19873611 C17.7583489,5.30490353 17.4838527,5.0243182 17.4628531,4.98640127 C17.327855,4.82108343 17.0833585,4.68761581 16.9288607,4.57689836 C16.8193624,4.47528097 16.8733616,4.41764723 16.6858642,4.33119662 C16.6168652,4.29934639 16.4293679,4.14616197 16.3708688,4.14161194 C16.3003698,4.13857858 16.3813685,4.28721297 16.3828686,4.30389642 C16.3933683,4.41006384 16.5088668,4.41764723 16.5778657,4.48741439 C16.6333649,4.54504813 16.6828643,4.61633197 16.6348649,4.68003243 C16.6333649,4.68003243 16.5478663,4.83776688 16.5433663,4.82411678 C16.5673659,4.89085059 16.766863,5.0379683 16.8163624,5.09256869 C16.8073624,5.07891859 17.0773586,5.43230443 17.0998583,5.24423643 C17.1088581,5.17598594 17.0323592,5.09408536 17.0428591,5.03493494 C17.0503591,4.99853468 17.3518546,5.37922072 17.3683544,5.41258762 C17.4418534,5.61430572 17.4358535,5.38225407 17.5123523,5.40652091 C17.5753514,5.42623772 17.7448491,5.63705588 17.603851,5.64615595 C17.3893541,5.65980604 17.6428505,5.84635737 17.7028496,5.87517424 C17.8273479,5.93584134 17.9128467,6.07689234 18.0418448,6.12997605 C18.2443419,6.21187663 18.2023425,6.35747766 18.304341,6.50611205 C18.3373405,6.55312905 17.7223494,6.50611205 17.67435,6.53341224 C17.5933512,6.59711269 17.9833456,7.03239912 17.9848456,7.09761624 C17.9878456,7.23411721 18.0658444,7.33421792 18.0868442,7.47071889 C18.098844,7.59660312 18.0838442,7.76192096 18.1663429,7.86353835 C18.233842,7.92572212 18.2968411,7.76040428 18.4033395,7.85898831 C18.442339,7.87718844 18.4978383,7.92117209 18.5098381,7.95453899 C18.5458375,8.04857299 18.7498347,8.62339374 18.5053381,8.58092677 C18.3973396,8.56120996 18.4588388,9.03289664 18.4663386,9.10114713 C18.4993382,9.24978151 18.5593373,9.23764809 18.5203379,9.4257161 C18.5248379,9.58193387 18.3988397,9.6608011 18.3088409,9.77303523 C18.2668416,9.82460226 18.2398419,9.88526936 18.2203422,9.95048649 C18.1708429,9.90195281 18.1468433,9.82460226 18.0778442,9.79881874 C18.0028453,9.77000187 17.8378476,9.86100252 17.7718486,9.89285274 C17.612851,9.97323665 17.7298493,10.1188377 17.6668501,10.2447219 C17.6218508,10.3372392 17.4868527,10.3827396 17.3983539,10.4282399 C17.2918555,10.4828403 17.1493575,10.540474 17.044359,10.4525067 C16.9543604,10.3797062 16.9933599,10.2325885 16.8988611,10.168888 C16.7923627,10.0976042 16.7818629,10.2568553 16.7578632,10.3205558 C16.7113639,10.4403733 16.5718659,10.4813236 16.6078654,10.6314747 C16.6228651,10.6936584 16.6543647,10.7497755 16.6663645,10.8119593 C16.6813642,10.8893098 16.6423649,10.959077 16.6378649,11.0364275 C16.628865,11.1683785 16.766863,11.198712 16.8028626,11.3063961 C16.8343621,11.4034635 16.8013625,11.5536145 16.6933642,11.5900148 C16.5778657,11.6309651 16.4503675,11.5263143 16.3363691,11.5141809 C16.2223709,11.5020475 16.076873,11.5338977 16.0558732,11.664332 C16.0363735,11.7795995 16.1533718,11.8736335 16.0963726,11.9919343 C16.072373,12.0419847 16.0303736,12.0814183 16.0003741,12.1269186 C15.9478747,12.2027525 15.9178752,12.2907198 15.868376,12.3680703 C15.9268751,12.369587 15.9208752,12.3331867 15.9733744,12.3438035 C16.0288737,12.3559369 16.0783729,12.2998198 16.1248722,12.2816197 C16.1338722,12.31802 16.1293722,12.3559369 16.1338722,12.3923371 C16.1728715,12.4044706 16.211871,12.3893038 16.2463705,12.3756537 C16.2508705,12.4075039 16.2358706,12.4439042 16.2493705,12.4757544 C16.2598704,12.5030546 16.2913699,12.5121547 16.3093696,12.5333881 C16.3558689,12.5895052 16.2988697,12.6820225 16.2658703,12.7305562 C16.1713716,12.8700905 16.0093739,12.9474411 15.9043754,13.0778753 C15.8053768,13.1992095 15.7963769,13.3463272 15.7228781,13.4782782 C15.6973784,13.5237785 15.6718788,13.5859623 15.7393777,13.6071958 C15.7543776,13.5829289 15.7768773,13.5632121 15.8083769,13.5632121 C15.8548761,13.5616954 15.8368765,13.596579 15.860876,13.6238792 C15.9583746,13.7482468 16.0618731,13.5434953 16.1038725,13.4843449 C16.145872,13.4206444 16.3198695,13.3250938 16.3678688,13.4312612 C16.4053683,13.5116451 16.3618688,13.6253959 16.3243694,13.6981964 C16.3873684,13.7270133 16.3693687,13.7725136 16.3843685,13.827114 C16.4038682,13.9029479 16.4728672,13.9514815 16.4728672,14.0348988 C16.4728672,14.1349995 16.2538705,14.3336843 16.3393692,14.4095181 C16.4458677,14.5035521 16.5718659,14.2426836 16.6138653,14.1865665 C16.6933642,14.0788824 16.8748615,14.0637157 16.9198609,13.9317647 C16.9693601,13.7816137 16.9528603,13.6921297 17.1523575,13.6875797 C17.2363564,13.686063 17.2978554,13.6329793 17.3773543,13.6162958 C17.4643531,13.5996124 17.5033525,13.5905123 17.5588518,13.5237785 C17.6383505,13.4282278 17.7103496,13.5419786 17.7133495,13.6178125 C17.7163495,13.6951631 17.68185,13.7922304 17.7298493,13.8619976 C17.7883484,13.9469315 17.8528475,13.8346974 17.9128467,13.780097 C17.9068468,13.8377307 17.9833456,13.8710976 18.028345,13.8892978 C18.0973439,13.8422808 18.1408433,13.7649302 18.2143423,13.7224632 C18.2488418,13.7027464 18.2878413,13.6951631 18.3268407,13.6890963 C18.3373405,13.7512801 18.3478404,13.8180139 18.4033395,13.8483475 C18.4828384,13.8938478 18.3853398,13.9378314 18.4903384,13.9863651 C18.6208365,14.0364155 18.6643359,14.1683664 18.727335,14.2745338 C18.7573345,14.3230675 19.0303306,14.0273154 19.1353291,14.0166987 C19.4833242,13.9772651 19.6648215,13.5237785 19.7938196,13.2553266 C19.9798171,12.8716072 20.0638158,12.4454209 20.1118151,12.049568 C20.2303134,11.805383 20.2783127,11.3579631 20.2303134,11.0743445 C20.2018138,10.9014432 20.3008124,10.6618082 20.2123137,10.498007 L20.2123137,10.498007 L20.2123137,10.498007 Z M17.0623589,16.2568313 C17.0713587,16.2143643 17.1613573,16.0566299 17.0773586,16.0338797 C17.047359,16.0262963 17.0218595,16.0717966 16.9948598,16.07938 C16.9588602,16.0915134 16.9198609,16.0687633 16.8868613,16.0854467 C16.8568618,16.1021302 16.8283622,16.1506639 16.8103624,16.177964 C16.7878628,16.211331 16.7968627,16.224981 16.832862,16.2431812 C16.8688616,16.262898 16.9153609,16.271998 16.9363607,16.3114317 C16.9543604,16.3463152 16.9453605,16.3933322 16.9408605,16.4297325 C16.9408605,16.4282158 16.9468604,16.4221491 16.9483605,16.4160824 C16.9543604,16.4145657 16.9648603,16.4130491 16.9708602,16.4145657 L16.9633602,16.4297325 C17.0593589,16.4448993 17.051859,16.319015 17.0623589,16.2568313 L17.0623589,16.2568313 L17.0623589,16.2568313 Z M18.1858427,14.3594678 C18.1888426,14.4049681 18.238342,14.4004181 18.2713415,14.3882846 C18.3013411,14.3791846 18.3208408,14.3534011 18.3403404,14.3306509 C18.3673401,14.2957673 18.3838399,14.2608837 18.3598402,14.2199334 C18.3358406,14.1774665 18.3208408,14.1471329 18.3088409,14.0970826 C18.2893412,14.1076993 18.2653416,14.1258994 18.2443419,14.1319662 C18.2233421,14.1395495 18.2203422,14.1334828 18.1963426,14.1319662 C18.1408433,14.1304495 18.1528432,14.1729164 18.1288435,14.2078 C18.1093437,14.2381336 18.0658444,14.250267 18.0823442,14.2897006 C18.094344,14.3200342 18.1438434,14.3458177 18.172343,14.3594678 L18.1798428,14.3503677 C18.1783429,14.3534011 18.1768428,14.3549177 18.1753429,14.3579511 C18.1783429,14.3594678 18.1828427,14.3594678 18.1858427,14.3594678 L18.1858427,14.3594678 L18.1858427,14.3594678 Z M12.7709203,17.6157743 C12.8264195,17.5171902 12.9254181,17.4550064 13.0139168,17.386756 C13.1204153,17.3048554 13.214914,17.2093047 13.2989128,17.1061706 C13.2494135,17.0940372 13.2464135,17.0470202 13.214914,17.0166867 C13.1669147,16.9696697 13.0934157,17.0000032 13.0829159,16.9287194 C13.072416,16.8650189 13.0199168,16.8513688 12.9689175,16.8255853 C12.8519192,16.7664349 12.8039199,16.6511674 12.7094212,16.5707835 C12.6059227,16.4797829 12.4649247,16.5101164 12.3389265,16.4858496 C12.2279281,16.4646161 12.1154297,16.2871648 11.9969314,16.3629987 C11.9219325,16.4100157 11.887433,16.5298332 11.9384323,16.6041504 C11.9789317,16.6617841 12.0479307,16.690601 12.0749303,16.7588515 C12.0344309,16.7967684 12.025431,16.8210353 12.0749303,16.8559189 C12.1334295,16.8968691 12.2459278,16.9393361 12.2174283,17.0288201 C12.2024285,17.0773538 12.1619291,17.1243708 12.1094298,17.1334708 C12.0719303,17.1410542 11.9804317,17.1198207 11.9999314,17.1880712 C11.957932,17.0758371 11.8394337,17.2517717 11.7749346,17.1592543 C11.7224354,17.0849372 11.6894358,17.0182033 11.606937,16.968153 C11.5004385,16.9029359 11.6504364,16.8392354 11.6324367,16.7406514 C11.605437,16.596567 11.4299395,16.6360006 11.3669405,16.5298332 C11.329441,16.4691661 11.3804403,16.4206324 11.4044399,16.3675487 C11.4284396,16.3129483 11.4929386,16.356932 11.5274382,16.3720988 C11.6399365,16.4251825 11.8049342,16.4054657 11.8994328,16.3281151 C11.9429322,16.2917149 12.0389308,16.1233637 11.9174326,16.1233637 C11.8334338,16.1248803 11.7689347,16.1976809 11.6894358,16.2022309 C11.677436,16.1066802 11.6324367,15.9443957 11.7344352,15.8806953 C11.8274339,15.8230615 12.0224311,15.7563277 11.9399322,15.6076933 C11.9099327,15.5561263 11.8574334,15.6410602 11.813934,15.6031433 C11.7959343,15.5879765 11.8094341,15.5530929 11.815434,15.5364095 C11.7869344,15.510626 11.7599348,15.4787758 11.744935,15.4423755 C11.678936,15.2816076 11.8739332,15.1557234 11.7914344,14.9858555 C11.7569349,14.9145717 11.6984357,14.8721047 11.6339366,14.8281211 C11.5694376,14.7826208 11.5664376,14.724987 11.5424379,14.6567365 C11.5304381,14.6188196 11.467439,14.5293356 11.4134398,14.5520858 C11.3669405,14.5702859 11.3534406,14.6506698 11.3174412,14.6840367 C11.2364423,14.762904 11.0654448,14.7932375 10.9574463,14.767454 C10.8704476,14.7477372 10.8749475,14.7143703 10.8329481,14.6567365 C10.8194483,14.6355031 10.7894487,14.6339864 10.766949,14.6248863 C10.7279496,14.6097195 10.7234497,14.574836 10.7144498,14.5399524 C10.6784503,14.4080014 10.4174541,14.5718026 10.3769546,14.386768 C10.3679548,14.344301 10.3829546,14.2730172 10.3214554,14.2639171 C10.2524564,14.2517837 10.2494565,14.1850499 10.2494565,14.1289328 C10.2494565,14.0834325 10.2524564,14.019732 10.2044571,13.9939485 C10.142958,13.9605816 10.1279582,13.9757484 10.1084585,13.9059812 C10.0859588,13.8164972 10.0244597,13.9090146 9.97346042,13.8908144 C9.86546198,13.8483475 9.88496169,13.9014312 9.79796295,13.9469315 C9.64946507,14.0257987 9.63296531,13.6602795 9.58196604,13.5889956 C9.48296745,13.4524947 9.5084671,13.7573468 9.44096806,13.7952638 C9.37946895,13.8301473 9.31496987,13.7497634 9.29247019,13.7027464 C9.27897039,13.6754462 9.2714705,13.6451127 9.25497073,13.6178125 C9.22947109,13.5783789 9.18447174,13.5616954 9.15897211,13.5222618 C9.1379724,13.4873782 9.10647286,13.446428 9.09147307,13.408511 C9.07797327,13.3751441 9.08097322,13.3326771 9.05547359,13.305377 C9.02397404,13.2704934 9.06297347,13.2128596 9.08547316,13.168876 C9.1244726,13.1537092 9.18297177,13.1840428 9.21147134,13.2098263 C9.28197034,13.2689767 9.38846881,13.5283285 9.51146705,13.4797949 C9.48596742,13.446428 9.50246719,13.4054777 9.48446744,13.3690774 C9.46496771,13.3311605 9.42746826,13.3084103 9.39896866,13.2780768 C9.33596956,13.2052762 9.26697056,13.1324757 9.22497116,13.0445084 C9.18897168,12.9686746 9.17097194,12.8882907 9.09747298,12.835207 C9.03747384,12.7912233 8.91897554,12.7487563 8.94447517,12.6547223 C8.94447517,12.6532057 8.94597516,12.651689 8.94597516,12.651689 C8.99547445,12.6623057 9.02997396,12.698706 9.06597344,12.7305562 C9.11847269,12.7760565 9.18597171,12.7988067 9.24747084,12.8291402 C9.35996923,12.8837406 9.49046736,12.9216576 9.58646598,13.0050748 C9.64646512,13.0551252 9.61496557,13.1673593 9.68996449,13.2295431 C9.7454637,13.2750434 9.82796252,13.4282278 9.92996105,13.3675607 C9.96746051,13.3448106 9.98396028,13.3008269 10.0214597,13.2750434 C10.0619592,13.2462265 10.1294582,13.220443 10.1759575,13.1992095 C10.2044571,13.1855594 10.2524564,13.1901095 10.2749561,13.1673593 C10.3109556,13.1324757 10.2254568,13.032375 10.2059571,13.0065915 C10.1294582,12.9080075 10.0589592,12.8003234 9.95696066,12.7260062 C9.90446142,12.6880892 9.85346215,12.647139 9.79196304,12.6213554 C9.7589635,12.6077053 9.70046434,12.609222 9.69446443,12.5637217 C9.71096419,12.5743384 9.71996406,12.5713051 9.72296402,12.5546216 C9.72146405,12.5242881 9.67796466,12.5227714 9.65696497,12.5167047 C9.60596569,12.5030546 9.57146619,12.5030546 9.55196647,12.4605876 C9.52646684,12.4090206 9.43646812,12.4105373 9.38846881,12.3984039 C9.31646984,12.3802037 9.25947067,12.3256033 9.19047165,12.2983031 C9.10947281,12.2679696 9.0479737,12.2998198 8.96847483,12.31802 C8.95497503,12.3210533 8.92797542,12.3680703 8.90247578,12.4075039 C8.8454766,12.3938538 8.7824775,12.3999205 8.73447819,12.4378375 C8.66247923,12.4939545 8.62047983,12.5788885 8.56348064,12.6486556 C8.53948099,12.6774725 8.50498148,12.7062894 8.47048197,12.6956726 C8.46448206,12.6926393 8.46748202,12.6850559 8.46298208,12.6820225 C8.50648145,12.3711037 8.53048111,12.0571514 8.50198153,12.1193352 C8.44498234,12.2406694 8.40298294,12.3256033 8.35948356,12.4135706 C8.29648447,12.3862704 8.22298552,12.3847538 8.19598591,12.4514876 C8.16748632,12.5227714 8.20498577,12.6152887 8.15998643,12.6774725 C8.14948658,12.694156 8.13298681,12.6926393 8.11798703,12.7002227 C8.11498707,12.6926393 8.09998729,12.6729225 8.10148726,12.6714058 C8.08798746,12.694156 8.08648747,12.7017393 8.07448765,12.7214561 C8.0294883,12.7229728 7.97398909,12.7047727 7.91848989,12.6865726 C7.91848989,12.6865726 7.91848989,12.6820225 7.91698991,12.6820225 C7.91548992,12.6835392 7.91548992,12.6835392 7.91398995,12.6850559 C7.8479909,12.6623057 7.77899188,12.6410722 7.71899274,12.6623057 C7.59899447,12.7047727 7.59449453,12.8761572 7.51799562,12.980808 C7.40399726,13.1400591 7.11000147,13.0718086 7.05900221,12.882224 C7.10100161,12.8306569 7.14150103,12.7790899 7.18350043,12.7275229 C7.11900135,12.5637217 6.94650382,12.4499709 6.77250631,12.4545209 C6.72300702,12.4560376 6.67050778,12.4651377 6.62550841,12.4439042 C6.57900908,12.421154 6.55350945,12.3726203 6.51301003,12.3422868 C6.38701184,12.2482528 6.21901425,12.3726203 6.11401575,12.4894045 C5.93401832,12.519738 5.76752071,12.6213554 5.65502233,12.7669565 C5.58302336,12.7608898 5.5110244,12.754823 5.4405254,12.7487563 C5.4825248,12.8640238 5.34302681,12.9580578 5.28002771,13.0642252 C5.20202883,13.1931428 5.24252825,13.3432939 5.31902714,13.4813115 C5.31002728,13.502545 5.30402736,13.5252952 5.28452765,13.5343952 C5.19302895,13.5829289 5.21702861,13.6117458 5.24402822,13.7103298 C5.26802788,13.7952638 5.259028,13.9545149 5.23952829,14.0394488 C5.22452851,14.1061826 5.15852944,14.2669504 5.07753061,14.2290335 C5.03553122,14.2078 4.99053186,14.1911166 4.95153241,14.2305502 C4.93353267,14.2472336 4.92153284,14.2684671 4.91553293,14.2912173 C4.88853332,14.292734 4.8615337,14.2957673 4.83603407,14.301834 C4.7850348,14.3124508 4.73103557,14.3245842 4.68153629,14.3033507 C4.63203699,14.2821172 4.56603794,14.2411669 4.51053874,14.2593671 C4.4640394,14.2745338 4.37254072,14.3154841 4.35304099,14.3640178 C4.34404113,14.3852513 4.37254072,14.4413683 4.37254072,14.4701852 C4.37104073,14.5217523 4.41154015,14.5991028 4.39504039,14.6461198 C4.35754093,14.6279197 4.30204173,14.621853 4.27654209,14.583936 C4.25254243,14.5520858 4.21654295,14.5596692 4.18954334,14.5263023 C4.18354343,14.583936 4.16254372,14.6643199 4.09354471,14.6794867 C4.02754566,14.6946535 3.96304658,14.6415698 3.89554755,14.6597699 C3.71855009,14.7052702 4.00204603,14.9267051 4.03954548,14.9691721 C4.10254459,15.0404559 4.12354428,15.1344899 4.17004361,15.2148738 C4.22104289,15.3028411 4.32604139,15.331658 4.3875405,15.4090085 C4.43853977,15.4742257 4.44903961,15.562193 4.52253857,15.6107267 C4.60353741,15.6668438 4.67703635,15.7168941 4.71003588,15.8124448 C4.74453538,15.7790779 4.81653436,15.9368123 4.8900333,15.8109281 C4.92903274,15.7426776 4.99803174,15.6835272 5.04153113,15.7942447 C5.07903059,15.8897953 5.04153113,15.9504624 5.12402995,16.0338797 C5.18702904,16.0990968 5.18402909,16.1764474 5.07603062,16.168864 C5.09403037,16.2173977 5.12252996,16.2659313 5.07603062,16.309915 C5.05503093,16.3311485 4.9950318,16.3751321 5.03853117,16.4054657 C5.08803045,16.3827155 5.1405297,16.3690654 5.190029,16.3463152 C5.24402822,16.3235651 5.29652747,16.2689647 5.35952656,16.2704814 C5.36252653,16.2901982 5.27552777,16.3478319 5.32952699,16.352382 C5.37452635,16.356932 5.43902543,16.309915 5.47502491,16.3493486 C5.51552433,16.3918156 5.46452506,16.4570327 5.48852472,16.5040497 C5.51252437,16.5525834 5.59352321,16.5161831 5.63252265,16.5237665 C5.61602289,16.5662335 5.55302379,16.5586501 5.51852428,16.5753335 C5.59802315,16.6724009 5.49452463,16.8149686 5.37902629,16.8180019 C5.32352708,16.8180019 5.1270299,16.592017 5.11803004,16.737618 C5.11653005,16.780085 5.13002986,16.8331687 5.14352967,16.874119 C5.16152941,16.9272027 5.29052756,16.9044525 5.3370269,16.9241693 C5.40452592,16.9514695 5.4825248,17.0166867 5.50952442,17.0849372 C5.53502405,17.1547043 5.59352321,17.2002046 5.61602289,17.2669384 C5.65952227,17.391306 5.77802057,17.4079894 5.90101881,17.4443897 C6.06451645,17.4929234 5.9850176,17.7462085 5.97601774,17.8675427 C5.96851783,17.9873602 6.13651543,18.0161771 6.21301434,18.0844276 C6.29851311,18.1587448 6.31201291,18.3028291 6.17101494,18.3149625 C6.09901596,18.3210293 5.97901769,18.2876624 5.95201807,18.3801797 C5.91301863,18.5106139 6.11101579,18.4969638 6.19801455,18.5257807 C6.23851397,18.5394308 6.40201163,18.5561143 6.41701141,18.5940312 C6.43951109,18.6531816 6.42151135,18.7335655 6.44251104,18.7957493 C6.49351031,18.9550004 6.64050821,19.0611678 6.78300616,19.1400351 C7.08900178,19.3114196 7.45199657,19.4191037 7.78799176,19.5146544 C7.97548908,19.5692548 8.16598634,19.6117217 8.35798358,19.6375053 C8.54398093,19.6617721 8.70747857,19.6466053 8.86647629,19.7497394 C8.97597473,19.8210232 9.04497373,19.7679395 9.15747212,19.7876563 C9.20547143,19.7967564 9.22647114,19.84074 9.26247062,19.8665236 C9.30297004,19.8983738 9.34946938,19.8574235 9.39296875,19.8741069 C9.40046864,19.8452901 9.39746869,19.8179899 9.38396888,19.7906897 C9.46946765,19.8225399 9.57446615,19.9135406 9.66446486,19.84074 C9.70946422,19.8043398 9.73946379,19.7542894 9.78596312,19.7194058 C9.84146232,19.7239559 9.89546155,19.7269892 9.95096074,19.7269892 C10.1834574,19.7269892 10.3664548,19.6208218 10.5524521,19.4964543 C10.7519493,19.3629866 10.9919458,19.3599533 11.2229425,19.3387198 C11.467439,19.314453 11.7269353,19.2826027 11.9504321,19.1764353 C12.1454293,19.0824013 12.1949286,18.9110168 12.2504278,18.7214321 C12.3104269,18.515164 12.4904244,18.4317467 12.6089227,18.2649122 C12.7484206,18.0707775 12.656922,17.8174923 12.7709203,17.6157743 L12.7709203,17.6157743 L12.7709203,17.6157743 Z M3.82354859,14.8796881 C3.81604868,14.8311544 3.79804894,14.8008209 3.76054949,14.772004 C3.75904951,14.7765541 3.75754954,14.7795874 3.75754954,14.7856541 C3.70655026,14.7598706 3.70055035,14.6628033 3.63155133,14.6703866 C3.57455216,14.579386 3.43055421,14.6203363 3.38555487,14.7037535 C3.3555553,14.7598706 3.39155478,14.7856541 3.4245543,14.8266044 C3.4665537,14.8781714 3.45905381,14.9176051 3.4740536,14.9767555 C3.51155306,15.1299399 3.63905124,15.0252892 3.72904994,15.0844396 C3.76354945,15.1071897 3.77704925,15.1724069 3.82804851,15.1617901 C3.8850477,15.14814 3.88204775,15.0632061 3.86704796,15.0252892 C3.84604827,14.9706888 3.83104848,14.9388385 3.82354859,14.8796881 L3.82354859,14.8796881 L3.82354859,14.8796881 Z M5.0220314,16.686051 C5.03103128,16.6602675 4.99053186,16.6314506 4.96353224,16.6329673 C4.93953258,16.634484 4.91703292,16.6678509 4.90803304,16.686051 C4.87803347,16.7391347 4.911033,16.8104185 4.980032,16.8104185 C4.99653177,16.7816017 4.99053186,16.7254846 5.03403123,16.7209346 C5.03103128,16.7072845 5.0220314,16.7027344 5.01003157,16.6981844 L5.0220314,16.686051 L5.0220314,16.686051 Z M3.74704968,14.7613873 C3.75154962,14.7644206 3.75604955,14.767454 3.76054949,14.772004 C3.76504943,14.7644206 3.76954936,14.7583539 3.77254931,14.7492539 L3.74704968,14.7613873 L3.74704968,14.7613873 Z M17.8018483,16.8892858 C17.7763486,16.9135526 17.7838484,16.9408528 17.7718486,16.9696697 C17.7583489,17.0045532 17.7058496,17.0182033 17.67735,17.0364035 C17.6323506,17.0652203 17.6173508,17.1334708 17.5678516,17.1501543 C17.5468518,17.1183041 17.5153523,17.0288201 17.4718529,17.1061706 C17.4463533,17.156221 17.4628531,17.2062713 17.4223537,17.2517717 C17.3818543,17.2942386 17.3848542,17.3458057 17.3563546,17.3928227 C17.3128551,17.4671399 17.2693558,17.5111235 17.195857,17.5551072 C17.1373577,17.5899907 17.1253579,17.6552079 17.0833585,17.7037415 C17.0383591,17.7568253 16.9693601,17.7765421 16.9078611,17.8038423 C16.8643616,17.8220424 16.7923627,17.8736094 16.7428634,17.8447925 C16.6768644,17.8038423 16.766863,17.7310417 16.7983626,17.7052582 C16.8283622,17.6809914 16.9708602,17.5945408 16.9378606,17.5460071 C16.9153609,17.5141569 16.8343621,17.5187069 16.8013625,17.5217402 C16.7338636,17.5293236 16.6828643,17.5975741 16.6258651,17.6294243 C16.561366,17.6658246 16.5058668,17.6961582 16.4353678,17.720425 C16.354369,17.7492419 16.3483691,17.8250757 16.28387,17.8705761 C16.2328707,17.9100097 16.1668716,17.9403432 16.1008726,17.9403432 C16.0138739,17.9403432 16.0183738,17.8675427 15.9928741,17.8068756 C15.9598747,17.8114256 15.9313751,17.8538926 15.8998755,17.8675427 C15.8488762,17.8887762 15.8158767,17.913043 15.8383763,17.9691601 C15.859376,18.0267938 15.6268794,18.0798775 15.58638,18.1086944 C15.5773802,18.0798775 15.6223794,18.049544 15.6403792,18.0328605 C15.5683802,18.0267938 15.4873814,18.0889776 15.4138824,18.0995943 C15.3433835,18.1086944 15.2563847,18.1572281 15.2458848,18.2300286 C15.238385,18.2876624 15.1618861,18.2861457 15.1138867,18.3058625 C15.0343879,18.3392294 15.0673874,18.3862464 15.0523876,18.4499468 C15.0223881,18.5697644 14.7718916,18.4787637 14.8993898,18.3195126 C14.9443892,18.2633955 15.0118882,18.2269953 15.0523876,18.1693615 C15.098887,18.1026277 15.1063869,18.0192104 15.1423863,17.9479266 C15.0628875,17.9706768 15.0028883,18.0161771 14.9353893,18.0601607 C14.8588904,18.1102111 14.7943913,18.0980777 14.7088926,18.0813942 C14.609894,18.0601607 14.540895,18.1102111 14.4493963,18.1329613 C14.3908971,18.1466113 14.260399,18.1420613 14.2528991,18.2269953 C14.2483992,18.2815956 14.3428978,18.2922124 14.3743973,18.3240626 C14.4208967,18.3725963 14.471896,18.4438801 14.5108954,18.4984805 C14.5423949,18.5409475 14.6593933,18.6000979 14.6518934,18.6546983 C14.6398935,18.7608657 14.5063954,18.7426656 14.4343965,18.7608657 C14.3623975,18.7790658 14.3518977,18.844283 14.2993984,18.8837166 C14.2393993,18.9277002 14.1584004,18.8761332 14.0924014,18.9034334 C14.0249024,18.9292169 13.976903,18.989884 13.9184039,19.0293176 C13.8194053,19.0975681 13.7549062,19.0232509 13.6529077,19.0187009 C13.5704089,19.0156675 13.4954099,19.0535845 13.4174111,19.0702679 C13.3469121,19.0854347 13.2614133,19.0975681 13.2074141,19.1476185 C13.0889158,19.2537859 13.4699103,19.224969 13.5059098,19.2219356 C13.48941,19.2826027 13.4729103,19.3538866 13.4174111,19.3933202 C13.3484121,19.4433706 13.2509134,19.4312371 13.1714146,19.4494373 C13.1099155,19.464604 13.0244167,19.5328545 13.1144154,19.5813882 C13.1954142,19.6223385 13.2989128,19.601105 13.3799116,19.5722881 C13.4729103,19.5404379 13.559909,19.4903876 13.6574076,19.4691541 C13.7594062,19.4464039 13.8659046,19.4570206 13.9679032,19.4388205 C14.0774016,19.417587 14.1734002,19.3584366 14.2753988,19.3174863 C14.3728974,19.2780527 14.4748959,19.2628859 14.5783944,19.2598526 C14.5573947,19.2977695 14.4553962,19.2947362 14.4163967,19.3038362 C14.3398978,19.319003 14.2873986,19.3857368 14.2078997,19.3811868 C14.117901,19.3781534 14.1284009,19.4418539 14.0639018,19.4570206 C14.0219024,19.4676374 13.9229038,19.5647047 13.8914043,19.5040376 C13.8674046,19.4570206 13.8209053,19.4661207 13.8059055,19.5237545 C13.7939057,19.5662214 13.8179053,19.5813882 13.7594062,19.5829049 C13.7144068,19.5829049 13.6934071,19.5647047 13.6529077,19.554088 C13.5719088,19.5328545 13.5359094,19.6162718 13.4789102,19.6375053 C13.3949114,19.6693555 13.3049127,19.6587387 13.2239138,19.7118225 C13.1774145,19.742156 13.1264152,19.7512561 13.070916,19.7679395 C12.9704175,19.7997897 12.8759188,19.8377067 12.7754203,19.8710736 C12.6974214,19.8983738 12.6194225,19.9317407 12.5354237,19.9332574 C12.5009242,19.9332574 12.3629262,19.9089905 12.3404265,19.9499408 C12.2984271,20.0257747 12.4274253,19.9969578 12.4544249,19.981791 C12.5309238,19.9408407 12.6239224,19.9651076 12.7094212,19.9651076 C12.8144197,19.9651076 12.9014185,19.9438741 12.9959171,19.8953404 C13.0214167,19.8816903 13.1909143,19.8452901 13.1999142,19.8650069 C13.214914,19.8741069 13.3274123,19.8331566 13.3469121,19.8286066 C13.4384108,19.8073731 13.5299094,19.7876563 13.6199082,19.7649062 C13.8974042,19.6936223 14.1704003,19.5859382 14.4403964,19.4934209 C14.9788887,19.3114196 15.4738816,19.0035341 15.9418748,18.6910985 C16.1533718,18.5500475 16.3258694,18.3650129 16.5553661,18.2497454 C16.7863628,18.1344779 16.9933599,17.9812935 17.2048568,17.8372092 C17.4118538,17.6961582 17.5693515,17.5020234 17.7418491,17.3245722 C17.9158466,17.1440876 18.0628444,16.9742197 18.1393434,16.7345847 C18.095844,16.7239679 18.0508447,16.8134519 18.0148451,16.8301353 C17.954846,16.8589522 17.8498476,16.8437854 17.8018483,16.8892858 L17.8018483,16.8892858 L17.8018483,16.8892858 Z M16.5088668,16.6451007 C16.561366,16.5738169 16.5163667,16.4767495 16.423368,16.5328666 C16.3888685,16.5525834 16.3933683,16.592017 16.3663688,16.6162838 C16.3378692,16.6420673 16.3348692,16.6117338 16.3063697,16.6041504 C16.2658703,16.5950503 16.1998712,16.6496507 16.1863714,16.686051 C16.1308722,16.6845343 16.0813728,16.7467181 16.1068725,16.7952518 C16.1803715,16.7679516 16.2253709,16.6997011 16.3048696,16.7209346 C16.3678688,16.737618 16.4683674,16.6981844 16.5088668,16.6451007 L16.5088668,16.6451007 L16.5088668,16.6451007 Z"/>
    </mask>
  </defs>

  <rect width="24" height="24" mask="url(#mask-globe)" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>
PK
!<3��22@chrome/browser/skin/classic/browser/controlcenter/connection.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="24" height="24" viewBox="0 0 24 24" fill="context-fill" fill-opacity="context-fill-opacity">
  <path d="M18.75 9.977H18V7A6 6 0 0 0 6 7v2.977h-.75A2.25 2.25 0 0 0 3 12.227v7.523A2.25 2.25 0 0 0 5.25 22h13.5A2.25 2.25 0 0 0 21 19.75v-7.523a2.25 2.25 0 0 0-2.25-2.25zM9 7a3 3 0 0 1 6 0v2.977H9z"/>
</svg>
PK
!<V=�?chrome/browser/skin/classic/browser/controlcenter/extension.svg<?xml version="1.0" encoding="utf-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 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
     width="64" height="64" viewBox="0 0 64 64">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M42,62c2.2,0,4-1.8,4-4l0-14.2c0,0,0.4-3.7,2.8-3.7c2.4,0,2.2,3.9,6.7,3.9c2.3,0,6.2-1.2,6.2-8.2 c0-7-3.9-7.9-6.2-7.9c-4.5,0-4.3,3.7-6.7,3.7c-2.4,0-2.8-3.8-2.8-3.8V22c0-2.2-1.8-4-4-4H31.5c0,0-3.4-0.6-3.4-3 c0-2.4,3.8-2.6,3.8-7.1c0-2.3-1.3-5.9-8.3-5.9s-8,3.6-8,5.9c0,4.5,3.4,4.7,3.4,7.1c0,2.4-3.4,3-3.4,3H6c-2.2,0-4,1.8-4,4l0,7.8 c0,0-0.4,6,4.4,6c3.1,0,3.2-4.1,7.3-4.1c2,0,4,1.9,4,6c0,4.2-2,6.3-4,6.3c-4,0-4.2-4.1-7.3-4.1c-4.8,0-4.4,5.8-4.4,5.8L2,58 c0,2.2,1.8,4,4,4H19c0,0,6.3,0.4,6.3-4.4c0-3.1-4-3.6-4-7.7c0-2,2.2-4.5,6.4-4.5c4.2,0,6.6,2.5,6.6,4.5c0,4-3.9,4.6-3.9,7.7 c0,4.9,6.3,4.4,6.3,4.4H42z"/>
</svg>
PK
!<�a���Bchrome/browser/skin/classic/browser/controlcenter/mcb-disabled.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="24" height="24" viewBox="0 0 24 24" fill="context-fill" fill-opacity="context-fill-opacity">
  <path d="M18.75 9.977h-.727L6 22h12.75A2.25 2.25 0 0 0 21 19.75v-7.523a2.25 2.25 0 0 0-2.25-2.25zm-9.75 0V7a3 3 0 0 1 6 0v1.5l2.838-2.838A5.994 5.994 0 0 0 6 7v2.977h-.75A2.25 2.25 0 0 0 3 12.227v7.523a2.224 2.224 0 0 0 .105.645L13.523 9.977z"/>
  <path d="M2.5 23a1.5 1.5 0 0 1-1.061-2.561l19-19A1.5 1.5 0 0 1 22.56 3.56l-19 19A1.5 1.5 0 0 1 2.5 23z" fill="#ff0039" fill-opacity="1"/>
</svg>
PK
!<�;�|kkAchrome/browser/skin/classic/browser/controlcenter/permissions.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="24" height="24">
  <defs>
    <mask id="mask-permissions">
      <path fill="#fff" d="M2,1h20c1.1,0,2,0.9,2,2v18c0,1.1-0.9,2-2,2H2c-1.1,0-2-0.9-2-2V3 C0,1.9,0.9,1,2,1z"/>
      <path fill="#000" d="M12,3h9c0.6,0,1,0.4,1,1v16c0,0.6-0.4,1-1,1h-9V3z"/>
      <path fill="#000" d="M5.5,12.5l2.7-3.7C8.4,8.5,8.8,8.5,9,8.7l0.7,0.5 c0.2,0.2,0.2,0.5,0,0.7L5.8,15c-0.2,0.2-0.5,0.3-0.8,0.1l-2.2-2.2c-0.2-0.2-0.2-0.5,0-0.7l0.8-0.8c0.2-0.2,0.5-0.2,0.7,0L5.5,12.5z" />
      <rect x="16.3" y="8.5" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -3.5061 15.5355)" fill="#fff" width="1.4" height="7.1"/>
      <rect x="16.3" y="8.5" transform="matrix(-0.7071 -0.7071 0.7071 -0.7071 20.5355 32.5061)" fill="#fff" width="1.4" height="7.1"/>
    </mask>
  </defs>
  <rect fill="context-fill" fill-opacity="context-fill-opacity" width="24" height="24" mask="url(#mask-permissions)"/>
</svg>
PK
!<�a�5��Ichrome/browser/skin/classic/browser/controlcenter/tracking-protection.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24">
  <style>
    g:not(:target) {
      display: none;
    }
  </style>

  <defs>
    <path id="shape-shield-outer" d="M12,1L3.4,2.4C2.6,2.5,2,3.1,2,3.9c0,1.9,0,5.2,0.2,6.6c0.4,4.2,1.3,6.3,3.2,8.8C8,22.6,12,23,12,23s4-0.4,6.6-3.7 c1.9-2.4,2.8-4.5,3.2-8.8C22,9.1,22,5.7,22,3.9c0-0.8-0.6-1.4-1.4-1.5L12,1L12,1z"/>
    <path id="shape-shield-inner" d="M12,3l7.9,1.2c0.1,0,0.1,0,0.1,0.1c0,2.9,0,5.2-0.1,6.1c-0.4,4-1.2,5.6-2.8,7.6c-1.8,2.3-4.4,2.8-5.1,3 c-0.7-0.1-3.3-0.7-5.1-3c-1.6-1.9-2.4-3.6-2.8-7.6C4,9.5,4,7.3,4,4.3c0,0,0-0.1,0.1-0.1L12,3"/>
    <path id="shape-shield-detail" d="M12,20c-0.8-0.2-2.9-0.7-4.4-2.6c-1.4-1.8-2.1-3.2-2.5-7C5,9.6,5,7.7,5,5.1L12,4 V20z"/>

    <mask id="mask-shield-cutout">
      <rect width="24" height="24" fill="#000"/>
      <use xlink:href="#shape-shield-outer" fill="#fff"/>
      <use xlink:href="#shape-shield-inner" fill="#000"/>
      <use xlink:href="#shape-shield-detail" fill="#fff"/>
    </mask>

    <mask id="mask-shield-cutout-disabled">
      <rect width="24" height="24" fill="#000"/>
      <use xlink:href="#shape-shield-outer" fill="#fff"/>
      <use xlink:href="#shape-shield-inner" fill="#000"/>
      <use xlink:href="#shape-shield-detail" fill="#fff"/>
      <line x1="3" y1="24" x2="23" y2="3" stroke="#000" stroke-width="3"/>
    </mask>
  </defs>

  <g id="enabled">
    <use fill="context-fill" fill-opacity="context-fill-opacity" xlink:href="#shape-shield-outer" mask="url(#mask-shield-cutout)"/>
  </g>

  <g id="disabled">
    <use fill="context-fill" fill-opacity="context-fill-opacity" xlink:href="#shape-shield-outer" mask="url(#mask-shield-cutout-disabled)"/>
    <line x1="3" y1="22" x2="23" y2="1" stroke="#d92d21" stroke-width="3"/>
  </g>
</svg>
PK
!<f%%Bchrome/browser/skin/classic/browser/controlcenter/warning-gray.svg<?xml version="1.0" encoding="utf-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 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
     width="16" height="16" viewBox="0 0 16 16">
  <path fill="#808080" d="M14.8,12.5L9.3,1.9C9,1.3,8.5,1,8,1C7.5,1,7,1.3,6.7,1.9L1.2,12.5c-0.3,0.6-0.3,1.2,0,1.7C1.5,14.7,2,15,2.6,15h10.8 c0.6,0,1.1-0.3,1.4-0.8C15.1,13.7,15.1,13.1,14.8,12.5z"/>
  <path fill="#fff" d="M8,11c-0.8,0-1.5,0.7-1.5,1.5C6.5,13.3,7.2,14,8,14 c0.8,0,1.5-0.7,1.5-1.5C9.5,11.7,8.8,11,8,11z M8,10L8,10C8.6,10,9,9.6,9,9l0.2-4.2c0-0.7-0.5-1.2-1.2-1.2S6.8,4.1,6.8,4.8L7,9 C7,9.6,7.4,10,8,10z"/>
</svg>
PK
!<V�D,%%Dchrome/browser/skin/classic/browser/controlcenter/warning-yellow.svg<?xml version="1.0" encoding="utf-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 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
     width="16" height="16" viewBox="0 0 16 16">
  <path fill="#ffbf00" d="M14.8,12.5L9.3,1.9C9,1.3,8.5,1,8,1C7.5,1,7,1.3,6.7,1.9L1.2,12.5c-0.3,0.6-0.3,1.2,0,1.7C1.5,14.7,2,15,2.6,15h10.8 c0.6,0,1.1-0.3,1.4-0.8C15.1,13.7,15.1,13.1,14.8,12.5z"/>
  <path fill="#fff" d="M8,11c-0.8,0-1.5,0.7-1.5,1.5C6.5,13.3,7.2,14,8,14 c0.8,0,1.5-0.7,1.5-1.5C9.5,11.7,8.8,11,8,11z M8,10L8,10C8.6,10,9,9.6,9,9l0.2-4.2c0-0.7-0.5-1.2-1.2-1.2S6.8,4.1,6.8,4.8L7,9 C7,9.6,7.4,10,8,10z"/>
</svg>
PK
!<˴�yppFchrome/browser/skin/classic/browser/customizableui/density-compact.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="32" height="32" viewBox="0 0 32 32">
  <rect x="1" y="1" width="30" height="30" rx="2" ry="2" fill="#fff"/>
  <path d="M3 1h26c1.1 0 2 .479 2 1.071V14H1V2.071C1 1.479 1.9 1 3 1z" fill="#f9f9fa"/>
  <path d="M1 13.5h30" fill="none" stroke="#adadb3"/>
  <rect x="1" y="1" width="30" height="30" rx="2" ry="2" fill="none" stroke="#adadb3" stroke-width="2"/>
  <g opacity=".8" fill="none" stroke="#0c0c0d" stroke-linecap="round">
    <path d="M3.5 7.5h6"/>
    <path stroke-linejoin="round" d="M6.5 4.5l-3 3 3 3"/>
  </g>
  <g opacity=".8" fill="none" stroke="#0c0c0d" stroke-linecap="round">
    <path d="M18.5 7.5h-6"/>
    <path stroke-linejoin="round" d="M15.5 4.5l3 3-3 3"/>
  </g>
  <g opacity=".8" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round">
    <path d="M28.5 4.5v2h-2"/>
    <path d="M27.623 9.614a3.03 3.03 0 1 1 .636-3.324"/>
  </g>
</svg>
PK
!<#�5b��Echrome/browser/skin/classic/browser/customizableui/density-normal.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="32" height="32" viewBox="0 0 32 32">
  <rect x="1" y="1" width="30" height="30" rx="2" ry="2" fill="#fff"/>
  <path d="M3 1h26c1.1 0 2 .627 2 1.4V18H1V2.4C1 1.627 1.9 1 3 1z" fill="#f9f9fa"/>
  <g opacity=".8" fill="none" stroke="#0c0c0d" stroke-linecap="round">
    <path d="M25.5 9.5h-6"/>
    <path stroke-linejoin="round" d="M22.5 6.5l3 3-3 3"/>
  </g>
  <path d="M2 17.5h29" fill="none" stroke="#adadb3"/>
  <rect x="1" y="1" width="30" height="30" rx="2" ry="2" fill="none" stroke="#adadb3" stroke-width="2"/>
  <circle cx="9.5" cy="9.5" r="6" fill="#fff" stroke="#adadb3"/>
  <g opacity=".8" fill="none" stroke="#0c0c0d" stroke-linecap="round">
    <path d="M6.5 9.5h6"/>
    <path stroke-linejoin="round" d="M9.5 6.5l-3 3 3 3"/>
  </g>
</svg>
PK
!<ݬ(��Dchrome/browser/skin/classic/browser/customizableui/density-touch.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="32" height="32" viewBox="0 0 32 32">
  <rect x="1" y="1" width="30" height="30" rx="2" ry="2" fill="#fff"/>
  <path d="M2.942 1.073l26-.145a1.88 1.88 0 0 1 2.01 1.718l.108 19.27-30 .168L.951 2.813a1.88 1.88 0 0 1 1.991-1.74z" fill="#f9f9fa"/>
  <path d="M1 21.5h30" fill="none" stroke="#adadb3"/>
  <rect x="1" y="1" width="30" height="30" rx="2" ry="2" fill="none" stroke="#adadb3" stroke-width="2"/>
  <circle cx="15.5" cy="11.5" r="8" fill="#fff" stroke="#adadb3"/>
  <g opacity=".8" fill="none" stroke="#0c0c0d" stroke-linecap="round">
    <path d="M12.5 11.5h6"/>
    <path stroke-linejoin="round" d="M15.5 8.5l-3 3 3 3"/>
  </g>
</svg>
PK
!<>/B6B6Kchrome/browser/skin/classic/browser/customizableui/empty-overflow-panel.png�PNG


IHDR���Q�WsRGB���5�IDATx��t�X��m��m۶m۶m۶mL�r97������j��;�_�o�Ƭ��)��e-죔?OJ�Dk�ְ�1fV��<k�
�IȾ�:~˘�c����J�]Z�לK?�6��P�uE�1�~��鴆��/��
�`тaâ��K����Թ��0�c�uQM�5<��q���O��S �D�#���۩[�󩝨���;�0�_@�7B)��`��LJ{���"�L�GUa�j�Ǩ�T��Rn��~т���zZc�e��YӘx� f�
�9��}��4n'D�;f"ub4u�U��UR�5��c��:>;�̤{/��3ƿkm��A W�cQ���T*X���5Wڭ������9	�p&��u������|�N��$��k��
K���A�gL�X*c�}Oͦ��Lk� J���t7	���P�R{RSs
��?�T
>*`!1814wv�w���f·&? �{���yED|�j��.�n�DJw
}޷RƋ�A����J���A��I�z̲��+!�.��$�J�wR�yc�l��r�ӄb���Rp[�T6�>e�DA������T�����$�G�n�2�vL�:V��)�������q�!v�fJa���~;Z���l�@�u��뱹���DB-�������t����v+��K{IG�.H�Ȱ��	��7���g�j;Tmm�AD�Dt�ASk;��L�sRg�"{Bmm��|�ֿ����o�5&����c�����ɯ��c�x.�?@-@M:lذ);"N!��1D\�z������}F��)�>E�t�P�u
5��ƃ�1�R5],���u�^�&l�ܳEv� �+J�V�oي}q$"�F�ɵ�Ԃ
Z�Ŕ�/G�E�?T�!�;Y0�c�*�L��-�����E[��ާ[*��2;�v�/�	���4�W;��Z{C��`l(@�I
�k��\h���Pk8��Jg�1�c�1�c����U@7�6�y�ۺ�����������Upز���N�6/UhS�Iދ�n��~	��[�M�J�=gNt��wg��|o�@.@�6��`V(O;�,�#��؏�j�/0` ͵�O����UOVT=M�z�(p9ɢGO����C�����
�Z�ؗ.T�s�5�ю�;f	�E�7�v�|3�?49szy�ؔ|m����p�����eS
܋(۹KW��gϋ"O��j? R�L���F�.�Xy��E��n�x����4g������o�`���u�"��#!}'eRf�25�M$�Ϡo���<Ǿ����v'uY���M�����nGjx�����U��LJ���f�¢'	��|���kV�]dvm3n����h+��k.����m�q����Ì+�i&eF+3X��ϔ��)
���B����w'3�(�L<�f����~
����4��E^]��{ù�cf�:r�q�آ�g�����d`�R=�L�e%9=�l��:hb�i4Ԑ+��[3˺
?z3��01
���:�.��@�YO��vY�����nߜ&��by�6F��@
���l�t����k��+E���9S��;�[�ŵ'u�t.-5��0���0\����2-�f'%,�i�-��(�v����0U���@#U�R��B�k,���m�d2��wK����+3�L�)�XIJ+�/L�x��z[��ߠ�Q�g_)`V�E{�ePT���v�t+����U�C��k�+�<Z�}!l���*+�c]�@��/
�Xj�CՖ�N	��*��S)jC,
�Ŕ��J#
�8(V�/��d����'�ʡ��l3t�Ž�کY��U6�u�|�8�;�X�͔m20�H��[�K�z����M��zGXa��50�]X\�տ	�d��mJ��%��cT�}�E��׎I��ע1}���m�����+cfQUe���Գ�j���O�9�����R���D
�[Z�Y)��S��DZ_���G���K�M6��Dg���M�����A�;�#f�P5�f����2�&��$gxz8�Ii��F��^&�������	��LD����<�z�^�HŪ�*�9�hƏ����ѐn�&zj�
)���ܠ~��a��̀{�/�A���� R���9�߬ۤ�W��f�i�4��� �,�?�3"�GU-D	�/���@��;�R��Fw��H�����p�����o%�]ݷt��†��H
�F�\���:���K�
|���U�����A��ӡ�[�yF��JRz�29kUs:�ż"9u�!�B7u�5�̶v�%��Ik�jdUl%�|��͵�M�
hn�-O�X�iT��tq��9�7�f6��!�$��F0��;�f���z����|f����<^'o .�`�r�?�
���ڵ����0)�A�Y4U��-p�2+K+X���������L��$��Hf}�z2�VmWLT��-�ۣtegX������\(�jM��į<�@6s(�{�	1%��*���FR��]��2*�&��/�5�0��D��ݽ�!/�.J�%�F�7G�H��2"�4���}��;�S�����]:��J7��
���a�/�MՖ�Km*���Ҋ�xB�y���*���MMM�@ղP�=,�������x,�}ej����|{X�	�7DF��c� ����'+��HW�3���Zzd|J��[zm��U*ه�l8\&��U�%T�K����o����o�
�[���6�f׬{&U��l�h@Y�gú:���C��y�DvG ���"D#��ܙ��	�d�y5��h��]��ѕdQB!E��E�l����f{�n�<S:Rʿ*�f�])��!HGհ��;?$b�D���
�����h��6�\�	��<����VVf?�|��pqn��l{������Y8N�B����g�Q���C����*5�@:�=�L*���jU�>��x����b��$�.��b���j*p�{�>����Jl��i���}z�eA��bK�7J���#}%Ԍ�}�8��Z���i4�n�wv�8N� VplT�X�}�V�-�E{���#m�d�m�����
]���&?_V�Hu�]o3[[;�<^\�m�L+�L�UoCâN��]'��#�9��l?��Ɏ��l�t_'za�P?J�T�U�_�|��#�8m4_E�7}��#C�1��#��-��nY{5���%�H��|��6JH�2��+ܸ ���ݗ�F>�2���X��T�Ʀs}xy��Y��s�_	��9.@���E�����5��{ar�.�\E���^�h�\,ǧ@���ySw�ȾZEEM��9�p8���)1�����7 okyxBr�o1L���!^4�Z~&�H����P[GG��t40z�s�c���bgk<R悰	$/>�,J���2i��1t;���u�k�ܚ{I�|���	�{}�F(]ʠjM#�a��"�0�b/ƣ����`3�u�.�L��Xl7�E�=Yx�Wy�q�	2���K��c�s�[y�+rr]u�D��p��Q�����ȃX�N�ލ�!f�F��:K�nE�6Y�A��D��B�z	��_��8�ϒu�JB�Q+S��ܡ>P �H�;X���$N!�"w$�:�����ׄ�ن%n����<AF��t\U�vzU�n�3�z�-��k��&g���U2�L��IB)�J#��)Y2M��ԬH#��a�#U�S+��hě���i#&@��64�`�8tg�2��]���U�J�(�A�h���-=:�f5����z���ɼF��@�@؅T
\�����	Fۍ1��xG�F�ƾ��0A"�d\2�T��bΆ3%��zmz�����v�M�{(��Oy�i4��֐����Ԭ��kk�_��9&f@�%��E��uIJ��Ȫ�Z#/���Q�+�N>������ɛ����)���������0�j�4o��V}��i�?������HN�5:j��u(a�l��q��$Q�5Y�d��q��]/���cZ�T��h�܇���_�N����Y��v���tZUU�+�#A��F�@3+���=�C���
;fU���p�i���Q�K�S%ds�Dz����b_H)r��jUx��Ąfp��4��0[]u䞜���x3�����W�7y�o����CSWC~:w�?wa|�
ww)-�i��\q�
VVM��Z�Rǵ<�}��M�RF��͹�s%yI���s�����^|lz�:�d�dҌ�Ӵ��=,�X�*�j�k�\����lz�����]�ϴ�q��H��_��3G�d��uܒ{@�vgk��)r[H���zv�[4簕�0	�F�.��!J5n^l�(��,�e���̼`JT�n>�E�����k�o���BR���wZF\���858�dւ�"!��T��D��U΢�g�U�f	��bP$j��,��Ѥ����H�%�h����L�?n�m�:�P�d�9�I�|xǰ�W��c��@l�@�W�g�����c}>YY��q��hĵh��ZCܜؤ?L�=(3ۊ́�n+�W�(��k�U��X�k6l8��.�g�8cz���me�\����S.���M���2��^�F�C5�ìÇO`3���.����V�hKjQ�U�1�n�w�w���7�t)�ZfA��H�Q���ᷔbu��B�q���n��0��ĀuƔ��&�-�U��l��v��na��.�p�>-v<8�}W��ܨ2���_�j��Ev���a ��å�
��&��Y1+����ؓᳱn�-ȿB��%g|���k�r
ށ]s��X�
�����G�"�pjJ�$��������ֿ���E�v����!v�_*c7��6�;�+�X�ý�,�L��9	8����>��zS��n��a��s+���1λL�1���av�i�����p@;��cz�Z���|������_{vY	��O��X� 5��/�k����5�A�P��d��^��w����loq�d�K�y�(V+�M����'������O�1��9��>RE�p��<���w[�sV[�I��;�e�G��sM�>��5�Ag��"�,����W�j�����`���cC��9%����(+;P?�i���-�8'�
�jcEQ�&�i�{:(����B>������=��qe%]�,b�?�Z�F�Hma���y]C|+Be���F���>�b�o\���ݐ�EEE�T���(xOi�~A	̈���0
!��[�@}��Z<�W��HZZV�z����_AI0ֻ�������ZXd:s��E�ڒ�g��4�n��Nw�'�R���n�l�M[�E��{���仟�C&C2CR�}�ww#ZSH�ߠ~�Hհ�?.ܸQP/��^�+ �V�ʛ�m��fs��ڕ�շ�aG�ឹ'�}�9�G�$hK�������*#3�c0˙�O���$��h�|�%s|��+������э���iKF6����PX\��>U6`5�����x:��M���[�qϤk\�{<��|��i:k�h�6�#�U	�˦L��6�V�bQ��6�F�2h���A	�=���	58�Գն���>�C����Ž�z�u�疒�wT���H�|��6ɣ\�|�L����phm`s��F��.]��l��H����u�8~�|���^�u�h�Bd���~Gh�mt�=6$i��.33�~��b��k�΁n�tБ�f�b����]�\>9R`��/�Q=��T�'�Q���"�1Q��'G�
��2�=�������D��l�U�@�Y�9{�]d��!����<s*���5�5G�N�@8��TtKmH�":FQy�m2�u֩��e�w��ȁk?�7���'��W����,|����c@�?��щ@;�S�[�gX�j�d�>6Y�B�I����V�f���!��M��zhn��_�W���;HI���*0ݴ!xS������_� mCڊ�bD�ol���orO�ʮ�>"��&��Q�Mu�r�l̰p��9CZh�\;v��rO��ǫ��{L>��{��ȅ����M��x�+�[������n��u|E�C�9>�B���s�uܗ��y�f�(,,�^g�V{K�Y��@~߷�#�D.��y?Vƶʕi{e�k�|�,��f���x�Z�.�0覤���ۥ�G�Lt-� ��2�M���b\��eڜuvs��<J��G��s�y��cx�Nw�cbi���Yq"##߷��Mdm"��Z�'%%�p�M���8����U��D��.���������v�r�Q����"C$��7�p�w$��G���Ww��'@��쵦����A@��^�h����A�)Re�2[s����	zK�S�5�*�,��1g'`�PC�be7�_5[���X��7�?�Oe�.(
���"LǪ��{"kI�"ظ^}�*�?����!@�H�o����ڱ��;�=����<�_����-�ؽ�\���gX��q�r0�^v��i$����ց�>?��:�!����h+Ǚ���G�7��`�Uؐi��ηjxA��\κע��E��9Q7����,͂A���l����>G���q�CRRR��&*��hӁ��_?2�83��o��:K��\w|� J7VX�g~2� ��	�&b�f_o0Obc��JV+�؍�%,?b~��9���<L�G2��4��*K�&���U�������9sVe�y����8uh�cL����,I��æ��s�R��4��̎2��p��H�˻��?�w�ZK4Y�?��2xOf�j��ƨh��$�a��]L��Ԉ֩�	8n���ٜ�`5�hj�5�s�6�J��ڼ�7������|�O��ե�|�A���	�RR�
�'�Rd�W��Zs�C�jgKC��/W�䚳y����s-�y�3��E��V$�f�~h������z3+�Ar�]_����;d<kK9�6�u4!�u�[M^�:ٲ�
lKv�12X�c�~�3$���`���W�������JU���3v -�-GNj7ԛ�ݱ����6�;-���\͘���6��tf@v��>�e�)������L�]Q���F�f�o�ä6�zdR��k>�U�9�d�����:�J�]S��@�e��pWi:�J�c@m4��2-
�������������$Z[M�AW�:-��6��}N�_�6��Q�Q挸p�W�}�?h+T|N��_�>iR��
d�n�-�c���JC|���Α�f����S,$H�ui��|d�����s��L�I	����T#�uP�1��Pm˓��@���am뾊{Y:�W���L.��1�\$#�l=�w�� ]�
r%�ӞjP�7�l�����.a1H���$>;�13�!y������7Yj���I����|���<m�W!s�n�l������4D�fƀ�E��z'mh��h
9_FW� ߐy�t.����x��׫6xd%��ӯ��U~����\�Q��CA���r}6^/��^�[��"��?*7����<������Gx���������;	w�Ӗ)�
9��U�Nީ�_����MW����'�
2Ǔ����A�m
���Ch����H����" �@e��LK;cV���.��4�߃q���-~��<`a[x�]����g��u�Ҁ�E%%T�<767=��Q�j��S������IG�j��d����!�*�s��V�P%]	H�T@���Z�����29�S,$HP[����x"��hc��̃�u���&4X�I	�T���awe0E\�i	�:]��Tmkx�����ܗ.'J�IV_�Z?���d��}X��x_)�W8���x��]aS`sDsc��4A�ٯ�i	6��YPi�@%z���z:����~��ޥ�U���
ht�[���0Hl,����#_�v���	G���H&V]B����r�<tt�X8iF~^���/�����K2���O�}����à-����My�9 �(���`���R�l�<P��ڵk�=*?2'� ��\]�Ae�x߾ô��o��h���з5�_x�C�J-���J���s�-mc�����=��6��x�r��u����$濏���}�=��X�GM�F.m�X-�v�f�y��/٘*���V�����W��ӡ@�%��G�u��e+����I�-Y��[�i��]�Vc="�����
^,�Oz���>�=��F������ꉿ�:�#"�7@�'�B�k,�!�}VN��Z��*�^���C�
tד����5�Я4�J�9�xyy�
����Xbd4�\��T@�9U���n�#	�¦->�'m���Q��'�1K(�x�n�6ϒ��z�n�uK4N-�Ln�|��?��\?�h)e�^;����mC��},���u�GX<��t��oFy��&���r\0@5�!�Z!]��g�FZ���]ڠ�S�RAW�Ϫ,*55�.|�Q|���&o!��3���My(�s��C4G#�~��
���ů�sr��EV@S6�M�D���b)%�dWϪIfb��6��
�V6��J��į�r��6�VhAM�-�!�O��
X�ެ��@�Mf-�>�K���F�G�	�|z�1��q��t��t4��/I��C�OR��U>$Q��D��,�����;,�~��!��`����
B���k�t��`5�1>r�L?�3-���ՀA�LU�@&�h�[��D/a��R��DfәB5�v�r�s�ծ����0�J�w��s
h��H�箲�!������3%��d����q�����q/N�<%�J�x���O�k��ȹ���V;6ޫfsI�ѣ�S~N#h�q#@�U��rn��C�eڵ́<�E>��E{Mu0�n�Q�!��+�c/6�<�1>�#��=ؘ�U�biU�FT�.[�ז�ZT6�A;M9Nx��)��6���R�S���Æ�n*�H��9�@��r�[����#�ϧ�%����;�:��I�#>[.�~��I��6��0t�����hH��V^^<?xѴ�e�L�裝���Z����4�#��S��&��ni�H�=ŗ^D9#�g*�ߐ��!�&#����z�?6\��ry�NxC7�}&7��R�G�(��R�w���n���լ�Ni�Z3~�oJ%^�T�Z�FIF`Z��mɩT���q12��O�+g��V��҉iU|���M�!�,yU��%��M\�����ܢ�*���o\^Wx')Ր���u�?�wM���"d��EiōR'�I�Z�C�Ty�M37(�uw+;���
�:��
�5V�4�3*���.Lh>�uQzҝOi&ot)!;V��V��y����8Χ�d��� �1
i��?h�i��3=�G2yA�Fk�q>���3�²�ތ�'�wG]��5�{?�qm_ׄ�Ó�y���)�*5Նa�����TH��O2�I7��2�omp�
7��Q���:XÙr�$W��M�=�)W��y2��I��Ꚛ��H ��`�<f�_���?�Us���d�1��8Q����#x�;���;2q�9o�����؜���^��q
83�؜B�0&z%%$����ǓzB��ԣJ����Y��'Z�*������6\��66\)�
�`�:�f�b����L_��#����@U���x��+�w)&��T��P�Q�PŦc����J��n�����}
�6箠qσ�h}�8Ɖ�ư��vJ��;M���ຟ	'JF�g�{��O�u�nO�5$���q�t���mFG�G�Q�Y���ҏ���:��>$.�'Wm,�s��)O%��o�o�.r�'�6����+>�/ʍ��lz@���4�P�YW*�B�K���d�#'d�Y��:L`Q��V��Jr*p���������O��/}��$����U�d�������$���n0�[�N���}��*y9����l.2C�M�]�>�W�P��%�æ)b�"]�� {/�=��Lt��N�ɾH�
[�6 �q���qD&�
�W@���b�\E�f-Q�e��O��x�����w��Ԡ��c���+���$-�D.�dD�"����(ŮZc�7O�>�&��OB<�J0���w��w��a��g?]��J%$��W�6��m@]���a���4�v8�+��va�@���5`&IG�Qnn1�̾�t1�K{Uo���
���S\n^����t�u�h������^ܷ8�㰩�����*��əQ5
�2�u�je�A-��xy�T�,��ؿ,�j�)�\:�7���\�XKmt��Q��z�O~UG�Z��ЮXS o�#Y<�u�k�tM��=
Pp$	���3�àr��!��W-�o���w��9�#1Wc�1K�d3�9"��yJvN�6l�
p[/g`p�?��p�h˲}3cP��D벙�V"����Ew�Ь�Իpz�a�u��m����1��Y��?�
�=/�~�O��'���I�Y-���,��.�b��mg
����-ޡ�!��	dڦ�xɖ=rߌ\�/F{�O�L�|�1n7�5����7�P��>�o}�O��7��g�>����k�V�V���s�r�c��d,&
@='���`.~K2$�f�kIv�y*^F���Z��mzR[���n��$cQ�.�����>p�߳���yM#A��6m���Ђ�ȥ�V�e��`�/�\�C�IR�h��x[8�i?n���`|.=�|ԥ�Y�]y�Z�$Xn����x��a6 o�� &�r��MWߊlme2�Fp�b�+d�4��L.�\�/L��j���c���(��� y�^��/�9���!�;�/�; �������'?׋K���O���W�_z��*���Ks���%wC�M��ٲ>UQCRe��*�l�#p��pFl���m���j�ˊ$nt������a��M������]������&>�^������}K�7E��\y�վ��\��lJ?�߶���{;���+�_���(9G}�C��f䣄h��Ni�c��VP�rY}]��Qd
4p�+5�wƈ��.ү������;A�����|��*IJΔ�ޒ����
��5��	:����
ⴐ�3�ӝ�����b,��gf�@�Y�n�c�Fr��ŘR�$=V`�d�>��N��Mo����g?���t��o�<��
4O��ڽ!� �|�Si���]������S��|�nm�&'�R��c�*Y�I�_l�X�Oz�L~x||�
Z��7tЊ�����������7��*��T�T�8����]��l͂V�YR	�����|�$[n�ȶfjs�=4�8�&�H�F�8Ҿ2xD�Hca0�T'<!����q���$Gl6=�w����
t�lά�I��i���A�{�+��h�y㑠"�H;�v�k�7��ƃ�
9�J���1*����z5�A���b�6{�S���T�Z6��^�K��{K0�AhE��xnjM͑���<$-��� �L�l�N=�W"w��׆I�������Zu|O�g�'�%��LD�L$fM����F����XǏZ��5簋@;@��l�ί�\Q�;9S�l���B#�B�i�`;���$��'%ϊdU)
)�U$VS���15J���E��JU�ZJI�謁�fq(߳�/U���W��	��bU�v�ڌ��Η�
��"��X=������y�;l��ma� e�:�� HXa��,s�$]Փ��Ä�|MU��֬#S��m3��Hg@`,���up*�sDE�^AO�e+�ENNΝ��(�Ȓia%%��)T��af���9N3�|�6/�5�`<��8ih�8J��*��i�Ҏ���m�R���a��2��qd�:�+��%����(�z9����'>5'���
% �V�ug�9�Ƙ?�r�9H��`C�C鱭%��'Xj�%��AN;��MH�Cʃ?Ȅ#���y|O#Q-���&:m5B�J���J��]D��eM>Zx;�(o�|T�x�xO^��3Pvz����8��b��2?b��$�^��`� �#EM��ډ�q�cƺ̏��Ͽ���|��8�7�$%?�@��Q!�pu�
�QeG��
���#G*�h�]��^���i\�i3#��o�L��o��\Y�0�g۶m۶m۶m۶m�mo��	f���}�}w��Y�]�ԠӃ�������bT��ҝ�_�{�*ʶ�eE�SO����m.���UT\�S����	I>��E%ڐ�3YK
2dȐ!C�2dȐ!C�2dȐ!C�MR�-%���.^^�F���sk�5;{���XKegg/�l�II�l��ghh�ؚЌD"���芡PlM<	6d����h|x��ز�MT߲�5���
����bD}
x������	�ٿƢЯ9�+ћ�uk�19��nS�1m��7t�y/�z�LdWr�B�&q�����;<���h�\���S�K���mˊ���ϊDb��/�_�ñ�)ˆ+��<�wv��pen_L�V��z���9�b�+��~��#V��H�~/�׎�b���/���w�S�象��Wu��[]�n��r�BZ5���A"�>hջ����D`�ߡ�-\���Z�j�SfN@T����q�
T	�ע׭�;=�t�1�n�[Mz`щס����<�o����&V�ӆ�VW�T�WX�:��1���w�Fݗ���9z����Yg)w������8M	i�TlO�}�m�����Z����b�q8 �~��+'%����X��xP/���sn@{9`{M�L�|~II�3�ꋓR��,�Hǩ�n�c�SH�؞�����0��ݿ��=�HSYAZ\�Jء�ҬT��H�qBzzp����\TT�.�H�嶉�M�d��ٯ��U��@5�X@t�k���]9]���a��/���";p��7�Mt������$�$�`z_�7�ߊ:�6�E��2T���+�xk�N�&c��Qi�d�#�5�}Lc:�d Y����8u�#ՒaG�2o|$i'=ߡ��������]�V���aX�a�uw�6n��L��J��H���}�q�)�q��ŠU��cPY�,����h'�qeƔ��:��d���d�N��d��g��ƕ�a{w:��t��eed������C]�:}ss�T7b@!n푽z���X�	T�ᣝg;<���4�p�`y��s���򽇎6��!�Z��|����[=�̜�F^=%@d�ٝ��x=�B�=�r�L񝃞Rg����m	�#t?���~�+�ERa�3\>��^9<[!�J����Jm�R�y��L6��S<�z��,l��4se��X����i�p�%Tn�w�'��H�w9��*�ר�snO�;�H7����)�����皚����R4]p�2�;�?ީ��݋�~>�{<�1�W���@�����+ӿ��<G��^]Yi{c�&yh/�@��m�tJ'��B#pݧ��Vs���o��e>C�&��R�kH�]�<��1�_0n/*�<(Q�WlȒ�'ˣn#W���ZbN�<J49�������Z��xo�/��y���IEND�B`�PK
!<�I�*�*�Nchrome/browser/skin/classic/browser/customizableui/[email protected]�PNG


IHDR���C^sRGB�����IDATx��tdI��Jf?۶m۶m۶mkm�ޱz2I��-_4҈�~u�d�X��'��������Vu@�r9���郅pO�\=�T�-���R��Jŏ�Z�kpp�W�r�Я�F��|)]I)7�ht���j�Iiڤz=��Y�����:�R��OJ��nڴ�`)���	J�JΥ4<<Bi� !��])�;���}������O���+�=��f)�D�9Jq\'�b��7�맲�P�T�cL�k�v�3I��U�����|���!q���������ܼ�s{�ֱ�Vs�$�R����)�P(��k���Vk��!$��f><����������lkҘd"�����wb	�s�!c�S֦�ah~����[��ݒs�k�=o3&Y/�y[A >E�i���0Toc; ������{��#}'�N����}����@�M�.
��O�ar���\~���؁C�t窣��j���v
�>�C}��]�S�bߥ����vp��'�"�����Ĺ�68�-@!��Z�9�]��y��}Zm�|O�y���Y��l�&���c�y4�C�Z�87'3����o����8�CX�|��$��
�l�����~���'����2�T�8gL���]�?��W*��-[����B�6��u�V'�e�("z�o-�8�24T~�1�I�U*�#��{(�SJ�;c���t�JRFSJE���}T��)�֦���b˖���ي%M��87��|!-����н�ǵ����b1xۃRI��ڴ��v�-��s��A?��8�UVv��X�/�2��R�4�Hv�[��ι�a�l����&e��Z�5�u�K��մt��ݿ���AkLR��o��X�/[Y:W��:���P׷��|�R�[JE��'�bιy���~��@;�75::���^��m�iE��g[�A�O�����u)��%Ic���p ���VfR�l�rf�a��0o���R.�o&eTH�:���Q������.B�M�j�����_w� �#��͉ T�`]���C}O��a�����ܾĘ4�
�0tWe+8�����w�ҩZm�z�J�ݘ�u�l[Qʨ��U�JD�A`O�^$�s�A��o|����*�|g���7�A�X����i��Y�q����h�z�z��ׯ�8��
��ǥ�~�_�M*�[ѝ}��+��]w\�fG�k�i�ϊ'0�@[�`^.Wx�RqlL�J��FQ��@�����[G��{���u�g6ԌI��Uv�m���L�6)���6��tEDk��M���݉��N;V*��ٽ5k�|臰��J�B8Y�5��
��:0��&Dd�)eED��Z�{d�PC��l�SJw���;��D�/�J��)Ǟ�C慡��1�D�3z1�����=�V�\y�0�gew�8���MXok���6���4������!�f��?|:t
��t�U3&ِm?*�z$I}0�j466N=�H����������)֦�(�u�E��{���lm�����I��}kiq��ݝ��~(M��]p�ݙ����<'�rL�|uZ<g�nq��F!�٫�pn�E�S��SS3���[dmB33�;�e���1&)%Ic�s��;���922NR:� �ɘ�ul^ڷ��FB�uAи�
@D/��%���n/XC�{�-�;vxx��0�b������@��\��}h�+���M~�n�Nk�o;���Y��2O)� c��1q:0P���˞�(�e[Xq\_�(�F�$�TDR:� ح�tm=�����T���,f��>#�y_�\�-�ι[f/�˹�R�����J���F���9�������V�߶��=�����9���Emz��Zm�Nͼ������?.I�ߠ�[ԜK� D�+�-܀��蓴/`�׽2"���>�{/q��I�
��=p�����[jD�<�:�K\ɗо�9��U|�V��w[*Dt)<���!l��Qt���^l��?��@��"�[A _�u�'��_8��߂��5�/��`K@��Z�5'�#���������=�-��~�|^��X�(��7��^�ϋg�
�����o1X����ͤt?�6ZGB���Y��rpp��� 05&!�dm����IM��~���so��Z�ڢ4^��j-��
��C,/D�'�y���)����e�~oɩ���o�֘;�M��@�l��*�1yۓA���6_��Z���S��������l�����O����l�{��`�ݝ���-�+��*�?۠�6'��ߒ��os|y��ПK��u����Z�m�>���z��|���m�/�(�t�����g[�d�إ�\zy����lS�xֳ`h(x���s���>��ߟ����l�}G��}���w�i��?���Hm|���e��m��ݺ��.�$�v3n�N���IH����B�5�|��p�%4�����<�!e��2�����
�B6�"�Q�R�&Duq��!f*��dW�0����34wi�s��#��G��y-8�mCB�G���SI�W����<��}�p��mo�|�GB.��K$�H$y�-�
M��Xx҂áfD��ڛz��P�T�8C�GΦ��:�X��mCv��E��w����ҋH_��G��Jz�|MDy�j�z
��ϧA�t:Ow���F��t1=�o�c����իWH"��Y��YTR�Ö�RQŷ蟏�G�GbW�<Z��L��و�u!k�#4Ԅ���*Y"w�-�z�.�3{�KU�q����,�;
5�/�_l���Q�8B�ȍ4G/T쪕K����x�Cd�~���sI�3���H☈qf�Lm(���@ �:�'�
�H$�?e/�
{�-G���&�-f��>Lk����!�G�@
�D�K
iL�5�!D��ƑyU"��@��K��{���$�)�+JKOI}�=�����kUMp��S_+�h�`W�"c���Ӽ"�"&=;C�=�5N����m�36�S��ڂ蓼F"���
���mV�XE&�BԊ+����4�l����u�z��h�}�&��s�5T�\F�Ju����\�D��)���3�f�:�6�C�_A�y��	���YدK�B�����8gl��ې��N�D�q�?�b[-���O��wʱ�Ʌ�U�R��cC��;��E��b��R�H��u~e&$S�g�qkHjeSG<����x/I�(h�3 TW�\��51�Y	1�J$�҂�ٹZ�H$k˿�l���N,h|��#��O�aH���h��\���㓚��ŵ���(���)��
���c2�z��~
q�F?��,#�uqm���@��L9D
���~؊,��v��$�P��H�S6TĔMU,^ǦJܚb�f�kH�e���Q~{ӈ�-��g]�q�]d
���/��L�kY��v�rL	[}���5�m�$�H�~�����QA"�`�YW�6�Q1u�q���D$��|��øpg�L[\�$j�����Ǩ�_���k�������~j��D"��5��!�P6����q�x*.�\��*�!�tY���6� {�TxI\���jAYlI$I�(=t&�ʕ-�,Vӏ�Ղ��I�ž��	}�P��A����,���<�,D:��K�؏ɡ��*��$	y��v�^���hH$���/�}���!��`���T�p��������@iVs*��m"l���E�e�
�>��aaZ����崢D2߹B)1�1�8AC��M6*2-��HY�d��;�O&�)�=
A�z���$[z=����T�x��?e�Fm�,2�Q�w�p8��ij�F��H�"댟г�Y�O�~���K1�q�nv�Ǝ��$wآW*�q���
a�F�k�=��~KeM�/P�}*Y��Q�	.�d�37�|��
_���8{���}������ot�S�D�����1��,+K+2����sh)P�K��E���䢻_�%��Ҫ�"�ӆl�����U�ZG��
�k�����Ru�]�K�A4�L9~D"�fÿ�9��i�X2���/\��\m7�D��XW~?�������g?A��"Kp�2�d�q-�]T%].��ص�d-g7t�V��K2���\a
ޭ���!h�0�W��3���zC^��`4̢����}������
�X��zB��K��d#�3��8��P�іr�_���.����O֟q�H�N	�.Q�2�,,ƶs��D,ih:.[�M����	ZCR�!��/[X7��Wt?�X,)��X<W��C8ce˃�K?zr--��I"Y?��jt�b�����
��i���/G��LV���x;Y��I9ϟ<$f�m�a�!C���b?��M[ެ	��g͍�M���@ p��������p'ʍo�y��߂�хg��޹Sz�.v$�*[��;���R�Y�>Z]�8���2d^?:�+sĴ�B��vk�I^�i92��-Mek-�l�l��yto3�kY:�zJm�#�@x��O�M���v�f��y��r��ϗ��C

^YF�,r��o�1����ã�[(��3��!��!S����tY\ž��1��
���8�-�֓!heX��ѳ3���fϻ緜��?ʂ��&��m��4��Ǿ(���9;��Ґ�=����3��7��ՃlL4.�{R'8K
�<4L�΃�ؠ�U:�;cN�sD�Ƣ����6���,V�^	�)&��KC��G��.��7[������AC��{*;9�iJ��AhN�z;�rc qy��UY3{�$��$�EɆ���l��^ڣy�._y)���#��d��(��d#�Y���*>�R��B���^dL��l|��9�hi�\s�M����Z���)��7Zc��2�Y��0�uƋQ�VvVdҢ8���v��m?&�O�Bb
_����J]{Rq�R��*�)���D��(�p��	٢�.�	�i���І�H��5p]n����Dz4����A���`��^���o���tc�NG_�P���>�ޘ��>�_�V�,��&=�L�����������L�t
�av�*u�����Y��E�?�BR�^�Q{���;f�B�3���=�?Ҟ��嬏����F��(�TV�^�k��'��N.;��I��2�?�O����>�Ht�vD�c
Q��7�6��?ztP�/2B�n-+�A'���N��B�4��m��]
�p$ `���B�&2�LOV�r�Y(ۏL�K+9�C��NՑ���>9�/#IaPx3޻1}9>���0]C3�;NY��4��c���P(��xp���0p��W�QoGG/OE����
�$�j�W�'>��[[}��3x�n�"�z8DngCC��dRT�'�&���X��Ԍ1��Zd��j����?�S���Kh�a�U[�w�妾b�V�HR8`�U��G]����=$ɂ'q�zI
���%ȶ�����fs��[�|��oD���x�4<���q�3�6X��przg_��WӍ�g^-��
�E�FD�826DF��Y����5^NE�k���/_�4�M�N�T�z9I
K�=�5�'��0��X/�ľDI!���{��zԙ�\.�}��w"�!jD"q�Q����JJ��hi�=������Gf'��!�o��������+�M�V�N�;{�V���YĞq�_V�^X5��)���b	�K�*��4|_������*<½M�⚡�-��g@�s?D��А5|�"s��$�Ȩ��Leg���!����@�E�L+[�?��!ԫ�8s���>��[Чq��{@���|<h?��fմ��� 8-�Φ�LD<f�jG5�ԉ���S(<4�Q���<K3��{^J���\��S�3��LI�ˢ�/�݅�U}٢�pƚn8�h$[�*���P�#��/[��?��;P!fOq��}�}6����
��?!;ʧ�B�� b�E��=��&��DR�v�h&?:�#b2c��?N����ҊLߡ\a�=��C��|����~DI9F�m��듙;����7�IBq*7�b?�C��(�H$�){9-�w�y`�+-����k��}��M}�j0���8�/�L�ˏ_����u(U`�H$�������9c6W+��T��\�4�Vq�6FǞ�Vɂ�5e�Ͽ����Y�Xsa��%��Ht0i��j��1���
�s��S�,��e��,R�>�=wu����xV�D�;��ȥx�
$�]b�5��v�2�fdl�.��J!�y��_A|�!IJL	4�QX��.v�^YY��&cͳw�ƜD�#��)�ۄ��Y��m��0��AT���C�O��C�zfٍ���x�"��:����$9T3��t�=4K���܊]��yo�Wm��"�-�\u	�T^��477���^��x|7Ch���w�JE���d��}gg��GF_r:�߂�.��I���C����7�����?��w� p56"�;.=��|fV�Y1�i��ˌY�����6Y�ʮz�<hՆK�������4#٣�MYǾ@�&�!�$&��j�$�w�b�6\=����H�H��ww8�8�J��1�j\��<���&���vZH�]�OQÝ�=b��A��6����z��Դ n#{����o<����?�h�Z�txX)��	q��MU�`�6�dm&x��fd_U���y�Z[�v�Ƈ>ih�j��&� ���h#� ͅ����
kӖ	�^}T���Dbn��E개�-f[C��7ޏ,(}rtt���%/,�<��[|�p7�;�m^9�^-~$N��]�n.9�����x<)f@?�g��x~��X�=O��ieg��' Z���^HM�����C��3z�N���e-c��"f�7-�^W��P��M�/Y��QY��'�`M�N�EU�weF��,��| �r��!#;��k��7A�]T���35��iHO>�s�1�	]lq���{#-~$Ȃ^�O(1���D"�ɡ��Q1s~�>��U��|��{Ǹ��W��aM-f�m�i�[��Ma�.�d����cF7g[\Z���-ጓn�t��z:5�Y��T(v�1q��y�����=z�o?�^EN��45
9"7�(��d��Ey�D
�vbtdo4�d�H��Rs}~U���g#���8�7����b�6µd���
�
bv� >G���{m8��cX$�i�&-h�&[�����'2�ԏ� [��Ҋ�ٱ���d�E�s4�����_N��<�G�֮qE�[��%)�>Ι�7%������n�eD��Ƙ�S)W�����B!���B�ZZ���0��Y,���%�<\W�͢�?'�ho�ōDqJ"��a?ƞ�^��E\M%�ռ�}nQ�;���ڔ�F�;�k_��R<�=��R%~�}4�k�:���"}�X�$�Ļ��⼦�8�V�˓c�7�wȄ~1��#Jm��P���x�3�$g��]�[�8�Y�'��7���F<�`^V `� {���҂�55'e�@b�h.�=��'���Y>�|VU�~��V�)����
c��C�H|�OXۨhfc�S���=8���@؞��.&��"�GF�C8�ೱ��
o',�▎,�K�[W����&��9Z-T�HV�y��Ȣ^�
�z���8š~v\4t|�l�[HL��5r��ߪԄ�R�j�o�&h�͞w�w�,f�����;�9��&���N�*�������4��B�)�s�J�b/��lD���}pk�?z���Լp�����X��!V�e�Dy�	�}���nI"K�g���Om���
*L$6�����CG�Z�#�3�Z�Ƈ�?���=r7MDu�x�[d�=�r�m;���8�>/��x��F�v?�1��]��u�u�����"a�������z�s
����ˍ��"�D�H`���Ӧ#�(T�c�q��"����������-��V�ص�
�ŶSa6�%B(�n݋c���6>���d���&����ܣ�YØ�p���@m�u(ܩ���Sٙ�[�ߕ.q~槐��"<��x��M�\�Q?��~���=Td��l�y�hqzK��p�Ć�۪�q�Z�d�ZL9Εg�孬�g�4|z>�5�
�D����>v�F�ye�31>�<��J��;d��S�U��-ZDjX<�*��vLL���H�I}rqT�|B��O[O��Q�3�0H�1�H:��PE�3*�9�R�it�"Vd�h۫��C��2y�]4sʎ�P*�0����ڢ�����у��6�q!F�[�F�ly���3ʈ��CR�
>=���d
�F��Z�n%k��o�#���<g:��լ"s��T�ab������B��c�t�Qz�Yii���z���� 1���C��C
�5������^��g��c���p�K�E���Zu�Q��>W�_{�rwf|��:��lx�{P�pj=4��1s���{-�'��Bܙ6TO��Wт!�vQ��m�
X]M/#�ۇk�l���_�>�z����[��L��6����۹�X.33��d9���B���GG���C��@�-��;���zv�����Ō���e�8��q�Ʀ�\n<��ⳭX�n�DY�0��h��k�����x��!�bF�
��4F(�@��"(�D
�2��%55��o�\C���.1>��,K��ٺ
��x��a}�c|�~�̴�=f�zil�U�-�
�4���3�3<"^��.Ȧ��VM$ј���(�D"���WR�I�r��2C�O�LFŽ������)Ԉi/S�=����ճ�,���k�X1��W{��"�E���� �D")2������N�x�N�!�(�
��R�C41<n��`�Vφ��^Ģf+jjU�o�<F"�3m��
��d��B�����Ӵ�:�9��܄l�l�{pi��C�<���
�a��L�����;�:?���Hؙ{f[��.��0O	�e5Ph�H*)����zF��>X�m��
j#�1A�5�
��b�o�5���Q�$�V�����$]{g�Y�����B�f�/�ci�%���oC�𨞝�U�`>��L�X��W��}��{��y����Z��S���xQS9S˫%O�4FGvf�����Ɛ��[r�L�p�n�ݴ����?��"�^vTl|x���o��~���6X�.�æ��h��Z8U�{x6NE%DmU�,^��E�<A"����X���ΐ���U"Zb�|�� h�P(·���{���_L3F"��3�!3��ygy*h��T��G{YSBt�
q�wH\0vi�Ţ��I\EF?M��4�PP��(���������e(�����_�1�AZ� �=-k�.;�Ѵ�H�<y&M��lG��,;�kʬ@��k��̳��"�Q���v��aď f
?.��1��*Z.$�$G45�����J�x3���z�x���/����b˼��aݍ�͋[Z|��)��	���z�DRb�fދ�^v|Ėe�E�,1c���1Q�ˏ:N�~13�}nS���$���Hr@��}p	�'W0�����T��W�>.�!C�R���
,��6g������-[/��C'�����w�83��-�fw�̵�@��c�K!AeS�����9����Rե�l�A;�4�25r��{4Q�M�_+[:����<�*{�N쫨ko?�BOB���;Me��u]Q�7���-HB�ao�(!!���'�IM���_���0��$�L�{��<�'�o���=��=��"��b�¦M[x����QV�}�Yc����9T�?Θf�B���"�ʗD1���*2u��ٔ�Vz��,�k6�����L�"-k���gv|�'/��Oz6����1��!�u�ز㤶�`��TIPK�j�V}��y��Ud������܍	�����
mښ5���^�hC����0����no@C���Xnn������vHL�^*}L����gu\q�ǖ���q��)*?UPP�Gh�X�EwA�>X愅�M��eјE��˲"i?A��笭�'�Տ�>H�����ǃ�#� �H��dw�قy����'��E�r6omښ`Xl����-�`����{��@W�h�=|.�)7�g
]�je�a#��E�b� @�c�_q[]�.fh��c��={�V]PP�Ӑ��
�������%�!R��qX�o�ט�jg�}aޚ��j̒����~���ĀA�9E�P)�{I60<D6/X>���c��:H���|���M��.�B�撲2��W���B|<ku� W��,�E
��x���"7%���W��ʐ�����B��п�%��|DEm
�𺺜���{M�(�ͷ��+{�f.�����0DNp�׸a�e�z��J�'�}����Fj����r��1o��.K�!���Mǖ�9D��h�r��|�{ac�@ڴ��praa�����}I�����2��/�գy�ܹ��'�p�u��B��;UO�q�MwL?9����kXcq�$G��V����C�/����i�F�I5�o����"ӂ�ŗQ�y��Zѐ-��{�T��c��G
���c
S����&�Y�T@HP�]F��ɳ�9H]b�00Z�6p���'��D=�D�6m`����"���!���#"yu����Y#�-۶m7@~/�F�uˊ`�v���l߆H戮M��J��X��Ó�ʙA��B7�b��*!��u�-�#���D�OUR<)H�y} d3����""�v�u�Ar�bӦa|^�A�j�"9�8��y�nM�^���c��VQ�����3F�w�GDYYY����3�++{��3�6�1~iw����C0�����y���C�����+0��@z�mbil�X�4�cj�8�vӱ���WJ�x�ܣ�|3�
m]��Xv@*�/+\eGX���Uè��#<6m����mT��k�eUN�/�3w�3�#��������q����׫�D�	�j���*����Ĭ�N�h�l�:e�H-����k���g�_��B[��-[�.E
��D���!�~�pa���~�)�}H9uY:�6mC}sN����́�N`F��N2�8�5r�EX�sX/�
�E�4iY����5�V�8��_�i��ҎA���08���67���N
�*�\�����&̔<�V"�c��A����"=�S[׵�}�6�/���R0�8�� X\w��?CG=��#���H�Z�����:�A��l�I�7٭����wc�i��-��A��0�`g}@���'G��Oں����D�UΚ׷���+<����^x���ykk�!���}�AJLۋ�ל�T�&Fg3�~q�9Xx�l�v1��Df��ը�=\�^��$:\KPk�H
NJ��|�d��5Y�-q	h�$R�cV���m�����`F�"�3�����Տ�kj�mG=�E���ڏ5���yyeW�4�u�(n��%-8ޣ�}��h,�C��i�~(��T`�<�rA�B;�t�b���B��b�`f56Ip��y/Y"�]-B��.����m��[�D�|9�7m��֩-#�~fi�m�R�0Q�� h���V^^���mW*�ɪ�J[S&��P��X��AJz?��7
�}k�b�5��������u�Z��N�`��lv���LBX�W�>ղ,���_t8�љR�pj_f�4r�{�c�-VO#p"�8B8l�@�M��
v
h�C��F�Hv�h���CQ��:���������2�T)O�l��`]K03�n;%�o�h�i�8?|����g����_�@�v2�����'Z״����7����x�-rz��h�6�ga��ϰ�n}w����T�3fu��Xt묩Y��!��~���x�"<����p=���SDn"`������F�؎a���=h��E-
�HmL��LЩ��g5�D�t��ј�Z����
�,H�ɹRhk�4�3��R2W&�g����R��2{���64mc�3�dP³��NS"_�_%_�_�K+��C2��LX�+��"6܄y�c�G�9��i��׭L�>?�s��8߃��k�(m�H�%�iid�
��Q�Is�6�����}H�u��P�Y����B[�M9�^�@%8���½Y�rq+ww3��8�vQ��v��Y&�<
��+�)@S�ćJK]�-�e������)Gm���2H�$��B���L:�-P��@�R1��K���� :S�F�1�t����4��'���c꿎�9"&���I�7N�?
�s����׹���ǟ߃�,���������׈�lZE>D#�e�F���&O��0�O�W1�S4[��N���v%"��+@3��
�N�y�TV�w��6�m�o���J(��Eұp�yI@{s@�\����BYT��jƙj�^�Gpj�H�/�L4#�]����R����ƿ�"��wF�$����	h�y;~֐�<�JR���P�^��iBx��ZiK����2�WZ���A���o��7y�[�0��V}�6�u�,c|ӿ������
�24���� ��;�r�Q�Y,h�7ދ4#g�u8}6T��R����Zmh12:��iv3����J�Gk>�Qk冂W��k�`y��L�'�A)��)G|�l�F:�������2.��4!a%�SnK��fE����j�5ҍ�; �"Z�4}ߺ���68�KK+	ƍO�F�h��~����yߜ���@[�t��|� ��Y�xQ���7���T,۴E�;����q/Ң��@��u��"�_d������.��hv]M�#�{¾'b�"���Y�>������
B�&�֖�r룢#	��'�����s���X��f	H;�Y�洆
�Cr��1פ��4V
mM�`:����e����:��0��>��4�>3��h
��9��2<q�|��]2*H6�]�u)@�FfcBBVccI,H/�q�ɮ]{%��_���
���֬)<-'��J�-n��l��6R����>�oچ��'!B��)���A��d��H��tU|�9��@�E�j�g;ҏ+�VS+c��S2��@F�M4m��]�:��
i���f�

ʯA���)`Gc����H!�L�W
U��
.��u9�{�ia�Ê�#`H�wfEz^S����>�e4<�ỷ��`�� ̈�LF�B�6m^�*��;L@3��WDۘ�1�ٳɚ#�Ε�B�pf��M�K����k��4�Nj_g�֌H͢����+����y���	��?��7܀���\3t(�ڴi�Z��4���hII�M�mL�X���f}{`��ir.��`٩��Xh�E�M�%qRt�H��&�l�fg*��QU��5��	����v�i_	��V
L����z�G8|ģ��9_hӦ�댚�`D��Z�~�Z�K��#�ƴ��'��W;'�+�b�[�Ţ:)�)����=P��/f�$�w��3f�P��Gj$��
ŌԚ���Jr	�4�`v�Dy��VDfR.��t�T�jp'��P���\�r�gV>vx�o�CB�6m^M mk�^�p��l�x6V�e��(��h�ͪ���rD��rXP��>,I���~p�o����<��Ŗ��{�ĪXy�ˉ����n��өU5]]���9�=#{��"��i����&�	���Er�ЦM[Cچ���N������K03�sX�yݷP�50]�� N���Lt�t����H-��~�5	 ���&���a4l��Li���ձ7�l����#|Sںɠ�P��&��j��/�i��l����������#�8Ly�Gm���k����!Ϛ�EMEk��<���)vi��q�����'�"�L�Mr#��4nT��7�=�/ں��1w���F�����aB�6m�-�w��c|�>�J^q����v�9�k
#	�Lyμxh]�7�xD�s�\h<N�͊���ːS�V\r��V����n��c{�8ܓ�g�O_׵��R����Q�!<\�SZz�ЦM�k��m5)����j@s�z�d3Ӊ�Ѩ�OIhgΈ��ɐ����5��;��A+�~��U,.��(@�ꥺ���|-CN�6U�)y���ޠ����_�F��i�>�f�7�/O��� �)l�GB���]0B#?Q5^{h�����4�B�s5
hd?2z�'ks�o�Y��MӬ-���~숄2�򊃄�YvvI>�~6�VW��e��m�3��j(k��8�lv�vW} �l�͑�a�=��g��|���(���ɾy��!��8�t�Z�Sq�iG>�����x����4�j�l6�+��Kg=mr@16[��J.)-];�VXX8��ӝ����7��3���#�Ќ�$���w^cڊK+'�1e��Paa�`�Y-<P�2/H����7��ȫ�7���Tʡ	=�%�va
�I"l���#[�|.�:�]̘v�*���8u�&R}�&y��$9�oF�J��-j ��@���;K�T��^fo�s�c�y'M�'/�c���l�HEE�/
/2���3�Ĕ#K�ޢ���6/;�*���h�G��6�[�K��і����#���Ʌ�[����d�sN�,���gi�����rR�,6W�S����L4_��8�j�,�<�'����E�h|��9ԽP�x']G�_���M�$��<:�ձ愃����I}sЯ�)O�/�Ild�^�F�,�����/��Bo�'w�!����>UF�����.^d����~Q#�A�a�Tx�i��,�-6�����׭}A��N�;��
��=�ډ�A�#?a���$�p8Ͷz���¢���X'�z=AU|�\S����%	 �9h�z�vwܙ&Oq�?날�^"����ԥf�G�ฝ����7˗}��dDœ��Y(��A��&��������2�v��v�������#���6�9~�$�:����,�(�j�z_m�jnm�NE9���
o4m��tp�z��Š[�ܱ�۹s?���[w��UWW_U\\TX�5�y��/�
 S�V�u��IJ*�!��I^��$�
�Q����;��%㒨�u��y�!q��@�`"�%�HZ��dd�v��X_�I�?9A|`�|x@��/S�c>)ɀ7�?��J�q���J��l�����%w�F�c�.Ԫ|�YNN�y�闃��茋Rfqq��NY�%��j�'F�юf�ڇs�9>�Hii�% �܂�};�'�b��p�7ڻ?^i�rˊ�z)�3�H�gNJ�gN��� O'�#��M�/�ڍ\�\E6e�n��93����)��g�q�
V��+BC}��x�#�������m˖��]3�X�X����/�]5�e�^k�rss�H�1��6�]R�l�<���V���r�*38��2�p������s���#
S�̀��~DgG��� ���Zsl�v�Ls�.���_���"�o�E�r?��r���\t��c��N��;jW�>(��G��W��l�^m�K�DUi�}����c�q>���V�V�8W���9���4"�"�x�?v���1XV�hP� �H����h�H�2��Ӎ'�6����!:��1��e��dj�����k>��j�GEE�{?D��qN1�&�--�=׎5�
�`�۶m;;?��r\�;����2:��Y��>�e���n4�%3r����E��ۥ���P)Y��[Ҏ��"���YW�3�w��z)~�ժ6�4�))��NUS�C2*BD�Bx�
:���i�+��sk���-k�M�6mK�^T��������p���5ܿ����ROz�.@=�>�{���$��`�cG��(\E���Y��Aڱ�����|ްx��\�y�^<&�n���xo�ɏ��9���`�����U�6ed��$��%kBF����_�}��@��l���H�v$�ҋ���ށF����u۴ �Kx����^ʉ3��拤ǘ<��w=�Qǂ���O�o���X�}o����M9{.tS��~V5�ѨLP����:XФ���۾Q�Z��5�wG��RC����^��XFc����8�>\�Z�dZI	!<��v�H���"�;W�q{�� ����ϲD�%��B7��7K����+��t�#�ϥw~ ��7�t(�l����OE�n�!_r���TR���|-I����{�lF��5<��J'LA"����i^�/4I�Ï>�f���Tԏb�f��mGaauS���~���k7�yd?�}��"��<�y>#*��c�@�ejIFd�p�pZ/�
?�%�������py�
�?�1���v�9ל��M�\�-IViI�2P9uR���O�Ւj>����9A-��uW��J!����l��%������	�;&��Glz]����/{���M0�r���j/���k�Wա��r���@#E4	�E�_XZ���kkb�`-R���h�^^XZys�����
�C��j��M�:�sQ��>jd}A�YIM���X�lN~V���-"�v��Ħ���x��#���kԂ
w3�R-�7,��׈��De��/����d���de4�dPcT�4�C�Ά&��p��|�H�pR��L.a�u����`�"�}��?K
ןڴu�(�F`,�~��MB���b������n��ܛ3u@'s�>5��|�\��Eƌ�N�I`���_��A̓E�n�-5G&�UzqS� �F�8۷9|����Q�^ݮ���+��Q��Ӵ'C���haA��w,�/-���L�԰��ml�u���n������� �4��Dd���?C��Za��k�$ML�0w�gaJ$Nu��[��g����e�3۹�l�ee�Nj7S�>�V%��l��@��*�C���E|�R�o�+0�e{j����,1�v�^--�AC[�:�jyH=��h�7n��'u4���,�3���Y�f��΂�ZH �Ѥ�ǯͯێ�ܹ�����m�J1���78���Qz\�OFFƙ<�	f�F�dw?��ܹ�q+TT�_1�[Q��}�%���:��=S�����y�>aP�@�Pt�v|�
�fp?qPcm�T�X�+hbɆvE�_���<���>��U����8�}jb���qIo1���u�.�j�%�a���hj]b����,��	j�rg
Pk6�j�f�V���&��lǎ=���l�`y~���@�|�_��ҿ�}n�|F��1ʛ({��&ꅅ��b����Q�ƨam.����<��fw�n[�f�u0�Ư����w<ܥ�+X�����]""n�h��3H�x N�~6D�cr�~U'~k@-
u�=R݇M�!�a����hֻ�V#[*�&XAA� �~'�9���#�+�P���f�@[�ݴЦ��LK~&��p}]ZZ9�5>Xގ�!������}�Q0���`nC�9��h��\����o~�A���H8@�CRHc`F�O���tYBA�T}��i��B�h�t�녜@�Vݱ؏����%[]�O�>�-�m�#M�c���yn�na���a0S�@�%��)�)��H��9e��@���V7��䴁;TB.ez=X��ͱgTr����J(�iC+����H�Qw��8tt3�K�ho�~��֩��8�֭+A�����-�z1�c?�A��s͐oܪ~�jvԮ��e2	&K�H�0@�����D*�����ҍ>х��3.�)UR|�eꫪ�ήf��~�u�|m�4�or�_�Rԗmr>�$�%T4�IVFi|�;@�����B��6m��眍d�(8�K�R���
���<�Yl42x
�c
o�~4� @��oNǂ�ha����)����mc�Gd�m5��xF�7Y)�W�[���4��H�>c���!M[Va�'�R���&�\�/�<׸�CQ����4?��.���2<��p��C�E*f�i�J�s@{��L���(�ת�����C��=_��{l���(ĒV��vb%X�I�h¼�׻��'~*v�>��*F=&��Z�������� �	bb��(|��x�~�Q��)<4Fd�m�F͋���a�8g�as�GfG���y!��ZG-�J��ĐTo�bMb��gl�"���L�[��|��(�|h�mKG=l����?�����C�4�
����Z���m���<������}F�lj~f��!I��������
Q�q�Ԇ�m�|�<;ҍ��[���ڃ�K=@㢂�j��`ٿ�u3��K�
�F
�9�����`V7
h�d;/�Yp��}�����렱�L
!&Ps���*#^���k��~�re��ǃ��������eb�kp�{h9s���`��<l
fađh��$/�9���Ղ�#r<�S��7L
�u�fמ�����5���_��"�s�')J6҂�x@iD�b�]Rfeۂ�K�#�!�?�g�K#�{/��!:6F�
XF^^/���}�;�|�xU�fG"���]�r�@_yŇ��4�]h���YC���k�覛�̔ڙ�€�	��.��"�Sd둚����[�~��7[�#p�U���VC*��A��rx2��s�ٚF$�[> h�X*�G��x�AAe���7��x��Ҥ���x�	���!��_�`�?OŲ������4�B�M���!*��������b,�X;�h,"�D�꬏a����ET�*.���֞���0�h�*P�8�
�����Iq�T�s�;f��r�?���tl�N�c��O��ʘ���%���O?��o�$�/d��k2Z�D4��?�f�P���:}�Ao��1P�����1��Z�~���s�?��ލ#*��*W����Ԩ+���nz.o3��v�+&p�9.Rl��;!d]�U��	�
�ĪD������vJI6w�5]<���#���)�?7��;�T�奊�x�|��QT#`���*��@;4gp�+�TV�:j+R�K˫��0��CpT53�w����U��)��g��
���L*((����:�1qu\������R�-���	�/c��
Z�S�/3E_����Z��_)�׷���
5ۋDKجD*����z��s�ʕ�OXT�P�\g$�p�jP������}J5���a���ݘ��k��Z5}��ݱ����@�.��5���^?�f<������l��a{Cb���M��<�ÚbFC}z�:Zd��c�^�Y���Z���A{�y�MU��`�'D�����_<�u3�M���v�0/��Զ��c陞'`�Xhh;<
ȸ��+q����q�K�Nؾ��k�^���:wDFw&@KB/�oDG3
h�̀&0�tAT�	�5&<�Wo����v|J��_�g{��R5Zu��?0j��<��s�i���Kq.��.=�EM-*l������9�����.�`��Q�y�;`F��l�eK>g�@�]xbo�p�;�>c��H��^+�M��F��f�~�>7A�U�����˩��F|nX���z�r���A+q�c�@R
�)9��\��s_��i��D��c

���#��f
�p��o��z�V��E�ZԽ� Wu��.
�I�h��k���W\SHyT}Ĵ��zUyj&�B7\�^��~�.)�Z�P4��g8?��$�s�.����+!���Dp"?'���v��^닀6'�o��EF��v�\H����f���������?=�p�
ZV�������n��No���a�4��c�÷�z]3M5V�nK����쿛��lE���p矃�q����y����+��٤�WQQQc)��K�ߩ���⊧^d��rg��,����):�i�Gz�w�4|��n6E��{V�W���[4b������PRh��M�$.�@$9B�k��y%����CRH3�� ���?�G�y[�>B�-<1�z<�)}�R�fA4w�+e����w����uM@�sR�������oDŽI�T���2��"��Q�X�����a��	PƘ�ä����ܞ�Ώud@#��\��!�d�e�1�
�������qp'��;v�⨉ض�v�aLi�J���c��dw�;E�v3����7!_nq��@S��ň���APFQ],�.
OlY� ��1"{�'����O���x\����쒀ƨ�Ԉ�r�ŀ��~�ɳ��ǣE��b�ļ5���^���Yf��[�|T@9%��+S�eee�"5c�"R)�3D�`���'b�S����Iƈ��3������'�X�Mȭ��p�(
}��F�@��ϡ��,��'�P��n��M���2�x@#���z��%�2���
>����	��<�q~��F`���sՈ�a�����|P���g��}��k+�[`�f�?q@c�G���C�:9J���@�����ǣ�-���Am|�c"��Ń[�f��N��E�hy)����B.UU�óVQ�jm%��":S*"�_r#,{�H ik��,A�߳A��~��J���Eq1#�65ŰTm�������#�S���G�7��n�dj�X�/�
�P��X��ᬡ�����]=l���f�4�BI�QM�lT&��A��|���x3@n=i���i��@c�zѬOZИ*F���#U� �7�y~�������U��ь}�7L�8IQ�Q�>'G��n��16��)�b4b秦^(�u*ù5� �R�ԅLy�P�	#��՚]�~m��S^��9�y3��<C(c�*v�u.�$�?ļ�rR�[)�h�)$�x��p�̄;��.�
�?KU�W�h��%56�s1RP"_��}>^[��-�T'�|�����<)H�'��@6����lZ���|��t�> �'Ϗ��h#4�8p>|ύN��`�Ȍ7�N��}h����8J- ����X�A��׎��U�r��:Yg�Ъ^ݽ{?�g�m��AJ䂵�"�8PۭZж5��:C���m��E�
��d�"�M(%�u�C-I
�eə�?;ѯ硉֭jD�|zv���[F��p�,��&�����H��t��l�f�S�Ut��^۰����>|?�G�3�!Z�"��m���Z��
�.��8�,�i9P�(X�C�}�i��A�p����4͵���%�9���B�Nf.�d�p��=�����m΀�u"�v�jR}lH�Vb�":�i�j~O�ç���b�T��{Ǻ�WVPPqX�w�ڼy�f�]�sQ���V�fcS*R~��l�^�e -�"�U��{:����繘C5�:y-w97�d���*�b��ԖO��s@����|�4� �z~���V�ȧ>����AرY=��^����~)�˨5�<����U����T>�3@C
m@��	h2��y�՞�`ٌN�g'`�ce�)�r�@~p�������f��W�|��w�\�"m��b����L7�9i�B(�c�ho#� ��+��!?*}ڧ���i,"�n:�%��ㄶNcl�<�U�J�A
�s�@I�u����C�:�^��)�;js��r��-6@���oc��C��q}�"��"�0>�P
m�������Y�ǭϓ�'���5�S.�/��&qb�w)nL%
Fb
���q��#L�r0�φbc��<'�|�����)9*W���)g%>Ks��R�h]ђ�M��I��{��r ���G�`Ӑ��3@KV#d�'�x�=
�ߠ�z��ۂx�)����F4G�~� �8�7�n'��rgK�����6ޮ��𹾤R���l1*/x�;�,��?���`4�^��i�VZl}Ye
Q	�������,vRU ���P�Ѥ��6�t���w��ډ���)_�e�<u^"�N��h�#@S��e�<���¿e_�g�@�G���~���5�����")�ɦ�1��d�ܲS�=\|��6����ϵ�	��)ɏ6cI�J��ǽh'�����H����xސ�Z���N����.��9w��3��U�k�*�Z׉z��$�͟)r�?�@o��������碢����+�E��6�/W��e��ex�n��Ĉ�)`��z�^5�Lè����<��@m~����K'�6�h�������i5�x�^�1"S��1,N�YF,?0姆_:պ�ŒTc\�Q1��f6^~���69
���㱽)�+�,������i�����3F�k�qδ�9i:��##Q+'�+؊Z�O�z�k�mڼ��^�w.�l?@�X걤�>1�7���J����K&/�+fEjpo+��6�	��_���AR���d;���[Tͨ\��Tf*�/��Y����
��-��o�U	qcX'�~L	���4l���΀#oz�*#������j]�Jc4��F�l����߁��bL��y�Kl�֭�!�w�ڵ��M[ײ8Lp�4�PK�@���fx��&�ubt����Ïw�>
xk��P�
���bJ�?��]���PS�����W��R����)6�����A�L1��5Ěn6�&�O�%��Z���H(@S���36˩�c�B��3U?����e��E�Az|V�/<`�<'� ̓��+��M+���,Ï�]6��v�qbI��_u�Ц����#��s�����������/BE�ɣ������I�
�C&����S��oa1���E���!G�E�[�'��$X�q��'Ϟ���Ӽ��٭hK\����gTCO���Uޒd=.b�-���g��<��er@�Y]�ģ1�I&Lc��s:Ӛ���8ߜ��}��|�7�,oٙ�r�}�+$q_n�BoP��Ք��M�X����d`�*-��0��ja���6]4�/��1~��X<���`�r�a([NV�"��
n,�99�7�Yk\,����x'�S�s[7B��@Z��eF��0l(�f�c�-OZex�JS1��f��@��&f8��6s4F��_��y�b�o�#�l��N��q��Q�)Ӧ	��z�_��Ͽ\O��֙�m��g�� n� �~�FEC������7��q���a!98ɿ�e�Ɲ,u�HP�U�q<'�D�DZ���G����Z)�3RSu�֭��{м�՜�VM9
���d4��y�sP��-:�zk
Ӎr��"ًL�L�����]#��t�f�s�@���?�O�G��G�W�	���Y?`څ)H5u�����;�E�(�؎lڴI)}p^����&_�U�}���n�'��2h�o�[1���(�X��@-��6;h�ⱀ�!��9�����,��?�gOK�be��*����g�-��H�_��~S���n��Gάqْ��e�P��(O��(��h�|`�5�i��y���_3�k�(���5�X�@�Y�����i[5�7{�辩|޷c	�V��>�U�$�L��)d�q�U�	mڼЊ�mW��uR�6[m6j���;�~�CȾ��+���}��%�]\Xg��`&��I�E��́5���J3��52_����H��n��r@Ao
@�u�4�ɓߓ'�\,-�0˶3��|��Q��1R,�`�s��Q&g�qERDn�WL̕�n��~�V���J��{%��f:�uYS:ќṘ�1~Y���x�e���E7�B��zB�d}�6D���9�ӱ`�ڴy�Q���5�j>��*�mn�8�����	�v&-��$��'k�X��
(��S�7
�T�f,$y�ެ�ۨ��X�S�-��u�"���-��#B���b���zO��R��;;&�$�::R��|��^��/#� �������ߐ��v-�s���@�42/�[ b%��X��y\S���W���N�"Q�U��`�d���ٓ��R��#1��kb�n��hߜ��[���\�᷹��%%���I�)�v{�.��.�'�����Ux�Ц��S��ND}w
6]q�32J)�H�Ԍ^2�P���qg���;�O��1���hع�0r#���M�&Qhg��iKFpT���j�(
%��-r��������_8 �]�K���)��GTQ�F��2�*�I<	�O}z�1Q_��_GJ1m�Dj�%�N�ŝ0�󚿍���kV#i&��I�njvƚ��v���嘻#B�2
`����=7?d$r2S�S�	`�8�_� V���D
x6]q�}����M���kR�]�H_�"�j�42S��XT~&
h�⒄���Ҋ����OUs����� b�#�XMV.��0
SE�o�~N��o�97VJy���3��8ߌ�������������OO�V�����;�p���溉JY6�|ˆ�LȈRN��'��C���#�ш܌���R��TP���?��C���i�c����h�Vq���!���zH�ۨ%�#D2?,l2��NN���C+�ߞ���?�ፈ��u	�,�Y�7)� 5MZ>�_8jp>T�Vof$�L46HxF=�5߯���6�wf�-�]$����	U������j������T#5T=���z7U�5�D�'d�|���+�_��{l(g�.l��q9$��KK�O�j/vO8{T��[������;\�0��ғ�^�\R?M	7v�\�Ȥ|=pӐ�$�r6a��mB7[|�
|
STo���)}��̖�^�%C�]J����8հ��L�~���w���[תQ��k�@��fg��H
�M�12��E�Av�\N�Ǭ��s�[Ulݐ���Ա����E>^���>p��:�Tۨ��HY�8�p^7���u�(;�7+lT�̡��ʹ�)��z�c�cXd���\l�i h���Q�2�c׽&##�"��K�n�R�T��������R��|�n��!����
q�ZL^@z���e�%��c��i������Nz7#3@1�㢅ǜ�Η)K\:RK8"}��U��8�;�E����Y��[���9=W?���2Z:���geODk�E�$Z��ȿ���z���u���lf���X�0g6����ϿK�YN)�q�h�{P �62�I�����҇y�Q>����W)|�H߬wF�e�3�g�5���Fhk�Yrs+��@
�b�%�`aǼg��]���)��"�H�P��+���q,h�0/k8f�=C&�f÷�u�!jΖ6憎��T܊�|8"��Z+p}-Ήl|g帾����� ��A�$^3�ᾱ��y��9�׎��/	aRt>c�z�˾�!c��6�7c�ﱘ��e�Ƃu��p���͙S��Ǻv��)Ee�DD/l�W�S)EN�khT�g�� ����I1�s)ڈ�H�3��b�@Fw�E�Z?G�4��G�/l�r���rr�,9��0֣�\���H��Ff/k�c|�6���s�1}Roz�/�njKL�=���>��`��CTV�m?��;~!���)���̴8����F#�r#�����-�n��>����*�R�:��;ӆ*r�sy��dklT�
m�*B}F���6�{��Q�3��8��(,h�Q�FT��X�r�y*Bsx�^`x_H�Φˉ�	}�p�f˗}�ȿ�^ C���Ӿ�K������x�S�lmLۧ�o��D55�s7@�G;X���ο��{��c����l�$z��ex�J��Ṇv�o�ψ��?a#�(���h�rT��7���m�h�����$��.��?a1Y[�}��nϛ�m�n��U
E���V���h�y�k	X33��>:�� mX��|�1.�۴Hn�i/��7�ϖ�M�+�3�O��5�Oƨ�}3���\�E�,�뱣_��d����]�򟷧��nK�C�J�=�,/��(��\��ZI��%����t;T7�"��	x��Eˮ5X�ɵ��5Vy�[)��'���,y��������2�������D[_�;
�:ט�Y��W�v�ؾ�F�ʸz��'�bA��6ZE�dDw5��pFw�=F\4�`F�eNYY٥�]MU��(�w����R��/�Rq��7�5)�?VV�vg�iJ��
�Ø'�{�R�"a"�K�2\�z�
Ț�դ�w<0SJ"qe�k�^���I5[z��%�oX ��i\�Y�>��)/���r]Vk5F~�mk��&
��Z�[?���1����u��

�Z�H����h�֜���:˪W�:�\Л�T�d%0�<��Uڐu����s<��gw&�,��SQ��,$Vrڶs��2��"2�2�M[��Kv�;��������
m�(�VzꖫiETV�].:�i����,��jY��\Gz?�Ӧ��T[J`h
P��H�7|U�q�E؏R�[b����o|,Ϟ�Zvk�䴺
�ݞ�.��9����}d��e7�]�&�V�}�2p6������G�f���C�g�|W����FM���X����}�����K9�tbew��L������#����]�$"c9Y���BwD�V��@%£���E;e�ג�ק�bʋ��ϼ#�zp�����jHe]}�K��?�G^}�S�란��hξ_0��m���}�}z�]��%%��ԭ<0`���g@�}���a]��w�6�5fU�Q1��Ni�lRQQ� �����ʮ�Zm�9S�L�2�KP+-�zKt@����������A[����H���<��+/zt����q���^�}PB�lz��G6@��`E��q:�F\y�E�\����o�����iӗ�����#����Y�3-#��`�E3��8ŕ;R���,6I�ۨ�Fڴy���1GMv&�VII4�aM��.��nA��4ՙ�s\y�@�ύ.��^��^�/�=&�zT����A=Š_#��o�����}�/.�q�#�4�}�)�|�5y��i�熽%��1>�������ig��or�5-ͺ���3.��$2�X+"���6m�d̆���(�L�P�+..�GtpӶ$z0ҁ�N��HcF�O����K��O
��<����g`���Ӑ��oM��}��ܰ1U�_��^��ʀ��Z?���&KJ��2���~�K��1y�=c	^�k}닚��	W}w���q�EԆ��6mڼI�)����
�@ƙ �u��~ز<a��	��g�P�1��!V���O��\��X�t!��g�}]oϿ���C}��Q�"�g�C�ݻ��R���C˱�OK1�׍�+=�CS�˷ٮI +��X�q�ЦM��Ҋ��~Q�Y�i[xJJ�)�S����ad�j_eJKqO�!E\�R�>`$�A�Q��|Vf�Ieq	��zW���؋ç�1��Ɂ��1�菔�o^]I�m��9p�.��)�޲?m�(�ˆ^D�Ќ����ŏ�q�*0����׮�8Sh�tf��	�\�:��E��:�cPcD'>tR1"�1
�:R����o�D��ܹk7#6���	;ߛ>�C=jg�'��&��f�X���9�
��]�@>+Z�G�M�(�������ω�L�j��22����F�&����m��)Lǹ�osT��q�DL��u���'��H�{US#��q����'��Y��@52�!0S��6����!��%��/�ҴsE�6
h(��K5Kk?��^�46K�x�*z>.�ߗVRR�;�LGj��Z���OZ�Ń�����\�e1R;��O��-S�7	d��D�s��!�����������T��˙i�ӌ�%��n�X��j�8ڴed^���O�0��1��
�332tV���cWBV�Ʊ?��M@��D1m֌�8	�v�Q��I��!y"��q���ٮA�Q ��ě��@�[,*c���[_��O]Ϳ�~dL�[&'#��H�.���\��"b��
B�Y��6���g55������5W
mǛ����A��8V���󻤼�����&�����CP��+D4�/3]�GSz��V�+�~�DFU�f_��ǧJ���Y�i���͍��OJ���9��~�'*v52�6mmd�0�-/��r�����t��nڴisaKc��TRQ�	���+0��\���#�N.��P�w����ʿ��}�<��kֿR�^p�	&���.w��2�i����8�:���ϝ��6�pN�3��L��Q�#�ik�����+_�P�=��_�W�za�6�1Ц�ɖl�#�'�7Hq&`8VW3R���!pP��
�w�[��s��%���%�w�S��H?��(�@��u7_�f$懾�+�:B���<yҌ�d�2>R�O���59�A��͸�/�n�lڴ�HG�e���j��c��]d�Iƺ��T܄�׃L�6ma��'j��KU�U�^ɨi�x����a`ct�z��o�e�J��J������
�_����hy�i��?�1��T�̲��
�n5Ҥ�eR8)e$	Od���b�}�6m^b��5��x#iL�f��E^tbKIɿ���6�����d�*7mڤ��`�/�������5�4\�!�}L屆��<�S�	$j�ل��m�g 3$��I��?���LE��̉�����;\�F��6�����n��fP���j`���V}U��W�QXX���lUGt����شi��JDD��������W���u��h�bi�(��H� <17R�p4w�0����
�!]jaz��W�.��#b\�i�X�>@h�z�3��~o�هc����3Q
����jA�SiV��U��Z�J[s	#K1ֲ2Y�J`�po�)b�;El5T���}6��D��8��R��LF����,�
�z=�����2sTF3���
0;�Q��<���֩���D�����1��E�
��Q�2cL�.�C��JO@�@���/�!zy	V>�i@�ۧ����2J�_�)b#�0��l((e!r��ڜJ�vFU&3U�'9E��3���j���yә�Q!�#�sy\
*����K�6m���o0�iɪ�**lA�W�>��wW�BMp*���)Ed5�����_,Z̴i��R4c�B����՜R�*��c_d�J��H$t3��6#,ֵH�0���	Κ#?��IcJ��=T�7S���T�Ws�\�����QXX,Ԥm���BQ�\���$�
$������{��3�_���H-�`���������7�k�G�E��6m﮽��/��,Kd��*js�d�2��%UQ�\��͆}�6^�V4jb1������2��ms4��G���My |�ᛮڴubc�	�-��\�b�C� @��K*+��WVV�Ж-[�y��AB�"{��ΡŸl�!��{�M[[����x�l"�hg�hF>���R�$�����t#������R#K�Pn��7�2�3"�5[HD!D���.S�0�4q{�ׁ��/}��"�M[j��at�\F��`f��x�����V���-EE��orss��ʪ������022KJ�C�1AYI�|��**��u!����2֦-r}/�0H�\�J*��e�v��Y������x~�+�;i-�`T��!�6mD}�e�xS�V��k�t�� S<���s���56e�Rժx��4e�����KAA���/��j�WYY���=ח�W� �ˆW"���J�`N
��-�Z�W���:���i>G�6/�w��#�Tf�e�q�}�Y۲*E��\�'4}>OE^-�/�����ҸE��O�E�
mڴ9Y^^�e�問x��JNG��D�
����K�^����R���W��?��^Jw�SѶ���"HtxӦmu�Y"b��p��)9��W]� ��g$0��T%�+:���t����햨��&׿Kcf8�}��hM�զ�	f��|>"����}ĺUAAiр���^���$��LN0�o#��wH����ώ�|�+OڴuJ{6�1����^bYL����V���{u�D<
`����M�""�O��2���Ǟ] P�%	%�F�p�4-p��n�����O#}!���b��c~>?6��=j����~���2�ﯣ���@jPM�׫Bh}Ǹ�{����U@7�3��}�������������333,����m�Cl�ʴ���_��B��m_5��ʍey�#��s]��/Y]]�Zu��qeeb���O&�k���;oh��:𚦥�2M�7�p���w),�&��TUU�ݐ���t!�ׄ���t�6�fފ��H���l�=޲ܽ�p��4kˉ��z[]*Q��=z��(KIFn�(�V���5��p}C��e-+�B,����J���S�(���\ijI*��-��N��`��S �7A>����������R��'�YM��>�`>!R���
E��L�
�"�E���nm��A�+��Y���@�3�ڎ2�o3�@�(�
tMs.��}I$k���ƾO�g��(rBG���0I�q�MИwH�£</sJ��d
7�q>���_��h�_DQ����1h�cpn<r
�p<�����
Y>0����s�K���վ���ށsץӅs}?<:����Ѧy+�,���[,�~T�d�Pk]��I�;NP:	&*z��H��!_1�,r0�N�L
�|���MIZ�v�H��Bi]VNW���R/�a۾IL��7�Aߥ���h�r^��;�C7�4y��z�[��m �Sc�� �ř�6%4�m������(w@�O݀��C�8�����\]��B���⸽Ph,��6��'[Z�icc[Z�uu���\�[��[Z>�'����]�D�9�	}�V����E����0�}�g"�#��d�wpg�F/0��^�x�h�����D�[�\ޚ���V��ey]d ɍ �H�}#�3r^H˕�k���S�:1d�7�_�_Ka�i���ʕ�Ŗ��B��E��@<YH���|��������A�9:���z���̼�����s���֢z��Dݞ�n�]�Q�U\�
�������0����f6�
?C�	�&� ڊ��-$���껀kJA��͘R_�%��BB$�����R�q�Xt��D:������dעw�Z��Y]V1�� �at�G4�����1��ժț`�%Q��p}���8?���v2�z�sW��`z:�'���+O���$A�p��=z*��s��X�i��A�M9T�t3��w�λ���Ia��i�y��}��MUU���#����9���$$���d�{��}�٨J����31���Q�ք��$=&r���^r�d}�^X?$v���aL|�wxvC���β!��N�DI<.v�!`۞�=�q��@:rt7�AoF�"�� ��t�ޅ�5��r�0��g¾�:��Y2�G���̤��z-!���z|\%��m�k@b?�8�I`�<�@+Қ}��@r��Pl~�3���Z��R���V�y	Ʒ����r��!�M�z�Kf�d5]•��ߪ����"��Ӗ�~���g��y9�_p^M�X�"��`L?���ңచ"��9�Y(4��x\Ǘ	����(I$��A^cHXrA�\��sU�at���u�JwΈ�Ap[�R���x|ɾ����`��e���{PDӃ �a��i[f�:t�"���O���| ���w��8L&�u��e,�3�)��º�^O��0�u{�JJl����Q���P��N� srm�j��Y���8A�\�р
�8T��"{�C��B���s�JzP�Y��B,�PD�{ �+�</8\��~)rUބl��W�b��=������B��-����A���9j���HE��Vq���� jM��@21or��dп��F�ڛ�f�K��-Ԗ%M����� ���8��!�f�=B��%*+�Txj�M�C]�{������M��\���/�v #3(Q��b��N�m�6�L��H�UTT,3�7T�qf)nV7M�~n77OO,��I"�j�έ�7���K7_�!��}?: ��g0�〬��Q��Q�~���ْ�X��WFN	�|y�I�щv��.3!���9N؎�����Y�[��'��L&͋X���ry�_1 p���]�7��$/E�$&7_s����a��j�;�4'�6�π1~��lT��(7�G��=U����E/�s�$�x��(*GGy.���Y�MM�+�uB���Q?JWB�n�fjNHN#�y�iY�3���!��Ǻ #�TUKTV&�d��D�:�0��̽�4Ȳ�g���|�%7V<��
��'xm硌�U�㑺����A����g���7��S�`�3�O%#cL	yU���u�
sEP9DX���5{�>����BO�ɸp*앒9%��Xd���6Ӑ���%�1�o<�(?B��t���8e�'��{B�Ok�}�����sଡ଼�@�B8��9�4�u��eFV�q��4��Y����H��O��?�bwO�����C����xVvϛ�^�$ �M=.�gҗ�漸��9�,��DY��}�TQ}��·"M�W
�†h,�#=��b�����0�_��F��(���8�@�@;I���H0$�.��H����8[��L����.������B�,:��9)#���a(���X��S��0����b��Րi�ԭ�@nO?Z��h���hȻ>��3@��-����e^Oȗ=����k�=@��	9��˗kNr���t� �$�I�8(�A�ɤ�E�2q�1$l�#���u� �Kt�gt�v���d�M<�0U%J<xz(���V �}@pGQ�D�9~�D� �a�{^͇�h��L>DgB�X�cf����X.�ǽ^�=�}y������'C����ȷ��e6�`��LIo�0��7����{�Υ���-��������G�wwI4�?�X�Ĕ�n!9H�����}�]�? �_H��]p�MN|M�ԍ�t��X:���2�>IN�|A�M8�E�U�y�j%="j�����D��l��<Y���~"J����m�</�f���&��\BBh�ɿ�&�����%��� �4#�O9�טH���l���~���N�b�a��H_4M�~��wTu�܁�^�;�D�(Q�DɟV,���	�-?���Ķ}B�I
󒚄qQ
VR
�=��0����@^ov~�f4�2�c�D��� d�=��N�_���s5$n@�,���5�~��	IEND�B`�PK
!<y�ff=chrome/browser/skin/classic/browser/customizableui/whimsy.png�PNG


IHDR@@�iq�-IDATx�uT�����n��6)A:���y���E�[�);P��*%�t���g�㬃��?��֙b�����#1vp��FK�HeE�-�����6Y�U?��=���s�,��GyL�bچ��{m���[�����Ϯ��,D�3�J�����QX�DŽ��~���!��I���q��\:ws�/CG�>�ˀʶ;�X|g{/8�^,^B�3w	��i�~̫�w��3���w$���K�o +-�C�Сk�Qok��h�.
`�mT��,�����2�D�8zA����j��m����!�i�Ie�!v
�yD��6�ޕ,��G7A�6���<��J���3���V�_G�E�����
�a�� (rE!Q�@F�k�ט��+��~���H�ގ�Ho'�P|O�ŃŰ���8���K�s9Dkb���"DE���K�q�x"�.X���� 0h=lzx��o�J��5���[�	Ҁ�:���
'���n�F��h;#����n�:M���L���|�yi��"�a!��l�k��j@\�Ԏ,�9�e�h`�I	,�Z��y�[^S�?a6�>�C���h�ߌ��1�ר�=��Dqv%r�*�$h�,zL� ��,��Kw�wsm�U[�s��~����0��CÑ`9$V�.����Z\�/�@�:٥��|��mâ�k�i�~DF��\hX��B�0og��Z3�SS���>�-h{)���qN3L���Os�c�$8nD'�ʈ��2�_�ʂ�����Ç��ux���D��[��p=4��г~/��P2oi�i�6���M�48�#h{q�vf=l:�P0�Q(�2?_�$��%��_�fT�m���(˫D�J�x\���J�yE��zfR��BRŀ
�f�jՀ!S�#e��m�wp�N�	'.FA.����;�ȡBQ��ۮ@�W�����D�Y����R�E�6����Ӟ�g�(���&Н����C����/���]�}��R@�9k���a�p��SP����$�iu~�x|��	�q��-�ӷ<E����5Au����������~g9�"qi=��¾��x��zx������8w�
��/��a��a���D��͸�����~�����ed��z�Ej_���߁H�"�U��=�5�L�m�\6Fg����8�dd��D�����i`���ٔ��,��_�|�V�D��X���fз�]!�}ujO��k�շ��ٻ��]OX�;���\�Q�B'ib���.h��im0b��H�����پ3�:��%ѕ*�s��AY�y9����j��v#@���Q|���ф-�V�(2�	Y&��E�X�cόV(�!���m�q�N�o��^X�zc�iiY��d����3��";KW��c���dm3�p}[ê�pz S����KX�7D3��Ծ/�vi�,��(��2:�A�/ڸ���l� u��%m�"h6�/^�;��E��p5cR����#�&�+���� ds�v�
{E�s��a���*�W�Ѧl����on�
{�_A�S	[/'D�j�a�������"d�&R�}��%�7����\�Ĺ�ɜ��գ�&d;4
�8�G��}���Q\T��|�C��1���=\P��)���_י���܏���BB�?ʀ��[��d�`��zB�3�%���ر	J�}�[A�!د-�fw���Ȥ
"
}-�
���@��\ܺ�����:��RQ�&��J�t����}�ͳ	S�ۂ��暠�c?�]a�i	�1-�?
�f7a�/
pt5�Ma+��|8,m�qR�%L�p�}s��Ws�	�F`_x�,�Ė��)�-����j�z�&�	G��Ab�e6a��C�2����R��zAHkT����Y'��Co5��օpp������h�۷}{�K�׿C ����g���65��N�lb��M�@|DN����Uї�	(!+}3|��Se�Z�H}���U(*i;�:Z�O�p��
��PqT��um0k�5�����%��WE���!���΂����w���m<f�^��h!�
g`�s��EL0��m1\�[\i;d%22��
P�.Tʼ��Mxň�2�oy��uM�b�W͸�9?��]u�<"������
1�~7?��[b��$�pF�0v�3'.j
k0�����ix���cx���S\M�!�nV4�%�y���uw�&��p<�iI
&T1���Qu_%��T����-o�ԏ�@K*�
�<$�� }\�c�?Ô��~�v����%�ȁ#kN|��u��a�f\Ʉ�AQ?��m�s�&aj�͘2�w̝6��\W��(顕*�
H���r�E�
>&��r���	9/�Ѳ}���4�D��v:yb� g�6G鎺(9�v,6���`R·����6��τ�N��D��/����e0u"��5&D/k����b|��f�|�Я�0]s|�gc��z}űx�X�UQ|)�X���ҰsW$o���@�1����*�������=~�Wef$�pE�-�ww�v�ùF��
�?E�j]�&����	3���Nc@oq!Z�(A��p��`@�K�4�TϼDz��VO��c�k�_��E�X����`͢0$��"&U�Y�yJqP^�����X�v+o���B�k3��	�棽E�Op�v�v-����-qå'�����q6���?����O�`FT��:c��V،}�:!2h/(@���,h�A��}�8a��`�\�-��_�'���0k^0��Ǫ��"�8�~���7q-���qр�'�J����<Bf�vtCEJ<�C��VHcG&Hp3��
ф�U	f��Ũ�1��.����O�d�q��*�LhZcźM().�.X����ȫ���Y)=w�Di���6k1Dy�eEr�:b<ֆ젚!�g�gcᒵh��&�c�Ob~p�s��Mr�6�r���>�3�
�t�,B���"q�~�2a�����s���Ɉx��G�ӪVV�+�L���/��w,G�U�9م��:a�"Y��L��.B_��2t,\�e�4?ަySa�T0@��e�FٸC����q��	�z�P?�nY���~��Z�K����믻`d1�c�BZ&�(Z�&�E�T��՞Ӟ ����`ʋ����x�ۮS�n��goR��ő#g�l�&��p]c[hv�=غi����M�h�t���:��x��6@]�"�
����
�N�P��ٶf�j�g,�`W��(��=��(65���&�n�72�R�jdE��-�w�r/�2������6��gR�W��!�����6�##�Qt3�v�zC�S�HAl��
����'�w���H�I�R6@]<�Ա���V�(s҄�{dv5�IS�y �go���{$H���組s_We���«�� ZE��,@��T�'$\fy��V��	��R0��&��Ub?��I�*�xU��T/�b��}��(LA��
��o������
�|tP����2D���,^!�*��ռ��ꂫ#F���K���Gi��}��cl7[F���b�)�ȋ�)S����A6a�h��H���@�Uؠ?
�F搙�}kL5������0�s`5�rQ�_
-{KJHx����0�X��*�
��	��\@�TO�]M<#�`V��]���]=�,C
�cw<��%���߾5����-�ł&F���Jt�xI�[R$"��k����0�7�>@&
e�o��X\��׉'�M�5��q����~���M�1Mg9~!3���������z��b�(����n�t.��w�0e�,�5��T2�

�� Nr������$)�%W랓P�bu����,q?^ƙRM#���a0��ȣ*_VU|�(�_��� .�"���)1�t�.XQ3o��Ʌ�.J(�^D<!7�㩲��R��sp��Tѯ"��4@��q����LM�(�_P�����O�q?A�%<�
%�E�llJ�]�ѷL���3��CIZl������[ <t��#35�?g���#�^��6O�|����<|}o�d��У� �>�����NLl�z
ŖmuEyR,X�����"�]�ocX�;6���'��6(+Q�<�����0i�"��������Y�[�߰y/�� �EJ�Ȍ�,���|,�{�'����j�iW��fr����[(Η��tdP*�튂���u;(��7���>�]{bXx�
��S�/p}��
����]�n��W��_�%��Ąd&g
s�1k�2�՟�[������Cx�Q��Ru(�?|�����J�7CXщ���`7���M��[��rq��QH���S�c٪��8�:�dU������K��K��,-�“
/��������/���#A�g/��+p��
��|y
r��k؀��a�N�>MY �\5�������N
>95wpA�bр{wRq�N8��"���m�b�.�WC-�Z��pdg���$�sPD���dh�[��@��[qg�@�kP���X��_]jbDv퉦b�]�N4!�Y��e(y�	l�iֆ�Ǚ��6h�Wx߉�>��?~�0�
�}�1�_��O��M[��	����Fb]�N\�rW��U#�M�x�Ճ'��	R�.^�����εh��ot23�����5�d�M����$�7�|zjU��(̓rV0Ռ`���ٴ��NDw����a��`l�Uh�cU��HM�Š�[0q���8��gc��
	�9���L�hX8��c���zz��|�i��_�&�����3�T���'Li�;�B��pde��}oO�۸XԚ�p�B2�ϓ�!�Qν@��#��s��wCGN��@\�p\��[����3��t�fG�)���
n��g���m�V�ن�<y5��O��{w���e���bS��i�U�@{��a���S�E�W���p8[�G�9�?���;���[��T�����<�S� 
s��}��8�q�`l�^C|q��
в�c�<@�e��Ĥ��AP	G_�B-��K�A���'t�i�B�KD
��6-���/�~��&S��8y�� �Zz�K��$$^�P���3�>S!�Et�t۾t�1��P;􂞉=���T��QS5�n����G�d�D�����F��=�,�z�C���,8~�|��5��X0>}~����s�4>�
�ׂ���S"D����);�����

}K���BVpv.]S���/go�����T���3�
;|$���?�Q��s4�-����6� �J�^�����0B�rP^Q+0���3���}1�w�O(�y��Ÿ���+W��54>���j2S�h�Z���.����o��'t�����i(�zy�…�س/�_�
��~�€�u�~.|�-���M�Uww�]��{ӑs
�>YW��ص7��IZj6EϼH�ɕ1�Ë&hٝ�v���Ū!�@zuN��1���|�.�
C�DZ�.���ށ�F;=]�N{���q�!���c�^��񀞑wVҞ<GNv1���òY-Zo���Ѳw}���w��M�	^��8��,W�nI|��"6���`uhX�
U|�8z,����e!��s^ӳp�
�YYՇed|�
��ڵ�����-�	��V���l����}�����)U-��G_4�o�
`�k��7�:F�s5mR��r�T������ꭐ��T�� c1\U+���v���	�=�f�+���<�O�;IEND�B`�PK
!<\��1chrome/browser/skin/classic/browser/customize.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M4 10a1.994 1.994 0 0 0-1.911 1.44c0 .01-.014.015-.017.025-.362 1.135-.705 2.11-1.759 2.573l-.023.012-.024.012A.5.5 0 0 0 0 14.5a.5.5 0 0 0 .5.5 6.974 6.974 0 0 0 4.825-1.5c.006-.006.007-.013.013-.019A1.993 1.993 0 0 0 4 10zM15.693.307a.984.984 0 0 0-1.338-.046l-8.031 7a.982.982 0 0 0-.049 1.433l1.032 1.031a.983.983 0 0 0 .693.287h.033a.982.982 0 0 0 .706-.335l7-8.031a.982.982 0 0 0-.046-1.339z"/>
</svg>
PK
!<��@�XX1chrome/browser/skin/classic/browser/developer.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M14.555 3.2l-2.434 2.436a1.243 1.243 0 1 1-1.757-1.757L12.8 1.445A3.956 3.956 0 0 0 11 1a3.976 3.976 0 0 0-3.434 6.02l-6.273 6.273a1 1 0 1 0 1.414 1.414L8.98 8.434A3.96 3.96 0 0 0 11 9a4 4 0 0 0 4-4 3.956 3.956 0 0 0-.445-1.8z"/>
</svg>
PK
!<}�6chrome/browser/skin/classic/browser/device-desktop.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M15.5 12H15V3a1 1 0 0 0-1-1H2a1 1 0 0 0-1 1v9H.5a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h15a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5zM10 13H6v-1h4zm3-2H3V4h10z"/>
</svg>
PK
!<��&O5chrome/browser/skin/classic/browser/device-mobile.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M12 0H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zM9 15H7v-1h2zm3-2.5a.5.5 0 0 1-.5.5h-7a.5.5 0 0 1-.5-.5v-10a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 .5.5z"/>
</svg>
PK
!<�r�cCC5chrome/browser/skin/classic/browser/device-tablet.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M14 1H2C.895 1 0 1.895 0 3v10c0 1.105.895 2 2 2h12c1.105 0 2-.895 2-2V3c0-1.105-.895-2-2-2zm-1 11.5c0 .276-.224.5-.5.5h-10c-.276 0-.5-.224-.5-.5v-9c0-.276.224-.5.5-.5h10c.276 0 .5.224.5.5v9zM15 9h-1V7h1v2z"/>
</svg>
PK
!<��R33Ichrome/browser/skin/classic/browser/downloads/allDownloadsViewOverlay.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */



/*** View and outer controls ***/

#downloadsRichListBox {
  /** The default listbox appearance comes with an unwanted margin. **/
  -moz-appearance: none;
  margin: 0;
}

/*** List items ***/

#downloadsRichListBox > richlistitem.download {
  height: var(--downloads-item-height);
}

.downloadTypeIcon {
  margin: 8px 13px;
  width: 32px;
  height: 32px;
}

.downloadBlockedBadge {
  margin: 0 5px;
  background: url("chrome://browser/skin/downloads/download-blocked.svg") top right / 16px no-repeat;
}

.downloadBlockedBadge:-moz-locale-dir(rtl) {
  background-position-x: left;
}

richlistitem.download[verdict="PotentiallyUnwanted"] .downloadBlockedBadge {
  background-image: url("chrome://browser/skin/warning.svg");
}

richlistitem.download[verdict="Uncommon"] .downloadBlockedBadge {
  background-image: url("chrome://browser/skin/info.svg");
}

richlistitem.download > toolbarseparator {
  display: none;
}

.downloadTarget {
  margin: 0;
}

.downloadDetails {
  opacity: 0.7;
  font-size: 95%;
  /* Use calc() to keep the height consistent with .downloadTarget, so that the
     progress bar can be vertically centered. */
  margin: 4px 0 calc(1em / 0.95 - 1em);
}

.downloadDetailsNormal,
.downloadDetailsHover,
.downloadOpenFile,
.downloadShowMoreInfo,
.downloadButtonLabels {
  display: none;
}

.downloadButton {
  -moz-appearance: none;
  -moz-box-align: center;
  background: transparent;
  min-width: 0;
  min-height: 0;
  margin: 0;
  border: none;
  color: inherit;
  padding: 0 18px;
}

.downloadButton > .button-box {
  -moz-appearance: none;
  padding: 2px !important;
}

.downloadButton > .button-box > .button-icon {
  width: 16px;
  height: 16px;
  margin: 0;
  -moz-context-properties: fill;
  fill: currentColor;
}

.downloadButton > .button-box > .button-text {
  display: none;
}

.downloadButton:hover > .button-box {
  background-color: graytext;
  color: -moz-field;
  border-radius: 50%;
}

.downloadButton:hover:active > .button-box {
  background-color: -moz-fieldtext;
}

richlistitem.download[selected] > .downloadButtonArea > .downloadButton:hover > .button-box {
  background-color: HighlightText;
  color: Highlight;
}

richlistitem.download[selected] > .downloadButtonArea > .downloadButton:hover:active > .button-box {
  background-color: -moz-field;
  color: -moz-fieldtext;
}

/*** Button icons ***/

.downloadIconCancel > .button-box > .button-icon {
  list-style-image: url("chrome://browser/skin/panel-icon-cancel.svg");
}

.downloadIconShow > .button-box > .button-icon {
  list-style-image: url("chrome://browser/skin/panel-icon-folder.svg");
}

.downloadIconRetry > .button-box > .button-icon {
  list-style-image: url("chrome://browser/skin/panel-icon-retry.svg");
}

/*** Progressmeter ***/
/*** Common-styled progressmeter ***/
.downloadProgress {
  height: 8px;
  border-radius: 1px;
  margin: 4px 0 0;
  margin-inline-end: 12px;

  /* for overriding rules in progressmeter.css */
  -moz-appearance: none;
  border-style: none;
  background-color: transparent;
  min-width: initial;
  min-height: initial;
}

.downloadProgress[mode="undetermined"] {
  /* for overriding rules on global.css in Linux. */
  -moz-binding: url("chrome://global/content/bindings/progressmeter.xml#progressmeter");
}

.downloadProgress > .progress-bar {
  background-color: Highlight;

  /* for overriding rules in progressmeter.css */
  -moz-appearance: none;
}

.downloadProgress[paused="true"] > .progress-bar {
  background-color: GrayText;
}

.downloadProgress[mode="undetermined"] > .progress-bar {
  /* Make a white reflecting animation.
     Create a gradient with 2 identical pattern, and enlarge the size to 200%.
     This allows us to animate background-position with percentage. */
  background-image: linear-gradient(90deg, transparent 0%,
                                           rgba(255,255,255,0.5) 25%,
                                           transparent 50%,
                                           rgba(255,255,255,0.5) 75%,
                                           transparent 100%);
  background-blend-mode: lighten;
  background-size: 200% 100%;
  animation: downloadProgressSlideX 1.5s linear infinite;
}

.downloadProgress > .progress-remainder {
  border: solid ButtonShadow;
  border-block-start-width: 1px;
  border-block-end-width: 1px;
  border-inline-start-width: 0;
  border-inline-end-width: 1px;
  background-color: ButtonFace;
}

.downloadProgress[value="0"] > .progress-remainder {
  border-width: 1px;
}

.downloadProgress > .progress-remainder[mode="undetermined"] {
  border: none;
}

@keyframes downloadProgressSlideX {
  0% {
    background-position: 0 0;
  }
  100% {
    background-position: -100% 0;
  }
}

/*** List items ***/

:root {
  --downloads-item-height: 5em;
}
PK
!<����Jchrome/browser/skin/classic/browser/downloads/contentAreaDownloadsView.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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://global/skin/in-content/common.css");

#contentAreaDownloadsView {
  padding: 18px;
}

#downloadsRichListBox:empty {
  border-color: transparent;
  background-color: transparent;
}

.downloadButton:not([disabled="true"]):hover,
.downloadButton:not([disabled="true"]):hover:active,
.downloadButton:not([disabled]):hover:active {
  background: transparent;
  border: none;
}

.downloadButton > .button-box {
  padding-bottom: 0;
}

#downloadsListEmptyDescription {
  margin: 1em;
  text-align: center;
  color: GrayText;
}
PK
!<9A�@@Bchrome/browser/skin/classic/browser/downloads/download-blocked.svg<?xml version="1.0" encoding="utf-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 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16">
  <style>
    circle {
      fill: #D92215;
    }
    rect {
      fill: #fff;
    }
  </style>

  <circle cx="8" cy="8" r="8" />
  <rect x="3" y="6" width="10" height="4" rx=".5" ry=".5" />
</svg>
PK
!<F�@chrome/browser/skin/classic/browser/downloads/download-icons.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <style>
    :root > use:not(:target),
    :root > g:not(:target) {
      display: none;
    }
  </style>
  <defs>
    <path id="arrow-icon" d="M7.293 12.725a1 1 0 0 0 1.414 0l5-5a1 1 0 0 0-1.414-1.413L9 9.605V1.019a1 1 0 0 0-2 0v8.586L3.707 6.312a1 1 0 0 0-1.414 1.413l5 5z"/>
    <path id="short-bar-icon" d="m 13,14 a 1,1 0 1 1 0,2 h -10 a 1,1 0 1 1 0,-2 z"/>
    <path id="long-bar-icon" d="m 14,14 a 1,1 0 1 1 0,2 h -12 a 1,1 0 1 1 0,-2"/>
  </defs>
  <use id="arrow" fill="context-fill" fill-opacity="context-fill-opacity" href="#arrow-icon"/>
  <g id="arrow-with-bar" fill="context-fill" fill-opacity="context-fill-opacity">
    <use href="#arrow-icon"/>
    <use href="#short-bar-icon"/>
  </g>
  <use id="default-bar" fill="context-fill" fill-opacity="context-fill-opacity" href="#short-bar-icon"/>
  <use id="progress-bar-bg" fill="context-fill" fill-opacity="0.2" href="#long-bar-icon"/>
  <use id="progress-bar-fg" fill="context-fill" fill-opacity="context-fill-opacity" href="#long-bar-icon"/>
</svg>
PK
!<�ڟh��Bchrome/browser/skin/classic/browser/downloads/download-summary.svg<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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"
     width="32" height="32" viewBox="0 0 32 32">
  <path fill="#fff" d="M26.2,31.4H6.8c-1.8,0-3.4-1.5-3.4-3.3V3.8c0-1.8,1.6-3.3,3.4-3.3H23l5.6,6.4V28C28.7,29.9,28,31.4,26.2,31.4z" />
  <path fill="#939393" d="M26.2,31.9H6.8c-2,0-3.8-1.7-3.8-3.8V3.8C3,1.7,4.8,0,6.8,0h16.4L29,6.7v21.4C29,30.2,28.2,31.9,26.2,31.9z M6.8,1C5.2,1,4,2.3,4,3.8V28c0,1.6,1.4,3,2.9,3h19.2c1.6,0,2-1.5,2-3V7.1L22.7,1C22.7,1,6.8,1,6.8,1z" />
  <path fill="#4c4c4c" d="M22.5,18.2L17,23.6c-0.3,0.3-0.6,0.4-1.1,0.4c-0.3,0-0.8-0.1-1.1-0.4l-5.5-5.4c-0.5-0.5-0.4-1.1,0.4-1.1H13 v-5.9c0-0.5,0.4-1,1-1h3.9c0.5,0,1,0.4,1,1v5.9h3.1C22.8,17.2,23,17.6,22.5,18.2z" />
  <polygon fill="#939393" points="27,9 21,9 21,1.9 22,1.9 22,8 27,8" />
</svg>
PK
!<c�J$J$Nchrome/browser/skin/classic/browser/downloads/notification-start-animation.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="1344" height="98">
  <svg width="42" height="98"/>
  <svg width="42" height="98" x="42"/>
  <svg width="42" height="98" x="84">
    <path fill="context-fill" d="M-0.712 6.158a1 1 0 0 0 1.414 0l5 -5a1 1 0 0 0 -1.414 -1.413l-3.293 3.293v-8.586a1 1 0 0 0 -2 0v8.586l-3.293 -3.293a1 1 0 0 0 -1.414 0.025a0.999 0.999 0 0 0 0 1.388l5 5z" opacity=".333" transform="translate(21.018 22.854) scale(3.50932)"/>
  </svg>
  <svg width="42" height="98" x="126">
    <path fill="context-fill" d="M-0.712 6.158a1 1 0 0 0 1.414 0l5 -5a1 1 0 0 0 -1.414 -1.413l-3.293 3.293v-8.586a1 1 0 0 0 -2 0v8.586l-3.293 -3.293a1 1 0 0 0 -1.414 0.025a0.999 0.999 0 0 0 0 1.388l5 5z" opacity=".667" transform="translate(21.017 23.815) scale(3.41419)"/>
  </svg>
  <svg width="42" height="98" x="168">
    <path fill="context-fill" d="M-0.712 6.158a1 1 0 0 0 1.414 0l5 -5a1 1 0 0 0 -1.414 -1.413l-3.293 3.293v-8.586a1 1 0 0 0 -2 0v8.586l-3.293 -3.293a1 1 0 0 0 -1.414 0.025a0.999 0.999 0 0 0 0 1.388l5 5z" transform="translate(21.017 24.798) scale(3.31695)"/>
  </svg>
  <svg width="42" height="98" x="210">
    <path fill="context-fill" d="M-0.712 6.158a1 1 0 0 0 1.414 0l5 -5a1 1 0 0 0 -1.414 -1.413l-3.293 3.293v-8.586a1 1 0 0 0 -2 0v8.586l-3.293 -3.293a1 1 0 0 0 -1.414 0.025a0.999 0.999 0 0 0 0 1.388l5 5z" transform="translate(21.016 25.793) scale(3.21855)"/>
  </svg>
  <svg width="42" height="98" x="252">
    <path fill="context-fill" d="M-0.712 6.158a1 1 0 0 0 1.414 0l5 -5a1 1 0 0 0 -1.414 -1.413l-3.293 3.293v-8.586a1 1 0 0 0 -2 0v8.586l-3.293 -3.293a1 1 0 0 0 -1.414 0.025a0.999 0.999 0 0 0 0 1.388l5 5z" transform="translate(21.016 26.795) scale(3.11952)"/>
  </svg>
  <svg width="42" height="98" x="294">
    <path fill="context-fill" d="M-0.712 6.158a1 1 0 0 0 1.414 0l5 -5a1 1 0 0 0 -1.414 -1.413l-3.293 3.293v-8.586a1 1 0 0 0 -2 0v8.586l-3.293 -3.293a1 1 0 0 0 -1.414 0.025a0.999 0.999 0 0 0 0 1.388l5 5z" transform="translate(21.015 27.8) scale(3.02017)"/>
  </svg>
  <svg width="42" height="98" x="336">
    <path fill="context-fill" d="M-0.712 6.158a1 1 0 0 0 1.414 0l5 -5a1 1 0 0 0 -1.414 -1.413l-3.293 3.293v-8.586a1 1 0 0 0 -2 0v8.586l-3.293 -3.293a1 1 0 0 0 -1.414 0.025a0.999 0.999 0 0 0 0 1.388l5 5z" transform="translate(21.015 28.805) scale(2.92072)"/>
  </svg>
  <svg width="42" height="98" x="378">
    <path fill="context-fill" d="M-0.712 6.158a1 1 0 0 0 1.414 0l5 -5a1 1 0 0 0 -1.414 -1.413l-3.293 3.293v-8.586a1 1 0 0 0 -2 0v8.586l-3.293 -3.293a1 1 0 0 0 -1.414 0.025a0.999 0.999 0 0 0 0 1.388l5 5z" transform="translate(21.014 29.81) scale(2.82134)"/>
  </svg>
  <svg width="42" height="98" x="420">
    <path fill="context-fill" d="M-0.712 6.158a1 1 0 0 0 1.414 0l5 -5a1 1 0 0 0 -1.414 -1.413l-3.293 3.293v-8.586a1 1 0 0 0 -2 0v8.586l-3.293 -3.293a1 1 0 0 0 -1.414 0.025a0.999 0.999 0 0 0 0 1.388l5 5z" transform="translate(21.014 30.813) scale(2.72216)"/>
  </svg>
  <svg width="42" height="98" x="462">
    <path fill="context-fill" d="M-0.712 6.158a1 1 0 0 0 1.414 0l5 -5a1 1 0 0 0 -1.414 -1.413l-3.293 3.293v-8.586a1 1 0 0 0 -2 0v8.586l-3.293 -3.293a1 1 0 0 0 -1.414 0.025a0.999 0.999 0 0 0 0 1.388l5 5z" transform="translate(21.013 31.813) scale(2.6233)"/>
  </svg>
  <svg width="42" height="98" x="504">
    <path fill="context-fill" d="M-0.712 6.158a1 1 0 0 0 1.414 0l5 -5a1 1 0 0 0 -1.414 -1.413l-3.293 3.293v-8.586a1 1 0 0 0 -2 0v8.586l-3.293 -3.293a1 1 0 0 0 -1.414 0.025a0.999 0.999 0 0 0 0 1.388l5 5z" transform="translate(21.013 32.809) scale(2.52484)"/>
  </svg>
  <svg width="42" height="98" x="546">
    <path fill="context-fill" d="M-0.712 6.158a1 1 0 0 0 1.414 0l5 -5a1 1 0 0 0 -1.414 -1.413l-3.293 3.293v-8.586a1 1 0 0 0 -2 0v8.586l-3.293 -3.293a1 1 0 0 0 -1.414 0.025a0.999 0.999 0 0 0 0 1.388l5 5z" transform="translate(21.012 33.799) scale(2.42691)"/>
  </svg>
  <svg width="42" height="98" x="588">
    <path fill="context-fill" d="M-0.712 6.158a1 1 0 0 0 1.414 0l5 -5a1 1 0 0 0 -1.414 -1.413l-3.293 3.293v-8.586a1 1 0 0 0 -2 0v8.586l-3.293 -3.293a1 1 0 0 0 -1.414 0.025a0.999 0.999 0 0 0 0 1.388l5 5z" transform="translate(21.012 34.783) scale(2.32959)"/>
  </svg>
  <svg width="42" height="98" x="630">
    <path fill="context-fill" d="M-0.712 6.158a1 1 0 0 0 1.414 0l5 -5a1 1 0 0 0 -1.414 -1.413l-3.293 3.293v-8.586a1 1 0 0 0 -2 0v8.586l-3.293 -3.293a1 1 0 0 0 -1.414 0.025a0.999 0.999 0 0 0 0 1.388l5 5z" transform="translate(21.011 35.76) scale(2.23297)"/>
  </svg>
  <svg width="42" height="98" x="672">
    <path fill="context-fill" d="M-0.712 6.158a1 1 0 0 0 1.414 0l5 -5a1 1 0 0 0 -1.414 -1.413l-3.293 3.293v-8.586a1 1 0 0 0 -2 0v8.586l-3.293 -3.293a1 1 0 0 0 -1.414 0.025a0.999 0.999 0 0 0 0 1.388l5 5z" transform="translate(21.01 36.727) scale(2.13718)"/>
  </svg>
  <svg width="42" height="98" x="714">
    <path fill="context-fill" d="M-0.712 6.158a1 1 0 0 0 1.414 0l5 -5a1 1 0 0 0 -1.414 -1.413l-3.293 3.293v-8.586a1 1 0 0 0 -2 0v8.586l-3.293 -3.293a1 1 0 0 0 -1.414 0.025a0.999 0.999 0 0 0 0 1.388l5 5z" transform="translate(21.01 37.686) scale(2.0423)"/>
  </svg>
  <svg width="42" height="98" x="756">
    <path fill="context-fill" d="M-0.712 6.158a1 1 0 0 0 1.414 0l5 -5a1 1 0 0 0 -1.414 -1.413l-3.293 3.293v-8.586a1 1 0 0 0 -2 0v8.586l-3.293 -3.293a1 1 0 0 0 -1.414 0.025a0.999 0.999 0 0 0 0 1.388l5 5z" transform="translate(21.01 38.634) scale(1.94849)"/>
  </svg>
  <svg width="42" height="98" x="798">
    <path fill="context-fill" d="M-0.712 6.158a1 1 0 0 0 1.414 0l5 -5a1 1 0 0 0 -1.414 -1.413l-3.293 3.293v-8.586a1 1 0 0 0 -2 0v8.586l-3.293 -3.293a1 1 0 0 0 -1.414 0.025a0.999 0.999 0 0 0 0 1.388l5 5z" transform="translate(21.01 39.57) scale(1.85587)"/>
  </svg>
  <svg width="42" height="98" x="840">
    <path fill="context-fill" d="M-0.712 6.158a1 1 0 0 0 1.414 0l5 -5a1 1 0 0 0 -1.414 -1.413l-3.293 3.293v-8.586a1 1 0 0 0 -2 0v8.586l-3.293 -3.293a1 1 0 0 0 -1.414 0.025a0.999 0.999 0 0 0 0 1.388l5 5z" transform="translate(21.009 40.491) scale(1.76462)"/>
  </svg>
  <svg width="42" height="98" x="882">
    <path fill="context-fill" d="M-0.712 6.158a1 1 0 0 0 1.414 0l5 -5a1 1 0 0 0 -1.414 -1.413l-3.293 3.293v-8.586a1 1 0 0 0 -2 0v8.586l-3.293 -3.293a1 1 0 0 0 -1.414 0.025a0.999 0.999 0 0 0 0 1.388l5 5z" transform="translate(21.008 41.397) scale(1.67492)"/>
  </svg>
  <svg width="42" height="98" x="924">
    <path fill="context-fill" d="M-0.712 6.158a1 1 0 0 0 1.414 0l5 -5a1 1 0 0 0 -1.414 -1.413l-3.293 3.293v-8.586a1 1 0 0 0 -2 0v8.586l-3.293 -3.293a1 1 0 0 0 -1.414 0.025a0.999 0.999 0 0 0 0 1.388l5 5z" transform="translate(21.008 42.284) scale(1.58703)"/>
  </svg>
  <svg width="42" height="98" x="966">
    <path fill="context-fill" d="M-0.712 6.158a1 1 0 0 0 1.414 0l5 -5a1 1 0 0 0 -1.414 -1.413l-3.293 3.293v-8.586a1 1 0 0 0 -2 0v8.586l-3.293 -3.293a1 1 0 0 0 -1.414 0.025a0.999 0.999 0 0 0 0 1.388l5 5z" transform="translate(21.008 43.15) scale(1.50124)"/>
  </svg>
  <svg width="42" height="98" x="1008">
    <path fill="context-fill" d="M-0.712 6.158a1 1 0 0 0 1.414 0l5 -5a1 1 0 0 0 -1.414 -1.413l-3.293 3.293v-8.586a1 1 0 0 0 -2 0v8.586l-3.293 -3.293a1 1 0 0 0 -1.414 0.025a0.999 0.999 0 0 0 0 1.388l5 5z" transform="translate(21.007 43.99) scale(1.41793)"/>
  </svg>
  <svg width="42" height="98" x="1050">
    <path fill="context-fill" d="M-0.712 6.158a1 1 0 0 0 1.414 0l5 -5a1 1 0 0 0 -1.414 -1.413l-3.293 3.293v-8.586a1 1 0 0 0 -2 0v8.586l-3.293 -3.293a1 1 0 0 0 -1.414 0.025a0.999 0.999 0 0 0 0 1.388l5 5z" transform="translate(21.007 44.8) scale(1.33758)"/>
  </svg>
  <svg width="42" height="98" x="1092">
    <path fill="context-fill" d="M-0.712 6.158a1 1 0 0 0 1.414 0l5 -5a1 1 0 0 0 -1.414 -1.413l-3.293 3.293v-8.586a1 1 0 0 0 -2 0v8.586l-3.293 -3.293a1 1 0 0 0 -1.414 0.025a0.999 0.999 0 0 0 0 1.388l5 5z" transform="translate(21.006 45.573) scale(1.26087)"/>
  </svg>
  <svg width="42" height="98" x="1134">
    <path fill="context-fill" d="M-0.712 6.158a1 1 0 0 0 1.414 0l5 -5a1 1 0 0 0 -1.414 -1.413l-3.293 3.293v-8.586a1 1 0 0 0 -2 0v8.586l-3.293 -3.293a1 1 0 0 0 -1.414 0.025a0.999 0.999 0 0 0 0 1.388l5 5z" transform="translate(21.006 46.299) scale(1.18877)"/>
  </svg>
  <svg width="42" height="98" x="1176">
    <path fill="context-fill" d="M-0.712 6.158a1 1 0 0 0 1.414 0l5 -5a1 1 0 0 0 -1.414 -1.413l-3.293 3.293v-8.586a1 1 0 0 0 -2 0v8.586l-3.293 -3.293a1 1 0 0 0 -1.414 0.025a0.999 0.999 0 0 0 0 1.388l5 5z" transform="translate(21.006 46.963) scale(1.1227)"/>
  </svg>
  <svg width="42" height="98" x="1218">
    <path fill="context-fill" d="M-0.712 6.158a1 1 0 0 0 1.414 0l5 -5a1 1 0 0 0 -1.414 -1.413l-3.293 3.293v-8.586a1 1 0 0 0 -2 0v8.586l-3.293 -3.293a1 1 0 0 0 -1.414 0.025a0.999 0.999 0 0 0 0 1.388l5 5z" transform="translate(21.005 47.541) scale(1.0651)"/>
  </svg>
  <svg width="42" height="98" x="1260">
    <path fill="context-fill" d="M-0.712 6.158a1 1 0 0 0 1.414 0l5 -5a1 1 0 0 0 -1.414 -1.413l-3.293 3.293v-8.586a1 1 0 0 0 -2 0v8.586l-3.293 -3.293a1 1 0 0 0 -1.414 0.025a0.999 0.999 0 0 0 0 1.388l5 5z" transform="translate(21.005 47.988) scale(1.02049)"/>
  </svg>
  <svg width="42" height="98" x="1302"/>
</svg>
PK
!<����yy0chrome/browser/skin/classic/browser/drm-icon.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
  <path d="M7.058,9.72c-0.245,0.245-0.62,0.27-0.834,0.056C6.01,9.562,6.035,9.186,6.28,8.942l0.218-0.218 c-0.245-0.245-0.645-0.245-0.89,0L4.496,9.836c-0.245,0.245-0.245,0.645,0,0.89l0.779,0.779c0.245,0.245,0.645,0.245,0.89,0 l1.112-1.112c0.245-0.245,0.245-0.645,0-0.89L7.058,9.72z"/>
  <path d="M10.726,4.496c-0.245-0.245-0.645-0.245-0.89,0L8.723,5.608c-0.245,0.245-0.245,0.645,0,0.89 L8.95,6.272c0.245-0.245,0.62-0.27,0.834-0.056s0.189,0.59-0.056,0.834L9.502,7.277c0.245,0.245,0.645,0.245,0.89,0l1.112-1.112 c0.245-0.245,0.245-0.645,0-0.89L10.726,4.496z"/>
  <path d="M8,0C3.582,0,0,3.582,0,8s3.582,8,8,8s8-3.582,8-8S12.418,0,8,0z M12.527,6.81l-1.489,1.489 c-0.631,0.631-1.663,0.631-2.293,0L8.612,8.167L8.167,8.612l0.133,0.133c0.631,0.631,0.631,1.663,0,2.293L6.81,12.527 c-0.631,0.631-1.663,0.631-2.293,0l-1.044-1.044c-0.631-0.631-0.631-1.663,0-2.293l1.489-1.489c0.631-0.631,1.663-0.631,2.293,0 l0.133,0.133l0.445-0.445L7.701,7.255c-0.631-0.631-0.631-1.663,0-2.293L9.19,3.473c0.631-0.631,1.663-0.631,2.293,0l1.044,1.044 C13.158,5.148,13.158,6.18,12.527,6.81z"/>
</svg>
PK
!<Mml��2chrome/browser/skin/classic/browser/[email protected]�PNG


IHDR��i7�@�IDATx�t���dDZ�)���2ϼefff�\fffff޳���4�p�09�%��i7g���ݞt��`�'��וj�`���MUUUUUUUUUUUUUUU<��S`UpA�(��@f�6�$}6?�'���k>"H�>�����T��G?�@�,�*��R��)%f��/�ᾤ�)�|�NK�kI�*X�������f�	�NX��=j�<��f��vْ����S_k$���b&*)�j�`�����S��4�+4�p+
� %"+8�ġA�H�	!��k�Po�H�T��+�.��u�@g��w
V9�I�
���K
�s)Q�g�Y�#J��X��)3������]3�ʥ�$:�gA�k�)�0+\�)ls��{�R����׋Z7�Ê^��Qh[��C��XԞ��Y�߽te�����;�7�����+y."�~������[�7��=�3�ۣ����A��^�L�cP��PJl7yϘ�d�O���7�4`�Q���H�3b�7
��`�%�#�O���(�a�7�\�"�.e�I��
�rj	PT��a�
�n(�� �[_��5k&i��bb-s�5��o�� 
�8Pm�Yi�b�)O��j�a�"p�l-)�zD9j"浔u����s78N-���fg�@x�o]�4��> ��6� �1�jQT	��^�}t���_ٞ/ A��*���½q���g8���
Ģ(=?3��C8n/����J\�4��E����\��v�c�3q�8*
�h�W�]v�=K���s���c��O�Cv)���b��a-��(BgS���j�ž�w
���S[�u�`�&�^���̬�G��tR���#���eM1k�e"���J�K�)s�2��zhy�
+\�꓏�$0�1�#� ^d����g���<I��-W#�������ݕM���=�(��6�g�O86�3�����1s�?on���u��T�fl4�pF��c�F�r�#���;�h��]���э�֧5-!�	�R̒�t�v�\ݑ���4?#��W2���<����$SX�a#��c���g6/��c��N�	2>�i(.y�����X�؝�b�aV�nX(N�!kr���x9����S"��,*�y����0���o�X���`��79�^N�g���E���"�F�����Sto2�����Y�.�k˦DF�0Ț������`R��_p��0h"PH	>����a�?�����fF�~C����I��ZH����?&�zxߜ�=�x�����V��KA�2�h8f	E�'"R������!9�X(o
9���>�+^���UD6�^Ul�~FD��23!ի׺<���'���s�?��CQ+��6W�Jc�lRt�0)�~g�4�z�������}B*g"h8n����/�t�3�Z�䩡i�r{'m
9�7�O��.���.!�
傇ȯ�t��溯m�7k�����G�m�`���XЅ�Wj�KH��¢�kt_2��z���oڱ.eV@��H��-�@;�a\@��Wk��q"P�=5��sh���5N�ͩZ��0����rn.S}i���^�D0����>�#�CY�ϰ�1;b�=[
~����KAKj6����A��������:���~��J�N�h��t��5&��
Eis�=��
��"����"E}2�Z�o�!!%y����<�ѿ)JQV:��7� ����똅�~=����%T[����?�M}̬�w<��6���W�e Xp�[�_�,^6��/�/.z%yM�u�۵!-)�9T��錁^}���{�n�".�'@��wy��K�Y���V����l��o���E���f9(KH�w���%Ķ���94ӫ
+���\��`���_	J���I4E��`!�]�|�4Ե�|����޶�߫��y2�+ذ����\þ�rUB�V@p�-��>�����ҢW�����l�3k��F@�t�s���9�_��
@ar7���w���P�O�J�ֹq�����0����iW�!b@:,b`~�	}�S�i|wq�2�QP<���'0�D�G��=�ɡ�.�� ���b\
�#Y
ʒ�S��K:�oK�#:>���2���w��d�`@9[��S��z_��)n"#~KlHIs��~2�O�w��u�sk�Nm�a7���n����	����!G�����:'�4-�R����s����|Sr�v�K[��~E:�!�=�D=��?u�;w�˴�IyL�]������cX�E��ktV������B?��y��r�I�����(:7�⟃r4��dWSL��*+C�~k�H���v�u��LS���9gS�8�f����������^�5b�U;~ '8P�;�^�Iz�]���ܸ	���nYL1�H��u<��|�3^ٰ5jV˺)
��e��;�x�I>o��s�&���=��,h��S��׆�|ѷ�u�7&%�?�)��"�LAG�r�j����4�Ú�;Pq�&c�
e��u�Y�<��sښ�vq�m����A��̅�h�L`�����.<�%Od�/d�<��x��m�)|��3��fw��k5=;B�1�+�qq�gfC�ѳ�}g'�������,
N �&�RN������6iom�rFD����u&�2?]��ٕ�3�!3Q�K`�1�J�-�ķ�o|_X��u%EM��&=^q||�*�������&1E����?�l����m��wV�w���|�=˖��;�;�0kzgf�(H�B,p�2���a|�>Ǻ�U����z�#��I�bP�{�C�
��u��	P�&U�E~Y�;s�:��6�n�����A���dFJ}��G[ɴ�Y��%�BN���yi'�"�4�-dws��vc��Em>s��R��[�Gy�7G���@qeCs�n��n�-wm��J��w� ��w�Τ��[��u��f4�/����N]����.v#�<�g��Ѹ���^b�[a�Ң���^�q�-=������{jX/Pg�c���ӎ'�
vߦ�EE`�@�A���p?����ǚMS��(����4�m��/�ɻd��`��	2$s��O�A����_ܨ9ꎏ�+�-���gHDf,�156�����\��Gs'�
p�J;ϙ�%u�z3k^T8Z��g�nPC�ז8�E<��ݼ)��lT��S�#��8!�j"���/���,d+�}%K�����Y��?�]c�&T��EaG�+^�R��(�,W��pd�/�?�1Q*^>Z��S/�lj�:�1`H�	��M?%dˏ����<��Lƌ�;A��̃��>��H�*������1+1@��1�Ƭ���ib���C9a�  X�U��4t)�MjM8f�ƀ.#e8x]���e̖+���m��݁���
�`:H�n��.�3��Q�HQ$��%��/x��`e�����k���Џ^�9��!O[9�����`�&(��6T�����[����մ6�������������������?�8�&'���IEND�B`�PK
!<T��8��1chrome/browser/skin/classic/browser/edit-copy.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M14.707 8.293l-3-3A1 1 0 0 0 11 5h-1V4a1 1 0 0 0-.293-.707l-3-3A1 1 0 0 0 6 0H3a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h3v3a2 2 0 0 0 2 2h5a2 2 0 0 0 2-2V9a1 1 0 0 0-.293-.707zM12.586 9H11V7.414zm-5-5H6V2.414zM6 7v2H3V2h2v2.5a.5.5 0 0 0 .5.5H8a2 2 0 0 0-2 2zm2 7V7h2v2.5a.5.5 0 0 0 .5.5H13v4z"/>
</svg>
PK
!<|��0chrome/browser/skin/classic/browser/edit-cut.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M11.5 10a2.481 2.481 0 0 0-.379.038L3.977 1.214a2.5 2.5 0 0 0-.371 3.515l2.789 3.444-1.51 1.866A2.486 2.486 0 0 0 4.5 10a2.522 2.522 0 1 0 2.329 1.609L8 10.159 9.172 11.6A2.5 2.5 0 1 0 11.5 10zm-7 3.75a1.25 1.25 0 1 1 1.25-1.25 1.251 1.251 0 0 1-1.25 1.25zm7 0a1.25 1.25 0 1 1 1.25-1.25 1.251 1.251 0 0 1-1.25 1.25zm.9-9.021a2.5 2.5 0 0 0-.371-3.515L8.5 5.569l1.608 1.986z"/>
</svg>
PK
!<a�(���2chrome/browser/skin/classic/browser/edit-paste.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M11 2H9.95a2.5 2.5 0 0 0-4.9 0H4a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h7a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2zm0 7a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V5h7zm0-5H4V3h1.05a1 1 0 0 0 .98-.8 1.5 1.5 0 0 1 2.939 0 1 1 0 0 0 .98.8H11zM7.5 2a.5.5 0 1 0 .5.5.5.5 0 0 0-.5-.5zm-2 5h4a.5.5 0 0 0 0-1h-4a.5.5 0 0 0 0 1zm0 2h2a.5.5 0 0 0 0-1h-2a.5.5 0 0 0 0 1z"/>
</svg>
PK
!<J�Y���3chrome/browser/skin/classic/browser/error-pages.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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://global/skin/in-content/info-pages.css");

body {
  background-size: 64px 32px;
  background-repeat: repeat-x;
  /* Top padding for when the window height is small.
     Bottom padding to keep everything centered. */
  padding: 75px 0;
  /* info-pages.css sets a minimum width of 13em to the content
   * container. If we don't set a min-width here, the content
   * gets clipped in iframes with small width. We don't accomodate
   * any padding to prioritize real estate in the small viewport. */
  min-width: 13em;
}

.button-container {
  display: flex;
  flex-flow: row wrap;
  justify-content: end;
}

.button-spacer {
  flex: 1;
}

@media only screen and (max-width: 959px) {
  body {
    padding: 75px 48px;
  }

  .title {
    background-image: none !important;
    padding-inline-start: 0;
    margin-inline-start: 0;
  }

  .title-text {
    padding-top: 0;
  }
}

@media only screen and (max-width: 640px) {
  body {
    justify-content: unset;
    /* Now that everything is top-aligned, we don't need the
     * bottom padding for centering - though it's added back
     * when the viewport height is < 480px (see below). */
    padding: 75px 20px 0;
  }

  .title-text {
    padding-bottom: 0;
    border-bottom: none;
  }
}

@media only screen and (max-width: 480px) {
  .button-container button {
    /* Force buttons to display: block here to try and enforce collapsing margins */
    display: block;
    width: 100%;
    margin: 0.66em 0 0;
  }
}

/* For small window height, shift the stripes up by 10px.
 * We could just change the background size, but that changes
 * the angle of the stripes so just shifting up is easier. */
@media only screen and (max-height: 480px) {
  body {
    background-position: 10px -10px;
    padding-top: 38px;
    /* We get rid of bottom padding for width < 640px, but
     * for height < 480px a bit of space between the content
     * and the viewport edge is nice. */
    padding-bottom: 38px;
  }
}
PK
!<U���))9chrome/browser/skin/classic/browser/favicon-search-16.svg<?xml version="1.0" encoding="utf-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 xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
  <circle cx="8" cy="8" r="8" fill="#58bf43"/>
  <circle cx="8" cy="8" r="7.5" stroke="#41a833" stroke-width="1" fill="none"/>
  <path d="M12.879,12L12,12.879,9.015,9.9A4.276,4.276,0,1,1,9.9,9.015ZM6.5,3.536A2.964,2.964,0,1,0,9.464,6.5,2.964,2.964,0,0,0,6.5,3.536Z" stroke="#41a833" stroke-width="2" fill="none"/>
  <path d="M12.879,12L12,12.879,9.015,9.9A4.276,4.276,0,1,1,9.9,9.015ZM6.5,3.536A2.964,2.964,0,1,0,9.464,6.5,2.964,2.964,0,0,0,6.5,3.536Z" fill="#fff"/>
</svg>
PK
!<��r�DD,chrome/browser/skin/classic/browser/feed.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M3.5 10A2.5 2.5 0 1 0 6 12.5 2.5 2.5 0 0 0 3.5 10zM2 1a1 1 0 0 0 0 2 10.883 10.883 0 0 1 11 11 1 1 0 0 0 2 0A12.862 12.862 0 0 0 2 1zm0 4a1 1 0 0 0 0 2 6.926 6.926 0 0 1 7 7 1 1 0 0 0 2 0 8.9 8.9 0 0 0-9-9z"/>
</svg>
PK
!<���KK6chrome/browser/skin/classic/browser/feeds/feedIcon.png�PNG


IHDR  szz�IDATxŗ�,Kօ���1��g۶m����m۶�g׾c_��OWV�Ȫ��Ĭ���]{�UR"�!Vܡ.	��
�I�4�	�8M�'�����HX$�8�w���/i�y J�4��?v?��P
Z�hmъd�Q�W��y���+���|7�v��ig]���#�&[J��H%�  �������z��|^)��̴�n[��o��<�)`�|�G��{$���S�T���y_D��+��ߥ����Vf��P*�
JE����؝ߙB9yU�)�ſ4yr���"IQ* ��Q� #a�1B�-�%?#��g��Bpڇ	�|	j�Q�:��k��pm��~SuW���\�t�:��/����t��N 8�ك�8����
8H�I���-�
XA�4�ɘ�nZE2�������B?@�j��k�a�|#�ߨ�4Ofr�c|���t0����x#?���k�6*������=�L�x[��{�8��O��1	9j����Iʐ�a7�#��C�x��3	�}��g�w��qX�E�_oXĺ��lZ_����PQ��.?
;4�lY��7�!8�J�K?��?��{�s�ٷ��� ��;U��)ق�<5�G��Dz��<��?!8�b�Aנ��!]��4���z��
$eo"��q�k�&<��n��� �q+z� a�{�;P <�5����.H��k�p��Kt�����1�ÁWv�2�So@��E!��	����wf��"���u󐡝�W�5 �(�v�wo�vc��)S=�8�[A~b��V�u��OnFv���.�D��'��4IƲ�jm'��	���K髧S~�՘�?��mD缟貯�1����)P~�P 8�.gJ��Ѽ�I2G�|�=��֕�K�J�g�S����m��	��%���Π�� ]�\� &<��^�� �0�)( (|�t���y�/G|�W	�� S�=�z�Wq��3��#�����^Z\~��)���"� I(�b�������ʯP��ٕK5]�c�Ή��	��'a��A,�Ԅވ�G�F���8'��
@��(��Ί'��S3s�﮸٤����U�4��?z8��k�A���M�,�6v�2̯~Jx�m��'b�/�1�_��&F�&#�^�_����`ӎ�d�z
����Ѩ�
8
8Ox�l�tOm����:q�n��9yᗨE���~]k+v���;�3���ڑ����Z:+1���H�
��(��N_�0+P%�Y�m�_��D`�6w�N{u�:>��أo$:�ձ�VX�G����og�᷿V?^&�ص�}�nt{g��2��%��]=nMUl���oO뭊�i'����s��
��w��4lʨBiԤc���c�=�35о}�U���P=��F7y�4�]�L��*J!0��*~6mޜ7�:��u�(4�4���s����Gٱ��K����s70�<L��\}�,n���!�b��{����/>����z�@;�	��( @
��`�o���.�*n�IEND�B`�PK
!<6X��8chrome/browser/skin/classic/browser/feeds/feedIcon16.png�PNG


IHDR�auIDATx����J��޵m�fx�k۶m۶m۶m�l�Q��t����$+{��76�75��l������F3C�l��6�k�lkǻ�Q�8=����:7��(�3#!O
�<1���=�vR[Cvכ���q�;*þ���;�o���!�C�+���ʒ�ˊ�/)�cI��q����-��1jw%ȋ�a"�8��Cl��AV�� �B�M��W&Cn��oBJ�Cb�7Sϲ�9ԓ#�u�o�Y�-�q�G@��K�*���ܜ`S�|gG��!�<�l^n�99 �u��`��@^���u�{ t։9a�M��|q�~�y�ᛚbg� @�S��ӓ�����1=caX�z+��o�5&	��A��֨D���A��9�no�O����&��VUljB� lm-j�@�f`++lc#/a_o8��Q���B�M�C�s��VTli
sT8��S���6Gĥ3�__�����y�5UCqo;����Hm6--đ�GGz�iݺ����%w_Y��a�)�I֘�*�c��[I��)\�q�����Ҏ<��h�#�!w�V�C��Bl��@+2�Z�q��ueۍ�F�����z\�?lX�@��oIEND�B`�PK
!<:���TT7chrome/browser/skin/classic/browser/feeds/subscribe.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

html {
  background: -moz-Dialog;
  font: 3mm tahoma,arial,helvetica,sans-serif;
}

#subscribeUsingDescription,
#subscribeButton {
  display: block;
}

#subscribeUsingDescription {
  margin-bottom: 0.5em;
}

#subscribeButton {
  margin-top: 0.5em;
  margin-inline-start: auto;
}

#feedBody {
  border: 1px solid THreeDShadow;
  padding: 3em;
  padding-inline-start: 30px;
  margin: 2em auto;
  background: -moz-Field;
}

#feedHeaderContainer {
  border: 1px solid ThreeDShadow;
  border-radius: 10px;
  margin: -4em auto 0 auto;
  background-color: InfoBackground;
  -moz-appearance: -moz-gtk-info-bar;
  display: flex;
}

#feedHeaderContainerSpacer {
  flex-grow: 1;
}

#feedHeader {
  margin-top: 4.9em;
  margin-bottom: 1em;
  margin-inline-start: 1.4em;
  margin-inline-end: 1em;
  padding-inline-start: 2.9em;
  font-size: 110%;
  color: -moz-gtk-info-bar-text;
}

.feedBackground {
  background: url("chrome://browser/skin/feeds/feedIcon.png") 0% 10% no-repeat;
}

.videoPodcastBackground {
  background: url("chrome://browser/skin/feeds/videoFeedIcon.png") 0% 10% no-repeat;
}

.audioPodcastBackground {
  background: url("chrome://browser/skin/feeds/audioFeedIcon.png") 0% 10% no-repeat;
}

#feedHeader[dir="rtl"] {
  background-position: 100% 10%;
}

#feedIntroText {
  display: none;
}

#feedHeader[firstrun="true"] #feedIntroText {
  padding-top: 0.1em;
  padding-inline-start: 0.6em;
  display: block;
}

#feedHeader[firstrun="true"] > #feedSubscribeLine {
  padding-inline-start: 1.8em;
}

#feedSubscribeLine {
  padding-top: 0.2em;
  padding-inline-start: 0.5em;
}

/* Don't print subscription UI */
@media print {
  #feedHeaderContainer {
    display: none;
  }
}

body {
  margin: 0;
  padding: 0 3em;
  color: -moz-fieldText;
  font: message-box;
}

h1 {
  font-size: 160%;
  border-bottom: 2px solid ThreeDLightShadow;
  margin: 0 0 .2em 0;
}

h2 {
  color: GrayText;
  font-size: 110%;
  font-weight: normal;
  margin: 0 0 .6em 0;
}

#feedTitleLink {
  float: right;
  margin-inline-start: .6em;
  margin-inline-end: 0;
  margin-top: 0;
  margin-bottom: 0;
}

a[href] img {
  border: none;
}

#feedTitleContainer {
  margin-inline-start: 0;
  margin-inline-end: .6em;
  margin-top: 0;
  margin-bottom: 0;
}

#feedTitleImage {
  margin-inline-start: .6em;
  margin-inline-end: 0;
  margin-top: 0;
  margin-bottom: 0;
  max-width: 300px;
  max-height: 150px;
}

.feedEntryContent {
  font-size: 110%;
}
 
.link {
  color: #0000FF;
  text-decoration: underline;
  cursor: pointer;
}

.link:hover:active {
  color: #FF0000;
}

.lastUpdated {
  font-size: 85%;
  font-weight: normal;
}

.type-icon {
  vertical-align: bottom;
  height: 16px;
  width: 16px;
}

.enclosures {
  border: 1px solid THreeDShadow;
  padding: 1em;
  margin: 1em auto;
  background: -moz-Dialog;
}

.enclosure {
  vertical-align: middle;
  margin-left: 2px;
}

.handlersMenuList > .menulist-label-box > .menulist-icon {
  max-width: 16px;
  max-height: 16px;
}
PK
!<q���"".chrome/browser/skin/classic/browser/folder.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M14 3H8.151L6.584 1.538A2 2 0 0 0 5.219 1H2a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2zM5.219 3l1.072 1H2V3zM14 13H2V5h6v-.014c.05 0 .1.014.151.014H14z"/>
</svg>
PK
!<(��k��.chrome/browser/skin/classic/browser/forget.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M6.854 10.854l2-2A.5.5 0 0 0 9 8.5v-4a.5.5 0 0 0-1 0v3.793l-1.854 1.853a.5.5 0 1 0 .707.707zM8 0a8.011 8.011 0 0 0-7 4.184V1.5a.5.5 0 0 0-1 0v5a.5.5 0 0 0 .5.5h5a.5.5 0 0 0 0-1H2.344a.938.938 0 0 0 .056-.085 6 6 0 1 1 0 4.184 1 1 0 0 0-1.873.7A7.991 7.991 0 1 0 8 0z"/>
</svg>
PK
!<V%�%��;chrome/browser/skin/classic/browser/fullscreen/insecure.svg<?xml version="1.0" encoding="utf-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 xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink"
     width="24" height="24" viewBox="0 0 24 24">
  <style>
    .icon-default {
      fill: #fafafa;
    }
  </style>

  <defs>
    <rect id="shape-lock-clasp-outer" x="5" y="1" width="14" height="20" rx="7" ry="7" />
    <rect id="shape-lock-clasp-inner" x="8" y="4" width="8" height="14" rx="4" ry="4" />
    <rect id="shape-lock-base" x="3" y="10" width="18" height="13" rx="1.5" ry="1.5" />

    <mask id="mask-clasp-cutout">
      <rect width="24" height="24" fill="#000" />
      <use xlink:href="#shape-lock-clasp-outer" fill="#fff" />
      <use xlink:href="#shape-lock-clasp-inner" fill="#000" />
      <line x1="3" y1="21" x2="21.5" y2="0.5" stroke="#000" stroke-width="3" />
      <line x1="3" y1="25" x2="21.5" y2="4.5" stroke="#000" stroke-width="3" />
      <rect x="3" y="10" width="18" height="13" rx="1.5" ry="1.5" />
    </mask>

    <mask id="mask-base-cutout">
      <rect width="24" height="24" fill="#000" />
      <use xlink:href="#shape-lock-base" fill="#fff" />
      <line x1="2.25" y1="24.75" x2="21.5" y2="4.5" stroke="#000" stroke-width="3" />
    </mask>
  </defs>

  <use xlink:href="#shape-lock-clasp-outer" mask="url(#mask-clasp-cutout)" class="icon-default" />
  <use xlink:href="#shape-lock-base" mask="url(#mask-base-cutout)" class="icon-default" />

  <line x1="2.25" y1="22.75" x2="21.5" y2="2.5" stroke="#d92d21" stroke-width="3" />
</svg>
PK
!<F9��dd9chrome/browser/skin/classic/browser/fullscreen/secure.svg<?xml version="1.0" encoding="utf-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 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24">
  <style>
    .icon-default {
      fill: #fafafa;
    }
  </style>

  <defs>
    <rect id="shape-lock-clasp-outer" x="5" y="1" width="14" height="20" rx="7" ry="7" />
    <rect id="shape-lock-clasp-inner" x="8" y="4" width="8" height="14" rx="4" ry="4" />
    <rect id="shape-lock-base" x="3" y="10" width="18" height="13" rx="1.5" ry="1.5" />

    <mask id="mask-clasp-cutout">
      <rect width="24" height="24" fill="#000" />
      <use xlink:href="#shape-lock-clasp-outer" fill="#fff" />
      <use xlink:href="#shape-lock-clasp-inner" fill="#000" />
    </mask>
  </defs>

  <use xlink:href="#shape-lock-clasp-outer" mask="url(#mask-clasp-cutout)" class="icon-default" />
  <use xlink:href="#shape-lock-base" class="icon-default" />
</svg>
PK
!<,�0�UU7chrome/browser/skin/classic/browser/fullscreen-exit.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16" fill="context-fill" fill-opacity="context-fill-opacity">
  <path d="M7,8H2a1,1,0,0,0,0,2H4.586L1.293,13.293a1,1,0,1,0,1.414,1.414L6,11.414V14a1,1,0,0,0,2,0V9A1,1,0,0,0,7,8Z"/>
  <path d="M14,6H11.414l3.293-3.293a1,1,0,0,0-1.414-1.414L10,4.586V2A1,1,0,0,0,8,2V7A1,1,0,0,0,9,8h5a1,1,0,0,0,0-2Z"/>
</svg>
PK
!<ô"�YY2chrome/browser/skin/classic/browser/fullscreen.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M7.707 8.293a1 1 0 0 0-1.414 0L3 11.586V9a1 1 0 0 0-2 0v5a1 1 0 0 0 1 1h5a1 1 0 1 0 0-2H4.414l3.293-3.293a1 1 0 0 0 0-1.414zM14 1H9a1 1 0 0 0 0 2h2.586L8.293 6.293a1 1 0 1 0 1.414 1.414L13 4.414V7a1 1 0 0 0 2 0V2a1 1 0 0 0-1-1z"/>
</svg>
PK
!<�W

:chrome/browser/skin/classic/browser/fxa/default-avatar.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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="194 -104 1000 1000" width="80" height="80">
  <path fill="#c3cfd8" d="M694-104.3c276.1 0 500 223.9 500 500s-223.9 500-500 500-500-223.9-500-500c0-276.2 223.9-500 500-500z"/>
  <path fill="#fff" fill-rule="evenodd" clip-rule="evenodd" d="M892.4 585.9c10 3.1 19.1 5.7 27.5 8.2 34.5 10 44.8 54.6 17.5 78.1-65.4 56.5-150.7 90.8-244 90.8-92.8 0-177.6-33.8-242.9-89.8-27.4-23.5-17.3-68.2 17.4-78.3 9.2-2.7 19.2-5.5 30.2-9 62.6-19.5 92.6-43.7 98.2-68.7 0-.1 0-.2.1-.2 3.6-16.1-2.8-32.9-15.5-43.5-26.4-22.1-37.1-59.8-44.1-87.5-.8-3.2-1.7-6.5-2.5-9.8-12.1-2.1-25.4-17.3-32.2-38.5-8.2-25.5-3.9-49.8 9.6-54.1 1.3-.4 2.6-.4 3.9-.5-3.1-18.2-6.9-45.4-7.3-69.3-.1-5.2-.2-10.9-.2-16.9 0-3 .1-6.1.1-9.3 0-1.6.1-3.2.2-4.8.1-1.6.2-3.2.3-4.9.9-13.1 2.9-26.8 7-40 7.4-23.7 21.6-45.4 47.4-57.3 5.8-2.7 11-6.4 15.1-11.3 22.4-26.4 49.1-39.6 74.2-45.4 6.9-1.6 13.6-2.6 20.1-3.2 3.2-.3 6.4-.5 9.5-.6 1.6-.1 3.1-.1 4.6-.1h4.5c11.7.3 22 1.8 29.6 3.7 50 12.3 89.2 38 116.4 69.5 13.5 15.8 23.9 33 30.7 50.7 3.4 8.9 5.9 17.9 7.4 26.9.8 4.5 1.3 9 1.6 13.5.3 4.5.3 8.9.1 13.4-1.5 27.1-4.4 45.9-7.3 60.1-2.3 11.1.1 22.2 5 32.4 4.9 10.3 5.3 26.7.2 43.9-6.1 20.3-18.3 35.3-29.8 38.7-2.2 8.1-3.8 13.5-3.9 13.5-3.8 29-10.7 59.8-35.3 82.9-10.5 9.8-15 24.5-13.1 38.7.5 3.5 1 6.6 1.6 9.2 5.6 25.1 35.5 49.3 98.1 68.8z"/>
</svg>
PK
!<���v v Cchrome/browser/skin/classic/browser/fxa/sync-illustration-issue.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 106.6 89.6">
  <style>
    .st0{fill:#fff}
  </style>
  <path class="st0" d="M99.2 24.4H83.9V7.5c0-4.2-3.4-7.5-7.6-7.5H14.9c-4.2 0-7.5 3.4-7.5 7.5v40.7c0 1.3.4 2.6 1 3.7-.4.5-.8 1-1 1.6L.5 69.6c-.3.7-.5 1.5-.5 2.3V73c.1 3.4 2.8 6.1 6.2 6h60v3.2c0 4.1 3.3 7.4 7.4 7.4h25.6c4.1 0 7.4-3.3 7.4-7.4V31.9c.1-4.2-3.2-7.5-7.4-7.5z"/>
  <path class="st0" d="M14.6 3.7H76c2 0 3.6 1.6 3.6 3.5v40.7c0 2-1.6 3.6-3.6 3.6H14.6c-2 0-3.5-1.6-3.5-3.6V7.3c0-2 1.6-3.6 3.5-3.6z"/>
  <path class="st0" d="M16.5 9.7h57.7c1.2 0 2.1.9 2.1 2.1v34.4c0 1.2-.9 2.1-2.1 2.1H16.5c-1.2 0-2.1-.9-2.1-2.1V11.8c0-1.1 1-2.1 2.1-2.1z"/>
  <linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="19.2468" y1="-1740.7432" x2="92.0768" y2="-1813.5831" gradientTransform="matrix(1 0 0 -1 .02 -1737.83)">
    <stop offset="0" stop-color="#ccfbff"/>
    <stop offset="1" stop-color="#c9e4ff"/>
  </linearGradient>
  <path d="M74.2 10.7c.6 0 1.1.5 1.1 1.1v34.4c0 .6-.5 1.1-1.1 1.1H16.5c-.6 0-1.1-.5-1.1-1.1V11.8c0-.6.5-1.1 1.1-1.1h57.7" fill="url(#SVGID_1_)"/>
  <path class="st0" d="M79.5 54.8c-.4-.8-1.2-1.3-2.1-1.2H12.8c-.9 0-1.7.5-2.1 1.3L3.8 71c-.1.2-.1.5-.1.7v1.1c.1 1.2 1 2.1 2.2 2h78.9c1.2.1 2.2-.8 2.2-2v-1c0-.3-.1-.5-.2-.7l-7.3-16.3z"/>
  <linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="-40.7021" y1="-1702.8644" x2="146.2979" y2="-1872.3644" gradientTransform="matrix(1 0 0 -1 .02 -1737.83)">
    <stop offset="0" stop-color="#00c8d7"/>
    <stop offset="1" stop-color="#0a84ff"/>
  </linearGradient>
  <path d="M88.7 70.1L81.4 54c-.3-.8-.9-1.4-1.6-1.8 1.2-1.1 1.9-2.6 1.9-4.2V7.3c0-3.1-2.5-5.5-5.6-5.5H14.7c-3.1 0-5.5 2.5-5.5 5.5V48c0 1.5.6 3 1.7 4-.9.4-1.6 1.1-2 2L2 70.2c-.2.5-.3 1-.3 1.5v1.1c.1 2.3 1.9 4.1 4.2 4h78.9c2.3.1 4.2-1.7 4.2-4v-1c0-.6-.1-1.2-.3-1.7zM87 72.7c-.1 1.2-1 2.1-2.2 2H5.9c-1.2.1-2.2-.8-2.2-2v-1.1c0-.2 0-.5.1-.7l6.9-16.1c.4-.8 1.2-1.3 2.1-1.3h64.7c.9 0 1.7.5 2.1 1.2l7.3 16.1c.1.2.2.5.2.7l-.1 1.2zM11.1 48V7.3c0-2 1.6-3.5 3.5-3.5H76c2 0 3.6 1.6 3.6 3.5V48c0 2-1.6 3.6-3.5 3.6H14.7c-2 0-3.6-1.6-3.6-3.6z" fill="url(#SVGID_2_)"/>
  <linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="-87.5994" y1="-1666.14" x2="165.4006" y2="-1916.14" gradientTransform="matrix(1 0 0 -1 .02 -1737.83)">
    <stop offset="0" stop-color="#00c8d7"/>
    <stop offset="1" stop-color="#0a84ff"/>
  </linearGradient>
  <path d="M58.5 69H32.2c-.4 0-.8-.2-.8-.5.3-1.7.5-2.6.8-4.4 0-.2.3-.4.7-.4l25.3-.2c.4 0 .7.2.7.4.2 1.5.4 3 .4 4.5.1.4-.3.6-.8.6zm-30.7-9.4h-3.9c-.3 0-.6.2-.7.4-.1.5-.2.7-.3 1.2s.4.7.9.7h3.8c.3 0 .6-.1.7-.3.1-.5.2-.7.3-1.2s-.3-.8-.8-.8zm9.9 0h-4.1c-.4 0-.7.2-.7.4-.1.5-.1.7-.2 1.2-.1.3.4.6.9.6h3.9c.3 0 .6-.1.7-.4.1-.5.2-.7.3-1.2.1-.4-.3-.7-.8-.6zM58.2 61c-.1-.5-.1-.7-.2-1.2 0-.2-.3-.4-.7-.4h-4.1c-.5 0-.9.3-.9.6l.3 1.2c0 .2.3.4.7.4h3.9c.6 0 1.1-.3 1-.6zm-9.9.1l-.2-1.2c0-.2-.4-.4-.8-.4h-3.7c-.4 0-.8.2-.8.4-.1.5-.2.7-.2 1.2-.1.3.3.6.8.6h4.1c.4-.1.8-.3.8-.6zm19.6-.2l-.2-1.2c0-.2-.3-.3-.6-.3h-3.9c-.5 0-1 .3-.9.7l.3 1.2c.1.2.3.3.7.3h3.8c.4-.1.9-.4.8-.7zm-39.1-5h-3.7c-.3 0-.5.1-.6.3-.1.4-.2.7-.3 1.1s.3.6.8.6h3.6c.3 0 .5-.1.7-.3.2-.4.2-.7.4-1.1s-.4-.6-.9-.6zm9.4 0h-3.9c-.4 0-.7.2-.7.4-.1.4-.2.7-.3 1.1s.3.6.8.6h3.8c.3 0 .6-.1.7-.4.1-.4.2-.7.3-1.1s-.3-.6-.7-.6zm19.6 1.4l-.2-1.1c0-.2-.3-.4-.7-.4H53c-.5 0-.9.3-.8.6l.2 1.1c0 .2.3.4.7.4h3.8c.5-.1.9-.3.9-.6zm-9.5.1l-.2-1.1c0-.2-.3-.4-.7-.4h-3.6c-.4 0-.7.2-.8.4-.1.4-.2.7-.3 1.1-.1.3.3.5.8.5h3.9c.5 0 .9-.3.9-.5zm18.7-.1l-.2-1.1c0-.2-.3-.3-.6-.3h-3.7c-.5 0-1 .3-.9.6l.3 1.1c.1.2.3.3.6.3h3.6c.5-.1.9-.4.9-.6zm-48.1 2.4h-3.8c-.3 0-.6.1-.7.4-.2.4-.3.8-.4 1.2-.1.3.4.7.9.7h3.7c.3 0 .6-.2.6-.4.1-.4.2-.8.4-1.2.2-.5-.2-.8-.7-.7zm1.3-3.7h-3.5c-.2 0-.5.1-.6.3-.1.4-.2.7-.4 1.1-.1.3.2.6.7.6h3.5c.3 0 .5-.1.7-.3.2-.4.3-.7.5-1.1s-.4-.7-.9-.6zm50.9 4.1c.1.4.3.8.3 1.1 0 .2.3.3.6.3h3.7c.5 0 1-.3.9-.6-.1-.4-.2-.8-.3-1.1-.1-.2-.4-.4-.7-.3h-3.7c-.5-.1-.9.2-.8.6zM70 56.4c.1.4.2.7.4 1.1.1.2.4.3.6.3h3.4c.5 0 .8-.3.8-.6-.1-.4-.2-.7-.3-1.1 0-.2-.2-.3-.6-.3h-3.5c-.4 0-.9.3-.8.6z" fill="url(#SVGID_3_)"/>
  <g>
    <linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="-58.8206" y1="-1638.5309" x2="189.1794" y2="-1888.5308" gradientTransform="matrix(1 0 0 -1 0 -1738)">
      <stop offset="0" stop-color="#00c8d7"/>
      <stop offset="1" stop-color="#0a84ff"/>
    </linearGradient>
    <circle cx="46.4" cy="6.6" r="1.2" fill="url(#SVGID_4_)"/>
  </g>
  <path class="st0" d="M73.4 27.2H99c2.5 0 4.4 2 4.4 4.4v50.3c0 2.5-2 4.4-4.4 4.4H73.4c-2.5 0-4.4-2-4.4-4.4V31.6c-.1-2.4 1.9-4.4 4.4-4.4z"/>
  <linearGradient id="SVGID_5_" gradientUnits="userSpaceOnUse" x1="-55.4553" y1="-1665.9757" x2="185.0447" y2="-1884.4757" gradientTransform="matrix(1 0 0 -1 .02 -1737.83)">
    <stop offset="0" stop-color="#00c8d7"/>
    <stop offset="1" stop-color="#0a84ff"/>
  </linearGradient>
  <path d="M98.9 28.2c1.9 0 3.4 1.5 3.4 3.4v50.3c0 1.9-1.5 3.4-3.4 3.4H73.3c-1.9 0-3.4-1.5-3.4-3.4V31.6c0-1.9 1.5-3.4 3.4-3.4h25.6m0-2H73.3c-3 0-5.4 2.4-5.4 5.4v50.3c0 3 2.4 5.4 5.4 5.4h25.6c3 0 5.4-2.4 5.4-5.4V31.6c.1-3-2.4-5.4-5.4-5.4z" fill="url(#SVGID_5_)"/>
  <g>
    <path class="st0" d="M74.9 32h22.4c.9 0 1.7.8 1.7 1.7v41.9c0 .9-.8 1.7-1.7 1.7H74.9c-.9 0-1.7-.8-1.7-1.7V33.7c0-.9.8-1.7 1.7-1.7z"/>
    <linearGradient id="SVGID_6_" gradientUnits="userSpaceOnUse" x1="26.795" y1="-1733.1949" x2="99.635" y2="-1806.0349" gradientTransform="matrix(1 0 0 -1 .02 -1737.83)">
      <stop offset="0" stop-color="#ccfbff"/>
      <stop offset="1" stop-color="#c9e4ff"/>
    </linearGradient>
    <path d="M97.3 33c.4 0 .7.3.7.7v41.9c0 .4-.3.7-.7.7H74.9c-.4 0-.7-.3-.7-.7V33.7c0-.4.3-.7.7-.7h22.4" fill="url(#SVGID_6_)"/>
  </g>
  <linearGradient id="SVGID_7_" gradientUnits="userSpaceOnUse" x1="-109.61" y1="-1776.874" x2="226.72" y2="-1776.874" gradientTransform="matrix(1 0 0 -1 .02 -1737.83)">
    <stop offset="0" stop-color="#00c8d7"/>
    <stop offset="1" stop-color="#0a84ff"/>
  </linearGradient>
  <path d="M46.7 45c-.6 0-1.1-.2-1.6-.6l-15-12.1c-1.5-1-2.7-2.2-3.7-3.7-3.3-5.1-2.6-11.7 1.7-16 5-5 13-5 17.9 0 .1.1.3.2.5.2s.4-.1.5-.2c5.1-4.8 13.1-4.6 18 .5s4.6 13.1-.5 18c-.5.4-1 .9-1.5 1.2L48.2 44.5c-.4.3-1 .5-1.5.5zm41 23.7l11-9c4.4-3 5.6-9 2.7-13.4-3-4.4-9-5.6-13.4-2.7-.5.3-1 .7-1.4 1.2 0 0-.1.1-.2.1s-.1 0-.2-.1c-3.8-3.8-9.9-3.8-13.7 0-3.2 3.2-3.8 8.3-1.3 12.2.7 1.1 1.7 2.1 2.8 2.8l11.1 9c.7.6 1.8.6 2.6-.1z" fill="url(#SVGID_7_)"/>
  <path class="st0" d="M37.1 10c3.1 0 6.1 1.2 8.3 3.4.3.3.7.5 1.2.5.4 0 .9-.2 1.2-.5 4.6-4.5 12-4.5 16.6.1 4.5 4.6 4.5 12-.1 16.6-.5.5-1.1 1-1.7 1.4l-15 12.2c-.3.2-.6.3-.9.3s-.7-.1-.9-.3l-15-12.2c-1.4-.9-2.5-2-3.4-3.4-3-4.6-2.4-10.7 1.5-14.7C31 11.3 34 10 37.1 10m0-2c-3.6 0-7.1 1.4-9.7 4-4.6 4.6-5.3 11.8-1.8 17.2 1 1.6 2.3 2.9 3.9 3.9l15 12.1c1.3 1 3.1 1 4.3 0l14.9-12.1c6.3-4.2 8-12.7 3.8-19s-12.7-8-19-3.8c-.7.5-1.3 1-1.9 1.5C44 9.4 40.6 8 37.1 8z"/>
  <path class="st0" d="M30.1 23.4h-.2c-1.1-.1-1.9-1-1.8-2.1.3-3.8 3.1-7 6.8-7.8 1-.4 2.2 0 2.6 1s0 2.2-1 2.6c-.2.1-.5.2-.8.2-2.1.5-3.6 2.2-3.8 4.3 0 1-.8 1.7-1.8 1.8zM79.2 43c2.3 0 4.5.9 6.1 2.6.2.2.5.4.9.4.3 0 .6-.1.9-.4 3.4-3.4 8.9-3.4 12.3 0s3.4 8.9 0 12.3c-.4.4-.8.8-1.3 1.1L87 68c-.2.2-.4.2-.7.2-.2 0-.5-.1-.7-.2l-11.1-9c-1-.7-1.9-1.5-2.5-2.5-2.2-3.4-1.7-8 1.1-10.9 1.6-1.7 3.8-2.6 6.1-2.6m0-2c-2.8 0-5.5 1.1-7.5 3.1-3.6 3.6-4.1 9.2-1.4 13.4.8 1.2 1.8 2.2 3 3l11 8.9c1.1.9 2.7.9 3.9 0l11-9c4.9-3.3 6.1-10 2.8-14.8-3.3-4.9-10-6.1-14.8-2.8-.3.2-.7.5-1 .7-2-1.6-4.5-2.6-7-2.5z"/>
  <path class="st0" d="M74.1 52.9c-.9-.1-1.5-.8-1.4-1.6.2-2.8 2.2-5.2 5-5.8.8-.2 1.6.3 1.8 1.1.2.8-.3 1.6-1.1 1.8h-.1c-1.5.3-2.7 1.6-2.8 3.2-.1.7-.7 1.3-1.4 1.3z"/>
  <path class="st0" d="M46.4 45.3c-.7 0-1.2-.6-1.2-1.2v-6.2c0-.3.1-.6.4-.9l1.7-1.7-4-4c-.5-.5-.5-1.3 0-1.8l6.3-6.3-3.9-3.9c-.2-.2-.4-.6-.4-.9v-4.6c0-.7.6-1.2 1.2-1.2s1.2.6 1.2 1.2v4.1l4.4 4.4c.5.5.5 1.3 0 1.8l-6.3 6.3 4 4c.5.5.5 1.3 0 1.8l-2.1 2.1V44c0 .7-.6 1.3-1.3 1.3zm39.7 23.8c-.7 0-1.2-.6-1.2-1.2v-4.8c0-.3.1-.6.4-.9l1.1-1.1-2.9-2.9c-.2-.2-.4-.6-.4-.9s.1-.6.4-.9l4.6-4.6-2.7-2.7c-.2-.2-.4-.6-.4-.9v-3.5c0-.7.6-1.2 1.2-1.2s1.2.6 1.2 1.2v3l3.2 3.2c.2.2.4.6.4.9s-.1.6-.4.9L86 57.3l2.9 2.9c.5.5.5 1.3 0 1.8l-1.6 1.6v4.2c.1.8-.5 1.3-1.2 1.3z"/>
</svg>
PK
!<Y�|##=chrome/browser/skin/classic/browser/fxa/sync-illustration.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 106.6 89.6">
  <style>
    .st0{fill:#fff}
  </style>
  <path class="st0" d="M99.2 24.4H83.9V7.5c0-4.2-3.4-7.5-7.6-7.5H14.9c-4.2 0-7.5 3.4-7.5 7.5v40.7c0 1.3.4 2.6 1 3.7-.4.5-.8 1-1 1.6L.5 69.6c-.3.7-.5 1.5-.5 2.3V73c.1 3.4 2.8 6.1 6.2 6h60v3.2c0 4.1 3.3 7.4 7.4 7.4h25.6c4.1 0 7.4-3.3 7.4-7.4V31.9c.1-4.2-3.2-7.5-7.4-7.5z"/>
  <path class="st0" d="M14.6 3.7H76c2 0 3.6 1.6 3.6 3.5v40.7c0 2-1.6 3.6-3.6 3.6H14.6c-2 0-3.5-1.6-3.5-3.6V7.3c0-2 1.6-3.6 3.5-3.6z"/>
  <path class="st0" d="M16.5 9.7h57.7c1.2 0 2.1.9 2.1 2.1v34.4c0 1.2-.9 2.1-2.1 2.1H16.5c-1.2 0-2.1-.9-2.1-2.1V11.8c0-1.1 1-2.1 2.1-2.1z"/>
  <linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="19.2468" y1="1832.7432" x2="92.0768" y2="1905.5831" gradientTransform="translate(.02 -1829.83)">
    <stop offset="0" stop-color="#ccfbff"/>
    <stop offset="1" stop-color="#c9e4ff"/>
  </linearGradient>
  <path d="M74.2 10.7c.6 0 1.1.5 1.1 1.1v34.4c0 .6-.5 1.1-1.1 1.1H16.5c-.6 0-1.1-.5-1.1-1.1V11.8c0-.6.5-1.1 1.1-1.1h57.7" fill="url(#SVGID_1_)"/>
  <path class="st0" d="M79.5 54.8c-.4-.8-1.2-1.3-2.1-1.2H12.8c-.9 0-1.7.5-2.1 1.3L3.8 71c-.1.2-.1.5-.1.7v1.1c.1 1.2 1 2.1 2.2 2h78.9c1.2.1 2.2-.8 2.2-2v-1c0-.3-.1-.5-.2-.7l-7.3-16.3z"/>
  <linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="-40.7021" y1="1794.8644" x2="146.2979" y2="1964.3644" gradientTransform="translate(.02 -1829.83)">
    <stop offset="0" stop-color="#00c8d7"/>
    <stop offset="1" stop-color="#0a84ff"/>
  </linearGradient>
  <path d="M88.7 70.1L81.4 54c-.3-.8-.9-1.4-1.6-1.8 1.2-1.1 1.9-2.6 1.9-4.2V7.3c0-3.1-2.5-5.5-5.6-5.5H14.7c-3.1 0-5.5 2.5-5.5 5.5V48c0 1.5.6 3 1.7 4-.9.4-1.6 1.1-2 2L2 70.2c-.2.5-.3 1-.3 1.5v1.1c.1 2.3 1.9 4.1 4.2 4h78.9c2.3.1 4.2-1.7 4.2-4v-1c0-.6-.1-1.2-.3-1.7zM87 72.7c-.1 1.2-1 2.1-2.2 2H5.9c-1.2.1-2.2-.8-2.2-2v-1.1c0-.2 0-.5.1-.7l6.9-16.1c.4-.8 1.2-1.3 2.1-1.3h64.7c.9 0 1.7.5 2.1 1.2l7.3 16.1c.1.2.2.5.2.7l-.1 1.2zM11.1 48V7.3c0-2 1.6-3.5 3.5-3.5H76c2 0 3.6 1.6 3.6 3.5V48c0 2-1.6 3.6-3.5 3.6H14.7c-2 0-3.6-1.6-3.6-3.6z" fill="url(#SVGID_2_)"/>
  <linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="-87.5994" y1="1758.14" x2="165.4006" y2="2008.14" gradientTransform="translate(.02 -1829.83)">
    <stop offset="0" stop-color="#00c8d7"/>
    <stop offset="1" stop-color="#0a84ff"/>
  </linearGradient>
  <path d="M58.5 69H32.2c-.4 0-.8-.2-.8-.5.3-1.7.5-2.6.8-4.4 0-.2.3-.4.7-.4l25.3-.2c.4 0 .7.2.7.4.2 1.5.4 3 .4 4.5.1.4-.3.6-.8.6zm-30.7-9.4h-3.9c-.3 0-.6.2-.7.4-.1.5-.2.7-.3 1.2s.4.7.9.7h3.8c.3 0 .6-.1.7-.3.1-.5.2-.7.3-1.2s-.3-.8-.8-.8zm9.9 0h-4.1c-.4 0-.7.2-.7.4-.1.5-.1.7-.2 1.2-.1.3.4.6.9.6h3.9c.3 0 .6-.1.7-.4.1-.5.2-.7.3-1.2.1-.4-.3-.7-.8-.6zM58.2 61c-.1-.5-.1-.7-.2-1.2 0-.2-.3-.4-.7-.4h-4.1c-.5 0-.9.3-.9.6l.3 1.2c0 .2.3.4.7.4h3.9c.6 0 1.1-.3 1-.6zm-9.9.1l-.2-1.2c0-.2-.4-.4-.8-.4h-3.7c-.4 0-.8.2-.8.4-.1.5-.2.7-.2 1.2-.1.3.3.6.8.6h4.1c.4-.1.8-.3.8-.6zm19.6-.2l-.2-1.2c0-.2-.3-.3-.6-.3h-3.9c-.5 0-1 .3-.9.7l.3 1.2c.1.2.3.3.7.3h3.8c.4-.1.9-.4.8-.7zm-39.1-5h-3.7c-.3 0-.5.1-.6.3-.1.4-.2.7-.3 1.1s.3.6.8.6h3.6c.3 0 .5-.1.7-.3.2-.4.2-.7.4-1.1s-.4-.6-.9-.6zm9.4 0h-3.9c-.4 0-.7.2-.7.4-.1.4-.2.7-.3 1.1s.3.6.8.6h3.8c.3 0 .6-.1.7-.4.1-.4.2-.7.3-1.1s-.3-.6-.7-.6zm19.6 1.4l-.2-1.1c0-.2-.3-.4-.7-.4H53c-.5 0-.9.3-.8.6l.2 1.1c0 .2.3.4.7.4h3.8c.5-.1.9-.3.9-.6zm-9.5.1l-.2-1.1c0-.2-.3-.4-.7-.4h-3.6c-.4 0-.7.2-.8.4-.1.4-.2.7-.3 1.1-.1.3.3.5.8.5h3.9c.5 0 .9-.3.9-.5zm18.7-.1l-.2-1.1c0-.2-.3-.3-.6-.3h-3.7c-.5 0-1 .3-.9.6l.3 1.1c.1.2.3.3.6.3h3.6c.5-.1.9-.4.9-.6zm-48.1 2.4h-3.8c-.3 0-.6.1-.7.4-.2.4-.3.8-.4 1.2-.1.3.4.7.9.7h3.7c.3 0 .6-.2.6-.4.1-.4.2-.8.4-1.2.2-.5-.2-.8-.7-.7zm1.3-3.7h-3.5c-.2 0-.5.1-.6.3-.1.4-.2.7-.4 1.1-.1.3.2.6.7.6h3.5c.3 0 .5-.1.7-.3.2-.4.3-.7.5-1.1s-.4-.7-.9-.6zm50.9 4.1c.1.4.3.8.3 1.1 0 .2.3.3.6.3h3.7c.5 0 1-.3.9-.6-.1-.4-.2-.8-.3-1.1-.1-.2-.4-.4-.7-.3h-3.7c-.5-.1-.9.2-.8.6zM70 56.4c.1.4.2.7.4 1.1.1.2.4.3.6.3h3.4c.5 0 .8-.3.8-.6-.1-.4-.2-.7-.3-1.1 0-.2-.2-.3-.6-.3h-3.5c-.4 0-.9.3-.8.6z" fill="url(#SVGID_3_)"/>
  <g>
    <linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="-58.8206" y1="1730.5309" x2="189.1794" y2="1980.5308" gradientTransform="translate(0 -1830)">
      <stop offset="0" stop-color="#00c8d7"/>
      <stop offset="1" stop-color="#0a84ff"/>
    </linearGradient>
    <circle cx="46.4" cy="6.6" r="1.2" fill="url(#SVGID_4_)"/>
  </g>
  <path class="st0" d="M73.4 27.2H99c2.5 0 4.4 2 4.4 4.4v50.3c0 2.5-2 4.4-4.4 4.4H73.4c-2.5 0-4.4-2-4.4-4.4V31.6c-.1-2.4 1.9-4.4 4.4-4.4z"/>
  <linearGradient id="SVGID_5_" gradientUnits="userSpaceOnUse" x1="-55.4553" y1="1757.9757" x2="185.0447" y2="1976.4757" gradientTransform="translate(.02 -1829.83)">
    <stop offset="0" stop-color="#00c8d7"/>
    <stop offset="1" stop-color="#0a84ff"/>
  </linearGradient>
  <path d="M98.9 28.2c1.9 0 3.4 1.5 3.4 3.4v50.3c0 1.9-1.5 3.4-3.4 3.4H73.3c-1.9 0-3.4-1.5-3.4-3.4V31.6c0-1.9 1.5-3.4 3.4-3.4h25.6m0-2H73.3c-3 0-5.4 2.4-5.4 5.4v50.3c0 3 2.4 5.4 5.4 5.4h25.6c3 0 5.4-2.4 5.4-5.4V31.6c.1-3-2.4-5.4-5.4-5.4z" fill="url(#SVGID_5_)"/>
  <g>
    <path class="st0" d="M74.9 32h22.4c.9 0 1.7.8 1.7 1.7v41.9c0 .9-.8 1.7-1.7 1.7H74.9c-.9 0-1.7-.8-1.7-1.7V33.7c0-.9.8-1.7 1.7-1.7z"/>
    <linearGradient id="SVGID_6_" gradientUnits="userSpaceOnUse" x1="26.795" y1="1825.1949" x2="99.635" y2="1898.0349" gradientTransform="translate(.02 -1829.83)">
      <stop offset="0" stop-color="#ccfbff"/>
      <stop offset="1" stop-color="#c9e4ff"/>
    </linearGradient>
    <path d="M97.3 33c.4 0 .7.3.7.7v41.9c0 .4-.3.7-.7.7H74.9c-.4 0-.7-.3-.7-.7V33.7c0-.4.3-.7.7-.7h22.4" fill="url(#SVGID_6_)"/>
  </g>
  <linearGradient id="SVGID_7_" gradientUnits="userSpaceOnUse" x1="-109.61" y1="1868.874" x2="226.72" y2="1868.874" gradientTransform="translate(.02 -1829.83)">
    <stop offset="0" stop-color="#00c8d7"/>
    <stop offset="1" stop-color="#0a84ff"/>
  </linearGradient>
  <path d="M46.7 45c-.6 0-1.1-.2-1.6-.6l-15-12.1c-1.5-1-2.7-2.2-3.7-3.7-3.3-5.1-2.6-11.7 1.7-16 5-5 13-5 17.9 0 .1.1.3.2.5.2s.4-.1.5-.2c5.1-4.8 13.1-4.6 18 .5s4.6 13.1-.5 18c-.5.4-1 .9-1.5 1.2L48.2 44.5c-.4.3-1 .5-1.5.5zm41 23.7l11-9c4.4-3 5.6-9 2.7-13.4-3-4.4-9-5.6-13.4-2.7-.5.3-1 .7-1.4 1.2 0 0-.1.1-.2.1s-.1 0-.2-.1c-3.8-3.8-9.9-3.8-13.7 0-3.2 3.2-3.8 8.3-1.3 12.2.7 1.1 1.7 2.1 2.8 2.8l11.1 9c.7.6 1.8.6 2.6-.1z" fill="url(#SVGID_7_)"/>
  <path class="st0" d="M37.1 10c3.1 0 6.1 1.2 8.3 3.4.3.3.7.5 1.2.5.4 0 .9-.2 1.2-.5 4.6-4.5 12-4.5 16.6.1 4.5 4.6 4.5 12-.1 16.6-.5.5-1.1 1-1.7 1.4l-15 12.2c-.3.2-.6.3-.9.3s-.7-.1-.9-.3l-15-12.2c-1.4-.9-2.5-2-3.4-3.4-3-4.6-2.4-10.7 1.5-14.7C31 11.3 34 10 37.1 10m0-2c-3.6 0-7.1 1.4-9.7 4-4.6 4.6-5.3 11.8-1.8 17.2 1 1.6 2.3 2.9 3.9 3.9l15 12.1c1.3 1 3.1 1 4.3 0l14.9-12.1c6.3-4.2 8-12.7 3.8-19s-12.7-8-19-3.8c-.7.5-1.3 1-1.9 1.5C44 9.4 40.6 8 37.1 8z"/>
  <path class="st0" d="M30.1 23.4h-.2c-1.1-.1-1.9-1-1.8-2.1.3-3.8 3.1-7 6.8-7.8 1-.4 2.2 0 2.6 1s0 2.2-1 2.6c-.2.1-.5.2-.8.2-2.1.5-3.6 2.2-3.8 4.3 0 1-.8 1.7-1.8 1.8zM79.2 43c2.3 0 4.5.9 6.1 2.6.2.2.5.4.9.4.3 0 .6-.1.9-.4 3.4-3.4 8.9-3.4 12.3 0 3.4 3.4 3.4 8.9 0 12.3-.4.4-.8.8-1.3 1.1L87 68c-.2.2-.4.2-.7.2-.2 0-.5-.1-.7-.2l-11.1-9c-1-.7-1.9-1.5-2.5-2.5-2.2-3.4-1.7-8 1.1-10.9 1.6-1.7 3.8-2.6 6.1-2.6m0-2c-2.8 0-5.5 1.1-7.5 3.1-3.6 3.6-4.1 9.2-1.4 13.4.8 1.2 1.8 2.2 3 3l11 8.9c1.1.9 2.7.9 3.9 0l11-9c4.9-3.3 6.1-10 2.8-14.8-3.3-4.9-10-6.1-14.8-2.8-.3.2-.7.5-1 .7-2-1.6-4.5-2.6-7-2.5z"/>
  <path class="st0" d="M74.1 52.9c-.9-.1-1.5-.8-1.4-1.6.2-2.8 2.2-5.2 5-5.8.8-.2 1.6.3 1.8 1.1.2.8-.3 1.6-1.1 1.8h-.1c-1.5.3-2.7 1.6-2.8 3.2-.1.7-.7 1.3-1.4 1.3z"/>
</svg>
PK
!<LӍ�/chrome/browser/skin/classic/browser/history.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M8 0a8 8 0 1 0 8 8 8.009 8.009 0 0 0-8-8zm0 14a6 6 0 1 1 6-6 6.007 6.007 0 0 1-6 6zm3.5-6H8V4.5a.5.5 0 0 0-1 0v4a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 0-1z"/>
</svg>
PK
!<�T#��6chrome/browser/skin/classic/browser/icon-search-64.svg<?xml version="1.0" encoding="utf-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 xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
  <ellipse cx="32" cy="34" rx="29.5" ry="30" fill="#000" fill-opacity=".1"/>
  <circle cx="32" cy="32" r="30" fill="#58bf43"/>
  <circle cx="32" cy="32" r="29.5" stroke="#41a833" stroke-width="1" fill="none"/>
  <path d="M50,47.131L47.131,50,36.776,39.647a16.038,16.038,0,1,1,2.871-2.871ZM27,15A12,12,0,1,0,39,27,12,12,0,0,0,27,15Z" stroke="#41a833" stroke-width="2" fill="none"/>
  <path d="M50,47.131L47.131,50,36.776,39.647a16.038,16.038,0,1,1,2.871-2.871ZM27,15A12,12,0,1,0,39,27,12,12,0,0,0,27,15Z" fill="#fff"/>
  <circle cx="27" cy="27" r="13" fill="#fff" fill-opacity=".2"/>
</svg>
PK
!<�m���<chrome/browser/skin/classic/browser/identity-icon-notice.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" fill-rule="evenodd" d="M13.5 5A2.5 2.5 0 1 1 16 2.5 2.5 2.5 0 0 1 13.5 5zM8 6a1 1 0 1 1 1-1 1 1 0 0 1-1 1zm1 5a1 1 0 0 1-2 0V8a1 1 0 0 1 2 0v3zM8 2a6.08 6.08 0 1 0 5.629 3.987 3.452 3.452 0 0 0 .984-.185A6.9 6.9 0 0 1 15 8a7 7 0 1 1-7-7 6.9 6.9 0 0 1 2.2.387 3.452 3.452 0 0 0-.185.984A5.951 5.951 0 0 0 8 2z"/>
</svg>
PK
!<;v�|�T�TKchrome/browser/skin/classic/browser/illustrations/error-session-restore.svg<svg xmlns="http://www.w3.org/2000/svg" width="300" height="300" viewBox="0 0 300 300">
  <defs>
    <linearGradient id="a" x1="-300.021" y1="-272.736" x2="547.138" y2="574.423" gradientUnits="userSpaceOnUse">
      <stop offset="0" stop-color="#ccfbff"/>
      <stop offset="1" stop-color="#c9e4ff"/>
    </linearGradient>
    <linearGradient id="b" x1="-18.672" y1="23.78" x2="279.805" y2="322.256" gradientUnits="userSpaceOnUse">
      <stop offset="0" stop-color="#00c8d7"/>
      <stop offset="1" stop-color="#0a84ff"/>
    </linearGradient>
  </defs>
  <ellipse cx="147.98" cy="227.18" rx="37.772" ry="5.739" fill="#eaeaee"/>
  <path d="M166.909 241.114c-3.981-6.089-4.649-6.1-5.174-6.1-.694-.012-20.082-1.128-30.409-1.727l-5.48-2.529a1 1 0 0 0-.736-.04c-.521.174-8.625 2.562-11.7 3.468a1 1 0 0 0 .217 1.957l13.172.854-6.831 3.564a1 1 0 0 0 .458 1.887l45.649.217a1 1 0 0 0 .837-1.547z" fill="#eaeaee"/>
  <path d="M147.941 251.71l-6.3-4.348a.934.934 0 0 0-.6-.176l-36.954 1.3a1 1 0 0 0-.649.27l-3.478 3.261a1 1 0 0 0 .684 1.729l46.736-.217a1 1 0 0 0 .563-1.823z" fill="#eaeaee"/>
  <path d="M196.264 252.187l-11.3-9.13a1 1 0 0 0-.718-.218l-26.5 2.39-4.328.216a1 1 0 0 0-.648 1.715l8.7 8.479a1 1 0 0 0 .7.284h.059l33.476-1.957a1 1 0 0 0 .57-1.776z" fill="#eaeaee"/>
  <path d="M223.976 231.634a1.006 1.006 0 0 0-.71-.319c-11.112-.254-28.009-.689-29.167-.85a1 1 0 0 0-.893 1.7l5 4.782a1 1 0 0 0 .691.277h.051c9.877-.507 24.978-1.217 26.009-1.114a1.108 1.108 0 0 0 1.258-.485c.354-.598.434-1.111-2.239-3.991z" fill="#eaeaee"/>
  <path d="M157.336 56.28H133.1c-2.108-3.9-7.559-12.719-14.4-14.023-8.952-1.707-10.737 7.217-10.737 7.217s-5.949-15.469-21-13.419c-16.878 2.3-8.928 20.065-8.928 20.065H52.627l8.187.159h-8.191a1 1 0 0 0 0 2h104.713a1 1 0 0 0 0-2z" fill="#fff"/>
  <path d="M255.293 70.249H241.6c-1.129-2.113-4.19-7.156-8.057-7.894-4.978-.949-5.971 4.013-5.971 4.013s-3.309-8.6-11.68-7.462c-9.386 1.278-4.965 11.158-4.965 11.158H196.8l9.462.184h-9.2a1 1 0 0 0 0 2h58.233a1 1 0 0 0 0-2z" fill="#fff"/>
  <path d="M241.739 47.853l.045-.1c-.058.025-.063.06-.045.1z" fill="#fff"/>
  <path d="M153.55 53.855h-3a.5.5 0 0 1 0-1h3a.5.5 0 0 1 0 1zm-11 0h-8.07a.5.5 0 0 1-.452-.285 38.288 38.288 0 0 0-1.976-3.334.5.5 0 0 1 .837-.549c.393.6 1.445 2.295 1.889 3.168h7.771a.5.5 0 0 1 0 1zm-66.488-.712h-3a.5.5 0 0 1 0-1h3a.5.5 0 0 1 0 1zm-11 0h-12a.5.5 0 0 1 0-1h12a.5.5 0 0 1 0 1zm12.838-1.49a.5.5 0 0 1-.473-.339c-.1-.29-.2-.616-.311-.972a.5.5 0 1 1 .958-.287c.1.344.2.656.3.937a.5.5 0 0 1-.312.635.515.515 0 0 1-.162.026zm30.5-5.153a.506.506 0 0 1-.468-.32 23.563 23.563 0 0 0-2.181-4.037.5.5 0 0 1 .834-.553 27.46 27.46 0 0 1 1.718 2.983 10.516 10.516 0 0 1 3.679-5.017.5.5 0 1 1 .571.82 10.183 10.183 0 0 0-3.663 5.724.5.5 0 0 1-.4.394.492.492 0 0 1-.09.006zm18.343-2.966a.5.5 0 0 1-.339-.133q-.361-.333-.734-.65a.5.5 0 1 1 .646-.762q.39.33.766.678a.5.5 0 0 1-.339.867zM76.821 41.81a.563.563 0 0 1-.09-.008.5.5 0 0 1-.4-.582c.792-4.351 3.544-7.229 8.181-8.555a.5.5 0 1 1 .275.961c-4.24 1.213-6.754 3.828-7.473 7.773a.5.5 0 0 1-.493.411zm45.892-1.221a.5.5 0 0 1-.236-.06 12.614 12.614 0 0 0-2.667-1.082.5.5 0 0 1 .262-.965 13.589 13.589 0 0 1 2.879 1.166.5.5 0 0 1-.237.94zm-22.76-4.664a.5.5 0 0 1-.28-.086q-.4-.271-.82-.525a.5.5 0 1 1 .517-.855q.444.267.865.553a.5.5 0 0 1-.281.914zm-4.47-2.2a.5.5 0 0 1-.154-.024 16.412 16.412 0 0 0-2.832-.647.5.5 0 0 1 .137-.99 17.452 17.452 0 0 1 3 .687.5.5 0 0 1-.154.976z" fill="#eaeaee"/>
  <path d="M182.316 98.659a18.015 18.015 0 0 1-9.94-2.7 21.661 21.661 0 0 0-21.783 0 19.662 19.662 0 0 1-19.879 0 21.655 21.655 0 0 0-21.78 0 19.65 19.65 0 0 1-19.873 0 19.92 19.92 0 0 0-10.886-2.937 19.931 19.931 0 0 0-10.889 2.938 18.005 18.005 0 0 1-9.937 2.7 18 18 0 0 1-9.936-2.7 19.926 19.926 0 0 0-10.888-2.937 1 1 0 0 0 0 2 18 18 0 0 1 9.936 2.7 19.926 19.926 0 0 0 10.888 2.938 19.932 19.932 0 0 0 10.89-2.938 18 18 0 0 1 9.937-2.7 18 18 0 0 1 9.934 2.7 21.649 21.649 0 0 0 21.777 0 19.657 19.657 0 0 1 19.876 0 21.659 21.659 0 0 0 21.782 0 19.666 19.666 0 0 1 19.881 0 19.937 19.937 0 0 0 10.892 2.938 1 1 0 0 0 0-2zm66.143-10.24a.5.5 0 0 0-.7-.084 19.457 19.457 0 0 1-9.2 3.162 11.427 11.427 0 0 1-3.738-.029c2-.62 3.759-1.488 4.548-2.628.953-1.376.377-2.177.086-2.457-.88-.849-2.82-.664-4.236.4-1.065.8-2.892 2.666-2.477 4.2a29.546 29.546 0 0 1-4.815.685.5.5 0 0 0 .024 1h.025a30.5 30.5 0 0 0 5.355-.79c.88.715 2.643.915 5.337.616 6.261-.7 9.574-3.263 9.712-3.371a.5.5 0 0 0 .079-.704zm-12.646-.839a3.515 3.515 0 0 1 2.052-.735 1.318 1.318 0 0 1 .891.259c.31.3 0 .859-.215 1.168-.734 1.06-2.664 1.894-4.832 2.472-.233-.777.881-2.244 2.104-3.164zm-103.279 3.449a17.836 17.836 0 0 0 2.88 1.09.5.5 0 0 0 .27-.963 16.942 16.942 0 0 1-2.722-1.031.5.5 0 1 0-.428.9zm-18.885-2.8a.483.483 0 0 0 .168-.029 18.775 18.775 0 0 1 6.459-1 20.224 20.224 0 0 1 5.081.581.5.5 0 1 0 .249-.969 21.191 21.191 0 0 0-5.33-.612 19.728 19.728 0 0 0-6.795 1.062.5.5 0 0 0 .168.971zm35.6 2.948a.5.5 0 0 0 .208-.046c.81-.373 1.565-.782 2.3-1.178a18.324 18.324 0 0 1 8.719-2.718.5.5 0 0 0-.057-1 19.381 19.381 0 0 0-9.139 2.837c-.716.388-1.458.789-2.237 1.148a.5.5 0 0 0 .209.954zm18.929-3.052a19.413 19.413 0 0 1 2.681 1.165.5.5 0 0 0 .457-.891 20.737 20.737 0 0 0-2.815-1.222.5.5 0 0 0-.323.947zm-28.717 4.656c.328.02.666.034 1.017.043h.013a.5.5 0 0 0 .013-1 26.883 26.883 0 0 1-.981-.041.5.5 0 0 0-.061 1zm-34.925-.516a.516.516 0 0 0 .121-.015c.345-.086.674-.18.992-.28a.5.5 0 1 0-.3-.953q-.448.142-.932.263a.5.5 0 0 0 .12.985zm69.891-1.124c.3.141.619.276.944.4a.486.486 0 0 0 .184.035.5.5 0 0 0 .184-.965c-.308-.122-.6-.25-.893-.383a.5.5 0 0 0-.418.908zm31.973-4.795a24.722 24.722 0 0 0-2.812-.152h-.246a.5.5 0 0 0 .006 1h.239a23.378 23.378 0 0 1 2.7.146h.058a.5.5 0 0 0 .057-1zm4.957 1.286a18.905 18.905 0 0 0-.957-.373.5.5 0 1 0-.337.941c.313.112.614.231.906.354a.5.5 0 0 0 .388-.922zm13.064 4.2a20.357 20.357 0 0 1-4.951-.548.5.5 0 0 0-.241.971 21.357 21.357 0 0 0 5.192.577.5.5 0 1 0 0-1zM78.626 87.193A18.422 18.422 0 0 1 88.8 89.95l.942.506a.5.5 0 0 0 .468-.885l-.934-.5a19.489 19.489 0 0 0-10.649-2.878.5.5 0 0 0 0 1zm116.741.636c-.863.392-1.662.824-2.435 1.242a18.337 18.337 0 0 1-8.572 2.708.5.5 0 0 0 .031 1h.032a19.407 19.407 0 0 0 8.985-2.827c.758-.409 1.542-.833 2.373-1.211a.5.5 0 1 0-.413-.91zm-97.823 4.933c.6.044 1.243.066 1.905.066q.6 0 1.148-.022a.5.5 0 0 0 .479-.521.493.493 0 0 0-.52-.479 26.789 26.789 0 0 1-2.94-.042.493.493 0 0 0-.535.462.5.5 0 0 0 .463.536z" fill="#eaeaee"/>
  <path d="M93.5 151.647a18.413 18.413 0 0 1 10.172 2.757c.313.17.627.34.945.507a.5.5 0 1 0 .465-.885c-.314-.165-.624-.333-.934-.5a19.486 19.486 0 0 0-10.648-2.879.5.5 0 0 0 0 1zm18.919 5.567c.518.038 1.067.056 1.631.062v-1a24.239 24.239 0 0 1-1.56-.06.5.5 0 0 0-.073 1zm84.386 7.1a17.993 17.993 0 0 1-9.934-2.7 24.348 24.348 0 0 0-4.74-2.115q-.039 1.039-.087 2.073a24.339 24.339 0 0 1 3.875 1.8 19.91 19.91 0 0 0 10.886 2.938 1 1 0 1 0 0-2zm-83.284 0a18 18 0 0 1-9.935-2.7 19.924 19.924 0 0 0-10.887-2.937 19.92 19.92 0 0 0-10.886 2.938 18 18 0 0 1-9.935 2.7 18 18 0 0 1-9.934-2.7 19.92 19.92 0 0 0-10.886-2.937 1 1 0 0 0 0 2 18 18 0 0 1 9.934 2.7 19.92 19.92 0 0 0 10.886 2.938 19.922 19.922 0 0 0 10.887-2.937 19.644 19.644 0 0 1 19.869 0 19.923 19.923 0 0 0 10.887 2.938c.222 0 .432-.006.645-.011q-.027-1-.047-2c-.202.002-.397.007-.603.007z" fill="#eaeaee"/>
  <path d="M209.235 68.394h-12a.5.5 0 0 1 0-1h12a.5.5 0 0 1 0 1zm45.582-.184h-1.8a.5.5 0 0 1 0-1h1.8a.5.5 0 0 1 0 1zm-10.8 0h-1a.5.5 0 0 1 0-1h1a.5.5 0 0 1 0 1zm-3.491-2.881a.5.5 0 0 1-.39-.187 18.3 18.3 0 0 0-2-2.129.5.5 0 1 1 .668-.744 19.181 19.181 0 0 1 2.109 2.246.5.5 0 0 1-.389.813zm-12.517-.629a.5.5 0 0 1-.466-.32 13.846 13.846 0 0 0-3.855-5.164.5.5 0 1 1 .623-.781 14.475 14.475 0 0 1 3.623 4.438 5.416 5.416 0 0 1 3.107-3.029.5.5 0 0 1 .326.945 5 5 0 0 0-2.867 3.51.5.5 0 0 1-.447.4zm-17.64-2.036h-.051a.5.5 0 0 1-.447-.548 6.448 6.448 0 0 1 1.057-3.035.5.5 0 1 1 .824.566 5.457 5.457 0 0 0-.886 2.57.5.5 0 0 1-.497.444zm4.193-5.039a.5.5 0 0 1-.151-.977 10.123 10.123 0 0 1 1.018-.264.5.5 0 0 1 .2.98 8.89 8.89 0 0 0-.916.236.512.512 0 0 1-.151.022z" fill="#eaeaee"/>
  <path d="M104.646 204.6H40.958a1 1 0 0 1 0-2h63.688a1 1 0 1 1 0 2z" fill="#eaeaee"/>
  <path d="M209.627 204.6h-21.586a1 1 0 0 1 0-2h21.587a1 1 0 0 1 0 2z" fill="#eaeaee"/>
  <path d="M264.9 204.6h-22.368a1 1 0 0 1 0-2H264.9a1 1 0 0 1 0 2z" fill="#eaeaee"/>
  <path d="M212.21 123.577a.479.479 0 0 0 .167-.029 18.777 18.777 0 0 1 6.456-1 .5.5 0 0 0 0-1 19.725 19.725 0 0 0-6.791 1.06.5.5 0 0 0 .167.971zm-9.118 4.039a.516.516 0 0 0 .121-.015c.345-.086.674-.18.992-.28a.5.5 0 1 0-.3-.953q-.448.142-.932.263a.5.5 0 0 0 .12.985zm19.345-4.964h.025a30.515 30.515 0 0 0 5.356-.79c.881.714 2.644.916 5.336.616 6.261-.7 9.575-3.264 9.712-3.372a.5.5 0 0 0-.619-.785 19.464 19.464 0 0 1-9.2 3.163 11.451 11.451 0 0 1-3.737-.029c2-.621 3.758-1.488 4.548-2.629.952-1.375.377-2.176.086-2.457-.88-.846-2.82-.664-4.236.4-1.065.8-2.892 2.667-2.476 4.2a29.559 29.559 0 0 1-4.815.685.5.5 0 0 0 .024 1zm7.867-5.087a3.514 3.514 0 0 1 2.051-.735 1.319 1.319 0 0 1 .892.259c.31.3 0 .859-.214 1.168-.734 1.06-2.664 1.894-4.832 2.473-.234-.777.881-2.248 2.099-3.165zm-34.2 10.55c.6.044 1.242.066 1.9.066q.6 0 1.15-.022a.5.5 0 0 0 .479-.521.5.5 0 0 0-.52-.479 26.784 26.784 0 0 1-2.94-.042.494.494 0 0 0-.535.462.5.5 0 0 0 .468.536zm65.04.63a19.926 19.926 0 0 0-10.888 2.938 19.646 19.646 0 0 1-19.87 0 21.646 21.646 0 0 0-21.775 0 19.648 19.648 0 0 1-19.871 0 27.517 27.517 0 0 0-3.86-1.825 8.043 8.043 0 0 1-1.24 1.6l-.067.067a23.017 23.017 0 0 1 4.215 1.913 21.646 21.646 0 0 0 21.775 0 19.648 19.648 0 0 1 19.871 0 21.644 21.644 0 0 0 21.774 0 18 18 0 0 1 9.936-2.7 1 1 0 0 0 0-2zm-15.1 13.382q-.448.142-.932.263a.5.5 0 0 0 .12.985.516.516 0 0 0 .121-.015c.345-.086.674-.18.992-.28a.5.5 0 1 0-.3-.953zM187.36 125.3c.313.17.626.34.944.507a.5.5 0 0 0 .466-.885c-.313-.165-.624-.333-.936-.5a30.785 30.785 0 0 0-3.129-1.539 7.669 7.669 0 0 1 .688 1.4c.675.317 1.317.669 1.967 1.017zm53.9 17.615a26.531 26.531 0 0 1-2.94-.042.5.5 0 0 0-.073 1c.6.044 1.241.066 1.9.066q.6 0 1.152-.022a.5.5 0 0 0 .479-.521.517.517 0 0 0-.52-.477zm19.709-5.611a19.715 19.715 0 0 0-6.786 1.058.5.5 0 0 0 .167.971.479.479 0 0 0 .167-.029 18.767 18.767 0 0 1 6.451-1 .5.5 0 0 0 0-1zm-30.059 3.378c-.314-.165-.624-.333-.934-.5a19.5 19.5 0 0 0-10.648-2.877.5.5 0 0 0 0 1 18.429 18.429 0 0 1 10.172 2.756c.313.17.627.34.945.507a.5.5 0 1 0 .465-.885z" fill="#eaeaee"/>
  <path d="M186.068 174.772c1.01-10.949 1.759-25.609.451-38.9l.514-.514c5.006-5.033 4.911-12.421-.216-17.577a12.97 12.97 0 0 0-17.732-.631l-4.1-1.107s.429-2.85-.321-3.725a2.606 2.606 0 0 0-1.935-.82s.81-4.055-1.065-5.555c-2.071-1.656-7.945 3-7.945 3-3.551 1.84-9.684 4.594-12.2 4.956-2.875.413-9.386 2.182-15.237 3.867l-.414-.417a12.97 12.97 0 0 0-18.4 0 13.094 13.094 0 0 0 0 18.425l1.835 1.845a281.627 281.627 0 0 0-.386 35.906 21.818 21.818 0 0 0-5.216 17.689c.914 6.436 4.8 9.96 8.711 12.237a45.905 45.905 0 0 0 2.483 7.891 48.514 48.514 0 0 0 1.584 5.944c2.971 8.511 6.707 12.826 11.1 12.826a6.214 6.214 0 0 0 3.926-1.4c1.7-1.391 2.456-3.106 2.576-9.037 4.45.407 9.479.63 14.506.63 3.466 0 7.989-.107 12.491-.521-.062 5.242.283 7.251 2.332 8.929a6.041 6.041 0 0 0 3.818 1.358c5.994 0 9.9-8.623 11.693-13.763a54.51 54.51 0 0 0 2.272-9.4 22.5 22.5 0 0 0 .282-1.662c5.6-3.181 9.206-8.133 10.03-13.929a18.73 18.73 0 0 0-5.437-16.545z" fill="#fff"/>
  <path d="M180.923 176.649c1.356-13.432 2.047-29.548.435-42.9l2.282-2.282c3.039-3.056 2.813-7.153-.227-10.209a7.753 7.753 0 0 0-11.006 0l-1.5 1.506-10.849-2.927V116.9l-5.485.652 1.837-4.185s-9.323 4.837-13.862 5.489c-3.256.468-12.518 3.116-17.495 4.577l-2.6-2.612a7.753 7.753 0 0 0-11.006 0 7.856 7.856 0 0 0 0 11.067l3.588 3.608c-1 8.19-1.392 24.339-.5 39.878a16.356 16.356 0 0 0-5.384 14.9c.691 4.861 3.718 7.353 8.133 9.5a43.736 43.736 0 0 0 2.876 9.68 46.786 46.786 0 0 0 1.529 5.906c2.594 7.432 5.5 10.17 6.8 9.108.621-.509.764-5.158.623-10.882 10.036 1.55 27.191 1.879 37.611.037-.141 5.711-.339 10.337.281 10.845 1.3 1.062 4.686-2.654 7.28-10.086a49.724 49.724 0 0 0 2.075-8.686c.24-1.009.389-2.865.65-4.129 4.971-2.094 8.871-5.932 9.619-11.195 1.06-7.448-2.858-11.281-5.705-13.723z" fill="url(#a)"/>
  <g fill="#f9f9fa">
    <path d="M157.681 202.625c-7.81-1.785-7.984-6.013-7.316-8.5l-5.042-.149c.345 2.51-.575 6.011-7.5 7.594a37.408 37.408 0 0 1-10.215.863c9.456 9.453 32.143 9.571 41.307.752a33.732 33.732 0 0 1-11.234-.56z"/>
    <path d="M174.91 164.318a9.715 9.715 0 0 0 2.452-3.815c.485-10.321.055-20.871-2.338-29.052-2.232-7.632-28.394-17.552-50.949-2.609-4.418 2.927-5.452 17.609-4.682 33.059a13.622 13.622 0 0 0 1.968 2.818c2.466 2.659.879 4.441-1.338 6.078a179.491 179.491 0 0 0 2.655 18.852 16.685 16.685 0 0 0 4.478 1.311l-.076-24.062 17.388.691 2.177-5.053h6.114l2.011 5.434 15.319.637-.706 23.843a20.118 20.118 0 0 0 4.39-1.865c.9-4.84 1.941-11.371 2.7-18.563-2.273-2.035-4.473-5.278-1.563-7.704z"/>
  </g>
  <g fill="url(#b)">
    <path d="M128.086 142.316a1.412 1.412 0 0 0 1.411-1.41v-3.162a1.41 1.41 0 1 0-2.821 0v3.162a1.412 1.412 0 0 0 1.41 1.41z"/>
    <path d="M163.123 142.316a1.412 1.412 0 0 0 1.411-1.41v-3.162a1.41 1.41 0 1 0-2.821 0v3.162a1.412 1.412 0 0 0 1.41 1.41z"/>
    <path d="M167.475 144.641a1 1 0 0 0-1.383.28 3.677 3.677 0 0 1-5.937 0 1 1 0 0 0-1.675 1.093 5.674 5.674 0 0 0 9.287 0 1 1 0 0 0-.292-1.373z"/>
    <path d="M132.434 144.633a1 1 0 0 0-1.383.295 3.758 3.758 0 0 1-2.965 1.5 3.709 3.709 0 0 1-2.968-1.506 1 1 0 0 0-1.675 1.093 5.674 5.674 0 0 0 9.287 0 1 1 0 0 0-.296-1.382z"/>
    <path d="M156.383 149.1a3.079 3.079 0 0 1-2.589-.454 5.065 5.065 0 0 1-1.237-1.437 14.022 14.022 0 0 0 4.227-4.094 2.611 2.611 0 0 0 .316-2.192 1.475 1.475 0 0 0-1.215-.9c-2.294-.468-9.138 1.038-12.121 4.226a1.236 1.236 0 0 0-.175 1.476c.667 1.18 4.491 2.1 6.339 2.316a7.2 7.2 0 0 1-.174 1.908 4.738 4.738 0 0 1-3.063 3.2 1 1 0 0 0 .563 1.919 6.765 6.765 0 0 0 4.441-4.631 7.768 7.768 0 0 0 .161-.853 4.526 4.526 0 0 0 .864.75 5.069 5.069 0 0 0 4.408.626 1 1 0 0 0-.743-1.857z"/>
    <path stroke="#59acff" stroke-linecap="round" stroke-linejoin="round" d="M116.035 173.443l-4.139 3.327"/>
    <path d="M125.989 190.92h-.029c-4.94-.28-6.414-3.149-6.474-3.271a.5.5 0 0 1 .9-.445c.055.105 1.317 2.473 5.636 2.719a.5.5 0 0 1-.028 1z"/>
    <path d="M169.551 192.631a4.541 4.541 0 0 1-.563-.035.5.5 0 1 1 .124-.992c3.16.386 6.822-3.654 6.858-3.7a.5.5 0 0 1 .746.666c-.154.178-3.658 4.061-7.165 4.061z"/>
    <path d="M183.016 175.823c1.106-11.346 2.011-27.289.441-41.342l1.6-1.6c3.752-3.772 3.659-9.13-.223-13.034a9.753 9.753 0 0 0-13.842 0l-.68.683-8.249-2.225v-1.4a2 2 0 0 0-2.236-1.986l-2.011.239.431-.982a2 2 0 0 0-2.752-2.579c-2.511 1.3-9.781 4.79-13.226 5.284-3.123.449-11.319 2.756-16.631 4.3l-1.759-1.769a9.752 9.752 0 0 0-13.842 0 9.869 9.869 0 0 0 0 13.888l2.909 2.925c-.839 7.717-1.311 22.481-.456 38.32a18.247 18.247 0 0 0-5.315 16.012c.785 5.528 4.3 8.4 7.952 10.359a.973.973 0 0 0 .379.315l.044.018a44.416 44.416 0 0 0 2.707 8.865 47.481 47.481 0 0 0 1.548 5.908c1.391 3.985 4.288 10.669 8.067 10.669a2.948 2.948 0 0 0 1.888-.672c.741-.607 1.506-1.232 1.4-10.127a149.239 149.239 0 0 0 17.72.992 122.747 122.747 0 0 0 15.786-.912c-.187 7.245-.156 9.04 1.075 10.048a2.777 2.777 0 0 0 1.78.631c3.395 0 6.7-6 8.655-11.606a51.053 51.053 0 0 0 2.149-8.956c.138-.606.239-1.364.346-2.163.043-.324.087-.653.133-.968a21.447 21.447 0 0 0 2.278-1.27c.384-.2.763-.42 1.127-.66a.978.978 0 0 0 .307-.341 15.378 15.378 0 0 0 6.094-10.03 15.622 15.622 0 0 0-5.594-14.834zm3.615 14.554a13.725 13.725 0 0 1-6.578 9.6c-4.748 2.479-11.193 2.162-11.262 2.162a1 1 0 0 0-.111 2c.065 0 .426.022 1 .022a30.255 30.255 0 0 0 7.052-.837c-.121.893-.231 1.788-.373 2.385a49.721 49.721 0 0 1-2.075 8.686c-2.3 6.6-5.231 10.265-6.767 10.265a.788.788 0 0 1-.513-.178c-.588-.481-.44-4.669-.3-9.971a92.144 92.144 0 0 0 3.713-.719.5.5 0 1 0-.213-.977 95.1 95.1 0 0 1-10.957 1.71c-3.286.248-6.816.369-10.368.369-3.15 0-6.3-.1-9.3-.267a93.424 93.424 0 0 1-15-2.351.5.5 0 0 0-.265.965c.06.016 1.8.483 4.818 1.03.114 5.411-.04 9.722-.637 10.211a.956.956 0 0 1-.621.22c-1.47 0-3.942-2.918-6.179-9.328a46.793 46.793 0 0 1-1.529-5.906 37.051 37.051 0 0 1-2.391-7.444 43.727 43.727 0 0 0 9.71 1.22h.005a1 1 0 0 0 .005-2c-.065 0-6.126-.055-10.213-1.46-4.41-2.147-7.435-4.639-8.125-9.5a16.356 16.356 0 0 1 5.384-14.9c-.888-15.538-.5-31.687.5-39.878l-3.588-3.608a7.856 7.856 0 0 1 0-11.067 7.752 7.752 0 0 1 11.006 0l1.839 1.849c-6.694 2.774-7.5 6.758-7.537 6.949a.5.5 0 0 0 .983.183c.008-.043.855-3.866 7.552-6.438 5.027-1.473 14.049-4.046 17.255-4.507 4.54-.652 13.862-5.489 13.862-5.489l-1.837 4.185 5.485-.652v2.935l10.7 2.888c5.988 2.057 5.959 4.674 5.957 4.737a.5.5 0 0 0 .448.538h.05a.5.5 0 0 0 .5-.45c.014-.139.235-3.223-6.035-5.565l.726-.73a7.753 7.753 0 0 1 11.006 0c3.039 3.056 3.266 7.154.227 10.209l-2.282 2.282c1.562 12.943.959 28.472-.314 41.645l-3.794-3.566a.5.5 0 1 0-.685.729l4.357 4.1.01.009 1.883 1.77c.011.01.025.014.037.024a13.352 13.352 0 0 1 3.779 11.911z"/>
    <path d="M168.257 240.857h-.01l-48.258-.979a.5.5 0 0 1-.263-.919l8.478-5.543a.449.449 0 0 1 .3-.08c28.881 1.809 32.47.4 32.853.169a.5.5 0 0 1 .729-.023L168.61 240a.5.5 0 0 1-.354.854z"/>
    <path d="M147.391 237.905c-4.526 0-12.919-.456-20.082-3.389-9.543-3.907-13.037-8.2-13.719-9.141a.5.5 0 0 1 0-.9l12.716-6.2a.493.493 0 0 1 .449.006.5.5 0 0 1 .263.363c.011.068 1.141 6.818 5.027 9.79 5.259 4.022 11.862 1.9 11.929 1.88a.5.5 0 0 1 .511.121L151 236.96a.5.5 0 0 1-.315.853c-.038.002-1.285.092-3.294.092z"/>
    <path d="M164.795 253.513c-2.23 0-3.545-.125-3.574-.127a.5.5 0 0 1-.373-.229l-8.561-13.457a.5.5 0 0 1 .4-.769c.109 0 11.008-.447 18.976-4.033 7.87-3.541 10.335-8.282 10.359-8.33a.5.5 0 0 1 .382-.271.5.5 0 0 1 .438.163l13.047 14.678a.5.5 0 0 1 .006.657 36.427 36.427 0 0 1-19.868 10.769 69.143 69.143 0 0 1-11.232.949z"/>
    <path d="M148.408 251.333l-48.267-.189a.5.5 0 0 1-.343-.862l6.677-6.361a.5.5 0 0 1 .728.041c.378.239 3.928 1.74 32.847.626a.542.542 0 0 1 .3.088l8.341 5.746a.5.5 0 0 1-.284.912z"/>
    <path d="M226.286 234.554h-.027c-.12-.006-12.14-.7-21.877-4.159-9.865-3.507-14.313-8.4-15.115-9.353a.5.5 0 0 1-.045-.909l9.663-5a.5.5 0 0 1 .635.151c.042.058 4.181 5.759 8.169 8.808 5.376 4.112 15.307 5.541 15.406 5.555a.505.505 0 0 1 .315.175l3.26 3.913a.5.5 0 0 1-.384.82z"/>
    <path d="M209.79 213.262a.5.5 0 0 1-.408-.788l5.322-7.532a.5.5 0 0 1 .277-.194 24.254 24.254 0 0 0 10.284-6.463 23.325 23.325 0 0 0 4.677-8.068.5.5 0 0 1 .619-.339l13.582 3.951a.5.5 0 0 1 .148.89c-.513 1.044-3.23 5.865-11.973 11.332-9.476 5.926-22.353 7.2-22.482 7.21z"/>
    <path d="M144.443 167.645l-.217 8.151 1.304-.543.87 1.087 1.087-.979.978 1.196.978-1.196 1.087 1.305.869-1.196.979 1.304.978-1.521 1.413.652v-7.934l-10.326-.326z"/>
    <path d="M165.622 173.79l-2.857-2.5a.5.5 0 0 0-.65-.007l-2.643 2.214a.5.5 0 0 0 .239.876l1.283.214-.058 1.921a.5.5 0 0 0 .457.513l1.643.144h.043a.5.5 0 0 0 .5-.5v-1.832l1.762-.17a.5.5 0 0 0 .281-.874z"/>
    <path d="M169.732 172.623a.524.524 0 0 0-.519.481l-.8 21.024-40.759-1.183-.159-15.1a.5.5 0 0 0-.5-.5h-.005a.5.5 0 0 0-.495.5l.164 15.582c0 .015.007.027.009.041a.5.5 0 0 0 .289.569l13.975 6.062a22.293 22.293 0 0 1-6.079 1.544.5.5 0 0 0 .05 1h.051c.194-.02 4.631-.49 7.136-2.035l2.022.877a.5.5 0 0 0 .7-.433l.35-6.6 6.365.185a14.875 14.875 0 0 1-2.8 4.539 24.216 24.216 0 0 1-10.479 6.371.5.5 0 0 0-.342.292l-3.56 8.508a.5.5 0 0 0 .461.693.492.492 0 0 0 .154-.024 64.014 64.014 0 0 0 17.133-9.089l1.849 4.061a.5.5 0 0 0 .374.286.46.46 0 0 0 .082.007.5.5 0 0 0 .364-.157l14.088-14.968s0-.012.009-.017h.044a.5.5 0 0 0 .5-.481l.815-21.52a.5.5 0 0 0-.487-.515z"/>
    <path d="M170.579 168.566a.439.439 0 0 0-.054-.187.471.471 0 0 0-.049-.078.485.485 0 0 0-.074-.073c-.014-.012-.022-.028-.038-.038l-8.958-5.92a.5.5 0 0 0-.261-.083l-25.205-.746c-.011 0-.021.005-.032.006a.488.488 0 0 0-.115.021 17.081 17.081 0 0 0-.113.036l-8.846 4.957c-.01.006-.013.018-.022.024a.494.494 0 0 0-.189.208.5.5 0 0 0-.042.2l-.08 7.691a.5.5 0 0 0 .495.5H127a.5.5 0 0 0 .5-.5l.075-7.174 14.4.657H142a.5.5 0 0 0 .022-1l-13.17-.6 7.2-4.033 24.924.737 7.375 4.874-11.173-.325a.514.514 0 0 0-.515.485.5.5 0 0 0 .485.515l12.929.376h.015a.49.49 0 0 0 .214-.053.481.481 0 0 0 .05-.033.488.488 0 0 0 .124-.113c.008-.01.021-.015.029-.026s.008-.024.014-.035a.621.621 0 0 0 .06-.187c0-.013.008-.024.009-.038a.475.475 0 0 0-.013-.045z"/>
    <path d="M169.876 169.851a.5.5 0 0 0-.5.5v.815a.5.5 0 1 0 1 0v-.815a.5.5 0 0 0-.5-.5z"/>
  </g>
  <g fill="#fff">
    <path d="M161.635 234.443c-1.483.6-5.74.859-12.372.777l1.737 1.74a.5.5 0 0 1-.315.853c-.04 0-1.287.093-3.3.093-4.39 0-12.412-.436-19.426-3.137l-6.338 4.144 45.4.92z"/>
    <path d="M114.627 225.078c1.019 1.253 4.62 5.057 13.061 8.513 8.37 3.426 18.489 3.393 21.81 3.279l-5.512-5.512c-1.421.4-7.462 1.769-12.552-2.125-3.5-2.678-4.888-7.92-5.281-9.77z"/>
    <path d="M190.3 220.7c1.248 1.357 5.686 5.649 14.414 8.753 7.842 2.788 17.2 3.754 20.433 4.018l-2.385-2.862c-1.471-.224-10.457-1.726-15.683-5.722a49.522 49.522 0 0 1-8.117-8.669z"/>
    <path d="M215.419 205.661l-4.57 6.469c3.292-.446 13.271-2.132 20.939-6.928 7.762-4.854 10.646-9.2 11.432-10.6l-12.474-3.629a24.19 24.19 0 0 1-4.746 7.985 24.936 24.936 0 0 1-10.581 6.703z"/>
    <path d="M152.6 194.682a14.244 14.244 0 0 1-3.155 5.187 25.007 25.007 0 0 1-10.718 6.586L135.7 213.7c3.117-1.151 12.491-4.964 18.936-11.311a40.684 40.684 0 0 0 6.307-7.457z"/>
    <path d="M101.386 250.148l45.406.178-6.872-4.733c-18.61.709-30.359.451-33.017-.7z"/>
    <path d="M182.553 227.638c-.948 1.463-3.872 5.2-10.477 8.174a38.538 38.538 0 0 1-5.678 1.98L168.61 240a.5.5 0 0 1-.354.854h-.01l-14.223-.288 7.535 11.841a63.477 63.477 0 0 0 14.307-.831 35.459 35.459 0 0 0 18.979-10.112z"/>
  </g>
</svg>
PK
!<�p�T��,chrome/browser/skin/classic/browser/info.svg<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <circle fill="#00a1f2" cx="8" cy="8" r="8" />
  <circle fill="#fff" cx="8" cy="4" r="1.25" />
  <rect x="7" y="7" width="2" height="6" rx="1" ry="1" fill="#fff" />
</svg>
PK
!<]zxururBchrome/browser/skin/classic/browser/library-bookmark-animation.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="1078" height="54" fill="context-fill" fill-opacity="context-fill-opacity">
  <defs>
    <mask id="a" mask-type="alpha">
      <path d="M-29.1-76.9l-15.5 48.7 21.4 12 5.4-.8 3-.3 2.3 4.7 5.5.1.8-2.1h10L13 10.8l4.2-39.2 6-25.2-.8-22.3-51.6-1z" transform="matrix(.5 0 0 .5 13.108 28.313)"/>
    </mask>
    <path id="books" d="M8 22a1 1 0 0 0-1 1v10a1 1 0 0 0 2 0V23a1 1 0 0 0-1-1zm3-1a1 1 0 0 0-1 1v11a1 1 0 0 0 2 0V22a1 1 0 0 0-1-1zm8 11.7l-4-11a1 1 0 1 0-2 .6l4 11a1 1 0 1 0 2-.6zM5 20a1 1 0 0 0-1 1v12a1 1 0 0 0 2 0V21a1 1 0 0 0-1-1z"/>
  </defs>
  <svg>
    <use href="#books"/>
  </svg>
  <svg x="22">
    <use href="#books"/>
    <g mask="url(#a)" opacity=".043">
      <path fill="context-stroke" stroke="none" d="M13.6 1.7L14 5l.1 1.5 1.3.6 3 1.7-3.2 1.6-1.2.7-.2 1.3-.7 3.4-2.3-2.4-1-1.2-1.5.3-3.2.5 1.6-3 .6-1.3L7 7.5 5.4 4.3l3.4.5 1.4.3 1-1 2.4-2.4"/>
      <path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M13.6 1.7L14 5l.1 1.5 1.3.6 3 1.7-3.2 1.6-1.2.7-.2 1.3-.7 3.4-2.3-2.4-1-1.2-1.5.3-3.2.5 1.6-3 .6-1.3L7 7.5 5.4 4.3l3.4.5 1.4.3 1-1 2.4-2.4"/>
    </g>
  </svg>
  <svg x="44">
    <use href="#books"/>
    <g mask="url(#a)" opacity=".156">
      <path fill="context-stroke" stroke="none" d="M13.3 1.6l.6 3.3.3 1.4 1.2.6 3 1.5-3 1.8-1.2.7-.2 1.3-.5 3.5-2.4-2.4-1-1-1.5.2-3.2.7L6.9 10l.6-1.3-.6-1.2-1.6-3.1 3.4.4L10 5l1-1 2.2-2.5"/>
      <path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M13.3 1.6l.6 3.3.3 1.4 1.2.6 3 1.5-3 1.8-1.2.7-.2 1.3-.5 3.5-2.4-2.4-1-1-1.5.2-3.2.7L6.9 10l.6-1.3-.6-1.2-1.6-3.1 3.4.4L10 5l1-1 2.2-2.5"/>
    </g>
  </svg>
  <svg x="66">
    <use href="#books"/>
    <g mask="url(#a)" opacity=".316">
      <path fill="context-stroke" stroke="none" d="M13 1.5l.7 3.3L14 6l1.3.6 3 1.3-2.8 2-1.2.7V12l-.4 3.4-2.5-2.2-1-1-1.5.4-3.2.8L7 10.3 7.5 9 7 7.8l-1.7-3 3.3.2 1.5.1.9-1 2-2.6"/>
      <path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M13 1.5l.7 3.3L14 6l1.3.6 3 1.3-2.8 2-1.2.7V12l-.4 3.4-2.5-2.2-1-1-1.5.4-3.2.8L7 10.3 7.5 9 7 7.8l-1.7-3 3.3.2 1.5.1.9-1 2-2.6"/>
    </g>
  </svg>
  <svg x="88">
    <use href="#books"/>
    <g mask="url(#a)" opacity=".5">
      <path fill="context-stroke" stroke="none" d="M12.5 1.4l1 3.2.4 1.3 1.3.5 3 1.1-2.7 2.1-1 .8v1.3l-.1 3.4-2.6-2-1.2-.9-1.4.5-3 1 1-3.2.5-1.3-.8-1.1-1.8-3 3.3.1h1.4l.8-1.1 2-2.7"/>
      <path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M12.5 1.4l1 3.2.4 1.3 1.3.5 3 1.1-2.7 2.1-1 .8v1.3l-.1 3.4-2.6-2-1.2-.9-1.4.5-3 1 1-3.2.5-1.3-.8-1.1-1.8-3 3.3.1h1.4l.8-1.1 2-2.7"/>
    </g>
  </svg>
  <svg x="110">
    <use href="#books"/>
    <g mask="url(#a)" opacity=".684">
      <path fill="context-stroke" stroke="none" d="M12.1 1.4l1.2 3 .4 1.4 1.4.3 3 1-2.5 2.2-1 .8v1.4l.2 3.3L12 13l-1.2-.8-1.4.5-3 1.2 1-3.2.3-1.3-.8-1-2-2.8 3.2-.2h1.4l.8-1.3L12 1.4"/>
      <path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M12.1 1.4l1.2 3 .4 1.4 1.4.3 3 1-2.5 2.2-1 .8v1.4l.2 3.3L12 13l-1.2-.8-1.4.5-3 1.2 1-3.2.3-1.3-.8-1-2-2.8 3.2-.2h1.4l.8-1.3L12 1.4"/>
    </g>
  </svg>
  <svg x="132">
    <use href="#books"/>
    <g mask="url(#a)" opacity=".844">
      <path fill="context-stroke" stroke="none" d="M11.7 1.4l1.3 3 .6 1.2 1.3.3 3.1.7L15.6 9l-1 1 .2 1.2.3 3.3-2.7-1.6L11 12l-1.3.6L7 14.1l.6-3.3.3-1.3-.8-1L4.9 6l3.2-.3 1.4-.2.7-1.2 1.5-2.8"/>
      <path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M11.7 1.4l1.3 3 .6 1.2 1.3.3 3.1.7L15.6 9l-1 1 .2 1.2.3 3.3-2.7-1.6L11 12l-1.3.6L7 14.1l.6-3.3.3-1.3-.8-1L4.9 6l3.2-.3 1.4-.2.7-1.2 1.5-2.8"/>
    </g>
  </svg>
  <svg x="154">
    <use href="#books"/>
    <g mask="url(#a)" opacity=".957">
      <path fill="context-stroke" stroke="none" d="M11.5 1.4l1.4 2.9.6 1.2 1.3.3 3.1.5-2.3 2.5-.9 1L15 11l.5 3.3-2.8-1.6-1.3-.7-1.3.7-2.8 1.4.6-3.2.2-1.3-.9-1L5 6.1l3-.4 1.3-.2.7-1.3 1.4-2.8"/>
      <path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M11.5 1.4l1.4 2.9.6 1.2 1.3.3 3.1.5-2.3 2.5-.9 1L15 11l.5 3.3-2.8-1.6-1.3-.7-1.3.7-2.8 1.4.6-3.2.2-1.3-.9-1L5 6.1l3-.4 1.3-.2.7-1.3 1.4-2.8"/>
    </g>
  </svg>
  <svg x="176">
    <use href="#books"/>
    <g mask="url(#a)">
      <path fill="context-stroke" stroke="none" d="M11.4 1.4l1.4 2.8.7 1.2 1.3.3 3.1.5-2.3 2.5-.8 1 .2 1.2.5 3.3-2.9-1.5-1.2-.7-1.3.7-2.8 1.5.5-3.3.2-1.3-.9-1L5 6.3l3-.6 1.4-.3.6-1.2 1.4-2.8"/>
      <path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M11.4 1.4l1.4 2.8.7 1.2 1.3.3 3.1.5-2.3 2.5-.8 1 .2 1.2.5 3.3-2.9-1.5-1.2-.7-1.3.7-2.8 1.5.5-3.3.2-1.3-.9-1L5 6.3l3-.6 1.4-.3.6-1.2 1.4-2.8"/>
    </g>
  </svg>
  <svg x="198">
    <use href="#books"/>
    <g mask="url(#a)">
      <path fill="context-stroke" stroke="none" d="M11.3 1.4l1.5 2.9.6 1.2 1.4.2 3.1.5-2.3 2.5-.8 1 .2 1.2.5 3.3-2.8-1.5-1.3-.7-1.3.7-2.7 1.5.5-3.2.1-1.3-.8-1-2.3-2.4L8 5.8l1.4-.3.6-1.3 1.3-2.8"/>
      <path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M11.3 1.4l1.5 2.9.6 1.2 1.4.2 3.1.5-2.3 2.5-.8 1 .2 1.2.5 3.3-2.8-1.5-1.3-.7-1.3.7-2.7 1.5.5-3.2.1-1.3-.8-1-2.3-2.4L8 5.8l1.4-.3.6-1.3 1.3-2.8"/>
    </g>
  </svg>
  <svg x="220">
    <use href="#books"/>
    <g mask="url(#a)">
      <path fill="context-stroke" stroke="none" d="M11.3 1.5l1.5 2.8.6 1.2 1.4.2 3 .5-2.1 2.5-.9 1L15 11l.6 3.2-2.9-1.4-1.3-.7-1.2.7-2.7 1.5.4-3.2.2-1.3-1-1-2.3-2.4L8 6l1.4-.3.5-1.3 1.4-2.8"/>
      <path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M11.3 1.5l1.5 2.8.6 1.2 1.4.2 3 .5-2.1 2.5-.9 1L15 11l.6 3.2-2.9-1.4-1.3-.7-1.2.7-2.7 1.5.4-3.2.2-1.3-1-1-2.3-2.4L8 6l1.4-.3.5-1.3 1.4-2.8"/>
    </g>
  </svg>
  <svg x="242">
    <use href="#books"/>
    <g mask="url(#a)">
      <path fill="context-stroke" stroke="none" d="M11.2 1.6l1.5 2.8.7 1.2 1.3.1 3.2.5-2.2 2.5-.9 1 .3 1.3.6 3.2-3-1.4-1.2-.6-1.3.7-2.7 1.6.4-3.3.2-1.3-1-1-2.3-2.3L8 6l1.3-.3.6-1.3 1.3-2.8"/>
      <path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M11.2 1.6l1.5 2.8.7 1.2 1.3.1 3.2.5-2.2 2.5-.9 1 .3 1.3.6 3.2-3-1.4-1.2-.6-1.3.7-2.7 1.6.4-3.3.2-1.3-1-1-2.3-2.3L8 6l1.3-.3.6-1.3 1.3-2.8"/>
    </g>
  </svg>
  <svg x="264">
    <use href="#books"/>
    <g mask="url(#a)">
      <path fill="context-stroke" stroke="none" d="M11 1.7l1.7 2.7.6 1.2 1.4.2 3.1.4-2.1 2.5-.9 1 .3 1.3.7 3.2-3-1.3-1.2-.6-1.3.7-2.7 1.6.4-3.2V10L7 9 4.7 6.7 7.8 6l1.4-.3.5-1.3L11 1.6"/>
      <path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M11 1.7l1.7 2.7.6 1.2 1.4.2 3.1.4-2.1 2.5-.9 1 .3 1.3.7 3.2-3-1.3-1.2-.6-1.3.7-2.7 1.6.4-3.2V10L7 9 4.7 6.7 7.8 6l1.4-.3.5-1.3L11 1.6"/>
    </g>
  </svg>
  <svg x="286">
    <use href="#books"/>
    <g mask="url(#a)">
      <path fill="context-stroke" stroke="none" d="M11 1.8l1.6 2.7.7 1.2 1.4.1 3.1.4-2.1 2.6-.8 1 .2 1.3.8 3.2-3-1.3-1.3-.6-1.2.7-2.7 1.7.3-3.3.2-1.3-1-.9L4.8 7l3.1-.7L9.3 6l.5-1.3 1.2-3"/>
      <path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M11 1.8l1.6 2.7.7 1.2 1.4.1 3.1.4-2.1 2.6-.8 1 .2 1.3.8 3.2-3-1.3-1.3-.6-1.2.7-2.7 1.7.3-3.3.2-1.3-1-.9L4.8 7l3.1-.7L9.3 6l.5-1.3 1.2-3"/>
    </g>
  </svg>
  <svg x="308">
    <use href="#books"/>
    <g mask="url(#a)">
      <path fill="context-stroke" stroke="none" d="M10.9 1.9l1.7 2.7.7 1.2 1.3.1 3.2.3-2.1 2.6-.8 1 .3 1.3.7 3.2-2.9-1.2-1.3-.6-1.2.8L7.8 15l.3-3.3v-1.3l-.9-.9-2.4-2.3 3-.7L9.3 6l.5-1.3L11 2"/>
      <path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M10.9 1.9l1.7 2.7.7 1.2 1.3.1 3.2.3-2.1 2.6-.8 1 .3 1.3.7 3.2-2.9-1.2-1.3-.6-1.2.8L7.8 15l.3-3.3v-1.3l-.9-.9-2.4-2.3 3-.7L9.3 6l.5-1.3L11 2"/>
    </g>
  </svg>
  <svg x="330">
    <use href="#books"/>
    <g mask="url(#a)">
      <path fill="context-stroke" stroke="none" d="M10.8 2l1.7 2.8.7 1.1 1.4.1 3.1.2-2 2.7-.8 1 .3 1.3.8 3.2-3-1.2-1.2-.6-1.2.8L8 15.2l.1-3.3.1-1.3-1-.9-2.5-2.2 3.1-.8 1.4-.4.5-1.3 1-3"/>
      <path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M10.8 2l1.7 2.8.7 1.1 1.4.1 3.1.2-2 2.7-.8 1 .3 1.3.8 3.2-3-1.2-1.2-.6-1.2.8L8 15.2l.1-3.3.1-1.3-1-.9-2.5-2.2 3.1-.8 1.4-.4.5-1.3 1-3"/>
    </g>
  </svg>
  <svg x="352">
    <use href="#books"/>
    <g mask="url(#a)">
      <path fill="context-stroke" stroke="none" d="M10.7 2.2L12.4 5l.8 1.1 1.3.1 3.2.2-2 2.7-.8 1 .4 1.3.8 3.2-3-1.2-1.3-.5-1.2.8-2.5 1.8.1-3.3v-1.3l-1-.9-2.5-2.1 3.1-1 1.4-.3.4-1.3 1-3"/>
      <path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M10.7 2.2L12.4 5l.8 1.1 1.3.1 3.2.2-2 2.7-.8 1 .4 1.3.8 3.2-3-1.2-1.3-.5-1.2.8-2.5 1.8.1-3.3v-1.3l-1-.9-2.5-2.1 3.1-1 1.4-.3.4-1.3 1-3"/>
    </g>
  </svg>
  <svg x="374">
    <use href="#books"/>
    <g mask="url(#a)">
      <path fill="context-stroke" stroke="none" d="M10.6 2.4l1.8 2.7.7 1 1.4.1 3.1.2L15.7 9l-.7 1 .3 1.3 1 3.2-3-1.1-1.4-.5-1.2.8-2.5 1.8V11l-1-.8-2.5-2.1 3-1 1.4-.4.5-1.3 1-3"/>
      <path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M10.6 2.4l1.8 2.7.7 1 1.4.1 3.1.2L15.7 9l-.7 1 .3 1.3 1 3.2-3-1.1-1.4-.5-1.2.8-2.5 1.8V11l-1-.8-2.5-2.1 3-1 1.4-.4.5-1.3 1-3"/>
    </g>
  </svg>
  <svg x="396">
    <use href="#books"/>
    <g mask="url(#a)">
      <path fill="context-stroke" stroke="none" d="M10.4 2.6l1.9 2.7.8 1 1.3.1h3.2l-1.9 2.8-.7 1.1.4 1.3 1 3.1-3-1-1.4-.5-1.2.8-2.5 2v-4.6l-1-.8-2.6-2.1 3-1L9.2 7l.4-1.4 1-3"/>
      <path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M10.4 2.6l1.9 2.7.8 1 1.3.1h3.2l-1.9 2.8-.7 1.1.4 1.3 1 3.1-3-1-1.4-.5-1.2.8-2.5 2v-4.6l-1-.8-2.6-2.1 3-1L9.2 7l.4-1.4 1-3"/>
    </g>
  </svg>
  <svg x="418">
    <use href="#books"/>
    <g mask="url(#a)">
      <path fill="context-stroke" stroke="none" d="M10.3 2.9l2 2.6.7 1h4.6l-1.9 2.9-.7 1 .4 1.3 1 3.1-3-1-1.4-.4-1.1.9-2.5 1.9v-4.6l-1-.8-2.7-2 3-1.1L9 7.2 9.4 6l1-3"/>
      <path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M10.3 2.9l2 2.6.7 1h4.6l-1.9 2.9-.7 1 .4 1.3 1 3.1-3-1-1.4-.4-1.1.9-2.5 1.9v-4.6l-1-.8-2.7-2 3-1.1L9 7.2 9.4 6l1-3"/>
    </g>
  </svg>
  <svg x="440">
    <use href="#books"/>
    <g mask="url(#a)">
      <path fill="context-stroke" stroke="none" d="M10.2 3.2l2 2.5.8 1.1h4.5l-1.8 2.8-.7 1 .5 1.3 1 3.1-3-1-1.4-.4-1.1 1-2.5 1.9v-4.6L7.3 11 4.6 9l3-1L9 7.4l.4-1.3.8-3"/>
      <path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M10.2 3.2l2 2.5.8 1.1h4.5l-1.8 2.8-.7 1 .5 1.3 1 3.1-3-1-1.4-.4-1.1 1-2.5 1.9v-4.6L7.3 11 4.6 9l3-1L9 7.4l.4-1.3.8-3"/>
    </g>
  </svg>
  <svg x="462">
    <use href="#books"/>
    <g mask="url(#a)">
      <path fill="context-stroke" stroke="none" d="M10.1 3.5l2 2.5.8 1h4.6l-1.8 2.8-.6 1.1.4 1.2 1.1 3.1-3-.9-1.4-.4-1.1 1-2.4 2-.2-3.4v-1.3l-1.1-.7-2.7-2 3-1.1L9 7.9l.3-1.4.8-3"/>
      <path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M10.1 3.5l2 2.5.8 1h4.6l-1.8 2.8-.6 1.1.4 1.2 1.1 3.1-3-.9-1.4-.4-1.1 1-2.4 2-.2-3.4v-1.3l-1.1-.7-2.7-2 3-1.1L9 7.9l.3-1.4.8-3"/>
    </g>
  </svg>
  <svg x="484">
    <use href="#books"/>
    <g mask="url(#a)">
      <path fill="context-stroke" stroke="none" d="M10 3.8l2 2.5.9 1h1.3l3.2-.2-1.7 3-.6 1 .4 1.3 1.2 3-3-.8-1.4-.4-1.1 1-2.4 2-.2-3.3-.1-1.3-1-.7-2.8-2 3-1.1 1.2-.6.4-1.3.7-3"/>
      <path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M10 3.8l2 2.5.9 1h1.3l3.2-.2-1.7 3-.6 1 .4 1.3 1.2 3-3-.8-1.4-.4-1.1 1-2.4 2-.2-3.3-.1-1.3-1-.7-2.8-2 3-1.1 1.2-.6.4-1.3.7-3"/>
    </g>
  </svg>
  <svg x="506">
    <use href="#books"/>
    <g mask="url(#a)">
      <path fill="context-stroke" stroke="none" d="M9.9 4.2l2 2.5 1 1h1.3l3.1-.3-1.6 3-.6 1 .5 1.3 1.2 3-3-.8-1.5-.3-1 1-2.4 2-.3-3.3V13l-1.1-.7-2.8-1.8 3-1.3 1.2-.5.3-1.4.7-3"/>
      <path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M9.9 4.2l2 2.5 1 1h1.3l3.1-.3-1.6 3-.6 1 .5 1.3 1.2 3-3-.8-1.5-.3-1 1-2.4 2-.3-3.3V13l-1.1-.7-2.8-1.8 3-1.3 1.2-.5.3-1.4.7-3"/>
    </g>
  </svg>
  <svg x="528">
    <use href="#books"/>
    <g mask="url(#a)">
      <path fill="context-stroke" stroke="none" d="M9.8 4.7l2 2.4 1 1L14 8l3.2-.3-1.6 3-.6 1.1.5 1.2 1.3 3-3.1-.7-1.4-.3-1 1L9 18l-.3-3.2-.1-1.3-1.1-.7L4.7 11l2.9-1.3L8.9 9 9 7.7l.6-3"/>
      <path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M9.8 4.7l2 2.4 1 1L14 8l3.2-.3-1.6 3-.6 1.1.5 1.2 1.3 3-3.1-.7-1.4-.3-1 1L9 18l-.3-3.2-.1-1.3-1.1-.7L4.7 11l2.9-1.3L8.9 9 9 7.7l.6-3"/>
    </g>
  </svg>
  <svg x="550">
    <use href="#books"/>
    <g mask="url(#a)">
      <path fill="context-stroke" stroke="none" d="M9.6 5.2l2.2 2.3 1 1 1.3-.1 3.1-.3-1.5 3-.6 1.1.5 1.2 1.4 3-3.1-.7-1.4-.3-1 1-2.3 2.2-.4-3.3-.2-1.3-1-.7-3-1.7 3-1.4 1.2-.6.3-1.4.5-3"/>
      <path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M9.6 5.2l2.2 2.3 1 1 1.3-.1 3.1-.3-1.5 3-.6 1.1.5 1.2 1.4 3-3.1-.7-1.4-.3-1 1-2.3 2.2-.4-3.3-.2-1.3-1-.7-3-1.7 3-1.4 1.2-.6.3-1.4.5-3"/>
    </g>
  </svg>
  <svg x="572">
    <use href="#books"/>
    <g mask="url(#a)">
      <path fill="context-stroke" stroke="none" d="M9.5 5.7L11.7 8l1 1H14l3.2-.4-1.5 3-.6 1.2.6 1.1 1.4 3-3.2-.6-1.4-.3-1 1-2.2 2.2-.5-3.2-.1-1.3-1.2-.7-2.8-1.7 2.8-1.4 1.3-.6.2-1.4.5-3"/>
      <path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M9.5 5.7L11.7 8l1 1H14l3.2-.4-1.5 3-.6 1.2.6 1.1 1.4 3-3.2-.6-1.4-.3-1 1-2.2 2.2-.5-3.2-.1-1.3-1.2-.7-2.8-1.7 2.8-1.4 1.3-.6.2-1.4.5-3"/>
    </g>
  </svg>
  <svg x="594">
    <defs>
      <mask id="A" mask-type="alpha">
        <path d="M-29.1-76.9l-15.5 48.7 21.4 12 5.4-.8 3-.3 2.3 4.7 5.5.1.8-2.1h10L13 10.8l4.2-39.2 6-25.2-.8-22.3-51.6-1z" transform="matrix(.5 0 0 .5 13.108 28.313)"/>
      </mask>
    </defs>
    <use href="#books"/>
    <g mask="url(#A)">
      <path fill="context-stroke" stroke="none" d="M9.4 6.3l2.3 2.3 1 1 1.3-.2 3-.4-1.3 3-.6 1.2.6 1.2 1.5 3-3.2-.7-1.4-.2-1 1-2.2 2.3-.5-3.3-.2-1.3-1.1-.6-3-1.7 3-1.4 1.2-.7.2-1.3.4-3.1"/>
      <path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M9.4 6.3l2.3 2.3 1 1 1.3-.2 3-.4-1.3 3-.6 1.2.6 1.2 1.5 3-3.2-.7-1.4-.2-1 1-2.2 2.3-.5-3.3-.2-1.3-1.1-.6-3-1.7 3-1.4 1.2-.7.2-1.3.4-3.1"/>
    </g>
  </svg>
  <svg x="616">
    <defs>
      <mask id="B" mask-type="alpha">
        <path d="M-29.1-76.9l-15.5 48.7 21.4 12 5.4-.8 3-.3 2.3 4.7 5.5.1.8-2.1h10L13 10.8l4.2-39.2 6-25.2-.8-22.3-51.6-1z" transform="matrix(.5 0 0 .5 13.108 28.313)"/>
      </mask>
    </defs>
    <use href="#books"/>
    <g mask="url(#B)">
      <path fill="context-stroke" stroke="none" d="M9.3 7l2.3 2.3 1 1 1.3-.2 3.1-.5-1.3 3-.6 1.2.6 1.2 1.6 2.9-3.2-.5-1.4-.3-1 1.1-2.2 2.3-.5-3.2-.2-1.3-1.2-.7-3-1.6 2.9-1.5 1.2-.6.2-1.4.4-3.1"/>
      <path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M9.3 7l2.3 2.3 1 1 1.3-.2 3.1-.5-1.3 3-.6 1.2.6 1.2 1.6 2.9-3.2-.5-1.4-.3-1 1.1-2.2 2.3-.5-3.2-.2-1.3-1.2-.7-3-1.6 2.9-1.5 1.2-.6.2-1.4.4-3.1"/>
    </g>
  </svg>
  <svg x="638">
    <defs>
      <mask id="C" mask-type="alpha">
        <path d="M-29.1-76.9l-15.5 48.7 21.4 12 5.4-.8 3-.3 2.3 4.7 5.5.1.8-2.1h10L13 10.8l4.2-39.2 6-25.2-.8-22.3-51.6-1z" transform="matrix(.5166 0 0 .5 13.263 28.454)"/>
      </mask>
    </defs>
    <path d="M8 22.1a1 1 0 0 0-1 1v10c0 .6.4 1 1 1s1-.4 1-1v-10c0-.5-.4-1-1-1zm3-1a1 1 0 0 0-1 1v11c0 .6.5 1 1 1 .7 0 1.1-.4 1.1-1v-11c0-.5-.4-1-1-1zm8.3 11.7l-4.1-11a1 1 0 0 0-1.4-.6 1 1 0 0 0-.6 1.2l4.1 11c.2.6.8.9 1.4.7.5-.2.8-.7.6-1.3zM4.9 20.1a1 1 0 0 0-1 1v12c0 .6.4 1 1 1s1-.4 1-1v-12c0-.5-.4-1-1-1z"/>
    <g mask="url(#C)">
      <path fill="context-stroke" stroke="none" d="M9.2 8l2.3 2.1 1 1 1.3-.3 3.1-.5-1.3 3-.5 1.2.7 1.2 1.5 2.9-3.1-.5-1.5-.2-1 1-2 2.4-.7-3.2-.2-1.3-1.2-.6-3-1.5 3-1.7 1.2-.7.1-1.4.4-3"/>
      <path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M9.2 8l2.3 2.1 1 1 1.3-.3 3.1-.5-1.3 3-.5 1.2.7 1.2 1.5 2.9-3.1-.5-1.5-.2-1 1-2 2.4-.7-3.2-.2-1.3-1.2-.6-3-1.5 3-1.7 1.2-.7.1-1.4.4-3"/>
    </g>
  </svg>
  <svg x="660">
    <defs>
      <mask id="D" mask-type="alpha">
        <path d="M-29.1-76.9l-15.5 48.7 21.4 12 5.4-.8 3-.3 2.3 4.7 5.5.1.8-2.1h10L13 10.8l4.2-39.2 6-25.2-.8-22.3-51.6-1z" transform="matrix(.5332 0 0 .5 13.482 28.845)"/>
      </mask>
    </defs>
    <path d="M8 22.5a1 1 0 0 0-1 1v10c0 .6.4 1 1 1s1.1-.4 1.1-1v-10c0-.5-.5-1-1-1zm3.2-1a1 1 0 0 0-1 1v11c0 .6.4 1 1 1s1.1-.4 1.1-1v-11c0-.5-.5-1-1-1zm8.5 11.7l-4.3-11a1 1 0 0 0-1.3-.6 1 1 0 0 0-.7 1.2l4.3 11c.2.6.8.9 1.4.7.5-.2.8-.8.6-1.3zM4.8 20.5a1 1 0 0 0-1 1v12c0 .6.4 1 1 1s1.1-.4 1.1-1v-12c0-.5-.5-1-1-1z"/>
    <g mask="url(#D)">
      <path fill="context-stroke" stroke="none" d="M9 8.9l2.4 2.2 1 .9 1.4-.3 3-.6-1.2 3.1-.5 1.2.7 1.2 1.6 2.8-3.2-.4-1.4-.2-1 1.1-2 2.4L9 19l-.3-1.3-1.1-.6-3-1.5L7.3 14l1.3-.7V12l.6-3"/>
      <path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M9 8.9l2.4 2.2 1 .9 1.4-.3 3-.6-1.2 3.1-.5 1.2.7 1.2 1.6 2.8-3.2-.4-1.4-.2-1 1.1-2 2.4L9 19l-.3-1.3-1.1-.6-3-1.5L7.3 14l1.3-.7V12l.6-3"/>
    </g>
  </svg>
  <svg x="682">
    <defs>
      <mask id="E" mask-type="alpha">
        <path d="M-29.1-76.9l-15.5 48.7 21.4 12 5.4-.8 3-.3 2.3 4.7 5.5.1.8-2.1h10L13 10.8l4.2-39.2 6-25.2-.8-22.3-51.6-1z" transform="matrix(.54974 0 0 .5 13.727 29.36)"/>
      </mask>
    </defs>
    <path d="M8.1 23a1 1 0 0 0-1 1v10c0 .6.4 1 1 1s1.1-.4 1.1-1V24c0-.5-.5-1-1-1zm3.3-1a1 1 0 0 0-1 1v11c0 .6.4 1 1 1s1.1-.4 1.1-1V23c0-.5-.5-1-1-1zm8.7 11.7l-4.4-11c-.2-.5-.8-.8-1.4-.6a1 1 0 0 0-.6 1.3l4.4 11c.2.5.8.8 1.4.6.5-.2.8-.7.7-1.3zM4.8 21a1 1 0 0 0-1 1v12c0 .6.4 1 1 1S6 34.6 6 34V22c0-.5-.5-1-1-1z"/>
    <g mask="url(#E)">
      <path fill="context-stroke" stroke="none" d="M9 10l2.4 2.2 1 .9 1.3-.3 3.1-.7-1.2 3.2-.5 1.2.7 1.1 1.7 2.8-3.2-.3-1.5-.2L12 21l-2 2.4-.7-3.2L9 19l-1.2-.6-3-1.4 2.7-1.7 1.2-.7.1-1.4L9 10"/>
      <path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M9 10l2.4 2.2 1 .9 1.3-.3 3.1-.7-1.2 3.2-.5 1.2.7 1.1 1.7 2.8-3.2-.3-1.5-.2L12 21l-2 2.4-.7-3.2L9 19l-1.2-.6-3-1.4 2.7-1.7 1.2-.7.1-1.4L9 10"/>
    </g>
  </svg>
  <svg x="704">
    <defs>
      <mask id="F" mask-type="alpha">
        <path d="M-29.1-76.9l-15.5 48.7 21.4 12 5.4-.8 3-.3 2.3 4.7 5.5.1.8-2.1h10L13 10.8l4.2-39.2 6-25.2-.8-22.3-51.6-1z" transform="matrix(.56608 0 0 .5 13.962 29.878)"/>
      </mask>
    </defs>
    <path d="M8.2 23.6c-.6 0-1.2.4-1.2 1v10c0 .5.6 1 1.2 1s1.1-.5 1.1-1v-10c0-.6-.5-1-1.1-1zm3.4-1c-.6 0-1.2.4-1.2 1v11c0 .5.6 1 1.2 1s1.1-.5 1.1-1v-11c0-.6-.5-1-1.1-1zm9 11.6l-4.6-11c-.2-.5-.8-.8-1.4-.6-.6.2-1 .7-.7 1.3l4.5 11c.2.5.9.8 1.5.6.6-.1.9-.7.7-1.2zM4.8 21.6c-.6 0-1.1.4-1.1 1v12c0 .5.5 1 1 1 .7 0 1.2-.5 1.2-1v-12c0-.6-.5-1-1.1-1z"/>
    <g mask="url(#F)">
      <path fill="context-stroke" stroke="none" d="M8.8 11.5l2.5 2 1 1 1.3-.4 3.1-.7-1.1 3.2-.5 1.2.7 1.1 1.7 2.8-3.2-.3-1.4-.1-1 1.1-1.9 2.4-.8-3.1-.3-1.3-1.2-.6-3-1.3 2.7-1.8 1.2-.7v-1.4l.2-3.1"/>
      <path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M8.8 11.5l2.5 2 1 1 1.3-.4 3.1-.7-1.1 3.2-.5 1.2.7 1.1 1.7 2.8-3.2-.3-1.4-.1-1 1.1-1.9 2.4-.8-3.1-.3-1.3-1.2-.6-3-1.3 2.7-1.8 1.2-.7v-1.4l.2-3.1"/>
    </g>
  </svg>
  <svg x="726">
    <defs>
      <mask id="G" mask-type="alpha">
        <path d="M-29.1-76.9l-15.5 48.7 21.4 12 5.4-.8 3-.3 2.3 4.7 5.5.1.8-2.1h10L13 10.8l4.2-39.2 6-25.2-.8-22.3-51.6-1z" transform="matrix(.58198 0 0 .5 14.154 30.278)"/>
      </mask>
    </defs>
    <path d="M8.2 24c-.6 0-1.2.4-1.2 1v10c0 .5.6 1 1.2 1 .7 0 1.2-.5 1.2-1V25c0-.6-.5-1-1.2-1zm3.5-1c-.6 0-1.2.4-1.2 1v11c0 .5.6 1 1.2 1s1.2-.5 1.2-1V24c0-.6-.6-1-1.2-1zm9.2 11.6l-4.6-11c-.2-.5-.9-.8-1.5-.6-.6.2-1 .7-.7 1.3l4.7 11c.2.5.8.8 1.4.6.6-.1 1-.7.8-1.2zM4.7 22c-.6 0-1.1.4-1.1 1v12c0 .5.5 1 1.1 1 .7 0 1.2-.5 1.2-1V23c0-.6-.5-1-1.2-1z"/>
    <g mask="url(#G)">
      <path fill="context-stroke" stroke="none" d="M8.7 13.3l2.5 2 1 .9 1.4-.3 3-.8-1 3.2-.5 1.2.7 1.1 1.8 2.8-3.2-.2-1.5-.2-.8 1.2-2 2.4-.8-3.1-.4-1.3-1.1-.5-3.1-1.4 2.7-1.7 1.1-.8v-1.4l.2-3.1"/>
      <path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M8.7 13.3l2.5 2 1 .9 1.4-.3 3-.8-1 3.2-.5 1.2.7 1.1 1.8 2.8-3.2-.2-1.5-.2-.8 1.2-2 2.4-.8-3.1-.4-1.3-1.1-.5-3.1-1.4 2.7-1.7 1.1-.8v-1.4l.2-3.1"/>
    </g>
  </svg>
  <svg x="748">
    <defs>
      <mask id="H" mask-type="alpha">
        <path d="M-29.1-76.9l-15.5 48.7 21.4 12 5.4-.8 3-.3 2.3 4.7 5.5.1.8-2.1h10L13 10.8l4.2-39.2 6-25.2-.8-22.3-51.6-1z" transform="matrix(.5969 0 0 .5 14.267 30.438)"/>
      </mask>
    </defs>
    <path d="M8.2 24.1c-.7 0-1.2.5-1.2 1v10c0 .6.5 1 1.2 1 .6 0 1.2-.4 1.2-1v-10c0-.5-.6-1-1.2-1zm3.6-1c-.7 0-1.2.5-1.2 1v11c0 .6.5 1 1.2 1 .6 0 1.1-.4 1.1-1v-11c0-.5-.5-1-1.2-1zm9.4 11.7l-4.7-11c-.3-.5-1-.8-1.6-.6-.6.1-1 .7-.7 1.2l4.8 11c.2.6.9.9 1.5.7.6-.2 1-.8.7-1.3zM4.6 22c-.7 0-1.2.5-1.2 1v12c0 .6.5 1 1.2 1 .6 0 1.2-.4 1.2-1V23c0-.5-.6-1-1.2-1z"/>
    <g mask="url(#H)">
      <path fill="context-stroke" stroke="none" d="M8.6 15.7l2.5 2 1 .9 1.4-.4 3-.8-1 3.2-.4 1.2.7 1.1 1.8 2.8-3.2-.2-1.4-.1-.9 1.1-1.9 2.5-.9-3.1-.3-1.3-1.2-.5-3.1-1.3L7.3 21l1.2-.8v-4.4"/>
      <path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M8.6 15.7l2.5 2 1 .9 1.4-.4 3-.8-1 3.2-.4 1.2.7 1.1 1.8 2.8-3.2-.2-1.4-.1-.9 1.1-1.9 2.5-.9-3.1-.3-1.3-1.2-.5-3.1-1.3L7.3 21l1.2-.8v-4.4"/>
    </g>
  </svg>
  <svg x="770">
    <defs>
      <mask id="I" mask-type="alpha">
        <path d="M-29.1-76.9l-15.5 48.7 21.4 12 5.4-.8 3-.3 2.3 4.7 5.5.1.8-2.1h10L13 10.8l4.2-39.2 6-25.2-.8-22.3-51.6-1z" transform="matrix(.60979 0 0 .5 14.335 30.217)"/>
      </mask>
    </defs>
    <path d="M8.1 23.9c-.7 0-1.2.5-1.2 1v10c0 .6.5 1 1.2 1s1.2-.4 1.2-1v-10c0-.5-.5-1-1.2-1zm3.7-1c-.7 0-1.3.5-1.3 1v11c0 .6.6 1 1.3 1 .6 0 1.2-.4 1.2-1v-11c0-.5-.6-1-1.2-1zm9.6 11.7l-4.8-11c-.3-.6-1-.8-1.6-.7-.6.2-1 .8-.7 1.3l4.9 11c.2.6.9.8 1.5.7.6-.2 1-.8.8-1.3zm-17-12.7c-.6 0-1.2.5-1.2 1v12c0 .6.6 1 1.2 1 .7 0 1.3-.4 1.3-1v-12c0-.5-.6-1-1.3-1z"/>
    <g mask="url(#I)">
      <path fill="context-stroke" stroke="none" d="M8.5 18.8l2.5 2 1 .8 1.4-.4 3-.8-1 3.2-.3 1.2.7 1 1.9 2.8-3.2-.1H13l-.8 1-1.9 2.6-1-3.1-.3-1.3-1.2-.5L4.7 26l2.6-2 1.1-.8v-4.5"/>
      <path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M8.5 18.8l2.5 2 1 .8 1.4-.4 3-.8-1 3.2-.3 1.2.7 1 1.9 2.8-3.2-.1H13l-.8 1-1.9 2.6-1-3.1-.3-1.3-1.2-.5L4.7 26l2.6-2 1.1-.8v-4.5"/>
    </g>
  </svg>
  <svg x="792">
    <defs>
      <mask id="J" mask-type="alpha">
        <path d="M-29.1-76.9l-15.5 48.7 21.4 12 5.4-.8 3-.3 2.3 4.7 5.5.1.8-2.1h10L13 10.8l4.2-39.2 6-25.2-.8-22.3-51.6-1z" transform="matrix(.61897 0 0 .5 14.4 29.689)"/>
      </mask>
    </defs>
    <path d="M8 23.4c-.6 0-1.2.4-1.2 1v10c0 .5.6 1 1.3 1 .7 0 1.2-.5 1.2-1v-10c0-.6-.5-1-1.2-1zm3.8-1c-.7 0-1.2.4-1.2 1v11c0 .5.5 1 1.2 1s1.2-.5 1.2-1v-11c0-.6-.5-1-1.2-1zM21.6 34l-5-11c-.2-.5-.9-.8-1.5-.6-.7.2-1 .8-.8 1.3l5 11c.2.5 1 .8 1.6.6.6-.1 1-.7.7-1.2zM4.4 21.4c-.7 0-1.3.4-1.3 1v12c0 .5.6 1 1.3 1 .6 0 1.2-.5 1.2-1v-12c0-.6-.6-1-1.2-1z"/>
    <g mask="url(#J)">
      <path fill="context-stroke" stroke="none" d="M8.4 21.7l2.5 2 1.1.8 1.3-.4 3-1-.9 3.3-.4 1.2.8 1.1 1.9 2.7h-3.2l-1.5-.1-.8 1.2-1.8 2.5-1-3.1-.4-1.3-1.2-.4L4.7 29l2.6-2 1-.7v-4.6"/>
      <path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M8.4 21.7l2.5 2 1.1.8 1.3-.4 3-1-.9 3.3-.4 1.2.8 1.1 1.9 2.7h-3.2l-1.5-.1-.8 1.2-1.8 2.5-1-3.1-.4-1.3-1.2-.4L4.7 29l2.6-2 1-.7v-4.6"/>
    </g>
  </svg>
  <svg x="814">
    <defs>
      <mask id="K" mask-type="alpha">
        <path d="M-29.1-76.9l-15.5 48.7 21.4 12 5.4-.8 3-.3 2.3 4.7 5.5.1.8-2.1h10L13 10.8l4.2-39.2 6-25.2-.8-22.3-51.6-1z" transform="matrix(.62351 0 0 .5 14.434 29.059)"/>
      </mask>
    </defs>
    <path d="M8 22.7c-.6 0-1.2.5-1.2 1v10c0 .6.6 1 1.3 1 .7 0 1.2-.4 1.2-1v-10c0-.5-.5-1-1.2-1zm3.8-1c-.7 0-1.2.5-1.2 1v11c0 .6.5 1 1.2 1s1.3-.4 1.3-1v-11c0-.5-.6-1-1.3-1zm9.9 11.7l-5-11c-.2-.5-1-.8-1.6-.6-.6.2-1 .7-.7 1.3l5 11c.2.5.9.8 1.5.6.7-.2 1-.7.8-1.3zM4.3 20.7c-.7 0-1.2.5-1.2 1v12c0 .6.5 1 1.2 1s1.3-.4 1.3-1v-12c0-.5-.6-1-1.3-1z"/>
    <g mask="url(#K)">
      <path fill="context-stroke" stroke="none" d="M8.2 23.6l2.7 1.9 1 .8 1.3-.4 3-1-.8 3.3-.4 1.2.8 1 2 2.7H13l-.8 1.2-1.8 2.5-1-3-.5-1.3-1.2-.5-3-1 2.5-2 1.2-.8v-1.4l-.2-3"/>
      <path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M8.2 23.6l2.7 1.9 1 .8 1.3-.4 3-1-.8 3.3-.4 1.2.8 1 2 2.7H13l-.8 1.2-1.8 2.5-1-3-.5-1.3-1.2-.5-3-1 2.5-2 1.2-.8v-1.4l-.2-3"/>
    </g>
  </svg>
  <svg x="836">
    <defs>
      <mask id="L" mask-type="alpha">
        <path d="M-29.1-76.9l-15.5 48.7 21.4 12 5.4-.8 3-.3 2.3 4.7 5.5.1.8-2.1h10L13 10.8l4.2-39.2 6-25.2-.8-22.3-51.6-1z" transform="matrix(.62484 0 0 .5 14.423 28.531)"/>
      </mask>
    </defs>
    <path d="M8 22.2c-.7 0-1.2.5-1.2 1v10c0 .6.5 1 1.2 1s1.3-.4 1.3-1v-10c0-.5-.6-1-1.3-1zm3.8-1c-.7 0-1.3.5-1.3 1v11c0 .6.6 1 1.3 1s1.2-.4 1.2-1v-11c0-.5-.5-1-1.2-1zm10 11.7l-5-11c-.3-.5-1-.8-1.7-.6-.6.1-1 .7-.8 1.2l5 11c.3.6 1 .9 1.6.7.7-.2 1-.8.8-1.3zM4.2 20.2c-.7 0-1.3.5-1.3 1v12c0 .6.6 1 1.3 1s1.2-.4 1.2-1v-12c0-.5-.5-1-1.2-1z"/>
  </svg>
  <svg x="858">
    <path d="M8 22c-.7 0-1.3.4-1.3 1v10c0 .6.6 1 1.3 1s1.3-.4 1.3-1V23c0-.6-.6-1-1.3-1zm3.8-1c-.7 0-1.3.4-1.3 1v11c0 .6.6 1 1.3 1s1.2-.4 1.2-1V22c0-.6-.6-1-1.3-1zm9.9 11.7l-5-11c-.2-.6-1-.8-1.6-.7-.7.2-1 .8-.8 1.3l5 11c.2.6 1 .8 1.6.7.7-.2 1-.8.8-1.3zM4.3 20c-.7 0-1.3.4-1.3 1v12c0 .6.6 1 1.3 1s1.2-.4 1.2-1V21c0-.6-.6-1-1.3-1z"/>
  </svg>
  <svg x="880">
    <path d="M8 22c-.6 0-1.2.4-1.2 1v10c0 .5.6 1 1.3 1 .6 0 1.2-.5 1.2-1V23c0-.6-.6-1-1.2-1zm3.7-1c-.7 0-1.2.4-1.2 1v11c0 .5.5 1 1.2 1s1.2-.5 1.2-1V22c0-.6-.5-1-1.2-1zm9.7 11.6l-4.9-11c-.2-.5-.9-.8-1.5-.6-.7.2-1 .7-.8 1.2l5 11c.1.6.8.9 1.5.7.6-.2 1-.7.7-1.3zm-17-12.7c-.7 0-1.2.5-1.2 1v12c0 .6.5 1 1.2 1s1.2-.4 1.2-1V21c0-.5-.5-1-1.2-1z"/>
  </svg>
  <svg x="902">
    <path d="M8 21.9c-.6 0-1.1.4-1.1 1v10c0 .5.5 1 1.2 1 .6 0 1.1-.5 1.1-1v-10c0-.6-.5-1-1.1-1zm3.6-1c-.6 0-1.2.4-1.2 1v11c0 .5.6 1 1.2 1 .7 0 1.2-.5 1.2-1v-11c0-.6-.5-1-1.2-1zm9.5 11.6l-4.8-11c-.2-.5-.9-.8-1.5-.6-.6.2-1 .8-.7 1.3l4.8 11c.2.5.9.8 1.5.6.6-.1 1-.7.7-1.2zM4.5 20c-.7 0-1.2.4-1.2 1v12c0 .5.5 1 1.2 1 .6 0 1.2-.5 1.2-1V21c0-.6-.6-1-1.2-1z"/>
  </svg>
  <svg x="924">
    <path d="M8 21.8c-.6 0-1.2.5-1.2 1v10c0 .6.6 1 1.2 1 .7 0 1.2-.4 1.2-1v-10c0-.5-.5-1-1.2-1zm3.5-1c-.6 0-1.2.5-1.2 1v11c0 .6.6 1 1.2 1 .7 0 1.2-.4 1.2-1v-11c0-.5-.5-1-1.2-1zm9.3 11.7l-4.7-11c-.2-.5-.9-.8-1.5-.6-.6.2-1 .7-.7 1.2l4.7 11c.2.6.9.9 1.5.7.6-.2.9-.7.7-1.3zM4.5 19.8c-.6 0-1.2.5-1.2 1v12c0 .6.6 1 1.2 1 .7 0 1.2-.4 1.2-1v-12c0-.5-.5-1-1.2-1z"/>
  </svg>
  <svg x="946">
    <path d="M8 21.9c-.7 0-1.2.4-1.2 1v10c0 .5.5 1 1.2 1s1.1-.5 1.1-1v-10c0-.6-.5-1-1.1-1zm3.4-1c-.6 0-1.2.4-1.2 1v11c0 .5.6 1 1.2 1s1.1-.5 1.1-1v-11c0-.6-.5-1-1.1-1zm9 11.6l-4.5-11c-.2-.5-.9-.8-1.5-.6-.6.2-.9.7-.7 1.3l4.6 11c.2.5.8.8 1.4.6.6-.2 1-.7.7-1.3zM4.6 20c-.7 0-1.2.4-1.2 1v12c0 .5.5 1 1.2 1 .6 0 1-.5 1-1V21c0-.6-.4-1-1-1z"/>
  </svg>
  <svg x="968">
    <path d="M8 21.9c-.7 0-1.2.4-1.2 1v10c0 .5.5 1 1.1 1 .6 0 1.1-.5 1.1-1v-10c0-.6-.5-1-1-1zm3.3-1c-.6 0-1.1.4-1.1 1v11c0 .5.5 1 1 1 .7 0 1.2-.5 1.2-1v-11c0-.6-.5-1-1.1-1zM20 32.5l-4.5-11c-.2-.5-.8-.8-1.4-.6-.5.2-.9.8-.7 1.3l4.5 11c.2.5.8.8 1.4.6.6-.1.9-.7.7-1.2zM4.6 20c-.6 0-1.1.4-1.1 1v12c0 .5.5 1 1.1 1 .6 0 1.1-.5 1.1-1V21c0-.6-.5-1-1.1-1z"/>
  </svg>
  <svg x="990">
    <path d="M8 22c-.7 0-1.2.4-1.2 1v10c0 .5.5 1 1.1 1S9 33.4 9 33V23c0-.6-.5-1-1-1zm3.2-1c-.6 0-1.1.4-1.1 1v11c0 .5.5 1 1 1s1.1-.5 1.1-1V22c0-.6-.4-1-1-1zm8.6 11.6l-4.4-11c-.2-.5-.8-.8-1.3-.6-.6.1-1 .7-.7 1.2l4.3 11c.2.6.8.9 1.4.7.6-.2.9-.8.7-1.3zM4.7 19.9c-.6 0-1.1.5-1.1 1v12c0 .6.5 1 1 1s1.1-.4 1.1-1V21c0-.5-.4-1-1-1z"/>
  </svg>
  <svg x="1012">
    <path d="M8 22a1 1 0 0 0-1.1 1v10c0 .5.4 1 1 1s1-.5 1-1V23c0-.6-.4-1-1-1zm3-1a1 1 0 0 0-1 1v11c0 .5.5 1 1 1 .7 0 1.1-.5 1.1-1V22c0-.6-.4-1-1-1zm8.5 11.6l-4.3-11A1 1 0 0 0 14 21a1 1 0 0 0-.7 1.3l4.3 11c.2.5.8.8 1.3.6.6-.2.9-.7.7-1.2zM4.7 20a1 1 0 0 0-1 1v12c0 .5.5 1 1 1 .6 0 1.1-.5 1.1-1V21c0-.6-.5-1-1-1z"/>
  </svg>
  <svg x="1034">
    <path d="M8 22a1 1 0 0 0-1 1v10c0 .5.4 1 1 1 .5 0 1-.5 1-1V23c0-.6-.5-1-1-1zm3-1a1 1 0 0 0-1 1v11c0 .5.5 1 1 1 .6 0 1-.5 1-1V22c0-.6-.4-1-1-1zm8.2 11.6l-4.1-11a1 1 0 0 0-1.3-.6 1 1 0 0 0-.7 1.3l4.2 11c.1.6.7.8 1.3.7.5-.2.8-.8.6-1.3zM4.9 20a1 1 0 0 0-1 1v12c0 .5.4 1 1 1 .5 0 1-.5 1-1V21c0-.6-.5-1-1-1z"/>
  </svg>
  <svg x="1056">
    <use href="#books"/>
  </svg>
  <svg x="1078">
    <use href="#books"/>
  </svg>
</svg>
PK
!<+Z&��,chrome/browser/skin/classic/browser/link.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16" fill="context-fill" fill-opacity="context-fill-opacity">
  <rect x="7" y="3.286" width="2" height="9.429" rx="1" ry="1" transform="rotate(-45 8 8)"/>
  <path d="M2.354 4.522L4.485 2.39a.5.5 0 0 1 .711 0l3.19 3.19.014-.015a2 2 0 0 0 0-2.821L6.272.616a2 2 0 0 0-2.821 0L.616 3.451a2 2 0 0 0 0 2.821L2.744 8.4a1.993 1.993 0 0 0 2.8.02l-3.19-3.186a.5.5 0 0 1 0-.712z"/>
  <path d="M15.416 9.759L13.287 7.63a2 2 0 0 0-2.821 0l-.015.015 3.189 3.189a.5.5 0 0 1 0 .711l-2.132 2.132a.5.5 0 0 1-.711 0L7.61 10.49a1.993 1.993 0 0 0 .02 2.8l2.128 2.128a2 2 0 0 0 2.821 0l2.835-2.835a2 2 0 0 0 .002-2.824z"/>
</svg>
PK
!<5�����,chrome/browser/skin/classic/browser/mail.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16" fill="context-fill" fill-opacity="context-fill-opacity">
  <path d="M13 2H3a3.013 3.013 0 0 0-3 3v6a3.013 3.013 0 0 0 3 3h10a3.013 3.013 0 0 0 3-3V5a3.013 3.013 0 0 0-3-3zm1 9a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1z"/>
  <path d="M8 9a.5.5 0 0 1-.294-.1l-5.5-4a.5.5 0 1 1 .588-.8L8 7.882 13.207 4.1a.5.5 0 0 1 .588.809l-5.5 4A.5.5 0 0 1 8 9z"/>
</svg>
PK
!<)����3chrome/browser/skin/classic/browser/menu-badged.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M3,4H7.755A8,8,0,0,1,6.584,2H3A1,1,0,0,0,3,4ZM13,7H3A1,1,0,0,0,3,9H13a1,1,0,0,0,0-2Zm0,5H3a1,1,0,0,0,0,2H13a1,1,0,0,0,0-2Z"/>
</svg>
PK
!<+jYY/chrome/browser/skin/classic/browser/monitor.png�PNG


IHDR��D�Z}gAMA���aIDATxԖ�3+I��1r���,_/Ky-����\q�3X٤�Z���f�Y�n���V���w���nG'��3�֤�����i�&V�-�Zkl4c�W��n�K�[)�X��<���Q�k�n�����޾	���G�0�>t��";�c�7Hb���1leǎ��Z�e�j��+��T��3�l�`F��V�]��>j�6;��Uȑ�^�u����ć������7���.�������ׯ� ���p�5�	���w��r���;������4�<��.�K���ܼwh��G=�M���^��H�B(�}*���g�
)���N�5��@b^�4�,�lc�����o������R�P��|��h"�bS�3P���|�'��Ӵ�7LV�Y�H�1>#�sz� ⃪��-����㱖z��3����	D>��_���*$�1��c��\�!.�9MYH���T������kp�y��C��j*f�N
�X���>==��.�f�%Pva �
�C�9�4����?�]����؁?�B�Yk.�Gqu5�z�;z����� ;}�Cb����C��,D�g��
���|��o��9O|.~t@@�O̩���o�%y��H�w��n�u����N�?n�@C���mC�c�kς���qv�\��&Iu��$u�&y��>YD%���Հ�q�=0
�}��Rw��э��`I��X�"K�%���=��&���.�n�����6\B��u2H�8��5��8D���5�@?�gJ�[������wm{s��]�k����[�����>���6��-��i�mn �e�����Z\8�K����z��ISd�d�=7��\��]�k������eO�w�
��O������~A	г���\�G�(���S똑.`�1v<vQ��RD��H��6�x�~DD��i�ѳ�%�m��X�(-
p�My�����5�[1�=[;��b_t�fx�9��Vk��{��l䛛ג����]��olu�d�ZL,~~��߃�1߭9Mz�
��Jzn<GJ���(����k�Ne
�n�$��
�O��x������W�5��V��D�B�}��-k��������+Qs�
h���AO�G��j;���ԍK�r�4���Q��>�����v&���f����~�9~�<_9�hǶ�@�RЉl������R���G�����fw�>���@����_|��V�����{1�T��z��]�n�n�j�fss~}x�@�/��:�gU�
woj��s�@����µQ�~op�E���p]{C�����0�X�`a`�1_+d�v�4l���\��4�0>�[�I?��[���	%�;���f(���^L8qȂl�6�(b6K�
a��r�mi�
�3ٌ�T'���5�L�oZſ��9�ٻ�a ����?sAC
�ΰ��f�i�j#���;3靏4� �z�^hA���B���ƃ��惾���KܨP����_I�_�}�3/:/輠�΋�:/�輠��:/:/輠��gu^/la�ʐ�@*��2�D��H���:/輠���:/:/輠�΋�:/�輠��:/:/輠����� ��T�TRY�2�	V"�	:/輠���:/:/輠�B�O
p�	��a�&l`†	����:/:/輠��~:/輠���:/輸D\"RRHe ��T��H�	V"�yA��t^t^�yA�E�u����Y�ƚq�?�a���y��K�s.�k.�S�.�s�y���w�0��O�O*���yqޔT��j�wuu��'��������~��0U�av��[���C�H�<���.�<�a��#\�^O�C:qe}�	�pn�|$\�y;1�_i_T�;����['m���e��L�X�ܘ��4΂+�Cy���v�j��n�M��i�v�;�n��I�kQ�o<d't�
e��Z�z�C��H[#�X`�j�`D��Q%}%�^ڏ4��euu
I���G:ej g��`�����"���l������N�Qg�@�'���o7�~��^0�B�G&���{�s��\6�u0�][^.kK+e=�o��>����aD_%c@�L$��CJ֩��A�Bi��e��`�-O�l�KL�Â	p���m3�n��z�^�ņ������~��Nw����Ɣ�
p�G�On�*؃�m?��
�z*Ĵ�6$}A�mI�IʘF��� �Kw|�OM��ɱ2u��?�R�!�����O�m�]���;��[�I��ْ�n�af�	g�Ւj�\�r'=�a
�``Q�B�wS�7XP�p��ڞ��P���i�
�I��P�|�M:��z�sE�<7�M�t ��w�ľ�p�Mz������n���KN(GQ�������s�-ݱ��o�R�w�|�/VSr�z��Zʐς����|3����>�BrM\�#܋eHW���F)��Q�Z4Ǘ\�Y�d��WK)�����{�m4��9qN&�<F�ؤ��y��~�.�kR�|*�o;M��m�;:R�'�ʩ�<�q�{��`-����ҍt�݈�� �
��3� �G�q&�t'P [_���/=N�#L�z��^������rP^KH{ź.��2Mk��n=�s�Cy�y�J���K��A����QWf�/q�e�[�V#C��8��n����׽�.S'�9~Ƒe�񰼇��{��Q����>����-��C[��Nc ���H�I.��S_
�I)�]7�:���
q'���Ҳ�M�!,n���I�����__����_"eݖ���!N��2,��˜2~N��&��q^-�	3NJ��y�,�.}��d���S�,�����/����r��.g}��r��)���ҼF63��m�m�k��ژq_��G8�ZZ��"x�g�Ёd����/
��l֭��
�|�D���,���]��l��W��:/걎@�6�&�<��t�OMX�X�J��<�j=6a��@���3�c��k}�ŋ���se��O���sS��g����wsy�_O��م�p7��ӆ1LNN��Y�8)r�8��3�g&o���@�l���}1�Hw�X��7J.��G��~�j?	e���s�����,�����`�-IY]�4ùO]ZV�(�ܞ
W�~z*��Ibus��t�wu�Ic.81F�KÓ"�)2��lea���D8�����qt��|�/�=�li�055���m����x
r�d-'P����p�������u�ظ����-��s�\�;�DI�k�G�7=a/�E�2.����g1���pI�h�[���N_"j{�9tGo0�����HYbM:å��I�����|x�6��#��P
LHPd��i��‹V�[���Z ���9����P�����8�4�z;H}�[��	��2ڤ�>�Ы��k9uI]�4t��
t���@̵t��\��~�"Ww�E��8d��i�n�3��~~��Nj#��/�j�A���Ö�9,�Æ���5�Og�ڒg	�
��d������[4��%��궮��|�_&^����J.���x�-�y�AM9��9�裿��b7���G=)������D����I��~�;�
�;�
��3O��Nj���MM�H�Q�gw�qW�~������~8���Lx�7��@>W�y�c1��1&��+c�߾�^��'�d9�����M��#xD�k�f2���t�|��'�����
⯡'�|2���I*7�<����R$�i��	�\���H��_①�{"xeޝ�Ω��ze�����K��\�['Ǣ���X3d�&��sss�,b�P<�exxxOߞ����R|��h�������W^y�_xS�i�.��1��"��c��AM��K!��
�_}�ۤ?�6;��s���6��:^޴�r܀D�[�

9�,8���.��RN��_z�o�x�?�w�t�QG}k~~~��p�)�}�H ��M/b>�n�&H��<L\��6�V.���:J�#S.3::Z��(�`�I�7*̕�����J�fS2n%yG�E�V��w���l8"቉	��XH?��J�?�t/��i),/��S���W��=7	�Ԁ�r��B�}����LAJ���;vXrl�W�1���kevv�0?���v�7���@��C�@,�ռT���u��������[��K&Nz&ur	�,����P?2���4�ӄ��Y:�Ώs�FDҶ}�wH`1��L2֖8RP���E��ZE��:R0���s��!�,g������#�֣���_ ��x
ZV��v��h��]@k�!݊m�x�u�0�L��SPk�a��^Ǧ$_2�%��%�H���>֖2�uաKK�OZ~�qL�hW��nfҲ�eN3�7�։�w��چ	�)���k�$㔳��l9%-#���ej�!ԭ�מ(�B�Z��&�8nX�*o^pI� ĥ�6�}��<x���	~���s%,1�C����8֘0gߖ8�E$O_X�)2�غ�Һ�R�1,轀�/��ʺ�/	d~�_yCz	�}�BS/h\�(�8��x�-���j1=ѹ����u�
~uo�Z_ڴЄ����&.cٳUԂ�/�V,U.�o>�
N/m���'�MS?�H7�U�w�i2����ZI��.��^��q�d@j�M�.#���Jҕ��3^�����lY�E�~��쮨�~)}b�9O��~<�ૃO>.���C�G-�N�ja�u%_+l��5�������3(k�֓2�r�zC(}���Z����J�O?|c����wgil��X�J�GcQGB��K��{Lu'�ݍp'Ґ�a�`�D�8ڑފ2���
зH��8��M��:R/~=Ҳ�����5e�z��`�	�a�U|)�b����$_ۼG̗]L��ǻ��$IEND�B`�PK
!<���$$5chrome/browser/skin/classic/browser/monitor_16-10.png�PNG


IHDR��~d�gAMA���a�IDATxܕn!Di����g�ۤ�`=��M�%�m�Xf�9�4�?���+�$���dv����ִ��q�e5�+����L�;�\O�M:"�3d����r�|���˲8��Xr�!�.��#�Ua��s���z �
 �p�=�"�j��`����V|�避�Cw�m��zz�L�7�]]_��#Q���g�/p�
��#�GB~���t������p��IG�Ȧszh�yz�|~�H:��,6w"�ω�?c�7;��tJ۶�&Cd���L/��|9��
���h�M���YLpnY���Z ��"KK���<��ƞ"I��7�ڻ��b�+��(�{�=��Y>�,D�Ӝvz�?��9�s�?�ĈC�
|��!�Z��F��l?�����	a���N�y���3�m[�b�h�����E6��>A�ͨ#�"KG)�h��
 ڎ4!{
:�ha������ܶ���hG�/���v~��~�sq,��O��(C�wb��C��!c�aط/����>�>P��D
�n5�I����xY:���9Ǒ>i[J#‰����B!�/�BDtbH��4X+�X��'{
��}���vY�e�X�9�K[�R�RH7j�n3�U���_Ⱥ�~���n����5���d������1����^o����ޔ�E�v��? ��-��E��9���:1^�@
 o��	6R*6�J�<�
Y��|f��+�!d����oo)�-�;�=�|�m�蛹{MX�"��^�����t�	�$>�
ہ,xK�F�}lY�O=�!�lv6Ԋf���q΅
�>��|�h[���)���mO�.m{͈W�nP����L))�p����P }�
��U���SvtN쌑x�킢H-�o��v֙���}�5Q��vQ��	ӷF.}�u�D(��J�Łm����	IB����J��8���O��e�<��۶�] `� �[�3~�*�[k�
�|&V�,T�wh羬R(`@������c�mW�eC�a�b�be:9���(��EJ[sp�oi��0�j�
/U�����	Ӥ�T��y�8� HD= ��(��p,��(���L�(�8������5�R�<,j�߱���t���.y޽�VN^ȀJC\�n ���/ʲȦD�y*i�t(��!��9�y�~��U~K['��`̓�j~�ǯ��œ�c��&G?�r'�X�h�A��K����5��8}��!߀�Du��
 ��"��q#�H�k�6 �~'��T>, A޹�w��U��!Ӳ��.N­�v�ƪB��?�=��%���z,c���9[�S�Sa��I\�Fu��^6	�S�c�B
�=d��A9�K,��7W���ONO�cȘ'��D羹V�?%@�q�������52�5 O�V�!‘Ϭ_����$�a�<�߱J���y�@Q��
��5q�g��*Q����&���
C����o�l� �Q~R�?e��K��'�:.^�{ȩIL�
�ΤO_&496
��@O}�����=?��
|,*yC����6u�b"XԈ$߹��2�-��W�<��}��o�.e!�V6���˹n��u��ŋn��\�^׹MY'�SDe,r'��
���_��a
�P��It�[���bM�R��B=���5��ܱ�GD��r�Lb)z+�����ߛ�L�0���3�顰m��ɺ��麿�0~O_
���J@�V�kY�!����?���l-�]I�:�0�À:�À:�0�t�a@�t�at/�`V�2����feH������À:�0�:�0�À:�À:�0�À�0�À:�0��.wfe0+�Y��`V\����~�a@��a@�t�a@��_\�'(���'P�A��P�A�t�a@�t�a@��<@�t�a@�Ɓ�A�iKfe0+�Y��`V����~�a@�9�a@�t�a@�Y�"�ΞY,;s�PX�Ǿ|��̰
��<L�(��6�̌׿a&sJ��=�/$�t�j<ju�Z�Ӓ�/to*$�j���<��0'X��<n%<����/�2�r���ܞg��~��'9F�z(��\}�y�Q����c|����9<���Q�4D�K�.[춧肽�5���x����Yx:�w�����(R��hd:��j���&��췹WVVl?9�h4�S����i��_,��Z�'���/��y���w�W_�%�y����x<L�I/�KW�$��Էȵ$=%�9�t:
��{�>��y�s����m���#'��vYk���]�s�2N��2F�&M����S�=9��,Z͕z������Yu�U����ɠz�6���g���{����9G"撓hLuƲ6�r��c8H��ϵ$#�H��G㥓� ��s�o*��>�Nj8~����ؼ�9��s��'g���i�H�^����������x���Guond��v�V?��&&뱲�+[�}���5��P��\��$��&�ho�0#�F�
(�a�O��bp�4�R�#��8P�.:��k<n,=��Ds�����]��$�(ܒ��z�~0Oǎ]N�Z�2��ۀ̤�Ѐ�T�Kg>���6'�Տ���	n�q�����&`�"�������,��~l�:���6���/��Z���'�x�X�`+�Oc��-A��~Z^����R��<}�Y�7y=�"����Y�6��!�u�i�����}�D�%K�H���Т�Q�4d�=J�=���Qu��r!l�H�	�aF$"sX�E���2D+b��s��p�^���E�D�e-�Nߊ��1~z��N���ǿ�U\?��S,�Gz�,b�E��&��}���WD��_rw�_�`�;���q�RSn@�G"��i^�Sg�y�
P8��l֏9���f�%�Dr���򐧟5-�x��2����f;��ϴ��I��e�V|�oN�)������{�H*�>�]!
%�&�Ӹ݇��O�w~	��x2��k΍�+6b���wvb����ό�0�����,�r�ϻ�߭����@�MZ��,��vS�G.�?�Q�$ؤZvH8���ئ!$U�"�Q��S��W���E��^@ȡg�dp���O �<
���8���$�e~ds����v���G|]�M��gmY�/̧,=���0B���/��u_��yFl]�o��~L?�%f���_cN	4�!��)T ���![D��E?�s�ͅ��ӑe��d��:�k)Ţ0'"�O<O9x�z��h:�
��0&��:.hm�^'P�N����I>>m<��Rҡ9���ޣ��q&�V�|}9<��8���a�_�
�.��w��N��;��w�?|�W|������?��c��Nt����:�p���ҧ�:��@t�w����k�w��������j87��k�;���٘"�i��8QX�H��'�_Oh&�5Z���
G�uL�zij�83}��@6�� h�c_��X�I�I
^���X:g��
�R&IJ��ߵ��r�Y��h�=��K;�kO��>]��Z�Q?g��o�w�}���袋bgg�T���Ā�!S�7��R8#2���qJ�t,��?�
|��T�j*e:�=��9Y3]�P7{-�2�;�pp�W���2�wPho�t+�o ��������4�o��25��
���鵮�J��P
X�U�������^VWW�\��CP�>��Y����_MMD��f
�Cx����i�j��*�y$�)��̓���ݘ������~�S��9r֏��}^�M���0����sP�iXל�q�\�v�"	����U�����qc��im���fo��|�Y�Vfffn�/�?CNc��Ed����|��Zj��y��6��s�����:6_���ԩS�</86	[��GcE4t�׸d��q�HWWO�ڊp��ٮ�=n-�!�^��~&0Mhԣ�Xz�ը�L����7���
�Q�k=��
�S1t%�ek_������0�L��O=�E�}4a„�5�.���ҥK����c�����?��m+S��ݣ'L�=az��	�a���ئ��/��9�h#�"#W緪�����n��'o��g#of;�w`��n���C��cbl�q8�I�fP�KV1!��K�3f�7����AZ`4��۶��ȸ���^�r%?�z��D�%�\�q�N�3���9[�)���6�t]ƹ:�+�o߾� y+"�{�\|�e��b<8s����1[���&��^uw������+���ģp��D/*�r�goS.M���<�%[�l���j�M�s�o���ŋ����� _� hn����c4Q�=��&�C��Yz���6|�mx�?���GCp0&��u5o��m�݆�]�iӦ���⋏{�t.�p׮]31�<��sVgG#���]�#�q���6�G�ʜ�6�a��k� �0O=y�d�x4<��ў��YQ����w�C͸��-��0e{�t�oC���2m�4�i�'�!��\V��$��>�qs�8k����Y�U��KL�!	J{�]By�M���,����Y�Ѝ�,������?�PƼm۶fǎ��ݻwsׅ��Ϡ'LG!0�F��9�C��X#���f�: �S(�I�oh����-�F�1�M@���l���4_�I�tې�h����z�t3_0S�X�
q�D"]�ֈ|�����Y4`�H6M����
a:�\F�#�GzmG�\� S��h�#��$�}��>O>��w���	�]1�l�d�ax���A�8��mi��G���bXP���PO'Y+H�}��z�����$�|I+�������W�J2`�n�'_��!��!
�=
Ƨ!���<�kl#��[�2�A5 =�G!,*i$�m��}<HJ�z	h��/	��a�!	d!͗P6V��2�Ֆ��t�"�#-װ�8�ٳg��e�8^�0_>�Z!��p$F����4rOV���1��v�#�\����(�.��@�Q�m�A���>��G����gq)��Y�7�ͥٺu��4/��|�����z��=a���G"W�`h��M���%���H	�:�.�!��j7$�i�O{�>��	�aL�l�������Ǹu$A�/d@z-���MԤ�
�x�IH��?ĩb[���z&۴_j���V
z�\�K����"��ȼȜ�d�1 1ɤ!_ocX�H�
�׼�n�0�_L����ZB
�!��j��ڮ�}���Y�%�E��z�\|�2$���4�Qѓc�cH��'e���.i|�4!��I����O� �b��>6eƦ��!ڸ/j1�az�A�(O�H�p�d	
0p©{(d!~(mT�9H��	x�.q�[�S�;��C=�؛#�� ~�])w6�=�IEND�B`�PK
!<k+�AA/chrome/browser/skin/classic/browser/new-tab.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M11 11V9a1 1 0 0 1 1-1h1a1 1 0 0 1 1 1V5a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v6H1a1 1 0 0 0 0 2h7v-1a1 1 0 0 1 1-1zm4.5 1H13V9.5a.5.5 0 0 0-1 0V12H9.5a.5.5 0 0 0 0 1H12v2.5a.5.5 0 0 0 1 0V13h2.5a.5.5 0 0 0 0-1z"/>
</svg>
PK
!<�.���2chrome/browser/skin/classic/browser/new-window.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16" fill="context-fill" fill-opacity="context-fill-opacity">
  <path d="M15.5 12H13V9.5a.5.5 0 0 0-1 0V12H9.5a.5.5 0 0 0 0 1H12v2.5a.5.5 0 0 0 1 0V13h2.5a.5.5 0 0 0 0-1z"/>
  <path d="M16 4a3 3 0 0 0-3-3H3a3 3 0 0 0-3 3v8a3 3 0 0 0 3 3h4.03v-.006a.994.994 0 0 0 0-1.987V13H3a1 1 0 0 1-1-1V6h12v1.952h.01c0 .017-.01.031-.01.048a1 1 0 0 0 2 0c0-.017-.009-.031-.01-.048H16zM2 5V4a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v1z"/>
</svg>
PK
!<��]ttIchrome/browser/skin/classic/browser/notification-icons/camera-blocked.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
  <path d="M9.7 13a1.345 1.345 0 0 0 1.3-1.389V9.552l3.037 2.648a1.007 1.007 0 0 0 .963.285V4l-9 9zm5.007-11.707a1 1 0 0 0-1.414 0l-2.452 2.452A1.284 1.284 0 0 0 9.7 3H2.3A1.345 1.345 0 0 0 1 4.389v7.222a1.4 1.4 0 0 0 .731 1.244l-.438.438a1 1 0 1 0 1.414 1.414l12-12a1 1 0 0 0 0-1.414z"/>
</svg>
PK
!<��$BBAchrome/browser/skin/classic/browser/notification-icons/camera.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
  <path d="M14.037 3.828L11 6.479v-2.09A1.345 1.345 0 0 0 9.7 3H2.3A1.345 1.345 0 0 0 1 4.389v7.222A1.345 1.345 0 0 0 2.3 13h7.4a1.345 1.345 0 0 0 1.3-1.389V9.552l3.037 2.648a1.007 1.007 0 0 0 .963.285V3.542a1.007 1.007 0 0 0-.963.286z"/>
</svg>
PK
!<r�*�Ichrome/browser/skin/classic/browser/notification-icons/canvas-blocked.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 43.688 44.188" fill="context-fill" fill-opacity="context-fill-opacity">
  <path d="M39.687 41.687H11.816l3.946-4h20.925c2.209 0 3-.791 3-3V13.435l4-4.055v28.307a4 4 0 0 1-4 4zm-5.529-19.5l3.529 3.6v9.9H17.735l12.324-12.492 1.452 1.692zM5.349 43.222l-.076.077a3.081 3.081 0 0 1-4.4-.077 3.184 3.184 0 0 1 .076-4.459l.776-.786c-.007-.099-.038-.19-.038-.29v-30a4 4 0 0 1 4-4h29.866L38.279.924a3.081 3.081 0 0 1 4.4 0 3.184 3.184 0 0 1 0 4.459zM8.687 7.687c-2.209 0-3 .791-3 3v23.274l4.455-4.516 17.893-18.137 3.572-3.621H8.687zm4.733 12.7a5.852 5.852 0 1 1 5.733-5.851 5.793 5.793 0 0 1-5.733 5.851z"/>
</svg>
PK
!<axa���Achrome/browser/skin/classic/browser/notification-icons/canvas.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 42 38" fill="context-fill" fill-opacity="context-fill-opacity">
  <path d="M38 38H4a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4h34a4 4 0 0 1 4 4v30a4 4 0 0 1-4 4zm0-31c0-2.209-.791-3-3-3H7c-2.209 0-3 .791-3 3v24c0 2.209.791 3 3 3h28c2.209 0 3-.791 3-3V7zM6 28.6l5.412-5.5 1.647 1.7L23.647 14l6.176 7.2 2.647-2.7L36 22.1V32H6v-3.4zm5.733-11.9a5.852 5.852 0 1 1 5.733-5.851 5.793 5.793 0 0 1-5.733 5.851z"/>
</svg>
PK
!<�����Gchrome/browser/skin/classic/browser/notification-icons/default-info.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
  <path fill-rule="evenodd" d="M8 1a7 7 0 1 1-7 7 7 7 0 0 1 7-7zm0 3a1 1 0 1 1-1 1 1 1 0 0 1 1-1zm0 3a1 1 0 0 1 1 1v3a1 1 0 0 1-2 0V8a1 1 0 0 1 1-1z"/>
</svg>
PK
!<�/RbbWchrome/browser/skin/classic/browser/notification-icons/desktop-notification-blocked.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
  <path d="M15.961 3.039L14 5v4.577a.423.423 0 0 1-.423.423H12a1 1 0 0 0-1 1v1.194l-1.491-1.827a.978.978 0 0 0-.558-.318L7 12h1.26l2.966 3.633A1 1 0 0 0 13 15v-3h.577A2.426 2.426 0 0 0 16 9.577V3.423a2.4 2.4 0 0 0-.039-.384zm-1.254-1.746A1.013 1.013 0 0 0 14 1H2.423A2.426 2.426 0 0 0 0 3.423v6.154A2.426 2.426 0 0 0 2.423 12h.163l-1.293 1.293a1 1 0 1 0 1.414 1.414l12-12a1 1 0 0 0 0-1.414zM2.423 10A.423.423 0 0 1 2 9.577V3.423A.423.423 0 0 1 2.423 3h9.163l-2 2H4.5a.5.5 0 0 0 0 1h4.086l-1 1H4.5a.5.5 0 0 0 0 1h2.086l-2 2z"/>
</svg>
PK
!<�ﱷ&&Ochrome/browser/skin/classic/browser/notification-icons/desktop-notification.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
  <path d="M13.577 1H2.423A2.426 2.426 0 0 0 0 3.423v6.154A2.426 2.426 0 0 0 2.423 12H8.26l2.966 3.633A1 1 0 0 0 13 15v-3h.577A2.426 2.426 0 0 0 16 9.577V3.423A2.426 2.426 0 0 0 13.577 1zM14 9.577a.423.423 0 0 1-.423.423H12a1 1 0 0 0-1 1v1.194l-1.491-1.827A1 1 0 0 0 8.734 10H2.423A.423.423 0 0 1 2 9.577V3.423A.423.423 0 0 1 2.423 3h11.154a.423.423 0 0 1 .423.423z"/>
  <path d="M11.5 5h-7a.5.5 0 0 0 0 1h7a.5.5 0 0 0 0-1zm0 2h-7a.5.5 0 0 0 0 1h7a.5.5 0 0 0 0-1z"/>
</svg>
PK
!<��ڸ�Nchrome/browser/skin/classic/browser/notification-icons/focus-tab-by-prompt.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
     width="32" height="32" viewBox="0 0 32 32">
  <path d="M29.43,25,18.57,3.8A2.92,2.92,0,0,0,16,2a2.92,2.92,0,0,0-2.57,1.8L2.57,25a3.47,3.47,0,0,0,0,3.4A3.15,3.15,0,0,0,5.33,30H26.66a3.15,3.15,0,0,0,2.77-1.6A3.47,3.47,0,0,0,29.43,25ZM16,7.2a2.3,2.3,0,0,1,2.37,2.4L18,18a1.88,1.88,0,0,1-2,2,1.88,1.88,0,0,1-2-2l-.4-8.4A2.3,2.3,0,0,1,16,7.2ZM16,28a3,3,0,0,1,0-6,3,3,0,0,1,0,6Z"/>
</svg>
PK
!<�JȋFchrome/browser/skin/classic/browser/notification-icons/geo-blocked.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
  <path d="M12.7 6.3a4.953 4.953 0 0 1 .2.7H12l-1 1a1 1 0 0 0 1 1h.9A5.015 5.015 0 0 1 9 12.9V12a1 1 0 0 0-1-1l-1 1v.9a4.953 4.953 0 0 1-.7-.2l-1.512 1.512a6.989 6.989 0 0 0 9.425-9.425zm2.007-5.007a1 1 0 0 0-1.414 0L12.184 2.4A6.986 6.986 0 0 0 2.4 12.184l-1.107 1.109a1 1 0 1 0 1.414 1.414l12-12a1 1 0 0 0 0-1.414zM3.1 9H4a1 1 0 0 0 0-2h-.9A5.015 5.015 0 0 1 7 3.1V4a1 1 0 0 0 2 0v-.9a4.955 4.955 0 0 1 1.747.738l-6.908 6.909A4.957 4.957 0 0 1 3.1 9z"/>
</svg>
PK
!<N��+,,Gchrome/browser/skin/classic/browser/notification-icons/geo-detailed.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
     width="32" height="32" viewBox="0 0 32 32">
  <path d="m 2,15.9 a 14,14 0 1 1 0,0.2 z m 3,2.1 a 11,11 0 0 0 9,9 l 1,-5 2,0 1,5 a 11,11 0 0 0 9,-9 l -5,-1 0,-2 5,-1 a 11,11 0 0 0 -9,-9 l -1,5 -2,0 -1,-5 a 11,11 0 0 0 -9,9 l 5,1 0,2 z"/>
</svg>
PK
!<w���EE>chrome/browser/skin/classic/browser/notification-icons/geo.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
  <path d="M8 1a7 7 0 1 0 7 7 7.008 7.008 0 0 0-7-7zm1 11.9V12a1 1 0 0 0-2 0v.9A5.015 5.015 0 0 1 3.1 9H4a1 1 0 0 0 0-2h-.9A5.015 5.015 0 0 1 7 3.1V4a1 1 0 0 0 2 0v-.9A5.015 5.015 0 0 1 12.9 7H12a1 1 0 0 0 0 2h.9A5.015 5.015 0 0 1 9 12.9z"/>
</svg>
PK
!<g��KbbDchrome/browser/skin/classic/browser/notification-icons/indexedDB.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
  <path d="M14 1H2a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h2a1 1 0 0 0 0-2H3V3h10v5h-1a1 1 0 0 0 0 2h2a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1z"/>
  <path d="M10.293 11.293L9 12.586V8a1 1 0 0 0-2 0v4.586l-1.293-1.293a1 1 0 0 0-1.414 1.414l3 3a1 1 0 0 0 1.414 0l3-3a1 1 0 0 0-1.414-1.414z"/>
</svg>
PK
!<�����Ichrome/browser/skin/classic/browser/notification-icons/login-detailed.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
     width="32" height="32" viewBox="0 0 32 32">
  <path d="m 1,27 0,3.5 a 0.5,0.5 0 0 0 0.5,0.5 l 5,0 a 0.5,0.5 0 0 0 0.5,-0.5 l 0,-1.5 1.5,0 a 0.5,0.5 0 0 0 0.5,-0.5 l 0,-1.5 1,0 a 0.5,0.5 0 0 0 0.5,-0.5 l 0,-1 1,0 a 0.5,0.5 0 0 0 0.5,-0.5 l 0,-2 2,0 2.5,-2.5 q 0.5,-0.5 1,0 l 1,1 c 0.5,0.5 1,0.5 1.5,-0.5 l 1,-2 a 9,9 0 1 0 -8,-8 l -2,1 c -1,0.5 -1,1 -0.5,1.5 l 1.5,1.5 q 0.5,0.5 0,1 z m 21,-19.1 a 2,2 0 1 1 0,0.2 z"/>
</svg>
PK
!<
Ϊ0oo@chrome/browser/skin/classic/browser/notification-icons/login.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
  <path d="M10.992 1a4.009 4.009 0 0 0-4.009 4.008c0 .1.022.187.028.282-.059.05-.119.087-.178.143L5.667 6.6a.366.366 0 0 0 0 .467A1.878 1.878 0 0 0 6 7.5.353.353 0 0 1 6 8l-5 5v1.767a.229.229 0 0 0 .233.233H3.77a.229.229 0 0 0 .23-.233v-.778h.75a.227.227 0 0 0 .233-.228v-.768H5.2s.28 0 .28-.235V12.5h.779s.233-.1.233-.244v-1.271h.855l1.12-1.118H8.7l.467.467c.233.233.233.233.365.233a.437.437 0 0 0 .275-.127l.993-1.273c.034-.053.054-.107.084-.161.036 0 .07.011.107.011a4.008 4.008 0 1 0 0-8.017zM12.5 4.489a1 1 0 1 1 1-1 1 1 0 0 1-1 1z"/>
</svg>
PK
!<�����Mchrome/browser/skin/classic/browser/notification-icons/microphone-blocked.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
  <path d="M11 8v1a3 3 0 0 1-3 3 2.958 2.958 0 0 1-.859-.141l-.775.775a4.012 4.012 0 0 0 .634.224V14H5.5a.5.5 0 0 0 0 1h5a.5.5 0 0 0 0-1H9v-1.142A4 4 0 0 0 12 9V7.5a.5.5 0 0 0-.146-.354zm3.707-6.707a1 1 0 0 0-1.414 0L10 4.586V3a2 2 0 1 0-4 0v5.586l-.945.945A2.856 2.856 0 0 1 5 9V7.5a.5.5 0 0 0-1 0V9a3.843 3.843 0 0 0 .248 1.338l-2.955 2.955a1 1 0 1 0 1.414 1.414l12-12a1 1 0 0 0 0-1.414z"/>
</svg>
PK
!<��$YffNchrome/browser/skin/classic/browser/notification-icons/microphone-detailed.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
     width="32" height="32" viewBox="0 0 32 32">
  <path d="m 8,18 a 8,8 0 0 0 6,7.7 l 0,2.3 -1,0 a 3,2 0 0 0 -3,2 l 12,0 a 3,2 0 0 0 -3,-2 l -1,0 0,-2.3 a 8,8 0 0 0 6,-7.7 l 0,-4 a 1,1 0 0 0 -2,0 l 0,4 a 6,6 0 0 1 -12,0 l 0,-4 a 1,1 0 0 0 -2,0 z m 4,0 a 4,4 0 0 0 8,0 l 0,-12 a 4,4 0 0 0 -8,0 z"/>
</svg>
PK
!<蔢�44Echrome/browser/skin/classic/browser/notification-icons/microphone.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
  <path d="M8 11a2 2 0 0 0 2-2V3a2 2 0 1 0-4 0v6a2 2 0 0 0 2 2z"/>
  <path d="M12 9V7.5a.5.5 0 0 0-1 0V9a3 3 0 0 1-6 0V7.5a.5.5 0 0 0-1 0V9a4 4 0 0 0 3 3.858V14H5.5a.5.5 0 0 0 0 1h5a.5.5 0 0 0 0-1H9v-1.142A4 4 0 0 0 12 9z"/>
</svg>
PK
!<%9�X?chrome/browser/skin/classic/browser/notification-icons/midi.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 16 16" fill="context-fill" fill-opacity="context-fill-opacity">
  <path d="M14 1H5c-.6 0-1 .4-1 1v9.3c-.3-.2-.6-.3-1-.3-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2V5h9v6.3c-1-.6-2.2-.2-2.7.7-.6 1-.2 2.2.7 2.7s2.2.2 2.7-.7c.2-.3.3-.7.3-1V2c0-.6-.4-1-1-1z"/>
</svg>
PK
!<�ԇUchrome/browser/skin/classic/browser/notification-icons/persistent-storage-blocked.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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="32" height="32" fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg">
  <path d="M4 24.172V23.1c0-1.1.9-2 2-2h1.072l2.1-2.1H6c-.7 0-1.4.2-2 .5V6.2C4 4.4 5.3 3 7 3h18c.056 0 .112.002.167.005l1.42-1.42c.78-.78 2.046-.78 2.827 0 .78.782.78 2.048 0 2.83l-24 24c-.78.78-2.047.78-2.828 0-.78-.782-.78-2.048 0-2.83L4 24.173zM7.828 29l7.9-7.9H26c1.1 0 2 .9 2 2V27c0 1.1-.9 2-2 2H7.828zM28 8.828V19.5c-.6-.3-1.3-.5-2-.5h-8.172L28 8.828zM24.1 27c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2z" fill-rule="nonzero"/>
</svg>
PK
!<F'6�CCMchrome/browser/skin/classic/browser/notification-icons/persistent-storage.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg" width="32" height="32">
  <path d="M26 21.1H6c-1.1 0-2 .9-2 2V27c0 1.1.9 2 2 2h20c1.1 0 2-.9 2-2v-3.9c0-1.1-.9-2-2-2zM24.1 27c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zM25 3H7C5.3 3 4 4.4 4 6.2v13.3c.6-.3 1.3-.5 2-.5h20c.7 0 1.4.2 2 .5V6.2C28 4.4 26.7 3 25 3z"/>
</svg>
PK
!<X	7���Gchrome/browser/skin/classic/browser/notification-icons/plugin-badge.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
     width="32" height="32" viewBox="0 0 32 32">
  <circle fill="#3088d4" cx="27" cy="5" r="5"/>
</svg>
PK
!<�Mj+Hchrome/browser/skin/classic/browser/notification-icons/popup-subitem.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
  <path d="M13.707 7.293l-3-3a1 1 0 0 0-1.414 1.414L10.586 7H6.012A.918.918 0 0 1 5 6V3a1 1 0 0 0-2 0v3a2.916 2.916 0 0 0 3 3h4.586l-1.293 1.293a1 1 0 1 0 1.414 1.414l3-3a1 1 0 0 0 0-1.414z"/>
</svg>
PK
!<V� M��@chrome/browser/skin/classic/browser/notification-icons/popup.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
  <path d="M12 8a4 4 0 1 0 4 4 4 4 0 0 0-4-4zm0 1a2.975 2.975 0 0 1 1.733.56L9.56 13.733A2.991 2.991 0 0 1 12 9zm0 6a2.975 2.975 0 0 1-1.733-.56l4.173-4.173A2.991 2.991 0 0 1 12 15z"/>
  <path d="M13 1H3a3 3 0 0 0-3 3v8a3 3 0 0 0 3 3h3a1 1 0 0 0 0-2H3a1 1 0 0 1-1-1V6h12a1 1 0 0 0 2 0V4a3 3 0 0 0-3-3zM2 5V4a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v1z"/>
</svg>
PK
!<�?Hl��Ichrome/browser/skin/classic/browser/notification-icons/screen-blocked.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
  <path d="M4 10a1 1 0 0 0 0-2H2V3h6v1a1 1 0 0 0 2 0V2a1 1 0 0 0-1-1H1a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1zm11-4h-2l-2 2h3v5H8v-2l-2 2v1a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1z"/>
  <path d="M14.707 1.293a1 1 0 0 0-1.414 0L8.586 6H7a1 1 0 0 0-1 1v1.586l-4.707 4.707a1 1 0 1 0 1.414 1.414l12-12a1 1 0 0 0 0-1.414z"/>
</svg>
PK
!<ȳ*�Achrome/browser/skin/classic/browser/notification-icons/screen.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
  <path d="M15 6H7a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1zm-1 7H8V8h6z"/>
  <path d="M4 8H2V3h6v1a1 1 0 0 0 2 0V2a1 1 0 0 0-1-1H1a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h3a1 1 0 0 0 0-2z"/>
</svg>
PK
!<'|����Achrome/browser/skin/classic/browser/notification-icons/update.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
     width="32" height="32" viewBox="0 0 32 32">
  <path stroke="#fff" stroke-width="3px" stroke-linecap="round" d="M 16,9 L 16,24 M 16,9 L 11,14 M 16,9 L 21,14"/>
</svg>
PK
!<�����Cchrome/browser/skin/classic/browser/notification-icons/webauthn.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 32 32" fill-opacity="context-fill-opacity" fill="context-fill" fill-rule="evenodd">
  <path d="M11.0013,17.9999 C11.0013,15.2386153 13.2400153,12.9999 16.0013,12.9999 C18.7625847,12.9999 21.0013,15.2386153 21.0013,17.9999 C21.0013,23.5534188 17.4872463,28.4378884 12.3346491,30.2597035 C11.8139531,30.4438071 11.2426002,30.1709451 11.0584965,29.6502491 C10.8743929,29.1295531 11.1472549,28.5582002 11.6679509,28.3740965 C16.027576,26.8326543 19.0013,22.699233 19.0013,17.9999 C19.0013,16.3431847 17.6580153,14.9999 16.0013,14.9999 C14.3445847,14.9999 13.0013,16.3431847 13.0013,17.9999 C13.0013,20.7611847 10.7625847,22.9999 8.0013,22.9999 C5.24001525,22.9999 3.0013,20.7611847 3.0013,17.9999 C3.0013,10.8206153 8.82201525,4.9999 16.0013,4.9999 C23.1805847,4.9999 29.0013,10.8206153 29.0013,17.9999 C29.0013,19.4211716 28.8597186,20.8259325 28.581352,22.1986412 C28.4715903,22.739909 27.9438266,23.0897137 27.4025588,22.979952 C26.861291,22.8701903 26.5114863,22.3424266 26.621248,21.8011588 C26.8731384,20.559012 27.0013,19.2874016 27.0013,17.9999 C27.0013,11.9251847 22.0760153,6.9999 16.0013,6.9999 C9.92658475,6.9999 5.0013,11.9251847 5.0013,17.9999 C5.0013,19.6566153 6.34458475,20.9999 8.0013,20.9999 C9.65801525,20.9999 11.0013,19.6566153 11.0013,17.9999 Z"/>
  <path d="M8.0013,26.9999 C7.44901525,26.9999 7.0013,26.5521847 7.0013,25.9999 C7.0013,25.4476153 7.44901525,24.9999 8.0013,24.9999 C11.8670153,24.9999 15.0013,21.8656153 15.0013,17.9999 C15.0013,17.4476153 15.4490153,16.9999 16.0013,16.9999 C16.5535847,16.9999 17.0013,17.4476153 17.0013,17.9999 C17.0013,22.9701847 12.9715847,26.9999 8.0013,26.9999 Z"/>
  <path d="M9.0013 17.9999C9.0013 18.5521847 8.55358475 18.9999 8.0013 18.9999 7.44901525 18.9999 7.0013 18.5521847 7.0013 17.9999 7.0013 13.0296153 11.0310153 8.9999 16.0013 8.9999 20.9715847 8.9999 25.0013 13.0296153 25.0013 17.9999 25.0013 21.0536668 24.2197241 23.9472067 22.7567899 26.4983547 22.4820533 26.9774562 21.8709467 27.1431266 21.3918453 26.8683899 20.9127438 26.5936533 20.7470734 25.9825467 21.0218101 25.5034453 22.3122709 23.2530661 23.0013 20.7021516 23.0013 18.0499 23.0013 14.1341847 19.8670153 10.9999 16.0013 10.9999 12.1355847 10.9999 9.0013 14.1341847 9.0013 17.9999zM4.74954564 8.07962613C4.38408439 8.49369922 3.75214695 8.53310689 3.33807387 8.16764564 2.92400078 7.80218439 2.88459311 7.17024695 3.25005436 6.75617387 6.45721717 3.12241027 11.0590025.9999 16.0008.9999 20.9435975.9999 25.5453828 3.12241027 28.7525456 6.75617387 29.1180069 7.17024695 29.0785992 7.80218439 28.6645261 8.16764564 28.2504531 8.53310689 27.6185156 8.49369922 27.2530544 8.07962613 24.4216114 4.87155881 20.3636937 2.9999 16.0018 2.9999 11.6389063 2.9999 7.58098857 4.87155881 4.74954564 8.07962613z"/>
</svg>
PK
!<2:0`��,chrome/browser/skin/classic/browser/open.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M14.859 3.2a1.335 1.335 0 0 1-1.217.8H13v1h1v8H2V5h8V4h-.642a1.365 1.365 0 0 1-1.325-1.11L6.584 1.538A2 2 0 0 0 5.219 1H2a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V5a2 2 0 0 0-1.141-1.8zM2 3h3.219l1.072 1H2zm7.854-.146L11 1.707V8.5a.5.5 0 0 0 1 0V1.707l1.146 1.146a.5.5 0 1 0 .707-.707l-2-2a.5.5 0 0 0-.707 0l-2 2a.5.5 0 0 0 .707.707z"/>
</svg>
PK
!<h�3S~~6chrome/browser/skin/classic/browser/page-livemarks.png�PNG


IHDR�aEIDATxt�C�^1�S�\նm�ƪ�m۶m۶m��6ɻI�:��Ź|���#ʉ�ȱ��9�
� RR��K!��~�E��]��{`ó9�A�CMM+	���=N%��i�^Q
ގf0�z�^��|�GaN􁚖^Q]��a�d�"�����u��zy	�h�ӵ�K
�b�b�!דc�=�v7��b��<IXQ91)�������b�#��E�n���1��Y�KŞD�%��^�f'܃E��FB�.>ԣD�0Ob����ll8@�%���zA.j,���ρ=?zVf	RO/���D���%�8�D���"�߱��+&�Iɥ=��tw��H�yn/ҊW�)�i�/r��)��:zY~�'��u�@x[�2`��࿱���������$ަ
0�Z��
��6W��ې+������nM����:zq.���	�ZQ�hk������
w�&��S �%��5+ܙN�
��s����W�A>���r��U�'`c��$�=�r|B�N5)�q'ZqLFx@M���pX�*
���.(�!��<�� �����'U���IEND�B`�PK
!<T0x�tt0chrome/browser/skin/classic/browser/pageInfo.css
@import url("chrome://global/skin/");

/* View buttons */
#viewGroup > radio {
  list-style-image: url("chrome://browser/skin/pageInfo.png");
  -moz-appearance: none;
  min-width: 4.5em;
  margin: 0;
  padding: 3px;
  color: -moz-FieldText;
}

#viewGroup > radio[selected="true"] {
  background-color: Highlight;
  color: HighlightText;
}

#viewGroup > radio > .radio-label-box {
  padding: 0 6px;
}

#topBar {
  border-bottom: 1px solid ThreeDShadow;
  padding-inline-start: 10px;
  background-color: -moz-Field;
  color: -moz-FieldText;
}

#generalTab {
  -moz-image-region: rect(0px, 32px, 32px, 0px)
}

#mediaTab {
  -moz-image-region: rect(0px, 64px, 32px, 32px)
}

#feedTab {
  -moz-image-region: rect(0px, 96px, 32px, 64px)
}

#permTab {
  -moz-image-region: rect(0px, 128px, 32px, 96px)
}

#securityTab {
  -moz-image-region: rect(0px, 160px, 32px, 128px)
}

#mainDeck {
  padding-left: 2px;
  padding-right: 2px;
}

#mainDeck {
  padding: 10px;
}

/* Misc */
tree {
  margin: .5em;
}

.gridSeparator {
  width: .5em;
}

textbox {
  background: transparent !important;
  border: none;
  padding: 0;
  margin-top: 1px;
  -moz-appearance: none;
}

textbox.header {
  margin-inline-start: 0;
}

#imagecontainerbox {
  margin: .5em;
  background: white;
  overflow: auto;
  border: 1px solid ThreeDLightShadow;
}

textbox[disabled] {
  font-style: italic;
}

/* General Tab */
#generalPanel > #titletext {
  margin-inline-start: 5px;
}

groupbox.collapsable caption .caption-icon {
  width: 9px;
  height: 9px;
  background-repeat: no-repeat;
  background-position: center;
  margin-inline-start: 1px;
  margin-inline-end: 3px;
  background-image: url("chrome://global/skin/tree/twisty-open.png");
}

groupbox.collapsable[closed="true"] {
  border: none;
}

groupbox.collapsable[closed="true"] caption .caption-icon {
  background-image: url("chrome://global/skin/tree/twisty-clsd.png");
}

groupbox tree {
  margin: 0;
  border: none;
}

groupbox.treebox .groupbox-body {
  margin-inline-start: 5px;
  margin-inline-end: 1px;
  padding-top: 0;
}

#securityBox description {
  margin-inline-start: 10px;
}

#general-security-identity {
  white-space: pre-wrap;
  line-height: 2em;
}

/* Media Tab */
#imagetree {
  min-height: 10em;
  margin-top: 2px;
  margin-bottom: 0;
}

#mediaSplitter {
  -moz-appearance: none;
  height: .8em;
}

#mediaGrid {
  min-height: 9em;
}

#mediaLabelColumn {
  min-width: 10em;
}

#thepreviewimage {
  margin: 1em;
}

treechildren::-moz-tree-cell-text(broken) {
  font-style: italic;
  color: graytext;
}

/* Feeds Tab */
#feedPanel {
  margin-left: 2px;
  margin-right: 2px;
}

#feedtree {
  margin-bottom: 0px;
}

#feedListbox richlistitem {
  padding-top: 6px;
  padding-bottom: 6px;
  padding-inline-start: 7px;
  padding-inline-end: 7px;
  min-height: 25px;
  border-bottom: 1px dotted ThreeDShadow;
  color: -moz-FieldText;
}

#feedListbox richlistitem[selected="true"] {
  background-color: -moz-Dialog;
  color: -moz-DialogText;
}

#feedListbox {
  margin-bottom: 0;
}

.feedTitle {
  font-weight: bold;
}

/* Permissions Tab */

#permList {
  -moz-appearance: listbox;
  margin: .5em;
  overflow: auto;
  color: -moz-FieldText;
}

.permission {
  padding: 6px 7px;
  min-height: 25px;
  border-bottom: 1px dotted ThreeDShadow;
}

.permissionLabel {
  font-weight: bold;
}

.permission:hover {
  background-color: -moz-dialog;
  color: -moz-DialogText;
}

/* Security Tab */
#securityPanel .caption-icon {
  display: none;
}

#securityPanel .header {
  font-size: 120%;
}

#securityPanel .fieldLabel {
  margin: 2px 10px 3px;
}

#securityPanel .fieldValue {
  font-weight: bold;
  margin: 2px 10px 3px;
}

#securityPanel row {
  -moz-box-align: center;
}
PK
!<f�R� � 0chrome/browser/skin/classic/browser/pageInfo.png�PNG


IHDR� �s� �IDATx�tGֶ�jZ`�;q�����a�,�q�-3s��v�9N3�1Ȳ�qF�]�w��eY��o~}��N?�޺U=���{�B�_J)O�}�()�*����Ǔ���F��^q���:��Sk?���K����TC0e���#��ZJVN"-������Ɔ����8\���K�����Y�e��/w�u�3&O�*�i�ssb�G+��?�<'��]p�U�2������^��?��M��y�N�ceq��u�\Ga��s!��mvPK�ᨠ�u��0��H�����o�G��AZ��+�c��)n67)��*�\�t]��s��L6�z��=�36�0��)������x�ǧQ2�����SO>ͬWy�O>��w���߿�� �ϲ����O�v���c,�Q�/��]�c��V<:�GŊ��B#K��
��JI��ʇ�̷J�'RM���,z��L_O���s���Z�}���~A���*���A�rh[�˴s��g|���_}���o��S�)�~י{XA�3����z��d����m��[������>?yޥg_ڳ�J)!���h0=Ho5�''�xr��?z7p"ے4�u��`ޣO�~��l۲]�Z�>�F�;;����sҗ���9��WQ�!B�B�4
��������z_���ݘt�Wbu�=y3�����0'����|=<�B�&�*����[/<�?�7��w����ڝ�K�͟?�I�D,/w�^{�����l&CWw��Ų-,�	�y�=y�{��Շ�Z�b�?��.��ڦ��Bm�ۼ��
�Lj���,k&_�җ����u�u�����O�
!�8ٷE �v�	U���~ۡ�_�9�@�������wGտ�����Щ2#�N(�"��o

�uLl��(���[���1n����(� �ˢ��5�J���:t9%�K�/������F"��#��s�v����H2��g�r�)���2��t���lO����/}É8�D<)++��w��}/'0����}���;�B�
�/)Դ��>U
�����g���D����<�|���F0��N��3�:��,ۊ�z�.�R,��_�9`&;.!L[s$�
���0͆�B'R�v�,���	�F�K�N�@�LBP�TUV���A `��U�w�jG�:u:ӂ.|.<�黃������Sm�bf�+R|n>
*����'j&��\�L`9���o�zII�S�����P��l�����X��j����zثU��e��u��3>��/&v�uws�ҥ�"�Á�0 ��gv�t:
��)�p"��� 8��@�.�~��p����D��a� 0���1v9��t�u��wP�+@���Ю��n�*���!��P��B�)ttt���7�'��sǝw`��LSrw��&�VBmz�>�1���'��?W���}
a(��c�w�?��cݽ]�m���77�5T�^xe[�M��;w�m���o���r���%=䰢�_}���P�]�9�g�bLy�W�b0p���8�ݜ��H����W:R�	��4���ֹ���q>����[)�[뱿p48����d�?s�9��E6����~T#a5�߮�p�|_��@B7u�T,�&��G	풆�?;�Ɔ�(�&ᑣ�mCI4��#��81</G���q��~f}㦃ƕWP׸��k?N{�ܥ�\sC��E]���?���\7'�I��N3��_��1�gv�Rx8&ٖ
��+�ҁ�����1�a5*�Ne�L`�<,�Ȝ��3_�LC(�F�O%�P�B~s-����~��V��a�8��\�8��; �C8>+�0P�H��O)�>�7���*
�c;(Tx�m�,�|�1��f=J_��?�<��r��k9|�լ�|�]K�B.�ۢ�KMMk��T�aH%S����wM?������2���Ғ2�����+�c����L�w��]!8۔B;��H͛��^g�}�j����=5a("�GI��mΝ����]v�j��(�|I|7��Փ����@��Q6%p�#1�81دF˰1v=
c����!r�k�+�
ᓒ�RR�P�
�6��j�HhJILӢ5��K�|���t*c�9u���ʹp���Y�$w���?|e�T?��w�PB�ч�Jj��F���b������%�]M�>u�)Ʉq1v���s�E�=s��lC�s����@Jy��^_���U����O,f������䟌��Pì<��6/��.���rs�#[���rO]�9�KX_�/�_A�)��o`���΅��EI��@*
�p��뇍Ň���:*�ܵ�2�?����|uʅ�bM�ܶ�&$v�}�Ra�4
����P;���<�3g��X���wZxiI���ģEL�aV�f �e]K���W��9��sߏ�/��\o+���e�w�q���~��u�O`�h�"�2����"Nj���g�[���\0!5F���?���8�>t�C!�A��b���L�Z?${�W1w?���!*v恗#�&�~s>�n='8�:)�[�/n�{h�<Bs�ig�o�������n�*O`���w�����S�J]�
~��d���D<�+(\���$%�)5�D�\ݾ�Y
��p]W���%(:�Mu���>�� ��f�I�z*IϠ�'&�*�Rsz��븶g 3'����H����嗟��0�a���!����l�~��Vj�|VI)���S�o��t�y����̓ ��f:"Տ��2{猇1g	���I�u�����4�� 4_y��Di_���O�DH
r���SW���I5$�>�r�5��Go�7���������\?�U~��g]Ik[����mjΜ9Uv����Ov]/��B
"Y�L�?ni�e��ˇƔRv�q֜�|��,X�:J
VC8s���An{�#v�^���%�9<%p�v���h<�9�+v�V�h��ߵ�����/.GKa�x�R���;�|�w7ږ�s�C�zjD0c�Q��?���T��i��
"���+���4c�5'3$�
9`���9�\nձ*����� ��D1����Z��{,���:�{X�^��H:`���P ���on@"��c���g�cBM�>?���
���u���?OcB�L���R���J���jR�N�d[�l�o�����cO�Τub;6�ii�B�Q�]�`����<�l6�#�J��_����_x^gn{"Z��v�6��o��d穕Ũ�H�E(�:�&[���_��l2�Ej0�b�D"*�W�DK�c���"�')JX�O~t�F�F,
G��.6M������e�m�9$��ӎ)��S�`���f�ȷzh>�*��*�J�a�y�.>D��0�DL@�,�{�:T�f
QZ���cCQ65����Cֿ���@%���?�����l�b}�� ���ʛ����c�t�ө�kS��Q3���m��2�4ͭ����ݎ��H�9%N���-烷���Q���H�‚��6EGOX$2~A��fS��o@�צ��eѮӶ
��(Bf�3#��6��WZDk_.�0J"��XDC���I}����:�RR���s�:�XT�my�����_�щ�
�kҸ#)j0����xJ�f�����I��0�p{+yn�p�'�{��3��âPƄq�|����/�[C%b%�3r�A�EzLH�X;a�9����7K���W��b��w\0�;��Ų\WOG���[��.�I�&�m�[��%w���aZ6�������1D�r����c�@��v�|q��ࢤ���yOC.��<�I'�{���k�)�����j��xeq�Κ@Kw'��8�S�P|u��0��%�=����hp��-r�M&k10���$�h��$p�����0��+j�1_9AĀ&�`�ncV���F��S������TE��A�b+)���E4�[�`Ƙ�Ƥ1&����H��W?��	�"T���s�'r�����x����M������`y���:�Tv�5�w�}O�w��`X�X<�w��ꉄ�8o���Ԅ?i���̜���a�Z���#�:�d�&;�	������� e[e�5��G����yZ(N=F�d���"xm9x�`��Y�!�V����6S��'��q%$l[��L��A_���ElC�/,i!1�j74���y7�8�i�+��k�<㏿����;C,6�C�&�G)�I�����V�\*��7&�&�����K�A���v#�\��I]���q }T�2��E��w��3�^��=0g� ��

]��=�_�O���`;��ӗ�.�>�r�v@:`uM5���L
�J����a��&̕���~���a,\��o~IZf��8:~�,��H��s��nI�UH�X�������j'倽c��Т���`/[�s�"H� %�/,^]���N67�1�`D��7n���j�4
2��aX��`LSP���
9`"f�sB�vd|�i��R�䥐�M7^�aѲ6�D���K;1�Z~Qե��</��(|L+��1�(�L�%���r]�P��S��0�p��jYJ��4k��'�!��P]Q(}��ڏ�9Bs���K@��;��H���6���J���c����o�ͳ��D�D�=���M�߽�ͷ1�/�yx�9d2i�p̶
K�:�S?@�~��!���b˦5"��b�I�~�{���LPPU�j%�}鼡x�^	�1䀻l��4��Ig="13��!��He}{��G�kР�/����t�/u����75�X��#{�=�v�s�)k�4���knyO;n�65܉�G�>����|"��c�V�l�7�қ�“�����z2�o��Kf38�"T��ǐe��{�"�I��Ў��ۿ����7Q�v�b"U�Eֽ���l%��B^[8�~�pl��)P@²��z;�O<�"�^{"O:_)��~�z�5��8�J��+:ދO���e�w(/�cܸq$�	�T�Ȱ���w�cz_�A�KgW'՝�L<����jVn���QR�p#��ҙb�i�6˱�|?��K��/�هB���+����������&<ϛ
llj��6�����\S�X-�R�D��[����ض�_�i�ؖE����&L_Q�6׷�A|eI�Ҹ
@:�ٺ�h�6־gcz	������!��<�����1�s@6|���5O��2������B��G��P�;j8�\��p��Ƚ�F��g
�o~���
�cQ$J^c��k����7������^��4bјfd@����0J5�$�����ޞz�p]O�-5�0L��g1\�E��d=�z�/���f]lS�)���Y�o��w�)�����>	W�����R���`���8�:����!}!H+���BW�z�l���@�S3(�@�"b��������d)]J��eq&W�9`�qtuua+W���H�X����+ؾ��[|�i1ފyX��
���-\�B�1~OF��0CH��mu��81}
��o��9U�:���}ܱx��1u
B��~E)Q�P�'O"��l���p�x"��1-��d���a����Hғ�P����8.������M����'䧳d�9�'��-Cë�������n }�OoeH=�ߟ`k
����P��UvCk7��ћ*�'9�d�Ɨ��T�Ǚ{�7I�����N<��x�Z��?�X�]:�<�Z�q!hѕr�ToC��@mu\�\��U(L�v�t����~A��q~H��y����
5t@Ŷ��Lcc�v�d2E__?�����u��u4}3�ڕ�������Ɖ��dr��5��0DZ�+<��dI
f���9
biq������s�Bhە$8�>$�6!�����Q;��5�b��.6���ӟ"�%�,�������bt�5�ɦ�m�]ih���^�\���B���.��J%[�(�8�
Ԉ�1-�D��4�.TO_7Ӿ�2��N���x��RjH�هQv�Է6P(�61�&�L�:�lV; �k���)�.֫7�H�ʪ*w@Az�Q��ׯ���9z{��1����$�����`���]���չ�r���Lf��#���@�����ˀva�7�o�;`*��S��R�ʒ�4
f����'����TY��<F͸8����>���ڵq��e�~��~�qP�C��4��9�N
�/�c�:�#[���ic�O#<�2�n�"R��嵬�(a�&��`KV����������d�������|�	2�5s&�'�@��W��Ӂi��~�i9���P"H�"��5n.�@r�1�Bi�$R�I�>/%Aѣ�/pO�'uZDa
��ahk.p�]g1\�x�w�Q��EUEEC��R���z�܅D��Z����@2M����8JG{g����zž��B����8�/�HI�.��DmCS��]�u���
T�A��� L��mWW'uuu�u����+6�����}�
�ll�E)�o�[�bo�]ȍτ�i:��ij������2�����<�����ĤT�eZ8vCX����!E�8��<���.J/���+���-�����|�EӴ�0����0
�L$jڴiz�����r�x�vJ�B!���(.)���HsSQQ��!���t�07�mYېNgm:7ݾ�����i'��\2WÖD�P���\��CqQ,�����ӓ�����x�t����2�l�{��eّ,|w7�u
]Q{�b&h}�#�AN�$�V�\__�����omk�5x��A�����,p@bqd$���AFq	F�u� �����ڇ�5��!Tޢ����mqND��mP,�oƢq�����0C�F���U4JJJ�������`��T��

�
�>퀺`�F���6���cR��������G�~A�ɏ�������xɇGt�����~j�+9�+U��&�ϘNMU	}�ٌK:�c��ٶEY<N"!=��v��A7�:k�K?J��)I.p��<pzR���.�>�6��79ֽu/�d�}��q�7/:`���kV_�ԓO���@�*^�,#����h2h��	�>W9ah��
�nJA�N
$7�t]���f�R�w+��Jl�T+/^ܑw=�o���`��M�8����Ҳ JC�J��=��nuJVJ; =����MY_?�D�X<�������G~h����t�{�9���6���O�`�d�l\�����Ɂe�	דx����_�@���>���=�l^}E����Q 
�Rv �<
�ά�(�P�#�N�ii�i�k���4#g%;�xG��LG��h�N��M����_Vu�[H�t?SK���K�߳����}��=��3��SJ�Hn��m���Ͳ����`H��m T��=��3�t�!��,Ɂ�v@۲_jǀA�ޗ
�n!��l*�&��j�Y�d�L%��=��@j��������Ȋ�).�p��tw�VZ^d�+���EȦ�&����y}=�2;�f��{��
v��mXR��J*���-:u����g>�ǫK����>���c|�����y�i��`E'~���⇷l��G8ư���{��f>\��G�A��{��+��a��k�a�l�
��V���f�)�'�y�ٹ@7���,|�ÿ���a�/H)���%��&�]��ҿ�M�$�D&0�Q4���iM�f�}b3���O�i�Ȉ$�3�\��#�ݰ9ݵaM���@s>�v��੧�|�ܵt颡;&���_��X������~���X���=���sl�*��7ĉ_�Ɯ����z�l|��G~��<I�U�@$�\$p+��f�=��np�(J��P��g�| �}@
�~�~�a;��k���<�U�����C�
t��6����U���/]�'��>B��Ow3�v.{�b6��ˍW]��
��#��)�[�~9@(�C`�T7#�!�|:��
O�"Th>N�C>�
6���
����ž�IEND�B`�PK
!<V5��=chrome/browser/skin/classic/browser/panel-icon-arrow-left.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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"
     width="32" height="32" viewBox="0 0 32 32">
  <path fill="context-fill" d="M23.5,25l-9-9l9-9l-3-3l-12,12l12,12L23.5,25z" />
</svg>

PK
!<vΥ#��>chrome/browser/skin/classic/browser/panel-icon-arrow-right.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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"
     width="32" height="32" viewBox="0 0 32 32">
  <path fill="context-fill" d="M11.6,28l12-12l-12-12l-3,3l9,9l-9,9L11.6,28z" />
</svg>

PK
!<��B���9chrome/browser/skin/classic/browser/panel-icon-cancel.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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"
     width="32" height="32" viewBox="0 0 32 32">
  <path fill="context-fill" d="m 6,9.5 6.5,6.5 -6.5,6.5 3.5,3.5 6.5,-6.5 6.5,6.5 3.5,-3.5 -6.5,-6.5 6.5,-6.5 -3.5,-3.5 -6.5,6.5 -6.5,-6.5 z" />
</svg>

PK
!<�Q�"9chrome/browser/skin/classic/browser/panel-icon-folder.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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"
     width="32" height="32" viewBox="0 0 32 32">
  <path fill="context-fill" d="M17.3,9.4c0,0,1.1,0,3.7,0c1.7,0,2,0,5.6,0c0.6,0,0.6,0,1.1,0V9.2c0-1.5-0.9-2.6-2-2.6h-5.8V6.3c0-0.6-1.5-2-2.8-2h-7.1 H7.6H4.9v2.4v2.4v2.2c2.8,0,8.5,0,8.5,0C16.4,11.3,17.3,9.4,17.3,9.4z M29,13c0-0.6-0.6-1.1-1.5-1.7l0,0c-0.2,0-0.6,0-0.9,0 c-2.8,0-3,0-4.8,0c-1.9,0-3.3,0-3.3,0s-1.5,2.4-3.7,2.4c0,0-6.5,0-9.1,0H5.4C3,13.7,3,15.9,3,15.9l1.1,9.7C4.1,27.1,5,28,6.5,28 h19.1c1.5,0,2.4-0.9,2.4-2.4L29,13.7l0,0l0,0C29,13.7,29,13,29,13z" />
</svg>

PK
!<���448chrome/browser/skin/classic/browser/panel-icon-retry.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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"
     width="32" height="32" viewBox="0 0 32 32">
  <path fill="context-fill" d="M28,16.5v-14l-5,4.8c-1.8-1.4-4.4-2.4-7-2.4c-6.4,0-11.8,5.2-11.8,11.8c0,6.4,5.2,11.8,11.8,11.8c3.4,0,6.2-1.4,8.2-3.6 l-3.4-3.4c-1.2,1.2-3,1.8-5,1.8c-3.6,0.2-6.8-2.8-6.8-6.8c0-3.8,3-7.2,7-7.2c1.4,0,2.6,0.4,3.6,1l-6,6.2H28z"/>
</svg>

PK
!<��wnXX:chrome/browser/skin/classic/browser/panic-panel/header.png�PNG


IHDR00W��IDATx����������m۶m۶m[k��;�m{��}Xe���s��Ru�W٤w���AK���e?�*�|�.�d��������5�p5���3�Ћ�_�[T>O
+�;��h//����CBT}x�����k��jP��"Y�
š��>g{'���*����|T���*vpQ��N*���g��Ԣ��dq�q�(`s�S���C�P�!A�؝�:�Eq=�E���l̘��~멱��\��|p��wvSi�>.F2���f����W�2�*��=<T�>��%�Y��3�0������/eo���A*�I%<L���J�
���˘�Y�ɳ_[0�%��f�}�R�O����m\�������,ncfb6Fﳜ��We?E*
��]�ԱhA��h���
f9=^x�ڠ����8@d`��POAA���``�0�������P�*䠑\y�i���H,��?�,`��뵜��
7�梂�mD��;
��`�oi���6��P���T��A_\��6,��.㯖�-�|����(?4���ܖzJ�j�Y3�?�8��E���&f�Fi(��Z��;:)�E\�{�:V.r���TꫯS���ɢ��w�&�1�aý>_d��ܠ-.�E���ǃ-����؃���Wi��s�ު
*�o�9`���Y�t��䠜�uT�y�~����P��u��u�����=B�`#XU|�4�.�Pv�F��]v���Ӻq��jrwQwn6AP�3�	e�
�`U�N���*���مG��w1��ఽi��{ӫ�kX(�`U-0�e�%-j�L.����:oְ5��IT���B�`����s�#6��[��!e��GH�eE2�V�_�SCz���Iuom5A=�B�`�j�yncO*�1�yRݾvA==B�`�j��.cO�7��\w��߸�H=��Z�{��a��D�Ivo]��WH��`U-��]��It֑5ϝ)�F��xْ2c��D�#���먯�	��7^�٨\���A���!�F��xȐ��R���zFvl���4������q=腐�L�>0�U����~t�h
����G�:s�h�>����#;sc?�D{�V��%k�߶$u�th��׆�ԑ��i��lJ=`��P�CA��L�������W�����*.��驮$�Oa�;�Q���jqV��KԿٟ��U���G���`�E�p��dZ�m�wg�S�Z=�5Ԗ�q���=ܻ��0��O�������0$d�phi�K/�U!�J�z����h$���g��Fi8ݬM8�uS"-��qz5����F�sfRwy
���&�\����ΗF��?��K2-������M�9)��6hZ��%�R�nJ
�����Io����˞7j�X�ASa��\&)qß��i^|ޘ@�p�b�Lc&f�,�h��W9�.����
	�%�:8|�����wk5�A�8 �v����
�eJ�iJ2-rh'�����L̖�Ax���R�+���-/r�G�D�����Ҏ���LdcϺ
3����S�������k�>0%ҏ�d�o�҂8��"Y�D6f������N�&E��a���a���e��w�'���D�*���q�3��kP��"��ґ�l̐&Z�%�/�����{��O��g�2=�~� �K엷2�p5�Ez��,dJ�,,�v���۴���䙏����urC�¸�<��Q��"C�j��[��5�d��md�`\��P�Z�p�72#��\��IEND�B`�PK
!<8w���=chrome/browser/skin/classic/browser/panic-panel/[email protected]�PNG


IHDR``�w8�IDATx�x+[����޶�DM��m��m۶m�6��ڍ����_����i�ٓ�X���oώnX��X��X�9q\����g=���3���t�J��j5��N���4�&���jM�W�R5�����X�ȵB� �)�Y%���S���2��<����
��N�8)ʊ��]���kp-�`-r r"7j��
���S�Im��v4��%.j_�K|ԱE�Ν*u�֨kou�c="�������=u�b�5�k�9�9�5P5Q{�B���tޕ����a@�b�&����!r"7j�j6pm��8������z!v`Kޥ�+b��عI�unS�c��O/��^�8��(C�,�Y/q���b
�&5�P��zAO�
=.9�G�M��]Ф�3�v+������M�N-q�l"�BN�F�+�PC/W�@��=/��|��o�>^榎�� �c�e��{+-B-�D�+}�'��+zF����c��uy����J�ԾU�a��إ�d���0Bm�^���+zF�<��0ˢ�D���(�3�y�yP}���m�<���
Ey�LzBo���g�x���i��L�j���`xV�C�4�n��E�/�^�3z��3a�L#�}��4ʻ4v�o�Q�@<�X�9��W�|�̂�x6Œ�5���m7���m�C�||7��[�h��"<�h�w����Ϧ	�ĉ�0fĬ�9S��vB��
g%Է�h��-2E
������tO7M�qS
�0f�L�
3bV̌�|�s#�DC�B'�ەK���\Ӏ�4;9I�
3a6̈Y13f_�;����d������.���*��a�*t��7�D�Sz�K�0fĬ���X����
�D��K;�}��3��y�����;!��k�(̈Y13f��>ۇ����>ȗ(��G���%N
�I�۵�-��$`�I�0+f�m�	�L�_��؇������eʠ��|<M��"��03f��1��d[u+�����O�H�۔{���M$��@�r7��	؀����4;���˅�x(ȍU����5�G��YP�X�	�0������Z�/ǧ�8��+��>Lxu�>@��b`6`Ĭʅ�?��t�g;۬��b;
p�&N��)��B)��X�I��0bV�L�/�Ul(�B�
�8��]��g?�f��(��1����@?%��4�ϿR�׾@�/z&�d�N��l���L�z�w�����N�M^
o�%?��w��f���Z\-f��i�Ͽ'��ϓP�t�
����^[���/vPt�9qô��yt=��M�����n؀X��{�u����z�)٪7c�o�Q��Em\H�z>�^��i���#޽uM���^�ϪVl�gG#�eӚ�_�l�6����0u��妚V`v`x��Y�4*v
��^��ZU�i
?r;%���
��h�ߡ�#h�=N󍾯|/�L������=�Zx^�Q��o���t���3�0'Gh��ćh��JF1���f���e�x-�U��_�"?�k-�--j-r��~6_����x>%B�����S�3�j��?�eJ���ȷN��7{�EC��� ��12���B ?l��7�\x�`v`�,�`:o��k):'[�9�N��xug[�^�\�yX��ϡ��1Jxص^x�`�`�L�S1�-ռ����G���ۂ)��}5R�y�c�j���#�{;0K0��[�����6j[�&Ŷ�j]_Lc���@K豔sᮢ�YJ�W�Px�`�`:/�?w=t�q�e�A�;`���X3B�L���Lb@�z��yJM
8���
�`y�g[C~����,Ԕo��25����ԇ���pMy���ɠ��E�O�3�%�2�|C~��~�y�B-��0φ$�?���ו#Y��B{C�S�54���V���/uR�l�X���.�����&�|�0;1AM�^���%��Ν�aC���I(q`qF��L�����%���&�O�S���[9'����"��y�RV��u�,��AZd�S���SМ�Z�B��v^lY�J��9���`	�������,̷Q_�>�ϹOG��Bk�%����A��/T�T�ߗ�z��9L��	��`ʛ{��w6-���τ	��; C���JT��5�Eז��n��:�Y�՝el����U�r�f����Jv	���q>8bh���"�X�L4t������;0~���J//Z&J�4t�^L���Ѐ�[�;q.7���K_
��R������/X�;eh�G���I��0@��)Y���4�����Ѐ�d�󾿺�p�Uu��]4=0�?���J�פU�9��͸��	�.���`kh����鿐���l���o(Y���9��eq�������ڶm۶m[c�|��g۶_歟m�=��NU*�qnF9U�����=��^LI�O�|vl�D#�`�Ө�Q�/���<��dH��A�V�����=���)��E�|����DL����˞�؏jL�/L���T<�}��g�[��b�1r#ǂǃ%Lak�e�|�z�l�ӧTzz�.��e��g|�1��Op�Q�X���+�1��tl���Ob�O�O���7�K���g�����X\|#��X��X\X���}ѵ_�w���^�X���U/З{�nh�ֻ��)xL|�;����W��H�0��\�%V�
�@m��=��Z��P�7��{2��p���k�X6�8���R��&v���!,aj��X�~u�:A���Qc��kkԽٹ=i����ԫe�7c��ތL�;���@�3�UM2u&�u�83"�t���t_vv�N��?�Jo|����=��ï4c�����H��?�گ�j�1��qg}W2��J'M6�?,��{���˗�t��+O/��|�k�5b��3���i�C��9_oI8�^Y�V�S�J�K��ڴ#��/�f��!,����J�ĝ��hA��WJWMj���N,Y�M��a�X�v0�kҹG��s~�1�TH/�+��]�v�7�ٙm[�Y��a+�	�_�p(
x��m��qg�ttJE� őL����>2u"���4�0�*Z�0��`CX�	���w�ͻ�H5�ʣ�Eժ�V�����npt�L}6�K_:~L_�t�o}�{7�����1E�60��`g
��P�?Ŝ�m��ٗT�/	&������ܧH{����!�\�w�TG�Tҝ���
�`�g�r��zvk�!���^V�$�L`#XY�6β�V��Q{}��M���{�0�L`�̹�D�w�����m�L�.	,��ƥ���ɻC�ad�26��
�LCܾ��L�M�fj�,��ga��xM6�A��O<�������wI�7.D��"X�6V1L=�~�J�ʵ�m���t_YX/��ƴ�1S/��0��i�u��*��gO�n�mɐ^X9��S�eçv��lB�Z��_{jZc̖����k��%�tq�;&D-�Dm�H��L�0���hB8P�p��
YM��_ҋF1xr�j���_J��L��'��df����jc����u�?(f4���=S5Im�����t�1Ј�(�����_��D�$ۚՈ;RA=�r�7��5�75P5Q5}�1�E��
���S�r���7b�l9�9fç�e5��5���(^�Tŏ]�Z�/���_m�ےA=�<"�wXE�Җ���3�S�P����ї5O��dX�鷞�\�u3�蚸��I����5�X�$vn>�H��L��@-�_��l�6D��ۮ��=���oDK�f\'@�0S��z~�����.tr!'r���+9��m��F<$387Kqߗ"��S��4e�Mt���U���I�:]�N'�*j`>�5��^�0m9�s�r���An��?�+�(D1�������L�55Qu�!���-E��Y#"r!'|�\��hL��F���ԋ~઺?Ȭ���ЉELb�C6�|��Xm�#Dn/7���U�����S��U�7��oLs/���=����_��71���%k�ߴ*R�(@2�T�����_��/��_��^���J��SGDg��)��7��p/c�|e��c)b[%��
; ���*������u���*%
�\��5pܿ�*�����d%+Y�˺��~PIEND�B`�PK
!<����9chrome/browser/skin/classic/browser/panic-panel/icons.png�PNG


IHDR@��y)eIDATxb�hhh�bE@�u
$UDa�qw���w��Opww'��MpOpwMp�pw��A]��]��g�~��B��ԛ5���� �����~�:B"��w�4�8����
,@�����ETG1H6J`���Z����茟�i^{����m&�M��C֙�^�@E���z�
�������j�t~c9���Kt@}H�͏O���J�4S�}^
��O'�%ԶX��[��`v�5v�5/�o%�^��r�!Nv���]PK�n ���["�;p�0�����n#(1�#h�d�;.[M�Fp��;B�3��+l�K�[����j��8�Ƕ�QQ�[Q�~'����~xpu�
�f�����!�:�}fX�!F)�/�-~�a�Q���Q�t�R��r�D0��</f�1�zC��(����{�pM�1�9�n������Y GQ�p���5���� X;�i��9
�1C[�b���N����N휟3_H�BcF�T����q�ET�d���`N���B���j�;�B��{2���.�R�c��{3tk�L����G��o���X�'>�zn�rpJ�-�J8��uS@5��8qIEND�B`�PK
!<BNƝTT<chrome/browser/skin/classic/browser/panic-panel/[email protected]�PNG


IHDR� �"p%IDATx���,9���m[k۶m۶m�ֶm۶�l]f�U�թ373�t��T�
�}�['_��2�>�>����N�a0�����춙��S��9����F���d`
܄Q�:����ה�9.�8،�w}6��3���O.f����
��L�V�۰z+�x\[%��_�����;�%���f���	5�ז�(3%�b:�aG}{�qA��{��`5��t��G��ok<��M��JV�O�bG}{����U*
�:��-�C0��(���bK��h���w�5�?������?\
�-7#0���Dr��n����sg��G3#|(�8�|�S�H�0,��v=���>�0�~�m5`�Y6��I��
M`���!��7bul��+���0��%�a��
��r��a�R��[�g�8�|?�I`X��^��²M��z�D�K_����o�����|�2LFq��%��5����!�1]�{M�v����d�S	���u�k�4��L�a]l����X��#�(ڸ&<�-�v��Y����0�kxV�+.�
��@/\��{�
�E�f�B�*��Ӕ�^�0�h	[��x���Ö�F*�+�AK�X܆�^��O�o�\�\��7��q�Hx��:���8���w�m'����B[�ߏIf��L�χ>���&5�p���������7�ua��K0:0-��[���u[��a�� ��*c��qdd�a����q-���[¯�//���
���
����v��V�C����Mp�H���3��n�6��#�໠�
��>d�~#�uD���2��"���0Α��_�v�O�m-7�2�o[��U9
�"~�m�>��f����:Ku��Z�n[���ĥHq�u?�:�Q!�XPy��Ҝ�za�7��Rp^����^be<w��
�9���	�o�p�Y��AN���^�ݯ�Y�>~!�� �;勺���0���l��5�@�����_�x+�D�> ���W�#a�{N�&���8:br����	X
C��D����"P����W�P�$�~�5� �����PԨ�`I�?������^&�@�az����4X'��{�/֋!�e1$�3��?�Ő�a~z�����b��*��c$�Oq�B��xu�f�^
��Rx9d��W�^w�V�+�/�&�m~,�V��'����N���>�'���IEND�B`�PK
!<*�,ZZ;chrome/browser/skin/classic/browser/places/allBookmarks.png�PNG


IHDR�agAMA���aIDATx��3�i���0����6���=��b��m��mU)�&�m;����3^��?����\l;�g4�[��Jju\J�5��w$D;(�P�lH]�^�����g(�u���l^����{DEF�`�m\��/�k�a2k�	�:��C�o�a�R
�(U�J�������F�� ;3�ͯ0)�0<��� �Q]��L2�����<~�
�$f�8g�~�����(GQ]A]D��+��`Ֆm�
�)����l"�;9Oi�~�ֿ������N%3mRdԭ@:/���K��?~���o�\�N���Y��09�#ީ[�#cyn�Ů]'��7m���`��+<͌¶~k8���#1�7�v�)_�^���ر��y%JF6�@�Ib�ʼn�wy�0�?�|���h��/�dcU�]�����Rɓa�_���yp�N�x���$oh��<��B�,������,>J�T��������h(|f~�h�S�
Ο[��.\����λ{_��d��yߦ��~��IEND�B`�PK
!<Zj����Cchrome/browser/skin/classic/browser/places/bookmarks-menu-arrow.png�PNG


IHDR w}Y}IDATxc�`P�����"�W555������q���E������&�9��?�/ZZL�v:Ÿ�psK��Ɩ������#�G�mj��Z��?�?ҿ 
`˕h�\�IEND�B`�PK
!<v5��U
U
Lchrome/browser/skin/classic/browser/places/bookmarks-notification-finish.png�PNG


IHDR@@�iq�
IDATx�[T���R�%�������BqwB]q*8����Q���$�7��=�'u�?��㜋~���;�����!>�g���\2�7�;����Z�6�`X�m#{��w������v�)�n�z۩Y�v�h�?7�u*<S%`�2d4�A݃w��Ò�H�;���E��h軕�x��E)�ȳ=�7i|�}�v�b\2��M�F.�n-w"��A�x�JE�a�i߆��Qː��U��q��� ��Rj79c��}~�%��;U*��F��C�2.Ǫ��.s���j-*�1�w�q�R��~fgI�*��$FzN�va�aK��Ņ�Z������!��lj7eU.?�L�eAՉ�v�yu���we�s/2q�A9Ɲ%�	�h|�E���~K��}wG_�iZd�����,`l�FՈ�ʫsU�ߗ9ğ��$��b9���m(���Y���D[��G+��6�yT�#}y��)�͢SWg3qΜֺ,���~C���+�ū&|W���_(1��,�9K�N_ߓ�:���W�V�c���/���1�� $�:N[���,��=W]�y��R���T=]b�# �}4g��oJ�����ȂJ}vڽ���BUh.9�h�1�IRsL_���,�S���!^knƨ��-��>O�i��E��ꐳ`cɝ��:�����G,s1﷝�Y��!�d-9���>QCʠ��y�zd�+2�Ep�
�2(Ql\xo!3v�
K\�<;W���]w;���o��wH���6�f����8,�!�l�n���J�Bx|�Q�� Z�~��kgv�7��~�-t'E����"8�쒴d�JO�
)wP�9���H��p؂M164a^�O�F�,pyl��g +R����H�.�>���8��Ã�	dLvY~1ixJ᪮�7�v_Vfu��S$=%$���ٹ/�v��iψ�=���L`[�`��?>�APt€���{3E��1Ҋk��&
�ql�C�i~&�1e�/�'��arf�k^_������1�Z�1�d���d鱆T�O�m���R�`��g�iǹ���gʱ��Z.��W�:M]�shb�O,��na����pDV���#7~���:?�/sC��x&aЂ�_:L���ח'j���՘�R�1id��d1a)Yyo Ep�&!�Y��u�y���Ǝ3�d�A�� ڴ�8G�3'դCl3�,��|���}�"J�)�Y����������y]l��]7o[s�������hTC�%G�F/��uD�����FR��r�QR��@�:�
���"��%�r�M��gH��l�&���b�Ò�a����,�bLBIc�O��թY�q�5�ĝ,@�[緾:=�rtb���:RL<�O0�&�D�@TC�	L4Ib���ͼ�����f�N��d龚�H���Og1Sa\@�
-���5Y���{�z�ɖ�M�ĠJ	\X�kdᱞZ�g,]�6����:�ݴ
ˬB��MB^�R'��|���[�d<&Ժ#�@t�.�n�m|6�b9�$UH���I���o��/F��w�@��#�����BRX��/�:��ns�_:�ߖ0�Q�q��Y ��/<�T��BWL�T�!�2�ɇ�.�~�z����?�����h�2v`��Le�
el	��Se�/"e���a���"��:3jc�
���Q?�Z�����7�Z��C��EPΧJ���W1(�H*�������P0����o����d�!�<���)!+����V�,y����k΄^��O����Z��ʪ" P�a�>��ױ�^$ȃӿ�?�b}ߛ��[��B}�
h�j.���a���R��{7#{���㓿KL�`�/���!*/�B}�
���b!ZCaG�k\�6d-�>y��	�K
B���s����O�eL)Y�Ӌ���u��ۅ@��{n�-t"��\�A��Ln93k�y�)��f�Ƞ�朕�#r�طJL��v�uw{/c��
��M�q�l�d�XđA�f�S�z֮}�S(�@�<��y���=�&3rO�O�H�d�O=O-��c_f�O#Q��G���-/�s�3ûߟɔ!�2L����˿`�k|;����٨s����{
&�ܯ���C����7���[�6���&�h2LRǙ�wA�\����Z��y5�_q�4�7�	z��������J�`G�
u�t���|�E�G�a0��\q�# 69��~,&�
��|��)�T>��p̸AJ�b�%e/�:|��ѷ8\�����&2�(#�H2fkI��Z�wCI$���Z_�O�(48��M���#��&l��wи�N|�:�^�Aq��:N;�޺U滟�E�A!�.�^�����E�^�`q�J��y}�Е�7�G�դu����^k,���ڞ����!�2,B~�F�+~Ŵ,6<��Y:��B��y�M��kO���
�M�M�����"���ښx�ܖ_�������f���Yqm)�%�p�d2CC&���1��L��I��d�}c��;���V�f�2	���V��HO�&ړ�Q�L�5�2�3�����7�/2q�����%d�')��o��`Sж���t��,�5~�V+��"�Vx���.#�M�(����Ӵ^�B�!`������0�(`��p̔8B ~�,}�;�����|:jw�c��������Y�#��}�5&a�]
�x�
��-�����tFcL�z��L^����Ϣ@"��2f�>ڶ�9>4 ���""� ���f������V�s�����ܖm>؞�~�8c�f�N��FX�y%`�〓 �%8-��]�bF�n�u�1�6E�[���V�AF!U��G��=C��q_};����>{ˍ�/rۥ���85	�,`�A�,}f��F>��&���\�%�V�����]�Vݜ/���]oI��_�	!��5��	��k��M�:"�9Pa~��k�qPrq��n�� 1���m?y�%#�FS�Kd�s���Yǜ�_MƜ���� R:�N1��@�-�����~��K��Y��}IF,�ۀ{>u��wDג�e��������ɗ�甚{�����;ߟO��8~N7��,N��B`^���6���ߜY�X��Wnq5�lm�YAۤwDs�˞ �V1�e�GT0�3\ŕ�� g!Խğ�_m�ɵ#�B �`S�4EX>�mI,�@���XFK�zS�:ԇ�ۙ�S|�F�B�j*l��/▨B/ѿ;51�!��>�7���*�Z�Sd�>���*6HJ3IEND�B`�PK
!<���z!z!Ochrome/browser/skin/classic/browser/places/[email protected]�PNG


IHDR���>a�!AIDATx�\xG�}`�lK�La��e�����u���R��0��affrt�=�ݼ����D�����F�s`�������������������m���l&w��{�k�s�~��Bzߵ��Gm"�A�I��}Fl$?y���|W��'�F>�-��YO��)��(���\�Q�`%�4p�>ĉ�uBj �f����J����I��XJ� ��SD�%������ݥ�m��Ll5���oX��^�ϟ�ϟ�8�����*"p�Тߖ|q�S���#7QK~�]�us�^� �PJ�5�;@�""��?py�8�|�3p9Hf����h���Os�L__�O�ںћS��A�yWFS���"�R�:>Ja�͠��I��~�$����5�߿���f�;�Qoy6�dD�G�*�.zW�5'�w��w[@�%<��g��!�@W��q-�>
�*�}���L���������w��kɷ�{��2��[����)"�f��X@�ѝ Y�p���G
�rLbM&��ߋZ�s�>}�\���#�Qp�
�?O��G�o�VҵK�
�r���N`t��H�X]���S@s?���F�K�߉��y��M*�C�����T8�.΢{�ȧ�,��koi��Ko3�!F�
d���p��ܿ�* �%���d	h�x�4r��I�0�@Aq��8�#*�#� o7��E!�_��(.����y	T��T����Q�?�#��� �.�Y����Em�~]�ێ�0I"�R�k��(�VLްћ,���R���A�Rn�h�N�B���d�샣7SD�j3�;y
h�g����Lऐ�-zw]�E�q*�a�B����?� �@��ݜ]KQb~��R@s?������7��A��7@�BP�2�XG�{�"�]%��_�����'׏���G�L�?���Y��.v��*[�wN;c�ݑ����uT�O��p���~��W���;��ϓ<G��q
�ȴt\�oR��G��q�W���Bp�v�?���8���2}�P
h�o��s���|
`��ߥ��kH�'����)�x�yS@s�i��m�t]���0�������)��'o0��(�U�����/��>ҷ˴t^t`�m
�����~&f(�_�k�)��j��C)���7g/f�44�7I�z���\��g������A*���{)�e�Z
8�?q?Y�k�Iè��x����E@������Bޗ�Z
<b�s�?�>��4��Ns��
b����~�ȰtYrh������2NFn.��R���?wR�0PK�G|��=�7K�v��c��f���Ө`��)0m/�ڦ[�.9<��K�
���w�
R܏k���g�g���z�Ө@��)0�S�0`�.%p�:�+���������-����@v?H*Pp
�8�m3�v��\I��H������:j��y�Q�_��%k
T8�Wއ�J
0j�S�o�&�l��xJ��?���K��v*=$s
L��)�~�ǻ��()����?���n4��j�Y���=����Dq�@�#��%�����o�Db���=$	�>��h0��Ջ;e�G
vYCyLn���^�y�nj���H�G
���S@�&�n�N���d��Se1	D�,3W�D�nA�6�W8���ځ���fY�YL��.þ.X�����v?y0K
T�)%�I:�	�f��P���	C�c���P�����f�l��ጒ(�d��3�������S�mr]��>�g�Q@�?��� �H����b޷Y螰��&�`�UƕpL86F1�$[ ���Ԉ8l���D3�HAW
��0*˚�g�4V��R�Pq�oe��s�"+���g�GTe��ݼo��h�t�q����� j)!�
�������
<�S
���h���r!�k�
� tF{FO�`�(��������)�ob�����S��S�0y7j����Ϯ�}̔G&0�L��1l�c�1��J�"��ja��
CՕ���P6��u
�rB&�aDcY��h8ږh���;u�E�	�0z���a�8�1�3��!�1���t�y�|�r+�\%�drX�/�"y�����xk
��}��H��1	uc�[o�sa��0a�<�'�s&��Q��*��ʌ
�aف�P�*�k���xݖhF'qD�@!z��dO�{1}W��i���:�V��/oo�qg}����u^�3q��|�H����0*14
�Q�C��*��za�ۚ�}����Y8&9��"�������T���Y�+.J��G\t��3����+a#l���
����ZOI|���-��{ѣ�3�lFO9�v��5a��
}�w��>��|���-d�7#�����2�t]��a�7N�~u
��ਝ��y!�k�u����[ݩ�c�n0�z�v�'*���N��M��Z�oչL�0�m�F��f��!fj�t%��+���Rp2_�e2*P�3p���᝵%�"9U�p)fBD����V�Ƃ�;[�cy���0~�����^�W殠�Q��B&�r{=T��F�#�V�V'�,&}����L�V��
��'7���oN&���y�S��/��g�f��	�z-'�.�ȳ]Da@����7�F��1'Y��֞�rK��/V�Yq�~b�
��0Z1�aFI�j�Y���Y���]�
� _
��/��tF#�Y2
;�11fKt7>A�K��T��N&zO`��3�uCo��J�!��#��4&:�f�W2џ	�ߑ!t7�&㬓d
?��L~�7Ȝ��'Y䤰�>��Z�v��3O��c�uOö�0>a,&/�0R�'�ap
�B"k�_�
)���ӕ��QGp#uECEU��b0���k��g�iW��r�:�ْ��-C�I��B�%�r��lY]m̻;�L��2�,}�����!�s�$��#z�+Ѧ�D_q�Z�q�"��"�o��	�V��*�\k�,HG�)�ҫ�[HO�h
F���+�|c6Ώ�z�B5ad1t�zz�~�CR9�E-���F���v�*���7�*��s�{D7�%z)����e���?�ӏ<Ht�Mg �	���R�@���a�<0�m-ˆP�&����}ﳞ�N���ZLjEEd����SD��I�r(��]}rm\�^�;�ޜt������;��D�h����=|;����|'�%ô#|��~���+d��I~�|���!h�X��s�چ�0$�I2�0|C�����ͷq��]�J�J��''�G�$k7���;U�[7b}���ov��Z�*T�P���*�D�����ovĽq4W����7؉��z�Dk�@[�Fo�&��_���'��q>�����2r�e0�D)૨OU[�(P�_��dF�g�~yk=�r���0y?~�tV�5aD_c��Z
�𙵛�>n�;(Q�|MI��A[�i��rKՁ>-�.�j5���;dJ$
N��n�a�ikb�K<�\��ab
9M�oW�VC����a>f�f��)�4����w�!��{Y}�����s�@���g�uc�5�1]�g)��{'���&>T�lTr�?f���Ea�v�*��h��6Q��{�����"P.���${O�����s���`���QA~ǥ'�P�cX�"P&��0���y#����{�GL3V�����<9��"�\�N=BOMݲ��ڻ(9��;�=F�[sֶm�'��=���vl'#N&�l챺����:���j���'Nz��z�����Wn ��
���;�����ܰ
�g�V�ȑ>��=l�~�~q^܊�QKZ�q�#���_ۿ�H��K>���o�s��4���
�d�lר�c��b��f�uP�i[�/t~�Hd���c�����x��� 0�Y�ǘ���v�ӓm��r�J8���
����g���S�>��hVA�_��
n��i� 3�C�Kx��O0�ȗ'ً�r�$�b�d����]�!;����j��VU�-�Tˋ.cY���%H7��x���%59���.�L)��:"�/8:dF�g8�L?���6��@.[��@�0�ɡ�������[i�n��$%���g'Y��v�KP�m�Q�NVa�
,���J�՞��ْ}	����������@[y�I0� A���sL�d+��?���,‰%w�����"3HW��$H{��ݿ�7��U$�p�e��T�}�Jƭ��Y��[;I�V�-�zTo�!��4���wM��n�˭�#RD{�!o;�_�����BKRQ�2r��8���;Y�!CE��!F����8�ovN�ijo�;��֭�
�*�J�jV]"��
��#EN$x��m�mk���j
����j�+U�X�`�b���C}n���Z�eŊ�գH����'>�2�Ϻ��/�N^U6��U���y �6.�o{���#���
U�2둏7��mh�ͫ~o�-�8���O�X�Zv���Ak��m���H��q�<–��Yg� Z�8�%���7,�ot����K�ͭ��m~�mӜ����1� �����qf��c�.��or��J�%��"O~��oy��-��b�j�P�
|���SH��|�)��ѫ�y�+%��/d�_iGA�>��湜<U���)�"g܀E�D���Y��;n���k0@����c��*Ucg�bq�X�s�@����!�*��H�4��N$Ȼ9���;n�\X��}S�Jr$��͎{�
��鿦��m{Z'��zQK���dC��g�z�mwECr���R�9j�2���J��bk$`��"Ao�#ݯ�(���!|$	|��b��˾E�f�*��T#�Zq�\���H�"�d����ʴ��^	�:��셗T�RU�y���UM�
|�-����$	����	�z��Ϲ�{YJ�&�]r�M��
ѽ%�oREV`&����Q-�.
:�&	Ti�=˂�tI�O/NZ��;qw�&���)��,� ��c�u����\Z��Q�!N�>S��3Z%���c���paj�n���
�-�+���*�l�ܯ�`(�*^�u/���.H�J�� d�0��w���%��D�/�6�k���.�ӯ:x�۸u����L�Ԫ�9�7N.�;r�K��[�-<�׋G�_�\�Ż0���1�w�~}��!����J��sy��M�J�'��xD�7%�/�wD��E�K�s�
��v��j<Emu;k
#a�9d�n�N�+��x�V��B'V�SOЩ^M�����CD�}�Uy&��M�2h�~�Du�u}���bg����>S^��ݗ)��o��u����!ld��etNm�~S�����3�Qx��)u½�t1|�O����8$��NVCt������l��|k�\��$�3GtO�j���?[7���/�G�`sə,\|!a�3_7��=����Oq�����%��ك���]�P:�����˂� )�3'0ţ�L�t����S�#׉�b��#8�@�]Á���&����cf��3#�yr��<��lѭ�=v�8g�,��i7��/�H��zšU���¢ё޽'4x$���S���=�7�P񫞠ša(�S�.`+Q���S}f�7Sxd�H=.<{Ml�ɱ1��h]A�Qp�t�šef�>���=�a�d���#�� �<�๢����p�˫���Q��n]@�DЃ���;�.5�(�Y)�{Kf�0Y!S43H���r>�g�%�Fv�L.p���ˣ���D�7���C`	)���[ɔp?>�u5�n=�r2�|s�Erd�W���K�Fqq�$�?�����l��p�Q�K�V�j9c(����wO�o����8m���,Β����\A����3s��r5�c�9��=�J�?1$����3�Z�YA�+���LY_�~���’�(,�bHJ�0��*���~��B�w�6)��$�Z6�$<42�y�Ja�p�+Io���_W��L)����A&U���M�W0�n��Uh	������޼��%"c��)��OSI!='���L��l��4@�J�q�}F���BV,���l�����g��&U��<Io��a��]R���9:�k��*�>~+�� s�!�A8L�S�&��<��1#�{��7'���"A6'�
s�.y=W?�aeɝ��W�E�֙���Y���蜯ͺG�<� 2�|��>��z�P[
��'S�����d��⚻F�ˏ^��m��J���#5�ps^y�WXY�ٶW�S�;�	DŽG�	��d�����&�P⤞�y�ӵ�sZsm,���;��N�����c�۸j�d@����?�&���#���l�/���aD�L}f���|��)�8'�t�
*%M*	�����Z��<�'R�hG�<�~�V�x]fI��#��x��aRA0mSJ$���!3�?�<�p�{ؚf�����o�
�/��|
�DU[��:@X��n���Ra]׾�"����H-�6���R���h��ʛ�!|R�%��bYL=��E	-a�Z�q�%�ڗ��J�mi�s�{?]�"q����:��!�v���	S�h{���wH�K�
��v�K�xO{O�M��eW�Cw����3Hɒq���p�s�[L���#��g
a��e����Ժ�Hl��
�7��ɶ���o���q~|~e߉���5�S��U��:�U}%C�灤��k9o�N?}������W~�󙱫v�"6�4�LimL�����vq&�`�N ���� �*ņ��-oZ^��lJ<�F�7�B�&d+���/�H��#p��J���<M�0S��O�����߽�7�sP
$Bܴ�m�i�{L�[�)��� 
O�>��@T��P�����CS��”���6)
�KEV��c�{����;'�s>� :��0�8V���7��;��nSD`ESt4�^=�i'E0Em�D��_[˿Z�s�Ɏ�e�\�-E�T�,��u_�,�'�En�����P%��&*���,ޤ�
O Q�ߊ�Q<�>\�j�&?��y*"��,������+��1����+g�1E� �ȯ����(
XS�9b��qHy�tw8�`1B���W��i�*�P����C��
?��Yl�N���8<�Wx�G��?���M���[�:�b�g�r�O�1%z�c�=c�/�+�2������O���E����#��E]��p�2�E_G70��o��6
\���H����.Vt��w7�L�c$6[J6 �R��$���'�.���D`�k����5<�r�Q�|�u�mo�Y�VRk��#�P�7��,���꒩��[�g��R7F �XU
�r�����xXP�\/�҄q7yz�bw�В���Z����Q"���4ڵ�8�HF��5"�F��I�4E��w1��2�J�R��4�wCvi�ghY��u����6�k�7]��s��h�E�3���b��(퀡D�[`�R[Z�d�+�i7攭�X�N7u 7�1l:6_��8�\���_%��D�q�9�z�ŘǫL}�"�<|N�GW����V4���56�N��f��]\��ln��R|tC5vN(��_:���?l�^��ݒ�r�MrC�[�1�&��GHY�U��E#�տ��ǩB\�L�M��F�o��D�:�51�q��z9�
"�Q��%���-!�8Ҡ�8�n2p҈����W�����yAzIy2�n���/{�Q{�}������#��B��N�^��7*��[7��]$���'���\n򹮀oό��$�?�N�p�"��SD�[,eЌ�1R5,>%*�gHI
��?d��˓�g��o���׎��J�ͼ 5-��7n���)�� |��s��()1���(�qg๹���h�]�^�A:��\�+���Ff�9R��?��/ҍ�I�_��S��ph��h��r>>;7�U\�\�Sp��A�d�^���<"�=����s����f��E�k�u�lExޚgk���UY�ҕ�.�,f���M���]%h�������j3�;�J5`mK��v�\��*x��n:}�~͆�0���ɸ��uLj�y 
���o���L�E����b�Y��VkG�Ak�h֭?���஑ݧ`S ���k>P�sz^�M^Vk�ڸ�S�f�~�Z��Y����|
	��A�C�~�@o[��o�5��~�^��wA�!ZQ��E�^��=��ߌo��%��^IEND�B`�PK
!<�#J7<chrome/browser/skin/classic/browser/places/bookmarksMenu.png�PNG


IHDR�a�IDATxcp�_��$u�l(���mX���,Y�З��h�y�$���\x+3�l�� Ο?�?���+�\���F�:�f `�a�/n�������������w`5����l����)��?�����SGQ���������}��_j��߼���4�¨�.�g�H�ž�;>���?1��݀X ~2���G6DP�$EHc8�SIEND�B`�PK
!<\Ĉy��<chrome/browser/skin/classic/browser/places/bookmarksMenu.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg">
  <path fill-opacity=".05" d="M15,2H1v12c0,0.6,0.5,1,1,1h12c0.6,0,1-0.4,1-1V2L15,2z"/>
  <path d="M3,5v1h2V5H3z M3,9h2V8H3V9z M3,12h2v-1H3V12z"/>
  <path fill-opacity=".9" d="M6,5v1h7V5H6z M12,8H6v1h6V8z M6,12h7v-1H6V12z"/>
  <path d="M7,2V1H1v1v1v11c0,0.5,0.5,1,1,1h12c0.5,0,1-0.5,1-1V2H7z M13.5,14h-11C2.2,14,2,13.8,2,13.5V3h12v10.5 C14,13.8,13.8,14,13.5,14z"/>
</svg>
PK
!<t[l�YY?chrome/browser/skin/classic/browser/places/bookmarksToolbar.png�PNG


IHDR�a IDATx��C�aǿM�>��I�(i��M�m۶��Nv]��~�;���QuRoR�i�EkP��'b��3����+l"�������jy�!m����4�7�Yb�7$�)�eޣ๏r���R�O�!��$2�GHOBY$�G�q=A��Yσ��$a	vQ������#��͢i�p�$��sp� ������A�r"��j��5I�ٵs�)��k�
�W��=����h�.M�q���L�5y-dA*Ǿ$�l�SԖ#��@Rf����>7�
^]#�IEND�B`�PK
!<b��k��?chrome/browser/skin/classic/browser/places/bookmarksToolbar.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg">
  <path fill-opacity=".15" d="M15,14H1c-0.6,0-1-0.4-1-1V3c0-0.5,0.4-1,1-1h14c0.6,0,1,0.5,1,1v10C16,13.6,15.6,14,15,14z"/>
  <path d="M8.4,4.2l1,2.1l2.3,0.3c0.3,0,0.5,0.4,0.2,0.7l-1.7,1.7l0.4,2.4c0.1,0.3-0.3,0.6-0.6,0.4L8,10.8L6,12 c-0.3,0.1-0.6-0.1-0.6-0.4l0.4-2.4L4.1,7.4C3.9,7.2,4,6.8,4.3,6.7l2.3-0.3l1-2.1C7.8,3.9,8.2,3.9,8.4,4.2z"/>
  <path d="M15,2H1C0.5,2,0,2.5,0,3v10c0,0.5,0.5,1,1,1h14c0.5,0,1-0.5,1-1V3C16,2.5,15.5,2,15,2z M15,12.5 c0,0.3-0.2,0.5-0.5,0.5h-13C1.2,13,1,12.8,1,12.5v-9C1,3.2,1.2,3,1.5,3h13C14.8,3,15,3.2,15,3.5V12.5z"/>
</svg>
PK
!<m����8chrome/browser/skin/classic/browser/places/downloads.png�PNG


IHDR�a�IDATx���fA�϶m۶m۶m#8۶m3<��͸�km��K:5��L��H�C��x2�qY�x�a�J�J��ߝס���O�B�y�%�_]�q�0�p�̗�R&��<�S�q�s�oGpT7�pN��}/�iz�)�}_Om��[ՎpƼ����w�`Ƴ��򲟕�в[�	g,�U	sE��I������7��7��0�E�L��E8G�w�0�iYw"�$��EM�%�|lR����1`vI3��.w���t�x���kzX�%٬hfr��vcd2��Q�ݼ�5]ea��9�)��f�=��鳳hGv��Nt�LB6!W�E;�y\�qZ9g��0�IMw�兺�v����B�DÑy�:[R;�\I}��KVR����_]�
e�j����1#�]�9��GIEND�B`�PK
!<�sW���Bchrome/browser/skin/classic/browser/places/editBookmarkOverlay.css/**** folder menulist ****/
.folder-icon > .menulist-label-box > .menulist-icon {
  width: 16px;
  height: 16px;
}

.folder-icon > .menu-iconic-left {
  display: -moz-box;
}

.folder-icon {
  list-style-image: url("moz-icon://stock/gtk-directory?size=menu") !important;
}


/**** expanders ****/

.expander-up,
.expander-down {
  min-width: 0;
  padding: 2px 0;
  padding-inline-start: 2px;
}

.expander-up > .button-box {
  -moz-appearance: button-arrow-up;
}

.expander-down > .button-box {
  -moz-appearance: button-arrow-down;
}

#editBookmarkPanelContent {
  min-width: 23em;
}

#editBMPanel_folderTree {
  margin-top: 2px;
  margin-bottom: 2px;
}

/* Hide the value column of the tag autocomplete popup
 * leaving only the comment column visible. This is
 * so that only the tag being edited is shown in the
 * popup.
 */
#editBMPanel_tagsField #treecolAutoCompleteValue {
  visibility: collapse;
}


/* Bookmark panel dropdown menu items */
#editBMPanel_folderMenuList[selectedIndex="0"],
#editBMPanel_toolbarFolderItem {
  list-style-image: url("chrome://browser/skin/places/bookmarksToolbar.png") !important;  
}

#editBMPanel_folderMenuList[selectedIndex="1"],
#editBMPanel_bmRootItem {
  list-style-image: url("chrome://browser/skin/places/bookmarksMenu.png") !important;  
}

#editBMPanel_folderMenuList[selectedIndex="2"],
#editBMPanel_unfiledRootItem {
  list-style-image: url("chrome://browser/skin/places/unsortedBookmarks.png") !important;  
}
PK
!<�
J:DD:chrome/browser/skin/classic/browser/places/folder-live.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg">
  <path d="M3.5,10A2.5,2.5,0,1,0,6,12.5,2.5,2.5,0,0,0,3.5,10ZM2,1A1,1,0,0,0,2,3,10.883,10.883,0,0,1,13,14a1,1,0,0,0,2,0A12.862,12.862,0,0,0,2,1ZM2,5A1,1,0,0,0,2,7a6.926,6.926,0,0,1,7,7,1,1,0,0,0,2,0A8.9,8.9,0,0,0,2,5Z"/>
</svg>
PK
!<E݃#pp;chrome/browser/skin/classic/browser/places/folder-smart.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg">
  <path  d="M14,7H12.9a4.967,4.967,0,0,0-.732-1.753l.782-.783A1,1,0,1,0,11.535,3.05l-.782.783A4.968,4.968,0,0,0,9,3.1V2A1,1,0,0,0,7,2V3.1a4.968,4.968,0,0,0-1.753.732L4.464,3.05A1,1,0,0,0,3.05,4.464l.783.783A4.968,4.968,0,0,0,3.1,7H2A1,1,0,0,0,2,9H3.1a4.968,4.968,0,0,0,.732,1.753l-.783.782a1,1,0,1,0,1.414,1.414l.783-.782A4.967,4.967,0,0,0,7,12.9V14a1,1,0,0,0,2,0V12.9a4.968,4.968,0,0,0,1.753-.732l.782.782a1,1,0,0,0,1.414-1.414l-.782-.782A4.968,4.968,0,0,0,12.9,9H14a1,1,0,0,0,0-2ZM8,11a3,3,0,1,1,3-3A3,3,0,0,1,8,11Z"/>
</svg>
PK
!<�U�R5chrome/browser/skin/classic/browser/places/folder.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg">
  <path d="M14.5 3H6.914a.5.5 0 0 1-.354-.146L5.146 1.439A1.491 1.491 0 0 0 4.086 1H1.5A1.5 1.5 0 0 0 0 2.5v11A1.5 1.5 0 0 0 1.5 15h13a1.5 1.5 0 0 0 1.5-1.5v-9A1.5 1.5 0 0 0 14.5 3zm.5 10.5a.5.5 0 0 1-.5.5h-13a.5.5 0 0 1-.5-.5V6h14zM1 5V2.5a.5.5 0 0 1 .5-.5h2.586a.5.5 0 0 1 .354.146l1.414 1.415A1.491 1.491 0 0 0 6.914 4H14.5a.5.5 0 0 1 .5.5V5z"/>
  <path d="M15 13.5a.5.5 0 0 1-.5.5h-13a.5.5 0 0 1-.5-.5V6h14z" fill-opacity=".2"/>
  <path d="M1 5V2.5a.5.5 0 0 1 .5-.5h2.586a.5.5 0 0 1 .354.146l1.414 1.415A1.491 1.491 0 0 0 6.914 4H14.5a.5.5 0 0 1 .5.5V5z" fill-opacity=".1"/>
  <path d="M15 13.5a.5.5 0 0 1-.5.5h-13a.5.5 0 0 1-.5-.5V13h14z" fill-opacity=".05"/>
</svg>
PK
!<�!�w��6chrome/browser/skin/classic/browser/places/history.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" fill="context-fill" xmlns="http://www.w3.org/2000/svg">
  <path fill-opacity=".15" d="M8,0a8,8,0,1,0,8,8A8,8,0,0,0,8,0ZM8,13a5,5,0,1,1,5-5A5,5,0,0,1,8,13Z"/>
  <path d="M8,1A7,7,0,1,1,1,8,7,7,0,0,1,8,1M8,0a8,8,0,1,0,8,8A8,8,0,0,0,8,0Z"/>
  <path fill-opacity=".25" d="M8,3A5,5,0,1,1,3,8,5,5,0,0,1,8,3M8,2a6,6,0,1,0,6,6A6,6,0,0,0,8,2Z"/>
  <path d="M10.5,9H7V4.5a.5.5,0,0,1,1,0V8h2.5a.5.5,0,1,1,0,1Z"/>
</svg>
PK
!<�Ѐ�RR<chrome/browser/skin/classic/browser/places/livemark-item.png�PNG


IHDR w}YIDATx�U�dW�m�ƶS��Rl夰���Z���~�N�8�NU�ř�}λ��^�q<qq=qs�C�hD�''�F��mhUZV�h�͇���r�@��!����h�a�F4*-�x�8\�Z8�HZW4�����/iܟ8?W7�^i�5w��]	��Q���`;l7�W4����+�b!q'�Of5J��~X��M����8h������]�	�
�&��#���I���WT�bnQ}#�s��T����0X��0,��Cx�x�Z!�t1��u@��Lj`��)�=l��f-�^����Mm�Li����;H���K�<�p��B���h�2�L¥&�'`�+f�|:si�;ӗT2}q�C�Wp��!�
+M�z0��f��q�����m��^�!Æp�I0���L_�bE�������q���؏�9�'7P��P��B0@ 4cC=0_���=���/8і� � р��>���E�8�pń9��e,��3^��R���zv#�p1b5W!�m{����?-Y���bi���~�/X���!\|ҸYK�`��_�ߏM4�Hi�@�����+υ�;}e90W!г	�w�d/ ��k����G�?2�o��a�.���i��ʸl�+/�J�;14����'cx<��	���>�;9fe�KM�E���?���	f~���`�"�~`�U�����ʈǷ�@����U|"*ϟ����g+-+�+�����I���4��X8��oc����7��tIEND�B`�PK
!<���n 	 	8chrome/browser/skin/classic/browser/places/organizer.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


/* Toolbar */
#placesToolbar {
  -moz-appearance: menubar;
}

@media (-moz-menubar-drag) {
  #placesToolbar {
    -moz-binding: url("chrome://global/content/bindings/toolbar.xml#toolbar-drag");
  }
}

#placesToolbar > toolbarbutton {
  color: -moz-menubartext;
}

#placesToolbar > toolbarbutton:hover {
  color: ButtonText;
}

#placesToolbar > toolbarbutton[disabled=true] {
  color: GrayText;
}

/* back button */

#back-button {
  list-style-image: url("moz-icon://stock/gtk-go-back-ltr?size=toolbar");
}
#back-button[disabled="true"] {
  list-style-image: url("moz-icon://stock/gtk-go-back-ltr?size=toolbar&state=disabled");
}

#back-button:-moz-locale-dir(rtl) {
  list-style-image: url("moz-icon://stock/gtk-go-back-rtl?size=toolbar");
}
#back-button[disabled="true"]:-moz-locale-dir(rtl) {
  list-style-image: url("moz-icon://stock/gtk-go-back-rtl?size=toolbar&state=disabled");
}

/* forward button */

#forward-button {
  list-style-image: url("moz-icon://stock/gtk-go-forward-ltr?size=toolbar");
}
#forward-button[disabled="true"] {
  list-style-image: url("moz-icon://stock/gtk-go-forward-ltr?size=toolbar&state=disabled");
}

#forward-button:-moz-locale-dir(rtl) {
  list-style-image: url("moz-icon://stock/gtk-go-forward-rtl?size=toolbar");
}
#forward-button[disabled="true"]:-moz-locale-dir(rtl) {
  list-style-image: url("moz-icon://stock/gtk-go-forward-rtl?size=toolbar&state=disabled");
}

/* Menu */
#placesMenu {
  -moz-appearance: none;
  border: none;
}

#placesMenu > menu {
  padding-inline-start: 4px;
  -moz-binding: url("chrome://browser/skin/places/organizer.xml#toolbarbutton-dropdown");
  -moz-appearance: toolbarbutton;
}

#placesMenu > menu:active,
#placesMenu > menu:hover,
#placesMenu > menu[open] {
  color: ButtonText;
}

#placesMenu > menu > .menubar-right {
  -moz-appearance: toolbarbutton-dropdown;
  width: 12px;
  height: 12px;
}

/* Root View */
#placesView {
  background-color: Window;
}

/* Info box */
#detailsDeck {
  padding: 5px;
}

#infoBoxExpanderLabel {
  padding-inline-start: 2px;
}

/**
 * Downloads pane
 */

#clearDownloadsButton > .toolbarbutton-icon {
  display: none;
}
PK
!<���jWW8chrome/browser/skin/classic/browser/places/organizer.xml<?xml version="1.0"?>

<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<bindings id="organizerBindings"
          xmlns="http://www.mozilla.org/xbl"
          xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
          xmlns:xbl="http://www.mozilla.org/xbl">

  <binding id="toolbarbutton-dropdown"
           extends="chrome://global/content/bindings/menu.xml#menu-base">
    <content>
      <xul:image class="menubar-left" xbl:inherits="src=image"/>
      <xul:label class="menubar-text" xbl:inherits="value=label,accesskey,crop" crop="right"/>
      <xul:hbox class="menubar-right"/>
      <children includes="menupopup"/>
    </content>
  </binding>
</bindings>
PK
!<"G���5chrome/browser/skin/classic/browser/places/places.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* Sidebars */

#history-panel,
#bookmarksPanel {
  -moz-appearance: none;
  background-color: transparent;
}

#sidebar-search-container {
  padding: 8px;
}

#search-box {
  margin: 0;
}

#viewButton {
  -moz-appearance: none;
  border-radius: 4px;
  margin: 1px 0;
  margin-inline-start: 4px;
  padding: 2px 4px;
  color: inherit;
}

#viewButton:-moz-focusring:not(:hover):not([open]) {
  outline: 1px dotted -moz-DialogText;
}

#viewButton:hover {
  background: hsla(240, 5%, 5%, 0.1);
}

#viewButton[open] {
  background: hsla(240, 5%, 5%, 0.15);
}

.sidebar-placesTree {
  margin: 0;
  color: inherit;
  -moz-appearance: none;
  background: transparent;
}

.sidebar-placesTreechildren::-moz-tree-row {
  min-height: 24px;
}

:root[uidensity=touch] #search-box,
:root[uidensity=touch] .sidebar-placesTreechildren::-moz-tree-row {
  min-height: 32px;
}

.sidebar-placesTreechildren::-moz-tree-cell(leaf) ,
.sidebar-placesTreechildren::-moz-tree-image(leaf) {
  cursor: pointer;
}

.sidebar-placesTreechildren::-moz-tree-cell-text(leaf, hover) {
  cursor: pointer;
  text-decoration: underline;
}

.sidebar-placesTreechildren::-moz-tree-cell(separator) {
  cursor: default;
}

/* Trees */

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

treechildren:-moz-tree-image {
  -moz-context-properties: fill, fill-opacity;
  fill: -moz-FieldText;
  fill-opacity: 0.7;
}

treechildren::-moz-tree-image(title) {
  list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.svg");
  padding-inline-end: 2px;
  margin: 0 2px;
  width: 16px;
  height: 16px;
}

treechildren::-moz-tree-image(title, livemarkItem) {
  list-style-image: url("chrome://browser/skin/places/livemark-item.png");
  -moz-image-region: rect(0px, 16px, 16px, 0px);
}

treechildren::-moz-tree-image(title, livemarkItem, visited) {
  -moz-image-region: rect(0px, 32px, 16px, 16px);
}

treechildren::-moz-tree-image(title, container),
treechildren::-moz-tree-image(title, open) {
  list-style-image: url("chrome://browser/skin/places/folder.svg");
}

treechildren::-moz-tree-image(title, separator) {
  list-style-image: none;
  width: 0 !important;
  height: 0 !important;
  margin: 0;
}

treechildren::-moz-tree-image(container, livemark) {
  list-style-image: url("chrome://browser/skin/places/folder-live.svg");
}

treechildren::-moz-tree-image(container, queryFolder_toolbar_____) {
  list-style-image: url("chrome://browser/skin/places/bookmarksToolbar.svg");
}

treechildren::-moz-tree-image(container, queryFolder_menu________) {
  list-style-image: url("chrome://browser/skin/places/bookmarksMenu.svg");
}

treechildren::-moz-tree-image(container, queryFolder_unfiled_____) {
  list-style-image: url("chrome://browser/skin/places/unfiledBookmarks.svg");
}

/* query-nodes should be styled even if they're not expandable */
treechildren::-moz-tree-image(query) {
  list-style-image: url("chrome://browser/skin/places/folder-smart.svg");
}

treechildren::-moz-tree-image(query, OrganizerQuery_AllBookmarks) {
  list-style-image: url("chrome://browser/skin/places/allBookmarks.png");
}

treechildren::-moz-tree-image(query, OrganizerQuery_Downloads) {
  list-style-image: url("chrome://browser/skin/places/downloads.png");
}

treechildren::-moz-tree-image(title, query, tagContainer),
treechildren::-moz-tree-image(query, OrganizerQuery_Tags) {
  list-style-image: url("chrome://browser/skin/places/tag.png");
}

/* calendar icon for folders grouping items by date */
treechildren::-moz-tree-image(title, query, dayContainer) {
  list-style-image: url("chrome://browser/skin/places/history.svg");
}

treechildren::-moz-tree-image(title, query, hostContainer) {
  list-style-image: url("chrome://browser/skin/places/folder.svg");
}

treechildren::-moz-tree-image(query, OrganizerQuery_History) {
  list-style-image: url("chrome://browser/skin/places/history.svg");
}

/* We want some queries to look like ordinary folders. This must come
   after the (title, query) selector, or it would get overridden. */
treechildren::-moz-tree-image(title, query, folder) {
  list-style-image: url("chrome://browser/skin/places/folder.svg");
}

treechildren::-moz-tree-cell-text(title, separator) {
  color: ThreeDShadow;
  margin: 0 5px;
}

treechildren::-moz-tree-cell-text(title, separator, selected, focus) {
  color: HighlightText;
}

treechildren::-moz-tree-twisty(title, separator) {
  -moz-appearance: none;
  padding: 0;
}

treechildren::-moz-tree-image(cutting) {
  opacity: 0.5;
}

treechildren::-moz-tree-cell-text(cutting) {
  opacity: 0.7;
}

treechildren:-moz-tree-image(container,selected),
treechildren:-moz-tree-image(selected,focus) {
  fill: HighlightText;
}
PK
!<����2chrome/browser/skin/classic/browser/places/tag.png�PNG


IHDR�a�IDATx��ӂ3w�k��Q]A�b�{5kd'��d�c2kǶm'�F������z�'�	����qA�%�^�]\�=z��#������'�J������a�^�F��J	�6K��A�v/�Xĥ�u?�������u}cu�h� ��|���;(�f���?���/����9�b�0�����T*}�.@+���A�>{Z$�
���q��J�srr���JXr{��[+��|NW,��f�`�#^�R$�����Mٹ\�K�2�� =�
�t�(r�߃QC
�m�9MP(e�6�7����N��x8��'醢>H�rb�0xN��`�l��e�R��f�6V��?�3�y*
�[�Ut��	��Y���B�(��UTJ�U��X1v���4��L�Ѝ�"��l?�B9GBP��ѮC���l�Ogb�cI�����h�l��EY�jEpy�PGBk�ׂt6폽[��-�L�z�|
��<T�%��Q��N:{mP��C���]}�ŦO
�n��E���ު�R'�kt��َz��}Fy����x�R�x]��4&��0�սNC��3�ԗ^"�V9�3ƽ
����!�A�a4J�d?�����^|�%;/IEND�B`�PK
!<D�}
[[?chrome/browser/skin/classic/browser/places/unfiledBookmarks.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg">
  <path fill-opacity=".05" d="M12.7,1H3.3l-.2.6L.1,9,0,9.2V13a2.006,2.006,0,0,0,2,2H14a2.006,2.006,0,0,0,2-2V9.2L15.9,9l-3-7.4L12.7,1Z"/>
  <path d="M12,2l3,7.4V13a.945.945,0,0,1-1,1H2a.945.945,0,0,1-1-1V9.4L4,2h8m.7-1H3.3L.1,9.1,0,9.2V13a2.006,2.006,0,0,0,2,2H14a2.006,2.006,0,0,0,2-2V9.2L15.9,9,12.7,1Z"/>
  <path fill-opacity=".05" d="M14.988,9,12,2H4L.851,9H5.023A2.931,2.931,0,0,0,8,11.6,2.889,2.889,0,0,0,11.012,9Z"/>
  <path d="M10,9A2,2,0,0,1,6,9H0v1H5.184a2.983,2.983,0,0,0,5.633,0H16V9Z"/>
</svg>
PK
!<#��}��@chrome/browser/skin/classic/browser/places/unsortedBookmarks.png�PNG


IHDR�a�IDATx�����Qģ*v���m#z���S%�۶�qm��Z�3��Y|?;;��YN�K�s&��w��<ź4�D�R���zu��^[����	6v[��G_#8����`�`�7�)˳sӕ����rn���h��x�5 :$�x�u�k�7B97Y�av؝���^Z^Q����9{P� �񷈅��J���ߏC���Y��.�
ܾw���0/OG�g!:vBp\h�o2��r�Б�Ǐ&����p�����B7ú��7��a����(�,A���e:�º2��y�>,���1����@5�@}�ƥ90�� ��xc���D����;�o���'���7�ozz�hHl��@M��;r����������֦�Äo2ǎ���44�̲�a o_viQq!|~���_�,;�~�+�;�������,;�ԍ*o�%�IEND�B`�PK
!<M���ww=chrome/browser/skin/classic/browser/preferences/alwaysAsk.png�PNG


IHDR�a>IDATxb��jUTN�Ha�p",AN���;ĸdx�Ƹ����?�𮞵��ڞ�N{g�dsk�+�S>���
6'�'��|�� ��
A��{y/2��#��}�)��8Ը�$��8�-PոM�i��r,A�
l�Pu���-�Q�܉�K�eH�l^S��^�&�&��T����*i@*P���c*�5� �%%J#�|��~�Z�_c�֓�T�^d�U�:�'����;����eАA��Y����C�40ԛ��+#��BD����~kmo���b��B(X\��x��ᮐ|�!'�}!|��Xe
�IEND�B`�PK
!<|�H��@chrome/browser/skin/classic/browser/preferences/applications.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
 * Line up the actions menu with action labels above and below it.
 * Equalize the distance from the left side of the action box to the left side
 * of the icon for both the menu and the non-menu versions of the action box.
 * Also make sure the labels are the same distance away from the icons.
 */
.actionsMenu {
  margin-top: -1px;
  margin-bottom: -1px;
  margin-inline-start: -1px;
  margin-inline-end: 0;
}

.typeIcon,
.actionIcon {
  margin-inline-start: 3px;
  margin-inline-end: 3px;
}

#handlersView > richlistitem label {
  margin-inline-start: 1px;
  margin-top: 2px;
}

#handlersView > richlistitem {
  min-height: 25px;
}

richlistitem[appHandlerIcon="ask"],
menuitem[appHandlerIcon="ask"] {
  list-style-image: url("chrome://browser/skin/preferences/alwaysAsk.png");
}

richlistitem[appHandlerIcon="save"],
menuitem[appHandlerIcon="save"] {
  list-style-image: url("moz-icon://stock/gtk-save?size=menu");
}

richlistitem[appHandlerIcon="feed"],
menuitem[appHandlerIcon="feed"] {
  list-style-image: url("chrome://browser/skin/page-livemarks.png");
}

richlistitem[appHandlerIcon="plugin"],
menuitem[appHandlerIcon="plugin"] {
  list-style-image: url("chrome://mozapps/skin/plugins/pluginGeneric.svg");
}

.actionsMenu .menulist-icon {
  margin-inline-end: 1px;
  height: 16px;
  width: 16px;
}

.actionsMenu > menupopup > menuitem > .menu-iconic-left {
  padding-inline-start: 0;
  padding-inline-end: 4px !important;
}

.actionsMenu > menupopup > menuitem {
  padding-inline-start: 3px;
}
PK
!<^�k~��>chrome/browser/skin/classic/browser/preferences/containers.css/* Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

[data-identity-color="blue"] {
  --identity-tab-color: #37adff;
  --identity-icon-color: #37adff;
}

[data-identity-color="turquoise"] {
  --identity-tab-color: #00c79a;
  --identity-icon-color: #00c79a;
}

[data-identity-color="green"] {
  --identity-tab-color: #51cd00;
  --identity-icon-color: #51cd00;
}

[data-identity-color="yellow"] {
  --identity-tab-color: #ffcb00;
  --identity-icon-color: #ffcb00;
}

[data-identity-color="orange"] {
  --identity-tab-color: #ff9f00;
  --identity-icon-color: #ff9f00;
}

[data-identity-color="red"] {
  --identity-tab-color: #ff613d;
  --identity-icon-color: #ff613d;
}

[data-identity-color="pink"] {
  --identity-tab-color: #ff4bda;
  --identity-icon-color: #ff4bda;
}

[data-identity-color="purple"] {
  --identity-tab-color: #af51f5;
  --identity-icon-color: #af51f5;
}

[data-identity-icon="fingerprint"] {
  --identity-icon: url("resource://usercontext-content/fingerprint.svg");
}

[data-identity-icon="briefcase"] {
  --identity-icon: url("resource://usercontext-content/briefcase.svg");
}

[data-identity-icon="dollar"] {
  --identity-icon: url("resource://usercontext-content/dollar.svg");
}

[data-identity-icon="cart"] {
  --identity-icon: url("resource://usercontext-content/cart.svg");
}

[data-identity-icon="circle"] {
  --identity-icon: url("resource://usercontext-content/circle.svg");
}

[data-identity-icon="vacation"] {
  --identity-icon: url("resource://usercontext-content/vacation.svg");
}

[data-identity-icon="gift"] {
  --identity-icon: url("resource://usercontext-content/gift.svg");
}

[data-identity-icon="food"] {
  --identity-icon: url("resource://usercontext-content/food.svg");
}

[data-identity-icon="fruit"] {
  --identity-icon: url("resource://usercontext-content/fruit.svg");
}

[data-identity-icon="pet"] {
  --identity-icon: url("resource://usercontext-content/pet.svg");
}

[data-identity-icon="tree"] {
  --identity-icon: url("resource://usercontext-content/tree.svg");
}

[data-identity-icon="chill"] {
  --identity-icon: url("resource://usercontext-content/chill.svg");
}

#userContext-indicator {
  height: 16px;
  width: 16px;
}

#userContext-label {
  margin-inline-start: 0;
  margin-inline-end: 3px;
  color: var(--identity-tab-color);
}

#userContext-icons {
  -moz-box-align: center;
}

.tabbrowser-tab[usercontextid] > .tab-stack > .tab-background > .tab-bottom-line {
  background: linear-gradient(to right, transparent 10%, var(--identity-tab-color) 10%, var(--identity-tab-color) 90%, transparent 90%);
  height: 2px;
}

.tabbrowser-tab[pinned][usercontextid] > .tab-stack > .tab-background > .tab-bottom-line {
  background: linear-gradient(to right, transparent 30%, var(--identity-tab-color) 30%, var(--identity-tab-color) 70%, transparent 70%);
}

.userContext-icon,
.menuitem-iconic[data-usercontextid] > .menu-iconic-left > .menu-iconic-icon,
.subviewbutton[usercontextid] > .toolbarbutton-icon,
#userContext-indicator {
  background-image: var(--identity-icon);
  -moz-context-properties: fill;
  fill: var(--identity-icon-color);
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center center;
}

:root {
  --preference-selected-color: #0996f8;
  --preference-unselected-color: #333;
  --preference-active-color: #858585;
}

.radio-buttons {
  display: flex;
  margin-inline-start: 0.35rem;
}

.radio-buttons > radio {
  flex: auto;
  display: flex;
  align-items: center;
  justify-content: center;
  -moz-user-select: none;
  outline: 2px solid transparent;
  outline-offset: 4px;
  -moz-outline-radius: 100%;
  min-block-size: 24px;
  min-inline-size: 24px;
  border-radius: 50%;
  padding: 2px;
  margin: 9px;
}

.icon-buttons > radio > [data-identity-icon] {
  fill: #4d4d4d;
}

.radio-buttons > radio {
  padding-inline-start: 2px;
}

radio > [data-identity-icon] {
  inline-size: 22px;
  block-size: 22px;
}

.radio-buttons > radio[selected=true] {
  outline-color: var(--preference-unselected-color);
}

.radio-buttons > radio[focused=true] {
  outline-color: var(--preference-selected-color);
}

.radio-buttons > radio:hover:active {
  outline-color: var(--preference-active-color);
}
PK
!<d�M��Ichrome/browser/skin/classic/browser/preferences/in-content/containers.css/* Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

[data-identity-color="blue"] {
  --identity-tab-color: #37adff;
  --identity-icon-color: #37adff;
}

[data-identity-color="turquoise"] {
  --identity-tab-color: #00c79a;
  --identity-icon-color: #00c79a;
}

[data-identity-color="green"] {
  --identity-tab-color: #51cd00;
  --identity-icon-color: #51cd00;
}

[data-identity-color="yellow"] {
  --identity-tab-color: #ffcb00;
  --identity-icon-color: #ffcb00;
}

[data-identity-color="orange"] {
  --identity-tab-color: #ff9f00;
  --identity-icon-color: #ff9f00;
}

[data-identity-color="red"] {
  --identity-tab-color: #ff613d;
  --identity-icon-color: #ff613d;
}

[data-identity-color="pink"] {
  --identity-tab-color: #ff4bda;
  --identity-icon-color: #ff4bda;
}

[data-identity-color="purple"] {
  --identity-tab-color: #af51f5;
  --identity-icon-color: #af51f5;
}

[data-identity-icon="fingerprint"] {
  --identity-icon: url("resource://usercontext-content/fingerprint.svg");
}

[data-identity-icon="briefcase"] {
  --identity-icon: url("resource://usercontext-content/briefcase.svg");
}

[data-identity-icon="dollar"] {
  --identity-icon: url("resource://usercontext-content/dollar.svg");
}

[data-identity-icon="cart"] {
  --identity-icon: url("resource://usercontext-content/cart.svg");
}

[data-identity-icon="circle"] {
  --identity-icon: url("resource://usercontext-content/circle.svg");
}

[data-identity-icon="vacation"] {
  --identity-icon: url("resource://usercontext-content/vacation.svg");
}

[data-identity-icon="gift"] {
  --identity-icon: url("resource://usercontext-content/gift.svg");
}

[data-identity-icon="food"] {
  --identity-icon: url("resource://usercontext-content/food.svg");
}

[data-identity-icon="fruit"] {
  --identity-icon: url("resource://usercontext-content/fruit.svg");
}

[data-identity-icon="pet"] {
  --identity-icon: url("resource://usercontext-content/pet.svg");
}

[data-identity-icon="tree"] {
  --identity-icon: url("resource://usercontext-content/tree.svg");
}

[data-identity-icon="chill"] {
  --identity-icon: url("resource://usercontext-content/chill.svg");
}

#userContext-indicator {
  height: 16px;
  width: 16px;
}

#userContext-label {
  margin-inline-start: 0;
  margin-inline-end: 3px;
  color: var(--identity-tab-color);
}

#userContext-icons {
  -moz-box-align: center;
}

.tabbrowser-tab[usercontextid] > .tab-stack > .tab-background > .tab-bottom-line {
  background: linear-gradient(to right, transparent 10%, var(--identity-tab-color) 10%, var(--identity-tab-color) 90%, transparent 90%);
  height: 2px;
}

.tabbrowser-tab[pinned][usercontextid] > .tab-stack > .tab-background > .tab-bottom-line {
  background: linear-gradient(to right, transparent 30%, var(--identity-tab-color) 30%, var(--identity-tab-color) 70%, transparent 70%);
}

.userContext-icon,
.menuitem-iconic[data-usercontextid] > .menu-iconic-left > .menu-iconic-icon,
.subviewbutton[usercontextid] > .toolbarbutton-icon,
#userContext-indicator {
  background-image: var(--identity-icon);
  -moz-context-properties: fill;
  fill: var(--identity-icon-color);
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center center;
}

.container-header-links {
  font-size: 1.25rem;
  margin-block-end: 15px;
}

[data-identity-icon] {
  margin: 0;
  margin-inline-end: 16px;
}

#containersView {
  border: 0 none;
  background: transparent;
}

#containersView richlistitem {
  margin: 0px;
  margin-inline-end: 8px;
  padding: 0;
  padding-block-end: 8px;
  border-block-end: 1px solid var(--in-content-box-border-color);
}

#containersView richlistitem:last-of-type {
  border-block-end: 0 none;
  margin-block-end: 8px;
}
PK
!<�P`z�z�Pchrome/browser/skin/classic/browser/preferences/in-content/critters-postcard.jpg���JFIFHH��C




��C		

�������������̥jJ�fq�83��F��b�9P��1��{⎾8�>��5���PĨ�
sa*��ͣ5f�cTʲ���f�,���7W�3*H�2�k/Z��ol�����N���~�`�r������le�1s�\��.EdeR�,M)΂V[c��:QQ�����Š�����g��-��3���]��)ˏ��V�@����	�����e^uڢ�b��f���ycfʙi-KRT$���EL�7�C�PS!+�T��z��/�W�*��aq^�8�[�x�x[�CX�\��c>�{}�O=i�]�ŧ�UgQ�H�,"vO5K-fZ��OY|�e��6�D"��rz̷%U���T
Fj�B�$�Q1S%0F?O.���q�q��ڮ��V���:���e"�'^�B�P8�����d�9՚�f�v\���bE^�;�͝��g�Ԍ�Ȭ�t̷�����ћ4��cU��lCd�bJ��M�2�\���V����\�W4�T�s��Ϩ��zJnrD�\A���J��d�]��J�{ed��ž��]c�eHB�vD�)D��W,�uT�Ke��R��(zʕ��G���'�j:r�|�r�1�4L��o?o�t�`��/u�z�-3s�I	���J앉��=��#Ԛ"D���se5j:�\�qS��aD:P��p
�AHd�	�b����z�to;������Y��s�N��0�z}C���{n�sE%�D���똬�Y-�R�Q�Ԏ5;%���:�*��KQ��*�%�1��L��!�Z�nz׾X�<��u��uu�I<t�oT��'�O7gV"Y��;R��4U�
��]̷�$�}J#�@��B}a��,� �J
��RHʀ�:`:�BZ1m��Io�gH�;���s�uGN8�<�}���ͅɖ�O\�t9O��Ϲ,��u+*��Ԑ�,���Xn]�O���*�Y�͑-��1SD�%N *:	
���.�j��j��@D�\���{1єo�o&7^(X��������@������oNs��*��H�r�R�,�B���rah!�1�)@ )�J3���\���*X����ar�E��ݩv��c��#�9K0{yq�y��k%��OQ��ʝ9_����gY=��3��hʬ�՚�vdz���.�F%B�FYY����y���̍��z>~���I�D��b��'���W�7�1����'5M�GN8}����F�Ür�J���΀�����[Vk门+�R8��UgrDJ�L�_I�D�$e��*�;�w�tO�BV�)Z:���轟�k$=Z�"22��!Vdo,	R��9ca�k����y!3_,Ӿ8ӟ����`k���zw=L���p�U,�kƙ�j���s]��#������W���ӡ�}|.^���j3Q���i��=G����e�U�:%�,��Z�e�r��u-�u �Ȍ:�o�]_��b������^�o�Ǥ��v�����N�zs��C��=Gb�{����k��+cf��;��Q��ek>}�Y��{�e�gp��j2�:�N�˜�n��s����!J�!,�J%r�iv��� �(�Qq%�������ߚ��)�:��[�ϨC��^��VY�[���L���­߳���aTb��g��Q�������_/f�,��1�X�FN�۞����\��	T:�;�\1�>5n��"@:H� E���wι5>ϗF���1���uOX���K�v�-눗�ñ�S�j�6����
P����?�r�o����	�չ�f8� !�`�+�J�?��~b�H
3mJ�VE�AR!��"���H��/���v��ˮ�_W�{@4�~�_��۬Op.�����	kWb,6s���(0�w���>o�{��ެ|��υ��wTt׷}��[
�XYAD%�\�����'=S�;]����XV2�`��z��_ʣ�N_[�}
g�ۑrD�%�Ϣ}��f��wL�D""הH�R�7�ϋ��7��_~�e��,�q�ǟ/<�����[��_S����������v��kSæ���v`s�=[b#��Nd�#s�Ӈ?��Ͽ�}�@����yo����H���'�>��*BF�tY$j�V �{����~�����=�ӥ�9S�f8��n86$Y(!�p��}����O��`�*�뜝e�����F���y�%W��s-a*�w�G�5�_o���.o%�}�7.[7��q!�:t"!V�t��%����p�?G���k'���'<��/<���/<y�wǯ\�����+���z퉮ޣ����đ�T����91C�Zc���1sk��&�ck�/7�>�Q�?nn`��'���ԕ1Б	l�hZ���^�?�?��/�����OH_9�Ny3�Vyfg����g,.~��!u����덮����_��K��"U;,�u~�V�!X�2��"���֐���*��as�'~=�`��_fÕ��ެG�*�)D�%@����<�[����>b�l�S71|��L瓞y�9ߞv�͗
��]Q��^��덮�}8}!��H�v�f�bB!*+��PeQ0��&T�?NO�1��}�'�z��jN���C)v�YD�H��J�W�����.��\�&osʜ�%��gL[1dʤE���n��ؾ���wD%�0#��h�6Z���PV$Ew0H�s�L����q�2=|���S/r]��`8zI�J�O���5���qo����d��(�M����2',��ߡ,V�u[W�'�>�Ǣiv)J��5Ԭ`0�J
%"�U�FX�^u��}݀�O�>n�$���͖�.��4҉�>�r=~�/��e�:5�+@]���|ϫ��}
�<�?��Y��1t�.�����NΏ��:�W��z|��< �:P�Q�f�����~����Ӹ��e�dIEr�dܓPf)��x9�y��O�c��)Z)gd���6t�Ԋ��j�z����������
 �>]���?|�����<yn���^�ճ4��~?���ۮdx�/��N������̏Lq�S�^u�+��k]ێ������=��z����o�Y�|��|��|O�0�C"��Z�$����r�;�|�^h�R[7�jOщ��Ȟo�'�}O�;����A@B^o��������*ةΫΫ����7�/���O��[�\�n=�[��_�����㹿�࿮z���i��y$�O?�7��3�K2��=yk���>�.����?��/��ϻ�|��_��׿-�N���3��0������ˠk���b���y�Ӊj+y��C�>��xOg�����L�<PPi���8�_v�q��Nu^U^U9�y�x����^���{_��/�<�����Ο������L�\�]��������D ��g����O�|�}��Ig�_L?����
٬�Ʒ�Kϴ�{����r�Χ�6v���!o�g��{�8y��.W>@P�Dc�O�C��Bf�Ʒ*o�:ܡs�7��w?����?��M����޿��'��}~O�?e����^[�7��>'ލB�YP$AQ
c���#���c�G=�L~��>��A!.>.��y��'\?/[�Y�����]��/��^��n��|��_?�'�`@��Uk\O��r?�+��q����>��Nj����wk��ߝmOSȿE��7�÷�=�ݿ��b6B����c�k;��<�����#��(De��{�6�pK����!R�4�x���{�Me�U����͵����Ȣ� )��}~{�����Wy���u��A���.v��k������=��g��q�f)����y�+�U��\��)R�"�f�s�}�?���;O��}�򟩳:db�1T�~�?C� �7MG�������T�K����z����l1�����8��Qt'N��O���z��7�=��޾�\>>��~v���l�ލ�V�۾�����ۺ��������W��?���Hknk�����Y�^�Lm���\��~������͕[$L�g��5\7��w����VY��|��C~��S7��w>]�5�%(!�@W��?'�_�Ӯ�zq|_/��K�x꾏���N�כ��_/�k"�3}�=���_�w����Ӡ����ⵞ�>��~�F;_��2�R�2��-�N�Ǧ�]Ĺ]9��ƫ������?+�oC��ޕ�yBY�x�=��k�kI�ԕ�I,�����/���oI�W�m�\�\s�y��oVy�����'��s�X�noB���������Ǐv�}�u�_�^r�-���������룏����6i�����|��r����IC��.ک��ӨOu�&!%z�^{���~s��E*
��U#,�\z�O .w����w�ԕ"Vyw��>�gc�����Ͻ21��>~����3�}{ec��a�=7�ϻ�Q����������}O����e��wN�c�5��[.�w�q�t>理�o���"9?���~�m|����u���t󛝏>��^kXVEV�'N^w����S����$�%�Q��G��c������VV�:��N��������\������J2�����m8ye��=~�|��,�\����;�~c�u�~��|}���i|o�m�����w�e�}W�?7Fp�k���G����S�<�l�4�=�[Y\�_�f�둍��z|�߻��c{�/��vj�dl���Ͼ���o����|��De�V����q�˔�~��@�R�	j)~a�_��N��"�g�ζ\<v��
5��7g��H���O���_~gM���c�u�K����
��gs�7mg���>V!��T.F�n�?Cy��Æ����}��U��>�ѾtjAo�����IJBY"�N��/���7�z����?��Ǔ��Ec����"q'ՙ���[LdO�~��N��M�o��vz�翟l�xt~ߵ�g����9��I;���{�#�������|�NuŞ|��|����n�����əfI*K��[�����ׯ�}���ّ��X
\0#D1Y��o���y��ƾ���3���a����@�����'�*�:`|��s�~�ץ�雮�/�~o��};y���;�.��S�|_���,�����������bj��Ճ�??��೜��_�O2@��`%�"6��}:k?�.��:��O~N7)P��d2`����'�~s�o|^�����cӿ9��S�?�B����z���8�Ԑ�L��r������],����=C�?i����<�n����������x�yv��<7�Q��7��y��u����;��V�>7O�fq��$9�9J���l~ް?�t��˩��G��s�d

��nNr�z�}9q_W�w���?I�����>W��/����:!�I���[@o�ѕ�+�g�3�O�߮�ޖ믯|��zg�����s�q�Tv���9��q��}�׎WW�?7�~G�
��OA��g7�+$"I)*[�eWTd�W~[�����O/�k��V�nNr�
u�N�x���;��}w���������޻���z_��F+�G/s�T�x;�jZ�:`"��T�߹��ޗk��~��Y}q���z�(Q�濛�,�ߙ�|��.H��s��ġ�,�#l*+_�5������~=��2�����*M���%#!To�8qO�v�����OS���y��"�t�k�<�z�����eP�R�AD����_C�}�wk�џ+�~���bs�۞^��(;�JR�������<��N�'�;���;�����ՙ���r�?g���ߏ��]�9|��s�fu v�&��Y��<N�8_��:��y��W���|>���)���B!.�^��y�;�׋�|�n�y���`��ߐ~��*���_�������ccy}��kX����>\��������{����շ9��;���箎]'?6,�l�N��s¿��n�������+�k������m��I)�qw��;������y}3�/��?j�����˽�>m֠9���vjAB�Q�����{:�y��^}�n[�����:s��'c�TQ�a�덝�~_4�[���-�r��[V�r�w�K���;�ǏW�W�N����G�K����}O'm��w���'m����k�K��>��t��~�M�
�O��򿡷H�!�ˉ���^n�PC���h;~��kzN=�qm�w}�����f�fw.�U5��`��>wf�x�������.�C+��m�{�O�ںi>O��k����_߶��\n�0��g[��O���O���\��c���d�����s�|�q�r�7C��w�[��������w����h��0|�cV�qn�*Y"[�eY+��z1q�L�2��ܕ��c��(+tX��ݦ����ybə�2��=������9����t^�N��9�|ڵ�M~��;����z/����	��G>��~:Nn��X�R�Z�F������<ť{W�����F	i��~�y��޻��V: *�w\!�=/�[��A^T���V��"t�)��}�'�g�g�?���Ý�Ź̤r+zo9������_ҹ�|���.?�����?g�ϥ�7�5�'�f�7vwv5,�V�Zj��*���x��i]�������YM	c��~�î5<w��v*��bo��v����4�4��y�qV�9C*�K7^�9[�5e�ߓ�ƿ.r�g'=c��//�/��'�-IJe�GM�u��nߟ��:�}I�bz(��/��/�q�$�{�[�g,��,�Z�uX\��2��{gC��+�w�GҿY11�6>�|k�kI��tЩ�o��z?O�<?'����~{�;��{��v�eq��L��#X��Y��te�q�����e?��?�<2x�fy�m�ǯ���s�>w�s�cVΙ�\��O�e�}o7���}v���J�b�Zlq]����Ad�W~C�=W��*����6�gǾ01t�?�R�[��G��x�G虻���=�W���}A��ˍ3Q"Yr�V��L\�e/.ק?y>�|>~�_O����s���yNN��}z���^D4	Xi>�]�o�7�1o�:_���蟗�G��s{�/�����U1J�/9׏�ͦ��[��Z}��o��cqFZ�F(α=<�oW L���ꞣH�	����s]~��C�
d5{�_������f��̬����`D�^��Gh��������@<���~�?��~?��~��jR�D��Sܟig�9?C�����������n+#sϦ˞��cOӜ5�t���<�~��o�%��mQ����z�S���ߢ�>X��=�/ӟדr������2��9z��Tgu�Vt�3����?�����VO��y7*�i>��~��'���vS.$�,�;�;f�<���Ͽ_�����z<���@97~oG[��7�v/oğ���b��ߢ?7�������{8�D�~o��:�/��o6�~�Gw��n�p����?N�׳|��v�>C�K�ϭ9�%�Kݫ�g�<���&�Ɨf��'��II$$�8)ԗIo�9_W�O��e~������U�{L�Yݺ�y���������_����d%,R�$FZ�S�=���r?;�t"u�ӿ�_g�&���.N���.�������S�Oy��4K	u��N��3�ྦ���	k�6�}#�L.}�fd�&e�r+D"rm	X�-��+�r���>L|i؆D�|;�/������۾-���� ›�7M�>������f��"j���ϧ�.#��㧚Wuo^���c�o}YU�Z?��Mף�/=�r�������UWu9!�cu�V�o<ۜ�f��L�IH���
�E��e}nK��6�����#�e��]��i2�ȢX�^R�Z��o>���ׇ��"ZD`h�{9G���ת���_�}���:�|����}���_�O?l\�]������b�gX�������
ڵ�^�a�<[�ٜ�fY��r9$�@��s>���α
ܿ��w7�9G����3ы����|�w�FL�BJ5�8��k��=��HPS�X�������{�ź��c׌세�i�u��=fcˎ����t��g�c~w�^:��/N�\yY�ٜ�39'�)'2�H�q���/LL_��(d���C��w\ܽ<���SFlW����=X�q��! ��������q�|�T�x�Q�S��g�eJ�``�鍞��&n�m�M`��;��%E+��=x߂�H4VսG���,�g+\�q��xW�uy�W���Ι�>z<��-G���F���9]����X����w�vm�C�/�|l�o�4C�!,f�g^mo?Ga�8�5@4^N��7*��B�*
�t�=:]�+�|1���u�3�s�7MG�oҮz���q�����VQŏ;v<��X�aF7����r�&��_��ϙۧ���'�)4���~c�Z��6׭c��zֿ�'�?��ߙ���a7�VآT-NR�Zҍy���E�&�4~N؜5Xս1* �X�`j������q�\������>�cx�=�����IDa<��%)���A��k�a4��>��m�j�B��cv��k-�y�}0�ܴߣ����6�<���v�����W.>����v�Bt6�i��u�n��)���b�t�u���u�;��SU׎N����ƹ?�~�/���^�xIfCG1��§$.�ޏB�
#hJL�xrbv�^��yk�G��_3+�v���,�(t��<�K3Ѭ�4k�������:-�4�n��yT쳦l�a����Kn��+��t���������:X��k�_����ߧ�^o4��)$1��%#�����>����!���l.�����m�������?S�,�
��D��r���4�h�7�/:��t�^���y�h��[��Wu��E)�Q^k,�pZٕ1c�c�����?��	�JIH䔒G
�T-���s����]�ׅ�C�EP����[�z��>����=뾧��(vF��%S:��~rX�N��bT�8�g�]WMj��yߛ�e�u�~��%L���ټ�&��*ё���s֗���y����_��$+,�{͛�q������Y}q��:���˦'.������|��������K$J�'@��
�N���.P�%m9�3Uc�W��u��=9_��e��qv���-d%`�p�onvk J앀JȔ��D�**�����D��:Q!P�T�JUebc�$5�:�� ��N�=+�\���{�>z�~?�d�u�'���;18z-���'���Yإ�KJ�BD�AJ$:bFT���bU(���J�VU�>+�]�^q=mD��5w���>����~/�S����i����,������D40+Τ�����Z%$u]��
����5�N�T;
!)"P����T��_���"���3Q�OGq��2�_��R��,��2�y�4�wf�쐄a
YREZĝ�� �B����%CB��)U��bHw8�����^p$Wiu�ߣ��vG��/�z�7~�I���|������U*	X�#,����ase��1+@�"2��*�eabF��G:(D�jE�=�T�as���W3f{կ<+j���v�G�<��c���%�g�<��]E$%L`F'det�2�Y#�(G*�J���qdHK"vE!y��7ƛ�~�ŋs&��EۈJY}�|��ƾK�Z�‰��FX��辯Xy��Qƅ�\�n�,�G���Q�(	�]C(MYc��"HU8Ե���R�Z�v��(P��-����In}Vg�{�o?Mz���I���,��5f.�YY�c��U���.����ޯ`p��cq��[��ی�L����f��2���;
a�C5J�¢ě�U�N���F��VFP@�KbDG\c������zV���Q���9����N����/,}yZU$ �==>���>�7��=ru�ۜ��{��-���2V0#,s�o���M�ȸ���Վ��9J���@;#)P)b8,Z��~5-�,�즩�)g��u+1��=���W��m\�8k��{�^����KY�!:d^Igs~����^bBi�ʤ�Ye�ap��u�׀BU-8��n���=\�����H��-R���Tu�A�n�����
�'um���s��_V��S���Օ��րr�7��|^��VY��p�͏sm+Βټ��X�=1xz
�z|SFu^mY�l1z����'�jH�"*�=eʖ	d�z�g�>���Y~�e�[ev=�X��5sOR꺲��|C����[y�a��[�=f�ruc�U��:����=���e��Y��e�!UIG_����;%A��ߩS3[j�B��T�..���7~[�vA$��'�/2V)k1�VF��v\�k3霮���YuY�U�ͥ�eVA ��u^oD�ˋ��_NWܚ͚��N�R� ��5! 1A"024#3$B%@P&5`p������Y������v#v�����8N�3i���{�rm���K��*��ʹ��>�etJ�������|2��	�p1}ƙ}ʝ�97x�ނw=}�b~�r�mBuMF;����a�_N
e0k�Y�������'5�,hrڞ
w�P�t��?���N����S��3W�)��K��/����(O�ԣp�(�U9�Q28vˑ�iB���B�1�i�s��Fdn���sy��x�;x�E�k�k�<�C�!eX����2��#QQ��j��:78B7HѺ��?r����:��}]C�g�˻�[
삾��BлalA�bڰ��s��"2�[��1�5Ƈ�x�)#;�nW�[q8�Q�M<�[��V��ܼ������F�Q�̓r�+��+'z3ʋ�+c��4�s�h[AX@-������
���5�r�!c���9�:a
q�QN�rsKF^p����(,`���,}ڎ.�W���V��h�=�,a79w�+�4�
#����u:�P�_:�R��-��x<�~>tw��rsv;��=H�G���<#¡v۟��s[��)�+8w��4h������hV3�s�+�P�M�p��w�ЮQCߑ�"�3�O��1�p[��?'�F�������(̠r}?!��yچ���G�����K��_׈��x�t�Μk��/��x�݈;�8s]�#���AX)�s�w���8:B�>����c+�aDpS�8j��^���ƿ%~K��)��KHs^_�/�r?'z�-8��f��.nV� -��G�C�g��n1�B�6�i���|~���^xN8YG��'<�CLjݸ���K��
N?��#��*��5mX[HhnG ���
��M����21�3�A���ԢP�Ģy�Θ���8���9��� rqǥ8�c;��W�`Aaa���"�W�W����c��%��&M��އ�<\��s��)��o����,Ý��0���{G�'�)�K�^1[����GO�h"�=���ܚ土c���Sp��Mr�e�VT�
��#T7(%�q��yX�"0Y�N4!N	Æ~&_��F�!O����>7!�m���zsV�I^� xω�nY�G��Yttߦ�i���5푫[��yGG7'gF��Χ\gC��Ĝ�-�G�@�9m����t�l��SJ�'AΘ�##n�„�~��Fv��iC�UwH(��w���=enE�z޷��z�-�*�Di�-rk� �n��s����${@R��\T�������~��
*m�
����I��'a�Elti�ue��#�FD^�-�zܷ��z޷��=S�IL�K�U_��(�_�Ec(p��Ņ����]>sA�w�zi@~�3�X����f��$H)[SP���Q�w�ѕ��������:���d�6����w+�''��'8;��=�o��w�C�����pW�r���w�}eD��I22-�M��PUIL�m՗��9�-�weۼ�4i���;��sw���o��N����Vz�P���#k���_L��?EEd��~��dšd��SS�qL��IAV+�	�~TTg�/�	����b@���;��p�w�[G
]8����7Qt7ž6�lb��.��`_T�_RWԹw޻�FG.��sqڎ�U$����S�yN+�'�M�B��'�hp�f���j0��i��݃_k8N,��N����GOV��U��8X�x�������~�'�nM)�4��0�%dc�+I)�8���~t���d!��hv��ozo�C��*Q����V���P�3?����<�N��u�I����g'9�M)�4��u+K�zZ\��	�s��q])t��5w[��Wx(����Ls���Wҵ};LA��'z,�A��V��� �q�YGͼx|�V\!�l�V�e(�)���Ó���Ȝ0|������'Ӕ�Mh@�m܅"�B����Q��>ڻ�]�#R�j���}Q_R��z�w���\�V1#��on�ʘa���s�J�[������)E
�MrA�=��+rދ�r.Nr%[�
¸47L(��w��K�J��.��y���8�x��(�E��o^Q�W��C�jO���s�GSY�]�
A�9 啽o[�r.Eȹ�B�i��>^��+�S���\9q��N�j-�Y����1�z���g�V��sN����/kB5��v���A�egR�����KWIl�����?�X�)NJ���o����騩���������r��Ir��\|&�c��"��T�=�66�ߧ]� B��nյmL��:���*JRƪg���Ar���~t��?'U7j!Y�p�t�Me��!�O�ƿ+�����Ƨ��'x����Mk��ֵ�]��ߪ���p���F쨧|+�e���R\��U7��ԁ}�:�WF����(#�:��a�Ωuv��nt���,��9���'S�]�k�ڦ���t�SB�'X�9�c2TֽTݫj�ev�oS�KK�z��O�>�rª�Na�n��p��@t�^��[Dg���hat襤e+?[�ڻ3�۶���D�Q(��?Mԟ���}���)#tF�{��Q����W�ԗ9ۥ��Qjw���.1Z�dM�ɑ`j�H��a�)mUQ#�7R��X��MY
c/]VX�f���SR>��_q:en�N
X�4�4�5�Z	��+��[���Ǭj�*���Q��SH4:QD���4� �a�r�}�V�����1��K�����g��L)��ʦ���S��b�&B[�ܷ-�z���TY#z���������K7��,hQR`�g45����G���2P��X���|SFa��QУ�MT}OO���C�}Nl�}�|t�O��S�+�'��*J2��TY8���nYYYY�++)�6���Э�rm���t(�´ۼn?���ꢢ����]S�#�x����Q��{�^��QEzK���t��=֢:���#m]J装�4��v
��<�;{T%�<��>����qO:��Ӗ7W�x�L����xܓP:>q�\\]-h��F���ER�,sB��+iq��YP!��
$�ٺ^�54SR;��|��t�H���m��\U?���5���E�|
(��r���s1��cι_	�������V�:�i�ڣ*3��*B�1�!���T�:r���5m�;��H��c��
:�ʜ��"��a�Ʒ);��U��ʞ�i6��j,E�@i�o���ڇ�[�r��4ˤ�f��+B�5w��]⽣��y���C�+�Ѷ�l�U�4o��£)��"x����d�T�iy�����zl����p��PU�����-�:�e`��]i�����
�g���R��OV�"��k\�Ե�:����Y>�|�g�V��8�)������j�`}Vs����ׁ�O��
c��ҘT@�VL`�mō��]��98�?�.�n�Yj��[��4M�G2��;eTV�,Q�AWڠ�
�9(jZ�G�ž{}Gy��-�F41�ԩB��pv���t�+k������~�
u�	��h�4��1�#FH���/�l��l�����(]�Ѻ&�]���!q��*����*��
�����UO0��u
(��4���V�=$u�t�QZ�F�6��x99;�tg����ޭ�?��X}
�u|m�;�
���Ç S��2L����$BU�]����7I�m�5��%���"T_�7��!CV\��(���3���G&S�^�Z�HMF�A�b۔�v&:7��9�b��֧N��ŕ�.�q'R�����3Z[A�4u�rϽ\���H�O,�vCG�,�Ԭ߆�=(i_[<�>�VJ���A�=wqw��Q����E#'}|���]�d�朆�2L(�0�nq�"��1�p._ZU³�4�15a:���0.�lHF��s�NU�'ڢ��[Ů�Λ������E?,ƃ�Ɵ7���-;]�����'����ةaeC�r�-�z�ڌ��7�K̷����F���ʊ(8ƅ[��(�?VTr.��6�e2��杚�m[PR��M�L6���m)�u<�5�\iΥHqW���{@��.��:�wi�V���w�c�1�Yq.֚�QV�,Ө���ؙx�o�yp|��M=�ު$
�r�g���mA�,黜땕��ґqyV�f��y�ρR{�yuOD�>��`��ʈK:躾u*��y4bQ�W�=��-�ף��0g0Ǖm�S���w����tI��$��Sٽ��6��]kAUGHF�9(rm�f����|�GC;�1J�[�ƙ�++:�ee8�
?���7�𲇿��ٶ��c=��E�t_��Ъ��DwE㍵hegǬ`5P)�N����f*�W�
�e5��P�B��4���U�;�Ӧ�CMH�f�egX�k�Q�{�,M������������P+)ɿ�Z�54�t��)ʸ�S�U�*�]��<�����W+��鿯�.f��f��i��_�
�Q��&�c��7��íz7�!���2����,���������}�n?������uqNU�o�蝆��nb�j[�����R�t���kF�w?���s��]��][HA@��;:p�x�	����?N}��z(�B�(++++*6�g�t�T�>��j�Ӕ
n��z���ҺJaAbʠv'aM('&�S�yU|TtlFK�S�>�s])o�����i���E�]��z���G5�휈��'��2x$��t���:���fP8M�%=��Iƌ���+�'���b)�5��Ӯ�Eءd�6�I{+�3&�\��*Z��E[Lhk��~洠V|��Ƅ��xh��KMw��5�Ҷ��[5�������p���j�5��.�]�.㊸ةn�~�����o��c�J�-78Mz�N�͒竭ɖ�-ێS\��Mr4�}�5�z����ց=j�����X�P���m�>�?R<Iwʦ?�i@��L���JqN*�\ꩵ��'�k}k�Y��X���q���]��Dm���7z��.��@Y]�A�H������5'U����:E�
��顿_�y�iծ@��}�M
}dv�]P�2yp��1���9<�D����9���Է�)���m���P(��u�%9ȕ[n��ig�U^�m��k�Я�T�tv�x�ە��N8
;�;��k�l���|���8"%|I��6��BE�]��Gmj�[���Mg��Ե��SJι[ʠ��YGp�VT]&dz�O$[ӕ�L~�ӵ��Tt%;%�u��
���P(9oA�+r܉D��J����;q��5<��?�|n_����N�x�_�,pD"�Z�D-�e�N�S���!w­�2���"I ����d�&�
g��3L5n�)�ּb�S�V��L2�K�W=�0}-��旣#uPud��ʊc�r����\����u��:nj��j����s��#���
����Um���#yƻQ	���+|a}
9B���Z���am��kGٮ����4�P<�YOzk���f��S�MV�.���Lcco���n�
��E&U�~�6�v��X[U�v�[���1�F�Ѕ��T��U����186۫j��:x��M�����H[Q�
/ĉTS
�<n~���?ݭYQ\�<&�-�rs��uj���z����=��qw��C�W�%�Qպ�jY㫋bڶ�-��mA�7���xq�++�i����w.V����w��w)���)���u�o�T��ԕn���յv�l�ܥ�tM�ܛ+��C܋�-:U�[�z���-4p�:C�7�|�k�S�%$���`�Kr����ΙY[����r��zk�7����Ө*$������D���]��]��O�jo԰*۴��eeF�+��m,:	ƥP.�5c4�+��+R���<�x���՛Ú�tBD\�",�:a1��IB�*|:�����V�o�6C�� PzNkee��b��i5Ŏ��[
OV���G��ʚ�[2�W�p����Y]+K��A�QT����o�G�CZ�
-eR�(�'V�U7��	�V���r�0.ˑk��0WevQ�h޻�����[�qwq\z5�z�6��8� ��euK6�R��}m,�z�mv�iv��&uA��Z�
G[��ԑ�D�X�Qwn:z@�L���C����C�mm0Vij��S�oFD���l���#��|x[VյV;++++rފܻ�N~���צ����M����[��c#hw�2�~WUR�P�tM���
���>
q��|�6�yXU�Q\).�ɭYT��VN%az/E˦i�t<��u��ʸ����~pry��<Je���j��9}C�u��ⷹor2.�j����W�V��\�Bl�w��g:�G*�*<���t*{�JU�NU-a�sf4�.�n�e9C׃݁;�,�g/Nz{���#��ː+<��-�@��Y���==��[V����s%(�3�(f*���*koOGl���&ǵlA�jxG��y?����V��n&n��xgA��v*�jʱ�6�J��jN���HdEȔ�e9�	D�
��
�L�����ti�ii�*6� ��.j�ջr%Z{eCIi[Pjہ�x�}�?�	�>3�`z��Ʒ���V��)�r�D��^�e#���nE��Nr%p�Z���(���)����
Ι�cDm2#"y�5�޷"�^�8�nFB�^��*v����B�Ds�cB�Y¸^�����SGx*�rc�*�'L�"s�ʏ�nEȹ<��+�Wt��y�2/�J��Ijо��#F������Ƚ����V�d5+w�!GL�Ȕ^��Ys�6�$Tԑ�h}<
r*��v�O��W�B>���Ӛ�����"�
(�,jZVm#i?�I�ܲ�ke{)!b
��]��FE�o[��Xҝ(�ū���eܲ���]�-}OPj��eee'I�z��6�j�&*���)���ΧW)�a��ۼkG���hО3�u���n[�z�K��Y*��I��L��r�>>�}
�tm޷-�r�����ήn��zk�����P����
O��x��E��99����+�\f�GA,�<�+n��L9�(�p��8�ۼk��S�7\�	��j�.N�N�eMU:�n|��	L�.[D��9-��51��7��zܲ�������������[�����y�>H�����/۪fQ�,б2�&��Յ�����ή�98�;�b������@t�@a8�>BQN�A�j|[��x&T�uO_`�ŝ<B��֦[cj�b|�ƥ��햚���VP+:�ee�����Y�rܧ��n[��IV]R\�2��[%/m��,"4a���<,��|����x����jg��Q�'-�j؇+�mXXXXR9W@�'����n[���n[��o[�qwuwP.)��|�k�gbptkrܜr&�Zu�)�)󮟵�%�7�����YGL'�/�<n_���c������rh��aK��o%ICP�`�j�H[�ruݝw']ʅ�d��e
j�!CX��UAM�����m����ƀ�i�zu�#e��bSt�s*���b��̨!c�,,-�d�=񻖻+v�V�����#�D
)��4<�zE� ����,,,-�ll`[��ƘX��^���ǁ��^�v8�%9Z�����y�g�{�A9�
�_E|��@elX[Q��+cL,,~���#Hw��"|����ƫ���3%d����(��2���|�� 0��ƣ�GO���/�΄jx�����..@�4+(�A���o��8����?H��Ⳡ�������(�x��_??{X_9D
)���-h�o.ۖ����Ӕ}�|���/@xt�/�
'V�\T�.
<
��<霣�Wy�I�V�/���F�G_��Y_?�փ��F�8��[���tϼ�J��\��n;�#k�ODr�����p�eo%ӵ5ۆ��i�7�?:���Ӡ�:	�8@�����(��)vrs�a{i�Ğ0��p^����X6՞SN���b�|q���|cR���24''�
�����CO�շ�S4��%|���M�$��Á)��(��<��������IM
���q8��s�uΙ��s�Y��A�a��՝��2�P�@�P9Y^�(r��ˎս?�nƙY���k�v�+����	� ��}�ܡ�<�N1�t�� �Z�]����t(;���j|��tώVVVVS�Q<�D^�����j}l--����S]#�j����ܾ��Ŷʒ��"m�ɶv�hP�R��x��vi�rN(�J!Fr3�|��"��n�96@��O;�=���+��w���]�x���r�.YY�r�-�z�[��&pc������W��(�"
�r���B�TK�S9L��௴ľ�J���!AL��5��[�����T|�Ť�<��)�ށ˱���&��d;%9� ��n[�=�zܷwZ'���d��FI|��92�/��#�j�-�Z�_lqB�Ѕ�%��d-t�

`�4!lh?������Aɿ�o
"YM��w!ߏu�w#F�,6���P�)j�m
j��}���j�}��H��iSm4�_k��iab��m]kyw�V!k�_k�B�P��9P��6v��4a50B�H�Q��H{LA��%��?	 !10A"2Q@aqBPR����#3����`4Cp���?�����pn����͉F����{C�5�5����I��7d�I�L&�B��M��X+	XJ�W,�Q\��\����V�l�
��8��B0�74&�u['���{-�'b��AXJ�V��`\��XB����R�<�u�V�Q�@��
"Jn�4�':�d���l??r�	XJ�W,�Y\��k���,!@Pu�>�=��0�&�D�V
�d�����vR��#E�J�i����iv�r
�.@B�{�K�允�XTl���t�+�׏u}A9��9�*F�n��:�Q颈P��18�Ҁ.�n���	��"v_Z�!v�e9�.�r�ݲ��z'�t4�l��u����eC����: 1�-��Eоh]��|z}Ū�
�)�j�2ê:&�w�p�v�s7L�*\�`��&��J�G�����TN�"�-Ԯ�|s����J#���BB,Ct��n�]�|T'�&�۰[�5�M����S߲%n��T��Q��'0��=�j��Z�0!wEwCT7NQ���٫P>�e
Q��8�۩[�趸Tߺ����~���DB���DCd���Sw�;�=ԩ�JЄ��F�n��v�7J�?tF���AZ��D�U<A��%F�Q�f�!��O�qSt���{������{��W�V�)Ԟ�3}�t�t�D�Q��,�BtJ"����T`<4�~A���QF�_Pr(^"u��*V�`�X�S�'#�S��j�Ø|�Kj}�ߊ�[!7�uU|�u��y�&�xT��55�["3e(���Ǩ�:���g�������R�N��*�9�֙�湆:êDݱGd�Q��+el���H�3S]�r�S�	�,ANHQ}[Kq�}W\y�T(2�a�B��aXV�aXVR�jx�[���$斘p��(��P"���\Je?��\Է�zR�F�&ᦫĦn�.�gw��\��*(P�B�
°��vV�:�/5�zED��$�Naj;ꈃ*&�ɚ��tT��)�o�N���U-��
A�1`XT(�B�
��_�4*��v�x"WtS��r��!wV.j~d/:t��X�܆k�Mjkj�B=XU(��p�h���L�ޔu[�"�(���2}��l��my��E!Jċ��T���ʲ��x�Y1��A���	��Q��ثUf��G�D�J�7��2�5[�[�vV�N�����홻�y��7…
(P�B���Au?�N3*�SZ�P�(���ӊ���?��z(+a*T�Qɺ���XUA-NУ�U��0�E�q��j�z*a1���E\�@�����i��sN@n
. w�d5U��J��}�͘ n��8s0ҟUD&�Q��$S��}LMs}:@ \}�Q����H*
��a�ʕ*T�5[mv�%r�,���Ԟ��r��j�L0*T�)�ʈLAQE���99�g��\Z�6������ı)SЅ
(P�FB��g�Lf������_����)_h�)�
!�D"
.�
�@+Uqe�꧲&u9!B�
u�V�;u�G8��\}���-%QLB�y
(Qt(P�B��r����f���y��}Q23�V�M�+p�eñr��TJX��e������o'	��J�(�J��-J��C[��ӳ~���7[����U�G\�ti�}Sī/��6�6�ZK
��2�B��@�����|���0�quZXƛ��0×1s5�Jh�J�)�k���^�K@q����JC�ҭo���ʵYe0�+ҥ�r��?���m5jHݺ.v.(kyZ��jڝ��VnJ���#�'ѧӥT�v �Wea��=�x�'�	F��e�ދ��)��7�:�hR�cT������c�6��0�*�ěi�i�����	qN�T���#A���"�֧O���#@m�we��7����#\�ګT�:�M���:b��*Vy=B�C;6�k���X8K�EJ�7�T鲓p0@D�S�^z���k�����E�(�J������S�V���)��L��+7,���*���Чx&{+M�և���j�g�l�Z�U�:�'d�5WvB����K�[꽄~$l.���5V�Z��;]j{9R��T*uYTK�W�?T��q�Ǻ��V+w8��o��0Wl�n��K
[a�m�����79��zu��zQ����8c���\&�]g�7
�Z��Ta����U�g��t��J�8���o�I�|�S��y�MkY�EҥJ�*S����X�|�'�u3��Zd*n��\��Ч������ ��{���N�ÁA؄�n(�Q���6����B����a?�S�r����*��}qh���y.u֪|�.hMN��5k��-�CU{>v�.�����
Ўfd���>��5VnF��|G�M��9;��gwٸ��(�_�նڿ�4�J��V*x)�g�:��emA��U��aW
�h2[l�5�wN2n��4�jT�%Wfiwed����;�f�
@6��w?�S���L�7S��N0���h�Nj�a;�+'�O�C������"9��*I�U��*5�Z��xr�Tkg������Uv�Us[�u1'�P�H��vN��Tֆ-�;|�ߣ���.����jjj�J�G�6Q��G���T~�q��#�?�T�������M���v��"��
�UM��U2�i���ުg-c����7
P8e����\Uq,���d��(P��Q{���;9�V쩱����rpDu+�ygP�4&�ƪ�<6�ɏ~�&�n���7�)X�r+&U�ʊ���L9�3�Yݹ�ڬx�ұ��Wg.��p@��\��; _�(���Uw	��R�)R�#��������xԢ�N	Ǧ�a�2\�M�ڻ�F���'�n��%5���so��QS�����F��j�o�A�w�T�i;	A��v˔�4���	p��s�e�i{��7�pj��]L��^ۈO	�������5pTqT!�5��@/�61Z�-���0�rǙS{G�N�9J�x�5�T��s�[q2}Pk�
[O$I�
��-EFW"�_���tjf�k�HswVw�����.�'5��j-XV�S�T�aUq{��N�	VZ�nXP/�5[K���@������]6��`J��n.9��N�c��0��j���Ptn�iبD��u�.���.L�Oʅg�j�Ol�?�U��8�4Tx�*�;CЩװ;�����U��Zf��*����M*t�R�XV�aV���,���*�K��!��w�^.8�QF����_��+k�T�R�p��k�7�\�J5ʧ�B�k��,79Uty����!V{Cmo�3�� Sh�)@�J��5���(;3
��T"�Z	T��ǽСB�\DS�����4:�.X
�O�*r�ɔ�LC\N*�} �{{������&��,,n�L*�ʄ*>@�X�@��Ш�ch�{\Zd*5�]�ە��t8u�V�p�4�}nE��Q���X}��,�������g�Uk�+x)xG�x�S�i�ep0_U�>q�0�Xu
��n�7�K/$p�|�z,�
*�.�9#���F�Yρ�U����X�M���#G'4�Ů��SSO�����.}�B���Y�n(��R�Z)mC���J�b�Tmv��*�'���@���cT��
]d��i�b�ki��*u�A��ap�#J��?�"b�#¬�M�k�{�[l��h}���u�ʇX��W
��F9�]��~YN����p�ശ{�P(�㣖>w8���"�-g��=�b��5Ňw\;��O��y��+��}���|�:�Qj��\��9Ԅ�Ubq��{��uw�j9�p�h��;gn��ӈ+-v�f ��\q�Y���4�NG�l�g�F�������.�e��F�:�zF�S�b�>d
B��eˆG4�¶��Ҏ�B�f�ʤ��#��th�}�ؘ�q:U4~��\W[W���
��y�ks��o+	CK�	�7�6�X�7+٨�d��J}���R�9��?�Դru
)�b�{f��+M^uI�Yh�j�A��Q�36�*T��2���ݐ+��;�Ol���ꀪRXX��[ n@+�谐���sYHCS�iֳ�{Sе9YV[P�Eފ�0<���
�J%��*�4�;�e�ʧ��9�N�P�B�
F�R��)�R�].�+Uf׬j7�s�; cCq
: ,*j��2�v"5�
�54)m �ZI�]�᤬�PF��\ٟ�ip}B����D
�%�G\�J<��;���*T�R�Nfoy�P�aP�aX/s�F"�v�]�o"�60�⨫U[
	��S%
M�
�7�<'Ei��-g��
�NbQ(������T�&����tZ](���EСB��h>������8uH���G,&�b�>U8�P8](?���F(�𹻪6ֲ���Zo���X����
�f����@��T��Q(�rp���9��A�-�e�������R��)�m1��q�U�7�Bn�8AT�uc6��|��6�����W�v��~�9l�V�+mL����������[qQ9��J�7�?�tv���{5.uV�q;>&���fˏe�˘�O\�Z��rwR��Q������o��P5��bo�O��g����-�͊�k�p�14�E�8�
2XDS���?|��?
d�s��V���t����c�;�C�U~���?��e'�1�i�	���}��*��*W�˴����t]
;-j�Ff��w�����g�@a�!B°�*^� ��k�i� �5P�B�
,+
œ����Ht]�o��}���>�]v?ee+p����U�b����{���J�p7�*�M��?ғ�uz���5*���u��N��G'��Ů�B���/��?f��B��a�������i�,��2.6M�>�vXV�aXT�5Z6NyvV� 7���%��:�JB[�]ŸK}F`�k�MСD)�M��-x�쁑��Yy��7p�p�mG���k���N�(E��U����/���AQú�9sܹ�F��u�Ҙ�?P/��U�?�F��I�­�l������S�T(�&r��V�t�Aͨ`)RV"�,AH�nh�aaXV�`�!B°�)�,մi����hkvhD"��6��)���{-n�9�f�ĥJą�٥2�U�m;
&��E�}����?���8�rN�/��t�5����^킶Z�it�l�ʕ7Y�2Td�B€�=ĄB�H;m�ȱ��.E/�?Eȣ��
]�?D��B��/"�;p���~q�J5�g�*3p���Z���S�!,+
q�ц���ghN�)FH�
�M�8D*��j7�Z�T���m��8�-
�,��L�/�_Gx\��(\Uحn����!B�4�2�F�M{_�P�@G��U�!�{��hovv힕��#�8�a�h)�r>��|J��A��
��DZ�|�N������2eV?l�N��S�
����z))͕���=(z*���5A��f�w^ɦ��J�d�9�a�k�t�n���Zl�;�>?�Z�-��5�"npQt ހ�O=����Lؚ�Ĥ Q�蓈�+
e�
,*%�$(P�B��Wt0hlu��T�R��t{�~���n�/°��Y@s�P�aXB��ѫI�׵�l�`���'�{A�\���E�i[�v�
e��P�/�
,2���\aT���*���>PeM��l�Fvu6�z,ec�S)����C��
�`��Q�	XV�aXT(P�B�*�*^&J���D�en�P�B
P�Z���ʩ�^��{�SW܆�n�P�;:�=��z_c�ʕp�[(r-ت#�f-f�b<=�;���'C����P�B�B�����R�����wh(S���R�:^r�q04���T�U��*dn"o;�:P�B�3��L�H�T���AR�J�O�M�ֳT�KU�pB¡B�8�[�P��D"z ׍�!�ne/UKmz��䣡�+k�ON�Mtn�gT�w���=�e�cEĨP���{��2�hF���
�
5�|t�EС3�B�
&�.ԡL�MZ�B�Z�O�᪙	��u3�p�����zZ�
~�A9���GF�]����õ
(P�B�
(P�B°�(�7\�C�mT�^�E��e����k��)�
Ls*l�HSB���^�c�i��FW���uAکD��wS:!�j~e��9?|��|�H8�d�Uf���ց�#[�/��~��w�_Y����?�}d��߁}d�¾���B�·�^�]{mč���Hת~�Q{��8���P��l�l�~5�ƨqk]��T>�Qv��-W�t�O�d;��]gxUC)�X���V4O{�z~`���1|��#(�g�IUD�E�D�B��awC3|�l��)�;�푭D���t
�
*b�4���7L�N�wd��5�zC�	''Ts����g\�7�:��0��|g#N�y��i�2B>�j�/���q�:쎁b�)(�>�vɮŖu�7�S݄J0N�4N���Ң�e��fs�KB/��J�Uj�g���A�aT(;	A���P��J'�j�Z��F�4T��8\�a��T��|Q각�30�Z"#O�%yuN=�@�P3�ۡ�'ĉ��n������|J��ʨyEP�l�l,�5��NB,%T�.��-}���4ᨪf
�Cp=��8&��oq�9��	��/*��)Dz"%4�h�
'=��ꌦ���UV��=%�n�4��d�q7��)��S�]��S�
n<�Ոl�G�:m��B�J��Si�yTJ&tL��#E��)��w�4�k���N	��#I���	oP��A:/�)�·Xa
P�]�
��V ���9����arڰ5a��u������?r�)�����)��Bs{����r"Bsa�������Mݔ\IN���<��!HX��0��h�{"�V��}��w��D'	�YF&VuO�!�hŽ��4^a�c0�@��1)ڍ�=��j�HX��0��jQt�eJ�T�'�/�P��F���D�|��	�"WmQ�X�捂Ӫ�6�j��\ōcX��V2����b��B5��W���{K��9�J�9cr�T�?�+��> !102A"Q@aRq���#3BP��S��C`b�p��?�����i�7�Kf%c`\�{.h:�o���|k�!Q�btn�%��r�,@,m\�.s�1|@\���Y˜����<�Xܤ�3*��Mb�E��:��+��D���P��D6��!b�Xڹ�\�sW8.w�s�<�/�>˚��r�9bqZ�T(P�B�
/������D/�N�`�ȝWqMD]��)X��=�1���u�b����j���#�|A\��v�V7,K��GF.��T��'�/
e�"�7;]T".��L��l���x`�#l�Ϣ��#k��|UU������Q���"T��A�&�2+�Q||�| gP��<�4EJ����
eL����{I.}ж�>��q@��p(����u�!��opG�PQ�4�[/��54h�ݾ�u&����
%�پ�
n��#��q,X��tSS}��l�N���gp����� �*!l��0��p���7�+L�eH��S�B	��=^�:QqJ( 4M��
�]�-5����"Q��i)�4X�U��i(쏺�(w篣�P��e��ۮ�{�Fh¶�e���4�����SF��1�ΊctJ7L =C=q��txP��
��jtE�"а�<7�β�-���!�����J�:�0��#�kG}�B�Dd�+7�:�n���A��~X��:�)�
�2����2AG|֑�Q�۷P��qj�<�굉����n�6��mf;�ЇD\
v�3�F�;Ҿ�����;��[��n�F�D tDy����C�Hh��Z�oE�͓m� A�u���(�iw�Ph��:����kW�:�����.!B�[�A���Pc�'#붙��)��CP�o�*S^[��ín���:AlT�ҧD��GꝚ�ې\B�>�!n�
<&�'tmyj:]���X��ʕ*rʕ)���T�
v�Ӧn��Na6�bCE)�#���p�QE�����4���cc[�e8�S�j?#*������ޤu%xAu�*bD�eWh_�3Z{4��;�(^D���t�W��v�|�^Xd*5�]<��Jd8U/tU@�h�_��"Wݒ�,)��e>�Z���g�4��	����b���1~�j�(�[]�[�.�s�unj���̜��-��,AcX�%��EIRU���f=au��H�4esV���桖��꾩�)�9S�9�v�����2�]�D���|��l0r9��Dêk�\D&�s;dBD�����L��ؿ�B�2ճI9$,Ab>�<+
°�!�[���U;�(�~�h��R�in��g/E�����,(�!HX��0��kX��T��-����ݹD\�R���R�6F�u#4ecq:2ı,Eb*J׮.� ��ݞ�qSsN�/�H!0��Q?/A�1|�F�]��iP�pE�)��B����Ê@PBm��'Y�7�4��Ȁ]�T�k.�ɍ�g��n)E��:��!N;�Z

1lMV�!�n"T•*T�C �d�)�f��G��T�6��ץB�n�ʥ�^u�a[�ѳJ���T�;����Ii�̦�ҙek{�O�3N�Jb�p�V��oq�?e��'�`r��,.Z�T�N�47k�Y\�L�	��Mr �B�	�/0��]�Oᖆ	�A�u:5*v6Sxm����=�.C���:�j�JwmG��]2��zt0�_͞�z>כƉ�rѳz��
!�J{���Ul�p�,	�����cO��}�^׉i�]�4�f�Z�yM�+-��f}Sn�Y)�u;�V:4�m�Mӭ��[|��|c���d-��m��h�X�Uݪ�
p֑�R��x�c��&���
�6U�*�`�~�o�!��=�K��BD�E���*vv���r=w���:#K�ս�U�|�U]Eؚ��,��\vV��(��MϨ�}����]��B�	��V�Z�;�*6�j���J��[�}��\3Z<"{��" ����
4��rw\���4݄�[�Bs���[�S�
+K=��Jw)�u��&�\S�.�U�7M��B����
�|~�oqX��A�Y��s9
�W�T j¢����T��ov�\�w\+m)o0t[P5���5�e�Z�T���TyF�
��	�K�'Ej/��n;¶ZEa�1NL��_aѳ6�!E;�n6��V����L#h�<����2V!u:Q��ɶ���-s��������E�gÜ-�)�.8���}�����С�1��z�m����*�ְ�f�6�Q��׻�Uc���!bA�TB�k�;G�M!�FZ
�I��.(�nYS�*�w�8���*rG�5n�{\2Sg1Ѐ@�QG�F����.�c�O�꼪E6����MzmEN�a���O��3��v��L�B��f�͢ףy���.�!( ��P��3���B�
^U��{
��_9�)�;51V�XW ‪�
�R�A�U�t鍌�Q�Q\>�},D#�q�n��Prc֕�8n����k؏����YmT픹��G.8�cEɲ�ͻ������@�s?���{3����l�����?F�V~U1�^��]쀝JǨs,¯��p�2�_��aD�
��s���Jm<BQ��j4-B���D��uT��4�r0Aqo㏻��9(m��&��2����Bh���+
¡6�Q�S���1��l�b��A���,O�~����L���Fs���e��zs����
�h��Q��vD8n�NɔI�B '�r4�� y�;B�X�i8�����Եn�9T<�;-��ZFX3U�)7Mp�15�3U
(P���m�M�UZ͢�F���[E�ܺ}���D"q
LtAMr5�>��6wZ��=��?�o�?Gn!cX�%�J��R�ֲQ��ڭ6wY�Z�f���w�7at��W��6	�u3-)���8@����
�ч���	-�d�3�W)��W�0�e���Z>����t�4�兄7�A���\�|�b�j��*�qG,�B�}	��t�`y�{��ҫQ}����w
J�2J�?�L���}j�$%J��Z������f���MG�����;�Z�����t��EJ!�e8�7J���
�apn���jp�7W�z�Q���y�w�Ym�,�7o�c�Q������hڿ�-.���=?~[i�pԮ{��J�WTh.V�h2��"$B��*h�m��N��s,T��wV�_Èn�GUv'�R�j
S�Xc��]��R?_�!����D�p�����}}���oߕ���w�(}��^�hOar�<	����ܼ�W�'�Wv$��ic����`l��Tu8P�u/�y��4z�Q���X�]fg&�X�qj�Z̬�Cl�{��M��rZ�" ���Q��n��vnB2GC�R�[��	�o�V�DW�H{�AV�E�AS��g�=�X�s*�`�J���Us�!�j�<��M�4�}�Ñ��A�*�D�v/�AE�e�B�;�S|��b�\�z
~'z���V�|�*��E�A4�B�N�194���l�ʤ}�f��ݳW�t��E֮�M�t)���%U`��Ҝ!��T�^Ȱ����kvF�N˘ⱹcr�<.ouG��ƪ�Nc��!�A��LU�>5��]~uM6��ݳW�tf��{vB�I����s*aТ'P�R��+�V���H�E؈X�4&�Ų�[
}b�;��\��"�Y�|5N`�Ul�{nr=0�kc%��.�����]
:N���ݖ ����*T�X�Ƌԓq jU��0��/eD�T~�q֡H�)4n��]��hUZX����e�����&���P��Z���ݜn��j��1q��abX��R����8�'FXE��PĒ�Ψ.���X�n��!�?������mM�|�]
]
/�݇��5nޯ��4�X�%+5=�pD\r�t*��NU!�
�׉S:��	V~�X���sE�t]�Sv�T��[��h��9FHQp�1�rڹH�2���B"�P�D-xX��b~͔Ӆ��
D�z����H�o�r�m��aC5=�s#k�.�&Y\{�����D�E(ΥE�^��B�5:�6w9U��iMK�d�y_P�!hB�(8�S���.Y��;\�ql�dn�J�*T�*T����lv:����3�J��X
��0@���j�5�	Ni
n���tB�E�1]h��l�Zj�̪��u,��T,Z8HP��e����X�%*V%�cB�mR�hM�
�V;uk��?�V7F�PQÅ��:��ԩR�J�)���7&ڧ;��F�َ�d�g,ջ]O|�[�$�S(�2۫��6�V���:!V����hUZn��.�B�dB���aors����*Pr+Be`S9�U�x�GU�DG����I���5r�
B�B�^�i
'[�o�EǥK�� �4ݸ_��`�êB���7B¡Z*��Ѭ����������#Z!]
�
���U/���\q;�(95�dʁ˅q�Yb�}Y��dm�A�M�M�9J�*nu��F�O�4�۪�o���Ly[����=�T�vg�p�U�f�P�h���P��B�
��2��Ѩ��KąY�ȏe�
��/2~@92�*ugue��&jis��\��h�+�W��s��\]��4ʳ?���9���N�-�s��,8�c�ċ��!a��
�����4��
'�[V=1���yj�����T�ʱZ�G�OK�>ZX�u��nz�{�q�ǖ
�v�㪅
��b���SR�N������U����٪ٌUl(�/�`�>*���c��5�A�ت�}gb@_O|�;�v�{v(Z]�!j�*u�S�ѥg �WJ
T�ns��B��J55�p��Z8}����=��sS�R�'U0����)�=��UJn�a�hP�W)��U*�oЯ����⍪��?����l�*�j�v2�*�YK�©�����~���|f��*����ݞ�wN����Uhx}LA
��"o�7��)��N�ٛ�A���lo�?�Z�+�R�Zvd~@"'���Y�:�	օU�� \�*���>5s��{"�uR�)\:�+<U�Ͽ�������*�6���OBS�{���u�ұ)D�sq��e�XcU��U��	:&�y�g(�ZW�u���I�R6)�\]S��V%����9���!4��@S�*t_P�`������p���c�d��	�f��Su��*T�
�X,���^��)�MŎ�(P���Me&�kF����#h+�Q�W5s1J�|��X���,�ipQt_e]�x\Y��O�>�M)�(A�
��M}C`}U	I��8�:l�0�B=A�������8m�J�Z6|t��
�U(��rl��}.��u�)RRaߪ�bF�Ʊ,K�*T�n�Y�l�—�V�J��P�B�.c��Aq����T �B�Z�&ʡ����~
���f�uX�%=.t3V�qC^��Dd
'd)…=��K�Uh�ᵱ7oV�Ӷ��T�}�V9��_��E���B�
"��ּn]f�+�=�?���,�?�~��ž�d"�)��ն�o�7�°�bX��EF�?���lPZN/�U{M��L#����Q��(�&{(P�B��Eڔ)��i�K �ø\~�������vm��m�dt������T��|��0�Ä(P�B�
(P�B�
°�L�@����3��߮�3��w$��p�U?䟹>��P�	�eM�)
hST,la\=_�q�*2�ͳV�k��b�|B�z.c_�%>�e��1�~��!�_�	e>O��T|<��T��_����`3�O���?���
�~K�
/�

����_�����<�����6�?�Y,�jc�Q�6h�3�4ݻB6+3��?�:�?U�:�?U�b��v�J�_���S�5^�v�	���A�ꐆA��٪v����\���j�Ӕ�[fwi�6�wCN���.
[f;#p1�dp��9#-<�PB��5��GX^7��S3��ߵ�4GTA~�Q^Q�>�Y�z���Sp�T=�@����V�B����s���{s�!��"��7��q)�/�So�پ{G�/7��"T�@�%NR����|-����29�����i�Sq�mt�t��0SI'�(����y��D��aw��r��n\��5�\���,!@�ڷ(�#웽�bD�SO���4�ߪ�
��&�=�e��˖��z�9rO��W/�k\��
�XB��>�St��t\L�;#7�R�ƒ�;�r޹O\�.I�\�(���r��mX찷�@��]����p�v�2a+X�
/+���zr�U��%����o���5(�»�|)�_�_�_
�t(0h�\�{.[}������H
 !1"02AQaq3@��#BRbr�����P��4c��$CSs`����p��?����&*��w��3�u���'�`�ܦ�Nw3$e
�Y���;���]a˯z�;�Q��.tVi)�7�q��:��_��]sO%�O�*B��5uq<Q�ax�\�/0w-`95u����g�H	�y���B�`���D�Q%�G�虁�+X+�4w��3�u�\�y4��T�7��;�]⺶���0�
]d��~Qu�.���Ǜ�7+���2�V�I_E<W5�����4]G���qob�
����Z�ς�r�y�S�\{º�r�G5�Ş��rY�]tO��ԯ��Z���qҝ	D#<�rRR�B�Ŗ�<�+�U��{��Y%uAܯx˭>
���]s�R�w{��j�pe������A)xQ2���qi�F�-��~�F�x�r�3S�v4އb2�E�L�,]��%HcM�+��'����Q=詩I`�C�;A[�?
>���S������:gc'���yS�c&��vgD�ߤ�E�a�u�9.4�ض�*t�.JEɧ>�h���
&J[
�WbJ�˚�}qn/2���*3V��}��pC��!��T�^��b��CO�>v�D�_E�t
o�cٍ����I#<f�͈oJ�pG�i�-=�!J�)�E~�Ws[̮��+5�w#�Az�+
�S�pOnࠟPZ��ٝ���H[����ՏF�ͱ�Â#C��qZ�N�v+�_�3ڸ)!D���������AJ��EYƫB-�63~ӡ�}۶ "���0U�C�񢕝�ln�J�8ӓ����CYm'r�N��"V��pY��O%7a�n�M�-<�	z�M��hh�&
ċP�OM7+�7\T�@��w%6�Q_폙Ry�����o�v�;��_}��Z��Q~Qg�s��x�9�j/w��A��C�v*��>�]�9C�Q��a�샢h�'�E��2����ẫ�]X��%V�jr�ܚ)���9�nZ�]bV*e�c~�q��2�&%ӹϔ��3^�Uͼ
0�㎃5n�$���Gu�b
����j��L��F�Q;�z#;�bе�+j��U��,V��*m�������[�h+�k55�	��6������=O������zG墭��⃘�杣K���]�]b�Œ&�hͱ%$��8.�Q�4�
���Ҕ���C#�z�k]?��b�O
�1��!�h+Ybhh���$��ޙŤZt���q
��JtR$]|�W{D��`
���L�5���Z�s���X��j��U�X�e�l� b�K��`��Èܛ˳��<�-�$3X��[+�|{
8���Ko��ǰ�HNh�c���f����P��n7�lp��=5�*� �Ȣ�!�7��F\/U��C
C���(n���	�2�p�>��i���q;qy�
Ѣ�w��U�M����9��͊H�����ܦL�&,1��L}Ј�&;f�E���0�Z���is�� �|O��b���4s%4KEWZt��/��Ï��M�>��	�^R�B�cD���>(Z�8��N��Q3sv�[�7����)�3��K��uw�{v8Q6:K�3����ˮg�W�ٽJe�9g���.��et�TX9K��]�]�}ԡ����,ha�¹�7M5��:��ɱ�>�3��I|G1������ō��K��F��������s�Y�|_e�u��W�ϒ����U�?e�u���s
_��+K�Æ�	MƱ�(�(�0��ڠiJ$��Q8���z@�e>�ϹN��$GbJ
h�]{��IU°;
�5ގœe�dxhr#9:���1c7�9���%�s��'���N#��s��qY��J�
��X�u���|3�;aY�[�34Q&���uuF�\�⵼���WZ�tgx,ȭw1%���z�� ���L�,�g��¯#b7�FDG�eZ+���f���t3�2x�nQ����[���^��FSZ#�a��
�1ށΖ��7	��e/�w��Œ�'Cxx�S�#�}Y9����f�?d�Zd�F��G�Q���7�O<�n�
q]��&40p�J#C����pR��qخ$l��b0��3B��H��RP}�M�tqV�b�ӄlo:9v7
�I�v#C�>s�A�}��h����#S�[a�z�Vs��fR?˵ӿ�B�TxM�"c�4V�n��y�@n�Upi�Q�
�{F�9+N"~V�(<�j#�о4gpۉ)�ɿ�A��9�qs��'m�؄Q����q\�p������"�4j�`�$�8��ʠ�μB�~(ց �+�3XѰ��5�����AD{˰�}.��p�ڄx�Y����
��n�条Uu�4L��N��?�d��V��M_�8��Mʥ�~�ɖF��J47B>�L����l8N��$7Ie��ƾB�m�ЌFJ�5D�N�E�:��v�*:x�P��.)�cCXۀ���=�.�eS�W�������L��'�;�U�+G�HD�wz����4�A�+g+:�*m��R�J#�ƞ�#���j��#\�h`��,�o��G��4���O�,h�F
r��-7��#�7j��ܝ+�H��dz�d�`��[�� Ŀ6RW�н���Dcۼ�xS�Uءn��^/��(Ld!��� ����ppPcJ�H��,�qz(p�N��A�\@�ʌ�����Xӂœ��A�Zv�!n���,OG�ɾJ��M�Ȯ`kA�2�d�f�Ci'���u�
���x��gp�I�ڱ6D��&;�l�83�;�!�<�k��A�h�eQ%��⮦�X������dZ}�D���N���t�X�]Y�63��� �S-���4zX��ˊ���
i�L^i�[����t�8���t�|+���u�"�lF��
��Bnf�M�����
@]l���Б���΁��+1�D�*��Hq�=ӤZq�n{�M	��Z�x�K6�`��E�>!��0�d���
�l�.*7b��
"������s��8��^O<�w��8N�
�:�+P6�Oh���dHD� ��^tL<T��6��T�thy��D6�'pFA#�[�6��8��:فރ|�:�O1�8���3iߢl��ҹ0��':j��C=xM���Y��md�{��StS��b��E"c�>�*�\'E|WÆ8���C�[-�y<Z���8Ehp��\���I�
(u��xW�7n�̕=�.�=��#MSW
Y���;����F��J}su�7(��*"�^F91~bĊ��6u�u��W�b�Xǁ���N��n>7�~�4'�"��ߚ�{^<����19�,�&�5�\I��46�k��B�
�z�3�agP���ز�~sbV��.���i�
�i�Gɪ�0;�f�>��_��X[�6�ͪN�?�f4OE��?�M�]�hr�PL��+`��l3#�����-�)��\��[K��r���7l����L�,6���[�>�{+�|a�rc�M�
���n3+7��Nç4��q�eQ'� *�v=ks��Do����N��w�YF�_A�U�̓�����!�7�Mϻr�x�Š�a�I�iͅ�X��s%f���.��Јp{��42\�#��W^w�1�c� �O�rJ�2C{����P��N��Of)��;�єF��o-�e1��0]�bp����V�s-��Vc��)�2�a��2��_��G�I}�"`�)�vD�����țt�~X�y��C�\h���R�F��N�m=���|�_&߭0 ��Vn�l��L��4����.-�rrܪ'xR�?�q� ��\�n ���'�ѳ��V4�ۚ1`7��yH��A`�Լ�w�ڽS��w��[��‹�v��p�0�7�N,3�242c�A�Z�=R���kZAk,M��1[V#��'��Wj���rx���;�i�Q}���
�L��L�<�U`���yY��X��������ȟl��f�g��w�&A9��YCa
�4>������Q���j'�~J+L�V�y0XX����]\\Qd�]���dѥ]�v���U�f4�Q;�њ7��q67+�ؤ�q;�r�L��Eة�����]k<Vi��"*,VjEkb7������n�~" �/���[|�ޛj	�VŊ����g����,�&��4����y%f͹35�6�;��1���S�.�D�J��K6MY�%&�Aઞ�|VIkrf�x��擆gE1
]��>E����&��ͻ�o�?$9�Py��R�t�X������y��q=��kK���.S��grV�Y;Nl!�:g�	9��ak�G�� 1�
'`�^��},?N���en����A�ʑ�]��!Vl���e�oT~c�G1��ݘӗ�d�~z	'����#����dl�h.2Z���ڶ���׽��b/�-�`t_̋y�F,x������>sO�$A�VJ�I둍�4C�6��e��?�7�����߹E�T�ѹ�;�YU�-ㄍ�� via�4��\���r��+ڞ�
��6�����'��1nAg���Fn56oY��q��m�_�����6_8����e��id�@��(~���V�5�`��e�{&Ȟ�H!D��W����1�Ni��2�ͼ0U��������}�?��f='ML\P�Ջ��$W�l��A9�47e�kVL�B��
�l���K��K����S '�^�bג��<zUn�U���c��c�d�pۢ������m��^��7h�,��>�2&�,������c|���u�wف�L��7s�x����9�B�+&��r��m��Z�nq\�ى�C=J��gx��[�_8��rnW���I�Ho9��v���Ōl��i�b��F8Rغ����4�#����c�:F� o��'Ur�Pތ�M�V�����7�Ѽ�,��w�����H~�l5��q�P�RCGv�����ɲ e�E�s�z�E��d�߮߭��ft��K��S������!�<���śrf�� �mcpL�5�D������f�w
C�K\6�twj��'sj���Մ=�|r��*��{��':6�9�93�}s����3�?���L�b<�Z��9{wD�]a�':M���k�8V�h�aM�g��3"���<Ӹ&������4��2F��X�`"~릅w��2sH�I��`K
���$[��C⼼Hp�(: 9C�ؘx(�4��{9
��ӈ�є�$]	�G3��Y7~v���y8�;ڦ��e��[
���&������O���ƌ�m��Ny��,��Q#$��P��hd�	)Xˡ�:J�޿�D�d,{�KE�WX�����qFQw�����q�9ٚ߮��������k���m�=���?��c_�;3��ֲ�C��X����6L���|A�| r����b��8�6��az�?;v�ظ,յ`Q���AB!!�;N�S�0[��n1�V��i��e�2pS�� ��U��a�ϭ<�y����\T���r��u8+�W�&�Sj(�?������=(xx&��p}Yȍ�з��j�O-0���i�1��D�����3���b�ٞKV\�tF����V�Z�u�s�XM^%`h�E�e�GNT7A&��N#�
���I�E�
�xZ���-R�
حb�_"�Us�Z��W	
� �nu�&$�;Yg/�'G�r��1
o�z�E^J�b�n���nv�ۚƉ�H��I�}�K�z�>��)9����i�즳�)ɀ��Btp���sM�p�	�`
hl�{�c|U�Z��y�閡��
	��g=��\�pX,\Pp�aUc
��)1��9�*��z6���q[��
<�8څ޷谱�\ϊƯ%z�W�oW{��R9�t���;�c��ʼ��U�u\%�௳�7s��چ}o�8"f��~�9���)�c���.���4w�J���io1I��M���
��~q���P��h.u�	�p��>�ծ�uK����uK�]XZ��n�W�W�W��j�\�j�c�j˒��+�-j�xS��F�f�#e�
7B��ϳ���Zq�D|��Sƈ�0�B�a�9�f0����<(��M�@x�[Q}���%`�n�hY���"h���Ƙ�_qi½pX�h#���9�_����w�-�q�XSu��6M�t�*�H�)Y)�n/�h�
�5=�� �]r'j��ƃG73n �j�`������:o��v%��VtJ��@���N���;4��S�;��+�p��g�;}Ӣt_�<鳳z��ڥj�Oo���<WZ�s��z�S����~�d|���<[�]D��+��?�*�Bo)��C��;(>�d����tS�K�y�q���`p�W@��S�
$���j�hT�3�����P7P7Gb��K�_�+5�+��w&vO��
ſu�˛�Ԅ�o?ey�9Mu�+�����_�D�׈F�W�q�_���Wd�k@�;,#�^�F�ȉ $.
�V7�z�ar;��))MK�C���9�.��*��q
+5�_�f���%vL��1Fy=_i�jC�r�B�y�+���S1�97�W�>�W��x�,b|�����]H_��+�g�S�'�#bz%L`U`���;��[���+�*�:�3��Ϛ�Z���7�2�ܝ�%!�U��B�X��?e��o�5��xC����{�:4g{����u^.%O�gŁf�cy5a��~O+�~�_�<�	|֣;ް�=�W��J�!�ݚ��<%��}��;����.�h�O�@Sl`�hZ��W	����,!1AQaq� ��������0@P`p��?!�����rU�&�
�"�|�!�E\љc���v��@Jׯ堪�lA5�":֤�=�!��I���x
״K�&��bm	�0)����1T6�
�S,���)!h�&ߑ~��xf�~�,/#��"����v�b��[����=�u���\B��M8�=�$�-�ɢ.�d�N��M��S���-��b�e��L�F�|�񂍥��N殭��۹)s���XG?�]��+�O���&c�d	K�[�<��Bs�_�)�D��1��l��	΃',f���H)��T�LZ_��̒�]�`A%s����a���	n��� �!7Z�c@�*�?#�u=��|���I-P��ȭ��v+'
F)'�
Y,���0A45Y)aR%b�ī@K�96Vȉ��Nt:&�u�O���y��ϞC�XЦݘq^"��	��/�Q�v?|Qf��3F����F�]�����2��-��I��J�o�$�V�xE���/����J]ay;
CvS�{gAMU�Ĉ��52J��y��BPBm���SO�ѩ��Pȑ�e�'aa�G��1��N�iPѢ�`Т��+n����ڜh:�Z��tA%��LF-B��E��\<��l�\�I�@šމ�#��3�\(ͦ�ȿ������NV�c=�hߤ&�.ڄ���*�!��QM��K(��fVP?rCJ��J�B����Ɲ�$�{#ѓc�h%	$�A����&!;�(�"(��k�D%��������r�F5���"�IwФÑI�(�S��7-�+��.l^�jC��v��^�g�#�Pզ!�~�K��=�������&��aN��!0v�঄�K�Oo!OE�Z�ւIVJ#r�cPNTO����i����$�n5nP�q��	�1�BJ��)�a���o�GxI�r*�r��N����#�����"�:^OB*Ќ�����/�ȕ�2d$ݟ���a��Sa�#P״
��+"n��(�#���^gZ�i�:p7��,��(BU5wK�퉙[�y�x2j�U�҉ɜ1�!�~�ћ4�%w!m�hO��l8@�8W��ɤ�!7iy,�8�~���gQ�-�..�U��ى�L>㯥�P�-I0����,�УN�$��)�
ԒB~(�C+�-BJ9)G�M��5JS�4YK��%uF*%qZܪ�z�/C~lI-�����
'R',�j24BMsZ�4�Dji�)����z��2��;ԃM^b��JC��K�|	�<�ޫagT=H{G�TA���/�'��	;�M	*���E(��CDin%c�:���Sž曏x2��^ƈxHp���H}ƜD*��›�")Řc\�1�b�R-	I�ނ�0I��"S��O���8��8"{5E>JSŏ�:�Jkq"fY( u�2���k��@�ɤb~�0�m{My߿�k�����H*q�`b��:١x��ȋ�o%�z%��v�*F�IsP7q�
K��۟��pij$*s��rܒ�!?���R$�K���anGp�!��[����2)T,�Z4dA6����IL�$B�h���0Sv3-�&hH������Z�p�!E2BL)WD$l�薂��rD�Om��`��39�O�8�W����r�u��'|D�D��d�y,�j�&[_cJ$RH)�ܥ�fx��1j������������!��%"nL\�L�=��Tp)�ۑe�a*��=(ĥ�PƦ�k�%�Y���H�-
6��bv�Zdi�hg�D���D��Zp"-P��T�h#)=;��S�rA�;�u�-��/.�dC�扱c/g�m[/�����UC��y�vmw��܍��7*J`��00�� d��n0%)#��K�I
,l�m�d�ҙ5U��`�A
�
V����N���]���.�t��E�
�I�&�F�J������m���	����RY��[��"��$�!ВY‚i�.I+9���\�j'�1�H�n��VA;td�N��xhx�,���#F��t֓D���!�ɩ����A>겈�]:EFH"�-��j����M�#��>�#HP���Y�57#��90�yԳ%��}�Z��1bs�Y�ę���$�࠭ɶ
V_�gƃ\�Mf���cL�J��ʈN��B�h:c(��KMC�f�t�%3�@ғ²f*�$d)�����Z/�0-�
���-���Vz`z�v[���<܄&�T��h��s꬚I���dWҰ"`Y0�F@�+k��R|w�&����'�"]1�ʽ�f�¼?84z��E���H��_�SN�mFE��pjRm	4>�X$B4��B5 O���Fʌ16�f��+[�a��9&D�X�:,�}�<V,^��=H]���8����6�ADS�5��-	��O<&����)��r�-��9�҇���E�?i�oa�<��Ɏ�1���"E9�����¶��2���n��d�D�����De��A��i�6��f�i(c&��d�b��9%�GѢ4щ�����c�H��5�S!,8*�l9��Q$��l��d�cΙ���l�I��D�,�A���TL�C%�Us����T�䥺0��2�����OK�K_�~��_�mB��CT5}&k�]4{�L��ה,���F�Uf`���WG�����D�MM;��h����
]'�JۦT@�2�;27���k�H�4=�HKC|O�D��������"���N2S��p@�a�h���A]���U����깿kI4�~�BO�N~@nY��D���=�=�t\�9a�/AERy��}�3��
x2>��F�����)�2U����3I�Ў�P5���F��&<
��X��)��In,Y��gGBݽO�de�ꔬ��X��d�P�ﶤ�a/ȝ�8Ѝ�F�W�J�8f�6��Y�����ݽ�źKf�}>QHT�T�'K(yk�b���w	R���ܶ��$��$r����Ga�B��И�BE]J��Ӱ��%�"PdH��vg�qSGq�b��Լđ�̅pM�4�"iT{��zn+\M<DI�WrNn�$�[��)t�p�~I��y_���(�Q<B���"�b�5�H.DAah5�m�.���P,v5��˜�3��4���
G&i$�+��%4=D©kZ�i2�)cz�)�g�rՊ�MΨR�I/�
��s�_��aK.�K���\
�}���a�Jl��[��3��܉
.�>�D���,�S^�R�'Q���?pG(=qw-Z��b؛��>�5B-!�B��B�W����r%�L�� 7~���Fm�*_bF�9����T��V�}�5�hA&I-�Y��H1�,�1q�q�)H7h^�F��(�3�6����Z��%����+��V�T��.�瑩F�$�9tނT���KXw��h��!�5����M=����J+~��XByI�aS��+X"ո�+�r���3�~��%*�4�C(`u��-��4�k�r1��t�ȱ�=80�Ư���KS�R�	��X9?H$�c�`�e�Q.��p�t��ԁc��E�����Ty-�QW��(y��b>�?8_�(�#w�>l�}�V�?����$I�IEK�	B�50��H�AGo‘�x�_2S�O��
��?�;���t-�D-0T�\��䟝8����O�U�-d��A�|�(ˡ�����')8��bV葞ĉ�Y�A3�8��8�V��%�N	���i��+8
�g߳�z��GK�eDQQ�B��Z�F���W��h��&ƙ�ZC?���b~Ճy+�Z/b�{&c�c"p� 	��K�'+O\�C;�}^4,A�!�{�M�\v�"�=":GL
���B;�!�a�7��;!���ے�.��nL�z	RD��;"U�/IY1��ڢ�2,�pF�nQ�|�k�M4aI<s����^�tF�\&`E/�\Ʊ��; 4�<t�m�{H�BID.���8i�M>C��`ݷ�ٯ��nv��4�Ww$��H����9-�!<�(fƥ�*�X�
*��X2�&6QO��K�/�9[�ms�L����m^�%<�1v�Īi�?��m��FL�+�A5.o��5��bf�/_jN'��U�@�w���\��u��$�кO$�H�&Ք��p4 a�E
H�>dY�]���8�ѿ��]Fsyr'�Q���X��N(aXВE�f�ġp+��]5�!)V�e�L�b����E��N��ݧ��C��t�o_�8�n66�%4w�bx�f_/���d+~Egn�Ѓ<�x�w�%h �r�#a-=��i�ɧJd��`i$$D�m<_�d��*�_���j�D��2l��dYk0ZZ1���[�[�Vԉ��,"��݊�vO�N�n�id�!�;�8h�������UjP�S��(�H۔+��P�b{؃�M&_�#���l���(I��4AΓ�d�^D�VL��L#r�
zG��y$�:ʤ�X�ğ��S��W(��Y��,�B��׹�l����LN�E�`_r��4<���IK2|�%SH���8-�6
x�9�s3F��@�b��w���q���]��js��єd�d�B����R���#�����o�5��ϰ�ʩb��U�F[��&��zn���k
�m%۠]jE�i&}�>�֫YmYh��
]�ȷ�����:P��n<��̦�"ť�`|��=~��i7�O�ex|@��}̪�|�O���d�ljǼ��L�9M� ��xp���7ئ��%��,
&�)>MD�L�Z;&�Y��m���p��Dҿc('℡��L@�Ғ`�"��K8��|�ܲ|ͮ��,�2��|F�D�=Q���eV��A���@�Ƚ��������~�4)��� �J%6����U����2�������.���q���V|ɨ���l�t�2|�_��{�~E��!�8�K�>y?L�,5
!��v�K��+����x���#�x���G�F[o�`_}֦0
��5��cq�#UL
�
{?P���Ǫ&0��(}Е��:	�q-�-����%)�?��d���sa�Lб�p���a�?CH�l��!��F�
��)|�k�wL��xz����h�Mz�3���$���HA��	�km�S���6��̎^?1�8}�
���D�������(�>:ƫ��`����#ܣlbC�p�V�tǗ��JӖ�yb��ɠ�L��,}�&L��e�lh.ؗ���D_	3��Y�&��C�a�:�[�w�Y��;�j�%�-�Nu-�0��~y?b�.�����xT՛��-����#�]m)��"wh�rB�!�}�Wb]��
�>P"P��tjc;�>����8�'^FқI!-����!�QzЮ������>�hs��3�#w��̚I�����c7�ڶ`,��5��Ҧ�g��@}S�j��*K��<c�����YD��$��5H��C�?˾}��y�E�m�o�o���`�d��I&d�0�a�q�oH��JC�6���-ܱ�Y�l�5&��$��3n�R��_��D�y�L���d}d��({�r�OK��Q�g!�w�Xc�$�VH�ĉV�C?��=�C�{r/�XU�5oB)χؼ���{n^��_c�+Yy#�z)&�H�)Mr.�����4x*�Yh����
(��:c�s��5 �ɳ���f{��Ƌe�"�$$�����e
��"���F��ڙ���g/�{"F���d���ev���~���b�]vԓV�G���5.|c�_{�Աl-��Ɛ6��G0��ŗ"�p-�H�$ى�ȑ��#8���d�(����JX�Įے�8�O�]�lVF#Р�%�2xm%��Ѧ\V�E�}��$�貛��{��ﰈ�p�d�bFƨa,Xo�64/Ϩ�^�A���b�`IB�>Ʀ?-�X%�fS��W��Q�հ����ɐn�2T�����W�I$�� �L�v�);2��j1��)��`y���}�#��2;��>��+���	��7G�|	�=��@S��3���n�l���1Y7,�o�BÔ-��}�&�
ω�7�))�Qk�l(A4�Yg�=I-�j�I�H^���(�r�s�o�1�ct�_��/�I�����b$!5u�׃��#ib}��c��It1�Z�ª~Cn�����nВBT�]#"
4�h�F7�c����G0��b�����=FZ�X����!�4K�;Y��'�55өxi.=��`t'b�kyҧse�����s?�|u�2S�t��X�
dT;J�l�#hT���D�y�'5���E/�ޢ���j:���q�*iB[.��BI(9P�BD�rs�E�R8��N�DPP�"U�'�k���#�O0���q�8�^h�'bg����C�t��o�t�j?ϻ) ��Z��A������ ��]�Ζd8z�
����j���6
v�!�����ӏ(�-K82BF���&G���[cu~A-��5:9MQ;c�K4��!�Jm3���/Aq���r����VA�����t���h{+-+�R�AUx�H�8$E��"��=�HǦ�F���W�.���`bY{o�{���F�جӭ����Ԣ+�a۷j|q���Q���l1�I+F�����Q_�M&+j����d�Ȣ3�]��2��t'�5������n�N��mr�gAn�>!�0v@ޥ7a�7\m�Dj6=�Z�61jl�<�.��|u]�ɂ!�Ce3"n�����:`њE�Ki��#ЋAC�-�Ƅ����!�����g�1�ǁ�h��Z��C��ӂn�M��,-ѱ.��'6�����'bz	��#��a=4'19S�L_oe�&��9]�mr��
)SL7bd"���|�}���JO뒹��6�;���2U05�>�hS)�Dڇy=��$�I$�����f*����"E�ѧHt@J0��3���+�?��z��I>Z��-���$�hfu��"(�3Y�_�����K��hS��AҫwEr2UrD�%�b6����2>��KH|�j��z}���
K����)�ll�ӏ��*��']��j���cC�>|�5Y��?h�Bk�v�R��\	3�P�� ��~�'�.�Ћ͇?��|�nk��@�
:W��iT]��y��	�PR����@�O��y��2�e���W�R�Q�y���a>n*w�0����7!��!�z�&M5{�;��&�6Ĝ�1i‡#c��~�&�{���%��>Q�PV�Ѿ ݘ�
P2R�?q��5��1�D{�շOB̵B��|��BB��v�֧��DA�&#q���čв���bt����n�.@�aR運- ��o��m����RB/�[�D����w&�IM<�s���T��K�K�"x�Ys�)P�}D�5>�����t(�&5��"�E�'S�eܓ3�hkO��X�
aeZ�CcD3Yk.?��#�A�M��J�����I�9!:�!L��T�J1�PK��%!K��"T&����Xj0VTy8vfR�GJ��m��鸫�%/��"�M�D��Gb�<��ԩ[��|IX�+�Iz0�з��
v��V��sUh�X|��a�P�L}�'*W�8RNH��,�4Jhc�Q|�_��&|(�hM��+*� �hR�,/:��o��C���(4	�А�H�-��XpB։
�
T�5�o宂
�?��vI����9�_��������~MmetD�Ap�"ߑ����){*�r\e��֠Z��2���~
۩����-��hT�Cթ�8��n����z��gҪ.�K�,C)��2��1��l�K�h�R[�:�\�b�_��)��I�ۡgAz�6�Q.����������>g��+C��o�:3��*S�<���:��D���ב>؛���N�Q#�h(G�
��7u��
� ���5к�|��Lݹ��Nn�r�󏠄p%�'fdY4��|��&q2��3I-��*�|��R���,�F)�k��1Q���d��d��`�f)���!�OVBz�
g��PQ�RD[m�5�}����F�=*:�鷂�lz��������w��Kԩ3\=�r؀]��2�djdjш-]�,n�BBX0'�@�!:�u�"��h�7�ӕ����׸�܈�z��'l&~�)��!z��'"[Y�b�Kb蔆�%���l�dS��>B8(-�c�m_p�1�>�"��E�v��ޓ��IU�ÖO�U���A1tHůI��a�$�[|~7����)��n fiF��^��iS�C_l���qz�!I�S�Uܒp"���5�++�t��^��&R��;�Cd�fԇ�� k���n�D�4]#:,?_�:I��7zW�TV��6�|�3�R�%D�ٗ7H�va�f�m�mG��B�gpV�2v��]���҇���:r�oi�:ѹ��/���*�γw-�B%
7IVț��v�L!�{}ި��xBE4���84���"�$ؖP�fb��G�D��E����{�c��j�Jr�p��]�ُ��$�Fb�y��S��z?*w5]M\V!2��c�&�p�J�S:��2�Ի_���k"0iᐏ�H���0C��b��̧F�	v�>h4ku���cb�4��}ĩȽ7��e�j�0��9�8urE]��9���!��+d�R@I�~���	<KBq_�6Di�a��
	b��h�P�tn(�iI�!29��9��]R\���I�^��i�!��82������kr�;]ɗNN��bwE;�CY��͇!���S�����ݏ��p'�'�Ȥ���ImKw�P[�g�]nm~+rߴ��> Bܔ�@s(�g�sR<

�_D��� �8C�fq�e�
̏)��F���Jn����u�j*.7�5b�a���E�
#/�kV,�,̸�a�V49�o"ҭ��s��?�C�JT�ܔ�@�!I�����:�ߡ�%K�:c�1�5B�"�6)K���P��Hә��Gy0�0�D��I�Z��׎�&Mu�C���4�?s�Zq҂tO���5�WJicRx,7-������YЩ�H���m�"�NN�S]��-$v��C���y$v�#�F)���J$�z��e�j%Xw�?d*��ϙdm���5��f%Yg���N�s�u���P�Ȑ�z�)2�K���G�*�h#�d�ho�C�?<��o�a}�,�� �Q+��h��6�:)���-�e�eI�fP�ɓx5��t�/�k�~�ބ9�_{(�1��P����Z͊���S���D��[�	K*��&�,r$A�����k�ȢtϿ�}�p���GG=�W�>��ىQ��Q�V��$�b�Pڶ�w$�$��^�|�Q%�h�E�Ƽv����D
�S&�șm�s�0\�ы��&��I��^��IA\��BN$-���=��1�]�/N��U�P��/�+��̡���^�D�NDM���I54�^w����M2��g�Ai��j�v���;�Gb���STY���	ᕁ��gM�{p���}���1������Ӆ��Tx�����Û�o���#
4���{&�}�?��e����䅁	�H�E��R㢢uF"q�؞R<�7Z�C�c���F��*��>����~�-���RsR�	����J��㨌2�$���Bl�<��#�t2�����3�.��Yr�[8�>V]"2�
P���@��r���_�L[l��'��:Gp6$zjJ��1�����{�{1�S�'Ba������zm{)O�!>���K�N��$�:0�p�-u�isx�����D2�釩h��w�!=F��U�dc7=�� �.a �!z�����F��ܑ��rqԎf�ȅP:�i|=Pm��,��n��2B�l���$���Cq��(�T���[�b�a%z��jq��n���9EM�o�1��19]]D���,�`���F�I�n��6������1��w�N[���-�t�dl�����=Ƭnt�*"�(���;�Q�ȑx�[!z�o�Z&a����[D���K�_�
i��ı^�Է�����
I�� ��=$�L�!$M�$.��	�X�bN�`�=t�tZo�Sa�!�!Þ�5�I�9��b�J�y�P�=�bZ����at�@ө|��$J$j,H���ȱ�]�j���e��<f
���ѣ7�_�j�t�?�b$�U�e��;�DLy_Q��ŭO9��=	R�,��b7
M3-�
Bb}
5����I����0ρ�Ee�^ᗃrŎ@�;��7ե�-ݱm���"�HCM�E�f�@��<��o�X
{o�l[�A��Z�q���B$�L��̇%.�>�g����^��=��e��>K���+�����C�D&,	�FI#/Vx.�H�=
)cd�]�fb�؆�V�D�n@�,MNX�O�BJRh2Gp]ɂt4-:wE�
�<A��&���Ԑ��xA|���	͊��Qa��G&����g��K4�,��@�J��Q�4�і�a�por2����h͍,�5-�A�+�OT�ȡ�Q��67�Zt#V��IvE'd4l��7#��(�*([W�.��������F��	6(!�G�S��<z�7b�)�FvO.0�C3�(��؉5r�oB����5z(G̘�RJ�ðbH&zR�^��{�d+��$Y��P���/��o�ϴC1�_��i�҄��1�$�o��)g3؞���iǣ��	���vFR,.Š�>�`��e[0Z,v%���P�֎��N��O
�_�
a��d��bszNL��L85�dd)Z�[�*���S@lɴ�[�i�B+�p:hcV�5��5���4�dm$>�}��lf9.
&�t�z�ݦ�d���n�ip�%���А�Щ�]�n�f��o��Y��w�����#�0�-�S��Y���q�5l�R�i��[�dn]Yb"�]|�����AD�i��ݣ�GF��4%��yخa�QE�^��w��!�N�O(r�쉙M�hd�Z]�2�!���q�bv����m��P�c�|7�� &�0�zh,�
�g�M.W��By��'�P\�,$1�2FHgr6T��{$|�����)4�:(xS�y��Ԃ%
gs$���K�n��[����U�$�
K��/e�v�#�6I<gqb]���y2��R���v��@��C�2ʊ��� �v��T&CD�{V�f�����cT�P�Eѻ�_�BI5(��1?�C�䣥�7M��䯱������tQ�P�Ѽ�)�)�C�I��!�L��O�b	��T��dhAf&ȧ]ϩ.�S/a�����B��
,l4�X��l#m���e,|b,�)	yy*/�5�����Cp�A�Wy�?p�A�f���(��ܷT��Q�t0(G����˥�+�D�I��4LdBY5�*6��b��x4%C5�Ú�HW���Rw�_�
`D�i�B����}`���C��H�L��c��v�%ȒWQ�9�R�
iH��E	�%���V�	�Vh!�h[���=1�M���dH��DF���O� �R�ܧ�����'�Q�r��Z�wx^v;�B]E��N�(� AE�QRI�
���_�D<�ǁY�l�w�8�&��v�X��~>��7ȵf�X�Y�dv����+ȚG~�n`TܺI)����A7d�RA��!y(F��
j�T�k{E~�IS�z�KB��hO�f�Ɋ*O�~��"�2��B��_�MX۱G�4J��Y���|2�l7xytF�J��f!�௢s�Zϼ*8��HBm�H�at�tW�$�ٕ$�:p�r,m�{�*lnz4^�z����K�Z�f������Hu�Н�wE�41��)��6D�ț��&�@��#_!��׷L�вdj�<��aq݄��p��B�Ä����1�$�Jƈ�iЈ�{	)�ų
�I�H4��X�� �B0�\
���C���#�9b7\���b���c����l�؋w�z�9��J�Udf���Fm̲�" �Y4�8��ܚV�RG9Sh���NH�ɰ����V���<��oQ3
3�����^I;�0@r4�ô`�I3.�Q"�r7hxt$�dP��>�a�Gqkrkc�;E!L^x4S�L
|��4F	&�5ֻ������/Q*K{'��DUGǩ%�
�F�tH�����\ح�L�m} x�Xn��M. T�L�e�΄�pic�K��&�'8Ά�$bMX����iX�I��M�hY]!<�c�J�CZ	�
X��[�e��2�t��I��%q5:�����x���8&��ֆU�ZZdlh���}�(��_d��5���%|��4ڈ��-t|�D�ԲU�iBS��rM�*�#B�h�Cؔ􁣑d�dFxosto84�d�w)��'PՇF��9�=,���I�G��;OD�	����OQ�3,�D鞘\q���:��o��4D(����_x��^›J3b
0L(c½����z�e�P��Y���-��t1�Q�e8�]س��>���X��&��{	BǿD�j	&5M�[S/�Ӣ�k�k��G��ƻ�E��ܽ�F[ؙ|�A����we�K��8I�lm��TFQ8O1�mQɝFQUi��BM��$m`�X=�3��x)(�%�i�q�4�z��jh�Fn)�D!�|���x0�Td�(�kQ�:�2M>
�TWH3CBQ��oP>��=4Ƀ�%�g��lk�A��Z���H�b��$��ǟ�����
�Pɧ�R2Z�fR5D���jxz
�yr��:2�[�IL5N;�L��af�|4�'#���贞최e�|ŝũ�>=q)�k�SP�� �k�<���/���\^b�0�*+SBR�}2fn}
�_����;
�>D��?�7y2�<Q��u(��GH��
��<%��y45���e���o�T8�jhh��"Z�m���܉m�6��܄PmC�B,C�h�%f�r�%m0���3�9j�4a�$���~Af%
%
Eе�a5%���|hG�\��o�"�i(O��ԡ�D�N���"��7��Z`��&��D�͖"an+�8�%�:��;�;�nUU�A�ʼn��Ƣth�$Ha!�.95��`܈tFl�>��<�/�,��*F�_�Tl5\��O�P7���	
�'��rP����B���B�.��_�D�kai�1'-&(Y���8f�=�j6������u���Y3͈��*r��JT}��s��iQl��g��!ܚ�A6�pЄ���(i<{
�D9L�6jn�T<H��4�d�v�:��7��ڡ��]�ʋ5�'������8*3%NX�bc��a4L�v;��.��=2��{���Y��lX�n���L��D�|�R�*�&���N
Z�`�˜�Z�w�.�S$���Zt"[�>��M'o�<��Ŵm��ȕ�n5Ia#�j��̃.N;�cM�QSdi&��tଃR��&e�6,2�rru���F%J�"��4f��i�Л�RDL8d���AZ�D��S�v���xG��#�2�xF�Nl��0�X�Ь�G�d�WR+"tf�t=�YgvFi�$&��#E����=7�F�Kv�˹��X!�
M�Cʤݱ�m�Iz�)_�݄�W�Hď'�
��t�����F��LT�Bo5E����DN��F�"�%�A��.�*oO���[h��V劲6��:�K	�G"\�w#��{_��-72cp�r	o�3X�"/T�\r�<
\םG���T�U��-=�#X]�����{4S�i�k��Im����|���x�[��W�߁�׍�ȈR�bv?��B�	ef?c$�g�5HZ�.���Z�b���[��[��O�o�&�}��U-l&{
��t6LӁ����_R�*9Rq�%�	
92��[(��`NI4�����
D�ͫJKB$�t52JՎ�.�E�Uf9���_D���YЄ��WD�*�a�f��+���}�$/�5E��;@-AF�N��A���'���f~p*��=�EN���
i��M?�8�s��<q}��_I8����W�ǿ�-�ofa�F��D��O��E
�wjX��Jp�����;S�F.YM���DIIua!�°Nȉ�<��Uo�Q��J|��cNm�<����%������3�2�Y�z#D���M��o�q��e
�`�bs�٢�H���L|��ؕe���|y(X�/�[}j������Mr�2���l��MYj?y̝�H~������O
�>�a}$;����>̲�`���I�?�����%���I$�I$�I$�I$�I$�I$�I$�I$�I$�$�I$�I$�I$�I$�I$�I$�I$�I'�R.�v3rI$�I$�I$�I$�I$�I��]����޼�	$�I$�I$�I$��\��F�[��9�43t�I$�I%X�㌜�g����l�u��<��NI$מ}K4���¯B]�i3�h!c�b���|Y$��!*��U 9�3Y�3u���_���8���:�I%�"�����\Zl銈1��5�FL=�I$��ؙ�c��utH�AM$�x���X���L׉Lw/{v��"��9xh����
C�I%W��{�?���MY�0!�����Y$��d���Mx��� ������;c�IŒIw$'���)��e�ꢄ`�����n�I$p��I�$��iP|��e� �nh��1�����4�ߖ��o��g;Z���V�{���f��I&v���/�S��z���9���N=@:2Ӊ$�N������|cb�/���8�~"�iB��JLa�
��@CIV��$�~�?ûmD�w��I"l�>�6> ���i4�M'��V=n��\+
~�$�.b��� �Y�|8�I,�L�#��r�2C����H�i�7hzF�(��M$�I&"i{��2є�I$:�CU���}3h�Id�����j��q��$�J��--��rmˤ��%�P����DY�V1�I,o����y [���͒�4CA����3¹$�#��1�J�_NVI������@��(O�}�I�<�dl��`���2JT�0�4��D'�uI$!4�Ww���O6��j�E��j�bX1�$�*��w��@hZ�B���@��&�8aw���I٦5�^H �M��^�Z�-B�Ǎ�Bu��$����5?$���c�)d	�Xhq/����i<�JB
̆̒JDQ<�~�I0̂�*�;9�Y>6I&�)*�f�@4
)���aR���A��$����v֦%�&��x��+0B�O�x3|�a�H��IQ�B
 	I�q��bcޏ�y\I$��$d�q���h(
@9�l��`��	.N��K�=c��$s�&H`���?����C�1�;IƢ@wiZ0W�{����=�� ػԱ(v�$�
6�8�>`@=�4�X����ϵ�l�j���H�|��`=s=��K'�����Ij4v�x8�I'w���>����]K�g�k��d��G��$�P��?7?-�ќ!���es�˱���Ӥ�H��<?Ps���x���iU�ꭶ�<�%�n�$�W�6<�N�$�`�g�8j��F3��ԒK
	��{g�%����@��*�Z��i$p�)VI#׉-$�N�$�O�dgY���%F���e$��P�A^,�Ez���f�o��OsՖn���I@[Љ=��Y/�����P(
4e�p
1��0
PI$���˼�_./�:/�Ҵ� ��ZH8'$�+��+�$FNh��5���-�L'�PM�wrI��Gz#�N��G"�6d�J���K8I$��׈F$�/�
����l��8n�6r�H���(�@��۞����vT���~�@�4�I"{���aA՘��Y��EA!n{/J���+$�H����S��|Շ�|��A���V��I�I�4D���ܓ��t�֟�1�$;�HM�I$�r�N�?^A�@��Ӱ�Ty��~��n��L
S)�.�ـ9�v�m�Z�œE�z�{I%�����Ut�%7-�b���)�:���	D��$��%z
ث���;�������R���y�L#ke�H��jAޒh��-�J��i�0(TA�%�$w�-_Û�~������gEwr<�B��$��[R�o����~����:��=B1T���I����n=K��R-Ӭ!�p������	$���ү筭�l�LL���k�|ţI��A�l�M���i3h�\{R�7R-ȢM͛>�)
$I&�Y�Y-$�1Ǖ����rS�+�$$�o�$�B����x%œ�S��ތ
w�
i,E�;̒H��å��\��#�:��s����<��Jy�I$�D�y�}�'�s;"ʑA�l���$ͱt�y���tZ��׎מyfH[�ð�p�����I;].L�x��|Ev��]�[���ze��)�Y$��o�%�N
ϥ��v�^��$�(.��14�H�im�^�%��q�=_�HLp���B�I$��f�Y��{J7��d-��y����!7$�!'�0ʑ-\�A�y��(>?n�2$�/�0ԒI�ϊ:�y@Lq�ksz�۶{�1�DTA�yI$�<�r�Q؃�`ø-��i}ctAT�����O���	ك�n�4�׿鱌'��1G���'�I'v��'�3��"[r��p�uK�{�8�$�I$�I$�Z�pK���@���y�I$�I$�I$�I$�I[��Ѻ|E�I$�I$�I$�I$�I$�I$�I$�.t�ĒI$�I$�I$�I$�I$�I$�I$�I$�I$�I$�I$�I$�I$�I$�I$�I$�I$�I$���,!1 AQaq�����0����@P`p��?��cd`����]�o��0�̏�=��XJw�%k(��>�W��/-?|B�����0b�?춥nd�>�nh� ;����N�L�N���ޝ��)�Hi�r���!a��Q(����mH���P���:�ۼhvb���5�C����i�-�	�K��Z�g��*w���DyJݠtL�.^Z1z\.Z*̼BR?���r�I���o�IJ/f^y��EB�[.򪶃$�W���Y���L�In��;z}]��S���|�)�PF^�._˃En?� �mE��C@$KfQ��0:bFi�D6
�{>w� �N2NB���QϜ�u��h �Sȅ�9"r�Ѝ`�R�ʎv���\��0K�/�`�LQ���x軉d���Y|LVl.�
���E��8K�,D�^:�7�ۛ�0���h*)䄧��o
ê ���cu���1�:E�`�&���������{M��\W��q���H����n�9)jnF�b����S#nR�`���Q(M�����V��{�H5����* ��s���gl[��1,XVf-���Y̺w�j��b�]=���4�d�Q��\��qoK�߁�%ޯ�E��n^F$�d�C�"o���(��~bIK��0R�7(��?ߎ7`���7���T�NQؕLƎ#��
�	J"9��|�!�F�/47�D2�2Ɲ�؋J����#��K^xD#2oCz�O)�M���6���n� �N6�t�J_(6M��7�
�ɍ
��KZJ�1��R"[p���$Y2jU
�J�d���ʹ��~��Q,���/"6��?�J%���mY�%$��JI����}������(�yq%Ƣ�s�9F͡n�#Jk�U,�)�D���W�Sf	inU��sqH��ܨ�����a�*V82-�i��l8�嘑\�0���ոA]Й��B� `xF����ۏ��8Ws@��9C`A�%�
+(�qr��DV�0��/[`���H������=
�Xq-�q+z46���[����	z��Q�e��-����:.DF��0¥�0�B�E��B�	���J�0.U.6�����N���hƿ�t4�/�Ӗ����
n�A�����d;3	ur�N���e�F%����(�^��mS� ��T�d�Τ��%<>&�bf�Z�Yv�s|�;�%�x�?���y�{�����V�OzϮ��e���fgS>�p_/S��a.;�o5ʄi��G���iz���jn��v ��T�Ëk(�Df=^���%K��s��%kC*�H*�3f%5pw�����G��ӟ�+��g��uI����0z:_�U�	Q��
`&�(n��	p�Rf���.e��[��*/����+w)ٗ�8!�D��3������<���E��/9�=���p���JQ�o7��΂`�:,Q���3�f������^13��R�q�hd�P8��p�L�(���qQ.� �JPX7�em��|Y"IJ��d�M
��A���΢>2-行L�����\�
�4N?�0�l;�<a�c˟��q�.�'i�
��#,�Kv��o�l�];\Bk���]�P�ǘ�H�Щ�.�ڭ�4L#R����?3��]�CH�T��e��9�����t|��
�%h��1�nj+�!t��< r��hDS��;Gݎ'�F��J�e��Q�j1�+D-�0���=nݿ�����2�)��ay}�.M�����}��r���(�a�[�I� �q	���3�(a�	~Ҋ0�<����]�
�#�E�c�:^�^e�N���g����v�VB�c�w�,�=���Y����z�G����B�:�+rZ�1�ڜ��l/F��R�
�}�B�˜y^�|��-��"#L�L�3Wˁ$�fZZ[O�VVRRRZ� <����K��E�0��2�����6�1�\<��-�4U���F
"-r��0VI˼��Љ�_�-�lH��&.l�]�=)!�|�+��F
�[D�?�#x���Ĥ�0�.S�w�.+�!	(���_�|m��H�{?����-�#��q��m.σn
̾��_��0�$A�򟛗��}��ք��V�ynq��^z⻖ci9�dm|�`"���IRL���x��ѥ�J���ž7��n�z�E�_^��؂� �4�X��Jg����1����ku�n���[�
2�iy}z�JJ�!c;e)p�'Hq� M�%5�+�2W��=��'�6lD�
��2�p�(r�c��=j#R�T�^�����C�8���fbb#�'0�v_ˏ2�m*�L S��:8��zc��3"D��%�0�,V�$�h�km}�+ɏ�M	\w–D$b�-��R,5���'X�l�˜KeS��B���'?764I�7�2��6}"D��V":���Q2/Yk�������ǣ6���2��<d��lDm"XF� �	vR*�P�v�Q�W��K%}��������vF!�a���D����4��H���g�_R0{��f�%�H�h�]g��MI�K�-���0�z�\{y�b�e���Z�>��NK����ϴ�/������@/�޻���.�E�J12��n���S~���aS�"
��C��x�W�);@����A� ��es�R���$�(o��;�H/)N�@Į��^�����=mwW�QRGZ(:�)_7q-IڎVK�v�����`�|G�a���8����x�O����{��O�y��o�M����t|�z1)��_F|%���cG]���E��S(2L[��,X�.�3��Ea+�[[��W�a��~p�gt��Ig:�?�1p�w�z���9��_��tCЗ>�%Abݦ��?��D�k_Q�j�V�[����{�i{�rv��w��C��w9Dt-�w���xg�9�����%zC�>8�ξ���hRL;'G'��U6#��鿻)�|x��qO���Y��Ȼ1���}�^kK8&)���mQ�YL�9�S�0���uf`7[���j#@�U}k���F؋��/���1EX�e���yq[.V���Z9<ǩ��o��яܠ�Qu߿�Y�cG�_� �+E9]yL��M���v����]�;~ey���
m��2N͇Ը�.�w W����ű%�\�6*8���K��-k���Yh��?����c:�"�F{�~�0ͅ��C��`�����>O�$�δu��Q^�[^�*�yU�&kΫFj�
��BP�.\�r�˗.\(;"�/���U�_�/��nn����\b�,��B�ּ~~�>t�h(���Z��1����!f��AA�E��Z�n���lxړ(�tK���6����ן,U�
�e<�tz�>�g\�/�r�˗��\p�5�rieX���{yqhfŽ�le�����f��"�"P�M��v`�Z���M��y1�T�G��,5����!P+|짭��+8�^�ny�G���
.1��ڰA��er��p8*W�����|קc���
�Hi�ɣ�&4}Zl�� ��LX)v�x�GJ"�¥�x4���o�?#��MC�<ؓD#�M�.�z�D�(f��u��u:�FxQDS%��K�+��//-:u��w�����7��}`���43?�D-��I�o0�p{�tt��x��%1��`C�!|��T��i<G���f�L�t�r(ѱZ��Gh���SR���%A�w���H����iyyiko��h���
`���N��c��{]�O��]C��nTʬEz_�t��3���۹�4���^}�Z�us�g֮��E� @��-˗��
�<�&�����
q6����Kg��诖��~�%-�e�%�X`����T�h�`z��i�cp�Ƿ/H3- 1ߗ�p��0D�Apl����7���R�ȅYgch"�`�Z�AMOv��^qŸ�F)�ǢM�P���.�D�i���)w5^,���5�4&����4��	v�Ka�"!p��0E�6�jS+`t
1� 11ۦ����t-���R�v�VO4�﫤Qw~e)�|�|���2��Rijhp�a�o�o�ue����/@�lk��A���!T����cJ�Ƌ�'d�)�����n�'��34���@"����i�1�J��t��f�Mm�	C���NԎ�cM\���f�����E��@�C����x�ӄ�N>D�	��|Eu,\�K��g�۵K3_>&ϜF/l6��v!A�=m}tw�Ĩ˧�IG�>]<vb��pU�Ke��+Pv�)c@m�����
DI�19FɿhG�}�%�P��BkR�q�)��F��+�~z�zN���9�G��ߌȏ��	��{�ZRS9��<\_�ߖ��_�Y)�9x��]8	k�2�y�
F�|�|��ߔl���-��oڽ�dwN�~�Z�]�%<�톚����N��a�RĩR�Jƕ�<���8q�"Xv��
������ǯ���Go���� Ï#=�E7ܯ�>Qc@e�W]OAǹ�/GUIV�M�M�z%
�«�+�)�$	���	,��ͣ���̱*ŝ�_pzB��z�����]��۞�؊�f<N�<�ٳ�0�JPU���͎SX�Iz%iZ:J��n�<p�4v�c���U�Wuǥ>�^���i���&��j��_r�=���i
���,���;�El�M焺�梨udM��_��g�MD�Hc�n�%[�F����\��R�J�J�|br��D!����t����*V�t/�i��{�%i2yAm�gG�Ӵ5[b�5&Zg���p"
����]�<�a��xb�J�*T�R�J�%C�y��X���C���Y�l@�9��?2�tO~��H�q��>�yjBٹ�ݎ���9=��-�d�?a�;��q,1Γ�Z	I���z�����b��T��6�u��7��?��^�v��O�/A�܅k�bM�pa�GU�8*�T_��2
�?�Α��s��_	���ގ����K��~�d���S�yN����/Cgۣ�-�(&r!���z����t�L 3f/d�s0���U��?1[�OG9R�u��&�~QyK�؞���J�OP��/�������FS--//����7=�R�
�G{��|*{�*�^�q�+
o
NV^D�V&�V�<���#�n����e�kM�Y�GT���Oӎ�c�'�>�J��]m�eˋ������c�m7q<��������T܆��ry ��.��:���h&���{�z��N�IPC9���ȏe�4M����K]�d�v��A�n$m�"�˗��5�syo/����n�v� \Jԋ�:Jt��(�Mꃼ�iR��2U��x����pA1�r�Nk$)�XB�bw�{o�U.,"+%�����·�;M�R�3��|��$
x x��
᾿���*�i��̻�\X'�������\U�*�,�0z�����z|CPD�&��(�W��GWz��܁�s_�ߟ>���~�vX�K�w���+���j}��T!�0���b�1�T����&��-��t5s�e��'8�O�n���@�L�E�ļ5%��_=�mZ�Jb-�TH���/;H�pc0�N�_D>el痶ޣ*M��{���~�66D��P�	R�4��G42�K��'g78�1��u%y+� ,c��w���Ձ����xQ��|#	e�α�9��q��Jl���e�t���kF�|��$a4�6i�]�'E_���~R�G������Ta��k�G��y��oA��d�hTqs��ĩR��>��K��!x����]�nz�S��m�S�7u�[��\���
�*�r{0����ݎѫ� [�L���cϫ��q��:�A^��:3A������T0�B�X�N��A��+J�aD��/m�s�#`[�%?ז��E`y�v�E4SC����7}�
�)pA3�Z�$��np7�k9���v�����o����#w���-��Yp\�������80�,�?���#I�����Fv& l�j51�Jцz��qP�i�lv����k$ؿo�I�=�����*�b����n�<�J�-|g��x�0>��s��|˛_.����<鑤7y;����K��a=h&��zV�SC��3"#��i˱�˦zA�PG]�^��%D�bؤ��/��ϟY�m��d��@��q�xH�gv���fW��﷧�cP��W+��(�?^u�M�ޜfAЌ&~a�d��f̦PT�-��M�
�A����I[R����R�hY�Y��	�c�q�x�&�
���w�Y/r_���O�����b<"b�%�/�ʕ��_�&
���l���Ѧ��G����z?���j�A�@��?~�lw���'Un�%@�p����9�:1��P#�oR����{���w)��h���V������f����<�>��9�Ja���������$$�	�e����M��<�Br�c}��-\�IY�-�;#1��C�+S��w�eL�W�~ܭRU�,�ۃ�c��p99=�(�#����
���D�ox�v~{B��A\zDr@iP�Vb`r=�1g��@�0f$H�$�R��P��rD�l֥D������!�Ay=E�M��*]��#W�x�!�R�a*X�/Y�Ït��B���[�l��P3�"KcIL��+�e\6O�����a:)0���#��D�H��J���Ʋ	�_�\�_V�/\��I�tOHr����i�"�O~^��w/<a�Um��~	Ў��e#�m q�M�
4S��A�D7��c��o��k�E9%����`\cr�J�-��H1+@�h9hJ��^u"��|Bj�]���-�`�R��A��XE�a�����@\�_�mplҴ���,��ᆆj�����Ŗ��H�9�yQ��\�F�J���:2�1,�*/A`��t3�{�rW��P�\��2�J���Ht���w��@�{��9N|[X�0�-oZ����������g�l�����ъ�a���ɶ.٩Z�*6�B���r��+A!J�`
6�%���a6<����##��f1Ӧǡ-�y��l҅35p�u���bT��"�s��@�7����0����dm(����#2�9�S�fX:�WM>8}w�D�����H�R�J�R�iZ*"�,�Y]�CR	��RV�j��=s��18�am�PpcD��Cd+��e�텡#���h��0\[����3�h����'�=�Z��kT�0�ۓu^����ߨ�%c��Q���J�T�Z�Z��9e"Db�hv|s���~+�
�&�T��HHQ�D[H�:F�;M�������U�\.��&ћ���z�q������;l�x�uV�״��U�L^^����Q�}�����\Ǫz?s����}:7o��rJt߁�
�#Е**#�v��n�K�aGhh����៿�dڡ�[�Z�a��S$���m�s|��p�K�~��O'�w�:�՟S�O��'���_�t�y~�+���M��P&���>�n����ü�3�f��/�e��z�n���\'����'�1B�T�����W,�#�r�\h�D*$���V׷$�P����P
Ϲ�]��m�!���b��Z%35*oE+_�z����?=����ܸ�)�,(�8\�9�܂��d��&;��m@���Z���m�r������x���ܽ9�||�.^���Kִ?t��=fc��-��Pf6�.�x�Ylę"Y�(`1��%|�q[o�ݘ�
����z���,�QQc�0tAoD(�2C����^�;���-�

X�}�+��Q��P�z����UpT�����ڱ4T�x�.E� o%M���Ts�
�
1�8��r�f�%A"�
�7��?!��-�	�P�|E�I�ib�sx�4S��J�ˇl,+��� �[��\8����q�i�8�U�
�Ȁ-������e
b}����7�u%W���f�wK�(T��AN�E��+�r��q�t�M�h������="l��}�s���Fd�,�x`�^yC����bt�L�sT����v:��Þsm
�Z�ez.�Z�$���*�E]�%4
���J
t�%�9Rx��@SG8pT�G����2�xkz��o8��a'׏��E�KԈ������u4�[�φ�;����3�6
�w��/>���nP�v�"KnF�2�E��~� ���6kA��2�lG;����\�����iC/	Y����4n�|�Z%~U*�8Ae�UCq~�=�c�8�b85�㨎�K�D�Rx�(�9�x�
�=�� +�K�F �^S�+
������_��
	��?)�=�,giC���3��1�
)��7��̦��/A��YyÖ"b�[�
�"4��]n����:�����D�"ٶcU9pʟW���j0q8�Bp���8����1���8lg-���7nV��)*jR�,��Xqm���i95�%�IW��]}�����w�:�?�/������?
��СNq
�m�-.5���d
��B��(����7S����V�s(&U��_4����Jִ��S,J�
7\��ܔ�xG<�rS�wt�S�C�<<��IΘ(������/Jp��6�
�_I�}`;w�+n�B���
0_�Z�)�������m*��}#�$�GO8}G�f	���T�12��
�^��f������;��O"6؉[�����ޝ�o����f�
��.~��M+_��@%�
F�D&�y��	B����rTtr��{Ɩ��"�*��vȢ������۔�I~��;�^s���+G'߽��9��J�d5!��ًs��w9n�����,!1A Qaq�������0��P@`p��?����"&��OiL)�%�r4���C(���"�o�T�~��^���p�������bd֡�s?3$X�vl��i�y#�b\Ĺ�h�d�b_b-�pȖѦ�/wa�K��ƺ}a��A���Bf"�;A�B�'5;�������;��3�j�=��ea�T(c��g;���ss���q2�\'	XW$9D̫�\@��a�
2Jp"�M�)k����_؈P�5�X�|͝%�I�%|]?޲�G���1�xQH��%��%W��G�e���˴C���؊�o�7Ŗx� XX/rU���*T�+BrL�rUJ��p�*\��Ӻ��3�n�w���w2�ٕ�l�M�2�9 ��A[�g���G�`��^��#a�,��Ж[�!�A�ƕa7&)�e�|����Q�Vq�a�!ws��n83q��e]�|Ĺ �7�8�X�U6A�T��#�e^��M�t��
��b�?�`��'0�SU
��Kw�����Uo[�p`>�0�#l�·m���X��0m.�%+��n@�#����(���)BM�m�Z]d�Y�i�E�l��k�U���<J�(9��g
U�k�!�Őm�������r�3�����O?��6�~��\�Q��\Z�L����5�J
8�]��F���.�g2��X�"n�R�s�ЗM�b:��t�%dW;��$,��E[��`����QRϬ� ��M�^ۮ���yK�jXfU�g%�����3�YxeRȸ8�;���0Rfe�.8�(�ȅ�8�9�q+��1���p�*b2����0��ڊE�qs�UzŦ",f&1,�r�'�Q���M�xTkngq�2S��r��ʳ��詻���Ь���*n��x�)cy��$�7/FWC7���Fq�����1om�ȅE�@�0�2p�Oq��B �Lv2�SeE�yo�Y�e��xfo�\���fl�
fx��	X�I�)"����\LB�ٸNz�7��5�몋[A΃��.�sKb�f!m��d��{�63�QO?�T��ۯ5���Q�"��--P\�R�,��T�1/
a�M�ٙR�}�)�_�vz����?���2��j��Aj>!���6��{F�
�P"��Oh> B#B�J"[\� �uc�XD�� jm&T b�a�B�O(V���m��@��S{�f�Y�K cG@�R���op�d[ ��4fІ��,�-k��J�L�[��3��̳{��|�0�S;�A�dse�fvf��pj�b���x�l�^��\�1&?w��Z<M�
7�F�'9O�z����^�%0f$�54�X���ڹi���$�-���3a�$�4��:�^���d�A�StȆceM�8�;˛",�S-| "�pe�E	D��h�m�i�q�1��Л,�r��],@�^c�3)��3}��A��k11�(����~����,��L��d��Za*��P��(d���ey��@X!�T8�9����f��-K�/E��ۨ���f��\%��/M����}l�a�Ѓf��@�l�"���E����f-EJ�,Ś�� "�c��D9�r�'�Ѐ��6�A�̤����`�b�˗/A�jU9Q�N�э7�Q1+��kzy���K�p00%C\f	4Lj�z�x����L�3,AT��!�V	-3Ȃ��i�T
*}L��Hy��1����r�r'�iy"WM�ꥲU�:.�Vn9�Js/|"��bd�Ө/��R
�U�L�`V�L�/tJ�
���8Ήr�Lv�߸��n]:`��g�T�*Tn�k`�F�{'��Lb#�f/h�%Q��c��P��^er0�]-�Ӡ8�fZ������f�6��}� \�ҢF?�@	������|Fs��**(Kؔ&zU��n��3�V�K����������˜65*Svb�T���[�<KT�O&�;G�_�c�<҇�~`QPQ+J�`�a_ؿ��;â�K�zh�)(yg��tF!�E�&W�YYe�YTS���,����wv��,�k�6m3�2�S�/0�C	�a�%�ކ1"D�SlF�� @�?O�-uW\z(�9��ohm�"�0��@y��^�y���-�k`�mÃiE�e9�ݭ�iΛ�R!Ǝ�bD���*������H J����x��A�!
4_����=r���pa�f�Z[ܴ�����=BĂ�-�P:N�8�V�hj��Љ�u���;�x1ԑ"LE��� @�n��� ܫ�<!�-=r��F��<�ɤ�.%�i�K�\�4���8�������f	wBB"J,s�$�=F�^2���6�H�"D�T�R�B˾�9�%���=2�����e�j�.Z历���qs��,,a^I�1X�=b���iw���6�s�kS��m��^�bi$IR�J�*T4�7~��ѸF\�#�GyX�j8t�s8GcPޠ�q�k$��h��9ُ�%�M�z;�r��(�Q�����������K����M2�˗��J�+W��H�t�K讇A�+:���cM���֢� ��@n�T�%Ҫ��� �e��,�6�%��Q�Iz؃m�m�7��'s���
�~+cx�N��MP�r�ˁT-�t���f�KmzA�7%ˣJ�E��2��(]�Ǿ�3Gc/�������U���΁Z\��lĭ�GR�g>�3��]�XH�AF��6Ұ?S=���o�������h��Dx�T�����\;��71="�L�P�ke��V=ؤ6����?Q�d���>"���Vf]pk�h��\X$>����}(���ީ�	�\��(���+��8�Ě�˗�=C��5�*-���=w���EVf����a)�F��v�>a�����ȃۏ�(�����h�N%���(x�����%mv�
���<}��E�����ɒ�r���-Q;��?��}���)��x0=ϼf����\�d|��0�a}F{a8}�~%��?��H?O���T��TXq�y��|��eb��
AK��u'��;�B��JT���P�\K��Y~4M��L�Һ�q�\�9���Ua4I@�}�WЪ�?�l���	ʚ>��-���:ʸnY���{������z���1�����+W���K�9�)�e��Of����C$p���2��!b�r�1�e�D�AECJA���m�V�m<�﫥�o��击��p���1����7��A�06���.��ʍO����\�T�R�J�*Tv
f���`�0Y���ua�;\X����*�S$�e8aj������^O�oܠQƉ2�[.l�j��}8���tX=���H��k��s&�B.���{w<�DԹ;c��]5�J��4��<y�y���12��V��7�Vҭ�$4��~�3	Ufj��M����J�G�� Z���?�\��.�B����pL�X�l ��$����+FV��B�U�_������N����ї�r�#���P�����4�1\��y���a����ʭwά:b�����%:��3�G����x%0d�,'yFF.���J�š�Fa��uX�K˗9A�@�+�e�&��˹w63g�Z��L}����3�4��0ʐu^���TTj8��7V�/�#��m�c�K�"E 2�E�*��zOQ���8��E�>�U�lM�H��V[d��
��%Jb[G��=����RR1�#���\0�-�lҴ;�?0Лāa��@ֺo������8�!c�3�<��J~���SqT�*?
AЁ�1�I^�wn5���h77�ӳ�و-p�9G�&t(��"�-����V�T��.�ߧ�v�h���f�K>��ߥ@���Ru�Ca
%ztTk��Qqn���+����U�	��B\.-J�{8��T�����w�>�7el1�GGK���x����.\a�y(9��*��2�"�����
���a�I�����L�X����{�D-�+���;˂�)d�XMcdۈ�e_x ���7�[K�T��Ǽ*A*f��~�Py�]�Bت��)���"l�#_֓|����aEM�s-���":?^e��<��hV��&�*͹��%�z[�o�r��:9�,�?���)fTyY�U�2�D[؄&�/F�G��K>޲ɹ�M�T�P}0��ϧ�m�0����h]�
 J��������F�A
��4L��1S}��q����躛$�ygK�}�~�w�*W���o��<9e���WC>���UP�.��F��/*�BZikn�~�P�S���[DƆ2�����e�-����nSZ�~bKw��R��=_�o�CQ�c����P4U/�FT�U=��w0׭йz�4]N�)t,��M��Л9/�8<����<Gh�:�/3n`s�zw0��B��ި�q�a.���~ag��?��Z����a/��`���Gs����)�b����+���������.�!���TPTˌ0ʕ2%J�*T�P�T��Պe{����y��I�9��_�'��v�}�y
��l����c/O�ׄ
�oM嶼0`���o��4�CtC��	JA%Cw񢽎�P�:��FkՇ�e���j_~��G���sߘ�7Qb��*T�P"J�<��p�ǡ�ӡꮣEG�JU�����^��ܹO�ã�#��I�OXo����0`��?i�X́��	$l�L�g�XWQk*�>acdX���;{J���F�#���R�J�*T�������� ��(\�������~a5?��1ь�����4��� 7!�3�_��1��⃿B^�EM��0cNL���T�R�J�1>�#��(��E
�phz�MU�|��{��}�nNG�{�\)�����0~��]XH2��[�GY��aH��Je'�<M�B
���*$�Px� Z
�61vq��yr�鉼`�ϴ"�����il��ī#��}�J����|փ�ҧț=C'��Cz�hʕ0����{��J47�5��)��a&r!�e�F��I *`c:]���fJ)zL0
�
ʚ�$IZ� "]�؟�������V�Ʀ�O��9}a.XfY.]˗--�W�m��Q"vD��	?�q$l�6��yfm��+@H�E�(bTD���7Q�O?وP��'�c�}&���0e�$IQ45�A[��`L��_��%V�Ωbm�^t�E�Y��8Ћ2D���v��f4�$d�PQ
S_o��a%K�UQЗ z���ĉh42�%{��u|�`�H��J� �ƹ7-�w��ށ�8>��^�;��ft�L�̥�S�
��*1U��3Ȅ}9Dޘ��	�28Ԓ1�2�|����\�������1�6v��
�`��z����h�&�GQZ0����*T���v[�����3+�y���6�\#N�K��@\Gdmm��Q����+�BB�ŷ��j�)���%�y�=��D�J�bF*1Z�3%J��g��GV8���r�������CQ�l֕F"�s�<06ۉK<�]lJ�*�1�~X	LK�G��k�"T`S�v�m�_TH��G�1vb��.:=R��Z\�;�����&�z��dt�i�dL�h���������DUĕ9���u:(���AB�l뮅�f<�m��/���r,}+f`���9?\��[H1�+@�]��KD�=�k�ױ_�T��ьv?���=}	�T�L8A��ǿu��)���.��3f;. [���� r@ǵ�3$�c���D��g�=��
t��,���1QR���pQ'x��ߠE�g�u�/�yoYh�n	�%�Ӷ/�6�qa��-{�

�
؍�}�*8Ռp��6�d���w��E����*�&���Sdu��7��>��T�v;Ϥ�6m}����X��;��9��)����/�ݻZ��K�%H�^S
ĩ�bn&��-�R���ӂ���n0��?�O,� �̠�h9~��>���c:֌c����bj&�Eo'�D�{�����C�NVV0{gb!��w����oW�h�W0ȍ�l�߁���܎ֽo�{�?o��<��Z�Ǡ.��@�B(� $�#����xXa�L��K��/�,KV�E�ر?{�3
�^���X�7��T^����	�����Oi�w4��7>��)HE�y�����v̦K[`�$�J^򞀉7��b1Qr�cc�L��c��z��`�b=!��ν����`籘�=Fy���������3�X9���꽱��p�m�7��Μ�Vo{u��2�gt�ñ��5�QG���̨ʯ�vEg�qޘ��Xk�6��EF1���P�I2��f^�N������x���K����0�����pe�хsLTs?�N�^��3(��ip�$A�_h�
�U�b;�B�
�r'�4��?C��⥶'nϢa��!J�	�d�$H��o�4J��非N�O��QR˖@)���/U��^�����;M��c��Ng���b�PP{�#��fp�*F0��1&�Y��_�ٹ�^%�p���x@A2D�$IZWE_���
e_��G�~����kLe��Tm1L(vi���?p?������[����}��~�>�௖�.Q}����f��,���Q����ʕ*WQ�����aԩ�m.������]}�-"��
im�e�Sb}H}�[؟�Y�AgG�ǹT�_�U�ȯg��Q*Y$IQ%J��A* a�/w�m�jm.7yeB�2�U)O�0�e�v)g,�--9�Q8��h�Z^ݣ�]��'��y��t+G0[N��u�!�
�#�1�1A�t:KjVLb�N.�j��P{��A��~��L��3��3��͙~?s��Fn�M�	9Ɍ���IZn�r��mbE�U-�.-vK���A����o0�W��0%D��9���Q��=Xh�[�a�JT�PԲ��wh(��6���9��%0Ҫ�7r���&�gu�+�����LK�*��`?h%����Аi��x��kv�߶�pW�����s���*S�v���1�*nĸ7�c�04I�iG���s�Z��s/̹zp��s}
�3.\d��^��>��^��Ƿ��U���	�[?U1���w��w	��/�Q*�[�[-�lYz)
F)�k������#�9��^�h$2����v�������;��|�[�>f�>��H��_wo�JFя]*��
M��o��X�tE�m����4-�:6��2愁E�>�C��O�&ۅ,�Y/ѣ�$��t������q�j�2�iZV����?���^�
J�� ���~��f2���o�P��lVT�Z���C:]�S��a�2G���Yd��٥�M�͢�
�C�=�;�;k���c/����9����b����Q.TIQ%J�*$�R�MV�/PpM���#a��̭����s����t��Q��Bm�3
�ql}>�\�`�"���o�A�TZ-�%ʙ l�ʕ�Jт܋��j��z��kÇ�2��ږ�yyih8�6��'��@���>�"0>��Ph�?~%��'�T��b��������w���<M�h�S]�0/3��*o�f+Q�]m.�;�-j�.Um��(Jb*���G@(���+�C�@���p,�	�"/��?s����͇����~�{��J~7�;���*�>�l������\���KB�Z��os��Aڥ�}�h�?��qy��\F#*T���i��ĸi�V}:�l�<D�MEb�0!6�`K�eG��&e�c�J�u\t�z��Е*$44�
��,�0�ṯ{J��S�\\K�u|(��E��ۃ���Z�+T����Ui�hGh�&�8��*�i��1�J��:3&��\�P"@�����7�joS,9�"�ᣴ���o�c�!l�B�)Zs��)R�9�A���
�v�y�Si�J��t��%M�I��c�[�W<G[�/U[�*��0bY
S�Z"�
�^a��;G�,��ä�*�x�y��(�K�U���S7�Q�Е8ц4�����M�.I�!�ei��w�w�e��Jď&:���е�:T�^eņ�n��0���q�0j\��!����X��., �s��1�����y���/,ݹ�*���Yq窿�K�%�7�i|hj0�Ąet-ku(�;�������6��̫���ƀ �)�o�0�3s��(��*[�gvW�JZh�����D2�39�y�e�0�#��J'd1;ʛ�V T\�8����J\K
$J*��9��ǟo�X�?�*X�X�ɣ��#��N3+�LY�9��+.<͓b&a
h�����^a���ܞ#���"K8�i�
����[��č����x���fm��B��"���]F"�ƬsA�ň,;��
.��r�p�<iK�qNȕ�uS'E���LI�s���/E�[a��Jy%�Z�%Y��e����K�0sQ�w�
錨T�.�E�2�f��0|��ƷR�z��r�ˋ7.\�P��
�o�%� m��KsP�3��J�N�!�x�ģ�w���e�.y��T��]!�����V0S,�
0E�k�rJJ�D�B	r�̲�Q�B�`eŗ.-K�ô����
H��q����_��q<P�M���N�(�i�x�q��-�y�����Ym%A@<Af^c�(��r�o-y�-������i6J��8O	�@��R픶�]�y�sF�X"���h�
�(�W�ƙ�ɶa� ��.��3�Sd��-1,v��%�F�*��1.XV<�(b�(��f�fH9�Wl���!����Ё�8������,!1AQaq������ �0@P`p��?��J�������r�r�
d]c~�t�W� 0py����_�仼Ί�w
��&4��ox�E�ӝC|}�Յ��w�aSY�Hp�U۠�<?�8͊��Hr���iu�?}�x�h�WW_�4�H��,���p�W��ί�%mOzG��;��ѳ�~�FGTrЙhe=1�(_	��E�ϭ�
۝%��i ����H�!R��������^H
@����Z4�#Y�[��n�6�֮��U�Jj�x���'��[�w�{rj��yk�+������+c���w�P�Am���o8���㏜_ݚћm�i�_�̢��?�la����c�xw���{��i��c�*�5��Ef
a��*€)��_@
��������T�'&j�)]G��?;�h�%2�G���@���6C��V#:˂a�Q]F���
�<�q��ME����yl-,i7f��̑����c�|�1��wv��!��UT�Y��eUuq�0A-�np5���]�5�Q���2[-Ld���6S(^��z�.j傴��Z�g�W� ˰ڻ�+ҥ�ݟh�dȾ7��45�3�k�Ow��M)/_|Lq*�j��t���sٿ�@X�(���XqYo�����]f�����&��P�ڹ����<=�}�LT�s>r��P<F���w~߸�ڙ�/��W6f���|A����,���뷧�j�n��Pb�Ul�g��5����%z�u��������.��f>`@PO���EP �x��KL^1q�ɻ���t��E�{7���]�{���-�q���v+�^a�����u��5b9͜C-
����ָ�-E��O��Xa���U@L)(,��ߡ������V��Jk����|~{C,/���e�P��`dh�V����m�?�ۘ��d��J�Fbr��oj�LjYh_��M2��pĪ�.�c�@QD�_��SC�JP�Jk?�b��f� @�8��
^+PA�q
)�.P�U��0-S�����5l�`-���A��0�ۼ��A��:?zC&{���,פh:�@�R��AUx��=������q-,��zq����ɌW�0��V���NZp{�`��8,Z�j�p,X��kW�^�%��%���2��W�hkB�:��R��c���D�8�yw�X�����j8��L�B����pܠ҆�e���&�����9j�㒚�
dh=��%�"�Ts��#Q�| ����ǣ�n���<��x���4nkJ�����Y����!*����ò����:���Ļ��G��M�K��{}��6
u���D����L��-�u|��8�q����-O�����%�
�YJ|��s8mZ����Xr��Mx��V�ZW��~d����z�`o�
��:�?�+�l-tf����/z���B�6�X��E*�~�$�*�G�(�:zW��Jj��ɍ�Sm���	ie<��w�i�[Z�Xg�4z]�qF���~�	fm�)<�˫�c�y��J��S�̼(�;�ҙWpƴ5c�ޟ�
;���Z;zF����l鋨E�eN�u�@��}�A��tK֍<�@���c�ۇj�I���-�=�Jr���0�C$M�d��z�j,o�u����t�H�dv^�_z�(P�G_$�l-���,G���'���{�k#B�7ݿH$�VO�{��X%��QϏB�
���x�������(#���[�2�H������z�[\|Gc�����-��:�' ������"d���C��,�+�z�`ݾ΅��qC���D��X���b�`~��/��f ��%]FUJy�+]��p���
<�eb�D�NjU�y����u��+|�0e
T���%�R��g�3#Z��\1�����	Z�e�+W���	��7S%<����aH�'7������Y�ao?�����6�mX������q�k�C��c��Vk̺0U=z����	@X�$�S��#K,���TDF/�%i8S4o1,�q�
�M��Z�%UW��1Z�&�]���.�(e�ɢ���0�e4��@��1�'��� 1�����Kos(`�X���eh�����!UU���\T64DDa�
k�r�0xg�~��Hl]���8�����5]����g�#B��Q�7��<�u�_}a$83oI�	��/���H�o<r�97�����T��$�(��\����<���K&9��,�L���KR(��G�l�)t߈��*,n�L
�sU��Z�Ym��Oޱ�,�9uq����Ǽ�����x�ް?NvܲP��,c�|�ꄡ5���'iʹ�v�/"=~����T�j)�H
0�ݍ���̰�`��_{�@o}9����:��-�:O� ��Ǐx,��B&Z)�
p
��y�Ej.&���zBd�/���<7m�_{�Y�n��{�`ʱk�Q4Uf��>�@D�n�2����L��DF�)�k��ţ���WE4?
�F@E6k�UimW�šx_n0�xÃ�`��N=0C�-5��^\~��xq�L��sK뉅��a�Bռ�Y5�l���X�Yz��3E� -��O�`d5���.�T5fS��/e˥�1�
�GNx��;׷�)1B�͡3I����������+�P��/?1�N5��قŊJ+�3��8��F�i<�=�P�K��{���0!_�q
崼��,�m�;�0�� :�b�(]�\��}�r%(ښI�'>�v�p\�1ش��u��ʼ^��"��#ƨ��V
 ��/{#D`( ���﯒9�et~�qx[t��C	=��BԵ�њ-�asy�peS���i�ꡠ��7�a�z��JQEe��'h���%��q��ﴤ@vLC�����@*]��)�Р�ύ{��1���ʝ���hD=�y�Uu�)���	շ_{Mі^h�"��2&���bq�����}���[�1"bL�Pd3�3qdbS�g�߻�;�t>��`���,��}��ab��������h��=v�ڢ�Hk���9Q��`ƨ�}��0�K��e���̓����AC���XY�A0h�0������8R�x=��:7�A�����hCP�r�dKL�gl%EfV���1!�c,����?�2e�\��ݯiK�ko>ϴ�v�Z�}�x��7��v!ۺ��U�Ѿ��`�b�c{_�״��:>��q,YkF<ƶ��"bp�1A��h4[w�S<4�a���,�n��m�M��yqhf��]剳0z��0�Z�͋ߵ}ĩJ���D��p�8��`����F]�|KB�j��4�,��P�e¹�fj�%�A\�Sm�?����9c�����k�-q�-!)��Pd��8�O��-�k��0L���`ʶ��1�#�:��!E(1��&j��f�����5O�-��>�Qk+uz���}9�-�M�[#W^�Հy�(L��_��P�w�u�w�%mQ���u� F��x�ް
����Ǝ�Yrc$�s�G3 !�%�qNq��P\�^�x��.S �O�5��ϟ�Ũ���5�|���%� X�z)��S������VJ]y���z����q�ߦf-�VΟw)�#�NsدX���X�*�9���}�.���߼H�-���m�]�o��(6�x�I͈?�Z��������˸GR�ܘ�k��Ɯ�؎ݥ�T��o_��*\�t�W���W4�]���%�=�%D�!��*pͯ�c�+�B���ˁ��~cN̔�08
��,j�Ҙ���Ѻ??,d_΢/#� �J��lqB؏��dR��݁�zD (F�+d����3eU��Kl���K�_n�we"����������� g�R1@G�өx��8(�-J��ƺ?5�ʹ���!m�e��`SEt�F���@���Je��%����m�&���Ҿ��f'�(mן�;��ɣ�#5z阶!���ƈ�A�����T<e�‹���� h-��b�N:z��V.�/�;3�t��80s��@�z����3�ç8��CQ���Z�,)
�7���a,!�1��9���S��Zc��%<�c)�B��o���Ϋe�W��q�cu�~`
9-�@���Zq�%yER����0K�n�Ew!2�*�Y��*PQx�/�{b�@g����u��C~�r�"�o�H	�C��^�+ݲ�-`�W:�:��8'@��HA� ��ܼ���v�U����1]mh�2CL�L	�m_�ʁ+��c�{���)�J��J�oY�QȻ��]c
�S@֩��3��Pq[�Z�*�d����-\��PFE.���u�WFK��3īgc�FXA1���U�[�%��M7]�EVs�2F��e$
��(Q�_3y/w��it᫵�UAy�(��z�lYR:00�26?�;ʱt=&N
Ճ�5,�o�-i��j!�Vr����y� ��,T����&��j%�Eo�?~�d.�-��aAn�_��VB)�S[��;h����������{��T'L�ݐ�W+�]B�����+fM�n�2�������5�Gh��[��ޝf<���L~c�F[�A2V�R���\v��NNYǬw
0����YW���*����*�:}0�,R��?_��XӊVPP��x�A7u���`�5��T/gB��ʼ�6�ExV�f�I�S��ĺ�LS��D
�Qp˪0LXo�W%蝿2�"
5oT��Q�����(��q��`�hqo߬0a^"�@�먺�k��� ��x+�y��_U6�����Z���#ܲPF�Ȩ�#�=�G����
�X.fzV�����
⹈��K�Rc�!h���)z�p+�����s����(�#����Qa��Ԣ�q�St��VmÊ�+X��\�S�*���0@�&��_|�إtu�}��;9up���4�[W�flFR��h�h�����J�U����%��R���,���`Z�v�^�d�la���7��p�
 �{J]����Q�_}e:ul��dž�����=yD��Z��� ����r�0���0���OX�Q�`�@{����/�
� �����
�Z�`��BS@l-k��V����\ڜx9`��0����Z����jf�t=�����ܮ((X��m]qx��Sk���T�Yd�ƻE�Z�)�+3��{�Hl1~a�]^Ȋ*���E>�j``�|��b�P+�5cG>��!��b#��p%J�ּq��"2�J�e�/L@���d��X6�8uu��޽NiѩU[VYW셢9LfV=Ծv��`FA��*g/���-��&.����(at�X�[4�IR�F�����ڋ���w�;�J���]���Gk��x���8(�g��C+�̪.�Q�i�o�[,�{��X�[$��M��8��A�5��}�T�3&���������mk{�ĪU��*c�[��U��4QT��K^�aT�[j(D���R��B�D��᙭��P�����c��S��9���巫�Yjy�Ĩ)E�,yu4�A�?�����e@�O�H�`v}�Dz�����xo��
��;���xܹ\�$&��v�Yy"Q�x��et-R�o0�7�����&�si�@ƨ�D)�+�KT.��|��'�k^a�w��'�Ϗ�-U�n����b�V/g��Yj&:1(Ʃ5�PŢ���X`���\�^qÏ��u��z���bXC^��c8�j<��S4�� Q���������~�����z���4}���͝9􎫙Vx��$!{S��)���p-}`�\��q�Rɝj��t����aBP/\}�.t�ACG�mF/?{̍q��4
rt����c�O�'Ur��އoB�m�G��W�����Xqj��,Zj�0sY��@*�N��ph<�>��OJ7�]Ŗ�D��V�H�ӉbA*�}�t�7z�]2���f��f�?��kG����dαy�7�
�o:�y���p��9�8�n"�ݷ�gH�S�F�`�P(@���B�6rW�/S���'����Q�������	(hj�\@F��AiÛ�C*���4-�,�؂!�b�DF9wx�V%���r'�1���@}��k��"aocd���̫��b�~�[t�"�VX��K��b5Ie.ת�׳����fh6�u{��p]FAK�b�f#�Qj��e��Ee�
JM…�:�z�������/n���%�k
����C�P�Y��F�״��X�b�qo	��U���i�e1U[����]��qF:���E�i|]�5���fe]uY�&�;b��
�2��R�������`q(vV�ƕ�u\��3Z}#`ikl0U�s��Yc!~aKz7Q2�S�
R'YP�`�J�ϯ1�=�<|G��!_�1aڴ4m���mW
s[�R�!o#X�"�0������/���h��c����@4�/'��U��k�����
���|L1�8pjZ���8�Xh���7�Ӻ��K��Ri�ۚ����6]v��D1��4.2�����/r]R��3wk�\q�E���UIj��ϴH����0KvS�􋨰��+M�]g+l�yD��Tk��Q9Krþ��3��3\X�e���������핅��@�*s�����"˝B�'E�=�e�sA��fa)�S��|Ʊ���� �2
�EjX�X��<z@�X���2g4b����[��z+B��SI��\JTF�/H-��NM#�E�>awK"�_�,���2�I���I�k%t�B�v:��j6/����eo��~5!���.e3��a�X
iqϭ��!��>_��`���o��cq}/�-�Ⱦ�p�<9�-���*��l2�YP��P�T�k������Ƹ�Q_�GoQ
�Dɳr�X#Z�J������^a*cQ�S$�g����G;Op��_�2���΢�;�TB�u��h�^e�'(�+��˖�9��F�1��ŭk��3 N��=���r˞�EЏX�ͩ��/����66P�Z�Pcu�}�Kh_���o��B�����hq�:G��\���M�4���(��pD�37^�0����h+�Y���s'
�\�H(���JȚ���PU���
�ø�pS�B�}�7��W�϶B�0�×�IB�����/�O��Q2b���[��s�GC��`4�]�Ī�	���ezY�{�l@�8����l�p�R�WF����hT��!.ѩV?��Vp���.�<�İ�ST}2�/LÈD���aV�Y*ךּc+�<M��b�@d�4CڃnW9�z+J��E2`������c��DS9�N������������=~�
�g��J�ʿ�R
��[���7j����Xy/�!���@��`ٰʼ��%��l�^t�[���KW
_�6qEc�pX�˾�%���a�l�G~BjQ���;����>T�\W-���R�}> �C:Zѷ��	��Q��т�R����p�T93W������&�=%��	`��
�HM�d�3Z/��zAVPj�f�%[#�1G'�a!cI�`@5����K���x�>F�y}�R�u􍄵�-��#j���߄��A
�=�q�7��������:#V�x��N*�䃑�$K�2�xg%�����KTr'��rb!;�~�h��]`6a��^��-w���
i7[��BeY8���֢�CZ �Z��*�l�k��ʆ�Um8��G=�8��lα����L>�B�X��P��ԮG;�
�mߏ��#�H��9�-]U�(Q`e��z��r��Q�V�U�� �ц�]-��[��������`����a�ǿ�(	V𘋘/��q,'L��&�3��P
�)N>�w�k��%ܫq��e�/�9�]h���&P�j|g$��G`�g�nu��?�~��b9U�@h6.���sjˬd�3<�e�� ��Ne9MT���XR�onIc�+��Ź�̠�o����WU��7�mw6NJ�U^��
��#���h���s�����-�n��*`��h�w�Ye��r���a@J�H���$8��f�p>����<Lj�F�u+��d�.2�Q���a�B�qT\�
���,`���b,O.`��t?x����S�1 X{��=`�PT����}�E�Ҽ{֎&Fh�wt��XRDYfHa����p�YE7��U���5x8��D6�����գ�����fi���y���6���٢]+�A\� 0�����
ES=��Vfq'�H&�6UUȄc��(���J�l��UnX�9	S*��U~b�cI.ෘ46�ά&�?�D�7%�6F_t�L���Z^�K����GL�bl³�tzG2���!���T��@rIp���|���՞bX[��[�Z.Q�<D`E�0T�*��E�X[EB��/=~�%`)����?H��+4	���:����?��S�C��
�1Z�e�׃�q�P��C�x�n����g�˜%z���EJ�
�@�����I��q�
��ief�_����d縬�W2���#J�FλwV1���R�<�D�6�8R�Aq��ĤE.��W�C��4,{�9��=S���̹��UK)����'�$�[���x�
0y^�_�w��s6̈́�ex�?H���4�/a����mqFH��x���8�3aR�('Z���~��hgK�����f�S��M����]��MK��*�	JL�����]��@e�r+�m��]��``m�_q�V��FJͫ��O�Y���\3^G�sl'[T���Ҿq����Q]^3���yh��M9���S헰I�FK��…��H�#+�5W���\\.��7����mf1Y��!�y��Ҁ/�Yi�Z�g6|@�t�B����Y�������W���F:�ĀlnNj��7���.p�2��*�C���
�b�X��*�>��yZ���!�K�3u�ve��
���#�TUCNN�7Ya�m�r�����Mu�.V�ܹ�
�S�l+j�-�׬`��S%3J|���A��f�G�-�?��5����^}!�
��K��u�{���_k^�5��c[6�z1�A�׍Mᣧ���wysQ�˿O��6P@A[�W���,��"��Z�{ǚ��fi�r��� ���P_Rқ���<���}�)UGw�3���Үd�n%j��q����:�e�Rԗz~H{ڽaa���ĵ�����U�Y.���K�d�S�'{
�q�Ч��[_�\����b��ۨ/x�߈C���g��_qwAg�����kQ�Z� 
J�A�$*R���'����V?M�rq�P������> �K��ŭ��UR���PLR�\�o�e�G5mj�n��.��
2X���\DXsu�]}g�a+:1)xzŮɚ�� ����2����h�*���bT��W0�7�C,���W_��\���?����fgq�.����վu�X��K�3�����z�����9���Pft=O�<�^`����F��T��<{q�z���l
�c\׌���������"��Z��W�y���6d���Ͱ�l��Ǝ�{B�lj��)���o�J%2�ը�Q
�QSG3�5�#�.tfyF���gw3�6�;�8÷
��|u�P�N`�7�J�W��V���U��zor���<�\[i�U��ˊ��HT�t��TS�Q��3�����>s��P62�/P����aFkL�h(f��5�E:b� P�u�
�mƱ_|KU���5��?�P�����dʍĭ��7f��f�F��`ˠ�CqV����EyE���ix�YY��73�+K0�(�_��v4��
�����/Ё�yY2w`V/ΜAMA�\�b�+�(�4��%=��uq��/�"���H5+�
9Tw�gW���r��KB��}%�9�_6k��_�d/��̦:p��8y'PgU���1LGS
7[�7�w1-�X��E�;�������v/eT��
�Ô�yo�n�K�<1��WUqR��5�qǿ����f4R�YwRb�A�K���6R�|K�X��t���ʴ[�>�W^M2TsI}�
�U�޸��8ϯ�:���f5��e�\6w�Be�̷��W��8�[�j	IL�[]_��QJ�|F��Iz��g!F��v���{/x6_�g%B�[��
��t��e�w����M�&er�W����b��R��L�.���3��
=o��
�_iV��'3A�N!G7�$�>>��|�x�[�%��#n���y��'�lVV�(�zT�_��o�9f�����m�C��=ֹ�Qۦ�	��K��x�qL�V���i����G�&�+���1��� k}c����}"����|`��N^pcH{f1k��,��.롚�Т��*��,Q��c�M�9�����gxÍu��Vt��iC��8��7\��*+l�:��h6^EqPŨo5r�J�/Kה��)u����0퓘�<�Q�X��	A�xL�V2�
\�\!��H%�6�U������^ �K7^��-�����$���
��For����wK#
^�Р�5YY�r�*���V������5�~����n�;�K\����Q�e��f���(H<�zǼ���m��B��m�����X(,�Ï�A[¥�W�||M�}s�>���h7��8 P-+�)n���4lӛ���A#JL�J�Ew����c�$���7���ێ�	֡�O���¾� ��v�?�I�B��+٘��^
}�z�nw�#�z��gW�G���2.7����Fug�!��]3��a�[����0����q�4�ei@��c:Rn�4Ӧ��������X�����C��຃�@8�J�R�axS�k��怭�/��;��ag� ]����Bwo���CؙO���p߲�lL�wc�1�p�܈�<^��
�M��V$��K%aʂ�,�R���j�ƪ��$�`�(f�B,���b��~ ITK<q1�H�Z��;���KU���r�
)�-�O��~`����On�H������N�~�W ԣ9�����:�L5��~x�����=����>ҩ��"jUQB��
�^7���QX���y��3�ػ��hQ�݆&,�c�pNJ`9W����.9�����.X��QT�w`���`\�Zַ)�t�7VyGf��	p�����6Jf�|A��;�T���)V3ϏԤ�cX&.����e�u3���}W\�bS��P������(�`S��RI�Q����
�9{�tw�11
3D:�\��g1����[��m^���$���"#lӧӤAZ75������c���Å��`����"�9�o����r0��5=ހ�H]s�^�oN��@�����#�}K��j.Z��,T�6����0�ڠ��-i@�����ԱY�\r�_6m�)��
0X;˖'����[=nMށ�������
��1�P�ՋϘ
B���t2�����q@��թ�M�"��4�'RT����^����E��^���V��}�'��E7��K`B��q�F�'>}�� QF���f�p�����w*T�2�-�OU�}Gy|�m��ur��d�o1�(��!�ab:��g�l(r�ƺ���><�$
���HII����a^�RY�*�S�[}�ʲJ� �#��B�n_hʂ�@:Z{@�u�>Uз��ɜ���^l�����6��fk�/����e���E
�#96�7�����p)ܽ=���G_����
Z��#�y���K#\<�����4�R���n�B��a�/�Y.��"=���Q�����p�c���9�^'��]����=�&�6z���<��?�PR����5���`ZhP�}��%���9ݳ�09���q�+��n�X���o=?�q�>�פ�]�,��j��[H���J@b.�=g0�+3�R옹v��O�H������;%>��HѾQQ��Qn���}�cp�U�Z�9r���(t��gC�9���氉&�r*�rrZ^P��J8=��]���F�ȡZa"Y��~e��P�S��-+��(R�P0ҥ�E��x�f�/�6�۳P�)�Z��:���k���o��r�
�]��P�W�&�z��Cq�q5�Nox����rlk;����e`��
������J@Z��vn�">ҁ�����1E���Mr�wY�f�����K��Wہ��~�E�].&�ٚ��P��]y����]p�hT9��gv)l�1T�º���p��
�3��[�cWQԾ59���8��13ۤ���f�p@Q�r�q�t�8�a��3��:�.HAcz��Z�a`#��Yk�'��e%�s�����y�ʁ�(@Q�%&�A/Sa��P1ns�G&�H<�}���R��$�}c<��Yl��Q)�e�X�H���.ݸ��c��ƢkuC��E~2}��jo%S���)��ϡ�I����
�lvZ�l�����-VB��(�Z\gx����,Z�<t�
�02����1~���
/�%�*�c��m(S��2�_X�K���3���ī���ī\����ZS���@X�6R�H�EB
��1οq�$1?����^?��t��j�,ū���r��:OS�r�1�����������=�}����0ƀψ�T�����-	�S�u:�r�D��x�yx#����iźs�Z ݫ�Q���CU��_��@�S�|�Pt=5�ܸ�M5�_��2(�r���a��T����-��
J&���'�9��y�JC���爪��F��;��z)����Y�6;��q�}�΢B����:���G{�0g`-�i�ʰ�zC��2��<���SCFa1�ʽ ��Q.7
�EeK��j(�y5פ-ΗH��ɭ�Z:V�\[��06-m�j.��/w�qVGο��h,q�B��6�V)M����U�n��!��)�r���Ģ�c:�h���?�P���s-̱����`�ϡ6pCb��o.q�n%Ҷ~�$���C�[B�ѻ��%��uM��.�	]^�s3�Xdi�t�<T�u��9������̰��]���RW��.��_��:>w:3���R���C-~��� r����J���"r��ཞ�hO�_N�����-2�x�����X���eخ��j�;�"f��(�
�;E0�w֗��Gxl���Y����zn8ժ����lTa
�G9h�>e�?d�(�Yj�+��q������b���V�`��b7�2�)ʄָ��H��[|�	Kѷ�P���/��52:���]`�L���:P����
(>fc�z�Ÿ$e��9�;F�����yXN2���lCF���ݹX�FLB���%�G*�OnJ�5E��g-�����l;�tG�g�g�d'rQ��.�/H>�y.�Sz��RX�Y�}�M�%
G]�e@[��Bf���6\n��{k�����ljF,���q ����9Ɋ�j����������]=���׽�P<���D����U�ozbw��mh�q��aҲb�Z;���RQk�_���Mi?�d��.��L�;�}��E�̡�`�`�ҳ�!t}�{z=�?ڶ�*و�,M2و(4sd"�o�X��j�2��}��5N�:v���id�AM��A�|���]�Jl#�"ZV�JX�jR��FrÏ�#��7	���V���]�A!�;aO����.���.�=��+���A��5����`��n��K�7�RP�8�z�|��P�9�& s�:�)i���l��d���N��#@�L#jl/��H
�{Ŭƻ=����8�cO�N#�Z��߸���&"����Q��w��'���Ld�1ĿZ��K�k�v�0�1CW���U��P���M�!��W	+F����y����	I2-#w�r�V8�d
�7�����ZA�IMn`xʘ؟[�_L~�l��C��
~���)�����U����h��`7�}j!3v��W�*����&Ccf߼�r�����'?㥀���p=�K��_Y}!�TBƍ!msy�a���qGQ���,�˃��`W����}C�U��G���k�N�/��P��2Cn;eUfg�%��@1j̝�ь����߬q���<u�z"5Bлz�x���ޒh��4QYRt���BT<������e���-j���L1�.�4b
�M:wI�Q��00���P��W�i������@qULNSD���rK��<Ʀ�����`t�'��A\W�0c?��A�C��T
��&u��ZE�&믘,�As?�awj�	��"���E����51��/�פȥA�B(�Y����-�~ck�O��yV������#���?{yh3/g<����Uu�?���WKD
����)���Tw>e&�T�ڜ,��^
�X.t��f,+�&����@W*��j೾�t_e���pWى��2�G��Ⳙ�E<֠��'���zB����Nf�q�8u��Z�a�Ch���Z�s���1V5�Q�&���]r@���	�d�J��K�0�a�;�̚���r{BM�#��oxZӰV�_M�������y/V��C�i��(���Q������sv)k
f����x�;'��*`mAHh�M|SpU@��v����
�_��u#&��"l�l�]XǬ�M�S�@%aʼn���`���|�:���ā����KZW��L�<d�l췮w/r�M�(��E�Y���Zj�HwB8F|�(l�鿉��?9�
���5����NG�r$��ȵ��W��)�Σs��V��\乴�򣳘e�H�����0/B�پ���`DP#v��m�s��+5ԃQ8�B�^ftf,�Z����c��L�%4q8�K�\#ts*��|ט�!��n��-���b?{�!��0�;FL��w�r�ms�1Y��%��_$Oa�c��d�� �!Z�;CBU7d6t7;e7�c���i,���Σ��+��^!"5B��F�uj
�H��s����3Y��m��,Xj���,;���
Oi7�W���g���'����%E;geI�:k����Ӛ��yl>;��}t��sV�v�F`4��~rM�$d}e��hYw��"�5��bs5	��7�}3����̃I�2k�I��An���L)%[�0k5s�F*���1���c>�7�hт����:��:�崶�;/^��
�����ũ���eZWp{�x"�Ԯ�#� �S���i9�D_���:sy�Q)�#k�b-y���F�~�VPK���/%_�[D?��C�1pa�̠�Gُ�JUٱN�ȫ9�lb��%�E�[Q[��{B���QUa����[�nf�����e����K����a�G!�u0��&���UZ5z�h� `���R�	�t�U��2נ��F�^y�7P�L��4z�?�$��%Eܗo�l��L}�
"]]8q�5~X����w�����sS"٬+��J�nW��(Yn�w�V��l��X�0��M�L�׼J>�fP�K�8V�[�nk��V��i6��xW�{h�o1jk�`R�T���-��������Hؒ�HU�m�)�%��Ȃb����3t�T�>ф��0�^�N��N��y"'��YR���������S�!cu�3�d��-S���V�f�̵��^GX��,���q('�j��c�Yk�w�ò����4�01�G�Y>jr��nX�:t���` +�JݞeXG�RC�S��Z�����F�����ˊ�Z�=��X/@5�zQMav�w	��DG�gl�p�*�/�&�;`�钫�ЫyW���mt���Ɨa���J�ѶZ�}�1�	��m�+���L�=�0�
5E�
xC����(��Xם8j5WJ��gN�e�0���ʞ2P#���ՍP�4����*;�M��eخ��v"5Xr
+��
Fh����J��X�P
?J���F�f�x��bl�#�2��m�J-��1QqY�Z�-���F6��C
���ə�����tЫs���~@�8���^�i��Yt� 2#WV����i���>�eÞ�%�֥�1��7\��i�Ѹ�
(��^d�<���*���'I�qF<G-]�֠����K��$X"79�s;�]S	S-���	_����$>�?�di�t�w�IL��������b��w�`���	���S�i�`���VUP;�ە�+sj��T�!�ߣI�3%���%��}���
�G>'0��+�eʹ��@�\d��`Q��8�W�R2���J�KL���1b¹����(R@Ȅ|nVyp��'F���ɓf`�	�hZ��a�[�����?O�-L
,�x����(�(V�M4��{��}Ґp�����۾�‹m�&$ºt�Q�:h�k�A�.f͈X��W� �mZ�Z4k$m�w���Eޞ�R���%n�|n%��Žn6��Q����l���Z;	������rR�xq;8�%�Z"Y�� �bn���C
1K3���'=&�OMC��e��S:�����c��U�j������`)]Q��ž`��]U�†R��������n&��Lc> 	K;�Lt�x����j�T�W�}��8	�ﴥTi�h����~���U�-u��ڝ߾��3ku�q;1���e�v��5פa
Am�u�p��z%��*)/���I�f8��;���?��-J�G����Y�����`�";��My[�?�­37���'yq�CU��Y��?��gr^�n�@ٜ�X�TnV��RӨG�*s/��,\�F-���qj4�R��1�����&B+K\b��Y�s65�68�Ҥ��y�X)��XV�H�+�k[��2��ht,ɛ���H���'A�}��g��%g��-�e�%T�`nM�NNAh�1��W+�)V�s��ivg�mc��FE~_��[
8;���!�Y`,[��Ȋ�Lho喾�kvw�_�B��{�P��X�Y�x*����	l�T^v��b8�	�uy ��.��F;üJ�^�@p���,����'�\��G����`��J0��;�	͵I�V�(L�s� l�8��#�@0z��?Y����r�[냈Nrh#��DBl�E>c14��|Tٴ����Qb��gqhbղ�R7-�c�2ec�����y�tX�����Z��T\���5Y%t��jy�6��b�?�~�:
E����Ȑ��wK�S�\Vг�q�{&w~�z�X*�6}��P<w�
��Ӗ��E���	�j�idw�;u?/�j��|}����sF��^�x��<e���\B:�m��^)2H��o�2��W���fhP~Sˎ��$w��J���q��QؘDL2��E�5P��/��aG-i�6����*���=?��q����E4@g��U�W�d����`P��N�:�K��8����،�[�x�����t���T�^Zy���Qxgg�-]Օ�x�/�ly�p�Y�Gڜ���ũJ//�aŏ}zB�e�q�-�PU�����b+)�<�J�,�q��_��b�qp�02��V��x� ���4�h
�eh��tJ�fL�����,�ޑT0|Dy�EmKX�&���.�Y�O�&y)R�ɹA`/u�̰�mfA��{��|@�4,C�ޥ��{�����U�vb a~��a"6��-�t\Ama�
=�0��џ���U���oH�[��ڦ���!��Y��'B냟��.G0�
h��3b�F��;���o�n�4�rF*/�8�N|={=�B�v.Ͻ�a�Ja����Fv��W�0UX3�K�}^�CO�C�KX��]�f\�\�̠�7~`�9�k'8�FgUO�^��_Ķ��U�[k��Ԥ�콭�5�b���.&=�3��Hp�U��'"��{���c�t��a@�D:F� 
[���!'i�8�I��Β�ӹ��I��/��剘w�L�FY�/NҠ����`�#nh��ys]��������^Z.<��8����C�z@"Zo=����jݻ�ڵV�����F12�Gh�:�qb����/s�0`�5Fe ��џ>=�≸x��Yfڮ���0���r�����*�J�JP�����_3l�����W7Y�y��0?�B
��S����~S5�=���l��=}�&k�u>�Ns4�Q�bh%�8���:ŽeqW�����k�:a�B�WTX*���
t�9[Y`�.Z���S�ۇl��ʯ�ó��������'w9�:�q(5Q
B�ް�+`{���<4�^�>!t�6�f��iw8�ۣT�X���4�c�_��j���Fj��^
=���z��M,s�\�
��e����xLƂ�s���Fs�^ou.n�8�X�h/��?��.YX�U`wj��e���ol��=�e�����R����\>ĸ8��a�B����d`�t�$�B�d~�تpJ��]�`TT�V�~�0,�\���
(�Tm��	STkA��hf��2�9��adE�/����2����`�7\c�ە����Ӝ��p���P��qX1Y�,+��rzKB�=�6���yXf
ޭ>��E����_1��62Ǽ�>��rre�H+*\A�r���1�����G,�- BpX����s�����Q/ؚ����!��kbYMlIe���
�i��pA�
���Q���9Ρ�˞�p��ѕv�i��C��!>Q�차3k�y�|��ڴ�N��x"��'R�齐�Ť.㨔uTn�݂�zS��2ٛ��Bc�pd~!�Yu�r�
w�P�x%��= �Hb�R�13.��U�}�˥&Π?�\���L��V��u�O���V�#\~1�wY� (5���nYĭ���a!��b���2�%�7+�ܲ5X���L�m��s
�v�_�a;�1����@���aNڇ������1Қ鮐1t�WZ������.��d��\�-q�ɍC�ĩ��/�Z���73��f�+ʾ��M{G.`�[
��(,[�<u���"՞�ZΆ�S�N,���Ì���tР� ���(�t��T�_00Wyeb1NaH2�p��&|���@>��m�It��"��"؍��o��i]��fyig���O���@I#�z�+ͺْ��/S��)"a)0��_�Q7��E3���f���ٌH��%ǏX�8�E��q��R��,�7��k-�'Y�X��;�%����~ģ�6p�9z|>!Hk��ED�8`�.)�C�!��;��~�{�CgN�@ͭS�t� �)bu����_�a	��z+�����hC�r��7�Y�l���,�z��q��.�8����C��;q�}�d�HZU�vC�,;��&O =i����񹌻�ˢ�ōWO�Og來5m]:���#�3�-
��*�8&`��tF]%��/@u{A2L�L��9,/%��Q�!b��G���b`H����qDQ8v`me�����c*Abs����;n�ZȬ����=�2��\D��P�2�Y��2q2��%.�K�_D/�u-^��Z}�ɘ=A�0`�r���)�5�R��)�˼���g�@<��w'v
e�7�Ǖ
��q)5P0-�?���۴�ʔ�m�jcR��h߬�K�iAz�Xh4�]���yC������Yk�����,0�_�ϼ��'���Q�*��l3e�DQ�C ��B*����ڃ�
�U�Pg6���R-J��?p�i+��2��9�`v�̺]�xH�(�j}K��R�"s
V�����\tUsf�CzU��s�c��
2�5j�xt�G���l	��ex�v��+7,[�|�
]��A���5�Z�y�Ei9��ڮ&B�6%������9�M|��A�������9��1<`7w��%���2��љ��ۼ-��=A�]�y�5c{D�R�sw�<�����X]�ڽt�������t�x��-5a��ۿ��1�*�N���c������M^��^��7�آ(Qy�nRJʯ������@��;���.`9Nz|:���c)�Z4�|����(->���R���)�q��R�LѲ��XJ�ok:~�u����r9N��9�5����B*��!�0$u
0lM~�d�]��|K1�d��,�U�t��(�˫��*|��ebj���v9黎��
h���U������e��]؏|��)Xo��(M����1q�VT;O�mƂ�q~�эv�s�0�;y߆0L)T<d�R+A�
���&
���Jlb|��
І4U�+mk3��0(*�����&�k��5���3`���Iӭ�}�'"a,f-�4�َFt��:�r��B�w0Q��jŸ�:�`V��1��:K�d�2�P�t�w-�y�_��@�rmHu2���8Lb��W��шqp�W�i��6
+�`~P�t��G!�f]E�{D�S½����DR�9�6[o[�0S����xP{�����T�&E��a��)Yn����E�7R��j�i�5�s�Qm�p1P��_5��	��|��/i���ku��ұ���tLf�>��S��5�[C)��X�OJ:�P��"2�te��+>��w�8�X"V�ʀx���u�IQ1���f)�3��1���H5iNnq7�}��9�!��P�>e����Rc���q0�aN�{Bq<�yM ��v��y!������}���HN�LRR��W�7�:�/-�q�m�¨�fun�&��
��Pc�Df
.P(*��D��_jѬ�T-GȊ8��-�u$|�_k*z$<r�`E�^Y��-�^��^h+t�X���D�ڝgB�h�o�!%�	bv����W9��OR+prH���&�^~>����w�(AL���V3v����5�nbF�5���-;��Icp�3��[iB�K."&��Jž��~�X
�RvH���1����0*���Hː�p���,6�0��+�Z�9p4�W
�^�C6���'�h~e�ފ��}!�r��K�#�P�
�lMt���1@E]����aW����W��!M�2��f���쎃�@	��`"�@��X	���^//���.q������(���oM�\�]�C�7���(�S�F"3Y���g�Yy�M��������Q<M/?jW�sq�TWF[�2�f(�Z�"g���A�e	B5����P��v��`*�İ����!�Y�A�q�nj)��������RZ��(�jޛ��g^z|�V�H���f��NW|킔2�Q�\�.�5��/���"��"�X�M∇����^�`[��T���%b�n׍�YɈ)
����'a̻�܏$�����0gI�vwm�����|�7k�;&Kd{#��c��/=��=e���N���:+b��0*i�{$\SoxԪ�,B�����[��g��,�`k���T��>� 6<��X0t��5��8�{G3}fJȚ�� j-���V��F�&d��eo��^Z���6���օ�7ڇ�a���t�U@��o��׈Ð!�����g����O�:���H=w��si�@��Ih��6T	��������=b1kg��iUE5 ���i�}O�2�B:��_'�Ф[���K�>��[a:���)�wg1\\��]�%&�dK�A^����ktQI�7gW�됙MB��&�U�i̲��:hC�7�%h�"��U�+�WB��0X�i���0�(\�n�4���3
����Ҡ=��M\AWz�b&*3���g�0���
kQ���P�fV�'j�E��`�[0k<�,�~�eJ�K1q:t��mj-�[�^�`�눀G�+"�E�������S�	N3IkQ"w���7sy�K�9��s�LW�#�i	|S4%�����
�$nu�pSe�[n����ޞ����P�+���Pie�<YP�E"�7��T<��=����h����@�n��k����_����j��")K�f��Gz��+Q�8��E;lvNy��(+�Q)��d��xz;�Tx�n[r�m�;��
	p�����g�d�\o^���ez��*�j�W=�*�_X���b�0v�`�OR��8�)~��EC�A��_]���.�#>�C��U�<�M`�4�%ң��5%��8x���Uy��o�&�8��3
���(��®�����0
a�3���Ǝ�5�J�s=kOYlv09�7
��K���P��Zti�d�S�D��Ÿ�@����(�9�U�>@:B��a�~��1Hp���b�(s��s>��&Z���
i��B	���09/g?�݌�M=� �Q����(iJ�O�Ak�h*�t�04"���K>���PB�&�U-��y�� ���K�s��f�/7A[=B����D����8��p��{��+@�ZZ��=� 2$}?r�9��|Am��̬�-��h9��}%7�g���r�}���59�u����冝�,x�m�y����pd=q�B���<�����gL�����(6��9�,<u��7�]beGV��a���#���	T�LD�UA�j�W��dQt+�/�HRQo�,��`�V�Y�c{sX�wU��j%�v���
�~~�.�.�9`�L�C0Jcm��4��\	��rS�یd-�J��pa5�,I�O�6�R��>�4V���E�e�z}��́e��B�!U��ܔs�1C����U�QG.+�Q֬�?q���yr��š�n�:��D�tڰ]m�L�Y�JB��-/�8n�"\UWO2��[�s࠾�R�[�J[�ԯT�L�e�0/x$���1�����Z�;�3�ء����
8�=�Jv��\o<�� �o�2兕���PZ�D@
�Ya-ucM%�Ra=� %@N����@���M�L�b�,T���ׄ�1����Sŗ��ް
��:�V��j�wGY�Ճ���}:Kt�
�������ھ 4�Q��k�>����MV�S��u5�͋�w�K�p-�Ƌ*�S�V:
�a%��)"��'�]j#�(c�="��>�Gr��C��,ҌS��9ø'���f"�\���@6q�Q�����m׬ȴj����*���� ��`��o39��i�����4jKŴ�R�	�+&&����e���)m�>���V���_^�ᦣ�S>&� ^ܙ3*VR�fB�*�}c��r�o潂^�ܴ�9��l��(|��"{0[�C��N� �;��Ēt���)�D�����mЯ�NY���@�E�m]n]9n�����B�U�[��V��i��95�٫��W���9M)�����ĺ��%�[%�����.2���pVɰ��8�ӌP�����Y����i����돾�9<&��,�ܭ��
iO�0qF��A1���e/��׬�ݪ�:u�*M�}k��of�7ݏ��r�^���QVΊ������ XKՊjd`>/�l��}n$x�0�
*�@g����i^} )����5P_7!N���͍#�`�a��i}(F�Ц�\:Fw���)�E���8>��V�Z8��0��S[��Nr��2n��:J�`Ht��č�	�����n�Y����:@�@���CQ��\ū��X[��]TҤ[�@a���qkņ[��
�B�%�1�)<�N��ʹ$�_.��(Z���\q,����G2��%�����UT����v�#T�4��D&�i+ǫ�YB�3:zf+��^L�7�u+'�&~`�B�e��=lHA����ˬ�p�N*�
~��l�� h�g���"p�ی�_���c��[����,{��`�4A/��_�*h�4C o��/�nN�
Q�\�H�rVJ���b�Ϸ�"�>_�/cG�b�9�E	L�*��1o��a��*J��X��rˁ.��"^�E�mV�V��.LS#_q/7�=�ip�2ƈ��,�z��U�g�ӼQTl��v5�)�[��}��|2a���ɜ$H�K��幍��*�
�U��Z4���9�����i�7S1�U]���.z����u:3��)We�����AT��8�U��[�4yINZ|�c5㙈�=�`<A��/�TU�A�
�T�R��t�a�u�m&X�e�a�9�q��
��{
̝([TU�Y�)ј�\*�ѣ$��Ӷ�-<ك��Mo�Y
R�q0��@a@S�	�.�V�5��pT(q�IJ�@��8��k<~��`�e�V�R���"����>�V�Ӄ���[n\8]��I��Mq���B�U�(�e��|@9WW�����Ĥ
_y�Z�����MY)�u�V�� X�}�DK3�K���\E@�ŭ�:}�pQÉSg/��""��Jc�kN^��ԅNV��a}+W�8az�Q�A����X6�-*�~p����Rq
�����7��)�����(���S;�"��.]\��V��ĺ�#�N�lj� �m�7�B�yf*U���Ss���W�U�a��31���V${!�̏2��ûIsy�ȢꪮS�V��e���"��.m�֢h�އ��-���X
�(}=}��U���.:�Ƃ��G���U��U���%��E�7��f�^z��x-9u"p�o�6��_�K�@E�O�p4��YL��U�[5����� o�n�����*�v�1p%{��m���.���E]�EU������=!�i�{BV�G]��+(������1�N��0JY�C��H����ӆ$�p�"��V.���%k�割��0{��r�|�p�
u\W\�D3X�3"�0���,�k���b�R&�gas���򨌫	�%/R9U�`���Ek;�ck����2���U�7�=���q�S���?�,%�{��x_\RD۞��?	��O��[n�?{ŃD�V
�nmax�((yZ�]��|ˊ�7 o�&/�7�%���J���0�@��ȎZ���&)�P�+�ڊ�Jё�D���l�80�V��—D�o�:�v�--�`j��zK�(V?�'��oI���ϧ��#ir]}���m^o� ��ȧQ��y���Vw���H*��n RѬ'��x���j]B�o�jU���1ֆX�0��KV|2�p������ὸkX���֬^�
^:B�S�+Y���Μ�m���TϝrJQ(�`�Û}�n�|�&s�Hm��v]B��]�N��pnw"�~�1窃=�Q�X���FWW[�Uu^��`"�`n���V;s��=ت�qS�G��ƀjlkȩ�	�b4�Ԅ��x%����f�9h9�,s�qr���+��������U��-���u�p�0pL6��ׁ�_QI����B�r5��ijR�(:'߉H�8�O�\���V�\ߠ���Q+��o�hDTY�W_k�-#��*�Y/��{y?~�Y+������-'��2@���-Ujֱ0�u�rQZ�����L�8��ؔ	j�����[��md��M��FG<@�.��s�������qDP�o�uN�Q�1�L�Sl��DYf]a�@��N���˅Ý	�C[�~��������m�M��S���3 ��j	�S��7�>i�yc)��|F1z���w�a��WqnF+c4�'#i|;�p��T��*t���]%�mO=~�����9��u]���;�p���n�%���T����7��6���8�"6���.�^7�<x�7x:u��Ub\K����ε�?0T��U�A�
�&��!:sU����^3�a*^٨�6�+�z���K���^Y<Q
o�~'#�
�yk~�e�tGM��S�%)���f�yuAI�,>����L����1h�Z(�e�����6L�G��}~��V�����L��,Z
�V�2�9�=\�A����Z�=e�^v�<[�=��@�;Eh^w^�F��K,��6���nj���B�K} F�`�ޒ��X�e
�~�J.�A�̛b�ٔ��"����T��e���Q�+���W@�\s��H]C���,0Y�O%�~�,eU�mQ�q��`E�`UƢ�8XߤMW&q()vs�R�8V��t�+Z3�d+8����s�(ن"b�*���ݗVzA��/WC8�I��z����z}�'
�P����i��x��b*�s�U-s������x�?PnP����?�>T�2�����b;X�k��@awmc򐰖o.�+>%
*���㯙U��!�j���(��~%U������#DP8��Z����Tbe(A�ĵ��UT�1j�h�-�s��+���������M���ȉ �w��˪��TBҲ��
�_^���@hź%eg�e��j�i�َ65����Q���
�\bPl�}���3��H�y�{�Y�O�m���?�fSY}ҿ+�ܠ)Nx��aT|���gu9�7n-%QJf�&r�������oV?|0���bP��n+��߬����y+S�8�
�«���G��Q���p���q^˥�"�G`��+Ȇ/����i)��ޛ��e=�jRWE&q�tQ����mQ\!�E�i�VUF&J��b������j-D+�\�30<�x��{7�9fp�|�TM�E���]k�"8��������W��셎﹌M,J��خ:���-��9�j��B�a.�
(��u�K%o�1�To��=��]��8�(��ѝ`�ɿ<{�+��ߺ�o(R�h���F�x{�P,^�w���Q����#�OU�>�#�eϹ0��{��e���b�t�r_�� rV�5���8|��f��j�0�$h��
L��_�{<�_߬���ۧHU��P�#W�Q1�ښg!����n���&A�����7&[��K����f+���h�=y:}��׭}�,�GۂPdj5Jy�5Pk�����n`q�s�+K�Vl�@+�&�N�H
S&F�h8�n6�|	E�b������hWu�y��Kןq.����&�V�� �uՎj�x8����Z�Klå�S�
Hk�s��3y��������kYE���꠆�:�0@�t����o��p�}��l*ܡ+mӷ�3,W��	��N1���i��NV��T
�,�W%�b��v<`,�������?(Wh-��*4A��3*(#ڋ�?��C��Y���IY��;Vo���C�+���#�����abr�98��2�:��6�(8�`�[3H�ŷ_DEҭ,x�~�ԧ-���ՁIh�k�Y��j^�fj�p���ؔ�oXX�z�2���!�(`������deзX��i�������5��=e�t���lrA�]8���5����廖l����/�:��@>��i��؍[���0
\�8�G=w��e�-������㴧Ek4D �.OԨ@���f��T_�5��ƿs4�r�`���9ˊ�ef���l�W4Ƶ\��Պ�J�T�=�
��i{��z��J�&�i��-�L;�<���~%���;<W��H�S��5Z�P(��U�e�T0�9W	�v^�%Rü��Â��>�
`��4�.�L�uw���6k-	�1��d�s�3��ǔ�����7�T��8T�lUF�8�Z>3qaM�]|�p�0+���v���!^j#��ʓ60����Z�fU�T��'��:��_�KF��fQ�tΠ]�C����^�UY��գ���A1]
99L���p���j�m��^5P��p�v5Xk_q-�ҕ�{4�T�s������U[[sy�(X�.\N�Wbb�%�ob�ɋ�����m�ֺ�+9R��Q��FK�L}��-W�[e�’�]>�����_�͝���X4�5K����䓮��r�V�Uxy��g���>�B<�[WC�����@k�#�i��6g���7��E6[��$4,�]K'?��e��J�ө�����������5{���Q�R.�}�%�.ˣ����`xO�j�G�pʲ�g��C��
����(V�c:��@��]�{V}%��v�8��0���W�xO4�T�jk��K�s
���oWV����
�X�W�.���y*��k�(+Ci|���%���D��ZW�)�]�-��si��
�c.e%��H%"]��B�O�w��da��_�`�9ĥM�s8�2Wy]��e�9��%+ܢT"�\\���Z���u�J���2zK��C��(88ϼJ�.o���cH-c��-��31Y�<�}f� C5��n��~��y�w
�ٗ�=^|�	
aQ>���n����ʵ�LF1n��Δ�'�%܇p��� {w����n�}��.�*�c,��w1�c)ʢM��~���%��K���a��ۊq��R���0�Hz��`�?{—@i.�:���1� ����jVڞ���P��_3ȅ��	�3a(�j���+ױp%+�ݳ��RNX�
^1����x3��EV�k>z��#��Zc�F�/oO��kPRV�g�`QhK���#U�003�Mڲi���Y��"����_?�L���E$��pW=g.���	`���U/���ؾ��U/a߮��sU����!��qR�0
2�ޓ�=f_2H^�P�ٚ`�vq���_�����E
���
��hk��f�x�$h���46�OT(�V�}����~�SWN�J�zҘ"���Qrw3)ك���	���:�#y⭿��Vb��q��1t�Ox�(�(��ƥ�nn�[�w7�U\B�D7�����ʙ�Ez�
��Ǽ�VkRƒ&�q�1n�+��"ʷ~�"�x���،`)�������Ͻ�ش��bY
/'��AF��_h�8��ʎ����_�����b՗L��gx��7F[S�[�.47\}�%`%w��&vt���z�9ljiq�)v�����oz|�$`�<�~euN���w�H(�}I���,r+Ns�b�
g�z�R]M��B����*Qt��o0�ݖ8���[�п7��v�> H9R�U��e��1�d��
�@�o��M��M�{�y㏑�)��+X��X9W~�0�Pv�-�rs.���,�i?2���s0o�Ͻ�QB,�(83UG��P,y����9�3]��������j�iY��U��W�1
Ҿ�!�hV�T|~b!UZ�st䡺���� ��{�lX-l�[���P�"��v�K�B�k�X�i����ä����zStM�����{�duT\���h�lɟ�^V�]�7K=e�r��/��8�H�+N��[�ۧ��ܯ������B��%O��
m3]��U�F���Yzqt+���^>�P���/#���&hZ�'�Y�m�ߙYj��MA�լ[�1�E�q��+����@V0��E�+T�8{c� �@�����]B���_���oym��Z��Ҵ5ѳ&���ak�g�>�I�'9W�a:��/���Ӣc&Q�����u�|��t�_i`hr�{�b��X�����R��^�ҽ�"�[�
|��
�E��Ez�W��FcCW�_�2� ool5�p��k��H*/�`5[g����?�e��a�O�N*��v��֋z�2d���N�lG8��B������E�.��3��\@QAid\)�.���Kh�0Q.��c�ʁ�^�??�Ӫ��
I���ӴF�V.�aҏIi��»c�%��s����L�qƼ�6��m+j&������+y�xCt@kc�(�!)_\��9�������� -h;}`����X6Z�G�������λ#ضݦy~���_w���E9\���b�G\C��JH�6W�LF�?�ŭWL��F���!��
���~��M���/<t�Ά9u��x�:��0ʦ��9�.3Anx�p�6��=�;F��y��F��~�=Z�(�C�9,�8s��D`.��3�~��`EZ���Tq�^ٖ �[�eC}Z#�M1J�����,�Z*�-�����@RU�Uˁp�"0+��?1&R���uA�k���c��7�1^�76�z�u��+�uu�e����ĸ��<ך�e�YC㘂#de�N8�4���_�2��ǧ�,1E����Z݇�^[�A�Mt�aR�eo�Ř;A��	�����߽�2U���w��Ǽ[U�'	�qA�o	w�RpǪ�Mb%�`L��wڹe5|��\�/_����#�)PG�$T��c
�<ۋ>Ԩ�p���Q��蕿.t�{F�.�K�h%/x��}���sZQ|�R���K
�C.v��Ϭf�|F(St٣�S�+R�kX�{��(P��UҦ�iM��)[-֬��&8x���/P҆���fOv!�c�T�e�Z��u_{�Z0W���(�ot|{Ʒ�	[���{�����c�i[d�Wz�S
�xW�2���_�0&ثY�?�&h��WDhris����P�Q��
��׼�K���e�Ky�r�qw| �K�>Dh�j��}�������ĭ�!�{�
S��_��ߞ���	v��iV���'���pٗx�R�A]�E�5��L��Pp��L+��ty�'��_̠�!*���q`����-E�rv+޻T`T�LJienX�v�X�*�;l=���b�^��raJ��J]\�=�0]���곁,��b�8��Z�W�&�Q���J9*κ��heYÃ3��C��hC�v3��̼Qoi�F�q�߇�V�Cx���+j�Q����S=����"�E���[��ĩ
q��3^q(�*?{˗�L� �*�}���fB��#-�Tzo0�0��XW8�ۍhm���=����`�N�g�]7�UO��9Maf}��@�Vů/�;��YZՅq����:�A�*�jn|�Y�vj,,Bd5vz\��居^DD����r�b�`xU�c(�΂�S��w[DK�]�t���o�/Y���Ј[=K���(���VS��
�i)C$,�Y^b�-4���� w�W.��̎�;y����n�;�eAkB�TJ��u}��=N5n����KS��>���������_]��5�sd0:_��vQ-b�?�yg
�Q�����
k��&#�-�9�I3�<G�Vb(�A繫l-T��?i�	��P%+_��
�%�3wxӿ��V�r<w��K���f�an�����r��c[`]���+ld�.�4vM�����8��oZ��
R4&N���[N(U�1�u�U%��4&���3��V�`�ba�!��$c����5`�y��W�
�6[�Qj݃�����(({ϱ���Y
,�~��0k+��)�%軭u���H�Y��a^�[o�����Xj�vB/���N�rx>����B��+��F�YZpyb��������S�.\>�3 d���	]��?1�i,��=;L���m��u���M�͞C�Wh��'7~��(2�9���4���<�Z���[�
<�?�h�9�@��	-�����T4#<+�K�u���*�'l�}fUCt���M_��4`��ڡe� %��u����7�\tJ~=PU���,�k��T�lV�^�(8��ie��SB���l҅/Ouzj`�J�.9���������
z�<����*^����f����"����Tlm/�K
��z��ʥ�&�[u��g]�\��'�zC}�Jk1���u��]�����PK
!<��ÿEchrome/browser/skin/classic/browser/preferences/in-content/dialog.css/* - This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this file,
   - You can obtain one at http://mozilla.org/MPL/2.0/. */


dialog,
window,
.prefpane,
.windowDialog {
  -moz-appearance: none;
  background-color: #fbfbfb;
  color: #424e5a;
  margin: 0;
  padding: 0;
}

.contentPane {
  margin: 0;
}

tabbox {
  /* override the rule in certManager.xul */
  margin: 0 0 5px !important;
}

tabpanels {
  font-size: 1em;
}

tabs,
label,
description,
#useDocumentColors {
  margin-right: 4px;
  margin-left: 4px;
}

tree:not(#rejectsTree) {
  min-height: 15em;
}

.actionButtons {
  margin: 3px 0 0 !important;
}

caption {
  padding-inline-start: 0;
}

groupbox {
  margin-top: 0;
  margin-right: 4px;
  margin-left: 4px;
  padding-top: 0;
  padding-bottom: 5px;
}

.prefpane .groupbox-body {
  padding: 0 0 5px;
}

groupbox description {
  margin-right: 0;
  margin-left: 0;
}

menulist label {
  font-weight: unset;
}

:root > * {
  font-size: 1.05em;
}

PK
!<i�|���Gchrome/browser/skin/classic/browser/preferences/in-content/face-sad.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 20 20">
  <path fill="#00D3E7" d="M9.94.8a9.01 9.01 0 0 0-9 9.42 1.07 1.07 0 0 0 .13 2.12h.21A9.01 9.01 0 1 0 9.94.8"/>
  <path fill="#00A1C1" d="M18.93 10.21v-.4c0-2.4-.93-4.6-2.47-6.2A9.26 9.26 0 0 1 3.72 16.34a9 9 0 0 0 14.85-4l.21-.01c.6 0 1.07-.48 1.07-1.07.01-.54-.4-.99-.92-1.06"/>
  <path fill="#323B59" d="M5.47 6.55a.99.99 0 0 0-.99.99v2.22a.99.99 0 1 0 1.98 0V7.54a.99.99 0 0 0-1-.99m8.98 0a.99.99 0 0 0-1 .99v2.22a.99.99 0 1 0 1.98 0V7.54a.99.99 0 0 0-.98-.99m-4.5 5.47a2.27 2.27 0 0 0-2.34 2.13c-.02.23.09.45.27.58.19.14.43.17.64.09 0 0 .47-.17 1.34-.17.91 0 1.55.19 1.56.19a.69.69 0 0 0 .61-.11.67.67 0 0 0 .26-.58 2.27 2.27 0 0 0-2.34-2.13"/>
  <path fill="#00A1C1" d="M4.59 7.02c-.36 0-.72-.11-1.03-.32a.318.318 0 1 1 .35-.53 1.25 1.25 0 0 0 1.8-.47.313.313 0 1 1 .56.28 1.88 1.88 0 0 1-1.68 1.04m10.47 0a1.88 1.88 0 0 1-1.68-1.04.313.313 0 1 1 .56-.28 1.24 1.24 0 0 0 1.8.48.32.32 0 0 1 .35.53c-.3.2-.66.3-1.03.3"/>
</svg>
PK
!<D�1Ichrome/browser/skin/classic/browser/preferences/in-content/face-smile.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 20 20">
  <path fill="#FFBF49" d="M9.95.8A9.03 9.03 0 0 0 .92 9.83l.01.4a1.07 1.07 0 0 0 .14 2.13h.21A9.04 9.04 0 1 0 9.95.8"/>
  <path fill="#323B59" d="M14.44 6.7c.55 0 1 .44 1 .99V9.9c0 .55-.45.12-1 .12s-.99.42-.99-.12V7.69c0-.55.45-1 1-1"/>
  <path fill="#FFA500" d="M13.54 10.46a.31.31 0 0 1-.14-.59 2.36 2.36 0 0 1 2.08-.03.3.3 0 0 1 .14.41.3.3 0 0 1-.41.15 1.75 1.75 0 0 0-1.53.02.28.28 0 0 1-.14.04"/>
  <path fill="#323B59" d="M5.46 6.7c.55 0 .99.44.99.99V9.9c0 .55-.44.12-.99.12s-.99.42-.99-.12V7.69c0-.55.45-1 .99-1"/>
  <path fill="#FFA500" d="M4.56 10.46a.31.31 0 0 1-.14-.59 2.36 2.36 0 0 1 2.07-.03.3.3 0 0 1 .15.41.3.3 0 0 1-.41.15 1.75 1.75 0 0 0-1.53.02.3.3 0 0 1-.14.04"/>
  <path fill="#323B59" d="M16.06 12.42l-.62-.05c-.06.02-.21.06-.45.1-.3.04-.73.15-1.25.2-.51.04-1.12.14-1.76.17-.65.04-1.33.05-2.03.06-.68-.01-1.37-.02-2.02-.07-.65-.03-1.25-.12-1.76-.17-.52-.04-.95-.15-1.25-.2l-.45-.09a30.35 30.35 0 0 0-.7.07.17.17 0 0 0-.04.23l.1.17a7.67 7.67 0 0 0 1.54 1.75c.5.45 1.01.78 1.53 1.06l.41.19c.78-.55 1.68.2 2.64.2.97 0 1.87-.74 2.64-.2.14-.05.28-.1.42-.18a8.34 8.34 0 0 0 3.06-2.82l.1-.18c.02-.02.03-.04.03-.07a.16.16 0 0 0-.14-.17"/>
  <path fill="#FFF" d="M6.17 12.66c.51.05 1.12.14 1.76.17.65.05 1.33.06 2.02.07.7-.01 1.38-.02 2.03-.06.65-.04 1.25-.13 1.76-.17.52-.05.95-.15 1.25-.2.24-.04.4-.08.45-.1l-.9-.05-1.53-.06c-1.02-.03-2.04-.03-3.06-.05-1.02.02-2.04.02-3.05.05l-1.53.06-.9.05c.05.02.21.06.45.1.3.04.73.15 1.25.2"/>
  <path fill="#FFA500" d="M18.96 10.23v-.4c0-2.42-.94-4.6-2.48-6.22A9.27 9.27 0 0 1 3.72 16.37a9.02 9.02 0 0 0 14.87-4l.22-.01c.6 0 1.08-.48 1.08-1.07 0-.55-.4-1-.93-1.06"/>
  <path fill="#FF7664" d="M12.58 15.85h.01c-.77-.55-1.68-.37-2.64-.25-.96-.12-1.87-.3-2.64.25.87.39 1.73.57 2.6.58H10c.86 0 1.73-.2 2.6-.58h-.02"/>
</svg>
PK
!<)M���Ichrome/browser/skin/classic/browser/preferences/in-content/fxa-avatar.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="80" height="80">
  <circle r="40" cx="40" cy="40" fill="#E1E1E6"/>
  <path fill="#F5F5F7" d="M70.5 41.2l.02-1.36A30.5 30.5 0 0 0 39.97 9.41 30.5 30.5 0 0 0 9.41 39.84l.02 1.36a3.62 3.62 0 0 0-3.15 3.58c0 2 1.62 3.6 3.63 3.6h.72a30.46 30.46 0 0 0 8.3 13.51l-.04-.02a30.54 30.54 0 0 0 19.72 8.35l.24.01.43.01.51.01h.86l.41-.02h.27c.16-.02.33-.02.5-.03l.12-.01A30.55 30.55 0 0 0 69.3 48.37h.68a3.61 3.61 0 0 0 .51-7.18"/>
  <path fill="#C8C8CC" d="M58.53 39.81v-7.18a3.35 3.35 0 1 0-6.7 0v7.27l-.17.08a1.05 1.05 0 0 0 .47 1.98c.17 0 .32-.04.47-.12a5.94 5.94 0 0 1 5.18-.07c.53.25 1.15.03 1.4-.5.25-.52.03-1.14-.5-1.39l-.15-.07zm-30.41 0v-7.18a3.35 3.35 0 1 0-6.7 0v7.27l-.17.08a1.05 1.05 0 0 0 .47 1.98 1 1 0 0 0 .47-.12 5.94 5.94 0 0 1 5.18-.07c.53.25 1.15.03 1.4-.5.25-.52.03-1.14-.5-1.39l-.15-.07zM48.85 60.1l.07.04c.47-.2.93-.38 1.4-.62A28.29 28.29 0 0 0 60.68 50l.37-.58a.58.58 0 0 0 .08-.26.53.53 0 0 0-.47-.57c-.7-.07-1.4-.11-2.1-.15-.2.05-.72.18-1.53.33-1.02.14-2.47.5-4.22.66-1.74.15-3.8.47-5.97.57-2.19.15-4.51.18-6.85.2-2.33-.04-4.67-.07-6.86-.22-2.19-.1-4.22-.41-5.97-.56-1.74-.17-3.21-.51-4.22-.66-.8-.16-1.35-.28-1.53-.33-.72.04-1.42.1-2.14.15a.47.47 0 0 0-.25.1.56.56 0 0 0-.13.75l.38.57a30.15 30.15 0 0 0 5.18 5.91 27.28 27.28 0 0 0 5.18 3.57c.47.25.94.43 1.41.64a3.84 3.84 0 0 1-.01.02l.02.02-.02.01a21.8 21.8 0 0 0 8.79 1.93l.14.02h.03l.13-.02a21.8 21.8 0 0 0 8.8-1.93l-.03-.01c.02 0 .02 0 .03-.02a6.87 6.87 0 0 0-.07-.04z"/>
</svg>
PK
!<�+�!��Fchrome/browser/skin/classic/browser/preferences/in-content/general.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 24 24">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M7 11.5a4.5 4.5 0 1 1 9 0 4.5 4.5 0 0 1-9 0M21.48 10h-2.65a7.45 7.45 0 0 0-1.1-2.64l1.88-1.87a1.5 1.5 0 1 0-2.12-2.12l-1.87 1.87c-.8-.52-1.69-.9-2.65-1.1l.01-.15V1.5a1.5 1.5 0 1 0-3 0V4l.02.14c-.96.2-1.86.58-2.65 1.1L5.5 3.37a1.5 1.5 0 1 0-2.12 2.12l1.87 1.87c-.52.79-.9 1.68-1.1 2.63H1.5a1.5 1.5 0 1 0 0 3h2.64c.2.96.58 1.85 1.1 2.64L3.37 17.5a1.5 1.5 0 1 0 2.12 2.12l1.87-1.87c.79.52 1.68.9 2.63 1.1v2.64a1.5 1.5 0 1 0 3 0v-2.65c.96-.2 1.85-.57 2.64-1.1l1.87 1.88a1.5 1.5 0 0 0 2.12 0 1.5 1.5 0 0 0 0-2.12l-1.87-1.87c.52-.8.9-1.68 1.1-2.64h2.64a1.5 1.5 0 1 0 0-3"/>
</svg>
PK
!<)i�PFFKchrome/browser/skin/classic/browser/preferences/in-content/logo-android.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 20 20">
  <path fill="context-fill" d="M9.24 16.03v2.73c0 .7-.56 1.24-1.25 1.24-.7 0-1.26-.55-1.26-1.24v-2.73h-.9c-.75 0-1.35-.59-1.35-1.33v-8h11.2v8c0 .74-.6 1.33-1.36 1.33h-.9v2.73c0 .7-.55 1.24-1.25 1.24s-1.26-.55-1.26-1.24v-2.73H9.24zM2.76 6.5c.7 0 1.25.55 1.25 1.24v5.15c0 .7-.56 1.24-1.25 1.24-.7 0-1.26-.55-1.26-1.24V7.73c0-.69.56-1.24 1.26-1.24zm14.64 0c.7 0 1.25.55 1.25 1.24v5.15c0 .7-.55 1.24-1.25 1.24s-1.26-.55-1.26-1.24V7.73c0-.69.56-1.24 1.26-1.24zM6.6 0c.06 0 .12.03.16.1l.9 1.58a6.04 6.04 0 0 1 4.84 0L13.4.1a.18.18 0 0 1 .24-.07c.09.05.12.15.07.24l-.89 1.58a5.04 5.04 0 0 1 2.86 4.43H4.48c0-1.9 1.15-3.56 2.85-4.43L6.45.26a.17.17 0 0 1 .07-.24A.18.18 0 0 1 6.6 0zm.9 3.33c-.26 0-.47.2-.47.46a.47.47 0 0 0 .93 0 .47.47 0 0 0-.47-.46zm5.16 0c-.25 0-.47.2-.47.46a.47.47 0 0 0 .94 0 .47.47 0 0 0-.47-.46z"/>
</svg>
PK
!<7�sS��Gchrome/browser/skin/classic/browser/preferences/in-content/logo-ios.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 20 20">
  <path fill="context-fill" d="M16.84 15.03c-.27.63-.59 1.2-.96 1.74-.51.72-.93 1.23-1.25 1.5-.5.46-1.03.7-1.6.71-.41 0-.9-.12-1.48-.35a4.25 4.25 0 0 0-1.6-.36c-.5 0-1.05.12-1.63.36A4.4 4.4 0 0 1 6.9 19c-.55.02-1.1-.22-1.64-.73-.35-.3-.78-.82-1.3-1.56-.56-.78-1.02-1.7-1.38-2.73A10.03 10.03 0 0 1 2 10.7c0-1.2.26-2.25.78-3.12a4.6 4.6 0 0 1 3.86-2.28c.43 0 1 .13 1.71.4.71.26 1.16.4 1.36.4.15 0 .66-.16 1.51-.47.8-.3 1.5-.41 2.05-.37 1.51.13 2.65.72 3.4 1.8a3.8 3.8 0 0 0-2 3.44 4.1 4.1 0 0 0 2.5 3.68c-.1.3-.21.57-.33.84zM13.37 1.36c0 .9-.33 1.74-.98 2.52-.8.92-1.75 1.46-2.79 1.37a3.95 3.95 0 0 1 1.02-2.89c.34-.38.76-.7 1.28-.95.51-.25 1-.39 1.45-.41.02.12.02.24.02.36z"/>
</svg>
PK
!<B�ffLchrome/browser/skin/classic/browser/preferences/in-content/no-search-bar.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="631" height="36" viewBox="0 0 631 36"><style>.addressBarOutline{stroke:#b1b1b3;stroke-linejoin:round;stroke-width:1px}.addressBarIcons{fill:#b1b1b3;fill-rule:evenodd}</style><rect class="addressBarOutline" fill="#fff" x=".5" y=".5" width="630" height="35" rx="4" ry="4"/><rect class="addressBarOutline" fill="none" x="110.5" y="6.5" width="434" height="23" rx="4" ry="4"/><path class="addressBarIcons" d="M604 .5h.5v34h-.5V.5zM126.41 22l-3.09-3.085a3.881 3.881 0 0 0-.64-5.2 3.855 3.855 0 0 0-5.4 5.462 3.958 3.958 0 0 0 5.21.643l3.08 3.085a.622.622 0 0 0 .9 0 .677.677 0 0 0-.06-.9zm-6.23-2.764a2.571 2.571 0 1 1 2.57-2.57 2.652 2.652 0 0 1-2.57 2.574zM620.75 17.25h-7.5a.75.75 0 0 0 0 1.5h7.5a.75.75 0 0 0 0-1.5m0 3.75h-7.5a.75.75 0 0 0 0 1.5h7.5a.75.75 0 0 0 0-1.5m0-7.5h-7.5a.75.75 0 0 0 0 1.5h7.5a.75.75 0 0 0 0-1.5M585.77 13.5a.75.75 0 0 0-.52 1.28l3.18 3.22-3.18 3.22a.746.746 0 1 0 1.05 1.06l3.7-3.75a.774.774 0 0 0 0-1.06l-3.7-3.75a.754.754 0 0 0-.53-.22m4.44 0a.715.715 0 0 0-.52.22.754.754 0 0 0 0 1.06l3.17 3.22-3.17 3.22a.754.754 0 0 0 0 1.06.715.715 0 0 0 .52.22.754.754 0 0 0 .53-.22l3.69-3.75a.754.754 0 0 0 0-1.06l-3.69-3.75a.754.754 0 0 0-.53-.22M567.37 15.75h1.5a.375.375 0 1 0 0-.75h-1.5a.375.375 0 0 0 0 .75zm2.63-3h-9a1.5 1.5 0 0 0-1.5 1.5v7.5a1.5 1.5 0 0 0 1.5 1.5h9a1.5 1.5 0 0 0 1.5-1.5v-7.5a1.5 1.5 0 0 0-1.5-1.5zm-4.5 9H561v-7.5h4.5v7.5zm4.5 0h-3.75v-7.5H570v7.5zm-2.63-4.5h1.5a.375.375 0 1 0 0-.75h-1.5a.375.375 0 0 0 0 .75zm0 1.5h.75a.375.375 0 1 0 0-.75h-.75a.375.375 0 0 0 0 .75zM89.83 21.25a.375.375 0 1 1 .37-.375.356.356 0 0 1-.37.375m-2.6 1.5a.7.7 0 0 1-.742-.75v-4.95l2.961-3 2.97 3V22a.706.706 0 0 1-.74.75h-.74V19a.706.706 0 0 0-.74-.75h-1.49a.706.706 0 0 0-.74.75v3.75h-.739m2.219-10.5a.7.7 0 0 0-.51.225l-5.193 5.25a.738.738 0 1 0 1.037 1.05l.223-.225v4.2a1.5 1.5 0 0 0 1.482 1.5h5.931a1.491 1.491 0 0 0 1.48-1.5v-4.2l.22.225a.678.678 0 0 0 .52.225.663.663 0 0 0 .52-.225.725.725 0 0 0 0-1.05l-5.19-5.25a.709.709 0 0 0-.52-.225M70.375 13a.749.749 0 0 0-.75.75v1.808a5.245 5.245 0 1 0-.788 6.4.75.75 0 0 0-1.061-1.061 3.755 3.755 0 1 1 .776-4.151h-1.927a.75.75 0 0 0 0 1.5h3.75a.749.749 0 0 0 .75-.75v-3.75a.749.749 0 0 0-.75-.75M36.217 17.292h8.649l-3.206-3.2a.738.738 0 0 1 1.044-1.043l4.474 4.47a.72.72 0 0 1 0 1.043l-4.474 4.47a.72.72 0 0 1-1.044 0 .72.72 0 0 1 0-1.043l3.206-3.2h-8.649a.749.749 0 1 1 0-1.497z"/><circle class="addressBarOutline" fill="#f9f9fa" cx="18.5" cy="18" r="12"/><path class="addressBarIcons" d="M23.783 17.292h-8.649l3.206-3.2a.738.738 0 0 0-1.044-1.043l-4.474 4.47a.72.72 0 0 0 0 1.043l4.474 4.47a.72.72 0 0 0 1.044 0 .72.72 0 0 0 0-1.043l-3.206-3.2h8.649a.749.749 0 1 0 0-1.497z"/></svg>
PK
!<��ߋM+M+Pchrome/browser/skin/classic/browser/preferences/in-content/no-search-results.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 380 293">
  <path fill="#FFFFFF" d="M2.72 39.24h40.9s-12.8-28.6 14.37-32.3c24.22-3.3 33.8 21.6 33.8 21.6s2.87-14.36 17.29-11.61c14.2 2.7 24.7 25.56 24.7 25.56h35.6"/>
  <path fill="#E1E1E6" d="M170.09 35.84h-35.62c-.4 0-.75-.22-.93-.57-.1-.2-10.43-20.33-23.96-22.9-13.2-2.53-15.95 10.24-16.06 10.79-.1.45-.48.8-.94.83-.47.05-.9-.23-1.07-.66a41.04 41.04 0 0 0-7.65-11.75c-6.94-7.41-15.36-10.5-25.04-9.2-7.04.97-11.89 3.74-14.4 8.26-5.06 9.1.8 22.45.85 22.58a1.06 1.06 0 0 1-.96 1.48H3.41a1.05 1.05 0 0 1 0-2.1h39.35C41.3 28.68 38 17.86 42.58 9.63 45.43 4.5 50.8 1.36 58.54.3c20.08-2.73 30.5 13.43 33.77 19.78 1.72-4.25 6.43-11.93 17.66-9.79 13.37 2.55 23.28 19.96 25.14 23.44h34.98a1.05 1.05 0 0 1 0 2.1"/>
  <path fill="#FFFFFF" d="M171.26 43.2H2.71a2.1 2.1 0 1 1 0-4.2h168.55a2.1 2.1 0 0 1 0 4.2"/>
  <path fill="#E1E1E6" d="M338.53 124.92H211.12a2.1 2.1 0 1 1 0-4.2h127.41a2.1 2.1 0 0 1 0 4.2M121.81 99.33H55.02a2.1 2.1 0 1 1 0-4.2h66.8a2.1 2.1 0 0 1 0 4.2m-32.54-9.76H70.34a1.05 1.05 0 0 1 0-2.1h18.94a1.05 1.05 0 0 1 0 2.1m47.26 95.55H2.1a2.1 2.1 0 1 1 0-4.2h134.44a2.1 2.1 0 0 1 0 4.2m-21.96-9.57H50.03a1.05 1.05 0 0 1 0-2.1h64.55a1.05 1.05 0 0 1 0 2.1m206.66-61.59h-56.42a1.05 1.05 0 0 1 0-2.1h56.42a1.05 1.05 0 0 1 0 2.1m50.17 19.83H179.66a1.05 1.05 0 0 1 0-2.1H371.4a1.05 1.05 0 0 1 0 2.1"/>
  <path fill="#FFFFFF" d="M283.11 65.18h22.75s-7.12-15.9 7.99-17.96c13.48-1.84 18.8 12.01 18.8 12.01s1.6-7.99 9.61-6.46c7.9 1.5 13.74 14.21 13.74 14.21h19.8"/>
  <path fill="#E1E1E6" d="M306.56 62.73h-22.74a1.05 1.05 0 1 1 0-2.1h21.21c-.88-2.61-2.26-8.18.16-12.53 1.66-2.99 4.76-4.81 9.22-5.42 10.6-1.44 16.48 6.34 18.78 10.37 1.42-2.86 4.5-5.84 9.97-4.8 7.32 1.39 12.8 9.82 14.14 12.09h19.2a1.05 1.05 0 0 1 0 2.1h-19.8c-.39 0-.74-.2-.92-.55-.06-.1-5.7-10.19-13.01-11.58-6.93-1.33-8.33 5.35-8.4 5.63a1.05 1.05 0 0 1-2 .17c-.2-.53-5.2-13.06-17.68-11.35-3.77.52-6.35 1.98-7.67 4.36-2.7 4.86.46 12.06.5 12.14.14.32.11.7-.08 1-.2.3-.53.47-.88.47"/>
  <path fill="#FFFFFF" d="M377.27 69.19h-93.74a2.1 2.1 0 1 1 0-4.2h93.74a2.1 2.1 0 1 1 0 4.2"/>
  <path fill="#E1E1E6" d="M117.87 251.41H9.4a2.1 2.1 0 1 1 0-4.2h108.47a2.1 2.1 0 0 1 0 4.2m242.44 0H241.8a2.1 2.1 0 1 1 0-4.2h118.5a2.1 2.1 0 0 1 0 4.2m-232.88-32.15a4.2 4.2 0 0 1-4.16-3.62l-4.55-32.19a4.2 4.2 0 0 1 8.32-1.17l4.55 32.19a4.2 4.2 0 0 1-4.16 4.79"/>
  <path fill="#E1E1E6" d="M125.24 205.61c-.47-.94-10.5-16.8-13.65-16.8-3.15 0-11.55 7-11.55 8.05 0 1.05 2.45 5.25 8.05 3.85l5.6-1.4s8.05 15.05 9.8 17.15c1.75 2.1 8.4 2.45 1.75-10.85"/>
  <path fill="#0A84FF" d="M105.66 197.3c-.4 0-.8-.11-1.17-.35l-5.24-3.5a2.1 2.1 0 0 1 2.33-3.5l5.25 3.5a2.1 2.1 0 0 1-1.17 3.85"/>
  <path fill="#5CE6E6" d="M122.9 182l5.64 1.3"/>
  <path fill="#E1E1E6" d="M128.54 187.5c-.32 0-.64-.03-.96-.1l-5.64-1.32a4.2 4.2 0 0 1 1.9-8.18l5.65 1.31a4.2 4.2 0 0 1-.95 8.3m106.4 31.75a4.2 4.2 0 0 1-4.17-4.7l3.7-30.92a4.2 4.2 0 1 1 8.33 1l-3.7 30.91a4.2 4.2 0 0 1-4.16 3.7"/>
  <path fill="#E1E1E6" d="M236.5 205.61c.48-.94 10.5-16.8 13.65-16.8 3.15 0 11.55 7 11.55 8.05 0 1.05-2.45 5.25-8.05 3.85l-5.6-1.4s-8.04 15.05-9.8 17.15c-1.74 2.1-8.4 2.45-1.74-10.85"/>
  <path fill="#E1E1E6" d="M257.07 198.18a2.1 2.1 0 0 1-.35-4.17l6.12-1.05a2.1 2.1 0 1 1 .7 4.14l-6.11 1.04c-.12.03-.24.04-.36.04"/>
  <path fill="#E1E1E6" d="M253.75 196.08a2.1 2.1 0 0 1-.73-4.07l6.65-2.45a2.1 2.1 0 0 1 1.45 3.94l-6.64 2.45a2.1 2.1 0 0 1-.73.13m-21.1-6.57a4.2 4.2 0 0 1-.96-8.29l5.64-1.31a4.2 4.2 0 0 1 1.9 8.18l-5.63 1.31c-.32.07-.64.1-.96.1m-82.18-33.66c-3.22 0-6.44-1.23-8.9-3.7l-19.6-19.58a12.6 12.6 0 1 1 17.82-17.82l19.6 19.6a12.6 12.6 0 0 1-8.92 21.5m59.48.7a12.6 12.6 0 0 1-8.9-21.5l19.58-19.6a12.6 12.6 0 0 1 17.82 17.81l-19.6 19.6a12.56 12.56 0 0 1-8.9 3.69"/>
  <path fill="#EDEDF2" d="M121.36 283.07c0-5.1 27.22-9.24 60.8-9.24s60.8 4.13 60.8 9.24c0 5.1-27.22 9.23-60.8 9.23s-60.8-4.13-60.8-9.23"/>
  <path fill="#E1E1E6" d="M130.06 129.95c-6.4 4.08-4.99 121.7 8.65 130.86 9.37 6.3 75.64 8.4 85.73-1.4 2.97-2.88 17.29-74.88 10.08-125.96-1.88-13.34-81.4-18.2-104.46-3.5"/>
  <path fill="#E1E1E6" d="M132.23 229.32s2.1 25.64 6.3 37.6c4.2 11.96 9.1 17.95 11.2 16.24 2.1-1.71 0-39.31 0-39.31l-17.5-14.53zm97.51 0s-2.1 25.64-6.3 37.6c-4.2 11.96-9.1 17.95-11.2 16.24-2.1-1.71 0-39.31 0-39.31l17.5-14.53zM138.71 120.5s26.24-7.87 33.59-8.92c7.35-1.05 23.1-8.92 23.1-8.92l-3.68 8.4 8.92-2.63v4.72l21.52 5.78-4.72 6.3h-69.81l-8.92-4.73z"/>
  <path fill="#F5F5F7" d="M142.82 129.42c-15.06 9.98-5.7 104.71 3.93 116.52 15.84 19.42 63.87 17.85 73.07-3.67 1.52-3.57 15.91-71.38 5-108.65-3.58-12.28-45.7-28.25-82-4.2"/>
  <path fill="#7A7B7E" d="M149.28 141.48a2.27 2.27 0 0 1 2.27 2.27v5.1a2.27 2.27 0 0 1-4.55 0v-5.1a2.26 2.26 0 0 1 2.28-2.27m56.4 0a2.27 2.27 0 0 1 2.26 2.27v5.1a2.27 2.27 0 0 1-4.54 0v-5.1a2.27 2.27 0 0 1 2.27-2.27"/>
  <path fill="#C8C8CC" d="M149.27 161.44a9.75 9.75 0 0 1-7.88-4.1 2.1 2.1 0 0 1 3.51-2.3 5.5 5.5 0 0 0 4.37 2.2 5.52 5.52 0 0 0 4.37-2.2 2.1 2.1 0 0 1 3.52 2.28c-.1.18-2.72 4.12-7.89 4.12m56.4 0a9.75 9.75 0 0 1-7.88-4.1 2.1 2.1 0 0 1 3.51-2.3 5.5 5.5 0 0 0 4.37 2.2 5.52 5.52 0 0 0 4.37-2.2 2.1 2.1 0 0 1 3.52 2.28c-.11.18-2.72 4.12-7.89 4.12m-26 10.75a2.1 2.1 0 0 1-.6-4.11c.18-.05 3.7-1.18 4.6-4.8.7-2.76-.1-6.02-.11-6.05a2.1 2.1 0 0 1 3.95-1.37c.75 1.66 2.3 4.32 3.41 5.03 2 1.28 3.68.7 3.76.68a2.12 2.12 0 0 1 2.69 1.23c.4 1.07-.1 2.26-1.17 2.68a8.71 8.71 0 0 1-8.34-1.66l-.11.48c-1.5 6-7.24 7.74-7.49 7.8-.2.07-.39.1-.58.1"/>
  <path fill="#7A7B7E" d="M175.69 155.33c-1.31 1.4 8.6 3.75 9.86 3.48 1.9-.4 10-6.46 8.92-9.38-.58-1.57-13.61.37-18.78 5.9"/>
  <path fill="#7A7B7E" d="M178.95 155.28a31 31 0 0 0 6.23 1.45c1.54-.54 5.53-3.76 6.85-5.73-2.86.13-9.1 1.46-13.08 4.28m6.54 5.62c-2.45 0-10.34-1.68-11.69-4.05a2.47 2.47 0 0 1 .35-2.95c4.91-5.25 16.2-7.72 19.97-6.95 1.5.3 2.08 1.12 2.32 1.76.43 1.18.25 2.5-.54 3.93-1.9 3.43-7.72 7.75-9.9 8.22-.13.03-.3.04-.51.04"/>
  <path fill="#E1E1E6" d="M231.08 178.78s-1.05 8.4-6.3 13.12c-5.24 4.72 6.83 11.54 6.3 22.04-.52 10.5 0-35.16 0-35.16m-98.93-4.73s1.05 8.4 6.3 13.12c5.25 4.73-6.82 11.55-6.3 22.05.52 10.5 0-35.17 0-35.17"/>
  <path fill="#C8C8CC" d="M195.5 105.2h.01-.01zm0-4.2c-.32 0-.64.07-.94.21-.15.08-15.5 7.73-22.42 8.72-5.1.73-18.85 4.6-27.37 7.07l-3.32-3.32a14.57 14.57 0 0 0-10.37-4.3 14.7 14.7 0 0 0-10.38 25.05l5.08 5.09c-.96 8.47-1.4 21.64-1.24 36.66a6.26 6.26 0 0 0-7.69 6.47c-.05.51-.04.97.02 1.42l1.05 7.45c-3.8-4.5-5.22-4.5-6.1-4.5-1.59 0-3.83 1-6.66 2.94l-2.17-1.44a4.2 4.2 0 0 0-6.49 2.96 4.19 4.19 0 0 0-.9 8.26l4.46 1.14a8.3 8.3 0 0 0 6.1 2.5c.86 0 1.77-.1 2.68-.34l4-1c2.04 3.78 7.16 13.17 8.98 15.68a6.29 6.29 0 0 0 5.02 3.85c1.27 12.93 3.54 29.01 7.42 36.75a75.23 75.23 0 0 0 2.49 9.5c1.47 4.2 6.72 17.9 12.35 17.9.78 0 1.52-.26 2.13-.76.97-.79 1.97-1.6 1.65-18.26 8.4 1.12 19.07 1.75 29.79 1.75 7.3 0 17.6-.3 26.69-1.65-.32 16.56.68 17.37 1.64 18.16.61.5 1.35.76 2.13.76 5.64 0 10.88-13.7 12.36-17.9 1.3-3.72 2.53-9.04 3.65-15.82 1.89-7.91 3.84-18.87 5.43-30.39l.42.01a6.29 6.29 0 0 0 6.23-5.46 355.82 355.82 0 0 0 7.94-14.12l4 1c.92.23 1.82.35 2.68.35a8.38 8.38 0 0 0 7.1-3.76l.97-.17a4.17 4.17 0 0 0 3.43-4.84 4.18 4.18 0 0 0-2.76-3.26 4.21 4.21 0 0 0-5.62-3.46l-3.38 1.24c-3.38-2.11-4.88-2.11-5.4-2.11-.86 0-2.27 0-6 4.37l.74-6.2a6.3 6.3 0 0 0-4.2-6.69c-.74-.3-1.5-.46-2.28-.48.53-14.85.12-28.34-1.25-40.13l2.77-2.77a14.58 14.58 0 0 0 4.3-10.38c0-3.92-1.52-7.6-4.3-10.38a14.58 14.58 0 0 0-10.37-4.3c-3.92 0-7.61 1.53-10.38 4.3l-1.56 1.57-14.81-3.97v-3.11a2.1 2.1 0 0 0-2.7-2.02l-4.58 1.35 1.86-4.26a2.1 2.1 0 0 0-1.92-2.94zm0 2.1l-3.67 8.38 8.91-2.62v4.71l17.53 4.7 2.42-2.41a12.54 12.54 0 0 1 17.79 0 12.58 12.58 0 0 1 0 17.79l-3.49 3.49c1.66 13.68 1.88 28.68 1.25 43.36l1.14-.27a4.12 4.12 0 0 1 2.6.25 4.2 4.2 0 0 1 2.86 4.48l-1.66 13.86c3.2-4.58 7.21-9.7 9-9.7 1.2 0 3.17 1.03 5.15 2.35l4.35-1.6a2.1 2.1 0 0 1 2.7 1.24c.32.88 0 1.8-.7 2.35l1.17-.2.36-.03a2.1 2.1 0 0 1 .35 4.16l-2 .35a6.07 6.07 0 0 1-5.72 3.55 9 9 0 0 1-2.17-.28l-5.59-1.4s-6.2 11.6-8.9 15.83l-.04.39a4.2 4.2 0 0 1-6.32 3.08c-1.83 13.82-4 25.42-5.74 32.68-.94 5.73-2.13 11.43-3.57 15.53-3.72 10.6-8 16.5-10.38 16.5-.3 0-.57-.1-.8-.29-1-.81-1.05-9.82-.82-19-7.97 1.4-18.37 2.02-28.84 2.02-11.66 0-23.4-.77-31.95-2.08.23 9.2.2 18.24-.81 19.06-.24.2-.51.29-.81.29-2.38 0-6.65-5.9-10.37-16.5a74.98 74.98 0 0 1-2.47-9.5c-3.48-6.7-5.95-21.27-7.52-38.26-.17.04-.33.1-.5.12a4.2 4.2 0 0 1-4.55-2.79c-1.8-2.19-9.77-17.08-9.77-17.08l-5.59 1.4c-.78.2-1.5.28-2.17.28a6.24 6.24 0 0 1-4.96-2.29l-5.07-1.3a2.1 2.1 0 1 1 1.04-4.05l3.52.9-1.19-.8a2.1 2.1 0 0 1 2.33-3.49l3.37 2.26c2.42-1.79 5.12-3.4 6.63-3.4 1.84 0 6.05 5.46 9.31 10.16l-2.2-15.5a4.08 4.08 0 0 1 .02-1.09c-.05-.43-.04-.88.07-1.33a4.2 4.2 0 0 1 5.03-3.13l2.6.6c-.24-16.38.23-31.22 1.32-40.08l-5.8-5.8a12.58 12.58 0 0 1 17.79-17.79l4.2 4.2c8.04-2.35 23-6.6 28.27-7.36 7.34-1.05 23.06-8.9 23.06-8.9zM52.65 141.37a25.41 25.41 0 0 1 38.25 33.29l3.91 3.91-4.96 4.96-3.91-3.9a25.42 25.42 0 0 1-33.29-38.26"/>
  <path fill="#F5F5F7" d="M70.62 138.63a20.72 20.72 0 1 1 0 41.45 20.72 20.72 0 0 1 0-41.45"/>
  <path fill="#C8C8CC" d="M71.95 141.08a20.72 20.72 0 0 0-18.74 29.58 20.72 20.72 0 0 1 36.2-20.02 20.7 20.7 0 0 0-17.46-9.56"/>
  <path fill="#7A7B7E" d="M115.81 204.54c2.38-2.38 3.3-5.36 2.03-6.63L95.7 177.6c-1.27-1.27-3.34-1.26-5.72 1.12s-2.39 4.46-1.12 5.72l20.31 22.13c1.27 1.26 4.25.35 6.63-2.03"/>
  <polygon fill="#626366" points="113.396 193.835 105.106 202.128 95.104 191.234 102.502 183.834"/>
  <path fill="#E1E1E6" d="M101.75 189.01s-9.1 2.1-8.75 4.9c.35 2.8 5.39 11.38 8.75 9.8 3.36-1.58 8.75-13.3 0-14.7"/>
  <path fill="#C8C8CC" d="M134.37 128.24c-.1 0-.2-.02-.3-.05a1.04 1.04 0 0 1-.71-1.29c.08-.29 2.1-7.15 11.74-9.9.55-.16 1.14.16 1.3.71.16.56-.17 1.14-.73 1.3-8.45 2.42-10.22 8.22-10.3 8.47-.13.45-.55.76-1 .76m95.52-.26c-.37 0-.73-.2-.92-.55-4.4-8.07-11.78-9.43-11.85-9.44a1.05 1.05 0 0 1 .35-2.07c.34.05 8.43 1.5 13.34 10.5a1.04 1.04 0 0 1-.92 1.56m-76.1 139.08h-.1c-5.5-.55-11.09-5.03-11.32-5.22a1.05 1.05 0 1 1 1.32-1.63c.05.04 5.33 4.27 10.2 4.76a1.05 1.05 0 0 1-.1 2.1m56.42-.27a1.05 1.05 0 0 1-.22-2.07c3.53-.76 9.45-3.33 9.5-3.35a1.05 1.05 0 0 1 .85 1.92c-.25.11-6.18 2.68-9.91 3.48a.99.99 0 0 1-.22.02m23.35-45.4c-.85 0-1.58-.37-2.12-1.07-2.52-3.29-.2-14.94 1.29-21.2l-1.08 1a1.05 1.05 0 0 1-1.42-1.54l3.67-3.41a1.05 1.05 0 0 1 1.73 1.04c-2.01 7.48-4.47 20.29-2.53 22.83.16.2.3.25.46.25a1.05 1.05 0 0 1 0 2.1m-105.5.26a1.05 1.05 0 0 1-.04-2.1c.16 0 .36-.05.6-.36 2.4-3.08.3-18.67-1.58-27.76a1.05 1.05 0 0 1 1.77-.96l2.89 2.88c.4.42.4 1.08 0 1.5-.41.4-1.08.4-1.49 0l-.46-.47c1.27 7.02 3.58 22.18.52 26.1a2.84 2.84 0 0 1-2.16 1.17h-.04M100.6 205.1c-.79 0-1.73-.23-2.77-.92-3.5-2.34-6.98-6.52-6.35-10.18.26-1.48 1.3-3.43 4.84-4.31a1.05 1.05 0 0 1 .5 2.03c-1.95.5-3.05 1.38-3.27 2.64-.4 2.27 1.95 5.74 5.44 8.07.86.57 1.62.7 2.34.43 2.25-.88 3.8-5.46 4.2-7.15.13-.56.7-.9 1.26-.78.57.14.91.7.78 1.27-.17.73-1.8 7.17-5.48 8.61-.4.16-.9.29-1.49.29"/>
</svg>
PK
!<ծ���B�BJchrome/browser/skin/classic/browser/preferences/in-content/preferences.css/* - This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this file,
   - You can obtain one at http://mozilla.org/MPL/2.0/. */

@namespace html "http://www.w3.org/1999/xhtml";

* {
  -moz-user-select: text;
}

:root {
  --in-content-category-background: #fafafc;
}

.main-content {
  padding-top: 0;
}

.pane-container {
  /* A workaround to keep the container always float on the `top: 0` (Bug 1377009) */
  display: block;
  width: 664px;
}

#mainPrefPane {
  width: 100%;
  padding: 0;
}

.prefpane > groupbox + groupbox {
  margin-top: 16px;
}

groupbox + groupbox > .groupbox-body,
groupbox + vbox groupbox > .groupbox-body {
  margin-top: 4px;
}

.groupbox-title {
  margin-top: 16px;
}

description.indent,
.indent > description {
  color: #737373;
}

button,
treecol,
html|option {
  /* override the * rule */
  -moz-user-select: none;
}

description,
label {
  line-height: 1.8em;
  margin-top: 0 !important;
  margin-bottom: 0 !important;
}

.tip-caption {
  font-size: .9em;
}

menulist > hbox > label,
menuitem > label,
button > hbox > label {
  line-height: unset;
}

.indent {
  margin-inline-start: 28px !important;
}

.checkbox-check {
  margin-inline-end: 8px;
  width: 20px;
  height: 20px;
}

.tail-with-learn-more {
  margin-inline-start: 0px;
  margin-inline-end: 10px;
}

/* Add a bit of space to the end of descriptions to
 * leave margin with e.g. additional buttons on the side. */
.description-with-side-element {
  margin-inline-end: 10px !important;
}

.learnMore {
  margin-inline-start: 0px;
  font-weight: normal;
  white-space: nowrap;
}

.accessory-button {
  height: 30px;
  min-width: 150px;
  margin: 4px 0;
}

.spotlight {
  background-color: rgba(0,200,215,0.3);
  /* Show the border to spotlight the components in high-contrast mode. */
  border: 1px solid transparent;
  border-radius: 2px;
}

[data-subcategory] {
  margin-left: -4px;
  margin-right: -4px;
  padding-left: 4px;
  padding-right: 4px;
}

[data-subcategory] > .groupbox-title {
  padding-inline-start: 4px;
}

.ac-site-icon {
  display: -moz-box;
}

/* Subcategory title */

/**
 * The first subcategory title for each category should not have margin-top.
 */

.subcategory:not([hidden]) ~ .subcategory {
  margin-top: 16px;
  padding-top: 16px;
  border-top: 1px solid rgba(12, 12, 13, 0.15);
}

/* Category List */

#categories > scrollbox {
  overflow: visible !important; /* Cancel scrollbar and do not clip overflow content when window size goes very small */
}

#categories > scrollbox > box {
  padding: 1px; /* Adding padding around richlistitem in order to make entire keyboard focusing outline visible */
}

#category-general > .category-icon {
  list-style-image: url("chrome://browser/skin/preferences/in-content/general.svg");
}

#category-search > .category-icon {
  list-style-image: url("chrome://browser/skin/preferences/in-content/search.svg");
}

#category-privacy > .category-icon {
  list-style-image: url("chrome://browser/skin/preferences/in-content/privacy-security.svg");
}

#category-sync > .category-icon {
  list-style-image: url("chrome://browser/skin/preferences/in-content/sync.svg");
}

/* header */
.header {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.header[hidden=true] {
  display: none;
}

/* General Pane */

#isDefaultLabel {
  font-weight: 600;
}

#startupPageBox {
  padding-top: 32px;
}

#browserHomePage {
  margin-inline-start: 0;
  margin-inline-end: 0;
  margin-bottom: 4px;
}

.homepage-button:first-of-type {
  margin-inline-start: 0;
}

.homepage-button:last-of-type {
  margin-inline-end: 0;
}

.extension-controlled-icon {
  height: 20px;
  margin: 2px 0 6px;
  vertical-align: middle;
  width: 20px;
}

.extension-controlled-disabled {
  -moz-context-properties: fill, fill-opacity, stroke-opacity;
  color: GrayText;
  fill: currentColor;
  fill-opacity: 1;
  stroke-opacity: 1;
}

.extension-controlled-disabled > .extension-controlled-button {
  display: none;
}

.extension-controlled-icon.close-icon {
  height: 30px;
  width: 30px;
  margin-inline-start: 5px;
}

#getStarted {
  font-size: 90%;
}

#downloadFolder {
  margin-inline-start: 0;
}

#browserHomePage:-moz-locale-dir(rtl) input {
  unicode-bidi: plaintext;
  direction: rtl;
}

#updateApp > .groupbox-body > label {
  margin: 0 0 4px;
}

#updateApp > .groupbox-body > description {
  margin: 0;
}

#updateBox {
  margin-top: 4px;
  margin-bottom: 32px;
}

#updateDeck > hbox > button {
  margin-top: 0;
  margin-bottom: 0;
  margin-inline-end: 0;
}

#updateDeck > hbox > label {
  -moz-box-flex: 1;
}

#manualLink {
  margin-inline-start: 6px !important;
}

#updateRadioGroup > radio,
#browserStartupPage > radio {
  height: 30px;
  margin: 2px 0;
}

#filter {
  margin: 4px 0 8px;
}

#handlersView {
  height: 25em;
  margin-inline-end: 0;
}

#handlersView > richlistitem {
  min-height: 30px !important;
}

#handlersView > richlistitem > hbox > hbox > menulist {
  margin-top: 0;
  margin-bottom: 0;
  margin-inline-end: 0;
  min-height: 0;
}

.typeIcon {
  margin-inline-start: 10px !important;
  margin-inline-end: 9px !important;
}

.actionIcon {
  margin-inline-start: 11px !important;
  margin-inline-end: 8px !important;
}

.actionsMenu {
  min-height: 36px;
}

.actionsMenu > menupopup > menuitem {
  padding-inline-start: 10px !important;
}

.actionsMenu > menupopup > menuitem > .menu-iconic-left {
  margin-inline-end: 8px !important;
}

/* Search Pane */

#engineList {
  margin: 2px 0 5px;
}

#engineList > treechildren::-moz-tree-image(engineShown, checked),
#blocklistsTree > treechildren::-moz-tree-image(selectionCol, checked) {
  list-style-image: url("chrome://global/skin/in-content/check.svg");
  -moz-context-properties: fill, stroke;
  fill: #2292d0;
  stroke: none;
  width: 21px;
  height: 21px;
}

#engineList > treechildren::-moz-tree-image(engineShown, checked, selected),
#blocklistsTree > treechildren::-moz-tree-image(selectionCol, checked, selected) {
  fill: white;
  stroke: #0095dd;
}

#engineList > treechildren::-moz-tree-row,
#blocklistsTree > treechildren::-moz-tree-row {
  min-height: 36px;
}

#selectionCol {
  min-width: 26px;
}

#addEnginesBox {
  margin: 4px 0 0;
}

/* Privacy Pane */

#formAutofillGroup {
  margin-top: 28px;
}

.doNotTrackLearnMore {
  margin-inline-start: calc(1em + 30px);
  margin-bottom: 1em;
  font-weight: normal;
}

.doNotTrackLearnMore > label {
  font-size: 1em !important;
  margin-left: 0;
}

#locationBarGroup > .text-link {
  margin-top: 6px;
  line-height: 30px;
}

#allowSmartSize {
  margin-top: 0;
  margin-bottom: 4px;
}

#doNotTrackLearnMoreBox {
  margin-top: 32px;
}

#trackingProtectionAdvancedSettings {
  margin-inline-start: 15px;
}

#historyPane {
  margin-top: 4px;
}

#acceptCookies {
  margin-top: 1.5em;
}

/* Collapse the non-active vboxes in decks to use only the height the
   active vbox needs */
#historyPane:not([selectedIndex="1"]) > #historyDontRememberPane,
#historyPane:not([selectedIndex="2"]) > #historyCustomPane,
#weavePrefsDeck:not([selectedIndex="1"]) > #hasFxaAccount,
#fxaLoginStatus:not([selectedIndex="1"]) > #fxaLoginUnverified,
#fxaLoginStatus:not([selectedIndex="2"]) > #fxaLoginRejected {
  visibility: collapse;
}

#weavePrefsDeck > vbox > label,
#weavePrefsDeck > vbox > groupbox,
#weavePrefsDeck > vbox > description,
#weavePrefsDeck > #hasFxaAccount > vbox > label,
#weavePrefsDeck > #hasFxaAccount > hbox > label {
  /* no margin-inline-start for elements at the beginning of a line */
  margin-inline-start: 0;
}

#weavePrefsDeck > vbox > groupbox {
  margin-top: 16px;
}

#tabsElement {
  margin-inline-end: 4px; /* add the 4px end-margin of other elements */
}

.text-link {
  margin-bottom: 0;
}

#showUpdateHistory {
  margin-inline-start: 0;
}

/**
 * Dialog
 */

.dialogOverlay {
  visibility: hidden;
}

.dialogOverlay[topmost="true"] {
  background-color: rgba(0,0,0,0.5);
}

.dialogBox {
  background-color: #fbfbfb;
  background-clip: content-box;
  color: #424e5a;
  /* `transparent` will use the dialogText color in high-contrast themes and
     when page colors are disabled */
  border: 1px solid transparent;
  border-radius: 3.5px;
  box-shadow: 0 2px 6px 0 rgba(0,0,0,0.3);
  display: -moz-box;
  margin: 0;
  padding: 0;
}

.dialogBox[resizable="true"] {
  resize: both;
  overflow: hidden;
  min-height: 20em;
  min-width: 66ch;
}

.dialogBox > .groupbox-title {
  margin-top: 0;
  padding: 3.5px 0;
  background-color: #F1F1F1;
  border-bottom: 1px solid #C1C1C1;
}

.dialogTitle {
  font-size: .9em;
  text-align: center;
  -moz-user-select: none;
}

.close-icon {
  border: none;
  background: none !important;
  min-width: 0;
  min-height: auto;
}

.dialogBox > .groupbox-body {
  -moz-appearance: none;
  padding: 20px;
}

.dialogFrame {
  -moz-box-flex: 1;
  /* Default dialog dimensions */
  width: 66ch;
}

.largeDialogContainer.doScroll {
  overflow-y: auto;
  -moz-box-flex: 1;
}

/**
 * End Dialog
 */

/**
 * Font group and font dialog menulist fixes
 */

#defaultFont {
  width: 25ch;
}

#defaultFontType,
#serif,
#sans-serif,
#monospace {
  min-width: 30ch;
}

/**
 * Sync
 */

.fxaProfileImage {
  width: 80px;
  height: 80px;
  border-radius: 50%;
  border: 1px solid transparent;
  list-style-image: url("chrome://browser/skin/preferences/in-content/fxa-avatar.svg");
  margin-inline-end: 24px;
  -moz-user-focus: normal;
}

.fxaProfileImage.actionable {
  cursor: pointer;
}

.fxaProfileImage.actionable:hover {
  border-color: #0095DD;
}

.fxaProfileImage.actionable:hover:active {
  border-color: #ff9500;
}

#useFirefoxSync  {
  font-size: 90%;
  margin-inline-end: 8px !important;
}

#fxaNoLoginStatus {
  margin-top: 46px;
  margin-bottom: 64px;
}

#fxaSyncComputerName {
  margin-inline-start: 0;
  margin-bottom: 4px;
}

#fxaChangeDeviceName {
  margin-top: 4px;
  margin-bottom: 4px;
}

#tosPP-small {
  margin-top: 32px;
}

#tosPP-small-ToS {
  margin-bottom: 14px;
}

#noFxaCaption {
  line-height: 30px;
  margin-top: 0;
  margin-bottom: 4px;
}

#noFxaSignIn {
  margin-inline-start: 8px;
}

.fxaSyncIllustration {
  list-style-image: url("chrome://browser/skin/preferences/in-content/sync-devices.svg");
  width: 312px;
  height: 136px;
}

#noFxaDescription {
  padding-inline-end: 52px;
}

.separator {
  border-bottom: 1px solid var(--in-content-box-border-color);
}

#fxaGroup {
  margin-bottom: 32px;
}

.openLink {
  line-height: 30px;
  cursor: pointer;
}

.openLink:visited {
  color: var(--in-content-link-color);
}

#fxaDisplayName {
  margin-inline-end: 10px !important;
}

.fxaEmailAddress {
  margin-inline-end: 8px !important;
}

.fxaLoginRejectedWarning {
  list-style-image: url(chrome://browser/skin/warning.svg);
  margin-inline-start: 4px;
  margin-inline-end: 8px;
}

#fxaSyncEngines > vbox > checkbox {
  max-width: 224px;
}

.fxaMobilePromo {
  margin-top: 2px !important;
}

.androidLink {
  list-style-image: url("chrome://browser/skin/preferences/in-content/logo-android.svg");
}

.iOSLink {
  list-style-image: url("chrome://browser/skin/preferences/in-content/logo-ios.svg");
}

.androidLink,
.iOSLink {
  margin-inline-start: 2px;
  margin-inline-end: 4px;
  width: 20px;
  height: 20px;
  vertical-align: text-bottom;
  -moz-context-properties: fill;
  fill: currentColor;
}

#updateDeck > hbox > label {
  margin-inline-end: 5px ! important;
}

.update-throbber {
  width: 16px;
  min-height: 16px;
  margin-inline-end: 8px;
  margin-top: 7px;
  margin-bottom: 7px;
  list-style-image: url("chrome://global/skin/icons/loading.png");
}

@media (min-resolution: 1.1dppx) {
  .update-throbber {
    list-style-image: url("chrome://global/skin/icons/[email protected]");
  }
}

.navigation {
  overflow-x: hidden;
  overflow-y: auto;
}

.navigation > .help-button {
  margin-bottom: 36px;
  padding: 1px; /* Adding padding around help label in order to make entire keyboard focusing outline visible */
}

.help-button > .text-link {
  -moz-box-flex: 1;
  margin-inline-start: 34px;
  margin-inline-end: 0;
  text-decoration: none;
  color: currentColor;
  width: 36px;
  height: 36px;
  cursor: default;
}

.help-button > .text-link:hover {
  background-color: var(--in-content-category-background-hover);
  border-radius: 2px;
}

.help-button > .text-link:hover:active:not([disabled]) {
  background-color: var(--in-content-category-background-active);
}

.help-button > .text-link:-moz-focusring {
  outline: var(--in-content-category-outline-focus);
}

.help-icon {
  list-style-image: url("chrome://global/skin/icons/help.svg");
  -moz-context-properties: fill, fill-opacity;
  fill: currentColor;
  width: 16px;
  height: 16px;
  margin: 10px;
  margin-inline-start: 13px;
  vertical-align: middle;
}

.help-icon:hover {
  fill: currentColor !important;
}

.help-label {
  margin: 0 4px;
  font-size: .9em;
  -moz-user-select: none;
}

@media (max-width: 830px) {
  .help-button > .text-link {
    -moz-box-flex: 0;
    width: 36px;
    height: 36px;
    margin-inline-start: 0;
  }

  .help-icon {
    margin: 10px;
  }

  .help-label {
    display: none;
  }
}

.face-sad {
  list-style-image: url("chrome://browser/skin/preferences/in-content/face-sad.svg");
  width: 20px;
  height: 20px;
  margin-inline-end: 8px;
  margin-top: 5px;
  margin-bottom: 5px;
}

.face-smile {
  list-style-image: url("chrome://browser/skin/preferences/in-content/face-smile.svg");
  width: 20px;
  height: 20px;
  margin-inline-end: 8px;
  margin-top: 5px;
  margin-bottom: 5px;
}

#policies-container,
#searchInput {
  min-height: 32px;
  margin: 20px 0 30px 0px;
}

#policies-container {
  background-color: #ededf0;
  padding: 0px 8px;
  margin-inline-end: 16px;
  border-radius: 2px;
}

.info-icon {
  list-style-image: url("chrome://browser/skin/identity-icon.svg");
  fill: currentColor;
  -moz-context-properties: fill;
  width: 16px;
  height: 16px;
  margin-top: calc((32px - 16px) / 2);
}

.sticky-container {
  position: sticky;
  background-color: var(--in-content-page-background);
  top: 0;
  z-index: 1;
  /* The sticky-container should have the capability to cover all spotlight area. */
  width: calc(100% + 8px);
  margin-left: -4px;
  margin-right: -4px;
  padding-left: 4px;
  padding-right: 4px;
  font-size: 85%;
}

#searchInput .textbox-search-icons:not([selectedIndex="1"]) {
  display: none;
}

.search-tooltip {
  max-width: 150px;
  font-size: 1.25rem;
  position: absolute;
  padding: 0 10px;
  background-color: #ffe900;
  border: 1px solid #d7b600;
  bottom: 36px;
  opacity: .85;
}

.search-tooltip:hover {
  opacity: .1;
}

.search-tooltip::before {
  position: absolute;
  content: "";
  border: 7px solid transparent;
  border-top-color: #d7b600;
  top: 100%;
  offset-inline-start: calc(50% - 7px);
}

.search-tooltip::after {
  position: absolute;
  content: "";
  border: 6px solid transparent;
  border-top-color: #ffe900;
  top: 100%;
  offset-inline-start: calc(50% - 6px);
}

.search-tooltip-parent {
  position: relative;
}

.search-tooltip > span {
  -moz-user-select: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.visually-hidden {
  visibility: hidden;
}

menulist {
  height: 30px;
  margin-top: 4px;
  margin-bottom: 4px;
}

menulist[indicator=true] > menupopup menuitem:not([image]) > .menu-iconic-left {
  display: -moz-box;
  min-width: auto; /* Override the min-width defined in menu.css */
  margin-inline-end: 6px;
}

menulist[indicator=true] > menupopup menuitem:not([image]) > .menu-iconic-left > .menu-iconic-icon {
  width: 8px;
  height: 10px;
  margin: 0;
}

menulist[indicator=true] > menupopup menuitem[indicator=true]:not([image]) > .menu-iconic-left > .menu-iconic-icon {
  list-style-image: url(chrome://browser/skin/preferences/in-content/search-arrow-indicator.svg);
}

menulist[indicator=true] > menupopup menuitem[indicator=true]:not([image]) > .menu-iconic-left > .menu-iconic-icon:-moz-locale-dir(rtl) {
  transform: scaleX(-1);
}

.menu-iconic-highlightable-text {
  margin: 0; /* Align with the margin of xul:label.menu-iconic-text */
}

.no-results-message > label {
  margin: 2px 0;
  line-height: 30px;
}

.no-results-container:nth-child(2) {
  margin-top: 64px;
}

.no-results-container:nth-child(2) > image {
  list-style-image: url("chrome://browser/skin/preferences/in-content/no-search-results.svg");
  width: 380px;
  height: 293px;
}

.no-results-message[query*=🔥🦊] {
  background-image: url("chrome://browser/skin/preferences/in-content/critters-postcard.jpg");
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
}

.no-results-message[query*=🔥🦊] > .no-results-container {
  visibility: hidden;
}

/* Proxy port input */

.proxy-port-input {
  width: calc(5ch + 22px); /* 5 chars + 11px padding on both sides */
}

:root > * {
  font-size: 1.11em;
}

.treecol-sortdirection {
  /* override the Linux only toolkit rule */
  -moz-appearance: none;
}

.actionsMenu > .menulist-label-box > .menulist-icon {
  margin-inline-start: 1px;
  margin-inline-end: 8px;
}

filefield + button {
  margin-inline-start: -4px;
}
PK
!<];�eOchrome/browser/skin/classic/browser/preferences/in-content/privacy-security.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 24 24">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M8 6a3 3 0 0 1 6 0v4H8V6zm9 4V6A6 6 0 0 0 5 6v4h-.75C3.01 10 2 11 2 12.25v7.52c0 1.24 1 2.25 2.25 2.25h13.5c1.24 0 2.25-1 2.25-2.25v-7.52C20 11 19 10 17.75 10H17z"/>
</svg>
PK
!<��oMMFchrome/browser/skin/classic/browser/preferences/in-content/privacy.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

.permission-icon {
  height: 20px;
  width: 20px;
  vertical-align: middle;
  -moz-context-properties: fill;
  fill: currentColor;
}

.geo-icon {
  list-style-image: url(chrome://browser/skin/notification-icons/geo.svg);
}

.camera-icon {
  list-style-image: url(chrome://browser/skin/notification-icons/camera.svg);
}

.microphone-icon {
  list-style-image: url(chrome://browser/skin/notification-icons/microphone.svg);
}

.desktop-notification-icon {
  list-style-image: url(chrome://browser/skin/notification-icons/desktop-notification.svg);
}

.midi-icon {
  list-style-image: url(chrome://browser/skin/notification-icons/midi.svg);
}
PK
!<xU��EEUchrome/browser/skin/classic/browser/preferences/in-content/search-arrow-indicator.svg<?xml version="1.0" encoding="UTF-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 xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 10">
  <defs>
    <path id="anchor" d="M33 20l5 8H28z"/>
  </defs>
  <g fill="none" fill-rule="evenodd" transform="rotate(90 28 0)">
    <use fill="#FFEB19" href="#anchor"/>
    <path stroke="#0C0C0D" stroke-opacity=".2" d="M33 20.94l-4.1 6.56h8.2L33 20.94z"/>
  </g>
</svg>
PK
!<����
�
Ichrome/browser/skin/classic/browser/preferences/in-content/search-bar.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="631" height="36" viewBox="0 0 631 36"><style>.addressBarOutline{stroke:#b1b1b3;stroke-linejoin:round;stroke-width:1px}.addressBarIcons{fill:#b1b1b3}.addressBarIcons,.searchBarIcons{fill-rule:evenodd}.searchBarFill{fill:#0a84ff}.searchBarOutline{fill-opacity:.2;stroke:#0a84ff}</style><rect class="addressBarOutline" fill="#fff" x=".5" y=".5" width="630" height="35" rx="4" ry="4"/><rect class="addressBarOutline" fill="none" x="110.5" y="6.5" width="314" height="23" rx="4" ry="4"/><path class="addressBarIcons" d="M604 .5h.5v34h-.5V.5zM126.41 22l-3.09-3.085a3.881 3.881 0 0 0-.64-5.2 3.855 3.855 0 0 0-5.4 5.462 3.958 3.958 0 0 0 5.21.643l3.08 3.085a.622.622 0 0 0 .9 0 .677.677 0 0 0-.06-.9zm-6.23-2.764a2.571 2.571 0 1 1 2.57-2.57 2.652 2.652 0 0 1-2.57 2.574z"/><rect class="addressBarOutline searchBarFill searchBarOutline" x="429.5" y="6.5" width="115" height="23" rx="4" ry="4"/><path class="searchBarFill searchBarIcons" d="M445.41 22l-3.09-3.085a3.881 3.881 0 0 0-.64-5.2 3.855 3.855 0 0 0-5.4 5.462 3.958 3.958 0 0 0 5.21.643l3.08 3.085a.622.622 0 0 0 .9 0 .677.677 0 0 0-.06-.9zm-6.23-2.764a2.571 2.571 0 1 1 2.57-2.57 2.652 2.652 0 0 1-2.57 2.574z"/><path class="addressBarIcons" d="M620.75 17.25h-7.5a.75.75 0 0 0 0 1.5h7.5a.75.75 0 0 0 0-1.5m0 3.75h-7.5a.75.75 0 0 0 0 1.5h7.5a.75.75 0 0 0 0-1.5m0-7.5h-7.5a.75.75 0 0 0 0 1.5h7.5a.75.75 0 0 0 0-1.5M585.77 13.5a.75.75 0 0 0-.52 1.28l3.18 3.22-3.18 3.22a.746.746 0 1 0 1.05 1.06l3.7-3.75a.774.774 0 0 0 0-1.06l-3.7-3.75a.754.754 0 0 0-.53-.22m4.44 0a.715.715 0 0 0-.52.22.754.754 0 0 0 0 1.06l3.17 3.22-3.17 3.22a.754.754 0 0 0 0 1.06.715.715 0 0 0 .52.22.754.754 0 0 0 .53-.22l3.69-3.75a.754.754 0 0 0 0-1.06l-3.69-3.75a.754.754 0 0 0-.53-.22M567.37 15.75h1.5a.375.375 0 1 0 0-.75h-1.5a.375.375 0 0 0 0 .75zm2.63-3h-9a1.5 1.5 0 0 0-1.5 1.5v7.5a1.5 1.5 0 0 0 1.5 1.5h9a1.5 1.5 0 0 0 1.5-1.5v-7.5a1.5 1.5 0 0 0-1.5-1.5zm-4.5 9H561v-7.5h4.5v7.5zm4.5 0h-3.75v-7.5H570v7.5zm-2.63-4.5h1.5a.375.375 0 1 0 0-.75h-1.5a.375.375 0 0 0 0 .75zm0 1.5h.75a.375.375 0 1 0 0-.75h-.75a.375.375 0 0 0 0 .75zM89.83 21.25a.375.375 0 1 1 .37-.375.356.356 0 0 1-.37.375m-2.6 1.5a.7.7 0 0 1-.742-.75v-4.95l2.961-3 2.97 3V22a.706.706 0 0 1-.74.75h-.74V19a.706.706 0 0 0-.74-.75h-1.49a.706.706 0 0 0-.74.75v3.75h-.739m2.219-10.5a.7.7 0 0 0-.51.225l-5.193 5.25a.738.738 0 1 0 1.037 1.05l.223-.225v4.2a1.5 1.5 0 0 0 1.482 1.5h5.931a1.491 1.491 0 0 0 1.48-1.5v-4.2l.22.225a.678.678 0 0 0 .52.225.663.663 0 0 0 .52-.225.725.725 0 0 0 0-1.05l-5.19-5.25a.709.709 0 0 0-.52-.225M70.375 13a.749.749 0 0 0-.75.75v1.808a5.245 5.245 0 1 0-.788 6.4.75.75 0 0 0-1.061-1.061 3.755 3.755 0 1 1 .776-4.151h-1.927a.75.75 0 0 0 0 1.5h3.75a.749.749 0 0 0 .75-.75v-3.75a.749.749 0 0 0-.75-.75M36.217 17.292h8.649l-3.206-3.2a.738.738 0 0 1 1.044-1.043l4.474 4.47a.72.72 0 0 1 0 1.043l-4.474 4.47a.72.72 0 0 1-1.044 0 .72.72 0 0 1 0-1.043l3.206-3.2h-8.649a.749.749 0 1 1 0-1.497z"/><circle class="addressBarOutline" fill="#f9f9fa" cx="18.5" cy="18" r="12"/><path class="addressBarIcons" d="M23.783 17.292h-8.649l3.206-3.2a.738.738 0 0 0-1.044-1.043l-4.474 4.47a.72.72 0 0 0 0 1.043l4.474 4.47a.72.72 0 0 0 1.044 0 .72.72 0 0 0 0-1.043l-3.206-3.2h8.649a.749.749 0 1 0 0-1.497z"/></svg>
PK
!<H0z33Echrome/browser/skin/classic/browser/preferences/in-content/search.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

.searchBarImage {
  height: 36px;
  width: 631px;
  margin-left: 33px;
}

.searchBarHiddenImage {
  list-style-image: url("chrome://browser/skin/preferences/in-content/no-search-bar.svg");
}

#searchBarShownRadio {
  /* Allow a little visual space to separate the radio from the image above it. */
  margin-top: 10px;
}

.searchBarShownImage  {
  list-style-image: url("chrome://browser/skin/preferences/in-content/search-bar.svg");
}

.searchBarImage:-moz-locale-dir(rtl) {
  transform: scaleX(-1);
}

#defaultEngine {
 margin-inline-start: 0;
}

#defaultEngine > .menulist-label-box > .menulist-icon {
  height: 16px;
}

/* work around a display: none in Linux's menu.css, see bug 1112310 */
.searchengine-menuitem > .menu-iconic-left {
  display: -moz-box;
}

#engineList {
  margin: .5em 0;
}

#engineList treechildren::-moz-tree-image(engineName) {
  margin-inline-end: 10px;
  margin-inline-start: 1px;
  width: 16px;
  height: 16px;
}

#engineList treechildren::-moz-tree-drop-feedback {
  background-color: Highlight;
  width: 10000px; /* 100% doesn't work; 10k is hopefully larger than any window
                     we may have, overflow isn't visible. */
  height: 2px;
  margin-inline-start: 0;
}

#engineShown {
  min-width: 26px;
}

#addEnginesBox {
  margin-bottom: 1em;
}

#removeEngineButton,
#restoreDefaultSearchEngines {
  margin-right: 0;
  margin-left: 0;
}
PK
!<CUeeEchrome/browser/skin/classic/browser/preferences/in-content/search.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 24 24">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M10,15.1c-3.3,0-6-2.7-6-6c0-3.3,2.7-6,6-6c3.3,0,6,2.7,6,6S13.3,15.1,10,15.1L10,15.1 M17.3,14.3 c2.9-4,2-9.7-2.1-12.6s-9.7-2-12.6,2.1C1.6,5.3,1,7.2,1,9.1c0,5,4.1,9,9,9c1.8,0,3.6-0.6,5.2-1.6l6.2,6.2c0.6,0.6,1.5,0.6,2.1,0	c0.6-0.6,0.6-1.5,0-2.1L17.3,14.3L17.3,14.3z"/>
</svg>
PK
!<���9^^Ochrome/browser/skin/classic/browser/preferences/in-content/siteDataSettings.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
 * Site Data - Settings dialog
 */
#sitesList {
  min-height: 20em;
}

/* Show selected items in high contrast mode. */
#sitesList > richlistitem[selected] {
  outline: 1px solid transparent;
}

#sitesList > richlistitem > hbox,
.item-box > label {
  -moz-box-flex: 1;
}

.item-box {
  padding: 5px 8px;
  -moz-box-align: center;
  width: 50px;
}

/**
 * Confirmation dialog of removing sites selected
 */
#SiteDataRemoveSelectedDialog {
  padding: 16px;
  min-height: 36em;
}

#contentContainer {
  font-size: 1.2em;
  margin-bottom: 10px;
}

.question-icon {
  margin: 6px;
}

#removing-label {
  font-weight: bold;
}

#sitesTree {
  height: 15em;
}
PK
!<E�睽m�mKchrome/browser/skin/classic/browser/preferences/in-content/sync-devices.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 312 136">
  <defs>
    <path id="a" d="M.458.334h136.808v135.128H.458V.334z"/>
    <path id="c" d="M.677.334h136.808v135.128H.677z"/>
  </defs>
  <path fill="#E1E1E6" d="M247.42 12.25c-.04 0-.08 0-.13-.02a58.49 58.49 0 0 0-9.54-1.34.58.58 0 0 1-.55-.62c.02-.32.3-.56.62-.54 3.28.19 6.55.65 9.74 1.37a.58.58 0 0 1-.14 1.15m-31.25 1.39a.58.58 0 0 1-.18-1.13 59.24 59.24 0 0 1 12-2.55.58.58 0 1 1 .13 1.16 58.1 58.1 0 0 0-11.77 2.5.6.6 0 0 1-.18.02m38.26.67a.57.57 0 0 1-.2-.04c-.76-.27-1.52-.53-2.29-.77a.58.58 0 0 1-.38-.73.6.6 0 0 1 .74-.38c.79.25 1.56.51 2.33.8a.58.58 0 0 1-.2 1.12m-47.22 3.08a.59.59 0 0 1-.52-.3.58.58 0 0 1 .25-.8c.72-.37 1.46-.73 2.2-1.08a.6.6 0 0 1 .78.28c.14.29.01.64-.28.77-.72.34-1.44.7-2.15 1.07a.6.6 0 0 1-.28.06m64.05 6.46a.6.6 0 0 1-.37-.14 57.82 57.82 0 0 0-10.04-6.55.58.58 0 0 1-.26-.78.6.6 0 0 1 .8-.25 59.05 59.05 0 0 1 10.25 6.69c.25.2.29.57.08.81a.6.6 0 0 1-.46.22m-75.99 1.8a.59.59 0 0 1-.43-.19.58.58 0 0 1 .04-.82 59.25 59.25 0 0 1 7.8-5.9.6.6 0 0 1 .82.18c.18.27.1.63-.18.8a57.97 57.97 0 0 0-7.65 5.78.59.59 0 0 1-.4.15M284 38.3c-.2 0-.4-.1-.5-.28a57.31 57.31 0 0 0-5.7-7.67.57.57 0 0 1 .06-.82.6.6 0 0 1 .83.06 58.26 58.26 0 0 1 5.81 7.83.59.59 0 0 1-.5.89m-101.87 4.52a.6.6 0 0 1-.27-.06.58.58 0 0 1-.26-.78 58 58 0 0 1 6.56-10.25.6.6 0 0 1 .83-.1c.25.2.3.57.1.82a56.84 56.84 0 0 0-6.44 10.05c-.1.2-.3.32-.52.32m105.28 1.84a.6.6 0 0 1-.54-.34c-.33-.72-.67-1.44-1.03-2.14a.58.58 0 0 1 .26-.78.6.6 0 0 1 .8.25c.36.73.71 1.46 1.05 2.2a.58.58 0 0 1-.54.81m-108.85 7.11c-.06 0-.11 0-.17-.02a.58.58 0 0 1-.4-.73c.24-.78.5-1.55.76-2.31a.6.6 0 0 1 .75-.36c.3.1.47.43.36.74-.26.75-.5 1.5-.74 2.26a.59.59 0 0 1-.56.42M292.3 63.22a.59.59 0 0 1-.58-.53 56.2 56.2 0 0 0-2.3-11.67c-.1-.31.08-.64.4-.73.3-.1.63.08.73.38a57.26 57.26 0 0 1 2.34 11.91c.03.32-.21.6-.54.63h-.05m-116.12 2.77h-.03a.58.58 0 0 1-.56-.6c.13-3.24.53-6.48 1.2-9.64a.58.58 0 0 1 .7-.45c.31.07.52.38.45.7a56.6 56.6 0 0 0-1.18 9.44.58.58 0 0 1-.58.55m115.54 11.87l-.1-.01a.58.58 0 0 1-.48-.67c.53-3.11.8-6.3.8-9.48a.59.59 0 0 1 1.18 0c0 3.24-.28 6.5-.82 9.67a.59.59 0 0 1-.58.49m-1.66 7.02c-.06 0-.11 0-.17-.02a.58.58 0 0 1-.4-.73 57 57 0 0 0 .65-2.3.59.59 0 0 1 1.14.3c-.2.78-.42 1.57-.66 2.34a.59.59 0 0 1-.56.41m-110.67 2.4a.59.59 0 0 1-.56-.38 57.22 57.22 0 0 1-2.81-11.8.58.58 0 0 1 .5-.66.6.6 0 0 1 .66.5c.51 3.95 1.44 7.84 2.76 11.58a.58.58 0 0 1-.55.77m3.98 8.77c-.2 0-.4-.1-.52-.3-.4-.7-.77-1.42-1.14-2.15a.58.58 0 0 1 .26-.78.6.6 0 0 1 .8.26c.35.71.73 1.41 1.11 2.1.16.29.06.64-.23.8a.59.59 0 0 1-.28.07m98.05 5.99a.59.59 0 0 1-.34-.11.58.58 0 0 1-.13-.81 56.78 56.78 0 0 0 6.02-10.3.6.6 0 0 1 .78-.3c.3.14.43.48.3.77a57.93 57.93 0 0 1-6.15 10.51.59.59 0 0 1-.48.24m-89.46 5.64a.59.59 0 0 1-.42-.18 58.68 58.68 0 0 1-6.13-7.6.58.58 0 0 1 .16-.8.6.6 0 0 1 .82.15 57.6 57.6 0 0 0 6 7.45c.22.23.21.6-.02.82a.59.59 0 0 1-.4.16m75.6 7.77a.59.59 0 0 1-.48-.24.58.58 0 0 1 .14-.81 57.93 57.93 0 0 0 7.42-6.08.6.6 0 0 1 .83.01c.23.23.23.6 0 .82a59.29 59.29 0 0 1-7.57 6.2.6.6 0 0 1-.34.1m-6.24 3.75a.58.58 0 0 1-.27-1.1c.71-.37 1.41-.75 2.1-1.15a.6.6 0 0 1 .81.21c.16.28.07.64-.21.8-.71.4-1.43.8-2.15 1.17a.6.6 0 0 1-.28.07m-51.72 1.12a.6.6 0 0 1-.25-.05 58.95 58.95 0 0 1-10.5-6.28.58.58 0 0 1-.12-.82.6.6 0 0 1 .82-.1c3.2 2.39 6.67 4.46 10.3 6.14.3.14.42.49.29.78a.59.59 0 0 1-.54.33m9.13 3.34c-.05 0-.1 0-.15-.02-.8-.22-1.58-.45-2.36-.7a.58.58 0 0 1-.38-.73c.1-.3.43-.47.74-.37.77.24 1.54.47 2.31.68a.58.58 0 0 1-.16 1.14m24.15 1.47a.58.58 0 0 1-.08-1.16c3.97-.57 7.89-1.57 11.65-2.95a.6.6 0 0 1 .75.34c.12.3-.04.63-.34.74a59.03 59.03 0 0 1-11.98 3.03m-9.72.6h-.01c-3.28-.06-6.57-.4-9.78-.99a.58.58 0 0 1-.47-.68.59.59 0 0 1 .69-.46c3.14.58 6.36.91 9.58.97.33.01.59.28.58.6 0 .31-.27.57-.59.57"/>
  <path fill="#E1E1E6" d="M234.35 7.2c-33.8 0-61.31 27.3-61.31 60.88 0 33.57 27.5 60.89 61.31 60.89 33.8 0 61.31-27.32 61.31-60.9 0-33.56-27.5-60.88-61.31-60.88m0 124.1c-35.1 0-63.66-28.36-63.66-63.21 0-34.86 28.56-63.22 63.66-63.22 35.1 0 63.66 28.36 63.66 63.22 0 34.85-28.56 63.21-63.66 63.21"/>
  <g transform="translate(165.794)">
    <mask id="b" fill="#fff">
      <use xlink:href="#a"/>
    </mask>
    <path fill="#E1E1E6" d="M73.86 1.66h-.04a70.03 70.03 0 0 0-3.42-.16.59.59 0 0 1-.57-.6c0-.32.22-.57.6-.57 1.15.03 2.32.08 3.48.17.32.02.56.3.54.62a.59.59 0 0 1-.59.54m5.72.65h-.1l-1.12-.17a.58.58 0 0 1-.5-.66.6.6 0 0 1 .66-.5l1.15.18a.58.58 0 0 1-.1 1.15M47.76 4.8a.58.58 0 0 1-.18-1.13c4.4-1.43 8.97-2.4 13.59-2.92a.6.6 0 0 1 .65.51c.03.32-.2.61-.52.65-4.54.5-9.03 1.46-13.36 2.86a.55.55 0 0 1-.18.03M37.17 9.23a.6.6 0 0 1-.52-.3.58.58 0 0 1 .25-.8l1.02-.52a.6.6 0 0 1 .8.25c.14.29.03.64-.26.78l-1.01.52a.6.6 0 0 1-.28.07m65.1.93a.6.6 0 0 1-.29-.07 67.54 67.54 0 0 0-12.52-5.43.58.58 0 0 1-.38-.74.6.6 0 0 1 .74-.37c4.41 1.4 8.7 3.26 12.74 5.52a.58.58 0 0 1-.29 1.1m-71.97 3.2a.6.6 0 0 1-.49-.26.58.58 0 0 1 .15-.8c.96-.66 1.93-1.3 2.91-1.9a.6.6 0 0 1 .81.2c.17.27.09.63-.19.8-.96.58-1.92 1.2-2.86 1.85a.6.6 0 0 1-.33.1m82.34 3.95a.59.59 0 0 1-.38-.13 68.68 68.68 0 0 0-2.67-2.12.58.58 0 0 1-.1-.82.6.6 0 0 1 .82-.1c.92.68 1.83 1.4 2.71 2.15.25.2.28.57.07.82a.59.59 0 0 1-.45.2m4.24 3.85a.6.6 0 0 1-.42-.17l-.81-.79a.58.58 0 0 1-.02-.82.6.6 0 0 1 .83-.01l.83.8c.23.23.23.6 0 .82a.59.59 0 0 1-.41.17M13.92 29.2a.59.59 0 0 1-.34-.1.58.58 0 0 1-.13-.82 68.36 68.36 0 0 1 9.26-10.25.6.6 0 0 1 .83.03c.22.24.2.6-.04.83a67.25 67.25 0 0 0-9.1 10.07.59.59 0 0 1-.48.24m-5.94 9.74a.58.58 0 0 1-.53-.84l.53-1.03a.6.6 0 0 1 .8-.25c.28.15.4.5.25.78l-.53 1c-.1.2-.31.33-.53.33m122.6 1.75a.59.59 0 0 1-.53-.34c-1.9-4.09-4.22-8.01-6.92-11.65a.58.58 0 0 1 .13-.81.6.6 0 0 1 .82.13 67.36 67.36 0 0 1 7.04 11.85c.13.29 0 .63-.3.77a.57.57 0 0 1-.24.05M4.86 46.28a.58.58 0 0 1-.56-.77c.39-1.1.8-2.18 1.25-3.24.12-.3.47-.44.76-.31.3.12.45.46.32.76-.43 1.03-.84 2.1-1.21 3.16a.59.59 0 0 1-.56.4m129.87 6.2a.59.59 0 0 1-.57-.44 65.8 65.8 0 0 0-.9-3.27c-.1-.3.08-.63.4-.72.3-.1.63.08.73.39.33 1.09.64 2.2.91 3.32a.58.58 0 0 1-.57.72m1.13 5.57a.59.59 0 0 1-.58-.49l-.19-1.11a.59.59 0 1 1 1.16-.2l.2 1.13a.58.58 0 0 1-.59.67M1.05 68.6a.58.58 0 0 1-.6-.58v-.12c0-4.56.47-9.12 1.38-13.56a.59.59 0 0 1 1.16.24A66.14 66.14 0 0 0 1.64 67.9v.12c0 .32-.27.58-.6.58m.99 11.32a.59.59 0 0 1-.58-.49l-.19-1.13a.58.58 0 0 1 .5-.66c.33-.05.62.16.67.48l.18 1.11a.58.58 0 0 1-.58.69m133.26 2.11a.58.58 0 0 1-.58-.7c.92-4.39 1.38-8.9 1.38-13.43v-.24c0-.32.26-.58.59-.58.32 0 .58.26.58.58v.24c0 4.6-.47 9.2-1.4 13.67a.59.59 0 0 1-.57.46M3.87 87.66a.59.59 0 0 1-.57-.41c-.33-1.1-.63-2.21-.9-3.33a.58.58 0 0 1 .42-.7c.32-.08.64.12.72.43.27 1.1.57 2.19.89 3.26a.58.58 0 0 1-.56.75M131.59 94a.58.58 0 0 1-.54-.8c.43-1.04.84-2.1 1.22-3.17a.59.59 0 0 1 1.1.4c-.38 1.07-.8 2.15-1.24 3.21a.59.59 0 0 1-.54.36m-2.42 5.17a.58.58 0 0 1-.52-.85l.5-1.01a.6.6 0 0 1 .8-.26c.29.14.4.49.27.78l-.53 1.02c-.1.2-.31.32-.52.32m-115.12 8.78a.6.6 0 0 1-.48-.24c-2.74-3.7-5.1-7.7-7.01-11.86a.59.59 0 1 1 1.07-.48c1.88 4.09 4.2 8 6.9 11.65a.58.58 0 0 1-.48.93m7.53 8.57a.6.6 0 0 1-.4-.16l-.83-.8a.58.58 0 0 1 0-.83.6.6 0 0 1 .83 0l.81.8c.24.22.24.59.01.82a.59.59 0 0 1-.42.17m92.95 1.5a.59.59 0 0 1-.43-.19.58.58 0 0 1 .03-.82 67.08 67.08 0 0 0 9.13-10.06.6.6 0 0 1 .82-.13c.26.19.32.55.13.81a68.46 68.46 0 0 1-9.28 10.24.59.59 0 0 1-.4.15m-86.85 3.7a.6.6 0 0 1-.36-.11c-.92-.7-1.83-1.43-2.71-2.16a.58.58 0 0 1-.07-.82.6.6 0 0 1 .83-.07c.86.72 1.76 1.44 2.67 2.12.25.2.3.56.1.82a.59.59 0 0 1-.46.23m76.76 3.81c-.2 0-.39-.1-.5-.28a.58.58 0 0 1 .2-.8c.95-.58 1.92-1.2 2.85-1.84a.6.6 0 0 1 .82.15c.19.26.12.62-.15.8-.95.66-1.93 1.29-2.9 1.88-.1.06-.21.09-.32.09m-5.01 2.78a.59.59 0 0 1-.53-.32.58.58 0 0 1 .26-.78l1.01-.52a.6.6 0 0 1 .8.24c.15.29.04.64-.25.79l-1.03.52a.57.57 0 0 1-.26.07m-51.46 3.93c-.06 0-.12 0-.19-.03-4.4-1.4-8.69-3.27-12.72-5.54a.58.58 0 0 1-.22-.8.6.6 0 0 1 .8-.22 67.47 67.47 0 0 0 12.5 5.45.58.58 0 0 1-.17 1.14m11.2 2.56h-.09l-1.14-.18a.58.58 0 0 1-.5-.66.59.59 0 0 1 .68-.48l1.13.16c.32.05.55.34.5.66-.04.3-.3.5-.58.5m17.21.27a.59.59 0 0 1-.58-.51c-.04-.32.2-.61.52-.65 4.54-.5 9.03-1.45 13.36-2.84.3-.1.64.07.74.37.1.31-.07.64-.38.74a69.01 69.01 0 0 1-13.66 2.9m-9.19.38h-.01a72.02 72.02 0 0 1-3.48-.17.58.58 0 1 1 .09-1.16c1.13.08 2.28.14 3.41.17.33 0 .59.27.58.6 0 .31-.27.56-.59.56" mask="url(#b)"/>
  </g>
  <path fill="#E1E1E6" d="M99.82 12.25c-.04 0-.09 0-.13-.02a58.5 58.5 0 0 0-9.54-1.34.58.58 0 0 1-.55-.62c.02-.32.3-.56.62-.54 3.27.19 6.55.65 9.73 1.37a.58.58 0 0 1-.13 1.15m-31.25 1.39a.58.58 0 0 1-.18-1.13 59.24 59.24 0 0 1 12-2.55c.33-.03.61.2.65.52.03.31-.2.6-.52.63-4 .43-7.96 1.27-11.77 2.5a.6.6 0 0 1-.18.03m38.26.67a.56.56 0 0 1-.2-.04c-.76-.27-1.53-.53-2.3-.77a.58.58 0 0 1-.37-.73.6.6 0 0 1 .74-.38c.78.25 1.56.51 2.33.8.3.1.46.44.35.74a.59.59 0 0 1-.55.38M59.6 17.39a.59.59 0 0 1-.52-.3.58.58 0 0 1 .24-.8c.73-.37 1.46-.73 2.2-1.08a.6.6 0 0 1 .78.28c.14.29.02.64-.27.77-.73.34-1.45.7-2.16 1.07a.6.6 0 0 1-.27.06m64.05 6.46a.6.6 0 0 1-.37-.14 57.82 57.82 0 0 0-10.05-6.55.58.58 0 0 1-.25-.78.6.6 0 0 1 .8-.25 59.05 59.05 0 0 1 10.24 6.69c.25.2.29.57.08.81a.6.6 0 0 1-.45.22m-76 1.8a.59.59 0 0 1-.43-.19.57.57 0 0 1 .04-.82 59.26 59.26 0 0 1 7.81-5.9.6.6 0 0 1 .82.18c.17.27.1.63-.18.8a57.9 57.9 0 0 0-7.66 5.78.59.59 0 0 1-.4.15M136.4 38.3a.6.6 0 0 1-.5-.28 57.28 57.28 0 0 0-5.7-7.67.57.57 0 0 1 .06-.82.6.6 0 0 1 .83.06 58.41 58.41 0 0 1 5.82 7.83c.17.27.08.63-.2.8a.6.6 0 0 1-.3.08m-101.9 4.53a.6.6 0 0 1-.26-.06.58.58 0 0 1-.27-.78 58.13 58.13 0 0 1 6.57-10.25.6.6 0 0 1 .82-.1c.26.2.3.57.1.82a56.9 56.9 0 0 0-6.43 10.05.6.6 0 0 1-.53.32m105.29 1.84a.6.6 0 0 1-.54-.34c-.33-.72-.68-1.44-1.03-2.14a.58.58 0 0 1 .26-.78.6.6 0 0 1 .79.25c.36.73.72 1.46 1.05 2.2a.58.58 0 0 1-.53.81M30.95 51.78a.58.58 0 0 1-.56-.75 59 59 0 0 1 .75-2.31.6.6 0 0 1 .75-.36c.31.1.47.43.37.74-.26.75-.51 1.5-.74 2.26a.59.59 0 0 1-.57.42M144.7 63.22a.59.59 0 0 1-.59-.53 56.2 56.2 0 0 0-2.3-11.67c-.09-.31.09-.64.4-.73.31-.1.64.08.73.38a57.26 57.26 0 0 1 2.34 11.91c.03.32-.2.6-.53.63h-.05M28.57 65.99h-.02a.58.58 0 0 1-.57-.6c.13-3.24.53-6.48 1.2-9.64a.58.58 0 0 1 .7-.45c.32.07.52.38.45.7a56.6 56.6 0 0 0-1.17 9.44.58.58 0 0 1-.59.55m115.54 11.87l-.1-.01a.58.58 0 0 1-.48-.67c.53-3.11.8-6.3.8-9.48 0-.33.26-.59.59-.59.32 0 .59.26.59.59 0 3.24-.28 6.5-.82 9.67a.59.59 0 0 1-.58.49m-1.66 7.02c-.05 0-.11 0-.17-.02a.58.58 0 0 1-.39-.73 57 57 0 0 0 .65-2.3c.08-.3.4-.49.71-.4.32.07.5.39.43.7-.2.78-.43 1.57-.66 2.34a.59.59 0 0 1-.57.41m-110.67 2.4a.59.59 0 0 1-.55-.38 57.22 57.22 0 0 1-2.82-11.8.59.59 0 0 1 1.17-.15c.5 3.94 1.43 7.83 2.76 11.57a.58.58 0 0 1-.56.77m3.98 8.77a.6.6 0 0 1-.51-.3c-.4-.7-.78-1.42-1.15-2.15a.58.58 0 0 1 .27-.78.6.6 0 0 1 .79.26c.36.71.73 1.41 1.12 2.1.15.29.05.64-.23.8a.6.6 0 0 1-.29.07m98.06 5.99a.59.59 0 0 1-.35-.11.58.58 0 0 1-.13-.81 56.79 56.79 0 0 0 6.03-10.3c.13-.29.48-.43.78-.3.3.14.43.48.3.77a57.85 57.85 0 0 1-6.15 10.51.59.59 0 0 1-.48.24m-89.46 5.64a.59.59 0 0 1-.43-.18 58.58 58.58 0 0 1-6.12-7.6.58.58 0 0 1 .16-.8.6.6 0 0 1 .81.15 57.59 57.59 0 0 0 6 7.45c.23.23.22.6-.02.82a.58.58 0 0 1-.4.16m75.6 7.77a.59.59 0 0 1-.48-.24.58.58 0 0 1 .15-.81 58.03 58.03 0 0 0 7.42-6.08.6.6 0 0 1 .83.01c.23.23.22.6-.01.82a59.22 59.22 0 0 1-7.57 6.2c-.1.07-.22.1-.33.1m-6.24 3.75a.59.59 0 0 1-.52-.31.58.58 0 0 1 .24-.79c.72-.37 1.42-.75 2.11-1.15a.6.6 0 0 1 .8.21c.17.28.07.64-.2.8-.72.4-1.43.8-2.16 1.17a.6.6 0 0 1-.27.07M62 120.32a.6.6 0 0 1-.25-.05 59 59 0 0 1-10.51-6.28.58.58 0 0 1-.12-.82.6.6 0 0 1 .83-.1c3.2 2.39 6.67 4.46 10.3 6.14.3.14.42.49.28.78a.59.59 0 0 1-.53.33m9.13 3.34c-.05 0-.1 0-.16-.02-.79-.22-1.58-.45-2.35-.7a.58.58 0 0 1-.38-.73c.1-.3.43-.47.74-.37.76.24 1.53.47 2.3.68a.58.58 0 0 1-.15 1.14m24.14 1.47a.59.59 0 0 1-.58-.5.58.58 0 0 1 .5-.66c3.97-.57 7.9-1.57 11.65-2.95a.6.6 0 0 1 .76.34c.11.3-.04.63-.35.74a59.06 59.06 0 0 1-11.98 3.03m-9.71.6h-.02c-3.28-.06-6.57-.4-9.78-.99a.58.58 0 1 1 .22-1.14c3.14.58 6.37.91 9.59.97a.59.59 0 0 1-.01 1.16"/>
  <path fill="#E1E1E6" d="M86.74 7.2c-33.8 0-61.31 27.3-61.31 60.88 0 33.57 27.5 60.89 61.31 60.89s61.32-27.32 61.32-60.9c0-33.56-27.5-60.88-61.32-60.88m0 124.1c-35.1 0-63.65-28.36-63.65-63.21 0-34.86 28.55-63.22 63.65-63.22 35.1 0 63.66 28.36 63.66 63.22 0 34.85-28.55 63.21-63.66 63.21"/>
  <g transform="translate(17.49)">
    <mask id="d" fill="#fff">
      <use xlink:href="#c"/>
    </mask>
    <path fill="#E1E1E6" d="M74.08 1.66h-.04a70.03 70.03 0 0 0-3.42-.16.59.59 0 0 1-.57-.6c0-.32.24-.57.6-.57 1.15.03 2.32.08 3.48.17.32.02.56.3.54.62a.59.59 0 0 1-.59.54m5.72.65h-.1l-1.12-.17a.58.58 0 0 1-.5-.66.59.59 0 0 1 .66-.5l1.15.18a.58.58 0 0 1-.1 1.15M47.98 4.8a.58.58 0 0 1-.18-1.13c4.4-1.43 8.97-2.4 13.59-2.92a.6.6 0 0 1 .65.51c.03.32-.2.61-.52.65-4.54.5-9.03 1.46-13.36 2.86a.55.55 0 0 1-.18.03M37.39 9.23a.6.6 0 0 1-.52-.3.58.58 0 0 1 .25-.8l1.02-.52a.6.6 0 0 1 .8.25c.14.29.03.64-.26.78l-1.01.52a.6.6 0 0 1-.28.07m65.1.93c-.1 0-.2-.02-.29-.07a67.42 67.42 0 0 0-12.52-5.43.58.58 0 0 1-.38-.73.6.6 0 0 1 .74-.38c4.41 1.4 8.7 3.26 12.74 5.52.29.16.38.52.22.8-.1.19-.3.3-.5.3m-71.98 3.2a.6.6 0 0 1-.49-.26.58.58 0 0 1 .15-.8 67.5 67.5 0 0 1 2.91-1.9.6.6 0 0 1 .81.2c.17.27.09.63-.19.8-.96.58-1.92 1.2-2.86 1.85a.6.6 0 0 1-.33.1m82.34 3.95a.59.59 0 0 1-.38-.13 68.68 68.68 0 0 0-2.67-2.12.58.58 0 0 1-.1-.82.6.6 0 0 1 .82-.1c.91.68 1.83 1.4 2.71 2.15.25.2.28.57.07.82a.59.59 0 0 1-.45.2m4.23 3.85a.59.59 0 0 1-.41-.17l-.81-.79a.58.58 0 0 1-.02-.82.6.6 0 0 1 .83-.01l.83.8c.23.23.23.6 0 .82a.59.59 0 0 1-.42.17M14.14 29.2a.59.59 0 0 1-.34-.1.58.58 0 0 1-.13-.82 68.44 68.44 0 0 1 9.26-10.25.6.6 0 0 1 .83.03c.22.24.2.6-.04.83a67.26 67.26 0 0 0-9.1 10.07.59.59 0 0 1-.48.24M8.2 38.93a.58.58 0 0 1-.53-.84l.53-1.03a.6.6 0 0 1 .79-.25c.29.15.4.5.26.78l-.52 1.01c-.1.2-.31.33-.53.33m122.6 1.75a.59.59 0 0 1-.53-.34c-1.9-4.09-4.22-8.01-6.92-11.65a.58.58 0 0 1 .13-.81.6.6 0 0 1 .82.13 67.36 67.36 0 0 1 7.04 11.85c.13.29 0 .63-.3.77a.57.57 0 0 1-.24.05M5.08 46.28a.58.58 0 0 1-.56-.77c.39-1.1.8-2.18 1.25-3.24a.6.6 0 0 1 .76-.31c.3.12.45.46.32.76-.43 1.03-.84 2.1-1.21 3.16a.59.59 0 0 1-.56.4m129.87 6.2a.59.59 0 0 1-.57-.44c-.27-1.1-.58-2.2-.9-3.27-.1-.3.08-.63.4-.72.3-.1.63.08.73.39.33 1.09.63 2.2.91 3.32a.58.58 0 0 1-.57.72m1.13 5.57a.59.59 0 0 1-.58-.49l-.19-1.11a.58.58 0 0 1 .48-.67c.32-.06.63.15.68.47l.2 1.13a.58.58 0 0 1-.6.67M1.28 68.6a.59.59 0 0 1-.6-.58v-.12c0-4.56.47-9.12 1.38-13.56a.59.59 0 0 1 1.16.24A66.14 66.14 0 0 0 1.85 67.9v.12c0 .32-.26.58-.58.58m.98 11.32a.59.59 0 0 1-.58-.49l-.19-1.13a.58.58 0 0 1 .5-.66.6.6 0 0 1 .67.48l.18 1.11a.58.58 0 0 1-.58.69m133.26 2.11a.58.58 0 0 1-.58-.7c.91-4.39 1.38-8.9 1.38-13.43v-.24c0-.32.26-.58.59-.58.32 0 .59.26.59.58v.24c0 4.6-.48 9.2-1.4 13.67a.59.59 0 0 1-.58.46M4.09 87.66a.59.59 0 0 1-.57-.41c-.33-1.1-.63-2.21-.9-3.33a.58.58 0 0 1 .42-.7c.32-.08.64.12.72.43.26 1.1.57 2.19.89 3.26a.58.58 0 0 1-.56.75M131.8 94a.58.58 0 0 1-.55-.8c.44-1.04.85-2.1 1.23-3.17.1-.3.44-.46.75-.35.3.1.47.44.36.74-.39 1.08-.8 2.16-1.25 3.22a.59.59 0 0 1-.54.36m-2.42 5.17a.58.58 0 0 1-.52-.85l.5-1.01a.6.6 0 0 1 .8-.26c.29.14.4.49.26.78l-.52 1.02c-.1.2-.31.32-.52.32m-115.12 8.78a.6.6 0 0 1-.48-.24 67.5 67.5 0 0 1-7.01-11.87c-.14-.29 0-.63.29-.77a.6.6 0 0 1 .78.3c1.88 4.09 4.2 8 6.9 11.65.18.26.13.63-.14.82a.59.59 0 0 1-.34.1m7.53 8.58a.6.6 0 0 1-.4-.16l-.83-.8a.58.58 0 0 1 0-.83.6.6 0 0 1 .83 0l.81.8c.24.22.24.59.02.82a.59.59 0 0 1-.43.17m92.95 1.5a.59.59 0 0 1-.43-.19.58.58 0 0 1 .03-.82 67.22 67.22 0 0 0 9.13-10.06.6.6 0 0 1 .82-.13c.26.19.32.55.13.81a68.52 68.52 0 0 1-9.28 10.24.6.6 0 0 1-.4.15m-86.85 3.7a.6.6 0 0 1-.36-.11c-.92-.7-1.83-1.43-2.71-2.16a.58.58 0 0 1-.07-.82.6.6 0 0 1 .83-.07c.86.72 1.76 1.44 2.67 2.12a.59.59 0 1 1-.36 1.04m76.76 3.82c-.2 0-.39-.1-.5-.28a.58.58 0 0 1 .2-.8 70.2 70.2 0 0 0 2.85-1.84.6.6 0 0 1 .82.15c.19.26.12.62-.15.8-.95.66-1.93 1.29-2.91 1.88-.1.06-.2.09-.3.09m-5.02 2.78a.59.59 0 0 1-.53-.32.58.58 0 0 1 .26-.79c.34-.16.68-.34 1.01-.51a.6.6 0 0 1 .8.24c.15.29.04.64-.25.79l-1.03.52a.58.58 0 0 1-.26.07m-51.46 3.93c-.06 0-.12 0-.18-.03a68.66 68.66 0 0 1-12.73-5.54.58.58 0 0 1-.22-.8.6.6 0 0 1 .8-.21 67.41 67.41 0 0 0 12.5 5.45.58.58 0 0 1-.17 1.13m11.2 2.56h-.09l-1.14-.18a.58.58 0 0 1-.5-.66.59.59 0 0 1 .68-.48l1.13.16c.32.05.55.34.5.66-.04.3-.3.5-.58.5m17.21.27a.58.58 0 0 1-.06-1.16c4.54-.5 9.03-1.45 13.36-2.84.3-.1.64.07.74.37.1.31-.07.64-.38.74a69 69 0 0 1-13.66 2.9m-9.19.38h-.01a72.02 72.02 0 0 1-3.48-.17.58.58 0 1 1 .08-1.16c1.14.08 2.29.14 3.42.17.33 0 .59.27.58.6-.01.31-.27.56-.59.56" mask="url(#d)"/>
  </g>
  <path fill="#FFF" d="M272.75 105.6a7.58 7.58 0 0 1-7.62-7.52V39.5a7.58 7.58 0 0 1 7.62-7.5h21.14c.03-1.18 1-2.13 2.2-2.13h5.68c1.21 0 2.2.97 2.2 2.17v.02a7.56 7.56 0 0 1 6.63 7.44v58.58a7.58 7.58 0 0 1-7.62 7.51h-30.23z"/>
  <path fill="#FAFAFC" d="M302.04 28.48h-5.64c-1.43 0-2.65.88-3.14 2.12H273.2a8.71 8.71 0 0 0-8.76 8.65v58.39a8.71 8.71 0 0 0 8.76 8.65h30.05a8.71 8.71 0 0 0 8.76-8.65v-58.4a8.69 8.69 0 0 0-6.74-8.41 3.37 3.37 0 0 0-3.22-2.35m0 2.32a1 1 0 0 1 1.01 1v.97l-.02.15h.21c3.53 0 6.4 2.84 6.4 6.33v58.39a6.37 6.37 0 0 1-6.4 6.32H273.2a6.37 6.37 0 0 1-6.4-6.32v-58.4a6.37 6.37 0 0 1 6.4-6.32h22.22l-.03-.15v-.97a1 1 0 0 1 1.02-1h5.64"/>
  <path fill="#FFF" d="M9.2 113.23a7.8 7.8 0 0 1-7.8-7.79V31.41a7.8 7.8 0 0 1 7.8-7.8h48.05c4.3 0 7.8 3.5 7.8 7.8v74.03a7.8 7.8 0 0 1-7.8 7.8H9.21z"/>
  <path fill="#FAFAFC" d="M57.42 22.23H9.04A9 9 0 0 0 0 31.16V105a9 9 0 0 0 9.04 8.94h48.38a9 9 0 0 0 9.04-8.94V31.16a9 9 0 0 0-9.04-8.93m0 2.33a6.65 6.65 0 0 1 6.68 6.6V105c0 3.64-3 6.6-6.68 6.6H9.04a6.65 6.65 0 0 1-6.68-6.6V31.16c0-3.64 3-6.6 6.68-6.6h48.38"/>
  <path fill="#FFF" d="M114.56 114.62c-3.38 0-6.13-2.59-6.13-5.77v-1.23c0-.75.15-1.48.45-2.18l8.09-18.77c.35-.83.9-1.56 1.6-2.14a7.48 7.48 0 0 1-1.44-4.45V32.64c0-4.2 3.45-7.63 7.7-7.63h72.14c4.25 0 7.7 3.42 7.7 7.63v47.44c0 1.72-.6 3.39-1.67 4.73.52.5.94 1.09 1.24 1.74l8.62 18.8c.33.73.5 1.5.5 2.3v1.2c0 3.18-2.74 5.77-6.12 5.77h-92.68z"/>
  <path fill="#FAFAFC" d="M197.12 23.62h-72.45a8.85 8.85 0 0 0-8.9 8.77v47.3c0 1.5.4 2.98 1.13 4.27-.5.56-.9 1.18-1.2 1.86l-8.12 18.72a6.57 6.57 0 0 0-.55 2.63v1.22c0 3.82 3.3 6.93 7.33 6.93h93.07c4.04 0 7.33-3.11 7.33-6.93v-1.2c0-.95-.2-1.89-.61-2.77l-8.65-18.74c-.22-.48-.5-.93-.82-1.35a8.64 8.64 0 0 0 1.35-4.63V32.4c0-4.85-4-8.78-8.9-8.78m0 2.32a6.5 6.5 0 0 1 6.54 6.45v47.3c0 1.95-.87 3.68-2.25 4.86a4.7 4.7 0 0 1 1.93 2.09l8.65 18.75c.27.57.4 1.18.4 1.8v1.2c0 2.54-2.23 4.6-4.97 4.6h-93.07c-2.74 0-4.97-2.06-4.97-4.6v-1.22c0-.59.12-1.17.36-1.72l8.12-18.72a4.71 4.71 0 0 1 2.3-2.35 6.38 6.38 0 0 1-2.05-4.68V32.4a6.5 6.5 0 0 1 6.55-6.46h72.45"/>
  <path fill="#E1E1E6" d="M272.47 102.81a5.2 5.2 0 0 1-5.24-5.16V39.2a5.21 5.21 0 0 1 5.24-5.17h30.1a5.2 5.2 0 0 1 5.23 5.17v58.44a5.2 5.2 0 0 1-5.23 5.16h-30.1z"/>
  <path fill="#C8C8CC" d="M302.82 33.34h-29.91a6.38 6.38 0 0 0-6.38 6.37v58.82a6.38 6.38 0 0 0 6.38 6.37h29.91a6.38 6.38 0 0 0 6.38-6.37V39.7a6.38 6.38 0 0 0-6.38-6.37m0 2.34c2.23 0 4.04 1.8 4.04 4.03v58.82c0 2.22-1.8 4.03-4.04 4.03h-29.91a4.03 4.03 0 0 1-4.04-4.03V39.7c0-2.22 1.8-4.03 4.04-4.03h29.91"/>
  <path fill="#FFF" d="M302.9 41.6v48.8a2 2 0 0 1-2 2h-26.07a2 2 0 0 1-2-2V41.6c0-1.1.9-2 2-2h26.07a2 2 0 0 1 2 2"/>
  <path fill="#C8C8CC" d="M285.2 98.64h6.03c1.04 0 1.88-.76 1.88-1.7v-.06c0-.95-.84-1.7-1.88-1.7h-6.03c-1.04 0-1.88.75-1.88 1.7v.06c0 .94.84 1.7 1.88 1.7m16.69-67.38h-5.66c-.56 0-1.02.42-1.02.94v.9c0 .52.46.94 1.02.94h5.66c.56 0 1.02-.42 1.02-.94v-.9c0-.52-.46-.94-1.02-.94m-19.47 32.38v.01zm10.12 1.56h.01zm-5.32 3l2.21 1.14-.36-2.55 1.78-1.9-2.5-.44-1.13-2.23-1.12 2.23-2.5.45 1.78 1.9-.36 2.54 2.2-1.14zm-3.82 1.97zm.4 2.08l-.6-.36a1.08 1.08 0 0 1-.45-1.05l.47-3.38-2.7-2.87.17-.65c.13-.38.45-.66.85-.73l3.2-.56 1.78-3.56.7-.04c.42 0 .8.23.99.6l1.5 3 3.8.66.25.63c.12.37.03.79-.24 1.08l-2.3 2.44.57 4-.55.43a1.1 1.1 0 0 1-1.14.07l-2.88-1.48-3.43 1.77zm3.37-26.47a4.6 4.6 0 0 0-4.62 4.58 4.6 4.6 0 0 0 4.62 4.58 4.6 4.6 0 0 0 4.6-4.58 4.6 4.6 0 0 0-4.6-4.58m0 11.18a6.63 6.63 0 0 1-6.65-6.6c0-3.64 2.98-6.6 6.65-6.6a6.63 6.63 0 0 1 6.64 6.6c0 3.64-2.98 6.6-6.64 6.6"/>
  <path fill="#C8C8CC" d="M289.76 51.4h-3.09a.56.56 0 0 1-.55-.55V47.8a.55.55 0 0 1 1.11 0v2.51h2.53c.3 0 .55.25.55.55 0 .3-.25.56-.55.56m-7.46 35.42c-.6 0-1.08-.48-1.08-1.07v-3.4c0-.6.49-1.08 1.08-1.08h3.43c.6 0 1.09.48 1.09 1.07 0 .6-.49 1.08-1.09 1.08h-2.34v2.33c0 .6-.49 1.07-1.09 1.07m10.43-6.25h-3.43c-.6 0-1.08-.48-1.08-1.07 0-.6.48-1.08 1.08-1.08h2.34V76.1c0-.6.49-1.07 1.09-1.07.6 0 1.08.48 1.08 1.07v3.4c0 .6-.48 1.08-1.08 1.08"/>
  <path fill="#C8C8CC" d="M282.92 80.78a1.03 1.03 0 0 1-.97-1.28 5.77 5.77 0 0 1 5.57-4.47 5.74 5.74 0 0 1 4.95 2.94c.28.5.12 1.12-.36 1.41a.99.99 0 0 1-1.37-.37 3.7 3.7 0 0 0-3.22-1.93c-1.72 0-3.21 1.2-3.63 2.92a1 1 0 0 1-.97.78m4.6 6.05a5.74 5.74 0 0 1-4.96-2.94c-.28-.5-.12-1.12.36-1.41a.99.99 0 0 1 1.37.37 3.7 3.7 0 0 0 3.23 1.93c1.71 0 3.2-1.2 3.62-2.92a1 1 0 0 1 1.21-.75c.54.14.87.7.73 1.25a5.77 5.77 0 0 1-5.56 4.47"/>
  <path fill="#E1E1E6" d="M9 110.45a5.48 5.48 0 0 1-5.5-5.44V31.14c0-3 2.47-5.44 5.5-5.44h48.45c3.04 0 5.51 2.44 5.51 5.44v73.87c0 3-2.47 5.44-5.5 5.44H9z"/>
  <path fill="#C8C8CC" d="M57 25H8.76a6.63 6.63 0 0 0-6.66 6.6v73.66a6.63 6.63 0 0 0 6.66 6.58H57a6.63 6.63 0 0 0 6.66-6.58V31.6A6.63 6.63 0 0 0 57 25m0 2.33c2.38 0 4.3 1.91 4.3 4.27v73.66a4.28 4.28 0 0 1-4.3 4.26H8.76a4.29 4.29 0 0 1-4.3-4.26V31.6a4.29 4.29 0 0 1 4.3-4.27H57"/>
  <path fill="#FFF" d="M56.66 34.08v63.13c0 1.18-.95 2.13-2.14 2.13H10.54a2.13 2.13 0 0 1-2.15-2.13V34.08c0-1.17.96-2.12 2.15-2.12h43.98c1.19 0 2.14.95 2.14 2.12"/>
  <path fill="#C8C8CC" d="M38.34 106.29H27.42c-.85 0-1.54-.75-1.54-1.65v-1.57c0-.9.7-1.65 1.54-1.65h10.92c.84 0 1.53.74 1.53 1.65v1.57c0 .9-.69 1.65-1.53 1.65M27.35 63.42zm11.08 1.77h.01zm-5.83 3.2l2.37 1.23-.4-2.73 1.92-2.04-2.69-.48-1.2-2.4-1.2 2.4-2.7.48 1.91 2.04-.38 2.73 2.37-1.22zm-4.22 2.18zm6.87 1.1zm-6.43 1.27l-.67-.4a1.23 1.23 0 0 1-.51-1.2l.52-3.69-2.98-3.16.2-.74c.14-.43.51-.74.96-.83l3.49-.61 1.97-3.9.8-.06c.48 0 .9.27 1.12.7l1.64 3.26 4.16.73.29.7c.14.44.04.91-.27 1.24l-2.5 2.67.62 4.4-.62.48c-.38.27-.88.3-1.3.09L32.6 71l-3.78 1.94zm3.7-31.73a5.01 5.01 0 0 0-5.02 4.99 5.01 5.01 0 0 0 5.03 4.99 5.01 5.01 0 0 0 5.02-5c0-2.75-2.25-4.98-5.02-4.98m0 12.28a7.33 7.33 0 0 1-7.35-7.3c0-4.01 3.3-7.29 7.35-7.29a7.33 7.33 0 0 1 7.34 7.3c0 4.02-3.3 7.29-7.34 7.29"/>
  <path fill="#C8C8CC" d="M35.77 47.93h-3.69a.6.6 0 0 1-.6-.6v-3.66a.6.6 0 1 1 1.21 0v3.06h3.08c.34 0 .6.27.6.6a.6.6 0 0 1-.6.6M27 91.7c-.61 0-1.1-.5-1.1-1.1v-3.36c0-.6.49-1.1 1.1-1.1h3.38c.61 0 1.11.5 1.11 1.1 0 .6-.5 1.1-1.1 1.1H28.1v2.26c0 .6-.5 1.1-1.1 1.1m10.37-6.95h-3.38c-.61 0-1.11-.5-1.11-1.1 0-.6.5-1.1 1.1-1.1h2.28v-2.26a1.1 1.1 0 0 1 2.22 0v3.36c0 .6-.5 1.1-1.11 1.1"/>
  <path fill="#C8C8CC" d="M27.05 85.67a1.19 1.19 0 0 1-1.13-1.47c.72-2.95 3.3-5 6.26-5a6.47 6.47 0 0 1 5.58 3.29 1.2 1.2 0 0 1-.42 1.62c-.56.33-1.27.14-1.6-.43a4.09 4.09 0 0 0-7.57 1.1c-.13.53-.6.89-1.12.89m5.13 6.72a6.46 6.46 0 0 1-5.58-3.3 1.2 1.2 0 0 1 .42-1.61 1.15 1.15 0 0 1 1.6.42 4.09 4.09 0 0 0 7.57-1.09c.15-.63.78-1.02 1.4-.86.63.16 1 .8.85 1.44-.72 2.94-3.3 5-6.26 5"/>
  <path fill="#E1E1E6" d="M125 85.45a5.36 5.36 0 0 1-5.38-5.34V32.42A5.36 5.36 0 0 1 125 27.1h72.5c2.96 0 5.37 2.4 5.37 5.33v47.7a5.36 5.36 0 0 1-5.37 5.33H125z"/>
  <path fill="#C8C8CC" d="M197.03 25.7h-72.27c-3.6 0-6.54 2.9-6.54 6.48v47.49a6.51 6.51 0 0 0 6.54 6.47h72.27c3.6 0 6.54-2.9 6.54-6.47v-47.5a6.51 6.51 0 0 0-6.54-6.47m0 2.33c2.31 0 4.19 1.86 4.19 4.15v47.49a4.16 4.16 0 0 1-4.19 4.14h-72.27a4.16 4.16 0 0 1-4.18-4.14v-47.5a4.16 4.16 0 0 1 4.18-4.14h72.27"/>
  <path fill="#FFF" d="M126.98 35.43h67.83c1.36 0 2.46 1.1 2.46 2.45v40.25c0 1.36-1.1 2.45-2.46 2.45h-67.83a2.46 2.46 0 0 1-2.46-2.45V37.88c0-1.35 1.1-2.45 2.46-2.45"/>
  <path fill="#E1E1E6" d="M114.3 112.54c-2.08 0-3.77-1.56-3.77-3.48v-1.23c0-.44.09-.87.26-1.27l8.07-18.91a3.76 3.76 0 0 1 3.51-2.2h75.83c1.52 0 2.88.83 3.47 2.13l8.6 18.93c.2.43.3.88.3 1.35v1.2c0 1.92-1.7 3.48-3.77 3.48h-92.5z"/>
  <path fill="#C8C8CC" d="M198.36 84.06H122.2a4.95 4.95 0 0 0-4.6 2.88l-8.11 18.75a4.32 4.32 0 0 0-.36 1.72v1.22c0 2.54 2.23 4.6 4.96 4.6H207c2.74 0 4.96-2.06 4.96-4.6v-1.2c0-.63-.13-1.24-.4-1.81l-8.63-18.77a4.98 4.98 0 0 0-4.57-2.8m0 2.33c1.07 0 2.03.57 2.42 1.43l8.64 18.77c.13.27.19.56.19.85v1.2c0 1.26-1.17 2.28-2.61 2.28h-92.9c-1.45 0-2.62-1.02-2.62-2.28v-1.22c0-.28.06-.55.17-.81l8.1-18.74a2.63 2.63 0 0 1 2.45-1.48h76.16"/>
  <path fill="#C8C8CC" d="M177.69 103.53a47.48 47.48 0 0 0-.52-5.11c0-.27-.33-.47-.77-.47l-29.82.22c-.45 0-.8.2-.8.48-.4 2.03-.58 3.03-.97 4.95-.02.32.42.6.93.6h30.92c.5 0 1-.32 1.03-.67m-36.4-9.62l-.42 1.49c-.07.26-.4.44-.8.44l-4.64.03c-.64 0-1.2-.42-1.1-.83.12-.6.19-.9.33-1.49.06-.26.38-.44.8-.45l4.74-.01c.66 0 1.21.41 1.1.82m10.18-.82l-4.84.02c-.46 0-.82.21-.87.5l-.26 1.5c-.06.4.44.76 1.04.76l4.7-.03c.44 0 .8-.22.88-.5l.36-1.49c.1-.4-.4-.77-1.01-.76m24.8 1.98c-.07-.6-.11-.89-.2-1.48-.03-.3-.39-.5-.85-.5l-4.88.02c-.62 0-1.13.38-1.05.78l.3 1.48c.07.3.43.5.88.5l4.73-.03c.6 0 1.12-.38 1.07-.77m-11.9.04l-.23-1.48c-.05-.31-.47-.55-.97-.54l-4.48.02c-.5 0-.92.24-.99.55l-.3 1.49c-.08.38.39.72.98.72l5-.04c.58 0 1.06-.34 1-.72m23.08-.1l-.27-1.48c-.05-.27-.37-.44-.79-.44l-4.78.01c-.66 0-1.24.43-1.13.85l.36 1.48c.06.26.38.44.8.44l4.67-.03c.64 0 1.22-.43 1.14-.83m-46.41-6.1l-4.23.02c-.37 0-.67.14-.73.33-.16.45-.23.67-.37 1.12-.1.31.38.62.94.62h4.13c.37 0 .67-.14.75-.34l.44-1.12c.12-.3-.35-.62-.93-.62m11.85 0l-4.79.01c-.45 0-.82.16-.89.38l-.32 1.12c-.08.3.41.57 1 .57l4.67-.01c.44 0 .81-.16.9-.38.15-.44.23-.67.4-1.11.12-.3-.35-.58-.97-.58m21.97 1.5l-.17-1.12c-.04-.22-.36-.38-.77-.38l-4.38.01c-.57 0-1.03.3-.96.59l.27 1.11c.05.22.38.37.79.37l4.26-.01c.55 0 1.01-.28.96-.57m-10.49.02l-.2-1.11c-.04-.24-.4-.41-.85-.41l-4.02.01c-.45 0-.83.18-.9.42-.13.45-.2.67-.31 1.12-.08.28.33.53.87.53l4.5-.01c.53 0 .96-.26.91-.55m21.68-.06l-.25-1.13c-.05-.2-.33-.33-.7-.33l-4.3.01c-.6 0-1.12.32-1.02.63l.33 1.12c.06.2.36.32.73.32h4.2c.57 0 1.09-.32 1.01-.62M130.1 94.6c-.21.6-.3.9-.44 1.49-.06.26-.33.44-.7.44l-4.14.02c-.56 0-1.07-.41-.98-.82.14-.6.24-.9.44-1.5.1-.27.4-.45.77-.45h4.18c.58-.01 1.01.41.87.82m1.16-5.7l-4.16.01c-.36 0-.64.14-.7.34-.15.44-.24.67-.44 1.12-.14.3.27.62.83.62h4.1c.36 0 .68-.14.78-.34.23-.44.35-.67.53-1.12.13-.3-.36-.62-.94-.62m60.66 4.18l4.21-.01c.37 0 .68.18.76.44.18.6.26.9.38 1.48.09.41-.44.83-1.01.83l-4.17.03c-.37 0-.64-.18-.69-.44-.11-.6-.2-.89-.39-1.49-.13-.41.32-.84.9-.84m-2.31-3.54c.14.45.23.68.43 1.12.09.2.4.32.78.32h4.16c.56 0 1-.31.9-.62a6.6 6.6 0 0 1-.33-1.12c-.04-.2-.3-.34-.67-.34h-4.23c-.6 0-1.14.33-1.04.64m-28.7-59a1.4 1.4 0 1 1 0 2.78 1.4 1.4 0 0 1-1.4-1.39c0-.76.63-1.38 1.4-1.38m6.26 25.59h.01-.01zm-9.73-.34l1.91 2.04-.38 2.73 2.36-1.22 2.37 1.22-.38-2.73 1.91-2.04-2.7-.48-1.2-2.4-1.2 2.4-2.7.48zm-.32 5.72zm6.87 1.1zm-6.43 1.27l-.67-.41a1.23 1.23 0 0 1-.52-1.19l.53-3.69-2.98-3.16.2-.74c.15-.43.52-.75.97-.83l3.48-.61 1.96-3.91.8-.05c.48 0 .91.26 1.12.69l1.65 3.27 4.16.73.29.71c.14.43.04.9-.27 1.24l-2.5 2.66.61 4.4-.61.48c-.39.27-.89.3-1.3.08l-3.15-1.62-3.77 1.95zm-15.88-12.28a5.01 5.01 0 0 0-5.03 4.99c0 2.75 2.26 4.99 5.03 4.99a5.01 5.01 0 0 0 5.02-5c0-2.75-2.25-4.98-5.02-4.98m0 12.28a7.33 7.33 0 0 1-7.35-7.3c0-4.02 3.3-7.29 7.35-7.29a7.33 7.33 0 0 1 7.34 7.3c0 4.02-3.3 7.29-7.34 7.29"/>
  <path fill="#C8C8CC" d="M144.9 57.66h-3.69a.6.6 0 0 1-.6-.6V53.4a.6.6 0 0 1 1.21 0v3.06h3.08c.34 0 .6.27.6.6a.6.6 0 0 1-.6.6m30.4 4.86c-.61 0-1.11-.5-1.11-1.1v-3.36c0-.6.5-1.1 1.1-1.1h3.39c.6 0 1.1.5 1.1 1.1 0 .61-.5 1.1-1.1 1.1h-2.27v2.26c0 .6-.5 1.1-1.11 1.1m11.07-6.95H183c-.61 0-1.1-.49-1.1-1.1 0-.6.49-1.1 1.1-1.1h2.27v-2.25c0-.61.5-1.1 1.11-1.1.61 0 1.11.49 1.11 1.1v3.35c0 .61-.5 1.1-1.1 1.1"/>
  <path fill="#C8C8CC" d="M176.05 56.5c-.1 0-.19-.02-.28-.04-.62-.16-1-.8-.85-1.44.72-2.95 3.3-5 6.26-5a6.46 6.46 0 0 1 5.58 3.3 1.2 1.2 0 0 1-.42 1.61c-.55.33-1.27.14-1.59-.43a4.09 4.09 0 0 0-7.57 1.1c-.14.53-.6.9-1.13.9m5.13 6.72a6.47 6.47 0 0 1-5.57-3.3 1.2 1.2 0 0 1 .42-1.62 1.15 1.15 0 0 1 1.59.43 4.09 4.09 0 0 0 7.57-1.1c.16-.63.8-1.01 1.4-.85.63.15 1.01.8.86 1.43-.73 2.95-3.3 5-6.27 5"/>
</svg>PK
!<��"�''Cchrome/browser/skin/classic/browser/preferences/in-content/sync.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 24 24">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M20.5 0c-.83 0-1.5.67-1.5 1.5v1.72A10.47 10.47 0 0 0 11.5 0C6.69 0 2.51 3.27 1.34 7.96a1.5 1.5 0 1 0 2.91.73A7.47 7.47 0 0 1 11.5 3c2.37 0 4.54 1.1 5.95 3H14.5a1.5 1.5 0 1 0 0 3h6c.83 0 1.5-.67 1.5-1.5v-6c0-.83-.67-1.5-1.5-1.5m.07 11.22a1.5 1.5 0 0 0-1.82 1.1A7.48 7.48 0 0 1 11.5 18a7.38 7.38 0 0 1-5.95-3H8.5a1.5 1.5 0 1 0 0-3h-6c-.83 0-1.5.67-1.5 1.5v6a1.5 1.5 0 0 0 3 0v-1.72A10.47 10.47 0 0 0 11.5 21c4.81 0 8.99-3.27 10.16-7.96a1.5 1.5 0 0 0-1.1-1.82"/>
</svg>
PK
!<}�6?chrome/browser/skin/classic/browser/preferences/preferences.css/*
# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

/* Pref Window & Pane */
.prefwindow {
  padding: 0px;
}

.prefwindow[type="child"] {
  padding: 8px;
}

.prefpane {
  padding: 8px;
}

.prefwindow[type="child"] > .prefpane {
  padding: 0px;
}

/* General Pane */
#useFirefoxSync,
#getStarted {
  font-size: 90%;
}

#isNotDefaultLabel {
  font-weight: bold;
}

/* Content Pane */
#translationAttributionImage {
  width: 70px;
  cursor: pointer;
}

/* Modeless Window Dialogs */
.windowDialog,
.windowDialog .prefpane {
  padding: 0px;
}

.contentPane {
  margin: 9px 8px 5px 8px;
}

.actionButtons {
  margin: 0px 3px 6px 3px !important;
}

/* Cookies Manager */
#cookiesChildren::-moz-tree-image(domainCol) {
  width: 16px;
  height: 16px;
  margin: 0 2px;
  list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.svg");
  -moz-context-properties: fill;
  fill: currentColor;
}

#linksOpenInBox {
  margin-top: 5px;
}

#advancedPrefs {
  margin-left: 0;
  margin-right: 0;
}

#cookiesChildren::-moz-tree-image(domainCol, container) {
  list-style-image: url("moz-icon://stock/gtk-directory?size=menu");
}

#cookieInfoBox {
  border: 1px solid ThreeDShadow;
  border-radius: 0px;
  margin: 4px;
  padding: 0px;
}

/* bottom-most box containing a groupbox in a prefpane. Prevents the bottom
   of the groupbox from being cutoff */
.bottomBox {
  padding-bottom: 4px;
}

/**
 * Clear Private Data
 */
#SanitizeDialogPane > groupbox {
  margin-top: 0;
}

/* Sync Pane */

#noFxaAccount {
  line-height: 1.2em;
}

#noFxaAccount > label:first-child {
  margin-bottom: 0.6em;
}
PK
!<	~~-chrome/browser/skin/classic/browser/print.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M14 5h-1V1a1 1 0 0 0-1-1H4a1 1 0 0 0-1 1v4H2a2 2 0 0 0-2 2v5h3v3a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-3h3V7a2 2 0 0 0-2-2zM2.5 8a.5.5 0 1 1 .5-.5.5.5 0 0 1-.5.5zm9.5 7H4v-5h8zm0-10H4V1h8zm-6.5 7h4a.5.5 0 0 0 0-1h-4a.5.5 0 1 0 0 1zm0 2h5a.5.5 0 0 0 0-1h-5a.5.5 0 1 0 0 1z"/>
</svg>
PK
!< �LL8chrome/browser/skin/classic/browser/private-browsing.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="24px" height="24px">
<path fill="#8000D7" d="M12,24L12,24C5.4,24,0,18.6,0,12l0,0C0,5.4,5.4,0,12,0l0,0c6.6,0,12,5.4,12,12l0,0
	C24,18.6,18.6,24,12,24z"/>
<path fill="#FFFFFF" d="M15.4,11c-1-0.1-1.9,0.5-2.1,1.5c0,0.3,1.2,0.7,2.3,0.7s2.1-0.7,2.1-0.9C17.7,12.1,17,11.1,15.4,11
	L15.4,11z M8.6,11c-1.6,0.1-2.3,1-2.3,1.3c0,0.2,1.1,0.9,2.1,0.9c1,0,2.3-0.4,2.3-0.7C10.5,11.5,9.5,10.9,8.6,11L8.6,11z M16.4,16.5
	c-1.7,0-2.8-2-4.4-2c-1.6,0-2.8,2-4.4,2c-2.1,0-3.6-1.9-3.6-5.3c0-2.1,0.6-2.7,3.3-2.7s3.4,1.1,4.7,1.1s2.1-1.1,4.7-1.1
	S20,9.2,20,11.2C20,14.6,18.5,16.5,16.4,16.5L16.4,16.5z"/>
</svg>
PK
!<!T�..7chrome/browser/skin/classic/browser/privateBrowsing.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M12.408 11.992c-1.663 0-2.813-2-4.408-2s-2.844 2-4.408 2C1.54 11.992.025 10.048 0 6.719c-.015-2.068.6-2.727 3.265-2.727S6.709 5.082 8 5.082s2.071-1.091 4.735-1.091 3.28.66 3.265 2.727c-.025 3.33-1.54 5.274-3.592 5.274zM4.572 6.537c-1.619.07-2.286 1.035-2.286 1.273s1.073.909 2.122.909 2.286-.384 2.286-.727a1.9 1.9 0 0 0-2.122-1.455zm6.857 0a1.9 1.9 0 0 0-2.123 1.455c0 .343 1.236.727 2.286.727s2.122-.671 2.122-.909-.667-1.203-2.286-1.273z"/>
</svg>
PK
!<�@���Lchrome/browser/skin/classic/browser/privatebrowsing/aboutPrivateBrowsing.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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://global/skin/in-content/info-pages.css");

:root {
  --color-grey: #505473;
}

html.private {
  --in-content-page-color: white;
  --in-content-text-color: white;
  --in-content-page-background: #25003e;
}

a:link {
  color: inherit;
  text-decoration: underline;
}

.container {
  max-width: 48em;
}

.section-main {
  margin-bottom: 48px;
}

.section-main:last-child {
  margin-bottom: 0;
}

p {
  line-height: 1.5em;
}

.list-row {
  overflow: auto;
}

.list-row > ul > li {
  float: inline-start;
  width: 16em;
  line-height: 2em;
  margin-inline-start: 1em;
  margin-bottom: 0;
}

.title {
  background-image: url("chrome://browser/skin/privatebrowsing/private-browsing.svg");
  background-position: left center;
  background-size: 2em;
  line-height: 2em;
  margin-inline-start: calc(-2em - 10px);
  padding-inline-start: calc(2em + 10px);
}

.title:dir(rtl) {
  background-position: right center;
}

.about-subheader {
  display: flex;
  align-items: center;
  font-size: 1.5em;
  font-weight: lighter;
  background-image: url("chrome://browser/skin/privatebrowsing/tracking-protection.svg");
  background-repeat: no-repeat;
  background-position: left center;
  background-size: 1.5em;
  line-height: 1.5em;
  margin-inline-start: calc(-1.5em - 10px);
  padding-inline-start: calc(1.5em + 10px);
}

.about-subheader:dir(rtl) {
  background-position: right;
}

.about-subheader.tp-off {
  background-image: url("chrome://browser/skin/privatebrowsing/tracking-protection-off.svg");
}

.about-info {
  font-size: .9em;
}

.tpTitle {
  margin-inline-end: 12px;
}

a.button {
  padding: 3px 20px;
  background-color: #8000d7;
  border: 1px solid #6000a1;
  text-decoration: none;
  display: inline-block;
}

/**
 * We want to hide the checkbox in lieu of the toggle-btn
 * "slider toggle". We need to make the toggle keyboard
 * focusable, however, which is not possible if it's
 * display:none. We work around this by making the toggle
 * invisible but still present in the display list, allowing
 * it to receive keyboard events. When it is focused by keyboard,
 * we use the -moz-focusring selector on the invisible checkbox
 * to show a focus ring around the slider toggle.
 */
.toggle-input {
  opacity: 0;
  width: 0;
  pointer-events: none;
  position: absolute;
}

.toggle + .toggle-btn {
  box-sizing: border-box;
  cursor: pointer;
  min-width: 48px;
  height: 27px;
  border-radius: 13px;
  background-color: var(--color-grey);
  border: 1px solid #202340;
}

.toggle + .toggle-btn::after {
  position: relative;
  display: block;
  content: "";
  width: 25px;
  height: 100%;
  left: 0;
  border-radius: 50%;
  background: white;
  transition: left .2s ease;
}

.toggle + .toggle-btn:dir(rtl)::after {
  left: auto;
  right: 0;
  transition-property: right;
}

.toggle:checked + .toggle-btn {
  background: #16da00;
  border-color: #0CA700;
}

.toggle:checked + .toggle-btn::after {
  offset-inline-start: 21px;
}

.toggle:-moz-focusring + .toggle-btn {
  outline: 2px solid rgba(0, 149, 221, 0.5);
  outline-offset: 1px;
  -moz-outline-radius: 2px;
}
PK
!<���?chrome/browser/skin/classic/browser/privatebrowsing/favicon.svg<?xml version="1.0" encoding="utf-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 xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
  <circle cx="8" cy="8" r="8" fill="#8d20ae" />
  <circle cx="8" cy="8" r="7.5" stroke="#7b149a" stroke-width="1" fill="none" />
  <path d="M11.309,10.995C10.061,10.995,9.2,9.5,8,9.5s-2.135,1.5-3.309,1.5c-1.541,0-2.678-1.455-2.7-3.948C1.983,5.5,2.446,5.005,4.446,5.005S7.031,5.822,8,5.822s1.555-.817,3.555-0.817S14.017,5.5,14.006,7.047C13.987,9.54,12.85,10.995,11.309,10.995ZM5.426,6.911a1.739,1.739,0,0,0-1.716.953A2.049,2.049,0,0,0,5.3,8.544c0.788,0,1.716-.288,1.716-0.544A1.428,1.428,0,0,0,5.426,6.911Zm5.148,0A1.429,1.429,0,0,0,8.981,8c0,0.257.928,0.544,1.716,0.544a2.049,2.049,0,0,0,1.593-.681A1.739,1.739,0,0,0,10.574,6.911Z" stroke="#670c83" stroke-width="2" fill="none" />
  <path d="M11.309,10.995C10.061,10.995,9.2,9.5,8,9.5s-2.135,1.5-3.309,1.5c-1.541,0-2.678-1.455-2.7-3.948C1.983,5.5,2.446,5.005,4.446,5.005S7.031,5.822,8,5.822s1.555-.817,3.555-0.817S14.017,5.5,14.006,7.047C13.987,9.54,12.85,10.995,11.309,10.995ZM5.426,6.911a1.739,1.739,0,0,0-1.716.953A2.049,2.049,0,0,0,5.3,8.544c0.788,0,1.716-.288,1.716-0.544A1.428,1.428,0,0,0,5.426,6.911Zm5.148,0A1.429,1.429,0,0,0,8.981,8c0,0.257.928,0.544,1.716,0.544a2.049,2.049,0,0,0,1.593-.681A1.739,1.739,0,0,0,10.574,6.911Z" fill="#fff" />
</svg>
PK
!<�?����Hchrome/browser/skin/classic/browser/privatebrowsing/private-browsing.svg<?xml version="1.0" encoding="utf-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 xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
  <ellipse cx="32" cy="34" rx="29.5" ry="30" fill="#000" fill-opacity=".1" />
  <circle cx="32" cy="32" r="30" fill="#8d20ae" />
  <circle cx="32" cy="32" r="29.5" stroke="#7b149a" stroke-width="1" fill="none" />
  <path d="M45.225,43c-4.989,0-8.44-5.5-13.224-5.5S23.468,43,18.776,43C12.62,43,8.074,37.656,8,28.5,7.954,22.815,9.805,21,17.8,21S28.128,24,32,24s6.214-3,14.2-3,9.842,1.815,9.8,7.5C55.926,37.656,51.381,43,45.225,43ZM21.714,28c-4.857.193-6.857,2.846-6.857,3.5s3.22,2.5,6.367,2.5,6.857-1.057,6.857-2C28.082,30.948,26.3,27.818,21.714,28Zm20.572,0c-4.583-.182-6.367,2.948-6.367,4,0,0.943,3.709,2,6.857,2s6.367-1.846,6.367-2.5S47.143,28.193,42.286,28Z" stroke="#670c83" stroke-width="2" fill="none" />
  <path d="M45.225,43c-4.989,0-8.44-5.5-13.224-5.5S23.468,43,18.776,43C12.62,43,8.074,37.656,8,28.5,7.954,22.815,9.805,21,17.8,21S28.128,24,32,24s6.214-3,14.2-3,9.842,1.815,9.8,7.5C55.926,37.656,51.381,43,45.225,43ZM21.714,28c-4.857.193-6.857,2.846-6.857,3.5s3.22,2.5,6.367,2.5,6.857-1.057,6.857-2C28.082,30.948,26.3,27.818,21.714,28Zm20.572,0c-4.583-.182-6.367,2.948-6.367,4,0,0.943,3.709,2,6.857,2s6.367-1.846,6.367-2.5S47.143,28.193,42.286,28Z" fill="#fff" />
</svg>
PK
!<�]ˮ��Ochrome/browser/skin/classic/browser/privatebrowsing/tracking-protection-off.svg<?xml version="1.0" encoding="utf-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 xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
  <g fill="#ccc">
    <path d="M28.8,0.3l-2.4,2.4L16.1,1.1L4.9,2.9c-1,0.2-1.8,1-1.8,2c0,2.5,0,6.9,0.3,8.7c0.4,4.3,1.2,6.9,2.7,9.4l-3.5,3.5l2,2
      L30.8,2.3L28.8,0.3z M5.3,13.5c-0.2-1.9-0.2-6.2-0.2-8.6c0,0,0,0,0.1,0l10.9-1.8l8.6,1.4L16.1,13V5L7.2,6.6c-0.1,0-0.1,0-0.1,0
      c0,2,0,5.6,0.2,7.1c0.3,3,0.8,4.9,1.6,6.5l-1.4,1.4C6.3,19.6,5.6,17.3,5.3,13.5z"/>
    <path d="M16.1,20.3l-3.9,3.9c1.7,1.2,3.4,1.6,3.9,1.7V20.3z"/>
    <path d="M26.9,13.4c-0.5,5.6-1.7,8-3.8,10.7c-2.4,3.1-6.1,3.9-7,4.1c-0.7-0.2-3.2-0.7-5.4-2.5L9.3,27c3.1,2.7,6.7,3,6.7,3
      s5.2-0.5,8.6-4.9c2.5-3.2,3.6-5.9,4.2-11.6c0.1-1.3,0.2-4,0.2-6.3l-2,2C27,10.9,27,12.5,26.9,13.4z"/>
  </g>
</svg>
PK
!<�DT��Kchrome/browser/skin/classic/browser/privatebrowsing/tracking-protection.svg<?xml version="1.0" encoding="utf-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 xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
  <path fill="#ccc" d="M27.2,2.8L16,1L4.8,2.8C3.8,3,3,3.8,3,4.8c0,2.5,0,6.9,0.3,8.7C3.8,19,5,21.8,7.5,25.1C10.8,29.5,16,30,16,30
    s5.2-0.5,8.6-4.9c2.5-3.2,3.6-5.9,4.2-11.6C29,11.7,29,7.2,29,4.8C29,3.8,28.2,3,27.2,2.8z M26.8,13.3L26.8,13.3L26.8,13.3
    c-0.5,5.6-1.7,8-3.8,10.7c-2.4,3.1-6.1,3.9-7,4.1c-0.9-0.2-4.6-1-7-4.1c-2.1-2.8-3.3-5.2-3.8-10.6l0,0l0,0C5,11.5,5,7.2,5,4.8
    c0,0,0,0,0.1,0l0,0l0,0L16,3l10.8,1.8l0,0l0,0c0.1,0,0.1,0,0.1,0C27,7,27,11.5,26.8,13.3z M7.1,6.5L7.1,6.5L7.1,6.5
    C7,6.5,7,6.5,7.1,6.5C7,8.5,7,12.1,7.2,13.6l0,0l0,0c0.4,4.5,1.4,6.5,3.1,8.9c2,2.6,5,3.3,5.7,3.4v-21L7.1,6.5z"/>
</svg>
PK
!<�r�l)),chrome/browser/skin/classic/browser/quit.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M8 6a1 1 0 0 0 1-1V1a1 1 0 0 0-2 0v4a1 1 0 0 0 1 1zm3.5-4.032a1 1 0 0 0-1 1.732A4.946 4.946 0 0 1 13 8 5 5 0 0 1 3 8a4.946 4.946 0 0 1 2.5-4.3 1 1 0 0 0-1-1.732 7 7 0 1 0 7.006 0z"/>
</svg>
PK
!<`�Iii7chrome/browser/skin/classic/browser/restore-session.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16" fill="context-fill" fill-opacity="context-fill-opacity">
  <path d="M13 0H3a3 3 0 0 0-3 3v8a3 3 0 0 0 3 3h4l-.3.4a1 1 0 1 0 1.6 1.2l1.5-2a1 1 0 0 0 0-1.2l-1.5-2a1 1 0 0 0-1.6 1.2l.3.4H3a1 1 0 0 1-1-1V5h12v6a1 1 0 0 1-1 1 1 1 0 0 0 0 2 3 3 0 0 0 3-3V3a3 3 0 0 0-3-3zM2 4V3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v1z"/>
</svg>
PK
!<���%k	k	6chrome/browser/skin/classic/browser/sanitizeDialog.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#sanitizeDurationChoice {
  margin-inline-end: 0;
}

/* Align the duration label with the warning box and item list */
#sanitizeDurationLabel {
  margin-inline-start: 3px;
}


/* Hide the duration dropdown suffix label if it's empty.  Otherwise it
   takes up a little space, causing the end of the dropdown to not be aligned
   with the warning box. */
#sanitizeDurationSuffixLabel[value=""] {
  display: none;
}


/* Places tree */
#placesTreechildren::-moz-tree-row(selected),
#placesTreechildren::-moz-tree-row(grippyRow) {
  background: #999;
}

#placesTreechildren::-moz-tree-cell-text(selected) {
  color: #111;
}


/* Sanitize everything warning box */
#sanitizeEverythingWarningBox {
  background-color: Window;
  border: 1px solid ThreeDDarkShadow;
  border-radius: 5px;
  padding: 16px;
}

#sanitizeEverythingWarningIcon {
  list-style-image: url("moz-icon://stock/gtk-dialog-warning?size=dialog");
  padding: 0;
  margin: 0;
}

#sanitizeEverythingWarningDescBox {
  padding: 0 16px;
  margin: 0;
}


/* Progressive disclosure button */
#detailsExpanderWrapper {
  padding: 0;
  margin-top: 6px;
  margin-bottom: 6px;
  margin-inline-start: -4px;
  margin-inline-end: 0;
}

.expander-up,
.expander-down {
  min-width: 0;
  padding: 2px 0;
  padding-inline-start: 2px;
}

.expander-up {
  list-style-image: url("chrome://global/skin/arrow/arrow-up.gif");
}

.expander-down {
  list-style-image: url("chrome://global/skin/arrow/arrow-dn.gif");
}

.expander-down:hover:active {
  list-style-image: url("chrome://global/skin/arrow/arrow-dn-hov.gif");
}

.expander-up:hover:active {
  list-style-image: url("chrome://global/skin/arrow/arrow-up-hov.gif");
}


/* Make the item list the same width as the warning box */
#itemList {
  margin-inline-start: 0;
  margin-inline-end: 0;
}

/* Without this a useless scrollbar appears in the listbox when its rows
   attribute is set to the total number of listitems, as it is currently.  See
   bug 489958 comment 14 and bug 491788. */
#itemList > listitem {
  padding: 1px 0;
}


/* Align the last dialog button with the end of the warning box */
.dialog-button-box {
  margin-inline-end: 0;
}
.dialog-button[dlgtype="accept"] {
  margin-inline-end: 0;
}
PK
!<t���,chrome/browser/skin/classic/browser/save.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16" fill="context-fill" fill-opacity="context-fill-opacity">
  <path d="M14 3h-2v2h2v8H2V5h7V3h-.849L6.584 1.538A2 2 0 0 0 5.219 1H2a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2zM2 3h3.219l1.072 1H2z"/>
  <path d="M8.146 6.146a.5.5 0 0 0 0 .707l2 2a.5.5 0 0 0 .707 0l2-2a.5.5 0 1 0-.707-.707L11 7.293V.5a.5.5 0 0 0-1 0v6.793L8.854 6.146a.5.5 0 0 0-.708 0z"/>
</svg>
PK
!<�����Achrome/browser/skin/classic/browser/search-engine-placeholder.png�PNG


IHDR��7��IDATx���
�@��$�*곦�~"o�V'�	�(�S�a�2\�Yf�%�ڏa�W5,������p
��d0 �f��MpD Bn/+j�pU��Y!Book�S��}�G������5��1�(46*\k�����:'����4�|A�+���p��A�(�nH��WR�����ό�A}��=���,!B�+^����7gP�TIEND�B`�PK
!<��+��Dchrome/browser/skin/classic/browser/[email protected]�PNG


IHDR  �s��IDATx����0�{	�����|$����+�N/�b���7�;�	W!�g1�ŵs���z��������)��!�J+���SOi�p=\�G7zx�l�D�^H0B� ��	&؁-�a�@�;P@��>���%3�x�\K~r� ��XҍR2���`��Ul�p!$�R"�0п3��%�τEZ��ʯ�W��-�5�mm؆d�@i
"�8�Lj��'�O�0���EmЂ�=$�*�m&$HOM�P��O�t��D� X�_F+p���[!��*�r�0��1���n~#y)Rw6ӝ?y�6�p+����4)"���+�U)+��
��4L{�Gw;������~�i9ؑ/�<T�۱n��[~�_m�?VĄ�6�3\A�1�8|����'0B!O���z�x�5�M<IEND�B`�PK
!<!�$Bchrome/browser/skin/classic/browser/search-indicator-badge-add.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="11" height="11" viewBox="0 0 11 11">
  <circle fill="#fff" cx="5.5" cy="5.5" r="5.5"/>
  <circle fill="#12bc00" class="cls-2" cx="5.5" cy="5.5" r="4.5"/>
  <path fill="#fff" d="M7.5 6H6v1.5a.5.5 0 0 1-1 0V6H3.5a.5.5 0 0 1 0-1H5V3.5a.5.5 0 0 1 1 0V5h1.5a.5.5 0 0 1 0 1z"/>
</svg>
PK
!<8���  3chrome/browser/skin/classic/browser/searchReset.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

body {
  align-items: center;
}

.title {
  background-image: url("chrome://browser/skin/icon-search-64.svg");
}

#defaultEngine {
  padding-inline-start: 26px;
  background-repeat: no-repeat;
  background-position: 5px center;
  background-size: 16px, 16px;
}

#defaultEngine:dir(rtl) {
  background-position: calc(100% - 5px) center;
}
PK
!<��06chrome/browser/skin/classic/browser/send-to-device.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M12 0H4a2 2 0 0 0-2 2v2a1 1 0 0 0 1 1 1 1 0 0 0 1-1V2.5a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 .5.5v10a.5.5 0 0 1-.5.5h-7a.5.5 0 0 1-.5-.5V11a1 1 0 0 0-1-1 1 1 0 0 0-1 1v3a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zM9 15H7v-1h2z"/>
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M5.146 10.146a.5.5 0 1 0 .707.707l3-3a.5.5 0 0 0 0-.708l-3-3a.5.5 0 0 0-.707.707L7.293 7H.5a.5.5 0 0 0 0 1h6.793z"/>
</svg>
PK
!<���D99<chrome/browser/skin/classic/browser/setDesktopBackground.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
@namespace html url("http://www.w3.org/1999/xhtml");

html|canvas#screen {
  margin: 12px 11px 32px;
}

#monitor {
  list-style-image: url("chrome://browser/skin/monitor.png");
}

#monitor[aspectratio="16:10"] {
  list-style-image: url("chrome://browser/skin/monitor_16-10.png");
}
PK
!<��e��6chrome/browser/skin/classic/browser/sidebars-right.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16" fill="context-fill" fill-opacity="context-fill-opacity">
  <path d="M13 1H3a3.007 3.007 0 0 0-3 3v8a3.009 3.009 0 0 0 3 3h10a3.005 3.005 0 0 0 3-3V4a3.012 3.012 0 0 0-3-3zM2 12V4a1 1 0 0 1 1-1h5v10H3a1 1 0 0 1-1-1zm12 0a1 1 0 0 1-1 1H9V3h4a1 1 0 0 1 1 1z"/>
  <path d="M12.5 5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 0 1zm0 2h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 0 1zm-1 2h-1a.5.5 0 0 1 0-1h1a.5.5 0 0 1 0 1z"/>
</svg>
PK
!<p���6chrome/browser/skin/classic/browser/slowStartup-16.png�PNG


IHDR�a�IDATx��3`�a��ۥ�ڥk۶ms�m۶��v�Yb;9�_s�����
���hص�q����

GC�X��
�e���D��}�K@#(��\�j��]4펧�k�2���e�@�4��בeod���|�Y������OO{�.�a������-{+���!
8��%�ʦ�	��l	wAMF"%8�~G�����R8���R6��=�+>ᎌ�%x����r���A5D�O{,�K��w�Ex�&ܖ���>c/�#v3��3�>��
����dd#���#��!�\�F;�ޭ���VX�6�d�q��Z����^;�N��*yN�l�F�?�36
��ۼ�f>�\{e|��0p�	��g��{(�zlq*��x�K��	^��B��}sddf)��z^e3��IEND�B`�PK
!<ֽ�;�;6chrome/browser/skin/classic/browser/stop-to-reload.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="468" height="20" fill="context-fill" fill-opacity="context-fill-opacity">
  <path d="M13.55 8H11a1 1 0 0 0 0 2h5a1 1 0 0 0 1-1V4a1 1 0 0 0-2 0v2.418"/>
  <path d="M15 6.418C13.02 3.1 8.724 2.018 5.408 4c-3.317 1.98-4.4 6.275-2.42 9.592A6.997 6.997 0 0 0 9 17a6.954 6.954 0 0 0 4.95-2.05 1 1 0 1 0-1.414-1.414A5.02 5.02 0 1 1 13.55 8"/>
  <svg x="18">
    <path d="M13.735 8.606l-1.72-.78a.74.74 0 0 0-.61 1.35l3.37 1.53a.74.74 0 0 0 .98-.37l1.532-3.372a.74.74 0 0 0-1.35-.612l-.74 1.63"/>
    <path d="M15.91 8.954c-.58-3.82-4.146-6.446-7.965-5.867-3.82.58-6.447 4.144-5.868 7.964a6.997 6.997 0 0 0 4.275 5.43 6.954 6.954 0 0 0 5.357-.025 1 1 0 1 0-.775-1.844 5.02 5.02 0 1 1 3.032-4.74"/>
  </svg>
  <svg x="36">
    <path d="M14.088 9.318l-.89-.845a.482.482 0 0 0-.663.7l1.747 1.656a.482.482 0 0 0 .68-.02l1.657-1.746a.482.482 0 0 0-.7-.662l-.8.844"/>
    <path d="M15.79 11.646c.91-3.755-1.397-7.536-5.15-8.444-3.756-.91-7.537 1.396-8.446 5.15a6.997 6.997 0 0 0 1.903 6.644A6.954 6.954 0 0 0 9.067 17a1 1 0 1 0-.02-2 5.02 5.02 0 1 1 4.6-3.242"/>
  </svg>
  <svg x="54">
    <path d="M14.43 10.23l-.315-.47a.222.222 0 0 0-.37.247l.618.923a.222.222 0 0 0 .308.062l.925-.618a.222.222 0 0 0-.247-.37l-.447.3"/>
    <path d="M14.664 14.093c2.262-3.132 1.556-7.504-1.575-9.766C9.956 2.065 5.584 2.77 3.322 5.9a6.997 6.997 0 0 0-.752 6.87 6.954 6.954 0 0 0 3.843 3.735 1 1 0 1 0 .74-1.858 5.02 5.02 0 1 1 5.485-1.26"/>
  </svg>
  <svg x="72">
    <defs>
      <filter id="a">
        <feColorMatrix color-interpolation-filters="sRGB" type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 -1 1"/>
      </filter>
      <mask id="b" mask-type="alpha" fill="#fff">
        <g filter="url(#a)">
          <path d="M0 0h18v20H0z" opacity="0"/>
          <path d="M8.85 10.044l-1.465 8.154s4.614.676 4.642.584c.03-.092 3.69-3.364 3.69-3.364L8.85 10.044z"/>
        </g>
      </mask>
    </defs>
    <path d="M12.694 15.932c3.28-2.043 4.28-6.358 2.237-9.636-2.042-3.28-6.355-4.28-9.634-2.238A6.997 6.997 0 0 0 2 10.132a6.954 6.954 0 0 0 2.144 4.91 1 1 0 1 0 1.387-1.44 5.02 5.02 0 1 1 5.555.91" mask="url(#b)" opacity=".943"/>
  </svg>
  <svg x="90">
    <defs>
      <filter id="c">
        <feColorMatrix color-interpolation-filters="sRGB" type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 -1 1"/>
      </filter>
      <mask id="d" mask-type="alpha" fill="#fff">
        <g filter="url(#c)">
          <path d="M0 0h18v20H0z" opacity="0"/>
          <path d="M8.85 10.044L5.634 17.05s5.448 2.043 5.504 1.86c.057-.185 4.615-3.567 4.615-3.567l-6.903-5.3z"/>
        </g>
      </mask>
    </defs>
    <path d="M10.175 16.888c3.81-.65 6.368-4.265 5.717-8.073-.65-3.808-4.264-6.368-8.073-5.717A6.997 6.997 0 0 0 2.47 7.474a6.954 6.954 0 0 0 .125 5.356 1 1 0 1 0 1.83-.808 5.02 5.02 0 1 1 4.796 2.943" mask="url(#d)" opacity=".749"/>
  </svg>
  <svg x="108">
    <defs>
      <filter id="e">
        <feColorMatrix color-interpolation-filters="sRGB" type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 -1 1"/>
      </filter>
      <mask id="f" mask-type="alpha" fill="#fff">
        <g filter="url(#e)">
          <path d="M0 0h18v20H0z" opacity="0"/>
          <path d="M8.85 10.044L3.883 15.9s6.28 3.412 6.366 3.136c.084-.276 5.537-3.767 5.537-3.767L8.85 10.043z"/>
        </g>
      </mask>
    </defs>
    <path d="M7.482 16.82c3.77.84 7.508-1.54 8.346-5.31.84-3.77-1.538-7.507-5.31-8.346a6.997 6.997 0 0 0-6.605 2.028 6.954 6.954 0 0 0-1.91 5.005 1 1 0 1 0 2-.056 5.02 5.02 0 1 1 3.327 4.54" mask="url(#f)" opacity=".485"/>
  </svg>
  <svg x="126">
    <defs>
      <filter id="g">
        <feColorMatrix color-interpolation-filters="sRGB" type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 -1 1"/>
      </filter>
      <mask id="h" mask-type="alpha" fill="#fff">
        <g filter="url(#g)">
          <path d="M0 0h18v20H0z" opacity="0"/>
          <path d="M8.85 10.044L2.13 14.75s7.116 4.78 7.23 4.413c.113-.368 6.462-3.968 6.462-3.968l-6.972-5.15z"/>
        </g>
      </mask>
    </defs>
    <path d="M5.014 15.74c3.174 2.203 7.532 1.415 9.735-1.758 2.202-3.174 1.416-7.532-1.758-9.735a6.997 6.997 0 0 0-6.882-.623 6.954 6.954 0 0 0-3.662 3.91 1 1 0 1 0 1.872.705 5.02 5.02 0 1 1 1.363 5.46" mask="url(#h)" opacity=".256"/>
  </svg>
  <svg x="144">
    <defs>
      <mask id="k" mask-type="alpha" fill="#fff">
        <path d="M.31-4.362a6.07 6.07 0 0 1 6.067 6.067A6.07 6.07 0 0 1 .31 7.772a6.07 6.07 0 0 1-6.067-6.067A6.07 6.07 0 0 1 .31-4.362z"/>
      </mask>
      <filter id="i">
        <feColorMatrix color-interpolation-filters="sRGB" type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 -1 1"/>
      </filter>
      <mask id="j" mask-type="alpha" fill="#fff">
        <g filter="url(#i)">
          <path d="M0 0h18v20H0z" opacity="0"/>
          <path d="M8.85 10.044L.38 13.6s7.95 6.15 8.09 5.69c.142-.46 7.386-4.17 7.386-4.17L8.85 10.044z"/>
        </g>
      </mask>
    </defs>
    <path d="M3.14 13.805c2.103 3.24 6.436 4.16 9.675 2.056 3.24-2.103 4.16-6.435 2.057-9.675a6.997 6.997 0 0 0-6.135-3.18 6.954 6.954 0 0 0-4.87 2.235A1 1 0 1 0 5.333 6.6a5.02 5.02 0 1 1-.804 5.57" mask="url(#j)" opacity=".104"/>
    <g mask="url(#k)">
      <path d="M8.12 10.527l6.173 6.18a1 1 0 0 0 1.414-.025 1 1 0 0 0 0-1.39l-6.173-6.18"/>
      <path d="M9.706 9.268l-6-5.975a1 1 0 0 0-1.413.024 1 1 0 0 0 0 1.39l6 5.975"/>
    </g>
  </svg>
  <svg x="162">
    <defs>
      <mask id="n" mask-type="alpha" fill="#fff">
        <path d="M.556-8.94A10.648 10.648 0 0 1 11.2 1.705 10.648 10.648 0 0 1 .557 12.35 10.648 10.648 0 0 1-10.09 1.705 10.648 10.648 0 0 1 .557-8.94z"/>
      </mask>
      <filter id="l">
        <feColorMatrix color-interpolation-filters="sRGB" type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 -1 1"/>
      </filter>
      <mask id="m" mask-type="alpha" fill="#fff">
        <g filter="url(#l)">
          <path d="M0 0h18v20H0z" opacity="0"/>
          <path d="M8.85 10.044l-9.2 1.394s7.603 8.78 7.773 8.227c.17-.553 8.467-4.62 8.467-4.62l-7.04-5z"/>
        </g>
      </mask>
    </defs>
    <path d="M2.135 11.305c.722 3.795 4.384 6.286 8.18 5.563 3.795-.72 6.286-4.383 5.564-8.178a6.997 6.997 0 0 0-4.477-5.265 6.954 6.954 0 0 0-5.353.227 1 1 0 1 0 .843 1.813 5.02 5.02 0 1 1-2.852 4.852" mask="url(#m)" opacity=".023"/>
    <path d="M9.706 9.268l-6-5.975a1 1 0 0 0-1.413.024 1 1 0 0 0 0 1.39l6 5.975" mask="url(#n)"/>
  </svg>
  <svg x="180">
    <defs>
      <mask id="o" mask-type="alpha" fill="#fff">
        <path d="M.728-12.144a13.854 13.854 0 0 1 13.85 13.85A13.854 13.854 0 0 1 .727 15.553a13.854 13.854 0 0 1-13.85-13.85A13.854 13.854 0 0 1 .73-12.143z"/>
      </mask>
    </defs>
    <g mask="url(#o)">
      <path d="M8.12 10.527l6.173 6.18a1 1 0 0 0 1.414-.025 1 1 0 0 0 0-1.39l-6.173-6.18"/>
      <path d="M9.706 9.268l-6-5.975a1 1 0 0 0-1.413.024 1 1 0 0 0 0 1.39l6 5.975"/>
    </g>
  </svg>
  <svg x="198">
    <defs>
      <mask id="p" mask-type="alpha" fill="#fff">
        <path d="M.842-14.252A15.963 15.963 0 0 1 16.8 1.705 15.963 15.963 0 0 1 .84 17.662 15.963 15.963 0 0 1-15.115 1.705 15.963 15.963 0 0 1 .842-14.252z"/>
      </mask>
    </defs>
    <g mask="url(#p)">
      <path d="M8.12 10.527l6.173 6.18a1 1 0 0 0 1.414-.025 1 1 0 0 0 0-1.39l-6.173-6.18"/>
      <path d="M9.706 9.268l-6-5.975a1 1 0 0 0-1.413.024 1 1 0 0 0 0 1.39l6 5.975"/>
    </g>
  </svg>
  <svg x="216">
    <defs>
      <mask id="q" mask-type="alpha" fill="#fff">
        <path d="M.922-15.747A17.458 17.458 0 0 1 18.374 1.705 17.458 17.458 0 0 1 .922 19.157 17.458 17.458 0 0 1-16.53 1.705 17.458 17.458 0 0 1 .922-15.747z"/>
      </mask>
    </defs>
    <g mask="url(#q)">
      <path d="M8.12 10.527l6.173 6.18a1 1 0 0 0 1.414-.025 1 1 0 0 0 0-1.39l-6.173-6.18"/>
      <path d="M9.706 9.268l-6-5.975a1 1 0 0 0-1.413.024 1 1 0 0 0 0 1.39l6 5.975"/>
    </g>
  </svg>
  <svg x="234">
    <defs>
      <mask id="r" mask-type="alpha" fill="#fff">
        <path d="M.982-16.86A18.57 18.57 0 0 1 19.546 1.706 18.57 18.57 0 0 1 .982 20.27 18.57 18.57 0 0 1-17.582 1.704 18.57 18.57 0 0 1 .982-16.86z"/>
      </mask>
    </defs>
    <g mask="url(#r)">
      <path d="M8.12 10.527l6.173 6.18a1 1 0 0 0 1.414-.025 1 1 0 0 0 0-1.39l-6.173-6.18"/>
      <path d="M9.706 9.268l-6-5.975a1 1 0 0 0-1.413.024 1 1 0 0 0 0 1.39l6 5.975"/>
    </g>
  </svg>
  <svg x="252">
    <defs>
      <mask id="s" mask-type="alpha" fill="#fff">
        <path d="M1.027-17.71A19.42 19.42 0 0 1 20.44 1.706 19.42 19.42 0 0 1 1.028 21.12 19.42 19.42 0 0 1-18.387 1.704 19.42 19.42 0 0 1 1.027-17.71z"/>
      </mask>
    </defs>
    <g mask="url(#s)">
      <path d="M8.12 10.527l6.173 6.18a1 1 0 0 0 1.414-.025 1 1 0 0 0 0-1.39l-6.173-6.18"/>
      <path d="M9.706 9.268l-6-5.975a1 1 0 0 0-1.413.024 1 1 0 0 0 0 1.39l6 5.975"/>
    </g>
  </svg>
  <svg x="270">
    <defs>
      <mask id="u" mask-type="alpha" fill="#fff">
        <path d="M.46 12.297a6.327 6.327 0 0 1 6.325 6.325A6.327 6.327 0 0 1 .46 24.947a6.327 6.327 0 0 1-6.325-6.325A6.327 6.327 0 0 1 .46 12.297z"/>
      </mask>
      <mask id="t" mask-type="alpha" fill="#fff">
        <path d="M1.062-18.366a20.077 20.077 0 0 1 20.07 20.07 20.077 20.077 0 0 1-20.07 20.072 20.077 20.077 0 0 1-20.07-20.07A20.077 20.077 0 0 1 1.06-18.367z"/>
      </mask>
    </defs>
    <g mask="url(#t)">
      <path d="M8.12 10.527l6.173 6.18a1 1 0 0 0 1.414-.025 1 1 0 0 0 0-1.39l-6.173-6.18"/>
      <path d="M9.706 9.268l-6-5.975a1 1 0 0 0-1.413.024 1 1 0 0 0 0 1.39l6 5.975"/>
    </g>
    <path d="M7.71 9.883l-5.417 5.41a1 1 0 0 0-.025 1.414 1 1 0 0 0 1.44 0l5.417-5.41m1.2-1.152l5.38-5.438a1 1 0 0 0-.023-1.414 1 1 0 0 0-1.39 0L8.912 8.73" mask="url(#u)"/>
  </svg>
  <svg x="288">
    <defs>
      <mask id="w" mask-type="alpha" fill="#fff">
        <path d="M.704 7.752a10.874 10.874 0 0 1 10.87 10.87 10.874 10.874 0 0 1-10.87 10.87 10.874 10.874 0 0 1-10.87-10.87A10.874 10.874 0 0 1 .704 7.752z"/>
      </mask>
      <mask id="v" mask-type="alpha" fill="#fff">
        <path d="M1.09-18.874a20.586 20.586 0 0 1 20.58 20.58A20.586 20.586 0 0 1 1.09 22.283a20.586 20.586 0 0 1-20.58-20.58A20.586 20.586 0 0 1 1.09-18.873z"/>
      </mask>
    </defs>
    <g mask="url(#v)">
      <path d="M8.12 10.527l6.173 6.18a1 1 0 0 0 1.414-.025 1 1 0 0 0 0-1.39l-6.173-6.18"/>
      <path d="M9.706 9.268l-6-5.975a1 1 0 0 0-1.413.024 1 1 0 0 0 0 1.39l6 5.975"/>
    </g>
    <path d="M7.71 9.883l-5.417 5.41a1 1 0 0 0-.025 1.414 1 1 0 0 0 1.44 0l5.417-5.41m1.2-1.152l5.38-5.438a1 1 0 0 0-.023-1.414 1 1 0 0 0-1.39 0L8.912 8.73" mask="url(#w)"/>
  </svg>
  <svg x="306">
    <defs>
      <mask id="y" mask-type="alpha" fill="#fff">
        <path d="M.87 4.63A13.998 13.998 0 0 1 14.865 18.62 13.998 13.998 0 0 1 .87 32.615 13.998 13.998 0 0 1-13.12 18.622 13.998 13.998 0 0 1 .87 4.63z"/>
      </mask>
      <mask id="x" mask-type="alpha" fill="#fff">
        <path d="M1.11-19.264a20.976 20.976 0 0 1 20.97 20.97A20.976 20.976 0 0 1 1.11 22.673a20.976 20.976 0 0 1-20.97-20.97A20.976 20.976 0 0 1 1.11-19.263z"/>
      </mask>
    </defs>
    <g mask="url(#x)">
      <path d="M8.12 10.527l6.173 6.18a1 1 0 0 0 1.414-.025 1 1 0 0 0 0-1.39l-6.173-6.18"/>
      <path d="M9.706 9.268l-6-5.975a1 1 0 0 0-1.413.024 1 1 0 0 0 0 1.39l6 5.975"/>
    </g>
    <path d="M7.71 9.883l-5.417 5.41a1 1 0 0 0-.025 1.414 1 1 0 0 0 1.44 0l5.417-5.41m1.2-1.152l5.38-5.438a1 1 0 0 0-.023-1.414 1 1 0 0 0-1.39 0L8.912 8.73" mask="url(#y)"/>
  </svg>
  <svg x="324">
    <defs>
      <mask id="z" mask-type="alpha" fill="#fff">
        <path d="M.982 2.565A16.062 16.062 0 0 1 17.04 18.622 16.062 16.062 0 0 1 .98 34.68 16.062 16.062 0 0 1-15.075 18.62 16.062 16.062 0 0 1 .982 2.565z"/>
      </mask>
    </defs>
    <path d="M10.414 10l5.293-5.293a1 1 0 0 0-1.414-1.414L9 8.586 3.707 3.293a1 1 0 0 0-1.414 1.414L7.586 10l-5.293 5.293a1 1 0 1 0 1.414 1.414L9 11.414l5.293 5.293a1 1 0 0 0 1.414-1.414z" mask="url(#z)"/>
  </svg>
  <svg x="342">
    <defs>
      <mask id="A" mask-type="alpha" fill="#fff">
        <path d="M1.06 1.097a17.53 17.53 0 0 1 17.525 17.525A17.53 17.53 0 0 1 1.06 36.147a17.53 17.53 0 0 1-17.525-17.525A17.53 17.53 0 0 1 1.06 1.097z"/>
      </mask>
    </defs>
    <path d="M10.414 10l5.293-5.293a1 1 0 0 0-1.414-1.414L9 8.586 3.707 3.293a1 1 0 0 0-1.414 1.414L7.586 10l-5.293 5.293a1 1 0 1 0 1.414 1.414L9 11.414l5.293 5.293a1 1 0 0 0 1.414-1.414z" mask="url(#A)"/>
  </svg>
  <svg x="360">
    <defs>
      <mask id="B" mask-type="alpha" fill="#fff">
        <path d="M1.12.003a18.626 18.626 0 0 1 18.62 18.62A18.626 18.626 0 0 1 1.12 37.24 18.626 18.626 0 0 1-17.5 18.623 18.626 18.626 0 0 1 1.12.002z"/>
      </mask>
    </defs>
    <path d="M10.414 10l5.293-5.293a1 1 0 0 0-1.414-1.414L9 8.586 3.707 3.293a1 1 0 0 0-1.414 1.414L7.586 10l-5.293 5.293a1 1 0 1 0 1.414 1.414L9 11.414l5.293 5.293a1 1 0 0 0 1.414-1.414z" mask="url(#B)"/>
  </svg>
  <svg x="378">
    <defs>
      <mask id="C" mask-type="alpha" fill="#fff">
        <path d="M1.164-.834A19.463 19.463 0 0 1 20.62 18.622 19.463 19.463 0 0 1 1.164 38.078a19.463 19.463 0 0 1-19.456-19.456A19.463 19.463 0 0 1 1.164-.834z"/>
      </mask>
    </defs>
    <path d="M10.414 10l5.293-5.293a1 1 0 0 0-1.414-1.414L9 8.586 3.707 3.293a1 1 0 0 0-1.414 1.414L7.586 10l-5.293 5.293a1 1 0 1 0 1.414 1.414L9 11.414l5.293 5.293a1 1 0 0 0 1.414-1.414z" mask="url(#C)"/>
  </svg>
  <svg x="396">
    <defs>
      <mask id="D" mask-type="alpha" fill="#fff">
        <path d="M1.2-1.482a20.11 20.11 0 0 1 20.104 20.104A20.11 20.11 0 0 1 1.2 38.726a20.11 20.11 0 0 1-20.104-20.104A20.11 20.11 0 0 1 1.2-1.482z"/>
      </mask>
    </defs>
    <path d="M10.414 10l5.293-5.293a1 1 0 0 0-1.414-1.414L9 8.586 3.707 3.293a1 1 0 0 0-1.414 1.414L7.586 10l-5.293 5.293a1 1 0 1 0 1.414 1.414L9 11.414l5.293 5.293a1 1 0 0 0 1.414-1.414z" mask="url(#D)"/>
  </svg>
  <svg x="414">
    <defs>
      <mask id="E" mask-type="alpha" fill="#fff">
        <path d="M1.226-1.982A20.61 20.61 0 0 1 21.83 18.622 20.61 20.61 0 0 1 1.226 39.226a20.61 20.61 0 0 1-20.604-20.604A20.61 20.61 0 0 1 1.226-1.982z"/>
      </mask>
    </defs>
    <path d="M10.414 10l5.293-5.293a1 1 0 0 0-1.414-1.414L9 8.586 3.707 3.293a1 1 0 0 0-1.414 1.414L7.586 10l-5.293 5.293a1 1 0 1 0 1.414 1.414L9 11.414l5.293 5.293a1 1 0 0 0 1.414-1.414z" mask="url(#E)"/>
  </svg>
  <svg x="432">
    <mask id="F" mask-type="alpha" fill="#fff">
      <path d="M1.15-20.02A21.733 21.733 0 0 1 22.878 1.704 21.733 21.733 0 0 1 1.15 23.43 21.733 21.733 0 0 1-20.574 1.706 21.733 21.733 0 0 1 1.15-20.02z"/>
    </mask>
    <path d="M10.414 10l5.293-5.293a1 1 0 0 0-1.414-1.414L9 8.586 3.707 3.293a1 1 0 0 0-1.414 1.414L7.586 10l-5.293 5.293a1 1 0 1 0 1.414 1.414L9 11.414l5.293 5.293a1 1 0 0 0 1.414-1.414z" mask="url(#F)"/>
  </svg>
  <svg x="450">
    <path d="M10.414 10l5.293-5.293a1 1 0 0 0-1.414-1.414L9 8.586 3.707 3.293a1 1 0 0 0-1.414 1.414L7.586 10l-5.293 5.293a1 1 0 1 0 1.414 1.414L9 11.414l5.293 5.293a1 1 0 0 0 1.414-1.414z" mask="url(#F)"/>
  </svg>
</svg>

PK
!<���8chrome/browser/skin/classic/browser/sync-desktopIcon.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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"
     width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" d="M15,14H1a1,1,0,0,1-1-1V12.526H16V13A1,1,0,0,1,15,14ZM1,4A1,1,0,0,1,2,3H14a1,1,0,0,1,1,1v8H1V4Zm1,7H14V4H2v7Z"/>
  <rect fill="context-fill" fill-opacity="0.15" x="2" y="4" width="12" height="7"/>
</svg>
PK
!<Hm|EE7chrome/browser/skin/classic/browser/sync-mobileIcon.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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"
     width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" d="M12,16H4a1,1,0,0,1-1-1V1A1,1,0,0,1,4,0h8a1,1,0,0,1,1,1V15A1,1,0,0,1,12,16Zm-4-.684a0.785,0.785,0,1,0-.785-0.785A0.785,0.785,0,0,0,8,15.316ZM12,2H4V13h8V2Z"/>
  <rect fill="context-fill" fill-opacity="0.15" x="4" y="2" width="8" height="11"/>
</svg>
PK
!<Q���,chrome/browser/skin/classic/browser/sync.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M14 1a1 1 0 0 0-1 1v1.146A6.948 6.948 0 0 0 1.227 6.307a1 1 0 1 0 1.94.484A4.983 4.983 0 0 1 8 3a4.919 4.919 0 0 1 3.967 2H10a1 1 0 0 0 0 2h4a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zm.046 7.481a1 1 0 0 0-1.213.728A4.983 4.983 0 0 1 8 13a4.919 4.919 0 0 1-3.967-2H6a1 1 0 0 0 0-2H2a1 1 0 0 0-1 1v4a1 1 0 0 0 2 0v-1.146a6.948 6.948 0 0 0 11.773-3.161 1 1 0 0 0-.727-1.212z"/>
</svg>
PK
!<�jRR:chrome/browser/skin/classic/browser/syncedtabs/sidebar.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


/* These styles are intended to mimic XUL trees and the XUL search box. */

html {
  height: 100%;
}

body {
  height: 100%;
  margin: 0;
  font: message-box;
  color: #333333;
  -moz-user-select: none;
}

/* The content-container holds the non-scrollable header and the scrollable
   content area.
*/
.content-container {
  display: flex;
  flex-flow: column;
  height: 100%;
}

/* The content header is not scrollable */
.content-header {
  flex: 0 1 auto;
}

/* The main content area is scrollable and fills the rest of the area */
.content-scrollable {
  flex: 1 1 auto;
  overflow: auto;
}

.emptyListInfo {
  cursor: default;
  padding: 3em 1em;
  text-align: center;
}

.list,
.item-tabs-list {
  display: flex;
  flex-flow: column;
  flex-grow: 1;
}

.item.client {
  opacity: 1;
  max-height: unset;
  display: unset;
}

.item.client.closed .item-tabs-list {
  display: none;
}

.item {
  display: inline-block;
  opacity: 1;
  flex: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  outline: none;
  color: -moz-FieldText;
}

.item.selected > .item-title-container {
  background-color: -moz-cellhighlight;
  color: -moz-cellhighlighttext;
}

.item.selected:focus > .item-title-container {
  background-color: Highlight;
  color: HighlightText;
}

.client .item.tab > .item-title-container {
  padding-inline-start: 35px;
}

.item.tab > .item-title-container {
  padding-inline-start: 20px;
}

.item.client.device-image-desktop > .item-title-container > .item-icon-container {
  background-image: url("chrome://browser/skin/sync-desktopIcon.svg");
  -moz-context-properties: fill;
  fill: #4d4d4d;
}

.item.client.device-image-desktop.selected:focus > .item-title-container > .item-icon-container {
  fill: white;
}

.item.client.device-image-mobile > .item-title-container > .item-icon-container {
  background-image: url("chrome://browser/skin/sync-mobileIcon.svg");
  -moz-context-properties: fill;
  fill: #4d4d4d;
}

.item.client.device-image-mobile.selected:focus > .item-title-container > .item-icon-container {
  fill: white;
}

.item.tab > .item-title-container > .item-icon-container {
  background-image: url("chrome://mozapps/skin/places/defaultFavicon.svg");
  -moz-context-properties: fill;
  fill: currentColor;
}

.item-icon-container {
  min-width: 16px;
  max-width: 16px;
  min-height: 16px;
  max-height: 16px;
  margin-right: 5px;
  margin-left: 5px;
  background-size: 16px 16px;
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
}

.item-title-container {
  display: flex;
  flex-flow: row;
  overflow: hidden;
  flex-grow: 1;
  padding: 4px;
}

.item-title {
  flex-grow: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  margin: 0px;
  line-height: 1.3;
  cursor: default;
}

.item[hidden] {
  opacity: 0;
  max-height: 0;
  transition: opacity 150ms ease-in-out, max-height 150ms ease-in-out 150ms;
}

.item.empty .item-title-container {
  color: #aeaeae;
}

.client .item.empty > .item-title-container {
  padding-inline-start: 35px;
}

.text-input-box {
  display: flex;
  flex-flow: row nowrap;
}

.textbox-input-box {
  display: flex;
  flex-direction: row;
}

.tabsFilter {
  flex: 1;
  /* min-width of anything to override the implicit "-moz-min-content" value.
     0px is safe as the sidebar itself has a constrained size meaning we will
     never actually hit this minimum
  */
  min-width: 0px;
}

.sync-state > p {
  padding-inline-end: 10px;
  padding-inline-start: 10px;
  color: #888;
}

.text-link {
  color: rgb(0, 149, 221);
  cursor: pointer;
}

.text-link:hover {
  text-decoration: underline;
}

.text-link,
.text-link:focus {
  margin: 0px;
  padding: 0px;
  border: 0px;
}

.deck .sync-state {
  display: none;
  opacity: 0;
  transition: opacity 1.5s;
  border-top: 1px solid #bdbdbd;
}

.deck .sync-state.tabs-container {
  border-top: 0px;
}

.deck .sync-state.selected {
  display: unset;
  opacity: 100;
}

.deck .syncIllustration,
.deck .syncIllustrationIssue {
  height: 91px;
  margin: 38px 8px 15px;
  background-position: center;
  background-repeat: no-repeat;
}

.deck .syncIllustration {
  background-image: url(chrome://browser/skin/fxa/sync-illustration.svg);
}

.deck .syncIllustrationIssue {
  background-image: url(chrome://browser/skin/fxa/sync-illustration-issue.svg);
}

.deck .instructions {
  text-align: center;
  color: rgba(12, 12, 13, .5);
  padding: 0 11px;
  max-width: 15em;
  margin: 0 auto;
}

.deck .button {
  display: block;
  background-color: #0060df;
  color: white;
  border: 0;
  border-radius: 2px;
  margin: 15px auto;
  padding: 8px;
  text-shadow: none;
  width: calc(100% - 22px);
  max-width: 200px;
  text-overflow: ellipsis;
  white-space: nowrap;
  overflow: hidden;
}

.deck .button:hover {
  background-color: #003eaa;
}

.deck .button:hover:active {
  background-color: #002275;
}

.sidebar-search-container.tabs-container:not(.selected) {
  display: none;
}

.textbox-search-clear:not([disabled]) {
  cursor: default;
}

.textbox-search-icons .textbox-search-clear,
.filtered .textbox-search-icons .textbox-search-icon {
  display: none;
}

.filtered .textbox-search-icons .textbox-search-clear {
  display: block;
}

/* These styles are intended to mimic XUL trees and the XUL search box. */

html {
  background-color: -moz-Field;
  color: -moz-FieldText;
  box-sizing: border-box;
}

.item {
  padding-inline-end: 0;
}

.item-title {
  margin: 1px 0 0;
  margin-inline-end: 6px;
}


.search-box {
  -moz-appearance: textfield;
  cursor: text;
  margin: 4px;
}

.textbox-search-clear {
  background-image: url(moz-icon://stock/gtk-clear?size=menu);
  background-repeat: no-repeat;
  width: 16px;
  height: 16px;
}

.search-box > .textbox-input-box {
  background-image: url(chrome://global/skin/icons/search-textbox.svg);
  background-repeat: no-repeat;
  background-size: 12px 12px;
  background-position: left center;
  padding-inline-start: 14px;
}

.search-box > .textbox-input-box:-moz-locale-dir(rtl) {
  background-position: right center;
}

.textbox-search-clear:not([disabled]) {
  cursor: pointer;
}

.item.client .item-twisty-container {
  -moz-appearance: treetwistyopen;
  margin-top: 3px;
  margin-left: 2px;
}

.item.client.closed .item-twisty-container {
  -moz-appearance: treetwisty;
}
PK
!<�]���3chrome/browser/skin/classic/browser/tab-crashed.svg<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 40 40">
  <defs>
    <linearGradient id="gradient" gradientUnits="userSpaceOnUse" x1="20" y1="2.85" x2="20" y2="37.15">
      <stop offset="0" stop-color="#e63b2e"/>
      <stop offset="1" stop-color="#c33931"/>
    </linearGradient>
  </defs>
  <path fill-rule="evenodd" clip-rule="evenodd" fill="url(#gradient)" d="M39.048,6.798H19.004 C16.715,6.782 16.195,5.656 15.839,4.397 15.48,3.128 14.763,2 12.61,2 6.835,2 7.1899998,2 6.443,2 4.29,2 3.573,3.127 3.214,4.397 2.855,5.666 2.332,6.8 0,6.8H0.94 C0.421,6.808 0.003,7.233 0.003,7.758v27.583c0,0.53 0.426,0.959 0.952,0.959H39.048 C39.574,36.3 40,35.871 40,35.341 V 7.757 C40,7.227 39.574,6.798 39.048,6.798 Z M8.4409998,17.082c0,-2.119 1.7050002,-3.837 3.8090002,-3.837 2.103,0 3.809,1.718 3.809,3.837 0,2.119 -1.705,3.837 -3.809,3.837 -2.104,0 -3.8090002,-1.718 -3.8090002,-3.837z M26.717,30.98c-1.525,0 -1.525,-2.305 -6.864,-2.305 -5.339,0 -5.339,2.305 -6.864,2.305 -0.842,0 -1.526,-0.512 -1.526,-1.537 0,-1.024 1.271,-3.842 8.39,-3.842 7.119,0 8.39,2.804 8.39,3.842 0,1.038 -0.683,1.537 -1.526,1.537z m 0.768,-10.061c-2.104,0 -3.809,-1.718 -3.809,-3.837 0,-2.119 1.705,-3.837 3.809,-3.837 2.104,0 3.809,1.718 3.809,3.837 0,2.119 -1.706,3.837 -3.809,3.837z"/>
</svg>
PK
!<�����+chrome/browser/skin/classic/browser/tab.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M15 11h-1V5a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v6H1a1 1 0 0 0 0 2h14a1 1 0 1 0 0-2z"/>
</svg>
PK
!<��``:chrome/browser/skin/classic/browser/tabbrowser/crashed.svg<?xml version="1.0" encoding="utf-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 xmlns="http://www.w3.org/2000/svg" viewBox="22 22 16 16">
  <defs>
    <linearGradient id="gradient" gradientUnits="userSpaceOnUse" x1="30" y1="23" x2="30" y2="37">
      <stop offset="0" style="stop-color: #e63b2e"/>
      <stop offset="1" style="stop-color: #c33931"/>
    </linearGradient>
  </defs>
  <circle fill="url(#gradient)" cx="30" cy="30" r="7"/>
  <path fill="#fff" d="M31.03,33.304c0,0.6-0.479,1.092-1.091,1.092c-0.6,0-1.079-0.492-1.079-1.092 c0-0.588,0.479-1.079,1.079-1.079C30.551,32.225,31.03,32.716,31.03,33.304z M29.171,31.133l-0.24-5.253h2.015l-0.24,5.253H29.171z"/>
</svg>
PK
!<�8>�Jchrome/browser/skin/classic/browser/tabbrowser/indicator-tab-attention.svg<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12">
  <circle r="6" cy="6" cx="6" fill-opacity=".2" fill="#00C8D7" />
  <circle r="4" cy="6" cx="6" fill-opacity=".6" fill="#00C8D7"  />
  <circle r="2" cy="6" cx="6" fill="#00FEFF" />
</svg>PK
!<ȩS�'�'?chrome/browser/skin/classic/browser/tabbrowser/pendingpaint.png�PNG


IHDR<<���acTLL�-tRNSv��8fcTL<<
h;>,~IDATHǥ��V�H�y�7��u��=8�Ӏڲ��Ҥ�CȞ�ʏ��,�����1`U�ܵnUjW
(7?T�|\���F���r	�\܌v{�.›)�l/(�^�B�	���`���.삱5�t�������1���l�nOۭ*al]
�����jN�v�}��՚û�pؿ�w���N�5��5��-�7�+a�-�EfT�ȃm���U<����o��V�sֵ�;E2'��k\6;1��	���H�0�5|FF
�k��_~��ZJ�QQH2�{8 �¯���D<#ij�
Ihظ�ջ鳕i���k�(��l�v��h�di�x�;n���k���F��x�86������y��n��>ד5���f�E˥F�j2�L&�;٧8���o�W��x~:es�3���<:jӔS��-�'��L�����P�h�$M�/_�
�6�~j6�{>1|�b�U�i8"�|�h�_�ՙF;^>����9q��y�!���h=j4�-b\�3Շ�Ƿ�~���,�	lU�����麢ia��AГ������_I�d\�K�q���7K����x�UvK�:튐T6�[�6+^�����q�{–�\�IYl��a��B>k>,�=�Bm�4�ȃ�U�/��R�^}^fOy�0��O�D���>w>
�S9`W��?b����/{{{��s�'X2�e��{��4`��T}�H��ņ.v��b[��J��K���(�K�Ր��� `"$�^	/`L�"��*֕D:onl���Z�¢�W��&�T���d�m�"~�ٞ��."��|�YL<&��h�頫�u?k�*E��� �to;�$��n✜�f{���1�2K�!H��KI0��.%F�n�T�h��6����Nc�p���46�o7�J7s"�:!��릩��3�iD�(w�QD�ڤ*��	,urZ��  �lJ�f~�D^��X,�QH�?WT!��T�K�T6���I�Yl�9�����n3!Ռ2�`�����,�6|�(���Ф����N�[	G�֔(P�
}�q��.+��e�1�lUx��du�e���,U>�ZS�6�J��Uu�=�X�p���ޫ���lw_演��D��R���:�>�S�.^�fcTL<<
�O�n�fdATH��Ys�@�����)�JTtTБ�EA'��Ÿ�FL�&U�V�#$3O9>�E}}N�{�68�^�:>5���'�Ţ`xf['�_�WZ��+j?V%m����B�
~���r�Sg��ᲂ<�H�(��3z�9�˻��jY�ʵ���fy>[v��X��w�nb��ݻnױ�([���̽{�ഊ���}�r���&֕پ�0���O��L�붽[0�T���N���{�f��j�l`_��l��<�j��~�5\Y�C�<<(���kb,>�/�R�Xǹ���j����Q,�ݙ�~!�q�!Z5|Tbl<c�'i��Z�Z�Y����j���4 �cl2ɦh�%ZQ��b���
�Ih��,acכg��MpUwme��ƓX񔸳���T��\����j�)����Uw�W�x����%�L�:@Z��mv,��o6��S����
j������،�G�u�r�*���mW��½��B��D�z2��N���\��K���j�x5��?�]Ǹ�Rs]���9V4z�_g�%Lx���������a���e��0�-����ߺ�|eJ:O��'�#�t|�й�\�Ɏ&DS�����9;|�
�4
\(�x_)������t:��	)�a�D�Kf���N2s�u�hRb�y��g��`(�j���T8��5����:�x���G�H��7#b�$��vfcTL9:
�K�fdATH�ŕkS�P���?�o�Ųhhy)D�����"h`3goPK�pΚq``=�k���-�D��q$E���f�y����SZ�Vk�.$��b{�vK�J7��.'ڡ�;�0�J$9�Y�MF>B�%G���<Jr�q�5�45�f�ZT��r�T!��a�Φ����AU�')�f&F$AѾ�G��
$�HI8��fJ>v�
ɮ����:q��v5_a{�v���os�Y�%���fC7#����:�հ֨!���]�E��z_��&�|[n��*��آ �f�;gvY��g۪\���麱�A/$A�',��� MA1�7^svL9�?��Z��-Y���`�*d���3wo�\P��������Ӷ&T�jص�A����lӮ�T<�t;j^�U�#�����1��(�(�}�J0�����%�+HV�nЪ$X�]7$a>+�DY�����v*KN�1���Q�9��yzl#�#�m$�d������qv��ҵ�}9/_�˗��%*p��-��v�C�37�
��*�}��1�d=�i=��a8������^,1�O�U�~��,V&��vH�b>���c�[�w:��_�@��{;j6�_5��f�g(���Qn%b�\U�.��ђ�~��<�/dɅ�6Đ<)���d>�o��Zhf܉��|>{1�3>գ@�)���2�\|���b�P�P^
�N�P����2@�7)N�� �?'u��߹L�L���Uc�Tǐ%�)�>w���Ә�#x�
fcTL<<
�E��fdATH�ŖyW�P��(MVG��5���TDkຠ����sqI��.�3�z<Dž���.�E�3�4��b�T���� [���h(Cx6��+�����g�6�-�`c��nJ�A;oC�
�TvTAj�xڂ�a�#I��JH���V�޾Pn�$<;a�܂�'����W��|��ge��Ѷb�6�CdZ�N�z>
�n�aK
\w�Ǥd�߇_�j)�h؂�v��z]FC�HW���i,ܛ�!i}9n��F����+�J��P
v0)��C���
�X���V�c9��М����_�J.ϋ;�J,��\�<Ԯ���'�X]��+�\���7���/�a�P�-6��+�z.�y���;����c����҆%�%�q�ʲ�b��( v��O�����4�
��n��[�S�w۸}<>�v��i�����;fF�G���}7����t�y�:�n?�d:'�1����L&�����Gb��x���p��g�Y���VP+��~]���n5�\'��1���f�FTޞ�K��"�����2�f�8�cj�T*@عE�6
vu6�WA�*�œ�'UeJ�I��Y�����J�<!#�')�����|qq�~����T
c6<�/a�i��b��$�,��>{���`���rN�a�@�*`�>ہ[�$E�z߷�iU|�L����wa�~v�9Z#�!�Q��}�8M�~�]��ͅ������ب�X�fcTL<<
i���fdATH�ŗiS�@���]	aAv5�����$�Bp�pH0\B&[�3-f�Z�����g��@Dx���Ա-gF��m[�ݵَm���y2�;;xf�GR��0�V�{��@aN�4,��-�0t�C�Q��-w��f�3؟�m��X+�^Y�������p�K�o��Gc���E�ì#C���ѫ�<5u]7��9�WdE����E�l��VVTUQ�>�;�3�U,�
�����x�r���PUV����Y��Ϊ�n<c@�ѪaT��}Wk�b�M�jmYV�=R7���b�
�|Y۝]���*a�@_�Rue�s��
��e���^�O��(i.a�)'	�g�^hWR.��ĪC0z�z^DtN�jQ�c	�Ԥ<_��
����~������y�N��o���wV�I��l3�fQ@����v��?�p�������Ƿ�(���.
�W	�NpS�J�V�ly�����M��>�ؠ�t���xK�R��n9,�s�
Ãם\蝣�'y���9=͘��5��lq�YLX;�i*=��xx��#�(�)��f]a�|��xV82�~%N�y�a�e!��(��8�[4�k;��p�����[Ws����`^fx��F%~H%i*�/��0��,�]FLs�f��卲WeP;g1����$w�^��&����;ݾ���1��n5��l6m�=��;�l��/b���\�_�A��ۛ��dfcTL	::
�#�A%fdAT
H�Ŗkw�0����K�z��h+��bkEY� łQ��=gx�V�;'��'3�w2	g�ķ���TI��a׫�(I�"��)�$(��o"�Du#�B	��N���{�p5	mU�b�s�1��(AQYƨ��䚱���yv�
ʑ��RO�G�2B�ٮ��.��s�c,���Ik$t��z���-o�z��]��v��j@�J�b�)]�M��`__�O��CN;=Պ�jl�t�Q4t.����m���콁hc�l4������D����&�j���84�H蓇r>	4�`��#�i
�����T��P��/�'���[ڷ�����E�3��4��Ɨiۙ�//R�$���������}��t:���}]�A�ʰ,�����sV�M2邉�7�ͺ0���!�@`:�";Pԅی똥k��g�٭k2Q���y;z��K�I�Q���S�L]`���Y�,�͓,<T�m�n�L�+�%R�TU��U~bX���*A���6�F����_�~�.o��vjq���l�r���x���FPT���U{�5vU�LF�]%8B.��r2�V7_(����Hjxd2;�����-r�aܟ�R�\�p�"�;����u�ޢ�;�臓zC;m�-z�Gn�j��f28�ݘ[��CM���k�C9�N@$�Li��t��[|Am�X�H:#��j��!�h�
9%�'�((d��^�c�_*$Q
iN|?�|�㉪°+JWo�#q�e-��ׇ�+����8��Q˥��ĺ�e����7	z����fcTL</

]��W�fdATHǥ�ks�0��� ��`����ՀW���P/�Z��lB��U��|`␇��MΉ�	g�������I<�"�!}]Z:�4
i�ѿ�����dvn �b�ѩY/F��3p��C���y����W���X�4F*2���c��� v́���k8�#��=t�F5�>��A�h_$~��߄�HXo��x��T��Ǚ��^����T�h�0L�s?�N����q�Ӟ|Ͱ/m�Nx�D��{d֑��MG�y�e�R7�i����9B����x}I3ɔy0�$K��;_���$��ҡ�٩`��Ω�RI�ah�lR��2�%I���o�WCc��=ؿYos�ePV�W�x���r=��U2#�`5lB	�R��n�WK_���s�T^ٺM���>[�qI^�t�M1%�{Knpñl�7�#kp�e^��{�`}ә��U�ؤ(��{�FUiU�xyQ䛫�K�kfp&&��b��2�ǣN6�ϒ:
"�A�x�@=�a�t����b���Q�Hˋ��Hg2\��°g��6��T�g�8,��ʠ����c�^Ns���L���7��aa���*ÂhE�
��%���U�Y=Jzt���f�'a��3��*�I��R��^��NT��'N���&>������l���ѫ=�g7_,�5�ӭw۾b��ˊ�7�(W"���P�w(��fcTL
<0
��K�fdATHǽ��n�@��_�*�^�TQ��H��E+bQQ����24���k1���5!���93A�Q��������QX{��a1�U�#�ÒI�.���S�F)��d"�q8I��ű�b�a�]�O��Û�����y���B'nA�~��D�W��A8&���@z�,a�cj}�
�.h���I�0x���NZ�A��!�BJ������डJ�l�S6���a�� YvYYV��L�A3�{����dYv�藾i��-��}F�
�e�k�I���v�L�Zw�������Ƃ��=5�9���wy�D`q�e�[�Dh��?1��-�
�n7I�Yg[*�w�\�_\E�nC�./}�hL�
��ߦA�\�(�co)�q�:p��E�-��X�*yx[6;&8�D��*���*����5���n��pQ��s�!�>�$A���h2,�/����/�D�v<Zf�\���g�H�0`�f8���.v秔V�o��X�2F��W$t��e9��Y�"���ȠL8C��k4]sg�Z��v]��E;��<��ܜ���2�9(4�4n�X�@,���à�p,����X�ȣÍrB�Z ��iS���Ԭ��=GM"��\��9�r,���j�$޿����`�V��~���Wa`�l3�΁�6;�l5=�,�)���~�U���Z�Q>n���
������fcTL9:
�ӝS%fdATHǥ�
s�@�����HbmU�U�*�X5r

~���@gz�I�НɄ�{�w��[��a;����v�M�K���$�R�:�.ؙfR�T:ѫ���Uq]�#'S�tRpb�@dΈ�T'��t���v�
%%�9�BU�Bg�I7)%���o+��tW�I��KJ��*K5��Ё�d2q�\B�u}Meﺞ1����ח�Ig1Jš�W�N��l�{M �"�S��vB� ]���ϊ�ϒH3�aQw�ѾA�R�Y����H�xtb�!���:�[�
;�Z;�h~h�S���?qۉ��z���P(�z 
�28򅻞E�CL�@����%�m��n�5���6܍�͇3�Zt�ry��\��f�W���*���:���;_�]Ad�F�F�<Y��ٴU�\�\���:�]T�n�%��J��h~��
;���Bm�=��	-7'�5�տ�p=1��+�cG3o���K��wH��l��c&���r��(��Y�j�.�]�o~�a�֫}A$��3�Vc�k�����:��;�C��	CA�s��q����7TaX:�Ti���jk)�X�j���N��pJ�݋��졤�O�kOdn�
$����j��.M$���PyS��}���V�[�p�����ʙ�0gp��
��H�♗�#N��y�yT�G�C�U�aV?�?'Xac��s��Yg�R0!�+	&=Q�B���lw�U?]�e��8c�z���~�r�����'��%��s��}��V��e�r�%)���Z��YE��fcTL;<
��i�fdATHǭ��r�@���%�Ԋ�dM-�����x>��;�cJЙ�./��Ͽ�o� 8P]�-,�n�D"��fb��h�t>;�|^��W��U�\U��Ә�]A���f�X��bwM���� p�O����&�|��e#T�J��N�2�Џ�p��'�`!�+���3��$'�ꖽ>�3���b1��5����ˋ�P-�{�����X�K��=e2��*��aR�DYm�[w<�%�s���S#�&$Y��Te�����_�.!��3(q�R����I���(''0,�R�K=�˖�H�X\L懴�
�<��%)���_1�½(ɉ�=����J�X�%�f'�����D������Gån���,R�6d~�#U���whX��}��^�S��[xFp�����K��X5�Y=�h��<�F͜�j��]���nA%��dk���'U��sP2^a��bv�!4y?2�z�����<[��|�ZG���;Ύ���h�ɆDC�0�W_�_M˶���Z�ϙ�`��5�4����i���f-7��پ��5L,L<s��R��:Ƭm�O{^�MC�/�H�\���ľn/N�/�0���o��x'['�rhZ���!=�:^�˴��F��,41ԧ�(ĩ����]�,x%-�B�S�=�_Xh��;��2��j�!:�Nl��f��pDw��6l�9���7�Cx�^�vFa���~Y�,�ʣX>�N��q�*�ru��e�U�w뫲�×lfcTL<<
h����fdATH�Ֆks�@�����F%јITM���4��APDbt�3�Q�R�b�����}�eZ���1�I�ou������Φ��-ܞx�f��#��f��0��F��k>P)BZ�|���>��E*��"�)]��〲o{�R)*u5�Fخ�i:�𠼇]�NPT���
4�W=s�̡`�[	S�(a_r��mg|���&IQ��^��"6�����J^��C�Ρ�=�n�1�D�	!�
����9��*,���]۳���_����*R�W�����Z�N��j�
�C�Dg]EZ����.�p���0a8dX�}���t�|3��l��D*y5���Y�\fKծ�}��������ť�=��p^J�.��������㷣G�o=��2��2��F��먥��x⏒u���J�{xQ�eK����8�™�2L׍�/G���g
����1S�6M�k�C<uȄ�Z�U���e�����8k��ت�[\m����Q<V{m
/.�i5y��J�������M/Ua��|���+�sܔt�YC��%�;ǢW�
�;���KW�&���Z�$I];𮊮���̏-�dYR?X膡���h��XY�P�f����_h���v�U�9Ѱ`VY�r���ቷ&4�`�}b؆.��Q��p��š�݈lC�*��E��#�Hs�*���ߑ�*0k�pp���}D;q��b<�<T?;k��φ��fcTL<<
�añfdATHǽ�iw�@����D��U�M��]���Ņ ��9\0&Bd��	��{ߙ;s�W�����V�R��$��JgR����R�*�����p�
���4<�S�n�rt��4��	<�,���ӷ��i�$�sb����\u��3��9�"�w�=�h��G��I����kf!�ֻ��4ARx����sKw�+[��("9�?�;�k�N;��AQ�ߟ���B:�<��H���9o�1�ó薦��O$��7 ]�j�.G�������j�v�L�J�YQ,0��ۄ��8���Q�>-z�)2lg�[���3�o�f~[N��n�����E4BL�]��E�-2e���7��qj��Xu��֫x$�aј���i�7��kxZ�d���uZ���%����~�γ���c��s�����(���ӫ-�:�2�����D1���Z�����=����E��g:�t���b�ϸ-ơ�H��;'u��0�o��r�"
���`T�,�lJ\
,���w�l�ZU����)�tϣ�A�p�Q��T�W�pp�Vox��vH���M�v=��W�k[���K��]�K<Ǎ�PS�p<�u硛����A���Oa�Mء�b�d2�A9^Է�~�VB{�r�[���#���]u��9H��ٮ�t��/g��(Hg�>�v/Z/Á(
�ڜ~a5��-g24-��L\�"Z��ağlu]ˈ�
�2P܎��†[W4�\�� ƨ��L�K�[la��]_a����b�."ðtEXtSoftwareAPNG Assembler 2.9&[��IEND�B`�PK
!<"�e�Dchrome/browser/skin/classic/browser/tabbrowser/tab-audio-blocked.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16" fill="context-fill">
  <path d="M8 2a6 6 0 1 0 6 6 6.007 6.007 0 0 0-6-6zm0 11a5 5 0 1 1 5-5 5.006 5.006 0 0 1-5 5z"/>
  <path d="M6.75 4.969A.5.5 0 0 0 6 5.4v5.2a.5.5 0 0 0 .75.433l4.5-2.6a.5.5 0 0 0 0-.866z"/>
</svg>
PK
!<"ob��Bchrome/browser/skin/classic/browser/tabbrowser/tab-audio-muted.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16" fill="context-fill">
  <path d="M13 8a2.813 2.813 0 0 0-.465-1.535l-.744.744A1.785 1.785 0 0 1 12 8a2.008 2.008 0 0 1-1.4 1.848.5.5 0 0 0 .343.939A3 3 0 0 0 13 8z"/>
  <path d="M13.273 5.727A3.934 3.934 0 0 1 14 8a3.984 3.984 0 0 1-2.742 3.775.5.5 0 0 0 .316.949A4.985 4.985 0 0 0 15 8a4.93 4.93 0 0 0-1.012-2.988z"/>
  <path d="M8.67 13.717a.2.2 0 0 0 .33-.152V10l-2.154 2.154z"/>
  <path d="M14.707 1.293a1 1 0 0 0-1.414 0L9 5.586V2.544a.25.25 0 0 0-.413-.19L5.5 5H4.191A2.191 2.191 0 0 0 2 7.191v1.618a2.186 2.186 0 0 0 1.659 2.118l-2.366 2.366a1 1 0 1 0 1.414 1.414l12-12a1 1 0 0 0 0-1.414z"/>
</svg>
PK
!<��b���Dchrome/browser/skin/classic/browser/tabbrowser/tab-audio-playing.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16" fill="context-fill">
  <path d="M8.587 2.354L5.5 5H4.191A2.191 2.191 0 0 0 2 7.191v1.618A2.191 2.191 0 0 0 4.191 11H5.5l3.17 2.717a.2.2 0 0 0 .33-.152V2.544a.25.25 0 0 0-.413-.19z"/>
  <path d="M11.575 3.275a.5.5 0 0 0-.316.949 3.97 3.97 0 0 1 0 7.551.5.5 0 0 0 .316.949 4.971 4.971 0 0 0 0-9.449z"/>
  <path d="M13 8a3 3 0 0 0-2.056-2.787.5.5 0 1 0-.343.939A2.008 2.008 0 0 1 12 8a2.008 2.008 0 0 1-1.4 1.848.5.5 0 0 0 .343.939A3 3 0 0 0 13 8z"/>
</svg>
PK
!<��rzkkBchrome/browser/skin/classic/browser/tabbrowser/tab-audio-small.svg<?xml version="1.0" encoding="utf-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 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16">
  <style>
    .icon:not(:target) {
      display: none;
    }

    .icon {
      fill: #262626;
    }
    .icon > .outline {
      fill: #fff;
    }

    .icon.white {
      fill: #fff;
    }
    .icon.white > .outline {
      fill: #000;
      fill-opacity: .5;
    }
  </style>

  <g id="tab-audio" class="icon">
    <path class="outline" d="M12.4,3.6l-1-0.6l-0.9,2.5H10V1.8c0-0.4-0.5-0.7-0.9-0.4L5.6,5H4C2.9,5,2,5.9,2,7v2c0,1.1,0.9,2,2,2h1.6l3.6,3.6 c0.3,0.3,0.9,0.1,0.9-0.4v-3.7h0.5l0.9,2.5l1-0.6C14,11.5,15,9.8,15,8S14,4.5,12.4,3.6z M9,13l-3-3H4c-0.6,0-1-0.4-1-1V7 c0-0.6,0.4-1,1-1h2l3-3V13z M10,9.5v-3c0.8,0,1.5,0.7,1.5,1.5S10.8,9.5,10,9.5z M11.9,11.5l-0.4-0.9C12.4,10,13,9.1,13,8 s-0.6-2-1.4-2.5l0.3-1C13.2,5.2,14,6.5,14,8S13.2,10.8,11.9,11.5z"/>
    <path d="M4,6C3.4,6,3,6.4,3,7v2c0,0.6,0.4,1,1,1h2l3,3V3L6,6H4z M10,6.5v3c0.8,0,1.5-0.7,1.5-1.5S10.8,6.5,10,6.5z M11.9,4.5 l-0.4,0.9C12.4,6,13,6.9,13,8s-0.6,2-1.4,2.5l0.4,0.9c1.2-0.7,2.1-2,2.1-3.5S13.2,5.2,11.9,4.5z"/>
  </g>
  <g id="tab-audio-muted" class="icon">
    <path class="outline" d="M5.6,5H4C2.9,5,2,5.9,2,7v2c0,0.7,0.3,1.3,0.9,1.7l-1.8,1.8l2.5,2.5l3-3l2.6,2.6c0.3,0.3,0.9,0.1,0.9-0.4V8.5l3.9-3.9 l-2.5-2.5L10,3.5V1.8c0-0.4-0.5-0.7-0.9-0.4L5.6,5z"/>
    <path d="M11.5,3.5L9,5.9V3L6,6H4C3.4,6,3,6.4,3,7v2c0,0.6,0.4,1,1,1h0.9l-2.5,2.5l1.1,1.1l9-9L11.5,3.5z M9,13V9.7l-1.7,1.7L9,13z"/>
  </g>
  <g id="tab-audio-blocked" class="icon">
    <path class="outline" d="M8,2c3.3,0,6,2.7,6,6s-2.7,6-6,6s-6-2.7-6-6S4.7,2,8,2 M8,12.5c2.4,0,4.5-2,4.5-4.5S10.4,3.5,8,3.5
      S3.5,5.5,3.5,8S5.6,12.5,8,12.5 M6,5l5,3l-5,3V5 M8,1C4.1,1,1,4.1,1,8s3.1,7,7,7s7-3.1,7-7S11.9,1,8,1L8,1z M7.3,4.6
      C7.5,4.5,7.7,4.5,8,4.5c1.5,0,2.9,1.1,3.4,2.5L7.3,4.6L7.3,4.6z M5,9.7C4.7,9.2,4.5,8.6,4.5,8S4.7,6.8,5,6.3V9.7L5,9.7z M7.3,11.4
      L11.4,9c-0.4,1.4-1.8,2.5-3.4,2.5C7.7,11.5,7.5,11.5,7.3,11.4L7.3,11.4z"/>
    <path d="M8,2C4.7,2,2,4.7,2,8s2.7,6,6,6s6-2.7,6-6S11.3,2,8,2z M8,12.5c-2.4,0-4.5-2-4.5-4.5S5.6,3.5,8,3.5
      s4.5,2,4.5,4.5S10.4,12.5,8,12.5z M6,5v6l5-3L6,5z"/>
  </g>

  <g id="tab-audio-white" class="icon white">
    <path class="outline" d="M12.4,3.6l-1-0.6l-0.9,2.5H10V1.8c0-0.4-0.5-0.7-0.9-0.4L5.6,5H4C2.9,5,2,5.9,2,7v2c0,1.1,0.9,2,2,2h1.6l3.6,3.6 c0.3,0.3,0.9,0.1,0.9-0.4v-3.7h0.5l0.9,2.5l1-0.6C14,11.5,15,9.8,15,8S14,4.5,12.4,3.6z M9,13l-3-3H4c-0.6,0-1-0.4-1-1V7 c0-0.6,0.4-1,1-1h2l3-3V13z M10,9.5v-3c0.8,0,1.5,0.7,1.5,1.5S10.8,9.5,10,9.5z M11.9,11.5l-0.4-0.9C12.4,10,13,9.1,13,8 s-0.6-2-1.4-2.5l0.3-1C13.2,5.2,14,6.5,14,8S13.2,10.8,11.9,11.5z"/>
    <path d="M4,6C3.4,6,3,6.4,3,7v2c0,0.6,0.4,1,1,1h2l3,3V3L6,6H4z M10,6.5v3c0.8,0,1.5-0.7,1.5-1.5S10.8,6.5,10,6.5z M11.9,4.5 l-0.4,0.9C12.4,6,13,6.9,13,8s-0.6,2-1.4,2.5l0.4,0.9c1.2-0.7,2.1-2,2.1-3.5S13.2,5.2,11.9,4.5z"/>
  </g>
  <g id="tab-audio-white-muted" class="icon white">
    <path class="outline" d="M5.6,5H4C2.9,5,2,5.9,2,7v2c0,0.7,0.3,1.3,0.9,1.7l-1.8,1.8l2.5,2.5l3-3l2.6,2.6c0.3,0.3,0.9,0.1,0.9-0.4V8.5l3.9-3.9 l-2.5-2.5L10,3.5V1.8c0-0.4-0.5-0.7-0.9-0.4L5.6,5z"/>
    <path d="M11.5,3.5L9,5.9V3L6,6H4C3.4,6,3,6.4,3,7v2c0,0.6,0.4,1,1,1h0.9l-2.5,2.5l1.1,1.1l9-9L11.5,3.5z M9,13V9.7l-1.7,1.7L9,13z"/>
  </g>
  <g id="tab-audio-white-blocked" class="icon white">
    <path class="outline" d="M8,2c3.3,0,6,2.7,6,6s-2.7,6-6,6s-6-2.7-6-6S4.7,2,8,2 M8,12.5c2.4,0,4.5-2,4.5-4.5S10.4,3.5,8,3.5
      S3.5,5.5,3.5,8S5.6,12.5,8,12.5 M6,5l5,3l-5,3V5 M8,1C4.1,1,1,4.1,1,8s3.1,7,7,7s7-3.1,7-7S11.9,1,8,1L8,1z M7.3,4.6
      C7.5,4.5,7.7,4.5,8,4.5c1.5,0,2.9,1.1,3.4,2.5L7.3,4.6L7.3,4.6z M5,9.7C4.7,9.2,4.5,8.6,4.5,8S4.7,6.8,5,6.3V9.7L5,9.7z M7.3,11.4
      L11.4,9c-0.4,1.4-1.8,2.5-3.4,2.5C7.7,11.5,7.5,11.5,7.3,11.4L7.3,11.4z"/>
    <path d="M8,2C4.7,2,2,4.7,2,8s2.7,6,6,6s6-2.7,6-6S11.3,2,8,2z M8,12.5c-2.4,0-4.5-2-4.5-4.5S5.6,3.5,8,3.5
      s4.5,2,4.5,4.5S10.4,12.5,8,12.5z M6,5v6l5-3L6,5z"/>
  </g>
</svg>
PK
!<�`���Achrome/browser/skin/classic/browser/tabbrowser/tab-connecting.png�PNG


IHDR(-SacTL<휕�@PLTERRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRT����tRNS

 "#$%&()+,-./012346789;<?@BDEFGIJKMNQSUVWYZ[]^_bcefhjkmnprtuwxy{|~�������������������������������������������������������������������������������������������������o�fcTLdf�S�IDAT�c`��^��E`������
`h�?	ul�XVfcTLd�zfdAT�c���������J�EL�{EPKfcTLdH��fdAT�c`��=A��~���)P,p�	yb�=fcTLd��ofdAT�c`p[�:���w���eaD��
ҟ��fcTLd��c>fdAT�c`0�_Q-��f���0
"�
��fcTL	dw�G�fdAT
�c`0�۠��e���saD�-6�6fcTLd�
ːfdAT�c`�o�R���`�V��i�8�j��U�fcTL
d6�n�fdAT�c�m�La(��aD�_�,�:fcTLd�fdAT�c`P�l��fgPڵ��4T��_L5m�fcTLd!'U�fdAT�c`w˫I�g`��np*�&��#fcTLd�5!�fdAT�c`P���d`���D��4�)h�rfcTLd���fdAT�c��*�I�d`��l�4T����F<ofcTLdF�nfdAT�c`��m�pab�ۻ�4T�RO��g�fcTL	d�
��fdAT�c����ah��0�,����"fcTL	dP�_CfdAT�c`p�Q,���&�(� �jQZ>:�fcTL
d���fdAT�c�iZҩ�п���P,��
�F�dfcTL
dst/9fdAT �c`�^;ӌ�q���+�$Xv�
#�4��fcTL!
d����fdAT"�c`pZ�֎�q���k�$Xl�	m�}�fcTL#d����fdAT$�c�[�5���w����P,t�	uu�V�fcTL%d+E�tfdAT&�c���;��!����J�E=
��}�fcTL'd��	�fdAT(�c��bh�`�```�`+ ����#fcTL)d+�8�fdAT*�c`�c�dpf``H��`)��[j="fcTL+d�6�(fdAT,�c`�c�*�`���%�"(2{�[UfcTL-d\���fdAT.�c`H�>G�Ay����P,o	un�=fcTL/d�jJ�fdAT0�c`ߺH�A����J�E8����TfcTL1d]l�=fdAT2�c`ڲԘA`�S�$XnJ	{����fcTL3d���fdAT4�c`�ݼ”�k�����$Xm�	u���fcTL5d]0l�fdAT6�c`�ڴʚ�}���$Xm�	{u�2fcTL7d���GfdAT8�c`p۸ƚ�u����$Xm2	s�ZfcTL9d�ˏfdAT:�c`xՍ^fcTL;d9���fdAT<�c�bX����a�q.i��ffcTL=d]�/�fdAT>�c`H�>O�A~����P,o	yJ���fcTL?d�� fdAT@�c���3���l����P,s�	wwSt�fcTLA
d���XfdATB�cН�!��a���$Xxr	���fcTLCd6^�pfdATD�c�,Z֯�����P,w�
#���fcTLEd*��fdATF�c`p�03��Av����aD�
yT��cfcTLGd^��MfdATH�c`���fd�0
"�2
�w���fcTLIdr=^�fdATJ�c`�h`fH�Q0
"�X
��ufcTLKd����fdATL�c`t�l�b��`D��#Xx�mfcTLMdT-�fdATN�c`PM���g`Pߵ,�4T�k��fcTLOd��f�fdATP�c������a`���0�i�8��e�*�AfcTLQd�C��fdATR�c`PI��2``Pڵ,�4T�Na���fcTLSd�,�QfdATT�c`t�i�d��}0
"��` �fcTLUd��jfdATV�c�I�mua`(�60
"��
߳�fcTLWdU&�kfdATX�c���٠�0i���K�$X~
�ئ�&fcTLYd�R�fdATZ�c0�]��ɰ���!J�EG*��S�fcTL[d.x�fdAT\�c`pX�2���m���eaD��	�+�dhfcTL]dy��dfdAT^�c`�߲ܘ�o��Ӡ$Xm�	w���fcTL_d�n�fdAT`�c`��9S�Ag����P,o�	{�Y#fcTLad
��fdATb�c`���+����%�";*�
�3fcTLcd�d6zfdATd�c��bh�`�```�`+
�m�fcTLed
�DfdATf�c��P���`	%�"+��9| �fcTLgd�?�fdATh�cP��3���f���ZP,s�	{�ffcTLid
K��fdATj�c�=������A
J�E>���k>�fcTLkd��E�fdATl�c0X�5��a����$Xt�	u��L�fcTLmdz7�fdATn�c0Y�ُ�a��{x�$XuZ	wz��fcTLod���YfdATp�c0_�ً�a��;ء$Xu\	qK�_NfcTLqd{�SIfdATr�c�^�ѝ�a���X�$Xu�	u5�w�fcTLsd���fdATt�c�^����a�[Y�$Xu�	s�ðfcTLud> ~fdATv�cXİ�EPu��tEXtSoftwareezgif.com�óX1tEXtCommentCreated with ezgif.com Animated PNG maker��e^IEND�B`�PK
!<�#VVDchrome/browser/skin/classic/browser/tabbrowser/[email protected]�PNG


IHDR  D���acTL<휕��PLTERRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRTRRT����tRNS	

 !"#$%&()*+,-./0123456789:;<=>?ABCDEGHIJKLMNOPQRSTUVWXYZ[\]^`abcdefghijklmnopqrsuvwxyz{|}~������������������������������������������������������������������������������������������������������������������!:fcTL  d�ο9IDAT8�c`�(.x�z�=nyٛ��N���$N� 
�I����I�`��/u0�r
fcTL	de�!4fdAT8�c``p���9�7��Ùw`�� a�;��B�"這YA/s�
j�fcTL	d���4fdAT8�c`�]���v���;C�;08�p�z'��B�"t�M{�/qU�fcTL
d�МY5fdAT8�c``�Zr��|c Hx�^30�>}���A�;2I�6�a��2K/F�fcTLd��v)5fdAT8�c``��[s`]��\x�u��=� �$��;0@a#�Aы0�;4�[���fcTL		dj�ls6fdAT
8�c``��zA��/�x������B6�d�HfZ7e{k�PfcTLd�,75fdAT8�c``�i������X��<Jg`�}����(ld5�z��Ѽ9�'��fcTL

d�:Ns4fdAT8�c`���$m00����8/�A�K w�;8X��CU�j
�
�j;�na<!fcTLd��?�8fdAT8�c`��#&0P_��ܬd�̟����@��w��ES�f�EY�=�v��fcTL
d���9fdAT8�c````�v�uUcb��ށ��>9����`�-íw�����щ?
gdfcTLdF�b�6fdAT8�c`q}km!(��v�\��q;����w�@����<T�&,@�s�GfcTLd�~~2<fdAT8�c`Co_W%0_��\k�R߾��˪�/�!�Lt.�b�QW�F�X��>�6�ߎfcTLd��7fdAT8�c`��̜P
(��N����o�@��}��]=�y(�!{=���mfcTLd�V"�4fdAT8�c```v*joH�c�K�`�(����1�s��rP���l4�:�4Sy,fcTLd�p��5fdAT8�c``�ʚ��'��
���.���&�.�]
Y
�^$3�48s���fcTL
d�xsy2fdAT8�c``p��}s�1ۂw`P�P�
N�����H
��!>I5�����fcTL
d4���5fdAT 8�c``n8tuG6+���w ����a�;(``z	enEf")@�7�2��_�fcTL!	d��s03fdAT"8�c``p���,)��w pQ�������A�x����E耛]
/{�e�fcTL#	dզ��4fdAT$8�c`�����^W�K��mC�;0��İ�z���B�"t�Mz�/w�*��fcTL%	d8l��0fdAT&8�c``.���r93ìw 0����X0�@Y�H,�,B�l�/�{�fcTL'	d�V
3fdAT(8�c``����t	��w pF���6������x����E耛]�/y�>FfcTL)
d���4fdAT*8�c``�^���:# �z����ށ�& �1������I�0&/mO�4�fcTL+	d�k3fdAT,8�c`�������@ ����x���a=������E耛x�/}�\BfcTL-	d�\)�2fdAT.8�c`�h���l#�jhc`�3ު2tBX�H,�,B�/��̻IfcTL/	d���0fdAT08�c``�?��zC�;X�Ƞ��2��&$B�n
j�/��覇fcTL1	d��}�3fdAT28�c``�9��n7C�;��� ��z���a����B�"t�Md=/qW\fcTL3	db]��2fdAT48�c``�?��1�88����b(g0�0�#��pS*�v16|fcTL5	d���3fdAT68�c``p����l���@���U0haP�0�!��pSt\ۖn>fcTL7	d�4fdAT88�c``p����9�7��Ùw`�� a�;��B�"這Y�/uy�fcTL9	d�rL3fdAT:8�c``���)��ܒe`8	f1L`��0� ��H:`��a�$H�fcTL;	db�ݥ*fdAT<8�c``�gE v1n2�A?��u���E�5��}y�fcTL=	d�2�3fdAT>8�c``L;��VC�;X�Ġ��z�a��Eb!d:�gu/�ع�fcTL?	d�*�!3fdAT@8�c``�>��^?C�;��� ��z���	a����B�"t�McU/}���OfcTLA	dgs�0fdATB8�c``��~�x���w pF���Ldh���"��pS�1%��*fcTLCd�8z4fdATD8�c``�[z`}	?p��A��w��9
�A�(C1�h�Q2���߹fcTLE
d��X�5fdATF8�c```
��jN/�x�� �M� �7X��`�)F7
�"��4!_��fcTLG
dG��4fdATH8�c``�H�Ta�E/ށ�JA'���v�x��1x�*QMA���5k�](�fcTLI	dvD5fdATJ8�c``�
(���cƒ��@`�:C�;(�)���~���FV���LI�6�����fcTLK
dT ��7fdATL8�c```P	Ɉ�be۽�@�n)3�����6��%t�JTSPlU�7����fcTLMd[�˭6fdATN8�c`eW_GI`�9����xL+�@+���{+��ES�f�EU|8S��l�fcTLO
d͞pl6fdATP8�c`A=M60�-?�^mrdN����=������8�_�fcTLQd��TV6fdATR8�c`5Wi`κ���x�K�@�k
�{-��ES�f�EOI89��\fcTLS
d�28fdATT8�c```Ѝ�I�ae���@�v&#����ya�w/Y�y�*QMA�Kv7S隹�fcTLU
d�v��6fdATV8�c```P�hjJ�a���@�M7���L)�q޽�A硪D3��_6LO�QfcTLWd��6fdATX8�c``�O���ӓ����]2@�ҳw��)}�he( 
؈4�ѝ�2fcTLYdMTtK5fdATZ8�c``p��cC�<0NyO���w�H��a%��.
���Y/���;3+R��fcTL[	d�m�1fdAT\8�c`�?tek��w 0��a�;a�eMGb!d:���1g���kfcTL]
d�3fdAT^8�c``0Z���*m 0~���߁�rַ�c&�dmp�(�/mL��
fcTL_	dT�W�3fdAT`8�c``H9��v7C�;��� ��z�a����B�"t�Mf�/����fcTLa	d�7�3fdATb8�c`���x,ۮw P��f�{!�0�z��B�"t�Ms�/{�0�\fcTLc	dN��4fdATd8�c`P[��FK�{��-��w`����������E耛�/{ga�1fcTLe	d{�[4fdATf8�c``p���|9�7��$�w`�͠a�;��B�"t�M[/{$E��fcTLg	d�sA�3fdATh8�c``�;��n7/C�;��� ��z���a����B�"t�Md�/팯'fcTLi	d{p�0fdATj8�c``.���Jôw 0�����0AYUH,�,B�m�/�����fcTLk	dᑓ�2fdATl8�c`�j��p:����i420���� ,�h$B�n
��-���fcTLm	d{\�}3fdATn8�c`�p�ݑ0΃�@ ��!�x���a9���	���E耛v�/�KtfcTLo	d��22fdATp8�c`�����>/�� �Pf0�fe�a1"��pS8�c_y�fcTLq	d
̅3fdATr8�c`�����ng�+@��/C\dd8a1� ��pS�b�
p��fcTLs	d�]fm4fdATt8�c`P\��v{����C�;08�p�z'��B�"t�M|G/s�_g`fcTLu	d
�$�2fdATv8�c`Pbx�`�� w��&�'�AXH,�,B���	�4�tEXtSoftwareezgif.com�óX1tEXtCommentCreated with ezgif.com Animated PNG maker��e^IEND�B`�PK
!<�I����Gchrome/browser/skin/classic/browser/tabbrowser/tab-loading-inverted.png�PNG


IHDR(-SacTL<휕�7PLTE������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������i��r�tRNS

 "#$%&()+,-./012346789;<?@BDEFGIJKMNQSUVWYZ[]^_bcefhjkmnprtuwxy{|~�����������������������������������������������������������������������������������������������eR�fcTLdf�S�IDAT�c`��X��E`Ξ=[��
`h�!4	?�a�fcTLd�zfdAT�c�i�Q�����J�EJ�K$狻fcTLdH��fdAT�c`,��+�`�gϞD(	m�	=�^��fcTLd��ofdAT�c`p^�"���k�={�`D�d	��.yfcTLd �UfdAT�c��YZ�ϰrϞ=�P,~,
a���fcTL	dw�G�fdAT
�c`�m�U+��qϞ=�`D��
��$T/fcTLd�
ːfdAT�c`�i�X$�`��3�i�8��M���fcTL
d6�n�fdAT�c`jjKd(�3`D�o��b)WfcTLd�fdAT�c`PLk,�`a�߾��4T�&�;x�fcTLd!'U�fdAT�c`qήL�a`h��p*�4aI�fcTLd�5!�fdAT�c`P����c`p�����L��\�)fcTLd���fdAT�c`sϫLRc`��Vp*��aCx�qfcTLdF�nfdAT�c`��j)ud`�޵b�4T���WO�fcTL	d�
��fdAT�c�no��dh�50�,���HfKfcTL	dP�_CfdAT�c`��Z ʠ��`D��\�G>fcTL
d���fdAT�c��_ئ�гgϞyP,}�
�`eI�fcTL
djoxfdAT �c`�X5͘�nϞ=K�$Xt 	�԰��fcTL!d���)fdAT"�c�_�Қa͞=+!�\\	9����fcTL#d����fdAT$�cО�)���kϞ=BP,r$	?N��fcTL%d+E�tfdAT&�c��#����$X;:ͽSJfcTL'd��	�fdAT(�c�ighdee``p��`)pk�jfcTL)d+�8�fdAT*�c`�f��cp```���`(eV^�fcTL+d�1۾fdAT,�c`��1I�AϞ=�P,l�	3jicZfcTL-d+��RfdAT.�c`H�2S�A����J�E7��M�"fcTL/d�jJ�fdAT0�c`�4_�A����J�E6��Z�SfcTL1d]l�=fdAT2�c`�߸Ȁ�wמ=�$Xk�	E�8�fcTL3d���fdAT4�c`�ڰԈ�}Ǟ=ӡ$Xk:	?�v(ffcTL5d]0l�fdAT6�c`p_�܂�e�=s�$Xk:	Ejz�fcTL7d���GfdAT8�c`p^�҂�i�=�$Xj�	=���GfcTL9d�ˏfdAT:�c`xՍ^fcTL;d9���fdAT<�cpgX����a.�q-s�J�fcTL=d]�/�fdAT>�c`��2[�AfϞ=MP,l�	C���fcTL?d�� fdAT@�c���-���xϞ=ZP,q	A�M��fcTLA
d���XfdATB�cК�6��až={̡$Xu�	��s#�fcTLCd6^�pfdATD�c`�_ܣ��gϞ9P,u
	�1t��fcTLEd*��fdATF�c`���� �{Ϟ=0
"��
7�˚ fcTLGd^��MfdATH�c`���``��0
"�p
i��=fcTLIdr=^�fdATJ�c`lm�e`H��0
"�h
���`fcTLKd����fdATL�c`�/���g���aD�?
���HfcTLMdM6�YfdATN�c`PJ�H�a`Pپ��4T��#<B�fcTLOdnYשfdATP�c`w����d��f�(� �>��K�fcTLQd�C��fdATR�c`PL�J�e`�߾L�4T����fcTLSd�,�QfdATT�c`p�l��c���0
"�
�3��fcTLUd��jfdATV�c��jrd`(��0
"�H
���}
fcTLWdU&�kfdATX�c�(�V��пgϞ�P,{H
O�3D�fcTLYd�R�fdATZ�c�횟�ư���!J�EE2���fcTL[d.x�fdAT\�c`���,���y�={�`D�|	�}mfcTL]dy��dfdAT^�c`�ٸĀ�{�=��$Xk�	Am��NfcTL_d�n�fdAT`�c`H�6M�AsϞ=eP,mF	E��~~fcTLad
��fdATb�c`�������%�"92�7=�fcTLcd�d6zfdATd�c�igh`ee``p��`)Zi����fcTLedz�t�fdATf�c�����!cϞ=fP,pV	?���RfcTLgd�8��fdATh�c���-������AJ�E<��<G�fcTLid
K��fdATj�cP��%������AJ�E<�ӨB�PfcTLkd��E�fdATl�c�]�)���oϞ�P,rR	?dO��fcTLmdz7�fdATn�c0\����a�=;9�$Xr�	A	ӽ�fcTLod���YfdATp�c0Y����a��=[Y�$Xr�	;"��fcTLqd{�SIfdATr�c�X�΅�a��=���$Xs0	?wxU�fcTLsd���fdATt�c�X�֙�aў=��$Xs,	=�f�1fcTLud> ~fdATv�c��0�?��tEXtSoftwareezgif.com�óX1tEXtCommentCreated with ezgif.com Animated PNG maker��e^IEND�B`�PK
!<�l�00Jchrome/browser/skin/classic/browser/tabbrowser/[email protected]�PNG


IHDR  D���acTL<휕��PLTE���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������&i�6�tRNS	

 !"#$%&()*+,-./0123456789:;<=>?ABCDEGHIJKLMNOPQRSTUVWXYZ[\]^`abcdefghijklmnopqrsuvwxyz{|}~���������������������������������������������������������������������������������������������������������������<�fcTL  d�ο9IDAT8�c`���{�b�-ny�k���N����N� 
^K����IЛ�`���.�8���fcTL	de�!4fdAT8�c``�����<��������`�� a�>��B�"這>�.�C�efcTL	d���4fdAT8�c`�����;�k���+C�k08�p�z-��B�"t�M`	.��0nfcTL
d�˭5fdAT8�c``�Xx��\C �{�^20?z
�x_C�+2I�6�a�21��i?fcTL
dx�5fdAT8�c`���{W
0����� pE����8�a@�����
/�4�$fcTL		dj�ls6fdAT
8�c```���b^��y
O3�.��7?�<6�d�Hf3p6��d�fcTLd�,73fdAT8�c``�j�����_���T���0�p�ދ�FV���L��8Ѿfu?fcTL

d�:Ns4fdAT8�c`̈́��M00�v��8�^C�3 w�k8X��CU�j
�
�:�2�
�fcTLd��?�7fdAT8�c`>���0P]�5\+g�L���M@�����ES�f�E"�<���~�fcTL
d���8fdAT8�c````�t�vVa���_�����g�a�5���������>%�k�fcTLd_�S�6fdAT8�c`Q]KM(0�r�5���q�^��<���@����<T��?���fcTLd�!�9fdAT8�c`6}Oog(0]��5\�q�_����@�O_#�tL>�z�P�L?=���۰fcTLd��7fdAT8�c`��`5(������/_�@��}��]=�y(���<-�mj7fcTLd�V"�4fdAT8�c```p(h���a��`�0���0l�s^��rP���l4�O:�F��fcTLd�p��5fdAT8�c``�Ș8�+�����Nѯ��:�v�u
Y
�^$3v�7�f�AfcTL
d�cB82fdAT8�c``p��eC�!˼�`P�P�
���H
��!�4�V�;�fcTL	dO6��1fdAT 8�c`��iS&3��� ���a�k�cxemBb!d:��31���
fcTL!	d��Bq3fdAT"8�c``p��	��� pN���2����A�x����E�3A_.� �4fcTL#
d�14fdAT$8�c``�����.g 9�^������2���|���DR��
n�.�;#�LfcTL%	d8l��/fdAT&8�c``(��B)�x
3�^C�C�U��B�"t�MQq.ӂ�g=fcTL'	d��f�2fdAT(8�c``����T1�p8��|�b�b0�06!��pS�
�g��#fcTL)
d���4fdAT*8�c``�\���j ��_��z �������I�0<.��F�fcTL+	d�k3fdAT,8�c`�t��^_�c�A ����x}��a
������E耛\5.ɞ`c�fcTL-	d�[1fdAT.8�c``k���T�x
-�`��W����x$B�n
U1.ѻ��fcTL/	d���0fdAT08�c``�=�J
C�kX���\Ҡ�$B�n
O�.��A&fcTL1	d��}�3fdAT28�c``�:�V'C�k��� ��z���
a����B�"t�MI/.���fcTL3	db]��2fdAT48�c``����$�8��v�b(e0�0� ��pSl
�j�p�fcTL5	d���o4fdAT68�c``p���L)�����å�`�Ġ
a�ލ�B�"這@.�d0�#fcTL7	db?2fdAT88�c``���0O������H00��zd ��H,�,��)�
-�^;^fcTL9	d�rL3fdAT:8�c``������� �p]����1�C'�XY$0S7PUQ2�fcTL;	db�ݥ*fdAT<8�c``�ey v1�1�A/��u���E�5ٖ�oҍ�fcTL=	d�2�3fdAT>8�c``H9��zC�kX�Ƞ��z�a��Fb!d:�LY.��'G�fcTL?	d�*�!3fdAT@8�c``�<��v/C�k��� ��z���a����B�"t�MHG.�8fcTLA	dgs�0fdATB8�c``��r�H�C�k8)�`�����KH,�,B��Y0s�gfcTLCd�8z4fdATD8�c``�Y�wM/p�y
gA�����9�B�(C1�hfq1��}UfcTLE
d��X�5fdATF8�c```��|V
7H�}
�M ��!�2'X�5xa�)F7
�"Q�3YL��DfcTLG
dG��4fdATH8�c``K�Pf�O_��2~'�5���x�e1x�*QMA���4�w��fcTLI	dvD4fdATJ8�c``�+�L�a���A`�*C�k(�&�p�~���FV���L#�5ɮ0fcTLK
dT ��6fdATL8�c```P
J��`f�]�A�V1���5L�ֽ�t�JTSPl&�6��C�2fcTLMdB��5fdATN8�c`Ego{q`�:�v���K_�@3���~%��ES�n�E":7{yE�fcTLOdn��6fdATP8�c`~3u�.=�����0�
�v�F;.�b4�P-J8n��fcTLQd��TV5fdATR8�c`g_gI`�8����_�@
�g	�~!��ES�n�E7]a�ufcTLS
d�27fdATT8�c```Ў�J�bf�C�A�F:���5�y�`��Ϙ�y�*QMA�T6��8H�fcTLU
d�v��6fdATV8�c```PNkhH�b�ٯA�e'���5L)�q^��B硪D3��5Ab?H�fcTLWd��\5fdATX8�c``�M�8�ݝ\o���R@������)~
�he( 
��3����fcTLY
d���3fdATZ8�c``p��um�,Lz
=b^C�}�eP��d&�$m���2o&��fcTL[	d�m�0fdAT\8�c`��qS#���� 0����51\���"��pS��0�:n�fcTL]
d�3fdAT^8�c``0X���rM 0|����^���W�&�dmp�	�.�)��RfcTL_	dT�W�3fdAT`8�c``H:��F'C�k��� ��z�a����B�"t�MK�.ӹ�I�fcTLa	d�7�3fdATb8�c`���H4�� P�f�~*�0�z��B�"t�MXa.�B��fcTLc	dW�?�4fdATd8�c`PY��:s�ۯ��5���`����*������E耛d�.���%�fcTLe
dس�4fdATf8�c``p���\ p�.��g^�A'����0
I�6�a�.�6{�0fcTLg	d�sA�3fdATh8�c``�9�V'7C�k��� ��z��a����B�"t�MI}.�b��fcTLi	d{p�0fdATj8�c``(9��b� 0����5X1@YH,�,B�R�.�U,��fcTLk	d���3fdATl8�c`�h���D
��� P�f�~!��a��Db!d:�V�.�7v��fcTLm	d[��2fdATn8�c`���p0����}d00d�x�@XH,�,B�����fcTLo	d��22fdATp8�c`�v��n�S �P
f0�`f�a1�#��pS-�
�XA�pfcTLq	dz˵�4fdATr8�c`�����Gы���7C�k08�p�z���B�"t�M_�.��6>�fcTLs	d�ZV�1fdATt8�c`�����[�k@��C/#0�Bb!d:�v��!��fcTLu	d
�$�2fdATv8�c`P`x�`�� s������!,1$B�n
�	OI�
�tEXtSoftwareezgif.com�óX1tEXtCommentCreated with ezgif.com Animated PNG maker��e^IEND�B`�PK
!<� ӱ��>chrome/browser/skin/classic/browser/tabbrowser/tab-loading.png�PNG


IHDR(-SacTL<휕�7PLTE
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
���s-�tRNS

 "#$%&()+,-./012346789;<?@BDEFGIJKMNQSUVWYZ[]^_bcefhjkmnprtuwxy{|~�����������������������������������������������������������������������������������������������eR�fcTLdf�S�IDAT�c`��X��E`Ξ=[��
`h�!4	?�a�fcTLd�zfdAT�c�i�Q�����J�EJ�K$狻fcTLdH��fdAT�c`,��+�`�gϞD(	m�	=�^��fcTLd��ofdAT�c`p^�"���k�={�`D�d	��.yfcTLd �UfdAT�c��YZ�ϰrϞ=�P,~,
a���fcTL	dw�G�fdAT
�c`�m�U+��qϞ=�`D��
��$T/fcTLd�
ːfdAT�c`�i�X$�`��3�i�8��M���fcTL
d6�n�fdAT�c`jjKd(�3`D�o��b)WfcTLd�fdAT�c`PLk,�`a�߾��4T�&�;x�fcTLd!'U�fdAT�c`qήL�a`h��p*�4aI�fcTLd�5!�fdAT�c`P����c`p�����L��\�)fcTLd���fdAT�c`sϫLRc`��Vp*��aCx�qfcTLdF�nfdAT�c`��j)ud`�޵b�4T���WO�fcTL	d�
��fdAT�c�no��dh�50�,���i'�fcTL	dP�_CfdAT�c`��Z ʠ��`D��\�G>fcTL
d���fdAT�c��_ئ�гgϞyP,}�
�`eI�fcTL
djoxfdAT �c`�X5͘�nϞ=K�$Xt 	�԰��fcTL!d���)fdAT"�c�_�Қa͞=+!�\\	9����fcTL#d����fdAT$�cО�)���kϞ=BP,r$	?N��fcTL%d+E�tfdAT&�c��#����$X;:ͽSJfcTL'd��	�fdAT(�c�ighdee``p��`)pk�jfcTL)d+�8�fdAT*�c`�f��cp```���`(eV^�fcTL+d�1۾fdAT,�c`��1I�AϞ=�P,l�	3jicZfcTL-d+��RfdAT.�c`H�2S�A����J�E7��M�"fcTL/d�jJ�fdAT0�c`�4_�A����J�E6��Z�SfcTL1d]l�=fdAT2�c`�߸Ȁ�wמ=�$Xk�	E�8�fcTL3d���fdAT4�c`�ڰԈ�}Ǟ=ӡ$Xk:	?�v(ffcTL5d]0l�fdAT6�c`p_�܂�e�=s�$Xk:	Ejz�fcTL7d���GfdAT8�c`p^�҂�i�=�$Xj�	=���GfcTL9d�ˏfdAT:�c`xՍ^fcTL;d9���fdAT<�cpgX����a.�q-s�J�fcTL=d]�/�fdAT>�c`��2[�AfϞ=MP,l�	C���fcTL?d�� fdAT@�c���-���xϞ=ZP,q	A�M��fcTLA
d���XfdATB�cК�6��až={̡$Xu�	��s#�fcTLCd6^�pfdATD�c`�_ܣ��gϞ9P,u
	�1t��fcTLEd*��fdATF�c`���� �{Ϟ=0
"��
7�˚ fcTLGd^��MfdATH�c`���``��0
"�p
i��=fcTLIdr=^�fdATJ�c`lm�e`H��0
"�h
���`fcTLKd����fdATL�c`�/���g���aD�?
���HfcTLMdM6�YfdATN�c`PJ�H�a`Pپ��4T��#<B�fcTLOdnYשfdATP�c`w����d��f�(� �>��K�fcTLQd�C��fdATR�c`PL�J�e`�߾L�4T����fcTLSd�,�QfdATT�c`p�l��c���0
"�
�3��fcTLUd��jfdATV�c��jrd`(��0
"�H
���}
fcTLWdU&�kfdATX�c�(�V��пgϞ�P,{H
O�3D�fcTLYd�R�fdATZ�c�횟�ư���!J�EE2���fcTL[d.x�fdAT\�c`���,���y�={�`D�|	�}mfcTL]dy��dfdAT^�c`�ٸĀ�{�=��$Xk�	Am��NfcTL_d�n�fdAT`�c`H�6M�AsϞ=eP,mF	E��~~fcTLad
��fdATb�c`�������%�"92�7=�fcTLcd�d6zfdATd�c�igh`ee``p��`)Zi����fcTLedz�t�fdATf�c�����!cϞ=fP,pV	?���RfcTLgd�8��fdATh�c���-������AJ�E<��<G�fcTLid
K��fdATj�cP��%������AJ�E<�ӨB�PfcTLkd��E�fdATl�c�]�)���oϞ�P,rR	?dO��fcTLmdz7�fdATn�c0\����a�=;9�$Xr�	A	ӽ�fcTLod���YfdATp�c0Y����a��=[Y�$Xr�	;"��fcTLqd{�SIfdATr�c�X�΅�a��=���$Xs0	?wxU�fcTLsd���fdATt�c�X�֙�aў=��$Xs,	=�f�1fcTLud> ~fdATv�c��0�?��tEXtSoftwareezgif.com�óX1tEXtCommentCreated with ezgif.com Animated PNG maker��e^IEND�B`�PK
!<�k(@66Achrome/browser/skin/classic/browser/tabbrowser/[email protected]�PNG


IHDR  D���acTL<휕��PLTE
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
����U��tRNS	

 !"#$%&'()*+,-./0123456789:;<=>?ABCDEGHIJKLMNOPQRSTUVWXYZ[\]^`abcdefghijklmnopqrsuvwxyz{|}~����������������������������������������������������������������������������������������������������������������
aCfcTL  d�ο9IDAT8�c`����r�ny��o��+N}o��8N�!
�H����IЛ�`��.���o�fcTL	de�!4fdAT8�c``p���|�Wo��é7`�� a�9��B�"這GK.�UAN3fcTL	d���4fdAT8�c`�����V{��o��C�08�p�z#��B�"t�Mi�.�~��sfcTL
d�˭5fdAT8�c``�\t��<# ��^10?~�y��@�2I�6�a�21�;6�fcTL
dx�5fdAT8�c`��U��	0��ҹ7 pU����8�o`@�����
=w4U�_�*fcTL		dj�ls6fdAT
8�c```���r~��}�2����w��|6�d�Hf@�6��`�DfcTLd�,73fdAT8�c``�n�����ހ��4��70�p�އ�FV���L�09Xl��fcTL

d�!24fdAT8�c`��D-00�����8��@�s w�8X��CU�j
�
�t;;A�fcTL

d�f�8fdAT8�c```�s��7d��/ހ��
V��o``33��7��CU�j
�
`�<�C�PfcTL
d���8fdAT8�c````�r�qQa�iހ��^���o`�
Í7������#>u��`<fcTLd_�S�6fdAT8�c`Q=+](0�z�
\��q����|�7�@����<T��?�$��fcTLd�!�9fdAT8�c`6/(0[��
\nqS^�����@��� �L>�z�P�_�>E�M��fcTLd��7fdAT8�c`���u(���������@%��}�]=�y(��6<{��H�fcTLd�V"�4fdAT8�c```p,l���a�o�`�0���0l�s��rP���l4�p:O����fcTLd�p��5fdAT8�c``�Ȝ��;��
���k�1o����M#
Y
�^$3�j7�ɔ�fcTL
d�cB82fdAT8�c``p��uc��7`P�P�
N���H
��!(I5)��u�fcTL	dO6��1fdAT 8�c`�?pys3��7 ���a��gxemFb!d:�ۓ2#��yfcTL!	d��Bq3fdAT"8�c``p���L	��7 p^���
����A�x����E�3Jg/���fcTL#
d�14fdAT$8�c``�����n ��^��Mo��
���|���DR��
n�.��L��fcTL%	d8l��/fdAT&8�c``(���b�|3��@�9C1�U��B�"t�MZ�/�<gfcTL'	d��f�2fdAT(8�c``����41�8��|�b�f0�06#��pS!b
�_���fcTL)
d���4fdAT*8�c``�Z���C м��Kހ� �!������I�0�.�h$�fcTL+	d�k3fdAT,8�c`�|�>?��o@ ����xs��a-������E耛eM/w`.fcTL-	d�[1fdAT.8�c``k���t&�|�>`ƛ��֛$B�n
^I/�.��fcTL/	d���0fdAT08�c``�;��j-C�X���\ҡ�F$B�n
X�/��T�fcTL1	d��}�3fdAT28�c``�>��vC���� ��z���a����B�"t�MR7/�xfcTL3	db]��2fdAT48�c``����d�X8��v�b(c0�0�"��pS"�
�l�7fcTL5	d���o4fdAT68�c``p���,)�Wo����7`�̠a�ك�B�"這I.�W��fcTL7	db?2fdAT88�c``p��0_������J00��zd �cH,�,��)��
=�:mfcTL9	d�rL3fdAT:8�c``���1���K �pC���8�3�C��XY$0S:nipufcTL;	db�ݥ*fdAT<8�c``�cy v1�3�A��u���E�5��Գ��fcTL=	d�2�3fdAT>8�c``H=��FC�X�Ƞ�
�z�
a��Ab!d:�U�/
 1\fcTL?	d�*�!3fdAT@8�c``�:��N7/C���� ��z���a����B�"t�MQO/	��BfcTLA	dgs�0fdATB8�c``��z�h�Ä7 pJ���
L`h��.#��pS��0�&��fcTLCd�#KI4fdATD8�c``�]�om1/p�}�A��7��9	�B�(C1�hr�2�Ȁ�fcTLE
d� �55fdATF8�c```��bv*7H�{WL��
o �*'���Fa#�Aы0�O3���A%fcTLG
dG��4fdATH8�c``K�Xn���ހ�r~'�
��v�xod1x�*QMA�ړ4�y�n�fcTLI	dvD4fdATJ8�c``�/�J�e��o@`�C�(�.���~���FV���L16��fcTLK
dT ��6fdATL8�c```P
N��df��o@�v	����
��ֿ�t�JTSPl6H7d��[fcTLMdB��5fdATN8�c`Eq`�>�������@���y-��ES�n�E2�7���FfcTLOdn��6fdATP8�c`~]s
�.;�^��0��v�A{.�b4�P-[#8Y�'fcTLQd��TV5fdATR8�c`?I`�<�������@-�g�y)��ES�n�E+�7�z}�yfcTLS
d�27fdATT8�c```Љ�N�ff��o@�f����
�y�a�7ϙ�y�*QMA�-\6˰3�fcTLU
d�v��7fdATV8�c```PNojL�b�9o@�U���
LrTa�7���y�*�LA�չ5�w�M�fcTLWd��\5fdATX8�c``�M���Ã�n��R@�“7��)yhe( 
�j41lH6#fcTLY
d���3fdATZ8�c``p��m]�,L~�<b�@���P�Hd&�$m���2����WfcTL[	d�m�0fdAT\8�c`�;pis"#��7 0����
3\���!��pS�0�7dt9fcTL]
d�3fdAT^8�c``0\���
- 0z����߀�R���C&�dmp��.�^��HfcTL_	dT�W�3fdAT`8�c``H>��f+'C���� ��z�
a����B�"t�MT�/
�]V�fcTLa	d�7�3fdATb8�c`���hˎ7 P�f�y&�0�z��B�"t�May/t��fcTLc	dW�?�4fdATd8�c`PY���z�;o��
Ü7`����������E耛m�/�X�fcTLe
dس�4fdATf8�c``p���< �x����g߀A����
I�6�a^/�[�bfcTLg	d�sA�3fdATh8�c``�=��v7C��� ��z��a����B�"t�MR�/S�ifcTLi	d{p�0fdATj8�c``(���R%��7 0����
X3BY�H,�,B�\/��fcTLk	d���3fdATl8�c`�h���d*�7 ��f�y)��a��Bb!d:�_�/
��t�fcTLm	d[��2fdATn8�c`���p(����}?d20d�y�BX�H,�,B�������fcTLo	d��22fdATp8�c`���O�� �Pf0�df�a1 ��pS1J
���qfcTLq	dz˵�4fdATr8�c`�����N'�Ko��C�08�p�z���B�"t�Mh�/��(tfcTLs	d�ZV�1fdATt8�c`�����V;��@��+C#0�Bb!d:�zn�l�$fcTLu	d
�$�2fdATv8�c`P`x�`�� s�����cg ,1$B�n
�;	]�}#tEXtSoftwareezgif.com�óX1tEXtCommentCreated with ezgif.com Animated PNG maker��e^IEND�B`�PK
!<�����>chrome/browser/skin/classic/browser/tracking-protection-16.svg<?xml version="1.0" encoding="utf-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 xmlns="http://www.w3.org/2000/svg"
     width="16" height="16" viewBox="0 0 16 16">
  <style>
    g:not(:target) {
      display: none;
    }
  </style>

  <defs>
    <path id="shape-shield-outer" d="M8,1L2.8,1.9C2.4,1.9,2,2.4,2,2.8C2,4,2,6.1,2.1,7.1c0.3,2.7,0.8,4,1.9,5.6C5.6,14.7,8,15,8,15s2.4-0.3,4-2.4 c1.2-1.5,1.7-2.9,1.9-5.6C14,6.1,14,4,14,2.8c0-0.5-0.4-0.9-0.8-1L8,1L8,1z"/>
    <path id="shape-shield-inner" d="M8,2l5,0.8c0,2,0,3.5-0.1,4.1c-0.3,2.7-0.8,3.8-1.7,5.1c-1.1,1.5-2.7,1.9-3.2,2c-0.4-0.1-2.1-0.5-3.2-2 c-1-1.3-1.5-2.4-1.7-5.1C3,6.3,3,4.8,3,2.8L8,2"/>
    <path id="shape-shield-detail" d="M8,13c-0.5-0.1-1.6-0.5-2.4-1.5c-0.9-1.2-1.3-2.1-1.5-4.6C4,6.3,4,5.2,4,3.7L8,3 V13z"/>

    <mask id="mask-shield-cutout">
      <rect width="16" height="16" fill="#000" />
      <use href="#shape-shield-outer" fill="#fff"/>
      <use href="#shape-shield-inner" fill="#000"/>
      <use href="#shape-shield-detail" fill="#fff"/>
    </mask>

    <mask id="mask-shield-cutout-disabled">
      <rect width="16" height="16" fill="#000"/>
      <use href="#shape-shield-outer" fill="#fff"/>
      <use href="#shape-shield-inner" fill="#000"/>
      <use href="#shape-shield-detail" fill="#fff"/>
      <line x1="3" y1="15" x2="15" y2="3" stroke="#000" stroke-width="2"/>
    </mask>

    <line id="strike-through-red" x1="3" y1="14" x2="15" y2="2" stroke="#d92d21" stroke-width="2"/>
  </defs>

  <g id="enabled">
    <use fill="context-fill" fill-opacity="context-fill-opacity" href="#shape-shield-outer" mask="url(#mask-shield-cutout)"/>
  </g>

  <g id="disabled">
    <use fill="context-fill" fill-opacity="context-fill-opacity" href="#shape-shield-outer" mask="url(#mask-shield-cutout-disabled)"/>
    <use href="#strike-through-red"/>
  </g>
</svg>
PK
!<��	�SS6chrome/browser/skin/classic/browser/translating-16.png�PNG


IHDR�aacTL4���VfcTLxiĉIDAT8�}��KSQ�o)=�Ym�nK�Iի�TA�����7A�P ���Q�F�f�����E%�HI�=���n���6�6w�w>l��IRt�����{�9����TfkN0u�Ĵ��T#�Y�!gp����%�~������\�.��
L��+x"�P�ĥ��2|�H����30��alg��+%�"�ľymq��Cݗ)��9�GSx�ANs|k�mi�W׈B�F�� ~h> �1��`�L+O&�J̢?�D��),���\�t/�a�m6��`�՘�#<���>���L�)�p�GB��\��Lǘd��R&�D��;?z��|��9�����q�H�x7CS�I�h� ���Ʊ��;�o
���y�a�.V��!��r �]�q5����0��l�%��o�:�=FG@mp�jE_n2^
��7v��h7���>�ο�5�.v��0M�Ȳk�i��v�o�#��.e���h���z����C��Z�׎�l�cۭ 66�X~Χ��q|���Ƈ4�_���/k��ei�}��r/*ߌ�/8�K�DW�Vr�K7�|հ�?�q���.8��*��䌀-��q�Y��9��'��̍���7�l�C�6J�y*t�B]��jn��ZuZp[��x72���1�	���XQ*`���|H�ս_k�"-iϺK��.ag�O9#>1|	L+zm����o�?�������.X;���5�ʗ?PF�T!`e�������]�sM��qGآ���;ܯh��Ԑ�'K�����fcTL�.]fdAT8�}��k\U����g&ӹ��:��5��jSA4�PiT
�[l���B[/EE|�� �؀�� �A�-��E|P��4&�&�g2�9�s����QA���XK�}\N9��J��NI[A�|B0™K{3��䦩ռ*����.��1)W�ЁA[i�����Ur�u-���WoKї1\Xv|_�=_G�0i�@�~8�����:}y����5��B�����b����u^�����5�:Y�����ܳ����P���u�%ǁ/k�Ɓ����+0�ȣx-�� �#O�ˑy�Z)+>]cWw��B�k8�X�Ʉ�h���_D�@���!;~�ƽ}I..E��H�-o��L�h��vT���~��9��=vel���X�O���iC���s�NK��we�q������Ot���u�TF~9X��1z�W勅'��m���J�N�0T�4:rIY����`��>��XyY�~�^o/���@4�d�B�R	=���ִA��n��W��=pV�߃���~}F�o�#my�t��e:2AU�n���ľ@���Mj��.>��R<2����c�Uޙ�М2<5�'P�ш���:_�8�u�8�ƒ���e-o>�΅������f�q�
(`6F����>�}>���iu���{����)Z7Y�2���I2�^|���O
�~,��i)��C��p�`����z�9�u��5G*�f��|�ҧ��]˕���{����#rƫ���w�6�efcTL����fdAT8�}�]HSa�W�>=�FmvΖm3!��.� ���"²!� �̑��V)~,KMMgXBQ+��(��H-Κ��;��8�y���G�{w��!�����y��<��yU*�F�ф��0A6
�fP&z��(��F{OU�����2"��q�U?��$����>*�D{�#�����)�+�G_'���||s����`tPM�rl���ĔJ�鎣elBb�9�x3��̥J��!�S����+L�qF�#G�|�_NeM��x,��X}+&�՚�g{�q��fu�����1fy[T��"�[T�p���A�x:���@5FS��cJ����{ԠB1��%j�wDq�5���$ξ�p{<���,�;cдG�y��;⊒��DI��7�|3�Y��R�C�"��E����0̷��a�2mgd��F�|�E�B���.e"GD���N�����"E�ZCdK[x�j;6�-��������,d�� ̭�މ`�#�5W���B����*ث�vڴr������z�_XuهƷs�I#Lr8�Am�b�- S=�P1�6M��(=��|��K�n�ƙG"��E�X];	m����>������^�y�)��O����
��q��$��i����$�Zf�Eꭓ�V�D��,����B0N+��Z�U��������_�Y���s����/����b�47�y�����ƭ��-�v�6V�Duj�,;���N�+��Oh�]�y��k��
�8�fcTL��}��fdAT8�U�Mh\e����	i2�$M�$m���&�(���-��F)�PDT,X0��*�P4U�Dł]H(���D�����D�$�Թ�����9.jJ����-��}?d����8��F3�N0�R"��kO%OpO`��FŌz3�D�q����
O��-� �:�?S�v``�l%�ƣ��(��X��J��[`��xo���.}�>H0����mfo�,m%�6�J�ڧ�}=r�~�.T���<Gv;���*B�,�u
�&���+�v8cy8oA?�j�7����rf�����^����@��ZL�5��+1f+1�偈���3�S��g����8�S�v�ܙ� Q}�m�9v����[z�ѲB�D��4p�e�'ѕ�jSC-W$��^�})C,G�~��tpf�r$2����H
BR�5'R2���W���s�E�6��0��U��%'�:iS����N"D�*�܈5�i���M��]m���x�R���u�I����y���y��M��?6�l>��qz�BlF��
��\�`'-�K�ۻ�,�C���'{뎙_h��J�wzw�o�ޞ�`�Y��pL�)���ѝ8F{�$�[*�?�{_{��$�Ƚ��.ig~��V�|�k�f�K�����>�7}�vp-��l[�	��1��/jvl����?�kVeڗsNfcTL�7΋ofdAT8�=�MkTg@��;w2�0��h�im���EE�Ʈ,T��$Rhi7�nB��	"��6`5��RW�ݴTD7m
B����0&��$wr��y\�΁s������u�hzވ�#�!�)�D�=Ov5��Tu��ޠ�;a���c�U%�p���V15�l��ͺ���yN����L�)��ZٷHh��լ�?��yID�F{�g>��?��2�i](y��+ft�FM
}�'©��[�s<�d5����|���Β�yU��^���1��PO�=j�.(�#J���	b<�N�W���,7���&�G[�����'
��hM�Z0�����`<cI��ij�ف�����z݊Fz��Zv��̋-����R��)o<�ī��c�����&v����[:/{4H�(�˞Mo�h��Ԃwjm&� �L�N�	�����r�g*U�^�`��QL|e!\&�A�"��h��w����#O�[	�JAl� ���j<��
�{Z8����T0@21,~�w~�`���P
|�]dEs�8`Ukf
�_;�5��m,��k����:K�{ɿ/R
	 RZ|	��,�/�~6��dJ��\�D�~x
C?5S.�XvfcTL	HD��0fdAT
8�E�OHTQ�����_�q�45�$[T��]B��]����m$�]P-
[E���EѦ �EJ`X���of�y�M>�|8��Q���맀ǁ�P|��i�i��)��ӧ�^���9wB���3���[Ҿb��Hx�@�&��ýI.t��W��E��T��%GkJ5D ���cd�J_�G脖��f�C��zR|���%��s]1���#���	�4iV�Fĺ����:����$Y�����2{r>�	�$t��H�>��|��Ic�˭��Ol��g�Ƙ���!�L*�^|S��m
c���5��_x�����I�W�e��,�P
����2�V3W[F|1�5�A+����`z� ���okn�f�@�b9�#��ꓚ��c��K�L\G�фv
c]��nXcb>��-��]qJ���8+7l(�X��r���_�5����D���띣6tM�p�'C{��
(`wc6,ΈڊxH>�R\y�����z�|�̷�*�8`m6*
 �}�O�—��wfdP:R�\Ƴ�ʻM��狀\�fcTL.��DfdAT8�}�OHTQ����͛f�3���!.�
�*A���@B��\��e��E[�E7Am�6�"
!��$��$"���,'�ir���{ڨ����|�s��;�U�oׅ@CKLa<�n�\jZ�%r�…����Q�bCXٱd[4���
�BIj�}��reLJm�R�2�f8WI�H@���gN��&M&���5��*�%"�Z>�
5+���n��P��tϓB6�.[�f<$Q�-���e�z�u�v�80	���cJ|�V0��3��Uq,n6h��2w-��8q�g�~Љ8A��-��{e(����ǍS	��Y�1t$�B�p�ʨ���a�|EW�ǧ
���ލ~��Jh:�h�<;���_l8��<�.wx�[�_�h���7�J�s�yo�*΍`-���W�A6�x<���3F�"�f���s.�S4�;U��|@O���Y�
���D����B��h$��,o��P
b\4���)����

+�L��k�$54,b\<
���J��c��o�~-㜐�1$c�t��8��'�1�<��ˠ���k���Nf���D-��@fcTL
�@f9fdAT8�u��K�Q���}��5�Q3�$*�Z�o䪅�����\T;�].2�Z�B"Z�R!�Z�BES���83�3��_�h��?��p)9�n,ya��/�B���B+���4��b�%�~�a��05hx����6���i�x��M9�>a�O.e�D����~�&
k��I�dE^o�X�l�!d
d54��w�$q'S���]�nd�b�z���}9�H�bn,�N܎��+T櫝�[D� S�6��{���M�(�-י8�a|��ۡ!zAU�U-�%��'( �(�hKty(�n�/���8W�\,g�yWcm�ћ�Eu|�G$��F1P4���Ρȁ6u2:��{ͳ�#��D�mp>���{��f�z���W0<]�g~u�R�ԏ�Ͱɚ��;��?�ٸ3X�P�Y\���y|���x/V�h���P5ml�g��z�hx~��H��[B��^� ����T�7�f��r<2���ћ7�"�p���@+	JB�[�x��ͩΏߚ̾��ʩK�c>�Z�P�K��xith�Vk&-m��BZ���ے�j%xi�j	$�'�fcTL��MfdAT8�}�;�Tg���r�8;3�3+�[V-���ib�l4`��`!I�D�4"(���`�`�hB҈,beTo����5;�eg�9��}6V�Vo���d�F�ی�e�8�lL,��)���.���f��A5�hb���(�O"�C��U8>Q�uW�=�W!uf�@@��$��ؔ��x�����cd�q�YayJ���OW���
�^�5�L>�ز�S��|Ȓ��4�c�w�,�����PvЙYH+����UPeb���C�改�ȃ���j�!�u���Tm�ۆ��"�Z�X�o��
�/�RV
[�t��w:��{V���c�%�V�'l\V��#�,�}�Y�����K��)r�i3r�
,���>���-�Ny�>���<{�Wٷ���K\|ۣ՗��_�Lz����/3��,)�腛^���Gv������k�@,>����Fr�y�\x������ůK�>�1�ȏU�Ꞩ�E|3�@_��ؚ��z�_tk��U�����6����=ǿ�}�%0��}ū譑E��?/{n��k�
1媓z����@�g���Q�n����x'Q�D��� �'m�����fcTL�$�XfdAT8ˍ�MHTQ��{ߝ7�3�kt�!-��\���H�6ҦUnj�Ѧ�Qml�"ZA�mk1��B���
��N��s�{��-"�l��,>���Q��f�K����wb�����LjC��%	7dȚ�x&V���������J`��	��HOe�|2`<��wT�&�D{�CM�="d+7���N8��7�m��Aɛ���#{��d�M:&�KJ�5W�V��ҞC�%�t,�f9��D$R�G���Uz�h�M؂�����(a�_��y���1�չF.��X�Yů��J>'[�M
dž���`��Vo
[���6�x��L���d�?�2���Sr5��#��D��z���f@J�W��ŀt����If�\2+.�uRd`�K��L�����=�B�OX���&��U�r �5��G���'fѕ��Jp�f#��?Tߛ-�����|@��4Ǭ7¹�h<�pi��Q2+*��~&�V�/'Zj�m]�H����%�z���l�)�x��D�T
X�T��F���x���/�䷛]Ö�5�P�N"t6��ot�J.��:���������6��}s�~i'��:�fcTL��vxfdAT8ˍ�KhTW���9's瑙�I���qшJ
�X���vUЂ �iw]�P� %P	(��B�.J�"*.EA��tQ��h��4b��;�;s�9��
��o����?6q��YD�V�ʝW��Y�?"�c( t@��\��;�Ʃ��E
��WǢZDdk��>쑠'.����;�x_)�Ʃ�k;�LI2��,�ȭBc�7��8�|7�^��&*9���O��
�AK3	���1���D�e���wWY�/d䍰�$����^&a���a��/P+Z�Ϯpr�%���Z��o�rkD�d�6jy���v� -�4�f�+wc��5�>�^���O<���B�+)O;Jd!2�I>j�-O�d���Q6�8�~Mh�㕐)�m�{�]c�jjya�pb������/k-���,#%��E����m.�aؐw��\�{z��Yߋ�����$
L�s|��͍�m<���h(��*�T���.�j��a�'7��脜�M?����<�����=,Q�l{v��.6*#Ϻ����L���[ӳ�V��P�~X��}����7!pՈ~�r�#'מY*���*�@�	�|<�#������" tzif��ץ��/Կ�i�$"��f��y�W��#h���fcTL�R��fdAT8�e�Mh\U��{�{o>�f2q&�IhJH�)֪P����*]IE�Q�M!;? �(EA�Ņ�-����ЅZ����.��&j�N��L��d��{�^W��g���#�c�Q`;��Q痞�.m;՜Ϥǟ[x�w~3%�c��nK2O�y�P0�����(u��<�p���u�R�ߦ6�Q>�Q�q� �J3����d���Q2�>�߃�o�m����A��ߛ���ҝ�<0�g��9:��[߮RH�_�o�������c�w�g�Sg�ԯ����#{z9>נcY;���\.mv�Y;��"�w~I��I��n��h�~��$|w1a�O#]���~m�߹nLڎT�����6r�47��T
�e$�oWS���rHQCҶ�k�]��}@�o8��bJN��#������W�l�$�g����Ϸ��ޡ�<k����+�!�T�b� �^���&2��of�o���R��`Q9#?��O��-��/�4D
6GΈ7GF��C[��u~�j�v�!�h/KO>Y��{k�;��RF��7v�y�L����_�5=����̻W��;#&�9-Lϵ�u8`OŐu��u�:�xX�=`�;΀?�������j�Sv�X8��g�d��ʋ��?pd�e�P-����V��{8di��HCE����B���?�=�.��}~�����aD��v
J9��?s1(	O�xfcTL�V��fdAT8�m�Kh\e���\f�\r�K:�L'�&�`)��ƅ�EE*-.� X](T�f!.�P+��A�ktS�EQ�� ��h�`Ǧ���fn93��B�">뗗�W�o^�\�0V�X�K�#�؞���������}+������$]��ui�����\ 8��J�3G�uf�jhO->�?���{qk����ܓ��G���T�ի=L/^�2��y��`�u�~0����.7�ڦ��R��рcw�w�^$%�٘�\_w��
�D�Q��Ԩ�4Z�j���L<.��#�c����`�zBC��m�`��7T��f�Ȏ�˾��|ʥ�q�UL�%�4F��Ϻ}N6)���Q65��[�X��g�(�<}[�[�R,�B��?��U+l�S��
ͮ%T�ԦP-�E��g)9ƈ3�ތ�ē+�1[C.�#?�w��E�Ǟ�pG����.�-a�@�x�
9l������]ҭ(�,e����{�^��T�j��u�Mˑ���T��O��xj��w^I䒺��d3�
I	�\�x9b��$]H:�	-&�ꨱg52��3h�|��x���F{�j���U�۷{�0‰�)�m���sK������"�+���wN�W8�u��a�����|�%��u��ŇA��j�W^���R�;�>?�FL��T�y��?���C�_��|����k_�шon�,{�����Rθg�V^PW@�a�fcTL���7fdAT8�}��OQ�'B�`0��0-B[ ����1��M\^x2��5J�D+ FA�q�B�F����V[�
��~�3,�|N�@$o�˽99�Y��9�6�DV� I�v	E���l�
��#���R�١�������.lnt`�%�dSh'�藣��_��V]���C�98I��|�/� Ckc5=�D�C	�\^A���,c�M7���=�0�`u)��KV��+X��4k�C{E*E��
�/�KY������?�`��am	�u����T��8u��]}/u�_�j��m���:b����sN:���H8k�d�愘Xȳ[=��6oT��C�/�[����b3�"�_���GU��(�"��i�G�+�Yͮ�
M.d�tI���&C�c�*�
�ǩ�AB&�s&�vr�7��u��}6CKm�oɺ�9��k(�;Oȵ�[c��3F:�`@l4�j�
$�'L�0��q�#~ ���{��78�D���Q�O%.�{1�y�C��a�[7�s��ǞW6=��s�Z�a��F;#����3W���O�	tG�;�-�S�ܯ�(��P�ϙ�M8���`��qs�
�6�ʺ��f�9>�N������G��Gr{̽ YrL��Ϩ����c{��&�VC�x-��p�.f��,�-�Đ/�v�,V����o]a9$�`��Gee�\j��dŵi�KL]�Z8����,Lb%�i���Q6�48�[�8�Dǰ��w!�����n�tn�wR�Z�q�[nCB��P5v���4�Ț����6�r��"�fcTLAy��fdAT8�c`�VܜfcTL����fdAT8�c`xmBfcTLA%V.fdAT 8�c`�u�fcTL!���fdAT"8�c`iN9�fcTL#BW>OfdAT$8�c`�s�ffcTL%��L5fdAT&8�c`jH�fcTL'B��fdAT(8�c`�y��fcTL)�x��fdAT*8�c`oBOxfcTL+B�}ifdAT,8�c`��fcTL-�$fdAT.8�c`lDtfcTL/B��fdAT08�c`�mJ�fcTL1��k�fdAT28�c`eV�zfcTL3C%�fdAT48�c`�kq�fcTL5�� fdAT68�}��k\U�?��;3���<'��&��Z!B�b�u#uc�ںr��](E�UQQ+T7�ZE���
Bhp��j�X��6i���3���N�9��@���Ň�W�/�(&�!2@6���wf�P�]���ͅf�uR�"��#�L�оI��DWS��M�~ٗY��@;c�l��Zs�4�h�oO�\j�D"/�02?t2\:>�7��1�S�˼7�h�l�wB�aq�����ɇC���&��冣��x���~������E�uS��@�A����J1�Rc�Nj����5�n�Nr�@���c:��璤}E�j���Y/�oNc�iQ���,+xXc�jO��s5^�\�3���P���*i��Ʀ�^���t�h��Ef��c$�/�>�;�:���kŧ��k��f�X�9�ʥ6�V���{�siO�<�A�F��s:��k1�Z|�4�W��~�=u!��g��
�N��":Y��jJhP��
�fN�ܯ
ǖ^q�����O�W�����M��P��x�x���#2��iQ}�_�8 V��7�4�V��bK#}^��[
i͹5��u�sg�(T%��Ղ|�qyb�;�uw,�/�K�v���
N��,ք�)
��a���٠���~c�Qg�;D��Z�}]����j�XX�;ήX��Z�o�}꯳����8XQ��˪�ю`�Ux�'����7�����G�JO�׫��a]�O�u6ՈZ�J{���YH��J�pg�m���9�jhT���<���rhwUc���I�CK)r�3F�?�w�%5�fcTL7
G��fdAT88�}�[HTQ�)�ԙh�q:3�sK�ꩇ(����{(�ЃPՃA�$�KZ9� f�ٔ)f�FZ(R�Ѕh4���cgnǙq�����ߞ=�Ԃ����Zk�a�in�r5f�p��G&/�]�%W5{I6���b�g�&^�j�j�	}3���~lk�"����,4&�gb�?�F[xmF\sՍ��y|�/�G287Bn���x`�%0��QC/9�R��ͪ��du�;ͳ8;( �����i�U8!��J��{�rCO��h�2���S,[�$�z7>�$��>�Qg6a���p�%�ͥB����-a��3�I��*˔��}f�����[�������|q[@�~�������8.	(.�e�6��m���C�p��h�Xwښ�n��s��DQkE����mT:Z7^�1͜��5ʜ����P�۱��^�>'���,��t}d� )G�7fwH��ȷ<����P�E�v���?-|'Ɓ�t�@�u9&�V}�����>iN6�e(�����X
-�4���c��8��ǐ�\Ȥê�q�:��U�'��q�
jd=1��H��/i������P�g����a�a���\����J�+
��L������M-B��x�P�em(��J)P^u�+k��m��7"�қ��%�0J�s2���)�D���<ST8�)��L|���3KRgFE<��`��䚃��=��ol�p��7p�u���F��?��G{{B��=��?T/8���#L/��G�Z�$�����^Q�r�y���,W�fcTL9����fdAT:8�U�Mh\e����;w&��'�$m�$��u����U�p'�E�"]�����.��*���]�.DK��v�4�i�dL2d&wr���;�ESl�9�/�c�>�`�3p0�Jb��BC����K�
>�]gv��,)l���
�E�f"\mX*]F!W�..��w@@�S�ڭ�fƱ��'{��Hǟ�Xmt����u��TT����g��''J�t�"�TE�9��2@�Viu������W�x#a�z�@��s�X-��7@C�φ�ܘnGUZmg�B�,pm=����jH�I�1"43�����IF%O���63�ש��?6������;Y�H&���"�S�6���4����x�W���g�/��+����YJV�^�qb����K�Z���[{����_S+�m�l[��f�\�卓����_0�go�:���AO�P�/����k������Z
,�,�@��J�LE�@���)�h�ϯ?|n��rE����-�`�<q;;Z�'sh�>At��/v8����V�>�=h'�����:z˧����1uzB3����]>O�*!��ʆٱ.6b���sVf�?W�G�m�j�v��w��ǣ�^hse�1�`Sgd4w_{g1Jz�������B.�3���X5�ɟ�\��R)��W�����g�?���x���`�R��H���Ū>{�x�?�e61�fcTL;
q�&��fdAT<8�U�KhUw���<�#�ALLԘ�b�Z�]���;����E�-���H!.D�PZ]�����n���JQںP4�BL
�$���y�\��ta�:�a>�@���vc�v�_��A:0dC Nt��і��V��}.���1,�{�|蟰�c(/l�ଔ�*	�t~1��]������4L�#��?���W�!�h�WzG"~�=I�󈵭i�,M13����S+]mW���C�(���O���6���1:��*��i�帊���{�9x�����5yJ��`�B�
N�M�>@aÑ��n�ݧ5�Y�=��;�7\#���y٠��
U��4u<��^�����RW�)�ذ�n���;���탇]6K~i8����c�R͹�3U�%�����+�Q�n_���M��E���SC{L6����ϱ�ST�
u��ʲ0et0��Cc��:o��CN~�E�hMy����s�"��L���Y�Cw`ꥣ��+z�/j�Eu�U���\���7j�B-�E��e[
TY���?��'iƫBϘen����5r���v�>�}�aI�c���ª���4D���;������7ut=�N�~�,ǹ�*������"�B>�5YN~{=�5QZ��oă����'��ƛ��JHD>w~7fcTL=K�ԐLfdAT>8�M�MhTW���ͼ��d�L5�ilk�hP�"B?v
t袡�2����!��Pҕ�E\�(�B�M��iS�Im�N$y��3o��{��:c���|^Ƕ��_�UQ�$D 

Ŝ�*��_��o�}�N>�#���-��/2�D�C� ο�;�}o�0YeS���
��Wi�]j���8��@i�t��a���|4��{y^�2P��y�/���V�3s����<}����xE{������8�&�l	�
x����Gzb���J�N|��O�,v�-�n���XNYXI)V#l!d�tTn�����SW��/�)�J(�J�o��.��y36���^�V�'�q���ê*Q`�C��t%�^����;��`X���,%ƒ�g(4,%F���$��i&���ݞGw�4;�oc��FƏ/��NJ�"q[��5��v��C��)xA��h&g:���@�jGT��t{��n��%n�_�<�����|d�r|o�w+�g���Ä]#!��]o;�U����L��K���s�p�K�mn��R˙J{#��(nK|�nhO��_���j�-E�C�^��+��/�"fcTL?夔TfdAT@8�}�?HUa���?��c�y��1�AhL�h2�"����j�����[��Z��0�5DFD�-uA��y����y��`D��7����(r������
P
\�vv�{?��>O3�4�LlV�@���������|*�{\?�I�OM�?6Xj�"�y����,'��s)�%��e6T�).��X���0�t|�f�z�F�.�-w�s+���E���nv�97�M�.�t�M�'�b-c�%R+��1�`�4�K��íw}��#�.�SZ�ǩPl/�*�we3\���:xL�2�@Q.�8y�R��QW�,8��y��e*~��m�i5��5ƨ�/���S�)h㌻�h�&������29�xL4Zx�ݲ������M����M��˕c.1Ł�'B�Z�ލ_&,�j���ʛJ��{��׫q�xհd �B[Aq��gCY��z�R�Ԭӵ�ŭ��лVs�"Ƿ��Sk�|�6M�/��_y�N.�{s�̒c|*%B
y����Tʉ�1q"t���#���Q�5t�t����<
a���3��̳:��Jڈ�Ѽڪ�.�(fcTLA鶻�DfdATB8�u�=kQ��;;��f;�	&C��b������~?A@,,RX
��NAA�^�����0�QL"����Nv���F�����(r2���Q<Sp:Ʉ$ʞ�u"�.�<@2̀Pr�U�vbY�Ψ�R3�J�#��n3���s��N���~�b�M�F�v�<��)��?u��ئ�;�M��egǨ ?�l̏w�;��4w/�9R�,�%D�E��P��O�x��BX�����m8��N���n��B�ɺG�z�����Gj�I�<3��v*$F=+���?�~Ϩ�uΌ,�%�R˃�m��O��&�ơ�nb`��rn��᪡���o��#i��_}�bOE�(�ڊ��f�ny�h�@!�u��H�0]7|i[��)���&{?;]��!����޽\+Ͽ���9\>�Qt�S�A�����yg;z����H43�rv���A�W�(����[�\@U��(����"�|�����e�ޫ�R��8��Ufw���‡���k�p��E����* 3q��^<��;u)`j��{۰�X�B�u��8���}�qfcTLC h
VfdATD8�}�=�Tg����Ν��;3��fQ]q;EF���MRD,$Hژ"B �
*� HR�`��FB҈l$�J�"�?PVPg�����w���}-���?Oq�1�`����O���Wb��Ӡ��1���f�sMruͷ}+�;��ʑ��d�@͠l,[boy��i�R��\���ݴ�Vˎ�".�MR-Z��W74�)�j�y��[�1�����E1��Ƌ�}����/sp[—�>�X��?z�֔b?s�{��u���g��x��qka��s�8�����(2kȚ�%/"�T�Y�S��½z )*O��
�MX_�l������|�i;2���"Gg"�}��Jৗ�nN)�on����l�8~���Շ�
�H��*Ys�\y<`ה#��%M3L9�V��G���=P J���������T�Û"Pev��x)�ֈ�^�C���ә�/8�}A�~fK׷r���Z��Vd(ʫ��ݟ~��2mzH�6.l]���~b�G\�ב��8�'��?=n/�Ef]g�W|Y��JO����4S>�p�ދ�����T�/zg��|��`�b�d�����Ŏ���3AD�G	��/x9�vfcTLE��p[fdATF8ˍ�MoLQ���9�N�s��D;�jiʐx	Ał� ��666"��4ABl��,��JbQ�A��
Fcz{{���s��EW���{V����mBd���׋7Y�<rkn�E`%0�\{6��Y��
FK
��~�+��VfI:(�i%8Ǥ�����_��5��I�5)�}E_��R��߆��B���x�8v�u-�(h�e1�����B��.����X��jAsi�ɕ'M��7����[�–\8R�pCYB_�m3�[!��~q�����s�i�zl�dž��ŭ]�͛���T���y��Ps�P���-�~i1P�(�p���=����]�,��Y�İ���>���[�hj���vjE5�+4��{�䠚֕��ۺ�J�0�9����@q�Uę��xآ7�drrs���y-������R3��N+6�ߛA�����W��V���+S1{�s<���R���5G5	�T�/�d�$W)y�����ȏ�HVݨ?�'v4n[p��}��Y�e �??��Wh0t�:k�f��Q˜���*x2�'~���_R��꩷�Ed%"��_�b��xJ����t$fcTLG|ə�fdATH8ˍ�M��e���y��܏�;�q0E��I]�j��v��U`(BB�A�\��e�J�q5C$�&utʙh��}�{�<�%Av��s��w����K��"�Cѹ��������›/Eob�P�)rt�@��'~��J�P�+ؿ*�w^�F>-��:��SS�b����ͼ��ᑱH&��ל���+�S�VP`04�?wù�̇�Jj�<W/��2#�_G�8�lY��Y��� ��(U�̓-Fh�	+�Ѫ�I<��u��,Ǿ;�!��~���a��e~�s�
��vTpr������壙U�|�J�tM��^��q�~�/=�b�b N<��^�p\�!�l������AUҴPӎ^^gٷ6`��P�6$|<9�J��5��WG���}���-�����>c��l��+�=k\扻�uu�KB�,͚eb�L�B�z���}5�$.�y�Ub�p���DZ��e,�d�N�>Z���7~��_JIs_�M疯/���8�
&�J�v|��k:V��b��s���+h�
k���7���R#o�^��t.X��Ւ��0����3kޙox畒<`~�pk��G��=��i��"b�ݥ�������{�@�#2�|j�}�q�&��N1fcTLI����fdATJ8�U�Kh\e���?�L���4Ij�&��b�Z�Ru��RD�
�qQE�
u�EETD�bu��UQ�*4b�Em�S%Yhj:a.gfΜ��?R�������x�3΃U����r�`am��漈�V�|�Piy��.�_8�f��J�`�E�wK'���gj�K�[0�YK{����%�]O�����>�%�'k�?��F�dX|��,�O�
�?v1���s['O���^�T!�D��,TOe`�@'Qr�o_���v]6����|�w� "k�p3��;�4d(���7�dy�6��~���m���D�3BH�;}4lw�<c��m�l =�w<ⶑ���wZux��a�lh&J�n��
"��'�c�̰|�2?1S
8�U�<�c#��r�fs�N�mp�Å�-SﮟY�����(]kkk���x�����մ��Gػ#�k�T�w��X%I�$��T٦�q\#��_V;KI;�u$'cI��k4�LC�z��u��s�=��c��V	U��x&�Z��e�����\��ٕ�M��Lf��JJ�0[�P��x�����KG����;Ѓ�t�=�tj���m�1���j�n�9�Ԥ�q�VIF^�=��?�E8*���:}p�i�@о�@�^1"��`�د�9�����/�L�2�wJ�lh�F���R{7���zfcTLK�+,�fdATL8�m�Mh\U����;�I�q�f�h��.�&QZ� �V,])U��H���
ſZ\�⢶��TPw*�b\H&�Q�&4���s��D|�/����ϝr[-V"�dA���`�ٗ[�Z�������ɥ�Ff��
�`Œ�R�YF�(p�M�ҁ�3�����w�ܝ��$o�Nqp*A��N7p��g�׷�zz���mAXH��]�2;p`"I*���V�IR,=�cq{|�/F99q��z
�V�r�H��b�UIL3y����ӱS[0���$N?V���أ^n5���ś3\�oH�‹�-~�t�Cl죸�I\�F�׵bq�]��W��;q�’䏯�S)�M��Ɓ��B}���F���ߊ�
�Uڱ���Lo�(��O2��Ƨ�
7ӌutsn$�^���V�ٕ3?vvNUI��PO@-�*�B� "��kS�	Q�hZ]f��'L���t���ߑ��������~S��*���a����W��:��,��d��CuW�ġ״Z�G�X��O����X59��c�bw��w�O�k��嚜�=�B��"���8E�����/�<,pX+>��5�"4.m��R�L̍�Y݌)����hu�ȿ�z���;W��6�ݑ�՞'��s�d�嵈v�9�]�J�!�C�:���uW�=���3�f�@Ю�@ؙ1"��cj��tfcTLM�5��fdATN8�}��KSQ�o)=�Ym�nK�Iի�TA�����7A�P ���Q�F�f�����E%�HI�=���n���6�6w�w>l��IRt�����{�9����TfkN0u�Ĵ��T#�Y�!gp����%�~������\�.��
L��+x"�P�ĥ��2|�H����30��alg��+%�"�ľymq��Cݗ)��9�GSx�ANs|k�mi�W׈B�F�� ~h> �1��`�L+O&�J̢?�D��),���\�t/�a�m6��`�՘�#<���>���L�)�p�GB��\��Lǘd��R&�D��;?z��|��9�����q�H�x7CS�I�h� ���Ʊ��;�o
���y�a�.V��!��r �]�q5����0��l�%��o�:�=FG@mp�jE_n2^
��7v��h7���>�ο�5�.v��0M�Ȳk�i��v�o�#��.e���h���z����C��Z�׎�l�cۭ 66�X~Χ��q|���Ƈ4�_���/k��ei�}��r/*ߌ�/8�K�DW�Vr�K7�|հ�?�q���.8��*��䌀-��q�Y��9��'��̍���7�l�C�6J�y*t�B]��jn��ZuZp[��x72���1�	���XQ*`���|H�ս_k�"-iϺK��.ag�O9#>1|	L+zm����o�?�������.X;���5�ʗ?PF�T!`e�������]�sM��qGآ���;ܯh��Ԑ�'K����lT�fcTLOE��RfdATP8�c`�?&�fcTLQ��~BfdATR8�c`M�rfcTLSD
��fdATT8�c`�9�fcTLU����fdATV8�c`N�
fcTLWDV8fdATX8�c`�3PfcTLY�%=dfdATZ8�c`KΌfcTL[D��fdAT\8�c`�5klfcTL]�y��fdAT^8�c`H��fcTL_D�OfdAT`8�c`��fcTLa��fdATb8�c`Y-�vfcTLcG�'fdATd8�c`�+�fcTLe�WUfdATf8�c`Z+�	tEXtSoftwareAPNG Assembler 2.7��ӈIEND�B`�PK
!<��d��t�t9chrome/browser/skin/classic/browser/[email protected]�PNG


IHDR  szz�acTL4���VfcTL  �(F�IDATXå�OLWpN�C�
H�u
�*	M�x�	����`"�5��A�6�5M۴���hQ����(H�ll�hۋ���v���c��3;�~gwf����&�$�{��o���9;���$��<�"g���k�IX#��ΉJo�Q���w#0��[�B0��|�N�Z[I��{�iY��6�Ƭ����鉀ۓ"��.%
pG%���I�ϣ�8�y�2��N�]�7D�(w�l+�p��Q�1�0�"�gD���+�RĤ]�@5�蘇j� J����H����o��T�!��:�S����t�D%#�~�^�5���.��8w�6*��s>%��QJe&s�F�J6D��\�C���<ƹ���xNĐ?y=���u��-b�n�J�3���䏦^������ �~�`,���EĖ$�YT��G(Yܾ� �qf��}���ܭ������ޙOL~�i%W|�Y��d�=�5����3-��5�#�O�ʜmmȬ����<"�GcZ������$�A��:�P�e:��&A�HdM��A��݃�В�pP�pn ���[O^hz�:F�����}�$�j�Ÿ�P�͡�o���l���%��Pw+}^��E�L	J^"kJ�ӳNB&�V7�������n�\�Ț�yj��\�U���f{�g� ��CA��`�
rVI��>Q�䬧UgU@��IX����'��$H��P�k�M�|�JAG���
0�S�L��D�~Yr��	+Ie,˜�6���(���m[�}�-���c67r�:*�E>�D0ȴc��cV����[z�y�q��H��Au��6�LHf5?����68��l�Z��1�6a�w�
i�J��M��сl38tۃ�w�Z-Ҳ�~3�O!�S�I�-*U����7�X�0;Z�a̛~�-_��'�@"]�t�z(N*U2	��X\J>���97��-�Z����eO�#�j)L� B5��}�Nm�3<�r���	��\�a�e�w���\:F#�$�&��Q���OH�c�^B��L���i���w�y^���a�#i�I;�N��S���g���|Փ��T������0�ʚ��{ۦ����Dۮ�1�iv�e�]���;	k�Z�
U���JS��-�N++�y�fcTL  {’nfdATXå�oLgp^_,�2���f�I�d���
K �d�&&�w}��..3�3˶l����P��&�Q����n/0�?B�
��������{�^{��K>/�y~��|ۻ>w��S����jYR�T��z��,
�
rp��z+���0޼��t�z���Q��0���)im}��������D����;���'"�LI�o~%��1��|D�����׫ΐ]w>L'�`}���]��?��1�$c�!g$\��_�"�[;@�v]��'���Y�h���v�OEf��V'J��!:H�HR�d:�1�K���W���e~R��U���(�^�aLe�9_�	�?HE�������&x���9	þ����#���1�}�$��i�/{�(�����$��i���sz�v��������� 1�0�zNwޫ����4E�݅�䗞�Qqի}\
ʂ��k�_��H�3�UGb�OG&��E5ڠ�(,��ɇ<z�"Z�O����o�c�A�*�}H�3~ �Ց��7�ɒ<��;=�
�hD����RD�Z���H�����D}�dI).�э���9���ⵟ=(=o��d	��2~s�h�퇾^avi�WK�Z'�%m�gg���v7��3�we�����"d1/:3SC.B���ص6:����P��*^GM5	�,)c���[�F�P]�-�@ؠ���NM[H&�H�!Ԛ
K����B2AG��tuI�ڦQrʼ_�<Y�dB"��0ֽg��D�H|�V���2�<��\
Px�QC.�d��HI!�5��f�-��&m�U]��_�
���ԣkۢ0�A��;;޹2��&G�:����/���
��h"�D�2�hg��[gp��G��k��Ԛ�j������45o����n��(F��h{胱>�&����$H�kt�QEվ��,�ğR6���.LcO۔����zŚ���c��)DX�NA[��n�mO��vZ	`��-9��X��Q�j�������c�ZA��~L�V߬G�s,<�Z��C�磛i��z�}I��g��j'�_g���I�6��V;>�*��'�}1k��4�����ڜ<dD l�+��א��%�261� 5l���fcTL z���?fdATXÝ�oLwp^_,�2���f�I�d�a	d%ٲ����B_,{��˖M�ٖM�9 .s(Nq���ƨ�ōu{����@�\������8z�}�zw\��r�'���=�r��^A�vl�MԐ@ȓ��� ߣ�gQ ����5n�x�f��I�uw	Nu����a� 	���K�ZՍET�%�п�6>w��t-ODܞ����L�^Hc>%��Gci��-����a��A��H2wvI�Cf$�a��a����Z�n���zb��%5�u0��[q�?q�k�^��t�����ɴ�1�ߢ0���"���Y�����^���z�ƵZ�J쁗"���q^'�x<'a(���	)s%l3�q��*IZ��-��a(�;��r�ļNoJ���2R+2&�%�<��/��/}�6c�`�٫ս��_桨�����4���a�d��ܙȜ���i3�H��Ld
+���!(�b����є��@\��s3��V�Z��@�g�SG��n!����bggS�#(&�87���%p��6���hy���Ga>[��"�\'��FS��A�*:�P{-�7~
��lf�“%,.��'��V�~��q�E�V�ɝ���O�!�� ���Y8�j\wQ���3� �ɯ��@a�4�Ӧ�-P�F��B��=������RT��"�u���rj�M2�D��j�u3���I&���2Z�Y*ڦQv*w]�:�yR&8�e�˪��E�x�T�1�ܞ޹e4�9�Z
,>�kd��H.�/���k�x�E��G`���u�����	/�zLk���0+�n�^�AY�O���5B��3k�-���&I�á���>|���!�;o�=��z�_��P�@�Ti�����A,�i8m"��;�ܛ���'�@]�tP[SU��ay%��E�}n�ڦ�����9�6��>��!�}��1�O����u�X;�Z���Wxx��N����|����pb�G1Y}���Q|4{�s��~9��6�ND��۳�2�����Dƿ���y��~1�ݭ��:��rdD]��4�=�>[oށ/~>,�)��gC� �ɯ��������CfcTL ��@fdATXå��oSe��Ka�:���H&KL����D���ygƅ��`4�ĨA6�#�0���!@�A�
���:�v�N׮���Ӟ��{z~�=��l��'�y����==紡A;&��D X'6����h�$��F���̢g*�}ws�u�W?�jV}�`c2 ��tLf�z�lKA7�D��%�F�j@8�`.YBTT��T	|�f�� ��\Ԥ�]���<�r
<2�"���"C�W�&��y5���>�'�8fDt�Jc詄�T�{9W��}V����M��:��E�K��D�䚗�6�����x�x�Z�4��������Y�Ve����#+���ð���n�W��Տ/]��i��M�ɴ��[T�s����G��8��r����y�9n�	��6~��鹓T'_x�G���_��eR�_y�>�JC�G$�N1�
G���R'�DdL̋F���i��[7�6�k�g,}�&w�2ή��5�R�h,�����ệYܧ�*�/e��a�5�D����S���Ut]��_"h9[�]x�C���7���[	����HZ�ܢ	ki
cۙ5���<ۉH��V�H�`�l��ƥ��8�9�6k�:�@�Nl��~�
}D$���@�A�a�e��H����0�N����F�B�sͧ�>Ϙ�����Z�”7��X~L���Z@ߍj�al9�W,$km�h�xd$�GH��u>p��& �\�y�f�}��ۗV���W�{]!#��?b����x�	�LvruX���=�
ߎ��ݨ�+Q�k|��D��Kڴ:x^##�Pk8�a��6�p�(D&W�Ϫ��~�姜;,��s���\2����w���zH���`,v����v;��i`�S�Ŧ#������E{LDzE���b��ᡯ떣f?S�wҦ�=��W�H���jU�8 ��_?�9X�q��e����Z�տ��Uo݀>�6(X?��.$X��������H��fcTL ê:W�fdATXå��OW��+���V7
���Foh)&m*pA�{a�2����M[�Zv��"�B+4F
-)�z���O�]Xf�e��ggw~�眙a�a/�'y���{��9�g����Ρt!X#����:�S�u"مwi�ži��d�ɓV[7c�I���Y�1�S����)��mY�p�@:�2-(�/\��`V�t��pN��gE������&5�K
�#K�@F�IG��L�0�M��f!�Cg���8�*Å���&�9!�8͙�&�U�����Lu��XG���5+�f K�G���B��΁{q�i���fY�TT�gZ��:�`��Gs�".�~+FUo�Ԭ��w2Ǜ�Z�#WP��sE�M�kN@����C����q��oNdQw;b|K�	(��#���Hz�.����g	)����I��ك�l�����Y���2��!,$�(!�����i��K/�-DǛ4��&Bf5V��Nu�2�F�����������*�ze�=���gh�&B�­W�RTu��Z�u�~S�nŕ�F�O`��XoY�Q��7�5�z�s��C:h;$��:��-�e�V���/�@�D�V�J�s��J�3��}���r0��"�Ǎ�'��8|?k����~�����Ų��b}�ߖ�=��M�f*~���%T���m��!=�g�<����V��Z
+;۽�ҵ��B8�$l��lY!<;�M�h���{A��"VΗQX�\��̜J�P�WN�E/��4���k�����"
s�!��߶ifZ{%��]���7�.3���8rN͖�|��������'����?��|6�^�3lgy�w30sy4�޾�
��,L*���`�ۅ�N��~�_ݖ�E�[���s���p��em�NNK։�.�f���X#>����AJp;�.fcTL	 k��HfdAT
Xå��OA�+PQ���141Q/z��E��oFƓ�h�Q�Bi�QA|��w0*P/>˻�v�~��m��lٙe�8�簿��>ۙ�L��Ǯ�hA `�О
ٓ��l���HA��(�F�8<ÿ�pQgj�N�AǰUd5m_���UY&*�A��w�K"~yӒ��b:��7���gC(�.2��2�'>g,�"s�x1��2O� dvw����ⷘ$�M&P>F�$jHM��a��oPR��k�f�Ȑ�g���\��6?d��@�A?�"�������}��Q��a��}�h>�H�H<SBj�lK!���L�Z��p�%��C��������z�'�;�Uăj�H2C�ܒϏ�!�1Qq��sBRh�#�g6�D�������<�W;������
J����p�	B�,�O���{�d�����Gr����� S��Fe���x�i��^��!���%���y
m�XM��Oל�s�cG%�N�:��޼B��� �Nh��
���
��m�6�L�����(��PԸ�<�����щх8B��1���ɝ��>�Y
ܷ@��v';�xf�)a�:�Ew�PRxόcm�Y��j���n�A�g�-w�fW����zP;�e"����,ݚ���.D��k�'�P�h��y��i� �ΝJ�+�#�m8�_b"ݸ�<��n�A�A`MO�2h�n��Q�"Oa�M����Ђ��5ዦ�5�E?�G�!�A�
��pѵ(y4�_�/s��?x�՛�Ok�<]����:3μp�Պm�g�ZY�<�,��L�eZ `��{(��T%�N�:��^���!��LfcTL���gQfdATXå��OQ�W��,
��!��(�`�q�7���`4�ƨq!��qeq�

�J���r��P��v��t�N;_ߛ2�0h|�K>�y�{��{�f��1����JG��5��]��E��u±��I��]}Q�c�pW�.�@��q�6d����ETEWe�E��a�4��1� �/%��qS�4|	Q�6��P�)�")X�-'@���犉�&�1���S<�y
��#�]a��
��$ҏ'P�A��Zҧ��0���BMQG�ȝk[����̣�3�"�.z�LyOsd��#�W �_Q!�O_��U5̢m����$�
�c�LH���'�H��y�)k$s�Ca�u�'>P���I�N�Q��W~�NL2��̒��@�c�yA91�&�
xmN(�3ci|_PZ>�We��u�(;�^,DR� ̋h���(>�%Lg�Mߢ�k(̢�Gn��yP����^�d����a1)⍕GM�<E�mpT8�J�}'r�$
���{�R�58t�MG�zɹ�6B�nk����oi��o��m��8�]T?5�L.���Nd}�&=I�B��0��nZ��p�K9��xzŁ�x�6�.�n���x�D�-�t]cr)�����E���AKa�GMN�xQ7�SD�O�UYvѕY�9���"�Ɵ��c�<�h��Y�7X�Le�!�n�Z�(5,("���y
���d���2�C�L��T?��ݥ"U��,�Xg�̞;V�K/E4���A���L��<��r��.�0
5w�+�eƛD���|�D|�%�ͳ��OAK�ނc��ٰ��Է��,��UYfцsS��.:���� `��h-��7L���`0fcTL
�+��EfdATXå��OQ�W��)%���D�����oFƓ�h\�Q�BPi�QQp�"�!@��Dp�� V(�b;���t�Nۯ�M�a:�ć/���{���{���GIo������b���=a��5±�I���}�a�p�]W�.|���c�c�.*seoè��d���]<d���Û���X�����§��S؅&R��N�zĈ�Mc�HGE�NP�)��� d���Q���̷�$4L�Q�B�O�dN��an}��m���'ю�U�E@iW0+�.|�Lś �I��1�$�?�ψ���[D���Y������3Y\$�s\Z��#�x2
+��2��P�f���؅O��J������OyJ�C
�̠5�էFB�똅E-PN���&="z-qExv4$]������rO�r�.��"p�P���+sT�BO&��3���T��h�����.���X��>�z�����h��"�H�M@M@�S�˻�P��ٍ�����e���
v��mF
�:���b'T�6�/@�!�#�1��̑��0/%p��,���m�e��Ý.�xb���^��6�.�a����Vx����6�w�ɥH�}�A�gn�>-%�63-�v��K�^Eh��ɲ��A��7n��V��~h���
W� S�`C"�yc�n��8Pn��
c~��va�,d�vp���ѹrÂ2�D��<�Y���2��ِ^�K�E�h����>C^�Mu+y
����4����6�I����|��n�C�g^���=+���p�Ŏ��3�܎�s�0�r���u�9ֈ�]x�W5�I#vZ����yM��fcTL
}��NfdATXõ��Oa��d�\؅N���D����
�=OF�q�Q�Bi��dQi�C��Q��]\
��B;����M�8p�_��_�g�}��2�&.E`a+�?W�P����%�3���n0� V�x�l a�hՓ~�z���<!����0qBM9D�DhMV���Ϗ�31�/s��J�b{$�y_�h�?���)����
�Y�!~[8�OY9t�� �S�{}Ȱ�����y�j:���Zfc�Hj�l���^�)��ܙt%I�5���V�|�����MV��p��ǃ�a�3뇃Ü'=����w;��-^��odYdA.@�톖H8���Fy�Gs�D
r1J��@��j&�����a���_��M;g#P�rwM�����.�6�Gpn2�S��z6�LmFa���)g��M����QA|i*��v'��z�v�Y���5
�[�1'46L.F��C'�u�qf�	i���ۉ�@B�c)�M���{�H˓���GE�&�bN�ڄVy#�U���@�/|fGY�Un�ᄢ���:g��Sxk��v����09��߀c=V>4�ǵ� ��
i��m]-v5�6:oav�]���:#Ni��8��7.A���]�-xb����y{b���%��R�@����qk��O���=�#mfT/b�'�y�|����P��
�f�VZ;�ZjMT,�Sd��_�#Á�F�Vw(���^,y������*���<E���b��<���G�;y%����4/_|uR�8����v�n��އ(W�de��]�g	��E���\�B@���g��h2˭�-���Ʈ�Z�0fcTL �h
��fdATXõ��OQ�+d���!����`�A�7�cbb4\bԸ�"m  �.4(j�0(���#P�ʔ���N�i����2L{��K>�y��:�M[�[�Q�#�H+1�3J��n5G�Q�&A�4�f��=
.���D,O*3o�7�,�OZ�_��aG�;��yO���)6���2W�"��>qQ��7�*	O�D��R���+���~�br�Pc�8\�|<.ġ�3\~3�,����x	��>�L��Ь�;c�Eo	��4�Lu��(�p�kPn@?F�+?:~Gp����y��kV�Sb�AAS��̆G�%�^ܧO�qZ�x�=��<��q
j{�vN� ��r![���(��pB�e6<�(�?�aR�+7�>'aԓ������T5\�e�j��r0Fyoh�b��
���n��ˍ��D%]#a���La*4SSeF,�����˽h�Z�%3v?�E��|\o�+M��6���KV��'g���;��zvCʼ1'���_��r.LB��т��Y}�ĩ~�r1�g�la�Sv`@�3�N	���Jͱ!�|~�K��l/��x�:.���=w �����m��֜lS����"	��7i)��-���R�p�g'0���X29�`��l3L+��@�g
n:%�s�z�Ɩ�N�n%��
"M�%��Wޔ<�e���yu]�r�vz�ln�~J�ka
��̺6;O���Á���m\֩q��zÂĿG�c��Ŕg���D�c<��u������j���&��yR��3���^	�����g�k��0�r����"�I��te��EٌXsb�'X&[�
�"6�%91�j0^��z��k���*AR���xi�琫Ĥ�Ӥ��V��@%o�
�fcTL Ӕf��fdATXõ��oG�+$�b�J��
8.��$�
�C� �"Jj��D@c�
��	����5���DHBl'�:v�X��^{���؞���D��9���;;;#-cKlu=ᏉJ��9@��D' ��>T[sMm"�s�D$B�ʊ�m�V�,�i]{'�)�kZYu��,�b��H
[yz
Ka�-�t?��	|�8>��:��|�mS�9=�8g�)��y���u���EK�,˪�'`��݋GZ2+nb��4���Q���z5��M
'u�dD�/a`$�E i��1-[��X�/.����p�j�Dn�떋�������4o�(MbpZ�őTQ�c�?9aV���]�F�	��n�{�
xDi>΅u754��p��[��hyA��Q�9[�V��@���.�O��C?�>�	�Q˲�3.���LoZڲ����z�Ey��q�}uׇh*����y��5WBҖ�a8��ڗ3:ޅr�!����:S
[~|
']Hgr�a_
�^��&ۤ���Y�<��rP�t1L����I��{*�$��I�j����&��_�ul���(�խȇ����}������)ϭ�# �|�Koh�,ڼm���y��}�/"��UC��_]0r�`,��UL�fw�(��c�y���/|�D�!��;cIY�`D�o�����BsoHֱ�oGavf0T����h��5�ʝDi������ۏ�HF�i)��8�"�g�)���x�5�82�R�V'�^W��ˍ�ߍ���'Ʊ��*����f��V��a9���&�h�����Ey�-;<�,�g�Y�B0K������e߼�J<�浫~��<��e�t��H$��a��f��p�|�&F�����O�>�%*Y�$��� =i8F�fcTL �4�3fdATXå��Og��""be
�*	M�x�	����`"�5rhz�Ʀ����M+h-��*��� 5j��%E�^�=`�M؅e�ew�a�������.:����y~��G*lqW�LX%1�J�Q���YrR��$�K+�M�LX#���KQBa�����S�?�;�_�%�:D�:�$j4ڟ���S.�R�2��ӹ��[�S���{��	���'`��}}�1�	�C������Z+H/8�^��B*�Ndl<�YMnj��"WĒM�W^r�Z�mї����K����l��
�u�ݫ�(jp���3F_�>k?��!�`��/�M�R��������i����sF-2js=�-m�=��Ш^/�?>��nyV���hy䇵~[w��d�*�9U҆/ǐVzʉ�Ň�Q��)�l�4h~臹^�u�e��҆cϑ��]6;��-�v6OcgD�z�v3���v�}1�u?�����wtio||�E�W~�h`�Ǎǖ�ۍP��bz}�K5��z¨�d�Jy����L��{�]@�9�	
���֛�MOܝT��ܢ>��0<��\<yW��IJzA�;2��f>�&co�����v4�aW�3�֜�����c�c&�a�M�O��4�d��φe��SY��~#��� >�;��h⨼B�3�9f�M��:TMn�*���T�J:��;�|���*(�_V���I˖�����i�n�c<��!OfU���CDM~�=*�Ms�^PMn�hּ'�v��}��0��T��>��cjq抃M��p�����
"=/��0��.��:!G��9_{o^���g1�]�-���Ă���H���B��qA�{C*�U���6������*�G�F�������A|�~c_�l�X�RY?N�Yjޤr���v�{1Z4*.D����U$R��$��!�]1���{���A:D=�����AZI�,�o��O^�.$�.?�"���W���;�녜��9_Kk��D�f�����Ճ-�s�w��~���t�FL�?I��&j6���&7a�\b��.�71�pfcTL �/�yfdATXå�oLg�2�X|)""Ljx3�$a�|1|�� ��D�n�/����e��̲-��s@4:��IEAf���FD����F�	-�JiKK�G��]��q���%�����׻�|�%��SNn�*��[�{�6�݄5r�^o��/��SXy�����ΐ�����l9']Ȥ�iy�2_��C�(J�`��37P?���S��!O@>$꟞�.�������(H	���!�F"�50m`�q'�ۦ���r0�l"u^v�c�<ǎ_�%Tj��c��(�”��w��~�W&�W�T�k�SZ�釱~�=��J�	t<� ��Q�w�}�$����������i�f��|ٿ�^ҝw�?���G���M/��G�?�`�7m��*�U��l�j)E'���_���޾0�=M�Z����2=��_	ٔ�-Ǟ#e�[[��<��qL;-7���-����OW�@БȦeF�?:!%��4��X@���q��z�J�0�'o%	�ȶ� �!��
,y��}�?1!�_W����rrVI��ܒ�� �J���Z\��2��n{�i�֎97a�ܖ͟
�	k���D%�"���{Q�]׳l�t��<�Ur�2�`t�QLy}�.Q+�CYw$�֘�6�Ԥ�j�M�K?4<pg\�3Jޘ���f�ɝ=J@_/ӧ��$C�	T�l���-.�1�1�0�&z'E\��2��6��Ҏ�؄�v�]�C(�����?\}q������#�צ�iXA�$c�@�M�I�)l�2?����aE�K��ұ_M���&Y��C�ޛ�
-�ȓi��~���}��+�@w�Y:֩I'Ү�6��r������D���W{01�8�����8���w����,s(���(�ԼI%j�)IWswV��ҳ�����#S+/Ț����'�%��b� IM<���!:L]�� ���v?�L���9���7�\�VPy
�^(mhɘ��2�j(bL?�(U�Rnw���m� �B_?<��!_EB
��'}2.fL�l:AC���dU�)�-Ө����`=���4��y	�;��B_/{��L����8��씛���ً�s�{��j���
�V�%�?�=͉�^�fcTL  ���fdATXå�MLwpN�C�Q��PYå�JB�z(^h)$m,�Ho
{hz�ƦMkM�6���@$Z���
�Ԩ�Ɩm{���/a��]>��e����������,,�&��μ3�wg�y^2�]��$l�|LiF���z���Iδ�uv
�H���3�H&�q
9g���g�l���7P7�wm3���B�H40���n7��2�y�~5����Ӕ�f;O9�W�6�,�̕}�X��%�]0����(DHb�*�7�z����εi��:��ն�����X�׶�*�(J��1:J7H$�F�#�
d~3��u�g���n��?�5��/�V>Ǟ_|ETl<7��Bc
PVb_�B��[�XV.����c�z�؞kޗI$Щ���E\���-?x�޺4��Z
�����}�g����E���N>G��V���^nyہ�qm�9�]�̴���+H H�Q��,cljĽ��R��\F��>�{E��a�9�<�R/Kv�s[�$LHdU
w|>�s}�U���{	u�!F%�;��~���{q����DV�h�gC0*n���Nj�Į/��m�kGq���P��C���l9	�Ⱥ�`��CN�&�Ry��>?SN�&���`�4)��+w�tv���J�xoW����
��)5�j(B�4`�\t6�i�
�$�Y/кMXn�DE����
��!�����V�y/P��
t`T�(��	�L�ڶ��E��j�3��$��'��6���?pwB���J�a	CQ�Gbo��z�<���<*ɘ�$�i���)�z����LH�0��qyHH���5�;u۶�L��b_���~|�O(^���	�����ܦ��3�jլiߺq�zM~�W�K���aPо���l>Ȣ���cT`R��y����0�_�%�̉�ƞ��{ϊ��c����GHR3_�t��Q7E�GEj�����~�@DY�ϩ%D8_��T��G ���?��%���*
�_'D�񬯺����ʳ0
�{V
��򂬹g�ݚzڌ��e�g�i��t�,j�+o}�WN���5���/����O�
�P���f���ٿ���d���[ݘ,k�	��C��qy+��S�$��
��J�yE~��nz���nX.ƶ]y�⒄�������3�IH��<��I���o�i��\�
�����N+{�|=fcTLAy��fdATX�c`�U
PfcTL����fdATX�c`;n��fcTLA%V.fdAT X�c`�vq�fcTL!���fdAT"X�c`*M�JfcTL#BW>OfdAT$X�c`�pJ�fcTL%��L5fdAT&X�c`)K�5fcTL'B��fdAT(X�c`�z+fcTL)�x��fdAT*X�c`,A��fcTL+B�}ifdAT,X�c`�|<TfcTL-�$fdAT.X�c`/G��fcTL/B��fdAT0X�c`�n�)fcTL1��k�fdAT2X�c`&U�fcTL3C%�fdAT4X�c`�h�VfcTL5  ����fdAT6Xå�oLwp^_,��!"Ljx3�$Y��b��%�A�eٻ��X�B��͙e[6A砑�P��&�Q����n/0�Oh��B)mi)�q��z\�^��K>/���=����4+K=�l�rr6H�S�����r6ɕq;�N!'�{ff�lS�?k~^�X���B[7�@�8�޳Ϡw,�`4��h`v	��^ce��&rT�t�r֕��SN�l�Q6^�΁H<����n��A3tp��u@/�;޹6��:���>�%�>���c�+�#t�D��It8m9ߌ�hO��ۧq�'��i	�
���K�Ը&�^�1�χ(�<��F���-��K�?l��0ƛ�����e	t�4��EB�'��-?xo]��~ۄ�@�C?��2�Z/]�m�t��4`����H8���6���-��o����	��eI	�4WE�@"��Mڤ�#Hx�'���/�����Z�9�8�/3~��o	�$�&M�|z�I���w	�� �$�;�1>�U-�8[IAG"���φ`T���]x�e;�V�Ս�@�sU�٭��SI�$���~|�E�$w�����3�%�i�O+�MؠIy�Z/�]M�y�/�6���{����"l�<�<]_PKQ�J���s�E�$�YO`%I���&a�Ʌ^�Fuoo�_D��z��+I�:/Pe�:B0�}Q~�=��6Vҽ����Xc/`%)U�'�V%���hx*��斕<	C�1�E�o��`�x��� 7�T��֓@II�k��.1�a3���Dߴ��Cª>!]_0��N��6YR�:���DIg��/V�Q��	����c��&����/�^�2�񚢶����aP�>���/ȥ�Z�Ǩ8E�j}W�5���J��dVĠ?�<��s��#�P7'�K���0Ij�����M1������7��@T��ϩ%D�O8�T��g(�ܡ� ��|��WS�X�5��޼���g�\�%5�5򆌹�ߚ{Cڼu��Y�s�4
�j��O�v냠�x�WD�HTK��r����6��Bi�_�	6���5+m��z���k�b"��m$\�_���x�[S��'a}O��?)��+�ZfQqӇ�~��r1>v��"�$��Ps'}�,�X�]3�f
.��wg�@�t�	4)�M��?�m5l(�]fcTL7 yEބbfdAT8Xå�_Lwp����n��^f�$K6_�%�A�e��B�=��esfٖM�9h4s(Nq�
�ƨ�ō���F�	�JiKK����zǯ��Xv�'�w�����q�����V��U�Dȓ6�� ߭�E��$��������Nx�K�k%'s�8��h�6OA�{�	D���6����x=�XMYW�4Lǩ�[�y ��9��0)�0B��U�}��/sG�h_�o&!*�vo]�EI�G���B��=�����Ct���B�>�d
,�zv;�=x�=�÷8rg�
t��+������4A�(e~9��X��N����`o_o���K��XF�_��Tqƒe�bi�(~o���n״�z/q�F[�ŋ�M�&��%�Pf��'0�����i�v�NY�Ni��x���B���+�#c0���������0�B�8��c�ѵ�s��@Ʉ,Ե��F!:5J�nO�h��eU�?�$��ųUzv��d�@%�Z�#���:��.Kx�݋����v5�co�'c�����%�@*9�l�dD"��/ۭ�퇹ZJ�3��揇k�GȓW��끲��lq�r�r���&��'mNu�>l�$�et�,I�u��]�$5�m2e�:�r��+7b��O�;qd;.{�I���d�M�®�~B�e��������bc�T���d�
}��G2nM+�waE�'T�,�b!i<-�"�b?������#��P��~����B��sq��Up~D���l}8��a�&�h�ngw�=|�w�x�&Qu3���2s�8֔��Rݦ�Ό�1��R��E���'�c2*���g��"���m�*l���k����u��y�!�z��:>�+(�d���kHMu�BW� J�Aqq�yCI}�?f��dN�KT�C�D�/��/s^Χ
���b��3������؅�	T^��\5hs{�8��G�yO}�c��q5���4�݈��`@A�X�
�h ��FX?�ڵ����=�*�n#m0{mWG��+("�87�wb��S��*1��3O����}5W�x��g�}ű���W����a��5y��$B.�6?J��<��?��l
�y�js�u��$�!��fcTL9 ��(y#fdAT:Xå��oSe��Ku���Jv#�,1Qn�f$[,�F	�Ό�h�Q#l n
Da(+�
B��.����u��֝�X��?N�z�>�շ�k�͓|��=�<�{z�guu�es;�@�NlOG]���/$�,������NPM�s	����gj�[�=W���|�R�{����QXc��Qq�� �k�t�����x�2'sZ�s�� ���.ѰB�W�����\��ׯ~�v��"��5�y$u�Ȅ����e��j{��B8|'�#�bf�_ɊX�cۯ�׈���IC�����}t#��t4��|��5�ȳ�r�U"��f�W������v��F$�u~	���f��	����K��\��8ak�R���a80,����q��]��iV��g���,���G�`x�?�@<�����XH�f}]7-�3MC�}D"�
|���T��ܟG��\T�$��5��h:�� ��T��YX���+�
`�umg�<��+b�<�?ED" �#z�Y�`���W~ˏaɓC���t� �:�^닻�L�V���5`�x����t�s����>��}lE � �����%b�_�@��oe�5���{Y�u�WmHġ���U�Ü�����Z���Od*b��"�u��J��(��تZ �S0�*"����9�>���b��D�;~䲗.Gu�Y*2�qaV�灊�n���qk/�}]�㚈�1��՚�t��|&�0��~��=]&;̶��tS�H��7�^�9��:�*���@I�}�KZ��Þ)x��lx�,c&�=���=�G�ܞ�����A��]&W�~�pnJ&S�aw^M��Ry�>*��J��֊�a쫘��ӻH��类�)u��g9�]��
�n��b���p2m�[�?�������}Q�<�1>�7|2�V�}+�{�G�s��e���Q,�W���t�?��UQL��3XsT��~�jZF�8�%
�9m���,V

~�K辝��,@�\ ��>A�٪����	u� �:�^#�H�H�/
�fcTL; ��R6�fdAT<X�OSg�+6q2Pj��0I�l��
&Y�q��[���jq��Θi�ėA�QQ��&
��v��#��)-}=}==�|�{������$�4}���i�sN����Q��
�e��T�GM��X!�C�������D�{�g+�3����|�	a�/�
�_(`���YF��z������Cfw��ɔșR�j�|�Ge��ݦ�25?����<jO���OȒ�#{��?�������^�Y����yx��GQK�3�Ȯ�-��%��a2_�	#�-]�<����cݍ�B��UG�!h<�G��a
ۯ�a�{�v?�C�3ĽX{=6d
���`O�bm�H���6w�Xs�P�3����OA���~�D,�������p���G��ވF�ג��$d΍����]����Q�E�y��u���(	����-�>|yS����|?��6����n�#����m�ƅ��P����)^���o߶A�$��VU�2-�B`��=-�?B�`Z!�Bх���۴`��{����Q��.#w�F��n�`g��t��5�t7���#k�B�:
�����'�ȅᜁ�dѼ��O�E�y��K��y��ew�����I����:�����M)ug�-M�*��;k��X�S�y����Y��B���+��L��?���M�~��LihH���.���gl���W]�ė:�������9�먗��.4J��S����a�6������O�
�SO��C�<q�S��V!��.l'2r��?L��794݌��Iu0e�̣�?�*��RV�D��{���XD��T�~3���;�%�e���2GYʃ�cS_��EK�j��eq�E��ҋ��z���cu�<�� h�]@��>�5�es���,���5t�O@�3B�3�@%\=a�]��t
�ϷA�$��2ǿ*Jp$�ҐfcTL= h[��KfdAT>Xå��OA�W�TJ8��z�&/x3x0��F�5>�"4E4�4F
�����]([Z�n�}wi���3�b�I>������tv,�ű��.#���)�0Q�(�BD&�P;������v!E�Y��f��pQ�4��{�;��H��at>���>��&ZwS���mU�@��&ӟK��a�s�u'�l��āgsȿ%�ϕv�]��9�Dk�M�La����9�~���~&�}.�rѕI9�ʋH�/�8l��y&Zsy:�nH�͠�Mb��Y��0Q�7	�<���'�s�UdM
d���~���Q�!Oa����yW@F�AD�#�iIa�qr�U�<��.��Ƚ>iɾL�R��䇒��+s��Ώ���z'��q�Ʌ
���Z�I��e�hչ1���梳�	nr�E{-���2�H@��~Y�""+��C�NR0��M�q�ÿ�pQGf����_��RX��u�=�eY&*h��S7�Ļ�����`,��?��N���1O�";),�YT��8�yb�Yߜ�'cI�.jAgk���.�~ƴ�s(�ҷa4�$q�ԌY&��"#EmA��7��2`w$��=�$�E�$��y”��y$m����O^�&�����(iR��u4�6}�M!A�%'���%a�%Gzd�}\��J��j���e`�e_II�S[��0�>&����'Y

�t;Lt�/���T�G�jw����̓R���Lx�	�d��b�3�7����EW7��Ca���^�5ͣ�3��}�>�j��1DR|��|�1Oa����lX��Ԑu��Cy���	nr�E{u�_��LJ��fcTL?�� WfdAT@Xå��O�@��� �k8"��r�&/x3z0��F�b��
���6��%`P"���8@@�[X�쳻]����~�im)Et�I>��|�ig����زl�O�*�5%fQ
���̢�[s��E��X�\��o�<NaU�����1��hKF�	}�9Kae��`�`�[$�!Se�uq��<��F��;���<�+9����eW�`�3��_����U��q�z���1�,�.�<#'�{� -.���>0�E�.MA#��C�n=	{�ϡ�6��j�v��Ihj�I��2Ѿ������y
�h�	h��!�>�$>1#Ⱥh�󌋋y
��8��
K�eҗ@U�r*�o�8�yvѹ1�)�u�����ȬW��WN���[��l})!�����Xsv�_qY�Ox�FN�OY�5g~�'��b�"���2���v�F�q#�ko����1e�{��ޗ�b�=�eYfі5�^���O*"O,��P
��z�'��y
��N
~񖗗��i�����1	�<�]��ƶN�OE��U?%Cq�
�aD�)�g�j0�6?�Hn{���͍���OH��-ɳ�
�(z´��E���CP�+"����#c���Y��5��-�Q2�L<m�qe�s	��W�A�v\X��C="�:v�?(e�!e����������$��P��xZ����c}�2ɐOƳ��.:=V��*�G���C�V�.j􂲽͇�pR�R�����%�A���_����h5fQ�=4r��(�
`�#��j_�p�4�p�_a�S�E�yV���AN���.vѝ�R���U⤵�������x�fcTLA�GQfdATBXå��OA�W��N���D����
�=OF�Q!F��6��Z4�J�4(P/��H��n�ݾ�m���˶MF'�v�����F����:G#��N�,l�8��YXzׁ��Y��Ũ28P~��8�]غ5�M.�-�'2�-K���p|ȋ�,�Y��
j��DQ���qI*_�:��g^�BM�
+>]A�-�x�dr)ҫ����37\[D>�Zm8jZ���^\z�S��/��,���Ԝz�F$����f� ?�,\y2;nې��E�;���T��~"u��.l��L��S&� �G���KJ_;��fa�%d�ܳ!+= ��4:������ͶW�:G�Z(�hΡ}<��o��ZG}2Y|�'
���G�Z)�0W�^��^p8�cGi�Y��yk5z[AV�$�k8���8�����4�c��ũ�����ɸ�<#vA��
EP;EE�����f�N��T��i����e?|iQ�g1���N���@��0��J)��GN�9q�uŲ0��޹$�y
��/��<t/y���^ܙj߄�+�3�O��an}���� ��'�o��dIbG('�.|�L������G��QA��)��JU
�p�?(Z��dr����	q�ώ�[i%7�3`K7�8�C�c>���.$N��W���i��;���'�똅=^PN���d3^�,	Exv<,���J�ror����N���"�Yt��pg*�	������(�
����2U=������.����"�,>ؒhz�:Oan~��(��Ӎ����Nv�z��F촖��E��谜fcTLCRJYXfdATDXõ��OA�D�,��J
'c$1Q.p��E��oƓ�h�Q� �F#������
J�h��ni�s���~��u�m+��8��3��|vgv'�0��t}�B!�?������'��Z���e	P	�Z��ՁA�x]�+Qjp@ۺ�8E��yJ]�����@[�0��D�K�����lPr��%��1��u�'����W�P������n��.Y~��y����s�fk�
���8�ƃ�w^Yl���2[�G�	,�P|q6��/�'�,��>�s�n ��/Efe��ؐ��I_(w{�;PnX��QF����DY�|A�p{�E�qJQӌL]+K�ɲҾr���w��y
=_�M��P0h�徲�aT
F�kM�Ef�M���YF���,6��jm�%��)�� �6���?��B�;��)��7.�j�RrVsΜ��ƾ�x�h�O�����3%F2�o>�|Fq��]�4�D>���)!g5gM9T�8�ŁN;֟7};�ϢRo��2%=AHl��P6��ķ��R��Q�:���N�>eVsނ5gLjq2[����'���ɲY1-	l�
9m�KX�5����N�a�t�!Q�"�9�Ϸ8��c�/�w�����G��F�	���]�SD�o�T\�|r$'ߧ��L��[H���AR�z�C/(�o�¤�&c({��8�멈d�Z�-86�T�Z�����!N�t���%.�O����}��rӽ��C�S/��ʎ.B˲�K�q<��#%K��}@��E �PT��wݐ(�\BM���x��'�=��"�L�-���(���w\,+��pcS���N��ۋ5'yb��������՜�'���*�fsfcTLE �3t�fdATFXõ��OQ�+DP���1����`�Q�7�cbb4�5.�AŨ(.�����J���r��Be��u�w�~��g���^�9̛���̛yy7��[0���N�� 9KJ��lUa#�%
�V@�5�m�\�م)�I]����<�4�E0��8�Qt��9QH�y
�g��Obu�'�"�S7�yZ�,�$ $�`-G&��̇�X�3=���ė�v@���#M���C	������
�g��{X�:����*;i�)?edž�nT�qH�M�Z��C<gzA�m�RB�ST�j�EQ�3Ė�1X���{^�p�_-��9d��4;Z~��rL�OFJ�&r
g�O�k���6�)=6�������6�!���Z�xQ���:<��TD�[�͡�a��{tX���\�7�u7Ʊ�<���1��g���0K
�y,`�׸�ПD����������G��6w��dW�u��U��j_+@�p�U��z�����RX��ŗ��V^t '�b�	a48�V��;���r�Ew�P�yƈ0�~�'D��!&�#�����u��������3i���	�1u0@��4�@RJ�0�F���NE�X�pD��6�P�J�n�c�;���*�-;3�5�C,��L㫰4���	�<�6�&6!ż��K��7%o�!�`�\\e����4x�O�[R-`o_D���yHZ�-��Bɛ�o������r���`�u�0ɨ�n��q�[i)��>���1(9�L(���@��V��t]�+�h:�7��^���g�.`�OP��݋ʫﻊ/಻������+K�]�v֣]����w.�8i��/�T��,�jv�1r��fb��d{%mM��WfcTLG �pXP�fdATHXõ��OY��W��J	W���z!�`��F�����F/�j4k�5F�
A��F�Uܕ���Q�ƕ�{#z���ʔ�~N��t��L۱�&@<��b�<s�$el���#����L�&�r�HD!����
U��Y� v!E����h���[ܜ@0O��������[$0�Zh��GQ��cӳf��oy�Ģ0�f��&>z]�6W�3��ި![��S�e%ͮ�Ͱ�N9-��x2.�?�6�+��'��&���sE�c:;�����Z�+��R���3d9Vf����$�8��)�&�'%�N"?�^p���Qk���P\���׵7݆<˞S��"��D�7���}A��$�\~6��C�����L����^�[=Zg^y�=����n�/�9d�V[��a]~�Y�Ay�?['q��'���5,o�y��N9�a����)	_���!*e���^	ey5�N�����tR�l��Ƙg+o�����IT�>�P^M����sPSU� ���»zE�ꖞ�T69�Jg�&����[Ƶ̯��<�V����iX
�G�݀�����SGfk��E��[z�fW���]����֛Ǵ�˼��<�J�|��РOB�-�鏨��>
������ ru'l�u��P2/�h��B~;Y#tO�t5���5�>�G�ZXL*�1��Q������D���
���\���/��~�Z\	C����ENE�6?��?}0]����tJ�sg��C��sdž
j��rW��6��6�έi�F�Ӑe+�z���Z�(�6�}������ي+����`�\�vɯ_87�<�x����"ɞS;��%��T�8t>w%{V%r� I�I�,���= =�s��fcTLI �i�)fdATJXå��Ow��&5<�a��l>>a	d3��D�l�����9�lft�F��7�(Ȍ\���mOt{�����P�PJ[Z��8z���;��V�&����~z��U���%��J�Z���'�S�!d
�1*�7�"a��2gt%y,P}��q#��q4&��r]7+��"��_h?�+$�Z��
\��j�$�1�����m����TQ�uoνBnŨ�ր)3�>�pkRƿsKZ�����4�R��y,���^%�06_
�L2���0�(�<��c&�`�Mܛ�q~D�e��З.�����}r��d��z5���(>�;�5�6�B��:�H8�9s�.g?��$�`�P(�OuB6Cyw�M�#��c*z�������$rP�I�����f�x4+c8�y�r�9x�QfZS��%��J�Z!�
]!�x���\)�?��ZR�aS��0��Ls������"a�DAϧ����'IT^w��Q=�5�=�h>��n<1��z.Mw���CA}c)��O�Ŵ��܈h1��BuO�ȳ�/��)���=�F(=;ն� &cK�QI����{�}F��<h}��FU�2��{�����2q��\�����ժ��Еw΢�Z����lf������x3s���w���#�H2�,ʚ�~[����H���@陼���x`V���^�FI�W���1������¦S�u�'��O][p�
�--^��Ɓ[A�3g4�g�V���E)�2"����)�jf]`AJ#�h��^`u�P�b͉$��ċ_�CWq܋ť���
Hx����O
���\��m�\\�|zMs�T98���]�q��nun{ۄ1wRm�T����#��l>2m]��vá1�^��%�s
ŗ��W�g����9~V�ʕiŲ��"G�T�5�(�N�/�ip-w�W�/k��'�=�ds��
��Oi�PՆ�#��n���"vu���Qmn[�8v�ym�yo-�i��53�կ�lD$���|���r����Pk�:\G~�*�Ե���7
fcTLK �SRqkfdATLXå�_LSW�>u��^f�$,���@�&���>,{��e��Ȳ-� s@4:��IAf���f�n{��c��Bi���������޶���-����s�����BM{z���"��|O�i��ܽ�"l�+m�����np ��[a�<���{]ג�(���,�	I�t��V��ޑGQȭ�)�}Ž(���X�)B�`��3�$F�Sw&E�7���D%��1���#�8�����W)@q����Q�N"�56�I��K��E�c&"a�!��E\�V(����@	���f�+4N��H$T��
��;����(�b(�B�3�٧�MY����D�4��VaGW�R}�s�#�:�W�M���6zA�@6��H9ps���T{2+bП8a1q{Dh�1��\	�|In�0%��������S&�sj�%	v�*���%��"u_�G���"�`@"K*�����2��gQ_�O��V��5w퉭��AH]�j��a����DmR%��~P|�+�g$���?�\�V@��,�v��լu�~�T�@ �,��J�}�^L��ԉ����|�8��܊x���IX�䶸��EX'��rS2���m7|x�g/�}��F��(�7����h�e�M.a�\�]�f\�Ḽ��xݽ��6ô��t�	����it"�–)��|]��<0���d:���:d9
�z�:��Qc�WV>:��E�B_+[��i!I�P���d�t@��cF}}��׹����*,$%$�2�������x��4����Z���?|���+� 7��$}J�0D�7�����{�i��ʼn{sj���i�F+�%�uk���|5�oz� �a�Z���7ZQ}2�H{�:�/~9���F��X6���.Naˤ����z�ъr(�L8NE5oP��n)��]�d���j�9��^�iU�IJ��:�c�Kq
PɎ#Hy���W�/��ֿ��/��5��1�n�^���֍�PX�X#B�;>�֙~��}�.����K����~�e�,:EÚԓt��J�gC�+m��k.lsb��J߾�1hv��>����J[��?r6Ƚ�_V�?� 7a���}�=�&���fcTLM  T9�fdATNXå�OLWpN�C�
H�u
�*	M�x�	����`"�5��A�6�5M۴���hQ����(H�ll�hۋ���v���c��3;�~gwf����&�$�{��o���9;���$��<�"g���k�IX#��ΉJo�Q���w#0��[�B0��|�N�Z[I��{�iY��6�Ƭ����鉀ۓ"��.%
pG%���I�ϣ�8�y�2��N�]�7D�(w�l+�p��Q�1�0�"�gD���+�RĤ]�@5�蘇j� J����H����o��T�!��:�S����t�D%#�~�^�5���.��8w�6*��s>%��QJe&s�F�J6D��\�C���<ƹ���xNĐ?y=���u��-b�n�J�3���䏦^������ �~�`,���EĖ$�YT��G(Yܾ� �qf��}���ܭ������ޙOL~�i%W|�Y��d�=�5����3-��5�#�O�ʜmmȬ����<"�GcZ������$�A��:�P�e:��&A�HdM��A��݃�В�pP�pn ���[O^hz�:F�����}�$�j�Ÿ�P�͡�o���l���%��Pw+}^��E�L	J^"kJ�ӳNB&�V7�������n�\�Ț�yj��\�U���f{�g� ��CA��`�
rVI��>Q�䬧UgU@��IX����'��$H��P�k�M�|�JAG���
0�S�L��D�~Yr��	+Ie,˜�6���(���m[�}�-���c67r�:*�E>�D0ȴc��cV����[z�y�q��H��Au��6�LHf5?����68��l�Z��1�6a�w�
i�J��M��сl38tۃ�w�Z-Ҳ�~3�O!�S�I�-*U����7�X�0;Z�a̛~�-_��'�@"]�t�z(N*U2	��X\J>���97��-�Z����eO�#�j)L� B5��}�Nm�3<�r���	��\�a�e�w���\:F#�$�&��Q���OH�c�^B��L���i���w�y^���a�#i�I;�N��S���g���|Փ��T������0�ʚ��{ۦ����Dۮ�1�iv�e�]���;	k�Z�
U���JS��-�N+��mfcTLOE��RfdATPX�c`�<�!fcTLQ��~BfdATRX�c`n�fcTLSD
��fdATTX�c`�:�^fcTLU����fdATVX�c`
U�fcTLWDV8fdATXX�c`�0��fcTLY�%=dfdATZX�c`@fcTL[D��fdAT\X�c`�6��fcTL]�y��fdAT^X�c`
#?fcTL_D�OfdAT`X�c`��%fcTLa��fdATbX�c`.X�fcTLcG�'fdATdX�c`��ZfcTLe�WUfdATfX�c`(c�tEXtSoftwareAPNG Assembler 2.7��ӈIEND�B`�PK
!<<��raa6chrome/browser/skin/classic/browser/translation-16.png�PNG


IHDR w}Y(IDATx��C�\W��Ͻϊ1�m�ֶ���m۶ݮ�*�;�ڶ'so�gMt����<����i-�~�����k�q�ħy�?]���v�n��ZYss���ԍY�t-6sΆM��W#�-���Bk����w�]kk��6�Ȍ3twwC�����q^�BYq�R��nկ%q�Z�~��9l����,��_�={�����/����_O�,�c�Xn�K����|���ܵc�w�����LC���e��4��{�
J�RxI��>}:����:N _��r`���k�c�Z���*ݸ�o�Yo`�M6���f�m�=Z����B�[f�ܰ���m��<gl��)3�oM�S�oY|���Nq������#�i��T*a?�W�
�µx��ȍ�|0�x�Z�.�0C���%��/X6��s�=��
W��w8C��I���~�B|��喛O�b��0��/� ���ym�0X���>�yb{]>��;�܍7�?�$��a�Ih:��q������[���$*	��Z�9r�������i�~Y��{�q�V��g�+*��;��r�-�y�9�������n�	�W^%�$�����~m��]��ߵ�>��#^�	�n��X�)�CP�QG%�R���ɫ�K�*^9r���\�y�h���P��_�jv�P(�4i�5�XCGG������v�6/4�ŸvY�Հ��&r�n�A]?�v�m�\.�裏<ظq��LJ�]d�I;��ꭆ}0æ�w��]�뮻n��t ���]�_�q�wk0��IEND�B`�PK
!<O��?��9chrome/browser/skin/classic/browser/[email protected]�PNG


IHDR@ ��~��IDATxŘ�\������۶�k۶����m��ڸ;��s��W5U�S'�����|�vO�׭^|�ű��@:F6�U`�`QʂF��N.���]��o�)�(',,���8�n7%%%���/:��w(ZG[(��\/��_�HJJ
cǎ�ƍlڴ	��Ixxx���	�+/�cs���tcW$�)n�k
�@�=���'::���Tf͚%���>����O�q@.���l0'p~��I�
	"���$:	5߮�k����W@rr2-Z�����k?
���a�bfo��K{����a�����04݆�h��R��9_R��2
4���z8�!�͛7c�Xhٲ%Ç��ɓ\�|�Ν;K�.]„[�p뀝������x7�G�]�-04�Ƣ��|��g�������j�ܬ„��g�}6pJ%����*/^d߾}x�^P@O �Žt�-�
�#�I����%��vga;�h:����'ܔW������X&L� '@NN�|�|t��I��իWc�朔Y�?'
��K��4���(ZF[8Q�#Ҧhg�]�3����1fm�@�?	8�7
@)%;LLgΜ�'�����JKKY�r�l�iiiddd���Ib�*�%E��`��]��G�4��;�<,��«�9�ʭr�i�X;.�n�6z&[9��˯�S�!Tbb"�O��СC@���e��ر��E~~�T�'��a(��0٘��_����6:�Yy뢛�']��;�:��@�.��>�G9�T@��4�w0���9������m�6n߾-3�$//��>f���x@�
����8�B�H����;
�fz�lcP���l�=x�����W˖-���ܹse�<�ϟ��QVG�f�PJ��8x� (��$c��E�&H�S��8�FY(�ꬼ�ԣ�+�N�t;+NT��xe�����`~�t�Rl6[�'˽�����j�*��;wj�EY
@(!�@� ��mb��5
\U���ۅq ��7Ϻؓ�
�o�į�,Y����Z�h�v�%�c�oM�X	�0P.D��wX>MG�y$~�x��q��@F&�
`=��������կ-Z�/�M���˝�74���6(��f�R7���kzFc��|xP�J`��������Z�%9����g<0r��Z�4=:�C���D�r�XE�EH�ԧO���UM)�(�ۤ�5��_s'��C�t�N:p���%��Xw�#���8�5D&ę�
R�����9s�جi�����o������e�x�U���-�pv�����0��z�9������?����@`-0
���*@)EӦM��ʒF�M�6r��Na�"yF�S��?�<�ߪ�g�0�ey����:��g���<w��@l����������+�ܪU+�����큏�f{�Y�s`��}��l�Z8I��Iw�'�i_]��ˀoQ �LNq/���=\Rƌ#�F�`9���x"��M$�a1�Cd��6�8
�
Lʁ�������K�\��[�N��������5̟ee�$�Vť|/?����J�}�k
�_]�>��1�	�͸C�2<����_��ݻw�r�?�����wJ���v]uq�։v�X!�
0�!��)0h�]`����=A^��<��2����]�v��Ե���*��YV
f>�E�莑��!��o)�Z?�/�!h�7�.<b����K@(��V��"o����*�<띫���I|g@~�ׄ��T9?��l/c^��
0�&�l�©S�HJJ�����'N�0Jk`�_6��щR�%;(qi����c�ĆY1�ԟ��'�IV=���<���o��|�^�y]oT���Ɉ3ꏖZIEND�B`�PK
!<�^����;chrome/browser/skin/classic/browser/update-badge-failed.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="10px" height="10px">
  <path d="M5,6C4.2,6,3.5,6.7,3.5,7.5S4.2,9,5,9s1.5-0.7,1.5-1.5S5.8,6,5,6z M5,5L5,5c0.6,0,1-0.4,1-1l0.2-2.8 C6.2,0.5,5.7,0,5,0S3.8,0.5,3.8,1.2L4,4C4,4.6,4.4,5,5,5z" fill="#fff"/>
</svg>
PK
!<���<AA4chrome/browser/skin/classic/browser/update-badge.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="10px" height="10px">
  <line x1="5" x2="5" y1="9" y2="2" stroke="#fff" stroke-width="1.5" stroke-linecap="round"/>
  <line x1="5" x2="2" y1="2" y2="5" stroke="#fff" stroke-width="1.5" stroke-linecap="round"/>
  <line x1="5" x2="8" y1="2" y2="5" stroke="#fff" stroke-width="1.5" stroke-linecap="round"/>
</svg>
PK
!<��YS��5chrome/browser/skin/classic/browser/warning-white.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16">
  <path fill="#fff" stroke="#000" stroke-opacity="0.3" d="M15.4,12.9 9.46,1.41 C9.12,0.756 8.59,0.381 8,0.381 7.41,0.381 6.88,0.756 6.54,1.41 L0.642,12.9 c-0.331,0.6 -0.348,1.3 -0.05,1.9 0.299,0.5 0.854,0.8 1.534,0.8 H13.9 c0.6,0 1.2,-0.3 1.5,-0.8 0.3,-0.6 0.3,-1.3 0,-1.9z M8.83,5.07 8.65,10.5 H7.34 L7.15,5.07 H8.83z M8,13.7 c-0.55,0 -0.99,-0.5 -0.99,-1 0,-0.6 0.44,-1 0.99,-1 0.56,0 0.99,0.4 0.99,1 0,0.5 -0.43,1 -0.99,1z"/>
</svg>
PK
!<蹔���/chrome/browser/skin/classic/browser/warning.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="#ffbf00" d="M14.8,12.5L9.3,1.9C9,1.3,8.5,1,8,1C7.5,1,7,1.3,6.7,1.9L1.2,12.5c-0.3,0.6-0.3,1.2,0,1.7C1.5,14.7,2,15,2.6,15h10.8 c0.6,0,1.1-0.3,1.4-0.8C15.1,13.7,15.1,13.1,14.8,12.5z"/>
  <path fill="#fff" d="M8,11c-0.8,0-1.5,0.7-1.5,1.5C6.5,13.3,7.2,14,8,14 c0.8,0,1.5-0.7,1.5-1.5C9.5,11.7,8.8,11,8,11z M8,10L8,10C8.6,10,9,9.6,9,9l0.2-4.2c0-0.7-0.5-1.2-1.2-1.2S6.8,4.1,6.8,4.8L7,9 C7,9.6,7.4,10,8,10z"/>
</svg>
PK
!<$)��.chrome/browser/skin/classic/browser/webIDE.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M9.884 13.209l-2.121-2.122 6.176-6.148L16.06 7.06zM11.46 5c.02.1.019.217.037.32l-.875.9A10.929 10.929 0 0 0 10.456 5H8v2h1.856L8.88 8H8v.9L7 9.925V8H4.322a12.382 12.382 0 0 0 .222 2h2.383l-.977 1H4.808a7.92 7.92 0 0 0 .342.887l-.837 2.379A7.486 7.486 0 1 1 13.6 3.164L11.809 5zm-6.832 7.966A8.619 8.619 0 0 1 3.79 11H2.362a6.111 6.111 0 0 0 2.266 1.966zM1.813 10H3.54a12.64 12.64 0 0 1-.23-2H1.332a6.184 6.184 0 0 0 .481 2zm0-5a6.184 6.184 0 0 0-.481 2H3.31a12.64 12.64 0 0 1 .23-2zm.549-1H3.79a8.619 8.619 0 0 1 .838-1.966A6.111 6.111 0 0 0 2.362 4zM7 1.332a6.216 6.216 0 0 0-.669.13A5.269 5.269 0 0 0 4.808 4H7zM7 5H4.544a12.382 12.382 0 0 0-.222 2H7zm1.669-3.538A6.209 6.209 0 0 0 8 1.332V4h2.192a5.268 5.268 0 0 0-1.523-2.538zm1.7.573A8.619 8.619 0 0 1 11.21 4h1.428a6.111 6.111 0 0 0-2.266-1.966zM9.034 14.148L5 16l1.863-4.024zm1.8.039l3.239-3.14a7.483 7.483 0 0 1-3.241 3.14z" fill-rule="evenodd"/>
</svg>
PK
!<Os���8chrome/browser/skin/classic/browser/webRTC-indicator.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

window {
  border: 1px solid #ff9500;
}

#audioVideoButton,
#screenShareButton,
#firefoxButton {
  height: 29px;
  margin: 0;
  -moz-appearance: none;
  border-style: none;
}

#audioVideoButton,
#screenShareButton {
  -moz-context-properties: fill;
  fill: white;
}

#firefoxButton {
  background-image: url("chrome://branding/content/icon48.png");
  background-repeat: no-repeat;
  background-size: 22px;
  background-position: center center;
  min-width: 29px;
  background-color: white;
}

#firefoxButton:hover {
  background-color: #f2f2f2;
}

#screenShareButton {
  background-image: url("chrome://browser/skin/notification-icons/screen.svg");
  background-position: center center;
  background-repeat: no-repeat;
  background-size: 16px;
  min-width: 27px;
  display: none;
}

window[sharingscreen] > #screenShareButton {
  display: -moz-box;
}

#audioVideoButton {
  display: none;
  background-repeat: no-repeat;
}

/* When screen sharing, need to pull in the separator: */
window[sharingscreen] > #audioVideoButton {
  margin-right: -1px;
}

/* Single icon button: */
window[sharingvideo] > #audioVideoButton,
window[sharingaudio] > #audioVideoButton {
  display: -moz-box;
  background-position: center center;
  background-size: 16px;
  min-width: 26px;
}

window[sharingvideo] > #audioVideoButton {
  background-image: url("chrome://browser/skin/notification-icons/camera.svg");
}

window[sharingaudio] > #audioVideoButton {
  background-image: url("chrome://browser/skin/notification-icons/microphone.svg");
}

/* Multi-icon button: */
window[sharingaudio][sharingvideo] > #audioVideoButton {
  background-image: url("chrome://browser/skin/notification-icons/camera.svg"),
                    url("chrome://browser/skin/notification-icons/microphone.svg");
  background-position: 6px center, 26px center;
  background-size: 16px, 16px;
  min-width: 46px;
}

/* Hover styles */
#audioVideoButton,
#screenShareButton {
  background-color: #ffaa33;
}

#audioVideoButton:hover,
#screenShareButton:hover {
  background-color: #ff9500;
}

/* Don't show the dropmarker for the type="menu" case */
#audioVideoButton > .box-inherit > .button-menu-dropmarker,
#screenShareButton > .box-inherit > .button-menu-dropmarker {
  display: none;
}

/* Separator in case of screen sharing + video/audio sharing */
#shareSeparator {
  width: 1px;
  margin: 4px -1px 4px 0;
  background-color: #FFCA80;
  /* Separator needs to show above either button when they're hovered: */
  position: relative;
  z-index: 1;
  display: none;
}

window[sharingscreen][sharingvideo] > #shareSeparator,
window[sharingscreen][sharingaudio] > #shareSeparator {
  display: -moz-box;
}

:-moz-any(#audioVideoButton, #screenShareButton,
          #firefoxButton):-moz-focusring {
  outline: none;
}
PK
!<Y�}��4chrome/browser/skin/classic/browser/welcome-back.svg<?xml version="1.0" encoding="utf-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 xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60">
  <defs>
    <linearGradient id="gradient" gradientUnits="userSpaceOnUse" x1="0" y1="30" x2="60" y2="30">
      <stop offset="0" style="stop-color: #fd6b0c"/>
      <stop offset="1" style="stop-color: #e65206"/>
    </linearGradient>
  </defs>
  <path fill="url(#gradient)" d="M45.844,41.272c0.018-0.08,0.039-0.156,0.055-0.239c0.097-0.35,0.164-0.735,0.215-1.136 c0.375-0.835,0.747-2.172,0.5-3.936c-0.021-0.326-0.073-0.669-0.14-1.018c4.957-3.957,24.499-20.957,5.137-29.039 c0,0,4.23,6.9-2.898,13.92c-4.332,4.266-5.37,8.436-5.058,11.538c0,0,0.036,0.21,0.096,0.564c-1.62-2.178-5.652-4.53-14.256-2.022 c-8.736,2.544-7.476,4.518-9.234,3.606c0,0-0.93-2.166-3.564-4.452c0,0,0.726-4.416-0.42-5.472 c-1.146-1.062-2.628,2.244-5.346,3.432c-2.712,1.188-6.234,2.928-6.57,6.024l-3.39,2.328c0,0-1.446,0.684-0.81,1.32 c0.636,0.636,1.698,1.44,3.012,1.314c1.314-0.126,2.928-0.51,4.158-0.168c1.23,0.336,2.202,2.67,4.872,6.102 c0,0,2.142,4.542,6.936,5.412c0.036,0.024,0.072,0.054,0.108,0.078c1.404,1.026,4.584,3.336,5.148,3.834 c0.744,0.636,7.422,1.158,9.486,0.474c0,0-0.6-3.408-5.04-1.944c0,0-2.082,0.078-4.59-2.892c0.228-0.072,0.456-0.156,0.69-0.252 c1.056-0.402,2.184-0.966,3.39-1.728c0,0,1.542-0.774,3.846-1.356c0,0,2.497-0.555,4.376,0.455c2.542,1.829,6.483,2.442,12.58-0.566 c0,0,5.357,5.102,7.575,8.644c0,0,4.916-1.89-5.065-11.76C51.643,42.336,47.455,42.801,45.844,41.272z"/>
</svg>
PK
!<~���nn,chrome/browser/skin/classic/browser/wifi.svg<?xml version="1.0" encoding="utf-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 version="1.1"
     xmlns="http://www.w3.org/2000/svg"
     width="64"
     height="64"
     viewBox="0 0 64 64">

  <style>
    .gray {
      fill: #797c80;
    }
  </style>

  <defs>
    <clipPath id="clip-path">
      <polygon points="32 52.35 78.88 6.06 -14.88 6.06 32 52.35"/>
    </clipPath>
  </defs>

  <circle class="gray" cx="32" cy="52" r="6"/>

  <g clip-path="url('#clip-path')">
    <path class="gray" d="M71.63,52A39.63,39.63,0,1,1,32,12.38,39.63,39.63,0,0,1,71.63,52ZM32,7.63A44.38,44.38,0,1,0,76.38,52,44.38,44.38,0,0,0,32,7.63Z"/>
    <path class="gray" d="M47.75,52A15.75,15.75,0,1,1,32,36.25,15.75,15.75,0,0,1,47.75,52ZM32,31.65A20.35,20.35,0,1,0,52.35,52,20.35,20.35,0,0,0,32,31.65Z"/>
    <path class="gray" d="M59.58,52A27.58,27.58,0,1,1,32,24.42,27.58,27.58,0,0,1,59.58,52ZM32,19.38A32.63,32.63,0,1,0,64.63,52,32.63,32.63,0,0,0,32,19.38Z"/>
  </g>
</svg>
PK
!<,�E�.chrome/browser/skin/classic/browser/window.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M13 1H3a3 3 0 0 0-3 3v8a3 3 0 0 0 3 3h11a2 2 0 0 0 2-2V4a3 3 0 0 0-3-3zm1 11a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V6h12zm0-7H2V4a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1z"/>
</svg>
PK
!<�޶��/chrome/browser/skin/classic/browser/zoom-in.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
  <path d="M14 7H9V2a1 1 0 0 0-2 0v5H2a1 1 0 0 0 0 2h5v5a1 1 0 0 0 2 0V9h5a1 1 0 0 0 0-2z"/>
</svg>

PK
!<�� ��0chrome/browser/skin/classic/browser/zoom-out.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
  <rect x="2" y="7" width="12" height="2" rx="1"/>
</svg>
PK
!<�U��9chrome/browser/skin/classic/communicator/communicator.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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://global/skin/");

PK
!<Y�Ocw1w1chrome/pdfjs/content/PdfJs.jsm/* Copyright 2012 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.
 */

"use strict";

var EXPORTED_SYMBOLS = ["PdfJs"];

const Cm = Components.manager;

const PREF_PREFIX = "pdfjs";
const PREF_DISABLED = PREF_PREFIX + ".disabled";
const PREF_MIGRATION_VERSION = PREF_PREFIX + ".migrationVersion";
const PREF_PREVIOUS_ACTION = PREF_PREFIX + ".previousHandler.preferredAction";
const PREF_PREVIOUS_ASK = PREF_PREFIX +
                          ".previousHandler.alwaysAskBeforeHandling";
const PREF_DISABLED_PLUGIN_TYPES = "plugin.disable_full_page_plugin_for_types";
const PREF_ENABLED_CACHE_STATE = PREF_PREFIX + ".enabledCache.state";
const PREF_ENABLED_CACHE_INITIALIZED = PREF_PREFIX +
                                       ".enabledCache.initialized";
const PREF_APP_UPDATE_POSTUPDATE = "app.update.postupdate";
const TOPIC_PDFJS_HANDLER_CHANGED = "pdfjs:handlerChanged";
const TOPIC_PLUGINS_LIST_UPDATED = "plugins-list-updated";
const TOPIC_PLUGIN_INFO_UPDATED = "plugin-info-updated";
const PDF_CONTENT_TYPE = "application/pdf";

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");

var Svc = {};
XPCOMUtils.defineLazyServiceGetter(Svc, "mime",
                                   "@mozilla.org/mime;1",
                                   "nsIMIMEService");
XPCOMUtils.defineLazyServiceGetter(Svc, "pluginHost",
                                   "@mozilla.org/plugin/host;1",
                                   "nsIPluginHost");
ChromeUtils.defineModuleGetter(this, "PdfjsChromeUtils",
                               "resource://pdf.js/PdfjsChromeUtils.jsm");
ChromeUtils.defineModuleGetter(this, "PdfjsContentUtils",
                               "resource://pdf.js/PdfjsContentUtils.jsm");

function getBoolPref(aPref, aDefaultValue) {
  try {
    return Services.prefs.getBoolPref(aPref);
  } catch (ex) {
    return aDefaultValue;
  }
}

function getIntPref(aPref, aDefaultValue) {
  try {
    return Services.prefs.getIntPref(aPref);
  } catch (ex) {
    return aDefaultValue;
  }
}

function isDefaultHandler() {
  if (Services.appinfo.processType !== Services.appinfo.PROCESS_TYPE_DEFAULT) {
    throw new Error("isDefaultHandler should only get called in the parent " +
                    "process.");
  }
  return PdfjsChromeUtils.isDefaultHandlerApp();
}

function initializeDefaultPreferences() {
  /* eslint-disable semi */
  var DEFAULT_PREFERENCES =
{
  "showPreviousViewOnLoad": true,
  "defaultZoomValue": "",
  "sidebarViewOnLoad": 0,
  "cursorToolOnLoad": 0,
  "enableWebGL": false,
  "pdfBugEnabled": false,
  "disableRange": false,
  "disableStream": false,
  "disableAutoFetch": false,
  "disableFontFace": false,
  "textLayerMode": 1,
  "useOnlyCssZoom": false,
  "externalLinkTarget": 0,
  "renderer": "canvas",
  "renderInteractiveForms": false,
  "enablePrintAutoRotate": false,
  "disablePageMode": false,
  "disablePageLabels": false
}

  /* eslint-enable semi */

  var defaultBranch = Services.prefs.getDefaultBranch(PREF_PREFIX + ".");
  var defaultValue;
  for (var key in DEFAULT_PREFERENCES) {
    defaultValue = DEFAULT_PREFERENCES[key];
    switch (typeof defaultValue) {
      case "boolean":
        defaultBranch.setBoolPref(key, defaultValue);
        break;
      case "number":
        defaultBranch.setIntPref(key, defaultValue);
        break;
      case "string":
        defaultBranch.setCharPref(key, defaultValue);
        break;
    }
  }
}

// Register/unregister a constructor as a factory.
function Factory() {}
Factory.prototype = {
  register: function register(targetConstructor) {
    var proto = targetConstructor.prototype;
    this._classID = proto.classID;

    var factory = XPCOMUtils._getFactory(targetConstructor);
    this._factory = factory;

    var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
    registrar.registerFactory(proto.classID, proto.classDescription,
                              proto.contractID, factory);

    if (proto.classID2) {
      this._classID2 = proto.classID2;
      registrar.registerFactory(proto.classID2, proto.classDescription,
                                proto.contractID2, factory);
    }
  },

  unregister: function unregister() {
    var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
    registrar.unregisterFactory(this._classID, this._factory);
    if (this._classID2) {
      registrar.unregisterFactory(this._classID2, this._factory);
    }
    this._factory = null;
  },
};

var PdfJs = {
  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
  _registered: false,
  _initialized: false,

  init: function init(remote) {
    if (Services.appinfo.processType !==
        Services.appinfo.PROCESS_TYPE_DEFAULT) {
      throw new Error("PdfJs.init should only get called " +
                      "in the parent process.");
    }
    PdfjsChromeUtils.init();
    if (!remote) {
      PdfjsContentUtils.init();
    }
    this.initPrefs();
    this.updateRegistration();
  },

  initPrefs: function initPrefs() {
    if (this._initialized) {
      return;
    }
    this._initialized = true;

    if (!getBoolPref(PREF_DISABLED, true)) {
      this._migrate();
    }

    // Listen for when pdf.js is completely disabled or a different pdf handler
    // is chosen.
    Services.prefs.addObserver(PREF_DISABLED, this);
    Services.prefs.addObserver(PREF_DISABLED_PLUGIN_TYPES, this);
    Services.obs.addObserver(this, TOPIC_PDFJS_HANDLER_CHANGED);
    Services.obs.addObserver(this, TOPIC_PLUGINS_LIST_UPDATED);
    Services.obs.addObserver(this, TOPIC_PLUGIN_INFO_UPDATED);

    initializeDefaultPreferences();
  },

  updateRegistration: function updateRegistration() {
    if (this.checkEnabled()) {
      this.ensureRegistered();
    } else {
      this.ensureUnregistered();
    }
  },

  uninit: function uninit() {
    if (this._initialized) {
      Services.prefs.removeObserver(PREF_DISABLED, this);
      Services.prefs.removeObserver(PREF_DISABLED_PLUGIN_TYPES, this);
      Services.obs.removeObserver(this, TOPIC_PDFJS_HANDLER_CHANGED);
      Services.obs.removeObserver(this, TOPIC_PLUGINS_LIST_UPDATED);
      Services.obs.removeObserver(this, TOPIC_PLUGIN_INFO_UPDATED);
      this._initialized = false;
    }
    this.ensureUnregistered();
  },

  _migrate: function migrate() {
    const VERSION = 2;
    var currentVersion = getIntPref(PREF_MIGRATION_VERSION, 0);
    if (currentVersion >= VERSION) {
      return;
    }
    // Make pdf.js the default pdf viewer on the first migration.
    if (currentVersion < 1) {
      this._becomeHandler();
    }
    if (currentVersion < 2) {
      // cleaning up of unused database preference (see #3994)
      Services.prefs.clearUserPref(PREF_PREFIX + ".database");
    }
    Services.prefs.setIntPref(PREF_MIGRATION_VERSION, VERSION);
  },

  _becomeHandler: function _becomeHandler() {
    let handlerInfo = Svc.mime.getFromTypeAndExtension(PDF_CONTENT_TYPE, "pdf");
    let prefs = Services.prefs;
    if (handlerInfo.preferredAction !== Ci.nsIHandlerInfo.handleInternally &&
        handlerInfo.preferredAction !== false) {
      // Store the previous settings of preferredAction and
      // alwaysAskBeforeHandling in case we need to revert them in a hotfix that
      // would turn pdf.js off.
      prefs.setIntPref(PREF_PREVIOUS_ACTION, handlerInfo.preferredAction);
      prefs.setBoolPref(PREF_PREVIOUS_ASK, handlerInfo.alwaysAskBeforeHandling);
    }

    let handlerService = Cc["@mozilla.org/uriloader/handler-service;1"].
                         getService(Ci.nsIHandlerService);

    // Change and save mime handler settings.
    handlerInfo.alwaysAskBeforeHandling = false;
    handlerInfo.preferredAction = Ci.nsIHandlerInfo.handleInternally;
    handlerService.store(handlerInfo);

    // Also disable any plugins for pdfs.
    var stringTypes = "";
    var types = [];
    if (prefs.prefHasUserValue(PREF_DISABLED_PLUGIN_TYPES)) {
      stringTypes = prefs.getCharPref(PREF_DISABLED_PLUGIN_TYPES);
    }
    if (stringTypes !== "") {
      types = stringTypes.split(",");
    }

    if (!types.includes(PDF_CONTENT_TYPE)) {
      types.push(PDF_CONTENT_TYPE);
    }
    prefs.setCharPref(PREF_DISABLED_PLUGIN_TYPES, types.join(","));

    // Update the category manager in case the plugins are already loaded.
    let categoryManager = Cc["@mozilla.org/categorymanager;1"];
    categoryManager.getService(Ci.nsICategoryManager).
                    deleteCategoryEntry("Gecko-Content-Viewers",
                                        PDF_CONTENT_TYPE,
                                        false);
  },

  _isEnabled: function _isEnabled() {
    var disabled = getBoolPref(PREF_DISABLED, true);
    if (disabled) {
      return false;
    }

    // Check if the 'application/pdf' preview handler is configured properly.
    if (!isDefaultHandler()) {
      return false;
    }

    // Check if we have disabled plugin handling of 'application/pdf' in prefs
    if (Services.prefs.prefHasUserValue(PREF_DISABLED_PLUGIN_TYPES)) {
      let disabledPluginTypes =
        Services.prefs.getCharPref(PREF_DISABLED_PLUGIN_TYPES).split(",");
      if (disabledPluginTypes.includes(PDF_CONTENT_TYPE)) {
        return true;
      }
    }

    // Check if there is an enabled pdf plugin.
    // Note: this check is performed last because getPluginTags() triggers
    // costly plugin list initialization (bug 881575)
    let tags = Cc["@mozilla.org/plugin/host;1"].
                  getService(Ci.nsIPluginHost).
                  getPluginTags();
    let enabledPluginFound = tags.some(function(tag) {
      if (tag.disabled) {
        return false;
      }
      let mimeTypes = tag.getMimeTypes();
      return mimeTypes.some(function(mimeType) {
        return mimeType === PDF_CONTENT_TYPE;
      });
    });

    // Use pdf.js if pdf plugin is not present or disabled
    return !enabledPluginFound;
  },

  checkEnabled: function checkEnabled() {
    let isEnabled = this._isEnabled();
    // This will be updated any time we observe a dependency changing, since
    // updateRegistration internally calls enabled.
    Services.prefs.setBoolPref(PREF_ENABLED_CACHE_STATE, isEnabled);
    return isEnabled;
  },

  // nsIObserver
  observe: function observe(aSubject, aTopic, aData) {
    if (Services.appinfo.processType !==
        Services.appinfo.PROCESS_TYPE_DEFAULT) {
      throw new Error("Only the parent process should be observing PDF " +
                      "handler changes.");
    }

    this.updateRegistration();
    let jsm = "resource://pdf.js/PdfjsChromeUtils.jsm";
    let PdfjsChromeUtils = ChromeUtils.import(jsm, {}).PdfjsChromeUtils;
    PdfjsChromeUtils.notifyChildOfSettingsChange(this.enabled);
  },

  /**
   * pdf.js is only enabled if it is both selected as the pdf viewer and if the
   * global switch enabling it is true.
   * @return {boolean} Whether or not it's enabled.
   */
  get enabled() {
    if (!Services.policies.isAllowed("PDF.js")) {
      return false;
    }

    if (!Services.prefs.getBoolPref(PREF_ENABLED_CACHE_INITIALIZED, false)) {
      // If we just updated, and the cache hasn't been initialized, then we
      // can't assume a default state, and need to synchronously initialize
      // PdfJs
      if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_POSTUPDATE)) {
        this.checkEnabled();
      }

      Services.prefs.setBoolPref(PREF_ENABLED_CACHE_INITIALIZED, true);
    }
    return Services.prefs.getBoolPref(PREF_ENABLED_CACHE_STATE, true);
  },

  ensureRegistered: function ensureRegistered() {
    if (this._registered) {
      return;
    }
    this._pdfStreamConverterFactory = new Factory();
    ChromeUtils.import("resource://pdf.js/PdfStreamConverter.jsm");
    this._pdfStreamConverterFactory.register(PdfStreamConverter);

    this._registered = true;
  },

  ensureUnregistered: function ensureUnregistered() {
    if (!this._registered) {
      return;
    }
    this._pdfStreamConverterFactory.unregister();
    Cu.unload("resource://pdf.js/PdfStreamConverter.jsm");
    delete this._pdfStreamConverterFactory;

    this._registered = false;
  },
};

PK
!<�s��XX%chrome/pdfjs/content/PdfJsNetwork.jsm/* Copyright 2012 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.
 */

"use strict";

ChromeUtils.import("resource://gre/modules/Services.jsm");

var EXPORTED_SYMBOLS = ["NetworkManager"];

function log(aMsg) { // eslint-disable-line no-unused-vars
  var msg = "PdfJsNetwork.jsm: " + (aMsg.join ? aMsg.join("") : aMsg);
  Services.console.logStringMessage(msg);
}

var NetworkManager = (function NetworkManagerClosure() {

  const OK_RESPONSE = 200;
  const PARTIAL_CONTENT_RESPONSE = 206;

  function getArrayBuffer(xhr) {
    var data = xhr.response;
    if (typeof data !== "string") {
      return data;
    }
    var length = data.length;
    var array = new Uint8Array(length);
    for (var i = 0; i < length; i++) {
      array[i] = data.charCodeAt(i) & 0xFF;
    }
    return array.buffer;
  }

  class NetworkManagerClass {
    constructor(url, args) {
      this.url = url;
      args = args || {};
      this.isHttp = /^https?:/i.test(url);
      this.httpHeaders = (this.isHttp && args.httpHeaders) || {};
      this.withCredentials = args.withCredentials || false;
      this.getXhr = args.getXhr ||
        function NetworkManager_getXhr() {
          return new XMLHttpRequest();
        };

      this.currXhrId = 0;
      this.pendingRequests = Object.create(null);
      this.loadedRequests = Object.create(null);
    }

    requestRange(begin, end, listeners) {
      var args = {
        begin,
        end,
      };
      for (var prop in listeners) {
        args[prop] = listeners[prop];
      }
      return this.request(args);
    }

    requestFull(listeners) {
      return this.request(listeners);
    }

    request(args) {
      var xhr = this.getXhr();
      var xhrId = this.currXhrId++;
      var pendingRequest = this.pendingRequests[xhrId] = {
        xhr,
      };

      xhr.open("GET", this.url);
      xhr.withCredentials = this.withCredentials;
      for (var property in this.httpHeaders) {
        var value = this.httpHeaders[property];
        if (typeof value === "undefined") {
          continue;
        }
        xhr.setRequestHeader(property, value);
      }
      if (this.isHttp && "begin" in args && "end" in args) {
        var rangeStr = args.begin + "-" + (args.end - 1);
        xhr.setRequestHeader("Range", "bytes=" + rangeStr);
        pendingRequest.expectedStatus = 206;
      } else {
        pendingRequest.expectedStatus = 200;
      }

      var useMozChunkedLoading = !!args.onProgressiveData;
      if (useMozChunkedLoading) {
        xhr.responseType = "moz-chunked-arraybuffer";
        pendingRequest.onProgressiveData = args.onProgressiveData;
        pendingRequest.mozChunked = true;
      } else {
        xhr.responseType = "arraybuffer";
      }

      if (args.onError) {
        xhr.onerror = function(evt) {
          args.onError(xhr.status);
        };
      }
      xhr.onreadystatechange = this.onStateChange.bind(this, xhrId);
      xhr.onprogress = this.onProgress.bind(this, xhrId);

      pendingRequest.onHeadersReceived = args.onHeadersReceived;
      pendingRequest.onDone = args.onDone;
      pendingRequest.onError = args.onError;
      pendingRequest.onProgress = args.onProgress;

      xhr.send(null);

      return xhrId;
    }

    onProgress(xhrId, evt) {
      var pendingRequest = this.pendingRequests[xhrId];
      if (!pendingRequest) {
        // Maybe abortRequest was called...
        return;
      }

      if (pendingRequest.mozChunked) {
        var chunk = getArrayBuffer(pendingRequest.xhr);
        pendingRequest.onProgressiveData(chunk);
      }

      var onProgress = pendingRequest.onProgress;
      if (onProgress) {
        onProgress(evt);
      }
    }

    onStateChange(xhrId, evt) {
      var pendingRequest = this.pendingRequests[xhrId];
      if (!pendingRequest) {
        // Maybe abortRequest was called...
        return;
      }

      var xhr = pendingRequest.xhr;
      if (xhr.readyState >= 2 && pendingRequest.onHeadersReceived) {
        pendingRequest.onHeadersReceived();
        delete pendingRequest.onHeadersReceived;
      }

      if (xhr.readyState !== 4) {
        return;
      }

      if (!(xhrId in this.pendingRequests)) {
        // The XHR request might have been aborted in onHeadersReceived()
        // callback, in which case we should abort request
        return;
      }

      delete this.pendingRequests[xhrId];

      // success status == 0 can be on ftp, file and other protocols
      if (xhr.status === 0 && this.isHttp) {
        if (pendingRequest.onError) {
          pendingRequest.onError(xhr.status);
        }
        return;
      }
      var xhrStatus = xhr.status || OK_RESPONSE;

      // From http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.2:
      // "A server MAY ignore the Range header". This means it's possible to
      // get a 200 rather than a 206 response from a range request.
      var ok_response_on_range_request =
          xhrStatus === OK_RESPONSE &&
          pendingRequest.expectedStatus === PARTIAL_CONTENT_RESPONSE;

      if (!ok_response_on_range_request &&
          xhrStatus !== pendingRequest.expectedStatus) {
        if (pendingRequest.onError) {
          pendingRequest.onError(xhr.status);
        }
        return;
      }

      this.loadedRequests[xhrId] = true;

      var chunk = getArrayBuffer(xhr);
      if (xhrStatus === PARTIAL_CONTENT_RESPONSE) {
        var rangeHeader = xhr.getResponseHeader("Content-Range");
        var matches = /bytes (\d+)-(\d+)\/(\d+)/.exec(rangeHeader);
        var begin = parseInt(matches[1], 10);
        pendingRequest.onDone({
          begin,
          chunk,
        });
      } else if (pendingRequest.onProgressiveData) {
        pendingRequest.onDone(null);
      } else if (chunk) {
        pendingRequest.onDone({
          begin: 0,
          chunk,
        });
      } else if (pendingRequest.onError) {
        pendingRequest.onError(xhr.status);
      }
    }

    hasPendingRequests() {
      for (var xhrId in this.pendingRequests) {
        return true;
      }
      return false;
    }

    getRequestXhr(xhrId) {
      return this.pendingRequests[xhrId].xhr;
    }

    isStreamingRequest(xhrId) {
      return !!(this.pendingRequests[xhrId].onProgressiveData);
    }

    isPendingRequest(xhrId) {
      return xhrId in this.pendingRequests;
    }

    isLoadedRequest(xhrId) {
      return xhrId in this.loadedRequests;
    }

    abortAllRequests() {
      for (var xhrId in this.pendingRequests) {
        this.abortRequest(xhrId | 0);
      }
    }

    abortRequest(xhrId) {
      var xhr = this.pendingRequests[xhrId].xhr;
      delete this.pendingRequests[xhrId];
      xhr.abort();
    }
  }

  return NetworkManagerClass;
})();

PK
!<�r�		'chrome/pdfjs/content/PdfJsTelemetry.jsm/* Copyright 2013 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.
 */
/* eslint max-len: ["error", 100] */

"use strict";

var EXPORTED_SYMBOLS = ["PdfJsTelemetry"];

ChromeUtils.import("resource://gre/modules/Services.jsm");

var PdfJsTelemetry = {
  onViewerIsUsed() {
    let histogram = Services.telemetry.getHistogramById("PDF_VIEWER_USED");
    histogram.add(true);
  },
  onFallback() {
    let histogram = Services.telemetry.getHistogramById("PDF_VIEWER_FALLBACK_SHOWN");
    histogram.add(true);
  },
  onDocumentSize(size) {
    let histogram = Services.telemetry.getHistogramById("PDF_VIEWER_DOCUMENT_SIZE_KB");
    histogram.add(size / 1024);
  },
  onDocumentVersion(versionId) {
    let histogram = Services.telemetry.getHistogramById("PDF_VIEWER_DOCUMENT_VERSION");
    histogram.add(versionId);
  },
  onDocumentGenerator(generatorId) {
    let histogram = Services.telemetry.getHistogramById("PDF_VIEWER_DOCUMENT_GENERATOR");
    histogram.add(generatorId);
  },
  onEmbed(isObject) {
    let histogram = Services.telemetry.getHistogramById("PDF_VIEWER_EMBED");
    histogram.add(isObject);
  },
  onFontType(fontTypeId) {
    let histogram = Services.telemetry.getHistogramById("PDF_VIEWER_FONT_TYPES");
    histogram.add(fontTypeId);
  },
  onForm(isAcroform) {
    let histogram = Services.telemetry.getHistogramById("PDF_VIEWER_FORM");
    histogram.add(isAcroform);
  },
  onPrint() {
    let histogram = Services.telemetry.getHistogramById("PDF_VIEWER_PRINT");
    histogram.add(true);
  },
  onStreamType(streamTypeId) {
    let histogram = Services.telemetry.getHistogramById("PDF_VIEWER_STREAM_TYPES");
    histogram.add(streamTypeId);
  },
  onTimeToView(ms) {
    let histogram = Services.telemetry.getHistogramById("PDF_VIEWER_TIME_TO_VIEW_MS");
    histogram.add(ms);
  },
};
PK
!<Œ�H�H�+chrome/pdfjs/content/PdfStreamConverter.jsm/* Copyright 2012 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.
 */

"use strict";

var EXPORTED_SYMBOLS = ["PdfStreamConverter"];

const PDFJS_EVENT_ID = "pdf.js.message";
const PREF_PREFIX = "pdfjs";
const PDF_VIEWER_ORIGIN = "resource://pdf.js";
const PDF_VIEWER_WEB_PAGE = "resource://pdf.js/web/viewer.html";
const MAX_NUMBER_OF_PREFS = 50;
const MAX_STRING_PREF_LENGTH = 128;

ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");

ChromeUtils.defineModuleGetter(this, "NetUtil",
  "resource://gre/modules/NetUtil.jsm");

ChromeUtils.defineModuleGetter(this, "NetworkManager",
  "resource://pdf.js/PdfJsNetwork.jsm");

ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
  "resource://gre/modules/PrivateBrowsingUtils.jsm");

ChromeUtils.defineModuleGetter(this, "PdfJsTelemetry",
  "resource://pdf.js/PdfJsTelemetry.jsm");

ChromeUtils.defineModuleGetter(this, "PdfjsContentUtils",
  "resource://pdf.js/PdfjsContentUtils.jsm");

Cu.importGlobalProperties(["XMLHttpRequest"]);

var Svc = {};
XPCOMUtils.defineLazyServiceGetter(Svc, "mime",
                                   "@mozilla.org/mime;1",
                                   "nsIMIMEService");

function getContainingBrowser(domWindow) {
  return domWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                  .getInterface(Ci.nsIWebNavigation)
                  .QueryInterface(Ci.nsIDocShell)
                  .chromeEventHandler;
}

function getFindBar(domWindow) {
  if (PdfjsContentUtils.isRemote) {
    throw new Error("FindBar is not accessible from the content process.");
  }
  try {
    var browser = getContainingBrowser(domWindow);
    var tabbrowser = browser.getTabBrowser();
    var tab = tabbrowser.getTabForBrowser(browser);
    return tabbrowser.getFindBar(tab);
  } catch (e) {
    // Suppress errors for PDF files opened in the bookmark sidebar, see
    // https://bugzilla.mozilla.org/show_bug.cgi?id=1248959.
    return null;
  }
}

function getBoolPref(pref, def) {
  try {
    return Services.prefs.getBoolPref(pref);
  } catch (ex) {
    return def;
  }
}

function getIntPref(pref, def) {
  try {
    return Services.prefs.getIntPref(pref);
  } catch (ex) {
    return def;
  }
}

function getStringPref(pref, def) {
  try {
    return Services.prefs.getStringPref(pref);
  } catch (ex) {
    return def;
  }
}

function log(aMsg) {
  if (!getBoolPref(PREF_PREFIX + ".pdfBugEnabled", false)) {
    return;
  }
  var msg = "PdfStreamConverter.js: " + (aMsg.join ? aMsg.join("") : aMsg);
  Services.console.logStringMessage(msg);
  dump(msg + "\n");
}

function getDOMWindow(aChannel, aPrincipal) {
  var requestor = aChannel.notificationCallbacks ?
                  aChannel.notificationCallbacks :
                  aChannel.loadGroup.notificationCallbacks;
  var win = requestor.getInterface(Ci.nsIDOMWindow);
  // Ensure the window wasn't navigated to something that is not PDF.js.
  if (!win.document.nodePrincipal.equals(aPrincipal)) {
    return null;
  }
  return win;
}

function getLocalizedStrings(path) {
  var stringBundle =
    Services.strings.createBundle("chrome://pdf.js/locale/" + path);

  var map = {};
  var enumerator = stringBundle.getSimpleEnumeration();
  while (enumerator.hasMoreElements()) {
    var string = enumerator.getNext().QueryInterface(Ci.nsIPropertyElement);
    var key = string.key, property = "textContent";
    var i = key.lastIndexOf(".");
    if (i >= 0) {
      property = key.substring(i + 1);
      key = key.substring(0, i);
    }
    if (!(key in map)) {
      map[key] = {};
    }
    map[key][property] = string.value;
  }
  return map;
}
function getLocalizedString(strings, id, property) {
  property = property || "textContent";
  if (id in strings) {
    return strings[id][property];
  }
  return id;
}

// PDF data storage
function PdfDataListener(length) {
  this.length = length; // less than 0, if length is unknown
  this.buffers = [];
  this.loaded = 0;
}

PdfDataListener.prototype = {
  append: function PdfDataListener_append(chunk) {
    // In most of the cases we will pass data as we receive it, but at the
    // beginning of the loading we may accumulate some data.
    this.buffers.push(chunk);
    this.loaded += chunk.length;
    if (this.length >= 0 && this.length < this.loaded) {
      this.length = -1; // reset the length, server is giving incorrect one
    }
    this.onprogress(this.loaded, this.length >= 0 ? this.length : void 0);
  },
  readData: function PdfDataListener_readData() {
    if (this.buffers.length === 0) {
      return null;
    }
    if (this.buffers.length === 1) {
      return this.buffers.pop();
    }
    // There are multiple buffers that need to be combined into a single
    // buffer.
    let combinedLength = 0;
    for (let buffer of this.buffers) {
      combinedLength += buffer.length;
    }
    let combinedArray = new Uint8Array(combinedLength);
    let writeOffset = 0;
    while (this.buffers.length) {
      let buffer = this.buffers.shift();
      combinedArray.set(buffer, writeOffset);
      writeOffset += buffer.length;
    }
    return combinedArray;
  },
  finish: function PdfDataListener_finish() {
    this.isDataReady = true;
    if (this.oncompleteCallback) {
      this.oncompleteCallback(this.readData());
    }
  },
  error: function PdfDataListener_error(errorCode) {
    this.errorCode = errorCode;
    if (this.oncompleteCallback) {
      this.oncompleteCallback(null, errorCode);
    }
  },
  onprogress() {},
  get oncomplete() {
    return this.oncompleteCallback;
  },
  set oncomplete(value) {
    this.oncompleteCallback = value;
    if (this.isDataReady) {
      value(this.readData());
    }
    if (this.errorCode) {
      value(null, this.errorCode);
    }
  },
};

/**
 * All the privileged actions.
 */
class ChromeActions {
  constructor(domWindow, contentDispositionFilename) {
    this.domWindow = domWindow;
    this.contentDispositionFilename = contentDispositionFilename;
    this.telemetryState = {
      documentInfo: false,
      firstPageInfo: false,
      streamTypesUsed: [],
      fontTypesUsed: [],
      startAt: Date.now(),
    };
  }

  isInPrivateBrowsing() {
    return PrivateBrowsingUtils.isContentWindowPrivate(this.domWindow);
  }

  download(data, sendResponse) {
    var self = this;
    var originalUrl = data.originalUrl;
    var blobUrl = data.blobUrl || originalUrl;
    // The data may not be downloaded so we need just retry getting the pdf with
    // the original url.
    var originalUri = NetUtil.newURI(originalUrl);
    var filename = data.filename;
    if (typeof filename !== "string" ||
        (!/\.pdf$/i.test(filename) && !data.isAttachment)) {
      filename = "document.pdf";
    }
    var blobUri = NetUtil.newURI(blobUrl);
    var extHelperAppSvc =
          Cc["@mozilla.org/uriloader/external-helper-app-service;1"].
             getService(Ci.nsIExternalHelperAppService);

    var docIsPrivate = this.isInPrivateBrowsing();
    var netChannel = NetUtil.newChannel({
      uri: blobUri,
      loadUsingSystemPrincipal: true,
    });
    if ("nsIPrivateBrowsingChannel" in Ci &&
        netChannel instanceof Ci.nsIPrivateBrowsingChannel) {
      netChannel.setPrivate(docIsPrivate);
    }
    NetUtil.asyncFetch(netChannel, function(aInputStream, aResult) {
      if (!Components.isSuccessCode(aResult)) {
        if (sendResponse) {
          sendResponse(true);
        }
        return;
      }
      // Create a nsIInputStreamChannel so we can set the url on the channel
      // so the filename will be correct.
      var channel = Cc["@mozilla.org/network/input-stream-channel;1"].
                       createInstance(Ci.nsIInputStreamChannel);
      channel.QueryInterface(Ci.nsIChannel);
      try {
        // contentDisposition/contentDispositionFilename is readonly before FF18
        channel.contentDisposition = Ci.nsIChannel.DISPOSITION_ATTACHMENT;
        if (self.contentDispositionFilename && !data.isAttachment) {
          channel.contentDispositionFilename = self.contentDispositionFilename;
        } else {
          channel.contentDispositionFilename = filename;
        }
      } catch (e) {}
      channel.setURI(originalUri);
      channel.loadInfo = netChannel.loadInfo;
      channel.contentStream = aInputStream;
      if ("nsIPrivateBrowsingChannel" in Ci &&
          channel instanceof Ci.nsIPrivateBrowsingChannel) {
        channel.setPrivate(docIsPrivate);
      }

      var listener = {
        extListener: null,
        onStartRequest(aRequest, aContext) {
          var loadContext = self.domWindow
                                .QueryInterface(Ci.nsIInterfaceRequestor)
                                .getInterface(Ci.nsIWebNavigation)
                                .QueryInterface(Ci.nsILoadContext);
          this.extListener = extHelperAppSvc.doContent(
            (data.isAttachment ? "application/octet-stream" :
                                 "application/pdf"),
            aRequest, loadContext, false);
          this.extListener.onStartRequest(aRequest, aContext);
        },
        onStopRequest(aRequest, aContext, aStatusCode) {
          if (this.extListener) {
            this.extListener.onStopRequest(aRequest, aContext, aStatusCode);
          }
          // Notify the content code we're done downloading.
          if (sendResponse) {
            sendResponse(false);
          }
        },
        onDataAvailable(aRequest, aContext, aDataInputStream, aOffset, aCount) {
          this.extListener.onDataAvailable(aRequest, aContext, aDataInputStream,
                                           aOffset, aCount);
        },
      };

      channel.asyncOpen2(listener);
    });
  }

  getLocale() {
    return Services.locale.getRequestedLocale() || "en-US";
  }

  getStrings(data) {
    try {
      // Lazy initialization of localizedStrings
      if (!("localizedStrings" in this)) {
        this.localizedStrings = getLocalizedStrings("viewer.properties");
      }
      var result = this.localizedStrings[data];
      return JSON.stringify(result || null);
    } catch (e) {
      log("Unable to retrieve localized strings: " + e);
      return "null";
    }
  }

  supportsIntegratedFind() {
    // Integrated find is only supported when we're not in a frame
    if (this.domWindow.frameElement !== null) {
      return false;
    }

    // ... and we are in a child process
    if (PdfjsContentUtils.isRemote) {
      return true;
    }

    // ... or when the new find events code exists.
    var findBar = getFindBar(this.domWindow);
    return !!findBar && ("updateControlState" in findBar);
  }

  supportsDocumentFonts() {
    var prefBrowser = getIntPref("browser.display.use_document_fonts", 1);
    var prefGfx = getBoolPref("gfx.downloadable_fonts.enabled", true);
    return (!!prefBrowser && prefGfx);
  }

  supportsDocumentColors() {
    return getIntPref("browser.display.document_color_use", 0) !== 2;
  }

  supportedMouseWheelZoomModifierKeys() {
    return {
      ctrlKey: getIntPref("mousewheel.with_control.action", 3) === 3,
      metaKey: getIntPref("mousewheel.with_meta.action", 1) === 3,
    };
  }

  reportTelemetry(data) {
    var probeInfo = JSON.parse(data);
    switch (probeInfo.type) {
      case "documentInfo":
        if (!this.telemetryState.documentInfo) {
          PdfJsTelemetry.onDocumentVersion(probeInfo.version | 0);
          PdfJsTelemetry.onDocumentGenerator(probeInfo.generator | 0);
          if (probeInfo.formType) {
            PdfJsTelemetry.onForm(probeInfo.formType === "acroform");
          }
          this.telemetryState.documentInfo = true;
        }
        break;
      case "pageInfo":
        if (!this.telemetryState.firstPageInfo) {
          var duration = Date.now() - this.telemetryState.startAt;
          PdfJsTelemetry.onTimeToView(duration);
          this.telemetryState.firstPageInfo = true;
        }
        break;
      case "documentStats":
        // documentStats can be called several times for one documents.
        // if stream/font types are reported, trying not to submit the same
        // enumeration value multiple times.
        var documentStats = probeInfo.stats;
        if (!documentStats || typeof documentStats !== "object") {
          break;
        }
        var i, streamTypes = documentStats.streamTypes;
        if (Array.isArray(streamTypes)) {
          var STREAM_TYPE_ID_LIMIT = 20;
          for (i = 0; i < STREAM_TYPE_ID_LIMIT; i++) {
            if (streamTypes[i] &&
                !this.telemetryState.streamTypesUsed[i]) {
              PdfJsTelemetry.onStreamType(i);
              this.telemetryState.streamTypesUsed[i] = true;
            }
          }
        }
        var fontTypes = documentStats.fontTypes;
        if (Array.isArray(fontTypes)) {
          var FONT_TYPE_ID_LIMIT = 20;
          for (i = 0; i < FONT_TYPE_ID_LIMIT; i++) {
            if (fontTypes[i] &&
                !this.telemetryState.fontTypesUsed[i]) {
              PdfJsTelemetry.onFontType(i);
              this.telemetryState.fontTypesUsed[i] = true;
            }
          }
        }
        break;
      case "print":
        PdfJsTelemetry.onPrint();
        break;
    }
  }

  /**
   * @param {Object} args - Object with `featureId` and `url` properties.
   * @param {function} sendResponse - Callback function.
   */
  fallback(args, sendResponse) {
    var featureId = args.featureId;

    var domWindow = this.domWindow;
    var strings = getLocalizedStrings("chrome.properties");
    var message;
    if (featureId === "forms") {
      message = getLocalizedString(strings, "unsupported_feature_forms");
    } else {
      message = getLocalizedString(strings, "unsupported_feature");
    }
    PdfJsTelemetry.onFallback();
    PdfjsContentUtils.displayWarning(domWindow, message,
      getLocalizedString(strings, "open_with_different_viewer"),
      getLocalizedString(strings, "open_with_different_viewer", "accessKey"));

    let winmm = domWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsIDocShell)
                         .QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsIContentFrameMessageManager);

    winmm.addMessageListener("PDFJS:Child:fallbackDownload",
      function fallbackDownload(msg) {
        let data = msg.data;
        sendResponse(data.download);

        winmm.removeMessageListener("PDFJS:Child:fallbackDownload",
                                    fallbackDownload);
      });
  }

  updateFindControlState(data) {
    if (!this.supportsIntegratedFind()) {
      return;
    }
    // Verify what we're sending to the findbar.
    var result = data.result;
    var findPrevious = data.findPrevious;
    var findPreviousType = typeof findPrevious;
    if ((typeof result !== "number" || result < 0 || result > 3) ||
        (findPreviousType !== "undefined" && findPreviousType !== "boolean")) {
      return;
    }

    var winmm = this.domWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                              .getInterface(Ci.nsIDocShell)
                              .QueryInterface(Ci.nsIInterfaceRequestor)
                              .getInterface(Ci.nsIContentFrameMessageManager);

    winmm.sendAsyncMessage("PDFJS:Parent:updateControlState", data);
  }

  setPreferences(prefs, sendResponse) {
    var defaultBranch = Services.prefs.getDefaultBranch(PREF_PREFIX + ".");
    var numberOfPrefs = 0;
    var prefValue, prefName;
    for (var key in prefs) {
      if (++numberOfPrefs > MAX_NUMBER_OF_PREFS) {
        log("setPreferences - Exceeded the maximum number of preferences " +
            "that is allowed to be set at once.");
        break;
      } else if (!defaultBranch.getPrefType(key)) {
        continue;
      }
      prefValue = prefs[key];
      prefName = (PREF_PREFIX + "." + key);
      switch (typeof prefValue) {
        case "boolean":
          PdfjsContentUtils.setBoolPref(prefName, prefValue);
          break;
        case "number":
          PdfjsContentUtils.setIntPref(prefName, prefValue);
          break;
        case "string":
          if (prefValue.length > MAX_STRING_PREF_LENGTH) {
            log("setPreferences - Exceeded the maximum allowed length " +
                "for a string preference.");
          } else {
            PdfjsContentUtils.setStringPref(prefName, prefValue);
          }
          break;
      }
    }
    if (sendResponse) {
      sendResponse(true);
    }
  }

  getPreferences(prefs, sendResponse) {
    var defaultBranch = Services.prefs.getDefaultBranch(PREF_PREFIX + ".");
    var currentPrefs = {}, numberOfPrefs = 0;
    var prefValue, prefName;
    for (var key in prefs) {
      if (++numberOfPrefs > MAX_NUMBER_OF_PREFS) {
        log("getPreferences - Exceeded the maximum number of preferences " +
            "that is allowed to be fetched at once.");
        break;
      } else if (!defaultBranch.getPrefType(key)) {
        continue;
      }
      prefValue = prefs[key];
      prefName = (PREF_PREFIX + "." + key);
      switch (typeof prefValue) {
        case "boolean":
          currentPrefs[key] = getBoolPref(prefName, prefValue);
          break;
        case "number":
          currentPrefs[key] = getIntPref(prefName, prefValue);
          break;
        case "string":
          currentPrefs[key] = getStringPref(prefName, prefValue);
          break;
      }
    }
    let result = JSON.stringify(currentPrefs);
    if (sendResponse) {
      sendResponse(result);
    }
    return result;
  }
}

/**
 * This is for range requests.
 */
class RangedChromeActions extends ChromeActions {
  constructor(domWindow, contentDispositionFilename, originalRequest,
              rangeEnabled, streamingEnabled, dataListener) {

    super(domWindow, contentDispositionFilename);
    this.dataListener = dataListener;
    this.originalRequest = originalRequest;
    this.rangeEnabled = rangeEnabled;
    this.streamingEnabled = streamingEnabled;

    this.pdfUrl = originalRequest.URI.spec;
    this.contentLength = originalRequest.contentLength;

    // Pass all the headers from the original request through
    var httpHeaderVisitor = {
      headers: {},
      visitHeader(aHeader, aValue) {
        if (aHeader === "Range") {
          // When loading the PDF from cache, firefox seems to set the Range
          // request header to fetch only the unfetched portions of the file
          // (e.g. 'Range: bytes=1024-'). However, we want to set this header
          // manually to fetch the PDF in chunks.
          return;
        }
        this.headers[aHeader] = aValue;
      },
    };
    if (originalRequest.visitRequestHeaders) {
      originalRequest.visitRequestHeaders(httpHeaderVisitor);
    }

    var self = this;
    var xhr_onreadystatechange = function xhr_onreadystatechange() {
      if (this.readyState === 1) { // LOADING
        var netChannel = this.channel;
        if ("nsIPrivateBrowsingChannel" in Ci &&
            netChannel instanceof Ci.nsIPrivateBrowsingChannel) {
          var docIsPrivate = self.isInPrivateBrowsing();
          netChannel.setPrivate(docIsPrivate);
        }
      }
    };
    var getXhr = function getXhr() {
      var xhr = new XMLHttpRequest();
      xhr.addEventListener("readystatechange", xhr_onreadystatechange);
      return xhr;
    };

    this.networkManager = new NetworkManager(this.pdfUrl, {
      httpHeaders: httpHeaderVisitor.headers,
      getXhr,
    });

    // If we are in range request mode, this means we manually issued xhr
    // requests, which we need to abort when we leave the page
    domWindow.addEventListener("unload", function unload(e) {
      domWindow.removeEventListener(e.type, unload);
      self.abortLoading();
    });
  }

  initPassiveLoading() {
    var data;
    if (!this.streamingEnabled) {
      this.originalRequest.cancel(Cr.NS_BINDING_ABORTED);
      this.originalRequest = null;
      data = this.dataListener.readData();
      this.dataListener = null;
    } else {
      data = this.dataListener.readData();

      this.dataListener.onprogress = (loaded, total) => {
        this.domWindow.postMessage({
          pdfjsLoadAction: "progressiveRead",
          loaded,
          total,
          chunk: this.dataListener.readData(),
        }, PDF_VIEWER_ORIGIN);
      };
      this.dataListener.oncomplete = () => {
        this.dataListener = null;
      };
    }

    this.domWindow.postMessage({
      pdfjsLoadAction: "supportsRangedLoading",
      rangeEnabled: this.rangeEnabled,
      streamingEnabled: this.streamingEnabled,
      pdfUrl: this.pdfUrl,
      length: this.contentLength,
      data,
    }, PDF_VIEWER_ORIGIN);

    return true;
  }

  requestDataRange(args) {
    if (!this.rangeEnabled) {
      return;
    }

    var begin = args.begin;
    var end = args.end;
    var domWindow = this.domWindow;
    // TODO(mack): Support error handler. We're not currently not handling
    // errors from chrome code for non-range requests, so this doesn't
    // seem high-pri
    this.networkManager.requestRange(begin, end, {
      onDone: function RangedChromeActions_onDone(aArgs) {
        domWindow.postMessage({
          pdfjsLoadAction: "range",
          begin: aArgs.begin,
          chunk: aArgs.chunk,
        }, PDF_VIEWER_ORIGIN);
      },
      onProgress: function RangedChromeActions_onProgress(evt) {
        domWindow.postMessage({
          pdfjsLoadAction: "rangeProgress",
          loaded: evt.loaded,
        }, PDF_VIEWER_ORIGIN);
      },
    });
  }

  abortLoading() {
    this.networkManager.abortAllRequests();
    if (this.originalRequest) {
      this.originalRequest.cancel(Cr.NS_BINDING_ABORTED);
      this.originalRequest = null;
    }
    this.dataListener = null;
  }
}

/**
 * This is for a single network stream.
 */
class StandardChromeActions extends ChromeActions {
  constructor(domWindow, contentDispositionFilename, originalRequest,
              dataListener) {
    super(domWindow, contentDispositionFilename);
    this.originalRequest = originalRequest;
    this.dataListener = dataListener;
  }

  initPassiveLoading() {
    if (!this.dataListener) {
      return false;
    }

    this.dataListener.onprogress = (loaded, total) => {
      this.domWindow.postMessage({
        pdfjsLoadAction: "progress",
        loaded,
        total,
      }, PDF_VIEWER_ORIGIN);
    };

    this.dataListener.oncomplete = (data, errorCode) => {
      this.domWindow.postMessage({
        pdfjsLoadAction: "complete",
        data,
        errorCode,
      }, PDF_VIEWER_ORIGIN);

      this.dataListener = null;
      this.originalRequest = null;
    };

    return true;
  }

  abortLoading() {
    if (this.originalRequest) {
      this.originalRequest.cancel(Cr.NS_BINDING_ABORTED);
      this.originalRequest = null;
    }
    this.dataListener = null;
  }
}

/**
 * Event listener to trigger chrome privileged code.
 */
class RequestListener {
  constructor(actions) {
    this.actions = actions;
  }

  // Receive an event and synchronously or asynchronously responds.
  receive(event) {
    var message = event.target;
    var doc = message.ownerDocument;
    var action = event.detail.action;
    var data = event.detail.data;
    var sync = event.detail.sync;
    var actions = this.actions;
    if (!(action in actions)) {
      log("Unknown action: " + action);
      return;
    }
    var response;
    if (sync) {
      response = actions[action].call(this.actions, data);
      event.detail.response = Cu.cloneInto(response, doc.defaultView);
    } else {
      if (!event.detail.responseExpected) {
        doc.documentElement.removeChild(message);
        response = null;
      } else {
        response = function sendResponse(aResponse) {
          try {
            var listener = doc.createEvent("CustomEvent");
            let detail = Cu.cloneInto({ response: aResponse, },
                                      doc.defaultView);
            listener.initCustomEvent("pdf.js.response", true, false, detail);
            return message.dispatchEvent(listener);
          } catch (e) {
            // doc is no longer accessible because the requestor is already
            // gone. unloaded content cannot receive the response anyway.
            return false;
          }
        };
      }
      actions[action].call(this.actions, data, response);
    }
  }
}

/**
 * Forwards events from the eventElement to the contentWindow only if the
 * content window matches the currently selected browser window.
 */
class FindEventManager {
  constructor(contentWindow) {
    this.contentWindow = contentWindow;
    this.winmm = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                              .getInterface(Ci.nsIDocShell)
                              .QueryInterface(Ci.nsIInterfaceRequestor)
                              .getInterface(Ci.nsIContentFrameMessageManager);
  }

  bind() {
    var unload = (evt) => {
      this.unbind();
      this.contentWindow.removeEventListener(evt.type, unload);
    };
    this.contentWindow.addEventListener("unload", unload);

    // We cannot directly attach listeners to for the find events
    // since the FindBar is in the parent process. Instead we're
    // asking the PdfjsChromeUtils to do it for us and forward
    // all the find events to us.
    this.winmm.sendAsyncMessage("PDFJS:Parent:addEventListener");
    this.winmm.addMessageListener("PDFJS:Child:handleEvent", this);
  }

  receiveMessage(msg) {
    var detail = msg.data.detail;
    var type = msg.data.type;
    var contentWindow = this.contentWindow;

    detail = Cu.cloneInto(detail, contentWindow);
    var forward = contentWindow.document.createEvent("CustomEvent");
    forward.initCustomEvent(type, true, true, detail);
    contentWindow.dispatchEvent(forward);
  }

  unbind() {
    this.winmm.sendAsyncMessage("PDFJS:Parent:removeEventListener");
  }
}

function PdfStreamConverter() {
}

PdfStreamConverter.prototype = {

  // properties required for XPCOM registration:
  classID: Components.ID("{d0c5195d-e798-49d4-b1d3-9324328b2291}"),
  classDescription: "pdf.js Component",
  contractID: "@mozilla.org/streamconv;1?from=application/pdf&to=*/*",

  classID2: Components.ID("{d0c5195d-e798-49d4-b1d3-9324328b2292}"),
  contractID2: "@mozilla.org/streamconv;1?from=application/pdf&to=text/html",

  QueryInterface: XPCOMUtils.generateQI([
      Ci.nsISupports,
      Ci.nsIStreamConverter,
      Ci.nsIStreamListener,
      Ci.nsIRequestObserver
  ]),

  /*
   * This component works as such:
   * 1. asyncConvertData stores the listener
   * 2. onStartRequest creates a new channel, streams the viewer
   * 3. If range requests are supported:
   *      3.1. Leave the request open until the viewer is ready to switch to
   *           range requests.
   *
   *    If range rquests are not supported:
   *      3.1. Read the stream as it's loaded in onDataAvailable to send
   *           to the viewer
   *
   * The convert function just returns the stream, it's just the synchronous
   * version of asyncConvertData.
   */

  // nsIStreamConverter::convert
  convert(aFromStream, aFromType, aToType, aCtxt) {
    throw Cr.NS_ERROR_NOT_IMPLEMENTED;
  },

  // nsIStreamConverter::asyncConvertData
  asyncConvertData(aFromType, aToType, aListener, aCtxt) {
    // Store the listener passed to us
    this.listener = aListener;
  },

  // nsIStreamListener::onDataAvailable
  onDataAvailable(aRequest, aContext, aInputStream, aOffset, aCount) {
    if (!this.dataListener) {
      return;
    }

    var binaryStream = this.binaryStream;
    binaryStream.setInputStream(aInputStream);
    let chunk = new ArrayBuffer(aCount);
    binaryStream.readArrayBuffer(aCount, chunk);
    this.dataListener.append(new Uint8Array(chunk));
  },

  // nsIRequestObserver::onStartRequest
  onStartRequest(aRequest, aContext) {
    // Setup the request so we can use it below.
    var isHttpRequest = false;
    try {
      aRequest.QueryInterface(Ci.nsIHttpChannel);
      isHttpRequest = true;
    } catch (e) {}

    var rangeRequest = false;
    var streamRequest = false;
    if (isHttpRequest) {
      var contentEncoding = "identity";
      try {
        contentEncoding = aRequest.getResponseHeader("Content-Encoding");
      } catch (e) {}

      var acceptRanges;
      try {
        acceptRanges = aRequest.getResponseHeader("Accept-Ranges");
      } catch (e) {}

      var hash = aRequest.URI.ref;
      var isPDFBugEnabled = getBoolPref(PREF_PREFIX + ".pdfBugEnabled", false);
      rangeRequest = contentEncoding === "identity" &&
                     acceptRanges === "bytes" &&
                     aRequest.contentLength >= 0 &&
                     !getBoolPref(PREF_PREFIX + ".disableRange", false) &&
                     (!isPDFBugEnabled ||
                      !hash.toLowerCase().includes("disablerange=true"));
      streamRequest = contentEncoding === "identity" &&
                      aRequest.contentLength >= 0 &&
                      !getBoolPref(PREF_PREFIX + ".disableStream", false) &&
                      (!isPDFBugEnabled ||
                       !hash.toLowerCase().includes("disablestream=true"));
    }

    aRequest.QueryInterface(Ci.nsIChannel);

    aRequest.QueryInterface(Ci.nsIWritablePropertyBag);

    var contentDispositionFilename;
    try {
      contentDispositionFilename = aRequest.contentDispositionFilename;
    } catch (e) {}

    // Change the content type so we don't get stuck in a loop.
    aRequest.setProperty("contentType", aRequest.contentType);
    aRequest.contentType = "text/html";
    if (isHttpRequest) {
      // We trust PDF viewer, using no CSP
      aRequest.setResponseHeader("Content-Security-Policy", "", false);
      aRequest.setResponseHeader("Content-Security-Policy-Report-Only", "",
                                 false);
      // The viewer does not need to handle HTTP Refresh header.
      aRequest.setResponseHeader("Refresh", "", false);
    }

    PdfJsTelemetry.onViewerIsUsed();
    PdfJsTelemetry.onDocumentSize(aRequest.contentLength);

    // Creating storage for PDF data
    var contentLength = aRequest.contentLength;
    this.dataListener = new PdfDataListener(contentLength);
    this.binaryStream = Cc["@mozilla.org/binaryinputstream;1"]
                        .createInstance(Ci.nsIBinaryInputStream);

    // Create a new channel that is viewer loaded as a resource.
    var channel = NetUtil.newChannel({
      uri: PDF_VIEWER_WEB_PAGE,
      loadUsingSystemPrincipal: true,
    });

    var listener = this.listener;
    var dataListener = this.dataListener;
    // Proxy all the request observer calls, when it gets to onStopRequest
    // we can get the dom window.  We also intentionally pass on the original
    // request(aRequest) below so we don't overwrite the original channel and
    // trigger an assertion.
    var proxy = {
      onStartRequest(request, context) {
        listener.onStartRequest(aRequest, aContext);
      },
      onDataAvailable(request, context, inputStream, offset, count) {
        listener.onDataAvailable(aRequest, aContext, inputStream,
                                 offset, count);
      },
      onStopRequest(request, context, statusCode) {
        var domWindow = getDOMWindow(channel, resourcePrincipal);
        if (!Components.isSuccessCode(statusCode) || !domWindow) {
          // The request may have been aborted and the document may have been
          // replaced with something that is not PDF.js, abort attaching.
          listener.onStopRequest(aRequest, context, statusCode);
          return;
        }
        var actions;
        if (rangeRequest || streamRequest) {
          actions = new RangedChromeActions(
            domWindow, contentDispositionFilename, aRequest,
            rangeRequest, streamRequest, dataListener);
        } else {
          actions = new StandardChromeActions(
            domWindow, contentDispositionFilename, aRequest, dataListener);
        }
        var requestListener = new RequestListener(actions);
        domWindow.document.addEventListener(PDFJS_EVENT_ID, function(event) {
          requestListener.receive(event);
        }, false, true);
        if (actions.supportsIntegratedFind()) {
          var findEventManager = new FindEventManager(domWindow);
          findEventManager.bind();
        }
        listener.onStopRequest(aRequest, aContext, statusCode);

        if (domWindow.frameElement) {
          var isObjectEmbed = domWindow.frameElement.tagName !== "IFRAME" ||
            domWindow.frameElement.className === "previewPluginContentFrame";
          PdfJsTelemetry.onEmbed(isObjectEmbed);
        }
      },
    };

    // Keep the URL the same so the browser sees it as the same.
    channel.originalURI = aRequest.URI;
    channel.loadGroup = aRequest.loadGroup;
    channel.loadInfo.originAttributes = aRequest.loadInfo.originAttributes;

    // We can use the resource principal when data is fetched by the chrome,
    // e.g. useful for NoScript. Make make sure we reuse the origin attributes
    // from the request channel to keep isolation consistent.
    var uri = NetUtil.newURI(PDF_VIEWER_WEB_PAGE);
    var resourcePrincipal =
      Services.scriptSecurityManager.createCodebasePrincipal(uri,
        aRequest.loadInfo.originAttributes);
    aRequest.owner = resourcePrincipal;

    channel.asyncOpen2(proxy);
  },

  // nsIRequestObserver::onStopRequest
  onStopRequest(aRequest, aContext, aStatusCode) {
    if (!this.dataListener) {
      // Do nothing
      return;
    }

    if (Components.isSuccessCode(aStatusCode)) {
      this.dataListener.finish();
    } else {
      this.dataListener.error(aStatusCode);
    }
    delete this.dataListener;
    delete this.binaryStream;
  },
};

PK
!<�kޠ�*chrome/pdfjs/content/PdfjsContentUtils.jsm/* Copyright 2012 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.
 */

"use strict";

var EXPORTED_SYMBOLS = ["PdfjsContentUtils"];

ChromeUtils.import("resource://gre/modules/Services.jsm");

var PdfjsContentUtils = {
  _mm: null,

  /*
   * Public API
   */

  get isRemote() {
    return (Services.appinfo.processType ===
            Services.appinfo.PROCESS_TYPE_CONTENT);
  },

  init() {
    // child *process* mm, or when loaded into the parent for in-content
    // support the psuedo child process mm 'child PPMM'.
    if (!this._mm) {
      this._mm = Services.cpmm;
      this._mm.addMessageListener("PDFJS:Child:updateSettings", this);

      Services.obs.addObserver(this, "quit-application");
    }
  },

  uninit() {
    if (this._mm) {
      this._mm.removeMessageListener("PDFJS:Child:updateSettings", this);
      Services.obs.removeObserver(this, "quit-application");
    }
    this._mm = null;
  },

  /*
   * prefs utilities - the child does not have write access to prefs.
   * note, the pref names here are cross-checked against a list of
   * approved pdfjs prefs in chrome utils.
   */

  clearUserPref(aPrefName) {
    this._mm.sendSyncMessage("PDFJS:Parent:clearUserPref", {
      name: aPrefName,
    });
  },

  setIntPref(aPrefName, aPrefValue) {
    this._mm.sendSyncMessage("PDFJS:Parent:setIntPref", {
      name: aPrefName,
      value: aPrefValue,
    });
  },

  setBoolPref(aPrefName, aPrefValue) {
    this._mm.sendSyncMessage("PDFJS:Parent:setBoolPref", {
      name: aPrefName,
      value: aPrefValue,
    });
  },

  setCharPref(aPrefName, aPrefValue) {
    this._mm.sendSyncMessage("PDFJS:Parent:setCharPref", {
      name: aPrefName,
      value: aPrefValue,
    });
  },

  setStringPref(aPrefName, aPrefValue) {
    this._mm.sendSyncMessage("PDFJS:Parent:setStringPref", {
      name: aPrefName,
      value: aPrefValue,
    });
  },

  /*
   * Request the display of a notification warning in the associated window
   * when the renderer isn't sure a pdf displayed correctly.
   */
  displayWarning(aWindow, aMessage, aLabel, aAccessKey) {
    // the child's dom frame mm associated with the window.
    let winmm = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                       .getInterface(Ci.nsIDocShell)
                       .QueryInterface(Ci.nsIInterfaceRequestor)
                       .getInterface(Ci.nsIContentFrameMessageManager);
    winmm.sendAsyncMessage("PDFJS:Parent:displayWarning", {
      message: aMessage,
      label: aLabel,
      accessKey: aAccessKey,
    });
  },

  /*
   * Events
   */

  observe(aSubject, aTopic, aData) {
    if (aTopic === "quit-application") {
      this.uninit();
    }
  },

  receiveMessage(aMsg) {
    switch (aMsg.name) {
      case "PDFJS:Child:updateSettings":
        // Only react to this if we are remote.
        if (Services.appinfo.processType ===
            Services.appinfo.PROCESS_TYPE_CONTENT) {
          let jsm = "resource://pdf.js/PdfJs.jsm";
          let pdfjs = ChromeUtils.import(jsm, {}).PdfJs;
          if (aMsg.data.enabled) {
            pdfjs.ensureRegistered();
          } else {
            pdfjs.ensureUnregistered();
          }
        }
        break;
    }
  },
};

PK
!<��!chrome/pdfjs/content/build/pdf.js/**
 * @licstart The following is the entire license notice for the
 * Javascript code in this page
 *
 * Copyright 2017 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.
 *
 * @licend The above is the entire license notice for the
 * Javascript code in this page
 */

(function webpackUniversalModuleDefinition(root, factory) {
	if(typeof exports === 'object' && typeof module === 'object')
		module.exports = factory();
	else if(typeof define === 'function' && define.amd)
		define("pdfjs-dist/build/pdf", [], factory);
	else if(typeof exports === 'object')
		exports["pdfjs-dist/build/pdf"] = factory();
	else
		root["pdfjs-dist/build/pdf"] = root.pdfjsDistBuildPdf = factory();
})(typeof self !== 'undefined' ? self : this, function() {
return /******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};
/******/
/******/ 	// The require function
/******/ 	function __w_pdfjs_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, __w_pdfjs_require__);
/******/
/******/ 		// Flag the module as loaded
/******/ 		module.l = true;
/******/
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/
/******/
/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__w_pdfjs_require__.m = modules;
/******/
/******/ 	// expose the module cache
/******/ 	__w_pdfjs_require__.c = installedModules;
/******/
/******/ 	// define getter function for harmony exports
/******/ 	__w_pdfjs_require__.d = function(exports, name, getter) {
/******/ 		if(!__w_pdfjs_require__.o(exports, name)) {
/******/ 			Object.defineProperty(exports, name, {
/******/ 				configurable: false,
/******/ 				enumerable: true,
/******/ 				get: getter
/******/ 			});
/******/ 		}
/******/ 	};
/******/
/******/ 	// getDefaultExport function for compatibility with non-harmony modules
/******/ 	__w_pdfjs_require__.n = function(module) {
/******/ 		var getter = module && module.__esModule ?
/******/ 			function getDefault() { return module['default']; } :
/******/ 			function getModuleExports() { return module; };
/******/ 		__w_pdfjs_require__.d(getter, 'a', getter);
/******/ 		return getter;
/******/ 	};
/******/
/******/ 	// Object.prototype.hasOwnProperty.call
/******/ 	__w_pdfjs_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ 	// __webpack_public_path__
/******/ 	__w_pdfjs_require__.p = "";
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __w_pdfjs_require__(__w_pdfjs_require__.s = 9);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.unreachable = exports.warn = exports.utf8StringToString = exports.stringToUTF8String = exports.stringToPDFString = exports.stringToBytes = exports.string32 = exports.shadow = exports.setVerbosityLevel = exports.ReadableStream = exports.removeNullCharacters = exports.readUint32 = exports.readUint16 = exports.readInt8 = exports.log2 = exports.isEvalSupported = exports.isLittleEndian = exports.createValidAbsoluteUrl = exports.isSameOrigin = exports.isSpace = exports.isString = exports.isNum = exports.isEmptyObj = exports.isBool = exports.isArrayBuffer = exports.info = exports.getVerbosityLevel = exports.getLookupTableFactory = exports.deprecated = exports.createObjectURL = exports.createPromiseCapability = exports.createBlob = exports.bytesToString = exports.assert = exports.arraysToBytes = exports.arrayByteLength = exports.FormatError = exports.XRefParseException = exports.Util = exports.UnknownErrorException = exports.UnexpectedResponseException = exports.TextRenderingMode = exports.StreamType = exports.PasswordResponses = exports.PasswordException = exports.PageViewport = exports.NotImplementedException = exports.NativeImageDecoding = exports.MissingPDFException = exports.MissingDataException = exports.MessageHandler = exports.InvalidPDFException = exports.AbortException = exports.CMapCompressionType = exports.ImageKind = exports.FontType = exports.AnnotationType = exports.AnnotationFlag = exports.AnnotationFieldFlag = exports.AnnotationBorderStyleType = exports.UNSUPPORTED_FEATURES = exports.VerbosityLevel = exports.OPS = exports.IDENTITY_MATRIX = exports.FONT_IDENTITY_MATRIX = undefined;

__w_pdfjs_require__(10);

var _streams_polyfill = __w_pdfjs_require__(11);

var FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0];
const NativeImageDecoding = {
  NONE: 'none',
  DECODE: 'decode',
  DISPLAY: 'display'
};
var TextRenderingMode = {
  FILL: 0,
  STROKE: 1,
  FILL_STROKE: 2,
  INVISIBLE: 3,
  FILL_ADD_TO_PATH: 4,
  STROKE_ADD_TO_PATH: 5,
  FILL_STROKE_ADD_TO_PATH: 6,
  ADD_TO_PATH: 7,
  FILL_STROKE_MASK: 3,
  ADD_TO_PATH_FLAG: 4
};
var ImageKind = {
  GRAYSCALE_1BPP: 1,
  RGB_24BPP: 2,
  RGBA_32BPP: 3
};
var AnnotationType = {
  TEXT: 1,
  LINK: 2,
  FREETEXT: 3,
  LINE: 4,
  SQUARE: 5,
  CIRCLE: 6,
  POLYGON: 7,
  POLYLINE: 8,
  HIGHLIGHT: 9,
  UNDERLINE: 10,
  SQUIGGLY: 11,
  STRIKEOUT: 12,
  STAMP: 13,
  CARET: 14,
  INK: 15,
  POPUP: 16,
  FILEATTACHMENT: 17,
  SOUND: 18,
  MOVIE: 19,
  WIDGET: 20,
  SCREEN: 21,
  PRINTERMARK: 22,
  TRAPNET: 23,
  WATERMARK: 24,
  THREED: 25,
  REDACT: 26
};
var AnnotationFlag = {
  INVISIBLE: 0x01,
  HIDDEN: 0x02,
  PRINT: 0x04,
  NOZOOM: 0x08,
  NOROTATE: 0x10,
  NOVIEW: 0x20,
  READONLY: 0x40,
  LOCKED: 0x80,
  TOGGLENOVIEW: 0x100,
  LOCKEDCONTENTS: 0x200
};
var AnnotationFieldFlag = {
  READONLY: 0x0000001,
  REQUIRED: 0x0000002,
  NOEXPORT: 0x0000004,
  MULTILINE: 0x0001000,
  PASSWORD: 0x0002000,
  NOTOGGLETOOFF: 0x0004000,
  RADIO: 0x0008000,
  PUSHBUTTON: 0x0010000,
  COMBO: 0x0020000,
  EDIT: 0x0040000,
  SORT: 0x0080000,
  FILESELECT: 0x0100000,
  MULTISELECT: 0x0200000,
  DONOTSPELLCHECK: 0x0400000,
  DONOTSCROLL: 0x0800000,
  COMB: 0x1000000,
  RICHTEXT: 0x2000000,
  RADIOSINUNISON: 0x2000000,
  COMMITONSELCHANGE: 0x4000000
};
var AnnotationBorderStyleType = {
  SOLID: 1,
  DASHED: 2,
  BEVELED: 3,
  INSET: 4,
  UNDERLINE: 5
};
var StreamType = {
  UNKNOWN: 0,
  FLATE: 1,
  LZW: 2,
  DCT: 3,
  JPX: 4,
  JBIG: 5,
  A85: 6,
  AHX: 7,
  CCF: 8,
  RL: 9
};
var FontType = {
  UNKNOWN: 0,
  TYPE1: 1,
  TYPE1C: 2,
  CIDFONTTYPE0: 3,
  CIDFONTTYPE0C: 4,
  TRUETYPE: 5,
  CIDFONTTYPE2: 6,
  TYPE3: 7,
  OPENTYPE: 8,
  TYPE0: 9,
  MMTYPE1: 10
};
const VerbosityLevel = {
  ERRORS: 0,
  WARNINGS: 1,
  INFOS: 5
};
var CMapCompressionType = {
  NONE: 0,
  BINARY: 1,
  STREAM: 2
};
var OPS = {
  dependency: 1,
  setLineWidth: 2,
  setLineCap: 3,
  setLineJoin: 4,
  setMiterLimit: 5,
  setDash: 6,
  setRenderingIntent: 7,
  setFlatness: 8,
  setGState: 9,
  save: 10,
  restore: 11,
  transform: 12,
  moveTo: 13,
  lineTo: 14,
  curveTo: 15,
  curveTo2: 16,
  curveTo3: 17,
  closePath: 18,
  rectangle: 19,
  stroke: 20,
  closeStroke: 21,
  fill: 22,
  eoFill: 23,
  fillStroke: 24,
  eoFillStroke: 25,
  closeFillStroke: 26,
  closeEOFillStroke: 27,
  endPath: 28,
  clip: 29,
  eoClip: 30,
  beginText: 31,
  endText: 32,
  setCharSpacing: 33,
  setWordSpacing: 34,
  setHScale: 35,
  setLeading: 36,
  setFont: 37,
  setTextRenderingMode: 38,
  setTextRise: 39,
  moveText: 40,
  setLeadingMoveText: 41,
  setTextMatrix: 42,
  nextLine: 43,
  showText: 44,
  showSpacedText: 45,
  nextLineShowText: 46,
  nextLineSetSpacingShowText: 47,
  setCharWidth: 48,
  setCharWidthAndBounds: 49,
  setStrokeColorSpace: 50,
  setFillColorSpace: 51,
  setStrokeColor: 52,
  setStrokeColorN: 53,
  setFillColor: 54,
  setFillColorN: 55,
  setStrokeGray: 56,
  setFillGray: 57,
  setStrokeRGBColor: 58,
  setFillRGBColor: 59,
  setStrokeCMYKColor: 60,
  setFillCMYKColor: 61,
  shadingFill: 62,
  beginInlineImage: 63,
  beginImageData: 64,
  endInlineImage: 65,
  paintXObject: 66,
  markPoint: 67,
  markPointProps: 68,
  beginMarkedContent: 69,
  beginMarkedContentProps: 70,
  endMarkedContent: 71,
  beginCompat: 72,
  endCompat: 73,
  paintFormXObjectBegin: 74,
  paintFormXObjectEnd: 75,
  beginGroup: 76,
  endGroup: 77,
  beginAnnotations: 78,
  endAnnotations: 79,
  beginAnnotation: 80,
  endAnnotation: 81,
  paintJpegXObject: 82,
  paintImageMaskXObject: 83,
  paintImageMaskXObjectGroup: 84,
  paintImageXObject: 85,
  paintInlineImageXObject: 86,
  paintInlineImageXObjectGroup: 87,
  paintImageXObjectRepeat: 88,
  paintImageMaskXObjectRepeat: 89,
  paintSolidColorImageMask: 90,
  constructPath: 91
};
let verbosity = VerbosityLevel.WARNINGS;
function setVerbosityLevel(level) {
  if (Number.isInteger(level)) {
    verbosity = level;
  }
}
function getVerbosityLevel() {
  return verbosity;
}
function info(msg) {
  if (verbosity >= VerbosityLevel.INFOS) {
    console.log('Info: ' + msg);
  }
}
function warn(msg) {
  if (verbosity >= VerbosityLevel.WARNINGS) {
    console.log('Warning: ' + msg);
  }
}
function deprecated(details) {
  console.log('Deprecated API usage: ' + details);
}
function unreachable(msg) {
  throw new Error(msg);
}
function assert(cond, msg) {
  if (!cond) {
    unreachable(msg);
  }
}
var UNSUPPORTED_FEATURES = {
  unknown: 'unknown',
  forms: 'forms',
  javaScript: 'javaScript',
  smask: 'smask',
  shadingPattern: 'shadingPattern',
  font: 'font'
};
function isSameOrigin(baseUrl, otherUrl) {
  try {
    var base = new URL(baseUrl);
    if (!base.origin || base.origin === 'null') {
      return false;
    }
  } catch (e) {
    return false;
  }
  var other = new URL(otherUrl, base);
  return base.origin === other.origin;
}
function isValidProtocol(url) {
  if (!url) {
    return false;
  }
  switch (url.protocol) {
    case 'http:':
    case 'https:':
    case 'ftp:':
    case 'mailto:':
    case 'tel:':
      return true;
    default:
      return false;
  }
}
function createValidAbsoluteUrl(url, baseUrl) {
  if (!url) {
    return null;
  }
  try {
    var absoluteUrl = baseUrl ? new URL(url, baseUrl) : new URL(url);
    if (isValidProtocol(absoluteUrl)) {
      return absoluteUrl;
    }
  } catch (ex) {}
  return null;
}
function shadow(obj, prop, value) {
  Object.defineProperty(obj, prop, {
    value,
    enumerable: true,
    configurable: true,
    writable: false
  });
  return value;
}
function getLookupTableFactory(initializer) {
  var lookup;
  return function () {
    if (initializer) {
      lookup = Object.create(null);
      initializer(lookup);
      initializer = null;
    }
    return lookup;
  };
}
var PasswordResponses = {
  NEED_PASSWORD: 1,
  INCORRECT_PASSWORD: 2
};
var PasswordException = function PasswordExceptionClosure() {
  function PasswordException(msg, code) {
    this.name = 'PasswordException';
    this.message = msg;
    this.code = code;
  }
  PasswordException.prototype = new Error();
  PasswordException.constructor = PasswordException;
  return PasswordException;
}();
var UnknownErrorException = function UnknownErrorExceptionClosure() {
  function UnknownErrorException(msg, details) {
    this.name = 'UnknownErrorException';
    this.message = msg;
    this.details = details;
  }
  UnknownErrorException.prototype = new Error();
  UnknownErrorException.constructor = UnknownErrorException;
  return UnknownErrorException;
}();
var InvalidPDFException = function InvalidPDFExceptionClosure() {
  function InvalidPDFException(msg) {
    this.name = 'InvalidPDFException';
    this.message = msg;
  }
  InvalidPDFException.prototype = new Error();
  InvalidPDFException.constructor = InvalidPDFException;
  return InvalidPDFException;
}();
var MissingPDFException = function MissingPDFExceptionClosure() {
  function MissingPDFException(msg) {
    this.name = 'MissingPDFException';
    this.message = msg;
  }
  MissingPDFException.prototype = new Error();
  MissingPDFException.constructor = MissingPDFException;
  return MissingPDFException;
}();
var UnexpectedResponseException = function UnexpectedResponseExceptionClosure() {
  function UnexpectedResponseException(msg, status) {
    this.name = 'UnexpectedResponseException';
    this.message = msg;
    this.status = status;
  }
  UnexpectedResponseException.prototype = new Error();
  UnexpectedResponseException.constructor = UnexpectedResponseException;
  return UnexpectedResponseException;
}();
var NotImplementedException = function NotImplementedExceptionClosure() {
  function NotImplementedException(msg) {
    this.message = msg;
  }
  NotImplementedException.prototype = new Error();
  NotImplementedException.prototype.name = 'NotImplementedException';
  NotImplementedException.constructor = NotImplementedException;
  return NotImplementedException;
}();
var MissingDataException = function MissingDataExceptionClosure() {
  function MissingDataException(begin, end) {
    this.begin = begin;
    this.end = end;
    this.message = 'Missing data [' + begin + ', ' + end + ')';
  }
  MissingDataException.prototype = new Error();
  MissingDataException.prototype.name = 'MissingDataException';
  MissingDataException.constructor = MissingDataException;
  return MissingDataException;
}();
var XRefParseException = function XRefParseExceptionClosure() {
  function XRefParseException(msg) {
    this.message = msg;
  }
  XRefParseException.prototype = new Error();
  XRefParseException.prototype.name = 'XRefParseException';
  XRefParseException.constructor = XRefParseException;
  return XRefParseException;
}();
let FormatError = function FormatErrorClosure() {
  function FormatError(msg) {
    this.message = msg;
  }
  FormatError.prototype = new Error();
  FormatError.prototype.name = 'FormatError';
  FormatError.constructor = FormatError;
  return FormatError;
}();
let AbortException = function AbortExceptionClosure() {
  function AbortException(msg) {
    this.name = 'AbortException';
    this.message = msg;
  }
  AbortException.prototype = new Error();
  AbortException.constructor = AbortException;
  return AbortException;
}();
var NullCharactersRegExp = /\x00/g;
function removeNullCharacters(str) {
  if (typeof str !== 'string') {
    warn('The argument for removeNullCharacters must be a string.');
    return str;
  }
  return str.replace(NullCharactersRegExp, '');
}
function bytesToString(bytes) {
  assert(bytes !== null && typeof bytes === 'object' && bytes.length !== undefined, 'Invalid argument for bytesToString');
  var length = bytes.length;
  var MAX_ARGUMENT_COUNT = 8192;
  if (length < MAX_ARGUMENT_COUNT) {
    return String.fromCharCode.apply(null, bytes);
  }
  var strBuf = [];
  for (var i = 0; i < length; i += MAX_ARGUMENT_COUNT) {
    var chunkEnd = Math.min(i + MAX_ARGUMENT_COUNT, length);
    var chunk = bytes.subarray(i, chunkEnd);
    strBuf.push(String.fromCharCode.apply(null, chunk));
  }
  return strBuf.join('');
}
function stringToBytes(str) {
  assert(typeof str === 'string', 'Invalid argument for stringToBytes');
  var length = str.length;
  var bytes = new Uint8Array(length);
  for (var i = 0; i < length; ++i) {
    bytes[i] = str.charCodeAt(i) & 0xFF;
  }
  return bytes;
}
function arrayByteLength(arr) {
  if (arr.length !== undefined) {
    return arr.length;
  }
  assert(arr.byteLength !== undefined);
  return arr.byteLength;
}
function arraysToBytes(arr) {
  if (arr.length === 1 && arr[0] instanceof Uint8Array) {
    return arr[0];
  }
  var resultLength = 0;
  var i,
      ii = arr.length;
  var item, itemLength;
  for (i = 0; i < ii; i++) {
    item = arr[i];
    itemLength = arrayByteLength(item);
    resultLength += itemLength;
  }
  var pos = 0;
  var data = new Uint8Array(resultLength);
  for (i = 0; i < ii; i++) {
    item = arr[i];
    if (!(item instanceof Uint8Array)) {
      if (typeof item === 'string') {
        item = stringToBytes(item);
      } else {
        item = new Uint8Array(item);
      }
    }
    itemLength = item.byteLength;
    data.set(item, pos);
    pos += itemLength;
  }
  return data;
}
function string32(value) {
  return String.fromCharCode(value >> 24 & 0xff, value >> 16 & 0xff, value >> 8 & 0xff, value & 0xff);
}
function log2(x) {
  if (x <= 0) {
    return 0;
  }
  return Math.ceil(Math.log2(x));
}
function readInt8(data, start) {
  return data[start] << 24 >> 24;
}
function readUint16(data, offset) {
  return data[offset] << 8 | data[offset + 1];
}
function readUint32(data, offset) {
  return (data[offset] << 24 | data[offset + 1] << 16 | data[offset + 2] << 8 | data[offset + 3]) >>> 0;
}
function isLittleEndian() {
  var buffer8 = new Uint8Array(4);
  buffer8[0] = 1;
  var view32 = new Uint32Array(buffer8.buffer, 0, 1);
  return view32[0] === 1;
}
function isEvalSupported() {
  try {
    new Function('');
    return true;
  } catch (e) {
    return false;
  }
}
var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0];
var Util = function UtilClosure() {
  function Util() {}
  var rgbBuf = ['rgb(', 0, ',', 0, ',', 0, ')'];
  Util.makeCssRgb = function Util_makeCssRgb(r, g, b) {
    rgbBuf[1] = r;
    rgbBuf[3] = g;
    rgbBuf[5] = b;
    return rgbBuf.join('');
  };
  Util.transform = function Util_transform(m1, m2) {
    return [m1[0] * m2[0] + m1[2] * m2[1], m1[1] * m2[0] + m1[3] * m2[1], m1[0] * m2[2] + m1[2] * m2[3], m1[1] * m2[2] + m1[3] * m2[3], m1[0] * m2[4] + m1[2] * m2[5] + m1[4], m1[1] * m2[4] + m1[3] * m2[5] + m1[5]];
  };
  Util.applyTransform = function Util_applyTransform(p, m) {
    var xt = p[0] * m[0] + p[1] * m[2] + m[4];
    var yt = p[0] * m[1] + p[1] * m[3] + m[5];
    return [xt, yt];
  };
  Util.applyInverseTransform = function Util_applyInverseTransform(p, m) {
    var d = m[0] * m[3] - m[1] * m[2];
    var xt = (p[0] * m[3] - p[1] * m[2] + m[2] * m[5] - m[4] * m[3]) / d;
    var yt = (-p[0] * m[1] + p[1] * m[0] + m[4] * m[1] - m[5] * m[0]) / d;
    return [xt, yt];
  };
  Util.getAxialAlignedBoundingBox = function Util_getAxialAlignedBoundingBox(r, m) {
    var p1 = Util.applyTransform(r, m);
    var p2 = Util.applyTransform(r.slice(2, 4), m);
    var p3 = Util.applyTransform([r[0], r[3]], m);
    var p4 = Util.applyTransform([r[2], r[1]], m);
    return [Math.min(p1[0], p2[0], p3[0], p4[0]), Math.min(p1[1], p2[1], p3[1], p4[1]), Math.max(p1[0], p2[0], p3[0], p4[0]), Math.max(p1[1], p2[1], p3[1], p4[1])];
  };
  Util.inverseTransform = function Util_inverseTransform(m) {
    var d = m[0] * m[3] - m[1] * m[2];
    return [m[3] / d, -m[1] / d, -m[2] / d, m[0] / d, (m[2] * m[5] - m[4] * m[3]) / d, (m[4] * m[1] - m[5] * m[0]) / d];
  };
  Util.apply3dTransform = function Util_apply3dTransform(m, v) {
    return [m[0] * v[0] + m[1] * v[1] + m[2] * v[2], m[3] * v[0] + m[4] * v[1] + m[5] * v[2], m[6] * v[0] + m[7] * v[1] + m[8] * v[2]];
  };
  Util.singularValueDecompose2dScale = function Util_singularValueDecompose2dScale(m) {
    var transpose = [m[0], m[2], m[1], m[3]];
    var a = m[0] * transpose[0] + m[1] * transpose[2];
    var b = m[0] * transpose[1] + m[1] * transpose[3];
    var c = m[2] * transpose[0] + m[3] * transpose[2];
    var d = m[2] * transpose[1] + m[3] * transpose[3];
    var first = (a + d) / 2;
    var second = Math.sqrt((a + d) * (a + d) - 4 * (a * d - c * b)) / 2;
    var sx = first + second || 1;
    var sy = first - second || 1;
    return [Math.sqrt(sx), Math.sqrt(sy)];
  };
  Util.normalizeRect = function Util_normalizeRect(rect) {
    var r = rect.slice(0);
    if (rect[0] > rect[2]) {
      r[0] = rect[2];
      r[2] = rect[0];
    }
    if (rect[1] > rect[3]) {
      r[1] = rect[3];
      r[3] = rect[1];
    }
    return r;
  };
  Util.intersect = function Util_intersect(rect1, rect2) {
    function compare(a, b) {
      return a - b;
    }
    var orderedX = [rect1[0], rect1[2], rect2[0], rect2[2]].sort(compare),
        orderedY = [rect1[1], rect1[3], rect2[1], rect2[3]].sort(compare),
        result = [];
    rect1 = Util.normalizeRect(rect1);
    rect2 = Util.normalizeRect(rect2);
    if (orderedX[0] === rect1[0] && orderedX[1] === rect2[0] || orderedX[0] === rect2[0] && orderedX[1] === rect1[0]) {
      result[0] = orderedX[1];
      result[2] = orderedX[2];
    } else {
      return false;
    }
    if (orderedY[0] === rect1[1] && orderedY[1] === rect2[1] || orderedY[0] === rect2[1] && orderedY[1] === rect1[1]) {
      result[1] = orderedY[1];
      result[3] = orderedY[2];
    } else {
      return false;
    }
    return result;
  };
  var ROMAN_NUMBER_MAP = ['', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM', '', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC', '', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX'];
  Util.toRoman = function Util_toRoman(number, lowerCase) {
    assert(Number.isInteger(number) && number > 0, 'The number should be a positive integer.');
    var pos,
        romanBuf = [];
    while (number >= 1000) {
      number -= 1000;
      romanBuf.push('M');
    }
    pos = number / 100 | 0;
    number %= 100;
    romanBuf.push(ROMAN_NUMBER_MAP[pos]);
    pos = number / 10 | 0;
    number %= 10;
    romanBuf.push(ROMAN_NUMBER_MAP[10 + pos]);
    romanBuf.push(ROMAN_NUMBER_MAP[20 + number]);
    var romanStr = romanBuf.join('');
    return lowerCase ? romanStr.toLowerCase() : romanStr;
  };
  Util.appendToArray = function Util_appendToArray(arr1, arr2) {
    Array.prototype.push.apply(arr1, arr2);
  };
  Util.prependToArray = function Util_prependToArray(arr1, arr2) {
    Array.prototype.unshift.apply(arr1, arr2);
  };
  Util.extendObj = function extendObj(obj1, obj2) {
    for (var key in obj2) {
      obj1[key] = obj2[key];
    }
  };
  Util.getInheritableProperty = function Util_getInheritableProperty(dict, name, getArray) {
    while (dict && !dict.has(name)) {
      dict = dict.get('Parent');
    }
    if (!dict) {
      return null;
    }
    return getArray ? dict.getArray(name) : dict.get(name);
  };
  Util.inherit = function Util_inherit(sub, base, prototype) {
    sub.prototype = Object.create(base.prototype);
    sub.prototype.constructor = sub;
    for (var prop in prototype) {
      sub.prototype[prop] = prototype[prop];
    }
  };
  Util.loadScript = function Util_loadScript(src, callback) {
    var script = document.createElement('script');
    var loaded = false;
    script.setAttribute('src', src);
    if (callback) {
      script.onload = function () {
        if (!loaded) {
          callback();
        }
        loaded = true;
      };
    }
    document.getElementsByTagName('head')[0].appendChild(script);
  };
  return Util;
}();
var PageViewport = function PageViewportClosure() {
  function PageViewport(viewBox, scale, rotation, offsetX, offsetY, dontFlip) {
    this.viewBox = viewBox;
    this.scale = scale;
    this.rotation = rotation;
    this.offsetX = offsetX;
    this.offsetY = offsetY;
    var centerX = (viewBox[2] + viewBox[0]) / 2;
    var centerY = (viewBox[3] + viewBox[1]) / 2;
    var rotateA, rotateB, rotateC, rotateD;
    rotation = rotation % 360;
    rotation = rotation < 0 ? rotation + 360 : rotation;
    switch (rotation) {
      case 180:
        rotateA = -1;
        rotateB = 0;
        rotateC = 0;
        rotateD = 1;
        break;
      case 90:
        rotateA = 0;
        rotateB = 1;
        rotateC = 1;
        rotateD = 0;
        break;
      case 270:
        rotateA = 0;
        rotateB = -1;
        rotateC = -1;
        rotateD = 0;
        break;
      default:
        rotateA = 1;
        rotateB = 0;
        rotateC = 0;
        rotateD = -1;
        break;
    }
    if (dontFlip) {
      rotateC = -rotateC;
      rotateD = -rotateD;
    }
    var offsetCanvasX, offsetCanvasY;
    var width, height;
    if (rotateA === 0) {
      offsetCanvasX = Math.abs(centerY - viewBox[1]) * scale + offsetX;
      offsetCanvasY = Math.abs(centerX - viewBox[0]) * scale + offsetY;
      width = Math.abs(viewBox[3] - viewBox[1]) * scale;
      height = Math.abs(viewBox[2] - viewBox[0]) * scale;
    } else {
      offsetCanvasX = Math.abs(centerX - viewBox[0]) * scale + offsetX;
      offsetCanvasY = Math.abs(centerY - viewBox[1]) * scale + offsetY;
      width = Math.abs(viewBox[2] - viewBox[0]) * scale;
      height = Math.abs(viewBox[3] - viewBox[1]) * scale;
    }
    this.transform = [rotateA * scale, rotateB * scale, rotateC * scale, rotateD * scale, offsetCanvasX - rotateA * scale * centerX - rotateC * scale * centerY, offsetCanvasY - rotateB * scale * centerX - rotateD * scale * centerY];
    this.width = width;
    this.height = height;
    this.fontScale = scale;
  }
  PageViewport.prototype = {
    clone: function PageViewPort_clone(args) {
      args = args || {};
      var scale = 'scale' in args ? args.scale : this.scale;
      var rotation = 'rotation' in args ? args.rotation : this.rotation;
      return new PageViewport(this.viewBox.slice(), scale, rotation, this.offsetX, this.offsetY, args.dontFlip);
    },
    convertToViewportPoint: function PageViewport_convertToViewportPoint(x, y) {
      return Util.applyTransform([x, y], this.transform);
    },
    convertToViewportRectangle: function PageViewport_convertToViewportRectangle(rect) {
      var tl = Util.applyTransform([rect[0], rect[1]], this.transform);
      var br = Util.applyTransform([rect[2], rect[3]], this.transform);
      return [tl[0], tl[1], br[0], br[1]];
    },
    convertToPdfPoint: function PageViewport_convertToPdfPoint(x, y) {
      return Util.applyInverseTransform([x, y], this.transform);
    }
  };
  return PageViewport;
}();
var PDFStringTranslateTable = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2D8, 0x2C7, 0x2C6, 0x2D9, 0x2DD, 0x2DB, 0x2DA, 0x2DC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2022, 0x2020, 0x2021, 0x2026, 0x2014, 0x2013, 0x192, 0x2044, 0x2039, 0x203A, 0x2212, 0x2030, 0x201E, 0x201C, 0x201D, 0x2018, 0x2019, 0x201A, 0x2122, 0xFB01, 0xFB02, 0x141, 0x152, 0x160, 0x178, 0x17D, 0x131, 0x142, 0x153, 0x161, 0x17E, 0, 0x20AC];
function stringToPDFString(str) {
  var i,
      n = str.length,
      strBuf = [];
  if (str[0] === '\xFE' && str[1] === '\xFF') {
    for (i = 2; i < n; i += 2) {
      strBuf.push(String.fromCharCode(str.charCodeAt(i) << 8 | str.charCodeAt(i + 1)));
    }
  } else {
    for (i = 0; i < n; ++i) {
      var code = PDFStringTranslateTable[str.charCodeAt(i)];
      strBuf.push(code ? String.fromCharCode(code) : str.charAt(i));
    }
  }
  return strBuf.join('');
}
function stringToUTF8String(str) {
  return decodeURIComponent(escape(str));
}
function utf8StringToString(str) {
  return unescape(encodeURIComponent(str));
}
function isEmptyObj(obj) {
  for (var key in obj) {
    return false;
  }
  return true;
}
function isBool(v) {
  return typeof v === 'boolean';
}
function isNum(v) {
  return typeof v === 'number';
}
function isString(v) {
  return typeof v === 'string';
}
function isArrayBuffer(v) {
  return typeof v === 'object' && v !== null && v.byteLength !== undefined;
}
function isSpace(ch) {
  return ch === 0x20 || ch === 0x09 || ch === 0x0D || ch === 0x0A;
}
function createPromiseCapability() {
  var capability = {};
  capability.promise = new Promise(function (resolve, reject) {
    capability.resolve = resolve;
    capability.reject = reject;
  });
  return capability;
}
var createBlob = function createBlob(data, contentType) {
  if (typeof Blob !== 'undefined') {
    return new Blob([data], { type: contentType });
  }
  throw new Error('The "Blob" constructor is not supported.');
};
var createObjectURL = function createObjectURLClosure() {
  var digits = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
  return function createObjectURL(data, contentType, forceDataSchema = false) {
    if (!forceDataSchema && URL.createObjectURL) {
      var blob = createBlob(data, contentType);
      return URL.createObjectURL(blob);
    }
    var buffer = 'data:' + contentType + ';base64,';
    for (var i = 0, ii = data.length; i < ii; i += 3) {
      var b1 = data[i] & 0xFF;
      var b2 = data[i + 1] & 0xFF;
      var b3 = data[i + 2] & 0xFF;
      var d1 = b1 >> 2,
          d2 = (b1 & 3) << 4 | b2 >> 4;
      var d3 = i + 1 < ii ? (b2 & 0xF) << 2 | b3 >> 6 : 64;
      var d4 = i + 2 < ii ? b3 & 0x3F : 64;
      buffer += digits[d1] + digits[d2] + digits[d3] + digits[d4];
    }
    return buffer;
  };
}();
function resolveCall(fn, args, thisArg = null) {
  if (!fn) {
    return Promise.resolve(undefined);
  }
  return new Promise((resolve, reject) => {
    resolve(fn.apply(thisArg, args));
  });
}
function wrapReason(reason) {
  if (typeof reason !== 'object') {
    return reason;
  }
  switch (reason.name) {
    case 'AbortException':
      return new AbortException(reason.message);
    case 'MissingPDFException':
      return new MissingPDFException(reason.message);
    case 'UnexpectedResponseException':
      return new UnexpectedResponseException(reason.message, reason.status);
    default:
      return new UnknownErrorException(reason.message, reason.details);
  }
}
function makeReasonSerializable(reason) {
  if (!(reason instanceof Error) || reason instanceof AbortException || reason instanceof MissingPDFException || reason instanceof UnexpectedResponseException || reason instanceof UnknownErrorException) {
    return reason;
  }
  return new UnknownErrorException(reason.message, reason.toString());
}
function resolveOrReject(capability, success, reason) {
  if (success) {
    capability.resolve();
  } else {
    capability.reject(reason);
  }
}
function finalize(promise) {
  return Promise.resolve(promise).catch(() => {});
}
function MessageHandler(sourceName, targetName, comObj) {
  this.sourceName = sourceName;
  this.targetName = targetName;
  this.comObj = comObj;
  this.callbackId = 1;
  this.streamId = 1;
  this.postMessageTransfers = true;
  this.streamSinks = Object.create(null);
  this.streamControllers = Object.create(null);
  let callbacksCapabilities = this.callbacksCapabilities = Object.create(null);
  let ah = this.actionHandler = Object.create(null);
  this._onComObjOnMessage = event => {
    let data = event.data;
    if (data.targetName !== this.sourceName) {
      return;
    }
    if (data.stream) {
      this._processStreamMessage(data);
    } else if (data.isReply) {
      let callbackId = data.callbackId;
      if (data.callbackId in callbacksCapabilities) {
        let callback = callbacksCapabilities[callbackId];
        delete callbacksCapabilities[callbackId];
        if ('error' in data) {
          callback.reject(wrapReason(data.error));
        } else {
          callback.resolve(data.data);
        }
      } else {
        throw new Error(`Cannot resolve callback ${callbackId}`);
      }
    } else if (data.action in ah) {
      let action = ah[data.action];
      if (data.callbackId) {
        let sourceName = this.sourceName;
        let targetName = data.sourceName;
        Promise.resolve().then(function () {
          return action[0].call(action[1], data.data);
        }).then(result => {
          comObj.postMessage({
            sourceName,
            targetName,
            isReply: true,
            callbackId: data.callbackId,
            data: result
          });
        }, reason => {
          comObj.postMessage({
            sourceName,
            targetName,
            isReply: true,
            callbackId: data.callbackId,
            error: makeReasonSerializable(reason)
          });
        });
      } else if (data.streamId) {
        this._createStreamSink(data);
      } else {
        action[0].call(action[1], data.data);
      }
    } else {
      throw new Error(`Unknown action from worker: ${data.action}`);
    }
  };
  comObj.addEventListener('message', this._onComObjOnMessage);
}
MessageHandler.prototype = {
  on(actionName, handler, scope) {
    var ah = this.actionHandler;
    if (ah[actionName]) {
      throw new Error(`There is already an actionName called "${actionName}"`);
    }
    ah[actionName] = [handler, scope];
  },
  send(actionName, data, transfers) {
    var message = {
      sourceName: this.sourceName,
      targetName: this.targetName,
      action: actionName,
      data
    };
    this.postMessage(message, transfers);
  },
  sendWithPromise(actionName, data, transfers) {
    var callbackId = this.callbackId++;
    var message = {
      sourceName: this.sourceName,
      targetName: this.targetName,
      action: actionName,
      data,
      callbackId
    };
    var capability = createPromiseCapability();
    this.callbacksCapabilities[callbackId] = capability;
    try {
      this.postMessage(message, transfers);
    } catch (e) {
      capability.reject(e);
    }
    return capability.promise;
  },
  sendWithStream(actionName, data, queueingStrategy, transfers) {
    let streamId = this.streamId++;
    let sourceName = this.sourceName;
    let targetName = this.targetName;
    return new _streams_polyfill.ReadableStream({
      start: controller => {
        let startCapability = createPromiseCapability();
        this.streamControllers[streamId] = {
          controller,
          startCall: startCapability,
          isClosed: false
        };
        this.postMessage({
          sourceName,
          targetName,
          action: actionName,
          streamId,
          data,
          desiredSize: controller.desiredSize
        });
        return startCapability.promise;
      },
      pull: controller => {
        let pullCapability = createPromiseCapability();
        this.streamControllers[streamId].pullCall = pullCapability;
        this.postMessage({
          sourceName,
          targetName,
          stream: 'pull',
          streamId,
          desiredSize: controller.desiredSize
        });
        return pullCapability.promise;
      },
      cancel: reason => {
        let cancelCapability = createPromiseCapability();
        this.streamControllers[streamId].cancelCall = cancelCapability;
        this.streamControllers[streamId].isClosed = true;
        this.postMessage({
          sourceName,
          targetName,
          stream: 'cancel',
          reason,
          streamId
        });
        return cancelCapability.promise;
      }
    }, queueingStrategy);
  },
  _createStreamSink(data) {
    let self = this;
    let action = this.actionHandler[data.action];
    let streamId = data.streamId;
    let desiredSize = data.desiredSize;
    let sourceName = this.sourceName;
    let targetName = data.sourceName;
    let capability = createPromiseCapability();
    let sendStreamRequest = ({ stream, chunk, transfers, success, reason }) => {
      this.postMessage({
        sourceName,
        targetName,
        stream,
        streamId,
        chunk,
        success,
        reason
      }, transfers);
    };
    let streamSink = {
      enqueue(chunk, size = 1, transfers) {
        if (this.isCancelled) {
          return;
        }
        let lastDesiredSize = this.desiredSize;
        this.desiredSize -= size;
        if (lastDesiredSize > 0 && this.desiredSize <= 0) {
          this.sinkCapability = createPromiseCapability();
          this.ready = this.sinkCapability.promise;
        }
        sendStreamRequest({
          stream: 'enqueue',
          chunk,
          transfers
        });
      },
      close() {
        if (this.isCancelled) {
          return;
        }
        this.isCancelled = true;
        sendStreamRequest({ stream: 'close' });
        delete self.streamSinks[streamId];
      },
      error(reason) {
        if (this.isCancelled) {
          return;
        }
        this.isCancelled = true;
        sendStreamRequest({
          stream: 'error',
          reason
        });
      },
      sinkCapability: capability,
      onPull: null,
      onCancel: null,
      isCancelled: false,
      desiredSize,
      ready: null
    };
    streamSink.sinkCapability.resolve();
    streamSink.ready = streamSink.sinkCapability.promise;
    this.streamSinks[streamId] = streamSink;
    resolveCall(action[0], [data.data, streamSink], action[1]).then(() => {
      sendStreamRequest({
        stream: 'start_complete',
        success: true
      });
    }, reason => {
      sendStreamRequest({
        stream: 'start_complete',
        success: false,
        reason
      });
    });
  },
  _processStreamMessage(data) {
    let sourceName = this.sourceName;
    let targetName = data.sourceName;
    let streamId = data.streamId;
    let sendStreamResponse = ({ stream, success, reason }) => {
      this.comObj.postMessage({
        sourceName,
        targetName,
        stream,
        success,
        streamId,
        reason
      });
    };
    let deleteStreamController = () => {
      Promise.all([this.streamControllers[data.streamId].startCall, this.streamControllers[data.streamId].pullCall, this.streamControllers[data.streamId].cancelCall].map(function (capability) {
        return capability && finalize(capability.promise);
      })).then(() => {
        delete this.streamControllers[data.streamId];
      });
    };
    switch (data.stream) {
      case 'start_complete':
        resolveOrReject(this.streamControllers[data.streamId].startCall, data.success, wrapReason(data.reason));
        break;
      case 'pull_complete':
        resolveOrReject(this.streamControllers[data.streamId].pullCall, data.success, wrapReason(data.reason));
        break;
      case 'pull':
        if (!this.streamSinks[data.streamId]) {
          sendStreamResponse({
            stream: 'pull_complete',
            success: true
          });
          break;
        }
        if (this.streamSinks[data.streamId].desiredSize <= 0 && data.desiredSize > 0) {
          this.streamSinks[data.streamId].sinkCapability.resolve();
        }
        this.streamSinks[data.streamId].desiredSize = data.desiredSize;
        resolveCall(this.streamSinks[data.streamId].onPull).then(() => {
          sendStreamResponse({
            stream: 'pull_complete',
            success: true
          });
        }, reason => {
          sendStreamResponse({
            stream: 'pull_complete',
            success: false,
            reason
          });
        });
        break;
      case 'enqueue':
        assert(this.streamControllers[data.streamId], 'enqueue should have stream controller');
        if (!this.streamControllers[data.streamId].isClosed) {
          this.streamControllers[data.streamId].controller.enqueue(data.chunk);
        }
        break;
      case 'close':
        assert(this.streamControllers[data.streamId], 'close should have stream controller');
        if (this.streamControllers[data.streamId].isClosed) {
          break;
        }
        this.streamControllers[data.streamId].isClosed = true;
        this.streamControllers[data.streamId].controller.close();
        deleteStreamController();
        break;
      case 'error':
        assert(this.streamControllers[data.streamId], 'error should have stream controller');
        this.streamControllers[data.streamId].controller.error(wrapReason(data.reason));
        deleteStreamController();
        break;
      case 'cancel_complete':
        resolveOrReject(this.streamControllers[data.streamId].cancelCall, data.success, wrapReason(data.reason));
        deleteStreamController();
        break;
      case 'cancel':
        if (!this.streamSinks[data.streamId]) {
          break;
        }
        resolveCall(this.streamSinks[data.streamId].onCancel, [wrapReason(data.reason)]).then(() => {
          sendStreamResponse({
            stream: 'cancel_complete',
            success: true
          });
        }, reason => {
          sendStreamResponse({
            stream: 'cancel_complete',
            success: false,
            reason
          });
        });
        this.streamSinks[data.streamId].sinkCapability.reject(wrapReason(data.reason));
        this.streamSinks[data.streamId].isCancelled = true;
        delete this.streamSinks[data.streamId];
        break;
      default:
        throw new Error('Unexpected stream case');
    }
  },
  postMessage(message, transfers) {
    if (transfers && this.postMessageTransfers) {
      this.comObj.postMessage(message, transfers);
    } else {
      this.comObj.postMessage(message);
    }
  },
  destroy() {
    this.comObj.removeEventListener('message', this._onComObjOnMessage);
  }
};
exports.FONT_IDENTITY_MATRIX = FONT_IDENTITY_MATRIX;
exports.IDENTITY_MATRIX = IDENTITY_MATRIX;
exports.OPS = OPS;
exports.VerbosityLevel = VerbosityLevel;
exports.UNSUPPORTED_FEATURES = UNSUPPORTED_FEATURES;
exports.AnnotationBorderStyleType = AnnotationBorderStyleType;
exports.AnnotationFieldFlag = AnnotationFieldFlag;
exports.AnnotationFlag = AnnotationFlag;
exports.AnnotationType = AnnotationType;
exports.FontType = FontType;
exports.ImageKind = ImageKind;
exports.CMapCompressionType = CMapCompressionType;
exports.AbortException = AbortException;
exports.InvalidPDFException = InvalidPDFException;
exports.MessageHandler = MessageHandler;
exports.MissingDataException = MissingDataException;
exports.MissingPDFException = MissingPDFException;
exports.NativeImageDecoding = NativeImageDecoding;
exports.NotImplementedException = NotImplementedException;
exports.PageViewport = PageViewport;
exports.PasswordException = PasswordException;
exports.PasswordResponses = PasswordResponses;
exports.StreamType = StreamType;
exports.TextRenderingMode = TextRenderingMode;
exports.UnexpectedResponseException = UnexpectedResponseException;
exports.UnknownErrorException = UnknownErrorException;
exports.Util = Util;
exports.XRefParseException = XRefParseException;
exports.FormatError = FormatError;
exports.arrayByteLength = arrayByteLength;
exports.arraysToBytes = arraysToBytes;
exports.assert = assert;
exports.bytesToString = bytesToString;
exports.createBlob = createBlob;
exports.createPromiseCapability = createPromiseCapability;
exports.createObjectURL = createObjectURL;
exports.deprecated = deprecated;
exports.getLookupTableFactory = getLookupTableFactory;
exports.getVerbosityLevel = getVerbosityLevel;
exports.info = info;
exports.isArrayBuffer = isArrayBuffer;
exports.isBool = isBool;
exports.isEmptyObj = isEmptyObj;
exports.isNum = isNum;
exports.isString = isString;
exports.isSpace = isSpace;
exports.isSameOrigin = isSameOrigin;
exports.createValidAbsoluteUrl = createValidAbsoluteUrl;
exports.isLittleEndian = isLittleEndian;
exports.isEvalSupported = isEvalSupported;
exports.log2 = log2;
exports.readInt8 = readInt8;
exports.readUint16 = readUint16;
exports.readUint32 = readUint32;
exports.removeNullCharacters = removeNullCharacters;
exports.ReadableStream = _streams_polyfill.ReadableStream;
exports.setVerbosityLevel = setVerbosityLevel;
exports.shadow = shadow;
exports.string32 = string32;
exports.stringToBytes = stringToBytes;
exports.stringToPDFString = stringToPDFString;
exports.stringToUTF8String = stringToUTF8String;
exports.utf8StringToString = utf8StringToString;
exports.warn = warn;
exports.unreachable = unreachable;

/***/ }),
/* 1 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.DummyStatTimer = exports.StatTimer = exports.SimpleXMLParser = exports.DOMSVGFactory = exports.DOMCMapReaderFactory = exports.DOMCanvasFactory = exports.DEFAULT_LINK_REL = exports.getDefaultSetting = exports.LinkTarget = exports.getFilenameFromUrl = exports.addLinkAttributes = exports.RenderingCancelledException = undefined;

var _util = __w_pdfjs_require__(0);

var _global_scope = __w_pdfjs_require__(2);

var _global_scope2 = _interopRequireDefault(_global_scope);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

const DEFAULT_LINK_REL = 'noopener noreferrer nofollow';
const SVG_NS = 'http://www.w3.org/2000/svg';
class DOMCanvasFactory {
  create(width, height) {
    if (width <= 0 || height <= 0) {
      throw new Error('invalid canvas size');
    }
    let canvas = document.createElement('canvas');
    let context = canvas.getContext('2d');
    canvas.width = width;
    canvas.height = height;
    return {
      canvas,
      context
    };
  }
  reset(canvasAndContext, width, height) {
    if (!canvasAndContext.canvas) {
      throw new Error('canvas is not specified');
    }
    if (width <= 0 || height <= 0) {
      throw new Error('invalid canvas size');
    }
    canvasAndContext.canvas.width = width;
    canvasAndContext.canvas.height = height;
  }
  destroy(canvasAndContext) {
    if (!canvasAndContext.canvas) {
      throw new Error('canvas is not specified');
    }
    canvasAndContext.canvas.width = 0;
    canvasAndContext.canvas.height = 0;
    canvasAndContext.canvas = null;
    canvasAndContext.context = null;
  }
}
class DOMCMapReaderFactory {
  constructor({ baseUrl = null, isCompressed = false }) {
    this.baseUrl = baseUrl;
    this.isCompressed = isCompressed;
  }
  fetch({ name }) {
    if (!this.baseUrl) {
      return Promise.reject(new Error('CMap baseUrl must be specified, ' + 'see "PDFJS.cMapUrl" (and also "PDFJS.cMapPacked").'));
    }
    if (!name) {
      return Promise.reject(new Error('CMap name must be specified.'));
    }
    return new Promise((resolve, reject) => {
      let url = this.baseUrl + name + (this.isCompressed ? '.bcmap' : '');
      let request = new XMLHttpRequest();
      request.open('GET', url, true);
      if (this.isCompressed) {
        request.responseType = 'arraybuffer';
      }
      request.onreadystatechange = () => {
        if (request.readyState !== XMLHttpRequest.DONE) {
          return;
        }
        if (request.status === 200 || request.status === 0) {
          let data;
          if (this.isCompressed && request.response) {
            data = new Uint8Array(request.response);
          } else if (!this.isCompressed && request.responseText) {
            data = (0, _util.stringToBytes)(request.responseText);
          }
          if (data) {
            resolve({
              cMapData: data,
              compressionType: this.isCompressed ? _util.CMapCompressionType.BINARY : _util.CMapCompressionType.NONE
            });
            return;
          }
        }
        reject(new Error('Unable to load ' + (this.isCompressed ? 'binary ' : '') + 'CMap at: ' + url));
      };
      request.send(null);
    });
  }
}
class DOMSVGFactory {
  create(width, height) {
    (0, _util.assert)(width > 0 && height > 0, 'Invalid SVG dimensions');
    let svg = document.createElementNS(SVG_NS, 'svg:svg');
    svg.setAttribute('version', '1.1');
    svg.setAttribute('width', width + 'px');
    svg.setAttribute('height', height + 'px');
    svg.setAttribute('preserveAspectRatio', 'none');
    svg.setAttribute('viewBox', '0 0 ' + width + ' ' + height);
    return svg;
  }
  createElement(type) {
    (0, _util.assert)(typeof type === 'string', 'Invalid SVG element type');
    return document.createElementNS(SVG_NS, type);
  }
}
class SimpleDOMNode {
  constructor(nodeName, nodeValue) {
    this.nodeName = nodeName;
    this.nodeValue = nodeValue;
    Object.defineProperty(this, 'parentNode', {
      value: null,
      writable: true
    });
  }
  get firstChild() {
    return this.childNodes[0];
  }
  get nextSibling() {
    let index = this.parentNode.childNodes.indexOf(this);
    return this.parentNode.childNodes[index + 1];
  }
  get textContent() {
    if (!this.childNodes) {
      return this.nodeValue || '';
    }
    return this.childNodes.map(function (child) {
      return child.textContent;
    }).join('');
  }
  hasChildNodes() {
    return this.childNodes && this.childNodes.length > 0;
  }
}
class SimpleXMLParser {
  parseFromString(data) {
    let nodes = [];
    data = data.replace(/<\?[\s\S]*?\?>|<!--[\s\S]*?-->/g, '').trim();
    data = data.replace(/<!DOCTYPE[^>\[]+(\[[^\]]+)?[^>]+>/g, '').trim();
    data = data.replace(/>([^<][\s\S]*?)</g, (all, text) => {
      let length = nodes.length;
      let node = new SimpleDOMNode('#text', this._decodeXML(text));
      nodes.push(node);
      if (node.textContent.trim().length === 0) {
        return '><';
      }
      return '>' + length + ',<';
    });
    data = data.replace(/<!\[CDATA\[([\s\S]*?)\]\]>/g, function (all, text) {
      let length = nodes.length;
      let node = new SimpleDOMNode('#text', text);
      nodes.push(node);
      return length + ',';
    });
    let regex = /<([\w\:]+)((?:[\s\w:=]|'[^']*'|"[^"]*")*)(?:\/>|>([\d,]*)<\/[^>]+>)/g;
    let lastLength;
    do {
      lastLength = nodes.length;
      data = data.replace(regex, function (all, name, attrs, data) {
        let length = nodes.length;
        let node = new SimpleDOMNode(name);
        let children = [];
        if (data) {
          data = data.split(',');
          data.pop();
          data.forEach(function (child) {
            let childNode = nodes[+child];
            childNode.parentNode = node;
            children.push(childNode);
          });
        }
        node.childNodes = children;
        nodes.push(node);
        return length + ',';
      });
    } while (lastLength < nodes.length);
    return { documentElement: nodes.pop() };
  }
  _decodeXML(text) {
    if (!text.includes('&')) {
      return text;
    }
    return text.replace(/&(#(x[0-9a-f]+|\d+)|\w+);/gi, function (all, entityName, number) {
      if (number) {
        if (number[0] === 'x') {
          number = parseInt(number.substring(1), 16);
        } else {
          number = +number;
        }
        return String.fromCharCode(number);
      }
      switch (entityName) {
        case 'amp':
          return '&';
        case 'lt':
          return '<';
        case 'gt':
          return '>';
        case 'quot':
          return '\"';
        case 'apos':
          return '\'';
      }
      return '&' + entityName + ';';
    });
  }
}
var RenderingCancelledException = function RenderingCancelledException() {
  function RenderingCancelledException(msg, type) {
    this.message = msg;
    this.type = type;
  }
  RenderingCancelledException.prototype = new Error();
  RenderingCancelledException.prototype.name = 'RenderingCancelledException';
  RenderingCancelledException.constructor = RenderingCancelledException;
  return RenderingCancelledException;
}();
const LinkTarget = {
  NONE: 0,
  SELF: 1,
  BLANK: 2,
  PARENT: 3,
  TOP: 4
};
const LinkTargetStringMap = ['', '_self', '_blank', '_parent', '_top'];
function addLinkAttributes(link, { url, target, rel } = {}) {
  link.href = link.title = url ? (0, _util.removeNullCharacters)(url) : '';
  if (url) {
    const LinkTargetValues = Object.values(LinkTarget);
    let targetIndex = LinkTargetValues.includes(target) ? target : LinkTarget.NONE;
    link.target = LinkTargetStringMap[targetIndex];
    link.rel = typeof rel === 'string' ? rel : DEFAULT_LINK_REL;
  }
}
function getFilenameFromUrl(url) {
  var anchor = url.indexOf('#');
  var query = url.indexOf('?');
  var end = Math.min(anchor > 0 ? anchor : url.length, query > 0 ? query : url.length);
  return url.substring(url.lastIndexOf('/', end) + 1, end);
}
function getDefaultSetting(id) {
  var globalSettings = _global_scope2.default.PDFJS;
  switch (id) {
    case 'pdfBug':
      return globalSettings ? globalSettings.pdfBug : false;
    case 'disableAutoFetch':
      return globalSettings ? globalSettings.disableAutoFetch : false;
    case 'disableStream':
      return globalSettings ? globalSettings.disableStream : false;
    case 'disableRange':
      return globalSettings ? globalSettings.disableRange : false;
    case 'disableFontFace':
      return globalSettings ? globalSettings.disableFontFace : false;
    case 'disableCreateObjectURL':
      return globalSettings ? globalSettings.disableCreateObjectURL : false;
    case 'cMapUrl':
      return globalSettings ? globalSettings.cMapUrl : null;
    case 'cMapPacked':
      return globalSettings ? globalSettings.cMapPacked : false;
    case 'maxImageSize':
      return globalSettings ? globalSettings.maxImageSize : -1;
    case 'isEvalSupported':
      return globalSettings ? globalSettings.isEvalSupported : true;
    default:
      throw new Error('Unknown default setting: ' + id);
  }
}
class StatTimer {
  constructor(enable = true) {
    this.enabled = !!enable;
    this.started = Object.create(null);
    this.times = [];
  }
  time(name) {
    if (!this.enabled) {
      return;
    }
    if (name in this.started) {
      (0, _util.warn)('Timer is already running for ' + name);
    }
    this.started[name] = Date.now();
  }
  timeEnd(name) {
    if (!this.enabled) {
      return;
    }
    if (!(name in this.started)) {
      (0, _util.warn)('Timer has not been started for ' + name);
    }
    this.times.push({
      'name': name,
      'start': this.started[name],
      'end': Date.now()
    });
    delete this.started[name];
  }
  toString() {
    let times = this.times;
    let out = '',
        longest = 0;
    for (let i = 0, ii = times.length; i < ii; ++i) {
      let name = times[i]['name'];
      if (name.length > longest) {
        longest = name.length;
      }
    }
    for (let i = 0, ii = times.length; i < ii; ++i) {
      let span = times[i];
      let duration = span.end - span.start;
      out += `${span['name'].padEnd(longest)} ${duration}ms\n`;
    }
    return out;
  }
}
class DummyStatTimer {
  constructor() {
    (0, _util.unreachable)('Cannot initialize DummyStatTimer.');
  }
  static time(name) {}
  static timeEnd(name) {}
  static toString() {
    return '';
  }
}
exports.RenderingCancelledException = RenderingCancelledException;
exports.addLinkAttributes = addLinkAttributes;
exports.getFilenameFromUrl = getFilenameFromUrl;
exports.LinkTarget = LinkTarget;
exports.getDefaultSetting = getDefaultSetting;
exports.DEFAULT_LINK_REL = DEFAULT_LINK_REL;
exports.DOMCanvasFactory = DOMCanvasFactory;
exports.DOMCMapReaderFactory = DOMCMapReaderFactory;
exports.DOMSVGFactory = DOMSVGFactory;
exports.SimpleXMLParser = SimpleXMLParser;
exports.StatTimer = StatTimer;
exports.DummyStatTimer = DummyStatTimer;

/***/ }),
/* 2 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


module.exports = typeof window !== 'undefined' && window.Math === Math ? window : typeof global !== 'undefined' && global.Math === Math ? global : typeof self !== 'undefined' && self.Math === Math ? self : {};

/***/ }),
/* 3 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
const GlobalWorkerOptions = Object.create(null);
GlobalWorkerOptions.workerPort = GlobalWorkerOptions.workerPort === undefined ? null : GlobalWorkerOptions.workerPort;
GlobalWorkerOptions.workerSrc = GlobalWorkerOptions.workerSrc === undefined ? '' : GlobalWorkerOptions.workerSrc;
exports.GlobalWorkerOptions = GlobalWorkerOptions;

/***/ }),
/* 4 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.build = exports.version = exports.setPDFNetworkStreamFactory = exports.PDFPageProxy = exports.PDFDocumentProxy = exports.PDFWorker = exports.PDFDataRangeTransport = exports.LoopbackPort = exports.getDocument = undefined;

var _util = __w_pdfjs_require__(0);

var _dom_utils = __w_pdfjs_require__(1);

var _font_loader = __w_pdfjs_require__(14);

var _canvas = __w_pdfjs_require__(15);

var _global_scope = __w_pdfjs_require__(2);

var _global_scope2 = _interopRequireDefault(_global_scope);

var _worker_options = __w_pdfjs_require__(3);

var _metadata = __w_pdfjs_require__(5);

var _transport_stream = __w_pdfjs_require__(17);

var _webgl = __w_pdfjs_require__(18);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

var DEFAULT_RANGE_CHUNK_SIZE = 65536;
let isWorkerDisabled = false;
let workerSrc;
const pdfjsFilePath = null;
var fakeWorkerFilesLoader = null;
var useRequireEnsure = false;
;
var createPDFNetworkStream;
function setPDFNetworkStreamFactory(pdfNetworkStreamFactory) {
  createPDFNetworkStream = pdfNetworkStreamFactory;
}
function getDocument(src) {
  var task = new PDFDocumentLoadingTask();
  var source;
  if (typeof src === 'string') {
    source = { url: src };
  } else if ((0, _util.isArrayBuffer)(src)) {
    source = { data: src };
  } else if (src instanceof PDFDataRangeTransport) {
    source = { range: src };
  } else {
    if (typeof src !== 'object') {
      throw new Error('Invalid parameter in getDocument, ' + 'need either Uint8Array, string or a parameter object');
    }
    if (!src.url && !src.data && !src.range) {
      throw new Error('Invalid parameter object: need either .data, .range or .url');
    }
    source = src;
  }
  var params = {};
  var rangeTransport = null;
  let worker = null;
  var CMapReaderFactory = _dom_utils.DOMCMapReaderFactory;
  for (var key in source) {
    if (key === 'url' && typeof window !== 'undefined') {
      params[key] = new URL(source[key], window.location).href;
      continue;
    } else if (key === 'range') {
      rangeTransport = source[key];
      continue;
    } else if (key === 'worker') {
      worker = source[key];
      continue;
    } else if (key === 'data' && !(source[key] instanceof Uint8Array)) {
      var pdfBytes = source[key];
      if (typeof pdfBytes === 'string') {
        params[key] = (0, _util.stringToBytes)(pdfBytes);
      } else if (typeof pdfBytes === 'object' && pdfBytes !== null && !isNaN(pdfBytes.length)) {
        params[key] = new Uint8Array(pdfBytes);
      } else if ((0, _util.isArrayBuffer)(pdfBytes)) {
        params[key] = new Uint8Array(pdfBytes);
      } else {
        throw new Error('Invalid PDF binary data: either typed array, ' + 'string or array-like object is expected in the ' + 'data property.');
      }
      continue;
    } else if (key === 'CMapReaderFactory') {
      CMapReaderFactory = source[key];
      continue;
    }
    params[key] = source[key];
  }
  params.rangeChunkSize = params.rangeChunkSize || DEFAULT_RANGE_CHUNK_SIZE;
  params.ignoreErrors = params.stopAtErrors !== true;
  const nativeImageDecoderValues = Object.values(_util.NativeImageDecoding);
  if (params.nativeImageDecoderSupport === undefined || !nativeImageDecoderValues.includes(params.nativeImageDecoderSupport)) {
    params.nativeImageDecoderSupport = _util.NativeImageDecoding.DECODE;
  }
  (0, _util.setVerbosityLevel)(params.verbosity);
  if (!worker) {
    const workerParams = {
      postMessageTransfers: params.postMessageTransfers,
      verbosity: params.verbosity
    };
    let workerPort = _worker_options.GlobalWorkerOptions.workerPort;
    if (workerPort) {
      workerParams.port = workerPort;
      worker = PDFWorker.fromPort(workerParams);
    } else {
      worker = new PDFWorker(workerParams);
    }
    task._worker = worker;
  }
  var docId = task.docId;
  worker.promise.then(function () {
    if (task.destroyed) {
      throw new Error('Loading aborted');
    }
    return _fetchDocument(worker, params, rangeTransport, docId).then(function (workerId) {
      if (task.destroyed) {
        throw new Error('Loading aborted');
      }
      let networkStream;
      if (rangeTransport) {
        networkStream = new _transport_stream.PDFDataTransportStream(params, rangeTransport);
      } else if (!params.data) {
        networkStream = createPDFNetworkStream(params);
      }
      var messageHandler = new _util.MessageHandler(docId, workerId, worker.port);
      messageHandler.postMessageTransfers = worker.postMessageTransfers;
      var transport = new WorkerTransport(messageHandler, task, networkStream, CMapReaderFactory);
      task._transport = transport;
      messageHandler.send('Ready', null);
    });
  }).catch(task._capability.reject);
  return task;
}
function _fetchDocument(worker, source, pdfDataRangeTransport, docId) {
  if (worker.destroyed) {
    return Promise.reject(new Error('Worker was destroyed'));
  }
  let apiVersion = '2.0.402';
  source.disableRange = (0, _dom_utils.getDefaultSetting)('disableRange');
  source.disableAutoFetch = (0, _dom_utils.getDefaultSetting)('disableAutoFetch');
  source.disableStream = (0, _dom_utils.getDefaultSetting)('disableStream');
  if (pdfDataRangeTransport) {
    source.length = pdfDataRangeTransport.length;
    source.initialData = pdfDataRangeTransport.initialData;
  }
  return worker.messageHandler.sendWithPromise('GetDocRequest', {
    docId,
    apiVersion,
    source: {
      data: source.data,
      url: source.url,
      password: source.password,
      disableAutoFetch: source.disableAutoFetch,
      rangeChunkSize: source.rangeChunkSize,
      length: source.length
    },
    maxImageSize: (0, _dom_utils.getDefaultSetting)('maxImageSize'),
    disableFontFace: (0, _dom_utils.getDefaultSetting)('disableFontFace'),
    disableCreateObjectURL: (0, _dom_utils.getDefaultSetting)('disableCreateObjectURL'),
    postMessageTransfers: worker.postMessageTransfers,
    docBaseUrl: source.docBaseUrl,
    nativeImageDecoderSupport: source.nativeImageDecoderSupport,
    ignoreErrors: source.ignoreErrors,
    isEvalSupported: (0, _dom_utils.getDefaultSetting)('isEvalSupported')
  }).then(function (workerId) {
    if (worker.destroyed) {
      throw new Error('Worker was destroyed');
    }
    return workerId;
  });
}
var PDFDocumentLoadingTask = function PDFDocumentLoadingTaskClosure() {
  var nextDocumentId = 0;
  function PDFDocumentLoadingTask() {
    this._capability = (0, _util.createPromiseCapability)();
    this._transport = null;
    this._worker = null;
    this.docId = 'd' + nextDocumentId++;
    this.destroyed = false;
    this.onPassword = null;
    this.onProgress = null;
    this.onUnsupportedFeature = null;
  }
  PDFDocumentLoadingTask.prototype = {
    get promise() {
      return this._capability.promise;
    },
    destroy() {
      this.destroyed = true;
      var transportDestroyed = !this._transport ? Promise.resolve() : this._transport.destroy();
      return transportDestroyed.then(() => {
        this._transport = null;
        if (this._worker) {
          this._worker.destroy();
          this._worker = null;
        }
      });
    },
    then: function PDFDocumentLoadingTask_then(onFulfilled, onRejected) {
      return this.promise.then.apply(this.promise, arguments);
    }
  };
  return PDFDocumentLoadingTask;
}();
var PDFDataRangeTransport = function pdfDataRangeTransportClosure() {
  function PDFDataRangeTransport(length, initialData) {
    this.length = length;
    this.initialData = initialData;
    this._rangeListeners = [];
    this._progressListeners = [];
    this._progressiveReadListeners = [];
    this._readyCapability = (0, _util.createPromiseCapability)();
  }
  PDFDataRangeTransport.prototype = {
    addRangeListener: function PDFDataRangeTransport_addRangeListener(listener) {
      this._rangeListeners.push(listener);
    },
    addProgressListener: function PDFDataRangeTransport_addProgressListener(listener) {
      this._progressListeners.push(listener);
    },
    addProgressiveReadListener: function PDFDataRangeTransport_addProgressiveReadListener(listener) {
      this._progressiveReadListeners.push(listener);
    },
    onDataRange: function PDFDataRangeTransport_onDataRange(begin, chunk) {
      var listeners = this._rangeListeners;
      for (var i = 0, n = listeners.length; i < n; ++i) {
        listeners[i](begin, chunk);
      }
    },
    onDataProgress: function PDFDataRangeTransport_onDataProgress(loaded) {
      this._readyCapability.promise.then(() => {
        var listeners = this._progressListeners;
        for (var i = 0, n = listeners.length; i < n; ++i) {
          listeners[i](loaded);
        }
      });
    },
    onDataProgressiveRead: function PDFDataRangeTransport_onDataProgress(chunk) {
      this._readyCapability.promise.then(() => {
        var listeners = this._progressiveReadListeners;
        for (var i = 0, n = listeners.length; i < n; ++i) {
          listeners[i](chunk);
        }
      });
    },
    transportReady: function PDFDataRangeTransport_transportReady() {
      this._readyCapability.resolve();
    },
    requestDataRange: function PDFDataRangeTransport_requestDataRange(begin, end) {
      (0, _util.unreachable)('Abstract method PDFDataRangeTransport.requestDataRange');
    },
    abort: function PDFDataRangeTransport_abort() {}
  };
  return PDFDataRangeTransport;
}();
var PDFDocumentProxy = function PDFDocumentProxyClosure() {
  function PDFDocumentProxy(pdfInfo, transport, loadingTask) {
    this.pdfInfo = pdfInfo;
    this.transport = transport;
    this.loadingTask = loadingTask;
  }
  PDFDocumentProxy.prototype = {
    get numPages() {
      return this.pdfInfo.numPages;
    },
    get fingerprint() {
      return this.pdfInfo.fingerprint;
    },
    getPage(pageNumber) {
      return this.transport.getPage(pageNumber);
    },
    getPageIndex: function PDFDocumentProxy_getPageIndex(ref) {
      return this.transport.getPageIndex(ref);
    },
    getDestinations: function PDFDocumentProxy_getDestinations() {
      return this.transport.getDestinations();
    },
    getDestination: function PDFDocumentProxy_getDestination(id) {
      return this.transport.getDestination(id);
    },
    getPageLabels: function PDFDocumentProxy_getPageLabels() {
      return this.transport.getPageLabels();
    },
    getPageMode() {
      return this.transport.getPageMode();
    },
    getAttachments: function PDFDocumentProxy_getAttachments() {
      return this.transport.getAttachments();
    },
    getJavaScript() {
      return this.transport.getJavaScript();
    },
    getOutline: function PDFDocumentProxy_getOutline() {
      return this.transport.getOutline();
    },
    getMetadata: function PDFDocumentProxy_getMetadata() {
      return this.transport.getMetadata();
    },
    getData: function PDFDocumentProxy_getData() {
      return this.transport.getData();
    },
    getDownloadInfo: function PDFDocumentProxy_getDownloadInfo() {
      return this.transport.downloadInfoCapability.promise;
    },
    getStats: function PDFDocumentProxy_getStats() {
      return this.transport.getStats();
    },
    cleanup: function PDFDocumentProxy_cleanup() {
      this.transport.startCleanup();
    },
    destroy: function PDFDocumentProxy_destroy() {
      return this.loadingTask.destroy();
    }
  };
  return PDFDocumentProxy;
}();
var PDFPageProxy = function PDFPageProxyClosure() {
  function PDFPageProxy(pageIndex, pageInfo, transport) {
    this.pageIndex = pageIndex;
    this.pageInfo = pageInfo;
    this.transport = transport;
    this._stats = (0, _dom_utils.getDefaultSetting)('pdfBug') ? new _dom_utils.StatTimer() : _dom_utils.DummyStatTimer;
    this.commonObjs = transport.commonObjs;
    this.objs = new PDFObjects();
    this.cleanupAfterRender = false;
    this.pendingCleanup = false;
    this.intentStates = Object.create(null);
    this.destroyed = false;
  }
  PDFPageProxy.prototype = {
    get pageNumber() {
      return this.pageIndex + 1;
    },
    get rotate() {
      return this.pageInfo.rotate;
    },
    get ref() {
      return this.pageInfo.ref;
    },
    get userUnit() {
      return this.pageInfo.userUnit;
    },
    get view() {
      return this.pageInfo.view;
    },
    getViewport(scale, rotate = this.rotate, dontFlip = false) {
      return new _util.PageViewport(this.view, scale, rotate, 0, 0, dontFlip);
    },
    getAnnotations: function PDFPageProxy_getAnnotations(params) {
      var intent = params && params.intent || null;
      if (!this.annotationsPromise || this.annotationsIntent !== intent) {
        this.annotationsPromise = this.transport.getAnnotations(this.pageIndex, intent);
        this.annotationsIntent = intent;
      }
      return this.annotationsPromise;
    },
    render: function PDFPageProxy_render(params) {
      let stats = this._stats;
      stats.time('Overall');
      this.pendingCleanup = false;
      var renderingIntent = params.intent === 'print' ? 'print' : 'display';
      var canvasFactory = params.canvasFactory || new _dom_utils.DOMCanvasFactory();
      let webGLContext = new _webgl.WebGLContext({ enable: params.enableWebGL });
      if (!this.intentStates[renderingIntent]) {
        this.intentStates[renderingIntent] = Object.create(null);
      }
      var intentState = this.intentStates[renderingIntent];
      if (!intentState.displayReadyCapability) {
        intentState.receivingOperatorList = true;
        intentState.displayReadyCapability = (0, _util.createPromiseCapability)();
        intentState.operatorList = {
          fnArray: [],
          argsArray: [],
          lastChunk: false
        };
        stats.time('Page Request');
        this.transport.messageHandler.send('RenderPageRequest', {
          pageIndex: this.pageNumber - 1,
          intent: renderingIntent,
          renderInteractiveForms: params.renderInteractiveForms === true
        });
      }
      var complete = error => {
        var i = intentState.renderTasks.indexOf(internalRenderTask);
        if (i >= 0) {
          intentState.renderTasks.splice(i, 1);
        }
        if (this.cleanupAfterRender) {
          this.pendingCleanup = true;
        }
        this._tryCleanup();
        if (error) {
          internalRenderTask.capability.reject(error);
        } else {
          internalRenderTask.capability.resolve();
        }
        stats.timeEnd('Rendering');
        stats.timeEnd('Overall');
      };
      var internalRenderTask = new InternalRenderTask(complete, params, this.objs, this.commonObjs, intentState.operatorList, this.pageNumber, canvasFactory, webGLContext);
      internalRenderTask.useRequestAnimationFrame = renderingIntent !== 'print';
      if (!intentState.renderTasks) {
        intentState.renderTasks = [];
      }
      intentState.renderTasks.push(internalRenderTask);
      var renderTask = internalRenderTask.task;
      intentState.displayReadyCapability.promise.then(transparency => {
        if (this.pendingCleanup) {
          complete();
          return;
        }
        stats.time('Rendering');
        internalRenderTask.initializeGraphics(transparency);
        internalRenderTask.operatorListChanged();
      }).catch(complete);
      return renderTask;
    },
    getOperatorList: function PDFPageProxy_getOperatorList() {
      function operatorListChanged() {
        if (intentState.operatorList.lastChunk) {
          intentState.opListReadCapability.resolve(intentState.operatorList);
          var i = intentState.renderTasks.indexOf(opListTask);
          if (i >= 0) {
            intentState.renderTasks.splice(i, 1);
          }
        }
      }
      var renderingIntent = 'oplist';
      if (!this.intentStates[renderingIntent]) {
        this.intentStates[renderingIntent] = Object.create(null);
      }
      var intentState = this.intentStates[renderingIntent];
      var opListTask;
      if (!intentState.opListReadCapability) {
        opListTask = {};
        opListTask.operatorListChanged = operatorListChanged;
        intentState.receivingOperatorList = true;
        intentState.opListReadCapability = (0, _util.createPromiseCapability)();
        intentState.renderTasks = [];
        intentState.renderTasks.push(opListTask);
        intentState.operatorList = {
          fnArray: [],
          argsArray: [],
          lastChunk: false
        };
        this.transport.messageHandler.send('RenderPageRequest', {
          pageIndex: this.pageIndex,
          intent: renderingIntent
        });
      }
      return intentState.opListReadCapability.promise;
    },
    streamTextContent(params = {}) {
      const TEXT_CONTENT_CHUNK_SIZE = 100;
      return this.transport.messageHandler.sendWithStream('GetTextContent', {
        pageIndex: this.pageNumber - 1,
        normalizeWhitespace: params.normalizeWhitespace === true,
        combineTextItems: params.disableCombineTextItems !== true
      }, {
        highWaterMark: TEXT_CONTENT_CHUNK_SIZE,
        size(textContent) {
          return textContent.items.length;
        }
      });
    },
    getTextContent: function PDFPageProxy_getTextContent(params) {
      params = params || {};
      let readableStream = this.streamTextContent(params);
      return new Promise(function (resolve, reject) {
        function pump() {
          reader.read().then(function ({ value, done }) {
            if (done) {
              resolve(textContent);
              return;
            }
            _util.Util.extendObj(textContent.styles, value.styles);
            _util.Util.appendToArray(textContent.items, value.items);
            pump();
          }, reject);
        }
        let reader = readableStream.getReader();
        let textContent = {
          items: [],
          styles: Object.create(null)
        };
        pump();
      });
    },
    _destroy: function PDFPageProxy_destroy() {
      this.destroyed = true;
      this.transport.pageCache[this.pageIndex] = null;
      var waitOn = [];
      Object.keys(this.intentStates).forEach(function (intent) {
        if (intent === 'oplist') {
          return;
        }
        var intentState = this.intentStates[intent];
        intentState.renderTasks.forEach(function (renderTask) {
          var renderCompleted = renderTask.capability.promise.catch(function () {});
          waitOn.push(renderCompleted);
          renderTask.cancel();
        });
      }, this);
      this.objs.clear();
      this.annotationsPromise = null;
      this.pendingCleanup = false;
      return Promise.all(waitOn);
    },
    cleanup(resetStats = false) {
      this.pendingCleanup = true;
      this._tryCleanup(resetStats);
    },
    _tryCleanup(resetStats = false) {
      if (!this.pendingCleanup || Object.keys(this.intentStates).some(function (intent) {
        var intentState = this.intentStates[intent];
        return intentState.renderTasks.length !== 0 || intentState.receivingOperatorList;
      }, this)) {
        return;
      }
      Object.keys(this.intentStates).forEach(function (intent) {
        delete this.intentStates[intent];
      }, this);
      this.objs.clear();
      this.annotationsPromise = null;
      if (resetStats && this._stats instanceof _dom_utils.StatTimer) {
        this._stats = new _dom_utils.StatTimer();
      }
      this.pendingCleanup = false;
    },
    _startRenderPage: function PDFPageProxy_startRenderPage(transparency, intent) {
      var intentState = this.intentStates[intent];
      if (intentState.displayReadyCapability) {
        intentState.displayReadyCapability.resolve(transparency);
      }
    },
    _renderPageChunk: function PDFPageProxy_renderPageChunk(operatorListChunk, intent) {
      var intentState = this.intentStates[intent];
      var i, ii;
      for (i = 0, ii = operatorListChunk.length; i < ii; i++) {
        intentState.operatorList.fnArray.push(operatorListChunk.fnArray[i]);
        intentState.operatorList.argsArray.push(operatorListChunk.argsArray[i]);
      }
      intentState.operatorList.lastChunk = operatorListChunk.lastChunk;
      for (i = 0; i < intentState.renderTasks.length; i++) {
        intentState.renderTasks[i].operatorListChanged();
      }
      if (operatorListChunk.lastChunk) {
        intentState.receivingOperatorList = false;
        this._tryCleanup();
      }
    },
    get stats() {
      return this._stats instanceof _dom_utils.StatTimer ? this._stats : null;
    }
  };
  return PDFPageProxy;
}();
class LoopbackPort {
  constructor(defer) {
    this._listeners = [];
    this._defer = defer;
    this._deferred = Promise.resolve(undefined);
  }
  postMessage(obj, transfers) {
    function cloneValue(value) {
      if (typeof value !== 'object' || value === null) {
        return value;
      }
      if (cloned.has(value)) {
        return cloned.get(value);
      }
      var result;
      var buffer;
      if ((buffer = value.buffer) && (0, _util.isArrayBuffer)(buffer)) {
        var transferable = transfers && transfers.includes(buffer);
        if (value === buffer) {
          result = value;
        } else if (transferable) {
          result = new value.constructor(buffer, value.byteOffset, value.byteLength);
        } else {
          result = new value.constructor(value);
        }
        cloned.set(value, result);
        return result;
      }
      result = Array.isArray(value) ? [] : {};
      cloned.set(value, result);
      for (var i in value) {
        var desc,
            p = value;
        while (!(desc = Object.getOwnPropertyDescriptor(p, i))) {
          p = Object.getPrototypeOf(p);
        }
        if (typeof desc.value === 'undefined' || typeof desc.value === 'function') {
          continue;
        }
        result[i] = cloneValue(desc.value);
      }
      return result;
    }
    if (!this._defer) {
      this._listeners.forEach(function (listener) {
        listener.call(this, { data: obj });
      }, this);
      return;
    }
    var cloned = new WeakMap();
    var e = { data: cloneValue(obj) };
    this._deferred.then(() => {
      this._listeners.forEach(function (listener) {
        listener.call(this, e);
      }, this);
    });
  }
  addEventListener(name, listener) {
    this._listeners.push(listener);
  }
  removeEventListener(name, listener) {
    var i = this._listeners.indexOf(listener);
    this._listeners.splice(i, 1);
  }
  terminate() {
    this._listeners = [];
  }
}
var PDFWorker = function PDFWorkerClosure() {
  let nextFakeWorkerId = 0;
  function getWorkerSrc() {
    if (_worker_options.GlobalWorkerOptions.workerSrc) {
      return _worker_options.GlobalWorkerOptions.workerSrc;
    }
    if (typeof workerSrc !== 'undefined') {
      return workerSrc;
    }
    throw new Error('No "GlobalWorkerOptions.workerSrc" specified.');
  }
  function getMainThreadWorkerMessageHandler() {
    if (typeof window === 'undefined') {
      return null;
    }
    return window.pdfjsDistBuildPdfWorker && window.pdfjsDistBuildPdfWorker.WorkerMessageHandler;
  }
  let fakeWorkerFilesLoadedCapability;
  function setupFakeWorkerGlobal() {
    if (fakeWorkerFilesLoadedCapability) {
      return fakeWorkerFilesLoadedCapability.promise;
    }
    fakeWorkerFilesLoadedCapability = (0, _util.createPromiseCapability)();
    let mainWorkerMessageHandler = getMainThreadWorkerMessageHandler();
    if (mainWorkerMessageHandler) {
      fakeWorkerFilesLoadedCapability.resolve(mainWorkerMessageHandler);
      return fakeWorkerFilesLoadedCapability.promise;
    }
    let loader = fakeWorkerFilesLoader || function (callback) {
      _util.Util.loadScript(getWorkerSrc(), function () {
        callback(window.pdfjsDistBuildPdfWorker.WorkerMessageHandler);
      });
    };
    loader(fakeWorkerFilesLoadedCapability.resolve);
    return fakeWorkerFilesLoadedCapability.promise;
  }
  function createCDNWrapper(url) {
    var wrapper = 'importScripts(\'' + url + '\');';
    return URL.createObjectURL(new Blob([wrapper]));
  }
  let pdfWorkerPorts = new WeakMap();
  function PDFWorker({ name = null, port = null, postMessageTransfers = true, verbosity = null } = {}) {
    if (port && pdfWorkerPorts.has(port)) {
      throw new Error('Cannot use more than one PDFWorker per port');
    }
    this.name = name;
    this.destroyed = false;
    this.postMessageTransfers = postMessageTransfers !== false;
    this.verbosity = (0, _util.isNum)(verbosity) ? verbosity : (0, _util.getVerbosityLevel)();
    this._readyCapability = (0, _util.createPromiseCapability)();
    this._port = null;
    this._webWorker = null;
    this._messageHandler = null;
    if (port) {
      pdfWorkerPorts.set(port, this);
      this._initializeFromPort(port);
      return;
    }
    this._initialize();
  }
  PDFWorker.prototype = {
    get promise() {
      return this._readyCapability.promise;
    },
    get port() {
      return this._port;
    },
    get messageHandler() {
      return this._messageHandler;
    },
    _initializeFromPort: function PDFWorker_initializeFromPort(port) {
      this._port = port;
      this._messageHandler = new _util.MessageHandler('main', 'worker', port);
      this._messageHandler.on('ready', function () {});
      this._readyCapability.resolve();
    },
    _initialize: function PDFWorker_initialize() {
      if (typeof Worker !== 'undefined' && !isWorkerDisabled && !getMainThreadWorkerMessageHandler()) {
        var workerSrc = getWorkerSrc();
        try {
          var worker = new Worker(workerSrc);
          var messageHandler = new _util.MessageHandler('main', 'worker', worker);
          var terminateEarly = () => {
            worker.removeEventListener('error', onWorkerError);
            messageHandler.destroy();
            worker.terminate();
            if (this.destroyed) {
              this._readyCapability.reject(new Error('Worker was destroyed'));
            } else {
              this._setupFakeWorker();
            }
          };
          var onWorkerError = () => {
            if (!this._webWorker) {
              terminateEarly();
            }
          };
          worker.addEventListener('error', onWorkerError);
          messageHandler.on('test', data => {
            worker.removeEventListener('error', onWorkerError);
            if (this.destroyed) {
              terminateEarly();
              return;
            }
            var supportTypedArray = data && data.supportTypedArray;
            if (supportTypedArray) {
              this._messageHandler = messageHandler;
              this._port = worker;
              this._webWorker = worker;
              if (!data.supportTransfers) {
                this.postMessageTransfers = false;
              }
              this._readyCapability.resolve();
              messageHandler.send('configure', { verbosity: this.verbosity });
            } else {
              this._setupFakeWorker();
              messageHandler.destroy();
              worker.terminate();
            }
          });
          messageHandler.on('ready', data => {
            worker.removeEventListener('error', onWorkerError);
            if (this.destroyed) {
              terminateEarly();
              return;
            }
            try {
              sendTest();
            } catch (e) {
              this._setupFakeWorker();
            }
          });
          const sendTest = () => {
            let testObj = new Uint8Array([this.postMessageTransfers ? 255 : 0]);
            try {
              messageHandler.send('test', testObj, [testObj.buffer]);
            } catch (ex) {
              (0, _util.info)('Cannot use postMessage transfers');
              testObj[0] = 0;
              messageHandler.send('test', testObj);
            }
          };
          sendTest();
          return;
        } catch (e) {
          (0, _util.info)('The worker has been disabled.');
        }
      }
      this._setupFakeWorker();
    },
    _setupFakeWorker: function PDFWorker_setupFakeWorker() {
      if (!isWorkerDisabled) {
        (0, _util.warn)('Setting up fake worker.');
        isWorkerDisabled = true;
      }
      setupFakeWorkerGlobal().then(WorkerMessageHandler => {
        if (this.destroyed) {
          this._readyCapability.reject(new Error('Worker was destroyed'));
          return;
        }
        var isTypedArraysPresent = Uint8Array !== Float32Array;
        var port = new LoopbackPort(isTypedArraysPresent);
        this._port = port;
        var id = 'fake' + nextFakeWorkerId++;
        var workerHandler = new _util.MessageHandler(id + '_worker', id, port);
        WorkerMessageHandler.setup(workerHandler, port);
        var messageHandler = new _util.MessageHandler(id, id + '_worker', port);
        this._messageHandler = messageHandler;
        this._readyCapability.resolve();
      });
    },
    destroy: function PDFWorker_destroy() {
      this.destroyed = true;
      if (this._webWorker) {
        this._webWorker.terminate();
        this._webWorker = null;
      }
      pdfWorkerPorts.delete(this._port);
      this._port = null;
      if (this._messageHandler) {
        this._messageHandler.destroy();
        this._messageHandler = null;
      }
    }
  };
  PDFWorker.fromPort = function (params) {
    if (pdfWorkerPorts.has(params.port)) {
      return pdfWorkerPorts.get(params.port);
    }
    return new PDFWorker(params);
  };
  PDFWorker.getWorkerSrc = function () {
    return getWorkerSrc();
  };
  return PDFWorker;
}();
var WorkerTransport = function WorkerTransportClosure() {
  function WorkerTransport(messageHandler, loadingTask, networkStream, CMapReaderFactory) {
    this.messageHandler = messageHandler;
    this.loadingTask = loadingTask;
    this.commonObjs = new PDFObjects();
    this.fontLoader = new _font_loader.FontLoader(loadingTask.docId);
    this.CMapReaderFactory = new CMapReaderFactory({
      baseUrl: (0, _dom_utils.getDefaultSetting)('cMapUrl'),
      isCompressed: (0, _dom_utils.getDefaultSetting)('cMapPacked')
    });
    this.destroyed = false;
    this.destroyCapability = null;
    this._passwordCapability = null;
    this._networkStream = networkStream;
    this._fullReader = null;
    this._lastProgress = null;
    this.pageCache = [];
    this.pagePromises = [];
    this.downloadInfoCapability = (0, _util.createPromiseCapability)();
    this.setupMessageHandler();
  }
  WorkerTransport.prototype = {
    destroy: function WorkerTransport_destroy() {
      if (this.destroyCapability) {
        return this.destroyCapability.promise;
      }
      this.destroyed = true;
      this.destroyCapability = (0, _util.createPromiseCapability)();
      if (this._passwordCapability) {
        this._passwordCapability.reject(new Error('Worker was destroyed during onPassword callback'));
      }
      var waitOn = [];
      this.pageCache.forEach(function (page) {
        if (page) {
          waitOn.push(page._destroy());
        }
      });
      this.pageCache = [];
      this.pagePromises = [];
      var terminated = this.messageHandler.sendWithPromise('Terminate', null);
      waitOn.push(terminated);
      Promise.all(waitOn).then(() => {
        this.fontLoader.clear();
        if (this._networkStream) {
          this._networkStream.cancelAllRequests();
        }
        if (this.messageHandler) {
          this.messageHandler.destroy();
          this.messageHandler = null;
        }
        this.destroyCapability.resolve();
      }, this.destroyCapability.reject);
      return this.destroyCapability.promise;
    },
    setupMessageHandler: function WorkerTransport_setupMessageHandler() {
      var messageHandler = this.messageHandler;
      var loadingTask = this.loadingTask;
      messageHandler.on('GetReader', function (data, sink) {
        (0, _util.assert)(this._networkStream);
        this._fullReader = this._networkStream.getFullReader();
        this._fullReader.onProgress = evt => {
          this._lastProgress = {
            loaded: evt.loaded,
            total: evt.total
          };
        };
        sink.onPull = () => {
          this._fullReader.read().then(function ({ value, done }) {
            if (done) {
              sink.close();
              return;
            }
            (0, _util.assert)((0, _util.isArrayBuffer)(value));
            sink.enqueue(new Uint8Array(value), 1, [value]);
          }).catch(reason => {
            sink.error(reason);
          });
        };
        sink.onCancel = reason => {
          this._fullReader.cancel(reason);
        };
      }, this);
      messageHandler.on('ReaderHeadersReady', function (data) {
        let headersCapability = (0, _util.createPromiseCapability)();
        let fullReader = this._fullReader;
        fullReader.headersReady.then(() => {
          if (!fullReader.isStreamingSupported || !fullReader.isRangeSupported) {
            if (this._lastProgress) {
              let loadingTask = this.loadingTask;
              if (loadingTask.onProgress) {
                loadingTask.onProgress(this._lastProgress);
              }
            }
            fullReader.onProgress = evt => {
              let loadingTask = this.loadingTask;
              if (loadingTask.onProgress) {
                loadingTask.onProgress({
                  loaded: evt.loaded,
                  total: evt.total
                });
              }
            };
          }
          headersCapability.resolve({
            isStreamingSupported: fullReader.isStreamingSupported,
            isRangeSupported: fullReader.isRangeSupported,
            contentLength: fullReader.contentLength
          });
        }, headersCapability.reject);
        return headersCapability.promise;
      }, this);
      messageHandler.on('GetRangeReader', function (data, sink) {
        (0, _util.assert)(this._networkStream);
        let _rangeReader = this._networkStream.getRangeReader(data.begin, data.end);
        sink.onPull = () => {
          _rangeReader.read().then(function ({ value, done }) {
            if (done) {
              sink.close();
              return;
            }
            (0, _util.assert)((0, _util.isArrayBuffer)(value));
            sink.enqueue(new Uint8Array(value), 1, [value]);
          }).catch(reason => {
            sink.error(reason);
          });
        };
        sink.onCancel = reason => {
          _rangeReader.cancel(reason);
        };
      }, this);
      messageHandler.on('GetDoc', function transportDoc({ pdfInfo }) {
        this.numPages = pdfInfo.numPages;
        var loadingTask = this.loadingTask;
        var pdfDocument = new PDFDocumentProxy(pdfInfo, this, loadingTask);
        this.pdfDocument = pdfDocument;
        loadingTask._capability.resolve(pdfDocument);
      }, this);
      messageHandler.on('PasswordRequest', function transportPasswordRequest(exception) {
        this._passwordCapability = (0, _util.createPromiseCapability)();
        if (loadingTask.onPassword) {
          var updatePassword = password => {
            this._passwordCapability.resolve({ password });
          };
          loadingTask.onPassword(updatePassword, exception.code);
        } else {
          this._passwordCapability.reject(new _util.PasswordException(exception.message, exception.code));
        }
        return this._passwordCapability.promise;
      }, this);
      messageHandler.on('PasswordException', function transportPasswordException(exception) {
        loadingTask._capability.reject(new _util.PasswordException(exception.message, exception.code));
      }, this);
      messageHandler.on('InvalidPDF', function transportInvalidPDF(exception) {
        this.loadingTask._capability.reject(new _util.InvalidPDFException(exception.message));
      }, this);
      messageHandler.on('MissingPDF', function transportMissingPDF(exception) {
        this.loadingTask._capability.reject(new _util.MissingPDFException(exception.message));
      }, this);
      messageHandler.on('UnexpectedResponse', function transportUnexpectedResponse(exception) {
        this.loadingTask._capability.reject(new _util.UnexpectedResponseException(exception.message, exception.status));
      }, this);
      messageHandler.on('UnknownError', function transportUnknownError(exception) {
        this.loadingTask._capability.reject(new _util.UnknownErrorException(exception.message, exception.details));
      }, this);
      messageHandler.on('DataLoaded', function transportPage(data) {
        this.downloadInfoCapability.resolve(data);
      }, this);
      messageHandler.on('PDFManagerReady', function transportPage(data) {}, this);
      messageHandler.on('StartRenderPage', function transportRender(data) {
        if (this.destroyed) {
          return;
        }
        var page = this.pageCache[data.pageIndex];
        page._stats.timeEnd('Page Request');
        page._startRenderPage(data.transparency, data.intent);
      }, this);
      messageHandler.on('RenderPageChunk', function transportRender(data) {
        if (this.destroyed) {
          return;
        }
        var page = this.pageCache[data.pageIndex];
        page._renderPageChunk(data.operatorList, data.intent);
      }, this);
      messageHandler.on('commonobj', function transportObj(data) {
        if (this.destroyed) {
          return;
        }
        var id = data[0];
        var type = data[1];
        if (this.commonObjs.hasData(id)) {
          return;
        }
        switch (type) {
          case 'Font':
            var exportedData = data[2];
            if ('error' in exportedData) {
              var exportedError = exportedData.error;
              (0, _util.warn)('Error during font loading: ' + exportedError);
              this.commonObjs.resolve(id, exportedError);
              break;
            }
            var fontRegistry = null;
            if ((0, _dom_utils.getDefaultSetting)('pdfBug') && _global_scope2.default.FontInspector && _global_scope2.default['FontInspector'].enabled) {
              fontRegistry = {
                registerFont(font, url) {
                  _global_scope2.default['FontInspector'].fontAdded(font, url);
                }
              };
            }
            var font = new _font_loader.FontFaceObject(exportedData, {
              isEvalSupported: (0, _dom_utils.getDefaultSetting)('isEvalSupported'),
              disableFontFace: (0, _dom_utils.getDefaultSetting)('disableFontFace'),
              fontRegistry
            });
            var fontReady = fontObjs => {
              this.commonObjs.resolve(id, font);
            };
            this.fontLoader.bind([font], fontReady);
            break;
          case 'FontPath':
            this.commonObjs.resolve(id, data[2]);
            break;
          default:
            throw new Error(`Got unknown common object type ${type}`);
        }
      }, this);
      messageHandler.on('obj', function transportObj(data) {
        if (this.destroyed) {
          return;
        }
        var id = data[0];
        var pageIndex = data[1];
        var type = data[2];
        var pageProxy = this.pageCache[pageIndex];
        var imageData;
        if (pageProxy.objs.hasData(id)) {
          return;
        }
        switch (type) {
          case 'JpegStream':
            imageData = data[3];
            return new Promise((resolve, reject) => {
              const img = new Image();
              img.onload = function () {
                resolve(img);
              };
              img.onerror = function () {
                reject(new Error('Error during JPEG image loading'));
              };
              img.src = imageData;
            }).then(img => {
              pageProxy.objs.resolve(id, img);
            });
          case 'Image':
            imageData = data[3];
            pageProxy.objs.resolve(id, imageData);
            var MAX_IMAGE_SIZE_TO_STORE = 8000000;
            if (imageData && 'data' in imageData && imageData.data.length > MAX_IMAGE_SIZE_TO_STORE) {
              pageProxy.cleanupAfterRender = true;
            }
            break;
          default:
            throw new Error(`Got unknown object type ${type}`);
        }
      }, this);
      messageHandler.on('DocProgress', function transportDocProgress(data) {
        if (this.destroyed) {
          return;
        }
        var loadingTask = this.loadingTask;
        if (loadingTask.onProgress) {
          loadingTask.onProgress({
            loaded: data.loaded,
            total: data.total
          });
        }
      }, this);
      messageHandler.on('PageError', function transportError(data) {
        if (this.destroyed) {
          return;
        }
        var page = this.pageCache[data.pageNum - 1];
        var intentState = page.intentStates[data.intent];
        if (intentState.displayReadyCapability) {
          intentState.displayReadyCapability.reject(data.error);
        } else {
          throw new Error(data.error);
        }
        if (intentState.operatorList) {
          intentState.operatorList.lastChunk = true;
          for (var i = 0; i < intentState.renderTasks.length; i++) {
            intentState.renderTasks[i].operatorListChanged();
          }
        }
      }, this);
      messageHandler.on('UnsupportedFeature', function (data) {
        if (this.destroyed) {
          return;
        }
        let loadingTask = this.loadingTask;
        if (loadingTask.onUnsupportedFeature) {
          loadingTask.onUnsupportedFeature(data.featureId);
        }
      }, this);
      messageHandler.on('JpegDecode', function (data) {
        if (this.destroyed) {
          return Promise.reject(new Error('Worker was destroyed'));
        }
        if (typeof document === 'undefined') {
          return Promise.reject(new Error('"document" is not defined.'));
        }
        var imageUrl = data[0];
        var components = data[1];
        if (components !== 3 && components !== 1) {
          return Promise.reject(new Error('Only 3 components or 1 component can be returned'));
        }
        return new Promise(function (resolve, reject) {
          var img = new Image();
          img.onload = function () {
            var width = img.width;
            var height = img.height;
            var size = width * height;
            var rgbaLength = size * 4;
            var buf = new Uint8Array(size * components);
            var tmpCanvas = document.createElement('canvas');
            tmpCanvas.width = width;
            tmpCanvas.height = height;
            var tmpCtx = tmpCanvas.getContext('2d');
            tmpCtx.drawImage(img, 0, 0);
            var data = tmpCtx.getImageData(0, 0, width, height).data;
            var i, j;
            if (components === 3) {
              for (i = 0, j = 0; i < rgbaLength; i += 4, j += 3) {
                buf[j] = data[i];
                buf[j + 1] = data[i + 1];
                buf[j + 2] = data[i + 2];
              }
            } else if (components === 1) {
              for (i = 0, j = 0; i < rgbaLength; i += 4, j++) {
                buf[j] = data[i];
              }
            }
            resolve({
              data: buf,
              width,
              height
            });
          };
          img.onerror = function () {
            reject(new Error('JpegDecode failed to load image'));
          };
          img.src = imageUrl;
        });
      }, this);
      messageHandler.on('FetchBuiltInCMap', function (data) {
        if (this.destroyed) {
          return Promise.reject(new Error('Worker was destroyed'));
        }
        return this.CMapReaderFactory.fetch({ name: data.name });
      }, this);
    },
    getData: function WorkerTransport_getData() {
      return this.messageHandler.sendWithPromise('GetData', null);
    },
    getPage(pageNumber) {
      if (!Number.isInteger(pageNumber) || pageNumber <= 0 || pageNumber > this.numPages) {
        return Promise.reject(new Error('Invalid page request'));
      }
      var pageIndex = pageNumber - 1;
      if (pageIndex in this.pagePromises) {
        return this.pagePromises[pageIndex];
      }
      var promise = this.messageHandler.sendWithPromise('GetPage', { pageIndex }).then(pageInfo => {
        if (this.destroyed) {
          throw new Error('Transport destroyed');
        }
        var page = new PDFPageProxy(pageIndex, pageInfo, this);
        this.pageCache[pageIndex] = page;
        return page;
      });
      this.pagePromises[pageIndex] = promise;
      return promise;
    },
    getPageIndex: function WorkerTransport_getPageIndexByRef(ref) {
      return this.messageHandler.sendWithPromise('GetPageIndex', { ref }).catch(function (reason) {
        return Promise.reject(new Error(reason));
      });
    },
    getAnnotations: function WorkerTransport_getAnnotations(pageIndex, intent) {
      return this.messageHandler.sendWithPromise('GetAnnotations', {
        pageIndex,
        intent
      });
    },
    getDestinations: function WorkerTransport_getDestinations() {
      return this.messageHandler.sendWithPromise('GetDestinations', null);
    },
    getDestination: function WorkerTransport_getDestination(id) {
      return this.messageHandler.sendWithPromise('GetDestination', { id });
    },
    getPageLabels: function WorkerTransport_getPageLabels() {
      return this.messageHandler.sendWithPromise('GetPageLabels', null);
    },
    getPageMode() {
      return this.messageHandler.sendWithPromise('GetPageMode', null);
    },
    getAttachments: function WorkerTransport_getAttachments() {
      return this.messageHandler.sendWithPromise('GetAttachments', null);
    },
    getJavaScript: function WorkerTransport_getJavaScript() {
      return this.messageHandler.sendWithPromise('GetJavaScript', null);
    },
    getOutline: function WorkerTransport_getOutline() {
      return this.messageHandler.sendWithPromise('GetOutline', null);
    },
    getMetadata: function WorkerTransport_getMetadata() {
      return this.messageHandler.sendWithPromise('GetMetadata', null).then(results => {
        return {
          info: results[0],
          metadata: results[1] ? new _metadata.Metadata(results[1]) : null,
          contentDispositionFilename: this._fullReader ? this._fullReader.filename : null
        };
      });
    },
    getStats: function WorkerTransport_getStats() {
      return this.messageHandler.sendWithPromise('GetStats', null);
    },
    startCleanup: function WorkerTransport_startCleanup() {
      this.messageHandler.sendWithPromise('Cleanup', null).then(() => {
        for (var i = 0, ii = this.pageCache.length; i < ii; i++) {
          var page = this.pageCache[i];
          if (page) {
            page.cleanup();
          }
        }
        this.commonObjs.clear();
        this.fontLoader.clear();
      });
    }
  };
  return WorkerTransport;
}();
var PDFObjects = function PDFObjectsClosure() {
  function PDFObjects() {
    this.objs = Object.create(null);
  }
  PDFObjects.prototype = {
    ensureObj: function PDFObjects_ensureObj(objId) {
      if (this.objs[objId]) {
        return this.objs[objId];
      }
      var obj = {
        capability: (0, _util.createPromiseCapability)(),
        data: null,
        resolved: false
      };
      this.objs[objId] = obj;
      return obj;
    },
    get: function PDFObjects_get(objId, callback) {
      if (callback) {
        this.ensureObj(objId).capability.promise.then(callback);
        return null;
      }
      var obj = this.objs[objId];
      if (!obj || !obj.resolved) {
        throw new Error(`Requesting object that isn't resolved yet ${objId}`);
      }
      return obj.data;
    },
    resolve: function PDFObjects_resolve(objId, data) {
      var obj = this.ensureObj(objId);
      obj.resolved = true;
      obj.data = data;
      obj.capability.resolve(data);
    },
    isResolved: function PDFObjects_isResolved(objId) {
      var objs = this.objs;
      if (!objs[objId]) {
        return false;
      }
      return objs[objId].resolved;
    },
    hasData: function PDFObjects_hasData(objId) {
      return this.isResolved(objId);
    },
    getData: function PDFObjects_getData(objId) {
      var objs = this.objs;
      if (!objs[objId] || !objs[objId].resolved) {
        return null;
      }
      return objs[objId].data;
    },
    clear: function PDFObjects_clear() {
      this.objs = Object.create(null);
    }
  };
  return PDFObjects;
}();
var RenderTask = function RenderTaskClosure() {
  function RenderTask(internalRenderTask) {
    this._internalRenderTask = internalRenderTask;
    this.onContinue = null;
  }
  RenderTask.prototype = {
    get promise() {
      return this._internalRenderTask.capability.promise;
    },
    cancel: function RenderTask_cancel() {
      this._internalRenderTask.cancel();
    },
    then: function RenderTask_then(onFulfilled, onRejected) {
      return this.promise.then.apply(this.promise, arguments);
    }
  };
  return RenderTask;
}();
var InternalRenderTask = function InternalRenderTaskClosure() {
  let canvasInRendering = new WeakMap();
  function InternalRenderTask(callback, params, objs, commonObjs, operatorList, pageNumber, canvasFactory, webGLContext) {
    this.callback = callback;
    this.params = params;
    this.objs = objs;
    this.commonObjs = commonObjs;
    this.operatorListIdx = null;
    this.operatorList = operatorList;
    this.pageNumber = pageNumber;
    this.canvasFactory = canvasFactory;
    this.webGLContext = webGLContext;
    this.running = false;
    this.graphicsReadyCallback = null;
    this.graphicsReady = false;
    this.useRequestAnimationFrame = false;
    this.cancelled = false;
    this.capability = (0, _util.createPromiseCapability)();
    this.task = new RenderTask(this);
    this._continueBound = this._continue.bind(this);
    this._scheduleNextBound = this._scheduleNext.bind(this);
    this._nextBound = this._next.bind(this);
    this._canvas = params.canvasContext.canvas;
  }
  InternalRenderTask.prototype = {
    initializeGraphics: function InternalRenderTask_initializeGraphics(transparency) {
      if (this._canvas) {
        if (canvasInRendering.has(this._canvas)) {
          throw new Error('Cannot use the same canvas during multiple render() operations. ' + 'Use different canvas or ensure previous operations were ' + 'cancelled or completed.');
        }
        canvasInRendering.set(this._canvas, this);
      }
      if (this.cancelled) {
        return;
      }
      if ((0, _dom_utils.getDefaultSetting)('pdfBug') && _global_scope2.default.StepperManager && _global_scope2.default.StepperManager.enabled) {
        this.stepper = _global_scope2.default.StepperManager.create(this.pageNumber - 1);
        this.stepper.init(this.operatorList);
        this.stepper.nextBreakPoint = this.stepper.getNextBreakPoint();
      }
      var params = this.params;
      this.gfx = new _canvas.CanvasGraphics(params.canvasContext, this.commonObjs, this.objs, this.canvasFactory, this.webGLContext, params.imageLayer);
      this.gfx.beginDrawing({
        transform: params.transform,
        viewport: params.viewport,
        transparency,
        background: params.background
      });
      this.operatorListIdx = 0;
      this.graphicsReady = true;
      if (this.graphicsReadyCallback) {
        this.graphicsReadyCallback();
      }
    },
    cancel: function InternalRenderTask_cancel() {
      this.running = false;
      this.cancelled = true;
      if (this._canvas) {
        canvasInRendering.delete(this._canvas);
      }
      this.callback(new _dom_utils.RenderingCancelledException('Rendering cancelled, page ' + this.pageNumber, 'canvas'));
    },
    operatorListChanged: function InternalRenderTask_operatorListChanged() {
      if (!this.graphicsReady) {
        if (!this.graphicsReadyCallback) {
          this.graphicsReadyCallback = this._continueBound;
        }
        return;
      }
      if (this.stepper) {
        this.stepper.updateOperatorList(this.operatorList);
      }
      if (this.running) {
        return;
      }
      this._continue();
    },
    _continue: function InternalRenderTask__continue() {
      this.running = true;
      if (this.cancelled) {
        return;
      }
      if (this.task.onContinue) {
        this.task.onContinue(this._scheduleNextBound);
      } else {
        this._scheduleNext();
      }
    },
    _scheduleNext: function InternalRenderTask__scheduleNext() {
      if (this.useRequestAnimationFrame && typeof window !== 'undefined') {
        window.requestAnimationFrame(this._nextBound);
      } else {
        Promise.resolve(undefined).then(this._nextBound);
      }
    },
    _next: function InternalRenderTask__next() {
      if (this.cancelled) {
        return;
      }
      this.operatorListIdx = this.gfx.executeOperatorList(this.operatorList, this.operatorListIdx, this._continueBound, this.stepper);
      if (this.operatorListIdx === this.operatorList.argsArray.length) {
        this.running = false;
        if (this.operatorList.lastChunk) {
          this.gfx.endDrawing();
          if (this._canvas) {
            canvasInRendering.delete(this._canvas);
          }
          this.callback();
        }
      }
    }
  };
  return InternalRenderTask;
}();
var version, build;
{
  exports.version = version = '2.0.402';
  exports.build = build = '401f3a9d';
}
exports.getDocument = getDocument;
exports.LoopbackPort = LoopbackPort;
exports.PDFDataRangeTransport = PDFDataRangeTransport;
exports.PDFWorker = PDFWorker;
exports.PDFDocumentProxy = PDFDocumentProxy;
exports.PDFPageProxy = PDFPageProxy;
exports.setPDFNetworkStreamFactory = setPDFNetworkStreamFactory;
exports.version = version;
exports.build = build;

/***/ }),
/* 5 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.Metadata = undefined;

var _util = __w_pdfjs_require__(0);

var _dom_utils = __w_pdfjs_require__(1);

class Metadata {
  constructor(data) {
    (0, _util.assert)(typeof data === 'string', 'Metadata: input is not a string');
    data = this._repair(data);
    let parser = new _dom_utils.SimpleXMLParser();
    data = parser.parseFromString(data);
    this._metadata = Object.create(null);
    this._parse(data);
  }
  _repair(data) {
    return data.replace(/>\\376\\377([^<]+)/g, function (all, codes) {
      let bytes = codes.replace(/\\([0-3])([0-7])([0-7])/g, function (code, d1, d2, d3) {
        return String.fromCharCode(d1 * 64 + d2 * 8 + d3 * 1);
      }).replace(/&(amp|apos|gt|lt|quot);/g, function (str, name) {
        switch (name) {
          case 'amp':
            return '&';
          case 'apos':
            return '\'';
          case 'gt':
            return '>';
          case 'lt':
            return '<';
          case 'quot':
            return '\"';
        }
        throw new Error(`_repair: ${name} isn't defined.`);
      });
      let chars = '';
      for (let i = 0, ii = bytes.length; i < ii; i += 2) {
        let code = bytes.charCodeAt(i) * 256 + bytes.charCodeAt(i + 1);
        if (code >= 32 && code < 127 && code !== 60 && code !== 62 && code !== 38) {
          chars += String.fromCharCode(code);
        } else {
          chars += '&#x' + (0x10000 + code).toString(16).substring(1) + ';';
        }
      }
      return '>' + chars;
    });
  }
  _parse(domDocument) {
    let rdf = domDocument.documentElement;
    if (rdf.nodeName.toLowerCase() !== 'rdf:rdf') {
      rdf = rdf.firstChild;
      while (rdf && rdf.nodeName.toLowerCase() !== 'rdf:rdf') {
        rdf = rdf.nextSibling;
      }
    }
    let nodeName = rdf ? rdf.nodeName.toLowerCase() : null;
    if (!rdf || nodeName !== 'rdf:rdf' || !rdf.hasChildNodes()) {
      return;
    }
    let children = rdf.childNodes;
    for (let i = 0, ii = children.length; i < ii; i++) {
      let desc = children[i];
      if (desc.nodeName.toLowerCase() !== 'rdf:description') {
        continue;
      }
      for (let j = 0, jj = desc.childNodes.length; j < jj; j++) {
        if (desc.childNodes[j].nodeName.toLowerCase() !== '#text') {
          let entry = desc.childNodes[j];
          let name = entry.nodeName.toLowerCase();
          this._metadata[name] = entry.textContent.trim();
        }
      }
    }
  }
  get(name) {
    return this._metadata[name] || null;
  }
  getAll() {
    return this._metadata;
  }
  has(name) {
    return typeof this._metadata[name] !== 'undefined';
  }
}
exports.Metadata = Metadata;

/***/ }),
/* 6 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.AnnotationLayer = undefined;

var _dom_utils = __w_pdfjs_require__(1);

var _util = __w_pdfjs_require__(0);

class AnnotationElementFactory {
  static create(parameters) {
    let subtype = parameters.data.annotationType;
    switch (subtype) {
      case _util.AnnotationType.LINK:
        return new LinkAnnotationElement(parameters);
      case _util.AnnotationType.TEXT:
        return new TextAnnotationElement(parameters);
      case _util.AnnotationType.WIDGET:
        let fieldType = parameters.data.fieldType;
        switch (fieldType) {
          case 'Tx':
            return new TextWidgetAnnotationElement(parameters);
          case 'Btn':
            if (parameters.data.radioButton) {
              return new RadioButtonWidgetAnnotationElement(parameters);
            } else if (parameters.data.checkBox) {
              return new CheckboxWidgetAnnotationElement(parameters);
            }
            return new PushButtonWidgetAnnotationElement(parameters);
          case 'Ch':
            return new ChoiceWidgetAnnotationElement(parameters);
        }
        return new WidgetAnnotationElement(parameters);
      case _util.AnnotationType.POPUP:
        return new PopupAnnotationElement(parameters);
      case _util.AnnotationType.LINE:
        return new LineAnnotationElement(parameters);
      case _util.AnnotationType.SQUARE:
        return new SquareAnnotationElement(parameters);
      case _util.AnnotationType.CIRCLE:
        return new CircleAnnotationElement(parameters);
      case _util.AnnotationType.POLYLINE:
        return new PolylineAnnotationElement(parameters);
      case _util.AnnotationType.POLYGON:
        return new PolygonAnnotationElement(parameters);
      case _util.AnnotationType.HIGHLIGHT:
        return new HighlightAnnotationElement(parameters);
      case _util.AnnotationType.UNDERLINE:
        return new UnderlineAnnotationElement(parameters);
      case _util.AnnotationType.SQUIGGLY:
        return new SquigglyAnnotationElement(parameters);
      case _util.AnnotationType.STRIKEOUT:
        return new StrikeOutAnnotationElement(parameters);
      case _util.AnnotationType.STAMP:
        return new StampAnnotationElement(parameters);
      case _util.AnnotationType.FILEATTACHMENT:
        return new FileAttachmentAnnotationElement(parameters);
      default:
        return new AnnotationElement(parameters);
    }
  }
}
class AnnotationElement {
  constructor(parameters, isRenderable = false, ignoreBorder = false) {
    this.isRenderable = isRenderable;
    this.data = parameters.data;
    this.layer = parameters.layer;
    this.page = parameters.page;
    this.viewport = parameters.viewport;
    this.linkService = parameters.linkService;
    this.downloadManager = parameters.downloadManager;
    this.imageResourcesPath = parameters.imageResourcesPath;
    this.renderInteractiveForms = parameters.renderInteractiveForms;
    this.svgFactory = parameters.svgFactory;
    if (isRenderable) {
      this.container = this._createContainer(ignoreBorder);
    }
  }
  _createContainer(ignoreBorder = false) {
    let data = this.data,
        page = this.page,
        viewport = this.viewport;
    let container = document.createElement('section');
    let width = data.rect[2] - data.rect[0];
    let height = data.rect[3] - data.rect[1];
    container.setAttribute('data-annotation-id', data.id);
    let rect = _util.Util.normalizeRect([data.rect[0], page.view[3] - data.rect[1] + page.view[1], data.rect[2], page.view[3] - data.rect[3] + page.view[1]]);
    container.style.transform = 'matrix(' + viewport.transform.join(',') + ')';
    container.style.transformOrigin = -rect[0] + 'px ' + -rect[1] + 'px';
    if (!ignoreBorder && data.borderStyle.width > 0) {
      container.style.borderWidth = data.borderStyle.width + 'px';
      if (data.borderStyle.style !== _util.AnnotationBorderStyleType.UNDERLINE) {
        width = width - 2 * data.borderStyle.width;
        height = height - 2 * data.borderStyle.width;
      }
      let horizontalRadius = data.borderStyle.horizontalCornerRadius;
      let verticalRadius = data.borderStyle.verticalCornerRadius;
      if (horizontalRadius > 0 || verticalRadius > 0) {
        let radius = horizontalRadius + 'px / ' + verticalRadius + 'px';
        container.style.borderRadius = radius;
      }
      switch (data.borderStyle.style) {
        case _util.AnnotationBorderStyleType.SOLID:
          container.style.borderStyle = 'solid';
          break;
        case _util.AnnotationBorderStyleType.DASHED:
          container.style.borderStyle = 'dashed';
          break;
        case _util.AnnotationBorderStyleType.BEVELED:
          (0, _util.warn)('Unimplemented border style: beveled');
          break;
        case _util.AnnotationBorderStyleType.INSET:
          (0, _util.warn)('Unimplemented border style: inset');
          break;
        case _util.AnnotationBorderStyleType.UNDERLINE:
          container.style.borderBottomStyle = 'solid';
          break;
        default:
          break;
      }
      if (data.color) {
        container.style.borderColor = _util.Util.makeCssRgb(data.color[0] | 0, data.color[1] | 0, data.color[2] | 0);
      } else {
        container.style.borderWidth = 0;
      }
    }
    container.style.left = rect[0] + 'px';
    container.style.top = rect[1] + 'px';
    container.style.width = width + 'px';
    container.style.height = height + 'px';
    return container;
  }
  _createPopup(container, trigger, data) {
    if (!trigger) {
      trigger = document.createElement('div');
      trigger.style.height = container.style.height;
      trigger.style.width = container.style.width;
      container.appendChild(trigger);
    }
    let popupElement = new PopupElement({
      container,
      trigger,
      color: data.color,
      title: data.title,
      contents: data.contents,
      hideWrapper: true
    });
    let popup = popupElement.render();
    popup.style.left = container.style.width;
    container.appendChild(popup);
  }
  render() {
    (0, _util.unreachable)('Abstract method `AnnotationElement.render` called');
  }
}
class LinkAnnotationElement extends AnnotationElement {
  constructor(parameters) {
    let isRenderable = !!(parameters.data.url || parameters.data.dest || parameters.data.action);
    super(parameters, isRenderable);
  }
  render() {
    this.container.className = 'linkAnnotation';
    let { data, linkService } = this;
    let link = document.createElement('a');
    (0, _dom_utils.addLinkAttributes)(link, {
      url: data.url,
      target: data.newWindow ? _dom_utils.LinkTarget.BLANK : linkService.externalLinkTarget,
      rel: linkService.externalLinkRel
    });
    if (!data.url) {
      if (data.action) {
        this._bindNamedAction(link, data.action);
      } else {
        this._bindLink(link, data.dest);
      }
    }
    this.container.appendChild(link);
    return this.container;
  }
  _bindLink(link, destination) {
    link.href = this.linkService.getDestinationHash(destination);
    link.onclick = () => {
      if (destination) {
        this.linkService.navigateTo(destination);
      }
      return false;
    };
    if (destination) {
      link.className = 'internalLink';
    }
  }
  _bindNamedAction(link, action) {
    link.href = this.linkService.getAnchorUrl('');
    link.onclick = () => {
      this.linkService.executeNamedAction(action);
      return false;
    };
    link.className = 'internalLink';
  }
}
class TextAnnotationElement extends AnnotationElement {
  constructor(parameters) {
    let isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents);
    super(parameters, isRenderable);
  }
  render() {
    this.container.className = 'textAnnotation';
    let image = document.createElement('img');
    image.style.height = this.container.style.height;
    image.style.width = this.container.style.width;
    image.src = this.imageResourcesPath + 'annotation-' + this.data.name.toLowerCase() + '.svg';
    image.alt = '[{{type}} Annotation]';
    image.dataset.l10nId = 'text_annotation_type';
    image.dataset.l10nArgs = JSON.stringify({ type: this.data.name });
    if (!this.data.hasPopup) {
      this._createPopup(this.container, image, this.data);
    }
    this.container.appendChild(image);
    return this.container;
  }
}
class WidgetAnnotationElement extends AnnotationElement {
  render() {
    return this.container;
  }
}
class TextWidgetAnnotationElement extends WidgetAnnotationElement {
  constructor(parameters) {
    let isRenderable = parameters.renderInteractiveForms || !parameters.data.hasAppearance && !!parameters.data.fieldValue;
    super(parameters, isRenderable);
  }
  render() {
    const TEXT_ALIGNMENT = ['left', 'center', 'right'];
    this.container.className = 'textWidgetAnnotation';
    let element = null;
    if (this.renderInteractiveForms) {
      if (this.data.multiLine) {
        element = document.createElement('textarea');
        element.textContent = this.data.fieldValue;
      } else {
        element = document.createElement('input');
        element.type = 'text';
        element.setAttribute('value', this.data.fieldValue);
      }
      element.disabled = this.data.readOnly;
      if (this.data.maxLen !== null) {
        element.maxLength = this.data.maxLen;
      }
      if (this.data.comb) {
        let fieldWidth = this.data.rect[2] - this.data.rect[0];
        let combWidth = fieldWidth / this.data.maxLen;
        element.classList.add('comb');
        element.style.letterSpacing = 'calc(' + combWidth + 'px - 1ch)';
      }
    } else {
      element = document.createElement('div');
      element.textContent = this.data.fieldValue;
      element.style.verticalAlign = 'middle';
      element.style.display = 'table-cell';
      let font = null;
      if (this.data.fontRefName) {
        font = this.page.commonObjs.getData(this.data.fontRefName);
      }
      this._setTextStyle(element, font);
    }
    if (this.data.textAlignment !== null) {
      element.style.textAlign = TEXT_ALIGNMENT[this.data.textAlignment];
    }
    this.container.appendChild(element);
    return this.container;
  }
  _setTextStyle(element, font) {
    let style = element.style;
    style.fontSize = this.data.fontSize + 'px';
    style.direction = this.data.fontDirection < 0 ? 'rtl' : 'ltr';
    if (!font) {
      return;
    }
    style.fontWeight = font.black ? font.bold ? '900' : 'bold' : font.bold ? 'bold' : 'normal';
    style.fontStyle = font.italic ? 'italic' : 'normal';
    let fontFamily = font.loadedName ? '"' + font.loadedName + '", ' : '';
    let fallbackName = font.fallbackName || 'Helvetica, sans-serif';
    style.fontFamily = fontFamily + fallbackName;
  }
}
class CheckboxWidgetAnnotationElement extends WidgetAnnotationElement {
  constructor(parameters) {
    super(parameters, parameters.renderInteractiveForms);
  }
  render() {
    this.container.className = 'buttonWidgetAnnotation checkBox';
    let element = document.createElement('input');
    element.disabled = this.data.readOnly;
    element.type = 'checkbox';
    if (this.data.fieldValue && this.data.fieldValue !== 'Off') {
      element.setAttribute('checked', true);
    }
    this.container.appendChild(element);
    return this.container;
  }
}
class RadioButtonWidgetAnnotationElement extends WidgetAnnotationElement {
  constructor(parameters) {
    super(parameters, parameters.renderInteractiveForms);
  }
  render() {
    this.container.className = 'buttonWidgetAnnotation radioButton';
    let element = document.createElement('input');
    element.disabled = this.data.readOnly;
    element.type = 'radio';
    element.name = this.data.fieldName;
    if (this.data.fieldValue === this.data.buttonValue) {
      element.setAttribute('checked', true);
    }
    this.container.appendChild(element);
    return this.container;
  }
}
class PushButtonWidgetAnnotationElement extends LinkAnnotationElement {
  render() {
    let container = super.render();
    container.className = 'buttonWidgetAnnotation pushButton';
    return container;
  }
}
class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement {
  constructor(parameters) {
    super(parameters, parameters.renderInteractiveForms);
  }
  render() {
    this.container.className = 'choiceWidgetAnnotation';
    let selectElement = document.createElement('select');
    selectElement.disabled = this.data.readOnly;
    if (!this.data.combo) {
      selectElement.size = this.data.options.length;
      if (this.data.multiSelect) {
        selectElement.multiple = true;
      }
    }
    for (let i = 0, ii = this.data.options.length; i < ii; i++) {
      let option = this.data.options[i];
      let optionElement = document.createElement('option');
      optionElement.textContent = option.displayValue;
      optionElement.value = option.exportValue;
      if (this.data.fieldValue.includes(option.displayValue)) {
        optionElement.setAttribute('selected', true);
      }
      selectElement.appendChild(optionElement);
    }
    this.container.appendChild(selectElement);
    return this.container;
  }
}
class PopupAnnotationElement extends AnnotationElement {
  constructor(parameters) {
    let isRenderable = !!(parameters.data.title || parameters.data.contents);
    super(parameters, isRenderable);
  }
  render() {
    const IGNORE_TYPES = ['Line', 'Square', 'Circle', 'PolyLine', 'Polygon'];
    this.container.className = 'popupAnnotation';
    if (IGNORE_TYPES.includes(this.data.parentType)) {
      return this.container;
    }
    let selector = '[data-annotation-id="' + this.data.parentId + '"]';
    let parentElement = this.layer.querySelector(selector);
    if (!parentElement) {
      return this.container;
    }
    let popup = new PopupElement({
      container: this.container,
      trigger: parentElement,
      color: this.data.color,
      title: this.data.title,
      contents: this.data.contents
    });
    let parentLeft = parseFloat(parentElement.style.left);
    let parentWidth = parseFloat(parentElement.style.width);
    this.container.style.transformOrigin = -(parentLeft + parentWidth) + 'px -' + parentElement.style.top;
    this.container.style.left = parentLeft + parentWidth + 'px';
    this.container.appendChild(popup.render());
    return this.container;
  }
}
class PopupElement {
  constructor(parameters) {
    this.container = parameters.container;
    this.trigger = parameters.trigger;
    this.color = parameters.color;
    this.title = parameters.title;
    this.contents = parameters.contents;
    this.hideWrapper = parameters.hideWrapper || false;
    this.pinned = false;
  }
  render() {
    const BACKGROUND_ENLIGHT = 0.7;
    let wrapper = document.createElement('div');
    wrapper.className = 'popupWrapper';
    this.hideElement = this.hideWrapper ? wrapper : this.container;
    this.hideElement.setAttribute('hidden', true);
    let popup = document.createElement('div');
    popup.className = 'popup';
    let color = this.color;
    if (color) {
      let r = BACKGROUND_ENLIGHT * (255 - color[0]) + color[0];
      let g = BACKGROUND_ENLIGHT * (255 - color[1]) + color[1];
      let b = BACKGROUND_ENLIGHT * (255 - color[2]) + color[2];
      popup.style.backgroundColor = _util.Util.makeCssRgb(r | 0, g | 0, b | 0);
    }
    let contents = this._formatContents(this.contents);
    let title = document.createElement('h1');
    title.textContent = this.title;
    this.trigger.addEventListener('click', this._toggle.bind(this));
    this.trigger.addEventListener('mouseover', this._show.bind(this, false));
    this.trigger.addEventListener('mouseout', this._hide.bind(this, false));
    popup.addEventListener('click', this._hide.bind(this, true));
    popup.appendChild(title);
    popup.appendChild(contents);
    wrapper.appendChild(popup);
    return wrapper;
  }
  _formatContents(contents) {
    let p = document.createElement('p');
    let lines = contents.split(/(?:\r\n?|\n)/);
    for (let i = 0, ii = lines.length; i < ii; ++i) {
      let line = lines[i];
      p.appendChild(document.createTextNode(line));
      if (i < ii - 1) {
        p.appendChild(document.createElement('br'));
      }
    }
    return p;
  }
  _toggle() {
    if (this.pinned) {
      this._hide(true);
    } else {
      this._show(true);
    }
  }
  _show(pin = false) {
    if (pin) {
      this.pinned = true;
    }
    if (this.hideElement.hasAttribute('hidden')) {
      this.hideElement.removeAttribute('hidden');
      this.container.style.zIndex += 1;
    }
  }
  _hide(unpin = true) {
    if (unpin) {
      this.pinned = false;
    }
    if (!this.hideElement.hasAttribute('hidden') && !this.pinned) {
      this.hideElement.setAttribute('hidden', true);
      this.container.style.zIndex -= 1;
    }
  }
}
class LineAnnotationElement extends AnnotationElement {
  constructor(parameters) {
    let isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents);
    super(parameters, isRenderable, true);
  }
  render() {
    this.container.className = 'lineAnnotation';
    let data = this.data;
    let width = data.rect[2] - data.rect[0];
    let height = data.rect[3] - data.rect[1];
    let svg = this.svgFactory.create(width, height);
    let line = this.svgFactory.createElement('svg:line');
    line.setAttribute('x1', data.rect[2] - data.lineCoordinates[0]);
    line.setAttribute('y1', data.rect[3] - data.lineCoordinates[1]);
    line.setAttribute('x2', data.rect[2] - data.lineCoordinates[2]);
    line.setAttribute('y2', data.rect[3] - data.lineCoordinates[3]);
    line.setAttribute('stroke-width', data.borderStyle.width);
    line.setAttribute('stroke', 'transparent');
    svg.appendChild(line);
    this.container.append(svg);
    this._createPopup(this.container, line, data);
    return this.container;
  }
}
class SquareAnnotationElement extends AnnotationElement {
  constructor(parameters) {
    let isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents);
    super(parameters, isRenderable, true);
  }
  render() {
    this.container.className = 'squareAnnotation';
    let data = this.data;
    let width = data.rect[2] - data.rect[0];
    let height = data.rect[3] - data.rect[1];
    let svg = this.svgFactory.create(width, height);
    let borderWidth = data.borderStyle.width;
    let square = this.svgFactory.createElement('svg:rect');
    square.setAttribute('x', borderWidth / 2);
    square.setAttribute('y', borderWidth / 2);
    square.setAttribute('width', width - borderWidth);
    square.setAttribute('height', height - borderWidth);
    square.setAttribute('stroke-width', borderWidth);
    square.setAttribute('stroke', 'transparent');
    square.setAttribute('fill', 'none');
    svg.appendChild(square);
    this.container.append(svg);
    this._createPopup(this.container, square, data);
    return this.container;
  }
}
class CircleAnnotationElement extends AnnotationElement {
  constructor(parameters) {
    let isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents);
    super(parameters, isRenderable, true);
  }
  render() {
    this.container.className = 'circleAnnotation';
    let data = this.data;
    let width = data.rect[2] - data.rect[0];
    let height = data.rect[3] - data.rect[1];
    let svg = this.svgFactory.create(width, height);
    let borderWidth = data.borderStyle.width;
    let circle = this.svgFactory.createElement('svg:ellipse');
    circle.setAttribute('cx', width / 2);
    circle.setAttribute('cy', height / 2);
    circle.setAttribute('rx', width / 2 - borderWidth / 2);
    circle.setAttribute('ry', height / 2 - borderWidth / 2);
    circle.setAttribute('stroke-width', borderWidth);
    circle.setAttribute('stroke', 'transparent');
    circle.setAttribute('fill', 'none');
    svg.appendChild(circle);
    this.container.append(svg);
    this._createPopup(this.container, circle, data);
    return this.container;
  }
}
class PolylineAnnotationElement extends AnnotationElement {
  constructor(parameters) {
    let isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents);
    super(parameters, isRenderable, true);
    this.containerClassName = 'polylineAnnotation';
    this.svgElementName = 'svg:polyline';
  }
  render() {
    this.container.className = this.containerClassName;
    let data = this.data;
    let width = data.rect[2] - data.rect[0];
    let height = data.rect[3] - data.rect[1];
    let svg = this.svgFactory.create(width, height);
    let vertices = data.vertices;
    let points = [];
    for (let i = 0, ii = vertices.length; i < ii; i++) {
      let x = vertices[i].x - data.rect[0];
      let y = data.rect[3] - vertices[i].y;
      points.push(x + ',' + y);
    }
    points = points.join(' ');
    let borderWidth = data.borderStyle.width;
    let polyline = this.svgFactory.createElement(this.svgElementName);
    polyline.setAttribute('points', points);
    polyline.setAttribute('stroke-width', borderWidth);
    polyline.setAttribute('stroke', 'transparent');
    polyline.setAttribute('fill', 'none');
    svg.appendChild(polyline);
    this.container.append(svg);
    this._createPopup(this.container, polyline, data);
    return this.container;
  }
}
class PolygonAnnotationElement extends PolylineAnnotationElement {
  constructor(parameters) {
    super(parameters);
    this.containerClassName = 'polygonAnnotation';
    this.svgElementName = 'svg:polygon';
  }
}
class HighlightAnnotationElement extends AnnotationElement {
  constructor(parameters) {
    let isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents);
    super(parameters, isRenderable, true);
  }
  render() {
    this.container.className = 'highlightAnnotation';
    if (!this.data.hasPopup) {
      this._createPopup(this.container, null, this.data);
    }
    return this.container;
  }
}
class UnderlineAnnotationElement extends AnnotationElement {
  constructor(parameters) {
    let isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents);
    super(parameters, isRenderable, true);
  }
  render() {
    this.container.className = 'underlineAnnotation';
    if (!this.data.hasPopup) {
      this._createPopup(this.container, null, this.data);
    }
    return this.container;
  }
}
class SquigglyAnnotationElement extends AnnotationElement {
  constructor(parameters) {
    let isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents);
    super(parameters, isRenderable, true);
  }
  render() {
    this.container.className = 'squigglyAnnotation';
    if (!this.data.hasPopup) {
      this._createPopup(this.container, null, this.data);
    }
    return this.container;
  }
}
class StrikeOutAnnotationElement extends AnnotationElement {
  constructor(parameters) {
    let isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents);
    super(parameters, isRenderable, true);
  }
  render() {
    this.container.className = 'strikeoutAnnotation';
    if (!this.data.hasPopup) {
      this._createPopup(this.container, null, this.data);
    }
    return this.container;
  }
}
class StampAnnotationElement extends AnnotationElement {
  constructor(parameters) {
    let isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents);
    super(parameters, isRenderable, true);
  }
  render() {
    this.container.className = 'stampAnnotation';
    if (!this.data.hasPopup) {
      this._createPopup(this.container, null, this.data);
    }
    return this.container;
  }
}
class FileAttachmentAnnotationElement extends AnnotationElement {
  constructor(parameters) {
    super(parameters, true);
    let file = this.data.file;
    this.filename = (0, _dom_utils.getFilenameFromUrl)(file.filename);
    this.content = file.content;
    this.linkService.onFileAttachmentAnnotation({
      id: (0, _util.stringToPDFString)(file.filename),
      filename: file.filename,
      content: file.content
    });
  }
  render() {
    this.container.className = 'fileAttachmentAnnotation';
    let trigger = document.createElement('div');
    trigger.style.height = this.container.style.height;
    trigger.style.width = this.container.style.width;
    trigger.addEventListener('dblclick', this._download.bind(this));
    if (!this.data.hasPopup && (this.data.title || this.data.contents)) {
      this._createPopup(this.container, trigger, this.data);
    }
    this.container.appendChild(trigger);
    return this.container;
  }
  _download() {
    if (!this.downloadManager) {
      (0, _util.warn)('Download cannot be started due to unavailable download manager');
      return;
    }
    this.downloadManager.downloadData(this.content, this.filename, '');
  }
}
class AnnotationLayer {
  static render(parameters) {
    for (let i = 0, ii = parameters.annotations.length; i < ii; i++) {
      let data = parameters.annotations[i];
      if (!data) {
        continue;
      }
      let element = AnnotationElementFactory.create({
        data,
        layer: parameters.div,
        page: parameters.page,
        viewport: parameters.viewport,
        linkService: parameters.linkService,
        downloadManager: parameters.downloadManager,
        imageResourcesPath: parameters.imageResourcesPath || '',
        renderInteractiveForms: parameters.renderInteractiveForms || false,
        svgFactory: new _dom_utils.DOMSVGFactory()
      });
      if (element.isRenderable) {
        parameters.div.appendChild(element.render());
      }
    }
  }
  static update(parameters) {
    for (let i = 0, ii = parameters.annotations.length; i < ii; i++) {
      let data = parameters.annotations[i];
      let element = parameters.div.querySelector('[data-annotation-id="' + data.id + '"]');
      if (element) {
        element.style.transform = 'matrix(' + parameters.viewport.transform.join(',') + ')';
      }
    }
    parameters.div.removeAttribute('hidden');
  }
}
exports.AnnotationLayer = AnnotationLayer;

/***/ }),
/* 7 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.renderTextLayer = undefined;

var _util = __w_pdfjs_require__(0);

var _dom_utils = __w_pdfjs_require__(1);

var renderTextLayer = function renderTextLayerClosure() {
  var MAX_TEXT_DIVS_TO_RENDER = 100000;
  var NonWhitespaceRegexp = /\S/;
  function isAllWhitespace(str) {
    return !NonWhitespaceRegexp.test(str);
  }
  var styleBuf = ['left: ', 0, 'px; top: ', 0, 'px; font-size: ', 0, 'px; font-family: ', '', ';'];
  function appendText(task, geom, styles) {
    var textDiv = document.createElement('div');
    var textDivProperties = {
      style: null,
      angle: 0,
      canvasWidth: 0,
      isWhitespace: false,
      originalTransform: null,
      paddingBottom: 0,
      paddingLeft: 0,
      paddingRight: 0,
      paddingTop: 0,
      scale: 1
    };
    task._textDivs.push(textDiv);
    if (isAllWhitespace(geom.str)) {
      textDivProperties.isWhitespace = true;
      task._textDivProperties.set(textDiv, textDivProperties);
      return;
    }
    var tx = _util.Util.transform(task._viewport.transform, geom.transform);
    var angle = Math.atan2(tx[1], tx[0]);
    var style = styles[geom.fontName];
    if (style.vertical) {
      angle += Math.PI / 2;
    }
    var fontHeight = Math.sqrt(tx[2] * tx[2] + tx[3] * tx[3]);
    var fontAscent = fontHeight;
    if (style.ascent) {
      fontAscent = style.ascent * fontAscent;
    } else if (style.descent) {
      fontAscent = (1 + style.descent) * fontAscent;
    }
    var left;
    var top;
    if (angle === 0) {
      left = tx[4];
      top = tx[5] - fontAscent;
    } else {
      left = tx[4] + fontAscent * Math.sin(angle);
      top = tx[5] - fontAscent * Math.cos(angle);
    }
    styleBuf[1] = left;
    styleBuf[3] = top;
    styleBuf[5] = fontHeight;
    styleBuf[7] = style.fontFamily;
    textDivProperties.style = styleBuf.join('');
    textDiv.setAttribute('style', textDivProperties.style);
    textDiv.textContent = geom.str;
    if ((0, _dom_utils.getDefaultSetting)('pdfBug')) {
      textDiv.dataset.fontName = geom.fontName;
    }
    if (angle !== 0) {
      textDivProperties.angle = angle * (180 / Math.PI);
    }
    if (geom.str.length > 1) {
      if (style.vertical) {
        textDivProperties.canvasWidth = geom.height * task._viewport.scale;
      } else {
        textDivProperties.canvasWidth = geom.width * task._viewport.scale;
      }
    }
    task._textDivProperties.set(textDiv, textDivProperties);
    if (task._textContentStream) {
      task._layoutText(textDiv);
    }
    if (task._enhanceTextSelection) {
      var angleCos = 1,
          angleSin = 0;
      if (angle !== 0) {
        angleCos = Math.cos(angle);
        angleSin = Math.sin(angle);
      }
      var divWidth = (style.vertical ? geom.height : geom.width) * task._viewport.scale;
      var divHeight = fontHeight;
      var m, b;
      if (angle !== 0) {
        m = [angleCos, angleSin, -angleSin, angleCos, left, top];
        b = _util.Util.getAxialAlignedBoundingBox([0, 0, divWidth, divHeight], m);
      } else {
        b = [left, top, left + divWidth, top + divHeight];
      }
      task._bounds.push({
        left: b[0],
        top: b[1],
        right: b[2],
        bottom: b[3],
        div: textDiv,
        size: [divWidth, divHeight],
        m
      });
    }
  }
  function render(task) {
    if (task._canceled) {
      return;
    }
    var textDivs = task._textDivs;
    var capability = task._capability;
    var textDivsLength = textDivs.length;
    if (textDivsLength > MAX_TEXT_DIVS_TO_RENDER) {
      task._renderingDone = true;
      capability.resolve();
      return;
    }
    if (!task._textContentStream) {
      for (var i = 0; i < textDivsLength; i++) {
        task._layoutText(textDivs[i]);
      }
    }
    task._renderingDone = true;
    capability.resolve();
  }
  function expand(task) {
    var bounds = task._bounds;
    var viewport = task._viewport;
    var expanded = expandBounds(viewport.width, viewport.height, bounds);
    for (var i = 0; i < expanded.length; i++) {
      var div = bounds[i].div;
      var divProperties = task._textDivProperties.get(div);
      if (divProperties.angle === 0) {
        divProperties.paddingLeft = bounds[i].left - expanded[i].left;
        divProperties.paddingTop = bounds[i].top - expanded[i].top;
        divProperties.paddingRight = expanded[i].right - bounds[i].right;
        divProperties.paddingBottom = expanded[i].bottom - bounds[i].bottom;
        task._textDivProperties.set(div, divProperties);
        continue;
      }
      var e = expanded[i],
          b = bounds[i];
      var m = b.m,
          c = m[0],
          s = m[1];
      var points = [[0, 0], [0, b.size[1]], [b.size[0], 0], b.size];
      var ts = new Float64Array(64);
      points.forEach(function (p, i) {
        var t = _util.Util.applyTransform(p, m);
        ts[i + 0] = c && (e.left - t[0]) / c;
        ts[i + 4] = s && (e.top - t[1]) / s;
        ts[i + 8] = c && (e.right - t[0]) / c;
        ts[i + 12] = s && (e.bottom - t[1]) / s;
        ts[i + 16] = s && (e.left - t[0]) / -s;
        ts[i + 20] = c && (e.top - t[1]) / c;
        ts[i + 24] = s && (e.right - t[0]) / -s;
        ts[i + 28] = c && (e.bottom - t[1]) / c;
        ts[i + 32] = c && (e.left - t[0]) / -c;
        ts[i + 36] = s && (e.top - t[1]) / -s;
        ts[i + 40] = c && (e.right - t[0]) / -c;
        ts[i + 44] = s && (e.bottom - t[1]) / -s;
        ts[i + 48] = s && (e.left - t[0]) / s;
        ts[i + 52] = c && (e.top - t[1]) / -c;
        ts[i + 56] = s && (e.right - t[0]) / s;
        ts[i + 60] = c && (e.bottom - t[1]) / -c;
      });
      var findPositiveMin = function (ts, offset, count) {
        var result = 0;
        for (var i = 0; i < count; i++) {
          var t = ts[offset++];
          if (t > 0) {
            result = result ? Math.min(t, result) : t;
          }
        }
        return result;
      };
      var boxScale = 1 + Math.min(Math.abs(c), Math.abs(s));
      divProperties.paddingLeft = findPositiveMin(ts, 32, 16) / boxScale;
      divProperties.paddingTop = findPositiveMin(ts, 48, 16) / boxScale;
      divProperties.paddingRight = findPositiveMin(ts, 0, 16) / boxScale;
      divProperties.paddingBottom = findPositiveMin(ts, 16, 16) / boxScale;
      task._textDivProperties.set(div, divProperties);
    }
  }
  function expandBounds(width, height, boxes) {
    var bounds = boxes.map(function (box, i) {
      return {
        x1: box.left,
        y1: box.top,
        x2: box.right,
        y2: box.bottom,
        index: i,
        x1New: undefined,
        x2New: undefined
      };
    });
    expandBoundsLTR(width, bounds);
    var expanded = new Array(boxes.length);
    bounds.forEach(function (b) {
      var i = b.index;
      expanded[i] = {
        left: b.x1New,
        top: 0,
        right: b.x2New,
        bottom: 0
      };
    });
    boxes.map(function (box, i) {
      var e = expanded[i],
          b = bounds[i];
      b.x1 = box.top;
      b.y1 = width - e.right;
      b.x2 = box.bottom;
      b.y2 = width - e.left;
      b.index = i;
      b.x1New = undefined;
      b.x2New = undefined;
    });
    expandBoundsLTR(height, bounds);
    bounds.forEach(function (b) {
      var i = b.index;
      expanded[i].top = b.x1New;
      expanded[i].bottom = b.x2New;
    });
    return expanded;
  }
  function expandBoundsLTR(width, bounds) {
    bounds.sort(function (a, b) {
      return a.x1 - b.x1 || a.index - b.index;
    });
    var fakeBoundary = {
      x1: -Infinity,
      y1: -Infinity,
      x2: 0,
      y2: Infinity,
      index: -1,
      x1New: 0,
      x2New: 0
    };
    var horizon = [{
      start: -Infinity,
      end: Infinity,
      boundary: fakeBoundary
    }];
    bounds.forEach(function (boundary) {
      var i = 0;
      while (i < horizon.length && horizon[i].end <= boundary.y1) {
        i++;
      }
      var j = horizon.length - 1;
      while (j >= 0 && horizon[j].start >= boundary.y2) {
        j--;
      }
      var horizonPart, affectedBoundary;
      var q,
          k,
          maxXNew = -Infinity;
      for (q = i; q <= j; q++) {
        horizonPart = horizon[q];
        affectedBoundary = horizonPart.boundary;
        var xNew;
        if (affectedBoundary.x2 > boundary.x1) {
          xNew = affectedBoundary.index > boundary.index ? affectedBoundary.x1New : boundary.x1;
        } else if (affectedBoundary.x2New === undefined) {
          xNew = (affectedBoundary.x2 + boundary.x1) / 2;
        } else {
          xNew = affectedBoundary.x2New;
        }
        if (xNew > maxXNew) {
          maxXNew = xNew;
        }
      }
      boundary.x1New = maxXNew;
      for (q = i; q <= j; q++) {
        horizonPart = horizon[q];
        affectedBoundary = horizonPart.boundary;
        if (affectedBoundary.x2New === undefined) {
          if (affectedBoundary.x2 > boundary.x1) {
            if (affectedBoundary.index > boundary.index) {
              affectedBoundary.x2New = affectedBoundary.x2;
            }
          } else {
            affectedBoundary.x2New = maxXNew;
          }
        } else if (affectedBoundary.x2New > maxXNew) {
          affectedBoundary.x2New = Math.max(maxXNew, affectedBoundary.x2);
        }
      }
      var changedHorizon = [],
          lastBoundary = null;
      for (q = i; q <= j; q++) {
        horizonPart = horizon[q];
        affectedBoundary = horizonPart.boundary;
        var useBoundary = affectedBoundary.x2 > boundary.x2 ? affectedBoundary : boundary;
        if (lastBoundary === useBoundary) {
          changedHorizon[changedHorizon.length - 1].end = horizonPart.end;
        } else {
          changedHorizon.push({
            start: horizonPart.start,
            end: horizonPart.end,
            boundary: useBoundary
          });
          lastBoundary = useBoundary;
        }
      }
      if (horizon[i].start < boundary.y1) {
        changedHorizon[0].start = boundary.y1;
        changedHorizon.unshift({
          start: horizon[i].start,
          end: boundary.y1,
          boundary: horizon[i].boundary
        });
      }
      if (boundary.y2 < horizon[j].end) {
        changedHorizon[changedHorizon.length - 1].end = boundary.y2;
        changedHorizon.push({
          start: boundary.y2,
          end: horizon[j].end,
          boundary: horizon[j].boundary
        });
      }
      for (q = i; q <= j; q++) {
        horizonPart = horizon[q];
        affectedBoundary = horizonPart.boundary;
        if (affectedBoundary.x2New !== undefined) {
          continue;
        }
        var used = false;
        for (k = i - 1; !used && k >= 0 && horizon[k].start >= affectedBoundary.y1; k--) {
          used = horizon[k].boundary === affectedBoundary;
        }
        for (k = j + 1; !used && k < horizon.length && horizon[k].end <= affectedBoundary.y2; k++) {
          used = horizon[k].boundary === affectedBoundary;
        }
        for (k = 0; !used && k < changedHorizon.length; k++) {
          used = changedHorizon[k].boundary === affectedBoundary;
        }
        if (!used) {
          affectedBoundary.x2New = maxXNew;
        }
      }
      Array.prototype.splice.apply(horizon, [i, j - i + 1].concat(changedHorizon));
    });
    horizon.forEach(function (horizonPart) {
      var affectedBoundary = horizonPart.boundary;
      if (affectedBoundary.x2New === undefined) {
        affectedBoundary.x2New = Math.max(width, affectedBoundary.x2);
      }
    });
  }
  function TextLayerRenderTask({ textContent, textContentStream, container, viewport, textDivs, textContentItemsStr, enhanceTextSelection }) {
    this._textContent = textContent;
    this._textContentStream = textContentStream;
    this._container = container;
    this._viewport = viewport;
    this._textDivs = textDivs || [];
    this._textContentItemsStr = textContentItemsStr || [];
    this._enhanceTextSelection = !!enhanceTextSelection;
    this._reader = null;
    this._layoutTextLastFontSize = null;
    this._layoutTextLastFontFamily = null;
    this._layoutTextCtx = null;
    this._textDivProperties = new WeakMap();
    this._renderingDone = false;
    this._canceled = false;
    this._capability = (0, _util.createPromiseCapability)();
    this._renderTimer = null;
    this._bounds = [];
  }
  TextLayerRenderTask.prototype = {
    get promise() {
      return this._capability.promise;
    },
    cancel: function TextLayer_cancel() {
      if (this._reader) {
        this._reader.cancel(new _util.AbortException('text layer task cancelled'));
        this._reader = null;
      }
      this._canceled = true;
      if (this._renderTimer !== null) {
        clearTimeout(this._renderTimer);
        this._renderTimer = null;
      }
      this._capability.reject('canceled');
    },
    _processItems(items, styleCache) {
      for (let i = 0, len = items.length; i < len; i++) {
        this._textContentItemsStr.push(items[i].str);
        appendText(this, items[i], styleCache);
      }
    },
    _layoutText(textDiv) {
      let textLayerFrag = this._container;
      let textDivProperties = this._textDivProperties.get(textDiv);
      if (textDivProperties.isWhitespace) {
        return;
      }
      let fontSize = textDiv.style.fontSize;
      let fontFamily = textDiv.style.fontFamily;
      if (fontSize !== this._layoutTextLastFontSize || fontFamily !== this._layoutTextLastFontFamily) {
        this._layoutTextCtx.font = fontSize + ' ' + fontFamily;
        this._lastFontSize = fontSize;
        this._lastFontFamily = fontFamily;
      }
      let width = this._layoutTextCtx.measureText(textDiv.textContent).width;
      let transform = '';
      if (textDivProperties.canvasWidth !== 0 && width > 0) {
        textDivProperties.scale = textDivProperties.canvasWidth / width;
        transform = 'scaleX(' + textDivProperties.scale + ')';
      }
      if (textDivProperties.angle !== 0) {
        transform = 'rotate(' + textDivProperties.angle + 'deg) ' + transform;
      }
      if (transform !== '') {
        textDivProperties.originalTransform = transform;
        textDiv.style.transform = transform;
      }
      this._textDivProperties.set(textDiv, textDivProperties);
      textLayerFrag.appendChild(textDiv);
    },
    _render: function TextLayer_render(timeout) {
      let capability = (0, _util.createPromiseCapability)();
      let styleCache = Object.create(null);
      let canvas = document.createElement('canvas');
      canvas.mozOpaque = true;
      this._layoutTextCtx = canvas.getContext('2d', { alpha: false });
      if (this._textContent) {
        let textItems = this._textContent.items;
        let textStyles = this._textContent.styles;
        this._processItems(textItems, textStyles);
        capability.resolve();
      } else if (this._textContentStream) {
        let pump = () => {
          this._reader.read().then(({ value, done }) => {
            if (done) {
              capability.resolve();
              return;
            }
            _util.Util.extendObj(styleCache, value.styles);
            this._processItems(value.items, styleCache);
            pump();
          }, capability.reject);
        };
        this._reader = this._textContentStream.getReader();
        pump();
      } else {
        throw new Error('Neither "textContent" nor "textContentStream"' + ' parameters specified.');
      }
      capability.promise.then(() => {
        styleCache = null;
        if (!timeout) {
          render(this);
        } else {
          this._renderTimer = setTimeout(() => {
            render(this);
            this._renderTimer = null;
          }, timeout);
        }
      }, this._capability.reject);
    },
    expandTextDivs: function TextLayer_expandTextDivs(expandDivs) {
      if (!this._enhanceTextSelection || !this._renderingDone) {
        return;
      }
      if (this._bounds !== null) {
        expand(this);
        this._bounds = null;
      }
      for (var i = 0, ii = this._textDivs.length; i < ii; i++) {
        var div = this._textDivs[i];
        var divProperties = this._textDivProperties.get(div);
        if (divProperties.isWhitespace) {
          continue;
        }
        if (expandDivs) {
          var transform = '',
              padding = '';
          if (divProperties.scale !== 1) {
            transform = 'scaleX(' + divProperties.scale + ')';
          }
          if (divProperties.angle !== 0) {
            transform = 'rotate(' + divProperties.angle + 'deg) ' + transform;
          }
          if (divProperties.paddingLeft !== 0) {
            padding += ' padding-left: ' + divProperties.paddingLeft / divProperties.scale + 'px;';
            transform += ' translateX(' + -divProperties.paddingLeft / divProperties.scale + 'px)';
          }
          if (divProperties.paddingTop !== 0) {
            padding += ' padding-top: ' + divProperties.paddingTop + 'px;';
            transform += ' translateY(' + -divProperties.paddingTop + 'px)';
          }
          if (divProperties.paddingRight !== 0) {
            padding += ' padding-right: ' + divProperties.paddingRight / divProperties.scale + 'px;';
          }
          if (divProperties.paddingBottom !== 0) {
            padding += ' padding-bottom: ' + divProperties.paddingBottom + 'px;';
          }
          if (padding !== '') {
            div.setAttribute('style', divProperties.style + padding);
          }
          if (transform !== '') {
            div.style.transform = transform;
          }
        } else {
          div.style.padding = 0;
          div.style.transform = divProperties.originalTransform || '';
        }
      }
    }
  };
  function renderTextLayer(renderParameters) {
    var task = new TextLayerRenderTask({
      textContent: renderParameters.textContent,
      textContentStream: renderParameters.textContentStream,
      container: renderParameters.container,
      viewport: renderParameters.viewport,
      textDivs: renderParameters.textDivs,
      textContentItemsStr: renderParameters.textContentItemsStr,
      enhanceTextSelection: renderParameters.enhanceTextSelection
    });
    task._render(renderParameters.timeout);
    return task;
  }
  return renderTextLayer;
}();
exports.renderTextLayer = renderTextLayer;

/***/ }),
/* 8 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.SVGGraphics = undefined;

var _util = __w_pdfjs_require__(0);

var _dom_utils = __w_pdfjs_require__(1);

var _is_node = __w_pdfjs_require__(19);

var _is_node2 = _interopRequireDefault(_is_node);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

var SVGGraphics = function () {
  throw new Error('Not implemented: SVGGraphics');
};
;
exports.SVGGraphics = SVGGraphics;

/***/ }),
/* 9 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


var pdfjsVersion = '2.0.402';
var pdfjsBuild = '401f3a9d';
var pdfjsSharedUtil = __w_pdfjs_require__(0);
var pdfjsDisplayGlobal = __w_pdfjs_require__(13);
var pdfjsDisplayAPI = __w_pdfjs_require__(4);
var pdfjsDisplayTextLayer = __w_pdfjs_require__(7);
var pdfjsDisplayAnnotationLayer = __w_pdfjs_require__(6);
var pdfjsDisplayDOMUtils = __w_pdfjs_require__(1);
var pdfjsDisplaySVG = __w_pdfjs_require__(8);
let pdfjsDisplayWorkerOptions = __w_pdfjs_require__(3);
;
exports.PDFJS = pdfjsDisplayGlobal.PDFJS;
exports.build = pdfjsDisplayAPI.build;
exports.version = pdfjsDisplayAPI.version;
exports.getDocument = pdfjsDisplayAPI.getDocument;
exports.LoopbackPort = pdfjsDisplayAPI.LoopbackPort;
exports.PDFDataRangeTransport = pdfjsDisplayAPI.PDFDataRangeTransport;
exports.PDFWorker = pdfjsDisplayAPI.PDFWorker;
exports.renderTextLayer = pdfjsDisplayTextLayer.renderTextLayer;
exports.AnnotationLayer = pdfjsDisplayAnnotationLayer.AnnotationLayer;
exports.createPromiseCapability = pdfjsSharedUtil.createPromiseCapability;
exports.PasswordResponses = pdfjsSharedUtil.PasswordResponses;
exports.InvalidPDFException = pdfjsSharedUtil.InvalidPDFException;
exports.MissingPDFException = pdfjsSharedUtil.MissingPDFException;
exports.SVGGraphics = pdfjsDisplaySVG.SVGGraphics;
exports.NativeImageDecoding = pdfjsSharedUtil.NativeImageDecoding;
exports.UnexpectedResponseException = pdfjsSharedUtil.UnexpectedResponseException;
exports.OPS = pdfjsSharedUtil.OPS;
exports.VerbosityLevel = pdfjsSharedUtil.VerbosityLevel;
exports.UNSUPPORTED_FEATURES = pdfjsSharedUtil.UNSUPPORTED_FEATURES;
exports.createValidAbsoluteUrl = pdfjsSharedUtil.createValidAbsoluteUrl;
exports.createObjectURL = pdfjsSharedUtil.createObjectURL;
exports.removeNullCharacters = pdfjsSharedUtil.removeNullCharacters;
exports.shadow = pdfjsSharedUtil.shadow;
exports.createBlob = pdfjsSharedUtil.createBlob;
exports.RenderingCancelledException = pdfjsDisplayDOMUtils.RenderingCancelledException;
exports.getFilenameFromUrl = pdfjsDisplayDOMUtils.getFilenameFromUrl;
exports.LinkTarget = pdfjsDisplayDOMUtils.LinkTarget;
exports.addLinkAttributes = pdfjsDisplayDOMUtils.addLinkAttributes;
exports.GlobalWorkerOptions = pdfjsDisplayWorkerOptions.GlobalWorkerOptions;

/***/ }),
/* 10 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


;

/***/ }),
/* 11 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


let isReadableStreamSupported = false;
if (typeof ReadableStream !== 'undefined') {
  try {
    new ReadableStream({
      start(controller) {
        controller.close();
      }
    });
    isReadableStreamSupported = true;
  } catch (e) {}
}
if (isReadableStreamSupported) {
  exports.ReadableStream = ReadableStream;
} else {
  exports.ReadableStream = __w_pdfjs_require__(12).ReadableStream;
}

/***/ }),
/* 12 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


(function (e, a) {
  for (var i in a) e[i] = a[i];
})(exports, function (modules) {
  var installedModules = {};
  function __w_pdfjs_require__(moduleId) {
    if (installedModules[moduleId]) return installedModules[moduleId].exports;
    var module = installedModules[moduleId] = {
      i: moduleId,
      l: false,
      exports: {}
    };
    modules[moduleId].call(module.exports, module, module.exports, __w_pdfjs_require__);
    module.l = true;
    return module.exports;
  }
  __w_pdfjs_require__.m = modules;
  __w_pdfjs_require__.c = installedModules;
  __w_pdfjs_require__.i = function (value) {
    return value;
  };
  __w_pdfjs_require__.d = function (exports, name, getter) {
    if (!__w_pdfjs_require__.o(exports, name)) {
      Object.defineProperty(exports, name, {
        configurable: false,
        enumerable: true,
        get: getter
      });
    }
  };
  __w_pdfjs_require__.n = function (module) {
    var getter = module && module.__esModule ? function getDefault() {
      return module['default'];
    } : function getModuleExports() {
      return module;
    };
    __w_pdfjs_require__.d(getter, 'a', getter);
    return getter;
  };
  __w_pdfjs_require__.o = function (object, property) {
    return Object.prototype.hasOwnProperty.call(object, property);
  };
  __w_pdfjs_require__.p = "";
  return __w_pdfjs_require__(__w_pdfjs_require__.s = 7);
}([function (module, exports, __w_pdfjs_require__) {
  "use strict";

  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;
  };
  var _require = __w_pdfjs_require__(1),
      assert = _require.assert;
  function IsPropertyKey(argument) {
    return typeof argument === 'string' || (typeof argument === 'undefined' ? 'undefined' : _typeof(argument)) === 'symbol';
  }
  exports.typeIsObject = function (x) {
    return (typeof x === 'undefined' ? 'undefined' : _typeof(x)) === 'object' && x !== null || typeof x === 'function';
  };
  exports.createDataProperty = function (o, p, v) {
    assert(exports.typeIsObject(o));
    Object.defineProperty(o, p, {
      value: v,
      writable: true,
      enumerable: true,
      configurable: true
    });
  };
  exports.createArrayFromList = function (elements) {
    return elements.slice();
  };
  exports.ArrayBufferCopy = function (dest, destOffset, src, srcOffset, n) {
    new Uint8Array(dest).set(new Uint8Array(src, srcOffset, n), destOffset);
  };
  exports.CreateIterResultObject = function (value, done) {
    assert(typeof done === 'boolean');
    var obj = {};
    Object.defineProperty(obj, 'value', {
      value: value,
      enumerable: true,
      writable: true,
      configurable: true
    });
    Object.defineProperty(obj, 'done', {
      value: done,
      enumerable: true,
      writable: true,
      configurable: true
    });
    return obj;
  };
  exports.IsFiniteNonNegativeNumber = function (v) {
    if (Number.isNaN(v)) {
      return false;
    }
    if (v === Infinity) {
      return false;
    }
    if (v < 0) {
      return false;
    }
    return true;
  };
  function Call(F, V, args) {
    if (typeof F !== 'function') {
      throw new TypeError('Argument is not a function');
    }
    return Function.prototype.apply.call(F, V, args);
  }
  exports.InvokeOrNoop = function (O, P, args) {
    assert(O !== undefined);
    assert(IsPropertyKey(P));
    assert(Array.isArray(args));
    var method = O[P];
    if (method === undefined) {
      return undefined;
    }
    return Call(method, O, args);
  };
  exports.PromiseInvokeOrNoop = function (O, P, args) {
    assert(O !== undefined);
    assert(IsPropertyKey(P));
    assert(Array.isArray(args));
    try {
      return Promise.resolve(exports.InvokeOrNoop(O, P, args));
    } catch (returnValueE) {
      return Promise.reject(returnValueE);
    }
  };
  exports.PromiseInvokeOrPerformFallback = function (O, P, args, F, argsF) {
    assert(O !== undefined);
    assert(IsPropertyKey(P));
    assert(Array.isArray(args));
    assert(Array.isArray(argsF));
    var method = void 0;
    try {
      method = O[P];
    } catch (methodE) {
      return Promise.reject(methodE);
    }
    if (method === undefined) {
      return F.apply(null, argsF);
    }
    try {
      return Promise.resolve(Call(method, O, args));
    } catch (e) {
      return Promise.reject(e);
    }
  };
  exports.TransferArrayBuffer = function (O) {
    return O.slice();
  };
  exports.ValidateAndNormalizeHighWaterMark = function (highWaterMark) {
    highWaterMark = Number(highWaterMark);
    if (Number.isNaN(highWaterMark) || highWaterMark < 0) {
      throw new RangeError('highWaterMark property of a queuing strategy must be non-negative and non-NaN');
    }
    return highWaterMark;
  };
  exports.ValidateAndNormalizeQueuingStrategy = function (size, highWaterMark) {
    if (size !== undefined && typeof size !== 'function') {
      throw new TypeError('size property of a queuing strategy must be a function');
    }
    highWaterMark = exports.ValidateAndNormalizeHighWaterMark(highWaterMark);
    return {
      size: size,
      highWaterMark: highWaterMark
    };
  };
}, function (module, exports, __w_pdfjs_require__) {
  "use strict";

  function rethrowAssertionErrorRejection(e) {
    if (e && e.constructor === AssertionError) {
      setTimeout(function () {
        throw e;
      }, 0);
    }
  }
  function AssertionError(message) {
    this.name = 'AssertionError';
    this.message = message || '';
    this.stack = new Error().stack;
  }
  AssertionError.prototype = Object.create(Error.prototype);
  AssertionError.prototype.constructor = AssertionError;
  function assert(value, message) {
    if (!value) {
      throw new AssertionError(message);
    }
  }
  module.exports = {
    rethrowAssertionErrorRejection: rethrowAssertionErrorRejection,
    AssertionError: AssertionError,
    assert: assert
  };
}, function (module, exports, __w_pdfjs_require__) {
  "use strict";

  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");
    }
  }
  var _require = __w_pdfjs_require__(0),
      InvokeOrNoop = _require.InvokeOrNoop,
      PromiseInvokeOrNoop = _require.PromiseInvokeOrNoop,
      ValidateAndNormalizeQueuingStrategy = _require.ValidateAndNormalizeQueuingStrategy,
      typeIsObject = _require.typeIsObject;
  var _require2 = __w_pdfjs_require__(1),
      assert = _require2.assert,
      rethrowAssertionErrorRejection = _require2.rethrowAssertionErrorRejection;
  var _require3 = __w_pdfjs_require__(3),
      DequeueValue = _require3.DequeueValue,
      EnqueueValueWithSize = _require3.EnqueueValueWithSize,
      PeekQueueValue = _require3.PeekQueueValue,
      ResetQueue = _require3.ResetQueue;
  var WritableStream = function () {
    function WritableStream() {
      var underlyingSink = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
          size = _ref.size,
          _ref$highWaterMark = _ref.highWaterMark,
          highWaterMark = _ref$highWaterMark === undefined ? 1 : _ref$highWaterMark;
      _classCallCheck(this, WritableStream);
      this._state = 'writable';
      this._storedError = undefined;
      this._writer = undefined;
      this._writableStreamController = undefined;
      this._writeRequests = [];
      this._inFlightWriteRequest = undefined;
      this._closeRequest = undefined;
      this._inFlightCloseRequest = undefined;
      this._pendingAbortRequest = undefined;
      this._backpressure = false;
      var type = underlyingSink.type;
      if (type !== undefined) {
        throw new RangeError('Invalid type is specified');
      }
      this._writableStreamController = new WritableStreamDefaultController(this, underlyingSink, size, highWaterMark);
      this._writableStreamController.__startSteps();
    }
    _createClass(WritableStream, [{
      key: 'abort',
      value: function abort(reason) {
        if (IsWritableStream(this) === false) {
          return Promise.reject(streamBrandCheckException('abort'));
        }
        if (IsWritableStreamLocked(this) === true) {
          return Promise.reject(new TypeError('Cannot abort a stream that already has a writer'));
        }
        return WritableStreamAbort(this, reason);
      }
    }, {
      key: 'getWriter',
      value: function getWriter() {
        if (IsWritableStream(this) === false) {
          throw streamBrandCheckException('getWriter');
        }
        return AcquireWritableStreamDefaultWriter(this);
      }
    }, {
      key: 'locked',
      get: function get() {
        if (IsWritableStream(this) === false) {
          throw streamBrandCheckException('locked');
        }
        return IsWritableStreamLocked(this);
      }
    }]);
    return WritableStream;
  }();
  module.exports = {
    AcquireWritableStreamDefaultWriter: AcquireWritableStreamDefaultWriter,
    IsWritableStream: IsWritableStream,
    IsWritableStreamLocked: IsWritableStreamLocked,
    WritableStream: WritableStream,
    WritableStreamAbort: WritableStreamAbort,
    WritableStreamDefaultControllerError: WritableStreamDefaultControllerError,
    WritableStreamDefaultWriterCloseWithErrorPropagation: WritableStreamDefaultWriterCloseWithErrorPropagation,
    WritableStreamDefaultWriterRelease: WritableStreamDefaultWriterRelease,
    WritableStreamDefaultWriterWrite: WritableStreamDefaultWriterWrite,
    WritableStreamCloseQueuedOrInFlight: WritableStreamCloseQueuedOrInFlight
  };
  function AcquireWritableStreamDefaultWriter(stream) {
    return new WritableStreamDefaultWriter(stream);
  }
  function IsWritableStream(x) {
    if (!typeIsObject(x)) {
      return false;
    }
    if (!Object.prototype.hasOwnProperty.call(x, '_writableStreamController')) {
      return false;
    }
    return true;
  }
  function IsWritableStreamLocked(stream) {
    assert(IsWritableStream(stream) === true, 'IsWritableStreamLocked should only be used on known writable streams');
    if (stream._writer === undefined) {
      return false;
    }
    return true;
  }
  function WritableStreamAbort(stream, reason) {
    var state = stream._state;
    if (state === 'closed') {
      return Promise.resolve(undefined);
    }
    if (state === 'errored') {
      return Promise.reject(stream._storedError);
    }
    var error = new TypeError('Requested to abort');
    if (stream._pendingAbortRequest !== undefined) {
      return Promise.reject(error);
    }
    assert(state === 'writable' || state === 'erroring', 'state must be writable or erroring');
    var wasAlreadyErroring = false;
    if (state === 'erroring') {
      wasAlreadyErroring = true;
      reason = undefined;
    }
    var promise = new Promise(function (resolve, reject) {
      stream._pendingAbortRequest = {
        _resolve: resolve,
        _reject: reject,
        _reason: reason,
        _wasAlreadyErroring: wasAlreadyErroring
      };
    });
    if (wasAlreadyErroring === false) {
      WritableStreamStartErroring(stream, error);
    }
    return promise;
  }
  function WritableStreamAddWriteRequest(stream) {
    assert(IsWritableStreamLocked(stream) === true);
    assert(stream._state === 'writable');
    var promise = new Promise(function (resolve, reject) {
      var writeRequest = {
        _resolve: resolve,
        _reject: reject
      };
      stream._writeRequests.push(writeRequest);
    });
    return promise;
  }
  function WritableStreamDealWithRejection(stream, error) {
    var state = stream._state;
    if (state === 'writable') {
      WritableStreamStartErroring(stream, error);
      return;
    }
    assert(state === 'erroring');
    WritableStreamFinishErroring(stream);
  }
  function WritableStreamStartErroring(stream, reason) {
    assert(stream._storedError === undefined, 'stream._storedError === undefined');
    assert(stream._state === 'writable', 'state must be writable');
    var controller = stream._writableStreamController;
    assert(controller !== undefined, 'controller must not be undefined');
    stream._state = 'erroring';
    stream._storedError = reason;
    var writer = stream._writer;
    if (writer !== undefined) {
      WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer, reason);
    }
    if (WritableStreamHasOperationMarkedInFlight(stream) === false && controller._started === true) {
      WritableStreamFinishErroring(stream);
    }
  }
  function WritableStreamFinishErroring(stream) {
    assert(stream._state === 'erroring', 'stream._state === erroring');
    assert(WritableStreamHasOperationMarkedInFlight(stream) === false, 'WritableStreamHasOperationMarkedInFlight(stream) === false');
    stream._state = 'errored';
    stream._writableStreamController.__errorSteps();
    var storedError = stream._storedError;
    for (var i = 0; i < stream._writeRequests.length; i++) {
      var writeRequest = stream._writeRequests[i];
      writeRequest._reject(storedError);
    }
    stream._writeRequests = [];
    if (stream._pendingAbortRequest === undefined) {
      WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream);
      return;
    }
    var abortRequest = stream._pendingAbortRequest;
    stream._pendingAbortRequest = undefined;
    if (abortRequest._wasAlreadyErroring === true) {
      abortRequest._reject(storedError);
      WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream);
      return;
    }
    var promise = stream._writableStreamController.__abortSteps(abortRequest._reason);
    promise.then(function () {
      abortRequest._resolve();
      WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream);
    }, function (reason) {
      abortRequest._reject(reason);
      WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream);
    });
  }
  function WritableStreamFinishInFlightWrite(stream) {
    assert(stream._inFlightWriteRequest !== undefined);
    stream._inFlightWriteRequest._resolve(undefined);
    stream._inFlightWriteRequest = undefined;
  }
  function WritableStreamFinishInFlightWriteWithError(stream, error) {
    assert(stream._inFlightWriteRequest !== undefined);
    stream._inFlightWriteRequest._reject(error);
    stream._inFlightWriteRequest = undefined;
    assert(stream._state === 'writable' || stream._state === 'erroring');
    WritableStreamDealWithRejection(stream, error);
  }
  function WritableStreamFinishInFlightClose(stream) {
    assert(stream._inFlightCloseRequest !== undefined);
    stream._inFlightCloseRequest._resolve(undefined);
    stream._inFlightCloseRequest = undefined;
    var state = stream._state;
    assert(state === 'writable' || state === 'erroring');
    if (state === 'erroring') {
      stream._storedError = undefined;
      if (stream._pendingAbortRequest !== undefined) {
        stream._pendingAbortRequest._resolve();
        stream._pendingAbortRequest = undefined;
      }
    }
    stream._state = 'closed';
    var writer = stream._writer;
    if (writer !== undefined) {
      defaultWriterClosedPromiseResolve(writer);
    }
    assert(stream._pendingAbortRequest === undefined, 'stream._pendingAbortRequest === undefined');
    assert(stream._storedError === undefined, 'stream._storedError === undefined');
  }
  function WritableStreamFinishInFlightCloseWithError(stream, error) {
    assert(stream._inFlightCloseRequest !== undefined);
    stream._inFlightCloseRequest._reject(error);
    stream._inFlightCloseRequest = undefined;
    assert(stream._state === 'writable' || stream._state === 'erroring');
    if (stream._pendingAbortRequest !== undefined) {
      stream._pendingAbortRequest._reject(error);
      stream._pendingAbortRequest = undefined;
    }
    WritableStreamDealWithRejection(stream, error);
  }
  function WritableStreamCloseQueuedOrInFlight(stream) {
    if (stream._closeRequest === undefined && stream._inFlightCloseRequest === undefined) {
      return false;
    }
    return true;
  }
  function WritableStreamHasOperationMarkedInFlight(stream) {
    if (stream._inFlightWriteRequest === undefined && stream._inFlightCloseRequest === undefined) {
      return false;
    }
    return true;
  }
  function WritableStreamMarkCloseRequestInFlight(stream) {
    assert(stream._inFlightCloseRequest === undefined);
    assert(stream._closeRequest !== undefined);
    stream._inFlightCloseRequest = stream._closeRequest;
    stream._closeRequest = undefined;
  }
  function WritableStreamMarkFirstWriteRequestInFlight(stream) {
    assert(stream._inFlightWriteRequest === undefined, 'there must be no pending write request');
    assert(stream._writeRequests.length !== 0, 'writeRequests must not be empty');
    stream._inFlightWriteRequest = stream._writeRequests.shift();
  }
  function WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream) {
    assert(stream._state === 'errored', '_stream_.[[state]] is `"errored"`');
    if (stream._closeRequest !== undefined) {
      assert(stream._inFlightCloseRequest === undefined);
      stream._closeRequest._reject(stream._storedError);
      stream._closeRequest = undefined;
    }
    var writer = stream._writer;
    if (writer !== undefined) {
      defaultWriterClosedPromiseReject(writer, stream._storedError);
      writer._closedPromise.catch(function () {});
    }
  }
  function WritableStreamUpdateBackpressure(stream, backpressure) {
    assert(stream._state === 'writable');
    assert(WritableStreamCloseQueuedOrInFlight(stream) === false);
    var writer = stream._writer;
    if (writer !== undefined && backpressure !== stream._backpressure) {
      if (backpressure === true) {
        defaultWriterReadyPromiseReset(writer);
      } else {
        assert(backpressure === false);
        defaultWriterReadyPromiseResolve(writer);
      }
    }
    stream._backpressure = backpressure;
  }
  var WritableStreamDefaultWriter = function () {
    function WritableStreamDefaultWriter(stream) {
      _classCallCheck(this, WritableStreamDefaultWriter);
      if (IsWritableStream(stream) === false) {
        throw new TypeError('WritableStreamDefaultWriter can only be constructed with a WritableStream instance');
      }
      if (IsWritableStreamLocked(stream) === true) {
        throw new TypeError('This stream has already been locked for exclusive writing by another writer');
      }
      this._ownerWritableStream = stream;
      stream._writer = this;
      var state = stream._state;
      if (state === 'writable') {
        if (WritableStreamCloseQueuedOrInFlight(stream) === false && stream._backpressure === true) {
          defaultWriterReadyPromiseInitialize(this);
        } else {
          defaultWriterReadyPromiseInitializeAsResolved(this);
        }
        defaultWriterClosedPromiseInitialize(this);
      } else if (state === 'erroring') {
        defaultWriterReadyPromiseInitializeAsRejected(this, stream._storedError);
        this._readyPromise.catch(function () {});
        defaultWriterClosedPromiseInitialize(this);
      } else if (state === 'closed') {
        defaultWriterReadyPromiseInitializeAsResolved(this);
        defaultWriterClosedPromiseInitializeAsResolved(this);
      } else {
        assert(state === 'errored', 'state must be errored');
        var storedError = stream._storedError;
        defaultWriterReadyPromiseInitializeAsRejected(this, storedError);
        this._readyPromise.catch(function () {});
        defaultWriterClosedPromiseInitializeAsRejected(this, storedError);
        this._closedPromise.catch(function () {});
      }
    }
    _createClass(WritableStreamDefaultWriter, [{
      key: 'abort',
      value: function abort(reason) {
        if (IsWritableStreamDefaultWriter(this) === false) {
          return Promise.reject(defaultWriterBrandCheckException('abort'));
        }
        if (this._ownerWritableStream === undefined) {
          return Promise.reject(defaultWriterLockException('abort'));
        }
        return WritableStreamDefaultWriterAbort(this, reason);
      }
    }, {
      key: 'close',
      value: function close() {
        if (IsWritableStreamDefaultWriter(this) === false) {
          return Promise.reject(defaultWriterBrandCheckException('close'));
        }
        var stream = this._ownerWritableStream;
        if (stream === undefined) {
          return Promise.reject(defaultWriterLockException('close'));
        }
        if (WritableStreamCloseQueuedOrInFlight(stream) === true) {
          return Promise.reject(new TypeError('cannot close an already-closing stream'));
        }
        return WritableStreamDefaultWriterClose(this);
      }
    }, {
      key: 'releaseLock',
      value: function releaseLock() {
        if (IsWritableStreamDefaultWriter(this) === false) {
          throw defaultWriterBrandCheckException('releaseLock');
        }
        var stream = this._ownerWritableStream;
        if (stream === undefined) {
          return;
        }
        assert(stream._writer !== undefined);
        WritableStreamDefaultWriterRelease(this);
      }
    }, {
      key: 'write',
      value: function write(chunk) {
        if (IsWritableStreamDefaultWriter(this) === false) {
          return Promise.reject(defaultWriterBrandCheckException('write'));
        }
        if (this._ownerWritableStream === undefined) {
          return Promise.reject(defaultWriterLockException('write to'));
        }
        return WritableStreamDefaultWriterWrite(this, chunk);
      }
    }, {
      key: 'closed',
      get: function get() {
        if (IsWritableStreamDefaultWriter(this) === false) {
          return Promise.reject(defaultWriterBrandCheckException('closed'));
        }
        return this._closedPromise;
      }
    }, {
      key: 'desiredSize',
      get: function get() {
        if (IsWritableStreamDefaultWriter(this) === false) {
          throw defaultWriterBrandCheckException('desiredSize');
        }
        if (this._ownerWritableStream === undefined) {
          throw defaultWriterLockException('desiredSize');
        }
        return WritableStreamDefaultWriterGetDesiredSize(this);
      }
    }, {
      key: 'ready',
      get: function get() {
        if (IsWritableStreamDefaultWriter(this) === false) {
          return Promise.reject(defaultWriterBrandCheckException('ready'));
        }
        return this._readyPromise;
      }
    }]);
    return WritableStreamDefaultWriter;
  }();
  function IsWritableStreamDefaultWriter(x) {
    if (!typeIsObject(x)) {
      return false;
    }
    if (!Object.prototype.hasOwnProperty.call(x, '_ownerWritableStream')) {
      return false;
    }
    return true;
  }
  function WritableStreamDefaultWriterAbort(writer, reason) {
    var stream = writer._ownerWritableStream;
    assert(stream !== undefined);
    return WritableStreamAbort(stream, reason);
  }
  function WritableStreamDefaultWriterClose(writer) {
    var stream = writer._ownerWritableStream;
    assert(stream !== undefined);
    var state = stream._state;
    if (state === 'closed' || state === 'errored') {
      return Promise.reject(new TypeError('The stream (in ' + state + ' state) is not in the writable state and cannot be closed'));
    }
    assert(state === 'writable' || state === 'erroring');
    assert(WritableStreamCloseQueuedOrInFlight(stream) === false);
    var promise = new Promise(function (resolve, reject) {
      var closeRequest = {
        _resolve: resolve,
        _reject: reject
      };
      stream._closeRequest = closeRequest;
    });
    if (stream._backpressure === true && state === 'writable') {
      defaultWriterReadyPromiseResolve(writer);
    }
    WritableStreamDefaultControllerClose(stream._writableStreamController);
    return promise;
  }
  function WritableStreamDefaultWriterCloseWithErrorPropagation(writer) {
    var stream = writer._ownerWritableStream;
    assert(stream !== undefined);
    var state = stream._state;
    if (WritableStreamCloseQueuedOrInFlight(stream) === true || state === 'closed') {
      return Promise.resolve();
    }
    if (state === 'errored') {
      return Promise.reject(stream._storedError);
    }
    assert(state === 'writable' || state === 'erroring');
    return WritableStreamDefaultWriterClose(writer);
  }
  function WritableStreamDefaultWriterEnsureClosedPromiseRejected(writer, error) {
    if (writer._closedPromiseState === 'pending') {
      defaultWriterClosedPromiseReject(writer, error);
    } else {
      defaultWriterClosedPromiseResetToRejected(writer, error);
    }
    writer._closedPromise.catch(function () {});
  }
  function WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer, error) {
    if (writer._readyPromiseState === 'pending') {
      defaultWriterReadyPromiseReject(writer, error);
    } else {
      defaultWriterReadyPromiseResetToRejected(writer, error);
    }
    writer._readyPromise.catch(function () {});
  }
  function WritableStreamDefaultWriterGetDesiredSize(writer) {
    var stream = writer._ownerWritableStream;
    var state = stream._state;
    if (state === 'errored' || state === 'erroring') {
      return null;
    }
    if (state === 'closed') {
      return 0;
    }
    return WritableStreamDefaultControllerGetDesiredSize(stream._writableStreamController);
  }
  function WritableStreamDefaultWriterRelease(writer) {
    var stream = writer._ownerWritableStream;
    assert(stream !== undefined);
    assert(stream._writer === writer);
    var releasedError = new TypeError('Writer was released and can no longer be used to monitor the stream\'s closedness');
    WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer, releasedError);
    WritableStreamDefaultWriterEnsureClosedPromiseRejected(writer, releasedError);
    stream._writer = undefined;
    writer._ownerWritableStream = undefined;
  }
  function WritableStreamDefaultWriterWrite(writer, chunk) {
    var stream = writer._ownerWritableStream;
    assert(stream !== undefined);
    var controller = stream._writableStreamController;
    var chunkSize = WritableStreamDefaultControllerGetChunkSize(controller, chunk);
    if (stream !== writer._ownerWritableStream) {
      return Promise.reject(defaultWriterLockException('write to'));
    }
    var state = stream._state;
    if (state === 'errored') {
      return Promise.reject(stream._storedError);
    }
    if (WritableStreamCloseQueuedOrInFlight(stream) === true || state === 'closed') {
      return Promise.reject(new TypeError('The stream is closing or closed and cannot be written to'));
    }
    if (state === 'erroring') {
      return Promise.reject(stream._storedError);
    }
    assert(state === 'writable');
    var promise = WritableStreamAddWriteRequest(stream);
    WritableStreamDefaultControllerWrite(controller, chunk, chunkSize);
    return promise;
  }
  var WritableStreamDefaultController = function () {
    function WritableStreamDefaultController(stream, underlyingSink, size, highWaterMark) {
      _classCallCheck(this, WritableStreamDefaultController);
      if (IsWritableStream(stream) === false) {
        throw new TypeError('WritableStreamDefaultController can only be constructed with a WritableStream instance');
      }
      if (stream._writableStreamController !== undefined) {
        throw new TypeError('WritableStreamDefaultController instances can only be created by the WritableStream constructor');
      }
      this._controlledWritableStream = stream;
      this._underlyingSink = underlyingSink;
      this._queue = undefined;
      this._queueTotalSize = undefined;
      ResetQueue(this);
      this._started = false;
      var normalizedStrategy = ValidateAndNormalizeQueuingStrategy(size, highWaterMark);
      this._strategySize = normalizedStrategy.size;
      this._strategyHWM = normalizedStrategy.highWaterMark;
      var backpressure = WritableStreamDefaultControllerGetBackpressure(this);
      WritableStreamUpdateBackpressure(stream, backpressure);
    }
    _createClass(WritableStreamDefaultController, [{
      key: 'error',
      value: function error(e) {
        if (IsWritableStreamDefaultController(this) === false) {
          throw new TypeError('WritableStreamDefaultController.prototype.error can only be used on a WritableStreamDefaultController');
        }
        var state = this._controlledWritableStream._state;
        if (state !== 'writable') {
          return;
        }
        WritableStreamDefaultControllerError(this, e);
      }
    }, {
      key: '__abortSteps',
      value: function __abortSteps(reason) {
        return PromiseInvokeOrNoop(this._underlyingSink, 'abort', [reason]);
      }
    }, {
      key: '__errorSteps',
      value: function __errorSteps() {
        ResetQueue(this);
      }
    }, {
      key: '__startSteps',
      value: function __startSteps() {
        var _this = this;
        var startResult = InvokeOrNoop(this._underlyingSink, 'start', [this]);
        var stream = this._controlledWritableStream;
        Promise.resolve(startResult).then(function () {
          assert(stream._state === 'writable' || stream._state === 'erroring');
          _this._started = true;
          WritableStreamDefaultControllerAdvanceQueueIfNeeded(_this);
        }, function (r) {
          assert(stream._state === 'writable' || stream._state === 'erroring');
          _this._started = true;
          WritableStreamDealWithRejection(stream, r);
        }).catch(rethrowAssertionErrorRejection);
      }
    }]);
    return WritableStreamDefaultController;
  }();
  function WritableStreamDefaultControllerClose(controller) {
    EnqueueValueWithSize(controller, 'close', 0);
    WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller);
  }
  function WritableStreamDefaultControllerGetChunkSize(controller, chunk) {
    var strategySize = controller._strategySize;
    if (strategySize === undefined) {
      return 1;
    }
    try {
      return strategySize(chunk);
    } catch (chunkSizeE) {
      WritableStreamDefaultControllerErrorIfNeeded(controller, chunkSizeE);
      return 1;
    }
  }
  function WritableStreamDefaultControllerGetDesiredSize(controller) {
    return controller._strategyHWM - controller._queueTotalSize;
  }
  function WritableStreamDefaultControllerWrite(controller, chunk, chunkSize) {
    var writeRecord = { chunk: chunk };
    try {
      EnqueueValueWithSize(controller, writeRecord, chunkSize);
    } catch (enqueueE) {
      WritableStreamDefaultControllerErrorIfNeeded(controller, enqueueE);
      return;
    }
    var stream = controller._controlledWritableStream;
    if (WritableStreamCloseQueuedOrInFlight(stream) === false && stream._state === 'writable') {
      var backpressure = WritableStreamDefaultControllerGetBackpressure(controller);
      WritableStreamUpdateBackpressure(stream, backpressure);
    }
    WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller);
  }
  function IsWritableStreamDefaultController(x) {
    if (!typeIsObject(x)) {
      return false;
    }
    if (!Object.prototype.hasOwnProperty.call(x, '_underlyingSink')) {
      return false;
    }
    return true;
  }
  function WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller) {
    var stream = controller._controlledWritableStream;
    if (controller._started === false) {
      return;
    }
    if (stream._inFlightWriteRequest !== undefined) {
      return;
    }
    var state = stream._state;
    if (state === 'closed' || state === 'errored') {
      return;
    }
    if (state === 'erroring') {
      WritableStreamFinishErroring(stream);
      return;
    }
    if (controller._queue.length === 0) {
      return;
    }
    var writeRecord = PeekQueueValue(controller);
    if (writeRecord === 'close') {
      WritableStreamDefaultControllerProcessClose(controller);
    } else {
      WritableStreamDefaultControllerProcessWrite(controller, writeRecord.chunk);
    }
  }
  function WritableStreamDefaultControllerErrorIfNeeded(controller, error) {
    if (controller._controlledWritableStream._state === 'writable') {
      WritableStreamDefaultControllerError(controller, error);
    }
  }
  function WritableStreamDefaultControllerProcessClose(controller) {
    var stream = controller._controlledWritableStream;
    WritableStreamMarkCloseRequestInFlight(stream);
    DequeueValue(controller);
    assert(controller._queue.length === 0, 'queue must be empty once the final write record is dequeued');
    var sinkClosePromise = PromiseInvokeOrNoop(controller._underlyingSink, 'close', []);
    sinkClosePromise.then(function () {
      WritableStreamFinishInFlightClose(stream);
    }, function (reason) {
      WritableStreamFinishInFlightCloseWithError(stream, reason);
    }).catch(rethrowAssertionErrorRejection);
  }
  function WritableStreamDefaultControllerProcessWrite(controller, chunk) {
    var stream = controller._controlledWritableStream;
    WritableStreamMarkFirstWriteRequestInFlight(stream);
    var sinkWritePromise = PromiseInvokeOrNoop(controller._underlyingSink, 'write', [chunk, controller]);
    sinkWritePromise.then(function () {
      WritableStreamFinishInFlightWrite(stream);
      var state = stream._state;
      assert(state === 'writable' || state === 'erroring');
      DequeueValue(controller);
      if (WritableStreamCloseQueuedOrInFlight(stream) === false && state === 'writable') {
        var backpressure = WritableStreamDefaultControllerGetBackpressure(controller);
        WritableStreamUpdateBackpressure(stream, backpressure);
      }
      WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller);
    }, function (reason) {
      WritableStreamFinishInFlightWriteWithError(stream, reason);
    }).catch(rethrowAssertionErrorRejection);
  }
  function WritableStreamDefaultControllerGetBackpressure(controller) {
    var desiredSize = WritableStreamDefaultControllerGetDesiredSize(controller);
    return desiredSize <= 0;
  }
  function WritableStreamDefaultControllerError(controller, error) {
    var stream = controller._controlledWritableStream;
    assert(stream._state === 'writable');
    WritableStreamStartErroring(stream, error);
  }
  function streamBrandCheckException(name) {
    return new TypeError('WritableStream.prototype.' + name + ' can only be used on a WritableStream');
  }
  function defaultWriterBrandCheckException(name) {
    return new TypeError('WritableStreamDefaultWriter.prototype.' + name + ' can only be used on a WritableStreamDefaultWriter');
  }
  function defaultWriterLockException(name) {
    return new TypeError('Cannot ' + name + ' a stream using a released writer');
  }
  function defaultWriterClosedPromiseInitialize(writer) {
    writer._closedPromise = new Promise(function (resolve, reject) {
      writer._closedPromise_resolve = resolve;
      writer._closedPromise_reject = reject;
      writer._closedPromiseState = 'pending';
    });
  }
  function defaultWriterClosedPromiseInitializeAsRejected(writer, reason) {
    writer._closedPromise = Promise.reject(reason);
    writer._closedPromise_resolve = undefined;
    writer._closedPromise_reject = undefined;
    writer._closedPromiseState = 'rejected';
  }
  function defaultWriterClosedPromiseInitializeAsResolved(writer) {
    writer._closedPromise = Promise.resolve(undefined);
    writer._closedPromise_resolve = undefined;
    writer._closedPromise_reject = undefined;
    writer._closedPromiseState = 'resolved';
  }
  function defaultWriterClosedPromiseReject(writer, reason) {
    assert(writer._closedPromise_resolve !== undefined, 'writer._closedPromise_resolve !== undefined');
    assert(writer._closedPromise_reject !== undefined, 'writer._closedPromise_reject !== undefined');
    assert(writer._closedPromiseState === 'pending', 'writer._closedPromiseState is pending');
    writer._closedPromise_reject(reason);
    writer._closedPromise_resolve = undefined;
    writer._closedPromise_reject = undefined;
    writer._closedPromiseState = 'rejected';
  }
  function defaultWriterClosedPromiseResetToRejected(writer, reason) {
    assert(writer._closedPromise_resolve === undefined, 'writer._closedPromise_resolve === undefined');
    assert(writer._closedPromise_reject === undefined, 'writer._closedPromise_reject === undefined');
    assert(writer._closedPromiseState !== 'pending', 'writer._closedPromiseState is not pending');
    writer._closedPromise = Promise.reject(reason);
    writer._closedPromiseState = 'rejected';
  }
  function defaultWriterClosedPromiseResolve(writer) {
    assert(writer._closedPromise_resolve !== undefined, 'writer._closedPromise_resolve !== undefined');
    assert(writer._closedPromise_reject !== undefined, 'writer._closedPromise_reject !== undefined');
    assert(writer._closedPromiseState === 'pending', 'writer._closedPromiseState is pending');
    writer._closedPromise_resolve(undefined);
    writer._closedPromise_resolve = undefined;
    writer._closedPromise_reject = undefined;
    writer._closedPromiseState = 'resolved';
  }
  function defaultWriterReadyPromiseInitialize(writer) {
    writer._readyPromise = new Promise(function (resolve, reject) {
      writer._readyPromise_resolve = resolve;
      writer._readyPromise_reject = reject;
    });
    writer._readyPromiseState = 'pending';
  }
  function defaultWriterReadyPromiseInitializeAsRejected(writer, reason) {
    writer._readyPromise = Promise.reject(reason);
    writer._readyPromise_resolve = undefined;
    writer._readyPromise_reject = undefined;
    writer._readyPromiseState = 'rejected';
  }
  function defaultWriterReadyPromiseInitializeAsResolved(writer) {
    writer._readyPromise = Promise.resolve(undefined);
    writer._readyPromise_resolve = undefined;
    writer._readyPromise_reject = undefined;
    writer._readyPromiseState = 'fulfilled';
  }
  function defaultWriterReadyPromiseReject(writer, reason) {
    assert(writer._readyPromise_resolve !== undefined, 'writer._readyPromise_resolve !== undefined');
    assert(writer._readyPromise_reject !== undefined, 'writer._readyPromise_reject !== undefined');
    writer._readyPromise_reject(reason);
    writer._readyPromise_resolve = undefined;
    writer._readyPromise_reject = undefined;
    writer._readyPromiseState = 'rejected';
  }
  function defaultWriterReadyPromiseReset(writer) {
    assert(writer._readyPromise_resolve === undefined, 'writer._readyPromise_resolve === undefined');
    assert(writer._readyPromise_reject === undefined, 'writer._readyPromise_reject === undefined');
    writer._readyPromise = new Promise(function (resolve, reject) {
      writer._readyPromise_resolve = resolve;
      writer._readyPromise_reject = reject;
    });
    writer._readyPromiseState = 'pending';
  }
  function defaultWriterReadyPromiseResetToRejected(writer, reason) {
    assert(writer._readyPromise_resolve === undefined, 'writer._readyPromise_resolve === undefined');
    assert(writer._readyPromise_reject === undefined, 'writer._readyPromise_reject === undefined');
    writer._readyPromise = Promise.reject(reason);
    writer._readyPromiseState = 'rejected';
  }
  function defaultWriterReadyPromiseResolve(writer) {
    assert(writer._readyPromise_resolve !== undefined, 'writer._readyPromise_resolve !== undefined');
    assert(writer._readyPromise_reject !== undefined, 'writer._readyPromise_reject !== undefined');
    writer._readyPromise_resolve(undefined);
    writer._readyPromise_resolve = undefined;
    writer._readyPromise_reject = undefined;
    writer._readyPromiseState = 'fulfilled';
  }
}, function (module, exports, __w_pdfjs_require__) {
  "use strict";

  var _require = __w_pdfjs_require__(0),
      IsFiniteNonNegativeNumber = _require.IsFiniteNonNegativeNumber;
  var _require2 = __w_pdfjs_require__(1),
      assert = _require2.assert;
  exports.DequeueValue = function (container) {
    assert('_queue' in container && '_queueTotalSize' in container, 'Spec-level failure: DequeueValue should only be used on containers with [[queue]] and [[queueTotalSize]].');
    assert(container._queue.length > 0, 'Spec-level failure: should never dequeue from an empty queue.');
    var pair = container._queue.shift();
    container._queueTotalSize -= pair.size;
    if (container._queueTotalSize < 0) {
      container._queueTotalSize = 0;
    }
    return pair.value;
  };
  exports.EnqueueValueWithSize = function (container, value, size) {
    assert('_queue' in container && '_queueTotalSize' in container, 'Spec-level failure: EnqueueValueWithSize should only be used on containers with [[queue]] and ' + '[[queueTotalSize]].');
    size = Number(size);
    if (!IsFiniteNonNegativeNumber(size)) {
      throw new RangeError('Size must be a finite, non-NaN, non-negative number.');
    }
    container._queue.push({
      value: value,
      size: size
    });
    container._queueTotalSize += size;
  };
  exports.PeekQueueValue = function (container) {
    assert('_queue' in container && '_queueTotalSize' in container, 'Spec-level failure: PeekQueueValue should only be used on containers with [[queue]] and [[queueTotalSize]].');
    assert(container._queue.length > 0, 'Spec-level failure: should never peek at an empty queue.');
    var pair = container._queue[0];
    return pair.value;
  };
  exports.ResetQueue = function (container) {
    assert('_queue' in container && '_queueTotalSize' in container, 'Spec-level failure: ResetQueue should only be used on containers with [[queue]] and [[queueTotalSize]].');
    container._queue = [];
    container._queueTotalSize = 0;
  };
}, function (module, exports, __w_pdfjs_require__) {
  "use strict";

  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");
    }
  }
  var _require = __w_pdfjs_require__(0),
      ArrayBufferCopy = _require.ArrayBufferCopy,
      CreateIterResultObject = _require.CreateIterResultObject,
      IsFiniteNonNegativeNumber = _require.IsFiniteNonNegativeNumber,
      InvokeOrNoop = _require.InvokeOrNoop,
      PromiseInvokeOrNoop = _require.PromiseInvokeOrNoop,
      TransferArrayBuffer = _require.TransferArrayBuffer,
      ValidateAndNormalizeQueuingStrategy = _require.ValidateAndNormalizeQueuingStrategy,
      ValidateAndNormalizeHighWaterMark = _require.ValidateAndNormalizeHighWaterMark;
  var _require2 = __w_pdfjs_require__(0),
      createArrayFromList = _require2.createArrayFromList,
      createDataProperty = _require2.createDataProperty,
      typeIsObject = _require2.typeIsObject;
  var _require3 = __w_pdfjs_require__(1),
      assert = _require3.assert,
      rethrowAssertionErrorRejection = _require3.rethrowAssertionErrorRejection;
  var _require4 = __w_pdfjs_require__(3),
      DequeueValue = _require4.DequeueValue,
      EnqueueValueWithSize = _require4.EnqueueValueWithSize,
      ResetQueue = _require4.ResetQueue;
  var _require5 = __w_pdfjs_require__(2),
      AcquireWritableStreamDefaultWriter = _require5.AcquireWritableStreamDefaultWriter,
      IsWritableStream = _require5.IsWritableStream,
      IsWritableStreamLocked = _require5.IsWritableStreamLocked,
      WritableStreamAbort = _require5.WritableStreamAbort,
      WritableStreamDefaultWriterCloseWithErrorPropagation = _require5.WritableStreamDefaultWriterCloseWithErrorPropagation,
      WritableStreamDefaultWriterRelease = _require5.WritableStreamDefaultWriterRelease,
      WritableStreamDefaultWriterWrite = _require5.WritableStreamDefaultWriterWrite,
      WritableStreamCloseQueuedOrInFlight = _require5.WritableStreamCloseQueuedOrInFlight;
  var ReadableStream = function () {
    function ReadableStream() {
      var underlyingSource = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
          size = _ref.size,
          highWaterMark = _ref.highWaterMark;
      _classCallCheck(this, ReadableStream);
      this._state = 'readable';
      this._reader = undefined;
      this._storedError = undefined;
      this._disturbed = false;
      this._readableStreamController = undefined;
      var type = underlyingSource.type;
      var typeString = String(type);
      if (typeString === 'bytes') {
        if (highWaterMark === undefined) {
          highWaterMark = 0;
        }
        this._readableStreamController = new ReadableByteStreamController(this, underlyingSource, highWaterMark);
      } else if (type === undefined) {
        if (highWaterMark === undefined) {
          highWaterMark = 1;
        }
        this._readableStreamController = new ReadableStreamDefaultController(this, underlyingSource, size, highWaterMark);
      } else {
        throw new RangeError('Invalid type is specified');
      }
    }
    _createClass(ReadableStream, [{
      key: 'cancel',
      value: function cancel(reason) {
        if (IsReadableStream(this) === false) {
          return Promise.reject(streamBrandCheckException('cancel'));
        }
        if (IsReadableStreamLocked(this) === true) {
          return Promise.reject(new TypeError('Cannot cancel a stream that already has a reader'));
        }
        return ReadableStreamCancel(this, reason);
      }
    }, {
      key: 'getReader',
      value: function getReader() {
        var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
            mode = _ref2.mode;
        if (IsReadableStream(this) === false) {
          throw streamBrandCheckException('getReader');
        }
        if (mode === undefined) {
          return AcquireReadableStreamDefaultReader(this);
        }
        mode = String(mode);
        if (mode === 'byob') {
          return AcquireReadableStreamBYOBReader(this);
        }
        throw new RangeError('Invalid mode is specified');
      }
    }, {
      key: 'pipeThrough',
      value: function pipeThrough(_ref3, options) {
        var writable = _ref3.writable,
            readable = _ref3.readable;
        var promise = this.pipeTo(writable, options);
        ifIsObjectAndHasAPromiseIsHandledInternalSlotSetPromiseIsHandledToTrue(promise);
        return readable;
      }
    }, {
      key: 'pipeTo',
      value: function pipeTo(dest) {
        var _this = this;
        var _ref4 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
            preventClose = _ref4.preventClose,
            preventAbort = _ref4.preventAbort,
            preventCancel = _ref4.preventCancel;
        if (IsReadableStream(this) === false) {
          return Promise.reject(streamBrandCheckException('pipeTo'));
        }
        if (IsWritableStream(dest) === false) {
          return Promise.reject(new TypeError('ReadableStream.prototype.pipeTo\'s first argument must be a WritableStream'));
        }
        preventClose = Boolean(preventClose);
        preventAbort = Boolean(preventAbort);
        preventCancel = Boolean(preventCancel);
        if (IsReadableStreamLocked(this) === true) {
          return Promise.reject(new TypeError('ReadableStream.prototype.pipeTo cannot be used on a locked ReadableStream'));
        }
        if (IsWritableStreamLocked(dest) === true) {
          return Promise.reject(new TypeError('ReadableStream.prototype.pipeTo cannot be used on a locked WritableStream'));
        }
        var reader = AcquireReadableStreamDefaultReader(this);
        var writer = AcquireWritableStreamDefaultWriter(dest);
        var shuttingDown = false;
        var currentWrite = Promise.resolve();
        return new Promise(function (resolve, reject) {
          function pipeLoop() {
            currentWrite = Promise.resolve();
            if (shuttingDown === true) {
              return Promise.resolve();
            }
            return writer._readyPromise.then(function () {
              return ReadableStreamDefaultReaderRead(reader).then(function (_ref5) {
                var value = _ref5.value,
                    done = _ref5.done;
                if (done === true) {
                  return;
                }
                currentWrite = WritableStreamDefaultWriterWrite(writer, value).catch(function () {});
              });
            }).then(pipeLoop);
          }
          isOrBecomesErrored(_this, reader._closedPromise, function (storedError) {
            if (preventAbort === false) {
              shutdownWithAction(function () {
                return WritableStreamAbort(dest, storedError);
              }, true, storedError);
            } else {
              shutdown(true, storedError);
            }
          });
          isOrBecomesErrored(dest, writer._closedPromise, function (storedError) {
            if (preventCancel === false) {
              shutdownWithAction(function () {
                return ReadableStreamCancel(_this, storedError);
              }, true, storedError);
            } else {
              shutdown(true, storedError);
            }
          });
          isOrBecomesClosed(_this, reader._closedPromise, function () {
            if (preventClose === false) {
              shutdownWithAction(function () {
                return WritableStreamDefaultWriterCloseWithErrorPropagation(writer);
              });
            } else {
              shutdown();
            }
          });
          if (WritableStreamCloseQueuedOrInFlight(dest) === true || dest._state === 'closed') {
            var destClosed = new TypeError('the destination writable stream closed before all data could be piped to it');
            if (preventCancel === false) {
              shutdownWithAction(function () {
                return ReadableStreamCancel(_this, destClosed);
              }, true, destClosed);
            } else {
              shutdown(true, destClosed);
            }
          }
          pipeLoop().catch(function (err) {
            currentWrite = Promise.resolve();
            rethrowAssertionErrorRejection(err);
          });
          function waitForWritesToFinish() {
            var oldCurrentWrite = currentWrite;
            return currentWrite.then(function () {
              return oldCurrentWrite !== currentWrite ? waitForWritesToFinish() : undefined;
            });
          }
          function isOrBecomesErrored(stream, promise, action) {
            if (stream._state === 'errored') {
              action(stream._storedError);
            } else {
              promise.catch(action).catch(rethrowAssertionErrorRejection);
            }
          }
          function isOrBecomesClosed(stream, promise, action) {
            if (stream._state === 'closed') {
              action();
            } else {
              promise.then(action).catch(rethrowAssertionErrorRejection);
            }
          }
          function shutdownWithAction(action, originalIsError, originalError) {
            if (shuttingDown === true) {
              return;
            }
            shuttingDown = true;
            if (dest._state === 'writable' && WritableStreamCloseQueuedOrInFlight(dest) === false) {
              waitForWritesToFinish().then(doTheRest);
            } else {
              doTheRest();
            }
            function doTheRest() {
              action().then(function () {
                return finalize(originalIsError, originalError);
              }, function (newError) {
                return finalize(true, newError);
              }).catch(rethrowAssertionErrorRejection);
            }
          }
          function shutdown(isError, error) {
            if (shuttingDown === true) {
              return;
            }
            shuttingDown = true;
            if (dest._state === 'writable' && WritableStreamCloseQueuedOrInFlight(dest) === false) {
              waitForWritesToFinish().then(function () {
                return finalize(isError, error);
              }).catch(rethrowAssertionErrorRejection);
            } else {
              finalize(isError, error);
            }
          }
          function finalize(isError, error) {
            WritableStreamDefaultWriterRelease(writer);
            ReadableStreamReaderGenericRelease(reader);
            if (isError) {
              reject(error);
            } else {
              resolve(undefined);
            }
          }
        });
      }
    }, {
      key: 'tee',
      value: function tee() {
        if (IsReadableStream(this) === false) {
          throw streamBrandCheckException('tee');
        }
        var branches = ReadableStreamTee(this, false);
        return createArrayFromList(branches);
      }
    }, {
      key: 'locked',
      get: function get() {
        if (IsReadableStream(this) === false) {
          throw streamBrandCheckException('locked');
        }
        return IsReadableStreamLocked(this);
      }
    }]);
    return ReadableStream;
  }();
  module.exports = {
    ReadableStream: ReadableStream,
    IsReadableStreamDisturbed: IsReadableStreamDisturbed,
    ReadableStreamDefaultControllerClose: ReadableStreamDefaultControllerClose,
    ReadableStreamDefaultControllerEnqueue: ReadableStreamDefaultControllerEnqueue,
    ReadableStreamDefaultControllerError: ReadableStreamDefaultControllerError,
    ReadableStreamDefaultControllerGetDesiredSize: ReadableStreamDefaultControllerGetDesiredSize
  };
  function AcquireReadableStreamBYOBReader(stream) {
    return new ReadableStreamBYOBReader(stream);
  }
  function AcquireReadableStreamDefaultReader(stream) {
    return new ReadableStreamDefaultReader(stream);
  }
  function IsReadableStream(x) {
    if (!typeIsObject(x)) {
      return false;
    }
    if (!Object.prototype.hasOwnProperty.call(x, '_readableStreamController')) {
      return false;
    }
    return true;
  }
  function IsReadableStreamDisturbed(stream) {
    assert(IsReadableStream(stream) === true, 'IsReadableStreamDisturbed should only be used on known readable streams');
    return stream._disturbed;
  }
  function IsReadableStreamLocked(stream) {
    assert(IsReadableStream(stream) === true, 'IsReadableStreamLocked should only be used on known readable streams');
    if (stream._reader === undefined) {
      return false;
    }
    return true;
  }
  function ReadableStreamTee(stream, cloneForBranch2) {
    assert(IsReadableStream(stream) === true);
    assert(typeof cloneForBranch2 === 'boolean');
    var reader = AcquireReadableStreamDefaultReader(stream);
    var teeState = {
      closedOrErrored: false,
      canceled1: false,
      canceled2: false,
      reason1: undefined,
      reason2: undefined
    };
    teeState.promise = new Promise(function (resolve) {
      teeState._resolve = resolve;
    });
    var pull = create_ReadableStreamTeePullFunction();
    pull._reader = reader;
    pull._teeState = teeState;
    pull._cloneForBranch2 = cloneForBranch2;
    var cancel1 = create_ReadableStreamTeeBranch1CancelFunction();
    cancel1._stream = stream;
    cancel1._teeState = teeState;
    var cancel2 = create_ReadableStreamTeeBranch2CancelFunction();
    cancel2._stream = stream;
    cancel2._teeState = teeState;
    var underlyingSource1 = Object.create(Object.prototype);
    createDataProperty(underlyingSource1, 'pull', pull);
    createDataProperty(underlyingSource1, 'cancel', cancel1);
    var branch1Stream = new ReadableStream(underlyingSource1);
    var underlyingSource2 = Object.create(Object.prototype);
    createDataProperty(underlyingSource2, 'pull', pull);
    createDataProperty(underlyingSource2, 'cancel', cancel2);
    var branch2Stream = new ReadableStream(underlyingSource2);
    pull._branch1 = branch1Stream._readableStreamController;
    pull._branch2 = branch2Stream._readableStreamController;
    reader._closedPromise.catch(function (r) {
      if (teeState.closedOrErrored === true) {
        return;
      }
      ReadableStreamDefaultControllerError(pull._branch1, r);
      ReadableStreamDefaultControllerError(pull._branch2, r);
      teeState.closedOrErrored = true;
    });
    return [branch1Stream, branch2Stream];
  }
  function create_ReadableStreamTeePullFunction() {
    function f() {
      var reader = f._reader,
          branch1 = f._branch1,
          branch2 = f._branch2,
          teeState = f._teeState;
      return ReadableStreamDefaultReaderRead(reader).then(function (result) {
        assert(typeIsObject(result));
        var value = result.value;
        var done = result.done;
        assert(typeof done === 'boolean');
        if (done === true && teeState.closedOrErrored === false) {
          if (teeState.canceled1 === false) {
            ReadableStreamDefaultControllerClose(branch1);
          }
          if (teeState.canceled2 === false) {
            ReadableStreamDefaultControllerClose(branch2);
          }
          teeState.closedOrErrored = true;
        }
        if (teeState.closedOrErrored === true) {
          return;
        }
        var value1 = value;
        var value2 = value;
        if (teeState.canceled1 === false) {
          ReadableStreamDefaultControllerEnqueue(branch1, value1);
        }
        if (teeState.canceled2 === false) {
          ReadableStreamDefaultControllerEnqueue(branch2, value2);
        }
      });
    }
    return f;
  }
  function create_ReadableStreamTeeBranch1CancelFunction() {
    function f(reason) {
      var stream = f._stream,
          teeState = f._teeState;
      teeState.canceled1 = true;
      teeState.reason1 = reason;
      if (teeState.canceled2 === true) {
        var compositeReason = createArrayFromList([teeState.reason1, teeState.reason2]);
        var cancelResult = ReadableStreamCancel(stream, compositeReason);
        teeState._resolve(cancelResult);
      }
      return teeState.promise;
    }
    return f;
  }
  function create_ReadableStreamTeeBranch2CancelFunction() {
    function f(reason) {
      var stream = f._stream,
          teeState = f._teeState;
      teeState.canceled2 = true;
      teeState.reason2 = reason;
      if (teeState.canceled1 === true) {
        var compositeReason = createArrayFromList([teeState.reason1, teeState.reason2]);
        var cancelResult = ReadableStreamCancel(stream, compositeReason);
        teeState._resolve(cancelResult);
      }
      return teeState.promise;
    }
    return f;
  }
  function ReadableStreamAddReadIntoRequest(stream) {
    assert(IsReadableStreamBYOBReader(stream._reader) === true);
    assert(stream._state === 'readable' || stream._state === 'closed');
    var promise = new Promise(function (resolve, reject) {
      var readIntoRequest = {
        _resolve: resolve,
        _reject: reject
      };
      stream._reader._readIntoRequests.push(readIntoRequest);
    });
    return promise;
  }
  function ReadableStreamAddReadRequest(stream) {
    assert(IsReadableStreamDefaultReader(stream._reader) === true);
    assert(stream._state === 'readable');
    var promise = new Promise(function (resolve, reject) {
      var readRequest = {
        _resolve: resolve,
        _reject: reject
      };
      stream._reader._readRequests.push(readRequest);
    });
    return promise;
  }
  function ReadableStreamCancel(stream, reason) {
    stream._disturbed = true;
    if (stream._state === 'closed') {
      return Promise.resolve(undefined);
    }
    if (stream._state === 'errored') {
      return Promise.reject(stream._storedError);
    }
    ReadableStreamClose(stream);
    var sourceCancelPromise = stream._readableStreamController.__cancelSteps(reason);
    return sourceCancelPromise.then(function () {
      return undefined;
    });
  }
  function ReadableStreamClose(stream) {
    assert(stream._state === 'readable');
    stream._state = 'closed';
    var reader = stream._reader;
    if (reader === undefined) {
      return undefined;
    }
    if (IsReadableStreamDefaultReader(reader) === true) {
      for (var i = 0; i < reader._readRequests.length; i++) {
        var _resolve = reader._readRequests[i]._resolve;
        _resolve(CreateIterResultObject(undefined, true));
      }
      reader._readRequests = [];
    }
    defaultReaderClosedPromiseResolve(reader);
    return undefined;
  }
  function ReadableStreamError(stream, e) {
    assert(IsReadableStream(stream) === true, 'stream must be ReadableStream');
    assert(stream._state === 'readable', 'state must be readable');
    stream._state = 'errored';
    stream._storedError = e;
    var reader = stream._reader;
    if (reader === undefined) {
      return undefined;
    }
    if (IsReadableStreamDefaultReader(reader) === true) {
      for (var i = 0; i < reader._readRequests.length; i++) {
        var readRequest = reader._readRequests[i];
        readRequest._reject(e);
      }
      reader._readRequests = [];
    } else {
      assert(IsReadableStreamBYOBReader(reader), 'reader must be ReadableStreamBYOBReader');
      for (var _i = 0; _i < reader._readIntoRequests.length; _i++) {
        var readIntoRequest = reader._readIntoRequests[_i];
        readIntoRequest._reject(e);
      }
      reader._readIntoRequests = [];
    }
    defaultReaderClosedPromiseReject(reader, e);
    reader._closedPromise.catch(function () {});
  }
  function ReadableStreamFulfillReadIntoRequest(stream, chunk, done) {
    var reader = stream._reader;
    assert(reader._readIntoRequests.length > 0);
    var readIntoRequest = reader._readIntoRequests.shift();
    readIntoRequest._resolve(CreateIterResultObject(chunk, done));
  }
  function ReadableStreamFulfillReadRequest(stream, chunk, done) {
    var reader = stream._reader;
    assert(reader._readRequests.length > 0);
    var readRequest = reader._readRequests.shift();
    readRequest._resolve(CreateIterResultObject(chunk, done));
  }
  function ReadableStreamGetNumReadIntoRequests(stream) {
    return stream._reader._readIntoRequests.length;
  }
  function ReadableStreamGetNumReadRequests(stream) {
    return stream._reader._readRequests.length;
  }
  function ReadableStreamHasBYOBReader(stream) {
    var reader = stream._reader;
    if (reader === undefined) {
      return false;
    }
    if (IsReadableStreamBYOBReader(reader) === false) {
      return false;
    }
    return true;
  }
  function ReadableStreamHasDefaultReader(stream) {
    var reader = stream._reader;
    if (reader === undefined) {
      return false;
    }
    if (IsReadableStreamDefaultReader(reader) === false) {
      return false;
    }
    return true;
  }
  var ReadableStreamDefaultReader = function () {
    function ReadableStreamDefaultReader(stream) {
      _classCallCheck(this, ReadableStreamDefaultReader);
      if (IsReadableStream(stream) === false) {
        throw new TypeError('ReadableStreamDefaultReader can only be constructed with a ReadableStream instance');
      }
      if (IsReadableStreamLocked(stream) === true) {
        throw new TypeError('This stream has already been locked for exclusive reading by another reader');
      }
      ReadableStreamReaderGenericInitialize(this, stream);
      this._readRequests = [];
    }
    _createClass(ReadableStreamDefaultReader, [{
      key: 'cancel',
      value: function cancel(reason) {
        if (IsReadableStreamDefaultReader(this) === false) {
          return Promise.reject(defaultReaderBrandCheckException('cancel'));
        }
        if (this._ownerReadableStream === undefined) {
          return Promise.reject(readerLockException('cancel'));
        }
        return ReadableStreamReaderGenericCancel(this, reason);
      }
    }, {
      key: 'read',
      value: function read() {
        if (IsReadableStreamDefaultReader(this) === false) {
          return Promise.reject(defaultReaderBrandCheckException('read'));
        }
        if (this._ownerReadableStream === undefined) {
          return Promise.reject(readerLockException('read from'));
        }
        return ReadableStreamDefaultReaderRead(this);
      }
    }, {
      key: 'releaseLock',
      value: function releaseLock() {
        if (IsReadableStreamDefaultReader(this) === false) {
          throw defaultReaderBrandCheckException('releaseLock');
        }
        if (this._ownerReadableStream === undefined) {
          return;
        }
        if (this._readRequests.length > 0) {
          throw new TypeError('Tried to release a reader lock when that reader has pending read() calls un-settled');
        }
        ReadableStreamReaderGenericRelease(this);
      }
    }, {
      key: 'closed',
      get: function get() {
        if (IsReadableStreamDefaultReader(this) === false) {
          return Promise.reject(defaultReaderBrandCheckException('closed'));
        }
        return this._closedPromise;
      }
    }]);
    return ReadableStreamDefaultReader;
  }();
  var ReadableStreamBYOBReader = function () {
    function ReadableStreamBYOBReader(stream) {
      _classCallCheck(this, ReadableStreamBYOBReader);
      if (!IsReadableStream(stream)) {
        throw new TypeError('ReadableStreamBYOBReader can only be constructed with a ReadableStream instance given a ' + 'byte source');
      }
      if (IsReadableByteStreamController(stream._readableStreamController) === false) {
        throw new TypeError('Cannot construct a ReadableStreamBYOBReader for a stream not constructed with a byte ' + 'source');
      }
      if (IsReadableStreamLocked(stream)) {
        throw new TypeError('This stream has already been locked for exclusive reading by another reader');
      }
      ReadableStreamReaderGenericInitialize(this, stream);
      this._readIntoRequests = [];
    }
    _createClass(ReadableStreamBYOBReader, [{
      key: 'cancel',
      value: function cancel(reason) {
        if (!IsReadableStreamBYOBReader(this)) {
          return Promise.reject(byobReaderBrandCheckException('cancel'));
        }
        if (this._ownerReadableStream === undefined) {
          return Promise.reject(readerLockException('cancel'));
        }
        return ReadableStreamReaderGenericCancel(this, reason);
      }
    }, {
      key: 'read',
      value: function read(view) {
        if (!IsReadableStreamBYOBReader(this)) {
          return Promise.reject(byobReaderBrandCheckException('read'));
        }
        if (this._ownerReadableStream === undefined) {
          return Promise.reject(readerLockException('read from'));
        }
        if (!ArrayBuffer.isView(view)) {
          return Promise.reject(new TypeError('view must be an array buffer view'));
        }
        if (view.byteLength === 0) {
          return Promise.reject(new TypeError('view must have non-zero byteLength'));
        }
        return ReadableStreamBYOBReaderRead(this, view);
      }
    }, {
      key: 'releaseLock',
      value: function releaseLock() {
        if (!IsReadableStreamBYOBReader(this)) {
          throw byobReaderBrandCheckException('releaseLock');
        }
        if (this._ownerReadableStream === undefined) {
          return;
        }
        if (this._readIntoRequests.length > 0) {
          throw new TypeError('Tried to release a reader lock when that reader has pending read() calls un-settled');
        }
        ReadableStreamReaderGenericRelease(this);
      }
    }, {
      key: 'closed',
      get: function get() {
        if (!IsReadableStreamBYOBReader(this)) {
          return Promise.reject(byobReaderBrandCheckException('closed'));
        }
        return this._closedPromise;
      }
    }]);
    return ReadableStreamBYOBReader;
  }();
  function IsReadableStreamBYOBReader(x) {
    if (!typeIsObject(x)) {
      return false;
    }
    if (!Object.prototype.hasOwnProperty.call(x, '_readIntoRequests')) {
      return false;
    }
    return true;
  }
  function IsReadableStreamDefaultReader(x) {
    if (!typeIsObject(x)) {
      return false;
    }
    if (!Object.prototype.hasOwnProperty.call(x, '_readRequests')) {
      return false;
    }
    return true;
  }
  function ReadableStreamReaderGenericInitialize(reader, stream) {
    reader._ownerReadableStream = stream;
    stream._reader = reader;
    if (stream._state === 'readable') {
      defaultReaderClosedPromiseInitialize(reader);
    } else if (stream._state === 'closed') {
      defaultReaderClosedPromiseInitializeAsResolved(reader);
    } else {
      assert(stream._state === 'errored', 'state must be errored');
      defaultReaderClosedPromiseInitializeAsRejected(reader, stream._storedError);
      reader._closedPromise.catch(function () {});
    }
  }
  function ReadableStreamReaderGenericCancel(reader, reason) {
    var stream = reader._ownerReadableStream;
    assert(stream !== undefined);
    return ReadableStreamCancel(stream, reason);
  }
  function ReadableStreamReaderGenericRelease(reader) {
    assert(reader._ownerReadableStream !== undefined);
    assert(reader._ownerReadableStream._reader === reader);
    if (reader._ownerReadableStream._state === 'readable') {
      defaultReaderClosedPromiseReject(reader, new TypeError('Reader was released and can no longer be used to monitor the stream\'s closedness'));
    } else {
      defaultReaderClosedPromiseResetToRejected(reader, new TypeError('Reader was released and can no longer be used to monitor the stream\'s closedness'));
    }
    reader._closedPromise.catch(function () {});
    reader._ownerReadableStream._reader = undefined;
    reader._ownerReadableStream = undefined;
  }
  function ReadableStreamBYOBReaderRead(reader, view) {
    var stream = reader._ownerReadableStream;
    assert(stream !== undefined);
    stream._disturbed = true;
    if (stream._state === 'errored') {
      return Promise.reject(stream._storedError);
    }
    return ReadableByteStreamControllerPullInto(stream._readableStreamController, view);
  }
  function ReadableStreamDefaultReaderRead(reader) {
    var stream = reader._ownerReadableStream;
    assert(stream !== undefined);
    stream._disturbed = true;
    if (stream._state === 'closed') {
      return Promise.resolve(CreateIterResultObject(undefined, true));
    }
    if (stream._state === 'errored') {
      return Promise.reject(stream._storedError);
    }
    assert(stream._state === 'readable');
    return stream._readableStreamController.__pullSteps();
  }
  var ReadableStreamDefaultController = function () {
    function ReadableStreamDefaultController(stream, underlyingSource, size, highWaterMark) {
      _classCallCheck(this, ReadableStreamDefaultController);
      if (IsReadableStream(stream) === false) {
        throw new TypeError('ReadableStreamDefaultController can only be constructed with a ReadableStream instance');
      }
      if (stream._readableStreamController !== undefined) {
        throw new TypeError('ReadableStreamDefaultController instances can only be created by the ReadableStream constructor');
      }
      this._controlledReadableStream = stream;
      this._underlyingSource = underlyingSource;
      this._queue = undefined;
      this._queueTotalSize = undefined;
      ResetQueue(this);
      this._started = false;
      this._closeRequested = false;
      this._pullAgain = false;
      this._pulling = false;
      var normalizedStrategy = ValidateAndNormalizeQueuingStrategy(size, highWaterMark);
      this._strategySize = normalizedStrategy.size;
      this._strategyHWM = normalizedStrategy.highWaterMark;
      var controller = this;
      var startResult = InvokeOrNoop(underlyingSource, 'start', [this]);
      Promise.resolve(startResult).then(function () {
        controller._started = true;
        assert(controller._pulling === false);
        assert(controller._pullAgain === false);
        ReadableStreamDefaultControllerCallPullIfNeeded(controller);
      }, function (r) {
        ReadableStreamDefaultControllerErrorIfNeeded(controller, r);
      }).catch(rethrowAssertionErrorRejection);
    }
    _createClass(ReadableStreamDefaultController, [{
      key: 'close',
      value: function close() {
        if (IsReadableStreamDefaultController(this) === false) {
          throw defaultControllerBrandCheckException('close');
        }
        if (this._closeRequested === true) {
          throw new TypeError('The stream has already been closed; do not close it again!');
        }
        var state = this._controlledReadableStream._state;
        if (state !== 'readable') {
          throw new TypeError('The stream (in ' + state + ' state) is not in the readable state and cannot be closed');
        }
        ReadableStreamDefaultControllerClose(this);
      }
    }, {
      key: 'enqueue',
      value: function enqueue(chunk) {
        if (IsReadableStreamDefaultController(this) === false) {
          throw defaultControllerBrandCheckException('enqueue');
        }
        if (this._closeRequested === true) {
          throw new TypeError('stream is closed or draining');
        }
        var state = this._controlledReadableStream._state;
        if (state !== 'readable') {
          throw new TypeError('The stream (in ' + state + ' state) is not in the readable state and cannot be enqueued to');
        }
        return ReadableStreamDefaultControllerEnqueue(this, chunk);
      }
    }, {
      key: 'error',
      value: function error(e) {
        if (IsReadableStreamDefaultController(this) === false) {
          throw defaultControllerBrandCheckException('error');
        }
        var stream = this._controlledReadableStream;
        if (stream._state !== 'readable') {
          throw new TypeError('The stream is ' + stream._state + ' and so cannot be errored');
        }
        ReadableStreamDefaultControllerError(this, e);
      }
    }, {
      key: '__cancelSteps',
      value: function __cancelSteps(reason) {
        ResetQueue(this);
        return PromiseInvokeOrNoop(this._underlyingSource, 'cancel', [reason]);
      }
    }, {
      key: '__pullSteps',
      value: function __pullSteps() {
        var stream = this._controlledReadableStream;
        if (this._queue.length > 0) {
          var chunk = DequeueValue(this);
          if (this._closeRequested === true && this._queue.length === 0) {
            ReadableStreamClose(stream);
          } else {
            ReadableStreamDefaultControllerCallPullIfNeeded(this);
          }
          return Promise.resolve(CreateIterResultObject(chunk, false));
        }
        var pendingPromise = ReadableStreamAddReadRequest(stream);
        ReadableStreamDefaultControllerCallPullIfNeeded(this);
        return pendingPromise;
      }
    }, {
      key: 'desiredSize',
      get: function get() {
        if (IsReadableStreamDefaultController(this) === false) {
          throw defaultControllerBrandCheckException('desiredSize');
        }
        return ReadableStreamDefaultControllerGetDesiredSize(this);
      }
    }]);
    return ReadableStreamDefaultController;
  }();
  function IsReadableStreamDefaultController(x) {
    if (!typeIsObject(x)) {
      return false;
    }
    if (!Object.prototype.hasOwnProperty.call(x, '_underlyingSource')) {
      return false;
    }
    return true;
  }
  function ReadableStreamDefaultControllerCallPullIfNeeded(controller) {
    var shouldPull = ReadableStreamDefaultControllerShouldCallPull(controller);
    if (shouldPull === false) {
      return undefined;
    }
    if (controller._pulling === true) {
      controller._pullAgain = true;
      return undefined;
    }
    assert(controller._pullAgain === false);
    controller._pulling = true;
    var pullPromise = PromiseInvokeOrNoop(controller._underlyingSource, 'pull', [controller]);
    pullPromise.then(function () {
      controller._pulling = false;
      if (controller._pullAgain === true) {
        controller._pullAgain = false;
        return ReadableStreamDefaultControllerCallPullIfNeeded(controller);
      }
      return undefined;
    }, function (e) {
      ReadableStreamDefaultControllerErrorIfNeeded(controller, e);
    }).catch(rethrowAssertionErrorRejection);
    return undefined;
  }
  function ReadableStreamDefaultControllerShouldCallPull(controller) {
    var stream = controller._controlledReadableStream;
    if (stream._state === 'closed' || stream._state === 'errored') {
      return false;
    }
    if (controller._closeRequested === true) {
      return false;
    }
    if (controller._started === false) {
      return false;
    }
    if (IsReadableStreamLocked(stream) === true && ReadableStreamGetNumReadRequests(stream) > 0) {
      return true;
    }
    var desiredSize = ReadableStreamDefaultControllerGetDesiredSize(controller);
    if (desiredSize > 0) {
      return true;
    }
    return false;
  }
  function ReadableStreamDefaultControllerClose(controller) {
    var stream = controller._controlledReadableStream;
    assert(controller._closeRequested === false);
    assert(stream._state === 'readable');
    controller._closeRequested = true;
    if (controller._queue.length === 0) {
      ReadableStreamClose(stream);
    }
  }
  function ReadableStreamDefaultControllerEnqueue(controller, chunk) {
    var stream = controller._controlledReadableStream;
    assert(controller._closeRequested === false);
    assert(stream._state === 'readable');
    if (IsReadableStreamLocked(stream) === true && ReadableStreamGetNumReadRequests(stream) > 0) {
      ReadableStreamFulfillReadRequest(stream, chunk, false);
    } else {
      var chunkSize = 1;
      if (controller._strategySize !== undefined) {
        var strategySize = controller._strategySize;
        try {
          chunkSize = strategySize(chunk);
        } catch (chunkSizeE) {
          ReadableStreamDefaultControllerErrorIfNeeded(controller, chunkSizeE);
          throw chunkSizeE;
        }
      }
      try {
        EnqueueValueWithSize(controller, chunk, chunkSize);
      } catch (enqueueE) {
        ReadableStreamDefaultControllerErrorIfNeeded(controller, enqueueE);
        throw enqueueE;
      }
    }
    ReadableStreamDefaultControllerCallPullIfNeeded(controller);
    return undefined;
  }
  function ReadableStreamDefaultControllerError(controller, e) {
    var stream = controller._controlledReadableStream;
    assert(stream._state === 'readable');
    ResetQueue(controller);
    ReadableStreamError(stream, e);
  }
  function ReadableStreamDefaultControllerErrorIfNeeded(controller, e) {
    if (controller._controlledReadableStream._state === 'readable') {
      ReadableStreamDefaultControllerError(controller, e);
    }
  }
  function ReadableStreamDefaultControllerGetDesiredSize(controller) {
    var stream = controller._controlledReadableStream;
    var state = stream._state;
    if (state === 'errored') {
      return null;
    }
    if (state === 'closed') {
      return 0;
    }
    return controller._strategyHWM - controller._queueTotalSize;
  }
  var ReadableStreamBYOBRequest = function () {
    function ReadableStreamBYOBRequest(controller, view) {
      _classCallCheck(this, ReadableStreamBYOBRequest);
      this._associatedReadableByteStreamController = controller;
      this._view = view;
    }
    _createClass(ReadableStreamBYOBRequest, [{
      key: 'respond',
      value: function respond(bytesWritten) {
        if (IsReadableStreamBYOBRequest(this) === false) {
          throw byobRequestBrandCheckException('respond');
        }
        if (this._associatedReadableByteStreamController === undefined) {
          throw new TypeError('This BYOB request has been invalidated');
        }
        ReadableByteStreamControllerRespond(this._associatedReadableByteStreamController, bytesWritten);
      }
    }, {
      key: 'respondWithNewView',
      value: function respondWithNewView(view) {
        if (IsReadableStreamBYOBRequest(this) === false) {
          throw byobRequestBrandCheckException('respond');
        }
        if (this._associatedReadableByteStreamController === undefined) {
          throw new TypeError('This BYOB request has been invalidated');
        }
        if (!ArrayBuffer.isView(view)) {
          throw new TypeError('You can only respond with array buffer views');
        }
        ReadableByteStreamControllerRespondWithNewView(this._associatedReadableByteStreamController, view);
      }
    }, {
      key: 'view',
      get: function get() {
        return this._view;
      }
    }]);
    return ReadableStreamBYOBRequest;
  }();
  var ReadableByteStreamController = function () {
    function ReadableByteStreamController(stream, underlyingByteSource, highWaterMark) {
      _classCallCheck(this, ReadableByteStreamController);
      if (IsReadableStream(stream) === false) {
        throw new TypeError('ReadableByteStreamController can only be constructed with a ReadableStream instance given ' + 'a byte source');
      }
      if (stream._readableStreamController !== undefined) {
        throw new TypeError('ReadableByteStreamController instances can only be created by the ReadableStream constructor given a byte ' + 'source');
      }
      this._controlledReadableStream = stream;
      this._underlyingByteSource = underlyingByteSource;
      this._pullAgain = false;
      this._pulling = false;
      ReadableByteStreamControllerClearPendingPullIntos(this);
      this._queue = this._queueTotalSize = undefined;
      ResetQueue(this);
      this._closeRequested = false;
      this._started = false;
      this._strategyHWM = ValidateAndNormalizeHighWaterMark(highWaterMark);
      var autoAllocateChunkSize = underlyingByteSource.autoAllocateChunkSize;
      if (autoAllocateChunkSize !== undefined) {
        if (Number.isInteger(autoAllocateChunkSize) === false || autoAllocateChunkSize <= 0) {
          throw new RangeError('autoAllocateChunkSize must be a positive integer');
        }
      }
      this._autoAllocateChunkSize = autoAllocateChunkSize;
      this._pendingPullIntos = [];
      var controller = this;
      var startResult = InvokeOrNoop(underlyingByteSource, 'start', [this]);
      Promise.resolve(startResult).then(function () {
        controller._started = true;
        assert(controller._pulling === false);
        assert(controller._pullAgain === false);
        ReadableByteStreamControllerCallPullIfNeeded(controller);
      }, function (r) {
        if (stream._state === 'readable') {
          ReadableByteStreamControllerError(controller, r);
        }
      }).catch(rethrowAssertionErrorRejection);
    }
    _createClass(ReadableByteStreamController, [{
      key: 'close',
      value: function close() {
        if (IsReadableByteStreamController(this) === false) {
          throw byteStreamControllerBrandCheckException('close');
        }
        if (this._closeRequested === true) {
          throw new TypeError('The stream has already been closed; do not close it again!');
        }
        var state = this._controlledReadableStream._state;
        if (state !== 'readable') {
          throw new TypeError('The stream (in ' + state + ' state) is not in the readable state and cannot be closed');
        }
        ReadableByteStreamControllerClose(this);
      }
    }, {
      key: 'enqueue',
      value: function enqueue(chunk) {
        if (IsReadableByteStreamController(this) === false) {
          throw byteStreamControllerBrandCheckException('enqueue');
        }
        if (this._closeRequested === true) {
          throw new TypeError('stream is closed or draining');
        }
        var state = this._controlledReadableStream._state;
        if (state !== 'readable') {
          throw new TypeError('The stream (in ' + state + ' state) is not in the readable state and cannot be enqueued to');
        }
        if (!ArrayBuffer.isView(chunk)) {
          throw new TypeError('You can only enqueue array buffer views when using a ReadableByteStreamController');
        }
        ReadableByteStreamControllerEnqueue(this, chunk);
      }
    }, {
      key: 'error',
      value: function error(e) {
        if (IsReadableByteStreamController(this) === false) {
          throw byteStreamControllerBrandCheckException('error');
        }
        var stream = this._controlledReadableStream;
        if (stream._state !== 'readable') {
          throw new TypeError('The stream is ' + stream._state + ' and so cannot be errored');
        }
        ReadableByteStreamControllerError(this, e);
      }
    }, {
      key: '__cancelSteps',
      value: function __cancelSteps(reason) {
        if (this._pendingPullIntos.length > 0) {
          var firstDescriptor = this._pendingPullIntos[0];
          firstDescriptor.bytesFilled = 0;
        }
        ResetQueue(this);
        return PromiseInvokeOrNoop(this._underlyingByteSource, 'cancel', [reason]);
      }
    }, {
      key: '__pullSteps',
      value: function __pullSteps() {
        var stream = this._controlledReadableStream;
        assert(ReadableStreamHasDefaultReader(stream) === true);
        if (this._queueTotalSize > 0) {
          assert(ReadableStreamGetNumReadRequests(stream) === 0);
          var entry = this._queue.shift();
          this._queueTotalSize -= entry.byteLength;
          ReadableByteStreamControllerHandleQueueDrain(this);
          var view = void 0;
          try {
            view = new Uint8Array(entry.buffer, entry.byteOffset, entry.byteLength);
          } catch (viewE) {
            return Promise.reject(viewE);
          }
          return Promise.resolve(CreateIterResultObject(view, false));
        }
        var autoAllocateChunkSize = this._autoAllocateChunkSize;
        if (autoAllocateChunkSize !== undefined) {
          var buffer = void 0;
          try {
            buffer = new ArrayBuffer(autoAllocateChunkSize);
          } catch (bufferE) {
            return Promise.reject(bufferE);
          }
          var pullIntoDescriptor = {
            buffer: buffer,
            byteOffset: 0,
            byteLength: autoAllocateChunkSize,
            bytesFilled: 0,
            elementSize: 1,
            ctor: Uint8Array,
            readerType: 'default'
          };
          this._pendingPullIntos.push(pullIntoDescriptor);
        }
        var promise = ReadableStreamAddReadRequest(stream);
        ReadableByteStreamControllerCallPullIfNeeded(this);
        return promise;
      }
    }, {
      key: 'byobRequest',
      get: function get() {
        if (IsReadableByteStreamController(this) === false) {
          throw byteStreamControllerBrandCheckException('byobRequest');
        }
        if (this._byobRequest === undefined && this._pendingPullIntos.length > 0) {
          var firstDescriptor = this._pendingPullIntos[0];
          var view = new Uint8Array(firstDescriptor.buffer, firstDescriptor.byteOffset + firstDescriptor.bytesFilled, firstDescriptor.byteLength - firstDescriptor.bytesFilled);
          this._byobRequest = new ReadableStreamBYOBRequest(this, view);
        }
        return this._byobRequest;
      }
    }, {
      key: 'desiredSize',
      get: function get() {
        if (IsReadableByteStreamController(this) === false) {
          throw byteStreamControllerBrandCheckException('desiredSize');
        }
        return ReadableByteStreamControllerGetDesiredSize(this);
      }
    }]);
    return ReadableByteStreamController;
  }();
  function IsReadableByteStreamController(x) {
    if (!typeIsObject(x)) {
      return false;
    }
    if (!Object.prototype.hasOwnProperty.call(x, '_underlyingByteSource')) {
      return false;
    }
    return true;
  }
  function IsReadableStreamBYOBRequest(x) {
    if (!typeIsObject(x)) {
      return false;
    }
    if (!Object.prototype.hasOwnProperty.call(x, '_associatedReadableByteStreamController')) {
      return false;
    }
    return true;
  }
  function ReadableByteStreamControllerCallPullIfNeeded(controller) {
    var shouldPull = ReadableByteStreamControllerShouldCallPull(controller);
    if (shouldPull === false) {
      return undefined;
    }
    if (controller._pulling === true) {
      controller._pullAgain = true;
      return undefined;
    }
    assert(controller._pullAgain === false);
    controller._pulling = true;
    var pullPromise = PromiseInvokeOrNoop(controller._underlyingByteSource, 'pull', [controller]);
    pullPromise.then(function () {
      controller._pulling = false;
      if (controller._pullAgain === true) {
        controller._pullAgain = false;
        ReadableByteStreamControllerCallPullIfNeeded(controller);
      }
    }, function (e) {
      if (controller._controlledReadableStream._state === 'readable') {
        ReadableByteStreamControllerError(controller, e);
      }
    }).catch(rethrowAssertionErrorRejection);
    return undefined;
  }
  function ReadableByteStreamControllerClearPendingPullIntos(controller) {
    ReadableByteStreamControllerInvalidateBYOBRequest(controller);
    controller._pendingPullIntos = [];
  }
  function ReadableByteStreamControllerCommitPullIntoDescriptor(stream, pullIntoDescriptor) {
    assert(stream._state !== 'errored', 'state must not be errored');
    var done = false;
    if (stream._state === 'closed') {
      assert(pullIntoDescriptor.bytesFilled === 0);
      done = true;
    }
    var filledView = ReadableByteStreamControllerConvertPullIntoDescriptor(pullIntoDescriptor);
    if (pullIntoDescriptor.readerType === 'default') {
      ReadableStreamFulfillReadRequest(stream, filledView, done);
    } else {
      assert(pullIntoDescriptor.readerType === 'byob');
      ReadableStreamFulfillReadIntoRequest(stream, filledView, done);
    }
  }
  function ReadableByteStreamControllerConvertPullIntoDescriptor(pullIntoDescriptor) {
    var bytesFilled = pullIntoDescriptor.bytesFilled;
    var elementSize = pullIntoDescriptor.elementSize;
    assert(bytesFilled <= pullIntoDescriptor.byteLength);
    assert(bytesFilled % elementSize === 0);
    return new pullIntoDescriptor.ctor(pullIntoDescriptor.buffer, pullIntoDescriptor.byteOffset, bytesFilled / elementSize);
  }
  function ReadableByteStreamControllerEnqueueChunkToQueue(controller, buffer, byteOffset, byteLength) {
    controller._queue.push({
      buffer: buffer,
      byteOffset: byteOffset,
      byteLength: byteLength
    });
    controller._queueTotalSize += byteLength;
  }
  function ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(controller, pullIntoDescriptor) {
    var elementSize = pullIntoDescriptor.elementSize;
    var currentAlignedBytes = pullIntoDescriptor.bytesFilled - pullIntoDescriptor.bytesFilled % elementSize;
    var maxBytesToCopy = Math.min(controller._queueTotalSize, pullIntoDescriptor.byteLength - pullIntoDescriptor.bytesFilled);
    var maxBytesFilled = pullIntoDescriptor.bytesFilled + maxBytesToCopy;
    var maxAlignedBytes = maxBytesFilled - maxBytesFilled % elementSize;
    var totalBytesToCopyRemaining = maxBytesToCopy;
    var ready = false;
    if (maxAlignedBytes > currentAlignedBytes) {
      totalBytesToCopyRemaining = maxAlignedBytes - pullIntoDescriptor.bytesFilled;
      ready = true;
    }
    var queue = controller._queue;
    while (totalBytesToCopyRemaining > 0) {
      var headOfQueue = queue[0];
      var bytesToCopy = Math.min(totalBytesToCopyRemaining, headOfQueue.byteLength);
      var destStart = pullIntoDescriptor.byteOffset + pullIntoDescriptor.bytesFilled;
      ArrayBufferCopy(pullIntoDescriptor.buffer, destStart, headOfQueue.buffer, headOfQueue.byteOffset, bytesToCopy);
      if (headOfQueue.byteLength === bytesToCopy) {
        queue.shift();
      } else {
        headOfQueue.byteOffset += bytesToCopy;
        headOfQueue.byteLength -= bytesToCopy;
      }
      controller._queueTotalSize -= bytesToCopy;
      ReadableByteStreamControllerFillHeadPullIntoDescriptor(controller, bytesToCopy, pullIntoDescriptor);
      totalBytesToCopyRemaining -= bytesToCopy;
    }
    if (ready === false) {
      assert(controller._queueTotalSize === 0, 'queue must be empty');
      assert(pullIntoDescriptor.bytesFilled > 0);
      assert(pullIntoDescriptor.bytesFilled < pullIntoDescriptor.elementSize);
    }
    return ready;
  }
  function ReadableByteStreamControllerFillHeadPullIntoDescriptor(controller, size, pullIntoDescriptor) {
    assert(controller._pendingPullIntos.length === 0 || controller._pendingPullIntos[0] === pullIntoDescriptor);
    ReadableByteStreamControllerInvalidateBYOBRequest(controller);
    pullIntoDescriptor.bytesFilled += size;
  }
  function ReadableByteStreamControllerHandleQueueDrain(controller) {
    assert(controller._controlledReadableStream._state === 'readable');
    if (controller._queueTotalSize === 0 && controller._closeRequested === true) {
      ReadableStreamClose(controller._controlledReadableStream);
    } else {
      ReadableByteStreamControllerCallPullIfNeeded(controller);
    }
  }
  function ReadableByteStreamControllerInvalidateBYOBRequest(controller) {
    if (controller._byobRequest === undefined) {
      return;
    }
    controller._byobRequest._associatedReadableByteStreamController = undefined;
    controller._byobRequest._view = undefined;
    controller._byobRequest = undefined;
  }
  function ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller) {
    assert(controller._closeRequested === false);
    while (controller._pendingPullIntos.length > 0) {
      if (controller._queueTotalSize === 0) {
        return;
      }
      var pullIntoDescriptor = controller._pendingPullIntos[0];
      if (ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(controller, pullIntoDescriptor) === true) {
        ReadableByteStreamControllerShiftPendingPullInto(controller);
        ReadableByteStreamControllerCommitPullIntoDescriptor(controller._controlledReadableStream, pullIntoDescriptor);
      }
    }
  }
  function ReadableByteStreamControllerPullInto(controller, view) {
    var stream = controller._controlledReadableStream;
    var elementSize = 1;
    if (view.constructor !== DataView) {
      elementSize = view.constructor.BYTES_PER_ELEMENT;
    }
    var ctor = view.constructor;
    var pullIntoDescriptor = {
      buffer: view.buffer,
      byteOffset: view.byteOffset,
      byteLength: view.byteLength,
      bytesFilled: 0,
      elementSize: elementSize,
      ctor: ctor,
      readerType: 'byob'
    };
    if (controller._pendingPullIntos.length > 0) {
      pullIntoDescriptor.buffer = TransferArrayBuffer(pullIntoDescriptor.buffer);
      controller._pendingPullIntos.push(pullIntoDescriptor);
      return ReadableStreamAddReadIntoRequest(stream);
    }
    if (stream._state === 'closed') {
      var emptyView = new view.constructor(pullIntoDescriptor.buffer, pullIntoDescriptor.byteOffset, 0);
      return Promise.resolve(CreateIterResultObject(emptyView, true));
    }
    if (controller._queueTotalSize > 0) {
      if (ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(controller, pullIntoDescriptor) === true) {
        var filledView = ReadableByteStreamControllerConvertPullIntoDescriptor(pullIntoDescriptor);
        ReadableByteStreamControllerHandleQueueDrain(controller);
        return Promise.resolve(CreateIterResultObject(filledView, false));
      }
      if (controller._closeRequested === true) {
        var e = new TypeError('Insufficient bytes to fill elements in the given buffer');
        ReadableByteStreamControllerError(controller, e);
        return Promise.reject(e);
      }
    }
    pullIntoDescriptor.buffer = TransferArrayBuffer(pullIntoDescriptor.buffer);
    controller._pendingPullIntos.push(pullIntoDescriptor);
    var promise = ReadableStreamAddReadIntoRequest(stream);
    ReadableByteStreamControllerCallPullIfNeeded(controller);
    return promise;
  }
  function ReadableByteStreamControllerRespondInClosedState(controller, firstDescriptor) {
    firstDescriptor.buffer = TransferArrayBuffer(firstDescriptor.buffer);
    assert(firstDescriptor.bytesFilled === 0, 'bytesFilled must be 0');
    var stream = controller._controlledReadableStream;
    if (ReadableStreamHasBYOBReader(stream) === true) {
      while (ReadableStreamGetNumReadIntoRequests(stream) > 0) {
        var pullIntoDescriptor = ReadableByteStreamControllerShiftPendingPullInto(controller);
        ReadableByteStreamControllerCommitPullIntoDescriptor(stream, pullIntoDescriptor);
      }
    }
  }
  function ReadableByteStreamControllerRespondInReadableState(controller, bytesWritten, pullIntoDescriptor) {
    if (pullIntoDescriptor.bytesFilled + bytesWritten > pullIntoDescriptor.byteLength) {
      throw new RangeError('bytesWritten out of range');
    }
    ReadableByteStreamControllerFillHeadPullIntoDescriptor(controller, bytesWritten, pullIntoDescriptor);
    if (pullIntoDescriptor.bytesFilled < pullIntoDescriptor.elementSize) {
      return;
    }
    ReadableByteStreamControllerShiftPendingPullInto(controller);
    var remainderSize = pullIntoDescriptor.bytesFilled % pullIntoDescriptor.elementSize;
    if (remainderSize > 0) {
      var end = pullIntoDescriptor.byteOffset + pullIntoDescriptor.bytesFilled;
      var remainder = pullIntoDescriptor.buffer.slice(end - remainderSize, end);
      ReadableByteStreamControllerEnqueueChunkToQueue(controller, remainder, 0, remainder.byteLength);
    }
    pullIntoDescriptor.buffer = TransferArrayBuffer(pullIntoDescriptor.buffer);
    pullIntoDescriptor.bytesFilled -= remainderSize;
    ReadableByteStreamControllerCommitPullIntoDescriptor(controller._controlledReadableStream, pullIntoDescriptor);
    ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller);
  }
  function ReadableByteStreamControllerRespondInternal(controller, bytesWritten) {
    var firstDescriptor = controller._pendingPullIntos[0];
    var stream = controller._controlledReadableStream;
    if (stream._state === 'closed') {
      if (bytesWritten !== 0) {
        throw new TypeError('bytesWritten must be 0 when calling respond() on a closed stream');
      }
      ReadableByteStreamControllerRespondInClosedState(controller, firstDescriptor);
    } else {
      assert(stream._state === 'readable');
      ReadableByteStreamControllerRespondInReadableState(controller, bytesWritten, firstDescriptor);
    }
  }
  function ReadableByteStreamControllerShiftPendingPullInto(controller) {
    var descriptor = controller._pendingPullIntos.shift();
    ReadableByteStreamControllerInvalidateBYOBRequest(controller);
    return descriptor;
  }
  function ReadableByteStreamControllerShouldCallPull(controller) {
    var stream = controller._controlledReadableStream;
    if (stream._state !== 'readable') {
      return false;
    }
    if (controller._closeRequested === true) {
      return false;
    }
    if (controller._started === false) {
      return false;
    }
    if (ReadableStreamHasDefaultReader(stream) === true && ReadableStreamGetNumReadRequests(stream) > 0) {
      return true;
    }
    if (ReadableStreamHasBYOBReader(stream) === true && ReadableStreamGetNumReadIntoRequests(stream) > 0) {
      return true;
    }
    if (ReadableByteStreamControllerGetDesiredSize(controller) > 0) {
      return true;
    }
    return false;
  }
  function ReadableByteStreamControllerClose(controller) {
    var stream = controller._controlledReadableStream;
    assert(controller._closeRequested === false);
    assert(stream._state === 'readable');
    if (controller._queueTotalSize > 0) {
      controller._closeRequested = true;
      return;
    }
    if (controller._pendingPullIntos.length > 0) {
      var firstPendingPullInto = controller._pendingPullIntos[0];
      if (firstPendingPullInto.bytesFilled > 0) {
        var e = new TypeError('Insufficient bytes to fill elements in the given buffer');
        ReadableByteStreamControllerError(controller, e);
        throw e;
      }
    }
    ReadableStreamClose(stream);
  }
  function ReadableByteStreamControllerEnqueue(controller, chunk) {
    var stream = controller._controlledReadableStream;
    assert(controller._closeRequested === false);
    assert(stream._state === 'readable');
    var buffer = chunk.buffer;
    var byteOffset = chunk.byteOffset;
    var byteLength = chunk.byteLength;
    var transferredBuffer = TransferArrayBuffer(buffer);
    if (ReadableStreamHasDefaultReader(stream) === true) {
      if (ReadableStreamGetNumReadRequests(stream) === 0) {
        ReadableByteStreamControllerEnqueueChunkToQueue(controller, transferredBuffer, byteOffset, byteLength);
      } else {
        assert(controller._queue.length === 0);
        var transferredView = new Uint8Array(transferredBuffer, byteOffset, byteLength);
        ReadableStreamFulfillReadRequest(stream, transferredView, false);
      }
    } else if (ReadableStreamHasBYOBReader(stream) === true) {
      ReadableByteStreamControllerEnqueueChunkToQueue(controller, transferredBuffer, byteOffset, byteLength);
      ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller);
    } else {
      assert(IsReadableStreamLocked(stream) === false, 'stream must not be locked');
      ReadableByteStreamControllerEnqueueChunkToQueue(controller, transferredBuffer, byteOffset, byteLength);
    }
  }
  function ReadableByteStreamControllerError(controller, e) {
    var stream = controller._controlledReadableStream;
    assert(stream._state === 'readable');
    ReadableByteStreamControllerClearPendingPullIntos(controller);
    ResetQueue(controller);
    ReadableStreamError(stream, e);
  }
  function ReadableByteStreamControllerGetDesiredSize(controller) {
    var stream = controller._controlledReadableStream;
    var state = stream._state;
    if (state === 'errored') {
      return null;
    }
    if (state === 'closed') {
      return 0;
    }
    return controller._strategyHWM - controller._queueTotalSize;
  }
  function ReadableByteStreamControllerRespond(controller, bytesWritten) {
    bytesWritten = Number(bytesWritten);
    if (IsFiniteNonNegativeNumber(bytesWritten) === false) {
      throw new RangeError('bytesWritten must be a finite');
    }
    assert(controller._pendingPullIntos.length > 0);
    ReadableByteStreamControllerRespondInternal(controller, bytesWritten);
  }
  function ReadableByteStreamControllerRespondWithNewView(controller, view) {
    assert(controller._pendingPullIntos.length > 0);
    var firstDescriptor = controller._pendingPullIntos[0];
    if (firstDescriptor.byteOffset + firstDescriptor.bytesFilled !== view.byteOffset) {
      throw new RangeError('The region specified by view does not match byobRequest');
    }
    if (firstDescriptor.byteLength !== view.byteLength) {
      throw new RangeError('The buffer of view has different capacity than byobRequest');
    }
    firstDescriptor.buffer = view.buffer;
    ReadableByteStreamControllerRespondInternal(controller, view.byteLength);
  }
  function streamBrandCheckException(name) {
    return new TypeError('ReadableStream.prototype.' + name + ' can only be used on a ReadableStream');
  }
  function readerLockException(name) {
    return new TypeError('Cannot ' + name + ' a stream using a released reader');
  }
  function defaultReaderBrandCheckException(name) {
    return new TypeError('ReadableStreamDefaultReader.prototype.' + name + ' can only be used on a ReadableStreamDefaultReader');
  }
  function defaultReaderClosedPromiseInitialize(reader) {
    reader._closedPromise = new Promise(function (resolve, reject) {
      reader._closedPromise_resolve = resolve;
      reader._closedPromise_reject = reject;
    });
  }
  function defaultReaderClosedPromiseInitializeAsRejected(reader, reason) {
    reader._closedPromise = Promise.reject(reason);
    reader._closedPromise_resolve = undefined;
    reader._closedPromise_reject = undefined;
  }
  function defaultReaderClosedPromiseInitializeAsResolved(reader) {
    reader._closedPromise = Promise.resolve(undefined);
    reader._closedPromise_resolve = undefined;
    reader._closedPromise_reject = undefined;
  }
  function defaultReaderClosedPromiseReject(reader, reason) {
    assert(reader._closedPromise_resolve !== undefined);
    assert(reader._closedPromise_reject !== undefined);
    reader._closedPromise_reject(reason);
    reader._closedPromise_resolve = undefined;
    reader._closedPromise_reject = undefined;
  }
  function defaultReaderClosedPromiseResetToRejected(reader, reason) {
    assert(reader._closedPromise_resolve === undefined);
    assert(reader._closedPromise_reject === undefined);
    reader._closedPromise = Promise.reject(reason);
  }
  function defaultReaderClosedPromiseResolve(reader) {
    assert(reader._closedPromise_resolve !== undefined);
    assert(reader._closedPromise_reject !== undefined);
    reader._closedPromise_resolve(undefined);
    reader._closedPromise_resolve = undefined;
    reader._closedPromise_reject = undefined;
  }
  function byobReaderBrandCheckException(name) {
    return new TypeError('ReadableStreamBYOBReader.prototype.' + name + ' can only be used on a ReadableStreamBYOBReader');
  }
  function defaultControllerBrandCheckException(name) {
    return new TypeError('ReadableStreamDefaultController.prototype.' + name + ' can only be used on a ReadableStreamDefaultController');
  }
  function byobRequestBrandCheckException(name) {
    return new TypeError('ReadableStreamBYOBRequest.prototype.' + name + ' can only be used on a ReadableStreamBYOBRequest');
  }
  function byteStreamControllerBrandCheckException(name) {
    return new TypeError('ReadableByteStreamController.prototype.' + name + ' can only be used on a ReadableByteStreamController');
  }
  function ifIsObjectAndHasAPromiseIsHandledInternalSlotSetPromiseIsHandledToTrue(promise) {
    try {
      Promise.prototype.then.call(promise, undefined, function () {});
    } catch (e) {}
  }
}, function (module, exports, __w_pdfjs_require__) {
  "use strict";

  var transformStream = __w_pdfjs_require__(6);
  var readableStream = __w_pdfjs_require__(4);
  var writableStream = __w_pdfjs_require__(2);
  exports.TransformStream = transformStream.TransformStream;
  exports.ReadableStream = readableStream.ReadableStream;
  exports.IsReadableStreamDisturbed = readableStream.IsReadableStreamDisturbed;
  exports.ReadableStreamDefaultControllerClose = readableStream.ReadableStreamDefaultControllerClose;
  exports.ReadableStreamDefaultControllerEnqueue = readableStream.ReadableStreamDefaultControllerEnqueue;
  exports.ReadableStreamDefaultControllerError = readableStream.ReadableStreamDefaultControllerError;
  exports.ReadableStreamDefaultControllerGetDesiredSize = readableStream.ReadableStreamDefaultControllerGetDesiredSize;
  exports.AcquireWritableStreamDefaultWriter = writableStream.AcquireWritableStreamDefaultWriter;
  exports.IsWritableStream = writableStream.IsWritableStream;
  exports.IsWritableStreamLocked = writableStream.IsWritableStreamLocked;
  exports.WritableStream = writableStream.WritableStream;
  exports.WritableStreamAbort = writableStream.WritableStreamAbort;
  exports.WritableStreamDefaultControllerError = writableStream.WritableStreamDefaultControllerError;
  exports.WritableStreamDefaultWriterCloseWithErrorPropagation = writableStream.WritableStreamDefaultWriterCloseWithErrorPropagation;
  exports.WritableStreamDefaultWriterRelease = writableStream.WritableStreamDefaultWriterRelease;
  exports.WritableStreamDefaultWriterWrite = writableStream.WritableStreamDefaultWriterWrite;
}, function (module, exports, __w_pdfjs_require__) {
  "use strict";

  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");
    }
  }
  var _require = __w_pdfjs_require__(1),
      assert = _require.assert;
  var _require2 = __w_pdfjs_require__(0),
      InvokeOrNoop = _require2.InvokeOrNoop,
      PromiseInvokeOrPerformFallback = _require2.PromiseInvokeOrPerformFallback,
      PromiseInvokeOrNoop = _require2.PromiseInvokeOrNoop,
      typeIsObject = _require2.typeIsObject;
  var _require3 = __w_pdfjs_require__(4),
      ReadableStream = _require3.ReadableStream,
      ReadableStreamDefaultControllerClose = _require3.ReadableStreamDefaultControllerClose,
      ReadableStreamDefaultControllerEnqueue = _require3.ReadableStreamDefaultControllerEnqueue,
      ReadableStreamDefaultControllerError = _require3.ReadableStreamDefaultControllerError,
      ReadableStreamDefaultControllerGetDesiredSize = _require3.ReadableStreamDefaultControllerGetDesiredSize;
  var _require4 = __w_pdfjs_require__(2),
      WritableStream = _require4.WritableStream,
      WritableStreamDefaultControllerError = _require4.WritableStreamDefaultControllerError;
  function TransformStreamCloseReadable(transformStream) {
    if (transformStream._errored === true) {
      throw new TypeError('TransformStream is already errored');
    }
    if (transformStream._readableClosed === true) {
      throw new TypeError('Readable side is already closed');
    }
    TransformStreamCloseReadableInternal(transformStream);
  }
  function TransformStreamEnqueueToReadable(transformStream, chunk) {
    if (transformStream._errored === true) {
      throw new TypeError('TransformStream is already errored');
    }
    if (transformStream._readableClosed === true) {
      throw new TypeError('Readable side is already closed');
    }
    var controller = transformStream._readableController;
    try {
      ReadableStreamDefaultControllerEnqueue(controller, chunk);
    } catch (e) {
      transformStream._readableClosed = true;
      TransformStreamErrorIfNeeded(transformStream, e);
      throw transformStream._storedError;
    }
    var desiredSize = ReadableStreamDefaultControllerGetDesiredSize(controller);
    var maybeBackpressure = desiredSize <= 0;
    if (maybeBackpressure === true && transformStream._backpressure === false) {
      TransformStreamSetBackpressure(transformStream, true);
    }
  }
  function TransformStreamError(transformStream, e) {
    if (transformStream._errored === true) {
      throw new TypeError('TransformStream is already errored');
    }
    TransformStreamErrorInternal(transformStream, e);
  }
  function TransformStreamCloseReadableInternal(transformStream) {
    assert(transformStream._errored === false);
    assert(transformStream._readableClosed === false);
    try {
      ReadableStreamDefaultControllerClose(transformStream._readableController);
    } catch (e) {
      assert(false);
    }
    transformStream._readableClosed = true;
  }
  function TransformStreamErrorIfNeeded(transformStream, e) {
    if (transformStream._errored === false) {
      TransformStreamErrorInternal(transformStream, e);
    }
  }
  function TransformStreamErrorInternal(transformStream, e) {
    assert(transformStream._errored === false);
    transformStream._errored = true;
    transformStream._storedError = e;
    if (transformStream._writableDone === false) {
      WritableStreamDefaultControllerError(transformStream._writableController, e);
    }
    if (transformStream._readableClosed === false) {
      ReadableStreamDefaultControllerError(transformStream._readableController, e);
    }
  }
  function TransformStreamReadableReadyPromise(transformStream) {
    assert(transformStream._backpressureChangePromise !== undefined, '_backpressureChangePromise should have been initialized');
    if (transformStream._backpressure === false) {
      return Promise.resolve();
    }
    assert(transformStream._backpressure === true, '_backpressure should have been initialized');
    return transformStream._backpressureChangePromise;
  }
  function TransformStreamSetBackpressure(transformStream, backpressure) {
    assert(transformStream._backpressure !== backpressure, 'TransformStreamSetBackpressure() should be called only when backpressure is changed');
    if (transformStream._backpressureChangePromise !== undefined) {
      transformStream._backpressureChangePromise_resolve(backpressure);
    }
    transformStream._backpressureChangePromise = new Promise(function (resolve) {
      transformStream._backpressureChangePromise_resolve = resolve;
    });
    transformStream._backpressureChangePromise.then(function (resolution) {
      assert(resolution !== backpressure, '_backpressureChangePromise should be fulfilled only when backpressure is changed');
    });
    transformStream._backpressure = backpressure;
  }
  function TransformStreamDefaultTransform(chunk, transformStreamController) {
    var transformStream = transformStreamController._controlledTransformStream;
    TransformStreamEnqueueToReadable(transformStream, chunk);
    return Promise.resolve();
  }
  function TransformStreamTransform(transformStream, chunk) {
    assert(transformStream._errored === false);
    assert(transformStream._transforming === false);
    assert(transformStream._backpressure === false);
    transformStream._transforming = true;
    var transformer = transformStream._transformer;
    var controller = transformStream._transformStreamController;
    var transformPromise = PromiseInvokeOrPerformFallback(transformer, 'transform', [chunk, controller], TransformStreamDefaultTransform, [chunk, controller]);
    return transformPromise.then(function () {
      transformStream._transforming = false;
      return TransformStreamReadableReadyPromise(transformStream);
    }, function (e) {
      TransformStreamErrorIfNeeded(transformStream, e);
      return Promise.reject(e);
    });
  }
  function IsTransformStreamDefaultController(x) {
    if (!typeIsObject(x)) {
      return false;
    }
    if (!Object.prototype.hasOwnProperty.call(x, '_controlledTransformStream')) {
      return false;
    }
    return true;
  }
  function IsTransformStream(x) {
    if (!typeIsObject(x)) {
      return false;
    }
    if (!Object.prototype.hasOwnProperty.call(x, '_transformStreamController')) {
      return false;
    }
    return true;
  }
  var TransformStreamSink = function () {
    function TransformStreamSink(transformStream, startPromise) {
      _classCallCheck(this, TransformStreamSink);
      this._transformStream = transformStream;
      this._startPromise = startPromise;
    }
    _createClass(TransformStreamSink, [{
      key: 'start',
      value: function start(c) {
        var transformStream = this._transformStream;
        transformStream._writableController = c;
        return this._startPromise.then(function () {
          return TransformStreamReadableReadyPromise(transformStream);
        });
      }
    }, {
      key: 'write',
      value: function write(chunk) {
        var transformStream = this._transformStream;
        return TransformStreamTransform(transformStream, chunk);
      }
    }, {
      key: 'abort',
      value: function abort() {
        var transformStream = this._transformStream;
        transformStream._writableDone = true;
        TransformStreamErrorInternal(transformStream, new TypeError('Writable side aborted'));
      }
    }, {
      key: 'close',
      value: function close() {
        var transformStream = this._transformStream;
        assert(transformStream._transforming === false);
        transformStream._writableDone = true;
        var flushPromise = PromiseInvokeOrNoop(transformStream._transformer, 'flush', [transformStream._transformStreamController]);
        return flushPromise.then(function () {
          if (transformStream._errored === true) {
            return Promise.reject(transformStream._storedError);
          }
          if (transformStream._readableClosed === false) {
            TransformStreamCloseReadableInternal(transformStream);
          }
          return Promise.resolve();
        }).catch(function (r) {
          TransformStreamErrorIfNeeded(transformStream, r);
          return Promise.reject(transformStream._storedError);
        });
      }
    }]);
    return TransformStreamSink;
  }();
  var TransformStreamSource = function () {
    function TransformStreamSource(transformStream, startPromise) {
      _classCallCheck(this, TransformStreamSource);
      this._transformStream = transformStream;
      this._startPromise = startPromise;
    }
    _createClass(TransformStreamSource, [{
      key: 'start',
      value: function start(c) {
        var transformStream = this._transformStream;
        transformStream._readableController = c;
        return this._startPromise.then(function () {
          assert(transformStream._backpressureChangePromise !== undefined, '_backpressureChangePromise should have been initialized');
          if (transformStream._backpressure === true) {
            return Promise.resolve();
          }
          assert(transformStream._backpressure === false, '_backpressure should have been initialized');
          return transformStream._backpressureChangePromise;
        });
      }
    }, {
      key: 'pull',
      value: function pull() {
        var transformStream = this._transformStream;
        assert(transformStream._backpressure === true, 'pull() should be never called while _backpressure is false');
        assert(transformStream._backpressureChangePromise !== undefined, '_backpressureChangePromise should have been initialized');
        TransformStreamSetBackpressure(transformStream, false);
        return transformStream._backpressureChangePromise;
      }
    }, {
      key: 'cancel',
      value: function cancel() {
        var transformStream = this._transformStream;
        transformStream._readableClosed = true;
        TransformStreamErrorInternal(transformStream, new TypeError('Readable side canceled'));
      }
    }]);
    return TransformStreamSource;
  }();
  var TransformStreamDefaultController = function () {
    function TransformStreamDefaultController(transformStream) {
      _classCallCheck(this, TransformStreamDefaultController);
      if (IsTransformStream(transformStream) === false) {
        throw new TypeError('TransformStreamDefaultController can only be ' + 'constructed with a TransformStream instance');
      }
      if (transformStream._transformStreamController !== undefined) {
        throw new TypeError('TransformStreamDefaultController instances can ' + 'only be created by the TransformStream constructor');
      }
      this._controlledTransformStream = transformStream;
    }
    _createClass(TransformStreamDefaultController, [{
      key: 'enqueue',
      value: function enqueue(chunk) {
        if (IsTransformStreamDefaultController(this) === false) {
          throw defaultControllerBrandCheckException('enqueue');
        }
        TransformStreamEnqueueToReadable(this._controlledTransformStream, chunk);
      }
    }, {
      key: 'close',
      value: function close() {
        if (IsTransformStreamDefaultController(this) === false) {
          throw defaultControllerBrandCheckException('close');
        }
        TransformStreamCloseReadable(this._controlledTransformStream);
      }
    }, {
      key: 'error',
      value: function error(reason) {
        if (IsTransformStreamDefaultController(this) === false) {
          throw defaultControllerBrandCheckException('error');
        }
        TransformStreamError(this._controlledTransformStream, reason);
      }
    }, {
      key: 'desiredSize',
      get: function get() {
        if (IsTransformStreamDefaultController(this) === false) {
          throw defaultControllerBrandCheckException('desiredSize');
        }
        var transformStream = this._controlledTransformStream;
        var readableController = transformStream._readableController;
        return ReadableStreamDefaultControllerGetDesiredSize(readableController);
      }
    }]);
    return TransformStreamDefaultController;
  }();
  var TransformStream = function () {
    function TransformStream() {
      var transformer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      _classCallCheck(this, TransformStream);
      this._transformer = transformer;
      var readableStrategy = transformer.readableStrategy,
          writableStrategy = transformer.writableStrategy;
      this._transforming = false;
      this._errored = false;
      this._storedError = undefined;
      this._writableController = undefined;
      this._readableController = undefined;
      this._transformStreamController = undefined;
      this._writableDone = false;
      this._readableClosed = false;
      this._backpressure = undefined;
      this._backpressureChangePromise = undefined;
      this._backpressureChangePromise_resolve = undefined;
      this._transformStreamController = new TransformStreamDefaultController(this);
      var startPromise_resolve = void 0;
      var startPromise = new Promise(function (resolve) {
        startPromise_resolve = resolve;
      });
      var source = new TransformStreamSource(this, startPromise);
      this._readable = new ReadableStream(source, readableStrategy);
      var sink = new TransformStreamSink(this, startPromise);
      this._writable = new WritableStream(sink, writableStrategy);
      assert(this._writableController !== undefined);
      assert(this._readableController !== undefined);
      var desiredSize = ReadableStreamDefaultControllerGetDesiredSize(this._readableController);
      TransformStreamSetBackpressure(this, desiredSize <= 0);
      var transformStream = this;
      var startResult = InvokeOrNoop(transformer, 'start', [transformStream._transformStreamController]);
      startPromise_resolve(startResult);
      startPromise.catch(function (e) {
        if (transformStream._errored === false) {
          transformStream._errored = true;
          transformStream._storedError = e;
        }
      });
    }
    _createClass(TransformStream, [{
      key: 'readable',
      get: function get() {
        if (IsTransformStream(this) === false) {
          throw streamBrandCheckException('readable');
        }
        return this._readable;
      }
    }, {
      key: 'writable',
      get: function get() {
        if (IsTransformStream(this) === false) {
          throw streamBrandCheckException('writable');
        }
        return this._writable;
      }
    }]);
    return TransformStream;
  }();
  module.exports = { TransformStream: TransformStream };
  function defaultControllerBrandCheckException(name) {
    return new TypeError('TransformStreamDefaultController.prototype.' + name + ' can only be used on a TransformStreamDefaultController');
  }
  function streamBrandCheckException(name) {
    return new TypeError('TransformStream.prototype.' + name + ' can only be used on a TransformStream');
  }
}, function (module, exports, __w_pdfjs_require__) {
  module.exports = __w_pdfjs_require__(5);
}]));

/***/ }),
/* 13 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.PDFJS = exports.globalScope = undefined;

var _util = __w_pdfjs_require__(0);

var _dom_utils = __w_pdfjs_require__(1);

var _api = __w_pdfjs_require__(4);

var _annotation_layer = __w_pdfjs_require__(6);

var _global_scope = __w_pdfjs_require__(2);

var _global_scope2 = _interopRequireDefault(_global_scope);

var _worker_options = __w_pdfjs_require__(3);

var _metadata = __w_pdfjs_require__(5);

var _text_layer = __w_pdfjs_require__(7);

var _svg = __w_pdfjs_require__(8);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

if (!_global_scope2.default.PDFJS) {
  _global_scope2.default.PDFJS = {};
}
var PDFJS = _global_scope2.default.PDFJS;
PDFJS.pdfBug = false;
PDFJS.OPS = _util.OPS;
PDFJS.UNSUPPORTED_FEATURES = _util.UNSUPPORTED_FEATURES;
PDFJS.shadow = _util.shadow;
PDFJS.createBlob = _util.createBlob;
PDFJS.createObjectURL = function PDFJS_createObjectURL(data, contentType) {
  return (0, _util.createObjectURL)(data, contentType, PDFJS.disableCreateObjectURL);
};
Object.defineProperty(PDFJS, 'isLittleEndian', {
  configurable: true,
  get: function PDFJS_isLittleEndian() {
    return (0, _util.shadow)(PDFJS, 'isLittleEndian', (0, _util.isLittleEndian)());
  }
});
PDFJS.removeNullCharacters = _util.removeNullCharacters;
PDFJS.PasswordResponses = _util.PasswordResponses;
PDFJS.PasswordException = _util.PasswordException;
PDFJS.UnknownErrorException = _util.UnknownErrorException;
PDFJS.InvalidPDFException = _util.InvalidPDFException;
PDFJS.MissingPDFException = _util.MissingPDFException;
PDFJS.UnexpectedResponseException = _util.UnexpectedResponseException;
PDFJS.Util = _util.Util;
PDFJS.PageViewport = _util.PageViewport;
PDFJS.createPromiseCapability = _util.createPromiseCapability;
PDFJS.maxImageSize = PDFJS.maxImageSize === undefined ? -1 : PDFJS.maxImageSize;
PDFJS.cMapUrl = PDFJS.cMapUrl === undefined ? null : PDFJS.cMapUrl;
PDFJS.cMapPacked = PDFJS.cMapPacked === undefined ? false : PDFJS.cMapPacked;
PDFJS.disableFontFace = PDFJS.disableFontFace === undefined ? false : PDFJS.disableFontFace;
PDFJS.disableRange = PDFJS.disableRange === undefined ? false : PDFJS.disableRange;
PDFJS.disableStream = PDFJS.disableStream === undefined ? false : PDFJS.disableStream;
PDFJS.disableAutoFetch = PDFJS.disableAutoFetch === undefined ? false : PDFJS.disableAutoFetch;
PDFJS.pdfBug = PDFJS.pdfBug === undefined ? false : PDFJS.pdfBug;
PDFJS.disableCreateObjectURL = PDFJS.disableCreateObjectURL === undefined ? false : PDFJS.disableCreateObjectURL;
PDFJS.externalLinkTarget = PDFJS.externalLinkTarget === undefined ? _dom_utils.LinkTarget.NONE : PDFJS.externalLinkTarget;
PDFJS.externalLinkRel = PDFJS.externalLinkRel === undefined ? _dom_utils.DEFAULT_LINK_REL : PDFJS.externalLinkRel;
PDFJS.isEvalSupported = PDFJS.isEvalSupported === undefined ? true : PDFJS.isEvalSupported;
PDFJS.getDocument = _api.getDocument;
PDFJS.LoopbackPort = _api.LoopbackPort;
PDFJS.PDFDataRangeTransport = _api.PDFDataRangeTransport;
PDFJS.PDFWorker = _api.PDFWorker;
PDFJS.GlobalWorkerOptions = _worker_options.GlobalWorkerOptions;
PDFJS.getFilenameFromUrl = _dom_utils.getFilenameFromUrl;
PDFJS.AnnotationLayer = _annotation_layer.AnnotationLayer;
PDFJS.renderTextLayer = _text_layer.renderTextLayer;
PDFJS.Metadata = _metadata.Metadata;
PDFJS.SVGGraphics = _svg.SVGGraphics;
exports.globalScope = _global_scope2.default;
exports.PDFJS = PDFJS;

/***/ }),
/* 14 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.FontLoader = exports.FontFaceObject = undefined;

var _util = __w_pdfjs_require__(0);

function FontLoader(docId) {
  this.docId = docId;
  this.styleElement = null;
}
FontLoader.prototype = {
  insertRule: function fontLoaderInsertRule(rule) {
    var styleElement = this.styleElement;
    if (!styleElement) {
      styleElement = this.styleElement = document.createElement('style');
      styleElement.id = 'PDFJS_FONT_STYLE_TAG_' + this.docId;
      document.documentElement.getElementsByTagName('head')[0].appendChild(styleElement);
    }
    var styleSheet = styleElement.sheet;
    styleSheet.insertRule(rule, styleSheet.cssRules.length);
  },
  clear: function fontLoaderClear() {
    if (this.styleElement) {
      this.styleElement.remove();
      this.styleElement = null;
    }
  }
};
{
  FontLoader.prototype.bind = function fontLoaderBind(fonts, callback) {
    for (var i = 0, ii = fonts.length; i < ii; i++) {
      var font = fonts[i];
      if (font.attached) {
        continue;
      }
      font.attached = true;
      var rule = font.createFontFaceRule();
      if (rule) {
        this.insertRule(rule);
      }
    }
    setTimeout(callback);
  };
}
;
;
var IsEvalSupportedCached = {
  get value() {
    return (0, _util.shadow)(this, 'value', (0, _util.isEvalSupported)());
  }
};
var FontFaceObject = function FontFaceObjectClosure() {
  function FontFaceObject(translatedData, options) {
    this.compiledGlyphs = Object.create(null);
    for (var i in translatedData) {
      this[i] = translatedData[i];
    }
    this.options = options;
  }
  FontFaceObject.prototype = {
    createNativeFontFace: function FontFaceObject_createNativeFontFace() {
      throw new Error('Not implemented: createNativeFontFace');
    },
    createFontFaceRule: function FontFaceObject_createFontFaceRule() {
      if (!this.data) {
        return null;
      }
      if (this.options.disableFontFace) {
        this.disableFontFace = true;
        return null;
      }
      var data = (0, _util.bytesToString)(new Uint8Array(this.data));
      var fontName = this.loadedName;
      var url = 'url(data:' + this.mimetype + ';base64,' + btoa(data) + ');';
      var rule = '@font-face { font-family:"' + fontName + '";src:' + url + '}';
      if (this.options.fontRegistry) {
        this.options.fontRegistry.registerFont(this, url);
      }
      return rule;
    },
    getPathGenerator: function FontFaceObject_getPathGenerator(objs, character) {
      if (!(character in this.compiledGlyphs)) {
        var cmds = objs.get(this.loadedName + '_path_' + character);
        var current, i, len;
        if (this.options.isEvalSupported && IsEvalSupportedCached.value) {
          var args,
              js = '';
          for (i = 0, len = cmds.length; i < len; i++) {
            current = cmds[i];
            if (current.args !== undefined) {
              args = current.args.join(',');
            } else {
              args = '';
            }
            js += 'c.' + current.cmd + '(' + args + ');\n';
          }
          this.compiledGlyphs[character] = new Function('c', 'size', js);
        } else {
          this.compiledGlyphs[character] = function (c, size) {
            for (i = 0, len = cmds.length; i < len; i++) {
              current = cmds[i];
              if (current.cmd === 'scale') {
                current.args = [size, -size];
              }
              c[current.cmd].apply(c, current.args);
            }
          };
        }
      }
      return this.compiledGlyphs[character];
    }
  };
  return FontFaceObject;
}();
exports.FontFaceObject = FontFaceObject;
exports.FontLoader = FontLoader;

/***/ }),
/* 15 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.CanvasGraphics = undefined;

var _util = __w_pdfjs_require__(0);

var _pattern_helper = __w_pdfjs_require__(16);

var MIN_FONT_SIZE = 16;
var MAX_FONT_SIZE = 100;
var MAX_GROUP_SIZE = 4096;
var MIN_WIDTH_FACTOR = 0.65;
var COMPILE_TYPE3_GLYPHS = true;
var MAX_SIZE_TO_COMPILE = 1000;
var FULL_CHUNK_HEIGHT = 16;
var IsLittleEndianCached = {
  get value() {
    return (0, _util.shadow)(IsLittleEndianCached, 'value', (0, _util.isLittleEndian)());
  }
};
function addContextCurrentTransform(ctx) {
  if (!ctx.mozCurrentTransform) {
    ctx._originalSave = ctx.save;
    ctx._originalRestore = ctx.restore;
    ctx._originalRotate = ctx.rotate;
    ctx._originalScale = ctx.scale;
    ctx._originalTranslate = ctx.translate;
    ctx._originalTransform = ctx.transform;
    ctx._originalSetTransform = ctx.setTransform;
    ctx._transformMatrix = ctx._transformMatrix || [1, 0, 0, 1, 0, 0];
    ctx._transformStack = [];
    Object.defineProperty(ctx, 'mozCurrentTransform', {
      get: function getCurrentTransform() {
        return this._transformMatrix;
      }
    });
    Object.defineProperty(ctx, 'mozCurrentTransformInverse', {
      get: function getCurrentTransformInverse() {
        var m = this._transformMatrix;
        var a = m[0],
            b = m[1],
            c = m[2],
            d = m[3],
            e = m[4],
            f = m[5];
        var ad_bc = a * d - b * c;
        var bc_ad = b * c - a * d;
        return [d / ad_bc, b / bc_ad, c / bc_ad, a / ad_bc, (d * e - c * f) / bc_ad, (b * e - a * f) / ad_bc];
      }
    });
    ctx.save = function ctxSave() {
      var old = this._transformMatrix;
      this._transformStack.push(old);
      this._transformMatrix = old.slice(0, 6);
      this._originalSave();
    };
    ctx.restore = function ctxRestore() {
      var prev = this._transformStack.pop();
      if (prev) {
        this._transformMatrix = prev;
        this._originalRestore();
      }
    };
    ctx.translate = function ctxTranslate(x, y) {
      var m = this._transformMatrix;
      m[4] = m[0] * x + m[2] * y + m[4];
      m[5] = m[1] * x + m[3] * y + m[5];
      this._originalTranslate(x, y);
    };
    ctx.scale = function ctxScale(x, y) {
      var m = this._transformMatrix;
      m[0] = m[0] * x;
      m[1] = m[1] * x;
      m[2] = m[2] * y;
      m[3] = m[3] * y;
      this._originalScale(x, y);
    };
    ctx.transform = function ctxTransform(a, b, c, d, e, f) {
      var m = this._transformMatrix;
      this._transformMatrix = [m[0] * a + m[2] * b, m[1] * a + m[3] * b, m[0] * c + m[2] * d, m[1] * c + m[3] * d, m[0] * e + m[2] * f + m[4], m[1] * e + m[3] * f + m[5]];
      ctx._originalTransform(a, b, c, d, e, f);
    };
    ctx.setTransform = function ctxSetTransform(a, b, c, d, e, f) {
      this._transformMatrix = [a, b, c, d, e, f];
      ctx._originalSetTransform(a, b, c, d, e, f);
    };
    ctx.rotate = function ctxRotate(angle) {
      var cosValue = Math.cos(angle);
      var sinValue = Math.sin(angle);
      var m = this._transformMatrix;
      this._transformMatrix = [m[0] * cosValue + m[2] * sinValue, m[1] * cosValue + m[3] * sinValue, m[0] * -sinValue + m[2] * cosValue, m[1] * -sinValue + m[3] * cosValue, m[4], m[5]];
      this._originalRotate(angle);
    };
  }
}
var CachedCanvases = function CachedCanvasesClosure() {
  function CachedCanvases(canvasFactory) {
    this.canvasFactory = canvasFactory;
    this.cache = Object.create(null);
  }
  CachedCanvases.prototype = {
    getCanvas: function CachedCanvases_getCanvas(id, width, height, trackTransform) {
      var canvasEntry;
      if (this.cache[id] !== undefined) {
        canvasEntry = this.cache[id];
        this.canvasFactory.reset(canvasEntry, width, height);
        canvasEntry.context.setTransform(1, 0, 0, 1, 0, 0);
      } else {
        canvasEntry = this.canvasFactory.create(width, height);
        this.cache[id] = canvasEntry;
      }
      if (trackTransform) {
        addContextCurrentTransform(canvasEntry.context);
      }
      return canvasEntry;
    },
    clear() {
      for (var id in this.cache) {
        var canvasEntry = this.cache[id];
        this.canvasFactory.destroy(canvasEntry);
        delete this.cache[id];
      }
    }
  };
  return CachedCanvases;
}();
function compileType3Glyph(imgData) {
  var POINT_TO_PROCESS_LIMIT = 1000;
  var width = imgData.width,
      height = imgData.height;
  var i,
      j,
      j0,
      width1 = width + 1;
  var points = new Uint8Array(width1 * (height + 1));
  var POINT_TYPES = new Uint8Array([0, 2, 4, 0, 1, 0, 5, 4, 8, 10, 0, 8, 0, 2, 1, 0]);
  var lineSize = width + 7 & ~7,
      data0 = imgData.data;
  var data = new Uint8Array(lineSize * height),
      pos = 0,
      ii;
  for (i = 0, ii = data0.length; i < ii; i++) {
    var mask = 128,
        elem = data0[i];
    while (mask > 0) {
      data[pos++] = elem & mask ? 0 : 255;
      mask >>= 1;
    }
  }
  var count = 0;
  pos = 0;
  if (data[pos] !== 0) {
    points[0] = 1;
    ++count;
  }
  for (j = 1; j < width; j++) {
    if (data[pos] !== data[pos + 1]) {
      points[j] = data[pos] ? 2 : 1;
      ++count;
    }
    pos++;
  }
  if (data[pos] !== 0) {
    points[j] = 2;
    ++count;
  }
  for (i = 1; i < height; i++) {
    pos = i * lineSize;
    j0 = i * width1;
    if (data[pos - lineSize] !== data[pos]) {
      points[j0] = data[pos] ? 1 : 8;
      ++count;
    }
    var sum = (data[pos] ? 4 : 0) + (data[pos - lineSize] ? 8 : 0);
    for (j = 1; j < width; j++) {
      sum = (sum >> 2) + (data[pos + 1] ? 4 : 0) + (data[pos - lineSize + 1] ? 8 : 0);
      if (POINT_TYPES[sum]) {
        points[j0 + j] = POINT_TYPES[sum];
        ++count;
      }
      pos++;
    }
    if (data[pos - lineSize] !== data[pos]) {
      points[j0 + j] = data[pos] ? 2 : 4;
      ++count;
    }
    if (count > POINT_TO_PROCESS_LIMIT) {
      return null;
    }
  }
  pos = lineSize * (height - 1);
  j0 = i * width1;
  if (data[pos] !== 0) {
    points[j0] = 8;
    ++count;
  }
  for (j = 1; j < width; j++) {
    if (data[pos] !== data[pos + 1]) {
      points[j0 + j] = data[pos] ? 4 : 8;
      ++count;
    }
    pos++;
  }
  if (data[pos] !== 0) {
    points[j0 + j] = 4;
    ++count;
  }
  if (count > POINT_TO_PROCESS_LIMIT) {
    return null;
  }
  var steps = new Int32Array([0, width1, -1, 0, -width1, 0, 0, 0, 1]);
  var outlines = [];
  for (i = 0; count && i <= height; i++) {
    var p = i * width1;
    var end = p + width;
    while (p < end && !points[p]) {
      p++;
    }
    if (p === end) {
      continue;
    }
    var coords = [p % width1, i];
    var type = points[p],
        p0 = p,
        pp;
    do {
      var step = steps[type];
      do {
        p += step;
      } while (!points[p]);
      pp = points[p];
      if (pp !== 5 && pp !== 10) {
        type = pp;
        points[p] = 0;
      } else {
        type = pp & 0x33 * type >> 4;
        points[p] &= type >> 2 | type << 2;
      }
      coords.push(p % width1);
      coords.push(p / width1 | 0);
      --count;
    } while (p0 !== p);
    outlines.push(coords);
    --i;
  }
  var drawOutline = function (c) {
    c.save();
    c.scale(1 / width, -1 / height);
    c.translate(0, -height);
    c.beginPath();
    for (var i = 0, ii = outlines.length; i < ii; i++) {
      var o = outlines[i];
      c.moveTo(o[0], o[1]);
      for (var j = 2, jj = o.length; j < jj; j += 2) {
        c.lineTo(o[j], o[j + 1]);
      }
    }
    c.fill();
    c.beginPath();
    c.restore();
  };
  return drawOutline;
}
var CanvasExtraState = function CanvasExtraStateClosure() {
  function CanvasExtraState() {
    this.alphaIsShape = false;
    this.fontSize = 0;
    this.fontSizeScale = 1;
    this.textMatrix = _util.IDENTITY_MATRIX;
    this.textMatrixScale = 1;
    this.fontMatrix = _util.FONT_IDENTITY_MATRIX;
    this.leading = 0;
    this.x = 0;
    this.y = 0;
    this.lineX = 0;
    this.lineY = 0;
    this.charSpacing = 0;
    this.wordSpacing = 0;
    this.textHScale = 1;
    this.textRenderingMode = _util.TextRenderingMode.FILL;
    this.textRise = 0;
    this.fillColor = '#000000';
    this.strokeColor = '#000000';
    this.patternFill = false;
    this.fillAlpha = 1;
    this.strokeAlpha = 1;
    this.lineWidth = 1;
    this.activeSMask = null;
    this.resumeSMaskCtx = null;
  }
  CanvasExtraState.prototype = {
    clone: function CanvasExtraState_clone() {
      return Object.create(this);
    },
    setCurrentPoint: function CanvasExtraState_setCurrentPoint(x, y) {
      this.x = x;
      this.y = y;
    }
  };
  return CanvasExtraState;
}();
var CanvasGraphics = function CanvasGraphicsClosure() {
  var EXECUTION_TIME = 15;
  var EXECUTION_STEPS = 10;
  function CanvasGraphics(canvasCtx, commonObjs, objs, canvasFactory, webGLContext, imageLayer) {
    this.ctx = canvasCtx;
    this.current = new CanvasExtraState();
    this.stateStack = [];
    this.pendingClip = null;
    this.pendingEOFill = false;
    this.res = null;
    this.xobjs = null;
    this.commonObjs = commonObjs;
    this.objs = objs;
    this.canvasFactory = canvasFactory;
    this.webGLContext = webGLContext;
    this.imageLayer = imageLayer;
    this.groupStack = [];
    this.processingType3 = null;
    this.baseTransform = null;
    this.baseTransformStack = [];
    this.groupLevel = 0;
    this.smaskStack = [];
    this.smaskCounter = 0;
    this.tempSMask = null;
    this.cachedCanvases = new CachedCanvases(this.canvasFactory);
    if (canvasCtx) {
      addContextCurrentTransform(canvasCtx);
    }
    this.cachedGetSinglePixelWidth = null;
  }
  function putBinaryImageData(ctx, imgData) {
    if (typeof ImageData !== 'undefined' && imgData instanceof ImageData) {
      ctx.putImageData(imgData, 0, 0);
      return;
    }
    var height = imgData.height,
        width = imgData.width;
    var partialChunkHeight = height % FULL_CHUNK_HEIGHT;
    var fullChunks = (height - partialChunkHeight) / FULL_CHUNK_HEIGHT;
    var totalChunks = partialChunkHeight === 0 ? fullChunks : fullChunks + 1;
    var chunkImgData = ctx.createImageData(width, FULL_CHUNK_HEIGHT);
    var srcPos = 0,
        destPos;
    var src = imgData.data;
    var dest = chunkImgData.data;
    var i, j, thisChunkHeight, elemsInThisChunk;
    if (imgData.kind === _util.ImageKind.GRAYSCALE_1BPP) {
      var srcLength = src.byteLength;
      var dest32 = new Uint32Array(dest.buffer, 0, dest.byteLength >> 2);
      var dest32DataLength = dest32.length;
      var fullSrcDiff = width + 7 >> 3;
      var white = 0xFFFFFFFF;
      var black = IsLittleEndianCached.value ? 0xFF000000 : 0x000000FF;
      for (i = 0; i < totalChunks; i++) {
        thisChunkHeight = i < fullChunks ? FULL_CHUNK_HEIGHT : partialChunkHeight;
        destPos = 0;
        for (j = 0; j < thisChunkHeight; j++) {
          var srcDiff = srcLength - srcPos;
          var k = 0;
          var kEnd = srcDiff > fullSrcDiff ? width : srcDiff * 8 - 7;
          var kEndUnrolled = kEnd & ~7;
          var mask = 0;
          var srcByte = 0;
          for (; k < kEndUnrolled; k += 8) {
            srcByte = src[srcPos++];
            dest32[destPos++] = srcByte & 128 ? white : black;
            dest32[destPos++] = srcByte & 64 ? white : black;
            dest32[destPos++] = srcByte & 32 ? white : black;
            dest32[destPos++] = srcByte & 16 ? white : black;
            dest32[destPos++] = srcByte & 8 ? white : black;
            dest32[destPos++] = srcByte & 4 ? white : black;
            dest32[destPos++] = srcByte & 2 ? white : black;
            dest32[destPos++] = srcByte & 1 ? white : black;
          }
          for (; k < kEnd; k++) {
            if (mask === 0) {
              srcByte = src[srcPos++];
              mask = 128;
            }
            dest32[destPos++] = srcByte & mask ? white : black;
            mask >>= 1;
          }
        }
        while (destPos < dest32DataLength) {
          dest32[destPos++] = 0;
        }
        ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT);
      }
    } else if (imgData.kind === _util.ImageKind.RGBA_32BPP) {
      j = 0;
      elemsInThisChunk = width * FULL_CHUNK_HEIGHT * 4;
      for (i = 0; i < fullChunks; i++) {
        dest.set(src.subarray(srcPos, srcPos + elemsInThisChunk));
        srcPos += elemsInThisChunk;
        ctx.putImageData(chunkImgData, 0, j);
        j += FULL_CHUNK_HEIGHT;
      }
      if (i < totalChunks) {
        elemsInThisChunk = width * partialChunkHeight * 4;
        dest.set(src.subarray(srcPos, srcPos + elemsInThisChunk));
        ctx.putImageData(chunkImgData, 0, j);
      }
    } else if (imgData.kind === _util.ImageKind.RGB_24BPP) {
      thisChunkHeight = FULL_CHUNK_HEIGHT;
      elemsInThisChunk = width * thisChunkHeight;
      for (i = 0; i < totalChunks; i++) {
        if (i >= fullChunks) {
          thisChunkHeight = partialChunkHeight;
          elemsInThisChunk = width * thisChunkHeight;
        }
        destPos = 0;
        for (j = elemsInThisChunk; j--;) {
          dest[destPos++] = src[srcPos++];
          dest[destPos++] = src[srcPos++];
          dest[destPos++] = src[srcPos++];
          dest[destPos++] = 255;
        }
        ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT);
      }
    } else {
      throw new Error(`bad image kind: ${imgData.kind}`);
    }
  }
  function putBinaryImageMask(ctx, imgData) {
    var height = imgData.height,
        width = imgData.width;
    var partialChunkHeight = height % FULL_CHUNK_HEIGHT;
    var fullChunks = (height - partialChunkHeight) / FULL_CHUNK_HEIGHT;
    var totalChunks = partialChunkHeight === 0 ? fullChunks : fullChunks + 1;
    var chunkImgData = ctx.createImageData(width, FULL_CHUNK_HEIGHT);
    var srcPos = 0;
    var src = imgData.data;
    var dest = chunkImgData.data;
    for (var i = 0; i < totalChunks; i++) {
      var thisChunkHeight = i < fullChunks ? FULL_CHUNK_HEIGHT : partialChunkHeight;
      var destPos = 3;
      for (var j = 0; j < thisChunkHeight; j++) {
        var mask = 0;
        for (var k = 0; k < width; k++) {
          if (!mask) {
            var elem = src[srcPos++];
            mask = 128;
          }
          dest[destPos] = elem & mask ? 0 : 255;
          destPos += 4;
          mask >>= 1;
        }
      }
      ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT);
    }
  }
  function copyCtxState(sourceCtx, destCtx) {
    var properties = ['strokeStyle', 'fillStyle', 'fillRule', 'globalAlpha', 'lineWidth', 'lineCap', 'lineJoin', 'miterLimit', 'globalCompositeOperation', 'font'];
    for (var i = 0, ii = properties.length; i < ii; i++) {
      var property = properties[i];
      if (sourceCtx[property] !== undefined) {
        destCtx[property] = sourceCtx[property];
      }
    }
    if (sourceCtx.setLineDash !== undefined) {
      destCtx.setLineDash(sourceCtx.getLineDash());
      destCtx.lineDashOffset = sourceCtx.lineDashOffset;
    }
  }
  function resetCtxToDefault(ctx) {
    ctx.strokeStyle = '#000000';
    ctx.fillStyle = '#000000';
    ctx.fillRule = 'nonzero';
    ctx.globalAlpha = 1;
    ctx.lineWidth = 1;
    ctx.lineCap = 'butt';
    ctx.lineJoin = 'miter';
    ctx.miterLimit = 10;
    ctx.globalCompositeOperation = 'source-over';
    ctx.font = '10px sans-serif';
    if (ctx.setLineDash !== undefined) {
      ctx.setLineDash([]);
      ctx.lineDashOffset = 0;
    }
  }
  function composeSMaskBackdrop(bytes, r0, g0, b0) {
    var length = bytes.length;
    for (var i = 3; i < length; i += 4) {
      var alpha = bytes[i];
      if (alpha === 0) {
        bytes[i - 3] = r0;
        bytes[i - 2] = g0;
        bytes[i - 1] = b0;
      } else if (alpha < 255) {
        var alpha_ = 255 - alpha;
        bytes[i - 3] = bytes[i - 3] * alpha + r0 * alpha_ >> 8;
        bytes[i - 2] = bytes[i - 2] * alpha + g0 * alpha_ >> 8;
        bytes[i - 1] = bytes[i - 1] * alpha + b0 * alpha_ >> 8;
      }
    }
  }
  function composeSMaskAlpha(maskData, layerData, transferMap) {
    var length = maskData.length;
    var scale = 1 / 255;
    for (var i = 3; i < length; i += 4) {
      var alpha = transferMap ? transferMap[maskData[i]] : maskData[i];
      layerData[i] = layerData[i] * alpha * scale | 0;
    }
  }
  function composeSMaskLuminosity(maskData, layerData, transferMap) {
    var length = maskData.length;
    for (var i = 3; i < length; i += 4) {
      var y = maskData[i - 3] * 77 + maskData[i - 2] * 152 + maskData[i - 1] * 28;
      layerData[i] = transferMap ? layerData[i] * transferMap[y >> 8] >> 8 : layerData[i] * y >> 16;
    }
  }
  function genericComposeSMask(maskCtx, layerCtx, width, height, subtype, backdrop, transferMap) {
    var hasBackdrop = !!backdrop;
    var r0 = hasBackdrop ? backdrop[0] : 0;
    var g0 = hasBackdrop ? backdrop[1] : 0;
    var b0 = hasBackdrop ? backdrop[2] : 0;
    var composeFn;
    if (subtype === 'Luminosity') {
      composeFn = composeSMaskLuminosity;
    } else {
      composeFn = composeSMaskAlpha;
    }
    var PIXELS_TO_PROCESS = 1048576;
    var chunkSize = Math.min(height, Math.ceil(PIXELS_TO_PROCESS / width));
    for (var row = 0; row < height; row += chunkSize) {
      var chunkHeight = Math.min(chunkSize, height - row);
      var maskData = maskCtx.getImageData(0, row, width, chunkHeight);
      var layerData = layerCtx.getImageData(0, row, width, chunkHeight);
      if (hasBackdrop) {
        composeSMaskBackdrop(maskData.data, r0, g0, b0);
      }
      composeFn(maskData.data, layerData.data, transferMap);
      maskCtx.putImageData(layerData, 0, row);
    }
  }
  function composeSMask(ctx, smask, layerCtx, webGLContext) {
    var mask = smask.canvas;
    var maskCtx = smask.context;
    ctx.setTransform(smask.scaleX, 0, 0, smask.scaleY, smask.offsetX, smask.offsetY);
    var backdrop = smask.backdrop || null;
    if (!smask.transferMap && webGLContext.isEnabled) {
      let composed = webGLContext.composeSMask({
        layer: layerCtx.canvas,
        mask,
        properties: {
          subtype: smask.subtype,
          backdrop
        }
      });
      ctx.setTransform(1, 0, 0, 1, 0, 0);
      ctx.drawImage(composed, smask.offsetX, smask.offsetY);
      return;
    }
    genericComposeSMask(maskCtx, layerCtx, mask.width, mask.height, smask.subtype, backdrop, smask.transferMap);
    ctx.drawImage(mask, 0, 0);
  }
  var LINE_CAP_STYLES = ['butt', 'round', 'square'];
  var LINE_JOIN_STYLES = ['miter', 'round', 'bevel'];
  var NORMAL_CLIP = {};
  var EO_CLIP = {};
  CanvasGraphics.prototype = {
    beginDrawing({ transform, viewport, transparency, background = null }) {
      var width = this.ctx.canvas.width;
      var height = this.ctx.canvas.height;
      this.ctx.save();
      this.ctx.fillStyle = background || 'rgb(255, 255, 255)';
      this.ctx.fillRect(0, 0, width, height);
      this.ctx.restore();
      if (transparency) {
        var transparentCanvas = this.cachedCanvases.getCanvas('transparent', width, height, true);
        this.compositeCtx = this.ctx;
        this.transparentCanvas = transparentCanvas.canvas;
        this.ctx = transparentCanvas.context;
        this.ctx.save();
        this.ctx.transform.apply(this.ctx, this.compositeCtx.mozCurrentTransform);
      }
      this.ctx.save();
      resetCtxToDefault(this.ctx);
      if (transform) {
        this.ctx.transform.apply(this.ctx, transform);
      }
      this.ctx.transform.apply(this.ctx, viewport.transform);
      this.baseTransform = this.ctx.mozCurrentTransform.slice();
      if (this.imageLayer) {
        this.imageLayer.beginLayout();
      }
    },
    executeOperatorList: function CanvasGraphics_executeOperatorList(operatorList, executionStartIdx, continueCallback, stepper) {
      var argsArray = operatorList.argsArray;
      var fnArray = operatorList.fnArray;
      var i = executionStartIdx || 0;
      var argsArrayLen = argsArray.length;
      if (argsArrayLen === i) {
        return i;
      }
      var chunkOperations = argsArrayLen - i > EXECUTION_STEPS && typeof continueCallback === 'function';
      var endTime = chunkOperations ? Date.now() + EXECUTION_TIME : 0;
      var steps = 0;
      var commonObjs = this.commonObjs;
      var objs = this.objs;
      var fnId;
      while (true) {
        if (stepper !== undefined && i === stepper.nextBreakPoint) {
          stepper.breakIt(i, continueCallback);
          return i;
        }
        fnId = fnArray[i];
        if (fnId !== _util.OPS.dependency) {
          this[fnId].apply(this, argsArray[i]);
        } else {
          var deps = argsArray[i];
          for (var n = 0, nn = deps.length; n < nn; n++) {
            var depObjId = deps[n];
            var common = depObjId[0] === 'g' && depObjId[1] === '_';
            var objsPool = common ? commonObjs : objs;
            if (!objsPool.isResolved(depObjId)) {
              objsPool.get(depObjId, continueCallback);
              return i;
            }
          }
        }
        i++;
        if (i === argsArrayLen) {
          return i;
        }
        if (chunkOperations && ++steps > EXECUTION_STEPS) {
          if (Date.now() > endTime) {
            continueCallback();
            return i;
          }
          steps = 0;
        }
      }
    },
    endDrawing: function CanvasGraphics_endDrawing() {
      if (this.current.activeSMask !== null) {
        this.endSMaskGroup();
      }
      this.ctx.restore();
      if (this.transparentCanvas) {
        this.ctx = this.compositeCtx;
        this.ctx.save();
        this.ctx.setTransform(1, 0, 0, 1, 0, 0);
        this.ctx.drawImage(this.transparentCanvas, 0, 0);
        this.ctx.restore();
        this.transparentCanvas = null;
      }
      this.cachedCanvases.clear();
      this.webGLContext.clear();
      if (this.imageLayer) {
        this.imageLayer.endLayout();
      }
    },
    setLineWidth: function CanvasGraphics_setLineWidth(width) {
      this.current.lineWidth = width;
      this.ctx.lineWidth = width;
    },
    setLineCap: function CanvasGraphics_setLineCap(style) {
      this.ctx.lineCap = LINE_CAP_STYLES[style];
    },
    setLineJoin: function CanvasGraphics_setLineJoin(style) {
      this.ctx.lineJoin = LINE_JOIN_STYLES[style];
    },
    setMiterLimit: function CanvasGraphics_setMiterLimit(limit) {
      this.ctx.miterLimit = limit;
    },
    setDash: function CanvasGraphics_setDash(dashArray, dashPhase) {
      var ctx = this.ctx;
      if (ctx.setLineDash !== undefined) {
        ctx.setLineDash(dashArray);
        ctx.lineDashOffset = dashPhase;
      }
    },
    setRenderingIntent: function CanvasGraphics_setRenderingIntent(intent) {},
    setFlatness: function CanvasGraphics_setFlatness(flatness) {},
    setGState: function CanvasGraphics_setGState(states) {
      for (var i = 0, ii = states.length; i < ii; i++) {
        var state = states[i];
        var key = state[0];
        var value = state[1];
        switch (key) {
          case 'LW':
            this.setLineWidth(value);
            break;
          case 'LC':
            this.setLineCap(value);
            break;
          case 'LJ':
            this.setLineJoin(value);
            break;
          case 'ML':
            this.setMiterLimit(value);
            break;
          case 'D':
            this.setDash(value[0], value[1]);
            break;
          case 'RI':
            this.setRenderingIntent(value);
            break;
          case 'FL':
            this.setFlatness(value);
            break;
          case 'Font':
            this.setFont(value[0], value[1]);
            break;
          case 'CA':
            this.current.strokeAlpha = state[1];
            break;
          case 'ca':
            this.current.fillAlpha = state[1];
            this.ctx.globalAlpha = state[1];
            break;
          case 'BM':
            this.ctx.globalCompositeOperation = value;
            break;
          case 'SMask':
            if (this.current.activeSMask) {
              if (this.stateStack.length > 0 && this.stateStack[this.stateStack.length - 1].activeSMask === this.current.activeSMask) {
                this.suspendSMaskGroup();
              } else {
                this.endSMaskGroup();
              }
            }
            this.current.activeSMask = value ? this.tempSMask : null;
            if (this.current.activeSMask) {
              this.beginSMaskGroup();
            }
            this.tempSMask = null;
            break;
        }
      }
    },
    beginSMaskGroup: function CanvasGraphics_beginSMaskGroup() {
      var activeSMask = this.current.activeSMask;
      var drawnWidth = activeSMask.canvas.width;
      var drawnHeight = activeSMask.canvas.height;
      var cacheId = 'smaskGroupAt' + this.groupLevel;
      var scratchCanvas = this.cachedCanvases.getCanvas(cacheId, drawnWidth, drawnHeight, true);
      var currentCtx = this.ctx;
      var currentTransform = currentCtx.mozCurrentTransform;
      this.ctx.save();
      var groupCtx = scratchCanvas.context;
      groupCtx.scale(1 / activeSMask.scaleX, 1 / activeSMask.scaleY);
      groupCtx.translate(-activeSMask.offsetX, -activeSMask.offsetY);
      groupCtx.transform.apply(groupCtx, currentTransform);
      activeSMask.startTransformInverse = groupCtx.mozCurrentTransformInverse;
      copyCtxState(currentCtx, groupCtx);
      this.ctx = groupCtx;
      this.setGState([['BM', 'source-over'], ['ca', 1], ['CA', 1]]);
      this.groupStack.push(currentCtx);
      this.groupLevel++;
    },
    suspendSMaskGroup: function CanvasGraphics_endSMaskGroup() {
      var groupCtx = this.ctx;
      this.groupLevel--;
      this.ctx = this.groupStack.pop();
      composeSMask(this.ctx, this.current.activeSMask, groupCtx, this.webGLContext);
      this.ctx.restore();
      this.ctx.save();
      copyCtxState(groupCtx, this.ctx);
      this.current.resumeSMaskCtx = groupCtx;
      var deltaTransform = _util.Util.transform(this.current.activeSMask.startTransformInverse, groupCtx.mozCurrentTransform);
      this.ctx.transform.apply(this.ctx, deltaTransform);
      groupCtx.save();
      groupCtx.setTransform(1, 0, 0, 1, 0, 0);
      groupCtx.clearRect(0, 0, groupCtx.canvas.width, groupCtx.canvas.height);
      groupCtx.restore();
    },
    resumeSMaskGroup: function CanvasGraphics_endSMaskGroup() {
      var groupCtx = this.current.resumeSMaskCtx;
      var currentCtx = this.ctx;
      this.ctx = groupCtx;
      this.groupStack.push(currentCtx);
      this.groupLevel++;
    },
    endSMaskGroup: function CanvasGraphics_endSMaskGroup() {
      var groupCtx = this.ctx;
      this.groupLevel--;
      this.ctx = this.groupStack.pop();
      composeSMask(this.ctx, this.current.activeSMask, groupCtx, this.webGLContext);
      this.ctx.restore();
      copyCtxState(groupCtx, this.ctx);
      var deltaTransform = _util.Util.transform(this.current.activeSMask.startTransformInverse, groupCtx.mozCurrentTransform);
      this.ctx.transform.apply(this.ctx, deltaTransform);
    },
    save: function CanvasGraphics_save() {
      this.ctx.save();
      var old = this.current;
      this.stateStack.push(old);
      this.current = old.clone();
      this.current.resumeSMaskCtx = null;
    },
    restore: function CanvasGraphics_restore() {
      if (this.current.resumeSMaskCtx) {
        this.resumeSMaskGroup();
      }
      if (this.current.activeSMask !== null && (this.stateStack.length === 0 || this.stateStack[this.stateStack.length - 1].activeSMask !== this.current.activeSMask)) {
        this.endSMaskGroup();
      }
      if (this.stateStack.length !== 0) {
        this.current = this.stateStack.pop();
        this.ctx.restore();
        this.pendingClip = null;
        this.cachedGetSinglePixelWidth = null;
      }
    },
    transform: function CanvasGraphics_transform(a, b, c, d, e, f) {
      this.ctx.transform(a, b, c, d, e, f);
      this.cachedGetSinglePixelWidth = null;
    },
    constructPath: function CanvasGraphics_constructPath(ops, args) {
      var ctx = this.ctx;
      var current = this.current;
      var x = current.x,
          y = current.y;
      for (var i = 0, j = 0, ii = ops.length; i < ii; i++) {
        switch (ops[i] | 0) {
          case _util.OPS.rectangle:
            x = args[j++];
            y = args[j++];
            var width = args[j++];
            var height = args[j++];
            if (width === 0) {
              width = this.getSinglePixelWidth();
            }
            if (height === 0) {
              height = this.getSinglePixelWidth();
            }
            var xw = x + width;
            var yh = y + height;
            this.ctx.moveTo(x, y);
            this.ctx.lineTo(xw, y);
            this.ctx.lineTo(xw, yh);
            this.ctx.lineTo(x, yh);
            this.ctx.lineTo(x, y);
            this.ctx.closePath();
            break;
          case _util.OPS.moveTo:
            x = args[j++];
            y = args[j++];
            ctx.moveTo(x, y);
            break;
          case _util.OPS.lineTo:
            x = args[j++];
            y = args[j++];
            ctx.lineTo(x, y);
            break;
          case _util.OPS.curveTo:
            x = args[j + 4];
            y = args[j + 5];
            ctx.bezierCurveTo(args[j], args[j + 1], args[j + 2], args[j + 3], x, y);
            j += 6;
            break;
          case _util.OPS.curveTo2:
            ctx.bezierCurveTo(x, y, args[j], args[j + 1], args[j + 2], args[j + 3]);
            x = args[j + 2];
            y = args[j + 3];
            j += 4;
            break;
          case _util.OPS.curveTo3:
            x = args[j + 2];
            y = args[j + 3];
            ctx.bezierCurveTo(args[j], args[j + 1], x, y, x, y);
            j += 4;
            break;
          case _util.OPS.closePath:
            ctx.closePath();
            break;
        }
      }
      current.setCurrentPoint(x, y);
    },
    closePath: function CanvasGraphics_closePath() {
      this.ctx.closePath();
    },
    stroke: function CanvasGraphics_stroke(consumePath) {
      consumePath = typeof consumePath !== 'undefined' ? consumePath : true;
      var ctx = this.ctx;
      var strokeColor = this.current.strokeColor;
      ctx.lineWidth = Math.max(this.getSinglePixelWidth() * MIN_WIDTH_FACTOR, this.current.lineWidth);
      ctx.globalAlpha = this.current.strokeAlpha;
      if (strokeColor && strokeColor.hasOwnProperty('type') && strokeColor.type === 'Pattern') {
        ctx.save();
        ctx.strokeStyle = strokeColor.getPattern(ctx, this);
        ctx.stroke();
        ctx.restore();
      } else {
        ctx.stroke();
      }
      if (consumePath) {
        this.consumePath();
      }
      ctx.globalAlpha = this.current.fillAlpha;
    },
    closeStroke: function CanvasGraphics_closeStroke() {
      this.closePath();
      this.stroke();
    },
    fill: function CanvasGraphics_fill(consumePath) {
      consumePath = typeof consumePath !== 'undefined' ? consumePath : true;
      var ctx = this.ctx;
      var fillColor = this.current.fillColor;
      var isPatternFill = this.current.patternFill;
      var needRestore = false;
      if (isPatternFill) {
        ctx.save();
        if (this.baseTransform) {
          ctx.setTransform.apply(ctx, this.baseTransform);
        }
        ctx.fillStyle = fillColor.getPattern(ctx, this);
        needRestore = true;
      }
      if (this.pendingEOFill) {
        ctx.fill('evenodd');
        this.pendingEOFill = false;
      } else {
        ctx.fill();
      }
      if (needRestore) {
        ctx.restore();
      }
      if (consumePath) {
        this.consumePath();
      }
    },
    eoFill: function CanvasGraphics_eoFill() {
      this.pendingEOFill = true;
      this.fill();
    },
    fillStroke: function CanvasGraphics_fillStroke() {
      this.fill(false);
      this.stroke(false);
      this.consumePath();
    },
    eoFillStroke: function CanvasGraphics_eoFillStroke() {
      this.pendingEOFill = true;
      this.fillStroke();
    },
    closeFillStroke: function CanvasGraphics_closeFillStroke() {
      this.closePath();
      this.fillStroke();
    },
    closeEOFillStroke: function CanvasGraphics_closeEOFillStroke() {
      this.pendingEOFill = true;
      this.closePath();
      this.fillStroke();
    },
    endPath: function CanvasGraphics_endPath() {
      this.consumePath();
    },
    clip: function CanvasGraphics_clip() {
      this.pendingClip = NORMAL_CLIP;
    },
    eoClip: function CanvasGraphics_eoClip() {
      this.pendingClip = EO_CLIP;
    },
    beginText: function CanvasGraphics_beginText() {
      this.current.textMatrix = _util.IDENTITY_MATRIX;
      this.current.textMatrixScale = 1;
      this.current.x = this.current.lineX = 0;
      this.current.y = this.current.lineY = 0;
    },
    endText: function CanvasGraphics_endText() {
      var paths = this.pendingTextPaths;
      var ctx = this.ctx;
      if (paths === undefined) {
        ctx.beginPath();
        return;
      }
      ctx.save();
      ctx.beginPath();
      for (var i = 0; i < paths.length; i++) {
        var path = paths[i];
        ctx.setTransform.apply(ctx, path.transform);
        ctx.translate(path.x, path.y);
        path.addToPath(ctx, path.fontSize);
      }
      ctx.restore();
      ctx.clip();
      ctx.beginPath();
      delete this.pendingTextPaths;
    },
    setCharSpacing: function CanvasGraphics_setCharSpacing(spacing) {
      this.current.charSpacing = spacing;
    },
    setWordSpacing: function CanvasGraphics_setWordSpacing(spacing) {
      this.current.wordSpacing = spacing;
    },
    setHScale: function CanvasGraphics_setHScale(scale) {
      this.current.textHScale = scale / 100;
    },
    setLeading: function CanvasGraphics_setLeading(leading) {
      this.current.leading = -leading;
    },
    setFont: function CanvasGraphics_setFont(fontRefName, size) {
      var fontObj = this.commonObjs.get(fontRefName);
      var current = this.current;
      if (!fontObj) {
        throw new Error(`Can't find font for ${fontRefName}`);
      }
      current.fontMatrix = fontObj.fontMatrix ? fontObj.fontMatrix : _util.FONT_IDENTITY_MATRIX;
      if (current.fontMatrix[0] === 0 || current.fontMatrix[3] === 0) {
        (0, _util.warn)('Invalid font matrix for font ' + fontRefName);
      }
      if (size < 0) {
        size = -size;
        current.fontDirection = -1;
      } else {
        current.fontDirection = 1;
      }
      this.current.font = fontObj;
      this.current.fontSize = size;
      if (fontObj.isType3Font) {
        return;
      }
      var name = fontObj.loadedName || 'sans-serif';
      var bold = fontObj.black ? '900' : fontObj.bold ? 'bold' : 'normal';
      var italic = fontObj.italic ? 'italic' : 'normal';
      var typeface = '"' + name + '", ' + fontObj.fallbackName;
      var browserFontSize = size < MIN_FONT_SIZE ? MIN_FONT_SIZE : size > MAX_FONT_SIZE ? MAX_FONT_SIZE : size;
      this.current.fontSizeScale = size / browserFontSize;
      var rule = italic + ' ' + bold + ' ' + browserFontSize + 'px ' + typeface;
      this.ctx.font = rule;
    },
    setTextRenderingMode: function CanvasGraphics_setTextRenderingMode(mode) {
      this.current.textRenderingMode = mode;
    },
    setTextRise: function CanvasGraphics_setTextRise(rise) {
      this.current.textRise = rise;
    },
    moveText: function CanvasGraphics_moveText(x, y) {
      this.current.x = this.current.lineX += x;
      this.current.y = this.current.lineY += y;
    },
    setLeadingMoveText: function CanvasGraphics_setLeadingMoveText(x, y) {
      this.setLeading(-y);
      this.moveText(x, y);
    },
    setTextMatrix: function CanvasGraphics_setTextMatrix(a, b, c, d, e, f) {
      this.current.textMatrix = [a, b, c, d, e, f];
      this.current.textMatrixScale = Math.sqrt(a * a + b * b);
      this.current.x = this.current.lineX = 0;
      this.current.y = this.current.lineY = 0;
    },
    nextLine: function CanvasGraphics_nextLine() {
      this.moveText(0, this.current.leading);
    },
    paintChar(character, x, y, patternTransform) {
      var ctx = this.ctx;
      var current = this.current;
      var font = current.font;
      var textRenderingMode = current.textRenderingMode;
      var fontSize = current.fontSize / current.fontSizeScale;
      var fillStrokeMode = textRenderingMode & _util.TextRenderingMode.FILL_STROKE_MASK;
      var isAddToPathSet = !!(textRenderingMode & _util.TextRenderingMode.ADD_TO_PATH_FLAG);
      let patternFill = current.patternFill && font.data;
      var addToPath;
      if (font.disableFontFace || isAddToPathSet || patternFill) {
        addToPath = font.getPathGenerator(this.commonObjs, character);
      }
      if (font.disableFontFace || patternFill) {
        ctx.save();
        ctx.translate(x, y);
        ctx.beginPath();
        addToPath(ctx, fontSize);
        if (patternTransform) {
          ctx.setTransform.apply(ctx, patternTransform);
        }
        if (fillStrokeMode === _util.TextRenderingMode.FILL || fillStrokeMode === _util.TextRenderingMode.FILL_STROKE) {
          ctx.fill();
        }
        if (fillStrokeMode === _util.TextRenderingMode.STROKE || fillStrokeMode === _util.TextRenderingMode.FILL_STROKE) {
          ctx.stroke();
        }
        ctx.restore();
      } else {
        if (fillStrokeMode === _util.TextRenderingMode.FILL || fillStrokeMode === _util.TextRenderingMode.FILL_STROKE) {
          ctx.fillText(character, x, y);
        }
        if (fillStrokeMode === _util.TextRenderingMode.STROKE || fillStrokeMode === _util.TextRenderingMode.FILL_STROKE) {
          ctx.strokeText(character, x, y);
        }
      }
      if (isAddToPathSet) {
        var paths = this.pendingTextPaths || (this.pendingTextPaths = []);
        paths.push({
          transform: ctx.mozCurrentTransform,
          x,
          y,
          fontSize,
          addToPath
        });
      }
    },
    get isFontSubpixelAAEnabled() {
      var ctx = this.canvasFactory.create(10, 10).context;
      ctx.scale(1.5, 1);
      ctx.fillText('I', 0, 10);
      var data = ctx.getImageData(0, 0, 10, 10).data;
      var enabled = false;
      for (var i = 3; i < data.length; i += 4) {
        if (data[i] > 0 && data[i] < 255) {
          enabled = true;
          break;
        }
      }
      return (0, _util.shadow)(this, 'isFontSubpixelAAEnabled', enabled);
    },
    showText: function CanvasGraphics_showText(glyphs) {
      var current = this.current;
      var font = current.font;
      if (font.isType3Font) {
        return this.showType3Text(glyphs);
      }
      var fontSize = current.fontSize;
      if (fontSize === 0) {
        return;
      }
      var ctx = this.ctx;
      var fontSizeScale = current.fontSizeScale;
      var charSpacing = current.charSpacing;
      var wordSpacing = current.wordSpacing;
      var fontDirection = current.fontDirection;
      var textHScale = current.textHScale * fontDirection;
      var glyphsLength = glyphs.length;
      var vertical = font.vertical;
      var spacingDir = vertical ? 1 : -1;
      var defaultVMetrics = font.defaultVMetrics;
      var widthAdvanceScale = fontSize * current.fontMatrix[0];
      var simpleFillText = current.textRenderingMode === _util.TextRenderingMode.FILL && !font.disableFontFace && !current.patternFill;
      ctx.save();
      let patternTransform;
      if (current.patternFill) {
        ctx.save();
        let pattern = current.fillColor.getPattern(ctx, this);
        patternTransform = ctx.mozCurrentTransform;
        ctx.restore();
        ctx.fillStyle = pattern;
      }
      ctx.transform.apply(ctx, current.textMatrix);
      ctx.translate(current.x, current.y + current.textRise);
      if (fontDirection > 0) {
        ctx.scale(textHScale, -1);
      } else {
        ctx.scale(textHScale, 1);
      }
      var lineWidth = current.lineWidth;
      var scale = current.textMatrixScale;
      if (scale === 0 || lineWidth === 0) {
        var fillStrokeMode = current.textRenderingMode & _util.TextRenderingMode.FILL_STROKE_MASK;
        if (fillStrokeMode === _util.TextRenderingMode.STROKE || fillStrokeMode === _util.TextRenderingMode.FILL_STROKE) {
          this.cachedGetSinglePixelWidth = null;
          lineWidth = this.getSinglePixelWidth() * MIN_WIDTH_FACTOR;
        }
      } else {
        lineWidth /= scale;
      }
      if (fontSizeScale !== 1.0) {
        ctx.scale(fontSizeScale, fontSizeScale);
        lineWidth /= fontSizeScale;
      }
      ctx.lineWidth = lineWidth;
      var x = 0,
          i;
      for (i = 0; i < glyphsLength; ++i) {
        var glyph = glyphs[i];
        if ((0, _util.isNum)(glyph)) {
          x += spacingDir * glyph * fontSize / 1000;
          continue;
        }
        var restoreNeeded = false;
        var spacing = (glyph.isSpace ? wordSpacing : 0) + charSpacing;
        var character = glyph.fontChar;
        var accent = glyph.accent;
        var scaledX, scaledY, scaledAccentX, scaledAccentY;
        var width = glyph.width;
        if (vertical) {
          var vmetric, vx, vy;
          vmetric = glyph.vmetric || defaultVMetrics;
          vx = glyph.vmetric ? vmetric[1] : width * 0.5;
          vx = -vx * widthAdvanceScale;
          vy = vmetric[2] * widthAdvanceScale;
          width = vmetric ? -vmetric[0] : width;
          scaledX = vx / fontSizeScale;
          scaledY = (x + vy) / fontSizeScale;
        } else {
          scaledX = x / fontSizeScale;
          scaledY = 0;
        }
        if (font.remeasure && width > 0) {
          var measuredWidth = ctx.measureText(character).width * 1000 / fontSize * fontSizeScale;
          if (width < measuredWidth && this.isFontSubpixelAAEnabled) {
            var characterScaleX = width / measuredWidth;
            restoreNeeded = true;
            ctx.save();
            ctx.scale(characterScaleX, 1);
            scaledX /= characterScaleX;
          } else if (width !== measuredWidth) {
            scaledX += (width - measuredWidth) / 2000 * fontSize / fontSizeScale;
          }
        }
        if (glyph.isInFont || font.missingFile) {
          if (simpleFillText && !accent) {
            ctx.fillText(character, scaledX, scaledY);
          } else {
            this.paintChar(character, scaledX, scaledY, patternTransform);
            if (accent) {
              scaledAccentX = scaledX + accent.offset.x / fontSizeScale;
              scaledAccentY = scaledY - accent.offset.y / fontSizeScale;
              this.paintChar(accent.fontChar, scaledAccentX, scaledAccentY, patternTransform);
            }
          }
        }
        var charWidth = width * widthAdvanceScale + spacing * fontDirection;
        x += charWidth;
        if (restoreNeeded) {
          ctx.restore();
        }
      }
      if (vertical) {
        current.y -= x * textHScale;
      } else {
        current.x += x * textHScale;
      }
      ctx.restore();
    },
    showType3Text: function CanvasGraphics_showType3Text(glyphs) {
      var ctx = this.ctx;
      var current = this.current;
      var font = current.font;
      var fontSize = current.fontSize;
      var fontDirection = current.fontDirection;
      var spacingDir = font.vertical ? 1 : -1;
      var charSpacing = current.charSpacing;
      var wordSpacing = current.wordSpacing;
      var textHScale = current.textHScale * fontDirection;
      var fontMatrix = current.fontMatrix || _util.FONT_IDENTITY_MATRIX;
      var glyphsLength = glyphs.length;
      var isTextInvisible = current.textRenderingMode === _util.TextRenderingMode.INVISIBLE;
      var i, glyph, width, spacingLength;
      if (isTextInvisible || fontSize === 0) {
        return;
      }
      this.cachedGetSinglePixelWidth = null;
      ctx.save();
      ctx.transform.apply(ctx, current.textMatrix);
      ctx.translate(current.x, current.y);
      ctx.scale(textHScale, fontDirection);
      for (i = 0; i < glyphsLength; ++i) {
        glyph = glyphs[i];
        if ((0, _util.isNum)(glyph)) {
          spacingLength = spacingDir * glyph * fontSize / 1000;
          this.ctx.translate(spacingLength, 0);
          current.x += spacingLength * textHScale;
          continue;
        }
        var spacing = (glyph.isSpace ? wordSpacing : 0) + charSpacing;
        var operatorList = font.charProcOperatorList[glyph.operatorListId];
        if (!operatorList) {
          (0, _util.warn)(`Type3 character "${glyph.operatorListId}" is not available.`);
          continue;
        }
        this.processingType3 = glyph;
        this.save();
        ctx.scale(fontSize, fontSize);
        ctx.transform.apply(ctx, fontMatrix);
        this.executeOperatorList(operatorList);
        this.restore();
        var transformed = _util.Util.applyTransform([glyph.width, 0], fontMatrix);
        width = transformed[0] * fontSize + spacing;
        ctx.translate(width, 0);
        current.x += width * textHScale;
      }
      ctx.restore();
      this.processingType3 = null;
    },
    setCharWidth: function CanvasGraphics_setCharWidth(xWidth, yWidth) {},
    setCharWidthAndBounds: function CanvasGraphics_setCharWidthAndBounds(xWidth, yWidth, llx, lly, urx, ury) {
      this.ctx.rect(llx, lly, urx - llx, ury - lly);
      this.clip();
      this.endPath();
    },
    getColorN_Pattern: function CanvasGraphics_getColorN_Pattern(IR) {
      var pattern;
      if (IR[0] === 'TilingPattern') {
        var color = IR[1];
        var baseTransform = this.baseTransform || this.ctx.mozCurrentTransform.slice();
        var canvasGraphicsFactory = {
          createCanvasGraphics: ctx => {
            return new CanvasGraphics(ctx, this.commonObjs, this.objs, this.canvasFactory, this.webGLContext);
          }
        };
        pattern = new _pattern_helper.TilingPattern(IR, color, this.ctx, canvasGraphicsFactory, baseTransform);
      } else {
        pattern = (0, _pattern_helper.getShadingPatternFromIR)(IR);
      }
      return pattern;
    },
    setStrokeColorN: function CanvasGraphics_setStrokeColorN() {
      this.current.strokeColor = this.getColorN_Pattern(arguments);
    },
    setFillColorN: function CanvasGraphics_setFillColorN() {
      this.current.fillColor = this.getColorN_Pattern(arguments);
      this.current.patternFill = true;
    },
    setStrokeRGBColor: function CanvasGraphics_setStrokeRGBColor(r, g, b) {
      var color = _util.Util.makeCssRgb(r, g, b);
      this.ctx.strokeStyle = color;
      this.current.strokeColor = color;
    },
    setFillRGBColor: function CanvasGraphics_setFillRGBColor(r, g, b) {
      var color = _util.Util.makeCssRgb(r, g, b);
      this.ctx.fillStyle = color;
      this.current.fillColor = color;
      this.current.patternFill = false;
    },
    shadingFill: function CanvasGraphics_shadingFill(patternIR) {
      var ctx = this.ctx;
      this.save();
      var pattern = (0, _pattern_helper.getShadingPatternFromIR)(patternIR);
      ctx.fillStyle = pattern.getPattern(ctx, this, true);
      var inv = ctx.mozCurrentTransformInverse;
      if (inv) {
        var canvas = ctx.canvas;
        var width = canvas.width;
        var height = canvas.height;
        var bl = _util.Util.applyTransform([0, 0], inv);
        var br = _util.Util.applyTransform([0, height], inv);
        var ul = _util.Util.applyTransform([width, 0], inv);
        var ur = _util.Util.applyTransform([width, height], inv);
        var x0 = Math.min(bl[0], br[0], ul[0], ur[0]);
        var y0 = Math.min(bl[1], br[1], ul[1], ur[1]);
        var x1 = Math.max(bl[0], br[0], ul[0], ur[0]);
        var y1 = Math.max(bl[1], br[1], ul[1], ur[1]);
        this.ctx.fillRect(x0, y0, x1 - x0, y1 - y0);
      } else {
        this.ctx.fillRect(-1e10, -1e10, 2e10, 2e10);
      }
      this.restore();
    },
    beginInlineImage: function CanvasGraphics_beginInlineImage() {
      (0, _util.unreachable)('Should not call beginInlineImage');
    },
    beginImageData: function CanvasGraphics_beginImageData() {
      (0, _util.unreachable)('Should not call beginImageData');
    },
    paintFormXObjectBegin: function CanvasGraphics_paintFormXObjectBegin(matrix, bbox) {
      this.save();
      this.baseTransformStack.push(this.baseTransform);
      if (Array.isArray(matrix) && matrix.length === 6) {
        this.transform.apply(this, matrix);
      }
      this.baseTransform = this.ctx.mozCurrentTransform;
      if (Array.isArray(bbox) && bbox.length === 4) {
        var width = bbox[2] - bbox[0];
        var height = bbox[3] - bbox[1];
        this.ctx.rect(bbox[0], bbox[1], width, height);
        this.clip();
        this.endPath();
      }
    },
    paintFormXObjectEnd: function CanvasGraphics_paintFormXObjectEnd() {
      this.restore();
      this.baseTransform = this.baseTransformStack.pop();
    },
    beginGroup: function CanvasGraphics_beginGroup(group) {
      this.save();
      var currentCtx = this.ctx;
      if (!group.isolated) {
        (0, _util.info)('TODO: Support non-isolated groups.');
      }
      if (group.knockout) {
        (0, _util.warn)('Knockout groups not supported.');
      }
      var currentTransform = currentCtx.mozCurrentTransform;
      if (group.matrix) {
        currentCtx.transform.apply(currentCtx, group.matrix);
      }
      if (!group.bbox) {
        throw new Error('Bounding box is required.');
      }
      var bounds = _util.Util.getAxialAlignedBoundingBox(group.bbox, currentCtx.mozCurrentTransform);
      var canvasBounds = [0, 0, currentCtx.canvas.width, currentCtx.canvas.height];
      bounds = _util.Util.intersect(bounds, canvasBounds) || [0, 0, 0, 0];
      var offsetX = Math.floor(bounds[0]);
      var offsetY = Math.floor(bounds[1]);
      var drawnWidth = Math.max(Math.ceil(bounds[2]) - offsetX, 1);
      var drawnHeight = Math.max(Math.ceil(bounds[3]) - offsetY, 1);
      var scaleX = 1,
          scaleY = 1;
      if (drawnWidth > MAX_GROUP_SIZE) {
        scaleX = drawnWidth / MAX_GROUP_SIZE;
        drawnWidth = MAX_GROUP_SIZE;
      }
      if (drawnHeight > MAX_GROUP_SIZE) {
        scaleY = drawnHeight / MAX_GROUP_SIZE;
        drawnHeight = MAX_GROUP_SIZE;
      }
      var cacheId = 'groupAt' + this.groupLevel;
      if (group.smask) {
        cacheId += '_smask_' + this.smaskCounter++ % 2;
      }
      var scratchCanvas = this.cachedCanvases.getCanvas(cacheId, drawnWidth, drawnHeight, true);
      var groupCtx = scratchCanvas.context;
      groupCtx.scale(1 / scaleX, 1 / scaleY);
      groupCtx.translate(-offsetX, -offsetY);
      groupCtx.transform.apply(groupCtx, currentTransform);
      if (group.smask) {
        this.smaskStack.push({
          canvas: scratchCanvas.canvas,
          context: groupCtx,
          offsetX,
          offsetY,
          scaleX,
          scaleY,
          subtype: group.smask.subtype,
          backdrop: group.smask.backdrop,
          transferMap: group.smask.transferMap || null,
          startTransformInverse: null
        });
      } else {
        currentCtx.setTransform(1, 0, 0, 1, 0, 0);
        currentCtx.translate(offsetX, offsetY);
        currentCtx.scale(scaleX, scaleY);
      }
      copyCtxState(currentCtx, groupCtx);
      this.ctx = groupCtx;
      this.setGState([['BM', 'source-over'], ['ca', 1], ['CA', 1]]);
      this.groupStack.push(currentCtx);
      this.groupLevel++;
      this.current.activeSMask = null;
    },
    endGroup: function CanvasGraphics_endGroup(group) {
      this.groupLevel--;
      var groupCtx = this.ctx;
      this.ctx = this.groupStack.pop();
      if (this.ctx.imageSmoothingEnabled !== undefined) {
        this.ctx.imageSmoothingEnabled = false;
      } else {
        this.ctx.mozImageSmoothingEnabled = false;
      }
      if (group.smask) {
        this.tempSMask = this.smaskStack.pop();
      } else {
        this.ctx.drawImage(groupCtx.canvas, 0, 0);
      }
      this.restore();
    },
    beginAnnotations: function CanvasGraphics_beginAnnotations() {
      this.save();
      if (this.baseTransform) {
        this.ctx.setTransform.apply(this.ctx, this.baseTransform);
      }
    },
    endAnnotations: function CanvasGraphics_endAnnotations() {
      this.restore();
    },
    beginAnnotation: function CanvasGraphics_beginAnnotation(rect, transform, matrix) {
      this.save();
      resetCtxToDefault(this.ctx);
      this.current = new CanvasExtraState();
      if (Array.isArray(rect) && rect.length === 4) {
        var width = rect[2] - rect[0];
        var height = rect[3] - rect[1];
        this.ctx.rect(rect[0], rect[1], width, height);
        this.clip();
        this.endPath();
      }
      this.transform.apply(this, transform);
      this.transform.apply(this, matrix);
    },
    endAnnotation: function CanvasGraphics_endAnnotation() {
      this.restore();
    },
    paintJpegXObject: function CanvasGraphics_paintJpegXObject(objId, w, h) {
      var domImage = this.objs.get(objId);
      if (!domImage) {
        (0, _util.warn)('Dependent image isn\'t ready yet');
        return;
      }
      this.save();
      var ctx = this.ctx;
      ctx.scale(1 / w, -1 / h);
      ctx.drawImage(domImage, 0, 0, domImage.width, domImage.height, 0, -h, w, h);
      if (this.imageLayer) {
        var currentTransform = ctx.mozCurrentTransformInverse;
        var position = this.getCanvasPosition(0, 0);
        this.imageLayer.appendImage({
          objId,
          left: position[0],
          top: position[1],
          width: w / currentTransform[0],
          height: h / currentTransform[3]
        });
      }
      this.restore();
    },
    paintImageMaskXObject: function CanvasGraphics_paintImageMaskXObject(img) {
      var ctx = this.ctx;
      var width = img.width,
          height = img.height;
      var fillColor = this.current.fillColor;
      var isPatternFill = this.current.patternFill;
      var glyph = this.processingType3;
      if (COMPILE_TYPE3_GLYPHS && glyph && glyph.compiled === undefined) {
        if (width <= MAX_SIZE_TO_COMPILE && height <= MAX_SIZE_TO_COMPILE) {
          glyph.compiled = compileType3Glyph({
            data: img.data,
            width,
            height
          });
        } else {
          glyph.compiled = null;
        }
      }
      if (glyph && glyph.compiled) {
        glyph.compiled(ctx);
        return;
      }
      var maskCanvas = this.cachedCanvases.getCanvas('maskCanvas', width, height);
      var maskCtx = maskCanvas.context;
      maskCtx.save();
      putBinaryImageMask(maskCtx, img);
      maskCtx.globalCompositeOperation = 'source-in';
      maskCtx.fillStyle = isPatternFill ? fillColor.getPattern(maskCtx, this) : fillColor;
      maskCtx.fillRect(0, 0, width, height);
      maskCtx.restore();
      this.paintInlineImageXObject(maskCanvas.canvas);
    },
    paintImageMaskXObjectRepeat: function CanvasGraphics_paintImageMaskXObjectRepeat(imgData, scaleX, scaleY, positions) {
      var width = imgData.width;
      var height = imgData.height;
      var fillColor = this.current.fillColor;
      var isPatternFill = this.current.patternFill;
      var maskCanvas = this.cachedCanvases.getCanvas('maskCanvas', width, height);
      var maskCtx = maskCanvas.context;
      maskCtx.save();
      putBinaryImageMask(maskCtx, imgData);
      maskCtx.globalCompositeOperation = 'source-in';
      maskCtx.fillStyle = isPatternFill ? fillColor.getPattern(maskCtx, this) : fillColor;
      maskCtx.fillRect(0, 0, width, height);
      maskCtx.restore();
      var ctx = this.ctx;
      for (var i = 0, ii = positions.length; i < ii; i += 2) {
        ctx.save();
        ctx.transform(scaleX, 0, 0, scaleY, positions[i], positions[i + 1]);
        ctx.scale(1, -1);
        ctx.drawImage(maskCanvas.canvas, 0, 0, width, height, 0, -1, 1, 1);
        ctx.restore();
      }
    },
    paintImageMaskXObjectGroup: function CanvasGraphics_paintImageMaskXObjectGroup(images) {
      var ctx = this.ctx;
      var fillColor = this.current.fillColor;
      var isPatternFill = this.current.patternFill;
      for (var i = 0, ii = images.length; i < ii; i++) {
        var image = images[i];
        var width = image.width,
            height = image.height;
        var maskCanvas = this.cachedCanvases.getCanvas('maskCanvas', width, height);
        var maskCtx = maskCanvas.context;
        maskCtx.save();
        putBinaryImageMask(maskCtx, image);
        maskCtx.globalCompositeOperation = 'source-in';
        maskCtx.fillStyle = isPatternFill ? fillColor.getPattern(maskCtx, this) : fillColor;
        maskCtx.fillRect(0, 0, width, height);
        maskCtx.restore();
        ctx.save();
        ctx.transform.apply(ctx, image.transform);
        ctx.scale(1, -1);
        ctx.drawImage(maskCanvas.canvas, 0, 0, width, height, 0, -1, 1, 1);
        ctx.restore();
      }
    },
    paintImageXObject: function CanvasGraphics_paintImageXObject(objId) {
      var imgData = this.objs.get(objId);
      if (!imgData) {
        (0, _util.warn)('Dependent image isn\'t ready yet');
        return;
      }
      this.paintInlineImageXObject(imgData);
    },
    paintImageXObjectRepeat: function CanvasGraphics_paintImageXObjectRepeat(objId, scaleX, scaleY, positions) {
      var imgData = this.objs.get(objId);
      if (!imgData) {
        (0, _util.warn)('Dependent image isn\'t ready yet');
        return;
      }
      var width = imgData.width;
      var height = imgData.height;
      var map = [];
      for (var i = 0, ii = positions.length; i < ii; i += 2) {
        map.push({
          transform: [scaleX, 0, 0, scaleY, positions[i], positions[i + 1]],
          x: 0,
          y: 0,
          w: width,
          h: height
        });
      }
      this.paintInlineImageXObjectGroup(imgData, map);
    },
    paintInlineImageXObject: function CanvasGraphics_paintInlineImageXObject(imgData) {
      var width = imgData.width;
      var height = imgData.height;
      var ctx = this.ctx;
      this.save();
      ctx.scale(1 / width, -1 / height);
      var currentTransform = ctx.mozCurrentTransformInverse;
      var a = currentTransform[0],
          b = currentTransform[1];
      var widthScale = Math.max(Math.sqrt(a * a + b * b), 1);
      var c = currentTransform[2],
          d = currentTransform[3];
      var heightScale = Math.max(Math.sqrt(c * c + d * d), 1);
      var imgToPaint, tmpCanvas;
      if (imgData instanceof HTMLElement || !imgData.data) {
        imgToPaint = imgData;
      } else {
        tmpCanvas = this.cachedCanvases.getCanvas('inlineImage', width, height);
        var tmpCtx = tmpCanvas.context;
        putBinaryImageData(tmpCtx, imgData);
        imgToPaint = tmpCanvas.canvas;
      }
      var paintWidth = width,
          paintHeight = height;
      var tmpCanvasId = 'prescale1';
      while (widthScale > 2 && paintWidth > 1 || heightScale > 2 && paintHeight > 1) {
        var newWidth = paintWidth,
            newHeight = paintHeight;
        if (widthScale > 2 && paintWidth > 1) {
          newWidth = Math.ceil(paintWidth / 2);
          widthScale /= paintWidth / newWidth;
        }
        if (heightScale > 2 && paintHeight > 1) {
          newHeight = Math.ceil(paintHeight / 2);
          heightScale /= paintHeight / newHeight;
        }
        tmpCanvas = this.cachedCanvases.getCanvas(tmpCanvasId, newWidth, newHeight);
        tmpCtx = tmpCanvas.context;
        tmpCtx.clearRect(0, 0, newWidth, newHeight);
        tmpCtx.drawImage(imgToPaint, 0, 0, paintWidth, paintHeight, 0, 0, newWidth, newHeight);
        imgToPaint = tmpCanvas.canvas;
        paintWidth = newWidth;
        paintHeight = newHeight;
        tmpCanvasId = tmpCanvasId === 'prescale1' ? 'prescale2' : 'prescale1';
      }
      ctx.drawImage(imgToPaint, 0, 0, paintWidth, paintHeight, 0, -height, width, height);
      if (this.imageLayer) {
        var position = this.getCanvasPosition(0, -height);
        this.imageLayer.appendImage({
          imgData,
          left: position[0],
          top: position[1],
          width: width / currentTransform[0],
          height: height / currentTransform[3]
        });
      }
      this.restore();
    },
    paintInlineImageXObjectGroup: function CanvasGraphics_paintInlineImageXObjectGroup(imgData, map) {
      var ctx = this.ctx;
      var w = imgData.width;
      var h = imgData.height;
      var tmpCanvas = this.cachedCanvases.getCanvas('inlineImage', w, h);
      var tmpCtx = tmpCanvas.context;
      putBinaryImageData(tmpCtx, imgData);
      for (var i = 0, ii = map.length; i < ii; i++) {
        var entry = map[i];
        ctx.save();
        ctx.transform.apply(ctx, entry.transform);
        ctx.scale(1, -1);
        ctx.drawImage(tmpCanvas.canvas, entry.x, entry.y, entry.w, entry.h, 0, -1, 1, 1);
        if (this.imageLayer) {
          var position = this.getCanvasPosition(entry.x, entry.y);
          this.imageLayer.appendImage({
            imgData,
            left: position[0],
            top: position[1],
            width: w,
            height: h
          });
        }
        ctx.restore();
      }
    },
    paintSolidColorImageMask: function CanvasGraphics_paintSolidColorImageMask() {
      this.ctx.fillRect(0, 0, 1, 1);
    },
    paintXObject: function CanvasGraphics_paintXObject() {
      (0, _util.warn)('Unsupported \'paintXObject\' command.');
    },
    markPoint: function CanvasGraphics_markPoint(tag) {},
    markPointProps: function CanvasGraphics_markPointProps(tag, properties) {},
    beginMarkedContent: function CanvasGraphics_beginMarkedContent(tag) {},
    beginMarkedContentProps: function CanvasGraphics_beginMarkedContentProps(tag, properties) {},
    endMarkedContent: function CanvasGraphics_endMarkedContent() {},
    beginCompat: function CanvasGraphics_beginCompat() {},
    endCompat: function CanvasGraphics_endCompat() {},
    consumePath: function CanvasGraphics_consumePath() {
      var ctx = this.ctx;
      if (this.pendingClip) {
        if (this.pendingClip === EO_CLIP) {
          ctx.clip('evenodd');
        } else {
          ctx.clip();
        }
        this.pendingClip = null;
      }
      ctx.beginPath();
    },
    getSinglePixelWidth: function CanvasGraphics_getSinglePixelWidth(scale) {
      if (this.cachedGetSinglePixelWidth === null) {
        this.ctx.save();
        var inverse = this.ctx.mozCurrentTransformInverse;
        this.ctx.restore();
        this.cachedGetSinglePixelWidth = Math.sqrt(Math.max(inverse[0] * inverse[0] + inverse[1] * inverse[1], inverse[2] * inverse[2] + inverse[3] * inverse[3]));
      }
      return this.cachedGetSinglePixelWidth;
    },
    getCanvasPosition: function CanvasGraphics_getCanvasPosition(x, y) {
      var transform = this.ctx.mozCurrentTransform;
      return [transform[0] * x + transform[2] * y + transform[4], transform[1] * x + transform[3] * y + transform[5]];
    }
  };
  for (var op in _util.OPS) {
    CanvasGraphics.prototype[_util.OPS[op]] = CanvasGraphics.prototype[op];
  }
  return CanvasGraphics;
}();
exports.CanvasGraphics = CanvasGraphics;

/***/ }),
/* 16 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.TilingPattern = exports.getShadingPatternFromIR = undefined;

var _util = __w_pdfjs_require__(0);

var ShadingIRs = {};
ShadingIRs.RadialAxial = {
  fromIR: function RadialAxial_fromIR(raw) {
    var type = raw[1];
    var colorStops = raw[2];
    var p0 = raw[3];
    var p1 = raw[4];
    var r0 = raw[5];
    var r1 = raw[6];
    return {
      type: 'Pattern',
      getPattern: function RadialAxial_getPattern(ctx) {
        var grad;
        if (type === 'axial') {
          grad = ctx.createLinearGradient(p0[0], p0[1], p1[0], p1[1]);
        } else if (type === 'radial') {
          grad = ctx.createRadialGradient(p0[0], p0[1], r0, p1[0], p1[1], r1);
        }
        for (var i = 0, ii = colorStops.length; i < ii; ++i) {
          var c = colorStops[i];
          grad.addColorStop(c[0], c[1]);
        }
        return grad;
      }
    };
  }
};
var createMeshCanvas = function createMeshCanvasClosure() {
  function drawTriangle(data, context, p1, p2, p3, c1, c2, c3) {
    var coords = context.coords,
        colors = context.colors;
    var bytes = data.data,
        rowSize = data.width * 4;
    var tmp;
    if (coords[p1 + 1] > coords[p2 + 1]) {
      tmp = p1;
      p1 = p2;
      p2 = tmp;
      tmp = c1;
      c1 = c2;
      c2 = tmp;
    }
    if (coords[p2 + 1] > coords[p3 + 1]) {
      tmp = p2;
      p2 = p3;
      p3 = tmp;
      tmp = c2;
      c2 = c3;
      c3 = tmp;
    }
    if (coords[p1 + 1] > coords[p2 + 1]) {
      tmp = p1;
      p1 = p2;
      p2 = tmp;
      tmp = c1;
      c1 = c2;
      c2 = tmp;
    }
    var x1 = (coords[p1] + context.offsetX) * context.scaleX;
    var y1 = (coords[p1 + 1] + context.offsetY) * context.scaleY;
    var x2 = (coords[p2] + context.offsetX) * context.scaleX;
    var y2 = (coords[p2 + 1] + context.offsetY) * context.scaleY;
    var x3 = (coords[p3] + context.offsetX) * context.scaleX;
    var y3 = (coords[p3 + 1] + context.offsetY) * context.scaleY;
    if (y1 >= y3) {
      return;
    }
    var c1r = colors[c1],
        c1g = colors[c1 + 1],
        c1b = colors[c1 + 2];
    var c2r = colors[c2],
        c2g = colors[c2 + 1],
        c2b = colors[c2 + 2];
    var c3r = colors[c3],
        c3g = colors[c3 + 1],
        c3b = colors[c3 + 2];
    var minY = Math.round(y1),
        maxY = Math.round(y3);
    var xa, car, cag, cab;
    var xb, cbr, cbg, cbb;
    var k;
    for (var y = minY; y <= maxY; y++) {
      if (y < y2) {
        k = y < y1 ? 0 : y1 === y2 ? 1 : (y1 - y) / (y1 - y2);
        xa = x1 - (x1 - x2) * k;
        car = c1r - (c1r - c2r) * k;
        cag = c1g - (c1g - c2g) * k;
        cab = c1b - (c1b - c2b) * k;
      } else {
        k = y > y3 ? 1 : y2 === y3 ? 0 : (y2 - y) / (y2 - y3);
        xa = x2 - (x2 - x3) * k;
        car = c2r - (c2r - c3r) * k;
        cag = c2g - (c2g - c3g) * k;
        cab = c2b - (c2b - c3b) * k;
      }
      k = y < y1 ? 0 : y > y3 ? 1 : (y1 - y) / (y1 - y3);
      xb = x1 - (x1 - x3) * k;
      cbr = c1r - (c1r - c3r) * k;
      cbg = c1g - (c1g - c3g) * k;
      cbb = c1b - (c1b - c3b) * k;
      var x1_ = Math.round(Math.min(xa, xb));
      var x2_ = Math.round(Math.max(xa, xb));
      var j = rowSize * y + x1_ * 4;
      for (var x = x1_; x <= x2_; x++) {
        k = (xa - x) / (xa - xb);
        k = k < 0 ? 0 : k > 1 ? 1 : k;
        bytes[j++] = car - (car - cbr) * k | 0;
        bytes[j++] = cag - (cag - cbg) * k | 0;
        bytes[j++] = cab - (cab - cbb) * k | 0;
        bytes[j++] = 255;
      }
    }
  }
  function drawFigure(data, figure, context) {
    var ps = figure.coords;
    var cs = figure.colors;
    var i, ii;
    switch (figure.type) {
      case 'lattice':
        var verticesPerRow = figure.verticesPerRow;
        var rows = Math.floor(ps.length / verticesPerRow) - 1;
        var cols = verticesPerRow - 1;
        for (i = 0; i < rows; i++) {
          var q = i * verticesPerRow;
          for (var j = 0; j < cols; j++, q++) {
            drawTriangle(data, context, ps[q], ps[q + 1], ps[q + verticesPerRow], cs[q], cs[q + 1], cs[q + verticesPerRow]);
            drawTriangle(data, context, ps[q + verticesPerRow + 1], ps[q + 1], ps[q + verticesPerRow], cs[q + verticesPerRow + 1], cs[q + 1], cs[q + verticesPerRow]);
          }
        }
        break;
      case 'triangles':
        for (i = 0, ii = ps.length; i < ii; i += 3) {
          drawTriangle(data, context, ps[i], ps[i + 1], ps[i + 2], cs[i], cs[i + 1], cs[i + 2]);
        }
        break;
      default:
        throw new Error('illegal figure');
    }
  }
  function createMeshCanvas(bounds, combinesScale, coords, colors, figures, backgroundColor, cachedCanvases, webGLContext) {
    var EXPECTED_SCALE = 1.1;
    var MAX_PATTERN_SIZE = 3000;
    var BORDER_SIZE = 2;
    var offsetX = Math.floor(bounds[0]);
    var offsetY = Math.floor(bounds[1]);
    var boundsWidth = Math.ceil(bounds[2]) - offsetX;
    var boundsHeight = Math.ceil(bounds[3]) - offsetY;
    var width = Math.min(Math.ceil(Math.abs(boundsWidth * combinesScale[0] * EXPECTED_SCALE)), MAX_PATTERN_SIZE);
    var height = Math.min(Math.ceil(Math.abs(boundsHeight * combinesScale[1] * EXPECTED_SCALE)), MAX_PATTERN_SIZE);
    var scaleX = boundsWidth / width;
    var scaleY = boundsHeight / height;
    var context = {
      coords,
      colors,
      offsetX: -offsetX,
      offsetY: -offsetY,
      scaleX: 1 / scaleX,
      scaleY: 1 / scaleY
    };
    var paddedWidth = width + BORDER_SIZE * 2;
    var paddedHeight = height + BORDER_SIZE * 2;
    var canvas, tmpCanvas, i, ii;
    if (webGLContext.isEnabled) {
      canvas = webGLContext.drawFigures({
        width,
        height,
        backgroundColor,
        figures,
        context
      });
      tmpCanvas = cachedCanvases.getCanvas('mesh', paddedWidth, paddedHeight, false);
      tmpCanvas.context.drawImage(canvas, BORDER_SIZE, BORDER_SIZE);
      canvas = tmpCanvas.canvas;
    } else {
      tmpCanvas = cachedCanvases.getCanvas('mesh', paddedWidth, paddedHeight, false);
      var tmpCtx = tmpCanvas.context;
      var data = tmpCtx.createImageData(width, height);
      if (backgroundColor) {
        var bytes = data.data;
        for (i = 0, ii = bytes.length; i < ii; i += 4) {
          bytes[i] = backgroundColor[0];
          bytes[i + 1] = backgroundColor[1];
          bytes[i + 2] = backgroundColor[2];
          bytes[i + 3] = 255;
        }
      }
      for (i = 0; i < figures.length; i++) {
        drawFigure(data, figures[i], context);
      }
      tmpCtx.putImageData(data, BORDER_SIZE, BORDER_SIZE);
      canvas = tmpCanvas.canvas;
    }
    return {
      canvas,
      offsetX: offsetX - BORDER_SIZE * scaleX,
      offsetY: offsetY - BORDER_SIZE * scaleY,
      scaleX,
      scaleY
    };
  }
  return createMeshCanvas;
}();
ShadingIRs.Mesh = {
  fromIR: function Mesh_fromIR(raw) {
    var coords = raw[2];
    var colors = raw[3];
    var figures = raw[4];
    var bounds = raw[5];
    var matrix = raw[6];
    var background = raw[8];
    return {
      type: 'Pattern',
      getPattern: function Mesh_getPattern(ctx, owner, shadingFill) {
        var scale;
        if (shadingFill) {
          scale = _util.Util.singularValueDecompose2dScale(ctx.mozCurrentTransform);
        } else {
          scale = _util.Util.singularValueDecompose2dScale(owner.baseTransform);
          if (matrix) {
            var matrixScale = _util.Util.singularValueDecompose2dScale(matrix);
            scale = [scale[0] * matrixScale[0], scale[1] * matrixScale[1]];
          }
        }
        var temporaryPatternCanvas = createMeshCanvas(bounds, scale, coords, colors, figures, shadingFill ? null : background, owner.cachedCanvases, owner.webGLContext);
        if (!shadingFill) {
          ctx.setTransform.apply(ctx, owner.baseTransform);
          if (matrix) {
            ctx.transform.apply(ctx, matrix);
          }
        }
        ctx.translate(temporaryPatternCanvas.offsetX, temporaryPatternCanvas.offsetY);
        ctx.scale(temporaryPatternCanvas.scaleX, temporaryPatternCanvas.scaleY);
        return ctx.createPattern(temporaryPatternCanvas.canvas, 'no-repeat');
      }
    };
  }
};
ShadingIRs.Dummy = {
  fromIR: function Dummy_fromIR() {
    return {
      type: 'Pattern',
      getPattern: function Dummy_fromIR_getPattern() {
        return 'hotpink';
      }
    };
  }
};
function getShadingPatternFromIR(raw) {
  var shadingIR = ShadingIRs[raw[0]];
  if (!shadingIR) {
    throw new Error(`Unknown IR type: ${raw[0]}`);
  }
  return shadingIR.fromIR(raw);
}
var TilingPattern = function TilingPatternClosure() {
  var PaintType = {
    COLORED: 1,
    UNCOLORED: 2
  };
  var MAX_PATTERN_SIZE = 3000;
  function TilingPattern(IR, color, ctx, canvasGraphicsFactory, baseTransform) {
    this.operatorList = IR[2];
    this.matrix = IR[3] || [1, 0, 0, 1, 0, 0];
    this.bbox = IR[4];
    this.xstep = IR[5];
    this.ystep = IR[6];
    this.paintType = IR[7];
    this.tilingType = IR[8];
    this.color = color;
    this.canvasGraphicsFactory = canvasGraphicsFactory;
    this.baseTransform = baseTransform;
    this.type = 'Pattern';
    this.ctx = ctx;
  }
  TilingPattern.prototype = {
    createPatternCanvas: function TilinPattern_createPatternCanvas(owner) {
      var operatorList = this.operatorList;
      var bbox = this.bbox;
      var xstep = this.xstep;
      var ystep = this.ystep;
      var paintType = this.paintType;
      var tilingType = this.tilingType;
      var color = this.color;
      var canvasGraphicsFactory = this.canvasGraphicsFactory;
      (0, _util.info)('TilingType: ' + tilingType);
      var x0 = bbox[0],
          y0 = bbox[1],
          x1 = bbox[2],
          y1 = bbox[3];
      var topLeft = [x0, y0];
      var botRight = [x0 + xstep, y0 + ystep];
      var width = botRight[0] - topLeft[0];
      var height = botRight[1] - topLeft[1];
      var matrixScale = _util.Util.singularValueDecompose2dScale(this.matrix);
      var curMatrixScale = _util.Util.singularValueDecompose2dScale(this.baseTransform);
      var combinedScale = [matrixScale[0] * curMatrixScale[0], matrixScale[1] * curMatrixScale[1]];
      width = Math.min(Math.ceil(Math.abs(width * combinedScale[0])), MAX_PATTERN_SIZE);
      height = Math.min(Math.ceil(Math.abs(height * combinedScale[1])), MAX_PATTERN_SIZE);
      var tmpCanvas = owner.cachedCanvases.getCanvas('pattern', width, height, true);
      var tmpCtx = tmpCanvas.context;
      var graphics = canvasGraphicsFactory.createCanvasGraphics(tmpCtx);
      graphics.groupLevel = owner.groupLevel;
      this.setFillAndStrokeStyleToContext(graphics, paintType, color);
      this.setScale(width, height, xstep, ystep);
      this.transformToScale(graphics);
      var tmpTranslate = [1, 0, 0, 1, -topLeft[0], -topLeft[1]];
      graphics.transform.apply(graphics, tmpTranslate);
      this.clipBbox(graphics, bbox, x0, y0, x1, y1);
      graphics.executeOperatorList(operatorList);
      return tmpCanvas.canvas;
    },
    setScale: function TilingPattern_setScale(width, height, xstep, ystep) {
      this.scale = [width / xstep, height / ystep];
    },
    transformToScale: function TilingPattern_transformToScale(graphics) {
      var scale = this.scale;
      var tmpScale = [scale[0], 0, 0, scale[1], 0, 0];
      graphics.transform.apply(graphics, tmpScale);
    },
    scaleToContext: function TilingPattern_scaleToContext() {
      var scale = this.scale;
      this.ctx.scale(1 / scale[0], 1 / scale[1]);
    },
    clipBbox: function clipBbox(graphics, bbox, x0, y0, x1, y1) {
      if (Array.isArray(bbox) && bbox.length === 4) {
        var bboxWidth = x1 - x0;
        var bboxHeight = y1 - y0;
        graphics.ctx.rect(x0, y0, bboxWidth, bboxHeight);
        graphics.clip();
        graphics.endPath();
      }
    },
    setFillAndStrokeStyleToContext: function setFillAndStrokeStyleToContext(graphics, paintType, color) {
      let context = graphics.ctx,
          current = graphics.current;
      switch (paintType) {
        case PaintType.COLORED:
          var ctx = this.ctx;
          context.fillStyle = ctx.fillStyle;
          context.strokeStyle = ctx.strokeStyle;
          current.fillColor = ctx.fillStyle;
          current.strokeColor = ctx.strokeStyle;
          break;
        case PaintType.UNCOLORED:
          var cssColor = _util.Util.makeCssRgb(color[0], color[1], color[2]);
          context.fillStyle = cssColor;
          context.strokeStyle = cssColor;
          current.fillColor = cssColor;
          current.strokeColor = cssColor;
          break;
        default:
          throw new _util.FormatError(`Unsupported paint type: ${paintType}`);
      }
    },
    getPattern: function TilingPattern_getPattern(ctx, owner) {
      var temporaryPatternCanvas = this.createPatternCanvas(owner);
      ctx = this.ctx;
      ctx.setTransform.apply(ctx, this.baseTransform);
      ctx.transform.apply(ctx, this.matrix);
      this.scaleToContext();
      return ctx.createPattern(temporaryPatternCanvas, 'repeat');
    }
  };
  return TilingPattern;
}();
exports.getShadingPatternFromIR = getShadingPatternFromIR;
exports.TilingPattern = TilingPattern;

/***/ }),
/* 17 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.PDFDataTransportStream = undefined;

var _util = __w_pdfjs_require__(0);

var PDFDataTransportStream = function PDFDataTransportStreamClosure() {
  function PDFDataTransportStream(params, pdfDataRangeTransport) {
    (0, _util.assert)(pdfDataRangeTransport);
    this._queuedChunks = [];
    var initialData = params.initialData;
    if (initialData && initialData.length > 0) {
      let buffer = new Uint8Array(initialData).buffer;
      this._queuedChunks.push(buffer);
    }
    this._pdfDataRangeTransport = pdfDataRangeTransport;
    this._isStreamingSupported = !params.disableStream;
    this._isRangeSupported = !params.disableRange;
    this._contentLength = params.length;
    this._fullRequestReader = null;
    this._rangeReaders = [];
    this._pdfDataRangeTransport.addRangeListener((begin, chunk) => {
      this._onReceiveData({
        begin,
        chunk
      });
    });
    this._pdfDataRangeTransport.addProgressListener(loaded => {
      this._onProgress({ loaded });
    });
    this._pdfDataRangeTransport.addProgressiveReadListener(chunk => {
      this._onReceiveData({ chunk });
    });
    this._pdfDataRangeTransport.transportReady();
  }
  PDFDataTransportStream.prototype = {
    _onReceiveData: function PDFDataTransportStream_onReceiveData(args) {
      let buffer = new Uint8Array(args.chunk).buffer;
      if (args.begin === undefined) {
        if (this._fullRequestReader) {
          this._fullRequestReader._enqueue(buffer);
        } else {
          this._queuedChunks.push(buffer);
        }
      } else {
        var found = this._rangeReaders.some(function (rangeReader) {
          if (rangeReader._begin !== args.begin) {
            return false;
          }
          rangeReader._enqueue(buffer);
          return true;
        });
        (0, _util.assert)(found);
      }
    },
    _onProgress: function PDFDataTransportStream_onDataProgress(evt) {
      if (this._rangeReaders.length > 0) {
        var firstReader = this._rangeReaders[0];
        if (firstReader.onProgress) {
          firstReader.onProgress({ loaded: evt.loaded });
        }
      }
    },
    _removeRangeReader: function PDFDataTransportStream_removeRangeReader(reader) {
      var i = this._rangeReaders.indexOf(reader);
      if (i >= 0) {
        this._rangeReaders.splice(i, 1);
      }
    },
    getFullReader: function PDFDataTransportStream_getFullReader() {
      (0, _util.assert)(!this._fullRequestReader);
      var queuedChunks = this._queuedChunks;
      this._queuedChunks = null;
      return new PDFDataTransportStreamReader(this, queuedChunks);
    },
    getRangeReader: function PDFDataTransportStream_getRangeReader(begin, end) {
      var reader = new PDFDataTransportStreamRangeReader(this, begin, end);
      this._pdfDataRangeTransport.requestDataRange(begin, end);
      this._rangeReaders.push(reader);
      return reader;
    },
    cancelAllRequests: function PDFDataTransportStream_cancelAllRequests(reason) {
      if (this._fullRequestReader) {
        this._fullRequestReader.cancel(reason);
      }
      var readers = this._rangeReaders.slice(0);
      readers.forEach(function (rangeReader) {
        rangeReader.cancel(reason);
      });
      this._pdfDataRangeTransport.abort();
    }
  };
  function PDFDataTransportStreamReader(stream, queuedChunks) {
    this._stream = stream;
    this._done = false;
    this._filename = null;
    this._queuedChunks = queuedChunks || [];
    this._requests = [];
    this._headersReady = Promise.resolve();
    stream._fullRequestReader = this;
    this.onProgress = null;
  }
  PDFDataTransportStreamReader.prototype = {
    _enqueue: function PDFDataTransportStreamReader_enqueue(chunk) {
      if (this._done) {
        return;
      }
      if (this._requests.length > 0) {
        var requestCapability = this._requests.shift();
        requestCapability.resolve({
          value: chunk,
          done: false
        });
        return;
      }
      this._queuedChunks.push(chunk);
    },
    get headersReady() {
      return this._headersReady;
    },
    get filename() {
      return this._filename;
    },
    get isRangeSupported() {
      return this._stream._isRangeSupported;
    },
    get isStreamingSupported() {
      return this._stream._isStreamingSupported;
    },
    get contentLength() {
      return this._stream._contentLength;
    },
    read: function PDFDataTransportStreamReader_read() {
      if (this._queuedChunks.length > 0) {
        var chunk = this._queuedChunks.shift();
        return Promise.resolve({
          value: chunk,
          done: false
        });
      }
      if (this._done) {
        return Promise.resolve({
          value: undefined,
          done: true
        });
      }
      var requestCapability = (0, _util.createPromiseCapability)();
      this._requests.push(requestCapability);
      return requestCapability.promise;
    },
    cancel: function PDFDataTransportStreamReader_cancel(reason) {
      this._done = true;
      this._requests.forEach(function (requestCapability) {
        requestCapability.resolve({
          value: undefined,
          done: true
        });
      });
      this._requests = [];
    }
  };
  function PDFDataTransportStreamRangeReader(stream, begin, end) {
    this._stream = stream;
    this._begin = begin;
    this._end = end;
    this._queuedChunk = null;
    this._requests = [];
    this._done = false;
    this.onProgress = null;
  }
  PDFDataTransportStreamRangeReader.prototype = {
    _enqueue: function PDFDataTransportStreamRangeReader_enqueue(chunk) {
      if (this._done) {
        return;
      }
      if (this._requests.length === 0) {
        this._queuedChunk = chunk;
      } else {
        var requestsCapability = this._requests.shift();
        requestsCapability.resolve({
          value: chunk,
          done: false
        });
        this._requests.forEach(function (requestCapability) {
          requestCapability.resolve({
            value: undefined,
            done: true
          });
        });
        this._requests = [];
      }
      this._done = true;
      this._stream._removeRangeReader(this);
    },
    get isStreamingSupported() {
      return false;
    },
    read: function PDFDataTransportStreamRangeReader_read() {
      if (this._queuedChunk) {
        let chunk = this._queuedChunk;
        this._queuedChunk = null;
        return Promise.resolve({
          value: chunk,
          done: false
        });
      }
      if (this._done) {
        return Promise.resolve({
          value: undefined,
          done: true
        });
      }
      var requestCapability = (0, _util.createPromiseCapability)();
      this._requests.push(requestCapability);
      return requestCapability.promise;
    },
    cancel: function PDFDataTransportStreamRangeReader_cancel(reason) {
      this._done = true;
      this._requests.forEach(function (requestCapability) {
        requestCapability.resolve({
          value: undefined,
          done: true
        });
      });
      this._requests = [];
      this._stream._removeRangeReader(this);
    }
  };
  return PDFDataTransportStream;
}();
exports.PDFDataTransportStream = PDFDataTransportStream;

/***/ }),
/* 18 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.WebGLContext = undefined;

var _util = __w_pdfjs_require__(0);

class WebGLContext {
  constructor({
    enable = false
  }) {
    this._enabled = enable === true;
  }
  get isEnabled() {
    let enabled = this._enabled;
    if (enabled) {
      enabled = WebGLUtils.tryInitGL();
    }
    return (0, _util.shadow)(this, 'isEnabled', enabled);
  }
  composeSMask({ layer, mask, properties }) {
    return WebGLUtils.composeSMask(layer, mask, properties);
  }
  drawFigures({ width, height, backgroundColor, figures, context }) {
    return WebGLUtils.drawFigures(width, height, backgroundColor, figures, context);
  }
  clear() {
    WebGLUtils.cleanup();
  }
}
var WebGLUtils = function WebGLUtilsClosure() {
  function loadShader(gl, code, shaderType) {
    var shader = gl.createShader(shaderType);
    gl.shaderSource(shader, code);
    gl.compileShader(shader);
    var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
    if (!compiled) {
      var errorMsg = gl.getShaderInfoLog(shader);
      throw new Error('Error during shader compilation: ' + errorMsg);
    }
    return shader;
  }
  function createVertexShader(gl, code) {
    return loadShader(gl, code, gl.VERTEX_SHADER);
  }
  function createFragmentShader(gl, code) {
    return loadShader(gl, code, gl.FRAGMENT_SHADER);
  }
  function createProgram(gl, shaders) {
    var program = gl.createProgram();
    for (var i = 0, ii = shaders.length; i < ii; ++i) {
      gl.attachShader(program, shaders[i]);
    }
    gl.linkProgram(program);
    var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
    if (!linked) {
      var errorMsg = gl.getProgramInfoLog(program);
      throw new Error('Error during program linking: ' + errorMsg);
    }
    return program;
  }
  function createTexture(gl, image, textureId) {
    gl.activeTexture(textureId);
    var texture = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, texture);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
    return texture;
  }
  var currentGL, currentCanvas;
  function generateGL() {
    if (currentGL) {
      return;
    }
    currentCanvas = document.createElement('canvas');
    currentGL = currentCanvas.getContext('webgl', { premultipliedalpha: false });
  }
  var smaskVertexShaderCode = '\
  attribute vec2 a_position;                                    \
  attribute vec2 a_texCoord;                                    \
                                                                \
  uniform vec2 u_resolution;                                    \
                                                                \
  varying vec2 v_texCoord;                                      \
                                                                \
  void main() {                                                 \
    vec2 clipSpace = (a_position / u_resolution) * 2.0 - 1.0;   \
    gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);          \
                                                                \
    v_texCoord = a_texCoord;                                    \
  }                                                             ';
  var smaskFragmentShaderCode = '\
  precision mediump float;                                      \
                                                                \
  uniform vec4 u_backdrop;                                      \
  uniform int u_subtype;                                        \
  uniform sampler2D u_image;                                    \
  uniform sampler2D u_mask;                                     \
                                                                \
  varying vec2 v_texCoord;                                      \
                                                                \
  void main() {                                                 \
    vec4 imageColor = texture2D(u_image, v_texCoord);           \
    vec4 maskColor = texture2D(u_mask, v_texCoord);             \
    if (u_backdrop.a > 0.0) {                                   \
      maskColor.rgb = maskColor.rgb * maskColor.a +             \
                      u_backdrop.rgb * (1.0 - maskColor.a);     \
    }                                                           \
    float lum;                                                  \
    if (u_subtype == 0) {                                       \
      lum = maskColor.a;                                        \
    } else {                                                    \
      lum = maskColor.r * 0.3 + maskColor.g * 0.59 +            \
            maskColor.b * 0.11;                                 \
    }                                                           \
    imageColor.a *= lum;                                        \
    imageColor.rgb *= imageColor.a;                             \
    gl_FragColor = imageColor;                                  \
  }                                                             ';
  var smaskCache = null;
  function initSmaskGL() {
    var canvas, gl;
    generateGL();
    canvas = currentCanvas;
    currentCanvas = null;
    gl = currentGL;
    currentGL = null;
    var vertexShader = createVertexShader(gl, smaskVertexShaderCode);
    var fragmentShader = createFragmentShader(gl, smaskFragmentShaderCode);
    var program = createProgram(gl, [vertexShader, fragmentShader]);
    gl.useProgram(program);
    var cache = {};
    cache.gl = gl;
    cache.canvas = canvas;
    cache.resolutionLocation = gl.getUniformLocation(program, 'u_resolution');
    cache.positionLocation = gl.getAttribLocation(program, 'a_position');
    cache.backdropLocation = gl.getUniformLocation(program, 'u_backdrop');
    cache.subtypeLocation = gl.getUniformLocation(program, 'u_subtype');
    var texCoordLocation = gl.getAttribLocation(program, 'a_texCoord');
    var texLayerLocation = gl.getUniformLocation(program, 'u_image');
    var texMaskLocation = gl.getUniformLocation(program, 'u_mask');
    var texCoordBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0]), gl.STATIC_DRAW);
    gl.enableVertexAttribArray(texCoordLocation);
    gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0);
    gl.uniform1i(texLayerLocation, 0);
    gl.uniform1i(texMaskLocation, 1);
    smaskCache = cache;
  }
  function composeSMask(layer, mask, properties) {
    var width = layer.width,
        height = layer.height;
    if (!smaskCache) {
      initSmaskGL();
    }
    var cache = smaskCache,
        canvas = cache.canvas,
        gl = cache.gl;
    canvas.width = width;
    canvas.height = height;
    gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
    gl.uniform2f(cache.resolutionLocation, width, height);
    if (properties.backdrop) {
      gl.uniform4f(cache.resolutionLocation, properties.backdrop[0], properties.backdrop[1], properties.backdrop[2], 1);
    } else {
      gl.uniform4f(cache.resolutionLocation, 0, 0, 0, 0);
    }
    gl.uniform1i(cache.subtypeLocation, properties.subtype === 'Luminosity' ? 1 : 0);
    var texture = createTexture(gl, layer, gl.TEXTURE0);
    var maskTexture = createTexture(gl, mask, gl.TEXTURE1);
    var buffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0, 0, width, 0, 0, height, 0, height, width, 0, width, height]), gl.STATIC_DRAW);
    gl.enableVertexAttribArray(cache.positionLocation);
    gl.vertexAttribPointer(cache.positionLocation, 2, gl.FLOAT, false, 0, 0);
    gl.clearColor(0, 0, 0, 0);
    gl.enable(gl.BLEND);
    gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.drawArrays(gl.TRIANGLES, 0, 6);
    gl.flush();
    gl.deleteTexture(texture);
    gl.deleteTexture(maskTexture);
    gl.deleteBuffer(buffer);
    return canvas;
  }
  var figuresVertexShaderCode = '\
  attribute vec2 a_position;                                    \
  attribute vec3 a_color;                                       \
                                                                \
  uniform vec2 u_resolution;                                    \
  uniform vec2 u_scale;                                         \
  uniform vec2 u_offset;                                        \
                                                                \
  varying vec4 v_color;                                         \
                                                                \
  void main() {                                                 \
    vec2 position = (a_position + u_offset) * u_scale;          \
    vec2 clipSpace = (position / u_resolution) * 2.0 - 1.0;     \
    gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);          \
                                                                \
    v_color = vec4(a_color / 255.0, 1.0);                       \
  }                                                             ';
  var figuresFragmentShaderCode = '\
  precision mediump float;                                      \
                                                                \
  varying vec4 v_color;                                         \
                                                                \
  void main() {                                                 \
    gl_FragColor = v_color;                                     \
  }                                                             ';
  var figuresCache = null;
  function initFiguresGL() {
    var canvas, gl;
    generateGL();
    canvas = currentCanvas;
    currentCanvas = null;
    gl = currentGL;
    currentGL = null;
    var vertexShader = createVertexShader(gl, figuresVertexShaderCode);
    var fragmentShader = createFragmentShader(gl, figuresFragmentShaderCode);
    var program = createProgram(gl, [vertexShader, fragmentShader]);
    gl.useProgram(program);
    var cache = {};
    cache.gl = gl;
    cache.canvas = canvas;
    cache.resolutionLocation = gl.getUniformLocation(program, 'u_resolution');
    cache.scaleLocation = gl.getUniformLocation(program, 'u_scale');
    cache.offsetLocation = gl.getUniformLocation(program, 'u_offset');
    cache.positionLocation = gl.getAttribLocation(program, 'a_position');
    cache.colorLocation = gl.getAttribLocation(program, 'a_color');
    figuresCache = cache;
  }
  function drawFigures(width, height, backgroundColor, figures, context) {
    if (!figuresCache) {
      initFiguresGL();
    }
    var cache = figuresCache,
        canvas = cache.canvas,
        gl = cache.gl;
    canvas.width = width;
    canvas.height = height;
    gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
    gl.uniform2f(cache.resolutionLocation, width, height);
    var count = 0;
    var i, ii, rows;
    for (i = 0, ii = figures.length; i < ii; i++) {
      switch (figures[i].type) {
        case 'lattice':
          rows = figures[i].coords.length / figures[i].verticesPerRow | 0;
          count += (rows - 1) * (figures[i].verticesPerRow - 1) * 6;
          break;
        case 'triangles':
          count += figures[i].coords.length;
          break;
      }
    }
    var coords = new Float32Array(count * 2);
    var colors = new Uint8Array(count * 3);
    var coordsMap = context.coords,
        colorsMap = context.colors;
    var pIndex = 0,
        cIndex = 0;
    for (i = 0, ii = figures.length; i < ii; i++) {
      var figure = figures[i],
          ps = figure.coords,
          cs = figure.colors;
      switch (figure.type) {
        case 'lattice':
          var cols = figure.verticesPerRow;
          rows = ps.length / cols | 0;
          for (var row = 1; row < rows; row++) {
            var offset = row * cols + 1;
            for (var col = 1; col < cols; col++, offset++) {
              coords[pIndex] = coordsMap[ps[offset - cols - 1]];
              coords[pIndex + 1] = coordsMap[ps[offset - cols - 1] + 1];
              coords[pIndex + 2] = coordsMap[ps[offset - cols]];
              coords[pIndex + 3] = coordsMap[ps[offset - cols] + 1];
              coords[pIndex + 4] = coordsMap[ps[offset - 1]];
              coords[pIndex + 5] = coordsMap[ps[offset - 1] + 1];
              colors[cIndex] = colorsMap[cs[offset - cols - 1]];
              colors[cIndex + 1] = colorsMap[cs[offset - cols - 1] + 1];
              colors[cIndex + 2] = colorsMap[cs[offset - cols - 1] + 2];
              colors[cIndex + 3] = colorsMap[cs[offset - cols]];
              colors[cIndex + 4] = colorsMap[cs[offset - cols] + 1];
              colors[cIndex + 5] = colorsMap[cs[offset - cols] + 2];
              colors[cIndex + 6] = colorsMap[cs[offset - 1]];
              colors[cIndex + 7] = colorsMap[cs[offset - 1] + 1];
              colors[cIndex + 8] = colorsMap[cs[offset - 1] + 2];
              coords[pIndex + 6] = coords[pIndex + 2];
              coords[pIndex + 7] = coords[pIndex + 3];
              coords[pIndex + 8] = coords[pIndex + 4];
              coords[pIndex + 9] = coords[pIndex + 5];
              coords[pIndex + 10] = coordsMap[ps[offset]];
              coords[pIndex + 11] = coordsMap[ps[offset] + 1];
              colors[cIndex + 9] = colors[cIndex + 3];
              colors[cIndex + 10] = colors[cIndex + 4];
              colors[cIndex + 11] = colors[cIndex + 5];
              colors[cIndex + 12] = colors[cIndex + 6];
              colors[cIndex + 13] = colors[cIndex + 7];
              colors[cIndex + 14] = colors[cIndex + 8];
              colors[cIndex + 15] = colorsMap[cs[offset]];
              colors[cIndex + 16] = colorsMap[cs[offset] + 1];
              colors[cIndex + 17] = colorsMap[cs[offset] + 2];
              pIndex += 12;
              cIndex += 18;
            }
          }
          break;
        case 'triangles':
          for (var j = 0, jj = ps.length; j < jj; j++) {
            coords[pIndex] = coordsMap[ps[j]];
            coords[pIndex + 1] = coordsMap[ps[j] + 1];
            colors[cIndex] = colorsMap[cs[j]];
            colors[cIndex + 1] = colorsMap[cs[j] + 1];
            colors[cIndex + 2] = colorsMap[cs[j] + 2];
            pIndex += 2;
            cIndex += 3;
          }
          break;
      }
    }
    if (backgroundColor) {
      gl.clearColor(backgroundColor[0] / 255, backgroundColor[1] / 255, backgroundColor[2] / 255, 1.0);
    } else {
      gl.clearColor(0, 0, 0, 0);
    }
    gl.clear(gl.COLOR_BUFFER_BIT);
    var coordsBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, coordsBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, coords, gl.STATIC_DRAW);
    gl.enableVertexAttribArray(cache.positionLocation);
    gl.vertexAttribPointer(cache.positionLocation, 2, gl.FLOAT, false, 0, 0);
    var colorsBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, colorsBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW);
    gl.enableVertexAttribArray(cache.colorLocation);
    gl.vertexAttribPointer(cache.colorLocation, 3, gl.UNSIGNED_BYTE, false, 0, 0);
    gl.uniform2f(cache.scaleLocation, context.scaleX, context.scaleY);
    gl.uniform2f(cache.offsetLocation, context.offsetX, context.offsetY);
    gl.drawArrays(gl.TRIANGLES, 0, count);
    gl.flush();
    gl.deleteBuffer(coordsBuffer);
    gl.deleteBuffer(colorsBuffer);
    return canvas;
  }
  return {
    tryInitGL() {
      try {
        generateGL();
        return !!currentGL;
      } catch (ex) {}
      return false;
    },
    composeSMask,
    drawFigures,
    cleanup() {
      if (smaskCache && smaskCache.canvas) {
        smaskCache.canvas.width = 0;
        smaskCache.canvas.height = 0;
      }
      if (figuresCache && figuresCache.canvas) {
        figuresCache.canvas.width = 0;
        figuresCache.canvas.height = 0;
      }
      smaskCache = null;
      figuresCache = null;
    }
  };
}();
exports.WebGLContext = WebGLContext;

/***/ }),
/* 19 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


module.exports = function isNodeJS() {
  return typeof process === 'object' && process + '' === '[object process]';
};

/***/ })
/******/ ]);
});PK
!<�W�KK(chrome/pdfjs/content/build/pdf.worker.js/**
 * @licstart The following is the entire license notice for the
 * Javascript code in this page
 *
 * Copyright 2017 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.
 *
 * @licend The above is the entire license notice for the
 * Javascript code in this page
 */

(function webpackUniversalModuleDefinition(root, factory) {
	if(typeof exports === 'object' && typeof module === 'object')
		module.exports = factory();
	else if(typeof define === 'function' && define.amd)
		define("pdfjs-dist/build/pdf.worker", [], factory);
	else if(typeof exports === 'object')
		exports["pdfjs-dist/build/pdf.worker"] = factory();
	else
		root["pdfjs-dist/build/pdf.worker"] = root.pdfjsDistBuildPdfWorker = factory();
})(typeof self !== 'undefined' ? self : this, function() {
return /******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};
/******/
/******/ 	// The require function
/******/ 	function __w_pdfjs_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, __w_pdfjs_require__);
/******/
/******/ 		// Flag the module as loaded
/******/ 		module.l = true;
/******/
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/
/******/
/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__w_pdfjs_require__.m = modules;
/******/
/******/ 	// expose the module cache
/******/ 	__w_pdfjs_require__.c = installedModules;
/******/
/******/ 	// define getter function for harmony exports
/******/ 	__w_pdfjs_require__.d = function(exports, name, getter) {
/******/ 		if(!__w_pdfjs_require__.o(exports, name)) {
/******/ 			Object.defineProperty(exports, name, {
/******/ 				configurable: false,
/******/ 				enumerable: true,
/******/ 				get: getter
/******/ 			});
/******/ 		}
/******/ 	};
/******/
/******/ 	// getDefaultExport function for compatibility with non-harmony modules
/******/ 	__w_pdfjs_require__.n = function(module) {
/******/ 		var getter = module && module.__esModule ?
/******/ 			function getDefault() { return module['default']; } :
/******/ 			function getModuleExports() { return module; };
/******/ 		__w_pdfjs_require__.d(getter, 'a', getter);
/******/ 		return getter;
/******/ 	};
/******/
/******/ 	// Object.prototype.hasOwnProperty.call
/******/ 	__w_pdfjs_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ 	// __webpack_public_path__
/******/ 	__w_pdfjs_require__.p = "";
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __w_pdfjs_require__(__w_pdfjs_require__.s = 19);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.unreachable = exports.warn = exports.utf8StringToString = exports.stringToUTF8String = exports.stringToPDFString = exports.stringToBytes = exports.string32 = exports.shadow = exports.setVerbosityLevel = exports.ReadableStream = exports.removeNullCharacters = exports.readUint32 = exports.readUint16 = exports.readInt8 = exports.log2 = exports.isEvalSupported = exports.isLittleEndian = exports.createValidAbsoluteUrl = exports.isSameOrigin = exports.isSpace = exports.isString = exports.isNum = exports.isEmptyObj = exports.isBool = exports.isArrayBuffer = exports.info = exports.getVerbosityLevel = exports.getLookupTableFactory = exports.deprecated = exports.createObjectURL = exports.createPromiseCapability = exports.createBlob = exports.bytesToString = exports.assert = exports.arraysToBytes = exports.arrayByteLength = exports.FormatError = exports.XRefParseException = exports.Util = exports.UnknownErrorException = exports.UnexpectedResponseException = exports.TextRenderingMode = exports.StreamType = exports.PasswordResponses = exports.PasswordException = exports.PageViewport = exports.NotImplementedException = exports.NativeImageDecoding = exports.MissingPDFException = exports.MissingDataException = exports.MessageHandler = exports.InvalidPDFException = exports.AbortException = exports.CMapCompressionType = exports.ImageKind = exports.FontType = exports.AnnotationType = exports.AnnotationFlag = exports.AnnotationFieldFlag = exports.AnnotationBorderStyleType = exports.UNSUPPORTED_FEATURES = exports.VerbosityLevel = exports.OPS = exports.IDENTITY_MATRIX = exports.FONT_IDENTITY_MATRIX = undefined;

__w_pdfjs_require__(21);

var _streams_polyfill = __w_pdfjs_require__(22);

var FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0];
const NativeImageDecoding = {
  NONE: 'none',
  DECODE: 'decode',
  DISPLAY: 'display'
};
var TextRenderingMode = {
  FILL: 0,
  STROKE: 1,
  FILL_STROKE: 2,
  INVISIBLE: 3,
  FILL_ADD_TO_PATH: 4,
  STROKE_ADD_TO_PATH: 5,
  FILL_STROKE_ADD_TO_PATH: 6,
  ADD_TO_PATH: 7,
  FILL_STROKE_MASK: 3,
  ADD_TO_PATH_FLAG: 4
};
var ImageKind = {
  GRAYSCALE_1BPP: 1,
  RGB_24BPP: 2,
  RGBA_32BPP: 3
};
var AnnotationType = {
  TEXT: 1,
  LINK: 2,
  FREETEXT: 3,
  LINE: 4,
  SQUARE: 5,
  CIRCLE: 6,
  POLYGON: 7,
  POLYLINE: 8,
  HIGHLIGHT: 9,
  UNDERLINE: 10,
  SQUIGGLY: 11,
  STRIKEOUT: 12,
  STAMP: 13,
  CARET: 14,
  INK: 15,
  POPUP: 16,
  FILEATTACHMENT: 17,
  SOUND: 18,
  MOVIE: 19,
  WIDGET: 20,
  SCREEN: 21,
  PRINTERMARK: 22,
  TRAPNET: 23,
  WATERMARK: 24,
  THREED: 25,
  REDACT: 26
};
var AnnotationFlag = {
  INVISIBLE: 0x01,
  HIDDEN: 0x02,
  PRINT: 0x04,
  NOZOOM: 0x08,
  NOROTATE: 0x10,
  NOVIEW: 0x20,
  READONLY: 0x40,
  LOCKED: 0x80,
  TOGGLENOVIEW: 0x100,
  LOCKEDCONTENTS: 0x200
};
var AnnotationFieldFlag = {
  READONLY: 0x0000001,
  REQUIRED: 0x0000002,
  NOEXPORT: 0x0000004,
  MULTILINE: 0x0001000,
  PASSWORD: 0x0002000,
  NOTOGGLETOOFF: 0x0004000,
  RADIO: 0x0008000,
  PUSHBUTTON: 0x0010000,
  COMBO: 0x0020000,
  EDIT: 0x0040000,
  SORT: 0x0080000,
  FILESELECT: 0x0100000,
  MULTISELECT: 0x0200000,
  DONOTSPELLCHECK: 0x0400000,
  DONOTSCROLL: 0x0800000,
  COMB: 0x1000000,
  RICHTEXT: 0x2000000,
  RADIOSINUNISON: 0x2000000,
  COMMITONSELCHANGE: 0x4000000
};
var AnnotationBorderStyleType = {
  SOLID: 1,
  DASHED: 2,
  BEVELED: 3,
  INSET: 4,
  UNDERLINE: 5
};
var StreamType = {
  UNKNOWN: 0,
  FLATE: 1,
  LZW: 2,
  DCT: 3,
  JPX: 4,
  JBIG: 5,
  A85: 6,
  AHX: 7,
  CCF: 8,
  RL: 9
};
var FontType = {
  UNKNOWN: 0,
  TYPE1: 1,
  TYPE1C: 2,
  CIDFONTTYPE0: 3,
  CIDFONTTYPE0C: 4,
  TRUETYPE: 5,
  CIDFONTTYPE2: 6,
  TYPE3: 7,
  OPENTYPE: 8,
  TYPE0: 9,
  MMTYPE1: 10
};
const VerbosityLevel = {
  ERRORS: 0,
  WARNINGS: 1,
  INFOS: 5
};
var CMapCompressionType = {
  NONE: 0,
  BINARY: 1,
  STREAM: 2
};
var OPS = {
  dependency: 1,
  setLineWidth: 2,
  setLineCap: 3,
  setLineJoin: 4,
  setMiterLimit: 5,
  setDash: 6,
  setRenderingIntent: 7,
  setFlatness: 8,
  setGState: 9,
  save: 10,
  restore: 11,
  transform: 12,
  moveTo: 13,
  lineTo: 14,
  curveTo: 15,
  curveTo2: 16,
  curveTo3: 17,
  closePath: 18,
  rectangle: 19,
  stroke: 20,
  closeStroke: 21,
  fill: 22,
  eoFill: 23,
  fillStroke: 24,
  eoFillStroke: 25,
  closeFillStroke: 26,
  closeEOFillStroke: 27,
  endPath: 28,
  clip: 29,
  eoClip: 30,
  beginText: 31,
  endText: 32,
  setCharSpacing: 33,
  setWordSpacing: 34,
  setHScale: 35,
  setLeading: 36,
  setFont: 37,
  setTextRenderingMode: 38,
  setTextRise: 39,
  moveText: 40,
  setLeadingMoveText: 41,
  setTextMatrix: 42,
  nextLine: 43,
  showText: 44,
  showSpacedText: 45,
  nextLineShowText: 46,
  nextLineSetSpacingShowText: 47,
  setCharWidth: 48,
  setCharWidthAndBounds: 49,
  setStrokeColorSpace: 50,
  setFillColorSpace: 51,
  setStrokeColor: 52,
  setStrokeColorN: 53,
  setFillColor: 54,
  setFillColorN: 55,
  setStrokeGray: 56,
  setFillGray: 57,
  setStrokeRGBColor: 58,
  setFillRGBColor: 59,
  setStrokeCMYKColor: 60,
  setFillCMYKColor: 61,
  shadingFill: 62,
  beginInlineImage: 63,
  beginImageData: 64,
  endInlineImage: 65,
  paintXObject: 66,
  markPoint: 67,
  markPointProps: 68,
  beginMarkedContent: 69,
  beginMarkedContentProps: 70,
  endMarkedContent: 71,
  beginCompat: 72,
  endCompat: 73,
  paintFormXObjectBegin: 74,
  paintFormXObjectEnd: 75,
  beginGroup: 76,
  endGroup: 77,
  beginAnnotations: 78,
  endAnnotations: 79,
  beginAnnotation: 80,
  endAnnotation: 81,
  paintJpegXObject: 82,
  paintImageMaskXObject: 83,
  paintImageMaskXObjectGroup: 84,
  paintImageXObject: 85,
  paintInlineImageXObject: 86,
  paintInlineImageXObjectGroup: 87,
  paintImageXObjectRepeat: 88,
  paintImageMaskXObjectRepeat: 89,
  paintSolidColorImageMask: 90,
  constructPath: 91
};
let verbosity = VerbosityLevel.WARNINGS;
function setVerbosityLevel(level) {
  if (Number.isInteger(level)) {
    verbosity = level;
  }
}
function getVerbosityLevel() {
  return verbosity;
}
function info(msg) {
  if (verbosity >= VerbosityLevel.INFOS) {
    console.log('Info: ' + msg);
  }
}
function warn(msg) {
  if (verbosity >= VerbosityLevel.WARNINGS) {
    console.log('Warning: ' + msg);
  }
}
function deprecated(details) {
  console.log('Deprecated API usage: ' + details);
}
function unreachable(msg) {
  throw new Error(msg);
}
function assert(cond, msg) {
  if (!cond) {
    unreachable(msg);
  }
}
var UNSUPPORTED_FEATURES = {
  unknown: 'unknown',
  forms: 'forms',
  javaScript: 'javaScript',
  smask: 'smask',
  shadingPattern: 'shadingPattern',
  font: 'font'
};
function isSameOrigin(baseUrl, otherUrl) {
  try {
    var base = new URL(baseUrl);
    if (!base.origin || base.origin === 'null') {
      return false;
    }
  } catch (e) {
    return false;
  }
  var other = new URL(otherUrl, base);
  return base.origin === other.origin;
}
function isValidProtocol(url) {
  if (!url) {
    return false;
  }
  switch (url.protocol) {
    case 'http:':
    case 'https:':
    case 'ftp:':
    case 'mailto:':
    case 'tel:':
      return true;
    default:
      return false;
  }
}
function createValidAbsoluteUrl(url, baseUrl) {
  if (!url) {
    return null;
  }
  try {
    var absoluteUrl = baseUrl ? new URL(url, baseUrl) : new URL(url);
    if (isValidProtocol(absoluteUrl)) {
      return absoluteUrl;
    }
  } catch (ex) {}
  return null;
}
function shadow(obj, prop, value) {
  Object.defineProperty(obj, prop, {
    value,
    enumerable: true,
    configurable: true,
    writable: false
  });
  return value;
}
function getLookupTableFactory(initializer) {
  var lookup;
  return function () {
    if (initializer) {
      lookup = Object.create(null);
      initializer(lookup);
      initializer = null;
    }
    return lookup;
  };
}
var PasswordResponses = {
  NEED_PASSWORD: 1,
  INCORRECT_PASSWORD: 2
};
var PasswordException = function PasswordExceptionClosure() {
  function PasswordException(msg, code) {
    this.name = 'PasswordException';
    this.message = msg;
    this.code = code;
  }
  PasswordException.prototype = new Error();
  PasswordException.constructor = PasswordException;
  return PasswordException;
}();
var UnknownErrorException = function UnknownErrorExceptionClosure() {
  function UnknownErrorException(msg, details) {
    this.name = 'UnknownErrorException';
    this.message = msg;
    this.details = details;
  }
  UnknownErrorException.prototype = new Error();
  UnknownErrorException.constructor = UnknownErrorException;
  return UnknownErrorException;
}();
var InvalidPDFException = function InvalidPDFExceptionClosure() {
  function InvalidPDFException(msg) {
    this.name = 'InvalidPDFException';
    this.message = msg;
  }
  InvalidPDFException.prototype = new Error();
  InvalidPDFException.constructor = InvalidPDFException;
  return InvalidPDFException;
}();
var MissingPDFException = function MissingPDFExceptionClosure() {
  function MissingPDFException(msg) {
    this.name = 'MissingPDFException';
    this.message = msg;
  }
  MissingPDFException.prototype = new Error();
  MissingPDFException.constructor = MissingPDFException;
  return MissingPDFException;
}();
var UnexpectedResponseException = function UnexpectedResponseExceptionClosure() {
  function UnexpectedResponseException(msg, status) {
    this.name = 'UnexpectedResponseException';
    this.message = msg;
    this.status = status;
  }
  UnexpectedResponseException.prototype = new Error();
  UnexpectedResponseException.constructor = UnexpectedResponseException;
  return UnexpectedResponseException;
}();
var NotImplementedException = function NotImplementedExceptionClosure() {
  function NotImplementedException(msg) {
    this.message = msg;
  }
  NotImplementedException.prototype = new Error();
  NotImplementedException.prototype.name = 'NotImplementedException';
  NotImplementedException.constructor = NotImplementedException;
  return NotImplementedException;
}();
var MissingDataException = function MissingDataExceptionClosure() {
  function MissingDataException(begin, end) {
    this.begin = begin;
    this.end = end;
    this.message = 'Missing data [' + begin + ', ' + end + ')';
  }
  MissingDataException.prototype = new Error();
  MissingDataException.prototype.name = 'MissingDataException';
  MissingDataException.constructor = MissingDataException;
  return MissingDataException;
}();
var XRefParseException = function XRefParseExceptionClosure() {
  function XRefParseException(msg) {
    this.message = msg;
  }
  XRefParseException.prototype = new Error();
  XRefParseException.prototype.name = 'XRefParseException';
  XRefParseException.constructor = XRefParseException;
  return XRefParseException;
}();
let FormatError = function FormatErrorClosure() {
  function FormatError(msg) {
    this.message = msg;
  }
  FormatError.prototype = new Error();
  FormatError.prototype.name = 'FormatError';
  FormatError.constructor = FormatError;
  return FormatError;
}();
let AbortException = function AbortExceptionClosure() {
  function AbortException(msg) {
    this.name = 'AbortException';
    this.message = msg;
  }
  AbortException.prototype = new Error();
  AbortException.constructor = AbortException;
  return AbortException;
}();
var NullCharactersRegExp = /\x00/g;
function removeNullCharacters(str) {
  if (typeof str !== 'string') {
    warn('The argument for removeNullCharacters must be a string.');
    return str;
  }
  return str.replace(NullCharactersRegExp, '');
}
function bytesToString(bytes) {
  assert(bytes !== null && typeof bytes === 'object' && bytes.length !== undefined, 'Invalid argument for bytesToString');
  var length = bytes.length;
  var MAX_ARGUMENT_COUNT = 8192;
  if (length < MAX_ARGUMENT_COUNT) {
    return String.fromCharCode.apply(null, bytes);
  }
  var strBuf = [];
  for (var i = 0; i < length; i += MAX_ARGUMENT_COUNT) {
    var chunkEnd = Math.min(i + MAX_ARGUMENT_COUNT, length);
    var chunk = bytes.subarray(i, chunkEnd);
    strBuf.push(String.fromCharCode.apply(null, chunk));
  }
  return strBuf.join('');
}
function stringToBytes(str) {
  assert(typeof str === 'string', 'Invalid argument for stringToBytes');
  var length = str.length;
  var bytes = new Uint8Array(length);
  for (var i = 0; i < length; ++i) {
    bytes[i] = str.charCodeAt(i) & 0xFF;
  }
  return bytes;
}
function arrayByteLength(arr) {
  if (arr.length !== undefined) {
    return arr.length;
  }
  assert(arr.byteLength !== undefined);
  return arr.byteLength;
}
function arraysToBytes(arr) {
  if (arr.length === 1 && arr[0] instanceof Uint8Array) {
    return arr[0];
  }
  var resultLength = 0;
  var i,
      ii = arr.length;
  var item, itemLength;
  for (i = 0; i < ii; i++) {
    item = arr[i];
    itemLength = arrayByteLength(item);
    resultLength += itemLength;
  }
  var pos = 0;
  var data = new Uint8Array(resultLength);
  for (i = 0; i < ii; i++) {
    item = arr[i];
    if (!(item instanceof Uint8Array)) {
      if (typeof item === 'string') {
        item = stringToBytes(item);
      } else {
        item = new Uint8Array(item);
      }
    }
    itemLength = item.byteLength;
    data.set(item, pos);
    pos += itemLength;
  }
  return data;
}
function string32(value) {
  return String.fromCharCode(value >> 24 & 0xff, value >> 16 & 0xff, value >> 8 & 0xff, value & 0xff);
}
function log2(x) {
  if (x <= 0) {
    return 0;
  }
  return Math.ceil(Math.log2(x));
}
function readInt8(data, start) {
  return data[start] << 24 >> 24;
}
function readUint16(data, offset) {
  return data[offset] << 8 | data[offset + 1];
}
function readUint32(data, offset) {
  return (data[offset] << 24 | data[offset + 1] << 16 | data[offset + 2] << 8 | data[offset + 3]) >>> 0;
}
function isLittleEndian() {
  var buffer8 = new Uint8Array(4);
  buffer8[0] = 1;
  var view32 = new Uint32Array(buffer8.buffer, 0, 1);
  return view32[0] === 1;
}
function isEvalSupported() {
  try {
    new Function('');
    return true;
  } catch (e) {
    return false;
  }
}
var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0];
var Util = function UtilClosure() {
  function Util() {}
  var rgbBuf = ['rgb(', 0, ',', 0, ',', 0, ')'];
  Util.makeCssRgb = function Util_makeCssRgb(r, g, b) {
    rgbBuf[1] = r;
    rgbBuf[3] = g;
    rgbBuf[5] = b;
    return rgbBuf.join('');
  };
  Util.transform = function Util_transform(m1, m2) {
    return [m1[0] * m2[0] + m1[2] * m2[1], m1[1] * m2[0] + m1[3] * m2[1], m1[0] * m2[2] + m1[2] * m2[3], m1[1] * m2[2] + m1[3] * m2[3], m1[0] * m2[4] + m1[2] * m2[5] + m1[4], m1[1] * m2[4] + m1[3] * m2[5] + m1[5]];
  };
  Util.applyTransform = function Util_applyTransform(p, m) {
    var xt = p[0] * m[0] + p[1] * m[2] + m[4];
    var yt = p[0] * m[1] + p[1] * m[3] + m[5];
    return [xt, yt];
  };
  Util.applyInverseTransform = function Util_applyInverseTransform(p, m) {
    var d = m[0] * m[3] - m[1] * m[2];
    var xt = (p[0] * m[3] - p[1] * m[2] + m[2] * m[5] - m[4] * m[3]) / d;
    var yt = (-p[0] * m[1] + p[1] * m[0] + m[4] * m[1] - m[5] * m[0]) / d;
    return [xt, yt];
  };
  Util.getAxialAlignedBoundingBox = function Util_getAxialAlignedBoundingBox(r, m) {
    var p1 = Util.applyTransform(r, m);
    var p2 = Util.applyTransform(r.slice(2, 4), m);
    var p3 = Util.applyTransform([r[0], r[3]], m);
    var p4 = Util.applyTransform([r[2], r[1]], m);
    return [Math.min(p1[0], p2[0], p3[0], p4[0]), Math.min(p1[1], p2[1], p3[1], p4[1]), Math.max(p1[0], p2[0], p3[0], p4[0]), Math.max(p1[1], p2[1], p3[1], p4[1])];
  };
  Util.inverseTransform = function Util_inverseTransform(m) {
    var d = m[0] * m[3] - m[1] * m[2];
    return [m[3] / d, -m[1] / d, -m[2] / d, m[0] / d, (m[2] * m[5] - m[4] * m[3]) / d, (m[4] * m[1] - m[5] * m[0]) / d];
  };
  Util.apply3dTransform = function Util_apply3dTransform(m, v) {
    return [m[0] * v[0] + m[1] * v[1] + m[2] * v[2], m[3] * v[0] + m[4] * v[1] + m[5] * v[2], m[6] * v[0] + m[7] * v[1] + m[8] * v[2]];
  };
  Util.singularValueDecompose2dScale = function Util_singularValueDecompose2dScale(m) {
    var transpose = [m[0], m[2], m[1], m[3]];
    var a = m[0] * transpose[0] + m[1] * transpose[2];
    var b = m[0] * transpose[1] + m[1] * transpose[3];
    var c = m[2] * transpose[0] + m[3] * transpose[2];
    var d = m[2] * transpose[1] + m[3] * transpose[3];
    var first = (a + d) / 2;
    var second = Math.sqrt((a + d) * (a + d) - 4 * (a * d - c * b)) / 2;
    var sx = first + second || 1;
    var sy = first - second || 1;
    return [Math.sqrt(sx), Math.sqrt(sy)];
  };
  Util.normalizeRect = function Util_normalizeRect(rect) {
    var r = rect.slice(0);
    if (rect[0] > rect[2]) {
      r[0] = rect[2];
      r[2] = rect[0];
    }
    if (rect[1] > rect[3]) {
      r[1] = rect[3];
      r[3] = rect[1];
    }
    return r;
  };
  Util.intersect = function Util_intersect(rect1, rect2) {
    function compare(a, b) {
      return a - b;
    }
    var orderedX = [rect1[0], rect1[2], rect2[0], rect2[2]].sort(compare),
        orderedY = [rect1[1], rect1[3], rect2[1], rect2[3]].sort(compare),
        result = [];
    rect1 = Util.normalizeRect(rect1);
    rect2 = Util.normalizeRect(rect2);
    if (orderedX[0] === rect1[0] && orderedX[1] === rect2[0] || orderedX[0] === rect2[0] && orderedX[1] === rect1[0]) {
      result[0] = orderedX[1];
      result[2] = orderedX[2];
    } else {
      return false;
    }
    if (orderedY[0] === rect1[1] && orderedY[1] === rect2[1] || orderedY[0] === rect2[1] && orderedY[1] === rect1[1]) {
      result[1] = orderedY[1];
      result[3] = orderedY[2];
    } else {
      return false;
    }
    return result;
  };
  var ROMAN_NUMBER_MAP = ['', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM', '', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC', '', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX'];
  Util.toRoman = function Util_toRoman(number, lowerCase) {
    assert(Number.isInteger(number) && number > 0, 'The number should be a positive integer.');
    var pos,
        romanBuf = [];
    while (number >= 1000) {
      number -= 1000;
      romanBuf.push('M');
    }
    pos = number / 100 | 0;
    number %= 100;
    romanBuf.push(ROMAN_NUMBER_MAP[pos]);
    pos = number / 10 | 0;
    number %= 10;
    romanBuf.push(ROMAN_NUMBER_MAP[10 + pos]);
    romanBuf.push(ROMAN_NUMBER_MAP[20 + number]);
    var romanStr = romanBuf.join('');
    return lowerCase ? romanStr.toLowerCase() : romanStr;
  };
  Util.appendToArray = function Util_appendToArray(arr1, arr2) {
    Array.prototype.push.apply(arr1, arr2);
  };
  Util.prependToArray = function Util_prependToArray(arr1, arr2) {
    Array.prototype.unshift.apply(arr1, arr2);
  };
  Util.extendObj = function extendObj(obj1, obj2) {
    for (var key in obj2) {
      obj1[key] = obj2[key];
    }
  };
  Util.getInheritableProperty = function Util_getInheritableProperty(dict, name, getArray) {
    while (dict && !dict.has(name)) {
      dict = dict.get('Parent');
    }
    if (!dict) {
      return null;
    }
    return getArray ? dict.getArray(name) : dict.get(name);
  };
  Util.inherit = function Util_inherit(sub, base, prototype) {
    sub.prototype = Object.create(base.prototype);
    sub.prototype.constructor = sub;
    for (var prop in prototype) {
      sub.prototype[prop] = prototype[prop];
    }
  };
  Util.loadScript = function Util_loadScript(src, callback) {
    var script = document.createElement('script');
    var loaded = false;
    script.setAttribute('src', src);
    if (callback) {
      script.onload = function () {
        if (!loaded) {
          callback();
        }
        loaded = true;
      };
    }
    document.getElementsByTagName('head')[0].appendChild(script);
  };
  return Util;
}();
var PageViewport = function PageViewportClosure() {
  function PageViewport(viewBox, scale, rotation, offsetX, offsetY, dontFlip) {
    this.viewBox = viewBox;
    this.scale = scale;
    this.rotation = rotation;
    this.offsetX = offsetX;
    this.offsetY = offsetY;
    var centerX = (viewBox[2] + viewBox[0]) / 2;
    var centerY = (viewBox[3] + viewBox[1]) / 2;
    var rotateA, rotateB, rotateC, rotateD;
    rotation = rotation % 360;
    rotation = rotation < 0 ? rotation + 360 : rotation;
    switch (rotation) {
      case 180:
        rotateA = -1;
        rotateB = 0;
        rotateC = 0;
        rotateD = 1;
        break;
      case 90:
        rotateA = 0;
        rotateB = 1;
        rotateC = 1;
        rotateD = 0;
        break;
      case 270:
        rotateA = 0;
        rotateB = -1;
        rotateC = -1;
        rotateD = 0;
        break;
      default:
        rotateA = 1;
        rotateB = 0;
        rotateC = 0;
        rotateD = -1;
        break;
    }
    if (dontFlip) {
      rotateC = -rotateC;
      rotateD = -rotateD;
    }
    var offsetCanvasX, offsetCanvasY;
    var width, height;
    if (rotateA === 0) {
      offsetCanvasX = Math.abs(centerY - viewBox[1]) * scale + offsetX;
      offsetCanvasY = Math.abs(centerX - viewBox[0]) * scale + offsetY;
      width = Math.abs(viewBox[3] - viewBox[1]) * scale;
      height = Math.abs(viewBox[2] - viewBox[0]) * scale;
    } else {
      offsetCanvasX = Math.abs(centerX - viewBox[0]) * scale + offsetX;
      offsetCanvasY = Math.abs(centerY - viewBox[1]) * scale + offsetY;
      width = Math.abs(viewBox[2] - viewBox[0]) * scale;
      height = Math.abs(viewBox[3] - viewBox[1]) * scale;
    }
    this.transform = [rotateA * scale, rotateB * scale, rotateC * scale, rotateD * scale, offsetCanvasX - rotateA * scale * centerX - rotateC * scale * centerY, offsetCanvasY - rotateB * scale * centerX - rotateD * scale * centerY];
    this.width = width;
    this.height = height;
    this.fontScale = scale;
  }
  PageViewport.prototype = {
    clone: function PageViewPort_clone(args) {
      args = args || {};
      var scale = 'scale' in args ? args.scale : this.scale;
      var rotation = 'rotation' in args ? args.rotation : this.rotation;
      return new PageViewport(this.viewBox.slice(), scale, rotation, this.offsetX, this.offsetY, args.dontFlip);
    },
    convertToViewportPoint: function PageViewport_convertToViewportPoint(x, y) {
      return Util.applyTransform([x, y], this.transform);
    },
    convertToViewportRectangle: function PageViewport_convertToViewportRectangle(rect) {
      var tl = Util.applyTransform([rect[0], rect[1]], this.transform);
      var br = Util.applyTransform([rect[2], rect[3]], this.transform);
      return [tl[0], tl[1], br[0], br[1]];
    },
    convertToPdfPoint: function PageViewport_convertToPdfPoint(x, y) {
      return Util.applyInverseTransform([x, y], this.transform);
    }
  };
  return PageViewport;
}();
var PDFStringTranslateTable = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2D8, 0x2C7, 0x2C6, 0x2D9, 0x2DD, 0x2DB, 0x2DA, 0x2DC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2022, 0x2020, 0x2021, 0x2026, 0x2014, 0x2013, 0x192, 0x2044, 0x2039, 0x203A, 0x2212, 0x2030, 0x201E, 0x201C, 0x201D, 0x2018, 0x2019, 0x201A, 0x2122, 0xFB01, 0xFB02, 0x141, 0x152, 0x160, 0x178, 0x17D, 0x131, 0x142, 0x153, 0x161, 0x17E, 0, 0x20AC];
function stringToPDFString(str) {
  var i,
      n = str.length,
      strBuf = [];
  if (str[0] === '\xFE' && str[1] === '\xFF') {
    for (i = 2; i < n; i += 2) {
      strBuf.push(String.fromCharCode(str.charCodeAt(i) << 8 | str.charCodeAt(i + 1)));
    }
  } else {
    for (i = 0; i < n; ++i) {
      var code = PDFStringTranslateTable[str.charCodeAt(i)];
      strBuf.push(code ? String.fromCharCode(code) : str.charAt(i));
    }
  }
  return strBuf.join('');
}
function stringToUTF8String(str) {
  return decodeURIComponent(escape(str));
}
function utf8StringToString(str) {
  return unescape(encodeURIComponent(str));
}
function isEmptyObj(obj) {
  for (var key in obj) {
    return false;
  }
  return true;
}
function isBool(v) {
  return typeof v === 'boolean';
}
function isNum(v) {
  return typeof v === 'number';
}
function isString(v) {
  return typeof v === 'string';
}
function isArrayBuffer(v) {
  return typeof v === 'object' && v !== null && v.byteLength !== undefined;
}
function isSpace(ch) {
  return ch === 0x20 || ch === 0x09 || ch === 0x0D || ch === 0x0A;
}
function createPromiseCapability() {
  var capability = {};
  capability.promise = new Promise(function (resolve, reject) {
    capability.resolve = resolve;
    capability.reject = reject;
  });
  return capability;
}
var createBlob = function createBlob(data, contentType) {
  if (typeof Blob !== 'undefined') {
    return new Blob([data], { type: contentType });
  }
  throw new Error('The "Blob" constructor is not supported.');
};
var createObjectURL = function createObjectURLClosure() {
  var digits = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
  return function createObjectURL(data, contentType, forceDataSchema = false) {
    if (!forceDataSchema && URL.createObjectURL) {
      var blob = createBlob(data, contentType);
      return URL.createObjectURL(blob);
    }
    var buffer = 'data:' + contentType + ';base64,';
    for (var i = 0, ii = data.length; i < ii; i += 3) {
      var b1 = data[i] & 0xFF;
      var b2 = data[i + 1] & 0xFF;
      var b3 = data[i + 2] & 0xFF;
      var d1 = b1 >> 2,
          d2 = (b1 & 3) << 4 | b2 >> 4;
      var d3 = i + 1 < ii ? (b2 & 0xF) << 2 | b3 >> 6 : 64;
      var d4 = i + 2 < ii ? b3 & 0x3F : 64;
      buffer += digits[d1] + digits[d2] + digits[d3] + digits[d4];
    }
    return buffer;
  };
}();
function resolveCall(fn, args, thisArg = null) {
  if (!fn) {
    return Promise.resolve(undefined);
  }
  return new Promise((resolve, reject) => {
    resolve(fn.apply(thisArg, args));
  });
}
function wrapReason(reason) {
  if (typeof reason !== 'object') {
    return reason;
  }
  switch (reason.name) {
    case 'AbortException':
      return new AbortException(reason.message);
    case 'MissingPDFException':
      return new MissingPDFException(reason.message);
    case 'UnexpectedResponseException':
      return new UnexpectedResponseException(reason.message, reason.status);
    default:
      return new UnknownErrorException(reason.message, reason.details);
  }
}
function makeReasonSerializable(reason) {
  if (!(reason instanceof Error) || reason instanceof AbortException || reason instanceof MissingPDFException || reason instanceof UnexpectedResponseException || reason instanceof UnknownErrorException) {
    return reason;
  }
  return new UnknownErrorException(reason.message, reason.toString());
}
function resolveOrReject(capability, success, reason) {
  if (success) {
    capability.resolve();
  } else {
    capability.reject(reason);
  }
}
function finalize(promise) {
  return Promise.resolve(promise).catch(() => {});
}
function MessageHandler(sourceName, targetName, comObj) {
  this.sourceName = sourceName;
  this.targetName = targetName;
  this.comObj = comObj;
  this.callbackId = 1;
  this.streamId = 1;
  this.postMessageTransfers = true;
  this.streamSinks = Object.create(null);
  this.streamControllers = Object.create(null);
  let callbacksCapabilities = this.callbacksCapabilities = Object.create(null);
  let ah = this.actionHandler = Object.create(null);
  this._onComObjOnMessage = event => {
    let data = event.data;
    if (data.targetName !== this.sourceName) {
      return;
    }
    if (data.stream) {
      this._processStreamMessage(data);
    } else if (data.isReply) {
      let callbackId = data.callbackId;
      if (data.callbackId in callbacksCapabilities) {
        let callback = callbacksCapabilities[callbackId];
        delete callbacksCapabilities[callbackId];
        if ('error' in data) {
          callback.reject(wrapReason(data.error));
        } else {
          callback.resolve(data.data);
        }
      } else {
        throw new Error(`Cannot resolve callback ${callbackId}`);
      }
    } else if (data.action in ah) {
      let action = ah[data.action];
      if (data.callbackId) {
        let sourceName = this.sourceName;
        let targetName = data.sourceName;
        Promise.resolve().then(function () {
          return action[0].call(action[1], data.data);
        }).then(result => {
          comObj.postMessage({
            sourceName,
            targetName,
            isReply: true,
            callbackId: data.callbackId,
            data: result
          });
        }, reason => {
          comObj.postMessage({
            sourceName,
            targetName,
            isReply: true,
            callbackId: data.callbackId,
            error: makeReasonSerializable(reason)
          });
        });
      } else if (data.streamId) {
        this._createStreamSink(data);
      } else {
        action[0].call(action[1], data.data);
      }
    } else {
      throw new Error(`Unknown action from worker: ${data.action}`);
    }
  };
  comObj.addEventListener('message', this._onComObjOnMessage);
}
MessageHandler.prototype = {
  on(actionName, handler, scope) {
    var ah = this.actionHandler;
    if (ah[actionName]) {
      throw new Error(`There is already an actionName called "${actionName}"`);
    }
    ah[actionName] = [handler, scope];
  },
  send(actionName, data, transfers) {
    var message = {
      sourceName: this.sourceName,
      targetName: this.targetName,
      action: actionName,
      data
    };
    this.postMessage(message, transfers);
  },
  sendWithPromise(actionName, data, transfers) {
    var callbackId = this.callbackId++;
    var message = {
      sourceName: this.sourceName,
      targetName: this.targetName,
      action: actionName,
      data,
      callbackId
    };
    var capability = createPromiseCapability();
    this.callbacksCapabilities[callbackId] = capability;
    try {
      this.postMessage(message, transfers);
    } catch (e) {
      capability.reject(e);
    }
    return capability.promise;
  },
  sendWithStream(actionName, data, queueingStrategy, transfers) {
    let streamId = this.streamId++;
    let sourceName = this.sourceName;
    let targetName = this.targetName;
    return new _streams_polyfill.ReadableStream({
      start: controller => {
        let startCapability = createPromiseCapability();
        this.streamControllers[streamId] = {
          controller,
          startCall: startCapability,
          isClosed: false
        };
        this.postMessage({
          sourceName,
          targetName,
          action: actionName,
          streamId,
          data,
          desiredSize: controller.desiredSize
        });
        return startCapability.promise;
      },
      pull: controller => {
        let pullCapability = createPromiseCapability();
        this.streamControllers[streamId].pullCall = pullCapability;
        this.postMessage({
          sourceName,
          targetName,
          stream: 'pull',
          streamId,
          desiredSize: controller.desiredSize
        });
        return pullCapability.promise;
      },
      cancel: reason => {
        let cancelCapability = createPromiseCapability();
        this.streamControllers[streamId].cancelCall = cancelCapability;
        this.streamControllers[streamId].isClosed = true;
        this.postMessage({
          sourceName,
          targetName,
          stream: 'cancel',
          reason,
          streamId
        });
        return cancelCapability.promise;
      }
    }, queueingStrategy);
  },
  _createStreamSink(data) {
    let self = this;
    let action = this.actionHandler[data.action];
    let streamId = data.streamId;
    let desiredSize = data.desiredSize;
    let sourceName = this.sourceName;
    let targetName = data.sourceName;
    let capability = createPromiseCapability();
    let sendStreamRequest = ({ stream, chunk, transfers, success, reason }) => {
      this.postMessage({
        sourceName,
        targetName,
        stream,
        streamId,
        chunk,
        success,
        reason
      }, transfers);
    };
    let streamSink = {
      enqueue(chunk, size = 1, transfers) {
        if (this.isCancelled) {
          return;
        }
        let lastDesiredSize = this.desiredSize;
        this.desiredSize -= size;
        if (lastDesiredSize > 0 && this.desiredSize <= 0) {
          this.sinkCapability = createPromiseCapability();
          this.ready = this.sinkCapability.promise;
        }
        sendStreamRequest({
          stream: 'enqueue',
          chunk,
          transfers
        });
      },
      close() {
        if (this.isCancelled) {
          return;
        }
        this.isCancelled = true;
        sendStreamRequest({ stream: 'close' });
        delete self.streamSinks[streamId];
      },
      error(reason) {
        if (this.isCancelled) {
          return;
        }
        this.isCancelled = true;
        sendStreamRequest({
          stream: 'error',
          reason
        });
      },
      sinkCapability: capability,
      onPull: null,
      onCancel: null,
      isCancelled: false,
      desiredSize,
      ready: null
    };
    streamSink.sinkCapability.resolve();
    streamSink.ready = streamSink.sinkCapability.promise;
    this.streamSinks[streamId] = streamSink;
    resolveCall(action[0], [data.data, streamSink], action[1]).then(() => {
      sendStreamRequest({
        stream: 'start_complete',
        success: true
      });
    }, reason => {
      sendStreamRequest({
        stream: 'start_complete',
        success: false,
        reason
      });
    });
  },
  _processStreamMessage(data) {
    let sourceName = this.sourceName;
    let targetName = data.sourceName;
    let streamId = data.streamId;
    let sendStreamResponse = ({ stream, success, reason }) => {
      this.comObj.postMessage({
        sourceName,
        targetName,
        stream,
        success,
        streamId,
        reason
      });
    };
    let deleteStreamController = () => {
      Promise.all([this.streamControllers[data.streamId].startCall, this.streamControllers[data.streamId].pullCall, this.streamControllers[data.streamId].cancelCall].map(function (capability) {
        return capability && finalize(capability.promise);
      })).then(() => {
        delete this.streamControllers[data.streamId];
      });
    };
    switch (data.stream) {
      case 'start_complete':
        resolveOrReject(this.streamControllers[data.streamId].startCall, data.success, wrapReason(data.reason));
        break;
      case 'pull_complete':
        resolveOrReject(this.streamControllers[data.streamId].pullCall, data.success, wrapReason(data.reason));
        break;
      case 'pull':
        if (!this.streamSinks[data.streamId]) {
          sendStreamResponse({
            stream: 'pull_complete',
            success: true
          });
          break;
        }
        if (this.streamSinks[data.streamId].desiredSize <= 0 && data.desiredSize > 0) {
          this.streamSinks[data.streamId].sinkCapability.resolve();
        }
        this.streamSinks[data.streamId].desiredSize = data.desiredSize;
        resolveCall(this.streamSinks[data.streamId].onPull).then(() => {
          sendStreamResponse({
            stream: 'pull_complete',
            success: true
          });
        }, reason => {
          sendStreamResponse({
            stream: 'pull_complete',
            success: false,
            reason
          });
        });
        break;
      case 'enqueue':
        assert(this.streamControllers[data.streamId], 'enqueue should have stream controller');
        if (!this.streamControllers[data.streamId].isClosed) {
          this.streamControllers[data.streamId].controller.enqueue(data.chunk);
        }
        break;
      case 'close':
        assert(this.streamControllers[data.streamId], 'close should have stream controller');
        if (this.streamControllers[data.streamId].isClosed) {
          break;
        }
        this.streamControllers[data.streamId].isClosed = true;
        this.streamControllers[data.streamId].controller.close();
        deleteStreamController();
        break;
      case 'error':
        assert(this.streamControllers[data.streamId], 'error should have stream controller');
        this.streamControllers[data.streamId].controller.error(wrapReason(data.reason));
        deleteStreamController();
        break;
      case 'cancel_complete':
        resolveOrReject(this.streamControllers[data.streamId].cancelCall, data.success, wrapReason(data.reason));
        deleteStreamController();
        break;
      case 'cancel':
        if (!this.streamSinks[data.streamId]) {
          break;
        }
        resolveCall(this.streamSinks[data.streamId].onCancel, [wrapReason(data.reason)]).then(() => {
          sendStreamResponse({
            stream: 'cancel_complete',
            success: true
          });
        }, reason => {
          sendStreamResponse({
            stream: 'cancel_complete',
            success: false,
            reason
          });
        });
        this.streamSinks[data.streamId].sinkCapability.reject(wrapReason(data.reason));
        this.streamSinks[data.streamId].isCancelled = true;
        delete this.streamSinks[data.streamId];
        break;
      default:
        throw new Error('Unexpected stream case');
    }
  },
  postMessage(message, transfers) {
    if (transfers && this.postMessageTransfers) {
      this.comObj.postMessage(message, transfers);
    } else {
      this.comObj.postMessage(message);
    }
  },
  destroy() {
    this.comObj.removeEventListener('message', this._onComObjOnMessage);
  }
};
exports.FONT_IDENTITY_MATRIX = FONT_IDENTITY_MATRIX;
exports.IDENTITY_MATRIX = IDENTITY_MATRIX;
exports.OPS = OPS;
exports.VerbosityLevel = VerbosityLevel;
exports.UNSUPPORTED_FEATURES = UNSUPPORTED_FEATURES;
exports.AnnotationBorderStyleType = AnnotationBorderStyleType;
exports.AnnotationFieldFlag = AnnotationFieldFlag;
exports.AnnotationFlag = AnnotationFlag;
exports.AnnotationType = AnnotationType;
exports.FontType = FontType;
exports.ImageKind = ImageKind;
exports.CMapCompressionType = CMapCompressionType;
exports.AbortException = AbortException;
exports.InvalidPDFException = InvalidPDFException;
exports.MessageHandler = MessageHandler;
exports.MissingDataException = MissingDataException;
exports.MissingPDFException = MissingPDFException;
exports.NativeImageDecoding = NativeImageDecoding;
exports.NotImplementedException = NotImplementedException;
exports.PageViewport = PageViewport;
exports.PasswordException = PasswordException;
exports.PasswordResponses = PasswordResponses;
exports.StreamType = StreamType;
exports.TextRenderingMode = TextRenderingMode;
exports.UnexpectedResponseException = UnexpectedResponseException;
exports.UnknownErrorException = UnknownErrorException;
exports.Util = Util;
exports.XRefParseException = XRefParseException;
exports.FormatError = FormatError;
exports.arrayByteLength = arrayByteLength;
exports.arraysToBytes = arraysToBytes;
exports.assert = assert;
exports.bytesToString = bytesToString;
exports.createBlob = createBlob;
exports.createPromiseCapability = createPromiseCapability;
exports.createObjectURL = createObjectURL;
exports.deprecated = deprecated;
exports.getLookupTableFactory = getLookupTableFactory;
exports.getVerbosityLevel = getVerbosityLevel;
exports.info = info;
exports.isArrayBuffer = isArrayBuffer;
exports.isBool = isBool;
exports.isEmptyObj = isEmptyObj;
exports.isNum = isNum;
exports.isString = isString;
exports.isSpace = isSpace;
exports.isSameOrigin = isSameOrigin;
exports.createValidAbsoluteUrl = createValidAbsoluteUrl;
exports.isLittleEndian = isLittleEndian;
exports.isEvalSupported = isEvalSupported;
exports.log2 = log2;
exports.readInt8 = readInt8;
exports.readUint16 = readUint16;
exports.readUint32 = readUint32;
exports.removeNullCharacters = removeNullCharacters;
exports.ReadableStream = _streams_polyfill.ReadableStream;
exports.setVerbosityLevel = setVerbosityLevel;
exports.shadow = shadow;
exports.string32 = string32;
exports.stringToBytes = stringToBytes;
exports.stringToPDFString = stringToPDFString;
exports.stringToUTF8String = stringToUTF8String;
exports.utf8StringToString = utf8StringToString;
exports.warn = warn;
exports.unreachable = unreachable;

/***/ }),
/* 1 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
var EOF = {};
var Name = function NameClosure() {
  function Name(name) {
    this.name = name;
  }
  Name.prototype = {};
  var nameCache = Object.create(null);
  Name.get = function Name_get(name) {
    var nameValue = nameCache[name];
    return nameValue ? nameValue : nameCache[name] = new Name(name);
  };
  return Name;
}();
var Cmd = function CmdClosure() {
  function Cmd(cmd) {
    this.cmd = cmd;
  }
  Cmd.prototype = {};
  var cmdCache = Object.create(null);
  Cmd.get = function Cmd_get(cmd) {
    var cmdValue = cmdCache[cmd];
    return cmdValue ? cmdValue : cmdCache[cmd] = new Cmd(cmd);
  };
  return Cmd;
}();
var Dict = function DictClosure() {
  var nonSerializable = function nonSerializableClosure() {
    return nonSerializable;
  };
  function Dict(xref) {
    this._map = Object.create(null);
    this.xref = xref;
    this.objId = null;
    this.suppressEncryption = false;
    this.__nonSerializable__ = nonSerializable;
  }
  Dict.prototype = {
    assignXref: function Dict_assignXref(newXref) {
      this.xref = newXref;
    },
    get: function Dict_get(key1, key2, key3) {
      var value;
      var xref = this.xref,
          suppressEncryption = this.suppressEncryption;
      if (typeof (value = this._map[key1]) !== 'undefined' || key1 in this._map || typeof key2 === 'undefined') {
        return xref ? xref.fetchIfRef(value, suppressEncryption) : value;
      }
      if (typeof (value = this._map[key2]) !== 'undefined' || key2 in this._map || typeof key3 === 'undefined') {
        return xref ? xref.fetchIfRef(value, suppressEncryption) : value;
      }
      value = this._map[key3] || null;
      return xref ? xref.fetchIfRef(value, suppressEncryption) : value;
    },
    getAsync: function Dict_getAsync(key1, key2, key3) {
      var value;
      var xref = this.xref,
          suppressEncryption = this.suppressEncryption;
      if (typeof (value = this._map[key1]) !== 'undefined' || key1 in this._map || typeof key2 === 'undefined') {
        if (xref) {
          return xref.fetchIfRefAsync(value, suppressEncryption);
        }
        return Promise.resolve(value);
      }
      if (typeof (value = this._map[key2]) !== 'undefined' || key2 in this._map || typeof key3 === 'undefined') {
        if (xref) {
          return xref.fetchIfRefAsync(value, suppressEncryption);
        }
        return Promise.resolve(value);
      }
      value = this._map[key3] || null;
      if (xref) {
        return xref.fetchIfRefAsync(value, suppressEncryption);
      }
      return Promise.resolve(value);
    },
    getArray: function Dict_getArray(key1, key2, key3) {
      var value = this.get(key1, key2, key3);
      var xref = this.xref,
          suppressEncryption = this.suppressEncryption;
      if (!Array.isArray(value) || !xref) {
        return value;
      }
      value = value.slice();
      for (var i = 0, ii = value.length; i < ii; i++) {
        if (!isRef(value[i])) {
          continue;
        }
        value[i] = xref.fetch(value[i], suppressEncryption);
      }
      return value;
    },
    getRaw: function Dict_getRaw(key) {
      return this._map[key];
    },
    getKeys: function Dict_getKeys() {
      return Object.keys(this._map);
    },
    set: function Dict_set(key, value) {
      this._map[key] = value;
    },
    has: function Dict_has(key) {
      return key in this._map;
    },
    forEach: function Dict_forEach(callback) {
      for (var key in this._map) {
        callback(key, this.get(key));
      }
    }
  };
  Dict.empty = new Dict(null);
  Dict.merge = function (xref, dictArray) {
    let mergedDict = new Dict(xref);
    for (let i = 0, ii = dictArray.length; i < ii; i++) {
      let dict = dictArray[i];
      if (!isDict(dict)) {
        continue;
      }
      for (let keyName in dict._map) {
        if (mergedDict._map[keyName] !== undefined) {
          continue;
        }
        mergedDict._map[keyName] = dict._map[keyName];
      }
    }
    return mergedDict;
  };
  return Dict;
}();
var Ref = function RefClosure() {
  function Ref(num, gen) {
    this.num = num;
    this.gen = gen;
  }
  Ref.prototype = {
    toString: function Ref_toString() {
      var str = this.num + 'R';
      if (this.gen !== 0) {
        str += this.gen;
      }
      return str;
    }
  };
  return Ref;
}();
var RefSet = function RefSetClosure() {
  function RefSet() {
    this.dict = Object.create(null);
  }
  RefSet.prototype = {
    has: function RefSet_has(ref) {
      return ref.toString() in this.dict;
    },
    put: function RefSet_put(ref) {
      this.dict[ref.toString()] = true;
    },
    remove: function RefSet_remove(ref) {
      delete this.dict[ref.toString()];
    }
  };
  return RefSet;
}();
var RefSetCache = function RefSetCacheClosure() {
  function RefSetCache() {
    this.dict = Object.create(null);
  }
  RefSetCache.prototype = {
    get: function RefSetCache_get(ref) {
      return this.dict[ref.toString()];
    },
    has: function RefSetCache_has(ref) {
      return ref.toString() in this.dict;
    },
    put: function RefSetCache_put(ref, obj) {
      this.dict[ref.toString()] = obj;
    },
    putAlias: function RefSetCache_putAlias(ref, aliasRef) {
      this.dict[ref.toString()] = this.get(aliasRef);
    },
    forEach: function RefSetCache_forEach(fn, thisArg) {
      for (var i in this.dict) {
        fn.call(thisArg, this.dict[i]);
      }
    },
    clear: function RefSetCache_clear() {
      this.dict = Object.create(null);
    }
  };
  return RefSetCache;
}();
function isEOF(v) {
  return v === EOF;
}
function isName(v, name) {
  return v instanceof Name && (name === undefined || v.name === name);
}
function isCmd(v, cmd) {
  return v instanceof Cmd && (cmd === undefined || v.cmd === cmd);
}
function isDict(v, type) {
  return v instanceof Dict && (type === undefined || isName(v.get('Type'), type));
}
function isRef(v) {
  return v instanceof Ref;
}
function isRefsEqual(v1, v2) {
  return v1.num === v2.num && v1.gen === v2.gen;
}
function isStream(v) {
  return typeof v === 'object' && v !== null && v.getBytes !== undefined;
}
exports.EOF = EOF;
exports.Cmd = Cmd;
exports.Dict = Dict;
exports.Name = Name;
exports.Ref = Ref;
exports.RefSet = RefSet;
exports.RefSetCache = RefSetCache;
exports.isEOF = isEOF;
exports.isCmd = isCmd;
exports.isDict = isDict;
exports.isName = isName;
exports.isRef = isRef;
exports.isRefsEqual = isRefsEqual;
exports.isStream = isStream;

/***/ }),
/* 2 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.LZWStream = exports.StringStream = exports.StreamsSequenceStream = exports.Stream = exports.RunLengthStream = exports.PredictorStream = exports.NullStream = exports.FlateStream = exports.DecodeStream = exports.DecryptStream = exports.AsciiHexStream = exports.Ascii85Stream = undefined;

var _util = __w_pdfjs_require__(0);

var _primitives = __w_pdfjs_require__(1);

var Stream = function StreamClosure() {
  function Stream(arrayBuffer, start, length, dict) {
    this.bytes = arrayBuffer instanceof Uint8Array ? arrayBuffer : new Uint8Array(arrayBuffer);
    this.start = start || 0;
    this.pos = this.start;
    this.end = start + length || this.bytes.length;
    this.dict = dict;
  }
  Stream.prototype = {
    get length() {
      return this.end - this.start;
    },
    get isEmpty() {
      return this.length === 0;
    },
    getByte: function Stream_getByte() {
      if (this.pos >= this.end) {
        return -1;
      }
      return this.bytes[this.pos++];
    },
    getUint16: function Stream_getUint16() {
      var b0 = this.getByte();
      var b1 = this.getByte();
      if (b0 === -1 || b1 === -1) {
        return -1;
      }
      return (b0 << 8) + b1;
    },
    getInt32: function Stream_getInt32() {
      var b0 = this.getByte();
      var b1 = this.getByte();
      var b2 = this.getByte();
      var b3 = this.getByte();
      return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3;
    },
    getBytes: function Stream_getBytes(length) {
      var bytes = this.bytes;
      var pos = this.pos;
      var strEnd = this.end;
      if (!length) {
        return bytes.subarray(pos, strEnd);
      }
      var end = pos + length;
      if (end > strEnd) {
        end = strEnd;
      }
      this.pos = end;
      return bytes.subarray(pos, end);
    },
    peekByte: function Stream_peekByte() {
      var peekedByte = this.getByte();
      this.pos--;
      return peekedByte;
    },
    peekBytes: function Stream_peekBytes(length) {
      var bytes = this.getBytes(length);
      this.pos -= bytes.length;
      return bytes;
    },
    skip: function Stream_skip(n) {
      if (!n) {
        n = 1;
      }
      this.pos += n;
    },
    reset: function Stream_reset() {
      this.pos = this.start;
    },
    moveStart: function Stream_moveStart() {
      this.start = this.pos;
    },
    makeSubStream: function Stream_makeSubStream(start, length, dict) {
      return new Stream(this.bytes.buffer, start, length, dict);
    }
  };
  return Stream;
}();
var StringStream = function StringStreamClosure() {
  function StringStream(str) {
    let bytes = (0, _util.stringToBytes)(str);
    Stream.call(this, bytes);
  }
  StringStream.prototype = Stream.prototype;
  return StringStream;
}();
var DecodeStream = function DecodeStreamClosure() {
  var emptyBuffer = new Uint8Array(0);
  function DecodeStream(maybeMinBufferLength) {
    this.pos = 0;
    this.bufferLength = 0;
    this.eof = false;
    this.buffer = emptyBuffer;
    this.minBufferLength = 512;
    if (maybeMinBufferLength) {
      while (this.minBufferLength < maybeMinBufferLength) {
        this.minBufferLength *= 2;
      }
    }
  }
  DecodeStream.prototype = {
    get isEmpty() {
      while (!this.eof && this.bufferLength === 0) {
        this.readBlock();
      }
      return this.bufferLength === 0;
    },
    ensureBuffer: function DecodeStream_ensureBuffer(requested) {
      var buffer = this.buffer;
      if (requested <= buffer.byteLength) {
        return buffer;
      }
      var size = this.minBufferLength;
      while (size < requested) {
        size *= 2;
      }
      var buffer2 = new Uint8Array(size);
      buffer2.set(buffer);
      return this.buffer = buffer2;
    },
    getByte: function DecodeStream_getByte() {
      var pos = this.pos;
      while (this.bufferLength <= pos) {
        if (this.eof) {
          return -1;
        }
        this.readBlock();
      }
      return this.buffer[this.pos++];
    },
    getUint16: function DecodeStream_getUint16() {
      var b0 = this.getByte();
      var b1 = this.getByte();
      if (b0 === -1 || b1 === -1) {
        return -1;
      }
      return (b0 << 8) + b1;
    },
    getInt32: function DecodeStream_getInt32() {
      var b0 = this.getByte();
      var b1 = this.getByte();
      var b2 = this.getByte();
      var b3 = this.getByte();
      return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3;
    },
    getBytes: function DecodeStream_getBytes(length) {
      var end,
          pos = this.pos;
      if (length) {
        this.ensureBuffer(pos + length);
        end = pos + length;
        while (!this.eof && this.bufferLength < end) {
          this.readBlock();
        }
        var bufEnd = this.bufferLength;
        if (end > bufEnd) {
          end = bufEnd;
        }
      } else {
        while (!this.eof) {
          this.readBlock();
        }
        end = this.bufferLength;
      }
      this.pos = end;
      return this.buffer.subarray(pos, end);
    },
    peekByte: function DecodeStream_peekByte() {
      var peekedByte = this.getByte();
      this.pos--;
      return peekedByte;
    },
    peekBytes: function DecodeStream_peekBytes(length) {
      var bytes = this.getBytes(length);
      this.pos -= bytes.length;
      return bytes;
    },
    makeSubStream: function DecodeStream_makeSubStream(start, length, dict) {
      var end = start + length;
      while (this.bufferLength <= end && !this.eof) {
        this.readBlock();
      }
      return new Stream(this.buffer, start, length, dict);
    },
    skip: function DecodeStream_skip(n) {
      if (!n) {
        n = 1;
      }
      this.pos += n;
    },
    reset: function DecodeStream_reset() {
      this.pos = 0;
    },
    getBaseStreams: function DecodeStream_getBaseStreams() {
      if (this.str && this.str.getBaseStreams) {
        return this.str.getBaseStreams();
      }
      return [];
    }
  };
  return DecodeStream;
}();
var StreamsSequenceStream = function StreamsSequenceStreamClosure() {
  function StreamsSequenceStream(streams) {
    this.streams = streams;
    DecodeStream.call(this, null);
  }
  StreamsSequenceStream.prototype = Object.create(DecodeStream.prototype);
  StreamsSequenceStream.prototype.readBlock = function streamSequenceStreamReadBlock() {
    var streams = this.streams;
    if (streams.length === 0) {
      this.eof = true;
      return;
    }
    var stream = streams.shift();
    var chunk = stream.getBytes();
    var bufferLength = this.bufferLength;
    var newLength = bufferLength + chunk.length;
    var buffer = this.ensureBuffer(newLength);
    buffer.set(chunk, bufferLength);
    this.bufferLength = newLength;
  };
  StreamsSequenceStream.prototype.getBaseStreams = function StreamsSequenceStream_getBaseStreams() {
    var baseStreams = [];
    for (var i = 0, ii = this.streams.length; i < ii; i++) {
      var stream = this.streams[i];
      if (stream.getBaseStreams) {
        _util.Util.appendToArray(baseStreams, stream.getBaseStreams());
      }
    }
    return baseStreams;
  };
  return StreamsSequenceStream;
}();
var FlateStream = function FlateStreamClosure() {
  var codeLenCodeMap = new Int32Array([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);
  var lengthDecode = new Int32Array([0x00003, 0x00004, 0x00005, 0x00006, 0x00007, 0x00008, 0x00009, 0x0000a, 0x1000b, 0x1000d, 0x1000f, 0x10011, 0x20013, 0x20017, 0x2001b, 0x2001f, 0x30023, 0x3002b, 0x30033, 0x3003b, 0x40043, 0x40053, 0x40063, 0x40073, 0x50083, 0x500a3, 0x500c3, 0x500e3, 0x00102, 0x00102, 0x00102]);
  var distDecode = new Int32Array([0x00001, 0x00002, 0x00003, 0x00004, 0x10005, 0x10007, 0x20009, 0x2000d, 0x30011, 0x30019, 0x40021, 0x40031, 0x50041, 0x50061, 0x60081, 0x600c1, 0x70101, 0x70181, 0x80201, 0x80301, 0x90401, 0x90601, 0xa0801, 0xa0c01, 0xb1001, 0xb1801, 0xc2001, 0xc3001, 0xd4001, 0xd6001]);
  var fixedLitCodeTab = [new Int32Array([0x70100, 0x80050, 0x80010, 0x80118, 0x70110, 0x80070, 0x80030, 0x900c0, 0x70108, 0x80060, 0x80020, 0x900a0, 0x80000, 0x80080, 0x80040, 0x900e0, 0x70104, 0x80058, 0x80018, 0x90090, 0x70114, 0x80078, 0x80038, 0x900d0, 0x7010c, 0x80068, 0x80028, 0x900b0, 0x80008, 0x80088, 0x80048, 0x900f0, 0x70102, 0x80054, 0x80014, 0x8011c, 0x70112, 0x80074, 0x80034, 0x900c8, 0x7010a, 0x80064, 0x80024, 0x900a8, 0x80004, 0x80084, 0x80044, 0x900e8, 0x70106, 0x8005c, 0x8001c, 0x90098, 0x70116, 0x8007c, 0x8003c, 0x900d8, 0x7010e, 0x8006c, 0x8002c, 0x900b8, 0x8000c, 0x8008c, 0x8004c, 0x900f8, 0x70101, 0x80052, 0x80012, 0x8011a, 0x70111, 0x80072, 0x80032, 0x900c4, 0x70109, 0x80062, 0x80022, 0x900a4, 0x80002, 0x80082, 0x80042, 0x900e4, 0x70105, 0x8005a, 0x8001a, 0x90094, 0x70115, 0x8007a, 0x8003a, 0x900d4, 0x7010d, 0x8006a, 0x8002a, 0x900b4, 0x8000a, 0x8008a, 0x8004a, 0x900f4, 0x70103, 0x80056, 0x80016, 0x8011e, 0x70113, 0x80076, 0x80036, 0x900cc, 0x7010b, 0x80066, 0x80026, 0x900ac, 0x80006, 0x80086, 0x80046, 0x900ec, 0x70107, 0x8005e, 0x8001e, 0x9009c, 0x70117, 0x8007e, 0x8003e, 0x900dc, 0x7010f, 0x8006e, 0x8002e, 0x900bc, 0x8000e, 0x8008e, 0x8004e, 0x900fc, 0x70100, 0x80051, 0x80011, 0x80119, 0x70110, 0x80071, 0x80031, 0x900c2, 0x70108, 0x80061, 0x80021, 0x900a2, 0x80001, 0x80081, 0x80041, 0x900e2, 0x70104, 0x80059, 0x80019, 0x90092, 0x70114, 0x80079, 0x80039, 0x900d2, 0x7010c, 0x80069, 0x80029, 0x900b2, 0x80009, 0x80089, 0x80049, 0x900f2, 0x70102, 0x80055, 0x80015, 0x8011d, 0x70112, 0x80075, 0x80035, 0x900ca, 0x7010a, 0x80065, 0x80025, 0x900aa, 0x80005, 0x80085, 0x80045, 0x900ea, 0x70106, 0x8005d, 0x8001d, 0x9009a, 0x70116, 0x8007d, 0x8003d, 0x900da, 0x7010e, 0x8006d, 0x8002d, 0x900ba, 0x8000d, 0x8008d, 0x8004d, 0x900fa, 0x70101, 0x80053, 0x80013, 0x8011b, 0x70111, 0x80073, 0x80033, 0x900c6, 0x70109, 0x80063, 0x80023, 0x900a6, 0x80003, 0x80083, 0x80043, 0x900e6, 0x70105, 0x8005b, 0x8001b, 0x90096, 0x70115, 0x8007b, 0x8003b, 0x900d6, 0x7010d, 0x8006b, 0x8002b, 0x900b6, 0x8000b, 0x8008b, 0x8004b, 0x900f6, 0x70103, 0x80057, 0x80017, 0x8011f, 0x70113, 0x80077, 0x80037, 0x900ce, 0x7010b, 0x80067, 0x80027, 0x900ae, 0x80007, 0x80087, 0x80047, 0x900ee, 0x70107, 0x8005f, 0x8001f, 0x9009e, 0x70117, 0x8007f, 0x8003f, 0x900de, 0x7010f, 0x8006f, 0x8002f, 0x900be, 0x8000f, 0x8008f, 0x8004f, 0x900fe, 0x70100, 0x80050, 0x80010, 0x80118, 0x70110, 0x80070, 0x80030, 0x900c1, 0x70108, 0x80060, 0x80020, 0x900a1, 0x80000, 0x80080, 0x80040, 0x900e1, 0x70104, 0x80058, 0x80018, 0x90091, 0x70114, 0x80078, 0x80038, 0x900d1, 0x7010c, 0x80068, 0x80028, 0x900b1, 0x80008, 0x80088, 0x80048, 0x900f1, 0x70102, 0x80054, 0x80014, 0x8011c, 0x70112, 0x80074, 0x80034, 0x900c9, 0x7010a, 0x80064, 0x80024, 0x900a9, 0x80004, 0x80084, 0x80044, 0x900e9, 0x70106, 0x8005c, 0x8001c, 0x90099, 0x70116, 0x8007c, 0x8003c, 0x900d9, 0x7010e, 0x8006c, 0x8002c, 0x900b9, 0x8000c, 0x8008c, 0x8004c, 0x900f9, 0x70101, 0x80052, 0x80012, 0x8011a, 0x70111, 0x80072, 0x80032, 0x900c5, 0x70109, 0x80062, 0x80022, 0x900a5, 0x80002, 0x80082, 0x80042, 0x900e5, 0x70105, 0x8005a, 0x8001a, 0x90095, 0x70115, 0x8007a, 0x8003a, 0x900d5, 0x7010d, 0x8006a, 0x8002a, 0x900b5, 0x8000a, 0x8008a, 0x8004a, 0x900f5, 0x70103, 0x80056, 0x80016, 0x8011e, 0x70113, 0x80076, 0x80036, 0x900cd, 0x7010b, 0x80066, 0x80026, 0x900ad, 0x80006, 0x80086, 0x80046, 0x900ed, 0x70107, 0x8005e, 0x8001e, 0x9009d, 0x70117, 0x8007e, 0x8003e, 0x900dd, 0x7010f, 0x8006e, 0x8002e, 0x900bd, 0x8000e, 0x8008e, 0x8004e, 0x900fd, 0x70100, 0x80051, 0x80011, 0x80119, 0x70110, 0x80071, 0x80031, 0x900c3, 0x70108, 0x80061, 0x80021, 0x900a3, 0x80001, 0x80081, 0x80041, 0x900e3, 0x70104, 0x80059, 0x80019, 0x90093, 0x70114, 0x80079, 0x80039, 0x900d3, 0x7010c, 0x80069, 0x80029, 0x900b3, 0x80009, 0x80089, 0x80049, 0x900f3, 0x70102, 0x80055, 0x80015, 0x8011d, 0x70112, 0x80075, 0x80035, 0x900cb, 0x7010a, 0x80065, 0x80025, 0x900ab, 0x80005, 0x80085, 0x80045, 0x900eb, 0x70106, 0x8005d, 0x8001d, 0x9009b, 0x70116, 0x8007d, 0x8003d, 0x900db, 0x7010e, 0x8006d, 0x8002d, 0x900bb, 0x8000d, 0x8008d, 0x8004d, 0x900fb, 0x70101, 0x80053, 0x80013, 0x8011b, 0x70111, 0x80073, 0x80033, 0x900c7, 0x70109, 0x80063, 0x80023, 0x900a7, 0x80003, 0x80083, 0x80043, 0x900e7, 0x70105, 0x8005b, 0x8001b, 0x90097, 0x70115, 0x8007b, 0x8003b, 0x900d7, 0x7010d, 0x8006b, 0x8002b, 0x900b7, 0x8000b, 0x8008b, 0x8004b, 0x900f7, 0x70103, 0x80057, 0x80017, 0x8011f, 0x70113, 0x80077, 0x80037, 0x900cf, 0x7010b, 0x80067, 0x80027, 0x900af, 0x80007, 0x80087, 0x80047, 0x900ef, 0x70107, 0x8005f, 0x8001f, 0x9009f, 0x70117, 0x8007f, 0x8003f, 0x900df, 0x7010f, 0x8006f, 0x8002f, 0x900bf, 0x8000f, 0x8008f, 0x8004f, 0x900ff]), 9];
  var fixedDistCodeTab = [new Int32Array([0x50000, 0x50010, 0x50008, 0x50018, 0x50004, 0x50014, 0x5000c, 0x5001c, 0x50002, 0x50012, 0x5000a, 0x5001a, 0x50006, 0x50016, 0x5000e, 0x00000, 0x50001, 0x50011, 0x50009, 0x50019, 0x50005, 0x50015, 0x5000d, 0x5001d, 0x50003, 0x50013, 0x5000b, 0x5001b, 0x50007, 0x50017, 0x5000f, 0x00000]), 5];
  function FlateStream(str, maybeLength) {
    this.str = str;
    this.dict = str.dict;
    var cmf = str.getByte();
    var flg = str.getByte();
    if (cmf === -1 || flg === -1) {
      throw new _util.FormatError(`Invalid header in flate stream: ${cmf}, ${flg}`);
    }
    if ((cmf & 0x0f) !== 0x08) {
      throw new _util.FormatError(`Unknown compression method in flate stream: ${cmf}, ${flg}`);
    }
    if (((cmf << 8) + flg) % 31 !== 0) {
      throw new _util.FormatError(`Bad FCHECK in flate stream: ${cmf}, ${flg}`);
    }
    if (flg & 0x20) {
      throw new _util.FormatError(`FDICT bit set in flate stream: ${cmf}, ${flg}`);
    }
    this.codeSize = 0;
    this.codeBuf = 0;
    DecodeStream.call(this, maybeLength);
  }
  FlateStream.prototype = Object.create(DecodeStream.prototype);
  FlateStream.prototype.getBits = function FlateStream_getBits(bits) {
    var str = this.str;
    var codeSize = this.codeSize;
    var codeBuf = this.codeBuf;
    var b;
    while (codeSize < bits) {
      if ((b = str.getByte()) === -1) {
        throw new _util.FormatError('Bad encoding in flate stream');
      }
      codeBuf |= b << codeSize;
      codeSize += 8;
    }
    b = codeBuf & (1 << bits) - 1;
    this.codeBuf = codeBuf >> bits;
    this.codeSize = codeSize -= bits;
    return b;
  };
  FlateStream.prototype.getCode = function FlateStream_getCode(table) {
    var str = this.str;
    var codes = table[0];
    var maxLen = table[1];
    var codeSize = this.codeSize;
    var codeBuf = this.codeBuf;
    var b;
    while (codeSize < maxLen) {
      if ((b = str.getByte()) === -1) {
        break;
      }
      codeBuf |= b << codeSize;
      codeSize += 8;
    }
    var code = codes[codeBuf & (1 << maxLen) - 1];
    var codeLen = code >> 16;
    var codeVal = code & 0xffff;
    if (codeLen < 1 || codeSize < codeLen) {
      throw new _util.FormatError('Bad encoding in flate stream');
    }
    this.codeBuf = codeBuf >> codeLen;
    this.codeSize = codeSize - codeLen;
    return codeVal;
  };
  FlateStream.prototype.generateHuffmanTable = function flateStreamGenerateHuffmanTable(lengths) {
    var n = lengths.length;
    var maxLen = 0;
    var i;
    for (i = 0; i < n; ++i) {
      if (lengths[i] > maxLen) {
        maxLen = lengths[i];
      }
    }
    var size = 1 << maxLen;
    var codes = new Int32Array(size);
    for (var len = 1, code = 0, skip = 2; len <= maxLen; ++len, code <<= 1, skip <<= 1) {
      for (var val = 0; val < n; ++val) {
        if (lengths[val] === len) {
          var code2 = 0;
          var t = code;
          for (i = 0; i < len; ++i) {
            code2 = code2 << 1 | t & 1;
            t >>= 1;
          }
          for (i = code2; i < size; i += skip) {
            codes[i] = len << 16 | val;
          }
          ++code;
        }
      }
    }
    return [codes, maxLen];
  };
  FlateStream.prototype.readBlock = function FlateStream_readBlock() {
    var buffer, len;
    var str = this.str;
    var hdr = this.getBits(3);
    if (hdr & 1) {
      this.eof = true;
    }
    hdr >>= 1;
    if (hdr === 0) {
      var b;
      if ((b = str.getByte()) === -1) {
        throw new _util.FormatError('Bad block header in flate stream');
      }
      var blockLen = b;
      if ((b = str.getByte()) === -1) {
        throw new _util.FormatError('Bad block header in flate stream');
      }
      blockLen |= b << 8;
      if ((b = str.getByte()) === -1) {
        throw new _util.FormatError('Bad block header in flate stream');
      }
      var check = b;
      if ((b = str.getByte()) === -1) {
        throw new _util.FormatError('Bad block header in flate stream');
      }
      check |= b << 8;
      if (check !== (~blockLen & 0xffff) && (blockLen !== 0 || check !== 0)) {
        throw new _util.FormatError('Bad uncompressed block length in flate stream');
      }
      this.codeBuf = 0;
      this.codeSize = 0;
      var bufferLength = this.bufferLength;
      buffer = this.ensureBuffer(bufferLength + blockLen);
      var end = bufferLength + blockLen;
      this.bufferLength = end;
      if (blockLen === 0) {
        if (str.peekByte() === -1) {
          this.eof = true;
        }
      } else {
        for (var n = bufferLength; n < end; ++n) {
          if ((b = str.getByte()) === -1) {
            this.eof = true;
            break;
          }
          buffer[n] = b;
        }
      }
      return;
    }
    var litCodeTable;
    var distCodeTable;
    if (hdr === 1) {
      litCodeTable = fixedLitCodeTab;
      distCodeTable = fixedDistCodeTab;
    } else if (hdr === 2) {
      var numLitCodes = this.getBits(5) + 257;
      var numDistCodes = this.getBits(5) + 1;
      var numCodeLenCodes = this.getBits(4) + 4;
      var codeLenCodeLengths = new Uint8Array(codeLenCodeMap.length);
      var i;
      for (i = 0; i < numCodeLenCodes; ++i) {
        codeLenCodeLengths[codeLenCodeMap[i]] = this.getBits(3);
      }
      var codeLenCodeTab = this.generateHuffmanTable(codeLenCodeLengths);
      len = 0;
      i = 0;
      var codes = numLitCodes + numDistCodes;
      var codeLengths = new Uint8Array(codes);
      var bitsLength, bitsOffset, what;
      while (i < codes) {
        var code = this.getCode(codeLenCodeTab);
        if (code === 16) {
          bitsLength = 2;
          bitsOffset = 3;
          what = len;
        } else if (code === 17) {
          bitsLength = 3;
          bitsOffset = 3;
          what = len = 0;
        } else if (code === 18) {
          bitsLength = 7;
          bitsOffset = 11;
          what = len = 0;
        } else {
          codeLengths[i++] = len = code;
          continue;
        }
        var repeatLength = this.getBits(bitsLength) + bitsOffset;
        while (repeatLength-- > 0) {
          codeLengths[i++] = what;
        }
      }
      litCodeTable = this.generateHuffmanTable(codeLengths.subarray(0, numLitCodes));
      distCodeTable = this.generateHuffmanTable(codeLengths.subarray(numLitCodes, codes));
    } else {
      throw new _util.FormatError('Unknown block type in flate stream');
    }
    buffer = this.buffer;
    var limit = buffer ? buffer.length : 0;
    var pos = this.bufferLength;
    while (true) {
      var code1 = this.getCode(litCodeTable);
      if (code1 < 256) {
        if (pos + 1 >= limit) {
          buffer = this.ensureBuffer(pos + 1);
          limit = buffer.length;
        }
        buffer[pos++] = code1;
        continue;
      }
      if (code1 === 256) {
        this.bufferLength = pos;
        return;
      }
      code1 -= 257;
      code1 = lengthDecode[code1];
      var code2 = code1 >> 16;
      if (code2 > 0) {
        code2 = this.getBits(code2);
      }
      len = (code1 & 0xffff) + code2;
      code1 = this.getCode(distCodeTable);
      code1 = distDecode[code1];
      code2 = code1 >> 16;
      if (code2 > 0) {
        code2 = this.getBits(code2);
      }
      var dist = (code1 & 0xffff) + code2;
      if (pos + len >= limit) {
        buffer = this.ensureBuffer(pos + len);
        limit = buffer.length;
      }
      for (var k = 0; k < len; ++k, ++pos) {
        buffer[pos] = buffer[pos - dist];
      }
    }
  };
  return FlateStream;
}();
var PredictorStream = function PredictorStreamClosure() {
  function PredictorStream(str, maybeLength, params) {
    if (!(0, _primitives.isDict)(params)) {
      return str;
    }
    var predictor = this.predictor = params.get('Predictor') || 1;
    if (predictor <= 1) {
      return str;
    }
    if (predictor !== 2 && (predictor < 10 || predictor > 15)) {
      throw new _util.FormatError(`Unsupported predictor: ${predictor}`);
    }
    if (predictor === 2) {
      this.readBlock = this.readBlockTiff;
    } else {
      this.readBlock = this.readBlockPng;
    }
    this.str = str;
    this.dict = str.dict;
    var colors = this.colors = params.get('Colors') || 1;
    var bits = this.bits = params.get('BitsPerComponent') || 8;
    var columns = this.columns = params.get('Columns') || 1;
    this.pixBytes = colors * bits + 7 >> 3;
    this.rowBytes = columns * colors * bits + 7 >> 3;
    DecodeStream.call(this, maybeLength);
    return this;
  }
  PredictorStream.prototype = Object.create(DecodeStream.prototype);
  PredictorStream.prototype.readBlockTiff = function predictorStreamReadBlockTiff() {
    var rowBytes = this.rowBytes;
    var bufferLength = this.bufferLength;
    var buffer = this.ensureBuffer(bufferLength + rowBytes);
    var bits = this.bits;
    var colors = this.colors;
    var rawBytes = this.str.getBytes(rowBytes);
    this.eof = !rawBytes.length;
    if (this.eof) {
      return;
    }
    var inbuf = 0,
        outbuf = 0;
    var inbits = 0,
        outbits = 0;
    var pos = bufferLength;
    var i;
    if (bits === 1 && colors === 1) {
      for (i = 0; i < rowBytes; ++i) {
        var c = rawBytes[i] ^ inbuf;
        c ^= c >> 1;
        c ^= c >> 2;
        c ^= c >> 4;
        inbuf = (c & 1) << 7;
        buffer[pos++] = c;
      }
    } else if (bits === 8) {
      for (i = 0; i < colors; ++i) {
        buffer[pos++] = rawBytes[i];
      }
      for (; i < rowBytes; ++i) {
        buffer[pos] = buffer[pos - colors] + rawBytes[i];
        pos++;
      }
    } else if (bits === 16) {
      var bytesPerPixel = colors * 2;
      for (i = 0; i < bytesPerPixel; ++i) {
        buffer[pos++] = rawBytes[i];
      }
      for (; i < rowBytes; i += 2) {
        var sum = ((rawBytes[i] & 0xFF) << 8) + (rawBytes[i + 1] & 0xFF) + ((buffer[pos - bytesPerPixel] & 0xFF) << 8) + (buffer[pos - bytesPerPixel + 1] & 0xFF);
        buffer[pos++] = sum >> 8 & 0xFF;
        buffer[pos++] = sum & 0xFF;
      }
    } else {
      var compArray = new Uint8Array(colors + 1);
      var bitMask = (1 << bits) - 1;
      var j = 0,
          k = bufferLength;
      var columns = this.columns;
      for (i = 0; i < columns; ++i) {
        for (var kk = 0; kk < colors; ++kk) {
          if (inbits < bits) {
            inbuf = inbuf << 8 | rawBytes[j++] & 0xFF;
            inbits += 8;
          }
          compArray[kk] = compArray[kk] + (inbuf >> inbits - bits) & bitMask;
          inbits -= bits;
          outbuf = outbuf << bits | compArray[kk];
          outbits += bits;
          if (outbits >= 8) {
            buffer[k++] = outbuf >> outbits - 8 & 0xFF;
            outbits -= 8;
          }
        }
      }
      if (outbits > 0) {
        buffer[k++] = (outbuf << 8 - outbits) + (inbuf & (1 << 8 - outbits) - 1);
      }
    }
    this.bufferLength += rowBytes;
  };
  PredictorStream.prototype.readBlockPng = function predictorStreamReadBlockPng() {
    var rowBytes = this.rowBytes;
    var pixBytes = this.pixBytes;
    var predictor = this.str.getByte();
    var rawBytes = this.str.getBytes(rowBytes);
    this.eof = !rawBytes.length;
    if (this.eof) {
      return;
    }
    var bufferLength = this.bufferLength;
    var buffer = this.ensureBuffer(bufferLength + rowBytes);
    var prevRow = buffer.subarray(bufferLength - rowBytes, bufferLength);
    if (prevRow.length === 0) {
      prevRow = new Uint8Array(rowBytes);
    }
    var i,
        j = bufferLength,
        up,
        c;
    switch (predictor) {
      case 0:
        for (i = 0; i < rowBytes; ++i) {
          buffer[j++] = rawBytes[i];
        }
        break;
      case 1:
        for (i = 0; i < pixBytes; ++i) {
          buffer[j++] = rawBytes[i];
        }
        for (; i < rowBytes; ++i) {
          buffer[j] = buffer[j - pixBytes] + rawBytes[i] & 0xFF;
          j++;
        }
        break;
      case 2:
        for (i = 0; i < rowBytes; ++i) {
          buffer[j++] = prevRow[i] + rawBytes[i] & 0xFF;
        }
        break;
      case 3:
        for (i = 0; i < pixBytes; ++i) {
          buffer[j++] = (prevRow[i] >> 1) + rawBytes[i];
        }
        for (; i < rowBytes; ++i) {
          buffer[j] = (prevRow[i] + buffer[j - pixBytes] >> 1) + rawBytes[i] & 0xFF;
          j++;
        }
        break;
      case 4:
        for (i = 0; i < pixBytes; ++i) {
          up = prevRow[i];
          c = rawBytes[i];
          buffer[j++] = up + c;
        }
        for (; i < rowBytes; ++i) {
          up = prevRow[i];
          var upLeft = prevRow[i - pixBytes];
          var left = buffer[j - pixBytes];
          var p = left + up - upLeft;
          var pa = p - left;
          if (pa < 0) {
            pa = -pa;
          }
          var pb = p - up;
          if (pb < 0) {
            pb = -pb;
          }
          var pc = p - upLeft;
          if (pc < 0) {
            pc = -pc;
          }
          c = rawBytes[i];
          if (pa <= pb && pa <= pc) {
            buffer[j++] = left + c;
          } else if (pb <= pc) {
            buffer[j++] = up + c;
          } else {
            buffer[j++] = upLeft + c;
          }
        }
        break;
      default:
        throw new _util.FormatError(`Unsupported predictor: ${predictor}`);
    }
    this.bufferLength += rowBytes;
  };
  return PredictorStream;
}();
var DecryptStream = function DecryptStreamClosure() {
  function DecryptStream(str, maybeLength, decrypt) {
    this.str = str;
    this.dict = str.dict;
    this.decrypt = decrypt;
    this.nextChunk = null;
    this.initialized = false;
    DecodeStream.call(this, maybeLength);
  }
  var chunkSize = 512;
  DecryptStream.prototype = Object.create(DecodeStream.prototype);
  DecryptStream.prototype.readBlock = function DecryptStream_readBlock() {
    var chunk;
    if (this.initialized) {
      chunk = this.nextChunk;
    } else {
      chunk = this.str.getBytes(chunkSize);
      this.initialized = true;
    }
    if (!chunk || chunk.length === 0) {
      this.eof = true;
      return;
    }
    this.nextChunk = this.str.getBytes(chunkSize);
    var hasMoreData = this.nextChunk && this.nextChunk.length > 0;
    var decrypt = this.decrypt;
    chunk = decrypt(chunk, !hasMoreData);
    var bufferLength = this.bufferLength;
    var i,
        n = chunk.length;
    var buffer = this.ensureBuffer(bufferLength + n);
    for (i = 0; i < n; i++) {
      buffer[bufferLength++] = chunk[i];
    }
    this.bufferLength = bufferLength;
  };
  return DecryptStream;
}();
var Ascii85Stream = function Ascii85StreamClosure() {
  function Ascii85Stream(str, maybeLength) {
    this.str = str;
    this.dict = str.dict;
    this.input = new Uint8Array(5);
    if (maybeLength) {
      maybeLength = 0.8 * maybeLength;
    }
    DecodeStream.call(this, maybeLength);
  }
  Ascii85Stream.prototype = Object.create(DecodeStream.prototype);
  Ascii85Stream.prototype.readBlock = function Ascii85Stream_readBlock() {
    var TILDA_CHAR = 0x7E;
    var Z_LOWER_CHAR = 0x7A;
    var EOF = -1;
    var str = this.str;
    var c = str.getByte();
    while ((0, _util.isSpace)(c)) {
      c = str.getByte();
    }
    if (c === EOF || c === TILDA_CHAR) {
      this.eof = true;
      return;
    }
    var bufferLength = this.bufferLength,
        buffer;
    var i;
    if (c === Z_LOWER_CHAR) {
      buffer = this.ensureBuffer(bufferLength + 4);
      for (i = 0; i < 4; ++i) {
        buffer[bufferLength + i] = 0;
      }
      this.bufferLength += 4;
    } else {
      var input = this.input;
      input[0] = c;
      for (i = 1; i < 5; ++i) {
        c = str.getByte();
        while ((0, _util.isSpace)(c)) {
          c = str.getByte();
        }
        input[i] = c;
        if (c === EOF || c === TILDA_CHAR) {
          break;
        }
      }
      buffer = this.ensureBuffer(bufferLength + i - 1);
      this.bufferLength += i - 1;
      if (i < 5) {
        for (; i < 5; ++i) {
          input[i] = 0x21 + 84;
        }
        this.eof = true;
      }
      var t = 0;
      for (i = 0; i < 5; ++i) {
        t = t * 85 + (input[i] - 0x21);
      }
      for (i = 3; i >= 0; --i) {
        buffer[bufferLength + i] = t & 0xFF;
        t >>= 8;
      }
    }
  };
  return Ascii85Stream;
}();
var AsciiHexStream = function AsciiHexStreamClosure() {
  function AsciiHexStream(str, maybeLength) {
    this.str = str;
    this.dict = str.dict;
    this.firstDigit = -1;
    if (maybeLength) {
      maybeLength = 0.5 * maybeLength;
    }
    DecodeStream.call(this, maybeLength);
  }
  AsciiHexStream.prototype = Object.create(DecodeStream.prototype);
  AsciiHexStream.prototype.readBlock = function AsciiHexStream_readBlock() {
    var UPSTREAM_BLOCK_SIZE = 8000;
    var bytes = this.str.getBytes(UPSTREAM_BLOCK_SIZE);
    if (!bytes.length) {
      this.eof = true;
      return;
    }
    var maxDecodeLength = bytes.length + 1 >> 1;
    var buffer = this.ensureBuffer(this.bufferLength + maxDecodeLength);
    var bufferLength = this.bufferLength;
    var firstDigit = this.firstDigit;
    for (var i = 0, ii = bytes.length; i < ii; i++) {
      var ch = bytes[i],
          digit;
      if (ch >= 0x30 && ch <= 0x39) {
        digit = ch & 0x0F;
      } else if (ch >= 0x41 && ch <= 0x46 || ch >= 0x61 && ch <= 0x66) {
        digit = (ch & 0x0F) + 9;
      } else if (ch === 0x3E) {
        this.eof = true;
        break;
      } else {
        continue;
      }
      if (firstDigit < 0) {
        firstDigit = digit;
      } else {
        buffer[bufferLength++] = firstDigit << 4 | digit;
        firstDigit = -1;
      }
    }
    if (firstDigit >= 0 && this.eof) {
      buffer[bufferLength++] = firstDigit << 4;
      firstDigit = -1;
    }
    this.firstDigit = firstDigit;
    this.bufferLength = bufferLength;
  };
  return AsciiHexStream;
}();
var RunLengthStream = function RunLengthStreamClosure() {
  function RunLengthStream(str, maybeLength) {
    this.str = str;
    this.dict = str.dict;
    DecodeStream.call(this, maybeLength);
  }
  RunLengthStream.prototype = Object.create(DecodeStream.prototype);
  RunLengthStream.prototype.readBlock = function RunLengthStream_readBlock() {
    var repeatHeader = this.str.getBytes(2);
    if (!repeatHeader || repeatHeader.length < 2 || repeatHeader[0] === 128) {
      this.eof = true;
      return;
    }
    var buffer;
    var bufferLength = this.bufferLength;
    var n = repeatHeader[0];
    if (n < 128) {
      buffer = this.ensureBuffer(bufferLength + n + 1);
      buffer[bufferLength++] = repeatHeader[1];
      if (n > 0) {
        var source = this.str.getBytes(n);
        buffer.set(source, bufferLength);
        bufferLength += n;
      }
    } else {
      n = 257 - n;
      var b = repeatHeader[1];
      buffer = this.ensureBuffer(bufferLength + n + 1);
      for (var i = 0; i < n; i++) {
        buffer[bufferLength++] = b;
      }
    }
    this.bufferLength = bufferLength;
  };
  return RunLengthStream;
}();
var LZWStream = function LZWStreamClosure() {
  function LZWStream(str, maybeLength, earlyChange) {
    this.str = str;
    this.dict = str.dict;
    this.cachedData = 0;
    this.bitsCached = 0;
    var maxLzwDictionarySize = 4096;
    var lzwState = {
      earlyChange,
      codeLength: 9,
      nextCode: 258,
      dictionaryValues: new Uint8Array(maxLzwDictionarySize),
      dictionaryLengths: new Uint16Array(maxLzwDictionarySize),
      dictionaryPrevCodes: new Uint16Array(maxLzwDictionarySize),
      currentSequence: new Uint8Array(maxLzwDictionarySize),
      currentSequenceLength: 0
    };
    for (var i = 0; i < 256; ++i) {
      lzwState.dictionaryValues[i] = i;
      lzwState.dictionaryLengths[i] = 1;
    }
    this.lzwState = lzwState;
    DecodeStream.call(this, maybeLength);
  }
  LZWStream.prototype = Object.create(DecodeStream.prototype);
  LZWStream.prototype.readBits = function LZWStream_readBits(n) {
    var bitsCached = this.bitsCached;
    var cachedData = this.cachedData;
    while (bitsCached < n) {
      var c = this.str.getByte();
      if (c === -1) {
        this.eof = true;
        return null;
      }
      cachedData = cachedData << 8 | c;
      bitsCached += 8;
    }
    this.bitsCached = bitsCached -= n;
    this.cachedData = cachedData;
    this.lastCode = null;
    return cachedData >>> bitsCached & (1 << n) - 1;
  };
  LZWStream.prototype.readBlock = function LZWStream_readBlock() {
    var blockSize = 512;
    var estimatedDecodedSize = blockSize * 2,
        decodedSizeDelta = blockSize;
    var i, j, q;
    var lzwState = this.lzwState;
    if (!lzwState) {
      return;
    }
    var earlyChange = lzwState.earlyChange;
    var nextCode = lzwState.nextCode;
    var dictionaryValues = lzwState.dictionaryValues;
    var dictionaryLengths = lzwState.dictionaryLengths;
    var dictionaryPrevCodes = lzwState.dictionaryPrevCodes;
    var codeLength = lzwState.codeLength;
    var prevCode = lzwState.prevCode;
    var currentSequence = lzwState.currentSequence;
    var currentSequenceLength = lzwState.currentSequenceLength;
    var decodedLength = 0;
    var currentBufferLength = this.bufferLength;
    var buffer = this.ensureBuffer(this.bufferLength + estimatedDecodedSize);
    for (i = 0; i < blockSize; i++) {
      var code = this.readBits(codeLength);
      var hasPrev = currentSequenceLength > 0;
      if (code < 256) {
        currentSequence[0] = code;
        currentSequenceLength = 1;
      } else if (code >= 258) {
        if (code < nextCode) {
          currentSequenceLength = dictionaryLengths[code];
          for (j = currentSequenceLength - 1, q = code; j >= 0; j--) {
            currentSequence[j] = dictionaryValues[q];
            q = dictionaryPrevCodes[q];
          }
        } else {
          currentSequence[currentSequenceLength++] = currentSequence[0];
        }
      } else if (code === 256) {
        codeLength = 9;
        nextCode = 258;
        currentSequenceLength = 0;
        continue;
      } else {
        this.eof = true;
        delete this.lzwState;
        break;
      }
      if (hasPrev) {
        dictionaryPrevCodes[nextCode] = prevCode;
        dictionaryLengths[nextCode] = dictionaryLengths[prevCode] + 1;
        dictionaryValues[nextCode] = currentSequence[0];
        nextCode++;
        codeLength = nextCode + earlyChange & nextCode + earlyChange - 1 ? codeLength : Math.min(Math.log(nextCode + earlyChange) / 0.6931471805599453 + 1, 12) | 0;
      }
      prevCode = code;
      decodedLength += currentSequenceLength;
      if (estimatedDecodedSize < decodedLength) {
        do {
          estimatedDecodedSize += decodedSizeDelta;
        } while (estimatedDecodedSize < decodedLength);
        buffer = this.ensureBuffer(this.bufferLength + estimatedDecodedSize);
      }
      for (j = 0; j < currentSequenceLength; j++) {
        buffer[currentBufferLength++] = currentSequence[j];
      }
    }
    lzwState.nextCode = nextCode;
    lzwState.codeLength = codeLength;
    lzwState.prevCode = prevCode;
    lzwState.currentSequenceLength = currentSequenceLength;
    this.bufferLength = currentBufferLength;
  };
  return LZWStream;
}();
var NullStream = function NullStreamClosure() {
  function NullStream() {
    Stream.call(this, new Uint8Array(0));
  }
  NullStream.prototype = Stream.prototype;
  return NullStream;
}();
exports.Ascii85Stream = Ascii85Stream;
exports.AsciiHexStream = AsciiHexStream;
exports.DecryptStream = DecryptStream;
exports.DecodeStream = DecodeStream;
exports.FlateStream = FlateStream;
exports.NullStream = NullStream;
exports.PredictorStream = PredictorStream;
exports.RunLengthStream = RunLengthStream;
exports.Stream = Stream;
exports.StreamsSequenceStream = StreamsSequenceStream;
exports.StringStream = StringStream;
exports.LZWStream = LZWStream;

/***/ }),
/* 3 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.ColorSpace = undefined;

var _util = __w_pdfjs_require__(0);

var _primitives = __w_pdfjs_require__(1);

var ColorSpace = function ColorSpaceClosure() {
  function resizeRgbImage(src, bpc, w1, h1, w2, h2, alpha01, dest) {
    var COMPONENTS = 3;
    alpha01 = alpha01 !== 1 ? 0 : alpha01;
    var xRatio = w1 / w2;
    var yRatio = h1 / h2;
    var i,
        j,
        py,
        newIndex = 0,
        oldIndex;
    var xScaled = new Uint16Array(w2);
    var w1Scanline = w1 * COMPONENTS;
    for (i = 0; i < w2; i++) {
      xScaled[i] = Math.floor(i * xRatio) * COMPONENTS;
    }
    for (i = 0; i < h2; i++) {
      py = Math.floor(i * yRatio) * w1Scanline;
      for (j = 0; j < w2; j++) {
        oldIndex = py + xScaled[j];
        dest[newIndex++] = src[oldIndex++];
        dest[newIndex++] = src[oldIndex++];
        dest[newIndex++] = src[oldIndex++];
        newIndex += alpha01;
      }
    }
  }
  function ColorSpace() {
    (0, _util.unreachable)('should not call ColorSpace constructor');
  }
  ColorSpace.prototype = {
    getRgb: function ColorSpace_getRgb(src, srcOffset) {
      var rgb = new Uint8Array(3);
      this.getRgbItem(src, srcOffset, rgb, 0);
      return rgb;
    },
    getRgbItem: function ColorSpace_getRgbItem(src, srcOffset, dest, destOffset) {
      (0, _util.unreachable)('Should not call ColorSpace.getRgbItem');
    },
    getRgbBuffer: function ColorSpace_getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) {
      (0, _util.unreachable)('Should not call ColorSpace.getRgbBuffer');
    },
    getOutputLength: function ColorSpace_getOutputLength(inputLength, alpha01) {
      (0, _util.unreachable)('Should not call ColorSpace.getOutputLength');
    },
    isPassthrough: function ColorSpace_isPassthrough(bits) {
      return false;
    },
    fillRgb: function ColorSpace_fillRgb(dest, originalWidth, originalHeight, width, height, actualHeight, bpc, comps, alpha01) {
      var count = originalWidth * originalHeight;
      var rgbBuf = null;
      var numComponentColors = 1 << bpc;
      var needsResizing = originalHeight !== height || originalWidth !== width;
      var i, ii;
      if (this.isPassthrough(bpc)) {
        rgbBuf = comps;
      } else if (this.numComps === 1 && count > numComponentColors && this.name !== 'DeviceGray' && this.name !== 'DeviceRGB') {
        var allColors = bpc <= 8 ? new Uint8Array(numComponentColors) : new Uint16Array(numComponentColors);
        var key;
        for (i = 0; i < numComponentColors; i++) {
          allColors[i] = i;
        }
        var colorMap = new Uint8Array(numComponentColors * 3);
        this.getRgbBuffer(allColors, 0, numComponentColors, colorMap, 0, bpc, 0);
        var destPos, rgbPos;
        if (!needsResizing) {
          destPos = 0;
          for (i = 0; i < count; ++i) {
            key = comps[i] * 3;
            dest[destPos++] = colorMap[key];
            dest[destPos++] = colorMap[key + 1];
            dest[destPos++] = colorMap[key + 2];
            destPos += alpha01;
          }
        } else {
          rgbBuf = new Uint8Array(count * 3);
          rgbPos = 0;
          for (i = 0; i < count; ++i) {
            key = comps[i] * 3;
            rgbBuf[rgbPos++] = colorMap[key];
            rgbBuf[rgbPos++] = colorMap[key + 1];
            rgbBuf[rgbPos++] = colorMap[key + 2];
          }
        }
      } else {
        if (!needsResizing) {
          this.getRgbBuffer(comps, 0, width * actualHeight, dest, 0, bpc, alpha01);
        } else {
          rgbBuf = new Uint8Array(count * 3);
          this.getRgbBuffer(comps, 0, count, rgbBuf, 0, bpc, 0);
        }
      }
      if (rgbBuf) {
        if (needsResizing) {
          resizeRgbImage(rgbBuf, bpc, originalWidth, originalHeight, width, height, alpha01, dest);
        } else {
          rgbPos = 0;
          destPos = 0;
          for (i = 0, ii = width * actualHeight; i < ii; i++) {
            dest[destPos++] = rgbBuf[rgbPos++];
            dest[destPos++] = rgbBuf[rgbPos++];
            dest[destPos++] = rgbBuf[rgbPos++];
            destPos += alpha01;
          }
        }
      }
    },
    usesZeroToOneRange: true
  };
  ColorSpace.parse = function (cs, xref, res, pdfFunctionFactory) {
    let IR = ColorSpace.parseToIR(cs, xref, res, pdfFunctionFactory);
    return ColorSpace.fromIR(IR);
  };
  ColorSpace.fromIR = function (IR) {
    var name = Array.isArray(IR) ? IR[0] : IR;
    var whitePoint, blackPoint, gamma;
    switch (name) {
      case 'DeviceGrayCS':
        return this.singletons.gray;
      case 'DeviceRgbCS':
        return this.singletons.rgb;
      case 'DeviceCmykCS':
        return this.singletons.cmyk;
      case 'CalGrayCS':
        whitePoint = IR[1];
        blackPoint = IR[2];
        gamma = IR[3];
        return new CalGrayCS(whitePoint, blackPoint, gamma);
      case 'CalRGBCS':
        whitePoint = IR[1];
        blackPoint = IR[2];
        gamma = IR[3];
        var matrix = IR[4];
        return new CalRGBCS(whitePoint, blackPoint, gamma, matrix);
      case 'PatternCS':
        var basePatternCS = IR[1];
        if (basePatternCS) {
          basePatternCS = ColorSpace.fromIR(basePatternCS);
        }
        return new PatternCS(basePatternCS);
      case 'IndexedCS':
        var baseIndexedCS = IR[1];
        var hiVal = IR[2];
        var lookup = IR[3];
        return new IndexedCS(ColorSpace.fromIR(baseIndexedCS), hiVal, lookup);
      case 'AlternateCS':
        var numComps = IR[1];
        var alt = IR[2];
        var tintFn = IR[3];
        return new AlternateCS(numComps, ColorSpace.fromIR(alt), tintFn);
      case 'LabCS':
        whitePoint = IR[1];
        blackPoint = IR[2];
        var range = IR[3];
        return new LabCS(whitePoint, blackPoint, range);
      default:
        throw new _util.FormatError(`Unknown colorspace name: ${name}`);
    }
  };
  ColorSpace.parseToIR = function (cs, xref, res = null, pdfFunctionFactory) {
    cs = xref.fetchIfRef(cs);
    if ((0, _primitives.isName)(cs)) {
      switch (cs.name) {
        case 'DeviceGray':
        case 'G':
          return 'DeviceGrayCS';
        case 'DeviceRGB':
        case 'RGB':
          return 'DeviceRgbCS';
        case 'DeviceCMYK':
        case 'CMYK':
          return 'DeviceCmykCS';
        case 'Pattern':
          return ['PatternCS', null];
        default:
          if ((0, _primitives.isDict)(res)) {
            let colorSpaces = res.get('ColorSpace');
            if ((0, _primitives.isDict)(colorSpaces)) {
              let resCS = colorSpaces.get(cs.name);
              if (resCS) {
                if ((0, _primitives.isName)(resCS)) {
                  return ColorSpace.parseToIR(resCS, xref, res, pdfFunctionFactory);
                }
                cs = resCS;
                break;
              }
            }
          }
          throw new _util.FormatError(`unrecognized colorspace ${cs.name}`);
      }
    }
    if (Array.isArray(cs)) {
      var mode = xref.fetchIfRef(cs[0]).name;
      var numComps, params, alt, whitePoint, blackPoint, gamma;
      switch (mode) {
        case 'DeviceGray':
        case 'G':
          return 'DeviceGrayCS';
        case 'DeviceRGB':
        case 'RGB':
          return 'DeviceRgbCS';
        case 'DeviceCMYK':
        case 'CMYK':
          return 'DeviceCmykCS';
        case 'CalGray':
          params = xref.fetchIfRef(cs[1]);
          whitePoint = params.getArray('WhitePoint');
          blackPoint = params.getArray('BlackPoint');
          gamma = params.get('Gamma');
          return ['CalGrayCS', whitePoint, blackPoint, gamma];
        case 'CalRGB':
          params = xref.fetchIfRef(cs[1]);
          whitePoint = params.getArray('WhitePoint');
          blackPoint = params.getArray('BlackPoint');
          gamma = params.getArray('Gamma');
          var matrix = params.getArray('Matrix');
          return ['CalRGBCS', whitePoint, blackPoint, gamma, matrix];
        case 'ICCBased':
          var stream = xref.fetchIfRef(cs[1]);
          var dict = stream.dict;
          numComps = dict.get('N');
          alt = dict.get('Alternate');
          if (alt) {
            var altIR = ColorSpace.parseToIR(alt, xref, res, pdfFunctionFactory);
            var altCS = ColorSpace.fromIR(altIR, pdfFunctionFactory);
            if (altCS.numComps === numComps) {
              return altIR;
            }
            (0, _util.warn)('ICCBased color space: Ignoring incorrect /Alternate entry.');
          }
          if (numComps === 1) {
            return 'DeviceGrayCS';
          } else if (numComps === 3) {
            return 'DeviceRgbCS';
          } else if (numComps === 4) {
            return 'DeviceCmykCS';
          }
          break;
        case 'Pattern':
          var basePatternCS = cs[1] || null;
          if (basePatternCS) {
            basePatternCS = ColorSpace.parseToIR(basePatternCS, xref, res, pdfFunctionFactory);
          }
          return ['PatternCS', basePatternCS];
        case 'Indexed':
        case 'I':
          var baseIndexedCS = ColorSpace.parseToIR(cs[1], xref, res, pdfFunctionFactory);
          var hiVal = xref.fetchIfRef(cs[2]) + 1;
          var lookup = xref.fetchIfRef(cs[3]);
          if ((0, _primitives.isStream)(lookup)) {
            lookup = lookup.getBytes();
          }
          return ['IndexedCS', baseIndexedCS, hiVal, lookup];
        case 'Separation':
        case 'DeviceN':
          var name = xref.fetchIfRef(cs[1]);
          numComps = Array.isArray(name) ? name.length : 1;
          alt = ColorSpace.parseToIR(cs[2], xref, res, pdfFunctionFactory);
          let tintFn = pdfFunctionFactory.create(xref.fetchIfRef(cs[3]));
          return ['AlternateCS', numComps, alt, tintFn];
        case 'Lab':
          params = xref.fetchIfRef(cs[1]);
          whitePoint = params.getArray('WhitePoint');
          blackPoint = params.getArray('BlackPoint');
          var range = params.getArray('Range');
          return ['LabCS', whitePoint, blackPoint, range];
        default:
          throw new _util.FormatError(`unimplemented color space object "${mode}"`);
      }
    }
    throw new _util.FormatError(`unrecognized color space object: "${cs}"`);
  };
  ColorSpace.isDefaultDecode = function ColorSpace_isDefaultDecode(decode, n) {
    if (!Array.isArray(decode)) {
      return true;
    }
    if (n * 2 !== decode.length) {
      (0, _util.warn)('The decode map is not the correct length');
      return true;
    }
    for (var i = 0, ii = decode.length; i < ii; i += 2) {
      if (decode[i] !== 0 || decode[i + 1] !== 1) {
        return false;
      }
    }
    return true;
  };
  ColorSpace.singletons = {
    get gray() {
      return (0, _util.shadow)(this, 'gray', new DeviceGrayCS());
    },
    get rgb() {
      return (0, _util.shadow)(this, 'rgb', new DeviceRgbCS());
    },
    get cmyk() {
      return (0, _util.shadow)(this, 'cmyk', new DeviceCmykCS());
    }
  };
  return ColorSpace;
}();
var AlternateCS = function AlternateCSClosure() {
  function AlternateCS(numComps, base, tintFn) {
    this.name = 'Alternate';
    this.numComps = numComps;
    this.defaultColor = new Float32Array(numComps);
    for (var i = 0; i < numComps; ++i) {
      this.defaultColor[i] = 1;
    }
    this.base = base;
    this.tintFn = tintFn;
    this.tmpBuf = new Float32Array(base.numComps);
  }
  AlternateCS.prototype = {
    getRgb: ColorSpace.prototype.getRgb,
    getRgbItem: function AlternateCS_getRgbItem(src, srcOffset, dest, destOffset) {
      var tmpBuf = this.tmpBuf;
      this.tintFn(src, srcOffset, tmpBuf, 0);
      this.base.getRgbItem(tmpBuf, 0, dest, destOffset);
    },
    getRgbBuffer: function AlternateCS_getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) {
      var tintFn = this.tintFn;
      var base = this.base;
      var scale = 1 / ((1 << bits) - 1);
      var baseNumComps = base.numComps;
      var usesZeroToOneRange = base.usesZeroToOneRange;
      var isPassthrough = (base.isPassthrough(8) || !usesZeroToOneRange) && alpha01 === 0;
      var pos = isPassthrough ? destOffset : 0;
      var baseBuf = isPassthrough ? dest : new Uint8Array(baseNumComps * count);
      var numComps = this.numComps;
      var scaled = new Float32Array(numComps);
      var tinted = new Float32Array(baseNumComps);
      var i, j;
      for (i = 0; i < count; i++) {
        for (j = 0; j < numComps; j++) {
          scaled[j] = src[srcOffset++] * scale;
        }
        tintFn(scaled, 0, tinted, 0);
        if (usesZeroToOneRange) {
          for (j = 0; j < baseNumComps; j++) {
            baseBuf[pos++] = tinted[j] * 255;
          }
        } else {
          base.getRgbItem(tinted, 0, baseBuf, pos);
          pos += baseNumComps;
        }
      }
      if (!isPassthrough) {
        base.getRgbBuffer(baseBuf, 0, count, dest, destOffset, 8, alpha01);
      }
    },
    getOutputLength: function AlternateCS_getOutputLength(inputLength, alpha01) {
      return this.base.getOutputLength(inputLength * this.base.numComps / this.numComps, alpha01);
    },
    isPassthrough: ColorSpace.prototype.isPassthrough,
    fillRgb: ColorSpace.prototype.fillRgb,
    isDefaultDecode: function AlternateCS_isDefaultDecode(decodeMap) {
      return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
    },
    usesZeroToOneRange: true
  };
  return AlternateCS;
}();
var PatternCS = function PatternCSClosure() {
  function PatternCS(baseCS) {
    this.name = 'Pattern';
    this.base = baseCS;
  }
  PatternCS.prototype = {};
  return PatternCS;
}();
var IndexedCS = function IndexedCSClosure() {
  function IndexedCS(base, highVal, lookup) {
    this.name = 'Indexed';
    this.numComps = 1;
    this.defaultColor = new Uint8Array(this.numComps);
    this.base = base;
    this.highVal = highVal;
    var baseNumComps = base.numComps;
    var length = baseNumComps * highVal;
    if ((0, _primitives.isStream)(lookup)) {
      this.lookup = new Uint8Array(length);
      var bytes = lookup.getBytes(length);
      this.lookup.set(bytes);
    } else if ((0, _util.isString)(lookup)) {
      this.lookup = new Uint8Array(length);
      for (var i = 0; i < length; ++i) {
        this.lookup[i] = lookup.charCodeAt(i);
      }
    } else if (lookup instanceof Uint8Array) {
      this.lookup = lookup;
    } else {
      throw new _util.FormatError(`Unrecognized lookup table: ${lookup}`);
    }
  }
  IndexedCS.prototype = {
    getRgb: ColorSpace.prototype.getRgb,
    getRgbItem: function IndexedCS_getRgbItem(src, srcOffset, dest, destOffset) {
      var numComps = this.base.numComps;
      var start = src[srcOffset] * numComps;
      this.base.getRgbBuffer(this.lookup, start, 1, dest, destOffset, 8, 0);
    },
    getRgbBuffer: function IndexedCS_getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) {
      var base = this.base;
      var numComps = base.numComps;
      var outputDelta = base.getOutputLength(numComps, alpha01);
      var lookup = this.lookup;
      for (var i = 0; i < count; ++i) {
        var lookupPos = src[srcOffset++] * numComps;
        base.getRgbBuffer(lookup, lookupPos, 1, dest, destOffset, 8, alpha01);
        destOffset += outputDelta;
      }
    },
    getOutputLength: function IndexedCS_getOutputLength(inputLength, alpha01) {
      return this.base.getOutputLength(inputLength * this.base.numComps, alpha01);
    },
    isPassthrough: ColorSpace.prototype.isPassthrough,
    fillRgb: ColorSpace.prototype.fillRgb,
    isDefaultDecode: function IndexedCS_isDefaultDecode(decodeMap) {
      return true;
    },
    usesZeroToOneRange: true
  };
  return IndexedCS;
}();
var DeviceGrayCS = function DeviceGrayCSClosure() {
  function DeviceGrayCS() {
    this.name = 'DeviceGray';
    this.numComps = 1;
    this.defaultColor = new Float32Array(this.numComps);
  }
  DeviceGrayCS.prototype = {
    getRgb: ColorSpace.prototype.getRgb,
    getRgbItem: function DeviceGrayCS_getRgbItem(src, srcOffset, dest, destOffset) {
      var c = src[srcOffset] * 255 | 0;
      c = c < 0 ? 0 : c > 255 ? 255 : c;
      dest[destOffset] = dest[destOffset + 1] = dest[destOffset + 2] = c;
    },
    getRgbBuffer: function DeviceGrayCS_getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) {
      var scale = 255 / ((1 << bits) - 1);
      var j = srcOffset,
          q = destOffset;
      for (var i = 0; i < count; ++i) {
        var c = scale * src[j++] | 0;
        dest[q++] = c;
        dest[q++] = c;
        dest[q++] = c;
        q += alpha01;
      }
    },
    getOutputLength: function DeviceGrayCS_getOutputLength(inputLength, alpha01) {
      return inputLength * (3 + alpha01);
    },
    isPassthrough: ColorSpace.prototype.isPassthrough,
    fillRgb: ColorSpace.prototype.fillRgb,
    isDefaultDecode: function DeviceGrayCS_isDefaultDecode(decodeMap) {
      return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
    },
    usesZeroToOneRange: true
  };
  return DeviceGrayCS;
}();
var DeviceRgbCS = function DeviceRgbCSClosure() {
  function DeviceRgbCS() {
    this.name = 'DeviceRGB';
    this.numComps = 3;
    this.defaultColor = new Float32Array(this.numComps);
  }
  DeviceRgbCS.prototype = {
    getRgb: ColorSpace.prototype.getRgb,
    getRgbItem: function DeviceRgbCS_getRgbItem(src, srcOffset, dest, destOffset) {
      var r = src[srcOffset] * 255 | 0;
      var g = src[srcOffset + 1] * 255 | 0;
      var b = src[srcOffset + 2] * 255 | 0;
      dest[destOffset] = r < 0 ? 0 : r > 255 ? 255 : r;
      dest[destOffset + 1] = g < 0 ? 0 : g > 255 ? 255 : g;
      dest[destOffset + 2] = b < 0 ? 0 : b > 255 ? 255 : b;
    },
    getRgbBuffer: function DeviceRgbCS_getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) {
      if (bits === 8 && alpha01 === 0) {
        dest.set(src.subarray(srcOffset, srcOffset + count * 3), destOffset);
        return;
      }
      var scale = 255 / ((1 << bits) - 1);
      var j = srcOffset,
          q = destOffset;
      for (var i = 0; i < count; ++i) {
        dest[q++] = scale * src[j++] | 0;
        dest[q++] = scale * src[j++] | 0;
        dest[q++] = scale * src[j++] | 0;
        q += alpha01;
      }
    },
    getOutputLength: function DeviceRgbCS_getOutputLength(inputLength, alpha01) {
      return inputLength * (3 + alpha01) / 3 | 0;
    },
    isPassthrough: function DeviceRgbCS_isPassthrough(bits) {
      return bits === 8;
    },
    fillRgb: ColorSpace.prototype.fillRgb,
    isDefaultDecode: function DeviceRgbCS_isDefaultDecode(decodeMap) {
      return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
    },
    usesZeroToOneRange: true
  };
  return DeviceRgbCS;
}();
var DeviceCmykCS = function DeviceCmykCSClosure() {
  function convertToRgb(src, srcOffset, srcScale, dest, destOffset) {
    var c = src[srcOffset + 0] * srcScale;
    var m = src[srcOffset + 1] * srcScale;
    var y = src[srcOffset + 2] * srcScale;
    var k = src[srcOffset + 3] * srcScale;
    var r = c * (-4.387332384609988 * c + 54.48615194189176 * m + 18.82290502165302 * y + 212.25662451639585 * k + -285.2331026137004) + m * (1.7149763477362134 * m - 5.6096736904047315 * y + -17.873870861415444 * k - 5.497006427196366) + y * (-2.5217340131683033 * y - 21.248923337353073 * k + 17.5119270841813) + k * (-21.86122147463605 * k - 189.48180835922747) + 255 | 0;
    var g = c * (8.841041422036149 * c + 60.118027045597366 * m + 6.871425592049007 * y + 31.159100130055922 * k + -79.2970844816548) + m * (-15.310361306967817 * m + 17.575251261109482 * y + 131.35250912493976 * k - 190.9453302588951) + y * (4.444339102852739 * y + 9.8632861493405 * k - 24.86741582555878) + k * (-20.737325471181034 * k - 187.80453709719578) + 255 | 0;
    var b = c * (0.8842522430003296 * c + 8.078677503112928 * m + 30.89978309703729 * y - 0.23883238689178934 * k + -14.183576799673286) + m * (10.49593273432072 * m + 63.02378494754052 * y + 50.606957656360734 * k - 112.23884253719248) + y * (0.03296041114873217 * y + 115.60384449646641 * k + -193.58209356861505) + k * (-22.33816807309886 * k - 180.12613974708367) + 255 | 0;
    dest[destOffset] = r > 255 ? 255 : r < 0 ? 0 : r;
    dest[destOffset + 1] = g > 255 ? 255 : g < 0 ? 0 : g;
    dest[destOffset + 2] = b > 255 ? 255 : b < 0 ? 0 : b;
  }
  function DeviceCmykCS() {
    this.name = 'DeviceCMYK';
    this.numComps = 4;
    this.defaultColor = new Float32Array(this.numComps);
    this.defaultColor[3] = 1;
  }
  DeviceCmykCS.prototype = {
    getRgb: ColorSpace.prototype.getRgb,
    getRgbItem: function DeviceCmykCS_getRgbItem(src, srcOffset, dest, destOffset) {
      convertToRgb(src, srcOffset, 1, dest, destOffset);
    },
    getRgbBuffer: function DeviceCmykCS_getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) {
      var scale = 1 / ((1 << bits) - 1);
      for (var i = 0; i < count; i++) {
        convertToRgb(src, srcOffset, scale, dest, destOffset);
        srcOffset += 4;
        destOffset += 3 + alpha01;
      }
    },
    getOutputLength: function DeviceCmykCS_getOutputLength(inputLength, alpha01) {
      return inputLength / 4 * (3 + alpha01) | 0;
    },
    isPassthrough: ColorSpace.prototype.isPassthrough,
    fillRgb: ColorSpace.prototype.fillRgb,
    isDefaultDecode: function DeviceCmykCS_isDefaultDecode(decodeMap) {
      return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
    },
    usesZeroToOneRange: true
  };
  return DeviceCmykCS;
}();
var CalGrayCS = function CalGrayCSClosure() {
  function CalGrayCS(whitePoint, blackPoint, gamma) {
    this.name = 'CalGray';
    this.numComps = 1;
    this.defaultColor = new Float32Array(this.numComps);
    if (!whitePoint) {
      throw new _util.FormatError('WhitePoint missing - required for color space CalGray');
    }
    blackPoint = blackPoint || [0, 0, 0];
    gamma = gamma || 1;
    this.XW = whitePoint[0];
    this.YW = whitePoint[1];
    this.ZW = whitePoint[2];
    this.XB = blackPoint[0];
    this.YB = blackPoint[1];
    this.ZB = blackPoint[2];
    this.G = gamma;
    if (this.XW < 0 || this.ZW < 0 || this.YW !== 1) {
      throw new _util.FormatError(`Invalid WhitePoint components for ${this.name}` + ', no fallback available');
    }
    if (this.XB < 0 || this.YB < 0 || this.ZB < 0) {
      (0, _util.info)('Invalid BlackPoint for ' + this.name + ', falling back to default');
      this.XB = this.YB = this.ZB = 0;
    }
    if (this.XB !== 0 || this.YB !== 0 || this.ZB !== 0) {
      (0, _util.warn)(this.name + ', BlackPoint: XB: ' + this.XB + ', YB: ' + this.YB + ', ZB: ' + this.ZB + ', only default values are supported.');
    }
    if (this.G < 1) {
      (0, _util.info)('Invalid Gamma: ' + this.G + ' for ' + this.name + ', falling back to default');
      this.G = 1;
    }
  }
  function convertToRgb(cs, src, srcOffset, dest, destOffset, scale) {
    var A = src[srcOffset] * scale;
    var AG = Math.pow(A, cs.G);
    var L = cs.YW * AG;
    var val = Math.max(295.8 * Math.pow(L, 0.333333333333333333) - 40.8, 0) | 0;
    dest[destOffset] = val;
    dest[destOffset + 1] = val;
    dest[destOffset + 2] = val;
  }
  CalGrayCS.prototype = {
    getRgb: ColorSpace.prototype.getRgb,
    getRgbItem: function CalGrayCS_getRgbItem(src, srcOffset, dest, destOffset) {
      convertToRgb(this, src, srcOffset, dest, destOffset, 1);
    },
    getRgbBuffer: function CalGrayCS_getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) {
      var scale = 1 / ((1 << bits) - 1);
      for (var i = 0; i < count; ++i) {
        convertToRgb(this, src, srcOffset, dest, destOffset, scale);
        srcOffset += 1;
        destOffset += 3 + alpha01;
      }
    },
    getOutputLength: function CalGrayCS_getOutputLength(inputLength, alpha01) {
      return inputLength * (3 + alpha01);
    },
    isPassthrough: ColorSpace.prototype.isPassthrough,
    fillRgb: ColorSpace.prototype.fillRgb,
    isDefaultDecode: function CalGrayCS_isDefaultDecode(decodeMap) {
      return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
    },
    usesZeroToOneRange: true
  };
  return CalGrayCS;
}();
var CalRGBCS = function CalRGBCSClosure() {
  var BRADFORD_SCALE_MATRIX = new Float32Array([0.8951, 0.2664, -0.1614, -0.7502, 1.7135, 0.0367, 0.0389, -0.0685, 1.0296]);
  var BRADFORD_SCALE_INVERSE_MATRIX = new Float32Array([0.9869929, -0.1470543, 0.1599627, 0.4323053, 0.5183603, 0.0492912, -0.0085287, 0.0400428, 0.9684867]);
  var SRGB_D65_XYZ_TO_RGB_MATRIX = new Float32Array([3.2404542, -1.5371385, -0.4985314, -0.9692660, 1.8760108, 0.0415560, 0.0556434, -0.2040259, 1.0572252]);
  var FLAT_WHITEPOINT_MATRIX = new Float32Array([1, 1, 1]);
  var tempNormalizeMatrix = new Float32Array(3);
  var tempConvertMatrix1 = new Float32Array(3);
  var tempConvertMatrix2 = new Float32Array(3);
  var DECODE_L_CONSTANT = Math.pow((8 + 16) / 116, 3) / 8.0;
  function CalRGBCS(whitePoint, blackPoint, gamma, matrix) {
    this.name = 'CalRGB';
    this.numComps = 3;
    this.defaultColor = new Float32Array(this.numComps);
    if (!whitePoint) {
      throw new _util.FormatError('WhitePoint missing - required for color space CalRGB');
    }
    blackPoint = blackPoint || new Float32Array(3);
    gamma = gamma || new Float32Array([1, 1, 1]);
    matrix = matrix || new Float32Array([1, 0, 0, 0, 1, 0, 0, 0, 1]);
    var XW = whitePoint[0];
    var YW = whitePoint[1];
    var ZW = whitePoint[2];
    this.whitePoint = whitePoint;
    var XB = blackPoint[0];
    var YB = blackPoint[1];
    var ZB = blackPoint[2];
    this.blackPoint = blackPoint;
    this.GR = gamma[0];
    this.GG = gamma[1];
    this.GB = gamma[2];
    this.MXA = matrix[0];
    this.MYA = matrix[1];
    this.MZA = matrix[2];
    this.MXB = matrix[3];
    this.MYB = matrix[4];
    this.MZB = matrix[5];
    this.MXC = matrix[6];
    this.MYC = matrix[7];
    this.MZC = matrix[8];
    if (XW < 0 || ZW < 0 || YW !== 1) {
      throw new _util.FormatError(`Invalid WhitePoint components for ${this.name}` + ', no fallback available');
    }
    if (XB < 0 || YB < 0 || ZB < 0) {
      (0, _util.info)('Invalid BlackPoint for ' + this.name + ' [' + XB + ', ' + YB + ', ' + ZB + '], falling back to default');
      this.blackPoint = new Float32Array(3);
    }
    if (this.GR < 0 || this.GG < 0 || this.GB < 0) {
      (0, _util.info)('Invalid Gamma [' + this.GR + ', ' + this.GG + ', ' + this.GB + '] for ' + this.name + ', falling back to default');
      this.GR = this.GG = this.GB = 1;
    }
    if (this.MXA < 0 || this.MYA < 0 || this.MZA < 0 || this.MXB < 0 || this.MYB < 0 || this.MZB < 0 || this.MXC < 0 || this.MYC < 0 || this.MZC < 0) {
      (0, _util.info)('Invalid Matrix for ' + this.name + ' [' + this.MXA + ', ' + this.MYA + ', ' + this.MZA + this.MXB + ', ' + this.MYB + ', ' + this.MZB + this.MXC + ', ' + this.MYC + ', ' + this.MZC + '], falling back to default');
      this.MXA = this.MYB = this.MZC = 1;
      this.MXB = this.MYA = this.MZA = this.MXC = this.MYC = this.MZB = 0;
    }
  }
  function matrixProduct(a, b, result) {
    result[0] = a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
    result[1] = a[3] * b[0] + a[4] * b[1] + a[5] * b[2];
    result[2] = a[6] * b[0] + a[7] * b[1] + a[8] * b[2];
  }
  function convertToFlat(sourceWhitePoint, LMS, result) {
    result[0] = LMS[0] * 1 / sourceWhitePoint[0];
    result[1] = LMS[1] * 1 / sourceWhitePoint[1];
    result[2] = LMS[2] * 1 / sourceWhitePoint[2];
  }
  function convertToD65(sourceWhitePoint, LMS, result) {
    var D65X = 0.95047;
    var D65Y = 1;
    var D65Z = 1.08883;
    result[0] = LMS[0] * D65X / sourceWhitePoint[0];
    result[1] = LMS[1] * D65Y / sourceWhitePoint[1];
    result[2] = LMS[2] * D65Z / sourceWhitePoint[2];
  }
  function sRGBTransferFunction(color) {
    if (color <= 0.0031308) {
      return adjustToRange(0, 1, 12.92 * color);
    }
    return adjustToRange(0, 1, (1 + 0.055) * Math.pow(color, 1 / 2.4) - 0.055);
  }
  function adjustToRange(min, max, value) {
    return Math.max(min, Math.min(max, value));
  }
  function decodeL(L) {
    if (L < 0) {
      return -decodeL(-L);
    }
    if (L > 8.0) {
      return Math.pow((L + 16) / 116, 3);
    }
    return L * DECODE_L_CONSTANT;
  }
  function compensateBlackPoint(sourceBlackPoint, XYZ_Flat, result) {
    if (sourceBlackPoint[0] === 0 && sourceBlackPoint[1] === 0 && sourceBlackPoint[2] === 0) {
      result[0] = XYZ_Flat[0];
      result[1] = XYZ_Flat[1];
      result[2] = XYZ_Flat[2];
      return;
    }
    var zeroDecodeL = decodeL(0);
    var X_DST = zeroDecodeL;
    var X_SRC = decodeL(sourceBlackPoint[0]);
    var Y_DST = zeroDecodeL;
    var Y_SRC = decodeL(sourceBlackPoint[1]);
    var Z_DST = zeroDecodeL;
    var Z_SRC = decodeL(sourceBlackPoint[2]);
    var X_Scale = (1 - X_DST) / (1 - X_SRC);
    var X_Offset = 1 - X_Scale;
    var Y_Scale = (1 - Y_DST) / (1 - Y_SRC);
    var Y_Offset = 1 - Y_Scale;
    var Z_Scale = (1 - Z_DST) / (1 - Z_SRC);
    var Z_Offset = 1 - Z_Scale;
    result[0] = XYZ_Flat[0] * X_Scale + X_Offset;
    result[1] = XYZ_Flat[1] * Y_Scale + Y_Offset;
    result[2] = XYZ_Flat[2] * Z_Scale + Z_Offset;
  }
  function normalizeWhitePointToFlat(sourceWhitePoint, XYZ_In, result) {
    if (sourceWhitePoint[0] === 1 && sourceWhitePoint[2] === 1) {
      result[0] = XYZ_In[0];
      result[1] = XYZ_In[1];
      result[2] = XYZ_In[2];
      return;
    }
    var LMS = result;
    matrixProduct(BRADFORD_SCALE_MATRIX, XYZ_In, LMS);
    var LMS_Flat = tempNormalizeMatrix;
    convertToFlat(sourceWhitePoint, LMS, LMS_Flat);
    matrixProduct(BRADFORD_SCALE_INVERSE_MATRIX, LMS_Flat, result);
  }
  function normalizeWhitePointToD65(sourceWhitePoint, XYZ_In, result) {
    var LMS = result;
    matrixProduct(BRADFORD_SCALE_MATRIX, XYZ_In, LMS);
    var LMS_D65 = tempNormalizeMatrix;
    convertToD65(sourceWhitePoint, LMS, LMS_D65);
    matrixProduct(BRADFORD_SCALE_INVERSE_MATRIX, LMS_D65, result);
  }
  function convertToRgb(cs, src, srcOffset, dest, destOffset, scale) {
    var A = adjustToRange(0, 1, src[srcOffset] * scale);
    var B = adjustToRange(0, 1, src[srcOffset + 1] * scale);
    var C = adjustToRange(0, 1, src[srcOffset + 2] * scale);
    var AGR = Math.pow(A, cs.GR);
    var BGG = Math.pow(B, cs.GG);
    var CGB = Math.pow(C, cs.GB);
    var X = cs.MXA * AGR + cs.MXB * BGG + cs.MXC * CGB;
    var Y = cs.MYA * AGR + cs.MYB * BGG + cs.MYC * CGB;
    var Z = cs.MZA * AGR + cs.MZB * BGG + cs.MZC * CGB;
    var XYZ = tempConvertMatrix1;
    XYZ[0] = X;
    XYZ[1] = Y;
    XYZ[2] = Z;
    var XYZ_Flat = tempConvertMatrix2;
    normalizeWhitePointToFlat(cs.whitePoint, XYZ, XYZ_Flat);
    var XYZ_Black = tempConvertMatrix1;
    compensateBlackPoint(cs.blackPoint, XYZ_Flat, XYZ_Black);
    var XYZ_D65 = tempConvertMatrix2;
    normalizeWhitePointToD65(FLAT_WHITEPOINT_MATRIX, XYZ_Black, XYZ_D65);
    var SRGB = tempConvertMatrix1;
    matrixProduct(SRGB_D65_XYZ_TO_RGB_MATRIX, XYZ_D65, SRGB);
    var sR = sRGBTransferFunction(SRGB[0]);
    var sG = sRGBTransferFunction(SRGB[1]);
    var sB = sRGBTransferFunction(SRGB[2]);
    dest[destOffset] = Math.round(sR * 255);
    dest[destOffset + 1] = Math.round(sG * 255);
    dest[destOffset + 2] = Math.round(sB * 255);
  }
  CalRGBCS.prototype = {
    getRgb: ColorSpace.prototype.getRgb,
    getRgbItem: function CalRGBCS_getRgbItem(src, srcOffset, dest, destOffset) {
      convertToRgb(this, src, srcOffset, dest, destOffset, 1);
    },
    getRgbBuffer: function CalRGBCS_getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) {
      var scale = 1 / ((1 << bits) - 1);
      for (var i = 0; i < count; ++i) {
        convertToRgb(this, src, srcOffset, dest, destOffset, scale);
        srcOffset += 3;
        destOffset += 3 + alpha01;
      }
    },
    getOutputLength: function CalRGBCS_getOutputLength(inputLength, alpha01) {
      return inputLength * (3 + alpha01) / 3 | 0;
    },
    isPassthrough: ColorSpace.prototype.isPassthrough,
    fillRgb: ColorSpace.prototype.fillRgb,
    isDefaultDecode: function CalRGBCS_isDefaultDecode(decodeMap) {
      return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
    },
    usesZeroToOneRange: true
  };
  return CalRGBCS;
}();
var LabCS = function LabCSClosure() {
  function LabCS(whitePoint, blackPoint, range) {
    this.name = 'Lab';
    this.numComps = 3;
    this.defaultColor = new Float32Array(this.numComps);
    if (!whitePoint) {
      throw new _util.FormatError('WhitePoint missing - required for color space Lab');
    }
    blackPoint = blackPoint || [0, 0, 0];
    range = range || [-100, 100, -100, 100];
    this.XW = whitePoint[0];
    this.YW = whitePoint[1];
    this.ZW = whitePoint[2];
    this.amin = range[0];
    this.amax = range[1];
    this.bmin = range[2];
    this.bmax = range[3];
    this.XB = blackPoint[0];
    this.YB = blackPoint[1];
    this.ZB = blackPoint[2];
    if (this.XW < 0 || this.ZW < 0 || this.YW !== 1) {
      throw new _util.FormatError('Invalid WhitePoint components, no fallback available');
    }
    if (this.XB < 0 || this.YB < 0 || this.ZB < 0) {
      (0, _util.info)('Invalid BlackPoint, falling back to default');
      this.XB = this.YB = this.ZB = 0;
    }
    if (this.amin > this.amax || this.bmin > this.bmax) {
      (0, _util.info)('Invalid Range, falling back to defaults');
      this.amin = -100;
      this.amax = 100;
      this.bmin = -100;
      this.bmax = 100;
    }
  }
  function fn_g(x) {
    var result;
    if (x >= 6 / 29) {
      result = x * x * x;
    } else {
      result = 108 / 841 * (x - 4 / 29);
    }
    return result;
  }
  function decode(value, high1, low2, high2) {
    return low2 + value * (high2 - low2) / high1;
  }
  function convertToRgb(cs, src, srcOffset, maxVal, dest, destOffset) {
    var Ls = src[srcOffset];
    var as = src[srcOffset + 1];
    var bs = src[srcOffset + 2];
    if (maxVal !== false) {
      Ls = decode(Ls, maxVal, 0, 100);
      as = decode(as, maxVal, cs.amin, cs.amax);
      bs = decode(bs, maxVal, cs.bmin, cs.bmax);
    }
    as = as > cs.amax ? cs.amax : as < cs.amin ? cs.amin : as;
    bs = bs > cs.bmax ? cs.bmax : bs < cs.bmin ? cs.bmin : bs;
    var M = (Ls + 16) / 116;
    var L = M + as / 500;
    var N = M - bs / 200;
    var X = cs.XW * fn_g(L);
    var Y = cs.YW * fn_g(M);
    var Z = cs.ZW * fn_g(N);
    var r, g, b;
    if (cs.ZW < 1) {
      r = X * 3.1339 + Y * -1.6170 + Z * -0.4906;
      g = X * -0.9785 + Y * 1.9160 + Z * 0.0333;
      b = X * 0.0720 + Y * -0.2290 + Z * 1.4057;
    } else {
      r = X * 3.2406 + Y * -1.5372 + Z * -0.4986;
      g = X * -0.9689 + Y * 1.8758 + Z * 0.0415;
      b = X * 0.0557 + Y * -0.2040 + Z * 1.0570;
    }
    dest[destOffset] = r <= 0 ? 0 : r >= 1 ? 255 : Math.sqrt(r) * 255 | 0;
    dest[destOffset + 1] = g <= 0 ? 0 : g >= 1 ? 255 : Math.sqrt(g) * 255 | 0;
    dest[destOffset + 2] = b <= 0 ? 0 : b >= 1 ? 255 : Math.sqrt(b) * 255 | 0;
  }
  LabCS.prototype = {
    getRgb: ColorSpace.prototype.getRgb,
    getRgbItem: function LabCS_getRgbItem(src, srcOffset, dest, destOffset) {
      convertToRgb(this, src, srcOffset, false, dest, destOffset);
    },
    getRgbBuffer: function LabCS_getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) {
      var maxVal = (1 << bits) - 1;
      for (var i = 0; i < count; i++) {
        convertToRgb(this, src, srcOffset, maxVal, dest, destOffset);
        srcOffset += 3;
        destOffset += 3 + alpha01;
      }
    },
    getOutputLength: function LabCS_getOutputLength(inputLength, alpha01) {
      return inputLength * (3 + alpha01) / 3 | 0;
    },
    isPassthrough: ColorSpace.prototype.isPassthrough,
    fillRgb: ColorSpace.prototype.fillRgb,
    isDefaultDecode: function LabCS_isDefaultDecode(decodeMap) {
      return true;
    },
    usesZeroToOneRange: false
  };
  return LabCS;
}();
exports.ColorSpace = ColorSpace;

/***/ }),
/* 4 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
const ExpertEncoding = ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 'space', 'exclamsmall', 'Hungarumlautsmall', '', 'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall', 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader', 'comma', 'hyphen', 'period', 'fraction', 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle', 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle', 'nineoldstyle', 'colon', 'semicolon', 'commasuperior', 'threequartersemdash', 'periodsuperior', 'questionsmall', '', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior', 'esuperior', '', '', '', 'isuperior', '', '', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior', '', '', 'rsuperior', 'ssuperior', 'tsuperior', '', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'parenleftinferior', '', 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall', 'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall', 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall', 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall', 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 'exclamdownsmall', 'centoldstyle', 'Lslashsmall', '', '', 'Scaronsmall', 'Zcaronsmall', 'Dieresissmall', 'Brevesmall', 'Caronsmall', '', 'Dotaccentsmall', '', '', 'Macronsmall', '', '', 'figuredash', 'hypheninferior', '', '', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall', '', '', '', 'onequarter', 'onehalf', 'threequarters', 'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths', 'onethird', 'twothirds', '', '', 'zerosuperior', 'onesuperior', 'twosuperior', 'threesuperior', 'foursuperior', 'fivesuperior', 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior', 'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior', 'sixinferior', 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior', 'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall', 'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall', 'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall', 'Edieresissmall', 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall', 'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall', 'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall', 'Ugravesmall', 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall', 'Yacutesmall', 'Thornsmall', 'Ydieresissmall'];
const MacExpertEncoding = ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 'space', 'exclamsmall', 'Hungarumlautsmall', 'centoldstyle', 'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall', 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader', 'comma', 'hyphen', 'period', 'fraction', 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle', 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle', 'nineoldstyle', 'colon', 'semicolon', '', 'threequartersemdash', '', 'questionsmall', '', '', '', '', 'Ethsmall', '', '', 'onequarter', 'onehalf', 'threequarters', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths', 'onethird', 'twothirds', '', '', '', '', '', '', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'parenleftinferior', '', 'parenrightinferior', 'Circumflexsmall', 'hypheninferior', 'Gravesmall', 'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall', 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall', 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall', 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall', '', '', 'asuperior', 'centsuperior', '', '', '', '', 'Aacutesmall', 'Agravesmall', 'Acircumflexsmall', 'Adieresissmall', 'Atildesmall', 'Aringsmall', 'Ccedillasmall', 'Eacutesmall', 'Egravesmall', 'Ecircumflexsmall', 'Edieresissmall', 'Iacutesmall', 'Igravesmall', 'Icircumflexsmall', 'Idieresissmall', 'Ntildesmall', 'Oacutesmall', 'Ogravesmall', 'Ocircumflexsmall', 'Odieresissmall', 'Otildesmall', 'Uacutesmall', 'Ugravesmall', 'Ucircumflexsmall', 'Udieresissmall', '', 'eightsuperior', 'fourinferior', 'threeinferior', 'sixinferior', 'eightinferior', 'seveninferior', 'Scaronsmall', '', 'centinferior', 'twoinferior', '', 'Dieresissmall', '', 'Caronsmall', 'osuperior', 'fiveinferior', '', 'commainferior', 'periodinferior', 'Yacutesmall', '', 'dollarinferior', '', '', 'Thornsmall', '', 'nineinferior', 'zeroinferior', 'Zcaronsmall', 'AEsmall', 'Oslashsmall', 'questiondownsmall', 'oneinferior', 'Lslashsmall', '', '', '', '', '', '', 'Cedillasmall', '', '', '', '', '', 'OEsmall', 'figuredash', 'hyphensuperior', '', '', '', '', 'exclamdownsmall', '', 'Ydieresissmall', '', 'onesuperior', 'twosuperior', 'threesuperior', 'foursuperior', 'fivesuperior', 'sixsuperior', 'sevensuperior', 'ninesuperior', 'zerosuperior', '', 'esuperior', 'rsuperior', 'tsuperior', '', '', 'isuperior', 'ssuperior', 'dsuperior', '', '', '', '', '', 'lsuperior', 'Ogoneksmall', 'Brevesmall', 'Macronsmall', 'bsuperior', 'nsuperior', 'msuperior', 'commasuperior', 'periodsuperior', 'Dotaccentsmall', 'Ringsmall', '', '', '', ''];
const MacRomanEncoding = ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent', 'ampersand', 'quotesingle', 'parenleft', 'parenright', 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash', 'bracketright', 'asciicircum', 'underscore', 'grave', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright', 'asciitilde', '', 'Adieresis', 'Aring', 'Ccedilla', 'Eacute', 'Ntilde', 'Odieresis', 'Udieresis', 'aacute', 'agrave', 'acircumflex', 'adieresis', 'atilde', 'aring', 'ccedilla', 'eacute', 'egrave', 'ecircumflex', 'edieresis', 'iacute', 'igrave', 'icircumflex', 'idieresis', 'ntilde', 'oacute', 'ograve', 'ocircumflex', 'odieresis', 'otilde', 'uacute', 'ugrave', 'ucircumflex', 'udieresis', 'dagger', 'degree', 'cent', 'sterling', 'section', 'bullet', 'paragraph', 'germandbls', 'registered', 'copyright', 'trademark', 'acute', 'dieresis', 'notequal', 'AE', 'Oslash', 'infinity', 'plusminus', 'lessequal', 'greaterequal', 'yen', 'mu', 'partialdiff', 'summation', 'product', 'pi', 'integral', 'ordfeminine', 'ordmasculine', 'Omega', 'ae', 'oslash', 'questiondown', 'exclamdown', 'logicalnot', 'radical', 'florin', 'approxequal', 'Delta', 'guillemotleft', 'guillemotright', 'ellipsis', 'space', 'Agrave', 'Atilde', 'Otilde', 'OE', 'oe', 'endash', 'emdash', 'quotedblleft', 'quotedblright', 'quoteleft', 'quoteright', 'divide', 'lozenge', 'ydieresis', 'Ydieresis', 'fraction', 'currency', 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'daggerdbl', 'periodcentered', 'quotesinglbase', 'quotedblbase', 'perthousand', 'Acircumflex', 'Ecircumflex', 'Aacute', 'Edieresis', 'Egrave', 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Oacute', 'Ocircumflex', 'apple', 'Ograve', 'Uacute', 'Ucircumflex', 'Ugrave', 'dotlessi', 'circumflex', 'tilde', 'macron', 'breve', 'dotaccent', 'ring', 'cedilla', 'hungarumlaut', 'ogonek', 'caron'];
const StandardEncoding = ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright', 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash', 'bracketright', 'asciicircum', 'underscore', 'quoteleft', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright', 'asciitilde', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin', 'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft', 'guilsinglleft', 'guilsinglright', 'fi', 'fl', '', 'endash', 'dagger', 'daggerdbl', 'periodcentered', '', 'paragraph', 'bullet', 'quotesinglbase', 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand', '', 'questiondown', '', 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve', 'dotaccent', 'dieresis', '', 'ring', 'cedilla', '', 'hungarumlaut', 'ogonek', 'caron', 'emdash', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 'AE', '', 'ordfeminine', '', '', '', '', 'Lslash', 'Oslash', 'OE', 'ordmasculine', '', '', '', '', '', 'ae', '', '', '', 'dotlessi', '', '', 'lslash', 'oslash', 'oe', 'germandbls', '', '', '', ''];
const WinAnsiEncoding = ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent', 'ampersand', 'quotesingle', 'parenleft', 'parenright', 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash', 'bracketright', 'asciicircum', 'underscore', 'grave', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright', 'asciitilde', 'bullet', 'Euro', 'bullet', 'quotesinglbase', 'florin', 'quotedblbase', 'ellipsis', 'dagger', 'daggerdbl', 'circumflex', 'perthousand', 'Scaron', 'guilsinglleft', 'OE', 'bullet', 'Zcaron', 'bullet', 'bullet', 'quoteleft', 'quoteright', 'quotedblleft', 'quotedblright', 'bullet', 'endash', 'emdash', 'tilde', 'trademark', 'scaron', 'guilsinglright', 'oe', 'bullet', 'zcaron', 'Ydieresis', 'space', 'exclamdown', 'cent', 'sterling', 'currency', 'yen', 'brokenbar', 'section', 'dieresis', 'copyright', 'ordfeminine', 'guillemotleft', 'logicalnot', 'hyphen', 'registered', 'macron', 'degree', 'plusminus', 'twosuperior', 'threesuperior', 'acute', 'mu', 'paragraph', 'periodcentered', 'cedilla', 'onesuperior', 'ordmasculine', 'guillemotright', 'onequarter', 'onehalf', 'threequarters', 'questiondown', 'Agrave', 'Aacute', 'Acircumflex', 'Atilde', 'Adieresis', 'Aring', 'AE', 'Ccedilla', 'Egrave', 'Eacute', 'Ecircumflex', 'Edieresis', 'Igrave', 'Iacute', 'Icircumflex', 'Idieresis', 'Eth', 'Ntilde', 'Ograve', 'Oacute', 'Ocircumflex', 'Otilde', 'Odieresis', 'multiply', 'Oslash', 'Ugrave', 'Uacute', 'Ucircumflex', 'Udieresis', 'Yacute', 'Thorn', 'germandbls', 'agrave', 'aacute', 'acircumflex', 'atilde', 'adieresis', 'aring', 'ae', 'ccedilla', 'egrave', 'eacute', 'ecircumflex', 'edieresis', 'igrave', 'iacute', 'icircumflex', 'idieresis', 'eth', 'ntilde', 'ograve', 'oacute', 'ocircumflex', 'otilde', 'odieresis', 'divide', 'oslash', 'ugrave', 'uacute', 'ucircumflex', 'udieresis', 'yacute', 'thorn', 'ydieresis'];
const SymbolSetEncoding = ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 'space', 'exclam', 'universal', 'numbersign', 'existential', 'percent', 'ampersand', 'suchthat', 'parenleft', 'parenright', 'asteriskmath', 'plus', 'comma', 'minus', 'period', 'slash', 'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', 'less', 'equal', 'greater', 'question', 'congruent', 'Alpha', 'Beta', 'Chi', 'Delta', 'Epsilon', 'Phi', 'Gamma', 'Eta', 'Iota', 'theta1', 'Kappa', 'Lambda', 'Mu', 'Nu', 'Omicron', 'Pi', 'Theta', 'Rho', 'Sigma', 'Tau', 'Upsilon', 'sigma1', 'Omega', 'Xi', 'Psi', 'Zeta', 'bracketleft', 'therefore', 'bracketright', 'perpendicular', 'underscore', 'radicalex', 'alpha', 'beta', 'chi', 'delta', 'epsilon', 'phi', 'gamma', 'eta', 'iota', 'phi1', 'kappa', 'lambda', 'mu', 'nu', 'omicron', 'pi', 'theta', 'rho', 'sigma', 'tau', 'upsilon', 'omega1', 'omega', 'xi', 'psi', 'zeta', 'braceleft', 'bar', 'braceright', 'similar', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 'Euro', 'Upsilon1', 'minute', 'lessequal', 'fraction', 'infinity', 'florin', 'club', 'diamond', 'heart', 'spade', 'arrowboth', 'arrowleft', 'arrowup', 'arrowright', 'arrowdown', 'degree', 'plusminus', 'second', 'greaterequal', 'multiply', 'proportional', 'partialdiff', 'bullet', 'divide', 'notequal', 'equivalence', 'approxequal', 'ellipsis', 'arrowvertex', 'arrowhorizex', 'carriagereturn', 'aleph', 'Ifraktur', 'Rfraktur', 'weierstrass', 'circlemultiply', 'circleplus', 'emptyset', 'intersection', 'union', 'propersuperset', 'reflexsuperset', 'notsubset', 'propersubset', 'reflexsubset', 'element', 'notelement', 'angle', 'gradient', 'registerserif', 'copyrightserif', 'trademarkserif', 'product', 'radical', 'dotmath', 'logicalnot', 'logicaland', 'logicalor', 'arrowdblboth', 'arrowdblleft', 'arrowdblup', 'arrowdblright', 'arrowdbldown', 'lozenge', 'angleleft', 'registersans', 'copyrightsans', 'trademarksans', 'summation', 'parenlefttp', 'parenleftex', 'parenleftbt', 'bracketlefttp', 'bracketleftex', 'bracketleftbt', 'bracelefttp', 'braceleftmid', 'braceleftbt', 'braceex', '', 'angleright', 'integral', 'integraltp', 'integralex', 'integralbt', 'parenrighttp', 'parenrightex', 'parenrightbt', 'bracketrighttp', 'bracketrightex', 'bracketrightbt', 'bracerighttp', 'bracerightmid', 'bracerightbt', ''];
const ZapfDingbatsEncoding = ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 'space', 'a1', 'a2', 'a202', 'a3', 'a4', 'a5', 'a119', 'a118', 'a117', 'a11', 'a12', 'a13', 'a14', 'a15', 'a16', 'a105', 'a17', 'a18', 'a19', 'a20', 'a21', 'a22', 'a23', 'a24', 'a25', 'a26', 'a27', 'a28', 'a6', 'a7', 'a8', 'a9', 'a10', 'a29', 'a30', 'a31', 'a32', 'a33', 'a34', 'a35', 'a36', 'a37', 'a38', 'a39', 'a40', 'a41', 'a42', 'a43', 'a44', 'a45', 'a46', 'a47', 'a48', 'a49', 'a50', 'a51', 'a52', 'a53', 'a54', 'a55', 'a56', 'a57', 'a58', 'a59', 'a60', 'a61', 'a62', 'a63', 'a64', 'a65', 'a66', 'a67', 'a68', 'a69', 'a70', 'a71', 'a72', 'a73', 'a74', 'a203', 'a75', 'a204', 'a76', 'a77', 'a78', 'a79', 'a81', 'a82', 'a83', 'a84', 'a97', 'a98', 'a99', 'a100', '', 'a89', 'a90', 'a93', 'a94', 'a91', 'a92', 'a205', 'a85', 'a206', 'a86', 'a87', 'a88', 'a95', 'a96', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 'a101', 'a102', 'a103', 'a104', 'a106', 'a107', 'a108', 'a112', 'a111', 'a110', 'a109', 'a120', 'a121', 'a122', 'a123', 'a124', 'a125', 'a126', 'a127', 'a128', 'a129', 'a130', 'a131', 'a132', 'a133', 'a134', 'a135', 'a136', 'a137', 'a138', 'a139', 'a140', 'a141', 'a142', 'a143', 'a144', 'a145', 'a146', 'a147', 'a148', 'a149', 'a150', 'a151', 'a152', 'a153', 'a154', 'a155', 'a156', 'a157', 'a158', 'a159', 'a160', 'a161', 'a163', 'a164', 'a196', 'a165', 'a192', 'a166', 'a167', 'a168', 'a169', 'a170', 'a171', 'a172', 'a173', 'a162', 'a174', 'a175', 'a176', 'a177', 'a178', 'a179', 'a193', 'a180', 'a199', 'a181', 'a200', 'a182', '', 'a201', 'a183', 'a184', 'a197', 'a185', 'a194', 'a198', 'a186', 'a195', 'a187', 'a188', 'a189', 'a190', 'a191', ''];
function getEncoding(encodingName) {
  switch (encodingName) {
    case 'WinAnsiEncoding':
      return WinAnsiEncoding;
    case 'StandardEncoding':
      return StandardEncoding;
    case 'MacRomanEncoding':
      return MacRomanEncoding;
    case 'SymbolSetEncoding':
      return SymbolSetEncoding;
    case 'ZapfDingbatsEncoding':
      return ZapfDingbatsEncoding;
    case 'ExpertEncoding':
      return ExpertEncoding;
    case 'MacExpertEncoding':
      return MacExpertEncoding;
    default:
      return null;
  }
}
exports.WinAnsiEncoding = WinAnsiEncoding;
exports.StandardEncoding = StandardEncoding;
exports.MacRomanEncoding = MacRomanEncoding;
exports.SymbolSetEncoding = SymbolSetEncoding;
exports.ZapfDingbatsEncoding = ZapfDingbatsEncoding;
exports.ExpertEncoding = ExpertEncoding;
exports.getEncoding = getEncoding;

/***/ }),
/* 5 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.Parser = exports.Linearization = exports.Lexer = undefined;

var _stream = __w_pdfjs_require__(2);

var _util = __w_pdfjs_require__(0);

var _primitives = __w_pdfjs_require__(1);

var _ccitt_stream = __w_pdfjs_require__(26);

var _jbig2_stream = __w_pdfjs_require__(27);

var _jpeg_stream = __w_pdfjs_require__(6);

var _jpx_stream = __w_pdfjs_require__(30);

const MAX_LENGTH_TO_CACHE = 1000;
const MAX_ADLER32_LENGTH = 5552;
function computeAdler32(bytes) {
  let bytesLength = bytes.length;
  if (bytesLength >= MAX_ADLER32_LENGTH) {
    throw new Error('computeAdler32: The input is too large.');
  }
  let a = 1,
      b = 0;
  for (let i = 0; i < bytesLength; ++i) {
    a += bytes[i] & 0xFF;
    b += a;
  }
  return b % 65521 << 16 | a % 65521;
}
var Parser = function ParserClosure() {
  function Parser(lexer, allowStreams, xref, recoveryMode) {
    this.lexer = lexer;
    this.allowStreams = allowStreams;
    this.xref = xref;
    this.recoveryMode = recoveryMode || false;
    this.imageCache = Object.create(null);
    this.refill();
  }
  Parser.prototype = {
    refill: function Parser_refill() {
      this.buf1 = this.lexer.getObj();
      this.buf2 = this.lexer.getObj();
    },
    shift: function Parser_shift() {
      if ((0, _primitives.isCmd)(this.buf2, 'ID')) {
        this.buf1 = this.buf2;
        this.buf2 = null;
      } else {
        this.buf1 = this.buf2;
        this.buf2 = this.lexer.getObj();
      }
    },
    tryShift: function Parser_tryShift() {
      try {
        this.shift();
        return true;
      } catch (e) {
        if (e instanceof _util.MissingDataException) {
          throw e;
        }
        return false;
      }
    },
    getObj: function Parser_getObj(cipherTransform) {
      var buf1 = this.buf1;
      this.shift();
      if (buf1 instanceof _primitives.Cmd) {
        switch (buf1.cmd) {
          case 'BI':
            return this.makeInlineImage(cipherTransform);
          case '[':
            var array = [];
            while (!(0, _primitives.isCmd)(this.buf1, ']') && !(0, _primitives.isEOF)(this.buf1)) {
              array.push(this.getObj(cipherTransform));
            }
            if ((0, _primitives.isEOF)(this.buf1)) {
              if (!this.recoveryMode) {
                throw new _util.FormatError('End of file inside array');
              }
              return array;
            }
            this.shift();
            return array;
          case '<<':
            var dict = new _primitives.Dict(this.xref);
            while (!(0, _primitives.isCmd)(this.buf1, '>>') && !(0, _primitives.isEOF)(this.buf1)) {
              if (!(0, _primitives.isName)(this.buf1)) {
                (0, _util.info)('Malformed dictionary: key must be a name object');
                this.shift();
                continue;
              }
              var key = this.buf1.name;
              this.shift();
              if ((0, _primitives.isEOF)(this.buf1)) {
                break;
              }
              dict.set(key, this.getObj(cipherTransform));
            }
            if ((0, _primitives.isEOF)(this.buf1)) {
              if (!this.recoveryMode) {
                throw new _util.FormatError('End of file inside dictionary');
              }
              return dict;
            }
            if ((0, _primitives.isCmd)(this.buf2, 'stream')) {
              return this.allowStreams ? this.makeStream(dict, cipherTransform) : dict;
            }
            this.shift();
            return dict;
          default:
            return buf1;
        }
      }
      if (Number.isInteger(buf1)) {
        var num = buf1;
        if (Number.isInteger(this.buf1) && (0, _primitives.isCmd)(this.buf2, 'R')) {
          var ref = new _primitives.Ref(num, this.buf1);
          this.shift();
          this.shift();
          return ref;
        }
        return num;
      }
      if ((0, _util.isString)(buf1)) {
        var str = buf1;
        if (cipherTransform) {
          str = cipherTransform.decryptString(str);
        }
        return str;
      }
      return buf1;
    },
    findDefaultInlineStreamEnd(stream) {
      const E = 0x45,
            I = 0x49,
            SPACE = 0x20,
            LF = 0xA,
            CR = 0xD;
      const n = 10,
            NUL = 0x0;
      let startPos = stream.pos,
          state = 0,
          ch,
          maybeEIPos;
      while ((ch = stream.getByte()) !== -1) {
        if (state === 0) {
          state = ch === E ? 1 : 0;
        } else if (state === 1) {
          state = ch === I ? 2 : 0;
        } else {
          (0, _util.assert)(state === 2);
          if (ch === SPACE || ch === LF || ch === CR) {
            maybeEIPos = stream.pos;
            let followingBytes = stream.peekBytes(n);
            for (let i = 0, ii = followingBytes.length; i < ii; i++) {
              ch = followingBytes[i];
              if (ch === NUL && followingBytes[i + 1] !== NUL) {
                continue;
              }
              if (ch !== LF && ch !== CR && (ch < SPACE || ch > 0x7F)) {
                state = 0;
                break;
              }
            }
            if (state === 2) {
              break;
            }
          } else {
            state = 0;
          }
        }
      }
      if (ch === -1) {
        (0, _util.warn)('findDefaultInlineStreamEnd: ' + 'Reached the end of the stream without finding a valid EI marker');
        if (maybeEIPos) {
          (0, _util.warn)('... trying to recover by using the last "EI" occurrence.');
          stream.skip(-(stream.pos - maybeEIPos));
        }
      }
      return stream.pos - 4 - startPos;
    },
    findDCTDecodeInlineStreamEnd: function Parser_findDCTDecodeInlineStreamEnd(stream) {
      var startPos = stream.pos,
          foundEOI = false,
          b,
          markerLength,
          length;
      while ((b = stream.getByte()) !== -1) {
        if (b !== 0xFF) {
          continue;
        }
        switch (stream.getByte()) {
          case 0x00:
            break;
          case 0xFF:
            stream.skip(-1);
            break;
          case 0xD9:
            foundEOI = true;
            break;
          case 0xC0:
          case 0xC1:
          case 0xC2:
          case 0xC3:
          case 0xC5:
          case 0xC6:
          case 0xC7:
          case 0xC9:
          case 0xCA:
          case 0xCB:
          case 0xCD:
          case 0xCE:
          case 0xCF:
          case 0xC4:
          case 0xCC:
          case 0xDA:
          case 0xDB:
          case 0xDC:
          case 0xDD:
          case 0xDE:
          case 0xDF:
          case 0xE0:
          case 0xE1:
          case 0xE2:
          case 0xE3:
          case 0xE4:
          case 0xE5:
          case 0xE6:
          case 0xE7:
          case 0xE8:
          case 0xE9:
          case 0xEA:
          case 0xEB:
          case 0xEC:
          case 0xED:
          case 0xEE:
          case 0xEF:
          case 0xFE:
            markerLength = stream.getUint16();
            if (markerLength > 2) {
              stream.skip(markerLength - 2);
            } else {
              stream.skip(-2);
            }
            break;
        }
        if (foundEOI) {
          break;
        }
      }
      length = stream.pos - startPos;
      if (b === -1) {
        (0, _util.warn)('Inline DCTDecode image stream: ' + 'EOI marker not found, searching for /EI/ instead.');
        stream.skip(-length);
        return this.findDefaultInlineStreamEnd(stream);
      }
      this.inlineStreamSkipEI(stream);
      return length;
    },
    findASCII85DecodeInlineStreamEnd: function Parser_findASCII85DecodeInlineStreamEnd(stream) {
      var TILDE = 0x7E,
          GT = 0x3E;
      var startPos = stream.pos,
          ch,
          length;
      while ((ch = stream.getByte()) !== -1) {
        if (ch === TILDE && stream.peekByte() === GT) {
          stream.skip();
          break;
        }
      }
      length = stream.pos - startPos;
      if (ch === -1) {
        (0, _util.warn)('Inline ASCII85Decode image stream: ' + 'EOD marker not found, searching for /EI/ instead.');
        stream.skip(-length);
        return this.findDefaultInlineStreamEnd(stream);
      }
      this.inlineStreamSkipEI(stream);
      return length;
    },
    findASCIIHexDecodeInlineStreamEnd: function Parser_findASCIIHexDecodeInlineStreamEnd(stream) {
      var GT = 0x3E;
      var startPos = stream.pos,
          ch,
          length;
      while ((ch = stream.getByte()) !== -1) {
        if (ch === GT) {
          break;
        }
      }
      length = stream.pos - startPos;
      if (ch === -1) {
        (0, _util.warn)('Inline ASCIIHexDecode image stream: ' + 'EOD marker not found, searching for /EI/ instead.');
        stream.skip(-length);
        return this.findDefaultInlineStreamEnd(stream);
      }
      this.inlineStreamSkipEI(stream);
      return length;
    },
    inlineStreamSkipEI: function Parser_inlineStreamSkipEI(stream) {
      var E = 0x45,
          I = 0x49;
      var state = 0,
          ch;
      while ((ch = stream.getByte()) !== -1) {
        if (state === 0) {
          state = ch === E ? 1 : 0;
        } else if (state === 1) {
          state = ch === I ? 2 : 0;
        } else if (state === 2) {
          break;
        }
      }
    },
    makeInlineImage: function Parser_makeInlineImage(cipherTransform) {
      var lexer = this.lexer;
      var stream = lexer.stream;
      let dict = new _primitives.Dict(this.xref),
          dictLength;
      while (!(0, _primitives.isCmd)(this.buf1, 'ID') && !(0, _primitives.isEOF)(this.buf1)) {
        if (!(0, _primitives.isName)(this.buf1)) {
          throw new _util.FormatError('Dictionary key must be a name object');
        }
        var key = this.buf1.name;
        this.shift();
        if ((0, _primitives.isEOF)(this.buf1)) {
          break;
        }
        dict.set(key, this.getObj(cipherTransform));
      }
      if (lexer.beginInlineImagePos !== -1) {
        dictLength = stream.pos - lexer.beginInlineImagePos;
      }
      var filter = dict.get('Filter', 'F'),
          filterName;
      if ((0, _primitives.isName)(filter)) {
        filterName = filter.name;
      } else if (Array.isArray(filter)) {
        var filterZero = this.xref.fetchIfRef(filter[0]);
        if ((0, _primitives.isName)(filterZero)) {
          filterName = filterZero.name;
        }
      }
      let startPos = stream.pos,
          length;
      if (filterName === 'DCTDecode' || filterName === 'DCT') {
        length = this.findDCTDecodeInlineStreamEnd(stream);
      } else if (filterName === 'ASCII85Decode' || filterName === 'A85') {
        length = this.findASCII85DecodeInlineStreamEnd(stream);
      } else if (filterName === 'ASCIIHexDecode' || filterName === 'AHx') {
        length = this.findASCIIHexDecodeInlineStreamEnd(stream);
      } else {
        length = this.findDefaultInlineStreamEnd(stream);
      }
      var imageStream = stream.makeSubStream(startPos, length, dict);
      let cacheKey;
      if (length < MAX_LENGTH_TO_CACHE && dictLength < MAX_ADLER32_LENGTH) {
        var imageBytes = imageStream.getBytes();
        imageStream.reset();
        const initialStreamPos = stream.pos;
        stream.pos = lexer.beginInlineImagePos;
        let dictBytes = stream.getBytes(dictLength);
        stream.pos = initialStreamPos;
        cacheKey = computeAdler32(imageBytes) + '_' + computeAdler32(dictBytes);
        let cacheEntry = this.imageCache[cacheKey];
        if (cacheEntry !== undefined) {
          this.buf2 = _primitives.Cmd.get('EI');
          this.shift();
          cacheEntry.reset();
          return cacheEntry;
        }
      }
      if (cipherTransform) {
        imageStream = cipherTransform.createStream(imageStream, length);
      }
      imageStream = this.filter(imageStream, dict, length);
      imageStream.dict = dict;
      if (cacheKey !== undefined) {
        imageStream.cacheKey = 'inline_' + length + '_' + cacheKey;
        this.imageCache[cacheKey] = imageStream;
      }
      this.buf2 = _primitives.Cmd.get('EI');
      this.shift();
      return imageStream;
    },
    makeStream: function Parser_makeStream(dict, cipherTransform) {
      var lexer = this.lexer;
      var stream = lexer.stream;
      lexer.skipToNextLine();
      var pos = stream.pos - 1;
      var length = dict.get('Length');
      if (!Number.isInteger(length)) {
        (0, _util.info)('Bad ' + length + ' attribute in stream');
        length = 0;
      }
      stream.pos = pos + length;
      lexer.nextChar();
      if (this.tryShift() && (0, _primitives.isCmd)(this.buf2, 'endstream')) {
        this.shift();
      } else {
        stream.pos = pos;
        var SCAN_BLOCK_SIZE = 2048;
        var ENDSTREAM_SIGNATURE_LENGTH = 9;
        var ENDSTREAM_SIGNATURE = [0x65, 0x6E, 0x64, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6D];
        var skipped = 0,
            found = false,
            i,
            j;
        while (stream.pos < stream.end) {
          var scanBytes = stream.peekBytes(SCAN_BLOCK_SIZE);
          var scanLength = scanBytes.length - ENDSTREAM_SIGNATURE_LENGTH;
          if (scanLength <= 0) {
            break;
          }
          found = false;
          i = 0;
          while (i < scanLength) {
            j = 0;
            while (j < ENDSTREAM_SIGNATURE_LENGTH && scanBytes[i + j] === ENDSTREAM_SIGNATURE[j]) {
              j++;
            }
            if (j >= ENDSTREAM_SIGNATURE_LENGTH) {
              found = true;
              break;
            }
            i++;
          }
          if (found) {
            skipped += i;
            stream.pos += i;
            break;
          }
          skipped += scanLength;
          stream.pos += scanLength;
        }
        if (!found) {
          throw new _util.FormatError('Missing endstream');
        }
        length = skipped;
        lexer.nextChar();
        this.shift();
        this.shift();
      }
      this.shift();
      stream = stream.makeSubStream(pos, length, dict);
      if (cipherTransform) {
        stream = cipherTransform.createStream(stream, length);
      }
      stream = this.filter(stream, dict, length);
      stream.dict = dict;
      return stream;
    },
    filter: function Parser_filter(stream, dict, length) {
      var filter = dict.get('Filter', 'F');
      var params = dict.get('DecodeParms', 'DP');
      if ((0, _primitives.isName)(filter)) {
        if (Array.isArray(params)) {
          (0, _util.warn)('/DecodeParms should not contain an Array, ' + 'when /Filter contains a Name.');
        }
        return this.makeFilter(stream, filter.name, length, params);
      }
      var maybeLength = length;
      if (Array.isArray(filter)) {
        var filterArray = filter;
        var paramsArray = params;
        for (var i = 0, ii = filterArray.length; i < ii; ++i) {
          filter = this.xref.fetchIfRef(filterArray[i]);
          if (!(0, _primitives.isName)(filter)) {
            throw new _util.FormatError('Bad filter name: ' + filter);
          }
          params = null;
          if (Array.isArray(paramsArray) && i in paramsArray) {
            params = this.xref.fetchIfRef(paramsArray[i]);
          }
          stream = this.makeFilter(stream, filter.name, maybeLength, params);
          maybeLength = null;
        }
      }
      return stream;
    },
    makeFilter: function Parser_makeFilter(stream, name, maybeLength, params) {
      if (maybeLength === 0) {
        (0, _util.warn)('Empty "' + name + '" stream.');
        return new _stream.NullStream();
      }
      try {
        var xrefStreamStats = this.xref.stats.streamTypes;
        if (name === 'FlateDecode' || name === 'Fl') {
          xrefStreamStats[_util.StreamType.FLATE] = true;
          if (params) {
            return new _stream.PredictorStream(new _stream.FlateStream(stream, maybeLength), maybeLength, params);
          }
          return new _stream.FlateStream(stream, maybeLength);
        }
        if (name === 'LZWDecode' || name === 'LZW') {
          xrefStreamStats[_util.StreamType.LZW] = true;
          var earlyChange = 1;
          if (params) {
            if (params.has('EarlyChange')) {
              earlyChange = params.get('EarlyChange');
            }
            return new _stream.PredictorStream(new _stream.LZWStream(stream, maybeLength, earlyChange), maybeLength, params);
          }
          return new _stream.LZWStream(stream, maybeLength, earlyChange);
        }
        if (name === 'DCTDecode' || name === 'DCT') {
          xrefStreamStats[_util.StreamType.DCT] = true;
          return new _jpeg_stream.JpegStream(stream, maybeLength, stream.dict, params);
        }
        if (name === 'JPXDecode' || name === 'JPX') {
          xrefStreamStats[_util.StreamType.JPX] = true;
          return new _jpx_stream.JpxStream(stream, maybeLength, stream.dict, params);
        }
        if (name === 'ASCII85Decode' || name === 'A85') {
          xrefStreamStats[_util.StreamType.A85] = true;
          return new _stream.Ascii85Stream(stream, maybeLength);
        }
        if (name === 'ASCIIHexDecode' || name === 'AHx') {
          xrefStreamStats[_util.StreamType.AHX] = true;
          return new _stream.AsciiHexStream(stream, maybeLength);
        }
        if (name === 'CCITTFaxDecode' || name === 'CCF') {
          xrefStreamStats[_util.StreamType.CCF] = true;
          return new _ccitt_stream.CCITTFaxStream(stream, maybeLength, params);
        }
        if (name === 'RunLengthDecode' || name === 'RL') {
          xrefStreamStats[_util.StreamType.RL] = true;
          return new _stream.RunLengthStream(stream, maybeLength);
        }
        if (name === 'JBIG2Decode') {
          xrefStreamStats[_util.StreamType.JBIG] = true;
          return new _jbig2_stream.Jbig2Stream(stream, maybeLength, stream.dict, params);
        }
        (0, _util.warn)('filter "' + name + '" not supported yet');
        return stream;
      } catch (ex) {
        if (ex instanceof _util.MissingDataException) {
          throw ex;
        }
        (0, _util.warn)('Invalid stream: \"' + ex + '\"');
        return new _stream.NullStream();
      }
    }
  };
  return Parser;
}();
var Lexer = function LexerClosure() {
  function Lexer(stream, knownCommands) {
    this.stream = stream;
    this.nextChar();
    this.strBuf = [];
    this.knownCommands = knownCommands;
    this.beginInlineImagePos = -1;
  }
  var specialChars = [1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
  function toHexDigit(ch) {
    if (ch >= 0x30 && ch <= 0x39) {
      return ch & 0x0F;
    }
    if (ch >= 0x41 && ch <= 0x46 || ch >= 0x61 && ch <= 0x66) {
      return (ch & 0x0F) + 9;
    }
    return -1;
  }
  Lexer.prototype = {
    nextChar: function Lexer_nextChar() {
      return this.currentChar = this.stream.getByte();
    },
    peekChar: function Lexer_peekChar() {
      return this.stream.peekByte();
    },
    getNumber: function Lexer_getNumber() {
      var ch = this.currentChar;
      var eNotation = false;
      var divideBy = 0;
      var sign = 1;
      if (ch === 0x2D) {
        sign = -1;
        ch = this.nextChar();
        if (ch === 0x2D) {
          ch = this.nextChar();
        }
      } else if (ch === 0x2B) {
        ch = this.nextChar();
      }
      if (ch === 0x2E) {
        divideBy = 10;
        ch = this.nextChar();
      }
      if (ch === 0x0A || ch === 0x0D) {
        do {
          ch = this.nextChar();
        } while (ch === 0x0A || ch === 0x0D);
      }
      if (ch < 0x30 || ch > 0x39) {
        throw new _util.FormatError(`Invalid number: ${String.fromCharCode(ch)} (charCode ${ch})`);
      }
      var baseValue = ch - 0x30;
      var powerValue = 0;
      var powerValueSign = 1;
      while ((ch = this.nextChar()) >= 0) {
        if (0x30 <= ch && ch <= 0x39) {
          var currentDigit = ch - 0x30;
          if (eNotation) {
            powerValue = powerValue * 10 + currentDigit;
          } else {
            if (divideBy !== 0) {
              divideBy *= 10;
            }
            baseValue = baseValue * 10 + currentDigit;
          }
        } else if (ch === 0x2E) {
          if (divideBy === 0) {
            divideBy = 1;
          } else {
            break;
          }
        } else if (ch === 0x2D) {
          (0, _util.warn)('Badly formatted number');
        } else if (ch === 0x45 || ch === 0x65) {
          ch = this.peekChar();
          if (ch === 0x2B || ch === 0x2D) {
            powerValueSign = ch === 0x2D ? -1 : 1;
            this.nextChar();
          } else if (ch < 0x30 || ch > 0x39) {
            break;
          }
          eNotation = true;
        } else {
          break;
        }
      }
      if (divideBy !== 0) {
        baseValue /= divideBy;
      }
      if (eNotation) {
        baseValue *= Math.pow(10, powerValueSign * powerValue);
      }
      return sign * baseValue;
    },
    getString: function Lexer_getString() {
      var numParen = 1;
      var done = false;
      var strBuf = this.strBuf;
      strBuf.length = 0;
      var ch = this.nextChar();
      while (true) {
        var charBuffered = false;
        switch (ch | 0) {
          case -1:
            (0, _util.warn)('Unterminated string');
            done = true;
            break;
          case 0x28:
            ++numParen;
            strBuf.push('(');
            break;
          case 0x29:
            if (--numParen === 0) {
              this.nextChar();
              done = true;
            } else {
              strBuf.push(')');
            }
            break;
          case 0x5C:
            ch = this.nextChar();
            switch (ch) {
              case -1:
                (0, _util.warn)('Unterminated string');
                done = true;
                break;
              case 0x6E:
                strBuf.push('\n');
                break;
              case 0x72:
                strBuf.push('\r');
                break;
              case 0x74:
                strBuf.push('\t');
                break;
              case 0x62:
                strBuf.push('\b');
                break;
              case 0x66:
                strBuf.push('\f');
                break;
              case 0x5C:
              case 0x28:
              case 0x29:
                strBuf.push(String.fromCharCode(ch));
                break;
              case 0x30:
              case 0x31:
              case 0x32:
              case 0x33:
              case 0x34:
              case 0x35:
              case 0x36:
              case 0x37:
                var x = ch & 0x0F;
                ch = this.nextChar();
                charBuffered = true;
                if (ch >= 0x30 && ch <= 0x37) {
                  x = (x << 3) + (ch & 0x0F);
                  ch = this.nextChar();
                  if (ch >= 0x30 && ch <= 0x37) {
                    charBuffered = false;
                    x = (x << 3) + (ch & 0x0F);
                  }
                }
                strBuf.push(String.fromCharCode(x));
                break;
              case 0x0D:
                if (this.peekChar() === 0x0A) {
                  this.nextChar();
                }
                break;
              case 0x0A:
                break;
              default:
                strBuf.push(String.fromCharCode(ch));
                break;
            }
            break;
          default:
            strBuf.push(String.fromCharCode(ch));
            break;
        }
        if (done) {
          break;
        }
        if (!charBuffered) {
          ch = this.nextChar();
        }
      }
      return strBuf.join('');
    },
    getName: function Lexer_getName() {
      var ch, previousCh;
      var strBuf = this.strBuf;
      strBuf.length = 0;
      while ((ch = this.nextChar()) >= 0 && !specialChars[ch]) {
        if (ch === 0x23) {
          ch = this.nextChar();
          if (specialChars[ch]) {
            (0, _util.warn)('Lexer_getName: ' + 'NUMBER SIGN (#) should be followed by a hexadecimal number.');
            strBuf.push('#');
            break;
          }
          var x = toHexDigit(ch);
          if (x !== -1) {
            previousCh = ch;
            ch = this.nextChar();
            var x2 = toHexDigit(ch);
            if (x2 === -1) {
              (0, _util.warn)('Lexer_getName: Illegal digit (' + String.fromCharCode(ch) + ') in hexadecimal number.');
              strBuf.push('#', String.fromCharCode(previousCh));
              if (specialChars[ch]) {
                break;
              }
              strBuf.push(String.fromCharCode(ch));
              continue;
            }
            strBuf.push(String.fromCharCode(x << 4 | x2));
          } else {
            strBuf.push('#', String.fromCharCode(ch));
          }
        } else {
          strBuf.push(String.fromCharCode(ch));
        }
      }
      if (strBuf.length > 127) {
        (0, _util.warn)('name token is longer than allowed by the spec: ' + strBuf.length);
      }
      return _primitives.Name.get(strBuf.join(''));
    },
    getHexString: function Lexer_getHexString() {
      var strBuf = this.strBuf;
      strBuf.length = 0;
      var ch = this.currentChar;
      var isFirstHex = true;
      var firstDigit;
      var secondDigit;
      while (true) {
        if (ch < 0) {
          (0, _util.warn)('Unterminated hex string');
          break;
        } else if (ch === 0x3E) {
          this.nextChar();
          break;
        } else if (specialChars[ch] === 1) {
          ch = this.nextChar();
          continue;
        } else {
          if (isFirstHex) {
            firstDigit = toHexDigit(ch);
            if (firstDigit === -1) {
              (0, _util.warn)('Ignoring invalid character "' + ch + '" in hex string');
              ch = this.nextChar();
              continue;
            }
          } else {
            secondDigit = toHexDigit(ch);
            if (secondDigit === -1) {
              (0, _util.warn)('Ignoring invalid character "' + ch + '" in hex string');
              ch = this.nextChar();
              continue;
            }
            strBuf.push(String.fromCharCode(firstDigit << 4 | secondDigit));
          }
          isFirstHex = !isFirstHex;
          ch = this.nextChar();
        }
      }
      return strBuf.join('');
    },
    getObj: function Lexer_getObj() {
      var comment = false;
      var ch = this.currentChar;
      while (true) {
        if (ch < 0) {
          return _primitives.EOF;
        }
        if (comment) {
          if (ch === 0x0A || ch === 0x0D) {
            comment = false;
          }
        } else if (ch === 0x25) {
          comment = true;
        } else if (specialChars[ch] !== 1) {
          break;
        }
        ch = this.nextChar();
      }
      switch (ch | 0) {
        case 0x30:
        case 0x31:
        case 0x32:
        case 0x33:
        case 0x34:
        case 0x35:
        case 0x36:
        case 0x37:
        case 0x38:
        case 0x39:
        case 0x2B:
        case 0x2D:
        case 0x2E:
          return this.getNumber();
        case 0x28:
          return this.getString();
        case 0x2F:
          return this.getName();
        case 0x5B:
          this.nextChar();
          return _primitives.Cmd.get('[');
        case 0x5D:
          this.nextChar();
          return _primitives.Cmd.get(']');
        case 0x3C:
          ch = this.nextChar();
          if (ch === 0x3C) {
            this.nextChar();
            return _primitives.Cmd.get('<<');
          }
          return this.getHexString();
        case 0x3E:
          ch = this.nextChar();
          if (ch === 0x3E) {
            this.nextChar();
            return _primitives.Cmd.get('>>');
          }
          return _primitives.Cmd.get('>');
        case 0x7B:
          this.nextChar();
          return _primitives.Cmd.get('{');
        case 0x7D:
          this.nextChar();
          return _primitives.Cmd.get('}');
        case 0x29:
          this.nextChar();
          throw new _util.FormatError(`Illegal character: ${ch}`);
      }
      var str = String.fromCharCode(ch);
      var knownCommands = this.knownCommands;
      var knownCommandFound = knownCommands && knownCommands[str] !== undefined;
      while ((ch = this.nextChar()) >= 0 && !specialChars[ch]) {
        var possibleCommand = str + String.fromCharCode(ch);
        if (knownCommandFound && knownCommands[possibleCommand] === undefined) {
          break;
        }
        if (str.length === 128) {
          throw new _util.FormatError(`Command token too long: ${str.length}`);
        }
        str = possibleCommand;
        knownCommandFound = knownCommands && knownCommands[str] !== undefined;
      }
      if (str === 'true') {
        return true;
      }
      if (str === 'false') {
        return false;
      }
      if (str === 'null') {
        return null;
      }
      if (str === 'BI') {
        this.beginInlineImagePos = this.stream.pos;
      }
      return _primitives.Cmd.get(str);
    },
    skipToNextLine: function Lexer_skipToNextLine() {
      var ch = this.currentChar;
      while (ch >= 0) {
        if (ch === 0x0D) {
          ch = this.nextChar();
          if (ch === 0x0A) {
            this.nextChar();
          }
          break;
        } else if (ch === 0x0A) {
          this.nextChar();
          break;
        }
        ch = this.nextChar();
      }
    }
  };
  return Lexer;
}();
var Linearization = {
  create: function LinearizationCreate(stream) {
    function getInt(name, allowZeroValue) {
      var obj = linDict.get(name);
      if (Number.isInteger(obj) && (allowZeroValue ? obj >= 0 : obj > 0)) {
        return obj;
      }
      throw new Error('The "' + name + '" parameter in the linearization ' + 'dictionary is invalid.');
    }
    function getHints() {
      var hints = linDict.get('H'),
          hintsLength,
          item;
      if (Array.isArray(hints) && ((hintsLength = hints.length) === 2 || hintsLength === 4)) {
        for (var index = 0; index < hintsLength; index++) {
          if (!(Number.isInteger(item = hints[index]) && item > 0)) {
            throw new Error('Hint (' + index + ') in the linearization dictionary is invalid.');
          }
        }
        return hints;
      }
      throw new Error('Hint array in the linearization dictionary is invalid.');
    }
    var parser = new Parser(new Lexer(stream), false, null);
    var obj1 = parser.getObj();
    var obj2 = parser.getObj();
    var obj3 = parser.getObj();
    var linDict = parser.getObj();
    var obj, length;
    if (!(Number.isInteger(obj1) && Number.isInteger(obj2) && (0, _primitives.isCmd)(obj3, 'obj') && (0, _primitives.isDict)(linDict) && (0, _util.isNum)(obj = linDict.get('Linearized')) && obj > 0)) {
      return null;
    } else if ((length = getInt('L')) !== stream.length) {
      throw new Error('The "L" parameter in the linearization dictionary ' + 'does not equal the stream length.');
    }
    return {
      length,
      hints: getHints(),
      objectNumberFirst: getInt('O'),
      endFirst: getInt('E'),
      numPages: getInt('N'),
      mainXRefEntriesOffset: getInt('T'),
      pageFirst: linDict.has('P') ? getInt('P', true) : 0
    };
  }
};
exports.Lexer = Lexer;
exports.Linearization = Linearization;
exports.Parser = Parser;

/***/ }),
/* 6 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.JpegStream = undefined;

var _util = __w_pdfjs_require__(0);

var _stream = __w_pdfjs_require__(2);

var _primitives = __w_pdfjs_require__(1);

var _jpg = __w_pdfjs_require__(29);

let JpegStream = function JpegStreamClosure() {
  function JpegStream(stream, maybeLength, dict, params) {
    let ch;
    while ((ch = stream.getByte()) !== -1) {
      if (ch === 0xFF) {
        stream.skip(-1);
        break;
      }
    }
    this.stream = stream;
    this.maybeLength = maybeLength;
    this.dict = dict;
    this.params = params;
    _stream.DecodeStream.call(this, maybeLength);
  }
  JpegStream.prototype = Object.create(_stream.DecodeStream.prototype);
  Object.defineProperty(JpegStream.prototype, 'bytes', {
    get: function JpegStream_bytes() {
      return (0, _util.shadow)(this, 'bytes', this.stream.getBytes(this.maybeLength));
    },
    configurable: true
  });
  JpegStream.prototype.ensureBuffer = function (requested) {};
  JpegStream.prototype.readBlock = function () {
    if (this.eof) {
      return;
    }
    let jpegImage = new _jpg.JpegImage();
    let decodeArr = this.dict.getArray('Decode', 'D');
    if (this.forceRGB && Array.isArray(decodeArr)) {
      let bitsPerComponent = this.dict.get('BitsPerComponent') || 8;
      let decodeArrLength = decodeArr.length;
      let transform = new Int32Array(decodeArrLength);
      let transformNeeded = false;
      let maxValue = (1 << bitsPerComponent) - 1;
      for (let i = 0; i < decodeArrLength; i += 2) {
        transform[i] = (decodeArr[i + 1] - decodeArr[i]) * 256 | 0;
        transform[i + 1] = decodeArr[i] * maxValue | 0;
        if (transform[i] !== 256 || transform[i + 1] !== 0) {
          transformNeeded = true;
        }
      }
      if (transformNeeded) {
        jpegImage.decodeTransform = transform;
      }
    }
    if ((0, _primitives.isDict)(this.params)) {
      let colorTransform = this.params.get('ColorTransform');
      if (Number.isInteger(colorTransform)) {
        jpegImage.colorTransform = colorTransform;
      }
    }
    jpegImage.parse(this.bytes);
    let data = jpegImage.getData(this.drawWidth, this.drawHeight, this.forceRGB);
    this.buffer = data;
    this.bufferLength = data.length;
    this.eof = true;
  };
  JpegStream.prototype.getIR = function (forceDataSchema = false) {
    return (0, _util.createObjectURL)(this.bytes, 'image/jpeg', forceDataSchema);
  };
  return JpegStream;
}();
exports.JpegStream = JpegStream;

/***/ }),
/* 7 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.OperatorList = undefined;

var _util = __w_pdfjs_require__(0);

var QueueOptimizer = function QueueOptimizerClosure() {
  function addState(parentState, pattern, checkFn, iterateFn, processFn) {
    var state = parentState;
    for (var i = 0, ii = pattern.length - 1; i < ii; i++) {
      var item = pattern[i];
      state = state[item] || (state[item] = []);
    }
    state[pattern[pattern.length - 1]] = {
      checkFn,
      iterateFn,
      processFn
    };
  }
  function handlePaintSolidColorImageMask(iFirstSave, count, fnArray, argsArray) {
    var iFirstPIMXO = iFirstSave + 2;
    for (var i = 0; i < count; i++) {
      var arg = argsArray[iFirstPIMXO + 4 * i];
      var imageMask = arg.length === 1 && arg[0];
      if (imageMask && imageMask.width === 1 && imageMask.height === 1 && (!imageMask.data.length || imageMask.data.length === 1 && imageMask.data[0] === 0)) {
        fnArray[iFirstPIMXO + 4 * i] = _util.OPS.paintSolidColorImageMask;
        continue;
      }
      break;
    }
    return count - i;
  }
  var InitialState = [];
  addState(InitialState, [_util.OPS.save, _util.OPS.transform, _util.OPS.paintInlineImageXObject, _util.OPS.restore], null, function iterateInlineImageGroup(context, i) {
    var fnArray = context.fnArray;
    var iFirstSave = context.iCurr - 3;
    var pos = (i - iFirstSave) % 4;
    switch (pos) {
      case 0:
        return fnArray[i] === _util.OPS.save;
      case 1:
        return fnArray[i] === _util.OPS.transform;
      case 2:
        return fnArray[i] === _util.OPS.paintInlineImageXObject;
      case 3:
        return fnArray[i] === _util.OPS.restore;
    }
  }, function foundInlineImageGroup(context, i) {
    var MIN_IMAGES_IN_INLINE_IMAGES_BLOCK = 10;
    var MAX_IMAGES_IN_INLINE_IMAGES_BLOCK = 200;
    var MAX_WIDTH = 1000;
    var IMAGE_PADDING = 1;
    var fnArray = context.fnArray,
        argsArray = context.argsArray;
    var curr = context.iCurr;
    var iFirstSave = curr - 3;
    var iFirstTransform = curr - 2;
    var iFirstPIIXO = curr - 1;
    var count = Math.min(Math.floor((i - iFirstSave) / 4), MAX_IMAGES_IN_INLINE_IMAGES_BLOCK);
    if (count < MIN_IMAGES_IN_INLINE_IMAGES_BLOCK) {
      return i - (i - iFirstSave) % 4;
    }
    var maxX = 0;
    var map = [],
        maxLineHeight = 0;
    var currentX = IMAGE_PADDING,
        currentY = IMAGE_PADDING;
    var q;
    for (q = 0; q < count; q++) {
      var transform = argsArray[iFirstTransform + (q << 2)];
      var img = argsArray[iFirstPIIXO + (q << 2)][0];
      if (currentX + img.width > MAX_WIDTH) {
        maxX = Math.max(maxX, currentX);
        currentY += maxLineHeight + 2 * IMAGE_PADDING;
        currentX = 0;
        maxLineHeight = 0;
      }
      map.push({
        transform,
        x: currentX,
        y: currentY,
        w: img.width,
        h: img.height
      });
      currentX += img.width + 2 * IMAGE_PADDING;
      maxLineHeight = Math.max(maxLineHeight, img.height);
    }
    var imgWidth = Math.max(maxX, currentX) + IMAGE_PADDING;
    var imgHeight = currentY + maxLineHeight + IMAGE_PADDING;
    var imgData = new Uint8Array(imgWidth * imgHeight * 4);
    var imgRowSize = imgWidth << 2;
    for (q = 0; q < count; q++) {
      var data = argsArray[iFirstPIIXO + (q << 2)][0].data;
      var rowSize = map[q].w << 2;
      var dataOffset = 0;
      var offset = map[q].x + map[q].y * imgWidth << 2;
      imgData.set(data.subarray(0, rowSize), offset - imgRowSize);
      for (var k = 0, kk = map[q].h; k < kk; k++) {
        imgData.set(data.subarray(dataOffset, dataOffset + rowSize), offset);
        dataOffset += rowSize;
        offset += imgRowSize;
      }
      imgData.set(data.subarray(dataOffset - rowSize, dataOffset), offset);
      while (offset >= 0) {
        data[offset - 4] = data[offset];
        data[offset - 3] = data[offset + 1];
        data[offset - 2] = data[offset + 2];
        data[offset - 1] = data[offset + 3];
        data[offset + rowSize] = data[offset + rowSize - 4];
        data[offset + rowSize + 1] = data[offset + rowSize - 3];
        data[offset + rowSize + 2] = data[offset + rowSize - 2];
        data[offset + rowSize + 3] = data[offset + rowSize - 1];
        offset -= imgRowSize;
      }
    }
    fnArray.splice(iFirstSave, count * 4, _util.OPS.paintInlineImageXObjectGroup);
    argsArray.splice(iFirstSave, count * 4, [{
      width: imgWidth,
      height: imgHeight,
      kind: _util.ImageKind.RGBA_32BPP,
      data: imgData
    }, map]);
    return iFirstSave + 1;
  });
  addState(InitialState, [_util.OPS.save, _util.OPS.transform, _util.OPS.paintImageMaskXObject, _util.OPS.restore], null, function iterateImageMaskGroup(context, i) {
    var fnArray = context.fnArray;
    var iFirstSave = context.iCurr - 3;
    var pos = (i - iFirstSave) % 4;
    switch (pos) {
      case 0:
        return fnArray[i] === _util.OPS.save;
      case 1:
        return fnArray[i] === _util.OPS.transform;
      case 2:
        return fnArray[i] === _util.OPS.paintImageMaskXObject;
      case 3:
        return fnArray[i] === _util.OPS.restore;
    }
  }, function foundImageMaskGroup(context, i) {
    var MIN_IMAGES_IN_MASKS_BLOCK = 10;
    var MAX_IMAGES_IN_MASKS_BLOCK = 100;
    var MAX_SAME_IMAGES_IN_MASKS_BLOCK = 1000;
    var fnArray = context.fnArray,
        argsArray = context.argsArray;
    var curr = context.iCurr;
    var iFirstSave = curr - 3;
    var iFirstTransform = curr - 2;
    var iFirstPIMXO = curr - 1;
    var count = Math.floor((i - iFirstSave) / 4);
    count = handlePaintSolidColorImageMask(iFirstSave, count, fnArray, argsArray);
    if (count < MIN_IMAGES_IN_MASKS_BLOCK) {
      return i - (i - iFirstSave) % 4;
    }
    var q;
    var isSameImage = false;
    var iTransform, transformArgs;
    var firstPIMXOArg0 = argsArray[iFirstPIMXO][0];
    if (argsArray[iFirstTransform][1] === 0 && argsArray[iFirstTransform][2] === 0) {
      isSameImage = true;
      var firstTransformArg0 = argsArray[iFirstTransform][0];
      var firstTransformArg3 = argsArray[iFirstTransform][3];
      iTransform = iFirstTransform + 4;
      var iPIMXO = iFirstPIMXO + 4;
      for (q = 1; q < count; q++, iTransform += 4, iPIMXO += 4) {
        transformArgs = argsArray[iTransform];
        if (argsArray[iPIMXO][0] !== firstPIMXOArg0 || transformArgs[0] !== firstTransformArg0 || transformArgs[1] !== 0 || transformArgs[2] !== 0 || transformArgs[3] !== firstTransformArg3) {
          if (q < MIN_IMAGES_IN_MASKS_BLOCK) {
            isSameImage = false;
          } else {
            count = q;
          }
          break;
        }
      }
    }
    if (isSameImage) {
      count = Math.min(count, MAX_SAME_IMAGES_IN_MASKS_BLOCK);
      var positions = new Float32Array(count * 2);
      iTransform = iFirstTransform;
      for (q = 0; q < count; q++, iTransform += 4) {
        transformArgs = argsArray[iTransform];
        positions[q << 1] = transformArgs[4];
        positions[(q << 1) + 1] = transformArgs[5];
      }
      fnArray.splice(iFirstSave, count * 4, _util.OPS.paintImageMaskXObjectRepeat);
      argsArray.splice(iFirstSave, count * 4, [firstPIMXOArg0, firstTransformArg0, firstTransformArg3, positions]);
    } else {
      count = Math.min(count, MAX_IMAGES_IN_MASKS_BLOCK);
      var images = [];
      for (q = 0; q < count; q++) {
        transformArgs = argsArray[iFirstTransform + (q << 2)];
        var maskParams = argsArray[iFirstPIMXO + (q << 2)][0];
        images.push({
          data: maskParams.data,
          width: maskParams.width,
          height: maskParams.height,
          transform: transformArgs
        });
      }
      fnArray.splice(iFirstSave, count * 4, _util.OPS.paintImageMaskXObjectGroup);
      argsArray.splice(iFirstSave, count * 4, [images]);
    }
    return iFirstSave + 1;
  });
  addState(InitialState, [_util.OPS.save, _util.OPS.transform, _util.OPS.paintImageXObject, _util.OPS.restore], function (context) {
    var argsArray = context.argsArray;
    var iFirstTransform = context.iCurr - 2;
    return argsArray[iFirstTransform][1] === 0 && argsArray[iFirstTransform][2] === 0;
  }, function (context, i) {
    var fnArray = context.fnArray,
        argsArray = context.argsArray;
    var iFirstSave = context.iCurr - 3;
    var pos = (i - iFirstSave) % 4;
    switch (pos) {
      case 0:
        return fnArray[i] === _util.OPS.save;
      case 1:
        if (fnArray[i] !== _util.OPS.transform) {
          return false;
        }
        var iFirstTransform = context.iCurr - 2;
        var firstTransformArg0 = argsArray[iFirstTransform][0];
        var firstTransformArg3 = argsArray[iFirstTransform][3];
        if (argsArray[i][0] !== firstTransformArg0 || argsArray[i][1] !== 0 || argsArray[i][2] !== 0 || argsArray[i][3] !== firstTransformArg3) {
          return false;
        }
        return true;
      case 2:
        if (fnArray[i] !== _util.OPS.paintImageXObject) {
          return false;
        }
        var iFirstPIXO = context.iCurr - 1;
        var firstPIXOArg0 = argsArray[iFirstPIXO][0];
        if (argsArray[i][0] !== firstPIXOArg0) {
          return false;
        }
        return true;
      case 3:
        return fnArray[i] === _util.OPS.restore;
    }
  }, function (context, i) {
    var MIN_IMAGES_IN_BLOCK = 3;
    var MAX_IMAGES_IN_BLOCK = 1000;
    var fnArray = context.fnArray,
        argsArray = context.argsArray;
    var curr = context.iCurr;
    var iFirstSave = curr - 3;
    var iFirstTransform = curr - 2;
    var iFirstPIXO = curr - 1;
    var firstPIXOArg0 = argsArray[iFirstPIXO][0];
    var firstTransformArg0 = argsArray[iFirstTransform][0];
    var firstTransformArg3 = argsArray[iFirstTransform][3];
    var count = Math.min(Math.floor((i - iFirstSave) / 4), MAX_IMAGES_IN_BLOCK);
    if (count < MIN_IMAGES_IN_BLOCK) {
      return i - (i - iFirstSave) % 4;
    }
    var positions = new Float32Array(count * 2);
    var iTransform = iFirstTransform;
    for (var q = 0; q < count; q++, iTransform += 4) {
      var transformArgs = argsArray[iTransform];
      positions[q << 1] = transformArgs[4];
      positions[(q << 1) + 1] = transformArgs[5];
    }
    var args = [firstPIXOArg0, firstTransformArg0, firstTransformArg3, positions];
    fnArray.splice(iFirstSave, count * 4, _util.OPS.paintImageXObjectRepeat);
    argsArray.splice(iFirstSave, count * 4, args);
    return iFirstSave + 1;
  });
  addState(InitialState, [_util.OPS.beginText, _util.OPS.setFont, _util.OPS.setTextMatrix, _util.OPS.showText, _util.OPS.endText], null, function (context, i) {
    var fnArray = context.fnArray,
        argsArray = context.argsArray;
    var iFirstSave = context.iCurr - 4;
    var pos = (i - iFirstSave) % 5;
    switch (pos) {
      case 0:
        return fnArray[i] === _util.OPS.beginText;
      case 1:
        return fnArray[i] === _util.OPS.setFont;
      case 2:
        return fnArray[i] === _util.OPS.setTextMatrix;
      case 3:
        if (fnArray[i] !== _util.OPS.showText) {
          return false;
        }
        var iFirstSetFont = context.iCurr - 3;
        var firstSetFontArg0 = argsArray[iFirstSetFont][0];
        var firstSetFontArg1 = argsArray[iFirstSetFont][1];
        if (argsArray[i][0] !== firstSetFontArg0 || argsArray[i][1] !== firstSetFontArg1) {
          return false;
        }
        return true;
      case 4:
        return fnArray[i] === _util.OPS.endText;
    }
  }, function (context, i) {
    var MIN_CHARS_IN_BLOCK = 3;
    var MAX_CHARS_IN_BLOCK = 1000;
    var fnArray = context.fnArray,
        argsArray = context.argsArray;
    var curr = context.iCurr;
    var iFirstBeginText = curr - 4;
    var iFirstSetFont = curr - 3;
    var iFirstSetTextMatrix = curr - 2;
    var iFirstShowText = curr - 1;
    var iFirstEndText = curr;
    var firstSetFontArg0 = argsArray[iFirstSetFont][0];
    var firstSetFontArg1 = argsArray[iFirstSetFont][1];
    var count = Math.min(Math.floor((i - iFirstBeginText) / 5), MAX_CHARS_IN_BLOCK);
    if (count < MIN_CHARS_IN_BLOCK) {
      return i - (i - iFirstBeginText) % 5;
    }
    var iFirst = iFirstBeginText;
    if (iFirstBeginText >= 4 && fnArray[iFirstBeginText - 4] === fnArray[iFirstSetFont] && fnArray[iFirstBeginText - 3] === fnArray[iFirstSetTextMatrix] && fnArray[iFirstBeginText - 2] === fnArray[iFirstShowText] && fnArray[iFirstBeginText - 1] === fnArray[iFirstEndText] && argsArray[iFirstBeginText - 4][0] === firstSetFontArg0 && argsArray[iFirstBeginText - 4][1] === firstSetFontArg1) {
      count++;
      iFirst -= 5;
    }
    var iEndText = iFirst + 4;
    for (var q = 1; q < count; q++) {
      fnArray.splice(iEndText, 3);
      argsArray.splice(iEndText, 3);
      iEndText += 2;
    }
    return iEndText + 1;
  });
  function QueueOptimizer(queue) {
    this.queue = queue;
    this.state = null;
    this.context = {
      iCurr: 0,
      fnArray: queue.fnArray,
      argsArray: queue.argsArray
    };
    this.match = null;
    this.lastProcessed = 0;
  }
  QueueOptimizer.prototype = {
    _optimize() {
      const fnArray = this.queue.fnArray;
      let i = this.lastProcessed,
          ii = fnArray.length;
      let state = this.state;
      let match = this.match;
      if (!state && !match && i + 1 === ii && !InitialState[fnArray[i]]) {
        this.lastProcessed = ii;
        return;
      }
      const context = this.context;
      while (i < ii) {
        if (match) {
          const iterate = (0, match.iterateFn)(context, i);
          if (iterate) {
            i++;
            continue;
          }
          i = (0, match.processFn)(context, i + 1);
          ii = fnArray.length;
          match = null;
          state = null;
          if (i >= ii) {
            break;
          }
        }
        state = (state || InitialState)[fnArray[i]];
        if (!state || Array.isArray(state)) {
          i++;
          continue;
        }
        context.iCurr = i;
        i++;
        if (state.checkFn && !(0, state.checkFn)(context)) {
          state = null;
          continue;
        }
        match = state;
        state = null;
      }
      this.state = state;
      this.match = match;
      this.lastProcessed = i;
    },
    push(fn, args) {
      this.queue.fnArray.push(fn);
      this.queue.argsArray.push(args);
      this._optimize();
    },
    flush() {
      while (this.match) {
        const length = this.queue.fnArray.length;
        this.lastProcessed = (0, this.match.processFn)(this.context, length);
        this.match = null;
        this.state = null;
        this._optimize();
      }
    },
    reset() {
      this.state = null;
      this.match = null;
      this.lastProcessed = 0;
    }
  };
  return QueueOptimizer;
}();
var NullOptimizer = function NullOptimizerClosure() {
  function NullOptimizer(queue) {
    this.queue = queue;
  }
  NullOptimizer.prototype = {
    push(fn, args) {
      this.queue.fnArray.push(fn);
      this.queue.argsArray.push(args);
    },
    flush() {}
  };
  return NullOptimizer;
}();
var OperatorList = function OperatorListClosure() {
  var CHUNK_SIZE = 1000;
  var CHUNK_SIZE_ABOUT = CHUNK_SIZE - 5;
  function getTransfers(queue) {
    var transfers = [];
    var fnArray = queue.fnArray,
        argsArray = queue.argsArray;
    for (var i = 0, ii = queue.length; i < ii; i++) {
      switch (fnArray[i]) {
        case _util.OPS.paintInlineImageXObject:
        case _util.OPS.paintInlineImageXObjectGroup:
        case _util.OPS.paintImageMaskXObject:
          var arg = argsArray[i][0];
          if (!arg.cached) {
            transfers.push(arg.data.buffer);
          }
          break;
      }
    }
    return transfers;
  }
  function OperatorList(intent, messageHandler, pageIndex) {
    this.messageHandler = messageHandler;
    this.fnArray = [];
    this.argsArray = [];
    if (messageHandler && this.intent !== 'oplist') {
      this.optimizer = new QueueOptimizer(this);
    } else {
      this.optimizer = new NullOptimizer(this);
    }
    this.dependencies = Object.create(null);
    this._totalLength = 0;
    this.pageIndex = pageIndex;
    this.intent = intent;
    this.weight = 0;
  }
  OperatorList.prototype = {
    get length() {
      return this.argsArray.length;
    },
    get totalLength() {
      return this._totalLength + this.length;
    },
    addOp(fn, args) {
      this.optimizer.push(fn, args);
      this.weight++;
      if (this.messageHandler) {
        if (this.weight >= CHUNK_SIZE) {
          this.flush();
        } else if (this.weight >= CHUNK_SIZE_ABOUT && (fn === _util.OPS.restore || fn === _util.OPS.endText)) {
          this.flush();
        }
      }
    },
    addDependency(dependency) {
      if (dependency in this.dependencies) {
        return;
      }
      this.dependencies[dependency] = true;
      this.addOp(_util.OPS.dependency, [dependency]);
    },
    addDependencies(dependencies) {
      for (var key in dependencies) {
        this.addDependency(key);
      }
    },
    addOpList(opList) {
      _util.Util.extendObj(this.dependencies, opList.dependencies);
      for (var i = 0, ii = opList.length; i < ii; i++) {
        this.addOp(opList.fnArray[i], opList.argsArray[i]);
      }
    },
    getIR() {
      return {
        fnArray: this.fnArray,
        argsArray: this.argsArray,
        length: this.length
      };
    },
    flush(lastChunk) {
      this.optimizer.flush();
      var transfers = getTransfers(this);
      var length = this.length;
      this._totalLength += length;
      this.messageHandler.send('RenderPageChunk', {
        operatorList: {
          fnArray: this.fnArray,
          argsArray: this.argsArray,
          lastChunk,
          length
        },
        pageIndex: this.pageIndex,
        intent: this.intent
      }, transfers);
      this.dependencies = Object.create(null);
      this.fnArray.length = 0;
      this.argsArray.length = 0;
      this.weight = 0;
      this.optimizer.reset();
    }
  };
  return OperatorList;
}();
exports.OperatorList = OperatorList;

/***/ }),
/* 8 */
/***/ (function(module, exports, __w_pdfjs_require__) {

var getLookupTableFactory = __w_pdfjs_require__(0).getLookupTableFactory;
var getGlyphsUnicode = getLookupTableFactory(function (t) {
 t['A'] = 0x0041;
 t['AE'] = 0x00C6;
 t['AEacute'] = 0x01FC;
 t['AEmacron'] = 0x01E2;
 t['AEsmall'] = 0xF7E6;
 t['Aacute'] = 0x00C1;
 t['Aacutesmall'] = 0xF7E1;
 t['Abreve'] = 0x0102;
 t['Abreveacute'] = 0x1EAE;
 t['Abrevecyrillic'] = 0x04D0;
 t['Abrevedotbelow'] = 0x1EB6;
 t['Abrevegrave'] = 0x1EB0;
 t['Abrevehookabove'] = 0x1EB2;
 t['Abrevetilde'] = 0x1EB4;
 t['Acaron'] = 0x01CD;
 t['Acircle'] = 0x24B6;
 t['Acircumflex'] = 0x00C2;
 t['Acircumflexacute'] = 0x1EA4;
 t['Acircumflexdotbelow'] = 0x1EAC;
 t['Acircumflexgrave'] = 0x1EA6;
 t['Acircumflexhookabove'] = 0x1EA8;
 t['Acircumflexsmall'] = 0xF7E2;
 t['Acircumflextilde'] = 0x1EAA;
 t['Acute'] = 0xF6C9;
 t['Acutesmall'] = 0xF7B4;
 t['Acyrillic'] = 0x0410;
 t['Adblgrave'] = 0x0200;
 t['Adieresis'] = 0x00C4;
 t['Adieresiscyrillic'] = 0x04D2;
 t['Adieresismacron'] = 0x01DE;
 t['Adieresissmall'] = 0xF7E4;
 t['Adotbelow'] = 0x1EA0;
 t['Adotmacron'] = 0x01E0;
 t['Agrave'] = 0x00C0;
 t['Agravesmall'] = 0xF7E0;
 t['Ahookabove'] = 0x1EA2;
 t['Aiecyrillic'] = 0x04D4;
 t['Ainvertedbreve'] = 0x0202;
 t['Alpha'] = 0x0391;
 t['Alphatonos'] = 0x0386;
 t['Amacron'] = 0x0100;
 t['Amonospace'] = 0xFF21;
 t['Aogonek'] = 0x0104;
 t['Aring'] = 0x00C5;
 t['Aringacute'] = 0x01FA;
 t['Aringbelow'] = 0x1E00;
 t['Aringsmall'] = 0xF7E5;
 t['Asmall'] = 0xF761;
 t['Atilde'] = 0x00C3;
 t['Atildesmall'] = 0xF7E3;
 t['Aybarmenian'] = 0x0531;
 t['B'] = 0x0042;
 t['Bcircle'] = 0x24B7;
 t['Bdotaccent'] = 0x1E02;
 t['Bdotbelow'] = 0x1E04;
 t['Becyrillic'] = 0x0411;
 t['Benarmenian'] = 0x0532;
 t['Beta'] = 0x0392;
 t['Bhook'] = 0x0181;
 t['Blinebelow'] = 0x1E06;
 t['Bmonospace'] = 0xFF22;
 t['Brevesmall'] = 0xF6F4;
 t['Bsmall'] = 0xF762;
 t['Btopbar'] = 0x0182;
 t['C'] = 0x0043;
 t['Caarmenian'] = 0x053E;
 t['Cacute'] = 0x0106;
 t['Caron'] = 0xF6CA;
 t['Caronsmall'] = 0xF6F5;
 t['Ccaron'] = 0x010C;
 t['Ccedilla'] = 0x00C7;
 t['Ccedillaacute'] = 0x1E08;
 t['Ccedillasmall'] = 0xF7E7;
 t['Ccircle'] = 0x24B8;
 t['Ccircumflex'] = 0x0108;
 t['Cdot'] = 0x010A;
 t['Cdotaccent'] = 0x010A;
 t['Cedillasmall'] = 0xF7B8;
 t['Chaarmenian'] = 0x0549;
 t['Cheabkhasiancyrillic'] = 0x04BC;
 t['Checyrillic'] = 0x0427;
 t['Chedescenderabkhasiancyrillic'] = 0x04BE;
 t['Chedescendercyrillic'] = 0x04B6;
 t['Chedieresiscyrillic'] = 0x04F4;
 t['Cheharmenian'] = 0x0543;
 t['Chekhakassiancyrillic'] = 0x04CB;
 t['Cheverticalstrokecyrillic'] = 0x04B8;
 t['Chi'] = 0x03A7;
 t['Chook'] = 0x0187;
 t['Circumflexsmall'] = 0xF6F6;
 t['Cmonospace'] = 0xFF23;
 t['Coarmenian'] = 0x0551;
 t['Csmall'] = 0xF763;
 t['D'] = 0x0044;
 t['DZ'] = 0x01F1;
 t['DZcaron'] = 0x01C4;
 t['Daarmenian'] = 0x0534;
 t['Dafrican'] = 0x0189;
 t['Dcaron'] = 0x010E;
 t['Dcedilla'] = 0x1E10;
 t['Dcircle'] = 0x24B9;
 t['Dcircumflexbelow'] = 0x1E12;
 t['Dcroat'] = 0x0110;
 t['Ddotaccent'] = 0x1E0A;
 t['Ddotbelow'] = 0x1E0C;
 t['Decyrillic'] = 0x0414;
 t['Deicoptic'] = 0x03EE;
 t['Delta'] = 0x2206;
 t['Deltagreek'] = 0x0394;
 t['Dhook'] = 0x018A;
 t['Dieresis'] = 0xF6CB;
 t['DieresisAcute'] = 0xF6CC;
 t['DieresisGrave'] = 0xF6CD;
 t['Dieresissmall'] = 0xF7A8;
 t['Digammagreek'] = 0x03DC;
 t['Djecyrillic'] = 0x0402;
 t['Dlinebelow'] = 0x1E0E;
 t['Dmonospace'] = 0xFF24;
 t['Dotaccentsmall'] = 0xF6F7;
 t['Dslash'] = 0x0110;
 t['Dsmall'] = 0xF764;
 t['Dtopbar'] = 0x018B;
 t['Dz'] = 0x01F2;
 t['Dzcaron'] = 0x01C5;
 t['Dzeabkhasiancyrillic'] = 0x04E0;
 t['Dzecyrillic'] = 0x0405;
 t['Dzhecyrillic'] = 0x040F;
 t['E'] = 0x0045;
 t['Eacute'] = 0x00C9;
 t['Eacutesmall'] = 0xF7E9;
 t['Ebreve'] = 0x0114;
 t['Ecaron'] = 0x011A;
 t['Ecedillabreve'] = 0x1E1C;
 t['Echarmenian'] = 0x0535;
 t['Ecircle'] = 0x24BA;
 t['Ecircumflex'] = 0x00CA;
 t['Ecircumflexacute'] = 0x1EBE;
 t['Ecircumflexbelow'] = 0x1E18;
 t['Ecircumflexdotbelow'] = 0x1EC6;
 t['Ecircumflexgrave'] = 0x1EC0;
 t['Ecircumflexhookabove'] = 0x1EC2;
 t['Ecircumflexsmall'] = 0xF7EA;
 t['Ecircumflextilde'] = 0x1EC4;
 t['Ecyrillic'] = 0x0404;
 t['Edblgrave'] = 0x0204;
 t['Edieresis'] = 0x00CB;
 t['Edieresissmall'] = 0xF7EB;
 t['Edot'] = 0x0116;
 t['Edotaccent'] = 0x0116;
 t['Edotbelow'] = 0x1EB8;
 t['Efcyrillic'] = 0x0424;
 t['Egrave'] = 0x00C8;
 t['Egravesmall'] = 0xF7E8;
 t['Eharmenian'] = 0x0537;
 t['Ehookabove'] = 0x1EBA;
 t['Eightroman'] = 0x2167;
 t['Einvertedbreve'] = 0x0206;
 t['Eiotifiedcyrillic'] = 0x0464;
 t['Elcyrillic'] = 0x041B;
 t['Elevenroman'] = 0x216A;
 t['Emacron'] = 0x0112;
 t['Emacronacute'] = 0x1E16;
 t['Emacrongrave'] = 0x1E14;
 t['Emcyrillic'] = 0x041C;
 t['Emonospace'] = 0xFF25;
 t['Encyrillic'] = 0x041D;
 t['Endescendercyrillic'] = 0x04A2;
 t['Eng'] = 0x014A;
 t['Enghecyrillic'] = 0x04A4;
 t['Enhookcyrillic'] = 0x04C7;
 t['Eogonek'] = 0x0118;
 t['Eopen'] = 0x0190;
 t['Epsilon'] = 0x0395;
 t['Epsilontonos'] = 0x0388;
 t['Ercyrillic'] = 0x0420;
 t['Ereversed'] = 0x018E;
 t['Ereversedcyrillic'] = 0x042D;
 t['Escyrillic'] = 0x0421;
 t['Esdescendercyrillic'] = 0x04AA;
 t['Esh'] = 0x01A9;
 t['Esmall'] = 0xF765;
 t['Eta'] = 0x0397;
 t['Etarmenian'] = 0x0538;
 t['Etatonos'] = 0x0389;
 t['Eth'] = 0x00D0;
 t['Ethsmall'] = 0xF7F0;
 t['Etilde'] = 0x1EBC;
 t['Etildebelow'] = 0x1E1A;
 t['Euro'] = 0x20AC;
 t['Ezh'] = 0x01B7;
 t['Ezhcaron'] = 0x01EE;
 t['Ezhreversed'] = 0x01B8;
 t['F'] = 0x0046;
 t['Fcircle'] = 0x24BB;
 t['Fdotaccent'] = 0x1E1E;
 t['Feharmenian'] = 0x0556;
 t['Feicoptic'] = 0x03E4;
 t['Fhook'] = 0x0191;
 t['Fitacyrillic'] = 0x0472;
 t['Fiveroman'] = 0x2164;
 t['Fmonospace'] = 0xFF26;
 t['Fourroman'] = 0x2163;
 t['Fsmall'] = 0xF766;
 t['G'] = 0x0047;
 t['GBsquare'] = 0x3387;
 t['Gacute'] = 0x01F4;
 t['Gamma'] = 0x0393;
 t['Gammaafrican'] = 0x0194;
 t['Gangiacoptic'] = 0x03EA;
 t['Gbreve'] = 0x011E;
 t['Gcaron'] = 0x01E6;
 t['Gcedilla'] = 0x0122;
 t['Gcircle'] = 0x24BC;
 t['Gcircumflex'] = 0x011C;
 t['Gcommaaccent'] = 0x0122;
 t['Gdot'] = 0x0120;
 t['Gdotaccent'] = 0x0120;
 t['Gecyrillic'] = 0x0413;
 t['Ghadarmenian'] = 0x0542;
 t['Ghemiddlehookcyrillic'] = 0x0494;
 t['Ghestrokecyrillic'] = 0x0492;
 t['Gheupturncyrillic'] = 0x0490;
 t['Ghook'] = 0x0193;
 t['Gimarmenian'] = 0x0533;
 t['Gjecyrillic'] = 0x0403;
 t['Gmacron'] = 0x1E20;
 t['Gmonospace'] = 0xFF27;
 t['Grave'] = 0xF6CE;
 t['Gravesmall'] = 0xF760;
 t['Gsmall'] = 0xF767;
 t['Gsmallhook'] = 0x029B;
 t['Gstroke'] = 0x01E4;
 t['H'] = 0x0048;
 t['H18533'] = 0x25CF;
 t['H18543'] = 0x25AA;
 t['H18551'] = 0x25AB;
 t['H22073'] = 0x25A1;
 t['HPsquare'] = 0x33CB;
 t['Haabkhasiancyrillic'] = 0x04A8;
 t['Hadescendercyrillic'] = 0x04B2;
 t['Hardsigncyrillic'] = 0x042A;
 t['Hbar'] = 0x0126;
 t['Hbrevebelow'] = 0x1E2A;
 t['Hcedilla'] = 0x1E28;
 t['Hcircle'] = 0x24BD;
 t['Hcircumflex'] = 0x0124;
 t['Hdieresis'] = 0x1E26;
 t['Hdotaccent'] = 0x1E22;
 t['Hdotbelow'] = 0x1E24;
 t['Hmonospace'] = 0xFF28;
 t['Hoarmenian'] = 0x0540;
 t['Horicoptic'] = 0x03E8;
 t['Hsmall'] = 0xF768;
 t['Hungarumlaut'] = 0xF6CF;
 t['Hungarumlautsmall'] = 0xF6F8;
 t['Hzsquare'] = 0x3390;
 t['I'] = 0x0049;
 t['IAcyrillic'] = 0x042F;
 t['IJ'] = 0x0132;
 t['IUcyrillic'] = 0x042E;
 t['Iacute'] = 0x00CD;
 t['Iacutesmall'] = 0xF7ED;
 t['Ibreve'] = 0x012C;
 t['Icaron'] = 0x01CF;
 t['Icircle'] = 0x24BE;
 t['Icircumflex'] = 0x00CE;
 t['Icircumflexsmall'] = 0xF7EE;
 t['Icyrillic'] = 0x0406;
 t['Idblgrave'] = 0x0208;
 t['Idieresis'] = 0x00CF;
 t['Idieresisacute'] = 0x1E2E;
 t['Idieresiscyrillic'] = 0x04E4;
 t['Idieresissmall'] = 0xF7EF;
 t['Idot'] = 0x0130;
 t['Idotaccent'] = 0x0130;
 t['Idotbelow'] = 0x1ECA;
 t['Iebrevecyrillic'] = 0x04D6;
 t['Iecyrillic'] = 0x0415;
 t['Ifraktur'] = 0x2111;
 t['Igrave'] = 0x00CC;
 t['Igravesmall'] = 0xF7EC;
 t['Ihookabove'] = 0x1EC8;
 t['Iicyrillic'] = 0x0418;
 t['Iinvertedbreve'] = 0x020A;
 t['Iishortcyrillic'] = 0x0419;
 t['Imacron'] = 0x012A;
 t['Imacroncyrillic'] = 0x04E2;
 t['Imonospace'] = 0xFF29;
 t['Iniarmenian'] = 0x053B;
 t['Iocyrillic'] = 0x0401;
 t['Iogonek'] = 0x012E;
 t['Iota'] = 0x0399;
 t['Iotaafrican'] = 0x0196;
 t['Iotadieresis'] = 0x03AA;
 t['Iotatonos'] = 0x038A;
 t['Ismall'] = 0xF769;
 t['Istroke'] = 0x0197;
 t['Itilde'] = 0x0128;
 t['Itildebelow'] = 0x1E2C;
 t['Izhitsacyrillic'] = 0x0474;
 t['Izhitsadblgravecyrillic'] = 0x0476;
 t['J'] = 0x004A;
 t['Jaarmenian'] = 0x0541;
 t['Jcircle'] = 0x24BF;
 t['Jcircumflex'] = 0x0134;
 t['Jecyrillic'] = 0x0408;
 t['Jheharmenian'] = 0x054B;
 t['Jmonospace'] = 0xFF2A;
 t['Jsmall'] = 0xF76A;
 t['K'] = 0x004B;
 t['KBsquare'] = 0x3385;
 t['KKsquare'] = 0x33CD;
 t['Kabashkircyrillic'] = 0x04A0;
 t['Kacute'] = 0x1E30;
 t['Kacyrillic'] = 0x041A;
 t['Kadescendercyrillic'] = 0x049A;
 t['Kahookcyrillic'] = 0x04C3;
 t['Kappa'] = 0x039A;
 t['Kastrokecyrillic'] = 0x049E;
 t['Kaverticalstrokecyrillic'] = 0x049C;
 t['Kcaron'] = 0x01E8;
 t['Kcedilla'] = 0x0136;
 t['Kcircle'] = 0x24C0;
 t['Kcommaaccent'] = 0x0136;
 t['Kdotbelow'] = 0x1E32;
 t['Keharmenian'] = 0x0554;
 t['Kenarmenian'] = 0x053F;
 t['Khacyrillic'] = 0x0425;
 t['Kheicoptic'] = 0x03E6;
 t['Khook'] = 0x0198;
 t['Kjecyrillic'] = 0x040C;
 t['Klinebelow'] = 0x1E34;
 t['Kmonospace'] = 0xFF2B;
 t['Koppacyrillic'] = 0x0480;
 t['Koppagreek'] = 0x03DE;
 t['Ksicyrillic'] = 0x046E;
 t['Ksmall'] = 0xF76B;
 t['L'] = 0x004C;
 t['LJ'] = 0x01C7;
 t['LL'] = 0xF6BF;
 t['Lacute'] = 0x0139;
 t['Lambda'] = 0x039B;
 t['Lcaron'] = 0x013D;
 t['Lcedilla'] = 0x013B;
 t['Lcircle'] = 0x24C1;
 t['Lcircumflexbelow'] = 0x1E3C;
 t['Lcommaaccent'] = 0x013B;
 t['Ldot'] = 0x013F;
 t['Ldotaccent'] = 0x013F;
 t['Ldotbelow'] = 0x1E36;
 t['Ldotbelowmacron'] = 0x1E38;
 t['Liwnarmenian'] = 0x053C;
 t['Lj'] = 0x01C8;
 t['Ljecyrillic'] = 0x0409;
 t['Llinebelow'] = 0x1E3A;
 t['Lmonospace'] = 0xFF2C;
 t['Lslash'] = 0x0141;
 t['Lslashsmall'] = 0xF6F9;
 t['Lsmall'] = 0xF76C;
 t['M'] = 0x004D;
 t['MBsquare'] = 0x3386;
 t['Macron'] = 0xF6D0;
 t['Macronsmall'] = 0xF7AF;
 t['Macute'] = 0x1E3E;
 t['Mcircle'] = 0x24C2;
 t['Mdotaccent'] = 0x1E40;
 t['Mdotbelow'] = 0x1E42;
 t['Menarmenian'] = 0x0544;
 t['Mmonospace'] = 0xFF2D;
 t['Msmall'] = 0xF76D;
 t['Mturned'] = 0x019C;
 t['Mu'] = 0x039C;
 t['N'] = 0x004E;
 t['NJ'] = 0x01CA;
 t['Nacute'] = 0x0143;
 t['Ncaron'] = 0x0147;
 t['Ncedilla'] = 0x0145;
 t['Ncircle'] = 0x24C3;
 t['Ncircumflexbelow'] = 0x1E4A;
 t['Ncommaaccent'] = 0x0145;
 t['Ndotaccent'] = 0x1E44;
 t['Ndotbelow'] = 0x1E46;
 t['Nhookleft'] = 0x019D;
 t['Nineroman'] = 0x2168;
 t['Nj'] = 0x01CB;
 t['Njecyrillic'] = 0x040A;
 t['Nlinebelow'] = 0x1E48;
 t['Nmonospace'] = 0xFF2E;
 t['Nowarmenian'] = 0x0546;
 t['Nsmall'] = 0xF76E;
 t['Ntilde'] = 0x00D1;
 t['Ntildesmall'] = 0xF7F1;
 t['Nu'] = 0x039D;
 t['O'] = 0x004F;
 t['OE'] = 0x0152;
 t['OEsmall'] = 0xF6FA;
 t['Oacute'] = 0x00D3;
 t['Oacutesmall'] = 0xF7F3;
 t['Obarredcyrillic'] = 0x04E8;
 t['Obarreddieresiscyrillic'] = 0x04EA;
 t['Obreve'] = 0x014E;
 t['Ocaron'] = 0x01D1;
 t['Ocenteredtilde'] = 0x019F;
 t['Ocircle'] = 0x24C4;
 t['Ocircumflex'] = 0x00D4;
 t['Ocircumflexacute'] = 0x1ED0;
 t['Ocircumflexdotbelow'] = 0x1ED8;
 t['Ocircumflexgrave'] = 0x1ED2;
 t['Ocircumflexhookabove'] = 0x1ED4;
 t['Ocircumflexsmall'] = 0xF7F4;
 t['Ocircumflextilde'] = 0x1ED6;
 t['Ocyrillic'] = 0x041E;
 t['Odblacute'] = 0x0150;
 t['Odblgrave'] = 0x020C;
 t['Odieresis'] = 0x00D6;
 t['Odieresiscyrillic'] = 0x04E6;
 t['Odieresissmall'] = 0xF7F6;
 t['Odotbelow'] = 0x1ECC;
 t['Ogoneksmall'] = 0xF6FB;
 t['Ograve'] = 0x00D2;
 t['Ogravesmall'] = 0xF7F2;
 t['Oharmenian'] = 0x0555;
 t['Ohm'] = 0x2126;
 t['Ohookabove'] = 0x1ECE;
 t['Ohorn'] = 0x01A0;
 t['Ohornacute'] = 0x1EDA;
 t['Ohorndotbelow'] = 0x1EE2;
 t['Ohorngrave'] = 0x1EDC;
 t['Ohornhookabove'] = 0x1EDE;
 t['Ohorntilde'] = 0x1EE0;
 t['Ohungarumlaut'] = 0x0150;
 t['Oi'] = 0x01A2;
 t['Oinvertedbreve'] = 0x020E;
 t['Omacron'] = 0x014C;
 t['Omacronacute'] = 0x1E52;
 t['Omacrongrave'] = 0x1E50;
 t['Omega'] = 0x2126;
 t['Omegacyrillic'] = 0x0460;
 t['Omegagreek'] = 0x03A9;
 t['Omegaroundcyrillic'] = 0x047A;
 t['Omegatitlocyrillic'] = 0x047C;
 t['Omegatonos'] = 0x038F;
 t['Omicron'] = 0x039F;
 t['Omicrontonos'] = 0x038C;
 t['Omonospace'] = 0xFF2F;
 t['Oneroman'] = 0x2160;
 t['Oogonek'] = 0x01EA;
 t['Oogonekmacron'] = 0x01EC;
 t['Oopen'] = 0x0186;
 t['Oslash'] = 0x00D8;
 t['Oslashacute'] = 0x01FE;
 t['Oslashsmall'] = 0xF7F8;
 t['Osmall'] = 0xF76F;
 t['Ostrokeacute'] = 0x01FE;
 t['Otcyrillic'] = 0x047E;
 t['Otilde'] = 0x00D5;
 t['Otildeacute'] = 0x1E4C;
 t['Otildedieresis'] = 0x1E4E;
 t['Otildesmall'] = 0xF7F5;
 t['P'] = 0x0050;
 t['Pacute'] = 0x1E54;
 t['Pcircle'] = 0x24C5;
 t['Pdotaccent'] = 0x1E56;
 t['Pecyrillic'] = 0x041F;
 t['Peharmenian'] = 0x054A;
 t['Pemiddlehookcyrillic'] = 0x04A6;
 t['Phi'] = 0x03A6;
 t['Phook'] = 0x01A4;
 t['Pi'] = 0x03A0;
 t['Piwrarmenian'] = 0x0553;
 t['Pmonospace'] = 0xFF30;
 t['Psi'] = 0x03A8;
 t['Psicyrillic'] = 0x0470;
 t['Psmall'] = 0xF770;
 t['Q'] = 0x0051;
 t['Qcircle'] = 0x24C6;
 t['Qmonospace'] = 0xFF31;
 t['Qsmall'] = 0xF771;
 t['R'] = 0x0052;
 t['Raarmenian'] = 0x054C;
 t['Racute'] = 0x0154;
 t['Rcaron'] = 0x0158;
 t['Rcedilla'] = 0x0156;
 t['Rcircle'] = 0x24C7;
 t['Rcommaaccent'] = 0x0156;
 t['Rdblgrave'] = 0x0210;
 t['Rdotaccent'] = 0x1E58;
 t['Rdotbelow'] = 0x1E5A;
 t['Rdotbelowmacron'] = 0x1E5C;
 t['Reharmenian'] = 0x0550;
 t['Rfraktur'] = 0x211C;
 t['Rho'] = 0x03A1;
 t['Ringsmall'] = 0xF6FC;
 t['Rinvertedbreve'] = 0x0212;
 t['Rlinebelow'] = 0x1E5E;
 t['Rmonospace'] = 0xFF32;
 t['Rsmall'] = 0xF772;
 t['Rsmallinverted'] = 0x0281;
 t['Rsmallinvertedsuperior'] = 0x02B6;
 t['S'] = 0x0053;
 t['SF010000'] = 0x250C;
 t['SF020000'] = 0x2514;
 t['SF030000'] = 0x2510;
 t['SF040000'] = 0x2518;
 t['SF050000'] = 0x253C;
 t['SF060000'] = 0x252C;
 t['SF070000'] = 0x2534;
 t['SF080000'] = 0x251C;
 t['SF090000'] = 0x2524;
 t['SF100000'] = 0x2500;
 t['SF110000'] = 0x2502;
 t['SF190000'] = 0x2561;
 t['SF200000'] = 0x2562;
 t['SF210000'] = 0x2556;
 t['SF220000'] = 0x2555;
 t['SF230000'] = 0x2563;
 t['SF240000'] = 0x2551;
 t['SF250000'] = 0x2557;
 t['SF260000'] = 0x255D;
 t['SF270000'] = 0x255C;
 t['SF280000'] = 0x255B;
 t['SF360000'] = 0x255E;
 t['SF370000'] = 0x255F;
 t['SF380000'] = 0x255A;
 t['SF390000'] = 0x2554;
 t['SF400000'] = 0x2569;
 t['SF410000'] = 0x2566;
 t['SF420000'] = 0x2560;
 t['SF430000'] = 0x2550;
 t['SF440000'] = 0x256C;
 t['SF450000'] = 0x2567;
 t['SF460000'] = 0x2568;
 t['SF470000'] = 0x2564;
 t['SF480000'] = 0x2565;
 t['SF490000'] = 0x2559;
 t['SF500000'] = 0x2558;
 t['SF510000'] = 0x2552;
 t['SF520000'] = 0x2553;
 t['SF530000'] = 0x256B;
 t['SF540000'] = 0x256A;
 t['Sacute'] = 0x015A;
 t['Sacutedotaccent'] = 0x1E64;
 t['Sampigreek'] = 0x03E0;
 t['Scaron'] = 0x0160;
 t['Scarondotaccent'] = 0x1E66;
 t['Scaronsmall'] = 0xF6FD;
 t['Scedilla'] = 0x015E;
 t['Schwa'] = 0x018F;
 t['Schwacyrillic'] = 0x04D8;
 t['Schwadieresiscyrillic'] = 0x04DA;
 t['Scircle'] = 0x24C8;
 t['Scircumflex'] = 0x015C;
 t['Scommaaccent'] = 0x0218;
 t['Sdotaccent'] = 0x1E60;
 t['Sdotbelow'] = 0x1E62;
 t['Sdotbelowdotaccent'] = 0x1E68;
 t['Seharmenian'] = 0x054D;
 t['Sevenroman'] = 0x2166;
 t['Shaarmenian'] = 0x0547;
 t['Shacyrillic'] = 0x0428;
 t['Shchacyrillic'] = 0x0429;
 t['Sheicoptic'] = 0x03E2;
 t['Shhacyrillic'] = 0x04BA;
 t['Shimacoptic'] = 0x03EC;
 t['Sigma'] = 0x03A3;
 t['Sixroman'] = 0x2165;
 t['Smonospace'] = 0xFF33;
 t['Softsigncyrillic'] = 0x042C;
 t['Ssmall'] = 0xF773;
 t['Stigmagreek'] = 0x03DA;
 t['T'] = 0x0054;
 t['Tau'] = 0x03A4;
 t['Tbar'] = 0x0166;
 t['Tcaron'] = 0x0164;
 t['Tcedilla'] = 0x0162;
 t['Tcircle'] = 0x24C9;
 t['Tcircumflexbelow'] = 0x1E70;
 t['Tcommaaccent'] = 0x0162;
 t['Tdotaccent'] = 0x1E6A;
 t['Tdotbelow'] = 0x1E6C;
 t['Tecyrillic'] = 0x0422;
 t['Tedescendercyrillic'] = 0x04AC;
 t['Tenroman'] = 0x2169;
 t['Tetsecyrillic'] = 0x04B4;
 t['Theta'] = 0x0398;
 t['Thook'] = 0x01AC;
 t['Thorn'] = 0x00DE;
 t['Thornsmall'] = 0xF7FE;
 t['Threeroman'] = 0x2162;
 t['Tildesmall'] = 0xF6FE;
 t['Tiwnarmenian'] = 0x054F;
 t['Tlinebelow'] = 0x1E6E;
 t['Tmonospace'] = 0xFF34;
 t['Toarmenian'] = 0x0539;
 t['Tonefive'] = 0x01BC;
 t['Tonesix'] = 0x0184;
 t['Tonetwo'] = 0x01A7;
 t['Tretroflexhook'] = 0x01AE;
 t['Tsecyrillic'] = 0x0426;
 t['Tshecyrillic'] = 0x040B;
 t['Tsmall'] = 0xF774;
 t['Twelveroman'] = 0x216B;
 t['Tworoman'] = 0x2161;
 t['U'] = 0x0055;
 t['Uacute'] = 0x00DA;
 t['Uacutesmall'] = 0xF7FA;
 t['Ubreve'] = 0x016C;
 t['Ucaron'] = 0x01D3;
 t['Ucircle'] = 0x24CA;
 t['Ucircumflex'] = 0x00DB;
 t['Ucircumflexbelow'] = 0x1E76;
 t['Ucircumflexsmall'] = 0xF7FB;
 t['Ucyrillic'] = 0x0423;
 t['Udblacute'] = 0x0170;
 t['Udblgrave'] = 0x0214;
 t['Udieresis'] = 0x00DC;
 t['Udieresisacute'] = 0x01D7;
 t['Udieresisbelow'] = 0x1E72;
 t['Udieresiscaron'] = 0x01D9;
 t['Udieresiscyrillic'] = 0x04F0;
 t['Udieresisgrave'] = 0x01DB;
 t['Udieresismacron'] = 0x01D5;
 t['Udieresissmall'] = 0xF7FC;
 t['Udotbelow'] = 0x1EE4;
 t['Ugrave'] = 0x00D9;
 t['Ugravesmall'] = 0xF7F9;
 t['Uhookabove'] = 0x1EE6;
 t['Uhorn'] = 0x01AF;
 t['Uhornacute'] = 0x1EE8;
 t['Uhorndotbelow'] = 0x1EF0;
 t['Uhorngrave'] = 0x1EEA;
 t['Uhornhookabove'] = 0x1EEC;
 t['Uhorntilde'] = 0x1EEE;
 t['Uhungarumlaut'] = 0x0170;
 t['Uhungarumlautcyrillic'] = 0x04F2;
 t['Uinvertedbreve'] = 0x0216;
 t['Ukcyrillic'] = 0x0478;
 t['Umacron'] = 0x016A;
 t['Umacroncyrillic'] = 0x04EE;
 t['Umacrondieresis'] = 0x1E7A;
 t['Umonospace'] = 0xFF35;
 t['Uogonek'] = 0x0172;
 t['Upsilon'] = 0x03A5;
 t['Upsilon1'] = 0x03D2;
 t['Upsilonacutehooksymbolgreek'] = 0x03D3;
 t['Upsilonafrican'] = 0x01B1;
 t['Upsilondieresis'] = 0x03AB;
 t['Upsilondieresishooksymbolgreek'] = 0x03D4;
 t['Upsilonhooksymbol'] = 0x03D2;
 t['Upsilontonos'] = 0x038E;
 t['Uring'] = 0x016E;
 t['Ushortcyrillic'] = 0x040E;
 t['Usmall'] = 0xF775;
 t['Ustraightcyrillic'] = 0x04AE;
 t['Ustraightstrokecyrillic'] = 0x04B0;
 t['Utilde'] = 0x0168;
 t['Utildeacute'] = 0x1E78;
 t['Utildebelow'] = 0x1E74;
 t['V'] = 0x0056;
 t['Vcircle'] = 0x24CB;
 t['Vdotbelow'] = 0x1E7E;
 t['Vecyrillic'] = 0x0412;
 t['Vewarmenian'] = 0x054E;
 t['Vhook'] = 0x01B2;
 t['Vmonospace'] = 0xFF36;
 t['Voarmenian'] = 0x0548;
 t['Vsmall'] = 0xF776;
 t['Vtilde'] = 0x1E7C;
 t['W'] = 0x0057;
 t['Wacute'] = 0x1E82;
 t['Wcircle'] = 0x24CC;
 t['Wcircumflex'] = 0x0174;
 t['Wdieresis'] = 0x1E84;
 t['Wdotaccent'] = 0x1E86;
 t['Wdotbelow'] = 0x1E88;
 t['Wgrave'] = 0x1E80;
 t['Wmonospace'] = 0xFF37;
 t['Wsmall'] = 0xF777;
 t['X'] = 0x0058;
 t['Xcircle'] = 0x24CD;
 t['Xdieresis'] = 0x1E8C;
 t['Xdotaccent'] = 0x1E8A;
 t['Xeharmenian'] = 0x053D;
 t['Xi'] = 0x039E;
 t['Xmonospace'] = 0xFF38;
 t['Xsmall'] = 0xF778;
 t['Y'] = 0x0059;
 t['Yacute'] = 0x00DD;
 t['Yacutesmall'] = 0xF7FD;
 t['Yatcyrillic'] = 0x0462;
 t['Ycircle'] = 0x24CE;
 t['Ycircumflex'] = 0x0176;
 t['Ydieresis'] = 0x0178;
 t['Ydieresissmall'] = 0xF7FF;
 t['Ydotaccent'] = 0x1E8E;
 t['Ydotbelow'] = 0x1EF4;
 t['Yericyrillic'] = 0x042B;
 t['Yerudieresiscyrillic'] = 0x04F8;
 t['Ygrave'] = 0x1EF2;
 t['Yhook'] = 0x01B3;
 t['Yhookabove'] = 0x1EF6;
 t['Yiarmenian'] = 0x0545;
 t['Yicyrillic'] = 0x0407;
 t['Yiwnarmenian'] = 0x0552;
 t['Ymonospace'] = 0xFF39;
 t['Ysmall'] = 0xF779;
 t['Ytilde'] = 0x1EF8;
 t['Yusbigcyrillic'] = 0x046A;
 t['Yusbigiotifiedcyrillic'] = 0x046C;
 t['Yuslittlecyrillic'] = 0x0466;
 t['Yuslittleiotifiedcyrillic'] = 0x0468;
 t['Z'] = 0x005A;
 t['Zaarmenian'] = 0x0536;
 t['Zacute'] = 0x0179;
 t['Zcaron'] = 0x017D;
 t['Zcaronsmall'] = 0xF6FF;
 t['Zcircle'] = 0x24CF;
 t['Zcircumflex'] = 0x1E90;
 t['Zdot'] = 0x017B;
 t['Zdotaccent'] = 0x017B;
 t['Zdotbelow'] = 0x1E92;
 t['Zecyrillic'] = 0x0417;
 t['Zedescendercyrillic'] = 0x0498;
 t['Zedieresiscyrillic'] = 0x04DE;
 t['Zeta'] = 0x0396;
 t['Zhearmenian'] = 0x053A;
 t['Zhebrevecyrillic'] = 0x04C1;
 t['Zhecyrillic'] = 0x0416;
 t['Zhedescendercyrillic'] = 0x0496;
 t['Zhedieresiscyrillic'] = 0x04DC;
 t['Zlinebelow'] = 0x1E94;
 t['Zmonospace'] = 0xFF3A;
 t['Zsmall'] = 0xF77A;
 t['Zstroke'] = 0x01B5;
 t['a'] = 0x0061;
 t['aabengali'] = 0x0986;
 t['aacute'] = 0x00E1;
 t['aadeva'] = 0x0906;
 t['aagujarati'] = 0x0A86;
 t['aagurmukhi'] = 0x0A06;
 t['aamatragurmukhi'] = 0x0A3E;
 t['aarusquare'] = 0x3303;
 t['aavowelsignbengali'] = 0x09BE;
 t['aavowelsigndeva'] = 0x093E;
 t['aavowelsigngujarati'] = 0x0ABE;
 t['abbreviationmarkarmenian'] = 0x055F;
 t['abbreviationsigndeva'] = 0x0970;
 t['abengali'] = 0x0985;
 t['abopomofo'] = 0x311A;
 t['abreve'] = 0x0103;
 t['abreveacute'] = 0x1EAF;
 t['abrevecyrillic'] = 0x04D1;
 t['abrevedotbelow'] = 0x1EB7;
 t['abrevegrave'] = 0x1EB1;
 t['abrevehookabove'] = 0x1EB3;
 t['abrevetilde'] = 0x1EB5;
 t['acaron'] = 0x01CE;
 t['acircle'] = 0x24D0;
 t['acircumflex'] = 0x00E2;
 t['acircumflexacute'] = 0x1EA5;
 t['acircumflexdotbelow'] = 0x1EAD;
 t['acircumflexgrave'] = 0x1EA7;
 t['acircumflexhookabove'] = 0x1EA9;
 t['acircumflextilde'] = 0x1EAB;
 t['acute'] = 0x00B4;
 t['acutebelowcmb'] = 0x0317;
 t['acutecmb'] = 0x0301;
 t['acutecomb'] = 0x0301;
 t['acutedeva'] = 0x0954;
 t['acutelowmod'] = 0x02CF;
 t['acutetonecmb'] = 0x0341;
 t['acyrillic'] = 0x0430;
 t['adblgrave'] = 0x0201;
 t['addakgurmukhi'] = 0x0A71;
 t['adeva'] = 0x0905;
 t['adieresis'] = 0x00E4;
 t['adieresiscyrillic'] = 0x04D3;
 t['adieresismacron'] = 0x01DF;
 t['adotbelow'] = 0x1EA1;
 t['adotmacron'] = 0x01E1;
 t['ae'] = 0x00E6;
 t['aeacute'] = 0x01FD;
 t['aekorean'] = 0x3150;
 t['aemacron'] = 0x01E3;
 t['afii00208'] = 0x2015;
 t['afii08941'] = 0x20A4;
 t['afii10017'] = 0x0410;
 t['afii10018'] = 0x0411;
 t['afii10019'] = 0x0412;
 t['afii10020'] = 0x0413;
 t['afii10021'] = 0x0414;
 t['afii10022'] = 0x0415;
 t['afii10023'] = 0x0401;
 t['afii10024'] = 0x0416;
 t['afii10025'] = 0x0417;
 t['afii10026'] = 0x0418;
 t['afii10027'] = 0x0419;
 t['afii10028'] = 0x041A;
 t['afii10029'] = 0x041B;
 t['afii10030'] = 0x041C;
 t['afii10031'] = 0x041D;
 t['afii10032'] = 0x041E;
 t['afii10033'] = 0x041F;
 t['afii10034'] = 0x0420;
 t['afii10035'] = 0x0421;
 t['afii10036'] = 0x0422;
 t['afii10037'] = 0x0423;
 t['afii10038'] = 0x0424;
 t['afii10039'] = 0x0425;
 t['afii10040'] = 0x0426;
 t['afii10041'] = 0x0427;
 t['afii10042'] = 0x0428;
 t['afii10043'] = 0x0429;
 t['afii10044'] = 0x042A;
 t['afii10045'] = 0x042B;
 t['afii10046'] = 0x042C;
 t['afii10047'] = 0x042D;
 t['afii10048'] = 0x042E;
 t['afii10049'] = 0x042F;
 t['afii10050'] = 0x0490;
 t['afii10051'] = 0x0402;
 t['afii10052'] = 0x0403;
 t['afii10053'] = 0x0404;
 t['afii10054'] = 0x0405;
 t['afii10055'] = 0x0406;
 t['afii10056'] = 0x0407;
 t['afii10057'] = 0x0408;
 t['afii10058'] = 0x0409;
 t['afii10059'] = 0x040A;
 t['afii10060'] = 0x040B;
 t['afii10061'] = 0x040C;
 t['afii10062'] = 0x040E;
 t['afii10063'] = 0xF6C4;
 t['afii10064'] = 0xF6C5;
 t['afii10065'] = 0x0430;
 t['afii10066'] = 0x0431;
 t['afii10067'] = 0x0432;
 t['afii10068'] = 0x0433;
 t['afii10069'] = 0x0434;
 t['afii10070'] = 0x0435;
 t['afii10071'] = 0x0451;
 t['afii10072'] = 0x0436;
 t['afii10073'] = 0x0437;
 t['afii10074'] = 0x0438;
 t['afii10075'] = 0x0439;
 t['afii10076'] = 0x043A;
 t['afii10077'] = 0x043B;
 t['afii10078'] = 0x043C;
 t['afii10079'] = 0x043D;
 t['afii10080'] = 0x043E;
 t['afii10081'] = 0x043F;
 t['afii10082'] = 0x0440;
 t['afii10083'] = 0x0441;
 t['afii10084'] = 0x0442;
 t['afii10085'] = 0x0443;
 t['afii10086'] = 0x0444;
 t['afii10087'] = 0x0445;
 t['afii10088'] = 0x0446;
 t['afii10089'] = 0x0447;
 t['afii10090'] = 0x0448;
 t['afii10091'] = 0x0449;
 t['afii10092'] = 0x044A;
 t['afii10093'] = 0x044B;
 t['afii10094'] = 0x044C;
 t['afii10095'] = 0x044D;
 t['afii10096'] = 0x044E;
 t['afii10097'] = 0x044F;
 t['afii10098'] = 0x0491;
 t['afii10099'] = 0x0452;
 t['afii10100'] = 0x0453;
 t['afii10101'] = 0x0454;
 t['afii10102'] = 0x0455;
 t['afii10103'] = 0x0456;
 t['afii10104'] = 0x0457;
 t['afii10105'] = 0x0458;
 t['afii10106'] = 0x0459;
 t['afii10107'] = 0x045A;
 t['afii10108'] = 0x045B;
 t['afii10109'] = 0x045C;
 t['afii10110'] = 0x045E;
 t['afii10145'] = 0x040F;
 t['afii10146'] = 0x0462;
 t['afii10147'] = 0x0472;
 t['afii10148'] = 0x0474;
 t['afii10192'] = 0xF6C6;
 t['afii10193'] = 0x045F;
 t['afii10194'] = 0x0463;
 t['afii10195'] = 0x0473;
 t['afii10196'] = 0x0475;
 t['afii10831'] = 0xF6C7;
 t['afii10832'] = 0xF6C8;
 t['afii10846'] = 0x04D9;
 t['afii299'] = 0x200E;
 t['afii300'] = 0x200F;
 t['afii301'] = 0x200D;
 t['afii57381'] = 0x066A;
 t['afii57388'] = 0x060C;
 t['afii57392'] = 0x0660;
 t['afii57393'] = 0x0661;
 t['afii57394'] = 0x0662;
 t['afii57395'] = 0x0663;
 t['afii57396'] = 0x0664;
 t['afii57397'] = 0x0665;
 t['afii57398'] = 0x0666;
 t['afii57399'] = 0x0667;
 t['afii57400'] = 0x0668;
 t['afii57401'] = 0x0669;
 t['afii57403'] = 0x061B;
 t['afii57407'] = 0x061F;
 t['afii57409'] = 0x0621;
 t['afii57410'] = 0x0622;
 t['afii57411'] = 0x0623;
 t['afii57412'] = 0x0624;
 t['afii57413'] = 0x0625;
 t['afii57414'] = 0x0626;
 t['afii57415'] = 0x0627;
 t['afii57416'] = 0x0628;
 t['afii57417'] = 0x0629;
 t['afii57418'] = 0x062A;
 t['afii57419'] = 0x062B;
 t['afii57420'] = 0x062C;
 t['afii57421'] = 0x062D;
 t['afii57422'] = 0x062E;
 t['afii57423'] = 0x062F;
 t['afii57424'] = 0x0630;
 t['afii57425'] = 0x0631;
 t['afii57426'] = 0x0632;
 t['afii57427'] = 0x0633;
 t['afii57428'] = 0x0634;
 t['afii57429'] = 0x0635;
 t['afii57430'] = 0x0636;
 t['afii57431'] = 0x0637;
 t['afii57432'] = 0x0638;
 t['afii57433'] = 0x0639;
 t['afii57434'] = 0x063A;
 t['afii57440'] = 0x0640;
 t['afii57441'] = 0x0641;
 t['afii57442'] = 0x0642;
 t['afii57443'] = 0x0643;
 t['afii57444'] = 0x0644;
 t['afii57445'] = 0x0645;
 t['afii57446'] = 0x0646;
 t['afii57448'] = 0x0648;
 t['afii57449'] = 0x0649;
 t['afii57450'] = 0x064A;
 t['afii57451'] = 0x064B;
 t['afii57452'] = 0x064C;
 t['afii57453'] = 0x064D;
 t['afii57454'] = 0x064E;
 t['afii57455'] = 0x064F;
 t['afii57456'] = 0x0650;
 t['afii57457'] = 0x0651;
 t['afii57458'] = 0x0652;
 t['afii57470'] = 0x0647;
 t['afii57505'] = 0x06A4;
 t['afii57506'] = 0x067E;
 t['afii57507'] = 0x0686;
 t['afii57508'] = 0x0698;
 t['afii57509'] = 0x06AF;
 t['afii57511'] = 0x0679;
 t['afii57512'] = 0x0688;
 t['afii57513'] = 0x0691;
 t['afii57514'] = 0x06BA;
 t['afii57519'] = 0x06D2;
 t['afii57534'] = 0x06D5;
 t['afii57636'] = 0x20AA;
 t['afii57645'] = 0x05BE;
 t['afii57658'] = 0x05C3;
 t['afii57664'] = 0x05D0;
 t['afii57665'] = 0x05D1;
 t['afii57666'] = 0x05D2;
 t['afii57667'] = 0x05D3;
 t['afii57668'] = 0x05D4;
 t['afii57669'] = 0x05D5;
 t['afii57670'] = 0x05D6;
 t['afii57671'] = 0x05D7;
 t['afii57672'] = 0x05D8;
 t['afii57673'] = 0x05D9;
 t['afii57674'] = 0x05DA;
 t['afii57675'] = 0x05DB;
 t['afii57676'] = 0x05DC;
 t['afii57677'] = 0x05DD;
 t['afii57678'] = 0x05DE;
 t['afii57679'] = 0x05DF;
 t['afii57680'] = 0x05E0;
 t['afii57681'] = 0x05E1;
 t['afii57682'] = 0x05E2;
 t['afii57683'] = 0x05E3;
 t['afii57684'] = 0x05E4;
 t['afii57685'] = 0x05E5;
 t['afii57686'] = 0x05E6;
 t['afii57687'] = 0x05E7;
 t['afii57688'] = 0x05E8;
 t['afii57689'] = 0x05E9;
 t['afii57690'] = 0x05EA;
 t['afii57694'] = 0xFB2A;
 t['afii57695'] = 0xFB2B;
 t['afii57700'] = 0xFB4B;
 t['afii57705'] = 0xFB1F;
 t['afii57716'] = 0x05F0;
 t['afii57717'] = 0x05F1;
 t['afii57718'] = 0x05F2;
 t['afii57723'] = 0xFB35;
 t['afii57793'] = 0x05B4;
 t['afii57794'] = 0x05B5;
 t['afii57795'] = 0x05B6;
 t['afii57796'] = 0x05BB;
 t['afii57797'] = 0x05B8;
 t['afii57798'] = 0x05B7;
 t['afii57799'] = 0x05B0;
 t['afii57800'] = 0x05B2;
 t['afii57801'] = 0x05B1;
 t['afii57802'] = 0x05B3;
 t['afii57803'] = 0x05C2;
 t['afii57804'] = 0x05C1;
 t['afii57806'] = 0x05B9;
 t['afii57807'] = 0x05BC;
 t['afii57839'] = 0x05BD;
 t['afii57841'] = 0x05BF;
 t['afii57842'] = 0x05C0;
 t['afii57929'] = 0x02BC;
 t['afii61248'] = 0x2105;
 t['afii61289'] = 0x2113;
 t['afii61352'] = 0x2116;
 t['afii61573'] = 0x202C;
 t['afii61574'] = 0x202D;
 t['afii61575'] = 0x202E;
 t['afii61664'] = 0x200C;
 t['afii63167'] = 0x066D;
 t['afii64937'] = 0x02BD;
 t['agrave'] = 0x00E0;
 t['agujarati'] = 0x0A85;
 t['agurmukhi'] = 0x0A05;
 t['ahiragana'] = 0x3042;
 t['ahookabove'] = 0x1EA3;
 t['aibengali'] = 0x0990;
 t['aibopomofo'] = 0x311E;
 t['aideva'] = 0x0910;
 t['aiecyrillic'] = 0x04D5;
 t['aigujarati'] = 0x0A90;
 t['aigurmukhi'] = 0x0A10;
 t['aimatragurmukhi'] = 0x0A48;
 t['ainarabic'] = 0x0639;
 t['ainfinalarabic'] = 0xFECA;
 t['aininitialarabic'] = 0xFECB;
 t['ainmedialarabic'] = 0xFECC;
 t['ainvertedbreve'] = 0x0203;
 t['aivowelsignbengali'] = 0x09C8;
 t['aivowelsigndeva'] = 0x0948;
 t['aivowelsigngujarati'] = 0x0AC8;
 t['akatakana'] = 0x30A2;
 t['akatakanahalfwidth'] = 0xFF71;
 t['akorean'] = 0x314F;
 t['alef'] = 0x05D0;
 t['alefarabic'] = 0x0627;
 t['alefdageshhebrew'] = 0xFB30;
 t['aleffinalarabic'] = 0xFE8E;
 t['alefhamzaabovearabic'] = 0x0623;
 t['alefhamzaabovefinalarabic'] = 0xFE84;
 t['alefhamzabelowarabic'] = 0x0625;
 t['alefhamzabelowfinalarabic'] = 0xFE88;
 t['alefhebrew'] = 0x05D0;
 t['aleflamedhebrew'] = 0xFB4F;
 t['alefmaddaabovearabic'] = 0x0622;
 t['alefmaddaabovefinalarabic'] = 0xFE82;
 t['alefmaksuraarabic'] = 0x0649;
 t['alefmaksurafinalarabic'] = 0xFEF0;
 t['alefmaksurainitialarabic'] = 0xFEF3;
 t['alefmaksuramedialarabic'] = 0xFEF4;
 t['alefpatahhebrew'] = 0xFB2E;
 t['alefqamatshebrew'] = 0xFB2F;
 t['aleph'] = 0x2135;
 t['allequal'] = 0x224C;
 t['alpha'] = 0x03B1;
 t['alphatonos'] = 0x03AC;
 t['amacron'] = 0x0101;
 t['amonospace'] = 0xFF41;
 t['ampersand'] = 0x0026;
 t['ampersandmonospace'] = 0xFF06;
 t['ampersandsmall'] = 0xF726;
 t['amsquare'] = 0x33C2;
 t['anbopomofo'] = 0x3122;
 t['angbopomofo'] = 0x3124;
 t['angbracketleft'] = 0x3008;
 t['angbracketright'] = 0x3009;
 t['angkhankhuthai'] = 0x0E5A;
 t['angle'] = 0x2220;
 t['anglebracketleft'] = 0x3008;
 t['anglebracketleftvertical'] = 0xFE3F;
 t['anglebracketright'] = 0x3009;
 t['anglebracketrightvertical'] = 0xFE40;
 t['angleleft'] = 0x2329;
 t['angleright'] = 0x232A;
 t['angstrom'] = 0x212B;
 t['anoteleia'] = 0x0387;
 t['anudattadeva'] = 0x0952;
 t['anusvarabengali'] = 0x0982;
 t['anusvaradeva'] = 0x0902;
 t['anusvaragujarati'] = 0x0A82;
 t['aogonek'] = 0x0105;
 t['apaatosquare'] = 0x3300;
 t['aparen'] = 0x249C;
 t['apostrophearmenian'] = 0x055A;
 t['apostrophemod'] = 0x02BC;
 t['apple'] = 0xF8FF;
 t['approaches'] = 0x2250;
 t['approxequal'] = 0x2248;
 t['approxequalorimage'] = 0x2252;
 t['approximatelyequal'] = 0x2245;
 t['araeaekorean'] = 0x318E;
 t['araeakorean'] = 0x318D;
 t['arc'] = 0x2312;
 t['arighthalfring'] = 0x1E9A;
 t['aring'] = 0x00E5;
 t['aringacute'] = 0x01FB;
 t['aringbelow'] = 0x1E01;
 t['arrowboth'] = 0x2194;
 t['arrowdashdown'] = 0x21E3;
 t['arrowdashleft'] = 0x21E0;
 t['arrowdashright'] = 0x21E2;
 t['arrowdashup'] = 0x21E1;
 t['arrowdblboth'] = 0x21D4;
 t['arrowdbldown'] = 0x21D3;
 t['arrowdblleft'] = 0x21D0;
 t['arrowdblright'] = 0x21D2;
 t['arrowdblup'] = 0x21D1;
 t['arrowdown'] = 0x2193;
 t['arrowdownleft'] = 0x2199;
 t['arrowdownright'] = 0x2198;
 t['arrowdownwhite'] = 0x21E9;
 t['arrowheaddownmod'] = 0x02C5;
 t['arrowheadleftmod'] = 0x02C2;
 t['arrowheadrightmod'] = 0x02C3;
 t['arrowheadupmod'] = 0x02C4;
 t['arrowhorizex'] = 0xF8E7;
 t['arrowleft'] = 0x2190;
 t['arrowleftdbl'] = 0x21D0;
 t['arrowleftdblstroke'] = 0x21CD;
 t['arrowleftoverright'] = 0x21C6;
 t['arrowleftwhite'] = 0x21E6;
 t['arrowright'] = 0x2192;
 t['arrowrightdblstroke'] = 0x21CF;
 t['arrowrightheavy'] = 0x279E;
 t['arrowrightoverleft'] = 0x21C4;
 t['arrowrightwhite'] = 0x21E8;
 t['arrowtableft'] = 0x21E4;
 t['arrowtabright'] = 0x21E5;
 t['arrowup'] = 0x2191;
 t['arrowupdn'] = 0x2195;
 t['arrowupdnbse'] = 0x21A8;
 t['arrowupdownbase'] = 0x21A8;
 t['arrowupleft'] = 0x2196;
 t['arrowupleftofdown'] = 0x21C5;
 t['arrowupright'] = 0x2197;
 t['arrowupwhite'] = 0x21E7;
 t['arrowvertex'] = 0xF8E6;
 t['asciicircum'] = 0x005E;
 t['asciicircummonospace'] = 0xFF3E;
 t['asciitilde'] = 0x007E;
 t['asciitildemonospace'] = 0xFF5E;
 t['ascript'] = 0x0251;
 t['ascriptturned'] = 0x0252;
 t['asmallhiragana'] = 0x3041;
 t['asmallkatakana'] = 0x30A1;
 t['asmallkatakanahalfwidth'] = 0xFF67;
 t['asterisk'] = 0x002A;
 t['asteriskaltonearabic'] = 0x066D;
 t['asteriskarabic'] = 0x066D;
 t['asteriskmath'] = 0x2217;
 t['asteriskmonospace'] = 0xFF0A;
 t['asterisksmall'] = 0xFE61;
 t['asterism'] = 0x2042;
 t['asuperior'] = 0xF6E9;
 t['asymptoticallyequal'] = 0x2243;
 t['at'] = 0x0040;
 t['atilde'] = 0x00E3;
 t['atmonospace'] = 0xFF20;
 t['atsmall'] = 0xFE6B;
 t['aturned'] = 0x0250;
 t['aubengali'] = 0x0994;
 t['aubopomofo'] = 0x3120;
 t['audeva'] = 0x0914;
 t['augujarati'] = 0x0A94;
 t['augurmukhi'] = 0x0A14;
 t['aulengthmarkbengali'] = 0x09D7;
 t['aumatragurmukhi'] = 0x0A4C;
 t['auvowelsignbengali'] = 0x09CC;
 t['auvowelsigndeva'] = 0x094C;
 t['auvowelsigngujarati'] = 0x0ACC;
 t['avagrahadeva'] = 0x093D;
 t['aybarmenian'] = 0x0561;
 t['ayin'] = 0x05E2;
 t['ayinaltonehebrew'] = 0xFB20;
 t['ayinhebrew'] = 0x05E2;
 t['b'] = 0x0062;
 t['babengali'] = 0x09AC;
 t['backslash'] = 0x005C;
 t['backslashmonospace'] = 0xFF3C;
 t['badeva'] = 0x092C;
 t['bagujarati'] = 0x0AAC;
 t['bagurmukhi'] = 0x0A2C;
 t['bahiragana'] = 0x3070;
 t['bahtthai'] = 0x0E3F;
 t['bakatakana'] = 0x30D0;
 t['bar'] = 0x007C;
 t['barmonospace'] = 0xFF5C;
 t['bbopomofo'] = 0x3105;
 t['bcircle'] = 0x24D1;
 t['bdotaccent'] = 0x1E03;
 t['bdotbelow'] = 0x1E05;
 t['beamedsixteenthnotes'] = 0x266C;
 t['because'] = 0x2235;
 t['becyrillic'] = 0x0431;
 t['beharabic'] = 0x0628;
 t['behfinalarabic'] = 0xFE90;
 t['behinitialarabic'] = 0xFE91;
 t['behiragana'] = 0x3079;
 t['behmedialarabic'] = 0xFE92;
 t['behmeeminitialarabic'] = 0xFC9F;
 t['behmeemisolatedarabic'] = 0xFC08;
 t['behnoonfinalarabic'] = 0xFC6D;
 t['bekatakana'] = 0x30D9;
 t['benarmenian'] = 0x0562;
 t['bet'] = 0x05D1;
 t['beta'] = 0x03B2;
 t['betasymbolgreek'] = 0x03D0;
 t['betdagesh'] = 0xFB31;
 t['betdageshhebrew'] = 0xFB31;
 t['bethebrew'] = 0x05D1;
 t['betrafehebrew'] = 0xFB4C;
 t['bhabengali'] = 0x09AD;
 t['bhadeva'] = 0x092D;
 t['bhagujarati'] = 0x0AAD;
 t['bhagurmukhi'] = 0x0A2D;
 t['bhook'] = 0x0253;
 t['bihiragana'] = 0x3073;
 t['bikatakana'] = 0x30D3;
 t['bilabialclick'] = 0x0298;
 t['bindigurmukhi'] = 0x0A02;
 t['birusquare'] = 0x3331;
 t['blackcircle'] = 0x25CF;
 t['blackdiamond'] = 0x25C6;
 t['blackdownpointingtriangle'] = 0x25BC;
 t['blackleftpointingpointer'] = 0x25C4;
 t['blackleftpointingtriangle'] = 0x25C0;
 t['blacklenticularbracketleft'] = 0x3010;
 t['blacklenticularbracketleftvertical'] = 0xFE3B;
 t['blacklenticularbracketright'] = 0x3011;
 t['blacklenticularbracketrightvertical'] = 0xFE3C;
 t['blacklowerlefttriangle'] = 0x25E3;
 t['blacklowerrighttriangle'] = 0x25E2;
 t['blackrectangle'] = 0x25AC;
 t['blackrightpointingpointer'] = 0x25BA;
 t['blackrightpointingtriangle'] = 0x25B6;
 t['blacksmallsquare'] = 0x25AA;
 t['blacksmilingface'] = 0x263B;
 t['blacksquare'] = 0x25A0;
 t['blackstar'] = 0x2605;
 t['blackupperlefttriangle'] = 0x25E4;
 t['blackupperrighttriangle'] = 0x25E5;
 t['blackuppointingsmalltriangle'] = 0x25B4;
 t['blackuppointingtriangle'] = 0x25B2;
 t['blank'] = 0x2423;
 t['blinebelow'] = 0x1E07;
 t['block'] = 0x2588;
 t['bmonospace'] = 0xFF42;
 t['bobaimaithai'] = 0x0E1A;
 t['bohiragana'] = 0x307C;
 t['bokatakana'] = 0x30DC;
 t['bparen'] = 0x249D;
 t['bqsquare'] = 0x33C3;
 t['braceex'] = 0xF8F4;
 t['braceleft'] = 0x007B;
 t['braceleftbt'] = 0xF8F3;
 t['braceleftmid'] = 0xF8F2;
 t['braceleftmonospace'] = 0xFF5B;
 t['braceleftsmall'] = 0xFE5B;
 t['bracelefttp'] = 0xF8F1;
 t['braceleftvertical'] = 0xFE37;
 t['braceright'] = 0x007D;
 t['bracerightbt'] = 0xF8FE;
 t['bracerightmid'] = 0xF8FD;
 t['bracerightmonospace'] = 0xFF5D;
 t['bracerightsmall'] = 0xFE5C;
 t['bracerighttp'] = 0xF8FC;
 t['bracerightvertical'] = 0xFE38;
 t['bracketleft'] = 0x005B;
 t['bracketleftbt'] = 0xF8F0;
 t['bracketleftex'] = 0xF8EF;
 t['bracketleftmonospace'] = 0xFF3B;
 t['bracketlefttp'] = 0xF8EE;
 t['bracketright'] = 0x005D;
 t['bracketrightbt'] = 0xF8FB;
 t['bracketrightex'] = 0xF8FA;
 t['bracketrightmonospace'] = 0xFF3D;
 t['bracketrighttp'] = 0xF8F9;
 t['breve'] = 0x02D8;
 t['brevebelowcmb'] = 0x032E;
 t['brevecmb'] = 0x0306;
 t['breveinvertedbelowcmb'] = 0x032F;
 t['breveinvertedcmb'] = 0x0311;
 t['breveinverteddoublecmb'] = 0x0361;
 t['bridgebelowcmb'] = 0x032A;
 t['bridgeinvertedbelowcmb'] = 0x033A;
 t['brokenbar'] = 0x00A6;
 t['bstroke'] = 0x0180;
 t['bsuperior'] = 0xF6EA;
 t['btopbar'] = 0x0183;
 t['buhiragana'] = 0x3076;
 t['bukatakana'] = 0x30D6;
 t['bullet'] = 0x2022;
 t['bulletinverse'] = 0x25D8;
 t['bulletoperator'] = 0x2219;
 t['bullseye'] = 0x25CE;
 t['c'] = 0x0063;
 t['caarmenian'] = 0x056E;
 t['cabengali'] = 0x099A;
 t['cacute'] = 0x0107;
 t['cadeva'] = 0x091A;
 t['cagujarati'] = 0x0A9A;
 t['cagurmukhi'] = 0x0A1A;
 t['calsquare'] = 0x3388;
 t['candrabindubengali'] = 0x0981;
 t['candrabinducmb'] = 0x0310;
 t['candrabindudeva'] = 0x0901;
 t['candrabindugujarati'] = 0x0A81;
 t['capslock'] = 0x21EA;
 t['careof'] = 0x2105;
 t['caron'] = 0x02C7;
 t['caronbelowcmb'] = 0x032C;
 t['caroncmb'] = 0x030C;
 t['carriagereturn'] = 0x21B5;
 t['cbopomofo'] = 0x3118;
 t['ccaron'] = 0x010D;
 t['ccedilla'] = 0x00E7;
 t['ccedillaacute'] = 0x1E09;
 t['ccircle'] = 0x24D2;
 t['ccircumflex'] = 0x0109;
 t['ccurl'] = 0x0255;
 t['cdot'] = 0x010B;
 t['cdotaccent'] = 0x010B;
 t['cdsquare'] = 0x33C5;
 t['cedilla'] = 0x00B8;
 t['cedillacmb'] = 0x0327;
 t['cent'] = 0x00A2;
 t['centigrade'] = 0x2103;
 t['centinferior'] = 0xF6DF;
 t['centmonospace'] = 0xFFE0;
 t['centoldstyle'] = 0xF7A2;
 t['centsuperior'] = 0xF6E0;
 t['chaarmenian'] = 0x0579;
 t['chabengali'] = 0x099B;
 t['chadeva'] = 0x091B;
 t['chagujarati'] = 0x0A9B;
 t['chagurmukhi'] = 0x0A1B;
 t['chbopomofo'] = 0x3114;
 t['cheabkhasiancyrillic'] = 0x04BD;
 t['checkmark'] = 0x2713;
 t['checyrillic'] = 0x0447;
 t['chedescenderabkhasiancyrillic'] = 0x04BF;
 t['chedescendercyrillic'] = 0x04B7;
 t['chedieresiscyrillic'] = 0x04F5;
 t['cheharmenian'] = 0x0573;
 t['chekhakassiancyrillic'] = 0x04CC;
 t['cheverticalstrokecyrillic'] = 0x04B9;
 t['chi'] = 0x03C7;
 t['chieuchacirclekorean'] = 0x3277;
 t['chieuchaparenkorean'] = 0x3217;
 t['chieuchcirclekorean'] = 0x3269;
 t['chieuchkorean'] = 0x314A;
 t['chieuchparenkorean'] = 0x3209;
 t['chochangthai'] = 0x0E0A;
 t['chochanthai'] = 0x0E08;
 t['chochingthai'] = 0x0E09;
 t['chochoethai'] = 0x0E0C;
 t['chook'] = 0x0188;
 t['cieucacirclekorean'] = 0x3276;
 t['cieucaparenkorean'] = 0x3216;
 t['cieuccirclekorean'] = 0x3268;
 t['cieuckorean'] = 0x3148;
 t['cieucparenkorean'] = 0x3208;
 t['cieucuparenkorean'] = 0x321C;
 t['circle'] = 0x25CB;
 t['circlecopyrt'] = 0x00A9;
 t['circlemultiply'] = 0x2297;
 t['circleot'] = 0x2299;
 t['circleplus'] = 0x2295;
 t['circlepostalmark'] = 0x3036;
 t['circlewithlefthalfblack'] = 0x25D0;
 t['circlewithrighthalfblack'] = 0x25D1;
 t['circumflex'] = 0x02C6;
 t['circumflexbelowcmb'] = 0x032D;
 t['circumflexcmb'] = 0x0302;
 t['clear'] = 0x2327;
 t['clickalveolar'] = 0x01C2;
 t['clickdental'] = 0x01C0;
 t['clicklateral'] = 0x01C1;
 t['clickretroflex'] = 0x01C3;
 t['club'] = 0x2663;
 t['clubsuitblack'] = 0x2663;
 t['clubsuitwhite'] = 0x2667;
 t['cmcubedsquare'] = 0x33A4;
 t['cmonospace'] = 0xFF43;
 t['cmsquaredsquare'] = 0x33A0;
 t['coarmenian'] = 0x0581;
 t['colon'] = 0x003A;
 t['colonmonetary'] = 0x20A1;
 t['colonmonospace'] = 0xFF1A;
 t['colonsign'] = 0x20A1;
 t['colonsmall'] = 0xFE55;
 t['colontriangularhalfmod'] = 0x02D1;
 t['colontriangularmod'] = 0x02D0;
 t['comma'] = 0x002C;
 t['commaabovecmb'] = 0x0313;
 t['commaaboverightcmb'] = 0x0315;
 t['commaaccent'] = 0xF6C3;
 t['commaarabic'] = 0x060C;
 t['commaarmenian'] = 0x055D;
 t['commainferior'] = 0xF6E1;
 t['commamonospace'] = 0xFF0C;
 t['commareversedabovecmb'] = 0x0314;
 t['commareversedmod'] = 0x02BD;
 t['commasmall'] = 0xFE50;
 t['commasuperior'] = 0xF6E2;
 t['commaturnedabovecmb'] = 0x0312;
 t['commaturnedmod'] = 0x02BB;
 t['compass'] = 0x263C;
 t['congruent'] = 0x2245;
 t['contourintegral'] = 0x222E;
 t['control'] = 0x2303;
 t['controlACK'] = 0x0006;
 t['controlBEL'] = 0x0007;
 t['controlBS'] = 0x0008;
 t['controlCAN'] = 0x0018;
 t['controlCR'] = 0x000D;
 t['controlDC1'] = 0x0011;
 t['controlDC2'] = 0x0012;
 t['controlDC3'] = 0x0013;
 t['controlDC4'] = 0x0014;
 t['controlDEL'] = 0x007F;
 t['controlDLE'] = 0x0010;
 t['controlEM'] = 0x0019;
 t['controlENQ'] = 0x0005;
 t['controlEOT'] = 0x0004;
 t['controlESC'] = 0x001B;
 t['controlETB'] = 0x0017;
 t['controlETX'] = 0x0003;
 t['controlFF'] = 0x000C;
 t['controlFS'] = 0x001C;
 t['controlGS'] = 0x001D;
 t['controlHT'] = 0x0009;
 t['controlLF'] = 0x000A;
 t['controlNAK'] = 0x0015;
 t['controlNULL'] = 0x0000;
 t['controlRS'] = 0x001E;
 t['controlSI'] = 0x000F;
 t['controlSO'] = 0x000E;
 t['controlSOT'] = 0x0002;
 t['controlSTX'] = 0x0001;
 t['controlSUB'] = 0x001A;
 t['controlSYN'] = 0x0016;
 t['controlUS'] = 0x001F;
 t['controlVT'] = 0x000B;
 t['copyright'] = 0x00A9;
 t['copyrightsans'] = 0xF8E9;
 t['copyrightserif'] = 0xF6D9;
 t['cornerbracketleft'] = 0x300C;
 t['cornerbracketlefthalfwidth'] = 0xFF62;
 t['cornerbracketleftvertical'] = 0xFE41;
 t['cornerbracketright'] = 0x300D;
 t['cornerbracketrighthalfwidth'] = 0xFF63;
 t['cornerbracketrightvertical'] = 0xFE42;
 t['corporationsquare'] = 0x337F;
 t['cosquare'] = 0x33C7;
 t['coverkgsquare'] = 0x33C6;
 t['cparen'] = 0x249E;
 t['cruzeiro'] = 0x20A2;
 t['cstretched'] = 0x0297;
 t['curlyand'] = 0x22CF;
 t['curlyor'] = 0x22CE;
 t['currency'] = 0x00A4;
 t['cyrBreve'] = 0xF6D1;
 t['cyrFlex'] = 0xF6D2;
 t['cyrbreve'] = 0xF6D4;
 t['cyrflex'] = 0xF6D5;
 t['d'] = 0x0064;
 t['daarmenian'] = 0x0564;
 t['dabengali'] = 0x09A6;
 t['dadarabic'] = 0x0636;
 t['dadeva'] = 0x0926;
 t['dadfinalarabic'] = 0xFEBE;
 t['dadinitialarabic'] = 0xFEBF;
 t['dadmedialarabic'] = 0xFEC0;
 t['dagesh'] = 0x05BC;
 t['dageshhebrew'] = 0x05BC;
 t['dagger'] = 0x2020;
 t['daggerdbl'] = 0x2021;
 t['dagujarati'] = 0x0AA6;
 t['dagurmukhi'] = 0x0A26;
 t['dahiragana'] = 0x3060;
 t['dakatakana'] = 0x30C0;
 t['dalarabic'] = 0x062F;
 t['dalet'] = 0x05D3;
 t['daletdagesh'] = 0xFB33;
 t['daletdageshhebrew'] = 0xFB33;
 t['dalethebrew'] = 0x05D3;
 t['dalfinalarabic'] = 0xFEAA;
 t['dammaarabic'] = 0x064F;
 t['dammalowarabic'] = 0x064F;
 t['dammatanaltonearabic'] = 0x064C;
 t['dammatanarabic'] = 0x064C;
 t['danda'] = 0x0964;
 t['dargahebrew'] = 0x05A7;
 t['dargalefthebrew'] = 0x05A7;
 t['dasiapneumatacyrilliccmb'] = 0x0485;
 t['dblGrave'] = 0xF6D3;
 t['dblanglebracketleft'] = 0x300A;
 t['dblanglebracketleftvertical'] = 0xFE3D;
 t['dblanglebracketright'] = 0x300B;
 t['dblanglebracketrightvertical'] = 0xFE3E;
 t['dblarchinvertedbelowcmb'] = 0x032B;
 t['dblarrowleft'] = 0x21D4;
 t['dblarrowright'] = 0x21D2;
 t['dbldanda'] = 0x0965;
 t['dblgrave'] = 0xF6D6;
 t['dblgravecmb'] = 0x030F;
 t['dblintegral'] = 0x222C;
 t['dbllowline'] = 0x2017;
 t['dbllowlinecmb'] = 0x0333;
 t['dbloverlinecmb'] = 0x033F;
 t['dblprimemod'] = 0x02BA;
 t['dblverticalbar'] = 0x2016;
 t['dblverticallineabovecmb'] = 0x030E;
 t['dbopomofo'] = 0x3109;
 t['dbsquare'] = 0x33C8;
 t['dcaron'] = 0x010F;
 t['dcedilla'] = 0x1E11;
 t['dcircle'] = 0x24D3;
 t['dcircumflexbelow'] = 0x1E13;
 t['dcroat'] = 0x0111;
 t['ddabengali'] = 0x09A1;
 t['ddadeva'] = 0x0921;
 t['ddagujarati'] = 0x0AA1;
 t['ddagurmukhi'] = 0x0A21;
 t['ddalarabic'] = 0x0688;
 t['ddalfinalarabic'] = 0xFB89;
 t['dddhadeva'] = 0x095C;
 t['ddhabengali'] = 0x09A2;
 t['ddhadeva'] = 0x0922;
 t['ddhagujarati'] = 0x0AA2;
 t['ddhagurmukhi'] = 0x0A22;
 t['ddotaccent'] = 0x1E0B;
 t['ddotbelow'] = 0x1E0D;
 t['decimalseparatorarabic'] = 0x066B;
 t['decimalseparatorpersian'] = 0x066B;
 t['decyrillic'] = 0x0434;
 t['degree'] = 0x00B0;
 t['dehihebrew'] = 0x05AD;
 t['dehiragana'] = 0x3067;
 t['deicoptic'] = 0x03EF;
 t['dekatakana'] = 0x30C7;
 t['deleteleft'] = 0x232B;
 t['deleteright'] = 0x2326;
 t['delta'] = 0x03B4;
 t['deltaturned'] = 0x018D;
 t['denominatorminusonenumeratorbengali'] = 0x09F8;
 t['dezh'] = 0x02A4;
 t['dhabengali'] = 0x09A7;
 t['dhadeva'] = 0x0927;
 t['dhagujarati'] = 0x0AA7;
 t['dhagurmukhi'] = 0x0A27;
 t['dhook'] = 0x0257;
 t['dialytikatonos'] = 0x0385;
 t['dialytikatonoscmb'] = 0x0344;
 t['diamond'] = 0x2666;
 t['diamondsuitwhite'] = 0x2662;
 t['dieresis'] = 0x00A8;
 t['dieresisacute'] = 0xF6D7;
 t['dieresisbelowcmb'] = 0x0324;
 t['dieresiscmb'] = 0x0308;
 t['dieresisgrave'] = 0xF6D8;
 t['dieresistonos'] = 0x0385;
 t['dihiragana'] = 0x3062;
 t['dikatakana'] = 0x30C2;
 t['dittomark'] = 0x3003;
 t['divide'] = 0x00F7;
 t['divides'] = 0x2223;
 t['divisionslash'] = 0x2215;
 t['djecyrillic'] = 0x0452;
 t['dkshade'] = 0x2593;
 t['dlinebelow'] = 0x1E0F;
 t['dlsquare'] = 0x3397;
 t['dmacron'] = 0x0111;
 t['dmonospace'] = 0xFF44;
 t['dnblock'] = 0x2584;
 t['dochadathai'] = 0x0E0E;
 t['dodekthai'] = 0x0E14;
 t['dohiragana'] = 0x3069;
 t['dokatakana'] = 0x30C9;
 t['dollar'] = 0x0024;
 t['dollarinferior'] = 0xF6E3;
 t['dollarmonospace'] = 0xFF04;
 t['dollaroldstyle'] = 0xF724;
 t['dollarsmall'] = 0xFE69;
 t['dollarsuperior'] = 0xF6E4;
 t['dong'] = 0x20AB;
 t['dorusquare'] = 0x3326;
 t['dotaccent'] = 0x02D9;
 t['dotaccentcmb'] = 0x0307;
 t['dotbelowcmb'] = 0x0323;
 t['dotbelowcomb'] = 0x0323;
 t['dotkatakana'] = 0x30FB;
 t['dotlessi'] = 0x0131;
 t['dotlessj'] = 0xF6BE;
 t['dotlessjstrokehook'] = 0x0284;
 t['dotmath'] = 0x22C5;
 t['dottedcircle'] = 0x25CC;
 t['doubleyodpatah'] = 0xFB1F;
 t['doubleyodpatahhebrew'] = 0xFB1F;
 t['downtackbelowcmb'] = 0x031E;
 t['downtackmod'] = 0x02D5;
 t['dparen'] = 0x249F;
 t['dsuperior'] = 0xF6EB;
 t['dtail'] = 0x0256;
 t['dtopbar'] = 0x018C;
 t['duhiragana'] = 0x3065;
 t['dukatakana'] = 0x30C5;
 t['dz'] = 0x01F3;
 t['dzaltone'] = 0x02A3;
 t['dzcaron'] = 0x01C6;
 t['dzcurl'] = 0x02A5;
 t['dzeabkhasiancyrillic'] = 0x04E1;
 t['dzecyrillic'] = 0x0455;
 t['dzhecyrillic'] = 0x045F;
 t['e'] = 0x0065;
 t['eacute'] = 0x00E9;
 t['earth'] = 0x2641;
 t['ebengali'] = 0x098F;
 t['ebopomofo'] = 0x311C;
 t['ebreve'] = 0x0115;
 t['ecandradeva'] = 0x090D;
 t['ecandragujarati'] = 0x0A8D;
 t['ecandravowelsigndeva'] = 0x0945;
 t['ecandravowelsigngujarati'] = 0x0AC5;
 t['ecaron'] = 0x011B;
 t['ecedillabreve'] = 0x1E1D;
 t['echarmenian'] = 0x0565;
 t['echyiwnarmenian'] = 0x0587;
 t['ecircle'] = 0x24D4;
 t['ecircumflex'] = 0x00EA;
 t['ecircumflexacute'] = 0x1EBF;
 t['ecircumflexbelow'] = 0x1E19;
 t['ecircumflexdotbelow'] = 0x1EC7;
 t['ecircumflexgrave'] = 0x1EC1;
 t['ecircumflexhookabove'] = 0x1EC3;
 t['ecircumflextilde'] = 0x1EC5;
 t['ecyrillic'] = 0x0454;
 t['edblgrave'] = 0x0205;
 t['edeva'] = 0x090F;
 t['edieresis'] = 0x00EB;
 t['edot'] = 0x0117;
 t['edotaccent'] = 0x0117;
 t['edotbelow'] = 0x1EB9;
 t['eegurmukhi'] = 0x0A0F;
 t['eematragurmukhi'] = 0x0A47;
 t['efcyrillic'] = 0x0444;
 t['egrave'] = 0x00E8;
 t['egujarati'] = 0x0A8F;
 t['eharmenian'] = 0x0567;
 t['ehbopomofo'] = 0x311D;
 t['ehiragana'] = 0x3048;
 t['ehookabove'] = 0x1EBB;
 t['eibopomofo'] = 0x311F;
 t['eight'] = 0x0038;
 t['eightarabic'] = 0x0668;
 t['eightbengali'] = 0x09EE;
 t['eightcircle'] = 0x2467;
 t['eightcircleinversesansserif'] = 0x2791;
 t['eightdeva'] = 0x096E;
 t['eighteencircle'] = 0x2471;
 t['eighteenparen'] = 0x2485;
 t['eighteenperiod'] = 0x2499;
 t['eightgujarati'] = 0x0AEE;
 t['eightgurmukhi'] = 0x0A6E;
 t['eighthackarabic'] = 0x0668;
 t['eighthangzhou'] = 0x3028;
 t['eighthnotebeamed'] = 0x266B;
 t['eightideographicparen'] = 0x3227;
 t['eightinferior'] = 0x2088;
 t['eightmonospace'] = 0xFF18;
 t['eightoldstyle'] = 0xF738;
 t['eightparen'] = 0x247B;
 t['eightperiod'] = 0x248F;
 t['eightpersian'] = 0x06F8;
 t['eightroman'] = 0x2177;
 t['eightsuperior'] = 0x2078;
 t['eightthai'] = 0x0E58;
 t['einvertedbreve'] = 0x0207;
 t['eiotifiedcyrillic'] = 0x0465;
 t['ekatakana'] = 0x30A8;
 t['ekatakanahalfwidth'] = 0xFF74;
 t['ekonkargurmukhi'] = 0x0A74;
 t['ekorean'] = 0x3154;
 t['elcyrillic'] = 0x043B;
 t['element'] = 0x2208;
 t['elevencircle'] = 0x246A;
 t['elevenparen'] = 0x247E;
 t['elevenperiod'] = 0x2492;
 t['elevenroman'] = 0x217A;
 t['ellipsis'] = 0x2026;
 t['ellipsisvertical'] = 0x22EE;
 t['emacron'] = 0x0113;
 t['emacronacute'] = 0x1E17;
 t['emacrongrave'] = 0x1E15;
 t['emcyrillic'] = 0x043C;
 t['emdash'] = 0x2014;
 t['emdashvertical'] = 0xFE31;
 t['emonospace'] = 0xFF45;
 t['emphasismarkarmenian'] = 0x055B;
 t['emptyset'] = 0x2205;
 t['enbopomofo'] = 0x3123;
 t['encyrillic'] = 0x043D;
 t['endash'] = 0x2013;
 t['endashvertical'] = 0xFE32;
 t['endescendercyrillic'] = 0x04A3;
 t['eng'] = 0x014B;
 t['engbopomofo'] = 0x3125;
 t['enghecyrillic'] = 0x04A5;
 t['enhookcyrillic'] = 0x04C8;
 t['enspace'] = 0x2002;
 t['eogonek'] = 0x0119;
 t['eokorean'] = 0x3153;
 t['eopen'] = 0x025B;
 t['eopenclosed'] = 0x029A;
 t['eopenreversed'] = 0x025C;
 t['eopenreversedclosed'] = 0x025E;
 t['eopenreversedhook'] = 0x025D;
 t['eparen'] = 0x24A0;
 t['epsilon'] = 0x03B5;
 t['epsilontonos'] = 0x03AD;
 t['equal'] = 0x003D;
 t['equalmonospace'] = 0xFF1D;
 t['equalsmall'] = 0xFE66;
 t['equalsuperior'] = 0x207C;
 t['equivalence'] = 0x2261;
 t['erbopomofo'] = 0x3126;
 t['ercyrillic'] = 0x0440;
 t['ereversed'] = 0x0258;
 t['ereversedcyrillic'] = 0x044D;
 t['escyrillic'] = 0x0441;
 t['esdescendercyrillic'] = 0x04AB;
 t['esh'] = 0x0283;
 t['eshcurl'] = 0x0286;
 t['eshortdeva'] = 0x090E;
 t['eshortvowelsigndeva'] = 0x0946;
 t['eshreversedloop'] = 0x01AA;
 t['eshsquatreversed'] = 0x0285;
 t['esmallhiragana'] = 0x3047;
 t['esmallkatakana'] = 0x30A7;
 t['esmallkatakanahalfwidth'] = 0xFF6A;
 t['estimated'] = 0x212E;
 t['esuperior'] = 0xF6EC;
 t['eta'] = 0x03B7;
 t['etarmenian'] = 0x0568;
 t['etatonos'] = 0x03AE;
 t['eth'] = 0x00F0;
 t['etilde'] = 0x1EBD;
 t['etildebelow'] = 0x1E1B;
 t['etnahtafoukhhebrew'] = 0x0591;
 t['etnahtafoukhlefthebrew'] = 0x0591;
 t['etnahtahebrew'] = 0x0591;
 t['etnahtalefthebrew'] = 0x0591;
 t['eturned'] = 0x01DD;
 t['eukorean'] = 0x3161;
 t['euro'] = 0x20AC;
 t['evowelsignbengali'] = 0x09C7;
 t['evowelsigndeva'] = 0x0947;
 t['evowelsigngujarati'] = 0x0AC7;
 t['exclam'] = 0x0021;
 t['exclamarmenian'] = 0x055C;
 t['exclamdbl'] = 0x203C;
 t['exclamdown'] = 0x00A1;
 t['exclamdownsmall'] = 0xF7A1;
 t['exclammonospace'] = 0xFF01;
 t['exclamsmall'] = 0xF721;
 t['existential'] = 0x2203;
 t['ezh'] = 0x0292;
 t['ezhcaron'] = 0x01EF;
 t['ezhcurl'] = 0x0293;
 t['ezhreversed'] = 0x01B9;
 t['ezhtail'] = 0x01BA;
 t['f'] = 0x0066;
 t['fadeva'] = 0x095E;
 t['fagurmukhi'] = 0x0A5E;
 t['fahrenheit'] = 0x2109;
 t['fathaarabic'] = 0x064E;
 t['fathalowarabic'] = 0x064E;
 t['fathatanarabic'] = 0x064B;
 t['fbopomofo'] = 0x3108;
 t['fcircle'] = 0x24D5;
 t['fdotaccent'] = 0x1E1F;
 t['feharabic'] = 0x0641;
 t['feharmenian'] = 0x0586;
 t['fehfinalarabic'] = 0xFED2;
 t['fehinitialarabic'] = 0xFED3;
 t['fehmedialarabic'] = 0xFED4;
 t['feicoptic'] = 0x03E5;
 t['female'] = 0x2640;
 t['ff'] = 0xFB00;
 t['ffi'] = 0xFB03;
 t['ffl'] = 0xFB04;
 t['fi'] = 0xFB01;
 t['fifteencircle'] = 0x246E;
 t['fifteenparen'] = 0x2482;
 t['fifteenperiod'] = 0x2496;
 t['figuredash'] = 0x2012;
 t['filledbox'] = 0x25A0;
 t['filledrect'] = 0x25AC;
 t['finalkaf'] = 0x05DA;
 t['finalkafdagesh'] = 0xFB3A;
 t['finalkafdageshhebrew'] = 0xFB3A;
 t['finalkafhebrew'] = 0x05DA;
 t['finalmem'] = 0x05DD;
 t['finalmemhebrew'] = 0x05DD;
 t['finalnun'] = 0x05DF;
 t['finalnunhebrew'] = 0x05DF;
 t['finalpe'] = 0x05E3;
 t['finalpehebrew'] = 0x05E3;
 t['finaltsadi'] = 0x05E5;
 t['finaltsadihebrew'] = 0x05E5;
 t['firsttonechinese'] = 0x02C9;
 t['fisheye'] = 0x25C9;
 t['fitacyrillic'] = 0x0473;
 t['five'] = 0x0035;
 t['fivearabic'] = 0x0665;
 t['fivebengali'] = 0x09EB;
 t['fivecircle'] = 0x2464;
 t['fivecircleinversesansserif'] = 0x278E;
 t['fivedeva'] = 0x096B;
 t['fiveeighths'] = 0x215D;
 t['fivegujarati'] = 0x0AEB;
 t['fivegurmukhi'] = 0x0A6B;
 t['fivehackarabic'] = 0x0665;
 t['fivehangzhou'] = 0x3025;
 t['fiveideographicparen'] = 0x3224;
 t['fiveinferior'] = 0x2085;
 t['fivemonospace'] = 0xFF15;
 t['fiveoldstyle'] = 0xF735;
 t['fiveparen'] = 0x2478;
 t['fiveperiod'] = 0x248C;
 t['fivepersian'] = 0x06F5;
 t['fiveroman'] = 0x2174;
 t['fivesuperior'] = 0x2075;
 t['fivethai'] = 0x0E55;
 t['fl'] = 0xFB02;
 t['florin'] = 0x0192;
 t['fmonospace'] = 0xFF46;
 t['fmsquare'] = 0x3399;
 t['fofanthai'] = 0x0E1F;
 t['fofathai'] = 0x0E1D;
 t['fongmanthai'] = 0x0E4F;
 t['forall'] = 0x2200;
 t['four'] = 0x0034;
 t['fourarabic'] = 0x0664;
 t['fourbengali'] = 0x09EA;
 t['fourcircle'] = 0x2463;
 t['fourcircleinversesansserif'] = 0x278D;
 t['fourdeva'] = 0x096A;
 t['fourgujarati'] = 0x0AEA;
 t['fourgurmukhi'] = 0x0A6A;
 t['fourhackarabic'] = 0x0664;
 t['fourhangzhou'] = 0x3024;
 t['fourideographicparen'] = 0x3223;
 t['fourinferior'] = 0x2084;
 t['fourmonospace'] = 0xFF14;
 t['fournumeratorbengali'] = 0x09F7;
 t['fouroldstyle'] = 0xF734;
 t['fourparen'] = 0x2477;
 t['fourperiod'] = 0x248B;
 t['fourpersian'] = 0x06F4;
 t['fourroman'] = 0x2173;
 t['foursuperior'] = 0x2074;
 t['fourteencircle'] = 0x246D;
 t['fourteenparen'] = 0x2481;
 t['fourteenperiod'] = 0x2495;
 t['fourthai'] = 0x0E54;
 t['fourthtonechinese'] = 0x02CB;
 t['fparen'] = 0x24A1;
 t['fraction'] = 0x2044;
 t['franc'] = 0x20A3;
 t['g'] = 0x0067;
 t['gabengali'] = 0x0997;
 t['gacute'] = 0x01F5;
 t['gadeva'] = 0x0917;
 t['gafarabic'] = 0x06AF;
 t['gaffinalarabic'] = 0xFB93;
 t['gafinitialarabic'] = 0xFB94;
 t['gafmedialarabic'] = 0xFB95;
 t['gagujarati'] = 0x0A97;
 t['gagurmukhi'] = 0x0A17;
 t['gahiragana'] = 0x304C;
 t['gakatakana'] = 0x30AC;
 t['gamma'] = 0x03B3;
 t['gammalatinsmall'] = 0x0263;
 t['gammasuperior'] = 0x02E0;
 t['gangiacoptic'] = 0x03EB;
 t['gbopomofo'] = 0x310D;
 t['gbreve'] = 0x011F;
 t['gcaron'] = 0x01E7;
 t['gcedilla'] = 0x0123;
 t['gcircle'] = 0x24D6;
 t['gcircumflex'] = 0x011D;
 t['gcommaaccent'] = 0x0123;
 t['gdot'] = 0x0121;
 t['gdotaccent'] = 0x0121;
 t['gecyrillic'] = 0x0433;
 t['gehiragana'] = 0x3052;
 t['gekatakana'] = 0x30B2;
 t['geometricallyequal'] = 0x2251;
 t['gereshaccenthebrew'] = 0x059C;
 t['gereshhebrew'] = 0x05F3;
 t['gereshmuqdamhebrew'] = 0x059D;
 t['germandbls'] = 0x00DF;
 t['gershayimaccenthebrew'] = 0x059E;
 t['gershayimhebrew'] = 0x05F4;
 t['getamark'] = 0x3013;
 t['ghabengali'] = 0x0998;
 t['ghadarmenian'] = 0x0572;
 t['ghadeva'] = 0x0918;
 t['ghagujarati'] = 0x0A98;
 t['ghagurmukhi'] = 0x0A18;
 t['ghainarabic'] = 0x063A;
 t['ghainfinalarabic'] = 0xFECE;
 t['ghaininitialarabic'] = 0xFECF;
 t['ghainmedialarabic'] = 0xFED0;
 t['ghemiddlehookcyrillic'] = 0x0495;
 t['ghestrokecyrillic'] = 0x0493;
 t['gheupturncyrillic'] = 0x0491;
 t['ghhadeva'] = 0x095A;
 t['ghhagurmukhi'] = 0x0A5A;
 t['ghook'] = 0x0260;
 t['ghzsquare'] = 0x3393;
 t['gihiragana'] = 0x304E;
 t['gikatakana'] = 0x30AE;
 t['gimarmenian'] = 0x0563;
 t['gimel'] = 0x05D2;
 t['gimeldagesh'] = 0xFB32;
 t['gimeldageshhebrew'] = 0xFB32;
 t['gimelhebrew'] = 0x05D2;
 t['gjecyrillic'] = 0x0453;
 t['glottalinvertedstroke'] = 0x01BE;
 t['glottalstop'] = 0x0294;
 t['glottalstopinverted'] = 0x0296;
 t['glottalstopmod'] = 0x02C0;
 t['glottalstopreversed'] = 0x0295;
 t['glottalstopreversedmod'] = 0x02C1;
 t['glottalstopreversedsuperior'] = 0x02E4;
 t['glottalstopstroke'] = 0x02A1;
 t['glottalstopstrokereversed'] = 0x02A2;
 t['gmacron'] = 0x1E21;
 t['gmonospace'] = 0xFF47;
 t['gohiragana'] = 0x3054;
 t['gokatakana'] = 0x30B4;
 t['gparen'] = 0x24A2;
 t['gpasquare'] = 0x33AC;
 t['gradient'] = 0x2207;
 t['grave'] = 0x0060;
 t['gravebelowcmb'] = 0x0316;
 t['gravecmb'] = 0x0300;
 t['gravecomb'] = 0x0300;
 t['gravedeva'] = 0x0953;
 t['gravelowmod'] = 0x02CE;
 t['gravemonospace'] = 0xFF40;
 t['gravetonecmb'] = 0x0340;
 t['greater'] = 0x003E;
 t['greaterequal'] = 0x2265;
 t['greaterequalorless'] = 0x22DB;
 t['greatermonospace'] = 0xFF1E;
 t['greaterorequivalent'] = 0x2273;
 t['greaterorless'] = 0x2277;
 t['greateroverequal'] = 0x2267;
 t['greatersmall'] = 0xFE65;
 t['gscript'] = 0x0261;
 t['gstroke'] = 0x01E5;
 t['guhiragana'] = 0x3050;
 t['guillemotleft'] = 0x00AB;
 t['guillemotright'] = 0x00BB;
 t['guilsinglleft'] = 0x2039;
 t['guilsinglright'] = 0x203A;
 t['gukatakana'] = 0x30B0;
 t['guramusquare'] = 0x3318;
 t['gysquare'] = 0x33C9;
 t['h'] = 0x0068;
 t['haabkhasiancyrillic'] = 0x04A9;
 t['haaltonearabic'] = 0x06C1;
 t['habengali'] = 0x09B9;
 t['hadescendercyrillic'] = 0x04B3;
 t['hadeva'] = 0x0939;
 t['hagujarati'] = 0x0AB9;
 t['hagurmukhi'] = 0x0A39;
 t['haharabic'] = 0x062D;
 t['hahfinalarabic'] = 0xFEA2;
 t['hahinitialarabic'] = 0xFEA3;
 t['hahiragana'] = 0x306F;
 t['hahmedialarabic'] = 0xFEA4;
 t['haitusquare'] = 0x332A;
 t['hakatakana'] = 0x30CF;
 t['hakatakanahalfwidth'] = 0xFF8A;
 t['halantgurmukhi'] = 0x0A4D;
 t['hamzaarabic'] = 0x0621;
 t['hamzalowarabic'] = 0x0621;
 t['hangulfiller'] = 0x3164;
 t['hardsigncyrillic'] = 0x044A;
 t['harpoonleftbarbup'] = 0x21BC;
 t['harpoonrightbarbup'] = 0x21C0;
 t['hasquare'] = 0x33CA;
 t['hatafpatah'] = 0x05B2;
 t['hatafpatah16'] = 0x05B2;
 t['hatafpatah23'] = 0x05B2;
 t['hatafpatah2f'] = 0x05B2;
 t['hatafpatahhebrew'] = 0x05B2;
 t['hatafpatahnarrowhebrew'] = 0x05B2;
 t['hatafpatahquarterhebrew'] = 0x05B2;
 t['hatafpatahwidehebrew'] = 0x05B2;
 t['hatafqamats'] = 0x05B3;
 t['hatafqamats1b'] = 0x05B3;
 t['hatafqamats28'] = 0x05B3;
 t['hatafqamats34'] = 0x05B3;
 t['hatafqamatshebrew'] = 0x05B3;
 t['hatafqamatsnarrowhebrew'] = 0x05B3;
 t['hatafqamatsquarterhebrew'] = 0x05B3;
 t['hatafqamatswidehebrew'] = 0x05B3;
 t['hatafsegol'] = 0x05B1;
 t['hatafsegol17'] = 0x05B1;
 t['hatafsegol24'] = 0x05B1;
 t['hatafsegol30'] = 0x05B1;
 t['hatafsegolhebrew'] = 0x05B1;
 t['hatafsegolnarrowhebrew'] = 0x05B1;
 t['hatafsegolquarterhebrew'] = 0x05B1;
 t['hatafsegolwidehebrew'] = 0x05B1;
 t['hbar'] = 0x0127;
 t['hbopomofo'] = 0x310F;
 t['hbrevebelow'] = 0x1E2B;
 t['hcedilla'] = 0x1E29;
 t['hcircle'] = 0x24D7;
 t['hcircumflex'] = 0x0125;
 t['hdieresis'] = 0x1E27;
 t['hdotaccent'] = 0x1E23;
 t['hdotbelow'] = 0x1E25;
 t['he'] = 0x05D4;
 t['heart'] = 0x2665;
 t['heartsuitblack'] = 0x2665;
 t['heartsuitwhite'] = 0x2661;
 t['hedagesh'] = 0xFB34;
 t['hedageshhebrew'] = 0xFB34;
 t['hehaltonearabic'] = 0x06C1;
 t['heharabic'] = 0x0647;
 t['hehebrew'] = 0x05D4;
 t['hehfinalaltonearabic'] = 0xFBA7;
 t['hehfinalalttwoarabic'] = 0xFEEA;
 t['hehfinalarabic'] = 0xFEEA;
 t['hehhamzaabovefinalarabic'] = 0xFBA5;
 t['hehhamzaaboveisolatedarabic'] = 0xFBA4;
 t['hehinitialaltonearabic'] = 0xFBA8;
 t['hehinitialarabic'] = 0xFEEB;
 t['hehiragana'] = 0x3078;
 t['hehmedialaltonearabic'] = 0xFBA9;
 t['hehmedialarabic'] = 0xFEEC;
 t['heiseierasquare'] = 0x337B;
 t['hekatakana'] = 0x30D8;
 t['hekatakanahalfwidth'] = 0xFF8D;
 t['hekutaarusquare'] = 0x3336;
 t['henghook'] = 0x0267;
 t['herutusquare'] = 0x3339;
 t['het'] = 0x05D7;
 t['hethebrew'] = 0x05D7;
 t['hhook'] = 0x0266;
 t['hhooksuperior'] = 0x02B1;
 t['hieuhacirclekorean'] = 0x327B;
 t['hieuhaparenkorean'] = 0x321B;
 t['hieuhcirclekorean'] = 0x326D;
 t['hieuhkorean'] = 0x314E;
 t['hieuhparenkorean'] = 0x320D;
 t['hihiragana'] = 0x3072;
 t['hikatakana'] = 0x30D2;
 t['hikatakanahalfwidth'] = 0xFF8B;
 t['hiriq'] = 0x05B4;
 t['hiriq14'] = 0x05B4;
 t['hiriq21'] = 0x05B4;
 t['hiriq2d'] = 0x05B4;
 t['hiriqhebrew'] = 0x05B4;
 t['hiriqnarrowhebrew'] = 0x05B4;
 t['hiriqquarterhebrew'] = 0x05B4;
 t['hiriqwidehebrew'] = 0x05B4;
 t['hlinebelow'] = 0x1E96;
 t['hmonospace'] = 0xFF48;
 t['hoarmenian'] = 0x0570;
 t['hohipthai'] = 0x0E2B;
 t['hohiragana'] = 0x307B;
 t['hokatakana'] = 0x30DB;
 t['hokatakanahalfwidth'] = 0xFF8E;
 t['holam'] = 0x05B9;
 t['holam19'] = 0x05B9;
 t['holam26'] = 0x05B9;
 t['holam32'] = 0x05B9;
 t['holamhebrew'] = 0x05B9;
 t['holamnarrowhebrew'] = 0x05B9;
 t['holamquarterhebrew'] = 0x05B9;
 t['holamwidehebrew'] = 0x05B9;
 t['honokhukthai'] = 0x0E2E;
 t['hookabovecomb'] = 0x0309;
 t['hookcmb'] = 0x0309;
 t['hookpalatalizedbelowcmb'] = 0x0321;
 t['hookretroflexbelowcmb'] = 0x0322;
 t['hoonsquare'] = 0x3342;
 t['horicoptic'] = 0x03E9;
 t['horizontalbar'] = 0x2015;
 t['horncmb'] = 0x031B;
 t['hotsprings'] = 0x2668;
 t['house'] = 0x2302;
 t['hparen'] = 0x24A3;
 t['hsuperior'] = 0x02B0;
 t['hturned'] = 0x0265;
 t['huhiragana'] = 0x3075;
 t['huiitosquare'] = 0x3333;
 t['hukatakana'] = 0x30D5;
 t['hukatakanahalfwidth'] = 0xFF8C;
 t['hungarumlaut'] = 0x02DD;
 t['hungarumlautcmb'] = 0x030B;
 t['hv'] = 0x0195;
 t['hyphen'] = 0x002D;
 t['hypheninferior'] = 0xF6E5;
 t['hyphenmonospace'] = 0xFF0D;
 t['hyphensmall'] = 0xFE63;
 t['hyphensuperior'] = 0xF6E6;
 t['hyphentwo'] = 0x2010;
 t['i'] = 0x0069;
 t['iacute'] = 0x00ED;
 t['iacyrillic'] = 0x044F;
 t['ibengali'] = 0x0987;
 t['ibopomofo'] = 0x3127;
 t['ibreve'] = 0x012D;
 t['icaron'] = 0x01D0;
 t['icircle'] = 0x24D8;
 t['icircumflex'] = 0x00EE;
 t['icyrillic'] = 0x0456;
 t['idblgrave'] = 0x0209;
 t['ideographearthcircle'] = 0x328F;
 t['ideographfirecircle'] = 0x328B;
 t['ideographicallianceparen'] = 0x323F;
 t['ideographiccallparen'] = 0x323A;
 t['ideographiccentrecircle'] = 0x32A5;
 t['ideographicclose'] = 0x3006;
 t['ideographiccomma'] = 0x3001;
 t['ideographiccommaleft'] = 0xFF64;
 t['ideographiccongratulationparen'] = 0x3237;
 t['ideographiccorrectcircle'] = 0x32A3;
 t['ideographicearthparen'] = 0x322F;
 t['ideographicenterpriseparen'] = 0x323D;
 t['ideographicexcellentcircle'] = 0x329D;
 t['ideographicfestivalparen'] = 0x3240;
 t['ideographicfinancialcircle'] = 0x3296;
 t['ideographicfinancialparen'] = 0x3236;
 t['ideographicfireparen'] = 0x322B;
 t['ideographichaveparen'] = 0x3232;
 t['ideographichighcircle'] = 0x32A4;
 t['ideographiciterationmark'] = 0x3005;
 t['ideographiclaborcircle'] = 0x3298;
 t['ideographiclaborparen'] = 0x3238;
 t['ideographicleftcircle'] = 0x32A7;
 t['ideographiclowcircle'] = 0x32A6;
 t['ideographicmedicinecircle'] = 0x32A9;
 t['ideographicmetalparen'] = 0x322E;
 t['ideographicmoonparen'] = 0x322A;
 t['ideographicnameparen'] = 0x3234;
 t['ideographicperiod'] = 0x3002;
 t['ideographicprintcircle'] = 0x329E;
 t['ideographicreachparen'] = 0x3243;
 t['ideographicrepresentparen'] = 0x3239;
 t['ideographicresourceparen'] = 0x323E;
 t['ideographicrightcircle'] = 0x32A8;
 t['ideographicsecretcircle'] = 0x3299;
 t['ideographicselfparen'] = 0x3242;
 t['ideographicsocietyparen'] = 0x3233;
 t['ideographicspace'] = 0x3000;
 t['ideographicspecialparen'] = 0x3235;
 t['ideographicstockparen'] = 0x3231;
 t['ideographicstudyparen'] = 0x323B;
 t['ideographicsunparen'] = 0x3230;
 t['ideographicsuperviseparen'] = 0x323C;
 t['ideographicwaterparen'] = 0x322C;
 t['ideographicwoodparen'] = 0x322D;
 t['ideographiczero'] = 0x3007;
 t['ideographmetalcircle'] = 0x328E;
 t['ideographmooncircle'] = 0x328A;
 t['ideographnamecircle'] = 0x3294;
 t['ideographsuncircle'] = 0x3290;
 t['ideographwatercircle'] = 0x328C;
 t['ideographwoodcircle'] = 0x328D;
 t['ideva'] = 0x0907;
 t['idieresis'] = 0x00EF;
 t['idieresisacute'] = 0x1E2F;
 t['idieresiscyrillic'] = 0x04E5;
 t['idotbelow'] = 0x1ECB;
 t['iebrevecyrillic'] = 0x04D7;
 t['iecyrillic'] = 0x0435;
 t['ieungacirclekorean'] = 0x3275;
 t['ieungaparenkorean'] = 0x3215;
 t['ieungcirclekorean'] = 0x3267;
 t['ieungkorean'] = 0x3147;
 t['ieungparenkorean'] = 0x3207;
 t['igrave'] = 0x00EC;
 t['igujarati'] = 0x0A87;
 t['igurmukhi'] = 0x0A07;
 t['ihiragana'] = 0x3044;
 t['ihookabove'] = 0x1EC9;
 t['iibengali'] = 0x0988;
 t['iicyrillic'] = 0x0438;
 t['iideva'] = 0x0908;
 t['iigujarati'] = 0x0A88;
 t['iigurmukhi'] = 0x0A08;
 t['iimatragurmukhi'] = 0x0A40;
 t['iinvertedbreve'] = 0x020B;
 t['iishortcyrillic'] = 0x0439;
 t['iivowelsignbengali'] = 0x09C0;
 t['iivowelsigndeva'] = 0x0940;
 t['iivowelsigngujarati'] = 0x0AC0;
 t['ij'] = 0x0133;
 t['ikatakana'] = 0x30A4;
 t['ikatakanahalfwidth'] = 0xFF72;
 t['ikorean'] = 0x3163;
 t['ilde'] = 0x02DC;
 t['iluyhebrew'] = 0x05AC;
 t['imacron'] = 0x012B;
 t['imacroncyrillic'] = 0x04E3;
 t['imageorapproximatelyequal'] = 0x2253;
 t['imatragurmukhi'] = 0x0A3F;
 t['imonospace'] = 0xFF49;
 t['increment'] = 0x2206;
 t['infinity'] = 0x221E;
 t['iniarmenian'] = 0x056B;
 t['integral'] = 0x222B;
 t['integralbottom'] = 0x2321;
 t['integralbt'] = 0x2321;
 t['integralex'] = 0xF8F5;
 t['integraltop'] = 0x2320;
 t['integraltp'] = 0x2320;
 t['intersection'] = 0x2229;
 t['intisquare'] = 0x3305;
 t['invbullet'] = 0x25D8;
 t['invcircle'] = 0x25D9;
 t['invsmileface'] = 0x263B;
 t['iocyrillic'] = 0x0451;
 t['iogonek'] = 0x012F;
 t['iota'] = 0x03B9;
 t['iotadieresis'] = 0x03CA;
 t['iotadieresistonos'] = 0x0390;
 t['iotalatin'] = 0x0269;
 t['iotatonos'] = 0x03AF;
 t['iparen'] = 0x24A4;
 t['irigurmukhi'] = 0x0A72;
 t['ismallhiragana'] = 0x3043;
 t['ismallkatakana'] = 0x30A3;
 t['ismallkatakanahalfwidth'] = 0xFF68;
 t['issharbengali'] = 0x09FA;
 t['istroke'] = 0x0268;
 t['isuperior'] = 0xF6ED;
 t['iterationhiragana'] = 0x309D;
 t['iterationkatakana'] = 0x30FD;
 t['itilde'] = 0x0129;
 t['itildebelow'] = 0x1E2D;
 t['iubopomofo'] = 0x3129;
 t['iucyrillic'] = 0x044E;
 t['ivowelsignbengali'] = 0x09BF;
 t['ivowelsigndeva'] = 0x093F;
 t['ivowelsigngujarati'] = 0x0ABF;
 t['izhitsacyrillic'] = 0x0475;
 t['izhitsadblgravecyrillic'] = 0x0477;
 t['j'] = 0x006A;
 t['jaarmenian'] = 0x0571;
 t['jabengali'] = 0x099C;
 t['jadeva'] = 0x091C;
 t['jagujarati'] = 0x0A9C;
 t['jagurmukhi'] = 0x0A1C;
 t['jbopomofo'] = 0x3110;
 t['jcaron'] = 0x01F0;
 t['jcircle'] = 0x24D9;
 t['jcircumflex'] = 0x0135;
 t['jcrossedtail'] = 0x029D;
 t['jdotlessstroke'] = 0x025F;
 t['jecyrillic'] = 0x0458;
 t['jeemarabic'] = 0x062C;
 t['jeemfinalarabic'] = 0xFE9E;
 t['jeeminitialarabic'] = 0xFE9F;
 t['jeemmedialarabic'] = 0xFEA0;
 t['jeharabic'] = 0x0698;
 t['jehfinalarabic'] = 0xFB8B;
 t['jhabengali'] = 0x099D;
 t['jhadeva'] = 0x091D;
 t['jhagujarati'] = 0x0A9D;
 t['jhagurmukhi'] = 0x0A1D;
 t['jheharmenian'] = 0x057B;
 t['jis'] = 0x3004;
 t['jmonospace'] = 0xFF4A;
 t['jparen'] = 0x24A5;
 t['jsuperior'] = 0x02B2;
 t['k'] = 0x006B;
 t['kabashkircyrillic'] = 0x04A1;
 t['kabengali'] = 0x0995;
 t['kacute'] = 0x1E31;
 t['kacyrillic'] = 0x043A;
 t['kadescendercyrillic'] = 0x049B;
 t['kadeva'] = 0x0915;
 t['kaf'] = 0x05DB;
 t['kafarabic'] = 0x0643;
 t['kafdagesh'] = 0xFB3B;
 t['kafdageshhebrew'] = 0xFB3B;
 t['kaffinalarabic'] = 0xFEDA;
 t['kafhebrew'] = 0x05DB;
 t['kafinitialarabic'] = 0xFEDB;
 t['kafmedialarabic'] = 0xFEDC;
 t['kafrafehebrew'] = 0xFB4D;
 t['kagujarati'] = 0x0A95;
 t['kagurmukhi'] = 0x0A15;
 t['kahiragana'] = 0x304B;
 t['kahookcyrillic'] = 0x04C4;
 t['kakatakana'] = 0x30AB;
 t['kakatakanahalfwidth'] = 0xFF76;
 t['kappa'] = 0x03BA;
 t['kappasymbolgreek'] = 0x03F0;
 t['kapyeounmieumkorean'] = 0x3171;
 t['kapyeounphieuphkorean'] = 0x3184;
 t['kapyeounpieupkorean'] = 0x3178;
 t['kapyeounssangpieupkorean'] = 0x3179;
 t['karoriisquare'] = 0x330D;
 t['kashidaautoarabic'] = 0x0640;
 t['kashidaautonosidebearingarabic'] = 0x0640;
 t['kasmallkatakana'] = 0x30F5;
 t['kasquare'] = 0x3384;
 t['kasraarabic'] = 0x0650;
 t['kasratanarabic'] = 0x064D;
 t['kastrokecyrillic'] = 0x049F;
 t['katahiraprolongmarkhalfwidth'] = 0xFF70;
 t['kaverticalstrokecyrillic'] = 0x049D;
 t['kbopomofo'] = 0x310E;
 t['kcalsquare'] = 0x3389;
 t['kcaron'] = 0x01E9;
 t['kcedilla'] = 0x0137;
 t['kcircle'] = 0x24DA;
 t['kcommaaccent'] = 0x0137;
 t['kdotbelow'] = 0x1E33;
 t['keharmenian'] = 0x0584;
 t['kehiragana'] = 0x3051;
 t['kekatakana'] = 0x30B1;
 t['kekatakanahalfwidth'] = 0xFF79;
 t['kenarmenian'] = 0x056F;
 t['kesmallkatakana'] = 0x30F6;
 t['kgreenlandic'] = 0x0138;
 t['khabengali'] = 0x0996;
 t['khacyrillic'] = 0x0445;
 t['khadeva'] = 0x0916;
 t['khagujarati'] = 0x0A96;
 t['khagurmukhi'] = 0x0A16;
 t['khaharabic'] = 0x062E;
 t['khahfinalarabic'] = 0xFEA6;
 t['khahinitialarabic'] = 0xFEA7;
 t['khahmedialarabic'] = 0xFEA8;
 t['kheicoptic'] = 0x03E7;
 t['khhadeva'] = 0x0959;
 t['khhagurmukhi'] = 0x0A59;
 t['khieukhacirclekorean'] = 0x3278;
 t['khieukhaparenkorean'] = 0x3218;
 t['khieukhcirclekorean'] = 0x326A;
 t['khieukhkorean'] = 0x314B;
 t['khieukhparenkorean'] = 0x320A;
 t['khokhaithai'] = 0x0E02;
 t['khokhonthai'] = 0x0E05;
 t['khokhuatthai'] = 0x0E03;
 t['khokhwaithai'] = 0x0E04;
 t['khomutthai'] = 0x0E5B;
 t['khook'] = 0x0199;
 t['khorakhangthai'] = 0x0E06;
 t['khzsquare'] = 0x3391;
 t['kihiragana'] = 0x304D;
 t['kikatakana'] = 0x30AD;
 t['kikatakanahalfwidth'] = 0xFF77;
 t['kiroguramusquare'] = 0x3315;
 t['kiromeetorusquare'] = 0x3316;
 t['kirosquare'] = 0x3314;
 t['kiyeokacirclekorean'] = 0x326E;
 t['kiyeokaparenkorean'] = 0x320E;
 t['kiyeokcirclekorean'] = 0x3260;
 t['kiyeokkorean'] = 0x3131;
 t['kiyeokparenkorean'] = 0x3200;
 t['kiyeoksioskorean'] = 0x3133;
 t['kjecyrillic'] = 0x045C;
 t['klinebelow'] = 0x1E35;
 t['klsquare'] = 0x3398;
 t['kmcubedsquare'] = 0x33A6;
 t['kmonospace'] = 0xFF4B;
 t['kmsquaredsquare'] = 0x33A2;
 t['kohiragana'] = 0x3053;
 t['kohmsquare'] = 0x33C0;
 t['kokaithai'] = 0x0E01;
 t['kokatakana'] = 0x30B3;
 t['kokatakanahalfwidth'] = 0xFF7A;
 t['kooposquare'] = 0x331E;
 t['koppacyrillic'] = 0x0481;
 t['koreanstandardsymbol'] = 0x327F;
 t['koroniscmb'] = 0x0343;
 t['kparen'] = 0x24A6;
 t['kpasquare'] = 0x33AA;
 t['ksicyrillic'] = 0x046F;
 t['ktsquare'] = 0x33CF;
 t['kturned'] = 0x029E;
 t['kuhiragana'] = 0x304F;
 t['kukatakana'] = 0x30AF;
 t['kukatakanahalfwidth'] = 0xFF78;
 t['kvsquare'] = 0x33B8;
 t['kwsquare'] = 0x33BE;
 t['l'] = 0x006C;
 t['labengali'] = 0x09B2;
 t['lacute'] = 0x013A;
 t['ladeva'] = 0x0932;
 t['lagujarati'] = 0x0AB2;
 t['lagurmukhi'] = 0x0A32;
 t['lakkhangyaothai'] = 0x0E45;
 t['lamaleffinalarabic'] = 0xFEFC;
 t['lamalefhamzaabovefinalarabic'] = 0xFEF8;
 t['lamalefhamzaaboveisolatedarabic'] = 0xFEF7;
 t['lamalefhamzabelowfinalarabic'] = 0xFEFA;
 t['lamalefhamzabelowisolatedarabic'] = 0xFEF9;
 t['lamalefisolatedarabic'] = 0xFEFB;
 t['lamalefmaddaabovefinalarabic'] = 0xFEF6;
 t['lamalefmaddaaboveisolatedarabic'] = 0xFEF5;
 t['lamarabic'] = 0x0644;
 t['lambda'] = 0x03BB;
 t['lambdastroke'] = 0x019B;
 t['lamed'] = 0x05DC;
 t['lameddagesh'] = 0xFB3C;
 t['lameddageshhebrew'] = 0xFB3C;
 t['lamedhebrew'] = 0x05DC;
 t['lamfinalarabic'] = 0xFEDE;
 t['lamhahinitialarabic'] = 0xFCCA;
 t['laminitialarabic'] = 0xFEDF;
 t['lamjeeminitialarabic'] = 0xFCC9;
 t['lamkhahinitialarabic'] = 0xFCCB;
 t['lamlamhehisolatedarabic'] = 0xFDF2;
 t['lammedialarabic'] = 0xFEE0;
 t['lammeemhahinitialarabic'] = 0xFD88;
 t['lammeeminitialarabic'] = 0xFCCC;
 t['largecircle'] = 0x25EF;
 t['lbar'] = 0x019A;
 t['lbelt'] = 0x026C;
 t['lbopomofo'] = 0x310C;
 t['lcaron'] = 0x013E;
 t['lcedilla'] = 0x013C;
 t['lcircle'] = 0x24DB;
 t['lcircumflexbelow'] = 0x1E3D;
 t['lcommaaccent'] = 0x013C;
 t['ldot'] = 0x0140;
 t['ldotaccent'] = 0x0140;
 t['ldotbelow'] = 0x1E37;
 t['ldotbelowmacron'] = 0x1E39;
 t['leftangleabovecmb'] = 0x031A;
 t['lefttackbelowcmb'] = 0x0318;
 t['less'] = 0x003C;
 t['lessequal'] = 0x2264;
 t['lessequalorgreater'] = 0x22DA;
 t['lessmonospace'] = 0xFF1C;
 t['lessorequivalent'] = 0x2272;
 t['lessorgreater'] = 0x2276;
 t['lessoverequal'] = 0x2266;
 t['lesssmall'] = 0xFE64;
 t['lezh'] = 0x026E;
 t['lfblock'] = 0x258C;
 t['lhookretroflex'] = 0x026D;
 t['lira'] = 0x20A4;
 t['liwnarmenian'] = 0x056C;
 t['lj'] = 0x01C9;
 t['ljecyrillic'] = 0x0459;
 t['ll'] = 0xF6C0;
 t['lladeva'] = 0x0933;
 t['llagujarati'] = 0x0AB3;
 t['llinebelow'] = 0x1E3B;
 t['llladeva'] = 0x0934;
 t['llvocalicbengali'] = 0x09E1;
 t['llvocalicdeva'] = 0x0961;
 t['llvocalicvowelsignbengali'] = 0x09E3;
 t['llvocalicvowelsigndeva'] = 0x0963;
 t['lmiddletilde'] = 0x026B;
 t['lmonospace'] = 0xFF4C;
 t['lmsquare'] = 0x33D0;
 t['lochulathai'] = 0x0E2C;
 t['logicaland'] = 0x2227;
 t['logicalnot'] = 0x00AC;
 t['logicalnotreversed'] = 0x2310;
 t['logicalor'] = 0x2228;
 t['lolingthai'] = 0x0E25;
 t['longs'] = 0x017F;
 t['lowlinecenterline'] = 0xFE4E;
 t['lowlinecmb'] = 0x0332;
 t['lowlinedashed'] = 0xFE4D;
 t['lozenge'] = 0x25CA;
 t['lparen'] = 0x24A7;
 t['lslash'] = 0x0142;
 t['lsquare'] = 0x2113;
 t['lsuperior'] = 0xF6EE;
 t['ltshade'] = 0x2591;
 t['luthai'] = 0x0E26;
 t['lvocalicbengali'] = 0x098C;
 t['lvocalicdeva'] = 0x090C;
 t['lvocalicvowelsignbengali'] = 0x09E2;
 t['lvocalicvowelsigndeva'] = 0x0962;
 t['lxsquare'] = 0x33D3;
 t['m'] = 0x006D;
 t['mabengali'] = 0x09AE;
 t['macron'] = 0x00AF;
 t['macronbelowcmb'] = 0x0331;
 t['macroncmb'] = 0x0304;
 t['macronlowmod'] = 0x02CD;
 t['macronmonospace'] = 0xFFE3;
 t['macute'] = 0x1E3F;
 t['madeva'] = 0x092E;
 t['magujarati'] = 0x0AAE;
 t['magurmukhi'] = 0x0A2E;
 t['mahapakhhebrew'] = 0x05A4;
 t['mahapakhlefthebrew'] = 0x05A4;
 t['mahiragana'] = 0x307E;
 t['maichattawalowleftthai'] = 0xF895;
 t['maichattawalowrightthai'] = 0xF894;
 t['maichattawathai'] = 0x0E4B;
 t['maichattawaupperleftthai'] = 0xF893;
 t['maieklowleftthai'] = 0xF88C;
 t['maieklowrightthai'] = 0xF88B;
 t['maiekthai'] = 0x0E48;
 t['maiekupperleftthai'] = 0xF88A;
 t['maihanakatleftthai'] = 0xF884;
 t['maihanakatthai'] = 0x0E31;
 t['maitaikhuleftthai'] = 0xF889;
 t['maitaikhuthai'] = 0x0E47;
 t['maitholowleftthai'] = 0xF88F;
 t['maitholowrightthai'] = 0xF88E;
 t['maithothai'] = 0x0E49;
 t['maithoupperleftthai'] = 0xF88D;
 t['maitrilowleftthai'] = 0xF892;
 t['maitrilowrightthai'] = 0xF891;
 t['maitrithai'] = 0x0E4A;
 t['maitriupperleftthai'] = 0xF890;
 t['maiyamokthai'] = 0x0E46;
 t['makatakana'] = 0x30DE;
 t['makatakanahalfwidth'] = 0xFF8F;
 t['male'] = 0x2642;
 t['mansyonsquare'] = 0x3347;
 t['maqafhebrew'] = 0x05BE;
 t['mars'] = 0x2642;
 t['masoracirclehebrew'] = 0x05AF;
 t['masquare'] = 0x3383;
 t['mbopomofo'] = 0x3107;
 t['mbsquare'] = 0x33D4;
 t['mcircle'] = 0x24DC;
 t['mcubedsquare'] = 0x33A5;
 t['mdotaccent'] = 0x1E41;
 t['mdotbelow'] = 0x1E43;
 t['meemarabic'] = 0x0645;
 t['meemfinalarabic'] = 0xFEE2;
 t['meeminitialarabic'] = 0xFEE3;
 t['meemmedialarabic'] = 0xFEE4;
 t['meemmeeminitialarabic'] = 0xFCD1;
 t['meemmeemisolatedarabic'] = 0xFC48;
 t['meetorusquare'] = 0x334D;
 t['mehiragana'] = 0x3081;
 t['meizierasquare'] = 0x337E;
 t['mekatakana'] = 0x30E1;
 t['mekatakanahalfwidth'] = 0xFF92;
 t['mem'] = 0x05DE;
 t['memdagesh'] = 0xFB3E;
 t['memdageshhebrew'] = 0xFB3E;
 t['memhebrew'] = 0x05DE;
 t['menarmenian'] = 0x0574;
 t['merkhahebrew'] = 0x05A5;
 t['merkhakefulahebrew'] = 0x05A6;
 t['merkhakefulalefthebrew'] = 0x05A6;
 t['merkhalefthebrew'] = 0x05A5;
 t['mhook'] = 0x0271;
 t['mhzsquare'] = 0x3392;
 t['middledotkatakanahalfwidth'] = 0xFF65;
 t['middot'] = 0x00B7;
 t['mieumacirclekorean'] = 0x3272;
 t['mieumaparenkorean'] = 0x3212;
 t['mieumcirclekorean'] = 0x3264;
 t['mieumkorean'] = 0x3141;
 t['mieumpansioskorean'] = 0x3170;
 t['mieumparenkorean'] = 0x3204;
 t['mieumpieupkorean'] = 0x316E;
 t['mieumsioskorean'] = 0x316F;
 t['mihiragana'] = 0x307F;
 t['mikatakana'] = 0x30DF;
 t['mikatakanahalfwidth'] = 0xFF90;
 t['minus'] = 0x2212;
 t['minusbelowcmb'] = 0x0320;
 t['minuscircle'] = 0x2296;
 t['minusmod'] = 0x02D7;
 t['minusplus'] = 0x2213;
 t['minute'] = 0x2032;
 t['miribaarusquare'] = 0x334A;
 t['mirisquare'] = 0x3349;
 t['mlonglegturned'] = 0x0270;
 t['mlsquare'] = 0x3396;
 t['mmcubedsquare'] = 0x33A3;
 t['mmonospace'] = 0xFF4D;
 t['mmsquaredsquare'] = 0x339F;
 t['mohiragana'] = 0x3082;
 t['mohmsquare'] = 0x33C1;
 t['mokatakana'] = 0x30E2;
 t['mokatakanahalfwidth'] = 0xFF93;
 t['molsquare'] = 0x33D6;
 t['momathai'] = 0x0E21;
 t['moverssquare'] = 0x33A7;
 t['moverssquaredsquare'] = 0x33A8;
 t['mparen'] = 0x24A8;
 t['mpasquare'] = 0x33AB;
 t['mssquare'] = 0x33B3;
 t['msuperior'] = 0xF6EF;
 t['mturned'] = 0x026F;
 t['mu'] = 0x00B5;
 t['mu1'] = 0x00B5;
 t['muasquare'] = 0x3382;
 t['muchgreater'] = 0x226B;
 t['muchless'] = 0x226A;
 t['mufsquare'] = 0x338C;
 t['mugreek'] = 0x03BC;
 t['mugsquare'] = 0x338D;
 t['muhiragana'] = 0x3080;
 t['mukatakana'] = 0x30E0;
 t['mukatakanahalfwidth'] = 0xFF91;
 t['mulsquare'] = 0x3395;
 t['multiply'] = 0x00D7;
 t['mumsquare'] = 0x339B;
 t['munahhebrew'] = 0x05A3;
 t['munahlefthebrew'] = 0x05A3;
 t['musicalnote'] = 0x266A;
 t['musicalnotedbl'] = 0x266B;
 t['musicflatsign'] = 0x266D;
 t['musicsharpsign'] = 0x266F;
 t['mussquare'] = 0x33B2;
 t['muvsquare'] = 0x33B6;
 t['muwsquare'] = 0x33BC;
 t['mvmegasquare'] = 0x33B9;
 t['mvsquare'] = 0x33B7;
 t['mwmegasquare'] = 0x33BF;
 t['mwsquare'] = 0x33BD;
 t['n'] = 0x006E;
 t['nabengali'] = 0x09A8;
 t['nabla'] = 0x2207;
 t['nacute'] = 0x0144;
 t['nadeva'] = 0x0928;
 t['nagujarati'] = 0x0AA8;
 t['nagurmukhi'] = 0x0A28;
 t['nahiragana'] = 0x306A;
 t['nakatakana'] = 0x30CA;
 t['nakatakanahalfwidth'] = 0xFF85;
 t['napostrophe'] = 0x0149;
 t['nasquare'] = 0x3381;
 t['nbopomofo'] = 0x310B;
 t['nbspace'] = 0x00A0;
 t['ncaron'] = 0x0148;
 t['ncedilla'] = 0x0146;
 t['ncircle'] = 0x24DD;
 t['ncircumflexbelow'] = 0x1E4B;
 t['ncommaaccent'] = 0x0146;
 t['ndotaccent'] = 0x1E45;
 t['ndotbelow'] = 0x1E47;
 t['nehiragana'] = 0x306D;
 t['nekatakana'] = 0x30CD;
 t['nekatakanahalfwidth'] = 0xFF88;
 t['newsheqelsign'] = 0x20AA;
 t['nfsquare'] = 0x338B;
 t['ngabengali'] = 0x0999;
 t['ngadeva'] = 0x0919;
 t['ngagujarati'] = 0x0A99;
 t['ngagurmukhi'] = 0x0A19;
 t['ngonguthai'] = 0x0E07;
 t['nhiragana'] = 0x3093;
 t['nhookleft'] = 0x0272;
 t['nhookretroflex'] = 0x0273;
 t['nieunacirclekorean'] = 0x326F;
 t['nieunaparenkorean'] = 0x320F;
 t['nieuncieuckorean'] = 0x3135;
 t['nieuncirclekorean'] = 0x3261;
 t['nieunhieuhkorean'] = 0x3136;
 t['nieunkorean'] = 0x3134;
 t['nieunpansioskorean'] = 0x3168;
 t['nieunparenkorean'] = 0x3201;
 t['nieunsioskorean'] = 0x3167;
 t['nieuntikeutkorean'] = 0x3166;
 t['nihiragana'] = 0x306B;
 t['nikatakana'] = 0x30CB;
 t['nikatakanahalfwidth'] = 0xFF86;
 t['nikhahitleftthai'] = 0xF899;
 t['nikhahitthai'] = 0x0E4D;
 t['nine'] = 0x0039;
 t['ninearabic'] = 0x0669;
 t['ninebengali'] = 0x09EF;
 t['ninecircle'] = 0x2468;
 t['ninecircleinversesansserif'] = 0x2792;
 t['ninedeva'] = 0x096F;
 t['ninegujarati'] = 0x0AEF;
 t['ninegurmukhi'] = 0x0A6F;
 t['ninehackarabic'] = 0x0669;
 t['ninehangzhou'] = 0x3029;
 t['nineideographicparen'] = 0x3228;
 t['nineinferior'] = 0x2089;
 t['ninemonospace'] = 0xFF19;
 t['nineoldstyle'] = 0xF739;
 t['nineparen'] = 0x247C;
 t['nineperiod'] = 0x2490;
 t['ninepersian'] = 0x06F9;
 t['nineroman'] = 0x2178;
 t['ninesuperior'] = 0x2079;
 t['nineteencircle'] = 0x2472;
 t['nineteenparen'] = 0x2486;
 t['nineteenperiod'] = 0x249A;
 t['ninethai'] = 0x0E59;
 t['nj'] = 0x01CC;
 t['njecyrillic'] = 0x045A;
 t['nkatakana'] = 0x30F3;
 t['nkatakanahalfwidth'] = 0xFF9D;
 t['nlegrightlong'] = 0x019E;
 t['nlinebelow'] = 0x1E49;
 t['nmonospace'] = 0xFF4E;
 t['nmsquare'] = 0x339A;
 t['nnabengali'] = 0x09A3;
 t['nnadeva'] = 0x0923;
 t['nnagujarati'] = 0x0AA3;
 t['nnagurmukhi'] = 0x0A23;
 t['nnnadeva'] = 0x0929;
 t['nohiragana'] = 0x306E;
 t['nokatakana'] = 0x30CE;
 t['nokatakanahalfwidth'] = 0xFF89;
 t['nonbreakingspace'] = 0x00A0;
 t['nonenthai'] = 0x0E13;
 t['nonuthai'] = 0x0E19;
 t['noonarabic'] = 0x0646;
 t['noonfinalarabic'] = 0xFEE6;
 t['noonghunnaarabic'] = 0x06BA;
 t['noonghunnafinalarabic'] = 0xFB9F;
 t['nooninitialarabic'] = 0xFEE7;
 t['noonjeeminitialarabic'] = 0xFCD2;
 t['noonjeemisolatedarabic'] = 0xFC4B;
 t['noonmedialarabic'] = 0xFEE8;
 t['noonmeeminitialarabic'] = 0xFCD5;
 t['noonmeemisolatedarabic'] = 0xFC4E;
 t['noonnoonfinalarabic'] = 0xFC8D;
 t['notcontains'] = 0x220C;
 t['notelement'] = 0x2209;
 t['notelementof'] = 0x2209;
 t['notequal'] = 0x2260;
 t['notgreater'] = 0x226F;
 t['notgreaternorequal'] = 0x2271;
 t['notgreaternorless'] = 0x2279;
 t['notidentical'] = 0x2262;
 t['notless'] = 0x226E;
 t['notlessnorequal'] = 0x2270;
 t['notparallel'] = 0x2226;
 t['notprecedes'] = 0x2280;
 t['notsubset'] = 0x2284;
 t['notsucceeds'] = 0x2281;
 t['notsuperset'] = 0x2285;
 t['nowarmenian'] = 0x0576;
 t['nparen'] = 0x24A9;
 t['nssquare'] = 0x33B1;
 t['nsuperior'] = 0x207F;
 t['ntilde'] = 0x00F1;
 t['nu'] = 0x03BD;
 t['nuhiragana'] = 0x306C;
 t['nukatakana'] = 0x30CC;
 t['nukatakanahalfwidth'] = 0xFF87;
 t['nuktabengali'] = 0x09BC;
 t['nuktadeva'] = 0x093C;
 t['nuktagujarati'] = 0x0ABC;
 t['nuktagurmukhi'] = 0x0A3C;
 t['numbersign'] = 0x0023;
 t['numbersignmonospace'] = 0xFF03;
 t['numbersignsmall'] = 0xFE5F;
 t['numeralsigngreek'] = 0x0374;
 t['numeralsignlowergreek'] = 0x0375;
 t['numero'] = 0x2116;
 t['nun'] = 0x05E0;
 t['nundagesh'] = 0xFB40;
 t['nundageshhebrew'] = 0xFB40;
 t['nunhebrew'] = 0x05E0;
 t['nvsquare'] = 0x33B5;
 t['nwsquare'] = 0x33BB;
 t['nyabengali'] = 0x099E;
 t['nyadeva'] = 0x091E;
 t['nyagujarati'] = 0x0A9E;
 t['nyagurmukhi'] = 0x0A1E;
 t['o'] = 0x006F;
 t['oacute'] = 0x00F3;
 t['oangthai'] = 0x0E2D;
 t['obarred'] = 0x0275;
 t['obarredcyrillic'] = 0x04E9;
 t['obarreddieresiscyrillic'] = 0x04EB;
 t['obengali'] = 0x0993;
 t['obopomofo'] = 0x311B;
 t['obreve'] = 0x014F;
 t['ocandradeva'] = 0x0911;
 t['ocandragujarati'] = 0x0A91;
 t['ocandravowelsigndeva'] = 0x0949;
 t['ocandravowelsigngujarati'] = 0x0AC9;
 t['ocaron'] = 0x01D2;
 t['ocircle'] = 0x24DE;
 t['ocircumflex'] = 0x00F4;
 t['ocircumflexacute'] = 0x1ED1;
 t['ocircumflexdotbelow'] = 0x1ED9;
 t['ocircumflexgrave'] = 0x1ED3;
 t['ocircumflexhookabove'] = 0x1ED5;
 t['ocircumflextilde'] = 0x1ED7;
 t['ocyrillic'] = 0x043E;
 t['odblacute'] = 0x0151;
 t['odblgrave'] = 0x020D;
 t['odeva'] = 0x0913;
 t['odieresis'] = 0x00F6;
 t['odieresiscyrillic'] = 0x04E7;
 t['odotbelow'] = 0x1ECD;
 t['oe'] = 0x0153;
 t['oekorean'] = 0x315A;
 t['ogonek'] = 0x02DB;
 t['ogonekcmb'] = 0x0328;
 t['ograve'] = 0x00F2;
 t['ogujarati'] = 0x0A93;
 t['oharmenian'] = 0x0585;
 t['ohiragana'] = 0x304A;
 t['ohookabove'] = 0x1ECF;
 t['ohorn'] = 0x01A1;
 t['ohornacute'] = 0x1EDB;
 t['ohorndotbelow'] = 0x1EE3;
 t['ohorngrave'] = 0x1EDD;
 t['ohornhookabove'] = 0x1EDF;
 t['ohorntilde'] = 0x1EE1;
 t['ohungarumlaut'] = 0x0151;
 t['oi'] = 0x01A3;
 t['oinvertedbreve'] = 0x020F;
 t['okatakana'] = 0x30AA;
 t['okatakanahalfwidth'] = 0xFF75;
 t['okorean'] = 0x3157;
 t['olehebrew'] = 0x05AB;
 t['omacron'] = 0x014D;
 t['omacronacute'] = 0x1E53;
 t['omacrongrave'] = 0x1E51;
 t['omdeva'] = 0x0950;
 t['omega'] = 0x03C9;
 t['omega1'] = 0x03D6;
 t['omegacyrillic'] = 0x0461;
 t['omegalatinclosed'] = 0x0277;
 t['omegaroundcyrillic'] = 0x047B;
 t['omegatitlocyrillic'] = 0x047D;
 t['omegatonos'] = 0x03CE;
 t['omgujarati'] = 0x0AD0;
 t['omicron'] = 0x03BF;
 t['omicrontonos'] = 0x03CC;
 t['omonospace'] = 0xFF4F;
 t['one'] = 0x0031;
 t['onearabic'] = 0x0661;
 t['onebengali'] = 0x09E7;
 t['onecircle'] = 0x2460;
 t['onecircleinversesansserif'] = 0x278A;
 t['onedeva'] = 0x0967;
 t['onedotenleader'] = 0x2024;
 t['oneeighth'] = 0x215B;
 t['onefitted'] = 0xF6DC;
 t['onegujarati'] = 0x0AE7;
 t['onegurmukhi'] = 0x0A67;
 t['onehackarabic'] = 0x0661;
 t['onehalf'] = 0x00BD;
 t['onehangzhou'] = 0x3021;
 t['oneideographicparen'] = 0x3220;
 t['oneinferior'] = 0x2081;
 t['onemonospace'] = 0xFF11;
 t['onenumeratorbengali'] = 0x09F4;
 t['oneoldstyle'] = 0xF731;
 t['oneparen'] = 0x2474;
 t['oneperiod'] = 0x2488;
 t['onepersian'] = 0x06F1;
 t['onequarter'] = 0x00BC;
 t['oneroman'] = 0x2170;
 t['onesuperior'] = 0x00B9;
 t['onethai'] = 0x0E51;
 t['onethird'] = 0x2153;
 t['oogonek'] = 0x01EB;
 t['oogonekmacron'] = 0x01ED;
 t['oogurmukhi'] = 0x0A13;
 t['oomatragurmukhi'] = 0x0A4B;
 t['oopen'] = 0x0254;
 t['oparen'] = 0x24AA;
 t['openbullet'] = 0x25E6;
 t['option'] = 0x2325;
 t['ordfeminine'] = 0x00AA;
 t['ordmasculine'] = 0x00BA;
 t['orthogonal'] = 0x221F;
 t['oshortdeva'] = 0x0912;
 t['oshortvowelsigndeva'] = 0x094A;
 t['oslash'] = 0x00F8;
 t['oslashacute'] = 0x01FF;
 t['osmallhiragana'] = 0x3049;
 t['osmallkatakana'] = 0x30A9;
 t['osmallkatakanahalfwidth'] = 0xFF6B;
 t['ostrokeacute'] = 0x01FF;
 t['osuperior'] = 0xF6F0;
 t['otcyrillic'] = 0x047F;
 t['otilde'] = 0x00F5;
 t['otildeacute'] = 0x1E4D;
 t['otildedieresis'] = 0x1E4F;
 t['oubopomofo'] = 0x3121;
 t['overline'] = 0x203E;
 t['overlinecenterline'] = 0xFE4A;
 t['overlinecmb'] = 0x0305;
 t['overlinedashed'] = 0xFE49;
 t['overlinedblwavy'] = 0xFE4C;
 t['overlinewavy'] = 0xFE4B;
 t['overscore'] = 0x00AF;
 t['ovowelsignbengali'] = 0x09CB;
 t['ovowelsigndeva'] = 0x094B;
 t['ovowelsigngujarati'] = 0x0ACB;
 t['p'] = 0x0070;
 t['paampssquare'] = 0x3380;
 t['paasentosquare'] = 0x332B;
 t['pabengali'] = 0x09AA;
 t['pacute'] = 0x1E55;
 t['padeva'] = 0x092A;
 t['pagedown'] = 0x21DF;
 t['pageup'] = 0x21DE;
 t['pagujarati'] = 0x0AAA;
 t['pagurmukhi'] = 0x0A2A;
 t['pahiragana'] = 0x3071;
 t['paiyannoithai'] = 0x0E2F;
 t['pakatakana'] = 0x30D1;
 t['palatalizationcyrilliccmb'] = 0x0484;
 t['palochkacyrillic'] = 0x04C0;
 t['pansioskorean'] = 0x317F;
 t['paragraph'] = 0x00B6;
 t['parallel'] = 0x2225;
 t['parenleft'] = 0x0028;
 t['parenleftaltonearabic'] = 0xFD3E;
 t['parenleftbt'] = 0xF8ED;
 t['parenleftex'] = 0xF8EC;
 t['parenleftinferior'] = 0x208D;
 t['parenleftmonospace'] = 0xFF08;
 t['parenleftsmall'] = 0xFE59;
 t['parenleftsuperior'] = 0x207D;
 t['parenlefttp'] = 0xF8EB;
 t['parenleftvertical'] = 0xFE35;
 t['parenright'] = 0x0029;
 t['parenrightaltonearabic'] = 0xFD3F;
 t['parenrightbt'] = 0xF8F8;
 t['parenrightex'] = 0xF8F7;
 t['parenrightinferior'] = 0x208E;
 t['parenrightmonospace'] = 0xFF09;
 t['parenrightsmall'] = 0xFE5A;
 t['parenrightsuperior'] = 0x207E;
 t['parenrighttp'] = 0xF8F6;
 t['parenrightvertical'] = 0xFE36;
 t['partialdiff'] = 0x2202;
 t['paseqhebrew'] = 0x05C0;
 t['pashtahebrew'] = 0x0599;
 t['pasquare'] = 0x33A9;
 t['patah'] = 0x05B7;
 t['patah11'] = 0x05B7;
 t['patah1d'] = 0x05B7;
 t['patah2a'] = 0x05B7;
 t['patahhebrew'] = 0x05B7;
 t['patahnarrowhebrew'] = 0x05B7;
 t['patahquarterhebrew'] = 0x05B7;
 t['patahwidehebrew'] = 0x05B7;
 t['pazerhebrew'] = 0x05A1;
 t['pbopomofo'] = 0x3106;
 t['pcircle'] = 0x24DF;
 t['pdotaccent'] = 0x1E57;
 t['pe'] = 0x05E4;
 t['pecyrillic'] = 0x043F;
 t['pedagesh'] = 0xFB44;
 t['pedageshhebrew'] = 0xFB44;
 t['peezisquare'] = 0x333B;
 t['pefinaldageshhebrew'] = 0xFB43;
 t['peharabic'] = 0x067E;
 t['peharmenian'] = 0x057A;
 t['pehebrew'] = 0x05E4;
 t['pehfinalarabic'] = 0xFB57;
 t['pehinitialarabic'] = 0xFB58;
 t['pehiragana'] = 0x307A;
 t['pehmedialarabic'] = 0xFB59;
 t['pekatakana'] = 0x30DA;
 t['pemiddlehookcyrillic'] = 0x04A7;
 t['perafehebrew'] = 0xFB4E;
 t['percent'] = 0x0025;
 t['percentarabic'] = 0x066A;
 t['percentmonospace'] = 0xFF05;
 t['percentsmall'] = 0xFE6A;
 t['period'] = 0x002E;
 t['periodarmenian'] = 0x0589;
 t['periodcentered'] = 0x00B7;
 t['periodhalfwidth'] = 0xFF61;
 t['periodinferior'] = 0xF6E7;
 t['periodmonospace'] = 0xFF0E;
 t['periodsmall'] = 0xFE52;
 t['periodsuperior'] = 0xF6E8;
 t['perispomenigreekcmb'] = 0x0342;
 t['perpendicular'] = 0x22A5;
 t['perthousand'] = 0x2030;
 t['peseta'] = 0x20A7;
 t['pfsquare'] = 0x338A;
 t['phabengali'] = 0x09AB;
 t['phadeva'] = 0x092B;
 t['phagujarati'] = 0x0AAB;
 t['phagurmukhi'] = 0x0A2B;
 t['phi'] = 0x03C6;
 t['phi1'] = 0x03D5;
 t['phieuphacirclekorean'] = 0x327A;
 t['phieuphaparenkorean'] = 0x321A;
 t['phieuphcirclekorean'] = 0x326C;
 t['phieuphkorean'] = 0x314D;
 t['phieuphparenkorean'] = 0x320C;
 t['philatin'] = 0x0278;
 t['phinthuthai'] = 0x0E3A;
 t['phisymbolgreek'] = 0x03D5;
 t['phook'] = 0x01A5;
 t['phophanthai'] = 0x0E1E;
 t['phophungthai'] = 0x0E1C;
 t['phosamphaothai'] = 0x0E20;
 t['pi'] = 0x03C0;
 t['pieupacirclekorean'] = 0x3273;
 t['pieupaparenkorean'] = 0x3213;
 t['pieupcieuckorean'] = 0x3176;
 t['pieupcirclekorean'] = 0x3265;
 t['pieupkiyeokkorean'] = 0x3172;
 t['pieupkorean'] = 0x3142;
 t['pieupparenkorean'] = 0x3205;
 t['pieupsioskiyeokkorean'] = 0x3174;
 t['pieupsioskorean'] = 0x3144;
 t['pieupsiostikeutkorean'] = 0x3175;
 t['pieupthieuthkorean'] = 0x3177;
 t['pieuptikeutkorean'] = 0x3173;
 t['pihiragana'] = 0x3074;
 t['pikatakana'] = 0x30D4;
 t['pisymbolgreek'] = 0x03D6;
 t['piwrarmenian'] = 0x0583;
 t['plus'] = 0x002B;
 t['plusbelowcmb'] = 0x031F;
 t['pluscircle'] = 0x2295;
 t['plusminus'] = 0x00B1;
 t['plusmod'] = 0x02D6;
 t['plusmonospace'] = 0xFF0B;
 t['plussmall'] = 0xFE62;
 t['plussuperior'] = 0x207A;
 t['pmonospace'] = 0xFF50;
 t['pmsquare'] = 0x33D8;
 t['pohiragana'] = 0x307D;
 t['pointingindexdownwhite'] = 0x261F;
 t['pointingindexleftwhite'] = 0x261C;
 t['pointingindexrightwhite'] = 0x261E;
 t['pointingindexupwhite'] = 0x261D;
 t['pokatakana'] = 0x30DD;
 t['poplathai'] = 0x0E1B;
 t['postalmark'] = 0x3012;
 t['postalmarkface'] = 0x3020;
 t['pparen'] = 0x24AB;
 t['precedes'] = 0x227A;
 t['prescription'] = 0x211E;
 t['primemod'] = 0x02B9;
 t['primereversed'] = 0x2035;
 t['product'] = 0x220F;
 t['projective'] = 0x2305;
 t['prolongedkana'] = 0x30FC;
 t['propellor'] = 0x2318;
 t['propersubset'] = 0x2282;
 t['propersuperset'] = 0x2283;
 t['proportion'] = 0x2237;
 t['proportional'] = 0x221D;
 t['psi'] = 0x03C8;
 t['psicyrillic'] = 0x0471;
 t['psilipneumatacyrilliccmb'] = 0x0486;
 t['pssquare'] = 0x33B0;
 t['puhiragana'] = 0x3077;
 t['pukatakana'] = 0x30D7;
 t['pvsquare'] = 0x33B4;
 t['pwsquare'] = 0x33BA;
 t['q'] = 0x0071;
 t['qadeva'] = 0x0958;
 t['qadmahebrew'] = 0x05A8;
 t['qafarabic'] = 0x0642;
 t['qaffinalarabic'] = 0xFED6;
 t['qafinitialarabic'] = 0xFED7;
 t['qafmedialarabic'] = 0xFED8;
 t['qamats'] = 0x05B8;
 t['qamats10'] = 0x05B8;
 t['qamats1a'] = 0x05B8;
 t['qamats1c'] = 0x05B8;
 t['qamats27'] = 0x05B8;
 t['qamats29'] = 0x05B8;
 t['qamats33'] = 0x05B8;
 t['qamatsde'] = 0x05B8;
 t['qamatshebrew'] = 0x05B8;
 t['qamatsnarrowhebrew'] = 0x05B8;
 t['qamatsqatanhebrew'] = 0x05B8;
 t['qamatsqatannarrowhebrew'] = 0x05B8;
 t['qamatsqatanquarterhebrew'] = 0x05B8;
 t['qamatsqatanwidehebrew'] = 0x05B8;
 t['qamatsquarterhebrew'] = 0x05B8;
 t['qamatswidehebrew'] = 0x05B8;
 t['qarneyparahebrew'] = 0x059F;
 t['qbopomofo'] = 0x3111;
 t['qcircle'] = 0x24E0;
 t['qhook'] = 0x02A0;
 t['qmonospace'] = 0xFF51;
 t['qof'] = 0x05E7;
 t['qofdagesh'] = 0xFB47;
 t['qofdageshhebrew'] = 0xFB47;
 t['qofhebrew'] = 0x05E7;
 t['qparen'] = 0x24AC;
 t['quarternote'] = 0x2669;
 t['qubuts'] = 0x05BB;
 t['qubuts18'] = 0x05BB;
 t['qubuts25'] = 0x05BB;
 t['qubuts31'] = 0x05BB;
 t['qubutshebrew'] = 0x05BB;
 t['qubutsnarrowhebrew'] = 0x05BB;
 t['qubutsquarterhebrew'] = 0x05BB;
 t['qubutswidehebrew'] = 0x05BB;
 t['question'] = 0x003F;
 t['questionarabic'] = 0x061F;
 t['questionarmenian'] = 0x055E;
 t['questiondown'] = 0x00BF;
 t['questiondownsmall'] = 0xF7BF;
 t['questiongreek'] = 0x037E;
 t['questionmonospace'] = 0xFF1F;
 t['questionsmall'] = 0xF73F;
 t['quotedbl'] = 0x0022;
 t['quotedblbase'] = 0x201E;
 t['quotedblleft'] = 0x201C;
 t['quotedblmonospace'] = 0xFF02;
 t['quotedblprime'] = 0x301E;
 t['quotedblprimereversed'] = 0x301D;
 t['quotedblright'] = 0x201D;
 t['quoteleft'] = 0x2018;
 t['quoteleftreversed'] = 0x201B;
 t['quotereversed'] = 0x201B;
 t['quoteright'] = 0x2019;
 t['quoterightn'] = 0x0149;
 t['quotesinglbase'] = 0x201A;
 t['quotesingle'] = 0x0027;
 t['quotesinglemonospace'] = 0xFF07;
 t['r'] = 0x0072;
 t['raarmenian'] = 0x057C;
 t['rabengali'] = 0x09B0;
 t['racute'] = 0x0155;
 t['radeva'] = 0x0930;
 t['radical'] = 0x221A;
 t['radicalex'] = 0xF8E5;
 t['radoverssquare'] = 0x33AE;
 t['radoverssquaredsquare'] = 0x33AF;
 t['radsquare'] = 0x33AD;
 t['rafe'] = 0x05BF;
 t['rafehebrew'] = 0x05BF;
 t['ragujarati'] = 0x0AB0;
 t['ragurmukhi'] = 0x0A30;
 t['rahiragana'] = 0x3089;
 t['rakatakana'] = 0x30E9;
 t['rakatakanahalfwidth'] = 0xFF97;
 t['ralowerdiagonalbengali'] = 0x09F1;
 t['ramiddlediagonalbengali'] = 0x09F0;
 t['ramshorn'] = 0x0264;
 t['ratio'] = 0x2236;
 t['rbopomofo'] = 0x3116;
 t['rcaron'] = 0x0159;
 t['rcedilla'] = 0x0157;
 t['rcircle'] = 0x24E1;
 t['rcommaaccent'] = 0x0157;
 t['rdblgrave'] = 0x0211;
 t['rdotaccent'] = 0x1E59;
 t['rdotbelow'] = 0x1E5B;
 t['rdotbelowmacron'] = 0x1E5D;
 t['referencemark'] = 0x203B;
 t['reflexsubset'] = 0x2286;
 t['reflexsuperset'] = 0x2287;
 t['registered'] = 0x00AE;
 t['registersans'] = 0xF8E8;
 t['registerserif'] = 0xF6DA;
 t['reharabic'] = 0x0631;
 t['reharmenian'] = 0x0580;
 t['rehfinalarabic'] = 0xFEAE;
 t['rehiragana'] = 0x308C;
 t['rekatakana'] = 0x30EC;
 t['rekatakanahalfwidth'] = 0xFF9A;
 t['resh'] = 0x05E8;
 t['reshdageshhebrew'] = 0xFB48;
 t['reshhebrew'] = 0x05E8;
 t['reversedtilde'] = 0x223D;
 t['reviahebrew'] = 0x0597;
 t['reviamugrashhebrew'] = 0x0597;
 t['revlogicalnot'] = 0x2310;
 t['rfishhook'] = 0x027E;
 t['rfishhookreversed'] = 0x027F;
 t['rhabengali'] = 0x09DD;
 t['rhadeva'] = 0x095D;
 t['rho'] = 0x03C1;
 t['rhook'] = 0x027D;
 t['rhookturned'] = 0x027B;
 t['rhookturnedsuperior'] = 0x02B5;
 t['rhosymbolgreek'] = 0x03F1;
 t['rhotichookmod'] = 0x02DE;
 t['rieulacirclekorean'] = 0x3271;
 t['rieulaparenkorean'] = 0x3211;
 t['rieulcirclekorean'] = 0x3263;
 t['rieulhieuhkorean'] = 0x3140;
 t['rieulkiyeokkorean'] = 0x313A;
 t['rieulkiyeoksioskorean'] = 0x3169;
 t['rieulkorean'] = 0x3139;
 t['rieulmieumkorean'] = 0x313B;
 t['rieulpansioskorean'] = 0x316C;
 t['rieulparenkorean'] = 0x3203;
 t['rieulphieuphkorean'] = 0x313F;
 t['rieulpieupkorean'] = 0x313C;
 t['rieulpieupsioskorean'] = 0x316B;
 t['rieulsioskorean'] = 0x313D;
 t['rieulthieuthkorean'] = 0x313E;
 t['rieultikeutkorean'] = 0x316A;
 t['rieulyeorinhieuhkorean'] = 0x316D;
 t['rightangle'] = 0x221F;
 t['righttackbelowcmb'] = 0x0319;
 t['righttriangle'] = 0x22BF;
 t['rihiragana'] = 0x308A;
 t['rikatakana'] = 0x30EA;
 t['rikatakanahalfwidth'] = 0xFF98;
 t['ring'] = 0x02DA;
 t['ringbelowcmb'] = 0x0325;
 t['ringcmb'] = 0x030A;
 t['ringhalfleft'] = 0x02BF;
 t['ringhalfleftarmenian'] = 0x0559;
 t['ringhalfleftbelowcmb'] = 0x031C;
 t['ringhalfleftcentered'] = 0x02D3;
 t['ringhalfright'] = 0x02BE;
 t['ringhalfrightbelowcmb'] = 0x0339;
 t['ringhalfrightcentered'] = 0x02D2;
 t['rinvertedbreve'] = 0x0213;
 t['rittorusquare'] = 0x3351;
 t['rlinebelow'] = 0x1E5F;
 t['rlongleg'] = 0x027C;
 t['rlonglegturned'] = 0x027A;
 t['rmonospace'] = 0xFF52;
 t['rohiragana'] = 0x308D;
 t['rokatakana'] = 0x30ED;
 t['rokatakanahalfwidth'] = 0xFF9B;
 t['roruathai'] = 0x0E23;
 t['rparen'] = 0x24AD;
 t['rrabengali'] = 0x09DC;
 t['rradeva'] = 0x0931;
 t['rragurmukhi'] = 0x0A5C;
 t['rreharabic'] = 0x0691;
 t['rrehfinalarabic'] = 0xFB8D;
 t['rrvocalicbengali'] = 0x09E0;
 t['rrvocalicdeva'] = 0x0960;
 t['rrvocalicgujarati'] = 0x0AE0;
 t['rrvocalicvowelsignbengali'] = 0x09C4;
 t['rrvocalicvowelsigndeva'] = 0x0944;
 t['rrvocalicvowelsigngujarati'] = 0x0AC4;
 t['rsuperior'] = 0xF6F1;
 t['rtblock'] = 0x2590;
 t['rturned'] = 0x0279;
 t['rturnedsuperior'] = 0x02B4;
 t['ruhiragana'] = 0x308B;
 t['rukatakana'] = 0x30EB;
 t['rukatakanahalfwidth'] = 0xFF99;
 t['rupeemarkbengali'] = 0x09F2;
 t['rupeesignbengali'] = 0x09F3;
 t['rupiah'] = 0xF6DD;
 t['ruthai'] = 0x0E24;
 t['rvocalicbengali'] = 0x098B;
 t['rvocalicdeva'] = 0x090B;
 t['rvocalicgujarati'] = 0x0A8B;
 t['rvocalicvowelsignbengali'] = 0x09C3;
 t['rvocalicvowelsigndeva'] = 0x0943;
 t['rvocalicvowelsigngujarati'] = 0x0AC3;
 t['s'] = 0x0073;
 t['sabengali'] = 0x09B8;
 t['sacute'] = 0x015B;
 t['sacutedotaccent'] = 0x1E65;
 t['sadarabic'] = 0x0635;
 t['sadeva'] = 0x0938;
 t['sadfinalarabic'] = 0xFEBA;
 t['sadinitialarabic'] = 0xFEBB;
 t['sadmedialarabic'] = 0xFEBC;
 t['sagujarati'] = 0x0AB8;
 t['sagurmukhi'] = 0x0A38;
 t['sahiragana'] = 0x3055;
 t['sakatakana'] = 0x30B5;
 t['sakatakanahalfwidth'] = 0xFF7B;
 t['sallallahoualayhewasallamarabic'] = 0xFDFA;
 t['samekh'] = 0x05E1;
 t['samekhdagesh'] = 0xFB41;
 t['samekhdageshhebrew'] = 0xFB41;
 t['samekhhebrew'] = 0x05E1;
 t['saraaathai'] = 0x0E32;
 t['saraaethai'] = 0x0E41;
 t['saraaimaimalaithai'] = 0x0E44;
 t['saraaimaimuanthai'] = 0x0E43;
 t['saraamthai'] = 0x0E33;
 t['saraathai'] = 0x0E30;
 t['saraethai'] = 0x0E40;
 t['saraiileftthai'] = 0xF886;
 t['saraiithai'] = 0x0E35;
 t['saraileftthai'] = 0xF885;
 t['saraithai'] = 0x0E34;
 t['saraothai'] = 0x0E42;
 t['saraueeleftthai'] = 0xF888;
 t['saraueethai'] = 0x0E37;
 t['saraueleftthai'] = 0xF887;
 t['sarauethai'] = 0x0E36;
 t['sarauthai'] = 0x0E38;
 t['sarauuthai'] = 0x0E39;
 t['sbopomofo'] = 0x3119;
 t['scaron'] = 0x0161;
 t['scarondotaccent'] = 0x1E67;
 t['scedilla'] = 0x015F;
 t['schwa'] = 0x0259;
 t['schwacyrillic'] = 0x04D9;
 t['schwadieresiscyrillic'] = 0x04DB;
 t['schwahook'] = 0x025A;
 t['scircle'] = 0x24E2;
 t['scircumflex'] = 0x015D;
 t['scommaaccent'] = 0x0219;
 t['sdotaccent'] = 0x1E61;
 t['sdotbelow'] = 0x1E63;
 t['sdotbelowdotaccent'] = 0x1E69;
 t['seagullbelowcmb'] = 0x033C;
 t['second'] = 0x2033;
 t['secondtonechinese'] = 0x02CA;
 t['section'] = 0x00A7;
 t['seenarabic'] = 0x0633;
 t['seenfinalarabic'] = 0xFEB2;
 t['seeninitialarabic'] = 0xFEB3;
 t['seenmedialarabic'] = 0xFEB4;
 t['segol'] = 0x05B6;
 t['segol13'] = 0x05B6;
 t['segol1f'] = 0x05B6;
 t['segol2c'] = 0x05B6;
 t['segolhebrew'] = 0x05B6;
 t['segolnarrowhebrew'] = 0x05B6;
 t['segolquarterhebrew'] = 0x05B6;
 t['segoltahebrew'] = 0x0592;
 t['segolwidehebrew'] = 0x05B6;
 t['seharmenian'] = 0x057D;
 t['sehiragana'] = 0x305B;
 t['sekatakana'] = 0x30BB;
 t['sekatakanahalfwidth'] = 0xFF7E;
 t['semicolon'] = 0x003B;
 t['semicolonarabic'] = 0x061B;
 t['semicolonmonospace'] = 0xFF1B;
 t['semicolonsmall'] = 0xFE54;
 t['semivoicedmarkkana'] = 0x309C;
 t['semivoicedmarkkanahalfwidth'] = 0xFF9F;
 t['sentisquare'] = 0x3322;
 t['sentosquare'] = 0x3323;
 t['seven'] = 0x0037;
 t['sevenarabic'] = 0x0667;
 t['sevenbengali'] = 0x09ED;
 t['sevencircle'] = 0x2466;
 t['sevencircleinversesansserif'] = 0x2790;
 t['sevendeva'] = 0x096D;
 t['seveneighths'] = 0x215E;
 t['sevengujarati'] = 0x0AED;
 t['sevengurmukhi'] = 0x0A6D;
 t['sevenhackarabic'] = 0x0667;
 t['sevenhangzhou'] = 0x3027;
 t['sevenideographicparen'] = 0x3226;
 t['seveninferior'] = 0x2087;
 t['sevenmonospace'] = 0xFF17;
 t['sevenoldstyle'] = 0xF737;
 t['sevenparen'] = 0x247A;
 t['sevenperiod'] = 0x248E;
 t['sevenpersian'] = 0x06F7;
 t['sevenroman'] = 0x2176;
 t['sevensuperior'] = 0x2077;
 t['seventeencircle'] = 0x2470;
 t['seventeenparen'] = 0x2484;
 t['seventeenperiod'] = 0x2498;
 t['seventhai'] = 0x0E57;
 t['sfthyphen'] = 0x00AD;
 t['shaarmenian'] = 0x0577;
 t['shabengali'] = 0x09B6;
 t['shacyrillic'] = 0x0448;
 t['shaddaarabic'] = 0x0651;
 t['shaddadammaarabic'] = 0xFC61;
 t['shaddadammatanarabic'] = 0xFC5E;
 t['shaddafathaarabic'] = 0xFC60;
 t['shaddakasraarabic'] = 0xFC62;
 t['shaddakasratanarabic'] = 0xFC5F;
 t['shade'] = 0x2592;
 t['shadedark'] = 0x2593;
 t['shadelight'] = 0x2591;
 t['shademedium'] = 0x2592;
 t['shadeva'] = 0x0936;
 t['shagujarati'] = 0x0AB6;
 t['shagurmukhi'] = 0x0A36;
 t['shalshelethebrew'] = 0x0593;
 t['shbopomofo'] = 0x3115;
 t['shchacyrillic'] = 0x0449;
 t['sheenarabic'] = 0x0634;
 t['sheenfinalarabic'] = 0xFEB6;
 t['sheeninitialarabic'] = 0xFEB7;
 t['sheenmedialarabic'] = 0xFEB8;
 t['sheicoptic'] = 0x03E3;
 t['sheqel'] = 0x20AA;
 t['sheqelhebrew'] = 0x20AA;
 t['sheva'] = 0x05B0;
 t['sheva115'] = 0x05B0;
 t['sheva15'] = 0x05B0;
 t['sheva22'] = 0x05B0;
 t['sheva2e'] = 0x05B0;
 t['shevahebrew'] = 0x05B0;
 t['shevanarrowhebrew'] = 0x05B0;
 t['shevaquarterhebrew'] = 0x05B0;
 t['shevawidehebrew'] = 0x05B0;
 t['shhacyrillic'] = 0x04BB;
 t['shimacoptic'] = 0x03ED;
 t['shin'] = 0x05E9;
 t['shindagesh'] = 0xFB49;
 t['shindageshhebrew'] = 0xFB49;
 t['shindageshshindot'] = 0xFB2C;
 t['shindageshshindothebrew'] = 0xFB2C;
 t['shindageshsindot'] = 0xFB2D;
 t['shindageshsindothebrew'] = 0xFB2D;
 t['shindothebrew'] = 0x05C1;
 t['shinhebrew'] = 0x05E9;
 t['shinshindot'] = 0xFB2A;
 t['shinshindothebrew'] = 0xFB2A;
 t['shinsindot'] = 0xFB2B;
 t['shinsindothebrew'] = 0xFB2B;
 t['shook'] = 0x0282;
 t['sigma'] = 0x03C3;
 t['sigma1'] = 0x03C2;
 t['sigmafinal'] = 0x03C2;
 t['sigmalunatesymbolgreek'] = 0x03F2;
 t['sihiragana'] = 0x3057;
 t['sikatakana'] = 0x30B7;
 t['sikatakanahalfwidth'] = 0xFF7C;
 t['siluqhebrew'] = 0x05BD;
 t['siluqlefthebrew'] = 0x05BD;
 t['similar'] = 0x223C;
 t['sindothebrew'] = 0x05C2;
 t['siosacirclekorean'] = 0x3274;
 t['siosaparenkorean'] = 0x3214;
 t['sioscieuckorean'] = 0x317E;
 t['sioscirclekorean'] = 0x3266;
 t['sioskiyeokkorean'] = 0x317A;
 t['sioskorean'] = 0x3145;
 t['siosnieunkorean'] = 0x317B;
 t['siosparenkorean'] = 0x3206;
 t['siospieupkorean'] = 0x317D;
 t['siostikeutkorean'] = 0x317C;
 t['six'] = 0x0036;
 t['sixarabic'] = 0x0666;
 t['sixbengali'] = 0x09EC;
 t['sixcircle'] = 0x2465;
 t['sixcircleinversesansserif'] = 0x278F;
 t['sixdeva'] = 0x096C;
 t['sixgujarati'] = 0x0AEC;
 t['sixgurmukhi'] = 0x0A6C;
 t['sixhackarabic'] = 0x0666;
 t['sixhangzhou'] = 0x3026;
 t['sixideographicparen'] = 0x3225;
 t['sixinferior'] = 0x2086;
 t['sixmonospace'] = 0xFF16;
 t['sixoldstyle'] = 0xF736;
 t['sixparen'] = 0x2479;
 t['sixperiod'] = 0x248D;
 t['sixpersian'] = 0x06F6;
 t['sixroman'] = 0x2175;
 t['sixsuperior'] = 0x2076;
 t['sixteencircle'] = 0x246F;
 t['sixteencurrencydenominatorbengali'] = 0x09F9;
 t['sixteenparen'] = 0x2483;
 t['sixteenperiod'] = 0x2497;
 t['sixthai'] = 0x0E56;
 t['slash'] = 0x002F;
 t['slashmonospace'] = 0xFF0F;
 t['slong'] = 0x017F;
 t['slongdotaccent'] = 0x1E9B;
 t['smileface'] = 0x263A;
 t['smonospace'] = 0xFF53;
 t['sofpasuqhebrew'] = 0x05C3;
 t['softhyphen'] = 0x00AD;
 t['softsigncyrillic'] = 0x044C;
 t['sohiragana'] = 0x305D;
 t['sokatakana'] = 0x30BD;
 t['sokatakanahalfwidth'] = 0xFF7F;
 t['soliduslongoverlaycmb'] = 0x0338;
 t['solidusshortoverlaycmb'] = 0x0337;
 t['sorusithai'] = 0x0E29;
 t['sosalathai'] = 0x0E28;
 t['sosothai'] = 0x0E0B;
 t['sosuathai'] = 0x0E2A;
 t['space'] = 0x0020;
 t['spacehackarabic'] = 0x0020;
 t['spade'] = 0x2660;
 t['spadesuitblack'] = 0x2660;
 t['spadesuitwhite'] = 0x2664;
 t['sparen'] = 0x24AE;
 t['squarebelowcmb'] = 0x033B;
 t['squarecc'] = 0x33C4;
 t['squarecm'] = 0x339D;
 t['squarediagonalcrosshatchfill'] = 0x25A9;
 t['squarehorizontalfill'] = 0x25A4;
 t['squarekg'] = 0x338F;
 t['squarekm'] = 0x339E;
 t['squarekmcapital'] = 0x33CE;
 t['squareln'] = 0x33D1;
 t['squarelog'] = 0x33D2;
 t['squaremg'] = 0x338E;
 t['squaremil'] = 0x33D5;
 t['squaremm'] = 0x339C;
 t['squaremsquared'] = 0x33A1;
 t['squareorthogonalcrosshatchfill'] = 0x25A6;
 t['squareupperlefttolowerrightfill'] = 0x25A7;
 t['squareupperrighttolowerleftfill'] = 0x25A8;
 t['squareverticalfill'] = 0x25A5;
 t['squarewhitewithsmallblack'] = 0x25A3;
 t['srsquare'] = 0x33DB;
 t['ssabengali'] = 0x09B7;
 t['ssadeva'] = 0x0937;
 t['ssagujarati'] = 0x0AB7;
 t['ssangcieuckorean'] = 0x3149;
 t['ssanghieuhkorean'] = 0x3185;
 t['ssangieungkorean'] = 0x3180;
 t['ssangkiyeokkorean'] = 0x3132;
 t['ssangnieunkorean'] = 0x3165;
 t['ssangpieupkorean'] = 0x3143;
 t['ssangsioskorean'] = 0x3146;
 t['ssangtikeutkorean'] = 0x3138;
 t['ssuperior'] = 0xF6F2;
 t['sterling'] = 0x00A3;
 t['sterlingmonospace'] = 0xFFE1;
 t['strokelongoverlaycmb'] = 0x0336;
 t['strokeshortoverlaycmb'] = 0x0335;
 t['subset'] = 0x2282;
 t['subsetnotequal'] = 0x228A;
 t['subsetorequal'] = 0x2286;
 t['succeeds'] = 0x227B;
 t['suchthat'] = 0x220B;
 t['suhiragana'] = 0x3059;
 t['sukatakana'] = 0x30B9;
 t['sukatakanahalfwidth'] = 0xFF7D;
 t['sukunarabic'] = 0x0652;
 t['summation'] = 0x2211;
 t['sun'] = 0x263C;
 t['superset'] = 0x2283;
 t['supersetnotequal'] = 0x228B;
 t['supersetorequal'] = 0x2287;
 t['svsquare'] = 0x33DC;
 t['syouwaerasquare'] = 0x337C;
 t['t'] = 0x0074;
 t['tabengali'] = 0x09A4;
 t['tackdown'] = 0x22A4;
 t['tackleft'] = 0x22A3;
 t['tadeva'] = 0x0924;
 t['tagujarati'] = 0x0AA4;
 t['tagurmukhi'] = 0x0A24;
 t['taharabic'] = 0x0637;
 t['tahfinalarabic'] = 0xFEC2;
 t['tahinitialarabic'] = 0xFEC3;
 t['tahiragana'] = 0x305F;
 t['tahmedialarabic'] = 0xFEC4;
 t['taisyouerasquare'] = 0x337D;
 t['takatakana'] = 0x30BF;
 t['takatakanahalfwidth'] = 0xFF80;
 t['tatweelarabic'] = 0x0640;
 t['tau'] = 0x03C4;
 t['tav'] = 0x05EA;
 t['tavdages'] = 0xFB4A;
 t['tavdagesh'] = 0xFB4A;
 t['tavdageshhebrew'] = 0xFB4A;
 t['tavhebrew'] = 0x05EA;
 t['tbar'] = 0x0167;
 t['tbopomofo'] = 0x310A;
 t['tcaron'] = 0x0165;
 t['tccurl'] = 0x02A8;
 t['tcedilla'] = 0x0163;
 t['tcheharabic'] = 0x0686;
 t['tchehfinalarabic'] = 0xFB7B;
 t['tchehinitialarabic'] = 0xFB7C;
 t['tchehmedialarabic'] = 0xFB7D;
 t['tcircle'] = 0x24E3;
 t['tcircumflexbelow'] = 0x1E71;
 t['tcommaaccent'] = 0x0163;
 t['tdieresis'] = 0x1E97;
 t['tdotaccent'] = 0x1E6B;
 t['tdotbelow'] = 0x1E6D;
 t['tecyrillic'] = 0x0442;
 t['tedescendercyrillic'] = 0x04AD;
 t['teharabic'] = 0x062A;
 t['tehfinalarabic'] = 0xFE96;
 t['tehhahinitialarabic'] = 0xFCA2;
 t['tehhahisolatedarabic'] = 0xFC0C;
 t['tehinitialarabic'] = 0xFE97;
 t['tehiragana'] = 0x3066;
 t['tehjeeminitialarabic'] = 0xFCA1;
 t['tehjeemisolatedarabic'] = 0xFC0B;
 t['tehmarbutaarabic'] = 0x0629;
 t['tehmarbutafinalarabic'] = 0xFE94;
 t['tehmedialarabic'] = 0xFE98;
 t['tehmeeminitialarabic'] = 0xFCA4;
 t['tehmeemisolatedarabic'] = 0xFC0E;
 t['tehnoonfinalarabic'] = 0xFC73;
 t['tekatakana'] = 0x30C6;
 t['tekatakanahalfwidth'] = 0xFF83;
 t['telephone'] = 0x2121;
 t['telephoneblack'] = 0x260E;
 t['telishagedolahebrew'] = 0x05A0;
 t['telishaqetanahebrew'] = 0x05A9;
 t['tencircle'] = 0x2469;
 t['tenideographicparen'] = 0x3229;
 t['tenparen'] = 0x247D;
 t['tenperiod'] = 0x2491;
 t['tenroman'] = 0x2179;
 t['tesh'] = 0x02A7;
 t['tet'] = 0x05D8;
 t['tetdagesh'] = 0xFB38;
 t['tetdageshhebrew'] = 0xFB38;
 t['tethebrew'] = 0x05D8;
 t['tetsecyrillic'] = 0x04B5;
 t['tevirhebrew'] = 0x059B;
 t['tevirlefthebrew'] = 0x059B;
 t['thabengali'] = 0x09A5;
 t['thadeva'] = 0x0925;
 t['thagujarati'] = 0x0AA5;
 t['thagurmukhi'] = 0x0A25;
 t['thalarabic'] = 0x0630;
 t['thalfinalarabic'] = 0xFEAC;
 t['thanthakhatlowleftthai'] = 0xF898;
 t['thanthakhatlowrightthai'] = 0xF897;
 t['thanthakhatthai'] = 0x0E4C;
 t['thanthakhatupperleftthai'] = 0xF896;
 t['theharabic'] = 0x062B;
 t['thehfinalarabic'] = 0xFE9A;
 t['thehinitialarabic'] = 0xFE9B;
 t['thehmedialarabic'] = 0xFE9C;
 t['thereexists'] = 0x2203;
 t['therefore'] = 0x2234;
 t['theta'] = 0x03B8;
 t['theta1'] = 0x03D1;
 t['thetasymbolgreek'] = 0x03D1;
 t['thieuthacirclekorean'] = 0x3279;
 t['thieuthaparenkorean'] = 0x3219;
 t['thieuthcirclekorean'] = 0x326B;
 t['thieuthkorean'] = 0x314C;
 t['thieuthparenkorean'] = 0x320B;
 t['thirteencircle'] = 0x246C;
 t['thirteenparen'] = 0x2480;
 t['thirteenperiod'] = 0x2494;
 t['thonangmonthothai'] = 0x0E11;
 t['thook'] = 0x01AD;
 t['thophuthaothai'] = 0x0E12;
 t['thorn'] = 0x00FE;
 t['thothahanthai'] = 0x0E17;
 t['thothanthai'] = 0x0E10;
 t['thothongthai'] = 0x0E18;
 t['thothungthai'] = 0x0E16;
 t['thousandcyrillic'] = 0x0482;
 t['thousandsseparatorarabic'] = 0x066C;
 t['thousandsseparatorpersian'] = 0x066C;
 t['three'] = 0x0033;
 t['threearabic'] = 0x0663;
 t['threebengali'] = 0x09E9;
 t['threecircle'] = 0x2462;
 t['threecircleinversesansserif'] = 0x278C;
 t['threedeva'] = 0x0969;
 t['threeeighths'] = 0x215C;
 t['threegujarati'] = 0x0AE9;
 t['threegurmukhi'] = 0x0A69;
 t['threehackarabic'] = 0x0663;
 t['threehangzhou'] = 0x3023;
 t['threeideographicparen'] = 0x3222;
 t['threeinferior'] = 0x2083;
 t['threemonospace'] = 0xFF13;
 t['threenumeratorbengali'] = 0x09F6;
 t['threeoldstyle'] = 0xF733;
 t['threeparen'] = 0x2476;
 t['threeperiod'] = 0x248A;
 t['threepersian'] = 0x06F3;
 t['threequarters'] = 0x00BE;
 t['threequartersemdash'] = 0xF6DE;
 t['threeroman'] = 0x2172;
 t['threesuperior'] = 0x00B3;
 t['threethai'] = 0x0E53;
 t['thzsquare'] = 0x3394;
 t['tihiragana'] = 0x3061;
 t['tikatakana'] = 0x30C1;
 t['tikatakanahalfwidth'] = 0xFF81;
 t['tikeutacirclekorean'] = 0x3270;
 t['tikeutaparenkorean'] = 0x3210;
 t['tikeutcirclekorean'] = 0x3262;
 t['tikeutkorean'] = 0x3137;
 t['tikeutparenkorean'] = 0x3202;
 t['tilde'] = 0x02DC;
 t['tildebelowcmb'] = 0x0330;
 t['tildecmb'] = 0x0303;
 t['tildecomb'] = 0x0303;
 t['tildedoublecmb'] = 0x0360;
 t['tildeoperator'] = 0x223C;
 t['tildeoverlaycmb'] = 0x0334;
 t['tildeverticalcmb'] = 0x033E;
 t['timescircle'] = 0x2297;
 t['tipehahebrew'] = 0x0596;
 t['tipehalefthebrew'] = 0x0596;
 t['tippigurmukhi'] = 0x0A70;
 t['titlocyrilliccmb'] = 0x0483;
 t['tiwnarmenian'] = 0x057F;
 t['tlinebelow'] = 0x1E6F;
 t['tmonospace'] = 0xFF54;
 t['toarmenian'] = 0x0569;
 t['tohiragana'] = 0x3068;
 t['tokatakana'] = 0x30C8;
 t['tokatakanahalfwidth'] = 0xFF84;
 t['tonebarextrahighmod'] = 0x02E5;
 t['tonebarextralowmod'] = 0x02E9;
 t['tonebarhighmod'] = 0x02E6;
 t['tonebarlowmod'] = 0x02E8;
 t['tonebarmidmod'] = 0x02E7;
 t['tonefive'] = 0x01BD;
 t['tonesix'] = 0x0185;
 t['tonetwo'] = 0x01A8;
 t['tonos'] = 0x0384;
 t['tonsquare'] = 0x3327;
 t['topatakthai'] = 0x0E0F;
 t['tortoiseshellbracketleft'] = 0x3014;
 t['tortoiseshellbracketleftsmall'] = 0xFE5D;
 t['tortoiseshellbracketleftvertical'] = 0xFE39;
 t['tortoiseshellbracketright'] = 0x3015;
 t['tortoiseshellbracketrightsmall'] = 0xFE5E;
 t['tortoiseshellbracketrightvertical'] = 0xFE3A;
 t['totaothai'] = 0x0E15;
 t['tpalatalhook'] = 0x01AB;
 t['tparen'] = 0x24AF;
 t['trademark'] = 0x2122;
 t['trademarksans'] = 0xF8EA;
 t['trademarkserif'] = 0xF6DB;
 t['tretroflexhook'] = 0x0288;
 t['triagdn'] = 0x25BC;
 t['triaglf'] = 0x25C4;
 t['triagrt'] = 0x25BA;
 t['triagup'] = 0x25B2;
 t['ts'] = 0x02A6;
 t['tsadi'] = 0x05E6;
 t['tsadidagesh'] = 0xFB46;
 t['tsadidageshhebrew'] = 0xFB46;
 t['tsadihebrew'] = 0x05E6;
 t['tsecyrillic'] = 0x0446;
 t['tsere'] = 0x05B5;
 t['tsere12'] = 0x05B5;
 t['tsere1e'] = 0x05B5;
 t['tsere2b'] = 0x05B5;
 t['tserehebrew'] = 0x05B5;
 t['tserenarrowhebrew'] = 0x05B5;
 t['tserequarterhebrew'] = 0x05B5;
 t['tserewidehebrew'] = 0x05B5;
 t['tshecyrillic'] = 0x045B;
 t['tsuperior'] = 0xF6F3;
 t['ttabengali'] = 0x099F;
 t['ttadeva'] = 0x091F;
 t['ttagujarati'] = 0x0A9F;
 t['ttagurmukhi'] = 0x0A1F;
 t['tteharabic'] = 0x0679;
 t['ttehfinalarabic'] = 0xFB67;
 t['ttehinitialarabic'] = 0xFB68;
 t['ttehmedialarabic'] = 0xFB69;
 t['tthabengali'] = 0x09A0;
 t['tthadeva'] = 0x0920;
 t['tthagujarati'] = 0x0AA0;
 t['tthagurmukhi'] = 0x0A20;
 t['tturned'] = 0x0287;
 t['tuhiragana'] = 0x3064;
 t['tukatakana'] = 0x30C4;
 t['tukatakanahalfwidth'] = 0xFF82;
 t['tusmallhiragana'] = 0x3063;
 t['tusmallkatakana'] = 0x30C3;
 t['tusmallkatakanahalfwidth'] = 0xFF6F;
 t['twelvecircle'] = 0x246B;
 t['twelveparen'] = 0x247F;
 t['twelveperiod'] = 0x2493;
 t['twelveroman'] = 0x217B;
 t['twentycircle'] = 0x2473;
 t['twentyhangzhou'] = 0x5344;
 t['twentyparen'] = 0x2487;
 t['twentyperiod'] = 0x249B;
 t['two'] = 0x0032;
 t['twoarabic'] = 0x0662;
 t['twobengali'] = 0x09E8;
 t['twocircle'] = 0x2461;
 t['twocircleinversesansserif'] = 0x278B;
 t['twodeva'] = 0x0968;
 t['twodotenleader'] = 0x2025;
 t['twodotleader'] = 0x2025;
 t['twodotleadervertical'] = 0xFE30;
 t['twogujarati'] = 0x0AE8;
 t['twogurmukhi'] = 0x0A68;
 t['twohackarabic'] = 0x0662;
 t['twohangzhou'] = 0x3022;
 t['twoideographicparen'] = 0x3221;
 t['twoinferior'] = 0x2082;
 t['twomonospace'] = 0xFF12;
 t['twonumeratorbengali'] = 0x09F5;
 t['twooldstyle'] = 0xF732;
 t['twoparen'] = 0x2475;
 t['twoperiod'] = 0x2489;
 t['twopersian'] = 0x06F2;
 t['tworoman'] = 0x2171;
 t['twostroke'] = 0x01BB;
 t['twosuperior'] = 0x00B2;
 t['twothai'] = 0x0E52;
 t['twothirds'] = 0x2154;
 t['u'] = 0x0075;
 t['uacute'] = 0x00FA;
 t['ubar'] = 0x0289;
 t['ubengali'] = 0x0989;
 t['ubopomofo'] = 0x3128;
 t['ubreve'] = 0x016D;
 t['ucaron'] = 0x01D4;
 t['ucircle'] = 0x24E4;
 t['ucircumflex'] = 0x00FB;
 t['ucircumflexbelow'] = 0x1E77;
 t['ucyrillic'] = 0x0443;
 t['udattadeva'] = 0x0951;
 t['udblacute'] = 0x0171;
 t['udblgrave'] = 0x0215;
 t['udeva'] = 0x0909;
 t['udieresis'] = 0x00FC;
 t['udieresisacute'] = 0x01D8;
 t['udieresisbelow'] = 0x1E73;
 t['udieresiscaron'] = 0x01DA;
 t['udieresiscyrillic'] = 0x04F1;
 t['udieresisgrave'] = 0x01DC;
 t['udieresismacron'] = 0x01D6;
 t['udotbelow'] = 0x1EE5;
 t['ugrave'] = 0x00F9;
 t['ugujarati'] = 0x0A89;
 t['ugurmukhi'] = 0x0A09;
 t['uhiragana'] = 0x3046;
 t['uhookabove'] = 0x1EE7;
 t['uhorn'] = 0x01B0;
 t['uhornacute'] = 0x1EE9;
 t['uhorndotbelow'] = 0x1EF1;
 t['uhorngrave'] = 0x1EEB;
 t['uhornhookabove'] = 0x1EED;
 t['uhorntilde'] = 0x1EEF;
 t['uhungarumlaut'] = 0x0171;
 t['uhungarumlautcyrillic'] = 0x04F3;
 t['uinvertedbreve'] = 0x0217;
 t['ukatakana'] = 0x30A6;
 t['ukatakanahalfwidth'] = 0xFF73;
 t['ukcyrillic'] = 0x0479;
 t['ukorean'] = 0x315C;
 t['umacron'] = 0x016B;
 t['umacroncyrillic'] = 0x04EF;
 t['umacrondieresis'] = 0x1E7B;
 t['umatragurmukhi'] = 0x0A41;
 t['umonospace'] = 0xFF55;
 t['underscore'] = 0x005F;
 t['underscoredbl'] = 0x2017;
 t['underscoremonospace'] = 0xFF3F;
 t['underscorevertical'] = 0xFE33;
 t['underscorewavy'] = 0xFE4F;
 t['union'] = 0x222A;
 t['universal'] = 0x2200;
 t['uogonek'] = 0x0173;
 t['uparen'] = 0x24B0;
 t['upblock'] = 0x2580;
 t['upperdothebrew'] = 0x05C4;
 t['upsilon'] = 0x03C5;
 t['upsilondieresis'] = 0x03CB;
 t['upsilondieresistonos'] = 0x03B0;
 t['upsilonlatin'] = 0x028A;
 t['upsilontonos'] = 0x03CD;
 t['uptackbelowcmb'] = 0x031D;
 t['uptackmod'] = 0x02D4;
 t['uragurmukhi'] = 0x0A73;
 t['uring'] = 0x016F;
 t['ushortcyrillic'] = 0x045E;
 t['usmallhiragana'] = 0x3045;
 t['usmallkatakana'] = 0x30A5;
 t['usmallkatakanahalfwidth'] = 0xFF69;
 t['ustraightcyrillic'] = 0x04AF;
 t['ustraightstrokecyrillic'] = 0x04B1;
 t['utilde'] = 0x0169;
 t['utildeacute'] = 0x1E79;
 t['utildebelow'] = 0x1E75;
 t['uubengali'] = 0x098A;
 t['uudeva'] = 0x090A;
 t['uugujarati'] = 0x0A8A;
 t['uugurmukhi'] = 0x0A0A;
 t['uumatragurmukhi'] = 0x0A42;
 t['uuvowelsignbengali'] = 0x09C2;
 t['uuvowelsigndeva'] = 0x0942;
 t['uuvowelsigngujarati'] = 0x0AC2;
 t['uvowelsignbengali'] = 0x09C1;
 t['uvowelsigndeva'] = 0x0941;
 t['uvowelsigngujarati'] = 0x0AC1;
 t['v'] = 0x0076;
 t['vadeva'] = 0x0935;
 t['vagujarati'] = 0x0AB5;
 t['vagurmukhi'] = 0x0A35;
 t['vakatakana'] = 0x30F7;
 t['vav'] = 0x05D5;
 t['vavdagesh'] = 0xFB35;
 t['vavdagesh65'] = 0xFB35;
 t['vavdageshhebrew'] = 0xFB35;
 t['vavhebrew'] = 0x05D5;
 t['vavholam'] = 0xFB4B;
 t['vavholamhebrew'] = 0xFB4B;
 t['vavvavhebrew'] = 0x05F0;
 t['vavyodhebrew'] = 0x05F1;
 t['vcircle'] = 0x24E5;
 t['vdotbelow'] = 0x1E7F;
 t['vecyrillic'] = 0x0432;
 t['veharabic'] = 0x06A4;
 t['vehfinalarabic'] = 0xFB6B;
 t['vehinitialarabic'] = 0xFB6C;
 t['vehmedialarabic'] = 0xFB6D;
 t['vekatakana'] = 0x30F9;
 t['venus'] = 0x2640;
 t['verticalbar'] = 0x007C;
 t['verticallineabovecmb'] = 0x030D;
 t['verticallinebelowcmb'] = 0x0329;
 t['verticallinelowmod'] = 0x02CC;
 t['verticallinemod'] = 0x02C8;
 t['vewarmenian'] = 0x057E;
 t['vhook'] = 0x028B;
 t['vikatakana'] = 0x30F8;
 t['viramabengali'] = 0x09CD;
 t['viramadeva'] = 0x094D;
 t['viramagujarati'] = 0x0ACD;
 t['visargabengali'] = 0x0983;
 t['visargadeva'] = 0x0903;
 t['visargagujarati'] = 0x0A83;
 t['vmonospace'] = 0xFF56;
 t['voarmenian'] = 0x0578;
 t['voicediterationhiragana'] = 0x309E;
 t['voicediterationkatakana'] = 0x30FE;
 t['voicedmarkkana'] = 0x309B;
 t['voicedmarkkanahalfwidth'] = 0xFF9E;
 t['vokatakana'] = 0x30FA;
 t['vparen'] = 0x24B1;
 t['vtilde'] = 0x1E7D;
 t['vturned'] = 0x028C;
 t['vuhiragana'] = 0x3094;
 t['vukatakana'] = 0x30F4;
 t['w'] = 0x0077;
 t['wacute'] = 0x1E83;
 t['waekorean'] = 0x3159;
 t['wahiragana'] = 0x308F;
 t['wakatakana'] = 0x30EF;
 t['wakatakanahalfwidth'] = 0xFF9C;
 t['wakorean'] = 0x3158;
 t['wasmallhiragana'] = 0x308E;
 t['wasmallkatakana'] = 0x30EE;
 t['wattosquare'] = 0x3357;
 t['wavedash'] = 0x301C;
 t['wavyunderscorevertical'] = 0xFE34;
 t['wawarabic'] = 0x0648;
 t['wawfinalarabic'] = 0xFEEE;
 t['wawhamzaabovearabic'] = 0x0624;
 t['wawhamzaabovefinalarabic'] = 0xFE86;
 t['wbsquare'] = 0x33DD;
 t['wcircle'] = 0x24E6;
 t['wcircumflex'] = 0x0175;
 t['wdieresis'] = 0x1E85;
 t['wdotaccent'] = 0x1E87;
 t['wdotbelow'] = 0x1E89;
 t['wehiragana'] = 0x3091;
 t['weierstrass'] = 0x2118;
 t['wekatakana'] = 0x30F1;
 t['wekorean'] = 0x315E;
 t['weokorean'] = 0x315D;
 t['wgrave'] = 0x1E81;
 t['whitebullet'] = 0x25E6;
 t['whitecircle'] = 0x25CB;
 t['whitecircleinverse'] = 0x25D9;
 t['whitecornerbracketleft'] = 0x300E;
 t['whitecornerbracketleftvertical'] = 0xFE43;
 t['whitecornerbracketright'] = 0x300F;
 t['whitecornerbracketrightvertical'] = 0xFE44;
 t['whitediamond'] = 0x25C7;
 t['whitediamondcontainingblacksmalldiamond'] = 0x25C8;
 t['whitedownpointingsmalltriangle'] = 0x25BF;
 t['whitedownpointingtriangle'] = 0x25BD;
 t['whiteleftpointingsmalltriangle'] = 0x25C3;
 t['whiteleftpointingtriangle'] = 0x25C1;
 t['whitelenticularbracketleft'] = 0x3016;
 t['whitelenticularbracketright'] = 0x3017;
 t['whiterightpointingsmalltriangle'] = 0x25B9;
 t['whiterightpointingtriangle'] = 0x25B7;
 t['whitesmallsquare'] = 0x25AB;
 t['whitesmilingface'] = 0x263A;
 t['whitesquare'] = 0x25A1;
 t['whitestar'] = 0x2606;
 t['whitetelephone'] = 0x260F;
 t['whitetortoiseshellbracketleft'] = 0x3018;
 t['whitetortoiseshellbracketright'] = 0x3019;
 t['whiteuppointingsmalltriangle'] = 0x25B5;
 t['whiteuppointingtriangle'] = 0x25B3;
 t['wihiragana'] = 0x3090;
 t['wikatakana'] = 0x30F0;
 t['wikorean'] = 0x315F;
 t['wmonospace'] = 0xFF57;
 t['wohiragana'] = 0x3092;
 t['wokatakana'] = 0x30F2;
 t['wokatakanahalfwidth'] = 0xFF66;
 t['won'] = 0x20A9;
 t['wonmonospace'] = 0xFFE6;
 t['wowaenthai'] = 0x0E27;
 t['wparen'] = 0x24B2;
 t['wring'] = 0x1E98;
 t['wsuperior'] = 0x02B7;
 t['wturned'] = 0x028D;
 t['wynn'] = 0x01BF;
 t['x'] = 0x0078;
 t['xabovecmb'] = 0x033D;
 t['xbopomofo'] = 0x3112;
 t['xcircle'] = 0x24E7;
 t['xdieresis'] = 0x1E8D;
 t['xdotaccent'] = 0x1E8B;
 t['xeharmenian'] = 0x056D;
 t['xi'] = 0x03BE;
 t['xmonospace'] = 0xFF58;
 t['xparen'] = 0x24B3;
 t['xsuperior'] = 0x02E3;
 t['y'] = 0x0079;
 t['yaadosquare'] = 0x334E;
 t['yabengali'] = 0x09AF;
 t['yacute'] = 0x00FD;
 t['yadeva'] = 0x092F;
 t['yaekorean'] = 0x3152;
 t['yagujarati'] = 0x0AAF;
 t['yagurmukhi'] = 0x0A2F;
 t['yahiragana'] = 0x3084;
 t['yakatakana'] = 0x30E4;
 t['yakatakanahalfwidth'] = 0xFF94;
 t['yakorean'] = 0x3151;
 t['yamakkanthai'] = 0x0E4E;
 t['yasmallhiragana'] = 0x3083;
 t['yasmallkatakana'] = 0x30E3;
 t['yasmallkatakanahalfwidth'] = 0xFF6C;
 t['yatcyrillic'] = 0x0463;
 t['ycircle'] = 0x24E8;
 t['ycircumflex'] = 0x0177;
 t['ydieresis'] = 0x00FF;
 t['ydotaccent'] = 0x1E8F;
 t['ydotbelow'] = 0x1EF5;
 t['yeharabic'] = 0x064A;
 t['yehbarreearabic'] = 0x06D2;
 t['yehbarreefinalarabic'] = 0xFBAF;
 t['yehfinalarabic'] = 0xFEF2;
 t['yehhamzaabovearabic'] = 0x0626;
 t['yehhamzaabovefinalarabic'] = 0xFE8A;
 t['yehhamzaaboveinitialarabic'] = 0xFE8B;
 t['yehhamzaabovemedialarabic'] = 0xFE8C;
 t['yehinitialarabic'] = 0xFEF3;
 t['yehmedialarabic'] = 0xFEF4;
 t['yehmeeminitialarabic'] = 0xFCDD;
 t['yehmeemisolatedarabic'] = 0xFC58;
 t['yehnoonfinalarabic'] = 0xFC94;
 t['yehthreedotsbelowarabic'] = 0x06D1;
 t['yekorean'] = 0x3156;
 t['yen'] = 0x00A5;
 t['yenmonospace'] = 0xFFE5;
 t['yeokorean'] = 0x3155;
 t['yeorinhieuhkorean'] = 0x3186;
 t['yerahbenyomohebrew'] = 0x05AA;
 t['yerahbenyomolefthebrew'] = 0x05AA;
 t['yericyrillic'] = 0x044B;
 t['yerudieresiscyrillic'] = 0x04F9;
 t['yesieungkorean'] = 0x3181;
 t['yesieungpansioskorean'] = 0x3183;
 t['yesieungsioskorean'] = 0x3182;
 t['yetivhebrew'] = 0x059A;
 t['ygrave'] = 0x1EF3;
 t['yhook'] = 0x01B4;
 t['yhookabove'] = 0x1EF7;
 t['yiarmenian'] = 0x0575;
 t['yicyrillic'] = 0x0457;
 t['yikorean'] = 0x3162;
 t['yinyang'] = 0x262F;
 t['yiwnarmenian'] = 0x0582;
 t['ymonospace'] = 0xFF59;
 t['yod'] = 0x05D9;
 t['yoddagesh'] = 0xFB39;
 t['yoddageshhebrew'] = 0xFB39;
 t['yodhebrew'] = 0x05D9;
 t['yodyodhebrew'] = 0x05F2;
 t['yodyodpatahhebrew'] = 0xFB1F;
 t['yohiragana'] = 0x3088;
 t['yoikorean'] = 0x3189;
 t['yokatakana'] = 0x30E8;
 t['yokatakanahalfwidth'] = 0xFF96;
 t['yokorean'] = 0x315B;
 t['yosmallhiragana'] = 0x3087;
 t['yosmallkatakana'] = 0x30E7;
 t['yosmallkatakanahalfwidth'] = 0xFF6E;
 t['yotgreek'] = 0x03F3;
 t['yoyaekorean'] = 0x3188;
 t['yoyakorean'] = 0x3187;
 t['yoyakthai'] = 0x0E22;
 t['yoyingthai'] = 0x0E0D;
 t['yparen'] = 0x24B4;
 t['ypogegrammeni'] = 0x037A;
 t['ypogegrammenigreekcmb'] = 0x0345;
 t['yr'] = 0x01A6;
 t['yring'] = 0x1E99;
 t['ysuperior'] = 0x02B8;
 t['ytilde'] = 0x1EF9;
 t['yturned'] = 0x028E;
 t['yuhiragana'] = 0x3086;
 t['yuikorean'] = 0x318C;
 t['yukatakana'] = 0x30E6;
 t['yukatakanahalfwidth'] = 0xFF95;
 t['yukorean'] = 0x3160;
 t['yusbigcyrillic'] = 0x046B;
 t['yusbigiotifiedcyrillic'] = 0x046D;
 t['yuslittlecyrillic'] = 0x0467;
 t['yuslittleiotifiedcyrillic'] = 0x0469;
 t['yusmallhiragana'] = 0x3085;
 t['yusmallkatakana'] = 0x30E5;
 t['yusmallkatakanahalfwidth'] = 0xFF6D;
 t['yuyekorean'] = 0x318B;
 t['yuyeokorean'] = 0x318A;
 t['yyabengali'] = 0x09DF;
 t['yyadeva'] = 0x095F;
 t['z'] = 0x007A;
 t['zaarmenian'] = 0x0566;
 t['zacute'] = 0x017A;
 t['zadeva'] = 0x095B;
 t['zagurmukhi'] = 0x0A5B;
 t['zaharabic'] = 0x0638;
 t['zahfinalarabic'] = 0xFEC6;
 t['zahinitialarabic'] = 0xFEC7;
 t['zahiragana'] = 0x3056;
 t['zahmedialarabic'] = 0xFEC8;
 t['zainarabic'] = 0x0632;
 t['zainfinalarabic'] = 0xFEB0;
 t['zakatakana'] = 0x30B6;
 t['zaqefgadolhebrew'] = 0x0595;
 t['zaqefqatanhebrew'] = 0x0594;
 t['zarqahebrew'] = 0x0598;
 t['zayin'] = 0x05D6;
 t['zayindagesh'] = 0xFB36;
 t['zayindageshhebrew'] = 0xFB36;
 t['zayinhebrew'] = 0x05D6;
 t['zbopomofo'] = 0x3117;
 t['zcaron'] = 0x017E;
 t['zcircle'] = 0x24E9;
 t['zcircumflex'] = 0x1E91;
 t['zcurl'] = 0x0291;
 t['zdot'] = 0x017C;
 t['zdotaccent'] = 0x017C;
 t['zdotbelow'] = 0x1E93;
 t['zecyrillic'] = 0x0437;
 t['zedescendercyrillic'] = 0x0499;
 t['zedieresiscyrillic'] = 0x04DF;
 t['zehiragana'] = 0x305C;
 t['zekatakana'] = 0x30BC;
 t['zero'] = 0x0030;
 t['zeroarabic'] = 0x0660;
 t['zerobengali'] = 0x09E6;
 t['zerodeva'] = 0x0966;
 t['zerogujarati'] = 0x0AE6;
 t['zerogurmukhi'] = 0x0A66;
 t['zerohackarabic'] = 0x0660;
 t['zeroinferior'] = 0x2080;
 t['zeromonospace'] = 0xFF10;
 t['zerooldstyle'] = 0xF730;
 t['zeropersian'] = 0x06F0;
 t['zerosuperior'] = 0x2070;
 t['zerothai'] = 0x0E50;
 t['zerowidthjoiner'] = 0xFEFF;
 t['zerowidthnonjoiner'] = 0x200C;
 t['zerowidthspace'] = 0x200B;
 t['zeta'] = 0x03B6;
 t['zhbopomofo'] = 0x3113;
 t['zhearmenian'] = 0x056A;
 t['zhebrevecyrillic'] = 0x04C2;
 t['zhecyrillic'] = 0x0436;
 t['zhedescendercyrillic'] = 0x0497;
 t['zhedieresiscyrillic'] = 0x04DD;
 t['zihiragana'] = 0x3058;
 t['zikatakana'] = 0x30B8;
 t['zinorhebrew'] = 0x05AE;
 t['zlinebelow'] = 0x1E95;
 t['zmonospace'] = 0xFF5A;
 t['zohiragana'] = 0x305E;
 t['zokatakana'] = 0x30BE;
 t['zparen'] = 0x24B5;
 t['zretroflexhook'] = 0x0290;
 t['zstroke'] = 0x01B6;
 t['zuhiragana'] = 0x305A;
 t['zukatakana'] = 0x30BA;
 t['.notdef'] = 0x0000;
 t['angbracketleftbig'] = 0x2329;
 t['angbracketleftBig'] = 0x2329;
 t['angbracketleftbigg'] = 0x2329;
 t['angbracketleftBigg'] = 0x2329;
 t['angbracketrightBig'] = 0x232A;
 t['angbracketrightbig'] = 0x232A;
 t['angbracketrightBigg'] = 0x232A;
 t['angbracketrightbigg'] = 0x232A;
 t['arrowhookleft'] = 0x21AA;
 t['arrowhookright'] = 0x21A9;
 t['arrowlefttophalf'] = 0x21BC;
 t['arrowleftbothalf'] = 0x21BD;
 t['arrownortheast'] = 0x2197;
 t['arrownorthwest'] = 0x2196;
 t['arrowrighttophalf'] = 0x21C0;
 t['arrowrightbothalf'] = 0x21C1;
 t['arrowsoutheast'] = 0x2198;
 t['arrowsouthwest'] = 0x2199;
 t['backslashbig'] = 0x2216;
 t['backslashBig'] = 0x2216;
 t['backslashBigg'] = 0x2216;
 t['backslashbigg'] = 0x2216;
 t['bardbl'] = 0x2016;
 t['bracehtipdownleft'] = 0xFE37;
 t['bracehtipdownright'] = 0xFE37;
 t['bracehtipupleft'] = 0xFE38;
 t['bracehtipupright'] = 0xFE38;
 t['braceleftBig'] = 0x007B;
 t['braceleftbig'] = 0x007B;
 t['braceleftbigg'] = 0x007B;
 t['braceleftBigg'] = 0x007B;
 t['bracerightBig'] = 0x007D;
 t['bracerightbig'] = 0x007D;
 t['bracerightbigg'] = 0x007D;
 t['bracerightBigg'] = 0x007D;
 t['bracketleftbig'] = 0x005B;
 t['bracketleftBig'] = 0x005B;
 t['bracketleftbigg'] = 0x005B;
 t['bracketleftBigg'] = 0x005B;
 t['bracketrightBig'] = 0x005D;
 t['bracketrightbig'] = 0x005D;
 t['bracketrightbigg'] = 0x005D;
 t['bracketrightBigg'] = 0x005D;
 t['ceilingleftbig'] = 0x2308;
 t['ceilingleftBig'] = 0x2308;
 t['ceilingleftBigg'] = 0x2308;
 t['ceilingleftbigg'] = 0x2308;
 t['ceilingrightbig'] = 0x2309;
 t['ceilingrightBig'] = 0x2309;
 t['ceilingrightbigg'] = 0x2309;
 t['ceilingrightBigg'] = 0x2309;
 t['circledotdisplay'] = 0x2299;
 t['circledottext'] = 0x2299;
 t['circlemultiplydisplay'] = 0x2297;
 t['circlemultiplytext'] = 0x2297;
 t['circleplusdisplay'] = 0x2295;
 t['circleplustext'] = 0x2295;
 t['contintegraldisplay'] = 0x222E;
 t['contintegraltext'] = 0x222E;
 t['coproductdisplay'] = 0x2210;
 t['coproducttext'] = 0x2210;
 t['floorleftBig'] = 0x230A;
 t['floorleftbig'] = 0x230A;
 t['floorleftbigg'] = 0x230A;
 t['floorleftBigg'] = 0x230A;
 t['floorrightbig'] = 0x230B;
 t['floorrightBig'] = 0x230B;
 t['floorrightBigg'] = 0x230B;
 t['floorrightbigg'] = 0x230B;
 t['hatwide'] = 0x0302;
 t['hatwider'] = 0x0302;
 t['hatwidest'] = 0x0302;
 t['intercal'] = 0x1D40;
 t['integraldisplay'] = 0x222B;
 t['integraltext'] = 0x222B;
 t['intersectiondisplay'] = 0x22C2;
 t['intersectiontext'] = 0x22C2;
 t['logicalanddisplay'] = 0x2227;
 t['logicalandtext'] = 0x2227;
 t['logicalordisplay'] = 0x2228;
 t['logicalortext'] = 0x2228;
 t['parenleftBig'] = 0x0028;
 t['parenleftbig'] = 0x0028;
 t['parenleftBigg'] = 0x0028;
 t['parenleftbigg'] = 0x0028;
 t['parenrightBig'] = 0x0029;
 t['parenrightbig'] = 0x0029;
 t['parenrightBigg'] = 0x0029;
 t['parenrightbigg'] = 0x0029;
 t['prime'] = 0x2032;
 t['productdisplay'] = 0x220F;
 t['producttext'] = 0x220F;
 t['radicalbig'] = 0x221A;
 t['radicalBig'] = 0x221A;
 t['radicalBigg'] = 0x221A;
 t['radicalbigg'] = 0x221A;
 t['radicalbt'] = 0x221A;
 t['radicaltp'] = 0x221A;
 t['radicalvertex'] = 0x221A;
 t['slashbig'] = 0x002F;
 t['slashBig'] = 0x002F;
 t['slashBigg'] = 0x002F;
 t['slashbigg'] = 0x002F;
 t['summationdisplay'] = 0x2211;
 t['summationtext'] = 0x2211;
 t['tildewide'] = 0x02DC;
 t['tildewider'] = 0x02DC;
 t['tildewidest'] = 0x02DC;
 t['uniondisplay'] = 0x22C3;
 t['unionmultidisplay'] = 0x228E;
 t['unionmultitext'] = 0x228E;
 t['unionsqdisplay'] = 0x2294;
 t['unionsqtext'] = 0x2294;
 t['uniontext'] = 0x22C3;
 t['vextenddouble'] = 0x2225;
 t['vextendsingle'] = 0x2223;
});
var getDingbatsGlyphsUnicode = getLookupTableFactory(function (t) {
 t['space'] = 0x0020;
 t['a1'] = 0x2701;
 t['a2'] = 0x2702;
 t['a202'] = 0x2703;
 t['a3'] = 0x2704;
 t['a4'] = 0x260E;
 t['a5'] = 0x2706;
 t['a119'] = 0x2707;
 t['a118'] = 0x2708;
 t['a117'] = 0x2709;
 t['a11'] = 0x261B;
 t['a12'] = 0x261E;
 t['a13'] = 0x270C;
 t['a14'] = 0x270D;
 t['a15'] = 0x270E;
 t['a16'] = 0x270F;
 t['a105'] = 0x2710;
 t['a17'] = 0x2711;
 t['a18'] = 0x2712;
 t['a19'] = 0x2713;
 t['a20'] = 0x2714;
 t['a21'] = 0x2715;
 t['a22'] = 0x2716;
 t['a23'] = 0x2717;
 t['a24'] = 0x2718;
 t['a25'] = 0x2719;
 t['a26'] = 0x271A;
 t['a27'] = 0x271B;
 t['a28'] = 0x271C;
 t['a6'] = 0x271D;
 t['a7'] = 0x271E;
 t['a8'] = 0x271F;
 t['a9'] = 0x2720;
 t['a10'] = 0x2721;
 t['a29'] = 0x2722;
 t['a30'] = 0x2723;
 t['a31'] = 0x2724;
 t['a32'] = 0x2725;
 t['a33'] = 0x2726;
 t['a34'] = 0x2727;
 t['a35'] = 0x2605;
 t['a36'] = 0x2729;
 t['a37'] = 0x272A;
 t['a38'] = 0x272B;
 t['a39'] = 0x272C;
 t['a40'] = 0x272D;
 t['a41'] = 0x272E;
 t['a42'] = 0x272F;
 t['a43'] = 0x2730;
 t['a44'] = 0x2731;
 t['a45'] = 0x2732;
 t['a46'] = 0x2733;
 t['a47'] = 0x2734;
 t['a48'] = 0x2735;
 t['a49'] = 0x2736;
 t['a50'] = 0x2737;
 t['a51'] = 0x2738;
 t['a52'] = 0x2739;
 t['a53'] = 0x273A;
 t['a54'] = 0x273B;
 t['a55'] = 0x273C;
 t['a56'] = 0x273D;
 t['a57'] = 0x273E;
 t['a58'] = 0x273F;
 t['a59'] = 0x2740;
 t['a60'] = 0x2741;
 t['a61'] = 0x2742;
 t['a62'] = 0x2743;
 t['a63'] = 0x2744;
 t['a64'] = 0x2745;
 t['a65'] = 0x2746;
 t['a66'] = 0x2747;
 t['a67'] = 0x2748;
 t['a68'] = 0x2749;
 t['a69'] = 0x274A;
 t['a70'] = 0x274B;
 t['a71'] = 0x25CF;
 t['a72'] = 0x274D;
 t['a73'] = 0x25A0;
 t['a74'] = 0x274F;
 t['a203'] = 0x2750;
 t['a75'] = 0x2751;
 t['a204'] = 0x2752;
 t['a76'] = 0x25B2;
 t['a77'] = 0x25BC;
 t['a78'] = 0x25C6;
 t['a79'] = 0x2756;
 t['a81'] = 0x25D7;
 t['a82'] = 0x2758;
 t['a83'] = 0x2759;
 t['a84'] = 0x275A;
 t['a97'] = 0x275B;
 t['a98'] = 0x275C;
 t['a99'] = 0x275D;
 t['a100'] = 0x275E;
 t['a101'] = 0x2761;
 t['a102'] = 0x2762;
 t['a103'] = 0x2763;
 t['a104'] = 0x2764;
 t['a106'] = 0x2765;
 t['a107'] = 0x2766;
 t['a108'] = 0x2767;
 t['a112'] = 0x2663;
 t['a111'] = 0x2666;
 t['a110'] = 0x2665;
 t['a109'] = 0x2660;
 t['a120'] = 0x2460;
 t['a121'] = 0x2461;
 t['a122'] = 0x2462;
 t['a123'] = 0x2463;
 t['a124'] = 0x2464;
 t['a125'] = 0x2465;
 t['a126'] = 0x2466;
 t['a127'] = 0x2467;
 t['a128'] = 0x2468;
 t['a129'] = 0x2469;
 t['a130'] = 0x2776;
 t['a131'] = 0x2777;
 t['a132'] = 0x2778;
 t['a133'] = 0x2779;
 t['a134'] = 0x277A;
 t['a135'] = 0x277B;
 t['a136'] = 0x277C;
 t['a137'] = 0x277D;
 t['a138'] = 0x277E;
 t['a139'] = 0x277F;
 t['a140'] = 0x2780;
 t['a141'] = 0x2781;
 t['a142'] = 0x2782;
 t['a143'] = 0x2783;
 t['a144'] = 0x2784;
 t['a145'] = 0x2785;
 t['a146'] = 0x2786;
 t['a147'] = 0x2787;
 t['a148'] = 0x2788;
 t['a149'] = 0x2789;
 t['a150'] = 0x278A;
 t['a151'] = 0x278B;
 t['a152'] = 0x278C;
 t['a153'] = 0x278D;
 t['a154'] = 0x278E;
 t['a155'] = 0x278F;
 t['a156'] = 0x2790;
 t['a157'] = 0x2791;
 t['a158'] = 0x2792;
 t['a159'] = 0x2793;
 t['a160'] = 0x2794;
 t['a161'] = 0x2192;
 t['a163'] = 0x2194;
 t['a164'] = 0x2195;
 t['a196'] = 0x2798;
 t['a165'] = 0x2799;
 t['a192'] = 0x279A;
 t['a166'] = 0x279B;
 t['a167'] = 0x279C;
 t['a168'] = 0x279D;
 t['a169'] = 0x279E;
 t['a170'] = 0x279F;
 t['a171'] = 0x27A0;
 t['a172'] = 0x27A1;
 t['a173'] = 0x27A2;
 t['a162'] = 0x27A3;
 t['a174'] = 0x27A4;
 t['a175'] = 0x27A5;
 t['a176'] = 0x27A6;
 t['a177'] = 0x27A7;
 t['a178'] = 0x27A8;
 t['a179'] = 0x27A9;
 t['a193'] = 0x27AA;
 t['a180'] = 0x27AB;
 t['a199'] = 0x27AC;
 t['a181'] = 0x27AD;
 t['a200'] = 0x27AE;
 t['a182'] = 0x27AF;
 t['a201'] = 0x27B1;
 t['a183'] = 0x27B2;
 t['a184'] = 0x27B3;
 t['a197'] = 0x27B4;
 t['a185'] = 0x27B5;
 t['a194'] = 0x27B6;
 t['a198'] = 0x27B7;
 t['a186'] = 0x27B8;
 t['a195'] = 0x27B9;
 t['a187'] = 0x27BA;
 t['a188'] = 0x27BB;
 t['a189'] = 0x27BC;
 t['a190'] = 0x27BD;
 t['a191'] = 0x27BE;
 t['a89'] = 0x2768;
 t['a90'] = 0x2769;
 t['a93'] = 0x276A;
 t['a94'] = 0x276B;
 t['a91'] = 0x276C;
 t['a92'] = 0x276D;
 t['a205'] = 0x276E;
 t['a85'] = 0x276F;
 t['a206'] = 0x2770;
 t['a86'] = 0x2771;
 t['a87'] = 0x2772;
 t['a88'] = 0x2773;
 t['a95'] = 0x2774;
 t['a96'] = 0x2775;
 t['.notdef'] = 0x0000;
});
exports.getGlyphsUnicode = getGlyphsUnicode;
exports.getDingbatsGlyphsUnicode = getDingbatsGlyphsUnicode;

/***/ }),
/* 9 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.ChunkedStreamManager = exports.ChunkedStream = undefined;

var _util = __w_pdfjs_require__(0);

var ChunkedStream = function ChunkedStreamClosure() {
  function ChunkedStream(length, chunkSize, manager) {
    this.bytes = new Uint8Array(length);
    this.start = 0;
    this.pos = 0;
    this.end = length;
    this.chunkSize = chunkSize;
    this.loadedChunks = [];
    this.numChunksLoaded = 0;
    this.numChunks = Math.ceil(length / chunkSize);
    this.manager = manager;
    this.progressiveDataLength = 0;
    this.lastSuccessfulEnsureByteChunk = -1;
  }
  ChunkedStream.prototype = {
    getMissingChunks: function ChunkedStream_getMissingChunks() {
      var chunks = [];
      for (var chunk = 0, n = this.numChunks; chunk < n; ++chunk) {
        if (!this.loadedChunks[chunk]) {
          chunks.push(chunk);
        }
      }
      return chunks;
    },
    getBaseStreams: function ChunkedStream_getBaseStreams() {
      return [this];
    },
    allChunksLoaded: function ChunkedStream_allChunksLoaded() {
      return this.numChunksLoaded === this.numChunks;
    },
    onReceiveData: function ChunkedStream_onReceiveData(begin, chunk) {
      var end = begin + chunk.byteLength;
      if (begin % this.chunkSize !== 0) {
        throw new Error(`Bad begin offset: ${begin}`);
      }
      var length = this.bytes.length;
      if (end % this.chunkSize !== 0 && end !== length) {
        throw new Error(`Bad end offset: ${end}`);
      }
      this.bytes.set(new Uint8Array(chunk), begin);
      var chunkSize = this.chunkSize;
      var beginChunk = Math.floor(begin / chunkSize);
      var endChunk = Math.floor((end - 1) / chunkSize) + 1;
      var curChunk;
      for (curChunk = beginChunk; curChunk < endChunk; ++curChunk) {
        if (!this.loadedChunks[curChunk]) {
          this.loadedChunks[curChunk] = true;
          ++this.numChunksLoaded;
        }
      }
    },
    onReceiveProgressiveData: function ChunkedStream_onReceiveProgressiveData(data) {
      var position = this.progressiveDataLength;
      var beginChunk = Math.floor(position / this.chunkSize);
      this.bytes.set(new Uint8Array(data), position);
      position += data.byteLength;
      this.progressiveDataLength = position;
      var endChunk = position >= this.end ? this.numChunks : Math.floor(position / this.chunkSize);
      var curChunk;
      for (curChunk = beginChunk; curChunk < endChunk; ++curChunk) {
        if (!this.loadedChunks[curChunk]) {
          this.loadedChunks[curChunk] = true;
          ++this.numChunksLoaded;
        }
      }
    },
    ensureByte: function ChunkedStream_ensureByte(pos) {
      var chunk = Math.floor(pos / this.chunkSize);
      if (chunk === this.lastSuccessfulEnsureByteChunk) {
        return;
      }
      if (!this.loadedChunks[chunk]) {
        throw new _util.MissingDataException(pos, pos + 1);
      }
      this.lastSuccessfulEnsureByteChunk = chunk;
    },
    ensureRange: function ChunkedStream_ensureRange(begin, end) {
      if (begin >= end) {
        return;
      }
      if (end <= this.progressiveDataLength) {
        return;
      }
      var chunkSize = this.chunkSize;
      var beginChunk = Math.floor(begin / chunkSize);
      var endChunk = Math.floor((end - 1) / chunkSize) + 1;
      for (var chunk = beginChunk; chunk < endChunk; ++chunk) {
        if (!this.loadedChunks[chunk]) {
          throw new _util.MissingDataException(begin, end);
        }
      }
    },
    nextEmptyChunk: function ChunkedStream_nextEmptyChunk(beginChunk) {
      var chunk,
          numChunks = this.numChunks;
      for (var i = 0; i < numChunks; ++i) {
        chunk = (beginChunk + i) % numChunks;
        if (!this.loadedChunks[chunk]) {
          return chunk;
        }
      }
      return null;
    },
    hasChunk: function ChunkedStream_hasChunk(chunk) {
      return !!this.loadedChunks[chunk];
    },
    get length() {
      return this.end - this.start;
    },
    get isEmpty() {
      return this.length === 0;
    },
    getByte: function ChunkedStream_getByte() {
      var pos = this.pos;
      if (pos >= this.end) {
        return -1;
      }
      this.ensureByte(pos);
      return this.bytes[this.pos++];
    },
    getUint16: function ChunkedStream_getUint16() {
      var b0 = this.getByte();
      var b1 = this.getByte();
      if (b0 === -1 || b1 === -1) {
        return -1;
      }
      return (b0 << 8) + b1;
    },
    getInt32: function ChunkedStream_getInt32() {
      var b0 = this.getByte();
      var b1 = this.getByte();
      var b2 = this.getByte();
      var b3 = this.getByte();
      return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3;
    },
    getBytes: function ChunkedStream_getBytes(length) {
      var bytes = this.bytes;
      var pos = this.pos;
      var strEnd = this.end;
      if (!length) {
        this.ensureRange(pos, strEnd);
        return bytes.subarray(pos, strEnd);
      }
      var end = pos + length;
      if (end > strEnd) {
        end = strEnd;
      }
      this.ensureRange(pos, end);
      this.pos = end;
      return bytes.subarray(pos, end);
    },
    peekByte: function ChunkedStream_peekByte() {
      var peekedByte = this.getByte();
      this.pos--;
      return peekedByte;
    },
    peekBytes: function ChunkedStream_peekBytes(length) {
      var bytes = this.getBytes(length);
      this.pos -= bytes.length;
      return bytes;
    },
    getByteRange: function ChunkedStream_getBytes(begin, end) {
      this.ensureRange(begin, end);
      return this.bytes.subarray(begin, end);
    },
    skip: function ChunkedStream_skip(n) {
      if (!n) {
        n = 1;
      }
      this.pos += n;
    },
    reset: function ChunkedStream_reset() {
      this.pos = this.start;
    },
    moveStart: function ChunkedStream_moveStart() {
      this.start = this.pos;
    },
    makeSubStream: function ChunkedStream_makeSubStream(start, length, dict) {
      this.ensureRange(start, start + length);
      function ChunkedStreamSubstream() {}
      ChunkedStreamSubstream.prototype = Object.create(this);
      ChunkedStreamSubstream.prototype.getMissingChunks = function () {
        var chunkSize = this.chunkSize;
        var beginChunk = Math.floor(this.start / chunkSize);
        var endChunk = Math.floor((this.end - 1) / chunkSize) + 1;
        var missingChunks = [];
        for (var chunk = beginChunk; chunk < endChunk; ++chunk) {
          if (!this.loadedChunks[chunk]) {
            missingChunks.push(chunk);
          }
        }
        return missingChunks;
      };
      var subStream = new ChunkedStreamSubstream();
      subStream.pos = subStream.start = start;
      subStream.end = start + length || this.end;
      subStream.dict = dict;
      return subStream;
    }
  };
  return ChunkedStream;
}();
var ChunkedStreamManager = function ChunkedStreamManagerClosure() {
  function ChunkedStreamManager(pdfNetworkStream, args) {
    var chunkSize = args.rangeChunkSize;
    var length = args.length;
    this.stream = new ChunkedStream(length, chunkSize, this);
    this.length = length;
    this.chunkSize = chunkSize;
    this.pdfNetworkStream = pdfNetworkStream;
    this.url = args.url;
    this.disableAutoFetch = args.disableAutoFetch;
    this.msgHandler = args.msgHandler;
    this.currRequestId = 0;
    this.chunksNeededByRequest = Object.create(null);
    this.requestsByChunk = Object.create(null);
    this.promisesByRequest = Object.create(null);
    this.progressiveDataLength = 0;
    this.aborted = false;
    this._loadedStreamCapability = (0, _util.createPromiseCapability)();
  }
  ChunkedStreamManager.prototype = {
    onLoadedStream: function ChunkedStreamManager_getLoadedStream() {
      return this._loadedStreamCapability.promise;
    },
    sendRequest: function ChunkedStreamManager_sendRequest(begin, end) {
      var rangeReader = this.pdfNetworkStream.getRangeReader(begin, end);
      if (!rangeReader.isStreamingSupported) {
        rangeReader.onProgress = this.onProgress.bind(this);
      }
      var chunks = [],
          loaded = 0;
      var manager = this;
      var promise = new Promise(function (resolve, reject) {
        var readChunk = function (chunk) {
          try {
            if (!chunk.done) {
              var data = chunk.value;
              chunks.push(data);
              loaded += (0, _util.arrayByteLength)(data);
              if (rangeReader.isStreamingSupported) {
                manager.onProgress({ loaded });
              }
              rangeReader.read().then(readChunk, reject);
              return;
            }
            var chunkData = (0, _util.arraysToBytes)(chunks);
            chunks = null;
            resolve(chunkData);
          } catch (e) {
            reject(e);
          }
        };
        rangeReader.read().then(readChunk, reject);
      });
      promise.then(data => {
        if (this.aborted) {
          return;
        }
        this.onReceiveData({
          chunk: data,
          begin
        });
      });
    },
    requestAllChunks: function ChunkedStreamManager_requestAllChunks() {
      var missingChunks = this.stream.getMissingChunks();
      this._requestChunks(missingChunks);
      return this._loadedStreamCapability.promise;
    },
    _requestChunks: function ChunkedStreamManager_requestChunks(chunks) {
      var requestId = this.currRequestId++;
      var i, ii;
      var chunksNeeded = Object.create(null);
      this.chunksNeededByRequest[requestId] = chunksNeeded;
      for (i = 0, ii = chunks.length; i < ii; i++) {
        if (!this.stream.hasChunk(chunks[i])) {
          chunksNeeded[chunks[i]] = true;
        }
      }
      if ((0, _util.isEmptyObj)(chunksNeeded)) {
        return Promise.resolve();
      }
      var capability = (0, _util.createPromiseCapability)();
      this.promisesByRequest[requestId] = capability;
      var chunksToRequest = [];
      for (var chunk in chunksNeeded) {
        chunk = chunk | 0;
        if (!(chunk in this.requestsByChunk)) {
          this.requestsByChunk[chunk] = [];
          chunksToRequest.push(chunk);
        }
        this.requestsByChunk[chunk].push(requestId);
      }
      if (!chunksToRequest.length) {
        return capability.promise;
      }
      var groupedChunksToRequest = this.groupChunks(chunksToRequest);
      for (i = 0; i < groupedChunksToRequest.length; ++i) {
        var groupedChunk = groupedChunksToRequest[i];
        var begin = groupedChunk.beginChunk * this.chunkSize;
        var end = Math.min(groupedChunk.endChunk * this.chunkSize, this.length);
        this.sendRequest(begin, end);
      }
      return capability.promise;
    },
    getStream: function ChunkedStreamManager_getStream() {
      return this.stream;
    },
    requestRange: function ChunkedStreamManager_requestRange(begin, end) {
      end = Math.min(end, this.length);
      var beginChunk = this.getBeginChunk(begin);
      var endChunk = this.getEndChunk(end);
      var chunks = [];
      for (var chunk = beginChunk; chunk < endChunk; ++chunk) {
        chunks.push(chunk);
      }
      return this._requestChunks(chunks);
    },
    requestRanges: function ChunkedStreamManager_requestRanges(ranges) {
      ranges = ranges || [];
      var chunksToRequest = [];
      for (var i = 0; i < ranges.length; i++) {
        var beginChunk = this.getBeginChunk(ranges[i].begin);
        var endChunk = this.getEndChunk(ranges[i].end);
        for (var chunk = beginChunk; chunk < endChunk; ++chunk) {
          if (!chunksToRequest.includes(chunk)) {
            chunksToRequest.push(chunk);
          }
        }
      }
      chunksToRequest.sort(function (a, b) {
        return a - b;
      });
      return this._requestChunks(chunksToRequest);
    },
    groupChunks: function ChunkedStreamManager_groupChunks(chunks) {
      var groupedChunks = [];
      var beginChunk = -1;
      var prevChunk = -1;
      for (var i = 0; i < chunks.length; ++i) {
        var chunk = chunks[i];
        if (beginChunk < 0) {
          beginChunk = chunk;
        }
        if (prevChunk >= 0 && prevChunk + 1 !== chunk) {
          groupedChunks.push({
            beginChunk,
            endChunk: prevChunk + 1
          });
          beginChunk = chunk;
        }
        if (i + 1 === chunks.length) {
          groupedChunks.push({
            beginChunk,
            endChunk: chunk + 1
          });
        }
        prevChunk = chunk;
      }
      return groupedChunks;
    },
    onProgress: function ChunkedStreamManager_onProgress(args) {
      var bytesLoaded = this.stream.numChunksLoaded * this.chunkSize + args.loaded;
      this.msgHandler.send('DocProgress', {
        loaded: bytesLoaded,
        total: this.length
      });
    },
    onReceiveData: function ChunkedStreamManager_onReceiveData(args) {
      var chunk = args.chunk;
      var isProgressive = args.begin === undefined;
      var begin = isProgressive ? this.progressiveDataLength : args.begin;
      var end = begin + chunk.byteLength;
      var beginChunk = Math.floor(begin / this.chunkSize);
      var endChunk = end < this.length ? Math.floor(end / this.chunkSize) : Math.ceil(end / this.chunkSize);
      if (isProgressive) {
        this.stream.onReceiveProgressiveData(chunk);
        this.progressiveDataLength = end;
      } else {
        this.stream.onReceiveData(begin, chunk);
      }
      if (this.stream.allChunksLoaded()) {
        this._loadedStreamCapability.resolve(this.stream);
      }
      var loadedRequests = [];
      var i, requestId;
      for (chunk = beginChunk; chunk < endChunk; ++chunk) {
        var requestIds = this.requestsByChunk[chunk] || [];
        delete this.requestsByChunk[chunk];
        for (i = 0; i < requestIds.length; ++i) {
          requestId = requestIds[i];
          var chunksNeeded = this.chunksNeededByRequest[requestId];
          if (chunk in chunksNeeded) {
            delete chunksNeeded[chunk];
          }
          if (!(0, _util.isEmptyObj)(chunksNeeded)) {
            continue;
          }
          loadedRequests.push(requestId);
        }
      }
      if (!this.disableAutoFetch && (0, _util.isEmptyObj)(this.requestsByChunk)) {
        var nextEmptyChunk;
        if (this.stream.numChunksLoaded === 1) {
          var lastChunk = this.stream.numChunks - 1;
          if (!this.stream.hasChunk(lastChunk)) {
            nextEmptyChunk = lastChunk;
          }
        } else {
          nextEmptyChunk = this.stream.nextEmptyChunk(endChunk);
        }
        if (Number.isInteger(nextEmptyChunk)) {
          this._requestChunks([nextEmptyChunk]);
        }
      }
      for (i = 0; i < loadedRequests.length; ++i) {
        requestId = loadedRequests[i];
        var capability = this.promisesByRequest[requestId];
        delete this.promisesByRequest[requestId];
        capability.resolve();
      }
      this.msgHandler.send('DocProgress', {
        loaded: this.stream.numChunksLoaded * this.chunkSize,
        total: this.length
      });
    },
    onError: function ChunkedStreamManager_onError(err) {
      this._loadedStreamCapability.reject(err);
    },
    getBeginChunk: function ChunkedStreamManager_getBeginChunk(begin) {
      var chunk = Math.floor(begin / this.chunkSize);
      return chunk;
    },
    getEndChunk: function ChunkedStreamManager_getEndChunk(end) {
      var chunk = Math.floor((end - 1) / this.chunkSize) + 1;
      return chunk;
    },
    abort: function ChunkedStreamManager_abort() {
      this.aborted = true;
      if (this.pdfNetworkStream) {
        this.pdfNetworkStream.cancelAllRequests('abort');
      }
      for (var requestId in this.promisesByRequest) {
        var capability = this.promisesByRequest[requestId];
        capability.reject(new Error('Request was aborted'));
      }
    }
  };
  return ChunkedStreamManager;
}();
exports.ChunkedStream = ChunkedStream;
exports.ChunkedStreamManager = ChunkedStreamManager;

/***/ }),
/* 10 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.FileSpec = exports.XRef = exports.ObjectLoader = exports.Catalog = undefined;

var _util = __w_pdfjs_require__(0);

var _primitives = __w_pdfjs_require__(1);

var _parser = __w_pdfjs_require__(5);

var _chunked_stream = __w_pdfjs_require__(9);

var _crypto = __w_pdfjs_require__(14);

var _colorspace = __w_pdfjs_require__(3);

var Catalog = function CatalogClosure() {
  function Catalog(pdfManager, xref, pageFactory) {
    this.pdfManager = pdfManager;
    this.xref = xref;
    this.catDict = xref.getCatalogObj();
    if (!(0, _primitives.isDict)(this.catDict)) {
      throw new _util.FormatError('catalog object is not a dictionary');
    }
    this.fontCache = new _primitives.RefSetCache();
    this.builtInCMapCache = Object.create(null);
    this.pageKidsCountCache = new _primitives.RefSetCache();
    this.pageFactory = pageFactory;
    this.pagePromises = [];
  }
  Catalog.prototype = {
    get metadata() {
      var streamRef = this.catDict.getRaw('Metadata');
      if (!(0, _primitives.isRef)(streamRef)) {
        return (0, _util.shadow)(this, 'metadata', null);
      }
      var encryptMetadata = !this.xref.encrypt ? false : this.xref.encrypt.encryptMetadata;
      var stream = this.xref.fetch(streamRef, !encryptMetadata);
      var metadata;
      if (stream && (0, _primitives.isDict)(stream.dict)) {
        var type = stream.dict.get('Type');
        var subtype = stream.dict.get('Subtype');
        if ((0, _primitives.isName)(type, 'Metadata') && (0, _primitives.isName)(subtype, 'XML')) {
          try {
            metadata = (0, _util.stringToUTF8String)((0, _util.bytesToString)(stream.getBytes()));
          } catch (e) {
            if (e instanceof _util.MissingDataException) {
              throw e;
            }
            (0, _util.info)('Skipping invalid metadata.');
          }
        }
      }
      return (0, _util.shadow)(this, 'metadata', metadata);
    },
    get toplevelPagesDict() {
      var pagesObj = this.catDict.get('Pages');
      if (!(0, _primitives.isDict)(pagesObj)) {
        throw new _util.FormatError('invalid top-level pages dictionary');
      }
      return (0, _util.shadow)(this, 'toplevelPagesDict', pagesObj);
    },
    get documentOutline() {
      var obj = null;
      try {
        obj = this.readDocumentOutline();
      } catch (ex) {
        if (ex instanceof _util.MissingDataException) {
          throw ex;
        }
        (0, _util.warn)('Unable to read document outline');
      }
      return (0, _util.shadow)(this, 'documentOutline', obj);
    },
    readDocumentOutline: function Catalog_readDocumentOutline() {
      var obj = this.catDict.get('Outlines');
      if (!(0, _primitives.isDict)(obj)) {
        return null;
      }
      obj = obj.getRaw('First');
      if (!(0, _primitives.isRef)(obj)) {
        return null;
      }
      var root = { items: [] };
      var queue = [{
        obj,
        parent: root
      }];
      var processed = new _primitives.RefSet();
      processed.put(obj);
      var xref = this.xref,
          blackColor = new Uint8Array(3);
      while (queue.length > 0) {
        var i = queue.shift();
        var outlineDict = xref.fetchIfRef(i.obj);
        if (outlineDict === null) {
          continue;
        }
        if (!outlineDict.has('Title')) {
          throw new _util.FormatError('Invalid outline item');
        }
        var data = {
          url: null,
          dest: null
        };
        Catalog.parseDestDictionary({
          destDict: outlineDict,
          resultObj: data,
          docBaseUrl: this.pdfManager.docBaseUrl
        });
        var title = outlineDict.get('Title');
        var flags = outlineDict.get('F') || 0;
        var color = outlineDict.getArray('C'),
            rgbColor = blackColor;
        if (Array.isArray(color) && color.length === 3 && (color[0] !== 0 || color[1] !== 0 || color[2] !== 0)) {
          rgbColor = _colorspace.ColorSpace.singletons.rgb.getRgb(color, 0);
        }
        var outlineItem = {
          dest: data.dest,
          url: data.url,
          unsafeUrl: data.unsafeUrl,
          newWindow: data.newWindow,
          title: (0, _util.stringToPDFString)(title),
          color: rgbColor,
          count: outlineDict.get('Count'),
          bold: !!(flags & 2),
          italic: !!(flags & 1),
          items: []
        };
        i.parent.items.push(outlineItem);
        obj = outlineDict.getRaw('First');
        if ((0, _primitives.isRef)(obj) && !processed.has(obj)) {
          queue.push({
            obj,
            parent: outlineItem
          });
          processed.put(obj);
        }
        obj = outlineDict.getRaw('Next');
        if ((0, _primitives.isRef)(obj) && !processed.has(obj)) {
          queue.push({
            obj,
            parent: i.parent
          });
          processed.put(obj);
        }
      }
      return root.items.length > 0 ? root.items : null;
    },
    get numPages() {
      var obj = this.toplevelPagesDict.get('Count');
      if (!Number.isInteger(obj)) {
        throw new _util.FormatError('page count in top level pages object is not an integer');
      }
      return (0, _util.shadow)(this, 'numPages', obj);
    },
    get destinations() {
      function fetchDestination(dest) {
        return (0, _primitives.isDict)(dest) ? dest.get('D') : dest;
      }
      var xref = this.xref;
      var dests = {},
          nameTreeRef,
          nameDictionaryRef;
      var obj = this.catDict.get('Names');
      if (obj && obj.has('Dests')) {
        nameTreeRef = obj.getRaw('Dests');
      } else if (this.catDict.has('Dests')) {
        nameDictionaryRef = this.catDict.get('Dests');
      }
      if (nameDictionaryRef) {
        obj = nameDictionaryRef;
        obj.forEach(function catalogForEach(key, value) {
          if (!value) {
            return;
          }
          dests[key] = fetchDestination(value);
        });
      }
      if (nameTreeRef) {
        var nameTree = new NameTree(nameTreeRef, xref);
        var names = nameTree.getAll();
        for (var name in names) {
          dests[name] = fetchDestination(names[name]);
        }
      }
      return (0, _util.shadow)(this, 'destinations', dests);
    },
    getDestination: function Catalog_getDestination(destinationId) {
      function fetchDestination(dest) {
        return (0, _primitives.isDict)(dest) ? dest.get('D') : dest;
      }
      var xref = this.xref;
      var dest = null,
          nameTreeRef,
          nameDictionaryRef;
      var obj = this.catDict.get('Names');
      if (obj && obj.has('Dests')) {
        nameTreeRef = obj.getRaw('Dests');
      } else if (this.catDict.has('Dests')) {
        nameDictionaryRef = this.catDict.get('Dests');
      }
      if (nameDictionaryRef) {
        var value = nameDictionaryRef.get(destinationId);
        if (value) {
          dest = fetchDestination(value);
        }
      }
      if (nameTreeRef) {
        var nameTree = new NameTree(nameTreeRef, xref);
        dest = fetchDestination(nameTree.get(destinationId));
      }
      return dest;
    },
    get pageLabels() {
      var obj = null;
      try {
        obj = this.readPageLabels();
      } catch (ex) {
        if (ex instanceof _util.MissingDataException) {
          throw ex;
        }
        (0, _util.warn)('Unable to read page labels.');
      }
      return (0, _util.shadow)(this, 'pageLabels', obj);
    },
    readPageLabels: function Catalog_readPageLabels() {
      var obj = this.catDict.getRaw('PageLabels');
      if (!obj) {
        return null;
      }
      var pageLabels = new Array(this.numPages);
      var style = null;
      var prefix = '';
      var numberTree = new NumberTree(obj, this.xref);
      var nums = numberTree.getAll();
      var currentLabel = '',
          currentIndex = 1;
      for (var i = 0, ii = this.numPages; i < ii; i++) {
        if (i in nums) {
          var labelDict = nums[i];
          if (!(0, _primitives.isDict)(labelDict)) {
            throw new _util.FormatError('The PageLabel is not a dictionary.');
          }
          var type = labelDict.get('Type');
          if (type && !(0, _primitives.isName)(type, 'PageLabel')) {
            throw new _util.FormatError('Invalid type in PageLabel dictionary.');
          }
          var s = labelDict.get('S');
          if (s && !(0, _primitives.isName)(s)) {
            throw new _util.FormatError('Invalid style in PageLabel dictionary.');
          }
          style = s ? s.name : null;
          var p = labelDict.get('P');
          if (p && !(0, _util.isString)(p)) {
            throw new _util.FormatError('Invalid prefix in PageLabel dictionary.');
          }
          prefix = p ? (0, _util.stringToPDFString)(p) : '';
          var st = labelDict.get('St');
          if (st && !(Number.isInteger(st) && st >= 1)) {
            throw new _util.FormatError('Invalid start in PageLabel dictionary.');
          }
          currentIndex = st || 1;
        }
        switch (style) {
          case 'D':
            currentLabel = currentIndex;
            break;
          case 'R':
          case 'r':
            currentLabel = _util.Util.toRoman(currentIndex, style === 'r');
            break;
          case 'A':
          case 'a':
            var LIMIT = 26;
            var A_UPPER_CASE = 0x41,
                A_LOWER_CASE = 0x61;
            var baseCharCode = style === 'a' ? A_LOWER_CASE : A_UPPER_CASE;
            var letterIndex = currentIndex - 1;
            var character = String.fromCharCode(baseCharCode + letterIndex % LIMIT);
            var charBuf = [];
            for (var j = 0, jj = letterIndex / LIMIT | 0; j <= jj; j++) {
              charBuf.push(character);
            }
            currentLabel = charBuf.join('');
            break;
          default:
            if (style) {
              throw new _util.FormatError(`Invalid style "${style}" in PageLabel dictionary.`);
            }
        }
        pageLabels[i] = prefix + currentLabel;
        currentLabel = '';
        currentIndex++;
      }
      return pageLabels;
    },
    get pageMode() {
      let obj = this.catDict.get('PageMode');
      let pageMode = 'UseNone';
      if ((0, _primitives.isName)(obj)) {
        switch (obj.name) {
          case 'UseNone':
          case 'UseOutlines':
          case 'UseThumbs':
          case 'FullScreen':
          case 'UseOC':
          case 'UseAttachments':
            pageMode = obj.name;
        }
      }
      return (0, _util.shadow)(this, 'pageMode', pageMode);
    },
    get attachments() {
      var xref = this.xref;
      var attachments = null,
          nameTreeRef;
      var obj = this.catDict.get('Names');
      if (obj) {
        nameTreeRef = obj.getRaw('EmbeddedFiles');
      }
      if (nameTreeRef) {
        var nameTree = new NameTree(nameTreeRef, xref);
        var names = nameTree.getAll();
        for (var name in names) {
          var fs = new FileSpec(names[name], xref);
          if (!attachments) {
            attachments = Object.create(null);
          }
          attachments[(0, _util.stringToPDFString)(name)] = fs.serializable;
        }
      }
      return (0, _util.shadow)(this, 'attachments', attachments);
    },
    get javaScript() {
      var xref = this.xref;
      var obj = this.catDict.get('Names');
      let javaScript = null;
      function appendIfJavaScriptDict(jsDict) {
        var type = jsDict.get('S');
        if (!(0, _primitives.isName)(type, 'JavaScript')) {
          return;
        }
        var js = jsDict.get('JS');
        if ((0, _primitives.isStream)(js)) {
          js = (0, _util.bytesToString)(js.getBytes());
        } else if (!(0, _util.isString)(js)) {
          return;
        }
        if (!javaScript) {
          javaScript = [];
        }
        javaScript.push((0, _util.stringToPDFString)(js));
      }
      if (obj && obj.has('JavaScript')) {
        var nameTree = new NameTree(obj.getRaw('JavaScript'), xref);
        var names = nameTree.getAll();
        for (var name in names) {
          var jsDict = names[name];
          if ((0, _primitives.isDict)(jsDict)) {
            appendIfJavaScriptDict(jsDict);
          }
        }
      }
      var openactionDict = this.catDict.get('OpenAction');
      if ((0, _primitives.isDict)(openactionDict, 'Action')) {
        var actionType = openactionDict.get('S');
        if ((0, _primitives.isName)(actionType, 'Named')) {
          var action = openactionDict.get('N');
          if ((0, _primitives.isName)(action, 'Print')) {
            if (!javaScript) {
              javaScript = [];
            }
            javaScript.push('print({});');
          }
        } else {
          appendIfJavaScriptDict(openactionDict);
        }
      }
      return (0, _util.shadow)(this, 'javaScript', javaScript);
    },
    cleanup: function Catalog_cleanup() {
      this.pageKidsCountCache.clear();
      var promises = [];
      this.fontCache.forEach(function (promise) {
        promises.push(promise);
      });
      return Promise.all(promises).then(translatedFonts => {
        for (var i = 0, ii = translatedFonts.length; i < ii; i++) {
          var font = translatedFonts[i].dict;
          delete font.translated;
        }
        this.fontCache.clear();
        this.builtInCMapCache = Object.create(null);
      });
    },
    getPage: function Catalog_getPage(pageIndex) {
      if (!(pageIndex in this.pagePromises)) {
        this.pagePromises[pageIndex] = this.getPageDict(pageIndex).then(([dict, ref]) => {
          return this.pageFactory.createPage(pageIndex, dict, ref, this.fontCache, this.builtInCMapCache);
        });
      }
      return this.pagePromises[pageIndex];
    },
    getPageDict: function Catalog_getPageDict(pageIndex) {
      var capability = (0, _util.createPromiseCapability)();
      var nodesToVisit = [this.catDict.getRaw('Pages')];
      var count,
          currentPageIndex = 0;
      var xref = this.xref,
          pageKidsCountCache = this.pageKidsCountCache;
      function next() {
        while (nodesToVisit.length) {
          var currentNode = nodesToVisit.pop();
          if ((0, _primitives.isRef)(currentNode)) {
            count = pageKidsCountCache.get(currentNode);
            if (count > 0 && currentPageIndex + count < pageIndex) {
              currentPageIndex += count;
              continue;
            }
            xref.fetchAsync(currentNode).then(function (obj) {
              if ((0, _primitives.isDict)(obj, 'Page') || (0, _primitives.isDict)(obj) && !obj.has('Kids')) {
                if (pageIndex === currentPageIndex) {
                  if (currentNode && !pageKidsCountCache.has(currentNode)) {
                    pageKidsCountCache.put(currentNode, 1);
                  }
                  capability.resolve([obj, currentNode]);
                } else {
                  currentPageIndex++;
                  next();
                }
                return;
              }
              nodesToVisit.push(obj);
              next();
            }, capability.reject);
            return;
          }
          if (!(0, _primitives.isDict)(currentNode)) {
            capability.reject(new _util.FormatError('page dictionary kid reference points to wrong type of object'));
            return;
          }
          count = currentNode.get('Count');
          var objId = currentNode.objId;
          if (objId && !pageKidsCountCache.has(objId)) {
            pageKidsCountCache.put(objId, count);
          }
          if (currentPageIndex + count <= pageIndex) {
            currentPageIndex += count;
            continue;
          }
          var kids = currentNode.get('Kids');
          if (!Array.isArray(kids)) {
            capability.reject(new _util.FormatError('page dictionary kids object is not an array'));
            return;
          }
          for (var last = kids.length - 1; last >= 0; last--) {
            nodesToVisit.push(kids[last]);
          }
        }
        capability.reject(new Error('Page index ' + pageIndex + ' not found.'));
      }
      next();
      return capability.promise;
    },
    getPageIndex: function Catalog_getPageIndex(pageRef) {
      var xref = this.xref;
      function pagesBeforeRef(kidRef) {
        var total = 0;
        var parentRef;
        return xref.fetchAsync(kidRef).then(function (node) {
          if ((0, _primitives.isRefsEqual)(kidRef, pageRef) && !(0, _primitives.isDict)(node, 'Page') && !((0, _primitives.isDict)(node) && !node.has('Type') && node.has('Contents'))) {
            throw new _util.FormatError('The reference does not point to a /Page Dict.');
          }
          if (!node) {
            return null;
          }
          if (!(0, _primitives.isDict)(node)) {
            throw new _util.FormatError('node must be a Dict.');
          }
          parentRef = node.getRaw('Parent');
          return node.getAsync('Parent');
        }).then(function (parent) {
          if (!parent) {
            return null;
          }
          if (!(0, _primitives.isDict)(parent)) {
            throw new _util.FormatError('parent must be a Dict.');
          }
          return parent.getAsync('Kids');
        }).then(function (kids) {
          if (!kids) {
            return null;
          }
          var kidPromises = [];
          var found = false;
          for (var i = 0; i < kids.length; i++) {
            var kid = kids[i];
            if (!(0, _primitives.isRef)(kid)) {
              throw new _util.FormatError('kid must be a Ref.');
            }
            if (kid.num === kidRef.num) {
              found = true;
              break;
            }
            kidPromises.push(xref.fetchAsync(kid).then(function (kid) {
              if (kid.has('Count')) {
                var count = kid.get('Count');
                total += count;
              } else {
                total++;
              }
            }));
          }
          if (!found) {
            throw new _util.FormatError('kid ref not found in parents kids');
          }
          return Promise.all(kidPromises).then(function () {
            return [total, parentRef];
          });
        });
      }
      var total = 0;
      function next(ref) {
        return pagesBeforeRef(ref).then(function (args) {
          if (!args) {
            return total;
          }
          var count = args[0];
          var parentRef = args[1];
          total += count;
          return next(parentRef);
        });
      }
      return next(pageRef);
    }
  };
  Catalog.parseDestDictionary = function Catalog_parseDestDictionary(params) {
    function addDefaultProtocolToUrl(url) {
      if (url.indexOf('www.') === 0) {
        return 'http://' + url;
      }
      return url;
    }
    function tryConvertUrlEncoding(url) {
      try {
        return (0, _util.stringToUTF8String)(url);
      } catch (e) {
        return url;
      }
    }
    var destDict = params.destDict;
    if (!(0, _primitives.isDict)(destDict)) {
      (0, _util.warn)('parseDestDictionary: "destDict" must be a dictionary.');
      return;
    }
    var resultObj = params.resultObj;
    if (typeof resultObj !== 'object') {
      (0, _util.warn)('parseDestDictionary: "resultObj" must be an object.');
      return;
    }
    var docBaseUrl = params.docBaseUrl || null;
    var action = destDict.get('A'),
        url,
        dest;
    if (!(0, _primitives.isDict)(action) && destDict.has('Dest')) {
      action = destDict.get('Dest');
    }
    if ((0, _primitives.isDict)(action)) {
      let actionType = action.get('S');
      if (!(0, _primitives.isName)(actionType)) {
        (0, _util.warn)('parseDestDictionary: Invalid type in Action dictionary.');
        return;
      }
      let actionName = actionType.name;
      switch (actionName) {
        case 'URI':
          url = action.get('URI');
          if ((0, _primitives.isName)(url)) {
            url = '/' + url.name;
          } else if ((0, _util.isString)(url)) {
            url = addDefaultProtocolToUrl(url);
          }
          break;
        case 'GoTo':
          dest = action.get('D');
          break;
        case 'Launch':
        case 'GoToR':
          var urlDict = action.get('F');
          if ((0, _primitives.isDict)(urlDict)) {
            url = urlDict.get('F') || null;
          } else if ((0, _util.isString)(urlDict)) {
            url = urlDict;
          }
          var remoteDest = action.get('D');
          if (remoteDest) {
            if ((0, _primitives.isName)(remoteDest)) {
              remoteDest = remoteDest.name;
            }
            if ((0, _util.isString)(url)) {
              let baseUrl = url.split('#')[0];
              if ((0, _util.isString)(remoteDest)) {
                url = baseUrl + '#' + remoteDest;
              } else if (Array.isArray(remoteDest)) {
                url = baseUrl + '#' + JSON.stringify(remoteDest);
              }
            }
          }
          var newWindow = action.get('NewWindow');
          if ((0, _util.isBool)(newWindow)) {
            resultObj.newWindow = newWindow;
          }
          break;
        case 'Named':
          var namedAction = action.get('N');
          if ((0, _primitives.isName)(namedAction)) {
            resultObj.action = namedAction.name;
          }
          break;
        case 'JavaScript':
          var jsAction = action.get('JS'),
              js;
          if ((0, _primitives.isStream)(jsAction)) {
            js = (0, _util.bytesToString)(jsAction.getBytes());
          } else if ((0, _util.isString)(jsAction)) {
            js = jsAction;
          }
          if (js) {
            var URL_OPEN_METHODS = ['app.launchURL', 'window.open'];
            var regex = new RegExp('^\\s*(' + URL_OPEN_METHODS.join('|').split('.').join('\\.') + ')\\((?:\'|\")([^\'\"]*)(?:\'|\")(?:,\\s*(\\w+)\\)|\\))', 'i');
            var jsUrl = regex.exec((0, _util.stringToPDFString)(js));
            if (jsUrl && jsUrl[2]) {
              url = jsUrl[2];
              if (jsUrl[3] === 'true' && jsUrl[1] === 'app.launchURL') {
                resultObj.newWindow = true;
              }
              break;
            }
          }
        default:
          (0, _util.warn)(`parseDestDictionary: Unsupported Action type "${actionName}".`);
          break;
      }
    } else if (destDict.has('Dest')) {
      dest = destDict.get('Dest');
    }
    if ((0, _util.isString)(url)) {
      url = tryConvertUrlEncoding(url);
      var absoluteUrl = (0, _util.createValidAbsoluteUrl)(url, docBaseUrl);
      if (absoluteUrl) {
        resultObj.url = absoluteUrl.href;
      }
      resultObj.unsafeUrl = url;
    }
    if (dest) {
      if ((0, _primitives.isName)(dest)) {
        dest = dest.name;
      }
      if ((0, _util.isString)(dest) || Array.isArray(dest)) {
        resultObj.dest = dest;
      }
    }
  };
  return Catalog;
}();
var XRef = function XRefClosure() {
  function XRef(stream, pdfManager) {
    this.stream = stream;
    this.pdfManager = pdfManager;
    this.entries = [];
    this.xrefstms = Object.create(null);
    this.cache = [];
    this.stats = {
      streamTypes: [],
      fontTypes: []
    };
  }
  XRef.prototype = {
    setStartXRef: function XRef_setStartXRef(startXRef) {
      this.startXRefQueue = [startXRef];
    },
    parse: function XRef_parse(recoveryMode) {
      var trailerDict;
      if (!recoveryMode) {
        trailerDict = this.readXRef();
      } else {
        (0, _util.warn)('Indexing all PDF objects');
        trailerDict = this.indexObjects();
      }
      trailerDict.assignXref(this);
      this.trailer = trailerDict;
      var encrypt = trailerDict.get('Encrypt');
      if ((0, _primitives.isDict)(encrypt)) {
        var ids = trailerDict.get('ID');
        var fileId = ids && ids.length ? ids[0] : '';
        encrypt.suppressEncryption = true;
        this.encrypt = new _crypto.CipherTransformFactory(encrypt, fileId, this.pdfManager.password);
      }
      if (!(this.root = trailerDict.get('Root'))) {
        throw new _util.FormatError('Invalid root reference');
      }
    },
    processXRefTable: function XRef_processXRefTable(parser) {
      if (!('tableState' in this)) {
        this.tableState = {
          entryNum: 0,
          streamPos: parser.lexer.stream.pos,
          parserBuf1: parser.buf1,
          parserBuf2: parser.buf2
        };
      }
      var obj = this.readXRefTable(parser);
      if (!(0, _primitives.isCmd)(obj, 'trailer')) {
        throw new _util.FormatError('Invalid XRef table: could not find trailer dictionary');
      }
      var dict = parser.getObj();
      if (!(0, _primitives.isDict)(dict) && dict.dict) {
        dict = dict.dict;
      }
      if (!(0, _primitives.isDict)(dict)) {
        throw new _util.FormatError('Invalid XRef table: could not parse trailer dictionary');
      }
      delete this.tableState;
      return dict;
    },
    readXRefTable: function XRef_readXRefTable(parser) {
      var stream = parser.lexer.stream;
      var tableState = this.tableState;
      stream.pos = tableState.streamPos;
      parser.buf1 = tableState.parserBuf1;
      parser.buf2 = tableState.parserBuf2;
      var obj;
      while (true) {
        if (!('firstEntryNum' in tableState) || !('entryCount' in tableState)) {
          if ((0, _primitives.isCmd)(obj = parser.getObj(), 'trailer')) {
            break;
          }
          tableState.firstEntryNum = obj;
          tableState.entryCount = parser.getObj();
        }
        var first = tableState.firstEntryNum;
        var count = tableState.entryCount;
        if (!Number.isInteger(first) || !Number.isInteger(count)) {
          throw new _util.FormatError('Invalid XRef table: wrong types in subsection header');
        }
        for (var i = tableState.entryNum; i < count; i++) {
          tableState.streamPos = stream.pos;
          tableState.entryNum = i;
          tableState.parserBuf1 = parser.buf1;
          tableState.parserBuf2 = parser.buf2;
          var entry = {};
          entry.offset = parser.getObj();
          entry.gen = parser.getObj();
          var type = parser.getObj();
          if ((0, _primitives.isCmd)(type, 'f')) {
            entry.free = true;
          } else if ((0, _primitives.isCmd)(type, 'n')) {
            entry.uncompressed = true;
          }
          if (!Number.isInteger(entry.offset) || !Number.isInteger(entry.gen) || !(entry.free || entry.uncompressed)) {
            throw new _util.FormatError(`Invalid entry in XRef subsection: ${first}, ${count}`);
          }
          if (i === 0 && entry.free && first === 1) {
            first = 0;
          }
          if (!this.entries[i + first]) {
            this.entries[i + first] = entry;
          }
        }
        tableState.entryNum = 0;
        tableState.streamPos = stream.pos;
        tableState.parserBuf1 = parser.buf1;
        tableState.parserBuf2 = parser.buf2;
        delete tableState.firstEntryNum;
        delete tableState.entryCount;
      }
      if (this.entries[0] && !this.entries[0].free) {
        throw new _util.FormatError('Invalid XRef table: unexpected first object');
      }
      return obj;
    },
    processXRefStream: function XRef_processXRefStream(stream) {
      if (!('streamState' in this)) {
        var streamParameters = stream.dict;
        var byteWidths = streamParameters.get('W');
        var range = streamParameters.get('Index');
        if (!range) {
          range = [0, streamParameters.get('Size')];
        }
        this.streamState = {
          entryRanges: range,
          byteWidths,
          entryNum: 0,
          streamPos: stream.pos
        };
      }
      this.readXRefStream(stream);
      delete this.streamState;
      return stream.dict;
    },
    readXRefStream: function XRef_readXRefStream(stream) {
      var i, j;
      var streamState = this.streamState;
      stream.pos = streamState.streamPos;
      var byteWidths = streamState.byteWidths;
      var typeFieldWidth = byteWidths[0];
      var offsetFieldWidth = byteWidths[1];
      var generationFieldWidth = byteWidths[2];
      var entryRanges = streamState.entryRanges;
      while (entryRanges.length > 0) {
        var first = entryRanges[0];
        var n = entryRanges[1];
        if (!Number.isInteger(first) || !Number.isInteger(n)) {
          throw new _util.FormatError(`Invalid XRef range fields: ${first}, ${n}`);
        }
        if (!Number.isInteger(typeFieldWidth) || !Number.isInteger(offsetFieldWidth) || !Number.isInteger(generationFieldWidth)) {
          throw new _util.FormatError(`Invalid XRef entry fields length: ${first}, ${n}`);
        }
        for (i = streamState.entryNum; i < n; ++i) {
          streamState.entryNum = i;
          streamState.streamPos = stream.pos;
          var type = 0,
              offset = 0,
              generation = 0;
          for (j = 0; j < typeFieldWidth; ++j) {
            type = type << 8 | stream.getByte();
          }
          if (typeFieldWidth === 0) {
            type = 1;
          }
          for (j = 0; j < offsetFieldWidth; ++j) {
            offset = offset << 8 | stream.getByte();
          }
          for (j = 0; j < generationFieldWidth; ++j) {
            generation = generation << 8 | stream.getByte();
          }
          var entry = {};
          entry.offset = offset;
          entry.gen = generation;
          switch (type) {
            case 0:
              entry.free = true;
              break;
            case 1:
              entry.uncompressed = true;
              break;
            case 2:
              break;
            default:
              throw new _util.FormatError(`Invalid XRef entry type: ${type}`);
          }
          if (!this.entries[first + i]) {
            this.entries[first + i] = entry;
          }
        }
        streamState.entryNum = 0;
        streamState.streamPos = stream.pos;
        entryRanges.splice(0, 2);
      }
    },
    indexObjects: function XRef_indexObjects() {
      var TAB = 0x9,
          LF = 0xA,
          CR = 0xD,
          SPACE = 0x20;
      var PERCENT = 0x25,
          LT = 0x3C;
      function readToken(data, offset) {
        var token = '',
            ch = data[offset];
        while (ch !== LF && ch !== CR && ch !== LT) {
          if (++offset >= data.length) {
            break;
          }
          token += String.fromCharCode(ch);
          ch = data[offset];
        }
        return token;
      }
      function skipUntil(data, offset, what) {
        var length = what.length,
            dataLength = data.length;
        var skipped = 0;
        while (offset < dataLength) {
          var i = 0;
          while (i < length && data[offset + i] === what[i]) {
            ++i;
          }
          if (i >= length) {
            break;
          }
          offset++;
          skipped++;
        }
        return skipped;
      }
      var objRegExp = /^(\d+)\s+(\d+)\s+obj\b/;
      const endobjRegExp = /\bendobj[\b\s]$/;
      const nestedObjRegExp = /\s+(\d+\s+\d+\s+obj[\b\s])$/;
      const CHECK_CONTENT_LENGTH = 25;
      var trailerBytes = new Uint8Array([116, 114, 97, 105, 108, 101, 114]);
      var startxrefBytes = new Uint8Array([115, 116, 97, 114, 116, 120, 114, 101, 102]);
      const objBytes = new Uint8Array([111, 98, 106]);
      var xrefBytes = new Uint8Array([47, 88, 82, 101, 102]);
      this.entries.length = 0;
      var stream = this.stream;
      stream.pos = 0;
      var buffer = stream.getBytes();
      var position = stream.start,
          length = buffer.length;
      var trailers = [],
          xrefStms = [];
      while (position < length) {
        var ch = buffer[position];
        if (ch === TAB || ch === LF || ch === CR || ch === SPACE) {
          ++position;
          continue;
        }
        if (ch === PERCENT) {
          do {
            ++position;
            if (position >= length) {
              break;
            }
            ch = buffer[position];
          } while (ch !== LF && ch !== CR);
          continue;
        }
        var token = readToken(buffer, position);
        var m;
        if (token.indexOf('xref') === 0 && (token.length === 4 || /\s/.test(token[4]))) {
          position += skipUntil(buffer, position, trailerBytes);
          trailers.push(position);
          position += skipUntil(buffer, position, startxrefBytes);
        } else if (m = objRegExp.exec(token)) {
          if (typeof this.entries[m[1]] === 'undefined') {
            this.entries[m[1]] = {
              offset: position - stream.start,
              gen: m[2] | 0,
              uncompressed: true
            };
          }
          let contentLength,
              startPos = position + token.length;
          while (startPos < buffer.length) {
            let endPos = startPos + skipUntil(buffer, startPos, objBytes) + 4;
            contentLength = endPos - position;
            let checkPos = Math.max(endPos - CHECK_CONTENT_LENGTH, startPos);
            let tokenStr = (0, _util.bytesToString)(buffer.subarray(checkPos, endPos));
            if (endobjRegExp.test(tokenStr)) {
              break;
            } else {
              let objToken = nestedObjRegExp.exec(tokenStr);
              if (objToken && objToken[1]) {
                (0, _util.warn)('indexObjects: Found new "obj" inside of another "obj", ' + 'caused by missing "endobj" -- trying to recover.');
                contentLength -= objToken[1].length;
                break;
              }
            }
            startPos += contentLength;
          }
          let content = buffer.subarray(position, position + contentLength);
          var xrefTagOffset = skipUntil(content, 0, xrefBytes);
          if (xrefTagOffset < contentLength && content[xrefTagOffset + 5] < 64) {
            xrefStms.push(position - stream.start);
            this.xrefstms[position - stream.start] = 1;
          }
          position += contentLength;
        } else if (token.indexOf('trailer') === 0 && (token.length === 7 || /\s/.test(token[7]))) {
          trailers.push(position);
          position += skipUntil(buffer, position, startxrefBytes);
        } else {
          position += token.length + 1;
        }
      }
      var i, ii;
      for (i = 0, ii = xrefStms.length; i < ii; ++i) {
        this.startXRefQueue.push(xrefStms[i]);
        this.readXRef(true);
      }
      var dict;
      for (i = 0, ii = trailers.length; i < ii; ++i) {
        stream.pos = trailers[i];
        var parser = new _parser.Parser(new _parser.Lexer(stream), true, this, true);
        var obj = parser.getObj();
        if (!(0, _primitives.isCmd)(obj, 'trailer')) {
          continue;
        }
        dict = parser.getObj();
        if (!(0, _primitives.isDict)(dict)) {
          continue;
        }
        if (dict.has('ID')) {
          return dict;
        }
      }
      if (dict) {
        return dict;
      }
      throw new _util.InvalidPDFException('Invalid PDF structure');
    },
    readXRef: function XRef_readXRef(recoveryMode) {
      var stream = this.stream;
      let startXRefParsedCache = Object.create(null);
      try {
        while (this.startXRefQueue.length) {
          var startXRef = this.startXRefQueue[0];
          if (startXRefParsedCache[startXRef]) {
            (0, _util.warn)('readXRef - skipping XRef table since it was already parsed.');
            this.startXRefQueue.shift();
            continue;
          }
          startXRefParsedCache[startXRef] = true;
          stream.pos = startXRef + stream.start;
          var parser = new _parser.Parser(new _parser.Lexer(stream), true, this);
          var obj = parser.getObj();
          var dict;
          if ((0, _primitives.isCmd)(obj, 'xref')) {
            dict = this.processXRefTable(parser);
            if (!this.topDict) {
              this.topDict = dict;
            }
            obj = dict.get('XRefStm');
            if (Number.isInteger(obj)) {
              var pos = obj;
              if (!(pos in this.xrefstms)) {
                this.xrefstms[pos] = 1;
                this.startXRefQueue.push(pos);
              }
            }
          } else if (Number.isInteger(obj)) {
            if (!Number.isInteger(parser.getObj()) || !(0, _primitives.isCmd)(parser.getObj(), 'obj') || !(0, _primitives.isStream)(obj = parser.getObj())) {
              throw new _util.FormatError('Invalid XRef stream');
            }
            dict = this.processXRefStream(obj);
            if (!this.topDict) {
              this.topDict = dict;
            }
            if (!dict) {
              throw new _util.FormatError('Failed to read XRef stream');
            }
          } else {
            throw new _util.FormatError('Invalid XRef stream header');
          }
          obj = dict.get('Prev');
          if (Number.isInteger(obj)) {
            this.startXRefQueue.push(obj);
          } else if ((0, _primitives.isRef)(obj)) {
            this.startXRefQueue.push(obj.num);
          }
          this.startXRefQueue.shift();
        }
        return this.topDict;
      } catch (e) {
        if (e instanceof _util.MissingDataException) {
          throw e;
        }
        (0, _util.info)('(while reading XRef): ' + e);
      }
      if (recoveryMode) {
        return;
      }
      throw new _util.XRefParseException();
    },
    getEntry: function XRef_getEntry(i) {
      var xrefEntry = this.entries[i];
      if (xrefEntry && !xrefEntry.free && xrefEntry.offset) {
        return xrefEntry;
      }
      return null;
    },
    fetchIfRef: function XRef_fetchIfRef(obj, suppressEncryption) {
      if (!(0, _primitives.isRef)(obj)) {
        return obj;
      }
      return this.fetch(obj, suppressEncryption);
    },
    fetch: function XRef_fetch(ref, suppressEncryption) {
      if (!(0, _primitives.isRef)(ref)) {
        throw new Error('ref object is not a reference');
      }
      var num = ref.num;
      if (num in this.cache) {
        var cacheEntry = this.cache[num];
        if (cacheEntry instanceof _primitives.Dict && !cacheEntry.objId) {
          cacheEntry.objId = ref.toString();
        }
        return cacheEntry;
      }
      var xrefEntry = this.getEntry(num);
      if (xrefEntry === null) {
        return this.cache[num] = null;
      }
      if (xrefEntry.uncompressed) {
        xrefEntry = this.fetchUncompressed(ref, xrefEntry, suppressEncryption);
      } else {
        xrefEntry = this.fetchCompressed(xrefEntry, suppressEncryption);
      }
      if ((0, _primitives.isDict)(xrefEntry)) {
        xrefEntry.objId = ref.toString();
      } else if ((0, _primitives.isStream)(xrefEntry)) {
        xrefEntry.dict.objId = ref.toString();
      }
      return xrefEntry;
    },
    fetchUncompressed: function XRef_fetchUncompressed(ref, xrefEntry, suppressEncryption) {
      var gen = ref.gen;
      var num = ref.num;
      if (xrefEntry.gen !== gen) {
        throw new _util.FormatError('inconsistent generation in XRef');
      }
      var stream = this.stream.makeSubStream(xrefEntry.offset + this.stream.start);
      var parser = new _parser.Parser(new _parser.Lexer(stream), true, this);
      var obj1 = parser.getObj();
      var obj2 = parser.getObj();
      var obj3 = parser.getObj();
      if (!Number.isInteger(obj1)) {
        obj1 = parseInt(obj1, 10);
      }
      if (!Number.isInteger(obj2)) {
        obj2 = parseInt(obj2, 10);
      }
      if (obj1 !== num || obj2 !== gen || !(0, _primitives.isCmd)(obj3)) {
        throw new _util.FormatError('bad XRef entry');
      }
      if (obj3.cmd !== 'obj') {
        if (obj3.cmd.indexOf('obj') === 0) {
          num = parseInt(obj3.cmd.substring(3), 10);
          if (!Number.isNaN(num)) {
            return num;
          }
        }
        throw new _util.FormatError('bad XRef entry');
      }
      if (this.encrypt && !suppressEncryption) {
        xrefEntry = parser.getObj(this.encrypt.createCipherTransform(num, gen));
      } else {
        xrefEntry = parser.getObj();
      }
      if (!(0, _primitives.isStream)(xrefEntry)) {
        this.cache[num] = xrefEntry;
      }
      return xrefEntry;
    },
    fetchCompressed: function XRef_fetchCompressed(xrefEntry, suppressEncryption) {
      var tableOffset = xrefEntry.offset;
      var stream = this.fetch(new _primitives.Ref(tableOffset, 0));
      if (!(0, _primitives.isStream)(stream)) {
        throw new _util.FormatError('bad ObjStm stream');
      }
      var first = stream.dict.get('First');
      var n = stream.dict.get('N');
      if (!Number.isInteger(first) || !Number.isInteger(n)) {
        throw new _util.FormatError('invalid first and n parameters for ObjStm stream');
      }
      var parser = new _parser.Parser(new _parser.Lexer(stream), false, this);
      parser.allowStreams = true;
      var i,
          entries = [],
          num,
          nums = [];
      for (i = 0; i < n; ++i) {
        num = parser.getObj();
        if (!Number.isInteger(num)) {
          throw new _util.FormatError(`invalid object number in the ObjStm stream: ${num}`);
        }
        nums.push(num);
        var offset = parser.getObj();
        if (!Number.isInteger(offset)) {
          throw new _util.FormatError(`invalid object offset in the ObjStm stream: ${offset}`);
        }
      }
      for (i = 0; i < n; ++i) {
        entries.push(parser.getObj());
        if ((0, _primitives.isCmd)(parser.buf1, 'endobj')) {
          parser.shift();
        }
        num = nums[i];
        var entry = this.entries[num];
        if (entry && entry.offset === tableOffset && entry.gen === i) {
          this.cache[num] = entries[i];
        }
      }
      xrefEntry = entries[xrefEntry.gen];
      if (xrefEntry === undefined) {
        throw new _util.FormatError('bad XRef entry for compressed object');
      }
      return xrefEntry;
    },
    fetchIfRefAsync: function XRef_fetchIfRefAsync(obj, suppressEncryption) {
      if (!(0, _primitives.isRef)(obj)) {
        return Promise.resolve(obj);
      }
      return this.fetchAsync(obj, suppressEncryption);
    },
    fetchAsync: function XRef_fetchAsync(ref, suppressEncryption) {
      var streamManager = this.stream.manager;
      var xref = this;
      return new Promise(function tryFetch(resolve, reject) {
        try {
          resolve(xref.fetch(ref, suppressEncryption));
        } catch (e) {
          if (e instanceof _util.MissingDataException) {
            streamManager.requestRange(e.begin, e.end).then(function () {
              tryFetch(resolve, reject);
            }, reject);
            return;
          }
          reject(e);
        }
      });
    },
    getCatalogObj: function XRef_getCatalogObj() {
      return this.root;
    }
  };
  return XRef;
}();
var NameOrNumberTree = function NameOrNumberTreeClosure() {
  function NameOrNumberTree(root, xref) {
    (0, _util.unreachable)('Cannot initialize NameOrNumberTree.');
  }
  NameOrNumberTree.prototype = {
    getAll: function NameOrNumberTree_getAll() {
      var dict = Object.create(null);
      if (!this.root) {
        return dict;
      }
      var xref = this.xref;
      var processed = new _primitives.RefSet();
      processed.put(this.root);
      var queue = [this.root];
      while (queue.length > 0) {
        var i, n;
        var obj = xref.fetchIfRef(queue.shift());
        if (!(0, _primitives.isDict)(obj)) {
          continue;
        }
        if (obj.has('Kids')) {
          var kids = obj.get('Kids');
          for (i = 0, n = kids.length; i < n; i++) {
            var kid = kids[i];
            if (processed.has(kid)) {
              throw new _util.FormatError(`Duplicate entry in "${this._type}" tree.`);
            }
            queue.push(kid);
            processed.put(kid);
          }
          continue;
        }
        var entries = obj.get(this._type);
        if (Array.isArray(entries)) {
          for (i = 0, n = entries.length; i < n; i += 2) {
            dict[xref.fetchIfRef(entries[i])] = xref.fetchIfRef(entries[i + 1]);
          }
        }
      }
      return dict;
    },
    get: function NameOrNumberTree_get(key) {
      if (!this.root) {
        return null;
      }
      var xref = this.xref;
      var kidsOrEntries = xref.fetchIfRef(this.root);
      var loopCount = 0;
      var MAX_LEVELS = 10;
      var l, r, m;
      while (kidsOrEntries.has('Kids')) {
        if (++loopCount > MAX_LEVELS) {
          (0, _util.warn)('Search depth limit reached for "' + this._type + '" tree.');
          return null;
        }
        var kids = kidsOrEntries.get('Kids');
        if (!Array.isArray(kids)) {
          return null;
        }
        l = 0;
        r = kids.length - 1;
        while (l <= r) {
          m = l + r >> 1;
          var kid = xref.fetchIfRef(kids[m]);
          var limits = kid.get('Limits');
          if (key < xref.fetchIfRef(limits[0])) {
            r = m - 1;
          } else if (key > xref.fetchIfRef(limits[1])) {
            l = m + 1;
          } else {
            kidsOrEntries = xref.fetchIfRef(kids[m]);
            break;
          }
        }
        if (l > r) {
          return null;
        }
      }
      var entries = kidsOrEntries.get(this._type);
      if (Array.isArray(entries)) {
        l = 0;
        r = entries.length - 2;
        while (l <= r) {
          m = l + r & ~1;
          var currentKey = xref.fetchIfRef(entries[m]);
          if (key < currentKey) {
            r = m - 2;
          } else if (key > currentKey) {
            l = m + 2;
          } else {
            return xref.fetchIfRef(entries[m + 1]);
          }
        }
      }
      return null;
    }
  };
  return NameOrNumberTree;
}();
var NameTree = function NameTreeClosure() {
  function NameTree(root, xref) {
    this.root = root;
    this.xref = xref;
    this._type = 'Names';
  }
  _util.Util.inherit(NameTree, NameOrNumberTree, {});
  return NameTree;
}();
var NumberTree = function NumberTreeClosure() {
  function NumberTree(root, xref) {
    this.root = root;
    this.xref = xref;
    this._type = 'Nums';
  }
  _util.Util.inherit(NumberTree, NameOrNumberTree, {});
  return NumberTree;
}();
var FileSpec = function FileSpecClosure() {
  function FileSpec(root, xref) {
    if (!root || !(0, _primitives.isDict)(root)) {
      return;
    }
    this.xref = xref;
    this.root = root;
    if (root.has('FS')) {
      this.fs = root.get('FS');
    }
    this.description = root.has('Desc') ? (0, _util.stringToPDFString)(root.get('Desc')) : '';
    if (root.has('RF')) {
      (0, _util.warn)('Related file specifications are not supported');
    }
    this.contentAvailable = true;
    if (!root.has('EF')) {
      this.contentAvailable = false;
      (0, _util.warn)('Non-embedded file specifications are not supported');
    }
  }
  function pickPlatformItem(dict) {
    if (dict.has('UF')) {
      return dict.get('UF');
    } else if (dict.has('F')) {
      return dict.get('F');
    } else if (dict.has('Unix')) {
      return dict.get('Unix');
    } else if (dict.has('Mac')) {
      return dict.get('Mac');
    } else if (dict.has('DOS')) {
      return dict.get('DOS');
    }
    return null;
  }
  FileSpec.prototype = {
    get filename() {
      if (!this._filename && this.root) {
        var filename = pickPlatformItem(this.root) || 'unnamed';
        this._filename = (0, _util.stringToPDFString)(filename).replace(/\\\\/g, '\\').replace(/\\\//g, '/').replace(/\\/g, '/');
      }
      return this._filename;
    },
    get content() {
      if (!this.contentAvailable) {
        return null;
      }
      if (!this.contentRef && this.root) {
        this.contentRef = pickPlatformItem(this.root.get('EF'));
      }
      var content = null;
      if (this.contentRef) {
        var xref = this.xref;
        var fileObj = xref.fetchIfRef(this.contentRef);
        if (fileObj && (0, _primitives.isStream)(fileObj)) {
          content = fileObj.getBytes();
        } else {
          (0, _util.warn)('Embedded file specification points to non-existing/invalid ' + 'content');
        }
      } else {
        (0, _util.warn)('Embedded file specification does not have a content');
      }
      return content;
    },
    get serializable() {
      return {
        filename: this.filename,
        content: this.content
      };
    }
  };
  return FileSpec;
}();
let ObjectLoader = function () {
  function mayHaveChildren(value) {
    return (0, _primitives.isRef)(value) || (0, _primitives.isDict)(value) || Array.isArray(value) || (0, _primitives.isStream)(value);
  }
  function addChildren(node, nodesToVisit) {
    if ((0, _primitives.isDict)(node) || (0, _primitives.isStream)(node)) {
      let dict = (0, _primitives.isDict)(node) ? node : node.dict;
      let dictKeys = dict.getKeys();
      for (let i = 0, ii = dictKeys.length; i < ii; i++) {
        let rawValue = dict.getRaw(dictKeys[i]);
        if (mayHaveChildren(rawValue)) {
          nodesToVisit.push(rawValue);
        }
      }
    } else if (Array.isArray(node)) {
      for (let i = 0, ii = node.length; i < ii; i++) {
        let value = node[i];
        if (mayHaveChildren(value)) {
          nodesToVisit.push(value);
        }
      }
    }
  }
  function ObjectLoader(dict, keys, xref) {
    this.dict = dict;
    this.keys = keys;
    this.xref = xref;
    this.refSet = null;
    this.capability = null;
  }
  ObjectLoader.prototype = {
    load() {
      this.capability = (0, _util.createPromiseCapability)();
      if (!(this.xref.stream instanceof _chunked_stream.ChunkedStream) || this.xref.stream.getMissingChunks().length === 0) {
        this.capability.resolve();
        return this.capability.promise;
      }
      let { keys, dict } = this;
      this.refSet = new _primitives.RefSet();
      let nodesToVisit = [];
      for (let i = 0, ii = keys.length; i < ii; i++) {
        let rawValue = dict.getRaw(keys[i]);
        if (rawValue !== undefined) {
          nodesToVisit.push(rawValue);
        }
      }
      this._walk(nodesToVisit);
      return this.capability.promise;
    },
    _walk(nodesToVisit) {
      let nodesToRevisit = [];
      let pendingRequests = [];
      while (nodesToVisit.length) {
        let currentNode = nodesToVisit.pop();
        if ((0, _primitives.isRef)(currentNode)) {
          if (this.refSet.has(currentNode)) {
            continue;
          }
          try {
            this.refSet.put(currentNode);
            currentNode = this.xref.fetch(currentNode);
          } catch (ex) {
            if (!(ex instanceof _util.MissingDataException)) {
              throw ex;
            }
            nodesToRevisit.push(currentNode);
            pendingRequests.push({
              begin: ex.begin,
              end: ex.end
            });
          }
        }
        if (currentNode && currentNode.getBaseStreams) {
          let baseStreams = currentNode.getBaseStreams();
          let foundMissingData = false;
          for (let i = 0, ii = baseStreams.length; i < ii; i++) {
            let stream = baseStreams[i];
            if (stream.getMissingChunks && stream.getMissingChunks().length) {
              foundMissingData = true;
              pendingRequests.push({
                begin: stream.start,
                end: stream.end
              });
            }
          }
          if (foundMissingData) {
            nodesToRevisit.push(currentNode);
          }
        }
        addChildren(currentNode, nodesToVisit);
      }
      if (pendingRequests.length) {
        this.xref.stream.manager.requestRanges(pendingRequests).then(() => {
          for (let i = 0, ii = nodesToRevisit.length; i < ii; i++) {
            let node = nodesToRevisit[i];
            if ((0, _primitives.isRef)(node)) {
              this.refSet.remove(node);
            }
          }
          this._walk(nodesToRevisit);
        }, this.capability.reject);
        return;
      }
      this.refSet = null;
      this.capability.resolve();
    }
  };
  return ObjectLoader;
}();
exports.Catalog = Catalog;
exports.ObjectLoader = ObjectLoader;
exports.XRef = XRef;
exports.FileSpec = FileSpec;

/***/ }),
/* 11 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.CCITTFaxDecoder = undefined;

var _util = __w_pdfjs_require__(0);

let CCITTFaxDecoder = function CCITTFaxDecoder() {
  const ccittEOL = -2;
  const ccittEOF = -1;
  const twoDimPass = 0;
  const twoDimHoriz = 1;
  const twoDimVert0 = 2;
  const twoDimVertR1 = 3;
  const twoDimVertL1 = 4;
  const twoDimVertR2 = 5;
  const twoDimVertL2 = 6;
  const twoDimVertR3 = 7;
  const twoDimVertL3 = 8;
  const twoDimTable = [[-1, -1], [-1, -1], [7, twoDimVertL3], [7, twoDimVertR3], [6, twoDimVertL2], [6, twoDimVertL2], [6, twoDimVertR2], [6, twoDimVertR2], [4, twoDimPass], [4, twoDimPass], [4, twoDimPass], [4, twoDimPass], [4, twoDimPass], [4, twoDimPass], [4, twoDimPass], [4, twoDimPass], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0]];
  const whiteTable1 = [[-1, -1], [12, ccittEOL], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [11, 1792], [11, 1792], [12, 1984], [12, 2048], [12, 2112], [12, 2176], [12, 2240], [12, 2304], [11, 1856], [11, 1856], [11, 1920], [11, 1920], [12, 2368], [12, 2432], [12, 2496], [12, 2560]];
  const whiteTable2 = [[-1, -1], [-1, -1], [-1, -1], [-1, -1], [8, 29], [8, 29], [8, 30], [8, 30], [8, 45], [8, 45], [8, 46], [8, 46], [7, 22], [7, 22], [7, 22], [7, 22], [7, 23], [7, 23], [7, 23], [7, 23], [8, 47], [8, 47], [8, 48], [8, 48], [6, 13], [6, 13], [6, 13], [6, 13], [6, 13], [6, 13], [6, 13], [6, 13], [7, 20], [7, 20], [7, 20], [7, 20], [8, 33], [8, 33], [8, 34], [8, 34], [8, 35], [8, 35], [8, 36], [8, 36], [8, 37], [8, 37], [8, 38], [8, 38], [7, 19], [7, 19], [7, 19], [7, 19], [8, 31], [8, 31], [8, 32], [8, 32], [6, 1], [6, 1], [6, 1], [6, 1], [6, 1], [6, 1], [6, 1], [6, 1], [6, 12], [6, 12], [6, 12], [6, 12], [6, 12], [6, 12], [6, 12], [6, 12], [8, 53], [8, 53], [8, 54], [8, 54], [7, 26], [7, 26], [7, 26], [7, 26], [8, 39], [8, 39], [8, 40], [8, 40], [8, 41], [8, 41], [8, 42], [8, 42], [8, 43], [8, 43], [8, 44], [8, 44], [7, 21], [7, 21], [7, 21], [7, 21], [7, 28], [7, 28], [7, 28], [7, 28], [8, 61], [8, 61], [8, 62], [8, 62], [8, 63], [8, 63], [8, 0], [8, 0], [8, 320], [8, 320], [8, 384], [8, 384], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [7, 27], [7, 27], [7, 27], [7, 27], [8, 59], [8, 59], [8, 60], [8, 60], [9, 1472], [9, 1536], [9, 1600], [9, 1728], [7, 18], [7, 18], [7, 18], [7, 18], [7, 24], [7, 24], [7, 24], [7, 24], [8, 49], [8, 49], [8, 50], [8, 50], [8, 51], [8, 51], [8, 52], [8, 52], [7, 25], [7, 25], [7, 25], [7, 25], [8, 55], [8, 55], [8, 56], [8, 56], [8, 57], [8, 57], [8, 58], [8, 58], [6, 192], [6, 192], [6, 192], [6, 192], [6, 192], [6, 192], [6, 192], [6, 192], [6, 1664], [6, 1664], [6, 1664], [6, 1664], [6, 1664], [6, 1664], [6, 1664], [6, 1664], [8, 448], [8, 448], [8, 512], [8, 512], [9, 704], [9, 768], [8, 640], [8, 640], [8, 576], [8, 576], [9, 832], [9, 896], [9, 960], [9, 1024], [9, 1088], [9, 1152], [9, 1216], [9, 1280], [9, 1344], [9, 1408], [7, 256], [7, 256], [7, 256], [7, 256], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [6, 16], [6, 16], [6, 16], [6, 16], [6, 16], [6, 16], [6, 16], [6, 16], [6, 17], [6, 17], [6, 17], [6, 17], [6, 17], [6, 17], [6, 17], [6, 17], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [6, 14], [6, 14], [6, 14], [6, 14], [6, 14], [6, 14], [6, 14], [6, 14], [6, 15], [6, 15], [6, 15], [6, 15], [6, 15], [6, 15], [6, 15], [6, 15], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7]];
  const blackTable1 = [[-1, -1], [-1, -1], [12, ccittEOL], [12, ccittEOL], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [11, 1792], [11, 1792], [11, 1792], [11, 1792], [12, 1984], [12, 1984], [12, 2048], [12, 2048], [12, 2112], [12, 2112], [12, 2176], [12, 2176], [12, 2240], [12, 2240], [12, 2304], [12, 2304], [11, 1856], [11, 1856], [11, 1856], [11, 1856], [11, 1920], [11, 1920], [11, 1920], [11, 1920], [12, 2368], [12, 2368], [12, 2432], [12, 2432], [12, 2496], [12, 2496], [12, 2560], [12, 2560], [10, 18], [10, 18], [10, 18], [10, 18], [10, 18], [10, 18], [10, 18], [10, 18], [12, 52], [12, 52], [13, 640], [13, 704], [13, 768], [13, 832], [12, 55], [12, 55], [12, 56], [12, 56], [13, 1280], [13, 1344], [13, 1408], [13, 1472], [12, 59], [12, 59], [12, 60], [12, 60], [13, 1536], [13, 1600], [11, 24], [11, 24], [11, 24], [11, 24], [11, 25], [11, 25], [11, 25], [11, 25], [13, 1664], [13, 1728], [12, 320], [12, 320], [12, 384], [12, 384], [12, 448], [12, 448], [13, 512], [13, 576], [12, 53], [12, 53], [12, 54], [12, 54], [13, 896], [13, 960], [13, 1024], [13, 1088], [13, 1152], [13, 1216], [10, 64], [10, 64], [10, 64], [10, 64], [10, 64], [10, 64], [10, 64], [10, 64]];
  const blackTable2 = [[8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [11, 23], [11, 23], [12, 50], [12, 51], [12, 44], [12, 45], [12, 46], [12, 47], [12, 57], [12, 58], [12, 61], [12, 256], [10, 16], [10, 16], [10, 16], [10, 16], [10, 17], [10, 17], [10, 17], [10, 17], [12, 48], [12, 49], [12, 62], [12, 63], [12, 30], [12, 31], [12, 32], [12, 33], [12, 40], [12, 41], [11, 22], [11, 22], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [9, 15], [9, 15], [9, 15], [9, 15], [9, 15], [9, 15], [9, 15], [9, 15], [12, 128], [12, 192], [12, 26], [12, 27], [12, 28], [12, 29], [11, 19], [11, 19], [11, 20], [11, 20], [12, 34], [12, 35], [12, 36], [12, 37], [12, 38], [12, 39], [11, 21], [11, 21], [12, 42], [12, 43], [10, 0], [10, 0], [10, 0], [10, 0], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12]];
  const blackTable3 = [[-1, -1], [-1, -1], [-1, -1], [-1, -1], [6, 9], [6, 8], [5, 7], [5, 7], [4, 6], [4, 6], [4, 6], [4, 6], [4, 5], [4, 5], [4, 5], [4, 5], [3, 1], [3, 1], [3, 1], [3, 1], [3, 1], [3, 1], [3, 1], [3, 1], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2]];
  function CCITTFaxDecoder(source, options = {}) {
    if (!source || typeof source.next !== 'function') {
      throw new Error('CCITTFaxDecoder - invalid "source" parameter.');
    }
    this.source = source;
    this.eof = false;
    this.encoding = options['K'] || 0;
    this.eoline = options['EndOfLine'] || false;
    this.byteAlign = options['EncodedByteAlign'] || false;
    this.columns = options['Columns'] || 1728;
    this.rows = options['Rows'] || 0;
    let eoblock = options['EndOfBlock'];
    if (eoblock === null || eoblock === undefined) {
      eoblock = true;
    }
    this.eoblock = eoblock;
    this.black = options['BlackIs1'] || false;
    this.codingLine = new Uint32Array(this.columns + 1);
    this.refLine = new Uint32Array(this.columns + 2);
    this.codingLine[0] = this.columns;
    this.codingPos = 0;
    this.row = 0;
    this.nextLine2D = this.encoding < 0;
    this.inputBits = 0;
    this.inputBuf = 0;
    this.outputBits = 0;
    this.rowsDone = false;
    let code1;
    while ((code1 = this._lookBits(12)) === 0) {
      this._eatBits(1);
    }
    if (code1 === 1) {
      this._eatBits(12);
    }
    if (this.encoding > 0) {
      this.nextLine2D = !this._lookBits(1);
      this._eatBits(1);
    }
  }
  CCITTFaxDecoder.prototype = {
    readNextChar() {
      if (this.eof) {
        return -1;
      }
      let refLine = this.refLine;
      let codingLine = this.codingLine;
      let columns = this.columns;
      let refPos, blackPixels, bits, i;
      if (this.outputBits === 0) {
        if (this.rowsDone) {
          this.eof = true;
        }
        if (this.eof) {
          return -1;
        }
        this.err = false;
        let code1, code2, code3;
        if (this.nextLine2D) {
          for (i = 0; codingLine[i] < columns; ++i) {
            refLine[i] = codingLine[i];
          }
          refLine[i++] = columns;
          refLine[i] = columns;
          codingLine[0] = 0;
          this.codingPos = 0;
          refPos = 0;
          blackPixels = 0;
          while (codingLine[this.codingPos] < columns) {
            code1 = this._getTwoDimCode();
            switch (code1) {
              case twoDimPass:
                this._addPixels(refLine[refPos + 1], blackPixels);
                if (refLine[refPos + 1] < columns) {
                  refPos += 2;
                }
                break;
              case twoDimHoriz:
                code1 = code2 = 0;
                if (blackPixels) {
                  do {
                    code1 += code3 = this._getBlackCode();
                  } while (code3 >= 64);
                  do {
                    code2 += code3 = this._getWhiteCode();
                  } while (code3 >= 64);
                } else {
                  do {
                    code1 += code3 = this._getWhiteCode();
                  } while (code3 >= 64);
                  do {
                    code2 += code3 = this._getBlackCode();
                  } while (code3 >= 64);
                }
                this._addPixels(codingLine[this.codingPos] + code1, blackPixels);
                if (codingLine[this.codingPos] < columns) {
                  this._addPixels(codingLine[this.codingPos] + code2, blackPixels ^ 1);
                }
                while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) {
                  refPos += 2;
                }
                break;
              case twoDimVertR3:
                this._addPixels(refLine[refPos] + 3, blackPixels);
                blackPixels ^= 1;
                if (codingLine[this.codingPos] < columns) {
                  ++refPos;
                  while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) {
                    refPos += 2;
                  }
                }
                break;
              case twoDimVertR2:
                this._addPixels(refLine[refPos] + 2, blackPixels);
                blackPixels ^= 1;
                if (codingLine[this.codingPos] < columns) {
                  ++refPos;
                  while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) {
                    refPos += 2;
                  }
                }
                break;
              case twoDimVertR1:
                this._addPixels(refLine[refPos] + 1, blackPixels);
                blackPixels ^= 1;
                if (codingLine[this.codingPos] < columns) {
                  ++refPos;
                  while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) {
                    refPos += 2;
                  }
                }
                break;
              case twoDimVert0:
                this._addPixels(refLine[refPos], blackPixels);
                blackPixels ^= 1;
                if (codingLine[this.codingPos] < columns) {
                  ++refPos;
                  while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) {
                    refPos += 2;
                  }
                }
                break;
              case twoDimVertL3:
                this._addPixelsNeg(refLine[refPos] - 3, blackPixels);
                blackPixels ^= 1;
                if (codingLine[this.codingPos] < columns) {
                  if (refPos > 0) {
                    --refPos;
                  } else {
                    ++refPos;
                  }
                  while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) {
                    refPos += 2;
                  }
                }
                break;
              case twoDimVertL2:
                this._addPixelsNeg(refLine[refPos] - 2, blackPixels);
                blackPixels ^= 1;
                if (codingLine[this.codingPos] < columns) {
                  if (refPos > 0) {
                    --refPos;
                  } else {
                    ++refPos;
                  }
                  while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) {
                    refPos += 2;
                  }
                }
                break;
              case twoDimVertL1:
                this._addPixelsNeg(refLine[refPos] - 1, blackPixels);
                blackPixels ^= 1;
                if (codingLine[this.codingPos] < columns) {
                  if (refPos > 0) {
                    --refPos;
                  } else {
                    ++refPos;
                  }
                  while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) {
                    refPos += 2;
                  }
                }
                break;
              case ccittEOF:
                this._addPixels(columns, 0);
                this.eof = true;
                break;
              default:
                (0, _util.info)('bad 2d code');
                this._addPixels(columns, 0);
                this.err = true;
            }
          }
        } else {
          codingLine[0] = 0;
          this.codingPos = 0;
          blackPixels = 0;
          while (codingLine[this.codingPos] < columns) {
            code1 = 0;
            if (blackPixels) {
              do {
                code1 += code3 = this._getBlackCode();
              } while (code3 >= 64);
            } else {
              do {
                code1 += code3 = this._getWhiteCode();
              } while (code3 >= 64);
            }
            this._addPixels(codingLine[this.codingPos] + code1, blackPixels);
            blackPixels ^= 1;
          }
        }
        let gotEOL = false;
        if (this.byteAlign) {
          this.inputBits &= ~7;
        }
        if (!this.eoblock && this.row === this.rows - 1) {
          this.rowsDone = true;
        } else {
          code1 = this._lookBits(12);
          if (this.eoline) {
            while (code1 !== ccittEOF && code1 !== 1) {
              this._eatBits(1);
              code1 = this._lookBits(12);
            }
          } else {
            while (code1 === 0) {
              this._eatBits(1);
              code1 = this._lookBits(12);
            }
          }
          if (code1 === 1) {
            this._eatBits(12);
            gotEOL = true;
          } else if (code1 === ccittEOF) {
            this.eof = true;
          }
        }
        if (!this.eof && this.encoding > 0 && !this.rowsDone) {
          this.nextLine2D = !this._lookBits(1);
          this._eatBits(1);
        }
        if (this.eoblock && gotEOL && this.byteAlign) {
          code1 = this._lookBits(12);
          if (code1 === 1) {
            this._eatBits(12);
            if (this.encoding > 0) {
              this._lookBits(1);
              this._eatBits(1);
            }
            if (this.encoding >= 0) {
              for (i = 0; i < 4; ++i) {
                code1 = this._lookBits(12);
                if (code1 !== 1) {
                  (0, _util.info)('bad rtc code: ' + code1);
                }
                this._eatBits(12);
                if (this.encoding > 0) {
                  this._lookBits(1);
                  this._eatBits(1);
                }
              }
            }
            this.eof = true;
          }
        } else if (this.err && this.eoline) {
          while (true) {
            code1 = this._lookBits(13);
            if (code1 === ccittEOF) {
              this.eof = true;
              return -1;
            }
            if (code1 >> 1 === 1) {
              break;
            }
            this._eatBits(1);
          }
          this._eatBits(12);
          if (this.encoding > 0) {
            this._eatBits(1);
            this.nextLine2D = !(code1 & 1);
          }
        }
        if (codingLine[0] > 0) {
          this.outputBits = codingLine[this.codingPos = 0];
        } else {
          this.outputBits = codingLine[this.codingPos = 1];
        }
        this.row++;
      }
      let c;
      if (this.outputBits >= 8) {
        c = this.codingPos & 1 ? 0 : 0xFF;
        this.outputBits -= 8;
        if (this.outputBits === 0 && codingLine[this.codingPos] < columns) {
          this.codingPos++;
          this.outputBits = codingLine[this.codingPos] - codingLine[this.codingPos - 1];
        }
      } else {
        bits = 8;
        c = 0;
        do {
          if (this.outputBits > bits) {
            c <<= bits;
            if (!(this.codingPos & 1)) {
              c |= 0xFF >> 8 - bits;
            }
            this.outputBits -= bits;
            bits = 0;
          } else {
            c <<= this.outputBits;
            if (!(this.codingPos & 1)) {
              c |= 0xFF >> 8 - this.outputBits;
            }
            bits -= this.outputBits;
            this.outputBits = 0;
            if (codingLine[this.codingPos] < columns) {
              this.codingPos++;
              this.outputBits = codingLine[this.codingPos] - codingLine[this.codingPos - 1];
            } else if (bits > 0) {
              c <<= bits;
              bits = 0;
            }
          }
        } while (bits);
      }
      if (this.black) {
        c ^= 0xFF;
      }
      return c;
    },
    _addPixels(a1, blackPixels) {
      let codingLine = this.codingLine;
      let codingPos = this.codingPos;
      if (a1 > codingLine[codingPos]) {
        if (a1 > this.columns) {
          (0, _util.info)('row is wrong length');
          this.err = true;
          a1 = this.columns;
        }
        if (codingPos & 1 ^ blackPixels) {
          ++codingPos;
        }
        codingLine[codingPos] = a1;
      }
      this.codingPos = codingPos;
    },
    _addPixelsNeg(a1, blackPixels) {
      let codingLine = this.codingLine;
      let codingPos = this.codingPos;
      if (a1 > codingLine[codingPos]) {
        if (a1 > this.columns) {
          (0, _util.info)('row is wrong length');
          this.err = true;
          a1 = this.columns;
        }
        if (codingPos & 1 ^ blackPixels) {
          ++codingPos;
        }
        codingLine[codingPos] = a1;
      } else if (a1 < codingLine[codingPos]) {
        if (a1 < 0) {
          (0, _util.info)('invalid code');
          this.err = true;
          a1 = 0;
        }
        while (codingPos > 0 && a1 < codingLine[codingPos - 1]) {
          --codingPos;
        }
        codingLine[codingPos] = a1;
      }
      this.codingPos = codingPos;
    },
    _findTableCode(start, end, table, limit) {
      let limitValue = limit || 0;
      for (let i = start; i <= end; ++i) {
        let code = this._lookBits(i);
        if (code === ccittEOF) {
          return [true, 1, false];
        }
        if (i < end) {
          code <<= end - i;
        }
        if (!limitValue || code >= limitValue) {
          let p = table[code - limitValue];
          if (p[0] === i) {
            this._eatBits(i);
            return [true, p[1], true];
          }
        }
      }
      return [false, 0, false];
    },
    _getTwoDimCode() {
      let code = 0;
      let p;
      if (this.eoblock) {
        code = this._lookBits(7);
        p = twoDimTable[code];
        if (p && p[0] > 0) {
          this._eatBits(p[0]);
          return p[1];
        }
      } else {
        let result = this._findTableCode(1, 7, twoDimTable);
        if (result[0] && result[2]) {
          return result[1];
        }
      }
      (0, _util.info)('Bad two dim code');
      return ccittEOF;
    },
    _getWhiteCode() {
      let code = 0;
      let p;
      if (this.eoblock) {
        code = this._lookBits(12);
        if (code === ccittEOF) {
          return 1;
        }
        if (code >> 5 === 0) {
          p = whiteTable1[code];
        } else {
          p = whiteTable2[code >> 3];
        }
        if (p[0] > 0) {
          this._eatBits(p[0]);
          return p[1];
        }
      } else {
        let result = this._findTableCode(1, 9, whiteTable2);
        if (result[0]) {
          return result[1];
        }
        result = this._findTableCode(11, 12, whiteTable1);
        if (result[0]) {
          return result[1];
        }
      }
      (0, _util.info)('bad white code');
      this._eatBits(1);
      return 1;
    },
    _getBlackCode() {
      let code, p;
      if (this.eoblock) {
        code = this._lookBits(13);
        if (code === ccittEOF) {
          return 1;
        }
        if (code >> 7 === 0) {
          p = blackTable1[code];
        } else if (code >> 9 === 0 && code >> 7 !== 0) {
          p = blackTable2[(code >> 1) - 64];
        } else {
          p = blackTable3[code >> 7];
        }
        if (p[0] > 0) {
          this._eatBits(p[0]);
          return p[1];
        }
      } else {
        let result = this._findTableCode(2, 6, blackTable3);
        if (result[0]) {
          return result[1];
        }
        result = this._findTableCode(7, 12, blackTable2, 64);
        if (result[0]) {
          return result[1];
        }
        result = this._findTableCode(10, 13, blackTable1);
        if (result[0]) {
          return result[1];
        }
      }
      (0, _util.info)('bad black code');
      this._eatBits(1);
      return 1;
    },
    _lookBits(n) {
      let c;
      while (this.inputBits < n) {
        if ((c = this.source.next()) === -1) {
          if (this.inputBits === 0) {
            return ccittEOF;
          }
          return this.inputBuf << n - this.inputBits & 0xFFFF >> 16 - n;
        }
        this.inputBuf = this.inputBuf << 8 | c;
        this.inputBits += 8;
      }
      return this.inputBuf >> this.inputBits - n & 0xFFFF >> 16 - n;
    },
    _eatBits(n) {
      if ((this.inputBits -= n) < 0) {
        this.inputBits = 0;
      }
    }
  };
  return CCITTFaxDecoder;
}();
exports.CCITTFaxDecoder = CCITTFaxDecoder;

/***/ }),
/* 12 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
var ArithmeticDecoder = function ArithmeticDecoderClosure() {
  var QeTable = [{
    qe: 0x5601,
    nmps: 1,
    nlps: 1,
    switchFlag: 1
  }, {
    qe: 0x3401,
    nmps: 2,
    nlps: 6,
    switchFlag: 0
  }, {
    qe: 0x1801,
    nmps: 3,
    nlps: 9,
    switchFlag: 0
  }, {
    qe: 0x0AC1,
    nmps: 4,
    nlps: 12,
    switchFlag: 0
  }, {
    qe: 0x0521,
    nmps: 5,
    nlps: 29,
    switchFlag: 0
  }, {
    qe: 0x0221,
    nmps: 38,
    nlps: 33,
    switchFlag: 0
  }, {
    qe: 0x5601,
    nmps: 7,
    nlps: 6,
    switchFlag: 1
  }, {
    qe: 0x5401,
    nmps: 8,
    nlps: 14,
    switchFlag: 0
  }, {
    qe: 0x4801,
    nmps: 9,
    nlps: 14,
    switchFlag: 0
  }, {
    qe: 0x3801,
    nmps: 10,
    nlps: 14,
    switchFlag: 0
  }, {
    qe: 0x3001,
    nmps: 11,
    nlps: 17,
    switchFlag: 0
  }, {
    qe: 0x2401,
    nmps: 12,
    nlps: 18,
    switchFlag: 0
  }, {
    qe: 0x1C01,
    nmps: 13,
    nlps: 20,
    switchFlag: 0
  }, {
    qe: 0x1601,
    nmps: 29,
    nlps: 21,
    switchFlag: 0
  }, {
    qe: 0x5601,
    nmps: 15,
    nlps: 14,
    switchFlag: 1
  }, {
    qe: 0x5401,
    nmps: 16,
    nlps: 14,
    switchFlag: 0
  }, {
    qe: 0x5101,
    nmps: 17,
    nlps: 15,
    switchFlag: 0
  }, {
    qe: 0x4801,
    nmps: 18,
    nlps: 16,
    switchFlag: 0
  }, {
    qe: 0x3801,
    nmps: 19,
    nlps: 17,
    switchFlag: 0
  }, {
    qe: 0x3401,
    nmps: 20,
    nlps: 18,
    switchFlag: 0
  }, {
    qe: 0x3001,
    nmps: 21,
    nlps: 19,
    switchFlag: 0
  }, {
    qe: 0x2801,
    nmps: 22,
    nlps: 19,
    switchFlag: 0
  }, {
    qe: 0x2401,
    nmps: 23,
    nlps: 20,
    switchFlag: 0
  }, {
    qe: 0x2201,
    nmps: 24,
    nlps: 21,
    switchFlag: 0
  }, {
    qe: 0x1C01,
    nmps: 25,
    nlps: 22,
    switchFlag: 0
  }, {
    qe: 0x1801,
    nmps: 26,
    nlps: 23,
    switchFlag: 0
  }, {
    qe: 0x1601,
    nmps: 27,
    nlps: 24,
    switchFlag: 0
  }, {
    qe: 0x1401,
    nmps: 28,
    nlps: 25,
    switchFlag: 0
  }, {
    qe: 0x1201,
    nmps: 29,
    nlps: 26,
    switchFlag: 0
  }, {
    qe: 0x1101,
    nmps: 30,
    nlps: 27,
    switchFlag: 0
  }, {
    qe: 0x0AC1,
    nmps: 31,
    nlps: 28,
    switchFlag: 0
  }, {
    qe: 0x09C1,
    nmps: 32,
    nlps: 29,
    switchFlag: 0
  }, {
    qe: 0x08A1,
    nmps: 33,
    nlps: 30,
    switchFlag: 0
  }, {
    qe: 0x0521,
    nmps: 34,
    nlps: 31,
    switchFlag: 0
  }, {
    qe: 0x0441,
    nmps: 35,
    nlps: 32,
    switchFlag: 0
  }, {
    qe: 0x02A1,
    nmps: 36,
    nlps: 33,
    switchFlag: 0
  }, {
    qe: 0x0221,
    nmps: 37,
    nlps: 34,
    switchFlag: 0
  }, {
    qe: 0x0141,
    nmps: 38,
    nlps: 35,
    switchFlag: 0
  }, {
    qe: 0x0111,
    nmps: 39,
    nlps: 36,
    switchFlag: 0
  }, {
    qe: 0x0085,
    nmps: 40,
    nlps: 37,
    switchFlag: 0
  }, {
    qe: 0x0049,
    nmps: 41,
    nlps: 38,
    switchFlag: 0
  }, {
    qe: 0x0025,
    nmps: 42,
    nlps: 39,
    switchFlag: 0
  }, {
    qe: 0x0015,
    nmps: 43,
    nlps: 40,
    switchFlag: 0
  }, {
    qe: 0x0009,
    nmps: 44,
    nlps: 41,
    switchFlag: 0
  }, {
    qe: 0x0005,
    nmps: 45,
    nlps: 42,
    switchFlag: 0
  }, {
    qe: 0x0001,
    nmps: 45,
    nlps: 43,
    switchFlag: 0
  }, {
    qe: 0x5601,
    nmps: 46,
    nlps: 46,
    switchFlag: 0
  }];
  function ArithmeticDecoder(data, start, end) {
    this.data = data;
    this.bp = start;
    this.dataEnd = end;
    this.chigh = data[start];
    this.clow = 0;
    this.byteIn();
    this.chigh = this.chigh << 7 & 0xFFFF | this.clow >> 9 & 0x7F;
    this.clow = this.clow << 7 & 0xFFFF;
    this.ct -= 7;
    this.a = 0x8000;
  }
  ArithmeticDecoder.prototype = {
    byteIn: function ArithmeticDecoder_byteIn() {
      var data = this.data;
      var bp = this.bp;
      if (data[bp] === 0xFF) {
        var b1 = data[bp + 1];
        if (b1 > 0x8F) {
          this.clow += 0xFF00;
          this.ct = 8;
        } else {
          bp++;
          this.clow += data[bp] << 9;
          this.ct = 7;
          this.bp = bp;
        }
      } else {
        bp++;
        this.clow += bp < this.dataEnd ? data[bp] << 8 : 0xFF00;
        this.ct = 8;
        this.bp = bp;
      }
      if (this.clow > 0xFFFF) {
        this.chigh += this.clow >> 16;
        this.clow &= 0xFFFF;
      }
    },
    readBit: function ArithmeticDecoder_readBit(contexts, pos) {
      var cx_index = contexts[pos] >> 1,
          cx_mps = contexts[pos] & 1;
      var qeTableIcx = QeTable[cx_index];
      var qeIcx = qeTableIcx.qe;
      var d;
      var a = this.a - qeIcx;
      if (this.chigh < qeIcx) {
        if (a < qeIcx) {
          a = qeIcx;
          d = cx_mps;
          cx_index = qeTableIcx.nmps;
        } else {
          a = qeIcx;
          d = 1 ^ cx_mps;
          if (qeTableIcx.switchFlag === 1) {
            cx_mps = d;
          }
          cx_index = qeTableIcx.nlps;
        }
      } else {
        this.chigh -= qeIcx;
        if ((a & 0x8000) !== 0) {
          this.a = a;
          return cx_mps;
        }
        if (a < qeIcx) {
          d = 1 ^ cx_mps;
          if (qeTableIcx.switchFlag === 1) {
            cx_mps = d;
          }
          cx_index = qeTableIcx.nlps;
        } else {
          d = cx_mps;
          cx_index = qeTableIcx.nmps;
        }
      }
      do {
        if (this.ct === 0) {
          this.byteIn();
        }
        a <<= 1;
        this.chigh = this.chigh << 1 & 0xFFFF | this.clow >> 15 & 1;
        this.clow = this.clow << 1 & 0xFFFF;
        this.ct--;
      } while ((a & 0x8000) === 0);
      this.a = a;
      contexts[pos] = cx_index << 1 | cx_mps;
      return d;
    }
  };
  return ArithmeticDecoder;
}();
exports.ArithmeticDecoder = ArithmeticDecoder;

/***/ }),
/* 13 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.JpxImage = undefined;

var _util = __w_pdfjs_require__(0);

var _arithmetic_decoder = __w_pdfjs_require__(12);

let JpxError = function JpxErrorClosure() {
  function JpxError(msg) {
    this.message = 'JPX error: ' + msg;
  }
  JpxError.prototype = new Error();
  JpxError.prototype.name = 'JpxError';
  JpxError.constructor = JpxError;
  return JpxError;
}();
var JpxImage = function JpxImageClosure() {
  var SubbandsGainLog2 = {
    'LL': 0,
    'LH': 1,
    'HL': 1,
    'HH': 2
  };
  function JpxImage() {
    this.failOnCorruptedImage = false;
  }
  JpxImage.prototype = {
    parse: function JpxImage_parse(data) {
      var head = (0, _util.readUint16)(data, 0);
      if (head === 0xFF4F) {
        this.parseCodestream(data, 0, data.length);
        return;
      }
      var position = 0,
          length = data.length;
      while (position < length) {
        var headerSize = 8;
        var lbox = (0, _util.readUint32)(data, position);
        var tbox = (0, _util.readUint32)(data, position + 4);
        position += headerSize;
        if (lbox === 1) {
          lbox = (0, _util.readUint32)(data, position) * 4294967296 + (0, _util.readUint32)(data, position + 4);
          position += 8;
          headerSize += 8;
        }
        if (lbox === 0) {
          lbox = length - position + headerSize;
        }
        if (lbox < headerSize) {
          throw new JpxError('Invalid box field size');
        }
        var dataLength = lbox - headerSize;
        var jumpDataLength = true;
        switch (tbox) {
          case 0x6A703268:
            jumpDataLength = false;
            break;
          case 0x636F6C72:
            var method = data[position];
            if (method === 1) {
              var colorspace = (0, _util.readUint32)(data, position + 3);
              switch (colorspace) {
                case 16:
                case 17:
                case 18:
                  break;
                default:
                  (0, _util.warn)('Unknown colorspace ' + colorspace);
                  break;
              }
            } else if (method === 2) {
              (0, _util.info)('ICC profile not supported');
            }
            break;
          case 0x6A703263:
            this.parseCodestream(data, position, position + dataLength);
            break;
          case 0x6A502020:
            if ((0, _util.readUint32)(data, position) !== 0x0d0a870a) {
              (0, _util.warn)('Invalid JP2 signature');
            }
            break;
          case 0x6A501A1A:
          case 0x66747970:
          case 0x72726571:
          case 0x72657320:
          case 0x69686472:
            break;
          default:
            var headerType = String.fromCharCode(tbox >> 24 & 0xFF, tbox >> 16 & 0xFF, tbox >> 8 & 0xFF, tbox & 0xFF);
            (0, _util.warn)('Unsupported header type ' + tbox + ' (' + headerType + ')');
            break;
        }
        if (jumpDataLength) {
          position += dataLength;
        }
      }
    },
    parseImageProperties: function JpxImage_parseImageProperties(stream) {
      var newByte = stream.getByte();
      while (newByte >= 0) {
        var oldByte = newByte;
        newByte = stream.getByte();
        var code = oldByte << 8 | newByte;
        if (code === 0xFF51) {
          stream.skip(4);
          var Xsiz = stream.getInt32() >>> 0;
          var Ysiz = stream.getInt32() >>> 0;
          var XOsiz = stream.getInt32() >>> 0;
          var YOsiz = stream.getInt32() >>> 0;
          stream.skip(16);
          var Csiz = stream.getUint16();
          this.width = Xsiz - XOsiz;
          this.height = Ysiz - YOsiz;
          this.componentsCount = Csiz;
          this.bitsPerComponent = 8;
          return;
        }
      }
      throw new JpxError('No size marker found in JPX stream');
    },
    parseCodestream: function JpxImage_parseCodestream(data, start, end) {
      var context = {};
      var doNotRecover = false;
      try {
        var position = start;
        while (position + 1 < end) {
          var code = (0, _util.readUint16)(data, position);
          position += 2;
          var length = 0,
              j,
              sqcd,
              spqcds,
              spqcdSize,
              scalarExpounded,
              tile;
          switch (code) {
            case 0xFF4F:
              context.mainHeader = true;
              break;
            case 0xFFD9:
              break;
            case 0xFF51:
              length = (0, _util.readUint16)(data, position);
              var siz = {};
              siz.Xsiz = (0, _util.readUint32)(data, position + 4);
              siz.Ysiz = (0, _util.readUint32)(data, position + 8);
              siz.XOsiz = (0, _util.readUint32)(data, position + 12);
              siz.YOsiz = (0, _util.readUint32)(data, position + 16);
              siz.XTsiz = (0, _util.readUint32)(data, position + 20);
              siz.YTsiz = (0, _util.readUint32)(data, position + 24);
              siz.XTOsiz = (0, _util.readUint32)(data, position + 28);
              siz.YTOsiz = (0, _util.readUint32)(data, position + 32);
              var componentsCount = (0, _util.readUint16)(data, position + 36);
              siz.Csiz = componentsCount;
              var components = [];
              j = position + 38;
              for (var i = 0; i < componentsCount; i++) {
                var component = {
                  precision: (data[j] & 0x7F) + 1,
                  isSigned: !!(data[j] & 0x80),
                  XRsiz: data[j + 1],
                  YRsiz: data[j + 2]
                };
                j += 3;
                calculateComponentDimensions(component, siz);
                components.push(component);
              }
              context.SIZ = siz;
              context.components = components;
              calculateTileGrids(context, components);
              context.QCC = [];
              context.COC = [];
              break;
            case 0xFF5C:
              length = (0, _util.readUint16)(data, position);
              var qcd = {};
              j = position + 2;
              sqcd = data[j++];
              switch (sqcd & 0x1F) {
                case 0:
                  spqcdSize = 8;
                  scalarExpounded = true;
                  break;
                case 1:
                  spqcdSize = 16;
                  scalarExpounded = false;
                  break;
                case 2:
                  spqcdSize = 16;
                  scalarExpounded = true;
                  break;
                default:
                  throw new Error('Invalid SQcd value ' + sqcd);
              }
              qcd.noQuantization = spqcdSize === 8;
              qcd.scalarExpounded = scalarExpounded;
              qcd.guardBits = sqcd >> 5;
              spqcds = [];
              while (j < length + position) {
                var spqcd = {};
                if (spqcdSize === 8) {
                  spqcd.epsilon = data[j++] >> 3;
                  spqcd.mu = 0;
                } else {
                  spqcd.epsilon = data[j] >> 3;
                  spqcd.mu = (data[j] & 0x7) << 8 | data[j + 1];
                  j += 2;
                }
                spqcds.push(spqcd);
              }
              qcd.SPqcds = spqcds;
              if (context.mainHeader) {
                context.QCD = qcd;
              } else {
                context.currentTile.QCD = qcd;
                context.currentTile.QCC = [];
              }
              break;
            case 0xFF5D:
              length = (0, _util.readUint16)(data, position);
              var qcc = {};
              j = position + 2;
              var cqcc;
              if (context.SIZ.Csiz < 257) {
                cqcc = data[j++];
              } else {
                cqcc = (0, _util.readUint16)(data, j);
                j += 2;
              }
              sqcd = data[j++];
              switch (sqcd & 0x1F) {
                case 0:
                  spqcdSize = 8;
                  scalarExpounded = true;
                  break;
                case 1:
                  spqcdSize = 16;
                  scalarExpounded = false;
                  break;
                case 2:
                  spqcdSize = 16;
                  scalarExpounded = true;
                  break;
                default:
                  throw new Error('Invalid SQcd value ' + sqcd);
              }
              qcc.noQuantization = spqcdSize === 8;
              qcc.scalarExpounded = scalarExpounded;
              qcc.guardBits = sqcd >> 5;
              spqcds = [];
              while (j < length + position) {
                spqcd = {};
                if (spqcdSize === 8) {
                  spqcd.epsilon = data[j++] >> 3;
                  spqcd.mu = 0;
                } else {
                  spqcd.epsilon = data[j] >> 3;
                  spqcd.mu = (data[j] & 0x7) << 8 | data[j + 1];
                  j += 2;
                }
                spqcds.push(spqcd);
              }
              qcc.SPqcds = spqcds;
              if (context.mainHeader) {
                context.QCC[cqcc] = qcc;
              } else {
                context.currentTile.QCC[cqcc] = qcc;
              }
              break;
            case 0xFF52:
              length = (0, _util.readUint16)(data, position);
              var cod = {};
              j = position + 2;
              var scod = data[j++];
              cod.entropyCoderWithCustomPrecincts = !!(scod & 1);
              cod.sopMarkerUsed = !!(scod & 2);
              cod.ephMarkerUsed = !!(scod & 4);
              cod.progressionOrder = data[j++];
              cod.layersCount = (0, _util.readUint16)(data, j);
              j += 2;
              cod.multipleComponentTransform = data[j++];
              cod.decompositionLevelsCount = data[j++];
              cod.xcb = (data[j++] & 0xF) + 2;
              cod.ycb = (data[j++] & 0xF) + 2;
              var blockStyle = data[j++];
              cod.selectiveArithmeticCodingBypass = !!(blockStyle & 1);
              cod.resetContextProbabilities = !!(blockStyle & 2);
              cod.terminationOnEachCodingPass = !!(blockStyle & 4);
              cod.verticalyStripe = !!(blockStyle & 8);
              cod.predictableTermination = !!(blockStyle & 16);
              cod.segmentationSymbolUsed = !!(blockStyle & 32);
              cod.reversibleTransformation = data[j++];
              if (cod.entropyCoderWithCustomPrecincts) {
                var precinctsSizes = [];
                while (j < length + position) {
                  var precinctsSize = data[j++];
                  precinctsSizes.push({
                    PPx: precinctsSize & 0xF,
                    PPy: precinctsSize >> 4
                  });
                }
                cod.precinctsSizes = precinctsSizes;
              }
              var unsupported = [];
              if (cod.selectiveArithmeticCodingBypass) {
                unsupported.push('selectiveArithmeticCodingBypass');
              }
              if (cod.resetContextProbabilities) {
                unsupported.push('resetContextProbabilities');
              }
              if (cod.terminationOnEachCodingPass) {
                unsupported.push('terminationOnEachCodingPass');
              }
              if (cod.verticalyStripe) {
                unsupported.push('verticalyStripe');
              }
              if (cod.predictableTermination) {
                unsupported.push('predictableTermination');
              }
              if (unsupported.length > 0) {
                doNotRecover = true;
                throw new Error('Unsupported COD options (' + unsupported.join(', ') + ')');
              }
              if (context.mainHeader) {
                context.COD = cod;
              } else {
                context.currentTile.COD = cod;
                context.currentTile.COC = [];
              }
              break;
            case 0xFF90:
              length = (0, _util.readUint16)(data, position);
              tile = {};
              tile.index = (0, _util.readUint16)(data, position + 2);
              tile.length = (0, _util.readUint32)(data, position + 4);
              tile.dataEnd = tile.length + position - 2;
              tile.partIndex = data[position + 8];
              tile.partsCount = data[position + 9];
              context.mainHeader = false;
              if (tile.partIndex === 0) {
                tile.COD = context.COD;
                tile.COC = context.COC.slice(0);
                tile.QCD = context.QCD;
                tile.QCC = context.QCC.slice(0);
              }
              context.currentTile = tile;
              break;
            case 0xFF93:
              tile = context.currentTile;
              if (tile.partIndex === 0) {
                initializeTile(context, tile.index);
                buildPackets(context);
              }
              length = tile.dataEnd - position;
              parseTilePackets(context, data, position, length);
              break;
            case 0xFF55:
            case 0xFF57:
            case 0xFF58:
            case 0xFF64:
              length = (0, _util.readUint16)(data, position);
              break;
            case 0xFF53:
              throw new Error('Codestream code 0xFF53 (COC) is ' + 'not implemented');
            default:
              throw new Error('Unknown codestream code: ' + code.toString(16));
          }
          position += length;
        }
      } catch (e) {
        if (doNotRecover || this.failOnCorruptedImage) {
          throw new JpxError(e.message);
        } else {
          (0, _util.warn)('JPX: Trying to recover from: ' + e.message);
        }
      }
      this.tiles = transformComponents(context);
      this.width = context.SIZ.Xsiz - context.SIZ.XOsiz;
      this.height = context.SIZ.Ysiz - context.SIZ.YOsiz;
      this.componentsCount = context.SIZ.Csiz;
    }
  };
  function calculateComponentDimensions(component, siz) {
    component.x0 = Math.ceil(siz.XOsiz / component.XRsiz);
    component.x1 = Math.ceil(siz.Xsiz / component.XRsiz);
    component.y0 = Math.ceil(siz.YOsiz / component.YRsiz);
    component.y1 = Math.ceil(siz.Ysiz / component.YRsiz);
    component.width = component.x1 - component.x0;
    component.height = component.y1 - component.y0;
  }
  function calculateTileGrids(context, components) {
    var siz = context.SIZ;
    var tile,
        tiles = [];
    var numXtiles = Math.ceil((siz.Xsiz - siz.XTOsiz) / siz.XTsiz);
    var numYtiles = Math.ceil((siz.Ysiz - siz.YTOsiz) / siz.YTsiz);
    for (var q = 0; q < numYtiles; q++) {
      for (var p = 0; p < numXtiles; p++) {
        tile = {};
        tile.tx0 = Math.max(siz.XTOsiz + p * siz.XTsiz, siz.XOsiz);
        tile.ty0 = Math.max(siz.YTOsiz + q * siz.YTsiz, siz.YOsiz);
        tile.tx1 = Math.min(siz.XTOsiz + (p + 1) * siz.XTsiz, siz.Xsiz);
        tile.ty1 = Math.min(siz.YTOsiz + (q + 1) * siz.YTsiz, siz.Ysiz);
        tile.width = tile.tx1 - tile.tx0;
        tile.height = tile.ty1 - tile.ty0;
        tile.components = [];
        tiles.push(tile);
      }
    }
    context.tiles = tiles;
    var componentsCount = siz.Csiz;
    for (var i = 0, ii = componentsCount; i < ii; i++) {
      var component = components[i];
      for (var j = 0, jj = tiles.length; j < jj; j++) {
        var tileComponent = {};
        tile = tiles[j];
        tileComponent.tcx0 = Math.ceil(tile.tx0 / component.XRsiz);
        tileComponent.tcy0 = Math.ceil(tile.ty0 / component.YRsiz);
        tileComponent.tcx1 = Math.ceil(tile.tx1 / component.XRsiz);
        tileComponent.tcy1 = Math.ceil(tile.ty1 / component.YRsiz);
        tileComponent.width = tileComponent.tcx1 - tileComponent.tcx0;
        tileComponent.height = tileComponent.tcy1 - tileComponent.tcy0;
        tile.components[i] = tileComponent;
      }
    }
  }
  function getBlocksDimensions(context, component, r) {
    var codOrCoc = component.codingStyleParameters;
    var result = {};
    if (!codOrCoc.entropyCoderWithCustomPrecincts) {
      result.PPx = 15;
      result.PPy = 15;
    } else {
      result.PPx = codOrCoc.precinctsSizes[r].PPx;
      result.PPy = codOrCoc.precinctsSizes[r].PPy;
    }
    result.xcb_ = r > 0 ? Math.min(codOrCoc.xcb, result.PPx - 1) : Math.min(codOrCoc.xcb, result.PPx);
    result.ycb_ = r > 0 ? Math.min(codOrCoc.ycb, result.PPy - 1) : Math.min(codOrCoc.ycb, result.PPy);
    return result;
  }
  function buildPrecincts(context, resolution, dimensions) {
    var precinctWidth = 1 << dimensions.PPx;
    var precinctHeight = 1 << dimensions.PPy;
    var isZeroRes = resolution.resLevel === 0;
    var precinctWidthInSubband = 1 << dimensions.PPx + (isZeroRes ? 0 : -1);
    var precinctHeightInSubband = 1 << dimensions.PPy + (isZeroRes ? 0 : -1);
    var numprecinctswide = resolution.trx1 > resolution.trx0 ? Math.ceil(resolution.trx1 / precinctWidth) - Math.floor(resolution.trx0 / precinctWidth) : 0;
    var numprecinctshigh = resolution.try1 > resolution.try0 ? Math.ceil(resolution.try1 / precinctHeight) - Math.floor(resolution.try0 / precinctHeight) : 0;
    var numprecincts = numprecinctswide * numprecinctshigh;
    resolution.precinctParameters = {
      precinctWidth,
      precinctHeight,
      numprecinctswide,
      numprecinctshigh,
      numprecincts,
      precinctWidthInSubband,
      precinctHeightInSubband
    };
  }
  function buildCodeblocks(context, subband, dimensions) {
    var xcb_ = dimensions.xcb_;
    var ycb_ = dimensions.ycb_;
    var codeblockWidth = 1 << xcb_;
    var codeblockHeight = 1 << ycb_;
    var cbx0 = subband.tbx0 >> xcb_;
    var cby0 = subband.tby0 >> ycb_;
    var cbx1 = subband.tbx1 + codeblockWidth - 1 >> xcb_;
    var cby1 = subband.tby1 + codeblockHeight - 1 >> ycb_;
    var precinctParameters = subband.resolution.precinctParameters;
    var codeblocks = [];
    var precincts = [];
    var i, j, codeblock, precinctNumber;
    for (j = cby0; j < cby1; j++) {
      for (i = cbx0; i < cbx1; i++) {
        codeblock = {
          cbx: i,
          cby: j,
          tbx0: codeblockWidth * i,
          tby0: codeblockHeight * j,
          tbx1: codeblockWidth * (i + 1),
          tby1: codeblockHeight * (j + 1)
        };
        codeblock.tbx0_ = Math.max(subband.tbx0, codeblock.tbx0);
        codeblock.tby0_ = Math.max(subband.tby0, codeblock.tby0);
        codeblock.tbx1_ = Math.min(subband.tbx1, codeblock.tbx1);
        codeblock.tby1_ = Math.min(subband.tby1, codeblock.tby1);
        var pi = Math.floor((codeblock.tbx0_ - subband.tbx0) / precinctParameters.precinctWidthInSubband);
        var pj = Math.floor((codeblock.tby0_ - subband.tby0) / precinctParameters.precinctHeightInSubband);
        precinctNumber = pi + pj * precinctParameters.numprecinctswide;
        codeblock.precinctNumber = precinctNumber;
        codeblock.subbandType = subband.type;
        codeblock.Lblock = 3;
        if (codeblock.tbx1_ <= codeblock.tbx0_ || codeblock.tby1_ <= codeblock.tby0_) {
          continue;
        }
        codeblocks.push(codeblock);
        var precinct = precincts[precinctNumber];
        if (precinct !== undefined) {
          if (i < precinct.cbxMin) {
            precinct.cbxMin = i;
          } else if (i > precinct.cbxMax) {
            precinct.cbxMax = i;
          }
          if (j < precinct.cbyMin) {
            precinct.cbxMin = j;
          } else if (j > precinct.cbyMax) {
            precinct.cbyMax = j;
          }
        } else {
          precincts[precinctNumber] = precinct = {
            cbxMin: i,
            cbyMin: j,
            cbxMax: i,
            cbyMax: j
          };
        }
        codeblock.precinct = precinct;
      }
    }
    subband.codeblockParameters = {
      codeblockWidth: xcb_,
      codeblockHeight: ycb_,
      numcodeblockwide: cbx1 - cbx0 + 1,
      numcodeblockhigh: cby1 - cby0 + 1
    };
    subband.codeblocks = codeblocks;
    subband.precincts = precincts;
  }
  function createPacket(resolution, precinctNumber, layerNumber) {
    var precinctCodeblocks = [];
    var subbands = resolution.subbands;
    for (var i = 0, ii = subbands.length; i < ii; i++) {
      var subband = subbands[i];
      var codeblocks = subband.codeblocks;
      for (var j = 0, jj = codeblocks.length; j < jj; j++) {
        var codeblock = codeblocks[j];
        if (codeblock.precinctNumber !== precinctNumber) {
          continue;
        }
        precinctCodeblocks.push(codeblock);
      }
    }
    return {
      layerNumber,
      codeblocks: precinctCodeblocks
    };
  }
  function LayerResolutionComponentPositionIterator(context) {
    var siz = context.SIZ;
    var tileIndex = context.currentTile.index;
    var tile = context.tiles[tileIndex];
    var layersCount = tile.codingStyleDefaultParameters.layersCount;
    var componentsCount = siz.Csiz;
    var maxDecompositionLevelsCount = 0;
    for (var q = 0; q < componentsCount; q++) {
      maxDecompositionLevelsCount = Math.max(maxDecompositionLevelsCount, tile.components[q].codingStyleParameters.decompositionLevelsCount);
    }
    var l = 0,
        r = 0,
        i = 0,
        k = 0;
    this.nextPacket = function JpxImage_nextPacket() {
      for (; l < layersCount; l++) {
        for (; r <= maxDecompositionLevelsCount; r++) {
          for (; i < componentsCount; i++) {
            var component = tile.components[i];
            if (r > component.codingStyleParameters.decompositionLevelsCount) {
              continue;
            }
            var resolution = component.resolutions[r];
            var numprecincts = resolution.precinctParameters.numprecincts;
            for (; k < numprecincts;) {
              var packet = createPacket(resolution, k, l);
              k++;
              return packet;
            }
            k = 0;
          }
          i = 0;
        }
        r = 0;
      }
      throw new JpxError('Out of packets');
    };
  }
  function ResolutionLayerComponentPositionIterator(context) {
    var siz = context.SIZ;
    var tileIndex = context.currentTile.index;
    var tile = context.tiles[tileIndex];
    var layersCount = tile.codingStyleDefaultParameters.layersCount;
    var componentsCount = siz.Csiz;
    var maxDecompositionLevelsCount = 0;
    for (var q = 0; q < componentsCount; q++) {
      maxDecompositionLevelsCount = Math.max(maxDecompositionLevelsCount, tile.components[q].codingStyleParameters.decompositionLevelsCount);
    }
    var r = 0,
        l = 0,
        i = 0,
        k = 0;
    this.nextPacket = function JpxImage_nextPacket() {
      for (; r <= maxDecompositionLevelsCount; r++) {
        for (; l < layersCount; l++) {
          for (; i < componentsCount; i++) {
            var component = tile.components[i];
            if (r > component.codingStyleParameters.decompositionLevelsCount) {
              continue;
            }
            var resolution = component.resolutions[r];
            var numprecincts = resolution.precinctParameters.numprecincts;
            for (; k < numprecincts;) {
              var packet = createPacket(resolution, k, l);
              k++;
              return packet;
            }
            k = 0;
          }
          i = 0;
        }
        l = 0;
      }
      throw new JpxError('Out of packets');
    };
  }
  function ResolutionPositionComponentLayerIterator(context) {
    var siz = context.SIZ;
    var tileIndex = context.currentTile.index;
    var tile = context.tiles[tileIndex];
    var layersCount = tile.codingStyleDefaultParameters.layersCount;
    var componentsCount = siz.Csiz;
    var l, r, c, p;
    var maxDecompositionLevelsCount = 0;
    for (c = 0; c < componentsCount; c++) {
      var component = tile.components[c];
      maxDecompositionLevelsCount = Math.max(maxDecompositionLevelsCount, component.codingStyleParameters.decompositionLevelsCount);
    }
    var maxNumPrecinctsInLevel = new Int32Array(maxDecompositionLevelsCount + 1);
    for (r = 0; r <= maxDecompositionLevelsCount; ++r) {
      var maxNumPrecincts = 0;
      for (c = 0; c < componentsCount; ++c) {
        var resolutions = tile.components[c].resolutions;
        if (r < resolutions.length) {
          maxNumPrecincts = Math.max(maxNumPrecincts, resolutions[r].precinctParameters.numprecincts);
        }
      }
      maxNumPrecinctsInLevel[r] = maxNumPrecincts;
    }
    l = 0;
    r = 0;
    c = 0;
    p = 0;
    this.nextPacket = function JpxImage_nextPacket() {
      for (; r <= maxDecompositionLevelsCount; r++) {
        for (; p < maxNumPrecinctsInLevel[r]; p++) {
          for (; c < componentsCount; c++) {
            var component = tile.components[c];
            if (r > component.codingStyleParameters.decompositionLevelsCount) {
              continue;
            }
            var resolution = component.resolutions[r];
            var numprecincts = resolution.precinctParameters.numprecincts;
            if (p >= numprecincts) {
              continue;
            }
            for (; l < layersCount;) {
              var packet = createPacket(resolution, p, l);
              l++;
              return packet;
            }
            l = 0;
          }
          c = 0;
        }
        p = 0;
      }
      throw new JpxError('Out of packets');
    };
  }
  function PositionComponentResolutionLayerIterator(context) {
    var siz = context.SIZ;
    var tileIndex = context.currentTile.index;
    var tile = context.tiles[tileIndex];
    var layersCount = tile.codingStyleDefaultParameters.layersCount;
    var componentsCount = siz.Csiz;
    var precinctsSizes = getPrecinctSizesInImageScale(tile);
    var precinctsIterationSizes = precinctsSizes;
    var l = 0,
        r = 0,
        c = 0,
        px = 0,
        py = 0;
    this.nextPacket = function JpxImage_nextPacket() {
      for (; py < precinctsIterationSizes.maxNumHigh; py++) {
        for (; px < precinctsIterationSizes.maxNumWide; px++) {
          for (; c < componentsCount; c++) {
            var component = tile.components[c];
            var decompositionLevelsCount = component.codingStyleParameters.decompositionLevelsCount;
            for (; r <= decompositionLevelsCount; r++) {
              var resolution = component.resolutions[r];
              var sizeInImageScale = precinctsSizes.components[c].resolutions[r];
              var k = getPrecinctIndexIfExist(px, py, sizeInImageScale, precinctsIterationSizes, resolution);
              if (k === null) {
                continue;
              }
              for (; l < layersCount;) {
                var packet = createPacket(resolution, k, l);
                l++;
                return packet;
              }
              l = 0;
            }
            r = 0;
          }
          c = 0;
        }
        px = 0;
      }
      throw new JpxError('Out of packets');
    };
  }
  function ComponentPositionResolutionLayerIterator(context) {
    var siz = context.SIZ;
    var tileIndex = context.currentTile.index;
    var tile = context.tiles[tileIndex];
    var layersCount = tile.codingStyleDefaultParameters.layersCount;
    var componentsCount = siz.Csiz;
    var precinctsSizes = getPrecinctSizesInImageScale(tile);
    var l = 0,
        r = 0,
        c = 0,
        px = 0,
        py = 0;
    this.nextPacket = function JpxImage_nextPacket() {
      for (; c < componentsCount; ++c) {
        var component = tile.components[c];
        var precinctsIterationSizes = precinctsSizes.components[c];
        var decompositionLevelsCount = component.codingStyleParameters.decompositionLevelsCount;
        for (; py < precinctsIterationSizes.maxNumHigh; py++) {
          for (; px < precinctsIterationSizes.maxNumWide; px++) {
            for (; r <= decompositionLevelsCount; r++) {
              var resolution = component.resolutions[r];
              var sizeInImageScale = precinctsIterationSizes.resolutions[r];
              var k = getPrecinctIndexIfExist(px, py, sizeInImageScale, precinctsIterationSizes, resolution);
              if (k === null) {
                continue;
              }
              for (; l < layersCount;) {
                var packet = createPacket(resolution, k, l);
                l++;
                return packet;
              }
              l = 0;
            }
            r = 0;
          }
          px = 0;
        }
        py = 0;
      }
      throw new JpxError('Out of packets');
    };
  }
  function getPrecinctIndexIfExist(pxIndex, pyIndex, sizeInImageScale, precinctIterationSizes, resolution) {
    var posX = pxIndex * precinctIterationSizes.minWidth;
    var posY = pyIndex * precinctIterationSizes.minHeight;
    if (posX % sizeInImageScale.width !== 0 || posY % sizeInImageScale.height !== 0) {
      return null;
    }
    var startPrecinctRowIndex = posY / sizeInImageScale.width * resolution.precinctParameters.numprecinctswide;
    return posX / sizeInImageScale.height + startPrecinctRowIndex;
  }
  function getPrecinctSizesInImageScale(tile) {
    var componentsCount = tile.components.length;
    var minWidth = Number.MAX_VALUE;
    var minHeight = Number.MAX_VALUE;
    var maxNumWide = 0;
    var maxNumHigh = 0;
    var sizePerComponent = new Array(componentsCount);
    for (var c = 0; c < componentsCount; c++) {
      var component = tile.components[c];
      var decompositionLevelsCount = component.codingStyleParameters.decompositionLevelsCount;
      var sizePerResolution = new Array(decompositionLevelsCount + 1);
      var minWidthCurrentComponent = Number.MAX_VALUE;
      var minHeightCurrentComponent = Number.MAX_VALUE;
      var maxNumWideCurrentComponent = 0;
      var maxNumHighCurrentComponent = 0;
      var scale = 1;
      for (var r = decompositionLevelsCount; r >= 0; --r) {
        var resolution = component.resolutions[r];
        var widthCurrentResolution = scale * resolution.precinctParameters.precinctWidth;
        var heightCurrentResolution = scale * resolution.precinctParameters.precinctHeight;
        minWidthCurrentComponent = Math.min(minWidthCurrentComponent, widthCurrentResolution);
        minHeightCurrentComponent = Math.min(minHeightCurrentComponent, heightCurrentResolution);
        maxNumWideCurrentComponent = Math.max(maxNumWideCurrentComponent, resolution.precinctParameters.numprecinctswide);
        maxNumHighCurrentComponent = Math.max(maxNumHighCurrentComponent, resolution.precinctParameters.numprecinctshigh);
        sizePerResolution[r] = {
          width: widthCurrentResolution,
          height: heightCurrentResolution
        };
        scale <<= 1;
      }
      minWidth = Math.min(minWidth, minWidthCurrentComponent);
      minHeight = Math.min(minHeight, minHeightCurrentComponent);
      maxNumWide = Math.max(maxNumWide, maxNumWideCurrentComponent);
      maxNumHigh = Math.max(maxNumHigh, maxNumHighCurrentComponent);
      sizePerComponent[c] = {
        resolutions: sizePerResolution,
        minWidth: minWidthCurrentComponent,
        minHeight: minHeightCurrentComponent,
        maxNumWide: maxNumWideCurrentComponent,
        maxNumHigh: maxNumHighCurrentComponent
      };
    }
    return {
      components: sizePerComponent,
      minWidth,
      minHeight,
      maxNumWide,
      maxNumHigh
    };
  }
  function buildPackets(context) {
    var siz = context.SIZ;
    var tileIndex = context.currentTile.index;
    var tile = context.tiles[tileIndex];
    var componentsCount = siz.Csiz;
    for (var c = 0; c < componentsCount; c++) {
      var component = tile.components[c];
      var decompositionLevelsCount = component.codingStyleParameters.decompositionLevelsCount;
      var resolutions = [];
      var subbands = [];
      for (var r = 0; r <= decompositionLevelsCount; r++) {
        var blocksDimensions = getBlocksDimensions(context, component, r);
        var resolution = {};
        var scale = 1 << decompositionLevelsCount - r;
        resolution.trx0 = Math.ceil(component.tcx0 / scale);
        resolution.try0 = Math.ceil(component.tcy0 / scale);
        resolution.trx1 = Math.ceil(component.tcx1 / scale);
        resolution.try1 = Math.ceil(component.tcy1 / scale);
        resolution.resLevel = r;
        buildPrecincts(context, resolution, blocksDimensions);
        resolutions.push(resolution);
        var subband;
        if (r === 0) {
          subband = {};
          subband.type = 'LL';
          subband.tbx0 = Math.ceil(component.tcx0 / scale);
          subband.tby0 = Math.ceil(component.tcy0 / scale);
          subband.tbx1 = Math.ceil(component.tcx1 / scale);
          subband.tby1 = Math.ceil(component.tcy1 / scale);
          subband.resolution = resolution;
          buildCodeblocks(context, subband, blocksDimensions);
          subbands.push(subband);
          resolution.subbands = [subband];
        } else {
          var bscale = 1 << decompositionLevelsCount - r + 1;
          var resolutionSubbands = [];
          subband = {};
          subband.type = 'HL';
          subband.tbx0 = Math.ceil(component.tcx0 / bscale - 0.5);
          subband.tby0 = Math.ceil(component.tcy0 / bscale);
          subband.tbx1 = Math.ceil(component.tcx1 / bscale - 0.5);
          subband.tby1 = Math.ceil(component.tcy1 / bscale);
          subband.resolution = resolution;
          buildCodeblocks(context, subband, blocksDimensions);
          subbands.push(subband);
          resolutionSubbands.push(subband);
          subband = {};
          subband.type = 'LH';
          subband.tbx0 = Math.ceil(component.tcx0 / bscale);
          subband.tby0 = Math.ceil(component.tcy0 / bscale - 0.5);
          subband.tbx1 = Math.ceil(component.tcx1 / bscale);
          subband.tby1 = Math.ceil(component.tcy1 / bscale - 0.5);
          subband.resolution = resolution;
          buildCodeblocks(context, subband, blocksDimensions);
          subbands.push(subband);
          resolutionSubbands.push(subband);
          subband = {};
          subband.type = 'HH';
          subband.tbx0 = Math.ceil(component.tcx0 / bscale - 0.5);
          subband.tby0 = Math.ceil(component.tcy0 / bscale - 0.5);
          subband.tbx1 = Math.ceil(component.tcx1 / bscale - 0.5);
          subband.tby1 = Math.ceil(component.tcy1 / bscale - 0.5);
          subband.resolution = resolution;
          buildCodeblocks(context, subband, blocksDimensions);
          subbands.push(subband);
          resolutionSubbands.push(subband);
          resolution.subbands = resolutionSubbands;
        }
      }
      component.resolutions = resolutions;
      component.subbands = subbands;
    }
    var progressionOrder = tile.codingStyleDefaultParameters.progressionOrder;
    switch (progressionOrder) {
      case 0:
        tile.packetsIterator = new LayerResolutionComponentPositionIterator(context);
        break;
      case 1:
        tile.packetsIterator = new ResolutionLayerComponentPositionIterator(context);
        break;
      case 2:
        tile.packetsIterator = new ResolutionPositionComponentLayerIterator(context);
        break;
      case 3:
        tile.packetsIterator = new PositionComponentResolutionLayerIterator(context);
        break;
      case 4:
        tile.packetsIterator = new ComponentPositionResolutionLayerIterator(context);
        break;
      default:
        throw new JpxError(`Unsupported progression order ${progressionOrder}`);
    }
  }
  function parseTilePackets(context, data, offset, dataLength) {
    var position = 0;
    var buffer,
        bufferSize = 0,
        skipNextBit = false;
    function readBits(count) {
      while (bufferSize < count) {
        var b = data[offset + position];
        position++;
        if (skipNextBit) {
          buffer = buffer << 7 | b;
          bufferSize += 7;
          skipNextBit = false;
        } else {
          buffer = buffer << 8 | b;
          bufferSize += 8;
        }
        if (b === 0xFF) {
          skipNextBit = true;
        }
      }
      bufferSize -= count;
      return buffer >>> bufferSize & (1 << count) - 1;
    }
    function skipMarkerIfEqual(value) {
      if (data[offset + position - 1] === 0xFF && data[offset + position] === value) {
        skipBytes(1);
        return true;
      } else if (data[offset + position] === 0xFF && data[offset + position + 1] === value) {
        skipBytes(2);
        return true;
      }
      return false;
    }
    function skipBytes(count) {
      position += count;
    }
    function alignToByte() {
      bufferSize = 0;
      if (skipNextBit) {
        position++;
        skipNextBit = false;
      }
    }
    function readCodingpasses() {
      if (readBits(1) === 0) {
        return 1;
      }
      if (readBits(1) === 0) {
        return 2;
      }
      var value = readBits(2);
      if (value < 3) {
        return value + 3;
      }
      value = readBits(5);
      if (value < 31) {
        return value + 6;
      }
      value = readBits(7);
      return value + 37;
    }
    var tileIndex = context.currentTile.index;
    var tile = context.tiles[tileIndex];
    var sopMarkerUsed = context.COD.sopMarkerUsed;
    var ephMarkerUsed = context.COD.ephMarkerUsed;
    var packetsIterator = tile.packetsIterator;
    while (position < dataLength) {
      alignToByte();
      if (sopMarkerUsed && skipMarkerIfEqual(0x91)) {
        skipBytes(4);
      }
      var packet = packetsIterator.nextPacket();
      if (!readBits(1)) {
        continue;
      }
      var layerNumber = packet.layerNumber;
      var queue = [],
          codeblock;
      for (var i = 0, ii = packet.codeblocks.length; i < ii; i++) {
        codeblock = packet.codeblocks[i];
        var precinct = codeblock.precinct;
        var codeblockColumn = codeblock.cbx - precinct.cbxMin;
        var codeblockRow = codeblock.cby - precinct.cbyMin;
        var codeblockIncluded = false;
        var firstTimeInclusion = false;
        var valueReady;
        if (codeblock['included'] !== undefined) {
          codeblockIncluded = !!readBits(1);
        } else {
          precinct = codeblock.precinct;
          var inclusionTree, zeroBitPlanesTree;
          if (precinct['inclusionTree'] !== undefined) {
            inclusionTree = precinct.inclusionTree;
          } else {
            var width = precinct.cbxMax - precinct.cbxMin + 1;
            var height = precinct.cbyMax - precinct.cbyMin + 1;
            inclusionTree = new InclusionTree(width, height, layerNumber);
            zeroBitPlanesTree = new TagTree(width, height);
            precinct.inclusionTree = inclusionTree;
            precinct.zeroBitPlanesTree = zeroBitPlanesTree;
          }
          if (inclusionTree.reset(codeblockColumn, codeblockRow, layerNumber)) {
            while (true) {
              if (readBits(1)) {
                valueReady = !inclusionTree.nextLevel();
                if (valueReady) {
                  codeblock.included = true;
                  codeblockIncluded = firstTimeInclusion = true;
                  break;
                }
              } else {
                inclusionTree.incrementValue(layerNumber);
                break;
              }
            }
          }
        }
        if (!codeblockIncluded) {
          continue;
        }
        if (firstTimeInclusion) {
          zeroBitPlanesTree = precinct.zeroBitPlanesTree;
          zeroBitPlanesTree.reset(codeblockColumn, codeblockRow);
          while (true) {
            if (readBits(1)) {
              valueReady = !zeroBitPlanesTree.nextLevel();
              if (valueReady) {
                break;
              }
            } else {
              zeroBitPlanesTree.incrementValue();
            }
          }
          codeblock.zeroBitPlanes = zeroBitPlanesTree.value;
        }
        var codingpasses = readCodingpasses();
        while (readBits(1)) {
          codeblock.Lblock++;
        }
        var codingpassesLog2 = (0, _util.log2)(codingpasses);
        var bits = (codingpasses < 1 << codingpassesLog2 ? codingpassesLog2 - 1 : codingpassesLog2) + codeblock.Lblock;
        var codedDataLength = readBits(bits);
        queue.push({
          codeblock,
          codingpasses,
          dataLength: codedDataLength
        });
      }
      alignToByte();
      if (ephMarkerUsed) {
        skipMarkerIfEqual(0x92);
      }
      while (queue.length > 0) {
        var packetItem = queue.shift();
        codeblock = packetItem.codeblock;
        if (codeblock['data'] === undefined) {
          codeblock.data = [];
        }
        codeblock.data.push({
          data,
          start: offset + position,
          end: offset + position + packetItem.dataLength,
          codingpasses: packetItem.codingpasses
        });
        position += packetItem.dataLength;
      }
    }
    return position;
  }
  function copyCoefficients(coefficients, levelWidth, levelHeight, subband, delta, mb, reversible, segmentationSymbolUsed) {
    var x0 = subband.tbx0;
    var y0 = subband.tby0;
    var width = subband.tbx1 - subband.tbx0;
    var codeblocks = subband.codeblocks;
    var right = subband.type.charAt(0) === 'H' ? 1 : 0;
    var bottom = subband.type.charAt(1) === 'H' ? levelWidth : 0;
    for (var i = 0, ii = codeblocks.length; i < ii; ++i) {
      var codeblock = codeblocks[i];
      var blockWidth = codeblock.tbx1_ - codeblock.tbx0_;
      var blockHeight = codeblock.tby1_ - codeblock.tby0_;
      if (blockWidth === 0 || blockHeight === 0) {
        continue;
      }
      if (codeblock['data'] === undefined) {
        continue;
      }
      var bitModel, currentCodingpassType;
      bitModel = new BitModel(blockWidth, blockHeight, codeblock.subbandType, codeblock.zeroBitPlanes, mb);
      currentCodingpassType = 2;
      var data = codeblock.data,
          totalLength = 0,
          codingpasses = 0;
      var j, jj, dataItem;
      for (j = 0, jj = data.length; j < jj; j++) {
        dataItem = data[j];
        totalLength += dataItem.end - dataItem.start;
        codingpasses += dataItem.codingpasses;
      }
      var encodedData = new Uint8Array(totalLength);
      var position = 0;
      for (j = 0, jj = data.length; j < jj; j++) {
        dataItem = data[j];
        var chunk = dataItem.data.subarray(dataItem.start, dataItem.end);
        encodedData.set(chunk, position);
        position += chunk.length;
      }
      var decoder = new _arithmetic_decoder.ArithmeticDecoder(encodedData, 0, totalLength);
      bitModel.setDecoder(decoder);
      for (j = 0; j < codingpasses; j++) {
        switch (currentCodingpassType) {
          case 0:
            bitModel.runSignificancePropagationPass();
            break;
          case 1:
            bitModel.runMagnitudeRefinementPass();
            break;
          case 2:
            bitModel.runCleanupPass();
            if (segmentationSymbolUsed) {
              bitModel.checkSegmentationSymbol();
            }
            break;
        }
        currentCodingpassType = (currentCodingpassType + 1) % 3;
      }
      var offset = codeblock.tbx0_ - x0 + (codeblock.tby0_ - y0) * width;
      var sign = bitModel.coefficentsSign;
      var magnitude = bitModel.coefficentsMagnitude;
      var bitsDecoded = bitModel.bitsDecoded;
      var magnitudeCorrection = reversible ? 0 : 0.5;
      var k, n, nb;
      position = 0;
      var interleave = subband.type !== 'LL';
      for (j = 0; j < blockHeight; j++) {
        var row = offset / width | 0;
        var levelOffset = 2 * row * (levelWidth - width) + right + bottom;
        for (k = 0; k < blockWidth; k++) {
          n = magnitude[position];
          if (n !== 0) {
            n = (n + magnitudeCorrection) * delta;
            if (sign[position] !== 0) {
              n = -n;
            }
            nb = bitsDecoded[position];
            var pos = interleave ? levelOffset + (offset << 1) : offset;
            if (reversible && nb >= mb) {
              coefficients[pos] = n;
            } else {
              coefficients[pos] = n * (1 << mb - nb);
            }
          }
          offset++;
          position++;
        }
        offset += width - blockWidth;
      }
    }
  }
  function transformTile(context, tile, c) {
    var component = tile.components[c];
    var codingStyleParameters = component.codingStyleParameters;
    var quantizationParameters = component.quantizationParameters;
    var decompositionLevelsCount = codingStyleParameters.decompositionLevelsCount;
    var spqcds = quantizationParameters.SPqcds;
    var scalarExpounded = quantizationParameters.scalarExpounded;
    var guardBits = quantizationParameters.guardBits;
    var segmentationSymbolUsed = codingStyleParameters.segmentationSymbolUsed;
    var precision = context.components[c].precision;
    var reversible = codingStyleParameters.reversibleTransformation;
    var transform = reversible ? new ReversibleTransform() : new IrreversibleTransform();
    var subbandCoefficients = [];
    var b = 0;
    for (var i = 0; i <= decompositionLevelsCount; i++) {
      var resolution = component.resolutions[i];
      var width = resolution.trx1 - resolution.trx0;
      var height = resolution.try1 - resolution.try0;
      var coefficients = new Float32Array(width * height);
      for (var j = 0, jj = resolution.subbands.length; j < jj; j++) {
        var mu, epsilon;
        if (!scalarExpounded) {
          mu = spqcds[0].mu;
          epsilon = spqcds[0].epsilon + (i > 0 ? 1 - i : 0);
        } else {
          mu = spqcds[b].mu;
          epsilon = spqcds[b].epsilon;
          b++;
        }
        var subband = resolution.subbands[j];
        var gainLog2 = SubbandsGainLog2[subband.type];
        var delta = reversible ? 1 : Math.pow(2, precision + gainLog2 - epsilon) * (1 + mu / 2048);
        var mb = guardBits + epsilon - 1;
        copyCoefficients(coefficients, width, height, subband, delta, mb, reversible, segmentationSymbolUsed);
      }
      subbandCoefficients.push({
        width,
        height,
        items: coefficients
      });
    }
    var result = transform.calculate(subbandCoefficients, component.tcx0, component.tcy0);
    return {
      left: component.tcx0,
      top: component.tcy0,
      width: result.width,
      height: result.height,
      items: result.items
    };
  }
  function transformComponents(context) {
    var siz = context.SIZ;
    var components = context.components;
    var componentsCount = siz.Csiz;
    var resultImages = [];
    for (var i = 0, ii = context.tiles.length; i < ii; i++) {
      var tile = context.tiles[i];
      var transformedTiles = [];
      var c;
      for (c = 0; c < componentsCount; c++) {
        transformedTiles[c] = transformTile(context, tile, c);
      }
      var tile0 = transformedTiles[0];
      var out = new Uint8ClampedArray(tile0.items.length * componentsCount);
      var result = {
        left: tile0.left,
        top: tile0.top,
        width: tile0.width,
        height: tile0.height,
        items: out
      };
      var shift, offset;
      var pos = 0,
          j,
          jj,
          y0,
          y1,
          y2;
      if (tile.codingStyleDefaultParameters.multipleComponentTransform) {
        var fourComponents = componentsCount === 4;
        var y0items = transformedTiles[0].items;
        var y1items = transformedTiles[1].items;
        var y2items = transformedTiles[2].items;
        var y3items = fourComponents ? transformedTiles[3].items : null;
        shift = components[0].precision - 8;
        offset = (128 << shift) + 0.5;
        var component0 = tile.components[0];
        var alpha01 = componentsCount - 3;
        jj = y0items.length;
        if (!component0.codingStyleParameters.reversibleTransformation) {
          for (j = 0; j < jj; j++, pos += alpha01) {
            y0 = y0items[j] + offset;
            y1 = y1items[j];
            y2 = y2items[j];
            out[pos++] = y0 + 1.402 * y2 >> shift;
            out[pos++] = y0 - 0.34413 * y1 - 0.71414 * y2 >> shift;
            out[pos++] = y0 + 1.772 * y1 >> shift;
          }
        } else {
          for (j = 0; j < jj; j++, pos += alpha01) {
            y0 = y0items[j] + offset;
            y1 = y1items[j];
            y2 = y2items[j];
            let g = y0 - (y2 + y1 >> 2);
            out[pos++] = g + y2 >> shift;
            out[pos++] = g >> shift;
            out[pos++] = g + y1 >> shift;
          }
        }
        if (fourComponents) {
          for (j = 0, pos = 3; j < jj; j++, pos += 4) {
            out[pos] = y3items[j] + offset >> shift;
          }
        }
      } else {
        for (c = 0; c < componentsCount; c++) {
          var items = transformedTiles[c].items;
          shift = components[c].precision - 8;
          offset = (128 << shift) + 0.5;
          for (pos = c, j = 0, jj = items.length; j < jj; j++) {
            out[pos] = items[j] + offset >> shift;
            pos += componentsCount;
          }
        }
      }
      resultImages.push(result);
    }
    return resultImages;
  }
  function initializeTile(context, tileIndex) {
    var siz = context.SIZ;
    var componentsCount = siz.Csiz;
    var tile = context.tiles[tileIndex];
    for (var c = 0; c < componentsCount; c++) {
      var component = tile.components[c];
      var qcdOrQcc = context.currentTile.QCC[c] !== undefined ? context.currentTile.QCC[c] : context.currentTile.QCD;
      component.quantizationParameters = qcdOrQcc;
      var codOrCoc = context.currentTile.COC[c] !== undefined ? context.currentTile.COC[c] : context.currentTile.COD;
      component.codingStyleParameters = codOrCoc;
    }
    tile.codingStyleDefaultParameters = context.currentTile.COD;
  }
  var TagTree = function TagTreeClosure() {
    function TagTree(width, height) {
      var levelsLength = (0, _util.log2)(Math.max(width, height)) + 1;
      this.levels = [];
      for (var i = 0; i < levelsLength; i++) {
        var level = {
          width,
          height,
          items: []
        };
        this.levels.push(level);
        width = Math.ceil(width / 2);
        height = Math.ceil(height / 2);
      }
    }
    TagTree.prototype = {
      reset: function TagTree_reset(i, j) {
        var currentLevel = 0,
            value = 0,
            level;
        while (currentLevel < this.levels.length) {
          level = this.levels[currentLevel];
          var index = i + j * level.width;
          if (level.items[index] !== undefined) {
            value = level.items[index];
            break;
          }
          level.index = index;
          i >>= 1;
          j >>= 1;
          currentLevel++;
        }
        currentLevel--;
        level = this.levels[currentLevel];
        level.items[level.index] = value;
        this.currentLevel = currentLevel;
        delete this.value;
      },
      incrementValue: function TagTree_incrementValue() {
        var level = this.levels[this.currentLevel];
        level.items[level.index]++;
      },
      nextLevel: function TagTree_nextLevel() {
        var currentLevel = this.currentLevel;
        var level = this.levels[currentLevel];
        var value = level.items[level.index];
        currentLevel--;
        if (currentLevel < 0) {
          this.value = value;
          return false;
        }
        this.currentLevel = currentLevel;
        level = this.levels[currentLevel];
        level.items[level.index] = value;
        return true;
      }
    };
    return TagTree;
  }();
  var InclusionTree = function InclusionTreeClosure() {
    function InclusionTree(width, height, defaultValue) {
      var levelsLength = (0, _util.log2)(Math.max(width, height)) + 1;
      this.levels = [];
      for (var i = 0; i < levelsLength; i++) {
        var items = new Uint8Array(width * height);
        for (var j = 0, jj = items.length; j < jj; j++) {
          items[j] = defaultValue;
        }
        var level = {
          width,
          height,
          items
        };
        this.levels.push(level);
        width = Math.ceil(width / 2);
        height = Math.ceil(height / 2);
      }
    }
    InclusionTree.prototype = {
      reset: function InclusionTree_reset(i, j, stopValue) {
        var currentLevel = 0;
        while (currentLevel < this.levels.length) {
          var level = this.levels[currentLevel];
          var index = i + j * level.width;
          level.index = index;
          var value = level.items[index];
          if (value === 0xFF) {
            break;
          }
          if (value > stopValue) {
            this.currentLevel = currentLevel;
            this.propagateValues();
            return false;
          }
          i >>= 1;
          j >>= 1;
          currentLevel++;
        }
        this.currentLevel = currentLevel - 1;
        return true;
      },
      incrementValue: function InclusionTree_incrementValue(stopValue) {
        var level = this.levels[this.currentLevel];
        level.items[level.index] = stopValue + 1;
        this.propagateValues();
      },
      propagateValues: function InclusionTree_propagateValues() {
        var levelIndex = this.currentLevel;
        var level = this.levels[levelIndex];
        var currentValue = level.items[level.index];
        while (--levelIndex >= 0) {
          level = this.levels[levelIndex];
          level.items[level.index] = currentValue;
        }
      },
      nextLevel: function InclusionTree_nextLevel() {
        var currentLevel = this.currentLevel;
        var level = this.levels[currentLevel];
        var value = level.items[level.index];
        level.items[level.index] = 0xFF;
        currentLevel--;
        if (currentLevel < 0) {
          return false;
        }
        this.currentLevel = currentLevel;
        level = this.levels[currentLevel];
        level.items[level.index] = value;
        return true;
      }
    };
    return InclusionTree;
  }();
  var BitModel = function BitModelClosure() {
    var UNIFORM_CONTEXT = 17;
    var RUNLENGTH_CONTEXT = 18;
    var LLAndLHContextsLabel = new Uint8Array([0, 5, 8, 0, 3, 7, 8, 0, 4, 7, 8, 0, 0, 0, 0, 0, 1, 6, 8, 0, 3, 7, 8, 0, 4, 7, 8, 0, 0, 0, 0, 0, 2, 6, 8, 0, 3, 7, 8, 0, 4, 7, 8, 0, 0, 0, 0, 0, 2, 6, 8, 0, 3, 7, 8, 0, 4, 7, 8, 0, 0, 0, 0, 0, 2, 6, 8, 0, 3, 7, 8, 0, 4, 7, 8]);
    var HLContextLabel = new Uint8Array([0, 3, 4, 0, 5, 7, 7, 0, 8, 8, 8, 0, 0, 0, 0, 0, 1, 3, 4, 0, 6, 7, 7, 0, 8, 8, 8, 0, 0, 0, 0, 0, 2, 3, 4, 0, 6, 7, 7, 0, 8, 8, 8, 0, 0, 0, 0, 0, 2, 3, 4, 0, 6, 7, 7, 0, 8, 8, 8, 0, 0, 0, 0, 0, 2, 3, 4, 0, 6, 7, 7, 0, 8, 8, 8]);
    var HHContextLabel = new Uint8Array([0, 1, 2, 0, 1, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 0, 3, 4, 5, 0, 4, 5, 5, 0, 5, 5, 5, 0, 0, 0, 0, 0, 6, 7, 7, 0, 7, 7, 7, 0, 7, 7, 7, 0, 0, 0, 0, 0, 8, 8, 8, 0, 8, 8, 8, 0, 8, 8, 8, 0, 0, 0, 0, 0, 8, 8, 8, 0, 8, 8, 8, 0, 8, 8, 8]);
    function BitModel(width, height, subband, zeroBitPlanes, mb) {
      this.width = width;
      this.height = height;
      this.contextLabelTable = subband === 'HH' ? HHContextLabel : subband === 'HL' ? HLContextLabel : LLAndLHContextsLabel;
      var coefficientCount = width * height;
      this.neighborsSignificance = new Uint8Array(coefficientCount);
      this.coefficentsSign = new Uint8Array(coefficientCount);
      this.coefficentsMagnitude = mb > 14 ? new Uint32Array(coefficientCount) : mb > 6 ? new Uint16Array(coefficientCount) : new Uint8Array(coefficientCount);
      this.processingFlags = new Uint8Array(coefficientCount);
      var bitsDecoded = new Uint8Array(coefficientCount);
      if (zeroBitPlanes !== 0) {
        for (var i = 0; i < coefficientCount; i++) {
          bitsDecoded[i] = zeroBitPlanes;
        }
      }
      this.bitsDecoded = bitsDecoded;
      this.reset();
    }
    BitModel.prototype = {
      setDecoder: function BitModel_setDecoder(decoder) {
        this.decoder = decoder;
      },
      reset: function BitModel_reset() {
        this.contexts = new Int8Array(19);
        this.contexts[0] = 4 << 1 | 0;
        this.contexts[UNIFORM_CONTEXT] = 46 << 1 | 0;
        this.contexts[RUNLENGTH_CONTEXT] = 3 << 1 | 0;
      },
      setNeighborsSignificance: function BitModel_setNeighborsSignificance(row, column, index) {
        var neighborsSignificance = this.neighborsSignificance;
        var width = this.width,
            height = this.height;
        var left = column > 0;
        var right = column + 1 < width;
        var i;
        if (row > 0) {
          i = index - width;
          if (left) {
            neighborsSignificance[i - 1] += 0x10;
          }
          if (right) {
            neighborsSignificance[i + 1] += 0x10;
          }
          neighborsSignificance[i] += 0x04;
        }
        if (row + 1 < height) {
          i = index + width;
          if (left) {
            neighborsSignificance[i - 1] += 0x10;
          }
          if (right) {
            neighborsSignificance[i + 1] += 0x10;
          }
          neighborsSignificance[i] += 0x04;
        }
        if (left) {
          neighborsSignificance[index - 1] += 0x01;
        }
        if (right) {
          neighborsSignificance[index + 1] += 0x01;
        }
        neighborsSignificance[index] |= 0x80;
      },
      runSignificancePropagationPass: function BitModel_runSignificancePropagationPass() {
        var decoder = this.decoder;
        var width = this.width,
            height = this.height;
        var coefficentsMagnitude = this.coefficentsMagnitude;
        var coefficentsSign = this.coefficentsSign;
        var neighborsSignificance = this.neighborsSignificance;
        var processingFlags = this.processingFlags;
        var contexts = this.contexts;
        var labels = this.contextLabelTable;
        var bitsDecoded = this.bitsDecoded;
        var processedInverseMask = ~1;
        var processedMask = 1;
        var firstMagnitudeBitMask = 2;
        for (var i0 = 0; i0 < height; i0 += 4) {
          for (var j = 0; j < width; j++) {
            var index = i0 * width + j;
            for (var i1 = 0; i1 < 4; i1++, index += width) {
              var i = i0 + i1;
              if (i >= height) {
                break;
              }
              processingFlags[index] &= processedInverseMask;
              if (coefficentsMagnitude[index] || !neighborsSignificance[index]) {
                continue;
              }
              var contextLabel = labels[neighborsSignificance[index]];
              var decision = decoder.readBit(contexts, contextLabel);
              if (decision) {
                var sign = this.decodeSignBit(i, j, index);
                coefficentsSign[index] = sign;
                coefficentsMagnitude[index] = 1;
                this.setNeighborsSignificance(i, j, index);
                processingFlags[index] |= firstMagnitudeBitMask;
              }
              bitsDecoded[index]++;
              processingFlags[index] |= processedMask;
            }
          }
        }
      },
      decodeSignBit: function BitModel_decodeSignBit(row, column, index) {
        var width = this.width,
            height = this.height;
        var coefficentsMagnitude = this.coefficentsMagnitude;
        var coefficentsSign = this.coefficentsSign;
        var contribution, sign0, sign1, significance1;
        var contextLabel, decoded;
        significance1 = column > 0 && coefficentsMagnitude[index - 1] !== 0;
        if (column + 1 < width && coefficentsMagnitude[index + 1] !== 0) {
          sign1 = coefficentsSign[index + 1];
          if (significance1) {
            sign0 = coefficentsSign[index - 1];
            contribution = 1 - sign1 - sign0;
          } else {
            contribution = 1 - sign1 - sign1;
          }
        } else if (significance1) {
          sign0 = coefficentsSign[index - 1];
          contribution = 1 - sign0 - sign0;
        } else {
          contribution = 0;
        }
        var horizontalContribution = 3 * contribution;
        significance1 = row > 0 && coefficentsMagnitude[index - width] !== 0;
        if (row + 1 < height && coefficentsMagnitude[index + width] !== 0) {
          sign1 = coefficentsSign[index + width];
          if (significance1) {
            sign0 = coefficentsSign[index - width];
            contribution = 1 - sign1 - sign0 + horizontalContribution;
          } else {
            contribution = 1 - sign1 - sign1 + horizontalContribution;
          }
        } else if (significance1) {
          sign0 = coefficentsSign[index - width];
          contribution = 1 - sign0 - sign0 + horizontalContribution;
        } else {
          contribution = horizontalContribution;
        }
        if (contribution >= 0) {
          contextLabel = 9 + contribution;
          decoded = this.decoder.readBit(this.contexts, contextLabel);
        } else {
          contextLabel = 9 - contribution;
          decoded = this.decoder.readBit(this.contexts, contextLabel) ^ 1;
        }
        return decoded;
      },
      runMagnitudeRefinementPass: function BitModel_runMagnitudeRefinementPass() {
        var decoder = this.decoder;
        var width = this.width,
            height = this.height;
        var coefficentsMagnitude = this.coefficentsMagnitude;
        var neighborsSignificance = this.neighborsSignificance;
        var contexts = this.contexts;
        var bitsDecoded = this.bitsDecoded;
        var processingFlags = this.processingFlags;
        var processedMask = 1;
        var firstMagnitudeBitMask = 2;
        var length = width * height;
        var width4 = width * 4;
        for (var index0 = 0, indexNext; index0 < length; index0 = indexNext) {
          indexNext = Math.min(length, index0 + width4);
          for (var j = 0; j < width; j++) {
            for (var index = index0 + j; index < indexNext; index += width) {
              if (!coefficentsMagnitude[index] || (processingFlags[index] & processedMask) !== 0) {
                continue;
              }
              var contextLabel = 16;
              if ((processingFlags[index] & firstMagnitudeBitMask) !== 0) {
                processingFlags[index] ^= firstMagnitudeBitMask;
                var significance = neighborsSignificance[index] & 127;
                contextLabel = significance === 0 ? 15 : 14;
              }
              var bit = decoder.readBit(contexts, contextLabel);
              coefficentsMagnitude[index] = coefficentsMagnitude[index] << 1 | bit;
              bitsDecoded[index]++;
              processingFlags[index] |= processedMask;
            }
          }
        }
      },
      runCleanupPass: function BitModel_runCleanupPass() {
        var decoder = this.decoder;
        var width = this.width,
            height = this.height;
        var neighborsSignificance = this.neighborsSignificance;
        var coefficentsMagnitude = this.coefficentsMagnitude;
        var coefficentsSign = this.coefficentsSign;
        var contexts = this.contexts;
        var labels = this.contextLabelTable;
        var bitsDecoded = this.bitsDecoded;
        var processingFlags = this.processingFlags;
        var processedMask = 1;
        var firstMagnitudeBitMask = 2;
        var oneRowDown = width;
        var twoRowsDown = width * 2;
        var threeRowsDown = width * 3;
        var iNext;
        for (var i0 = 0; i0 < height; i0 = iNext) {
          iNext = Math.min(i0 + 4, height);
          var indexBase = i0 * width;
          var checkAllEmpty = i0 + 3 < height;
          for (var j = 0; j < width; j++) {
            var index0 = indexBase + j;
            var allEmpty = checkAllEmpty && processingFlags[index0] === 0 && processingFlags[index0 + oneRowDown] === 0 && processingFlags[index0 + twoRowsDown] === 0 && processingFlags[index0 + threeRowsDown] === 0 && neighborsSignificance[index0] === 0 && neighborsSignificance[index0 + oneRowDown] === 0 && neighborsSignificance[index0 + twoRowsDown] === 0 && neighborsSignificance[index0 + threeRowsDown] === 0;
            var i1 = 0,
                index = index0;
            var i = i0,
                sign;
            if (allEmpty) {
              var hasSignificantCoefficent = decoder.readBit(contexts, RUNLENGTH_CONTEXT);
              if (!hasSignificantCoefficent) {
                bitsDecoded[index0]++;
                bitsDecoded[index0 + oneRowDown]++;
                bitsDecoded[index0 + twoRowsDown]++;
                bitsDecoded[index0 + threeRowsDown]++;
                continue;
              }
              i1 = decoder.readBit(contexts, UNIFORM_CONTEXT) << 1 | decoder.readBit(contexts, UNIFORM_CONTEXT);
              if (i1 !== 0) {
                i = i0 + i1;
                index += i1 * width;
              }
              sign = this.decodeSignBit(i, j, index);
              coefficentsSign[index] = sign;
              coefficentsMagnitude[index] = 1;
              this.setNeighborsSignificance(i, j, index);
              processingFlags[index] |= firstMagnitudeBitMask;
              index = index0;
              for (var i2 = i0; i2 <= i; i2++, index += width) {
                bitsDecoded[index]++;
              }
              i1++;
            }
            for (i = i0 + i1; i < iNext; i++, index += width) {
              if (coefficentsMagnitude[index] || (processingFlags[index] & processedMask) !== 0) {
                continue;
              }
              var contextLabel = labels[neighborsSignificance[index]];
              var decision = decoder.readBit(contexts, contextLabel);
              if (decision === 1) {
                sign = this.decodeSignBit(i, j, index);
                coefficentsSign[index] = sign;
                coefficentsMagnitude[index] = 1;
                this.setNeighborsSignificance(i, j, index);
                processingFlags[index] |= firstMagnitudeBitMask;
              }
              bitsDecoded[index]++;
            }
          }
        }
      },
      checkSegmentationSymbol: function BitModel_checkSegmentationSymbol() {
        var decoder = this.decoder;
        var contexts = this.contexts;
        var symbol = decoder.readBit(contexts, UNIFORM_CONTEXT) << 3 | decoder.readBit(contexts, UNIFORM_CONTEXT) << 2 | decoder.readBit(contexts, UNIFORM_CONTEXT) << 1 | decoder.readBit(contexts, UNIFORM_CONTEXT);
        if (symbol !== 0xA) {
          throw new JpxError('Invalid segmentation symbol');
        }
      }
    };
    return BitModel;
  }();
  var Transform = function TransformClosure() {
    function Transform() {}
    Transform.prototype.calculate = function transformCalculate(subbands, u0, v0) {
      var ll = subbands[0];
      for (var i = 1, ii = subbands.length; i < ii; i++) {
        ll = this.iterate(ll, subbands[i], u0, v0);
      }
      return ll;
    };
    Transform.prototype.extend = function extend(buffer, offset, size) {
      var i1 = offset - 1,
          j1 = offset + 1;
      var i2 = offset + size - 2,
          j2 = offset + size;
      buffer[i1--] = buffer[j1++];
      buffer[j2++] = buffer[i2--];
      buffer[i1--] = buffer[j1++];
      buffer[j2++] = buffer[i2--];
      buffer[i1--] = buffer[j1++];
      buffer[j2++] = buffer[i2--];
      buffer[i1] = buffer[j1];
      buffer[j2] = buffer[i2];
    };
    Transform.prototype.iterate = function Transform_iterate(ll, hl_lh_hh, u0, v0) {
      var llWidth = ll.width,
          llHeight = ll.height,
          llItems = ll.items;
      var width = hl_lh_hh.width;
      var height = hl_lh_hh.height;
      var items = hl_lh_hh.items;
      var i, j, k, l, u, v;
      for (k = 0, i = 0; i < llHeight; i++) {
        l = i * 2 * width;
        for (j = 0; j < llWidth; j++, k++, l += 2) {
          items[l] = llItems[k];
        }
      }
      llItems = ll.items = null;
      var bufferPadding = 4;
      var rowBuffer = new Float32Array(width + 2 * bufferPadding);
      if (width === 1) {
        if ((u0 & 1) !== 0) {
          for (v = 0, k = 0; v < height; v++, k += width) {
            items[k] *= 0.5;
          }
        }
      } else {
        for (v = 0, k = 0; v < height; v++, k += width) {
          rowBuffer.set(items.subarray(k, k + width), bufferPadding);
          this.extend(rowBuffer, bufferPadding, width);
          this.filter(rowBuffer, bufferPadding, width);
          items.set(rowBuffer.subarray(bufferPadding, bufferPadding + width), k);
        }
      }
      var numBuffers = 16;
      var colBuffers = [];
      for (i = 0; i < numBuffers; i++) {
        colBuffers.push(new Float32Array(height + 2 * bufferPadding));
      }
      var b,
          currentBuffer = 0;
      ll = bufferPadding + height;
      if (height === 1) {
        if ((v0 & 1) !== 0) {
          for (u = 0; u < width; u++) {
            items[u] *= 0.5;
          }
        }
      } else {
        for (u = 0; u < width; u++) {
          if (currentBuffer === 0) {
            numBuffers = Math.min(width - u, numBuffers);
            for (k = u, l = bufferPadding; l < ll; k += width, l++) {
              for (b = 0; b < numBuffers; b++) {
                colBuffers[b][l] = items[k + b];
              }
            }
            currentBuffer = numBuffers;
          }
          currentBuffer--;
          var buffer = colBuffers[currentBuffer];
          this.extend(buffer, bufferPadding, height);
          this.filter(buffer, bufferPadding, height);
          if (currentBuffer === 0) {
            k = u - numBuffers + 1;
            for (l = bufferPadding; l < ll; k += width, l++) {
              for (b = 0; b < numBuffers; b++) {
                items[k + b] = colBuffers[b][l];
              }
            }
          }
        }
      }
      return {
        width,
        height,
        items
      };
    };
    return Transform;
  }();
  var IrreversibleTransform = function IrreversibleTransformClosure() {
    function IrreversibleTransform() {
      Transform.call(this);
    }
    IrreversibleTransform.prototype = Object.create(Transform.prototype);
    IrreversibleTransform.prototype.filter = function irreversibleTransformFilter(x, offset, length) {
      var len = length >> 1;
      offset = offset | 0;
      var j, n, current, next;
      var alpha = -1.586134342059924;
      var beta = -0.052980118572961;
      var gamma = 0.882911075530934;
      var delta = 0.443506852043971;
      var K = 1.230174104914001;
      var K_ = 1 / K;
      j = offset - 3;
      for (n = len + 4; n--; j += 2) {
        x[j] *= K_;
      }
      j = offset - 2;
      current = delta * x[j - 1];
      for (n = len + 3; n--; j += 2) {
        next = delta * x[j + 1];
        x[j] = K * x[j] - current - next;
        if (n--) {
          j += 2;
          current = delta * x[j + 1];
          x[j] = K * x[j] - current - next;
        } else {
          break;
        }
      }
      j = offset - 1;
      current = gamma * x[j - 1];
      for (n = len + 2; n--; j += 2) {
        next = gamma * x[j + 1];
        x[j] -= current + next;
        if (n--) {
          j += 2;
          current = gamma * x[j + 1];
          x[j] -= current + next;
        } else {
          break;
        }
      }
      j = offset;
      current = beta * x[j - 1];
      for (n = len + 1; n--; j += 2) {
        next = beta * x[j + 1];
        x[j] -= current + next;
        if (n--) {
          j += 2;
          current = beta * x[j + 1];
          x[j] -= current + next;
        } else {
          break;
        }
      }
      if (len !== 0) {
        j = offset + 1;
        current = alpha * x[j - 1];
        for (n = len; n--; j += 2) {
          next = alpha * x[j + 1];
          x[j] -= current + next;
          if (n--) {
            j += 2;
            current = alpha * x[j + 1];
            x[j] -= current + next;
          } else {
            break;
          }
        }
      }
    };
    return IrreversibleTransform;
  }();
  var ReversibleTransform = function ReversibleTransformClosure() {
    function ReversibleTransform() {
      Transform.call(this);
    }
    ReversibleTransform.prototype = Object.create(Transform.prototype);
    ReversibleTransform.prototype.filter = function reversibleTransformFilter(x, offset, length) {
      var len = length >> 1;
      offset = offset | 0;
      var j, n;
      for (j = offset, n = len + 1; n--; j += 2) {
        x[j] -= x[j - 1] + x[j + 1] + 2 >> 2;
      }
      for (j = offset + 1, n = len; n--; j += 2) {
        x[j] += x[j - 1] + x[j + 1] >> 1;
      }
    };
    return ReversibleTransform;
  }();
  return JpxImage;
}();
exports.JpxImage = JpxImage;

/***/ }),
/* 14 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.calculateSHA512 = exports.calculateSHA384 = exports.calculateSHA256 = exports.calculateMD5 = exports.PDF20 = exports.PDF17 = exports.CipherTransformFactory = exports.ARCFourCipher = exports.AES256Cipher = exports.AES128Cipher = undefined;

var _util = __w_pdfjs_require__(0);

var _primitives = __w_pdfjs_require__(1);

var _stream = __w_pdfjs_require__(2);

var ARCFourCipher = function ARCFourCipherClosure() {
  function ARCFourCipher(key) {
    this.a = 0;
    this.b = 0;
    var s = new Uint8Array(256);
    var i,
        j = 0,
        tmp,
        keyLength = key.length;
    for (i = 0; i < 256; ++i) {
      s[i] = i;
    }
    for (i = 0; i < 256; ++i) {
      tmp = s[i];
      j = j + tmp + key[i % keyLength] & 0xFF;
      s[i] = s[j];
      s[j] = tmp;
    }
    this.s = s;
  }
  ARCFourCipher.prototype = {
    encryptBlock: function ARCFourCipher_encryptBlock(data) {
      var i,
          n = data.length,
          tmp,
          tmp2;
      var a = this.a,
          b = this.b,
          s = this.s;
      var output = new Uint8Array(n);
      for (i = 0; i < n; ++i) {
        a = a + 1 & 0xFF;
        tmp = s[a];
        b = b + tmp & 0xFF;
        tmp2 = s[b];
        s[a] = tmp2;
        s[b] = tmp;
        output[i] = data[i] ^ s[tmp + tmp2 & 0xFF];
      }
      this.a = a;
      this.b = b;
      return output;
    }
  };
  ARCFourCipher.prototype.decryptBlock = ARCFourCipher.prototype.encryptBlock;
  return ARCFourCipher;
}();
var calculateMD5 = function calculateMD5Closure() {
  var r = new Uint8Array([7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21]);
  var k = new Int32Array([-680876936, -389564586, 606105819, -1044525330, -176418897, 1200080426, -1473231341, -45705983, 1770035416, -1958414417, -42063, -1990404162, 1804603682, -40341101, -1502002290, 1236535329, -165796510, -1069501632, 643717713, -373897302, -701558691, 38016083, -660478335, -405537848, 568446438, -1019803690, -187363961, 1163531501, -1444681467, -51403784, 1735328473, -1926607734, -378558, -2022574463, 1839030562, -35309556, -1530992060, 1272893353, -155497632, -1094730640, 681279174, -358537222, -722521979, 76029189, -640364487, -421815835, 530742520, -995338651, -198630844, 1126891415, -1416354905, -57434055, 1700485571, -1894986606, -1051523, -2054922799, 1873313359, -30611744, -1560198380, 1309151649, -145523070, -1120210379, 718787259, -343485551]);
  function hash(data, offset, length) {
    var h0 = 1732584193,
        h1 = -271733879,
        h2 = -1732584194,
        h3 = 271733878;
    var paddedLength = length + 72 & ~63;
    var padded = new Uint8Array(paddedLength);
    var i, j, n;
    for (i = 0; i < length; ++i) {
      padded[i] = data[offset++];
    }
    padded[i++] = 0x80;
    n = paddedLength - 8;
    while (i < n) {
      padded[i++] = 0;
    }
    padded[i++] = length << 3 & 0xFF;
    padded[i++] = length >> 5 & 0xFF;
    padded[i++] = length >> 13 & 0xFF;
    padded[i++] = length >> 21 & 0xFF;
    padded[i++] = length >>> 29 & 0xFF;
    padded[i++] = 0;
    padded[i++] = 0;
    padded[i++] = 0;
    var w = new Int32Array(16);
    for (i = 0; i < paddedLength;) {
      for (j = 0; j < 16; ++j, i += 4) {
        w[j] = padded[i] | padded[i + 1] << 8 | padded[i + 2] << 16 | padded[i + 3] << 24;
      }
      var a = h0,
          b = h1,
          c = h2,
          d = h3,
          f,
          g;
      for (j = 0; j < 64; ++j) {
        if (j < 16) {
          f = b & c | ~b & d;
          g = j;
        } else if (j < 32) {
          f = d & b | ~d & c;
          g = 5 * j + 1 & 15;
        } else if (j < 48) {
          f = b ^ c ^ d;
          g = 3 * j + 5 & 15;
        } else {
          f = c ^ (b | ~d);
          g = 7 * j & 15;
        }
        var tmp = d,
            rotateArg = a + f + k[j] + w[g] | 0,
            rotate = r[j];
        d = c;
        c = b;
        b = b + (rotateArg << rotate | rotateArg >>> 32 - rotate) | 0;
        a = tmp;
      }
      h0 = h0 + a | 0;
      h1 = h1 + b | 0;
      h2 = h2 + c | 0;
      h3 = h3 + d | 0;
    }
    return new Uint8Array([h0 & 0xFF, h0 >> 8 & 0xFF, h0 >> 16 & 0xFF, h0 >>> 24 & 0xFF, h1 & 0xFF, h1 >> 8 & 0xFF, h1 >> 16 & 0xFF, h1 >>> 24 & 0xFF, h2 & 0xFF, h2 >> 8 & 0xFF, h2 >> 16 & 0xFF, h2 >>> 24 & 0xFF, h3 & 0xFF, h3 >> 8 & 0xFF, h3 >> 16 & 0xFF, h3 >>> 24 & 0xFF]);
  }
  return hash;
}();
var Word64 = function Word64Closure() {
  function Word64(highInteger, lowInteger) {
    this.high = highInteger | 0;
    this.low = lowInteger | 0;
  }
  Word64.prototype = {
    and: function Word64_and(word) {
      this.high &= word.high;
      this.low &= word.low;
    },
    xor: function Word64_xor(word) {
      this.high ^= word.high;
      this.low ^= word.low;
    },
    or: function Word64_or(word) {
      this.high |= word.high;
      this.low |= word.low;
    },
    shiftRight: function Word64_shiftRight(places) {
      if (places >= 32) {
        this.low = this.high >>> places - 32 | 0;
        this.high = 0;
      } else {
        this.low = this.low >>> places | this.high << 32 - places;
        this.high = this.high >>> places | 0;
      }
    },
    shiftLeft: function Word64_shiftLeft(places) {
      if (places >= 32) {
        this.high = this.low << places - 32;
        this.low = 0;
      } else {
        this.high = this.high << places | this.low >>> 32 - places;
        this.low = this.low << places;
      }
    },
    rotateRight: function Word64_rotateRight(places) {
      var low, high;
      if (places & 32) {
        high = this.low;
        low = this.high;
      } else {
        low = this.low;
        high = this.high;
      }
      places &= 31;
      this.low = low >>> places | high << 32 - places;
      this.high = high >>> places | low << 32 - places;
    },
    not: function Word64_not() {
      this.high = ~this.high;
      this.low = ~this.low;
    },
    add: function Word64_add(word) {
      var lowAdd = (this.low >>> 0) + (word.low >>> 0);
      var highAdd = (this.high >>> 0) + (word.high >>> 0);
      if (lowAdd > 0xFFFFFFFF) {
        highAdd += 1;
      }
      this.low = lowAdd | 0;
      this.high = highAdd | 0;
    },
    copyTo: function Word64_copyTo(bytes, offset) {
      bytes[offset] = this.high >>> 24 & 0xFF;
      bytes[offset + 1] = this.high >> 16 & 0xFF;
      bytes[offset + 2] = this.high >> 8 & 0xFF;
      bytes[offset + 3] = this.high & 0xFF;
      bytes[offset + 4] = this.low >>> 24 & 0xFF;
      bytes[offset + 5] = this.low >> 16 & 0xFF;
      bytes[offset + 6] = this.low >> 8 & 0xFF;
      bytes[offset + 7] = this.low & 0xFF;
    },
    assign: function Word64_assign(word) {
      this.high = word.high;
      this.low = word.low;
    }
  };
  return Word64;
}();
var calculateSHA256 = function calculateSHA256Closure() {
  function rotr(x, n) {
    return x >>> n | x << 32 - n;
  }
  function ch(x, y, z) {
    return x & y ^ ~x & z;
  }
  function maj(x, y, z) {
    return x & y ^ x & z ^ y & z;
  }
  function sigma(x) {
    return rotr(x, 2) ^ rotr(x, 13) ^ rotr(x, 22);
  }
  function sigmaPrime(x) {
    return rotr(x, 6) ^ rotr(x, 11) ^ rotr(x, 25);
  }
  function littleSigma(x) {
    return rotr(x, 7) ^ rotr(x, 18) ^ x >>> 3;
  }
  function littleSigmaPrime(x) {
    return rotr(x, 17) ^ rotr(x, 19) ^ x >>> 10;
  }
  var k = [0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2];
  function hash(data, offset, length) {
    var h0 = 0x6a09e667,
        h1 = 0xbb67ae85,
        h2 = 0x3c6ef372,
        h3 = 0xa54ff53a,
        h4 = 0x510e527f,
        h5 = 0x9b05688c,
        h6 = 0x1f83d9ab,
        h7 = 0x5be0cd19;
    var paddedLength = Math.ceil((length + 9) / 64) * 64;
    var padded = new Uint8Array(paddedLength);
    var i, j, n;
    for (i = 0; i < length; ++i) {
      padded[i] = data[offset++];
    }
    padded[i++] = 0x80;
    n = paddedLength - 8;
    while (i < n) {
      padded[i++] = 0;
    }
    padded[i++] = 0;
    padded[i++] = 0;
    padded[i++] = 0;
    padded[i++] = length >>> 29 & 0xFF;
    padded[i++] = length >> 21 & 0xFF;
    padded[i++] = length >> 13 & 0xFF;
    padded[i++] = length >> 5 & 0xFF;
    padded[i++] = length << 3 & 0xFF;
    var w = new Uint32Array(64);
    for (i = 0; i < paddedLength;) {
      for (j = 0; j < 16; ++j) {
        w[j] = padded[i] << 24 | padded[i + 1] << 16 | padded[i + 2] << 8 | padded[i + 3];
        i += 4;
      }
      for (j = 16; j < 64; ++j) {
        w[j] = littleSigmaPrime(w[j - 2]) + w[j - 7] + littleSigma(w[j - 15]) + w[j - 16] | 0;
      }
      var a = h0,
          b = h1,
          c = h2,
          d = h3,
          e = h4,
          f = h5,
          g = h6,
          h = h7,
          t1,
          t2;
      for (j = 0; j < 64; ++j) {
        t1 = h + sigmaPrime(e) + ch(e, f, g) + k[j] + w[j];
        t2 = sigma(a) + maj(a, b, c);
        h = g;
        g = f;
        f = e;
        e = d + t1 | 0;
        d = c;
        c = b;
        b = a;
        a = t1 + t2 | 0;
      }
      h0 = h0 + a | 0;
      h1 = h1 + b | 0;
      h2 = h2 + c | 0;
      h3 = h3 + d | 0;
      h4 = h4 + e | 0;
      h5 = h5 + f | 0;
      h6 = h6 + g | 0;
      h7 = h7 + h | 0;
    }
    return new Uint8Array([h0 >> 24 & 0xFF, h0 >> 16 & 0xFF, h0 >> 8 & 0xFF, h0 & 0xFF, h1 >> 24 & 0xFF, h1 >> 16 & 0xFF, h1 >> 8 & 0xFF, h1 & 0xFF, h2 >> 24 & 0xFF, h2 >> 16 & 0xFF, h2 >> 8 & 0xFF, h2 & 0xFF, h3 >> 24 & 0xFF, h3 >> 16 & 0xFF, h3 >> 8 & 0xFF, h3 & 0xFF, h4 >> 24 & 0xFF, h4 >> 16 & 0xFF, h4 >> 8 & 0xFF, h4 & 0xFF, h5 >> 24 & 0xFF, h5 >> 16 & 0xFF, h5 >> 8 & 0xFF, h5 & 0xFF, h6 >> 24 & 0xFF, h6 >> 16 & 0xFF, h6 >> 8 & 0xFF, h6 & 0xFF, h7 >> 24 & 0xFF, h7 >> 16 & 0xFF, h7 >> 8 & 0xFF, h7 & 0xFF]);
  }
  return hash;
}();
var calculateSHA512 = function calculateSHA512Closure() {
  function ch(result, x, y, z, tmp) {
    result.assign(x);
    result.and(y);
    tmp.assign(x);
    tmp.not();
    tmp.and(z);
    result.xor(tmp);
  }
  function maj(result, x, y, z, tmp) {
    result.assign(x);
    result.and(y);
    tmp.assign(x);
    tmp.and(z);
    result.xor(tmp);
    tmp.assign(y);
    tmp.and(z);
    result.xor(tmp);
  }
  function sigma(result, x, tmp) {
    result.assign(x);
    result.rotateRight(28);
    tmp.assign(x);
    tmp.rotateRight(34);
    result.xor(tmp);
    tmp.assign(x);
    tmp.rotateRight(39);
    result.xor(tmp);
  }
  function sigmaPrime(result, x, tmp) {
    result.assign(x);
    result.rotateRight(14);
    tmp.assign(x);
    tmp.rotateRight(18);
    result.xor(tmp);
    tmp.assign(x);
    tmp.rotateRight(41);
    result.xor(tmp);
  }
  function littleSigma(result, x, tmp) {
    result.assign(x);
    result.rotateRight(1);
    tmp.assign(x);
    tmp.rotateRight(8);
    result.xor(tmp);
    tmp.assign(x);
    tmp.shiftRight(7);
    result.xor(tmp);
  }
  function littleSigmaPrime(result, x, tmp) {
    result.assign(x);
    result.rotateRight(19);
    tmp.assign(x);
    tmp.rotateRight(61);
    result.xor(tmp);
    tmp.assign(x);
    tmp.shiftRight(6);
    result.xor(tmp);
  }
  var k = [new Word64(0x428a2f98, 0xd728ae22), new Word64(0x71374491, 0x23ef65cd), new Word64(0xb5c0fbcf, 0xec4d3b2f), new Word64(0xe9b5dba5, 0x8189dbbc), new Word64(0x3956c25b, 0xf348b538), new Word64(0x59f111f1, 0xb605d019), new Word64(0x923f82a4, 0xaf194f9b), new Word64(0xab1c5ed5, 0xda6d8118), new Word64(0xd807aa98, 0xa3030242), new Word64(0x12835b01, 0x45706fbe), new Word64(0x243185be, 0x4ee4b28c), new Word64(0x550c7dc3, 0xd5ffb4e2), new Word64(0x72be5d74, 0xf27b896f), new Word64(0x80deb1fe, 0x3b1696b1), new Word64(0x9bdc06a7, 0x25c71235), new Word64(0xc19bf174, 0xcf692694), new Word64(0xe49b69c1, 0x9ef14ad2), new Word64(0xefbe4786, 0x384f25e3), new Word64(0x0fc19dc6, 0x8b8cd5b5), new Word64(0x240ca1cc, 0x77ac9c65), new Word64(0x2de92c6f, 0x592b0275), new Word64(0x4a7484aa, 0x6ea6e483), new Word64(0x5cb0a9dc, 0xbd41fbd4), new Word64(0x76f988da, 0x831153b5), new Word64(0x983e5152, 0xee66dfab), new Word64(0xa831c66d, 0x2db43210), new Word64(0xb00327c8, 0x98fb213f), new Word64(0xbf597fc7, 0xbeef0ee4), new Word64(0xc6e00bf3, 0x3da88fc2), new Word64(0xd5a79147, 0x930aa725), new Word64(0x06ca6351, 0xe003826f), new Word64(0x14292967, 0x0a0e6e70), new Word64(0x27b70a85, 0x46d22ffc), new Word64(0x2e1b2138, 0x5c26c926), new Word64(0x4d2c6dfc, 0x5ac42aed), new Word64(0x53380d13, 0x9d95b3df), new Word64(0x650a7354, 0x8baf63de), new Word64(0x766a0abb, 0x3c77b2a8), new Word64(0x81c2c92e, 0x47edaee6), new Word64(0x92722c85, 0x1482353b), new Word64(0xa2bfe8a1, 0x4cf10364), new Word64(0xa81a664b, 0xbc423001), new Word64(0xc24b8b70, 0xd0f89791), new Word64(0xc76c51a3, 0x0654be30), new Word64(0xd192e819, 0xd6ef5218), new Word64(0xd6990624, 0x5565a910), new Word64(0xf40e3585, 0x5771202a), new Word64(0x106aa070, 0x32bbd1b8), new Word64(0x19a4c116, 0xb8d2d0c8), new Word64(0x1e376c08, 0x5141ab53), new Word64(0x2748774c, 0xdf8eeb99), new Word64(0x34b0bcb5, 0xe19b48a8), new Word64(0x391c0cb3, 0xc5c95a63), new Word64(0x4ed8aa4a, 0xe3418acb), new Word64(0x5b9cca4f, 0x7763e373), new Word64(0x682e6ff3, 0xd6b2b8a3), new Word64(0x748f82ee, 0x5defb2fc), new Word64(0x78a5636f, 0x43172f60), new Word64(0x84c87814, 0xa1f0ab72), new Word64(0x8cc70208, 0x1a6439ec), new Word64(0x90befffa, 0x23631e28), new Word64(0xa4506ceb, 0xde82bde9), new Word64(0xbef9a3f7, 0xb2c67915), new Word64(0xc67178f2, 0xe372532b), new Word64(0xca273ece, 0xea26619c), new Word64(0xd186b8c7, 0x21c0c207), new Word64(0xeada7dd6, 0xcde0eb1e), new Word64(0xf57d4f7f, 0xee6ed178), new Word64(0x06f067aa, 0x72176fba), new Word64(0x0a637dc5, 0xa2c898a6), new Word64(0x113f9804, 0xbef90dae), new Word64(0x1b710b35, 0x131c471b), new Word64(0x28db77f5, 0x23047d84), new Word64(0x32caab7b, 0x40c72493), new Word64(0x3c9ebe0a, 0x15c9bebc), new Word64(0x431d67c4, 0x9c100d4c), new Word64(0x4cc5d4be, 0xcb3e42b6), new Word64(0x597f299c, 0xfc657e2a), new Word64(0x5fcb6fab, 0x3ad6faec), new Word64(0x6c44198c, 0x4a475817)];
  function hash(data, offset, length, mode384) {
    mode384 = !!mode384;
    var h0, h1, h2, h3, h4, h5, h6, h7;
    if (!mode384) {
      h0 = new Word64(0x6a09e667, 0xf3bcc908);
      h1 = new Word64(0xbb67ae85, 0x84caa73b);
      h2 = new Word64(0x3c6ef372, 0xfe94f82b);
      h3 = new Word64(0xa54ff53a, 0x5f1d36f1);
      h4 = new Word64(0x510e527f, 0xade682d1);
      h5 = new Word64(0x9b05688c, 0x2b3e6c1f);
      h6 = new Word64(0x1f83d9ab, 0xfb41bd6b);
      h7 = new Word64(0x5be0cd19, 0x137e2179);
    } else {
      h0 = new Word64(0xcbbb9d5d, 0xc1059ed8);
      h1 = new Word64(0x629a292a, 0x367cd507);
      h2 = new Word64(0x9159015a, 0x3070dd17);
      h3 = new Word64(0x152fecd8, 0xf70e5939);
      h4 = new Word64(0x67332667, 0xffc00b31);
      h5 = new Word64(0x8eb44a87, 0x68581511);
      h6 = new Word64(0xdb0c2e0d, 0x64f98fa7);
      h7 = new Word64(0x47b5481d, 0xbefa4fa4);
    }
    var paddedLength = Math.ceil((length + 17) / 128) * 128;
    var padded = new Uint8Array(paddedLength);
    var i, j, n;
    for (i = 0; i < length; ++i) {
      padded[i] = data[offset++];
    }
    padded[i++] = 0x80;
    n = paddedLength - 16;
    while (i < n) {
      padded[i++] = 0;
    }
    padded[i++] = 0;
    padded[i++] = 0;
    padded[i++] = 0;
    padded[i++] = 0;
    padded[i++] = 0;
    padded[i++] = 0;
    padded[i++] = 0;
    padded[i++] = 0;
    padded[i++] = 0;
    padded[i++] = 0;
    padded[i++] = 0;
    padded[i++] = length >>> 29 & 0xFF;
    padded[i++] = length >> 21 & 0xFF;
    padded[i++] = length >> 13 & 0xFF;
    padded[i++] = length >> 5 & 0xFF;
    padded[i++] = length << 3 & 0xFF;
    var w = new Array(80);
    for (i = 0; i < 80; i++) {
      w[i] = new Word64(0, 0);
    }
    var a = new Word64(0, 0),
        b = new Word64(0, 0),
        c = new Word64(0, 0);
    var d = new Word64(0, 0),
        e = new Word64(0, 0),
        f = new Word64(0, 0);
    var g = new Word64(0, 0),
        h = new Word64(0, 0);
    var t1 = new Word64(0, 0),
        t2 = new Word64(0, 0);
    var tmp1 = new Word64(0, 0),
        tmp2 = new Word64(0, 0),
        tmp3;
    for (i = 0; i < paddedLength;) {
      for (j = 0; j < 16; ++j) {
        w[j].high = padded[i] << 24 | padded[i + 1] << 16 | padded[i + 2] << 8 | padded[i + 3];
        w[j].low = padded[i + 4] << 24 | padded[i + 5] << 16 | padded[i + 6] << 8 | padded[i + 7];
        i += 8;
      }
      for (j = 16; j < 80; ++j) {
        tmp3 = w[j];
        littleSigmaPrime(tmp3, w[j - 2], tmp2);
        tmp3.add(w[j - 7]);
        littleSigma(tmp1, w[j - 15], tmp2);
        tmp3.add(tmp1);
        tmp3.add(w[j - 16]);
      }
      a.assign(h0);
      b.assign(h1);
      c.assign(h2);
      d.assign(h3);
      e.assign(h4);
      f.assign(h5);
      g.assign(h6);
      h.assign(h7);
      for (j = 0; j < 80; ++j) {
        t1.assign(h);
        sigmaPrime(tmp1, e, tmp2);
        t1.add(tmp1);
        ch(tmp1, e, f, g, tmp2);
        t1.add(tmp1);
        t1.add(k[j]);
        t1.add(w[j]);
        sigma(t2, a, tmp2);
        maj(tmp1, a, b, c, tmp2);
        t2.add(tmp1);
        tmp3 = h;
        h = g;
        g = f;
        f = e;
        d.add(t1);
        e = d;
        d = c;
        c = b;
        b = a;
        tmp3.assign(t1);
        tmp3.add(t2);
        a = tmp3;
      }
      h0.add(a);
      h1.add(b);
      h2.add(c);
      h3.add(d);
      h4.add(e);
      h5.add(f);
      h6.add(g);
      h7.add(h);
    }
    var result;
    if (!mode384) {
      result = new Uint8Array(64);
      h0.copyTo(result, 0);
      h1.copyTo(result, 8);
      h2.copyTo(result, 16);
      h3.copyTo(result, 24);
      h4.copyTo(result, 32);
      h5.copyTo(result, 40);
      h6.copyTo(result, 48);
      h7.copyTo(result, 56);
    } else {
      result = new Uint8Array(48);
      h0.copyTo(result, 0);
      h1.copyTo(result, 8);
      h2.copyTo(result, 16);
      h3.copyTo(result, 24);
      h4.copyTo(result, 32);
      h5.copyTo(result, 40);
    }
    return result;
  }
  return hash;
}();
var calculateSHA384 = function calculateSHA384Closure() {
  function hash(data, offset, length) {
    return calculateSHA512(data, offset, length, true);
  }
  return hash;
}();
var NullCipher = function NullCipherClosure() {
  function NullCipher() {}
  NullCipher.prototype = {
    decryptBlock: function NullCipher_decryptBlock(data) {
      return data;
    }
  };
  return NullCipher;
}();
class AESBaseCipher {
  constructor() {
    if (this.constructor === AESBaseCipher) {
      (0, _util.unreachable)('Cannot initialize AESBaseCipher.');
    }
    this._s = new Uint8Array([0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16]);
    this._inv_s = new Uint8Array([0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d]);
    this._mix = new Uint32Array([0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927, 0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45, 0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb, 0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381, 0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf, 0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66, 0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0xab73d323, 0xa57ade28, 0xb761c935, 0xb968c43e, 0x9357e70f, 0x9d5eea04, 0x8f45fd19, 0x814cf012, 0x3bab6bcb, 0x35a266c0, 0x27b971dd, 0x29b07cd6, 0x038f5fe7, 0x0d8652ec, 0x1f9d45f1, 0x119448fa, 0x4be30393, 0x45ea0e98, 0x57f11985, 0x59f8148e, 0x73c737bf, 0x7dce3ab4, 0x6fd52da9, 0x61dc20a2, 0xad766df6, 0xa37f60fd, 0xb16477e0, 0xbf6d7aeb, 0x955259da, 0x9b5b54d1, 0x894043cc, 0x87494ec7, 0xdd3e05ae, 0xd33708a5, 0xc12c1fb8, 0xcf2512b3, 0xe51a3182, 0xeb133c89, 0xf9082b94, 0xf701269f, 0x4de6bd46, 0x43efb04d, 0x51f4a750, 0x5ffdaa5b, 0x75c2896a, 0x7bcb8461, 0x69d0937c, 0x67d99e77, 0x3daed51e, 0x33a7d815, 0x21bccf08, 0x2fb5c203, 0x058ae132, 0x0b83ec39, 0x1998fb24, 0x1791f62f, 0x764dd68d, 0x7844db86, 0x6a5fcc9b, 0x6456c190, 0x4e69e2a1, 0x4060efaa, 0x527bf8b7, 0x5c72f5bc, 0x0605bed5, 0x080cb3de, 0x1a17a4c3, 0x141ea9c8, 0x3e218af9, 0x302887f2, 0x223390ef, 0x2c3a9de4, 0x96dd063d, 0x98d40b36, 0x8acf1c2b, 0x84c61120, 0xaef93211, 0xa0f03f1a, 0xb2eb2807, 0xbce2250c, 0xe6956e65, 0xe89c636e, 0xfa877473, 0xf48e7978, 0xdeb15a49, 0xd0b85742, 0xc2a3405f, 0xccaa4d54, 0x41ecdaf7, 0x4fe5d7fc, 0x5dfec0e1, 0x53f7cdea, 0x79c8eedb, 0x77c1e3d0, 0x65daf4cd, 0x6bd3f9c6, 0x31a4b2af, 0x3fadbfa4, 0x2db6a8b9, 0x23bfa5b2, 0x09808683, 0x07898b88, 0x15929c95, 0x1b9b919e, 0xa17c0a47, 0xaf75074c, 0xbd6e1051, 0xb3671d5a, 0x99583e6b, 0x97513360, 0x854a247d, 0x8b432976, 0xd134621f, 0xdf3d6f14, 0xcd267809, 0xc32f7502, 0xe9105633, 0xe7195b38, 0xf5024c25, 0xfb0b412e, 0x9ad7618c, 0x94de6c87, 0x86c57b9a, 0x88cc7691, 0xa2f355a0, 0xacfa58ab, 0xbee14fb6, 0xb0e842bd, 0xea9f09d4, 0xe49604df, 0xf68d13c2, 0xf8841ec9, 0xd2bb3df8, 0xdcb230f3, 0xcea927ee, 0xc0a02ae5, 0x7a47b13c, 0x744ebc37, 0x6655ab2a, 0x685ca621, 0x42638510, 0x4c6a881b, 0x5e719f06, 0x5078920d, 0x0a0fd964, 0x0406d46f, 0x161dc372, 0x1814ce79, 0x322bed48, 0x3c22e043, 0x2e39f75e, 0x2030fa55, 0xec9ab701, 0xe293ba0a, 0xf088ad17, 0xfe81a01c, 0xd4be832d, 0xdab78e26, 0xc8ac993b, 0xc6a59430, 0x9cd2df59, 0x92dbd252, 0x80c0c54f, 0x8ec9c844, 0xa4f6eb75, 0xaaffe67e, 0xb8e4f163, 0xb6edfc68, 0x0c0a67b1, 0x02036aba, 0x10187da7, 0x1e1170ac, 0x342e539d, 0x3a275e96, 0x283c498b, 0x26354480, 0x7c420fe9, 0x724b02e2, 0x605015ff, 0x6e5918f4, 0x44663bc5, 0x4a6f36ce, 0x587421d3, 0x567d2cd8, 0x37a10c7a, 0x39a80171, 0x2bb3166c, 0x25ba1b67, 0x0f853856, 0x018c355d, 0x13972240, 0x1d9e2f4b, 0x47e96422, 0x49e06929, 0x5bfb7e34, 0x55f2733f, 0x7fcd500e, 0x71c45d05, 0x63df4a18, 0x6dd64713, 0xd731dcca, 0xd938d1c1, 0xcb23c6dc, 0xc52acbd7, 0xef15e8e6, 0xe11ce5ed, 0xf307f2f0, 0xfd0efffb, 0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f, 0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3]);
    this._mixCol = new Uint8Array(256);
    for (let i = 0; i < 256; i++) {
      if (i < 128) {
        this._mixCol[i] = i << 1;
      } else {
        this._mixCol[i] = i << 1 ^ 0x1b;
      }
    }
    this.buffer = new Uint8Array(16);
    this.bufferPosition = 0;
  }
  _expandKey(cipherKey) {
    (0, _util.unreachable)('Cannot call `_expandKey` on the base class');
  }
  _decrypt(input, key) {
    let t, u, v;
    let state = new Uint8Array(16);
    state.set(input);
    for (let j = 0, k = this._keySize; j < 16; ++j, ++k) {
      state[j] ^= key[k];
    }
    for (let i = this._cyclesOfRepetition - 1; i >= 1; --i) {
      t = state[13];
      state[13] = state[9];
      state[9] = state[5];
      state[5] = state[1];
      state[1] = t;
      t = state[14];
      u = state[10];
      state[14] = state[6];
      state[10] = state[2];
      state[6] = t;
      state[2] = u;
      t = state[15];
      u = state[11];
      v = state[7];
      state[15] = state[3];
      state[11] = t;
      state[7] = u;
      state[3] = v;
      for (let j = 0; j < 16; ++j) {
        state[j] = this._inv_s[state[j]];
      }
      for (let j = 0, k = i * 16; j < 16; ++j, ++k) {
        state[j] ^= key[k];
      }
      for (let j = 0; j < 16; j += 4) {
        let s0 = this._mix[state[j]];
        let s1 = this._mix[state[j + 1]];
        let s2 = this._mix[state[j + 2]];
        let s3 = this._mix[state[j + 3]];
        t = s0 ^ s1 >>> 8 ^ s1 << 24 ^ s2 >>> 16 ^ s2 << 16 ^ s3 >>> 24 ^ s3 << 8;
        state[j] = t >>> 24 & 0xFF;
        state[j + 1] = t >> 16 & 0xFF;
        state[j + 2] = t >> 8 & 0xFF;
        state[j + 3] = t & 0xFF;
      }
    }
    t = state[13];
    state[13] = state[9];
    state[9] = state[5];
    state[5] = state[1];
    state[1] = t;
    t = state[14];
    u = state[10];
    state[14] = state[6];
    state[10] = state[2];
    state[6] = t;
    state[2] = u;
    t = state[15];
    u = state[11];
    v = state[7];
    state[15] = state[3];
    state[11] = t;
    state[7] = u;
    state[3] = v;
    for (let j = 0; j < 16; ++j) {
      state[j] = this._inv_s[state[j]];
      state[j] ^= key[j];
    }
    return state;
  }
  _encrypt(input, key) {
    const s = this._s;
    let t, u, v;
    let state = new Uint8Array(16);
    state.set(input);
    for (let j = 0; j < 16; ++j) {
      state[j] ^= key[j];
    }
    for (let i = 1; i < this._cyclesOfRepetition; i++) {
      for (let j = 0; j < 16; ++j) {
        state[j] = s[state[j]];
      }
      v = state[1];
      state[1] = state[5];
      state[5] = state[9];
      state[9] = state[13];
      state[13] = v;
      v = state[2];
      u = state[6];
      state[2] = state[10];
      state[6] = state[14];
      state[10] = v;
      state[14] = u;
      v = state[3];
      u = state[7];
      t = state[11];
      state[3] = state[15];
      state[7] = v;
      state[11] = u;
      state[15] = t;
      for (let j = 0; j < 16; j += 4) {
        let s0 = state[j + 0];
        let s1 = state[j + 1];
        let s2 = state[j + 2];
        let s3 = state[j + 3];
        t = s0 ^ s1 ^ s2 ^ s3;
        state[j + 0] ^= t ^ this._mixCol[s0 ^ s1];
        state[j + 1] ^= t ^ this._mixCol[s1 ^ s2];
        state[j + 2] ^= t ^ this._mixCol[s2 ^ s3];
        state[j + 3] ^= t ^ this._mixCol[s3 ^ s0];
      }
      for (let j = 0, k = i * 16; j < 16; ++j, ++k) {
        state[j] ^= key[k];
      }
    }
    for (let j = 0; j < 16; ++j) {
      state[j] = s[state[j]];
    }
    v = state[1];
    state[1] = state[5];
    state[5] = state[9];
    state[9] = state[13];
    state[13] = v;
    v = state[2];
    u = state[6];
    state[2] = state[10];
    state[6] = state[14];
    state[10] = v;
    state[14] = u;
    v = state[3];
    u = state[7];
    t = state[11];
    state[3] = state[15];
    state[7] = v;
    state[11] = u;
    state[15] = t;
    for (let j = 0, k = this._keySize; j < 16; ++j, ++k) {
      state[j] ^= key[k];
    }
    return state;
  }
  _decryptBlock2(data, finalize) {
    let sourceLength = data.length;
    let buffer = this.buffer,
        bufferLength = this.bufferPosition;
    let result = [],
        iv = this.iv;
    for (let i = 0; i < sourceLength; ++i) {
      buffer[bufferLength] = data[i];
      ++bufferLength;
      if (bufferLength < 16) {
        continue;
      }
      let plain = this._decrypt(buffer, this._key);
      for (let j = 0; j < 16; ++j) {
        plain[j] ^= iv[j];
      }
      iv = buffer;
      result.push(plain);
      buffer = new Uint8Array(16);
      bufferLength = 0;
    }
    this.buffer = buffer;
    this.bufferLength = bufferLength;
    this.iv = iv;
    if (result.length === 0) {
      return new Uint8Array(0);
    }
    let outputLength = 16 * result.length;
    if (finalize) {
      let lastBlock = result[result.length - 1];
      let psLen = lastBlock[15];
      if (psLen <= 16) {
        for (let i = 15, ii = 16 - psLen; i >= ii; --i) {
          if (lastBlock[i] !== psLen) {
            psLen = 0;
            break;
          }
        }
        outputLength -= psLen;
        result[result.length - 1] = lastBlock.subarray(0, 16 - psLen);
      }
    }
    let output = new Uint8Array(outputLength);
    for (let i = 0, j = 0, ii = result.length; i < ii; ++i, j += 16) {
      output.set(result[i], j);
    }
    return output;
  }
  decryptBlock(data, finalize, iv = null) {
    let sourceLength = data.length;
    let buffer = this.buffer,
        bufferLength = this.bufferPosition;
    if (iv) {
      this.iv = iv;
    } else {
      for (let i = 0; bufferLength < 16 && i < sourceLength; ++i, ++bufferLength) {
        buffer[bufferLength] = data[i];
      }
      if (bufferLength < 16) {
        this.bufferLength = bufferLength;
        return new Uint8Array(0);
      }
      this.iv = buffer;
      data = data.subarray(16);
    }
    this.buffer = new Uint8Array(16);
    this.bufferLength = 0;
    this.decryptBlock = this._decryptBlock2;
    return this.decryptBlock(data, finalize);
  }
  encrypt(data, iv) {
    let sourceLength = data.length;
    let buffer = this.buffer,
        bufferLength = this.bufferPosition;
    let result = [];
    if (!iv) {
      iv = new Uint8Array(16);
    }
    for (let i = 0; i < sourceLength; ++i) {
      buffer[bufferLength] = data[i];
      ++bufferLength;
      if (bufferLength < 16) {
        continue;
      }
      for (let j = 0; j < 16; ++j) {
        buffer[j] ^= iv[j];
      }
      let cipher = this._encrypt(buffer, this._key);
      iv = cipher;
      result.push(cipher);
      buffer = new Uint8Array(16);
      bufferLength = 0;
    }
    this.buffer = buffer;
    this.bufferLength = bufferLength;
    this.iv = iv;
    if (result.length === 0) {
      return new Uint8Array(0);
    }
    let outputLength = 16 * result.length;
    let output = new Uint8Array(outputLength);
    for (let i = 0, j = 0, ii = result.length; i < ii; ++i, j += 16) {
      output.set(result[i], j);
    }
    return output;
  }
}
class AES128Cipher extends AESBaseCipher {
  constructor(key) {
    super();
    this._cyclesOfRepetition = 10;
    this._keySize = 160;
    this._rcon = new Uint8Array([0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d]);
    this._key = this._expandKey(key);
  }
  _expandKey(cipherKey) {
    const b = 176;
    const s = this._s;
    const rcon = this._rcon;
    let result = new Uint8Array(b);
    result.set(cipherKey);
    for (let j = 16, i = 1; j < b; ++i) {
      let t1 = result[j - 3];
      let t2 = result[j - 2];
      let t3 = result[j - 1];
      let t4 = result[j - 4];
      t1 = s[t1];
      t2 = s[t2];
      t3 = s[t3];
      t4 = s[t4];
      t1 = t1 ^ rcon[i];
      for (let n = 0; n < 4; ++n) {
        result[j] = t1 ^= result[j - 16];
        j++;
        result[j] = t2 ^= result[j - 16];
        j++;
        result[j] = t3 ^= result[j - 16];
        j++;
        result[j] = t4 ^= result[j - 16];
        j++;
      }
    }
    return result;
  }
}
class AES256Cipher extends AESBaseCipher {
  constructor(key) {
    super();
    this._cyclesOfRepetition = 14;
    this._keySize = 224;
    this._key = this._expandKey(key);
  }
  _expandKey(cipherKey) {
    const b = 240;
    const s = this._s;
    let result = new Uint8Array(b);
    result.set(cipherKey);
    let r = 1;
    let t1, t2, t3, t4;
    for (let j = 32, i = 1; j < b; ++i) {
      if (j % 32 === 16) {
        t1 = s[t1];
        t2 = s[t2];
        t3 = s[t3];
        t4 = s[t4];
      } else if (j % 32 === 0) {
        t1 = result[j - 3];
        t2 = result[j - 2];
        t3 = result[j - 1];
        t4 = result[j - 4];
        t1 = s[t1];
        t2 = s[t2];
        t3 = s[t3];
        t4 = s[t4];
        t1 = t1 ^ r;
        if ((r <<= 1) >= 256) {
          r = (r ^ 0x1b) & 0xFF;
        }
      }
      for (let n = 0; n < 4; ++n) {
        result[j] = t1 ^= result[j - 32];
        j++;
        result[j] = t2 ^= result[j - 32];
        j++;
        result[j] = t3 ^= result[j - 32];
        j++;
        result[j] = t4 ^= result[j - 32];
        j++;
      }
    }
    return result;
  }
}
var PDF17 = function PDF17Closure() {
  function compareByteArrays(array1, array2) {
    if (array1.length !== array2.length) {
      return false;
    }
    for (var i = 0; i < array1.length; i++) {
      if (array1[i] !== array2[i]) {
        return false;
      }
    }
    return true;
  }
  function PDF17() {}
  PDF17.prototype = {
    checkOwnerPassword: function PDF17_checkOwnerPassword(password, ownerValidationSalt, userBytes, ownerPassword) {
      var hashData = new Uint8Array(password.length + 56);
      hashData.set(password, 0);
      hashData.set(ownerValidationSalt, password.length);
      hashData.set(userBytes, password.length + ownerValidationSalt.length);
      var result = calculateSHA256(hashData, 0, hashData.length);
      return compareByteArrays(result, ownerPassword);
    },
    checkUserPassword: function PDF17_checkUserPassword(password, userValidationSalt, userPassword) {
      var hashData = new Uint8Array(password.length + 8);
      hashData.set(password, 0);
      hashData.set(userValidationSalt, password.length);
      var result = calculateSHA256(hashData, 0, hashData.length);
      return compareByteArrays(result, userPassword);
    },
    getOwnerKey: function PDF17_getOwnerKey(password, ownerKeySalt, userBytes, ownerEncryption) {
      var hashData = new Uint8Array(password.length + 56);
      hashData.set(password, 0);
      hashData.set(ownerKeySalt, password.length);
      hashData.set(userBytes, password.length + ownerKeySalt.length);
      var key = calculateSHA256(hashData, 0, hashData.length);
      var cipher = new AES256Cipher(key);
      return cipher.decryptBlock(ownerEncryption, false, new Uint8Array(16));
    },
    getUserKey: function PDF17_getUserKey(password, userKeySalt, userEncryption) {
      var hashData = new Uint8Array(password.length + 8);
      hashData.set(password, 0);
      hashData.set(userKeySalt, password.length);
      var key = calculateSHA256(hashData, 0, hashData.length);
      var cipher = new AES256Cipher(key);
      return cipher.decryptBlock(userEncryption, false, new Uint8Array(16));
    }
  };
  return PDF17;
}();
var PDF20 = function PDF20Closure() {
  function concatArrays(array1, array2) {
    var t = new Uint8Array(array1.length + array2.length);
    t.set(array1, 0);
    t.set(array2, array1.length);
    return t;
  }
  function calculatePDF20Hash(password, input, userBytes) {
    var k = calculateSHA256(input, 0, input.length).subarray(0, 32);
    var e = [0];
    var i = 0;
    while (i < 64 || e[e.length - 1] > i - 32) {
      var arrayLength = password.length + k.length + userBytes.length;
      var k1 = new Uint8Array(arrayLength * 64);
      var array = concatArrays(password, k);
      array = concatArrays(array, userBytes);
      for (var j = 0, pos = 0; j < 64; j++, pos += arrayLength) {
        k1.set(array, pos);
      }
      var cipher = new AES128Cipher(k.subarray(0, 16));
      e = cipher.encrypt(k1, k.subarray(16, 32));
      var remainder = 0;
      for (var z = 0; z < 16; z++) {
        remainder *= 256 % 3;
        remainder %= 3;
        remainder += (e[z] >>> 0) % 3;
        remainder %= 3;
      }
      if (remainder === 0) {
        k = calculateSHA256(e, 0, e.length);
      } else if (remainder === 1) {
        k = calculateSHA384(e, 0, e.length);
      } else if (remainder === 2) {
        k = calculateSHA512(e, 0, e.length);
      }
      i++;
    }
    return k.subarray(0, 32);
  }
  function PDF20() {}
  function compareByteArrays(array1, array2) {
    if (array1.length !== array2.length) {
      return false;
    }
    for (var i = 0; i < array1.length; i++) {
      if (array1[i] !== array2[i]) {
        return false;
      }
    }
    return true;
  }
  PDF20.prototype = {
    hash: function PDF20_hash(password, concatBytes, userBytes) {
      return calculatePDF20Hash(password, concatBytes, userBytes);
    },
    checkOwnerPassword: function PDF20_checkOwnerPassword(password, ownerValidationSalt, userBytes, ownerPassword) {
      var hashData = new Uint8Array(password.length + 56);
      hashData.set(password, 0);
      hashData.set(ownerValidationSalt, password.length);
      hashData.set(userBytes, password.length + ownerValidationSalt.length);
      var result = calculatePDF20Hash(password, hashData, userBytes);
      return compareByteArrays(result, ownerPassword);
    },
    checkUserPassword: function PDF20_checkUserPassword(password, userValidationSalt, userPassword) {
      var hashData = new Uint8Array(password.length + 8);
      hashData.set(password, 0);
      hashData.set(userValidationSalt, password.length);
      var result = calculatePDF20Hash(password, hashData, []);
      return compareByteArrays(result, userPassword);
    },
    getOwnerKey: function PDF20_getOwnerKey(password, ownerKeySalt, userBytes, ownerEncryption) {
      var hashData = new Uint8Array(password.length + 56);
      hashData.set(password, 0);
      hashData.set(ownerKeySalt, password.length);
      hashData.set(userBytes, password.length + ownerKeySalt.length);
      var key = calculatePDF20Hash(password, hashData, userBytes);
      var cipher = new AES256Cipher(key);
      return cipher.decryptBlock(ownerEncryption, false, new Uint8Array(16));
    },
    getUserKey: function PDF20_getUserKey(password, userKeySalt, userEncryption) {
      var hashData = new Uint8Array(password.length + 8);
      hashData.set(password, 0);
      hashData.set(userKeySalt, password.length);
      var key = calculatePDF20Hash(password, hashData, []);
      var cipher = new AES256Cipher(key);
      return cipher.decryptBlock(userEncryption, false, new Uint8Array(16));
    }
  };
  return PDF20;
}();
var CipherTransform = function CipherTransformClosure() {
  function CipherTransform(stringCipherConstructor, streamCipherConstructor) {
    this.StringCipherConstructor = stringCipherConstructor;
    this.StreamCipherConstructor = streamCipherConstructor;
  }
  CipherTransform.prototype = {
    createStream: function CipherTransform_createStream(stream, length) {
      var cipher = new this.StreamCipherConstructor();
      return new _stream.DecryptStream(stream, length, function cipherTransformDecryptStream(data, finalize) {
        return cipher.decryptBlock(data, finalize);
      });
    },
    decryptString: function CipherTransform_decryptString(s) {
      var cipher = new this.StringCipherConstructor();
      var data = (0, _util.stringToBytes)(s);
      data = cipher.decryptBlock(data, true);
      return (0, _util.bytesToString)(data);
    }
  };
  return CipherTransform;
}();
var CipherTransformFactory = function CipherTransformFactoryClosure() {
  var defaultPasswordBytes = new Uint8Array([0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08, 0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A]);
  function createEncryptionKey20(revision, password, ownerPassword, ownerValidationSalt, ownerKeySalt, uBytes, userPassword, userValidationSalt, userKeySalt, ownerEncryption, userEncryption, perms) {
    if (password) {
      var passwordLength = Math.min(127, password.length);
      password = password.subarray(0, passwordLength);
    } else {
      password = [];
    }
    var pdfAlgorithm;
    if (revision === 6) {
      pdfAlgorithm = new PDF20();
    } else {
      pdfAlgorithm = new PDF17();
    }
    if (pdfAlgorithm.checkUserPassword(password, userValidationSalt, userPassword)) {
      return pdfAlgorithm.getUserKey(password, userKeySalt, userEncryption);
    } else if (password.length && pdfAlgorithm.checkOwnerPassword(password, ownerValidationSalt, uBytes, ownerPassword)) {
      return pdfAlgorithm.getOwnerKey(password, ownerKeySalt, uBytes, ownerEncryption);
    }
    return null;
  }
  function prepareKeyData(fileId, password, ownerPassword, userPassword, flags, revision, keyLength, encryptMetadata) {
    var hashDataSize = 40 + ownerPassword.length + fileId.length;
    var hashData = new Uint8Array(hashDataSize),
        i = 0,
        j,
        n;
    if (password) {
      n = Math.min(32, password.length);
      for (; i < n; ++i) {
        hashData[i] = password[i];
      }
    }
    j = 0;
    while (i < 32) {
      hashData[i++] = defaultPasswordBytes[j++];
    }
    for (j = 0, n = ownerPassword.length; j < n; ++j) {
      hashData[i++] = ownerPassword[j];
    }
    hashData[i++] = flags & 0xFF;
    hashData[i++] = flags >> 8 & 0xFF;
    hashData[i++] = flags >> 16 & 0xFF;
    hashData[i++] = flags >>> 24 & 0xFF;
    for (j = 0, n = fileId.length; j < n; ++j) {
      hashData[i++] = fileId[j];
    }
    if (revision >= 4 && !encryptMetadata) {
      hashData[i++] = 0xFF;
      hashData[i++] = 0xFF;
      hashData[i++] = 0xFF;
      hashData[i++] = 0xFF;
    }
    var hash = calculateMD5(hashData, 0, i);
    var keyLengthInBytes = keyLength >> 3;
    if (revision >= 3) {
      for (j = 0; j < 50; ++j) {
        hash = calculateMD5(hash, 0, keyLengthInBytes);
      }
    }
    var encryptionKey = hash.subarray(0, keyLengthInBytes);
    var cipher, checkData;
    if (revision >= 3) {
      for (i = 0; i < 32; ++i) {
        hashData[i] = defaultPasswordBytes[i];
      }
      for (j = 0, n = fileId.length; j < n; ++j) {
        hashData[i++] = fileId[j];
      }
      cipher = new ARCFourCipher(encryptionKey);
      checkData = cipher.encryptBlock(calculateMD5(hashData, 0, i));
      n = encryptionKey.length;
      var derivedKey = new Uint8Array(n),
          k;
      for (j = 1; j <= 19; ++j) {
        for (k = 0; k < n; ++k) {
          derivedKey[k] = encryptionKey[k] ^ j;
        }
        cipher = new ARCFourCipher(derivedKey);
        checkData = cipher.encryptBlock(checkData);
      }
      for (j = 0, n = checkData.length; j < n; ++j) {
        if (userPassword[j] !== checkData[j]) {
          return null;
        }
      }
    } else {
      cipher = new ARCFourCipher(encryptionKey);
      checkData = cipher.encryptBlock(defaultPasswordBytes);
      for (j = 0, n = checkData.length; j < n; ++j) {
        if (userPassword[j] !== checkData[j]) {
          return null;
        }
      }
    }
    return encryptionKey;
  }
  function decodeUserPassword(password, ownerPassword, revision, keyLength) {
    var hashData = new Uint8Array(32),
        i = 0,
        j,
        n;
    n = Math.min(32, password.length);
    for (; i < n; ++i) {
      hashData[i] = password[i];
    }
    j = 0;
    while (i < 32) {
      hashData[i++] = defaultPasswordBytes[j++];
    }
    var hash = calculateMD5(hashData, 0, i);
    var keyLengthInBytes = keyLength >> 3;
    if (revision >= 3) {
      for (j = 0; j < 50; ++j) {
        hash = calculateMD5(hash, 0, hash.length);
      }
    }
    var cipher, userPassword;
    if (revision >= 3) {
      userPassword = ownerPassword;
      var derivedKey = new Uint8Array(keyLengthInBytes),
          k;
      for (j = 19; j >= 0; j--) {
        for (k = 0; k < keyLengthInBytes; ++k) {
          derivedKey[k] = hash[k] ^ j;
        }
        cipher = new ARCFourCipher(derivedKey);
        userPassword = cipher.encryptBlock(userPassword);
      }
    } else {
      cipher = new ARCFourCipher(hash.subarray(0, keyLengthInBytes));
      userPassword = cipher.encryptBlock(ownerPassword);
    }
    return userPassword;
  }
  var identityName = _primitives.Name.get('Identity');
  function CipherTransformFactory(dict, fileId, password) {
    var filter = dict.get('Filter');
    if (!(0, _primitives.isName)(filter, 'Standard')) {
      throw new _util.FormatError('unknown encryption method');
    }
    this.dict = dict;
    var algorithm = dict.get('V');
    if (!Number.isInteger(algorithm) || algorithm !== 1 && algorithm !== 2 && algorithm !== 4 && algorithm !== 5) {
      throw new _util.FormatError('unsupported encryption algorithm');
    }
    this.algorithm = algorithm;
    var keyLength = dict.get('Length');
    if (!keyLength) {
      if (algorithm <= 3) {
        keyLength = 40;
      } else {
        var cfDict = dict.get('CF');
        var streamCryptoName = dict.get('StmF');
        if ((0, _primitives.isDict)(cfDict) && (0, _primitives.isName)(streamCryptoName)) {
          cfDict.suppressEncryption = true;
          var handlerDict = cfDict.get(streamCryptoName.name);
          keyLength = handlerDict && handlerDict.get('Length') || 128;
          if (keyLength < 40) {
            keyLength <<= 3;
          }
        }
      }
    }
    if (!Number.isInteger(keyLength) || keyLength < 40 || keyLength % 8 !== 0) {
      throw new _util.FormatError('invalid key length');
    }
    var ownerPassword = (0, _util.stringToBytes)(dict.get('O')).subarray(0, 32);
    var userPassword = (0, _util.stringToBytes)(dict.get('U')).subarray(0, 32);
    var flags = dict.get('P');
    var revision = dict.get('R');
    var encryptMetadata = (algorithm === 4 || algorithm === 5) && dict.get('EncryptMetadata') !== false;
    this.encryptMetadata = encryptMetadata;
    var fileIdBytes = (0, _util.stringToBytes)(fileId);
    var passwordBytes;
    if (password) {
      if (revision === 6) {
        try {
          password = (0, _util.utf8StringToString)(password);
        } catch (ex) {
          (0, _util.warn)('CipherTransformFactory: ' + 'Unable to convert UTF8 encoded password.');
        }
      }
      passwordBytes = (0, _util.stringToBytes)(password);
    }
    var encryptionKey;
    if (algorithm !== 5) {
      encryptionKey = prepareKeyData(fileIdBytes, passwordBytes, ownerPassword, userPassword, flags, revision, keyLength, encryptMetadata);
    } else {
      var ownerValidationSalt = (0, _util.stringToBytes)(dict.get('O')).subarray(32, 40);
      var ownerKeySalt = (0, _util.stringToBytes)(dict.get('O')).subarray(40, 48);
      var uBytes = (0, _util.stringToBytes)(dict.get('U')).subarray(0, 48);
      var userValidationSalt = (0, _util.stringToBytes)(dict.get('U')).subarray(32, 40);
      var userKeySalt = (0, _util.stringToBytes)(dict.get('U')).subarray(40, 48);
      var ownerEncryption = (0, _util.stringToBytes)(dict.get('OE'));
      var userEncryption = (0, _util.stringToBytes)(dict.get('UE'));
      var perms = (0, _util.stringToBytes)(dict.get('Perms'));
      encryptionKey = createEncryptionKey20(revision, passwordBytes, ownerPassword, ownerValidationSalt, ownerKeySalt, uBytes, userPassword, userValidationSalt, userKeySalt, ownerEncryption, userEncryption, perms);
    }
    if (!encryptionKey && !password) {
      throw new _util.PasswordException('No password given', _util.PasswordResponses.NEED_PASSWORD);
    } else if (!encryptionKey && password) {
      var decodedPassword = decodeUserPassword(passwordBytes, ownerPassword, revision, keyLength);
      encryptionKey = prepareKeyData(fileIdBytes, decodedPassword, ownerPassword, userPassword, flags, revision, keyLength, encryptMetadata);
    }
    if (!encryptionKey) {
      throw new _util.PasswordException('Incorrect Password', _util.PasswordResponses.INCORRECT_PASSWORD);
    }
    this.encryptionKey = encryptionKey;
    if (algorithm >= 4) {
      var cf = dict.get('CF');
      if ((0, _primitives.isDict)(cf)) {
        cf.suppressEncryption = true;
      }
      this.cf = cf;
      this.stmf = dict.get('StmF') || identityName;
      this.strf = dict.get('StrF') || identityName;
      this.eff = dict.get('EFF') || this.stmf;
    }
  }
  function buildObjectKey(num, gen, encryptionKey, isAes) {
    var key = new Uint8Array(encryptionKey.length + 9),
        i,
        n;
    for (i = 0, n = encryptionKey.length; i < n; ++i) {
      key[i] = encryptionKey[i];
    }
    key[i++] = num & 0xFF;
    key[i++] = num >> 8 & 0xFF;
    key[i++] = num >> 16 & 0xFF;
    key[i++] = gen & 0xFF;
    key[i++] = gen >> 8 & 0xFF;
    if (isAes) {
      key[i++] = 0x73;
      key[i++] = 0x41;
      key[i++] = 0x6C;
      key[i++] = 0x54;
    }
    var hash = calculateMD5(key, 0, i);
    return hash.subarray(0, Math.min(encryptionKey.length + 5, 16));
  }
  function buildCipherConstructor(cf, name, num, gen, key) {
    if (!(0, _primitives.isName)(name)) {
      throw new _util.FormatError('Invalid crypt filter name.');
    }
    var cryptFilter = cf.get(name.name);
    var cfm;
    if (cryptFilter !== null && cryptFilter !== undefined) {
      cfm = cryptFilter.get('CFM');
    }
    if (!cfm || cfm.name === 'None') {
      return function cipherTransformFactoryBuildCipherConstructorNone() {
        return new NullCipher();
      };
    }
    if (cfm.name === 'V2') {
      return function cipherTransformFactoryBuildCipherConstructorV2() {
        return new ARCFourCipher(buildObjectKey(num, gen, key, false));
      };
    }
    if (cfm.name === 'AESV2') {
      return function cipherTransformFactoryBuildCipherConstructorAESV2() {
        return new AES128Cipher(buildObjectKey(num, gen, key, true));
      };
    }
    if (cfm.name === 'AESV3') {
      return function cipherTransformFactoryBuildCipherConstructorAESV3() {
        return new AES256Cipher(key);
      };
    }
    throw new _util.FormatError('Unknown crypto method');
  }
  CipherTransformFactory.prototype = {
    createCipherTransform: function CipherTransformFactory_createCipherTransform(num, gen) {
      if (this.algorithm === 4 || this.algorithm === 5) {
        return new CipherTransform(buildCipherConstructor(this.cf, this.stmf, num, gen, this.encryptionKey), buildCipherConstructor(this.cf, this.strf, num, gen, this.encryptionKey));
      }
      var key = buildObjectKey(num, gen, this.encryptionKey, false);
      var cipherConstructor = function buildCipherCipherConstructor() {
        return new ARCFourCipher(key);
      };
      return new CipherTransform(cipherConstructor, cipherConstructor);
    }
  };
  return CipherTransformFactory;
}();
exports.AES128Cipher = AES128Cipher;
exports.AES256Cipher = AES256Cipher;
exports.ARCFourCipher = ARCFourCipher;
exports.CipherTransformFactory = CipherTransformFactory;
exports.PDF17 = PDF17;
exports.PDF20 = PDF20;
exports.calculateMD5 = calculateMD5;
exports.calculateSHA256 = calculateSHA256;
exports.calculateSHA384 = calculateSHA384;
exports.calculateSHA512 = calculateSHA512;

/***/ }),
/* 15 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.CFFCompiler = exports.CFFPrivateDict = exports.CFFTopDict = exports.CFFCharset = exports.CFFIndex = exports.CFFStrings = exports.CFFHeader = exports.CFF = exports.CFFParser = exports.CFFStandardStrings = undefined;

var _util = __w_pdfjs_require__(0);

var _charsets = __w_pdfjs_require__(35);

var _encodings = __w_pdfjs_require__(4);

var MAX_SUBR_NESTING = 10;
var CFFStandardStrings = ['.notdef', 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright', 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash', 'bracketright', 'asciicircum', 'underscore', 'quoteleft', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright', 'asciitilde', 'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin', 'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft', 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'endash', 'dagger', 'daggerdbl', 'periodcentered', 'paragraph', 'bullet', 'quotesinglbase', 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand', 'questiondown', 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve', 'dotaccent', 'dieresis', 'ring', 'cedilla', 'hungarumlaut', 'ogonek', 'caron', 'emdash', 'AE', 'ordfeminine', 'Lslash', 'Oslash', 'OE', 'ordmasculine', 'ae', 'dotlessi', 'lslash', 'oslash', 'oe', 'germandbls', 'onesuperior', 'logicalnot', 'mu', 'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn', 'onequarter', 'divide', 'brokenbar', 'degree', 'thorn', 'threequarters', 'twosuperior', 'registered', 'minus', 'eth', 'multiply', 'threesuperior', 'copyright', 'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring', 'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave', 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute', 'Ocircumflex', 'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute', 'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron', 'aacute', 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla', 'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex', 'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex', 'odieresis', 'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis', 'ugrave', 'yacute', 'ydieresis', 'zcaron', 'exclamsmall', 'Hungarumlautsmall', 'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall', 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader', 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle', 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle', 'nineoldstyle', 'commasuperior', 'threequartersemdash', 'periodsuperior', 'questionsmall', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior', 'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior', 'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'ffi', 'ffl', 'parenleftinferior', 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall', 'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall', 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall', 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall', 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall', 'exclamdownsmall', 'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall', 'Dieresissmall', 'Brevesmall', 'Caronsmall', 'Dotaccentsmall', 'Macronsmall', 'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall', 'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths', 'onethird', 'twothirds', 'zerosuperior', 'foursuperior', 'fivesuperior', 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior', 'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior', 'sixinferior', 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior', 'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall', 'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall', 'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall', 'Edieresissmall', 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall', 'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall', 'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall', 'Ugravesmall', 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall', 'Yacutesmall', 'Thornsmall', 'Ydieresissmall', '001.000', '001.001', '001.002', '001.003', 'Black', 'Bold', 'Book', 'Light', 'Medium', 'Regular', 'Roman', 'Semibold'];
var CFFParser = function CFFParserClosure() {
  var CharstringValidationData = [null, {
    id: 'hstem',
    min: 2,
    stackClearing: true,
    stem: true
  }, null, {
    id: 'vstem',
    min: 2,
    stackClearing: true,
    stem: true
  }, {
    id: 'vmoveto',
    min: 1,
    stackClearing: true
  }, {
    id: 'rlineto',
    min: 2,
    resetStack: true
  }, {
    id: 'hlineto',
    min: 1,
    resetStack: true
  }, {
    id: 'vlineto',
    min: 1,
    resetStack: true
  }, {
    id: 'rrcurveto',
    min: 6,
    resetStack: true
  }, null, {
    id: 'callsubr',
    min: 1,
    undefStack: true
  }, {
    id: 'return',
    min: 0,
    undefStack: true
  }, null, null, {
    id: 'endchar',
    min: 0,
    stackClearing: true
  }, null, null, null, {
    id: 'hstemhm',
    min: 2,
    stackClearing: true,
    stem: true
  }, {
    id: 'hintmask',
    min: 0,
    stackClearing: true
  }, {
    id: 'cntrmask',
    min: 0,
    stackClearing: true
  }, {
    id: 'rmoveto',
    min: 2,
    stackClearing: true
  }, {
    id: 'hmoveto',
    min: 1,
    stackClearing: true
  }, {
    id: 'vstemhm',
    min: 2,
    stackClearing: true,
    stem: true
  }, {
    id: 'rcurveline',
    min: 8,
    resetStack: true
  }, {
    id: 'rlinecurve',
    min: 8,
    resetStack: true
  }, {
    id: 'vvcurveto',
    min: 4,
    resetStack: true
  }, {
    id: 'hhcurveto',
    min: 4,
    resetStack: true
  }, null, {
    id: 'callgsubr',
    min: 1,
    undefStack: true
  }, {
    id: 'vhcurveto',
    min: 4,
    resetStack: true
  }, {
    id: 'hvcurveto',
    min: 4,
    resetStack: true
  }];
  var CharstringValidationData12 = [null, null, null, {
    id: 'and',
    min: 2,
    stackDelta: -1
  }, {
    id: 'or',
    min: 2,
    stackDelta: -1
  }, {
    id: 'not',
    min: 1,
    stackDelta: 0
  }, null, null, null, {
    id: 'abs',
    min: 1,
    stackDelta: 0
  }, {
    id: 'add',
    min: 2,
    stackDelta: -1,
    stackFn: function stack_div(stack, index) {
      stack[index - 2] = stack[index - 2] + stack[index - 1];
    }
  }, {
    id: 'sub',
    min: 2,
    stackDelta: -1,
    stackFn: function stack_div(stack, index) {
      stack[index - 2] = stack[index - 2] - stack[index - 1];
    }
  }, {
    id: 'div',
    min: 2,
    stackDelta: -1,
    stackFn: function stack_div(stack, index) {
      stack[index - 2] = stack[index - 2] / stack[index - 1];
    }
  }, null, {
    id: 'neg',
    min: 1,
    stackDelta: 0,
    stackFn: function stack_div(stack, index) {
      stack[index - 1] = -stack[index - 1];
    }
  }, {
    id: 'eq',
    min: 2,
    stackDelta: -1
  }, null, null, {
    id: 'drop',
    min: 1,
    stackDelta: -1
  }, null, {
    id: 'put',
    min: 2,
    stackDelta: -2
  }, {
    id: 'get',
    min: 1,
    stackDelta: 0
  }, {
    id: 'ifelse',
    min: 4,
    stackDelta: -3
  }, {
    id: 'random',
    min: 0,
    stackDelta: 1
  }, {
    id: 'mul',
    min: 2,
    stackDelta: -1,
    stackFn: function stack_div(stack, index) {
      stack[index - 2] = stack[index - 2] * stack[index - 1];
    }
  }, null, {
    id: 'sqrt',
    min: 1,
    stackDelta: 0
  }, {
    id: 'dup',
    min: 1,
    stackDelta: 1
  }, {
    id: 'exch',
    min: 2,
    stackDelta: 0
  }, {
    id: 'index',
    min: 2,
    stackDelta: 0
  }, {
    id: 'roll',
    min: 3,
    stackDelta: -2
  }, null, null, null, {
    id: 'hflex',
    min: 7,
    resetStack: true
  }, {
    id: 'flex',
    min: 13,
    resetStack: true
  }, {
    id: 'hflex1',
    min: 9,
    resetStack: true
  }, {
    id: 'flex1',
    min: 11,
    resetStack: true
  }];
  function CFFParser(file, properties, seacAnalysisEnabled) {
    this.bytes = file.getBytes();
    this.properties = properties;
    this.seacAnalysisEnabled = !!seacAnalysisEnabled;
  }
  CFFParser.prototype = {
    parse: function CFFParser_parse() {
      var properties = this.properties;
      var cff = new CFF();
      this.cff = cff;
      var header = this.parseHeader();
      var nameIndex = this.parseIndex(header.endPos);
      var topDictIndex = this.parseIndex(nameIndex.endPos);
      var stringIndex = this.parseIndex(topDictIndex.endPos);
      var globalSubrIndex = this.parseIndex(stringIndex.endPos);
      var topDictParsed = this.parseDict(topDictIndex.obj.get(0));
      var topDict = this.createDict(CFFTopDict, topDictParsed, cff.strings);
      cff.header = header.obj;
      cff.names = this.parseNameIndex(nameIndex.obj);
      cff.strings = this.parseStringIndex(stringIndex.obj);
      cff.topDict = topDict;
      cff.globalSubrIndex = globalSubrIndex.obj;
      this.parsePrivateDict(cff.topDict);
      cff.isCIDFont = topDict.hasName('ROS');
      var charStringOffset = topDict.getByName('CharStrings');
      var charStringIndex = this.parseIndex(charStringOffset).obj;
      var fontMatrix = topDict.getByName('FontMatrix');
      if (fontMatrix) {
        properties.fontMatrix = fontMatrix;
      }
      var fontBBox = topDict.getByName('FontBBox');
      if (fontBBox) {
        properties.ascent = Math.max(fontBBox[3], fontBBox[1]);
        properties.descent = Math.min(fontBBox[1], fontBBox[3]);
        properties.ascentScaled = true;
      }
      var charset, encoding;
      if (cff.isCIDFont) {
        var fdArrayIndex = this.parseIndex(topDict.getByName('FDArray')).obj;
        for (var i = 0, ii = fdArrayIndex.count; i < ii; ++i) {
          var dictRaw = fdArrayIndex.get(i);
          var fontDict = this.createDict(CFFTopDict, this.parseDict(dictRaw), cff.strings);
          this.parsePrivateDict(fontDict);
          cff.fdArray.push(fontDict);
        }
        encoding = null;
        charset = this.parseCharsets(topDict.getByName('charset'), charStringIndex.count, cff.strings, true);
        cff.fdSelect = this.parseFDSelect(topDict.getByName('FDSelect'), charStringIndex.count);
      } else {
        charset = this.parseCharsets(topDict.getByName('charset'), charStringIndex.count, cff.strings, false);
        encoding = this.parseEncoding(topDict.getByName('Encoding'), properties, cff.strings, charset.charset);
      }
      cff.charset = charset;
      cff.encoding = encoding;
      var charStringsAndSeacs = this.parseCharStrings({
        charStrings: charStringIndex,
        localSubrIndex: topDict.privateDict.subrsIndex,
        globalSubrIndex: globalSubrIndex.obj,
        fdSelect: cff.fdSelect,
        fdArray: cff.fdArray,
        privateDict: topDict.privateDict
      });
      cff.charStrings = charStringsAndSeacs.charStrings;
      cff.seacs = charStringsAndSeacs.seacs;
      cff.widths = charStringsAndSeacs.widths;
      return cff;
    },
    parseHeader: function CFFParser_parseHeader() {
      var bytes = this.bytes;
      var bytesLength = bytes.length;
      var offset = 0;
      while (offset < bytesLength && bytes[offset] !== 1) {
        ++offset;
      }
      if (offset >= bytesLength) {
        throw new _util.FormatError('Invalid CFF header');
      }
      if (offset !== 0) {
        (0, _util.info)('cff data is shifted');
        bytes = bytes.subarray(offset);
        this.bytes = bytes;
      }
      var major = bytes[0];
      var minor = bytes[1];
      var hdrSize = bytes[2];
      var offSize = bytes[3];
      var header = new CFFHeader(major, minor, hdrSize, offSize);
      return {
        obj: header,
        endPos: hdrSize
      };
    },
    parseDict: function CFFParser_parseDict(dict) {
      var pos = 0;
      function parseOperand() {
        var value = dict[pos++];
        if (value === 30) {
          return parseFloatOperand();
        } else if (value === 28) {
          value = dict[pos++];
          value = (value << 24 | dict[pos++] << 16) >> 16;
          return value;
        } else if (value === 29) {
          value = dict[pos++];
          value = value << 8 | dict[pos++];
          value = value << 8 | dict[pos++];
          value = value << 8 | dict[pos++];
          return value;
        } else if (value >= 32 && value <= 246) {
          return value - 139;
        } else if (value >= 247 && value <= 250) {
          return (value - 247) * 256 + dict[pos++] + 108;
        } else if (value >= 251 && value <= 254) {
          return -((value - 251) * 256) - dict[pos++] - 108;
        }
        (0, _util.warn)('CFFParser_parseDict: "' + value + '" is a reserved command.');
        return NaN;
      }
      function parseFloatOperand() {
        var str = '';
        var eof = 15;
        var lookup = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', 'E', 'E-', null, '-'];
        var length = dict.length;
        while (pos < length) {
          var b = dict[pos++];
          var b1 = b >> 4;
          var b2 = b & 15;
          if (b1 === eof) {
            break;
          }
          str += lookup[b1];
          if (b2 === eof) {
            break;
          }
          str += lookup[b2];
        }
        return parseFloat(str);
      }
      var operands = [];
      var entries = [];
      pos = 0;
      var end = dict.length;
      while (pos < end) {
        var b = dict[pos];
        if (b <= 21) {
          if (b === 12) {
            b = b << 8 | dict[++pos];
          }
          entries.push([b, operands]);
          operands = [];
          ++pos;
        } else {
          operands.push(parseOperand());
        }
      }
      return entries;
    },
    parseIndex: function CFFParser_parseIndex(pos) {
      var cffIndex = new CFFIndex();
      var bytes = this.bytes;
      var count = bytes[pos++] << 8 | bytes[pos++];
      var offsets = [];
      var end = pos;
      var i, ii;
      if (count !== 0) {
        var offsetSize = bytes[pos++];
        var startPos = pos + (count + 1) * offsetSize - 1;
        for (i = 0, ii = count + 1; i < ii; ++i) {
          var offset = 0;
          for (var j = 0; j < offsetSize; ++j) {
            offset <<= 8;
            offset += bytes[pos++];
          }
          offsets.push(startPos + offset);
        }
        end = offsets[count];
      }
      for (i = 0, ii = offsets.length - 1; i < ii; ++i) {
        var offsetStart = offsets[i];
        var offsetEnd = offsets[i + 1];
        cffIndex.add(bytes.subarray(offsetStart, offsetEnd));
      }
      return {
        obj: cffIndex,
        endPos: end
      };
    },
    parseNameIndex: function CFFParser_parseNameIndex(index) {
      var names = [];
      for (var i = 0, ii = index.count; i < ii; ++i) {
        var name = index.get(i);
        names.push((0, _util.bytesToString)(name));
      }
      return names;
    },
    parseStringIndex: function CFFParser_parseStringIndex(index) {
      var strings = new CFFStrings();
      for (var i = 0, ii = index.count; i < ii; ++i) {
        var data = index.get(i);
        strings.add((0, _util.bytesToString)(data));
      }
      return strings;
    },
    createDict: function CFFParser_createDict(Type, dict, strings) {
      var cffDict = new Type(strings);
      for (var i = 0, ii = dict.length; i < ii; ++i) {
        var pair = dict[i];
        var key = pair[0];
        var value = pair[1];
        cffDict.setByKey(key, value);
      }
      return cffDict;
    },
    parseCharString: function CFFParser_parseCharString(state, data, localSubrIndex, globalSubrIndex) {
      if (!data || state.callDepth > MAX_SUBR_NESTING) {
        return false;
      }
      var stackSize = state.stackSize;
      var stack = state.stack;
      var length = data.length;
      for (var j = 0; j < length;) {
        var value = data[j++];
        var validationCommand = null;
        if (value === 12) {
          var q = data[j++];
          if (q === 0) {
            data[j - 2] = 139;
            data[j - 1] = 22;
            stackSize = 0;
          } else {
            validationCommand = CharstringValidationData12[q];
          }
        } else if (value === 28) {
          stack[stackSize] = (data[j] << 24 | data[j + 1] << 16) >> 16;
          j += 2;
          stackSize++;
        } else if (value === 14) {
          if (stackSize >= 4) {
            stackSize -= 4;
            if (this.seacAnalysisEnabled) {
              state.seac = stack.slice(stackSize, stackSize + 4);
              return false;
            }
          }
          validationCommand = CharstringValidationData[value];
        } else if (value >= 32 && value <= 246) {
          stack[stackSize] = value - 139;
          stackSize++;
        } else if (value >= 247 && value <= 254) {
          stack[stackSize] = value < 251 ? (value - 247 << 8) + data[j] + 108 : -(value - 251 << 8) - data[j] - 108;
          j++;
          stackSize++;
        } else if (value === 255) {
          stack[stackSize] = (data[j] << 24 | data[j + 1] << 16 | data[j + 2] << 8 | data[j + 3]) / 65536;
          j += 4;
          stackSize++;
        } else if (value === 19 || value === 20) {
          state.hints += stackSize >> 1;
          j += state.hints + 7 >> 3;
          stackSize %= 2;
          validationCommand = CharstringValidationData[value];
        } else if (value === 10 || value === 29) {
          var subrsIndex;
          if (value === 10) {
            subrsIndex = localSubrIndex;
          } else {
            subrsIndex = globalSubrIndex;
          }
          if (!subrsIndex) {
            validationCommand = CharstringValidationData[value];
            (0, _util.warn)('Missing subrsIndex for ' + validationCommand.id);
            return false;
          }
          var bias = 32768;
          if (subrsIndex.count < 1240) {
            bias = 107;
          } else if (subrsIndex.count < 33900) {
            bias = 1131;
          }
          var subrNumber = stack[--stackSize] + bias;
          if (subrNumber < 0 || subrNumber >= subrsIndex.count || isNaN(subrNumber)) {
            validationCommand = CharstringValidationData[value];
            (0, _util.warn)('Out of bounds subrIndex for ' + validationCommand.id);
            return false;
          }
          state.stackSize = stackSize;
          state.callDepth++;
          var valid = this.parseCharString(state, subrsIndex.get(subrNumber), localSubrIndex, globalSubrIndex);
          if (!valid) {
            return false;
          }
          state.callDepth--;
          stackSize = state.stackSize;
          continue;
        } else if (value === 11) {
          state.stackSize = stackSize;
          return true;
        } else {
          validationCommand = CharstringValidationData[value];
        }
        if (validationCommand) {
          if (validationCommand.stem) {
            state.hints += stackSize >> 1;
          }
          if ('min' in validationCommand) {
            if (!state.undefStack && stackSize < validationCommand.min) {
              (0, _util.warn)('Not enough parameters for ' + validationCommand.id + '; actual: ' + stackSize + ', expected: ' + validationCommand.min);
              return false;
            }
          }
          if (state.firstStackClearing && validationCommand.stackClearing) {
            state.firstStackClearing = false;
            stackSize -= validationCommand.min;
            if (stackSize >= 2 && validationCommand.stem) {
              stackSize %= 2;
            } else if (stackSize > 1) {
              (0, _util.warn)('Found too many parameters for stack-clearing command');
            }
            if (stackSize > 0 && stack[stackSize - 1] >= 0) {
              state.width = stack[stackSize - 1];
            }
          }
          if ('stackDelta' in validationCommand) {
            if ('stackFn' in validationCommand) {
              validationCommand.stackFn(stack, stackSize);
            }
            stackSize += validationCommand.stackDelta;
          } else if (validationCommand.stackClearing) {
            stackSize = 0;
          } else if (validationCommand.resetStack) {
            stackSize = 0;
            state.undefStack = false;
          } else if (validationCommand.undefStack) {
            stackSize = 0;
            state.undefStack = true;
            state.firstStackClearing = false;
          }
        }
      }
      state.stackSize = stackSize;
      return true;
    },
    parseCharStrings({ charStrings, localSubrIndex, globalSubrIndex, fdSelect, fdArray, privateDict }) {
      var seacs = [];
      var widths = [];
      var count = charStrings.count;
      for (var i = 0; i < count; i++) {
        var charstring = charStrings.get(i);
        var state = {
          callDepth: 0,
          stackSize: 0,
          stack: [],
          undefStack: true,
          hints: 0,
          firstStackClearing: true,
          seac: null,
          width: null
        };
        var valid = true;
        var localSubrToUse = null;
        var privateDictToUse = privateDict;
        if (fdSelect && fdArray.length) {
          var fdIndex = fdSelect.getFDIndex(i);
          if (fdIndex === -1) {
            (0, _util.warn)('Glyph index is not in fd select.');
            valid = false;
          }
          if (fdIndex >= fdArray.length) {
            (0, _util.warn)('Invalid fd index for glyph index.');
            valid = false;
          }
          if (valid) {
            privateDictToUse = fdArray[fdIndex].privateDict;
            localSubrToUse = privateDictToUse.subrsIndex;
          }
        } else if (localSubrIndex) {
          localSubrToUse = localSubrIndex;
        }
        if (valid) {
          valid = this.parseCharString(state, charstring, localSubrToUse, globalSubrIndex);
        }
        if (state.width !== null) {
          const nominalWidth = privateDictToUse.getByName('nominalWidthX');
          widths[i] = nominalWidth + state.width;
        } else {
          const defaultWidth = privateDictToUse.getByName('defaultWidthX');
          widths[i] = defaultWidth;
        }
        if (state.seac !== null) {
          seacs[i] = state.seac;
        }
        if (!valid) {
          charStrings.set(i, new Uint8Array([14]));
        }
      }
      return {
        charStrings,
        seacs,
        widths
      };
    },
    emptyPrivateDictionary: function CFFParser_emptyPrivateDictionary(parentDict) {
      var privateDict = this.createDict(CFFPrivateDict, [], parentDict.strings);
      parentDict.setByKey(18, [0, 0]);
      parentDict.privateDict = privateDict;
    },
    parsePrivateDict: function CFFParser_parsePrivateDict(parentDict) {
      if (!parentDict.hasName('Private')) {
        this.emptyPrivateDictionary(parentDict);
        return;
      }
      var privateOffset = parentDict.getByName('Private');
      if (!Array.isArray(privateOffset) || privateOffset.length !== 2) {
        parentDict.removeByName('Private');
        return;
      }
      var size = privateOffset[0];
      var offset = privateOffset[1];
      if (size === 0 || offset >= this.bytes.length) {
        this.emptyPrivateDictionary(parentDict);
        return;
      }
      var privateDictEnd = offset + size;
      var dictData = this.bytes.subarray(offset, privateDictEnd);
      var dict = this.parseDict(dictData);
      var privateDict = this.createDict(CFFPrivateDict, dict, parentDict.strings);
      parentDict.privateDict = privateDict;
      if (!privateDict.getByName('Subrs')) {
        return;
      }
      var subrsOffset = privateDict.getByName('Subrs');
      var relativeOffset = offset + subrsOffset;
      if (subrsOffset === 0 || relativeOffset >= this.bytes.length) {
        this.emptyPrivateDictionary(parentDict);
        return;
      }
      var subrsIndex = this.parseIndex(relativeOffset);
      privateDict.subrsIndex = subrsIndex.obj;
    },
    parseCharsets: function CFFParser_parseCharsets(pos, length, strings, cid) {
      if (pos === 0) {
        return new CFFCharset(true, CFFCharsetPredefinedTypes.ISO_ADOBE, _charsets.ISOAdobeCharset);
      } else if (pos === 1) {
        return new CFFCharset(true, CFFCharsetPredefinedTypes.EXPERT, _charsets.ExpertCharset);
      } else if (pos === 2) {
        return new CFFCharset(true, CFFCharsetPredefinedTypes.EXPERT_SUBSET, _charsets.ExpertSubsetCharset);
      }
      var bytes = this.bytes;
      var start = pos;
      var format = bytes[pos++];
      var charset = ['.notdef'];
      var id, count, i;
      length -= 1;
      switch (format) {
        case 0:
          for (i = 0; i < length; i++) {
            id = bytes[pos++] << 8 | bytes[pos++];
            charset.push(cid ? id : strings.get(id));
          }
          break;
        case 1:
          while (charset.length <= length) {
            id = bytes[pos++] << 8 | bytes[pos++];
            count = bytes[pos++];
            for (i = 0; i <= count; i++) {
              charset.push(cid ? id++ : strings.get(id++));
            }
          }
          break;
        case 2:
          while (charset.length <= length) {
            id = bytes[pos++] << 8 | bytes[pos++];
            count = bytes[pos++] << 8 | bytes[pos++];
            for (i = 0; i <= count; i++) {
              charset.push(cid ? id++ : strings.get(id++));
            }
          }
          break;
        default:
          throw new _util.FormatError('Unknown charset format');
      }
      var end = pos;
      var raw = bytes.subarray(start, end);
      return new CFFCharset(false, format, charset, raw);
    },
    parseEncoding: function CFFParser_parseEncoding(pos, properties, strings, charset) {
      var encoding = Object.create(null);
      var bytes = this.bytes;
      var predefined = false;
      var format, i, ii;
      var raw = null;
      function readSupplement() {
        var supplementsCount = bytes[pos++];
        for (i = 0; i < supplementsCount; i++) {
          var code = bytes[pos++];
          var sid = (bytes[pos++] << 8) + (bytes[pos++] & 0xff);
          encoding[code] = charset.indexOf(strings.get(sid));
        }
      }
      if (pos === 0 || pos === 1) {
        predefined = true;
        format = pos;
        var baseEncoding = pos ? _encodings.ExpertEncoding : _encodings.StandardEncoding;
        for (i = 0, ii = charset.length; i < ii; i++) {
          var index = baseEncoding.indexOf(charset[i]);
          if (index !== -1) {
            encoding[index] = i;
          }
        }
      } else {
        var dataStart = pos;
        format = bytes[pos++];
        switch (format & 0x7f) {
          case 0:
            var glyphsCount = bytes[pos++];
            for (i = 1; i <= glyphsCount; i++) {
              encoding[bytes[pos++]] = i;
            }
            break;
          case 1:
            var rangesCount = bytes[pos++];
            var gid = 1;
            for (i = 0; i < rangesCount; i++) {
              var start = bytes[pos++];
              var left = bytes[pos++];
              for (var j = start; j <= start + left; j++) {
                encoding[j] = gid++;
              }
            }
            break;
          default:
            throw new _util.FormatError(`Unknown encoding format: ${format} in CFF`);
        }
        var dataEnd = pos;
        if (format & 0x80) {
          bytes[dataStart] &= 0x7f;
          readSupplement();
        }
        raw = bytes.subarray(dataStart, dataEnd);
      }
      format = format & 0x7f;
      return new CFFEncoding(predefined, format, encoding, raw);
    },
    parseFDSelect: function CFFParser_parseFDSelect(pos, length) {
      var start = pos;
      var bytes = this.bytes;
      var format = bytes[pos++];
      var fdSelect = [],
          rawBytes;
      var i,
          invalidFirstGID = false;
      switch (format) {
        case 0:
          for (i = 0; i < length; ++i) {
            var id = bytes[pos++];
            fdSelect.push(id);
          }
          rawBytes = bytes.subarray(start, pos);
          break;
        case 3:
          var rangesCount = bytes[pos++] << 8 | bytes[pos++];
          for (i = 0; i < rangesCount; ++i) {
            var first = bytes[pos++] << 8 | bytes[pos++];
            if (i === 0 && first !== 0) {
              (0, _util.warn)('parseFDSelect: The first range must have a first GID of 0' + ' -- trying to recover.');
              invalidFirstGID = true;
              first = 0;
            }
            var fdIndex = bytes[pos++];
            var next = bytes[pos] << 8 | bytes[pos + 1];
            for (var j = first; j < next; ++j) {
              fdSelect.push(fdIndex);
            }
          }
          pos += 2;
          rawBytes = bytes.subarray(start, pos);
          if (invalidFirstGID) {
            rawBytes[3] = rawBytes[4] = 0;
          }
          break;
        default:
          throw new _util.FormatError(`parseFDSelect: Unknown format "${format}".`);
      }
      if (fdSelect.length !== length) {
        throw new _util.FormatError('parseFDSelect: Invalid font data.');
      }
      return new CFFFDSelect(fdSelect, rawBytes);
    }
  };
  return CFFParser;
}();
var CFF = function CFFClosure() {
  function CFF() {
    this.header = null;
    this.names = [];
    this.topDict = null;
    this.strings = new CFFStrings();
    this.globalSubrIndex = null;
    this.encoding = null;
    this.charset = null;
    this.charStrings = null;
    this.fdArray = [];
    this.fdSelect = null;
    this.isCIDFont = false;
  }
  return CFF;
}();
var CFFHeader = function CFFHeaderClosure() {
  function CFFHeader(major, minor, hdrSize, offSize) {
    this.major = major;
    this.minor = minor;
    this.hdrSize = hdrSize;
    this.offSize = offSize;
  }
  return CFFHeader;
}();
var CFFStrings = function CFFStringsClosure() {
  function CFFStrings() {
    this.strings = [];
  }
  CFFStrings.prototype = {
    get: function CFFStrings_get(index) {
      if (index >= 0 && index <= 390) {
        return CFFStandardStrings[index];
      }
      if (index - 391 <= this.strings.length) {
        return this.strings[index - 391];
      }
      return CFFStandardStrings[0];
    },
    add: function CFFStrings_add(value) {
      this.strings.push(value);
    },
    get count() {
      return this.strings.length;
    }
  };
  return CFFStrings;
}();
var CFFIndex = function CFFIndexClosure() {
  function CFFIndex() {
    this.objects = [];
    this.length = 0;
  }
  CFFIndex.prototype = {
    add: function CFFIndex_add(data) {
      this.length += data.length;
      this.objects.push(data);
    },
    set: function CFFIndex_set(index, data) {
      this.length += data.length - this.objects[index].length;
      this.objects[index] = data;
    },
    get: function CFFIndex_get(index) {
      return this.objects[index];
    },
    get count() {
      return this.objects.length;
    }
  };
  return CFFIndex;
}();
var CFFDict = function CFFDictClosure() {
  function CFFDict(tables, strings) {
    this.keyToNameMap = tables.keyToNameMap;
    this.nameToKeyMap = tables.nameToKeyMap;
    this.defaults = tables.defaults;
    this.types = tables.types;
    this.opcodes = tables.opcodes;
    this.order = tables.order;
    this.strings = strings;
    this.values = Object.create(null);
  }
  CFFDict.prototype = {
    setByKey: function CFFDict_setByKey(key, value) {
      if (!(key in this.keyToNameMap)) {
        return false;
      }
      var valueLength = value.length;
      if (valueLength === 0) {
        return true;
      }
      for (var i = 0; i < valueLength; i++) {
        if (isNaN(value[i])) {
          (0, _util.warn)('Invalid CFFDict value: "' + value + '" for key "' + key + '".');
          return true;
        }
      }
      var type = this.types[key];
      if (type === 'num' || type === 'sid' || type === 'offset') {
        value = value[0];
      }
      this.values[key] = value;
      return true;
    },
    setByName: function CFFDict_setByName(name, value) {
      if (!(name in this.nameToKeyMap)) {
        throw new _util.FormatError(`Invalid dictionary name "${name}"`);
      }
      this.values[this.nameToKeyMap[name]] = value;
    },
    hasName: function CFFDict_hasName(name) {
      return this.nameToKeyMap[name] in this.values;
    },
    getByName: function CFFDict_getByName(name) {
      if (!(name in this.nameToKeyMap)) {
        throw new _util.FormatError(`Invalid dictionary name ${name}"`);
      }
      var key = this.nameToKeyMap[name];
      if (!(key in this.values)) {
        return this.defaults[key];
      }
      return this.values[key];
    },
    removeByName: function CFFDict_removeByName(name) {
      delete this.values[this.nameToKeyMap[name]];
    }
  };
  CFFDict.createTables = function CFFDict_createTables(layout) {
    var tables = {
      keyToNameMap: {},
      nameToKeyMap: {},
      defaults: {},
      types: {},
      opcodes: {},
      order: []
    };
    for (var i = 0, ii = layout.length; i < ii; ++i) {
      var entry = layout[i];
      var key = Array.isArray(entry[0]) ? (entry[0][0] << 8) + entry[0][1] : entry[0];
      tables.keyToNameMap[key] = entry[1];
      tables.nameToKeyMap[entry[1]] = key;
      tables.types[key] = entry[2];
      tables.defaults[key] = entry[3];
      tables.opcodes[key] = Array.isArray(entry[0]) ? entry[0] : [entry[0]];
      tables.order.push(key);
    }
    return tables;
  };
  return CFFDict;
}();
var CFFTopDict = function CFFTopDictClosure() {
  var layout = [[[12, 30], 'ROS', ['sid', 'sid', 'num'], null], [[12, 20], 'SyntheticBase', 'num', null], [0, 'version', 'sid', null], [1, 'Notice', 'sid', null], [[12, 0], 'Copyright', 'sid', null], [2, 'FullName', 'sid', null], [3, 'FamilyName', 'sid', null], [4, 'Weight', 'sid', null], [[12, 1], 'isFixedPitch', 'num', 0], [[12, 2], 'ItalicAngle', 'num', 0], [[12, 3], 'UnderlinePosition', 'num', -100], [[12, 4], 'UnderlineThickness', 'num', 50], [[12, 5], 'PaintType', 'num', 0], [[12, 6], 'CharstringType', 'num', 2], [[12, 7], 'FontMatrix', ['num', 'num', 'num', 'num', 'num', 'num'], [0.001, 0, 0, 0.001, 0, 0]], [13, 'UniqueID', 'num', null], [5, 'FontBBox', ['num', 'num', 'num', 'num'], [0, 0, 0, 0]], [[12, 8], 'StrokeWidth', 'num', 0], [14, 'XUID', 'array', null], [15, 'charset', 'offset', 0], [16, 'Encoding', 'offset', 0], [17, 'CharStrings', 'offset', 0], [18, 'Private', ['offset', 'offset'], null], [[12, 21], 'PostScript', 'sid', null], [[12, 22], 'BaseFontName', 'sid', null], [[12, 23], 'BaseFontBlend', 'delta', null], [[12, 31], 'CIDFontVersion', 'num', 0], [[12, 32], 'CIDFontRevision', 'num', 0], [[12, 33], 'CIDFontType', 'num', 0], [[12, 34], 'CIDCount', 'num', 8720], [[12, 35], 'UIDBase', 'num', null], [[12, 37], 'FDSelect', 'offset', null], [[12, 36], 'FDArray', 'offset', null], [[12, 38], 'FontName', 'sid', null]];
  var tables = null;
  function CFFTopDict(strings) {
    if (tables === null) {
      tables = CFFDict.createTables(layout);
    }
    CFFDict.call(this, tables, strings);
    this.privateDict = null;
  }
  CFFTopDict.prototype = Object.create(CFFDict.prototype);
  return CFFTopDict;
}();
var CFFPrivateDict = function CFFPrivateDictClosure() {
  var layout = [[6, 'BlueValues', 'delta', null], [7, 'OtherBlues', 'delta', null], [8, 'FamilyBlues', 'delta', null], [9, 'FamilyOtherBlues', 'delta', null], [[12, 9], 'BlueScale', 'num', 0.039625], [[12, 10], 'BlueShift', 'num', 7], [[12, 11], 'BlueFuzz', 'num', 1], [10, 'StdHW', 'num', null], [11, 'StdVW', 'num', null], [[12, 12], 'StemSnapH', 'delta', null], [[12, 13], 'StemSnapV', 'delta', null], [[12, 14], 'ForceBold', 'num', 0], [[12, 17], 'LanguageGroup', 'num', 0], [[12, 18], 'ExpansionFactor', 'num', 0.06], [[12, 19], 'initialRandomSeed', 'num', 0], [20, 'defaultWidthX', 'num', 0], [21, 'nominalWidthX', 'num', 0], [19, 'Subrs', 'offset', null]];
  var tables = null;
  function CFFPrivateDict(strings) {
    if (tables === null) {
      tables = CFFDict.createTables(layout);
    }
    CFFDict.call(this, tables, strings);
    this.subrsIndex = null;
  }
  CFFPrivateDict.prototype = Object.create(CFFDict.prototype);
  return CFFPrivateDict;
}();
var CFFCharsetPredefinedTypes = {
  ISO_ADOBE: 0,
  EXPERT: 1,
  EXPERT_SUBSET: 2
};
var CFFCharset = function CFFCharsetClosure() {
  function CFFCharset(predefined, format, charset, raw) {
    this.predefined = predefined;
    this.format = format;
    this.charset = charset;
    this.raw = raw;
  }
  return CFFCharset;
}();
var CFFEncoding = function CFFEncodingClosure() {
  function CFFEncoding(predefined, format, encoding, raw) {
    this.predefined = predefined;
    this.format = format;
    this.encoding = encoding;
    this.raw = raw;
  }
  return CFFEncoding;
}();
var CFFFDSelect = function CFFFDSelectClosure() {
  function CFFFDSelect(fdSelect, raw) {
    this.fdSelect = fdSelect;
    this.raw = raw;
  }
  CFFFDSelect.prototype = {
    getFDIndex: function CFFFDSelect_get(glyphIndex) {
      if (glyphIndex < 0 || glyphIndex >= this.fdSelect.length) {
        return -1;
      }
      return this.fdSelect[glyphIndex];
    }
  };
  return CFFFDSelect;
}();
var CFFOffsetTracker = function CFFOffsetTrackerClosure() {
  function CFFOffsetTracker() {
    this.offsets = Object.create(null);
  }
  CFFOffsetTracker.prototype = {
    isTracking: function CFFOffsetTracker_isTracking(key) {
      return key in this.offsets;
    },
    track: function CFFOffsetTracker_track(key, location) {
      if (key in this.offsets) {
        throw new _util.FormatError(`Already tracking location of ${key}`);
      }
      this.offsets[key] = location;
    },
    offset: function CFFOffsetTracker_offset(value) {
      for (var key in this.offsets) {
        this.offsets[key] += value;
      }
    },
    setEntryLocation: function CFFOffsetTracker_setEntryLocation(key, values, output) {
      if (!(key in this.offsets)) {
        throw new _util.FormatError(`Not tracking location of ${key}`);
      }
      var data = output.data;
      var dataOffset = this.offsets[key];
      var size = 5;
      for (var i = 0, ii = values.length; i < ii; ++i) {
        var offset0 = i * size + dataOffset;
        var offset1 = offset0 + 1;
        var offset2 = offset0 + 2;
        var offset3 = offset0 + 3;
        var offset4 = offset0 + 4;
        if (data[offset0] !== 0x1d || data[offset1] !== 0 || data[offset2] !== 0 || data[offset3] !== 0 || data[offset4] !== 0) {
          throw new _util.FormatError('writing to an offset that is not empty');
        }
        var value = values[i];
        data[offset0] = 0x1d;
        data[offset1] = value >> 24 & 0xFF;
        data[offset2] = value >> 16 & 0xFF;
        data[offset3] = value >> 8 & 0xFF;
        data[offset4] = value & 0xFF;
      }
    }
  };
  return CFFOffsetTracker;
}();
var CFFCompiler = function CFFCompilerClosure() {
  function CFFCompiler(cff) {
    this.cff = cff;
  }
  CFFCompiler.prototype = {
    compile: function CFFCompiler_compile() {
      var cff = this.cff;
      var output = {
        data: [],
        length: 0,
        add: function CFFCompiler_add(data) {
          this.data = this.data.concat(data);
          this.length = this.data.length;
        }
      };
      var header = this.compileHeader(cff.header);
      output.add(header);
      var nameIndex = this.compileNameIndex(cff.names);
      output.add(nameIndex);
      if (cff.isCIDFont) {
        if (cff.topDict.hasName('FontMatrix')) {
          var base = cff.topDict.getByName('FontMatrix');
          cff.topDict.removeByName('FontMatrix');
          for (var i = 0, ii = cff.fdArray.length; i < ii; i++) {
            var subDict = cff.fdArray[i];
            var matrix = base.slice(0);
            if (subDict.hasName('FontMatrix')) {
              matrix = _util.Util.transform(matrix, subDict.getByName('FontMatrix'));
            }
            subDict.setByName('FontMatrix', matrix);
          }
        }
      }
      var compiled = this.compileTopDicts([cff.topDict], output.length, cff.isCIDFont);
      output.add(compiled.output);
      var topDictTracker = compiled.trackers[0];
      var stringIndex = this.compileStringIndex(cff.strings.strings);
      output.add(stringIndex);
      var globalSubrIndex = this.compileIndex(cff.globalSubrIndex);
      output.add(globalSubrIndex);
      if (cff.encoding && cff.topDict.hasName('Encoding')) {
        if (cff.encoding.predefined) {
          topDictTracker.setEntryLocation('Encoding', [cff.encoding.format], output);
        } else {
          var encoding = this.compileEncoding(cff.encoding);
          topDictTracker.setEntryLocation('Encoding', [output.length], output);
          output.add(encoding);
        }
      }
      if (cff.charset && cff.topDict.hasName('charset')) {
        if (cff.charset.predefined) {
          topDictTracker.setEntryLocation('charset', [cff.charset.format], output);
        } else {
          var charset = this.compileCharset(cff.charset);
          topDictTracker.setEntryLocation('charset', [output.length], output);
          output.add(charset);
        }
      }
      var charStrings = this.compileCharStrings(cff.charStrings);
      topDictTracker.setEntryLocation('CharStrings', [output.length], output);
      output.add(charStrings);
      if (cff.isCIDFont) {
        topDictTracker.setEntryLocation('FDSelect', [output.length], output);
        var fdSelect = this.compileFDSelect(cff.fdSelect.raw);
        output.add(fdSelect);
        compiled = this.compileTopDicts(cff.fdArray, output.length, true);
        topDictTracker.setEntryLocation('FDArray', [output.length], output);
        output.add(compiled.output);
        var fontDictTrackers = compiled.trackers;
        this.compilePrivateDicts(cff.fdArray, fontDictTrackers, output);
      }
      this.compilePrivateDicts([cff.topDict], [topDictTracker], output);
      output.add([0]);
      return output.data;
    },
    encodeNumber: function CFFCompiler_encodeNumber(value) {
      if (parseFloat(value) === parseInt(value, 10) && !isNaN(value)) {
        return this.encodeInteger(value);
      }
      return this.encodeFloat(value);
    },
    encodeFloat: function CFFCompiler_encodeFloat(num) {
      var value = num.toString();
      var m = /\.(\d*?)(?:9{5,20}|0{5,20})\d{0,2}(?:e(.+)|$)/.exec(value);
      if (m) {
        var epsilon = parseFloat('1e' + ((m[2] ? +m[2] : 0) + m[1].length));
        value = (Math.round(num * epsilon) / epsilon).toString();
      }
      var nibbles = '';
      var i, ii;
      for (i = 0, ii = value.length; i < ii; ++i) {
        var a = value[i];
        if (a === 'e') {
          nibbles += value[++i] === '-' ? 'c' : 'b';
        } else if (a === '.') {
          nibbles += 'a';
        } else if (a === '-') {
          nibbles += 'e';
        } else {
          nibbles += a;
        }
      }
      nibbles += nibbles.length & 1 ? 'f' : 'ff';
      var out = [30];
      for (i = 0, ii = nibbles.length; i < ii; i += 2) {
        out.push(parseInt(nibbles.substr(i, 2), 16));
      }
      return out;
    },
    encodeInteger: function CFFCompiler_encodeInteger(value) {
      var code;
      if (value >= -107 && value <= 107) {
        code = [value + 139];
      } else if (value >= 108 && value <= 1131) {
        value = value - 108;
        code = [(value >> 8) + 247, value & 0xFF];
      } else if (value >= -1131 && value <= -108) {
        value = -value - 108;
        code = [(value >> 8) + 251, value & 0xFF];
      } else if (value >= -32768 && value <= 32767) {
        code = [0x1c, value >> 8 & 0xFF, value & 0xFF];
      } else {
        code = [0x1d, value >> 24 & 0xFF, value >> 16 & 0xFF, value >> 8 & 0xFF, value & 0xFF];
      }
      return code;
    },
    compileHeader: function CFFCompiler_compileHeader(header) {
      return [header.major, header.minor, header.hdrSize, header.offSize];
    },
    compileNameIndex: function CFFCompiler_compileNameIndex(names) {
      var nameIndex = new CFFIndex();
      for (var i = 0, ii = names.length; i < ii; ++i) {
        var name = names[i];
        var length = Math.min(name.length, 127);
        var sanitizedName = new Array(length);
        for (var j = 0; j < length; j++) {
          var char = name[j];
          if (char < '!' || char > '~' || char === '[' || char === ']' || char === '(' || char === ')' || char === '{' || char === '}' || char === '<' || char === '>' || char === '/' || char === '%') {
            char = '_';
          }
          sanitizedName[j] = char;
        }
        sanitizedName = sanitizedName.join('');
        if (sanitizedName === '') {
          sanitizedName = 'Bad_Font_Name';
        }
        nameIndex.add((0, _util.stringToBytes)(sanitizedName));
      }
      return this.compileIndex(nameIndex);
    },
    compileTopDicts: function CFFCompiler_compileTopDicts(dicts, length, removeCidKeys) {
      var fontDictTrackers = [];
      var fdArrayIndex = new CFFIndex();
      for (var i = 0, ii = dicts.length; i < ii; ++i) {
        var fontDict = dicts[i];
        if (removeCidKeys) {
          fontDict.removeByName('CIDFontVersion');
          fontDict.removeByName('CIDFontRevision');
          fontDict.removeByName('CIDFontType');
          fontDict.removeByName('CIDCount');
          fontDict.removeByName('UIDBase');
        }
        var fontDictTracker = new CFFOffsetTracker();
        var fontDictData = this.compileDict(fontDict, fontDictTracker);
        fontDictTrackers.push(fontDictTracker);
        fdArrayIndex.add(fontDictData);
        fontDictTracker.offset(length);
      }
      fdArrayIndex = this.compileIndex(fdArrayIndex, fontDictTrackers);
      return {
        trackers: fontDictTrackers,
        output: fdArrayIndex
      };
    },
    compilePrivateDicts: function CFFCompiler_compilePrivateDicts(dicts, trackers, output) {
      for (var i = 0, ii = dicts.length; i < ii; ++i) {
        var fontDict = dicts[i];
        var privateDict = fontDict.privateDict;
        if (!privateDict || !fontDict.hasName('Private')) {
          throw new _util.FormatError('There must be a private dictionary.');
        }
        var privateDictTracker = new CFFOffsetTracker();
        var privateDictData = this.compileDict(privateDict, privateDictTracker);
        var outputLength = output.length;
        privateDictTracker.offset(outputLength);
        if (!privateDictData.length) {
          outputLength = 0;
        }
        trackers[i].setEntryLocation('Private', [privateDictData.length, outputLength], output);
        output.add(privateDictData);
        if (privateDict.subrsIndex && privateDict.hasName('Subrs')) {
          var subrs = this.compileIndex(privateDict.subrsIndex);
          privateDictTracker.setEntryLocation('Subrs', [privateDictData.length], output);
          output.add(subrs);
        }
      }
    },
    compileDict: function CFFCompiler_compileDict(dict, offsetTracker) {
      var out = [];
      var order = dict.order;
      for (var i = 0; i < order.length; ++i) {
        var key = order[i];
        if (!(key in dict.values)) {
          continue;
        }
        var values = dict.values[key];
        var types = dict.types[key];
        if (!Array.isArray(types)) {
          types = [types];
        }
        if (!Array.isArray(values)) {
          values = [values];
        }
        if (values.length === 0) {
          continue;
        }
        for (var j = 0, jj = types.length; j < jj; ++j) {
          var type = types[j];
          var value = values[j];
          switch (type) {
            case 'num':
            case 'sid':
              out = out.concat(this.encodeNumber(value));
              break;
            case 'offset':
              var name = dict.keyToNameMap[key];
              if (!offsetTracker.isTracking(name)) {
                offsetTracker.track(name, out.length);
              }
              out = out.concat([0x1d, 0, 0, 0, 0]);
              break;
            case 'array':
            case 'delta':
              out = out.concat(this.encodeNumber(value));
              for (var k = 1, kk = values.length; k < kk; ++k) {
                out = out.concat(this.encodeNumber(values[k]));
              }
              break;
            default:
              throw new _util.FormatError(`Unknown data type of ${type}`);
          }
        }
        out = out.concat(dict.opcodes[key]);
      }
      return out;
    },
    compileStringIndex: function CFFCompiler_compileStringIndex(strings) {
      var stringIndex = new CFFIndex();
      for (var i = 0, ii = strings.length; i < ii; ++i) {
        stringIndex.add((0, _util.stringToBytes)(strings[i]));
      }
      return this.compileIndex(stringIndex);
    },
    compileGlobalSubrIndex: function CFFCompiler_compileGlobalSubrIndex() {
      var globalSubrIndex = this.cff.globalSubrIndex;
      this.out.writeByteArray(this.compileIndex(globalSubrIndex));
    },
    compileCharStrings: function CFFCompiler_compileCharStrings(charStrings) {
      return this.compileIndex(charStrings);
    },
    compileCharset: function CFFCompiler_compileCharset(charset) {
      return this.compileTypedArray(charset.raw);
    },
    compileEncoding: function CFFCompiler_compileEncoding(encoding) {
      return this.compileTypedArray(encoding.raw);
    },
    compileFDSelect: function CFFCompiler_compileFDSelect(fdSelect) {
      return this.compileTypedArray(fdSelect);
    },
    compileTypedArray: function CFFCompiler_compileTypedArray(data) {
      var out = [];
      for (var i = 0, ii = data.length; i < ii; ++i) {
        out[i] = data[i];
      }
      return out;
    },
    compileIndex: function CFFCompiler_compileIndex(index, trackers) {
      trackers = trackers || [];
      var objects = index.objects;
      var count = objects.length;
      if (count === 0) {
        return [0, 0, 0];
      }
      var data = [count >> 8 & 0xFF, count & 0xff];
      var lastOffset = 1,
          i;
      for (i = 0; i < count; ++i) {
        lastOffset += objects[i].length;
      }
      var offsetSize;
      if (lastOffset < 0x100) {
        offsetSize = 1;
      } else if (lastOffset < 0x10000) {
        offsetSize = 2;
      } else if (lastOffset < 0x1000000) {
        offsetSize = 3;
      } else {
        offsetSize = 4;
      }
      data.push(offsetSize);
      var relativeOffset = 1;
      for (i = 0; i < count + 1; i++) {
        if (offsetSize === 1) {
          data.push(relativeOffset & 0xFF);
        } else if (offsetSize === 2) {
          data.push(relativeOffset >> 8 & 0xFF, relativeOffset & 0xFF);
        } else if (offsetSize === 3) {
          data.push(relativeOffset >> 16 & 0xFF, relativeOffset >> 8 & 0xFF, relativeOffset & 0xFF);
        } else {
          data.push(relativeOffset >>> 24 & 0xFF, relativeOffset >> 16 & 0xFF, relativeOffset >> 8 & 0xFF, relativeOffset & 0xFF);
        }
        if (objects[i]) {
          relativeOffset += objects[i].length;
        }
      }
      for (i = 0; i < count; i++) {
        if (trackers[i]) {
          trackers[i].offset(data.length);
        }
        for (var j = 0, jj = objects[i].length; j < jj; j++) {
          data.push(objects[i][j]);
        }
      }
      return data;
    }
  };
  return CFFCompiler;
}();
exports.CFFStandardStrings = CFFStandardStrings;
exports.CFFParser = CFFParser;
exports.CFF = CFF;
exports.CFFHeader = CFFHeader;
exports.CFFStrings = CFFStrings;
exports.CFFIndex = CFFIndex;
exports.CFFCharset = CFFCharset;
exports.CFFTopDict = CFFTopDict;
exports.CFFPrivateDict = CFFPrivateDict;
exports.CFFCompiler = CFFCompiler;

/***/ }),
/* 16 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getSupplementalGlyphMapForCalibri = exports.getSupplementalGlyphMapForArialBlack = exports.getGlyphMapForStandardFonts = exports.getSymbolsFonts = exports.getSerifFonts = exports.getNonStdFontMap = exports.getStdFontMap = undefined;

var _util = __w_pdfjs_require__(0);

var getStdFontMap = (0, _util.getLookupTableFactory)(function (t) {
  t['ArialNarrow'] = 'Helvetica';
  t['ArialNarrow-Bold'] = 'Helvetica-Bold';
  t['ArialNarrow-BoldItalic'] = 'Helvetica-BoldOblique';
  t['ArialNarrow-Italic'] = 'Helvetica-Oblique';
  t['ArialBlack'] = 'Helvetica';
  t['ArialBlack-Bold'] = 'Helvetica-Bold';
  t['ArialBlack-BoldItalic'] = 'Helvetica-BoldOblique';
  t['ArialBlack-Italic'] = 'Helvetica-Oblique';
  t['Arial-Black'] = 'Helvetica';
  t['Arial-Black-Bold'] = 'Helvetica-Bold';
  t['Arial-Black-BoldItalic'] = 'Helvetica-BoldOblique';
  t['Arial-Black-Italic'] = 'Helvetica-Oblique';
  t['Arial'] = 'Helvetica';
  t['Arial-Bold'] = 'Helvetica-Bold';
  t['Arial-BoldItalic'] = 'Helvetica-BoldOblique';
  t['Arial-Italic'] = 'Helvetica-Oblique';
  t['Arial-BoldItalicMT'] = 'Helvetica-BoldOblique';
  t['Arial-BoldMT'] = 'Helvetica-Bold';
  t['Arial-ItalicMT'] = 'Helvetica-Oblique';
  t['ArialMT'] = 'Helvetica';
  t['Courier-Bold'] = 'Courier-Bold';
  t['Courier-BoldItalic'] = 'Courier-BoldOblique';
  t['Courier-Italic'] = 'Courier-Oblique';
  t['CourierNew'] = 'Courier';
  t['CourierNew-Bold'] = 'Courier-Bold';
  t['CourierNew-BoldItalic'] = 'Courier-BoldOblique';
  t['CourierNew-Italic'] = 'Courier-Oblique';
  t['CourierNewPS-BoldItalicMT'] = 'Courier-BoldOblique';
  t['CourierNewPS-BoldMT'] = 'Courier-Bold';
  t['CourierNewPS-ItalicMT'] = 'Courier-Oblique';
  t['CourierNewPSMT'] = 'Courier';
  t['Helvetica'] = 'Helvetica';
  t['Helvetica-Bold'] = 'Helvetica-Bold';
  t['Helvetica-BoldItalic'] = 'Helvetica-BoldOblique';
  t['Helvetica-BoldOblique'] = 'Helvetica-BoldOblique';
  t['Helvetica-Italic'] = 'Helvetica-Oblique';
  t['Helvetica-Oblique'] = 'Helvetica-Oblique';
  t['SegoeUISymbol'] = 'Helvetica';
  t['Symbol-Bold'] = 'Symbol';
  t['Symbol-BoldItalic'] = 'Symbol';
  t['Symbol-Italic'] = 'Symbol';
  t['TimesNewRoman'] = 'Times-Roman';
  t['TimesNewRoman-Bold'] = 'Times-Bold';
  t['TimesNewRoman-BoldItalic'] = 'Times-BoldItalic';
  t['TimesNewRoman-Italic'] = 'Times-Italic';
  t['TimesNewRomanPS'] = 'Times-Roman';
  t['TimesNewRomanPS-Bold'] = 'Times-Bold';
  t['TimesNewRomanPS-BoldItalic'] = 'Times-BoldItalic';
  t['TimesNewRomanPS-BoldItalicMT'] = 'Times-BoldItalic';
  t['TimesNewRomanPS-BoldMT'] = 'Times-Bold';
  t['TimesNewRomanPS-Italic'] = 'Times-Italic';
  t['TimesNewRomanPS-ItalicMT'] = 'Times-Italic';
  t['TimesNewRomanPSMT'] = 'Times-Roman';
  t['TimesNewRomanPSMT-Bold'] = 'Times-Bold';
  t['TimesNewRomanPSMT-BoldItalic'] = 'Times-BoldItalic';
  t['TimesNewRomanPSMT-Italic'] = 'Times-Italic';
});
var getNonStdFontMap = (0, _util.getLookupTableFactory)(function (t) {
  t['Calibri'] = 'Helvetica';
  t['Calibri-Bold'] = 'Helvetica-Bold';
  t['Calibri-BoldItalic'] = 'Helvetica-BoldOblique';
  t['Calibri-Italic'] = 'Helvetica-Oblique';
  t['CenturyGothic'] = 'Helvetica';
  t['CenturyGothic-Bold'] = 'Helvetica-Bold';
  t['CenturyGothic-BoldItalic'] = 'Helvetica-BoldOblique';
  t['CenturyGothic-Italic'] = 'Helvetica-Oblique';
  t['ComicSansMS'] = 'Comic Sans MS';
  t['ComicSansMS-Bold'] = 'Comic Sans MS-Bold';
  t['ComicSansMS-BoldItalic'] = 'Comic Sans MS-BoldItalic';
  t['ComicSansMS-Italic'] = 'Comic Sans MS-Italic';
  t['LucidaConsole'] = 'Courier';
  t['LucidaConsole-Bold'] = 'Courier-Bold';
  t['LucidaConsole-BoldItalic'] = 'Courier-BoldOblique';
  t['LucidaConsole-Italic'] = 'Courier-Oblique';
  t['LucidaSans-Demi'] = 'Helvetica-Bold';
  t['MS-Gothic'] = 'MS Gothic';
  t['MS-Gothic-Bold'] = 'MS Gothic-Bold';
  t['MS-Gothic-BoldItalic'] = 'MS Gothic-BoldItalic';
  t['MS-Gothic-Italic'] = 'MS Gothic-Italic';
  t['MS-Mincho'] = 'MS Mincho';
  t['MS-Mincho-Bold'] = 'MS Mincho-Bold';
  t['MS-Mincho-BoldItalic'] = 'MS Mincho-BoldItalic';
  t['MS-Mincho-Italic'] = 'MS Mincho-Italic';
  t['MS-PGothic'] = 'MS PGothic';
  t['MS-PGothic-Bold'] = 'MS PGothic-Bold';
  t['MS-PGothic-BoldItalic'] = 'MS PGothic-BoldItalic';
  t['MS-PGothic-Italic'] = 'MS PGothic-Italic';
  t['MS-PMincho'] = 'MS PMincho';
  t['MS-PMincho-Bold'] = 'MS PMincho-Bold';
  t['MS-PMincho-BoldItalic'] = 'MS PMincho-BoldItalic';
  t['MS-PMincho-Italic'] = 'MS PMincho-Italic';
  t['NuptialScript'] = 'Times-Italic';
  t['Wingdings'] = 'ZapfDingbats';
});
var getSerifFonts = (0, _util.getLookupTableFactory)(function (t) {
  t['Adobe Jenson'] = true;
  t['Adobe Text'] = true;
  t['Albertus'] = true;
  t['Aldus'] = true;
  t['Alexandria'] = true;
  t['Algerian'] = true;
  t['American Typewriter'] = true;
  t['Antiqua'] = true;
  t['Apex'] = true;
  t['Arno'] = true;
  t['Aster'] = true;
  t['Aurora'] = true;
  t['Baskerville'] = true;
  t['Bell'] = true;
  t['Bembo'] = true;
  t['Bembo Schoolbook'] = true;
  t['Benguiat'] = true;
  t['Berkeley Old Style'] = true;
  t['Bernhard Modern'] = true;
  t['Berthold City'] = true;
  t['Bodoni'] = true;
  t['Bauer Bodoni'] = true;
  t['Book Antiqua'] = true;
  t['Bookman'] = true;
  t['Bordeaux Roman'] = true;
  t['Californian FB'] = true;
  t['Calisto'] = true;
  t['Calvert'] = true;
  t['Capitals'] = true;
  t['Cambria'] = true;
  t['Cartier'] = true;
  t['Caslon'] = true;
  t['Catull'] = true;
  t['Centaur'] = true;
  t['Century Old Style'] = true;
  t['Century Schoolbook'] = true;
  t['Chaparral'] = true;
  t['Charis SIL'] = true;
  t['Cheltenham'] = true;
  t['Cholla Slab'] = true;
  t['Clarendon'] = true;
  t['Clearface'] = true;
  t['Cochin'] = true;
  t['Colonna'] = true;
  t['Computer Modern'] = true;
  t['Concrete Roman'] = true;
  t['Constantia'] = true;
  t['Cooper Black'] = true;
  t['Corona'] = true;
  t['Ecotype'] = true;
  t['Egyptienne'] = true;
  t['Elephant'] = true;
  t['Excelsior'] = true;
  t['Fairfield'] = true;
  t['FF Scala'] = true;
  t['Folkard'] = true;
  t['Footlight'] = true;
  t['FreeSerif'] = true;
  t['Friz Quadrata'] = true;
  t['Garamond'] = true;
  t['Gentium'] = true;
  t['Georgia'] = true;
  t['Gloucester'] = true;
  t['Goudy Old Style'] = true;
  t['Goudy Schoolbook'] = true;
  t['Goudy Pro Font'] = true;
  t['Granjon'] = true;
  t['Guardian Egyptian'] = true;
  t['Heather'] = true;
  t['Hercules'] = true;
  t['High Tower Text'] = true;
  t['Hiroshige'] = true;
  t['Hoefler Text'] = true;
  t['Humana Serif'] = true;
  t['Imprint'] = true;
  t['Ionic No. 5'] = true;
  t['Janson'] = true;
  t['Joanna'] = true;
  t['Korinna'] = true;
  t['Lexicon'] = true;
  t['Liberation Serif'] = true;
  t['Linux Libertine'] = true;
  t['Literaturnaya'] = true;
  t['Lucida'] = true;
  t['Lucida Bright'] = true;
  t['Melior'] = true;
  t['Memphis'] = true;
  t['Miller'] = true;
  t['Minion'] = true;
  t['Modern'] = true;
  t['Mona Lisa'] = true;
  t['Mrs Eaves'] = true;
  t['MS Serif'] = true;
  t['Museo Slab'] = true;
  t['New York'] = true;
  t['Nimbus Roman'] = true;
  t['NPS Rawlinson Roadway'] = true;
  t['NuptialScript'] = true;
  t['Palatino'] = true;
  t['Perpetua'] = true;
  t['Plantin'] = true;
  t['Plantin Schoolbook'] = true;
  t['Playbill'] = true;
  t['Poor Richard'] = true;
  t['Rawlinson Roadway'] = true;
  t['Renault'] = true;
  t['Requiem'] = true;
  t['Rockwell'] = true;
  t['Roman'] = true;
  t['Rotis Serif'] = true;
  t['Sabon'] = true;
  t['Scala'] = true;
  t['Seagull'] = true;
  t['Sistina'] = true;
  t['Souvenir'] = true;
  t['STIX'] = true;
  t['Stone Informal'] = true;
  t['Stone Serif'] = true;
  t['Sylfaen'] = true;
  t['Times'] = true;
  t['Trajan'] = true;
  t['Trinité'] = true;
  t['Trump Mediaeval'] = true;
  t['Utopia'] = true;
  t['Vale Type'] = true;
  t['Bitstream Vera'] = true;
  t['Vera Serif'] = true;
  t['Versailles'] = true;
  t['Wanted'] = true;
  t['Weiss'] = true;
  t['Wide Latin'] = true;
  t['Windsor'] = true;
  t['XITS'] = true;
});
var getSymbolsFonts = (0, _util.getLookupTableFactory)(function (t) {
  t['Dingbats'] = true;
  t['Symbol'] = true;
  t['ZapfDingbats'] = true;
});
var getGlyphMapForStandardFonts = (0, _util.getLookupTableFactory)(function (t) {
  t[2] = 10;
  t[3] = 32;
  t[4] = 33;
  t[5] = 34;
  t[6] = 35;
  t[7] = 36;
  t[8] = 37;
  t[9] = 38;
  t[10] = 39;
  t[11] = 40;
  t[12] = 41;
  t[13] = 42;
  t[14] = 43;
  t[15] = 44;
  t[16] = 45;
  t[17] = 46;
  t[18] = 47;
  t[19] = 48;
  t[20] = 49;
  t[21] = 50;
  t[22] = 51;
  t[23] = 52;
  t[24] = 53;
  t[25] = 54;
  t[26] = 55;
  t[27] = 56;
  t[28] = 57;
  t[29] = 58;
  t[30] = 894;
  t[31] = 60;
  t[32] = 61;
  t[33] = 62;
  t[34] = 63;
  t[35] = 64;
  t[36] = 65;
  t[37] = 66;
  t[38] = 67;
  t[39] = 68;
  t[40] = 69;
  t[41] = 70;
  t[42] = 71;
  t[43] = 72;
  t[44] = 73;
  t[45] = 74;
  t[46] = 75;
  t[47] = 76;
  t[48] = 77;
  t[49] = 78;
  t[50] = 79;
  t[51] = 80;
  t[52] = 81;
  t[53] = 82;
  t[54] = 83;
  t[55] = 84;
  t[56] = 85;
  t[57] = 86;
  t[58] = 87;
  t[59] = 88;
  t[60] = 89;
  t[61] = 90;
  t[62] = 91;
  t[63] = 92;
  t[64] = 93;
  t[65] = 94;
  t[66] = 95;
  t[67] = 96;
  t[68] = 97;
  t[69] = 98;
  t[70] = 99;
  t[71] = 100;
  t[72] = 101;
  t[73] = 102;
  t[74] = 103;
  t[75] = 104;
  t[76] = 105;
  t[77] = 106;
  t[78] = 107;
  t[79] = 108;
  t[80] = 109;
  t[81] = 110;
  t[82] = 111;
  t[83] = 112;
  t[84] = 113;
  t[85] = 114;
  t[86] = 115;
  t[87] = 116;
  t[88] = 117;
  t[89] = 118;
  t[90] = 119;
  t[91] = 120;
  t[92] = 121;
  t[93] = 122;
  t[94] = 123;
  t[95] = 124;
  t[96] = 125;
  t[97] = 126;
  t[98] = 196;
  t[99] = 197;
  t[100] = 199;
  t[101] = 201;
  t[102] = 209;
  t[103] = 214;
  t[104] = 220;
  t[105] = 225;
  t[106] = 224;
  t[107] = 226;
  t[108] = 228;
  t[109] = 227;
  t[110] = 229;
  t[111] = 231;
  t[112] = 233;
  t[113] = 232;
  t[114] = 234;
  t[115] = 235;
  t[116] = 237;
  t[117] = 236;
  t[118] = 238;
  t[119] = 239;
  t[120] = 241;
  t[121] = 243;
  t[122] = 242;
  t[123] = 244;
  t[124] = 246;
  t[125] = 245;
  t[126] = 250;
  t[127] = 249;
  t[128] = 251;
  t[129] = 252;
  t[130] = 8224;
  t[131] = 176;
  t[132] = 162;
  t[133] = 163;
  t[134] = 167;
  t[135] = 8226;
  t[136] = 182;
  t[137] = 223;
  t[138] = 174;
  t[139] = 169;
  t[140] = 8482;
  t[141] = 180;
  t[142] = 168;
  t[143] = 8800;
  t[144] = 198;
  t[145] = 216;
  t[146] = 8734;
  t[147] = 177;
  t[148] = 8804;
  t[149] = 8805;
  t[150] = 165;
  t[151] = 181;
  t[152] = 8706;
  t[153] = 8721;
  t[154] = 8719;
  t[156] = 8747;
  t[157] = 170;
  t[158] = 186;
  t[159] = 8486;
  t[160] = 230;
  t[161] = 248;
  t[162] = 191;
  t[163] = 161;
  t[164] = 172;
  t[165] = 8730;
  t[166] = 402;
  t[167] = 8776;
  t[168] = 8710;
  t[169] = 171;
  t[170] = 187;
  t[171] = 8230;
  t[210] = 218;
  t[223] = 711;
  t[224] = 321;
  t[225] = 322;
  t[227] = 353;
  t[229] = 382;
  t[234] = 253;
  t[252] = 263;
  t[253] = 268;
  t[254] = 269;
  t[258] = 258;
  t[260] = 260;
  t[261] = 261;
  t[265] = 280;
  t[266] = 281;
  t[268] = 283;
  t[269] = 313;
  t[275] = 323;
  t[276] = 324;
  t[278] = 328;
  t[284] = 345;
  t[285] = 346;
  t[286] = 347;
  t[292] = 367;
  t[295] = 377;
  t[296] = 378;
  t[298] = 380;
  t[305] = 963;
  t[306] = 964;
  t[307] = 966;
  t[308] = 8215;
  t[309] = 8252;
  t[310] = 8319;
  t[311] = 8359;
  t[312] = 8592;
  t[313] = 8593;
  t[337] = 9552;
  t[493] = 1039;
  t[494] = 1040;
  t[705] = 1524;
  t[706] = 8362;
  t[710] = 64288;
  t[711] = 64298;
  t[759] = 1617;
  t[761] = 1776;
  t[763] = 1778;
  t[775] = 1652;
  t[777] = 1764;
  t[778] = 1780;
  t[779] = 1781;
  t[780] = 1782;
  t[782] = 771;
  t[783] = 64726;
  t[786] = 8363;
  t[788] = 8532;
  t[790] = 768;
  t[791] = 769;
  t[792] = 768;
  t[795] = 803;
  t[797] = 64336;
  t[798] = 64337;
  t[799] = 64342;
  t[800] = 64343;
  t[801] = 64344;
  t[802] = 64345;
  t[803] = 64362;
  t[804] = 64363;
  t[805] = 64364;
  t[2424] = 7821;
  t[2425] = 7822;
  t[2426] = 7823;
  t[2427] = 7824;
  t[2428] = 7825;
  t[2429] = 7826;
  t[2430] = 7827;
  t[2433] = 7682;
  t[2678] = 8045;
  t[2679] = 8046;
  t[2830] = 1552;
  t[2838] = 686;
  t[2840] = 751;
  t[2842] = 753;
  t[2843] = 754;
  t[2844] = 755;
  t[2846] = 757;
  t[2856] = 767;
  t[2857] = 848;
  t[2858] = 849;
  t[2862] = 853;
  t[2863] = 854;
  t[2864] = 855;
  t[2865] = 861;
  t[2866] = 862;
  t[2906] = 7460;
  t[2908] = 7462;
  t[2909] = 7463;
  t[2910] = 7464;
  t[2912] = 7466;
  t[2913] = 7467;
  t[2914] = 7468;
  t[2916] = 7470;
  t[2917] = 7471;
  t[2918] = 7472;
  t[2920] = 7474;
  t[2921] = 7475;
  t[2922] = 7476;
  t[2924] = 7478;
  t[2925] = 7479;
  t[2926] = 7480;
  t[2928] = 7482;
  t[2929] = 7483;
  t[2930] = 7484;
  t[2932] = 7486;
  t[2933] = 7487;
  t[2934] = 7488;
  t[2936] = 7490;
  t[2937] = 7491;
  t[2938] = 7492;
  t[2940] = 7494;
  t[2941] = 7495;
  t[2942] = 7496;
  t[2944] = 7498;
  t[2946] = 7500;
  t[2948] = 7502;
  t[2950] = 7504;
  t[2951] = 7505;
  t[2952] = 7506;
  t[2954] = 7508;
  t[2955] = 7509;
  t[2956] = 7510;
  t[2958] = 7512;
  t[2959] = 7513;
  t[2960] = 7514;
  t[2962] = 7516;
  t[2963] = 7517;
  t[2964] = 7518;
  t[2966] = 7520;
  t[2967] = 7521;
  t[2968] = 7522;
  t[2970] = 7524;
  t[2971] = 7525;
  t[2972] = 7526;
  t[2974] = 7528;
  t[2975] = 7529;
  t[2976] = 7530;
  t[2978] = 1537;
  t[2979] = 1538;
  t[2980] = 1539;
  t[2982] = 1549;
  t[2983] = 1551;
  t[2984] = 1552;
  t[2986] = 1554;
  t[2987] = 1555;
  t[2988] = 1556;
  t[2990] = 1623;
  t[2991] = 1624;
  t[2995] = 1775;
  t[2999] = 1791;
  t[3002] = 64290;
  t[3003] = 64291;
  t[3004] = 64292;
  t[3006] = 64294;
  t[3007] = 64295;
  t[3008] = 64296;
  t[3011] = 1900;
  t[3014] = 8223;
  t[3015] = 8244;
  t[3017] = 7532;
  t[3018] = 7533;
  t[3019] = 7534;
  t[3075] = 7590;
  t[3076] = 7591;
  t[3079] = 7594;
  t[3080] = 7595;
  t[3083] = 7598;
  t[3084] = 7599;
  t[3087] = 7602;
  t[3088] = 7603;
  t[3091] = 7606;
  t[3092] = 7607;
  t[3095] = 7610;
  t[3096] = 7611;
  t[3099] = 7614;
  t[3100] = 7615;
  t[3103] = 7618;
  t[3104] = 7619;
  t[3107] = 8337;
  t[3108] = 8338;
  t[3116] = 1884;
  t[3119] = 1885;
  t[3120] = 1885;
  t[3123] = 1886;
  t[3124] = 1886;
  t[3127] = 1887;
  t[3128] = 1887;
  t[3131] = 1888;
  t[3132] = 1888;
  t[3135] = 1889;
  t[3136] = 1889;
  t[3139] = 1890;
  t[3140] = 1890;
  t[3143] = 1891;
  t[3144] = 1891;
  t[3147] = 1892;
  t[3148] = 1892;
  t[3153] = 580;
  t[3154] = 581;
  t[3157] = 584;
  t[3158] = 585;
  t[3161] = 588;
  t[3162] = 589;
  t[3165] = 891;
  t[3166] = 892;
  t[3169] = 1274;
  t[3170] = 1275;
  t[3173] = 1278;
  t[3174] = 1279;
  t[3181] = 7622;
  t[3182] = 7623;
  t[3282] = 11799;
  t[3316] = 578;
  t[3379] = 42785;
  t[3393] = 1159;
  t[3416] = 8377;
});
var getSupplementalGlyphMapForArialBlack = (0, _util.getLookupTableFactory)(function (t) {
  t[227] = 322;
  t[264] = 261;
  t[291] = 346;
});
let getSupplementalGlyphMapForCalibri = (0, _util.getLookupTableFactory)(function (t) {
  t[1] = 32;
  t[4] = 65;
  t[17] = 66;
  t[18] = 67;
  t[24] = 68;
  t[28] = 69;
  t[38] = 70;
  t[39] = 71;
  t[44] = 72;
  t[47] = 73;
  t[58] = 74;
  t[60] = 75;
  t[62] = 76;
  t[68] = 77;
  t[69] = 78;
  t[75] = 79;
  t[87] = 80;
  t[89] = 81;
  t[90] = 82;
  t[94] = 83;
  t[100] = 84;
  t[104] = 85;
  t[115] = 86;
  t[116] = 87;
  t[121] = 88;
  t[122] = 89;
  t[127] = 90;
  t[258] = 97;
  t[268] = 261;
  t[271] = 98;
  t[272] = 99;
  t[273] = 263;
  t[282] = 100;
  t[286] = 101;
  t[295] = 281;
  t[296] = 102;
  t[336] = 103;
  t[346] = 104;
  t[349] = 105;
  t[361] = 106;
  t[364] = 107;
  t[367] = 108;
  t[371] = 322;
  t[373] = 109;
  t[374] = 110;
  t[381] = 111;
  t[383] = 243;
  t[393] = 112;
  t[395] = 113;
  t[396] = 114;
  t[400] = 115;
  t[401] = 347;
  t[410] = 116;
  t[437] = 117;
  t[448] = 118;
  t[449] = 119;
  t[454] = 120;
  t[455] = 121;
  t[460] = 122;
  t[463] = 380;
  t[853] = 44;
  t[855] = 58;
  t[856] = 46;
  t[876] = 47;
  t[878] = 45;
  t[882] = 45;
  t[894] = 40;
  t[895] = 41;
  t[896] = 91;
  t[897] = 93;
  t[923] = 64;
  t[1004] = 48;
  t[1005] = 49;
  t[1006] = 50;
  t[1007] = 51;
  t[1008] = 52;
  t[1009] = 53;
  t[1010] = 54;
  t[1011] = 55;
  t[1012] = 56;
  t[1013] = 57;
  t[1081] = 37;
  t[1085] = 43;
  t[1086] = 45;
});
exports.getStdFontMap = getStdFontMap;
exports.getNonStdFontMap = getNonStdFontMap;
exports.getSerifFonts = getSerifFonts;
exports.getSymbolsFonts = getSymbolsFonts;
exports.getGlyphMapForStandardFonts = getGlyphMapForStandardFonts;
exports.getSupplementalGlyphMapForArialBlack = getSupplementalGlyphMapForArialBlack;
exports.getSupplementalGlyphMapForCalibri = getSupplementalGlyphMapForCalibri;

/***/ }),
/* 17 */
/***/ (function(module, exports, __w_pdfjs_require__) {

var getLookupTableFactory = __w_pdfjs_require__(0).getLookupTableFactory;
var getSpecialPUASymbols = getLookupTableFactory(function (t) {
 t[63721] = 0x00A9;
 t[63193] = 0x00A9;
 t[63720] = 0x00AE;
 t[63194] = 0x00AE;
 t[63722] = 0x2122;
 t[63195] = 0x2122;
 t[63729] = 0x23A7;
 t[63730] = 0x23A8;
 t[63731] = 0x23A9;
 t[63740] = 0x23AB;
 t[63741] = 0x23AC;
 t[63742] = 0x23AD;
 t[63726] = 0x23A1;
 t[63727] = 0x23A2;
 t[63728] = 0x23A3;
 t[63737] = 0x23A4;
 t[63738] = 0x23A5;
 t[63739] = 0x23A6;
 t[63723] = 0x239B;
 t[63724] = 0x239C;
 t[63725] = 0x239D;
 t[63734] = 0x239E;
 t[63735] = 0x239F;
 t[63736] = 0x23A0;
});
function mapSpecialUnicodeValues(code) {
 if (code >= 0xFFF0 && code <= 0xFFFF) {
  return 0;
 } else if (code >= 0xF600 && code <= 0xF8FF) {
  return getSpecialPUASymbols()[code] || code;
 } else if (code === 0x00AD) {
  return 0x002D;
 }
 return code;
}
function getUnicodeForGlyph(name, glyphsUnicodeMap) {
 var unicode = glyphsUnicodeMap[name];
 if (unicode !== undefined) {
  return unicode;
 }
 if (!name) {
  return -1;
 }
 if (name[0] === 'u') {
  var nameLen = name.length, hexStr;
  if (nameLen === 7 && name[1] === 'n' && name[2] === 'i') {
   hexStr = name.substr(3);
  } else if (nameLen >= 5 && nameLen <= 7) {
   hexStr = name.substr(1);
  } else {
   return -1;
  }
  if (hexStr === hexStr.toUpperCase()) {
   unicode = parseInt(hexStr, 16);
   if (unicode >= 0) {
    return unicode;
   }
  }
 }
 return -1;
}
var UnicodeRanges = [
 {
  'begin': 0x0000,
  'end': 0x007F
 },
 {
  'begin': 0x0080,
  'end': 0x00FF
 },
 {
  'begin': 0x0100,
  'end': 0x017F
 },
 {
  'begin': 0x0180,
  'end': 0x024F
 },
 {
  'begin': 0x0250,
  'end': 0x02AF
 },
 {
  'begin': 0x02B0,
  'end': 0x02FF
 },
 {
  'begin': 0x0300,
  'end': 0x036F
 },
 {
  'begin': 0x0370,
  'end': 0x03FF
 },
 {
  'begin': 0x2C80,
  'end': 0x2CFF
 },
 {
  'begin': 0x0400,
  'end': 0x04FF
 },
 {
  'begin': 0x0530,
  'end': 0x058F
 },
 {
  'begin': 0x0590,
  'end': 0x05FF
 },
 {
  'begin': 0xA500,
  'end': 0xA63F
 },
 {
  'begin': 0x0600,
  'end': 0x06FF
 },
 {
  'begin': 0x07C0,
  'end': 0x07FF
 },
 {
  'begin': 0x0900,
  'end': 0x097F
 },
 {
  'begin': 0x0980,
  'end': 0x09FF
 },
 {
  'begin': 0x0A00,
  'end': 0x0A7F
 },
 {
  'begin': 0x0A80,
  'end': 0x0AFF
 },
 {
  'begin': 0x0B00,
  'end': 0x0B7F
 },
 {
  'begin': 0x0B80,
  'end': 0x0BFF
 },
 {
  'begin': 0x0C00,
  'end': 0x0C7F
 },
 {
  'begin': 0x0C80,
  'end': 0x0CFF
 },
 {
  'begin': 0x0D00,
  'end': 0x0D7F
 },
 {
  'begin': 0x0E00,
  'end': 0x0E7F
 },
 {
  'begin': 0x0E80,
  'end': 0x0EFF
 },
 {
  'begin': 0x10A0,
  'end': 0x10FF
 },
 {
  'begin': 0x1B00,
  'end': 0x1B7F
 },
 {
  'begin': 0x1100,
  'end': 0x11FF
 },
 {
  'begin': 0x1E00,
  'end': 0x1EFF
 },
 {
  'begin': 0x1F00,
  'end': 0x1FFF
 },
 {
  'begin': 0x2000,
  'end': 0x206F
 },
 {
  'begin': 0x2070,
  'end': 0x209F
 },
 {
  'begin': 0x20A0,
  'end': 0x20CF
 },
 {
  'begin': 0x20D0,
  'end': 0x20FF
 },
 {
  'begin': 0x2100,
  'end': 0x214F
 },
 {
  'begin': 0x2150,
  'end': 0x218F
 },
 {
  'begin': 0x2190,
  'end': 0x21FF
 },
 {
  'begin': 0x2200,
  'end': 0x22FF
 },
 {
  'begin': 0x2300,
  'end': 0x23FF
 },
 {
  'begin': 0x2400,
  'end': 0x243F
 },
 {
  'begin': 0x2440,
  'end': 0x245F
 },
 {
  'begin': 0x2460,
  'end': 0x24FF
 },
 {
  'begin': 0x2500,
  'end': 0x257F
 },
 {
  'begin': 0x2580,
  'end': 0x259F
 },
 {
  'begin': 0x25A0,
  'end': 0x25FF
 },
 {
  'begin': 0x2600,
  'end': 0x26FF
 },
 {
  'begin': 0x2700,
  'end': 0x27BF
 },
 {
  'begin': 0x3000,
  'end': 0x303F
 },
 {
  'begin': 0x3040,
  'end': 0x309F
 },
 {
  'begin': 0x30A0,
  'end': 0x30FF
 },
 {
  'begin': 0x3100,
  'end': 0x312F
 },
 {
  'begin': 0x3130,
  'end': 0x318F
 },
 {
  'begin': 0xA840,
  'end': 0xA87F
 },
 {
  'begin': 0x3200,
  'end': 0x32FF
 },
 {
  'begin': 0x3300,
  'end': 0x33FF
 },
 {
  'begin': 0xAC00,
  'end': 0xD7AF
 },
 {
  'begin': 0xD800,
  'end': 0xDFFF
 },
 {
  'begin': 0x10900,
  'end': 0x1091F
 },
 {
  'begin': 0x4E00,
  'end': 0x9FFF
 },
 {
  'begin': 0xE000,
  'end': 0xF8FF
 },
 {
  'begin': 0x31C0,
  'end': 0x31EF
 },
 {
  'begin': 0xFB00,
  'end': 0xFB4F
 },
 {
  'begin': 0xFB50,
  'end': 0xFDFF
 },
 {
  'begin': 0xFE20,
  'end': 0xFE2F
 },
 {
  'begin': 0xFE10,
  'end': 0xFE1F
 },
 {
  'begin': 0xFE50,
  'end': 0xFE6F
 },
 {
  'begin': 0xFE70,
  'end': 0xFEFF
 },
 {
  'begin': 0xFF00,
  'end': 0xFFEF
 },
 {
  'begin': 0xFFF0,
  'end': 0xFFFF
 },
 {
  'begin': 0x0F00,
  'end': 0x0FFF
 },
 {
  'begin': 0x0700,
  'end': 0x074F
 },
 {
  'begin': 0x0780,
  'end': 0x07BF
 },
 {
  'begin': 0x0D80,
  'end': 0x0DFF
 },
 {
  'begin': 0x1000,
  'end': 0x109F
 },
 {
  'begin': 0x1200,
  'end': 0x137F
 },
 {
  'begin': 0x13A0,
  'end': 0x13FF
 },
 {
  'begin': 0x1400,
  'end': 0x167F
 },
 {
  'begin': 0x1680,
  'end': 0x169F
 },
 {
  'begin': 0x16A0,
  'end': 0x16FF
 },
 {
  'begin': 0x1780,
  'end': 0x17FF
 },
 {
  'begin': 0x1800,
  'end': 0x18AF
 },
 {
  'begin': 0x2800,
  'end': 0x28FF
 },
 {
  'begin': 0xA000,
  'end': 0xA48F
 },
 {
  'begin': 0x1700,
  'end': 0x171F
 },
 {
  'begin': 0x10300,
  'end': 0x1032F
 },
 {
  'begin': 0x10330,
  'end': 0x1034F
 },
 {
  'begin': 0x10400,
  'end': 0x1044F
 },
 {
  'begin': 0x1D000,
  'end': 0x1D0FF
 },
 {
  'begin': 0x1D400,
  'end': 0x1D7FF
 },
 {
  'begin': 0xFF000,
  'end': 0xFFFFD
 },
 {
  'begin': 0xFE00,
  'end': 0xFE0F
 },
 {
  'begin': 0xE0000,
  'end': 0xE007F
 },
 {
  'begin': 0x1900,
  'end': 0x194F
 },
 {
  'begin': 0x1950,
  'end': 0x197F
 },
 {
  'begin': 0x1980,
  'end': 0x19DF
 },
 {
  'begin': 0x1A00,
  'end': 0x1A1F
 },
 {
  'begin': 0x2C00,
  'end': 0x2C5F
 },
 {
  'begin': 0x2D30,
  'end': 0x2D7F
 },
 {
  'begin': 0x4DC0,
  'end': 0x4DFF
 },
 {
  'begin': 0xA800,
  'end': 0xA82F
 },
 {
  'begin': 0x10000,
  'end': 0x1007F
 },
 {
  'begin': 0x10140,
  'end': 0x1018F
 },
 {
  'begin': 0x10380,
  'end': 0x1039F
 },
 {
  'begin': 0x103A0,
  'end': 0x103DF
 },
 {
  'begin': 0x10450,
  'end': 0x1047F
 },
 {
  'begin': 0x10480,
  'end': 0x104AF
 },
 {
  'begin': 0x10800,
  'end': 0x1083F
 },
 {
  'begin': 0x10A00,
  'end': 0x10A5F
 },
 {
  'begin': 0x1D300,
  'end': 0x1D35F
 },
 {
  'begin': 0x12000,
  'end': 0x123FF
 },
 {
  'begin': 0x1D360,
  'end': 0x1D37F
 },
 {
  'begin': 0x1B80,
  'end': 0x1BBF
 },
 {
  'begin': 0x1C00,
  'end': 0x1C4F
 },
 {
  'begin': 0x1C50,
  'end': 0x1C7F
 },
 {
  'begin': 0xA880,
  'end': 0xA8DF
 },
 {
  'begin': 0xA900,
  'end': 0xA92F
 },
 {
  'begin': 0xA930,
  'end': 0xA95F
 },
 {
  'begin': 0xAA00,
  'end': 0xAA5F
 },
 {
  'begin': 0x10190,
  'end': 0x101CF
 },
 {
  'begin': 0x101D0,
  'end': 0x101FF
 },
 {
  'begin': 0x102A0,
  'end': 0x102DF
 },
 {
  'begin': 0x1F030,
  'end': 0x1F09F
 }
];
function getUnicodeRangeFor(value) {
 for (var i = 0, ii = UnicodeRanges.length; i < ii; i++) {
  var range = UnicodeRanges[i];
  if (value >= range.begin && value < range.end) {
   return i;
  }
 }
 return -1;
}
function isRTLRangeFor(value) {
 var range = UnicodeRanges[13];
 if (value >= range.begin && value < range.end) {
  return true;
 }
 range = UnicodeRanges[11];
 if (value >= range.begin && value < range.end) {
  return true;
 }
 return false;
}
var getNormalizedUnicodes = getLookupTableFactory(function (t) {
 t['\u00A8'] = '\u0020\u0308';
 t['\u00AF'] = '\u0020\u0304';
 t['\u00B4'] = '\u0020\u0301';
 t['\u00B5'] = '\u03BC';
 t['\u00B8'] = '\u0020\u0327';
 t['\u0132'] = '\u0049\u004A';
 t['\u0133'] = '\u0069\u006A';
 t['\u013F'] = '\u004C\u00B7';
 t['\u0140'] = '\u006C\u00B7';
 t['\u0149'] = '\u02BC\u006E';
 t['\u017F'] = '\u0073';
 t['\u01C4'] = '\u0044\u017D';
 t['\u01C5'] = '\u0044\u017E';
 t['\u01C6'] = '\u0064\u017E';
 t['\u01C7'] = '\u004C\u004A';
 t['\u01C8'] = '\u004C\u006A';
 t['\u01C9'] = '\u006C\u006A';
 t['\u01CA'] = '\u004E\u004A';
 t['\u01CB'] = '\u004E\u006A';
 t['\u01CC'] = '\u006E\u006A';
 t['\u01F1'] = '\u0044\u005A';
 t['\u01F2'] = '\u0044\u007A';
 t['\u01F3'] = '\u0064\u007A';
 t['\u02D8'] = '\u0020\u0306';
 t['\u02D9'] = '\u0020\u0307';
 t['\u02DA'] = '\u0020\u030A';
 t['\u02DB'] = '\u0020\u0328';
 t['\u02DC'] = '\u0020\u0303';
 t['\u02DD'] = '\u0020\u030B';
 t['\u037A'] = '\u0020\u0345';
 t['\u0384'] = '\u0020\u0301';
 t['\u03D0'] = '\u03B2';
 t['\u03D1'] = '\u03B8';
 t['\u03D2'] = '\u03A5';
 t['\u03D5'] = '\u03C6';
 t['\u03D6'] = '\u03C0';
 t['\u03F0'] = '\u03BA';
 t['\u03F1'] = '\u03C1';
 t['\u03F2'] = '\u03C2';
 t['\u03F4'] = '\u0398';
 t['\u03F5'] = '\u03B5';
 t['\u03F9'] = '\u03A3';
 t['\u0587'] = '\u0565\u0582';
 t['\u0675'] = '\u0627\u0674';
 t['\u0676'] = '\u0648\u0674';
 t['\u0677'] = '\u06C7\u0674';
 t['\u0678'] = '\u064A\u0674';
 t['\u0E33'] = '\u0E4D\u0E32';
 t['\u0EB3'] = '\u0ECD\u0EB2';
 t['\u0EDC'] = '\u0EAB\u0E99';
 t['\u0EDD'] = '\u0EAB\u0EA1';
 t['\u0F77'] = '\u0FB2\u0F81';
 t['\u0F79'] = '\u0FB3\u0F81';
 t['\u1E9A'] = '\u0061\u02BE';
 t['\u1FBD'] = '\u0020\u0313';
 t['\u1FBF'] = '\u0020\u0313';
 t['\u1FC0'] = '\u0020\u0342';
 t['\u1FFE'] = '\u0020\u0314';
 t['\u2002'] = '\u0020';
 t['\u2003'] = '\u0020';
 t['\u2004'] = '\u0020';
 t['\u2005'] = '\u0020';
 t['\u2006'] = '\u0020';
 t['\u2008'] = '\u0020';
 t['\u2009'] = '\u0020';
 t['\u200A'] = '\u0020';
 t['\u2017'] = '\u0020\u0333';
 t['\u2024'] = '\u002E';
 t['\u2025'] = '\u002E\u002E';
 t['\u2026'] = '\u002E\u002E\u002E';
 t['\u2033'] = '\u2032\u2032';
 t['\u2034'] = '\u2032\u2032\u2032';
 t['\u2036'] = '\u2035\u2035';
 t['\u2037'] = '\u2035\u2035\u2035';
 t['\u203C'] = '\u0021\u0021';
 t['\u203E'] = '\u0020\u0305';
 t['\u2047'] = '\u003F\u003F';
 t['\u2048'] = '\u003F\u0021';
 t['\u2049'] = '\u0021\u003F';
 t['\u2057'] = '\u2032\u2032\u2032\u2032';
 t['\u205F'] = '\u0020';
 t['\u20A8'] = '\u0052\u0073';
 t['\u2100'] = '\u0061\u002F\u0063';
 t['\u2101'] = '\u0061\u002F\u0073';
 t['\u2103'] = '\u00B0\u0043';
 t['\u2105'] = '\u0063\u002F\u006F';
 t['\u2106'] = '\u0063\u002F\u0075';
 t['\u2107'] = '\u0190';
 t['\u2109'] = '\u00B0\u0046';
 t['\u2116'] = '\u004E\u006F';
 t['\u2121'] = '\u0054\u0045\u004C';
 t['\u2135'] = '\u05D0';
 t['\u2136'] = '\u05D1';
 t['\u2137'] = '\u05D2';
 t['\u2138'] = '\u05D3';
 t['\u213B'] = '\u0046\u0041\u0058';
 t['\u2160'] = '\u0049';
 t['\u2161'] = '\u0049\u0049';
 t['\u2162'] = '\u0049\u0049\u0049';
 t['\u2163'] = '\u0049\u0056';
 t['\u2164'] = '\u0056';
 t['\u2165'] = '\u0056\u0049';
 t['\u2166'] = '\u0056\u0049\u0049';
 t['\u2167'] = '\u0056\u0049\u0049\u0049';
 t['\u2168'] = '\u0049\u0058';
 t['\u2169'] = '\u0058';
 t['\u216A'] = '\u0058\u0049';
 t['\u216B'] = '\u0058\u0049\u0049';
 t['\u216C'] = '\u004C';
 t['\u216D'] = '\u0043';
 t['\u216E'] = '\u0044';
 t['\u216F'] = '\u004D';
 t['\u2170'] = '\u0069';
 t['\u2171'] = '\u0069\u0069';
 t['\u2172'] = '\u0069\u0069\u0069';
 t['\u2173'] = '\u0069\u0076';
 t['\u2174'] = '\u0076';
 t['\u2175'] = '\u0076\u0069';
 t['\u2176'] = '\u0076\u0069\u0069';
 t['\u2177'] = '\u0076\u0069\u0069\u0069';
 t['\u2178'] = '\u0069\u0078';
 t['\u2179'] = '\u0078';
 t['\u217A'] = '\u0078\u0069';
 t['\u217B'] = '\u0078\u0069\u0069';
 t['\u217C'] = '\u006C';
 t['\u217D'] = '\u0063';
 t['\u217E'] = '\u0064';
 t['\u217F'] = '\u006D';
 t['\u222C'] = '\u222B\u222B';
 t['\u222D'] = '\u222B\u222B\u222B';
 t['\u222F'] = '\u222E\u222E';
 t['\u2230'] = '\u222E\u222E\u222E';
 t['\u2474'] = '\u0028\u0031\u0029';
 t['\u2475'] = '\u0028\u0032\u0029';
 t['\u2476'] = '\u0028\u0033\u0029';
 t['\u2477'] = '\u0028\u0034\u0029';
 t['\u2478'] = '\u0028\u0035\u0029';
 t['\u2479'] = '\u0028\u0036\u0029';
 t['\u247A'] = '\u0028\u0037\u0029';
 t['\u247B'] = '\u0028\u0038\u0029';
 t['\u247C'] = '\u0028\u0039\u0029';
 t['\u247D'] = '\u0028\u0031\u0030\u0029';
 t['\u247E'] = '\u0028\u0031\u0031\u0029';
 t['\u247F'] = '\u0028\u0031\u0032\u0029';
 t['\u2480'] = '\u0028\u0031\u0033\u0029';
 t['\u2481'] = '\u0028\u0031\u0034\u0029';
 t['\u2482'] = '\u0028\u0031\u0035\u0029';
 t['\u2483'] = '\u0028\u0031\u0036\u0029';
 t['\u2484'] = '\u0028\u0031\u0037\u0029';
 t['\u2485'] = '\u0028\u0031\u0038\u0029';
 t['\u2486'] = '\u0028\u0031\u0039\u0029';
 t['\u2487'] = '\u0028\u0032\u0030\u0029';
 t['\u2488'] = '\u0031\u002E';
 t['\u2489'] = '\u0032\u002E';
 t['\u248A'] = '\u0033\u002E';
 t['\u248B'] = '\u0034\u002E';
 t['\u248C'] = '\u0035\u002E';
 t['\u248D'] = '\u0036\u002E';
 t['\u248E'] = '\u0037\u002E';
 t['\u248F'] = '\u0038\u002E';
 t['\u2490'] = '\u0039\u002E';
 t['\u2491'] = '\u0031\u0030\u002E';
 t['\u2492'] = '\u0031\u0031\u002E';
 t['\u2493'] = '\u0031\u0032\u002E';
 t['\u2494'] = '\u0031\u0033\u002E';
 t['\u2495'] = '\u0031\u0034\u002E';
 t['\u2496'] = '\u0031\u0035\u002E';
 t['\u2497'] = '\u0031\u0036\u002E';
 t['\u2498'] = '\u0031\u0037\u002E';
 t['\u2499'] = '\u0031\u0038\u002E';
 t['\u249A'] = '\u0031\u0039\u002E';
 t['\u249B'] = '\u0032\u0030\u002E';
 t['\u249C'] = '\u0028\u0061\u0029';
 t['\u249D'] = '\u0028\u0062\u0029';
 t['\u249E'] = '\u0028\u0063\u0029';
 t['\u249F'] = '\u0028\u0064\u0029';
 t['\u24A0'] = '\u0028\u0065\u0029';
 t['\u24A1'] = '\u0028\u0066\u0029';
 t['\u24A2'] = '\u0028\u0067\u0029';
 t['\u24A3'] = '\u0028\u0068\u0029';
 t['\u24A4'] = '\u0028\u0069\u0029';
 t['\u24A5'] = '\u0028\u006A\u0029';
 t['\u24A6'] = '\u0028\u006B\u0029';
 t['\u24A7'] = '\u0028\u006C\u0029';
 t['\u24A8'] = '\u0028\u006D\u0029';
 t['\u24A9'] = '\u0028\u006E\u0029';
 t['\u24AA'] = '\u0028\u006F\u0029';
 t['\u24AB'] = '\u0028\u0070\u0029';
 t['\u24AC'] = '\u0028\u0071\u0029';
 t['\u24AD'] = '\u0028\u0072\u0029';
 t['\u24AE'] = '\u0028\u0073\u0029';
 t['\u24AF'] = '\u0028\u0074\u0029';
 t['\u24B0'] = '\u0028\u0075\u0029';
 t['\u24B1'] = '\u0028\u0076\u0029';
 t['\u24B2'] = '\u0028\u0077\u0029';
 t['\u24B3'] = '\u0028\u0078\u0029';
 t['\u24B4'] = '\u0028\u0079\u0029';
 t['\u24B5'] = '\u0028\u007A\u0029';
 t['\u2A0C'] = '\u222B\u222B\u222B\u222B';
 t['\u2A74'] = '\u003A\u003A\u003D';
 t['\u2A75'] = '\u003D\u003D';
 t['\u2A76'] = '\u003D\u003D\u003D';
 t['\u2E9F'] = '\u6BCD';
 t['\u2EF3'] = '\u9F9F';
 t['\u2F00'] = '\u4E00';
 t['\u2F01'] = '\u4E28';
 t['\u2F02'] = '\u4E36';
 t['\u2F03'] = '\u4E3F';
 t['\u2F04'] = '\u4E59';
 t['\u2F05'] = '\u4E85';
 t['\u2F06'] = '\u4E8C';
 t['\u2F07'] = '\u4EA0';
 t['\u2F08'] = '\u4EBA';
 t['\u2F09'] = '\u513F';
 t['\u2F0A'] = '\u5165';
 t['\u2F0B'] = '\u516B';
 t['\u2F0C'] = '\u5182';
 t['\u2F0D'] = '\u5196';
 t['\u2F0E'] = '\u51AB';
 t['\u2F0F'] = '\u51E0';
 t['\u2F10'] = '\u51F5';
 t['\u2F11'] = '\u5200';
 t['\u2F12'] = '\u529B';
 t['\u2F13'] = '\u52F9';
 t['\u2F14'] = '\u5315';
 t['\u2F15'] = '\u531A';
 t['\u2F16'] = '\u5338';
 t['\u2F17'] = '\u5341';
 t['\u2F18'] = '\u535C';
 t['\u2F19'] = '\u5369';
 t['\u2F1A'] = '\u5382';
 t['\u2F1B'] = '\u53B6';
 t['\u2F1C'] = '\u53C8';
 t['\u2F1D'] = '\u53E3';
 t['\u2F1E'] = '\u56D7';
 t['\u2F1F'] = '\u571F';
 t['\u2F20'] = '\u58EB';
 t['\u2F21'] = '\u5902';
 t['\u2F22'] = '\u590A';
 t['\u2F23'] = '\u5915';
 t['\u2F24'] = '\u5927';
 t['\u2F25'] = '\u5973';
 t['\u2F26'] = '\u5B50';
 t['\u2F27'] = '\u5B80';
 t['\u2F28'] = '\u5BF8';
 t['\u2F29'] = '\u5C0F';
 t['\u2F2A'] = '\u5C22';
 t['\u2F2B'] = '\u5C38';
 t['\u2F2C'] = '\u5C6E';
 t['\u2F2D'] = '\u5C71';
 t['\u2F2E'] = '\u5DDB';
 t['\u2F2F'] = '\u5DE5';
 t['\u2F30'] = '\u5DF1';
 t['\u2F31'] = '\u5DFE';
 t['\u2F32'] = '\u5E72';
 t['\u2F33'] = '\u5E7A';
 t['\u2F34'] = '\u5E7F';
 t['\u2F35'] = '\u5EF4';
 t['\u2F36'] = '\u5EFE';
 t['\u2F37'] = '\u5F0B';
 t['\u2F38'] = '\u5F13';
 t['\u2F39'] = '\u5F50';
 t['\u2F3A'] = '\u5F61';
 t['\u2F3B'] = '\u5F73';
 t['\u2F3C'] = '\u5FC3';
 t['\u2F3D'] = '\u6208';
 t['\u2F3E'] = '\u6236';
 t['\u2F3F'] = '\u624B';
 t['\u2F40'] = '\u652F';
 t['\u2F41'] = '\u6534';
 t['\u2F42'] = '\u6587';
 t['\u2F43'] = '\u6597';
 t['\u2F44'] = '\u65A4';
 t['\u2F45'] = '\u65B9';
 t['\u2F46'] = '\u65E0';
 t['\u2F47'] = '\u65E5';
 t['\u2F48'] = '\u66F0';
 t['\u2F49'] = '\u6708';
 t['\u2F4A'] = '\u6728';
 t['\u2F4B'] = '\u6B20';
 t['\u2F4C'] = '\u6B62';
 t['\u2F4D'] = '\u6B79';
 t['\u2F4E'] = '\u6BB3';
 t['\u2F4F'] = '\u6BCB';
 t['\u2F50'] = '\u6BD4';
 t['\u2F51'] = '\u6BDB';
 t['\u2F52'] = '\u6C0F';
 t['\u2F53'] = '\u6C14';
 t['\u2F54'] = '\u6C34';
 t['\u2F55'] = '\u706B';
 t['\u2F56'] = '\u722A';
 t['\u2F57'] = '\u7236';
 t['\u2F58'] = '\u723B';
 t['\u2F59'] = '\u723F';
 t['\u2F5A'] = '\u7247';
 t['\u2F5B'] = '\u7259';
 t['\u2F5C'] = '\u725B';
 t['\u2F5D'] = '\u72AC';
 t['\u2F5E'] = '\u7384';
 t['\u2F5F'] = '\u7389';
 t['\u2F60'] = '\u74DC';
 t['\u2F61'] = '\u74E6';
 t['\u2F62'] = '\u7518';
 t['\u2F63'] = '\u751F';
 t['\u2F64'] = '\u7528';
 t['\u2F65'] = '\u7530';
 t['\u2F66'] = '\u758B';
 t['\u2F67'] = '\u7592';
 t['\u2F68'] = '\u7676';
 t['\u2F69'] = '\u767D';
 t['\u2F6A'] = '\u76AE';
 t['\u2F6B'] = '\u76BF';
 t['\u2F6C'] = '\u76EE';
 t['\u2F6D'] = '\u77DB';
 t['\u2F6E'] = '\u77E2';
 t['\u2F6F'] = '\u77F3';
 t['\u2F70'] = '\u793A';
 t['\u2F71'] = '\u79B8';
 t['\u2F72'] = '\u79BE';
 t['\u2F73'] = '\u7A74';
 t['\u2F74'] = '\u7ACB';
 t['\u2F75'] = '\u7AF9';
 t['\u2F76'] = '\u7C73';
 t['\u2F77'] = '\u7CF8';
 t['\u2F78'] = '\u7F36';
 t['\u2F79'] = '\u7F51';
 t['\u2F7A'] = '\u7F8A';
 t['\u2F7B'] = '\u7FBD';
 t['\u2F7C'] = '\u8001';
 t['\u2F7D'] = '\u800C';
 t['\u2F7E'] = '\u8012';
 t['\u2F7F'] = '\u8033';
 t['\u2F80'] = '\u807F';
 t['\u2F81'] = '\u8089';
 t['\u2F82'] = '\u81E3';
 t['\u2F83'] = '\u81EA';
 t['\u2F84'] = '\u81F3';
 t['\u2F85'] = '\u81FC';
 t['\u2F86'] = '\u820C';
 t['\u2F87'] = '\u821B';
 t['\u2F88'] = '\u821F';
 t['\u2F89'] = '\u826E';
 t['\u2F8A'] = '\u8272';
 t['\u2F8B'] = '\u8278';
 t['\u2F8C'] = '\u864D';
 t['\u2F8D'] = '\u866B';
 t['\u2F8E'] = '\u8840';
 t['\u2F8F'] = '\u884C';
 t['\u2F90'] = '\u8863';
 t['\u2F91'] = '\u897E';
 t['\u2F92'] = '\u898B';
 t['\u2F93'] = '\u89D2';
 t['\u2F94'] = '\u8A00';
 t['\u2F95'] = '\u8C37';
 t['\u2F96'] = '\u8C46';
 t['\u2F97'] = '\u8C55';
 t['\u2F98'] = '\u8C78';
 t['\u2F99'] = '\u8C9D';
 t['\u2F9A'] = '\u8D64';
 t['\u2F9B'] = '\u8D70';
 t['\u2F9C'] = '\u8DB3';
 t['\u2F9D'] = '\u8EAB';
 t['\u2F9E'] = '\u8ECA';
 t['\u2F9F'] = '\u8F9B';
 t['\u2FA0'] = '\u8FB0';
 t['\u2FA1'] = '\u8FB5';
 t['\u2FA2'] = '\u9091';
 t['\u2FA3'] = '\u9149';
 t['\u2FA4'] = '\u91C6';
 t['\u2FA5'] = '\u91CC';
 t['\u2FA6'] = '\u91D1';
 t['\u2FA7'] = '\u9577';
 t['\u2FA8'] = '\u9580';
 t['\u2FA9'] = '\u961C';
 t['\u2FAA'] = '\u96B6';
 t['\u2FAB'] = '\u96B9';
 t['\u2FAC'] = '\u96E8';
 t['\u2FAD'] = '\u9751';
 t['\u2FAE'] = '\u975E';
 t['\u2FAF'] = '\u9762';
 t['\u2FB0'] = '\u9769';
 t['\u2FB1'] = '\u97CB';
 t['\u2FB2'] = '\u97ED';
 t['\u2FB3'] = '\u97F3';
 t['\u2FB4'] = '\u9801';
 t['\u2FB5'] = '\u98A8';
 t['\u2FB6'] = '\u98DB';
 t['\u2FB7'] = '\u98DF';
 t['\u2FB8'] = '\u9996';
 t['\u2FB9'] = '\u9999';
 t['\u2FBA'] = '\u99AC';
 t['\u2FBB'] = '\u9AA8';
 t['\u2FBC'] = '\u9AD8';
 t['\u2FBD'] = '\u9ADF';
 t['\u2FBE'] = '\u9B25';
 t['\u2FBF'] = '\u9B2F';
 t['\u2FC0'] = '\u9B32';
 t['\u2FC1'] = '\u9B3C';
 t['\u2FC2'] = '\u9B5A';
 t['\u2FC3'] = '\u9CE5';
 t['\u2FC4'] = '\u9E75';
 t['\u2FC5'] = '\u9E7F';
 t['\u2FC6'] = '\u9EA5';
 t['\u2FC7'] = '\u9EBB';
 t['\u2FC8'] = '\u9EC3';
 t['\u2FC9'] = '\u9ECD';
 t['\u2FCA'] = '\u9ED1';
 t['\u2FCB'] = '\u9EF9';
 t['\u2FCC'] = '\u9EFD';
 t['\u2FCD'] = '\u9F0E';
 t['\u2FCE'] = '\u9F13';
 t['\u2FCF'] = '\u9F20';
 t['\u2FD0'] = '\u9F3B';
 t['\u2FD1'] = '\u9F4A';
 t['\u2FD2'] = '\u9F52';
 t['\u2FD3'] = '\u9F8D';
 t['\u2FD4'] = '\u9F9C';
 t['\u2FD5'] = '\u9FA0';
 t['\u3036'] = '\u3012';
 t['\u3038'] = '\u5341';
 t['\u3039'] = '\u5344';
 t['\u303A'] = '\u5345';
 t['\u309B'] = '\u0020\u3099';
 t['\u309C'] = '\u0020\u309A';
 t['\u3131'] = '\u1100';
 t['\u3132'] = '\u1101';
 t['\u3133'] = '\u11AA';
 t['\u3134'] = '\u1102';
 t['\u3135'] = '\u11AC';
 t['\u3136'] = '\u11AD';
 t['\u3137'] = '\u1103';
 t['\u3138'] = '\u1104';
 t['\u3139'] = '\u1105';
 t['\u313A'] = '\u11B0';
 t['\u313B'] = '\u11B1';
 t['\u313C'] = '\u11B2';
 t['\u313D'] = '\u11B3';
 t['\u313E'] = '\u11B4';
 t['\u313F'] = '\u11B5';
 t['\u3140'] = '\u111A';
 t['\u3141'] = '\u1106';
 t['\u3142'] = '\u1107';
 t['\u3143'] = '\u1108';
 t['\u3144'] = '\u1121';
 t['\u3145'] = '\u1109';
 t['\u3146'] = '\u110A';
 t['\u3147'] = '\u110B';
 t['\u3148'] = '\u110C';
 t['\u3149'] = '\u110D';
 t['\u314A'] = '\u110E';
 t['\u314B'] = '\u110F';
 t['\u314C'] = '\u1110';
 t['\u314D'] = '\u1111';
 t['\u314E'] = '\u1112';
 t['\u314F'] = '\u1161';
 t['\u3150'] = '\u1162';
 t['\u3151'] = '\u1163';
 t['\u3152'] = '\u1164';
 t['\u3153'] = '\u1165';
 t['\u3154'] = '\u1166';
 t['\u3155'] = '\u1167';
 t['\u3156'] = '\u1168';
 t['\u3157'] = '\u1169';
 t['\u3158'] = '\u116A';
 t['\u3159'] = '\u116B';
 t['\u315A'] = '\u116C';
 t['\u315B'] = '\u116D';
 t['\u315C'] = '\u116E';
 t['\u315D'] = '\u116F';
 t['\u315E'] = '\u1170';
 t['\u315F'] = '\u1171';
 t['\u3160'] = '\u1172';
 t['\u3161'] = '\u1173';
 t['\u3162'] = '\u1174';
 t['\u3163'] = '\u1175';
 t['\u3164'] = '\u1160';
 t['\u3165'] = '\u1114';
 t['\u3166'] = '\u1115';
 t['\u3167'] = '\u11C7';
 t['\u3168'] = '\u11C8';
 t['\u3169'] = '\u11CC';
 t['\u316A'] = '\u11CE';
 t['\u316B'] = '\u11D3';
 t['\u316C'] = '\u11D7';
 t['\u316D'] = '\u11D9';
 t['\u316E'] = '\u111C';
 t['\u316F'] = '\u11DD';
 t['\u3170'] = '\u11DF';
 t['\u3171'] = '\u111D';
 t['\u3172'] = '\u111E';
 t['\u3173'] = '\u1120';
 t['\u3174'] = '\u1122';
 t['\u3175'] = '\u1123';
 t['\u3176'] = '\u1127';
 t['\u3177'] = '\u1129';
 t['\u3178'] = '\u112B';
 t['\u3179'] = '\u112C';
 t['\u317A'] = '\u112D';
 t['\u317B'] = '\u112E';
 t['\u317C'] = '\u112F';
 t['\u317D'] = '\u1132';
 t['\u317E'] = '\u1136';
 t['\u317F'] = '\u1140';
 t['\u3180'] = '\u1147';
 t['\u3181'] = '\u114C';
 t['\u3182'] = '\u11F1';
 t['\u3183'] = '\u11F2';
 t['\u3184'] = '\u1157';
 t['\u3185'] = '\u1158';
 t['\u3186'] = '\u1159';
 t['\u3187'] = '\u1184';
 t['\u3188'] = '\u1185';
 t['\u3189'] = '\u1188';
 t['\u318A'] = '\u1191';
 t['\u318B'] = '\u1192';
 t['\u318C'] = '\u1194';
 t['\u318D'] = '\u119E';
 t['\u318E'] = '\u11A1';
 t['\u3200'] = '\u0028\u1100\u0029';
 t['\u3201'] = '\u0028\u1102\u0029';
 t['\u3202'] = '\u0028\u1103\u0029';
 t['\u3203'] = '\u0028\u1105\u0029';
 t['\u3204'] = '\u0028\u1106\u0029';
 t['\u3205'] = '\u0028\u1107\u0029';
 t['\u3206'] = '\u0028\u1109\u0029';
 t['\u3207'] = '\u0028\u110B\u0029';
 t['\u3208'] = '\u0028\u110C\u0029';
 t['\u3209'] = '\u0028\u110E\u0029';
 t['\u320A'] = '\u0028\u110F\u0029';
 t['\u320B'] = '\u0028\u1110\u0029';
 t['\u320C'] = '\u0028\u1111\u0029';
 t['\u320D'] = '\u0028\u1112\u0029';
 t['\u320E'] = '\u0028\u1100\u1161\u0029';
 t['\u320F'] = '\u0028\u1102\u1161\u0029';
 t['\u3210'] = '\u0028\u1103\u1161\u0029';
 t['\u3211'] = '\u0028\u1105\u1161\u0029';
 t['\u3212'] = '\u0028\u1106\u1161\u0029';
 t['\u3213'] = '\u0028\u1107\u1161\u0029';
 t['\u3214'] = '\u0028\u1109\u1161\u0029';
 t['\u3215'] = '\u0028\u110B\u1161\u0029';
 t['\u3216'] = '\u0028\u110C\u1161\u0029';
 t['\u3217'] = '\u0028\u110E\u1161\u0029';
 t['\u3218'] = '\u0028\u110F\u1161\u0029';
 t['\u3219'] = '\u0028\u1110\u1161\u0029';
 t['\u321A'] = '\u0028\u1111\u1161\u0029';
 t['\u321B'] = '\u0028\u1112\u1161\u0029';
 t['\u321C'] = '\u0028\u110C\u116E\u0029';
 t['\u321D'] = '\u0028\u110B\u1169\u110C\u1165\u11AB\u0029';
 t['\u321E'] = '\u0028\u110B\u1169\u1112\u116E\u0029';
 t['\u3220'] = '\u0028\u4E00\u0029';
 t['\u3221'] = '\u0028\u4E8C\u0029';
 t['\u3222'] = '\u0028\u4E09\u0029';
 t['\u3223'] = '\u0028\u56DB\u0029';
 t['\u3224'] = '\u0028\u4E94\u0029';
 t['\u3225'] = '\u0028\u516D\u0029';
 t['\u3226'] = '\u0028\u4E03\u0029';
 t['\u3227'] = '\u0028\u516B\u0029';
 t['\u3228'] = '\u0028\u4E5D\u0029';
 t['\u3229'] = '\u0028\u5341\u0029';
 t['\u322A'] = '\u0028\u6708\u0029';
 t['\u322B'] = '\u0028\u706B\u0029';
 t['\u322C'] = '\u0028\u6C34\u0029';
 t['\u322D'] = '\u0028\u6728\u0029';
 t['\u322E'] = '\u0028\u91D1\u0029';
 t['\u322F'] = '\u0028\u571F\u0029';
 t['\u3230'] = '\u0028\u65E5\u0029';
 t['\u3231'] = '\u0028\u682A\u0029';
 t['\u3232'] = '\u0028\u6709\u0029';
 t['\u3233'] = '\u0028\u793E\u0029';
 t['\u3234'] = '\u0028\u540D\u0029';
 t['\u3235'] = '\u0028\u7279\u0029';
 t['\u3236'] = '\u0028\u8CA1\u0029';
 t['\u3237'] = '\u0028\u795D\u0029';
 t['\u3238'] = '\u0028\u52B4\u0029';
 t['\u3239'] = '\u0028\u4EE3\u0029';
 t['\u323A'] = '\u0028\u547C\u0029';
 t['\u323B'] = '\u0028\u5B66\u0029';
 t['\u323C'] = '\u0028\u76E3\u0029';
 t['\u323D'] = '\u0028\u4F01\u0029';
 t['\u323E'] = '\u0028\u8CC7\u0029';
 t['\u323F'] = '\u0028\u5354\u0029';
 t['\u3240'] = '\u0028\u796D\u0029';
 t['\u3241'] = '\u0028\u4F11\u0029';
 t['\u3242'] = '\u0028\u81EA\u0029';
 t['\u3243'] = '\u0028\u81F3\u0029';
 t['\u32C0'] = '\u0031\u6708';
 t['\u32C1'] = '\u0032\u6708';
 t['\u32C2'] = '\u0033\u6708';
 t['\u32C3'] = '\u0034\u6708';
 t['\u32C4'] = '\u0035\u6708';
 t['\u32C5'] = '\u0036\u6708';
 t['\u32C6'] = '\u0037\u6708';
 t['\u32C7'] = '\u0038\u6708';
 t['\u32C8'] = '\u0039\u6708';
 t['\u32C9'] = '\u0031\u0030\u6708';
 t['\u32CA'] = '\u0031\u0031\u6708';
 t['\u32CB'] = '\u0031\u0032\u6708';
 t['\u3358'] = '\u0030\u70B9';
 t['\u3359'] = '\u0031\u70B9';
 t['\u335A'] = '\u0032\u70B9';
 t['\u335B'] = '\u0033\u70B9';
 t['\u335C'] = '\u0034\u70B9';
 t['\u335D'] = '\u0035\u70B9';
 t['\u335E'] = '\u0036\u70B9';
 t['\u335F'] = '\u0037\u70B9';
 t['\u3360'] = '\u0038\u70B9';
 t['\u3361'] = '\u0039\u70B9';
 t['\u3362'] = '\u0031\u0030\u70B9';
 t['\u3363'] = '\u0031\u0031\u70B9';
 t['\u3364'] = '\u0031\u0032\u70B9';
 t['\u3365'] = '\u0031\u0033\u70B9';
 t['\u3366'] = '\u0031\u0034\u70B9';
 t['\u3367'] = '\u0031\u0035\u70B9';
 t['\u3368'] = '\u0031\u0036\u70B9';
 t['\u3369'] = '\u0031\u0037\u70B9';
 t['\u336A'] = '\u0031\u0038\u70B9';
 t['\u336B'] = '\u0031\u0039\u70B9';
 t['\u336C'] = '\u0032\u0030\u70B9';
 t['\u336D'] = '\u0032\u0031\u70B9';
 t['\u336E'] = '\u0032\u0032\u70B9';
 t['\u336F'] = '\u0032\u0033\u70B9';
 t['\u3370'] = '\u0032\u0034\u70B9';
 t['\u33E0'] = '\u0031\u65E5';
 t['\u33E1'] = '\u0032\u65E5';
 t['\u33E2'] = '\u0033\u65E5';
 t['\u33E3'] = '\u0034\u65E5';
 t['\u33E4'] = '\u0035\u65E5';
 t['\u33E5'] = '\u0036\u65E5';
 t['\u33E6'] = '\u0037\u65E5';
 t['\u33E7'] = '\u0038\u65E5';
 t['\u33E8'] = '\u0039\u65E5';
 t['\u33E9'] = '\u0031\u0030\u65E5';
 t['\u33EA'] = '\u0031\u0031\u65E5';
 t['\u33EB'] = '\u0031\u0032\u65E5';
 t['\u33EC'] = '\u0031\u0033\u65E5';
 t['\u33ED'] = '\u0031\u0034\u65E5';
 t['\u33EE'] = '\u0031\u0035\u65E5';
 t['\u33EF'] = '\u0031\u0036\u65E5';
 t['\u33F0'] = '\u0031\u0037\u65E5';
 t['\u33F1'] = '\u0031\u0038\u65E5';
 t['\u33F2'] = '\u0031\u0039\u65E5';
 t['\u33F3'] = '\u0032\u0030\u65E5';
 t['\u33F4'] = '\u0032\u0031\u65E5';
 t['\u33F5'] = '\u0032\u0032\u65E5';
 t['\u33F6'] = '\u0032\u0033\u65E5';
 t['\u33F7'] = '\u0032\u0034\u65E5';
 t['\u33F8'] = '\u0032\u0035\u65E5';
 t['\u33F9'] = '\u0032\u0036\u65E5';
 t['\u33FA'] = '\u0032\u0037\u65E5';
 t['\u33FB'] = '\u0032\u0038\u65E5';
 t['\u33FC'] = '\u0032\u0039\u65E5';
 t['\u33FD'] = '\u0033\u0030\u65E5';
 t['\u33FE'] = '\u0033\u0031\u65E5';
 t['\uFB00'] = '\u0066\u0066';
 t['\uFB01'] = '\u0066\u0069';
 t['\uFB02'] = '\u0066\u006C';
 t['\uFB03'] = '\u0066\u0066\u0069';
 t['\uFB04'] = '\u0066\u0066\u006C';
 t['\uFB05'] = '\u017F\u0074';
 t['\uFB06'] = '\u0073\u0074';
 t['\uFB13'] = '\u0574\u0576';
 t['\uFB14'] = '\u0574\u0565';
 t['\uFB15'] = '\u0574\u056B';
 t['\uFB16'] = '\u057E\u0576';
 t['\uFB17'] = '\u0574\u056D';
 t['\uFB4F'] = '\u05D0\u05DC';
 t['\uFB50'] = '\u0671';
 t['\uFB51'] = '\u0671';
 t['\uFB52'] = '\u067B';
 t['\uFB53'] = '\u067B';
 t['\uFB54'] = '\u067B';
 t['\uFB55'] = '\u067B';
 t['\uFB56'] = '\u067E';
 t['\uFB57'] = '\u067E';
 t['\uFB58'] = '\u067E';
 t['\uFB59'] = '\u067E';
 t['\uFB5A'] = '\u0680';
 t['\uFB5B'] = '\u0680';
 t['\uFB5C'] = '\u0680';
 t['\uFB5D'] = '\u0680';
 t['\uFB5E'] = '\u067A';
 t['\uFB5F'] = '\u067A';
 t['\uFB60'] = '\u067A';
 t['\uFB61'] = '\u067A';
 t['\uFB62'] = '\u067F';
 t['\uFB63'] = '\u067F';
 t['\uFB64'] = '\u067F';
 t['\uFB65'] = '\u067F';
 t['\uFB66'] = '\u0679';
 t['\uFB67'] = '\u0679';
 t['\uFB68'] = '\u0679';
 t['\uFB69'] = '\u0679';
 t['\uFB6A'] = '\u06A4';
 t['\uFB6B'] = '\u06A4';
 t['\uFB6C'] = '\u06A4';
 t['\uFB6D'] = '\u06A4';
 t['\uFB6E'] = '\u06A6';
 t['\uFB6F'] = '\u06A6';
 t['\uFB70'] = '\u06A6';
 t['\uFB71'] = '\u06A6';
 t['\uFB72'] = '\u0684';
 t['\uFB73'] = '\u0684';
 t['\uFB74'] = '\u0684';
 t['\uFB75'] = '\u0684';
 t['\uFB76'] = '\u0683';
 t['\uFB77'] = '\u0683';
 t['\uFB78'] = '\u0683';
 t['\uFB79'] = '\u0683';
 t['\uFB7A'] = '\u0686';
 t['\uFB7B'] = '\u0686';
 t['\uFB7C'] = '\u0686';
 t['\uFB7D'] = '\u0686';
 t['\uFB7E'] = '\u0687';
 t['\uFB7F'] = '\u0687';
 t['\uFB80'] = '\u0687';
 t['\uFB81'] = '\u0687';
 t['\uFB82'] = '\u068D';
 t['\uFB83'] = '\u068D';
 t['\uFB84'] = '\u068C';
 t['\uFB85'] = '\u068C';
 t['\uFB86'] = '\u068E';
 t['\uFB87'] = '\u068E';
 t['\uFB88'] = '\u0688';
 t['\uFB89'] = '\u0688';
 t['\uFB8A'] = '\u0698';
 t['\uFB8B'] = '\u0698';
 t['\uFB8C'] = '\u0691';
 t['\uFB8D'] = '\u0691';
 t['\uFB8E'] = '\u06A9';
 t['\uFB8F'] = '\u06A9';
 t['\uFB90'] = '\u06A9';
 t['\uFB91'] = '\u06A9';
 t['\uFB92'] = '\u06AF';
 t['\uFB93'] = '\u06AF';
 t['\uFB94'] = '\u06AF';
 t['\uFB95'] = '\u06AF';
 t['\uFB96'] = '\u06B3';
 t['\uFB97'] = '\u06B3';
 t['\uFB98'] = '\u06B3';
 t['\uFB99'] = '\u06B3';
 t['\uFB9A'] = '\u06B1';
 t['\uFB9B'] = '\u06B1';
 t['\uFB9C'] = '\u06B1';
 t['\uFB9D'] = '\u06B1';
 t['\uFB9E'] = '\u06BA';
 t['\uFB9F'] = '\u06BA';
 t['\uFBA0'] = '\u06BB';
 t['\uFBA1'] = '\u06BB';
 t['\uFBA2'] = '\u06BB';
 t['\uFBA3'] = '\u06BB';
 t['\uFBA4'] = '\u06C0';
 t['\uFBA5'] = '\u06C0';
 t['\uFBA6'] = '\u06C1';
 t['\uFBA7'] = '\u06C1';
 t['\uFBA8'] = '\u06C1';
 t['\uFBA9'] = '\u06C1';
 t['\uFBAA'] = '\u06BE';
 t['\uFBAB'] = '\u06BE';
 t['\uFBAC'] = '\u06BE';
 t['\uFBAD'] = '\u06BE';
 t['\uFBAE'] = '\u06D2';
 t['\uFBAF'] = '\u06D2';
 t['\uFBB0'] = '\u06D3';
 t['\uFBB1'] = '\u06D3';
 t['\uFBD3'] = '\u06AD';
 t['\uFBD4'] = '\u06AD';
 t['\uFBD5'] = '\u06AD';
 t['\uFBD6'] = '\u06AD';
 t['\uFBD7'] = '\u06C7';
 t['\uFBD8'] = '\u06C7';
 t['\uFBD9'] = '\u06C6';
 t['\uFBDA'] = '\u06C6';
 t['\uFBDB'] = '\u06C8';
 t['\uFBDC'] = '\u06C8';
 t['\uFBDD'] = '\u0677';
 t['\uFBDE'] = '\u06CB';
 t['\uFBDF'] = '\u06CB';
 t['\uFBE0'] = '\u06C5';
 t['\uFBE1'] = '\u06C5';
 t['\uFBE2'] = '\u06C9';
 t['\uFBE3'] = '\u06C9';
 t['\uFBE4'] = '\u06D0';
 t['\uFBE5'] = '\u06D0';
 t['\uFBE6'] = '\u06D0';
 t['\uFBE7'] = '\u06D0';
 t['\uFBE8'] = '\u0649';
 t['\uFBE9'] = '\u0649';
 t['\uFBEA'] = '\u0626\u0627';
 t['\uFBEB'] = '\u0626\u0627';
 t['\uFBEC'] = '\u0626\u06D5';
 t['\uFBED'] = '\u0626\u06D5';
 t['\uFBEE'] = '\u0626\u0648';
 t['\uFBEF'] = '\u0626\u0648';
 t['\uFBF0'] = '\u0626\u06C7';
 t['\uFBF1'] = '\u0626\u06C7';
 t['\uFBF2'] = '\u0626\u06C6';
 t['\uFBF3'] = '\u0626\u06C6';
 t['\uFBF4'] = '\u0626\u06C8';
 t['\uFBF5'] = '\u0626\u06C8';
 t['\uFBF6'] = '\u0626\u06D0';
 t['\uFBF7'] = '\u0626\u06D0';
 t['\uFBF8'] = '\u0626\u06D0';
 t['\uFBF9'] = '\u0626\u0649';
 t['\uFBFA'] = '\u0626\u0649';
 t['\uFBFB'] = '\u0626\u0649';
 t['\uFBFC'] = '\u06CC';
 t['\uFBFD'] = '\u06CC';
 t['\uFBFE'] = '\u06CC';
 t['\uFBFF'] = '\u06CC';
 t['\uFC00'] = '\u0626\u062C';
 t['\uFC01'] = '\u0626\u062D';
 t['\uFC02'] = '\u0626\u0645';
 t['\uFC03'] = '\u0626\u0649';
 t['\uFC04'] = '\u0626\u064A';
 t['\uFC05'] = '\u0628\u062C';
 t['\uFC06'] = '\u0628\u062D';
 t['\uFC07'] = '\u0628\u062E';
 t['\uFC08'] = '\u0628\u0645';
 t['\uFC09'] = '\u0628\u0649';
 t['\uFC0A'] = '\u0628\u064A';
 t['\uFC0B'] = '\u062A\u062C';
 t['\uFC0C'] = '\u062A\u062D';
 t['\uFC0D'] = '\u062A\u062E';
 t['\uFC0E'] = '\u062A\u0645';
 t['\uFC0F'] = '\u062A\u0649';
 t['\uFC10'] = '\u062A\u064A';
 t['\uFC11'] = '\u062B\u062C';
 t['\uFC12'] = '\u062B\u0645';
 t['\uFC13'] = '\u062B\u0649';
 t['\uFC14'] = '\u062B\u064A';
 t['\uFC15'] = '\u062C\u062D';
 t['\uFC16'] = '\u062C\u0645';
 t['\uFC17'] = '\u062D\u062C';
 t['\uFC18'] = '\u062D\u0645';
 t['\uFC19'] = '\u062E\u062C';
 t['\uFC1A'] = '\u062E\u062D';
 t['\uFC1B'] = '\u062E\u0645';
 t['\uFC1C'] = '\u0633\u062C';
 t['\uFC1D'] = '\u0633\u062D';
 t['\uFC1E'] = '\u0633\u062E';
 t['\uFC1F'] = '\u0633\u0645';
 t['\uFC20'] = '\u0635\u062D';
 t['\uFC21'] = '\u0635\u0645';
 t['\uFC22'] = '\u0636\u062C';
 t['\uFC23'] = '\u0636\u062D';
 t['\uFC24'] = '\u0636\u062E';
 t['\uFC25'] = '\u0636\u0645';
 t['\uFC26'] = '\u0637\u062D';
 t['\uFC27'] = '\u0637\u0645';
 t['\uFC28'] = '\u0638\u0645';
 t['\uFC29'] = '\u0639\u062C';
 t['\uFC2A'] = '\u0639\u0645';
 t['\uFC2B'] = '\u063A\u062C';
 t['\uFC2C'] = '\u063A\u0645';
 t['\uFC2D'] = '\u0641\u062C';
 t['\uFC2E'] = '\u0641\u062D';
 t['\uFC2F'] = '\u0641\u062E';
 t['\uFC30'] = '\u0641\u0645';
 t['\uFC31'] = '\u0641\u0649';
 t['\uFC32'] = '\u0641\u064A';
 t['\uFC33'] = '\u0642\u062D';
 t['\uFC34'] = '\u0642\u0645';
 t['\uFC35'] = '\u0642\u0649';
 t['\uFC36'] = '\u0642\u064A';
 t['\uFC37'] = '\u0643\u0627';
 t['\uFC38'] = '\u0643\u062C';
 t['\uFC39'] = '\u0643\u062D';
 t['\uFC3A'] = '\u0643\u062E';
 t['\uFC3B'] = '\u0643\u0644';
 t['\uFC3C'] = '\u0643\u0645';
 t['\uFC3D'] = '\u0643\u0649';
 t['\uFC3E'] = '\u0643\u064A';
 t['\uFC3F'] = '\u0644\u062C';
 t['\uFC40'] = '\u0644\u062D';
 t['\uFC41'] = '\u0644\u062E';
 t['\uFC42'] = '\u0644\u0645';
 t['\uFC43'] = '\u0644\u0649';
 t['\uFC44'] = '\u0644\u064A';
 t['\uFC45'] = '\u0645\u062C';
 t['\uFC46'] = '\u0645\u062D';
 t['\uFC47'] = '\u0645\u062E';
 t['\uFC48'] = '\u0645\u0645';
 t['\uFC49'] = '\u0645\u0649';
 t['\uFC4A'] = '\u0645\u064A';
 t['\uFC4B'] = '\u0646\u062C';
 t['\uFC4C'] = '\u0646\u062D';
 t['\uFC4D'] = '\u0646\u062E';
 t['\uFC4E'] = '\u0646\u0645';
 t['\uFC4F'] = '\u0646\u0649';
 t['\uFC50'] = '\u0646\u064A';
 t['\uFC51'] = '\u0647\u062C';
 t['\uFC52'] = '\u0647\u0645';
 t['\uFC53'] = '\u0647\u0649';
 t['\uFC54'] = '\u0647\u064A';
 t['\uFC55'] = '\u064A\u062C';
 t['\uFC56'] = '\u064A\u062D';
 t['\uFC57'] = '\u064A\u062E';
 t['\uFC58'] = '\u064A\u0645';
 t['\uFC59'] = '\u064A\u0649';
 t['\uFC5A'] = '\u064A\u064A';
 t['\uFC5B'] = '\u0630\u0670';
 t['\uFC5C'] = '\u0631\u0670';
 t['\uFC5D'] = '\u0649\u0670';
 t['\uFC5E'] = '\u0020\u064C\u0651';
 t['\uFC5F'] = '\u0020\u064D\u0651';
 t['\uFC60'] = '\u0020\u064E\u0651';
 t['\uFC61'] = '\u0020\u064F\u0651';
 t['\uFC62'] = '\u0020\u0650\u0651';
 t['\uFC63'] = '\u0020\u0651\u0670';
 t['\uFC64'] = '\u0626\u0631';
 t['\uFC65'] = '\u0626\u0632';
 t['\uFC66'] = '\u0626\u0645';
 t['\uFC67'] = '\u0626\u0646';
 t['\uFC68'] = '\u0626\u0649';
 t['\uFC69'] = '\u0626\u064A';
 t['\uFC6A'] = '\u0628\u0631';
 t['\uFC6B'] = '\u0628\u0632';
 t['\uFC6C'] = '\u0628\u0645';
 t['\uFC6D'] = '\u0628\u0646';
 t['\uFC6E'] = '\u0628\u0649';
 t['\uFC6F'] = '\u0628\u064A';
 t['\uFC70'] = '\u062A\u0631';
 t['\uFC71'] = '\u062A\u0632';
 t['\uFC72'] = '\u062A\u0645';
 t['\uFC73'] = '\u062A\u0646';
 t['\uFC74'] = '\u062A\u0649';
 t['\uFC75'] = '\u062A\u064A';
 t['\uFC76'] = '\u062B\u0631';
 t['\uFC77'] = '\u062B\u0632';
 t['\uFC78'] = '\u062B\u0645';
 t['\uFC79'] = '\u062B\u0646';
 t['\uFC7A'] = '\u062B\u0649';
 t['\uFC7B'] = '\u062B\u064A';
 t['\uFC7C'] = '\u0641\u0649';
 t['\uFC7D'] = '\u0641\u064A';
 t['\uFC7E'] = '\u0642\u0649';
 t['\uFC7F'] = '\u0642\u064A';
 t['\uFC80'] = '\u0643\u0627';
 t['\uFC81'] = '\u0643\u0644';
 t['\uFC82'] = '\u0643\u0645';
 t['\uFC83'] = '\u0643\u0649';
 t['\uFC84'] = '\u0643\u064A';
 t['\uFC85'] = '\u0644\u0645';
 t['\uFC86'] = '\u0644\u0649';
 t['\uFC87'] = '\u0644\u064A';
 t['\uFC88'] = '\u0645\u0627';
 t['\uFC89'] = '\u0645\u0645';
 t['\uFC8A'] = '\u0646\u0631';
 t['\uFC8B'] = '\u0646\u0632';
 t['\uFC8C'] = '\u0646\u0645';
 t['\uFC8D'] = '\u0646\u0646';
 t['\uFC8E'] = '\u0646\u0649';
 t['\uFC8F'] = '\u0646\u064A';
 t['\uFC90'] = '\u0649\u0670';
 t['\uFC91'] = '\u064A\u0631';
 t['\uFC92'] = '\u064A\u0632';
 t['\uFC93'] = '\u064A\u0645';
 t['\uFC94'] = '\u064A\u0646';
 t['\uFC95'] = '\u064A\u0649';
 t['\uFC96'] = '\u064A\u064A';
 t['\uFC97'] = '\u0626\u062C';
 t['\uFC98'] = '\u0626\u062D';
 t['\uFC99'] = '\u0626\u062E';
 t['\uFC9A'] = '\u0626\u0645';
 t['\uFC9B'] = '\u0626\u0647';
 t['\uFC9C'] = '\u0628\u062C';
 t['\uFC9D'] = '\u0628\u062D';
 t['\uFC9E'] = '\u0628\u062E';
 t['\uFC9F'] = '\u0628\u0645';
 t['\uFCA0'] = '\u0628\u0647';
 t['\uFCA1'] = '\u062A\u062C';
 t['\uFCA2'] = '\u062A\u062D';
 t['\uFCA3'] = '\u062A\u062E';
 t['\uFCA4'] = '\u062A\u0645';
 t['\uFCA5'] = '\u062A\u0647';
 t['\uFCA6'] = '\u062B\u0645';
 t['\uFCA7'] = '\u062C\u062D';
 t['\uFCA8'] = '\u062C\u0645';
 t['\uFCA9'] = '\u062D\u062C';
 t['\uFCAA'] = '\u062D\u0645';
 t['\uFCAB'] = '\u062E\u062C';
 t['\uFCAC'] = '\u062E\u0645';
 t['\uFCAD'] = '\u0633\u062C';
 t['\uFCAE'] = '\u0633\u062D';
 t['\uFCAF'] = '\u0633\u062E';
 t['\uFCB0'] = '\u0633\u0645';
 t['\uFCB1'] = '\u0635\u062D';
 t['\uFCB2'] = '\u0635\u062E';
 t['\uFCB3'] = '\u0635\u0645';
 t['\uFCB4'] = '\u0636\u062C';
 t['\uFCB5'] = '\u0636\u062D';
 t['\uFCB6'] = '\u0636\u062E';
 t['\uFCB7'] = '\u0636\u0645';
 t['\uFCB8'] = '\u0637\u062D';
 t['\uFCB9'] = '\u0638\u0645';
 t['\uFCBA'] = '\u0639\u062C';
 t['\uFCBB'] = '\u0639\u0645';
 t['\uFCBC'] = '\u063A\u062C';
 t['\uFCBD'] = '\u063A\u0645';
 t['\uFCBE'] = '\u0641\u062C';
 t['\uFCBF'] = '\u0641\u062D';
 t['\uFCC0'] = '\u0641\u062E';
 t['\uFCC1'] = '\u0641\u0645';
 t['\uFCC2'] = '\u0642\u062D';
 t['\uFCC3'] = '\u0642\u0645';
 t['\uFCC4'] = '\u0643\u062C';
 t['\uFCC5'] = '\u0643\u062D';
 t['\uFCC6'] = '\u0643\u062E';
 t['\uFCC7'] = '\u0643\u0644';
 t['\uFCC8'] = '\u0643\u0645';
 t['\uFCC9'] = '\u0644\u062C';
 t['\uFCCA'] = '\u0644\u062D';
 t['\uFCCB'] = '\u0644\u062E';
 t['\uFCCC'] = '\u0644\u0645';
 t['\uFCCD'] = '\u0644\u0647';
 t['\uFCCE'] = '\u0645\u062C';
 t['\uFCCF'] = '\u0645\u062D';
 t['\uFCD0'] = '\u0645\u062E';
 t['\uFCD1'] = '\u0645\u0645';
 t['\uFCD2'] = '\u0646\u062C';
 t['\uFCD3'] = '\u0646\u062D';
 t['\uFCD4'] = '\u0646\u062E';
 t['\uFCD5'] = '\u0646\u0645';
 t['\uFCD6'] = '\u0646\u0647';
 t['\uFCD7'] = '\u0647\u062C';
 t['\uFCD8'] = '\u0647\u0645';
 t['\uFCD9'] = '\u0647\u0670';
 t['\uFCDA'] = '\u064A\u062C';
 t['\uFCDB'] = '\u064A\u062D';
 t['\uFCDC'] = '\u064A\u062E';
 t['\uFCDD'] = '\u064A\u0645';
 t['\uFCDE'] = '\u064A\u0647';
 t['\uFCDF'] = '\u0626\u0645';
 t['\uFCE0'] = '\u0626\u0647';
 t['\uFCE1'] = '\u0628\u0645';
 t['\uFCE2'] = '\u0628\u0647';
 t['\uFCE3'] = '\u062A\u0645';
 t['\uFCE4'] = '\u062A\u0647';
 t['\uFCE5'] = '\u062B\u0645';
 t['\uFCE6'] = '\u062B\u0647';
 t['\uFCE7'] = '\u0633\u0645';
 t['\uFCE8'] = '\u0633\u0647';
 t['\uFCE9'] = '\u0634\u0645';
 t['\uFCEA'] = '\u0634\u0647';
 t['\uFCEB'] = '\u0643\u0644';
 t['\uFCEC'] = '\u0643\u0645';
 t['\uFCED'] = '\u0644\u0645';
 t['\uFCEE'] = '\u0646\u0645';
 t['\uFCEF'] = '\u0646\u0647';
 t['\uFCF0'] = '\u064A\u0645';
 t['\uFCF1'] = '\u064A\u0647';
 t['\uFCF2'] = '\u0640\u064E\u0651';
 t['\uFCF3'] = '\u0640\u064F\u0651';
 t['\uFCF4'] = '\u0640\u0650\u0651';
 t['\uFCF5'] = '\u0637\u0649';
 t['\uFCF6'] = '\u0637\u064A';
 t['\uFCF7'] = '\u0639\u0649';
 t['\uFCF8'] = '\u0639\u064A';
 t['\uFCF9'] = '\u063A\u0649';
 t['\uFCFA'] = '\u063A\u064A';
 t['\uFCFB'] = '\u0633\u0649';
 t['\uFCFC'] = '\u0633\u064A';
 t['\uFCFD'] = '\u0634\u0649';
 t['\uFCFE'] = '\u0634\u064A';
 t['\uFCFF'] = '\u062D\u0649';
 t['\uFD00'] = '\u062D\u064A';
 t['\uFD01'] = '\u062C\u0649';
 t['\uFD02'] = '\u062C\u064A';
 t['\uFD03'] = '\u062E\u0649';
 t['\uFD04'] = '\u062E\u064A';
 t['\uFD05'] = '\u0635\u0649';
 t['\uFD06'] = '\u0635\u064A';
 t['\uFD07'] = '\u0636\u0649';
 t['\uFD08'] = '\u0636\u064A';
 t['\uFD09'] = '\u0634\u062C';
 t['\uFD0A'] = '\u0634\u062D';
 t['\uFD0B'] = '\u0634\u062E';
 t['\uFD0C'] = '\u0634\u0645';
 t['\uFD0D'] = '\u0634\u0631';
 t['\uFD0E'] = '\u0633\u0631';
 t['\uFD0F'] = '\u0635\u0631';
 t['\uFD10'] = '\u0636\u0631';
 t['\uFD11'] = '\u0637\u0649';
 t['\uFD12'] = '\u0637\u064A';
 t['\uFD13'] = '\u0639\u0649';
 t['\uFD14'] = '\u0639\u064A';
 t['\uFD15'] = '\u063A\u0649';
 t['\uFD16'] = '\u063A\u064A';
 t['\uFD17'] = '\u0633\u0649';
 t['\uFD18'] = '\u0633\u064A';
 t['\uFD19'] = '\u0634\u0649';
 t['\uFD1A'] = '\u0634\u064A';
 t['\uFD1B'] = '\u062D\u0649';
 t['\uFD1C'] = '\u062D\u064A';
 t['\uFD1D'] = '\u062C\u0649';
 t['\uFD1E'] = '\u062C\u064A';
 t['\uFD1F'] = '\u062E\u0649';
 t['\uFD20'] = '\u062E\u064A';
 t['\uFD21'] = '\u0635\u0649';
 t['\uFD22'] = '\u0635\u064A';
 t['\uFD23'] = '\u0636\u0649';
 t['\uFD24'] = '\u0636\u064A';
 t['\uFD25'] = '\u0634\u062C';
 t['\uFD26'] = '\u0634\u062D';
 t['\uFD27'] = '\u0634\u062E';
 t['\uFD28'] = '\u0634\u0645';
 t['\uFD29'] = '\u0634\u0631';
 t['\uFD2A'] = '\u0633\u0631';
 t['\uFD2B'] = '\u0635\u0631';
 t['\uFD2C'] = '\u0636\u0631';
 t['\uFD2D'] = '\u0634\u062C';
 t['\uFD2E'] = '\u0634\u062D';
 t['\uFD2F'] = '\u0634\u062E';
 t['\uFD30'] = '\u0634\u0645';
 t['\uFD31'] = '\u0633\u0647';
 t['\uFD32'] = '\u0634\u0647';
 t['\uFD33'] = '\u0637\u0645';
 t['\uFD34'] = '\u0633\u062C';
 t['\uFD35'] = '\u0633\u062D';
 t['\uFD36'] = '\u0633\u062E';
 t['\uFD37'] = '\u0634\u062C';
 t['\uFD38'] = '\u0634\u062D';
 t['\uFD39'] = '\u0634\u062E';
 t['\uFD3A'] = '\u0637\u0645';
 t['\uFD3B'] = '\u0638\u0645';
 t['\uFD3C'] = '\u0627\u064B';
 t['\uFD3D'] = '\u0627\u064B';
 t['\uFD50'] = '\u062A\u062C\u0645';
 t['\uFD51'] = '\u062A\u062D\u062C';
 t['\uFD52'] = '\u062A\u062D\u062C';
 t['\uFD53'] = '\u062A\u062D\u0645';
 t['\uFD54'] = '\u062A\u062E\u0645';
 t['\uFD55'] = '\u062A\u0645\u062C';
 t['\uFD56'] = '\u062A\u0645\u062D';
 t['\uFD57'] = '\u062A\u0645\u062E';
 t['\uFD58'] = '\u062C\u0645\u062D';
 t['\uFD59'] = '\u062C\u0645\u062D';
 t['\uFD5A'] = '\u062D\u0645\u064A';
 t['\uFD5B'] = '\u062D\u0645\u0649';
 t['\uFD5C'] = '\u0633\u062D\u062C';
 t['\uFD5D'] = '\u0633\u062C\u062D';
 t['\uFD5E'] = '\u0633\u062C\u0649';
 t['\uFD5F'] = '\u0633\u0645\u062D';
 t['\uFD60'] = '\u0633\u0645\u062D';
 t['\uFD61'] = '\u0633\u0645\u062C';
 t['\uFD62'] = '\u0633\u0645\u0645';
 t['\uFD63'] = '\u0633\u0645\u0645';
 t['\uFD64'] = '\u0635\u062D\u062D';
 t['\uFD65'] = '\u0635\u062D\u062D';
 t['\uFD66'] = '\u0635\u0645\u0645';
 t['\uFD67'] = '\u0634\u062D\u0645';
 t['\uFD68'] = '\u0634\u062D\u0645';
 t['\uFD69'] = '\u0634\u062C\u064A';
 t['\uFD6A'] = '\u0634\u0645\u062E';
 t['\uFD6B'] = '\u0634\u0645\u062E';
 t['\uFD6C'] = '\u0634\u0645\u0645';
 t['\uFD6D'] = '\u0634\u0645\u0645';
 t['\uFD6E'] = '\u0636\u062D\u0649';
 t['\uFD6F'] = '\u0636\u062E\u0645';
 t['\uFD70'] = '\u0636\u062E\u0645';
 t['\uFD71'] = '\u0637\u0645\u062D';
 t['\uFD72'] = '\u0637\u0645\u062D';
 t['\uFD73'] = '\u0637\u0645\u0645';
 t['\uFD74'] = '\u0637\u0645\u064A';
 t['\uFD75'] = '\u0639\u062C\u0645';
 t['\uFD76'] = '\u0639\u0645\u0645';
 t['\uFD77'] = '\u0639\u0645\u0645';
 t['\uFD78'] = '\u0639\u0645\u0649';
 t['\uFD79'] = '\u063A\u0645\u0645';
 t['\uFD7A'] = '\u063A\u0645\u064A';
 t['\uFD7B'] = '\u063A\u0645\u0649';
 t['\uFD7C'] = '\u0641\u062E\u0645';
 t['\uFD7D'] = '\u0641\u062E\u0645';
 t['\uFD7E'] = '\u0642\u0645\u062D';
 t['\uFD7F'] = '\u0642\u0645\u0645';
 t['\uFD80'] = '\u0644\u062D\u0645';
 t['\uFD81'] = '\u0644\u062D\u064A';
 t['\uFD82'] = '\u0644\u062D\u0649';
 t['\uFD83'] = '\u0644\u062C\u062C';
 t['\uFD84'] = '\u0644\u062C\u062C';
 t['\uFD85'] = '\u0644\u062E\u0645';
 t['\uFD86'] = '\u0644\u062E\u0645';
 t['\uFD87'] = '\u0644\u0645\u062D';
 t['\uFD88'] = '\u0644\u0645\u062D';
 t['\uFD89'] = '\u0645\u062D\u062C';
 t['\uFD8A'] = '\u0645\u062D\u0645';
 t['\uFD8B'] = '\u0645\u062D\u064A';
 t['\uFD8C'] = '\u0645\u062C\u062D';
 t['\uFD8D'] = '\u0645\u062C\u0645';
 t['\uFD8E'] = '\u0645\u062E\u062C';
 t['\uFD8F'] = '\u0645\u062E\u0645';
 t['\uFD92'] = '\u0645\u062C\u062E';
 t['\uFD93'] = '\u0647\u0645\u062C';
 t['\uFD94'] = '\u0647\u0645\u0645';
 t['\uFD95'] = '\u0646\u062D\u0645';
 t['\uFD96'] = '\u0646\u062D\u0649';
 t['\uFD97'] = '\u0646\u062C\u0645';
 t['\uFD98'] = '\u0646\u062C\u0645';
 t['\uFD99'] = '\u0646\u062C\u0649';
 t['\uFD9A'] = '\u0646\u0645\u064A';
 t['\uFD9B'] = '\u0646\u0645\u0649';
 t['\uFD9C'] = '\u064A\u0645\u0645';
 t['\uFD9D'] = '\u064A\u0645\u0645';
 t['\uFD9E'] = '\u0628\u062E\u064A';
 t['\uFD9F'] = '\u062A\u062C\u064A';
 t['\uFDA0'] = '\u062A\u062C\u0649';
 t['\uFDA1'] = '\u062A\u062E\u064A';
 t['\uFDA2'] = '\u062A\u062E\u0649';
 t['\uFDA3'] = '\u062A\u0645\u064A';
 t['\uFDA4'] = '\u062A\u0645\u0649';
 t['\uFDA5'] = '\u062C\u0645\u064A';
 t['\uFDA6'] = '\u062C\u062D\u0649';
 t['\uFDA7'] = '\u062C\u0645\u0649';
 t['\uFDA8'] = '\u0633\u062E\u0649';
 t['\uFDA9'] = '\u0635\u062D\u064A';
 t['\uFDAA'] = '\u0634\u062D\u064A';
 t['\uFDAB'] = '\u0636\u062D\u064A';
 t['\uFDAC'] = '\u0644\u062C\u064A';
 t['\uFDAD'] = '\u0644\u0645\u064A';
 t['\uFDAE'] = '\u064A\u062D\u064A';
 t['\uFDAF'] = '\u064A\u062C\u064A';
 t['\uFDB0'] = '\u064A\u0645\u064A';
 t['\uFDB1'] = '\u0645\u0645\u064A';
 t['\uFDB2'] = '\u0642\u0645\u064A';
 t['\uFDB3'] = '\u0646\u062D\u064A';
 t['\uFDB4'] = '\u0642\u0645\u062D';
 t['\uFDB5'] = '\u0644\u062D\u0645';
 t['\uFDB6'] = '\u0639\u0645\u064A';
 t['\uFDB7'] = '\u0643\u0645\u064A';
 t['\uFDB8'] = '\u0646\u062C\u062D';
 t['\uFDB9'] = '\u0645\u062E\u064A';
 t['\uFDBA'] = '\u0644\u062C\u0645';
 t['\uFDBB'] = '\u0643\u0645\u0645';
 t['\uFDBC'] = '\u0644\u062C\u0645';
 t['\uFDBD'] = '\u0646\u062C\u062D';
 t['\uFDBE'] = '\u062C\u062D\u064A';
 t['\uFDBF'] = '\u062D\u062C\u064A';
 t['\uFDC0'] = '\u0645\u062C\u064A';
 t['\uFDC1'] = '\u0641\u0645\u064A';
 t['\uFDC2'] = '\u0628\u062D\u064A';
 t['\uFDC3'] = '\u0643\u0645\u0645';
 t['\uFDC4'] = '\u0639\u062C\u0645';
 t['\uFDC5'] = '\u0635\u0645\u0645';
 t['\uFDC6'] = '\u0633\u062E\u064A';
 t['\uFDC7'] = '\u0646\u062C\u064A';
 t['\uFE49'] = '\u203E';
 t['\uFE4A'] = '\u203E';
 t['\uFE4B'] = '\u203E';
 t['\uFE4C'] = '\u203E';
 t['\uFE4D'] = '\u005F';
 t['\uFE4E'] = '\u005F';
 t['\uFE4F'] = '\u005F';
 t['\uFE80'] = '\u0621';
 t['\uFE81'] = '\u0622';
 t['\uFE82'] = '\u0622';
 t['\uFE83'] = '\u0623';
 t['\uFE84'] = '\u0623';
 t['\uFE85'] = '\u0624';
 t['\uFE86'] = '\u0624';
 t['\uFE87'] = '\u0625';
 t['\uFE88'] = '\u0625';
 t['\uFE89'] = '\u0626';
 t['\uFE8A'] = '\u0626';
 t['\uFE8B'] = '\u0626';
 t['\uFE8C'] = '\u0626';
 t['\uFE8D'] = '\u0627';
 t['\uFE8E'] = '\u0627';
 t['\uFE8F'] = '\u0628';
 t['\uFE90'] = '\u0628';
 t['\uFE91'] = '\u0628';
 t['\uFE92'] = '\u0628';
 t['\uFE93'] = '\u0629';
 t['\uFE94'] = '\u0629';
 t['\uFE95'] = '\u062A';
 t['\uFE96'] = '\u062A';
 t['\uFE97'] = '\u062A';
 t['\uFE98'] = '\u062A';
 t['\uFE99'] = '\u062B';
 t['\uFE9A'] = '\u062B';
 t['\uFE9B'] = '\u062B';
 t['\uFE9C'] = '\u062B';
 t['\uFE9D'] = '\u062C';
 t['\uFE9E'] = '\u062C';
 t['\uFE9F'] = '\u062C';
 t['\uFEA0'] = '\u062C';
 t['\uFEA1'] = '\u062D';
 t['\uFEA2'] = '\u062D';
 t['\uFEA3'] = '\u062D';
 t['\uFEA4'] = '\u062D';
 t['\uFEA5'] = '\u062E';
 t['\uFEA6'] = '\u062E';
 t['\uFEA7'] = '\u062E';
 t['\uFEA8'] = '\u062E';
 t['\uFEA9'] = '\u062F';
 t['\uFEAA'] = '\u062F';
 t['\uFEAB'] = '\u0630';
 t['\uFEAC'] = '\u0630';
 t['\uFEAD'] = '\u0631';
 t['\uFEAE'] = '\u0631';
 t['\uFEAF'] = '\u0632';
 t['\uFEB0'] = '\u0632';
 t['\uFEB1'] = '\u0633';
 t['\uFEB2'] = '\u0633';
 t['\uFEB3'] = '\u0633';
 t['\uFEB4'] = '\u0633';
 t['\uFEB5'] = '\u0634';
 t['\uFEB6'] = '\u0634';
 t['\uFEB7'] = '\u0634';
 t['\uFEB8'] = '\u0634';
 t['\uFEB9'] = '\u0635';
 t['\uFEBA'] = '\u0635';
 t['\uFEBB'] = '\u0635';
 t['\uFEBC'] = '\u0635';
 t['\uFEBD'] = '\u0636';
 t['\uFEBE'] = '\u0636';
 t['\uFEBF'] = '\u0636';
 t['\uFEC0'] = '\u0636';
 t['\uFEC1'] = '\u0637';
 t['\uFEC2'] = '\u0637';
 t['\uFEC3'] = '\u0637';
 t['\uFEC4'] = '\u0637';
 t['\uFEC5'] = '\u0638';
 t['\uFEC6'] = '\u0638';
 t['\uFEC7'] = '\u0638';
 t['\uFEC8'] = '\u0638';
 t['\uFEC9'] = '\u0639';
 t['\uFECA'] = '\u0639';
 t['\uFECB'] = '\u0639';
 t['\uFECC'] = '\u0639';
 t['\uFECD'] = '\u063A';
 t['\uFECE'] = '\u063A';
 t['\uFECF'] = '\u063A';
 t['\uFED0'] = '\u063A';
 t['\uFED1'] = '\u0641';
 t['\uFED2'] = '\u0641';
 t['\uFED3'] = '\u0641';
 t['\uFED4'] = '\u0641';
 t['\uFED5'] = '\u0642';
 t['\uFED6'] = '\u0642';
 t['\uFED7'] = '\u0642';
 t['\uFED8'] = '\u0642';
 t['\uFED9'] = '\u0643';
 t['\uFEDA'] = '\u0643';
 t['\uFEDB'] = '\u0643';
 t['\uFEDC'] = '\u0643';
 t['\uFEDD'] = '\u0644';
 t['\uFEDE'] = '\u0644';
 t['\uFEDF'] = '\u0644';
 t['\uFEE0'] = '\u0644';
 t['\uFEE1'] = '\u0645';
 t['\uFEE2'] = '\u0645';
 t['\uFEE3'] = '\u0645';
 t['\uFEE4'] = '\u0645';
 t['\uFEE5'] = '\u0646';
 t['\uFEE6'] = '\u0646';
 t['\uFEE7'] = '\u0646';
 t['\uFEE8'] = '\u0646';
 t['\uFEE9'] = '\u0647';
 t['\uFEEA'] = '\u0647';
 t['\uFEEB'] = '\u0647';
 t['\uFEEC'] = '\u0647';
 t['\uFEED'] = '\u0648';
 t['\uFEEE'] = '\u0648';
 t['\uFEEF'] = '\u0649';
 t['\uFEF0'] = '\u0649';
 t['\uFEF1'] = '\u064A';
 t['\uFEF2'] = '\u064A';
 t['\uFEF3'] = '\u064A';
 t['\uFEF4'] = '\u064A';
 t['\uFEF5'] = '\u0644\u0622';
 t['\uFEF6'] = '\u0644\u0622';
 t['\uFEF7'] = '\u0644\u0623';
 t['\uFEF8'] = '\u0644\u0623';
 t['\uFEF9'] = '\u0644\u0625';
 t['\uFEFA'] = '\u0644\u0625';
 t['\uFEFB'] = '\u0644\u0627';
 t['\uFEFC'] = '\u0644\u0627';
});
function reverseIfRtl(chars) {
 var charsLength = chars.length;
 if (charsLength <= 1 || !isRTLRangeFor(chars.charCodeAt(0))) {
  return chars;
 }
 var s = '';
 for (var ii = charsLength - 1; ii >= 0; ii--) {
  s += chars[ii];
 }
 return s;
}
exports.mapSpecialUnicodeValues = mapSpecialUnicodeValues;
exports.reverseIfRtl = reverseIfRtl;
exports.getUnicodeRangeFor = getUnicodeRangeFor;
exports.getNormalizedUnicodes = getNormalizedUnicodes;
exports.getUnicodeForGlyph = getUnicodeForGlyph;

/***/ }),
/* 18 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.PostScriptCompiler = exports.PostScriptEvaluator = exports.PDFFunctionFactory = exports.isPDFFunction = undefined;

var _util = __w_pdfjs_require__(0);

var _primitives = __w_pdfjs_require__(1);

var _ps_parser = __w_pdfjs_require__(41);

let IsEvalSupportedCached = {
  get value() {
    return (0, _util.shadow)(this, 'value', (0, _util.isEvalSupported)());
  }
};
class PDFFunctionFactory {
  constructor({ xref, isEvalSupported = true }) {
    this.xref = xref;
    this.isEvalSupported = isEvalSupported !== false;
  }
  create(fn) {
    return PDFFunction.parse({
      xref: this.xref,
      isEvalSupported: this.isEvalSupported,
      fn
    });
  }
  createFromArray(fnObj) {
    return PDFFunction.parseArray({
      xref: this.xref,
      isEvalSupported: this.isEvalSupported,
      fnObj
    });
  }
}
function toNumberArray(arr) {
  if (!Array.isArray(arr)) {
    return null;
  }
  const length = arr.length;
  for (let i = 0; i < length; i++) {
    if (typeof arr[i] !== 'number') {
      const result = new Array(length);
      for (let i = 0; i < length; i++) {
        result[i] = +arr[i];
      }
      return result;
    }
  }
  return arr;
}
var PDFFunction = function PDFFunctionClosure() {
  const CONSTRUCT_SAMPLED = 0;
  const CONSTRUCT_INTERPOLATED = 2;
  const CONSTRUCT_STICHED = 3;
  const CONSTRUCT_POSTSCRIPT = 4;
  return {
    getSampleArray(size, outputSize, bps, stream) {
      var i, ii;
      var length = 1;
      for (i = 0, ii = size.length; i < ii; i++) {
        length *= size[i];
      }
      length *= outputSize;
      var array = new Array(length);
      var codeSize = 0;
      var codeBuf = 0;
      var sampleMul = 1.0 / (Math.pow(2.0, bps) - 1);
      var strBytes = stream.getBytes((length * bps + 7) / 8);
      var strIdx = 0;
      for (i = 0; i < length; i++) {
        while (codeSize < bps) {
          codeBuf <<= 8;
          codeBuf |= strBytes[strIdx++];
          codeSize += 8;
        }
        codeSize -= bps;
        array[i] = (codeBuf >> codeSize) * sampleMul;
        codeBuf &= (1 << codeSize) - 1;
      }
      return array;
    },
    getIR({ xref, isEvalSupported, fn }) {
      var dict = fn.dict;
      if (!dict) {
        dict = fn;
      }
      var types = [this.constructSampled, null, this.constructInterpolated, this.constructStiched, this.constructPostScript];
      var typeNum = dict.get('FunctionType');
      var typeFn = types[typeNum];
      if (!typeFn) {
        throw new _util.FormatError('Unknown type of function');
      }
      return typeFn.call(this, {
        xref,
        isEvalSupported,
        fn,
        dict
      });
    },
    fromIR({ xref, isEvalSupported, IR }) {
      var type = IR[0];
      switch (type) {
        case CONSTRUCT_SAMPLED:
          return this.constructSampledFromIR({
            xref,
            isEvalSupported,
            IR
          });
        case CONSTRUCT_INTERPOLATED:
          return this.constructInterpolatedFromIR({
            xref,
            isEvalSupported,
            IR
          });
        case CONSTRUCT_STICHED:
          return this.constructStichedFromIR({
            xref,
            isEvalSupported,
            IR
          });
        default:
          return this.constructPostScriptFromIR({
            xref,
            isEvalSupported,
            IR
          });
      }
    },
    parse({ xref, isEvalSupported, fn }) {
      let IR = this.getIR({
        xref,
        isEvalSupported,
        fn
      });
      return this.fromIR({
        xref,
        isEvalSupported,
        IR
      });
    },
    parseArray({ xref, isEvalSupported, fnObj }) {
      if (!Array.isArray(fnObj)) {
        return this.parse({
          xref,
          isEvalSupported,
          fn: fnObj
        });
      }
      var fnArray = [];
      for (var j = 0, jj = fnObj.length; j < jj; j++) {
        fnArray.push(this.parse({
          xref,
          isEvalSupported,
          fn: xref.fetchIfRef(fnObj[j])
        }));
      }
      return function (src, srcOffset, dest, destOffset) {
        for (var i = 0, ii = fnArray.length; i < ii; i++) {
          fnArray[i](src, srcOffset, dest, destOffset + i);
        }
      };
    },
    constructSampled({ xref, isEvalSupported, fn, dict }) {
      function toMultiArray(arr) {
        var inputLength = arr.length;
        var out = [];
        var index = 0;
        for (var i = 0; i < inputLength; i += 2) {
          out[index] = [arr[i], arr[i + 1]];
          ++index;
        }
        return out;
      }
      var domain = toNumberArray(dict.getArray('Domain'));
      var range = toNumberArray(dict.getArray('Range'));
      if (!domain || !range) {
        throw new _util.FormatError('No domain or range');
      }
      var inputSize = domain.length / 2;
      var outputSize = range.length / 2;
      domain = toMultiArray(domain);
      range = toMultiArray(range);
      var size = toNumberArray(dict.get('Size'));
      var bps = dict.get('BitsPerSample');
      var order = dict.get('Order') || 1;
      if (order !== 1) {
        (0, _util.info)('No support for cubic spline interpolation: ' + order);
      }
      var encode = toNumberArray(dict.getArray('Encode'));
      if (!encode) {
        encode = [];
        for (var i = 0; i < inputSize; ++i) {
          encode.push([0, size[i] - 1]);
        }
      } else {
        encode = toMultiArray(encode);
      }
      var decode = toNumberArray(dict.getArray('Decode'));
      if (!decode) {
        decode = range;
      } else {
        decode = toMultiArray(decode);
      }
      var samples = this.getSampleArray(size, outputSize, bps, fn);
      return [CONSTRUCT_SAMPLED, inputSize, domain, encode, decode, samples, size, outputSize, Math.pow(2, bps) - 1, range];
    },
    constructSampledFromIR({ xref, isEvalSupported, IR }) {
      function interpolate(x, xmin, xmax, ymin, ymax) {
        return ymin + (x - xmin) * ((ymax - ymin) / (xmax - xmin));
      }
      return function constructSampledFromIRResult(src, srcOffset, dest, destOffset) {
        var m = IR[1];
        var domain = IR[2];
        var encode = IR[3];
        var decode = IR[4];
        var samples = IR[5];
        var size = IR[6];
        var n = IR[7];
        var range = IR[9];
        var cubeVertices = 1 << m;
        var cubeN = new Float64Array(cubeVertices);
        var cubeVertex = new Uint32Array(cubeVertices);
        var i, j;
        for (j = 0; j < cubeVertices; j++) {
          cubeN[j] = 1;
        }
        var k = n,
            pos = 1;
        for (i = 0; i < m; ++i) {
          var domain_2i = domain[i][0];
          var domain_2i_1 = domain[i][1];
          var xi = Math.min(Math.max(src[srcOffset + i], domain_2i), domain_2i_1);
          var e = interpolate(xi, domain_2i, domain_2i_1, encode[i][0], encode[i][1]);
          var size_i = size[i];
          e = Math.min(Math.max(e, 0), size_i - 1);
          var e0 = e < size_i - 1 ? Math.floor(e) : e - 1;
          var n0 = e0 + 1 - e;
          var n1 = e - e0;
          var offset0 = e0 * k;
          var offset1 = offset0 + k;
          for (j = 0; j < cubeVertices; j++) {
            if (j & pos) {
              cubeN[j] *= n1;
              cubeVertex[j] += offset1;
            } else {
              cubeN[j] *= n0;
              cubeVertex[j] += offset0;
            }
          }
          k *= size_i;
          pos <<= 1;
        }
        for (j = 0; j < n; ++j) {
          var rj = 0;
          for (i = 0; i < cubeVertices; i++) {
            rj += samples[cubeVertex[i] + j] * cubeN[i];
          }
          rj = interpolate(rj, 0, 1, decode[j][0], decode[j][1]);
          dest[destOffset + j] = Math.min(Math.max(rj, range[j][0]), range[j][1]);
        }
      };
    },
    constructInterpolated({ xref, isEvalSupported, fn, dict }) {
      var c0 = toNumberArray(dict.getArray('C0')) || [0];
      var c1 = toNumberArray(dict.getArray('C1')) || [1];
      var n = dict.get('N');
      var length = c0.length;
      var diff = [];
      for (var i = 0; i < length; ++i) {
        diff.push(c1[i] - c0[i]);
      }
      return [CONSTRUCT_INTERPOLATED, c0, diff, n];
    },
    constructInterpolatedFromIR({ xref, isEvalSupported, IR }) {
      var c0 = IR[1];
      var diff = IR[2];
      var n = IR[3];
      var length = diff.length;
      return function constructInterpolatedFromIRResult(src, srcOffset, dest, destOffset) {
        var x = n === 1 ? src[srcOffset] : Math.pow(src[srcOffset], n);
        for (var j = 0; j < length; ++j) {
          dest[destOffset + j] = c0[j] + x * diff[j];
        }
      };
    },
    constructStiched({ xref, isEvalSupported, fn, dict }) {
      var domain = toNumberArray(dict.getArray('Domain'));
      if (!domain) {
        throw new _util.FormatError('No domain');
      }
      var inputSize = domain.length / 2;
      if (inputSize !== 1) {
        throw new _util.FormatError('Bad domain for stiched function');
      }
      var fnRefs = dict.get('Functions');
      var fns = [];
      for (var i = 0, ii = fnRefs.length; i < ii; ++i) {
        fns.push(this.parse({
          xref,
          isEvalSupported,
          fn: xref.fetchIfRef(fnRefs[i])
        }));
      }
      var bounds = toNumberArray(dict.getArray('Bounds'));
      var encode = toNumberArray(dict.getArray('Encode'));
      return [CONSTRUCT_STICHED, domain, bounds, encode, fns];
    },
    constructStichedFromIR({ xref, isEvalSupported, IR }) {
      var domain = IR[1];
      var bounds = IR[2];
      var encode = IR[3];
      var fns = IR[4];
      var tmpBuf = new Float32Array(1);
      return function constructStichedFromIRResult(src, srcOffset, dest, destOffset) {
        var clip = function constructStichedFromIRClip(v, min, max) {
          if (v > max) {
            v = max;
          } else if (v < min) {
            v = min;
          }
          return v;
        };
        var v = clip(src[srcOffset], domain[0], domain[1]);
        for (var i = 0, ii = bounds.length; i < ii; ++i) {
          if (v < bounds[i]) {
            break;
          }
        }
        var dmin = domain[0];
        if (i > 0) {
          dmin = bounds[i - 1];
        }
        var dmax = domain[1];
        if (i < bounds.length) {
          dmax = bounds[i];
        }
        var rmin = encode[2 * i];
        var rmax = encode[2 * i + 1];
        tmpBuf[0] = dmin === dmax ? rmin : rmin + (v - dmin) * (rmax - rmin) / (dmax - dmin);
        fns[i](tmpBuf, 0, dest, destOffset);
      };
    },
    constructPostScript({ xref, isEvalSupported, fn, dict }) {
      var domain = toNumberArray(dict.getArray('Domain'));
      var range = toNumberArray(dict.getArray('Range'));
      if (!domain) {
        throw new _util.FormatError('No domain.');
      }
      if (!range) {
        throw new _util.FormatError('No range.');
      }
      var lexer = new _ps_parser.PostScriptLexer(fn);
      var parser = new _ps_parser.PostScriptParser(lexer);
      var code = parser.parse();
      return [CONSTRUCT_POSTSCRIPT, domain, range, code];
    },
    constructPostScriptFromIR({ xref, isEvalSupported, IR }) {
      var domain = IR[1];
      var range = IR[2];
      var code = IR[3];
      if (isEvalSupported && IsEvalSupportedCached.value) {
        let compiled = new PostScriptCompiler().compile(code, domain, range);
        if (compiled) {
          return new Function('src', 'srcOffset', 'dest', 'destOffset', compiled);
        }
      }
      (0, _util.info)('Unable to compile PS function');
      var numOutputs = range.length >> 1;
      var numInputs = domain.length >> 1;
      var evaluator = new PostScriptEvaluator(code);
      var cache = Object.create(null);
      var MAX_CACHE_SIZE = 2048 * 4;
      var cache_available = MAX_CACHE_SIZE;
      var tmpBuf = new Float32Array(numInputs);
      return function constructPostScriptFromIRResult(src, srcOffset, dest, destOffset) {
        var i, value;
        var key = '';
        var input = tmpBuf;
        for (i = 0; i < numInputs; i++) {
          value = src[srcOffset + i];
          input[i] = value;
          key += value + '_';
        }
        var cachedValue = cache[key];
        if (cachedValue !== undefined) {
          dest.set(cachedValue, destOffset);
          return;
        }
        var output = new Float32Array(numOutputs);
        var stack = evaluator.execute(input);
        var stackIndex = stack.length - numOutputs;
        for (i = 0; i < numOutputs; i++) {
          value = stack[stackIndex + i];
          var bound = range[i * 2];
          if (value < bound) {
            value = bound;
          } else {
            bound = range[i * 2 + 1];
            if (value > bound) {
              value = bound;
            }
          }
          output[i] = value;
        }
        if (cache_available > 0) {
          cache_available--;
          cache[key] = output;
        }
        dest.set(output, destOffset);
      };
    }
  };
}();
function isPDFFunction(v) {
  var fnDict;
  if (typeof v !== 'object') {
    return false;
  } else if ((0, _primitives.isDict)(v)) {
    fnDict = v;
  } else if ((0, _primitives.isStream)(v)) {
    fnDict = v.dict;
  } else {
    return false;
  }
  return fnDict.has('FunctionType');
}
var PostScriptStack = function PostScriptStackClosure() {
  var MAX_STACK_SIZE = 100;
  function PostScriptStack(initialStack) {
    this.stack = !initialStack ? [] : Array.prototype.slice.call(initialStack, 0);
  }
  PostScriptStack.prototype = {
    push: function PostScriptStack_push(value) {
      if (this.stack.length >= MAX_STACK_SIZE) {
        throw new Error('PostScript function stack overflow.');
      }
      this.stack.push(value);
    },
    pop: function PostScriptStack_pop() {
      if (this.stack.length <= 0) {
        throw new Error('PostScript function stack underflow.');
      }
      return this.stack.pop();
    },
    copy: function PostScriptStack_copy(n) {
      if (this.stack.length + n >= MAX_STACK_SIZE) {
        throw new Error('PostScript function stack overflow.');
      }
      var stack = this.stack;
      for (var i = stack.length - n, j = n - 1; j >= 0; j--, i++) {
        stack.push(stack[i]);
      }
    },
    index: function PostScriptStack_index(n) {
      this.push(this.stack[this.stack.length - n - 1]);
    },
    roll: function PostScriptStack_roll(n, p) {
      var stack = this.stack;
      var l = stack.length - n;
      var r = stack.length - 1,
          c = l + (p - Math.floor(p / n) * n),
          i,
          j,
          t;
      for (i = l, j = r; i < j; i++, j--) {
        t = stack[i];
        stack[i] = stack[j];
        stack[j] = t;
      }
      for (i = l, j = c - 1; i < j; i++, j--) {
        t = stack[i];
        stack[i] = stack[j];
        stack[j] = t;
      }
      for (i = c, j = r; i < j; i++, j--) {
        t = stack[i];
        stack[i] = stack[j];
        stack[j] = t;
      }
    }
  };
  return PostScriptStack;
}();
var PostScriptEvaluator = function PostScriptEvaluatorClosure() {
  function PostScriptEvaluator(operators) {
    this.operators = operators;
  }
  PostScriptEvaluator.prototype = {
    execute: function PostScriptEvaluator_execute(initialStack) {
      var stack = new PostScriptStack(initialStack);
      var counter = 0;
      var operators = this.operators;
      var length = operators.length;
      var operator, a, b;
      while (counter < length) {
        operator = operators[counter++];
        if (typeof operator === 'number') {
          stack.push(operator);
          continue;
        }
        switch (operator) {
          case 'jz':
            b = stack.pop();
            a = stack.pop();
            if (!a) {
              counter = b;
            }
            break;
          case 'j':
            a = stack.pop();
            counter = a;
            break;
          case 'abs':
            a = stack.pop();
            stack.push(Math.abs(a));
            break;
          case 'add':
            b = stack.pop();
            a = stack.pop();
            stack.push(a + b);
            break;
          case 'and':
            b = stack.pop();
            a = stack.pop();
            if ((0, _util.isBool)(a) && (0, _util.isBool)(b)) {
              stack.push(a && b);
            } else {
              stack.push(a & b);
            }
            break;
          case 'atan':
            a = stack.pop();
            stack.push(Math.atan(a));
            break;
          case 'bitshift':
            b = stack.pop();
            a = stack.pop();
            if (a > 0) {
              stack.push(a << b);
            } else {
              stack.push(a >> b);
            }
            break;
          case 'ceiling':
            a = stack.pop();
            stack.push(Math.ceil(a));
            break;
          case 'copy':
            a = stack.pop();
            stack.copy(a);
            break;
          case 'cos':
            a = stack.pop();
            stack.push(Math.cos(a));
            break;
          case 'cvi':
            a = stack.pop() | 0;
            stack.push(a);
            break;
          case 'cvr':
            break;
          case 'div':
            b = stack.pop();
            a = stack.pop();
            stack.push(a / b);
            break;
          case 'dup':
            stack.copy(1);
            break;
          case 'eq':
            b = stack.pop();
            a = stack.pop();
            stack.push(a === b);
            break;
          case 'exch':
            stack.roll(2, 1);
            break;
          case 'exp':
            b = stack.pop();
            a = stack.pop();
            stack.push(Math.pow(a, b));
            break;
          case 'false':
            stack.push(false);
            break;
          case 'floor':
            a = stack.pop();
            stack.push(Math.floor(a));
            break;
          case 'ge':
            b = stack.pop();
            a = stack.pop();
            stack.push(a >= b);
            break;
          case 'gt':
            b = stack.pop();
            a = stack.pop();
            stack.push(a > b);
            break;
          case 'idiv':
            b = stack.pop();
            a = stack.pop();
            stack.push(a / b | 0);
            break;
          case 'index':
            a = stack.pop();
            stack.index(a);
            break;
          case 'le':
            b = stack.pop();
            a = stack.pop();
            stack.push(a <= b);
            break;
          case 'ln':
            a = stack.pop();
            stack.push(Math.log(a));
            break;
          case 'log':
            a = stack.pop();
            stack.push(Math.log(a) / Math.LN10);
            break;
          case 'lt':
            b = stack.pop();
            a = stack.pop();
            stack.push(a < b);
            break;
          case 'mod':
            b = stack.pop();
            a = stack.pop();
            stack.push(a % b);
            break;
          case 'mul':
            b = stack.pop();
            a = stack.pop();
            stack.push(a * b);
            break;
          case 'ne':
            b = stack.pop();
            a = stack.pop();
            stack.push(a !== b);
            break;
          case 'neg':
            a = stack.pop();
            stack.push(-a);
            break;
          case 'not':
            a = stack.pop();
            if ((0, _util.isBool)(a)) {
              stack.push(!a);
            } else {
              stack.push(~a);
            }
            break;
          case 'or':
            b = stack.pop();
            a = stack.pop();
            if ((0, _util.isBool)(a) && (0, _util.isBool)(b)) {
              stack.push(a || b);
            } else {
              stack.push(a | b);
            }
            break;
          case 'pop':
            stack.pop();
            break;
          case 'roll':
            b = stack.pop();
            a = stack.pop();
            stack.roll(a, b);
            break;
          case 'round':
            a = stack.pop();
            stack.push(Math.round(a));
            break;
          case 'sin':
            a = stack.pop();
            stack.push(Math.sin(a));
            break;
          case 'sqrt':
            a = stack.pop();
            stack.push(Math.sqrt(a));
            break;
          case 'sub':
            b = stack.pop();
            a = stack.pop();
            stack.push(a - b);
            break;
          case 'true':
            stack.push(true);
            break;
          case 'truncate':
            a = stack.pop();
            a = a < 0 ? Math.ceil(a) : Math.floor(a);
            stack.push(a);
            break;
          case 'xor':
            b = stack.pop();
            a = stack.pop();
            if ((0, _util.isBool)(a) && (0, _util.isBool)(b)) {
              stack.push(a !== b);
            } else {
              stack.push(a ^ b);
            }
            break;
          default:
            throw new _util.FormatError(`Unknown operator ${operator}`);
        }
      }
      return stack.stack;
    }
  };
  return PostScriptEvaluator;
}();
var PostScriptCompiler = function PostScriptCompilerClosure() {
  function AstNode(type) {
    this.type = type;
  }
  AstNode.prototype.visit = function (visitor) {
    (0, _util.unreachable)('abstract method');
  };
  function AstArgument(index, min, max) {
    AstNode.call(this, 'args');
    this.index = index;
    this.min = min;
    this.max = max;
  }
  AstArgument.prototype = Object.create(AstNode.prototype);
  AstArgument.prototype.visit = function (visitor) {
    visitor.visitArgument(this);
  };
  function AstLiteral(number) {
    AstNode.call(this, 'literal');
    this.number = number;
    this.min = number;
    this.max = number;
  }
  AstLiteral.prototype = Object.create(AstNode.prototype);
  AstLiteral.prototype.visit = function (visitor) {
    visitor.visitLiteral(this);
  };
  function AstBinaryOperation(op, arg1, arg2, min, max) {
    AstNode.call(this, 'binary');
    this.op = op;
    this.arg1 = arg1;
    this.arg2 = arg2;
    this.min = min;
    this.max = max;
  }
  AstBinaryOperation.prototype = Object.create(AstNode.prototype);
  AstBinaryOperation.prototype.visit = function (visitor) {
    visitor.visitBinaryOperation(this);
  };
  function AstMin(arg, max) {
    AstNode.call(this, 'max');
    this.arg = arg;
    this.min = arg.min;
    this.max = max;
  }
  AstMin.prototype = Object.create(AstNode.prototype);
  AstMin.prototype.visit = function (visitor) {
    visitor.visitMin(this);
  };
  function AstVariable(index, min, max) {
    AstNode.call(this, 'var');
    this.index = index;
    this.min = min;
    this.max = max;
  }
  AstVariable.prototype = Object.create(AstNode.prototype);
  AstVariable.prototype.visit = function (visitor) {
    visitor.visitVariable(this);
  };
  function AstVariableDefinition(variable, arg) {
    AstNode.call(this, 'definition');
    this.variable = variable;
    this.arg = arg;
  }
  AstVariableDefinition.prototype = Object.create(AstNode.prototype);
  AstVariableDefinition.prototype.visit = function (visitor) {
    visitor.visitVariableDefinition(this);
  };
  function ExpressionBuilderVisitor() {
    this.parts = [];
  }
  ExpressionBuilderVisitor.prototype = {
    visitArgument(arg) {
      this.parts.push('Math.max(', arg.min, ', Math.min(', arg.max, ', src[srcOffset + ', arg.index, ']))');
    },
    visitVariable(variable) {
      this.parts.push('v', variable.index);
    },
    visitLiteral(literal) {
      this.parts.push(literal.number);
    },
    visitBinaryOperation(operation) {
      this.parts.push('(');
      operation.arg1.visit(this);
      this.parts.push(' ', operation.op, ' ');
      operation.arg2.visit(this);
      this.parts.push(')');
    },
    visitVariableDefinition(definition) {
      this.parts.push('var ');
      definition.variable.visit(this);
      this.parts.push(' = ');
      definition.arg.visit(this);
      this.parts.push(';');
    },
    visitMin(max) {
      this.parts.push('Math.min(');
      max.arg.visit(this);
      this.parts.push(', ', max.max, ')');
    },
    toString() {
      return this.parts.join('');
    }
  };
  function buildAddOperation(num1, num2) {
    if (num2.type === 'literal' && num2.number === 0) {
      return num1;
    }
    if (num1.type === 'literal' && num1.number === 0) {
      return num2;
    }
    if (num2.type === 'literal' && num1.type === 'literal') {
      return new AstLiteral(num1.number + num2.number);
    }
    return new AstBinaryOperation('+', num1, num2, num1.min + num2.min, num1.max + num2.max);
  }
  function buildMulOperation(num1, num2) {
    if (num2.type === 'literal') {
      if (num2.number === 0) {
        return new AstLiteral(0);
      } else if (num2.number === 1) {
        return num1;
      } else if (num1.type === 'literal') {
        return new AstLiteral(num1.number * num2.number);
      }
    }
    if (num1.type === 'literal') {
      if (num1.number === 0) {
        return new AstLiteral(0);
      } else if (num1.number === 1) {
        return num2;
      }
    }
    var min = Math.min(num1.min * num2.min, num1.min * num2.max, num1.max * num2.min, num1.max * num2.max);
    var max = Math.max(num1.min * num2.min, num1.min * num2.max, num1.max * num2.min, num1.max * num2.max);
    return new AstBinaryOperation('*', num1, num2, min, max);
  }
  function buildSubOperation(num1, num2) {
    if (num2.type === 'literal') {
      if (num2.number === 0) {
        return num1;
      } else if (num1.type === 'literal') {
        return new AstLiteral(num1.number - num2.number);
      }
    }
    if (num2.type === 'binary' && num2.op === '-' && num1.type === 'literal' && num1.number === 1 && num2.arg1.type === 'literal' && num2.arg1.number === 1) {
      return num2.arg2;
    }
    return new AstBinaryOperation('-', num1, num2, num1.min - num2.max, num1.max - num2.min);
  }
  function buildMinOperation(num1, max) {
    if (num1.min >= max) {
      return new AstLiteral(max);
    } else if (num1.max <= max) {
      return num1;
    }
    return new AstMin(num1, max);
  }
  function PostScriptCompiler() {}
  PostScriptCompiler.prototype = {
    compile: function PostScriptCompiler_compile(code, domain, range) {
      var stack = [];
      var i, ii;
      var instructions = [];
      var inputSize = domain.length >> 1,
          outputSize = range.length >> 1;
      var lastRegister = 0;
      var n, j;
      var num1, num2, ast1, ast2, tmpVar, item;
      for (i = 0; i < inputSize; i++) {
        stack.push(new AstArgument(i, domain[i * 2], domain[i * 2 + 1]));
      }
      for (i = 0, ii = code.length; i < ii; i++) {
        item = code[i];
        if (typeof item === 'number') {
          stack.push(new AstLiteral(item));
          continue;
        }
        switch (item) {
          case 'add':
            if (stack.length < 2) {
              return null;
            }
            num2 = stack.pop();
            num1 = stack.pop();
            stack.push(buildAddOperation(num1, num2));
            break;
          case 'cvr':
            if (stack.length < 1) {
              return null;
            }
            break;
          case 'mul':
            if (stack.length < 2) {
              return null;
            }
            num2 = stack.pop();
            num1 = stack.pop();
            stack.push(buildMulOperation(num1, num2));
            break;
          case 'sub':
            if (stack.length < 2) {
              return null;
            }
            num2 = stack.pop();
            num1 = stack.pop();
            stack.push(buildSubOperation(num1, num2));
            break;
          case 'exch':
            if (stack.length < 2) {
              return null;
            }
            ast1 = stack.pop();
            ast2 = stack.pop();
            stack.push(ast1, ast2);
            break;
          case 'pop':
            if (stack.length < 1) {
              return null;
            }
            stack.pop();
            break;
          case 'index':
            if (stack.length < 1) {
              return null;
            }
            num1 = stack.pop();
            if (num1.type !== 'literal') {
              return null;
            }
            n = num1.number;
            if (n < 0 || !Number.isInteger(n) || stack.length < n) {
              return null;
            }
            ast1 = stack[stack.length - n - 1];
            if (ast1.type === 'literal' || ast1.type === 'var') {
              stack.push(ast1);
              break;
            }
            tmpVar = new AstVariable(lastRegister++, ast1.min, ast1.max);
            stack[stack.length - n - 1] = tmpVar;
            stack.push(tmpVar);
            instructions.push(new AstVariableDefinition(tmpVar, ast1));
            break;
          case 'dup':
            if (stack.length < 1) {
              return null;
            }
            if (typeof code[i + 1] === 'number' && code[i + 2] === 'gt' && code[i + 3] === i + 7 && code[i + 4] === 'jz' && code[i + 5] === 'pop' && code[i + 6] === code[i + 1]) {
              num1 = stack.pop();
              stack.push(buildMinOperation(num1, code[i + 1]));
              i += 6;
              break;
            }
            ast1 = stack[stack.length - 1];
            if (ast1.type === 'literal' || ast1.type === 'var') {
              stack.push(ast1);
              break;
            }
            tmpVar = new AstVariable(lastRegister++, ast1.min, ast1.max);
            stack[stack.length - 1] = tmpVar;
            stack.push(tmpVar);
            instructions.push(new AstVariableDefinition(tmpVar, ast1));
            break;
          case 'roll':
            if (stack.length < 2) {
              return null;
            }
            num2 = stack.pop();
            num1 = stack.pop();
            if (num2.type !== 'literal' || num1.type !== 'literal') {
              return null;
            }
            j = num2.number;
            n = num1.number;
            if (n <= 0 || !Number.isInteger(n) || !Number.isInteger(j) || stack.length < n) {
              return null;
            }
            j = (j % n + n) % n;
            if (j === 0) {
              break;
            }
            Array.prototype.push.apply(stack, stack.splice(stack.length - n, n - j));
            break;
          default:
            return null;
        }
      }
      if (stack.length !== outputSize) {
        return null;
      }
      var result = [];
      instructions.forEach(function (instruction) {
        var statementBuilder = new ExpressionBuilderVisitor();
        instruction.visit(statementBuilder);
        result.push(statementBuilder.toString());
      });
      stack.forEach(function (expr, i) {
        var statementBuilder = new ExpressionBuilderVisitor();
        expr.visit(statementBuilder);
        var min = range[i * 2],
            max = range[i * 2 + 1];
        var out = [statementBuilder.toString()];
        if (min > expr.min) {
          out.unshift('Math.max(', min, ', ');
          out.push(')');
        }
        if (max < expr.max) {
          out.unshift('Math.min(', max, ', ');
          out.push(')');
        }
        out.unshift('dest[destOffset + ', i, '] = ');
        out.push(';');
        result.push(out.join(''));
      });
      return result.join('\n');
    }
  };
  return PostScriptCompiler;
}();
exports.isPDFFunction = isPDFFunction;
exports.PDFFunctionFactory = PDFFunctionFactory;
exports.PostScriptEvaluator = PostScriptEvaluator;
exports.PostScriptCompiler = PostScriptCompiler;

/***/ }),
/* 19 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


var pdfjsVersion = '2.0.402';
var pdfjsBuild = '401f3a9d';
var pdfjsCoreWorker = __w_pdfjs_require__(20);
exports.WorkerMessageHandler = pdfjsCoreWorker.WorkerMessageHandler;

/***/ }),
/* 20 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.WorkerMessageHandler = exports.WorkerTask = undefined;

var _util = __w_pdfjs_require__(0);

var _pdf_manager = __w_pdfjs_require__(24);

var _is_node = __w_pdfjs_require__(44);

var _is_node2 = _interopRequireDefault(_is_node);

var _primitives = __w_pdfjs_require__(1);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

var WorkerTask = function WorkerTaskClosure() {
  function WorkerTask(name) {
    this.name = name;
    this.terminated = false;
    this._capability = (0, _util.createPromiseCapability)();
  }
  WorkerTask.prototype = {
    get finished() {
      return this._capability.promise;
    },
    finish() {
      this._capability.resolve();
    },
    terminate() {
      this.terminated = true;
    },
    ensureNotTerminated() {
      if (this.terminated) {
        throw new Error('Worker task was terminated');
      }
    }
  };
  return WorkerTask;
}();
;
var PDFWorkerStream = function PDFWorkerStreamClosure() {
  function PDFWorkerStream(msgHandler) {
    this._msgHandler = msgHandler;
    this._contentLength = null;
    this._fullRequestReader = null;
    this._rangeRequestReaders = [];
  }
  PDFWorkerStream.prototype = {
    getFullReader() {
      (0, _util.assert)(!this._fullRequestReader);
      this._fullRequestReader = new PDFWorkerStreamReader(this._msgHandler);
      return this._fullRequestReader;
    },
    getRangeReader(begin, end) {
      let reader = new PDFWorkerStreamRangeReader(begin, end, this._msgHandler);
      this._rangeRequestReaders.push(reader);
      return reader;
    },
    cancelAllRequests(reason) {
      if (this._fullRequestReader) {
        this._fullRequestReader.cancel(reason);
      }
      let readers = this._rangeRequestReaders.slice(0);
      readers.forEach(function (reader) {
        reader.cancel(reason);
      });
    }
  };
  function PDFWorkerStreamReader(msgHandler) {
    this._msgHandler = msgHandler;
    this._contentLength = null;
    this._isRangeSupported = false;
    this._isStreamingSupported = false;
    let readableStream = this._msgHandler.sendWithStream('GetReader');
    this._reader = readableStream.getReader();
    this._headersReady = this._msgHandler.sendWithPromise('ReaderHeadersReady').then(data => {
      this._isStreamingSupported = data.isStreamingSupported;
      this._isRangeSupported = data.isRangeSupported;
      this._contentLength = data.contentLength;
    });
  }
  PDFWorkerStreamReader.prototype = {
    get headersReady() {
      return this._headersReady;
    },
    get contentLength() {
      return this._contentLength;
    },
    get isStreamingSupported() {
      return this._isStreamingSupported;
    },
    get isRangeSupported() {
      return this._isRangeSupported;
    },
    read() {
      return this._reader.read().then(function ({ value, done }) {
        if (done) {
          return {
            value: undefined,
            done: true
          };
        }
        return {
          value: value.buffer,
          done: false
        };
      });
    },
    cancel(reason) {
      this._reader.cancel(reason);
    }
  };
  function PDFWorkerStreamRangeReader(begin, end, msgHandler) {
    this._msgHandler = msgHandler;
    this.onProgress = null;
    let readableStream = this._msgHandler.sendWithStream('GetRangeReader', {
      begin,
      end
    });
    this._reader = readableStream.getReader();
  }
  PDFWorkerStreamRangeReader.prototype = {
    get isStreamingSupported() {
      return false;
    },
    read() {
      return this._reader.read().then(function ({ value, done }) {
        if (done) {
          return {
            value: undefined,
            done: true
          };
        }
        return {
          value: value.buffer,
          done: false
        };
      });
    },
    cancel(reason) {
      this._reader.cancel(reason);
    }
  };
  return PDFWorkerStream;
}();
var WorkerMessageHandler = {
  setup(handler, port) {
    var testMessageProcessed = false;
    handler.on('test', function wphSetupTest(data) {
      if (testMessageProcessed) {
        return;
      }
      testMessageProcessed = true;
      if (!(data instanceof Uint8Array)) {
        handler.send('test', 'main', false);
        return;
      }
      var supportTransfers = data[0] === 255;
      handler.postMessageTransfers = supportTransfers;
      var xhr = new XMLHttpRequest();
      var responseExists = 'response' in xhr;
      try {
        xhr.responseType;
      } catch (e) {
        responseExists = false;
      }
      if (!responseExists) {
        handler.send('test', false);
        return;
      }
      handler.send('test', {
        supportTypedArray: true,
        supportTransfers
      });
    });
    handler.on('configure', function wphConfigure(data) {
      (0, _util.setVerbosityLevel)(data.verbosity);
    });
    handler.on('GetDocRequest', function wphSetupDoc(data) {
      return WorkerMessageHandler.createDocumentHandler(data, port);
    });
  },
  createDocumentHandler(docParams, port) {
    var pdfManager;
    var terminated = false;
    var cancelXHRs = null;
    var WorkerTasks = [];
    let apiVersion = docParams.apiVersion;
    let workerVersion = '2.0.402';
    if (apiVersion !== null && apiVersion !== workerVersion) {
      throw new Error(`The API version "${apiVersion}" does not match ` + `the Worker version "${workerVersion}".`);
    }
    var docId = docParams.docId;
    var docBaseUrl = docParams.docBaseUrl;
    var workerHandlerName = docParams.docId + '_worker';
    var handler = new _util.MessageHandler(workerHandlerName, docId, port);
    handler.postMessageTransfers = docParams.postMessageTransfers;
    function ensureNotTerminated() {
      if (terminated) {
        throw new Error('Worker was terminated');
      }
    }
    function startWorkerTask(task) {
      WorkerTasks.push(task);
    }
    function finishWorkerTask(task) {
      task.finish();
      var i = WorkerTasks.indexOf(task);
      WorkerTasks.splice(i, 1);
    }
    function loadDocument(recoveryMode) {
      var loadDocumentCapability = (0, _util.createPromiseCapability)();
      var parseSuccess = function parseSuccess() {
        Promise.all([pdfManager.ensureDoc('numPages'), pdfManager.ensureDoc('fingerprint')]).then(function ([numPages, fingerprint]) {
          loadDocumentCapability.resolve({
            numPages,
            fingerprint
          });
        }, parseFailure);
      };
      var parseFailure = function parseFailure(e) {
        loadDocumentCapability.reject(e);
      };
      pdfManager.ensureDoc('checkHeader', []).then(function () {
        pdfManager.ensureDoc('parseStartXRef', []).then(function () {
          pdfManager.ensureDoc('parse', [recoveryMode]).then(parseSuccess, parseFailure);
        }, parseFailure);
      }, parseFailure);
      return loadDocumentCapability.promise;
    }
    function getPdfManager(data, evaluatorOptions) {
      var pdfManagerCapability = (0, _util.createPromiseCapability)();
      var pdfManager;
      var source = data.source;
      if (source.data) {
        try {
          pdfManager = new _pdf_manager.LocalPdfManager(docId, source.data, source.password, evaluatorOptions, docBaseUrl);
          pdfManagerCapability.resolve(pdfManager);
        } catch (ex) {
          pdfManagerCapability.reject(ex);
        }
        return pdfManagerCapability.promise;
      }
      var pdfStream,
          cachedChunks = [];
      try {
        pdfStream = new PDFWorkerStream(handler);
      } catch (ex) {
        pdfManagerCapability.reject(ex);
        return pdfManagerCapability.promise;
      }
      var fullRequest = pdfStream.getFullReader();
      fullRequest.headersReady.then(function () {
        if (!fullRequest.isRangeSupported) {
          return;
        }
        var disableAutoFetch = source.disableAutoFetch || fullRequest.isStreamingSupported;
        pdfManager = new _pdf_manager.NetworkPdfManager(docId, pdfStream, {
          msgHandler: handler,
          url: source.url,
          password: source.password,
          length: fullRequest.contentLength,
          disableAutoFetch,
          rangeChunkSize: source.rangeChunkSize
        }, evaluatorOptions, docBaseUrl);
        for (let i = 0; i < cachedChunks.length; i++) {
          pdfManager.sendProgressiveData(cachedChunks[i]);
        }
        cachedChunks = [];
        pdfManagerCapability.resolve(pdfManager);
        cancelXHRs = null;
      }).catch(function (reason) {
        pdfManagerCapability.reject(reason);
        cancelXHRs = null;
      });
      var loaded = 0;
      var flushChunks = function () {
        var pdfFile = (0, _util.arraysToBytes)(cachedChunks);
        if (source.length && pdfFile.length !== source.length) {
          (0, _util.warn)('reported HTTP length is different from actual');
        }
        try {
          pdfManager = new _pdf_manager.LocalPdfManager(docId, pdfFile, source.password, evaluatorOptions, docBaseUrl);
          pdfManagerCapability.resolve(pdfManager);
        } catch (ex) {
          pdfManagerCapability.reject(ex);
        }
        cachedChunks = [];
      };
      var readPromise = new Promise(function (resolve, reject) {
        var readChunk = function (chunk) {
          try {
            ensureNotTerminated();
            if (chunk.done) {
              if (!pdfManager) {
                flushChunks();
              }
              cancelXHRs = null;
              return;
            }
            var data = chunk.value;
            loaded += (0, _util.arrayByteLength)(data);
            if (!fullRequest.isStreamingSupported) {
              handler.send('DocProgress', {
                loaded,
                total: Math.max(loaded, fullRequest.contentLength || 0)
              });
            }
            if (pdfManager) {
              pdfManager.sendProgressiveData(data);
            } else {
              cachedChunks.push(data);
            }
            fullRequest.read().then(readChunk, reject);
          } catch (e) {
            reject(e);
          }
        };
        fullRequest.read().then(readChunk, reject);
      });
      readPromise.catch(function (e) {
        pdfManagerCapability.reject(e);
        cancelXHRs = null;
      });
      cancelXHRs = function () {
        pdfStream.cancelAllRequests('abort');
      };
      return pdfManagerCapability.promise;
    }
    function setupDoc(data) {
      function onSuccess(doc) {
        ensureNotTerminated();
        handler.send('GetDoc', { pdfInfo: doc });
      }
      function onFailure(e) {
        ensureNotTerminated();
        if (e instanceof _util.PasswordException) {
          var task = new WorkerTask('PasswordException: response ' + e.code);
          startWorkerTask(task);
          handler.sendWithPromise('PasswordRequest', e).then(function (data) {
            finishWorkerTask(task);
            pdfManager.updatePassword(data.password);
            pdfManagerReady();
          }).catch(function (ex) {
            finishWorkerTask(task);
            handler.send('PasswordException', ex);
          }.bind(null, e));
        } else if (e instanceof _util.InvalidPDFException) {
          handler.send('InvalidPDF', e);
        } else if (e instanceof _util.MissingPDFException) {
          handler.send('MissingPDF', e);
        } else if (e instanceof _util.UnexpectedResponseException) {
          handler.send('UnexpectedResponse', e);
        } else {
          handler.send('UnknownError', new _util.UnknownErrorException(e.message, e.toString()));
        }
      }
      function pdfManagerReady() {
        ensureNotTerminated();
        loadDocument(false).then(onSuccess, function loadFailure(ex) {
          ensureNotTerminated();
          if (!(ex instanceof _util.XRefParseException)) {
            onFailure(ex);
            return;
          }
          pdfManager.requestLoadedStream();
          pdfManager.onLoadedStream().then(function () {
            ensureNotTerminated();
            loadDocument(true).then(onSuccess, onFailure);
          });
        }, onFailure);
      }
      ensureNotTerminated();
      var evaluatorOptions = {
        forceDataSchema: data.disableCreateObjectURL,
        maxImageSize: data.maxImageSize === undefined ? -1 : data.maxImageSize,
        disableFontFace: data.disableFontFace,
        nativeImageDecoderSupport: data.nativeImageDecoderSupport,
        ignoreErrors: data.ignoreErrors,
        isEvalSupported: data.isEvalSupported
      };
      getPdfManager(data, evaluatorOptions).then(function (newPdfManager) {
        if (terminated) {
          newPdfManager.terminate();
          throw new Error('Worker was terminated');
        }
        pdfManager = newPdfManager;
        handler.send('PDFManagerReady', null);
        pdfManager.onLoadedStream().then(function (stream) {
          handler.send('DataLoaded', { length: stream.bytes.byteLength });
        });
      }).then(pdfManagerReady, onFailure);
    }
    handler.on('GetPage', function wphSetupGetPage(data) {
      return pdfManager.getPage(data.pageIndex).then(function (page) {
        var rotatePromise = pdfManager.ensure(page, 'rotate');
        var refPromise = pdfManager.ensure(page, 'ref');
        var userUnitPromise = pdfManager.ensure(page, 'userUnit');
        var viewPromise = pdfManager.ensure(page, 'view');
        return Promise.all([rotatePromise, refPromise, userUnitPromise, viewPromise]).then(function (results) {
          return {
            rotate: results[0],
            ref: results[1],
            userUnit: results[2],
            view: results[3]
          };
        });
      });
    });
    handler.on('GetPageIndex', function wphSetupGetPageIndex(data) {
      var ref = new _primitives.Ref(data.ref.num, data.ref.gen);
      var catalog = pdfManager.pdfDocument.catalog;
      return catalog.getPageIndex(ref);
    });
    handler.on('GetDestinations', function wphSetupGetDestinations(data) {
      return pdfManager.ensureCatalog('destinations');
    });
    handler.on('GetDestination', function wphSetupGetDestination(data) {
      return pdfManager.ensureCatalog('getDestination', [data.id]);
    });
    handler.on('GetPageLabels', function wphSetupGetPageLabels(data) {
      return pdfManager.ensureCatalog('pageLabels');
    });
    handler.on('GetPageMode', function wphSetupGetPageMode(data) {
      return pdfManager.ensureCatalog('pageMode');
    });
    handler.on('GetAttachments', function wphSetupGetAttachments(data) {
      return pdfManager.ensureCatalog('attachments');
    });
    handler.on('GetJavaScript', function wphSetupGetJavaScript(data) {
      return pdfManager.ensureCatalog('javaScript');
    });
    handler.on('GetOutline', function wphSetupGetOutline(data) {
      return pdfManager.ensureCatalog('documentOutline');
    });
    handler.on('GetMetadata', function wphSetupGetMetadata(data) {
      return Promise.all([pdfManager.ensureDoc('documentInfo'), pdfManager.ensureCatalog('metadata')]);
    });
    handler.on('GetData', function wphSetupGetData(data) {
      pdfManager.requestLoadedStream();
      return pdfManager.onLoadedStream().then(function (stream) {
        return stream.bytes;
      });
    });
    handler.on('GetStats', function wphSetupGetStats(data) {
      return pdfManager.pdfDocument.xref.stats;
    });
    handler.on('GetAnnotations', function wphSetupGetAnnotations(data) {
      return pdfManager.getPage(data.pageIndex).then(function (page) {
        return pdfManager.ensure(page, 'getAnnotationsData', [data.intent]);
      });
    });
    handler.on('RenderPageRequest', function wphSetupRenderPage(data) {
      var pageIndex = data.pageIndex;
      pdfManager.getPage(pageIndex).then(function (page) {
        var task = new WorkerTask('RenderPageRequest: page ' + pageIndex);
        startWorkerTask(task);
        var pageNum = pageIndex + 1;
        var start = Date.now();
        page.getOperatorList({
          handler,
          task,
          intent: data.intent,
          renderInteractiveForms: data.renderInteractiveForms
        }).then(function (operatorList) {
          finishWorkerTask(task);
          (0, _util.info)('page=' + pageNum + ' - getOperatorList: time=' + (Date.now() - start) + 'ms, len=' + operatorList.totalLength);
        }, function (e) {
          finishWorkerTask(task);
          if (task.terminated) {
            return;
          }
          handler.send('UnsupportedFeature', { featureId: _util.UNSUPPORTED_FEATURES.unknown });
          var minimumStackMessage = 'worker.js: while trying to getPage() and getOperatorList()';
          var wrappedException;
          if (typeof e === 'string') {
            wrappedException = {
              message: e,
              stack: minimumStackMessage
            };
          } else if (typeof e === 'object') {
            wrappedException = {
              message: e.message || e.toString(),
              stack: e.stack || minimumStackMessage
            };
          } else {
            wrappedException = {
              message: 'Unknown exception type: ' + typeof e,
              stack: minimumStackMessage
            };
          }
          handler.send('PageError', {
            pageNum,
            error: wrappedException,
            intent: data.intent
          });
        });
      });
    }, this);
    handler.on('GetTextContent', function wphExtractText(data, sink) {
      var pageIndex = data.pageIndex;
      sink.onPull = function (desiredSize) {};
      sink.onCancel = function (reason) {};
      pdfManager.getPage(pageIndex).then(function (page) {
        var task = new WorkerTask('GetTextContent: page ' + pageIndex);
        startWorkerTask(task);
        var pageNum = pageIndex + 1;
        var start = Date.now();
        page.extractTextContent({
          handler,
          task,
          sink,
          normalizeWhitespace: data.normalizeWhitespace,
          combineTextItems: data.combineTextItems
        }).then(function () {
          finishWorkerTask(task);
          (0, _util.info)('text indexing: page=' + pageNum + ' - time=' + (Date.now() - start) + 'ms');
          sink.close();
        }, function (reason) {
          finishWorkerTask(task);
          if (task.terminated) {
            return;
          }
          sink.error(reason);
          throw reason;
        });
      });
    });
    handler.on('Cleanup', function wphCleanup(data) {
      return pdfManager.cleanup();
    });
    handler.on('Terminate', function wphTerminate(data) {
      terminated = true;
      if (pdfManager) {
        pdfManager.terminate();
        pdfManager = null;
      }
      if (cancelXHRs) {
        cancelXHRs();
      }
      var waitOn = [];
      WorkerTasks.forEach(function (task) {
        waitOn.push(task.finished);
        task.terminate();
      });
      return Promise.all(waitOn).then(function () {
        handler.destroy();
        handler = null;
      });
    });
    handler.on('Ready', function wphReady(data) {
      setupDoc(docParams);
      docParams = null;
    });
    return workerHandlerName;
  },
  initializeFromPort(port) {
    var handler = new _util.MessageHandler('worker', 'main', port);
    WorkerMessageHandler.setup(handler, port);
    handler.send('ready', null);
  }
};
function isMessagePort(maybePort) {
  return typeof maybePort.postMessage === 'function' && 'onmessage' in maybePort;
}
if (typeof window === 'undefined' && !(0, _is_node2.default)() && typeof self !== 'undefined' && isMessagePort(self)) {
  WorkerMessageHandler.initializeFromPort(self);
}
exports.WorkerTask = WorkerTask;
exports.WorkerMessageHandler = WorkerMessageHandler;

/***/ }),
/* 21 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


;

/***/ }),
/* 22 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


let isReadableStreamSupported = false;
if (typeof ReadableStream !== 'undefined') {
  try {
    new ReadableStream({
      start(controller) {
        controller.close();
      }
    });
    isReadableStreamSupported = true;
  } catch (e) {}
}
if (isReadableStreamSupported) {
  exports.ReadableStream = ReadableStream;
} else {
  exports.ReadableStream = __w_pdfjs_require__(23).ReadableStream;
}

/***/ }),
/* 23 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


(function (e, a) {
  for (var i in a) e[i] = a[i];
})(exports, function (modules) {
  var installedModules = {};
  function __w_pdfjs_require__(moduleId) {
    if (installedModules[moduleId]) return installedModules[moduleId].exports;
    var module = installedModules[moduleId] = {
      i: moduleId,
      l: false,
      exports: {}
    };
    modules[moduleId].call(module.exports, module, module.exports, __w_pdfjs_require__);
    module.l = true;
    return module.exports;
  }
  __w_pdfjs_require__.m = modules;
  __w_pdfjs_require__.c = installedModules;
  __w_pdfjs_require__.i = function (value) {
    return value;
  };
  __w_pdfjs_require__.d = function (exports, name, getter) {
    if (!__w_pdfjs_require__.o(exports, name)) {
      Object.defineProperty(exports, name, {
        configurable: false,
        enumerable: true,
        get: getter
      });
    }
  };
  __w_pdfjs_require__.n = function (module) {
    var getter = module && module.__esModule ? function getDefault() {
      return module['default'];
    } : function getModuleExports() {
      return module;
    };
    __w_pdfjs_require__.d(getter, 'a', getter);
    return getter;
  };
  __w_pdfjs_require__.o = function (object, property) {
    return Object.prototype.hasOwnProperty.call(object, property);
  };
  __w_pdfjs_require__.p = "";
  return __w_pdfjs_require__(__w_pdfjs_require__.s = 7);
}([function (module, exports, __w_pdfjs_require__) {
  "use strict";

  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;
  };
  var _require = __w_pdfjs_require__(1),
      assert = _require.assert;
  function IsPropertyKey(argument) {
    return typeof argument === 'string' || (typeof argument === 'undefined' ? 'undefined' : _typeof(argument)) === 'symbol';
  }
  exports.typeIsObject = function (x) {
    return (typeof x === 'undefined' ? 'undefined' : _typeof(x)) === 'object' && x !== null || typeof x === 'function';
  };
  exports.createDataProperty = function (o, p, v) {
    assert(exports.typeIsObject(o));
    Object.defineProperty(o, p, {
      value: v,
      writable: true,
      enumerable: true,
      configurable: true
    });
  };
  exports.createArrayFromList = function (elements) {
    return elements.slice();
  };
  exports.ArrayBufferCopy = function (dest, destOffset, src, srcOffset, n) {
    new Uint8Array(dest).set(new Uint8Array(src, srcOffset, n), destOffset);
  };
  exports.CreateIterResultObject = function (value, done) {
    assert(typeof done === 'boolean');
    var obj = {};
    Object.defineProperty(obj, 'value', {
      value: value,
      enumerable: true,
      writable: true,
      configurable: true
    });
    Object.defineProperty(obj, 'done', {
      value: done,
      enumerable: true,
      writable: true,
      configurable: true
    });
    return obj;
  };
  exports.IsFiniteNonNegativeNumber = function (v) {
    if (Number.isNaN(v)) {
      return false;
    }
    if (v === Infinity) {
      return false;
    }
    if (v < 0) {
      return false;
    }
    return true;
  };
  function Call(F, V, args) {
    if (typeof F !== 'function') {
      throw new TypeError('Argument is not a function');
    }
    return Function.prototype.apply.call(F, V, args);
  }
  exports.InvokeOrNoop = function (O, P, args) {
    assert(O !== undefined);
    assert(IsPropertyKey(P));
    assert(Array.isArray(args));
    var method = O[P];
    if (method === undefined) {
      return undefined;
    }
    return Call(method, O, args);
  };
  exports.PromiseInvokeOrNoop = function (O, P, args) {
    assert(O !== undefined);
    assert(IsPropertyKey(P));
    assert(Array.isArray(args));
    try {
      return Promise.resolve(exports.InvokeOrNoop(O, P, args));
    } catch (returnValueE) {
      return Promise.reject(returnValueE);
    }
  };
  exports.PromiseInvokeOrPerformFallback = function (O, P, args, F, argsF) {
    assert(O !== undefined);
    assert(IsPropertyKey(P));
    assert(Array.isArray(args));
    assert(Array.isArray(argsF));
    var method = void 0;
    try {
      method = O[P];
    } catch (methodE) {
      return Promise.reject(methodE);
    }
    if (method === undefined) {
      return F.apply(null, argsF);
    }
    try {
      return Promise.resolve(Call(method, O, args));
    } catch (e) {
      return Promise.reject(e);
    }
  };
  exports.TransferArrayBuffer = function (O) {
    return O.slice();
  };
  exports.ValidateAndNormalizeHighWaterMark = function (highWaterMark) {
    highWaterMark = Number(highWaterMark);
    if (Number.isNaN(highWaterMark) || highWaterMark < 0) {
      throw new RangeError('highWaterMark property of a queuing strategy must be non-negative and non-NaN');
    }
    return highWaterMark;
  };
  exports.ValidateAndNormalizeQueuingStrategy = function (size, highWaterMark) {
    if (size !== undefined && typeof size !== 'function') {
      throw new TypeError('size property of a queuing strategy must be a function');
    }
    highWaterMark = exports.ValidateAndNormalizeHighWaterMark(highWaterMark);
    return {
      size: size,
      highWaterMark: highWaterMark
    };
  };
}, function (module, exports, __w_pdfjs_require__) {
  "use strict";

  function rethrowAssertionErrorRejection(e) {
    if (e && e.constructor === AssertionError) {
      setTimeout(function () {
        throw e;
      }, 0);
    }
  }
  function AssertionError(message) {
    this.name = 'AssertionError';
    this.message = message || '';
    this.stack = new Error().stack;
  }
  AssertionError.prototype = Object.create(Error.prototype);
  AssertionError.prototype.constructor = AssertionError;
  function assert(value, message) {
    if (!value) {
      throw new AssertionError(message);
    }
  }
  module.exports = {
    rethrowAssertionErrorRejection: rethrowAssertionErrorRejection,
    AssertionError: AssertionError,
    assert: assert
  };
}, function (module, exports, __w_pdfjs_require__) {
  "use strict";

  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");
    }
  }
  var _require = __w_pdfjs_require__(0),
      InvokeOrNoop = _require.InvokeOrNoop,
      PromiseInvokeOrNoop = _require.PromiseInvokeOrNoop,
      ValidateAndNormalizeQueuingStrategy = _require.ValidateAndNormalizeQueuingStrategy,
      typeIsObject = _require.typeIsObject;
  var _require2 = __w_pdfjs_require__(1),
      assert = _require2.assert,
      rethrowAssertionErrorRejection = _require2.rethrowAssertionErrorRejection;
  var _require3 = __w_pdfjs_require__(3),
      DequeueValue = _require3.DequeueValue,
      EnqueueValueWithSize = _require3.EnqueueValueWithSize,
      PeekQueueValue = _require3.PeekQueueValue,
      ResetQueue = _require3.ResetQueue;
  var WritableStream = function () {
    function WritableStream() {
      var underlyingSink = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
          size = _ref.size,
          _ref$highWaterMark = _ref.highWaterMark,
          highWaterMark = _ref$highWaterMark === undefined ? 1 : _ref$highWaterMark;
      _classCallCheck(this, WritableStream);
      this._state = 'writable';
      this._storedError = undefined;
      this._writer = undefined;
      this._writableStreamController = undefined;
      this._writeRequests = [];
      this._inFlightWriteRequest = undefined;
      this._closeRequest = undefined;
      this._inFlightCloseRequest = undefined;
      this._pendingAbortRequest = undefined;
      this._backpressure = false;
      var type = underlyingSink.type;
      if (type !== undefined) {
        throw new RangeError('Invalid type is specified');
      }
      this._writableStreamController = new WritableStreamDefaultController(this, underlyingSink, size, highWaterMark);
      this._writableStreamController.__startSteps();
    }
    _createClass(WritableStream, [{
      key: 'abort',
      value: function abort(reason) {
        if (IsWritableStream(this) === false) {
          return Promise.reject(streamBrandCheckException('abort'));
        }
        if (IsWritableStreamLocked(this) === true) {
          return Promise.reject(new TypeError('Cannot abort a stream that already has a writer'));
        }
        return WritableStreamAbort(this, reason);
      }
    }, {
      key: 'getWriter',
      value: function getWriter() {
        if (IsWritableStream(this) === false) {
          throw streamBrandCheckException('getWriter');
        }
        return AcquireWritableStreamDefaultWriter(this);
      }
    }, {
      key: 'locked',
      get: function get() {
        if (IsWritableStream(this) === false) {
          throw streamBrandCheckException('locked');
        }
        return IsWritableStreamLocked(this);
      }
    }]);
    return WritableStream;
  }();
  module.exports = {
    AcquireWritableStreamDefaultWriter: AcquireWritableStreamDefaultWriter,
    IsWritableStream: IsWritableStream,
    IsWritableStreamLocked: IsWritableStreamLocked,
    WritableStream: WritableStream,
    WritableStreamAbort: WritableStreamAbort,
    WritableStreamDefaultControllerError: WritableStreamDefaultControllerError,
    WritableStreamDefaultWriterCloseWithErrorPropagation: WritableStreamDefaultWriterCloseWithErrorPropagation,
    WritableStreamDefaultWriterRelease: WritableStreamDefaultWriterRelease,
    WritableStreamDefaultWriterWrite: WritableStreamDefaultWriterWrite,
    WritableStreamCloseQueuedOrInFlight: WritableStreamCloseQueuedOrInFlight
  };
  function AcquireWritableStreamDefaultWriter(stream) {
    return new WritableStreamDefaultWriter(stream);
  }
  function IsWritableStream(x) {
    if (!typeIsObject(x)) {
      return false;
    }
    if (!Object.prototype.hasOwnProperty.call(x, '_writableStreamController')) {
      return false;
    }
    return true;
  }
  function IsWritableStreamLocked(stream) {
    assert(IsWritableStream(stream) === true, 'IsWritableStreamLocked should only be used on known writable streams');
    if (stream._writer === undefined) {
      return false;
    }
    return true;
  }
  function WritableStreamAbort(stream, reason) {
    var state = stream._state;
    if (state === 'closed') {
      return Promise.resolve(undefined);
    }
    if (state === 'errored') {
      return Promise.reject(stream._storedError);
    }
    var error = new TypeError('Requested to abort');
    if (stream._pendingAbortRequest !== undefined) {
      return Promise.reject(error);
    }
    assert(state === 'writable' || state === 'erroring', 'state must be writable or erroring');
    var wasAlreadyErroring = false;
    if (state === 'erroring') {
      wasAlreadyErroring = true;
      reason = undefined;
    }
    var promise = new Promise(function (resolve, reject) {
      stream._pendingAbortRequest = {
        _resolve: resolve,
        _reject: reject,
        _reason: reason,
        _wasAlreadyErroring: wasAlreadyErroring
      };
    });
    if (wasAlreadyErroring === false) {
      WritableStreamStartErroring(stream, error);
    }
    return promise;
  }
  function WritableStreamAddWriteRequest(stream) {
    assert(IsWritableStreamLocked(stream) === true);
    assert(stream._state === 'writable');
    var promise = new Promise(function (resolve, reject) {
      var writeRequest = {
        _resolve: resolve,
        _reject: reject
      };
      stream._writeRequests.push(writeRequest);
    });
    return promise;
  }
  function WritableStreamDealWithRejection(stream, error) {
    var state = stream._state;
    if (state === 'writable') {
      WritableStreamStartErroring(stream, error);
      return;
    }
    assert(state === 'erroring');
    WritableStreamFinishErroring(stream);
  }
  function WritableStreamStartErroring(stream, reason) {
    assert(stream._storedError === undefined, 'stream._storedError === undefined');
    assert(stream._state === 'writable', 'state must be writable');
    var controller = stream._writableStreamController;
    assert(controller !== undefined, 'controller must not be undefined');
    stream._state = 'erroring';
    stream._storedError = reason;
    var writer = stream._writer;
    if (writer !== undefined) {
      WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer, reason);
    }
    if (WritableStreamHasOperationMarkedInFlight(stream) === false && controller._started === true) {
      WritableStreamFinishErroring(stream);
    }
  }
  function WritableStreamFinishErroring(stream) {
    assert(stream._state === 'erroring', 'stream._state === erroring');
    assert(WritableStreamHasOperationMarkedInFlight(stream) === false, 'WritableStreamHasOperationMarkedInFlight(stream) === false');
    stream._state = 'errored';
    stream._writableStreamController.__errorSteps();
    var storedError = stream._storedError;
    for (var i = 0; i < stream._writeRequests.length; i++) {
      var writeRequest = stream._writeRequests[i];
      writeRequest._reject(storedError);
    }
    stream._writeRequests = [];
    if (stream._pendingAbortRequest === undefined) {
      WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream);
      return;
    }
    var abortRequest = stream._pendingAbortRequest;
    stream._pendingAbortRequest = undefined;
    if (abortRequest._wasAlreadyErroring === true) {
      abortRequest._reject(storedError);
      WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream);
      return;
    }
    var promise = stream._writableStreamController.__abortSteps(abortRequest._reason);
    promise.then(function () {
      abortRequest._resolve();
      WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream);
    }, function (reason) {
      abortRequest._reject(reason);
      WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream);
    });
  }
  function WritableStreamFinishInFlightWrite(stream) {
    assert(stream._inFlightWriteRequest !== undefined);
    stream._inFlightWriteRequest._resolve(undefined);
    stream._inFlightWriteRequest = undefined;
  }
  function WritableStreamFinishInFlightWriteWithError(stream, error) {
    assert(stream._inFlightWriteRequest !== undefined);
    stream._inFlightWriteRequest._reject(error);
    stream._inFlightWriteRequest = undefined;
    assert(stream._state === 'writable' || stream._state === 'erroring');
    WritableStreamDealWithRejection(stream, error);
  }
  function WritableStreamFinishInFlightClose(stream) {
    assert(stream._inFlightCloseRequest !== undefined);
    stream._inFlightCloseRequest._resolve(undefined);
    stream._inFlightCloseRequest = undefined;
    var state = stream._state;
    assert(state === 'writable' || state === 'erroring');
    if (state === 'erroring') {
      stream._storedError = undefined;
      if (stream._pendingAbortRequest !== undefined) {
        stream._pendingAbortRequest._resolve();
        stream._pendingAbortRequest = undefined;
      }
    }
    stream._state = 'closed';
    var writer = stream._writer;
    if (writer !== undefined) {
      defaultWriterClosedPromiseResolve(writer);
    }
    assert(stream._pendingAbortRequest === undefined, 'stream._pendingAbortRequest === undefined');
    assert(stream._storedError === undefined, 'stream._storedError === undefined');
  }
  function WritableStreamFinishInFlightCloseWithError(stream, error) {
    assert(stream._inFlightCloseRequest !== undefined);
    stream._inFlightCloseRequest._reject(error);
    stream._inFlightCloseRequest = undefined;
    assert(stream._state === 'writable' || stream._state === 'erroring');
    if (stream._pendingAbortRequest !== undefined) {
      stream._pendingAbortRequest._reject(error);
      stream._pendingAbortRequest = undefined;
    }
    WritableStreamDealWithRejection(stream, error);
  }
  function WritableStreamCloseQueuedOrInFlight(stream) {
    if (stream._closeRequest === undefined && stream._inFlightCloseRequest === undefined) {
      return false;
    }
    return true;
  }
  function WritableStreamHasOperationMarkedInFlight(stream) {
    if (stream._inFlightWriteRequest === undefined && stream._inFlightCloseRequest === undefined) {
      return false;
    }
    return true;
  }
  function WritableStreamMarkCloseRequestInFlight(stream) {
    assert(stream._inFlightCloseRequest === undefined);
    assert(stream._closeRequest !== undefined);
    stream._inFlightCloseRequest = stream._closeRequest;
    stream._closeRequest = undefined;
  }
  function WritableStreamMarkFirstWriteRequestInFlight(stream) {
    assert(stream._inFlightWriteRequest === undefined, 'there must be no pending write request');
    assert(stream._writeRequests.length !== 0, 'writeRequests must not be empty');
    stream._inFlightWriteRequest = stream._writeRequests.shift();
  }
  function WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream) {
    assert(stream._state === 'errored', '_stream_.[[state]] is `"errored"`');
    if (stream._closeRequest !== undefined) {
      assert(stream._inFlightCloseRequest === undefined);
      stream._closeRequest._reject(stream._storedError);
      stream._closeRequest = undefined;
    }
    var writer = stream._writer;
    if (writer !== undefined) {
      defaultWriterClosedPromiseReject(writer, stream._storedError);
      writer._closedPromise.catch(function () {});
    }
  }
  function WritableStreamUpdateBackpressure(stream, backpressure) {
    assert(stream._state === 'writable');
    assert(WritableStreamCloseQueuedOrInFlight(stream) === false);
    var writer = stream._writer;
    if (writer !== undefined && backpressure !== stream._backpressure) {
      if (backpressure === true) {
        defaultWriterReadyPromiseReset(writer);
      } else {
        assert(backpressure === false);
        defaultWriterReadyPromiseResolve(writer);
      }
    }
    stream._backpressure = backpressure;
  }
  var WritableStreamDefaultWriter = function () {
    function WritableStreamDefaultWriter(stream) {
      _classCallCheck(this, WritableStreamDefaultWriter);
      if (IsWritableStream(stream) === false) {
        throw new TypeError('WritableStreamDefaultWriter can only be constructed with a WritableStream instance');
      }
      if (IsWritableStreamLocked(stream) === true) {
        throw new TypeError('This stream has already been locked for exclusive writing by another writer');
      }
      this._ownerWritableStream = stream;
      stream._writer = this;
      var state = stream._state;
      if (state === 'writable') {
        if (WritableStreamCloseQueuedOrInFlight(stream) === false && stream._backpressure === true) {
          defaultWriterReadyPromiseInitialize(this);
        } else {
          defaultWriterReadyPromiseInitializeAsResolved(this);
        }
        defaultWriterClosedPromiseInitialize(this);
      } else if (state === 'erroring') {
        defaultWriterReadyPromiseInitializeAsRejected(this, stream._storedError);
        this._readyPromise.catch(function () {});
        defaultWriterClosedPromiseInitialize(this);
      } else if (state === 'closed') {
        defaultWriterReadyPromiseInitializeAsResolved(this);
        defaultWriterClosedPromiseInitializeAsResolved(this);
      } else {
        assert(state === 'errored', 'state must be errored');
        var storedError = stream._storedError;
        defaultWriterReadyPromiseInitializeAsRejected(this, storedError);
        this._readyPromise.catch(function () {});
        defaultWriterClosedPromiseInitializeAsRejected(this, storedError);
        this._closedPromise.catch(function () {});
      }
    }
    _createClass(WritableStreamDefaultWriter, [{
      key: 'abort',
      value: function abort(reason) {
        if (IsWritableStreamDefaultWriter(this) === false) {
          return Promise.reject(defaultWriterBrandCheckException('abort'));
        }
        if (this._ownerWritableStream === undefined) {
          return Promise.reject(defaultWriterLockException('abort'));
        }
        return WritableStreamDefaultWriterAbort(this, reason);
      }
    }, {
      key: 'close',
      value: function close() {
        if (IsWritableStreamDefaultWriter(this) === false) {
          return Promise.reject(defaultWriterBrandCheckException('close'));
        }
        var stream = this._ownerWritableStream;
        if (stream === undefined) {
          return Promise.reject(defaultWriterLockException('close'));
        }
        if (WritableStreamCloseQueuedOrInFlight(stream) === true) {
          return Promise.reject(new TypeError('cannot close an already-closing stream'));
        }
        return WritableStreamDefaultWriterClose(this);
      }
    }, {
      key: 'releaseLock',
      value: function releaseLock() {
        if (IsWritableStreamDefaultWriter(this) === false) {
          throw defaultWriterBrandCheckException('releaseLock');
        }
        var stream = this._ownerWritableStream;
        if (stream === undefined) {
          return;
        }
        assert(stream._writer !== undefined);
        WritableStreamDefaultWriterRelease(this);
      }
    }, {
      key: 'write',
      value: function write(chunk) {
        if (IsWritableStreamDefaultWriter(this) === false) {
          return Promise.reject(defaultWriterBrandCheckException('write'));
        }
        if (this._ownerWritableStream === undefined) {
          return Promise.reject(defaultWriterLockException('write to'));
        }
        return WritableStreamDefaultWriterWrite(this, chunk);
      }
    }, {
      key: 'closed',
      get: function get() {
        if (IsWritableStreamDefaultWriter(this) === false) {
          return Promise.reject(defaultWriterBrandCheckException('closed'));
        }
        return this._closedPromise;
      }
    }, {
      key: 'desiredSize',
      get: function get() {
        if (IsWritableStreamDefaultWriter(this) === false) {
          throw defaultWriterBrandCheckException('desiredSize');
        }
        if (this._ownerWritableStream === undefined) {
          throw defaultWriterLockException('desiredSize');
        }
        return WritableStreamDefaultWriterGetDesiredSize(this);
      }
    }, {
      key: 'ready',
      get: function get() {
        if (IsWritableStreamDefaultWriter(this) === false) {
          return Promise.reject(defaultWriterBrandCheckException('ready'));
        }
        return this._readyPromise;
      }
    }]);
    return WritableStreamDefaultWriter;
  }();
  function IsWritableStreamDefaultWriter(x) {
    if (!typeIsObject(x)) {
      return false;
    }
    if (!Object.prototype.hasOwnProperty.call(x, '_ownerWritableStream')) {
      return false;
    }
    return true;
  }
  function WritableStreamDefaultWriterAbort(writer, reason) {
    var stream = writer._ownerWritableStream;
    assert(stream !== undefined);
    return WritableStreamAbort(stream, reason);
  }
  function WritableStreamDefaultWriterClose(writer) {
    var stream = writer._ownerWritableStream;
    assert(stream !== undefined);
    var state = stream._state;
    if (state === 'closed' || state === 'errored') {
      return Promise.reject(new TypeError('The stream (in ' + state + ' state) is not in the writable state and cannot be closed'));
    }
    assert(state === 'writable' || state === 'erroring');
    assert(WritableStreamCloseQueuedOrInFlight(stream) === false);
    var promise = new Promise(function (resolve, reject) {
      var closeRequest = {
        _resolve: resolve,
        _reject: reject
      };
      stream._closeRequest = closeRequest;
    });
    if (stream._backpressure === true && state === 'writable') {
      defaultWriterReadyPromiseResolve(writer);
    }
    WritableStreamDefaultControllerClose(stream._writableStreamController);
    return promise;
  }
  function WritableStreamDefaultWriterCloseWithErrorPropagation(writer) {
    var stream = writer._ownerWritableStream;
    assert(stream !== undefined);
    var state = stream._state;
    if (WritableStreamCloseQueuedOrInFlight(stream) === true || state === 'closed') {
      return Promise.resolve();
    }
    if (state === 'errored') {
      return Promise.reject(stream._storedError);
    }
    assert(state === 'writable' || state === 'erroring');
    return WritableStreamDefaultWriterClose(writer);
  }
  function WritableStreamDefaultWriterEnsureClosedPromiseRejected(writer, error) {
    if (writer._closedPromiseState === 'pending') {
      defaultWriterClosedPromiseReject(writer, error);
    } else {
      defaultWriterClosedPromiseResetToRejected(writer, error);
    }
    writer._closedPromise.catch(function () {});
  }
  function WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer, error) {
    if (writer._readyPromiseState === 'pending') {
      defaultWriterReadyPromiseReject(writer, error);
    } else {
      defaultWriterReadyPromiseResetToRejected(writer, error);
    }
    writer._readyPromise.catch(function () {});
  }
  function WritableStreamDefaultWriterGetDesiredSize(writer) {
    var stream = writer._ownerWritableStream;
    var state = stream._state;
    if (state === 'errored' || state === 'erroring') {
      return null;
    }
    if (state === 'closed') {
      return 0;
    }
    return WritableStreamDefaultControllerGetDesiredSize(stream._writableStreamController);
  }
  function WritableStreamDefaultWriterRelease(writer) {
    var stream = writer._ownerWritableStream;
    assert(stream !== undefined);
    assert(stream._writer === writer);
    var releasedError = new TypeError('Writer was released and can no longer be used to monitor the stream\'s closedness');
    WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer, releasedError);
    WritableStreamDefaultWriterEnsureClosedPromiseRejected(writer, releasedError);
    stream._writer = undefined;
    writer._ownerWritableStream = undefined;
  }
  function WritableStreamDefaultWriterWrite(writer, chunk) {
    var stream = writer._ownerWritableStream;
    assert(stream !== undefined);
    var controller = stream._writableStreamController;
    var chunkSize = WritableStreamDefaultControllerGetChunkSize(controller, chunk);
    if (stream !== writer._ownerWritableStream) {
      return Promise.reject(defaultWriterLockException('write to'));
    }
    var state = stream._state;
    if (state === 'errored') {
      return Promise.reject(stream._storedError);
    }
    if (WritableStreamCloseQueuedOrInFlight(stream) === true || state === 'closed') {
      return Promise.reject(new TypeError('The stream is closing or closed and cannot be written to'));
    }
    if (state === 'erroring') {
      return Promise.reject(stream._storedError);
    }
    assert(state === 'writable');
    var promise = WritableStreamAddWriteRequest(stream);
    WritableStreamDefaultControllerWrite(controller, chunk, chunkSize);
    return promise;
  }
  var WritableStreamDefaultController = function () {
    function WritableStreamDefaultController(stream, underlyingSink, size, highWaterMark) {
      _classCallCheck(this, WritableStreamDefaultController);
      if (IsWritableStream(stream) === false) {
        throw new TypeError('WritableStreamDefaultController can only be constructed with a WritableStream instance');
      }
      if (stream._writableStreamController !== undefined) {
        throw new TypeError('WritableStreamDefaultController instances can only be created by the WritableStream constructor');
      }
      this._controlledWritableStream = stream;
      this._underlyingSink = underlyingSink;
      this._queue = undefined;
      this._queueTotalSize = undefined;
      ResetQueue(this);
      this._started = false;
      var normalizedStrategy = ValidateAndNormalizeQueuingStrategy(size, highWaterMark);
      this._strategySize = normalizedStrategy.size;
      this._strategyHWM = normalizedStrategy.highWaterMark;
      var backpressure = WritableStreamDefaultControllerGetBackpressure(this);
      WritableStreamUpdateBackpressure(stream, backpressure);
    }
    _createClass(WritableStreamDefaultController, [{
      key: 'error',
      value: function error(e) {
        if (IsWritableStreamDefaultController(this) === false) {
          throw new TypeError('WritableStreamDefaultController.prototype.error can only be used on a WritableStreamDefaultController');
        }
        var state = this._controlledWritableStream._state;
        if (state !== 'writable') {
          return;
        }
        WritableStreamDefaultControllerError(this, e);
      }
    }, {
      key: '__abortSteps',
      value: function __abortSteps(reason) {
        return PromiseInvokeOrNoop(this._underlyingSink, 'abort', [reason]);
      }
    }, {
      key: '__errorSteps',
      value: function __errorSteps() {
        ResetQueue(this);
      }
    }, {
      key: '__startSteps',
      value: function __startSteps() {
        var _this = this;
        var startResult = InvokeOrNoop(this._underlyingSink, 'start', [this]);
        var stream = this._controlledWritableStream;
        Promise.resolve(startResult).then(function () {
          assert(stream._state === 'writable' || stream._state === 'erroring');
          _this._started = true;
          WritableStreamDefaultControllerAdvanceQueueIfNeeded(_this);
        }, function (r) {
          assert(stream._state === 'writable' || stream._state === 'erroring');
          _this._started = true;
          WritableStreamDealWithRejection(stream, r);
        }).catch(rethrowAssertionErrorRejection);
      }
    }]);
    return WritableStreamDefaultController;
  }();
  function WritableStreamDefaultControllerClose(controller) {
    EnqueueValueWithSize(controller, 'close', 0);
    WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller);
  }
  function WritableStreamDefaultControllerGetChunkSize(controller, chunk) {
    var strategySize = controller._strategySize;
    if (strategySize === undefined) {
      return 1;
    }
    try {
      return strategySize(chunk);
    } catch (chunkSizeE) {
      WritableStreamDefaultControllerErrorIfNeeded(controller, chunkSizeE);
      return 1;
    }
  }
  function WritableStreamDefaultControllerGetDesiredSize(controller) {
    return controller._strategyHWM - controller._queueTotalSize;
  }
  function WritableStreamDefaultControllerWrite(controller, chunk, chunkSize) {
    var writeRecord = { chunk: chunk };
    try {
      EnqueueValueWithSize(controller, writeRecord, chunkSize);
    } catch (enqueueE) {
      WritableStreamDefaultControllerErrorIfNeeded(controller, enqueueE);
      return;
    }
    var stream = controller._controlledWritableStream;
    if (WritableStreamCloseQueuedOrInFlight(stream) === false && stream._state === 'writable') {
      var backpressure = WritableStreamDefaultControllerGetBackpressure(controller);
      WritableStreamUpdateBackpressure(stream, backpressure);
    }
    WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller);
  }
  function IsWritableStreamDefaultController(x) {
    if (!typeIsObject(x)) {
      return false;
    }
    if (!Object.prototype.hasOwnProperty.call(x, '_underlyingSink')) {
      return false;
    }
    return true;
  }
  function WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller) {
    var stream = controller._controlledWritableStream;
    if (controller._started === false) {
      return;
    }
    if (stream._inFlightWriteRequest !== undefined) {
      return;
    }
    var state = stream._state;
    if (state === 'closed' || state === 'errored') {
      return;
    }
    if (state === 'erroring') {
      WritableStreamFinishErroring(stream);
      return;
    }
    if (controller._queue.length === 0) {
      return;
    }
    var writeRecord = PeekQueueValue(controller);
    if (writeRecord === 'close') {
      WritableStreamDefaultControllerProcessClose(controller);
    } else {
      WritableStreamDefaultControllerProcessWrite(controller, writeRecord.chunk);
    }
  }
  function WritableStreamDefaultControllerErrorIfNeeded(controller, error) {
    if (controller._controlledWritableStream._state === 'writable') {
      WritableStreamDefaultControllerError(controller, error);
    }
  }
  function WritableStreamDefaultControllerProcessClose(controller) {
    var stream = controller._controlledWritableStream;
    WritableStreamMarkCloseRequestInFlight(stream);
    DequeueValue(controller);
    assert(controller._queue.length === 0, 'queue must be empty once the final write record is dequeued');
    var sinkClosePromise = PromiseInvokeOrNoop(controller._underlyingSink, 'close', []);
    sinkClosePromise.then(function () {
      WritableStreamFinishInFlightClose(stream);
    }, function (reason) {
      WritableStreamFinishInFlightCloseWithError(stream, reason);
    }).catch(rethrowAssertionErrorRejection);
  }
  function WritableStreamDefaultControllerProcessWrite(controller, chunk) {
    var stream = controller._controlledWritableStream;
    WritableStreamMarkFirstWriteRequestInFlight(stream);
    var sinkWritePromise = PromiseInvokeOrNoop(controller._underlyingSink, 'write', [chunk, controller]);
    sinkWritePromise.then(function () {
      WritableStreamFinishInFlightWrite(stream);
      var state = stream._state;
      assert(state === 'writable' || state === 'erroring');
      DequeueValue(controller);
      if (WritableStreamCloseQueuedOrInFlight(stream) === false && state === 'writable') {
        var backpressure = WritableStreamDefaultControllerGetBackpressure(controller);
        WritableStreamUpdateBackpressure(stream, backpressure);
      }
      WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller);
    }, function (reason) {
      WritableStreamFinishInFlightWriteWithError(stream, reason);
    }).catch(rethrowAssertionErrorRejection);
  }
  function WritableStreamDefaultControllerGetBackpressure(controller) {
    var desiredSize = WritableStreamDefaultControllerGetDesiredSize(controller);
    return desiredSize <= 0;
  }
  function WritableStreamDefaultControllerError(controller, error) {
    var stream = controller._controlledWritableStream;
    assert(stream._state === 'writable');
    WritableStreamStartErroring(stream, error);
  }
  function streamBrandCheckException(name) {
    return new TypeError('WritableStream.prototype.' + name + ' can only be used on a WritableStream');
  }
  function defaultWriterBrandCheckException(name) {
    return new TypeError('WritableStreamDefaultWriter.prototype.' + name + ' can only be used on a WritableStreamDefaultWriter');
  }
  function defaultWriterLockException(name) {
    return new TypeError('Cannot ' + name + ' a stream using a released writer');
  }
  function defaultWriterClosedPromiseInitialize(writer) {
    writer._closedPromise = new Promise(function (resolve, reject) {
      writer._closedPromise_resolve = resolve;
      writer._closedPromise_reject = reject;
      writer._closedPromiseState = 'pending';
    });
  }
  function defaultWriterClosedPromiseInitializeAsRejected(writer, reason) {
    writer._closedPromise = Promise.reject(reason);
    writer._closedPromise_resolve = undefined;
    writer._closedPromise_reject = undefined;
    writer._closedPromiseState = 'rejected';
  }
  function defaultWriterClosedPromiseInitializeAsResolved(writer) {
    writer._closedPromise = Promise.resolve(undefined);
    writer._closedPromise_resolve = undefined;
    writer._closedPromise_reject = undefined;
    writer._closedPromiseState = 'resolved';
  }
  function defaultWriterClosedPromiseReject(writer, reason) {
    assert(writer._closedPromise_resolve !== undefined, 'writer._closedPromise_resolve !== undefined');
    assert(writer._closedPromise_reject !== undefined, 'writer._closedPromise_reject !== undefined');
    assert(writer._closedPromiseState === 'pending', 'writer._closedPromiseState is pending');
    writer._closedPromise_reject(reason);
    writer._closedPromise_resolve = undefined;
    writer._closedPromise_reject = undefined;
    writer._closedPromiseState = 'rejected';
  }
  function defaultWriterClosedPromiseResetToRejected(writer, reason) {
    assert(writer._closedPromise_resolve === undefined, 'writer._closedPromise_resolve === undefined');
    assert(writer._closedPromise_reject === undefined, 'writer._closedPromise_reject === undefined');
    assert(writer._closedPromiseState !== 'pending', 'writer._closedPromiseState is not pending');
    writer._closedPromise = Promise.reject(reason);
    writer._closedPromiseState = 'rejected';
  }
  function defaultWriterClosedPromiseResolve(writer) {
    assert(writer._closedPromise_resolve !== undefined, 'writer._closedPromise_resolve !== undefined');
    assert(writer._closedPromise_reject !== undefined, 'writer._closedPromise_reject !== undefined');
    assert(writer._closedPromiseState === 'pending', 'writer._closedPromiseState is pending');
    writer._closedPromise_resolve(undefined);
    writer._closedPromise_resolve = undefined;
    writer._closedPromise_reject = undefined;
    writer._closedPromiseState = 'resolved';
  }
  function defaultWriterReadyPromiseInitialize(writer) {
    writer._readyPromise = new Promise(function (resolve, reject) {
      writer._readyPromise_resolve = resolve;
      writer._readyPromise_reject = reject;
    });
    writer._readyPromiseState = 'pending';
  }
  function defaultWriterReadyPromiseInitializeAsRejected(writer, reason) {
    writer._readyPromise = Promise.reject(reason);
    writer._readyPromise_resolve = undefined;
    writer._readyPromise_reject = undefined;
    writer._readyPromiseState = 'rejected';
  }
  function defaultWriterReadyPromiseInitializeAsResolved(writer) {
    writer._readyPromise = Promise.resolve(undefined);
    writer._readyPromise_resolve = undefined;
    writer._readyPromise_reject = undefined;
    writer._readyPromiseState = 'fulfilled';
  }
  function defaultWriterReadyPromiseReject(writer, reason) {
    assert(writer._readyPromise_resolve !== undefined, 'writer._readyPromise_resolve !== undefined');
    assert(writer._readyPromise_reject !== undefined, 'writer._readyPromise_reject !== undefined');
    writer._readyPromise_reject(reason);
    writer._readyPromise_resolve = undefined;
    writer._readyPromise_reject = undefined;
    writer._readyPromiseState = 'rejected';
  }
  function defaultWriterReadyPromiseReset(writer) {
    assert(writer._readyPromise_resolve === undefined, 'writer._readyPromise_resolve === undefined');
    assert(writer._readyPromise_reject === undefined, 'writer._readyPromise_reject === undefined');
    writer._readyPromise = new Promise(function (resolve, reject) {
      writer._readyPromise_resolve = resolve;
      writer._readyPromise_reject = reject;
    });
    writer._readyPromiseState = 'pending';
  }
  function defaultWriterReadyPromiseResetToRejected(writer, reason) {
    assert(writer._readyPromise_resolve === undefined, 'writer._readyPromise_resolve === undefined');
    assert(writer._readyPromise_reject === undefined, 'writer._readyPromise_reject === undefined');
    writer._readyPromise = Promise.reject(reason);
    writer._readyPromiseState = 'rejected';
  }
  function defaultWriterReadyPromiseResolve(writer) {
    assert(writer._readyPromise_resolve !== undefined, 'writer._readyPromise_resolve !== undefined');
    assert(writer._readyPromise_reject !== undefined, 'writer._readyPromise_reject !== undefined');
    writer._readyPromise_resolve(undefined);
    writer._readyPromise_resolve = undefined;
    writer._readyPromise_reject = undefined;
    writer._readyPromiseState = 'fulfilled';
  }
}, function (module, exports, __w_pdfjs_require__) {
  "use strict";

  var _require = __w_pdfjs_require__(0),
      IsFiniteNonNegativeNumber = _require.IsFiniteNonNegativeNumber;
  var _require2 = __w_pdfjs_require__(1),
      assert = _require2.assert;
  exports.DequeueValue = function (container) {
    assert('_queue' in container && '_queueTotalSize' in container, 'Spec-level failure: DequeueValue should only be used on containers with [[queue]] and [[queueTotalSize]].');
    assert(container._queue.length > 0, 'Spec-level failure: should never dequeue from an empty queue.');
    var pair = container._queue.shift();
    container._queueTotalSize -= pair.size;
    if (container._queueTotalSize < 0) {
      container._queueTotalSize = 0;
    }
    return pair.value;
  };
  exports.EnqueueValueWithSize = function (container, value, size) {
    assert('_queue' in container && '_queueTotalSize' in container, 'Spec-level failure: EnqueueValueWithSize should only be used on containers with [[queue]] and ' + '[[queueTotalSize]].');
    size = Number(size);
    if (!IsFiniteNonNegativeNumber(size)) {
      throw new RangeError('Size must be a finite, non-NaN, non-negative number.');
    }
    container._queue.push({
      value: value,
      size: size
    });
    container._queueTotalSize += size;
  };
  exports.PeekQueueValue = function (container) {
    assert('_queue' in container && '_queueTotalSize' in container, 'Spec-level failure: PeekQueueValue should only be used on containers with [[queue]] and [[queueTotalSize]].');
    assert(container._queue.length > 0, 'Spec-level failure: should never peek at an empty queue.');
    var pair = container._queue[0];
    return pair.value;
  };
  exports.ResetQueue = function (container) {
    assert('_queue' in container && '_queueTotalSize' in container, 'Spec-level failure: ResetQueue should only be used on containers with [[queue]] and [[queueTotalSize]].');
    container._queue = [];
    container._queueTotalSize = 0;
  };
}, function (module, exports, __w_pdfjs_require__) {
  "use strict";

  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");
    }
  }
  var _require = __w_pdfjs_require__(0),
      ArrayBufferCopy = _require.ArrayBufferCopy,
      CreateIterResultObject = _require.CreateIterResultObject,
      IsFiniteNonNegativeNumber = _require.IsFiniteNonNegativeNumber,
      InvokeOrNoop = _require.InvokeOrNoop,
      PromiseInvokeOrNoop = _require.PromiseInvokeOrNoop,
      TransferArrayBuffer = _require.TransferArrayBuffer,
      ValidateAndNormalizeQueuingStrategy = _require.ValidateAndNormalizeQueuingStrategy,
      ValidateAndNormalizeHighWaterMark = _require.ValidateAndNormalizeHighWaterMark;
  var _require2 = __w_pdfjs_require__(0),
      createArrayFromList = _require2.createArrayFromList,
      createDataProperty = _require2.createDataProperty,
      typeIsObject = _require2.typeIsObject;
  var _require3 = __w_pdfjs_require__(1),
      assert = _require3.assert,
      rethrowAssertionErrorRejection = _require3.rethrowAssertionErrorRejection;
  var _require4 = __w_pdfjs_require__(3),
      DequeueValue = _require4.DequeueValue,
      EnqueueValueWithSize = _require4.EnqueueValueWithSize,
      ResetQueue = _require4.ResetQueue;
  var _require5 = __w_pdfjs_require__(2),
      AcquireWritableStreamDefaultWriter = _require5.AcquireWritableStreamDefaultWriter,
      IsWritableStream = _require5.IsWritableStream,
      IsWritableStreamLocked = _require5.IsWritableStreamLocked,
      WritableStreamAbort = _require5.WritableStreamAbort,
      WritableStreamDefaultWriterCloseWithErrorPropagation = _require5.WritableStreamDefaultWriterCloseWithErrorPropagation,
      WritableStreamDefaultWriterRelease = _require5.WritableStreamDefaultWriterRelease,
      WritableStreamDefaultWriterWrite = _require5.WritableStreamDefaultWriterWrite,
      WritableStreamCloseQueuedOrInFlight = _require5.WritableStreamCloseQueuedOrInFlight;
  var ReadableStream = function () {
    function ReadableStream() {
      var underlyingSource = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
          size = _ref.size,
          highWaterMark = _ref.highWaterMark;
      _classCallCheck(this, ReadableStream);
      this._state = 'readable';
      this._reader = undefined;
      this._storedError = undefined;
      this._disturbed = false;
      this._readableStreamController = undefined;
      var type = underlyingSource.type;
      var typeString = String(type);
      if (typeString === 'bytes') {
        if (highWaterMark === undefined) {
          highWaterMark = 0;
        }
        this._readableStreamController = new ReadableByteStreamController(this, underlyingSource, highWaterMark);
      } else if (type === undefined) {
        if (highWaterMark === undefined) {
          highWaterMark = 1;
        }
        this._readableStreamController = new ReadableStreamDefaultController(this, underlyingSource, size, highWaterMark);
      } else {
        throw new RangeError('Invalid type is specified');
      }
    }
    _createClass(ReadableStream, [{
      key: 'cancel',
      value: function cancel(reason) {
        if (IsReadableStream(this) === false) {
          return Promise.reject(streamBrandCheckException('cancel'));
        }
        if (IsReadableStreamLocked(this) === true) {
          return Promise.reject(new TypeError('Cannot cancel a stream that already has a reader'));
        }
        return ReadableStreamCancel(this, reason);
      }
    }, {
      key: 'getReader',
      value: function getReader() {
        var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
            mode = _ref2.mode;
        if (IsReadableStream(this) === false) {
          throw streamBrandCheckException('getReader');
        }
        if (mode === undefined) {
          return AcquireReadableStreamDefaultReader(this);
        }
        mode = String(mode);
        if (mode === 'byob') {
          return AcquireReadableStreamBYOBReader(this);
        }
        throw new RangeError('Invalid mode is specified');
      }
    }, {
      key: 'pipeThrough',
      value: function pipeThrough(_ref3, options) {
        var writable = _ref3.writable,
            readable = _ref3.readable;
        var promise = this.pipeTo(writable, options);
        ifIsObjectAndHasAPromiseIsHandledInternalSlotSetPromiseIsHandledToTrue(promise);
        return readable;
      }
    }, {
      key: 'pipeTo',
      value: function pipeTo(dest) {
        var _this = this;
        var _ref4 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
            preventClose = _ref4.preventClose,
            preventAbort = _ref4.preventAbort,
            preventCancel = _ref4.preventCancel;
        if (IsReadableStream(this) === false) {
          return Promise.reject(streamBrandCheckException('pipeTo'));
        }
        if (IsWritableStream(dest) === false) {
          return Promise.reject(new TypeError('ReadableStream.prototype.pipeTo\'s first argument must be a WritableStream'));
        }
        preventClose = Boolean(preventClose);
        preventAbort = Boolean(preventAbort);
        preventCancel = Boolean(preventCancel);
        if (IsReadableStreamLocked(this) === true) {
          return Promise.reject(new TypeError('ReadableStream.prototype.pipeTo cannot be used on a locked ReadableStream'));
        }
        if (IsWritableStreamLocked(dest) === true) {
          return Promise.reject(new TypeError('ReadableStream.prototype.pipeTo cannot be used on a locked WritableStream'));
        }
        var reader = AcquireReadableStreamDefaultReader(this);
        var writer = AcquireWritableStreamDefaultWriter(dest);
        var shuttingDown = false;
        var currentWrite = Promise.resolve();
        return new Promise(function (resolve, reject) {
          function pipeLoop() {
            currentWrite = Promise.resolve();
            if (shuttingDown === true) {
              return Promise.resolve();
            }
            return writer._readyPromise.then(function () {
              return ReadableStreamDefaultReaderRead(reader).then(function (_ref5) {
                var value = _ref5.value,
                    done = _ref5.done;
                if (done === true) {
                  return;
                }
                currentWrite = WritableStreamDefaultWriterWrite(writer, value).catch(function () {});
              });
            }).then(pipeLoop);
          }
          isOrBecomesErrored(_this, reader._closedPromise, function (storedError) {
            if (preventAbort === false) {
              shutdownWithAction(function () {
                return WritableStreamAbort(dest, storedError);
              }, true, storedError);
            } else {
              shutdown(true, storedError);
            }
          });
          isOrBecomesErrored(dest, writer._closedPromise, function (storedError) {
            if (preventCancel === false) {
              shutdownWithAction(function () {
                return ReadableStreamCancel(_this, storedError);
              }, true, storedError);
            } else {
              shutdown(true, storedError);
            }
          });
          isOrBecomesClosed(_this, reader._closedPromise, function () {
            if (preventClose === false) {
              shutdownWithAction(function () {
                return WritableStreamDefaultWriterCloseWithErrorPropagation(writer);
              });
            } else {
              shutdown();
            }
          });
          if (WritableStreamCloseQueuedOrInFlight(dest) === true || dest._state === 'closed') {
            var destClosed = new TypeError('the destination writable stream closed before all data could be piped to it');
            if (preventCancel === false) {
              shutdownWithAction(function () {
                return ReadableStreamCancel(_this, destClosed);
              }, true, destClosed);
            } else {
              shutdown(true, destClosed);
            }
          }
          pipeLoop().catch(function (err) {
            currentWrite = Promise.resolve();
            rethrowAssertionErrorRejection(err);
          });
          function waitForWritesToFinish() {
            var oldCurrentWrite = currentWrite;
            return currentWrite.then(function () {
              return oldCurrentWrite !== currentWrite ? waitForWritesToFinish() : undefined;
            });
          }
          function isOrBecomesErrored(stream, promise, action) {
            if (stream._state === 'errored') {
              action(stream._storedError);
            } else {
              promise.catch(action).catch(rethrowAssertionErrorRejection);
            }
          }
          function isOrBecomesClosed(stream, promise, action) {
            if (stream._state === 'closed') {
              action();
            } else {
              promise.then(action).catch(rethrowAssertionErrorRejection);
            }
          }
          function shutdownWithAction(action, originalIsError, originalError) {
            if (shuttingDown === true) {
              return;
            }
            shuttingDown = true;
            if (dest._state === 'writable' && WritableStreamCloseQueuedOrInFlight(dest) === false) {
              waitForWritesToFinish().then(doTheRest);
            } else {
              doTheRest();
            }
            function doTheRest() {
              action().then(function () {
                return finalize(originalIsError, originalError);
              }, function (newError) {
                return finalize(true, newError);
              }).catch(rethrowAssertionErrorRejection);
            }
          }
          function shutdown(isError, error) {
            if (shuttingDown === true) {
              return;
            }
            shuttingDown = true;
            if (dest._state === 'writable' && WritableStreamCloseQueuedOrInFlight(dest) === false) {
              waitForWritesToFinish().then(function () {
                return finalize(isError, error);
              }).catch(rethrowAssertionErrorRejection);
            } else {
              finalize(isError, error);
            }
          }
          function finalize(isError, error) {
            WritableStreamDefaultWriterRelease(writer);
            ReadableStreamReaderGenericRelease(reader);
            if (isError) {
              reject(error);
            } else {
              resolve(undefined);
            }
          }
        });
      }
    }, {
      key: 'tee',
      value: function tee() {
        if (IsReadableStream(this) === false) {
          throw streamBrandCheckException('tee');
        }
        var branches = ReadableStreamTee(this, false);
        return createArrayFromList(branches);
      }
    }, {
      key: 'locked',
      get: function get() {
        if (IsReadableStream(this) === false) {
          throw streamBrandCheckException('locked');
        }
        return IsReadableStreamLocked(this);
      }
    }]);
    return ReadableStream;
  }();
  module.exports = {
    ReadableStream: ReadableStream,
    IsReadableStreamDisturbed: IsReadableStreamDisturbed,
    ReadableStreamDefaultControllerClose: ReadableStreamDefaultControllerClose,
    ReadableStreamDefaultControllerEnqueue: ReadableStreamDefaultControllerEnqueue,
    ReadableStreamDefaultControllerError: ReadableStreamDefaultControllerError,
    ReadableStreamDefaultControllerGetDesiredSize: ReadableStreamDefaultControllerGetDesiredSize
  };
  function AcquireReadableStreamBYOBReader(stream) {
    return new ReadableStreamBYOBReader(stream);
  }
  function AcquireReadableStreamDefaultReader(stream) {
    return new ReadableStreamDefaultReader(stream);
  }
  function IsReadableStream(x) {
    if (!typeIsObject(x)) {
      return false;
    }
    if (!Object.prototype.hasOwnProperty.call(x, '_readableStreamController')) {
      return false;
    }
    return true;
  }
  function IsReadableStreamDisturbed(stream) {
    assert(IsReadableStream(stream) === true, 'IsReadableStreamDisturbed should only be used on known readable streams');
    return stream._disturbed;
  }
  function IsReadableStreamLocked(stream) {
    assert(IsReadableStream(stream) === true, 'IsReadableStreamLocked should only be used on known readable streams');
    if (stream._reader === undefined) {
      return false;
    }
    return true;
  }
  function ReadableStreamTee(stream, cloneForBranch2) {
    assert(IsReadableStream(stream) === true);
    assert(typeof cloneForBranch2 === 'boolean');
    var reader = AcquireReadableStreamDefaultReader(stream);
    var teeState = {
      closedOrErrored: false,
      canceled1: false,
      canceled2: false,
      reason1: undefined,
      reason2: undefined
    };
    teeState.promise = new Promise(function (resolve) {
      teeState._resolve = resolve;
    });
    var pull = create_ReadableStreamTeePullFunction();
    pull._reader = reader;
    pull._teeState = teeState;
    pull._cloneForBranch2 = cloneForBranch2;
    var cancel1 = create_ReadableStreamTeeBranch1CancelFunction();
    cancel1._stream = stream;
    cancel1._teeState = teeState;
    var cancel2 = create_ReadableStreamTeeBranch2CancelFunction();
    cancel2._stream = stream;
    cancel2._teeState = teeState;
    var underlyingSource1 = Object.create(Object.prototype);
    createDataProperty(underlyingSource1, 'pull', pull);
    createDataProperty(underlyingSource1, 'cancel', cancel1);
    var branch1Stream = new ReadableStream(underlyingSource1);
    var underlyingSource2 = Object.create(Object.prototype);
    createDataProperty(underlyingSource2, 'pull', pull);
    createDataProperty(underlyingSource2, 'cancel', cancel2);
    var branch2Stream = new ReadableStream(underlyingSource2);
    pull._branch1 = branch1Stream._readableStreamController;
    pull._branch2 = branch2Stream._readableStreamController;
    reader._closedPromise.catch(function (r) {
      if (teeState.closedOrErrored === true) {
        return;
      }
      ReadableStreamDefaultControllerError(pull._branch1, r);
      ReadableStreamDefaultControllerError(pull._branch2, r);
      teeState.closedOrErrored = true;
    });
    return [branch1Stream, branch2Stream];
  }
  function create_ReadableStreamTeePullFunction() {
    function f() {
      var reader = f._reader,
          branch1 = f._branch1,
          branch2 = f._branch2,
          teeState = f._teeState;
      return ReadableStreamDefaultReaderRead(reader).then(function (result) {
        assert(typeIsObject(result));
        var value = result.value;
        var done = result.done;
        assert(typeof done === 'boolean');
        if (done === true && teeState.closedOrErrored === false) {
          if (teeState.canceled1 === false) {
            ReadableStreamDefaultControllerClose(branch1);
          }
          if (teeState.canceled2 === false) {
            ReadableStreamDefaultControllerClose(branch2);
          }
          teeState.closedOrErrored = true;
        }
        if (teeState.closedOrErrored === true) {
          return;
        }
        var value1 = value;
        var value2 = value;
        if (teeState.canceled1 === false) {
          ReadableStreamDefaultControllerEnqueue(branch1, value1);
        }
        if (teeState.canceled2 === false) {
          ReadableStreamDefaultControllerEnqueue(branch2, value2);
        }
      });
    }
    return f;
  }
  function create_ReadableStreamTeeBranch1CancelFunction() {
    function f(reason) {
      var stream = f._stream,
          teeState = f._teeState;
      teeState.canceled1 = true;
      teeState.reason1 = reason;
      if (teeState.canceled2 === true) {
        var compositeReason = createArrayFromList([teeState.reason1, teeState.reason2]);
        var cancelResult = ReadableStreamCancel(stream, compositeReason);
        teeState._resolve(cancelResult);
      }
      return teeState.promise;
    }
    return f;
  }
  function create_ReadableStreamTeeBranch2CancelFunction() {
    function f(reason) {
      var stream = f._stream,
          teeState = f._teeState;
      teeState.canceled2 = true;
      teeState.reason2 = reason;
      if (teeState.canceled1 === true) {
        var compositeReason = createArrayFromList([teeState.reason1, teeState.reason2]);
        var cancelResult = ReadableStreamCancel(stream, compositeReason);
        teeState._resolve(cancelResult);
      }
      return teeState.promise;
    }
    return f;
  }
  function ReadableStreamAddReadIntoRequest(stream) {
    assert(IsReadableStreamBYOBReader(stream._reader) === true);
    assert(stream._state === 'readable' || stream._state === 'closed');
    var promise = new Promise(function (resolve, reject) {
      var readIntoRequest = {
        _resolve: resolve,
        _reject: reject
      };
      stream._reader._readIntoRequests.push(readIntoRequest);
    });
    return promise;
  }
  function ReadableStreamAddReadRequest(stream) {
    assert(IsReadableStreamDefaultReader(stream._reader) === true);
    assert(stream._state === 'readable');
    var promise = new Promise(function (resolve, reject) {
      var readRequest = {
        _resolve: resolve,
        _reject: reject
      };
      stream._reader._readRequests.push(readRequest);
    });
    return promise;
  }
  function ReadableStreamCancel(stream, reason) {
    stream._disturbed = true;
    if (stream._state === 'closed') {
      return Promise.resolve(undefined);
    }
    if (stream._state === 'errored') {
      return Promise.reject(stream._storedError);
    }
    ReadableStreamClose(stream);
    var sourceCancelPromise = stream._readableStreamController.__cancelSteps(reason);
    return sourceCancelPromise.then(function () {
      return undefined;
    });
  }
  function ReadableStreamClose(stream) {
    assert(stream._state === 'readable');
    stream._state = 'closed';
    var reader = stream._reader;
    if (reader === undefined) {
      return undefined;
    }
    if (IsReadableStreamDefaultReader(reader) === true) {
      for (var i = 0; i < reader._readRequests.length; i++) {
        var _resolve = reader._readRequests[i]._resolve;
        _resolve(CreateIterResultObject(undefined, true));
      }
      reader._readRequests = [];
    }
    defaultReaderClosedPromiseResolve(reader);
    return undefined;
  }
  function ReadableStreamError(stream, e) {
    assert(IsReadableStream(stream) === true, 'stream must be ReadableStream');
    assert(stream._state === 'readable', 'state must be readable');
    stream._state = 'errored';
    stream._storedError = e;
    var reader = stream._reader;
    if (reader === undefined) {
      return undefined;
    }
    if (IsReadableStreamDefaultReader(reader) === true) {
      for (var i = 0; i < reader._readRequests.length; i++) {
        var readRequest = reader._readRequests[i];
        readRequest._reject(e);
      }
      reader._readRequests = [];
    } else {
      assert(IsReadableStreamBYOBReader(reader), 'reader must be ReadableStreamBYOBReader');
      for (var _i = 0; _i < reader._readIntoRequests.length; _i++) {
        var readIntoRequest = reader._readIntoRequests[_i];
        readIntoRequest._reject(e);
      }
      reader._readIntoRequests = [];
    }
    defaultReaderClosedPromiseReject(reader, e);
    reader._closedPromise.catch(function () {});
  }
  function ReadableStreamFulfillReadIntoRequest(stream, chunk, done) {
    var reader = stream._reader;
    assert(reader._readIntoRequests.length > 0);
    var readIntoRequest = reader._readIntoRequests.shift();
    readIntoRequest._resolve(CreateIterResultObject(chunk, done));
  }
  function ReadableStreamFulfillReadRequest(stream, chunk, done) {
    var reader = stream._reader;
    assert(reader._readRequests.length > 0);
    var readRequest = reader._readRequests.shift();
    readRequest._resolve(CreateIterResultObject(chunk, done));
  }
  function ReadableStreamGetNumReadIntoRequests(stream) {
    return stream._reader._readIntoRequests.length;
  }
  function ReadableStreamGetNumReadRequests(stream) {
    return stream._reader._readRequests.length;
  }
  function ReadableStreamHasBYOBReader(stream) {
    var reader = stream._reader;
    if (reader === undefined) {
      return false;
    }
    if (IsReadableStreamBYOBReader(reader) === false) {
      return false;
    }
    return true;
  }
  function ReadableStreamHasDefaultReader(stream) {
    var reader = stream._reader;
    if (reader === undefined) {
      return false;
    }
    if (IsReadableStreamDefaultReader(reader) === false) {
      return false;
    }
    return true;
  }
  var ReadableStreamDefaultReader = function () {
    function ReadableStreamDefaultReader(stream) {
      _classCallCheck(this, ReadableStreamDefaultReader);
      if (IsReadableStream(stream) === false) {
        throw new TypeError('ReadableStreamDefaultReader can only be constructed with a ReadableStream instance');
      }
      if (IsReadableStreamLocked(stream) === true) {
        throw new TypeError('This stream has already been locked for exclusive reading by another reader');
      }
      ReadableStreamReaderGenericInitialize(this, stream);
      this._readRequests = [];
    }
    _createClass(ReadableStreamDefaultReader, [{
      key: 'cancel',
      value: function cancel(reason) {
        if (IsReadableStreamDefaultReader(this) === false) {
          return Promise.reject(defaultReaderBrandCheckException('cancel'));
        }
        if (this._ownerReadableStream === undefined) {
          return Promise.reject(readerLockException('cancel'));
        }
        return ReadableStreamReaderGenericCancel(this, reason);
      }
    }, {
      key: 'read',
      value: function read() {
        if (IsReadableStreamDefaultReader(this) === false) {
          return Promise.reject(defaultReaderBrandCheckException('read'));
        }
        if (this._ownerReadableStream === undefined) {
          return Promise.reject(readerLockException('read from'));
        }
        return ReadableStreamDefaultReaderRead(this);
      }
    }, {
      key: 'releaseLock',
      value: function releaseLock() {
        if (IsReadableStreamDefaultReader(this) === false) {
          throw defaultReaderBrandCheckException('releaseLock');
        }
        if (this._ownerReadableStream === undefined) {
          return;
        }
        if (this._readRequests.length > 0) {
          throw new TypeError('Tried to release a reader lock when that reader has pending read() calls un-settled');
        }
        ReadableStreamReaderGenericRelease(this);
      }
    }, {
      key: 'closed',
      get: function get() {
        if (IsReadableStreamDefaultReader(this) === false) {
          return Promise.reject(defaultReaderBrandCheckException('closed'));
        }
        return this._closedPromise;
      }
    }]);
    return ReadableStreamDefaultReader;
  }();
  var ReadableStreamBYOBReader = function () {
    function ReadableStreamBYOBReader(stream) {
      _classCallCheck(this, ReadableStreamBYOBReader);
      if (!IsReadableStream(stream)) {
        throw new TypeError('ReadableStreamBYOBReader can only be constructed with a ReadableStream instance given a ' + 'byte source');
      }
      if (IsReadableByteStreamController(stream._readableStreamController) === false) {
        throw new TypeError('Cannot construct a ReadableStreamBYOBReader for a stream not constructed with a byte ' + 'source');
      }
      if (IsReadableStreamLocked(stream)) {
        throw new TypeError('This stream has already been locked for exclusive reading by another reader');
      }
      ReadableStreamReaderGenericInitialize(this, stream);
      this._readIntoRequests = [];
    }
    _createClass(ReadableStreamBYOBReader, [{
      key: 'cancel',
      value: function cancel(reason) {
        if (!IsReadableStreamBYOBReader(this)) {
          return Promise.reject(byobReaderBrandCheckException('cancel'));
        }
        if (this._ownerReadableStream === undefined) {
          return Promise.reject(readerLockException('cancel'));
        }
        return ReadableStreamReaderGenericCancel(this, reason);
      }
    }, {
      key: 'read',
      value: function read(view) {
        if (!IsReadableStreamBYOBReader(this)) {
          return Promise.reject(byobReaderBrandCheckException('read'));
        }
        if (this._ownerReadableStream === undefined) {
          return Promise.reject(readerLockException('read from'));
        }
        if (!ArrayBuffer.isView(view)) {
          return Promise.reject(new TypeError('view must be an array buffer view'));
        }
        if (view.byteLength === 0) {
          return Promise.reject(new TypeError('view must have non-zero byteLength'));
        }
        return ReadableStreamBYOBReaderRead(this, view);
      }
    }, {
      key: 'releaseLock',
      value: function releaseLock() {
        if (!IsReadableStreamBYOBReader(this)) {
          throw byobReaderBrandCheckException('releaseLock');
        }
        if (this._ownerReadableStream === undefined) {
          return;
        }
        if (this._readIntoRequests.length > 0) {
          throw new TypeError('Tried to release a reader lock when that reader has pending read() calls un-settled');
        }
        ReadableStreamReaderGenericRelease(this);
      }
    }, {
      key: 'closed',
      get: function get() {
        if (!IsReadableStreamBYOBReader(this)) {
          return Promise.reject(byobReaderBrandCheckException('closed'));
        }
        return this._closedPromise;
      }
    }]);
    return ReadableStreamBYOBReader;
  }();
  function IsReadableStreamBYOBReader(x) {
    if (!typeIsObject(x)) {
      return false;
    }
    if (!Object.prototype.hasOwnProperty.call(x, '_readIntoRequests')) {
      return false;
    }
    return true;
  }
  function IsReadableStreamDefaultReader(x) {
    if (!typeIsObject(x)) {
      return false;
    }
    if (!Object.prototype.hasOwnProperty.call(x, '_readRequests')) {
      return false;
    }
    return true;
  }
  function ReadableStreamReaderGenericInitialize(reader, stream) {
    reader._ownerReadableStream = stream;
    stream._reader = reader;
    if (stream._state === 'readable') {
      defaultReaderClosedPromiseInitialize(reader);
    } else if (stream._state === 'closed') {
      defaultReaderClosedPromiseInitializeAsResolved(reader);
    } else {
      assert(stream._state === 'errored', 'state must be errored');
      defaultReaderClosedPromiseInitializeAsRejected(reader, stream._storedError);
      reader._closedPromise.catch(function () {});
    }
  }
  function ReadableStreamReaderGenericCancel(reader, reason) {
    var stream = reader._ownerReadableStream;
    assert(stream !== undefined);
    return ReadableStreamCancel(stream, reason);
  }
  function ReadableStreamReaderGenericRelease(reader) {
    assert(reader._ownerReadableStream !== undefined);
    assert(reader._ownerReadableStream._reader === reader);
    if (reader._ownerReadableStream._state === 'readable') {
      defaultReaderClosedPromiseReject(reader, new TypeError('Reader was released and can no longer be used to monitor the stream\'s closedness'));
    } else {
      defaultReaderClosedPromiseResetToRejected(reader, new TypeError('Reader was released and can no longer be used to monitor the stream\'s closedness'));
    }
    reader._closedPromise.catch(function () {});
    reader._ownerReadableStream._reader = undefined;
    reader._ownerReadableStream = undefined;
  }
  function ReadableStreamBYOBReaderRead(reader, view) {
    var stream = reader._ownerReadableStream;
    assert(stream !== undefined);
    stream._disturbed = true;
    if (stream._state === 'errored') {
      return Promise.reject(stream._storedError);
    }
    return ReadableByteStreamControllerPullInto(stream._readableStreamController, view);
  }
  function ReadableStreamDefaultReaderRead(reader) {
    var stream = reader._ownerReadableStream;
    assert(stream !== undefined);
    stream._disturbed = true;
    if (stream._state === 'closed') {
      return Promise.resolve(CreateIterResultObject(undefined, true));
    }
    if (stream._state === 'errored') {
      return Promise.reject(stream._storedError);
    }
    assert(stream._state === 'readable');
    return stream._readableStreamController.__pullSteps();
  }
  var ReadableStreamDefaultController = function () {
    function ReadableStreamDefaultController(stream, underlyingSource, size, highWaterMark) {
      _classCallCheck(this, ReadableStreamDefaultController);
      if (IsReadableStream(stream) === false) {
        throw new TypeError('ReadableStreamDefaultController can only be constructed with a ReadableStream instance');
      }
      if (stream._readableStreamController !== undefined) {
        throw new TypeError('ReadableStreamDefaultController instances can only be created by the ReadableStream constructor');
      }
      this._controlledReadableStream = stream;
      this._underlyingSource = underlyingSource;
      this._queue = undefined;
      this._queueTotalSize = undefined;
      ResetQueue(this);
      this._started = false;
      this._closeRequested = false;
      this._pullAgain = false;
      this._pulling = false;
      var normalizedStrategy = ValidateAndNormalizeQueuingStrategy(size, highWaterMark);
      this._strategySize = normalizedStrategy.size;
      this._strategyHWM = normalizedStrategy.highWaterMark;
      var controller = this;
      var startResult = InvokeOrNoop(underlyingSource, 'start', [this]);
      Promise.resolve(startResult).then(function () {
        controller._started = true;
        assert(controller._pulling === false);
        assert(controller._pullAgain === false);
        ReadableStreamDefaultControllerCallPullIfNeeded(controller);
      }, function (r) {
        ReadableStreamDefaultControllerErrorIfNeeded(controller, r);
      }).catch(rethrowAssertionErrorRejection);
    }
    _createClass(ReadableStreamDefaultController, [{
      key: 'close',
      value: function close() {
        if (IsReadableStreamDefaultController(this) === false) {
          throw defaultControllerBrandCheckException('close');
        }
        if (this._closeRequested === true) {
          throw new TypeError('The stream has already been closed; do not close it again!');
        }
        var state = this._controlledReadableStream._state;
        if (state !== 'readable') {
          throw new TypeError('The stream (in ' + state + ' state) is not in the readable state and cannot be closed');
        }
        ReadableStreamDefaultControllerClose(this);
      }
    }, {
      key: 'enqueue',
      value: function enqueue(chunk) {
        if (IsReadableStreamDefaultController(this) === false) {
          throw defaultControllerBrandCheckException('enqueue');
        }
        if (this._closeRequested === true) {
          throw new TypeError('stream is closed or draining');
        }
        var state = this._controlledReadableStream._state;
        if (state !== 'readable') {
          throw new TypeError('The stream (in ' + state + ' state) is not in the readable state and cannot be enqueued to');
        }
        return ReadableStreamDefaultControllerEnqueue(this, chunk);
      }
    }, {
      key: 'error',
      value: function error(e) {
        if (IsReadableStreamDefaultController(this) === false) {
          throw defaultControllerBrandCheckException('error');
        }
        var stream = this._controlledReadableStream;
        if (stream._state !== 'readable') {
          throw new TypeError('The stream is ' + stream._state + ' and so cannot be errored');
        }
        ReadableStreamDefaultControllerError(this, e);
      }
    }, {
      key: '__cancelSteps',
      value: function __cancelSteps(reason) {
        ResetQueue(this);
        return PromiseInvokeOrNoop(this._underlyingSource, 'cancel', [reason]);
      }
    }, {
      key: '__pullSteps',
      value: function __pullSteps() {
        var stream = this._controlledReadableStream;
        if (this._queue.length > 0) {
          var chunk = DequeueValue(this);
          if (this._closeRequested === true && this._queue.length === 0) {
            ReadableStreamClose(stream);
          } else {
            ReadableStreamDefaultControllerCallPullIfNeeded(this);
          }
          return Promise.resolve(CreateIterResultObject(chunk, false));
        }
        var pendingPromise = ReadableStreamAddReadRequest(stream);
        ReadableStreamDefaultControllerCallPullIfNeeded(this);
        return pendingPromise;
      }
    }, {
      key: 'desiredSize',
      get: function get() {
        if (IsReadableStreamDefaultController(this) === false) {
          throw defaultControllerBrandCheckException('desiredSize');
        }
        return ReadableStreamDefaultControllerGetDesiredSize(this);
      }
    }]);
    return ReadableStreamDefaultController;
  }();
  function IsReadableStreamDefaultController(x) {
    if (!typeIsObject(x)) {
      return false;
    }
    if (!Object.prototype.hasOwnProperty.call(x, '_underlyingSource')) {
      return false;
    }
    return true;
  }
  function ReadableStreamDefaultControllerCallPullIfNeeded(controller) {
    var shouldPull = ReadableStreamDefaultControllerShouldCallPull(controller);
    if (shouldPull === false) {
      return undefined;
    }
    if (controller._pulling === true) {
      controller._pullAgain = true;
      return undefined;
    }
    assert(controller._pullAgain === false);
    controller._pulling = true;
    var pullPromise = PromiseInvokeOrNoop(controller._underlyingSource, 'pull', [controller]);
    pullPromise.then(function () {
      controller._pulling = false;
      if (controller._pullAgain === true) {
        controller._pullAgain = false;
        return ReadableStreamDefaultControllerCallPullIfNeeded(controller);
      }
      return undefined;
    }, function (e) {
      ReadableStreamDefaultControllerErrorIfNeeded(controller, e);
    }).catch(rethrowAssertionErrorRejection);
    return undefined;
  }
  function ReadableStreamDefaultControllerShouldCallPull(controller) {
    var stream = controller._controlledReadableStream;
    if (stream._state === 'closed' || stream._state === 'errored') {
      return false;
    }
    if (controller._closeRequested === true) {
      return false;
    }
    if (controller._started === false) {
      return false;
    }
    if (IsReadableStreamLocked(stream) === true && ReadableStreamGetNumReadRequests(stream) > 0) {
      return true;
    }
    var desiredSize = ReadableStreamDefaultControllerGetDesiredSize(controller);
    if (desiredSize > 0) {
      return true;
    }
    return false;
  }
  function ReadableStreamDefaultControllerClose(controller) {
    var stream = controller._controlledReadableStream;
    assert(controller._closeRequested === false);
    assert(stream._state === 'readable');
    controller._closeRequested = true;
    if (controller._queue.length === 0) {
      ReadableStreamClose(stream);
    }
  }
  function ReadableStreamDefaultControllerEnqueue(controller, chunk) {
    var stream = controller._controlledReadableStream;
    assert(controller._closeRequested === false);
    assert(stream._state === 'readable');
    if (IsReadableStreamLocked(stream) === true && ReadableStreamGetNumReadRequests(stream) > 0) {
      ReadableStreamFulfillReadRequest(stream, chunk, false);
    } else {
      var chunkSize = 1;
      if (controller._strategySize !== undefined) {
        var strategySize = controller._strategySize;
        try {
          chunkSize = strategySize(chunk);
        } catch (chunkSizeE) {
          ReadableStreamDefaultControllerErrorIfNeeded(controller, chunkSizeE);
          throw chunkSizeE;
        }
      }
      try {
        EnqueueValueWithSize(controller, chunk, chunkSize);
      } catch (enqueueE) {
        ReadableStreamDefaultControllerErrorIfNeeded(controller, enqueueE);
        throw enqueueE;
      }
    }
    ReadableStreamDefaultControllerCallPullIfNeeded(controller);
    return undefined;
  }
  function ReadableStreamDefaultControllerError(controller, e) {
    var stream = controller._controlledReadableStream;
    assert(stream._state === 'readable');
    ResetQueue(controller);
    ReadableStreamError(stream, e);
  }
  function ReadableStreamDefaultControllerErrorIfNeeded(controller, e) {
    if (controller._controlledReadableStream._state === 'readable') {
      ReadableStreamDefaultControllerError(controller, e);
    }
  }
  function ReadableStreamDefaultControllerGetDesiredSize(controller) {
    var stream = controller._controlledReadableStream;
    var state = stream._state;
    if (state === 'errored') {
      return null;
    }
    if (state === 'closed') {
      return 0;
    }
    return controller._strategyHWM - controller._queueTotalSize;
  }
  var ReadableStreamBYOBRequest = function () {
    function ReadableStreamBYOBRequest(controller, view) {
      _classCallCheck(this, ReadableStreamBYOBRequest);
      this._associatedReadableByteStreamController = controller;
      this._view = view;
    }
    _createClass(ReadableStreamBYOBRequest, [{
      key: 'respond',
      value: function respond(bytesWritten) {
        if (IsReadableStreamBYOBRequest(this) === false) {
          throw byobRequestBrandCheckException('respond');
        }
        if (this._associatedReadableByteStreamController === undefined) {
          throw new TypeError('This BYOB request has been invalidated');
        }
        ReadableByteStreamControllerRespond(this._associatedReadableByteStreamController, bytesWritten);
      }
    }, {
      key: 'respondWithNewView',
      value: function respondWithNewView(view) {
        if (IsReadableStreamBYOBRequest(this) === false) {
          throw byobRequestBrandCheckException('respond');
        }
        if (this._associatedReadableByteStreamController === undefined) {
          throw new TypeError('This BYOB request has been invalidated');
        }
        if (!ArrayBuffer.isView(view)) {
          throw new TypeError('You can only respond with array buffer views');
        }
        ReadableByteStreamControllerRespondWithNewView(this._associatedReadableByteStreamController, view);
      }
    }, {
      key: 'view',
      get: function get() {
        return this._view;
      }
    }]);
    return ReadableStreamBYOBRequest;
  }();
  var ReadableByteStreamController = function () {
    function ReadableByteStreamController(stream, underlyingByteSource, highWaterMark) {
      _classCallCheck(this, ReadableByteStreamController);
      if (IsReadableStream(stream) === false) {
        throw new TypeError('ReadableByteStreamController can only be constructed with a ReadableStream instance given ' + 'a byte source');
      }
      if (stream._readableStreamController !== undefined) {
        throw new TypeError('ReadableByteStreamController instances can only be created by the ReadableStream constructor given a byte ' + 'source');
      }
      this._controlledReadableStream = stream;
      this._underlyingByteSource = underlyingByteSource;
      this._pullAgain = false;
      this._pulling = false;
      ReadableByteStreamControllerClearPendingPullIntos(this);
      this._queue = this._queueTotalSize = undefined;
      ResetQueue(this);
      this._closeRequested = false;
      this._started = false;
      this._strategyHWM = ValidateAndNormalizeHighWaterMark(highWaterMark);
      var autoAllocateChunkSize = underlyingByteSource.autoAllocateChunkSize;
      if (autoAllocateChunkSize !== undefined) {
        if (Number.isInteger(autoAllocateChunkSize) === false || autoAllocateChunkSize <= 0) {
          throw new RangeError('autoAllocateChunkSize must be a positive integer');
        }
      }
      this._autoAllocateChunkSize = autoAllocateChunkSize;
      this._pendingPullIntos = [];
      var controller = this;
      var startResult = InvokeOrNoop(underlyingByteSource, 'start', [this]);
      Promise.resolve(startResult).then(function () {
        controller._started = true;
        assert(controller._pulling === false);
        assert(controller._pullAgain === false);
        ReadableByteStreamControllerCallPullIfNeeded(controller);
      }, function (r) {
        if (stream._state === 'readable') {
          ReadableByteStreamControllerError(controller, r);
        }
      }).catch(rethrowAssertionErrorRejection);
    }
    _createClass(ReadableByteStreamController, [{
      key: 'close',
      value: function close() {
        if (IsReadableByteStreamController(this) === false) {
          throw byteStreamControllerBrandCheckException('close');
        }
        if (this._closeRequested === true) {
          throw new TypeError('The stream has already been closed; do not close it again!');
        }
        var state = this._controlledReadableStream._state;
        if (state !== 'readable') {
          throw new TypeError('The stream (in ' + state + ' state) is not in the readable state and cannot be closed');
        }
        ReadableByteStreamControllerClose(this);
      }
    }, {
      key: 'enqueue',
      value: function enqueue(chunk) {
        if (IsReadableByteStreamController(this) === false) {
          throw byteStreamControllerBrandCheckException('enqueue');
        }
        if (this._closeRequested === true) {
          throw new TypeError('stream is closed or draining');
        }
        var state = this._controlledReadableStream._state;
        if (state !== 'readable') {
          throw new TypeError('The stream (in ' + state + ' state) is not in the readable state and cannot be enqueued to');
        }
        if (!ArrayBuffer.isView(chunk)) {
          throw new TypeError('You can only enqueue array buffer views when using a ReadableByteStreamController');
        }
        ReadableByteStreamControllerEnqueue(this, chunk);
      }
    }, {
      key: 'error',
      value: function error(e) {
        if (IsReadableByteStreamController(this) === false) {
          throw byteStreamControllerBrandCheckException('error');
        }
        var stream = this._controlledReadableStream;
        if (stream._state !== 'readable') {
          throw new TypeError('The stream is ' + stream._state + ' and so cannot be errored');
        }
        ReadableByteStreamControllerError(this, e);
      }
    }, {
      key: '__cancelSteps',
      value: function __cancelSteps(reason) {
        if (this._pendingPullIntos.length > 0) {
          var firstDescriptor = this._pendingPullIntos[0];
          firstDescriptor.bytesFilled = 0;
        }
        ResetQueue(this);
        return PromiseInvokeOrNoop(this._underlyingByteSource, 'cancel', [reason]);
      }
    }, {
      key: '__pullSteps',
      value: function __pullSteps() {
        var stream = this._controlledReadableStream;
        assert(ReadableStreamHasDefaultReader(stream) === true);
        if (this._queueTotalSize > 0) {
          assert(ReadableStreamGetNumReadRequests(stream) === 0);
          var entry = this._queue.shift();
          this._queueTotalSize -= entry.byteLength;
          ReadableByteStreamControllerHandleQueueDrain(this);
          var view = void 0;
          try {
            view = new Uint8Array(entry.buffer, entry.byteOffset, entry.byteLength);
          } catch (viewE) {
            return Promise.reject(viewE);
          }
          return Promise.resolve(CreateIterResultObject(view, false));
        }
        var autoAllocateChunkSize = this._autoAllocateChunkSize;
        if (autoAllocateChunkSize !== undefined) {
          var buffer = void 0;
          try {
            buffer = new ArrayBuffer(autoAllocateChunkSize);
          } catch (bufferE) {
            return Promise.reject(bufferE);
          }
          var pullIntoDescriptor = {
            buffer: buffer,
            byteOffset: 0,
            byteLength: autoAllocateChunkSize,
            bytesFilled: 0,
            elementSize: 1,
            ctor: Uint8Array,
            readerType: 'default'
          };
          this._pendingPullIntos.push(pullIntoDescriptor);
        }
        var promise = ReadableStreamAddReadRequest(stream);
        ReadableByteStreamControllerCallPullIfNeeded(this);
        return promise;
      }
    }, {
      key: 'byobRequest',
      get: function get() {
        if (IsReadableByteStreamController(this) === false) {
          throw byteStreamControllerBrandCheckException('byobRequest');
        }
        if (this._byobRequest === undefined && this._pendingPullIntos.length > 0) {
          var firstDescriptor = this._pendingPullIntos[0];
          var view = new Uint8Array(firstDescriptor.buffer, firstDescriptor.byteOffset + firstDescriptor.bytesFilled, firstDescriptor.byteLength - firstDescriptor.bytesFilled);
          this._byobRequest = new ReadableStreamBYOBRequest(this, view);
        }
        return this._byobRequest;
      }
    }, {
      key: 'desiredSize',
      get: function get() {
        if (IsReadableByteStreamController(this) === false) {
          throw byteStreamControllerBrandCheckException('desiredSize');
        }
        return ReadableByteStreamControllerGetDesiredSize(this);
      }
    }]);
    return ReadableByteStreamController;
  }();
  function IsReadableByteStreamController(x) {
    if (!typeIsObject(x)) {
      return false;
    }
    if (!Object.prototype.hasOwnProperty.call(x, '_underlyingByteSource')) {
      return false;
    }
    return true;
  }
  function IsReadableStreamBYOBRequest(x) {
    if (!typeIsObject(x)) {
      return false;
    }
    if (!Object.prototype.hasOwnProperty.call(x, '_associatedReadableByteStreamController')) {
      return false;
    }
    return true;
  }
  function ReadableByteStreamControllerCallPullIfNeeded(controller) {
    var shouldPull = ReadableByteStreamControllerShouldCallPull(controller);
    if (shouldPull === false) {
      return undefined;
    }
    if (controller._pulling === true) {
      controller._pullAgain = true;
      return undefined;
    }
    assert(controller._pullAgain === false);
    controller._pulling = true;
    var pullPromise = PromiseInvokeOrNoop(controller._underlyingByteSource, 'pull', [controller]);
    pullPromise.then(function () {
      controller._pulling = false;
      if (controller._pullAgain === true) {
        controller._pullAgain = false;
        ReadableByteStreamControllerCallPullIfNeeded(controller);
      }
    }, function (e) {
      if (controller._controlledReadableStream._state === 'readable') {
        ReadableByteStreamControllerError(controller, e);
      }
    }).catch(rethrowAssertionErrorRejection);
    return undefined;
  }
  function ReadableByteStreamControllerClearPendingPullIntos(controller) {
    ReadableByteStreamControllerInvalidateBYOBRequest(controller);
    controller._pendingPullIntos = [];
  }
  function ReadableByteStreamControllerCommitPullIntoDescriptor(stream, pullIntoDescriptor) {
    assert(stream._state !== 'errored', 'state must not be errored');
    var done = false;
    if (stream._state === 'closed') {
      assert(pullIntoDescriptor.bytesFilled === 0);
      done = true;
    }
    var filledView = ReadableByteStreamControllerConvertPullIntoDescriptor(pullIntoDescriptor);
    if (pullIntoDescriptor.readerType === 'default') {
      ReadableStreamFulfillReadRequest(stream, filledView, done);
    } else {
      assert(pullIntoDescriptor.readerType === 'byob');
      ReadableStreamFulfillReadIntoRequest(stream, filledView, done);
    }
  }
  function ReadableByteStreamControllerConvertPullIntoDescriptor(pullIntoDescriptor) {
    var bytesFilled = pullIntoDescriptor.bytesFilled;
    var elementSize = pullIntoDescriptor.elementSize;
    assert(bytesFilled <= pullIntoDescriptor.byteLength);
    assert(bytesFilled % elementSize === 0);
    return new pullIntoDescriptor.ctor(pullIntoDescriptor.buffer, pullIntoDescriptor.byteOffset, bytesFilled / elementSize);
  }
  function ReadableByteStreamControllerEnqueueChunkToQueue(controller, buffer, byteOffset, byteLength) {
    controller._queue.push({
      buffer: buffer,
      byteOffset: byteOffset,
      byteLength: byteLength
    });
    controller._queueTotalSize += byteLength;
  }
  function ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(controller, pullIntoDescriptor) {
    var elementSize = pullIntoDescriptor.elementSize;
    var currentAlignedBytes = pullIntoDescriptor.bytesFilled - pullIntoDescriptor.bytesFilled % elementSize;
    var maxBytesToCopy = Math.min(controller._queueTotalSize, pullIntoDescriptor.byteLength - pullIntoDescriptor.bytesFilled);
    var maxBytesFilled = pullIntoDescriptor.bytesFilled + maxBytesToCopy;
    var maxAlignedBytes = maxBytesFilled - maxBytesFilled % elementSize;
    var totalBytesToCopyRemaining = maxBytesToCopy;
    var ready = false;
    if (maxAlignedBytes > currentAlignedBytes) {
      totalBytesToCopyRemaining = maxAlignedBytes - pullIntoDescriptor.bytesFilled;
      ready = true;
    }
    var queue = controller._queue;
    while (totalBytesToCopyRemaining > 0) {
      var headOfQueue = queue[0];
      var bytesToCopy = Math.min(totalBytesToCopyRemaining, headOfQueue.byteLength);
      var destStart = pullIntoDescriptor.byteOffset + pullIntoDescriptor.bytesFilled;
      ArrayBufferCopy(pullIntoDescriptor.buffer, destStart, headOfQueue.buffer, headOfQueue.byteOffset, bytesToCopy);
      if (headOfQueue.byteLength === bytesToCopy) {
        queue.shift();
      } else {
        headOfQueue.byteOffset += bytesToCopy;
        headOfQueue.byteLength -= bytesToCopy;
      }
      controller._queueTotalSize -= bytesToCopy;
      ReadableByteStreamControllerFillHeadPullIntoDescriptor(controller, bytesToCopy, pullIntoDescriptor);
      totalBytesToCopyRemaining -= bytesToCopy;
    }
    if (ready === false) {
      assert(controller._queueTotalSize === 0, 'queue must be empty');
      assert(pullIntoDescriptor.bytesFilled > 0);
      assert(pullIntoDescriptor.bytesFilled < pullIntoDescriptor.elementSize);
    }
    return ready;
  }
  function ReadableByteStreamControllerFillHeadPullIntoDescriptor(controller, size, pullIntoDescriptor) {
    assert(controller._pendingPullIntos.length === 0 || controller._pendingPullIntos[0] === pullIntoDescriptor);
    ReadableByteStreamControllerInvalidateBYOBRequest(controller);
    pullIntoDescriptor.bytesFilled += size;
  }
  function ReadableByteStreamControllerHandleQueueDrain(controller) {
    assert(controller._controlledReadableStream._state === 'readable');
    if (controller._queueTotalSize === 0 && controller._closeRequested === true) {
      ReadableStreamClose(controller._controlledReadableStream);
    } else {
      ReadableByteStreamControllerCallPullIfNeeded(controller);
    }
  }
  function ReadableByteStreamControllerInvalidateBYOBRequest(controller) {
    if (controller._byobRequest === undefined) {
      return;
    }
    controller._byobRequest._associatedReadableByteStreamController = undefined;
    controller._byobRequest._view = undefined;
    controller._byobRequest = undefined;
  }
  function ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller) {
    assert(controller._closeRequested === false);
    while (controller._pendingPullIntos.length > 0) {
      if (controller._queueTotalSize === 0) {
        return;
      }
      var pullIntoDescriptor = controller._pendingPullIntos[0];
      if (ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(controller, pullIntoDescriptor) === true) {
        ReadableByteStreamControllerShiftPendingPullInto(controller);
        ReadableByteStreamControllerCommitPullIntoDescriptor(controller._controlledReadableStream, pullIntoDescriptor);
      }
    }
  }
  function ReadableByteStreamControllerPullInto(controller, view) {
    var stream = controller._controlledReadableStream;
    var elementSize = 1;
    if (view.constructor !== DataView) {
      elementSize = view.constructor.BYTES_PER_ELEMENT;
    }
    var ctor = view.constructor;
    var pullIntoDescriptor = {
      buffer: view.buffer,
      byteOffset: view.byteOffset,
      byteLength: view.byteLength,
      bytesFilled: 0,
      elementSize: elementSize,
      ctor: ctor,
      readerType: 'byob'
    };
    if (controller._pendingPullIntos.length > 0) {
      pullIntoDescriptor.buffer = TransferArrayBuffer(pullIntoDescriptor.buffer);
      controller._pendingPullIntos.push(pullIntoDescriptor);
      return ReadableStreamAddReadIntoRequest(stream);
    }
    if (stream._state === 'closed') {
      var emptyView = new view.constructor(pullIntoDescriptor.buffer, pullIntoDescriptor.byteOffset, 0);
      return Promise.resolve(CreateIterResultObject(emptyView, true));
    }
    if (controller._queueTotalSize > 0) {
      if (ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(controller, pullIntoDescriptor) === true) {
        var filledView = ReadableByteStreamControllerConvertPullIntoDescriptor(pullIntoDescriptor);
        ReadableByteStreamControllerHandleQueueDrain(controller);
        return Promise.resolve(CreateIterResultObject(filledView, false));
      }
      if (controller._closeRequested === true) {
        var e = new TypeError('Insufficient bytes to fill elements in the given buffer');
        ReadableByteStreamControllerError(controller, e);
        return Promise.reject(e);
      }
    }
    pullIntoDescriptor.buffer = TransferArrayBuffer(pullIntoDescriptor.buffer);
    controller._pendingPullIntos.push(pullIntoDescriptor);
    var promise = ReadableStreamAddReadIntoRequest(stream);
    ReadableByteStreamControllerCallPullIfNeeded(controller);
    return promise;
  }
  function ReadableByteStreamControllerRespondInClosedState(controller, firstDescriptor) {
    firstDescriptor.buffer = TransferArrayBuffer(firstDescriptor.buffer);
    assert(firstDescriptor.bytesFilled === 0, 'bytesFilled must be 0');
    var stream = controller._controlledReadableStream;
    if (ReadableStreamHasBYOBReader(stream) === true) {
      while (ReadableStreamGetNumReadIntoRequests(stream) > 0) {
        var pullIntoDescriptor = ReadableByteStreamControllerShiftPendingPullInto(controller);
        ReadableByteStreamControllerCommitPullIntoDescriptor(stream, pullIntoDescriptor);
      }
    }
  }
  function ReadableByteStreamControllerRespondInReadableState(controller, bytesWritten, pullIntoDescriptor) {
    if (pullIntoDescriptor.bytesFilled + bytesWritten > pullIntoDescriptor.byteLength) {
      throw new RangeError('bytesWritten out of range');
    }
    ReadableByteStreamControllerFillHeadPullIntoDescriptor(controller, bytesWritten, pullIntoDescriptor);
    if (pullIntoDescriptor.bytesFilled < pullIntoDescriptor.elementSize) {
      return;
    }
    ReadableByteStreamControllerShiftPendingPullInto(controller);
    var remainderSize = pullIntoDescriptor.bytesFilled % pullIntoDescriptor.elementSize;
    if (remainderSize > 0) {
      var end = pullIntoDescriptor.byteOffset + pullIntoDescriptor.bytesFilled;
      var remainder = pullIntoDescriptor.buffer.slice(end - remainderSize, end);
      ReadableByteStreamControllerEnqueueChunkToQueue(controller, remainder, 0, remainder.byteLength);
    }
    pullIntoDescriptor.buffer = TransferArrayBuffer(pullIntoDescriptor.buffer);
    pullIntoDescriptor.bytesFilled -= remainderSize;
    ReadableByteStreamControllerCommitPullIntoDescriptor(controller._controlledReadableStream, pullIntoDescriptor);
    ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller);
  }
  function ReadableByteStreamControllerRespondInternal(controller, bytesWritten) {
    var firstDescriptor = controller._pendingPullIntos[0];
    var stream = controller._controlledReadableStream;
    if (stream._state === 'closed') {
      if (bytesWritten !== 0) {
        throw new TypeError('bytesWritten must be 0 when calling respond() on a closed stream');
      }
      ReadableByteStreamControllerRespondInClosedState(controller, firstDescriptor);
    } else {
      assert(stream._state === 'readable');
      ReadableByteStreamControllerRespondInReadableState(controller, bytesWritten, firstDescriptor);
    }
  }
  function ReadableByteStreamControllerShiftPendingPullInto(controller) {
    var descriptor = controller._pendingPullIntos.shift();
    ReadableByteStreamControllerInvalidateBYOBRequest(controller);
    return descriptor;
  }
  function ReadableByteStreamControllerShouldCallPull(controller) {
    var stream = controller._controlledReadableStream;
    if (stream._state !== 'readable') {
      return false;
    }
    if (controller._closeRequested === true) {
      return false;
    }
    if (controller._started === false) {
      return false;
    }
    if (ReadableStreamHasDefaultReader(stream) === true && ReadableStreamGetNumReadRequests(stream) > 0) {
      return true;
    }
    if (ReadableStreamHasBYOBReader(stream) === true && ReadableStreamGetNumReadIntoRequests(stream) > 0) {
      return true;
    }
    if (ReadableByteStreamControllerGetDesiredSize(controller) > 0) {
      return true;
    }
    return false;
  }
  function ReadableByteStreamControllerClose(controller) {
    var stream = controller._controlledReadableStream;
    assert(controller._closeRequested === false);
    assert(stream._state === 'readable');
    if (controller._queueTotalSize > 0) {
      controller._closeRequested = true;
      return;
    }
    if (controller._pendingPullIntos.length > 0) {
      var firstPendingPullInto = controller._pendingPullIntos[0];
      if (firstPendingPullInto.bytesFilled > 0) {
        var e = new TypeError('Insufficient bytes to fill elements in the given buffer');
        ReadableByteStreamControllerError(controller, e);
        throw e;
      }
    }
    ReadableStreamClose(stream);
  }
  function ReadableByteStreamControllerEnqueue(controller, chunk) {
    var stream = controller._controlledReadableStream;
    assert(controller._closeRequested === false);
    assert(stream._state === 'readable');
    var buffer = chunk.buffer;
    var byteOffset = chunk.byteOffset;
    var byteLength = chunk.byteLength;
    var transferredBuffer = TransferArrayBuffer(buffer);
    if (ReadableStreamHasDefaultReader(stream) === true) {
      if (ReadableStreamGetNumReadRequests(stream) === 0) {
        ReadableByteStreamControllerEnqueueChunkToQueue(controller, transferredBuffer, byteOffset, byteLength);
      } else {
        assert(controller._queue.length === 0);
        var transferredView = new Uint8Array(transferredBuffer, byteOffset, byteLength);
        ReadableStreamFulfillReadRequest(stream, transferredView, false);
      }
    } else if (ReadableStreamHasBYOBReader(stream) === true) {
      ReadableByteStreamControllerEnqueueChunkToQueue(controller, transferredBuffer, byteOffset, byteLength);
      ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller);
    } else {
      assert(IsReadableStreamLocked(stream) === false, 'stream must not be locked');
      ReadableByteStreamControllerEnqueueChunkToQueue(controller, transferredBuffer, byteOffset, byteLength);
    }
  }
  function ReadableByteStreamControllerError(controller, e) {
    var stream = controller._controlledReadableStream;
    assert(stream._state === 'readable');
    ReadableByteStreamControllerClearPendingPullIntos(controller);
    ResetQueue(controller);
    ReadableStreamError(stream, e);
  }
  function ReadableByteStreamControllerGetDesiredSize(controller) {
    var stream = controller._controlledReadableStream;
    var state = stream._state;
    if (state === 'errored') {
      return null;
    }
    if (state === 'closed') {
      return 0;
    }
    return controller._strategyHWM - controller._queueTotalSize;
  }
  function ReadableByteStreamControllerRespond(controller, bytesWritten) {
    bytesWritten = Number(bytesWritten);
    if (IsFiniteNonNegativeNumber(bytesWritten) === false) {
      throw new RangeError('bytesWritten must be a finite');
    }
    assert(controller._pendingPullIntos.length > 0);
    ReadableByteStreamControllerRespondInternal(controller, bytesWritten);
  }
  function ReadableByteStreamControllerRespondWithNewView(controller, view) {
    assert(controller._pendingPullIntos.length > 0);
    var firstDescriptor = controller._pendingPullIntos[0];
    if (firstDescriptor.byteOffset + firstDescriptor.bytesFilled !== view.byteOffset) {
      throw new RangeError('The region specified by view does not match byobRequest');
    }
    if (firstDescriptor.byteLength !== view.byteLength) {
      throw new RangeError('The buffer of view has different capacity than byobRequest');
    }
    firstDescriptor.buffer = view.buffer;
    ReadableByteStreamControllerRespondInternal(controller, view.byteLength);
  }
  function streamBrandCheckException(name) {
    return new TypeError('ReadableStream.prototype.' + name + ' can only be used on a ReadableStream');
  }
  function readerLockException(name) {
    return new TypeError('Cannot ' + name + ' a stream using a released reader');
  }
  function defaultReaderBrandCheckException(name) {
    return new TypeError('ReadableStreamDefaultReader.prototype.' + name + ' can only be used on a ReadableStreamDefaultReader');
  }
  function defaultReaderClosedPromiseInitialize(reader) {
    reader._closedPromise = new Promise(function (resolve, reject) {
      reader._closedPromise_resolve = resolve;
      reader._closedPromise_reject = reject;
    });
  }
  function defaultReaderClosedPromiseInitializeAsRejected(reader, reason) {
    reader._closedPromise = Promise.reject(reason);
    reader._closedPromise_resolve = undefined;
    reader._closedPromise_reject = undefined;
  }
  function defaultReaderClosedPromiseInitializeAsResolved(reader) {
    reader._closedPromise = Promise.resolve(undefined);
    reader._closedPromise_resolve = undefined;
    reader._closedPromise_reject = undefined;
  }
  function defaultReaderClosedPromiseReject(reader, reason) {
    assert(reader._closedPromise_resolve !== undefined);
    assert(reader._closedPromise_reject !== undefined);
    reader._closedPromise_reject(reason);
    reader._closedPromise_resolve = undefined;
    reader._closedPromise_reject = undefined;
  }
  function defaultReaderClosedPromiseResetToRejected(reader, reason) {
    assert(reader._closedPromise_resolve === undefined);
    assert(reader._closedPromise_reject === undefined);
    reader._closedPromise = Promise.reject(reason);
  }
  function defaultReaderClosedPromiseResolve(reader) {
    assert(reader._closedPromise_resolve !== undefined);
    assert(reader._closedPromise_reject !== undefined);
    reader._closedPromise_resolve(undefined);
    reader._closedPromise_resolve = undefined;
    reader._closedPromise_reject = undefined;
  }
  function byobReaderBrandCheckException(name) {
    return new TypeError('ReadableStreamBYOBReader.prototype.' + name + ' can only be used on a ReadableStreamBYOBReader');
  }
  function defaultControllerBrandCheckException(name) {
    return new TypeError('ReadableStreamDefaultController.prototype.' + name + ' can only be used on a ReadableStreamDefaultController');
  }
  function byobRequestBrandCheckException(name) {
    return new TypeError('ReadableStreamBYOBRequest.prototype.' + name + ' can only be used on a ReadableStreamBYOBRequest');
  }
  function byteStreamControllerBrandCheckException(name) {
    return new TypeError('ReadableByteStreamController.prototype.' + name + ' can only be used on a ReadableByteStreamController');
  }
  function ifIsObjectAndHasAPromiseIsHandledInternalSlotSetPromiseIsHandledToTrue(promise) {
    try {
      Promise.prototype.then.call(promise, undefined, function () {});
    } catch (e) {}
  }
}, function (module, exports, __w_pdfjs_require__) {
  "use strict";

  var transformStream = __w_pdfjs_require__(6);
  var readableStream = __w_pdfjs_require__(4);
  var writableStream = __w_pdfjs_require__(2);
  exports.TransformStream = transformStream.TransformStream;
  exports.ReadableStream = readableStream.ReadableStream;
  exports.IsReadableStreamDisturbed = readableStream.IsReadableStreamDisturbed;
  exports.ReadableStreamDefaultControllerClose = readableStream.ReadableStreamDefaultControllerClose;
  exports.ReadableStreamDefaultControllerEnqueue = readableStream.ReadableStreamDefaultControllerEnqueue;
  exports.ReadableStreamDefaultControllerError = readableStream.ReadableStreamDefaultControllerError;
  exports.ReadableStreamDefaultControllerGetDesiredSize = readableStream.ReadableStreamDefaultControllerGetDesiredSize;
  exports.AcquireWritableStreamDefaultWriter = writableStream.AcquireWritableStreamDefaultWriter;
  exports.IsWritableStream = writableStream.IsWritableStream;
  exports.IsWritableStreamLocked = writableStream.IsWritableStreamLocked;
  exports.WritableStream = writableStream.WritableStream;
  exports.WritableStreamAbort = writableStream.WritableStreamAbort;
  exports.WritableStreamDefaultControllerError = writableStream.WritableStreamDefaultControllerError;
  exports.WritableStreamDefaultWriterCloseWithErrorPropagation = writableStream.WritableStreamDefaultWriterCloseWithErrorPropagation;
  exports.WritableStreamDefaultWriterRelease = writableStream.WritableStreamDefaultWriterRelease;
  exports.WritableStreamDefaultWriterWrite = writableStream.WritableStreamDefaultWriterWrite;
}, function (module, exports, __w_pdfjs_require__) {
  "use strict";

  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");
    }
  }
  var _require = __w_pdfjs_require__(1),
      assert = _require.assert;
  var _require2 = __w_pdfjs_require__(0),
      InvokeOrNoop = _require2.InvokeOrNoop,
      PromiseInvokeOrPerformFallback = _require2.PromiseInvokeOrPerformFallback,
      PromiseInvokeOrNoop = _require2.PromiseInvokeOrNoop,
      typeIsObject = _require2.typeIsObject;
  var _require3 = __w_pdfjs_require__(4),
      ReadableStream = _require3.ReadableStream,
      ReadableStreamDefaultControllerClose = _require3.ReadableStreamDefaultControllerClose,
      ReadableStreamDefaultControllerEnqueue = _require3.ReadableStreamDefaultControllerEnqueue,
      ReadableStreamDefaultControllerError = _require3.ReadableStreamDefaultControllerError,
      ReadableStreamDefaultControllerGetDesiredSize = _require3.ReadableStreamDefaultControllerGetDesiredSize;
  var _require4 = __w_pdfjs_require__(2),
      WritableStream = _require4.WritableStream,
      WritableStreamDefaultControllerError = _require4.WritableStreamDefaultControllerError;
  function TransformStreamCloseReadable(transformStream) {
    if (transformStream._errored === true) {
      throw new TypeError('TransformStream is already errored');
    }
    if (transformStream._readableClosed === true) {
      throw new TypeError('Readable side is already closed');
    }
    TransformStreamCloseReadableInternal(transformStream);
  }
  function TransformStreamEnqueueToReadable(transformStream, chunk) {
    if (transformStream._errored === true) {
      throw new TypeError('TransformStream is already errored');
    }
    if (transformStream._readableClosed === true) {
      throw new TypeError('Readable side is already closed');
    }
    var controller = transformStream._readableController;
    try {
      ReadableStreamDefaultControllerEnqueue(controller, chunk);
    } catch (e) {
      transformStream._readableClosed = true;
      TransformStreamErrorIfNeeded(transformStream, e);
      throw transformStream._storedError;
    }
    var desiredSize = ReadableStreamDefaultControllerGetDesiredSize(controller);
    var maybeBackpressure = desiredSize <= 0;
    if (maybeBackpressure === true && transformStream._backpressure === false) {
      TransformStreamSetBackpressure(transformStream, true);
    }
  }
  function TransformStreamError(transformStream, e) {
    if (transformStream._errored === true) {
      throw new TypeError('TransformStream is already errored');
    }
    TransformStreamErrorInternal(transformStream, e);
  }
  function TransformStreamCloseReadableInternal(transformStream) {
    assert(transformStream._errored === false);
    assert(transformStream._readableClosed === false);
    try {
      ReadableStreamDefaultControllerClose(transformStream._readableController);
    } catch (e) {
      assert(false);
    }
    transformStream._readableClosed = true;
  }
  function TransformStreamErrorIfNeeded(transformStream, e) {
    if (transformStream._errored === false) {
      TransformStreamErrorInternal(transformStream, e);
    }
  }
  function TransformStreamErrorInternal(transformStream, e) {
    assert(transformStream._errored === false);
    transformStream._errored = true;
    transformStream._storedError = e;
    if (transformStream._writableDone === false) {
      WritableStreamDefaultControllerError(transformStream._writableController, e);
    }
    if (transformStream._readableClosed === false) {
      ReadableStreamDefaultControllerError(transformStream._readableController, e);
    }
  }
  function TransformStreamReadableReadyPromise(transformStream) {
    assert(transformStream._backpressureChangePromise !== undefined, '_backpressureChangePromise should have been initialized');
    if (transformStream._backpressure === false) {
      return Promise.resolve();
    }
    assert(transformStream._backpressure === true, '_backpressure should have been initialized');
    return transformStream._backpressureChangePromise;
  }
  function TransformStreamSetBackpressure(transformStream, backpressure) {
    assert(transformStream._backpressure !== backpressure, 'TransformStreamSetBackpressure() should be called only when backpressure is changed');
    if (transformStream._backpressureChangePromise !== undefined) {
      transformStream._backpressureChangePromise_resolve(backpressure);
    }
    transformStream._backpressureChangePromise = new Promise(function (resolve) {
      transformStream._backpressureChangePromise_resolve = resolve;
    });
    transformStream._backpressureChangePromise.then(function (resolution) {
      assert(resolution !== backpressure, '_backpressureChangePromise should be fulfilled only when backpressure is changed');
    });
    transformStream._backpressure = backpressure;
  }
  function TransformStreamDefaultTransform(chunk, transformStreamController) {
    var transformStream = transformStreamController._controlledTransformStream;
    TransformStreamEnqueueToReadable(transformStream, chunk);
    return Promise.resolve();
  }
  function TransformStreamTransform(transformStream, chunk) {
    assert(transformStream._errored === false);
    assert(transformStream._transforming === false);
    assert(transformStream._backpressure === false);
    transformStream._transforming = true;
    var transformer = transformStream._transformer;
    var controller = transformStream._transformStreamController;
    var transformPromise = PromiseInvokeOrPerformFallback(transformer, 'transform', [chunk, controller], TransformStreamDefaultTransform, [chunk, controller]);
    return transformPromise.then(function () {
      transformStream._transforming = false;
      return TransformStreamReadableReadyPromise(transformStream);
    }, function (e) {
      TransformStreamErrorIfNeeded(transformStream, e);
      return Promise.reject(e);
    });
  }
  function IsTransformStreamDefaultController(x) {
    if (!typeIsObject(x)) {
      return false;
    }
    if (!Object.prototype.hasOwnProperty.call(x, '_controlledTransformStream')) {
      return false;
    }
    return true;
  }
  function IsTransformStream(x) {
    if (!typeIsObject(x)) {
      return false;
    }
    if (!Object.prototype.hasOwnProperty.call(x, '_transformStreamController')) {
      return false;
    }
    return true;
  }
  var TransformStreamSink = function () {
    function TransformStreamSink(transformStream, startPromise) {
      _classCallCheck(this, TransformStreamSink);
      this._transformStream = transformStream;
      this._startPromise = startPromise;
    }
    _createClass(TransformStreamSink, [{
      key: 'start',
      value: function start(c) {
        var transformStream = this._transformStream;
        transformStream._writableController = c;
        return this._startPromise.then(function () {
          return TransformStreamReadableReadyPromise(transformStream);
        });
      }
    }, {
      key: 'write',
      value: function write(chunk) {
        var transformStream = this._transformStream;
        return TransformStreamTransform(transformStream, chunk);
      }
    }, {
      key: 'abort',
      value: function abort() {
        var transformStream = this._transformStream;
        transformStream._writableDone = true;
        TransformStreamErrorInternal(transformStream, new TypeError('Writable side aborted'));
      }
    }, {
      key: 'close',
      value: function close() {
        var transformStream = this._transformStream;
        assert(transformStream._transforming === false);
        transformStream._writableDone = true;
        var flushPromise = PromiseInvokeOrNoop(transformStream._transformer, 'flush', [transformStream._transformStreamController]);
        return flushPromise.then(function () {
          if (transformStream._errored === true) {
            return Promise.reject(transformStream._storedError);
          }
          if (transformStream._readableClosed === false) {
            TransformStreamCloseReadableInternal(transformStream);
          }
          return Promise.resolve();
        }).catch(function (r) {
          TransformStreamErrorIfNeeded(transformStream, r);
          return Promise.reject(transformStream._storedError);
        });
      }
    }]);
    return TransformStreamSink;
  }();
  var TransformStreamSource = function () {
    function TransformStreamSource(transformStream, startPromise) {
      _classCallCheck(this, TransformStreamSource);
      this._transformStream = transformStream;
      this._startPromise = startPromise;
    }
    _createClass(TransformStreamSource, [{
      key: 'start',
      value: function start(c) {
        var transformStream = this._transformStream;
        transformStream._readableController = c;
        return this._startPromise.then(function () {
          assert(transformStream._backpressureChangePromise !== undefined, '_backpressureChangePromise should have been initialized');
          if (transformStream._backpressure === true) {
            return Promise.resolve();
          }
          assert(transformStream._backpressure === false, '_backpressure should have been initialized');
          return transformStream._backpressureChangePromise;
        });
      }
    }, {
      key: 'pull',
      value: function pull() {
        var transformStream = this._transformStream;
        assert(transformStream._backpressure === true, 'pull() should be never called while _backpressure is false');
        assert(transformStream._backpressureChangePromise !== undefined, '_backpressureChangePromise should have been initialized');
        TransformStreamSetBackpressure(transformStream, false);
        return transformStream._backpressureChangePromise;
      }
    }, {
      key: 'cancel',
      value: function cancel() {
        var transformStream = this._transformStream;
        transformStream._readableClosed = true;
        TransformStreamErrorInternal(transformStream, new TypeError('Readable side canceled'));
      }
    }]);
    return TransformStreamSource;
  }();
  var TransformStreamDefaultController = function () {
    function TransformStreamDefaultController(transformStream) {
      _classCallCheck(this, TransformStreamDefaultController);
      if (IsTransformStream(transformStream) === false) {
        throw new TypeError('TransformStreamDefaultController can only be ' + 'constructed with a TransformStream instance');
      }
      if (transformStream._transformStreamController !== undefined) {
        throw new TypeError('TransformStreamDefaultController instances can ' + 'only be created by the TransformStream constructor');
      }
      this._controlledTransformStream = transformStream;
    }
    _createClass(TransformStreamDefaultController, [{
      key: 'enqueue',
      value: function enqueue(chunk) {
        if (IsTransformStreamDefaultController(this) === false) {
          throw defaultControllerBrandCheckException('enqueue');
        }
        TransformStreamEnqueueToReadable(this._controlledTransformStream, chunk);
      }
    }, {
      key: 'close',
      value: function close() {
        if (IsTransformStreamDefaultController(this) === false) {
          throw defaultControllerBrandCheckException('close');
        }
        TransformStreamCloseReadable(this._controlledTransformStream);
      }
    }, {
      key: 'error',
      value: function error(reason) {
        if (IsTransformStreamDefaultController(this) === false) {
          throw defaultControllerBrandCheckException('error');
        }
        TransformStreamError(this._controlledTransformStream, reason);
      }
    }, {
      key: 'desiredSize',
      get: function get() {
        if (IsTransformStreamDefaultController(this) === false) {
          throw defaultControllerBrandCheckException('desiredSize');
        }
        var transformStream = this._controlledTransformStream;
        var readableController = transformStream._readableController;
        return ReadableStreamDefaultControllerGetDesiredSize(readableController);
      }
    }]);
    return TransformStreamDefaultController;
  }();
  var TransformStream = function () {
    function TransformStream() {
      var transformer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      _classCallCheck(this, TransformStream);
      this._transformer = transformer;
      var readableStrategy = transformer.readableStrategy,
          writableStrategy = transformer.writableStrategy;
      this._transforming = false;
      this._errored = false;
      this._storedError = undefined;
      this._writableController = undefined;
      this._readableController = undefined;
      this._transformStreamController = undefined;
      this._writableDone = false;
      this._readableClosed = false;
      this._backpressure = undefined;
      this._backpressureChangePromise = undefined;
      this._backpressureChangePromise_resolve = undefined;
      this._transformStreamController = new TransformStreamDefaultController(this);
      var startPromise_resolve = void 0;
      var startPromise = new Promise(function (resolve) {
        startPromise_resolve = resolve;
      });
      var source = new TransformStreamSource(this, startPromise);
      this._readable = new ReadableStream(source, readableStrategy);
      var sink = new TransformStreamSink(this, startPromise);
      this._writable = new WritableStream(sink, writableStrategy);
      assert(this._writableController !== undefined);
      assert(this._readableController !== undefined);
      var desiredSize = ReadableStreamDefaultControllerGetDesiredSize(this._readableController);
      TransformStreamSetBackpressure(this, desiredSize <= 0);
      var transformStream = this;
      var startResult = InvokeOrNoop(transformer, 'start', [transformStream._transformStreamController]);
      startPromise_resolve(startResult);
      startPromise.catch(function (e) {
        if (transformStream._errored === false) {
          transformStream._errored = true;
          transformStream._storedError = e;
        }
      });
    }
    _createClass(TransformStream, [{
      key: 'readable',
      get: function get() {
        if (IsTransformStream(this) === false) {
          throw streamBrandCheckException('readable');
        }
        return this._readable;
      }
    }, {
      key: 'writable',
      get: function get() {
        if (IsTransformStream(this) === false) {
          throw streamBrandCheckException('writable');
        }
        return this._writable;
      }
    }]);
    return TransformStream;
  }();
  module.exports = { TransformStream: TransformStream };
  function defaultControllerBrandCheckException(name) {
    return new TypeError('TransformStreamDefaultController.prototype.' + name + ' can only be used on a TransformStreamDefaultController');
  }
  function streamBrandCheckException(name) {
    return new TypeError('TransformStream.prototype.' + name + ' can only be used on a TransformStream');
  }
}, function (module, exports, __w_pdfjs_require__) {
  module.exports = __w_pdfjs_require__(5);
}]));

/***/ }),
/* 24 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.NetworkPdfManager = exports.LocalPdfManager = undefined;

var _util = __w_pdfjs_require__(0);

var _chunked_stream = __w_pdfjs_require__(9);

var _document = __w_pdfjs_require__(25);

var _stream = __w_pdfjs_require__(2);

var BasePdfManager = function BasePdfManagerClosure() {
  function BasePdfManager() {
    (0, _util.unreachable)('Cannot initialize BaseManagerManager');
  }
  BasePdfManager.prototype = {
    get docId() {
      return this._docId;
    },
    get password() {
      return this._password;
    },
    get docBaseUrl() {
      var docBaseUrl = null;
      if (this._docBaseUrl) {
        var absoluteUrl = (0, _util.createValidAbsoluteUrl)(this._docBaseUrl);
        if (absoluteUrl) {
          docBaseUrl = absoluteUrl.href;
        } else {
          (0, _util.warn)('Invalid absolute docBaseUrl: "' + this._docBaseUrl + '".');
        }
      }
      return (0, _util.shadow)(this, 'docBaseUrl', docBaseUrl);
    },
    onLoadedStream: function BasePdfManager_onLoadedStream() {
      throw new _util.NotImplementedException();
    },
    ensureDoc: function BasePdfManager_ensureDoc(prop, args) {
      return this.ensure(this.pdfDocument, prop, args);
    },
    ensureXRef: function BasePdfManager_ensureXRef(prop, args) {
      return this.ensure(this.pdfDocument.xref, prop, args);
    },
    ensureCatalog: function BasePdfManager_ensureCatalog(prop, args) {
      return this.ensure(this.pdfDocument.catalog, prop, args);
    },
    getPage: function BasePdfManager_getPage(pageIndex) {
      return this.pdfDocument.getPage(pageIndex);
    },
    cleanup: function BasePdfManager_cleanup() {
      return this.pdfDocument.cleanup();
    },
    ensure: function BasePdfManager_ensure(obj, prop, args) {
      return new _util.NotImplementedException();
    },
    requestRange: function BasePdfManager_requestRange(begin, end) {
      return new _util.NotImplementedException();
    },
    requestLoadedStream: function BasePdfManager_requestLoadedStream() {
      return new _util.NotImplementedException();
    },
    sendProgressiveData: function BasePdfManager_sendProgressiveData(chunk) {
      return new _util.NotImplementedException();
    },
    updatePassword: function BasePdfManager_updatePassword(password) {
      this._password = password;
    },
    terminate: function BasePdfManager_terminate() {
      return new _util.NotImplementedException();
    }
  };
  return BasePdfManager;
}();
var LocalPdfManager = function LocalPdfManagerClosure() {
  function LocalPdfManager(docId, data, password, evaluatorOptions, docBaseUrl) {
    this._docId = docId;
    this._password = password;
    this._docBaseUrl = docBaseUrl;
    this.evaluatorOptions = evaluatorOptions;
    var stream = new _stream.Stream(data);
    this.pdfDocument = new _document.PDFDocument(this, stream);
    this._loadedStreamCapability = (0, _util.createPromiseCapability)();
    this._loadedStreamCapability.resolve(stream);
  }
  _util.Util.inherit(LocalPdfManager, BasePdfManager, {
    ensure: function LocalPdfManager_ensure(obj, prop, args) {
      return new Promise(function (resolve, reject) {
        try {
          var value = obj[prop];
          var result;
          if (typeof value === 'function') {
            result = value.apply(obj, args);
          } else {
            result = value;
          }
          resolve(result);
        } catch (e) {
          reject(e);
        }
      });
    },
    requestRange: function LocalPdfManager_requestRange(begin, end) {
      return Promise.resolve();
    },
    requestLoadedStream: function LocalPdfManager_requestLoadedStream() {},
    onLoadedStream: function LocalPdfManager_onLoadedStream() {
      return this._loadedStreamCapability.promise;
    },
    terminate: function LocalPdfManager_terminate() {}
  });
  return LocalPdfManager;
}();
var NetworkPdfManager = function NetworkPdfManagerClosure() {
  function NetworkPdfManager(docId, pdfNetworkStream, args, evaluatorOptions, docBaseUrl) {
    this._docId = docId;
    this._password = args.password;
    this._docBaseUrl = docBaseUrl;
    this.msgHandler = args.msgHandler;
    this.evaluatorOptions = evaluatorOptions;
    var params = {
      msgHandler: args.msgHandler,
      url: args.url,
      length: args.length,
      disableAutoFetch: args.disableAutoFetch,
      rangeChunkSize: args.rangeChunkSize
    };
    this.streamManager = new _chunked_stream.ChunkedStreamManager(pdfNetworkStream, params);
    this.pdfDocument = new _document.PDFDocument(this, this.streamManager.getStream());
  }
  _util.Util.inherit(NetworkPdfManager, BasePdfManager, {
    ensure: function NetworkPdfManager_ensure(obj, prop, args) {
      var pdfManager = this;
      return new Promise(function (resolve, reject) {
        function ensureHelper() {
          try {
            var result;
            var value = obj[prop];
            if (typeof value === 'function') {
              result = value.apply(obj, args);
            } else {
              result = value;
            }
            resolve(result);
          } catch (e) {
            if (!(e instanceof _util.MissingDataException)) {
              reject(e);
              return;
            }
            pdfManager.streamManager.requestRange(e.begin, e.end).then(ensureHelper, reject);
          }
        }
        ensureHelper();
      });
    },
    requestRange: function NetworkPdfManager_requestRange(begin, end) {
      return this.streamManager.requestRange(begin, end);
    },
    requestLoadedStream: function NetworkPdfManager_requestLoadedStream() {
      this.streamManager.requestAllChunks();
    },
    sendProgressiveData: function NetworkPdfManager_sendProgressiveData(chunk) {
      this.streamManager.onReceiveData({ chunk });
    },
    onLoadedStream: function NetworkPdfManager_onLoadedStream() {
      return this.streamManager.onLoadedStream();
    },
    terminate: function NetworkPdfManager_terminate() {
      this.streamManager.abort();
    }
  });
  return NetworkPdfManager;
}();
exports.LocalPdfManager = LocalPdfManager;
exports.NetworkPdfManager = NetworkPdfManager;

/***/ }),
/* 25 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.PDFDocument = exports.Page = undefined;

var _obj = __w_pdfjs_require__(10);

var _primitives = __w_pdfjs_require__(1);

var _util = __w_pdfjs_require__(0);

var _stream = __w_pdfjs_require__(2);

var _annotation = __w_pdfjs_require__(31);

var _crypto = __w_pdfjs_require__(14);

var _parser = __w_pdfjs_require__(5);

var _operator_list = __w_pdfjs_require__(7);

var _evaluator = __w_pdfjs_require__(32);

var _function = __w_pdfjs_require__(18);

var Page = function PageClosure() {
  var DEFAULT_USER_UNIT = 1.0;
  var LETTER_SIZE_MEDIABOX = [0, 0, 612, 792];
  function isAnnotationRenderable(annotation, intent) {
    return intent === 'display' && annotation.viewable || intent === 'print' && annotation.printable;
  }
  function Page({ pdfManager, xref, pageIndex, pageDict, ref, fontCache, builtInCMapCache, pdfFunctionFactory }) {
    this.pdfManager = pdfManager;
    this.pageIndex = pageIndex;
    this.pageDict = pageDict;
    this.xref = xref;
    this.ref = ref;
    this.fontCache = fontCache;
    this.builtInCMapCache = builtInCMapCache;
    this.pdfFunctionFactory = pdfFunctionFactory;
    this.evaluatorOptions = pdfManager.evaluatorOptions;
    this.resourcesPromise = null;
    var uniquePrefix = 'p' + this.pageIndex + '_';
    var idCounters = { obj: 0 };
    this.idFactory = {
      createObjId() {
        return uniquePrefix + ++idCounters.obj;
      }
    };
  }
  Page.prototype = {
    getPageProp: function Page_getPageProp(key) {
      return this.pageDict.get(key);
    },
    getInheritedPageProp: function Page_getInheritedPageProp(key, getArray) {
      var dict = this.pageDict,
          valueArray = null,
          loopCount = 0;
      var MAX_LOOP_COUNT = 100;
      getArray = getArray || false;
      while (dict) {
        var value = getArray ? dict.getArray(key) : dict.get(key);
        if (value !== undefined) {
          if (!valueArray) {
            valueArray = [];
          }
          valueArray.push(value);
        }
        if (++loopCount > MAX_LOOP_COUNT) {
          (0, _util.warn)('getInheritedPageProp: maximum loop count exceeded for ' + key);
          return valueArray ? valueArray[0] : undefined;
        }
        dict = dict.get('Parent');
      }
      if (!valueArray) {
        return undefined;
      }
      if (valueArray.length === 1 || !(0, _primitives.isDict)(valueArray[0])) {
        return valueArray[0];
      }
      return _primitives.Dict.merge(this.xref, valueArray);
    },
    get content() {
      return this.getPageProp('Contents');
    },
    get resources() {
      return (0, _util.shadow)(this, 'resources', this.getInheritedPageProp('Resources') || _primitives.Dict.empty);
    },
    get mediaBox() {
      var mediaBox = this.getInheritedPageProp('MediaBox', true);
      if (!Array.isArray(mediaBox) || mediaBox.length !== 4) {
        return (0, _util.shadow)(this, 'mediaBox', LETTER_SIZE_MEDIABOX);
      }
      return (0, _util.shadow)(this, 'mediaBox', mediaBox);
    },
    get cropBox() {
      var cropBox = this.getInheritedPageProp('CropBox', true);
      if (!Array.isArray(cropBox) || cropBox.length !== 4) {
        return (0, _util.shadow)(this, 'cropBox', this.mediaBox);
      }
      return (0, _util.shadow)(this, 'cropBox', cropBox);
    },
    get userUnit() {
      var obj = this.getPageProp('UserUnit');
      if (!(0, _util.isNum)(obj) || obj <= 0) {
        obj = DEFAULT_USER_UNIT;
      }
      return (0, _util.shadow)(this, 'userUnit', obj);
    },
    get view() {
      var mediaBox = this.mediaBox,
          cropBox = this.cropBox;
      if (mediaBox === cropBox) {
        return (0, _util.shadow)(this, 'view', mediaBox);
      }
      var intersection = _util.Util.intersect(cropBox, mediaBox);
      return (0, _util.shadow)(this, 'view', intersection || mediaBox);
    },
    get rotate() {
      var rotate = this.getInheritedPageProp('Rotate') || 0;
      if (rotate % 90 !== 0) {
        rotate = 0;
      } else if (rotate >= 360) {
        rotate = rotate % 360;
      } else if (rotate < 0) {
        rotate = (rotate % 360 + 360) % 360;
      }
      return (0, _util.shadow)(this, 'rotate', rotate);
    },
    getContentStream: function Page_getContentStream() {
      var content = this.content;
      var stream;
      if (Array.isArray(content)) {
        var xref = this.xref;
        var i,
            n = content.length;
        var streams = [];
        for (i = 0; i < n; ++i) {
          streams.push(xref.fetchIfRef(content[i]));
        }
        stream = new _stream.StreamsSequenceStream(streams);
      } else if ((0, _primitives.isStream)(content)) {
        stream = content;
      } else {
        stream = new _stream.NullStream();
      }
      return stream;
    },
    loadResources: function Page_loadResources(keys) {
      if (!this.resourcesPromise) {
        this.resourcesPromise = this.pdfManager.ensure(this, 'resources');
      }
      return this.resourcesPromise.then(() => {
        let objectLoader = new _obj.ObjectLoader(this.resources, keys, this.xref);
        return objectLoader.load();
      });
    },
    getOperatorList({ handler, task, intent, renderInteractiveForms }) {
      var contentStreamPromise = this.pdfManager.ensure(this, 'getContentStream');
      var resourcesPromise = this.loadResources(['ExtGState', 'ColorSpace', 'Pattern', 'Shading', 'XObject', 'Font']);
      var partialEvaluator = new _evaluator.PartialEvaluator({
        pdfManager: this.pdfManager,
        xref: this.xref,
        handler,
        pageIndex: this.pageIndex,
        idFactory: this.idFactory,
        fontCache: this.fontCache,
        builtInCMapCache: this.builtInCMapCache,
        options: this.evaluatorOptions,
        pdfFunctionFactory: this.pdfFunctionFactory
      });
      var dataPromises = Promise.all([contentStreamPromise, resourcesPromise]);
      var pageListPromise = dataPromises.then(([contentStream]) => {
        var opList = new _operator_list.OperatorList(intent, handler, this.pageIndex);
        handler.send('StartRenderPage', {
          transparency: partialEvaluator.hasBlendModes(this.resources),
          pageIndex: this.pageIndex,
          intent
        });
        return partialEvaluator.getOperatorList({
          stream: contentStream,
          task,
          resources: this.resources,
          operatorList: opList
        }).then(function () {
          return opList;
        });
      });
      var annotationsPromise = this.pdfManager.ensure(this, 'annotations');
      return Promise.all([pageListPromise, annotationsPromise]).then(function ([pageOpList, annotations]) {
        if (annotations.length === 0) {
          pageOpList.flush(true);
          return pageOpList;
        }
        var i,
            ii,
            opListPromises = [];
        for (i = 0, ii = annotations.length; i < ii; i++) {
          if (isAnnotationRenderable(annotations[i], intent)) {
            opListPromises.push(annotations[i].getOperatorList(partialEvaluator, task, renderInteractiveForms));
          }
        }
        return Promise.all(opListPromises).then(function (opLists) {
          pageOpList.addOp(_util.OPS.beginAnnotations, []);
          for (i = 0, ii = opLists.length; i < ii; i++) {
            pageOpList.addOpList(opLists[i]);
          }
          pageOpList.addOp(_util.OPS.endAnnotations, []);
          pageOpList.flush(true);
          return pageOpList;
        });
      });
    },
    extractTextContent({ handler, task, normalizeWhitespace, sink, combineTextItems }) {
      var contentStreamPromise = this.pdfManager.ensure(this, 'getContentStream');
      var resourcesPromise = this.loadResources(['ExtGState', 'XObject', 'Font']);
      var dataPromises = Promise.all([contentStreamPromise, resourcesPromise]);
      return dataPromises.then(([contentStream]) => {
        var partialEvaluator = new _evaluator.PartialEvaluator({
          pdfManager: this.pdfManager,
          xref: this.xref,
          handler,
          pageIndex: this.pageIndex,
          idFactory: this.idFactory,
          fontCache: this.fontCache,
          builtInCMapCache: this.builtInCMapCache,
          options: this.evaluatorOptions,
          pdfFunctionFactory: this.pdfFunctionFactory
        });
        return partialEvaluator.getTextContent({
          stream: contentStream,
          task,
          resources: this.resources,
          normalizeWhitespace,
          combineTextItems,
          sink
        });
      });
    },
    getAnnotationsData: function Page_getAnnotationsData(intent) {
      var annotations = this.annotations;
      var annotationsData = [];
      for (var i = 0, n = annotations.length; i < n; ++i) {
        if (!intent || isAnnotationRenderable(annotations[i], intent)) {
          annotationsData.push(annotations[i].data);
        }
      }
      return annotationsData;
    },
    get annotations() {
      var annotations = [];
      var annotationRefs = this.getInheritedPageProp('Annots') || [];
      for (var i = 0, n = annotationRefs.length; i < n; ++i) {
        var annotationRef = annotationRefs[i];
        var annotation = _annotation.AnnotationFactory.create(this.xref, annotationRef, this.pdfManager, this.idFactory);
        if (annotation) {
          annotations.push(annotation);
        }
      }
      return (0, _util.shadow)(this, 'annotations', annotations);
    }
  };
  return Page;
}();
var PDFDocument = function PDFDocumentClosure() {
  var FINGERPRINT_FIRST_BYTES = 1024;
  var EMPTY_FINGERPRINT = '\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00';
  function PDFDocument(pdfManager, arg) {
    var stream;
    if ((0, _primitives.isStream)(arg)) {
      stream = arg;
    } else if ((0, _util.isArrayBuffer)(arg)) {
      stream = new _stream.Stream(arg);
    } else {
      throw new Error('PDFDocument: Unknown argument type');
    }
    if (stream.length <= 0) {
      throw new Error('PDFDocument: stream must have data');
    }
    this.pdfManager = pdfManager;
    this.stream = stream;
    this.xref = new _obj.XRef(stream, pdfManager);
    let evaluatorOptions = pdfManager.evaluatorOptions;
    this.pdfFunctionFactory = new _function.PDFFunctionFactory({
      xref: this.xref,
      isEvalSupported: evaluatorOptions.isEvalSupported
    });
  }
  function find(stream, needle, limit, backwards) {
    var pos = stream.pos;
    var end = stream.end;
    var strBuf = [];
    if (pos + limit > end) {
      limit = end - pos;
    }
    for (var n = 0; n < limit; ++n) {
      strBuf.push(String.fromCharCode(stream.getByte()));
    }
    var str = strBuf.join('');
    stream.pos = pos;
    var index = backwards ? str.lastIndexOf(needle) : str.indexOf(needle);
    if (index === -1) {
      return false;
    }
    stream.pos += index;
    return true;
  }
  var DocumentInfoValidators = {
    get entries() {
      return (0, _util.shadow)(this, 'entries', {
        Title: _util.isString,
        Author: _util.isString,
        Subject: _util.isString,
        Keywords: _util.isString,
        Creator: _util.isString,
        Producer: _util.isString,
        CreationDate: _util.isString,
        ModDate: _util.isString,
        Trapped: _primitives.isName
      });
    }
  };
  PDFDocument.prototype = {
    parse: function PDFDocument_parse(recoveryMode) {
      this.setup(recoveryMode);
      var version = this.catalog.catDict.get('Version');
      if ((0, _primitives.isName)(version)) {
        this.pdfFormatVersion = version.name;
      }
      try {
        this.acroForm = this.catalog.catDict.get('AcroForm');
        if (this.acroForm) {
          this.xfa = this.acroForm.get('XFA');
          var fields = this.acroForm.get('Fields');
          if ((!fields || !Array.isArray(fields) || fields.length === 0) && !this.xfa) {
            this.acroForm = null;
          }
        }
      } catch (ex) {
        if (ex instanceof _util.MissingDataException) {
          throw ex;
        }
        (0, _util.info)('Something wrong with AcroForm entry');
        this.acroForm = null;
      }
    },
    get linearization() {
      var linearization = null;
      if (this.stream.length) {
        try {
          linearization = _parser.Linearization.create(this.stream);
        } catch (err) {
          if (err instanceof _util.MissingDataException) {
            throw err;
          }
          (0, _util.info)(err);
        }
      }
      return (0, _util.shadow)(this, 'linearization', linearization);
    },
    get startXRef() {
      var stream = this.stream;
      var startXRef = 0;
      var linearization = this.linearization;
      if (linearization) {
        stream.reset();
        if (find(stream, 'endobj', 1024)) {
          startXRef = stream.pos + 6;
        }
      } else {
        var step = 1024;
        var found = false,
            pos = stream.end;
        while (!found && pos > 0) {
          pos -= step - 'startxref'.length;
          if (pos < 0) {
            pos = 0;
          }
          stream.pos = pos;
          found = find(stream, 'startxref', step, true);
        }
        if (found) {
          stream.skip(9);
          var ch;
          do {
            ch = stream.getByte();
          } while ((0, _util.isSpace)(ch));
          var str = '';
          while (ch >= 0x20 && ch <= 0x39) {
            str += String.fromCharCode(ch);
            ch = stream.getByte();
          }
          startXRef = parseInt(str, 10);
          if (isNaN(startXRef)) {
            startXRef = 0;
          }
        }
      }
      return (0, _util.shadow)(this, 'startXRef', startXRef);
    },
    get mainXRefEntriesOffset() {
      var mainXRefEntriesOffset = 0;
      var linearization = this.linearization;
      if (linearization) {
        mainXRefEntriesOffset = linearization.mainXRefEntriesOffset;
      }
      return (0, _util.shadow)(this, 'mainXRefEntriesOffset', mainXRefEntriesOffset);
    },
    checkHeader: function PDFDocument_checkHeader() {
      var stream = this.stream;
      stream.reset();
      if (find(stream, '%PDF-', 1024)) {
        stream.moveStart();
        var MAX_VERSION_LENGTH = 12;
        var version = '',
            ch;
        while ((ch = stream.getByte()) > 0x20) {
          if (version.length >= MAX_VERSION_LENGTH) {
            break;
          }
          version += String.fromCharCode(ch);
        }
        if (!this.pdfFormatVersion) {
          this.pdfFormatVersion = version.substring(5);
        }
        return;
      }
    },
    parseStartXRef: function PDFDocument_parseStartXRef() {
      var startXRef = this.startXRef;
      this.xref.setStartXRef(startXRef);
    },
    setup: function PDFDocument_setup(recoveryMode) {
      this.xref.parse(recoveryMode);
      var pageFactory = {
        createPage: (pageIndex, dict, ref, fontCache, builtInCMapCache) => {
          return new Page({
            pdfManager: this.pdfManager,
            xref: this.xref,
            pageIndex,
            pageDict: dict,
            ref,
            fontCache,
            builtInCMapCache,
            pdfFunctionFactory: this.pdfFunctionFactory
          });
        }
      };
      this.catalog = new _obj.Catalog(this.pdfManager, this.xref, pageFactory);
    },
    get numPages() {
      var linearization = this.linearization;
      var num = linearization ? linearization.numPages : this.catalog.numPages;
      return (0, _util.shadow)(this, 'numPages', num);
    },
    get documentInfo() {
      var docInfo = {
        PDFFormatVersion: this.pdfFormatVersion,
        IsAcroFormPresent: !!this.acroForm,
        IsXFAPresent: !!this.xfa
      };
      var infoDict;
      try {
        infoDict = this.xref.trailer.get('Info');
      } catch (err) {
        if (err instanceof _util.MissingDataException) {
          throw err;
        }
        (0, _util.info)('The document information dictionary is invalid.');
      }
      if (infoDict) {
        var validEntries = DocumentInfoValidators.entries;
        for (var key in validEntries) {
          if (infoDict.has(key)) {
            var value = infoDict.get(key);
            if (validEntries[key](value)) {
              docInfo[key] = typeof value !== 'string' ? value : (0, _util.stringToPDFString)(value);
            } else {
              (0, _util.info)('Bad value in document info for "' + key + '"');
            }
          }
        }
      }
      return (0, _util.shadow)(this, 'documentInfo', docInfo);
    },
    get fingerprint() {
      var xref = this.xref,
          hash,
          fileID = '';
      var idArray = xref.trailer.get('ID');
      if (Array.isArray(idArray) && idArray[0] && (0, _util.isString)(idArray[0]) && idArray[0] !== EMPTY_FINGERPRINT) {
        hash = (0, _util.stringToBytes)(idArray[0]);
      } else {
        if (this.stream.ensureRange) {
          this.stream.ensureRange(0, Math.min(FINGERPRINT_FIRST_BYTES, this.stream.end));
        }
        hash = (0, _crypto.calculateMD5)(this.stream.bytes.subarray(0, FINGERPRINT_FIRST_BYTES), 0, FINGERPRINT_FIRST_BYTES);
      }
      for (var i = 0, n = hash.length; i < n; i++) {
        var hex = hash[i].toString(16);
        fileID += hex.length === 1 ? '0' + hex : hex;
      }
      return (0, _util.shadow)(this, 'fingerprint', fileID);
    },
    getPage: function PDFDocument_getPage(pageIndex) {
      return this.catalog.getPage(pageIndex);
    },
    cleanup: function PDFDocument_cleanup() {
      return this.catalog.cleanup();
    }
  };
  return PDFDocument;
}();
exports.Page = Page;
exports.PDFDocument = PDFDocument;

/***/ }),
/* 26 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.CCITTFaxStream = undefined;

var _primitives = __w_pdfjs_require__(1);

var _ccitt = __w_pdfjs_require__(11);

var _stream = __w_pdfjs_require__(2);

var CCITTFaxStream = function CCITTFaxStreamClosure() {
  function CCITTFaxStream(str, maybeLength, params) {
    this.str = str;
    this.dict = str.dict;
    if (!(0, _primitives.isDict)(params)) {
      params = _primitives.Dict.empty;
    }
    const source = {
      next() {
        return str.getByte();
      }
    };
    this.ccittFaxDecoder = new _ccitt.CCITTFaxDecoder(source, {
      K: params.get('K'),
      EndOfLine: params.get('EndOfLine'),
      EncodedByteAlign: params.get('EncodedByteAlign'),
      Columns: params.get('Columns'),
      Rows: params.get('Rows'),
      EndOfBlock: params.get('EndOfBlock'),
      BlackIs1: params.get('BlackIs1')
    });
    _stream.DecodeStream.call(this, maybeLength);
  }
  CCITTFaxStream.prototype = Object.create(_stream.DecodeStream.prototype);
  CCITTFaxStream.prototype.readBlock = function () {
    while (!this.eof) {
      let c = this.ccittFaxDecoder.readNextChar();
      if (c === -1) {
        this.eof = true;
        return;
      }
      this.ensureBuffer(this.bufferLength + 1);
      this.buffer[this.bufferLength++] = c;
    }
  };
  return CCITTFaxStream;
}();
exports.CCITTFaxStream = CCITTFaxStream;

/***/ }),
/* 27 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.Jbig2Stream = undefined;

var _primitives = __w_pdfjs_require__(1);

var _stream = __w_pdfjs_require__(2);

var _jbig = __w_pdfjs_require__(28);

var _util = __w_pdfjs_require__(0);

let Jbig2Stream = function Jbig2StreamClosure() {
  function Jbig2Stream(stream, maybeLength, dict, params) {
    this.stream = stream;
    this.maybeLength = maybeLength;
    this.dict = dict;
    this.params = params;
    _stream.DecodeStream.call(this, maybeLength);
  }
  Jbig2Stream.prototype = Object.create(_stream.DecodeStream.prototype);
  Object.defineProperty(Jbig2Stream.prototype, 'bytes', {
    get() {
      return (0, _util.shadow)(this, 'bytes', this.stream.getBytes(this.maybeLength));
    },
    configurable: true
  });
  Jbig2Stream.prototype.ensureBuffer = function (requested) {};
  Jbig2Stream.prototype.readBlock = function () {
    if (this.eof) {
      return;
    }
    let jbig2Image = new _jbig.Jbig2Image();
    let chunks = [];
    if ((0, _primitives.isDict)(this.params)) {
      let globalsStream = this.params.get('JBIG2Globals');
      if ((0, _primitives.isStream)(globalsStream)) {
        let globals = globalsStream.getBytes();
        chunks.push({
          data: globals,
          start: 0,
          end: globals.length
        });
      }
    }
    chunks.push({
      data: this.bytes,
      start: 0,
      end: this.bytes.length
    });
    let data = jbig2Image.parseChunks(chunks);
    let dataLength = data.length;
    for (let i = 0; i < dataLength; i++) {
      data[i] ^= 0xFF;
    }
    this.buffer = data;
    this.bufferLength = dataLength;
    this.eof = true;
  };
  return Jbig2Stream;
}();
exports.Jbig2Stream = Jbig2Stream;

/***/ }),
/* 28 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.Jbig2Image = undefined;

var _util = __w_pdfjs_require__(0);

var _arithmetic_decoder = __w_pdfjs_require__(12);

var _ccitt = __w_pdfjs_require__(11);

let Jbig2Error = function Jbig2ErrorClosure() {
  function Jbig2Error(msg) {
    this.message = 'JBIG2 error: ' + msg;
  }
  Jbig2Error.prototype = new Error();
  Jbig2Error.prototype.name = 'Jbig2Error';
  Jbig2Error.constructor = Jbig2Error;
  return Jbig2Error;
}();
var Jbig2Image = function Jbig2ImageClosure() {
  function ContextCache() {}
  ContextCache.prototype = {
    getContexts(id) {
      if (id in this) {
        return this[id];
      }
      return this[id] = new Int8Array(1 << 16);
    }
  };
  function DecodingContext(data, start, end) {
    this.data = data;
    this.start = start;
    this.end = end;
  }
  DecodingContext.prototype = {
    get decoder() {
      var decoder = new _arithmetic_decoder.ArithmeticDecoder(this.data, this.start, this.end);
      return (0, _util.shadow)(this, 'decoder', decoder);
    },
    get contextCache() {
      var cache = new ContextCache();
      return (0, _util.shadow)(this, 'contextCache', cache);
    }
  };
  function decodeInteger(contextCache, procedure, decoder) {
    var contexts = contextCache.getContexts(procedure);
    var prev = 1;
    function readBits(length) {
      var v = 0;
      for (var i = 0; i < length; i++) {
        var bit = decoder.readBit(contexts, prev);
        prev = prev < 256 ? prev << 1 | bit : (prev << 1 | bit) & 511 | 256;
        v = v << 1 | bit;
      }
      return v >>> 0;
    }
    var sign = readBits(1);
    var value = readBits(1) ? readBits(1) ? readBits(1) ? readBits(1) ? readBits(1) ? readBits(32) + 4436 : readBits(12) + 340 : readBits(8) + 84 : readBits(6) + 20 : readBits(4) + 4 : readBits(2);
    return sign === 0 ? value : value > 0 ? -value : null;
  }
  function decodeIAID(contextCache, decoder, codeLength) {
    var contexts = contextCache.getContexts('IAID');
    var prev = 1;
    for (var i = 0; i < codeLength; i++) {
      var bit = decoder.readBit(contexts, prev);
      prev = prev << 1 | bit;
    }
    if (codeLength < 31) {
      return prev & (1 << codeLength) - 1;
    }
    return prev & 0x7FFFFFFF;
  }
  var SegmentTypes = ['SymbolDictionary', null, null, null, 'IntermediateTextRegion', null, 'ImmediateTextRegion', 'ImmediateLosslessTextRegion', null, null, null, null, null, null, null, null, 'PatternDictionary', null, null, null, 'IntermediateHalftoneRegion', null, 'ImmediateHalftoneRegion', 'ImmediateLosslessHalftoneRegion', null, null, null, null, null, null, null, null, null, null, null, null, 'IntermediateGenericRegion', null, 'ImmediateGenericRegion', 'ImmediateLosslessGenericRegion', 'IntermediateGenericRefinementRegion', null, 'ImmediateGenericRefinementRegion', 'ImmediateLosslessGenericRefinementRegion', null, null, null, null, 'PageInformation', 'EndOfPage', 'EndOfStripe', 'EndOfFile', 'Profiles', 'Tables', null, null, null, null, null, null, null, null, 'Extension'];
  var CodingTemplates = [[{
    x: -1,
    y: -2
  }, {
    x: 0,
    y: -2
  }, {
    x: 1,
    y: -2
  }, {
    x: -2,
    y: -1
  }, {
    x: -1,
    y: -1
  }, {
    x: 0,
    y: -1
  }, {
    x: 1,
    y: -1
  }, {
    x: 2,
    y: -1
  }, {
    x: -4,
    y: 0
  }, {
    x: -3,
    y: 0
  }, {
    x: -2,
    y: 0
  }, {
    x: -1,
    y: 0
  }], [{
    x: -1,
    y: -2
  }, {
    x: 0,
    y: -2
  }, {
    x: 1,
    y: -2
  }, {
    x: 2,
    y: -2
  }, {
    x: -2,
    y: -1
  }, {
    x: -1,
    y: -1
  }, {
    x: 0,
    y: -1
  }, {
    x: 1,
    y: -1
  }, {
    x: 2,
    y: -1
  }, {
    x: -3,
    y: 0
  }, {
    x: -2,
    y: 0
  }, {
    x: -1,
    y: 0
  }], [{
    x: -1,
    y: -2
  }, {
    x: 0,
    y: -2
  }, {
    x: 1,
    y: -2
  }, {
    x: -2,
    y: -1
  }, {
    x: -1,
    y: -1
  }, {
    x: 0,
    y: -1
  }, {
    x: 1,
    y: -1
  }, {
    x: -2,
    y: 0
  }, {
    x: -1,
    y: 0
  }], [{
    x: -3,
    y: -1
  }, {
    x: -2,
    y: -1
  }, {
    x: -1,
    y: -1
  }, {
    x: 0,
    y: -1
  }, {
    x: 1,
    y: -1
  }, {
    x: -4,
    y: 0
  }, {
    x: -3,
    y: 0
  }, {
    x: -2,
    y: 0
  }, {
    x: -1,
    y: 0
  }]];
  var RefinementTemplates = [{
    coding: [{
      x: 0,
      y: -1
    }, {
      x: 1,
      y: -1
    }, {
      x: -1,
      y: 0
    }],
    reference: [{
      x: 0,
      y: -1
    }, {
      x: 1,
      y: -1
    }, {
      x: -1,
      y: 0
    }, {
      x: 0,
      y: 0
    }, {
      x: 1,
      y: 0
    }, {
      x: -1,
      y: 1
    }, {
      x: 0,
      y: 1
    }, {
      x: 1,
      y: 1
    }]
  }, {
    coding: [{
      x: -1,
      y: -1
    }, {
      x: 0,
      y: -1
    }, {
      x: 1,
      y: -1
    }, {
      x: -1,
      y: 0
    }],
    reference: [{
      x: 0,
      y: -1
    }, {
      x: -1,
      y: 0
    }, {
      x: 0,
      y: 0
    }, {
      x: 1,
      y: 0
    }, {
      x: 0,
      y: 1
    }, {
      x: 1,
      y: 1
    }]
  }];
  var ReusedContexts = [0x9B25, 0x0795, 0x00E5, 0x0195];
  var RefinementReusedContexts = [0x0020, 0x0008];
  function decodeBitmapTemplate0(width, height, decodingContext) {
    var decoder = decodingContext.decoder;
    var contexts = decodingContext.contextCache.getContexts('GB');
    var contextLabel,
        i,
        j,
        pixel,
        row,
        row1,
        row2,
        bitmap = [];
    var OLD_PIXEL_MASK = 0x7BF7;
    for (i = 0; i < height; i++) {
      row = bitmap[i] = new Uint8Array(width);
      row1 = i < 1 ? row : bitmap[i - 1];
      row2 = i < 2 ? row : bitmap[i - 2];
      contextLabel = row2[0] << 13 | row2[1] << 12 | row2[2] << 11 | row1[0] << 7 | row1[1] << 6 | row1[2] << 5 | row1[3] << 4;
      for (j = 0; j < width; j++) {
        row[j] = pixel = decoder.readBit(contexts, contextLabel);
        contextLabel = (contextLabel & OLD_PIXEL_MASK) << 1 | (j + 3 < width ? row2[j + 3] << 11 : 0) | (j + 4 < width ? row1[j + 4] << 4 : 0) | pixel;
      }
    }
    return bitmap;
  }
  function decodeBitmap(mmr, width, height, templateIndex, prediction, skip, at, decodingContext) {
    if (mmr) {
      let input = new Reader(decodingContext.data, decodingContext.start, decodingContext.end);
      return decodeMMRBitmap(input, width, height, false);
    }
    if (templateIndex === 0 && !skip && !prediction && at.length === 4 && at[0].x === 3 && at[0].y === -1 && at[1].x === -3 && at[1].y === -1 && at[2].x === 2 && at[2].y === -2 && at[3].x === -2 && at[3].y === -2) {
      return decodeBitmapTemplate0(width, height, decodingContext);
    }
    var useskip = !!skip;
    var template = CodingTemplates[templateIndex].concat(at);
    template.sort(function (a, b) {
      return a.y - b.y || a.x - b.x;
    });
    var templateLength = template.length;
    var templateX = new Int8Array(templateLength);
    var templateY = new Int8Array(templateLength);
    var changingTemplateEntries = [];
    var reuseMask = 0,
        minX = 0,
        maxX = 0,
        minY = 0;
    var c, k;
    for (k = 0; k < templateLength; k++) {
      templateX[k] = template[k].x;
      templateY[k] = template[k].y;
      minX = Math.min(minX, template[k].x);
      maxX = Math.max(maxX, template[k].x);
      minY = Math.min(minY, template[k].y);
      if (k < templateLength - 1 && template[k].y === template[k + 1].y && template[k].x === template[k + 1].x - 1) {
        reuseMask |= 1 << templateLength - 1 - k;
      } else {
        changingTemplateEntries.push(k);
      }
    }
    var changingEntriesLength = changingTemplateEntries.length;
    var changingTemplateX = new Int8Array(changingEntriesLength);
    var changingTemplateY = new Int8Array(changingEntriesLength);
    var changingTemplateBit = new Uint16Array(changingEntriesLength);
    for (c = 0; c < changingEntriesLength; c++) {
      k = changingTemplateEntries[c];
      changingTemplateX[c] = template[k].x;
      changingTemplateY[c] = template[k].y;
      changingTemplateBit[c] = 1 << templateLength - 1 - k;
    }
    var sbb_left = -minX;
    var sbb_top = -minY;
    var sbb_right = width - maxX;
    var pseudoPixelContext = ReusedContexts[templateIndex];
    var row = new Uint8Array(width);
    var bitmap = [];
    var decoder = decodingContext.decoder;
    var contexts = decodingContext.contextCache.getContexts('GB');
    var ltp = 0,
        j,
        i0,
        j0,
        contextLabel = 0,
        bit,
        shift;
    for (var i = 0; i < height; i++) {
      if (prediction) {
        var sltp = decoder.readBit(contexts, pseudoPixelContext);
        ltp ^= sltp;
        if (ltp) {
          bitmap.push(row);
          continue;
        }
      }
      row = new Uint8Array(row);
      bitmap.push(row);
      for (j = 0; j < width; j++) {
        if (useskip && skip[i][j]) {
          row[j] = 0;
          continue;
        }
        if (j >= sbb_left && j < sbb_right && i >= sbb_top) {
          contextLabel = contextLabel << 1 & reuseMask;
          for (k = 0; k < changingEntriesLength; k++) {
            i0 = i + changingTemplateY[k];
            j0 = j + changingTemplateX[k];
            bit = bitmap[i0][j0];
            if (bit) {
              bit = changingTemplateBit[k];
              contextLabel |= bit;
            }
          }
        } else {
          contextLabel = 0;
          shift = templateLength - 1;
          for (k = 0; k < templateLength; k++, shift--) {
            j0 = j + templateX[k];
            if (j0 >= 0 && j0 < width) {
              i0 = i + templateY[k];
              if (i0 >= 0) {
                bit = bitmap[i0][j0];
                if (bit) {
                  contextLabel |= bit << shift;
                }
              }
            }
          }
        }
        var pixel = decoder.readBit(contexts, contextLabel);
        row[j] = pixel;
      }
    }
    return bitmap;
  }
  function decodeRefinement(width, height, templateIndex, referenceBitmap, offsetX, offsetY, prediction, at, decodingContext) {
    var codingTemplate = RefinementTemplates[templateIndex].coding;
    if (templateIndex === 0) {
      codingTemplate = codingTemplate.concat([at[0]]);
    }
    var codingTemplateLength = codingTemplate.length;
    var codingTemplateX = new Int32Array(codingTemplateLength);
    var codingTemplateY = new Int32Array(codingTemplateLength);
    var k;
    for (k = 0; k < codingTemplateLength; k++) {
      codingTemplateX[k] = codingTemplate[k].x;
      codingTemplateY[k] = codingTemplate[k].y;
    }
    var referenceTemplate = RefinementTemplates[templateIndex].reference;
    if (templateIndex === 0) {
      referenceTemplate = referenceTemplate.concat([at[1]]);
    }
    var referenceTemplateLength = referenceTemplate.length;
    var referenceTemplateX = new Int32Array(referenceTemplateLength);
    var referenceTemplateY = new Int32Array(referenceTemplateLength);
    for (k = 0; k < referenceTemplateLength; k++) {
      referenceTemplateX[k] = referenceTemplate[k].x;
      referenceTemplateY[k] = referenceTemplate[k].y;
    }
    var referenceWidth = referenceBitmap[0].length;
    var referenceHeight = referenceBitmap.length;
    var pseudoPixelContext = RefinementReusedContexts[templateIndex];
    var bitmap = [];
    var decoder = decodingContext.decoder;
    var contexts = decodingContext.contextCache.getContexts('GR');
    var ltp = 0;
    for (var i = 0; i < height; i++) {
      if (prediction) {
        var sltp = decoder.readBit(contexts, pseudoPixelContext);
        ltp ^= sltp;
        if (ltp) {
          throw new Jbig2Error('prediction is not supported');
        }
      }
      var row = new Uint8Array(width);
      bitmap.push(row);
      for (var j = 0; j < width; j++) {
        var i0, j0;
        var contextLabel = 0;
        for (k = 0; k < codingTemplateLength; k++) {
          i0 = i + codingTemplateY[k];
          j0 = j + codingTemplateX[k];
          if (i0 < 0 || j0 < 0 || j0 >= width) {
            contextLabel <<= 1;
          } else {
            contextLabel = contextLabel << 1 | bitmap[i0][j0];
          }
        }
        for (k = 0; k < referenceTemplateLength; k++) {
          i0 = i + referenceTemplateY[k] - offsetY;
          j0 = j + referenceTemplateX[k] - offsetX;
          if (i0 < 0 || i0 >= referenceHeight || j0 < 0 || j0 >= referenceWidth) {
            contextLabel <<= 1;
          } else {
            contextLabel = contextLabel << 1 | referenceBitmap[i0][j0];
          }
        }
        var pixel = decoder.readBit(contexts, contextLabel);
        row[j] = pixel;
      }
    }
    return bitmap;
  }
  function decodeSymbolDictionary(huffman, refinement, symbols, numberOfNewSymbols, numberOfExportedSymbols, huffmanTables, templateIndex, at, refinementTemplateIndex, refinementAt, decodingContext, huffmanInput) {
    if (huffman && refinement) {
      throw new Jbig2Error('symbol refinement with Huffman is not supported');
    }
    var newSymbols = [];
    var currentHeight = 0;
    var symbolCodeLength = (0, _util.log2)(symbols.length + numberOfNewSymbols);
    var decoder = decodingContext.decoder;
    var contextCache = decodingContext.contextCache;
    let tableB1, symbolWidths;
    if (huffman) {
      tableB1 = getStandardTable(1);
      symbolWidths = [];
      symbolCodeLength = Math.max(symbolCodeLength, 1);
    }
    while (newSymbols.length < numberOfNewSymbols) {
      var deltaHeight = huffman ? huffmanTables.tableDeltaHeight.decode(huffmanInput) : decodeInteger(contextCache, 'IADH', decoder);
      currentHeight += deltaHeight;
      let currentWidth = 0,
          totalWidth = 0;
      let firstSymbol = huffman ? symbolWidths.length : 0;
      while (true) {
        var deltaWidth = huffman ? huffmanTables.tableDeltaWidth.decode(huffmanInput) : decodeInteger(contextCache, 'IADW', decoder);
        if (deltaWidth === null) {
          break;
        }
        currentWidth += deltaWidth;
        totalWidth += currentWidth;
        var bitmap;
        if (refinement) {
          var numberOfInstances = decodeInteger(contextCache, 'IAAI', decoder);
          if (numberOfInstances > 1) {
            bitmap = decodeTextRegion(huffman, refinement, currentWidth, currentHeight, 0, numberOfInstances, 1, symbols.concat(newSymbols), symbolCodeLength, 0, 0, 1, 0, huffmanTables, refinementTemplateIndex, refinementAt, decodingContext, 0, huffmanInput);
          } else {
            var symbolId = decodeIAID(contextCache, decoder, symbolCodeLength);
            var rdx = decodeInteger(contextCache, 'IARDX', decoder);
            var rdy = decodeInteger(contextCache, 'IARDY', decoder);
            var symbol = symbolId < symbols.length ? symbols[symbolId] : newSymbols[symbolId - symbols.length];
            bitmap = decodeRefinement(currentWidth, currentHeight, refinementTemplateIndex, symbol, rdx, rdy, false, refinementAt, decodingContext);
          }
          newSymbols.push(bitmap);
        } else if (huffman) {
          symbolWidths.push(currentWidth);
        } else {
          bitmap = decodeBitmap(false, currentWidth, currentHeight, templateIndex, false, null, at, decodingContext);
          newSymbols.push(bitmap);
        }
      }
      if (huffman && !refinement) {
        let bitmapSize = huffmanTables.tableBitmapSize.decode(huffmanInput);
        huffmanInput.byteAlign();
        let collectiveBitmap;
        if (bitmapSize === 0) {
          collectiveBitmap = readUncompressedBitmap(huffmanInput, totalWidth, currentHeight);
        } else {
          let originalEnd = huffmanInput.end;
          let bitmapEnd = huffmanInput.position + bitmapSize;
          huffmanInput.end = bitmapEnd;
          collectiveBitmap = decodeMMRBitmap(huffmanInput, totalWidth, currentHeight, false);
          huffmanInput.end = originalEnd;
          huffmanInput.position = bitmapEnd;
        }
        let numberOfSymbolsDecoded = symbolWidths.length;
        if (firstSymbol === numberOfSymbolsDecoded - 1) {
          newSymbols.push(collectiveBitmap);
        } else {
          let i,
              y,
              xMin = 0,
              xMax,
              bitmapWidth,
              symbolBitmap;
          for (i = firstSymbol; i < numberOfSymbolsDecoded; i++) {
            bitmapWidth = symbolWidths[i];
            xMax = xMin + bitmapWidth;
            symbolBitmap = [];
            for (y = 0; y < currentHeight; y++) {
              symbolBitmap.push(collectiveBitmap[y].subarray(xMin, xMax));
            }
            newSymbols.push(symbolBitmap);
            xMin = xMax;
          }
        }
      }
    }
    var exportedSymbols = [];
    var flags = [],
        currentFlag = false;
    var totalSymbolsLength = symbols.length + numberOfNewSymbols;
    while (flags.length < totalSymbolsLength) {
      var runLength = huffman ? tableB1.decode(huffmanInput) : decodeInteger(contextCache, 'IAEX', decoder);
      while (runLength--) {
        flags.push(currentFlag);
      }
      currentFlag = !currentFlag;
    }
    for (var i = 0, ii = symbols.length; i < ii; i++) {
      if (flags[i]) {
        exportedSymbols.push(symbols[i]);
      }
    }
    for (var j = 0; j < numberOfNewSymbols; i++, j++) {
      if (flags[i]) {
        exportedSymbols.push(newSymbols[j]);
      }
    }
    return exportedSymbols;
  }
  function decodeTextRegion(huffman, refinement, width, height, defaultPixelValue, numberOfSymbolInstances, stripSize, inputSymbols, symbolCodeLength, transposed, dsOffset, referenceCorner, combinationOperator, huffmanTables, refinementTemplateIndex, refinementAt, decodingContext, logStripSize, huffmanInput) {
    if (huffman && refinement) {
      throw new Jbig2Error('refinement with Huffman is not supported');
    }
    var bitmap = [];
    var i, row;
    for (i = 0; i < height; i++) {
      row = new Uint8Array(width);
      if (defaultPixelValue) {
        for (var j = 0; j < width; j++) {
          row[j] = defaultPixelValue;
        }
      }
      bitmap.push(row);
    }
    var decoder = decodingContext.decoder;
    var contextCache = decodingContext.contextCache;
    var stripT = huffman ? -huffmanTables.tableDeltaT.decode(huffmanInput) : -decodeInteger(contextCache, 'IADT', decoder);
    var firstS = 0;
    i = 0;
    while (i < numberOfSymbolInstances) {
      var deltaT = huffman ? huffmanTables.tableDeltaT.decode(huffmanInput) : decodeInteger(contextCache, 'IADT', decoder);
      stripT += deltaT;
      var deltaFirstS = huffman ? huffmanTables.tableFirstS.decode(huffmanInput) : decodeInteger(contextCache, 'IAFS', decoder);
      firstS += deltaFirstS;
      var currentS = firstS;
      do {
        let currentT = 0;
        if (stripSize > 1) {
          currentT = huffman ? huffmanInput.readBits(logStripSize) : decodeInteger(contextCache, 'IAIT', decoder);
        }
        var t = stripSize * stripT + currentT;
        var symbolId = huffman ? huffmanTables.symbolIDTable.decode(huffmanInput) : decodeIAID(contextCache, decoder, symbolCodeLength);
        var applyRefinement = refinement && (huffman ? huffmanInput.readBit() : decodeInteger(contextCache, 'IARI', decoder));
        var symbolBitmap = inputSymbols[symbolId];
        var symbolWidth = symbolBitmap[0].length;
        var symbolHeight = symbolBitmap.length;
        if (applyRefinement) {
          var rdw = decodeInteger(contextCache, 'IARDW', decoder);
          var rdh = decodeInteger(contextCache, 'IARDH', decoder);
          var rdx = decodeInteger(contextCache, 'IARDX', decoder);
          var rdy = decodeInteger(contextCache, 'IARDY', decoder);
          symbolWidth += rdw;
          symbolHeight += rdh;
          symbolBitmap = decodeRefinement(symbolWidth, symbolHeight, refinementTemplateIndex, symbolBitmap, (rdw >> 1) + rdx, (rdh >> 1) + rdy, false, refinementAt, decodingContext);
        }
        var offsetT = t - (referenceCorner & 1 ? 0 : symbolHeight - 1);
        var offsetS = currentS - (referenceCorner & 2 ? symbolWidth - 1 : 0);
        var s2, t2, symbolRow;
        if (transposed) {
          for (s2 = 0; s2 < symbolHeight; s2++) {
            row = bitmap[offsetS + s2];
            if (!row) {
              continue;
            }
            symbolRow = symbolBitmap[s2];
            var maxWidth = Math.min(width - offsetT, symbolWidth);
            switch (combinationOperator) {
              case 0:
                for (t2 = 0; t2 < maxWidth; t2++) {
                  row[offsetT + t2] |= symbolRow[t2];
                }
                break;
              case 2:
                for (t2 = 0; t2 < maxWidth; t2++) {
                  row[offsetT + t2] ^= symbolRow[t2];
                }
                break;
              default:
                throw new Jbig2Error(`operator ${combinationOperator} is not supported`);
            }
          }
          currentS += symbolHeight - 1;
        } else {
          for (t2 = 0; t2 < symbolHeight; t2++) {
            row = bitmap[offsetT + t2];
            if (!row) {
              continue;
            }
            symbolRow = symbolBitmap[t2];
            switch (combinationOperator) {
              case 0:
                for (s2 = 0; s2 < symbolWidth; s2++) {
                  row[offsetS + s2] |= symbolRow[s2];
                }
                break;
              case 2:
                for (s2 = 0; s2 < symbolWidth; s2++) {
                  row[offsetS + s2] ^= symbolRow[s2];
                }
                break;
              default:
                throw new Jbig2Error(`operator ${combinationOperator} is not supported`);
            }
          }
          currentS += symbolWidth - 1;
        }
        i++;
        var deltaS = huffman ? huffmanTables.tableDeltaS.decode(huffmanInput) : decodeInteger(contextCache, 'IADS', decoder);
        if (deltaS === null) {
          break;
        }
        currentS += deltaS + dsOffset;
      } while (true);
    }
    return bitmap;
  }
  function decodePatternDictionary(mmr, patternWidth, patternHeight, maxPatternIndex, template, decodingContext) {
    let at = [];
    if (!mmr) {
      at.push({
        x: -patternWidth,
        y: 0
      });
      if (template === 0) {
        at.push({
          x: -3,
          y: -1
        });
        at.push({
          x: 2,
          y: -2
        });
        at.push({
          x: -2,
          y: -2
        });
      }
    }
    let collectiveWidth = (maxPatternIndex + 1) * patternWidth;
    let collectiveBitmap = decodeBitmap(mmr, collectiveWidth, patternHeight, template, false, null, at, decodingContext);
    let patterns = [],
        i = 0,
        patternBitmap,
        xMin,
        xMax,
        y;
    while (i <= maxPatternIndex) {
      patternBitmap = [];
      xMin = patternWidth * i;
      xMax = xMin + patternWidth;
      for (y = 0; y < patternHeight; y++) {
        patternBitmap.push(collectiveBitmap[y].subarray(xMin, xMax));
      }
      patterns.push(patternBitmap);
      i++;
    }
    return patterns;
  }
  function decodeHalftoneRegion(mmr, patterns, template, regionWidth, regionHeight, defaultPixelValue, enableSkip, combinationOperator, gridWidth, gridHeight, gridOffsetX, gridOffsetY, gridVectorX, gridVectorY, decodingContext) {
    let skip = null;
    if (enableSkip) {
      throw new Jbig2Error('skip is not supported');
    }
    if (combinationOperator !== 0) {
      throw new Jbig2Error('operator ' + combinationOperator + ' is not supported in halftone region');
    }
    let regionBitmap = [];
    let i, j, row;
    for (i = 0; i < regionHeight; i++) {
      row = new Uint8Array(regionWidth);
      if (defaultPixelValue) {
        for (j = 0; j < regionWidth; j++) {
          row[j] = defaultPixelValue;
        }
      }
      regionBitmap.push(row);
    }
    let numberOfPatterns = patterns.length;
    let pattern0 = patterns[0];
    let patternWidth = pattern0[0].length,
        patternHeight = pattern0.length;
    let bitsPerValue = (0, _util.log2)(numberOfPatterns);
    let at = [];
    if (!mmr) {
      at.push({
        x: template <= 1 ? 3 : 2,
        y: -1
      });
      if (template === 0) {
        at.push({
          x: -3,
          y: -1
        });
        at.push({
          x: 2,
          y: -2
        });
        at.push({
          x: -2,
          y: -2
        });
      }
    }
    let grayScaleBitPlanes = [],
        mmrInput,
        bitmap;
    if (mmr) {
      mmrInput = new Reader(decodingContext.data, decodingContext.start, decodingContext.end);
    }
    for (i = bitsPerValue - 1; i >= 0; i--) {
      if (mmr) {
        bitmap = decodeMMRBitmap(mmrInput, gridWidth, gridHeight, true);
      } else {
        bitmap = decodeBitmap(false, gridWidth, gridHeight, template, false, skip, at, decodingContext);
      }
      grayScaleBitPlanes[i] = bitmap;
    }
    let mg, ng, bit, patternIndex, patternBitmap, x, y, patternRow, regionRow;
    for (mg = 0; mg < gridHeight; mg++) {
      for (ng = 0; ng < gridWidth; ng++) {
        bit = 0;
        patternIndex = 0;
        for (j = bitsPerValue - 1; j >= 0; j--) {
          bit = grayScaleBitPlanes[j][mg][ng] ^ bit;
          patternIndex |= bit << j;
        }
        patternBitmap = patterns[patternIndex];
        x = gridOffsetX + mg * gridVectorY + ng * gridVectorX >> 8;
        y = gridOffsetY + mg * gridVectorX - ng * gridVectorY >> 8;
        if (x >= 0 && x + patternWidth <= regionWidth && y >= 0 && y + patternHeight <= regionHeight) {
          for (i = 0; i < patternHeight; i++) {
            regionRow = regionBitmap[y + i];
            patternRow = patternBitmap[i];
            for (j = 0; j < patternWidth; j++) {
              regionRow[x + j] |= patternRow[j];
            }
          }
        } else {
          let regionX, regionY;
          for (i = 0; i < patternHeight; i++) {
            regionY = y + i;
            if (regionY < 0 || regionY >= regionHeight) {
              continue;
            }
            regionRow = regionBitmap[regionY];
            patternRow = patternBitmap[i];
            for (j = 0; j < patternWidth; j++) {
              regionX = x + j;
              if (regionX >= 0 && regionX < regionWidth) {
                regionRow[regionX] |= patternRow[j];
              }
            }
          }
        }
      }
    }
    return regionBitmap;
  }
  function readSegmentHeader(data, start) {
    var segmentHeader = {};
    segmentHeader.number = (0, _util.readUint32)(data, start);
    var flags = data[start + 4];
    var segmentType = flags & 0x3F;
    if (!SegmentTypes[segmentType]) {
      throw new Jbig2Error('invalid segment type: ' + segmentType);
    }
    segmentHeader.type = segmentType;
    segmentHeader.typeName = SegmentTypes[segmentType];
    segmentHeader.deferredNonRetain = !!(flags & 0x80);
    var pageAssociationFieldSize = !!(flags & 0x40);
    var referredFlags = data[start + 5];
    var referredToCount = referredFlags >> 5 & 7;
    var retainBits = [referredFlags & 31];
    var position = start + 6;
    if (referredFlags === 7) {
      referredToCount = (0, _util.readUint32)(data, position - 1) & 0x1FFFFFFF;
      position += 3;
      var bytes = referredToCount + 7 >> 3;
      retainBits[0] = data[position++];
      while (--bytes > 0) {
        retainBits.push(data[position++]);
      }
    } else if (referredFlags === 5 || referredFlags === 6) {
      throw new Jbig2Error('invalid referred-to flags');
    }
    segmentHeader.retainBits = retainBits;
    var referredToSegmentNumberSize = segmentHeader.number <= 256 ? 1 : segmentHeader.number <= 65536 ? 2 : 4;
    var referredTo = [];
    var i, ii;
    for (i = 0; i < referredToCount; i++) {
      var number = referredToSegmentNumberSize === 1 ? data[position] : referredToSegmentNumberSize === 2 ? (0, _util.readUint16)(data, position) : (0, _util.readUint32)(data, position);
      referredTo.push(number);
      position += referredToSegmentNumberSize;
    }
    segmentHeader.referredTo = referredTo;
    if (!pageAssociationFieldSize) {
      segmentHeader.pageAssociation = data[position++];
    } else {
      segmentHeader.pageAssociation = (0, _util.readUint32)(data, position);
      position += 4;
    }
    segmentHeader.length = (0, _util.readUint32)(data, position);
    position += 4;
    if (segmentHeader.length === 0xFFFFFFFF) {
      if (segmentType === 38) {
        var genericRegionInfo = readRegionSegmentInformation(data, position);
        var genericRegionSegmentFlags = data[position + RegionSegmentInformationFieldLength];
        var genericRegionMmr = !!(genericRegionSegmentFlags & 1);
        var searchPatternLength = 6;
        var searchPattern = new Uint8Array(searchPatternLength);
        if (!genericRegionMmr) {
          searchPattern[0] = 0xFF;
          searchPattern[1] = 0xAC;
        }
        searchPattern[2] = genericRegionInfo.height >>> 24 & 0xFF;
        searchPattern[3] = genericRegionInfo.height >> 16 & 0xFF;
        searchPattern[4] = genericRegionInfo.height >> 8 & 0xFF;
        searchPattern[5] = genericRegionInfo.height & 0xFF;
        for (i = position, ii = data.length; i < ii; i++) {
          var j = 0;
          while (j < searchPatternLength && searchPattern[j] === data[i + j]) {
            j++;
          }
          if (j === searchPatternLength) {
            segmentHeader.length = i + searchPatternLength;
            break;
          }
        }
        if (segmentHeader.length === 0xFFFFFFFF) {
          throw new Jbig2Error('segment end was not found');
        }
      } else {
        throw new Jbig2Error('invalid unknown segment length');
      }
    }
    segmentHeader.headerEnd = position;
    return segmentHeader;
  }
  function readSegments(header, data, start, end) {
    var segments = [];
    var position = start;
    while (position < end) {
      var segmentHeader = readSegmentHeader(data, position);
      position = segmentHeader.headerEnd;
      var segment = {
        header: segmentHeader,
        data
      };
      if (!header.randomAccess) {
        segment.start = position;
        position += segmentHeader.length;
        segment.end = position;
      }
      segments.push(segment);
      if (segmentHeader.type === 51) {
        break;
      }
    }
    if (header.randomAccess) {
      for (var i = 0, ii = segments.length; i < ii; i++) {
        segments[i].start = position;
        position += segments[i].header.length;
        segments[i].end = position;
      }
    }
    return segments;
  }
  function readRegionSegmentInformation(data, start) {
    return {
      width: (0, _util.readUint32)(data, start),
      height: (0, _util.readUint32)(data, start + 4),
      x: (0, _util.readUint32)(data, start + 8),
      y: (0, _util.readUint32)(data, start + 12),
      combinationOperator: data[start + 16] & 7
    };
  }
  var RegionSegmentInformationFieldLength = 17;
  function processSegment(segment, visitor) {
    var header = segment.header;
    var data = segment.data,
        position = segment.start,
        end = segment.end;
    var args, at, i, atLength;
    switch (header.type) {
      case 0:
        var dictionary = {};
        var dictionaryFlags = (0, _util.readUint16)(data, position);
        dictionary.huffman = !!(dictionaryFlags & 1);
        dictionary.refinement = !!(dictionaryFlags & 2);
        dictionary.huffmanDHSelector = dictionaryFlags >> 2 & 3;
        dictionary.huffmanDWSelector = dictionaryFlags >> 4 & 3;
        dictionary.bitmapSizeSelector = dictionaryFlags >> 6 & 1;
        dictionary.aggregationInstancesSelector = dictionaryFlags >> 7 & 1;
        dictionary.bitmapCodingContextUsed = !!(dictionaryFlags & 256);
        dictionary.bitmapCodingContextRetained = !!(dictionaryFlags & 512);
        dictionary.template = dictionaryFlags >> 10 & 3;
        dictionary.refinementTemplate = dictionaryFlags >> 12 & 1;
        position += 2;
        if (!dictionary.huffman) {
          atLength = dictionary.template === 0 ? 4 : 1;
          at = [];
          for (i = 0; i < atLength; i++) {
            at.push({
              x: (0, _util.readInt8)(data, position),
              y: (0, _util.readInt8)(data, position + 1)
            });
            position += 2;
          }
          dictionary.at = at;
        }
        if (dictionary.refinement && !dictionary.refinementTemplate) {
          at = [];
          for (i = 0; i < 2; i++) {
            at.push({
              x: (0, _util.readInt8)(data, position),
              y: (0, _util.readInt8)(data, position + 1)
            });
            position += 2;
          }
          dictionary.refinementAt = at;
        }
        dictionary.numberOfExportedSymbols = (0, _util.readUint32)(data, position);
        position += 4;
        dictionary.numberOfNewSymbols = (0, _util.readUint32)(data, position);
        position += 4;
        args = [dictionary, header.number, header.referredTo, data, position, end];
        break;
      case 6:
      case 7:
        var textRegion = {};
        textRegion.info = readRegionSegmentInformation(data, position);
        position += RegionSegmentInformationFieldLength;
        var textRegionSegmentFlags = (0, _util.readUint16)(data, position);
        position += 2;
        textRegion.huffman = !!(textRegionSegmentFlags & 1);
        textRegion.refinement = !!(textRegionSegmentFlags & 2);
        textRegion.logStripSize = textRegionSegmentFlags >> 2 & 3;
        textRegion.stripSize = 1 << textRegion.logStripSize;
        textRegion.referenceCorner = textRegionSegmentFlags >> 4 & 3;
        textRegion.transposed = !!(textRegionSegmentFlags & 64);
        textRegion.combinationOperator = textRegionSegmentFlags >> 7 & 3;
        textRegion.defaultPixelValue = textRegionSegmentFlags >> 9 & 1;
        textRegion.dsOffset = textRegionSegmentFlags << 17 >> 27;
        textRegion.refinementTemplate = textRegionSegmentFlags >> 15 & 1;
        if (textRegion.huffman) {
          var textRegionHuffmanFlags = (0, _util.readUint16)(data, position);
          position += 2;
          textRegion.huffmanFS = textRegionHuffmanFlags & 3;
          textRegion.huffmanDS = textRegionHuffmanFlags >> 2 & 3;
          textRegion.huffmanDT = textRegionHuffmanFlags >> 4 & 3;
          textRegion.huffmanRefinementDW = textRegionHuffmanFlags >> 6 & 3;
          textRegion.huffmanRefinementDH = textRegionHuffmanFlags >> 8 & 3;
          textRegion.huffmanRefinementDX = textRegionHuffmanFlags >> 10 & 3;
          textRegion.huffmanRefinementDY = textRegionHuffmanFlags >> 12 & 3;
          textRegion.huffmanRefinementSizeSelector = !!(textRegionHuffmanFlags & 0x4000);
        }
        if (textRegion.refinement && !textRegion.refinementTemplate) {
          at = [];
          for (i = 0; i < 2; i++) {
            at.push({
              x: (0, _util.readInt8)(data, position),
              y: (0, _util.readInt8)(data, position + 1)
            });
            position += 2;
          }
          textRegion.refinementAt = at;
        }
        textRegion.numberOfSymbolInstances = (0, _util.readUint32)(data, position);
        position += 4;
        args = [textRegion, header.referredTo, data, position, end];
        break;
      case 16:
        let patternDictionary = {};
        let patternDictionaryFlags = data[position++];
        patternDictionary.mmr = !!(patternDictionaryFlags & 1);
        patternDictionary.template = patternDictionaryFlags >> 1 & 3;
        patternDictionary.patternWidth = data[position++];
        patternDictionary.patternHeight = data[position++];
        patternDictionary.maxPatternIndex = (0, _util.readUint32)(data, position);
        position += 4;
        args = [patternDictionary, header.number, data, position, end];
        break;
      case 22:
      case 23:
        let halftoneRegion = {};
        halftoneRegion.info = readRegionSegmentInformation(data, position);
        position += RegionSegmentInformationFieldLength;
        let halftoneRegionFlags = data[position++];
        halftoneRegion.mmr = !!(halftoneRegionFlags & 1);
        halftoneRegion.template = halftoneRegionFlags >> 1 & 3;
        halftoneRegion.enableSkip = !!(halftoneRegionFlags & 8);
        halftoneRegion.combinationOperator = halftoneRegionFlags >> 4 & 7;
        halftoneRegion.defaultPixelValue = halftoneRegionFlags >> 7 & 1;
        halftoneRegion.gridWidth = (0, _util.readUint32)(data, position);
        position += 4;
        halftoneRegion.gridHeight = (0, _util.readUint32)(data, position);
        position += 4;
        halftoneRegion.gridOffsetX = (0, _util.readUint32)(data, position) & 0xFFFFFFFF;
        position += 4;
        halftoneRegion.gridOffsetY = (0, _util.readUint32)(data, position) & 0xFFFFFFFF;
        position += 4;
        halftoneRegion.gridVectorX = (0, _util.readUint16)(data, position);
        position += 2;
        halftoneRegion.gridVectorY = (0, _util.readUint16)(data, position);
        position += 2;
        args = [halftoneRegion, header.referredTo, data, position, end];
        break;
      case 38:
      case 39:
        var genericRegion = {};
        genericRegion.info = readRegionSegmentInformation(data, position);
        position += RegionSegmentInformationFieldLength;
        var genericRegionSegmentFlags = data[position++];
        genericRegion.mmr = !!(genericRegionSegmentFlags & 1);
        genericRegion.template = genericRegionSegmentFlags >> 1 & 3;
        genericRegion.prediction = !!(genericRegionSegmentFlags & 8);
        if (!genericRegion.mmr) {
          atLength = genericRegion.template === 0 ? 4 : 1;
          at = [];
          for (i = 0; i < atLength; i++) {
            at.push({
              x: (0, _util.readInt8)(data, position),
              y: (0, _util.readInt8)(data, position + 1)
            });
            position += 2;
          }
          genericRegion.at = at;
        }
        args = [genericRegion, data, position, end];
        break;
      case 48:
        var pageInfo = {
          width: (0, _util.readUint32)(data, position),
          height: (0, _util.readUint32)(data, position + 4),
          resolutionX: (0, _util.readUint32)(data, position + 8),
          resolutionY: (0, _util.readUint32)(data, position + 12)
        };
        if (pageInfo.height === 0xFFFFFFFF) {
          delete pageInfo.height;
        }
        var pageSegmentFlags = data[position + 16];
        (0, _util.readUint16)(data, position + 17);
        pageInfo.lossless = !!(pageSegmentFlags & 1);
        pageInfo.refinement = !!(pageSegmentFlags & 2);
        pageInfo.defaultPixelValue = pageSegmentFlags >> 2 & 1;
        pageInfo.combinationOperator = pageSegmentFlags >> 3 & 3;
        pageInfo.requiresBuffer = !!(pageSegmentFlags & 32);
        pageInfo.combinationOperatorOverride = !!(pageSegmentFlags & 64);
        args = [pageInfo];
        break;
      case 49:
        break;
      case 50:
        break;
      case 51:
        break;
      case 53:
        args = [header.number, data, position, end];
        break;
      case 62:
        break;
      default:
        throw new Jbig2Error(`segment type ${header.typeName}(${header.type})` + ' is not implemented');
    }
    var callbackName = 'on' + header.typeName;
    if (callbackName in visitor) {
      visitor[callbackName].apply(visitor, args);
    }
  }
  function processSegments(segments, visitor) {
    for (var i = 0, ii = segments.length; i < ii; i++) {
      processSegment(segments[i], visitor);
    }
  }
  function parseJbig2Chunks(chunks) {
    var visitor = new SimpleSegmentVisitor();
    for (var i = 0, ii = chunks.length; i < ii; i++) {
      var chunk = chunks[i];
      var segments = readSegments({}, chunk.data, chunk.start, chunk.end);
      processSegments(segments, visitor);
    }
    return visitor.buffer;
  }
  function SimpleSegmentVisitor() {}
  SimpleSegmentVisitor.prototype = {
    onPageInformation: function SimpleSegmentVisitor_onPageInformation(info) {
      this.currentPageInfo = info;
      var rowSize = info.width + 7 >> 3;
      var buffer = new Uint8ClampedArray(rowSize * info.height);
      if (info.defaultPixelValue) {
        for (var i = 0, ii = buffer.length; i < ii; i++) {
          buffer[i] = 0xFF;
        }
      }
      this.buffer = buffer;
    },
    drawBitmap: function SimpleSegmentVisitor_drawBitmap(regionInfo, bitmap) {
      var pageInfo = this.currentPageInfo;
      var width = regionInfo.width,
          height = regionInfo.height;
      var rowSize = pageInfo.width + 7 >> 3;
      var combinationOperator = pageInfo.combinationOperatorOverride ? regionInfo.combinationOperator : pageInfo.combinationOperator;
      var buffer = this.buffer;
      var mask0 = 128 >> (regionInfo.x & 7);
      var offset0 = regionInfo.y * rowSize + (regionInfo.x >> 3);
      var i, j, mask, offset;
      switch (combinationOperator) {
        case 0:
          for (i = 0; i < height; i++) {
            mask = mask0;
            offset = offset0;
            for (j = 0; j < width; j++) {
              if (bitmap[i][j]) {
                buffer[offset] |= mask;
              }
              mask >>= 1;
              if (!mask) {
                mask = 128;
                offset++;
              }
            }
            offset0 += rowSize;
          }
          break;
        case 2:
          for (i = 0; i < height; i++) {
            mask = mask0;
            offset = offset0;
            for (j = 0; j < width; j++) {
              if (bitmap[i][j]) {
                buffer[offset] ^= mask;
              }
              mask >>= 1;
              if (!mask) {
                mask = 128;
                offset++;
              }
            }
            offset0 += rowSize;
          }
          break;
        default:
          throw new Jbig2Error(`operator ${combinationOperator} is not supported`);
      }
    },
    onImmediateGenericRegion: function SimpleSegmentVisitor_onImmediateGenericRegion(region, data, start, end) {
      var regionInfo = region.info;
      var decodingContext = new DecodingContext(data, start, end);
      var bitmap = decodeBitmap(region.mmr, regionInfo.width, regionInfo.height, region.template, region.prediction, null, region.at, decodingContext);
      this.drawBitmap(regionInfo, bitmap);
    },
    onImmediateLosslessGenericRegion: function SimpleSegmentVisitor_onImmediateLosslessGenericRegion() {
      this.onImmediateGenericRegion.apply(this, arguments);
    },
    onSymbolDictionary: function SimpleSegmentVisitor_onSymbolDictionary(dictionary, currentSegment, referredSegments, data, start, end) {
      let huffmanTables, huffmanInput;
      if (dictionary.huffman) {
        huffmanTables = getSymbolDictionaryHuffmanTables(dictionary, referredSegments, this.customTables);
        huffmanInput = new Reader(data, start, end);
      }
      var symbols = this.symbols;
      if (!symbols) {
        this.symbols = symbols = {};
      }
      var inputSymbols = [];
      for (var i = 0, ii = referredSegments.length; i < ii; i++) {
        let referredSymbols = symbols[referredSegments[i]];
        if (referredSymbols) {
          inputSymbols = inputSymbols.concat(referredSymbols);
        }
      }
      var decodingContext = new DecodingContext(data, start, end);
      symbols[currentSegment] = decodeSymbolDictionary(dictionary.huffman, dictionary.refinement, inputSymbols, dictionary.numberOfNewSymbols, dictionary.numberOfExportedSymbols, huffmanTables, dictionary.template, dictionary.at, dictionary.refinementTemplate, dictionary.refinementAt, decodingContext, huffmanInput);
    },
    onImmediateTextRegion: function SimpleSegmentVisitor_onImmediateTextRegion(region, referredSegments, data, start, end) {
      var regionInfo = region.info;
      let huffmanTables, huffmanInput;
      var symbols = this.symbols;
      var inputSymbols = [];
      for (var i = 0, ii = referredSegments.length; i < ii; i++) {
        let referredSymbols = symbols[referredSegments[i]];
        if (referredSymbols) {
          inputSymbols = inputSymbols.concat(referredSymbols);
        }
      }
      var symbolCodeLength = (0, _util.log2)(inputSymbols.length);
      if (region.huffman) {
        huffmanInput = new Reader(data, start, end);
        huffmanTables = getTextRegionHuffmanTables(region, referredSegments, this.customTables, inputSymbols.length, huffmanInput);
      }
      var decodingContext = new DecodingContext(data, start, end);
      var bitmap = decodeTextRegion(region.huffman, region.refinement, regionInfo.width, regionInfo.height, region.defaultPixelValue, region.numberOfSymbolInstances, region.stripSize, inputSymbols, symbolCodeLength, region.transposed, region.dsOffset, region.referenceCorner, region.combinationOperator, huffmanTables, region.refinementTemplate, region.refinementAt, decodingContext, region.logStripSize, huffmanInput);
      this.drawBitmap(regionInfo, bitmap);
    },
    onImmediateLosslessTextRegion: function SimpleSegmentVisitor_onImmediateLosslessTextRegion() {
      this.onImmediateTextRegion.apply(this, arguments);
    },
    onPatternDictionary(dictionary, currentSegment, data, start, end) {
      let patterns = this.patterns;
      if (!patterns) {
        this.patterns = patterns = {};
      }
      let decodingContext = new DecodingContext(data, start, end);
      patterns[currentSegment] = decodePatternDictionary(dictionary.mmr, dictionary.patternWidth, dictionary.patternHeight, dictionary.maxPatternIndex, dictionary.template, decodingContext);
    },
    onImmediateHalftoneRegion(region, referredSegments, data, start, end) {
      let patterns = this.patterns[referredSegments[0]];
      let regionInfo = region.info;
      let decodingContext = new DecodingContext(data, start, end);
      let bitmap = decodeHalftoneRegion(region.mmr, patterns, region.template, regionInfo.width, regionInfo.height, region.defaultPixelValue, region.enableSkip, region.combinationOperator, region.gridWidth, region.gridHeight, region.gridOffsetX, region.gridOffsetY, region.gridVectorX, region.gridVectorY, decodingContext);
      this.drawBitmap(regionInfo, bitmap);
    },
    onImmediateLosslessHalftoneRegion() {
      this.onImmediateHalftoneRegion.apply(this, arguments);
    },
    onTables(currentSegment, data, start, end) {
      let customTables = this.customTables;
      if (!customTables) {
        this.customTables = customTables = {};
      }
      customTables[currentSegment] = decodeTablesSegment(data, start, end);
    }
  };
  function HuffmanLine(lineData) {
    if (lineData.length === 2) {
      this.isOOB = true;
      this.rangeLow = 0;
      this.prefixLength = lineData[0];
      this.rangeLength = 0;
      this.prefixCode = lineData[1];
      this.isLowerRange = false;
    } else {
      this.isOOB = false;
      this.rangeLow = lineData[0];
      this.prefixLength = lineData[1];
      this.rangeLength = lineData[2];
      this.prefixCode = lineData[3];
      this.isLowerRange = lineData[4] === 'lower';
    }
  }
  function HuffmanTreeNode(line) {
    this.children = [];
    if (line) {
      this.isLeaf = true;
      this.rangeLength = line.rangeLength;
      this.rangeLow = line.rangeLow;
      this.isLowerRange = line.isLowerRange;
      this.isOOB = line.isOOB;
    } else {
      this.isLeaf = false;
    }
  }
  HuffmanTreeNode.prototype = {
    buildTree(line, shift) {
      let bit = line.prefixCode >> shift & 1;
      if (shift <= 0) {
        this.children[bit] = new HuffmanTreeNode(line);
      } else {
        let node = this.children[bit];
        if (!node) {
          this.children[bit] = node = new HuffmanTreeNode(null);
        }
        node.buildTree(line, shift - 1);
      }
    },
    decodeNode(reader) {
      if (this.isLeaf) {
        if (this.isOOB) {
          return null;
        }
        let htOffset = reader.readBits(this.rangeLength);
        return this.rangeLow + (this.isLowerRange ? -htOffset : htOffset);
      }
      let node = this.children[reader.readBit()];
      if (!node) {
        throw new Jbig2Error('invalid Huffman data');
      }
      return node.decodeNode(reader);
    }
  };
  function HuffmanTable(lines, prefixCodesDone) {
    if (!prefixCodesDone) {
      this.assignPrefixCodes(lines);
    }
    this.rootNode = new HuffmanTreeNode(null);
    let i,
        ii = lines.length,
        line;
    for (i = 0; i < ii; i++) {
      line = lines[i];
      if (line.prefixLength > 0) {
        this.rootNode.buildTree(line, line.prefixLength - 1);
      }
    }
  }
  HuffmanTable.prototype = {
    decode(reader) {
      return this.rootNode.decodeNode(reader);
    },
    assignPrefixCodes(lines) {
      let linesLength = lines.length,
          prefixLengthMax = 0,
          i;
      for (i = 0; i < linesLength; i++) {
        prefixLengthMax = Math.max(prefixLengthMax, lines[i].prefixLength);
      }
      let histogram = new Uint32Array(prefixLengthMax + 1);
      for (i = 0; i < linesLength; i++) {
        histogram[lines[i].prefixLength]++;
      }
      let currentLength = 1,
          firstCode = 0,
          currentCode,
          currentTemp,
          line;
      histogram[0] = 0;
      while (currentLength <= prefixLengthMax) {
        firstCode = firstCode + histogram[currentLength - 1] << 1;
        currentCode = firstCode;
        currentTemp = 0;
        while (currentTemp < linesLength) {
          line = lines[currentTemp];
          if (line.prefixLength === currentLength) {
            line.prefixCode = currentCode;
            currentCode++;
          }
          currentTemp++;
        }
        currentLength++;
      }
    }
  };
  function decodeTablesSegment(data, start, end) {
    let flags = data[start];
    let lowestValue = (0, _util.readUint32)(data, start + 1) & 0xFFFFFFFF;
    let highestValue = (0, _util.readUint32)(data, start + 5) & 0xFFFFFFFF;
    let reader = new Reader(data, start + 9, end);
    let prefixSizeBits = (flags >> 1 & 7) + 1;
    let rangeSizeBits = (flags >> 4 & 7) + 1;
    let lines = [];
    let prefixLength,
        rangeLength,
        currentRangeLow = lowestValue;
    do {
      prefixLength = reader.readBits(prefixSizeBits);
      rangeLength = reader.readBits(rangeSizeBits);
      lines.push(new HuffmanLine([currentRangeLow, prefixLength, rangeLength, 0]));
      currentRangeLow += 1 << rangeLength;
    } while (currentRangeLow < highestValue);
    prefixLength = reader.readBits(prefixSizeBits);
    lines.push(new HuffmanLine([lowestValue - 1, prefixLength, 32, 0, 'lower']));
    prefixLength = reader.readBits(prefixSizeBits);
    lines.push(new HuffmanLine([highestValue, prefixLength, 32, 0]));
    if (flags & 1) {
      prefixLength = reader.readBits(prefixSizeBits);
      lines.push(new HuffmanLine([prefixLength, 0]));
    }
    return new HuffmanTable(lines, false);
  }
  let standardTablesCache = {};
  function getStandardTable(number) {
    let table = standardTablesCache[number];
    if (table) {
      return table;
    }
    let lines;
    switch (number) {
      case 1:
        lines = [[0, 1, 4, 0x0], [16, 2, 8, 0x2], [272, 3, 16, 0x6], [65808, 3, 32, 0x7]];
        break;
      case 2:
        lines = [[0, 1, 0, 0x0], [1, 2, 0, 0x2], [2, 3, 0, 0x6], [3, 4, 3, 0xE], [11, 5, 6, 0x1E], [75, 6, 32, 0x3E], [6, 0x3F]];
        break;
      case 3:
        lines = [[-256, 8, 8, 0xFE], [0, 1, 0, 0x0], [1, 2, 0, 0x2], [2, 3, 0, 0x6], [3, 4, 3, 0xE], [11, 5, 6, 0x1E], [-257, 8, 32, 0xFF, 'lower'], [75, 7, 32, 0x7E], [6, 0x3E]];
        break;
      case 4:
        lines = [[1, 1, 0, 0x0], [2, 2, 0, 0x2], [3, 3, 0, 0x6], [4, 4, 3, 0xE], [12, 5, 6, 0x1E], [76, 5, 32, 0x1F]];
        break;
      case 5:
        lines = [[-255, 7, 8, 0x7E], [1, 1, 0, 0x0], [2, 2, 0, 0x2], [3, 3, 0, 0x6], [4, 4, 3, 0xE], [12, 5, 6, 0x1E], [-256, 7, 32, 0x7F, 'lower'], [76, 6, 32, 0x3E]];
        break;
      case 6:
        lines = [[-2048, 5, 10, 0x1C], [-1024, 4, 9, 0x8], [-512, 4, 8, 0x9], [-256, 4, 7, 0xA], [-128, 5, 6, 0x1D], [-64, 5, 5, 0x1E], [-32, 4, 5, 0xB], [0, 2, 7, 0x0], [128, 3, 7, 0x2], [256, 3, 8, 0x3], [512, 4, 9, 0xC], [1024, 4, 10, 0xD], [-2049, 6, 32, 0x3E, 'lower'], [2048, 6, 32, 0x3F]];
        break;
      case 7:
        lines = [[-1024, 4, 9, 0x8], [-512, 3, 8, 0x0], [-256, 4, 7, 0x9], [-128, 5, 6, 0x1A], [-64, 5, 5, 0x1B], [-32, 4, 5, 0xA], [0, 4, 5, 0xB], [32, 5, 5, 0x1C], [64, 5, 6, 0x1D], [128, 4, 7, 0xC], [256, 3, 8, 0x1], [512, 3, 9, 0x2], [1024, 3, 10, 0x3], [-1025, 5, 32, 0x1E, 'lower'], [2048, 5, 32, 0x1F]];
        break;
      case 8:
        lines = [[-15, 8, 3, 0xFC], [-7, 9, 1, 0x1FC], [-5, 8, 1, 0xFD], [-3, 9, 0, 0x1FD], [-2, 7, 0, 0x7C], [-1, 4, 0, 0xA], [0, 2, 1, 0x0], [2, 5, 0, 0x1A], [3, 6, 0, 0x3A], [4, 3, 4, 0x4], [20, 6, 1, 0x3B], [22, 4, 4, 0xB], [38, 4, 5, 0xC], [70, 5, 6, 0x1B], [134, 5, 7, 0x1C], [262, 6, 7, 0x3C], [390, 7, 8, 0x7D], [646, 6, 10, 0x3D], [-16, 9, 32, 0x1FE, 'lower'], [1670, 9, 32, 0x1FF], [2, 0x1]];
        break;
      case 9:
        lines = [[-31, 8, 4, 0xFC], [-15, 9, 2, 0x1FC], [-11, 8, 2, 0xFD], [-7, 9, 1, 0x1FD], [-5, 7, 1, 0x7C], [-3, 4, 1, 0xA], [-1, 3, 1, 0x2], [1, 3, 1, 0x3], [3, 5, 1, 0x1A], [5, 6, 1, 0x3A], [7, 3, 5, 0x4], [39, 6, 2, 0x3B], [43, 4, 5, 0xB], [75, 4, 6, 0xC], [139, 5, 7, 0x1B], [267, 5, 8, 0x1C], [523, 6, 8, 0x3C], [779, 7, 9, 0x7D], [1291, 6, 11, 0x3D], [-32, 9, 32, 0x1FE, 'lower'], [3339, 9, 32, 0x1FF], [2, 0x0]];
        break;
      case 10:
        lines = [[-21, 7, 4, 0x7A], [-5, 8, 0, 0xFC], [-4, 7, 0, 0x7B], [-3, 5, 0, 0x18], [-2, 2, 2, 0x0], [2, 5, 0, 0x19], [3, 6, 0, 0x36], [4, 7, 0, 0x7C], [5, 8, 0, 0xFD], [6, 2, 6, 0x1], [70, 5, 5, 0x1A], [102, 6, 5, 0x37], [134, 6, 6, 0x38], [198, 6, 7, 0x39], [326, 6, 8, 0x3A], [582, 6, 9, 0x3B], [1094, 6, 10, 0x3C], [2118, 7, 11, 0x7D], [-22, 8, 32, 0xFE, 'lower'], [4166, 8, 32, 0xFF], [2, 0x2]];
        break;
      case 11:
        lines = [[1, 1, 0, 0x0], [2, 2, 1, 0x2], [4, 4, 0, 0xC], [5, 4, 1, 0xD], [7, 5, 1, 0x1C], [9, 5, 2, 0x1D], [13, 6, 2, 0x3C], [17, 7, 2, 0x7A], [21, 7, 3, 0x7B], [29, 7, 4, 0x7C], [45, 7, 5, 0x7D], [77, 7, 6, 0x7E], [141, 7, 32, 0x7F]];
        break;
      case 12:
        lines = [[1, 1, 0, 0x0], [2, 2, 0, 0x2], [3, 3, 1, 0x6], [5, 5, 0, 0x1C], [6, 5, 1, 0x1D], [8, 6, 1, 0x3C], [10, 7, 0, 0x7A], [11, 7, 1, 0x7B], [13, 7, 2, 0x7C], [17, 7, 3, 0x7D], [25, 7, 4, 0x7E], [41, 8, 5, 0xFE], [73, 8, 32, 0xFF]];
        break;
      case 13:
        lines = [[1, 1, 0, 0x0], [2, 3, 0, 0x4], [3, 4, 0, 0xC], [4, 5, 0, 0x1C], [5, 4, 1, 0xD], [7, 3, 3, 0x5], [15, 6, 1, 0x3A], [17, 6, 2, 0x3B], [21, 6, 3, 0x3C], [29, 6, 4, 0x3D], [45, 6, 5, 0x3E], [77, 7, 6, 0x7E], [141, 7, 32, 0x7F]];
        break;
      case 14:
        lines = [[-2, 3, 0, 0x4], [-1, 3, 0, 0x5], [0, 1, 0, 0x0], [1, 3, 0, 0x6], [2, 3, 0, 0x7]];
        break;
      case 15:
        lines = [[-24, 7, 4, 0x7C], [-8, 6, 2, 0x3C], [-4, 5, 1, 0x1C], [-2, 4, 0, 0xC], [-1, 3, 0, 0x4], [0, 1, 0, 0x0], [1, 3, 0, 0x5], [2, 4, 0, 0xD], [3, 5, 1, 0x1D], [5, 6, 2, 0x3D], [9, 7, 4, 0x7D], [-25, 7, 32, 0x7E, 'lower'], [25, 7, 32, 0x7F]];
        break;
      default:
        throw new Jbig2Error(`standard table B.${number} does not exist`);
    }
    let length = lines.length,
        i;
    for (i = 0; i < length; i++) {
      lines[i] = new HuffmanLine(lines[i]);
    }
    table = new HuffmanTable(lines, true);
    standardTablesCache[number] = table;
    return table;
  }
  function Reader(data, start, end) {
    this.data = data;
    this.start = start;
    this.end = end;
    this.position = start;
    this.shift = -1;
    this.currentByte = 0;
  }
  Reader.prototype = {
    readBit() {
      if (this.shift < 0) {
        if (this.position >= this.end) {
          throw new Jbig2Error('end of data while reading bit');
        }
        this.currentByte = this.data[this.position++];
        this.shift = 7;
      }
      let bit = this.currentByte >> this.shift & 1;
      this.shift--;
      return bit;
    },
    readBits(numBits) {
      let result = 0,
          i;
      for (i = numBits - 1; i >= 0; i--) {
        result |= this.readBit() << i;
      }
      return result;
    },
    byteAlign() {
      this.shift = -1;
    },
    next() {
      if (this.position >= this.end) {
        return -1;
      }
      return this.data[this.position++];
    }
  };
  function getCustomHuffmanTable(index, referredTo, customTables) {
    let currentIndex = 0,
        i,
        ii = referredTo.length,
        table;
    for (i = 0; i < ii; i++) {
      table = customTables[referredTo[i]];
      if (table) {
        if (index === currentIndex) {
          return table;
        }
        currentIndex++;
      }
    }
    throw new Jbig2Error('can\'t find custom Huffman table');
  }
  function getTextRegionHuffmanTables(textRegion, referredTo, customTables, numberOfSymbols, reader) {
    let codes = [],
        i,
        codeLength;
    for (i = 0; i <= 34; i++) {
      codeLength = reader.readBits(4);
      codes.push(new HuffmanLine([i, codeLength, 0, 0]));
    }
    let runCodesTable = new HuffmanTable(codes, false);
    codes.length = 0;
    for (i = 0; i < numberOfSymbols;) {
      codeLength = runCodesTable.decode(reader);
      if (codeLength >= 32) {
        let repeatedLength, numberOfRepeats, j;
        switch (codeLength) {
          case 32:
            if (i === 0) {
              throw new Jbig2Error('no previous value in symbol ID table');
            }
            numberOfRepeats = reader.readBits(2) + 3;
            repeatedLength = codes[i - 1].prefixLength;
            break;
          case 33:
            numberOfRepeats = reader.readBits(3) + 3;
            repeatedLength = 0;
            break;
          case 34:
            numberOfRepeats = reader.readBits(7) + 11;
            repeatedLength = 0;
            break;
          default:
            throw new Jbig2Error('invalid code length in symbol ID table');
        }
        for (j = 0; j < numberOfRepeats; j++) {
          codes.push(new HuffmanLine([i, repeatedLength, 0, 0]));
          i++;
        }
      } else {
        codes.push(new HuffmanLine([i, codeLength, 0, 0]));
        i++;
      }
    }
    reader.byteAlign();
    let symbolIDTable = new HuffmanTable(codes, false);
    let customIndex = 0,
        tableFirstS,
        tableDeltaS,
        tableDeltaT;
    switch (textRegion.huffmanFS) {
      case 0:
      case 1:
        tableFirstS = getStandardTable(textRegion.huffmanFS + 6);
        break;
      case 3:
        tableFirstS = getCustomHuffmanTable(customIndex, referredTo, customTables);
        customIndex++;
        break;
      default:
        throw new Jbig2Error('invalid Huffman FS selector');
    }
    switch (textRegion.huffmanDS) {
      case 0:
      case 1:
      case 2:
        tableDeltaS = getStandardTable(textRegion.huffmanDS + 8);
        break;
      case 3:
        tableDeltaS = getCustomHuffmanTable(customIndex, referredTo, customTables);
        customIndex++;
        break;
      default:
        throw new Jbig2Error('invalid Huffman DS selector');
    }
    switch (textRegion.huffmanDT) {
      case 0:
      case 1:
      case 2:
        tableDeltaT = getStandardTable(textRegion.huffmanDT + 11);
        break;
      case 3:
        tableDeltaT = getCustomHuffmanTable(customIndex, referredTo, customTables);
        customIndex++;
        break;
      default:
        throw new Jbig2Error('invalid Huffman DT selector');
    }
    if (textRegion.refinement) {
      throw new Jbig2Error('refinement with Huffman is not supported');
    }
    return {
      symbolIDTable,
      tableFirstS,
      tableDeltaS,
      tableDeltaT
    };
  }
  function getSymbolDictionaryHuffmanTables(dictionary, referredTo, customTables) {
    let customIndex = 0,
        tableDeltaHeight,
        tableDeltaWidth;
    switch (dictionary.huffmanDHSelector) {
      case 0:
      case 1:
        tableDeltaHeight = getStandardTable(dictionary.huffmanDHSelector + 4);
        break;
      case 3:
        tableDeltaHeight = getCustomHuffmanTable(customIndex, referredTo, customTables);
        customIndex++;
        break;
      default:
        throw new Jbig2Error('invalid Huffman DH selector');
    }
    switch (dictionary.huffmanDWSelector) {
      case 0:
      case 1:
        tableDeltaWidth = getStandardTable(dictionary.huffmanDWSelector + 2);
        break;
      case 3:
        tableDeltaWidth = getCustomHuffmanTable(customIndex, referredTo, customTables);
        customIndex++;
        break;
      default:
        throw new Jbig2Error('invalid Huffman DW selector');
    }
    let tableBitmapSize, tableAggregateInstances;
    if (dictionary.bitmapSizeSelector) {
      tableBitmapSize = getCustomHuffmanTable(customIndex, referredTo, customTables);
      customIndex++;
    } else {
      tableBitmapSize = getStandardTable(1);
    }
    if (dictionary.aggregationInstancesSelector) {
      tableAggregateInstances = getCustomHuffmanTable(customIndex, referredTo, customTables);
    } else {
      tableAggregateInstances = getStandardTable(1);
    }
    return {
      tableDeltaHeight,
      tableDeltaWidth,
      tableBitmapSize,
      tableAggregateInstances
    };
  }
  function readUncompressedBitmap(reader, width, height) {
    let bitmap = [],
        x,
        y,
        row;
    for (y = 0; y < height; y++) {
      row = new Uint8Array(width);
      bitmap.push(row);
      for (x = 0; x < width; x++) {
        row[x] = reader.readBit();
      }
      reader.byteAlign();
    }
    return bitmap;
  }
  function decodeMMRBitmap(input, width, height, endOfBlock) {
    let params = {
      K: -1,
      Columns: width,
      Rows: height,
      BlackIs1: true,
      EndOfBlock: endOfBlock
    };
    let decoder = new _ccitt.CCITTFaxDecoder(input, params);
    let bitmap = [],
        x,
        y,
        row,
        currentByte,
        shift,
        eof = false;
    for (y = 0; y < height; y++) {
      row = new Uint8Array(width);
      bitmap.push(row);
      shift = -1;
      for (x = 0; x < width; x++) {
        if (shift < 0) {
          currentByte = decoder.readNextChar();
          if (currentByte === -1) {
            currentByte = 0;
            eof = true;
          }
          shift = 7;
        }
        row[x] = currentByte >> shift & 1;
        shift--;
      }
    }
    if (endOfBlock && !eof) {
      const lookForEOFLimit = 5;
      for (let i = 0; i < lookForEOFLimit; i++) {
        if (decoder.readNextChar() === -1) {
          break;
        }
      }
    }
    return bitmap;
  }
  function Jbig2Image() {}
  Jbig2Image.prototype = {
    parseChunks: function Jbig2Image_parseChunks(chunks) {
      return parseJbig2Chunks(chunks);
    }
  };
  return Jbig2Image;
}();
exports.Jbig2Image = Jbig2Image;

/***/ }),
/* 29 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.JpegImage = undefined;

var _util = __w_pdfjs_require__(0);

let JpegError = function JpegErrorClosure() {
  function JpegError(msg) {
    this.message = 'JPEG error: ' + msg;
  }
  JpegError.prototype = new Error();
  JpegError.prototype.name = 'JpegError';
  JpegError.constructor = JpegError;
  return JpegError;
}();
let DNLMarkerError = function DNLMarkerErrorClosure() {
  function DNLMarkerError(message, scanLines) {
    this.message = message;
    this.scanLines = scanLines;
  }
  DNLMarkerError.prototype = new Error();
  DNLMarkerError.prototype.name = 'DNLMarkerError';
  DNLMarkerError.constructor = DNLMarkerError;
  return DNLMarkerError;
}();
var JpegImage = function JpegImageClosure() {
  var dctZigZag = new Uint8Array([0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63]);
  var dctCos1 = 4017;
  var dctSin1 = 799;
  var dctCos3 = 3406;
  var dctSin3 = 2276;
  var dctCos6 = 1567;
  var dctSin6 = 3784;
  var dctSqrt2 = 5793;
  var dctSqrt1d2 = 2896;
  function JpegImage() {
    this.decodeTransform = null;
    this.colorTransform = -1;
  }
  function buildHuffmanTable(codeLengths, values) {
    var k = 0,
        code = [],
        i,
        j,
        length = 16;
    while (length > 0 && !codeLengths[length - 1]) {
      length--;
    }
    code.push({
      children: [],
      index: 0
    });
    var p = code[0],
        q;
    for (i = 0; i < length; i++) {
      for (j = 0; j < codeLengths[i]; j++) {
        p = code.pop();
        p.children[p.index] = values[k];
        while (p.index > 0) {
          p = code.pop();
        }
        p.index++;
        code.push(p);
        while (code.length <= i) {
          code.push(q = {
            children: [],
            index: 0
          });
          p.children[p.index] = q.children;
          p = q;
        }
        k++;
      }
      if (i + 1 < length) {
        code.push(q = {
          children: [],
          index: 0
        });
        p.children[p.index] = q.children;
        p = q;
      }
    }
    return code[0].children;
  }
  function getBlockBufferOffset(component, row, col) {
    return 64 * ((component.blocksPerLine + 1) * row + col);
  }
  function decodeScan(data, offset, frame, components, resetInterval, spectralStart, spectralEnd, successivePrev, successive, parseDNLMarker = false) {
    var mcusPerLine = frame.mcusPerLine;
    var progressive = frame.progressive;
    var startOffset = offset,
        bitsData = 0,
        bitsCount = 0;
    function readBit() {
      if (bitsCount > 0) {
        bitsCount--;
        return bitsData >> bitsCount & 1;
      }
      bitsData = data[offset++];
      if (bitsData === 0xFF) {
        var nextByte = data[offset++];
        if (nextByte) {
          if (nextByte === 0xDC && parseDNLMarker) {
            offset += 2;
            const scanLines = data[offset++] << 8 | data[offset++];
            if (scanLines > 0 && scanLines !== frame.scanLines) {
              throw new DNLMarkerError('Found DNL marker (0xFFDC) while parsing scan data', scanLines);
            }
          }
          throw new JpegError(`unexpected marker ${(bitsData << 8 | nextByte).toString(16)}`);
        }
      }
      bitsCount = 7;
      return bitsData >>> 7;
    }
    function decodeHuffman(tree) {
      var node = tree;
      while (true) {
        node = node[readBit()];
        if (typeof node === 'number') {
          return node;
        }
        if (typeof node !== 'object') {
          throw new JpegError('invalid huffman sequence');
        }
      }
    }
    function receive(length) {
      var n = 0;
      while (length > 0) {
        n = n << 1 | readBit();
        length--;
      }
      return n;
    }
    function receiveAndExtend(length) {
      if (length === 1) {
        return readBit() === 1 ? 1 : -1;
      }
      var n = receive(length);
      if (n >= 1 << length - 1) {
        return n;
      }
      return n + (-1 << length) + 1;
    }
    function decodeBaseline(component, offset) {
      var t = decodeHuffman(component.huffmanTableDC);
      var diff = t === 0 ? 0 : receiveAndExtend(t);
      component.blockData[offset] = component.pred += diff;
      var k = 1;
      while (k < 64) {
        var rs = decodeHuffman(component.huffmanTableAC);
        var s = rs & 15,
            r = rs >> 4;
        if (s === 0) {
          if (r < 15) {
            break;
          }
          k += 16;
          continue;
        }
        k += r;
        var z = dctZigZag[k];
        component.blockData[offset + z] = receiveAndExtend(s);
        k++;
      }
    }
    function decodeDCFirst(component, offset) {
      var t = decodeHuffman(component.huffmanTableDC);
      var diff = t === 0 ? 0 : receiveAndExtend(t) << successive;
      component.blockData[offset] = component.pred += diff;
    }
    function decodeDCSuccessive(component, offset) {
      component.blockData[offset] |= readBit() << successive;
    }
    var eobrun = 0;
    function decodeACFirst(component, offset) {
      if (eobrun > 0) {
        eobrun--;
        return;
      }
      var k = spectralStart,
          e = spectralEnd;
      while (k <= e) {
        var rs = decodeHuffman(component.huffmanTableAC);
        var s = rs & 15,
            r = rs >> 4;
        if (s === 0) {
          if (r < 15) {
            eobrun = receive(r) + (1 << r) - 1;
            break;
          }
          k += 16;
          continue;
        }
        k += r;
        var z = dctZigZag[k];
        component.blockData[offset + z] = receiveAndExtend(s) * (1 << successive);
        k++;
      }
    }
    var successiveACState = 0,
        successiveACNextValue;
    function decodeACSuccessive(component, offset) {
      var k = spectralStart;
      var e = spectralEnd;
      var r = 0;
      var s;
      var rs;
      while (k <= e) {
        let offsetZ = offset + dctZigZag[k];
        let sign = component.blockData[offsetZ] < 0 ? -1 : 1;
        switch (successiveACState) {
          case 0:
            rs = decodeHuffman(component.huffmanTableAC);
            s = rs & 15;
            r = rs >> 4;
            if (s === 0) {
              if (r < 15) {
                eobrun = receive(r) + (1 << r);
                successiveACState = 4;
              } else {
                r = 16;
                successiveACState = 1;
              }
            } else {
              if (s !== 1) {
                throw new JpegError('invalid ACn encoding');
              }
              successiveACNextValue = receiveAndExtend(s);
              successiveACState = r ? 2 : 3;
            }
            continue;
          case 1:
          case 2:
            if (component.blockData[offsetZ]) {
              component.blockData[offsetZ] += sign * (readBit() << successive);
            } else {
              r--;
              if (r === 0) {
                successiveACState = successiveACState === 2 ? 3 : 0;
              }
            }
            break;
          case 3:
            if (component.blockData[offsetZ]) {
              component.blockData[offsetZ] += sign * (readBit() << successive);
            } else {
              component.blockData[offsetZ] = successiveACNextValue << successive;
              successiveACState = 0;
            }
            break;
          case 4:
            if (component.blockData[offsetZ]) {
              component.blockData[offsetZ] += sign * (readBit() << successive);
            }
            break;
        }
        k++;
      }
      if (successiveACState === 4) {
        eobrun--;
        if (eobrun === 0) {
          successiveACState = 0;
        }
      }
    }
    function decodeMcu(component, decode, mcu, row, col) {
      var mcuRow = mcu / mcusPerLine | 0;
      var mcuCol = mcu % mcusPerLine;
      var blockRow = mcuRow * component.v + row;
      var blockCol = mcuCol * component.h + col;
      var offset = getBlockBufferOffset(component, blockRow, blockCol);
      decode(component, offset);
    }
    function decodeBlock(component, decode, mcu) {
      var blockRow = mcu / component.blocksPerLine | 0;
      var blockCol = mcu % component.blocksPerLine;
      var offset = getBlockBufferOffset(component, blockRow, blockCol);
      decode(component, offset);
    }
    var componentsLength = components.length;
    var component, i, j, k, n;
    var decodeFn;
    if (progressive) {
      if (spectralStart === 0) {
        decodeFn = successivePrev === 0 ? decodeDCFirst : decodeDCSuccessive;
      } else {
        decodeFn = successivePrev === 0 ? decodeACFirst : decodeACSuccessive;
      }
    } else {
      decodeFn = decodeBaseline;
    }
    var mcu = 0,
        fileMarker;
    var mcuExpected;
    if (componentsLength === 1) {
      mcuExpected = components[0].blocksPerLine * components[0].blocksPerColumn;
    } else {
      mcuExpected = mcusPerLine * frame.mcusPerColumn;
    }
    var h, v;
    while (mcu < mcuExpected) {
      var mcuToRead = resetInterval ? Math.min(mcuExpected - mcu, resetInterval) : mcuExpected;
      for (i = 0; i < componentsLength; i++) {
        components[i].pred = 0;
      }
      eobrun = 0;
      if (componentsLength === 1) {
        component = components[0];
        for (n = 0; n < mcuToRead; n++) {
          decodeBlock(component, decodeFn, mcu);
          mcu++;
        }
      } else {
        for (n = 0; n < mcuToRead; n++) {
          for (i = 0; i < componentsLength; i++) {
            component = components[i];
            h = component.h;
            v = component.v;
            for (j = 0; j < v; j++) {
              for (k = 0; k < h; k++) {
                decodeMcu(component, decodeFn, mcu, j, k);
              }
            }
          }
          mcu++;
        }
      }
      bitsCount = 0;
      fileMarker = findNextFileMarker(data, offset);
      if (fileMarker && fileMarker.invalid) {
        (0, _util.warn)('decodeScan - unexpected MCU data, current marker is: ' + fileMarker.invalid);
        offset = fileMarker.offset;
      }
      var marker = fileMarker && fileMarker.marker;
      if (!marker || marker <= 0xFF00) {
        throw new JpegError('marker was not found');
      }
      if (marker >= 0xFFD0 && marker <= 0xFFD7) {
        offset += 2;
      } else {
        break;
      }
    }
    fileMarker = findNextFileMarker(data, offset);
    if (fileMarker && fileMarker.invalid) {
      (0, _util.warn)('decodeScan - unexpected Scan data, current marker is: ' + fileMarker.invalid);
      offset = fileMarker.offset;
    }
    return offset - startOffset;
  }
  function quantizeAndInverse(component, blockBufferOffset, p) {
    var qt = component.quantizationTable,
        blockData = component.blockData;
    var v0, v1, v2, v3, v4, v5, v6, v7;
    var p0, p1, p2, p3, p4, p5, p6, p7;
    var t;
    if (!qt) {
      throw new JpegError('missing required Quantization Table.');
    }
    for (var row = 0; row < 64; row += 8) {
      p0 = blockData[blockBufferOffset + row];
      p1 = blockData[blockBufferOffset + row + 1];
      p2 = blockData[blockBufferOffset + row + 2];
      p3 = blockData[blockBufferOffset + row + 3];
      p4 = blockData[blockBufferOffset + row + 4];
      p5 = blockData[blockBufferOffset + row + 5];
      p6 = blockData[blockBufferOffset + row + 6];
      p7 = blockData[blockBufferOffset + row + 7];
      p0 *= qt[row];
      if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) === 0) {
        t = dctSqrt2 * p0 + 512 >> 10;
        p[row] = t;
        p[row + 1] = t;
        p[row + 2] = t;
        p[row + 3] = t;
        p[row + 4] = t;
        p[row + 5] = t;
        p[row + 6] = t;
        p[row + 7] = t;
        continue;
      }
      p1 *= qt[row + 1];
      p2 *= qt[row + 2];
      p3 *= qt[row + 3];
      p4 *= qt[row + 4];
      p5 *= qt[row + 5];
      p6 *= qt[row + 6];
      p7 *= qt[row + 7];
      v0 = dctSqrt2 * p0 + 128 >> 8;
      v1 = dctSqrt2 * p4 + 128 >> 8;
      v2 = p2;
      v3 = p6;
      v4 = dctSqrt1d2 * (p1 - p7) + 128 >> 8;
      v7 = dctSqrt1d2 * (p1 + p7) + 128 >> 8;
      v5 = p3 << 4;
      v6 = p5 << 4;
      v0 = v0 + v1 + 1 >> 1;
      v1 = v0 - v1;
      t = v2 * dctSin6 + v3 * dctCos6 + 128 >> 8;
      v2 = v2 * dctCos6 - v3 * dctSin6 + 128 >> 8;
      v3 = t;
      v4 = v4 + v6 + 1 >> 1;
      v6 = v4 - v6;
      v7 = v7 + v5 + 1 >> 1;
      v5 = v7 - v5;
      v0 = v0 + v3 + 1 >> 1;
      v3 = v0 - v3;
      v1 = v1 + v2 + 1 >> 1;
      v2 = v1 - v2;
      t = v4 * dctSin3 + v7 * dctCos3 + 2048 >> 12;
      v4 = v4 * dctCos3 - v7 * dctSin3 + 2048 >> 12;
      v7 = t;
      t = v5 * dctSin1 + v6 * dctCos1 + 2048 >> 12;
      v5 = v5 * dctCos1 - v6 * dctSin1 + 2048 >> 12;
      v6 = t;
      p[row] = v0 + v7;
      p[row + 7] = v0 - v7;
      p[row + 1] = v1 + v6;
      p[row + 6] = v1 - v6;
      p[row + 2] = v2 + v5;
      p[row + 5] = v2 - v5;
      p[row + 3] = v3 + v4;
      p[row + 4] = v3 - v4;
    }
    for (var col = 0; col < 8; ++col) {
      p0 = p[col];
      p1 = p[col + 8];
      p2 = p[col + 16];
      p3 = p[col + 24];
      p4 = p[col + 32];
      p5 = p[col + 40];
      p6 = p[col + 48];
      p7 = p[col + 56];
      if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) === 0) {
        t = dctSqrt2 * p0 + 8192 >> 14;
        t = t < -2040 ? 0 : t >= 2024 ? 255 : t + 2056 >> 4;
        blockData[blockBufferOffset + col] = t;
        blockData[blockBufferOffset + col + 8] = t;
        blockData[blockBufferOffset + col + 16] = t;
        blockData[blockBufferOffset + col + 24] = t;
        blockData[blockBufferOffset + col + 32] = t;
        blockData[blockBufferOffset + col + 40] = t;
        blockData[blockBufferOffset + col + 48] = t;
        blockData[blockBufferOffset + col + 56] = t;
        continue;
      }
      v0 = dctSqrt2 * p0 + 2048 >> 12;
      v1 = dctSqrt2 * p4 + 2048 >> 12;
      v2 = p2;
      v3 = p6;
      v4 = dctSqrt1d2 * (p1 - p7) + 2048 >> 12;
      v7 = dctSqrt1d2 * (p1 + p7) + 2048 >> 12;
      v5 = p3;
      v6 = p5;
      v0 = (v0 + v1 + 1 >> 1) + 4112;
      v1 = v0 - v1;
      t = v2 * dctSin6 + v3 * dctCos6 + 2048 >> 12;
      v2 = v2 * dctCos6 - v3 * dctSin6 + 2048 >> 12;
      v3 = t;
      v4 = v4 + v6 + 1 >> 1;
      v6 = v4 - v6;
      v7 = v7 + v5 + 1 >> 1;
      v5 = v7 - v5;
      v0 = v0 + v3 + 1 >> 1;
      v3 = v0 - v3;
      v1 = v1 + v2 + 1 >> 1;
      v2 = v1 - v2;
      t = v4 * dctSin3 + v7 * dctCos3 + 2048 >> 12;
      v4 = v4 * dctCos3 - v7 * dctSin3 + 2048 >> 12;
      v7 = t;
      t = v5 * dctSin1 + v6 * dctCos1 + 2048 >> 12;
      v5 = v5 * dctCos1 - v6 * dctSin1 + 2048 >> 12;
      v6 = t;
      p0 = v0 + v7;
      p7 = v0 - v7;
      p1 = v1 + v6;
      p6 = v1 - v6;
      p2 = v2 + v5;
      p5 = v2 - v5;
      p3 = v3 + v4;
      p4 = v3 - v4;
      p0 = p0 < 16 ? 0 : p0 >= 4080 ? 255 : p0 >> 4;
      p1 = p1 < 16 ? 0 : p1 >= 4080 ? 255 : p1 >> 4;
      p2 = p2 < 16 ? 0 : p2 >= 4080 ? 255 : p2 >> 4;
      p3 = p3 < 16 ? 0 : p3 >= 4080 ? 255 : p3 >> 4;
      p4 = p4 < 16 ? 0 : p4 >= 4080 ? 255 : p4 >> 4;
      p5 = p5 < 16 ? 0 : p5 >= 4080 ? 255 : p5 >> 4;
      p6 = p6 < 16 ? 0 : p6 >= 4080 ? 255 : p6 >> 4;
      p7 = p7 < 16 ? 0 : p7 >= 4080 ? 255 : p7 >> 4;
      blockData[blockBufferOffset + col] = p0;
      blockData[blockBufferOffset + col + 8] = p1;
      blockData[blockBufferOffset + col + 16] = p2;
      blockData[blockBufferOffset + col + 24] = p3;
      blockData[blockBufferOffset + col + 32] = p4;
      blockData[blockBufferOffset + col + 40] = p5;
      blockData[blockBufferOffset + col + 48] = p6;
      blockData[blockBufferOffset + col + 56] = p7;
    }
  }
  function buildComponentData(frame, component) {
    var blocksPerLine = component.blocksPerLine;
    var blocksPerColumn = component.blocksPerColumn;
    var computationBuffer = new Int16Array(64);
    for (var blockRow = 0; blockRow < blocksPerColumn; blockRow++) {
      for (var blockCol = 0; blockCol < blocksPerLine; blockCol++) {
        var offset = getBlockBufferOffset(component, blockRow, blockCol);
        quantizeAndInverse(component, offset, computationBuffer);
      }
    }
    return component.blockData;
  }
  function findNextFileMarker(data, currentPos, startPos = currentPos) {
    function peekUint16(pos) {
      return data[pos] << 8 | data[pos + 1];
    }
    const maxPos = data.length - 1;
    var newPos = startPos < currentPos ? startPos : currentPos;
    if (currentPos >= maxPos) {
      return null;
    }
    var currentMarker = peekUint16(currentPos);
    if (currentMarker >= 0xFFC0 && currentMarker <= 0xFFFE) {
      return {
        invalid: null,
        marker: currentMarker,
        offset: currentPos
      };
    }
    var newMarker = peekUint16(newPos);
    while (!(newMarker >= 0xFFC0 && newMarker <= 0xFFFE)) {
      if (++newPos >= maxPos) {
        return null;
      }
      newMarker = peekUint16(newPos);
    }
    return {
      invalid: currentMarker.toString(16),
      marker: newMarker,
      offset: newPos
    };
  }
  JpegImage.prototype = {
    parse(data, {
      dnlScanLines = null
    } = {}) {
      function readUint16() {
        var value = data[offset] << 8 | data[offset + 1];
        offset += 2;
        return value;
      }
      function readDataBlock() {
        var length = readUint16();
        var endOffset = offset + length - 2;
        var fileMarker = findNextFileMarker(data, endOffset, offset);
        if (fileMarker && fileMarker.invalid) {
          (0, _util.warn)('readDataBlock - incorrect length, current marker is: ' + fileMarker.invalid);
          endOffset = fileMarker.offset;
        }
        var array = data.subarray(offset, endOffset);
        offset += array.length;
        return array;
      }
      function prepareComponents(frame) {
        var mcusPerLine = Math.ceil(frame.samplesPerLine / 8 / frame.maxH);
        var mcusPerColumn = Math.ceil(frame.scanLines / 8 / frame.maxV);
        for (var i = 0; i < frame.components.length; i++) {
          component = frame.components[i];
          var blocksPerLine = Math.ceil(Math.ceil(frame.samplesPerLine / 8) * component.h / frame.maxH);
          var blocksPerColumn = Math.ceil(Math.ceil(frame.scanLines / 8) * component.v / frame.maxV);
          var blocksPerLineForMcu = mcusPerLine * component.h;
          var blocksPerColumnForMcu = mcusPerColumn * component.v;
          var blocksBufferSize = 64 * blocksPerColumnForMcu * (blocksPerLineForMcu + 1);
          component.blockData = new Int16Array(blocksBufferSize);
          component.blocksPerLine = blocksPerLine;
          component.blocksPerColumn = blocksPerColumn;
        }
        frame.mcusPerLine = mcusPerLine;
        frame.mcusPerColumn = mcusPerColumn;
      }
      var offset = 0;
      var jfif = null;
      var adobe = null;
      var frame, resetInterval;
      let numSOSMarkers = 0;
      var quantizationTables = [];
      var huffmanTablesAC = [],
          huffmanTablesDC = [];
      var fileMarker = readUint16();
      if (fileMarker !== 0xFFD8) {
        throw new JpegError('SOI not found');
      }
      fileMarker = readUint16();
      while (fileMarker !== 0xFFD9) {
        var i, j, l;
        switch (fileMarker) {
          case 0xFFE0:
          case 0xFFE1:
          case 0xFFE2:
          case 0xFFE3:
          case 0xFFE4:
          case 0xFFE5:
          case 0xFFE6:
          case 0xFFE7:
          case 0xFFE8:
          case 0xFFE9:
          case 0xFFEA:
          case 0xFFEB:
          case 0xFFEC:
          case 0xFFED:
          case 0xFFEE:
          case 0xFFEF:
          case 0xFFFE:
            var appData = readDataBlock();
            if (fileMarker === 0xFFE0) {
              if (appData[0] === 0x4A && appData[1] === 0x46 && appData[2] === 0x49 && appData[3] === 0x46 && appData[4] === 0) {
                jfif = {
                  version: {
                    major: appData[5],
                    minor: appData[6]
                  },
                  densityUnits: appData[7],
                  xDensity: appData[8] << 8 | appData[9],
                  yDensity: appData[10] << 8 | appData[11],
                  thumbWidth: appData[12],
                  thumbHeight: appData[13],
                  thumbData: appData.subarray(14, 14 + 3 * appData[12] * appData[13])
                };
              }
            }
            if (fileMarker === 0xFFEE) {
              if (appData[0] === 0x41 && appData[1] === 0x64 && appData[2] === 0x6F && appData[3] === 0x62 && appData[4] === 0x65) {
                adobe = {
                  version: appData[5] << 8 | appData[6],
                  flags0: appData[7] << 8 | appData[8],
                  flags1: appData[9] << 8 | appData[10],
                  transformCode: appData[11]
                };
              }
            }
            break;
          case 0xFFDB:
            var quantizationTablesLength = readUint16();
            var quantizationTablesEnd = quantizationTablesLength + offset - 2;
            var z;
            while (offset < quantizationTablesEnd) {
              var quantizationTableSpec = data[offset++];
              var tableData = new Uint16Array(64);
              if (quantizationTableSpec >> 4 === 0) {
                for (j = 0; j < 64; j++) {
                  z = dctZigZag[j];
                  tableData[z] = data[offset++];
                }
              } else if (quantizationTableSpec >> 4 === 1) {
                for (j = 0; j < 64; j++) {
                  z = dctZigZag[j];
                  tableData[z] = readUint16();
                }
              } else {
                throw new JpegError('DQT - invalid table spec');
              }
              quantizationTables[quantizationTableSpec & 15] = tableData;
            }
            break;
          case 0xFFC0:
          case 0xFFC1:
          case 0xFFC2:
            if (frame) {
              throw new JpegError('Only single frame JPEGs supported');
            }
            readUint16();
            frame = {};
            frame.extended = fileMarker === 0xFFC1;
            frame.progressive = fileMarker === 0xFFC2;
            frame.precision = data[offset++];
            const sofScanLines = readUint16();
            frame.scanLines = dnlScanLines || sofScanLines;
            frame.samplesPerLine = readUint16();
            frame.components = [];
            frame.componentIds = {};
            var componentsCount = data[offset++],
                componentId;
            var maxH = 0,
                maxV = 0;
            for (i = 0; i < componentsCount; i++) {
              componentId = data[offset];
              var h = data[offset + 1] >> 4;
              var v = data[offset + 1] & 15;
              if (maxH < h) {
                maxH = h;
              }
              if (maxV < v) {
                maxV = v;
              }
              var qId = data[offset + 2];
              l = frame.components.push({
                h,
                v,
                quantizationId: qId,
                quantizationTable: null
              });
              frame.componentIds[componentId] = l - 1;
              offset += 3;
            }
            frame.maxH = maxH;
            frame.maxV = maxV;
            prepareComponents(frame);
            break;
          case 0xFFC4:
            var huffmanLength = readUint16();
            for (i = 2; i < huffmanLength;) {
              var huffmanTableSpec = data[offset++];
              var codeLengths = new Uint8Array(16);
              var codeLengthSum = 0;
              for (j = 0; j < 16; j++, offset++) {
                codeLengthSum += codeLengths[j] = data[offset];
              }
              var huffmanValues = new Uint8Array(codeLengthSum);
              for (j = 0; j < codeLengthSum; j++, offset++) {
                huffmanValues[j] = data[offset];
              }
              i += 17 + codeLengthSum;
              (huffmanTableSpec >> 4 === 0 ? huffmanTablesDC : huffmanTablesAC)[huffmanTableSpec & 15] = buildHuffmanTable(codeLengths, huffmanValues);
            }
            break;
          case 0xFFDD:
            readUint16();
            resetInterval = readUint16();
            break;
          case 0xFFDA:
            const parseDNLMarker = ++numSOSMarkers === 1 && !dnlScanLines;
            readUint16();
            var selectorsCount = data[offset++];
            var components = [],
                component;
            for (i = 0; i < selectorsCount; i++) {
              var componentIndex = frame.componentIds[data[offset++]];
              component = frame.components[componentIndex];
              var tableSpec = data[offset++];
              component.huffmanTableDC = huffmanTablesDC[tableSpec >> 4];
              component.huffmanTableAC = huffmanTablesAC[tableSpec & 15];
              components.push(component);
            }
            var spectralStart = data[offset++];
            var spectralEnd = data[offset++];
            var successiveApproximation = data[offset++];
            try {
              var processed = decodeScan(data, offset, frame, components, resetInterval, spectralStart, spectralEnd, successiveApproximation >> 4, successiveApproximation & 15, parseDNLMarker);
              offset += processed;
            } catch (ex) {
              if (ex instanceof DNLMarkerError) {
                (0, _util.warn)('Attempting to re-parse JPEG image using "scanLines" ' + 'parameter found in DNL marker (0xFFDC) segment.');
                return this.parse(data, { dnlScanLines: ex.scanLines });
              }
              throw ex;
            }
            break;
          case 0xFFDC:
            offset += 4;
            break;
          case 0xFFFF:
            if (data[offset] !== 0xFF) {
              offset--;
            }
            break;
          default:
            if (data[offset - 3] === 0xFF && data[offset - 2] >= 0xC0 && data[offset - 2] <= 0xFE) {
              offset -= 3;
              break;
            }
            let nextFileMarker = findNextFileMarker(data, offset - 2);
            if (nextFileMarker && nextFileMarker.invalid) {
              (0, _util.warn)('JpegImage.parse - unexpected data, current marker is: ' + nextFileMarker.invalid);
              offset = nextFileMarker.offset;
              break;
            }
            throw new JpegError('unknown marker ' + fileMarker.toString(16));
        }
        fileMarker = readUint16();
      }
      this.width = frame.samplesPerLine;
      this.height = frame.scanLines;
      this.jfif = jfif;
      this.adobe = adobe;
      this.components = [];
      for (i = 0; i < frame.components.length; i++) {
        component = frame.components[i];
        var quantizationTable = quantizationTables[component.quantizationId];
        if (quantizationTable) {
          component.quantizationTable = quantizationTable;
        }
        this.components.push({
          output: buildComponentData(frame, component),
          scaleX: component.h / frame.maxH,
          scaleY: component.v / frame.maxV,
          blocksPerLine: component.blocksPerLine,
          blocksPerColumn: component.blocksPerColumn
        });
      }
      this.numComponents = this.components.length;
    },
    _getLinearizedBlockData: function getLinearizedBlockData(width, height) {
      var scaleX = this.width / width,
          scaleY = this.height / height;
      var component, componentScaleX, componentScaleY, blocksPerScanline;
      var x, y, i, j, k;
      var index;
      var offset = 0;
      var output;
      var numComponents = this.components.length;
      var dataLength = width * height * numComponents;
      var data = new Uint8ClampedArray(dataLength);
      var xScaleBlockOffset = new Uint32Array(width);
      var mask3LSB = 0xfffffff8;
      for (i = 0; i < numComponents; i++) {
        component = this.components[i];
        componentScaleX = component.scaleX * scaleX;
        componentScaleY = component.scaleY * scaleY;
        offset = i;
        output = component.output;
        blocksPerScanline = component.blocksPerLine + 1 << 3;
        for (x = 0; x < width; x++) {
          j = 0 | x * componentScaleX;
          xScaleBlockOffset[x] = (j & mask3LSB) << 3 | j & 7;
        }
        for (y = 0; y < height; y++) {
          j = 0 | y * componentScaleY;
          index = blocksPerScanline * (j & mask3LSB) | (j & 7) << 3;
          for (x = 0; x < width; x++) {
            data[offset] = output[index + xScaleBlockOffset[x]];
            offset += numComponents;
          }
        }
      }
      var transform = this.decodeTransform;
      if (transform) {
        for (i = 0; i < dataLength;) {
          for (j = 0, k = 0; j < numComponents; j++, i++, k += 2) {
            data[i] = (data[i] * transform[k] >> 8) + transform[k + 1];
          }
        }
      }
      return data;
    },
    _isColorConversionNeeded() {
      if (this.adobe) {
        return !!this.adobe.transformCode;
      }
      if (this.numComponents === 3) {
        if (this.colorTransform === 0) {
          return false;
        }
        return true;
      }
      if (this.colorTransform === 1) {
        return true;
      }
      return false;
    },
    _convertYccToRgb: function convertYccToRgb(data) {
      var Y, Cb, Cr;
      for (var i = 0, length = data.length; i < length; i += 3) {
        Y = data[i];
        Cb = data[i + 1];
        Cr = data[i + 2];
        data[i] = Y - 179.456 + 1.402 * Cr;
        data[i + 1] = Y + 135.459 - 0.344 * Cb - 0.714 * Cr;
        data[i + 2] = Y - 226.816 + 1.772 * Cb;
      }
      return data;
    },
    _convertYcckToRgb: function convertYcckToRgb(data) {
      var Y, Cb, Cr, k;
      var offset = 0;
      for (var i = 0, length = data.length; i < length; i += 4) {
        Y = data[i];
        Cb = data[i + 1];
        Cr = data[i + 2];
        k = data[i + 3];
        data[offset++] = -122.67195406894 + Cb * (-6.60635669420364e-5 * Cb + 0.000437130475926232 * Cr - 5.4080610064599e-5 * Y + 0.00048449797120281 * k - 0.154362151871126) + Cr * (-0.000957964378445773 * Cr + 0.000817076911346625 * Y - 0.00477271405408747 * k + 1.53380253221734) + Y * (0.000961250184130688 * Y - 0.00266257332283933 * k + 0.48357088451265) + k * (-0.000336197177618394 * k + 0.484791561490776);
        data[offset++] = 107.268039397724 + Cb * (2.19927104525741e-5 * Cb - 0.000640992018297945 * Cr + 0.000659397001245577 * Y + 0.000426105652938837 * k - 0.176491792462875) + Cr * (-0.000778269941513683 * Cr + 0.00130872261408275 * Y + 0.000770482631801132 * k - 0.151051492775562) + Y * (0.00126935368114843 * Y - 0.00265090189010898 * k + 0.25802910206845) + k * (-0.000318913117588328 * k - 0.213742400323665);
        data[offset++] = -20.810012546947 + Cb * (-0.000570115196973677 * Cb - 2.63409051004589e-5 * Cr + 0.0020741088115012 * Y - 0.00288260236853442 * k + 0.814272968359295) + Cr * (-1.53496057440975e-5 * Cr - 0.000132689043961446 * Y + 0.000560833691242812 * k - 0.195152027534049) + Y * (0.00174418132927582 * Y - 0.00255243321439347 * k + 0.116935020465145) + k * (-0.000343531996510555 * k + 0.24165260232407);
      }
      return data.subarray(0, offset);
    },
    _convertYcckToCmyk: function convertYcckToCmyk(data) {
      var Y, Cb, Cr;
      for (var i = 0, length = data.length; i < length; i += 4) {
        Y = data[i];
        Cb = data[i + 1];
        Cr = data[i + 2];
        data[i] = 434.456 - Y - 1.402 * Cr;
        data[i + 1] = 119.541 - Y + 0.344 * Cb + 0.714 * Cr;
        data[i + 2] = 481.816 - Y - 1.772 * Cb;
      }
      return data;
    },
    _convertCmykToRgb: function convertCmykToRgb(data) {
      var c, m, y, k;
      var offset = 0;
      var scale = 1 / 255;
      for (var i = 0, length = data.length; i < length; i += 4) {
        c = data[i] * scale;
        m = data[i + 1] * scale;
        y = data[i + 2] * scale;
        k = data[i + 3] * scale;
        data[offset++] = 255 + c * (-4.387332384609988 * c + 54.48615194189176 * m + 18.82290502165302 * y + 212.25662451639585 * k - 285.2331026137004) + m * (1.7149763477362134 * m - 5.6096736904047315 * y - 17.873870861415444 * k - 5.497006427196366) + y * (-2.5217340131683033 * y - 21.248923337353073 * k + 17.5119270841813) - k * (21.86122147463605 * k + 189.48180835922747);
        data[offset++] = 255 + c * (8.841041422036149 * c + 60.118027045597366 * m + 6.871425592049007 * y + 31.159100130055922 * k - 79.2970844816548) + m * (-15.310361306967817 * m + 17.575251261109482 * y + 131.35250912493976 * k - 190.9453302588951) + y * (4.444339102852739 * y + 9.8632861493405 * k - 24.86741582555878) - k * (20.737325471181034 * k + 187.80453709719578);
        data[offset++] = 255 + c * (0.8842522430003296 * c + 8.078677503112928 * m + 30.89978309703729 * y - 0.23883238689178934 * k - 14.183576799673286) + m * (10.49593273432072 * m + 63.02378494754052 * y + 50.606957656360734 * k - 112.23884253719248) + y * (0.03296041114873217 * y + 115.60384449646641 * k - 193.58209356861505) - k * (22.33816807309886 * k + 180.12613974708367);
      }
      return data.subarray(0, offset);
    },
    getData: function getData(width, height, forceRGBoutput) {
      if (this.numComponents > 4) {
        throw new JpegError('Unsupported color mode');
      }
      var data = this._getLinearizedBlockData(width, height);
      if (this.numComponents === 1 && forceRGBoutput) {
        var dataLength = data.length;
        var rgbData = new Uint8ClampedArray(dataLength * 3);
        var offset = 0;
        for (var i = 0; i < dataLength; i++) {
          var grayColor = data[i];
          rgbData[offset++] = grayColor;
          rgbData[offset++] = grayColor;
          rgbData[offset++] = grayColor;
        }
        return rgbData;
      } else if (this.numComponents === 3 && this._isColorConversionNeeded()) {
        return this._convertYccToRgb(data);
      } else if (this.numComponents === 4) {
        if (this._isColorConversionNeeded()) {
          if (forceRGBoutput) {
            return this._convertYcckToRgb(data);
          }
          return this._convertYcckToCmyk(data);
        } else if (forceRGBoutput) {
          return this._convertCmykToRgb(data);
        }
      }
      return data;
    }
  };
  return JpegImage;
}();
exports.JpegImage = JpegImage;

/***/ }),
/* 30 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.JpxStream = undefined;

var _stream = __w_pdfjs_require__(2);

var _jpx = __w_pdfjs_require__(13);

var _util = __w_pdfjs_require__(0);

let JpxStream = function JpxStreamClosure() {
  function JpxStream(stream, maybeLength, dict, params) {
    this.stream = stream;
    this.maybeLength = maybeLength;
    this.dict = dict;
    this.params = params;
    _stream.DecodeStream.call(this, maybeLength);
  }
  JpxStream.prototype = Object.create(_stream.DecodeStream.prototype);
  Object.defineProperty(JpxStream.prototype, 'bytes', {
    get: function JpxStream_bytes() {
      return (0, _util.shadow)(this, 'bytes', this.stream.getBytes(this.maybeLength));
    },
    configurable: true
  });
  JpxStream.prototype.ensureBuffer = function (requested) {};
  JpxStream.prototype.readBlock = function () {
    if (this.eof) {
      return;
    }
    let jpxImage = new _jpx.JpxImage();
    jpxImage.parse(this.bytes);
    let width = jpxImage.width;
    let height = jpxImage.height;
    let componentsCount = jpxImage.componentsCount;
    let tileCount = jpxImage.tiles.length;
    if (tileCount === 1) {
      this.buffer = jpxImage.tiles[0].items;
    } else {
      let data = new Uint8ClampedArray(width * height * componentsCount);
      for (let k = 0; k < tileCount; k++) {
        let tileComponents = jpxImage.tiles[k];
        let tileWidth = tileComponents.width;
        let tileHeight = tileComponents.height;
        let tileLeft = tileComponents.left;
        let tileTop = tileComponents.top;
        let src = tileComponents.items;
        let srcPosition = 0;
        let dataPosition = (width * tileTop + tileLeft) * componentsCount;
        let imgRowSize = width * componentsCount;
        let tileRowSize = tileWidth * componentsCount;
        for (let j = 0; j < tileHeight; j++) {
          let rowBytes = src.subarray(srcPosition, srcPosition + tileRowSize);
          data.set(rowBytes, dataPosition);
          srcPosition += tileRowSize;
          dataPosition += imgRowSize;
        }
      }
      this.buffer = data;
    }
    this.bufferLength = this.buffer.length;
    this.eof = true;
  };
  return JpxStream;
}();
exports.JpxStream = JpxStream;

/***/ }),
/* 31 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.AnnotationFactory = exports.AnnotationBorderStyle = exports.Annotation = undefined;

var _util = __w_pdfjs_require__(0);

var _obj = __w_pdfjs_require__(10);

var _primitives = __w_pdfjs_require__(1);

var _colorspace = __w_pdfjs_require__(3);

var _operator_list = __w_pdfjs_require__(7);

var _stream = __w_pdfjs_require__(2);

class AnnotationFactory {
  static create(xref, ref, pdfManager, idFactory) {
    let dict = xref.fetchIfRef(ref);
    if (!(0, _primitives.isDict)(dict)) {
      return;
    }
    let id = (0, _primitives.isRef)(ref) ? ref.toString() : 'annot_' + idFactory.createObjId();
    let subtype = dict.get('Subtype');
    subtype = (0, _primitives.isName)(subtype) ? subtype.name : null;
    let parameters = {
      xref,
      dict,
      ref: (0, _primitives.isRef)(ref) ? ref : null,
      subtype,
      id,
      pdfManager
    };
    switch (subtype) {
      case 'Link':
        return new LinkAnnotation(parameters);
      case 'Text':
        return new TextAnnotation(parameters);
      case 'Widget':
        let fieldType = _util.Util.getInheritableProperty(dict, 'FT');
        fieldType = (0, _primitives.isName)(fieldType) ? fieldType.name : null;
        switch (fieldType) {
          case 'Tx':
            return new TextWidgetAnnotation(parameters);
          case 'Btn':
            return new ButtonWidgetAnnotation(parameters);
          case 'Ch':
            return new ChoiceWidgetAnnotation(parameters);
        }
        (0, _util.warn)('Unimplemented widget field type "' + fieldType + '", ' + 'falling back to base field type.');
        return new WidgetAnnotation(parameters);
      case 'Popup':
        return new PopupAnnotation(parameters);
      case 'Line':
        return new LineAnnotation(parameters);
      case 'Square':
        return new SquareAnnotation(parameters);
      case 'Circle':
        return new CircleAnnotation(parameters);
      case 'PolyLine':
        return new PolylineAnnotation(parameters);
      case 'Polygon':
        return new PolygonAnnotation(parameters);
      case 'Highlight':
        return new HighlightAnnotation(parameters);
      case 'Underline':
        return new UnderlineAnnotation(parameters);
      case 'Squiggly':
        return new SquigglyAnnotation(parameters);
      case 'StrikeOut':
        return new StrikeOutAnnotation(parameters);
      case 'Stamp':
        return new StampAnnotation(parameters);
      case 'FileAttachment':
        return new FileAttachmentAnnotation(parameters);
      default:
        if (!subtype) {
          (0, _util.warn)('Annotation is missing the required /Subtype.');
        } else {
          (0, _util.warn)('Unimplemented annotation type "' + subtype + '", ' + 'falling back to base annotation.');
        }
        return new Annotation(parameters);
    }
  }
}
function getTransformMatrix(rect, bbox, matrix) {
  let bounds = _util.Util.getAxialAlignedBoundingBox(bbox, matrix);
  let minX = bounds[0];
  let minY = bounds[1];
  let maxX = bounds[2];
  let maxY = bounds[3];
  if (minX === maxX || minY === maxY) {
    return [1, 0, 0, 1, rect[0], rect[1]];
  }
  let xRatio = (rect[2] - rect[0]) / (maxX - minX);
  let yRatio = (rect[3] - rect[1]) / (maxY - minY);
  return [xRatio, 0, 0, yRatio, rect[0] - minX * xRatio, rect[1] - minY * yRatio];
}
class Annotation {
  constructor(params) {
    let dict = params.dict;
    this.setFlags(dict.get('F'));
    this.setRectangle(dict.getArray('Rect'));
    this.setColor(dict.getArray('C'));
    this.setBorderStyle(dict);
    this.setAppearance(dict);
    this.data = {
      annotationFlags: this.flags,
      borderStyle: this.borderStyle,
      color: this.color,
      hasAppearance: !!this.appearance,
      id: params.id,
      rect: this.rectangle,
      subtype: params.subtype
    };
  }
  _hasFlag(flags, flag) {
    return !!(flags & flag);
  }
  _isViewable(flags) {
    return !this._hasFlag(flags, _util.AnnotationFlag.INVISIBLE) && !this._hasFlag(flags, _util.AnnotationFlag.HIDDEN) && !this._hasFlag(flags, _util.AnnotationFlag.NOVIEW);
  }
  _isPrintable(flags) {
    return this._hasFlag(flags, _util.AnnotationFlag.PRINT) && !this._hasFlag(flags, _util.AnnotationFlag.INVISIBLE) && !this._hasFlag(flags, _util.AnnotationFlag.HIDDEN);
  }
  get viewable() {
    if (this.flags === 0) {
      return true;
    }
    return this._isViewable(this.flags);
  }
  get printable() {
    if (this.flags === 0) {
      return false;
    }
    return this._isPrintable(this.flags);
  }
  setFlags(flags) {
    this.flags = Number.isInteger(flags) && flags > 0 ? flags : 0;
  }
  hasFlag(flag) {
    return this._hasFlag(this.flags, flag);
  }
  setRectangle(rectangle) {
    if (Array.isArray(rectangle) && rectangle.length === 4) {
      this.rectangle = _util.Util.normalizeRect(rectangle);
    } else {
      this.rectangle = [0, 0, 0, 0];
    }
  }
  setColor(color) {
    let rgbColor = new Uint8Array(3);
    if (!Array.isArray(color)) {
      this.color = rgbColor;
      return;
    }
    switch (color.length) {
      case 0:
        this.color = null;
        break;
      case 1:
        _colorspace.ColorSpace.singletons.gray.getRgbItem(color, 0, rgbColor, 0);
        this.color = rgbColor;
        break;
      case 3:
        _colorspace.ColorSpace.singletons.rgb.getRgbItem(color, 0, rgbColor, 0);
        this.color = rgbColor;
        break;
      case 4:
        _colorspace.ColorSpace.singletons.cmyk.getRgbItem(color, 0, rgbColor, 0);
        this.color = rgbColor;
        break;
      default:
        this.color = rgbColor;
        break;
    }
  }
  setBorderStyle(borderStyle) {
    this.borderStyle = new AnnotationBorderStyle();
    if (!(0, _primitives.isDict)(borderStyle)) {
      return;
    }
    if (borderStyle.has('BS')) {
      let dict = borderStyle.get('BS');
      let dictType = dict.get('Type');
      if (!dictType || (0, _primitives.isName)(dictType, 'Border')) {
        this.borderStyle.setWidth(dict.get('W'));
        this.borderStyle.setStyle(dict.get('S'));
        this.borderStyle.setDashArray(dict.getArray('D'));
      }
    } else if (borderStyle.has('Border')) {
      let array = borderStyle.getArray('Border');
      if (Array.isArray(array) && array.length >= 3) {
        this.borderStyle.setHorizontalCornerRadius(array[0]);
        this.borderStyle.setVerticalCornerRadius(array[1]);
        this.borderStyle.setWidth(array[2]);
        if (array.length === 4) {
          this.borderStyle.setDashArray(array[3]);
        }
      }
    } else {
      this.borderStyle.setWidth(0);
    }
  }
  setAppearance(dict) {
    this.appearance = null;
    let appearanceStates = dict.get('AP');
    if (!(0, _primitives.isDict)(appearanceStates)) {
      return;
    }
    let normalAppearanceState = appearanceStates.get('N');
    if ((0, _primitives.isStream)(normalAppearanceState)) {
      this.appearance = normalAppearanceState;
      return;
    }
    if (!(0, _primitives.isDict)(normalAppearanceState)) {
      return;
    }
    let as = dict.get('AS');
    if (!(0, _primitives.isName)(as) || !normalAppearanceState.has(as.name)) {
      return;
    }
    this.appearance = normalAppearanceState.get(as.name);
  }
  _preparePopup(dict) {
    if (!dict.has('C')) {
      this.data.color = null;
    }
    this.data.hasPopup = dict.has('Popup');
    this.data.title = (0, _util.stringToPDFString)(dict.get('T') || '');
    this.data.contents = (0, _util.stringToPDFString)(dict.get('Contents') || '');
  }
  loadResources(keys) {
    return this.appearance.dict.getAsync('Resources').then(resources => {
      if (!resources) {
        return;
      }
      let objectLoader = new _obj.ObjectLoader(resources, keys, resources.xref);
      return objectLoader.load().then(function () {
        return resources;
      });
    });
  }
  getOperatorList(evaluator, task, renderForms) {
    if (!this.appearance) {
      return Promise.resolve(new _operator_list.OperatorList());
    }
    let data = this.data;
    let appearanceDict = this.appearance.dict;
    let resourcesPromise = this.loadResources(['ExtGState', 'ColorSpace', 'Pattern', 'Shading', 'XObject', 'Font']);
    let bbox = appearanceDict.getArray('BBox') || [0, 0, 1, 1];
    let matrix = appearanceDict.getArray('Matrix') || [1, 0, 0, 1, 0, 0];
    let transform = getTransformMatrix(data.rect, bbox, matrix);
    return resourcesPromise.then(resources => {
      let opList = new _operator_list.OperatorList();
      opList.addOp(_util.OPS.beginAnnotation, [data.rect, transform, matrix]);
      return evaluator.getOperatorList({
        stream: this.appearance,
        task,
        resources,
        operatorList: opList
      }).then(() => {
        opList.addOp(_util.OPS.endAnnotation, []);
        this.appearance.reset();
        return opList;
      });
    });
  }
}
class AnnotationBorderStyle {
  constructor() {
    this.width = 1;
    this.style = _util.AnnotationBorderStyleType.SOLID;
    this.dashArray = [3];
    this.horizontalCornerRadius = 0;
    this.verticalCornerRadius = 0;
  }
  setWidth(width) {
    if (Number.isInteger(width)) {
      this.width = width;
    }
  }
  setStyle(style) {
    if (!style) {
      return;
    }
    switch (style.name) {
      case 'S':
        this.style = _util.AnnotationBorderStyleType.SOLID;
        break;
      case 'D':
        this.style = _util.AnnotationBorderStyleType.DASHED;
        break;
      case 'B':
        this.style = _util.AnnotationBorderStyleType.BEVELED;
        break;
      case 'I':
        this.style = _util.AnnotationBorderStyleType.INSET;
        break;
      case 'U':
        this.style = _util.AnnotationBorderStyleType.UNDERLINE;
        break;
      default:
        break;
    }
  }
  setDashArray(dashArray) {
    if (Array.isArray(dashArray) && dashArray.length > 0) {
      let isValid = true;
      let allZeros = true;
      for (let i = 0, len = dashArray.length; i < len; i++) {
        let element = dashArray[i];
        let validNumber = +element >= 0;
        if (!validNumber) {
          isValid = false;
          break;
        } else if (element > 0) {
          allZeros = false;
        }
      }
      if (isValid && !allZeros) {
        this.dashArray = dashArray;
      } else {
        this.width = 0;
      }
    } else if (dashArray) {
      this.width = 0;
    }
  }
  setHorizontalCornerRadius(radius) {
    if (Number.isInteger(radius)) {
      this.horizontalCornerRadius = radius;
    }
  }
  setVerticalCornerRadius(radius) {
    if (Number.isInteger(radius)) {
      this.verticalCornerRadius = radius;
    }
  }
}
class WidgetAnnotation extends Annotation {
  constructor(params) {
    super(params);
    let dict = params.dict;
    let data = this.data;
    data.annotationType = _util.AnnotationType.WIDGET;
    data.fieldName = this._constructFieldName(dict);
    data.fieldValue = _util.Util.getInheritableProperty(dict, 'V', true);
    data.alternativeText = (0, _util.stringToPDFString)(dict.get('TU') || '');
    data.defaultAppearance = _util.Util.getInheritableProperty(dict, 'DA') || '';
    let fieldType = _util.Util.getInheritableProperty(dict, 'FT');
    data.fieldType = (0, _primitives.isName)(fieldType) ? fieldType.name : null;
    this.fieldResources = _util.Util.getInheritableProperty(dict, 'DR') || _primitives.Dict.empty;
    data.fieldFlags = _util.Util.getInheritableProperty(dict, 'Ff');
    if (!Number.isInteger(data.fieldFlags) || data.fieldFlags < 0) {
      data.fieldFlags = 0;
    }
    data.readOnly = this.hasFieldFlag(_util.AnnotationFieldFlag.READONLY);
    if (data.fieldType === 'Sig') {
      this.setFlags(_util.AnnotationFlag.HIDDEN);
    }
  }
  _constructFieldName(dict) {
    if (!dict.has('T') && !dict.has('Parent')) {
      (0, _util.warn)('Unknown field name, falling back to empty field name.');
      return '';
    }
    if (!dict.has('Parent')) {
      return (0, _util.stringToPDFString)(dict.get('T'));
    }
    let fieldName = [];
    if (dict.has('T')) {
      fieldName.unshift((0, _util.stringToPDFString)(dict.get('T')));
    }
    let loopDict = dict;
    while (loopDict.has('Parent')) {
      loopDict = loopDict.get('Parent');
      if (!(0, _primitives.isDict)(loopDict)) {
        break;
      }
      if (loopDict.has('T')) {
        fieldName.unshift((0, _util.stringToPDFString)(loopDict.get('T')));
      }
    }
    return fieldName.join('.');
  }
  hasFieldFlag(flag) {
    return !!(this.data.fieldFlags & flag);
  }
  getOperatorList(evaluator, task, renderForms) {
    if (renderForms) {
      return Promise.resolve(new _operator_list.OperatorList());
    }
    return super.getOperatorList(evaluator, task, renderForms);
  }
}
class TextWidgetAnnotation extends WidgetAnnotation {
  constructor(params) {
    super(params);
    this.data.fieldValue = (0, _util.stringToPDFString)(this.data.fieldValue || '');
    let alignment = _util.Util.getInheritableProperty(params.dict, 'Q');
    if (!Number.isInteger(alignment) || alignment < 0 || alignment > 2) {
      alignment = null;
    }
    this.data.textAlignment = alignment;
    let maximumLength = _util.Util.getInheritableProperty(params.dict, 'MaxLen');
    if (!Number.isInteger(maximumLength) || maximumLength < 0) {
      maximumLength = null;
    }
    this.data.maxLen = maximumLength;
    this.data.multiLine = this.hasFieldFlag(_util.AnnotationFieldFlag.MULTILINE);
    this.data.comb = this.hasFieldFlag(_util.AnnotationFieldFlag.COMB) && !this.hasFieldFlag(_util.AnnotationFieldFlag.MULTILINE) && !this.hasFieldFlag(_util.AnnotationFieldFlag.PASSWORD) && !this.hasFieldFlag(_util.AnnotationFieldFlag.FILESELECT) && this.data.maxLen !== null;
  }
  getOperatorList(evaluator, task, renderForms) {
    if (renderForms || this.appearance) {
      return super.getOperatorList(evaluator, task, renderForms);
    }
    let operatorList = new _operator_list.OperatorList();
    if (!this.data.defaultAppearance) {
      return Promise.resolve(operatorList);
    }
    let stream = new _stream.Stream((0, _util.stringToBytes)(this.data.defaultAppearance));
    return evaluator.getOperatorList({
      stream,
      task,
      resources: this.fieldResources,
      operatorList
    }).then(function () {
      return operatorList;
    });
  }
}
class ButtonWidgetAnnotation extends WidgetAnnotation {
  constructor(params) {
    super(params);
    this.data.checkBox = !this.hasFieldFlag(_util.AnnotationFieldFlag.RADIO) && !this.hasFieldFlag(_util.AnnotationFieldFlag.PUSHBUTTON);
    this.data.radioButton = this.hasFieldFlag(_util.AnnotationFieldFlag.RADIO) && !this.hasFieldFlag(_util.AnnotationFieldFlag.PUSHBUTTON);
    this.data.pushButton = this.hasFieldFlag(_util.AnnotationFieldFlag.PUSHBUTTON);
    if (this.data.checkBox) {
      this._processCheckBox();
    } else if (this.data.radioButton) {
      this._processRadioButton(params);
    } else if (this.data.pushButton) {
      this._processPushButton(params);
    } else {
      (0, _util.warn)('Invalid field flags for button widget annotation');
    }
  }
  _processCheckBox() {
    if (!(0, _primitives.isName)(this.data.fieldValue)) {
      return;
    }
    this.data.fieldValue = this.data.fieldValue.name;
  }
  _processRadioButton(params) {
    this.data.fieldValue = this.data.buttonValue = null;
    let fieldParent = params.dict.get('Parent');
    if ((0, _primitives.isDict)(fieldParent) && fieldParent.has('V')) {
      let fieldParentValue = fieldParent.get('V');
      if ((0, _primitives.isName)(fieldParentValue)) {
        this.data.fieldValue = fieldParentValue.name;
      }
    }
    let appearanceStates = params.dict.get('AP');
    if (!(0, _primitives.isDict)(appearanceStates)) {
      return;
    }
    let normalAppearanceState = appearanceStates.get('N');
    if (!(0, _primitives.isDict)(normalAppearanceState)) {
      return;
    }
    let keys = normalAppearanceState.getKeys();
    for (let i = 0, ii = keys.length; i < ii; i++) {
      if (keys[i] !== 'Off') {
        this.data.buttonValue = keys[i];
        break;
      }
    }
  }
  _processPushButton(params) {
    if (!params.dict.has('A')) {
      (0, _util.warn)('Push buttons without action dictionaries are not supported');
      return;
    }
    _obj.Catalog.parseDestDictionary({
      destDict: params.dict,
      resultObj: this.data,
      docBaseUrl: params.pdfManager.docBaseUrl
    });
  }
}
class ChoiceWidgetAnnotation extends WidgetAnnotation {
  constructor(params) {
    super(params);
    this.data.options = [];
    let options = _util.Util.getInheritableProperty(params.dict, 'Opt');
    if (Array.isArray(options)) {
      let xref = params.xref;
      for (let i = 0, ii = options.length; i < ii; i++) {
        let option = xref.fetchIfRef(options[i]);
        let isOptionArray = Array.isArray(option);
        this.data.options[i] = {
          exportValue: isOptionArray ? xref.fetchIfRef(option[0]) : option,
          displayValue: (0, _util.stringToPDFString)(isOptionArray ? xref.fetchIfRef(option[1]) : option)
        };
      }
    }
    if (!Array.isArray(this.data.fieldValue)) {
      this.data.fieldValue = [this.data.fieldValue];
    }
    this.data.combo = this.hasFieldFlag(_util.AnnotationFieldFlag.COMBO);
    this.data.multiSelect = this.hasFieldFlag(_util.AnnotationFieldFlag.MULTISELECT);
  }
}
class TextAnnotation extends Annotation {
  constructor(parameters) {
    const DEFAULT_ICON_SIZE = 22;
    super(parameters);
    this.data.annotationType = _util.AnnotationType.TEXT;
    if (this.data.hasAppearance) {
      this.data.name = 'NoIcon';
    } else {
      this.data.rect[1] = this.data.rect[3] - DEFAULT_ICON_SIZE;
      this.data.rect[2] = this.data.rect[0] + DEFAULT_ICON_SIZE;
      this.data.name = parameters.dict.has('Name') ? parameters.dict.get('Name').name : 'Note';
    }
    this._preparePopup(parameters.dict);
  }
}
class LinkAnnotation extends Annotation {
  constructor(params) {
    super(params);
    this.data.annotationType = _util.AnnotationType.LINK;
    _obj.Catalog.parseDestDictionary({
      destDict: params.dict,
      resultObj: this.data,
      docBaseUrl: params.pdfManager.docBaseUrl
    });
  }
}
class PopupAnnotation extends Annotation {
  constructor(parameters) {
    super(parameters);
    this.data.annotationType = _util.AnnotationType.POPUP;
    let dict = parameters.dict;
    let parentItem = dict.get('Parent');
    if (!parentItem) {
      (0, _util.warn)('Popup annotation has a missing or invalid parent annotation.');
      return;
    }
    let parentSubtype = parentItem.get('Subtype');
    this.data.parentType = (0, _primitives.isName)(parentSubtype) ? parentSubtype.name : null;
    this.data.parentId = dict.getRaw('Parent').toString();
    this.data.title = (0, _util.stringToPDFString)(parentItem.get('T') || '');
    this.data.contents = (0, _util.stringToPDFString)(parentItem.get('Contents') || '');
    if (!parentItem.has('C')) {
      this.data.color = null;
    } else {
      this.setColor(parentItem.getArray('C'));
      this.data.color = this.color;
    }
    if (!this.viewable) {
      let parentFlags = parentItem.get('F');
      if (this._isViewable(parentFlags)) {
        this.setFlags(parentFlags);
      }
    }
  }
}
class LineAnnotation extends Annotation {
  constructor(parameters) {
    super(parameters);
    this.data.annotationType = _util.AnnotationType.LINE;
    let dict = parameters.dict;
    this.data.lineCoordinates = _util.Util.normalizeRect(dict.getArray('L'));
    this._preparePopup(dict);
  }
}
class SquareAnnotation extends Annotation {
  constructor(parameters) {
    super(parameters);
    this.data.annotationType = _util.AnnotationType.SQUARE;
    this._preparePopup(parameters.dict);
  }
}
class CircleAnnotation extends Annotation {
  constructor(parameters) {
    super(parameters);
    this.data.annotationType = _util.AnnotationType.CIRCLE;
    this._preparePopup(parameters.dict);
  }
}
class PolylineAnnotation extends Annotation {
  constructor(parameters) {
    super(parameters);
    this.data.annotationType = _util.AnnotationType.POLYLINE;
    let dict = parameters.dict;
    let rawVertices = dict.getArray('Vertices');
    this.data.vertices = [];
    for (let i = 0, ii = rawVertices.length; i < ii; i += 2) {
      this.data.vertices.push({
        x: rawVertices[i],
        y: rawVertices[i + 1]
      });
    }
    this._preparePopup(dict);
  }
}
class PolygonAnnotation extends PolylineAnnotation {
  constructor(parameters) {
    super(parameters);
    this.data.annotationType = _util.AnnotationType.POLYGON;
  }
}
class HighlightAnnotation extends Annotation {
  constructor(parameters) {
    super(parameters);
    this.data.annotationType = _util.AnnotationType.HIGHLIGHT;
    this._preparePopup(parameters.dict);
  }
}
class UnderlineAnnotation extends Annotation {
  constructor(parameters) {
    super(parameters);
    this.data.annotationType = _util.AnnotationType.UNDERLINE;
    this._preparePopup(parameters.dict);
  }
}
class SquigglyAnnotation extends Annotation {
  constructor(parameters) {
    super(parameters);
    this.data.annotationType = _util.AnnotationType.SQUIGGLY;
    this._preparePopup(parameters.dict);
  }
}
class StrikeOutAnnotation extends Annotation {
  constructor(parameters) {
    super(parameters);
    this.data.annotationType = _util.AnnotationType.STRIKEOUT;
    this._preparePopup(parameters.dict);
  }
}
class StampAnnotation extends Annotation {
  constructor(parameters) {
    super(parameters);
    this.data.annotationType = _util.AnnotationType.STAMP;
    this._preparePopup(parameters.dict);
  }
}
class FileAttachmentAnnotation extends Annotation {
  constructor(parameters) {
    super(parameters);
    let file = new _obj.FileSpec(parameters.dict.get('FS'), parameters.xref);
    this.data.annotationType = _util.AnnotationType.FILEATTACHMENT;
    this.data.file = file.serializable;
    this._preparePopup(parameters.dict);
  }
}
exports.Annotation = Annotation;
exports.AnnotationBorderStyle = AnnotationBorderStyle;
exports.AnnotationFactory = AnnotationFactory;

/***/ }),
/* 32 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.PartialEvaluator = undefined;

var _util = __w_pdfjs_require__(0);

var _cmap = __w_pdfjs_require__(33);

var _stream = __w_pdfjs_require__(2);

var _primitives = __w_pdfjs_require__(1);

var _fonts = __w_pdfjs_require__(34);

var _encodings = __w_pdfjs_require__(4);

var _unicode = __w_pdfjs_require__(17);

var _standard_fonts = __w_pdfjs_require__(16);

var _pattern = __w_pdfjs_require__(38);

var _parser = __w_pdfjs_require__(5);

var _bidi = __w_pdfjs_require__(39);

var _colorspace = __w_pdfjs_require__(3);

var _glyphlist = __w_pdfjs_require__(8);

var _metrics = __w_pdfjs_require__(40);

var _function = __w_pdfjs_require__(18);

var _jpeg_stream = __w_pdfjs_require__(6);

var _murmurhash = __w_pdfjs_require__(42);

var _operator_list = __w_pdfjs_require__(7);

var _image = __w_pdfjs_require__(43);

var PartialEvaluator = function PartialEvaluatorClosure() {
  const DefaultPartialEvaluatorOptions = {
    forceDataSchema: false,
    maxImageSize: -1,
    disableFontFace: false,
    nativeImageDecoderSupport: _util.NativeImageDecoding.DECODE,
    ignoreErrors: false,
    isEvalSupported: true
  };
  function NativeImageDecoder({ xref, resources, handler, forceDataSchema = false, pdfFunctionFactory }) {
    this.xref = xref;
    this.resources = resources;
    this.handler = handler;
    this.forceDataSchema = forceDataSchema;
    this.pdfFunctionFactory = pdfFunctionFactory;
  }
  NativeImageDecoder.prototype = {
    canDecode(image) {
      return image instanceof _jpeg_stream.JpegStream && NativeImageDecoder.isDecodable(image, this.xref, this.resources, this.pdfFunctionFactory);
    },
    decode(image) {
      var dict = image.dict;
      var colorSpace = dict.get('ColorSpace', 'CS');
      colorSpace = _colorspace.ColorSpace.parse(colorSpace, this.xref, this.resources, this.pdfFunctionFactory);
      return this.handler.sendWithPromise('JpegDecode', [image.getIR(this.forceDataSchema), colorSpace.numComps]).then(function ({ data, width, height }) {
        return new _stream.Stream(data, 0, data.length, image.dict);
      });
    }
  };
  NativeImageDecoder.isSupported = function (image, xref, res, pdfFunctionFactory) {
    var dict = image.dict;
    if (dict.has('DecodeParms') || dict.has('DP')) {
      return false;
    }
    var cs = _colorspace.ColorSpace.parse(dict.get('ColorSpace', 'CS'), xref, res, pdfFunctionFactory);
    return (cs.name === 'DeviceGray' || cs.name === 'DeviceRGB') && cs.isDefaultDecode(dict.getArray('Decode', 'D'));
  };
  NativeImageDecoder.isDecodable = function (image, xref, res, pdfFunctionFactory) {
    var dict = image.dict;
    if (dict.has('DecodeParms') || dict.has('DP')) {
      return false;
    }
    var cs = _colorspace.ColorSpace.parse(dict.get('ColorSpace', 'CS'), xref, res, pdfFunctionFactory);
    return (cs.numComps === 1 || cs.numComps === 3) && cs.isDefaultDecode(dict.getArray('Decode', 'D'));
  };
  function PartialEvaluator({ pdfManager, xref, handler, pageIndex, idFactory, fontCache, builtInCMapCache, options = null, pdfFunctionFactory }) {
    this.pdfManager = pdfManager;
    this.xref = xref;
    this.handler = handler;
    this.pageIndex = pageIndex;
    this.idFactory = idFactory;
    this.fontCache = fontCache;
    this.builtInCMapCache = builtInCMapCache;
    this.options = options || DefaultPartialEvaluatorOptions;
    this.pdfFunctionFactory = pdfFunctionFactory;
    this.fetchBuiltInCMap = name => {
      var cachedCMap = this.builtInCMapCache[name];
      if (cachedCMap) {
        return Promise.resolve(cachedCMap);
      }
      return this.handler.sendWithPromise('FetchBuiltInCMap', { name }).then(data => {
        if (data.compressionType !== _util.CMapCompressionType.NONE) {
          this.builtInCMapCache[name] = data;
        }
        return data;
      });
    };
  }
  var TIME_SLOT_DURATION_MS = 20;
  var CHECK_TIME_EVERY = 100;
  function TimeSlotManager() {
    this.reset();
  }
  TimeSlotManager.prototype = {
    check: function TimeSlotManager_check() {
      if (++this.checked < CHECK_TIME_EVERY) {
        return false;
      }
      this.checked = 0;
      return this.endTime <= Date.now();
    },
    reset: function TimeSlotManager_reset() {
      this.endTime = Date.now() + TIME_SLOT_DURATION_MS;
      this.checked = 0;
    }
  };
  function normalizeBlendMode(value) {
    if (!(0, _primitives.isName)(value)) {
      return 'source-over';
    }
    switch (value.name) {
      case 'Normal':
      case 'Compatible':
        return 'source-over';
      case 'Multiply':
        return 'multiply';
      case 'Screen':
        return 'screen';
      case 'Overlay':
        return 'overlay';
      case 'Darken':
        return 'darken';
      case 'Lighten':
        return 'lighten';
      case 'ColorDodge':
        return 'color-dodge';
      case 'ColorBurn':
        return 'color-burn';
      case 'HardLight':
        return 'hard-light';
      case 'SoftLight':
        return 'soft-light';
      case 'Difference':
        return 'difference';
      case 'Exclusion':
        return 'exclusion';
      case 'Hue':
        return 'hue';
      case 'Saturation':
        return 'saturation';
      case 'Color':
        return 'color';
      case 'Luminosity':
        return 'luminosity';
    }
    (0, _util.warn)('Unsupported blend mode: ' + value.name);
    return 'source-over';
  }
  var deferred = Promise.resolve();
  var TILING_PATTERN = 1,
      SHADING_PATTERN = 2;
  PartialEvaluator.prototype = {
    clone(newOptions = DefaultPartialEvaluatorOptions) {
      var newEvaluator = Object.create(this);
      newEvaluator.options = newOptions;
      return newEvaluator;
    },
    hasBlendModes: function PartialEvaluator_hasBlendModes(resources) {
      if (!(0, _primitives.isDict)(resources)) {
        return false;
      }
      var processed = Object.create(null);
      if (resources.objId) {
        processed[resources.objId] = true;
      }
      var nodes = [resources],
          xref = this.xref;
      while (nodes.length) {
        var key, i, ii;
        var node = nodes.shift();
        var graphicStates = node.get('ExtGState');
        if ((0, _primitives.isDict)(graphicStates)) {
          var graphicStatesKeys = graphicStates.getKeys();
          for (i = 0, ii = graphicStatesKeys.length; i < ii; i++) {
            key = graphicStatesKeys[i];
            var graphicState = graphicStates.get(key);
            var bm = graphicState.get('BM');
            if ((0, _primitives.isName)(bm) && bm.name !== 'Normal') {
              return true;
            }
          }
        }
        var xObjects = node.get('XObject');
        if (!(0, _primitives.isDict)(xObjects)) {
          continue;
        }
        var xObjectsKeys = xObjects.getKeys();
        for (i = 0, ii = xObjectsKeys.length; i < ii; i++) {
          key = xObjectsKeys[i];
          var xObject = xObjects.getRaw(key);
          if ((0, _primitives.isRef)(xObject)) {
            if (processed[xObject.toString()]) {
              continue;
            }
            xObject = xref.fetch(xObject);
          }
          if (!(0, _primitives.isStream)(xObject)) {
            continue;
          }
          if (xObject.dict.objId) {
            if (processed[xObject.dict.objId]) {
              continue;
            }
            processed[xObject.dict.objId] = true;
          }
          var xResources = xObject.dict.get('Resources');
          if ((0, _primitives.isDict)(xResources) && (!xResources.objId || !processed[xResources.objId])) {
            nodes.push(xResources);
            if (xResources.objId) {
              processed[xResources.objId] = true;
            }
          }
        }
      }
      return false;
    },
    buildFormXObject: function PartialEvaluator_buildFormXObject(resources, xobj, smask, operatorList, task, initialState) {
      var dict = xobj.dict;
      var matrix = dict.getArray('Matrix');
      var bbox = dict.getArray('BBox');
      var group = dict.get('Group');
      if (group) {
        var groupOptions = {
          matrix,
          bbox,
          smask,
          isolated: false,
          knockout: false
        };
        var groupSubtype = group.get('S');
        var colorSpace = null;
        if ((0, _primitives.isName)(groupSubtype, 'Transparency')) {
          groupOptions.isolated = group.get('I') || false;
          groupOptions.knockout = group.get('K') || false;
          if (group.has('CS')) {
            colorSpace = _colorspace.ColorSpace.parse(group.get('CS'), this.xref, resources, this.pdfFunctionFactory);
          }
        }
        if (smask && smask.backdrop) {
          colorSpace = colorSpace || _colorspace.ColorSpace.singletons.rgb;
          smask.backdrop = colorSpace.getRgb(smask.backdrop, 0);
        }
        operatorList.addOp(_util.OPS.beginGroup, [groupOptions]);
      }
      operatorList.addOp(_util.OPS.paintFormXObjectBegin, [matrix, bbox]);
      return this.getOperatorList({
        stream: xobj,
        task,
        resources: dict.get('Resources') || resources,
        operatorList,
        initialState
      }).then(function () {
        operatorList.addOp(_util.OPS.paintFormXObjectEnd, []);
        if (group) {
          operatorList.addOp(_util.OPS.endGroup, [groupOptions]);
        }
      });
    },
    buildPaintImageXObject({ resources, image, isInline = false, operatorList, cacheKey, imageCache, forceDisableNativeImageDecoder = false }) {
      var dict = image.dict;
      var w = dict.get('Width', 'W');
      var h = dict.get('Height', 'H');
      if (!(w && (0, _util.isNum)(w)) || !(h && (0, _util.isNum)(h))) {
        (0, _util.warn)('Image dimensions are missing, or not numbers.');
        return Promise.resolve();
      }
      var maxImageSize = this.options.maxImageSize;
      if (maxImageSize !== -1 && w * h > maxImageSize) {
        (0, _util.warn)('Image exceeded maximum allowed size and was removed.');
        return Promise.resolve();
      }
      var imageMask = dict.get('ImageMask', 'IM') || false;
      var imgData, args;
      if (imageMask) {
        var width = dict.get('Width', 'W');
        var height = dict.get('Height', 'H');
        var bitStrideLength = width + 7 >> 3;
        var imgArray = image.getBytes(bitStrideLength * height);
        var decode = dict.getArray('Decode', 'D');
        imgData = _image.PDFImage.createMask({
          imgArray,
          width,
          height,
          imageIsFromDecodeStream: image instanceof _stream.DecodeStream,
          inverseDecode: !!decode && decode[0] > 0
        });
        imgData.cached = true;
        args = [imgData];
        operatorList.addOp(_util.OPS.paintImageMaskXObject, args);
        if (cacheKey) {
          imageCache[cacheKey] = {
            fn: _util.OPS.paintImageMaskXObject,
            args
          };
        }
        return Promise.resolve();
      }
      var softMask = dict.get('SMask', 'SM') || false;
      var mask = dict.get('Mask') || false;
      var SMALL_IMAGE_DIMENSIONS = 200;
      if (isInline && !softMask && !mask && !(image instanceof _jpeg_stream.JpegStream) && w + h < SMALL_IMAGE_DIMENSIONS) {
        let imageObj = new _image.PDFImage({
          xref: this.xref,
          res: resources,
          image,
          isInline,
          pdfFunctionFactory: this.pdfFunctionFactory
        });
        imgData = imageObj.createImageData(true);
        operatorList.addOp(_util.OPS.paintInlineImageXObject, [imgData]);
        return Promise.resolve();
      }
      const nativeImageDecoderSupport = forceDisableNativeImageDecoder ? _util.NativeImageDecoding.NONE : this.options.nativeImageDecoderSupport;
      var objId = 'img_' + this.idFactory.createObjId();
      if (nativeImageDecoderSupport !== _util.NativeImageDecoding.NONE && !softMask && !mask && image instanceof _jpeg_stream.JpegStream && NativeImageDecoder.isSupported(image, this.xref, resources, this.pdfFunctionFactory)) {
        return this.handler.sendWithPromise('obj', [objId, this.pageIndex, 'JpegStream', image.getIR(this.options.forceDataSchema)]).then(function () {
          operatorList.addDependency(objId);
          args = [objId, w, h];
          operatorList.addOp(_util.OPS.paintJpegXObject, args);
          if (cacheKey) {
            imageCache[cacheKey] = {
              fn: _util.OPS.paintJpegXObject,
              args
            };
          }
        }, reason => {
          (0, _util.warn)('Native JPEG decoding failed -- trying to recover: ' + (reason && reason.message));
          return this.buildPaintImageXObject({
            resources,
            image,
            isInline,
            operatorList,
            cacheKey,
            imageCache,
            forceDisableNativeImageDecoder: true
          });
        });
      }
      var nativeImageDecoder = null;
      if (nativeImageDecoderSupport === _util.NativeImageDecoding.DECODE && (image instanceof _jpeg_stream.JpegStream || mask instanceof _jpeg_stream.JpegStream || softMask instanceof _jpeg_stream.JpegStream)) {
        nativeImageDecoder = new NativeImageDecoder({
          xref: this.xref,
          resources,
          handler: this.handler,
          forceDataSchema: this.options.forceDataSchema,
          pdfFunctionFactory: this.pdfFunctionFactory
        });
      }
      operatorList.addDependency(objId);
      args = [objId, w, h];
      _image.PDFImage.buildImage({
        handler: this.handler,
        xref: this.xref,
        res: resources,
        image,
        isInline,
        nativeDecoder: nativeImageDecoder,
        pdfFunctionFactory: this.pdfFunctionFactory
      }).then(imageObj => {
        var imgData = imageObj.createImageData(false);
        this.handler.send('obj', [objId, this.pageIndex, 'Image', imgData], [imgData.data.buffer]);
      }).catch(reason => {
        (0, _util.warn)('Unable to decode image: ' + reason);
        this.handler.send('obj', [objId, this.pageIndex, 'Image', null]);
      });
      operatorList.addOp(_util.OPS.paintImageXObject, args);
      if (cacheKey) {
        imageCache[cacheKey] = {
          fn: _util.OPS.paintImageXObject,
          args
        };
      }
      return Promise.resolve();
    },
    handleSMask: function PartialEvaluator_handleSmask(smask, resources, operatorList, task, stateManager) {
      var smaskContent = smask.get('G');
      var smaskOptions = {
        subtype: smask.get('S').name,
        backdrop: smask.get('BC')
      };
      var transferObj = smask.get('TR');
      if ((0, _function.isPDFFunction)(transferObj)) {
        let transferFn = this.pdfFunctionFactory.create(transferObj);
        var transferMap = new Uint8Array(256);
        var tmp = new Float32Array(1);
        for (var i = 0; i < 256; i++) {
          tmp[0] = i / 255;
          transferFn(tmp, 0, tmp, 0);
          transferMap[i] = tmp[0] * 255 | 0;
        }
        smaskOptions.transferMap = transferMap;
      }
      return this.buildFormXObject(resources, smaskContent, smaskOptions, operatorList, task, stateManager.state.clone());
    },
    handleTilingType(fn, args, resources, pattern, patternDict, operatorList, task) {
      let tilingOpList = new _operator_list.OperatorList();
      let resourcesArray = [patternDict.get('Resources'), resources];
      let patternResources = _primitives.Dict.merge(this.xref, resourcesArray);
      return this.getOperatorList({
        stream: pattern,
        task,
        resources: patternResources,
        operatorList: tilingOpList
      }).then(function () {
        return (0, _pattern.getTilingPatternIR)({
          fnArray: tilingOpList.fnArray,
          argsArray: tilingOpList.argsArray
        }, patternDict, args);
      }).then(function (tilingPatternIR) {
        operatorList.addDependencies(tilingOpList.dependencies);
        operatorList.addOp(fn, tilingPatternIR);
      }, reason => {
        if (this.options.ignoreErrors) {
          this.handler.send('UnsupportedFeature', { featureId: _util.UNSUPPORTED_FEATURES.unknown });
          (0, _util.warn)(`handleTilingType - ignoring pattern: "${reason}".`);
          return;
        }
        throw reason;
      });
    },
    handleSetFont: function PartialEvaluator_handleSetFont(resources, fontArgs, fontRef, operatorList, task, state) {
      var fontName;
      if (fontArgs) {
        fontArgs = fontArgs.slice();
        fontName = fontArgs[0].name;
      }
      return this.loadFont(fontName, fontRef, resources).then(translated => {
        if (!translated.font.isType3Font) {
          return translated;
        }
        return translated.loadType3Data(this, resources, operatorList, task).then(function () {
          return translated;
        }).catch(reason => {
          this.handler.send('UnsupportedFeature', { featureId: _util.UNSUPPORTED_FEATURES.font });
          return new TranslatedFont('g_font_error', new _fonts.ErrorFont('Type3 font load error: ' + reason), translated.font);
        });
      }).then(translated => {
        state.font = translated.font;
        translated.send(this.handler);
        return translated.loadedName;
      });
    },
    handleText: function PartialEvaluator_handleText(chars, state) {
      var font = state.font;
      var glyphs = font.charsToGlyphs(chars);
      var isAddToPathSet = !!(state.textRenderingMode & _util.TextRenderingMode.ADD_TO_PATH_FLAG);
      if (font.data && (isAddToPathSet || this.options.disableFontFace || state.fillColorSpace.name === 'Pattern')) {
        var buildPath = fontChar => {
          if (!font.renderer.hasBuiltPath(fontChar)) {
            var path = font.renderer.getPathJs(fontChar);
            this.handler.send('commonobj', [font.loadedName + '_path_' + fontChar, 'FontPath', path]);
          }
        };
        for (var i = 0, ii = glyphs.length; i < ii; i++) {
          var glyph = glyphs[i];
          buildPath(glyph.fontChar);
          var accent = glyph.accent;
          if (accent && accent.fontChar) {
            buildPath(accent.fontChar);
          }
        }
      }
      return glyphs;
    },
    setGState: function PartialEvaluator_setGState(resources, gState, operatorList, task, stateManager) {
      var gStateObj = [];
      var gStateKeys = gState.getKeys();
      var promise = Promise.resolve();
      for (var i = 0, ii = gStateKeys.length; i < ii; i++) {
        let key = gStateKeys[i];
        let value = gState.get(key);
        switch (key) {
          case 'Type':
            break;
          case 'LW':
          case 'LC':
          case 'LJ':
          case 'ML':
          case 'D':
          case 'RI':
          case 'FL':
          case 'CA':
          case 'ca':
            gStateObj.push([key, value]);
            break;
          case 'Font':
            promise = promise.then(() => {
              return this.handleSetFont(resources, null, value[0], operatorList, task, stateManager.state).then(function (loadedName) {
                operatorList.addDependency(loadedName);
                gStateObj.push([key, [loadedName, value[1]]]);
              });
            });
            break;
          case 'BM':
            gStateObj.push([key, normalizeBlendMode(value)]);
            break;
          case 'SMask':
            if ((0, _primitives.isName)(value, 'None')) {
              gStateObj.push([key, false]);
              break;
            }
            if ((0, _primitives.isDict)(value)) {
              promise = promise.then(() => {
                return this.handleSMask(value, resources, operatorList, task, stateManager);
              });
              gStateObj.push([key, true]);
            } else {
              (0, _util.warn)('Unsupported SMask type');
            }
            break;
          case 'OP':
          case 'op':
          case 'OPM':
          case 'BG':
          case 'BG2':
          case 'UCR':
          case 'UCR2':
          case 'TR':
          case 'TR2':
          case 'HT':
          case 'SM':
          case 'SA':
          case 'AIS':
          case 'TK':
            (0, _util.info)('graphic state operator ' + key);
            break;
          default:
            (0, _util.info)('Unknown graphic state operator ' + key);
            break;
        }
      }
      return promise.then(function () {
        if (gStateObj.length > 0) {
          operatorList.addOp(_util.OPS.setGState, [gStateObj]);
        }
      });
    },
    loadFont: function PartialEvaluator_loadFont(fontName, font, resources) {
      function errorFont() {
        return Promise.resolve(new TranslatedFont('g_font_error', new _fonts.ErrorFont('Font ' + fontName + ' is not available'), font));
      }
      var fontRef,
          xref = this.xref;
      if (font) {
        if (!(0, _primitives.isRef)(font)) {
          throw new Error('The "font" object should be a reference.');
        }
        fontRef = font;
      } else {
        var fontRes = resources.get('Font');
        if (fontRes) {
          fontRef = fontRes.getRaw(fontName);
        } else {
          (0, _util.warn)('fontRes not available');
          return errorFont();
        }
      }
      if (!fontRef) {
        (0, _util.warn)('fontRef not available');
        return errorFont();
      }
      if (this.fontCache.has(fontRef)) {
        return this.fontCache.get(fontRef);
      }
      font = xref.fetchIfRef(fontRef);
      if (!(0, _primitives.isDict)(font)) {
        return errorFont();
      }
      if (font.translated) {
        return font.translated;
      }
      var fontCapability = (0, _util.createPromiseCapability)();
      var preEvaluatedFont = this.preEvaluateFont(font);
      var descriptor = preEvaluatedFont.descriptor;
      var fontRefIsRef = (0, _primitives.isRef)(fontRef),
          fontID;
      if (fontRefIsRef) {
        fontID = fontRef.toString();
      }
      if ((0, _primitives.isDict)(descriptor)) {
        if (!descriptor.fontAliases) {
          descriptor.fontAliases = Object.create(null);
        }
        var fontAliases = descriptor.fontAliases;
        var hash = preEvaluatedFont.hash;
        if (fontAliases[hash]) {
          var aliasFontRef = fontAliases[hash].aliasRef;
          if (fontRefIsRef && aliasFontRef && this.fontCache.has(aliasFontRef)) {
            this.fontCache.putAlias(fontRef, aliasFontRef);
            return this.fontCache.get(fontRef);
          }
        } else {
          fontAliases[hash] = { fontID: _fonts.Font.getFontID() };
        }
        if (fontRefIsRef) {
          fontAliases[hash].aliasRef = fontRef;
        }
        fontID = fontAliases[hash].fontID;
      }
      if (fontRefIsRef) {
        this.fontCache.put(fontRef, fontCapability.promise);
      } else {
        if (!fontID) {
          fontID = this.idFactory.createObjId();
        }
        this.fontCache.put('id_' + fontID, fontCapability.promise);
      }
      (0, _util.assert)(fontID, 'The "fontID" must be defined.');
      font.loadedName = 'g_' + this.pdfManager.docId + '_f' + fontID;
      font.translated = fontCapability.promise;
      var translatedPromise;
      try {
        translatedPromise = this.translateFont(preEvaluatedFont);
      } catch (e) {
        translatedPromise = Promise.reject(e);
      }
      translatedPromise.then(function (translatedFont) {
        if (translatedFont.fontType !== undefined) {
          var xrefFontStats = xref.stats.fontTypes;
          xrefFontStats[translatedFont.fontType] = true;
        }
        fontCapability.resolve(new TranslatedFont(font.loadedName, translatedFont, font));
      }).catch(reason => {
        this.handler.send('UnsupportedFeature', { featureId: _util.UNSUPPORTED_FEATURES.font });
        try {
          var descriptor = preEvaluatedFont.descriptor;
          var fontFile3 = descriptor && descriptor.get('FontFile3');
          var subtype = fontFile3 && fontFile3.get('Subtype');
          var fontType = (0, _fonts.getFontType)(preEvaluatedFont.type, subtype && subtype.name);
          var xrefFontStats = xref.stats.fontTypes;
          xrefFontStats[fontType] = true;
        } catch (ex) {}
        fontCapability.resolve(new TranslatedFont(font.loadedName, new _fonts.ErrorFont(reason instanceof Error ? reason.message : reason), font));
      });
      return fontCapability.promise;
    },
    buildPath: function PartialEvaluator_buildPath(operatorList, fn, args) {
      var lastIndex = operatorList.length - 1;
      if (!args) {
        args = [];
      }
      if (lastIndex < 0 || operatorList.fnArray[lastIndex] !== _util.OPS.constructPath) {
        operatorList.addOp(_util.OPS.constructPath, [[fn], args]);
      } else {
        var opArgs = operatorList.argsArray[lastIndex];
        opArgs[0].push(fn);
        Array.prototype.push.apply(opArgs[1], args);
      }
    },
    handleColorN: function PartialEvaluator_handleColorN(operatorList, fn, args, cs, patterns, resources, task) {
      var patternName = args[args.length - 1];
      var pattern;
      if ((0, _primitives.isName)(patternName) && (pattern = patterns.get(patternName.name))) {
        var dict = (0, _primitives.isStream)(pattern) ? pattern.dict : pattern;
        var typeNum = dict.get('PatternType');
        if (typeNum === TILING_PATTERN) {
          var color = cs.base ? cs.base.getRgb(args, 0) : null;
          return this.handleTilingType(fn, color, resources, pattern, dict, operatorList, task);
        } else if (typeNum === SHADING_PATTERN) {
          var shading = dict.get('Shading');
          var matrix = dict.getArray('Matrix');
          pattern = _pattern.Pattern.parseShading(shading, matrix, this.xref, resources, this.handler, this.pdfFunctionFactory);
          operatorList.addOp(fn, pattern.getIR());
          return Promise.resolve();
        }
        return Promise.reject(new Error('Unknown PatternType: ' + typeNum));
      }
      operatorList.addOp(fn, args);
      return Promise.resolve();
    },
    getOperatorList({ stream, task, resources, operatorList, initialState = null }) {
      resources = resources || _primitives.Dict.empty;
      initialState = initialState || new EvalState();
      if (!operatorList) {
        throw new Error('getOperatorList: missing "operatorList" parameter');
      }
      var self = this;
      var xref = this.xref;
      var imageCache = Object.create(null);
      var xobjs = resources.get('XObject') || _primitives.Dict.empty;
      var patterns = resources.get('Pattern') || _primitives.Dict.empty;
      var stateManager = new StateManager(initialState);
      var preprocessor = new EvaluatorPreprocessor(stream, xref, stateManager);
      var timeSlotManager = new TimeSlotManager();
      function closePendingRestoreOPS(argument) {
        for (var i = 0, ii = preprocessor.savedStatesDepth; i < ii; i++) {
          operatorList.addOp(_util.OPS.restore, []);
        }
      }
      return new Promise(function promiseBody(resolve, reject) {
        var next = function (promise) {
          promise.then(function () {
            try {
              promiseBody(resolve, reject);
            } catch (ex) {
              reject(ex);
            }
          }, reject);
        };
        task.ensureNotTerminated();
        timeSlotManager.reset();
        var stop,
            operation = {},
            i,
            ii,
            cs;
        while (!(stop = timeSlotManager.check())) {
          operation.args = null;
          if (!preprocessor.read(operation)) {
            break;
          }
          var args = operation.args;
          var fn = operation.fn;
          switch (fn | 0) {
            case _util.OPS.paintXObject:
              var name = args[0].name;
              if (name && imageCache[name] !== undefined) {
                operatorList.addOp(imageCache[name].fn, imageCache[name].args);
                args = null;
                continue;
              }
              next(new Promise(function (resolveXObject, rejectXObject) {
                if (!name) {
                  throw new _util.FormatError('XObject must be referred to by name.');
                }
                let xobj = xobjs.get(name);
                if (!xobj) {
                  operatorList.addOp(fn, args);
                  resolveXObject();
                  return;
                }
                if (!(0, _primitives.isStream)(xobj)) {
                  throw new _util.FormatError('XObject should be a stream');
                }
                let type = xobj.dict.get('Subtype');
                if (!(0, _primitives.isName)(type)) {
                  throw new _util.FormatError('XObject should have a Name subtype');
                }
                if (type.name === 'Form') {
                  stateManager.save();
                  self.buildFormXObject(resources, xobj, null, operatorList, task, stateManager.state.clone()).then(function () {
                    stateManager.restore();
                    resolveXObject();
                  }, rejectXObject);
                  return;
                } else if (type.name === 'Image') {
                  self.buildPaintImageXObject({
                    resources,
                    image: xobj,
                    operatorList,
                    cacheKey: name,
                    imageCache
                  }).then(resolveXObject, rejectXObject);
                  return;
                } else if (type.name === 'PS') {
                  (0, _util.info)('Ignored XObject subtype PS');
                } else {
                  throw new _util.FormatError(`Unhandled XObject subtype ${type.name}`);
                }
                resolveXObject();
              }).catch(function (reason) {
                if (self.options.ignoreErrors) {
                  self.handler.send('UnsupportedFeature', { featureId: _util.UNSUPPORTED_FEATURES.unknown });
                  (0, _util.warn)(`getOperatorList - ignoring XObject: "${reason}".`);
                  return;
                }
                throw reason;
              }));
              return;
            case _util.OPS.setFont:
              var fontSize = args[1];
              next(self.handleSetFont(resources, args, null, operatorList, task, stateManager.state).then(function (loadedName) {
                operatorList.addDependency(loadedName);
                operatorList.addOp(_util.OPS.setFont, [loadedName, fontSize]);
              }));
              return;
            case _util.OPS.endInlineImage:
              var cacheKey = args[0].cacheKey;
              if (cacheKey) {
                var cacheEntry = imageCache[cacheKey];
                if (cacheEntry !== undefined) {
                  operatorList.addOp(cacheEntry.fn, cacheEntry.args);
                  args = null;
                  continue;
                }
              }
              next(self.buildPaintImageXObject({
                resources,
                image: args[0],
                isInline: true,
                operatorList,
                cacheKey,
                imageCache
              }));
              return;
            case _util.OPS.showText:
              args[0] = self.handleText(args[0], stateManager.state);
              break;
            case _util.OPS.showSpacedText:
              var arr = args[0];
              var combinedGlyphs = [];
              var arrLength = arr.length;
              var state = stateManager.state;
              for (i = 0; i < arrLength; ++i) {
                var arrItem = arr[i];
                if ((0, _util.isString)(arrItem)) {
                  Array.prototype.push.apply(combinedGlyphs, self.handleText(arrItem, state));
                } else if ((0, _util.isNum)(arrItem)) {
                  combinedGlyphs.push(arrItem);
                }
              }
              args[0] = combinedGlyphs;
              fn = _util.OPS.showText;
              break;
            case _util.OPS.nextLineShowText:
              operatorList.addOp(_util.OPS.nextLine);
              args[0] = self.handleText(args[0], stateManager.state);
              fn = _util.OPS.showText;
              break;
            case _util.OPS.nextLineSetSpacingShowText:
              operatorList.addOp(_util.OPS.nextLine);
              operatorList.addOp(_util.OPS.setWordSpacing, [args.shift()]);
              operatorList.addOp(_util.OPS.setCharSpacing, [args.shift()]);
              args[0] = self.handleText(args[0], stateManager.state);
              fn = _util.OPS.showText;
              break;
            case _util.OPS.setTextRenderingMode:
              stateManager.state.textRenderingMode = args[0];
              break;
            case _util.OPS.setFillColorSpace:
              stateManager.state.fillColorSpace = _colorspace.ColorSpace.parse(args[0], xref, resources, self.pdfFunctionFactory);
              continue;
            case _util.OPS.setStrokeColorSpace:
              stateManager.state.strokeColorSpace = _colorspace.ColorSpace.parse(args[0], xref, resources, self.pdfFunctionFactory);
              continue;
            case _util.OPS.setFillColor:
              cs = stateManager.state.fillColorSpace;
              args = cs.getRgb(args, 0);
              fn = _util.OPS.setFillRGBColor;
              break;
            case _util.OPS.setStrokeColor:
              cs = stateManager.state.strokeColorSpace;
              args = cs.getRgb(args, 0);
              fn = _util.OPS.setStrokeRGBColor;
              break;
            case _util.OPS.setFillGray:
              stateManager.state.fillColorSpace = _colorspace.ColorSpace.singletons.gray;
              args = _colorspace.ColorSpace.singletons.gray.getRgb(args, 0);
              fn = _util.OPS.setFillRGBColor;
              break;
            case _util.OPS.setStrokeGray:
              stateManager.state.strokeColorSpace = _colorspace.ColorSpace.singletons.gray;
              args = _colorspace.ColorSpace.singletons.gray.getRgb(args, 0);
              fn = _util.OPS.setStrokeRGBColor;
              break;
            case _util.OPS.setFillCMYKColor:
              stateManager.state.fillColorSpace = _colorspace.ColorSpace.singletons.cmyk;
              args = _colorspace.ColorSpace.singletons.cmyk.getRgb(args, 0);
              fn = _util.OPS.setFillRGBColor;
              break;
            case _util.OPS.setStrokeCMYKColor:
              stateManager.state.strokeColorSpace = _colorspace.ColorSpace.singletons.cmyk;
              args = _colorspace.ColorSpace.singletons.cmyk.getRgb(args, 0);
              fn = _util.OPS.setStrokeRGBColor;
              break;
            case _util.OPS.setFillRGBColor:
              stateManager.state.fillColorSpace = _colorspace.ColorSpace.singletons.rgb;
              args = _colorspace.ColorSpace.singletons.rgb.getRgb(args, 0);
              break;
            case _util.OPS.setStrokeRGBColor:
              stateManager.state.strokeColorSpace = _colorspace.ColorSpace.singletons.rgb;
              args = _colorspace.ColorSpace.singletons.rgb.getRgb(args, 0);
              break;
            case _util.OPS.setFillColorN:
              cs = stateManager.state.fillColorSpace;
              if (cs.name === 'Pattern') {
                next(self.handleColorN(operatorList, _util.OPS.setFillColorN, args, cs, patterns, resources, task));
                return;
              }
              args = cs.getRgb(args, 0);
              fn = _util.OPS.setFillRGBColor;
              break;
            case _util.OPS.setStrokeColorN:
              cs = stateManager.state.strokeColorSpace;
              if (cs.name === 'Pattern') {
                next(self.handleColorN(operatorList, _util.OPS.setStrokeColorN, args, cs, patterns, resources, task));
                return;
              }
              args = cs.getRgb(args, 0);
              fn = _util.OPS.setStrokeRGBColor;
              break;
            case _util.OPS.shadingFill:
              var shadingRes = resources.get('Shading');
              if (!shadingRes) {
                throw new _util.FormatError('No shading resource found');
              }
              var shading = shadingRes.get(args[0].name);
              if (!shading) {
                throw new _util.FormatError('No shading object found');
              }
              var shadingFill = _pattern.Pattern.parseShading(shading, null, xref, resources, self.handler, self.pdfFunctionFactory);
              var patternIR = shadingFill.getIR();
              args = [patternIR];
              fn = _util.OPS.shadingFill;
              break;
            case _util.OPS.setGState:
              var dictName = args[0];
              var extGState = resources.get('ExtGState');
              if (!(0, _primitives.isDict)(extGState) || !extGState.has(dictName.name)) {
                break;
              }
              var gState = extGState.get(dictName.name);
              next(self.setGState(resources, gState, operatorList, task, stateManager));
              return;
            case _util.OPS.moveTo:
            case _util.OPS.lineTo:
            case _util.OPS.curveTo:
            case _util.OPS.curveTo2:
            case _util.OPS.curveTo3:
            case _util.OPS.closePath:
              self.buildPath(operatorList, fn, args);
              continue;
            case _util.OPS.rectangle:
              self.buildPath(operatorList, fn, args);
              continue;
            case _util.OPS.markPoint:
            case _util.OPS.markPointProps:
            case _util.OPS.beginMarkedContent:
            case _util.OPS.beginMarkedContentProps:
            case _util.OPS.endMarkedContent:
            case _util.OPS.beginCompat:
            case _util.OPS.endCompat:
              continue;
            default:
              if (args !== null) {
                for (i = 0, ii = args.length; i < ii; i++) {
                  if (args[i] instanceof _primitives.Dict) {
                    break;
                  }
                }
                if (i < ii) {
                  (0, _util.warn)('getOperatorList - ignoring operator: ' + fn);
                  continue;
                }
              }
          }
          operatorList.addOp(fn, args);
        }
        if (stop) {
          next(deferred);
          return;
        }
        closePendingRestoreOPS();
        resolve();
      }).catch(reason => {
        if (this.options.ignoreErrors) {
          this.handler.send('UnsupportedFeature', { featureId: _util.UNSUPPORTED_FEATURES.unknown });
          (0, _util.warn)('getOperatorList - ignoring errors during task: ' + task.name);
          closePendingRestoreOPS();
          return;
        }
        throw reason;
      });
    },
    getTextContent({ stream, task, resources, stateManager = null, normalizeWhitespace = false, combineTextItems = false, sink, seenStyles = Object.create(null) }) {
      resources = resources || _primitives.Dict.empty;
      stateManager = stateManager || new StateManager(new TextState());
      var WhitespaceRegexp = /\s/g;
      var textContent = {
        items: [],
        styles: Object.create(null)
      };
      var textContentItem = {
        initialized: false,
        str: [],
        width: 0,
        height: 0,
        vertical: false,
        lastAdvanceWidth: 0,
        lastAdvanceHeight: 0,
        textAdvanceScale: 0,
        spaceWidth: 0,
        fakeSpaceMin: Infinity,
        fakeMultiSpaceMin: Infinity,
        fakeMultiSpaceMax: -0,
        textRunBreakAllowed: false,
        transform: null,
        fontName: null
      };
      var SPACE_FACTOR = 0.3;
      var MULTI_SPACE_FACTOR = 1.5;
      var MULTI_SPACE_FACTOR_MAX = 4;
      var self = this;
      var xref = this.xref;
      var xobjs = null;
      var skipEmptyXObjs = Object.create(null);
      var preprocessor = new EvaluatorPreprocessor(stream, xref, stateManager);
      var textState;
      function ensureTextContentItem() {
        if (textContentItem.initialized) {
          return textContentItem;
        }
        var font = textState.font;
        if (!(font.loadedName in seenStyles)) {
          seenStyles[font.loadedName] = true;
          textContent.styles[font.loadedName] = {
            fontFamily: font.fallbackName,
            ascent: font.ascent,
            descent: font.descent,
            vertical: font.vertical
          };
        }
        textContentItem.fontName = font.loadedName;
        var tsm = [textState.fontSize * textState.textHScale, 0, 0, textState.fontSize, 0, textState.textRise];
        if (font.isType3Font && textState.fontMatrix !== _util.FONT_IDENTITY_MATRIX && textState.fontSize === 1) {
          var glyphHeight = font.bbox[3] - font.bbox[1];
          if (glyphHeight > 0) {
            glyphHeight = glyphHeight * textState.fontMatrix[3];
            tsm[3] *= glyphHeight;
          }
        }
        var trm = _util.Util.transform(textState.ctm, _util.Util.transform(textState.textMatrix, tsm));
        textContentItem.transform = trm;
        if (!font.vertical) {
          textContentItem.width = 0;
          textContentItem.height = Math.sqrt(trm[2] * trm[2] + trm[3] * trm[3]);
          textContentItem.vertical = false;
        } else {
          textContentItem.width = Math.sqrt(trm[0] * trm[0] + trm[1] * trm[1]);
          textContentItem.height = 0;
          textContentItem.vertical = true;
        }
        var a = textState.textLineMatrix[0];
        var b = textState.textLineMatrix[1];
        var scaleLineX = Math.sqrt(a * a + b * b);
        a = textState.ctm[0];
        b = textState.ctm[1];
        var scaleCtmX = Math.sqrt(a * a + b * b);
        textContentItem.textAdvanceScale = scaleCtmX * scaleLineX;
        textContentItem.lastAdvanceWidth = 0;
        textContentItem.lastAdvanceHeight = 0;
        var spaceWidth = font.spaceWidth / 1000 * textState.fontSize;
        if (spaceWidth) {
          textContentItem.spaceWidth = spaceWidth;
          textContentItem.fakeSpaceMin = spaceWidth * SPACE_FACTOR;
          textContentItem.fakeMultiSpaceMin = spaceWidth * MULTI_SPACE_FACTOR;
          textContentItem.fakeMultiSpaceMax = spaceWidth * MULTI_SPACE_FACTOR_MAX;
          textContentItem.textRunBreakAllowed = !font.isMonospace;
        } else {
          textContentItem.spaceWidth = 0;
          textContentItem.fakeSpaceMin = Infinity;
          textContentItem.fakeMultiSpaceMin = Infinity;
          textContentItem.fakeMultiSpaceMax = 0;
          textContentItem.textRunBreakAllowed = false;
        }
        textContentItem.initialized = true;
        return textContentItem;
      }
      function replaceWhitespace(str) {
        var i = 0,
            ii = str.length,
            code;
        while (i < ii && (code = str.charCodeAt(i)) >= 0x20 && code <= 0x7F) {
          i++;
        }
        return i < ii ? str.replace(WhitespaceRegexp, ' ') : str;
      }
      function runBidiTransform(textChunk) {
        var str = textChunk.str.join('');
        var bidiResult = (0, _bidi.bidi)(str, -1, textChunk.vertical);
        return {
          str: normalizeWhitespace ? replaceWhitespace(bidiResult.str) : bidiResult.str,
          dir: bidiResult.dir,
          width: textChunk.width,
          height: textChunk.height,
          transform: textChunk.transform,
          fontName: textChunk.fontName
        };
      }
      function handleSetFont(fontName, fontRef) {
        return self.loadFont(fontName, fontRef, resources).then(function (translated) {
          textState.font = translated.font;
          textState.fontMatrix = translated.font.fontMatrix || _util.FONT_IDENTITY_MATRIX;
        });
      }
      function buildTextContentItem(chars) {
        var font = textState.font;
        var textChunk = ensureTextContentItem();
        var width = 0;
        var height = 0;
        var glyphs = font.charsToGlyphs(chars);
        for (var i = 0; i < glyphs.length; i++) {
          var glyph = glyphs[i];
          var glyphWidth = null;
          if (font.vertical && glyph.vmetric) {
            glyphWidth = glyph.vmetric[0];
          } else {
            glyphWidth = glyph.width;
          }
          var glyphUnicode = glyph.unicode;
          var NormalizedUnicodes = (0, _unicode.getNormalizedUnicodes)();
          if (NormalizedUnicodes[glyphUnicode] !== undefined) {
            glyphUnicode = NormalizedUnicodes[glyphUnicode];
          }
          glyphUnicode = (0, _unicode.reverseIfRtl)(glyphUnicode);
          var charSpacing = textState.charSpacing;
          if (glyph.isSpace) {
            var wordSpacing = textState.wordSpacing;
            charSpacing += wordSpacing;
            if (wordSpacing > 0) {
              addFakeSpaces(wordSpacing, textChunk.str);
            }
          }
          var tx = 0;
          var ty = 0;
          if (!font.vertical) {
            var w0 = glyphWidth * textState.fontMatrix[0];
            tx = (w0 * textState.fontSize + charSpacing) * textState.textHScale;
            width += tx;
          } else {
            var w1 = glyphWidth * textState.fontMatrix[0];
            ty = w1 * textState.fontSize + charSpacing;
            height += ty;
          }
          textState.translateTextMatrix(tx, ty);
          textChunk.str.push(glyphUnicode);
        }
        if (!font.vertical) {
          textChunk.lastAdvanceWidth = width;
          textChunk.width += width;
        } else {
          textChunk.lastAdvanceHeight = height;
          textChunk.height += Math.abs(height);
        }
        return textChunk;
      }
      function addFakeSpaces(width, strBuf) {
        if (width < textContentItem.fakeSpaceMin) {
          return;
        }
        if (width < textContentItem.fakeMultiSpaceMin) {
          strBuf.push(' ');
          return;
        }
        var fakeSpaces = Math.round(width / textContentItem.spaceWidth);
        while (fakeSpaces-- > 0) {
          strBuf.push(' ');
        }
      }
      function flushTextContentItem() {
        if (!textContentItem.initialized) {
          return;
        }
        textContentItem.width *= textContentItem.textAdvanceScale;
        textContentItem.height *= textContentItem.textAdvanceScale;
        textContent.items.push(runBidiTransform(textContentItem));
        textContentItem.initialized = false;
        textContentItem.str.length = 0;
      }
      function enqueueChunk() {
        let length = textContent.items.length;
        if (length > 0) {
          sink.enqueue(textContent, length);
          textContent.items = [];
          textContent.styles = Object.create(null);
        }
      }
      var timeSlotManager = new TimeSlotManager();
      return new Promise(function promiseBody(resolve, reject) {
        let next = function (promise) {
          enqueueChunk();
          Promise.all([promise, sink.ready]).then(function () {
            try {
              promiseBody(resolve, reject);
            } catch (ex) {
              reject(ex);
            }
          }, reject);
        };
        task.ensureNotTerminated();
        timeSlotManager.reset();
        var stop,
            operation = {},
            args = [];
        while (!(stop = timeSlotManager.check())) {
          args.length = 0;
          operation.args = args;
          if (!preprocessor.read(operation)) {
            break;
          }
          textState = stateManager.state;
          var fn = operation.fn;
          args = operation.args;
          var advance, diff;
          switch (fn | 0) {
            case _util.OPS.setFont:
              var fontNameArg = args[0].name,
                  fontSizeArg = args[1];
              if (textState.font && fontNameArg === textState.fontName && fontSizeArg === textState.fontSize) {
                break;
              }
              flushTextContentItem();
              textState.fontName = fontNameArg;
              textState.fontSize = fontSizeArg;
              next(handleSetFont(fontNameArg, null));
              return;
            case _util.OPS.setTextRise:
              flushTextContentItem();
              textState.textRise = args[0];
              break;
            case _util.OPS.setHScale:
              flushTextContentItem();
              textState.textHScale = args[0] / 100;
              break;
            case _util.OPS.setLeading:
              flushTextContentItem();
              textState.leading = args[0];
              break;
            case _util.OPS.moveText:
              var isSameTextLine = !textState.font ? false : (textState.font.vertical ? args[0] : args[1]) === 0;
              advance = args[0] - args[1];
              if (combineTextItems && isSameTextLine && textContentItem.initialized && advance > 0 && advance <= textContentItem.fakeMultiSpaceMax) {
                textState.translateTextLineMatrix(args[0], args[1]);
                textContentItem.width += args[0] - textContentItem.lastAdvanceWidth;
                textContentItem.height += args[1] - textContentItem.lastAdvanceHeight;
                diff = args[0] - textContentItem.lastAdvanceWidth - (args[1] - textContentItem.lastAdvanceHeight);
                addFakeSpaces(diff, textContentItem.str);
                break;
              }
              flushTextContentItem();
              textState.translateTextLineMatrix(args[0], args[1]);
              textState.textMatrix = textState.textLineMatrix.slice();
              break;
            case _util.OPS.setLeadingMoveText:
              flushTextContentItem();
              textState.leading = -args[1];
              textState.translateTextLineMatrix(args[0], args[1]);
              textState.textMatrix = textState.textLineMatrix.slice();
              break;
            case _util.OPS.nextLine:
              flushTextContentItem();
              textState.carriageReturn();
              break;
            case _util.OPS.setTextMatrix:
              advance = textState.calcTextLineMatrixAdvance(args[0], args[1], args[2], args[3], args[4], args[5]);
              if (combineTextItems && advance !== null && textContentItem.initialized && advance.value > 0 && advance.value <= textContentItem.fakeMultiSpaceMax) {
                textState.translateTextLineMatrix(advance.width, advance.height);
                textContentItem.width += advance.width - textContentItem.lastAdvanceWidth;
                textContentItem.height += advance.height - textContentItem.lastAdvanceHeight;
                diff = advance.width - textContentItem.lastAdvanceWidth - (advance.height - textContentItem.lastAdvanceHeight);
                addFakeSpaces(diff, textContentItem.str);
                break;
              }
              flushTextContentItem();
              textState.setTextMatrix(args[0], args[1], args[2], args[3], args[4], args[5]);
              textState.setTextLineMatrix(args[0], args[1], args[2], args[3], args[4], args[5]);
              break;
            case _util.OPS.setCharSpacing:
              textState.charSpacing = args[0];
              break;
            case _util.OPS.setWordSpacing:
              textState.wordSpacing = args[0];
              break;
            case _util.OPS.beginText:
              flushTextContentItem();
              textState.textMatrix = _util.IDENTITY_MATRIX.slice();
              textState.textLineMatrix = _util.IDENTITY_MATRIX.slice();
              break;
            case _util.OPS.showSpacedText:
              var items = args[0];
              var offset;
              for (var j = 0, jj = items.length; j < jj; j++) {
                if (typeof items[j] === 'string') {
                  buildTextContentItem(items[j]);
                } else if ((0, _util.isNum)(items[j])) {
                  ensureTextContentItem();
                  advance = items[j] * textState.fontSize / 1000;
                  var breakTextRun = false;
                  if (textState.font.vertical) {
                    offset = advance;
                    textState.translateTextMatrix(0, offset);
                    breakTextRun = textContentItem.textRunBreakAllowed && advance > textContentItem.fakeMultiSpaceMax;
                    if (!breakTextRun) {
                      textContentItem.height += offset;
                    }
                  } else {
                    advance = -advance;
                    offset = advance * textState.textHScale;
                    textState.translateTextMatrix(offset, 0);
                    breakTextRun = textContentItem.textRunBreakAllowed && advance > textContentItem.fakeMultiSpaceMax;
                    if (!breakTextRun) {
                      textContentItem.width += offset;
                    }
                  }
                  if (breakTextRun) {
                    flushTextContentItem();
                  } else if (advance > 0) {
                    addFakeSpaces(advance, textContentItem.str);
                  }
                }
              }
              break;
            case _util.OPS.showText:
              buildTextContentItem(args[0]);
              break;
            case _util.OPS.nextLineShowText:
              flushTextContentItem();
              textState.carriageReturn();
              buildTextContentItem(args[0]);
              break;
            case _util.OPS.nextLineSetSpacingShowText:
              flushTextContentItem();
              textState.wordSpacing = args[0];
              textState.charSpacing = args[1];
              textState.carriageReturn();
              buildTextContentItem(args[2]);
              break;
            case _util.OPS.paintXObject:
              flushTextContentItem();
              if (!xobjs) {
                xobjs = resources.get('XObject') || _primitives.Dict.empty;
              }
              var name = args[0].name;
              if (name && skipEmptyXObjs[name] !== undefined) {
                break;
              }
              next(new Promise(function (resolveXObject, rejectXObject) {
                if (!name) {
                  throw new _util.FormatError('XObject must be referred to by name.');
                }
                let xobj = xobjs.get(name);
                if (!xobj) {
                  resolveXObject();
                  return;
                }
                if (!(0, _primitives.isStream)(xobj)) {
                  throw new _util.FormatError('XObject should be a stream');
                }
                let type = xobj.dict.get('Subtype');
                if (!(0, _primitives.isName)(type)) {
                  throw new _util.FormatError('XObject should have a Name subtype');
                }
                if (type.name !== 'Form') {
                  skipEmptyXObjs[name] = true;
                  resolveXObject();
                  return;
                }
                let currentState = stateManager.state.clone();
                let xObjStateManager = new StateManager(currentState);
                let matrix = xobj.dict.getArray('Matrix');
                if (Array.isArray(matrix) && matrix.length === 6) {
                  xObjStateManager.transform(matrix);
                }
                enqueueChunk();
                let sinkWrapper = {
                  enqueueInvoked: false,
                  enqueue(chunk, size) {
                    this.enqueueInvoked = true;
                    sink.enqueue(chunk, size);
                  },
                  get desiredSize() {
                    return sink.desiredSize;
                  },
                  get ready() {
                    return sink.ready;
                  }
                };
                self.getTextContent({
                  stream: xobj,
                  task,
                  resources: xobj.dict.get('Resources') || resources,
                  stateManager: xObjStateManager,
                  normalizeWhitespace,
                  combineTextItems,
                  sink: sinkWrapper,
                  seenStyles
                }).then(function () {
                  if (!sinkWrapper.enqueueInvoked) {
                    skipEmptyXObjs[name] = true;
                  }
                  resolveXObject();
                }, rejectXObject);
              }).catch(function (reason) {
                if (reason instanceof _util.AbortException) {
                  return;
                }
                if (self.options.ignoreErrors) {
                  (0, _util.warn)(`getTextContent - ignoring XObject: "${reason}".`);
                  return;
                }
                throw reason;
              }));
              return;
            case _util.OPS.setGState:
              flushTextContentItem();
              var dictName = args[0];
              var extGState = resources.get('ExtGState');
              if (!(0, _primitives.isDict)(extGState) || !(0, _primitives.isName)(dictName)) {
                break;
              }
              var gState = extGState.get(dictName.name);
              if (!(0, _primitives.isDict)(gState)) {
                break;
              }
              var gStateFont = gState.get('Font');
              if (gStateFont) {
                textState.fontName = null;
                textState.fontSize = gStateFont[1];
                next(handleSetFont(null, gStateFont[0]));
                return;
              }
              break;
          }
          if (textContent.items.length >= sink.desiredSize) {
            stop = true;
            break;
          }
        }
        if (stop) {
          next(deferred);
          return;
        }
        flushTextContentItem();
        enqueueChunk();
        resolve();
      }).catch(reason => {
        if (reason instanceof _util.AbortException) {
          return;
        }
        if (this.options.ignoreErrors) {
          (0, _util.warn)('getTextContent - ignoring errors during task: ' + task.name);
          flushTextContentItem();
          enqueueChunk();
          return;
        }
        throw reason;
      });
    },
    extractDataStructures: function PartialEvaluator_extractDataStructures(dict, baseDict, properties) {
      var xref = this.xref;
      var toUnicode = dict.get('ToUnicode') || baseDict.get('ToUnicode');
      var toUnicodePromise = toUnicode ? this.readToUnicode(toUnicode) : Promise.resolve(undefined);
      if (properties.composite) {
        var cidSystemInfo = dict.get('CIDSystemInfo');
        if ((0, _primitives.isDict)(cidSystemInfo)) {
          properties.cidSystemInfo = {
            registry: cidSystemInfo.get('Registry'),
            ordering: cidSystemInfo.get('Ordering'),
            supplement: cidSystemInfo.get('Supplement')
          };
        }
        var cidToGidMap = dict.get('CIDToGIDMap');
        if ((0, _primitives.isStream)(cidToGidMap)) {
          properties.cidToGidMap = this.readCidToGidMap(cidToGidMap);
        }
      }
      var differences = [];
      var baseEncodingName = null;
      var encoding;
      if (dict.has('Encoding')) {
        encoding = dict.get('Encoding');
        if ((0, _primitives.isDict)(encoding)) {
          baseEncodingName = encoding.get('BaseEncoding');
          baseEncodingName = (0, _primitives.isName)(baseEncodingName) ? baseEncodingName.name : null;
          if (encoding.has('Differences')) {
            var diffEncoding = encoding.get('Differences');
            var index = 0;
            for (var j = 0, jj = diffEncoding.length; j < jj; j++) {
              var data = xref.fetchIfRef(diffEncoding[j]);
              if ((0, _util.isNum)(data)) {
                index = data;
              } else if ((0, _primitives.isName)(data)) {
                differences[index++] = data.name;
              } else {
                throw new _util.FormatError(`Invalid entry in 'Differences' array: ${data}`);
              }
            }
          }
        } else if ((0, _primitives.isName)(encoding)) {
          baseEncodingName = encoding.name;
        } else {
          throw new _util.FormatError('Encoding is not a Name nor a Dict');
        }
        if (baseEncodingName !== 'MacRomanEncoding' && baseEncodingName !== 'MacExpertEncoding' && baseEncodingName !== 'WinAnsiEncoding') {
          baseEncodingName = null;
        }
      }
      if (baseEncodingName) {
        properties.defaultEncoding = (0, _encodings.getEncoding)(baseEncodingName).slice();
      } else {
        var isSymbolicFont = !!(properties.flags & _fonts.FontFlags.Symbolic);
        var isNonsymbolicFont = !!(properties.flags & _fonts.FontFlags.Nonsymbolic);
        encoding = _encodings.StandardEncoding;
        if (properties.type === 'TrueType' && !isNonsymbolicFont) {
          encoding = _encodings.WinAnsiEncoding;
        }
        if (isSymbolicFont) {
          encoding = _encodings.MacRomanEncoding;
          if (!properties.file) {
            if (/Symbol/i.test(properties.name)) {
              encoding = _encodings.SymbolSetEncoding;
            } else if (/Dingbats/i.test(properties.name)) {
              encoding = _encodings.ZapfDingbatsEncoding;
            }
          }
        }
        properties.defaultEncoding = encoding;
      }
      properties.differences = differences;
      properties.baseEncodingName = baseEncodingName;
      properties.hasEncoding = !!baseEncodingName || differences.length > 0;
      properties.dict = dict;
      return toUnicodePromise.then(toUnicode => {
        properties.toUnicode = toUnicode;
        return this.buildToUnicode(properties);
      }).then(function (toUnicode) {
        properties.toUnicode = toUnicode;
        return properties;
      });
    },
    _buildSimpleFontToUnicode(properties) {
      (0, _util.assert)(!properties.composite, 'Must be a simple font.');
      let toUnicode = [],
          charcode,
          glyphName;
      let encoding = properties.defaultEncoding.slice();
      let baseEncodingName = properties.baseEncodingName;
      let differences = properties.differences;
      for (charcode in differences) {
        glyphName = differences[charcode];
        if (glyphName === '.notdef') {
          continue;
        }
        encoding[charcode] = glyphName;
      }
      let glyphsUnicodeMap = (0, _glyphlist.getGlyphsUnicode)();
      for (charcode in encoding) {
        glyphName = encoding[charcode];
        if (glyphName === '') {
          continue;
        } else if (glyphsUnicodeMap[glyphName] === undefined) {
          let code = 0;
          switch (glyphName[0]) {
            case 'G':
              if (glyphName.length === 3) {
                code = parseInt(glyphName.substr(1), 16);
              }
              break;
            case 'g':
              if (glyphName.length === 5) {
                code = parseInt(glyphName.substr(1), 16);
              }
              break;
            case 'C':
            case 'c':
              if (glyphName.length >= 3) {
                code = +glyphName.substr(1);
              }
              break;
            default:
              let unicode = (0, _unicode.getUnicodeForGlyph)(glyphName, glyphsUnicodeMap);
              if (unicode !== -1) {
                code = unicode;
              }
          }
          if (code) {
            if (baseEncodingName && code === +charcode) {
              let baseEncoding = (0, _encodings.getEncoding)(baseEncodingName);
              if (baseEncoding && (glyphName = baseEncoding[charcode])) {
                toUnicode[charcode] = String.fromCharCode(glyphsUnicodeMap[glyphName]);
                continue;
              }
            }
            toUnicode[charcode] = String.fromCharCode(code);
          }
          continue;
        }
        toUnicode[charcode] = String.fromCharCode(glyphsUnicodeMap[glyphName]);
      }
      return new _fonts.ToUnicodeMap(toUnicode);
    },
    buildToUnicode(properties) {
      properties.hasIncludedToUnicodeMap = !!properties.toUnicode && properties.toUnicode.length > 0;
      if (properties.hasIncludedToUnicodeMap) {
        if (!properties.composite && properties.hasEncoding) {
          properties.fallbackToUnicode = this._buildSimpleFontToUnicode(properties);
        }
        return Promise.resolve(properties.toUnicode);
      }
      if (!properties.composite) {
        return Promise.resolve(this._buildSimpleFontToUnicode(properties));
      }
      if (properties.composite && (properties.cMap.builtInCMap && !(properties.cMap instanceof _cmap.IdentityCMap) || properties.cidSystemInfo.registry === 'Adobe' && (properties.cidSystemInfo.ordering === 'GB1' || properties.cidSystemInfo.ordering === 'CNS1' || properties.cidSystemInfo.ordering === 'Japan1' || properties.cidSystemInfo.ordering === 'Korea1'))) {
        let registry = properties.cidSystemInfo.registry;
        let ordering = properties.cidSystemInfo.ordering;
        let ucs2CMapName = _primitives.Name.get(registry + '-' + ordering + '-UCS2');
        return _cmap.CMapFactory.create({
          encoding: ucs2CMapName,
          fetchBuiltInCMap: this.fetchBuiltInCMap,
          useCMap: null
        }).then(function (ucs2CMap) {
          let cMap = properties.cMap;
          let toUnicode = [];
          cMap.forEach(function (charcode, cid) {
            if (cid > 0xffff) {
              throw new _util.FormatError('Max size of CID is 65,535');
            }
            let ucs2 = ucs2CMap.lookup(cid);
            if (ucs2) {
              toUnicode[charcode] = String.fromCharCode((ucs2.charCodeAt(0) << 8) + ucs2.charCodeAt(1));
            }
          });
          return new _fonts.ToUnicodeMap(toUnicode);
        });
      }
      return Promise.resolve(new _fonts.IdentityToUnicodeMap(properties.firstChar, properties.lastChar));
    },
    readToUnicode: function PartialEvaluator_readToUnicode(toUnicode) {
      var cmapObj = toUnicode;
      if ((0, _primitives.isName)(cmapObj)) {
        return _cmap.CMapFactory.create({
          encoding: cmapObj,
          fetchBuiltInCMap: this.fetchBuiltInCMap,
          useCMap: null
        }).then(function (cmap) {
          if (cmap instanceof _cmap.IdentityCMap) {
            return new _fonts.IdentityToUnicodeMap(0, 0xFFFF);
          }
          return new _fonts.ToUnicodeMap(cmap.getMap());
        });
      } else if ((0, _primitives.isStream)(cmapObj)) {
        return _cmap.CMapFactory.create({
          encoding: cmapObj,
          fetchBuiltInCMap: this.fetchBuiltInCMap,
          useCMap: null
        }).then(function (cmap) {
          if (cmap instanceof _cmap.IdentityCMap) {
            return new _fonts.IdentityToUnicodeMap(0, 0xFFFF);
          }
          var map = new Array(cmap.length);
          cmap.forEach(function (charCode, token) {
            var str = [];
            for (var k = 0; k < token.length; k += 2) {
              var w1 = token.charCodeAt(k) << 8 | token.charCodeAt(k + 1);
              if ((w1 & 0xF800) !== 0xD800) {
                str.push(w1);
                continue;
              }
              k += 2;
              var w2 = token.charCodeAt(k) << 8 | token.charCodeAt(k + 1);
              str.push(((w1 & 0x3ff) << 10) + (w2 & 0x3ff) + 0x10000);
            }
            map[charCode] = String.fromCharCode.apply(String, str);
          });
          return new _fonts.ToUnicodeMap(map);
        });
      }
      return Promise.resolve(null);
    },
    readCidToGidMap: function PartialEvaluator_readCidToGidMap(cidToGidStream) {
      var glyphsData = cidToGidStream.getBytes();
      var result = [];
      for (var j = 0, jj = glyphsData.length; j < jj; j++) {
        var glyphID = glyphsData[j++] << 8 | glyphsData[j];
        if (glyphID === 0) {
          continue;
        }
        var code = j >> 1;
        result[code] = glyphID;
      }
      return result;
    },
    extractWidths: function PartialEvaluator_extractWidths(dict, descriptor, properties) {
      var xref = this.xref;
      var glyphsWidths = [];
      var defaultWidth = 0;
      var glyphsVMetrics = [];
      var defaultVMetrics;
      var i, ii, j, jj, start, code, widths;
      if (properties.composite) {
        defaultWidth = dict.has('DW') ? dict.get('DW') : 1000;
        widths = dict.get('W');
        if (widths) {
          for (i = 0, ii = widths.length; i < ii; i++) {
            start = xref.fetchIfRef(widths[i++]);
            code = xref.fetchIfRef(widths[i]);
            if (Array.isArray(code)) {
              for (j = 0, jj = code.length; j < jj; j++) {
                glyphsWidths[start++] = xref.fetchIfRef(code[j]);
              }
            } else {
              var width = xref.fetchIfRef(widths[++i]);
              for (j = start; j <= code; j++) {
                glyphsWidths[j] = width;
              }
            }
          }
        }
        if (properties.vertical) {
          var vmetrics = dict.getArray('DW2') || [880, -1000];
          defaultVMetrics = [vmetrics[1], defaultWidth * 0.5, vmetrics[0]];
          vmetrics = dict.get('W2');
          if (vmetrics) {
            for (i = 0, ii = vmetrics.length; i < ii; i++) {
              start = xref.fetchIfRef(vmetrics[i++]);
              code = xref.fetchIfRef(vmetrics[i]);
              if (Array.isArray(code)) {
                for (j = 0, jj = code.length; j < jj; j++) {
                  glyphsVMetrics[start++] = [xref.fetchIfRef(code[j++]), xref.fetchIfRef(code[j++]), xref.fetchIfRef(code[j])];
                }
              } else {
                var vmetric = [xref.fetchIfRef(vmetrics[++i]), xref.fetchIfRef(vmetrics[++i]), xref.fetchIfRef(vmetrics[++i])];
                for (j = start; j <= code; j++) {
                  glyphsVMetrics[j] = vmetric;
                }
              }
            }
          }
        }
      } else {
        var firstChar = properties.firstChar;
        widths = dict.get('Widths');
        if (widths) {
          j = firstChar;
          for (i = 0, ii = widths.length; i < ii; i++) {
            glyphsWidths[j++] = xref.fetchIfRef(widths[i]);
          }
          defaultWidth = parseFloat(descriptor.get('MissingWidth')) || 0;
        } else {
          var baseFontName = dict.get('BaseFont');
          if ((0, _primitives.isName)(baseFontName)) {
            var metrics = this.getBaseFontMetrics(baseFontName.name);
            glyphsWidths = this.buildCharCodeToWidth(metrics.widths, properties);
            defaultWidth = metrics.defaultWidth;
          }
        }
      }
      var isMonospace = true;
      var firstWidth = defaultWidth;
      for (var glyph in glyphsWidths) {
        var glyphWidth = glyphsWidths[glyph];
        if (!glyphWidth) {
          continue;
        }
        if (!firstWidth) {
          firstWidth = glyphWidth;
          continue;
        }
        if (firstWidth !== glyphWidth) {
          isMonospace = false;
          break;
        }
      }
      if (isMonospace) {
        properties.flags |= _fonts.FontFlags.FixedPitch;
      }
      properties.defaultWidth = defaultWidth;
      properties.widths = glyphsWidths;
      properties.defaultVMetrics = defaultVMetrics;
      properties.vmetrics = glyphsVMetrics;
    },
    isSerifFont: function PartialEvaluator_isSerifFont(baseFontName) {
      var fontNameWoStyle = baseFontName.split('-')[0];
      return fontNameWoStyle in (0, _standard_fonts.getSerifFonts)() || fontNameWoStyle.search(/serif/gi) !== -1;
    },
    getBaseFontMetrics: function PartialEvaluator_getBaseFontMetrics(name) {
      var defaultWidth = 0;
      var widths = [];
      var monospace = false;
      var stdFontMap = (0, _standard_fonts.getStdFontMap)();
      var lookupName = stdFontMap[name] || name;
      var Metrics = (0, _metrics.getMetrics)();
      if (!(lookupName in Metrics)) {
        if (this.isSerifFont(name)) {
          lookupName = 'Times-Roman';
        } else {
          lookupName = 'Helvetica';
        }
      }
      var glyphWidths = Metrics[lookupName];
      if ((0, _util.isNum)(glyphWidths)) {
        defaultWidth = glyphWidths;
        monospace = true;
      } else {
        widths = glyphWidths();
      }
      return {
        defaultWidth,
        monospace,
        widths
      };
    },
    buildCharCodeToWidth: function PartialEvaluator_bulildCharCodeToWidth(widthsByGlyphName, properties) {
      var widths = Object.create(null);
      var differences = properties.differences;
      var encoding = properties.defaultEncoding;
      for (var charCode = 0; charCode < 256; charCode++) {
        if (charCode in differences && widthsByGlyphName[differences[charCode]]) {
          widths[charCode] = widthsByGlyphName[differences[charCode]];
          continue;
        }
        if (charCode in encoding && widthsByGlyphName[encoding[charCode]]) {
          widths[charCode] = widthsByGlyphName[encoding[charCode]];
          continue;
        }
      }
      return widths;
    },
    preEvaluateFont: function PartialEvaluator_preEvaluateFont(dict) {
      var baseDict = dict;
      var type = dict.get('Subtype');
      if (!(0, _primitives.isName)(type)) {
        throw new _util.FormatError('invalid font Subtype');
      }
      var composite = false;
      var uint8array;
      if (type.name === 'Type0') {
        var df = dict.get('DescendantFonts');
        if (!df) {
          throw new _util.FormatError('Descendant fonts are not specified');
        }
        dict = Array.isArray(df) ? this.xref.fetchIfRef(df[0]) : df;
        type = dict.get('Subtype');
        if (!(0, _primitives.isName)(type)) {
          throw new _util.FormatError('invalid font Subtype');
        }
        composite = true;
      }
      var descriptor = dict.get('FontDescriptor');
      if (descriptor) {
        var hash = new _murmurhash.MurmurHash3_64();
        var encoding = baseDict.getRaw('Encoding');
        if ((0, _primitives.isName)(encoding)) {
          hash.update(encoding.name);
        } else if ((0, _primitives.isRef)(encoding)) {
          hash.update(encoding.toString());
        } else if ((0, _primitives.isDict)(encoding)) {
          var keys = encoding.getKeys();
          for (var i = 0, ii = keys.length; i < ii; i++) {
            var entry = encoding.getRaw(keys[i]);
            if ((0, _primitives.isName)(entry)) {
              hash.update(entry.name);
            } else if ((0, _primitives.isRef)(entry)) {
              hash.update(entry.toString());
            } else if (Array.isArray(entry)) {
              var diffLength = entry.length,
                  diffBuf = new Array(diffLength);
              for (var j = 0; j < diffLength; j++) {
                var diffEntry = entry[j];
                if ((0, _primitives.isName)(diffEntry)) {
                  diffBuf[j] = diffEntry.name;
                } else if ((0, _util.isNum)(diffEntry) || (0, _primitives.isRef)(diffEntry)) {
                  diffBuf[j] = diffEntry.toString();
                }
              }
              hash.update(diffBuf.join());
            }
          }
        }
        var toUnicode = dict.get('ToUnicode') || baseDict.get('ToUnicode');
        if ((0, _primitives.isStream)(toUnicode)) {
          var stream = toUnicode.str || toUnicode;
          uint8array = stream.buffer ? new Uint8Array(stream.buffer.buffer, 0, stream.bufferLength) : new Uint8Array(stream.bytes.buffer, stream.start, stream.end - stream.start);
          hash.update(uint8array);
        } else if ((0, _primitives.isName)(toUnicode)) {
          hash.update(toUnicode.name);
        }
        var widths = dict.get('Widths') || baseDict.get('Widths');
        if (widths) {
          uint8array = new Uint8Array(new Uint32Array(widths).buffer);
          hash.update(uint8array);
        }
      }
      return {
        descriptor,
        dict,
        baseDict,
        composite,
        type: type.name,
        hash: hash ? hash.hexdigest() : ''
      };
    },
    translateFont: function PartialEvaluator_translateFont(preEvaluatedFont) {
      var baseDict = preEvaluatedFont.baseDict;
      var dict = preEvaluatedFont.dict;
      var composite = preEvaluatedFont.composite;
      var descriptor = preEvaluatedFont.descriptor;
      var type = preEvaluatedFont.type;
      var maxCharIndex = composite ? 0xFFFF : 0xFF;
      var properties;
      if (!descriptor) {
        if (type === 'Type3') {
          descriptor = new _primitives.Dict(null);
          descriptor.set('FontName', _primitives.Name.get(type));
          descriptor.set('FontBBox', dict.getArray('FontBBox'));
        } else {
          var baseFontName = dict.get('BaseFont');
          if (!(0, _primitives.isName)(baseFontName)) {
            throw new _util.FormatError('Base font is not specified');
          }
          baseFontName = baseFontName.name.replace(/[,_]/g, '-');
          var metrics = this.getBaseFontMetrics(baseFontName);
          var fontNameWoStyle = baseFontName.split('-')[0];
          var flags = (this.isSerifFont(fontNameWoStyle) ? _fonts.FontFlags.Serif : 0) | (metrics.monospace ? _fonts.FontFlags.FixedPitch : 0) | ((0, _standard_fonts.getSymbolsFonts)()[fontNameWoStyle] ? _fonts.FontFlags.Symbolic : _fonts.FontFlags.Nonsymbolic);
          properties = {
            type,
            name: baseFontName,
            widths: metrics.widths,
            defaultWidth: metrics.defaultWidth,
            flags,
            firstChar: 0,
            lastChar: maxCharIndex
          };
          return this.extractDataStructures(dict, dict, properties).then(properties => {
            properties.widths = this.buildCharCodeToWidth(metrics.widths, properties);
            return new _fonts.Font(baseFontName, null, properties);
          });
        }
      }
      var firstChar = dict.get('FirstChar') || 0;
      var lastChar = dict.get('LastChar') || maxCharIndex;
      var fontName = descriptor.get('FontName');
      var baseFont = dict.get('BaseFont');
      if ((0, _util.isString)(fontName)) {
        fontName = _primitives.Name.get(fontName);
      }
      if ((0, _util.isString)(baseFont)) {
        baseFont = _primitives.Name.get(baseFont);
      }
      if (type !== 'Type3') {
        var fontNameStr = fontName && fontName.name;
        var baseFontStr = baseFont && baseFont.name;
        if (fontNameStr !== baseFontStr) {
          (0, _util.info)('The FontDescriptor\'s FontName is "' + fontNameStr + '" but should be the same as the Font\'s BaseFont "' + baseFontStr + '"');
          if (fontNameStr && baseFontStr && baseFontStr.indexOf(fontNameStr) === 0) {
            fontName = baseFont;
          }
        }
      }
      fontName = fontName || baseFont;
      if (!(0, _primitives.isName)(fontName)) {
        throw new _util.FormatError('invalid font name');
      }
      var fontFile = descriptor.get('FontFile', 'FontFile2', 'FontFile3');
      if (fontFile) {
        if (fontFile.dict) {
          var subtype = fontFile.dict.get('Subtype');
          if (subtype) {
            subtype = subtype.name;
          }
          var length1 = fontFile.dict.get('Length1');
          var length2 = fontFile.dict.get('Length2');
          var length3 = fontFile.dict.get('Length3');
        }
      }
      properties = {
        type,
        name: fontName.name,
        subtype,
        file: fontFile,
        length1,
        length2,
        length3,
        loadedName: baseDict.loadedName,
        composite,
        wideChars: composite,
        fixedPitch: false,
        fontMatrix: dict.getArray('FontMatrix') || _util.FONT_IDENTITY_MATRIX,
        firstChar: firstChar || 0,
        lastChar: lastChar || maxCharIndex,
        bbox: descriptor.getArray('FontBBox'),
        ascent: descriptor.get('Ascent'),
        descent: descriptor.get('Descent'),
        xHeight: descriptor.get('XHeight'),
        capHeight: descriptor.get('CapHeight'),
        flags: descriptor.get('Flags'),
        italicAngle: descriptor.get('ItalicAngle'),
        isType3Font: false
      };
      var cMapPromise;
      if (composite) {
        var cidEncoding = baseDict.get('Encoding');
        if ((0, _primitives.isName)(cidEncoding)) {
          properties.cidEncoding = cidEncoding.name;
        }
        cMapPromise = _cmap.CMapFactory.create({
          encoding: cidEncoding,
          fetchBuiltInCMap: this.fetchBuiltInCMap,
          useCMap: null
        }).then(function (cMap) {
          properties.cMap = cMap;
          properties.vertical = properties.cMap.vertical;
        });
      } else {
        cMapPromise = Promise.resolve(undefined);
      }
      return cMapPromise.then(() => {
        return this.extractDataStructures(dict, baseDict, properties);
      }).then(properties => {
        this.extractWidths(dict, descriptor, properties);
        if (type === 'Type3') {
          properties.isType3Font = true;
        }
        return new _fonts.Font(fontName.name, fontFile, properties);
      });
    }
  };
  return PartialEvaluator;
}();
var TranslatedFont = function TranslatedFontClosure() {
  function TranslatedFont(loadedName, font, dict) {
    this.loadedName = loadedName;
    this.font = font;
    this.dict = dict;
    this.type3Loaded = null;
    this.sent = false;
  }
  TranslatedFont.prototype = {
    send(handler) {
      if (this.sent) {
        return;
      }
      var fontData = this.font.exportData();
      handler.send('commonobj', [this.loadedName, 'Font', fontData]);
      this.sent = true;
    },
    loadType3Data(evaluator, resources, parentOperatorList, task) {
      if (!this.font.isType3Font) {
        throw new Error('Must be a Type3 font.');
      }
      if (this.type3Loaded) {
        return this.type3Loaded;
      }
      var type3Options = Object.create(evaluator.options);
      type3Options.ignoreErrors = false;
      var type3Evaluator = evaluator.clone(type3Options);
      var translatedFont = this.font;
      var loadCharProcsPromise = Promise.resolve();
      var charProcs = this.dict.get('CharProcs');
      var fontResources = this.dict.get('Resources') || resources;
      var charProcKeys = charProcs.getKeys();
      var charProcOperatorList = Object.create(null);
      for (var i = 0, n = charProcKeys.length; i < n; ++i) {
        let key = charProcKeys[i];
        loadCharProcsPromise = loadCharProcsPromise.then(function () {
          var glyphStream = charProcs.get(key);
          var operatorList = new _operator_list.OperatorList();
          return type3Evaluator.getOperatorList({
            stream: glyphStream,
            task,
            resources: fontResources,
            operatorList
          }).then(function () {
            charProcOperatorList[key] = operatorList.getIR();
            parentOperatorList.addDependencies(operatorList.dependencies);
          }).catch(function (reason) {
            (0, _util.warn)(`Type3 font resource "${key}" is not available.`);
            var operatorList = new _operator_list.OperatorList();
            charProcOperatorList[key] = operatorList.getIR();
          });
        });
      }
      this.type3Loaded = loadCharProcsPromise.then(function () {
        translatedFont.charProcOperatorList = charProcOperatorList;
      });
      return this.type3Loaded;
    }
  };
  return TranslatedFont;
}();
var StateManager = function StateManagerClosure() {
  function StateManager(initialState) {
    this.state = initialState;
    this.stateStack = [];
  }
  StateManager.prototype = {
    save() {
      var old = this.state;
      this.stateStack.push(this.state);
      this.state = old.clone();
    },
    restore() {
      var prev = this.stateStack.pop();
      if (prev) {
        this.state = prev;
      }
    },
    transform(args) {
      this.state.ctm = _util.Util.transform(this.state.ctm, args);
    }
  };
  return StateManager;
}();
var TextState = function TextStateClosure() {
  function TextState() {
    this.ctm = new Float32Array(_util.IDENTITY_MATRIX);
    this.fontName = null;
    this.fontSize = 0;
    this.font = null;
    this.fontMatrix = _util.FONT_IDENTITY_MATRIX;
    this.textMatrix = _util.IDENTITY_MATRIX.slice();
    this.textLineMatrix = _util.IDENTITY_MATRIX.slice();
    this.charSpacing = 0;
    this.wordSpacing = 0;
    this.leading = 0;
    this.textHScale = 1;
    this.textRise = 0;
  }
  TextState.prototype = {
    setTextMatrix: function TextState_setTextMatrix(a, b, c, d, e, f) {
      var m = this.textMatrix;
      m[0] = a;
      m[1] = b;
      m[2] = c;
      m[3] = d;
      m[4] = e;
      m[5] = f;
    },
    setTextLineMatrix: function TextState_setTextMatrix(a, b, c, d, e, f) {
      var m = this.textLineMatrix;
      m[0] = a;
      m[1] = b;
      m[2] = c;
      m[3] = d;
      m[4] = e;
      m[5] = f;
    },
    translateTextMatrix: function TextState_translateTextMatrix(x, y) {
      var m = this.textMatrix;
      m[4] = m[0] * x + m[2] * y + m[4];
      m[5] = m[1] * x + m[3] * y + m[5];
    },
    translateTextLineMatrix: function TextState_translateTextMatrix(x, y) {
      var m = this.textLineMatrix;
      m[4] = m[0] * x + m[2] * y + m[4];
      m[5] = m[1] * x + m[3] * y + m[5];
    },
    calcTextLineMatrixAdvance: function TextState_calcTextLineMatrixAdvance(a, b, c, d, e, f) {
      var font = this.font;
      if (!font) {
        return null;
      }
      var m = this.textLineMatrix;
      if (!(a === m[0] && b === m[1] && c === m[2] && d === m[3])) {
        return null;
      }
      var txDiff = e - m[4],
          tyDiff = f - m[5];
      if (font.vertical && txDiff !== 0 || !font.vertical && tyDiff !== 0) {
        return null;
      }
      var tx,
          ty,
          denominator = a * d - b * c;
      if (font.vertical) {
        tx = -tyDiff * c / denominator;
        ty = tyDiff * a / denominator;
      } else {
        tx = txDiff * d / denominator;
        ty = -txDiff * b / denominator;
      }
      return {
        width: tx,
        height: ty,
        value: font.vertical ? ty : tx
      };
    },
    calcRenderMatrix: function TextState_calcRendeMatrix(ctm) {
      var tsm = [this.fontSize * this.textHScale, 0, 0, this.fontSize, 0, this.textRise];
      return _util.Util.transform(ctm, _util.Util.transform(this.textMatrix, tsm));
    },
    carriageReturn: function TextState_carriageReturn() {
      this.translateTextLineMatrix(0, -this.leading);
      this.textMatrix = this.textLineMatrix.slice();
    },
    clone: function TextState_clone() {
      var clone = Object.create(this);
      clone.textMatrix = this.textMatrix.slice();
      clone.textLineMatrix = this.textLineMatrix.slice();
      clone.fontMatrix = this.fontMatrix.slice();
      return clone;
    }
  };
  return TextState;
}();
var EvalState = function EvalStateClosure() {
  function EvalState() {
    this.ctm = new Float32Array(_util.IDENTITY_MATRIX);
    this.font = null;
    this.textRenderingMode = _util.TextRenderingMode.FILL;
    this.fillColorSpace = _colorspace.ColorSpace.singletons.gray;
    this.strokeColorSpace = _colorspace.ColorSpace.singletons.gray;
  }
  EvalState.prototype = {
    clone: function CanvasExtraState_clone() {
      return Object.create(this);
    }
  };
  return EvalState;
}();
var EvaluatorPreprocessor = function EvaluatorPreprocessorClosure() {
  var getOPMap = (0, _util.getLookupTableFactory)(function (t) {
    t['w'] = {
      id: _util.OPS.setLineWidth,
      numArgs: 1,
      variableArgs: false
    };
    t['J'] = {
      id: _util.OPS.setLineCap,
      numArgs: 1,
      variableArgs: false
    };
    t['j'] = {
      id: _util.OPS.setLineJoin,
      numArgs: 1,
      variableArgs: false
    };
    t['M'] = {
      id: _util.OPS.setMiterLimit,
      numArgs: 1,
      variableArgs: false
    };
    t['d'] = {
      id: _util.OPS.setDash,
      numArgs: 2,
      variableArgs: false
    };
    t['ri'] = {
      id: _util.OPS.setRenderingIntent,
      numArgs: 1,
      variableArgs: false
    };
    t['i'] = {
      id: _util.OPS.setFlatness,
      numArgs: 1,
      variableArgs: false
    };
    t['gs'] = {
      id: _util.OPS.setGState,
      numArgs: 1,
      variableArgs: false
    };
    t['q'] = {
      id: _util.OPS.save,
      numArgs: 0,
      variableArgs: false
    };
    t['Q'] = {
      id: _util.OPS.restore,
      numArgs: 0,
      variableArgs: false
    };
    t['cm'] = {
      id: _util.OPS.transform,
      numArgs: 6,
      variableArgs: false
    };
    t['m'] = {
      id: _util.OPS.moveTo,
      numArgs: 2,
      variableArgs: false
    };
    t['l'] = {
      id: _util.OPS.lineTo,
      numArgs: 2,
      variableArgs: false
    };
    t['c'] = {
      id: _util.OPS.curveTo,
      numArgs: 6,
      variableArgs: false
    };
    t['v'] = {
      id: _util.OPS.curveTo2,
      numArgs: 4,
      variableArgs: false
    };
    t['y'] = {
      id: _util.OPS.curveTo3,
      numArgs: 4,
      variableArgs: false
    };
    t['h'] = {
      id: _util.OPS.closePath,
      numArgs: 0,
      variableArgs: false
    };
    t['re'] = {
      id: _util.OPS.rectangle,
      numArgs: 4,
      variableArgs: false
    };
    t['S'] = {
      id: _util.OPS.stroke,
      numArgs: 0,
      variableArgs: false
    };
    t['s'] = {
      id: _util.OPS.closeStroke,
      numArgs: 0,
      variableArgs: false
    };
    t['f'] = {
      id: _util.OPS.fill,
      numArgs: 0,
      variableArgs: false
    };
    t['F'] = {
      id: _util.OPS.fill,
      numArgs: 0,
      variableArgs: false
    };
    t['f*'] = {
      id: _util.OPS.eoFill,
      numArgs: 0,
      variableArgs: false
    };
    t['B'] = {
      id: _util.OPS.fillStroke,
      numArgs: 0,
      variableArgs: false
    };
    t['B*'] = {
      id: _util.OPS.eoFillStroke,
      numArgs: 0,
      variableArgs: false
    };
    t['b'] = {
      id: _util.OPS.closeFillStroke,
      numArgs: 0,
      variableArgs: false
    };
    t['b*'] = {
      id: _util.OPS.closeEOFillStroke,
      numArgs: 0,
      variableArgs: false
    };
    t['n'] = {
      id: _util.OPS.endPath,
      numArgs: 0,
      variableArgs: false
    };
    t['W'] = {
      id: _util.OPS.clip,
      numArgs: 0,
      variableArgs: false
    };
    t['W*'] = {
      id: _util.OPS.eoClip,
      numArgs: 0,
      variableArgs: false
    };
    t['BT'] = {
      id: _util.OPS.beginText,
      numArgs: 0,
      variableArgs: false
    };
    t['ET'] = {
      id: _util.OPS.endText,
      numArgs: 0,
      variableArgs: false
    };
    t['Tc'] = {
      id: _util.OPS.setCharSpacing,
      numArgs: 1,
      variableArgs: false
    };
    t['Tw'] = {
      id: _util.OPS.setWordSpacing,
      numArgs: 1,
      variableArgs: false
    };
    t['Tz'] = {
      id: _util.OPS.setHScale,
      numArgs: 1,
      variableArgs: false
    };
    t['TL'] = {
      id: _util.OPS.setLeading,
      numArgs: 1,
      variableArgs: false
    };
    t['Tf'] = {
      id: _util.OPS.setFont,
      numArgs: 2,
      variableArgs: false
    };
    t['Tr'] = {
      id: _util.OPS.setTextRenderingMode,
      numArgs: 1,
      variableArgs: false
    };
    t['Ts'] = {
      id: _util.OPS.setTextRise,
      numArgs: 1,
      variableArgs: false
    };
    t['Td'] = {
      id: _util.OPS.moveText,
      numArgs: 2,
      variableArgs: false
    };
    t['TD'] = {
      id: _util.OPS.setLeadingMoveText,
      numArgs: 2,
      variableArgs: false
    };
    t['Tm'] = {
      id: _util.OPS.setTextMatrix,
      numArgs: 6,
      variableArgs: false
    };
    t['T*'] = {
      id: _util.OPS.nextLine,
      numArgs: 0,
      variableArgs: false
    };
    t['Tj'] = {
      id: _util.OPS.showText,
      numArgs: 1,
      variableArgs: false
    };
    t['TJ'] = {
      id: _util.OPS.showSpacedText,
      numArgs: 1,
      variableArgs: false
    };
    t['\''] = {
      id: _util.OPS.nextLineShowText,
      numArgs: 1,
      variableArgs: false
    };
    t['"'] = {
      id: _util.OPS.nextLineSetSpacingShowText,
      numArgs: 3,
      variableArgs: false
    };
    t['d0'] = {
      id: _util.OPS.setCharWidth,
      numArgs: 2,
      variableArgs: false
    };
    t['d1'] = {
      id: _util.OPS.setCharWidthAndBounds,
      numArgs: 6,
      variableArgs: false
    };
    t['CS'] = {
      id: _util.OPS.setStrokeColorSpace,
      numArgs: 1,
      variableArgs: false
    };
    t['cs'] = {
      id: _util.OPS.setFillColorSpace,
      numArgs: 1,
      variableArgs: false
    };
    t['SC'] = {
      id: _util.OPS.setStrokeColor,
      numArgs: 4,
      variableArgs: true
    };
    t['SCN'] = {
      id: _util.OPS.setStrokeColorN,
      numArgs: 33,
      variableArgs: true
    };
    t['sc'] = {
      id: _util.OPS.setFillColor,
      numArgs: 4,
      variableArgs: true
    };
    t['scn'] = {
      id: _util.OPS.setFillColorN,
      numArgs: 33,
      variableArgs: true
    };
    t['G'] = {
      id: _util.OPS.setStrokeGray,
      numArgs: 1,
      variableArgs: false
    };
    t['g'] = {
      id: _util.OPS.setFillGray,
      numArgs: 1,
      variableArgs: false
    };
    t['RG'] = {
      id: _util.OPS.setStrokeRGBColor,
      numArgs: 3,
      variableArgs: false
    };
    t['rg'] = {
      id: _util.OPS.setFillRGBColor,
      numArgs: 3,
      variableArgs: false
    };
    t['K'] = {
      id: _util.OPS.setStrokeCMYKColor,
      numArgs: 4,
      variableArgs: false
    };
    t['k'] = {
      id: _util.OPS.setFillCMYKColor,
      numArgs: 4,
      variableArgs: false
    };
    t['sh'] = {
      id: _util.OPS.shadingFill,
      numArgs: 1,
      variableArgs: false
    };
    t['BI'] = {
      id: _util.OPS.beginInlineImage,
      numArgs: 0,
      variableArgs: false
    };
    t['ID'] = {
      id: _util.OPS.beginImageData,
      numArgs: 0,
      variableArgs: false
    };
    t['EI'] = {
      id: _util.OPS.endInlineImage,
      numArgs: 1,
      variableArgs: false
    };
    t['Do'] = {
      id: _util.OPS.paintXObject,
      numArgs: 1,
      variableArgs: false
    };
    t['MP'] = {
      id: _util.OPS.markPoint,
      numArgs: 1,
      variableArgs: false
    };
    t['DP'] = {
      id: _util.OPS.markPointProps,
      numArgs: 2,
      variableArgs: false
    };
    t['BMC'] = {
      id: _util.OPS.beginMarkedContent,
      numArgs: 1,
      variableArgs: false
    };
    t['BDC'] = {
      id: _util.OPS.beginMarkedContentProps,
      numArgs: 2,
      variableArgs: false
    };
    t['EMC'] = {
      id: _util.OPS.endMarkedContent,
      numArgs: 0,
      variableArgs: false
    };
    t['BX'] = {
      id: _util.OPS.beginCompat,
      numArgs: 0,
      variableArgs: false
    };
    t['EX'] = {
      id: _util.OPS.endCompat,
      numArgs: 0,
      variableArgs: false
    };
    t['BM'] = null;
    t['BD'] = null;
    t['true'] = null;
    t['fa'] = null;
    t['fal'] = null;
    t['fals'] = null;
    t['false'] = null;
    t['nu'] = null;
    t['nul'] = null;
    t['null'] = null;
  });
  function EvaluatorPreprocessor(stream, xref, stateManager) {
    this.opMap = getOPMap();
    this.parser = new _parser.Parser(new _parser.Lexer(stream, this.opMap), false, xref);
    this.stateManager = stateManager;
    this.nonProcessedArgs = [];
  }
  EvaluatorPreprocessor.prototype = {
    get savedStatesDepth() {
      return this.stateManager.stateStack.length;
    },
    read: function EvaluatorPreprocessor_read(operation) {
      var args = operation.args;
      while (true) {
        var obj = this.parser.getObj();
        if ((0, _primitives.isCmd)(obj)) {
          var cmd = obj.cmd;
          var opSpec = this.opMap[cmd];
          if (!opSpec) {
            (0, _util.warn)('Unknown command "' + cmd + '"');
            continue;
          }
          var fn = opSpec.id;
          var numArgs = opSpec.numArgs;
          var argsLength = args !== null ? args.length : 0;
          if (!opSpec.variableArgs) {
            if (argsLength !== numArgs) {
              var nonProcessedArgs = this.nonProcessedArgs;
              while (argsLength > numArgs) {
                nonProcessedArgs.push(args.shift());
                argsLength--;
              }
              while (argsLength < numArgs && nonProcessedArgs.length !== 0) {
                if (args === null) {
                  args = [];
                }
                args.unshift(nonProcessedArgs.pop());
                argsLength++;
              }
            }
            if (argsLength < numArgs) {
              (0, _util.warn)('Skipping command ' + fn + ': expected ' + numArgs + ' args, but received ' + argsLength + ' args.');
              if (args !== null) {
                args.length = 0;
              }
              continue;
            }
          } else if (argsLength > numArgs) {
            (0, _util.info)('Command ' + fn + ': expected [0,' + numArgs + '] args, but received ' + argsLength + ' args.');
          }
          this.preprocessCommand(fn, args);
          operation.fn = fn;
          operation.args = args;
          return true;
        }
        if ((0, _primitives.isEOF)(obj)) {
          return false;
        }
        if (obj !== null) {
          if (args === null) {
            args = [];
          }
          args.push(obj);
          if (args.length > 33) {
            throw new _util.FormatError('Too many arguments');
          }
        }
      }
    },
    preprocessCommand: function EvaluatorPreprocessor_preprocessCommand(fn, args) {
      switch (fn | 0) {
        case _util.OPS.save:
          this.stateManager.save();
          break;
        case _util.OPS.restore:
          this.stateManager.restore();
          break;
        case _util.OPS.transform:
          this.stateManager.transform(args);
          break;
      }
    }
  };
  return EvaluatorPreprocessor;
}();
exports.PartialEvaluator = PartialEvaluator;

/***/ }),
/* 33 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.CMapFactory = exports.IdentityCMap = exports.CMap = undefined;

var _util = __w_pdfjs_require__(0);

var _primitives = __w_pdfjs_require__(1);

var _parser = __w_pdfjs_require__(5);

var _stream = __w_pdfjs_require__(2);

var BUILT_IN_CMAPS = ['Adobe-GB1-UCS2', 'Adobe-CNS1-UCS2', 'Adobe-Japan1-UCS2', 'Adobe-Korea1-UCS2', '78-EUC-H', '78-EUC-V', '78-H', '78-RKSJ-H', '78-RKSJ-V', '78-V', '78ms-RKSJ-H', '78ms-RKSJ-V', '83pv-RKSJ-H', '90ms-RKSJ-H', '90ms-RKSJ-V', '90msp-RKSJ-H', '90msp-RKSJ-V', '90pv-RKSJ-H', '90pv-RKSJ-V', 'Add-H', 'Add-RKSJ-H', 'Add-RKSJ-V', 'Add-V', 'Adobe-CNS1-0', 'Adobe-CNS1-1', 'Adobe-CNS1-2', 'Adobe-CNS1-3', 'Adobe-CNS1-4', 'Adobe-CNS1-5', 'Adobe-CNS1-6', 'Adobe-GB1-0', 'Adobe-GB1-1', 'Adobe-GB1-2', 'Adobe-GB1-3', 'Adobe-GB1-4', 'Adobe-GB1-5', 'Adobe-Japan1-0', 'Adobe-Japan1-1', 'Adobe-Japan1-2', 'Adobe-Japan1-3', 'Adobe-Japan1-4', 'Adobe-Japan1-5', 'Adobe-Japan1-6', 'Adobe-Korea1-0', 'Adobe-Korea1-1', 'Adobe-Korea1-2', 'B5-H', 'B5-V', 'B5pc-H', 'B5pc-V', 'CNS-EUC-H', 'CNS-EUC-V', 'CNS1-H', 'CNS1-V', 'CNS2-H', 'CNS2-V', 'ETHK-B5-H', 'ETHK-B5-V', 'ETen-B5-H', 'ETen-B5-V', 'ETenms-B5-H', 'ETenms-B5-V', 'EUC-H', 'EUC-V', 'Ext-H', 'Ext-RKSJ-H', 'Ext-RKSJ-V', 'Ext-V', 'GB-EUC-H', 'GB-EUC-V', 'GB-H', 'GB-V', 'GBK-EUC-H', 'GBK-EUC-V', 'GBK2K-H', 'GBK2K-V', 'GBKp-EUC-H', 'GBKp-EUC-V', 'GBT-EUC-H', 'GBT-EUC-V', 'GBT-H', 'GBT-V', 'GBTpc-EUC-H', 'GBTpc-EUC-V', 'GBpc-EUC-H', 'GBpc-EUC-V', 'H', 'HKdla-B5-H', 'HKdla-B5-V', 'HKdlb-B5-H', 'HKdlb-B5-V', 'HKgccs-B5-H', 'HKgccs-B5-V', 'HKm314-B5-H', 'HKm314-B5-V', 'HKm471-B5-H', 'HKm471-B5-V', 'HKscs-B5-H', 'HKscs-B5-V', 'Hankaku', 'Hiragana', 'KSC-EUC-H', 'KSC-EUC-V', 'KSC-H', 'KSC-Johab-H', 'KSC-Johab-V', 'KSC-V', 'KSCms-UHC-H', 'KSCms-UHC-HW-H', 'KSCms-UHC-HW-V', 'KSCms-UHC-V', 'KSCpc-EUC-H', 'KSCpc-EUC-V', 'Katakana', 'NWP-H', 'NWP-V', 'RKSJ-H', 'RKSJ-V', 'Roman', 'UniCNS-UCS2-H', 'UniCNS-UCS2-V', 'UniCNS-UTF16-H', 'UniCNS-UTF16-V', 'UniCNS-UTF32-H', 'UniCNS-UTF32-V', 'UniCNS-UTF8-H', 'UniCNS-UTF8-V', 'UniGB-UCS2-H', 'UniGB-UCS2-V', 'UniGB-UTF16-H', 'UniGB-UTF16-V', 'UniGB-UTF32-H', 'UniGB-UTF32-V', 'UniGB-UTF8-H', 'UniGB-UTF8-V', 'UniJIS-UCS2-H', 'UniJIS-UCS2-HW-H', 'UniJIS-UCS2-HW-V', 'UniJIS-UCS2-V', 'UniJIS-UTF16-H', 'UniJIS-UTF16-V', 'UniJIS-UTF32-H', 'UniJIS-UTF32-V', 'UniJIS-UTF8-H', 'UniJIS-UTF8-V', 'UniJIS2004-UTF16-H', 'UniJIS2004-UTF16-V', 'UniJIS2004-UTF32-H', 'UniJIS2004-UTF32-V', 'UniJIS2004-UTF8-H', 'UniJIS2004-UTF8-V', 'UniJISPro-UCS2-HW-V', 'UniJISPro-UCS2-V', 'UniJISPro-UTF8-V', 'UniJISX0213-UTF32-H', 'UniJISX0213-UTF32-V', 'UniJISX02132004-UTF32-H', 'UniJISX02132004-UTF32-V', 'UniKS-UCS2-H', 'UniKS-UCS2-V', 'UniKS-UTF16-H', 'UniKS-UTF16-V', 'UniKS-UTF32-H', 'UniKS-UTF32-V', 'UniKS-UTF8-H', 'UniKS-UTF8-V', 'V', 'WP-Symbol'];
var CMap = function CMapClosure() {
  function CMap(builtInCMap) {
    this.codespaceRanges = [[], [], [], []];
    this.numCodespaceRanges = 0;
    this._map = [];
    this.name = '';
    this.vertical = false;
    this.useCMap = null;
    this.builtInCMap = builtInCMap;
  }
  CMap.prototype = {
    addCodespaceRange(n, low, high) {
      this.codespaceRanges[n - 1].push(low, high);
      this.numCodespaceRanges++;
    },
    mapCidRange(low, high, dstLow) {
      while (low <= high) {
        this._map[low++] = dstLow++;
      }
    },
    mapBfRange(low, high, dstLow) {
      var lastByte = dstLow.length - 1;
      while (low <= high) {
        this._map[low++] = dstLow;
        dstLow = dstLow.substr(0, lastByte) + String.fromCharCode(dstLow.charCodeAt(lastByte) + 1);
      }
    },
    mapBfRangeToArray(low, high, array) {
      var i = 0,
          ii = array.length;
      while (low <= high && i < ii) {
        this._map[low] = array[i++];
        ++low;
      }
    },
    mapOne(src, dst) {
      this._map[src] = dst;
    },
    lookup(code) {
      return this._map[code];
    },
    contains(code) {
      return this._map[code] !== undefined;
    },
    forEach(callback) {
      let map = this._map;
      let length = map.length;
      if (length <= 0x10000) {
        for (let i = 0; i < length; i++) {
          if (map[i] !== undefined) {
            callback(i, map[i]);
          }
        }
      } else {
        for (let i in map) {
          callback(i, map[i]);
        }
      }
    },
    charCodeOf(value) {
      let map = this._map;
      if (map.length <= 0x10000) {
        return map.indexOf(value);
      }
      for (let charCode in map) {
        if (map[charCode] === value) {
          return charCode | 0;
        }
      }
      return -1;
    },
    getMap() {
      return this._map;
    },
    readCharCode(str, offset, out) {
      var c = 0;
      var codespaceRanges = this.codespaceRanges;
      var codespaceRangesLen = this.codespaceRanges.length;
      for (var n = 0; n < codespaceRangesLen; n++) {
        c = (c << 8 | str.charCodeAt(offset + n)) >>> 0;
        var codespaceRange = codespaceRanges[n];
        for (var k = 0, kk = codespaceRange.length; k < kk;) {
          var low = codespaceRange[k++];
          var high = codespaceRange[k++];
          if (c >= low && c <= high) {
            out.charcode = c;
            out.length = n + 1;
            return;
          }
        }
      }
      out.charcode = 0;
      out.length = 1;
    },
    get length() {
      return this._map.length;
    },
    get isIdentityCMap() {
      if (!(this.name === 'Identity-H' || this.name === 'Identity-V')) {
        return false;
      }
      if (this._map.length !== 0x10000) {
        return false;
      }
      for (var i = 0; i < 0x10000; i++) {
        if (this._map[i] !== i) {
          return false;
        }
      }
      return true;
    }
  };
  return CMap;
}();
var IdentityCMap = function IdentityCMapClosure() {
  function IdentityCMap(vertical, n) {
    CMap.call(this);
    this.vertical = vertical;
    this.addCodespaceRange(n, 0, 0xffff);
  }
  _util.Util.inherit(IdentityCMap, CMap, {});
  IdentityCMap.prototype = {
    addCodespaceRange: CMap.prototype.addCodespaceRange,
    mapCidRange(low, high, dstLow) {
      (0, _util.unreachable)('should not call mapCidRange');
    },
    mapBfRange(low, high, dstLow) {
      (0, _util.unreachable)('should not call mapBfRange');
    },
    mapBfRangeToArray(low, high, array) {
      (0, _util.unreachable)('should not call mapBfRangeToArray');
    },
    mapOne(src, dst) {
      (0, _util.unreachable)('should not call mapCidOne');
    },
    lookup(code) {
      return Number.isInteger(code) && code <= 0xffff ? code : undefined;
    },
    contains(code) {
      return Number.isInteger(code) && code <= 0xffff;
    },
    forEach(callback) {
      for (var i = 0; i <= 0xffff; i++) {
        callback(i, i);
      }
    },
    charCodeOf(value) {
      return Number.isInteger(value) && value <= 0xffff ? value : -1;
    },
    getMap() {
      var map = new Array(0x10000);
      for (var i = 0; i <= 0xffff; i++) {
        map[i] = i;
      }
      return map;
    },
    readCharCode: CMap.prototype.readCharCode,
    get length() {
      return 0x10000;
    },
    get isIdentityCMap() {
      (0, _util.unreachable)('should not access .isIdentityCMap');
    }
  };
  return IdentityCMap;
}();
var BinaryCMapReader = function BinaryCMapReaderClosure() {
  function hexToInt(a, size) {
    var n = 0;
    for (var i = 0; i <= size; i++) {
      n = n << 8 | a[i];
    }
    return n >>> 0;
  }
  function hexToStr(a, size) {
    if (size === 1) {
      return String.fromCharCode(a[0], a[1]);
    }
    if (size === 3) {
      return String.fromCharCode(a[0], a[1], a[2], a[3]);
    }
    return String.fromCharCode.apply(null, a.subarray(0, size + 1));
  }
  function addHex(a, b, size) {
    var c = 0;
    for (var i = size; i >= 0; i--) {
      c += a[i] + b[i];
      a[i] = c & 255;
      c >>= 8;
    }
  }
  function incHex(a, size) {
    var c = 1;
    for (var i = size; i >= 0 && c > 0; i--) {
      c += a[i];
      a[i] = c & 255;
      c >>= 8;
    }
  }
  var MAX_NUM_SIZE = 16;
  var MAX_ENCODED_NUM_SIZE = 19;
  function BinaryCMapStream(data) {
    this.buffer = data;
    this.pos = 0;
    this.end = data.length;
    this.tmpBuf = new Uint8Array(MAX_ENCODED_NUM_SIZE);
  }
  BinaryCMapStream.prototype = {
    readByte() {
      if (this.pos >= this.end) {
        return -1;
      }
      return this.buffer[this.pos++];
    },
    readNumber() {
      var n = 0;
      var last;
      do {
        var b = this.readByte();
        if (b < 0) {
          throw new _util.FormatError('unexpected EOF in bcmap');
        }
        last = !(b & 0x80);
        n = n << 7 | b & 0x7F;
      } while (!last);
      return n;
    },
    readSigned() {
      var n = this.readNumber();
      return n & 1 ? ~(n >>> 1) : n >>> 1;
    },
    readHex(num, size) {
      num.set(this.buffer.subarray(this.pos, this.pos + size + 1));
      this.pos += size + 1;
    },
    readHexNumber(num, size) {
      var last;
      var stack = this.tmpBuf,
          sp = 0;
      do {
        var b = this.readByte();
        if (b < 0) {
          throw new _util.FormatError('unexpected EOF in bcmap');
        }
        last = !(b & 0x80);
        stack[sp++] = b & 0x7F;
      } while (!last);
      var i = size,
          buffer = 0,
          bufferSize = 0;
      while (i >= 0) {
        while (bufferSize < 8 && stack.length > 0) {
          buffer = stack[--sp] << bufferSize | buffer;
          bufferSize += 7;
        }
        num[i] = buffer & 255;
        i--;
        buffer >>= 8;
        bufferSize -= 8;
      }
    },
    readHexSigned(num, size) {
      this.readHexNumber(num, size);
      var sign = num[size] & 1 ? 255 : 0;
      var c = 0;
      for (var i = 0; i <= size; i++) {
        c = (c & 1) << 8 | num[i];
        num[i] = c >> 1 ^ sign;
      }
    },
    readString() {
      var len = this.readNumber();
      var s = '';
      for (var i = 0; i < len; i++) {
        s += String.fromCharCode(this.readNumber());
      }
      return s;
    }
  };
  function processBinaryCMap(data, cMap, extend) {
    return new Promise(function (resolve, reject) {
      var stream = new BinaryCMapStream(data);
      var header = stream.readByte();
      cMap.vertical = !!(header & 1);
      var useCMap = null;
      var start = new Uint8Array(MAX_NUM_SIZE);
      var end = new Uint8Array(MAX_NUM_SIZE);
      var char = new Uint8Array(MAX_NUM_SIZE);
      var charCode = new Uint8Array(MAX_NUM_SIZE);
      var tmp = new Uint8Array(MAX_NUM_SIZE);
      var code;
      var b;
      while ((b = stream.readByte()) >= 0) {
        var type = b >> 5;
        if (type === 7) {
          switch (b & 0x1F) {
            case 0:
              stream.readString();
              break;
            case 1:
              useCMap = stream.readString();
              break;
          }
          continue;
        }
        var sequence = !!(b & 0x10);
        var dataSize = b & 15;
        if (dataSize + 1 > MAX_NUM_SIZE) {
          throw new Error('processBinaryCMap: Invalid dataSize.');
        }
        var ucs2DataSize = 1;
        var subitemsCount = stream.readNumber();
        var i;
        switch (type) {
          case 0:
            stream.readHex(start, dataSize);
            stream.readHexNumber(end, dataSize);
            addHex(end, start, dataSize);
            cMap.addCodespaceRange(dataSize + 1, hexToInt(start, dataSize), hexToInt(end, dataSize));
            for (i = 1; i < subitemsCount; i++) {
              incHex(end, dataSize);
              stream.readHexNumber(start, dataSize);
              addHex(start, end, dataSize);
              stream.readHexNumber(end, dataSize);
              addHex(end, start, dataSize);
              cMap.addCodespaceRange(dataSize + 1, hexToInt(start, dataSize), hexToInt(end, dataSize));
            }
            break;
          case 1:
            stream.readHex(start, dataSize);
            stream.readHexNumber(end, dataSize);
            addHex(end, start, dataSize);
            stream.readNumber();
            for (i = 1; i < subitemsCount; i++) {
              incHex(end, dataSize);
              stream.readHexNumber(start, dataSize);
              addHex(start, end, dataSize);
              stream.readHexNumber(end, dataSize);
              addHex(end, start, dataSize);
              stream.readNumber();
            }
            break;
          case 2:
            stream.readHex(char, dataSize);
            code = stream.readNumber();
            cMap.mapOne(hexToInt(char, dataSize), code);
            for (i = 1; i < subitemsCount; i++) {
              incHex(char, dataSize);
              if (!sequence) {
                stream.readHexNumber(tmp, dataSize);
                addHex(char, tmp, dataSize);
              }
              code = stream.readSigned() + (code + 1);
              cMap.mapOne(hexToInt(char, dataSize), code);
            }
            break;
          case 3:
            stream.readHex(start, dataSize);
            stream.readHexNumber(end, dataSize);
            addHex(end, start, dataSize);
            code = stream.readNumber();
            cMap.mapCidRange(hexToInt(start, dataSize), hexToInt(end, dataSize), code);
            for (i = 1; i < subitemsCount; i++) {
              incHex(end, dataSize);
              if (!sequence) {
                stream.readHexNumber(start, dataSize);
                addHex(start, end, dataSize);
              } else {
                start.set(end);
              }
              stream.readHexNumber(end, dataSize);
              addHex(end, start, dataSize);
              code = stream.readNumber();
              cMap.mapCidRange(hexToInt(start, dataSize), hexToInt(end, dataSize), code);
            }
            break;
          case 4:
            stream.readHex(char, ucs2DataSize);
            stream.readHex(charCode, dataSize);
            cMap.mapOne(hexToInt(char, ucs2DataSize), hexToStr(charCode, dataSize));
            for (i = 1; i < subitemsCount; i++) {
              incHex(char, ucs2DataSize);
              if (!sequence) {
                stream.readHexNumber(tmp, ucs2DataSize);
                addHex(char, tmp, ucs2DataSize);
              }
              incHex(charCode, dataSize);
              stream.readHexSigned(tmp, dataSize);
              addHex(charCode, tmp, dataSize);
              cMap.mapOne(hexToInt(char, ucs2DataSize), hexToStr(charCode, dataSize));
            }
            break;
          case 5:
            stream.readHex(start, ucs2DataSize);
            stream.readHexNumber(end, ucs2DataSize);
            addHex(end, start, ucs2DataSize);
            stream.readHex(charCode, dataSize);
            cMap.mapBfRange(hexToInt(start, ucs2DataSize), hexToInt(end, ucs2DataSize), hexToStr(charCode, dataSize));
            for (i = 1; i < subitemsCount; i++) {
              incHex(end, ucs2DataSize);
              if (!sequence) {
                stream.readHexNumber(start, ucs2DataSize);
                addHex(start, end, ucs2DataSize);
              } else {
                start.set(end);
              }
              stream.readHexNumber(end, ucs2DataSize);
              addHex(end, start, ucs2DataSize);
              stream.readHex(charCode, dataSize);
              cMap.mapBfRange(hexToInt(start, ucs2DataSize), hexToInt(end, ucs2DataSize), hexToStr(charCode, dataSize));
            }
            break;
          default:
            reject(new Error('processBinaryCMap: Unknown type: ' + type));
            return;
        }
      }
      if (useCMap) {
        resolve(extend(useCMap));
        return;
      }
      resolve(cMap);
    });
  }
  function BinaryCMapReader() {}
  BinaryCMapReader.prototype = { process: processBinaryCMap };
  return BinaryCMapReader;
}();
var CMapFactory = function CMapFactoryClosure() {
  function strToInt(str) {
    var a = 0;
    for (var i = 0; i < str.length; i++) {
      a = a << 8 | str.charCodeAt(i);
    }
    return a >>> 0;
  }
  function expectString(obj) {
    if (!(0, _util.isString)(obj)) {
      throw new _util.FormatError('Malformed CMap: expected string.');
    }
  }
  function expectInt(obj) {
    if (!Number.isInteger(obj)) {
      throw new _util.FormatError('Malformed CMap: expected int.');
    }
  }
  function parseBfChar(cMap, lexer) {
    while (true) {
      var obj = lexer.getObj();
      if ((0, _primitives.isEOF)(obj)) {
        break;
      }
      if ((0, _primitives.isCmd)(obj, 'endbfchar')) {
        return;
      }
      expectString(obj);
      var src = strToInt(obj);
      obj = lexer.getObj();
      expectString(obj);
      var dst = obj;
      cMap.mapOne(src, dst);
    }
  }
  function parseBfRange(cMap, lexer) {
    while (true) {
      var obj = lexer.getObj();
      if ((0, _primitives.isEOF)(obj)) {
        break;
      }
      if ((0, _primitives.isCmd)(obj, 'endbfrange')) {
        return;
      }
      expectString(obj);
      var low = strToInt(obj);
      obj = lexer.getObj();
      expectString(obj);
      var high = strToInt(obj);
      obj = lexer.getObj();
      if (Number.isInteger(obj) || (0, _util.isString)(obj)) {
        var dstLow = Number.isInteger(obj) ? String.fromCharCode(obj) : obj;
        cMap.mapBfRange(low, high, dstLow);
      } else if ((0, _primitives.isCmd)(obj, '[')) {
        obj = lexer.getObj();
        var array = [];
        while (!(0, _primitives.isCmd)(obj, ']') && !(0, _primitives.isEOF)(obj)) {
          array.push(obj);
          obj = lexer.getObj();
        }
        cMap.mapBfRangeToArray(low, high, array);
      } else {
        break;
      }
    }
    throw new _util.FormatError('Invalid bf range.');
  }
  function parseCidChar(cMap, lexer) {
    while (true) {
      var obj = lexer.getObj();
      if ((0, _primitives.isEOF)(obj)) {
        break;
      }
      if ((0, _primitives.isCmd)(obj, 'endcidchar')) {
        return;
      }
      expectString(obj);
      var src = strToInt(obj);
      obj = lexer.getObj();
      expectInt(obj);
      var dst = obj;
      cMap.mapOne(src, dst);
    }
  }
  function parseCidRange(cMap, lexer) {
    while (true) {
      var obj = lexer.getObj();
      if ((0, _primitives.isEOF)(obj)) {
        break;
      }
      if ((0, _primitives.isCmd)(obj, 'endcidrange')) {
        return;
      }
      expectString(obj);
      var low = strToInt(obj);
      obj = lexer.getObj();
      expectString(obj);
      var high = strToInt(obj);
      obj = lexer.getObj();
      expectInt(obj);
      var dstLow = obj;
      cMap.mapCidRange(low, high, dstLow);
    }
  }
  function parseCodespaceRange(cMap, lexer) {
    while (true) {
      var obj = lexer.getObj();
      if ((0, _primitives.isEOF)(obj)) {
        break;
      }
      if ((0, _primitives.isCmd)(obj, 'endcodespacerange')) {
        return;
      }
      if (!(0, _util.isString)(obj)) {
        break;
      }
      var low = strToInt(obj);
      obj = lexer.getObj();
      if (!(0, _util.isString)(obj)) {
        break;
      }
      var high = strToInt(obj);
      cMap.addCodespaceRange(obj.length, low, high);
    }
    throw new _util.FormatError('Invalid codespace range.');
  }
  function parseWMode(cMap, lexer) {
    var obj = lexer.getObj();
    if (Number.isInteger(obj)) {
      cMap.vertical = !!obj;
    }
  }
  function parseCMapName(cMap, lexer) {
    var obj = lexer.getObj();
    if ((0, _primitives.isName)(obj) && (0, _util.isString)(obj.name)) {
      cMap.name = obj.name;
    }
  }
  function parseCMap(cMap, lexer, fetchBuiltInCMap, useCMap) {
    var previous;
    var embededUseCMap;
    objLoop: while (true) {
      try {
        var obj = lexer.getObj();
        if ((0, _primitives.isEOF)(obj)) {
          break;
        } else if ((0, _primitives.isName)(obj)) {
          if (obj.name === 'WMode') {
            parseWMode(cMap, lexer);
          } else if (obj.name === 'CMapName') {
            parseCMapName(cMap, lexer);
          }
          previous = obj;
        } else if ((0, _primitives.isCmd)(obj)) {
          switch (obj.cmd) {
            case 'endcmap':
              break objLoop;
            case 'usecmap':
              if ((0, _primitives.isName)(previous)) {
                embededUseCMap = previous.name;
              }
              break;
            case 'begincodespacerange':
              parseCodespaceRange(cMap, lexer);
              break;
            case 'beginbfchar':
              parseBfChar(cMap, lexer);
              break;
            case 'begincidchar':
              parseCidChar(cMap, lexer);
              break;
            case 'beginbfrange':
              parseBfRange(cMap, lexer);
              break;
            case 'begincidrange':
              parseCidRange(cMap, lexer);
              break;
          }
        }
      } catch (ex) {
        if (ex instanceof _util.MissingDataException) {
          throw ex;
        }
        (0, _util.warn)('Invalid cMap data: ' + ex);
        continue;
      }
    }
    if (!useCMap && embededUseCMap) {
      useCMap = embededUseCMap;
    }
    if (useCMap) {
      return extendCMap(cMap, fetchBuiltInCMap, useCMap);
    }
    return Promise.resolve(cMap);
  }
  function extendCMap(cMap, fetchBuiltInCMap, useCMap) {
    return createBuiltInCMap(useCMap, fetchBuiltInCMap).then(function (newCMap) {
      cMap.useCMap = newCMap;
      if (cMap.numCodespaceRanges === 0) {
        var useCodespaceRanges = cMap.useCMap.codespaceRanges;
        for (var i = 0; i < useCodespaceRanges.length; i++) {
          cMap.codespaceRanges[i] = useCodespaceRanges[i].slice();
        }
        cMap.numCodespaceRanges = cMap.useCMap.numCodespaceRanges;
      }
      cMap.useCMap.forEach(function (key, value) {
        if (!cMap.contains(key)) {
          cMap.mapOne(key, cMap.useCMap.lookup(key));
        }
      });
      return cMap;
    });
  }
  function createBuiltInCMap(name, fetchBuiltInCMap) {
    if (name === 'Identity-H') {
      return Promise.resolve(new IdentityCMap(false, 2));
    } else if (name === 'Identity-V') {
      return Promise.resolve(new IdentityCMap(true, 2));
    }
    if (!BUILT_IN_CMAPS.includes(name)) {
      return Promise.reject(new Error('Unknown CMap name: ' + name));
    }
    if (!fetchBuiltInCMap) {
      return Promise.reject(new Error('Built-in CMap parameters are not provided.'));
    }
    return fetchBuiltInCMap(name).then(function (data) {
      var cMapData = data.cMapData,
          compressionType = data.compressionType;
      var cMap = new CMap(true);
      if (compressionType === _util.CMapCompressionType.BINARY) {
        return new BinaryCMapReader().process(cMapData, cMap, function (useCMap) {
          return extendCMap(cMap, fetchBuiltInCMap, useCMap);
        });
      }
      if (compressionType === _util.CMapCompressionType.NONE) {
        var lexer = new _parser.Lexer(new _stream.Stream(cMapData));
        return parseCMap(cMap, lexer, fetchBuiltInCMap, null);
      }
      return Promise.reject(new Error('TODO: Only BINARY/NONE CMap compression is currently supported.'));
    });
  }
  return {
    create(params) {
      var encoding = params.encoding;
      var fetchBuiltInCMap = params.fetchBuiltInCMap;
      var useCMap = params.useCMap;
      if ((0, _primitives.isName)(encoding)) {
        return createBuiltInCMap(encoding.name, fetchBuiltInCMap);
      } else if ((0, _primitives.isStream)(encoding)) {
        var cMap = new CMap();
        var lexer = new _parser.Lexer(encoding);
        return parseCMap(cMap, lexer, fetchBuiltInCMap, useCMap).then(function (parsedCMap) {
          if (parsedCMap.isIdentityCMap) {
            return createBuiltInCMap(parsedCMap.name, fetchBuiltInCMap);
          }
          return parsedCMap;
        });
      }
      return Promise.reject(new Error('Encoding required.'));
    }
  };
}();
exports.CMap = CMap;
exports.IdentityCMap = IdentityCMap;
exports.CMapFactory = CMapFactory;

/***/ }),
/* 34 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getFontType = exports.ProblematicCharRanges = exports.IdentityToUnicodeMap = exports.ToUnicodeMap = exports.FontFlags = exports.Font = exports.ErrorFont = exports.PRIVATE_USE_OFFSET_END = exports.PRIVATE_USE_OFFSET_START = exports.SEAC_ANALYSIS_ENABLED = undefined;

var _util = __w_pdfjs_require__(0);

var _cff_parser = __w_pdfjs_require__(15);

var _glyphlist = __w_pdfjs_require__(8);

var _encodings = __w_pdfjs_require__(4);

var _standard_fonts = __w_pdfjs_require__(16);

var _unicode = __w_pdfjs_require__(17);

var _font_renderer = __w_pdfjs_require__(36);

var _stream = __w_pdfjs_require__(2);

var _type1_parser = __w_pdfjs_require__(37);

var PRIVATE_USE_OFFSET_START = 0xE000;
var PRIVATE_USE_OFFSET_END = 0xF8FF;
var SKIP_PRIVATE_USE_RANGE_F000_TO_F01F = false;
var PDF_GLYPH_SPACE_UNITS = 1000;
var SEAC_ANALYSIS_ENABLED = false;
var FontFlags = {
  FixedPitch: 1,
  Serif: 2,
  Symbolic: 4,
  Script: 8,
  Nonsymbolic: 32,
  Italic: 64,
  AllCap: 65536,
  SmallCap: 131072,
  ForceBold: 262144
};
var MacStandardGlyphOrdering = ['.notdef', '.null', 'nonmarkingreturn', 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent', 'ampersand', 'quotesingle', 'parenleft', 'parenright', 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash', 'bracketright', 'asciicircum', 'underscore', 'grave', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright', 'asciitilde', 'Adieresis', 'Aring', 'Ccedilla', 'Eacute', 'Ntilde', 'Odieresis', 'Udieresis', 'aacute', 'agrave', 'acircumflex', 'adieresis', 'atilde', 'aring', 'ccedilla', 'eacute', 'egrave', 'ecircumflex', 'edieresis', 'iacute', 'igrave', 'icircumflex', 'idieresis', 'ntilde', 'oacute', 'ograve', 'ocircumflex', 'odieresis', 'otilde', 'uacute', 'ugrave', 'ucircumflex', 'udieresis', 'dagger', 'degree', 'cent', 'sterling', 'section', 'bullet', 'paragraph', 'germandbls', 'registered', 'copyright', 'trademark', 'acute', 'dieresis', 'notequal', 'AE', 'Oslash', 'infinity', 'plusminus', 'lessequal', 'greaterequal', 'yen', 'mu', 'partialdiff', 'summation', 'product', 'pi', 'integral', 'ordfeminine', 'ordmasculine', 'Omega', 'ae', 'oslash', 'questiondown', 'exclamdown', 'logicalnot', 'radical', 'florin', 'approxequal', 'Delta', 'guillemotleft', 'guillemotright', 'ellipsis', 'nonbreakingspace', 'Agrave', 'Atilde', 'Otilde', 'OE', 'oe', 'endash', 'emdash', 'quotedblleft', 'quotedblright', 'quoteleft', 'quoteright', 'divide', 'lozenge', 'ydieresis', 'Ydieresis', 'fraction', 'currency', 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'daggerdbl', 'periodcentered', 'quotesinglbase', 'quotedblbase', 'perthousand', 'Acircumflex', 'Ecircumflex', 'Aacute', 'Edieresis', 'Egrave', 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Oacute', 'Ocircumflex', 'apple', 'Ograve', 'Uacute', 'Ucircumflex', 'Ugrave', 'dotlessi', 'circumflex', 'tilde', 'macron', 'breve', 'dotaccent', 'ring', 'cedilla', 'hungarumlaut', 'ogonek', 'caron', 'Lslash', 'lslash', 'Scaron', 'scaron', 'Zcaron', 'zcaron', 'brokenbar', 'Eth', 'eth', 'Yacute', 'yacute', 'Thorn', 'thorn', 'minus', 'multiply', 'onesuperior', 'twosuperior', 'threesuperior', 'onehalf', 'onequarter', 'threequarters', 'franc', 'Gbreve', 'gbreve', 'Idotaccent', 'Scedilla', 'scedilla', 'Cacute', 'cacute', 'Ccaron', 'ccaron', 'dcroat'];
function adjustWidths(properties) {
  if (!properties.fontMatrix) {
    return;
  }
  if (properties.fontMatrix[0] === _util.FONT_IDENTITY_MATRIX[0]) {
    return;
  }
  var scale = 0.001 / properties.fontMatrix[0];
  var glyphsWidths = properties.widths;
  for (var glyph in glyphsWidths) {
    glyphsWidths[glyph] *= scale;
  }
  properties.defaultWidth *= scale;
}
function adjustToUnicode(properties, builtInEncoding) {
  if (properties.hasIncludedToUnicodeMap) {
    return;
  }
  if (properties.hasEncoding) {
    return;
  }
  if (builtInEncoding === properties.defaultEncoding) {
    return;
  }
  if (properties.toUnicode instanceof IdentityToUnicodeMap) {
    return;
  }
  var toUnicode = [],
      glyphsUnicodeMap = (0, _glyphlist.getGlyphsUnicode)();
  for (var charCode in builtInEncoding) {
    var glyphName = builtInEncoding[charCode];
    var unicode = (0, _unicode.getUnicodeForGlyph)(glyphName, glyphsUnicodeMap);
    if (unicode !== -1) {
      toUnicode[charCode] = String.fromCharCode(unicode);
    }
  }
  properties.toUnicode.amend(toUnicode);
}
function getFontType(type, subtype) {
  switch (type) {
    case 'Type1':
      return subtype === 'Type1C' ? _util.FontType.TYPE1C : _util.FontType.TYPE1;
    case 'CIDFontType0':
      return subtype === 'CIDFontType0C' ? _util.FontType.CIDFONTTYPE0C : _util.FontType.CIDFONTTYPE0;
    case 'OpenType':
      return _util.FontType.OPENTYPE;
    case 'TrueType':
      return _util.FontType.TRUETYPE;
    case 'CIDFontType2':
      return _util.FontType.CIDFONTTYPE2;
    case 'MMType1':
      return _util.FontType.MMTYPE1;
    case 'Type0':
      return _util.FontType.TYPE0;
    default:
      return _util.FontType.UNKNOWN;
  }
}
function recoverGlyphName(name, glyphsUnicodeMap) {
  if (glyphsUnicodeMap[name] !== undefined) {
    return name;
  }
  var unicode = (0, _unicode.getUnicodeForGlyph)(name, glyphsUnicodeMap);
  if (unicode !== -1) {
    for (var key in glyphsUnicodeMap) {
      if (glyphsUnicodeMap[key] === unicode) {
        return key;
      }
    }
  }
  (0, _util.info)('Unable to recover a standard glyph name for: ' + name);
  return name;
}
var Glyph = function GlyphClosure() {
  function Glyph(fontChar, unicode, accent, width, vmetric, operatorListId, isSpace, isInFont) {
    this.fontChar = fontChar;
    this.unicode = unicode;
    this.accent = accent;
    this.width = width;
    this.vmetric = vmetric;
    this.operatorListId = operatorListId;
    this.isSpace = isSpace;
    this.isInFont = isInFont;
  }
  Glyph.prototype.matchesForCache = function (fontChar, unicode, accent, width, vmetric, operatorListId, isSpace, isInFont) {
    return this.fontChar === fontChar && this.unicode === unicode && this.accent === accent && this.width === width && this.vmetric === vmetric && this.operatorListId === operatorListId && this.isSpace === isSpace && this.isInFont === isInFont;
  };
  return Glyph;
}();
var ToUnicodeMap = function ToUnicodeMapClosure() {
  function ToUnicodeMap(cmap = []) {
    this._map = cmap;
  }
  ToUnicodeMap.prototype = {
    get length() {
      return this._map.length;
    },
    forEach(callback) {
      for (var charCode in this._map) {
        callback(charCode, this._map[charCode].charCodeAt(0));
      }
    },
    has(i) {
      return this._map[i] !== undefined;
    },
    get(i) {
      return this._map[i];
    },
    charCodeOf(value) {
      let map = this._map;
      if (map.length <= 0x10000) {
        return map.indexOf(value);
      }
      for (let charCode in map) {
        if (map[charCode] === value) {
          return charCode | 0;
        }
      }
      return -1;
    },
    amend(map) {
      for (var charCode in map) {
        this._map[charCode] = map[charCode];
      }
    }
  };
  return ToUnicodeMap;
}();
var IdentityToUnicodeMap = function IdentityToUnicodeMapClosure() {
  function IdentityToUnicodeMap(firstChar, lastChar) {
    this.firstChar = firstChar;
    this.lastChar = lastChar;
  }
  IdentityToUnicodeMap.prototype = {
    get length() {
      return this.lastChar + 1 - this.firstChar;
    },
    forEach(callback) {
      for (var i = this.firstChar, ii = this.lastChar; i <= ii; i++) {
        callback(i, i);
      }
    },
    has(i) {
      return this.firstChar <= i && i <= this.lastChar;
    },
    get(i) {
      if (this.firstChar <= i && i <= this.lastChar) {
        return String.fromCharCode(i);
      }
      return undefined;
    },
    charCodeOf(v) {
      return Number.isInteger(v) && v >= this.firstChar && v <= this.lastChar ? v : -1;
    },
    amend(map) {
      (0, _util.unreachable)('Should not call amend()');
    }
  };
  return IdentityToUnicodeMap;
}();
var OpenTypeFileBuilder = function OpenTypeFileBuilderClosure() {
  function writeInt16(dest, offset, num) {
    dest[offset] = num >> 8 & 0xFF;
    dest[offset + 1] = num & 0xFF;
  }
  function writeInt32(dest, offset, num) {
    dest[offset] = num >> 24 & 0xFF;
    dest[offset + 1] = num >> 16 & 0xFF;
    dest[offset + 2] = num >> 8 & 0xFF;
    dest[offset + 3] = num & 0xFF;
  }
  function writeData(dest, offset, data) {
    var i, ii;
    if (data instanceof Uint8Array) {
      dest.set(data, offset);
    } else if (typeof data === 'string') {
      for (i = 0, ii = data.length; i < ii; i++) {
        dest[offset++] = data.charCodeAt(i) & 0xFF;
      }
    } else {
      for (i = 0, ii = data.length; i < ii; i++) {
        dest[offset++] = data[i] & 0xFF;
      }
    }
  }
  function OpenTypeFileBuilder(sfnt) {
    this.sfnt = sfnt;
    this.tables = Object.create(null);
  }
  OpenTypeFileBuilder.getSearchParams = function OpenTypeFileBuilder_getSearchParams(entriesCount, entrySize) {
    var maxPower2 = 1,
        log2 = 0;
    while ((maxPower2 ^ entriesCount) > maxPower2) {
      maxPower2 <<= 1;
      log2++;
    }
    var searchRange = maxPower2 * entrySize;
    return {
      range: searchRange,
      entry: log2,
      rangeShift: entrySize * entriesCount - searchRange
    };
  };
  var OTF_HEADER_SIZE = 12;
  var OTF_TABLE_ENTRY_SIZE = 16;
  OpenTypeFileBuilder.prototype = {
    toArray: function OpenTypeFileBuilder_toArray() {
      var sfnt = this.sfnt;
      var tables = this.tables;
      var tablesNames = Object.keys(tables);
      tablesNames.sort();
      var numTables = tablesNames.length;
      var i, j, jj, table, tableName;
      var offset = OTF_HEADER_SIZE + numTables * OTF_TABLE_ENTRY_SIZE;
      var tableOffsets = [offset];
      for (i = 0; i < numTables; i++) {
        table = tables[tablesNames[i]];
        var paddedLength = (table.length + 3 & ~3) >>> 0;
        offset += paddedLength;
        tableOffsets.push(offset);
      }
      var file = new Uint8Array(offset);
      for (i = 0; i < numTables; i++) {
        table = tables[tablesNames[i]];
        writeData(file, tableOffsets[i], table);
      }
      if (sfnt === 'true') {
        sfnt = (0, _util.string32)(0x00010000);
      }
      file[0] = sfnt.charCodeAt(0) & 0xFF;
      file[1] = sfnt.charCodeAt(1) & 0xFF;
      file[2] = sfnt.charCodeAt(2) & 0xFF;
      file[3] = sfnt.charCodeAt(3) & 0xFF;
      writeInt16(file, 4, numTables);
      var searchParams = OpenTypeFileBuilder.getSearchParams(numTables, 16);
      writeInt16(file, 6, searchParams.range);
      writeInt16(file, 8, searchParams.entry);
      writeInt16(file, 10, searchParams.rangeShift);
      offset = OTF_HEADER_SIZE;
      for (i = 0; i < numTables; i++) {
        tableName = tablesNames[i];
        file[offset] = tableName.charCodeAt(0) & 0xFF;
        file[offset + 1] = tableName.charCodeAt(1) & 0xFF;
        file[offset + 2] = tableName.charCodeAt(2) & 0xFF;
        file[offset + 3] = tableName.charCodeAt(3) & 0xFF;
        var checksum = 0;
        for (j = tableOffsets[i], jj = tableOffsets[i + 1]; j < jj; j += 4) {
          var quad = (0, _util.readUint32)(file, j);
          checksum = checksum + quad >>> 0;
        }
        writeInt32(file, offset + 4, checksum);
        writeInt32(file, offset + 8, tableOffsets[i]);
        writeInt32(file, offset + 12, tables[tableName].length);
        offset += OTF_TABLE_ENTRY_SIZE;
      }
      return file;
    },
    addTable: function OpenTypeFileBuilder_addTable(tag, data) {
      if (tag in this.tables) {
        throw new Error('Table ' + tag + ' already exists');
      }
      this.tables[tag] = data;
    }
  };
  return OpenTypeFileBuilder;
}();
var ProblematicCharRanges = new Int32Array([0x0000, 0x0020, 0x007F, 0x00A1, 0x00AD, 0x00AE, 0x0600, 0x0780, 0x08A0, 0x10A0, 0x1780, 0x1800, 0x1C00, 0x1C50, 0x2000, 0x2010, 0x2011, 0x2012, 0x2028, 0x2030, 0x205F, 0x2070, 0x25CC, 0x25CD, 0x3000, 0x3001, 0x3164, 0x3165, 0xAA60, 0xAA80, 0xD800, 0xE000, 0xFFF0, 0x10000]);
var Font = function FontClosure() {
  function Font(name, file, properties) {
    var charCode;
    this.name = name;
    this.loadedName = properties.loadedName;
    this.isType3Font = properties.isType3Font;
    this.sizes = [];
    this.missingFile = false;
    this.glyphCache = Object.create(null);
    this.isSerifFont = !!(properties.flags & FontFlags.Serif);
    this.isSymbolicFont = !!(properties.flags & FontFlags.Symbolic);
    this.isMonospace = !!(properties.flags & FontFlags.FixedPitch);
    var type = properties.type;
    var subtype = properties.subtype;
    this.type = type;
    this.subtype = subtype;
    this.fallbackName = this.isMonospace ? 'monospace' : this.isSerifFont ? 'serif' : 'sans-serif';
    this.differences = properties.differences;
    this.widths = properties.widths;
    this.defaultWidth = properties.defaultWidth;
    this.composite = properties.composite;
    this.wideChars = properties.wideChars;
    this.cMap = properties.cMap;
    this.ascent = properties.ascent / PDF_GLYPH_SPACE_UNITS;
    this.descent = properties.descent / PDF_GLYPH_SPACE_UNITS;
    this.fontMatrix = properties.fontMatrix;
    this.bbox = properties.bbox;
    this.defaultEncoding = properties.defaultEncoding;
    this.toUnicode = properties.toUnicode;
    this.fallbackToUnicode = properties.fallbackToUnicode || new ToUnicodeMap();
    this.toFontChar = [];
    if (properties.type === 'Type3') {
      for (charCode = 0; charCode < 256; charCode++) {
        this.toFontChar[charCode] = this.differences[charCode] || properties.defaultEncoding[charCode];
      }
      this.fontType = _util.FontType.TYPE3;
      return;
    }
    this.cidEncoding = properties.cidEncoding;
    this.vertical = properties.vertical;
    if (this.vertical) {
      this.vmetrics = properties.vmetrics;
      this.defaultVMetrics = properties.defaultVMetrics;
    }
    if (!file || file.isEmpty) {
      if (file) {
        (0, _util.warn)('Font file is empty in "' + name + '" (' + this.loadedName + ')');
      }
      this.fallbackToSystemFont();
      return;
    }
    if (subtype === 'Type1C') {
      if (type !== 'Type1' && type !== 'MMType1') {
        if (isTrueTypeFile(file)) {
          subtype = 'TrueType';
        } else {
          type = 'Type1';
        }
      } else if (isOpenTypeFile(file)) {
        subtype = 'OpenType';
      }
    }
    if (subtype === 'CIDFontType0C' && type !== 'CIDFontType0') {
      type = 'CIDFontType0';
    }
    if (type === 'CIDFontType0') {
      if (isType1File(file)) {
        subtype = 'CIDFontType0';
      } else if (isOpenTypeFile(file)) {
        subtype = 'OpenType';
      } else {
        subtype = 'CIDFontType0C';
      }
    }
    if (subtype === 'OpenType' && type !== 'OpenType') {
      type = 'OpenType';
    }
    try {
      var data;
      switch (type) {
        case 'MMType1':
          (0, _util.info)('MMType1 font (' + name + '), falling back to Type1.');
        case 'Type1':
        case 'CIDFontType0':
          this.mimetype = 'font/opentype';
          var cff = subtype === 'Type1C' || subtype === 'CIDFontType0C' ? new CFFFont(file, properties) : new Type1Font(name, file, properties);
          adjustWidths(properties);
          data = this.convert(name, cff, properties);
          break;
        case 'OpenType':
        case 'TrueType':
        case 'CIDFontType2':
          this.mimetype = 'font/opentype';
          data = this.checkAndRepair(name, file, properties);
          if (this.isOpenType) {
            adjustWidths(properties);
            type = 'OpenType';
          }
          break;
        default:
          throw new _util.FormatError(`Font ${type} is not supported`);
      }
    } catch (e) {
      if (!(e instanceof _util.FormatError)) {
        throw e;
      }
      (0, _util.warn)(e);
      this.fallbackToSystemFont();
      return;
    }
    this.data = data;
    this.fontType = getFontType(type, subtype);
    this.fontMatrix = properties.fontMatrix;
    this.widths = properties.widths;
    this.defaultWidth = properties.defaultWidth;
    this.toUnicode = properties.toUnicode;
    this.encoding = properties.baseEncoding;
    this.seacMap = properties.seacMap;
    this.loading = true;
  }
  Font.getFontID = function () {
    var ID = 1;
    return function Font_getFontID() {
      return String(ID++);
    };
  }();
  function int16(b0, b1) {
    return (b0 << 8) + b1;
  }
  function writeSignedInt16(bytes, index, value) {
    bytes[index + 1] = value;
    bytes[index] = value >>> 8;
  }
  function signedInt16(b0, b1) {
    var value = (b0 << 8) + b1;
    return value & 1 << 15 ? value - 0x10000 : value;
  }
  function int32(b0, b1, b2, b3) {
    return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3;
  }
  function string16(value) {
    return String.fromCharCode(value >> 8 & 0xff, value & 0xff);
  }
  function safeString16(value) {
    value = value > 0x7FFF ? 0x7FFF : value < -0x8000 ? -0x8000 : value;
    return String.fromCharCode(value >> 8 & 0xff, value & 0xff);
  }
  function isTrueTypeFile(file) {
    var header = file.peekBytes(4);
    return (0, _util.readUint32)(header, 0) === 0x00010000;
  }
  function isTrueTypeCollectionFile(file) {
    let header = file.peekBytes(4);
    return (0, _util.bytesToString)(header) === 'ttcf';
  }
  function isOpenTypeFile(file) {
    var header = file.peekBytes(4);
    return (0, _util.bytesToString)(header) === 'OTTO';
  }
  function isType1File(file) {
    var header = file.peekBytes(2);
    if (header[0] === 0x25 && header[1] === 0x21) {
      return true;
    }
    if (header[0] === 0x80 && header[1] === 0x01) {
      return true;
    }
    return false;
  }
  function buildToFontChar(encoding, glyphsUnicodeMap, differences) {
    var toFontChar = [],
        unicode;
    for (var i = 0, ii = encoding.length; i < ii; i++) {
      unicode = (0, _unicode.getUnicodeForGlyph)(encoding[i], glyphsUnicodeMap);
      if (unicode !== -1) {
        toFontChar[i] = unicode;
      }
    }
    for (var charCode in differences) {
      unicode = (0, _unicode.getUnicodeForGlyph)(differences[charCode], glyphsUnicodeMap);
      if (unicode !== -1) {
        toFontChar[+charCode] = unicode;
      }
    }
    return toFontChar;
  }
  function isProblematicUnicodeLocation(code) {
    var i = 0,
        j = ProblematicCharRanges.length - 1;
    while (i < j) {
      var c = i + j + 1 >> 1;
      if (code < ProblematicCharRanges[c]) {
        j = c - 1;
      } else {
        i = c;
      }
    }
    return !(i & 1);
  }
  function adjustMapping(charCodeToGlyphId, properties, missingGlyphs) {
    var toUnicode = properties.toUnicode;
    var isSymbolic = !!(properties.flags & FontFlags.Symbolic);
    var isIdentityUnicode = properties.toUnicode instanceof IdentityToUnicodeMap;
    var newMap = Object.create(null);
    var toFontChar = [];
    var usedFontCharCodes = [];
    var nextAvailableFontCharCode = PRIVATE_USE_OFFSET_START;
    for (var originalCharCode in charCodeToGlyphId) {
      originalCharCode |= 0;
      var glyphId = charCodeToGlyphId[originalCharCode];
      if (missingGlyphs[glyphId]) {
        continue;
      }
      var fontCharCode = originalCharCode;
      var hasUnicodeValue = false;
      if (!isIdentityUnicode && toUnicode.has(originalCharCode)) {
        hasUnicodeValue = true;
        var unicode = toUnicode.get(fontCharCode);
        if (unicode.length === 1) {
          fontCharCode = unicode.charCodeAt(0);
        }
      }
      if (usedFontCharCodes[fontCharCode] !== undefined || isProblematicUnicodeLocation(fontCharCode) || isSymbolic && !hasUnicodeValue) {
        do {
          if (nextAvailableFontCharCode > PRIVATE_USE_OFFSET_END) {
            (0, _util.warn)('Ran out of space in font private use area.');
            break;
          }
          fontCharCode = nextAvailableFontCharCode++;
          if (SKIP_PRIVATE_USE_RANGE_F000_TO_F01F && fontCharCode === 0xF000) {
            fontCharCode = 0xF020;
            nextAvailableFontCharCode = fontCharCode + 1;
          }
        } while (usedFontCharCodes[fontCharCode] !== undefined);
      }
      newMap[fontCharCode] = glyphId;
      toFontChar[originalCharCode] = fontCharCode;
      usedFontCharCodes[fontCharCode] = true;
    }
    return {
      toFontChar,
      charCodeToGlyphId: newMap,
      nextAvailableFontCharCode
    };
  }
  function getRanges(glyphs, numGlyphs) {
    var codes = [];
    for (var charCode in glyphs) {
      if (glyphs[charCode] >= numGlyphs) {
        continue;
      }
      codes.push({
        fontCharCode: charCode | 0,
        glyphId: glyphs[charCode]
      });
    }
    if (codes.length === 0) {
      codes.push({
        fontCharCode: 0,
        glyphId: 0
      });
    }
    codes.sort(function fontGetRangesSort(a, b) {
      return a.fontCharCode - b.fontCharCode;
    });
    var ranges = [];
    var length = codes.length;
    for (var n = 0; n < length;) {
      var start = codes[n].fontCharCode;
      var codeIndices = [codes[n].glyphId];
      ++n;
      var end = start;
      while (n < length && end + 1 === codes[n].fontCharCode) {
        codeIndices.push(codes[n].glyphId);
        ++end;
        ++n;
        if (end === 0xFFFF) {
          break;
        }
      }
      ranges.push([start, end, codeIndices]);
    }
    return ranges;
  }
  function createCmapTable(glyphs, numGlyphs) {
    var ranges = getRanges(glyphs, numGlyphs);
    var numTables = ranges[ranges.length - 1][1] > 0xFFFF ? 2 : 1;
    var cmap = '\x00\x00' + string16(numTables) + '\x00\x03' + '\x00\x01' + (0, _util.string32)(4 + numTables * 8);
    var i, ii, j, jj;
    for (i = ranges.length - 1; i >= 0; --i) {
      if (ranges[i][0] <= 0xFFFF) {
        break;
      }
    }
    var bmpLength = i + 1;
    if (ranges[i][0] < 0xFFFF && ranges[i][1] === 0xFFFF) {
      ranges[i][1] = 0xFFFE;
    }
    var trailingRangesCount = ranges[i][1] < 0xFFFF ? 1 : 0;
    var segCount = bmpLength + trailingRangesCount;
    var searchParams = OpenTypeFileBuilder.getSearchParams(segCount, 2);
    var startCount = '';
    var endCount = '';
    var idDeltas = '';
    var idRangeOffsets = '';
    var glyphsIds = '';
    var bias = 0;
    var range, start, end, codes;
    for (i = 0, ii = bmpLength; i < ii; i++) {
      range = ranges[i];
      start = range[0];
      end = range[1];
      startCount += string16(start);
      endCount += string16(end);
      codes = range[2];
      var contiguous = true;
      for (j = 1, jj = codes.length; j < jj; ++j) {
        if (codes[j] !== codes[j - 1] + 1) {
          contiguous = false;
          break;
        }
      }
      if (!contiguous) {
        var offset = (segCount - i) * 2 + bias * 2;
        bias += end - start + 1;
        idDeltas += string16(0);
        idRangeOffsets += string16(offset);
        for (j = 0, jj = codes.length; j < jj; ++j) {
          glyphsIds += string16(codes[j]);
        }
      } else {
        var startCode = codes[0];
        idDeltas += string16(startCode - start & 0xFFFF);
        idRangeOffsets += string16(0);
      }
    }
    if (trailingRangesCount > 0) {
      endCount += '\xFF\xFF';
      startCount += '\xFF\xFF';
      idDeltas += '\x00\x01';
      idRangeOffsets += '\x00\x00';
    }
    var format314 = '\x00\x00' + string16(2 * segCount) + string16(searchParams.range) + string16(searchParams.entry) + string16(searchParams.rangeShift) + endCount + '\x00\x00' + startCount + idDeltas + idRangeOffsets + glyphsIds;
    var format31012 = '';
    var header31012 = '';
    if (numTables > 1) {
      cmap += '\x00\x03' + '\x00\x0A' + (0, _util.string32)(4 + numTables * 8 + 4 + format314.length);
      format31012 = '';
      for (i = 0, ii = ranges.length; i < ii; i++) {
        range = ranges[i];
        start = range[0];
        codes = range[2];
        var code = codes[0];
        for (j = 1, jj = codes.length; j < jj; ++j) {
          if (codes[j] !== codes[j - 1] + 1) {
            end = range[0] + j - 1;
            format31012 += (0, _util.string32)(start) + (0, _util.string32)(end) + (0, _util.string32)(code);
            start = end + 1;
            code = codes[j];
          }
        }
        format31012 += (0, _util.string32)(start) + (0, _util.string32)(range[1]) + (0, _util.string32)(code);
      }
      header31012 = '\x00\x0C' + '\x00\x00' + (0, _util.string32)(format31012.length + 16) + '\x00\x00\x00\x00' + (0, _util.string32)(format31012.length / 12);
    }
    return cmap + '\x00\x04' + string16(format314.length + 4) + format314 + header31012 + format31012;
  }
  function validateOS2Table(os2) {
    var stream = new _stream.Stream(os2.data);
    var version = stream.getUint16();
    stream.getBytes(60);
    var selection = stream.getUint16();
    if (version < 4 && selection & 0x0300) {
      return false;
    }
    var firstChar = stream.getUint16();
    var lastChar = stream.getUint16();
    if (firstChar > lastChar) {
      return false;
    }
    stream.getBytes(6);
    var usWinAscent = stream.getUint16();
    if (usWinAscent === 0) {
      return false;
    }
    os2.data[8] = os2.data[9] = 0;
    return true;
  }
  function createOS2Table(properties, charstrings, override) {
    override = override || {
      unitsPerEm: 0,
      yMax: 0,
      yMin: 0,
      ascent: 0,
      descent: 0
    };
    var ulUnicodeRange1 = 0;
    var ulUnicodeRange2 = 0;
    var ulUnicodeRange3 = 0;
    var ulUnicodeRange4 = 0;
    var firstCharIndex = null;
    var lastCharIndex = 0;
    if (charstrings) {
      for (var code in charstrings) {
        code |= 0;
        if (firstCharIndex > code || !firstCharIndex) {
          firstCharIndex = code;
        }
        if (lastCharIndex < code) {
          lastCharIndex = code;
        }
        var position = (0, _unicode.getUnicodeRangeFor)(code);
        if (position < 32) {
          ulUnicodeRange1 |= 1 << position;
        } else if (position < 64) {
          ulUnicodeRange2 |= 1 << position - 32;
        } else if (position < 96) {
          ulUnicodeRange3 |= 1 << position - 64;
        } else if (position < 123) {
          ulUnicodeRange4 |= 1 << position - 96;
        } else {
          throw new _util.FormatError('Unicode ranges Bits > 123 are reserved for internal usage');
        }
      }
    } else {
      firstCharIndex = 0;
      lastCharIndex = 255;
    }
    var bbox = properties.bbox || [0, 0, 0, 0];
    var unitsPerEm = override.unitsPerEm || 1 / (properties.fontMatrix || _util.FONT_IDENTITY_MATRIX)[0];
    var scale = properties.ascentScaled ? 1.0 : unitsPerEm / PDF_GLYPH_SPACE_UNITS;
    var typoAscent = override.ascent || Math.round(scale * (properties.ascent || bbox[3]));
    var typoDescent = override.descent || Math.round(scale * (properties.descent || bbox[1]));
    if (typoDescent > 0 && properties.descent > 0 && bbox[1] < 0) {
      typoDescent = -typoDescent;
    }
    var winAscent = override.yMax || typoAscent;
    var winDescent = -override.yMin || -typoDescent;
    return '\x00\x03' + '\x02\x24' + '\x01\xF4' + '\x00\x05' + '\x00\x00' + '\x02\x8A' + '\x02\xBB' + '\x00\x00' + '\x00\x8C' + '\x02\x8A' + '\x02\xBB' + '\x00\x00' + '\x01\xDF' + '\x00\x31' + '\x01\x02' + '\x00\x00' + '\x00\x00\x06' + String.fromCharCode(properties.fixedPitch ? 0x09 : 0x00) + '\x00\x00\x00\x00\x00\x00' + (0, _util.string32)(ulUnicodeRange1) + (0, _util.string32)(ulUnicodeRange2) + (0, _util.string32)(ulUnicodeRange3) + (0, _util.string32)(ulUnicodeRange4) + '\x2A\x32\x31\x2A' + string16(properties.italicAngle ? 1 : 0) + string16(firstCharIndex || properties.firstChar) + string16(lastCharIndex || properties.lastChar) + string16(typoAscent) + string16(typoDescent) + '\x00\x64' + string16(winAscent) + string16(winDescent) + '\x00\x00\x00\x00' + '\x00\x00\x00\x00' + string16(properties.xHeight) + string16(properties.capHeight) + string16(0) + string16(firstCharIndex || properties.firstChar) + '\x00\x03';
  }
  function createPostTable(properties) {
    var angle = Math.floor(properties.italicAngle * Math.pow(2, 16));
    return '\x00\x03\x00\x00' + (0, _util.string32)(angle) + '\x00\x00' + '\x00\x00' + (0, _util.string32)(properties.fixedPitch) + '\x00\x00\x00\x00' + '\x00\x00\x00\x00' + '\x00\x00\x00\x00' + '\x00\x00\x00\x00';
  }
  function createNameTable(name, proto) {
    if (!proto) {
      proto = [[], []];
    }
    var strings = [proto[0][0] || 'Original licence', proto[0][1] || name, proto[0][2] || 'Unknown', proto[0][3] || 'uniqueID', proto[0][4] || name, proto[0][5] || 'Version 0.11', proto[0][6] || '', proto[0][7] || 'Unknown', proto[0][8] || 'Unknown', proto[0][9] || 'Unknown'];
    var stringsUnicode = [];
    var i, ii, j, jj, str;
    for (i = 0, ii = strings.length; i < ii; i++) {
      str = proto[1][i] || strings[i];
      var strBufUnicode = [];
      for (j = 0, jj = str.length; j < jj; j++) {
        strBufUnicode.push(string16(str.charCodeAt(j)));
      }
      stringsUnicode.push(strBufUnicode.join(''));
    }
    var names = [strings, stringsUnicode];
    var platforms = ['\x00\x01', '\x00\x03'];
    var encodings = ['\x00\x00', '\x00\x01'];
    var languages = ['\x00\x00', '\x04\x09'];
    var namesRecordCount = strings.length * platforms.length;
    var nameTable = '\x00\x00' + string16(namesRecordCount) + string16(namesRecordCount * 12 + 6);
    var strOffset = 0;
    for (i = 0, ii = platforms.length; i < ii; i++) {
      var strs = names[i];
      for (j = 0, jj = strs.length; j < jj; j++) {
        str = strs[j];
        var nameRecord = platforms[i] + encodings[i] + languages[i] + string16(j) + string16(str.length) + string16(strOffset);
        nameTable += nameRecord;
        strOffset += str.length;
      }
    }
    nameTable += strings.join('') + stringsUnicode.join('');
    return nameTable;
  }
  Font.prototype = {
    name: null,
    font: null,
    mimetype: null,
    encoding: null,
    get renderer() {
      var renderer = _font_renderer.FontRendererFactory.create(this, SEAC_ANALYSIS_ENABLED);
      return (0, _util.shadow)(this, 'renderer', renderer);
    },
    exportData: function Font_exportData() {
      var data = {};
      for (var i in this) {
        if (this.hasOwnProperty(i)) {
          data[i] = this[i];
        }
      }
      return data;
    },
    fallbackToSystemFont: function Font_fallbackToSystemFont() {
      this.missingFile = true;
      var charCode, unicode;
      var name = this.name;
      var type = this.type;
      var subtype = this.subtype;
      var fontName = name.replace(/[,_]/g, '-');
      var stdFontMap = (0, _standard_fonts.getStdFontMap)(),
          nonStdFontMap = (0, _standard_fonts.getNonStdFontMap)();
      var isStandardFont = !!stdFontMap[fontName] || !!(nonStdFontMap[fontName] && stdFontMap[nonStdFontMap[fontName]]);
      fontName = stdFontMap[fontName] || nonStdFontMap[fontName] || fontName;
      this.bold = fontName.search(/bold/gi) !== -1;
      this.italic = fontName.search(/oblique/gi) !== -1 || fontName.search(/italic/gi) !== -1;
      this.black = name.search(/Black/g) !== -1;
      this.remeasure = Object.keys(this.widths).length > 0;
      if (isStandardFont && type === 'CIDFontType2' && this.cidEncoding.indexOf('Identity-') === 0) {
        var GlyphMapForStandardFonts = (0, _standard_fonts.getGlyphMapForStandardFonts)();
        var map = [];
        for (charCode in GlyphMapForStandardFonts) {
          map[+charCode] = GlyphMapForStandardFonts[charCode];
        }
        if (/Arial-?Black/i.test(name)) {
          var SupplementalGlyphMapForArialBlack = (0, _standard_fonts.getSupplementalGlyphMapForArialBlack)();
          for (charCode in SupplementalGlyphMapForArialBlack) {
            map[+charCode] = SupplementalGlyphMapForArialBlack[charCode];
          }
        } else if (/Calibri/i.test(name)) {
          let SupplementalGlyphMapForCalibri = (0, _standard_fonts.getSupplementalGlyphMapForCalibri)();
          for (charCode in SupplementalGlyphMapForCalibri) {
            map[+charCode] = SupplementalGlyphMapForCalibri[charCode];
          }
        }
        var isIdentityUnicode = this.toUnicode instanceof IdentityToUnicodeMap;
        if (!isIdentityUnicode) {
          this.toUnicode.forEach(function (charCode, unicodeCharCode) {
            map[+charCode] = unicodeCharCode;
          });
        }
        this.toFontChar = map;
        this.toUnicode = new ToUnicodeMap(map);
      } else if (/Symbol/i.test(fontName)) {
        this.toFontChar = buildToFontChar(_encodings.SymbolSetEncoding, (0, _glyphlist.getGlyphsUnicode)(), this.differences);
      } else if (/Dingbats/i.test(fontName)) {
        if (/Wingdings/i.test(name)) {
          (0, _util.warn)('Non-embedded Wingdings font, falling back to ZapfDingbats.');
        }
        this.toFontChar = buildToFontChar(_encodings.ZapfDingbatsEncoding, (0, _glyphlist.getDingbatsGlyphsUnicode)(), this.differences);
      } else if (isStandardFont) {
        this.toFontChar = buildToFontChar(this.defaultEncoding, (0, _glyphlist.getGlyphsUnicode)(), this.differences);
      } else {
        var glyphsUnicodeMap = (0, _glyphlist.getGlyphsUnicode)();
        this.toUnicode.forEach((charCode, unicodeCharCode) => {
          if (!this.composite) {
            var glyphName = this.differences[charCode] || this.defaultEncoding[charCode];
            unicode = (0, _unicode.getUnicodeForGlyph)(glyphName, glyphsUnicodeMap);
            if (unicode !== -1) {
              unicodeCharCode = unicode;
            }
          }
          this.toFontChar[charCode] = unicodeCharCode;
        });
      }
      this.loadedName = fontName.split('-')[0];
      this.loading = false;
      this.fontType = getFontType(type, subtype);
    },
    checkAndRepair: function Font_checkAndRepair(name, font, properties) {
      const VALID_TABLES = ['OS/2', 'cmap', 'head', 'hhea', 'hmtx', 'maxp', 'name', 'post', 'loca', 'glyf', 'fpgm', 'prep', 'cvt ', 'CFF '];
      function readTables(file, numTables) {
        let tables = Object.create(null);
        tables['OS/2'] = null;
        tables['cmap'] = null;
        tables['head'] = null;
        tables['hhea'] = null;
        tables['hmtx'] = null;
        tables['maxp'] = null;
        tables['name'] = null;
        tables['post'] = null;
        for (let i = 0; i < numTables; i++) {
          let table = readTableEntry(font);
          if (!VALID_TABLES.includes(table.tag)) {
            continue;
          }
          if (table.length === 0) {
            continue;
          }
          tables[table.tag] = table;
        }
        return tables;
      }
      function readTableEntry(file) {
        var tag = (0, _util.bytesToString)(file.getBytes(4));
        var checksum = file.getInt32() >>> 0;
        var offset = file.getInt32() >>> 0;
        var length = file.getInt32() >>> 0;
        var previousPosition = file.pos;
        file.pos = file.start ? file.start : 0;
        file.skip(offset);
        var data = file.getBytes(length);
        file.pos = previousPosition;
        if (tag === 'head') {
          data[8] = data[9] = data[10] = data[11] = 0;
          data[17] |= 0x20;
        }
        return {
          tag,
          checksum,
          length,
          offset,
          data
        };
      }
      function readOpenTypeHeader(ttf) {
        return {
          version: (0, _util.bytesToString)(ttf.getBytes(4)),
          numTables: ttf.getUint16(),
          searchRange: ttf.getUint16(),
          entrySelector: ttf.getUint16(),
          rangeShift: ttf.getUint16()
        };
      }
      function readTrueTypeCollectionHeader(ttc) {
        let ttcTag = (0, _util.bytesToString)(ttc.getBytes(4));
        (0, _util.assert)(ttcTag === 'ttcf', 'Must be a TrueType Collection font.');
        let majorVersion = ttc.getUint16();
        let minorVersion = ttc.getUint16();
        let numFonts = ttc.getInt32() >>> 0;
        let offsetTable = [];
        for (let i = 0; i < numFonts; i++) {
          offsetTable.push(ttc.getInt32() >>> 0);
        }
        let header = {
          ttcTag,
          majorVersion,
          minorVersion,
          numFonts,
          offsetTable
        };
        switch (majorVersion) {
          case 1:
            return header;
          case 2:
            header.dsigTag = ttc.getInt32() >>> 0;
            header.dsigLength = ttc.getInt32() >>> 0;
            header.dsigOffset = ttc.getInt32() >>> 0;
            return header;
        }
        throw new _util.FormatError(`Invalid TrueType Collection majorVersion: ${majorVersion}.`);
      }
      function readTrueTypeCollectionData(ttc, fontName) {
        let { numFonts, offsetTable } = readTrueTypeCollectionHeader(ttc);
        for (let i = 0; i < numFonts; i++) {
          ttc.pos = (ttc.start || 0) + offsetTable[i];
          let potentialHeader = readOpenTypeHeader(ttc);
          let potentialTables = readTables(ttc, potentialHeader.numTables);
          if (!potentialTables['name']) {
            throw new _util.FormatError('TrueType Collection font must contain a "name" table.');
          }
          let nameTable = readNameTable(potentialTables['name']);
          for (let j = 0, jj = nameTable.length; j < jj; j++) {
            for (let k = 0, kk = nameTable[j].length; k < kk; k++) {
              let nameEntry = nameTable[j][k];
              if (nameEntry && nameEntry.replace(/\s/g, '') === fontName) {
                return {
                  header: potentialHeader,
                  tables: potentialTables
                };
              }
            }
          }
        }
        throw new _util.FormatError(`TrueType Collection does not contain "${fontName}" font.`);
      }
      function readCmapTable(cmap, font, isSymbolicFont, hasEncoding) {
        if (!cmap) {
          (0, _util.warn)('No cmap table available.');
          return {
            platformId: -1,
            encodingId: -1,
            mappings: [],
            hasShortCmap: false
          };
        }
        var segment;
        var start = (font.start ? font.start : 0) + cmap.offset;
        font.pos = start;
        font.getUint16();
        var numTables = font.getUint16();
        var potentialTable;
        var canBreak = false;
        for (var i = 0; i < numTables; i++) {
          var platformId = font.getUint16();
          var encodingId = font.getUint16();
          var offset = font.getInt32() >>> 0;
          var useTable = false;
          if (potentialTable && potentialTable.platformId === platformId && potentialTable.encodingId === encodingId) {
            continue;
          }
          if (platformId === 0 && encodingId === 0) {
            useTable = true;
          } else if (platformId === 1 && encodingId === 0) {
            useTable = true;
          } else if (platformId === 3 && encodingId === 1 && (hasEncoding || !potentialTable)) {
            useTable = true;
            if (!isSymbolicFont) {
              canBreak = true;
            }
          } else if (isSymbolicFont && platformId === 3 && encodingId === 0) {
            useTable = true;
            canBreak = true;
          }
          if (useTable) {
            potentialTable = {
              platformId,
              encodingId,
              offset
            };
          }
          if (canBreak) {
            break;
          }
        }
        if (potentialTable) {
          font.pos = start + potentialTable.offset;
        }
        if (!potentialTable || font.peekByte() === -1) {
          (0, _util.warn)('Could not find a preferred cmap table.');
          return {
            platformId: -1,
            encodingId: -1,
            mappings: [],
            hasShortCmap: false
          };
        }
        var format = font.getUint16();
        font.getUint16();
        font.getUint16();
        var hasShortCmap = false;
        var mappings = [];
        var j, glyphId;
        if (format === 0) {
          for (j = 0; j < 256; j++) {
            var index = font.getByte();
            if (!index) {
              continue;
            }
            mappings.push({
              charCode: j,
              glyphId: index
            });
          }
          hasShortCmap = true;
        } else if (format === 4) {
          var segCount = font.getUint16() >> 1;
          font.getBytes(6);
          var segIndex,
              segments = [];
          for (segIndex = 0; segIndex < segCount; segIndex++) {
            segments.push({ end: font.getUint16() });
          }
          font.getUint16();
          for (segIndex = 0; segIndex < segCount; segIndex++) {
            segments[segIndex].start = font.getUint16();
          }
          for (segIndex = 0; segIndex < segCount; segIndex++) {
            segments[segIndex].delta = font.getUint16();
          }
          var offsetsCount = 0;
          for (segIndex = 0; segIndex < segCount; segIndex++) {
            segment = segments[segIndex];
            var rangeOffset = font.getUint16();
            if (!rangeOffset) {
              segment.offsetIndex = -1;
              continue;
            }
            var offsetIndex = (rangeOffset >> 1) - (segCount - segIndex);
            segment.offsetIndex = offsetIndex;
            offsetsCount = Math.max(offsetsCount, offsetIndex + segment.end - segment.start + 1);
          }
          var offsets = [];
          for (j = 0; j < offsetsCount; j++) {
            offsets.push(font.getUint16());
          }
          for (segIndex = 0; segIndex < segCount; segIndex++) {
            segment = segments[segIndex];
            start = segment.start;
            var end = segment.end;
            var delta = segment.delta;
            offsetIndex = segment.offsetIndex;
            for (j = start; j <= end; j++) {
              if (j === 0xFFFF) {
                continue;
              }
              glyphId = offsetIndex < 0 ? j : offsets[offsetIndex + j - start];
              glyphId = glyphId + delta & 0xFFFF;
              mappings.push({
                charCode: j,
                glyphId
              });
            }
          }
        } else if (format === 6) {
          var firstCode = font.getUint16();
          var entryCount = font.getUint16();
          for (j = 0; j < entryCount; j++) {
            glyphId = font.getUint16();
            var charCode = firstCode + j;
            mappings.push({
              charCode,
              glyphId
            });
          }
        } else {
          (0, _util.warn)('cmap table has unsupported format: ' + format);
          return {
            platformId: -1,
            encodingId: -1,
            mappings: [],
            hasShortCmap: false
          };
        }
        mappings.sort(function (a, b) {
          return a.charCode - b.charCode;
        });
        for (i = 1; i < mappings.length; i++) {
          if (mappings[i - 1].charCode === mappings[i].charCode) {
            mappings.splice(i, 1);
            i--;
          }
        }
        return {
          platformId: potentialTable.platformId,
          encodingId: potentialTable.encodingId,
          mappings,
          hasShortCmap
        };
      }
      function sanitizeMetrics(font, header, metrics, numGlyphs) {
        if (!header) {
          if (metrics) {
            metrics.data = null;
          }
          return;
        }
        font.pos = (font.start ? font.start : 0) + header.offset;
        font.pos += header.length - 2;
        var numOfMetrics = font.getUint16();
        if (numOfMetrics > numGlyphs) {
          (0, _util.info)('The numOfMetrics (' + numOfMetrics + ') should not be ' + 'greater than the numGlyphs (' + numGlyphs + ')');
          numOfMetrics = numGlyphs;
          header.data[34] = (numOfMetrics & 0xff00) >> 8;
          header.data[35] = numOfMetrics & 0x00ff;
        }
        var numOfSidebearings = numGlyphs - numOfMetrics;
        var numMissing = numOfSidebearings - (metrics.length - numOfMetrics * 4 >> 1);
        if (numMissing > 0) {
          var entries = new Uint8Array(metrics.length + numMissing * 2);
          entries.set(metrics.data);
          metrics.data = entries;
        }
      }
      function sanitizeGlyph(source, sourceStart, sourceEnd, dest, destStart, hintsValid) {
        var glyphProfile = {
          length: 0,
          sizeOfInstructions: 0
        };
        if (sourceEnd - sourceStart <= 12) {
          return glyphProfile;
        }
        var glyf = source.subarray(sourceStart, sourceEnd);
        var contoursCount = signedInt16(glyf[0], glyf[1]);
        if (contoursCount < 0) {
          contoursCount = -1;
          writeSignedInt16(glyf, 0, contoursCount);
          dest.set(glyf, destStart);
          glyphProfile.length = glyf.length;
          return glyphProfile;
        }
        var i,
            j = 10,
            flagsCount = 0;
        for (i = 0; i < contoursCount; i++) {
          var endPoint = glyf[j] << 8 | glyf[j + 1];
          flagsCount = endPoint + 1;
          j += 2;
        }
        var instructionsStart = j;
        var instructionsLength = glyf[j] << 8 | glyf[j + 1];
        glyphProfile.sizeOfInstructions = instructionsLength;
        j += 2 + instructionsLength;
        var instructionsEnd = j;
        var coordinatesLength = 0;
        for (i = 0; i < flagsCount; i++) {
          var flag = glyf[j++];
          if (flag & 0xC0) {
            glyf[j - 1] = flag & 0x3F;
          }
          var xyLength = (flag & 2 ? 1 : flag & 16 ? 0 : 2) + (flag & 4 ? 1 : flag & 32 ? 0 : 2);
          coordinatesLength += xyLength;
          if (flag & 8) {
            var repeat = glyf[j++];
            i += repeat;
            coordinatesLength += repeat * xyLength;
          }
        }
        if (coordinatesLength === 0) {
          return glyphProfile;
        }
        var glyphDataLength = j + coordinatesLength;
        if (glyphDataLength > glyf.length) {
          return glyphProfile;
        }
        if (!hintsValid && instructionsLength > 0) {
          dest.set(glyf.subarray(0, instructionsStart), destStart);
          dest.set([0, 0], destStart + instructionsStart);
          dest.set(glyf.subarray(instructionsEnd, glyphDataLength), destStart + instructionsStart + 2);
          glyphDataLength -= instructionsLength;
          if (glyf.length - glyphDataLength > 3) {
            glyphDataLength = glyphDataLength + 3 & ~3;
          }
          glyphProfile.length = glyphDataLength;
          return glyphProfile;
        }
        if (glyf.length - glyphDataLength > 3) {
          glyphDataLength = glyphDataLength + 3 & ~3;
          dest.set(glyf.subarray(0, glyphDataLength), destStart);
          glyphProfile.length = glyphDataLength;
          return glyphProfile;
        }
        dest.set(glyf, destStart);
        glyphProfile.length = glyf.length;
        return glyphProfile;
      }
      function sanitizeHead(head, numGlyphs, locaLength) {
        var data = head.data;
        var version = int32(data[0], data[1], data[2], data[3]);
        if (version >> 16 !== 1) {
          (0, _util.info)('Attempting to fix invalid version in head table: ' + version);
          data[0] = 0;
          data[1] = 1;
          data[2] = 0;
          data[3] = 0;
        }
        var indexToLocFormat = int16(data[50], data[51]);
        if (indexToLocFormat < 0 || indexToLocFormat > 1) {
          (0, _util.info)('Attempting to fix invalid indexToLocFormat in head table: ' + indexToLocFormat);
          var numGlyphsPlusOne = numGlyphs + 1;
          if (locaLength === numGlyphsPlusOne << 1) {
            data[50] = 0;
            data[51] = 0;
          } else if (locaLength === numGlyphsPlusOne << 2) {
            data[50] = 0;
            data[51] = 1;
          } else {
            throw new _util.FormatError('Could not fix indexToLocFormat: ' + indexToLocFormat);
          }
        }
      }
      function sanitizeGlyphLocations(loca, glyf, numGlyphs, isGlyphLocationsLong, hintsValid, dupFirstEntry, maxSizeOfInstructions) {
        var itemSize, itemDecode, itemEncode;
        if (isGlyphLocationsLong) {
          itemSize = 4;
          itemDecode = function fontItemDecodeLong(data, offset) {
            return data[offset] << 24 | data[offset + 1] << 16 | data[offset + 2] << 8 | data[offset + 3];
          };
          itemEncode = function fontItemEncodeLong(data, offset, value) {
            data[offset] = value >>> 24 & 0xFF;
            data[offset + 1] = value >> 16 & 0xFF;
            data[offset + 2] = value >> 8 & 0xFF;
            data[offset + 3] = value & 0xFF;
          };
        } else {
          itemSize = 2;
          itemDecode = function fontItemDecode(data, offset) {
            return data[offset] << 9 | data[offset + 1] << 1;
          };
          itemEncode = function fontItemEncode(data, offset, value) {
            data[offset] = value >> 9 & 0xFF;
            data[offset + 1] = value >> 1 & 0xFF;
          };
        }
        var locaData = loca.data;
        var locaDataSize = itemSize * (1 + numGlyphs);
        if (locaData.length !== locaDataSize) {
          locaData = new Uint8Array(locaDataSize);
          locaData.set(loca.data.subarray(0, locaDataSize));
          loca.data = locaData;
        }
        var oldGlyfData = glyf.data;
        var oldGlyfDataLength = oldGlyfData.length;
        var newGlyfData = new Uint8Array(oldGlyfDataLength);
        var startOffset = itemDecode(locaData, 0);
        var writeOffset = 0;
        var missingGlyphs = Object.create(null);
        itemEncode(locaData, 0, writeOffset);
        var i, j;
        var locaCount = dupFirstEntry ? numGlyphs - 1 : numGlyphs;
        for (i = 0, j = itemSize; i < locaCount; i++, j += itemSize) {
          var endOffset = itemDecode(locaData, j);
          if (endOffset === 0) {
            endOffset = startOffset;
          }
          if (endOffset > oldGlyfDataLength && (oldGlyfDataLength + 3 & ~3) === endOffset) {
            endOffset = oldGlyfDataLength;
          }
          if (endOffset > oldGlyfDataLength) {
            startOffset = endOffset;
          }
          var glyphProfile = sanitizeGlyph(oldGlyfData, startOffset, endOffset, newGlyfData, writeOffset, hintsValid);
          var newLength = glyphProfile.length;
          if (newLength === 0) {
            missingGlyphs[i] = true;
          }
          if (glyphProfile.sizeOfInstructions > maxSizeOfInstructions) {
            maxSizeOfInstructions = glyphProfile.sizeOfInstructions;
          }
          writeOffset += newLength;
          itemEncode(locaData, j, writeOffset);
          startOffset = endOffset;
        }
        if (writeOffset === 0) {
          var simpleGlyph = new Uint8Array([0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 0]);
          for (i = 0, j = itemSize; i < numGlyphs; i++, j += itemSize) {
            itemEncode(locaData, j, simpleGlyph.length);
          }
          glyf.data = simpleGlyph;
        } else if (dupFirstEntry) {
          var firstEntryLength = itemDecode(locaData, itemSize);
          if (newGlyfData.length > firstEntryLength + writeOffset) {
            glyf.data = newGlyfData.subarray(0, firstEntryLength + writeOffset);
          } else {
            glyf.data = new Uint8Array(firstEntryLength + writeOffset);
            glyf.data.set(newGlyfData.subarray(0, writeOffset));
          }
          glyf.data.set(newGlyfData.subarray(0, firstEntryLength), writeOffset);
          itemEncode(loca.data, locaData.length - itemSize, writeOffset + firstEntryLength);
        } else {
          glyf.data = newGlyfData.subarray(0, writeOffset);
        }
        return {
          missingGlyphs,
          maxSizeOfInstructions
        };
      }
      function readPostScriptTable(post, properties, maxpNumGlyphs) {
        var start = (font.start ? font.start : 0) + post.offset;
        font.pos = start;
        var length = post.length,
            end = start + length;
        var version = font.getInt32();
        font.getBytes(28);
        var glyphNames;
        var valid = true;
        var i;
        switch (version) {
          case 0x00010000:
            glyphNames = MacStandardGlyphOrdering;
            break;
          case 0x00020000:
            var numGlyphs = font.getUint16();
            if (numGlyphs !== maxpNumGlyphs) {
              valid = false;
              break;
            }
            var glyphNameIndexes = [];
            for (i = 0; i < numGlyphs; ++i) {
              var index = font.getUint16();
              if (index >= 32768) {
                valid = false;
                break;
              }
              glyphNameIndexes.push(index);
            }
            if (!valid) {
              break;
            }
            var customNames = [];
            var strBuf = [];
            while (font.pos < end) {
              var stringLength = font.getByte();
              strBuf.length = stringLength;
              for (i = 0; i < stringLength; ++i) {
                strBuf[i] = String.fromCharCode(font.getByte());
              }
              customNames.push(strBuf.join(''));
            }
            glyphNames = [];
            for (i = 0; i < numGlyphs; ++i) {
              var j = glyphNameIndexes[i];
              if (j < 258) {
                glyphNames.push(MacStandardGlyphOrdering[j]);
                continue;
              }
              glyphNames.push(customNames[j - 258]);
            }
            break;
          case 0x00030000:
            break;
          default:
            (0, _util.warn)('Unknown/unsupported post table version ' + version);
            valid = false;
            if (properties.defaultEncoding) {
              glyphNames = properties.defaultEncoding;
            }
            break;
        }
        properties.glyphNames = glyphNames;
        return valid;
      }
      function readNameTable(nameTable) {
        var start = (font.start ? font.start : 0) + nameTable.offset;
        font.pos = start;
        var names = [[], []];
        var length = nameTable.length,
            end = start + length;
        var format = font.getUint16();
        var FORMAT_0_HEADER_LENGTH = 6;
        if (format !== 0 || length < FORMAT_0_HEADER_LENGTH) {
          return names;
        }
        var numRecords = font.getUint16();
        var stringsStart = font.getUint16();
        var records = [];
        var NAME_RECORD_LENGTH = 12;
        var i, ii;
        for (i = 0; i < numRecords && font.pos + NAME_RECORD_LENGTH <= end; i++) {
          var r = {
            platform: font.getUint16(),
            encoding: font.getUint16(),
            language: font.getUint16(),
            name: font.getUint16(),
            length: font.getUint16(),
            offset: font.getUint16()
          };
          if (r.platform === 1 && r.encoding === 0 && r.language === 0 || r.platform === 3 && r.encoding === 1 && r.language === 0x409) {
            records.push(r);
          }
        }
        for (i = 0, ii = records.length; i < ii; i++) {
          var record = records[i];
          if (record.length <= 0) {
            continue;
          }
          var pos = start + stringsStart + record.offset;
          if (pos + record.length > end) {
            continue;
          }
          font.pos = pos;
          var nameIndex = record.name;
          if (record.encoding) {
            var str = '';
            for (var j = 0, jj = record.length; j < jj; j += 2) {
              str += String.fromCharCode(font.getUint16());
            }
            names[1][nameIndex] = str;
          } else {
            names[0][nameIndex] = (0, _util.bytesToString)(font.getBytes(record.length));
          }
        }
        return names;
      }
      var TTOpsStackDeltas = [0, 0, 0, 0, 0, 0, 0, 0, -2, -2, -2, -2, 0, 0, -2, -5, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, -1, 0, -1, -1, -1, -1, 1, -1, -999, 0, 1, 0, -1, -2, 0, -1, -2, -1, -1, 0, -1, -1, 0, 0, -999, -999, -1, -1, -1, -1, -2, -999, -2, -2, -999, 0, -2, -2, 0, 0, -2, 0, -2, 0, 0, 0, -2, -1, -1, 1, 1, 0, 0, -1, -1, -1, -1, -1, -1, -1, 0, 0, -1, 0, -1, -1, 0, -999, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -999, -999, -999, -999, -999, -1, -1, -2, -2, 0, 0, 0, 0, -1, -1, -999, -2, -2, 0, 0, -1, -2, -2, 0, 0, 0, -1, -1, -1, -2];
      function sanitizeTTProgram(table, ttContext) {
        var data = table.data;
        var i = 0,
            j,
            n,
            b,
            funcId,
            pc,
            lastEndf = 0,
            lastDeff = 0;
        var stack = [];
        var callstack = [];
        var functionsCalled = [];
        var tooComplexToFollowFunctions = ttContext.tooComplexToFollowFunctions;
        var inFDEF = false,
            ifLevel = 0,
            inELSE = 0;
        for (var ii = data.length; i < ii;) {
          var op = data[i++];
          if (op === 0x40) {
            n = data[i++];
            if (inFDEF || inELSE) {
              i += n;
            } else {
              for (j = 0; j < n; j++) {
                stack.push(data[i++]);
              }
            }
          } else if (op === 0x41) {
            n = data[i++];
            if (inFDEF || inELSE) {
              i += n * 2;
            } else {
              for (j = 0; j < n; j++) {
                b = data[i++];
                stack.push(b << 8 | data[i++]);
              }
            }
          } else if ((op & 0xF8) === 0xB0) {
            n = op - 0xB0 + 1;
            if (inFDEF || inELSE) {
              i += n;
            } else {
              for (j = 0; j < n; j++) {
                stack.push(data[i++]);
              }
            }
          } else if ((op & 0xF8) === 0xB8) {
            n = op - 0xB8 + 1;
            if (inFDEF || inELSE) {
              i += n * 2;
            } else {
              for (j = 0; j < n; j++) {
                b = data[i++];
                stack.push(b << 8 | data[i++]);
              }
            }
          } else if (op === 0x2B && !tooComplexToFollowFunctions) {
            if (!inFDEF && !inELSE) {
              funcId = stack[stack.length - 1];
              ttContext.functionsUsed[funcId] = true;
              if (funcId in ttContext.functionsStackDeltas) {
                stack.length += ttContext.functionsStackDeltas[funcId];
              } else if (funcId in ttContext.functionsDefined && !functionsCalled.includes(funcId)) {
                callstack.push({
                  data,
                  i,
                  stackTop: stack.length - 1
                });
                functionsCalled.push(funcId);
                pc = ttContext.functionsDefined[funcId];
                if (!pc) {
                  (0, _util.warn)('TT: CALL non-existent function');
                  ttContext.hintsValid = false;
                  return;
                }
                data = pc.data;
                i = pc.i;
              }
            }
          } else if (op === 0x2C && !tooComplexToFollowFunctions) {
            if (inFDEF || inELSE) {
              (0, _util.warn)('TT: nested FDEFs not allowed');
              tooComplexToFollowFunctions = true;
            }
            inFDEF = true;
            lastDeff = i;
            funcId = stack.pop();
            ttContext.functionsDefined[funcId] = {
              data,
              i
            };
          } else if (op === 0x2D) {
            if (inFDEF) {
              inFDEF = false;
              lastEndf = i;
            } else {
              pc = callstack.pop();
              if (!pc) {
                (0, _util.warn)('TT: ENDF bad stack');
                ttContext.hintsValid = false;
                return;
              }
              funcId = functionsCalled.pop();
              data = pc.data;
              i = pc.i;
              ttContext.functionsStackDeltas[funcId] = stack.length - pc.stackTop;
            }
          } else if (op === 0x89) {
            if (inFDEF || inELSE) {
              (0, _util.warn)('TT: nested IDEFs not allowed');
              tooComplexToFollowFunctions = true;
            }
            inFDEF = true;
            lastDeff = i;
          } else if (op === 0x58) {
            ++ifLevel;
          } else if (op === 0x1B) {
            inELSE = ifLevel;
          } else if (op === 0x59) {
            if (inELSE === ifLevel) {
              inELSE = 0;
            }
            --ifLevel;
          } else if (op === 0x1C) {
            if (!inFDEF && !inELSE) {
              var offset = stack[stack.length - 1];
              if (offset > 0) {
                i += offset - 1;
              }
            }
          }
          if (!inFDEF && !inELSE) {
            var stackDelta = op <= 0x8E ? TTOpsStackDeltas[op] : op >= 0xC0 && op <= 0xDF ? -1 : op >= 0xE0 ? -2 : 0;
            if (op >= 0x71 && op <= 0x75) {
              n = stack.pop();
              if (!isNaN(n)) {
                stackDelta = -n * 2;
              }
            }
            while (stackDelta < 0 && stack.length > 0) {
              stack.pop();
              stackDelta++;
            }
            while (stackDelta > 0) {
              stack.push(NaN);
              stackDelta--;
            }
          }
        }
        ttContext.tooComplexToFollowFunctions = tooComplexToFollowFunctions;
        var content = [data];
        if (i > data.length) {
          content.push(new Uint8Array(i - data.length));
        }
        if (lastDeff > lastEndf) {
          (0, _util.warn)('TT: complementing a missing function tail');
          content.push(new Uint8Array([0x22, 0x2D]));
        }
        foldTTTable(table, content);
      }
      function checkInvalidFunctions(ttContext, maxFunctionDefs) {
        if (ttContext.tooComplexToFollowFunctions) {
          return;
        }
        if (ttContext.functionsDefined.length > maxFunctionDefs) {
          (0, _util.warn)('TT: more functions defined than expected');
          ttContext.hintsValid = false;
          return;
        }
        for (var j = 0, jj = ttContext.functionsUsed.length; j < jj; j++) {
          if (j > maxFunctionDefs) {
            (0, _util.warn)('TT: invalid function id: ' + j);
            ttContext.hintsValid = false;
            return;
          }
          if (ttContext.functionsUsed[j] && !ttContext.functionsDefined[j]) {
            (0, _util.warn)('TT: undefined function: ' + j);
            ttContext.hintsValid = false;
            return;
          }
        }
      }
      function foldTTTable(table, content) {
        if (content.length > 1) {
          var newLength = 0;
          var j, jj;
          for (j = 0, jj = content.length; j < jj; j++) {
            newLength += content[j].length;
          }
          newLength = newLength + 3 & ~3;
          var result = new Uint8Array(newLength);
          var pos = 0;
          for (j = 0, jj = content.length; j < jj; j++) {
            result.set(content[j], pos);
            pos += content[j].length;
          }
          table.data = result;
          table.length = newLength;
        }
      }
      function sanitizeTTPrograms(fpgm, prep, cvt, maxFunctionDefs) {
        var ttContext = {
          functionsDefined: [],
          functionsUsed: [],
          functionsStackDeltas: [],
          tooComplexToFollowFunctions: false,
          hintsValid: true
        };
        if (fpgm) {
          sanitizeTTProgram(fpgm, ttContext);
        }
        if (prep) {
          sanitizeTTProgram(prep, ttContext);
        }
        if (fpgm) {
          checkInvalidFunctions(ttContext, maxFunctionDefs);
        }
        if (cvt && cvt.length & 1) {
          var cvtData = new Uint8Array(cvt.length + 1);
          cvtData.set(cvt.data);
          cvt.data = cvtData;
        }
        return ttContext.hintsValid;
      }
      font = new _stream.Stream(new Uint8Array(font.getBytes()));
      let header, tables;
      if (isTrueTypeCollectionFile(font)) {
        let ttcData = readTrueTypeCollectionData(font, this.name);
        header = ttcData.header;
        tables = ttcData.tables;
      } else {
        header = readOpenTypeHeader(font);
        tables = readTables(font, header.numTables);
      }
      let cff, cffFile;
      var isTrueType = !tables['CFF '];
      if (!isTrueType) {
        if (header.version === 'OTTO' && !(properties.composite && properties.cidToGidMap) || !tables['head'] || !tables['hhea'] || !tables['maxp'] || !tables['post']) {
          cffFile = new _stream.Stream(tables['CFF '].data);
          cff = new CFFFont(cffFile, properties);
          adjustWidths(properties);
          return this.convert(name, cff, properties);
        }
        delete tables['glyf'];
        delete tables['loca'];
        delete tables['fpgm'];
        delete tables['prep'];
        delete tables['cvt '];
        this.isOpenType = true;
      } else {
        if (!tables['loca']) {
          throw new _util.FormatError('Required "loca" table is not found');
        }
        if (!tables['glyf']) {
          (0, _util.warn)('Required "glyf" table is not found -- trying to recover.');
          tables['glyf'] = {
            tag: 'glyf',
            data: new Uint8Array(0)
          };
        }
        this.isOpenType = false;
      }
      if (!tables['maxp']) {
        throw new _util.FormatError('Required "maxp" table is not found');
      }
      font.pos = (font.start || 0) + tables['maxp'].offset;
      var version = font.getInt32();
      var numGlyphs = font.getUint16();
      var maxFunctionDefs = 0;
      var maxSizeOfInstructions = 0;
      if (version >= 0x00010000 && tables['maxp'].length >= 22) {
        font.pos += 8;
        var maxZones = font.getUint16();
        if (maxZones > 2) {
          tables['maxp'].data[14] = 0;
          tables['maxp'].data[15] = 2;
        }
        font.pos += 4;
        maxFunctionDefs = font.getUint16();
        font.pos += 4;
        maxSizeOfInstructions = font.getUint16();
      }
      var dupFirstEntry = false;
      if (properties.type === 'CIDFontType2' && properties.toUnicode && properties.toUnicode.get(0) > '\u0000') {
        dupFirstEntry = true;
        numGlyphs++;
        tables['maxp'].data[4] = numGlyphs >> 8;
        tables['maxp'].data[5] = numGlyphs & 255;
      }
      var hintsValid = sanitizeTTPrograms(tables['fpgm'], tables['prep'], tables['cvt '], maxFunctionDefs);
      if (!hintsValid) {
        delete tables['fpgm'];
        delete tables['prep'];
        delete tables['cvt '];
      }
      sanitizeMetrics(font, tables['hhea'], tables['hmtx'], numGlyphs);
      if (!tables['head']) {
        throw new _util.FormatError('Required "head" table is not found');
      }
      sanitizeHead(tables['head'], numGlyphs, isTrueType ? tables['loca'].length : 0);
      var missingGlyphs = Object.create(null);
      if (isTrueType) {
        var isGlyphLocationsLong = int16(tables['head'].data[50], tables['head'].data[51]);
        var glyphsInfo = sanitizeGlyphLocations(tables['loca'], tables['glyf'], numGlyphs, isGlyphLocationsLong, hintsValid, dupFirstEntry, maxSizeOfInstructions);
        missingGlyphs = glyphsInfo.missingGlyphs;
        if (version >= 0x00010000 && tables['maxp'].length >= 22) {
          tables['maxp'].data[26] = glyphsInfo.maxSizeOfInstructions >> 8;
          tables['maxp'].data[27] = glyphsInfo.maxSizeOfInstructions & 255;
        }
      }
      if (!tables['hhea']) {
        throw new _util.FormatError('Required "hhea" table is not found');
      }
      if (tables['hhea'].data[10] === 0 && tables['hhea'].data[11] === 0) {
        tables['hhea'].data[10] = 0xFF;
        tables['hhea'].data[11] = 0xFF;
      }
      var metricsOverride = {
        unitsPerEm: int16(tables['head'].data[18], tables['head'].data[19]),
        yMax: int16(tables['head'].data[42], tables['head'].data[43]),
        yMin: signedInt16(tables['head'].data[38], tables['head'].data[39]),
        ascent: int16(tables['hhea'].data[4], tables['hhea'].data[5]),
        descent: signedInt16(tables['hhea'].data[6], tables['hhea'].data[7])
      };
      this.ascent = metricsOverride.ascent / metricsOverride.unitsPerEm;
      this.descent = metricsOverride.descent / metricsOverride.unitsPerEm;
      if (tables['post']) {
        var valid = readPostScriptTable(tables['post'], properties, numGlyphs);
        if (!valid) {
          tables['post'] = null;
        }
      }
      var charCodeToGlyphId = [],
          charCode;
      function hasGlyph(glyphId) {
        return !missingGlyphs[glyphId];
      }
      if (properties.composite) {
        var cidToGidMap = properties.cidToGidMap || [];
        var isCidToGidMapEmpty = cidToGidMap.length === 0;
        properties.cMap.forEach(function (charCode, cid) {
          if (cid > 0xffff) {
            throw new _util.FormatError('Max size of CID is 65,535');
          }
          var glyphId = -1;
          if (isCidToGidMapEmpty) {
            glyphId = cid;
          } else if (cidToGidMap[cid] !== undefined) {
            glyphId = cidToGidMap[cid];
          }
          if (glyphId >= 0 && glyphId < numGlyphs && hasGlyph(glyphId)) {
            charCodeToGlyphId[charCode] = glyphId;
          }
        });
        if (dupFirstEntry && (isCidToGidMapEmpty || !charCodeToGlyphId[0])) {
          charCodeToGlyphId[0] = numGlyphs - 1;
        }
      } else {
        var cmapTable = readCmapTable(tables['cmap'], font, this.isSymbolicFont, properties.hasEncoding);
        var cmapPlatformId = cmapTable.platformId;
        var cmapEncodingId = cmapTable.encodingId;
        var cmapMappings = cmapTable.mappings;
        var cmapMappingsLength = cmapMappings.length;
        if (properties.hasEncoding && (cmapPlatformId === 3 && cmapEncodingId === 1 || cmapPlatformId === 1 && cmapEncodingId === 0) || cmapPlatformId === -1 && cmapEncodingId === -1 && !!(0, _encodings.getEncoding)(properties.baseEncodingName)) {
          var baseEncoding = [];
          if (properties.baseEncodingName === 'MacRomanEncoding' || properties.baseEncodingName === 'WinAnsiEncoding') {
            baseEncoding = (0, _encodings.getEncoding)(properties.baseEncodingName);
          }
          var glyphsUnicodeMap = (0, _glyphlist.getGlyphsUnicode)();
          for (charCode = 0; charCode < 256; charCode++) {
            var glyphName, standardGlyphName;
            if (this.differences && charCode in this.differences) {
              glyphName = this.differences[charCode];
            } else if (charCode in baseEncoding && baseEncoding[charCode] !== '') {
              glyphName = baseEncoding[charCode];
            } else {
              glyphName = _encodings.StandardEncoding[charCode];
            }
            if (!glyphName) {
              continue;
            }
            standardGlyphName = recoverGlyphName(glyphName, glyphsUnicodeMap);
            var unicodeOrCharCode;
            if (cmapPlatformId === 3 && cmapEncodingId === 1) {
              unicodeOrCharCode = glyphsUnicodeMap[standardGlyphName];
            } else if (cmapPlatformId === 1 && cmapEncodingId === 0) {
              unicodeOrCharCode = _encodings.MacRomanEncoding.indexOf(standardGlyphName);
            }
            var found = false;
            for (let i = 0; i < cmapMappingsLength; ++i) {
              if (cmapMappings[i].charCode !== unicodeOrCharCode) {
                continue;
              }
              charCodeToGlyphId[charCode] = cmapMappings[i].glyphId;
              found = true;
              break;
            }
            if (!found && properties.glyphNames) {
              var glyphId = properties.glyphNames.indexOf(glyphName);
              if (glyphId === -1 && standardGlyphName !== glyphName) {
                glyphId = properties.glyphNames.indexOf(standardGlyphName);
              }
              if (glyphId > 0 && hasGlyph(glyphId)) {
                charCodeToGlyphId[charCode] = glyphId;
              }
            }
          }
        } else if (cmapPlatformId === 0 && cmapEncodingId === 0) {
          for (let i = 0; i < cmapMappingsLength; ++i) {
            charCodeToGlyphId[cmapMappings[i].charCode] = cmapMappings[i].glyphId;
          }
        } else {
          for (let i = 0; i < cmapMappingsLength; ++i) {
            charCode = cmapMappings[i].charCode;
            if (cmapPlatformId === 3 && charCode >= 0xF000 && charCode <= 0xF0FF) {
              charCode &= 0xFF;
            }
            charCodeToGlyphId[charCode] = cmapMappings[i].glyphId;
          }
        }
      }
      if (charCodeToGlyphId.length === 0) {
        charCodeToGlyphId[0] = 0;
      }
      var newMapping = adjustMapping(charCodeToGlyphId, properties, missingGlyphs);
      this.toFontChar = newMapping.toFontChar;
      tables['cmap'] = {
        tag: 'cmap',
        data: createCmapTable(newMapping.charCodeToGlyphId, numGlyphs)
      };
      if (!tables['OS/2'] || !validateOS2Table(tables['OS/2'])) {
        tables['OS/2'] = {
          tag: 'OS/2',
          data: createOS2Table(properties, newMapping.charCodeToGlyphId, metricsOverride)
        };
      }
      if (!tables['post']) {
        tables['post'] = {
          tag: 'post',
          data: createPostTable(properties)
        };
      }
      if (!isTrueType) {
        try {
          cffFile = new _stream.Stream(tables['CFF '].data);
          var parser = new _cff_parser.CFFParser(cffFile, properties, SEAC_ANALYSIS_ENABLED);
          cff = parser.parse();
          var compiler = new _cff_parser.CFFCompiler(cff);
          tables['CFF '].data = compiler.compile();
        } catch (e) {
          (0, _util.warn)('Failed to compile font ' + properties.loadedName);
        }
      }
      if (!tables['name']) {
        tables['name'] = {
          tag: 'name',
          data: createNameTable(this.name)
        };
      } else {
        var namePrototype = readNameTable(tables['name']);
        tables['name'].data = createNameTable(name, namePrototype);
      }
      var builder = new OpenTypeFileBuilder(header.version);
      for (var tableTag in tables) {
        builder.addTable(tableTag, tables[tableTag].data);
      }
      return builder.toArray();
    },
    convert: function Font_convert(fontName, font, properties) {
      properties.fixedPitch = false;
      if (properties.builtInEncoding) {
        adjustToUnicode(properties, properties.builtInEncoding);
      }
      var mapping = font.getGlyphMapping(properties);
      var newMapping = adjustMapping(mapping, properties, Object.create(null));
      this.toFontChar = newMapping.toFontChar;
      var numGlyphs = font.numGlyphs;
      function getCharCodes(charCodeToGlyphId, glyphId) {
        var charCodes = null;
        for (var charCode in charCodeToGlyphId) {
          if (glyphId === charCodeToGlyphId[charCode]) {
            if (!charCodes) {
              charCodes = [];
            }
            charCodes.push(charCode | 0);
          }
        }
        return charCodes;
      }
      function createCharCode(charCodeToGlyphId, glyphId) {
        for (var charCode in charCodeToGlyphId) {
          if (glyphId === charCodeToGlyphId[charCode]) {
            return charCode | 0;
          }
        }
        newMapping.charCodeToGlyphId[newMapping.nextAvailableFontCharCode] = glyphId;
        return newMapping.nextAvailableFontCharCode++;
      }
      var seacs = font.seacs;
      if (SEAC_ANALYSIS_ENABLED && seacs && seacs.length) {
        var matrix = properties.fontMatrix || _util.FONT_IDENTITY_MATRIX;
        var charset = font.getCharset();
        var seacMap = Object.create(null);
        for (var glyphId in seacs) {
          glyphId |= 0;
          var seac = seacs[glyphId];
          var baseGlyphName = _encodings.StandardEncoding[seac[2]];
          var accentGlyphName = _encodings.StandardEncoding[seac[3]];
          var baseGlyphId = charset.indexOf(baseGlyphName);
          var accentGlyphId = charset.indexOf(accentGlyphName);
          if (baseGlyphId < 0 || accentGlyphId < 0) {
            continue;
          }
          var accentOffset = {
            x: seac[0] * matrix[0] + seac[1] * matrix[2] + matrix[4],
            y: seac[0] * matrix[1] + seac[1] * matrix[3] + matrix[5]
          };
          var charCodes = getCharCodes(mapping, glyphId);
          if (!charCodes) {
            continue;
          }
          for (var i = 0, ii = charCodes.length; i < ii; i++) {
            var charCode = charCodes[i];
            var charCodeToGlyphId = newMapping.charCodeToGlyphId;
            var baseFontCharCode = createCharCode(charCodeToGlyphId, baseGlyphId);
            var accentFontCharCode = createCharCode(charCodeToGlyphId, accentGlyphId);
            seacMap[charCode] = {
              baseFontCharCode,
              accentFontCharCode,
              accentOffset
            };
          }
        }
        properties.seacMap = seacMap;
      }
      var unitsPerEm = 1 / (properties.fontMatrix || _util.FONT_IDENTITY_MATRIX)[0];
      var builder = new OpenTypeFileBuilder('\x4F\x54\x54\x4F');
      builder.addTable('CFF ', font.data);
      builder.addTable('OS/2', createOS2Table(properties, newMapping.charCodeToGlyphId));
      builder.addTable('cmap', createCmapTable(newMapping.charCodeToGlyphId, numGlyphs));
      builder.addTable('head', '\x00\x01\x00\x00' + '\x00\x00\x10\x00' + '\x00\x00\x00\x00' + '\x5F\x0F\x3C\xF5' + '\x00\x00' + safeString16(unitsPerEm) + '\x00\x00\x00\x00\x9e\x0b\x7e\x27' + '\x00\x00\x00\x00\x9e\x0b\x7e\x27' + '\x00\x00' + safeString16(properties.descent) + '\x0F\xFF' + safeString16(properties.ascent) + string16(properties.italicAngle ? 2 : 0) + '\x00\x11' + '\x00\x00' + '\x00\x00' + '\x00\x00');
      builder.addTable('hhea', '\x00\x01\x00\x00' + safeString16(properties.ascent) + safeString16(properties.descent) + '\x00\x00' + '\xFF\xFF' + '\x00\x00' + '\x00\x00' + '\x00\x00' + safeString16(properties.capHeight) + safeString16(Math.tan(properties.italicAngle) * properties.xHeight) + '\x00\x00' + '\x00\x00' + '\x00\x00' + '\x00\x00' + '\x00\x00' + '\x00\x00' + string16(numGlyphs));
      builder.addTable('hmtx', function fontFieldsHmtx() {
        var charstrings = font.charstrings;
        var cffWidths = font.cff ? font.cff.widths : null;
        var hmtx = '\x00\x00\x00\x00';
        for (var i = 1, ii = numGlyphs; i < ii; i++) {
          var width = 0;
          if (charstrings) {
            var charstring = charstrings[i - 1];
            width = 'width' in charstring ? charstring.width : 0;
          } else if (cffWidths) {
            width = Math.ceil(cffWidths[i] || 0);
          }
          hmtx += string16(width) + string16(0);
        }
        return hmtx;
      }());
      builder.addTable('maxp', '\x00\x00\x50\x00' + string16(numGlyphs));
      builder.addTable('name', createNameTable(fontName));
      builder.addTable('post', createPostTable(properties));
      return builder.toArray();
    },
    get spaceWidth() {
      if ('_shadowWidth' in this) {
        return this._shadowWidth;
      }
      var possibleSpaceReplacements = ['space', 'minus', 'one', 'i', 'I'];
      var width;
      for (var i = 0, ii = possibleSpaceReplacements.length; i < ii; i++) {
        var glyphName = possibleSpaceReplacements[i];
        if (glyphName in this.widths) {
          width = this.widths[glyphName];
          break;
        }
        var glyphsUnicodeMap = (0, _glyphlist.getGlyphsUnicode)();
        var glyphUnicode = glyphsUnicodeMap[glyphName];
        var charcode = 0;
        if (this.composite) {
          if (this.cMap.contains(glyphUnicode)) {
            charcode = this.cMap.lookup(glyphUnicode);
          }
        }
        if (!charcode && this.toUnicode) {
          charcode = this.toUnicode.charCodeOf(glyphUnicode);
        }
        if (charcode <= 0) {
          charcode = glyphUnicode;
        }
        width = this.widths[charcode];
        if (width) {
          break;
        }
      }
      width = width || this.defaultWidth;
      this._shadowWidth = width;
      return width;
    },
    charToGlyph: function Font_charToGlyph(charcode, isSpace) {
      var fontCharCode, width, operatorListId;
      var widthCode = charcode;
      if (this.cMap && this.cMap.contains(charcode)) {
        widthCode = this.cMap.lookup(charcode);
      }
      width = this.widths[widthCode];
      width = (0, _util.isNum)(width) ? width : this.defaultWidth;
      var vmetric = this.vmetrics && this.vmetrics[widthCode];
      let unicode = this.toUnicode.get(charcode) || this.fallbackToUnicode.get(charcode) || charcode;
      if (typeof unicode === 'number') {
        unicode = String.fromCharCode(unicode);
      }
      var isInFont = charcode in this.toFontChar;
      fontCharCode = this.toFontChar[charcode] || charcode;
      if (this.missingFile) {
        fontCharCode = (0, _unicode.mapSpecialUnicodeValues)(fontCharCode);
      }
      if (this.isType3Font) {
        operatorListId = fontCharCode;
      }
      var accent = null;
      if (this.seacMap && this.seacMap[charcode]) {
        isInFont = true;
        var seac = this.seacMap[charcode];
        fontCharCode = seac.baseFontCharCode;
        accent = {
          fontChar: String.fromCharCode(seac.accentFontCharCode),
          offset: seac.accentOffset
        };
      }
      var fontChar = String.fromCharCode(fontCharCode);
      var glyph = this.glyphCache[charcode];
      if (!glyph || !glyph.matchesForCache(fontChar, unicode, accent, width, vmetric, operatorListId, isSpace, isInFont)) {
        glyph = new Glyph(fontChar, unicode, accent, width, vmetric, operatorListId, isSpace, isInFont);
        this.glyphCache[charcode] = glyph;
      }
      return glyph;
    },
    charsToGlyphs: function Font_charsToGlyphs(chars) {
      var charsCache = this.charsCache;
      var glyphs, glyph, charcode;
      if (charsCache) {
        glyphs = charsCache[chars];
        if (glyphs) {
          return glyphs;
        }
      }
      if (!charsCache) {
        charsCache = this.charsCache = Object.create(null);
      }
      glyphs = [];
      var charsCacheKey = chars;
      var i = 0,
          ii;
      if (this.cMap) {
        var c = Object.create(null);
        while (i < chars.length) {
          this.cMap.readCharCode(chars, i, c);
          charcode = c.charcode;
          var length = c.length;
          i += length;
          var isSpace = length === 1 && chars.charCodeAt(i - 1) === 0x20;
          glyph = this.charToGlyph(charcode, isSpace);
          glyphs.push(glyph);
        }
      } else {
        for (i = 0, ii = chars.length; i < ii; ++i) {
          charcode = chars.charCodeAt(i);
          glyph = this.charToGlyph(charcode, charcode === 0x20);
          glyphs.push(glyph);
        }
      }
      return charsCache[charsCacheKey] = glyphs;
    }
  };
  return Font;
}();
var ErrorFont = function ErrorFontClosure() {
  function ErrorFont(error) {
    this.error = error;
    this.loadedName = 'g_font_error';
    this.loading = false;
  }
  ErrorFont.prototype = {
    charsToGlyphs: function ErrorFont_charsToGlyphs() {
      return [];
    },
    exportData: function ErrorFont_exportData() {
      return { error: this.error };
    }
  };
  return ErrorFont;
}();
function type1FontGlyphMapping(properties, builtInEncoding, glyphNames) {
  var charCodeToGlyphId = Object.create(null);
  var glyphId, charCode, baseEncoding;
  var isSymbolicFont = !!(properties.flags & FontFlags.Symbolic);
  if (properties.baseEncodingName) {
    baseEncoding = (0, _encodings.getEncoding)(properties.baseEncodingName);
    for (charCode = 0; charCode < baseEncoding.length; charCode++) {
      glyphId = glyphNames.indexOf(baseEncoding[charCode]);
      if (glyphId >= 0) {
        charCodeToGlyphId[charCode] = glyphId;
      } else {
        charCodeToGlyphId[charCode] = 0;
      }
    }
  } else if (isSymbolicFont) {
    for (charCode in builtInEncoding) {
      charCodeToGlyphId[charCode] = builtInEncoding[charCode];
    }
  } else {
    baseEncoding = _encodings.StandardEncoding;
    for (charCode = 0; charCode < baseEncoding.length; charCode++) {
      glyphId = glyphNames.indexOf(baseEncoding[charCode]);
      if (glyphId >= 0) {
        charCodeToGlyphId[charCode] = glyphId;
      } else {
        charCodeToGlyphId[charCode] = 0;
      }
    }
  }
  var differences = properties.differences,
      glyphsUnicodeMap;
  if (differences) {
    for (charCode in differences) {
      var glyphName = differences[charCode];
      glyphId = glyphNames.indexOf(glyphName);
      if (glyphId === -1) {
        if (!glyphsUnicodeMap) {
          glyphsUnicodeMap = (0, _glyphlist.getGlyphsUnicode)();
        }
        var standardGlyphName = recoverGlyphName(glyphName, glyphsUnicodeMap);
        if (standardGlyphName !== glyphName) {
          glyphId = glyphNames.indexOf(standardGlyphName);
        }
      }
      if (glyphId >= 0) {
        charCodeToGlyphId[charCode] = glyphId;
      } else {
        charCodeToGlyphId[charCode] = 0;
      }
    }
  }
  return charCodeToGlyphId;
}
var Type1Font = function Type1FontClosure() {
  function findBlock(streamBytes, signature, startIndex) {
    var streamBytesLength = streamBytes.length;
    var signatureLength = signature.length;
    var scanLength = streamBytesLength - signatureLength;
    var i = startIndex,
        j,
        found = false;
    while (i < scanLength) {
      j = 0;
      while (j < signatureLength && streamBytes[i + j] === signature[j]) {
        j++;
      }
      if (j >= signatureLength) {
        i += j;
        while (i < streamBytesLength && (0, _util.isSpace)(streamBytes[i])) {
          i++;
        }
        found = true;
        break;
      }
      i++;
    }
    return {
      found,
      length: i
    };
  }
  function getHeaderBlock(stream, suggestedLength) {
    var EEXEC_SIGNATURE = [0x65, 0x65, 0x78, 0x65, 0x63];
    var streamStartPos = stream.pos;
    var headerBytes, headerBytesLength, block;
    try {
      headerBytes = stream.getBytes(suggestedLength);
      headerBytesLength = headerBytes.length;
    } catch (ex) {
      if (ex instanceof _util.MissingDataException) {
        throw ex;
      }
    }
    if (headerBytesLength === suggestedLength) {
      block = findBlock(headerBytes, EEXEC_SIGNATURE, suggestedLength - 2 * EEXEC_SIGNATURE.length);
      if (block.found && block.length === suggestedLength) {
        return {
          stream: new _stream.Stream(headerBytes),
          length: suggestedLength
        };
      }
    }
    (0, _util.warn)('Invalid "Length1" property in Type1 font -- trying to recover.');
    stream.pos = streamStartPos;
    var SCAN_BLOCK_LENGTH = 2048;
    var actualLength;
    while (true) {
      var scanBytes = stream.peekBytes(SCAN_BLOCK_LENGTH);
      block = findBlock(scanBytes, EEXEC_SIGNATURE, 0);
      if (block.length === 0) {
        break;
      }
      stream.pos += block.length;
      if (block.found) {
        actualLength = stream.pos - streamStartPos;
        break;
      }
    }
    stream.pos = streamStartPos;
    if (actualLength) {
      return {
        stream: new _stream.Stream(stream.getBytes(actualLength)),
        length: actualLength
      };
    }
    (0, _util.warn)('Unable to recover "Length1" property in Type1 font -- using as is.');
    return {
      stream: new _stream.Stream(stream.getBytes(suggestedLength)),
      length: suggestedLength
    };
  }
  function getEexecBlock(stream, suggestedLength) {
    var eexecBytes = stream.getBytes();
    return {
      stream: new _stream.Stream(eexecBytes),
      length: eexecBytes.length
    };
  }
  function Type1Font(name, file, properties) {
    var PFB_HEADER_SIZE = 6;
    var headerBlockLength = properties.length1;
    var eexecBlockLength = properties.length2;
    var pfbHeader = file.peekBytes(PFB_HEADER_SIZE);
    var pfbHeaderPresent = pfbHeader[0] === 0x80 && pfbHeader[1] === 0x01;
    if (pfbHeaderPresent) {
      file.skip(PFB_HEADER_SIZE);
      headerBlockLength = pfbHeader[5] << 24 | pfbHeader[4] << 16 | pfbHeader[3] << 8 | pfbHeader[2];
    }
    var headerBlock = getHeaderBlock(file, headerBlockLength);
    var headerBlockParser = new _type1_parser.Type1Parser(headerBlock.stream, false, SEAC_ANALYSIS_ENABLED);
    headerBlockParser.extractFontHeader(properties);
    if (pfbHeaderPresent) {
      pfbHeader = file.getBytes(PFB_HEADER_SIZE);
      eexecBlockLength = pfbHeader[5] << 24 | pfbHeader[4] << 16 | pfbHeader[3] << 8 | pfbHeader[2];
    }
    var eexecBlock = getEexecBlock(file, eexecBlockLength);
    var eexecBlockParser = new _type1_parser.Type1Parser(eexecBlock.stream, true, SEAC_ANALYSIS_ENABLED);
    var data = eexecBlockParser.extractFontProgram();
    for (var info in data.properties) {
      properties[info] = data.properties[info];
    }
    var charstrings = data.charstrings;
    var type2Charstrings = this.getType2Charstrings(charstrings);
    var subrs = this.getType2Subrs(data.subrs);
    this.charstrings = charstrings;
    this.data = this.wrap(name, type2Charstrings, this.charstrings, subrs, properties);
    this.seacs = this.getSeacs(data.charstrings);
  }
  Type1Font.prototype = {
    get numGlyphs() {
      return this.charstrings.length + 1;
    },
    getCharset: function Type1Font_getCharset() {
      var charset = ['.notdef'];
      var charstrings = this.charstrings;
      for (var glyphId = 0; glyphId < charstrings.length; glyphId++) {
        charset.push(charstrings[glyphId].glyphName);
      }
      return charset;
    },
    getGlyphMapping: function Type1Font_getGlyphMapping(properties) {
      var charstrings = this.charstrings;
      var glyphNames = ['.notdef'],
          glyphId;
      for (glyphId = 0; glyphId < charstrings.length; glyphId++) {
        glyphNames.push(charstrings[glyphId].glyphName);
      }
      var encoding = properties.builtInEncoding;
      if (encoding) {
        var builtInEncoding = Object.create(null);
        for (var charCode in encoding) {
          glyphId = glyphNames.indexOf(encoding[charCode]);
          if (glyphId >= 0) {
            builtInEncoding[charCode] = glyphId;
          }
        }
      }
      return type1FontGlyphMapping(properties, builtInEncoding, glyphNames);
    },
    getSeacs: function Type1Font_getSeacs(charstrings) {
      var i, ii;
      var seacMap = [];
      for (i = 0, ii = charstrings.length; i < ii; i++) {
        var charstring = charstrings[i];
        if (charstring.seac) {
          seacMap[i + 1] = charstring.seac;
        }
      }
      return seacMap;
    },
    getType2Charstrings: function Type1Font_getType2Charstrings(type1Charstrings) {
      var type2Charstrings = [];
      for (var i = 0, ii = type1Charstrings.length; i < ii; i++) {
        type2Charstrings.push(type1Charstrings[i].charstring);
      }
      return type2Charstrings;
    },
    getType2Subrs: function Type1Font_getType2Subrs(type1Subrs) {
      var bias = 0;
      var count = type1Subrs.length;
      if (count < 1133) {
        bias = 107;
      } else if (count < 33769) {
        bias = 1131;
      } else {
        bias = 32768;
      }
      var type2Subrs = [];
      var i;
      for (i = 0; i < bias; i++) {
        type2Subrs.push([0x0B]);
      }
      for (i = 0; i < count; i++) {
        type2Subrs.push(type1Subrs[i]);
      }
      return type2Subrs;
    },
    wrap: function Type1Font_wrap(name, glyphs, charstrings, subrs, properties) {
      var cff = new _cff_parser.CFF();
      cff.header = new _cff_parser.CFFHeader(1, 0, 4, 4);
      cff.names = [name];
      var topDict = new _cff_parser.CFFTopDict();
      topDict.setByName('version', 391);
      topDict.setByName('Notice', 392);
      topDict.setByName('FullName', 393);
      topDict.setByName('FamilyName', 394);
      topDict.setByName('Weight', 395);
      topDict.setByName('Encoding', null);
      topDict.setByName('FontMatrix', properties.fontMatrix);
      topDict.setByName('FontBBox', properties.bbox);
      topDict.setByName('charset', null);
      topDict.setByName('CharStrings', null);
      topDict.setByName('Private', null);
      cff.topDict = topDict;
      var strings = new _cff_parser.CFFStrings();
      strings.add('Version 0.11');
      strings.add('See original notice');
      strings.add(name);
      strings.add(name);
      strings.add('Medium');
      cff.strings = strings;
      cff.globalSubrIndex = new _cff_parser.CFFIndex();
      var count = glyphs.length;
      var charsetArray = [0];
      var i, ii;
      for (i = 0; i < count; i++) {
        var index = _cff_parser.CFFStandardStrings.indexOf(charstrings[i].glyphName);
        if (index === -1) {
          index = 0;
        }
        charsetArray.push(index >> 8 & 0xff, index & 0xff);
      }
      cff.charset = new _cff_parser.CFFCharset(false, 0, [], charsetArray);
      var charStringsIndex = new _cff_parser.CFFIndex();
      charStringsIndex.add([0x8B, 0x0E]);
      for (i = 0; i < count; i++) {
        var glyph = glyphs[i];
        if (glyph.length === 0) {
          charStringsIndex.add([0x8B, 0x0E]);
          continue;
        }
        charStringsIndex.add(glyph);
      }
      cff.charStrings = charStringsIndex;
      var privateDict = new _cff_parser.CFFPrivateDict();
      privateDict.setByName('Subrs', null);
      var fields = ['BlueValues', 'OtherBlues', 'FamilyBlues', 'FamilyOtherBlues', 'StemSnapH', 'StemSnapV', 'BlueShift', 'BlueFuzz', 'BlueScale', 'LanguageGroup', 'ExpansionFactor', 'ForceBold', 'StdHW', 'StdVW'];
      for (i = 0, ii = fields.length; i < ii; i++) {
        var field = fields[i];
        if (!(field in properties.privateData)) {
          continue;
        }
        var value = properties.privateData[field];
        if (Array.isArray(value)) {
          for (var j = value.length - 1; j > 0; j--) {
            value[j] -= value[j - 1];
          }
        }
        privateDict.setByName(field, value);
      }
      cff.topDict.privateDict = privateDict;
      var subrIndex = new _cff_parser.CFFIndex();
      for (i = 0, ii = subrs.length; i < ii; i++) {
        subrIndex.add(subrs[i]);
      }
      privateDict.subrsIndex = subrIndex;
      var compiler = new _cff_parser.CFFCompiler(cff);
      return compiler.compile();
    }
  };
  return Type1Font;
}();
var CFFFont = function CFFFontClosure() {
  function CFFFont(file, properties) {
    this.properties = properties;
    var parser = new _cff_parser.CFFParser(file, properties, SEAC_ANALYSIS_ENABLED);
    this.cff = parser.parse();
    var compiler = new _cff_parser.CFFCompiler(this.cff);
    this.seacs = this.cff.seacs;
    try {
      this.data = compiler.compile();
    } catch (e) {
      (0, _util.warn)('Failed to compile font ' + properties.loadedName);
      this.data = file;
    }
  }
  CFFFont.prototype = {
    get numGlyphs() {
      return this.cff.charStrings.count;
    },
    getCharset: function CFFFont_getCharset() {
      return this.cff.charset.charset;
    },
    getGlyphMapping: function CFFFont_getGlyphMapping() {
      var cff = this.cff;
      var properties = this.properties;
      var charsets = cff.charset.charset;
      var charCodeToGlyphId;
      var glyphId;
      if (properties.composite) {
        charCodeToGlyphId = Object.create(null);
        if (cff.isCIDFont) {
          for (glyphId = 0; glyphId < charsets.length; glyphId++) {
            var cid = charsets[glyphId];
            var charCode = properties.cMap.charCodeOf(cid);
            charCodeToGlyphId[charCode] = glyphId;
          }
        } else {
          for (glyphId = 0; glyphId < cff.charStrings.count; glyphId++) {
            charCodeToGlyphId[glyphId] = glyphId;
          }
        }
        return charCodeToGlyphId;
      }
      var encoding = cff.encoding ? cff.encoding.encoding : null;
      charCodeToGlyphId = type1FontGlyphMapping(properties, encoding, charsets);
      return charCodeToGlyphId;
    }
  };
  return CFFFont;
}();
(function checkSeacSupport() {
  if (typeof navigator !== 'undefined' && /Windows/.test(navigator.userAgent)) {
    exports.SEAC_ANALYSIS_ENABLED = SEAC_ANALYSIS_ENABLED = true;
  }
})();
(function checkChromeWindows() {
  if (typeof navigator !== 'undefined' && /Windows.*Chrome/.test(navigator.userAgent)) {
    SKIP_PRIVATE_USE_RANGE_F000_TO_F01F = true;
  }
})();
exports.SEAC_ANALYSIS_ENABLED = SEAC_ANALYSIS_ENABLED;
exports.PRIVATE_USE_OFFSET_START = PRIVATE_USE_OFFSET_START;
exports.PRIVATE_USE_OFFSET_END = PRIVATE_USE_OFFSET_END;
exports.ErrorFont = ErrorFont;
exports.Font = Font;
exports.FontFlags = FontFlags;
exports.ToUnicodeMap = ToUnicodeMap;
exports.IdentityToUnicodeMap = IdentityToUnicodeMap;
exports.ProblematicCharRanges = ProblematicCharRanges;
exports.getFontType = getFontType;

/***/ }),
/* 35 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
var ISOAdobeCharset = ['.notdef', 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright', 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash', 'bracketright', 'asciicircum', 'underscore', 'quoteleft', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright', 'asciitilde', 'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin', 'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft', 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'endash', 'dagger', 'daggerdbl', 'periodcentered', 'paragraph', 'bullet', 'quotesinglbase', 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand', 'questiondown', 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve', 'dotaccent', 'dieresis', 'ring', 'cedilla', 'hungarumlaut', 'ogonek', 'caron', 'emdash', 'AE', 'ordfeminine', 'Lslash', 'Oslash', 'OE', 'ordmasculine', 'ae', 'dotlessi', 'lslash', 'oslash', 'oe', 'germandbls', 'onesuperior', 'logicalnot', 'mu', 'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn', 'onequarter', 'divide', 'brokenbar', 'degree', 'thorn', 'threequarters', 'twosuperior', 'registered', 'minus', 'eth', 'multiply', 'threesuperior', 'copyright', 'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring', 'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave', 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute', 'Ocircumflex', 'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute', 'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron', 'aacute', 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla', 'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex', 'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex', 'odieresis', 'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis', 'ugrave', 'yacute', 'ydieresis', 'zcaron'];
var ExpertCharset = ['.notdef', 'space', 'exclamsmall', 'Hungarumlautsmall', 'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall', 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader', 'comma', 'hyphen', 'period', 'fraction', 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle', 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle', 'nineoldstyle', 'colon', 'semicolon', 'commasuperior', 'threequartersemdash', 'periodsuperior', 'questionsmall', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior', 'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior', 'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'parenleftinferior', 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall', 'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall', 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall', 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall', 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall', 'exclamdownsmall', 'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall', 'Dieresissmall', 'Brevesmall', 'Caronsmall', 'Dotaccentsmall', 'Macronsmall', 'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall', 'onequarter', 'onehalf', 'threequarters', 'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths', 'onethird', 'twothirds', 'zerosuperior', 'onesuperior', 'twosuperior', 'threesuperior', 'foursuperior', 'fivesuperior', 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior', 'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior', 'sixinferior', 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior', 'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall', 'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall', 'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall', 'Edieresissmall', 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall', 'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall', 'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall', 'Ugravesmall', 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall', 'Yacutesmall', 'Thornsmall', 'Ydieresissmall'];
var ExpertSubsetCharset = ['.notdef', 'space', 'dollaroldstyle', 'dollarsuperior', 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader', 'comma', 'hyphen', 'period', 'fraction', 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle', 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle', 'nineoldstyle', 'colon', 'semicolon', 'commasuperior', 'threequartersemdash', 'periodsuperior', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior', 'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior', 'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'parenleftinferior', 'parenrightinferior', 'hyphensuperior', 'colonmonetary', 'onefitted', 'rupiah', 'centoldstyle', 'figuredash', 'hypheninferior', 'onequarter', 'onehalf', 'threequarters', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths', 'onethird', 'twothirds', 'zerosuperior', 'onesuperior', 'twosuperior', 'threesuperior', 'foursuperior', 'fivesuperior', 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior', 'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior', 'sixinferior', 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior', 'dollarinferior', 'periodinferior', 'commainferior'];
exports.ISOAdobeCharset = ISOAdobeCharset;
exports.ExpertCharset = ExpertCharset;
exports.ExpertSubsetCharset = ExpertSubsetCharset;

/***/ }),
/* 36 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.FontRendererFactory = undefined;

var _util = __w_pdfjs_require__(0);

var _cff_parser = __w_pdfjs_require__(15);

var _glyphlist = __w_pdfjs_require__(8);

var _encodings = __w_pdfjs_require__(4);

var _stream = __w_pdfjs_require__(2);

var FontRendererFactory = function FontRendererFactoryClosure() {
  function getLong(data, offset) {
    return data[offset] << 24 | data[offset + 1] << 16 | data[offset + 2] << 8 | data[offset + 3];
  }
  function getUshort(data, offset) {
    return data[offset] << 8 | data[offset + 1];
  }
  function parseCmap(data, start, end) {
    var offset = getUshort(data, start + 2) === 1 ? getLong(data, start + 8) : getLong(data, start + 16);
    var format = getUshort(data, start + offset);
    var ranges, p, i;
    if (format === 4) {
      getUshort(data, start + offset + 2);
      var segCount = getUshort(data, start + offset + 6) >> 1;
      p = start + offset + 14;
      ranges = [];
      for (i = 0; i < segCount; i++, p += 2) {
        ranges[i] = { end: getUshort(data, p) };
      }
      p += 2;
      for (i = 0; i < segCount; i++, p += 2) {
        ranges[i].start = getUshort(data, p);
      }
      for (i = 0; i < segCount; i++, p += 2) {
        ranges[i].idDelta = getUshort(data, p);
      }
      for (i = 0; i < segCount; i++, p += 2) {
        var idOffset = getUshort(data, p);
        if (idOffset === 0) {
          continue;
        }
        ranges[i].ids = [];
        for (var j = 0, jj = ranges[i].end - ranges[i].start + 1; j < jj; j++) {
          ranges[i].ids[j] = getUshort(data, p + idOffset);
          idOffset += 2;
        }
      }
      return ranges;
    } else if (format === 12) {
      getLong(data, start + offset + 4);
      var groups = getLong(data, start + offset + 12);
      p = start + offset + 16;
      ranges = [];
      for (i = 0; i < groups; i++) {
        ranges.push({
          start: getLong(data, p),
          end: getLong(data, p + 4),
          idDelta: getLong(data, p + 8) - getLong(data, p)
        });
        p += 12;
      }
      return ranges;
    }
    throw new _util.FormatError(`unsupported cmap: ${format}`);
  }
  function parseCff(data, start, end, seacAnalysisEnabled) {
    var properties = {};
    var parser = new _cff_parser.CFFParser(new _stream.Stream(data, start, end - start), properties, seacAnalysisEnabled);
    var cff = parser.parse();
    return {
      glyphs: cff.charStrings.objects,
      subrs: cff.topDict.privateDict && cff.topDict.privateDict.subrsIndex && cff.topDict.privateDict.subrsIndex.objects,
      gsubrs: cff.globalSubrIndex && cff.globalSubrIndex.objects
    };
  }
  function parseGlyfTable(glyf, loca, isGlyphLocationsLong) {
    var itemSize, itemDecode;
    if (isGlyphLocationsLong) {
      itemSize = 4;
      itemDecode = function fontItemDecodeLong(data, offset) {
        return data[offset] << 24 | data[offset + 1] << 16 | data[offset + 2] << 8 | data[offset + 3];
      };
    } else {
      itemSize = 2;
      itemDecode = function fontItemDecode(data, offset) {
        return data[offset] << 9 | data[offset + 1] << 1;
      };
    }
    var glyphs = [];
    var startOffset = itemDecode(loca, 0);
    for (var j = itemSize; j < loca.length; j += itemSize) {
      var endOffset = itemDecode(loca, j);
      glyphs.push(glyf.subarray(startOffset, endOffset));
      startOffset = endOffset;
    }
    return glyphs;
  }
  function lookupCmap(ranges, unicode) {
    var code = unicode.charCodeAt(0),
        gid = 0;
    var l = 0,
        r = ranges.length - 1;
    while (l < r) {
      var c = l + r + 1 >> 1;
      if (code < ranges[c].start) {
        r = c - 1;
      } else {
        l = c;
      }
    }
    if (ranges[l].start <= code && code <= ranges[l].end) {
      gid = ranges[l].idDelta + (ranges[l].ids ? ranges[l].ids[code - ranges[l].start] : code) & 0xFFFF;
    }
    return {
      charCode: code,
      glyphId: gid
    };
  }
  function compileGlyf(code, cmds, font) {
    function moveTo(x, y) {
      cmds.push({
        cmd: 'moveTo',
        args: [x, y]
      });
    }
    function lineTo(x, y) {
      cmds.push({
        cmd: 'lineTo',
        args: [x, y]
      });
    }
    function quadraticCurveTo(xa, ya, x, y) {
      cmds.push({
        cmd: 'quadraticCurveTo',
        args: [xa, ya, x, y]
      });
    }
    var i = 0;
    var numberOfContours = (code[i] << 24 | code[i + 1] << 16) >> 16;
    var flags;
    var x = 0,
        y = 0;
    i += 10;
    if (numberOfContours < 0) {
      do {
        flags = code[i] << 8 | code[i + 1];
        var glyphIndex = code[i + 2] << 8 | code[i + 3];
        i += 4;
        var arg1, arg2;
        if (flags & 0x01) {
          arg1 = (code[i] << 24 | code[i + 1] << 16) >> 16;
          arg2 = (code[i + 2] << 24 | code[i + 3] << 16) >> 16;
          i += 4;
        } else {
          arg1 = code[i++];
          arg2 = code[i++];
        }
        if (flags & 0x02) {
          x = arg1;
          y = arg2;
        } else {
          x = 0;
          y = 0;
        }
        var scaleX = 1,
            scaleY = 1,
            scale01 = 0,
            scale10 = 0;
        if (flags & 0x08) {
          scaleX = scaleY = (code[i] << 24 | code[i + 1] << 16) / 1073741824;
          i += 2;
        } else if (flags & 0x40) {
          scaleX = (code[i] << 24 | code[i + 1] << 16) / 1073741824;
          scaleY = (code[i + 2] << 24 | code[i + 3] << 16) / 1073741824;
          i += 4;
        } else if (flags & 0x80) {
          scaleX = (code[i] << 24 | code[i + 1] << 16) / 1073741824;
          scale01 = (code[i + 2] << 24 | code[i + 3] << 16) / 1073741824;
          scale10 = (code[i + 4] << 24 | code[i + 5] << 16) / 1073741824;
          scaleY = (code[i + 6] << 24 | code[i + 7] << 16) / 1073741824;
          i += 8;
        }
        var subglyph = font.glyphs[glyphIndex];
        if (subglyph) {
          cmds.push({ cmd: 'save' });
          cmds.push({
            cmd: 'transform',
            args: [scaleX, scale01, scale10, scaleY, x, y]
          });
          compileGlyf(subglyph, cmds, font);
          cmds.push({ cmd: 'restore' });
        }
      } while (flags & 0x20);
    } else {
      var endPtsOfContours = [];
      var j, jj;
      for (j = 0; j < numberOfContours; j++) {
        endPtsOfContours.push(code[i] << 8 | code[i + 1]);
        i += 2;
      }
      var instructionLength = code[i] << 8 | code[i + 1];
      i += 2 + instructionLength;
      var numberOfPoints = endPtsOfContours[endPtsOfContours.length - 1] + 1;
      var points = [];
      while (points.length < numberOfPoints) {
        flags = code[i++];
        var repeat = 1;
        if (flags & 0x08) {
          repeat += code[i++];
        }
        while (repeat-- > 0) {
          points.push({ flags });
        }
      }
      for (j = 0; j < numberOfPoints; j++) {
        switch (points[j].flags & 0x12) {
          case 0x00:
            x += (code[i] << 24 | code[i + 1] << 16) >> 16;
            i += 2;
            break;
          case 0x02:
            x -= code[i++];
            break;
          case 0x12:
            x += code[i++];
            break;
        }
        points[j].x = x;
      }
      for (j = 0; j < numberOfPoints; j++) {
        switch (points[j].flags & 0x24) {
          case 0x00:
            y += (code[i] << 24 | code[i + 1] << 16) >> 16;
            i += 2;
            break;
          case 0x04:
            y -= code[i++];
            break;
          case 0x24:
            y += code[i++];
            break;
        }
        points[j].y = y;
      }
      var startPoint = 0;
      for (i = 0; i < numberOfContours; i++) {
        var endPoint = endPtsOfContours[i];
        var contour = points.slice(startPoint, endPoint + 1);
        if (contour[0].flags & 1) {
          contour.push(contour[0]);
        } else if (contour[contour.length - 1].flags & 1) {
          contour.unshift(contour[contour.length - 1]);
        } else {
          var p = {
            flags: 1,
            x: (contour[0].x + contour[contour.length - 1].x) / 2,
            y: (contour[0].y + contour[contour.length - 1].y) / 2
          };
          contour.unshift(p);
          contour.push(p);
        }
        moveTo(contour[0].x, contour[0].y);
        for (j = 1, jj = contour.length; j < jj; j++) {
          if (contour[j].flags & 1) {
            lineTo(contour[j].x, contour[j].y);
          } else if (contour[j + 1].flags & 1) {
            quadraticCurveTo(contour[j].x, contour[j].y, contour[j + 1].x, contour[j + 1].y);
            j++;
          } else {
            quadraticCurveTo(contour[j].x, contour[j].y, (contour[j].x + contour[j + 1].x) / 2, (contour[j].y + contour[j + 1].y) / 2);
          }
        }
        startPoint = endPoint + 1;
      }
    }
  }
  function compileCharString(code, cmds, font) {
    var stack = [];
    var x = 0,
        y = 0;
    var stems = 0;
    function moveTo(x, y) {
      cmds.push({
        cmd: 'moveTo',
        args: [x, y]
      });
    }
    function lineTo(x, y) {
      cmds.push({
        cmd: 'lineTo',
        args: [x, y]
      });
    }
    function bezierCurveTo(x1, y1, x2, y2, x, y) {
      cmds.push({
        cmd: 'bezierCurveTo',
        args: [x1, y1, x2, y2, x, y]
      });
    }
    function parse(code) {
      var i = 0;
      while (i < code.length) {
        var stackClean = false;
        var v = code[i++];
        var xa, xb, ya, yb, y1, y2, y3, n, subrCode;
        switch (v) {
          case 1:
            stems += stack.length >> 1;
            stackClean = true;
            break;
          case 3:
            stems += stack.length >> 1;
            stackClean = true;
            break;
          case 4:
            y += stack.pop();
            moveTo(x, y);
            stackClean = true;
            break;
          case 5:
            while (stack.length > 0) {
              x += stack.shift();
              y += stack.shift();
              lineTo(x, y);
            }
            break;
          case 6:
            while (stack.length > 0) {
              x += stack.shift();
              lineTo(x, y);
              if (stack.length === 0) {
                break;
              }
              y += stack.shift();
              lineTo(x, y);
            }
            break;
          case 7:
            while (stack.length > 0) {
              y += stack.shift();
              lineTo(x, y);
              if (stack.length === 0) {
                break;
              }
              x += stack.shift();
              lineTo(x, y);
            }
            break;
          case 8:
            while (stack.length > 0) {
              xa = x + stack.shift();
              ya = y + stack.shift();
              xb = xa + stack.shift();
              yb = ya + stack.shift();
              x = xb + stack.shift();
              y = yb + stack.shift();
              bezierCurveTo(xa, ya, xb, yb, x, y);
            }
            break;
          case 10:
            n = stack.pop() + font.subrsBias;
            subrCode = font.subrs[n];
            if (subrCode) {
              parse(subrCode);
            }
            break;
          case 11:
            return;
          case 12:
            v = code[i++];
            switch (v) {
              case 34:
                xa = x + stack.shift();
                xb = xa + stack.shift();
                y1 = y + stack.shift();
                x = xb + stack.shift();
                bezierCurveTo(xa, y, xb, y1, x, y1);
                xa = x + stack.shift();
                xb = xa + stack.shift();
                x = xb + stack.shift();
                bezierCurveTo(xa, y1, xb, y, x, y);
                break;
              case 35:
                xa = x + stack.shift();
                ya = y + stack.shift();
                xb = xa + stack.shift();
                yb = ya + stack.shift();
                x = xb + stack.shift();
                y = yb + stack.shift();
                bezierCurveTo(xa, ya, xb, yb, x, y);
                xa = x + stack.shift();
                ya = y + stack.shift();
                xb = xa + stack.shift();
                yb = ya + stack.shift();
                x = xb + stack.shift();
                y = yb + stack.shift();
                bezierCurveTo(xa, ya, xb, yb, x, y);
                stack.pop();
                break;
              case 36:
                xa = x + stack.shift();
                y1 = y + stack.shift();
                xb = xa + stack.shift();
                y2 = y1 + stack.shift();
                x = xb + stack.shift();
                bezierCurveTo(xa, y1, xb, y2, x, y2);
                xa = x + stack.shift();
                xb = xa + stack.shift();
                y3 = y2 + stack.shift();
                x = xb + stack.shift();
                bezierCurveTo(xa, y2, xb, y3, x, y);
                break;
              case 37:
                var x0 = x,
                    y0 = y;
                xa = x + stack.shift();
                ya = y + stack.shift();
                xb = xa + stack.shift();
                yb = ya + stack.shift();
                x = xb + stack.shift();
                y = yb + stack.shift();
                bezierCurveTo(xa, ya, xb, yb, x, y);
                xa = x + stack.shift();
                ya = y + stack.shift();
                xb = xa + stack.shift();
                yb = ya + stack.shift();
                x = xb;
                y = yb;
                if (Math.abs(x - x0) > Math.abs(y - y0)) {
                  x += stack.shift();
                } else {
                  y += stack.shift();
                }
                bezierCurveTo(xa, ya, xb, yb, x, y);
                break;
              default:
                throw new _util.FormatError(`unknown operator: 12 ${v}`);
            }
            break;
          case 14:
            if (stack.length >= 4) {
              var achar = stack.pop();
              var bchar = stack.pop();
              y = stack.pop();
              x = stack.pop();
              cmds.push({ cmd: 'save' });
              cmds.push({
                cmd: 'translate',
                args: [x, y]
              });
              var cmap = lookupCmap(font.cmap, String.fromCharCode(font.glyphNameMap[_encodings.StandardEncoding[achar]]));
              compileCharString(font.glyphs[cmap.glyphId], cmds, font);
              cmds.push({ cmd: 'restore' });
              cmap = lookupCmap(font.cmap, String.fromCharCode(font.glyphNameMap[_encodings.StandardEncoding[bchar]]));
              compileCharString(font.glyphs[cmap.glyphId], cmds, font);
            }
            return;
          case 18:
            stems += stack.length >> 1;
            stackClean = true;
            break;
          case 19:
            stems += stack.length >> 1;
            i += stems + 7 >> 3;
            stackClean = true;
            break;
          case 20:
            stems += stack.length >> 1;
            i += stems + 7 >> 3;
            stackClean = true;
            break;
          case 21:
            y += stack.pop();
            x += stack.pop();
            moveTo(x, y);
            stackClean = true;
            break;
          case 22:
            x += stack.pop();
            moveTo(x, y);
            stackClean = true;
            break;
          case 23:
            stems += stack.length >> 1;
            stackClean = true;
            break;
          case 24:
            while (stack.length > 2) {
              xa = x + stack.shift();
              ya = y + stack.shift();
              xb = xa + stack.shift();
              yb = ya + stack.shift();
              x = xb + stack.shift();
              y = yb + stack.shift();
              bezierCurveTo(xa, ya, xb, yb, x, y);
            }
            x += stack.shift();
            y += stack.shift();
            lineTo(x, y);
            break;
          case 25:
            while (stack.length > 6) {
              x += stack.shift();
              y += stack.shift();
              lineTo(x, y);
            }
            xa = x + stack.shift();
            ya = y + stack.shift();
            xb = xa + stack.shift();
            yb = ya + stack.shift();
            x = xb + stack.shift();
            y = yb + stack.shift();
            bezierCurveTo(xa, ya, xb, yb, x, y);
            break;
          case 26:
            if (stack.length % 2) {
              x += stack.shift();
            }
            while (stack.length > 0) {
              xa = x;
              ya = y + stack.shift();
              xb = xa + stack.shift();
              yb = ya + stack.shift();
              x = xb;
              y = yb + stack.shift();
              bezierCurveTo(xa, ya, xb, yb, x, y);
            }
            break;
          case 27:
            if (stack.length % 2) {
              y += stack.shift();
            }
            while (stack.length > 0) {
              xa = x + stack.shift();
              ya = y;
              xb = xa + stack.shift();
              yb = ya + stack.shift();
              x = xb + stack.shift();
              y = yb;
              bezierCurveTo(xa, ya, xb, yb, x, y);
            }
            break;
          case 28:
            stack.push((code[i] << 24 | code[i + 1] << 16) >> 16);
            i += 2;
            break;
          case 29:
            n = stack.pop() + font.gsubrsBias;
            subrCode = font.gsubrs[n];
            if (subrCode) {
              parse(subrCode);
            }
            break;
          case 30:
            while (stack.length > 0) {
              xa = x;
              ya = y + stack.shift();
              xb = xa + stack.shift();
              yb = ya + stack.shift();
              x = xb + stack.shift();
              y = yb + (stack.length === 1 ? stack.shift() : 0);
              bezierCurveTo(xa, ya, xb, yb, x, y);
              if (stack.length === 0) {
                break;
              }
              xa = x + stack.shift();
              ya = y;
              xb = xa + stack.shift();
              yb = ya + stack.shift();
              y = yb + stack.shift();
              x = xb + (stack.length === 1 ? stack.shift() : 0);
              bezierCurveTo(xa, ya, xb, yb, x, y);
            }
            break;
          case 31:
            while (stack.length > 0) {
              xa = x + stack.shift();
              ya = y;
              xb = xa + stack.shift();
              yb = ya + stack.shift();
              y = yb + stack.shift();
              x = xb + (stack.length === 1 ? stack.shift() : 0);
              bezierCurveTo(xa, ya, xb, yb, x, y);
              if (stack.length === 0) {
                break;
              }
              xa = x;
              ya = y + stack.shift();
              xb = xa + stack.shift();
              yb = ya + stack.shift();
              x = xb + stack.shift();
              y = yb + (stack.length === 1 ? stack.shift() : 0);
              bezierCurveTo(xa, ya, xb, yb, x, y);
            }
            break;
          default:
            if (v < 32) {
              throw new _util.FormatError(`unknown operator: ${v}`);
            }
            if (v < 247) {
              stack.push(v - 139);
            } else if (v < 251) {
              stack.push((v - 247) * 256 + code[i++] + 108);
            } else if (v < 255) {
              stack.push(-(v - 251) * 256 - code[i++] - 108);
            } else {
              stack.push((code[i] << 24 | code[i + 1] << 16 | code[i + 2] << 8 | code[i + 3]) / 65536);
              i += 4;
            }
            break;
        }
        if (stackClean) {
          stack.length = 0;
        }
      }
    }
    parse(code);
  }
  var noop = '';
  function CompiledFont(fontMatrix) {
    this.compiledGlyphs = Object.create(null);
    this.compiledCharCodeToGlyphId = Object.create(null);
    this.fontMatrix = fontMatrix;
  }
  CompiledFont.prototype = {
    getPathJs(unicode) {
      var cmap = lookupCmap(this.cmap, unicode);
      var fn = this.compiledGlyphs[cmap.glyphId];
      if (!fn) {
        fn = this.compileGlyph(this.glyphs[cmap.glyphId]);
        this.compiledGlyphs[cmap.glyphId] = fn;
      }
      if (this.compiledCharCodeToGlyphId[cmap.charCode] === undefined) {
        this.compiledCharCodeToGlyphId[cmap.charCode] = cmap.glyphId;
      }
      return fn;
    },
    compileGlyph(code) {
      if (!code || code.length === 0 || code[0] === 14) {
        return noop;
      }
      var cmds = [];
      cmds.push({ cmd: 'save' });
      cmds.push({
        cmd: 'transform',
        args: this.fontMatrix.slice()
      });
      cmds.push({
        cmd: 'scale',
        args: ['size', '-size']
      });
      this.compileGlyphImpl(code, cmds);
      cmds.push({ cmd: 'restore' });
      return cmds;
    },
    compileGlyphImpl() {
      (0, _util.unreachable)('Children classes should implement this.');
    },
    hasBuiltPath(unicode) {
      var cmap = lookupCmap(this.cmap, unicode);
      return this.compiledGlyphs[cmap.glyphId] !== undefined && this.compiledCharCodeToGlyphId[cmap.charCode] !== undefined;
    }
  };
  function TrueTypeCompiled(glyphs, cmap, fontMatrix) {
    fontMatrix = fontMatrix || [0.000488, 0, 0, 0.000488, 0, 0];
    CompiledFont.call(this, fontMatrix);
    this.glyphs = glyphs;
    this.cmap = cmap;
  }
  _util.Util.inherit(TrueTypeCompiled, CompiledFont, {
    compileGlyphImpl(code, cmds) {
      compileGlyf(code, cmds, this);
    }
  });
  function Type2Compiled(cffInfo, cmap, fontMatrix, glyphNameMap) {
    fontMatrix = fontMatrix || [0.001, 0, 0, 0.001, 0, 0];
    CompiledFont.call(this, fontMatrix);
    this.glyphs = cffInfo.glyphs;
    this.gsubrs = cffInfo.gsubrs || [];
    this.subrs = cffInfo.subrs || [];
    this.cmap = cmap;
    this.glyphNameMap = glyphNameMap || (0, _glyphlist.getGlyphsUnicode)();
    this.gsubrsBias = this.gsubrs.length < 1240 ? 107 : this.gsubrs.length < 33900 ? 1131 : 32768;
    this.subrsBias = this.subrs.length < 1240 ? 107 : this.subrs.length < 33900 ? 1131 : 32768;
  }
  _util.Util.inherit(Type2Compiled, CompiledFont, {
    compileGlyphImpl(code, cmds) {
      compileCharString(code, cmds, this);
    }
  });
  return {
    create: function FontRendererFactory_create(font, seacAnalysisEnabled) {
      var data = new Uint8Array(font.data);
      var cmap, glyf, loca, cff, indexToLocFormat, unitsPerEm;
      var numTables = getUshort(data, 4);
      for (var i = 0, p = 12; i < numTables; i++, p += 16) {
        var tag = (0, _util.bytesToString)(data.subarray(p, p + 4));
        var offset = getLong(data, p + 8);
        var length = getLong(data, p + 12);
        switch (tag) {
          case 'cmap':
            cmap = parseCmap(data, offset, offset + length);
            break;
          case 'glyf':
            glyf = data.subarray(offset, offset + length);
            break;
          case 'loca':
            loca = data.subarray(offset, offset + length);
            break;
          case 'head':
            unitsPerEm = getUshort(data, offset + 18);
            indexToLocFormat = getUshort(data, offset + 50);
            break;
          case 'CFF ':
            cff = parseCff(data, offset, offset + length, seacAnalysisEnabled);
            break;
        }
      }
      if (glyf) {
        var fontMatrix = !unitsPerEm ? font.fontMatrix : [1 / unitsPerEm, 0, 0, 1 / unitsPerEm, 0, 0];
        return new TrueTypeCompiled(parseGlyfTable(glyf, loca, indexToLocFormat), cmap, fontMatrix);
      }
      return new Type2Compiled(cff, cmap, font.fontMatrix, font.glyphNameMap);
    }
  };
}();
exports.FontRendererFactory = FontRendererFactory;

/***/ }),
/* 37 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.Type1Parser = undefined;

var _util = __w_pdfjs_require__(0);

var _encodings = __w_pdfjs_require__(4);

var _stream = __w_pdfjs_require__(2);

var HINTING_ENABLED = false;
var Type1CharString = function Type1CharStringClosure() {
  var COMMAND_MAP = {
    'hstem': [1],
    'vstem': [3],
    'vmoveto': [4],
    'rlineto': [5],
    'hlineto': [6],
    'vlineto': [7],
    'rrcurveto': [8],
    'callsubr': [10],
    'flex': [12, 35],
    'drop': [12, 18],
    'endchar': [14],
    'rmoveto': [21],
    'hmoveto': [22],
    'vhcurveto': [30],
    'hvcurveto': [31]
  };
  function Type1CharString() {
    this.width = 0;
    this.lsb = 0;
    this.flexing = false;
    this.output = [];
    this.stack = [];
  }
  Type1CharString.prototype = {
    convert: function Type1CharString_convert(encoded, subrs, seacAnalysisEnabled) {
      var count = encoded.length;
      var error = false;
      var wx, sbx, subrNumber;
      for (var i = 0; i < count; i++) {
        var value = encoded[i];
        if (value < 32) {
          if (value === 12) {
            value = (value << 8) + encoded[++i];
          }
          switch (value) {
            case 1:
              if (!HINTING_ENABLED) {
                this.stack = [];
                break;
              }
              error = this.executeCommand(2, COMMAND_MAP.hstem);
              break;
            case 3:
              if (!HINTING_ENABLED) {
                this.stack = [];
                break;
              }
              error = this.executeCommand(2, COMMAND_MAP.vstem);
              break;
            case 4:
              if (this.flexing) {
                if (this.stack.length < 1) {
                  error = true;
                  break;
                }
                var dy = this.stack.pop();
                this.stack.push(0, dy);
                break;
              }
              error = this.executeCommand(1, COMMAND_MAP.vmoveto);
              break;
            case 5:
              error = this.executeCommand(2, COMMAND_MAP.rlineto);
              break;
            case 6:
              error = this.executeCommand(1, COMMAND_MAP.hlineto);
              break;
            case 7:
              error = this.executeCommand(1, COMMAND_MAP.vlineto);
              break;
            case 8:
              error = this.executeCommand(6, COMMAND_MAP.rrcurveto);
              break;
            case 9:
              this.stack = [];
              break;
            case 10:
              if (this.stack.length < 1) {
                error = true;
                break;
              }
              subrNumber = this.stack.pop();
              if (!subrs[subrNumber]) {
                error = true;
                break;
              }
              error = this.convert(subrs[subrNumber], subrs, seacAnalysisEnabled);
              break;
            case 11:
              return error;
            case 13:
              if (this.stack.length < 2) {
                error = true;
                break;
              }
              wx = this.stack.pop();
              sbx = this.stack.pop();
              this.lsb = sbx;
              this.width = wx;
              this.stack.push(wx, sbx);
              error = this.executeCommand(2, COMMAND_MAP.hmoveto);
              break;
            case 14:
              this.output.push(COMMAND_MAP.endchar[0]);
              break;
            case 21:
              if (this.flexing) {
                break;
              }
              error = this.executeCommand(2, COMMAND_MAP.rmoveto);
              break;
            case 22:
              if (this.flexing) {
                this.stack.push(0);
                break;
              }
              error = this.executeCommand(1, COMMAND_MAP.hmoveto);
              break;
            case 30:
              error = this.executeCommand(4, COMMAND_MAP.vhcurveto);
              break;
            case 31:
              error = this.executeCommand(4, COMMAND_MAP.hvcurveto);
              break;
            case (12 << 8) + 0:
              this.stack = [];
              break;
            case (12 << 8) + 1:
              if (!HINTING_ENABLED) {
                this.stack = [];
                break;
              }
              error = this.executeCommand(2, COMMAND_MAP.vstem);
              break;
            case (12 << 8) + 2:
              if (!HINTING_ENABLED) {
                this.stack = [];
                break;
              }
              error = this.executeCommand(2, COMMAND_MAP.hstem);
              break;
            case (12 << 8) + 6:
              if (seacAnalysisEnabled) {
                this.seac = this.stack.splice(-4, 4);
                error = this.executeCommand(0, COMMAND_MAP.endchar);
              } else {
                error = this.executeCommand(4, COMMAND_MAP.endchar);
              }
              break;
            case (12 << 8) + 7:
              if (this.stack.length < 4) {
                error = true;
                break;
              }
              this.stack.pop();
              wx = this.stack.pop();
              var sby = this.stack.pop();
              sbx = this.stack.pop();
              this.lsb = sbx;
              this.width = wx;
              this.stack.push(wx, sbx, sby);
              error = this.executeCommand(3, COMMAND_MAP.rmoveto);
              break;
            case (12 << 8) + 12:
              if (this.stack.length < 2) {
                error = true;
                break;
              }
              var num2 = this.stack.pop();
              var num1 = this.stack.pop();
              this.stack.push(num1 / num2);
              break;
            case (12 << 8) + 16:
              if (this.stack.length < 2) {
                error = true;
                break;
              }
              subrNumber = this.stack.pop();
              var numArgs = this.stack.pop();
              if (subrNumber === 0 && numArgs === 3) {
                var flexArgs = this.stack.splice(this.stack.length - 17, 17);
                this.stack.push(flexArgs[2] + flexArgs[0], flexArgs[3] + flexArgs[1], flexArgs[4], flexArgs[5], flexArgs[6], flexArgs[7], flexArgs[8], flexArgs[9], flexArgs[10], flexArgs[11], flexArgs[12], flexArgs[13], flexArgs[14]);
                error = this.executeCommand(13, COMMAND_MAP.flex, true);
                this.flexing = false;
                this.stack.push(flexArgs[15], flexArgs[16]);
              } else if (subrNumber === 1 && numArgs === 0) {
                this.flexing = true;
              }
              break;
            case (12 << 8) + 17:
              break;
            case (12 << 8) + 33:
              this.stack = [];
              break;
            default:
              (0, _util.warn)('Unknown type 1 charstring command of "' + value + '"');
              break;
          }
          if (error) {
            break;
          }
          continue;
        } else if (value <= 246) {
          value = value - 139;
        } else if (value <= 250) {
          value = (value - 247) * 256 + encoded[++i] + 108;
        } else if (value <= 254) {
          value = -((value - 251) * 256) - encoded[++i] - 108;
        } else {
          value = (encoded[++i] & 0xff) << 24 | (encoded[++i] & 0xff) << 16 | (encoded[++i] & 0xff) << 8 | (encoded[++i] & 0xff) << 0;
        }
        this.stack.push(value);
      }
      return error;
    },
    executeCommand(howManyArgs, command, keepStack) {
      var stackLength = this.stack.length;
      if (howManyArgs > stackLength) {
        return true;
      }
      var start = stackLength - howManyArgs;
      for (var i = start; i < stackLength; i++) {
        var value = this.stack[i];
        if (Number.isInteger(value)) {
          this.output.push(28, value >> 8 & 0xff, value & 0xff);
        } else {
          value = 65536 * value | 0;
          this.output.push(255, value >> 24 & 0xFF, value >> 16 & 0xFF, value >> 8 & 0xFF, value & 0xFF);
        }
      }
      this.output.push.apply(this.output, command);
      if (keepStack) {
        this.stack.splice(start, howManyArgs);
      } else {
        this.stack.length = 0;
      }
      return false;
    }
  };
  return Type1CharString;
}();
var Type1Parser = function Type1ParserClosure() {
  var EEXEC_ENCRYPT_KEY = 55665;
  var CHAR_STRS_ENCRYPT_KEY = 4330;
  function isHexDigit(code) {
    return code >= 48 && code <= 57 || code >= 65 && code <= 70 || code >= 97 && code <= 102;
  }
  function decrypt(data, key, discardNumber) {
    if (discardNumber >= data.length) {
      return new Uint8Array(0);
    }
    var r = key | 0,
        c1 = 52845,
        c2 = 22719,
        i,
        j;
    for (i = 0; i < discardNumber; i++) {
      r = (data[i] + r) * c1 + c2 & (1 << 16) - 1;
    }
    var count = data.length - discardNumber;
    var decrypted = new Uint8Array(count);
    for (i = discardNumber, j = 0; j < count; i++, j++) {
      var value = data[i];
      decrypted[j] = value ^ r >> 8;
      r = (value + r) * c1 + c2 & (1 << 16) - 1;
    }
    return decrypted;
  }
  function decryptAscii(data, key, discardNumber) {
    var r = key | 0,
        c1 = 52845,
        c2 = 22719;
    var count = data.length,
        maybeLength = count >>> 1;
    var decrypted = new Uint8Array(maybeLength);
    var i, j;
    for (i = 0, j = 0; i < count; i++) {
      var digit1 = data[i];
      if (!isHexDigit(digit1)) {
        continue;
      }
      i++;
      var digit2;
      while (i < count && !isHexDigit(digit2 = data[i])) {
        i++;
      }
      if (i < count) {
        var value = parseInt(String.fromCharCode(digit1, digit2), 16);
        decrypted[j++] = value ^ r >> 8;
        r = (value + r) * c1 + c2 & (1 << 16) - 1;
      }
    }
    return Array.prototype.slice.call(decrypted, discardNumber, j);
  }
  function isSpecial(c) {
    return c === 0x2F || c === 0x5B || c === 0x5D || c === 0x7B || c === 0x7D || c === 0x28 || c === 0x29;
  }
  function Type1Parser(stream, encrypted, seacAnalysisEnabled) {
    if (encrypted) {
      var data = stream.getBytes();
      var isBinary = !(isHexDigit(data[0]) && isHexDigit(data[1]) && isHexDigit(data[2]) && isHexDigit(data[3]));
      stream = new _stream.Stream(isBinary ? decrypt(data, EEXEC_ENCRYPT_KEY, 4) : decryptAscii(data, EEXEC_ENCRYPT_KEY, 4));
    }
    this.seacAnalysisEnabled = !!seacAnalysisEnabled;
    this.stream = stream;
    this.nextChar();
  }
  Type1Parser.prototype = {
    readNumberArray: function Type1Parser_readNumberArray() {
      this.getToken();
      var array = [];
      while (true) {
        var token = this.getToken();
        if (token === null || token === ']' || token === '}') {
          break;
        }
        array.push(parseFloat(token || 0));
      }
      return array;
    },
    readNumber: function Type1Parser_readNumber() {
      var token = this.getToken();
      return parseFloat(token || 0);
    },
    readInt: function Type1Parser_readInt() {
      var token = this.getToken();
      return parseInt(token || 0, 10) | 0;
    },
    readBoolean: function Type1Parser_readBoolean() {
      var token = this.getToken();
      return token === 'true' ? 1 : 0;
    },
    nextChar: function Type1_nextChar() {
      return this.currentChar = this.stream.getByte();
    },
    getToken: function Type1Parser_getToken() {
      var comment = false;
      var ch = this.currentChar;
      while (true) {
        if (ch === -1) {
          return null;
        }
        if (comment) {
          if (ch === 0x0A || ch === 0x0D) {
            comment = false;
          }
        } else if (ch === 0x25) {
          comment = true;
        } else if (!(0, _util.isSpace)(ch)) {
          break;
        }
        ch = this.nextChar();
      }
      if (isSpecial(ch)) {
        this.nextChar();
        return String.fromCharCode(ch);
      }
      var token = '';
      do {
        token += String.fromCharCode(ch);
        ch = this.nextChar();
      } while (ch >= 0 && !(0, _util.isSpace)(ch) && !isSpecial(ch));
      return token;
    },
    readCharStrings: function Type1Parser_readCharStrings(bytes, lenIV) {
      if (lenIV === -1) {
        return bytes;
      }
      return decrypt(bytes, CHAR_STRS_ENCRYPT_KEY, lenIV);
    },
    extractFontProgram: function Type1Parser_extractFontProgram() {
      var stream = this.stream;
      var subrs = [],
          charstrings = [];
      var privateData = Object.create(null);
      privateData['lenIV'] = 4;
      var program = {
        subrs: [],
        charstrings: [],
        properties: { 'privateData': privateData }
      };
      var token, length, data, lenIV, encoded;
      while ((token = this.getToken()) !== null) {
        if (token !== '/') {
          continue;
        }
        token = this.getToken();
        switch (token) {
          case 'CharStrings':
            this.getToken();
            this.getToken();
            this.getToken();
            this.getToken();
            while (true) {
              token = this.getToken();
              if (token === null || token === 'end') {
                break;
              }
              if (token !== '/') {
                continue;
              }
              var glyph = this.getToken();
              length = this.readInt();
              this.getToken();
              data = stream.makeSubStream(stream.pos, length);
              lenIV = program.properties.privateData['lenIV'];
              encoded = this.readCharStrings(data.getBytes(), lenIV);
              stream.skip(length);
              this.nextChar();
              token = this.getToken();
              if (token === 'noaccess') {
                this.getToken();
              }
              charstrings.push({
                glyph,
                encoded
              });
            }
            break;
          case 'Subrs':
            this.readInt();
            this.getToken();
            while (this.getToken() === 'dup') {
              var index = this.readInt();
              length = this.readInt();
              this.getToken();
              data = stream.makeSubStream(stream.pos, length);
              lenIV = program.properties.privateData['lenIV'];
              encoded = this.readCharStrings(data.getBytes(), lenIV);
              stream.skip(length);
              this.nextChar();
              token = this.getToken();
              if (token === 'noaccess') {
                this.getToken();
              }
              subrs[index] = encoded;
            }
            break;
          case 'BlueValues':
          case 'OtherBlues':
          case 'FamilyBlues':
          case 'FamilyOtherBlues':
            var blueArray = this.readNumberArray();
            if (blueArray.length > 0 && blueArray.length % 2 === 0 && HINTING_ENABLED) {
              program.properties.privateData[token] = blueArray;
            }
            break;
          case 'StemSnapH':
          case 'StemSnapV':
            program.properties.privateData[token] = this.readNumberArray();
            break;
          case 'StdHW':
          case 'StdVW':
            program.properties.privateData[token] = this.readNumberArray()[0];
            break;
          case 'BlueShift':
          case 'lenIV':
          case 'BlueFuzz':
          case 'BlueScale':
          case 'LanguageGroup':
          case 'ExpansionFactor':
            program.properties.privateData[token] = this.readNumber();
            break;
          case 'ForceBold':
            program.properties.privateData[token] = this.readBoolean();
            break;
        }
      }
      for (var i = 0; i < charstrings.length; i++) {
        glyph = charstrings[i].glyph;
        encoded = charstrings[i].encoded;
        var charString = new Type1CharString();
        var error = charString.convert(encoded, subrs, this.seacAnalysisEnabled);
        var output = charString.output;
        if (error) {
          output = [14];
        }
        program.charstrings.push({
          glyphName: glyph,
          charstring: output,
          width: charString.width,
          lsb: charString.lsb,
          seac: charString.seac
        });
      }
      return program;
    },
    extractFontHeader: function Type1Parser_extractFontHeader(properties) {
      var token;
      while ((token = this.getToken()) !== null) {
        if (token !== '/') {
          continue;
        }
        token = this.getToken();
        switch (token) {
          case 'FontMatrix':
            var matrix = this.readNumberArray();
            properties.fontMatrix = matrix;
            break;
          case 'Encoding':
            var encodingArg = this.getToken();
            var encoding;
            if (!/^\d+$/.test(encodingArg)) {
              encoding = (0, _encodings.getEncoding)(encodingArg);
            } else {
              encoding = [];
              var size = parseInt(encodingArg, 10) | 0;
              this.getToken();
              for (var j = 0; j < size; j++) {
                token = this.getToken();
                while (token !== 'dup' && token !== 'def') {
                  token = this.getToken();
                  if (token === null) {
                    return;
                  }
                }
                if (token === 'def') {
                  break;
                }
                var index = this.readInt();
                this.getToken();
                var glyph = this.getToken();
                encoding[index] = glyph;
                this.getToken();
              }
            }
            properties.builtInEncoding = encoding;
            break;
          case 'FontBBox':
            var fontBBox = this.readNumberArray();
            properties.ascent = Math.max(fontBBox[3], fontBBox[1]);
            properties.descent = Math.min(fontBBox[1], fontBBox[3]);
            properties.ascentScaled = true;
            break;
        }
      }
    }
  };
  return Type1Parser;
}();
exports.Type1Parser = Type1Parser;

/***/ }),
/* 38 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getTilingPatternIR = exports.Pattern = undefined;

var _util = __w_pdfjs_require__(0);

var _colorspace = __w_pdfjs_require__(3);

var _primitives = __w_pdfjs_require__(1);

var ShadingType = {
  FUNCTION_BASED: 1,
  AXIAL: 2,
  RADIAL: 3,
  FREE_FORM_MESH: 4,
  LATTICE_FORM_MESH: 5,
  COONS_PATCH_MESH: 6,
  TENSOR_PATCH_MESH: 7
};
var Pattern = function PatternClosure() {
  function Pattern() {
    (0, _util.unreachable)('should not call Pattern constructor');
  }
  Pattern.prototype = {
    getPattern: function Pattern_getPattern(ctx) {
      (0, _util.unreachable)(`Should not call Pattern.getStyle: ${ctx}`);
    }
  };
  Pattern.parseShading = function (shading, matrix, xref, res, handler, pdfFunctionFactory) {
    var dict = (0, _primitives.isStream)(shading) ? shading.dict : shading;
    var type = dict.get('ShadingType');
    try {
      switch (type) {
        case ShadingType.AXIAL:
        case ShadingType.RADIAL:
          return new Shadings.RadialAxial(dict, matrix, xref, res, pdfFunctionFactory);
        case ShadingType.FREE_FORM_MESH:
        case ShadingType.LATTICE_FORM_MESH:
        case ShadingType.COONS_PATCH_MESH:
        case ShadingType.TENSOR_PATCH_MESH:
          return new Shadings.Mesh(shading, matrix, xref, res, pdfFunctionFactory);
        default:
          throw new _util.FormatError('Unsupported ShadingType: ' + type);
      }
    } catch (ex) {
      if (ex instanceof _util.MissingDataException) {
        throw ex;
      }
      handler.send('UnsupportedFeature', { featureId: _util.UNSUPPORTED_FEATURES.shadingPattern });
      (0, _util.warn)(ex);
      return new Shadings.Dummy();
    }
  };
  return Pattern;
}();
var Shadings = {};
Shadings.SMALL_NUMBER = 1e-6;
Shadings.RadialAxial = function RadialAxialClosure() {
  function RadialAxial(dict, matrix, xref, res, pdfFunctionFactory) {
    this.matrix = matrix;
    this.coordsArr = dict.getArray('Coords');
    this.shadingType = dict.get('ShadingType');
    this.type = 'Pattern';
    var cs = dict.get('ColorSpace', 'CS');
    cs = _colorspace.ColorSpace.parse(cs, xref, res, pdfFunctionFactory);
    this.cs = cs;
    var t0 = 0.0,
        t1 = 1.0;
    if (dict.has('Domain')) {
      var domainArr = dict.getArray('Domain');
      t0 = domainArr[0];
      t1 = domainArr[1];
    }
    var extendStart = false,
        extendEnd = false;
    if (dict.has('Extend')) {
      var extendArr = dict.getArray('Extend');
      extendStart = extendArr[0];
      extendEnd = extendArr[1];
    }
    if (this.shadingType === ShadingType.RADIAL && (!extendStart || !extendEnd)) {
      var x1 = this.coordsArr[0];
      var y1 = this.coordsArr[1];
      var r1 = this.coordsArr[2];
      var x2 = this.coordsArr[3];
      var y2 = this.coordsArr[4];
      var r2 = this.coordsArr[5];
      var distance = Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
      if (r1 <= r2 + distance && r2 <= r1 + distance) {
        (0, _util.warn)('Unsupported radial gradient.');
      }
    }
    this.extendStart = extendStart;
    this.extendEnd = extendEnd;
    var fnObj = dict.get('Function');
    var fn = pdfFunctionFactory.createFromArray(fnObj);
    var diff = t1 - t0;
    var step = diff / 10;
    var colorStops = this.colorStops = [];
    if (t0 >= t1 || step <= 0) {
      (0, _util.info)('Bad shading domain.');
      return;
    }
    var color = new Float32Array(cs.numComps),
        ratio = new Float32Array(1);
    var rgbColor;
    for (var i = t0; i <= t1; i += step) {
      ratio[0] = i;
      fn(ratio, 0, color, 0);
      rgbColor = cs.getRgb(color, 0);
      var cssColor = _util.Util.makeCssRgb(rgbColor[0], rgbColor[1], rgbColor[2]);
      colorStops.push([(i - t0) / diff, cssColor]);
    }
    var background = 'transparent';
    if (dict.has('Background')) {
      rgbColor = cs.getRgb(dict.get('Background'), 0);
      background = _util.Util.makeCssRgb(rgbColor[0], rgbColor[1], rgbColor[2]);
    }
    if (!extendStart) {
      colorStops.unshift([0, background]);
      colorStops[1][0] += Shadings.SMALL_NUMBER;
    }
    if (!extendEnd) {
      colorStops[colorStops.length - 1][0] -= Shadings.SMALL_NUMBER;
      colorStops.push([1, background]);
    }
    this.colorStops = colorStops;
  }
  RadialAxial.prototype = {
    getIR: function RadialAxial_getIR() {
      var coordsArr = this.coordsArr;
      var shadingType = this.shadingType;
      var type, p0, p1, r0, r1;
      if (shadingType === ShadingType.AXIAL) {
        p0 = [coordsArr[0], coordsArr[1]];
        p1 = [coordsArr[2], coordsArr[3]];
        r0 = null;
        r1 = null;
        type = 'axial';
      } else if (shadingType === ShadingType.RADIAL) {
        p0 = [coordsArr[0], coordsArr[1]];
        p1 = [coordsArr[3], coordsArr[4]];
        r0 = coordsArr[2];
        r1 = coordsArr[5];
        type = 'radial';
      } else {
        (0, _util.unreachable)(`getPattern type unknown: ${shadingType}`);
      }
      var matrix = this.matrix;
      if (matrix) {
        p0 = _util.Util.applyTransform(p0, matrix);
        p1 = _util.Util.applyTransform(p1, matrix);
        if (shadingType === ShadingType.RADIAL) {
          var scale = _util.Util.singularValueDecompose2dScale(matrix);
          r0 *= scale[0];
          r1 *= scale[1];
        }
      }
      return ['RadialAxial', type, this.colorStops, p0, p1, r0, r1];
    }
  };
  return RadialAxial;
}();
Shadings.Mesh = function MeshClosure() {
  function MeshStreamReader(stream, context) {
    this.stream = stream;
    this.context = context;
    this.buffer = 0;
    this.bufferLength = 0;
    var numComps = context.numComps;
    this.tmpCompsBuf = new Float32Array(numComps);
    var csNumComps = context.colorSpace.numComps;
    this.tmpCsCompsBuf = context.colorFn ? new Float32Array(csNumComps) : this.tmpCompsBuf;
  }
  MeshStreamReader.prototype = {
    get hasData() {
      if (this.stream.end) {
        return this.stream.pos < this.stream.end;
      }
      if (this.bufferLength > 0) {
        return true;
      }
      var nextByte = this.stream.getByte();
      if (nextByte < 0) {
        return false;
      }
      this.buffer = nextByte;
      this.bufferLength = 8;
      return true;
    },
    readBits: function MeshStreamReader_readBits(n) {
      var buffer = this.buffer;
      var bufferLength = this.bufferLength;
      if (n === 32) {
        if (bufferLength === 0) {
          return (this.stream.getByte() << 24 | this.stream.getByte() << 16 | this.stream.getByte() << 8 | this.stream.getByte()) >>> 0;
        }
        buffer = buffer << 24 | this.stream.getByte() << 16 | this.stream.getByte() << 8 | this.stream.getByte();
        var nextByte = this.stream.getByte();
        this.buffer = nextByte & (1 << bufferLength) - 1;
        return (buffer << 8 - bufferLength | (nextByte & 0xFF) >> bufferLength) >>> 0;
      }
      if (n === 8 && bufferLength === 0) {
        return this.stream.getByte();
      }
      while (bufferLength < n) {
        buffer = buffer << 8 | this.stream.getByte();
        bufferLength += 8;
      }
      bufferLength -= n;
      this.bufferLength = bufferLength;
      this.buffer = buffer & (1 << bufferLength) - 1;
      return buffer >> bufferLength;
    },
    align: function MeshStreamReader_align() {
      this.buffer = 0;
      this.bufferLength = 0;
    },
    readFlag: function MeshStreamReader_readFlag() {
      return this.readBits(this.context.bitsPerFlag);
    },
    readCoordinate: function MeshStreamReader_readCoordinate() {
      var bitsPerCoordinate = this.context.bitsPerCoordinate;
      var xi = this.readBits(bitsPerCoordinate);
      var yi = this.readBits(bitsPerCoordinate);
      var decode = this.context.decode;
      var scale = bitsPerCoordinate < 32 ? 1 / ((1 << bitsPerCoordinate) - 1) : 2.3283064365386963e-10;
      return [xi * scale * (decode[1] - decode[0]) + decode[0], yi * scale * (decode[3] - decode[2]) + decode[2]];
    },
    readComponents: function MeshStreamReader_readComponents() {
      var numComps = this.context.numComps;
      var bitsPerComponent = this.context.bitsPerComponent;
      var scale = bitsPerComponent < 32 ? 1 / ((1 << bitsPerComponent) - 1) : 2.3283064365386963e-10;
      var decode = this.context.decode;
      var components = this.tmpCompsBuf;
      for (var i = 0, j = 4; i < numComps; i++, j += 2) {
        var ci = this.readBits(bitsPerComponent);
        components[i] = ci * scale * (decode[j + 1] - decode[j]) + decode[j];
      }
      var color = this.tmpCsCompsBuf;
      if (this.context.colorFn) {
        this.context.colorFn(components, 0, color, 0);
      }
      return this.context.colorSpace.getRgb(color, 0);
    }
  };
  function decodeType4Shading(mesh, reader) {
    var coords = mesh.coords;
    var colors = mesh.colors;
    var operators = [];
    var ps = [];
    var verticesLeft = 0;
    while (reader.hasData) {
      var f = reader.readFlag();
      var coord = reader.readCoordinate();
      var color = reader.readComponents();
      if (verticesLeft === 0) {
        if (!(0 <= f && f <= 2)) {
          throw new _util.FormatError('Unknown type4 flag');
        }
        switch (f) {
          case 0:
            verticesLeft = 3;
            break;
          case 1:
            ps.push(ps[ps.length - 2], ps[ps.length - 1]);
            verticesLeft = 1;
            break;
          case 2:
            ps.push(ps[ps.length - 3], ps[ps.length - 1]);
            verticesLeft = 1;
            break;
        }
        operators.push(f);
      }
      ps.push(coords.length);
      coords.push(coord);
      colors.push(color);
      verticesLeft--;
      reader.align();
    }
    mesh.figures.push({
      type: 'triangles',
      coords: new Int32Array(ps),
      colors: new Int32Array(ps)
    });
  }
  function decodeType5Shading(mesh, reader, verticesPerRow) {
    var coords = mesh.coords;
    var colors = mesh.colors;
    var ps = [];
    while (reader.hasData) {
      var coord = reader.readCoordinate();
      var color = reader.readComponents();
      ps.push(coords.length);
      coords.push(coord);
      colors.push(color);
    }
    mesh.figures.push({
      type: 'lattice',
      coords: new Int32Array(ps),
      colors: new Int32Array(ps),
      verticesPerRow
    });
  }
  var MIN_SPLIT_PATCH_CHUNKS_AMOUNT = 3;
  var MAX_SPLIT_PATCH_CHUNKS_AMOUNT = 20;
  var TRIANGLE_DENSITY = 20;
  var getB = function getBClosure() {
    function buildB(count) {
      var lut = [];
      for (var i = 0; i <= count; i++) {
        var t = i / count,
            t_ = 1 - t;
        lut.push(new Float32Array([t_ * t_ * t_, 3 * t * t_ * t_, 3 * t * t * t_, t * t * t]));
      }
      return lut;
    }
    var cache = [];
    return function getB(count) {
      if (!cache[count]) {
        cache[count] = buildB(count);
      }
      return cache[count];
    };
  }();
  function buildFigureFromPatch(mesh, index) {
    var figure = mesh.figures[index];
    (0, _util.assert)(figure.type === 'patch', 'Unexpected patch mesh figure');
    var coords = mesh.coords,
        colors = mesh.colors;
    var pi = figure.coords;
    var ci = figure.colors;
    var figureMinX = Math.min(coords[pi[0]][0], coords[pi[3]][0], coords[pi[12]][0], coords[pi[15]][0]);
    var figureMinY = Math.min(coords[pi[0]][1], coords[pi[3]][1], coords[pi[12]][1], coords[pi[15]][1]);
    var figureMaxX = Math.max(coords[pi[0]][0], coords[pi[3]][0], coords[pi[12]][0], coords[pi[15]][0]);
    var figureMaxY = Math.max(coords[pi[0]][1], coords[pi[3]][1], coords[pi[12]][1], coords[pi[15]][1]);
    var splitXBy = Math.ceil((figureMaxX - figureMinX) * TRIANGLE_DENSITY / (mesh.bounds[2] - mesh.bounds[0]));
    splitXBy = Math.max(MIN_SPLIT_PATCH_CHUNKS_AMOUNT, Math.min(MAX_SPLIT_PATCH_CHUNKS_AMOUNT, splitXBy));
    var splitYBy = Math.ceil((figureMaxY - figureMinY) * TRIANGLE_DENSITY / (mesh.bounds[3] - mesh.bounds[1]));
    splitYBy = Math.max(MIN_SPLIT_PATCH_CHUNKS_AMOUNT, Math.min(MAX_SPLIT_PATCH_CHUNKS_AMOUNT, splitYBy));
    var verticesPerRow = splitXBy + 1;
    var figureCoords = new Int32Array((splitYBy + 1) * verticesPerRow);
    var figureColors = new Int32Array((splitYBy + 1) * verticesPerRow);
    var k = 0;
    var cl = new Uint8Array(3),
        cr = new Uint8Array(3);
    var c0 = colors[ci[0]],
        c1 = colors[ci[1]],
        c2 = colors[ci[2]],
        c3 = colors[ci[3]];
    var bRow = getB(splitYBy),
        bCol = getB(splitXBy);
    for (var row = 0; row <= splitYBy; row++) {
      cl[0] = (c0[0] * (splitYBy - row) + c2[0] * row) / splitYBy | 0;
      cl[1] = (c0[1] * (splitYBy - row) + c2[1] * row) / splitYBy | 0;
      cl[2] = (c0[2] * (splitYBy - row) + c2[2] * row) / splitYBy | 0;
      cr[0] = (c1[0] * (splitYBy - row) + c3[0] * row) / splitYBy | 0;
      cr[1] = (c1[1] * (splitYBy - row) + c3[1] * row) / splitYBy | 0;
      cr[2] = (c1[2] * (splitYBy - row) + c3[2] * row) / splitYBy | 0;
      for (var col = 0; col <= splitXBy; col++, k++) {
        if ((row === 0 || row === splitYBy) && (col === 0 || col === splitXBy)) {
          continue;
        }
        var x = 0,
            y = 0;
        var q = 0;
        for (var i = 0; i <= 3; i++) {
          for (var j = 0; j <= 3; j++, q++) {
            var m = bRow[row][i] * bCol[col][j];
            x += coords[pi[q]][0] * m;
            y += coords[pi[q]][1] * m;
          }
        }
        figureCoords[k] = coords.length;
        coords.push([x, y]);
        figureColors[k] = colors.length;
        var newColor = new Uint8Array(3);
        newColor[0] = (cl[0] * (splitXBy - col) + cr[0] * col) / splitXBy | 0;
        newColor[1] = (cl[1] * (splitXBy - col) + cr[1] * col) / splitXBy | 0;
        newColor[2] = (cl[2] * (splitXBy - col) + cr[2] * col) / splitXBy | 0;
        colors.push(newColor);
      }
    }
    figureCoords[0] = pi[0];
    figureColors[0] = ci[0];
    figureCoords[splitXBy] = pi[3];
    figureColors[splitXBy] = ci[1];
    figureCoords[verticesPerRow * splitYBy] = pi[12];
    figureColors[verticesPerRow * splitYBy] = ci[2];
    figureCoords[verticesPerRow * splitYBy + splitXBy] = pi[15];
    figureColors[verticesPerRow * splitYBy + splitXBy] = ci[3];
    mesh.figures[index] = {
      type: 'lattice',
      coords: figureCoords,
      colors: figureColors,
      verticesPerRow
    };
  }
  function decodeType6Shading(mesh, reader) {
    var coords = mesh.coords;
    var colors = mesh.colors;
    var ps = new Int32Array(16);
    var cs = new Int32Array(4);
    while (reader.hasData) {
      var f = reader.readFlag();
      if (!(0 <= f && f <= 3)) {
        throw new _util.FormatError('Unknown type6 flag');
      }
      var i, ii;
      var pi = coords.length;
      for (i = 0, ii = f !== 0 ? 8 : 12; i < ii; i++) {
        coords.push(reader.readCoordinate());
      }
      var ci = colors.length;
      for (i = 0, ii = f !== 0 ? 2 : 4; i < ii; i++) {
        colors.push(reader.readComponents());
      }
      var tmp1, tmp2, tmp3, tmp4;
      switch (f) {
        case 0:
          ps[12] = pi + 3;
          ps[13] = pi + 4;
          ps[14] = pi + 5;
          ps[15] = pi + 6;
          ps[8] = pi + 2;
          ps[11] = pi + 7;
          ps[4] = pi + 1;
          ps[7] = pi + 8;
          ps[0] = pi;
          ps[1] = pi + 11;
          ps[2] = pi + 10;
          ps[3] = pi + 9;
          cs[2] = ci + 1;
          cs[3] = ci + 2;
          cs[0] = ci;
          cs[1] = ci + 3;
          break;
        case 1:
          tmp1 = ps[12];
          tmp2 = ps[13];
          tmp3 = ps[14];
          tmp4 = ps[15];
          ps[12] = tmp4;
          ps[13] = pi + 0;
          ps[14] = pi + 1;
          ps[15] = pi + 2;
          ps[8] = tmp3;
          ps[11] = pi + 3;
          ps[4] = tmp2;
          ps[7] = pi + 4;
          ps[0] = tmp1;
          ps[1] = pi + 7;
          ps[2] = pi + 6;
          ps[3] = pi + 5;
          tmp1 = cs[2];
          tmp2 = cs[3];
          cs[2] = tmp2;
          cs[3] = ci;
          cs[0] = tmp1;
          cs[1] = ci + 1;
          break;
        case 2:
          tmp1 = ps[15];
          tmp2 = ps[11];
          ps[12] = ps[3];
          ps[13] = pi + 0;
          ps[14] = pi + 1;
          ps[15] = pi + 2;
          ps[8] = ps[7];
          ps[11] = pi + 3;
          ps[4] = tmp2;
          ps[7] = pi + 4;
          ps[0] = tmp1;
          ps[1] = pi + 7;
          ps[2] = pi + 6;
          ps[3] = pi + 5;
          tmp1 = cs[3];
          cs[2] = cs[1];
          cs[3] = ci;
          cs[0] = tmp1;
          cs[1] = ci + 1;
          break;
        case 3:
          ps[12] = ps[0];
          ps[13] = pi + 0;
          ps[14] = pi + 1;
          ps[15] = pi + 2;
          ps[8] = ps[1];
          ps[11] = pi + 3;
          ps[4] = ps[2];
          ps[7] = pi + 4;
          ps[0] = ps[3];
          ps[1] = pi + 7;
          ps[2] = pi + 6;
          ps[3] = pi + 5;
          cs[2] = cs[0];
          cs[3] = ci;
          cs[0] = cs[1];
          cs[1] = ci + 1;
          break;
      }
      ps[5] = coords.length;
      coords.push([(-4 * coords[ps[0]][0] - coords[ps[15]][0] + 6 * (coords[ps[4]][0] + coords[ps[1]][0]) - 2 * (coords[ps[12]][0] + coords[ps[3]][0]) + 3 * (coords[ps[13]][0] + coords[ps[7]][0])) / 9, (-4 * coords[ps[0]][1] - coords[ps[15]][1] + 6 * (coords[ps[4]][1] + coords[ps[1]][1]) - 2 * (coords[ps[12]][1] + coords[ps[3]][1]) + 3 * (coords[ps[13]][1] + coords[ps[7]][1])) / 9]);
      ps[6] = coords.length;
      coords.push([(-4 * coords[ps[3]][0] - coords[ps[12]][0] + 6 * (coords[ps[2]][0] + coords[ps[7]][0]) - 2 * (coords[ps[0]][0] + coords[ps[15]][0]) + 3 * (coords[ps[4]][0] + coords[ps[14]][0])) / 9, (-4 * coords[ps[3]][1] - coords[ps[12]][1] + 6 * (coords[ps[2]][1] + coords[ps[7]][1]) - 2 * (coords[ps[0]][1] + coords[ps[15]][1]) + 3 * (coords[ps[4]][1] + coords[ps[14]][1])) / 9]);
      ps[9] = coords.length;
      coords.push([(-4 * coords[ps[12]][0] - coords[ps[3]][0] + 6 * (coords[ps[8]][0] + coords[ps[13]][0]) - 2 * (coords[ps[0]][0] + coords[ps[15]][0]) + 3 * (coords[ps[11]][0] + coords[ps[1]][0])) / 9, (-4 * coords[ps[12]][1] - coords[ps[3]][1] + 6 * (coords[ps[8]][1] + coords[ps[13]][1]) - 2 * (coords[ps[0]][1] + coords[ps[15]][1]) + 3 * (coords[ps[11]][1] + coords[ps[1]][1])) / 9]);
      ps[10] = coords.length;
      coords.push([(-4 * coords[ps[15]][0] - coords[ps[0]][0] + 6 * (coords[ps[11]][0] + coords[ps[14]][0]) - 2 * (coords[ps[12]][0] + coords[ps[3]][0]) + 3 * (coords[ps[2]][0] + coords[ps[8]][0])) / 9, (-4 * coords[ps[15]][1] - coords[ps[0]][1] + 6 * (coords[ps[11]][1] + coords[ps[14]][1]) - 2 * (coords[ps[12]][1] + coords[ps[3]][1]) + 3 * (coords[ps[2]][1] + coords[ps[8]][1])) / 9]);
      mesh.figures.push({
        type: 'patch',
        coords: new Int32Array(ps),
        colors: new Int32Array(cs)
      });
    }
  }
  function decodeType7Shading(mesh, reader) {
    var coords = mesh.coords;
    var colors = mesh.colors;
    var ps = new Int32Array(16);
    var cs = new Int32Array(4);
    while (reader.hasData) {
      var f = reader.readFlag();
      if (!(0 <= f && f <= 3)) {
        throw new _util.FormatError('Unknown type7 flag');
      }
      var i, ii;
      var pi = coords.length;
      for (i = 0, ii = f !== 0 ? 12 : 16; i < ii; i++) {
        coords.push(reader.readCoordinate());
      }
      var ci = colors.length;
      for (i = 0, ii = f !== 0 ? 2 : 4; i < ii; i++) {
        colors.push(reader.readComponents());
      }
      var tmp1, tmp2, tmp3, tmp4;
      switch (f) {
        case 0:
          ps[12] = pi + 3;
          ps[13] = pi + 4;
          ps[14] = pi + 5;
          ps[15] = pi + 6;
          ps[8] = pi + 2;
          ps[9] = pi + 13;
          ps[10] = pi + 14;
          ps[11] = pi + 7;
          ps[4] = pi + 1;
          ps[5] = pi + 12;
          ps[6] = pi + 15;
          ps[7] = pi + 8;
          ps[0] = pi;
          ps[1] = pi + 11;
          ps[2] = pi + 10;
          ps[3] = pi + 9;
          cs[2] = ci + 1;
          cs[3] = ci + 2;
          cs[0] = ci;
          cs[1] = ci + 3;
          break;
        case 1:
          tmp1 = ps[12];
          tmp2 = ps[13];
          tmp3 = ps[14];
          tmp4 = ps[15];
          ps[12] = tmp4;
          ps[13] = pi + 0;
          ps[14] = pi + 1;
          ps[15] = pi + 2;
          ps[8] = tmp3;
          ps[9] = pi + 9;
          ps[10] = pi + 10;
          ps[11] = pi + 3;
          ps[4] = tmp2;
          ps[5] = pi + 8;
          ps[6] = pi + 11;
          ps[7] = pi + 4;
          ps[0] = tmp1;
          ps[1] = pi + 7;
          ps[2] = pi + 6;
          ps[3] = pi + 5;
          tmp1 = cs[2];
          tmp2 = cs[3];
          cs[2] = tmp2;
          cs[3] = ci;
          cs[0] = tmp1;
          cs[1] = ci + 1;
          break;
        case 2:
          tmp1 = ps[15];
          tmp2 = ps[11];
          ps[12] = ps[3];
          ps[13] = pi + 0;
          ps[14] = pi + 1;
          ps[15] = pi + 2;
          ps[8] = ps[7];
          ps[9] = pi + 9;
          ps[10] = pi + 10;
          ps[11] = pi + 3;
          ps[4] = tmp2;
          ps[5] = pi + 8;
          ps[6] = pi + 11;
          ps[7] = pi + 4;
          ps[0] = tmp1;
          ps[1] = pi + 7;
          ps[2] = pi + 6;
          ps[3] = pi + 5;
          tmp1 = cs[3];
          cs[2] = cs[1];
          cs[3] = ci;
          cs[0] = tmp1;
          cs[1] = ci + 1;
          break;
        case 3:
          ps[12] = ps[0];
          ps[13] = pi + 0;
          ps[14] = pi + 1;
          ps[15] = pi + 2;
          ps[8] = ps[1];
          ps[9] = pi + 9;
          ps[10] = pi + 10;
          ps[11] = pi + 3;
          ps[4] = ps[2];
          ps[5] = pi + 8;
          ps[6] = pi + 11;
          ps[7] = pi + 4;
          ps[0] = ps[3];
          ps[1] = pi + 7;
          ps[2] = pi + 6;
          ps[3] = pi + 5;
          cs[2] = cs[0];
          cs[3] = ci;
          cs[0] = cs[1];
          cs[1] = ci + 1;
          break;
      }
      mesh.figures.push({
        type: 'patch',
        coords: new Int32Array(ps),
        colors: new Int32Array(cs)
      });
    }
  }
  function updateBounds(mesh) {
    var minX = mesh.coords[0][0],
        minY = mesh.coords[0][1],
        maxX = minX,
        maxY = minY;
    for (var i = 1, ii = mesh.coords.length; i < ii; i++) {
      var x = mesh.coords[i][0],
          y = mesh.coords[i][1];
      minX = minX > x ? x : minX;
      minY = minY > y ? y : minY;
      maxX = maxX < x ? x : maxX;
      maxY = maxY < y ? y : maxY;
    }
    mesh.bounds = [minX, minY, maxX, maxY];
  }
  function packData(mesh) {
    var i, ii, j, jj;
    var coords = mesh.coords;
    var coordsPacked = new Float32Array(coords.length * 2);
    for (i = 0, j = 0, ii = coords.length; i < ii; i++) {
      var xy = coords[i];
      coordsPacked[j++] = xy[0];
      coordsPacked[j++] = xy[1];
    }
    mesh.coords = coordsPacked;
    var colors = mesh.colors;
    var colorsPacked = new Uint8Array(colors.length * 3);
    for (i = 0, j = 0, ii = colors.length; i < ii; i++) {
      var c = colors[i];
      colorsPacked[j++] = c[0];
      colorsPacked[j++] = c[1];
      colorsPacked[j++] = c[2];
    }
    mesh.colors = colorsPacked;
    var figures = mesh.figures;
    for (i = 0, ii = figures.length; i < ii; i++) {
      var figure = figures[i],
          ps = figure.coords,
          cs = figure.colors;
      for (j = 0, jj = ps.length; j < jj; j++) {
        ps[j] *= 2;
        cs[j] *= 3;
      }
    }
  }
  function Mesh(stream, matrix, xref, res, pdfFunctionFactory) {
    if (!(0, _primitives.isStream)(stream)) {
      throw new _util.FormatError('Mesh data is not a stream');
    }
    var dict = stream.dict;
    this.matrix = matrix;
    this.shadingType = dict.get('ShadingType');
    this.type = 'Pattern';
    this.bbox = dict.getArray('BBox');
    var cs = dict.get('ColorSpace', 'CS');
    cs = _colorspace.ColorSpace.parse(cs, xref, res, pdfFunctionFactory);
    this.cs = cs;
    this.background = dict.has('Background') ? cs.getRgb(dict.get('Background'), 0) : null;
    var fnObj = dict.get('Function');
    var fn = fnObj ? pdfFunctionFactory.createFromArray(fnObj) : null;
    this.coords = [];
    this.colors = [];
    this.figures = [];
    var decodeContext = {
      bitsPerCoordinate: dict.get('BitsPerCoordinate'),
      bitsPerComponent: dict.get('BitsPerComponent'),
      bitsPerFlag: dict.get('BitsPerFlag'),
      decode: dict.getArray('Decode'),
      colorFn: fn,
      colorSpace: cs,
      numComps: fn ? 1 : cs.numComps
    };
    var reader = new MeshStreamReader(stream, decodeContext);
    var patchMesh = false;
    switch (this.shadingType) {
      case ShadingType.FREE_FORM_MESH:
        decodeType4Shading(this, reader);
        break;
      case ShadingType.LATTICE_FORM_MESH:
        var verticesPerRow = dict.get('VerticesPerRow') | 0;
        if (verticesPerRow < 2) {
          throw new _util.FormatError('Invalid VerticesPerRow');
        }
        decodeType5Shading(this, reader, verticesPerRow);
        break;
      case ShadingType.COONS_PATCH_MESH:
        decodeType6Shading(this, reader);
        patchMesh = true;
        break;
      case ShadingType.TENSOR_PATCH_MESH:
        decodeType7Shading(this, reader);
        patchMesh = true;
        break;
      default:
        (0, _util.unreachable)('Unsupported mesh type.');
        break;
    }
    if (patchMesh) {
      updateBounds(this);
      for (var i = 0, ii = this.figures.length; i < ii; i++) {
        buildFigureFromPatch(this, i);
      }
    }
    updateBounds(this);
    packData(this);
  }
  Mesh.prototype = {
    getIR: function Mesh_getIR() {
      return ['Mesh', this.shadingType, this.coords, this.colors, this.figures, this.bounds, this.matrix, this.bbox, this.background];
    }
  };
  return Mesh;
}();
Shadings.Dummy = function DummyClosure() {
  function Dummy() {
    this.type = 'Pattern';
  }
  Dummy.prototype = {
    getIR: function Dummy_getIR() {
      return ['Dummy'];
    }
  };
  return Dummy;
}();
function getTilingPatternIR(operatorList, dict, args) {
  let matrix = dict.getArray('Matrix');
  let bbox = _util.Util.normalizeRect(dict.getArray('BBox'));
  let xstep = dict.get('XStep');
  let ystep = dict.get('YStep');
  let paintType = dict.get('PaintType');
  let tilingType = dict.get('TilingType');
  if (bbox[2] - bbox[0] === 0 || bbox[3] - bbox[1] === 0) {
    throw new _util.FormatError(`Invalid getTilingPatternIR /BBox array: [${bbox}].`);
  }
  return ['TilingPattern', args, operatorList, matrix, bbox, xstep, ystep, paintType, tilingType];
}
exports.Pattern = Pattern;
exports.getTilingPatternIR = getTilingPatternIR;

/***/ }),
/* 39 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.bidi = undefined;

var _util = __w_pdfjs_require__(0);

var baseTypes = ['BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'S', 'B', 'S', 'WS', 'B', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'B', 'B', 'B', 'S', 'WS', 'ON', 'ON', 'ET', 'ET', 'ET', 'ON', 'ON', 'ON', 'ON', 'ON', 'ES', 'CS', 'ES', 'CS', 'CS', 'EN', 'EN', 'EN', 'EN', 'EN', 'EN', 'EN', 'EN', 'EN', 'EN', 'CS', 'ON', 'ON', 'ON', 'ON', 'ON', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'ON', 'ON', 'ON', 'ON', 'ON', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'ON', 'ON', 'ON', 'ON', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'B', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'CS', 'ON', 'ET', 'ET', 'ET', 'ET', 'ON', 'ON', 'ON', 'ON', 'L', 'ON', 'ON', 'BN', 'ON', 'ON', 'ET', 'ET', 'EN', 'EN', 'ON', 'L', 'ON', 'ON', 'ON', 'EN', 'L', 'ON', 'ON', 'ON', 'ON', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L'];
var arabicTypes = ['AN', 'AN', 'AN', 'AN', 'AN', 'AN', 'ON', 'ON', 'AL', 'ET', 'ET', 'AL', 'CS', 'AL', 'ON', 'ON', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'AL', 'AL', '', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'AN', 'AN', 'AN', 'AN', 'AN', 'AN', 'AN', 'AN', 'AN', 'AN', 'ET', 'AN', 'AN', 'AL', 'AL', 'AL', 'NSM', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'AN', 'ON', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'AL', 'AL', 'NSM', 'NSM', 'ON', 'NSM', 'NSM', 'NSM', 'NSM', 'AL', 'AL', 'EN', 'EN', 'EN', 'EN', 'EN', 'EN', 'EN', 'EN', 'EN', 'EN', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL'];
function isOdd(i) {
  return (i & 1) !== 0;
}
function isEven(i) {
  return (i & 1) === 0;
}
function findUnequal(arr, start, value) {
  for (var j = start, jj = arr.length; j < jj; ++j) {
    if (arr[j] !== value) {
      return j;
    }
  }
  return j;
}
function setValues(arr, start, end, value) {
  for (var j = start; j < end; ++j) {
    arr[j] = value;
  }
}
function reverseValues(arr, start, end) {
  for (var i = start, j = end - 1; i < j; ++i, --j) {
    var temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
  }
}
function createBidiText(str, isLTR, vertical) {
  return {
    str,
    dir: vertical ? 'ttb' : isLTR ? 'ltr' : 'rtl'
  };
}
var chars = [];
var types = [];
function bidi(str, startLevel, vertical) {
  var isLTR = true;
  var strLength = str.length;
  if (strLength === 0 || vertical) {
    return createBidiText(str, isLTR, vertical);
  }
  chars.length = strLength;
  types.length = strLength;
  var numBidi = 0;
  var i, ii;
  for (i = 0; i < strLength; ++i) {
    chars[i] = str.charAt(i);
    var charCode = str.charCodeAt(i);
    var charType = 'L';
    if (charCode <= 0x00ff) {
      charType = baseTypes[charCode];
    } else if (0x0590 <= charCode && charCode <= 0x05f4) {
      charType = 'R';
    } else if (0x0600 <= charCode && charCode <= 0x06ff) {
      charType = arabicTypes[charCode & 0xff];
      if (!charType) {
        (0, _util.warn)('Bidi: invalid Unicode character ' + charCode.toString(16));
      }
    } else if (0x0700 <= charCode && charCode <= 0x08AC) {
      charType = 'AL';
    }
    if (charType === 'R' || charType === 'AL' || charType === 'AN') {
      numBidi++;
    }
    types[i] = charType;
  }
  if (numBidi === 0) {
    isLTR = true;
    return createBidiText(str, isLTR);
  }
  if (startLevel === -1) {
    if (numBidi / strLength < 0.3) {
      isLTR = true;
      startLevel = 0;
    } else {
      isLTR = false;
      startLevel = 1;
    }
  }
  var levels = [];
  for (i = 0; i < strLength; ++i) {
    levels[i] = startLevel;
  }
  var e = isOdd(startLevel) ? 'R' : 'L';
  var sor = e;
  var eor = sor;
  var lastType = sor;
  for (i = 0; i < strLength; ++i) {
    if (types[i] === 'NSM') {
      types[i] = lastType;
    } else {
      lastType = types[i];
    }
  }
  lastType = sor;
  var t;
  for (i = 0; i < strLength; ++i) {
    t = types[i];
    if (t === 'EN') {
      types[i] = lastType === 'AL' ? 'AN' : 'EN';
    } else if (t === 'R' || t === 'L' || t === 'AL') {
      lastType = t;
    }
  }
  for (i = 0; i < strLength; ++i) {
    t = types[i];
    if (t === 'AL') {
      types[i] = 'R';
    }
  }
  for (i = 1; i < strLength - 1; ++i) {
    if (types[i] === 'ES' && types[i - 1] === 'EN' && types[i + 1] === 'EN') {
      types[i] = 'EN';
    }
    if (types[i] === 'CS' && (types[i - 1] === 'EN' || types[i - 1] === 'AN') && types[i + 1] === types[i - 1]) {
      types[i] = types[i - 1];
    }
  }
  for (i = 0; i < strLength; ++i) {
    if (types[i] === 'EN') {
      var j;
      for (j = i - 1; j >= 0; --j) {
        if (types[j] !== 'ET') {
          break;
        }
        types[j] = 'EN';
      }
      for (j = i + 1; j < strLength; ++j) {
        if (types[j] !== 'ET') {
          break;
        }
        types[j] = 'EN';
      }
    }
  }
  for (i = 0; i < strLength; ++i) {
    t = types[i];
    if (t === 'WS' || t === 'ES' || t === 'ET' || t === 'CS') {
      types[i] = 'ON';
    }
  }
  lastType = sor;
  for (i = 0; i < strLength; ++i) {
    t = types[i];
    if (t === 'EN') {
      types[i] = lastType === 'L' ? 'L' : 'EN';
    } else if (t === 'R' || t === 'L') {
      lastType = t;
    }
  }
  for (i = 0; i < strLength; ++i) {
    if (types[i] === 'ON') {
      var end = findUnequal(types, i + 1, 'ON');
      var before = sor;
      if (i > 0) {
        before = types[i - 1];
      }
      var after = eor;
      if (end + 1 < strLength) {
        after = types[end + 1];
      }
      if (before !== 'L') {
        before = 'R';
      }
      if (after !== 'L') {
        after = 'R';
      }
      if (before === after) {
        setValues(types, i, end, before);
      }
      i = end - 1;
    }
  }
  for (i = 0; i < strLength; ++i) {
    if (types[i] === 'ON') {
      types[i] = e;
    }
  }
  for (i = 0; i < strLength; ++i) {
    t = types[i];
    if (isEven(levels[i])) {
      if (t === 'R') {
        levels[i] += 1;
      } else if (t === 'AN' || t === 'EN') {
        levels[i] += 2;
      }
    } else {
      if (t === 'L' || t === 'AN' || t === 'EN') {
        levels[i] += 1;
      }
    }
  }
  var highestLevel = -1;
  var lowestOddLevel = 99;
  var level;
  for (i = 0, ii = levels.length; i < ii; ++i) {
    level = levels[i];
    if (highestLevel < level) {
      highestLevel = level;
    }
    if (lowestOddLevel > level && isOdd(level)) {
      lowestOddLevel = level;
    }
  }
  for (level = highestLevel; level >= lowestOddLevel; --level) {
    var start = -1;
    for (i = 0, ii = levels.length; i < ii; ++i) {
      if (levels[i] < level) {
        if (start >= 0) {
          reverseValues(chars, start, i);
          start = -1;
        }
      } else if (start < 0) {
        start = i;
      }
    }
    if (start >= 0) {
      reverseValues(chars, start, levels.length);
    }
  }
  for (i = 0, ii = chars.length; i < ii; ++i) {
    var ch = chars[i];
    if (ch === '<' || ch === '>') {
      chars[i] = '';
    }
  }
  return createBidiText(chars.join(''), isLTR);
}
exports.bidi = bidi;

/***/ }),
/* 40 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getMetrics = undefined;

var _util = __w_pdfjs_require__(0);

var getMetrics = (0, _util.getLookupTableFactory)(function (t) {
  t['Courier'] = 600;
  t['Courier-Bold'] = 600;
  t['Courier-BoldOblique'] = 600;
  t['Courier-Oblique'] = 600;
  t['Helvetica'] = (0, _util.getLookupTableFactory)(function (t) {
    t['space'] = 278;
    t['exclam'] = 278;
    t['quotedbl'] = 355;
    t['numbersign'] = 556;
    t['dollar'] = 556;
    t['percent'] = 889;
    t['ampersand'] = 667;
    t['quoteright'] = 222;
    t['parenleft'] = 333;
    t['parenright'] = 333;
    t['asterisk'] = 389;
    t['plus'] = 584;
    t['comma'] = 278;
    t['hyphen'] = 333;
    t['period'] = 278;
    t['slash'] = 278;
    t['zero'] = 556;
    t['one'] = 556;
    t['two'] = 556;
    t['three'] = 556;
    t['four'] = 556;
    t['five'] = 556;
    t['six'] = 556;
    t['seven'] = 556;
    t['eight'] = 556;
    t['nine'] = 556;
    t['colon'] = 278;
    t['semicolon'] = 278;
    t['less'] = 584;
    t['equal'] = 584;
    t['greater'] = 584;
    t['question'] = 556;
    t['at'] = 1015;
    t['A'] = 667;
    t['B'] = 667;
    t['C'] = 722;
    t['D'] = 722;
    t['E'] = 667;
    t['F'] = 611;
    t['G'] = 778;
    t['H'] = 722;
    t['I'] = 278;
    t['J'] = 500;
    t['K'] = 667;
    t['L'] = 556;
    t['M'] = 833;
    t['N'] = 722;
    t['O'] = 778;
    t['P'] = 667;
    t['Q'] = 778;
    t['R'] = 722;
    t['S'] = 667;
    t['T'] = 611;
    t['U'] = 722;
    t['V'] = 667;
    t['W'] = 944;
    t['X'] = 667;
    t['Y'] = 667;
    t['Z'] = 611;
    t['bracketleft'] = 278;
    t['backslash'] = 278;
    t['bracketright'] = 278;
    t['asciicircum'] = 469;
    t['underscore'] = 556;
    t['quoteleft'] = 222;
    t['a'] = 556;
    t['b'] = 556;
    t['c'] = 500;
    t['d'] = 556;
    t['e'] = 556;
    t['f'] = 278;
    t['g'] = 556;
    t['h'] = 556;
    t['i'] = 222;
    t['j'] = 222;
    t['k'] = 500;
    t['l'] = 222;
    t['m'] = 833;
    t['n'] = 556;
    t['o'] = 556;
    t['p'] = 556;
    t['q'] = 556;
    t['r'] = 333;
    t['s'] = 500;
    t['t'] = 278;
    t['u'] = 556;
    t['v'] = 500;
    t['w'] = 722;
    t['x'] = 500;
    t['y'] = 500;
    t['z'] = 500;
    t['braceleft'] = 334;
    t['bar'] = 260;
    t['braceright'] = 334;
    t['asciitilde'] = 584;
    t['exclamdown'] = 333;
    t['cent'] = 556;
    t['sterling'] = 556;
    t['fraction'] = 167;
    t['yen'] = 556;
    t['florin'] = 556;
    t['section'] = 556;
    t['currency'] = 556;
    t['quotesingle'] = 191;
    t['quotedblleft'] = 333;
    t['guillemotleft'] = 556;
    t['guilsinglleft'] = 333;
    t['guilsinglright'] = 333;
    t['fi'] = 500;
    t['fl'] = 500;
    t['endash'] = 556;
    t['dagger'] = 556;
    t['daggerdbl'] = 556;
    t['periodcentered'] = 278;
    t['paragraph'] = 537;
    t['bullet'] = 350;
    t['quotesinglbase'] = 222;
    t['quotedblbase'] = 333;
    t['quotedblright'] = 333;
    t['guillemotright'] = 556;
    t['ellipsis'] = 1000;
    t['perthousand'] = 1000;
    t['questiondown'] = 611;
    t['grave'] = 333;
    t['acute'] = 333;
    t['circumflex'] = 333;
    t['tilde'] = 333;
    t['macron'] = 333;
    t['breve'] = 333;
    t['dotaccent'] = 333;
    t['dieresis'] = 333;
    t['ring'] = 333;
    t['cedilla'] = 333;
    t['hungarumlaut'] = 333;
    t['ogonek'] = 333;
    t['caron'] = 333;
    t['emdash'] = 1000;
    t['AE'] = 1000;
    t['ordfeminine'] = 370;
    t['Lslash'] = 556;
    t['Oslash'] = 778;
    t['OE'] = 1000;
    t['ordmasculine'] = 365;
    t['ae'] = 889;
    t['dotlessi'] = 278;
    t['lslash'] = 222;
    t['oslash'] = 611;
    t['oe'] = 944;
    t['germandbls'] = 611;
    t['Idieresis'] = 278;
    t['eacute'] = 556;
    t['abreve'] = 556;
    t['uhungarumlaut'] = 556;
    t['ecaron'] = 556;
    t['Ydieresis'] = 667;
    t['divide'] = 584;
    t['Yacute'] = 667;
    t['Acircumflex'] = 667;
    t['aacute'] = 556;
    t['Ucircumflex'] = 722;
    t['yacute'] = 500;
    t['scommaaccent'] = 500;
    t['ecircumflex'] = 556;
    t['Uring'] = 722;
    t['Udieresis'] = 722;
    t['aogonek'] = 556;
    t['Uacute'] = 722;
    t['uogonek'] = 556;
    t['Edieresis'] = 667;
    t['Dcroat'] = 722;
    t['commaaccent'] = 250;
    t['copyright'] = 737;
    t['Emacron'] = 667;
    t['ccaron'] = 500;
    t['aring'] = 556;
    t['Ncommaaccent'] = 722;
    t['lacute'] = 222;
    t['agrave'] = 556;
    t['Tcommaaccent'] = 611;
    t['Cacute'] = 722;
    t['atilde'] = 556;
    t['Edotaccent'] = 667;
    t['scaron'] = 500;
    t['scedilla'] = 500;
    t['iacute'] = 278;
    t['lozenge'] = 471;
    t['Rcaron'] = 722;
    t['Gcommaaccent'] = 778;
    t['ucircumflex'] = 556;
    t['acircumflex'] = 556;
    t['Amacron'] = 667;
    t['rcaron'] = 333;
    t['ccedilla'] = 500;
    t['Zdotaccent'] = 611;
    t['Thorn'] = 667;
    t['Omacron'] = 778;
    t['Racute'] = 722;
    t['Sacute'] = 667;
    t['dcaron'] = 643;
    t['Umacron'] = 722;
    t['uring'] = 556;
    t['threesuperior'] = 333;
    t['Ograve'] = 778;
    t['Agrave'] = 667;
    t['Abreve'] = 667;
    t['multiply'] = 584;
    t['uacute'] = 556;
    t['Tcaron'] = 611;
    t['partialdiff'] = 476;
    t['ydieresis'] = 500;
    t['Nacute'] = 722;
    t['icircumflex'] = 278;
    t['Ecircumflex'] = 667;
    t['adieresis'] = 556;
    t['edieresis'] = 556;
    t['cacute'] = 500;
    t['nacute'] = 556;
    t['umacron'] = 556;
    t['Ncaron'] = 722;
    t['Iacute'] = 278;
    t['plusminus'] = 584;
    t['brokenbar'] = 260;
    t['registered'] = 737;
    t['Gbreve'] = 778;
    t['Idotaccent'] = 278;
    t['summation'] = 600;
    t['Egrave'] = 667;
    t['racute'] = 333;
    t['omacron'] = 556;
    t['Zacute'] = 611;
    t['Zcaron'] = 611;
    t['greaterequal'] = 549;
    t['Eth'] = 722;
    t['Ccedilla'] = 722;
    t['lcommaaccent'] = 222;
    t['tcaron'] = 317;
    t['eogonek'] = 556;
    t['Uogonek'] = 722;
    t['Aacute'] = 667;
    t['Adieresis'] = 667;
    t['egrave'] = 556;
    t['zacute'] = 500;
    t['iogonek'] = 222;
    t['Oacute'] = 778;
    t['oacute'] = 556;
    t['amacron'] = 556;
    t['sacute'] = 500;
    t['idieresis'] = 278;
    t['Ocircumflex'] = 778;
    t['Ugrave'] = 722;
    t['Delta'] = 612;
    t['thorn'] = 556;
    t['twosuperior'] = 333;
    t['Odieresis'] = 778;
    t['mu'] = 556;
    t['igrave'] = 278;
    t['ohungarumlaut'] = 556;
    t['Eogonek'] = 667;
    t['dcroat'] = 556;
    t['threequarters'] = 834;
    t['Scedilla'] = 667;
    t['lcaron'] = 299;
    t['Kcommaaccent'] = 667;
    t['Lacute'] = 556;
    t['trademark'] = 1000;
    t['edotaccent'] = 556;
    t['Igrave'] = 278;
    t['Imacron'] = 278;
    t['Lcaron'] = 556;
    t['onehalf'] = 834;
    t['lessequal'] = 549;
    t['ocircumflex'] = 556;
    t['ntilde'] = 556;
    t['Uhungarumlaut'] = 722;
    t['Eacute'] = 667;
    t['emacron'] = 556;
    t['gbreve'] = 556;
    t['onequarter'] = 834;
    t['Scaron'] = 667;
    t['Scommaaccent'] = 667;
    t['Ohungarumlaut'] = 778;
    t['degree'] = 400;
    t['ograve'] = 556;
    t['Ccaron'] = 722;
    t['ugrave'] = 556;
    t['radical'] = 453;
    t['Dcaron'] = 722;
    t['rcommaaccent'] = 333;
    t['Ntilde'] = 722;
    t['otilde'] = 556;
    t['Rcommaaccent'] = 722;
    t['Lcommaaccent'] = 556;
    t['Atilde'] = 667;
    t['Aogonek'] = 667;
    t['Aring'] = 667;
    t['Otilde'] = 778;
    t['zdotaccent'] = 500;
    t['Ecaron'] = 667;
    t['Iogonek'] = 278;
    t['kcommaaccent'] = 500;
    t['minus'] = 584;
    t['Icircumflex'] = 278;
    t['ncaron'] = 556;
    t['tcommaaccent'] = 278;
    t['logicalnot'] = 584;
    t['odieresis'] = 556;
    t['udieresis'] = 556;
    t['notequal'] = 549;
    t['gcommaaccent'] = 556;
    t['eth'] = 556;
    t['zcaron'] = 500;
    t['ncommaaccent'] = 556;
    t['onesuperior'] = 333;
    t['imacron'] = 278;
    t['Euro'] = 556;
  });
  t['Helvetica-Bold'] = (0, _util.getLookupTableFactory)(function (t) {
    t['space'] = 278;
    t['exclam'] = 333;
    t['quotedbl'] = 474;
    t['numbersign'] = 556;
    t['dollar'] = 556;
    t['percent'] = 889;
    t['ampersand'] = 722;
    t['quoteright'] = 278;
    t['parenleft'] = 333;
    t['parenright'] = 333;
    t['asterisk'] = 389;
    t['plus'] = 584;
    t['comma'] = 278;
    t['hyphen'] = 333;
    t['period'] = 278;
    t['slash'] = 278;
    t['zero'] = 556;
    t['one'] = 556;
    t['two'] = 556;
    t['three'] = 556;
    t['four'] = 556;
    t['five'] = 556;
    t['six'] = 556;
    t['seven'] = 556;
    t['eight'] = 556;
    t['nine'] = 556;
    t['colon'] = 333;
    t['semicolon'] = 333;
    t['less'] = 584;
    t['equal'] = 584;
    t['greater'] = 584;
    t['question'] = 611;
    t['at'] = 975;
    t['A'] = 722;
    t['B'] = 722;
    t['C'] = 722;
    t['D'] = 722;
    t['E'] = 667;
    t['F'] = 611;
    t['G'] = 778;
    t['H'] = 722;
    t['I'] = 278;
    t['J'] = 556;
    t['K'] = 722;
    t['L'] = 611;
    t['M'] = 833;
    t['N'] = 722;
    t['O'] = 778;
    t['P'] = 667;
    t['Q'] = 778;
    t['R'] = 722;
    t['S'] = 667;
    t['T'] = 611;
    t['U'] = 722;
    t['V'] = 667;
    t['W'] = 944;
    t['X'] = 667;
    t['Y'] = 667;
    t['Z'] = 611;
    t['bracketleft'] = 333;
    t['backslash'] = 278;
    t['bracketright'] = 333;
    t['asciicircum'] = 584;
    t['underscore'] = 556;
    t['quoteleft'] = 278;
    t['a'] = 556;
    t['b'] = 611;
    t['c'] = 556;
    t['d'] = 611;
    t['e'] = 556;
    t['f'] = 333;
    t['g'] = 611;
    t['h'] = 611;
    t['i'] = 278;
    t['j'] = 278;
    t['k'] = 556;
    t['l'] = 278;
    t['m'] = 889;
    t['n'] = 611;
    t['o'] = 611;
    t['p'] = 611;
    t['q'] = 611;
    t['r'] = 389;
    t['s'] = 556;
    t['t'] = 333;
    t['u'] = 611;
    t['v'] = 556;
    t['w'] = 778;
    t['x'] = 556;
    t['y'] = 556;
    t['z'] = 500;
    t['braceleft'] = 389;
    t['bar'] = 280;
    t['braceright'] = 389;
    t['asciitilde'] = 584;
    t['exclamdown'] = 333;
    t['cent'] = 556;
    t['sterling'] = 556;
    t['fraction'] = 167;
    t['yen'] = 556;
    t['florin'] = 556;
    t['section'] = 556;
    t['currency'] = 556;
    t['quotesingle'] = 238;
    t['quotedblleft'] = 500;
    t['guillemotleft'] = 556;
    t['guilsinglleft'] = 333;
    t['guilsinglright'] = 333;
    t['fi'] = 611;
    t['fl'] = 611;
    t['endash'] = 556;
    t['dagger'] = 556;
    t['daggerdbl'] = 556;
    t['periodcentered'] = 278;
    t['paragraph'] = 556;
    t['bullet'] = 350;
    t['quotesinglbase'] = 278;
    t['quotedblbase'] = 500;
    t['quotedblright'] = 500;
    t['guillemotright'] = 556;
    t['ellipsis'] = 1000;
    t['perthousand'] = 1000;
    t['questiondown'] = 611;
    t['grave'] = 333;
    t['acute'] = 333;
    t['circumflex'] = 333;
    t['tilde'] = 333;
    t['macron'] = 333;
    t['breve'] = 333;
    t['dotaccent'] = 333;
    t['dieresis'] = 333;
    t['ring'] = 333;
    t['cedilla'] = 333;
    t['hungarumlaut'] = 333;
    t['ogonek'] = 333;
    t['caron'] = 333;
    t['emdash'] = 1000;
    t['AE'] = 1000;
    t['ordfeminine'] = 370;
    t['Lslash'] = 611;
    t['Oslash'] = 778;
    t['OE'] = 1000;
    t['ordmasculine'] = 365;
    t['ae'] = 889;
    t['dotlessi'] = 278;
    t['lslash'] = 278;
    t['oslash'] = 611;
    t['oe'] = 944;
    t['germandbls'] = 611;
    t['Idieresis'] = 278;
    t['eacute'] = 556;
    t['abreve'] = 556;
    t['uhungarumlaut'] = 611;
    t['ecaron'] = 556;
    t['Ydieresis'] = 667;
    t['divide'] = 584;
    t['Yacute'] = 667;
    t['Acircumflex'] = 722;
    t['aacute'] = 556;
    t['Ucircumflex'] = 722;
    t['yacute'] = 556;
    t['scommaaccent'] = 556;
    t['ecircumflex'] = 556;
    t['Uring'] = 722;
    t['Udieresis'] = 722;
    t['aogonek'] = 556;
    t['Uacute'] = 722;
    t['uogonek'] = 611;
    t['Edieresis'] = 667;
    t['Dcroat'] = 722;
    t['commaaccent'] = 250;
    t['copyright'] = 737;
    t['Emacron'] = 667;
    t['ccaron'] = 556;
    t['aring'] = 556;
    t['Ncommaaccent'] = 722;
    t['lacute'] = 278;
    t['agrave'] = 556;
    t['Tcommaaccent'] = 611;
    t['Cacute'] = 722;
    t['atilde'] = 556;
    t['Edotaccent'] = 667;
    t['scaron'] = 556;
    t['scedilla'] = 556;
    t['iacute'] = 278;
    t['lozenge'] = 494;
    t['Rcaron'] = 722;
    t['Gcommaaccent'] = 778;
    t['ucircumflex'] = 611;
    t['acircumflex'] = 556;
    t['Amacron'] = 722;
    t['rcaron'] = 389;
    t['ccedilla'] = 556;
    t['Zdotaccent'] = 611;
    t['Thorn'] = 667;
    t['Omacron'] = 778;
    t['Racute'] = 722;
    t['Sacute'] = 667;
    t['dcaron'] = 743;
    t['Umacron'] = 722;
    t['uring'] = 611;
    t['threesuperior'] = 333;
    t['Ograve'] = 778;
    t['Agrave'] = 722;
    t['Abreve'] = 722;
    t['multiply'] = 584;
    t['uacute'] = 611;
    t['Tcaron'] = 611;
    t['partialdiff'] = 494;
    t['ydieresis'] = 556;
    t['Nacute'] = 722;
    t['icircumflex'] = 278;
    t['Ecircumflex'] = 667;
    t['adieresis'] = 556;
    t['edieresis'] = 556;
    t['cacute'] = 556;
    t['nacute'] = 611;
    t['umacron'] = 611;
    t['Ncaron'] = 722;
    t['Iacute'] = 278;
    t['plusminus'] = 584;
    t['brokenbar'] = 280;
    t['registered'] = 737;
    t['Gbreve'] = 778;
    t['Idotaccent'] = 278;
    t['summation'] = 600;
    t['Egrave'] = 667;
    t['racute'] = 389;
    t['omacron'] = 611;
    t['Zacute'] = 611;
    t['Zcaron'] = 611;
    t['greaterequal'] = 549;
    t['Eth'] = 722;
    t['Ccedilla'] = 722;
    t['lcommaaccent'] = 278;
    t['tcaron'] = 389;
    t['eogonek'] = 556;
    t['Uogonek'] = 722;
    t['Aacute'] = 722;
    t['Adieresis'] = 722;
    t['egrave'] = 556;
    t['zacute'] = 500;
    t['iogonek'] = 278;
    t['Oacute'] = 778;
    t['oacute'] = 611;
    t['amacron'] = 556;
    t['sacute'] = 556;
    t['idieresis'] = 278;
    t['Ocircumflex'] = 778;
    t['Ugrave'] = 722;
    t['Delta'] = 612;
    t['thorn'] = 611;
    t['twosuperior'] = 333;
    t['Odieresis'] = 778;
    t['mu'] = 611;
    t['igrave'] = 278;
    t['ohungarumlaut'] = 611;
    t['Eogonek'] = 667;
    t['dcroat'] = 611;
    t['threequarters'] = 834;
    t['Scedilla'] = 667;
    t['lcaron'] = 400;
    t['Kcommaaccent'] = 722;
    t['Lacute'] = 611;
    t['trademark'] = 1000;
    t['edotaccent'] = 556;
    t['Igrave'] = 278;
    t['Imacron'] = 278;
    t['Lcaron'] = 611;
    t['onehalf'] = 834;
    t['lessequal'] = 549;
    t['ocircumflex'] = 611;
    t['ntilde'] = 611;
    t['Uhungarumlaut'] = 722;
    t['Eacute'] = 667;
    t['emacron'] = 556;
    t['gbreve'] = 611;
    t['onequarter'] = 834;
    t['Scaron'] = 667;
    t['Scommaaccent'] = 667;
    t['Ohungarumlaut'] = 778;
    t['degree'] = 400;
    t['ograve'] = 611;
    t['Ccaron'] = 722;
    t['ugrave'] = 611;
    t['radical'] = 549;
    t['Dcaron'] = 722;
    t['rcommaaccent'] = 389;
    t['Ntilde'] = 722;
    t['otilde'] = 611;
    t['Rcommaaccent'] = 722;
    t['Lcommaaccent'] = 611;
    t['Atilde'] = 722;
    t['Aogonek'] = 722;
    t['Aring'] = 722;
    t['Otilde'] = 778;
    t['zdotaccent'] = 500;
    t['Ecaron'] = 667;
    t['Iogonek'] = 278;
    t['kcommaaccent'] = 556;
    t['minus'] = 584;
    t['Icircumflex'] = 278;
    t['ncaron'] = 611;
    t['tcommaaccent'] = 333;
    t['logicalnot'] = 584;
    t['odieresis'] = 611;
    t['udieresis'] = 611;
    t['notequal'] = 549;
    t['gcommaaccent'] = 611;
    t['eth'] = 611;
    t['zcaron'] = 500;
    t['ncommaaccent'] = 611;
    t['onesuperior'] = 333;
    t['imacron'] = 278;
    t['Euro'] = 556;
  });
  t['Helvetica-BoldOblique'] = (0, _util.getLookupTableFactory)(function (t) {
    t['space'] = 278;
    t['exclam'] = 333;
    t['quotedbl'] = 474;
    t['numbersign'] = 556;
    t['dollar'] = 556;
    t['percent'] = 889;
    t['ampersand'] = 722;
    t['quoteright'] = 278;
    t['parenleft'] = 333;
    t['parenright'] = 333;
    t['asterisk'] = 389;
    t['plus'] = 584;
    t['comma'] = 278;
    t['hyphen'] = 333;
    t['period'] = 278;
    t['slash'] = 278;
    t['zero'] = 556;
    t['one'] = 556;
    t['two'] = 556;
    t['three'] = 556;
    t['four'] = 556;
    t['five'] = 556;
    t['six'] = 556;
    t['seven'] = 556;
    t['eight'] = 556;
    t['nine'] = 556;
    t['colon'] = 333;
    t['semicolon'] = 333;
    t['less'] = 584;
    t['equal'] = 584;
    t['greater'] = 584;
    t['question'] = 611;
    t['at'] = 975;
    t['A'] = 722;
    t['B'] = 722;
    t['C'] = 722;
    t['D'] = 722;
    t['E'] = 667;
    t['F'] = 611;
    t['G'] = 778;
    t['H'] = 722;
    t['I'] = 278;
    t['J'] = 556;
    t['K'] = 722;
    t['L'] = 611;
    t['M'] = 833;
    t['N'] = 722;
    t['O'] = 778;
    t['P'] = 667;
    t['Q'] = 778;
    t['R'] = 722;
    t['S'] = 667;
    t['T'] = 611;
    t['U'] = 722;
    t['V'] = 667;
    t['W'] = 944;
    t['X'] = 667;
    t['Y'] = 667;
    t['Z'] = 611;
    t['bracketleft'] = 333;
    t['backslash'] = 278;
    t['bracketright'] = 333;
    t['asciicircum'] = 584;
    t['underscore'] = 556;
    t['quoteleft'] = 278;
    t['a'] = 556;
    t['b'] = 611;
    t['c'] = 556;
    t['d'] = 611;
    t['e'] = 556;
    t['f'] = 333;
    t['g'] = 611;
    t['h'] = 611;
    t['i'] = 278;
    t['j'] = 278;
    t['k'] = 556;
    t['l'] = 278;
    t['m'] = 889;
    t['n'] = 611;
    t['o'] = 611;
    t['p'] = 611;
    t['q'] = 611;
    t['r'] = 389;
    t['s'] = 556;
    t['t'] = 333;
    t['u'] = 611;
    t['v'] = 556;
    t['w'] = 778;
    t['x'] = 556;
    t['y'] = 556;
    t['z'] = 500;
    t['braceleft'] = 389;
    t['bar'] = 280;
    t['braceright'] = 389;
    t['asciitilde'] = 584;
    t['exclamdown'] = 333;
    t['cent'] = 556;
    t['sterling'] = 556;
    t['fraction'] = 167;
    t['yen'] = 556;
    t['florin'] = 556;
    t['section'] = 556;
    t['currency'] = 556;
    t['quotesingle'] = 238;
    t['quotedblleft'] = 500;
    t['guillemotleft'] = 556;
    t['guilsinglleft'] = 333;
    t['guilsinglright'] = 333;
    t['fi'] = 611;
    t['fl'] = 611;
    t['endash'] = 556;
    t['dagger'] = 556;
    t['daggerdbl'] = 556;
    t['periodcentered'] = 278;
    t['paragraph'] = 556;
    t['bullet'] = 350;
    t['quotesinglbase'] = 278;
    t['quotedblbase'] = 500;
    t['quotedblright'] = 500;
    t['guillemotright'] = 556;
    t['ellipsis'] = 1000;
    t['perthousand'] = 1000;
    t['questiondown'] = 611;
    t['grave'] = 333;
    t['acute'] = 333;
    t['circumflex'] = 333;
    t['tilde'] = 333;
    t['macron'] = 333;
    t['breve'] = 333;
    t['dotaccent'] = 333;
    t['dieresis'] = 333;
    t['ring'] = 333;
    t['cedilla'] = 333;
    t['hungarumlaut'] = 333;
    t['ogonek'] = 333;
    t['caron'] = 333;
    t['emdash'] = 1000;
    t['AE'] = 1000;
    t['ordfeminine'] = 370;
    t['Lslash'] = 611;
    t['Oslash'] = 778;
    t['OE'] = 1000;
    t['ordmasculine'] = 365;
    t['ae'] = 889;
    t['dotlessi'] = 278;
    t['lslash'] = 278;
    t['oslash'] = 611;
    t['oe'] = 944;
    t['germandbls'] = 611;
    t['Idieresis'] = 278;
    t['eacute'] = 556;
    t['abreve'] = 556;
    t['uhungarumlaut'] = 611;
    t['ecaron'] = 556;
    t['Ydieresis'] = 667;
    t['divide'] = 584;
    t['Yacute'] = 667;
    t['Acircumflex'] = 722;
    t['aacute'] = 556;
    t['Ucircumflex'] = 722;
    t['yacute'] = 556;
    t['scommaaccent'] = 556;
    t['ecircumflex'] = 556;
    t['Uring'] = 722;
    t['Udieresis'] = 722;
    t['aogonek'] = 556;
    t['Uacute'] = 722;
    t['uogonek'] = 611;
    t['Edieresis'] = 667;
    t['Dcroat'] = 722;
    t['commaaccent'] = 250;
    t['copyright'] = 737;
    t['Emacron'] = 667;
    t['ccaron'] = 556;
    t['aring'] = 556;
    t['Ncommaaccent'] = 722;
    t['lacute'] = 278;
    t['agrave'] = 556;
    t['Tcommaaccent'] = 611;
    t['Cacute'] = 722;
    t['atilde'] = 556;
    t['Edotaccent'] = 667;
    t['scaron'] = 556;
    t['scedilla'] = 556;
    t['iacute'] = 278;
    t['lozenge'] = 494;
    t['Rcaron'] = 722;
    t['Gcommaaccent'] = 778;
    t['ucircumflex'] = 611;
    t['acircumflex'] = 556;
    t['Amacron'] = 722;
    t['rcaron'] = 389;
    t['ccedilla'] = 556;
    t['Zdotaccent'] = 611;
    t['Thorn'] = 667;
    t['Omacron'] = 778;
    t['Racute'] = 722;
    t['Sacute'] = 667;
    t['dcaron'] = 743;
    t['Umacron'] = 722;
    t['uring'] = 611;
    t['threesuperior'] = 333;
    t['Ograve'] = 778;
    t['Agrave'] = 722;
    t['Abreve'] = 722;
    t['multiply'] = 584;
    t['uacute'] = 611;
    t['Tcaron'] = 611;
    t['partialdiff'] = 494;
    t['ydieresis'] = 556;
    t['Nacute'] = 722;
    t['icircumflex'] = 278;
    t['Ecircumflex'] = 667;
    t['adieresis'] = 556;
    t['edieresis'] = 556;
    t['cacute'] = 556;
    t['nacute'] = 611;
    t['umacron'] = 611;
    t['Ncaron'] = 722;
    t['Iacute'] = 278;
    t['plusminus'] = 584;
    t['brokenbar'] = 280;
    t['registered'] = 737;
    t['Gbreve'] = 778;
    t['Idotaccent'] = 278;
    t['summation'] = 600;
    t['Egrave'] = 667;
    t['racute'] = 389;
    t['omacron'] = 611;
    t['Zacute'] = 611;
    t['Zcaron'] = 611;
    t['greaterequal'] = 549;
    t['Eth'] = 722;
    t['Ccedilla'] = 722;
    t['lcommaaccent'] = 278;
    t['tcaron'] = 389;
    t['eogonek'] = 556;
    t['Uogonek'] = 722;
    t['Aacute'] = 722;
    t['Adieresis'] = 722;
    t['egrave'] = 556;
    t['zacute'] = 500;
    t['iogonek'] = 278;
    t['Oacute'] = 778;
    t['oacute'] = 611;
    t['amacron'] = 556;
    t['sacute'] = 556;
    t['idieresis'] = 278;
    t['Ocircumflex'] = 778;
    t['Ugrave'] = 722;
    t['Delta'] = 612;
    t['thorn'] = 611;
    t['twosuperior'] = 333;
    t['Odieresis'] = 778;
    t['mu'] = 611;
    t['igrave'] = 278;
    t['ohungarumlaut'] = 611;
    t['Eogonek'] = 667;
    t['dcroat'] = 611;
    t['threequarters'] = 834;
    t['Scedilla'] = 667;
    t['lcaron'] = 400;
    t['Kcommaaccent'] = 722;
    t['Lacute'] = 611;
    t['trademark'] = 1000;
    t['edotaccent'] = 556;
    t['Igrave'] = 278;
    t['Imacron'] = 278;
    t['Lcaron'] = 611;
    t['onehalf'] = 834;
    t['lessequal'] = 549;
    t['ocircumflex'] = 611;
    t['ntilde'] = 611;
    t['Uhungarumlaut'] = 722;
    t['Eacute'] = 667;
    t['emacron'] = 556;
    t['gbreve'] = 611;
    t['onequarter'] = 834;
    t['Scaron'] = 667;
    t['Scommaaccent'] = 667;
    t['Ohungarumlaut'] = 778;
    t['degree'] = 400;
    t['ograve'] = 611;
    t['Ccaron'] = 722;
    t['ugrave'] = 611;
    t['radical'] = 549;
    t['Dcaron'] = 722;
    t['rcommaaccent'] = 389;
    t['Ntilde'] = 722;
    t['otilde'] = 611;
    t['Rcommaaccent'] = 722;
    t['Lcommaaccent'] = 611;
    t['Atilde'] = 722;
    t['Aogonek'] = 722;
    t['Aring'] = 722;
    t['Otilde'] = 778;
    t['zdotaccent'] = 500;
    t['Ecaron'] = 667;
    t['Iogonek'] = 278;
    t['kcommaaccent'] = 556;
    t['minus'] = 584;
    t['Icircumflex'] = 278;
    t['ncaron'] = 611;
    t['tcommaaccent'] = 333;
    t['logicalnot'] = 584;
    t['odieresis'] = 611;
    t['udieresis'] = 611;
    t['notequal'] = 549;
    t['gcommaaccent'] = 611;
    t['eth'] = 611;
    t['zcaron'] = 500;
    t['ncommaaccent'] = 611;
    t['onesuperior'] = 333;
    t['imacron'] = 278;
    t['Euro'] = 556;
  });
  t['Helvetica-Oblique'] = (0, _util.getLookupTableFactory)(function (t) {
    t['space'] = 278;
    t['exclam'] = 278;
    t['quotedbl'] = 355;
    t['numbersign'] = 556;
    t['dollar'] = 556;
    t['percent'] = 889;
    t['ampersand'] = 667;
    t['quoteright'] = 222;
    t['parenleft'] = 333;
    t['parenright'] = 333;
    t['asterisk'] = 389;
    t['plus'] = 584;
    t['comma'] = 278;
    t['hyphen'] = 333;
    t['period'] = 278;
    t['slash'] = 278;
    t['zero'] = 556;
    t['one'] = 556;
    t['two'] = 556;
    t['three'] = 556;
    t['four'] = 556;
    t['five'] = 556;
    t['six'] = 556;
    t['seven'] = 556;
    t['eight'] = 556;
    t['nine'] = 556;
    t['colon'] = 278;
    t['semicolon'] = 278;
    t['less'] = 584;
    t['equal'] = 584;
    t['greater'] = 584;
    t['question'] = 556;
    t['at'] = 1015;
    t['A'] = 667;
    t['B'] = 667;
    t['C'] = 722;
    t['D'] = 722;
    t['E'] = 667;
    t['F'] = 611;
    t['G'] = 778;
    t['H'] = 722;
    t['I'] = 278;
    t['J'] = 500;
    t['K'] = 667;
    t['L'] = 556;
    t['M'] = 833;
    t['N'] = 722;
    t['O'] = 778;
    t['P'] = 667;
    t['Q'] = 778;
    t['R'] = 722;
    t['S'] = 667;
    t['T'] = 611;
    t['U'] = 722;
    t['V'] = 667;
    t['W'] = 944;
    t['X'] = 667;
    t['Y'] = 667;
    t['Z'] = 611;
    t['bracketleft'] = 278;
    t['backslash'] = 278;
    t['bracketright'] = 278;
    t['asciicircum'] = 469;
    t['underscore'] = 556;
    t['quoteleft'] = 222;
    t['a'] = 556;
    t['b'] = 556;
    t['c'] = 500;
    t['d'] = 556;
    t['e'] = 556;
    t['f'] = 278;
    t['g'] = 556;
    t['h'] = 556;
    t['i'] = 222;
    t['j'] = 222;
    t['k'] = 500;
    t['l'] = 222;
    t['m'] = 833;
    t['n'] = 556;
    t['o'] = 556;
    t['p'] = 556;
    t['q'] = 556;
    t['r'] = 333;
    t['s'] = 500;
    t['t'] = 278;
    t['u'] = 556;
    t['v'] = 500;
    t['w'] = 722;
    t['x'] = 500;
    t['y'] = 500;
    t['z'] = 500;
    t['braceleft'] = 334;
    t['bar'] = 260;
    t['braceright'] = 334;
    t['asciitilde'] = 584;
    t['exclamdown'] = 333;
    t['cent'] = 556;
    t['sterling'] = 556;
    t['fraction'] = 167;
    t['yen'] = 556;
    t['florin'] = 556;
    t['section'] = 556;
    t['currency'] = 556;
    t['quotesingle'] = 191;
    t['quotedblleft'] = 333;
    t['guillemotleft'] = 556;
    t['guilsinglleft'] = 333;
    t['guilsinglright'] = 333;
    t['fi'] = 500;
    t['fl'] = 500;
    t['endash'] = 556;
    t['dagger'] = 556;
    t['daggerdbl'] = 556;
    t['periodcentered'] = 278;
    t['paragraph'] = 537;
    t['bullet'] = 350;
    t['quotesinglbase'] = 222;
    t['quotedblbase'] = 333;
    t['quotedblright'] = 333;
    t['guillemotright'] = 556;
    t['ellipsis'] = 1000;
    t['perthousand'] = 1000;
    t['questiondown'] = 611;
    t['grave'] = 333;
    t['acute'] = 333;
    t['circumflex'] = 333;
    t['tilde'] = 333;
    t['macron'] = 333;
    t['breve'] = 333;
    t['dotaccent'] = 333;
    t['dieresis'] = 333;
    t['ring'] = 333;
    t['cedilla'] = 333;
    t['hungarumlaut'] = 333;
    t['ogonek'] = 333;
    t['caron'] = 333;
    t['emdash'] = 1000;
    t['AE'] = 1000;
    t['ordfeminine'] = 370;
    t['Lslash'] = 556;
    t['Oslash'] = 778;
    t['OE'] = 1000;
    t['ordmasculine'] = 365;
    t['ae'] = 889;
    t['dotlessi'] = 278;
    t['lslash'] = 222;
    t['oslash'] = 611;
    t['oe'] = 944;
    t['germandbls'] = 611;
    t['Idieresis'] = 278;
    t['eacute'] = 556;
    t['abreve'] = 556;
    t['uhungarumlaut'] = 556;
    t['ecaron'] = 556;
    t['Ydieresis'] = 667;
    t['divide'] = 584;
    t['Yacute'] = 667;
    t['Acircumflex'] = 667;
    t['aacute'] = 556;
    t['Ucircumflex'] = 722;
    t['yacute'] = 500;
    t['scommaaccent'] = 500;
    t['ecircumflex'] = 556;
    t['Uring'] = 722;
    t['Udieresis'] = 722;
    t['aogonek'] = 556;
    t['Uacute'] = 722;
    t['uogonek'] = 556;
    t['Edieresis'] = 667;
    t['Dcroat'] = 722;
    t['commaaccent'] = 250;
    t['copyright'] = 737;
    t['Emacron'] = 667;
    t['ccaron'] = 500;
    t['aring'] = 556;
    t['Ncommaaccent'] = 722;
    t['lacute'] = 222;
    t['agrave'] = 556;
    t['Tcommaaccent'] = 611;
    t['Cacute'] = 722;
    t['atilde'] = 556;
    t['Edotaccent'] = 667;
    t['scaron'] = 500;
    t['scedilla'] = 500;
    t['iacute'] = 278;
    t['lozenge'] = 471;
    t['Rcaron'] = 722;
    t['Gcommaaccent'] = 778;
    t['ucircumflex'] = 556;
    t['acircumflex'] = 556;
    t['Amacron'] = 667;
    t['rcaron'] = 333;
    t['ccedilla'] = 500;
    t['Zdotaccent'] = 611;
    t['Thorn'] = 667;
    t['Omacron'] = 778;
    t['Racute'] = 722;
    t['Sacute'] = 667;
    t['dcaron'] = 643;
    t['Umacron'] = 722;
    t['uring'] = 556;
    t['threesuperior'] = 333;
    t['Ograve'] = 778;
    t['Agrave'] = 667;
    t['Abreve'] = 667;
    t['multiply'] = 584;
    t['uacute'] = 556;
    t['Tcaron'] = 611;
    t['partialdiff'] = 476;
    t['ydieresis'] = 500;
    t['Nacute'] = 722;
    t['icircumflex'] = 278;
    t['Ecircumflex'] = 667;
    t['adieresis'] = 556;
    t['edieresis'] = 556;
    t['cacute'] = 500;
    t['nacute'] = 556;
    t['umacron'] = 556;
    t['Ncaron'] = 722;
    t['Iacute'] = 278;
    t['plusminus'] = 584;
    t['brokenbar'] = 260;
    t['registered'] = 737;
    t['Gbreve'] = 778;
    t['Idotaccent'] = 278;
    t['summation'] = 600;
    t['Egrave'] = 667;
    t['racute'] = 333;
    t['omacron'] = 556;
    t['Zacute'] = 611;
    t['Zcaron'] = 611;
    t['greaterequal'] = 549;
    t['Eth'] = 722;
    t['Ccedilla'] = 722;
    t['lcommaaccent'] = 222;
    t['tcaron'] = 317;
    t['eogonek'] = 556;
    t['Uogonek'] = 722;
    t['Aacute'] = 667;
    t['Adieresis'] = 667;
    t['egrave'] = 556;
    t['zacute'] = 500;
    t['iogonek'] = 222;
    t['Oacute'] = 778;
    t['oacute'] = 556;
    t['amacron'] = 556;
    t['sacute'] = 500;
    t['idieresis'] = 278;
    t['Ocircumflex'] = 778;
    t['Ugrave'] = 722;
    t['Delta'] = 612;
    t['thorn'] = 556;
    t['twosuperior'] = 333;
    t['Odieresis'] = 778;
    t['mu'] = 556;
    t['igrave'] = 278;
    t['ohungarumlaut'] = 556;
    t['Eogonek'] = 667;
    t['dcroat'] = 556;
    t['threequarters'] = 834;
    t['Scedilla'] = 667;
    t['lcaron'] = 299;
    t['Kcommaaccent'] = 667;
    t['Lacute'] = 556;
    t['trademark'] = 1000;
    t['edotaccent'] = 556;
    t['Igrave'] = 278;
    t['Imacron'] = 278;
    t['Lcaron'] = 556;
    t['onehalf'] = 834;
    t['lessequal'] = 549;
    t['ocircumflex'] = 556;
    t['ntilde'] = 556;
    t['Uhungarumlaut'] = 722;
    t['Eacute'] = 667;
    t['emacron'] = 556;
    t['gbreve'] = 556;
    t['onequarter'] = 834;
    t['Scaron'] = 667;
    t['Scommaaccent'] = 667;
    t['Ohungarumlaut'] = 778;
    t['degree'] = 400;
    t['ograve'] = 556;
    t['Ccaron'] = 722;
    t['ugrave'] = 556;
    t['radical'] = 453;
    t['Dcaron'] = 722;
    t['rcommaaccent'] = 333;
    t['Ntilde'] = 722;
    t['otilde'] = 556;
    t['Rcommaaccent'] = 722;
    t['Lcommaaccent'] = 556;
    t['Atilde'] = 667;
    t['Aogonek'] = 667;
    t['Aring'] = 667;
    t['Otilde'] = 778;
    t['zdotaccent'] = 500;
    t['Ecaron'] = 667;
    t['Iogonek'] = 278;
    t['kcommaaccent'] = 500;
    t['minus'] = 584;
    t['Icircumflex'] = 278;
    t['ncaron'] = 556;
    t['tcommaaccent'] = 278;
    t['logicalnot'] = 584;
    t['odieresis'] = 556;
    t['udieresis'] = 556;
    t['notequal'] = 549;
    t['gcommaaccent'] = 556;
    t['eth'] = 556;
    t['zcaron'] = 500;
    t['ncommaaccent'] = 556;
    t['onesuperior'] = 333;
    t['imacron'] = 278;
    t['Euro'] = 556;
  });
  t['Symbol'] = (0, _util.getLookupTableFactory)(function (t) {
    t['space'] = 250;
    t['exclam'] = 333;
    t['universal'] = 713;
    t['numbersign'] = 500;
    t['existential'] = 549;
    t['percent'] = 833;
    t['ampersand'] = 778;
    t['suchthat'] = 439;
    t['parenleft'] = 333;
    t['parenright'] = 333;
    t['asteriskmath'] = 500;
    t['plus'] = 549;
    t['comma'] = 250;
    t['minus'] = 549;
    t['period'] = 250;
    t['slash'] = 278;
    t['zero'] = 500;
    t['one'] = 500;
    t['two'] = 500;
    t['three'] = 500;
    t['four'] = 500;
    t['five'] = 500;
    t['six'] = 500;
    t['seven'] = 500;
    t['eight'] = 500;
    t['nine'] = 500;
    t['colon'] = 278;
    t['semicolon'] = 278;
    t['less'] = 549;
    t['equal'] = 549;
    t['greater'] = 549;
    t['question'] = 444;
    t['congruent'] = 549;
    t['Alpha'] = 722;
    t['Beta'] = 667;
    t['Chi'] = 722;
    t['Delta'] = 612;
    t['Epsilon'] = 611;
    t['Phi'] = 763;
    t['Gamma'] = 603;
    t['Eta'] = 722;
    t['Iota'] = 333;
    t['theta1'] = 631;
    t['Kappa'] = 722;
    t['Lambda'] = 686;
    t['Mu'] = 889;
    t['Nu'] = 722;
    t['Omicron'] = 722;
    t['Pi'] = 768;
    t['Theta'] = 741;
    t['Rho'] = 556;
    t['Sigma'] = 592;
    t['Tau'] = 611;
    t['Upsilon'] = 690;
    t['sigma1'] = 439;
    t['Omega'] = 768;
    t['Xi'] = 645;
    t['Psi'] = 795;
    t['Zeta'] = 611;
    t['bracketleft'] = 333;
    t['therefore'] = 863;
    t['bracketright'] = 333;
    t['perpendicular'] = 658;
    t['underscore'] = 500;
    t['radicalex'] = 500;
    t['alpha'] = 631;
    t['beta'] = 549;
    t['chi'] = 549;
    t['delta'] = 494;
    t['epsilon'] = 439;
    t['phi'] = 521;
    t['gamma'] = 411;
    t['eta'] = 603;
    t['iota'] = 329;
    t['phi1'] = 603;
    t['kappa'] = 549;
    t['lambda'] = 549;
    t['mu'] = 576;
    t['nu'] = 521;
    t['omicron'] = 549;
    t['pi'] = 549;
    t['theta'] = 521;
    t['rho'] = 549;
    t['sigma'] = 603;
    t['tau'] = 439;
    t['upsilon'] = 576;
    t['omega1'] = 713;
    t['omega'] = 686;
    t['xi'] = 493;
    t['psi'] = 686;
    t['zeta'] = 494;
    t['braceleft'] = 480;
    t['bar'] = 200;
    t['braceright'] = 480;
    t['similar'] = 549;
    t['Euro'] = 750;
    t['Upsilon1'] = 620;
    t['minute'] = 247;
    t['lessequal'] = 549;
    t['fraction'] = 167;
    t['infinity'] = 713;
    t['florin'] = 500;
    t['club'] = 753;
    t['diamond'] = 753;
    t['heart'] = 753;
    t['spade'] = 753;
    t['arrowboth'] = 1042;
    t['arrowleft'] = 987;
    t['arrowup'] = 603;
    t['arrowright'] = 987;
    t['arrowdown'] = 603;
    t['degree'] = 400;
    t['plusminus'] = 549;
    t['second'] = 411;
    t['greaterequal'] = 549;
    t['multiply'] = 549;
    t['proportional'] = 713;
    t['partialdiff'] = 494;
    t['bullet'] = 460;
    t['divide'] = 549;
    t['notequal'] = 549;
    t['equivalence'] = 549;
    t['approxequal'] = 549;
    t['ellipsis'] = 1000;
    t['arrowvertex'] = 603;
    t['arrowhorizex'] = 1000;
    t['carriagereturn'] = 658;
    t['aleph'] = 823;
    t['Ifraktur'] = 686;
    t['Rfraktur'] = 795;
    t['weierstrass'] = 987;
    t['circlemultiply'] = 768;
    t['circleplus'] = 768;
    t['emptyset'] = 823;
    t['intersection'] = 768;
    t['union'] = 768;
    t['propersuperset'] = 713;
    t['reflexsuperset'] = 713;
    t['notsubset'] = 713;
    t['propersubset'] = 713;
    t['reflexsubset'] = 713;
    t['element'] = 713;
    t['notelement'] = 713;
    t['angle'] = 768;
    t['gradient'] = 713;
    t['registerserif'] = 790;
    t['copyrightserif'] = 790;
    t['trademarkserif'] = 890;
    t['product'] = 823;
    t['radical'] = 549;
    t['dotmath'] = 250;
    t['logicalnot'] = 713;
    t['logicaland'] = 603;
    t['logicalor'] = 603;
    t['arrowdblboth'] = 1042;
    t['arrowdblleft'] = 987;
    t['arrowdblup'] = 603;
    t['arrowdblright'] = 987;
    t['arrowdbldown'] = 603;
    t['lozenge'] = 494;
    t['angleleft'] = 329;
    t['registersans'] = 790;
    t['copyrightsans'] = 790;
    t['trademarksans'] = 786;
    t['summation'] = 713;
    t['parenlefttp'] = 384;
    t['parenleftex'] = 384;
    t['parenleftbt'] = 384;
    t['bracketlefttp'] = 384;
    t['bracketleftex'] = 384;
    t['bracketleftbt'] = 384;
    t['bracelefttp'] = 494;
    t['braceleftmid'] = 494;
    t['braceleftbt'] = 494;
    t['braceex'] = 494;
    t['angleright'] = 329;
    t['integral'] = 274;
    t['integraltp'] = 686;
    t['integralex'] = 686;
    t['integralbt'] = 686;
    t['parenrighttp'] = 384;
    t['parenrightex'] = 384;
    t['parenrightbt'] = 384;
    t['bracketrighttp'] = 384;
    t['bracketrightex'] = 384;
    t['bracketrightbt'] = 384;
    t['bracerighttp'] = 494;
    t['bracerightmid'] = 494;
    t['bracerightbt'] = 494;
    t['apple'] = 790;
  });
  t['Times-Roman'] = (0, _util.getLookupTableFactory)(function (t) {
    t['space'] = 250;
    t['exclam'] = 333;
    t['quotedbl'] = 408;
    t['numbersign'] = 500;
    t['dollar'] = 500;
    t['percent'] = 833;
    t['ampersand'] = 778;
    t['quoteright'] = 333;
    t['parenleft'] = 333;
    t['parenright'] = 333;
    t['asterisk'] = 500;
    t['plus'] = 564;
    t['comma'] = 250;
    t['hyphen'] = 333;
    t['period'] = 250;
    t['slash'] = 278;
    t['zero'] = 500;
    t['one'] = 500;
    t['two'] = 500;
    t['three'] = 500;
    t['four'] = 500;
    t['five'] = 500;
    t['six'] = 500;
    t['seven'] = 500;
    t['eight'] = 500;
    t['nine'] = 500;
    t['colon'] = 278;
    t['semicolon'] = 278;
    t['less'] = 564;
    t['equal'] = 564;
    t['greater'] = 564;
    t['question'] = 444;
    t['at'] = 921;
    t['A'] = 722;
    t['B'] = 667;
    t['C'] = 667;
    t['D'] = 722;
    t['E'] = 611;
    t['F'] = 556;
    t['G'] = 722;
    t['H'] = 722;
    t['I'] = 333;
    t['J'] = 389;
    t['K'] = 722;
    t['L'] = 611;
    t['M'] = 889;
    t['N'] = 722;
    t['O'] = 722;
    t['P'] = 556;
    t['Q'] = 722;
    t['R'] = 667;
    t['S'] = 556;
    t['T'] = 611;
    t['U'] = 722;
    t['V'] = 722;
    t['W'] = 944;
    t['X'] = 722;
    t['Y'] = 722;
    t['Z'] = 611;
    t['bracketleft'] = 333;
    t['backslash'] = 278;
    t['bracketright'] = 333;
    t['asciicircum'] = 469;
    t['underscore'] = 500;
    t['quoteleft'] = 333;
    t['a'] = 444;
    t['b'] = 500;
    t['c'] = 444;
    t['d'] = 500;
    t['e'] = 444;
    t['f'] = 333;
    t['g'] = 500;
    t['h'] = 500;
    t['i'] = 278;
    t['j'] = 278;
    t['k'] = 500;
    t['l'] = 278;
    t['m'] = 778;
    t['n'] = 500;
    t['o'] = 500;
    t['p'] = 500;
    t['q'] = 500;
    t['r'] = 333;
    t['s'] = 389;
    t['t'] = 278;
    t['u'] = 500;
    t['v'] = 500;
    t['w'] = 722;
    t['x'] = 500;
    t['y'] = 500;
    t['z'] = 444;
    t['braceleft'] = 480;
    t['bar'] = 200;
    t['braceright'] = 480;
    t['asciitilde'] = 541;
    t['exclamdown'] = 333;
    t['cent'] = 500;
    t['sterling'] = 500;
    t['fraction'] = 167;
    t['yen'] = 500;
    t['florin'] = 500;
    t['section'] = 500;
    t['currency'] = 500;
    t['quotesingle'] = 180;
    t['quotedblleft'] = 444;
    t['guillemotleft'] = 500;
    t['guilsinglleft'] = 333;
    t['guilsinglright'] = 333;
    t['fi'] = 556;
    t['fl'] = 556;
    t['endash'] = 500;
    t['dagger'] = 500;
    t['daggerdbl'] = 500;
    t['periodcentered'] = 250;
    t['paragraph'] = 453;
    t['bullet'] = 350;
    t['quotesinglbase'] = 333;
    t['quotedblbase'] = 444;
    t['quotedblright'] = 444;
    t['guillemotright'] = 500;
    t['ellipsis'] = 1000;
    t['perthousand'] = 1000;
    t['questiondown'] = 444;
    t['grave'] = 333;
    t['acute'] = 333;
    t['circumflex'] = 333;
    t['tilde'] = 333;
    t['macron'] = 333;
    t['breve'] = 333;
    t['dotaccent'] = 333;
    t['dieresis'] = 333;
    t['ring'] = 333;
    t['cedilla'] = 333;
    t['hungarumlaut'] = 333;
    t['ogonek'] = 333;
    t['caron'] = 333;
    t['emdash'] = 1000;
    t['AE'] = 889;
    t['ordfeminine'] = 276;
    t['Lslash'] = 611;
    t['Oslash'] = 722;
    t['OE'] = 889;
    t['ordmasculine'] = 310;
    t['ae'] = 667;
    t['dotlessi'] = 278;
    t['lslash'] = 278;
    t['oslash'] = 500;
    t['oe'] = 722;
    t['germandbls'] = 500;
    t['Idieresis'] = 333;
    t['eacute'] = 444;
    t['abreve'] = 444;
    t['uhungarumlaut'] = 500;
    t['ecaron'] = 444;
    t['Ydieresis'] = 722;
    t['divide'] = 564;
    t['Yacute'] = 722;
    t['Acircumflex'] = 722;
    t['aacute'] = 444;
    t['Ucircumflex'] = 722;
    t['yacute'] = 500;
    t['scommaaccent'] = 389;
    t['ecircumflex'] = 444;
    t['Uring'] = 722;
    t['Udieresis'] = 722;
    t['aogonek'] = 444;
    t['Uacute'] = 722;
    t['uogonek'] = 500;
    t['Edieresis'] = 611;
    t['Dcroat'] = 722;
    t['commaaccent'] = 250;
    t['copyright'] = 760;
    t['Emacron'] = 611;
    t['ccaron'] = 444;
    t['aring'] = 444;
    t['Ncommaaccent'] = 722;
    t['lacute'] = 278;
    t['agrave'] = 444;
    t['Tcommaaccent'] = 611;
    t['Cacute'] = 667;
    t['atilde'] = 444;
    t['Edotaccent'] = 611;
    t['scaron'] = 389;
    t['scedilla'] = 389;
    t['iacute'] = 278;
    t['lozenge'] = 471;
    t['Rcaron'] = 667;
    t['Gcommaaccent'] = 722;
    t['ucircumflex'] = 500;
    t['acircumflex'] = 444;
    t['Amacron'] = 722;
    t['rcaron'] = 333;
    t['ccedilla'] = 444;
    t['Zdotaccent'] = 611;
    t['Thorn'] = 556;
    t['Omacron'] = 722;
    t['Racute'] = 667;
    t['Sacute'] = 556;
    t['dcaron'] = 588;
    t['Umacron'] = 722;
    t['uring'] = 500;
    t['threesuperior'] = 300;
    t['Ograve'] = 722;
    t['Agrave'] = 722;
    t['Abreve'] = 722;
    t['multiply'] = 564;
    t['uacute'] = 500;
    t['Tcaron'] = 611;
    t['partialdiff'] = 476;
    t['ydieresis'] = 500;
    t['Nacute'] = 722;
    t['icircumflex'] = 278;
    t['Ecircumflex'] = 611;
    t['adieresis'] = 444;
    t['edieresis'] = 444;
    t['cacute'] = 444;
    t['nacute'] = 500;
    t['umacron'] = 500;
    t['Ncaron'] = 722;
    t['Iacute'] = 333;
    t['plusminus'] = 564;
    t['brokenbar'] = 200;
    t['registered'] = 760;
    t['Gbreve'] = 722;
    t['Idotaccent'] = 333;
    t['summation'] = 600;
    t['Egrave'] = 611;
    t['racute'] = 333;
    t['omacron'] = 500;
    t['Zacute'] = 611;
    t['Zcaron'] = 611;
    t['greaterequal'] = 549;
    t['Eth'] = 722;
    t['Ccedilla'] = 667;
    t['lcommaaccent'] = 278;
    t['tcaron'] = 326;
    t['eogonek'] = 444;
    t['Uogonek'] = 722;
    t['Aacute'] = 722;
    t['Adieresis'] = 722;
    t['egrave'] = 444;
    t['zacute'] = 444;
    t['iogonek'] = 278;
    t['Oacute'] = 722;
    t['oacute'] = 500;
    t['amacron'] = 444;
    t['sacute'] = 389;
    t['idieresis'] = 278;
    t['Ocircumflex'] = 722;
    t['Ugrave'] = 722;
    t['Delta'] = 612;
    t['thorn'] = 500;
    t['twosuperior'] = 300;
    t['Odieresis'] = 722;
    t['mu'] = 500;
    t['igrave'] = 278;
    t['ohungarumlaut'] = 500;
    t['Eogonek'] = 611;
    t['dcroat'] = 500;
    t['threequarters'] = 750;
    t['Scedilla'] = 556;
    t['lcaron'] = 344;
    t['Kcommaaccent'] = 722;
    t['Lacute'] = 611;
    t['trademark'] = 980;
    t['edotaccent'] = 444;
    t['Igrave'] = 333;
    t['Imacron'] = 333;
    t['Lcaron'] = 611;
    t['onehalf'] = 750;
    t['lessequal'] = 549;
    t['ocircumflex'] = 500;
    t['ntilde'] = 500;
    t['Uhungarumlaut'] = 722;
    t['Eacute'] = 611;
    t['emacron'] = 444;
    t['gbreve'] = 500;
    t['onequarter'] = 750;
    t['Scaron'] = 556;
    t['Scommaaccent'] = 556;
    t['Ohungarumlaut'] = 722;
    t['degree'] = 400;
    t['ograve'] = 500;
    t['Ccaron'] = 667;
    t['ugrave'] = 500;
    t['radical'] = 453;
    t['Dcaron'] = 722;
    t['rcommaaccent'] = 333;
    t['Ntilde'] = 722;
    t['otilde'] = 500;
    t['Rcommaaccent'] = 667;
    t['Lcommaaccent'] = 611;
    t['Atilde'] = 722;
    t['Aogonek'] = 722;
    t['Aring'] = 722;
    t['Otilde'] = 722;
    t['zdotaccent'] = 444;
    t['Ecaron'] = 611;
    t['Iogonek'] = 333;
    t['kcommaaccent'] = 500;
    t['minus'] = 564;
    t['Icircumflex'] = 333;
    t['ncaron'] = 500;
    t['tcommaaccent'] = 278;
    t['logicalnot'] = 564;
    t['odieresis'] = 500;
    t['udieresis'] = 500;
    t['notequal'] = 549;
    t['gcommaaccent'] = 500;
    t['eth'] = 500;
    t['zcaron'] = 444;
    t['ncommaaccent'] = 500;
    t['onesuperior'] = 300;
    t['imacron'] = 278;
    t['Euro'] = 500;
  });
  t['Times-Bold'] = (0, _util.getLookupTableFactory)(function (t) {
    t['space'] = 250;
    t['exclam'] = 333;
    t['quotedbl'] = 555;
    t['numbersign'] = 500;
    t['dollar'] = 500;
    t['percent'] = 1000;
    t['ampersand'] = 833;
    t['quoteright'] = 333;
    t['parenleft'] = 333;
    t['parenright'] = 333;
    t['asterisk'] = 500;
    t['plus'] = 570;
    t['comma'] = 250;
    t['hyphen'] = 333;
    t['period'] = 250;
    t['slash'] = 278;
    t['zero'] = 500;
    t['one'] = 500;
    t['two'] = 500;
    t['three'] = 500;
    t['four'] = 500;
    t['five'] = 500;
    t['six'] = 500;
    t['seven'] = 500;
    t['eight'] = 500;
    t['nine'] = 500;
    t['colon'] = 333;
    t['semicolon'] = 333;
    t['less'] = 570;
    t['equal'] = 570;
    t['greater'] = 570;
    t['question'] = 500;
    t['at'] = 930;
    t['A'] = 722;
    t['B'] = 667;
    t['C'] = 722;
    t['D'] = 722;
    t['E'] = 667;
    t['F'] = 611;
    t['G'] = 778;
    t['H'] = 778;
    t['I'] = 389;
    t['J'] = 500;
    t['K'] = 778;
    t['L'] = 667;
    t['M'] = 944;
    t['N'] = 722;
    t['O'] = 778;
    t['P'] = 611;
    t['Q'] = 778;
    t['R'] = 722;
    t['S'] = 556;
    t['T'] = 667;
    t['U'] = 722;
    t['V'] = 722;
    t['W'] = 1000;
    t['X'] = 722;
    t['Y'] = 722;
    t['Z'] = 667;
    t['bracketleft'] = 333;
    t['backslash'] = 278;
    t['bracketright'] = 333;
    t['asciicircum'] = 581;
    t['underscore'] = 500;
    t['quoteleft'] = 333;
    t['a'] = 500;
    t['b'] = 556;
    t['c'] = 444;
    t['d'] = 556;
    t['e'] = 444;
    t['f'] = 333;
    t['g'] = 500;
    t['h'] = 556;
    t['i'] = 278;
    t['j'] = 333;
    t['k'] = 556;
    t['l'] = 278;
    t['m'] = 833;
    t['n'] = 556;
    t['o'] = 500;
    t['p'] = 556;
    t['q'] = 556;
    t['r'] = 444;
    t['s'] = 389;
    t['t'] = 333;
    t['u'] = 556;
    t['v'] = 500;
    t['w'] = 722;
    t['x'] = 500;
    t['y'] = 500;
    t['z'] = 444;
    t['braceleft'] = 394;
    t['bar'] = 220;
    t['braceright'] = 394;
    t['asciitilde'] = 520;
    t['exclamdown'] = 333;
    t['cent'] = 500;
    t['sterling'] = 500;
    t['fraction'] = 167;
    t['yen'] = 500;
    t['florin'] = 500;
    t['section'] = 500;
    t['currency'] = 500;
    t['quotesingle'] = 278;
    t['quotedblleft'] = 500;
    t['guillemotleft'] = 500;
    t['guilsinglleft'] = 333;
    t['guilsinglright'] = 333;
    t['fi'] = 556;
    t['fl'] = 556;
    t['endash'] = 500;
    t['dagger'] = 500;
    t['daggerdbl'] = 500;
    t['periodcentered'] = 250;
    t['paragraph'] = 540;
    t['bullet'] = 350;
    t['quotesinglbase'] = 333;
    t['quotedblbase'] = 500;
    t['quotedblright'] = 500;
    t['guillemotright'] = 500;
    t['ellipsis'] = 1000;
    t['perthousand'] = 1000;
    t['questiondown'] = 500;
    t['grave'] = 333;
    t['acute'] = 333;
    t['circumflex'] = 333;
    t['tilde'] = 333;
    t['macron'] = 333;
    t['breve'] = 333;
    t['dotaccent'] = 333;
    t['dieresis'] = 333;
    t['ring'] = 333;
    t['cedilla'] = 333;
    t['hungarumlaut'] = 333;
    t['ogonek'] = 333;
    t['caron'] = 333;
    t['emdash'] = 1000;
    t['AE'] = 1000;
    t['ordfeminine'] = 300;
    t['Lslash'] = 667;
    t['Oslash'] = 778;
    t['OE'] = 1000;
    t['ordmasculine'] = 330;
    t['ae'] = 722;
    t['dotlessi'] = 278;
    t['lslash'] = 278;
    t['oslash'] = 500;
    t['oe'] = 722;
    t['germandbls'] = 556;
    t['Idieresis'] = 389;
    t['eacute'] = 444;
    t['abreve'] = 500;
    t['uhungarumlaut'] = 556;
    t['ecaron'] = 444;
    t['Ydieresis'] = 722;
    t['divide'] = 570;
    t['Yacute'] = 722;
    t['Acircumflex'] = 722;
    t['aacute'] = 500;
    t['Ucircumflex'] = 722;
    t['yacute'] = 500;
    t['scommaaccent'] = 389;
    t['ecircumflex'] = 444;
    t['Uring'] = 722;
    t['Udieresis'] = 722;
    t['aogonek'] = 500;
    t['Uacute'] = 722;
    t['uogonek'] = 556;
    t['Edieresis'] = 667;
    t['Dcroat'] = 722;
    t['commaaccent'] = 250;
    t['copyright'] = 747;
    t['Emacron'] = 667;
    t['ccaron'] = 444;
    t['aring'] = 500;
    t['Ncommaaccent'] = 722;
    t['lacute'] = 278;
    t['agrave'] = 500;
    t['Tcommaaccent'] = 667;
    t['Cacute'] = 722;
    t['atilde'] = 500;
    t['Edotaccent'] = 667;
    t['scaron'] = 389;
    t['scedilla'] = 389;
    t['iacute'] = 278;
    t['lozenge'] = 494;
    t['Rcaron'] = 722;
    t['Gcommaaccent'] = 778;
    t['ucircumflex'] = 556;
    t['acircumflex'] = 500;
    t['Amacron'] = 722;
    t['rcaron'] = 444;
    t['ccedilla'] = 444;
    t['Zdotaccent'] = 667;
    t['Thorn'] = 611;
    t['Omacron'] = 778;
    t['Racute'] = 722;
    t['Sacute'] = 556;
    t['dcaron'] = 672;
    t['Umacron'] = 722;
    t['uring'] = 556;
    t['threesuperior'] = 300;
    t['Ograve'] = 778;
    t['Agrave'] = 722;
    t['Abreve'] = 722;
    t['multiply'] = 570;
    t['uacute'] = 556;
    t['Tcaron'] = 667;
    t['partialdiff'] = 494;
    t['ydieresis'] = 500;
    t['Nacute'] = 722;
    t['icircumflex'] = 278;
    t['Ecircumflex'] = 667;
    t['adieresis'] = 500;
    t['edieresis'] = 444;
    t['cacute'] = 444;
    t['nacute'] = 556;
    t['umacron'] = 556;
    t['Ncaron'] = 722;
    t['Iacute'] = 389;
    t['plusminus'] = 570;
    t['brokenbar'] = 220;
    t['registered'] = 747;
    t['Gbreve'] = 778;
    t['Idotaccent'] = 389;
    t['summation'] = 600;
    t['Egrave'] = 667;
    t['racute'] = 444;
    t['omacron'] = 500;
    t['Zacute'] = 667;
    t['Zcaron'] = 667;
    t['greaterequal'] = 549;
    t['Eth'] = 722;
    t['Ccedilla'] = 722;
    t['lcommaaccent'] = 278;
    t['tcaron'] = 416;
    t['eogonek'] = 444;
    t['Uogonek'] = 722;
    t['Aacute'] = 722;
    t['Adieresis'] = 722;
    t['egrave'] = 444;
    t['zacute'] = 444;
    t['iogonek'] = 278;
    t['Oacute'] = 778;
    t['oacute'] = 500;
    t['amacron'] = 500;
    t['sacute'] = 389;
    t['idieresis'] = 278;
    t['Ocircumflex'] = 778;
    t['Ugrave'] = 722;
    t['Delta'] = 612;
    t['thorn'] = 556;
    t['twosuperior'] = 300;
    t['Odieresis'] = 778;
    t['mu'] = 556;
    t['igrave'] = 278;
    t['ohungarumlaut'] = 500;
    t['Eogonek'] = 667;
    t['dcroat'] = 556;
    t['threequarters'] = 750;
    t['Scedilla'] = 556;
    t['lcaron'] = 394;
    t['Kcommaaccent'] = 778;
    t['Lacute'] = 667;
    t['trademark'] = 1000;
    t['edotaccent'] = 444;
    t['Igrave'] = 389;
    t['Imacron'] = 389;
    t['Lcaron'] = 667;
    t['onehalf'] = 750;
    t['lessequal'] = 549;
    t['ocircumflex'] = 500;
    t['ntilde'] = 556;
    t['Uhungarumlaut'] = 722;
    t['Eacute'] = 667;
    t['emacron'] = 444;
    t['gbreve'] = 500;
    t['onequarter'] = 750;
    t['Scaron'] = 556;
    t['Scommaaccent'] = 556;
    t['Ohungarumlaut'] = 778;
    t['degree'] = 400;
    t['ograve'] = 500;
    t['Ccaron'] = 722;
    t['ugrave'] = 556;
    t['radical'] = 549;
    t['Dcaron'] = 722;
    t['rcommaaccent'] = 444;
    t['Ntilde'] = 722;
    t['otilde'] = 500;
    t['Rcommaaccent'] = 722;
    t['Lcommaaccent'] = 667;
    t['Atilde'] = 722;
    t['Aogonek'] = 722;
    t['Aring'] = 722;
    t['Otilde'] = 778;
    t['zdotaccent'] = 444;
    t['Ecaron'] = 667;
    t['Iogonek'] = 389;
    t['kcommaaccent'] = 556;
    t['minus'] = 570;
    t['Icircumflex'] = 389;
    t['ncaron'] = 556;
    t['tcommaaccent'] = 333;
    t['logicalnot'] = 570;
    t['odieresis'] = 500;
    t['udieresis'] = 556;
    t['notequal'] = 549;
    t['gcommaaccent'] = 500;
    t['eth'] = 500;
    t['zcaron'] = 444;
    t['ncommaaccent'] = 556;
    t['onesuperior'] = 300;
    t['imacron'] = 278;
    t['Euro'] = 500;
  });
  t['Times-BoldItalic'] = (0, _util.getLookupTableFactory)(function (t) {
    t['space'] = 250;
    t['exclam'] = 389;
    t['quotedbl'] = 555;
    t['numbersign'] = 500;
    t['dollar'] = 500;
    t['percent'] = 833;
    t['ampersand'] = 778;
    t['quoteright'] = 333;
    t['parenleft'] = 333;
    t['parenright'] = 333;
    t['asterisk'] = 500;
    t['plus'] = 570;
    t['comma'] = 250;
    t['hyphen'] = 333;
    t['period'] = 250;
    t['slash'] = 278;
    t['zero'] = 500;
    t['one'] = 500;
    t['two'] = 500;
    t['three'] = 500;
    t['four'] = 500;
    t['five'] = 500;
    t['six'] = 500;
    t['seven'] = 500;
    t['eight'] = 500;
    t['nine'] = 500;
    t['colon'] = 333;
    t['semicolon'] = 333;
    t['less'] = 570;
    t['equal'] = 570;
    t['greater'] = 570;
    t['question'] = 500;
    t['at'] = 832;
    t['A'] = 667;
    t['B'] = 667;
    t['C'] = 667;
    t['D'] = 722;
    t['E'] = 667;
    t['F'] = 667;
    t['G'] = 722;
    t['H'] = 778;
    t['I'] = 389;
    t['J'] = 500;
    t['K'] = 667;
    t['L'] = 611;
    t['M'] = 889;
    t['N'] = 722;
    t['O'] = 722;
    t['P'] = 611;
    t['Q'] = 722;
    t['R'] = 667;
    t['S'] = 556;
    t['T'] = 611;
    t['U'] = 722;
    t['V'] = 667;
    t['W'] = 889;
    t['X'] = 667;
    t['Y'] = 611;
    t['Z'] = 611;
    t['bracketleft'] = 333;
    t['backslash'] = 278;
    t['bracketright'] = 333;
    t['asciicircum'] = 570;
    t['underscore'] = 500;
    t['quoteleft'] = 333;
    t['a'] = 500;
    t['b'] = 500;
    t['c'] = 444;
    t['d'] = 500;
    t['e'] = 444;
    t['f'] = 333;
    t['g'] = 500;
    t['h'] = 556;
    t['i'] = 278;
    t['j'] = 278;
    t['k'] = 500;
    t['l'] = 278;
    t['m'] = 778;
    t['n'] = 556;
    t['o'] = 500;
    t['p'] = 500;
    t['q'] = 500;
    t['r'] = 389;
    t['s'] = 389;
    t['t'] = 278;
    t['u'] = 556;
    t['v'] = 444;
    t['w'] = 667;
    t['x'] = 500;
    t['y'] = 444;
    t['z'] = 389;
    t['braceleft'] = 348;
    t['bar'] = 220;
    t['braceright'] = 348;
    t['asciitilde'] = 570;
    t['exclamdown'] = 389;
    t['cent'] = 500;
    t['sterling'] = 500;
    t['fraction'] = 167;
    t['yen'] = 500;
    t['florin'] = 500;
    t['section'] = 500;
    t['currency'] = 500;
    t['quotesingle'] = 278;
    t['quotedblleft'] = 500;
    t['guillemotleft'] = 500;
    t['guilsinglleft'] = 333;
    t['guilsinglright'] = 333;
    t['fi'] = 556;
    t['fl'] = 556;
    t['endash'] = 500;
    t['dagger'] = 500;
    t['daggerdbl'] = 500;
    t['periodcentered'] = 250;
    t['paragraph'] = 500;
    t['bullet'] = 350;
    t['quotesinglbase'] = 333;
    t['quotedblbase'] = 500;
    t['quotedblright'] = 500;
    t['guillemotright'] = 500;
    t['ellipsis'] = 1000;
    t['perthousand'] = 1000;
    t['questiondown'] = 500;
    t['grave'] = 333;
    t['acute'] = 333;
    t['circumflex'] = 333;
    t['tilde'] = 333;
    t['macron'] = 333;
    t['breve'] = 333;
    t['dotaccent'] = 333;
    t['dieresis'] = 333;
    t['ring'] = 333;
    t['cedilla'] = 333;
    t['hungarumlaut'] = 333;
    t['ogonek'] = 333;
    t['caron'] = 333;
    t['emdash'] = 1000;
    t['AE'] = 944;
    t['ordfeminine'] = 266;
    t['Lslash'] = 611;
    t['Oslash'] = 722;
    t['OE'] = 944;
    t['ordmasculine'] = 300;
    t['ae'] = 722;
    t['dotlessi'] = 278;
    t['lslash'] = 278;
    t['oslash'] = 500;
    t['oe'] = 722;
    t['germandbls'] = 500;
    t['Idieresis'] = 389;
    t['eacute'] = 444;
    t['abreve'] = 500;
    t['uhungarumlaut'] = 556;
    t['ecaron'] = 444;
    t['Ydieresis'] = 611;
    t['divide'] = 570;
    t['Yacute'] = 611;
    t['Acircumflex'] = 667;
    t['aacute'] = 500;
    t['Ucircumflex'] = 722;
    t['yacute'] = 444;
    t['scommaaccent'] = 389;
    t['ecircumflex'] = 444;
    t['Uring'] = 722;
    t['Udieresis'] = 722;
    t['aogonek'] = 500;
    t['Uacute'] = 722;
    t['uogonek'] = 556;
    t['Edieresis'] = 667;
    t['Dcroat'] = 722;
    t['commaaccent'] = 250;
    t['copyright'] = 747;
    t['Emacron'] = 667;
    t['ccaron'] = 444;
    t['aring'] = 500;
    t['Ncommaaccent'] = 722;
    t['lacute'] = 278;
    t['agrave'] = 500;
    t['Tcommaaccent'] = 611;
    t['Cacute'] = 667;
    t['atilde'] = 500;
    t['Edotaccent'] = 667;
    t['scaron'] = 389;
    t['scedilla'] = 389;
    t['iacute'] = 278;
    t['lozenge'] = 494;
    t['Rcaron'] = 667;
    t['Gcommaaccent'] = 722;
    t['ucircumflex'] = 556;
    t['acircumflex'] = 500;
    t['Amacron'] = 667;
    t['rcaron'] = 389;
    t['ccedilla'] = 444;
    t['Zdotaccent'] = 611;
    t['Thorn'] = 611;
    t['Omacron'] = 722;
    t['Racute'] = 667;
    t['Sacute'] = 556;
    t['dcaron'] = 608;
    t['Umacron'] = 722;
    t['uring'] = 556;
    t['threesuperior'] = 300;
    t['Ograve'] = 722;
    t['Agrave'] = 667;
    t['Abreve'] = 667;
    t['multiply'] = 570;
    t['uacute'] = 556;
    t['Tcaron'] = 611;
    t['partialdiff'] = 494;
    t['ydieresis'] = 444;
    t['Nacute'] = 722;
    t['icircumflex'] = 278;
    t['Ecircumflex'] = 667;
    t['adieresis'] = 500;
    t['edieresis'] = 444;
    t['cacute'] = 444;
    t['nacute'] = 556;
    t['umacron'] = 556;
    t['Ncaron'] = 722;
    t['Iacute'] = 389;
    t['plusminus'] = 570;
    t['brokenbar'] = 220;
    t['registered'] = 747;
    t['Gbreve'] = 722;
    t['Idotaccent'] = 389;
    t['summation'] = 600;
    t['Egrave'] = 667;
    t['racute'] = 389;
    t['omacron'] = 500;
    t['Zacute'] = 611;
    t['Zcaron'] = 611;
    t['greaterequal'] = 549;
    t['Eth'] = 722;
    t['Ccedilla'] = 667;
    t['lcommaaccent'] = 278;
    t['tcaron'] = 366;
    t['eogonek'] = 444;
    t['Uogonek'] = 722;
    t['Aacute'] = 667;
    t['Adieresis'] = 667;
    t['egrave'] = 444;
    t['zacute'] = 389;
    t['iogonek'] = 278;
    t['Oacute'] = 722;
    t['oacute'] = 500;
    t['amacron'] = 500;
    t['sacute'] = 389;
    t['idieresis'] = 278;
    t['Ocircumflex'] = 722;
    t['Ugrave'] = 722;
    t['Delta'] = 612;
    t['thorn'] = 500;
    t['twosuperior'] = 300;
    t['Odieresis'] = 722;
    t['mu'] = 576;
    t['igrave'] = 278;
    t['ohungarumlaut'] = 500;
    t['Eogonek'] = 667;
    t['dcroat'] = 500;
    t['threequarters'] = 750;
    t['Scedilla'] = 556;
    t['lcaron'] = 382;
    t['Kcommaaccent'] = 667;
    t['Lacute'] = 611;
    t['trademark'] = 1000;
    t['edotaccent'] = 444;
    t['Igrave'] = 389;
    t['Imacron'] = 389;
    t['Lcaron'] = 611;
    t['onehalf'] = 750;
    t['lessequal'] = 549;
    t['ocircumflex'] = 500;
    t['ntilde'] = 556;
    t['Uhungarumlaut'] = 722;
    t['Eacute'] = 667;
    t['emacron'] = 444;
    t['gbreve'] = 500;
    t['onequarter'] = 750;
    t['Scaron'] = 556;
    t['Scommaaccent'] = 556;
    t['Ohungarumlaut'] = 722;
    t['degree'] = 400;
    t['ograve'] = 500;
    t['Ccaron'] = 667;
    t['ugrave'] = 556;
    t['radical'] = 549;
    t['Dcaron'] = 722;
    t['rcommaaccent'] = 389;
    t['Ntilde'] = 722;
    t['otilde'] = 500;
    t['Rcommaaccent'] = 667;
    t['Lcommaaccent'] = 611;
    t['Atilde'] = 667;
    t['Aogonek'] = 667;
    t['Aring'] = 667;
    t['Otilde'] = 722;
    t['zdotaccent'] = 389;
    t['Ecaron'] = 667;
    t['Iogonek'] = 389;
    t['kcommaaccent'] = 500;
    t['minus'] = 606;
    t['Icircumflex'] = 389;
    t['ncaron'] = 556;
    t['tcommaaccent'] = 278;
    t['logicalnot'] = 606;
    t['odieresis'] = 500;
    t['udieresis'] = 556;
    t['notequal'] = 549;
    t['gcommaaccent'] = 500;
    t['eth'] = 500;
    t['zcaron'] = 389;
    t['ncommaaccent'] = 556;
    t['onesuperior'] = 300;
    t['imacron'] = 278;
    t['Euro'] = 500;
  });
  t['Times-Italic'] = (0, _util.getLookupTableFactory)(function (t) {
    t['space'] = 250;
    t['exclam'] = 333;
    t['quotedbl'] = 420;
    t['numbersign'] = 500;
    t['dollar'] = 500;
    t['percent'] = 833;
    t['ampersand'] = 778;
    t['quoteright'] = 333;
    t['parenleft'] = 333;
    t['parenright'] = 333;
    t['asterisk'] = 500;
    t['plus'] = 675;
    t['comma'] = 250;
    t['hyphen'] = 333;
    t['period'] = 250;
    t['slash'] = 278;
    t['zero'] = 500;
    t['one'] = 500;
    t['two'] = 500;
    t['three'] = 500;
    t['four'] = 500;
    t['five'] = 500;
    t['six'] = 500;
    t['seven'] = 500;
    t['eight'] = 500;
    t['nine'] = 500;
    t['colon'] = 333;
    t['semicolon'] = 333;
    t['less'] = 675;
    t['equal'] = 675;
    t['greater'] = 675;
    t['question'] = 500;
    t['at'] = 920;
    t['A'] = 611;
    t['B'] = 611;
    t['C'] = 667;
    t['D'] = 722;
    t['E'] = 611;
    t['F'] = 611;
    t['G'] = 722;
    t['H'] = 722;
    t['I'] = 333;
    t['J'] = 444;
    t['K'] = 667;
    t['L'] = 556;
    t['M'] = 833;
    t['N'] = 667;
    t['O'] = 722;
    t['P'] = 611;
    t['Q'] = 722;
    t['R'] = 611;
    t['S'] = 500;
    t['T'] = 556;
    t['U'] = 722;
    t['V'] = 611;
    t['W'] = 833;
    t['X'] = 611;
    t['Y'] = 556;
    t['Z'] = 556;
    t['bracketleft'] = 389;
    t['backslash'] = 278;
    t['bracketright'] = 389;
    t['asciicircum'] = 422;
    t['underscore'] = 500;
    t['quoteleft'] = 333;
    t['a'] = 500;
    t['b'] = 500;
    t['c'] = 444;
    t['d'] = 500;
    t['e'] = 444;
    t['f'] = 278;
    t['g'] = 500;
    t['h'] = 500;
    t['i'] = 278;
    t['j'] = 278;
    t['k'] = 444;
    t['l'] = 278;
    t['m'] = 722;
    t['n'] = 500;
    t['o'] = 500;
    t['p'] = 500;
    t['q'] = 500;
    t['r'] = 389;
    t['s'] = 389;
    t['t'] = 278;
    t['u'] = 500;
    t['v'] = 444;
    t['w'] = 667;
    t['x'] = 444;
    t['y'] = 444;
    t['z'] = 389;
    t['braceleft'] = 400;
    t['bar'] = 275;
    t['braceright'] = 400;
    t['asciitilde'] = 541;
    t['exclamdown'] = 389;
    t['cent'] = 500;
    t['sterling'] = 500;
    t['fraction'] = 167;
    t['yen'] = 500;
    t['florin'] = 500;
    t['section'] = 500;
    t['currency'] = 500;
    t['quotesingle'] = 214;
    t['quotedblleft'] = 556;
    t['guillemotleft'] = 500;
    t['guilsinglleft'] = 333;
    t['guilsinglright'] = 333;
    t['fi'] = 500;
    t['fl'] = 500;
    t['endash'] = 500;
    t['dagger'] = 500;
    t['daggerdbl'] = 500;
    t['periodcentered'] = 250;
    t['paragraph'] = 523;
    t['bullet'] = 350;
    t['quotesinglbase'] = 333;
    t['quotedblbase'] = 556;
    t['quotedblright'] = 556;
    t['guillemotright'] = 500;
    t['ellipsis'] = 889;
    t['perthousand'] = 1000;
    t['questiondown'] = 500;
    t['grave'] = 333;
    t['acute'] = 333;
    t['circumflex'] = 333;
    t['tilde'] = 333;
    t['macron'] = 333;
    t['breve'] = 333;
    t['dotaccent'] = 333;
    t['dieresis'] = 333;
    t['ring'] = 333;
    t['cedilla'] = 333;
    t['hungarumlaut'] = 333;
    t['ogonek'] = 333;
    t['caron'] = 333;
    t['emdash'] = 889;
    t['AE'] = 889;
    t['ordfeminine'] = 276;
    t['Lslash'] = 556;
    t['Oslash'] = 722;
    t['OE'] = 944;
    t['ordmasculine'] = 310;
    t['ae'] = 667;
    t['dotlessi'] = 278;
    t['lslash'] = 278;
    t['oslash'] = 500;
    t['oe'] = 667;
    t['germandbls'] = 500;
    t['Idieresis'] = 333;
    t['eacute'] = 444;
    t['abreve'] = 500;
    t['uhungarumlaut'] = 500;
    t['ecaron'] = 444;
    t['Ydieresis'] = 556;
    t['divide'] = 675;
    t['Yacute'] = 556;
    t['Acircumflex'] = 611;
    t['aacute'] = 500;
    t['Ucircumflex'] = 722;
    t['yacute'] = 444;
    t['scommaaccent'] = 389;
    t['ecircumflex'] = 444;
    t['Uring'] = 722;
    t['Udieresis'] = 722;
    t['aogonek'] = 500;
    t['Uacute'] = 722;
    t['uogonek'] = 500;
    t['Edieresis'] = 611;
    t['Dcroat'] = 722;
    t['commaaccent'] = 250;
    t['copyright'] = 760;
    t['Emacron'] = 611;
    t['ccaron'] = 444;
    t['aring'] = 500;
    t['Ncommaaccent'] = 667;
    t['lacute'] = 278;
    t['agrave'] = 500;
    t['Tcommaaccent'] = 556;
    t['Cacute'] = 667;
    t['atilde'] = 500;
    t['Edotaccent'] = 611;
    t['scaron'] = 389;
    t['scedilla'] = 389;
    t['iacute'] = 278;
    t['lozenge'] = 471;
    t['Rcaron'] = 611;
    t['Gcommaaccent'] = 722;
    t['ucircumflex'] = 500;
    t['acircumflex'] = 500;
    t['Amacron'] = 611;
    t['rcaron'] = 389;
    t['ccedilla'] = 444;
    t['Zdotaccent'] = 556;
    t['Thorn'] = 611;
    t['Omacron'] = 722;
    t['Racute'] = 611;
    t['Sacute'] = 500;
    t['dcaron'] = 544;
    t['Umacron'] = 722;
    t['uring'] = 500;
    t['threesuperior'] = 300;
    t['Ograve'] = 722;
    t['Agrave'] = 611;
    t['Abreve'] = 611;
    t['multiply'] = 675;
    t['uacute'] = 500;
    t['Tcaron'] = 556;
    t['partialdiff'] = 476;
    t['ydieresis'] = 444;
    t['Nacute'] = 667;
    t['icircumflex'] = 278;
    t['Ecircumflex'] = 611;
    t['adieresis'] = 500;
    t['edieresis'] = 444;
    t['cacute'] = 444;
    t['nacute'] = 500;
    t['umacron'] = 500;
    t['Ncaron'] = 667;
    t['Iacute'] = 333;
    t['plusminus'] = 675;
    t['brokenbar'] = 275;
    t['registered'] = 760;
    t['Gbreve'] = 722;
    t['Idotaccent'] = 333;
    t['summation'] = 600;
    t['Egrave'] = 611;
    t['racute'] = 389;
    t['omacron'] = 500;
    t['Zacute'] = 556;
    t['Zcaron'] = 556;
    t['greaterequal'] = 549;
    t['Eth'] = 722;
    t['Ccedilla'] = 667;
    t['lcommaaccent'] = 278;
    t['tcaron'] = 300;
    t['eogonek'] = 444;
    t['Uogonek'] = 722;
    t['Aacute'] = 611;
    t['Adieresis'] = 611;
    t['egrave'] = 444;
    t['zacute'] = 389;
    t['iogonek'] = 278;
    t['Oacute'] = 722;
    t['oacute'] = 500;
    t['amacron'] = 500;
    t['sacute'] = 389;
    t['idieresis'] = 278;
    t['Ocircumflex'] = 722;
    t['Ugrave'] = 722;
    t['Delta'] = 612;
    t['thorn'] = 500;
    t['twosuperior'] = 300;
    t['Odieresis'] = 722;
    t['mu'] = 500;
    t['igrave'] = 278;
    t['ohungarumlaut'] = 500;
    t['Eogonek'] = 611;
    t['dcroat'] = 500;
    t['threequarters'] = 750;
    t['Scedilla'] = 500;
    t['lcaron'] = 300;
    t['Kcommaaccent'] = 667;
    t['Lacute'] = 556;
    t['trademark'] = 980;
    t['edotaccent'] = 444;
    t['Igrave'] = 333;
    t['Imacron'] = 333;
    t['Lcaron'] = 611;
    t['onehalf'] = 750;
    t['lessequal'] = 549;
    t['ocircumflex'] = 500;
    t['ntilde'] = 500;
    t['Uhungarumlaut'] = 722;
    t['Eacute'] = 611;
    t['emacron'] = 444;
    t['gbreve'] = 500;
    t['onequarter'] = 750;
    t['Scaron'] = 500;
    t['Scommaaccent'] = 500;
    t['Ohungarumlaut'] = 722;
    t['degree'] = 400;
    t['ograve'] = 500;
    t['Ccaron'] = 667;
    t['ugrave'] = 500;
    t['radical'] = 453;
    t['Dcaron'] = 722;
    t['rcommaaccent'] = 389;
    t['Ntilde'] = 667;
    t['otilde'] = 500;
    t['Rcommaaccent'] = 611;
    t['Lcommaaccent'] = 556;
    t['Atilde'] = 611;
    t['Aogonek'] = 611;
    t['Aring'] = 611;
    t['Otilde'] = 722;
    t['zdotaccent'] = 389;
    t['Ecaron'] = 611;
    t['Iogonek'] = 333;
    t['kcommaaccent'] = 444;
    t['minus'] = 675;
    t['Icircumflex'] = 333;
    t['ncaron'] = 500;
    t['tcommaaccent'] = 278;
    t['logicalnot'] = 675;
    t['odieresis'] = 500;
    t['udieresis'] = 500;
    t['notequal'] = 549;
    t['gcommaaccent'] = 500;
    t['eth'] = 500;
    t['zcaron'] = 389;
    t['ncommaaccent'] = 500;
    t['onesuperior'] = 300;
    t['imacron'] = 278;
    t['Euro'] = 500;
  });
  t['ZapfDingbats'] = (0, _util.getLookupTableFactory)(function (t) {
    t['space'] = 278;
    t['a1'] = 974;
    t['a2'] = 961;
    t['a202'] = 974;
    t['a3'] = 980;
    t['a4'] = 719;
    t['a5'] = 789;
    t['a119'] = 790;
    t['a118'] = 791;
    t['a117'] = 690;
    t['a11'] = 960;
    t['a12'] = 939;
    t['a13'] = 549;
    t['a14'] = 855;
    t['a15'] = 911;
    t['a16'] = 933;
    t['a105'] = 911;
    t['a17'] = 945;
    t['a18'] = 974;
    t['a19'] = 755;
    t['a20'] = 846;
    t['a21'] = 762;
    t['a22'] = 761;
    t['a23'] = 571;
    t['a24'] = 677;
    t['a25'] = 763;
    t['a26'] = 760;
    t['a27'] = 759;
    t['a28'] = 754;
    t['a6'] = 494;
    t['a7'] = 552;
    t['a8'] = 537;
    t['a9'] = 577;
    t['a10'] = 692;
    t['a29'] = 786;
    t['a30'] = 788;
    t['a31'] = 788;
    t['a32'] = 790;
    t['a33'] = 793;
    t['a34'] = 794;
    t['a35'] = 816;
    t['a36'] = 823;
    t['a37'] = 789;
    t['a38'] = 841;
    t['a39'] = 823;
    t['a40'] = 833;
    t['a41'] = 816;
    t['a42'] = 831;
    t['a43'] = 923;
    t['a44'] = 744;
    t['a45'] = 723;
    t['a46'] = 749;
    t['a47'] = 790;
    t['a48'] = 792;
    t['a49'] = 695;
    t['a50'] = 776;
    t['a51'] = 768;
    t['a52'] = 792;
    t['a53'] = 759;
    t['a54'] = 707;
    t['a55'] = 708;
    t['a56'] = 682;
    t['a57'] = 701;
    t['a58'] = 826;
    t['a59'] = 815;
    t['a60'] = 789;
    t['a61'] = 789;
    t['a62'] = 707;
    t['a63'] = 687;
    t['a64'] = 696;
    t['a65'] = 689;
    t['a66'] = 786;
    t['a67'] = 787;
    t['a68'] = 713;
    t['a69'] = 791;
    t['a70'] = 785;
    t['a71'] = 791;
    t['a72'] = 873;
    t['a73'] = 761;
    t['a74'] = 762;
    t['a203'] = 762;
    t['a75'] = 759;
    t['a204'] = 759;
    t['a76'] = 892;
    t['a77'] = 892;
    t['a78'] = 788;
    t['a79'] = 784;
    t['a81'] = 438;
    t['a82'] = 138;
    t['a83'] = 277;
    t['a84'] = 415;
    t['a97'] = 392;
    t['a98'] = 392;
    t['a99'] = 668;
    t['a100'] = 668;
    t['a89'] = 390;
    t['a90'] = 390;
    t['a93'] = 317;
    t['a94'] = 317;
    t['a91'] = 276;
    t['a92'] = 276;
    t['a205'] = 509;
    t['a85'] = 509;
    t['a206'] = 410;
    t['a86'] = 410;
    t['a87'] = 234;
    t['a88'] = 234;
    t['a95'] = 334;
    t['a96'] = 334;
    t['a101'] = 732;
    t['a102'] = 544;
    t['a103'] = 544;
    t['a104'] = 910;
    t['a106'] = 667;
    t['a107'] = 760;
    t['a108'] = 760;
    t['a112'] = 776;
    t['a111'] = 595;
    t['a110'] = 694;
    t['a109'] = 626;
    t['a120'] = 788;
    t['a121'] = 788;
    t['a122'] = 788;
    t['a123'] = 788;
    t['a124'] = 788;
    t['a125'] = 788;
    t['a126'] = 788;
    t['a127'] = 788;
    t['a128'] = 788;
    t['a129'] = 788;
    t['a130'] = 788;
    t['a131'] = 788;
    t['a132'] = 788;
    t['a133'] = 788;
    t['a134'] = 788;
    t['a135'] = 788;
    t['a136'] = 788;
    t['a137'] = 788;
    t['a138'] = 788;
    t['a139'] = 788;
    t['a140'] = 788;
    t['a141'] = 788;
    t['a142'] = 788;
    t['a143'] = 788;
    t['a144'] = 788;
    t['a145'] = 788;
    t['a146'] = 788;
    t['a147'] = 788;
    t['a148'] = 788;
    t['a149'] = 788;
    t['a150'] = 788;
    t['a151'] = 788;
    t['a152'] = 788;
    t['a153'] = 788;
    t['a154'] = 788;
    t['a155'] = 788;
    t['a156'] = 788;
    t['a157'] = 788;
    t['a158'] = 788;
    t['a159'] = 788;
    t['a160'] = 894;
    t['a161'] = 838;
    t['a163'] = 1016;
    t['a164'] = 458;
    t['a196'] = 748;
    t['a165'] = 924;
    t['a192'] = 748;
    t['a166'] = 918;
    t['a167'] = 927;
    t['a168'] = 928;
    t['a169'] = 928;
    t['a170'] = 834;
    t['a171'] = 873;
    t['a172'] = 828;
    t['a173'] = 924;
    t['a162'] = 924;
    t['a174'] = 917;
    t['a175'] = 930;
    t['a176'] = 931;
    t['a177'] = 463;
    t['a178'] = 883;
    t['a179'] = 836;
    t['a193'] = 836;
    t['a180'] = 867;
    t['a199'] = 867;
    t['a181'] = 696;
    t['a200'] = 696;
    t['a182'] = 874;
    t['a201'] = 874;
    t['a183'] = 760;
    t['a184'] = 946;
    t['a197'] = 771;
    t['a185'] = 865;
    t['a194'] = 771;
    t['a198'] = 888;
    t['a186'] = 967;
    t['a195'] = 888;
    t['a187'] = 831;
    t['a188'] = 873;
    t['a189'] = 927;
    t['a190'] = 970;
    t['a191'] = 918;
  });
});
exports.getMetrics = getMetrics;

/***/ }),
/* 41 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.PostScriptParser = exports.PostScriptLexer = undefined;

var _util = __w_pdfjs_require__(0);

var _primitives = __w_pdfjs_require__(1);

var PostScriptParser = function PostScriptParserClosure() {
  function PostScriptParser(lexer) {
    this.lexer = lexer;
    this.operators = [];
    this.token = null;
    this.prev = null;
  }
  PostScriptParser.prototype = {
    nextToken: function PostScriptParser_nextToken() {
      this.prev = this.token;
      this.token = this.lexer.getToken();
    },
    accept: function PostScriptParser_accept(type) {
      if (this.token.type === type) {
        this.nextToken();
        return true;
      }
      return false;
    },
    expect: function PostScriptParser_expect(type) {
      if (this.accept(type)) {
        return true;
      }
      throw new _util.FormatError(`Unexpected symbol: found ${this.token.type} expected ${type}.`);
    },
    parse: function PostScriptParser_parse() {
      this.nextToken();
      this.expect(PostScriptTokenTypes.LBRACE);
      this.parseBlock();
      this.expect(PostScriptTokenTypes.RBRACE);
      return this.operators;
    },
    parseBlock: function PostScriptParser_parseBlock() {
      while (true) {
        if (this.accept(PostScriptTokenTypes.NUMBER)) {
          this.operators.push(this.prev.value);
        } else if (this.accept(PostScriptTokenTypes.OPERATOR)) {
          this.operators.push(this.prev.value);
        } else if (this.accept(PostScriptTokenTypes.LBRACE)) {
          this.parseCondition();
        } else {
          return;
        }
      }
    },
    parseCondition: function PostScriptParser_parseCondition() {
      var conditionLocation = this.operators.length;
      this.operators.push(null, null);
      this.parseBlock();
      this.expect(PostScriptTokenTypes.RBRACE);
      if (this.accept(PostScriptTokenTypes.IF)) {
        this.operators[conditionLocation] = this.operators.length;
        this.operators[conditionLocation + 1] = 'jz';
      } else if (this.accept(PostScriptTokenTypes.LBRACE)) {
        var jumpLocation = this.operators.length;
        this.operators.push(null, null);
        var endOfTrue = this.operators.length;
        this.parseBlock();
        this.expect(PostScriptTokenTypes.RBRACE);
        this.expect(PostScriptTokenTypes.IFELSE);
        this.operators[jumpLocation] = this.operators.length;
        this.operators[jumpLocation + 1] = 'j';
        this.operators[conditionLocation] = endOfTrue;
        this.operators[conditionLocation + 1] = 'jz';
      } else {
        throw new _util.FormatError('PS Function: error parsing conditional.');
      }
    }
  };
  return PostScriptParser;
}();
var PostScriptTokenTypes = {
  LBRACE: 0,
  RBRACE: 1,
  NUMBER: 2,
  OPERATOR: 3,
  IF: 4,
  IFELSE: 5
};
var PostScriptToken = function PostScriptTokenClosure() {
  function PostScriptToken(type, value) {
    this.type = type;
    this.value = value;
  }
  var opCache = Object.create(null);
  PostScriptToken.getOperator = function PostScriptToken_getOperator(op) {
    var opValue = opCache[op];
    if (opValue) {
      return opValue;
    }
    return opCache[op] = new PostScriptToken(PostScriptTokenTypes.OPERATOR, op);
  };
  PostScriptToken.LBRACE = new PostScriptToken(PostScriptTokenTypes.LBRACE, '{');
  PostScriptToken.RBRACE = new PostScriptToken(PostScriptTokenTypes.RBRACE, '}');
  PostScriptToken.IF = new PostScriptToken(PostScriptTokenTypes.IF, 'IF');
  PostScriptToken.IFELSE = new PostScriptToken(PostScriptTokenTypes.IFELSE, 'IFELSE');
  return PostScriptToken;
}();
var PostScriptLexer = function PostScriptLexerClosure() {
  function PostScriptLexer(stream) {
    this.stream = stream;
    this.nextChar();
    this.strBuf = [];
  }
  PostScriptLexer.prototype = {
    nextChar: function PostScriptLexer_nextChar() {
      return this.currentChar = this.stream.getByte();
    },
    getToken: function PostScriptLexer_getToken() {
      var comment = false;
      var ch = this.currentChar;
      while (true) {
        if (ch < 0) {
          return _primitives.EOF;
        }
        if (comment) {
          if (ch === 0x0A || ch === 0x0D) {
            comment = false;
          }
        } else if (ch === 0x25) {
          comment = true;
        } else if (!(0, _util.isSpace)(ch)) {
          break;
        }
        ch = this.nextChar();
      }
      switch (ch | 0) {
        case 0x30:
        case 0x31:
        case 0x32:
        case 0x33:
        case 0x34:
        case 0x35:
        case 0x36:
        case 0x37:
        case 0x38:
        case 0x39:
        case 0x2B:
        case 0x2D:
        case 0x2E:
          return new PostScriptToken(PostScriptTokenTypes.NUMBER, this.getNumber());
        case 0x7B:
          this.nextChar();
          return PostScriptToken.LBRACE;
        case 0x7D:
          this.nextChar();
          return PostScriptToken.RBRACE;
      }
      var strBuf = this.strBuf;
      strBuf.length = 0;
      strBuf[0] = String.fromCharCode(ch);
      while ((ch = this.nextChar()) >= 0 && (ch >= 0x41 && ch <= 0x5A || ch >= 0x61 && ch <= 0x7A)) {
        strBuf.push(String.fromCharCode(ch));
      }
      var str = strBuf.join('');
      switch (str.toLowerCase()) {
        case 'if':
          return PostScriptToken.IF;
        case 'ifelse':
          return PostScriptToken.IFELSE;
        default:
          return PostScriptToken.getOperator(str);
      }
    },
    getNumber: function PostScriptLexer_getNumber() {
      var ch = this.currentChar;
      var strBuf = this.strBuf;
      strBuf.length = 0;
      strBuf[0] = String.fromCharCode(ch);
      while ((ch = this.nextChar()) >= 0) {
        if (ch >= 0x30 && ch <= 0x39 || ch === 0x2D || ch === 0x2E) {
          strBuf.push(String.fromCharCode(ch));
        } else {
          break;
        }
      }
      var value = parseFloat(strBuf.join(''));
      if (isNaN(value)) {
        throw new _util.FormatError(`Invalid floating point number: ${value}`);
      }
      return value;
    }
  };
  return PostScriptLexer;
}();
exports.PostScriptLexer = PostScriptLexer;
exports.PostScriptParser = PostScriptParser;

/***/ }),
/* 42 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.MurmurHash3_64 = undefined;

var _util = __w_pdfjs_require__(0);

var MurmurHash3_64 = function MurmurHash3_64Closure(seed) {
  var MASK_HIGH = 0xffff0000;
  var MASK_LOW = 0xffff;
  function MurmurHash3_64(seed) {
    var SEED = 0xc3d2e1f0;
    this.h1 = seed ? seed & 0xffffffff : SEED;
    this.h2 = seed ? seed & 0xffffffff : SEED;
  }
  MurmurHash3_64.prototype = {
    update: function MurmurHash3_64_update(input) {
      let data, length;
      if ((0, _util.isString)(input)) {
        data = new Uint8Array(input.length * 2);
        length = 0;
        for (let i = 0, ii = input.length; i < ii; i++) {
          var code = input.charCodeAt(i);
          if (code <= 0xff) {
            data[length++] = code;
          } else {
            data[length++] = code >>> 8;
            data[length++] = code & 0xff;
          }
        }
      } else if ((0, _util.isArrayBuffer)(input)) {
        data = input;
        length = data.byteLength;
      } else {
        throw new Error('Wrong data format in MurmurHash3_64_update. ' + 'Input must be a string or array.');
      }
      var blockCounts = length >> 2;
      var tailLength = length - blockCounts * 4;
      var dataUint32 = new Uint32Array(data.buffer, 0, blockCounts);
      var k1 = 0;
      var k2 = 0;
      var h1 = this.h1;
      var h2 = this.h2;
      var C1 = 0xcc9e2d51;
      var C2 = 0x1b873593;
      var C1_LOW = C1 & MASK_LOW;
      var C2_LOW = C2 & MASK_LOW;
      for (let i = 0; i < blockCounts; i++) {
        if (i & 1) {
          k1 = dataUint32[i];
          k1 = k1 * C1 & MASK_HIGH | k1 * C1_LOW & MASK_LOW;
          k1 = k1 << 15 | k1 >>> 17;
          k1 = k1 * C2 & MASK_HIGH | k1 * C2_LOW & MASK_LOW;
          h1 ^= k1;
          h1 = h1 << 13 | h1 >>> 19;
          h1 = h1 * 5 + 0xe6546b64;
        } else {
          k2 = dataUint32[i];
          k2 = k2 * C1 & MASK_HIGH | k2 * C1_LOW & MASK_LOW;
          k2 = k2 << 15 | k2 >>> 17;
          k2 = k2 * C2 & MASK_HIGH | k2 * C2_LOW & MASK_LOW;
          h2 ^= k2;
          h2 = h2 << 13 | h2 >>> 19;
          h2 = h2 * 5 + 0xe6546b64;
        }
      }
      k1 = 0;
      switch (tailLength) {
        case 3:
          k1 ^= data[blockCounts * 4 + 2] << 16;
        case 2:
          k1 ^= data[blockCounts * 4 + 1] << 8;
        case 1:
          k1 ^= data[blockCounts * 4];
          k1 = k1 * C1 & MASK_HIGH | k1 * C1_LOW & MASK_LOW;
          k1 = k1 << 15 | k1 >>> 17;
          k1 = k1 * C2 & MASK_HIGH | k1 * C2_LOW & MASK_LOW;
          if (blockCounts & 1) {
            h1 ^= k1;
          } else {
            h2 ^= k1;
          }
      }
      this.h1 = h1;
      this.h2 = h2;
      return this;
    },
    hexdigest: function MurmurHash3_64_hexdigest() {
      var h1 = this.h1;
      var h2 = this.h2;
      h1 ^= h2 >>> 1;
      h1 = h1 * 0xed558ccd & MASK_HIGH | h1 * 0x8ccd & MASK_LOW;
      h2 = h2 * 0xff51afd7 & MASK_HIGH | ((h2 << 16 | h1 >>> 16) * 0xafd7ed55 & MASK_HIGH) >>> 16;
      h1 ^= h2 >>> 1;
      h1 = h1 * 0x1a85ec53 & MASK_HIGH | h1 * 0xec53 & MASK_LOW;
      h2 = h2 * 0xc4ceb9fe & MASK_HIGH | ((h2 << 16 | h1 >>> 16) * 0xb9fe1a85 & MASK_HIGH) >>> 16;
      h1 ^= h2 >>> 1;
      for (var i = 0, arr = [h1, h2], str = ''; i < arr.length; i++) {
        var hex = (arr[i] >>> 0).toString(16);
        while (hex.length < 8) {
          hex = '0' + hex;
        }
        str += hex;
      }
      return str;
    }
  };
  return MurmurHash3_64;
}();
exports.MurmurHash3_64 = MurmurHash3_64;

/***/ }),
/* 43 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.PDFImage = undefined;

var _util = __w_pdfjs_require__(0);

var _primitives = __w_pdfjs_require__(1);

var _colorspace = __w_pdfjs_require__(3);

var _stream = __w_pdfjs_require__(2);

var _jpeg_stream = __w_pdfjs_require__(6);

var _jpx = __w_pdfjs_require__(13);

var PDFImage = function PDFImageClosure() {
  function handleImageData(image, nativeDecoder) {
    if (nativeDecoder && nativeDecoder.canDecode(image)) {
      return nativeDecoder.decode(image).catch(reason => {
        (0, _util.warn)('Native image decoding failed -- trying to recover: ' + (reason && reason.message));
        return image;
      });
    }
    return Promise.resolve(image);
  }
  function decodeAndClamp(value, addend, coefficient, max) {
    value = addend + value * coefficient;
    return value < 0 ? 0 : value > max ? max : value;
  }
  function resizeImageMask(src, bpc, w1, h1, w2, h2) {
    var length = w2 * h2;
    var dest = bpc <= 8 ? new Uint8Array(length) : bpc <= 16 ? new Uint16Array(length) : new Uint32Array(length);
    var xRatio = w1 / w2;
    var yRatio = h1 / h2;
    var i,
        j,
        py,
        newIndex = 0,
        oldIndex;
    var xScaled = new Uint16Array(w2);
    var w1Scanline = w1;
    for (i = 0; i < w2; i++) {
      xScaled[i] = Math.floor(i * xRatio);
    }
    for (i = 0; i < h2; i++) {
      py = Math.floor(i * yRatio) * w1Scanline;
      for (j = 0; j < w2; j++) {
        oldIndex = py + xScaled[j];
        dest[newIndex++] = src[oldIndex];
      }
    }
    return dest;
  }
  function PDFImage({ xref, res, image, isInline = false, smask = null, mask = null, isMask = false, pdfFunctionFactory }) {
    this.image = image;
    var dict = image.dict;
    if (dict.has('Filter')) {
      var filter = dict.get('Filter').name;
      if (filter === 'JPXDecode') {
        var jpxImage = new _jpx.JpxImage();
        jpxImage.parseImageProperties(image.stream);
        image.stream.reset();
        image.bitsPerComponent = jpxImage.bitsPerComponent;
        image.numComps = jpxImage.componentsCount;
      } else if (filter === 'JBIG2Decode') {
        image.bitsPerComponent = 1;
        image.numComps = 1;
      }
    }
    this.width = dict.get('Width', 'W');
    this.height = dict.get('Height', 'H');
    if (this.width < 1 || this.height < 1) {
      throw new _util.FormatError(`Invalid image width: ${this.width} or ` + `height: ${this.height}`);
    }
    this.interpolate = dict.get('Interpolate', 'I') || false;
    this.imageMask = dict.get('ImageMask', 'IM') || false;
    this.matte = dict.get('Matte') || false;
    var bitsPerComponent = image.bitsPerComponent;
    if (!bitsPerComponent) {
      bitsPerComponent = dict.get('BitsPerComponent', 'BPC');
      if (!bitsPerComponent) {
        if (this.imageMask) {
          bitsPerComponent = 1;
        } else {
          throw new _util.FormatError(`Bits per component missing in image: ${this.imageMask}`);
        }
      }
    }
    this.bpc = bitsPerComponent;
    if (!this.imageMask) {
      var colorSpace = dict.get('ColorSpace', 'CS');
      if (!colorSpace) {
        (0, _util.info)('JPX images (which do not require color spaces)');
        switch (image.numComps) {
          case 1:
            colorSpace = _primitives.Name.get('DeviceGray');
            break;
          case 3:
            colorSpace = _primitives.Name.get('DeviceRGB');
            break;
          case 4:
            colorSpace = _primitives.Name.get('DeviceCMYK');
            break;
          default:
            throw new Error(`JPX images with ${this.numComps} ` + 'color components not supported.');
        }
      }
      let resources = isInline ? res : null;
      this.colorSpace = _colorspace.ColorSpace.parse(colorSpace, xref, resources, pdfFunctionFactory);
      this.numComps = this.colorSpace.numComps;
    }
    this.decode = dict.getArray('Decode', 'D');
    this.needsDecode = false;
    if (this.decode && (this.colorSpace && !this.colorSpace.isDefaultDecode(this.decode) || isMask && !_colorspace.ColorSpace.isDefaultDecode(this.decode, 1))) {
      this.needsDecode = true;
      var max = (1 << bitsPerComponent) - 1;
      this.decodeCoefficients = [];
      this.decodeAddends = [];
      for (var i = 0, j = 0; i < this.decode.length; i += 2, ++j) {
        var dmin = this.decode[i];
        var dmax = this.decode[i + 1];
        this.decodeCoefficients[j] = dmax - dmin;
        this.decodeAddends[j] = max * dmin;
      }
    }
    if (smask) {
      this.smask = new PDFImage({
        xref,
        res,
        image: smask,
        isInline,
        pdfFunctionFactory
      });
    } else if (mask) {
      if ((0, _primitives.isStream)(mask)) {
        var maskDict = mask.dict,
            imageMask = maskDict.get('ImageMask', 'IM');
        if (!imageMask) {
          (0, _util.warn)('Ignoring /Mask in image without /ImageMask.');
        } else {
          this.mask = new PDFImage({
            xref,
            res,
            image: mask,
            isInline,
            isMask: true,
            pdfFunctionFactory
          });
        }
      } else {
        this.mask = mask;
      }
    }
  }
  PDFImage.buildImage = function ({ handler, xref, res, image, isInline = false, nativeDecoder = null, pdfFunctionFactory }) {
    var imagePromise = handleImageData(image, nativeDecoder);
    var smaskPromise;
    var maskPromise;
    var smask = image.dict.get('SMask');
    var mask = image.dict.get('Mask');
    if (smask) {
      smaskPromise = handleImageData(smask, nativeDecoder);
      maskPromise = Promise.resolve(null);
    } else {
      smaskPromise = Promise.resolve(null);
      if (mask) {
        if ((0, _primitives.isStream)(mask)) {
          maskPromise = handleImageData(mask, nativeDecoder);
        } else if (Array.isArray(mask)) {
          maskPromise = Promise.resolve(mask);
        } else {
          (0, _util.warn)('Unsupported mask format.');
          maskPromise = Promise.resolve(null);
        }
      } else {
        maskPromise = Promise.resolve(null);
      }
    }
    return Promise.all([imagePromise, smaskPromise, maskPromise]).then(function ([imageData, smaskData, maskData]) {
      return new PDFImage({
        xref,
        res,
        image: imageData,
        isInline,
        smask: smaskData,
        mask: maskData,
        pdfFunctionFactory
      });
    });
  };
  PDFImage.createMask = function ({ imgArray, width, height, imageIsFromDecodeStream, inverseDecode }) {
    var computedLength = (width + 7 >> 3) * height;
    var actualLength = imgArray.byteLength;
    var haveFullData = computedLength === actualLength;
    var data, i;
    if (imageIsFromDecodeStream && (!inverseDecode || haveFullData)) {
      data = imgArray;
    } else if (!inverseDecode) {
      data = new Uint8Array(actualLength);
      data.set(imgArray);
    } else {
      data = new Uint8Array(computedLength);
      data.set(imgArray);
      for (i = actualLength; i < computedLength; i++) {
        data[i] = 0xff;
      }
    }
    if (inverseDecode) {
      for (i = 0; i < actualLength; i++) {
        data[i] ^= 0xFF;
      }
    }
    return {
      data,
      width,
      height
    };
  };
  PDFImage.prototype = {
    get drawWidth() {
      return Math.max(this.width, this.smask && this.smask.width || 0, this.mask && this.mask.width || 0);
    },
    get drawHeight() {
      return Math.max(this.height, this.smask && this.smask.height || 0, this.mask && this.mask.height || 0);
    },
    decodeBuffer(buffer) {
      var bpc = this.bpc;
      var numComps = this.numComps;
      var decodeAddends = this.decodeAddends;
      var decodeCoefficients = this.decodeCoefficients;
      var max = (1 << bpc) - 1;
      var i, ii;
      if (bpc === 1) {
        for (i = 0, ii = buffer.length; i < ii; i++) {
          buffer[i] = +!buffer[i];
        }
        return;
      }
      var index = 0;
      for (i = 0, ii = this.width * this.height; i < ii; i++) {
        for (var j = 0; j < numComps; j++) {
          buffer[index] = decodeAndClamp(buffer[index], decodeAddends[j], decodeCoefficients[j], max);
          index++;
        }
      }
    },
    getComponents(buffer) {
      var bpc = this.bpc;
      if (bpc === 8) {
        return buffer;
      }
      var width = this.width;
      var height = this.height;
      var numComps = this.numComps;
      var length = width * height * numComps;
      var bufferPos = 0;
      var output = bpc <= 8 ? new Uint8Array(length) : bpc <= 16 ? new Uint16Array(length) : new Uint32Array(length);
      var rowComps = width * numComps;
      var max = (1 << bpc) - 1;
      var i = 0,
          ii,
          buf;
      if (bpc === 1) {
        var mask, loop1End, loop2End;
        for (var j = 0; j < height; j++) {
          loop1End = i + (rowComps & ~7);
          loop2End = i + rowComps;
          while (i < loop1End) {
            buf = buffer[bufferPos++];
            output[i] = buf >> 7 & 1;
            output[i + 1] = buf >> 6 & 1;
            output[i + 2] = buf >> 5 & 1;
            output[i + 3] = buf >> 4 & 1;
            output[i + 4] = buf >> 3 & 1;
            output[i + 5] = buf >> 2 & 1;
            output[i + 6] = buf >> 1 & 1;
            output[i + 7] = buf & 1;
            i += 8;
          }
          if (i < loop2End) {
            buf = buffer[bufferPos++];
            mask = 128;
            while (i < loop2End) {
              output[i++] = +!!(buf & mask);
              mask >>= 1;
            }
          }
        }
      } else {
        var bits = 0;
        buf = 0;
        for (i = 0, ii = length; i < ii; ++i) {
          if (i % rowComps === 0) {
            buf = 0;
            bits = 0;
          }
          while (bits < bpc) {
            buf = buf << 8 | buffer[bufferPos++];
            bits += 8;
          }
          var remainingBits = bits - bpc;
          var value = buf >> remainingBits;
          output[i] = value < 0 ? 0 : value > max ? max : value;
          buf = buf & (1 << remainingBits) - 1;
          bits = remainingBits;
        }
      }
      return output;
    },
    fillOpacity(rgbaBuf, width, height, actualHeight, image) {
      var smask = this.smask;
      var mask = this.mask;
      var alphaBuf, sw, sh, i, ii, j;
      if (smask) {
        sw = smask.width;
        sh = smask.height;
        alphaBuf = new Uint8Array(sw * sh);
        smask.fillGrayBuffer(alphaBuf);
        if (sw !== width || sh !== height) {
          alphaBuf = resizeImageMask(alphaBuf, smask.bpc, sw, sh, width, height);
        }
      } else if (mask) {
        if (mask instanceof PDFImage) {
          sw = mask.width;
          sh = mask.height;
          alphaBuf = new Uint8Array(sw * sh);
          mask.numComps = 1;
          mask.fillGrayBuffer(alphaBuf);
          for (i = 0, ii = sw * sh; i < ii; ++i) {
            alphaBuf[i] = 255 - alphaBuf[i];
          }
          if (sw !== width || sh !== height) {
            alphaBuf = resizeImageMask(alphaBuf, mask.bpc, sw, sh, width, height);
          }
        } else if (Array.isArray(mask)) {
          alphaBuf = new Uint8Array(width * height);
          var numComps = this.numComps;
          for (i = 0, ii = width * height; i < ii; ++i) {
            var opacity = 0;
            var imageOffset = i * numComps;
            for (j = 0; j < numComps; ++j) {
              var color = image[imageOffset + j];
              var maskOffset = j * 2;
              if (color < mask[maskOffset] || color > mask[maskOffset + 1]) {
                opacity = 255;
                break;
              }
            }
            alphaBuf[i] = opacity;
          }
        } else {
          throw new _util.FormatError('Unknown mask format.');
        }
      }
      if (alphaBuf) {
        for (i = 0, j = 3, ii = width * actualHeight; i < ii; ++i, j += 4) {
          rgbaBuf[j] = alphaBuf[i];
        }
      } else {
        for (i = 0, j = 3, ii = width * actualHeight; i < ii; ++i, j += 4) {
          rgbaBuf[j] = 255;
        }
      }
    },
    undoPreblend(buffer, width, height) {
      var matte = this.smask && this.smask.matte;
      if (!matte) {
        return;
      }
      var matteRgb = this.colorSpace.getRgb(matte, 0);
      var matteR = matteRgb[0];
      var matteG = matteRgb[1];
      var matteB = matteRgb[2];
      var length = width * height * 4;
      var r, g, b;
      for (var i = 0; i < length; i += 4) {
        var alpha = buffer[i + 3];
        if (alpha === 0) {
          buffer[i] = 255;
          buffer[i + 1] = 255;
          buffer[i + 2] = 255;
          continue;
        }
        var k = 255 / alpha;
        r = (buffer[i] - matteR) * k + matteR;
        g = (buffer[i + 1] - matteG) * k + matteG;
        b = (buffer[i + 2] - matteB) * k + matteB;
        buffer[i] = r <= 0 ? 0 : r >= 255 ? 255 : r | 0;
        buffer[i + 1] = g <= 0 ? 0 : g >= 255 ? 255 : g | 0;
        buffer[i + 2] = b <= 0 ? 0 : b >= 255 ? 255 : b | 0;
      }
    },
    createImageData(forceRGBA = false) {
      var drawWidth = this.drawWidth;
      var drawHeight = this.drawHeight;
      var imgData = {
        width: drawWidth,
        height: drawHeight
      };
      var numComps = this.numComps;
      var originalWidth = this.width;
      var originalHeight = this.height;
      var bpc = this.bpc;
      var rowBytes = originalWidth * numComps * bpc + 7 >> 3;
      var imgArray;
      if (!forceRGBA) {
        var kind;
        if (this.colorSpace.name === 'DeviceGray' && bpc === 1) {
          kind = _util.ImageKind.GRAYSCALE_1BPP;
        } else if (this.colorSpace.name === 'DeviceRGB' && bpc === 8 && !this.needsDecode) {
          kind = _util.ImageKind.RGB_24BPP;
        }
        if (kind && !this.smask && !this.mask && drawWidth === originalWidth && drawHeight === originalHeight) {
          imgData.kind = kind;
          imgArray = this.getImageBytes(originalHeight * rowBytes);
          if (this.image instanceof _stream.DecodeStream) {
            imgData.data = imgArray;
          } else {
            var newArray = new Uint8Array(imgArray.length);
            newArray.set(imgArray);
            imgData.data = newArray;
          }
          if (this.needsDecode) {
            (0, _util.assert)(kind === _util.ImageKind.GRAYSCALE_1BPP);
            var buffer = imgData.data;
            for (var i = 0, ii = buffer.length; i < ii; i++) {
              buffer[i] ^= 0xff;
            }
          }
          return imgData;
        }
        if (this.image instanceof _jpeg_stream.JpegStream && !this.smask && !this.mask) {
          let imageLength = originalHeight * rowBytes;
          switch (this.colorSpace.name) {
            case 'DeviceGray':
              imageLength *= 3;
            case 'DeviceRGB':
            case 'DeviceCMYK':
              imgData.kind = _util.ImageKind.RGB_24BPP;
              imgData.data = this.getImageBytes(imageLength, drawWidth, drawHeight, true);
              return imgData;
          }
        }
      }
      imgArray = this.getImageBytes(originalHeight * rowBytes);
      var actualHeight = 0 | imgArray.length / rowBytes * drawHeight / originalHeight;
      var comps = this.getComponents(imgArray);
      var alpha01, maybeUndoPreblend;
      if (!forceRGBA && !this.smask && !this.mask) {
        imgData.kind = _util.ImageKind.RGB_24BPP;
        imgData.data = new Uint8Array(drawWidth * drawHeight * 3);
        alpha01 = 0;
        maybeUndoPreblend = false;
      } else {
        imgData.kind = _util.ImageKind.RGBA_32BPP;
        imgData.data = new Uint8Array(drawWidth * drawHeight * 4);
        alpha01 = 1;
        maybeUndoPreblend = true;
        this.fillOpacity(imgData.data, drawWidth, drawHeight, actualHeight, comps);
      }
      if (this.needsDecode) {
        this.decodeBuffer(comps);
      }
      this.colorSpace.fillRgb(imgData.data, originalWidth, originalHeight, drawWidth, drawHeight, actualHeight, bpc, comps, alpha01);
      if (maybeUndoPreblend) {
        this.undoPreblend(imgData.data, drawWidth, actualHeight);
      }
      return imgData;
    },
    fillGrayBuffer(buffer) {
      var numComps = this.numComps;
      if (numComps !== 1) {
        throw new _util.FormatError(`Reading gray scale from a color image: ${numComps}`);
      }
      var width = this.width;
      var height = this.height;
      var bpc = this.bpc;
      var rowBytes = width * numComps * bpc + 7 >> 3;
      var imgArray = this.getImageBytes(height * rowBytes);
      var comps = this.getComponents(imgArray);
      var i, length;
      if (bpc === 1) {
        length = width * height;
        if (this.needsDecode) {
          for (i = 0; i < length; ++i) {
            buffer[i] = comps[i] - 1 & 255;
          }
        } else {
          for (i = 0; i < length; ++i) {
            buffer[i] = -comps[i] & 255;
          }
        }
        return;
      }
      if (this.needsDecode) {
        this.decodeBuffer(comps);
      }
      length = width * height;
      var scale = 255 / ((1 << bpc) - 1);
      for (i = 0; i < length; ++i) {
        buffer[i] = scale * comps[i] | 0;
      }
    },
    getImageBytes(length, drawWidth, drawHeight, forceRGB = false) {
      this.image.reset();
      this.image.drawWidth = drawWidth || this.width;
      this.image.drawHeight = drawHeight || this.height;
      this.image.forceRGB = !!forceRGB;
      return this.image.getBytes(length);
    }
  };
  return PDFImage;
}();
exports.PDFImage = PDFImage;

/***/ }),
/* 44 */
/***/ (function(module, exports, __w_pdfjs_require__) {

"use strict";


module.exports = function isNodeJS() {
  return typeof process === 'object' && process + '' === '[object process]';
};

/***/ })
/******/ ]);
});PK
!<{��[HH3chrome/pdfjs/content/pdfjschildbootstrap-enabled.js/* Copyright 2014 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.
*/

"use strict";

/*
 * pdfjschildbootstrap-enabled.js loads into the content process to
 * take care of initializing our built-in version of pdfjs when
 * running remote. It will only be run when PdfJs.enable is true.
 */

ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://pdf.js/PdfJs.jsm");

if (Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_CONTENT) {
  // register various pdfjs factories that hook us into content loading.
  PdfJs.ensureRegistered();
}
PK
!<+�1���+chrome/pdfjs/content/pdfjschildbootstrap.js/* Copyright 2014 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.
*/

"use strict";

/*
 * pdfjschildbootstrap.js loads into the content process to take care of
 * initializing our built-in version of pdfjs when running remote.
 */

ChromeUtils.import("resource://pdf.js/PdfjsContentUtils.jsm");

// init content utils shim pdfjs will use to access privileged apis.
PdfjsContentUtils.init();
PK
!<%���d	d	-chrome/pdfjs/content/web/cmaps/78-EUC-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE���?�A��] �g` ^�ga��?�F�A]�y�"
�W�	���0�&R�J�-U��*�s��H �# �Dq���e�Q�g�J�n��"�x�R1��S�3�T�8�U�Aq
���C�V	�M�W�X�X�\�Z�`�['�e�\�q���!�]�I�^4�+�_�a�`�{�a�~q����b��a�!��(�c�D�d�J�e�R�1�U�f�Yq
�� �]�g�r��h�
� ��i�"a��	�;�j�F�k=�[q����l��m�#�2�?�n�H�o�f�p�m�q�t�r�vq���w�s�{�t��u��v�%�w�)�x�,�
�2�y�;�z�H�{�P�|a���U�}�i�~<�vq��	�3�
�>�	�J��U�]�o������a��1���D��X�	�eq	���o��v�	�y�
�{�D�a��&�M�
�)�v����q	��	�+�	�6�4�A��x�
�~q
���	���
���(��>��H��Mq	���g�-�q�� ��'� �0q���E�!�G�""�g�#	��$��&�q���#�(�n�+�)�<�*�O�+�R�,�b�-�lq¡��.��/
�.�0�=�1
�D�2�P�3�W�5�^qá�_�6�f�7�i�9
�m�;1�|�<�/�=�4�>�;qġ�=�?	�Z�@�e�A�g�B�j�C�l�K�q�5"�xqš��D��E�"�F�)�G�1�H�:�I�C�J�F�K�M�_�R�M�_�P�i�6�n�N�sqơ�y�O��P��Q�.�S�5�U�:�V�@�X
�C�m�Oq
ǡ�W�Y�`�Z�p�[��\�	�]� �^�$q
ȡ�5�_�9�`�C�a"�E�b�i�c�w�d�
qɡ��e
��g�;�#�h�%�i	�(�j�3�k�A�l,�Dq	ʡ!�q�m��n��o�+�p�Jqˡ�O�q�Q�r �X�s�z�3��t��,�/�(q̡+�-�u
�Z�v�f�w�l�x�o�5�y��z�	�{q͡��|��}�;�~�?�4
�E��Ta
Ρ)�i-��D�#�H�X�]�`�M4�z�''�0a϶�wa���.��%���q	ѡ�X��u�����	0�aҡ]�6q	ӡ��
�$�"�.�
�R��_a��'aԡ"�r��w�6�"�P$�m��"�.[�0�"]��"�jY�n�">�H
�
�� �"�&�?0�Sa	����x�%��>����;���L��q
ۡ#���)��.�"�<��O�h�Q�	�Xa޹�m;�aܡ]�b�"]�@�"��7#�N�s�"]�|�"�Z7�`��0�"	�8�CL�Ia����U�8� �5�4�!�"q	�	��$;�!�n�^�Q�`�%�ea�(�t�&��'�(�T�2q	��R�(�k�\�x�)��*� a��u7� a��a��0�+�I�,	�K�-7�V�"$��.7�4�"B�l�/�0�0�5�1�>�"�J�2�^�37�e�4	��"�(
�**�6#�bq	����
�6�
�70�#�8�Uq	��d�9)�v��!�:�*�;�:a���*a
�+�B�o	�	��",� �N�l�n�"�~��&�5�"]�\a���<
�=�Y�>�?�@�:�A�B�Cq
�#�:�F
�_�G�k�H�q�1�I�a�]�q
��v�J
��K��w�L$�$�)	�Ja�/�T�M*��N�1PK
!<8�uH��-chrome/pdfjs/content/web/cmaps/78-EUC-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�78-EUC-Ha���O
�Q	�S�V�[A��m�?�2a���PK
!<�U+K	K	)chrome/pdfjs/content/web/cmaps/78-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE!!��]a!!]�y�"
�W�	���0�&R�J�-U��*�s��H �# �Dq0!�e�Q�g�J�n��"�x�R1��S�3�T�8�U�Aq
1!�C�V	�M�W�X�X�\�Z�`�['�e�\�q2!�!�]�I�^4�+�_�a�`�{�a�~q3!��b��a�!��(�c�D�d�J�e�R�1�U�f�Yq
4! �]�g�r��h�
� ��i�"a5!	�;�j�F�k=�[q6!��l��m�#�2�?�n�H�o�f�p�m�q�t�r�vq7!�w�s�{�t��u��v�%�w�)�x�,�
�2�y�;�z�H�{�P�|a8!�U�}�i�~<�vq9!	�3�
�>�	�J��U�]�o������a:!1���D��X�	�eq	;!�o��v�	�y�
�{�D�a<!&�M�
�)�v����q	=!	�+�	�6�4�A��x�
�~q
>!�	���
���(��>��H��Mq	?!�g�-�q�� ��'� �0q@!�E�!�G�""�g�#	��$��&�qA!�#�(�n�+�)�<�*�O�+�R�,�b�-�lqB!��.��/
�.�0�=�1
�D�2�P�3�W�5�^qC!�_�6�f�7�i�9
�m�;1�|�<�/�=�4�>�;qD!�=�?	�Z�@�e�A�g�B�j�C�l�K�q�5"�xqE!��D��E�"�F�)�G�1�H�:�I�C�J�F�K�M�_�R�M�_�P�i�6�n�N�sqF!�y�O��P��Q�.�S�5�U�:�V�@�X
�C�m�Oq
G!�W�Y�`�Z�p�[��\�	�]� �^�$q
H!�5�_�9�`�C�a"�E�b�i�c�w�d�
qI!��e
��g�;�#�h�%�i	�(�j�3�k�A�l,�Dq	J!!�q�m��n��o�+�p�JqK!�O�q�Q�r �X�s�z�3��t��,�/�(qL!+�-�u
�Z�v�f�w�l�x�o�5�y��z�	�{qM!��|��}�;�~�?�4
�E��Ta
N!)�i-��D�#�H�X�]�`�M4�z�''�0aO6�waNK�.��%���q	Q!�X��u�����	0�aR!]�6q	S!��
�$�"�.�
�R��_aYx�'aT!"�r��w�6�"�P$�m��"�.[�0�"]��"�jY�n�">�H
�
�� �"�&�?0�Sa	TD��x�%��>����;���L��q
[!#���)��.�"�<��O�h�Q�	�Xa^9�m;�a\!]�b�"]�@�"��7#�N�s�"]�|�"�Z7�`��0�"	�8�CL�Ia^P��U�8� �5�4�!�"q	b!	��$;�!�n�^�Q�`�%�eac!(�t�&��'�(�T�2q	d!�R�(�k�\�x�)��*� ai"�u7� ai.�ae!�0�+�I�,	�K�-7�V�"$��.7�4�"B�l�/�0�0�5�1�>�"�J�2�^�37�e�4	��"�(
�**�6#�bq	j!���
�6�
�70�#�8�Uq	k!�d�9)�v��!�:�*�;�:alM�*a
l!+�B�o	�	��",� �N�l�n�"�~��&�5�"]�\ali�<
�=�Y�>�?�@�:�A�B�Cq
p!#�:�F
�_�G�k�H�q�1�I�aq!]�q
r!�v�J
��K��w�L$�$�)	�Jas!/�T�M*��N�1PK
!<�n�^	^	.chrome/pdfjs/content/web/cmaps/78-RKSJ-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE��@�<�?�@�< �g` ^�ga
�@>�y,�8�"	���0R�JN>��\�s�i �#�D�Sq���e�Q�g�J�n��"�x�R1��S�3�T�8�U�Aq�@�C�V	�M�W�X�X�\�Z�`�[�eq
��
��\��]�I�^4�+�_�a�`�{�a�~a�@��b��a�!��(q���>�c�D�d�J�e�R�1�U�f$�Y�g�r��h�
� ��i�"a�@	�;�j�F�k�[q���z�l��m�#�2�?�n�H�o�f�p�m�q�t�r�vq�@�w�s�{�t��u��v�%�w�)�x�,�
�2q
���6�y�;�z�H�{�P�|�U�}�i�~<�vq	�@	�3�
�>�	�J��U�]�oq
���r�����6���D��X�	�eq	�@�o��v�	�y�
�{�%�a��E�.�v��C	�+	�6(�Aa���)a���
��m�
�q���j��x��~���
���(��>��H��Ma�@�g�-�q�� q����'� �0�!�G�""�g�#	��$��&�q�@�#�(�n�+�)�<�*�O�+�R�,q���b�-0�l�.��/
�.�0�=�1
�D�2�P�3�W�5�^q	�@�_�6�f�7�i�9
�m�;!�|q����<�/�=�4�>�;�?	�Z�@�e�A�g�B�j�C�l�K�q�5"�xq�@��D��E�"�F�)�G�1�H�:�I�C�J�F�K�M�_�Rq���Z�M�_�P�i�6�n�N"�s�O��P��Q�.�S�5�U�:�V�@�X
�C�m�Oa�@�W�Y�`�Z�p�[�q����\�	�]� �^�$�_�9�`�C�a"�E�b�i�c�w�d�
q�@��e
��g�;�#�h�%�i	�(�j�3�k�A�l
�Dq	��@�R�m��n��o�+�p�Ja�@�O�q�Q�r �X�s�zq��	��3��t��,�/0�(�u
�Z�v�f�w�l�x�o�5�y��z�	�{q	�@��|��}�;�~�?�4�Ea���J>�T-��DD�H�X�]�`,4�z�''�0a�U�wa���?�.�F���q	�@�X��u�����	�a���wa��|�C��$"�.�R
�S5�_��6C�P!�m��[�0C>�!�KY�nC>�Ha	�O�
	�#�

�6���&����q
���
��
��'� ��?�0�Sa���a�@#��)�.�"�<
�C�O�h�Qg�XC>�@6��m�7#�N�sa�d��!���9�`�?�Fa�@>�|#�;7�`��0C	�8�C-�Ia���5a��8� l�!�"q	�(�w�$;�!�n�^�Q�`�%�ea�@(�t�&��'�(�T�2q	�6�3�(�k�\�x�)��*� a�@�0�+�I�,	�K�-�VC�o�.7�4C>�lq
��+�/�0�0�5�1�>�2�^�37�e�4	�a�@�(�u
�*�*�6� �bq	�!�g��
�6�
�70�#�8�Ua�@�d�9)�v��!q��#�:�*�;3�:�*�o�<	��=	�a�@,� �>�Nq��_�?�l�@�n�A��B��C&�5a�@>�\q
�B��F
�_�G�k�H�q�1�I�a�@>�q
�*�W�J
��K��w�L$�$�)	�Ja�@/�T�M
���N�1PK
!<�@i��.chrome/pdfjs/content/web/cmaps/78-RKSJ-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�	78-RKSJ-Ha�A�O
�Q	�S�V�[A���m�S a���PK
!<a�bة�)chrome/pdfjs/content/web/cmaps/78-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�78-Ha!"�O
�Q	�S�V�[A!a�m�?�2a%u�PK
!<jM��[
[
0chrome/pdfjs/content/web/cmaps/78ms-RKSJ-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE��@�<�?�@�< �g` ]�g�wa�@>�y,�8�e�m�t��R	���0R�JN>��\�s�i �#�D�SQ ���7	w	WE$$a�@�q
�_�!�f�$�h�&�j�(�l�+�k�.�o�1a�~�q	���8�w
�=�z�y��H��Oq���e�Q�g�J�n��"�x�R1��S�3�T�8�U�Aq�@�C�V	�M�W�X�X�\�Z�`�[�eq
��
��\��]�I�^4�+�_�a�`�{�a�~a�@��b��a�!��(q���>�c�D�d�J�e�R�1�U�f$�Y�g�r��h�
� ��i�"a�@	�;�j�F�k�[q���z�l��m�#�2�?�n�H�o�f�p�m�q�t�r�vq�@�w�s�{�t��u��v�%�w�)�x�,�
�2q
���6�y�;�z�H�{�P�|�U�}�i�~<�vq	�@	�3�
�>�	�J��U�]�oq
���r�����6���D��X�	�eq	�@�o��v�	�y�
�{�%�a��E�.�v��C	�+	�6(�Aa���)a���
��m�
�q���j��x��~���
���(��>��H��Ma�@�g�-�q�� q����'� �0�!�G�""�g�#	��$��&�q�@�#�(�n�+�)�<�*�O�+�R�,q���b�-0�l�.��/
�.�0�=�1
�D�2�P�3�W�5�^q	�@�_�6�f�7�i�9
�m�;!�|q����<�/�=�4�>�;�?	�Z�@�e�A�g�B�j�C�l�K�q�5"�xq�@��D��E�"�F�)�G�1�H�:�I�C�J�F�K�M�_�Rq���Z�M�_�P�i�6�n�N"�s�O��P��Q�.�S�5�U�:�V�@�X
�C�m�Oa�@�W�Y�`�Z�p�[�q����\�	�]� �^�$�_�9�`�C�a"�E�b�i�c�w�d�
q�@��e
��g�;�#�h�%�i	�(�j�3�k�A�l
�Dq	��@�R�m��n��o�+�p�Ja�@�O�q�Q�r �X�s�zq��	��3��t��,�/0�(�u
�Z�v�f�w�l�x�o�5�y��z�	�{q	�@��|��}�;�~�?�4�Ea���J>�T-��DD�H�X�]�`,4�z�''�0a�U�wa���?�.�F���q	�@�X��u�����	�a���wa��|�C��$"�.�R
�S5�_��6C�P!�m��[�0C>�!�KY�nC>�Ha	�O�
	�#�

�6���&����q
���
��
��'� ��?�0�Sa���a�@#��)�.�"�<
�C�O�h�Qg�XC>�@6��m�7#�N�sa�d��!���9�`�?�Fa�@>�|#�;7�`��0C	�8�C-�Ia���5a��8� l�!�"q	�(�w�$;�!�n�^�Q�`�%�ea�@(�t�&��'�(�T�2q	�6�3�(�k�\�x�)��*� a�@�0�+�I�,	�K�-�VC�o�.7�4C>�lq
��+�/�0�0�5�1�>�2�^�37�e�4	�a�@�(�u
�*�*�6� �bq	�!�g��
�6�
�70�#�8�Ua�@�d�9)�v��!q��#�:�*�;3�:�*�o�<	��=	�a�@,� �>�Nq��_�?�l�@�n�A��B��C&�5a�@>�\q
�B��F
�_�G�k�H�q�1�I�a�@>�q
�*�W�J
��K��w�L$�$�)	�Ja��oa��Ia�@/�T�M
���N�1�\�>�'3�fG�C>�bl�!	��Eq	�@	�	��o�E�B�:�w�"�'a��O�J�I+�C>�F|�C�PK
!<!��8""0chrome/pdfjs/content/web/cmaps/78ms-RKSJ-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�78ms-RKSJ-Ha���c�b�`a�A�O
�Q	�S�V�[�ma�C�L�RA���N�r�AS a���Q ���9	>1>s"	F1N9/B%$7Bq�_��	���	�������a���PK
!<�Ll��0chrome/pdfjs/content/web/cmaps/83pv-RKSJ-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE��@�<�?�@�<� ` ^aa�@>�y,�8�e�m�t��R	���0R�JN>��\�s�i �#�D�SQ ���7	w	WE$$a
�@>�h��(>�G�C>�&�e�'�w�q�z�y��a/��]�eC>�C|�C>�|�>C>�;|�zC>�w|�6C>�3|�rC>�o|�.C>�+|�jC>�g|�&C>�#|�bC>�_|�C>�|�ZC>�W|�C>�|�RC>�O|�C>�|�JC2�G,]�zC>�X|�C>�|�SC>�P|�C>�|�KC>�H|�C>�|�CC>�@|�a��K�7R��!
�8�H�O`�?�Fa�@>�||�;a�@>�8|�wC>�t|�3C>�0|�oC>�l|�+C>�(|�gC>�d|�#C>� |�_C>�\|�C>�|�WC>�T"�a��\q�@�y�O�|�Q��S��V��[�4a��8�m*�:�e�m�t��R	���0Q	쟽n�G�F�E�D�C�B�A�@q��S�s�m�t�
�u��v��w�Q	�@�x�5�4�3�2�1�0�/�.a�I�&�}�@q
��\�~�`��b��d��k�a��s��9�z�y��a�@��1
�:�H�Oa�_��`���d|PK
!<#a���0chrome/pdfjs/content/web/cmaps/90ms-RKSJ-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE��@�<�?�@�< �g` ]�g�wa�@>�y,�8�e�m�t��R	���0R�JN>��\�s�i �#�D�SQ ���7	w	WE$$a�@�q
�_�!�f�$�h�&�j�(�l�+�k�.�o�1a�~�q	���8�w
�=�z�y��H��Oa��]�eC>�C|�a*�@>�|�>C>�;|�zC>�w|�6C>�3|�rC>�o|�.C>�+|�jC>�g|�&C>�#|�bC>�_|�C>�|�ZC>�W|�C>�|�RC>�O|�C>�|�JC2�G,]�zC>�X|�C>�|�SC>�P|�C>�|�KC>�H|�C>�|�Ca�@>�@|�`�?�Fa��oa��Ia�@>�||�;C>�8|�wC>�t|�3C>�0|�oC>�l|�+C>�(|�gC>�d|�#C>� |�_C>�\|�C>�|�WC>�T"��\�>�'3�fG�C>�bl�!	��Eq	�@	�	��o�E�B�:�w�"�'a��O�J�I+�C>�F|�C�PK
!<��Q�""0chrome/pdfjs/content/web/cmaps/90ms-RKSJ-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�90ms-RKSJ-Ha���c�b�`a�A�O
�Q	�S�V�[�ma�C�L�RA���N�r�AS a���Q ���9	>1>s"	F1N9/B%$7Bq�_��	���	�������a���PK
!<pNd��1chrome/pdfjs/content/web/cmaps/90msp-RKSJ-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE��@�<�?�@�< ` ^a�@>�y,�8�e�m�t��R	���0R�JN>��\�s�i �#�D�SQ ���7	w	WE$$a�@�q
�_�!�f�$�h�&�j�(�l�+�k�.�o�1a�~�q	���8�w
�=�z�y��H��Oa��]�eC>�C|�a*�@>�|�>C>�;|�zC>�w|�6C>�3|�rC>�o|�.C>�+|�jC>�g|�&C>�#|�bC>�_|�C>�|�ZC>�W|�C>�|�RC>�O|�C>�|�JC2�G,]�zC>�X|�C>�|�SC>�P|�C>�|�KC>�H|�C>�|�Ca�@>�@|�`�?�Fa��oa��Ia�@>�||�;C>�8|�wC>�t|�3C>�0|�oC>�l|�+C>�(|�gC>�d|�#C>� |�_C>�\|�C>�|�WC>�T"��\�>�'3�fG�C>�bl�!	��Eq	�@	�	��o�E�B�:�w�"�'a��O�J�I+�C>�F|�C�PK
!<�^G�##1chrome/pdfjs/content/web/cmaps/90msp-RKSJ-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�90msp-RKSJ-Ha���c�b�`a�A�O
�Q	�S�V�[�ma�C�L�RA���N�r�AS a���Q ���9	>1>s"	F1N9/B%$7Bq�_��	���	�������a���PK
!<L�gV��0chrome/pdfjs/content/web/cmaps/90pv-RKSJ-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE��@�<�?�@�<� ` ^aa�@>�y,�8�e�m�t��R	���0R�JN>��\�s�i �#�D�SQ ���7	w	WE$$a�@�K	�a�^�	�}	��0a�|�^�a#�!�g�jQ�@�1���B�0�q�>�I�B�C�va�L�5�e�X�q�d�b�_�rq	���:�s�R�P�S����a���z�x�t��u�N�M�L�K�i�Q�G�Vjw�
�$�+���"		a�V�K�N8�}�=Q
���Z�2�C@��C�}����a����j�(�hQ���&
�|�����A���pa/��]�eC>�C|�C>�|�>C>�;|�zC>�w|�6C>�3|�rC>�o|�.C>�+|�jC>�g|�&C>�#|�bC>�_|�C>�|�ZC>�W|�C>�|�RC>�O|�C>�|�JC2�G,]�zC>�X|�C>�|�SC>�P|�C>�|�KC>�H|�C>�|�CC>�@|�a���EX�H�M�8a���s�q&��v�k��y`�?�Fa�@>�||�;C>�8|�wC>�t|�3C>�0|�oC>�l|�+C>�(|�gC>�d|�#C>� |�_C>�\|�C>�|�WC>�T"�a�A�O
�Q	�S�V�[a��\A끽m�S a��`���d|PK
!<��'�0chrome/pdfjs/content/web/cmaps/90pv-RKSJ-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�90pv-RKSJ-Ha�A�O
�Q	�S�V�[A���m�S a���q����	���
�
����	��������a����!�$�&�%8�PK
!<��;s	s	*chrome/pdfjs/content/web/cmaps/Add-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE!!��]a!!]�y�"
�W�e�m�t���1	���0�&R�J��*U��*�s��H �# �DQ (!�7	w	WE$$A0!�e�Vq
0#�g��w�R1��S�3��6�T�8�U�Aq1!�C�V�M��V�W�X��\�Z�`�['�e�\�q2!�!�]�)�^4�+�_�a��h�`�{q	3!��bG��d�J�e�R�f�Ya4! �]�g	��h�
�i�"q	5!	�;�j�F�k �[��}��q6!��l��m�#� �;�n�H�o�f�p�m�q�t�r�vq	7!�w�t��w�)�z
�H�!�|a8!�U�}�i�~<�vq	9!	�3�@�>������a:!1���D��X�	�eq;!�o��v�	�y�
�{���"<�a<!&�M�
�u��q	=!	�+�	�6�4�A��w�
�~q>!�	��� ���>��H��Ma?!�g�-�q�#� ��'q@!�E�!B�G�#	��$��&�$��%�"q
A!�#�(�*�)�<�*�O�+�R�,�b�-�lqB!��'��.��/
�.�0�=�1
�D�2�P�5�^qC!�_�6�f�7�h�9
�m�;1�|�<�/�=�4�>�;q	D!�=�?	�Z�@�e�A�g�C.�lqE!��D��E�"�F�)�G�1�H�:�I�C�J�F�(�L�M�M�_�N�sq
F! �y�P��Q�-�S	�4�V�@�X
�C�@�OqG!�W�Y�)
�a�Z�p�[��\��]� �^�$q
H!�5�_�9�`�C�a�E�*�U�b�i�d�
q
I!��e
��g�"�h�%�i	�(�j�3�k/�AqJ!!�q�m��n��+�$�o�+�p�Jq	K!�O�r �X�s#�z�t��,�'q
L!+�-�u
�Z�v�f�w�l�x�o�y��z�	q	M!��|��}�;�~�?��TaN!X�i�E�#�H�X�`�M]�z�"�X�Z�u0��"�6E�NaNz��%������	aN|�-�%�.��/qS!��
�$��.�0�<�
�R��1�`a*T!"�r%��>�"�P$�m��(�"�.Y�0�$]��"�jY�n�"�H6�P
���"�&?�?��"#�$�)�O�"]�b�"?�@��".��N�T�t�"Q�|
�O�"�Z7�`��"	�8R�C�"M��e�"(�t��($�-aTD��x�%��>����;���L���%��d��"��2�8� �K�!�C�%�Z�&	�'a
TE�2&�3��4��5�)�7�Q�8�f�9�t�:�;�b�<q	d!�R�(�k�="�r�)��*� aw(��z�ya(e!�0�I	�K7�V�"'�4�7�"B�l�0�5�>�"�J�e0�w�"\�(�#�0�#�U�"�d2�v�*�"G�B��",� /�N�"�~�@��"]�\�"/�:,�k�"7�$�Q�"�v
��/�$�"/�T)��1�"�2ae9�+�,
�-��>��/�0�1�H�3�?�0�@�=�A1�8�B�93�:��<�d�>�Z�A�B��G��C�S�J�K�L��M�D*�N�'�\�z�E�H
�Iq
w<�1�T�7�U�4	�X��b��cawW�eqwY�$�!�f�"�g�&�j�(�,�k�.�+�l�/�nawt�u�:�{�|�*	�}���	���"�0
��J�D�L�C�O�B�T
�X�fq}!�O�L�R	�Q�Z�S�X�U�[�n�Ha}m�`�vPK
!<5�Pm	m	/chrome/pdfjs/content/web/cmaps/Add-RKSJ-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE��@�<�?�@�< �g` ^�ga�@>�y,�8�e�m�t��R	���0R�J�K>��\�s�i �#�D�SQ ���7	w	WE$$A���e�Vq
���g��w�R1��S�3��6�T�8�U�Aq
�@�C�V�M��V�W�X��\�Z�`�[�eq
��
��\��]�)�^4�+�_�a��h�`�{a�@��b<�q
��
�>�d�J�e�R�f$�Y�g	��h�
�i�"a�@	�;�j�F�k�[q���z��}���l��m�#� �;�n�H�o�f�p�m�q�t�r�va�@�w�t��w�)q
���6�z
�H�!�|�U�}�i�~<�va�@	�3�3�>q
���r�����6���D��X�	�eq�@�o��v�	�y�
�{���"�a
��E�.�
�u��C	�+�	�6�(�Aq���j��w��~��� ���>��H��Ma�@�g�-�q�#� q
����'�!B�G�#	��$��&�$��%�"q
�@�#�(�*�)�<�*�O�+�R�,q���b�-*�l�'��.��/
�.�0�=�1
�D�2�P�5�^q	�@�_�6�f�7�h�9
�m�;!�|q����<�/�=�4�>�;�?	�Z�@�e�A�g�C.�lq�@��D��E�"�F�)�G�1�H�:�I�C�J�F�(�L�Mq���Z�M�_�N&�s�P��Q�-�S	�4�V�@�X
�C�@�Oa�@�W�Y�)
�a�Z�p�[�q����\��]� �^�$�_�9�`�C�a�E�*�U�b�i�d�
q
�@��e
��g�"�h�%�i	�(�j�3�k�Aq��@�R�m��n��+�$�o�+�p�Ja�@�O�r �X�s�zq����t��,1�'�u
�Z�v�f�w�l�x�o�y��z�	a�@���;
�?�Jm�T�ED�H�X�`,]�zC�X�Z�u�5�E�Na
�Q�|�}�~�n�F������	a���-�F�.t�/q	�@��
�$��.�0�<�
�Rq	��
�S��14�`��2%��3�>a�@�P!�m���(Y�0E>�!�KY�nC�H6�P
�)�?�?�C#��)
�Cp�OC>�@a	�\�&�����]��*��,�&�a���4b�5�J�7�0�8q	����9K���N�:�T�;�t`�?�Fa
�@>�|�;�O7�`�C	�83�Cl�w�eC(�t��(�-a���8� l�!�"�%{�&	�'a�x�<q	�6�3�(�k�="�r�)��*� a�@�0�I	�K�VF�o4�7C>�la�X�+�,
�-a���>q��+�/�0�0�5�1%�>�3�e�?0�wa�G��z�ya�@>�(�g�0�#�UC�d,�v�#_�*�C,� �N&�_�@�C>�\N�,�kC7��Q*�W
��/�$C/�T���1a��@�A1�8c�94�:`�<��>9�A�B��G�(�C2�J�K�L�#�M�D+�N�\��E�H
�Iq
�[�1�T�7�U�4	�X��b��ca�v�e�$�!�f�"�gq
��&�j�(�,�k�.�+�l�/�na��u�:�{�|	�}���	��C�0
��J�D�L�C�O�B�T�X
�[�fq�@�O�L�R	�Q�Z�S�X�U�[�n�Ha��`�vPK
!<��)/chrome/pdfjs/content/web/cmaps/Add-RKSJ-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�
Add-RKSJ-Ha�A�O�L�R�Q	�S�V�Z�S�X�U�[A
���na���HA
�@�x a�����a���	��
�Q쀾	�������a���&�%�!�$��|�
PK
!<���v*chrome/pdfjs/content/web/cmaps/Add-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�Add-Ha!"�O�L�R�Q	�S�V�Z�S�X�U�[A
$!�na$u�HA
%!�xa%u���b���	��
�Qw`�	�������awk��&�%�!�$��|�
PK
!<�H�==1chrome/pdfjs/content/web/cmaps/Adobe-CNS1-0.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�q8��������������������������������������������������������������������������������������������������������������PK
!<h`�ss1chrome/pdfjs/content/web/cmaps/Adobe-CNS1-1.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE��qD�������������������������������������������������������������������������������������������������������������������������������������������PK
!<1HEYxx1chrome/pdfjs/content/web/cmaps/Adobe-CNS1-2.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE��qE��������������������������������������������������������������������������������������������������������������������������������������������@��PK
!<����1chrome/pdfjs/content/web/cmaps/Adobe-CNS1-3.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE��qJ�������������������������������������������������������������������������������������������������������������������������������������������������������������PK
!<�a���1chrome/pdfjs/content/web/cmaps/Adobe-CNS1-4.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE��qK���������������������������������������������������������������������������������������������������������������������������������������������������������������PK
!<QFC��1chrome/pdfjs/content/web/cmaps/Adobe-CNS1-5.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE��qK����������������������������������������������������������������������������������������������������������������������������������������������������������������PK
!<K��–�1chrome/pdfjs/content/web/cmaps/Adobe-CNS1-6.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE��qK��������������������������������������������������������������������������������������������������������������������������������������������������������������S��PK
!<�Nd���4chrome/pdfjs/content/web/cmaps/Adobe-CNS1-UCS2.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�����_�V��p�y�2��i�	g���0��=�#��|��s��7��l��E��D��/�E�� 5��_��`�)��6�q96n}K8�L�=���7�I��	��>�
��P��}��"`�fK��"���>�$�M��[�f�q��!����)X[Xq�@��X��4��D
�[�3ā�)��P���H�*%���n�)�$��@%p?� �&�����	��C�N(�
4�B&(�|`���z�4�<�h�6 �$�V�b�F�|��R$!��<�0�1~2P
Z�J
�h�bR��rV4�V�@�P�9,^$�<��l��
2�v�R"���[S�H,$*p�V��]��f��r�y>"�Z�c<�	�N��|JT�<���SD0J��<x�L�x$*h����*�_ÉZ(�@�.(V�.>�n�,�"fH�l�|"� �<��eD"�,�	N��L�N�"�*�*S
�}�T.�f&	�>�v.��<Z�p�V�
;^s�,�Z���&���Lg-�j�R�N�`�(�$b�bu��b` \�X ���j�,�rNi^�2�O;�z>6D�L��R��H0��T	

	��*
��Y�
	��[��`6�D�HN�^�4�z�n��b�$�:�d��g	f�V�,�4�
��lP
��pp�\�@�t�r��z�&d�
@������X�(,�6�B��_�j�HUL
K�"O= 18c<1D+"�bN�9R%��AR��l��N>1NO<!(
O@#�%ZT 6
�z
�<#
!")�f��<*,#4�n��[��8$�2�\��<@��X4�(�<F
�N�d$-$98
<GPUL�d�p��T��g\
+2��R�d6t1��l�*c*A6IZ	+
#"!,-�f�	�px�R�F�&�*�:�v��X�f�:�l��2�.�xD�z�Z�l����n<� ("��G��^ 	�J�x
�J	��
^8.�,0OoZ!5'\!'$?"41 /�2&�~z�x�,(�$_S��6@=8+	&?*q
D �V�.�>X��YD�R07D7<�2f�|Z��\�-,
�H	
�LB��^�d�"�_�@84
?&+
!�tR|�0a�
�b�A(&%
1HSL��b�5B�^�P(z2
	
�
�B:ITS*
�g"7H/%�`�
k�&��40e�)l�JoR7'P%Ve	J
#=�'
�h�Gr8<�,�0
�.�>�Yv�z^�`.��Bby��b���*n�r�h+!�j0�J-	&=2+�,D��)���D�
�F� �J�6F	�l\�R���a�O�Y
@5B+SB.O�>t�Z	�~
��F<�� &1J#%&',3	
%.�~�(VA7�	�	L94=!4
�Zr�6�	\M��HJ
�:D
�&HnD�__��/2HD
9P
�8�4r&'-
"8�x
�J"H�!�$�Y:;	6m�

W;J[,(�P�.0\8��R�L,93H'F�}m67M�% O8�~���h��s�$4�L$B�>��vx��v�v �(J"��yF��y˂f�z�~��b�*`� H�>�(��(

�,Z�6E:?8o2K,�P�$:�*�$f�
���&��0�F	�B���h��
&�b�BB
�Ld�l���4�5P#il[`'!j;/
&&o^-K|YXE�d�$UQ�T
�
��(>��Njw/&2WN7		�A"�{W�"�
�
�N�YZ�l	Of
	3�B�X�Q
���8#
�r|�~
��@�+.	
(4 �`�V(�!J'
*'��c(H��e�N�x

	�l��g�,5"W��9h)��6!	34eP�Z�V�4	l&��rTC"=rO87)2D#A\�j#�R~���L67�j�H�		;u��$H2�<�P	Hw(�� 7,%$�D
�#�V� X�n�.*	�<Hh`	�&J�b %(1"(�z&� 	��(��^L%.)4;?P4� j>/ ;,�&�	��}	�<	�lb�	΍w�hJ�B�


�8�	�^�
�R�.	�a

�8�L��v^���}�vC+	#6 Q>Q��>�\p�4�r�Dt�?
S>��p�mF	E2=3H
MhYZala*�`�*J	M*d}�L��&8U	J)�J�,��0�
^]#.+!��*�%
r^��l>(V
	�DF5,D/bS
LA60OB'�,
�bH�.kF]!`"snA
.axI 5%"�D�q�^� D�"
�e΂�4�E��JZBmt!0[�/O?�^��(
�l+�DZl5O\+.,}9BG^4�|a��KH '"
E5�[L�$�p��~�4
�Z�z�$�)u
~<�R2u�(��
�6
�`Q�,��,� 
�F�`Z�! +$!��@	b�Z	�P3�:d�(
ARL%3,S�Q���@�Z��W��8 52��-D3�	�4
		�L ��~�.	�@�r	#$�&�2�lL
	�>�@�ٖl�&R���Z��J*��3+&#*�J���&�Ri�h�|��*LW$-2-^�C�f1@gB%�f�B!�X!	�
"Y`�,D7$��@
�1\1�49$�j�t��?^��n`�#D_��P_RvS4K�f�o81$"�R�2/�PA ' �	icv�z�l�q<�H�te�<�|B/� �g�8KZM*"O>1~	� �&�g�	�4Jd�$�l,�6g h;U~ib.��w0',M@���ah(?Z��q&(n�%�^n	�j#� !#��t4)�	�uk�n
�,�v�@N�d�:�^�:
�.��J!�
�p.'U�#	��_�4
��d�
+@�:�2([7�
�Z�D�56�`Q�ZUQTg�v�
&3)�p�;
T���"$�HHE$%
	>�@X
�"	�%+�h�>�"	
�&�H�R�
��#5(#�	
	�v�	
���7"xT�2�
���<
��*���T�

�P�%.�n
�B�	
�S/�^+$1+�
�U�+9�4
�X^
,?@G.!�6�2!�l
�8X����$�VpSYNE�
5`�dTA#;P�*d�Jd �L�Q2f�%&�j� O'8/@��"q:JS
�VT�&R�HF;eRAFJw
NU�27:
501B%�F.�H
�`+;2��uv�! ��w[*!
��>B��x�()�t�T�_�<
��{w��A��:5.!i�$��rl	�bZ�X'�؁f�<��tu�ED#?V
O�.�22�����
	/����
�f,3x_&1{R�rN��=!��% �6!
�� 
�($�0#"10'� �(�>�Z>?8!0';(
4�z�~�`��V�>:�8
�^�#u���X��0���s,'
>G4�p� ��\�v� 4	5,�!�U�</5�B�<#�~N�
�n%�"�P
��[�
Z�(�B�&�|fvR��V�n�a_8�h�8_	�Y34S*gp�l\j�
�|'V'Ha6!6A<M8A�J�L�~?-(?h(=;>G!,T
*�0�=}(MT�89
�V�&�Z�&
�,��*5��@=�>E
�Fy��@�p��b-B
Pc�h�[}�)�`}�R-18H7��Gq߁|	X�^�H$X�zW&-\'$�V(�hG<39x=�+bW�l5"-& 
!�D��&��kZ	�L�.��x�z
�,�fY$7.Y(��╣�h`
Z�v�`���"�n�H������|��
	
�
Q�,�R��J��vR8}�!X�<(/"��6-,	�J�96]���
�NP�
�#X
$P/4;:%!2�~�t
H&U>%&/6#"' ��qf��b	o089F/1F�|�*�(�o?xg(%b3%8>)d��2�~�Z��d�"B�|�@��v�>R�R
�~
,3&�\
�f�|$!��{�CV�z�*&G&�t0�vnrx�큠
�|JGY���"QpM�v-
"!��d���b*_jX�
,J/B�`:�f�5.���b�7'2�6�D�
�p
�*�	d
G*P}�t�$�e�	�,X�"�,�,
�	�:�>}�':d�,
�J(��M��R��
		�TRd�f�>�J"!&&��P�\,��n:�v�NC.?�/�N�(	�:�d�-$-�j�"!�<"'?:-	%R/� :��x#!B?85!
&�9(��sh�f��"�X�t�"�|�zF
�\�<n�4
� ~
$)�Pp�
-�r��$�#�;�C
F�H6+�&�
�	�~&
I�.X��

�\J�@�'	�
�h�
�r�:�*}t	M\O**0+�B����Tp
	�O��fb�.	+�V.
�$�|t�P�-�x	�p��e ��0�
�B

�*�x`�&�d�@�`�<7�L�2	
��f��R&!:
|qK0�v�"VZ�0�Q�o��_�V)0�l)�( �R�l�:	�:�
	
��
"�J�X�N�
(Y�~	
�  1"&	'!�(�\�JK�p�$R@�N�pM
:4))@=6��0 =<7�^�\�B#*3�`f�	�X�@�r

�~�����p�R�`h�5�W45g�J%#(-
�����T:�
�d�d�^�|��\����ƞہD��S�d0�T�>�H�
���
�d��~�H��,�
�k_0l�2,��q�
�|�b.�*�
�T�
�y�>�b�	�
�.~^	�\�`�v�,t�MF3�j
%�JV��f �H�h�&�T��r	� >�Ї 1�x
�
�
�|��&�L�`�=�l�P�d���xfk� �~h,��s�b�)zX�$��4��I�
�� ���Z���*�0�F�0�J�"�.�r��|8
�T�
�~s�"`�>� +�����)�B�D4	 �ˋF$�\�b(#
�>
�<�ۑ��d!9�I��+ �f�.\�r�t<�D
�@���f�4#�ZQ �J	@��y�|�N�6r�p�$�\�:	����"�:�T�&�$��6\�*�/���c
��f�2��(./	�'A�d�*z�n	
!�F�v�b,�+
�F��&��,��:
�Vvd�^�7jV��"�.�@�\�<�p�&�"�n�2���\�(�R�x�|�^�4*'��� �l
�*�l�&���f
�l���2��N�b�Z� ��6��H�"R���Q<��j�(�v�`�H�J�,�^�N�F�J�F��X�<�B�$���ێ���L�v�/���R�R�~���\1�.�8nR����R�D��F��^�2�Z��*�b�0�
�&���X��W��ц��r�(��G�.��b�%wםpn�ZP�`�?�F�H�R�.
	��R�\�j��?��^���D�����@�>�`��X�n��P��Q�j�~�8�"�0��~��4�_�|�8b���G�T�4��e����R��Y��a2�0�H�N�wt�
��,��I�n�s>�H\��F�^��L�DP�F�\�B�"0��T���V	�b�$^�D�Y.�"�Pp� �v��~��@		�d�2�Z�"�&��!&o<G (78!:�<�B�*�vZx�x�2�	��	�,�|�,�&�\~�@�*�z.�2�@�
	
�"�n��HZ�
pq�X�
s��6�(�j�6�L��`�4����	��7L3 MP+=,W
B�ZR�D�:S	�2�Q?T0RYH"3	D�L�6�fr�J&%.
�X�*�D	�R�x	�n.�4�68
%,�
���b�- #
�v�"		(!���lx#0=
`[2m*$�J
�P�6
���s��N��v��v�l�@�f�-�x� �>���u�H9#(W�[^C.!$U�^:�l�RF+��$S<�*�d
;3L=nk�$�j


�2AWb
(�p�^(92
$K)$�4Z�\�L	(
 
�`��Pv_TZ	�@�0OPU63+:7$�nP�^8')&9	%��T$�&�J&�t3B98%!0%H-
�G�g�� �4�>|�j�0+G:Ora0LASU$;�:=�^�G
/�U21�')./�L�V$
�$	�~�5u>��,����
�d�~�r��n�2<(�d�~>+#2=6
0=<)��:H�V�v
�4���mC,$=N)9
d�.�l�t|4�B3R����-NA�	�T�a�56��V]��2�W�1
�<0�
�")8
%9+:�t�F�	#��\� 0���^ ��BLN^�`4:)3"8w>&�
�b�z-01*)��b�45� �L(�l+ 3	 ;,�#g�`UBCxA1JU.5�C(0}�mLC=n<�
:g�L?�8�2b>�%Fm(8[a�0_(@A#8H�,<i`s0I23�L1<�mp��.� qrl�62	�B�
lY$�@~�
�u����'���x
�l
� �b�$�	�|z��,p	`��^ʀD�@#0
:%
3�bB�d�H?.W�H�+: ,#


	2
��0�R`�T�`� i*!����a3+M|�*P
G�rl�8Q�
�$�?RU�T����	hWj7;J)0�WT�Z�]ẂJ�;1	/�v��4rZ
&(�pt
�,�$�.$#
�^�~
	�l�jM�@5"#65"
	!(�X�V:�
U��c6�@�W"'.F	Y2)L3</�	�~��e��(1�\�'�h;s4+0<!/"
<'+�p�(�u�:z�(�/�mv�S�&`uB	�B�aQlO;D*�SpD�]�X4�%�Z�/�*p�$�@j�


�"
5T	
�Is�Nu!r�  
	�|�& %�ew"�v$

�D�
}y��P	�q�z�� n�b	�0�t	`�*N�	
�Z�
,WnY`/7
>gEf o	,
:k<I@��
%/#+>
%���m� ��r!&�*�hFP�$x�4�T	
�>�	!��	
	�V�,����8�L�v���a�`
"(U2#.�^2�LPF
Z9
�|�4�N�2�\�t]U< 9L YBsJd/+7�|�f0e&5.'!$1In3X%&S278�.�"Mn;
3 )2,'�
�!)$FE)2�*	�$�0�'"
)670-@= 	��x��_6�D�T
n�9�`�(+/RI$�bdH~?!
&;?.E�uL_�eN�S�FO		;�D�r�.	

�j�@�"h��T�OfizG14-#nU8(/4Y*�p�?k��Ck��:�*+��xW|EVq.1L�(Ym�0U^m4'
B?RM*Q�|$->G.9&#8)'(�^� �r�:
!�R�Z	
/&�6�T��u��|
��J��w41*
�$M<�&��yc�2|�$�z�	�H
7"&

=�`�"3.
��}J'�tb
b�M��
�*V	�""�\L
��6$7'`a4&I<(Y
 ?LO0cL?Lq�D�P.
&1� L�Ñ O�Rl$
!	A�f�&	�4�4� o�}N�.�

�,
�
 ��$3
!(�
� ��ԑ
 ���X�,)(��V ���
��f
��T�2�@�b��y
'.Akf!�.�h�z�$�R,m��GJiB#"O2O*'�z�8=	g�$G.�J�7KN	MZUP/0�o�
):]�;zF�NU6E.+N/#�|�,�
�:!$*)�E.#!:7{�(#�|�8:,Y�2�US**C8�V�C<1&#N3�0h
L�1!ve�B�R!��s`Q�}:s@"8;%Jwv�N�y�8ES�9QX)D�
�!�h�Ghe(V�=�b�.	Nd�!�l0�DR|�%!�nE8o&6*�/,. -qT^an 
e/D"}� )$EFO&�A!�nZ�
 id'"?�^Z
�H"S�B+
"�f~�t�z��"1wD��f�4"=x\ �"�"�(��J.'�8	�l,! 7:#3(1#�"u�h�r�"|��U�\'�V�"���Y"��?j�
�&RR�GNRW.
2�DI�IL=-<Y~
cd�	`=Hg0P�sPPUlIp�D�2'% /�l�H8=

�q��
�"�N)>3�%"�R�Br	�	�



#%�R'�7#��
��,JQ
�:�@4E,/"'DK2% �H"�l�#R���
Z�v�0�B��t�$�#cN��1*
-&
% 2'�<�D�>#zU�.$;&!HU$��
RK/�"�|(	+	J5	=J��#-,)S�
�#�\&�z34
��<	�r
	�p�\��#�a+2		./>M,�>�����@V
H,I@�-#�d20
KJ79If$'�@D��d
9�N�
O*&e
Hc(@�#$&i5lsN�k��	hS\Wb=/dQ#)H?>G��
�2�$Kk��Bts`C�E$Vn� +GT%8OP;0		,:8
g,�F ?6
?*+H#�hz�Z

�t�$�tR
/�$�tN�:>�6�i�V>.+�66N�n�$�wR'&�d�L$
 �$�x�=>3*7�
	�.�.$�z�$	��t,'��0

&'!(�4�%lr�`%�!�	,7�#�x.W8�vV	�V�e,>*Q)#�
?o8^G4-�R�SO
�
oh�
$9�!�b9$+b o(
8�*o�'#h*u3h2=�t(%I<�.�+*,� �#%{���h*�

�=En
�4>�%�����%����:!=0
�"�%���'�$�#%ђHEVgbH�+6	L; sTD'BII�X�
%���
lV�z�8�&����j�n�^�
�0�x��/% 1#0�n�M&%R���v�t%
-$7"#D3
�&%,#1
0�\�X�`"!(!$# 	�.�P�~�<$ )
�&t^S�
�^V��j	08$�&�ar
&AJ?U\�$�~B,�D&�d}~�1fI#!\�X%"�vX��J:�$SZq2	W056(3^7 ah9A,t�b!@��&�k��L	"B[|ydK��;&�o'DSK8Vx�u|$!�6h}�;Az7% -0�J)*�X��^�* �"�!'5u}$�
	�F�)"#�\UX�f�'Xy��4<5��!'iz�2&%
$��)��(E�p�X?,Q`�'�}�GJ;N�R�O'���
p�X
��4&-G!,<g&3dc
Z}:0_lW?��"!�W%!N/(M"UN�'�8)*?�J!�'�!
	�b��%�L�(��
�|�d!�D(��*
�<
�b]HCJ*;0 e:1F��A$K0/	
SB%�=�`�:�0H���(e��*L�Rb�(x�_�*�b��QXq�j(�R��0
/"5#"
�>� &#	�:�~

�.�b	�	�,�1�N�P�0n3"6.)O��r�J7"	,	o�3�:�s(�f�� 
 e$8IvC'p?Pk	J;!�0qE("##:&-3^?'B�t�&�4�|7�.|i	U'jWvA*[8Axm�;�@"-P{nE**e& �l)(!
1
�H�z
�)ms]
�H�)vt�	��l�L�
)�v��b
�%&
�h)�xɃ 	
�K��p�&%B? +��V9!B)�,j~
�v�v	�|p�$,YP7"+8C^c1jqD=*QX1R Y@Qt�*��<K*�d�f�"*�w�R�]7	5 "%-V
'�`�T�*7��0�	�D4�"*I��63$+6�H��*

�p	
�3
�
*m�
�"
�*|�s�	�6
	��=*���0G0B% %
#D[J=
.3,-=V��?%�^�f`b
�.^
����*,�*љ��'*י��Lp�(F	��|��6+Q��*�X�!)0�H�X
� 

"
� �x	�(+8^f�r�J�`�B%\#"%Y�

ir..5�2�J�J�#+bf�%"�,� b;:kjU<7Sb?P
)4
IB�+�jF�� �'+�k�x�L:QF"
�U�J?VE�6�?H!-L=B=�(:�	+�q�%49�+�si�+�sl
�(4
�R�h�\�T�f�
+�w��
	�+�x�+�y��>�\	
�L K*
'�d�,~�,~/ �<�,)zlzt��		!��0,?�V#J=
1
:G@=6K8*G>A"VM �l�~	(?6=�	,q��3#�(�,|�'
�"�\�,���la Q*,!�,����H�pl�
�,��A#�z�p��fp�,����,,����.-,3�T�C##�3?��"G05xeBKR	-4�J �C,�VTl��6x
�b�h��"
tv� -41�8(%
�P�-3���-7��.T��/	��<�-DV��(�4��H-[]��h�&�T� 

�0jF�n�t+F9*	
/%H	'%L+
��"�6�G�A 
&+
�p� �	-�t�472�-�u�h�|�-�vG�8�����-�x�$
�L�f	
�F�-�|;	$5,1
�n-�|
�:�
�(�!<=D9�r�lr�X�b�L.%+.QPO.OCl!9@A
B��*).+		4
+(
-@Y� 7�T�8.[�

,'
*�h@J�H�j�<
	�v��r�B
	�=V�hI2[�0.��`B�:EB
53�Z
�l	�f	�n8�
	�B�.Ǚ*�N�@
�.ۙ��t~��"=6�Jc�.��1(�.��>��n�8D."@Hb���_�/V�	�,���.�/"a�
+$�*�.��/3f��XF7'�`�T��v

�"/Ko�TU$�l

�p��"��,\�*�"� /oy	�6�d�x!!
�V
	�B�/��u�5/��Ђ�*"

)6�"�Y�T�z	
&!2+N�j
�,/ˉI�@	"1"#,�dF�L
�6
	+�/��K�8�/��>�R�S
H9):36# 
?FA@#�0�уBF��1��8�
�

bB�0:����(	�r6V�-0M�)(�v	4;�<	 !�f�$B*
F�0|R��@�x�l�Zl�#0�]ÂX��v�	�j�2��)&49 ���6�(�	0�p)%�^�	0�r��R
�$��	0�w�X�V
�"�0�|3�`��Dp`�0�x�<j�0�
	)(

A<#�:
�
1�	�>�1�W		\\~�2$	�VfF�15�	�D����1E�S�@��`�`/.PAax92#"[��*�1e�����1q�\
�N�	�1~�	�Z�T�1���
	9(!!�8
/�1��}%�1��p6%'
?�V\v`�1ßM��-�t�^�B�f�:���
 �1�p<
 *�1�r��(�z�`&\�<H�d�2y$��v�2
z��"�P��r��r�B�
2�"G87	�6�2.�	��$z�P
��.	�(�2D�U�d�j�j�!2N�1H#-,.1	1#�e-4�.�2q���H�92{�P�<�2	��r#5�s �*

'
6-'�2�����X	n��/�<�V�2�[H��B��:�L
�2�pJ�2�r�H�0�$�J�8�H�<�.�%2�~��~�Z�h�X�t"	�<�D	��|g�	3��4�<�"�P�$3!���
	&&�f�"�B��H
�.�D���3G�F.�0�3Q�$�*

#
0�3b��6/	<327�B�Z,8,Z��3�
�J�
3~]�	�@�\
	�P���
3�r��*�0�*~��|�3�|W�2�>�zn�,��3��3
�j	��x�

�*�~
�3ď`�H��	3ДF�p�F��B��d�=3ۚL�~�"4�`
�H	!<A�0r�PR��K�6���4���,��A�b�4e)	�`�(�,4 k�t� ��>�X�F�$�\�,�
�b	����x�&�|��0�J
�4N��*�4T�R

�X	)"+�(�	4u�*6B�4�V�
��<�~�4�k�	��n�^�D�|�f�X�b�F�*�h*�4����@�P�4���@�~:�p�l��		�B		�4��	�6�zN��e�2��<�����^��F�D��>4鎥��*�P�X�
�:��&&z`��7����j�f�8�`�T��
�<�D&�2��y�"�d�&�<�z�>��5)���(��p�
5/�$�`�`���,��H�6�2�5;�i�`��&�F�j�z����j��D�5Ir)��t�2k�����D_������4�p��|��|����~�/��y ��*��2�6��@�́6�NZ�6��@܊�6�R�c��<��Q��P�6��]��Z6����\��;��\�7��>�n�U��)�,�(��[��M""��1��4"��)��*��-�7���S�N�7+�O�ׁ7,W��7-�[�Ӂ7.q>�7/�U��70i��71�b��72[t�(�74�V����n�76ze.�78�Vݬ�79z��?�7>�Y܍�7?A�����w��7E�W��7F{�Y�7I�T���^�7K{�g�X�7N�Vߴ���7P{�� ���+�7T�W� �7U����W�1��{��n�7Z�W��7[|D|�7]�R܂�7^|��7_�Q�x�7`|�<��S��j�b���7h�[�D�7i}]�7j�[�ց7k}�B�m�zD�7p�P�W��"�7r}���#��B�7u�E��7v}�7w�`�m�7x���7y�X�!p�7{~n��c��Z����>�#�7��X�Ё7�����7��X�Q�7���7��E�a��s�7�E\�7��M�f���&�7���7��Y܉�7��[��'(��>���7��h܇�	�7��a�7��I��7�I��7��Y�&ЁЄ�7�g%��7��b�H�7��$��U�7��I��7�l�7��Yܘ�7�d�7��E܊�7���7��F�^�7�jS�7��R�e^�7�Dz��\�7��Z�R؃)�7�O��7��E��7�����7��~ݏ��Q��o��{��7�����7��~ݔ�7�A���^�w��7��C���:�7�6��7��L�}��8�7Ȃ{�Lj�7��Oދ�.��=�"��7�=���:��c��0��u�7��[ݥ�7փ��� ��U�7��[�ԁ7�jW���	�7��[�B�7ބX�7��V��7�q��=� ���D�e��7�7��Zߖ��@i�7����<��Y��c��d� �7��Wܑ�7�B@�7��W��	7�EC��`�������;��P�,�7��a�%*�7����7��\܈�8��8�E݂������H�8Ej��z��A�8�Fܢ�8S��8	�\ߚ�8
�~��|�8�h��8
��8�K�'�8��Ph��]���-���8�]���O�T�8�^z����R�8!�]��8"����j��-(�8(�b�%�8)��8*�^�$�0�8,����T�]�80�^�Y�81z�82�^�:�83G�84�^�8�85q|�$�88�U�0�h�8:�?��z��a�I�8>�R�z�8?���Y�8B�E�߁8CF��r��8F�_�Tt����8I7%�8J�_�S�8K�փ8L�_ݘH�8N��8P�F��8Q����h���8U�~�ׁ8V���8W�`���]�j�8Z;|�8[�`ݼ��a�8]z��
� �w&�8d�`�e��X�8f���:�8h�hޟ����8j�����8l�b����C����8o^��8p�L�b�8q�����8s�H�%�8t9��8u�[�ׁ8v�7�8w�a�<��}�8y�a�8z�a�l�<�8|���8}�a��8~�ă8�a��8�����n���4�1�8��b�3��U�8����_��B@�8��Uݹ����W���`�c���U�8��,|�8��b����8�ZÃ8��b���8�Ie��<�8��b������u�	8��s�N�?�@��r��U��{�8��a����9�b�8�I���x��<4��U��l�8��[�Ձ8�a���[��q�8��b��8�����c�W�8��[ܸ��	��6؂<j���)�y��h�8��1�_�f��O�%��8��R��8�pu�i��2��B�8��d�Ձ8̗W���8��d��8ϗ_�u��+�8��Lܷ�8ԗ�*""�8��U�l�8ܗ���%�8��e�3�8�����8��e���G�8�J���D.4��1�8��\�u�8�=Q�8��A�0��V��R�8��' ���8��[�'���)�8�U���`
�i��8��R�)�8�Kr�8��f�W�Z�8���,\W��9�U�%�96ă9�dܱ��F�9���9
�f��9����M��
0�9�a��9P4��~��c�9�L�}�9�P!�9�g�>�9ZE�9�F�c�9���f�9�g�h�9�ԃ9�g߷��40���v�9%�~�9'�h�4�9(���9*�hݓ����K���n�9.�9�9/�hܹ�t�91����]��j�98�P�d�99����C�w�n�(�i�9?�c�+�9@�
��e���9D�[ݢ�9E��9F�~��9G�9��5+��t��9L�d܋�9M���g��z��)����b��q��1�9U�R�u�
�9Wqw�9X�R���a���9[s��9\�b����A�9^y��9_�b�~��n�9a�i��9c�b�D�9d���(��9g�b�l���g�9ir��Y��� �g�9n�Q�s��w�9pq~�9q�E��9rp��9s�Fܾ��4�9u>ǃ9v�F܅��R��o�9y7"�9z�E���z�9|6�9}�E�t��8���r�9�7#�9��E��9�W[�9��R�%�O�-�:��^�9���9��Mަ�9�����Q�9��Rݔ� �i��T��C�,�9�D�9��M����\�o�9�g���
�B�"��9��F�"�9�h��s�9��M�?��B�-��"�9�T��$�9��Yޱ�9�V��9��C����o��H�9����9��b��9�����I�d�%�9��Q�6�9�N��9��A�e�9�����|�9��U�Q�9�Zh�9��@ݫ�>�9�9��9��@�
���9�45�f�9��@����d���}�9Šڃ9��@��9�N��h~�W�?�9��R���	�	9�OB�Vz(�k0�d�F�r�9��Mݕ��F�
�9�mr�9��F܄Ȉ�9�Q�4�9��A��9�D݃9��Aܣ#�9�z�9��bޜ����9�RY��9��B�s�9�R���I��x��"�9��Pߌ�Y����9�iу9��C��9���@��t���}���9��R޼��_�9�S�����9��P�Ɂ9�U��9��@�E�:T̓:�B�Ƒ	:W��~�,��/�!������:�b�F���h�?�:Z,�i�
� �{�:�V�F��'�%Јf�:6����z�E�:�Wރ�:Z��:�b�����	�: ���)�:"�D��:#q��'��*�:&�b��:'����;��z�~�~-�:-�Fݖ5��b�:0^��}���:5�Fܠ�:66݃:7�E��:86���,�::�b�G�؂��n���j�:>_�:@�F�'��y�:BZk�:C�E�;�:D[D�:E�]��:F�`��E�:H�J�`��k�:J_���G�:L�Iݯ����+�:Pa��:Q�@�>ȇ(�:Ta��G�:W�Kܛ�V�����p�:[dq�:\�Q�e��w�:^:)�:_�J�"��Z�Ȃ2��e�:cc7�|��>�:g�R����y�:ib��:j�Kܡ�:kd;�^���}�:o�L܎�<��6��=�:sU
�:t�L�������J�:wf΃:x�Lމ�-�:z:�:{�U݄�:�'��'��<�m�:�x�:��L���3���:�4d�:��L�����:�f��:��[�$�:�fk�1�8�:��^�p����:�fc�:��L���:�f�:��V�r�:�8у:��N�:��}�:�;��:��Mߢ��I�:�t���u�:��Q�*�:�h���s����r�:��M����b�:�j3<�lv�:��F��:�e��f��k��D�8�:��O��M�:����]�:��b���?�:����:��`���z�:��g���J���:��O��:�=���h�:��P�<�:�Z=���8�:��:��P��I�:�p��<l�:��Qߏ�U�:�Qy�:��Rޤ���s�:�tz�:��O���3��T�g���:�?�+�:��R��������]�:�`���i��0�!��!��1�:��[��:�EQ��b��A�:��S��X�:�?X��x�:�:��i�Ɓ:���T�:��b�����C;�:�:��"�:��R�N�:�>��:��R���%�:��������:��b�����:�P߃:��T�!u�;wx�;�L�2�;w{�;�Qޗ��-�;:^�;�R��;	t8�D��9�;�R޺�;@ȃ;�Rޖ��.�;��;�U݁�;x�\����;�]�A��=�;A��#��N�q�; �O��;!z:�
��
�;$�E��;%A2���	�;(�C�L��~���g��@��;-5��&�w�r�;1�@����p�;3z����;5�I��r'�;8Z
�;9�@�[�;:x��
�;<�V�Ɓ;=z�����F��9�;B�V߳��-�;E|̓;F�R���;H|�%b�y�RTi�t�;P�X�a���V�;S}��;T�Wނ�;UBj�;V�Z�u���?�;Xgփ;Y�@�N��;[Wă;\�Y���5�;^��m�;`�B�,���x��2�;c{�;d�W��;e���C�:���;i�M�?��n��j�;l��;m�Yݠ�;n���;o�\�'��-�;qD�����k���p�;w�Yߴ�;x�Ã;y�Z�B�;z�b�;|�Z�Q�;}�S�;~�[ݧ�;��;��\��;�Z���;��F�@�;�[+�;��Fܡ�;�Z�;��F�؁;����;��~ݼ���[�;��-�;��]�"�;�Z��V��S��nL�|1�;��Uރ�;��T�;��]߅��`9{�G�;�>���j��]M�;��C�;��e�H�;�_M��:�;��Pބ�;�e��(�K�;��F�E�;��m���;��_�܁;�Y��P�'��9��*�x�;��S�	�;��02��-��F�D�;��H�!�;�H���:�]�;��E�ځ;�����i�;��R�/�;Đ����x�W��h��;��b�K�;�@b�;��b��;͔'�;��c�:�;Є��
�Z!�;��c�4�;�tE���;��R����1�;�>�;��L�%�;ޖ��;��c����;�>̃;��R������;�t�0��_���;��dܰ�;�hG�U�;��dܓ��i�;���+�;��b݉��8��o�c�;�����S��.��[��x�;��P��;�����B���;��C�&�;�]��<�b�����'�<��<�e�ځ<���<�e�ׁ<����K�<�b�P���S���x�<�܃<�b�E�<
?��R����\�L"�<�^��<=�����b��t�b�<�b�e�;�<j���"��G��`�<�f��< K��<!�f�H�<"����M��J���<'�D��<(X"�<)�Rݸ�!�<+xD�<,�I���0�<.h�����4��c��P�<3�I߁��R�<5a��N�
K�#��<=�g�-�<>���<?�h��o�<A�!��g�<C�P��<D���Q��6�<G�hݴ�
��	�<K���Q�<M�[����;�<P�����<R�[ݤ�>�)�<U� ��<W�[��S���<Zy�<[�Mݜ�F��:�<^=b��p�;��<b�@޿�<cx��z�*��S�I��F�<i�U�]�<jx=,�B�W�<n�E�W��b��n�<q6��.��N��9��@��<x�Pݵ�<yW��n�S�����8���<��Pݬ�<�q�<��[�@��S�<����<��Eߵ��&�<�a�}���<��Q܈�<�7,��<��b��<����<��F����s�<��r�<��[����[؋L��)��4�<�Y��<��OݷІ�<�}��<��X�[�o��W]���\�<������s���<��b�c��	�<�>�<��R޳�<�j��4��K�<��R�>�*�w������x��<�tH�<��R��<�p��<��R�v�<����=�p�<��A��<��1�<��b���q�<����<��b���%�<�I5�<��bނ�<�qk�<��R�C��	�<�V��<��A��� ���<�U���<��E��<�}��<��E����}�<�E.��$��o�<��b����'�<�Y��<��Fܿy����<�Z����<��O�[�<�6���\��M�<��bށ��?�<�t��O�<��B޴�<�J���R�;�<��Dߜ�<�qu��u�<��`��<�n�<��d��<�D���V�<��_�O�<�pg�q��3�<��O����<�n���;�<��@��
<�uQ��+�i�n�M�,�$��R��a���=�c���9�=
X���e�=
�E�R�=p��=�P�b������g���=h��Z�=�Dߘ�������+���F���[��9��H�=j�=�Mޭ��=!�>��?#�=$�[�&T��6�=(�����=+�E�:�=,��=-�[܀��$���q�=0Y��p�=2�E߳�=3Za�=5�F�Ѓl�=77-���=9�E�<�=:6�������m�=>�Pޥ�=?Zn��=A�Pޓ�=Bj+�=C�O��x�7��#�=Gq�=H�P�Y����=JO��=K�[�(�=L\ƒ=M�Q���|��!�=Pj�=Q�P�V�%�=S����j�=U�P��=V=���r�=X�~�%�=YY��`�=[�L��=\|Ӄ=]�^���}ȀB�=`�l�m�U�U�K�V�n��Y��m�.=mN!��.��[��:�j�M��V�9�O�k��*�t��}�<�q�i����l��e�]�]�Y�-�x�[�4��2����T�$��&����"��1���N�t�.��j���,�l�=��^�y�=����=��Rݺ�=����=��@�F�=�g��=��]�W��k��6���I�=�H��N�=��bޚ���?�����w�=�Q҃=��A��=�Y��=��Gި�=�;��=��O��=�9��=��a����@�=�7b�=��H�Á=��^�=��b�N�=������=��C�x���=������B�=��O�J��8�=�U��=��Z�R�=�4s�=��]�2�=�G�=��R�?�=�Pf�W�=��L�́=����|�=��b�H��q�=���=��_��=ҐV�=��Sߚ�=ԋb���=��Z��=���=��F��=ي�=��R����=�K��=��I��=ޔe�=��U��=�a��]�=��~�����K�=�Nj�=��R�4�=�V���=��[ܽ�=�w�=��Y��=����=��d����_�=����@���=��Y�i�ȁ�=�y��/�=��M��=�����$��T�R��_��5�=��Iݍ�><T��X�>�H�z�>�0�>�R�Ё>l9�>�C��>�'��>	�J�&�������>n��>
�R�*��>9���O����l�>�gܭ���>�C��I�>�E�
�>9��>�M�/���p���G�>q��p�>�dݨ�> ����r�>"�\ܯ��v���c�>%{C���>(�h�߁>)j�>*�`��>+S��>,�[��>-������L�>0�`�o��u������>4]��>5�c�0�>6W���>8�b�I�>9]��>:�R݈�>;e��,��>>�Rޥ��I�>@<��H�>B�Gߡ�
>C5��^��{�����G'�0��w���>M�e��>N���#�q��j�S�K���>U�^ܝ�>V7:��{�>X�`�$��M�>Z=�>[�OܷȆ@�>]V�����C�`��C��d��'�>e�gݘ�>fL}�H	��T��)�>k�O�@�>l�p�3��
�;����o�T���'��*�\��%���J�7��'��n��?�[�>��Cޝ�>�5���l)�~�>��`��>�6-���	m�G�4�>��C�A�(�>�^Ѓ>��C�v��V���>�����#��;�>��D�u�
>�b�����3��p�/�~��u���+�>��J�C�>��r�>��K޳�>�Q��w�>��Cݧ�>�Q��J� ��$��>��Z܊�
>�u�����p��m�#�J��a��.���!��h�.�>��C��>�rf��1�>��_��>�@���D��}�Y�>��Q�����6�>���>��]ݣ�>��+�>��X�H�>�q��T���3��t���>��Q��>Ђ-��E������B�>��gެ�>�M	��F����:�>��g�ށ>ۄ\�>��\޲�>�c.�l��w��:��y�1�u�\�>��A܎�>�v@�!�>��C޶�>�xz�>��_�.�>�X��Q�x����/�>�>��i�4�>���I��I��z��:�>��P��>��s��R�>��g��	>�R4���a����M�D��7�h�~�?�e���7�?���?�C�(��?	���b�?�G��?]���h�?�G�ё2?���k�[�D��c�"�w����a�4�Y��������3���f���V��O��
��4��M�Z�`�*�#�@�4��j�D���g����
����v�%��^�n����
���5��>�?A�E�m�?B����O��U��a�N�K��`��y�?J�cޗ�?Kc��s��x�)�k��@�?S�W�!�?T����
����g�?X�C�ԑ?Y�����B��J�j����B�*�O�D�?d�@��?eO9�D��?��8��N��	����j��{��6��R�P��S�
�
�ZI��a��Z��n�:�A����2�?~�i�J�?�w�6��n��o�t��7���?��`�}�?��(��E�+�M�]�x�A��<��1��'�?��h޲�?�xS�p�U��v�W�\�r�?��R޻�?��/��k��J��S���?��[��?��2��i�?��I��%?��5���R��^�X�-�@�5���~��
���h���5��6��W���p��c�+��"��3��a��"��l�[��y��<��
�q�r�:��a�����?��c�9�?��^��t��X��)��
�?��C�;�?�@e����Q��R��M�?��W��?�|ڊ[�p�-�'�B�u��*�?��]܆�?�[�W�$�?�H�?��Y�̑?�uS�*H�
��x�F�U�1�g�B�Q��f��J��G��&��v��?��P��
?�t����4�%�n����)��r��;��S�@�~�"�@	`��W�@�A�G�@�ۃ@
�A���J�@�L��/��r��u�Y�@�R�B���B�@�D�@�F�ہ@��@�O�ȁ@x<�q�K�@�R�3���l����Є$���]�@"O��@#�I��@$P�@%�[�)��4���	��`Ȇ=�@*���@+�@�~�@,_��@-�@߀s�@/nۃ@0�A��@1P�D��&��:��S��$�@8�Rݤ���;�@:Q`�@;�Lߴ�@<Qj�@=�B���x��/���E؂H��a�@D[���t�@F�Aݳ�J�@HQ�
��v��W
�k�l�@P�Rަ�@QQ���m��^��>��@W�M�-�
@X_{�a4*�p�=xH���X��E��X�@e�Z����*���@hUI��@j�O߀�Y�.�@mS3��m�@o�B��@pl˃@q�E�&�@rh�r���A�=�@w�E��@xq���w��@|�aޫ�@}S~�@~�~�2��9�x�@�w���'�@��bޛ�@�S��p�@��U�r�	@�?Y���7���h�K�]�@��Bߏ�@�S�@��O�����@�S��@��C�w�@�T�J��V��]�@��Z�S���?�@�U]�(�}�@��E�
��a�@�=��@��C�M���\��I�@�UG�5��N�#D��Y�@��F���D�@�ER�@��Q�5�@�f��@��Dܴ�@�V7�*�@��Lފ�@�f���O����B����*���#�@��b�f�@�6#�@��D�O�@�WF�@��Pݥ�@�ln�8���#�@��[�~�@�W�@��E��@�X�@��E�T،�@�X&�@��R��@�X�����|	�@��D�<�@�X���y�@��h�P���1�@����e��z���G��*�@��E�>�@�Z$��O�@��Eޒ�@���y�@��C�N���d�P����@�Yك@��E�����f�@�mq�@��F�(��'�@�Y��@��[�E�@�Z����{�@��Rݩ�@���������%�A�[ߡ������A72�A�E޸�A^��g�J�"��A
�^��A@�0�A�@��A[ՃA�F�4��.���O�A[��-��h�A�F�D�A\�t�u�A�Gܥ����A\I�Y�N�F
�6�A%�G�z�A']Z�A*�Gޤ�A+\��8��H���A/�R�Ђq�R�A2����#���V�6����A9�Qܼ�S�A;S��_��u��T�A?�E�q�A@^	��#�AB�a܂�AC^���7��6���8���AJ�@���$�AL:ރAM�D�:�AN_:�AO�Z܈���q��@�AR_c�AS�[�n�AT_r���AV�b�6�AW_��c��/�AZ�T�P�M������!�A^�փA_�@ޞ����Aa`1�&��E�F���,�Ag�Iߴ�Ahyq��c��:��C��X�Am�R݂��Apt����|�E�Au�~ܦ���)�Awa���Ay�A�V�Aza��;�A|�O߷��1�A~aӃA�bߝ��~�A�a���=�A��J݀��A�`#�p��'�A��@��A�bŃA��E�p�A�bՃA��K�
�A�cl�A��R�߁A�:��@��A��Dߎ��Z�A�o��A��K�6�A����A��U��A�d���*��+��&��G��j��A��R�(�A�e�1��E��n�A��R�@�A�K7�2��s�A��F�)�A�����	�A��M��A�fV�A��C�u�A�fg�A��T��A�fs��C�A��L�1������Y���A�wŃA��J��AÙ���E�A��Pߜ���A�i��A��M�����0�A�gg�A��P�����>�A�g���]��p�Z��V��w�A��L����r�A�h]�A��M�o�A�i�R�A��b�߁A�is�a�A��M�́A�i���A��M�<�A�;���V�h�A��b�J�A�B���r�B��o�A��@�ɁA�c��'�P�A��M�Y��_�A�jE�A��M��A�j���U��z�,�A��dݜ�A�<
���A��B�#�A�`��,|�A��I�́A�n��A��N�ہB���B�F�X�B7@�@�B�N�Z�Bk�B�O��Bk��4�B	�I܋ȁJ��>�BlZ��[�B�Oݼ�BDŃB�Oݽ��L��N��o�B6��
�B�C�2�B���B�L���Q�B��1�B�[�%�v��B!m�u�>��B%�R���u؇X�B(�3�B)�[�t�B*Q������B.�b�!��L���3�B1tS�B2�O߂�B3y��{�}�B6�L�K�B7o���W���B:�O�0�B;n��B<�Eܗ�J�B>EU��4��Y.����J�BD�d�p�BE=;��F�BG�P�D�BHoӃBI�Pܑ��9��y���BOQ߃BR�P�@��8�
BTpKdP��U��$��7�9��=�B`�P�w�Baq+2�{����Bf�P�e�BgqO��$�Bi�P��Bjq,�Bk�Q�Z���Bnq��Bo�b��Bpp�� ��f��[�	��b��?��8�Bx�U��Byr."�B{�R�t�B|h��.���B��L�D�B�h
�^�����Z�B��R�#����B�H�B��b��B�s(
�@K�B��C�:���f�B�s��~�B��P���w�x�B�f#��=�B��RݷU�Z�B�s�:�%�B��R�&�B�t9��E�B��~�/����B�t`�B��c޲�
B�tG�G�"���x���*��_�B��Rތ��[��2���*�B�[F�B��c���K�B�tȃB��F݈�B�u�B��c�����B�[׃B��cެ�B����q8�B��S߂�B�?�B��S��
B�u��c�*�RH>��F�B��X��B�v��<��o�B��Z��B�v�"(�B��[ߟ�Bڄb�B��Tܝ�>�C�B�w2�#�B��T� �B�wX�B��Lެ�B�w��B��b�d��O�B�w���o���H.�"�o�B��U��B�x��8��B��]��B����i�B��I��B�y��r�}�z�B��V�W�B�y��B��^�9�Cy<�X��C�\�&�C>���:�C�d�
�Cy���/��N��Q�C�C�B�$���*�CU��C�K�ƁC9�C�@�A�C�F�C�Sݸ���X�C���C�`ܾ�Cwz�C�K�8�C:4�@�C�`�]��W���Cd݃C�C�|�n�?�C"������`�C%�Cޖ�C&���C'�C�d�����X���C+���C,�C�F�����I�C/G�C0�Sާ��K�C2�����C4�e��C5?��O�C7�T������C9���C:�K�D�C;�n�3��N��6��9��l�#��M�CC�J�ʁCD����z��}�CG�Y��v���CJd�CK�T�R����CM�h��(�CO�b�L���7�CQ�����CS�@ݩ�CT?��p�CV�Kݍ�CW���CX�Q��������4����C\����v��y��x��{��l�Cb�J�f���Cd����Cf�Cݜ�Cgcy��w�Ci�I�u�Cj����?�4��s��
�Co�i����q��
�Cr���Ct�D��Ѓr�Cv9��f�Cx�`ޛ�Cy5x�Cz�W�I���C|���C~�Kܲ�ЉZ�C�4����C��S�;�C����
C��]�t���S�����$�����'�
�Ј���W���C�:�C��D�����T��A���C�����$�C��]�^�C�q@����0�1�����*�d�v�{B�"�C��~�(�C�5S�\�G��E������4�w����I��,�C��D�{�C����C��D�ӁC����C��D��C�����v���&���=��v��S��H�,��l�6,�'��	��$z�C��Q��C�s9��2��9�C��Sޥ�C�?ׁ�5����I�� ��m����"�J4��c��}��A��$��
��<�v��j��)��&���C��`݉�C鎰����?Z���@��y�|[�0��7��)�����e�|����6�:��
��w�	D_���p�y_�
�F�I�&�D��@��D�1ŃD��@��	��8�D�1ȃD��@�����d���l�D�Q�D��@܆�D�N[�>�{��H�Z�d����"�P��t�b�|�����D��R��D�R�/�z�2�D��X�j�D�4��D��X�K�D���z�D��Y���|�ES]���8�E�^ܲ�E���x�N�d�P�E�c��E����0��E�f��E�|�D�H��+��Q��^�E�@܇�E���M��T��=
�E#.�
�E0.��E4�hߩ���I�E6e
���>��`�_�E;�]�5�E<O�~�N��x�$$���&T�b�EQ\��V�h��h
�@�P�Z�p�\L`��(�>�,�nF�
Ek�n�;�T� ���#���+�Eu�B��Ev���N��u��v����E|�g�؇Q�E~�
�E�h�3�	E�5���0�F�K����4D���%�E��a�l��E��uȄ*�(E�N$�6nV��W�`�k�t�
N��>�U@�g��,�A�B�1�<�d�m��" ,<P�!2��.��%�E��_݄�E�@S�	E��D��S�؂x��G�3��+��b���E�@��E��K�ɁE�VR�E��C�1���T�����f�j�E�5��E��T�����/��G�E�?��	E��C�0�S�����<����<��n�x�E�?��E��Iޖ�E�d2�E��C߭ȁ��?��{�*���B�E�T���n���E��g�ÁE�&�a�E��K��E����!�E��Y�r�E�4��;�E��`ܽ�E�E��C��W�N���^�E�6	�E��C�ցE�V��E��Iߵ��Y��s�N��~���|�؆e��9��,��Y�FZT�q�F	�C��Ђ��p��1�u����F7�F�C߶�����.���H���G�AЄx����FX��c��F�U�=�F{����Q�V��t����n�%�F$�V�tЈo�VF&U�����,�h��[��((��3���:��wl���J�1��9��Z.��/��f�p��[�p�]��<0��y��h��<����\�����&��C��>��a��f��)��B����br��%��r�0�qt5� ��s�� ��-��X��O��p<B�~P�h��?��l>�4��%��h�F �8�F|�b��F}n�L��H��7��]�F��O�A�
F�o�x��)���=���B��7��|4�F��@�%�F�C��!�F��C�؁F�6N�<�F��W�e�F�f}�l�O����F��S߸���W���x�F�4~�F��K����
�F�F��q��a�j�$�F��Oߏ�F��L�F��S�ɁF�I*�F��h���1�w�F�:���<�F��I�z���Z�;�F�h��F��H�[�F��b�9�F��@��F��f�/�w�F��i�[�F�A���|���w�F��R��F�;��x�F��R�b�F�9���:��%���R��]��R�F��\�	�F�p��F��R�O�F�~e��m�y�F��R�]�}�F�}����C�F��N��M�F�W~�F��B����y�F�?�F��Gߞ���.�F�p�F��d�[�F�]p�<�F��b�Y���F�O���<�/N��1���F��R��F�s���/���#F�>�
��0�����24��#���L��1��:�e�K��8��G��J���U��x��]���T(�B�G�U�5�Gy3��S���h��?����x��k������$�,��e2��@��!��V��I��|�'�G4�C�ϑG5}�	X�l�+����D��A�V�,b��y��<��'���8�GOD��P��/��n��m��"D�^����L�&��Q��L�"�I�!�\F��	��rD,.N2��/�Gm��S��t>�8�*�� ��,� H�X�Gy�^�g�#Gz�8�6>�8�:R��	��T��-����M�?�}��,&��L��
��d���k��^$�
 F�-G����
�P��Q��

 ��)��RH�f�h��w��|��|��6����x��=��(��A��.��[��(:J��e��(��Q��<�$�2�f&����d��c�G��F�‘G͝I�h��]��X|�
��t�U�
��G��@ܔ�D�P�GߔÃG��A�ׁG��G��A��~�@���G�WW�6�G��B���b���v���a�G�T��G��B���~.", NN��*v��p�G�u���>L"�H6G�H�Dܿ�^�H
Sd�H�D���"���B�R�Hp��
H�F�-�x�.��H�
v�6~�Hq��H�H�E�Hi���C�H!�H�|�4D��^���:.V�$J`��v\،`؋A�HE�X�b�.��Ld�p�|���L�v�H@X��
HA�Nާ��R�N��>�(<L�B��Z�"�HKW��HL�Q�t�t��D�P�2�T�HRYe�HS�S߆��J�HUW?�HV�Tޙ��~XN� <�@��*�J�T�4��x�HdW��^�	Hf�X��^���r��X�j�HoV��Hp�Y��Hq6�Hr�Yߙ��(!�P�<$�d�L������8��d
�4�H����H��]�P�H�6��H��]�����H�V֓H��^��>���4��D2Ht�Fr@�>�T�VP�4��:��H�c�	H��b�V�B����h�v��LFV�H�=��H��d�^���)�H��ГH��e���r��<�n@�0�H�J����H�~��H��g�T�H�L���D�H��hޓ�H�q��H��h����H�M��H��Cܜ�HƏ�	�H��Iܰ�Hɏ��H��Rޓ�H�N�H��b�,�H͎��H��E���lXȃU��J��A��@�H�N��H��Wށ���)�4��*��Q�������s�H�=ƃH��W�؁H�N�H��C���CЅ`���H�U���N�H��D���H�[��H��Zܓ���?���t������G��J���;�D�ІX���{��j�>��/��h��i�E�D��2�H�N-���C� �1��h�I�R�n�IW��I�Vޕ���9��V��0���b������h���QЇx�'��,���%��c�IW��I�R�c�������n����I��I�R�>�I>�I�[�#���O����6�I"Wz�A�>�	I%�R�w��D��J������:�I����؉y����I.65�I/�b�-�I0R ��I2�O�����|��K��[���|�O�� ��ЁB���1���.��q�I@Eb�x�IB�X�L�IC�P�U�IE�X�k�	IFQ�>�T�#����4�e���IO�F�w�IP`���`�^�IS�P܌�����IV;+��IX�@ߵ�IYp��K�I[�A�ÁI\70�I]�D�v�6���0ЊQ�;Ia���"�� �%��s�z��[��0��/��0�mlA�X�]�`�}Ubg�}�D�I�@�5�<�A�p�c�2�7�8�A�q��t��s��t�/�lBg@�E�7�$�|A�I�.�P8��.��I��g�s�I�����;��
�I��P�N�I�pw��+�I��T�́I�5Y�I��W�0�I�a"�I��b�2�I������K�q�@�I��Lށ��
�I�<��I��F݀�I�K��&�E��)�I��a��I�@	��2�I��h޺�I�B#��V�K�I��f�G��;�IƗU���S�I��H�~�Iʓ��7�I��b��I͟�����u����N�� ��i��,��sz�I��Gݶ�I�^��P�I��I��I�e��I��L��I�i�R�I��O�c�I�l���I��Q��I�s���c�I��R��I�[�F��I��Wܤ���I�z��0�,�P��<�"�3�2�c�b�9�a����I��cݹ��u�J@q�`�n����{��k���0�J�R�{�J	���J
�\�
�JL��J�[�t�J
]{�J�Z���P�J[���0��)��W��_�;]�J�\�g�JMw�	�J�_ܱ�JL�J�_�ŁJ ;��@��J%�P޿��Q��t�����J*LW�J+�h�Q�J,GO�k�T�J/�_�l�J0M�G��J3�Z�#�J4r%�J5�V�T��c�D�4�J9fM�%��2��q�J=�b߹�J>=���~�J@�^��JA[ۃJB�G�^�JCZ����JE�gް�JFZ��J��b�H�JK�f�E�JLtav�JN�B�o�JOf��^�d���t�JT�L�V���JVC��6�<�JY�N��JZ�����M�J]�M�Q�J^]x��r��7���Jb�P����JdLw��]�Jg�@ݤ��8�Ji����1�Jk�@�9�JlY���X�R�Jo�f��Jp_;�Jq�Bߟ��B��V�JtA�p�Jv�d�y�Jw?��V�Jy�hݵ�Jz@��E�@�J}�[�F�J~A|�J�a޲��g�J�Em�3�J��Uܚ�J�Ea�
�Z��t��E�J��R��J�6a�xC�!�^ 0	�P�T��5���70�90�;0
�=0�?0�A0�C�Y  0��?��I�M�K�_	"f�b")!���i3�3�%�%m%�%q	�	!`0!�!�A����$1$`$f	$t	!p$R[P]�N�N�QmRR�_*N�N�Q�Q�S*^bSg*u(u0NNRORSpYY7[W[�be�f�l_lM��OPQ�R)R�T5%[Z\@\�2gNl�p|	u7:N�,Sw$YG\E^�_&_�"b�
b�gokf
l�r,u�y@BN�a\N_�_m=r�u�u�v�xy�y���(��K
PQ�$WYWNc*eHgl'4u�v�wP�* �����b.S??\\^�%b,e�"k�(q	t�ueu�R�{Q�v�Rt&X0X�
\
^�_�#c�	ebf� l.$qt5u%	v{,�4��;������P�
R�	U�U�W4b!df�Qt[v�x�z�i�y��r��=�������#V
![�abny�|�}��G��&������AQR�V/\d9ewk�(v!{����,�����5���>d�:q�uw�zM|���X�h�.���[Oe�o�Q�qJ������d�
j�
q�w�zaz�~T.�� �>X�)|>~i
���N����I|��'Q77�8�f��Q�=/�Z��vq���|�}�w8W"D\z&lKr�
��R�S�Qb�ls�u���-R2S
$Ws2_"PgxGu?5�m:T�	W�2\�^!bB
cg#g�
l %p�rI uGww�x{|x|�^O�Q�R^T�WZ-4cDe�gh2'mc/p�s�t�t�wy�
z�q�C�u ��	�NPcUut^�`�9hm"k7k�m�(r{ u�w1yiy�$}>���M�����\��
����
��Ve�1h�k�nFn<%n5AwFwKxd4>e��O���<Y���W�D%��7��N�R{>[�aId"-iH#k�k�n�EtJtOw_x�z.g�`��#���h������n#���w�
R�M^XaRdrDk�k�o;u!y�z�!}��O�&���h�D���>�`��QQR�je�ss^t�v�
x�h���c"���"�&
��)������=���'�6^g(f�#jMk�'q�	q�r�sfw�
x�x�|�~{�U0��	�-�����<�5��,��C����V�[�Hsn	uvHx�z�{�n��8�P0�+���.�BQ-\i^ma�f�j�po�"y ���5�P,�F�:���P�-Q4V�[=#p!	r�	w�	zg���
�Z��O���+���������t�@�V]�^�_�f�j�ry'zl�h
��V��!�.���b��9��V�pCpHpE~�%�z	�D$��(��[KkpUr
zp�4���J�H	�S=bk,�����	�nf������h��tkw����F�y��>�s�f
��pi^ 0�0�0>0A0�U0�6!�d�%z�C@�;}�E���:2�F��;w�g���A��L��Lݤ�BP�QpՃ%^ ^ 0�1�1�1�1��8<.�.�.�.�[��f�E��C�~&�D��D��F�n�qtW�Y�o0��H��b��I�#�r��PK
!<�#��0chrome/pdfjs/content/web/cmaps/Adobe-GB1-0.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�q�����������������������������������������������������������$�PK
!<��{���0chrome/pdfjs/content/web/cmaps/Adobe-GB1-1.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�q'����������������������������������������������������������������������������(�PK
!<�?���0chrome/pdfjs/content/web/cmaps/Adobe-GB1-2.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE��qW�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������n��PK
!<[�Q]��0chrome/pdfjs/content/web/cmaps/Adobe-GB1-3.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE��qX����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������P��PK
!<ɮ5YY0chrome/pdfjs/content/web/cmaps/Adobe-GB1-4.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE��qr�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PK
!<�GOqq0chrome/pdfjs/content/web/cmaps/Adobe-GB1-5.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE��qw�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������K��PK
!<r������3chrome/pdfjs/content/web/cmaps/Adobe-GB1-UCS2.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE������
c��"�?��4�c��m��n�0��J>��|��"4C\;���b�s�]d+	A��	�",���!��$���>�� 0���\�^uK$�o�,�
�!��~K��]Y�Y�/��j��:�]��h�K �� �A�X�]dUbg�}�D�I�@�5�<�A�p�c�2�7�8�A%�L�Z�w�`�N���͑�C�A�X�]dUbg�}�D�I�@�5�<�A�p�c�2�7�8�A%�L�Z�w�`�N��P��h��y�H�5t��� ��^���c�9��L�h��K�?����?���e�J�Y���+�8�*��$��%�d�H�P�r�C�u�Kr��<��7��p�a�>��l�T��Y���y��4�-�U��]�\�q�-��h���t�
�6��s�p�(����5�:�1��\��3�Y��V�6��q�.�*�F�_��d����	��,�
���3����[��<�h��9�����)�&����&���/�B�?�Cu��`�y� ���)��t��F�Z�"��/��4��v�����q��`�"��|��O����1�T�\�L�Q�+�
�b�f��!�'�t��2��=���x�6�"�i�G�*��W��0�u��2Q���B��
�;��Z��Z���A��|�}�>�
�U�U��'t�&�W��^�d���"���H�g�����'��/���d�]��K�V��4�A�f�M�5��P��=�s�:��T���'�j�B�2�P��%��j�H��c���(�m�x�:��=��d��\����$��=�,��9��=�&�N����9�	���i����_�H�|���T��7���D�#��o�=�h�2��p��C�Z��
��u�
����i�o�"����P�&��1��9�D�4������@��=�6�L��)�!��F����2���W�v��[�8�&��o�h��Q���V�#���1��o�.��|��+��7�<��E����a��d�E�4�*��7��<�X��O��C��`�g�Q���|��	��(�k���o�b��R�?�A�.�q�X��p��I�O�8��\�q�.�Y�?�n��e��`�g��i��6��!G�l���<�x�����D���J��1��\���*��E�<�
�B��;��0��C�M��v�P�kN��H��)�:� �)��Z��E�D�2�����V�v�d��_�x�w�d�O��]��-�\�*��S�U��*��_�����W�q�g�j��
�f�	��	��v�=��k�'�Q��2��n��f��s�>�$�;�7�S���Z��Y�� �U�W�-�Y�C�d�-� ��>�G��&�Y��
��P����8�q��6��D����A��Q�N�d�x��K��"��q�����}�G�D��H��m��6�w������,�y�AA�X�N�{���?��o��>��_�-�J��j��=��/�A�t��}�&�A�K��"�-����d�}���=�� �?���D��a���e��h�C��
�>������0��j�����!��'�J�,�l�A��[�?��Z����4�X�.�C��'����
���l��C�5�D�i�&�a�D��r����7� ��H�5�#�n�B���
��$��a��F�M��i�t���8����
��@���k����M�!�������0��7��p�?���u��&>�?��,��{�����i�\�7��P��-��8����0�I��)� �e�n��$�d�x��-�D����f��?��@��	�K��F��?��������`��W�t�0��G�F� �c�f�&�#��A��3���#�J�u��z��%�#��P�u�`�W��d���S��~��y�f�V��{��x�s��w��$��;��2��7�0����Z��c�b�+��`��1�t�9���}���H�
��u� �R�,��%��4���?�e�H��g��Z�>��i�K�����X��_�������2���/��h�@v�H�B��5�&�#��T��-��X��k�B��k��f�F����,���}��[����M��J��'��,�@��{��$���J�Q��,��Y���,���/�J�%��Z�:�0��/�L�I����7�"���U�D�S��7�.��p�����w��o��b�#��k�4�L��s��h��5��O��l��?�N�8�$�Q���2��(�A��J��C�5��<��o��x�Q�e�*�9���X���� ���|�N�U�h�[��^��a��n��W�@�~��k�B��P����x�+��e��n��;��3�'����_��J�:�c�-�9��p��	����3��Z��� �@��~��Y�r�M���H�q��B�C�H�m�C�8�D��M�j��6�W�&��/��.�g���K�%�&�7�S�m�M���k��q����a���n�1��a�<��8�����^����#����&�I��w���r��}��G�+����4�i�_�F�g�c�x�g�5�z�x��+���6����b��A��2��A�b�i�N��2�.�/�
��e�o�d���l�p����d�7�&�U��w��"��y��.�q�Y�)��l����:��Y�&�@�
��+�7����3�5���q�E��h�Z�]�\�8�I��h��$�I�r�i�_��L��E�n�%�.�g��r��5��^�[�R��S������
�j�Z�?�$��9����c����
�n��e�{�(��,��
��n���8�a�A��/�
�*��o����=���F���H��b��m�D�[�$�=�,�
��0���G���E�V|�5��+�k��N��c�J��
�E��"���C���v�M�_��{��v�~�z���J�l�0�Y��s�H�v��k��x>���n�,��p�	�p��y�4�0���*��:��	��R��3��Z��?��D�+��<��W�j�*���<���-��p��e��&{��C�Z�}�
��-��r�^�����o��h���|����{�t�(�o�v�F�r��M�%��X���q���D�O�)�u�)�z�<��'�x�7���k��0��	�a�X���o�\����?��:��?�h�=�[��L�R�~�O�L��G�%��6��%�� �6�	�o�\�7���%��J����x��{����T���
��h�s�`�N�
�-��?�2��6�m��1��T�S��@�b�<�<�����,�]�I�s�f��Y�"���'���l�I�3��[�)��2�
��C�f��O��X���4�r�q�|��C��D�d�.��Q�j��K�i�w���&��I��V�g�K�����:�f�6��u�� ��o��0�_��-�P��^��1����o��=�\�P�]��R���,��Q��d��7��h��S�N�*�(��o�D�P��
����F�q��f�_�����P��W�^�V��6�
�c��J�� ��}�f����P��d��I�s�� ����T�;�J�E��	��@�&��-���G��-���4���~�>�c��0����Z���U�k�� �P���p�=�>�%�j��|��)�~�P�v�k��d�Z��X��v��S��F�Y�
�>��
��=��	��S�^�L��6���Y�D��E�I���U�y�$��{�~�9�N���b�� ��I�� ��Y��>��2�s��1��>����]��;��\��y�p����M���+�J���O��d������C�6�?�B��-�v��-����#�
���2�/�0��{�����`�s��g�H�S��D��C�V�&��g����m�f�	�;�>�c�N�e����o�$��W�\�<��+�p����#�y����L��_�i�H���`�^��!�#�s�X�O�@��0����;�s��Q�d�q��z�.��Q�k����5��1����q�;�C�{���"�P��[�� ��7��N�5���T�:�V�"��O�$����8�
��g�`��+��K�n�^�z����B���#�!��,��9�@��a��x��S��.��/��A�v��@������T�u�K��T�H�9�>�<��E���D��'�a��J��I�`�F�|�������F���P��~����0��A��n�X�+�l��F��m�����G��
��V���e��D�{����:��H���q�z� �	��v�K��	��D��k�p�-���T�b�"���J��[�x��C�J�_��`��1��
��?����u�B�A	��f�v�E�I�v�G�4��3�h�c�v�c�	�\��	�����+�(���;�8�G�F�N�u�n�S�w�E��T�U��)��d��A�J��R*��O�	�f�7��j��!��b��o��~�I��b�/�5��z�/�0�s�?�u��F�%�2�(���~�T��/��$��a�T���V��a��D�#�T��`�$��K��N�o��\��e�|�O�/�/�@������>��[�d� ��+�k���T���4�7��o��7�X�s���-�v��,�(��A��v�>��6�8���h��}�1�C���n�I�9�%�~�a�A����_�� ��m��A�^��\����a�t�i���T��7�b�~����^��[�h���Z��Y���2��q�8��u�4�3�m�j�$�
�n��<��+�5���{����,�;�'�d��2���c��>����i�*�`��+��x��G�i��<��y�F��&����H��S�t�S�D��<��v����`��G�)���u�
�;��L�>��A��d�����6���;�n�z���F�J�l�x�]���c�*��o�N�!��N�?�D�r�c�k�	����)���z��c�@���?�Z�u����/�~�@�S��r�i�T��}�.�~�n�)�Q��a�l���D�P�V�w�+�=�
�<���a�S��B��C��t�>�*�;��I�b�I���R�� �W�C��|�%�B�t�`�;����Q�����D�L��b�G��X�=�g��2��v��k��H��G�
��T�K��q��n�����-�>�
�C�:�S��(�M��U��,��
�.���&��5�
�����k�]�4�����Z���3�-���t��P��i��~�4�q���=�>�c�T�n���)�f�e�4�I�y�?�q���i�x�3�(�	�>�|�1���~��s�`���5��k�I�D�Z�B�t��I�V��^���r�J���7�A��n���Nl����z���?����Q��*�G��M�����=�\��G�t�&P�?���p���g��:��{�N�H��b���]�@���'�&�|��`��Y�V��W�=��z��
��x��a�4�&�D��c���f�e��!��(�O�T�A�F�� �/��3�I��
�o�J�v�W��b���c�j�t�Y��g��0�]��Q��`��
��
��#���)��x��O��P��#��>��(�Q��Q���<��I��.��%�C��B�/�p��i�H��8�M�F��	��*��'�l�"�O�k�]���u�� ��f�C��b���
�H�9�f��W�$��2��Y��P��C���@�/��.�/��R� �M�R�s�=�{��0��w�b��h�^�6���N�q��\��}�R�q��~��|�I�Y��b�>���|��i�@�~�*�u�(��K��x��%�n�G��Z�}�Y�X��
��_�S�8�$��w���L����>�.��m�R�p�x��n�@�E�1�#�8�Z�|�T��!��6�]��_� ��L��9�]�B��������0��#�)�|�M�J��'��v��K�S���Q��W��N���s
�f��E���n�h�~��	��q���d��u�#��H�O�-�O�W�F�F�a��O�\
�g�W��V�eM��e��d��s�H�/�x�c��.��?��p��a��b�k�n�m��<�?��E��N��)��^��G���N��W��T���+�P�i�B��J���=�^�'���*��[�n��x�J��C���?��B���?�W�����l��5�/����S��\�� �,�q������z��z��S��N��=�\��=��0���$��� ����?�e�%���#�`�r��9�=��*�s��6��I��?��N��!�`���X�X��A��z���=�.�?��X�%�5�?�L�F���7�:����/�n�C��j����r��m��~���m��`p��3�v�u�C��"�*�����|���Y�:�:�a���
�h�9��J��U�Z����*�T�H��
�
�V�
�I�O��D�3�"�<���i�m�X��K�0���Xy��
��Q�y�~��Z��i�,�\�G�x��>����H�^��~K��E��D�?�#�S�f���"�Q�:�t����
��}��B��]�$�i�9��p�"�:���~��W��:���#�B�T�M����7�7���\�����)���S�V�a�
�~�%�
�A�"�!�)�a��N��k����Q�p����W�#�L�,���7�_�~���$����:�w��O����q��_�c���	��I�:�C��4��/���z���&����%�E���y�p�W�
��T�)�*�(�*��E� �� �U�}�0��k�Z����Z�+�.���"�;�s�D�j�e�)��j��;��T��&����|�W��5�8�v��a�%����W�@��U�N��#�r�@��)�x��@�{��h���1�#��^�K��$�L�$���u�k�&��Z��^��O��6�f�������>�J���L���f�m�@����f���}��n�,�F����>�'�#��r�m�v���u�r�B�1�����9�(���C�(�v�k�S�,�K�%�Z�u��*��#�@�^�V��f��5��U�=�z����7����\�[�+��k���P��O�
�#�G�x��&����N�+��5�^�y�P����$�/�����F�:���l�q�^��7��t����M�����E�4�P�y�X��N��A�_���q��Y��V�!�;�t��I�Q�5�b��T��s��_��t��I��o��O�#���-��&�D�Q�$�Y�o��d�_���g�M�E�����R�%����u��L��u�F�h��8��q�'� �g�	�e��h��=��0���s�V��x��y��4��^���H���o�,�q����c�vl�L��#��0�w�8��S��n��W�kq�E��F��M�
�y�_�
�8�r�,��~�-�
��5�,�Q�)��b����a�`����u��s�8��^�C�q����*�g��	�X��f�M����D��W��N�i�$��}�&�=��q���_��T�v���v��<�p��M�
�`�2�3��k��j�U��k��K���j��O��f���N�"�"�4���u�`��R��[�+��"�I���
���U��*��i�o��6�[�����U�M�,��W��\�z��S����6�����w�H�$� �s�<���H����L���H��y��J�b���+������y�{��b�P���N���o��B��/�v����!��5�>�_�A��z�)�"���]��&�\�"��!�E��N��,��1�/��*��?�y�6�x�.��_�*�e�p�}�4��4��)�'��0�X��3��>�9��#��
�?��M�i�h�N�v��O�.�3����}��(�S���z��1�u�r���b�i�3����Z���j��{���Q�k�6s����ra�}�Lj���,��v���s�9��,���M��P��k�6��t��A����{��v��9��&� ��0��g�>��Y�5�/��H��U���
���>�w�!��(��#��U�F�0�)�s�q��@�W�K��s���x�"����|�� ���}N�E��n�1��S�f��
��=�-��f�y�.��	��V��[���>�z��g�&��`��{�P��l��5�`�r�e��J�a�i�U��V�j����
���\�l�L�R�|��,�+�C���\�3��
��S�M�C�>��:��E�(��@��Q�+�Z�p�_�����%�/��r��e�j�Fp�B��e�`�9�E��<�[����*��O��N��O���9�P����>��A�q��4�m�t�i�h�����a�4�o�@�^��67�i�8����A��v�1�u�n��8��/��j�=��\�z��u�!�F��|��]�a�^�^��B��%�7�^�m��r�r�,�!�G�D�;�}��T���a�d�
�|���Z�$�5�.�C�5� �M��Z��U�1��D�a�Q�n��}�B�=��a��'�V�6�%����*����P���N���"�_�z��p����G����7����-�M��z�P�)�"�����f�]�~�7�D�&�D�R��E�\�F� �x��1��.�Z�{��+�:��\��O��>��f�I�1�>��!�I��]�}��L�g�x�>��m��v��1�O��_�W�q�v��4��6��)�z��C��8��O�n��%��G��\`���t�m�G�,��K��.�K��|�Q�_�w�b���7H����o�j�&�2�O��9��}���<�1�D��N��	�m�`�U�p�;�h�/�=�$�U�H��q�m��6�M���,�Z��O���0�h��:��Q�,�[�U���*��y��R�)�;�K�����.�a��Q��,�Q�K��q��>�
����a�
�p�*�<�����v�	�e�#�\��6�����Q����/��P�U���2�;�m��V��]��(��'��H��r����C�z�"�&��C�x��\�j�o�l�s����(���R��A��T����-��	�}�f�,�5��f�a�\�u�a��u��&��
����"��w�*��J���B�c���d��	�T��0��C�P��`��iR��h��[�_�D��
��l����4���:��m�C

<
���RL"��$��e�#��"��3
JxQ\��_^=Bd� �=Kp8
#\]^1�"K(0`D�3p;<_:!0�*w"
:R 6^3
0�]�>�K�P��"��5�Z��
����c��/�$�7&��r��o�K�Z�	�k�j�G��tj��_��<�z�%������
c��t���&

�<RSi ��D$

&0�3	$$<"$	

:	��s�h�A��R���_
2�\��<�3���d��s��0�W�G�8�8z�Q
�H�W\CL654'r1J8QCh5v)Z!!$:TI�( 5��N�`��5��r:I
"
.0�<����1'HUxN%'TW.E
f#Qz)����h����-�R{_>	6?S�gd���4��q�"8��//rYt{8
"~9��#�� ]>O�
6Oe^�\��K�Jm�t2��i�z#yD*�;v�&*S*J.;�P��T��5467y�8#>	
6�1�@\PC-h%/fD%4DNEF'J��}���j��E��$�.:h6,fY�"�R��&J�|EC�j
1<w2!���	m)2xJ/P9��d�@
L6�G����e�")$,!��TV'Ejm�";
�T�!ldG(:W�"Tr`b5�
-T�pxipG,C�9U*xNJ�YFY&- )<�LU3V\��Zc:C�.�-�F�
^U�� ]�*�"3T�qU�#��^��m-DKL
T?:W@(!bE
��V�6H
&

$�d>��^7�+%$
��\�0'4#
:"32�,�]'
1(x5\#&%A<,��*��y�Z��J�����r�*
�s	/&,)<�8��m����E��@�$�t
�1^� 
0	��>������mf+*G2�N_�@66Ch=�+^`{qN$0$!5
� cV%&M�V
#_8<Tl1$8@�1��*�
������N,��$�U�><%�0=��ltDLO�#�l�5KZF{B6t%7l�(8B?7hC�:E\���m\63�l�'0^�@`W	x�W�@�=�x�A8��'L��n2<�4]3���{*	>G4�dG��Do$9j(P�C>�>)
�,E"<!T:	2�= "$�l��\��U�!������,��\884+	�Q�-(�"V�P.!��E��t
��w�;�!�H�Q�b��*��%����q��|���[�zY�d?
ZAN3�4�Y�TK@$PG"!XL;3J$��l��I�Xs>9N7X6�(�E��$�"��z

�B/ �15��w��I�D�,�8^5	8��D��eCIn84`k#L4: :�Et�$,T1�%QgL-@v=;HedE)�f0cbCs�6gIHORD{RV�v�zhN&=><�Mn KJOA�M76Q�,�V�e��h�Ti4�*�iX�@qL=4L3(.A�	�.��i΁G*B&!LG(���'�b^���je�g�"
�B�!M�0+y+^F(0%&�4��k�&��
��������t�q�?�
f
�0�_"+���5,� �O'>4@�*f���
�%�3`��Ir��R�z��0�V�5�al!
�{>*�BM�b����P�e�d
�~��3(��
�����O�x��I1)2*@���q#���m
��5�N��$
�y�B�M�F
7�$$���Ց9�k��g��`��G����}P�R6
:
l=�
0�/GXQZ:4~$"F�y�V��$�w
��y;
�(�yS$�E�V@M.��4��Z
h�r�Y�D�
�r�(x�M�8/
H<�:x&R:`'()\!D
$#< 
*��L�E�Xv�$)@&�gwG	,
2!�uw��
$,�(�P�A�c.��(���*

(
*
&
"��:�'�8�i.4��v6H2��z�U�k�Q

�
���f�
<�2
�
]�q	
��C�1iu�'$&*	6% !4@5>'2 
(�4�I�5�z�
 
	
�`>F0)(P=)*`
#RH(�a�2�g�N�j
�Ԁ)MR,�"�h
�昏��M. X7@	/*0=2
7h���
G�k*+b	2.;)F)��A�/1�{7IB8q01Z6R}N-)Nrs.-&# N+ 
.�
�b��-�h{@9n#(#Ar+^S 3<��{�GH
8! ��{�B$&	"�z�	��1�.�/���>
�*��|��	���m�g���|�
�"�|�" �
�
�,	�w�|\�0�&��8	� >�
�M�
���d
$0���
�K�P�
d2�&�i�#��+J�,��*/H,
	.�+D�JJ

 	�M$	��s��t	
��N�X�U	,6��u����)�|���'�@�%>2�jV�>w�\X�&��z#��N8!f�M�̗�4	�x�@�E�ߚсf�SH@L�	��~;���y��
��d��c�D��{��S��n�x��:�J�0�'��p�y��
��r�<�A�}��&�3��[���{�8��t�Y�A�7�*��g�J�����>�Q��R���I�`�P�[�I�?�A��J��E��Y�H��+��^��U�I�B��I��*��A��x#�^�K���=��d�=�}��*�G��!�t�I�Y�n�����%�=��`�b����y��z���o�;��>��1��0��1���b���#�	��z�	�}���Z�@���<��)�<�W��{�"��}�*�r��l���9��
�G��E��Z�h��C��D�	�P�^��z�Y��%�y���C�T�7��=����I�M�����2��G��t���Z�W��8��+�{�C�t�G�r�<��e�,��"�}�t���)��`����b�I8�v�R�E�=��#�9��%��.���
�.�X�>��I�L�V��{�+��4�
��G�y��\�!�e��u��b����V���3�o�f�h�s�x���4�H�
���r�E��/��0�A��Q�t�j�r�b��{��|�*��;��D���v�3�@��!��P�]��{��l�;��;��h�,�`�1��%��6�u��u�
��@���5��(��w���9�Q��a��4��Z��[�V��c�%��`��j��s��Z�9�N��������V�/��F�i�9�?�U��`��U��4�J�����<�5��c�x��$�i�<�6��m����x����
�	��Q��Z��u�3���&�7�@��9��\�3�E�?�`�����}��s�f�j�7��(�Z���h�
�C�Q��r�+�u�E����s��P��?�y����F��Q�n����6��G�`�=��
��E�3�(���2��y�|�B��1��8�W��,��Y�3�R���3�� ��M��G�!�n�F�a���\� ��5�����:�������:�!�+�^����Y�v� �/�6��C��f�p�m�H��A��(�"���H�/��!��8�����_��:��y�k���1��P�[�N�#�b�i��5��(���l��i��V����(��{��F�r�
���9�\� ��7�y��*�W��b��	��Z�O��a�\��?�_�,��S�� ���J��a�G�U�P���`��7��
�e�j���r�r��7��P��K�{��j�i�J��4��e��P�|�h���=�$�4�y��|����T���}��b��!�r�&��7�d��L�)�:�[�&����:�J��k�����-��n���^��H��C�%�:�Z�p�?�����F��d�H�$�[�^�
�R�C��?��(���$�� �I�H��E���J���+�&��:�/�u��X����!��I�X�"�B�h��'��P�Z��:�G��L��y�|�R��7��z���K��B��#��V�����)X��J�5��z��9�YY�G�V�y�*��!�$��k��j�+��M�x�G��r�H�_��;��:�	��w�n���N��x��\�y�\��,�]����#�e�
���V�a��Y��n�'�+��)�9��u���	��
��K��n��+�D��F��x�y�
��k��d�3�x�@�I�u�6����;�r��w�&��h��0��=��j��C�?�7��N�i��X����d��U�[���q��+�&�F�r��Q��~��X�}��U�%��r��q�0�|��)�>�[��4��	�7�r�l��B��W�X�.�R�-��K�"����i�x�
�8�]�{��,�2�!�q��=��L��L�0��U�h�?��\�u���c��*�9�~�����p��W�2�a�&��O��$��_��b���s���B�d�Q���S�t�5��2����:��U��L�5�`�e�'��f�S�*�5��9�H���'���@�[���W���X�_� ]�3�;��B�@��d��9��|��_�0��
�s�3�#��$�'���P�R�k��L�B�Y�l� ���o��<�p������� �)�v��/����H�.�IV��w��~��9�(��Z�K�u�0�m���H��x�p��;�(�� �$�O�{�'�E��5�\��r�%�������'�&�������������	��A�|��?�z�j�:���;��0�e��r�X�1��r�h�:���Y��N�z��_�p��S�-�
�1��W��h��]�5�7�R��B��y�:�&�N�	�s�`�a�|�/�?�r�
��5��~��j��U�;����/��P�C�"����j<�����|�6�����>�3�	������8���9�a�d��^��7��N��O�i��b�y�W����)�6�B�r�|�U��u��t�=��3�b�d�r��i�d�m�f��a�b���m�����a��U�p���d����9��)������.��d���B�L�0���3�
����B�$��[�p�$�8�Z��M����g�c�b���x�n�)��D��~�9�|��/�B����&�'��O���<"�{�>��Q�`�F��K��8�Jz���~��t��-�$�*�e��"��u�D�o��
�x���p�g�>�+��X�C�w�|��Q���f�h��G�H��I�k�D�����A�U��'���w��P�-�)�x�4�|�d�}�;���O�g�1�X����:�(�w����"�\���s��$���]� �����S��.��]�T�
�7�^�@�@�o�/�p��m�]4��f�;��=����u�1��i�*�|'"N�M�pWR�c��T��.����L�c�7�@	�:�KS6
f�<(�p��$"���"	+~P';!��f�
4P�gT��=M>�+�,a�����|�M�
�s�Zsvk���Ns�D��>�N��t�y��7�,�j�7� rb�I��d��y�%�]"^�?�R��2�9��z6�	�B�
�P�Y�LI5^��c��f�P��,� "?<�W�E�E��GW�^2O�"�X�R�g�J�%��b��WC�b�vP)�!�'�6I�t��To�$��,mP_^(	��F�$��$�#���(6�j#��L*����~��O��	Y��|�.�[��[v���/�j��^�u
�0q��"�d�U	��7�U�9�k�H��@�/�l�s���M�T�6G��P$h���I�e�t��Q��}=P�'�4��K�&*#��t
�8H��h�Q��7x+/T'�$�O*�W	�}$ ~��]L�<�):H$j	 !M-,"B�^�y2UN&G�V�		&�]6P�k�1���t���}�8Z
��|��e�~�%z�\�M�v.�-�u�p�A�,�t�B�j��m�NU%:�f�	^�

��H,�P�W#�z�Q&�-$��,$��$�H�w���T#��%$�*	&�G&:3�q>�V�c�\���D�;�Z�a�%$�
�
���u�,5�R4B�bE6��w�|F�B�
����b��0-	��b��{u�:��$���B!�B�#:<iJG@KrV_	B1J�.%�?�@�a0?�|�M^�?�f�A�,���#jG��E)�R��~�_Q��G�\�5��J�eY@0�%C��6V�D�H&0(�%Q�
�=\E�
u&�>��C�i>3�,�U�E%j�� 7�M�Z�%�2��0�>`1(�K�;�� 8�F��q�
�m�R�S"�6���{�@�/bS2J70
�:�(
&%@��o�K�%�vIW8�%�vi�(#�R[R-V�Y�j�	�"�s%Ș!��7��H ,>#�D��8[�y�h�W����-$��Q�{��mk� }���<�K�g3�0��d�o�$�l�c{�
1K�}�	�e�F1�
	
�u�w�^�-��R��Y��
��8'���t�/�p�-&�ojO�l&=��+0A��+@"
 (�I
�b�Y��o�
�(�
�2�!��a�������U��6��)��X�O��d��P�3��7�"�l����\��s�2�O���z��L����Y��T��3�{�P�/���W�.�F�E��b���G��H���<�=��]��P������V�W���>�/���u��p&���r��\�X8�DL�i�x� �zD��K�b��x��C�V�m"n�4(
&
@
,V
&		,v2F
B	:  	
$,F

F	"&	(



 40" 	

@$$*
 0	&	2!l>

$$ \
6,>	*

B<*
"
%^=�<>	0<&	.	p
(	(L0
%f0(<
 2
(

((
!h&	"



,*:	,J
8
(B
":F
4,J&"
 :(
,,04
*(

	
 
�	3�d�
��b3�d�


	 
*(6


&>	 @>B",

 

 


0	 X
.	2	

* #X@F&>>R
$\"'b)f0.
282(
4
$

 
&	&
L
b
$

6"X$
@
 

D0�
$<:


*8 6'f
2<	 6
<
 4F>
2
D
"
0
 ..	 6D	 *B
)j0


J0
04
" 

0@
(
B&rJ6
	(.<$$j&b.
2'j0	$$
 V,44
(	\>	
">PPD0D
.	 *	"
,B	,08,
�	C�~� 
0��:C�C	$

6(4$
2,
*"(	
"
	4 

$
.h
&
F3� 4

 &>	(	 6@"d
 >	6
,2"
($P,
	(B
42	 ,L&<>"Z
((&$
 
"
 ",&
"


$^*p�
K*��2<(�8Kr��&"
F	"l
3�

>	$T

$jR^�	M�^*
��M��
,84


(
0"
2
$ n
	"
,(LD

.2
<.& (
($0D"
 0\�O��(@

��[P��	&$:	"
*
",

.&"6
*,0,
$.,:0$ "bF@>#V
 

$(		**#P
86	
(
$$04v$&5�8
 N 5�
D:& 	,
,!���H��P�V �@܇��V#.��\Y���P��-�v��,�-��8��M�&�G�V7;N�>�b�s�V;�E�ׁV<�����V��c��	�VE�Jݏ�VFC��c�B�p�+��X��-
�$ ��xd�VZI�	6���V_�P��	V`L�YT���_���'��p�y�;W0A�X�]dUbg�}�D�I�@�5�<�A�p�c�2�7�8�A%�L�Z�w�`�N�����������4�'�(
	
4?�
	�Wm0�2=�"�W�.�c�d�d� ���1��������4��m^ 0
  0000"'"n"d 2��!�$�$t	$`	2 !`!f�X�R0AH0�0�������5�9�?�=�A0�;�7�36 $1%-%]!�C���2dx���s�$�0��OR?���������������������*�.�2�5<R�W.H����[�m��"�����d�TRT�T�T�T�
U"UuU�U�U�U�V{^;\�],r�r��g����``x+����
��lhl�#m�nSoFD�5�Q"Y�Z4�w����������~�~�~�~�~�~�~�!*/1t�t���%h2h`ii�jk�k�k��q�u�y����e�f��2�E�H����r~l���
�Y�|����9bHy[(xxx9xV��wwPw�������������������������������������	�
���*�1�6�>�D�N�S�V�^�a�d�q�(�9�A�F�K�Z�f
u�1zx5�%�������������}/D{{�{���!�3�	|�|�"�N�}�����������A�Q+������������������������������������������
�=�c���$���Hj�C}}�-��%���l.�&���EvFvm�s��l	!p�!�"f#%P%�%�%�%�00!3�3�3�0�0�0�	�I�T
�Y�h/�NNN.N@NZNbNgNj	NtNN�N�N�N�N�N�N�N�N�N�N�OOOOO(O,O>ODOGOaOjOmOqOwO�O�O�O�O�O�O�O�O�O�O�O�O�O�O�PPPPP"
P/P?PDPJPPPV�	({P]PfPmPxP|P�P�P�P���D(�P�P�P�P�P�P�P�P�P�P�P�P�P�P�P�P�P�QQQQ"Q%Q+Q3Q8Q=QNQWQ]QcQfQ~Q�Q�Q�Q�Q�Q�Q�Q�Q�Q�Q�Q�Q�Q�Q�RRRRRR!R%R1R4RERHRNRRRWR_RbRkRpRvR�R�R�R�R�	R�R�R�R�R�
R�R�R�R�SS	SSSS$S'S+S2S<SKSXSlS{S�S�S�S�S�S�S�S�S�S�S�S�S�S�TT$T6TATDTLT]TiTyT~T�T�T�T�T�T�T�T�T�T�UU
UUUU%U(U4U8UGUKUQUWU_UbUhUoUyU�U�U�U�U�U�U�U�U�U�U�U�U�U�U�VV
VVV V%V*V<	VBVOVUVZV^VmV}V�V�V�V�V�V�V�V�V�V�V�V�V�V�V�V�V�V�V�V�V�WWWWWW W%W1W4W<WCWHWRWXWbWpWtWxW}W�W�W�W�W�W�W�W�W�W�W�W�W�W�W�W�W�W�XXXXXXX"X%X+X2
X6XEXUXYX_XfXmXvXzX�X�X�X�X�X�X�X�X�X�X�X�X�X�X�X�X�X�YYYYYY Y2Y5Y?YEYLYRY[YcYfYoYzY~Y�Y�Y�Y�Y�Y�Y�Y�Y�Y�Y�Y�Y�Y�Y�Y�Y�Y�Y�Z
Z
ZZZZ!Z&Z*Z7Z=ZBZG	ZKZVZ[Zc�
.ZhZkZnZxZ{Z�Z�
Z�Z�Z��+.?Z�Z�Z�Z�Z�Z�Z�Z�Z�Z�Z�Z�Z�Z�[[
[["[+[5[9
[A[M[`[g[m[v[{[�[�[�[�[�[�[�[�[�[�[�[�\\\�
/
\(\-\2\5\C\F\L\R\V\Z��|/+\i\r\{\�\�\�\�\�\�\�\�\�\�\�\�\�\�\�\�\�\�\�]]]]]]]*]/]5]?]H]M]Q]Y
]^]m]p]u]�]�]�]�]�]�]�]�]�]�]�]�]�]�]�]�^	^
^^^(^/^4^9^>^F^M^Y^\^d^m^�^�^�^�^�^�^�	^�^�^�^�^�^�^�^�^�_____!_+_6_=_A_I_Z_^_g_n_t_}_�_�_�_�_�_�_�_�_�_�_�_�_�_�_�_�_�_�_�_�``````"`,`0`6`=`D`N`S`V`[`^`e`q`t`�`�`�`�`�`�`�`�`�`�`�`�`�`�`�`�`�aa
aaaa!a(a,a5a8a<a@aOaRaVa[a`aealaq	axa�a�a�a�a�a�a�a�a�a�a�a�a�a�a�a�a�bbb(b5b8bDbObUbYb\bdbqbtbwbzb�b�b�b�b�b�b�b�b�b�b�b�b�b�cc
cccc&c,c0c3c;c?cGcQcVcdcocscxc|c�c�c�c�c�c�c�c�c�c�c�c�c�c�c�dddddd"d'd.d7d;dBdKdUdYd`djdpdtd|d�d�d�d�d�	d�d�d�d�d�d�d�eee
eee&e0e<e@eFeJeMeRe_edegemeye�e�e�e�e�e�e�e�e�e�e�e�e�e�e�e�e�ffffff!f)f2f7f?fDfMfPfXf[fbfifqfxf{ff�f�f�f�f�f�f�f�f�f�f�f�f�f�f�f�f�f�f�f�f�f�gggggg g2g6g;g>gDgJgTgWgbgfgkgxg�g�g�g�g�g�g�	g�g�g�g�g�g�g�hhhhh"h+h4h:	hVhlhxh�h�h�h�h�h�h�h�	h�h�h�h�h�h�h�h�h�h�h�iiii!i%i.i1i5i:i@iCiKiUiXi[iaidiiioirizi}i�i�i�i�	i�i�i�i�i�i�i�i�i�i�i�i�i�i�jjjjjj$j+j2j6j;j?jEjIjLjQj\jc
jfjrjzj}j�j�j�j�j�j�j�j�j�j�j�j�j�j�j�j�	7�j�j�j�j�kkkkk�M7�k%	k(k3k?kDkJkMkQkZkhkkksk}k�k�k�k�k�k�k�k�k�k�k�k�k�k�k�k�k�k�k�ll	ll6l9l>lClKlQlXlblelklwlzll�l�l�l�l�l�l�l�l�l�l�l�l�l�l�l�mmmmmmm m(m,m/m6m?mU�	9mamdmgmkmpmumzm}m��98m�m�m��	9Fm�m�m�m�m�m�m�m�mҡE9cm�m�m�m�m�nnnnnn'n0n5n;n?nEnOnYn\
n`nl
npn�n�n�n�n�n�n�n�n�n�n�n�n�n�n�n�n�n�n�ooo
oooo%o4o9oBoHoNoRoUoZo_ogouo}o�o�o�o�o�o�o�o�o��
:o�o�o�o�o�o�o�o�o�o�o�o�o�7:�o�ppppp!p$p)p3p6p:p?pEpMpRpVpYp_pephpqpyp�p�p�p�p�p�p�p�p�p�p�p�p�p�p�p�p�
p�qqqq"q'q2
q7qFqOqSq_qjqoqtq{q~q�q�q�q�q�q�q�q�q�q�q�q�q�q�q�q�q�q�q�q�q�q�q�rr	r	rr-r3r@rIrNrSrcrjrprsrvr{r�r�r�r�r�r�r�r�r�r�r�r�r�r�r�r�r�r�ssssss#s&s/s2s<sBsFsNsSsXsasss�s�s�s�s�s�s�s�s�s�s�s�s�s�s�s�s�s�s�s�
s�s�ttttt#t1t7t=tBtLt`tdtktntqtxt�t�
t�t�t�t�t�t�t�t�t�t�t�t�t�uuuuu u&u<uAuFuIuPuUu^ucuguluzu�u�u�u�u�u�u�u�u�u�u�u�u�u�u�u�u�u�vvvvv.v1v6v9vDvJvNvYv`vsvyvv�v�v�v�v�v�v�v�v�v�v�v�v�v�v�v�v�v�v�ww
www#w*w0w=wDwHwRw\w_wiwmwzw�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�w�xx
xx x*x.x1x5xAxHxSxXx^xexpxxx}x�x�x�x�x�x�x�x��@x�x�x�x�x�x�x�x�x�x�x�
@Dx�x�x�x�yyyyyy��V@ky'y-y2y5yByJyTyXycyiypy{y�y�y�	y�
y�y�y�y�y�y�y�y�y�y�y�y�y�y�zzzzzzzz!	z$z/z4zAzGzOzRzXzczlzqz{z�z�z�z�z�z�z�z�z�z�	z�z�z�z�z�z�z�z�z�{{{{{{!{/{4{?{M{^{c{h{o{s{|{�{�{�{�{�{�{�{�{�{�{�{�{�{�{�{�{�{�{�{�{�{�|||||$|(|,|9|D
|N|]|a|e|o|u
|~|�|�|�|�|�|�|�|�|�|�|�|�|�|�|�|�}}}}#}(},}4}=}G}J}Q}_}c}i}o}t}z}�}�}�}�}�}�}�}�}�}�}�}�}�}�}�}�}�}�}�~~~ ~$~(~/~?~B	~H~V~[~_~c~q~t~z~~�~�~�~�~�	;FR[ckuz�����������������������#�/�9�@�D�G�N�[	�_�k�z����������������������������-�3�:�?�W�[�b�r�u�����������������������������������$�<�?�E�L�P�[�`�i�l�u�{�������������������������������
��	��)�=�A�D�K�U�p�y�~����������������������������������������Fe����)�-�2�6�9�>�G�J�S�7F��]�g�{������������������������������
���	��
����$�'�.�>�D�K�O�Z�_�e�k�n�u�|�������������������������������
G�������������#�"G��*	�.�=�@�R�V�`�c�h�o�r�������������������������������������������	HT�&�*�/�2�5�8�<�@�J��#Hp�O�Z�a�g�q�y����������������������������������������������������$�,�3�7�=�A�G�N�U�^�f�s�z����������������������������������������������������"�&�,�9�<�B�E
�H�W�a�g�m�p�s�|�������������������������������������������� �&�+�.�2�7�;�?�B�I�V�Y�\�_�d�g�v�z�}�������������������������������������������"�)�.�4�:	�?�J�P�J��]�c�g�m�q�u�x�~�������K%���
K4�8�B�J�M�Q�V�[�c�l�t�{�����Ks��������������������������Q�h�
K��n�q�x���������������������K���������������������
��� �$�2�6�;�>�E�M�S�V�Z�d�g�j�w�}�����	L|�����������������ϡL�����������������	L��� �'�+�0�4�9�<�@�AL��G�J�O�U	����������������������������$�'�0�3�9�?�E�H�Y�f�j�o��������������������������������������
����$�(�7�;�D�S�X�[�_�f�z���	M���������������������\N������
�������
����(�1�5�;�A�F�J�N�R�X�\�_�c�g�n�u�|������������������������	��������������	����#�)�,�0�4�9�D�H�N�U�Y�\�f�m�q�w�{�����������������������������������������!�)�/�6�;�@�E�M
�U�a�f�n�s�z�����t�x���������������������������������#�+�/�7�N�Q�V�Y�e�m�y�~��	����������������������������
�����(�+�.�3�:�?�E�I�T�W�\�c�f�j�w�}��������������������������������������"�'�.�1�9�>�G�O�U�\�_�c�h�m��������������������������Q�������"�)�+�/�4�8�^R�@�F�M�X�_�x��
����������
����	������������1�9�F�K�P�X�\�`�f������������������������������	���� �*�3�=�J�R�U�[�p�u�x	������
����������������������
���!�&�*�.�<�?�F�J�Y�`�h	�n�}�������	T0������������������JTK��
�
�� �$�)	�1�<�@
�C�T�^�b�m�p�s����	������������
����	�����������
��;�R�_�n�v������������������������������������������������#�-�0�?�E�L�U�Z�]�d�g�	U��m�s�x�������������V�
���#�'��.�I�I�L�M^ -!/O;0�03081*1��
W�.�.�.�	.�.�.�.�.�.�.�.�
.�'.˱3XF4*4H�4t�5b5�6
6�h6�7�89T9_9o
9�(9�i:	�:tC;
�;;Ob<p<o+<�=
�>
�?
H@
�6@WPA�.A`�B'CsC8C�*C�4CށBD<DׁE7FFM�3FbGG$QG*G}�G��H,I1IHI{I~�mfI�I�I�lI��J%�K%QL%&LxnL�M{M2M��q�w���x��x��x��x�x6��PK
!<�
&��3chrome/pdfjs/content/web/cmaps/Adobe-Japan1-0.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�q!���������������������������������������������������������������[�PK
!<��V��3chrome/pdfjs/content/web/cmaps/Adobe-Japan1-1.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�q!����������������������������������������������������������������&�PK
!<��}��3chrome/pdfjs/content/web/cmaps/Adobe-Japan1-2.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�q#��������������������������������������������������������������������PK
!<�!�}��3chrome/pdfjs/content/web/cmaps/Adobe-Japan1-3.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�q%������������������������������������������������������������������������	�PK
!<�h/�QQ3chrome/pdfjs/content/web/cmaps/Adobe-Japan1-4.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�q=�����������������������������������������������������������������������������������������������������������������������S�PK
!<DZ���3chrome/pdfjs/content/web/cmaps/Adobe-Japan1-5.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE��qP����������������������������������������������������������������������������������������������������������������������������������������������������������������������������\��PK
!<�Ɣ���3chrome/pdfjs/content/web/cmaps/Adobe-Japan1-6.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE��q[���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PK
!<qW�����6chrome/pdfjs/content/web/cmaps/Adobe-Japan1-UCS2.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�����<�NS�
�*�{�v�9�
�e�W�n�c�L�
u��V�E�T�c�����
�%�$89R�a9�,�S�r�1V� ��4�i�e�v�
��>�L.��I��I�<�h!�0@��}��0�
�����`N��"���7� ��e��b�#rIC�0M�(0r�=0��"�	W�h!A>�b��3:0
5

9����#�G��
|���X��9=�d�i�N�U��~�����2�
�0��O��XB��n��u�����;:�	���FJ>�5����	��"*���!��$��=�C��*���<uK$��,���!��~�<�����JV8��X�!�2�
�"DA.��w�|��i��p���] ���TeN��r�X��v���4��e��V�[�3��M�l��q�p����E�Y�r�a�-���T�p�%�6���q��.��]�V�&��`��|�
��}�UH���V�P��8X��*^�@�D�b�h�(@��b��&��(�<�$H���M�r��$��%��_�`�`��*�
�j�;�F��]�\�F�|�4�2��8���p��h^�4��a1�&��Z��J��s��X��E��S���P�-��)�0�x�@��-�y�x�j��d���c��r�L�G�x��W�(�r��\�$� �f�tj�b�X�
�J��R����w�p�<��t��?��4��N��I�C�O��:�&�^��b����(�r�
��v�R��h�4�
��t�~��e�$�:���Y�hl�4�6�>�z�^�x��,�j�(�*=�j��G�h�H��o��T���!�T���@�z�`�~�J�F��Q��Q�L1�F]�X��2�
�b�hT�^��(2�*�L�n�,�`�z��RRP�d�n9�$�����|�z�N��)��
�F�P��B�R�.�������������V�~�`�^|�Re�Z��z�P����7���p�6�r�\�^�&�"��c��Y��.�G�~�D�"�2�X�:�z��4�h� ����v���m�6��~�������.�	��l��`�\n�0�.�R�j�`�(������\�c���k�&���,��]�[�T�$�v�����h�f��a��H���H�<����5���L�\�R�^��c����
�D�V���;��x�?��Q��8�b���&�C��v�J��,�R�v��4�8��\��L�(���h�%�T�N�N�f�B�~���r�X��n�(�0�<�0�� ��Z�(�!��A�d��&�8�D�^�'���>�j����/�D�T��"���t��v�b��B��G�x�V�4�_�(�"�J���K�8�~���"��6��(� ��V�Z��y�x��$��$� �V��J�`�j�B�"��f��	��:�*��/
�N�P���.��e��.��3�Z��n�j�j��]X���p�Z�j�H�"�V�V�^�`�(��dn�2�"9�N��f�A��D�Rp��r�,�V�4��o�H��f��e��O�O��`�j��\����:g�:�V��,�:�*$�H@8�b�r�b�j�T����
�P�R�:�.���U�X��&�V�[�b��&�s�`��y�<�D�l��N���|2�l�v��|�\�$�d�>�2�.�~��e��@��G�0�\�W�8�|�$�x��,��7�D����]����	�8��f�z��'�P����-�����@�'�o�A��z�
�W�0�?��4��u�P���N�_�P�&��w��B����v�$���2�x�?�
� B�`�����b��$� �$�,�z� ��b�>�Dv��t�4��,�8�h�/�*�2��C�.�F�x��0��1�a��2�2�>�6�^�D��7�%�'|�,�vT��N�j��Z�n�
�t�|�R��&\��J�4��[�8�B���^� �"��4�z�H��k�6&��T�h�h�f�r��f��r�*���x�0�.�z�,�$��r�v� ��R�L�r�r�R�'~��:�p�@���>�V���t�t��`�8(�z��L��L�8
�$�0��e��T��K�.d�R�dD�d2��6�Z�t�� �4�,b�*�,8�@�r�r����&���`�p�f�j�f�&�|�
�Z�(�(�H�$�@H$�
�$�F%�V�Dt�4�N�r(�r��2��T�v�e�x�N�P�n�&�|�D��A�� ��B�P��h�2��]�^��d���l�P�b���w�L�?�)�8�����@��1��<��s�J�f�2�6��<��d�n�
�:�H�f�&��O�h�p�x�P�l��:�2���
�o���,��m�@�d�o�(�8�h�p�F�&�6�j�(�h����( ��?�B�V�H��"��o��N�2�l��[���N��a��R���6�%��0��H��=�Z��d�R�V�@��"����'�� ��i��A��X�N�,�nn���\�N����
����r�H��Y�[���R�\�l�R� �v��F�0�f�T�$�Z�.�8�.��I�|�>�m�J��t�z
�`�n��>j� �x�hP�V�
�R�N�'�$T�h�0�&�j��n�l@"�l�>�`�D�J�B�0(�^�^���I�`� ���D��p��<�R�!��|�,�X�p�"L�l�R�����\��a���$��c�z��W��:��Q�Z�
�r�.�4�H�j��n�V����@�A��]��P��y��P�Y�^��k�T�F��z���"��0�Z�.�X�F�O�D���4�~�0�P�p�,�v�D�
��G�\��}�4�b��J�0�<�l��x�n�\���	��$�(��`�d�&��M��2�\�2�X�a�$�x�f��l���4b�`�j�V�:�F�t��2@�2�U��9��j�v�"�t�
�X��.��L�j��_��J�"�� �'��;�@�K��0�8��M�`�R��l�p�D��s�F���2�R�D��2�*�g��&�"�|��Al�D� ���I�6�>�u�l�����^�2�6�����~��[�N�z�L�H�v�:+�J� �*�z�p� �R
�*���,�`+�@���F�x*�����F-��>�r�6�F�F��^`�*�"�8�~�h�z�&�*�V8.<�Z�x�
��.�2��v��*p�~�0�:�J�.�*�*��p�D�Z�b�x�^�(�r�
�N��_�����#�>��,�L�D�t�f�p��6��m��PY�"�0�x�2��@��4�D�X�8�p��
�J�|���^��t�~��`�^�,�T�R����b�l�<��;�� �v�t�:���R�p�M��_���\��=��D���*�,�R�(��"�"�v�l��c��J��{�B��b��
�d��[�4��V�"�/��u���E��*�`�]�W�b�V�`�Z�J�P�\h�T�(�z�9��B�X�X�v�D�.��M����@n�"�h�~��!��n�h���\�z��\�v�D�2�~�`��B�n�x�z�m��E�"��8�U��(�U�x�V�z�i�*�x��a�\�t8��<&��@�P�D@�(�v]�	��b\�e�f�Z�X�^�:�"�@:�X��>�L��j9�b�T��C�l�f�z���8��:�K����,��&�8�|�j�~���n�F��X��5�|��s��,*�3�@��@�Z�e�0��
�PL�	�v�"�6��-�R^�.���9��>��4��^g��N�&��|��T� �V�R��j����l���B�$�.��;�`�$�V�v�2�*���V�����V��w�������A��W$�<������
��
�@��'�,���D�Z�f�A��T��F��+�d�n��"��?�z�*�4�T�P��`�Z�^�`�(�l�X�Z�6� ���q��l���@�|�X��9��)�`��$�
h���d�h�@��o�x�
��#�� ���G���I��
����i�X�(��K��8�������d��o�<�Z�>�<�8��N�$�r�"�~��t� �����
��q�F�l�\�P�R�h����G����k�T��(�J�&�J�&�
�F�X�Z�U�[�8�.�d��O�P�F��+�<��'��L��8�|�h�l����$�H�8��s���x�^�.��]���~�R�@8��$@j��z�<� �t� �F�N�~�r�|�z�8�P�Z�L�8��D��A�"�8�?�d��j�T�����b�z��`�8��|���N���q�P�)��D�+�e�>��+�`�
��h��~��A��0� ���@�0���4��-�6�j��n.�N�X�P��*�<� ���x�(�L�t�F�:��H�h�T�j��S���$�|��,��N��/���T��P�>�,���d�z�V��F�&�g�>�J�p��E��9�B�b�|����[��HB�(�f�b����2�F��L�P��Q(��!�v��(�:���"@d���|�z�@�(�`�0�.��"�pD�n��,�)�&�0�k��<�"��4����d�
�t�R�j���f��8�l��1����l�j��0�l�~�(�b����b��K��L���.6�P�8�R�6�H�%�2���U�:�S����U��V���M����W�M�l�.�<�J��L�"���j��
����4��=�q�8��\�h�m�=�� ��S��"�a���A���|���
�#�^�&��0��O����^��u�J�e�b�8�x��\��g����~�Y���w���
�u�{��
�<�P�(��+�p�c� �u��2��^j��d�7�g��a�*�D��2��!�T�6�n�z�]��\�n�|��s��2�h��.�&�o�~%�\�F���V�0�d�Z�`�0�r����Z��#�5�7�{���u�j�h�d��t�d�,��@��L����J�(��*�H��S��f��
���A���T�w��k��l��-�Z�8�$��5��>�)�d�*���_�<���.���^�J��T�J�H�Z�|�r�L�z�L�X�T�x�~�K���;��P�8��e�V��r�V�d�T��	L�(�8��h�j�,^b�H��n�J�v�D��d�V�Z�
����<�x��!�X��?�H�^�0� �8�&��X��c���=��"��B����l�&�3��9��8�F�j��+�y�^��h�%�Z��o�h�\�:��>�
�~�X�F��5�^�j��� m�-� �~��Y�T�>��p�"V�N��Y�L��S����t�J�I�4�0�r�>�^��~��^�R��|���
��NN��-�x�t�n���z��O�"��(�]�8���>�r�I���R��|�#�=�z�D�S��2��P���h�p�|�`��V�Y��4�(x�(��S�,���6�~��D�,�r�=��,��"�Q���
�
�(� ����~�V���o�r�z�~�@�"2�p����>�.��V��!��r�$��O�"�&��`�n�%��G�f��h�F�<�b�R��
W���T�.�&�$r�X�4�j�,�t�"��.�p�p�d�(�0�>�*��.�>�6�V�T�j�p��;�"�V��f�:�(��1jb?�h�L�h������z�.�d�|�f��%����+������d�|�>�>��G�:�7�
��q�.���5��H�;�|��V�L��a�Z�Z�d�f�K�^�	�����(��Y��&��k�o�x�H�`�b�~��0��/��~��"��q�4�:�T�N��A�l��4��%�B�S�M��*�%��{�f��_��f�y�����|�z�v��r��-��O�g�N�^� � �.�@�6���d�1�q�v�jX�j�.���P�@��}�4�v�~�,�~�H���O�j��`��W��G��
��C�,��b��C�L�(�h���E�,�J�:�v�.��S�x��=�h��N��2���G�t�H���`�0�4����=�0�8��2�.�D�B�&�d� ��7��ou1�<���b��F��o��q�� ���t�j� ��G��M�D�|�B>��4�Z��d�T�:�&��4�j��~�n�>�P�4�.����$�@� �`�.^�y��h�z��'�����9�
�6�X��s��4�f�L�B�>�r��}�J�*�P�2�:�x�<�X"�T�*�U��[�v����]���9�n�4�X��"�L�f�U��`��/�n�,��,��-N�V�s�T�B�T�N�t�D�D�T�\���D�R�$�J�D��{��j��M�X�|�f��D�h��p�f����?�.�*�\��u�"�(�V�r��B�*��,�@�|&����y��;��H�r��s���$�J�h�j�t�|�h���U��X��9�j�p�X���}��r�$�H.�*�H�$�2�`�&�x����C��J����L�9��S�<��H���2�{��m���"��3��n�K�U�3�Q�z�0�z���y(
&R��J��C������)N�.	(."� UT
!\";%@E,��	#�:P)*A�S�2T]Bx�v�]"	*2+\�ZP�&	&�	gQ
0�rQR
��*�	yQn"�*�
��Q�	 ��Q�"��h��Y

0
*��R^*C,��,��R�����$�/�S��^��W

�]�b�o��R�;	&

�����u�8�b�
v�"�T<`�50_"
B/�K*'.A,=26�U/1�,D/=Z/�O,U8Rv3�#�4(I2
eJE@b+27.!~;<1:E�(��W	
dkx6
**

,
��(�}WN'2�,qO:$

$�%�X
iN�(�#�/H��]+vf5FG,
��Y
����Y2

��&��{6���n&B��	�Z%
' JVG�
�Zb^BJ'�
�[6�E�2 �[e��
D"	&		

"�#\


�K*\A�=�t��&$�s�
"&�<�E@�{	f
	@ >[L
*�
w^D;��^z����{@N
��^�

�&�#
$?B�>�_Y
,2H/&)�*�iB|�07D-
*/T>3P1+	*A@ER%N�2�I,-,��`�(9�*SN?�	�#D�hq�a
:M�	aG-D!\�(ak%BFH1
!2:Z_ �Z�Y0$
�	Db
�ObA&
�T\b�z�# �
-&^s��&!YZ�=jK
3
�4%���TkX)
**��(TS
85Fe�":]$�L�
 3�.Q�6�+"Cf:8-\�B��b��lxm
4/<�
�e7�$�
(
�
Ӌ���_
��e���Q�n�	�f41d8��f_$
&F:
�C�L.��8�� .��!g�3p[%B49bnwnsjcV
k(@�h�}N;,76�H�Z&>!:31�T�k��+2�(��oza��T$%�@I[�Y3p!�7�d76/R!kto6S`F/�	B#"+�M�B$E9��;	<R209��#:�)�XcLS<�\�=!`q�Jy�c4=!-�Z$9�$�j��~�9bk^ � �9.��4��%0�H�G��h��O	
��k�
��k�	����m�glt.32;-<fX=-2%._	@&- h�Z�/"?6C
�,^_<Ah��?�f6�+"<]J%6-hC(|��+�0'C7���S]F&U�4�E4�N%`%6-(%�xln�*8G�Ba�t�A
��|���M�bW9�#)�.�
(
Ij`�9�!(9h,10'Z#,!#8�M�P <"j8��`)"	^<�!dE	-*D�V�o $T=&
"
��rF6
" �
�r���Q��"
,
	0�1
�؁�$
�+4'H!��;�R�[r4->
14$D�G�\�@t�
"+&�^ut*"
<6�wu�# 	*	*	��vg��v���v�	��iH
��w��wG$!2(
"

�<�w�

�&�
��K�5":,"B-BD$$(>L$��F��C�y�"H�
z �?�`&�&zi��f��[$<1
��y
�	@z�"	X�M{,2R�m���^!��^{l\%evb7",N]�t�AA>/X." &*

�0�	2t
; X;F
?&^]f%:<""%�"�7XI3 N
8#F0<%-,F>W
X�
�~y$�]�N�`�_�~�

�R�g �p�
1�
�D�-�4@�,		


*$��;��6\3*	d5R
6�~�(�+�h�v�t:�	f*
!"����,���
$0
���.��"��~06h	B�0�!7NUXa�6=6,-	�F׃�(�g�Tp�T/<�<Q;\
Wd�jO"4(�G�t���b�"�:�-�&3G5��	�(�Q�,
Ar%<k�~E�8�,1�D!��oTF9:wL��G(K"p(-,8 	6*��q��|B�D[��(-0�2�qP	&D*) R-�
0YJ; 
+
�4�G;X6�
�W0#&f%\�%��y|%&*>Sn�(6++,�Ȉԑˈ�6
"^K�ۉA&_�
���ڑ���$
	 2fDF!�0��@. Di(3`$sh
$�n�$-0 �L���O�:	�a��16��w��$"DD�v�
�z�w�
�� 
.P		(	!����0�F��D* 	mp@	
 *+0!$
	2	�⏜D�.�' ��) 6��1�t.
�b�m
0X��9��$L020>
�5��

@�*?�?(./	�%49��-�
��h-bJ+�-+,
@61�2�k��H�*o��+26j
!J)0 ,	��
GB�����
��)��
|���B	V7
@$�ǖ��̖���`qF

��D
$��-��0
�������  �B�;�'�,2V)*
	>��tlD.
�,�$	&	�,D	�J��Gl",	��o��	

"�}�%"6<!,#�7���!�0�H

R',+&6
�\0;.F	��	H_\�֝d'0,Fy>"
,
H%����,����	��T�	������	v���:"�
*�l<��c�.��"�k��E��3IkTkId4
�0<��3�G�	��l�Y��29��;���>��U^��*��%����	��,��K�i���c�ݑ�VB����9�J��c��	�Z�*��a��7�P��W���B��]���O�<�u��t��!���~���\Q��D�g�5���V�t��~k�4���c���P&�J�J�������B��,�)���j��{��F�X�!�y�3�`����~�ܑ&p|�H�R��e��'��/����n����V��.�-�o�M��D�c��y�D�t\�`��E��(��Y�<�(�d�j�'�6��/�T��K��%�6�~܄�7����K��Z���@�8�+�7��h����j����6����m��h��p��K�J�~�w�6K�ߤ��+��0��*��.�M���"�a�+�m�m���M���k����4�O��>��i�i����H��C�� ���j��W��[�1�y�X����B�D����$��(����7�G�V���p������t����~���Q����~���w��U9
�`��d�J�d���F���\[��d�"�8�6�@�D�&�@�>���P�����/�s΄j�z�P�h��V��	�l:�X$�h�2�<�� �"�BL���n�H�\�D�0\��,�8_��!��p�r�3�v �d��P�4�n�b�
� >����x�O��
��u����[����F2>$2>�3IkTkId4
�0<�=�����+�U�^�O�*���I��8�G��a��Z�U��6�{����V�x�
��i��&��T���N�b�~��4��T�
��p�l�hy��&��l�;�J�0��r�
�z�X3��	�4�b3�w�}�i%>f+w`*S�D��=�X�N-��o�}0.��2Q�$�2C

	

�j	
7*%$301�[��_��h��g�	�8��&�����D��sec����k�%�2��R�7�����A�1�,��r�_��5��^�c3?�+��	 J�"�O�� �F��D��S��I� W 
0��P�x� gXIII� hXIV� iXV� lxiii� mxiv� nxv� o!�k� rTB� s!;��}� x!�!�� }Y'�N��e��� �g	�POy>˗�����v� �3{� �g	�POy>˗�����v�w �334Y#Z4�(F:+?.w:8-��0�"�V��Y��'��b��,��`��}�m�;�p4j8v
p�d=G<NR]�"�>9�b2p@ZP�24H8�@<T����%<�*��$�6��q���&�0�
�*�6�R�\�/��&tbV����`T�\!]Ѕ $d�L�|N�&�]<J�N�t�$��Tx`�~�b(H!
�����@J"t&��(�,�%�����y��(��)����tKX�F�b�P8R/�TR�1L*DP
xDk(��X~@�8J�F�.�!^�A��$!_q�x�d�J����@�
#h%`fL*�B�Z�$2����\��/��bX**�j�!�y���s��~�j*��h��c�d��|��+&�4/j�v�h�2��x��;�@8�\�"�V�<
"��f��_�!���Z�8H�Z,@����Y�8V�`:�F��X��)�!��g�l����#"�d�!ƒ
^
	"1Z~�'!Ԓ�!��,��%�"	M��X��?:7�Z3�B.2p&��f��D��"���!��'�����^��08B��^����)�n��4�!�"L�S�
�*�{�v�9�
�e�W�n�c�L�
"���V�E�T�c�"���"�
�%�$89R�a9�,�S�r�1V� ��4�i�e�v�"���	"�`.��I��I��"�#2��#T >��}��b���eh!A>�#d��3:0
5

9�G��v�!�#�0�
���#�`��b�#rIC�#�0M�$0r�$0��"�$1[�h��eL��"�
�r��M�
$�R~�1r�R~�1r�$� p�G�g�$�E
�$���[�$�I
�$�(�P�$�O
�$���K
�J�g�$�e
�$���[�$�1
�$�)�P�$�o
�
$�ԁK
�N�>�g�/�Z�$�Z�$�s�w�Y�R,
��% �S�
�*�{�v�9�
�e�W�n�c�L�
%X��V�E�T�c�%d��%h
�%�$89R�a9�,�S�r�1V� ��4�i�e�v�%���
%�`.��I��I���v�r�%��
%�R~�1r�R~�1r�%� p�G�g�%�E
�%���[�%�I
�%�(�P�%�O
�%���K
�J�g�&e
�&��[�&1
�&)�P�&o
�
&��K
��#��#��h�&40/3�&7��&>1/7����~����~����~����~����~�����}����~����~����~����~����~�
&N1/10�������~�������~�������~�������������~������~������~������~������~������~������~������~�&[10/11�&\1/12�������~�������~�&_11/12��������&a0/3����|����~��������~��������~����~����~�������~��������~����~����~����~����~���������~����~����~���������~����~����~����~����~�
&}1/10�������~�������~�������~�������������~������~������~������~������~������~������~������~�&�10/11�&�1/12�������~�������~�&�11/12��������&�0��d�Y&�00��n��l��l��l��l��l��l��l�&�100�'xiii�'xiv�'xv�'XIII�'XIV�'XV�'.0B�'40M�
'H0r6�'d0��'x0��
'�SAN���"��0�U�N�˧�9�'�U��]�D�9�����='�0c��R��
�p���"��0�U�N��F��u�p���"��0�U�N��F��u�p���"��0�U�N��s��P����"��0�U�N��F��u�p���"��0�U�N��F���!�>5�'�()���D(32��l��l��l��l��l��l�(H100�(}0B�(�0M�(�0r:�X
�8��\��A��V���M���(�2��D�=��d�c�������^��_��d)00��l��l��l��l��l��l��l��l��l�)l100�)�0B�)�0M�
)�0r6�)�0��)�0��
*0��b�D�D�o���P��e�!������y��?�*
00�*1�*01�*2�*02�*3�*03�*4�*04�*5�*05�*6�*06�*7�*07�*8�*08�*9�[*09��l��l��l��l��l��l��l��l��l�*z100�*�0B�*�0M�
*�0r6�*�0��*�0��+e�D�D�o���P��e�����b������[���c�+00�+1�+ 01�+!2�+"02�+#3�+$03�+%4�+&04�+'5�+(05�+)6�+*06�++7�+,07�+-8�+.08�+/9�[+009��l��l��l��l��l��l��l��l��l�+�100�+�0B�+�0M�
+�0r6�+�0��,
0��, e�D�D�o���P��e�!������W��a�,,00�,-1�,.01�,/2�,002�,13�,203�,34�,404�,55�,605�,76�,806�,97�,:07�,;8�,<08�,=9�[,>09��l��l��l��l��l��l��l��l��l�,�100�,�0B�,�0M�
,�0r6�-0��-0��-.e�D�D�o���P��e�!������7�-900�-:1�-;01�-<2�-=02�->3�-?03�-@4�-A04�-B5�-C05�-D6�-E06�-F7�-G07�-H8�-I08�-J9�[-K09��l��l��l��l��l��l��l��l��l�-�100�-�0B�-�0M�
-�0r6�.0��.%0��.;e�D�D�o���P��e�!������W�B�o�.IPH�.J3��.NVS�.O!��.Qc/c�	.R3���"ML��r�.[m/m����~�._0U0X��^�.d3�.f0�0�0��.g3�.h0�0�0��.i3�.k0�0�0�0�0�0�0�0�.l3	�.m0�0�0�0�.n3�.o0�0�0ׁ.p3�.w0�0�0�0�0�0�.x3�.y0�0�0�0�.z3�.{0�0�0�0�0Ɂ.�3$�.�0�0��.�3%�.�0�0�.�0�0�0�0ޅ.�0�0�0ā.�3-�.�0�0�0�0�.�0�0�0�.�32�.�0�0�0�0������|�.�3<�.�0�0�0ȍ.�0�0�0�0�0�0�0�.�0�0�0��.�37�.�0�0��.�38�.�0�0�0�.�0�0Ɂ.�3R�.�0�0�0����P�.�3S�.�0�0ȁ.�3�.�0�0�0��.�3�.�0�0�0��.�3�.�0�0�0�0�0�0�0�0�.�3	�.�0�0�0�0�.�3�.�0�0�0ׁ.�3�.�0�0�0�0�0�0�.�3�.�0�0�0�0�.�3�.�0�0�0�0�0Ɂ.�3$�.�0�0��.�3%�.�0�0�.�0�0�0�0ޅ.�0�0�0ā.�3-�.�0�0�0�0�.�0�0�0�.�32�.�0�0�0�0������|�.�3<�.�0�0�0ȍ.�0�0�0�0�0�0�0�.�0�0�0��.�37�.�0�0��.�38�/0�0�0�/0�0Ɂ/3R�/0�0�0����P�/3ST�/
S;vBl�N��������~��������e������~����ڝ��j������ʶ������v�������������x���ߡ���{�������~��������e������~����ڝ��j������ʶ������v�������������x�	/-��
�x� ��W�/8JAS�/9!5�K�r�w��5�>
�/A��t�/D&l��%������Q��4��/U\\�/V"�;�.����p�/�'�tW���Z��
�4��[�!3y�/�!�w�/�%�R�S�P�S�P�S�P�S�P�S�p�=�
/�%��^�'�/�%�4/�[�f�/�0S�>=G�J
U�CD�N
K��c��`�-!�D�1�0A
">�1�0�C�+�
2>0�>��V��y�r�2J��
2MR~�1r�R~�1r�2] p�G�g�2pE
�2q��[�2sI
�2t(�P�2vO
�2w��K
�J�g�2|e
�2}��[�21
�2�)�P�2�o
�
2�ԁK
�N�)�/�Z�2�Z�2�[�c�@�R,
��2��S�
�*�{�v�9�
�e�W�n�c�L�
3��V�E�T�c�3 ��3$
�%�$89R�a9�,�S�r�1V� ��4�i�e�v�3>��
3~`.��I��I���v�r�3���
3�R~�1r�R~�1r�3� p�G�g�3�E
�3���[�3�I
�3�(�P�3�O
�3���K
�J�g�3�e
�3���[�3�1
�3�)�P�3�o
�3���K
��#��#����
�x� �4'�w����>��1��3+��V���)3��g��k\���8���w�I�8&3�4'�Q,��\�l�e�4-�~�E��%4.�V
?��L��GJ��.��
N��*��k��%��H��M��N$I(����K*0G#����y
��b��W��(��;����_�e�|�M��F����j�`��A������r����.���Z�O��3��J�J��Y��D�c��K���6�5�v���%�h�2�p�3����?����C��6�=��O�m��D���(��x��S�U���X��b�H�p� ��-�d��W�d�,��&���9��@��7�.�^�[�c��@��/�V�0�R�d�.��)�&�l�U�"��%��
��a�?��f�r�|��
����	��~���Y��,�y��H��A�,��t�Z�Z�c���i�Z�^�H�"�(�g��}�N�<�S�T�%u��[��&�v�Q��s���4��B���4�S@��0��a�.��:�\�6��p��2�T�~�H��R�L���D�T��z�w�l�8��n��Z'~��J�X�V�J�n�-�J�<�v�8��R�.�pd�p&
�8lT.�H�D�~��6�Nr6l\�Z�N�|�{�`�	�BJ^
�x�@�HD �4�L����Z4�,��p��/�J���&�bH��^��5��$��\��]�(�z��w�z�5X�~��
5YqY��t��?�y������t��;��4�B�(�J�5f�~ݕ�5g�����1�h�{�h�5o�~��5pi��)��Q���5t�~�"�5u�D����g�:�p�����w��O�_��v�H�v�~��.�T�5��B߷�
5�U���H�j��y�Z�X�h�e�R���5��g�K���N�5�_:�*�`�5��M�:�5�����.��-��1��N��A��n��e��L�I���Y�����V�b��L��{��>�Y����~�v�5��~ܬ�5�gC�J�^��5��B�d�5������ �>��i�N�����L�p�B�F�5��~�� 5�n/�J�|�z��@��{��n��?�T��e����_��n� �R��Q�z�Q��.��U��~��\�0��M�5��~��5�s��5��Bߟ�5�ch�f���5��~�(�5�r5�5��~�!�5�L��7�m�6�`�5��~�?�5�}B��f�T��E���8����&��k�n��-�P�\�6�~�s�6\�\�(�p�6�@��6[��6�~�R�6`ţ#����c���s�����\�N��=��@�6�~�G�6�2��o�6!�@ݢ�	6"R���|�t�+�"��G��6+�~ܲ�$6,vۥt�R�>T� �/��h�]��R�~�+�� ��.�|��U�K�4�f�\�� �V���-��(�+���K�6P�O��6Q�Z�6� ��i����~��"���e�(�6\�~��6]�����e���K��7��D�|��?�l��N�6h�~ܚ�6i_��x�T�6l�~��6m����?�s��@8��a��p��7�(�Z��H�y��e�[��
�5��3�6��D�=��8�+6�U����?�>�|�@��E����k�-�<��B��-�2��~��"��%�r��v��A��R�L�!�V��!��2��>��{�^�J�v��W�2�v��#��������t�s�6��~�b�"6��U��y�V�?� �l�b��'��p��[��,������0�V�p���u�F��2�L�p�d�Z�2�,�\���6�!6�b?�6��,�"��5����V�L��E�y��k������"�o�u� �.��g�i��B�H�9��h�y���'�s�q�6��~���6�R���X�6��U��6��J��]��K����}^�r��P��N��
��j��z���$�f���I��L��Z��m��x�T�7�Yް�7P��r�����
��7�@܊��B�75>�@�8��`��T�p�D�J�f�7)�~�m�7*\��e���Z��4�f�71�~ܶ�72b���-�B�76�Zݙ�77c��"�7<�~�ۑ'7=h�!�P�a�=�$� ��u��]�`��0����^�ZH�
�l�B��;��p|�,$��n��;�(�a���a��V�Z�7d�~�l�7e~"J'���7n�X�p�7q��.��]��@�	7x���U��@�
�G�l����B�X�7��@ܰ�+7��7����p����.�l�:�D6�0J��g��*�H�.�F,4��p�7��b݇�7����<�7��c��7����R�8�b.
v
�x�,
�P��R�8Lv�0�7��i���Y�7ˉ��)��m������7��~��7�js�7��Oܾ�[�7׊��g�7�ND*HD,8�KJ�7�O�L Z�7�PP_�&
^2
0��4B
�8Rj.XhN�8S>*|\bJ00
�8(�8:U�
�r\,
J,.&J8F66Z�8`W�,"�PH
D.b�8�8vZ,F >X>0&t�8�[��0d
$�B^8�n."�	8�^*R�@h�8�_"
H$.40Z:<
48�8�a�> "28j&2
�Zn@�8�cedX"b"�8�d�""Z>2xZr>�9faXFF0>(Rh�E,v�9%h[,@:
"�92h�(B
6f�
$ N
�9Rj�b
  BvD<D<8f�!9ol�68P8x
$p\�j\8L4
�9�o�F4~"&B�9�p�T0 000@.R8"�
9�r\`>$fX"P"�9�s�$
�9�t$�9�t9

�9�t�"�^T�:u�^(�:	v2�:v�>nJ�	:wr.(v"6�:*x0djR*j�:>y�>P
 N�:VzƁ$*"<:Nb 
4��:t|��	:{}
:"
8�:�}�<
L42�V�:�a^Z 
�:��&&F P�:��Bh^4R: "|>n�`
 �:Ӄ�HpVb ZNR`N,C�H,P $�:�
*d, �:���@(&rf"�;��T�;�'x�0*
V`�hT.�$;+�T��P<`h,
8�. >*fT@.6:H:<�^�;Q��2"R.�"D
,D($
R�;o��f@ �B�&;��Z>$Z<#..."H**�
Z8�F,V>P�;��~��;��� � L�	;���**$�;��Y$�8�4�".p&"�D8�<;�s
Z2
6"�~6
$F"@�tB*" 4��e�W�<�~��<V �T��m���<!�M�:�<"k$�(�'�����B�4�X��%��^����V���
,�$�d�:�X��b�,�x��H�F�<>�L��<?s���k�1�`��<D�M�ā<E����T��W��8�q��K��x�O�<M�g�=�<NFe�E��4�M�<S�I�	�)<W�z���j�9�*�c�v�A�/<A	gV8��G�2qDA.X?�i�|�=M.�p�<�0K0���~��~��~��~V���~��~��~��~��~��~�~��~
���~�=k0S�>�=��v=u��1�&�78
(�m�*�;8�V�Q
(�D�CQ�%lg
h.�%lg
d.�j�#&N�G&N�2%e8+$T[ �5�Z]b�-� XI��(@c<4M%tQx�v/'�r�3�Z�a�=�����y�=�%;@)
�#�
!�&�>�
>
j �>"
6�
>!"�
��e�
�>�p��9>/��1p8
(�mp8
(Q�%lg
h.�%lg
d.�7&N�G&N�>l0�j�A�>�0K0���~��~��~��~V���~��~��~��~��~��~�~��~
���~�??0S�>����5��&�1�p��
?Q0K0���~��~��~��~���~��~��~��~��~��~�~��~�?^���\�?v1�0��?� >����&�w��+��U�X�8<2����?�1�0��
?�0K0���~��~��~��~���~��~��~��~��~��~�~��~�?�1�0��
?�0K0���~��~��~��~���~��~��~��~��~��~�~��~�@1�0��@)��3�
@0K0���~��~��~��~���~��~��~��~��~��~�~��~�@51�0��@@0S�>�@L1�0��&@S0S�>�'�*�c�<A	gV8��G�2qDA.X?�i�|�=M.�p���v@���1�&�78
(�m�*�;8�V�Q
(�D�CQ�%lg
h.�%lg
d.�j�#&N�G&N�2%e8+$T[ �5�Z]b�-� XI��(@c<4M%tQx�v/'�r�3�Z�a�A����y�A%;@)
�#�
!�&�>�
A0j �A?"
6�
AD"�
��e�
�>�p��^AR��1p8
(�mp8
(Q�%lg
h.�%lg
d.�7&N�G&N��4,�BP�`Ejb>�E�(.�h�hH@�r�h�X�`V@�X�D��A��D��A�W�(�~H�A��E�n�A�X���@��m`��&~�A��Fܽ�A�[}*�|��-�A��E޴�A�\z(J�A��G�4�*A�]
rrDF�$�4@2,P�8"�*l�� �Pn(�L�J�P�&��0�f�81(�A��L�đA�f�Z>�<r"�@@�
�L|�B	�M�āB
iBB�R��3��J�B�M�?�Bj;�B�M�c�5Bj� ��-�� t�RN
�\F�Dk�T
0v*`�L2�$��8&6J�:L\~�D,8d� ��-��6�BJ�Q��BKs'P*v�46p>j.��K��6f
�686.
"0�Biwz
&��Bm�U܎�BnxCT��Bq�U��Brx��$6"
"d��Bz�U�q�B{y��.�B��V�āB�z�\(4f�"b�B��Wݡ�B�|m*h�6TLD6�J�\��2�$*$�X�B��m���y��&�B��Z��B����B���**NBjRN>�0 "06�B��[�@�
B…2) h*"*PB"�BΆ�B��\��
BІ><����.�����;��>
�z�B��]ބ�Bމ2�&�B� *&�4�j$f�V<�B
�2z�B��`�w�B�HD��jFL�B��`�́B���
2H�R6NH0��Z�.�.C��vH\,0�8 (��G��Z���<�0"�t� �`�T�zV
��c��*`:
&^,$T�CA�e�:nR)�CK�hݐ�CL��>�, �N�CQ�@܉�CRN,0�CX�@ܢ�CYNQ��C[�@ܤ�C\Nif�c�D#,@�[2�Ck�@��CnO��@i$
�Q�@b~7�C�P�(�C��@�+�C�P�$V�C��@߁�C�P΃C��@�q�C�P���C��@��C�Q�C��A�J�C�Q`�C��A�	�C�Qs��6�M�C��A�ցC�Q�<�C��A�O�C�RU�C��B��C�R��C��B�:�C�R�J�C��Bܹ�C�R�z�C��B�|@�C�Sg$R�C��B�ӁC�S��C��B��C�S�e� ^�u�(<P
�C��C�E�C�T�*d�7�@�?�v�C��C��C�U}����C��CޕQ�C�U������C��C�d�C�U�|�C��C�_�C�V>��y��,��Q�� $"�D�D��&�DW)�D	�D�{�DWM�D
�D�t�DWh�D�D���DW�\�D�D��DW̃D�D�6�
DW�"��M��Z2"�D)�D�āD*XI4J�D6�E�m�D7X�(T�D@�E�ׁDA6���V��I��X��a�DF�[�)�DGY_�DH�E�G�	DKY|D �DT�E�v�	DV6ρ�z
n(  �DaZ�4$8.
*"�Du�F�ÁDv[�*�	Dz[���U��
����J�D��G�V�D�\_ �D��G�-.d-�D�\����D��Gݡ�D�\��D��Gݒ�D�\��D��Gݷ�D�\ɃD��G���$�D�]4 ��#��p��m��v�D��G��D�7�������,0�D��G�v�D�]�
�D��G��D�]���u��<<��[��r��k�D��H�{�D�^�$�D��H��D�^��D��H߭�	D�_P6 $H�	D�_� ��9������b�$�D��I��D�`� (46����"2�D��J�[�D�a��D��Jܫ�D�a�.�D��Jݏ�	D�b#\,
Jv�E�J޸�Eb�
B�E�J�F�EcY�E�K���o�EclX�E�K�$�Ec�PD�Ec�V
T*�E,d��E.�K��E/d�4��#��@��C��T&",
N��}��D04��I�EN�L��b�EQfj�
ETf{��	��<��7���R �Ea�L�r�Ebg�Ec�L���EegM�Eh�L��&�Ekgt�El�L�ځEmg��En�L�߁Eo�c�'�6�Ev�M�J
�Eyh�E|�M�e�E}h3��A��f

��S�E�h�.~�E��M���j�	E�h�$���E��Mݔ�
E�;���pD.��]��b2 �E��M�9�E�i���M��L$�E��M��E�j?`Y"�E��M��E�j���7��`�E��M�d�E�j�"	�E��M�1�E�j��E��N�$�E�k�E��N�=�E�k��c��4&2$�E��Nޘ�E�k�8&&N�E��O��E�lM",��
�� *(��a���F�O��FmF�{�<2�F�O�@�rM�
Fm�
��&
 ,~1����@�F+nO�F.nW����h&�b� 
�F?�O�~�F@oRN
N?
���FMo�FN�Pܖ�FOo�0�FS�P��FTp:��c�� �F_�P�ƁF`p�"�Fb�P��Fcp�Eb����\@����-4�Fw�P߼�	Fxq�T����\(�F��Q�)�F�rx�F��Qޥ�F�r� ��C��V��M���F�>���j(��}��,�F��Rܖ�F�sq&


*
"23�"'F�F��R�M�F�t�6$�F��R�V�F�t��F��R�o�F�u 
��'�F��S��F�u@>"��/��X��W��p"(#��C�F��S��F�v�F��S�7�F�v%&�F��S�j�F�?���n�F��Sދ�F�vI��}��,L�G�T�J�GvɃG�T�U�Gv�G	�T�"�G
w$�G�Tݩ�G@9�G�T��1�GwX�G�T��Gw|�G�T�L�	G@X��~
�G!�U�.�G"x$4D�G)�U�ف	G*x�62 .�G5�Uݧ�G6yH��'��BD�GFy����GH�Uߩ�GJAO��t
��q��(
6�G\�V�ԁG]A�������6�Gd�V���Gg{='�
GkA���}��'��LF+�Gu�V߲�GvB������L�G~�W�K0�G�{�
$l
�G��W�.N�G�|��G��W�b�j�G�|ăG��W�G�|̓G��W��t�G�|�G��W�\�G�|���{���G��W���G�}@�G��X���#�G�B���
��'��B�G��X��G�}��G��X�`�	G�}�,��I�G��X��	G��X��
0^�G�C+��\��/��p���G��X�p*�G�mD��+�G��O��G��
 �G��Y��G��&.*$D(: �G��Y�~�
G�� L1
����/��]��f�G��Y��
G��=��k��@��%��P��G��j2$�H�Z�݁H
��H�Z��H�� �H�Z�o�H��H�Z�݁H��H�Z��H�<	�H�Z�X�HDv��8�H�Zތ�H �\�H"�Z޷�
H#D���V<
J
�H4��s$$��3*C�	HA��V&6�HJ�[�s�HK�B*�HS�[�ݑ	HT�e$��i��.��m�H_�ӃH`�[�e�Ha��@F>��Y���Hp�[ߔ�Hq�{�Hy�[��H|���
H��� �H��\�
�H��9�H��\�9�H��@$@�H�����]��
�H��r�H��\���H���=�H��\��H�E���0�H��]��H��� �H��]�I�H���	Hć�P(
��[��t�H��]��Hψi�H��]�1�Hшo`6�H��]ޓ�Hֈ���%��~�H��]��Hۉ7�H��]�#�H݉B�H��]�R�H�bL @$����"��!��B�H��^݅�H�!&�*=L>H�H��^ބ�H��
 ��C��.�I
�9�I�^߳�I�=�I
�^߾�I�E"�I�_ܸ�I����k��lt�I�_ݠ�I��I�_��I�	�r d����~�I'�`܊�I(� �I-�`ܻ�I.H��>4P
,
�I9�`ނ�I:HN�I;�`��I<�D(�IB�a��IC���IE�a�U�IF����:��5
6
 T�IR�a�k�IS�C2�	IW�� ��Y��t
�I`�a�ׁIa���Ic�a��Id��	+0"
0:4Z�I{�b�I�I}��(�I��b�k�I��B�I��b݈�I��
&,V�I��b��I���b�I��b�q]�I��Q 
Z�I��bޙf�I����,�I��b���I���U(Bl�I��b�Z�I��m��I��c��@�IĕȃI��c�F�Iǖ,�I��c�6�I˖<H�I��cމ�IΖ��I��c��IЖ��I��c�2�I�I������I��c��
Iؖ���3����u��
�I��dޠ �I痖�I뗾$��5�I��eܐ�I��%B<��J�e�ρ
J��*(���J�e��Pl�J�1�J�A"�, �J'�f�ƑJ(��
 $��-��X��K��X��K��L�J9�f�r�J:��D
"��;��T.�JP��
�JR�g�ۑ	JS��

h7�J\�g��h��J_��Jf�g�āJg�1
,�Jk�g���Jo�T$�V0�J�g�ׁJ���J��h�/�J�LăJ��h��J��2��C��p(�J��h��o�J��s�J���(: 
��9���<�J��H��J���*�J��hߌ�J����J��i�7�J���>����l�J��i��J��]�J��i��J��i�J��i޲�J�.��N�0�<��B�"�Z�r�P�T�J�Ny�r0"�$20�~$(,@H�	J�Q3vf�J�Q�J@:
�J�R��("N* �KS�d,� KTL�4$6""4(P  �K;V,&`2 (*�KOW�DKTW_&,L"J0L",F�Dj>:.V"�r
bH,�|>|D �^Fd6H�b\JN&�.K�_M�<z8�$�&p&N
,:4F
6Rx*.b2B&:�K�c�K�d

$(6�K�d�rT4,b<
�<� K�f��*>T$�R�:�HB6D�j�0"z�Lkn�Lk��<:Hd:�4xl�(Zd�L0o&�,* �*$P�.�L>qE�X�DF�LFrZ
�6$�3LTsM�x^4:<t8( (
(�6"&

.�L�w� .�
HB2*p$�!L�y?", \0J.Fv
@�X�Pn
<$�L�|r,6�B h(
,(�L�~/T�v:�L��,�L�
 4�
�0&0L..X�M���&M�>P,0
>2�>B�~�6t:z�N$�R�r�&F: �M6��  z"
&�MI��$�7MN�&HP0�f,>$ f42�VN|2&�"DD"�M���4�M��M"t, �#M����<,&8(""L(>h$4t2�$.@�MҒ��<BL` 2$�M�.:�v0:�$M��z,>
.�(.

j2
&$.<�N�� x>$�T$:�N$��N)�6$$.
$l20"�X �R��
NH��f,
�NT�4J(\�
NY�@�����Z����B`�$|�p�Nc5���C�Nh�h޲�NiN.�Nk�@�X�NlOs�D�,�z�Np�Aݱ�NqQ�<�Ns�A��NtRd�4(�V�Nz�C�X�N{U��:�H">�N�L6�j�n�~0l�N��V�̑N�^C�Fv�(�Pv���
��)&�N��K�B�N�d�� ��5�N��K��N�e��N��^ݴ�"N�e�����*�
�$4��/��t�@�Dz�t@�L�,��S���p��b�"N��g��,�R�0
�~�N��P�K�%N�r>��}�?��P�d�>�<8\�6�<�\��P��B�^�p�P��D`�H�<d>�DH�<tr�@�N��[ܞ�
N�Q"�V�|&N�nh��(
��{�N��_�<�N��j� �8����H��B��O���H�6��*�\�$�8�Z�.�,j� 
�6,r�`�2�j��O&�N�=�0O'{�3���A���w�*�Q�b�,��G����h���&��E��+�l����~��5��r��7�)�	���2��U�6�P�H��	��j��k��@��k���0�J�OW�X�L�OX��N�R�O[�g�=��8�2O_�!��V��
*6g:�; 6�H	�C�W�:�- 6�l�.�O�%ʑ2O��!��V��
*6g:�; 6�H	�C�W�:�- 6�l�.�O�%���	F=89tut7:��
��6��u�PCL�PKCL�����PAS
�Pk���T	��_�
P+0ׇ�~��~��~��~��~��~��~��~��~�PI^t��M��4�9�D�^�a�G�|�j�o��O�PU0{0K�	PVR��`��T��s�X���X�y�P_PV���PaS̃PbSSE�Pf0ǃPgHV�Ph �<Pm00��l��l��l��l��l�dP�'00��l��l��l��l��l��l��l��l��l�dQ
00��l��l��l��l��l��l��l��l��l�2Qq100��l��l��l��l�Q�.0���Q�ppb�Q�'S���<��~�
����r�Q�Jr.������Q�0�0�Q�0�0�0��Q�0�0�0�0ʉQ�0�0�0�0�0ȇQ�0�0�0�0�������~����~�Q�0�0�0�����~��~�Q�0�0�0�0�Q�0�0��Q�0�0�0ɉQ�0�0�0�0�0ȃQ�0�0�Q�0�0�0��Q�0�0�0�0ʉQ�0�0�0�0�0ȇQ�0�0�0�0�������~����~�Q�0�0�0�����~��~�Q�0�0�0�0�Q�0�0��Q�0�0�0ɉQ�0�0�0�0�0ȁQ���Q�00
�2Q��!��V��
*6g:�; 6�H	�C�W�:�- 6�l�.�R%ʑ2R�!��V��
*6g:�; 6�H	�C�W�:�- 6�l�.�	RN%ʂ�.��EA��C��L�RYN�.V

6�RjOR 

2�R}O�

$�R�P5

:$"L�R�Q/.`@D$@�R�R��R�R�*�R�S08:$&"�R�S�4.(
�
ST�T**�SU2L&:$@
2�	S9Vu6$�SGW�!SLW*($$
"&>(OT" $�SoX�:
H,�S�Y? "
T

�S�ZH8(BZ�$.�S�[�0�	S�[�.H�	S�\{(D,�S�] 6	 �S�]
.Z
$T�S�^\&:&4
4$�T_.,$8�T_�
$�T-`q
��{"�T?`�,
D

�TSa� f
�
Taa�"0,@�Tqb��-Twb�6"*88&
>,J4B
(�T�d�"$�T�eC:4D�T�f
�T�f

(
�T�f��<�T�f�
0(6L.�T�g�$:,�Uhy0D�Uh�
4&.�Ui{"&,j8$:(,�U*j�0.F
�U8k	<,("�UHk�l\
�*UUl.6. 4*$lT>
Dl,
H�U�n��*U�o>B,$(D(2.|0�

B��b*�U�q�:�	U�r*
�U�r�(^<
<@�U�si$0
 �U�t6�@&�U�t�(�V	u�:�. 
,(V2B*":�V.w�(D*
@
�1VBxcV0
&.">2
�VuzH0
T"F
�V�{j2
$�	V�|
�V�|F
$R&(JF
 
(�V�~=B 

^�V�?82,>L�!V�.
>&n6" 6*,8�W
��4 &�W��r
f
�W3�p(
"�W�(`�Y�*

(�WR��J�WW�
 �	Wg�y(�Ws��.
84>>�W���

�?�v>�W��E&�W���,
"�WÈK
 <"�WЈ�*�W։ 
�Wꉠ&@"H
�X��

�X��$"

**�P
$	�X/�f.�X9���J2
 i�XX�B4:B.
("
(&

�X��en4
 42�X��?D
B,L�X���'j

2
�X���02*
J 
�X��&
0
�Y���Y
��

:
*
*�t
�Y&��<|(4�Y4�n$F4(P�YF�{@
�"YX��<

0�:2 ."~ :�Y��$ "�Y��T��Y���:(
�Y��x

&P*(
8�vD$�YΝ:>:( &�`*$�Y���0"�Z�G"�?; ]� 9�  
�������� "9 ]?�`0�0�0�0�0�0�0K%0000J0j0~0�0�0p0s0v0y0|	�0�0�0�0�0
  �0	0	"f 2�!�"�"�"'"j"+  �	��!�AR0AU0��������'*6�T�M�\N��O_؁1s6�oN�)O�P�Q	Q?Qi	Q�Q�Q�RKR�R�/T,"T�UVOW7W�%X�Y,Y�	Z5
Z�
[Z\\8K^6
^u^�^�^�_V>`�`�a
aX(b	b2b�Td�e4
e�
e�f5	fgg&gc�j�$kxkk�lgn�xr;r?r�
sN1t�u�u�u�v vGvavhv�v�w%w7w�<y�z
zaz�z�{{p�~Y
~�~�~�LP_h�
�4�e������j�w"�F�8����D��%�����*������0���������������F�b���O�Q1����*����*���.�����=�����o��=�Q���������������"�'�.�M������7�]�����	�����	�_�f
K%$`	!`3�3�2�21")�0��L0
�0�G	00�00�=3�3�$�$t	!p	2R$�$#�#�3�2*+&&!j%�%�%m%�%q0�	  'v!z
!�0��]�\qF%�� �$�����w'�*; ]� 9�  
��������	!1* ]�?�`0�0�0�0�0�0�0J0j0~0�0�0p0s0v0y0|000K% 2�
![!S t	 �"YZ <]� 9�  
��������
 2�
![!S t	 �	0(.:	0(.:!S!U![H	0\	!p!z!`A0J0j0~0�0�0�0�0�0�0�	2 	0	0?$`$eE$�$�0J0j0~0�0�.2�	2�2�2�uaA0J0j0~0�0�0�0�0�0�0�{aA0J0j0~0�0�0�0�0�0�0�0�}aA0J0j0~0�0�0�0�0�0�0�zaA0J0j0~0�0�0�0�0�0�0�yaA0J0j0~0�0�0�0�0�0�0�!!3�3
3333(3.34
3>3C3K3O3U3
3333(3.34
3>3C3K3O3U000�0  &h03�	/Y"r000�_000�_��3/p#�#�#�0  !�!�!�!�%�
%�000
0�	0A0K0M0QA0S0�	0�0�0�A0�0�0�	0A0K0Q@0S0�	0�0�0�A0�0��E0�0��0�0	0
0A0LB0R
0�0�0�60�0�
 2�
![!S t	 �"YZ; ]� 9�  
��������
 2�
![!S t	 �	0(.:	0(.:000�0  �[�b.��F_�&s6pP�.�`NN/N@O}PRS
U`VVqV�W3W�Y�[~^^k^�_acBd�f[h0h�ii�jPj�l�!o�p]p�rUss�s�tt/t�u�vv�w_	x-x�y[z�|�}	}�R���������������C$������2��	�R�2���-���3t�`0�0�"f"�"�"�"�#"'"�"%"j"+"vU0A0�M0�	1�1�0�"�>�v�:b
�"r
>�90�0�U0A30�!0�	1�1�0�0)4)�	$�&	1�1�#�%� G1�"v"�"�"�#�&r0�0	1�1�#�
	1�1�
	1�1�	1�1�0�	1�1�"f"�"�"�"�#"'"�"%"j"+"v"�>�v�:b
�"r
>��'fwlwM5�g	��!�4���"�d.�O�(OwP�P�%R�5]U.V�WEWsW�X�X�X�YYuZ�7a\g6^m_�_�a��E�J�O�/Ec;c�c�d�&fl9h�h�	h�i�j2j�kk�k�m&m�nHnKnSo�(q�rns	sn-u=u}u�u�
vv�w�x�y�#{.(|�}=!~�
c}Q�����
�Q�Y���$�a�Hz�[��H�����d��D�� �!�IU�a�ȡIh���F����|�I��bݺ��I����	���3��
�2�������:1�k�v�	�I�i�m��v5N#Q	Q�R1RxS�T4 VWW=D^�.c�c�d�f� j�k}n�p�rOs:3w�y'!|R|[}�����&�����7���@��#���
��$�b���
��
�*!Rq)e]g��R�2"d�2"d���

R^0�0�0�0�A
1	0	0"$��t�2"d�2"d	NtOKO�O�P2PQP�P�P�P�QQ'Q,R�R�S'S+S�S�Tm
UUZU�VCVa	V�WW$!X�X�YY5ZD[�[�[�[�	\t	\�]_]y^P_+_t_�_�`V`�`�a�a�a�a�a�
b�b�b�-d�ee<e�e�ff�f�g�hph�ipj�j�j�j�kk�k�k�k�l&*n�n�*q�r	r{r�s]s�tgt�u{v9v�v�wwVwsx`1z4{?{�{�{�	|9}�}�~��!�������
��M������\�_	���
�X�`���<���H�������+�v�z���c�u������7�]������ �R�]�(�3������������$�.�5�\�n�������������%�9�V�w����"�������J������9�V�m�3�S������������C��PK
!<�9��3chrome/pdfjs/content/web/cmaps/Adobe-Korea1-0.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�q%�����������������������������������������������������������������������t�PK
!<��=��3chrome/pdfjs/content/web/cmaps/Adobe-Korea1-1.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE��qG��������������������������������������������������������������������������������������������������������������������������������������������������j��PK
!<��ȯ�3chrome/pdfjs/content/web/cmaps/Adobe-Korea1-2.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE��qH�����������������������������������������������������������������������������������������������������������������������������������������������������/��PK
!<:@.�Z�Z6chrome/pdfjs/content/web/cmaps/Adobe-Korea1-UCS2.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�����_��V�3�O��p���q��4�[��5��~��J>��P�*��	��$N���偙8�A��X�!�2��W�&�uK$�U�	�0�sDA.[<��⁇^V���N+ �C�D�u:��l;��W��H�3� �Z��&e	��\K#�
�%��|�3��Q��!��(�	�&l�$�G�
�`�K�>*�o�C��!�(%	w	WE$$a�
N!�V
�E�^#*��3�!
$-��M�v�S�r�1F�9������TCl��4�i<�P9��DJ��t�3�} ���6..

*.(
&$.>

6f. > P>.&>D(.T
..6F&66.

,&v
&&6
�1��&&�?�+>�G�c&(�?Z��
>
6.�
.&&N&6nD6L&
..L.&�	�o 4�6��&>P
6L.F�������8���T6&�*���
* T
	66&&6�	*���5�P*FV(	�|6(�
z���4(��3���
>.@
.&&L.PL"f6
&&>&6X"L.D
....

*

*..&> 6 &>L
(& L&(&N&Pn66L..&L. �	g�D�r�o�	|ͧ&.�"��LPL	6&>�	��t(&�,���(L	6&(L*6�=ѳ.6�.J�0&>
.	�.&���(D.���|
��ԋ(D�,��P
&(L&
�

'֐�
3���
@��
jכ��>j�&�b�L��
�^�F�n�h� �M
zg�8�j�~�r�<P�$`�Z�"��V�T�\��5�p��J�r�.�$��>.� ��A��f�r�Z���p�
�J�$�.�l�x�R�$�l�B�v� �r�N��Q�b�2�x�\��Z�h��2��!�j�6�B�$�<�D��\�d�,�x�4�^���h��
ɟ���e��
�P�H�.��{�X�&��j�>��X�N�4�2�$z�4���^�^�2�@� �r��yV�&�J�:�NR� �`�L�&�@��`�:�J�6�F���B�#��B�
�v���$��D�R�6�V�p��\���<��d��u�n�\�0�v�� �2�D�d�Z�j���d�<��o���2�&���S�����U�@��8�b��v��0�d���n\�.�h�f�v�B�P�\�Z���`�2�v�R�P��O��$�v�z�h��A�L��>��t�b�`�$�,�l�Hj�B�:��p�D�^�z�V�Z
H��$�~�d,���R�6�t��\�~��Bh�B���M� �L�,�"�D�b�2�^�Zn��B�V�*�\�z�^ ��0�:��j��A�@L��,�$�,�^��~X�Z��"�v�|�&�Z��(�\�:�Z�,,�d� �^��`�l�"�0�>�`�t��M�Z�,�8�`�l�P��a�f�X�h�T6�`�R�j�
��y��~��� �Z�h� �T8��j�&�:�@�P�|�X��;�\�L�&�~�h��fT�j�J�x�t��g�(�,�Z���n�&�l�,���D�P��\�h�t�T�`���u�\���*��q�|�.�n��x�.�B�N�&�r�@�R��k�h���+�R��&��x�P�~��`��e���0�Z��w�X�4�~�>�&��F�$�R�*�X�@�\�4�L�Z� �F��v�p�"�*��'X.�R�R�
���D�P�
�@X�H�~��V�6�p�L`�B��jJ�*�4�4�<�|�X�vR����d�(�� ��� �>�d�"�6�d�F�Z���#�h���*���3��x��h�f�&��C����c�H�Jh�>�:��
�"�~�|� �T�|�.�p�f��%�b� ��<��7��X�x�<�|��)���v���#��@�n�h�\�4�h�p�l�2�F��d��[��~�0�V�"��
��~v�.�p�8��F��)�<~�<�@�(�
�|��Z�b���.�n����t�|�x�J�v�V�"щ_���7�|�4���N������r�$��]�N�6��$�|�.d��`��z�D���f�,��g^�X�4�4�P�2�� ��.@�(��Z�"�
yz�4�T�r��zL�<��l�p�\�R�z�\��>�e�X����)�$�H�J��x��M�c�B��P^��F�K��Q�~����)T�G�n� �$��*�H��Q��+�F�@�N�>��;��J�^�+�l�6�d�)!�[��h�P���n���v�d����h��O�-��v��U�(�X���P�M�t�A��6��{�x��N�R�\��@\�2�h�.�>��\�~�X��>�P��A��v�z�6��}�l�X�2�d�2�r�"��l�$�r����$����~��t����b�*��H�,��*�>�8�h���H5�p�J�F���\�X�"�� �X�"� ��-�b�v�C�Z^�J�<B�l�"�*�(�4�V�R�(�n��V�j��*��d��2�:�8�n���H��
�(�t |�R��P�J���f�"��
�t�6�H�l�H��/�:�<�,���"�T� �p��M��&��I�@��l�4��<��|�:�~�l�b��\�B��S�t�l��\�V�$� �*��}�� �2�LZ�:��m�u�v�\�d��4�>�D���S�\�d�6�r�t�z�,��i�z���6�X���_�d��V�8�"�`��%��,��'�(��R�(�.�0�`�,�0��i����+��$�"��R�$�6��1��$��R��'�P���o�.�D�>�n��8j��D��f��3��z��R����(�F�V�b���h�Xl��g�T�`�J��l�>��e�^�D�~�J�~�T�t�|�n�x�L���2��P�v�r��s�p�V�&�v���#�"���~�B�*�t�Xf�n�.�*��"����-��6��
���3�Rv���r�n�,�2��*�$�*�X�x��t��S�d�@�P�t�t&�{��o�H�D�~�N� �:��;��r�N�n.2���c�&�&�N��h�f�T�8�b�~�T�J��k�P�@�`��h�V�f��L�2�V�L����@���4�d�r�,�p����b��c�����E�(�Z� ��b��?�p�$��\��i��p��_!�"�z�B�d���7��\�l�|�,��L�2��(�l��x�<��|� �
"�T�"�L��Y�@�"��B�^�p�f����f�N�h�Z�U�<�(�b�S���V���N�L��;�l��v��8���
�6�P�v�&�4�V�$�V�n�t��L�N�.�b�j��?�8�b�~�:�@���3�D�F\g�,�B�z�0�"�@�t���Z�0�z�,�d�T��(� �V�:��{���f�H��
�f��,'�X��E��P��
��f�~��^6�<X�j�$��a���Y�N�^� ���n�>�"�
�T�8��\�6�e��)�F��L�l�.�F�f��B� �V��>�l�D�v�H��tN�F��I�N�,��n�>��D��U�$�a��d��U�F�
�
�^�H�|��`�V�~� ���"�8|�x�F�h�p��H�*�*�J�P�.�F�N�p�6�N�r��s��^���x�X�&�n�\��^�V�j�\��a�*�&�m�L�F���B�v��X�0��R�4�p��P�R�$��{�|�,�:�F�h
l�:�^R�.�d����*�f��g�x�d��D���"�j�:�j��
�\�V��L�����*��`�>�&� �b�T�R�(�L��.�&T`�2�J�f�f�z�8��Y� � �|�X�T��J�`��(��A�R�x�:�~�<�^��|�t��� �J��6��*�T��Z�T�:�X���:��5�
�<��j�8�&�*�R��
w����F�8��w��T�f�$�e��D��G�P�<�T�*�,��J�d����|�@�h��	�H�\���&�(�|�]�H����p�,�|��\� �^��,��7�D���:�4��K��~��F�|��_�P�d�J�6�L6���Z����&��p�N��W���n�l�V�P��L��z�dl��n���
�t��l�*�F��R�j�H�d�|�d�R�X��M�m�� �n�#�cg�T�T�H��~��*�>�^�@��M��,�*� 0�l�R���t�f4�,��2�^��l� �^�0�l�>���J�0��2�8�
(�p�
�Z�f�~N�(���!�#�D��|�@2� �H��B�Z�6�V��(x� �(�\��+�v�N��3�B��B��P��/��v(L�b�<�H�T�.�|^�,�l�>�X��$Pl��x��.�|��T��*��0��^�N�� �d��:��x�>���z��>�L�v�X�(F���rV�N��U�J�@�P��izr�H��>�>�d
��8�n��l�\�2�\n�Z���f$��b��:z��J�v�x�v>�@�N�Z�4�&����j�.�`�|�|���x�"�D�z��'��y�"��b�b�h�0�r�r��6�n��s�4�N�F���A�~�H�P��p�$��i��� �8��K�X.��L�F�X�P�0��.�"*B�`�:��l�H�~�n�X��D�<��|�Z�x�z��}�<�~��m��2����X
�f��)�j��$L�2��x�X���<�:�6��:��~�N�6�d�X�8��&���P�$���.�~�\�n�<�8�~�h�2�&�j�8��>��%�`�z�v��.�F�l��L����n�X�z�^�t�$�"p�
�@�"��Z�(�0�&�N�@��
�P��^�H��@��>�b�x�~�x��
�.�p�F�N��j�2���V�H�A��0�t�$�*�~���BJ�T�R6�d�t�b��y���H�Z�f�l�~��	��B�F�2� �:�� �0�
���L�^��Z�&�P��n���h0�d��2�X�0�&�&��|���A�(�d�b��X��~���i�"���d�6���5�y��F�>���4���+�2�x��M�L��S�P�J�8�N�t��
�D(�H�6�z�x�&�2��� �R�H�.�j����T�f�t�<�,�$��*8�h�x���<���V�2�>�N���P�RD�"��_���R���x���
�R�4�,��"����.��n��9�JD�.�R�J�8�F�j�V�z�^�4�Z�@���p�F�f�\�|�B��,�N�Z��Q���8�h��=>��j�z��'�/�&�Fv��)r��j�R�n��B�6�0��#��8�(�0�&*�h��b�r�b$�vj�N�
�.��p���n�fH��
�>�t�>�z�>��
�N����J�@<��
��>�2�H�.��f�n�&�l�T�(�4�(�h�"F�h��@�^R��)�d��D�AB�n�z�^�2��(���m���.���c�J�����P��F�X�&�4��B�
��j�&��'�P���8f��@�D��8�"�r��L��]�\� �
�p�f������}��>��`���f�X�<�B�h�N�$��i�P�"��2��q�z�@�V��F��|��'�X��$��t�0�:�`�2�V���n�2�6�.����%� �D�T��w�6�t�J�8�x�r��v�.��;�$�@���R�N��=�h�<�*��z�(�T�b�0�N�j�T�Jt�J�x�2�r��X�x���
�8�4��|�J�n��X�"��Q�b�4�v�p���X�j�D�l�r��}��2��9��n��H����O�}� �P�8�z�.�@�`z�f�
:�t��e�p�2�2�.��j�,�(��m�� L�(��2+��n��B�<��,D�R"�r>�
�Z��:�"�v�4��c�~�V��~��	�J�t�B�~�
�6�P�R�.��s�x���}�X�p�\�Z�Th��t���0�&�@�� �T�p�f�
��:�\�Z�r� ��/�N�:�0�"�r�8�~�h�D��\H�"��2�Z��>�x��y�p�v�P�*�j�L��.�X�f��t�T��P�H��d�0�6�8�J�T��6��(��y�n���`���r�r��V�h����v�XL�4� �&�n���]�~�|ׁ�f�<�D��%��
�<�D�
�R0�T�"��Xx��Z�B�D��C�a�r��@�@��=�*�L��1�*�@�.�R��U�&�j�z�P���x�h�|�x�T�:�V�l�H�FL�.,�B�^�|�"�<�pL�"��)�f��T�
���A�6�R�^�~��^�|��$�l�4��:��
��� �����!��H�f�:N�&�� �j�N�b�L�v�P���N��l����F��Z�h�4�XURֲ �
��z�X�2����s���b�p��>�4�n��8��C�,��C��r��r���6��b� �nP�|�H�,�h� �f�p:�>��.�r�^��4��j���'���&��#�v�(�$�6�X��L�>�8��0�F�&��.�0�>�v�x�R�$�j���9�:��.��r� T��^��B|�H�f� �
�<�Z� �P��l�t\�(�@�4� �r�8��"�"�v��~�H��K��jP��c�R��T��x�D��n�!�c���H��[��:�^���`��1�
�����t��1���Z��i�V�`�|��9�x�J�6�d�^�X�0�~��"�
�t�T�V�N�0�f�t��?�0H�X�`��P~�
�8�L��8�Z� L� �8�^�V�&�X.��+�$�~^�:��A��X��D�4���n�@�v��:�r�>��o�2F�t�V�x��*�n���}�`�:��N�$�t�f��=�<�o�6�V��R��W�J�Z�@j� ��H���(�.�`�z��R�2�H�z�\�b�H�0���a�:��\�x�&v�x���|�f�b���j�`�B�F��2�^�0�j��J��)���&�f�x�4�V� �`�,�J�F���P�d���t�(�� �L�D�B�l�`t��j�0��H���v�<��8��X�*�R�*�R�F�>�>�N���C�B�z���'�F����>�~�p�P�,�V�|� �$�t�x�b�"�:�>�F�&�.�"�<�*� ��y�L�,�@�*�8�|��~���b�`�@�6�^�<�2�x�z��^�N��l�p�h�(8�l4�F�N��
�~�(�D�V��P�8�"�0�@�2|B0��<��j�&����< �d��/�D�Z���J�j�j��-�:�0�@��<�p�x���s�(��v��k�&.�\0�$�t�,�J�X����h�h�.�R�
�R�@�2�8�~Nl|�~��X�\�x�@�V��x�b���p�t�N�f�8���J�&�J�
�J���1�4�T�N�p��X��<�^��|�@�X��t���,l�B��z��y�n��:��B�,�|�D�6�.�pF�~r�J�.�0�p�z��~��6���h���H�V�,�f�P�^�T�2�N�<��j�.�2���-�n�V���W�b�q����m�^��"�t�R��(�~�&�2�(�r�|��2���~�*���|��_�&~����L��5�*� ���v��&�~p�z��.��R�P�L��jR�j�x�v�>��T�2�P�z�8�b��/�V��7�l���L��:�F�8�v�*�>�D�f�|�|�&�*�f�}��)���&���!�:�a�*�p��9�^�^��Z�f�j��J� �|��{�<���,�R
�N�~�p�"�D*�\�zr�"�p~�F���>�`�R2�@�f�n�r�f�}�H:�8�D��a��@�r�d�L�B�Z�P�@��6�4p�r�:�Nx�X�4�D&�:�f�N�D�,�2���t�j�&���X�p�<��h�n�n�T�P� ��x�B�@���j�=�>�
�"����#�x�"��a���R�*��|�d��L��\��F��K�h���T��T��u�|�l�b�R��^�!�J��^�d�$v��'�J������*�j��.�a��b�X�p��.�&�v��F���R�ZR�V�T�l�Z�F��b�J�>�x�J���t�N��n���L��JZ��W�T�@�*��Q�l���.��s�n�J��8�p�<�d�L�&�R�*�|�6��9�F�(� �f�N��`�2�T�6��z�V�L�^�r~�H���r�&�d�T�F�V�TZ�N��Y��N�x�>�>�L�(���I�>�.�@�4�n�@�`�>�*��?������
���'�v�*�2�`�
�^�j��o�B�P�V�^�,��^��4�8�&�l�F�t��B�8�f�~�p��8PD��W��6�R�P�n���:�t��,��
�n�4�\�8�<�8�d�h�:��+��N�V�I�@�T�J�R�J��� �^�Z�J�0�(�(p� �f���4�,��y����8�,�n�H�*�(4��.��W�2����f��Z��/�4�j�6�T2��������_�D�t��t�0�*�r�V�L�j�J�*�$��}�a�6�4�`��9�6�~�h�
�
�\�L��r�*>�X�p\�zf�d�
�h�d�X�R�<���z��N���O��f�#�X�d�d�^�D��j�<�q�A��
�+��2�^�l��D��>�<�>��`V��c�L�`�n�6���b�FZ�d*�<�@��O�h�>�B�"�f��(��2��W����J�h�X��-�0��M�v�:�r��;�F�r�j�T�t�R�~�Z�^�
�b�R���U�8�'�d�m����\��_�,�H�l�<�P��)�
�d�T�*�p���<�(�P��r�4���6��8��k�H�Z�*�D��x�T�6�Z��z��j�Z�v��g�:�x��4�
�x�T�0����<�G�4�"���F��`�2�^�*��a�@�T�\��5���b�
�~�@��F�>�V�G����x�8���k�j��|N�p�l���"��'�x�l�f��~�$�*�F�����V� �\�� �4��p�<�$�&���\P�f�h��#��X�P�~�
�P�>�
�~� ��|����:���F�$�:�
��e�\��v�x�&�������~�*P�t�n�<����X��c�� �P�p�N��B�B�\�2���l��	�L��.��>��E�*H�F�J���l�t�4��hߖP���C��"���Z��TL�`�r�D��j�4���c�~���"�~^���u�L�H�n� �4��*�&��P��D��4��A�^�:�>����(�\�~�V�*�R��h�4�T���u�r�E�B�,�n���C�J�8�n�\�R�����
���v�*�����,����D�X���T�T�z��+�>�b�"�D��~�0h��TF�j<��&�4��L�J��=�D����k�Y�f�R�|��t�~��4�X�@���6��r�,�>��l��<�D�,�~�@�.�|�L�2����u�P����J�`�H�6��{��R�0H��n�b�"��8��z�J��B�6��f�d�(h�
�d��Z�&����&� �(��d��&�H��)���@��;�h�N�������8�h���&�
�h�,�Z�X�f�$�\��K�j�p�h�`��.`�d�z���L���l��zk��g�B�j�*�(�N�n�
�y�Z�
���.�v�H�v��U�*�X����5�6�6�H�Pr6���i��6�:�R�0�R��T�r�J�T��r�v�j��)�\�8��.b�D�\�8�0�"�~��r�~�&�j�>�&�6����\�9�(��0��-�.�,�Tx��*�Zp�h�p��.��=�R��fZ�v�r��`�P�l�N��U.�`�R�j��V�h�D���>���8�
�L��Q��N�Q�F�`�4�z�R�V�@��[�P� �4�����R�R��c�\�"�Z��0��y��h�x��%�j����m8��n��.�d��e��z��B�<�8���0�X��d�sr��~�0�b�z���q�N�����h��%Xb��|�E��~� ((������� [��@�9�j�q	�	����[��B��+��B��K���u�K���B�z��5��X��[� Z��������4�������	 i1 އ�~��~��~��~��~��~��~��~� r[10]��~��~��~��~��~��~��~��~��~����~� }���p�q�	 �1 އ�~��~��~��~��~��~��~��~� �[10]��~��~��~��~��~��~��~��~��~����~� �(A)��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~�	 �1 އ�~��~��~��~��~��~��~��~� �[10]��~��~��~��~��~��~��~��~��~����~�	 �1 އ�~��~��~��~��~��~��~��~� �[10]��~��~��~��~��~��~��~��~��~����~�!"��	%�1F�6�
!:0 އ�~��~��~��~��~��~��~��~��~�4!NA)��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~�!�!����"�!�(27)��~��~����~�!�  �!�    ���!�      �	!���	��.��1��.��1�n�!�%� ����~����!�&�!�%� �������~������	!�'	�OG�G�
�`�!�(21)��~��~��~��~��~�!�&B�3��We�?�T2��p�<�}�"� ݁"#�e��Z��q��b�O�"'"�8�	"+"��w�\��K���V��E�"92��E�"<!?��~�"?"Č��7�
74/%�H�/_�"S%��L�"��
"\(21)��~��~��~��~��~��~��~��~����~�"f!�x�~:�S��S		
d[	��"�A.��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~��~����!������~�縀�������~��������~�~�Ȁ�����~������~����~�ˀ����~��~�����؀���~���~��Ȁ��������~�Lj����~������~����~�������~�����������~����~����������~������������~��؀������~�È���~������~���������~�����Ѐ�����~���~���~��~���~�����|������~��~ɿ�~ɿ�~��~ɿ�~��~��~��~��Ѐ�������~�������~��������~�~�Ȁ�����~�������~ק�~��~��������~���з�~���������#W(21)��~��~��~��~��~�#]�p ݃����������ϗ�~�����ԧ�~�~���~���~��~���~��~��~���~���~���~��~��������~�������~���~�������~�������~�#x2��#yS� ݕ����~������~�����������~�������~�����~�����~���#�2��0#�N� ݍ��~��~��~�Ȁ��~���~���~���ɿ�~��~����~������~ɿ�~ɿ�~��~ɿ�~��~��~��~��~���������~���������~���~���~��~���~��~��~���~���~��~���~���~������������~����ɿ�~��~ɿ�~���~�#�(27)��~��~����~�#�'��w�f�$
�� ݃���~������~���������~�����~��~��~��~��~��~��~��~�$[10]��~��~��~��~��~��~��~��~��~����~�
$%N ވ�~�������~������~�Ѐ���~��Ο�~�
$/[SAN]��~�������~������~�Ѐ���~��������
$9N ވ�~�������~������~�Ѐ���~��Ο�~�
$C[SAN]��~�������~������~�Ѐ���~��������$Me� ����~����~ø������~�����$T2��$[e� ����~����~ø������~������$f!�����x�O�-��@��A��~��k��*	��**:#X6	6$
 (j26A�"+n%X
6;�
:9�p�f�x-j
 8�68�E�"62r�J6=�
$;�B�/nH�"!X,j66C�6��T6�,n26:.j6266G�"22I�&22+j
&>�2>FH�"2
&�
�<*2=�26P
r�Z�P2)\	 6v���R6�3�66X$6P
?�"62JH62,ja�la�^�v�.2�$6
F $
H(.:222;�"2( j
6/r2;�2,j(jG�",n&X
6G�&b�^�y�*7�6	,^$6D�"G�",j H&X)j,j22F�&02-nw�D�"[�Z26A�6E�"F�-jk�+n
6H�&6*j6d�Z$b�^20n22A�22)n6622:@�"6'je���3�O��p�^�c�E�!^ 0 %   0	0"d 2��!�!�"j"+"�"�"'&`%�%�
  &i	:� �=211)1e	!p	!`���ñ/%%!%&%)%-%1%5%9%=%@%C3���7Q3�	3�3�3�3�	3�3�3�3�3�3�3�3�2`$�$`!S![2$�$t� �R0AU0�6�����,�/�8�p�w������������������������������,�4�D�l�s�{����������������0�7�@�E�L�\�_�h�x�{������������,�<�A�H�\�d���������������D�S�|������������������������#�4�7�@�P�T�x������������h�t��������������������������T�[�^�d�p�������������(�P�`���������$�'�0�@�C�K�\�_���������������������(�/�8�T�p�|���������������������$�4�7�\�l	�������<�X�h�t��������������������������8�H�M�S�d�g�p�����������4�;�D�O���������$�0�@�C�L������������������������H�X����������	���D�T�Y�`�p�s�{����������������@�P��
�h��������������������������#�&�,�/�8�H�K�T�d�g����������������(�1�P�l�|ˆ¤¬´������������$�(�h�x�|���������<�d�t����(�8�D�H�L�S�W�]�`�p�s�|ŌŗŴŸŻ��������������$�,�3�@�P�S�\�xƔƤưƸ������������� �0�<�L�Q�t�|ǃLJǐǖǠǣǬǼǿ�������,�H�L�pȀȅȋȼ��������P�`Ɉɘ�������������������L�\�_ʼ����� �H�X�x���!�'�8�;�D�T�W̴̨̘̫����	�l͔ͤ	�� �0�X�_�h�t΄ΐΠΣά����������,�/�T�d�p	�����4�D�l�|Фд��������������0�@�C�L�\ѠѰ�,�<ҀҐҸҿ�����������(�8�;�D�|ӌӏӘӨ��������x�Ԉ��<�L�X�h�tՄՇ����������8�H�T�p֌
�����(�4�P�V�`�|׈טg�M�Q��~"gyG��>_�FdxF}
e�h��
v}e\�#�P>kփ$���_z�|��X^|aias6�laX�.���hx�fC�q�00	0���[] 	00
0((00  �< }")0!
00(00/	'�$$�>"�"�"p"v"�$`V09	'v$"%"f"�"n-$�!�!�R!ā$��!�k2�0!������!�%�2�:�=	�A�N�U�Y�]�r�u�{���������������������������������������	%%����!�*�.�6�9�=�%O�J�Q�U�Y�d�n�q�w�
%v������
�������������ɡ'%�����������������*�2�5�;�G�Q�W�b�f�j�m�q�~��.����������
����������	���	���.�5�&��D�J
�Q�^�f�w�z�������������������'F����
�������������
��	��)�F�'��O�Z�^�~��������������������������!���
���&�)�-�(V�:�B�E�I�R�V�Y�]�a�z�}�(���������
��"��������������������	�
��)��!�5�=�Y�]�a	�j�v�}�����������)z�������������������������	�6��Y�`�l�r�u�y�	*������
�����������ɡ*U�ѱ	*\��������������!�*��,�5�R�U�Y�	*��f�m���������������+��������������������������+�2�5�9�F�N�Q�U=�b�������������ű+���
����"������&�-�5�I�e2�i��������!����������-���!��*�-�1�<�E�I�M	�V�a�e�i�v�~����������������
��!����������
�
���&�)�-�:�A�E�H�T�^�a�e�r�y�}�������������������������������!�>�A�E�M�R�Z�]�a�n�v�y�}������������������������������	!��:�=�C�O�V�Y�]�j�r�u	�y����������������!����������!�,�9�?�J�Q�U�Y�0n�d�m������������������������#�������0����� �*�.�2�5�9�F�J�N�Q%�^�����������������������������������1��
���%�-�A�J�M�Q�Z�b�e�i�������2'���2+��
����������������������2�����#� �F�I�O�\�b�e�k�v�~�����2���
��&���������������
�
�
3c!��B�E�I�R=�V�������������ݱ
4Y���=�R�Y�]�a%�j�������#4������������������������������	���!�(�2�:�=�A�N�V�Y�]�j�q�u�y�������5t����������������������������5�����������!�5�I�R�U�Z�a�f�n�q�u‚Š‘™ž¦©®!º��������6{���
�����&�*!�F�j�m�
6��s�z�~ÅÉÍ"Ý����ݱ7?���������	��%�-�1�5	�>�I�f�i�m�v�zāĕĝĹĽ)Ŀ������	86�	��
��
��*�-�1�58j�>�F�O�Z�b�e�i�v�~ŁŅňŒřŝš	ŪŶſ�������������	�
���&�)�1�<�B�E�I�V�^
�a�m�r�z�}ƁƎƖƙƝƪƲƵƻ�9d����������������������	�
�&9���"�%�)�8�>�A�E�Y�]�a�i�l�v�y�NjǒǛǧǮDZǵ����������������������!�%�	:\�2�9�=�A�J�N�U�r�u�<:��{ȈȎȕȢȩȾ�������������������-�5�R�U�Y�d�m�q�u
�}Ɋɍɑ!ɞ�����������������������*	�B�N�Q�U�b�i�~ʅ"ʙʾ��ű<|����
������	����"�B�J�M�Q�Z�^�e�l!�z˝˹����=v!�������#�*�1�?�F�I�M�Z�a�i�q �v̶̡̝̮̹̽̚�
>"��������������
�
��>h��%�)�-!�:�]�a�e�r�y
͉͖͙ͪ͝ͱ������������	?(���	�
���"�%�)�?S!�6�Z�]�b�n�v�y�}ΊΒΕΙΦή!������������	���!�%�2�9�V�Y�]�@F�j�r�u�yρφύϢϩϱ��������@��������.�6�9�=�A�
A�J�Q�U�Y
�a�n�q�u�~!ЂЦЩЭ�Abк�����������������!��2�5�;�F�N�Q�U�
A��b�i�m�}тхщѢѥѩ�B*Ѷѽ����
��
��.�1�5�B��B�I�]�e҂҅҉ҒҖҝҡҥҪҭҲҺҽ�3C����������	����������"�&�*�-�1�>5�F�~ӁӅӒӚӝӡӮӵӹӽ������������������	!��A�E�]�a�e�p�z�}ԃ�D~Ԏԕ!Ԫ������������������ D����	�

��#�>�A�E�R�Z�]�a�f�n�v�y�}ՊՑ!զ������������������	�	E����!�%	�.�:�=�A�F�E��N�R�V�Y	�]�j�m�r
�uֆ֎ֱּ֑֢֭֕֩���������ձ
FT������������������!�F��.�6�9�=�E�J�R�Z�
F��f�j�m�q�u�~ׂ׊׍ב�F�מ I5] PK
!<Lv�M>>)chrome/pdfjs/content/web/cmaps/B5-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE��@��> �P` ^�Pa�s�@c�/!�"T�"�x�w�yA>�"]�?A>�"�\�w�>�S"]�A>�p"]�/A>�
"]�LA>�*"]�iA>�G"]�A>�d"]�#A>�"]�@A>�"]�]A>�;"\�z�A>�W"]�A>�t"]�3A>�".�P.�A>�/"]�nA>�L"]�A>�i"]�(A>�"]�EA>�#"]�bA>�@"]�A>�]"]�A>�z"]�9A>�"]�VA>�4"]�sA>�Q"]�A>�n"&�-6�UA>�"]�KA>�)"]�hA�F�T+�X"]�A>�b"]�!A>�"]�>A>�"	�[S�fA>�:")�y�e2�#A �V�x"��/�.C�0A�t�w'�
"]�2A>�"]�OA>�-�A	�l�t �v�"�+�-�H�wA>�	"U�H�v�A>�%"]�dA>�B"]�A>�_"]�A>�|"]�;A>�"]�XA>�6"]�uA>�S"]�A>�p"]�/A>�
"]�LA>�*"]�iA>�G"]�A>�d"*�#�T1�OA9��9�;"]�?A>�"]�\A>�:"]�yA>�W"=��N�UA>�t"�3W�:A>�"]�QA>�/"Z�n��IA>�K"]�
A>�h"]�'A>�"]�DA>�""]�aA>�?"]�~A>�\"]�A>�y"]�8A>�"]�UA>�3"]�rA>�P"]�A>�m"�,[�/A5��B"]�KA>�)"]�hA�F#�b"O��.�VA>�c"<�"�A�_A>�"�>T�HA>�"I�\��&A>�9"]�xA�V�G'�l")��a2�>A"�q��`�"]�0A'��o�6"!�L;�oA4�+	�a"]�kA%�I�p"�	�nF��eA>�g"]�&A"��d�'"]�BA>� "]�_A>�="]�|A6�Z�""���=�I
�>�JPK
!<J#nŎ�)chrome/pdfjs/content/web/cmaps/B5-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�B5-Ha�K�N�/�1���
�����h�OPK
!<%��KK+chrome/pdfjs/content/web/cmaps/B5pc-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE��@��>� ` ^=a�t�@c�/!�"T�"�x�w�yA>�"]�?A>�"�\�w �2_>�S"]�A>�p"]�/A>�
"]�LA>�*"]�iA>�G"]�A>�d"]�#A>�"]�@A>�"]�]A>�;"\�z�A>�W"]�A>�t"]�3A>�".�P.�A>�/"]�nA>�L"]�A>�i"]�(A>�"]�EA>�#"]�bA>�@"]�A>�]"]�A>�z"]�9A>�"]�VA>�4"]�sA>�Q"]�A>�n"&�-6�UA>�"]�KA>�)"]�hA�F�T+�X"]�A>�b"]�!A>�"]�>A>�"	�[S�fA>�:")�y�e2�#A �V�x"��/�.C�0A�t�w'�
"]�2A>�"]�OA>�-�A	�l�t �v�"�+�-�H�wA>�	"U�H�v�A>�%"]�dA>�B"]�A>�_"]�A>�|"]�;A>�"]�XA>�6"]�uA>�S"]�A>�p"]�/A>�
"]�LA>�*"]�iA>�G"]�A>�d"*�#�T1�OA9��9�;"]�?A>�"]�\A>�:"]�yA>�W"=��N�UA>�t"�3W�:A>�"]�QA>�/"Z�n��IA>�K"]�
A>�h"]�'A>�"]�DA>�""]�aA>�?"]�~A>�\"]�A>�y"]�8A>�"]�UA>�3"]�rA>�P"]�A>�m"�,[�/A5��B"]�KA>�)"]�hA�F#�b"O��.�VA>�c"<�"�A�_A>�"�>T�HA>�"I�\��&A>�9"]�xA�V�G'�l")��a2�>A"�q��`�"]�0A'��o�6"!�L;�oA4�+	�a"]�kA%�I�p"�	�nF��eA>�g"]�&A"��d�'"]�BA>� "]�_A>�="]�|A6�Z�""���=�I
�>�J`�`PK
!<9k,���+chrome/pdfjs/content/web/cmaps/B5pc-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�B5pc-Ha�K�N�/�1���
�����h�OPK
!<��L��.chrome/pdfjs/content/web/cmaps/CNS-EUC-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�������]��"��]��"��]����] �P` ^�Pc����]c�"]�A�"-��R]�M�"K�+�w�0�zs2�����S��T��Z��[��_�n�`�b� �c�o�!�d�"�p�#�f�y�u�z�$�|�%��v��&���'��*�w�P�U�,�V�-�Z�
�a��e�
�j�Vs�����W�^�b�.�c�2�k�v��z�~�������g��0�� ��1�
�&�us
�����3�x�4�y�a�;�`��{��	�2�
c�
��¡ �2�_]�S�"]�1�"]��"]�m�"]�K�"]�)�"]��"]�e�"]�C�"]�!�"]��"]�]�"]�;�"]��"]�w�"]�U�"]�3�"]��"]�o�"]�M�"]�+�"]�	�"]�g�"]�E�"]�#�"]��"]�_�"]�=�"]��"]�y�"]�W�"]�5�"]��"]�q�"]�O�"]�-�"]��"]�i�"]�G�"]�%�"]��"]�a�"]�?�"]��"]�{�"]�Y�"]�7�"]��"]�s�"]�Q�"]�/�"]�
�"]�k�"]�I�"]�'�"]��"]�c�"*�A��U]�l�"]�J�"]�(�"]��"]�d�"]�B�"]� �"]�~�"]�\�"]�:�"]��"]�v�"]�T�"]�2�"]��"]�n�"]�L�"]�*�"]��"]�f�"]�D�"]�"�"]��"]�^�"]�<�"]��"]�x�"]�V�"]�4�"]��"]�p�"]�N�"]�,�"]�
�"]�h�"]�F�"]�$�"]��"]�`�"]�>�"]��"]�z�"]�X�"]�6�"]��"]�r�"]�P�"]�.�"]��"]�j�"]�H�"]�&�"]��"]�b�"]�@�"]��"]�|�"]�Z�"]�8�"]��"]�t�"]�R�"]�0�"]��"]�l�"]�J�"]�(�"]��"]�d�"]�B�"]� �"]�~�"]�\�"]�:�"]��"]�v�"]�T�"]�2�"]��"]�n�"]�L�"#�*a��]c�"]�A�"-��R]�M�"K�+�w�0�zq2���S��T��Z��[��_�n�`�b� �c�o�!�d�"�p�#�f�y�u�z�$�|�%��v��&���'��*�w�P�U�,�V�-�Z�
�a��e�
�j�Vq���W�^�b�.�c�2�k�v��z�~�������g��0�� ��1�
�&�uq
���3�x�4�y�a�;�`��{��	�2�
a;¡ �2�_]�S�"]�1�"]��"]�m�"]�K�"]�)�"]��"]�e�"]�C�"]�!�"]��"]�]�"]�;�"]��"]�w�"]�U�"]�3�"]��"]�o�"]�M�"]�+�"]�	�"]�g�"]�E�"]�#�"]��"]�_�"]�=�"]��"]�y�"]�W�"]�5�"]��"]�q�"]�O�"]�-�"]��"]�i�"]�G�"]�%�"]��"]�a�"]�?�"]��"]�{�"]�Y�"]�7�"]��"]�s�"]�Q�"]�/�"]�
�"]�k�"]�I�"]�'�"]��"]�c�"*�APK
!<�D��.chrome/pdfjs/content/web/cmaps/CNS-EUC-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�������]��"��]��"��]����] �P` ^�Ps����
c�N
o|~~�����
�
���������"�c����"�A�O9�e�"-��R]�M�"K�+�w�0�zs2�����S��T��Z��[��_�n�`�b� �c�o�!�d�"�p�#�f�y�u�z�$�|�%��v��&���'��*�w�P�U�,�V�-�Z�
�a��e�
�j�Vs�����W�^�b�.�c�2�k�v��z�~�������g��0�� ��1�
�&�us
�����3�x�4�y�a�;�`��{��	�2�
c�
��¡ �2�_]�S�"]�1�"]��"]�m�"]�K�"]�)�"]��"]�e�"]�C�"]�!�"]��"]�]�"]�;�"]��"]�w�"]�U�"]�3�"]��"]�o�"]�M�"]�+�"]�	�"]�g�"]�E�"]�#�"]��"]�_�"]�=�"]��"]�y�"]�W�"]�5�"]��"]�q�"]�O�"]�-�"]��"]�i�"]�G�"]�%�"]��"]�a�"]�?�"]��"]�{�"]�Y�"]�7�"]��"]�s�"]�Q�"]�/�"]�
�"]�k�"]�I�"]�'�"]��"]�c�"*�A��U]�l�"]�J�"]�(�"]��"]�d�"]�B�"]� �"]�~�"]�\�"]�:�"]��"]�v�"]�T�"]�2�"]��"]�n�"]�L�"]�*�"]��"]�f�"]�D�"]�"�"]��"]�^�"]�<�"]��"]�x�"]�V�"]�4�"]��"]�p�"]�N�"]�,�"]�
�"]�h�"]�F�"]�$�"]��"]�`�"]�>�"]��"]�z�"]�X�"]�6�"]��"]�r�"]�P�"]�.�"]��"]�j�"]�H�"]�&�"]��"]�b�"]�@�"]��"]�|�"]�Z�"]�8�"]��"]�t�"]�R�"]�0�"]��"]�l�"]�J�"]�(�"]��"]�d�"]�B�"]� �"]�~�"]�\�"]�:�"]��"]�v�"]�T�"]�2�"]��"]�n�"]�L�"#�*q��
c�N
o|~~�����
�
���������"�a��"�A�O9�e�"-��R]�M�"K�+�w�0�zq2���S��T��Z��[��_�n�`�b� �c�o�!�d�"�p�#�f�y�u�z�$�|�%��v��&���'��*�w�P�U�,�V�-�Z�
�a��e�
�j�Vq���W�^�b�.�c�2�k�v��z�~�������g��0�� ��1�
�&�uq
���3�x�4�y�a�;�`��{��	�2�
a;¡ �2�_]�S�"]�1�"]��"]�m�"]�K�"]�)�"]��"]�e�"]�C�"]�!�"]��"]�]�"]�;�"]��"]�w�"]�U�"]�3�"]��"]�o�"]�M�"]�+�"]�	�"]�g�"]�E�"]�#�"]��"]�_�"]�=�"]��"]�y�"]�W�"]�5�"]��"]�q�"]�O�"]�-�"]��"]�i�"]�G�"]�%�"]��"]�a�"]�?�"]��"]�{�"]�Y�"]�7�"]��"]�s�"]�Q�"]�/�"]�
�"]�k�"]�I�"]�'�"]��"]�c�"*�APK
!<����+chrome/pdfjs/content/web/cmaps/CNS1-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE!!��]a!!]c�"]�A�"-��R]�M�"K�+�w�0�zq2'!�S��T��Z��[��_�n�`�b� �c�o�!�d�"�p�#�f�y�u�z�$�|�%��v��&���'��*�w�P�U�,�V�-�Z�
�a��e�
�j�Vq(!�W�^�b�.�c�2�k�v��z�~�������g��0�� ��1�
�&�uq
)!�3�x�4�y�a�;�`��{��	�2�
a;B! �2�_]�S�"]�1�"]��"]�m�"]�K�"]�)�"]��"]�e�"]�C�"]�!�"]��"]�]�"]�;�"]��"]�w�"]�U�"]�3�"]��"]�o�"]�M�"]�+�"]�	�"]�g�"]�E�"]�#�"]��"]�_�"]�=�"]��"]�y�"]�W�"]�5�"]��"]�q�"]�O�"]�-�"]��"]�i�"]�G�"]�%�"]��"]�a�"]�?�"]��"]�{�"]�Y�"]�7�"]��"]�s�"]�Q�"]�/�"]�
�"]�k�"]�I�"]�'�"]��"]�c�"*�APK
!<�[P��+chrome/pdfjs/content/web/cmaps/CNS1-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�CNS1-Ha!,�N|~���
������h�OPK
!<(TY���+chrome/pdfjs/content/web/cmaps/CNS2-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE!!��]aR!!]�l�"]�J�"]�(�"]��"]�d�"]�B�"]� �"]�~�"]�\�"]�:�"]��"]�v�"]�T�"]�2�"]��"]�n�"]�L�"]�*�"]��"]�f�"]�D�"]�"�"]��"]�^�"]�<�"]��"]�x�"]�V�"]�4�"]��"]�p�"]�N�"]�,�"]�
�"]�h�"]�F�"]�$�"]��"]�`�"]�>�"]��"]�z�"]�X�"]�6�"]��"]�r�"]�P�"]�.�"]��"]�j�"]�H�"]�&�"]��"]�b�"]�@�"]��"]�|�"]�Z�"]�8�"]��"]�t�"]�R�"]�0�"]��"]�l�"]�J�"]�(�"]��"]�d�"]�B�"]� �"]�~�"]�\�"]�:�"]��"]�v�"]�T�"]�2�"]��"]�n�"]�L�"#�*PK
!<����]]+chrome/pdfjs/content/web/cmaps/CNS2-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�CNS2-HPK
!<2W�jJJ.chrome/pdfjs/content/web/cmaps/ETHK-B5-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE��@��> A�~�!�2A
�i�UA*�t�w���ss�,2�}n�pn��$�U�B��WA�o�h�y�M�+�P���m�xH�1�A�
�C��6�qST���
� �m	A�t�s�r�s�t�s
E��Y�X�w�v�q�p�o�n)
�E�D�C�B�SX�~���������� �!V;�y�Bxwc�8!�o78�x�y(26`J�<$�	�*	a����6���c
�b�3W�2�Y�^
�+��,"��%{�c�bZ��uA"Z2Q�f}�7�6i�/��2�o(Q	����J�>���?�]��e�dA���_��s�d��M�m�~�u���K���
�V�a�E�h��;�8�GA�x�%e�\�W\�t�AY���w�"�r�M�	��"�x� �m��o��8�^�8�<�@�':��E�(�M�T��15P}�jm�FA�h�$�'X�q��N���os�l�S�-�H�d�W��d�R�%A�v�P�8J�){�((�v�t�L���G�l�$��$�j�t�M��|�|��L��-�j�`�XVA�f�Eq�p�
�[q�8KAr��a	F	)'U

3	J	"
T

	
&#6-DH+��"+	
(>�A�*�`�j��&��0r�d�e�#A)�ҁ�MCHI?/.
iv&o�z 	-
	{+�_:�-�S�Z ?V�u���{�3A��Á�q 	FZ^=dc<"	/"uR$$	
H
	,
OE6
)F%
G'D
F

&
�|�\�a�14
|	V
Z xz
` ^a�Z����c!�"T�"�yA>�"]�?A>�"�\�w�>�S"]�A>�p"]�/A>�
"]�LA>�*"]�iA>�G"]�A>�d"]�#A>�"]�@A>�"]�]A>�;"\�zB>�W"]�A>�t"]�3A>�".�P.�A>�/"]�nA>�L"]�A>�i"]�(A>�"]�EA>�#"]�bA>�@"]�A>�]"]�A>�z"]�9A>�"]�VA>�4"]�sA>�Q"]�A>�n"&�-6�UA>�"]�KA>�)"]�hA�F+�X"]�A>�b"]�!A>�"]�>A>�"	�[S�fA>�:")�y2�#A �V�x"�C�0A�t'�
"]�2A>�"]�OA>�-"�z��h	�l �v�"�+-�H�wA>�	"U�H�A>�%"]�dA>�B"]�A>�_"]�A>�|"]�;A>�"]�XA>�6"]�uA>�S"]�A>�p"]�/A>�
"]�LA>�*"]�iA>�G"]�A>�d"*�#1�OA9��;"]�?A>�"]�\A>�:"]�yA>�W"=��UA>�t"�3W�:A>�"]�QA>�/"Z�n�IA>�K"]�
A>�h"]�'A>�"]�DA>�""]�aA>�?"]�~A>�\"]�A>�y"]�8A>�"]�UA>�3"]�rA>�P"]�A>�m"�,[�/A5��B"]�KA>�)"]�hA�F#�b"O��VA>�c"<�"�_A>�"�>T�HA>�"I�\�&A>�9"]�xA�V'�l")�2�>A"�q��"]�0A'��6"!�L;�oA4�+	�a"]�kA%�I�p"�	F��eA>�g"]�&A"��'"]�BA>� "]�_A>�="]�|A6�Z�""�
�>�Ja�����%W	������#��'��,��:��@'��G��O��R��X��a��o��t��~��[��
���o�+"�0�V�Y�_�d#�f�k�p�y�}�
���(�1�8A�@�Y�^�g	�l�w"�|��!�&�3�<D�V�l
�s	��
"��"�'�K�`A$�m��%�*�G5�PA���+�4"
�E�S�i�r��A'��E"�[�d!�{�� �"A�50�<�n"(�s2�A�Q�^�j�o"$��3$�FA�k�p�}�"2�*�]
�v�A>�"]�FA�$(�*�TH�_�c��N��U�c����/�#�%�+�4
�=�A�I�M�Q�SC�U�X�]�e�m�s�w�{$�~��	����!�$	�'�1�4�6R�FW�P�U�\�^�b�e�j!�m�A���1�4�:(�?�A�F�H�L�P�R�T�Z�]�_�u�w�zE������#
�*"�8�L�Q�U�`�h
�l�w�B���#�1�:�D�G$�K�O	�l�v�{����	r�/�{�3�:A>�Y"]�A>�v"2�5�(�hA����+��2"��J��T��g��o(��A��(	��1��<��W"��g��j����$����8��>A	��C��M��Q��S��c��m��t"��}��
����'	��=��G��JA��W��`��"����-&��0��X��fA��r����!��)"��/1��8��k��xa�:�@%����;��">��`��I(��a"��
�����4��7��<,��?"��l
��s
��v��	��������
��#��1�W��B��E��RD��\��b��e��i��n��t"��
��_��|B!��!��C"��_��d������P��XC��r��)��+��0��3��:��>"��@��D��F��R5��^��l���o��/�e��N/��Q��S��U"��Y
��[��f��n	��s��}������	������D���� ��'��+��2��9��>��A��G��I(��R��Y��\��_��b��q��s��x��z������
	��A	����&��2��8��>��F#��N��Q��T��[��b��k��t��z������������A��������'

��.��<��>*��B��E��G��I	��N��V��X��\	��a��d��hL��m��r��z.����������"��D��������#��'��)��0��C#��G	��M��O��S��X��Z��]	��`���h��k��r0��y	��}��������	��������R�����~��0��7��:�U��F����%��-	���*��F���R� ��]
��_PK
!<h(-��.chrome/pdfjs/content/web/cmaps/ETHK-B5-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�	ETHK-B5-Ha�]���
�����a�K�N�/�1��Oa���PK
!<ӡ0�ee.chrome/pdfjs/content/web/cmaps/ETen-B5-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE��@��> �P` ^�Pa�}�@c�/!�"T�"�x�w�yA>�"]�?A>�"�\�w�>�S"]�A>�p"]�/A>�
"]�LA>�*"]�iA>�G"]�A>�d"]�#A>�"]�@A>�"]�]A>�;"\�z�A>�W"]�A>�t"]�3A>�".�P.�A>�/"]�nA>�L"]�A>�i"]�(A>�"]�EA>�#"]�bA>�@"]�A>�]"]�A>�z"]�9A>�"]�VA>�4"]�sA>�Q"]�A>�n"&�-6�UA>�"]�KA>�)"]�hA�F�T+�X"]�A>�b"]�!A>�"]�>A>�"	�[S�fA>�:")�y�e2�#A �V�x"��/�.C�0A�t�w'�
"]�2A>�"]�OA>�-"�z��3��:A>�Y"]�A>�v"2�5l	�l�t �v�"�+�-�H�wA>�	"U�H�v�A>�%"]�dA>�B"]�A>�_"]�A>�|"]�;A>�"]�XA>�6"]�uA>�S"]�A>�p"]�/A>�
"]�LA>�*"]�iA>�G"]�A>�d"*�#�T1�OA9��9�;"]�?A>�"]�\A>�:"]�yA>�W"=��N�UA>�t"�3W�:A>�"]�QA>�/"Z�n��IA>�K"]�
A>�h"]�'A>�"]�DA>�""]�aA>�?"]�~A>�\"]�A>�y"]�8A>�"]�UA>�3"]�rA>�P"]�A>�m"�,[�/A5��B"]�KA>�)"]�hA�F#�b"O��.�VA>�c"<�"�A�_A>�"�>T�HA>�"I�\��&A>�9"]�xA�V�G'�l")��a2�>A"�q��`�"]�0A'��o�6"!�L;�oA4�+	�a"]�kA%�I�p"�	�nF��eA>�g"]�&A"��d�'"]�BA>� "]�_A>�="]�|A6�Z�""���=�I
�>�J(�hPK
!<7S-E��.chrome/pdfjs/content/web/cmaps/ETen-B5-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�	ETen-B5-Ha�]���
�����a�K�N�/�1��Oa���PK
!<�bt�ee0chrome/pdfjs/content/web/cmaps/ETenms-B5-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�	ETen-B5-H` ^PK
!<�T�ݬ�0chrome/pdfjs/content/web/cmaps/ETenms-B5-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�ETenms-B5-HA�K�N��C	��}��ha�]���
������"��
�?�PK
!<�wBB*chrome/pdfjs/content/web/cmaps/EUC-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE���?�A��] �g` ^�ga��?�F�A]�y�"
�W�e�m�t���1	���0�&R�J�-U��*�s��H �# �DQ ���7	w	WE$$aF��]�e�"]�C�"]�!�"]��"]�]�"]�;�"]��"]�w�"]�U�"]�3�"]��"]�o�"]�M�"]�+�"]�	�"]�g�"]�E�"]�#�"]��"]�_�"]�=�"]��"]�y�"]�W�"]�5�"]��"]�q�"]�O�"]�-�"]��"]�i�"2�G�M]�z�"]�X�"]�6�"]��"]�r�"]�P�"]�.�"]��"]�j�"]�H�"]�&�"]��"]�b�"]�@�"]��"]�|�"]�Z�"]�8�"]��"]�t�"]�R�"]�0�"]��"]�l�"]�J�"]�(�"]��"]�d�"]�B�"]� �"]�~�"]�\�"]�:�"]��"]�v�"]�T�"�2�\PK
!<��ͪ�*chrome/pdfjs/content/web/cmaps/EUC-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�EUC-Ha���O
�Q	�S�V�[A��m�?�2a���PK
!<b_��	�	*chrome/pdfjs/content/web/cmaps/Ext-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE!!��]a!!G�y�B�"
�W�	���0�&R�J�-U��*�s��H �# �D�/>�h��(�">�G��"P�&�'�w�q�z�y��a!i�6�:K�7�1��!	�8�H�Oa-_�q
0!�e�Q�g�"�x�R1��S�3�T�8�U�Aq
1!�C�V	�M�W�X�X�\�Y�`�['�e�\�q2!�!�]�I�^4�+�_�a�`�{�a�~q3!��b��a�!��(�c�D�d�J�e�R�1�U�f�Yq
4! �]�g�r��h�
� ��i�"a5!	�;�j�F�k=�[q6!��l��m�#�2�?�n�H�o�f�p�m�q�t�r�vq7!�w�s�{�t��u��v�%�w�)�x�,�
�2�y�;�z�H�{�P�|a8!�U�}�i�~<�vq9!	�3�
�>�	�J��U�]�o������a:!1���D��X�	�eq	;!�o��v�	�y�
�{�D�q
<!�M��e�
�)�v����q	=!	�+�	�6�4�A��x�
�~q
>!�	���
���(��>��H��Mq	?!�g�-�q�� ��'� �0q@!�E�!�G�""�g�#	��$��&�qA!�#�(�n�+�)�<�*�O�+�R�,�b�-�lqB!��.��/
�.�0�=�1
�D�2�P�3�W�5�^qC!�_�6�f�7�i�9
�m�;1�|�<�/�=�4�>�;q
D!�=�?	�Z�@�e�A�g�B�j�C
�l�5"�xqE!��D��E�"�F�)�G�1�H�:�I�C�J�F�K�M�_�R�M�_�P�i�6�n�N�sqF!�y�O��P��Q�.�S�5�U�:�V�@�X
�C�m�Oq
G!�W�Y�`�Z�p�[��\�	�]� �^�$q
H!�5�_�9�`�C�a"�E�b�i�c�w�d�
qI!��e
��g�;�#�h�%�i	�(�j�3�k�A�l,�Dq	J!!�q�m��n��o�+�p�Jq
K!�O�q�Q�r �X�s�z�3��t��/�(qL!+�-�u
�Z�v�f�w�l�x�o�5�y��z�	�{qM!��|��}�;�~�?�4
�E��Ta
N!)�i-��D�#�H�X�]�`�M4�z�''�0aO6�waNK�.��%���q	Q!�X��u�����	0�aR!]�6qS!��
�$��.��D�
�R��_aYx�'aT!"�r��w�6�"�P$�m��"�.N�0��"]��"�jY�n�">�H
�
�� �"�&�?0�Sa
TD��x�%��>�O��1��;���L��q
[!#���)��.�"�<��O�h�Q�	�Xa^9�m;�a\!]�b�"]�@�"��7#�N�s�"Q�|
�O�"�Z7�`��0�"	�8�CL�Ia^P��"��2�8� �5�4�!�"q	b!	��$;�!�n�^�Q�`�%�eac!(�t�&��'�(�T�2q	d!�R�(�k�\�x�)��*� ae!�0�+�I�,	�K�-7�V�"$��.7�4�"B�l�/�0�0�5�1�>�"�J�2�^�37�e�4	�q	i!�(�u
�*��6�5�D� #�bq	j!���
�6�
�70�#�8�Uq	k!�d�9)�v��!�:�*�;�:alM�*al!+�B�o	�	��",� �N�l�n�"�~��&�5�"C�\�!a	li�<
�=�Y�>�?�@�:�A�B�C�
�Dqp!�:�E�M�F
�_�G�k�H�q�1�I�aq!]�q
r!�v�J
��K��w�L$�$�)	�Ja|{�oaz6�Ias!/�T�M*��N�1�"]�'�"�G��"]�b�"M�@	��EPK
!<����	�	/chrome/pdfjs/content/web/cmaps/Ext-RKSJ-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE��@�<�?�@�< �g` ^�ga�@>�y�8"�B�"	���0R�JN>��\�s�i �#�D�S�.>�h��(>�G�C>�&�e�'�w�q�z�y��a���6�K�7R��!
�8�H�Oa�~�q
���e�Q�g�"�x�R1��S�3�T�8�U�Aq�@�C�V	�M�W�X�X�\�Y�`�[�eq
��
��\��]�I�^4�+�_�a�`�{�a�~a�@��b��a�!��(q���>�c�D�d�J�e�R�1�U�f$�Y�g�r��h�
� ��i�"a�@	�;�j�F�k�[q���z�l��m�#�2�?�n�H�o�f�p�m�q�t�r�vq�@�w�s�{�t��u��v�%�w�)�x�,�
�2q
���6�y�;�z�H�{�P�|�U�}�i�~<�vq	�@	�3�
�>�	�J��U�]�oq
���r�����6���D��X�	�eq	�@�o��v�	�y�
�{�%�q
��5�.��e�
�)�v����a�@	�+�	�6�(�Aq���j��x��~���
���(��>��H��Ma�@�g�-�q�� q����'� �0�!�G�""�g�#	��$��&�q�@�#�(�n�+�)�<�*�O�+�R�,q���b�-0�l�.��/
�.�0�=�1
�D�2�P�3�W�5�^q	�@�_�6�f�7�i�9
�m�;!�|q����<�/�=�4�>�;�?	�Z�@�e�A�g�B�j�C
�l�5"�xq�@��D��E�"�F�)�G�1�H�:�I�C�J�F�K�M�_�Rq���Z�M�_�P�i�6�n�N"�s�O��P��Q�.�S�5�U�:�V�@�X
�C�m�Oa�@�W�Y�`�Z�p�[�q����\�	�]� �^�$�_�9�`�C�a"�E�b�i�c�w�d�
q�@��e
��g�;�#�h�%�i	�(�j�3�k�A�l
�Dq	��@�R�m��n��o�+�p�Ja�@�O�q�Q�r �X�s�zq��	��3��t��/0�(�u
�Z�v�f�w�l�x�o�5�y��z�	�{q	�@��|��}�;�~�?�4�Ea���J>�T-��DD�H�X�]�`,4�z�''�0a�U�wa���?�.�F���q	�@�X��u�����	�a��|�q	�@��
�$��.��D�
�Ra���wa��
�S5�_��6C�P!�m��N�0�C>�!�KY�nC>�Ha���6���&��O��1�q
���
��
��'� ��?�0�Sa���a�@#��)�.�"�<
�C�O�h�Qg�XC>�@6��m�7#�N�sa�d��!���9�`�?�Fa�@>�|q	��;��O�7�`� ��5�0a�@	�8�!�C�"-�Iq	�(�w�$;�!�n�^�Q�`�%�ea�@(�t�&��'�(�T�2q	�6�3�(�k�\�x�)��*� a�@�0�+�I�,	�K�-�VC�o�.7�4C>�lq
��+�/�0�0�5�1�>�2�^�37�e�4	�q	�@�(�u
�*��6�5�D� �bq	�!�g��
�6�
�70�#�8�Ua�@�d�9)�v��!q��#�:�*�;3�:�*�o�<	��=	�a�@,� �>�Nq��_�?�l�@�n�A��B��C&�5a�@>�\q���D*�!�E�M�F
�_�G�k�H�q�1�I�a�@>�q
�*�W�J
��K��w�L$�$�)	�Ja��oa��Ia
�@/�T�M
���N�1�!>�'3�fG�C>�bl�!	��EPK
!<"D����/chrome/pdfjs/content/web/cmaps/Ext-RKSJ-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�
Ext-RKSJ-Ha�A�O�L�R�P�O�S�V�Y�T�W�V�[A���M�r�AS a����H��PK
!<U/����*chrome/pdfjs/content/web/cmaps/Ext-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�Ext-Ha!"�O�L�R�P�O�S�V�Y�T�W�V�[A!k�M�@�r�A�2a%u��I��PK
!<ܥ�8%%-chrome/pdfjs/content/web/cmaps/GB-EUC-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�����] �$` �$]�.aX��]`�21�>	�p�z�$]��"R�d�-U�7�*�
4�%�+ �Z �{�/�%�<�9K�b�1]�.�"��`]�,�"]�
�"]�h�"]�F�"]�$�"]��"]�`�"]�>�"]��"]�z�"]�X�"]�6�"]��"]�r�"]�P�"]�.�"]��"]�j�"]�H�"]�&�"]��"]�b�"]�@�"]��"]�|�"]�Z�"]�8�"]��"]�t�"]�R�"]�0�"]��"]�l�"]�J�"]�(�"]��"]�d�"]�B�"]� �"X�~�']�W�"]�5�"]��"]�q�"]�O�"]�-�"]��"]�i�"]�G�"]�%�"]��"]�a�"]�?�"]��"]�{�"]�Y�"]�7�"]��"]�s�"]�Q�"]�/�"]�
�"]�k�"]�I�"]�'�"]��"]�c�"]�A�"]��"]�}�"]�[�"]�9PK
!<fk1��-chrome/pdfjs/content/web/cmaps/GB-EUC-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�GB-EUC-Ha���?�>�V��W
�F>��"�B�D�=��@��C���X�T�U�PK
!<	��)chrome/pdfjs/content/web/cmaps/GB-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE!!��]aX!!]`�21�>	�p�z�$]��"R�d�-U�7�*�
4�%�+ �Z �{�/�%�<�9K�b�1]�.�"��`]�,�"]�
�"]�h�"]�F�"]�$�"]��"]�`�"]�>�"]��"]�z�"]�X�"]�6�"]��"]�r�"]�P�"]�.�"]��"]�j�"]�H�"]�&�"]��"]�b�"]�@�"]��"]�|�"]�Z�"]�8�"]��"]�t�"]�R�"]�0�"]��"]�l�"]�J�"]�(�"]��"]�d�"]�B�"]� �"X�~�']�W�"]�5�"]��"]�q�"]�O�"]�-�"]��"]�i�"]�G�"]�%�"]��"]�a�"]�?�"]��"]�{�"]�Y�"]�7�"]��"]�s�"]�Q�"]�/�"]�
�"]�k�"]�I�"]�'�"]��"]�c�"]�A�"]��"]�}�"]�[�"]�9PK
!<�7n���)chrome/pdfjs/content/web/cmaps/GB-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�GB-Ha!"�?�>�V��W
�F>��"�B�D�=��@��C���X�T�U�PK
!<M��d9d9.chrome/pdfjs/content/web/cmaps/GBK-EUC-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE��@��> �$` �$]�.a
�@8�X�Y���qe��r��]�
q�@��
�%�5�3�L�D��I�b�Kq��� �L��N�\�Z�!�n�e���M�'�
�>�(��0�.�4��K�l�8��9q�@�<�h�=�T�@�9�B�X�E�_�K�B�N��T�,�S
�Z��e��j�O�k��l�dq����m�V�r�P�N�t��v�v�|�1����R��W�U�Q��e
��%+�)�O�Uq	�@�]�Y�m�H�
�c���q&����
��+�%�J��(�O�1�O�6�:�4�I��5�u�7�>�R�M�_�)�`�f�a�'��b�2�d�#�p��s�u�uq
�@��E�^��F��o��P�$q���<�A�C�d�F�y�L��O�B�P�Z�Y�"K�ha�@	�4�m�>�G�G�l�Zq���p���I��p'���7�^�8�*�9�,	�;�X�@�E�o�H�!�N�y�S�r	�Xq�@�j�b�|�e�0�i�z�t�j�w�l�L�s�x�O�h�u�q�v�i�w�t�|�o��p�q4����v�s����t��5��[�r��n��7�"�u�&��+�
�.�k�9�9�:��?�}�u�B��C�{�~�L�J�[�L�j�	�N��O�!�S��=�o��pa�@�F�H�t�~�v�7�wq��9�/�*�i��z���%��,�
�.���8��s�q�@
�%�c�9�0�+�1�-�2��5�'�=�/�>�}�K�V	�N�B�Xq#��
�Z�2�e�_�r�y�v�Y�}�]�~�g	��m���&�)��R�;�(��'��+��.��I�"�8�$�	�<q�@�F�|�G�[�^�^�"�_�z�`�-�za
��c��?�cA�}���y/��|�5q���9�i�E�w�V�v�j�}�o�x�s�z�z�~�(	�{�������!��"
�q	�@�+�{	�/�Z�9�e�@�W!�Dq��
�f�e�q�/�#�!�r�T�e�s�
�7�y��&�h��Z�9��\��P�g�*�t�+�>�.��Ma�@2�R�	��7��4�q������
���*�
'�D��l�q�m��o��~�
q�@���
���3��<��i�(�U�-�
�.q���$�<�C�V�)�Y�w�]�'�_��c�
�d���r��v�D�w�2��~��s��`�q�@�+��x�.�v�1�=�E�C�x�Iq���c�u�i�u�j�n�v�0�w��z��{�|�~�E��!��� �	�&�|%�0a�@>�Vq����4�~�2�c�M��;�R�9�U�(�=�j�5�z�:�|�3�q�@�	�b��^��]��7��/�_��2�N��R��<�!�k	�"�2�,�?�3��<�>�6q���8�.�<�{�D�	�G�1�H�D�K��N�^�#�6
�[��^�i�`�l�8�o��xQ
�п~�[��0���[�Z�=�{q���}�a��#��$��s��0�a�@>�q
��5�\�Q���)�]�*�	�-�
�Z�7�
q	�@�T��d�X�p�[��6�q����[��`�+�a�2�@�3��?�_�B�^�D�j�E��M��U�_�\�,�T�_��c�@�b�eQ	���`����<�v�E�D�a���k�g�2�m�)�q�Lq�@�t�r�x���R�$����m��e��-�R�f��z��c���
�<�Q	�r�v�@�g�f�_�^�e�d�{a�{�q���Y�V�c��)�|�#�.�;�<�L�B�M��C
�V�T
�a�o�l�W�sa�@1��-�Cq���O��R�V�(�l�y��Z��'���&��N��)�'�g�,��5�<�;�\a�|�L�o�ua�V�D��j�ma�@�@$�V�{~�}A&�|�#�'�(q���8�k�_�N�N�S�r�X�s-�s�~�!�~�)q�@�0�u�5�
�8�o �=�h�^�
�_q���j�v�k�'�W�t�|��!
��*���l��>�)�L�,�:�0��2�C�@�w�A�t�J�]�Lq�@�X�h�[��\�r�|�5��x�	���
q-��������!����8�%��4��n�:�{��q�;��p�B�i�C��D�o�J��N� �P�u�X�t�]�}�a�y�c�~�f�z	�mq�@	�w�c��-�
�K��S��Y�$��3�(q���/�\�7��:��>�
�C�	��E��G��[�J�/$�P�@�u�m�z�A�}�J��g�B�a�@>�P�]�\	�.�[!�8q�@�Z�^�m�D�b�r�T�s���f�q����c��]�/�k�<�5�Z�&�^�c��t�=�x�A�y�K��q�@��$�	�H�`��w��:��1�#�A��)�8�-�v�/��2��7�J�8q-��	�9�>�C�4�E�W�H��O�a�l�V�j�Z�c��^�&�	�b��l�e�s�d�u�b�x�^�|�D
�~�6�)
�	�+��%����v��m�q!�@� ��%��P�'��/�Z�0�S�1�i�d�7�U�9�f�:�_�=�O�A�Y�C��E�l�k�F�X�n�Kq���M��R�m�S�z�\�V�h	�`��j��m�f�o�o�s�	�X�w�g<�y�#�6a�@
�?�z�M�%�mq���|�"��V��:� �n�"�U�$�(�@�W�C�|�V��_�X�a�:�f��nq�@�o�l�r�Y�t�'�v�_�z���]��<�
�Z��I��F	�q����$��4��6�f�7�C�I�L
�^�@	�l�Q�v�,�z�/�	a��]`�21�>	�p�z�$]��"R�d�-U�7�*�
4�%�+ �Z �{�/�%�<�9K�ba���a��	�)�>�3�r�*�� �"�#�Bq
�@��6�=!�&�5�H��I��L�?�*�Nq	���Q�R�k���l�"�m]�.q
�@�d�n�+�p��9�q�_�A��r�0�ta���a�F�@*�`
�\�]�%�ja�� �%��F)�L	�v����q�@� �a�)�[�=�b�?�T
�E�_	�Pq	���Z�l�\�]�`�c�c�d�ha�@�w�%��p�q	����4�B�8�N�=�1
�E�a�@#�P�F�t�.�
q
���
��	��2���+	��3�$�(�&q	�@�'�/�(�1	�)�,�3�N�7Q	�W��P�K�d���	��1q	�`�4�/�e�7�<�i�C�%�Sa���T�@�k��l]�,q
�@
�s�n�~�=���2��C+�a���-��D]�
q	�@�M�o$�N�n�s�t�x�	�~a��]�h�"]�Fa���p�
� �(�+,�(�i�e�za
����� ��&+�)
�U�c�k�n�oq
�@���H���d���f�w���k���'��1�la����9�k��<�c��A�j��J]�$q
�@��W���X�'	��a�f��k�g��l��n�h��ra�����f�\��+]�q
�@��.�"��@�]��B���E�[�8!��Fa�� ��h]�`q�@
��	�������w	�� �$�i�+��*��[��:a�����@�}��A]�>q�@��_� ���b�m��c�9��i�j��l�$�a��t�8�`��u�b��v�A��a��]��"]�za���G�M�A�d�!�3�oa���������3��C��S��l��o��q�@���$���h���c��"���$�f��*�g��3�A	��<q
����F�j��J���K�l��L�k��P�P���`]�Xq�@�w��a�

��i�i��w�n�:��y���|�e�m���	���e���#��a�� ��]�6A��6�u�|��H�$��NQ	�a��p�C�B�y�x�E�D�ya�j��XQ	�m�H��$��A��@��~��#�	�&a�v�����^���_�'��aQ
���#�����'��&��/�9�s�Pq	����e�s	��h�1�&�%� ��r]�q"�@��u��$��w�(��x�*�)��z���{�>��|�1��}�,���5���+�/���h�d���!������|�J�-���Tq���� �Q��!�3��(�/��)�.��+�0�[��,�X��1�B��4]�ra�@��9�9��<�6��=��9��@Q	�Q�E�b�K��f��C�d��dq�Z��G�_�8�2�t��J��1���K�5��O�;��Q�7�3��S�T�^��X�4��Y�=�	�<q����[�n�X��^�?�E��_�I��`�A��b�G�V��c���e���g�C�?��h�#��i�B��j�>��l]�Pq�@�&��m�N�F�K�D��y�'�L��}����G�I�H���B��
�n��
����J�\��Q	�v�;�(����L�K���
q�����:�m���R�Q	���U��&�	���'�T��+]�.q'�@�{��.�@��1�&�a���7�
�V�Y�X��8�.��:���?�z�M�W��A�S��I� ��J�_���K�n��L�$�p��O�:��P�Z��Q���Sa����U�b��p���q]�q�@��t�j���,	���b�q���r���r��$���&a����,�.
��A]�jq	�@
��L�`��Z�B��p�A��q�(��a€���
��Q
“��o�N���+�O�X���/a¡]�H�"]�&a �y��8�U7�R�G�ya���!��6�� ��W��Z��^��tq�@��y�#���E���I���������w�����*�%��,qĀ��0���1�-�H�(��@���D�E��E�J��F]�q�@��J�p��N�F��O�]�^��S�,��X�a��d�;�Y�K�I��gaŀ���a���o�����]�bq�@���p����� �x��"�"
��=�6��Kaƀ ��W]�@q	�@%��x�+���?��&�}��,�3��0aǀ ��3]�q�@��T�M��U�4��b�q��d���w�I��~�qȀ��
�F	���G������!��#�b��&]�|q
�@��)�M�H��8�`��U�E��V���\aɀ��c��5
��r�<��}]�Zq�@
���o���Q��� ���8������B��*�O��-�=��1�D�1��4�Kq
ʀ��5�;��6�P��=�4�&��A�K��C�U��J]�8q�@��P�>��S�t��T�0��U���X�Z��Z�J��b���g�	��h�?��r�t�C��qˀ���������Y���T���2��]�q�@�&�W�� �X�B�S��'�;��*�Z��6�V��9�7��<�y
��D�S��O�a��Rq
̀
��S���a�I��f�B��g�h��m]�ta͡]�Ra͐�Z�W�N�.
�2a�@>��p��/��?��F�&��N	��u��q
΀������\���l��$�&��&]�0q�@
��(�`��6�{��<�^��>�a��A�U��P�k��V�[��W�	��Y�Y��\q
π��^���_�)�V��f�]�_��n�o��w�X]�q�@��x�)���[���v���������?������
�����-aЀ ��.]�lq�@��O�q��d���g�X�,��n�'���v��aр���a�;��%]�Jq�@�>��'�]��)�=
��2�*��@�<�:��F�?��I���M�k��N���P�-��Rq
Ҁ	��[���e�2��h���k�6��m�8��n�:]�(q�@��v�<��z�d��}�9��~�=���>���?���R���}��
�;�-���	��$�
��%���&aӀ��(�_�K�
��-Q	Ӌ�w��p����
��!�� ������'qӔ��4�j��6�b�K��7�x��8�b��:�r]�q%�@��;�P��<�S��?�e��@�r��D���F�]��I�T��K�g��L���O�f��T�h��X�d��^�j��_�k��`�i�=��a�j��c�=��e�aԀ��g�v�g���i�7��kQ
Ԋ�,������w�v��t���?��n��L��}aԗ��p�p��u�n��w]�dq�@��x�m��l�b��{�~��~�y���/���6���w��Q�Z�=����
�f��$��K
��O��p��Uq�e���G���E���\������u������F���aՀ���4��QՄ�i��l��G��F��o��n��]��\��Q��P��Q�+qՐ��%�`���(�~�G��+���,�u��/]�Bq�@���0�
�S��1���4����5�_��7�	��8Q�R��}�0��8����~��g��f��!�� ��_�l�]��L���La�b��?�W��B���E���FQ
�o�����u�5��(��s�|�[��N��ka�y��L���Oqր��Q�
��T��
���V�c��[�:��`�x��d]� q�@��k���n��k��r���x�0��h��y�D��z�������	�U���J���@���S��q׀���I�����!�m�x��$�{���%���*���+X�~a�@��4�H��A�}*��Fq؀��q�;�����Y�$���:�e�p�d�0�a��]�WQ�@����K���8��3�� �����N���B��=�k�|�l��:��!�m�H�?9�+�*��6��+�q����Qa�]���2��Q���4��Q�l�3��J��u��t������O�t�i��;����%�Va�z����5�xqـ�T���s��%�)�7�(�	�G��*�.�1��/�f��1�[�D��2�]�5q
�@��3�*�1��4�=��9��\#��=���a�z��daڡ]��"]�qaڅ�n�x�Q��0�}���,�aڀ��k��p��x���
��*��A��G��K��Q��]���e��jQ	�O�|�a�j��`��M��L��a
q�X��u���z���~�#�������p��q܀���{�g�6�V���`�� ���!�
��'���*�]�Oq �@��.���4���:�������?���@�1��D���E���F�X���G���N�	�f��O�&��Y���Tq݀��X�;���Y�:�[��[�p��_� ��b�;��f���h�p�7��j�R��l�3]�-q�@�E�!��m�(��o�|�0��r�"	��w�F�������
���0����K�J�*��aހ���{��/�r��;]�q�@�~��<�	��=�;
��G�S��U�>�&���V�l��Y�>��Z�J��[�3��_���eA	�w�O�$����1��0����~��m�=q	߁��j�q��l�F��m�c�s��p]�iq�@������3
��"� ��-�G��/�<��7���8�Q��9q���>�;��E���J�-�+��K���L���M���O]�Gq
�@��X�! ��Z�U��{�
��~��$��	q�����X���z���y���4���s�x���w�H�v������E�{��#]�%q!�@��$�z��%�m
��&�}��1�y��2�~�M��3����8���:����=�]�%��@����A�z�&��C�U��K���Oq������P���V���W��1��Y����	��[��4����]�]�q"�@��a����c���f�V��i�
��l�S��m�z�N��o���q�U��t�
��w���|�*�������-���	�2���{q�	���/���,���(������/���<������5��!�
�&��"]�aq'�@�4�*��1���#�%��&�M��+�Z��,�r�>��-�O��1�C��2�W��3�<��4�)��7�!��9�	��;�?��E�D��F�#��:�E��I�=q���J�A��K�� �<�;�8��M�@��Q�M��R���U�K��Y�G�O��]]�?q)�@��^�0��d�F��e�L��g��R�0�I��i�Q�J��l�R��m�8��p�y��q�+��s�M�%��u�L���w�m��y�7��z�J�8���N���|�T��q����<���U���V��
�h���V���W�S���H��]�q�@�\���Y��!��U��%�}��(�~��+�|��.�Z��/�^��1�4	��3�^��=�`��>�"�T��GA怾A�y�cyR�+�@A�{��Iq���M� ��O�e��P�a��R�b��X�l��[�m� ��^�_�j]�{q�@��`�n��c�9�\��g�h�k�o��j�D�g��k�[��l���o�+��{�q��|�w��}�"���3��
q����*���6�s�o����x���p���u���A�]���]�Yq�@��$�z���'�w��(���*�$��+�{��.�#��2�y��7�f��8���=�|
��>���I�}��K�r��L�t��Rq��N��T�F��U���Y�v��Z�d�?��^��\]�7q
�@��m�u��y�}���?����@��Q�]�E��~��[�V����	�}���������vq�i��	�i���C����y�k���5�
��q��G�I�F���O���K���s��!�w�O�N�J�M��"�P��%�l��'�A��(�n�Q]�q�@�x��)�S��,�R�T���/�,��4�U��5�q���7�E$��9q����^���a���c���i�~�M��j���n�Y]�sq�@��w�2��x�R��{�y	��}�W���?�����
�=������%	���V���D����W���
���"a���'�P��,�C��0]�Qq�@��F�(��L�
��[���^���_�2��b���o�C��va���~�#	��]�/q�@�����$�^��5�]��:���<�_��>�
�}��?�@��E�e��J�g��L�f��Ma���RQ��I��2��5��>��t��5�	�|�S��������/��.��Ca�]�
q �@�G�A�
�/�\��h�?�4��k�I��n�H��r���t�K��u�}��{�~��}�L�C���9������Z���s�aq��M���N�v���K�Q���@������P�� �R��"�*��#�r�S��&]�kq�@�>�T�D��(�O�s��)���/�L��4���5�x��6�N��8�O��=�8��D�P��E�Q	��F�t��P���R�~��Sq���U����W�!��X�q��Y���[�"��\�K�6�5��c�-��g�S��h]�Iq"�@��j�#���k�g��l�$��d��n�8��r�%��t���v�C��x�.��~����&��
����5�'���(���u���)q��+�k�,���j�y���f���g���l�� �-	��#]�'q�@��-�m�B���?���A��i��B�W
��N�%��J��\����]A�~�Q
����4��]��\��9��8��=��<��i�8a�	��c�?��m�g��p]�q�@��x�W��}���~�����G�
������7���p��!����"�p��#���q���(�f��-�R����q��.�
��2�	��4���5�'��:�|��;]�cq�@�A���=���@�N��B����C���G�)��W�a��i�$�t�c�b��ja���o�u��{�L]�Aq�@���z	��� ���h��#�P��$���%�L��&�H��*�>��+�e��;�d��A�9a���B�J��V���Z���\]�q�@��`� ��e�!	��f�"��p�'�9�%��q�)��x�.�+
��z�*�-���(�9��q�����6�0���8���
�7���E��&�:��'�C]�}q�@��+�@��,�=�A��/�>��0�?�@��2�;��3�B��7�D��:�9��B�L��C�G�K��K�I��N�H��P�J���Qq����X�M��]�4��_�Q��`�P�R��b�O��eQ���26��(��#��"��i��h��o��n��+a��]�[q�@��l�T��n�X��t�U�M��v�[��~�Z���Y�.�/���F���&���\���,	���#���1a�� ��]�9q�@��=���?���B�\��D����J���K����Z�n
��[���f�C��k���l���mq����o���p�}��r�o��s�
�	��y�G������
q�@
���P������`������ ���(�6��)���+�v��-q����<���A���B���K���O���Pq�@��X���Z���]�L��b�D��f�`� ��l���o���p�l
��q�"���.����#��q
����
�	�%��
�$���&����'�"�)��q�@��#�(��,���4�!�*��8����9���R��{��U��t��Va����W�
��g�q��r�w��tq�@��u���y���}�Z��
�A���3���j���
�� �i��"���$a����*���-�s��3q�@��I�F�k��[�S���^���_���c���e���0��g�q��h���j���m���n�r��s���wq����x�7���3���"�m���4�����S���a�@�Y=�� ��NPK
!<��޴�.chrome/pdfjs/content/web/cmaps/GBK-EUC-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�	GBK-EUC-Ha���?�>�V��W
�F>��"�B�D�=��@��C���X�T�U�PK
!<�p��L�L,chrome/pdfjs/content/web/cmaps/GBK2K-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�0�0��	�@��> C
�0�6��R���:�x��]�@��r��-�
S
�9�0�&�]��a��l�I�Z�S
�9�0�o��-:�*�+��&�
�S
�9�0�;�)�V�C�9�
� �5�;�pS
�9�0�0�z�	���g�`�}�<�1S
�9�0�-� �u��/�6�(��a�6S
�9�0�n�#�U��X��K�p�D�m�;S
�9�0�@�M�$�{��I�$��S
�9�0�=���|�j���=�z�!S
�9�0�i�f�%�:��H�~��]�NS
�9�0�E�Z�h�M��M��<��;�@�S
�9�0�"�c�:��j����M�S
�9�0�\���k�b�r�7��&�%S
�9�0��C�9��c��>�|�}�S
�9�0�d��z��e�K�q�Z��$��
�}S
�9�0�}�9�S�8t�T�m�l�5S
�9�0�{�_�U��`��i�(��F��}�3�fS
�9�0�}��v��U��4��
���DS
�9�0�q����UF�B�8�S
�9�0�Z�"�F�k�w�5�d�;�V�QS
�9�0�q�C��j��{�q����g�n�XC�9�0�0C�9�1�F��`��C�9�0��W"�~���A�>�y�Y�.f��Tk�.�,�m�i�z�<G�n"�_�6���
�^�1�h�?�+E_�V�7�<�s;�$�S�L�%��|�y�_�)�

	�1	�t�=�|		�Y�~,�-Y� �\�K
�&�<�c	�j�~�L�y��aI�
�c�6x��I�\�`�%�F_��O�N	�/�@ �`�;�(�S�b	�Q���	�M�H-�!�'�L(�K[�i�
�)�N�#�b�.K��"���
��W�V�S�0

��5�$�o�z�I��e	���
�e����B�
�"E�l�s�R�?�
�>�S�tT�^�*�]N�
�e�0
�)�^
�'2�>�y�
�
�M�<�/�P�X�y��K�\�oL��<�{��Q�dj�]_�c�T0�'��-
�0�?�S�0
�C��.����z�d�_�7�'�L��}�C�D��S��m�Qy�d�%�x�4�k�`(�A�,
G�;�
�'�V�x�]�C5��!�:�|�m�%�$�e�>�Xe�L��j�qt�&�d!�x���$���I�b�
��D�n�m�7�b�J�#�(J�#�	�2���I�y�R�#
��g�6�@��!���N�[�
�m��$	�oA�U�_eA�S��;��i�n',X-�?S�A�~�Kc�6 E8ZA���4/TzbCvA���7|`4*B$@K��rH~,xb.A���z�D�l.R��4n�LsX$=j$�B�d(HA�L�/JQ
�п~�[��0���[�Z�=�{A���a}�c��C@�k��!�g�Q�b�O��K�H��C��_�|�i�.� ��g�1�f�n�BA���A����]�0�V�"Q	���`����<�v�E�D�A��g�k�DE�5�J�
�"�� ��H�1�A�=A�S��*A�T�	Q	�r�v�@�g�f�_�^�e�d�{A�$���Y���u�$���
	�I�7� �K�1��*�Z��"�_�@��<�A�1�4�	�TR�AW�N%�q�j�:�k�n'��#�F�.�k�M�l�W�7!�p�Q
�j	��^�H	�7��K�L�]�e�Z���x�l	�/P�k�8 �X�{��[�p	�(�/�C��5�%+"'*!"�C�W�|�		U�/m
�:�q	�
�
�A�/�V���i�&%� �'�&�o8�4�k�M
v�{�J�:���s�x�r
�e����H���m�C�]�9�.�S���a�4�6��7�T���D�%�6�k�z�+�Q�:
�O���z�	�5�b���
�J�+�lG�Y����z��*�� �!���.'*�>�C	�g�6"
�E�w�4��M�c�=�	XS T"��f
�9��L�[�\�7	�k� �=�<E�$�X�e��~��<�:
�#��
��n�E�8�o�f
�_�4��m�d9��6�<�s"�n�B�-�+�1��m� �}��P�7J�B�0�x�3*�>
	���w�4�l
��
�g�`
�B�
�k�<���;M�C�t�N�[�|
�b�7�&
���$�
�=A���L��A	�m�'��l
�C�`m�<�$�,|�RA�[�r��
`0-J�LTA	���nc62V�\�"(D�#A����A�のQQ	�W��P�K�d���	��1A;�b�/��y�	�h�1Y�cs�Z�_�`,�V�(�kJ��E�e
�#��v�K�+	�W
�|V�M?�r�<�N�-�q�8�+�P��!�~�B5�3
��
�x�|�}�<
�u�M�
*�!X�	�!�h�'�!�e��g� M�U�Xc�6�h�S�NA,�d�72���)�+%�0�/�8�f4�	�K
	�F;�4�
��}�6
�'�K
�z�J�	�V�b		�J�H�]�j�c�]A�~�S.�3$�n��L�@�R\�_r��Z�&�t�CB"�c�4Ar(�F>Q	�a��p�C�B�y�x�E�D�yQ	�m�H��$��A��@��~��#�	�&A�x��4����*Q
���#�����'��&��/�9�s�PA���s
�z�#�$
�rR�	�

�p�v��>�te�m�=�N��-�'�l�WA���&�&
�4
A
�D��w	"	�4 Q	�Q�E�b�K��f��C�d��dA/�]�_�0
�}�1�(�Y�^
�	�v�?��*�i�d��-3�
�y�n�u��^��X	�9�<	�-�a�n;H	�{�n�
�)�W��]A�@�&A�a��J
�@2Q	�v�;�(����L�K���
A���:��6�g�'7
T�PF�V�A�H�c�j�U�w�S�+�}
�j�d�O�8�.��`A���R
�&)�8�	g�H+�-!��n�B�S�~�oaA����&�7 "#>�D@��<*Q
“��o�N���+�O�X���/A� �y���87�x�L�-�-���?��<�f�Y�t��J�)�+�X��e�h�2�E�,�Q
�l�%!�^�R&�s��Y��j�E�M
�L��2�z�}�t
�i�c�1�T�Q�H�g�
�8�9(�c�.�;�L%'2!(�9�bH��(/��v�-�^�s�j
�n��
�c�"	��:�!��`�-� �1�<	?�}�2�e��f��J�I�

�)�e�r�6�(�!:�.�u�.�
��4�@
�1�0�!��[��i�&:��%�v�g�8��Z�/�I�"�b�2�%�%T�!�3��)�AÄ�G�G=	�)	�P�#	AŜ�o�$�W�`�4e�| �D�M�n�F	

�?�n�t�?�jc �w�!�V��EA��0&�)W��"A�@��TR��[l�
�<�C:$>j�`h&�j�H�s�0�ML��2�o�
'>�22Q	Ӌ�w��p����
��!�� ������'Aӛ�x�-�&�`�C1�)AӖ�j�n�/�L� �E�(�
�z��$�K�F	�u��Y�X[p�\�?A	ӗ��6� $Q
Ԋ�,������w�v��t���?��n��L��}A�F�b�6�AԜ�p�#���m�AԠ��w�/Q�Z�=����
�f��$��K
��O��p��UA�n�\�O_A�h�G�v�S�N�A�m��QՄ�i��l��G��F��o��n��]��\��Q��P��Q�+AՓ�`�3�3�.AՔ�/�  �o�^A՚��+� Q�R��}�0��8����~��g��f��!�� ��_�l�]��L���LA�e�W�T����iQ
�o�����u�5��(��s�|�[��N��kA+�|����
a�S�z�)���W�P�I�H�[�I
�&����j�[
�F��{�:�5��<���g�g��*�+�k�f�i�`Aׇ�A�Q��x7T
�{�,
Q�@����K���8��3�� �����N���B��=�k�|�l��:��!�m�H�?9�+�*��6��+�q����QA�c�2�/�����AQ�l�3��J��u��t������O�t�i��;����%�VA�|�3�I�<�j��@�z-��P�_� �(�[�Q�$��J
��Z�5�6A	�}�5�*K��u
�,Aٜ��1� Q	�O�|�a�j��`��M��L��a
A�'�f�#�
�k�V�c�>��=�7�,
�Q�L
��8��=��@�W��i�Ps�	��"�DY��U�r�!�X�%�>,�� �i�K
�=�.�+�1�N�:�]��/�_��$X�M��=�6�W�8��E�+�W�X�3�h�>�
�-��5��b	�*
!��
�x�Q�l�x�I�Y�n�w�$N�>{�g��W�V�#�	�>	��i�/�U�x�3�&�s�(��z�Ef�G�^� �/��Q�G��7	��_�`�
�m�k�2�.�-^��5��b�
�Lf�n
�y�O
�$�%�.�
��J��
�R��b	�s�N�G��3�c�(/�J�P�s�#)�`
�V
�!�t��Ow�4�C�<�S���Y�XA�ܐ��%	
b�"c

�1�N	�!
		&	#%�!*
&
	%.� "+
05
:C,;6
	�*

E0		-$�%�.
	�$ 	PI
yR	�"kD	a�

	�+ x	1�(8�?�,
'*�Q�PA
�]��&�f�t��b�f�l�!�4AS܏�� �H<
&�;(Ap� 0�q�"�r�
�
�E*!0



�."
�&	,�6 �H4(�A*
�LLQ�]�E��~��[�V����	�}���������vA@�l�i�MKg��(�x�o�v�1�.	�'�D��3�4�y�&�P�I�&&�W�
�^�Q�=��f	�Q� �0�>�3
�:�N�>�EQ�%�g�
���z��;�H�m� �A�O�R�!�{�HA
�x�y"�:H�&�s�1�W�ohgA�v��
�2=h�0
�i��	�>Q��I��2��5��>��t��5�	�|�S��������/��.��CA:�A�A�o�7�X�D��2�#���j��O�0�%�(�(���=�q�"��u�\�g�n�	�X	�'�+�|��5�#�^�9�^�g�&�=�P�+�~���	��?��(���}�F�%��7�A(�@�G�"�A�U	
�m�D�C�3	�r�u�`�2
A�V��t (�*
	�".D�FB 4Q
����4��]��\��9��8��=��<��i�8A1�?�N�,�!�)���
�7�p@�I�(�s��_�V��?�0�c�(�!�w��U���G�(Q�8�y�
�j	��_�@�#�~�Y�=�*�A2�t�a��$�8
Jq��(

	� $38A�F��}+F
�++L�br,�HF(,�%Q���26��(��#��"��i��h��o��n��+Ay�B�T���W�,Aja
�C�+�F�I�/�
�j�)�Y��	��4
�U�^�-�{��i�`�C�B�E�&	�)�%��6�	�g�j�i�8.56'&���0E45�r*�G�_�N�K�B�
�$�<�k�3�l>C��0�G�[�H�N���E��;�G�H
�A�v�#��s�e�� �gA����SA�W��~,�T\ �bj0�F>"�cj!8!:�OT�h|
!�i` ^c!�8�8���v	����v��(�x	��1�v	��;�v	��E�v	��O�v	��Y�v	��c�v	��m�v	��w�y��{��~�|���{���v	��
�v���x	���v	���v	���v	�� �v	��*�v	��4�v	��>�v	��H�v	��R�v	��\�v	��f�v	��p�v	��z�v	���v	���v	��a�@8�X��e���
c�x�0�0	��"�v	��,�v	��6�v	��@�v	��J�v	��T�v	��^�v	��h�v	��r�v	��|�v	���v	���v	���v	��$�v	��.�v	��8�v	��B�v	��L�v	��V�v	��`�v	��j�v	��t�v	��~�v	���v	���v	���v	��&�v	��0�v	��:�v	��D�v	��N�v	��X�v	��b�v	��l�v	��v�v	���v	��
�v	���v	���v	��(�v	��2�v	��<�v	��F�v	��P�v	��Z�v	��d�v	��n�v	��x�v	���v	���v	���v	�� �v	��*�v	��4�v	��>�v	��H�v	��R�v	��\�v	��f�v	��p�v	��z�v	���v	���v	���v	��"�v	��,�v	��6�v	��@�v	��J�v	��T�v	��^�v	��h�v	��r�v	��|�v	���v	���v	���v	��$�v	��.�v	��8�v	��B�v	��L�v	��V�v	��`�v	��j�v	��t�v	��~�v	���v	���v	���v	��&�v	��0�v	��:�v	��D�v	��N�v	��X�v	��b�v	��l�v	��v�v	���v	��
�v	���v	���v	��(�v	��2�v	��<�v	��F�v	��P�v	��Z�v	��d�v	��n�v	��x�v	���v	���v	���v	�� �v	��*�v	��4�v	��>�v	��H�v	��R�v	��\�v	��f�v	��p�v	��z�v	����v	���v	���v	��"�v	��,�v	��6�v	��@�v	��J�v	��T�v	��^�v	��h�v	��r�v	��|�v	���v	���v	���v	��$�v	��.�v	��8�v	��B�v	��L�v	��V�v	��`�v	��j�v	��t�v	��~�v	���v	���v	���v	��&�v	��0�v	��:�v	��D�v	��N�v	��X�v	��b�v	��l�v	��v�v	���v	��
�v	���v	���v	��(�v	��2�v	��<�v	��F�v	��P�v	��Z�v	��d�v	��n�v	��x�v	���v	���v	���v	�� �v	��*�v	��4�v	��>�v	��H�v	��R�v	��\�v	��f�v	��p�v	��z�v	���v	���v	���v	��"�v	��,�v	��6�v	��@�v	��J�v	��T�v	��^�v	��h�v	��r�v	��|�v	���v	���v	���v	��$�v	��.�v	��8�v	��B�v	��L�v	��V�v	��`�v	��j�v	��t�v	��~�v	���v	���v	���v	��&�v	��0�v	��:�v	��D�v	��N�v	��X�v	��b�v	��l�v	��v�v	���v	��
�v	���v	���v	��(�v	��2�v	��<�v	��F�v	��P�v	��Z�v	��d�v	��n�v	��x�v	���v	���v	���v	�� �v	��*�v	��4�v	��>�v	��H�v	��R�v	��\�v	��f�v	��p��v	��z�v	���v	���v	���v	��"�v	��,�v	��6�v	��@�v	��J�v	��T�v	��^�v	��h�v	��r�v	��|�v	���v	���v	���v	��$�v	��.�v	��8�v	��B�v	��L�v	��V�v	��`�v	��j�v	��t�v	��~�v	���v	���v	���v	��&�v	��0�v	��:�v	��D�v	��N�v	��X�v	��b�v	��l�v	��v�v	���v	��
�v	���v	���v	��(�v	��2�v	��<�v	��F�v	��P�v	��Z�v	��d�v	��n�v	��x�v	���v	���v	���v	�� �v	��*�v	��4�v	��>�v	��H�v	��R�v	��\�v	��f�v	��p�v	��z�v	���v	���v	���v	��"�v	��,�v	��6�v	��@�v	��J�v	��T�v	��^�v	��h�v	��r�v	��|�v	���v	���v	���v	��$�v	��.�v	��8�v	��B�v	��L�v	��V�v	��`�v	��j�v	��t�v	��~�v	���v	���v	���v	��&�v	��0�v	��:�v	��D�v	��N�v	��X�v	��b�v	��l�v	��v�v	���v	��
�v	���v	���v	��(�v	��2�v	��<�v	��F�v	��P�v	��Z�v	��d�v	��n�v	��x�v	���v	���v	���v	�� �v	��*�v	��4�v	��>�v	��H�v	��R�v	��\��v	��f�v	��p�v	��z�v	���v	���v	���v	��"�v	��,�v	��6�v	��@�v	��J�v	��T�v	��^�v	��h�v	��r�v	��|�v	���v	���v	���v	��$�v	��.�v	��8�v	��B�v	��L�v	��V�v	��`�v	��j�v	��t�v	��~�v	���v	���v	���v	��&�v	��0�v	��:�v	��D�v	��N�v	��X�v	��b�v	��l�v	��v�v	���v	��
�v	���v	���v	��(�v	��2�v	��<�v	��F�v	��P�v	��Z�v	��d�v	��n�v	��x�v	���v	���v	���v	�� �v	��*�v	��4�v	��>�v	��H�v	��R�v	��\�v	��f�v	��p�v	��z�v	���v	���v	���v	��"�v	��,�v	��6�v	��@�v	��J�v	��T�v	��^�v	��h�v	��r�v	��|�v	���v	���v	���v	��$�v	��.�v	��8�v	��B�v	��L�v	��V�v	��`�v	��j�v	��t�v	��~�v	���v	���v	���v	��&�v	��0�v	��:�v	��D�v	��N�v	��X�v	��b�v	��l�v	��v�v	���v	��
�v	���v	���v	��(�v	��2�v	��<�v	��F�v	��P�v	��Z�v	��d�v	��n�v	��x�v	���v	���v	���v	�� �v	��*�v	��4�v	��>�v	��H��v	��R�v	��\�v	��f�v	��p�v	��z�v	���v	���v	���v	��"�v	��,�v	��6�v	��@�v	��J�v	��T�v	��^�v	��h�v	��r�v	��|�v	���v	���v	���v	��$�v	��.�v	��8�v	��B�v	��L�v	��V�v	��`�v	��j�v	��t�v	��~�v	���v	���v	���v	��&�v	��0�v	��:�v	��D�v	��N�v	��X�v	��b�v	��l�v	��v�v	���v	��
�v	���v	���v	��(�v	��2�v	��<�v	��F�v	��P�v	��Z�v	��d�v	��n�v	��x�v	���v	���v	���v	�� �v	��*�v	��4�v	��>�v	��H�v	��R�v	��\�v	��f�v	��p�v	��z�v	���v	���v	���v	��"�v	��,�v	��6�v	��@�v	��J�v	��T�v	��^�v	��h�v	��r�v	��|�v	���v	���v	���v	��$�v	��.�v	��8�v	��B�v	��L�v	��V�v	��`�v	��j�v	��t�v	��~�v	���v	���v	���v	��&�v	��0�v	��:�v	��D�v	��N�v	��X�v	��b�v	��l�v	��v�v	���v	��
�v	���v	���v	��(�v	��2�v	��<�v	��F�v	��P�v	��Z�v	��d�v	��n�v	��x�v	���v	���v	���v	�� �v	��*�v	��4��v	��>�v	��H�v	��R�v	��\�v	��f�v	��p�v��z�z���v	���v	���v	��#�v	��-�v	��7�v	��A�v	��K�v	��U�v	��_�v	��i�v	��s�v	��}�v	���v	���v	���v	��%�v	��/�v	��9�v	��C�v	��M�v	��W�v	��a�v	��k�v	��u�v	���v	��	�v	���v	���v	��'�v	��1�v	��;�v	��E�v	��O�v	��Y�v	��c�v	��m�v	��w�v	���v	���v	���v	���v	��)�v	��3�v	��=�v	��G�v	��Q�v	��[�v	��e�v	��o�v	��y�v	���v	��
�v	���v	��!�v	��+�v	��5�v	��?�v	��I�v	��S�v	��]�v	��g�v	��q�v	��{�v	���v	���v	���v	��#�v	��-�v	��7�v	��A�v	��K�v	��U�v	��_�v	��i�v	��s�v	��}�v	���v	���v	���v	��%�v	��/�v	��9�v	��C�v	��M�v	��W�v	��a�v	��k�v	��u�v	���v	��	�v	���v	���v	��'�v	��1�v	��;�v	��E�v	��O�v	��Y�v	��c�v	��m�v	��w�v	����v	���v	���v	���v	��)�v	��3�v	��=�v	��G�v	��Q�v	��[�v	��e�v	��o�v	��y�v	���v��
�y���v	���v	��(�v	��2�v	��<�v��FaX��]`�21�>	�p�z�$]��"R�d�-U�7�*�
4�%�+ �Z �{�/�%�<�9K�b�1]�.�"��`]�,�"]�
�"]�h�"]�F�"]�$�"]��"]�`�"]�>�"]��"]�z�"]�X�"]�6�"]��"]�r�"]�P�"]�.�"]��"]�j�"]�H�"]�&�"]��"]�b�"]�@�"]��"]�|�"]�Z�"]�8�"]��"]�t�"]�R�"]�0�"]��"]�l�"]�J�"]�(�"]��"]�d�"]�B�"]� �"X�~�']�W�"]�5�"]��"]�q�"]�O�"]�-�"]��"]�i�"]�G�"]�%�"]��"]�a�"]�?�"]��"]�{�"]�Y�"]�7�"]��"]�s�"]�Q�"]�/�"]�
�"]�k�"]�I�"]�'�"]��"]�c�"]�A�"]��"]�}�"]�[�"]�9a�N�ya���x�)a�v���a	�A�/�4����s�U��6�O�-�hI�A�8�La�N�;�'�a��	�)�>�3�r�*�� �#�Ba��@�
�%�3�D�I�L�N�Z!�n��(�0�4�9C�=�@�B�E�K�N�T
�Z�e	�m�r�t�v�|����
�+�)�UA�]�m�
���
��%�(�1�5�7�R�b�d�p�s�uA����$�<�C�F�L�P�YK�hA	�4�>�G�Z�p��'��9	�;�E�H�N�S	�XB�b�e�j�l�s�w�|������"�&�+
�.�:�?�C�J�L�O�S�pC�t7�w9�/�i�z���
���A
�%�2�5�>�K	�N�X
�Z�e�r�v�~	�������"�$	�<C�G�`�zc�a�'���cA�}�/��5�9�E�V�j�o�s	�{����
�A�+	�/�9�@!�D
�f�s�y���+�.�MA2�R����
��*'�D�m�o�~B��
��(
�.�<�V�Y�]�_
�d�r�w�~��A�+�=�C�I�c�j�w�{�~�� 	�&%�0A>�V��2�M�R�U�j�z�|�A�	������	�"�,�3�6�8�<�D�H�K�N
�[�i�l�o�x
�}����A>�5�\��*	�-�7B�T�d�p�����+�3�?�B�E�M�U�\�_�c�e	�k�m�qB�t�x�����
�	���#�;�M
�V
�a�l�sA1��C�O�R�l����'�,�5�;B�@$�V�{~�}a�
�@&�|�#�(�8�N�S�X-�s�!�)A�0�5�8 �=
�_�k�t�
����)�,�0�2�A�J�LA�X�\�|��	�
������%�4�;�D�J�N�P�X�]�a�c�f	�mA	�w��
��$�(�/�7�:�>�C�E�G�J$�P�u�z�}�A>�P�]	�.!�8A�Z�m�s�����/�<�Z�^�t�y�B��	���#�)�-�/�2	�9�C�E�H�O�V�Z�^	�b�l�s�u�x�|
�~
�	����C� �%�'�1�7�:�=�A�C�F�K�M�S�\	�`�j�m�o�s�w<�y�6A
�?�M�m�|��� �"�$�@�C�V�_�a�fC�o�r�t�v�z����	��$�4�7�I
�^	�l�v�z�	�A�!�&�I�L�N�Q�'�n�r0�t �%��F)�L	�v������ �)�=�?
�E	�P�Z�\�`�c�h��w���4�8�=
�E� #�P�t�
�
	�	��$�&	�)�3�7�<�C�T�l�
�s+��-�D�!$�N�s�x	�~��� ��&+�)
�U�c�k�o�����������1��9��<��A��J�!��X	��a��k��n��r����+���.��@��B!��F ��h�
��	����	�� ��*��:��A���_��c��i��l��v���������3��C��S��l��o��� ������"��$��*��3	��<��F��L��P�#��a
��i��w��y��|���� �����6��H��N	��X��_��a
��e	��h��r���u��x��}������������!��)��,��1��4���9��=��@	��G��K��O��Q��S��Y��[��`��c��e��j�"��m��y��}����
��
��
����	����'��+� ��.��1��8��:��?��A��L��Q��S��U��q���t��	������$��&��,
��A�
��L��Z��q��
��
���!��6�� ��W��Z��^��t���y��������������*��,��1��@��F���J��O��S��X��d��g�������!���� ��"
��=��K ��W�%��x����&��,��0 ��3�!��U��b��d��w��~��
	������#��&���)��8��V��\��c
��r��}�
��������������*��-��1��6��=��A��C��J���P��U��X��Z��b	��h��r�������������!�� ��'��*��6��9��<
��D��O
��S��a��g��m�>��p��/��?��F�&��N	��u��������$��&�
��(��6��<��>��A��P��W��Y��\��_��f��n�"��x������������
�� ��.���O��d��g��n������%� ��'��)
��2��@��F��I��N��P��R	��[��e��h��k��n� ��v��z��~������������&��(��-	��4��8�$��<��@��D��F��I��L��O��T��X��a��c��e��g��i��k
��p��u�!��x��{��~����������������������%��(��,�%��1��5��8��?��B��F
��L��O��Q��T��V��[��`��d���k��n��r��z����	��������������!��%��+���4��A*��F��q���C����������%��*��/�(��4��9#��=��a��d��k��p��x���
��*��A��G��K��Q��]���e��j	��u��z��~������������!��'��*� ��.��4��:��@��G��O��T��Y��[��_��b��f��h��j�$��m��o��r	��w
����������/�$	��=
��G��V��[��_��e
��j��m��p�����
��"��-��/��9��>��E��M��O���X ��Z��{
��~��	����
�����&
��&��3��8��:��=��A��C��K��P��W��Y��[��]� ��a��c��f��i��m��o��q��t��w��|����		���������)��#��&��-��4��7��9	��;��F	��K��M��R��U��Y�#��^��e��g��i��m��q��s��u��w��z��������
�������"����!��%��(��+��/��1	��3��>��G��M��P��R��X��[��^�!��`��c��g	��l��o��}����
������������� ��$��(��+��.��2��8
��>��I��L��R��U��Z��\���m��y������	������������"��%�&��)��,��/��5��7$��9��^��a��c��j��n�"��x��{	��}����	��������"��'��,��0���F��L��[��_��b��o��v��~	�������$��5��:��<��?��E��J��M��R�4��h��k��n��r��u��{��}���������������� ��#��&�%��)��/��6��8��=	��F��P��S��U��Y��\��c��h�$��l��n��r��t��v��x��~����
������������ 	��#���-��?��B
��N��]	��c��m��p���x��~����������#��(��.��2��5��;�!��=��@��C��G��W��j��o��{� ��	������&��+��;��B��V��Z��\���`	��f��q��x
��z����������'�"��,��0��3��7��:��C��K��N��Q��X��]��`��b��e�*��l��n��t��v������	�� �����=��?��B��D��K
��[��f��m��p��s	��y����� 
�������� ��)��+��-��<��B��K��P���X��Z��]��b��f��l
��q������
��
���������#��,��4��9��R��W
��g��r�!��u��y��}��
������ ��"��$��*��-��3���I��[��_��c��e��h��j��n��s��x���������$=�� ��NPK
!<@%���,chrome/pdfjs/content/web/cmaps/GBK2K-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�GBK2K-Ha���?�>�V��W
�F>��"�B�D�=��@A���C(	A��� A����W�2
�qPK
!<�o�^9^9/chrome/pdfjs/content/web/cmaps/GBKp-EUC-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE��@��> ` ^a
�@8�X�Y���qe��r��]�
q�@��
�%�5�3�L�D��I�b�Kq��� �L��N�\�Z�!�n�e���M�'�
�>�(��0�.�4��K�l�8��9q�@�<�h�=�T�@�9�B�X�E�_�K�B�N��T�,�S
�Z��e��j�O�k��l�dq����m�V�r�P�N�t��v�v�|�1����R��W�U�Q��e
��%+�)�O�Uq	�@�]�Y�m�H�
�c���q&����
��+�%�J��(�O�1�O�6�:�4�I��5�u�7�>�R�M�_�)�`�f�a�'��b�2�d�#�p��s�u�uq
�@��E�^��F��o��P�$q���<�A�C�d�F�y�L��O�B�P�Z�Y�"K�ha�@	�4�m�>�G�G�l�Zq���p���I��p'���7�^�8�*�9�,	�;�X�@�E�o�H�!�N�y�S�r	�Xq�@�j�b�|�e�0�i�z�t�j�w�l�L�s�x�O�h�u�q�v�i�w�t�|�o��p�q4����v�s����t��5��[�r��n��7�"�u�&��+�
�.�k�9�9�:��?�}�u�B��C�{�~�L�J�[�L�j�	�N��O�!�S��=�o��pa�@�F�H�t�~�v�7�wq��9�/�*�i��z���%��,�
�.���8��s�q�@
�%�c�9�0�+�1�-�2��5�'�=�/�>�}�K�V	�N�B�Xq#��
�Z�2�e�_�r�y�v�Y�}�]�~�g	��m���&�)��R�;�(��'��+��.��I�"�8�$�	�<q�@�F�|�G�[�^�^�"�_�z�`�-�za
��c��?�cA�}���y/��|�5q���9�i�E�w�V�v�j�}�o�x�s�z�z�~�(	�{�������!��"
�q	�@�+�{	�/�Z�9�e�@�W!�Dq��
�f�e�q�/�#�!�r�T�e�s�
�7�y��&�h��Z�9��\��P�g�*�t�+�>�.��Ma�@2�R�	��7��4�q������
���*�
'�D��l�q�m��o��~�
q�@���
���3��<��i�(�U�-�
�.q���$�<�C�V�)�Y�w�]�'�_��c�
�d���r��v�D�w�2��~��s��`�q�@�+��x�.�v�1�=�E�C�x�Iq���c�u�i�u�j�n�v�0�w��z��{�|�~�E��!��� �	�&�|%�0a�@>�Vq����4�~�2�c�M��;�R�9�U�(�=�j�5�z�:�|�3�q�@�	�b��^��]��7��/�_��2�N��R��<�!�k	�"�2�,�?�3��<�>�6q���8�.�<�{�D�	�G�1�H�D�K��N�^�#�6
�[��^�i�`�l�8�o��xQ
�п~�[��0���[�Z�=�{q���}�a��#��$��s��0�a�@>�q
��5�\�Q���)�]�*�	�-�
�Z�7�
q	�@�T��d�X�p�[��6�q����[��`�+�a�2�@�3��?�_�B�^�D�j�E��M��U�_�\�,�T�_��c�@�b�eQ	���`����<�v�E�D�a���k�g�2�m�)�q�Lq�@�t�r�x���R�$����m��e��-�R�f��z��c���
�<�Q	�r�v�@�g�f�_�^�e�d�{a�{�q���Y�V�c��)�|�#�.�;�<�L�B�M��C
�V�T
�a�o�l�W�sa�@1��-�Cq���O��R�V�(�l�y��Z��'���&��N��)�'�g�,��5�<�;�\a�|�L�o�ua�V�D��j�ma�@�@$�V�{~�}A&�|�#�'�(q���8�k�_�N�N�S�r�X�s-�s�~�!�~�)q�@�0�u�5�
�8�o �=�h�^�
�_q���j�v�k�'�W�t�|��!
��*���l��>�)�L�,�:�0��2�C�@�w�A�t�J�]�Lq�@�X�h�[��\�r�|�5��x�	���
q-��������!����8�%��4��n�:�{��q�;��p�B�i�C��D�o�J��N� �P�u�X�t�]�}�a�y�c�~�f�z	�mq�@	�w�c��-�
�K��S��Y�$��3�(q���/�\�7��:��>�
�C�	��E��G��[�J�/$�P�@�u�m�z�A�}�J��g�B�a�@>�P�]�\	�.�[!�8q�@�Z�^�m�D�b�r�T�s���f�q����c��]�/�k�<�5�Z�&�^�c��t�=�x�A�y�K��q�@��$�	�H�`��w��:��1�#�A��)�8�-�v�/��2��7�J�8q-��	�9�>�C�4�E�W�H��O�a�l�V�j�Z�c��^�&�	�b��l�e�s�d�u�b�x�^�|�D
�~�6�)
�	�+��%����v��m�q!�@� ��%��P�'��/�Z�0�S�1�i�d�7�U�9�f�:�_�=�O�A�Y�C��E�l�k�F�X�n�Kq���M��R�m�S�z�\�V�h	�`��j��m�f�o�o�s�	�X�w�g<�y�#�6a�@
�?�z�M�%�mq���|�"��V��:� �n�"�U�$�(�@�W�C�|�V��_�X�a�:�f��nq�@�o�l�r�Y�t�'�v�_�z���]��<�
�Z��I��F	�q����$��4��6�f�7�C�I�L
�^�@	�l�Q�v�,�z�/�	a��]`�21�>	�p�z�$]��"R�d�-U�7�*�
4�%�+ �Z �{�/�%�<�9K�ba���a��	�)�>�3�r�*�� �"�#�Bq
�@��6�=!�&�5�H��I��L�?�*�Nq	���Q�R�k���l�"�m]�.q
�@�d�n�+�p��9�q�_�A��r�0�ta���a�F�@*�`
�\�]�%�ja�� �%��F)�L	�v����q�@� �a�)�[�=�b�?�T
�E�_	�Pq	���Z�l�\�]�`�c�c�d�ha�@�w�%��p�q	����4�B�8�N�=�1
�E�a�@#�P�F�t�.�
q
���
��	��2���+	��3�$�(�&q	�@�'�/�(�1	�)�,�3�N�7Q	�W��P�K�d���	��1q	�`�4�/�e�7�<�i�C�%�Sa���T�@�k��l]�,q
�@
�s�n�~�=���2��C+�a���-��D]�
q	�@�M�o$�N�n�s�t�x�	�~a��]�h�"]�Fa���p�
� �(�+,�(�i�e�za
����� ��&+�)
�U�c�k�n�oq
�@���H���d���f�w���k���'��1�la����9�k��<�c��A�j��J]�$q
�@��W���X�'	��a�f��k�g��l��n�h��ra�����f�\��+]�q
�@��.�"��@�]��B���E�[�8!��Fa�� ��h]�`q�@
��	�������w	�� �$�i�+��*��[��:a�����@�}��A]�>q�@��_� ���b�m��c�9��i�j��l�$�a��t�8�`��u�b��v�A��a��]��"]�za���G�M�A�d�!�3�oa���������3��C��S��l��o��q�@���$���h���c��"���$�f��*�g��3�A	��<q
����F�j��J���K�l��L�k��P�P���`]�Xq�@�w��a�

��i�i��w�n�:��y���|�e�m���	���e���#��a�� ��]�6A��6�u�|��H�$��NQ	�a��p�C�B�y�x�E�D�ya�j��XQ	�m�H��$��A��@��~��#�	�&a�v�����^���_�'��aQ
���#�����'��&��/�9�s�Pq	����e�s	��h�1�&�%� ��r]�q"�@��u��$��w�(��x�*�)��z���{�>��|�1��}�,���5���+�/���h�d���!������|�J�-���Tq���� �Q��!�3��(�/��)�.��+�0�[��,�X��1�B��4]�ra�@��9�9��<�6��=��9��@Q	�Q�E�b�K��f��C�d��dq�Z��G�_�8�2�t��J��1���K�5��O�;��Q�7�3��S�T�^��X�4��Y�=�	�<q����[�n�X��^�?�E��_�I��`�A��b�G�V��c���e���g�C�?��h�#��i�B��j�>��l]�Pq�@�&��m�N�F�K�D��y�'�L��}����G�I�H���B��
�n��
����J�\��Q	�v�;�(����L�K���
q�����:�m���R�Q	���U��&�	���'�T��+]�.q'�@�{��.�@��1�&�a���7�
�V�Y�X��8�.��:���?�z�M�W��A�S��I� ��J�_���K�n��L�$�p��O�:��P�Z��Q���Sa����U�b��p���q]�q�@��t�j���,	���b�q���r���r��$���&a����,�.
��A]�jq	�@
��L�`��Z�B��p�A��q�(��a€���
��Q
“��o�N���+�O�X���/a¡]�H�"]�&a �y��8�U7�R�G�ya���!��6�� ��W��Z��^��tq�@��y�#���E���I���������w�����*�%��,qĀ��0���1�-�H�(��@���D�E��E�J��F]�q�@��J�p��N�F��O�]�^��S�,��X�a��d�;�Y�K�I��gaŀ���a���o�����]�bq�@���p����� �x��"�"
��=�6��Kaƀ ��W]�@q	�@%��x�+���?��&�}��,�3��0aǀ ��3]�q�@��T�M��U�4��b�q��d���w�I��~�qȀ��
�F	���G������!��#�b��&]�|q
�@��)�M�H��8�`��U�E��V���\aɀ��c��5
��r�<��}]�Zq�@
���o���Q��� ���8������B��*�O��-�=��1�D�1��4�Kq
ʀ��5�;��6�P��=�4�&��A�K��C�U��J]�8q�@��P�>��S�t��T�0��U���X�Z��Z�J��b���g�	��h�?��r�t�C��qˀ���������Y���T���2��]�q�@�&�W�� �X�B�S��'�;��*�Z��6�V��9�7��<�y
��D�S��O�a��Rq
̀
��S���a�I��f�B��g�h��m]�ta͡]�Ra͐�Z�W�N�.
�2a�@>��p��/��?��F�&��N	��u��q
΀������\���l��$�&��&]�0q�@
��(�`��6�{��<�^��>�a��A�U��P�k��V�[��W�	��Y�Y��\q
π��^���_�)�V��f�]�_��n�o��w�X]�q�@��x�)���[���v���������?������
�����-aЀ ��.]�lq�@��O�q��d���g�X�,��n�'���v��aр���a�;��%]�Jq�@�>��'�]��)�=
��2�*��@�<�:��F�?��I���M�k��N���P�-��Rq
Ҁ	��[���e�2��h���k�6��m�8��n�:]�(q�@��v�<��z�d��}�9��~�=���>���?���R���}��
�;�-���	��$�
��%���&aӀ��(�_�K�
��-Q	Ӌ�w��p����
��!�� ������'qӔ��4�j��6�b�K��7�x��8�b��:�r]�q%�@��;�P��<�S��?�e��@�r��D���F�]��I�T��K�g��L���O�f��T�h��X�d��^�j��_�k��`�i�=��a�j��c�=��e�aԀ��g�v�g���i�7��kQ
Ԋ�,������w�v��t���?��n��L��}aԗ��p�p��u�n��w]�dq�@��x�m��l�b��{�~��~�y���/���6���w��Q�Z�=����
�f��$��K
��O��p��Uq�e���G���E���\������u������F���aՀ���4��QՄ�i��l��G��F��o��n��]��\��Q��P��Q�+qՐ��%�`���(�~�G��+���,�u��/]�Bq�@���0�
�S��1���4����5�_��7�	��8Q�R��}�0��8����~��g��f��!�� ��_�l�]��L���La�b��?�W��B���E���FQ
�o�����u�5��(��s�|�[��N��ka�y��L���Oqր��Q�
��T��
���V�c��[�:��`�x��d]� q�@��k���n��k��r���x�0��h��y�D��z�������	�U���J���@���S��q׀���I�����!�m�x��$�{���%���*���+X�~a�@��4�H��A�}*��Fq؀��q�;�����Y�$���:�e�p�d�0�a��]�WQ�@����K���8��3�� �����N���B��=�k�|�l��:��!�m�H�?9�+�*��6��+�q����Qa�]���2��Q���4��Q�l�3��J��u��t������O�t�i��;����%�Va�z����5�xqـ�T���s��%�)�7�(�	�G��*�.�1��/�f��1�[�D��2�]�5q
�@��3�*�1��4�=��9��\#��=���a�z��daڡ]��"]�qaڅ�n�x�Q��0�}���,�aڀ��k��p��x���
��*��A��G��K��Q��]���e��jQ	�O�|�a�j��`��M��L��a
q�X��u���z���~�#�������p��q܀���{�g�6�V���`�� ���!�
��'���*�]�Oq �@��.���4���:�������?���@�1��D���E���F�X���G���N�	�f��O�&��Y���Tq݀��X�;���Y�:�[��[�p��_� ��b�;��f���h�p�7��j�R��l�3]�-q�@�E�!��m�(��o�|�0��r�"	��w�F�������
���0����K�J�*��aހ���{��/�r��;]�q�@�~��<�	��=�;
��G�S��U�>�&���V�l��Y�>��Z�J��[�3��_���eA	�w�O�$����1��0����~��m�=q	߁��j�q��l�F��m�c�s��p]�iq�@������3
��"� ��-�G��/�<��7���8�Q��9q���>�;��E���J�-�+��K���L���M���O]�Gq
�@��X�! ��Z�U��{�
��~��$��	q�����X���z���y���4���s�x���w�H�v������E�{��#]�%q!�@��$�z��%�m
��&�}��1�y��2�~�M��3����8���:����=�]�%��@����A�z�&��C�U��K���Oq������P���V���W��1��Y����	��[��4����]�]�q"�@��a����c���f�V��i�
��l�S��m�z�N��o���q�U��t�
��w���|�*�������-���	�2���{q�	���/���,���(������/���<������5��!�
�&��"]�aq'�@�4�*��1���#�%��&�M��+�Z��,�r�>��-�O��1�C��2�W��3�<��4�)��7�!��9�	��;�?��E�D��F�#��:�E��I�=q���J�A��K�� �<�;�8��M�@��Q�M��R���U�K��Y�G�O��]]�?q)�@��^�0��d�F��e�L��g��R�0�I��i�Q�J��l�R��m�8��p�y��q�+��s�M�%��u�L���w�m��y�7��z�J�8���N���|�T��q����<���U���V��
�h���V���W�S���H��]�q�@�\���Y��!��U��%�}��(�~��+�|��.�Z��/�^��1�4	��3�^��=�`��>�"�T��GA怾A�y�cyR�+�@A�{��Iq���M� ��O�e��P�a��R�b��X�l��[�m� ��^�_�j]�{q�@��`�n��c�9�\��g�h�k�o��j�D�g��k�[��l���o�+��{�q��|�w��}�"���3��
q����*���6�s�o����x���p���u���A�]���]�Yq�@��$�z���'�w��(���*�$��+�{��.�#��2�y��7�f��8���=�|
��>���I�}��K�r��L�t��Rq��N��T�F��U���Y�v��Z�d�?��^��\]�7q
�@��m�u��y�}���?����@��Q�]�E��~��[�V����	�}���������vq�i��	�i���C����y�k���5�
��q��G�I�F���O���K���s��!�w�O�N�J�M��"�P��%�l��'�A��(�n�Q]�q�@�x��)�S��,�R�T���/�,��4�U��5�q���7�E$��9q����^���a���c���i�~�M��j���n�Y]�sq�@��w�2��x�R��{�y	��}�W���?�����
�=������%	���V���D����W���
���"a���'�P��,�C��0]�Qq�@��F�(��L�
��[���^���_�2��b���o�C��va���~�#	��]�/q�@�����$�^��5�]��:���<�_��>�
�}��?�@��E�e��J�g��L�f��Ma���RQ��I��2��5��>��t��5�	�|�S��������/��.��Ca�]�
q �@�G�A�
�/�\��h�?�4��k�I��n�H��r���t�K��u�}��{�~��}�L�C���9������Z���s�aq��M���N�v���K�Q���@������P�� �R��"�*��#�r�S��&]�kq�@�>�T�D��(�O�s��)���/�L��4���5�x��6�N��8�O��=�8��D�P��E�Q	��F�t��P���R�~��Sq���U����W�!��X�q��Y���[�"��\�K�6�5��c�-��g�S��h]�Iq"�@��j�#���k�g��l�$��d��n�8��r�%��t���v�C��x�.��~����&��
����5�'���(���u���)q��+�k�,���j�y���f���g���l�� �-	��#]�'q�@��-�m�B���?���A��i��B�W
��N�%��J��\����]A�~�Q
����4��]��\��9��8��=��<��i�8a�	��c�?��m�g��p]�q�@��x�W��}���~�����G�
������7���p��!����"�p��#���q���(�f��-�R����q��.�
��2�	��4���5�'��:�|��;]�cq�@�A���=���@�N��B����C���G�)��W�a��i�$�t�c�b��ja���o�u��{�L]�Aq�@���z	��� ���h��#�P��$���%�L��&�H��*�>��+�e��;�d��A�9a���B�J��V���Z���\]�q�@��`� ��e�!	��f�"��p�'�9�%��q�)��x�.�+
��z�*�-���(�9��q�����6�0���8���
�7���E��&�:��'�C]�}q�@��+�@��,�=�A��/�>��0�?�@��2�;��3�B��7�D��:�9��B�L��C�G�K��K�I��N�H��P�J���Qq����X�M��]�4��_�Q��`�P�R��b�O��eQ���26��(��#��"��i��h��o��n��+a��]�[q�@��l�T��n�X��t�U�M��v�[��~�Z���Y�.�/���F���&���\���,	���#���1a�� ��]�9q�@��=���?���B�\��D����J���K����Z�n
��[���f�C��k���l���mq����o���p�}��r�o��s�
�	��y�G������
q�@
���P������`������ ���(�6��)���+�v��-q����<���A���B���K���O���Pq�@��X���Z���]�L��b�D��f�`� ��l���o���p�l
��q�"���.����#��q
����
�	�%��
�$���&����'�"�)��q�@��#�(��,���4�!�*��8����9���R��{��U��t��Va����W�
��g�q��r�w��tq�@��u���y���}�Z��
�A���3���j���
�� �i��"���$a����*���-�s��3q�@��I�F�k��[�S���^���_���c���e���0��g�q��h���j���m���n�r��s���wq����x�7���3���"�m���4�����S���a�@�Y=�� ��NPK
!<�ԏJ��/chrome/pdfjs/content/web/cmaps/GBKp-EUC-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�
GBKp-EUC-Ha���?�>�V��W
�F>��"�B�D�=��@��C���X�T�U�PK
!<o㛱zz.chrome/pdfjs/content/web/cmaps/GBT-EUC-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�����] �$` �$]�.a��]`�21�>	�p�z�$]��"R�d�-U�7�*�
4�%�+ �Z �{�/�%�<�9K�b�1]�.�"�q���,�%�4�&�6�'	�:�)�E�*�L�+�_�,�a�-�f�.�h�/�p�0�y�2�{�3��4��5�q'���
�6��8��9��:��;��=�#�@�+�A�4�B�:�D�<�E�?�F�K�I�N�J�R�L�T�M�X�N�[�O�a�S�gq���h�T�o�V�t�W�}�X��Y�	�Z��d�&�e�(�f
�+�h�7	�i�Dq0���s�H�u�M�y�O�z�T�{�[�|�_�}�d�~�h��j��l��s��u��x��������	���
����
�������!q&����&��*��/��3��8��<��C��F��K��N��Q� �S�!�X�#�^�$�`�%�q�&�s�'�|�(�q#����)��*�
�.��/��1��6��7��:�(�;�-�<�2�=�5�>�A�A�E�B�I�D�O�E�Y�G�_q1���`�H�e�J�g�K�i�L�k�M�p�O�s�P�w�Q��T��U��V�
�W��Y��[��\��]��^�!�_�)�`�,�a�/�c�1�d�6�e�9�f�<q(���h�A�k�E�l�N�n�Q�o�U�q�W�r�b�t�g�u�j�v�m�w�o�x�v�z�z�{��~�������
����q#����	�$�
�0��6�
�8��;��?��C��E��K��R��V��`��e��g��o��r�!�tq���z�"��#��$��%�
�&��'
��)� �*�%�+�*�,�2Q	�۾-�q�p�o�n�m�l�k�jq���=�2�@�3�F�7�H�8�K�9�O�<�R�=�T�>�Wq���X�?�_�@
�c�A	�r�B�}�C�
�D
��F��G� �H�$�e�+�I�0q!���6�J�:�K�<�L�@�N�D�P�F�Q�I�R�M�U�P�W�g�X	�j�Z�}�c��d��e�
�h��i�q/����j��k��m��o��p�!�q�,�t�/�u�5�v�7�w�;�y�>�z�A�|�D�}�J��L��O��Q��U��X��\�	�`��g��pq���r��y��~� ��$��%��&��)��-��0�!�2�)�3�,�4�3�6�=�7�J�<�Oq*���=�R�?�U�@�X�A�Z�B�]�C�a�D�g�G�j�H�p�I	�y�J��K�	�L�
�M��N��O��P��Q��R�%�S�(�U�,q!���.�W�3�X�8�Y�;�Z�S�[�U�\�\�]�`�_�g�`�r�c�w�d�y�e�{�f��g��h��i�q"���k�
�l��m��n��o��q�"�u�0��9��@��B��D��I��L�	�U�
�X��]��fq#���j��o��w	���%��'��(��)��*��+��,�%�/�(�0�/�2�3�3�6�5�=�6�?�7�Gq!¡�H�;�P�B�[�K�`�L�e�N�g�O�i�P�o�S�x�Y�z�Z��b�
�g��j��o��p��t�"q#á�x�'�y�0�z�3�{�y�:�|�D�}�M��R��T��\��_��f��k��s��v�	�|���
�qġ�������*��-��5��:��=��F��I��P��U��X��\�!�`�#qš�$�d�&�k�+�s�,�w�-�{�0�}�1
��2��3
��4�$�5�)�6	�6qơ�@�7�N�9�V�;�[�<�^�=�`�>�d�@�m�C�s�D�w�E��G��H��I��J��K�q-ǡ��L�!�M�$�O�'�Q�)�R�-�T�3�W�8�Y�<�[�>�\�@�]�C�^�F�`�J�a�M�b�S�d�U�e�\�f�a�i�l�n�v�p�y�q�{q#ȡ�|�r��s��u��v��w
��x�"�|�$�}�)�~�+��.��5�
�:��I��O��T��V�	�Xq/ɡ�
�[��_��b��f��k��n��s��|��~��������������������"� �*�!�.�#�0�$�3�&q'ʡ�8�'�>�*�@�+�C�-�I�.�N�1	�S�2�^�3�b�4�f�6�l�8�q�9�v�:�x�;�|�<��>�
�@��A��B�q'ˡ��C��D�!�E�#�F�)�G�,�I�.�J�5�K�=�L�B�P�F�R�I�S�K�T�V�V�Z�W�]�Y�`�Z�f�\�l�^�nq%̡�_�v�a�|�b��c��h��j��k
��l� �m�"�n�$�o�*�p�.�q�0�r�4�s�6�t�9�u�I�v�N�xq͡�R�{�_�|�e�}�i�~�n��q��w�	�y�����������
	���$��*q&�0�
�5��7��=��A��C��M��P��V��X��Z��a�!�j�$�l�%
�o�'�{�(�}�)��*��+q0ϡ��-��.��/�!�0�&�2�(�3�+�4�/�7�3�8�5�9�9�;�<�=�>�>�F�C�H�D�J�E�M�F�P�G�U�H�X�I�Z�J�\�K�b�L�h�NqС�l�O�q�P�z�R��U��W��X��Y��Z�.�[�1�\�7�]�9�^�E�a�Hq(ѡ�b�L�d�N�e�Q�f�U�g�[�j�a�m�c�n�f�p�r�s�u�t�y�v��x��y��{��}��~�������#q,ҡ�(��*��-��1��7�	�;�
�>��E�
�G��K��M��O��W��]��b��m��s��w��y��}�����q$ӡ�	� ��*��+��,��/��0�!�1�+�2�-�3�2�6�;�7�F�9�I�:�L�;�R�=�U�>�Z�?�b�@q(ԡ�d�A�h�B�l�E�v�I�z�K�|�L��M��O��P��R�
�S��W��X��Y�$�^�(�_�*�`�2�a�8�f�=�gq*ա�h�D�j�K�k�M�l�P�m�S�n�Z�r�]�s�_�t�b�u�g�v�i�w�m�y�o�z�v�{��}��~��������k�q'֡�	�$�	�0��4��;�
�=��@��D��J��M��S��W��Z��_��a��e��h��o��r� �v�"qס�~�%��&��)�
�*��.��0��1	��6�%�7	�(�8�3�9�<�=�E�>�Hqء"�W�@�{�A�~�B��C��E��G�
�H��I��K�(�L�.q١�5�N�B�O�D�P�H�S�\�T�e�X�r�Y�v�Z�y�[�{�\��]�
q
ڡ�:�_	�R��]��j��l�qۡ�q��t��w� �{�"��#��$� �%�"�&�,�'�/�(�3�)�5�*�<�+�?�,�B�-�Hqܡ�O�0�k�1�n�2�q�3�y�6��7��8��9��;��=� �>�%�?�(�@qݡ�B�.�C�4�H�8�J�?�K�B�L�H�N�`�O�h�P�k�Q�q�R�x�S�~�T��V�	�Wqޡ��X��Y��Z
�%�[�1�\�:�]�=�_�C�`�M�a�S�b�W�c�^�d�gq'ߡ�i�e�l�g
�n�h�}�i��k��l��n��o��p��q�"�s�%�t�)�u�+�v�/�w�2�x�4�y�8�z�>�{�Bq��G�|�^�~�f��o��u��z�������q"�	�%��0�	�2�
�5��9��@�
�B��F��N�	�U��`�*��g��l��t��{��}��q������	��E�%�1�.�3�/�<�0�G�2�I�3�N�6�[�7�`q��a�;�c�<�f�=�l�>�u�?��V�$�W�.�X�3�Z�9�[q��?�\�D�^�J�_�P�b
�U�d�a�e�d�f�h�g�x�h�{�i�}�j��k��m
��nq���o�-�p�6�q�D�r�F�s �K�t�m�u�o�v�zq��{�x�~�y��{�	�|��~���$��(�q
�9�!��[��]� �^�(�_�/q)��7�b�?�c�C�d�M�e�Q�h�W�j�\�k�`�n�e�o�g�p�j�q�t�r�v�s�z�u�|�v�~�w��x��y��z��{�q��}
��~�"��*��,��9��;��>��K��O��Y��\�
�^��`�q���x�#�{�%	��&��'��(� �)�&�*�Bq��Q�@�X�A�[�B�c�C�j�D�|�F�~�G��H��I��J�(q��/�K�2�L�=�R�@�S�D�U�N�W�P�X�V�Y�Z�Z�q�[��\��]�q��^��_��`��b�!�c�#�d�%�e�'�f�,�g�0�i�:�k�@�l�D�m�U�n	�[�o�ga��k�q��r�
:�s�"P�.�q��'�
�**��`�+�d�-�f�.�p�/�s�0�v�1�~�2��3�q"���4�
�5��6��7��8�!�9�%�:�.�;�2�<�5�>�?�?�A�@�I�A�Q�B�T�C�\�D�^�Eq��H�p�U�u�W�|�X��Y��Z��\��]��^�(�_�1�`�:�a�@q��A�b�`�c�g�d�i�e�w�f
�z�g��i��j�
�k��l
��m�q���n�$�o�.�p
�4�q�@�r�F�s�O�u�U�v�i�w�n�x�tq���}�y��{��}�!�~�%��,��/��6��9��D��L��O�	�Yq���[�
�^��a��c�
�f��k��y������/�q���N�H�]�L�_�N�`�V�a�\�b�_�d�d�f�i�h�w�9�z�i��j�q9���k�L�
��q��N�s�v�t�S�~�v�D�w�<�y�C�{�+��N�i����q����Z��z
��Z�]�j�l�r�����>��{��p��#��=�Y�"�{�	�$a���!�%�:�&�U�(�}�PK
!<�Q���.chrome/pdfjs/content/web/cmaps/GBT-EUC-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�	GBT-EUC-Ha���?�>�V��W
�F>��"�B�D�=��@��C���X�T�U�PK
!<�d�ee*chrome/pdfjs/content/web/cmaps/GBT-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE!!��]a!!]`�21�>	�p�z�$]��"R�d�-U�7�*�
4�%�+ �Z �{�/�%�<�9K�b�1]�.�"�q0!�,�%�4�&�6�'	�:�)�E�*�L�+�_�,�a�-�f�.�h�/�p�0�y�2�{�3��4��5�q'1!�
�6��8��9��:��;��=�#�@�+�A�4�B�:�D�<�E�?�F�K�I�N�J�R�L�T�M�X�N�[�O�a�S�gq2!�h�T�o�V�t�W�}�X��Y�	�Z��d�&�e�(�f
�+�h�7	�i�Dq03!�s�H�u�M�y�O�z�T�{�[�|�_�}�d�~�h��j��l��s��u��x��������	���
����
�������!q&4!��&��*��/��3��8��<��C��F��K��N��Q� �S�!�X�#�^�$�`�%�q�&�s�'�|�(�q#5!��)��*�
�.��/��1��6��7��:�(�;�-�<�2�=�5�>�A�A�E�B�I�D�O�E�Y�G�_q16!�`�H�e�J�g�K�i�L�k�M�p�O�s�P�w�Q��T��U��V�
�W��Y��[��\��]��^�!�_�)�`�,�a�/�c�1�d�6�e�9�f�<q(7!�h�A�k�E�l�N�n�Q�o�U�q�W�r�b�t�g�u�j�v�m�w�o�x�v�z�z�{��~�������
����q#8!��	�$�
�0��6�
�8��;��?��C��E��K��R��V��`��e��g��o��r�!�tq9!�z�"��#��$��%�
�&��'
��)� �*�%�+�*�,�2Q	9[�-�q�p�o�n�m�l�k�jq9d�=�2�@�3�F�7�H�8�K�9�O�<�R�=�T�>�Wq:!�X�?�_�@
�c�A	�r�B�}�C�
�D
��F��G� �H�$�e�+�I�0q!;!�6�J�:�K�<�L�@�N�D�P�F�Q�I�R�M�U�P�W�g�X	�j�Z�}�c��d��e�
�h��i�q/<!��j��k��m��o��p�!�q�,�t�/�u�5�v�7�w�;�y�>�z�A�|�D�}�J��L��O��Q��U��X��\�	�`��g��pq=!�r��y��~� ��$��%��&��)��-��0�!�2�)�3�,�4�3�6�=�7�J�<�Oq*>!�=�R�?�U�@�X�A�Z�B�]�C�a�D�g�G�j�H�p�I	�y�J��K�	�L�
�M��N��O��P��Q��R�%�S�(�U�,q!?!�.�W�3�X�8�Y�;�Z�S�[�U�\�\�]�`�_�g�`�r�c�w�d�y�e�{�f��g��h��i�q"@!�k�
�l��m��n��o��q�"�u�0��9��@��B��D��I��L�	�U�
�X��]��fq#A!�j��o��w	���%��'��(��)��*��+��,�%�/�(�0�/�2�3�3�6�5�=�6�?�7�Gq!B!�H�;�P�B�[�K�`�L�e�N�g�O�i�P�o�S�x�Y�z�Z��b�
�g��j��o��p��t�"q#C!�x�'�y�0�z�3�{�y�:�|�D�}�M��R��T��\��_��f��k��s��v�	�|���
�qD!�������*��-��5��:��=��F��I��P��U��X��\�!�`�#qE!�$�d�&�k�+�s�,�w�-�{�0�}�1
��2��3
��4�$�5�)�6	�6qF!�@�7�N�9�V�;�[�<�^�=�`�>�d�@�m�C�s�D�w�E��G��H��I��J��K�q-G!��L�!�M�$�O�'�Q�)�R�-�T�3�W�8�Y�<�[�>�\�@�]�C�^�F�`�J�a�M�b�S�d�U�e�\�f�a�i�l�n�v�p�y�q�{q#H!�|�r��s��u��v��w
��x�"�|�$�}�)�~�+��.��5�
�:��I��O��T��V�	�Xq/I!�
�[��_��b��f��k��n��s��|��~��������������������"� �*�!�.�#�0�$�3�&q'J!�8�'�>�*�@�+�C�-�I�.�N�1	�S�2�^�3�b�4�f�6�l�8�q�9�v�:�x�;�|�<��>�
�@��A��B�q'K!��C��D�!�E�#�F�)�G�,�I�.�J�5�K�=�L�B�P�F�R�I�S�K�T�V�V�Z�W�]�Y�`�Z�f�\�l�^�nq%L!�_�v�a�|�b��c��h��j��k
��l� �m�"�n�$�o�*�p�.�q�0�r�4�s�6�t�9�u�I�v�N�xqM!�R�{�_�|�e�}�i�~�n��q��w�	�y�����������
	���$��*q&N!�0�
�5��7��=��A��C��M��P��V��X��Z��a�!�j�$�l�%
�o�'�{�(�}�)��*��+q0O!��-��.��/�!�0�&�2�(�3�+�4�/�7�3�8�5�9�9�;�<�=�>�>�F�C�H�D�J�E�M�F�P�G�U�H�X�I�Z�J�\�K�b�L�h�NqP!�l�O�q�P�z�R��U��W��X��Y��Z�.�[�1�\�7�]�9�^�E�a�Hq(Q!�b�L�d�N�e�Q�f�U�g�[�j�a�m�c�n�f�p�r�s�u�t�y�v��x��y��{��}��~�������#q,R!�(��*��-��1��7�	�;�
�>��E�
�G��K��M��O��W��]��b��m��s��w��y��}�����q$S!�	� ��*��+��,��/��0�!�1�+�2�-�3�2�6�;�7�F�9�I�:�L�;�R�=�U�>�Z�?�b�@q(T!�d�A�h�B�l�E�v�I�z�K�|�L��M��O��P��R�
�S��W��X��Y�$�^�(�_�*�`�2�a�8�f�=�gq*U!�h�D�j�K�k�M�l�P�m�S�n�Z�r�]�s�_�t�b�u�g�v�i�w�m�y�o�z�v�{��}��~��������k�q'V!�	�$�	�0��4��;�
�=��@��D��J��M��S��W��Z��_��a��e��h��o��r� �v�"qW!�~�%��&��)�
�*��.��0��1	��6�%�7	�(�8�3�9�<�=�E�>�HqX!"�W�@�{�A�~�B��C��E��G�
�H��I��K�(�L�.qY!�5�N�B�O�D�P�H�S�\�T�e�X�r�Y�v�Z�y�[�{�\��]�
q
Z!�:�_	�R��]��j��l�q[!�q��t��w� �{�"��#��$� �%�"�&�,�'�/�(�3�)�5�*�<�+�?�,�B�-�Hq\!�O�0�k�1�n�2�q�3�y�6��7��8��9��;��=� �>�%�?�(�@q]!�B�.�C�4�H�8�J�?�K�B�L�H�N�`�O�h�P�k�Q�q�R�x�S�~�T��V�	�Wq^!��X��Y��Z
�%�[�1�\�:�]�=�_�C�`�M�a�S�b�W�c�^�d�gq'_!�i�e�l�g
�n�h�}�i��k��l��n��o��p��q�"�s�%�t�)�u�+�v�/�w�2�x�4�y�8�z�>�{�Bq`!�G�|�^�~�f��o��u��z�������q"a!	�%��0�	�2�
�5��9��@�
�B��F��N�	�U��`�*��g��l��t��{��}��qb!�����	��E�%�1�.�3�/�<�0�G�2�I�3�N�6�[�7�`qc!�a�;�c�<�f�=�l�>�u�?��V�$�W�.�X�3�Z�9�[qd!�?�\�D�^�J�_�P�b
�U�d�a�e�d�f�h�g�x�h�{�i�}�j��k��m
��nqe!��o�-�p�6�q�D�r�F�s �K�t�m�u�o�v�zqf!�{�x�~�y��{�	�|��~���$��(�q
g!9�!��[��]� �^�(�_�/q)h!�7�b�?�c�C�d�M�e�Q�h�W�j�\�k�`�n�e�o�g�p�j�q�t�r�v�s�z�u�|�v�~�w��x��y��z��{�qi!�}
��~�"��*��,��9��;��>��K��O��Y��\�
�^��`�qj!��x�#�{�%	��&��'��(� �)�&�*�Bqk!�Q�@�X�A�[�B�c�C�j�D�|�F�~�G��H��I��J�(ql!�/�K�2�L�=�R�@�S�D�U�N�W�P�X�V�Y�Z�Z�q�[��\��]�qm!�^��_��`��b�!�c�#�d�%�e�'�f�,�g�0�i�:�k�@�l�D�m�U�n	�[�o�gan!�k�q��r�
:�s�"P�.�qp!�'�
�**��`�+�d�-�f�.�p�/�s�0�v�1�~�2��3�q"q!��4�
�5��6��7��8�!�9�%�:�.�;�2�<�5�>�?�?�A�@�I�A�Q�B�T�C�\�D�^�Eqr!�H�p�U�u�W�|�X��Y��Z��\��]��^�(�_�1�`�:�a�@qs!�A�b�`�c�g�d�i�e�w�f
�z�g��i��j�
�k��l
��m�qt!��n�$�o�.�p
�4�q�@�r�F�s�O�u�U�v�i�w�n�x�tqu!�}�y��{��}�!�~�%��,��/��6��9��D��L��O�	�Yqv!�[�
�^��a��c�
�f��k��y������/�qw!�N�H�]�L�_�N�`�V�a�\�b�_�d�d�f�i�h�w�9�z�i��j�q9x!�k�L�
��q��N�s�v�t�S�~�v�D�w�<�y�C�{�+��N�i����q����Z��z
��Z�]�j�l�r�����>��{��p��#��=�Y�"�{�	�$ay!�!�%�:�&�U�(�}�PK
!<��S���*chrome/pdfjs/content/web/cmaps/GBT-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�GBT-Ha!"�?�>�V��W
�F>��"�B�D�=��@��C���X�T�U�PK
!<i.���0chrome/pdfjs/content/web/cmaps/GBTpc-EUC-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�����]� ` ^� a��]`�21�>	�p�z�$]��"R�d�-U�7�*�
4�%�+ �Z �{�/�%�<�9K�b�1]�.�"�q���,�%�4�&�6�'	�:�)�E�*�L�+�_�,�a�-�f�.�h�/�p�0�y�2�{�3��4��5�q'���
�6��8��9��:��;��=�#�@�+�A�4�B�:�D�<�E�?�F�K�I�N�J�R�L�T�M�X�N�[�O�a�S�gq���h�T�o�V�t�W�}�X��Y�	�Z��d�&�e�(�f
�+�h�7	�i�Dq0���s�H�u�M�y�O�z�T�{�[�|�_�}�d�~�h��j��l��s��u��x��������	���
����
�������!q&����&��*��/��3��8��<��C��F��K��N��Q� �S�!�X�#�^�$�`�%�q�&�s�'�|�(�q#����)��*�
�.��/��1��6��7��:�(�;�-�<�2�=�5�>�A�A�E�B�I�D�O�E�Y�G�_q1���`�H�e�J�g�K�i�L�k�M�p�O�s�P�w�Q��T��U��V�
�W��Y��[��\��]��^�!�_�)�`�,�a�/�c�1�d�6�e�9�f�<q(���h�A�k�E�l�N�n�Q�o�U�q�W�r�b�t�g�u�j�v�m�w�o�x�v�z�z�{��~�������
����q#����	�$�
�0��6�
�8��;��?��C��E��K��R��V��`��e��g��o��r�!�tq���z�"��#��$��%�
�&��'
��)� �*�%�+�*�,�2Q	�۾-�q�p�o�n�m�l�k�jq���=�2�@�3�F�7�H�8�K�9�O�<�R�=�T�>�Wq���X�?�_�@
�c�A	�r�B�}�C�
�D
��F��G� �H�$�e�+�I�0q!���6�J�:�K�<�L�@�N�D�P�F�Q�I�R�M�U�P�W�g�X	�j�Z�}�c��d��e�
�h��i�q/����j��k��m��o��p�!�q�,�t�/�u�5�v�7�w�;�y�>�z�A�|�D�}�J��L��O��Q��U��X��\�	�`��g��pq���r��y��~� ��$��%��&��)��-��0�!�2�)�3�,�4�3�6�=�7�J�<�Oq*���=�R�?�U�@�X�A�Z�B�]�C�a�D�g�G�j�H�p�I	�y�J��K�	�L�
�M��N��O��P��Q��R�%�S�(�U�,q!���.�W�3�X�8�Y�;�Z�S�[�U�\�\�]�`�_�g�`�r�c�w�d�y�e�{�f��g��h��i�q"���k�
�l��m��n��o��q�"�u�0��9��@��B��D��I��L�	�U�
�X��]��fq#���j��o��w	���%��'��(��)��*��+��,�%�/�(�0�/�2�3�3�6�5�=�6�?�7�Gq!¡�H�;�P�B�[�K�`�L�e�N�g�O�i�P�o�S�x�Y�z�Z��b�
�g��j��o��p��t�"q#á�x�'�y�0�z�3�{�y�:�|�D�}�M��R��T��\��_��f��k��s��v�	�|���
�qġ�������*��-��5��:��=��F��I��P��U��X��\�!�`�#qš�$�d�&�k�+�s�,�w�-�{�0�}�1
��2��3
��4�$�5�)�6	�6qơ�@�7�N�9�V�;�[�<�^�=�`�>�d�@�m�C�s�D�w�E��G��H��I��J��K�q-ǡ��L�!�M�$�O�'�Q�)�R�-�T�3�W�8�Y�<�[�>�\�@�]�C�^�F�`�J�a�M�b�S�d�U�e�\�f�a�i�l�n�v�p�y�q�{q#ȡ�|�r��s��u��v��w
��x�"�|�$�}�)�~�+��.��5�
�:��I��O��T��V�	�Xq/ɡ�
�[��_��b��f��k��n��s��|��~��������������������"� �*�!�.�#�0�$�3�&q'ʡ�8�'�>�*�@�+�C�-�I�.�N�1	�S�2�^�3�b�4�f�6�l�8�q�9�v�:�x�;�|�<��>�
�@��A��B�q'ˡ��C��D�!�E�#�F�)�G�,�I�.�J�5�K�=�L�B�P�F�R�I�S�K�T�V�V�Z�W�]�Y�`�Z�f�\�l�^�nq%̡�_�v�a�|�b��c��h��j��k
��l� �m�"�n�$�o�*�p�.�q�0�r�4�s�6�t�9�u�I�v�N�xq͡�R�{�_�|�e�}�i�~�n��q��w�	�y�����������
	���$��*q&�0�
�5��7��=��A��C��M��P��V��X��Z��a�!�j�$�l�%
�o�'�{�(�}�)��*��+q0ϡ��-��.��/�!�0�&�2�(�3�+�4�/�7�3�8�5�9�9�;�<�=�>�>�F�C�H�D�J�E�M�F�P�G�U�H�X�I�Z�J�\�K�b�L�h�NqС�l�O�q�P�z�R��U��W��X��Y��Z�.�[�1�\�7�]�9�^�E�a�Hq(ѡ�b�L�d�N�e�Q�f�U�g�[�j�a�m�c�n�f�p�r�s�u�t�y�v��x��y��{��}��~�������#q,ҡ�(��*��-��1��7�	�;�
�>��E�
�G��K��M��O��W��]��b��m��s��w��y��}�����q$ӡ�	� ��*��+��,��/��0�!�1�+�2�-�3�2�6�;�7�F�9�I�:�L�;�R�=�U�>�Z�?�b�@q(ԡ�d�A�h�B�l�E�v�I�z�K�|�L��M��O��P��R�
�S��W��X��Y�$�^�(�_�*�`�2�a�8�f�=�gq*ա�h�D�j�K�k�M�l�P�m�S�n�Z�r�]�s�_�t�b�u�g�v�i�w�m�y�o�z�v�{��}��~��������k�q'֡�	�$�	�0��4��;�
�=��@��D��J��M��S��W��Z��_��a��e��h��o��r� �v�"qס�~�%��&��)�
�*��.��0��1	��6�%�7	�(�8�3�9�<�=�E�>�Hqء"�W�@�{�A�~�B��C��E��G�
�H��I��K�(�L�.q١�5�N�B�O�D�P�H�S�\�T�e�X�r�Y�v�Z�y�[�{�\��]�
q
ڡ�:�_	�R��]��j��l�qۡ�q��t��w� �{�"��#��$� �%�"�&�,�'�/�(�3�)�5�*�<�+�?�,�B�-�Hqܡ�O�0�k�1�n�2�q�3�y�6��7��8��9��;��=� �>�%�?�(�@qݡ�B�.�C�4�H�8�J�?�K�B�L�H�N�`�O�h�P�k�Q�q�R�x�S�~�T��V�	�Wqޡ��X��Y��Z
�%�[�1�\�:�]�=�_�C�`�M�a�S�b�W�c�^�d�gq'ߡ�i�e�l�g
�n�h�}�i��k��l��n��o��p��q�"�s�%�t�)�u�+�v�/�w�2�x�4�y�8�z�>�{�Bq��G�|�^�~�f��o��u��z�������q"�	�%��0�	�2�
�5��9��@�
�B��F��N�	�U��`�*��g��l��t��{��}��q������	��E�%�1�.�3�/�<�0�G�2�I�3�N�6�[�7�`q��a�;�c�<�f�=�l�>�u�?��V�$�W�.�X�3�Z�9�[q��?�\�D�^�J�_�P�b
�U�d�a�e�d�f�h�g�x�h�{�i�}�j��k��m
��nq���o�-�p�6�q�D�r�F�s �K�t�m�u�o�v�zq��{�x�~�y��{�	�|��~���$��(�q
�9�!��[��]� �^�(�_�/q)��7�b�?�c�C�d�M�e�Q�h�W�j�\�k�`�n�e�o�g�p�j�q�t�r�v�s�z�u�|�v�~�w��x��y��z��{�q��}
��~�"��*��,��9��;��>��K��O��Y��\�
�^��`�q���x�#�{�%	��&��'��(� �)�&�*�Bq��Q�@�X�A�[�B�c�C�j�D�|�F�~�G��H��I��J�(q��/�K�2�L�=�R�@�S�D�U�N�W�P�X�V�Y�Z�Z�q�[��\��]�q��^��_��`��b�!�c�#�d�%�e�'�f�,�g�0�i�:�k�@�l�D�m�U�n	�[�o�ga��k�q��r�
:�s�"P�.�q��'�
�**��`�+�d�-�f�.�p�/�s�0�v�1�~�2��3�q"���4�
�5��6��7��8�!�9�%�:�.�;�2�<�5�>�?�?�A�@�I�A�Q�B�T�C�\�D�^�Eq��H�p�U�u�W�|�X��Y��Z��\��]��^�(�_�1�`�:�a�@q��A�b�`�c�g�d�i�e�w�f
�z�g��i��j�
�k��l
��m�q���n�$�o�.�p
�4�q�@�r�F�s�O�u�U�v�i�w�n�x�tq���}�y��{��}�!�~�%��,��/��6��9��D��L��O�	�Yq���[�
�^��a��c�
�f��k��y������/�q���N�H�]�L�_�N�`�V�a�\�b�_�d�d�f�i�h�w�9�z�i��j�q9���k�L�
��q��N�s�v�t�S�~�v�D�w�<�y�C�{�+��N�i����q����Z��z
��Z�]�j�l�r�����>��{��p��#��=�Y�"�{�	�$a���!�%�:�&�U�(�}�`��!PK
!<4X�O��0chrome/pdfjs/content/web/cmaps/GBTpc-EUC-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�GBTpc-EUC-Ha���?�>�V��W
�F>��"�B�D�=��@��C���X�T�U�PK
!<�g��--/chrome/pdfjs/content/web/cmaps/GBpc-EUC-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�����]� ` ^� aX��]`�21�>	�p�z�$]��"R�d�-U�7�*�
4�%�+ �Z �{�/�%�<�9K�b�1]�.�"��`]�,�"]�
�"]�h�"]�F�"]�$�"]��"]�`�"]�>�"]��"]�z�"]�X�"]�6�"]��"]�r�"]�P�"]�.�"]��"]�j�"]�H�"]�&�"]��"]�b�"]�@�"]��"]�|�"]�Z�"]�8�"]��"]�t�"]�R�"]�0�"]��"]�l�"]�J�"]�(�"]��"]�d�"]�B�"]� �"X�~�']�W�"]�5�"]��"]�q�"]�O�"]�-�"]��"]�i�"]�G�"]�%�"]��"]�a�"]�?�"]��"]�{�"]�Y�"]�7�"]��"]�s�"]�Q�"]�/�"]�
�"]�k�"]�I�"]�'�"]��"]�c�"]�A�"]��"]�}�"]�[�"]�9`��!PK
!<s�cf��/chrome/pdfjs/content/web/cmaps/GBpc-EUC-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�
GBpc-EUC-Ha���?�>�V��W
�F>��"�B�D�=��@��C���X�T�U�PK
!<��))&chrome/pdfjs/content/web/cmaps/H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE!!��]a!!]�y�"
�W�e�m�t���1	���0�&R�J�-U��*�s��H �# �DQ (!�7	w	WE$$aF0!]�e�"]�C�"]�!�"]��"]�]�"]�;�"]��"]�w�"]�U�"]�3�"]��"]�o�"]�M�"]�+�"]�	�"]�g�"]�E�"]�#�"]��"]�_�"]�=�"]��"]�y�"]�W�"]�5�"]��"]�q�"]�O�"]�-�"]��"]�i�"2�G�M]�z�"]�X�"]�6�"]��"]�r�"]�P�"]�.�"]��"]�j�"]�H�"]�&�"]��"]�b�"]�@�"]��"]�|�"]�Z�"]�8�"]��"]�t�"]�R�"]�0�"]��"]�l�"]�J�"]�(�"]��"]�d�"]�B�"]� �"]�~�"]�\�"]�:�"]��"]�v�"]�T�"�2�\PK
!<R�Ǿ^
^
/chrome/pdfjs/content/web/cmaps/HKdla-B5-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE��@��> ` ^a�s�@c�/!�"T�"�x�w�yA>�"]�?A>�"�\�w�>�S"]�A>�p"]�/A>�
"]�LA>�*"]�iA>�G"]�A>�d"]�#A>�"]�@A>�"]�]A>�;"\�z�A>�W"]�A>�t"]�3A>�".�P.�A>�/"]�nA>�L"]�A>�i"]�(A>�"]�EA>�#"]�bA>�@"]�A>�]"]�A>�z"]�9A>�"]�VA>�4"]�sA>�Q"]�A>�n"&�-6�UA>�"]�KA>�)"]�hA�F�T+�X"]�A>�b"]�!A>�"]�>A>�"	�[S�fA>�:")�y�e2�#A �V�x"��/�.C�0A�t�w'�
"]�2A>�"]�OA>�-�A	�l�t �v�"�+�-�H�wA>�	"U�H�v�A>�%"]�dA>�B"]�A>�_"]�A>�|"]�;A>�"]�XA>�6"]�uA>�S"]�A>�p"]�/A>�
"]�LA>�*"]�iA>�G"]�A>�d"*�#�T1�OA9��9�;"]�?A>�"]�\A>�:"]�yA>�W"=��N�UA>�t"�3W�:A>�"]�QA>�/"Z�n��IA>�K"]�
A>�h"]�'A>�"]�DA>�""]�aA>�?"]�~A>�\"]�A>�y"]�8A>�"]�UA>�3"]�rA>�P"]�A>�m"�,[�/A5��B"]�KA>�)"]�hA�F#�b"O��.�VA>�c"<�"�A�_A>�"�>T�HA>�"I�\��&A>�9"]�xA�V�G'�l")��a2�>A"�q��`�"]�0A'��o�6"!�L;�oA4�+	�a"]�kA%�I�p"�	�nF��eA>�g"]�&A"��d�'"]�BA>� "]�_A>�="]�|A6�Z�""���=�I
�>�JQ>�A��I�M?�� �%�P�"����T�L�U�J�W�l��K�-��q�t�/�y�|�#�0�e� �G��x�T�Q�T��c�]�j�{
�e��3�0�V�F�k��Q�n)�g�t�3��sQ����u�U�3�%�|���t��~�q�C�X�N�Q+�a���QJ����l�S��#�-`�p��v �R�[� �&�;��z��#��\�o��|��+�#�8�W�O��h�c�a��%��\��b�[��J�q�f�C=�&�+0����>�g�V�6�&�
�e�"�W�V�9�;�Q6�@�:�~���?�o����p�/�5�|�\�>�g�r�A�:����v�c�D�M8�J�a�5��(�
�>G���}�j�(�a�
��E�%�R�?:�z�Ps�Y�]a�v�dA�x�=�P�-�"�m	�9Q^���&�3�b�u��J�-�&B�F�1o�f�5H�n�m�p�y�c�2�m�-��~�-�m�
�5�l.�r�k�~�i��_� �;�G�b�X��e������d��U��&1�s�*��\�?�,��O� �a�,���Si�s�8��x�2�o�\
�[��1��V�^��y���u�,��$�kQ?�@���1�<�;�*�/�F��{����~�
�<� �=�Z�Y�l�$��A�21�
�T�q�?�L�:	��M��P��_��,�G�)�3��c�x�]	�
��!���j$�e�`�&�/�[tjt���c�nQ���K�"��}��H��(�>�a�Z�YK��	���P����x�b�U�2�qa����^Q����k�K�(�;�.��G�3�S�fC�m�
�l�+��M�V�&�j�e���S��2�sa����@Q)��}����&�w�0I��W�`� ����%��
�V��0�U�f�g�N?�`�\�a�s�����X��y��bI�Q�;Q?�@�~�G��0����&��:�j��$�V��A�B�0
�Q��8�1�6��]�$��P�i�r�f�C��1�,�� 	�?�0�/�l�F��	/���I����tiQ^���8�7�B�$q��P�I�j��U�:�1	��{� �a�'�~�u�����/��J�g�~��6�_�@�U�l�A�,�%6�;�P�_��	��)�e����6�M��N�����K��A"�L�<�	�D�S�~�,�G�&�)�|�<�y��
�xQ(�@��J�%�z��u�D�0�K�0�	��+(7�O�r��k
	�)�'�H�-�9�P��J�S�XC�
�ha�h�kQ�j��~�g�s�3�,�E�8�(����@�nW�i�p�)Q���R�4�0�#�.�J�g�\�A
�:�=�>�=�.��B�^�5a���fQH����5���4�)��(��,�D��O�n�'���b�;�$�#���>�/�4��`�)�x4��1�6�?�V��N�c�q��J�S��x�
���4�k�L:�E�h�i��}��P�\�!�|�k�j�;�'�ZPK
!<}���/chrome/pdfjs/content/web/cmaps/HKdla-B5-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�
HKdla-B5-Ha�K�N�/�1���
�����h�OPK
!<dXLn	n	/chrome/pdfjs/content/web/cmaps/HKdlb-B5-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE��@��> ` ^Q?�@�w�X��,�=�8�S�(��x��;� ��:��z�X�3�sC��*�C�P�a� �>��
�,<�1�V�t�c��v�i�i�$�{�&�E�Q�{*�=h�k�H�Y�6�SY���Q'Q$���~��&��t�o�$�p�x�y�L�c�1�f�k�E�>�p�=�=��j�$�%��u�p�0��c�H��M	�ka���tQ8�ǁ�t�Q�%�k��F�}���0�I���i�j�;��i�H�I���+�H�U�p�W�n�}��t��?��m�V��2�w���/�'�(�M�$��/�����MiQ?�@�d�K�s�P�l�U��XE�P�G��U�T���z��^�A�H�	�,�W��
��(J�n�6��j�#�9V\�	�h�;�7�"��D�V�&�9�JX�6�w�B�5�p��Q�{�^�/Q^���2�9�D�s�)�\�0�t��
�@�[�*�#�G�b�c�	�}�n�&�f�Q�>��
��!�o�D�Q�o���p�5S��M����&�+H�d�!�Z�q��_�2�
z��>�i�P�/�(|��L��1�:e���m�|�=�;^�Y�3�h�	�g��{�J�{�3�Z�)���o�H�g�mWQ?�@��}�d���m� ��?�:�Y�:�+�?��C�ZH�e�es�,m�o��v�5�2�|���+�M�.�Z��fi��4�s�$�c�x��?�&�nK�S�`��{���U�V��1Q^���z�L�A�f�/�?�i�F�_�>�'�PN�)�e�~��Y���_�,�60
��"�-�;��I���]�%�X�y�)�d�
�?��<�5�b��f�F��O�	��)�;�7�f�)��M��\�5�>�=�.�=�'�^�9�_��U�7��s�f�W��4��n�'Q?�@�9�n�g�x�&�f�S�L���k�@�\�
�j�;�'��%��+��x�����[�X�4�-��B�U6��Y�$��^�P��h�5�8�Q�P��;�5��i�~�u�D�)�M�)�s�}��<�B��*�#Q���&�=�l�A�5��!���h�?�&��R����%��D�I�6�	+�:�e�Z���~�W�_a����^Q���v�3�R�m;�.�J��J�t�b���-�b��O��^�I�
�t�3Q(��4�.�e��	�V~�A�^��z�4�?�/��f�C��!��X�q�X��k�E�,L�9�}�R�-�^�$�
��P�l�j�5Q&�@�*�F��E�V�p�s���D��g�j�&|�+�G�2�-���Y�f�V��]�L�`�u�M�d�3��]�l��ya�s�@c�/!�"T�"�x�w�yA>�"]�?A>�"�\�w�>�S"]�A>�p"]�/A>�
"]�LA>�*"]�iA>�G"]�A>�d"]�#A>�"]�@A>�"]�]A>�;"\�z�A>�W"]�A>�t"]�3A>�".�P.�A>�/"]�nA>�L"]�A>�i"]�(A>�"]�EA>�#"]�bA>�@"]�A>�]"]�A>�z"]�9A>�"]�VA>�4"]�sA>�Q"]�A>�n"&�-6�UA>�"]�KA>�)"]�hA�F�T+�X"]�A>�b"]�!A>�"]�>A>�"	�[S�fA>�:")�y�e2�#A �V�x"��/�.C�0A�t�w'�
"]�2A>�"]�OA>�-�A	�l�t �v�"�+�-�H�wA>�	"U�H�v�A>�%"]�dA>�B"]�A>�_"]�A>�|"]�;A>�"]�XA>�6"]�uA>�S"]�A>�p"]�/A>�
"]�LA>�*"]�iA>�G"]�A>�d"*�#�T1�OA9��9�;"]�?A>�"]�\A>�:"]�yA>�W"=��N�UA>�t"�3W�:A>�"]�QA>�/"Z�n��IA>�K"]�
A>�h"]�'A>�"]�DA>�""]�aA>�?"]�~A>�\"]�A>�y"]�8A>�"]�UA>�3"]�rA>�P"]�A>�m"�,[�/A5��B"]�KA>�)"]�hA�F#�b"O��.�VA>�c"<�"�A�_A>�"�>T�HA>�"I�\��&A>�9"]�xA�V�G'�l")��a2�>A"�q��`�"]�0A'��o�6"!�L;�oA4�+	�a"]�kA%�I�p"�	�nF��eA>�g"]�&A"��d�'"]�BA>� "]�_A>�="]�|A6�Z�""���=�I
�>�JPK
!<�委�/chrome/pdfjs/content/web/cmaps/HKdlb-B5-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�
HKdlb-B5-Ha�K�N�/�1���
�����h�OPK
!<O�z���0chrome/pdfjs/content/web/cmaps/HKgccs-B5-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE��@��> ` ^a�~�!�0���aC�@�+$�0	�U�_#�f!�k�
�(�1A�@�Y�^�e"#�|� �! �3�TA�V�r	��i�
"��E�'A$�m��#�)[�*A>�"
�E1�S��A'��E"�[�d!�{��"A�57�;"]�sA�Q�^#�j"$�7�3A>�k"2�*)�]A>�"]�FA4�$�Y�Z(�[�\�]�^�_�a�b�ca�@>��"D��G[������������������A!��e	F	)'U

3a���A
��		Ja���a�k�jaC�^�����!"�"�#�%�)�*�+�3�4�;�<�=�?�@�A�D�E�F�G�H�I�M�O�P�Q�SC�U�X�[�\�]�d�e�j�k�l�m�p�q�r�s�w�z�{�}"�~����	�����$	�'�1�4�6�BH�C�D�FA	�O�H��a�u�@c!�"T�"�x�w�yA>�"]�?A>�"�\�w�>�S"]�A>�p"]�/A>�
"]�LA>�*"]�iA>�G"]�A>�d"]�#A>�"]�@A>�"]�]A>�;"\�z�A>�W"]�A>�t"]�3A>�".�P.�A>�/"]�nA>�L"]�A>�i"]�(A>�"]�EA>�#"]�bA>�@"]�A>�]"]�A>�z"]�9A>�"]�VA>�4"]�sA>�Q"]�A>�n"&�-6�UA>�"]�KA>�)"]�hA�F�T+�X"]�A>�b"]�!A>�"]�>A>�"	�[S�fA>�:")�y�e2�#A �V�x"��/�.C�0A�t�w'�
"]�2A>�"]�OA>�-�A	�l�t �v�"�+�-�H�wA>�	"U�H�v�A>�%"]�dA>�B"]�A>�_"]�A>�|"]�;A>�"]�XA>�6"]�uA>�S"]�A>�p"]�/A>�
"]�LA>�*"]�iA>�G"]�A>�d"*�#�T1�OA9��9�;"]�?A>�"]�\A>�:"]�yA>�W"=��N�UA>�t"�3W�:A>�"]�QA>�/"Z�n��IA>�K"]�
A>�h"]�'A>�"]�DA>�""]�aA>�?"]�~A>�\"]�A>�y"]�8A>�"]�UA>�3"]�rA>�P"]�A>�m"�,[�/A5��B"]�KA>�)"]�hA�F#�b"O��.�VA>�c"<�"�A�_A>�"�>T�HA>�"I�\��&A>�9"]�xA�V�G'�l")��a2�>A"�q��`�"]�0A'��o�6"!�L;�oA4�+	�a"]�kA%�I�p"�	�nF��eA>�g"]�&A"��d�'"]�BA>� "]�_A>�="]�|A6�Z�""���=�I
�>�J�y�/i�_�p� a�Y�/ab���P�T�U�X�Y�Z�[�\�^�`�b�d�e�j!�m�A"��4�6�7�8�9�:�<�=�>"�?�A�D�F�H�K�L�N�O�P�R�T�X�Z�\�]�_�w�z�|A�����#
�*"�8�L�Q�T!�U�w�B���#�:�G"�J�K�O	�l�v����Y>��"]��JA>��("��g��0��A	��C��M��Q��S��c��t"��}��%��	��=��GA��W4��`"P����fA,��r	����)"��/2��8��k��xPK
!<]??ϕ�0chrome/pdfjs/content/web/cmaps/HKgccs-B5-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�HKgccs-B5-Ha�K�N�/�1���
�����h�OPK
!<��#y��0chrome/pdfjs/content/web/cmaps/HKm314-B5-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE��@��> ` ^a_�@c�/!�"T�"�x�w�yA>�"]�?A>�"�\�w�>�S"]�A>�p"]�/A>�
"]�LA>�*"]�iA>�G"]�A>�d"]�#A>�"]�@A>�"]�]A>�;"\�z�A>�W"]�A>�t"]�3A>�".�P.�A>�/"]�nA>�L"]�A>�i"]�(A>�"]�EA>�#"]�bA>�@"]�A>�]"]�A>�z"]�9A>�"]�VA>�4"]�sA>�Q"]�A>�n"&�-6�UA>�"]�KA>�)"]�hA�F�T+�X"]�A>�b"]�!A>�"]�>A>�"	�[S�fA>�:")�y�e2�#A �V�x"��/�.C�0A�t�w'�
"]�2A>�"]�OA>�-Q�@�m�S�(�)�7�:��:�s�t�S�T�y�Ta�O��HQ!�Q��K�$��m�O�8��S�+�:�4�'� ��3��^;�(�C�;�R�4�!�9�aa�r�(Q�t�.�.�&��x�C�G9�
�|Qǡ�$#�D�5�4�aQ !��7�t�@�a�z�3�2�b�q�j�x�+��~aǾ��1Q���L�
��l�G�?��w�&��w�^�1�Q��`�la����<Q���?�&�I�b�k�z�Q���U�u�f��e�b�C�j�s�h�Y�j�a���BA��(�j/�K7R�uQ&�@�u�~
�:RI�M��o�~�W�|�uu���f�b�'�n�-�*�F�G�K�0�� ��a�Ka�f��K�2��
Q�l�_�P�7�@��~�;n�P�g�g�N��Y��g��Qȡ�F�K�8�(�y�e,�L�5��(�'�&��m�j�k�0�+�y&<�8�G�BaȽ��Q	��1�n�t�W�A~w�U�la����g�^��i�>��cQ��l�w
�0�'�
�A�V�p�7�&�a�]�	�&�N�O�q�����o���*����q��f�"�~�9�!��r�0�)�<�6��:a��@	�l�t �v�"�+�-�H�wA>�	"U�H�v�A>�%"]�dA>�B"]�A>�_"]�A>�|"]�;A>�"]�XA>�6"]�uA>�S"]�A>�p"]�/A>�
"]�LA>�*"]�iA>�G"]�A>�d"*�#�T1�OA9��9�;"]�?A>�"]�\A>�:"]�yA>�W"=��N�UA>�t"�3W�:A>�"]�QA>�/"Z�n��IA>�K"]�
A>�h"]�'A>�"]�DA>�""]�aA>�?"]�~A>�\"]�A>�y"]�8A>�"]�UA>�3"]�rA>�P"]�A>�m"�,[�/A5��B"]�KA>�)"]�hA�F#�b"O��.�VA>�c"<�"�A�_A>�"�>T�HA>�"I�\��&A>�9"]�xA�V�G'�l")��a2�>A"�q��`�"]�0A'��o�6"!�L;�oA4�+	�a"]�kA%�I�p"�	�nF��eA>�g"]�&A"��d�'"]�BA>� "]�_A>�="]�|A6�Z�""���=�I
�>�JPK
!<N����0chrome/pdfjs/content/web/cmaps/HKm314-B5-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�HKm314-B5-Ha�K�N�/�1���
�����h�OPK
!<�$+v{{0chrome/pdfjs/content/web/cmaps/HKm471-B5-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE��@��> ` ^a�s�@c�/!�"T�"�x�w�yA>�"]�?A>�"�\�w�>�S"]�A>�p"]�/A>�
"]�LA>�*"]�iA>�G"]�A>�d"]�#A>�"]�@A>�"]�]A>�;"\�z�A>�W"]�A>�t"]�3A>�".�P.�A>�/"]�nA>�L"]�A>�i"]�(A>�"]�EA>�#"]�bA>�@"]�A>�]"]�A>�z"]�9A>�"]�VA>�4"]�sA>�Q"]�A>�n"&�-6�UA>�"]�KA>�)"]�hA�F�T+�X"]�A>�b"]�!A>�"]�>A>�"	�[S�fA>�:")�y�e2�#A �V�x"��/�.C�0A�t�w'�
"]�2A>�"]�OA>�-�A	�l�t �v�"�+�-�H�wA>�	"U�H�v�A>�%"]�dA>�B"]�A>�_"]�A>�|"]�;A>�"]�XA>�6"]�uA>�S"]�A>�p"]�/A>�
"]�LA>�*"]�iA>�G"]�A>�d"*�#�T1�OA9��9�;"]�?A>�"]�\A>�:"]�yA>�W"=��N�UA>�t"�3W�:A>�"]�QA>�/"Z�n��IA>�K"]�
A>�h"]�'A>�"]�DA>�""]�aA>�?"]�~A>�\"]�A>�y"]�8A>�"]�UA>�3"]�rA>�P"]�A>�m"�,[�/A5��B"]�KA>�)"]�hA�F#�b"O��.�VA>�c"<�"�A�_A>�"�>T�HA>�"I�\��&A>�9"]�xA�V�G'�l")��a2�>A"�q��`�"]�0A'��o�6"!�L;�oA4�+	�a"]�kA%�I�p"�	�nF��eA>�g"]�&A"��d�'"]�BA>� "]�_A>�="]�|A6�Z�""���=�I
�>�JQ�@�m�S�(���(��I�:��:�sq�L�L���E����4���#�&�� ��Q��H��K��O��T��x�
��!��Z�$�=��#�?��\��_��c��$��zQ�m��}�+�n�5�4�[�2� ��3��^�6�e�Q����(�=��;�R�4�!�9�J�-a���(Q.���.�.�&��x�C�G9�
�|$�*�O�D�5�4�a�*�} !��7�t�@�a�z�3�2�b��g�z�j�x�	��7��~a����1Q�߁�L�
���l�G�?�B�]�w�\5�}�^�w�^�1�Q��`�l�H�9�,�=a����7A����#Q0�@��'�"�!� �K�2�K�`�!� �	��_�=��&�I�b�L�;�k�z�M�z����u�k�u�R�m��e�b�C�j�s�`�Y�j�d��a�p��BQ
�r��V�]�j%�K�x�1Ra�|�t�""�r�y�m��DQ.���2�&�5�:R�$�o�M�f�>�%�D�/�d�o�~�W�|�uu�$�;�@�#�f�W�8�'�n�-�*�F�G�K�0�� a����I��5��K�2��
Q���h�c�Z��7�@��~�;�>�M�L�Qq
����R��X�s��`����-�s�U��UA����Y��MQ4�@��l�g�2�3�W�S�}��4�=
��r�Y�8�Y�L�(�y�k 	�L�5��(�'�&��m�j��C�B�
�0��H��y
<a�t��`Q	�v�0�b�k�A�B�D�y�A���a���Q���1��O��0�u�t�W�M�"���_�B�w�U�la����g�^��i�>��cQ���l�w�\��]�\�g�m�0� �I�
�A�V�p�7�&�a�]�|��n�I�N��<�+�(�a����m�}�Q�߁�o�U�x�2�&�
�?���.�k�E�>�?��v�I�d�q�Na����rQ	���0��$�
�z�{�0�H��wPK
!<S�#��0chrome/pdfjs/content/web/cmaps/HKm471-B5-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�HKm471-B5-Ha�K�N�/�1���
�����h�OPK
!<f�dmUU/chrome/pdfjs/content/web/cmaps/HKscs-B5-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE��@��> A�~�!�2A
�i�UA*�t�w���ss�,2�}n�pn��$�U�B��WA�o�h�y�M�+�P���m�xH�1�A�
�C��6�qST���
� �m	A�t�s�r�s�t�s
E��Y�X�w�v�q�p�o�n)
�E�D�C�B�SX�~���������� �!V;�y�Bxwc�8!�o78�x�y(26`J�<$�	�*	a����6���c
�b�3W�2�Y�^
�+��,"��%{�c�bZ��uA"Z2Q�f}�7�6i�/��2�o(Q	����J�>���?�]��e�dA���_��s�d��M�m�~�u���U�#�s���
�V�a�E�h��;�8�GA�x�%e�\�W\�t�AY���w�"�r�M�	��"�x� �m��o�g�>�8�<�@�':��E�(�M�T��15P}�jm�FA�h�$�'X�q��N���os�l�S�-�H�d�W��d�R�%A�v�P�8J�){�((�v�t�L���G�l�$��$�j�t�M��|�|��L��-�j�`�XVA�f�Eq�p�
�[q�8KAr��a	F	)'U

3	J	"
T

	
&#6-DH+��"+	
(>�A�*�`�j��&��0r�d�e�#A)�ҁ�MCHI?/.
iv&o�z 	-
	{+�_:�-�S�Z ?V�u���{�3A��Á�q 	FZ^=dc<"	/"uR$$	
H
	,
OE6
)F%
G'D
F

&
�|�\�a�14
|	V
Z xz
` ^a�[����c!�"T�"�yA>�"]�?A>�"�\�w�>�S"]�A>�p"]�/A>�
"]�LA>�*"]�iA>�G"]�A>�d"]�#A>�"]�@A>�"]�]A>�;"\�zB>�W"]�A>�t"]�3A>�".�P.�A>�/"]�nA>�L"]�A>�i"]�(A>�"]�EA>�#"]�bA>�@"]�A>�]"]�A>�z"]�9A>�"]�VA>�4"]�sA>�Q"]�A>�n"&�-6�UA>�"]�KA>�)"]�hA�F+�X"]�A>�b"]�!A>�"]�>A>�"	�[S�fA>�:")�y2�#A �V�x"�C�0A�t'�
"]�2A>�"]�OA>�-"�z��*�m	�l �v�"�+-�H�wA>�	"U�H�A>�%"]�dA>�B"]�A>�_"]�A>�|"]�;A>�"]�XA>�6"]�uA>�S"]�A>�p"]�/A>�
"]�LA>�*"]�iA>�G"]�A>�d"*�#1�OA9��;"]�?A>�"]�\A>�:"]�yA>�W"=��UA>�t"�3W�:A>�"]�QA>�/"Z�n�IA>�K"]�
A>�h"]�'A>�"]�DA>�""]�aA>�?"]�~A>�\"]�A>�y"]�8A>�"]�UA>�3"]�rA>�P"]�A>�m"�,[�/A5��B"]�KA>�)"]�hA�F#�b"O��VA>�c"<�"�_A>�"�>T�HA>�"I�\�&A>�9"]�xA�V'�l")�2�>A"�q��"]�0A'��6"!�L;�oA4�+	�a"]�kA%�I�p"�	F��eA>�g"]�&A"��'"]�BA>� "]�_A>�="]�|A6�Z�""�
�>�Ja�����%W	������#��'��,��:��@'��G��O��R��X��a��o��t��~��[��
���o�+"�0�V�Y�_�d#�f�k�p�y�}�
���(�1�8A�@�Y�^�g	�l�w"�|��!�&�3�<D�V�l
�s	��
"��"�'�K�`A$�m��%�*�G5�PA���+�4"
�E�S�i�r��A'��E"�[�d!�{�� �"A�50�<�n"(�s2�A�Q�^�j�o"$��3$�FA�k�p�}�"2�*�]
�v�A>�"]�FA�$(�*�TH�_�c��N��U�c����/�#�%�+�4
�=�A�I�M�Q�SC�U�X�]�e�m�s�w�{$�~��	����!�$	�'�1�4�6R�FW�P�U�\�^�b�e�j!�m�A���1�4�:(�?�A�F�H�L�P�R�T�Z�]�_�u�w�zE������#
�*"�8�L�Q�U�`�h
�l�w�B���#�1�:�D�G$�K�O	�l�v�{����	r�/�{�3�:A>�Y"]�A>�v"�5(�a�(�hA����+��2"��J��T��g��o(��A��(	��1��<��W"��g��j����$����8��>A	��C��M��Q��S��c��m��t"��}��
����'	��=��G��JA��W��`��"����-&��0��X��fA��r����!��)"��/1��8��k��xa�:�@%����;��">��`��I(��a"��
�����4��7��<,��?"��l
��s
��v��	��������
��#��1�W��B��E��RD��\��b��e��i��n��t"��
��_��|B!��!��C"��_��d������P��XC��r��)��+��0��3��:��>"��@��D��F��R5��^��l���o��/�e��N/��Q��S��U"��Y
��[��f��n	��s��}������	������D���� ��'��+��2��9��>��A��G��I(��R��Y��\��_��b��q��s��x��z������
	��A	����&��2��8��>��F#��N��Q��T��[��b��k��t��z������������A��������'

��.��<��>*��B��E��G��I	��N��V��X��\	��a��d��hL��m��r��z.����������"��D��������#��'��)��0��C#��G	��M��O��S��X��Z��]	��`���h��k��r0��y	��}��������	��������R�����~��0��7��:�U��F����%��-	���*��F���R� ��]
��_PK
!<����/chrome/pdfjs/content/web/cmaps/HKscs-B5-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�
HKscs-B5-Ha�]���
�����a�K�N�/�1��Oa���PK
!<iF{τ�,chrome/pdfjs/content/web/cmaps/Hankaku.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�`
 ?�g�g�(�G	��V�>�G��PK
!<J�F||-chrome/pdfjs/content/web/cmaps/Hiragana.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�` ��G	��V,���;�>PK
!<o��88.chrome/pdfjs/content/web/cmaps/KSC-EUC-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�����] �` ^�a?��]e�"D�C�;]��"2�f)��"	�C	�M�W�o�(C��<N�K�1���M�'�"]�u�"R�S�-U�&�* �| ��/]�>�"]��"]�z�"]�X�"]�6�"]��"]�r�"]�P�"]�.�"]��"]�j�"]�H�"]�&�"]��"]�b�"]�@�"]��"]�|�"]�Z�"]�8�"]��"]�t�"]�R�"]�0�"]��"]�l�".�J��y�^�~��"]�%�"-���1�I�"�_�ZP�k�"Y�<�>�a���
a���\q	С��j��j�/�|�F�# �Rqѡ2�s��<�&��(��*�(��"�+�#�,�'�-�*�.�,�/�/�2�3�5�6�9q'ҡ�6�:�7�<�C�:�D�;�H
�?��J�!�%�M�&�,�N�-�0�7�O�=�P�A�S�C�F�T�T�V�[�`�U�|��Y��Za>ӡ]�`�"D�>�^��(��"��(��+�#�`O�%�"�u�G��L0� �")�Q�0�{��"]�-�"]��"]�i�"#�G��k�:�	�"�#X�'�"��W.��u�3	�Q�"Z�[�P�6�w�"�7,�I�r�v�m�|�"O��q�b�"�o�T>�{�E�:�"]�K�"S�)�}�#��.T�
a���aܥ�^�N�7�>�+�A�a�+�Jq��b�r�o�I�q�K�w�M�x�O��S��T�	�U�
�W"�
�Z�?�0�\q6��_�1�a�3�b�5�e�:�g�;�j�<�k�?�o�B�@�I�q�J�A�L�t�S�u�V�v�X�B�Z�w�\�x�]�z�y�{�_�|�`�~�/�a��c��T�d��C�F�f��gq#���l��p�	��r�G�
�s�
�u���g����������
�
��������%�/q
�E�6�G�|�I�T��K��(�	�L�
�Mq	���P��Q��S)�5�4�_q��f�k"��a
�)�W�7�c�9�e�=�fq��>�h�A�j�C�l�X�D�n�S�p�T�q�s�W�t�Z�w�^�x�0�`�{%�eq%������
��>��[��������� ���#�\	�%��/� �6�"�8�$�:�%
�<�'�J�*�Ka	��-�
�_�B�7�C��*
�t�N�fC�<�B�ga���g�N�c<��u�`a'�I�O��"9�)�^"�c�"]��"]�d�"]�B�"� ;�<�x�"�|L��"�Y�TE�_��%�"]�5�"R��f�c�g�"�o�	�*��"9�J��
��"]�&�"�B��G�"]�_�"�=T�E�"]�PK
!<��w��.chrome/pdfjs/content/web/cmaps/KSC-EUC-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�	KSC-EUC-Ha���x�z��{�~�-��5��
�������PK
!<k,�''*chrome/pdfjs/content/web/cmaps/KSC-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE!!��]a?!!]e�"D�C�;]��"2�f)��"	�C	�M�W�o�(C��<N�K�1���M�'�"]�u�"R�S�-U�&�* �| ��/]�>�"]��"]�z�"]�X�"]�6�"]��"]�r�"]�P�"]�.�"]��"]�j�"]�H�"]�&�"]��"]�b�"]�@�"]��"]�|�"]�Z�"]�8�"]��"]�t�"]�R�"]�0�"]��"]�l�".�J��y�^�~��"]�%�"-���1�I�"�_�ZP�k�"Y�<�>�aKg�
aMh�\q	P!��j��j�/�|�F�# �RqQ!2�s��<�&��(��*�(��"�+�#�,�'�-�*�.�,�/�/�2�3�5�6�9q'R!�6�:�7�<�C�:�D�;�H
�?��J�!�%�M�&�,�N�-�0�7�O�=�P�A�S�C�F�T�T�V�[�`�U�|��Y��Za>S!]�`�"D�>�^��(��"��(��+�#�`O�%�"�u�G��L0� �")�Q�0�{��"]�-�"]��"]�i�"#�G��k�:�	�"�#X�'�"��W.��u�3	�Q�"Z�[�P�6�w�"�7,�I�r�v�m�|�"O��q�b�"�o�T>�{�E�:�"]�K�"S�)�}�#��.T�
aWd�a\%�^�N�7�>�+�A�a�+�Jqe!�b�r�o�I�q�K�w�M�x�O��S��T�	�U�
�W"�
�Z�?�0�\q6f!�_�1�a�3�b�5�e�:�g�;�j�<�k�?�o�B�@�I�q�J�A�L�t�S�u�V�v�X�B�Z�w�\�x�]�z�y�{�_�|�`�~�/�a��c��T�d��C�F�f��gq#g!��l��p�	��r�G�
�s�
�u���g����������
�
��������%�/q
h!E�6�G�|�I�T��K��(�	�L�
�Mq	i!��P��Q��S)�5�4�_qj!�f�k"��a
�)�W�7�c�9�e�=�fqk!�>�h�A�j�C�l�X�D�n�S�p�T�q�s�W�t�Z�w�^�x�0�`�{%�eq%l!�����
��>��[��������� ���#�\	�%��/� �6�"�8�$�:�%
�<�'�J�*�Ka	m!�-�
�_�B�7�C��*
�t�N�fC�<�B�gamn�g�N�c<��u�`a'm$I�O��"9�)�^"�c�"]��"]�d�"]�B�"� ;�<�x�"�|L��"�Y�TE�_��%�"]�5�"R��f�c�g�"�o�	�*��"9�J��
��"]�&�"�B��G�"]�_�"�=T�E�"]�PK
!<�KR�A�A0chrome/pdfjs/content/web/cmaps/KSC-Johab-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE��A��=�2�M�2�M ` ^a�B�f�n�w�y�A�a�___�ea'�a�>�u�@�w�A�y�E�F�~�M�P��R��S��T�U��V��Y��[��\���]�!�^�"�_�(�`�+�a�.
�6q	���b�A�d�C�e�E�g�F�ha'���i�K�j�L�nC�q�N�r�Q�s�T�t�u�[�v�\�y�b�|�c�}�e���l��m��p��r��u��x���	�q���
�	���
�
�����a��������q	�����������ad����#��$��%� �+�!�.�"�1�#�9�$�;C�&�A�(�C�)�F�*�+�M�,�N�-�O�.�U�/�X�0�[�1�c�2�d�3�l�5�n�6�p�:�<�t�=�u�>�v�@�{�B�}�C���D��F��!�G�#�H�+�J�-�K�0�L�M�7�N�8C�O�@�P�C�Q�F
�N�R�Y�T�[�U�]�X�Y�c�Z�d�[�e�\�k
�{q	���]��_��`�
�b��ca	���d��e��f��h���gq	�a�i��l��m��n��oa;�s�p� �q�!�t�$�u�&�w�(�x�+�y�z�2�{�3�~�9��?
�G�R
�c��n��o��r���y��zC���

���
�����������$��&��*��,
�<q	����G��I��J��K�aK����R��S� �T�#�X�%�^�f�&�i�(�o�~�*�C�+��,��-��.�/��0��1�
�.�2�9�4�;�5�>�6�8�D�9�E�:�F�<�K�=�R�Z�>�]�@�c�B�e�C�h�D�E�o�F�q�G�x�H�{�I�~�J�K	�C�L�
�q�a�M�*�O�,�P�-�Q�.�R�/�Sa�A�ia�s�U�V�W�X%�Y�[�\�]�^�_�`�)�a�d�e�i�j�k�p�q�r�t�u�v�w�x	�{�}�~��a�t�3�4�5�8�:
�K�V�X�[�b�c�d�&�j�k�m�r�s�t�u�w�z����
�
���
�/q	����:��;��>��?�a"���	�C�
�D�
C��I��K��N���U��V��\��^��a���h��j��l� �o�!�r
�~q	���"�	�$��%��&��'af���(��)��*��+�-��.��/� �(�0�+�2
�CC�1�N�2�Q�3�T�4�5�[�6�\�7�d�9�f�:�i�;�q�<�r�=�s�>�y�@�{�A�}�C�D��E��F��G���H��I�&
�6�J�A�K�D�L�G�M�N	�NC�O�X�Q�^�R�S�e�T�h�U�n�W�p�X�s�[�\�x�]�y�^�z�`�}�a�~�b��c�
�q	���d��f��g��h��ia�A�la���j�k�l�m�$�n�q�r�w�y�z�������	�	a���"�#�$�(�&�)�*�,�/�0�3�5�8�?�@�F
�V�a
�rq	���
�}�
�~�����q
������������
a�A����
��������!��"�!�#�$�,�$�/�&�5�'�8
�Dq
���(�O�*�Q�+�S�-�T�.�UA���/Q	���0��N��O��N��O��N��O��N��OaH���\�5�^�6�a�7�d
�l�8�w��9�
C�:��;��<��=�>��?��@�'
�7�A�B�C�D�D�G�E�F�N�G�O�H�P�I�V�f�J�i�K�p��L��M�
�N�
�O��P��Q��R�C�S� �T�#�U�&�V�-�W�1q	�a�X�7�Z�9�[�;�]�<�^a�A�ma�s�_�`�a	�b�c�e�f�h�i�j�(�n�p�q�r�s�t�w�x�z�{�|�}�~a�t�A�B�C�I
�Y�d�f�h�o�p�%�u�w�z����	����
�+�6
�Gq	����R��T��W��X�a�A�n��va�����	�C�
�����	��*���
�	���_� �!;�"�$�%�&�()�)�*�+�,�-�.�/i�0�2�3�5�6�7�8�9�:�;�<
�=�>�?�@�A�B�C�)�D�F�G�H�I�J�N�P�R�S�T�U�V	�Y�[�\�])�^�`�a�b�c�d�gC�h�j�k�l�m�n�o	�p�r�s�t�u�v	�y�z
�{�|�}������	����J�	�
���
��	���	���	�������	��
� �!�#�$�%�&�'I�(�*�+�,�-�.�/	�0�2�3�4�5�6�7�9$�;�=�>�?�@�A�B�)�C�E�F�Ka�!���\�]F�b�d�g�n�o�u���
� �+�-�0�8�<�B�I
�Q�\
�lD�w�z
��
�"�-�/�2�8�<�B
�S�^
�n�y�|���	C�
� �+�-�/�6�7�?�B�E	�L�V�Y�\�c�d�e�%�k�m�p�w�x�{�}���������%
�6�A�C�F�M�NF�S�U�X�_�`�a�g�i�l�s�t�z�}�	�
������ �&�)�5�9�?�P�SD�Z�]�`�g�h�i�o�r�u�}�~����
�����)�,�3
�C�N�P�S�Z�\�]E�c�e�h�o�p�q�w�y�|�����
��$�&�)�0�1�2�%�8�:�;q	�s�L�@�M�A�N�B�P�D�Qa���R�E�T�G�U�J�V�W�Q�X�R�\�W�^�]�e�_�i�o
�q	���`��b�
�c��d��ea ���f��g��h��jC�k��m��n�!�o�p�(�q�)�t�/�v�1�w�4�<�x�>�{�?�|�C
�Sq	���}�^��_��b��c�a,����h��i��j��p��s��	��
�C��$��'�
�*��2��3��4��:��=��@��H��I��Q��R��T�q	����Y� �Z�!�[�"�^�#a*���$�_�%�b�&�e�'�m�(�n�)�v
��*��+��,�
�C�-�*�.�-�/�0�0�7�1�9�2�A�3�D�4�G�5�N�6�P�X
�iq	���7�t�9�v�:�x�<�y�=a�A�wa���>�?�B�C�%�D�I�N�O�P�Q�R�S�U�V�W�X�Y�\�]�_
�`a���~����(���	�
�������� �"�$	�0�:
�Kq	���a�V�c�X�d�Z�f�[�ga����h�`�i�a�j�bC�l�g�n�i�o�k�q�r�r�s�s�v�y�x�{�y�~�z��{��~�
����
���&��'��*���1��2�	�3�
�9��<�H��K�
�R�b��eC��l��n��q��	�x����
������� ���%� �&�!�'�"�*�$�+�%�2�:�&�=�'�D
�T�(�_�*�a�+�d�l�,�pC�-�v�.�y�/�|�0��1��2��3��5��6��7�8��9��!
�2q	���:�=�<�?�=�B�>�C�?a���@�H�A�I�B�J��xq	�a�E�N�G�P�H�S�I�T�Ja�A�zac�s�K�L�O�P�R�S�T�U�V	�Y�[/�\�^�_�a�bI�e�f	�g�i�j�k)�n�p�q�r�s�t�	�u�v�w	�x�z�{�|�}�~�)��	�����-���	�
���
�)����������� �!�"	�%�'�(�)�*�+�,	�-�.�/�0�1	�2�7�9�;�<�=�@D�A�C�D�E�F�G	�J�L�M�N�O�P	�S�T�U
�Val�t�Y�Z�_�a�d�k�l
�r
��
��'�)�+�2�4D�:�J�N
�T�b�c�i
�z���
���
�+�6
�GD�R
�b�m�}���	�����
�-�8
�I�T
�eD�p���
�
�	��!
�2�=�?�B�I�J�K�%�Q�R�T�Y�Z�]�_�a�d�k�l�r�t�w�~����	������ �$�%�)E�*�,�/�6�7�=�?�B�I�J�P�S�V�^�bq	���W�h�Z�i�[�l�\�m�]a9���^�r�_�s�`�t�a�w�b�y�d�{�e�~��f�
�g��h��i��j��k�C�m�&�n�)�o�,�p�q�3�r�4�s�<�u�>�v�A�w�x�H�y�I�z�J�{�P�}�R�~�TA	�����2��3��2��3��2��3��2a�A�{a1�����
�	���
�	�������I�����	��� �"�#�$�%)�&�(�)�+�-�.�/�0�$�1�3�5�6�7�8�:�;�=�>�?�@�A�Ca2���_�`�p�s�z�|���
������E�$�*�1�3�4�:�<�?�E�F�G�M
�^�i�k�m�s�t�u�y�%�z�{�~�������� �1�5�;
�Lq	���D�W�F�Y�G�\�H�]�Ia���J�b�K�dC�M�j�N�m�O�p
�x�
���P�#
�/q	���Q�:�S�<�T�>�V�?�Wa8���X�D�Y�G�Z�M�\�O�[�]�^�^�e�u�_�xC�`��a��b��c�d	��e�
�&�f�1�h�3�i�6�j�k�=�l�@�m�F�V�n�Y�o�`
�p�p�{�q�~
�
C��&�r�*q	�a�s�0�u�2�v�5�w�6�xa�A�|a�s�z
�{�|�}�~��������)��	�����������a�t	�:�D�G�J
�Q�\�^�a�h�i�j�%�p�r�s�w�x�{�|�~���	q	��� ��"��#��$��%q	���&��'��(��)��*a"���+� �,�#�-�&�.	�.�/�8�1�:�3�;�7�8�@�>�A�?�BC�@�C�B�E�C�H�D�E�O�F�P�G�Qq	�a�H�W�K�X�L�[�M�\�Oa�s�P�`�U�X�c�Y�f�Z�i�[�\�p�]�qq	���_�x�a�z�b�}�e�~�fav���h��i��j��k��l��n�	�o��p�q��r��u��w
��x�'�y�)�z�*C�{�0�}�2�~�5���<��=��>��D��F��I���P�	�Q�
�R��X�
�Z��]���b��c��d��j��l��o���v��x��~��� ��!�"�
�#�
�$��&��'��(�)��*� �+�!C�,�'�.�)�/�,�0�1�3�2�4�3�5�4�6q	�a�5�:�7�<�8�?�9�D�:a�A�}a�s�;�<�=�D�E�F�G�H�I�K�L�O�Q�R�Va
�t�E�F�G�J�M�T�V�^�`�c�g�h�kq	�a�W�l�Y�n�Z�o�]�p�^a�s�_�u�`�v�d�{�f�}�g��h�i��j�q	���m��o��p��q��ra
����s��t�#�u�&�v�)
�1q	���w�<�y�>�z�A�{�B�|a!���}�G�~�H��IC��N��P��S���Z��[��\�	�b�
�e��h��
�o��q��w
�q	�����������aa�����������"��(� �0�!�1�"�2�#�8�H�$�KC�&�Q�'�T�(�W�)�*�^�+�_�,�`�-�f�/�h�t�0�x�1�~�3��4��7�8��9�	�:�
�;�� �<�#�=�*
�:�>�E�@�G�A�J�B�C�Q�D�RC�E�Z�F�]�G�`�H
�g�I�r�K�t�L�w�M�N�~�O��P��
�q	���Q�"�S�$�T�&�V�'�Wa	���X�,�Y�-�Z�.�\�0��~q�a�^�1�`�3�a�4�b�5�c�7�da�A�a=�s�e�f	�i�k�l�m�n�o	�r�s�t)�u�w�x�y�z�{I�~�	��*�������	�
��
�
��J�����#�	������	��)�!_�"�#�$�%�&)�'�)�*�+�,�-�/aH�t�;�<�B�D�G�N�O�U�X�d�h�n
��
����D��-�1�7�G�J�Q
�b�m�o�r�y�z�{�|������(�+D�2�5�8	�?�I�Z�^�d�f�i�p�s�y�	��
�#�.
�>D�I
�Y�d�s�u�v�|
�
����$�'q	�a�0�+�2�-�3�.�4�/�5a�s�6�6�7�7�;�<�=�>�>�A�?�@�H�A�Iq	���D�O�E�R�F�S�G�T�Ha�A���a�4���I)�J�L�M�N�O�PI�S�U�V�W�X�Y�Z	�[�\�]
�^�_�`	�a�c�d�e�f�g�h	�i�j�k
�li�m�n�o�p�q�r�s	�t�u�v�x�y�z�{�|�}	�~�
���������I�	�
���
	�������)��������)� �"�#�$�%�&�'	�(�*�+�,�-�.	�1�3)�4�6�7�9�:�;I�>�@�A�B�C�D�E	�F�G�H�I�J�K	�N�O�Q�R�S�T�U�V	�W�Y�Z�[�\	�]�^I�_�`�a�b�d�e�f�g�h�i	�j�k�l
�m	�n�o	�p�r�s�t�u�v�wI�x�y�z�{�|�~����)������	�
�)��
����	������	��a�:���[�_�e
�v����
�E���� �!�"�(�+�7�:�A�D�P�T�Z�\�_�f�g�h�n�q�t�|��
�D�"�%�(�/�0�1�7
�F�Q�S�V�]�^�_�e�u�x��
�����&�'�(D�.�1�4�;�?�E�G�J�Q�R�S�Y
�j�u�w�y����%��	������� �'�(�.�=�A�G
�X�c�e�g�n�oE�u�w�z��������	����������
��-��8��:��=��D��E��F��L��N��Q��X��\��b��r��vD��|��
����
��&��1��3��6��=��>��?��E��H��K��S��W��]��m��q��w��y��|������D������
����#��%��(��/��2��8
��I��T��V��Y��`��a��b�%��h��j��m��s��t��z��|��������
����!��'
��8q	��� ��C�"��E�#��H�$��I�%aj���&��N�'��OC�*��U�,��W�-��Z�.�/��a�0��b�1��c�2��i�3��l��x�4��{�5���6��
���7���9���:��!�;�<��(�=��)�>��*�?��.�@��/�A��2
��>�B��I
��YC�C��d�D��g��s�E��u�F��v�G��|
���H���J���K���L�M��#�N��$�O��%�P��+��;�Q��>�R��E
��U�S��`�U��b�V��e�W�X��l�Y��oC�Z��u�[��x�\��{�]���^��q	�a�_���a���b���d���ea�A�a�s�f�g�h�i�j�k�l
�m�o�p�q�r�s�ta�t��������"��%	��,��6��8��;��B��C��Dq	�a�u��J�x��K�y��N�z��O�{ad�s�|��T�}��U���X���Z���\���_����f���g�
��m
��|��
�����#���%���(����/���0C���6���8���;����B���C���D���J���M���P� �!��W�"��Y�$��_�%��f�&��n�'��o�(��w�*��y�+��|�,�-���.���/���0�����1����%
��6C�2��A�3��D
��P�4��[�5��^�6��a�7��i�8��jq	΁�9��r�;��t�<��v�>��w�?a�A�aΓ�@�A�B	�C�D)�E�F�G�H�IK�J�K�L�M�N�O	�P�Q�R�S�T�U+�V�X�Y�Z�[�\�]a Δ��|��}��~��������
��/��:��=��@��G��ID��Q��T��W��^��`��a��g��j��m��t��u��}
����������%��&��'q	�a�^��-�`��/�a��2�b��6�ca�s�d��8�e��9�f��:�g��@�i��B�j��E�k�l��L�m��M�p��S��c�q��g��m
��~q	���r��	�t���u���v���wa&���x���y���z��C�{���}���~��!����(���)���*���0���2���5����<�	��=���C�
��F���I�	��Qq	ѡ���[���]���`���d�a2ѳ���f���g���h���k���m���o���r��z���|���}� ���"�����#���$��C�%���'���(���)��'�*��(�+��)�,��/�-��2�.��5�/��=�0��>q	ҁ�1��F�3��H�4��K�5��O�6aVҕ�7�8	�9�:�;�<�=	�>�@�A
�B	�C�E�F�G�H�I�JI�K�M�N�O�P�Q	�R�T�U�Y�Z�[�\�]�^�_�`�a�b�c	�d�f�g�h�i�j�k�yMeT�3KM��V4)�2	�C	�M�W
�o	�}C�LM�K����M�'2M�ub�C=M�&�t �| ��?M�l>�:��y�^�~�2M�%=�s��1�I2�_�Z@�ki�,�>�a���
��\a+ғ��Q��S��T��Z��]��`��g��k��q��s��v��~������
��
������E������!��(��*��+��1��3��4��:��;��<��?��A��D��G��N��Q��W��Y��\��c��d��eq	�1��j��j�/�|�F�#�Rq�B�c��<�&��(��*�(��"�+�#�,�'�-�*�.�,�/�/�2�3�5�6�9q"�1�6�:�7�<�C�:�D�;�H
�?��J�!�%�M�&�,�N�-�0�7�O�=�P�A�S�C�F�T�T�V�[�`�Ua	��X�|��Y�c�Z2D�>�^�q	���(��(��+�#�`O�%a�1�u�G��L � 9�A�0�{�2M�-m�{2M�i3�7��k�:�	2�#H�'�p�W.��u�3	�Q2M�[a���a�5�^�>�7q��)�P�6�w�7�+,�I�r�v�m�|a�1M��`�q�b�T>�{�E�:2M�Kc��}3��.D�
a��aa�1�Jq��R�r�o�I�q�K�w�M�x�O��S��T�	�U�
�W"�
�Z�?�0�\q+�1�_�1�a�3�b�5�e�:�g�;�j�<�k�?�o�B�@�I�q�J�A�L�t�S�u�V�v�X�B�Z�w�\�x�]�z�y�{�_�|�`�~�/�aq.���c��T�d��C�F�f��g��l��p�	��r�G�
�s�
�u���g����������
�
��������%�/a�1E�6�G�|q���I�T��K��(�	�L�
�M��P��Q��S)�5�4�_a�1�f�k"��a�)q ��2�W�7�c�9�e�=�f�>�h�A�j�C�l�X�D�n�S�p�T�q�s�W�t�Z�w�^�x�0�`�{%�eq!�1�����
��>��[��������� ���#�\	�%��/� �6�"�8�$�:�%�<q	��B�'�J�*�K�-I�O�g�a��_a�M�cN�a�19�)�^�cm�v2M�dm�22� 0�<
�m�xL�2�Y�TE�_�m�%2M�q���a�7�f�c�g���*	��t*�a�2�fC�<�B�ga���`a�19�J���k�2�B��Gm�O2�=D�Em�
PK
!<VK�Ǧ�0chrome/pdfjs/content/web/cmaps/KSC-Johab-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�KSC-Johab-Ha�2�x�z��{�~�-��5��
������-�PK
!<ul�C��*chrome/pdfjs/content/web/cmaps/KSC-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�KSC-Ha!"�x�z��{�~�-��5��
�������PK
!<�Q�
�
0chrome/pdfjs/content/web/cmaps/KSCms-UHC-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE��A��= ` ^a?��]e�"D�C�;]��"2�f)��"	�C	�M�W�o�(C��<N�K�1���M�'�"]�u�"R�S�-U�&�* �| ��/]�>�"]��"]�z�"]�X�"]�6�"]��"]�r�"]�P�"]�.�"]��"]�j�"]�H�"]�&�"]��"]�b�"]�@�"]��"]�|�"]�Z�"]�8�"]��"]�t�"]�R�"]�0�"]��"]�l�".�J��y�^�~��"]�%�"-���1�I�"�_�ZP�k�"Y�<�>�a���
a���\a�A�u�}�)a]�A�'�A}�[B�Y�s}�
B��%}�?B�=�W}�qB�o�	}�#B�!�;}�UB�S�m}�B��}�9B�7�Q}�kB�i�}�B��5}�OB�M�g}�B��}�3B�1�K}�eB�c�}}�B��/}�IB�G�a}�{B�y�}�-B�+�E}�_B�]�w}�B��)}�CB�A�[}�uB�s�
}�'B�%�?}�YB�W�q}�B�	�#}�=B�;�U}�oB�m�}�!B��9}�SB�Q�k}�B��}�7ap�A�5�O�i� �	�#�=� �]�w�� �1�K�e� ���9� �Y�s�
� �-�G�a� ���5� �U�o�	� �)�C�]� �}��1� �Q�k�� �%�?�Y� �y��-� �M�g�� �!�;�U� �u����)� ��I��c��}� ����7��Q� ��q����%� ��E��_��y� ����3��M� ��m����!� ��A��[��u� ����/��I� ��i����� ��=��W��q� ����+��E� ��e����� ��9��S��m� ��
��'��A� ��a��{��� ��5��O��i� ��	��#��=� ��]��w��� ��1��K��e� ������9� ��Yq	С��j��j�/�|�F�# �Rqѡ2�s��<�&��(��*�(��"�+�#�,�'�-�*�.�,�/�/�2�3�5�6�9q'ҡ�6�:�7�<�C�:�D�;�H
�?��J�!�%�M�&�,�N�-�0�7�O�=�P�A�S�C�F�T�T�V�[�`�U�|��Y��Za>ӡ]�`�"D�>�^��(��"��(��+�#�`O�%�"�u�G��L0� �")�Q�0�{��"]�-�"]��"]�i�"#�G��k�:�	�"�#X�'�"��W.��u�3	�Q�"Z�[�P�6�w�"�7,�I�r�v�m�|�"O��q�b�"�o�T>�{�E�:�"]�K�"S�)�}�#��.T�
a���aܥ�^�N�7�>�+�A�a�+�Jq��b�r�o�I�q�K�w�M�x�O��S��T�	�U�
�W"�
�Z�?�0�\q6��_�1�a�3�b�5�e�:�g�;�j�<�k�?�o�B�@�I�q�J�A�L�t�S�u�V�v�X�B�Z�w�\�x�]�z�y�{�_�|�`�~�/�a��c��T�d��C�F�f��gq#���l��p�	��r�G�
�s�
�u���g����������
�
��������%�/q
�E�6�G�|�I�T��K��(�	�L�
�Mq	���P��Q��S)�5�4�_q��f�k"��a
�)�W�7�c�9�e�=�fq��>�h�A�j�C�l�X�D�n�S�p�T�q�s�W�t�Z�w�^�x�0�`�{%�eq%������
��>��[��������� ���#�\	�%��/� �6�"�8�$�:�%
�<�'�J�*�Ka	��-�
�_�B�7�C��*
�t�N�fC�<�B�ga���g�N�c<��u�`a'�I�O��"9�)�^"�c�"]��"]�d�"]�B�"� ;�<�x�"�|L��"�Y�TE�_��%�"]�5�"R��f�c�g�"�o�	�*��"9�J��
��"]�&�"�B��G�"]�_�"�=T�E�"]�PK
!<�.d�
�
3chrome/pdfjs/content/web/cmaps/KSCms-UHC-HW-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE��A��= �` ^�a?��]e�"D�C�;]��"2�f)��"	�C	�M�W�o�(C��<N�K�1���M�'�"]�u�"R�S�-U�&�* �| ��/]�>�"]��"]�z�"]�X�"]�6�"]��"]�r�"]�P�"]�.�"]��"]�j�"]�H�"]�&�"]��"]�b�"]�@�"]��"]�|�"]�Z�"]�8�"]��"]�t�"]�R�"]�0�"]��"]�l�".�J��y�^�~��"]�%�"-���1�I�"�_�ZP�k�"Y�<�>�a���
a���\a�A�u�}�)a]�A�'�A}�[B�Y�s}�
B��%}�?B�=�W}�qB�o�	}�#B�!�;}�UB�S�m}�B��}�9B�7�Q}�kB�i�}�B��5}�OB�M�g}�B��}�3B�1�K}�eB�c�}}�B��/}�IB�G�a}�{B�y�}�-B�+�E}�_B�]�w}�B��)}�CB�A�[}�uB�s�
}�'B�%�?}�YB�W�q}�B�	�#}�=B�;�U}�oB�m�}�!B��9}�SB�Q�k}�B��}�7ap�A�5�O�i� �	�#�=� �]�w�� �1�K�e� ���9� �Y�s�
� �-�G�a� ���5� �U�o�	� �)�C�]� �}��1� �Q�k�� �%�?�Y� �y��-� �M�g�� �!�;�U� �u����)� ��I��c��}� ����7��Q� ��q����%� ��E��_��y� ����3��M� ��m����!� ��A��[��u� ����/��I� ��i����� ��=��W��q� ����+��E� ��e����� ��9��S��m� ��
��'��A� ��a��{��� ��5��O��i� ��	��#��=� ��]��w��� ��1��K��e� ������9� ��Yq	С��j��j�/�|�F�# �Rqѡ2�s��<�&��(��*�(��"�+�#�,�'�-�*�.�,�/�/�2�3�5�6�9q'ҡ�6�:�7�<�C�:�D�;�H
�?��J�!�%�M�&�,�N�-�0�7�O�=�P�A�S�C�F�T�T�V�[�`�U�|��Y��Za>ӡ]�`�"D�>�^��(��"��(��+�#�`O�%�"�u�G��L0� �")�Q�0�{��"]�-�"]��"]�i�"#�G��k�:�	�"�#X�'�"��W.��u�3	�Q�"Z�[�P�6�w�"�7,�I�r�v�m�|�"O��q�b�"�o�T>�{�E�:�"]�K�"S�)�}�#��.T�
a���aܥ�^�N�7�>�+�A�a�+�Jq��b�r�o�I�q�K�w�M�x�O��S��T�	�U�
�W"�
�Z�?�0�\q6��_�1�a�3�b�5�e�:�g�;�j�<�k�?�o�B�@�I�q�J�A�L�t�S�u�V�v�X�B�Z�w�\�x�]�z�y�{�_�|�`�~�/�a��c��T�d��C�F�f��gq#���l��p�	��r�G�
�s�
�u���g����������
�
��������%�/q
�E�6�G�|�I�T��K��(�	�L�
�Mq	���P��Q��S)�5�4�_q��f�k"��a
�)�W�7�c�9�e�=�fq��>�h�A�j�C�l�X�D�n�S�p�T�q�s�W�t�Z�w�^�x�0�`�{%�eq%������
��>��[��������� ���#�\	�%��/� �6�"�8�$�:�%
�<�'�J�*�Ka	��-�
�_�B�7�C��*
�t�N�fC�<�B�ga���g�N�c<��u�`a'�I�O��"9�)�^"�c�"]��"]�d�"]�B�"� ;�<�x�"�|L��"�Y�TE�_��%�"]�5�"R��f�c�g�"�o�	�*��"9�J��
��"]�&�"�B��G�"]�_�"�=T�E�"]�PK
!<�*|���3chrome/pdfjs/content/web/cmaps/KSCms-UHC-HW-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�KSCms-UHC-HW-Ha���x�z��{�~�-��5��
�������PK
!<;"oۦ�0chrome/pdfjs/content/web/cmaps/KSCms-UHC-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�KSCms-UHC-Ha���x�z��{�~�-��5��
�������PK
!<m�7���0chrome/pdfjs/content/web/cmaps/KSCpc-EUC-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE��A��=� ` ^`a@��eZh�"D�C�;]��"2�f)��"	�C	�M�W�o�(C��<N�K�1���M�'�"]�u�"R�S�-U�&�* �| ��/]�>�"]��"]�z�"]�X�"]�6�"]��"]�r�"]�P�"]�.�"]��"]�j�"]�H�"]�&�"]��"]�b�"]�@�"]��"]�|�"]�Z�"]�8�"]��"]�t�"]�R�"]�0�"]��"]�l�".�J��y�^�~��"]�%�"-���1�I�"�_�ZP�k�"Y�<�>�a���
a(�A<��<�V�[��]��iB<��?�!<�^��!<�:
�wN��D<�	�H�R�Y�_E�aB<�{�8P�WB<�f�#�!3�B�L<�v�3T
�RB<�]�W�9�rC<�=�z*�!� J<�%#�b�7�\q	С��j��j�/�|�F�# �Rqѡ2�s��<�&��(��*�(��"�+�#�,�'�-�*�.�,�/�/�2�3�5�6�9q'ҡ�6�:�7�<�C�:�D�;�H
�?��J�!�%�M�&�,�N�-�0�7�O�=�P�A�S�C�F�T�T�V�[�`�U�|��Y��Za>ӡ]�`�"D�>�^��(��"��(��+�#�`O�%�"�u�G��L0� �")�Q�0�{��"]�-�"]��"]�i�"#�G��k�:�	�"�#X�'�"��W.��u�3	�Q�"Z�[�P�6�w�"�7,�I�r�v�m�|�"O��q�b�"�o�T>�{�E�:�"]�K�"S�)�}�#��.T�
a���aܥ�^�N�7�>�+�A�a�+�Jq��b�r�o�I�q�K�w�M�x�O��S��T�	�U�
�W"�
�Z�?�0�\q6��_�1�a�3�b�5�e�:�g�;�j�<�k�?�o�B�@�I�q�J�A�L�t�S�u�V�v�X�B�Z�w�\�x�]�z�y�{�_�|�`�~�/�a��c��T�d��C�F�f��gq#���l��p�	��r�G�
�s�
�u���g����������
�
��������%�/q
�E�6�G�|�I�T��K��(�	�L�
�Mq	���P��Q��S)�5�4�_q��f�k"��a
�)�W�7�c�9�e�=�fq��>�h�A�j�C�l�X�D�n�S�p�T�q�s�W�t�Z�w�^�x�0�`�{%�eq%������
��>��[��������� ���#�\	�%��/� �6�"�8�$�:�%
�<�'�J�*�Ka	��-�
�_�B�7�C��*
�t�N�fC�<�B�ga���g�N�c<��u�`a'�I�O��"9�)�^"�c�"]��"]�d�"]�B�"� ;�<�x�"�|L��"�Y�TE�_��%�"]�5�"R��f�c�g�"�o�	�*��"9�J��
��"]�&�"�B��G�"]�_�"�=T�E�"]�`�cPK
!<{�s��0chrome/pdfjs/content/web/cmaps/KSCpc-EUC-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�KSCpc-EUC-Ha���x�z��{�~�-��5��
�������PK
!<����dd-chrome/pdfjs/content/web/cmaps/Katakana.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�` ?�F�PK
!<ٚ�  &chrome/pdfjs/content/web/cmaps/LICENSE%%Copyright: -----------------------------------------------------------
%%Copyright: Copyright 1990-2009 Adobe Systems Incorporated.
%%Copyright: All rights reserved.
%%Copyright:
%%Copyright: Redistribution and use in source and binary forms, with or
%%Copyright: without modification, are permitted provided that the
%%Copyright: following conditions are met:
%%Copyright:
%%Copyright: Redistributions of source code must retain the above
%%Copyright: copyright notice, this list of conditions and the following
%%Copyright: disclaimer.
%%Copyright:
%%Copyright: Redistributions in binary form must reproduce the above
%%Copyright: copyright notice, this list of conditions and the following
%%Copyright: disclaimer in the documentation and/or other materials
%%Copyright: provided with the distribution. 
%%Copyright:
%%Copyright: Neither the name of Adobe Systems Incorporated nor the names
%%Copyright: of its contributors may be used to endorse or promote
%%Copyright: products derived from this software without specific prior
%%Copyright: written permission. 
%%Copyright:
%%Copyright: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
%%Copyright: CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
%%Copyright: INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
%%Copyright: MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
%%Copyright: DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
%%Copyright: CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
%%Copyright: SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
%%Copyright: NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
%%Copyright: LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
%%Copyright: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
%%Copyright: CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
%%Copyright: OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
%%Copyright: SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
%%Copyright: -----------------------------------------------------------
PK
!<�^��
�
*chrome/pdfjs/content/web/cmaps/NWP-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE!!��]a)!>�h��(�">�G��"P�&�'�wa!!G�y�B�"
�W�e�m�t���1	���0�&R�J�-U��*�s��H �# �D�~�z�y��a!i�6�:K�7�1��!	�8�H�Oq!.!��!��H�K�M��"�N�4�|�5�}�	���;�9�<��O��&�G�'��
��0�(��)�	a/!�{�.Q/$�z�^�#rI�W�a/5�&Q/7�)q/E�F�N�Q�T�W�Z�`�b�d�k�o��l�j�qQ/e�p�a/u�L�O�R�U�Xq
0!�e�Q�g�"�x�R1��S�3�T�8�U�Aq
1!�C�V	�M�W�X�X�\�Y�`�['�e�\�q2!�!�]�I�^4�+�_�a�`�{�a�~q3!��b��a�!��(�c�D�d�J�e�R�1�U�f�Yq
4! �]�g�r��h�
� ��i�"a5!	�;�j�F�k=�[q6!��l��m�#�2�?�n�H�o�f�p�m�q�t�r�vq7!�w�s�{�t��u��v�%�w�)�x�,�
�2�y�;�z�H�{�P�|a8!�U�}�i�~<�vq9!	�3�
�>�	�J��U�]�o������a:!1���D��X�	�eq	;!�o��v�	�y�
�{�D�q
<!�M��e�
�)�v����q	=!	�+�	�6�4�A��x�
�~q
>!�	���
���(��>��H��Mq	?!�g�-�q�� ��'� �0q@!�E�!�G�""�g�#	��$��&�qA!�#�(�n�+�)�<�*�O�+�R�,�b�-�lqB!��.��/
�.�0�=�1
�D�2�P�3�W�5�^qC!�_�6�f�7�i�9
�m�;1�|�<�/�=�4�>�;q
D!�=�?	�Z�@�e�A�g�B�j�C
�l�5"�xqE!��D��E�"�F�)�G�1�H�:�I�C�J�F�K�M�_�R�M�_�P�i�6�n�N�sqF!�y�O��P��Q�.�S�5�U�:�V�@�X
�C�m�Oq
G!�W�Y�`�Z�p�[��\�	�]� �^�$q
H!�5�_�9�`�C�a"�E�b�i�c�w�d�
qI!��e
��g�;�#�h�%�i	�(�j�3�k�A�l,�Dq	J!!�q�m��n��o�+�p�Jq
K!�O�q�Q�r �X�s�z�3��t��/�(qL!+�-�u
�Z�v�f�w�l�x�o�5�y��z�	�{qM!��|��}�;�~�?�4
�E��Ta
N!)�i-��D�#�H�X�]�`�M4�z�''�0aO6�waNK�.��%���q	Q!�X��u�����	0�aR!]�6qS!��
�$��.��D�
�R��_aYx�'aT!"�r��w�6�"�P$�m��"�.N�0��"]��"�jY�n�">�H
�
�� �"�&�?0�Sa
TD��x�%��>�O��1��;���L��q
[!#���)��.�"�<��O�h�Q�	�Xa^9�m;�a\!]�b�"]�@�"��7#�N�s�"Q�|
�O�"�Z7�`��0�"	�8�CL�Ia^P��"��2�8� �5�4�!�"q	b!	��$;�!�n�^�Q�`�%�eac!(�t�&��'�(�T�2q	d!�R�(�k�\�x�)��*� ae!�0�+�I�,	�K�-7�V�"$��.7�4�"B�l�/�0�0�5�1�>�"�J�2�^�37�e�4	�q	i!�(�u
�*��6�5�D� #�bq	j!���
�6�
�70�#�8�Uq	k!�d�9)�v��!�:�*�;�:alM�*al!+�B�o	�	��",� �N�l�n�"�~��&�5�"C�\�!a	li�<
�=�Y�>�?�@�:�A�B�C�
�Dqp!�:�E�M�F
�_�G�k�H�q�1�I�aq!]�q
r!�v�J
��K��w�L$�$�)	�Jas!/�T�M*��N�1PK
!<j�B���*chrome/pdfjs/content/web/cmaps/NWP-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�NWP-Ha!"�O�L�R�P�O�S�V�Y�T�W�V�[A!k�M�@�r�A�2a%u��I���?�P�g�O�x�~�}�S-�P�OPK
!<��CA+chrome/pdfjs/content/web/cmaps/RKSJ-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE��@�<�?�@�< �g` ^�ga�@>�y,�8�e�m�t��R	���0R�JN>��\�s�i �#�D�SQ ���7	w	WE$$a��]�eC>�C|�a*�@>�|�>C>�;|�zC>�w|�6C>�3|�rC>�o|�.C>�+|�jC>�g|�&C>�#|�bC>�_|�C>�|�ZC>�W|�C>�|�RC>�O|�C>�|�JC2�G,]�zC>�X|�C>�|�SC>�P|�C>�|�KC>�H|�C>�|�Ca�@>�@|�`�?�Fa�@>�||�;a�@>�8|�wC>�t|�3C>�0|�oC>�l|�+C>�(|�gC>�d|�#C>� |�_C>�\|�C>�|�WC>�T"�a��\PK
!<L3���+chrome/pdfjs/content/web/cmaps/RKSJ-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�RKSJ-Ha�A�O
�Q	�S�V�[A���m�S a���PK
!<��,�``*chrome/pdfjs/content/web/cmaps/Roman.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�` ^�gPK
!<i�阼��2chrome/pdfjs/content/web/cmaps/UniCNS-UCS2-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE����!a ^#�A���%�J�	�C�<�O�H
A��3�Z��[AA3���d
.%
.%U "!":a*.)aA %*s;<

	a��!�2�9�J�Iy{�&�(a�p�j�q��a���n��
��p��A
 "h|"�D���A!�f
A ���Aa!`	�W&�x�u�w�v�y�|�{��U�\�i�h�~�}�e�m�n�p�o�d�]
�[�^�Y=�g�ja!p	���2�R>	�z
	�a!��--�,A%�7	Q%P�B��%%%%%��i��%��[��%%%��e��%a%m�>�A�@�J
�"A%��>
A	%��0A%��a%��FA%�I�t6A'=�@A.���H�&a.����� ��!A.��\A.Ɗ	A.���#a.��vO�'�Lce�3��������*�a�[$�Q�y�A�j��a0�:9R�A	�7U��=�5�2�ea.���.��0��1��3�<��FA3��"		OFAN�SA�W45�=
�R#�E�~�Z�T��D���F�!�
�VL�\
��S�6�Z�}�b	�h�M�sz�)�l�
$�6	��<�;	���A�^�e��\�Z��x�u�>�A� �9��t�0�-�u�@�p�5�I6��U�D�u��o��T��_�|�V��=���s3�D�`�;�
�|�g�m"�g�F�CB�P�
� 0?�_	�f��y�p�J7�|�'�(hD��,�O�|�c�	�r�
��5�{�(�,C�E2�t�%�n�Q�"�A��
	� �<�/��)����o�Q>�Nx�7�:��,�'�I	���5�,�1
�\�w�"
��e�0�
�-�&�k�y�%�:�M���
B�(��]�;3�B��1���~��G�X��0�b�;�!�j���u�d��g�.�j�YQ"�>	�G\�P�9<	�
R�c
�Z�B��S�=�8�)�L�0�w	�#9�o�h�<J��~�y	�t.�!)�FJ�t�U�F7�#�h�n.�S��BC�k�+w�	$�d�k��1�5��[�
���X�=���E�z�y
H�%	��6�	���?
�`���g
�p�c�<%�ZEM��.N�C�2$�7�Y�&5�h-�_)�*�s�^(DQ��)�
4�R�p�N��{�{��f���L�)���6=/0
�Px�t�r�s�-7�l7�'�5� �x�'�P�)��B%��C�<	�W,�^�G!�g0�I�6)�"�n�+�#�E\�~A4���{|)�;A�4L��0�&��+%ef+�Z�o�_�?
X�v�/�&�Q�]2�e#�6�s�O:�#,�>�#'�=w|B�&�$�#6
;.)��
GK%2�P�M&�p�o�
��wa�X#B�B�%#
��}�,��,�	�bA�`�. 
D(�gH�lK�#�b
�-�0K�!M�6](�?!�>
��Y�O(�`)�M+�^a
 R�{�6&�*�%��T�K6�Pu�vb���M<�*<=�!<� �.4�5C�@,� �.�6�h(�
�N�grQN�q���=�J�KAN�r	��;aN�nAN&�(�!�$R�|�y�$T�8�5x,P�8AN.�{$�J�KGAN!�m�>�m�6
F>��<aNR�pA
NV�)��;0	�	�n
� �AN\�m"�ANZ�1�f�
�/�~�%aN��aAN��z�� �#.P�,�p�j��,V�(�@AN��tAN����1� aN��.�$AN��2�/�

AN��c�MAN���<��\�k�<a	N��#�&�%�z�y����~aN���~����aN��bAN�x�.��p�u�7Q	N��l�u�~�o�t��`A	N��AO�;AO�HaO�}AO�{�dA	O�E	�nAO��<
��Q
O6�.��F��]������{�AOC�%.5*+AOA�-�2AOB�K��7aOP� QOR�+	����&�+
+� ���y�|AOg�*�[��3�2�?QOr�r�����~��n�q��'����}���n��}AO��2�%AO���$

AO��G�l�9�1�QOǴg�����Y�X�^�k�CAOӴ\QOִh�k�d�W�Z	�k	A
O�f�-�I�A-��B��;�0aO��(AO��9(AP�AO��3�d�^�eaP�3AP�1�KQP�A�<�7���\�A�DAP��=�Z�K�0�WQ
P%�8�j�Q&�,�5��|�;AP3�%�T�E�S��QPE�F
��~�y�~�w�|�uAPQ�Q-�i��H��9�Q	PZ��~�	�|	aPc�JAPe���Q	Ph�8�a#"����~��PAPt�	�\		�:
APr�> �V

�f
AP��N��a�[aP��AAP��@

�|
�zAP��j	�T�VAP��E�i�x�D�i�f�QP������u��\��W�l�yaQ����AQ�
�RAQ��zAQ
�F�Z�
aQ-����7�u�z�hAQ?�\.j�2��V��jAQ;�<AQ9�F��QQY��0�1�h�g�B�x��;�X�YAQe�]��@�t��h��=�aQm�,AQq�
�P�$��kAQ���RAQt��?�c� �SaQ��AQ���^�$�e� ��*AQ��3��g�AQ��#�8aQ��SAQ���L�AQ���jAQ��&�	�Z�+�*aQ��7��I�4��K��[�6QQƐX��"��e��$��-�9�D��$��u��t�A�B�#A
Qܢ�
�e�F�>�2�D�}�0AQ��u�X�DAQہ���{�\�[�FaQ���L�`�u�1�
��aR	���HaQ����
�3��!��YAR�	�t�#AR�G�ZAR�`��-�aR)�;�R�Q��AR6�S	�X	�6AR5��2�/�.�	AR4�{�>�saR^�=ARi�u�G�<ARa�:
�$AR`�)�^
�-aRt�s�K�L�J�
���aRx�w�v�x�#�v�	aRy��#AR��b�R�AR����jAR��?�:��E���0�2�;�:aR��?�>�A�8AR��V�h��n�s�D�,AR���,
� �,AR���\��N�?�p�yaR��PAR�� �*zAR�O	���F� �nAR��%�J�h�xaR��5�y�
��:�AS
�K��k�&�.����T�8�d�v�[�,�HA
S�B�z�!Pd
���<��Y�*AS'���Q�2�8�,aS?�ASA�d$���2��f�X�)�,�0��s�hASL�d�\�sASB��G��	�5�D�{�XaSp��C�D�\�M�`�=aSr�<	�r�p�M�=aSt��{����}AS��N�&AS���,�H�/AS����3QS��A�����s��v�B�5�|�;�3��lAS���_�>�W�
�q"��>�Z�&�J�!�@AS��~,�oAS���"�/�4�5�-�����o�/�2QS�	A
S��"��f���O��@��C
	QT�(�J�S	
��\�CAT�Q
T�G��>��'�f�.��PQT#��#��/
�{�~�s�\�G5 ��(��[�E��.��OaT5�XAT8�V
'&AT7�@	ATK�QUQT`�.�u�x�{��z�_��?��hQ
To�,�0�-�2�O��
	Q	Tz�%�{�^�q�pAT��j��\��[�bQT��u	�r�i��R��g�d�m��v���e��J��7AT��r��f�QT����@�
��Q�I% !�P
�A�V�U��D��[��"QT���S��(��g�+�F�Y�Z�G)�D�?aT��ATǍX
ATϴ{AT��#��Q�T	�nQT�W�O�(��v��o�C�&�I�6AT�o��r��5�Q�<aT��UAT��l�N�Y�(QU�f��J��g�M�.�;�B���7�A'�T��B��yA	U�F��L��E��8�
�K�����rQU*�a��p��S�z��r�}��o�X�{��|AU;�p'�#��8��q�{Q	UC�&������y��r���AUO�*�.�/AUM�^	AUS��.	���c�.��0aUu�[AUw�G��*Q
U{�{�&���3�"�
�$QU���.��Q��q)��f�_����
�� AU��&QU��W
�/��T��W	�j�yAU��		
AU��M	�n	-AU��u��V�C�
�aU��`QUǛY�d�S�6"�o�`�G�V�c�X��N���9�c�8�G�N�&��AU�_�TaU��[AU�b�o�h�AU��AU�|�U�j�Q
U��7	�d�s�3	�J�K�<AV�5���Y
�&aV
�Q	V�.�!��\�C�9�(�Q	V��8�f����@�^�
�>AV%�x��!����!aV/�Q
V2����n�e��0�7���AV?��pAV=�	
�
�

�rAVC�~	�z�"��'�m�OQ
Vh�	�Z	�W�P�U�`	�]AVv�"�\�o��Z�bjQ
V~�G�����AV���(�?�aV��BAV��9��VAV��A�"AV��&�9�D�GaV��AV��w�Y�AV��		�AV���w��^aV��JAVچ-�~�p	�*�ZAV��k� nP�*�4��HAV��]�)�^����
�		�7�:a
W�[�y�t�6�8�s�7�Y��dAW�zAW�!�FAW��H�/�f�^�{aW"�AW(�/	�	AW*�R�zAW2�y�6�"-Q
WI�T�_�^�W
�^AWa�}AWb�AAWT���@�I�x�)aWs�?AWw�z
�L�]�\
AWu�=�&


AWz�_�x�+��!�k�>�BaW��
A	W��i
��#�AW���2
�H�3A	W���d�^	�	���\�^�QWܼy��v���%������AW�QW������L�S�m�x�y	�j*��"QX�{$��� ��{�5���v�AX��z�)�J�
�|QX �������`�m
�AX,�m�%aX0�Q
X2�^���1�L��	AX=��q�4�pQ	XG�B�A�h�Q�PAXQ�g�@�O�ZQ	XW�i�f�e�f��T��]�k��pAXb�p	�fAXc� �:
AXi�e���\QXy�'�2�!
�4�;�6�EAX��(�~
�>�C
�~�8�A#X��<

!
�J	
�j�l�rA
X����T�1�V�g�6��%�:� aX��xAX�{�
�ZAX�z�&�"
�tAX�Y��
�p�C�]aX�� AX��"�q�@�:���@�"�g�>�.��Y�X�c�b�ZAY�AX���\�Z��	�/aY�4AY���N�]��RAY,�,AY��c�PaY7�6�T�p�X��E�A	YI��T�w�X�/�PaYW�AYZ��<�;�Z�v�t�zAY\��2	�8AYe��RQ
Yp��F�,��)�d��p��q�N�CQY{�X�7�:
�/�<AY��{
AY��]AY��0
�/�SQY��q��t��)	�S�8�]AY��QY�������m���>�;��
�x��
�AY��SQYòO��h��]���.��5�t�y�r�u	AY؍uAYֲP�4	#AYف�qQY�x�f�c��:���N%��.�
��!��>�Z��)�K�L�u�T�G����)AZ	�	AZ
�u
A	Z�	�#�K���M��caZ-�pA	Z3�w�H�-�l�E$�E��f�)QZ@�K	�Z!�\���D�EAZL�/QZP�(��@��W6!�8��M�4!�8��M��Z��1�/��:�
�O��n�C	�BAZp�0�Q	Zw�#�$�T�] ���YA	Z���&�a��B��IQ
Z��+�8�Z�|���D1����Q
Z��
��M�>+��~�-�QAZ��y��4�l��QZ��|�r�Y�n	�i�f�y��4�eQ
Z��.�o�v�_�z�&�XAZӁ�QZ��P�9�L
�O�J�A���o�=AZ��]�W�<
�0Q
Z��u�A�*�}��v��A
[�/�r�8�5�HA!Z��,�`�g
�V�&�+�d�a�>�c		�A[
���a[=�	A	[@���p��q��f�q�J�K�a[P���F�3�?�a[V�\a[K�|���1A[\�~�2�N� A[b�W�>�~A[f��N��}Q	[p�Q�0������w��$�W�kA[z�F��\�7�p�U�T�9A[{�XA[|���a[��AA
[��D�z�,A[��c�hA[���i�'a[���OA
[���(	A[��y�
�A[���{Q	[��V�F�I����#A[̘,A[���PA[Ձ��Ra	[��8�8�@�`�:�>�a�?�;A	[�9�h�0�1�n�{a[��YA[�����(�|A[��{�,�RA[�qta\
�/Q\��#�T�S���0��r���/A\� �9��*��E�X�*�&^�.�Y�$A\�:�O�NA\��j��>�<a\@�q\D�[�"�\�!���\�
�4�	���a\T�~��}��;�\�[�B�4�H�n�v�J��#�"�G�`a\c�b�7��a\S�
�gA\��A	\|�_�A\���!a\��	A\��l�I�H��6�wQ\���Z��a��~��
�u��w���t�	A\��*
�JA\��`�0

A\���+��Y�a\��AA\َ�.	
�p	
	A\ֵ@
�n�w�x
�p	A\偌7�+ �"�#�cQ
].�K�����4�U�J-$
A	]G�5���dA5]<�("),
�.�M
�<�[�T�P
�A]>��)��
�a�a�k��k�w�^�1�@Q]��,
��!	
	A]��LA]��5�Z�~��PA]���X�*�_a]��MA]ɬ�6�$j�#�@��4��/� �G�^�,�4�:A]��N�^�*A]ׁ�,a	]��	�K�a�
�I�6���9A
^�I��B�<A^�$��x�6A^	��@�m�Fa^!�JA^%��4����A
^$�N�p
�	�B�6�XA^.��5�T�ia^X�rQ	^[�E��J��e���eA^f�8a^g�6�D�]�M�\�A^o�9�&���Z��0�5Q	^x�1��3�H�^�(�R�'�xA^��A^��&|�"A^����a^��5A^��3�:�2A
^��R�f�uA^��Na	^��Q�O�s�r�p�P��=�t�=Q^Ę?����$�D�qQ^Ё�=�
�e�d�_�V
���!�w����!A^��>�{a^��OA^�)��K��
|�^�*��l�{�x�l�gA^�B�F�$�6A	^��P�^�\�&���a
_�N�?�>�g�M�z��s��0�8A_)�:�6�.��	�A	_(�r�F�l��x�ZA_:��NQ
_H�A��?�"�c��,��E�$��Y��|A_T�v�@�A�n�W��I�T��l�+�����f�h�`�&�K�F�G�J�t�wA_o�A_\�Z�n�ya	_��;�w��1��X����A_���&�	�A_��V�2A_���,�Ta_��BA_���z�i�~A	_��2�@�t��~�:�^A_���a_��QA_ÅP�^�2����`	�A�H�G�BA_ɯ'��&�'� �m�j�u
A_ā�j�A�HQ`	��M�?�
�]�X��tA`�~�m��a�JQ` �!�-�x��x��=��4�c�.�/	�p�A`5�G
�D	�	A`2��.�Z�[		�L�]�X�eA`1��a	�b�?�Q`b�(���
�j�L�U� ���f�e�'A
`��
���A`r�\
�p��{��s�r�
A`u�V�\�}�D�S�KQ`��\�=��H��I�t�<�'�>�/�#� �,Q
`ýj��[����C�Z���/A`јEa`��^A`ՕQ`ׁ�5��K�"�� �1�(�P��s�j�^�|�)A`�	��t�Q`�N�N�_�w�Z�U�~�)�g��b�L�M
�e�nQa�D�K��8��M�u�J�x��a�	�h�|Aa�W�r��~���x��z�Aa�?

�x�I�T�O�p�e�F�YAa�v��|�aaI�U�R����^�2�AaT���Q
aX�X��B��G�p�j��}�x�_��j��qaab�TAad��s�E��1�k��
�zQ
an�G��p��i�r
�i�V�eA
a|�K	�L�?�<�3	Aay�P��
�|�a	�Z�_�X�SAa}�=�r
�5��ZQa��O�f�'�T����~�G�`�L�2�[��
�[�d�kAa��a�[�,���o�Q	a��E�k�9�2�7�<�h�/A	aρ�:�s�1�$��n�yQ
a�E��k�(�#��	aa�� Aa��$�IQ
a���(�|�}�r�?�@�A�$f�<ab�Q�A�Q�P�S�"�!�Q�P��X�ab	�(
�� �aab��3A
b*�Z�z�<�:�v�w�	�t���4Ab#�c�d�Ab,��<
�E�1abB�lQ	bF�!�?�,�~�b�q��0��7�AbP�)�IabS�BAbX�V	�(AbY�r
		�LAbh�Qby�-�P�U�^�_�P�_�b��Z��q�W��X��5�8
�cAb��ab��Q	b���@��
�:�?Ab���[�.Q
b��c�t�s$�B�gAb���U�Z���q�b�{Qb‹Z��h��o��b��5
��q"�v��\�sab��_���nAb؋m
�$��V�@Qb�t�q���P�?��d��z�
��h��dA
c�8��%�z�X�Iac�{�~�1��A�S�/�QAc,�%��N��+Qc1�e�q�m
��H��]�t�S�;�j�-�=Ac@�<�M�<acD�&QcF�K�T�a�>�/
�RAcU�I�.&AcT�3,
�j"	"Acd�G�Q	cu�~�+��J��@��c�#Ac�K��3�*1��6Qc��z�
���0��[�\%���3Ac��L�_�0�)Qc��(�,"�W&!�v�P�IQc���&��	� Ac���W�VQc��]����	#��
���l ��Z�g����	�Qc��Y�g��{�f��
�e��6��=��J����7�g�g�xac��gAc�j�t	Ac��R,�z�
�$$ ?Ac����^�M�Ha
d�(�r���$��p�l��uAd*�Ad$�m&	Ad+�Q
d2��Y��W��.�+��8�=�R�eAd=�!	��k�^Ad@�Ad?�.Q
dX�\�(� �O���Adg�c�j�i�hAde�2��}�bAdq�[adr�Q
dt�	(�)�v�u��
�A
d��[�
��Ad�-�|�\�U�'�0�Ad���#�>Q
d��h��t����%��$Ad��dad��;Q	d��S������(�/
A	dǥ?�0�=�*�)�(�9Ad��RAd�	�4Qd�Z��|��u��Z��}���w	�@�?�Ad��h��h��
�$ad��VAd��T�Z�[�Z�&Ad��.		�L	�.�%Ae
��6Qe�R�?��(��}�i�*�1A	e/�U�S�T�B�t�A	e*�{L��m�T�>�xAe)��R�
�.��&aeH�VA
eO�=		�pAeJ�>��FAeM��
aeb�rAef�q�r�v�<�1Aed�o�4�T�xAek�l�na	ew�o�s�r�t�[�Z�o�n�aA
e��V�8�}�*�-�;��RAe���O�XAe���#�H�{�ae��tA	e��,�v�!�Z�Z��~�d�lAe��"�@Ae��5�L�Mae��uAe��Y�6��(Ae��Y��@�|Ae���'�o�4�^ae��A�Z�@�EAe;
�
�d�$�C�y�6�	�W�X�"Ae��q�|�N�Aeс�*��
ae��ZAe�:�vA
e�x�L�	Ae���JQf�{�\	�g�L	�I�T�e�d�a�b�_Af��1Q
f�	
����z������Af+�
Q
f-�G�������rAfA�b�R�VAf9��n����`A	fD��4�7�:�&�a��P�]�^Q	fv�z�(�-��z��B��;Af��|���\�-af��0A
f��/
�s�j	
�p�
Af���J�Af��	��!	�xQf���H��/��	�
�t����6�1�_�6A	f��c�v�w�+�"�+af��[�\�;�O�c�N��a	f��d�c�Z����u�|	�1af��`�f��8�w�b�r�3�1��Z�Y���Ag�p�
�5��HAg��p�Ag��D	�E�ag��H�dAg�f�R�d�0�|�{Ag����0�VAg��O�	�Yag*�JA	g,�I	�0�JAg3�
�HAg6�A
�2agN�=A	gS�G
	�")AgU�&
�Aga�f�Hq	go����	�<��7�
�FA
gz�?�C�X�e�h�_
Qg���p����]�Z�A�b�A
g��
�V�[�f��r�,�]Qg��4�;������
���}�'A	g��z�;(�?�"5ag��8Q
g˶<�/Q
g�X�j��e�o�.;�e�~Ag�f���*Qg�Y�B7��8��&�9��g�"�;��t�?Ag��P�VAg��0�XAg���U��i��=�V�+�KQ	h�K��L��=��J��q4�}��PA
h%�d�c�6ah2�MQh4�[
��0�\��k�l)�/	��$Qh@�w#�@�?#�����D�����=�^�7AhS�k��B�y����]ahm�A	ho���AQh{�6"�1�*�7(��n��i�l��@��	
Ah��T%A
h��%>!/2'(Ah��*Qh���d���z��x���i�N���Ah���NQhÁ�\���� �'*���&��3���"Q	h���9�fR�Q�
�Ahڙ�Xah��'Ahߙ
	���x4�9�&M
Qh��� �E�s�.�?� �*�
Qi��_�8�?��1�w�D1����kA�]�f�	�t$Ai0�>Ai�$
U�p+$CAi�"E��t	�\Qi;�71�X�u$)��p��Q)��"aiH�$AiJ�@�n;BQ+iQ�<�u���S�"#�S�`�g	�&�9��P��%��$��c)�|��)�X!�9�H�_�?�,�3��"�Z�M�q�h�q�b�O�:8A
i��>���^��Z��/��Z�W*1Q
i��W�A�p� 4��E�?Qi��Z��;2�,��a��E��3�4�A���2
�r�A�;�JE�Ai��RQ
i��w�	���Y�X�?�Ai��aQ	i��>(�?�(�7��hAiӟs�A
i��Q�Z		&#&Ai�<�B
�
�Qi���TM�c�;� �
��P��5�zB
�9��4��sAj��7Qj�}�$>G6�E��F�)�:�;�%��Aj#�u�FAj%�'�L		Aj+�B�P��wQ
j8�N	�L�i�@�I�HAjD�T��6�S�_��'��&ajM�Q
jO�|�b�s�_�,���HAj_�Z		�4Aj]�o$
#"
�$Aje��`�i�j�[Qj��+�8��[�(
�/�0�Qj���e�?�!���o���%�,�:�EAj���g���@aj��b�S�K�\�=��h�:Aj��`�(�5�,�T��Q	j��4�p��J�[Aj��$�)��>Q
j�*��-�=�l�5�<
�IAj��&��w�aj��bAj��xA
j��]�r�m�f	�"Aj���ja
k��F�� �}���C�N�LAk �^��l�A	k%�I�f�P�$�%� Ak�O�P�	�ak7�=A
k9�2�-�,�1�0�$Q
kE�C�q�v��@�E�@�=Akb�_�Z�,�HAkP��.�&�f�DA	kQ�$��K�F�I�h�Nakf�Akj�i��A�"����J�]�X�f�)�(�)��[�Akv�"Ako���3�b�xak��AA	k��@�l�5�.�3�2�ak��e�'�&��%�(��Ak��q�h�$� �=�&��L�n�2�[Ak���8Ak���lak��h�Ak˅a�R�{�v�D��@�Y�<�'�&�!��P�l�\Ak���b�@Ak܁��Nak��i�!�;�<�L�K�y�IQ
k��O�4��H�e�fAl�d�V��qAl�s�0:�,Al	��h�N�<a	l��o�����k�$�"al�.�M�D�t�Dal�+Al0�>�HQ	l3��=����	��8��o��f�T��A
l=����[�0�5�p�@�*����alK��pAlP�l
�HAlO�AlX�:�"al_�jAla�n�"�3�>�K�<Q
lm�:����'�C�>�3�B�A����Alx�0��:��u�XQl}�[����-,�,�E�4al��OA	l��W/A	l��b�]�0�%�JAl��	�2Ql���o��	�2�G�������P��M�tAl��S�S�R�Gal��'Q	l��6�^�k�N�Y�NQlɎp�m��j�����P��K)*�e�L�Q"QlٳR�v�+�D��b��1Al�%Q
l�V�k���>��{�+��%�P�yAl���VQm�U��0��-��0��)-�9��g�*!Am�y
Am�Y*Am�Qm$�C����z��e��!&�01�	��O�a�8��8�1�"�1�am=�{AmA�t�4A	m?�K"
�H
	AmN��X�K�P�
amc�vAme�Amg�#Amq��u�=Q%mt��A�U�6@
��B��Q�s�R�s
�n�s�R�+��v��3�W'��.��3Am��'&Am��FAm���v�{�zQ
m��M���x��9�}�\��hAm¾aQ
mĖ
+�2"�G
�lQ
mϾb�U�F/ &�C1*�&am��WQ	mޕ|�,�C�4:�o�D�Qm��~���'�n�6�+�6�o�V�5��Am��}	��}�VAm��?�1/�
An�z�y�\�Qn�B�-	�n�e�Z��<�q��s�
�>�a�v�c�J�/an5�lq	n8�&�_�:�S�E�>�J�Q�KanC�<�D�G�@AnI�C�1�HQ
nM�9��b�G�.�w��kQnX�1��h�q�}�N��(#��D��X�#Ann�4�(Ank�^!�	�:K�N50Anv�S�	�.�yan��TQn��_�&�3���.��g�]�.�=�H�)��}An��c
Q)n��t�G����0��W��X�i�y��@��E�~;��"����p&�g�>����'�v��n�hAn��h��*�!��� �
an���An���W�>�a�6�Qn��F@�[��t�:	�
�j�S�>�j�3�s�jC�x��Q�yAo�{�i"���Z�+ao�vAo�Q
o������X�)�|�mQo)�	"�j�5�T�c#��~�}�;�r/�x��q�|�r�RAoC�
�jQoN�u��k�f�}�^�&�!�SQoZ�9�Q��^�y�(�{�T#4�uAof�
Aog�J�Aot��CQ
ov�;,���V��S�|��|5>�'���Qo���X�S�@�-�2�@�1�?Ao��K3"-�EQo���9�9�P��7�)�e�z�o�r�\�S
#���
��t��}Q
o��(��t�.�I�V�C��pQ
o��1�H �p�C)A
oӁ�H�q�!��~��w�2
ao��vAo�~

�jAo��
�*�/	�K�J	Ao���7
�Tap�CAp�H�7Q
p�G�q�:
�G�2�;�T�O��N��W�@ap!�/Ap(�� Ap$�4�Q�<�x��nAp,��apL�z�HapC�X�]�[�Z��	apK��T����ApX�IQpZ�#�^��Q�J���-�\�5api�GApk�f��fApq�
�pApl��q�@��"ap|�gAp��C
�Ap��w

�Z	Ap~��U�z�
�A�]�N��!��6�ap��kA
p��e�/��4��A���s�S�����ap��'ApȒ!Qpʒ���r��e����#
ap���YApؒ

�lA	p׺!!

�"Ap߁�[�'Qp�t	��^��I�S�2�
�y�C��p�/Aq�~�^�Wa	q	��}�q�m��u�y�o�HQ	q�|�[��n�/�G�TAq%�v�Y�^�\�nQ	q.�s�M�P�6�-�#��AAqF�rAq:�{�~
)Aq;�E�/�R�mQqI�j��r�?�-�6�K���?��8��[ %AqV�t��9�HQq\�l���*		�!�.���1Aqn�N�(A
qp��<	
	Aqq�@�L�u�$�S��_�t	�*�9� Qq���_�}��v��w�n
��t�>�s�QAq��!�$
Aq��d�,Aq����X�;aq��5�1�wAqȥq
A	q��0	Aq��=aq��@Aqܥv�	
�LAq��
	�fAq݁�x
�o	�X�)aq��nAq��p�@�A	q��T�P�M�LAr	�;�j�ka	r�t��s�S�i�a�`�{�_�
A	r"�$�N�R�K�F��D��E��ar,�EAr0��t�I�<�6�"��Z��J�@�|�A�G�:ArD�"Ar.��yarI�oArK�}�[�x�v��'�XQrU��}��7��6��W�Q�$�%��Z��M�,�1�p�4Arg�I��Arj�z�l�xArf�;ar{�A
r}��N�c�s��R��e�`�ar��Qr����l�q�Q�z�H�M�y�t�Ar��g�	ar��B��:�?��>�=�;A
r��|�r�0���K�����5�4��
ar��Ar��K�;	�<Ar��K	�h�l�s	
�rAŕ�Jar��A
r���7
	�E�H�zQ
r��5���%��,�5��BAs�As��v�	�x�	�rAs�5�hQ
s%�T�p��~��i��2�(�'�M��|Q
s0��i�r�y�F��f�?�mAs>�z�`�e�V�s�rAs@��$��"�~AsA��l�e�Sash�yas^�j�o�rase�H�E�D�J�I�G�#Q	sp������$�b��C�B�G�DQsz���.�T�Z�
�6��#�L�'�h�\�o�2�As����,��)as��OAs��o�DAs��M�z	As��v�&�_Q
s���c�f��|��m��x��u�n	As��
A	s��As��=Qsŷ����o�f�E��|�`�kAsҺB��\��oas��GAsٺ:�+Qs�.����I��J��U�|�y�#�,As�+��FQs�+������P�E�(�)	�a�hQt�"�^�e�b�c�h�r�kAt�`At�
At��l�
�e�L�]�
�-at��tAt ��{��
Q
t(�b�t��v
�e�f

�
at5�\At7�aN�(����Qt?��<�A�F�j��x�e�|�_�6atJ��&�#�!A	tQ��&��U��n�-�;
at[�At]��F����F�o�}��(Qtg�0�z���q��p���q��m�f�z�x�w�nAt~�)A	ty�|�$�)Atz�Q�4at��tAt��'�(At��(�3�(�3At��!�v�U�,Q	t��{��h�	�5�B�C�0At���
�E
�D�At��*�b�L�Y�D�;�JAt��%��/�QtӁ�z��y�&�+�,�+���k�hat��JQ
t�&��S�2�b���k��H��a�]At���,��at��'�1�at��L� �+�*�)�2�3�~�}�R�1�.at��t�^��7Au�v�9�����#�d�IAu�_�J�<Au���au%�cq
u(�\��q�p� ��!�^���Za
u7�r�S�R�{�U����M�@�A
uK�$��tAu[�O�AuJ��?��X�
�aue�+Auj�f�o�,�H�^Aul�#����&�<Aug��@�x�y�x�_�UQ	u��L�U�^��X�u��-��	�	au��W�V�(�&�%�9au��T����Qau���G�-Au���H��-�HQu��?�4�/�>�)��(���;Auǒ8����au��#�4� �"�&�%�!�0Qu��+��t�{��J��M�y�x�y�p��Au��'�,Qu��0�Q�Z�U�X	�O
��~�/Av	�6�lAv�8���	Av�av!�1A	v$�/�{�r
�(� Av#��
�6Av,��J�5�javH�4Q	vL���V�!�2�S�N��%�T�A	vV�w�F��6�A	vW�e
�H�I��`�$Avf�*�|�Favq�#�k���C�/q
vx�*����n�c�u���S�V�tav��Y�+Av��@�h�v�Av��(���
�"�Av���T�	av��	Av��d�8�`���h�*�&A
v��7�^�&�6��T�|�<��&�M�Av���Ua
v��C�7�)�6�U�.�8�s�@�Av�8�v� �.�3�d�CQ
v��i�&��/��B�w�r�s�vAv���Q�T�Kaw�9�5�6�Faw����]�a�_�^�b�c�\�[aw�b���^Aw(�HAw"�e�"��
Aw$�h�l�Yaw1�-Qw3�0�q�X�Y�h�q�j�oAw@��`�R��3awF�/�4�6AwO�t�:AwM�2�	AwX��caw_�BAwa�=�AQwe��f�Y�T�_
�b�nAwr�kQww��)�S��S��~�'�	������Aw��7Aw���<�Aw���aw����a�dAw��
�"�F�O�@�?Q	w���e���H	�G�JAw��s�aw��|�z�{���%a
w��j�H�F�~���e�)��Saw����1��A��A	wڮS�Y�$�z�&��I��L��Faw��Aw�IQ	w��9�	�P�~�D��p��3��~Aw��^�8A
w���Aw��U	�ax�=�'�&�%�"Ax�<� 
Ax�*
�
Ax���Qx%�P�T�a�@	
�/�6�C�P�?�8�G�6Ax7�O
�f
�hAxE�<	�A
x9��V�y�a�M�L�R�Q�P��Uaxd�;Axf��Q
xh�D��n���A
xy�W�"��m�N�K�`��%ax��SAx��$	Ax��MAx��ax��!A
x��?�E�@�v�Ax��X��y�z		�Ax���l���ax��Axţ<Qxǁ�m�+�;�0�#�$��.�/�/Axׁ�r�M���D
ax��s�pAx��n�;�<Q
x�
�H���?��.��m
�C��"Ax���T�Uax��EAy�#�4Ax��C	�JAy�Oay�mAy��Ay�NAy#�	ay'�Ay:�j�hAy*�%�`�Ay)��:���f�O�9�V�g��B�oay@�`AyB�Q	yD�/�_�TQyO�v�A�F	�?���d��w�.�9�X�p�w	�yAyg�?�yayi�@Aym�D�HA
yk�C��.�~Ayq��h��*�
��ay��V�F�DQy��_�z��L�A�<Ay��^�Qy��u�P���(�R�d��9�N�L�iAy��P�a�P�u��r�?ay��CAy��-���V�HAy��E	�I�Ay�M�n�%ay��EAyؒdAyշ0�	Ayԁ�
�[ay��{��~�'�"�HAy��E�n�nA
y��G��4Az��az�aA
z�/
�,�|	Az�h�|	�Az-�Z�s�RQ
zC�l�m�t��?�J�v�%�azM�A	zT�S�W�V	�J�gaza��6�Q�"�!��
�Azt�l��T�F�6A
zu�� �$� 
Az}�7�
az��Az��F�pAz��J��6Az���Xaz��4�k�j�f�J�I�gQz��J��`�}�1�*Az����F�N��]az��V�;�AzǬ(�,�%��&�I�l�H��#��6�E�:�;�8�g�^�z�3Az�
��� �!�b�-Y�_�k�^AzɁ�p�fa{�8A{�h
�F
A
{�
	�A{�F�8�&
�MQ{"�R�	��$��-��|�e����A{8�]� �A�Q{B�mn�!
�-��'������
A{T�		�fA{X�Z�:
A{U�G�>Q
{l��(��q�o��$�+	�C�R�	�A{{��)�a�E�8�=�&Q	{��l�;�B�?�2�HA
{��S�~��z�s�=�2	A{��o
�b
	A{��K��a{��OA	{ɦ�*
�+�.�X��Q{���m�Z�U�^�A�%�Z�M�R�M�D�S�Va{��iA{��ZQ
{��+�t�A� �u��q�{�bA|�X�Q
|	�c�i�h"�)��$A|��a�SQ	|��Y�G
�i�b�_�h�mQ	|%��o�n�a� �7A	|0��"�
��e�^�]a|>�#A|s�wA|@�(��(�`�H�rA|B����N��*�I�D�)�V���P�s��a|x�;�:��-�I�L��=A
|��j�J�v�X�4A|��
	��v�s�t�>�~A	|��\��+
�J	�!�c�.�	�Ja|��YA|΂!A|ʣTA	|��{�6�O�`�a�\�[�$a|����\��A|٨3			�2A|���"�k�V�tA|�Y�a|��-Q	|��!�m�p�	�����K��~A|��{��b���Q#}�O�d�k�d�]	�^�c�:�r�c�R
�i�X
�e�\�m�h�U�P�k	A	}+��{
		A	}(�r
A}%�^a}>�nA	}@�Y�$���-�2Q
}M�]�[�/�:�5�"A}[�C�i
�	�~�H
A}X�`

�F	�A}Z��U+
�NQ	}��]�P�K�\�a�f�]�4�*Q}��'�:�#�R���N
�a����
A}���-�F�Q�fa}��^A
}��g�8�;�P�ia}��
A}́�n�O�Z����D�NQ}��o�o�?	�,�,��c�B�Y�FA	}�]��(�u�7�*�9Q
}��@�S�N�7�H�e��:�oA}��d�,�G��>�S�+Q~�M��W�|
�q�b
A~��u�n�ka~�A	~!��u��4�	�sQ~-�n�s�h�{	�n�c�p�	�t�e�x�g
�b�o�`�m���~�k�dA~L�y�
�q�ta~T�A~Y�A
~V�	
�@�A~e��Ra~i�,A~k�)�� �9Q~r�a�g�f�c�h�f�G�H�v�u�IA~��pa~��mA~��l�Y�Z�.��Q~��"�v��7�L�Q��
�A6�y�4�H�NA9�A	~���;�A
&0a>�q�AC�&�A�pQG���_��*�	�c�|�8�Y�{�V��F��#�YA	X�C�R�a�`�ae�s��EAn�D�R�O�:�Ak�	��V�LAq���a{�'A}��JQ��/�n�x�{�Z��7�^�6�a��A	��D�T��V��A�p�
�.��G�&a��u�b�vA��u�$��}��Q��C�,��k�8�J��F�T�Y�;�n�m�L�4�_���a�6�k�fAŒ|�a��d�e�f�	�q�oa��x�z�~�{�x���a��^Q���`�<�=�G�>�3�za��+�G���Ha������ia��Q
���K�q������)�$��A���A�2a��HA
��W�5��H�O�2A��J�P���#�0��e��|�0��A�9��F�RA� ��<a
�;���4���i���h�zA�R��V�L�2A�Q�y
�F�^�@A�T��@�7��a�q�IA
�s�M��x�#�T�=�]�X�L�e�`A�u��2�\A����~a���A���~�`
�Y�^A���(��			�JA����v�-��(�#�oa���^A�̏`A	�ŷS

A��tQ���}�K�{�~�q�f�aA��a�Z�2�lA��N
�4���(		�t�P�KA�過D�7	�k���$� �9��*�~�d�u
�~Q
�J��9
�4�?�6��#�6�-��Ta�Y��MA	�e�R
�S�JA
�[�

A�m��Da�y�VA��z	�nA
��� 
�\A�|��P�a���jA���m�	A���P	�B

	
A����S�_�eQ���2�E�j
�q����	�n�y�x�D��3�fA����a�ta���A	���m�s�P�O�J�2�^�#a���]Q	����� ��A���4�j��A���d���-��i�>��ra���A���}�h�U�\�<�4�N�H�h�H��#�\�<�(�$A���^�<��q�"�^A��n�vQ	��a��/�D�v�S�k�D�`�oA�%�?����g��*a�*�A�,���h��%Q	�2��K�@�?�@a�<�A�G�Z�LA�?��:	�^	A�>���(a�U�<�4A�f�6A�Z��
�4A�b�za�h�A�k�&��P���n�(�Q
�t��j����W�H����7�i��a���eA����jA���`�x�[A����2Q���z�|�w�`
�}�t�}��`��{
�z��a�t�i
Q���y�
�u	�t�=�6�wA���0��B�lQ�ρ�d�G��
�\�m��L��uA�ۏmQ�ޏz�r�6��;���vQ��t��<��7�j�}�p��Q���c&
��v�7��a��v��q�a	$�J�V�=A��6��
�gQ	��S�S��>��|�i	��iA� �UQ
�$�T,�g�T�e�;A�/�WQ
�1�,�0�7�X�k�~�N��0A�?�G�9�8�=�&.Q�G�Y�}�|	�O	�B�M�z�{A�w�A�V�F�0&9A�W��]
�O�h��a�{�A�}�)
'Q���S���b�e�^	�O�V�"��]�L�S�p�y�X�&��>�C�kA���Q���z�@ 
�a�(
�c��0A���9��bQ���|��8�s�?�v��P01�W�L�U��.�[/A���6�B��%�X*7�RQ���B�}�:�O+�f2CQ����O�38�`�a�
	�d�u�b!A���0&A���BKR=	A���_Q�	�,�e
 �T%&3B�.A
���7�{�s�b�=�	�}��z�s��Q�+�c�o�J�4�;+�;�X&1�C�n3�	�&*�o���cQ
�B�Q0-�C�b��t��m���+72Q�P�ga@�H�1�J�5�q��H�?�5��l�3 �{��d��e��p��W�-�2J�oQ�k�a�v��d�%�C�$�-�Z/(A���[
�dA�}�=FQ	JO�0#A��N�>�f	��.Q����o:�}�\�m�\�c�B:?$�D�oQ���9�<�=$-�]A���1&Q����H
�W��x�.��C�J
A�ġ�(�-�pQ�ɡ�f4�O�r�{�vA	���C�X�U�<�t�ar�@a���A��{A���/A���w	&-
Q����h�?;�y�BA��}�Q���k��[��NJ?���a�z��j��.�5A��g&7H�!Q	�#�x�F�A�
9&5a�,�tA
�.�| �L�i�f��Y�:Q�@�F�0��O�
�+
����Q�Q�L�8�M.
-�F�=���F�A
�]�Z�7�.'

&a�h�9�>�A�]�=�S��kA�~�\A	�q�Q�6'A�s�xQ���.7�m�$�	��w�d��#<�)A���_�jQ
���1�u	�,;'A���e�p�
�~A���A���&
Q
����H�G
�A�ͪ�0A���"�:�3� A��ma���8A���Q���w��v
�B�%Q
���%�W��	��0�M�<A��;�A��&�t�K�	�
�3�.A���>Q
���A�4�#�2��	A�,�*�I�n�>�Y�a�4�'A�8�A�6�,��NA�B�A�Ha�F�_A�M���j�^�0�~�H�n�/A�R�c�2A�K�>�Q�~��(�p�`�ha�m�FA�q�
�j�\A�o�E�p�d
A�r��o�KQ���x!����}�	A���7A���wA����zQ����`�e����5�w�ba���J�MQ���=�e�h��f�e�r�cA�˗�n�s��wQ
���m�;�u�|�y�~�wA��N�F
A���E�>

	A�qa��vQ��i�}
��s�\	A��pA��A
��n()
4a�&�hA�(�Q�m�
0
!�q���
�!�
��Q�L��L�`	���{�j����&
�?�,$a�c�Q
�e�(�K�R�Q�P!��"��CQ	�s��)�T�U� ��L�
A�}��EQ	���h�S��b	��	A����b�g��W�<a���oA���BA���f
'"	A���Q���l�m��
�*��I�:��-&�E��(�w�A
�Ȩo��-�"�T���Q���)� �?�o�P��X�8
&A
���3��,�"��U� �Xa���
A���>���(Q	���?�$��&�J��g�$A���!��&a��,�/�?A��0���4	Q���v�{�~�P�.�3�U�V�M�D�:�)E>�Y�XA�@�A�6�(:�lA�5��^.�pa�C�z�A�H�S��b�v��w���P��r�H�1�Z�i�j�{�n��A�Z�/A�J���PS�T�v�
�]a	�u�}�9�=��<����:A���	�xA����!	
			�A����w�O�i�
�(a���A
���\	�j�oa���R� �1�b��x�~�yA���j�W�wQ�ԝ|�f�A�1�~�}�t�A	���� �E��0�#�(a���qQ����R�#��&�+�$��y��3�	A
��0�U�Z�U�B�]�Xa���2��8�4A��#�LA��wA�$��wa�-�zA�2�G�TA�/��"
	�d
A�C���a	�P�I�S�D��B����Q�\��3�0	�6�a��"�CA�j��A�i�
�F�A�w�ja�y�bA4�{�e��
�b�:��T�3��t�	���7�(����8�y�l�|�c��2��5�T�>�_�n�&�c��H��3�J�W��r�{�>A���i
�A�����^/�k�Da���gA����EQ�����z
�>�OA����~�a���YA
��)�n�$�c��H�=�H�R��9a��A���v
�L

�|A��
��BA��#��)Q
�8��z��������h�C�#a�D�pA�F�`		A�H�sA�I�pa�W�nA�[�^	�v	A�Y�l�@A�g�a�r�A�y��0
�T�M�/�2A�t�	
�r	A�~�%�m��Q	���(	����!�*A���A��I	��D��9Q	���D�G�\�o�|
�_�^A�¤

A���OA���a���GQ
�֤�V�^��A�^�n
A��M
		A���

A��Ya���A	���[��j��s�z�a��VA��~�@Q�
�{�H�*�!�_�T�G�<�?�0�K�F�G�b�c�\�Y�:�6��wA�(�*A�"�a�X
A�-�Q�9�+�f�H�+�C�N�
A�E���� �9�8a�L�;�IQ�P�"�Q
	A�\�E�b���a�X�<�7Q	�e�:�i�h�|��[	�lA�o�G��a�z�A�}��	bBA�~�6��A���
a����-�gA�7�A���T��V��M�8��)�T�b��%�>�A�����B#Q
�E�A��k�x��:�b�k�`�a��}��A�T�c�-�t��Ea�\�m�x�e�����
A	�j�4�P�q�p�F�H�9�6A�{��F�A�o�p�d�a	�������6�5�U��A���_�$Q
���d��$�E��+�v��D��Ea���LA���.�xA���
�F
�A���qQ���"�s� ���	�"�	�pa����� ��
�8A�ӡ7���e�|�{Q	���X�l��G�X�i�^�cq
���,�)�V�(�\�Y�.�/��*A��`�J�TA
���	�2�bA����Ea��3A��M�x�A�	�5�@�x�o�HA���s�;a
��Y�o�B�/����F��0�/�!A	�k�:�l�C�x�	�LA�l��(�]��F��BA�z�a�a���%�*�#�+�(A	���<�$�)�H�Y�T�YQ��� �,�-�3���?��4A	����x�F�A����H�0
�NA���	
�@�.Q����z�q
�%��#A�ߞ'�
���
����
������Q���=�,
�R�i
A�	�Q�
����M�f�w
�L�M	�Ta�&�kA�)�9�ZA�.�e�
A�(��)�l�0a�<�(A�>�#�
Q
�D�a��V�1�_�\��A�S�w�|��nQ�Y�0�
�u�l�s�v�{�z�8�1�A�l�S�zA�m�:A�q��g�=�H�Q���@�e�^�[�Z�g���
�A���A�M�H�I�Ba���B�C�?�!�A���IQ���k@�T�}'���x��A���/��p	�G�r���BA�Ѽ�P	A	����i���g�$�>�w�D�a���~��	��
���A���	��\��o��`Q	����#�$�'A���2��!�	a�	�1�3�?�A�>�@a��>�Aa�
� ���zQ	��v�V�W�q
�nA
�%�F	�H�LA�#�y	�t	�A�-�c�&�a�F�w�7�8�zq	�M�8�W�C�B�A�E�V�D�BQ
�X�E	�*�(�)��Z�O�N�~a�f��iA
�n��k$���=�X�d��>��%��F��uQ���X�,�N��z�B��h�I��-��D�-��$a���kA��l
�A
���i	�B�t
A����F�s�<	�GQ��Y�x
�
��&��3���r���{�~�}A�
�6	A��A��`	�%Q��5�~�	�
�	�A�.��jA�-��JA�3���5Q�I�E���2���~���*A
�X�D

�A�g�A�a�y�\�nQ�r�p����U�F��	��
�B�T�Ya����^a
����:��9�;�Y����a���}�8�GA���A����	A���|Q	����]��x�A����A����r�}�A���D�
a���A�ݓ`�FA�ۼ%	A��a���
�F����
A��	�Q���(� �!�?��2
�E�J�;A��M
��Q��K���4�1�,
A�&��q�Xa�)�Q
�+��?�-�r
�k�j�h�a�5�;���|�~�H�G�Iq�C�H���F�^� ���d�c�*A�R�b�J��vA�P�)�R	�<	�bA�Q�I	�F�Sa�n�OA�p�8Q	�r�,�9�����8A�|�o�Ca���S�P�Q�Ra����
��
�@�A�>a����l�NA���t�Q����O�T�Y�R	�QA�����a���<�J�\��I�[�LA�Ɗ!�f�h�Q�z�P��q� �JA���P��.2"��'�,�.�XA���K�F�K�,�L��8�L�p�2�6�ia���A��N	A��A���xQ���?�\�a�U��D��O�DA��;�O��f��K�J��*a��A��A��I��oA�(����QQ�0�S$3��� ��1�tA	�<��R��	��t���Q�H�Q�w���4�-�&�+�z2�>�3�E��A�Z�[Q�]��W���L�G�+�A�k����s�N�GQ�v�1�d��#�N�5�L�Q��V��Q�>��G�b�^�]%�xA���^Q���Z�4�U�F�K�d#�/�fQ
���4"$�_�b1�MA���_	
A���0	1	A�����_��T�ra���Q�ŤT�4�%�
�d�o
4���j�yA	����p�c#
Q
�く�����

����qA���?�:��n�6a����Q����Y�C�>"�Y��F�w1���UA���@�?
A
��F@!
;A��Q��T#�!�"�[�]��I�|�I��*�����
A�3�x�@
�S�$()Q�@��U�B�{�J��E�K�z�&��e�j�y�a�P�A�R�Q	�T�)�d!.��RQ�^�#�.�M�
�	3�U��4�/����r��]�`�"A�p�'�nQ�s��
���b�0�?�%�0'�,��Q���� ���V�.�I�{�A��A����k�H�K#Q���?� ����~�F�G�:�S�
�2��#	�t�Q
���E��8��=�
�	��+�./A�ë`A����,!!A����L�Q�Q�
�Q���W��'�[�l!�e����o�X�a���bA�߫_A���K�(�
	�A��?�TQ
���Z�%�#,�'�~"�pA��S��jQ��k��/2&#�
��'�
A���H�:�+Q�$�C�g[�R�m����v��A�0�'
�	Q�5��
��������
A�B��r��-�Z	a�H�N�I�G�e�K�H�`A�`�A	�U�H�J�#�-	�,�MA�T��*a�h�&Q�j�1�f�0�#�E�D�I�H6�s�n�:A�{����ya�}�]�d�9A�w����JA	���8�v�?�P�L�`�|�A�����z�U�
6M�$0�oQ
����b�[�f�e	A���U��q��j�?Q
���r�f��������|�`a���`A
�����E�%�&�)��|a���lA���
�PA���i
A���)	a	���*�j�L���J�M�K�cA����"�pA�ܫk�R�;�&�X� �H�A
���l��6�H(�-�,�?�)�ha�K�#A�P�"�A�N�	�0A�V�ga�b�kA�d�p�@	� �'	A�k�&A�i��^a�v�WA	�x�T��.���X�d�i�Wa���#��!�%� �^a���N�P�v�ta���^A���]�A���w�4�FA���-�~a���A����L�x�w�|�#�&�z�n���V�YA���r�z�`A���5a���+�U�bA�˞a���.Q�ҡi��t�U�N�^�+�"�#�,�)�"A
��N�<��v�"�p��nA	���X�H�2�LA���e��6�a��cA�	�e�N	A��:A��t�zQ��I��/���|���)�*��w�r�qa
�+�g�o�V�@�#�1�B�!�2�bA�B�2	�%�A�X�U�LA
�?�Q�B���
h�x�/a�[�A	�^�"����V�z�S
�A�j�*�\�JA�]�o�A
��7a�w�~Q�z�W�c�T�cA	����@��(�/�+�,�+�Q���x�K�F�|���6��C��p�9�2�9A���V�A���,�T�W��
�P�I���H�I�HA����5
�0�1a���LA�ː(�A�Ʈ5@	�C�vA
���,���@�0�;�pa���nA��)��R�4A���Z�M�:�4�\�E���[�K�tA�恊q	���s�$�N�+�_�+�0�\�2a�
�nq
��p���k�o�m��R��<�b�nA
��l�^�_�X	A��H�n�A���h
�t�Q
�C�C	���>�I��J��a�P�7A
�S�\�.��z�{�v��a�b�vA�e�&�TH,A�d�5�b�'�<A�l��5a�s�'��
2�,�]��cA���p�0�;��
Q���7��n�k�z��7�2��~�a���yA�ĬW�U�
�|�tA��2�r�b
�L�
A��x��r�u�n�3�&�H
����9��+a���sA��r�p�u�L�WA��f�FA���ma��OA��q�\A�����A���<�a�+�EA�.�a�S�NA�-�K�h��{�&�+A�7��=a�>�^A�E�wA�A�;�~�
A�@�4��
�OQ
�P�{�E�N�*�_�$�'�"�yA���/A�\�vF�YA�[�W�L�l��@3��G�@��Y�4��fa���|A���ZQ����Y��m���'�"�'�$A���v�xA
���k	
�NA���K�,�(�a���UQ	�դw�B�C�����8�AA�ߤt�Z
A	���	
A����>Q	���S�V�Q
a���YA���:�.�JQ	�	�@���=�4��A��
�
Q��z�
�F�O�,�1�b�YQ
�)��U�P
�*���LQ�4��I�N�S�V�b
�~���#�,�1�*�-�(�-�(A	�H�]�e�\a�U�h���7�W�Xa�S�Y�O�P�.��,�-a�X���gA	�h�<�)�8�"��!�*a���wA���q��)�`Q
����p��m�R�P�L�I�W��b�u��r�I�LA���
�l�/�j�]a���A�֮O�1���ZA�߼3A	���/�t�C�|�/�Xa���{A���&�|A���c�^�A���
�a��KA��fQ��J�*�'�)�$�j�mA��
�a��E�c�k�QA�%�x	A�"�8.�'�T��A
�$�/�E��k�R�{�y�v���^�q�8a�>�t��zA�C�e�M�J�zQ	�J�O�O��X��M�4A�T�-�r�[�l���V�Ta�`�vA�o�A
�d�n	A�i��EQ
�z� �@�K�4�+�xA
���)�&�d�&A,����&�'	��P�~
A���n�h��&y7�1�j�q
��a���A
����v��\�k�U��\Q
��

�8�S���y�pA��_� A���`	A��:�F�x�s�N�Ia�(�OA�-�/zA�+�J�H	A�.�"�7�Q	�9�l�"���~��v�'�,A�D�k�
�Q	�H�i�{�T	�0�AA�T�>A�R�T

�2�<A�S�7	�=a�w�`A
�y�A�d�2h��a�x�`�/�(a���yA��~��T		A���{�p
�f
	
�,A��;	�k	j
wa�.�sA�;�?A
�0�w

A�4��J�9a�B�yA�Q�oA�E�x�P
A�D�+�B�)�_Q�V�e	�]��z�	�s�Q
�g�q��_�T�U��N��	
�K�ba�t�/A�w�"Q�{�5��#�n�Y#$�^�)�M�Fa���+A���$� �Q���3	#!�Ta���4A���1A���.�pA���0a���`Q���]�t�m�S��9�G�P��Q���W�*�C�@�'
�@
�
��Z�7�!�

A���f���c�F�W�D
a���;�^��Q�A�8�@�=A��7
Q
�	�?�|����^�]62Q	��1�$��1��4�}�9��PA�u�c�z��a	�|�=�d���z�|�v�r�1A���0�"��X�W�RQ���1�i�q�h�j���m��@�c�f�m��z��-�Jr�lQ���J��K��<���p��a��`�6�B���g�A����2�D�xQ
���f�0��*����b�u�t���m��0A�̮Q�+��t��fA����X�kA�Ɓ�V�?a���5�0��F�DA�ީC�\�`�$A	���o��`�0A�⁏Xa���sQ���>2�m��n��u�,��n�[��v�
�P�A��u�m���2��=a��~A��|A��s�A��8�r�$�+��Q�"��R�8��S���z�5�0�h�,�-�(�b�A�;��p�ea�@�AQ�B�
�^�@�#�tb2����^��^��A�R�	��l�i�Xa�V�DQ
�Y�C�>�I���9�4�3��N��S�a
�f�9�P�t��s�~�}�|�A�FA�w�@���p�A
�x�����b�*���~�A����s�|�-��Z�4aq���
����������#��%��+��-��0��8��;��=��F��P��W
��e��g��j��o��q��w��|������	����������$��'��1��4��6��9��E��G��I��N��P��R��U��[��_��g��j��m��r��v��y��}��������
����������%��*��/��9��?��B��J��M��O��Q��S��V��Z��_��O��g��m��u��y��|������	����
��������S����!��T��(��,��0��2��5��8��:��U��=��B��E��H��L��T��W��Z��]��a��e��j��nA	��q�0�/a2�
��	������������%��)��.��0��2��6��:��<��D��G��I��P��`��f��i��k��o��x��{������	����������[��!��#��.��3��6��9��<��B��D
��O��S��Y��[��b��d��f��]A��+A��qA��_a���4�8�>�E�I�N�T���Z�]�_	�h�j�p�u�w�y	���	��
�����!�%�'�� �)�+�-��!�1�4��"�8�>�C�F�K�U�Y�]��$�_��%�g�p�r��&�|����	��	��!�(�*�0�2��+�4�8�>�B�F�J�L�P��-�W�[�d�h�l�p��/�s�u�w�z�}����
���	��4�'�0�4�8�F�K�N�R�[�^�a�i�k�s�{�}���
���5�����6���'��8�*�/�8�?�D�L�U�X�\�_�c�g�n�q�s�v�z�}���	�����%�'�+�0�2��;�6�8�;�?�C�I
�T�X�\�^�b�f�k�p�x�}��
�����"�&�-�2�5�7�:�@�C�E�H�L�Q�W�\�_��?�g�j�o�t�x�{���
�������"�(�/�3�6��D�<�?�F�L�O�R�Y�c�g�l�o�t�v�{�~�������� �$�(�1�5�9�<�B�F�P�T�V�Y�\��G�`�d�i�j�m�o�w�y�|�~�����	���A
�$
ae���N�V�Y�]�_�c�j�m�q�r�z�~���������!�'�)�,�3�=�?�C�G�M�R�W�[�]�b�i�n�x����������&�)�.�6�8�<�>�A�D�F�I�N�P�V�Y�^�b�f�l�o�q�t�x�}
��
����$��O�+�-�2�5�9�>�A�C�H�K�M�Q�U�X��Q��U��Z�[��[��]��`��c��fA	�]��7�#4S�e�$a>�=��� �#�%�)�*�+�.�0�2�4�7�9�<�=�A�D�F�H�K�N�O�Q�S�V�X�Z�[�]�_�b�d�f�i�k�n	�t�x	�����	�
�����"�$�(�*�,�0�1�5�8�:�>�B%�Ma����0��2|��P�M��ha_���\��`��b��h��k��n��s��t��z��������������
����������!��'��*��+��,��-
��.��<��>��A��B��D��E��G��I��L��M��N��Q��S��T��V��X��Z��[��\��_��`��a��f��g��h��j��k��l��m	��p��r��t��u��v��w��x��y��z��|��}��~������������	������
���������������� ��!��"��#��&��'��)��,��/
��0��<A
�y��@�o�p�ma9���O�Y��Q��R�[��S��V�\��W��X��Z��\��]�e��_�k��a�����d��
� �"�%�,��g��h��j��k��q��r�:��u��v�=��w��x�B��y�E��{�G��|
�O�������U��	���Z��
�\����A
�_�e�@�7�6�7�6a�������A#���7�&���2�:�%�$����
�?�>�5

��a
�ba��t�#mza�
�ap���1��4��7��:��B��E�K��I��m��o������T��4
��;C��u��|����	1��5������������������7��%��8��'��,��0��4��7��9��:��:��;��C��<��=��G��K��>��O��S��V��X��?��b��f��i��o��A��t��x��z��C��~������D��E��I��N��R��X��Z����\����^��_����`����a����e����h��k��n��p��r��t��v
��x����	������$��a��d��u��z��|�����������!��|	�������4�/�2�4�7q
�3�/�1���
�����a�I�G�K�I�2prt� �M�_�q�l�d�.���/�c��0�Pd�Qg�	�Mji�V�X�Wk�	�m�>��?�4�Ea�A��x�q}�a�bPK
!<k5'���2chrome/pdfjs/content/web/cmaps/UniCNS-UCS2-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�
UniCNS-UCS2-Ha
 xzm�b������
��9�1�8�Q��PK
!<L�K����3chrome/pdfjs/content/web/cmaps/UniCNS-UTF16-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE���������!AE���%�%�D�(�	�C�<�O�H�g�^*�g�h��
'
�"�D����Um�,
{
Y*!
3(/�y�1�F�^�7	A	��3�Z��[A�x�xO@�D�(
�EwA8���d
.%
.%U "!":a*.)aA %*s;<

	�3Y<;<�j�Q%P�B��%%%%%��i��%��[��%%%��e��%A(%o�A!
�w
�&�t6�=���"��	�}�$"		OFA/�Ss|}�����$�+�@�A�\�S�`:�e�UBA/��A/��"A/��xA/ҥ	A/�n
$H�>A/��gA/��3A/��`A/��A/��
A%t�0�H�>A�_%���;�Caj��.
�R#�E�~�Z�T��D���F�!�
�VL�\
��S�6�Z�}�b	�h�M�sz�)�l�
�g�	��<�;	���A�^�e��\�Z��x�u�>�A� �9��t�0�-�u�@�p�5�I6��U�D�u��o��T��_�|�V��=���s3�D�`�;�
�|�g�m"�g�F�CB�P�
� 0?�_	�f��y�p�J7�|�'�(hD��,�O�|�c�	�r�
��5�{�(�,C�E2�t�%�n�Q�"�A��
	� �<�/��)����o�Q>�Nx�7�:��,�'�I	���5�,�1
�\�w�"
��e�0�
�-�&�k�y�%�:�M���
B�(��]�;3�B��1���~��G�X��0�b�;�!�j���u�d��g�.�j�YQ"�>	�G\�P�9<	�
R�c
�Z�B��S�=�8�)�L�0�w	�#9�o�h�<J��~�y	�t.�!)�FJ�t�U�F7�#�h�n.�S��BC�k�+w�	�w
�Z�k��1�5��[�
���X�=���E�z�y
H�%��6�	���?
�`���g
�p�&�6<�{%�ZEM��.N�C�2$�7�Y�&5�h-�_)�*�s�^(DQ��)�
�B�p�N��{�{��f���L�)���6=/0
�Px�t�r�s�-7�l7�'�5� �x�'�P�)��B%��C�<	�W,�^�G!�g0�I�6)�"�n�+�#�E\�~A�~.���H�&
�r�s�d�e�X�I�{�)���'0�&��;$�lW��r��%ef+�Z�o�_�?
X�v�/
�R�-�Q�]�f�M#�6�s�O:�#,�>�#'�=>�8�B�&�^%�/
�6
*�`�[.)��
GK%2�P�M�#�#�o�
��w�G�%�r�
?�#"�L��%#
�����)�#�R�'�	�bA�`�. 	�h�c	�:�;�z�yD(�g4�x�
K��L�k
�-�0�^B��<�r	
�1'�:5�7�<��!�>
�P5�<�-�2�3
�g�8c�)(�`�4&�+�^��a
 �bwr�!�a��B]�[�lD�	
�	�'�T-& �6�e�I�	�z��;>�7

H�WT�K6�Pu�vb���M<�T-�<=�!<� n�x?�u+���`�C�@,��&�7�P�C	�4D�yD�a(�
�J$"n�.�[�T�grQN�q���=�J�KAN�r	�p�!AN!�mAN��};�'Q
N*�W��Y����=�J��,��y�n�s�<A"N6��$T�8�5x,P�8�d��;0	�	�n
� ��Gz�� �#.P�,�pAN<�G
"�
�AN7��>��<�;�f�
�/�~�%�$�1� Q	N��-�j��,V��^��7A
N��
�6�/�


�&3A
N��c�M,	A	N���<��\�k�<�]�P�u�7Q	N��l�u�~�o�t��`AN��Q	N����*��I��k�p����A
O	�x	�dAO�=	�nAO��S�/
��Q
O6�.��F��]������{�AOC�%.5*+AOA�-�2AOB�K��7QOR�+	����&�+
+� ���y�|AOg�*�[��3�2�?QOr�r�����~��n�q��'����}���n��}AO��2�%AO���$

AO��G�l�9�1�>�-QOǴg�����Y�X�^�k�CAOӴ\QOִh�k�d�W�Z	�k	AO�L-%(�AO�l�n
$AO�f�-
�~�Q�k�d�^�eQP�A�<�7���\�A�DAP��=�Z�K�0�WQ
P%�8�j�Q&�,�5��|�;A	P3�%�T�E��t��I��QPE�F
��~�y�~�w�|�uAPQ�Q-�i��H��9�Q	PZ��~�	�|	APe���Q	Ph�8�a#"����~��PA&Pt�	�\		�:


�|
�zA2Pr�> �V

�f

	�T�VAP��N��a�[�"��i�x�D�i�f�QP������u��\��W�l�yAQ?�\.j�2��V��jAQ�
�R�z�AQ
��z�Z�a�z�\�_��n��QQY��0�1�h�g�B�x��;�X�YAQe�]��@�t�S��<�P�$��k
�`�^�$�e� ��*�m�L��vAQ���R�r��g���j�Z�8AQj��<	��c� �S�X�8
�5��B�i�+�*l
�]QQƐX��"��e��$��-�9�D��$��u��t�A�B�#A�;Qܢ�
�e�F�>�2�D�}�0��/�B����h�g	�H�U�T�{�(�
	�	�X�V�/�!�N�Y�v�A�D�K�@�=�6
��G���<�0����c�R��f��(���2�K�J�O�p�[���~��s�D�,	���F� �S�*z��W�F�_�l�S�N�9�&�.��(P��t�g�d��8�r��v�[�,�D�*�)�s$���2�d�e�f�X�)�,�0�L�A�s�>�W�J�s��H�%��g���,�O��q�@�EAQ��u� A
Q��"�D�%�d�#�J�h;�3�<AR�3��o��]�%?�ARY�dJ<A7Qہ���l��[�F�D�#�,��s��-��O�>�(�'�i�^
�-�>�I�:��m�2�O��N�?�p �U"��U�Q�2�8�,���	�5�D�{�X�
�	�3QS��A�����s��v�B�5�|�;�3��lAS���_�>�W�
�q"��>�Z�&�J�!�@AS��~,�oAS���"�/�4�5�-�����o�/�2QS�	A
S��"��f���O��@��C
	QT�(�J�S	
��\�CAT�Q
T�G��>��'�f�.��PQT#��#��/
�{�~�s�\�G5 ��(��[�E��.��O��AT8�V
'&AT7�@	ATK�Q�N�%QT`�.�u�x�{��z�_��?��hQ
To�,�0�-�2�O��
	Q	Tz�%�{�^�q�pAT��j��\��[�bQT��u	�r�i��R��g�d�m��v���e��J��7AT��r��f�QT����@�
��Q�I% !�P
�A�V�U��D��[��"QT���S��(��g�+�F�Y�Z�G)�D�?ATǍX
ATϴ{AT��#�|�E�T	�nQT�W�O�(��v��o�C�&�I�6AT�o	AT�RAT,QU�f��J��g�M�.�;�B���7�A'�T��B��yA	U�F��L��E��8�
�K�����rQU*�a��p��S�z��r�}��o�X�{��|AU;�p'�#��8��q�{Q	UC�&������y��r���AUO�*�.�/AUM�^	�6AUS��J	�U��c�.��0�pQ
U{�{�&���3�"�
�$QU���.��Q��q)��f�_����
�� AU��&QU��W
�/��T��W	�j�yAU��		
AU��M	�n	-AU��u��J�7�
�QUǛY�d�S�6"�o�`�G�V�c�X��N���9�c�8�G�N��H��%AU�_�o�h�AU��

AU�|�U�j�Q
U��7	�d�s�3	�J�K�<AV�5���Y
�&Q	V�.�!��\�C�9�(�Q	V��8�f����@�^�
�>AV%�x��!����!Q
V2����n�e��0�7���AV?��pAV=�	
�
�

�rAVC�~	�z�"��'�m�OQ
Vh�	�Z	�W�P�U�`	�]AVv�"�\�o��Z�bjQ
V~�G�����A>V��9��V�Y��:� nP�*��~�H�M�H�Y�X�]�R�Y� �y�Z��<��M��+�<�C�@�?	�J�K	�R�SAW�t�J�nA5V���(�?��M�K�"��9�D�G�		�l��o�J�%�)�^����
�			�t�-�:��/�f�^�{�f
��c�"-Q
WI�T�_�^�W
�^AWa�}
�L�]�\

��#�A)Wb�A�&


�2
�H�3AWT���@�I�x�)�l�
�+��!�k�>�B�6�^�b�m���\�^�QWܼy��v���%������AW�QW������L�S�m�x�y	�j*��"QX�{$��� ��{�5���v�AX��z�)��
��AQX�e�
�|�p��o������`�m
�AX,�m�%Q
X2�^���1�L��	AX=��q�4�pQ	XG�B�A�h�Q�PAXQ�g�@�O�ZQ	XW�i�f�e�f��T��]�k��pAXb�p	�fAXc� �:
AXi�e���\QXy�'�2�!
�4�;�6�EAX��(�~
�>�C
�~AX��<

!
�J	
�jAX���W���T�1�V�g�6��%�:Q	XΧB����/�����A@XةM�Z�&�"�K�
�Z�V��q�@�:���@�"�g�>�.��Y�X�c�b�Z�7��N�]����R�Z�[
�F�I�N�'�~��T�w�X�/�P�&�"�g��U�Z�v�:�G�zAX����
)�AX܁�Z�^�C�]�B�Z��	�/��3�P0�Q
Yp��F�,��)�d��p��q�N�CQY{�X�7�:
�/�<AY��{
AY��]AY��0
�/�SQY��q��t��)	�S�8�]AY��QY�������m���>�;��
�x��
���H��3QYòO��h��]���.��5�t�y�r�u	AY؍uAYֲP�4	#AYف�qQY�x�f�c��:���N%��.�
��!��>�Z��)�K�L�u�T�G����)AZ	�	�p
A
Z
�u
�H�@�E$AZ�	�#�K�\�O���M��c�`QZ@�K	�Z!�\���D�EAZL�/QZP�(��@��W6!�8��M�4!�8��M��Z��1�/��:�
�O��n�C	�BAZp�0�Q	Zw�#�$�T�] ���YA	Z���&�a��B��IQ
Z��+�8�Z�|���D1����Q
Z��
��M�>+��~�-�QAZ���C����4�l��QZ��|�r�Y�n	�i�f�y��4�eQ
Z��.�o�v�_�z�&�mAZс�F�	QZ��P�9�L
�O�J�A���o�=AZ��]�W�<
�0Q
Z��u�A�*�}��v��A
[S���X��2�N� A[�/�r�8�5�H���t�>�~A1Z��,�`�g
�����V�&�+��
�2�7�d�a�>�c		��^�v�q�J�K��u�\�\�8��}Q	[p�Q�0������w��$�W�kA[z�F��\�7�p�U�T�9�z�B�A�@�hA[{�XA[|���
�HQ	[��Z��n��q��pA
[���(	A[��y�
�A[���{�Q	[��V�F�I����#A%[���C�>�G�@�I�@�P��>�M�D�E
�h�0�1�n�{��,�!���j�C�|A[Ձ��R�MqtA[ہ�AQ\��#�T�S���0��r���/A:\� �2��m��*��E��E��i�&^�.�Y�$�&�w�P�R�W�(�B�C�>�W�P�A�L�>�(�_�J��Y�	�8�7�.��=�4�I�HA\m�A\��j��>�<%���5
�7�f�u�T�wQ\���Z��a��~��
�u��w���t�	A\��*
�J�.	
�p	
	A)\��`�0


�n�w�x
�p	A\���+��Y���C�+�
�<�C�"�#�cQ
].�K�����4�U�J-$
A]G�5�A]<�("),
�.�M
�<�[�TA]>��)��
�a�a�k��kQ]w�i��h��]��J��?A]��C�dA]��f
�A]��4�^�1�@Q]��,
��!	
	AJ]��L��6�$j�#�@��4��/� �G�^�,�4�:��w��v�'�U�v6�7�P�I�H��~���	�
��<�x��n�9��<�A��&�'�h�i�d�m�"���X�WA]��5�Z�~��P��^�*A]���X�*�_�X��<�m�F��T�iQ	^[�E��J��e���eA^r�
�Z�A^k�M	�,A^f�8�i�0�G�&Q	^x�1��3�H�^�(�R�'�xA^���F�:�2�A^��&|�"
�2�f�u�|A^�����#�=Q^Ę?����$�D�qQ^��1
�e�d�_�V
���!�w����!A.^��>�{��3��K��
|�^�P�'�J�6��l�{�x�l�g�\�P�5�X�M�:�q�6�~�l�?�:�/�:�;�:�)� �Z�cA^��P�^�\�A^���C�L�E�BQ
_H�A��?�"�c��,��E�$��Y��|Aj_T�v�@�A�n�W��I�T��l�+����|�e�f�h�`�&�K�F�G�J�t�w�r�=�|�y	
�&�L�7�0�)	�2�/�b�Y�Z�[����t��x�}�i�b�c��^��}�^�L���&�'�5���`�t�u��j�u�C�B
�W�J���U�N��A_\�Z�n�y�0 ��T	��	��A�HA_�\Q`	��M�?�
�]�X��tA`�~�m��a�JQ` �!�-�x��x��=��4�c�.�/	�p�A`5�G
�D	�	A`2��.�Z�[		�L�]�X�eA`1��a	�b�?�Q`b�(���
�j�L�U� ���f�e�'A
`��
���A`r�\
�p��{��s�r�
A`u�V�\�}�D�S�KQ`��\�=�����t�<�'�>�/�#� �,Q
`ýj��[����C�Z���/A`јE�iQ`ׁ�5��K�"�� �1�(��,��O�j�^�|�)A`�	��t��DQ`�N�N�_�w�Z�U�~�)�g��b�L�M
�e�nQa�D�K��8��M�u�J�x��a�	�h�|Aa�W�r��~���x��z����pAa�?

�x�I�T�O�p�e�F�Y�^Aa�v�n
�o�|�
�Q
aX�X��B��G�p�j��}�x�_��j��qAad��s�E��1�k��
�zQ
an�G��p��i�r
�i�V�eA
a|�K	�L�?�<�3	Aay�P��
�|�a	�Z�_�X�SAa}�=�r
�5��ZQa��O�f�'�T����~�G�`�L�2�[��
�[�d�kAa��a�[�,���o�Q	a��E�k�9�2�7�<�h�/A	aρ�:�s�1�$��n�yQ
a�E��k�(�#��	Aa��$�IQ
a���(�|�}�r�?�@�A�$f�<A!b�Q�,�O���^�����d�y�z���<�:�v�w�	�t���4Ab�3Ab,��<
�E�1Q	bF�!�?�,�~�b�q��0��7�A
bQ�E�"	�(AbP�)�
		�LAbh�Qby�-�P�U�^�_�P�_�b��Z��q�W��X��5�8
�cAb��Q	b���@��
�:�?Ab���[�.Q
b��c�t�s$�B�gAb���U�Z���q�b�{Qb���Q��o��h��o��b��5
��q"�v��\�sAbՁ���?
�$��V�@Qb�t�q���P�?��d��z�
��h��dAc�8��%�BA
c�p�X�K�`Ac��A�IQc1�e�q�m
��H��]�t�S�;�j�-�=Ac@�<�M�<QcF�K�T�a�>�/
�RAcU�I�.&AcT�3,
�j"	"Acd�G�Q	cu�~�+��J��@��c�#Ac�K��3�*1��6Qc��z�
���0��[�\%���3Ac��L�_�0�)Qc��(�,"�W&!�v�P�IQc���&��	� Ac���W�VQc��]����	#��
���l ��Z�g����	�Qc��Y�g��{�f��
�e��6��=��J����7�g�g�xAc�j�t	
Ac��R,�z�
�$$ ?
+	45&�<�'	Ac����^�M�HQ
d2��Y��W��.�+��8�=�R�eAd=�!	��k�^Ad@�Ad?�.Q
dX�\�(� �O���Adg�c�j�i�hAde�2��}�bAdq�[Q
dt�	(�)�v�u��
�A
d��[�
��Ad�-�|�\�U�'�0�Ad���#�>Q
d��h��t����%��$Ad��dQ	d��S������(�/
A	dǥ?�0�=�*�)�(�9Ad��RAd�	�4Qd�Z��|��u��Z��}���w	�@�?�Ad�[�h
	�Z�[�Z�&Ad��h�#�&		�L	�.�%Ae
��6Qe�R�?��(��}�i�*�1Ae)��?L�D�
��k�,�X��[�T�<�{�P�]��~��x�	�$�	�j�s�p�|��v��:�E��]�1��L�(�N���8�}�*�H�w�N����R�T���f��	�Z�Z�Z�@��~�d��l��/�b��e��]��~�W�R�S�J��
�d�$�C��|�N���W�6�	�W�X�"�>�L�I�L�Y�d	�cAe0�b��
�s�n�H�{�+�[�Y�\��
�6Ae���^Qf�{�\	�g�L	�I�T�e�d�a�b�_Af��1Q
f�	
����z������Af+�
Q
f-�G�������rAfA�bAf9��nAfD��4�7�:��]�aQfY�����n��F��5�x��
����X��]A
fj���#����t��Q	fv�z�(�-��z��B��;Af��3	
�s�j	
�p�
Af��|��J�Af���b�3��!	�xQf���H��/��	�
�t����6�1�_�6AZfĥJ�6�l�T�(�6��C�>�j�E�&�T�_� �
�5��&�_	�F�R�d�0�|�{�V	�0�<�A�0�A�2�H�=�<�I�N�G�<�I
�8
�U�N�M	�"�6�a"!�d�Q�D�[�h�C�X�e�h�_
Ag��p�K���0A f��c�v�w�"��+���J�j�m��L�~	�E��Z�F�Q�I�n�B
�2�k�H�r��HQg���p����]�Z�A�b�A
g��
�V�[�f��r�,�]Qg��4�;������
���}�'A	g��z�;(�?�"5Q
g˶<�/Q
g�X�j��e�o�.;�e�~Ag�f���*Qg�Y�B7��8��&�9��g�"�;��t�?Ag��P�VAg��0�XAg���U��i��=�V�+�KQ	h�K��L��=��J��q4�}��PA
h%�d�c�6Qh4�[
��0�\��k�l)�/	��$Qh@�w#�@�?#�����D�����=�^�7AhS�k!�PA	hk�)AAhU��y��Qh{�6"�1�*�7(��n��i�l��@��	
Ah��T%A
h��%>!/2'(Ah��*Qh���d���z��x���i�N���Ah���NQhÁ�\���� �'*���&��3���"Q	h���9�fR�Q�
�Ahڙ�X�w	���x4�9�&M
Qh��� �E�s�.�?� �*�
Qi��_�8�?��1�w�D1����kA�]�f�	�t$Ai0�>Ai�$
U�p+$CAi�"�z�A��t	�\Qi;�71�X�u$)��p��Q)��"AiJ�@�n;BQ+iQ�<�u���S�"#�S�`�g	�&�9��P��%��$��c)�|��)�X!�9�H�_�?�,�3��"�Z�M�q�h�q�b�O�:8A
i��>���^��Z��/��Z�W*1Q
i��W�A�p� 4��E�?Qi��Z��;2�,��a��E��3�4�A���2
�r�A�;�JE�Ai��RQ
i��w�	���Y�X�?�Ai��aQ	i��>(�?�(�7��hAiӟs�A
i��Q�Z		&#&Ai�<�B
�
�Qi���TM�c�;� �
��P��5�zB
�9��4��sAj��7Qj�}�$>G6�E��F�)�:�;�%��Aj#�u�FAj%�'�L		Aj)��*�Q�P��wQ
j8�N	�L�i�@�I�HA	jC��a����6�S�_��'��&Q
jO�|�b�s�_�,���HQj]�o$�Q�J�U��R��J�o"
�QAj~�lA	jm�p�$Ajq��b�i�j�[Qj��+�8��[�(
�/�0�Qj���e�?�!���o���%�,�:�EAj��]Aj��>Aj���g�)��8�Q	j��4�p��J�[Aj��$�)��>Q
j�*��-�=�l�5�<
�IAk �^��l��hAj��(
�8�l�
�
�f�P�$�%� �l�$Aj����w�r��)�m�f	�"�E�4�L��P�	�Q
kE�C�q�v��@�E�@�=AIkP��.�&�f�D�Q�Z�,�H�>��A�"����J�]�X�f�)�(�)��[��l�5�.�3�2��	�T�h�$� �=�&��L�n�2�[
�W�R�{�v�D��@�Y�<�'�&�!��P�l�\��2�AkQ�$�H�~�h�N�+�w�D�~�8�3�b�@AkR�n��L�3�b�x?�K�N�iQ
k��O�4��H�e�fAl�d�V��q�p��$�J�pAl�s�0:�,��L��rAl	��h�N�<�D�nQ	l3��=����	��8��o��f�T��Al>�T�p
�?
�H�=�RA
l?�-�*�v
Al=���i�'�"Q
lm�:����'�C�>�3�B�A����Alx�0��:��u�XQl}�[����-,�,�E�4A	l��W/A	l��b�]�0�%�JAl��	�2Ql���o��	�2�G�������P��M�tAl��S�S�R�GQ	l��6�^�k�N�Y�NQlɎp�m��j�����P��K)*�e�L�Q"QlٳR�v�+�D��b��1Al�%Q
l�V�k���>��{�+��%�P�yAl���VQl���c����0��-��0��)-�9��g�*!Am�y
Am�Y*Am�Qm$�C����z��e��!&�01�	��O�a�8��8�1�"�1�A	mA�t�4
Am?�K"
�H
	#AmN��X�K�P�
�[�=Q%mt��A�U�6@
��B��Q�s�R�s
�n�s�R�+��v��3�W'��.��3Am��'&Am��FAm���v�{�zQ
m��M���x��9�}�\��hAm¾aQ
mĖ
+�2"�G
�lQ
mϾb�U�F/ &�C1*�&Q	mޕ|�,�C�4:�o�D�Qm��~���'�n�6�+�6�o�V�5��Am��}	��}�VAm��?�1/�
Am���"�Q�y�\�Qn�B�-	�n�e�Z��<�q��s�
�>�a�v�c�J�/An8�&�p�K�0�+�
��	�1�HQnM�9��b�G�.�w��k��8��?��h�q�}�N��(#��D��X�#Ann�4�(Ank�^!�	�:K�N50Anv�S�	�.�yQn��_�&�3���.��g�]�.�=�H�)��}An��c
Q)n��t�G����0��W��X�i�y��@��E�~;��"����p&�g�>����'�v��n�hAn�An��h�>)Anف�i�*�!��
Qn��F@�[��t�:	�
�j�S�>�j�3�s�jC�x��Q�yAo�{�i"���Z�+*Q
o������X�)�|�mQo)�	"�j�5�T�c#��~�}�;�r/�x��q�|�r�RAoC�
�jQoN�u��k�f�}�^�&�!�SQoZ�9�Q��^�y�(�{�T#4�uAof�
Aog�J�Aot��CQ
ov�;,���V��S�|��|5>�'���Qo���X�S�@�-�2�@�1�?Ao��K3"-�EQo���9�9�P��7�)�e�z�o�r�\�S
#���
��t��}Q
o��(��t�.�I�V�C��pQ
o��1�H �p�C)Aoը


�jAo��

�|	�K�J	AoӁ�H�)��"
�TQ
p�G�q�:
�G�2�;�T�O��N��W�@Ap(�� 
�:�Ap$�4�Q�<�x��n�^�bAp,���u�d�oQpZ�#�^��Q�J���-�\�5Apk�f��f�2
�
�.Apq�
�p�d

�Z	Apl��q�@���@��v�z�
�A�]�N��!��0�u�[�P�s�S��Qpʒ���r��e����#
Apؒ

�lA	p׺!!

�"Ap߁�[�'Qp�t	��^��I�S�2�
�y�C��p�/Aq�~�^�W	"Q	q�|�[��n�/�G�TAq%�v�Y�^�\�nQ	q.�s�M�P�6�-�#��AAqF�rAq:�{�~
)Aq;�E�/�R�mQqI�j��r�?�-�6�K���?��8��[ %AqV�t��V��;�9�HQq\�l���*		�!�.���1Aqn�N�(A
qp��<	
	Aqq�@�L�u�$�S��_�t	�*�9� �lQq���_�}��v��w�n
��t�>�s�QAq��!�$
	
�	
�L�@�
�6A*q��d�,	�:
	�f�P�M�L�TAq����X�;�c�t
�o	�X�)	��S�j�k�l�A*Q
r"�$�N�R�z�G�F��D��E��Ar0��t�I�<�6�"��Z��J�@�|�h�+�G�:�h�[�x�/�XArP�IAr.��yQrU��}��7��6��W�Q�$�%��Z��M�,�1�p�4A
rg�I���f�r�s�nArj�z�l�x�"�|Arf�;��sQr����l�q�Q�z�H�M�y�t�A*r��#�`�N���5�4	�4	�w�|�9�2�7�<�h�l�s�o�h	
�[�\�a�h�r
	�E�H�zAr��g�.�p�0�Ar���:
�
�}�&�gQ
r��5���%��,�5��BAs�As��v�	�x�	�rAs�5�hQ
s%�T�p��~��i��2�(�'�M��|Q
s0��i�r�y�F��f�?�mAs>�z�`�e�V�s�r�&As@��$��"�~�*	
AsA��l�e�SQ	sp������$�b��C�B�G�DQsz���.�T�Z�
�6��#�L�'�h�\�o�2���nAs��o�DAs���	�z	As���e�_�&�_Q
s���c�f��|��m��x��u�n	Qs���L�W�f��b��5�DAs��	�UQs¶��P��e����o�f�E��|�`�kAsޒ0A	sҺBAsՁ�sQs�.����I��J��U�|�y�#�,As�+��FQs�+������P�E�(�)	�a�h��N��3�^�e�b�c�h�r�kAt�`

At�
At��l�
�e�L�]�
�-Q
t(�b�t��v
�e�f

�
At7�aN�(����Qt?��<�A�F�j��x�e�|�_�6AtU�}
AtM�&AtL��,�w�yQ	t]��F����F�R��C�}��(Qtg�0�z���q��p���q��m�f�z�x�w�nAt~�)A
ty�|�$�)�Z�3Atz�Q�4	��vQt��%�S��l�O�z��!��$���Q	t��{��h�	�5�B�C�0At���
�E
�D�At��*�b�L�Y�D�;�JAt��%����/�QtӁ�z��y�&�+�,�+���k�hQ
t�&��S�2�b���k��H��a�]Au�1�/�����s�^�
���t�p�o�,�HA&t�� ����(�<�Z�Q��<�o�=��� 	��|�z������&�<�/At�f��(-�0
���c�$�?�"��|��X�
���x�y�x�_�UQ	u��L�U�^��X�u��-��	�	Au��V
�	�Au���~�x
Au���G�5�4Qu��?�4�/�>�)��(���;A	uǒ8�����V	Qu��+��t�{��J��M�y�x�y�p��Au��'�,Qu��0�Q�Z�U�X	�O
��~�/Av	�6�l�{�r
�(� Av�8���	�
�6Av�$�t�5�jQ	vL���V�!�2�S�N��%�T�AOvV�w�Z�[�R
�	�P�Q��|�}�6�H�-���4�4��)�t�T�e�"�4�
�E�H���3�h�b�
�y�t�y���0�T�^�&�6��e�^�'�`��|��P��&��/�h�*�J�K�V�1�h�b�g�n�m�t���v� �.�3�d�CAvs�kAvf�*�|�F�j�e�		��jQ
v��i�&��/��B�w�r�s�vA
v��4	�"Av��
�
�"��
Aw�b�v�m�l�YQw3�0�q�X�Y�h�q�j�oAwO�t�:�N�AAwD�1�	Aw@��`�R�OQwe��f�Y�T�_
�b�nAwr�kQww��)�S��S��~�'�	������Aw��7��"Aw���<��<
Aw���Q	w���e���H	�G�JAwۆg�$�%��L�VAw��z�D�B�HZAw��s��J�O��S�p� �)�F��J�|��-�&�hQ	w��9�	�P�~�D��p��3��~Aw��^�8� 
Aw���
�
Aw��U	���Qx%�P�T�a�@	
�/�6�C�P�?�8�G�6Ax7�O
�f
�hAxE�<	�Ax9��V�y�a�M�L�R�Q�P��U�VQ
xh�D��n���Ax|�%			�.�E�@�v��zAxy�W�b�i	��y�z		�A	xz�i��X!�.���Qxǁ�m�+�;�0�#�$��.�/�/AxڦAx��o
Axׁ�r�M�ZQ
x�
�H���?��.��m
�C��"Ay:�j�hAx���4
�@��`�A$x��J	�
�A�8�n	�:���f�O�9�V�g��B�oQ	yD�/�_�TQyO�v�A�F	�?���d��w�.�9�X�p�w	�yAyh�C�H�4Ayg�?��.�~Ayq��h��*�
��Qy��_�z��L�A�<Ay��^�Qy��u�P���(�R�d��9�N�L�iAy��k�Ay�� �\�D�$�1�fAy��P�p��r�v�nQ	yȌc��\����V����hAyؒd�B�n�nAyշ0�		���4�|Ayԁ�
�[�g�vQ	z*��C��G���+��'�6�5��>A	z7�G�6�z��3	����Q
zC�l�m�t��?�J�v�%�AzW�&�6�>�k�H�/�N�{�|�$�]�X�Y�6�@
�I�<AzT�S	�J��Y�X�
Azr��=Qz��J����%��N��G�	�p�"Az��l�v�9Qz��J��`�}�1�*A+z���"��,�%��&�I�l�H��#��6�E�:�;�8�g�^�z�3��O�P	�G�V��� �)��
Az��9�I	�R
��� �!�b�-Y�_�k�^�o�8�&
�MAz���!�c�f �bQ{"�R�	��$��-��|�e����A{8�]� �A�Q{B�mn�!
�-��'������
A{T�		�fA{X�Z�:
A{U�G�>Q
{l��(��q�o��$�+	�C�R�	�A{{��)�a�E�8�=�&Q	{��l�;�B�?�2�HA{��S�~��z�s�=�2	��A{��o
�b
	
�6A{��K�
�^�Q� 	�+�Q{���m�Z�U�^�A�%�Z�M�R�M�D�S�V�FA{��ZQ
{��+�t�A� �u��q�{�bA|�X�Q
|	�c�i�h"�)��$A|��a�SQ	|��Y�G
�i�b�_�h�mQ	|%��o�n�a� �7A|s�wZ�-Av|0��"�
��e�^�]��f��c��^��M�(��l��*�I�D��]`�P�V���%��t��u����O�r�(���{���}�

�����a�Z�X�c�����#��v�!�"�s�+���c��L��������4�H�6�O�`�a�\���<�$�o���;��J��U		��`��/�B�V�tA|t���  
�~)�-Q	|��!�m�p�	�����K��~A|��{��b���Q#}�O�d�k�d�]	�^�c�:�r�c�R
�i�X
�e�\�m�h�U�P�k	A}+��{
		A
}(�r
A}%�^Q
}M�]�[�/�:�5�"A}[�C�i
�	�~�H
A}X�`

�F	�A}Z��U+�T	�H�QQ	}��]�P�K�\�a�f�]�4�*Q}��'�:�#�R���N
�a����
A}��i	A}��A}́�n��S�NQ}��o�o�?	�,�,��c�B�Y�FA	}�]��(�u�7�*�9Q
}��@�S�N�7�H�e��:�oA}��d�,�G��>�S�+Q~�M��W�|
�q�b
A~�#�,A~��&	A~'�Q~-�n�s�h�{	�n�c�p�	�t�e�x�g
�b�o�`�m���~�k�dA~R�	�0A~L�y�
	
�@��.�9A~[��	��<Q~r�a�g�f�c�h�f�G�H�v�u�IA~��p	�Y�Z�.��Q~��"�v��7�L�Q��
�A6�y�4��M�N�HAC�&�0A	~���;�A
&0QG���_��*�	�c�|�8�Y�{�V��F��#�YA_�z��R�O�:�AX�C�R��8	��V�L�0�JAq���Q��/�n�x�{�Z��7�^�6�a��A��{�N�NA	��D�T��
��z�>A���[�+Q��C�,��k�8�J��F�T�Y�;�n�m�L�4�_���a�6�k�fAŒ|�N�Aǿ}�r�BA�^Q���`�<�=�G�>�3�zA���{�v�v�W��r��s�Q
���K�q������)�$��Ak���A�2�5���b�a�~�7�2�0��e��|�0�\��i�.���`�R�c�^�_�|�7�2�;� �5�V�L�R�U�2�@�E��u�(�\��#�T�=�]�X�L�e�`�h��=�4�9�2�1�6�=�`�v�{�v	�i
�Y�^�p��H
�o�x�u�lA��+� ��@�w�g�A	� ��<"+��n��#Q���}�K�{�~�q�f�aA��a�Z�2�lA��N
�4���(		�t�P�KA�過D�7	�k���$� �9��*�~�d�u
�~Q
�J��9
�4�?�6��#�6�-��TA�[�

�
�Q
�k�U��.�S��c�$�-�"��SA
�x�Q�P	�n�	A�v�$�
�\	�B

	
A�|��P���_�eQ���2�E�j
�q����	�n�y�x�D��3�fA�ͪ�2��<A���A����0�^�2�^Q	����� ��A���4�j��A����>�*�h�U�\�<�4�N�H�h�H��#�\�<�(�$A	����f�;�<��D�7�"�^A�Q�a!�oQ	��a��/�D�v�S�k�D�`�oA�%�?����g��*��+��h��%Q	�2��K�@�?�@A�G�Z�L�h�U�n�(�A�?��:	�^	�n�
�4����+��PA�>���(Q
�t��j����W�H����7�i��A����jA���`�x�[A����2Q���z�|�w�`
�}�t�}��`��{
�z��a�t�i
Q���y�
�u	�t�=�6�w����3A�´?��B�lQ�ρ�d�G��
�\�m��L��uA�ۏmQ�ޏz�r�6��;���vQ ��t��<��7�j�}�p���t��&
��v�7��a��v��q�a	$�J�V�=A��6��
�gQ	��S�S��>��|�i	��iA� �UQ
�$�T,�g�T�e�;A�/�WQ
�1�,�0�7�X�k�~�N��0A�?�G�9�8�=�&.Q�G�Y�}�|	�O	�B�M�z�{A�w�A�V�F�0&9
'A�W��]
�O�h��Q���S���b�e�^	�O�V�"��]�L�S�p�y�X�&��>�C�kA���Q���z�@ 
�a�(
�c��0A���9��bQ���|��8�s�?�v��P01�W�L�U��.�[/A���6�B��%�X*7�RQ���B�}�:�O+�f2CQ����O�38�`�a�
	�d�u�b!A���0&A���BKR=	A���_Q�	�,�e
 �T%&3B�.A
���7�{�s�b�=�	�}��z�s��Q�+�c�o�J�4�;+�;�X&1�C�n3�	�&*�o���cQ
�B�Q0-�C�b��t��m���+72Q�P�ga@�H�1�J�5�q��H�?�5��l�3 �{��d��e��p��W�-�2J�oQ�k�a�v��d�%�C�$�-�Z/(A�}�=F�X�t��]����M��PQ���5JO�M�|��.�e�b�.�A:�}�\�m�\�c�B:?$�D�oQ���9�<�=$-�]A���1&Q����H
�W��x�.��C�J
A�ġ�(�-�pQ�ɡ�f4�O�r�{�vA��{A���C
�V	&�-�~
A��u��t�ar�@Q����h�?;�y�BQ��}���A��[��NJ?���a�z��j��.�5A��g&7H�!Q	�#�x�F�A�
9&5A
�.�| �L�i�f��Y�:Q�@�F�0��O�
�+
����Q�Q�L�8�M.
-�F�=���F�Q�]�Z�7��t�G'

&A�j�>�<A�k�A6�6'A�s�xQ���.7�m�$�	��w�d��#<�)Q����J��W�j���u	�,;'A���e�p�
�~A���A���&
Q
����H�G
�A�ͪ�0A���"�:�3� A�ց�	�5Q���w��v
�B�%Q
���%�W��	��0�M�<A��;�A��&�t�K�	�
�3�.A���B�@�KQ
���A�4�#�2��	A$�-�
�
���j�F�g�0�f�g�H�n�/�j�u�d�u�t�d�u�x�q�bA�,�*�Y�>�Y���"�U��Q�Q�~��(�p�`�hA�r��oQ���x!����}�	A���7A���wA����zQ����`�e����5�w�bQ���=�e�h��f�e�r�cA�˗�n�s��wQ
���m�;�u�|�y�~�wA��N�F
A���E�>

	A�qQ��i�}
��s�\	A��pA	��
	
A��n()
45
0
!,

Q�L��L�`	���{�j����&
�?�,$Q
�e�(�K�R�Q�P!��"��tQ	�s��)�T�U� ��L�
A�}��EQ	���h�S��b	�n�uA���DA
���c
'"	A����b�g�OQ���l�m��
�*��I�:��-&�E��(�w�A
�Ȩo��-�"�T���Q���)� �?�o�P��X�8
&A��$�0A���3�"�/�A���.Q	���?�$��&�J��g�$A�
�=A
����E�4	A��zQ���v�{�~�P�.�3�U�V�M�D�:�)E>�Y�XA)�6�(:�o�Z�(��b�v��w���P��r�H�1�Z�i�j�{�n�������]�rA�Z�/A�5��^.�p���PS�T�v�
�]�P�OQ	�����
������?�}�Q���Q�q�	
�~���-���	A����x�ZA���^	�		�*
A����u�I�c� �Q�ԝ|�f�A�1�~�}�t�A	���� �E��0�#�(Q����R�#��&�+�$��y��3�	A��
�[�L�T�A%��0
�|�"
	�d
�g�`�&A�$��w�C��Q�\��3�0	�6�a��"�CA@�i�
����|�q�6�;�J�V��
�b�:��T�3��t�	��-�(����8�y�l�|�c��2��7�T�>�_�n�&�c��H��3�J�W��r�{�>�D�EA�w�j�|
��JA����	�=�/�k�DQ�����z
�>�OA��
�x�v
�L

�|A����~��7�n�$�c��H�=�H�R�E
�	�4�s�P�QA�)��eQ
�8��z��������h�C�#A"�F�`		
	�v	
�0
�T�M�/�2A�H�s
�@	
�r	A�I�p�E,�m��Q	���(	����!�*A���A��I	��D��9Q	���D�G�\�o�|
�_�^A�¤

A���O��A�́�iQ
�֤�V�^��A�^�n
A��M
		��j	�XA���

�A��Y�Q�
�{�H�*�!�_�T�G�<�?�0�K�F�G�b�c�\�Y�:�6��wA�(�*A�"�a�X
A�-�Q�9�+�f�H�+�C�N�
Q	�E���� �9�8��aQ�P�"�Q
	A�\�E�b���a�X�<�7Q	�e�:�i�h�|��[	�lA�o�G��Q
�}��H�G��h�+�d�iA�7�A���@�m�h���'L��V��M�8��)�T�b��%�>�A����?�u�B#Q
�E�A��k�x��:�b�k�`�a��}��A�U�
A�T�c�H��E��'�j�~�Q�P�q�p�F�H�9��F��'�l��2�RA�o�p�d��7��2Q
���d��$�E��+�v��D��EA���.�xA���
�F
�A���qQ���"�s� ���	�"�	�pA	�ʞ��p��-���e�|�{Q	���X�l��G�X�i�^�cA ��)	�`�J�T�:�x��8H�A�@��l�C�x�	�L
��lA2���V
�(�Z�	�.�}�b�N�S��@��x�o�H�d�[R�I�(�]��F�Z�_��N�S�B�F� A�����uQ��� �,�-�3���?��4A	����x�F�A����H�0
�NA���	
�@�.Q����z�q
�%��#A�ߞ'�
���
����
������Q���=�,
�R�i
A�	�Q�
����M�f�w
�L�M	�TA�)�9�ZA	�.�e�
A�(��)�l�0Q
�D�a��V�1�_�\��A�S�w�|��nQ�Y�0�
�u�l�s�v�{�z�8�1�A�l�S�zA�m�:A�q��g�=�H�PQ���@�e�^�[�Z�g���
�A
���A�M�H�I�B�
�	�B�1Q���k@�T�}'���x��A���/��p	�G�r���BA�Ѽ�P	�4	A����i��.�/�g�$�>�w�D��Q	����#�$�'A���2�A���:	A�
� Q	��v�V�W�q
�nA�%�F	�H�L�L�<A�#�y	�t	��|��|A�-�c�&��Q
�X�E	�*�(�)��Z�O�N�~A���A����d��RA�n��k$��=�]�Q���X�,�N��z�B��h�I��-��D�-��$A����F��;��F��oQ�ɱh��J��	�����}��T���=�p�y�|A	��A��

A��KQ��Y�x
�
��&��3���r���{�~�}A�
�6	A��A��`	�%Q��5�~�	�
�	�A�.��jA�-��JA�3���5�tQ�I�E���2���~���*A
�X�D

�A�g�A�a�y�\�nQ�r�p����U�F��	��
�B�T�YA�����A���:�M�H� �$	�	A���}���hQ	����]��x�A������F�A����r�}�	�V�{�zA���D�
Q���(� �!�?��2
�E�J�;A��M
��Q��K���4�1�,
A�&��q�XQ
�+��?�-�r
�k�j�h�A�9��
�}�|�
�J��vA�P�)�R	�<	�bA�8��|���*�N	�	�F�S�jQ	�r�,�9�����8A���S�BA����j	A�|�o�x�=Q����O�T�Y�R	�QA�Ɗ!�f�h�Q�z�P��q� �J	A���\�h��.2"��'�,�.�XA����������F�K�,�L��8�L�p�2�6�i�T�sQ���?�\�a�U��D��O�DA�
�	A��;��oA����$�U�O��QQ�0�S$3��� ��1�tA	�<��R��	��t���Q�H�Q�w���4�-�&�+�z2�>�3�E��A�Z�[Q�]��W���L�G�+�A�k����s�N�GQ�v�1�d��#�N�5�L�Q��V��Q�>��G�b�^�]%�xA���^Q���Z�4�U�F�K�d#�/�fQ
���4"$�_�b1�MA���_	
A���0	1	A�����_��T�rQ�ŤT�4�%�
�d�o
4���j�yA	����p�c#
Q
�く�����

����qA���?�:��n�6Q����Y�C�>"�Y��F�w1���UA���@�?
A
��F@!
;A��Q��T#�!�"�[�]��I�|�I��*�����
A�3�x�@
�S�$()Q�@��U�B�{�J��E�K�z�&��e�j�y�A�R�Q	�T�)�d!.��RQ�^�#�.�M�
�	3�U��4�/����r��]�`�*A�p�'�nQ�s��
���b�0�?�%�0'�,��Q���� ���V�.�I�{�A��A����k�H�K#Q���?� ����~�F�G�:�S�
�2��#	�t�Q
���E��8��=�
�	��+�./A�ë`A����,!!A����L�Q�Q�
�Q���W��'�[�l!�e����o�X�A�߫_A���K	A��� �<�TQ���b�N�a�%�#,�'�~"�pA��S��jQ��k��/2&#�
��'�
A���H�:�+Q�$�C�g[�R�m����v�����u
�	Q�5��
��������
A�D�_\A�B��r�Q	�:�5�~�J�#�-	�,�MA�T��*Q�j�1�f�0�#�E�D�I�H6�s�n�:A�w����JA�|�_A�{���G��z�U�
6M�$0�o��O�P�L�`�L�Q�Q
����b�[�f�e	A���U��q��j�?Q
���r�f��������|�`A����3�%�&�)��|�Q�F
�r�}�r�w�x��=�$�+�>��1��6��+�4�X��� ������b�y�t�w�t�y�x	�k�j�K���z��@�(���'�"��'	���X�W�L�_�*�5��V�F�v�U�N��`�i�w�|�#�&�z�n���V�Y�V�i���.A�{�,
A�8�Q�)�-*�<
�QA����=�h�H(�-�,}#);�_Q�ҡi��t�U�N�^�+�"�#�,�)�"A��N�<��v�"�p��n�N	A���e�
�!�H�2�L�r�zA����Q��I��/���|���)�*��w�r�qA�0�V��	�%��
����V�z�S
�A�X�U�L�%�\�JA�1�@�|�� �!�<�B���
�f��x�D�u�D�A
��fQ�z�W�c�T�cA	����@��(�/�+�,�+�Q���x�K�F�|���6��C��p�9�2�9A�ː(!
A���V�� @�O�
�v�	�R�4�b�e�v�|�^�_�X	AE���,�T�<���
�P�I���6�o�I�H�:����@�0�;�p�L�M�:�4�<�a�E���[�K�t�.�G�`�H�� �-�5�J�~�w�^�g�X�k�L�Q�Q
�C�C	���>�I��J��A�S�\�.A�W�t�A�V��nQ	�d�5�������:�_�[��A�o�H,6�C��vA�r�r6�+�L�
A�u��
>�5Q���7��n�k�z��7�2��~�A�ĬW�U�
�|�t��p�u�L�W�\��S�N�,A&���7�<�&�o�r�b
�L�
�M�F	�\����h�~�
A��x��>
����9��+�
������,�D��
�OQ
�P�{�E�N�*�_�$�'�"�yA���/A�[�W�v�Hl5�{�@�K�4��[�nA�]��>�_7�v�Q����Y��m���'�"�'�$A���v�xA
���k	
�NA���K�,�(�Q	�դw�B�C�����8�AQ	�ߤt���m��G�L�<�3A����I�D�KQ	���S�V�Q
A���:�.�JQ	�	�@���=�4��A��
�
Q��z�
�F�O�,�1�b�YQ
�)��U�P
�*���LQ�4��I�N�S�V�b
�~���#�,�1�*�-�(�-�(A���vA�M�iXB>A�H�]�\�f�g��U�B�,�w�"�u�J��)�`Q
����g��[�R�P�L�I�W��b�u��r�I�LA�ϭ,�V]�H�1���Z
�|A�߼3A���
�l�<��t�C�|�/�X�R�u��p�o�bQ��J�*�'�)�$�j�mA�%�x	A	��k�.�'�T���SA��
��6
��E��k�R�{�y�v���^�q�8�^�a�zQ	�J�O�O��X��M�4A�T�-��1�>A�U�g��G�T�j	A�i��EQ
�z� �@�K�4�+�xA���)�&�d�&�!A0����&�'	��P�~
�tA���n�h��&y7�1�j�q
���*�^
�[Q
��

�8�S���y�pA��_� zA���`	�H	A	��:�F�x�s�N�I�	�7�Q	�9�l�"���~��v�'�,A�D�k�
�Q	�H�i�{�T	�0�AA�T�>��1�,	���T		�"�\AO�R�T�D�;
�F��<��V�dj�3�`�V�p
����3�&�/
	�4�+�,
�4�1
�n�a�t�g�P
�R�EA�|���L�t�)Q�V�e	�]��z�	�s�Q
�g�q��_�T�U��N��	
�K�bA�w�"Q�{�5��#�n�Y#$�^�)�M�FA���$� �Q���3	#!�TA���1A���.�pA���0Q���]�t�m�S��9�G�P��Q���W�*�C�@�'
�@
�
��Z�7�!�

A��A���f�c�F�W�D
�D
A����QQ
�	�?�|����^�]62Q	��1�$��1��4�}�9��PA�u�c�z�{�*�A	�z�j�3�T�f�"�X�W�RA�{��x�S�L�	Q���1�i�q�h�j���m��@�c�f�m��z��-�Jr�lQ���J��K��<���p��a��`�6�B���g�A����2�D�xQ
���f�0��*����b�u�t���m��0A�̮Q�+��t��f�*�\�`�$A
����X�k��P��`�0A�Ɓ�V�?�@Q���>2�m��n��u�,��n�[��v�
�P�A��{A��?
��A��u��2�C�r�$�+��Q�"��R�8��S���z�5�0�h�,�-�(�b�A�;��p�eQ�B�
�^�@�#�tb2����^��^��A�R�	��l�i�XQ
�Y�C�>�I���9�4�3��"��'�A�j�t����p�A�i�P��b�����b�*���~A����>�#�|Q	����*�fP>A
�����=�N0zC�@܊�2AC�f�@�!�u�I���/�|���7�"�3�d�V�N�j��#�P�h��{�G$�~$�	6�*�%�@�6&�w!�n�c)����=�G
�fE�}!��)�X�t�;H�Fj�$
��5�@�c
�f%�:�K�=%��7�-"�XG�'��2�;F�u�"�`�B0��<�y>�
c�x�Z�V�U�0�Z�K�d��
��
�<�{��|�3�k�7� !��\�E�)�5���m��&�	�|�
"�K�,�~�
�a���M�XW��S�t�K�K �,&�x�?�f#�Q�)�&�iO*�s��4�`�x�4!��[�29
z�	�W�X),�?+�8�M�B���^�4�VS�
	�{U(�^�-&�V�]��� �t
�;-�2�5�S�$�k1�D�m���zs�O�N*�L"�=0�@%8�
�L�1�9�	�k�B�\�9�W�%���X�-�D�
�Q�j��$�+�?�z���6��
��N��]�#T�a�\����0�F�B��#�
�N�j�S��N�L�O�T���F�:��E�z��e��}�V�J�7�$�>�U�% �1
�.
�K�h�;�U�2�{�H�)�(#�'�(�d
�D�R�@�x�N�x�C��`�j�)�)�$�
�*"�n�oQ�?�D�uf�4Z�6�3��'�ZP�,�(4�{-�`H�}�_H�D������>�x�� �n"[�q%
��2#�8�!�g\
�H�_<�&/�i\�i�!��cU�x�m2�f�*����kL�0�;5z.�sD�+��e�<hR�% �&�_�J���W�m���j�}8�s$�z�*�E@�o<�B"�%%�Z�i�n(A�i�F'�I�/�#��`�\�1�G�6(�;F�\y�y�0�
�!/�T�)�,	�*��y��}-�)&�� ��b�i$@
;:�	�9�)�v�a��q����*>���l�7r�43�@�):��A�L0���0�;�r��"�7F��*1
�
�:)�5�{�y&�%�r �[�`�M�p���b���<��m�!k�(��>�{�/�E��z�v5�G@�p�"�Y�.'��[�)�"�{�@%�i:4�z�!��Y�O�3��W �L�`�G�!�j8�W�J4��Z�W�d14�#��<%�
Q�i�'�j�`�� �'��3��W�z�-�j�
�0�7��="�F
�E/�X�=��)�Z���&�
�9�y:�,��0��s�2�U�L�T�C�z/�a�!-�[�j��7�Z&�w�2 �m�o'�x�u�6���<��	#�t�M�/�h�N0�G� �Y�8M�
V�O�pO��e:�1�P<�YP�fiP�j���3�^�Sm�T��'�B�t�K
�F	�;�C�Z�2�o
=�>�q�F�0�h�M�D�X�q�g��1��o�\�O�x�E�V�����R	�~�]�j�z�	�>
�
�O�b�Q��#�l�a�%�Z�$�!C[�@܆��dfB�:��|��YL��P�aH�$��;��Q�%�V����a7�C�v�}pOM+xb:q*@�('
,�!�O2'%[b��8�'~GOFi$H<�C�[0���T%�	�,��Z���i���P�S�*I���r�DE�u��X�j�9�>��Me�5�,w��n�"s;�
W�I5��v��M��6����|��:k�?MH��g�+�S�>����roQw�g�^Cg�@ܔ��\�"�h��6�}?`��K
�q/��?''�B;	dcUb+��
(o�'R>���!� U�T��e�<�B�|���g�E;�?��/�6�Z"��/��F�]+�A%0��;.P	�!�
�1W��f2�8�>���&�{����i�'��_�T&a��m�Q��:��b�h�C_�@�=�5�R��{�'Fs��Fv��5�)�u���W��|�Y�,8�@.bG>8�='*��S)�E(Q�OB�L���H�B�,�Q����D�;L�i�H�4;��b�z��>��#l�H
�W�Sl����@�f�B�P��#�UJ��x�'�=�b��n��	�;��]�6[���k��"�Q�s��h��W�zi�\[�M_�U�|S�_eV��y���H�oc�a4�Z/�7O�R%�M[snni�N��*�K_�a�w�bA�Y��n��E���{��p$$�d��U�S
�R�!��F�L��b�#�2c��M�"C�>�R�>�.�)� �/>�8�o�[��Q	��Y��e�^���@
��\�"�	�J�O�N�;� �K
�(	�!
�6	�'�����?��L9�m,�6�$�1P�Hi��A�4�F%�%����7�nJ�a�
bQch�E�w8�D�I.�6v�8F�]�l���	��;�D�n�F�a�2�a�0�/
)�c�j�e�@$�sK�1�<_�w��c�T�)L�l�5��^��u�:�SU�
�Md�>e�9�4�O�99T�I?�i�Z�;���5�o�G.�1<!!"��J:�*8�M�q"�N]�(0�3��,���F�g�*�]2��y|�K8�D�Q�.�z�o�n�d��}l�X6�4�e���G�`�V�.�K�#
�P��
x��
�(�B
�S-�R*�[r�I�d��;�H�G��N�#�E<�T;��-��26
�.�p(�X��2!��p�Q3�"�/6���)�i�>=���:��4
�Q�`�+�}
�2C�X�j��z�`��F��>C�S�끋td��-d��t���d��6��0�U��L`��)O�38C�R�O��Q
��kQ�n��|�p��/���B4�a'CS�R�w��%�7,�s	�6�X,�`^���/W�o�Jw�Y"�n+��i�E�����K�+w����
,'+�R��
�7�N�j_�2�1�����j$�kR�f�}�@@�_�K�Z^e��dC�pY��h�*�G�=U�8�)��H�fa�g~�uV�S �'�8�:�`��5�5G9�	�,�o�F�GF��6��^�$�l3�k^�@2�`�&
H7�qA��#�V(]�S	�[�"���)�&�u�c�.�2�C�]��8C�U�[�Q�%N�^��7�q�
�{�<�mB�7�x���o
�?/�a�� (�2�&��TM�D��V�,�
�N�>L�r�2*�^�-<�9�3��r�{�84�_&�D�A*�2��L�)=�E�I�%�,��6�g�'���a���m�@.�<�[�T�>
�%�_�
�S�7�&�D
�W�z%���j�,���WU�n%�Lm�y/�i�*��T
�8�w�5�H���W��
�>�+�
���p�@�	:$�Bp�<�5�^�j�J[���M�<�B��z)�w�$s'�W�n2�[�&=�rD�F�g5�7!�;*�,�[����E`�H�n�9�.{�r��_
�)�o�|,�+
Q,��p�&����R�Z�
�"�i�@�w��c��8��s��w�N`�/�n
�g�P�\�3�H�<�75�.=�*��2�C#�f��Lt��P�d�MA�F�G�f�{�6
��e�l�6�-�c�:�9�V(�V�-�F'��E�v�}�H�Z�}��=
�z	�y���*�W�u�i�.�g�j�3�d��a�yd�x��g�V+�W0�R�b3�G%�p
�5�� �#��@�e�\�I�N#�{�k� �p?�Y"�R+��/,�)��1$�.�Z��>��
� �q��
�L�+��3����wL�.E�i�z�C)�S�<?�Y+�@��n�7�G�O��W���}=�X�=m�(&�{:�AC�n�
�m&�t��(�1�2'�J�{�f$�;�nA��J*�G�4*�)k��V�I*�n��T���:�0B�'��*�K���}.a�R&�'
�L,�J�O�._�S�	�
�C�`C�\���}���� ���|o�h��;	��8p�It0P�����"�}��
��e�T�L�P��o�M�	�&�k�?�N�%C�[ޙ��&��oP��w=�:��i��R����D��sq�!b��$EC�^�g��yC�~�;��kCs�[�t��9�-�r�+��3D�sz�l�B���!a�rG#�$:?V�eh�1��2�3*�X/�Yk�D�,�0��P�u���B�u�Y����Y�^�	�}��|#D%i�g6�;�<���=$:c9 _j�k(Z��]�A�@\���}�8H�u�$J�%*�T+�#"�f'���<@�M�'�D�%T,�X�%�
��4�/�.��/��3�r�#+!���6�8�9�y��(�v�qA�&��! Xd�e��d�T�f�U@��G�*���V�V�-��]�s&�~D�}Q�P��y�Q�]^��U�8�@�SA#��:�4�a�E�h�:��G�!(�M�mb�r�15P<�j�I2�J�'�i
��t�w�E�s�n:�C2�}-�pL��$�y��p�T��WA�@�h��M�m�^�.�m�2H�k�A^�
�

+�}�|��4
�1�0V�U�T�y	�xR�$�	�	 �������c
�b�3�2�Y�^
�	�k��%:�c�b8��4"2/@D<�7�6G�/�]�2�oQ	酁�J�>���?�]��e�dA!��.�%��#�6���-��(�3>�Y�X�U�2
�/�X�Y�:{��<�g~�$~A��_�#�s���)�mA�^���jA랥i*�4A�B�%C�\3�aF�;�"�f�'A���$�'|�^���)s�t�yA�n�Y:�(A�@�PA�{�a����K�h�c�_�T�U�T�U�`�	H�#�d�e�d��W�V�]�"�#�:����'�T�U�T
�S��3�2�=�<�;&�
�X�Y�h��y�b���b��}���)�g�h�8�=
	�k�<�=�<�=�<�U	�i�\�]�l�m�H�M��		�,�-�,�-�,�!�a�d�>�7�2
�%�*�+�*���+�*�+�(�)�(�)�,�K�J�#�,�!� �!	�$�����
���
�	���}����Y�`
�#�(�_�K�8�3�2�3�6
�;�{�D�E�N�O�v�o�n�i�n�o�n�o�v�o�v�q�t)�c�f�g�p�q�v�s�r�s�r�g�j�c�f�e	�p�_�f�c�j�5�4��f	�."��
�K�J

�/	�.

�
��M	�T�w�z�qS2���
�
�	A�t�s�r�s�t�s
�A�X�w�v�q�p�o�n
�E�D�C�B�S���������� �!V;z�t2

F�378�x�y26`�}�H�K��
�5�w!�F�+�7�q��@�2�b3�K�#�XZa�c ^#��m�!�2�9�J�N�&�(�B	�W	��y��e;�Y��2@	�z
	��o�>�J
�"Y�F���[��d�z�|����'��*�P�V�Z�a�e�j�V�.�c�k�v�z�~����� ��
�&�u�y�a�;�{��2,e�������*�a�j�J$�Q�d����n2�pW�.�$�#�&�z�~+�}>� �$�(�3U�JM�AT�-�h4�,��S�7�63��`�1��;�*�=�s��?�83�P�5�
�3�/��\�=�%�X��0�U|�[N�`�[(� ��P�[�y
�d�O�?)�
���,�x� �4�6���T�p�!��?�+�A\�;#�/4��"��\�4�`�	A�A��	�9�Jq�5 �p�=Q�N�s�8X�;��B�7�T�(�Q���l�B9�C�_�n;�{�Q�&�'�g'�(�#�;=�VL�V�r�o	�a?�A�E�Z��0i�\
���H�d�J"�=��FO�8g�M�z�b�6�]�$�i,�.�D��"��p�j'�O3�'~�{$�v�z�v��g6�k�'A��H�(�w6�n/�E�oi�*��1�OB�G\�\$���J�L�'��c�\�^�r���+-�T�W�(�&�9 �0K�1N�#�n	�+D�C�#��c�F}��|*�#�=�"��!�'�`L�D)�C�E�{`��(��4#�:�8�D�Ov�#8�;�=;�Y��-�L�^���,�~�Ei�
�o�+�Ha�I�6�^�4�V �j��,�4)�e�v��/�t:�9z�8��F��v��?-�z0�}
�:>�RO��n�n��
�VK�I�Q�L$��8�,�*(�Y�i��Y�@�s�*[�<g��F�bs�`*�]�)�` �*~�#�k�W
�#
�^��+�b;�c0�!�b��%�_�0	�k�n�d�s?�^��S�w"��{Q�z�4�`��;��=^�D��9a^��Q��^���H�=T�A�M�W���N�#�d�-�6�@��H�F�n�\�u,�x�{�i�J�M��m��~�	�
�
�v�aGN��a����n�x�#	�	�K�8�~�r�o�U��N�uN��)�t�u�)�'i�$�T�e"�h-�<�L�I�8�l�E�@K�T��v�m��@��!�1�3�B�/�6�B��;�S��V��a��g��
�q�q%�s�7�v9��k�z�&�B��h�h�%�y"�q�g�gh�p�n��
��b�!$�%�(�R��>��\�O��P�t�c�~����c��t �v��ya�"�j�q��h�-�K�:9R�A	�7U��5�.��z�V�B��J�~�	�|�$�Yv��[�M��6��O�P�Q�+� ��E�`�1�Z��^�g�:6�b���L���`�C�/�X�]�[�	�G�U�5�@7�t	�
}�B�;�7�j�E�#��t��.�5�4R�	��d%�F��S�i��s�p�E�m��>�Q��N�V�.�i�^��M�f�m�r�'z��i�R�
�]s�<��}��K�'�_��+�od�
�,��4�z!�I��bw�Y�`�G�����g�w�s�3��k�(X�B�u�z.�w�C�B�Q�
�;�|�;��	�> �L�����v�N�I�&�9�7�l��c�O�g�o��L�n�sQ�7�v�'L�yS�O�Ey�|+�U(�YT�Y�-��K�E�Q�I�<�O��s�y0�/�+�4�`E�=��1M�5�s�~/�A�D�~�Faz��;�|��y�a�E�m�aw�a���kap���Ya.�����!>��.��1�C��F�#��I��R��X��]�O��O�F��ian����4��t�9��M���k���a#���a����c�C�~��\�����V�����2����5Q��2��]��Pȁ��"a����a���
������G�K�Ipt� �M�_���	�M�m���a���z��*a���3�:�n�JaL��+"�0�V�Y�_�d�f�k�p�y�}�
���(�1�8�Y�^�g	�l�w���!�&�3�<�V�l
�s	��
�"�'�K$�`����*�G9�P��+�4�S�i
�r�}�3��E�d!�{�� �"0�<�n&�u?��^�jC�o�3(�F�p	�r�}F��]
�vk���a�r�a������+��2��T��g��o0��	��1��<��W��j������
$����8��>��M��Q��S��c��m ��t��
����'	��=��G��J��`)����-&��0��X��f����!��)1��8��k��x�o6�q(�*�T&�_�c��N��U��A��
�!�#�%�+�4
�=�A�I�M�Q�S�U�X�]�e�m�s�w�{�}��	����!�$	�'�1�4�6�F5�P�U�\�^�b�e�j!�m���1�4�:�>�A�F�H�L�P�R�T�Z�]�_�u�w�z�~�����#!�*�L�Q�U�`�h
�l�w����#�1�:�D�G�K�O	�l�v�{����	���%	������#��'��,��:��@��G��O��R��X��a��o��t��~����
��a�2�G��F�K��R�Z��]J���)��/�M��N/��Q��S��U
��[��f��n	��s��}������	���������� ��'��+��2��9��>��B��G��I��R��Y��\��_��b��q��s��x��z������
����&��2��8��>��F��N��Q��T��[��b��k��t��z������������������'

��.��<��>��B��E��G��I	��N��V��X��\	��a��d��h��m��r��z����������"����������#��'��)��0��C��G	��M��O��S��X��Z��]	��`K��h��k��r��y	��}��������	�������������y��0��7��:�K��I0��a��T��4��7��<1��?
��s
��v��	��������
��#��1s��B��E	��R��\��b��e��i��n��r4��t��_��|��#��*��5��=��@��D��L��g��l��}��
��F��)��+��0��3��:��>��D��F��R5��^���m��F����%��-	��PK
!<wr���3chrome/pdfjs/content/web/cmaps/UniCNS-UTF16-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�UniCNS-UTF16-HA x��)�����Wa0������
��r�PK
!<�o�����3chrome/pdfjs/content/web/cmaps/UniCNS-UTF32-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�#CE���%�%�D�(�	�C�<�O�H�g�^*�g�h��
'
�"�D����Um�,
{
Y*!
3(/�y�1�F�^�7	C	��3�Z��[A�x�xO@�D�(
�EwC8���d
.%
.%U "!":a*.)aA %*s;<

	�3Y<;<�j�S%P�B��%%%%%��i��%��[��%%%��e��%C(%o�A!
�w
�&�t6�=���"��	�}�$"		OFC/�Ss|}�����$�+�@�A�\�S�`:�e�UBC/��C/��"C/��xC/ҥ	C/�n
$H�>C/��gC/��3C/��`C/��C/��
C%t�0�H�>C�_%���;�Caj��.
�R#�E�~�Z�T��D���F�!�
�VL�\
��S�6�Z�}�b	�h�M�sz�)�l�
�g�	��<�;	���A�^�e��\�Z��x�u�>�A� �9��t�0�-�u�@�p�5�I6��U�D�u��o��T��_�|�V��=���s3�D�`�;�
�|�g�m"�g�F�CB�P�
� 0?�_	�f��y�p�J7�|�'�(hD��,�O�|�c�	�r�
��5�{�(�,C�E2�t�%�n�Q�"�A��
	� �<�/��)����o�Q>�Nx�7�:��,�'�I	���5�,�1
�\�w�"
��e�0�
�-�&�k�y�%�:�M���
B�(��]�;3�B��1���~��G�X��0�b�;�!�j���u�d��g�.�j�YQ"�>	�G\�P�9<	�
R�c
�Z�B��S�=�8�)�L�0�w	�#9�o�h�<J��~�y	�t.�!)�FJ�t�U�F7�#�h�n.�S��BC�k�+w�	�w
�Z�k��1�5��[�
���X�=���E�z�y
H�%��6�	���?
�`���g
�p�&�6<�{%�ZEM��.N�C�2$�7�Y�&5�h-�_)�*�s�^(DQ��)�
�B�p�N��{�{��f���L�)���6=/0
�Px�t�r�s�-7�l7�'�5� �x�'�P�)��B%��C�<	�W,�^�G!�g0�I�6)�"�n�+�#�E\�~C�~.���H�&
�r�s�d�e�X�I�{�)���'0�&��;$�lW��r��%ef+�Z�o�_�?
X�v�/
�R�-�Q�]�f�M#�6�s�O:�#,�>�#'�=>�8�B�&�^%�/
�6
*�`�[.)��
GK%2�P�M�#�#�o�
��w�G�%�r�
?�#"�L��%#
�����)�#�R�'�	�bA�`�. 	�h�c	�:�;�z�yD(�g4�x�
K��L�k
�-�0�^B��<�r	
�1'�:5�7�<��!�>
�P5�<�-�2�3
�g�8c�)(�`�4&�+�^��a
 �bwr�!�a��B]�[�lD�	
�	�'�T-& �6�e�I�	�z��;>�7

H�WT�K6�Pu�vb���M<�T-�<=�!<� n�x?�u+���`�C�@,��&�7�P�C	�4D�yD�a(�
�J$"n�.�[�T�grSN�q���=�J�KCN�r	�p�!CN!�mCN��};�'S
N*�W��Y����=�J��,��y�n�s�<C"N6��$T�8�5x,P�8�d��;0	�	�n
� ��Gz�� �#.P�,�pCN<�G
"�
�CN7��>��<�;�f�
�/�~�%�$�1� S	N��-�j��,V��^��7C
N��
�6�/�


�&3C
N��c�M,	C	N���<��\�k�<�]�P�u�7S	N��l�u�~�o�t��`CN��S	N����*��I��k�p����C
O	�x	�dCO�=	�nCO��S�/
��S
O6�.��F��]������{�COC�%.5*+COA�-�2COB�K��7SOR�+	����&�+
+� ���y�|COg�*�[��3�2�?SOr�r�����~��n�q��'����}���n��}CO��2�%CO���$

CO��G�l�9�1�>�-SOǴg�����Y�X�^�k�CCOӴ\SOִh�k�d�W�Z	�k	CO�L-%(�CO�l�n
$CO�f�-
�~�Q�k�d�^�eSP�A�<�7���\�A�DCP��=�Z�K�0�WS
P%�8�j�Q&�,�5��|�;C	P3�%�T�E��t��I��SPE�F
��~�y�~�w�|�uCPQ�Q-�i��H��9�S	PZ��~�	�|	CPe���S	Ph�8�a#"����~��PC&Pt�	�\		�:


�|
�zC2Pr�> �V

�f

	�T�VCP��N��a�[�"��i�x�D�i�f�SP������u��\��W�l�yCQ?�\.j�2��V��jCQ�
�R�z�CQ
��z�Z�a�z�\�_��n��SQY��0�1�h�g�B�x��;�X�YCQe�]��@�t�S��<�P�$��k
�`�^�$�e� ��*�m�L��vCQ���R�r��g���j�Z�8CQj��<	��c� �S�X�8
�5��B�i�+�*l
�]SQƐX��"��e��$��-�9�D��$��u��t�A�B�#C�;Qܢ�
�e�F�>�2�D�}�0��/�B����h�g	�H�U�T�{�(�
	�	�X�V�/�!�N�Y�v�A�D�K�@�=�6
��G���<�0����c�R��f��(���2�K�J�O�p�[���~��s�D�,	���F� �S�*z��W�F�_�l�S�N�9�&�.��(P��t�g�d��8�r��v�[�,�D�*�)�s$���2�d�e�f�X�)�,�0�L�A�s�>�W�J�s��H�%��g���,�O��q�@�ECQ��u� C
Q��"�D�%�d�#�J�h;�3�<CR�3��o��]�%?�CRY�dJ<C7Qہ���l��[�F�D�#�,��s��-��O�>�(�'�i�^
�-�>�I�:��m�2�O��N�?�p �U"��U�Q�2�8�,���	�5�D�{�X�
�	�3SS��A�����s��v�B�5�|�;�3��lCS���_�>�W�
�q"��>�Z�&�J�!�@CS��~,�oCS���"�/�4�5�-�����o�/�2SS�	C
S��"��f���O��@��C
	ST�(�J�S	
��\�CCT�S
T�G��>��'�f�.��PST#��#��/
�{�~�s�\�G5 ��(��[�E��.��O��CT8�V
'&CT7�@	CTK�Q�N�%ST`�.�u�x�{��z�_��?��hS
To�,�0�-�2�O��
	S	Tz�%�{�^�q�pCT��j��\��[�bST��u	�r�i��R��g�d�m��v���e��J��7CT��r��f�ST����@�
��Q�I% !�P
�A�V�U��D��[��"ST���S��(��g�+�F�Y�Z�G)�D�?CTǍX
CTϴ{CT��#�|�E�T	�nST�W�O�(��v��o�C�&�I�6CT�o	CT�RCT,SU�f��J��g�M�.�;�B���7�A'�T��B��yC	U�F��L��E��8�
�K�����rSU*�a��p��S�z��r�}��o�X�{��|CU;�p'�#��8��q�{S	UC�&������y��r���CUO�*�.�/CUM�^	�6CUS��J	�U��c�.��0�pS
U{�{�&���3�"�
�$SU���.��Q��q)��f�_����
�� CU��&SU��W
�/��T��W	�j�yCU��		
CU��M	�n	-CU��u��J�7�
�SUǛY�d�S�6"�o�`�G�V�c�X��N���9�c�8�G�N��H��%CU�_�o�h�CU��

CU�|�U�j�S
U��7	�d�s�3	�J�K�<CV�5���Y
�&S	V�.�!��\�C�9�(�S	V��8�f����@�^�
�>CV%�x��!����!S
V2����n�e��0�7���CV?��pCV=�	
�
�

�rCVC�~	�z�"��'�m�OS
Vh�	�Z	�W�P�U�`	�]CVv�"�\�o��Z�bjS
V~�G�����C>V��9��V�Y��:� nP�*��~�H�M�H�Y�X�]�R�Y� �y�Z��<��M��+�<�C�@�?	�J�K	�R�SCW�t�J�nC5V���(�?��M�K�"��9�D�G�		�l��o�J�%�)�^����
�			�t�-�:��/�f�^�{�f
��c�"-S
WI�T�_�^�W
�^CWa�}
�L�]�\

��#�C)Wb�A�&


�2
�H�3CWT���@�I�x�)�l�
�+��!�k�>�B�6�^�b�m���\�^�SWܼy��v���%������CW�SW������L�S�m�x�y	�j*��"SX�{$��� ��{�5���v�CX��z�)��
��ASX�e�
�|�p��o������`�m
�CX,�m�%S
X2�^���1�L��	CX=��q�4�pS	XG�B�A�h�Q�PCXQ�g�@�O�ZS	XW�i�f�e�f��T��]�k��pCXb�p	�fCXc� �:
CXi�e���\SXy�'�2�!
�4�;�6�ECX��(�~
�>�C
�~CX��<

!
�J	
�jCX���W���T�1�V�g�6��%�:S	XΧB����/�����C@XةM�Z�&�"�K�
�Z�V��q�@�:���@�"�g�>�.��Y�X�c�b�Z�7��N�]����R�Z�[
�F�I�N�'�~��T�w�X�/�P�&�"�g��U�Z�v�:�G�zCX����
)�CX܁�Z�^�C�]�B�Z��	�/��3�P0�S
Yp��F�,��)�d��p��q�N�CSY{�X�7�:
�/�<CY��{
CY��]CY��0
�/�SSY��q��t��)	�S�8�]CY��SY�������m���>�;��
�x��
���H��3SYòO��h��]���.��5�t�y�r�u	CY؍uCYֲP�4	#CYف�qSY�x�f�c��:���N%��.�
��!��>�Z��)�K�L�u�T�G����)CZ	�	�p
C
Z
�u
�H�@�E$CZ�	�#�K�\�O���M��c�`SZ@�K	�Z!�\���D�ECZL�/SZP�(��@��W6!�8��M�4!�8��M��Z��1�/��:�
�O��n�C	�BCZp�0�S	Zw�#�$�T�] ���YC	Z���&�a��B��IS
Z��+�8�Z�|���D1����S
Z��
��M�>+��~�-�QCZ���C����4�l��SZ��|�r�Y�n	�i�f�y��4�eS
Z��.�o�v�_�z�&�mCZс�F�	SZ��P�9�L
�O�J�A���o�=CZ��]�W�<
�0S
Z��u�A�*�}��v��C
[S���X��2�N� C[�/�r�8�5�H���t�>�~C1Z��,�`�g
�����V�&�+��
�2�7�d�a�>�c		��^�v�q�J�K��u�\�\�8��}S	[p�Q�0������w��$�W�kC[z�F��\�7�p�U�T�9�z�B�A�@�hC[{�XC[|���
�HS	[��Z��n��q��pC
[���(	C[��y�
�C[���{�S	[��V�F�I����#C%[���C�>�G�@�I�@�P��>�M�D�E
�h�0�1�n�{��,�!���j�C�|C[Ձ��R�MqtC[ہ�AS\��#�T�S���0��r���/C:\� �2��m��*��E��E��i�&^�.�Y�$�&�w�P�R�W�(�B�C�>�W�P�A�L�>�(�_�J��Y�	�8�7�.��=�4�I�HC\m�C\��j��>�<%���5
�7�f�u�T�wS\���Z��a��~��
�u��w���t�	C\��*
�J�.	
�p	
	C)\��`�0


�n�w�x
�p	C\���+��Y���C�+�
�<�C�"�#�cS
].�K�����4�U�J-$
C]G�5�C]<�("),
�.�M
�<�[�TC]>��)��
�a�a�k��kS]w�i��h��]��J��?C]��C�dC]��f
�C]��4�^�1�@S]��,
��!	
	CJ]��L��6�$j�#�@��4��/� �G�^�,�4�:��w��v�'�U�v6�7�P�I�H��~���	�
��<�x��n�9��<�A��&�'�h�i�d�m�"���X�WC]��5�Z�~��P��^�*C]���X�*�_�X��<�m�F��T�iS	^[�E��J��e���eC^r�
�Z�C^k�M	�,C^f�8�i�0�G�&S	^x�1��3�H�^�(�R�'�xC^���F�:�2�C^��&|�"
�2�f�u�|C^�����#�=S^Ę?����$�D�qS^��1
�e�d�_�V
���!�w����!C.^��>�{��3��K��
|�^�P�'�J�6��l�{�x�l�g�\�P�5�X�M�:�q�6�~�l�?�:�/�:�;�:�)� �Z�cC^��P�^�\�C^���C�L�E�BS
_H�A��?�"�c��,��E�$��Y��|Cj_T�v�@�A�n�W��I�T��l�+����|�e�f�h�`�&�K�F�G�J�t�w�r�=�|�y	
�&�L�7�0�)	�2�/�b�Y�Z�[����t��x�}�i�b�c��^��}�^�L���&�'�5���`�t�u��j�u�C�B
�W�J���U�N��C_\�Z�n�y�0 ��T	��	��A�HC_�\S`	��M�?�
�]�X��tC`�~�m��a�JS` �!�-�x��x��=��4�c�.�/	�p�C`5�G
�D	�	C`2��.�Z�[		�L�]�X�eC`1��a	�b�?�S`b�(���
�j�L�U� ���f�e�'C
`��
���C`r�\
�p��{��s�r�
C`u�V�\�}�D�S�KS`��\�=�����t�<�'�>�/�#� �,S
`ýj��[����C�Z���/C`јE�iS`ׁ�5��K�"�� �1�(��,��O�j�^�|�)C`�	��t��DS`�N�N�_�w�Z�U�~�)�g��b�L�M
�e�nSa�D�K��8��M�u�J�x��a�	�h�|Ca�W�r��~���x��z����pCa�?

�x�I�T�O�p�e�F�Y�^Ca�v�n
�o�|�
�S
aX�X��B��G�p�j��}�x�_��j��qCad��s�E��1�k��
�zS
an�G��p��i�r
�i�V�eC
a|�K	�L�?�<�3	Cay�P��
�|�a	�Z�_�X�SCa}�=�r
�5��ZSa��O�f�'�T����~�G�`�L�2�[��
�[�d�kCa��a�[�,���o�S	a��E�k�9�2�7�<�h�/C	aρ�:�s�1�$��n�yS
a�E��k�(�#��	Ca��$�IS
a���(�|�}�r�?�@�A�$f�<C!b�Q�,�O���^�����d�y�z���<�:�v�w�	�t���4Cb�3Cb,��<
�E�1S	bF�!�?�,�~�b�q��0��7�C
bQ�E�"	�(CbP�)�
		�LCbh�Sby�-�P�U�^�_�P�_�b��Z��q�W��X��5�8
�cCb��S	b���@��
�:�?Cb���[�.S
b��c�t�s$�B�gCb���U�Z���q�b�{Sb���Q��o��h��o��b��5
��q"�v��\�sCbՁ���?
�$��V�@Sb�t�q���P�?��d��z�
��h��dCc�8��%�BC
c�p�X�K�`Cc��A�ISc1�e�q�m
��H��]�t�S�;�j�-�=Cc@�<�M�<ScF�K�T�a�>�/
�RCcU�I�.&CcT�3,
�j"	"Ccd�G�S	cu�~�+��J��@��c�#Cc�K��3�*1��6Sc��z�
���0��[�\%���3Cc��L�_�0�)Sc��(�,"�W&!�v�P�ISc���&��	� Cc���W�VSc��]����	#��
���l ��Z�g����	�Sc��Y�g��{�f��
�e��6��=��J����7�g�g�xCc�j�t	
Cc��R,�z�
�$$ ?
+	45&�<�'	Cc����^�M�HS
d2��Y��W��.�+��8�=�R�eCd=�!	��k�^Cd@�Cd?�.S
dX�\�(� �O���Cdg�c�j�i�hCde�2��}�bCdq�[S
dt�	(�)�v�u��
�C
d��[�
��Cd�-�|�\�U�'�0�Cd���#�>S
d��h��t����%��$Cd��dS	d��S������(�/
C	dǥ?�0�=�*�)�(�9Cd��RCd�	�4Sd�Z��|��u��Z��}���w	�@�?�Cd�[�h
	�Z�[�Z�&Cd��h�#�&		�L	�.�%Ce
��6Se�R�?��(��}�i�*�1Ce)��?L�D�
��k�,�X��[�T�<�{�P�]��~��x�	�$�	�j�s�p�|��v��:�E��]�1��L�(�N���8�}�*�H�w�N����R�T���f��	�Z�Z�Z�@��~�d��l��/�b��e��]��~�W�R�S�J��
�d�$�C��|�N���W�6�	�W�X�"�>�L�I�L�Y�d	�cCe0�b��
�s�n�H�{�+�[�Y�\��
�6Ce���^Sf�{�\	�g�L	�I�T�e�d�a�b�_Cf��1S
f�	
����z������Cf+�
S
f-�G�������rCfA�bCf9��nCfD��4�7�:��]�aSfY�����n��F��5�x��
����X��]C
fj���#����t��S	fv�z�(�-��z��B��;Cf��3	
�s�j	
�p�
Cf��|��J�Cf���b�3��!	�xSf���H��/��	�
�t����6�1�_�6CZfĥJ�6�l�T�(�6��C�>�j�E�&�T�_� �
�5��&�_	�F�R�d�0�|�{�V	�0�<�A�0�A�2�H�=�<�I�N�G�<�I
�8
�U�N�M	�"�6�a"!�d�Q�D�[�h�C�X�e�h�_
Cg��p�K���0C f��c�v�w�"��+���J�j�m��L�~	�E��Z�F�Q�I�n�B
�2�k�H�r��HSg���p����]�Z�A�b�C
g��
�V�[�f��r�,�]Sg��4�;������
���}�'C	g��z�;(�?�"5S
g˶<�/S
g�X�j��e�o�.;�e�~Cg�f���*Sg�Y�B7��8��&�9��g�"�;��t�?Cg��P�VCg��0�XCg���U��i��=�V�+�KS	h�K��L��=��J��q4�}��PC
h%�d�c�6Sh4�[
��0�\��k�l)�/	��$Sh@�w#�@�?#�����D�����=�^�7ChS�k!�PC	hk�)AChU��y��Sh{�6"�1�*�7(��n��i�l��@��	
Ch��T%C
h��%>!/2'(Ch��*Sh���d���z��x���i�N���Ch���NShÁ�\���� �'*���&��3���"S	h���9�fR�Q�
�Chڙ�X�w	���x4�9�&M
Sh��� �E�s�.�?� �*�
Si��_�8�?��1�w�D1����kA�]�f�	�t$Ci0�>Ci�$
U�p+$CCi�"�z�A��t	�\Si;�71�X�u$)��p��Q)��"CiJ�@�n;BS+iQ�<�u���S�"#�S�`�g	�&�9��P��%��$��c)�|��)�X!�9�H�_�?�,�3��"�Z�M�q�h�q�b�O�:8C
i��>���^��Z��/��Z�W*1S
i��W�A�p� 4��E�?Si��Z��;2�,��a��E��3�4�A���2
�r�A�;�JE�Ci��RS
i��w�	���Y�X�?�Ci��aS	i��>(�?�(�7��hCiӟs�C
i��Q�Z		&#&Ci�<�B
�
�Si���TM�c�;� �
��P��5�zB
�9��4��sCj��7Sj�}�$>G6�E��F�)�:�;�%��Cj#�u�FCj%�'�L		Cj)��*�Q�P��wS
j8�N	�L�i�@�I�HC	jC��a����6�S�_��'��&S
jO�|�b�s�_�,���HSj]�o$�Q�J�U��R��J�o"
�QCj~�lC	jm�p�$Cjq��b�i�j�[Sj��+�8��[�(
�/�0�Sj���e�?�!���o���%�,�:�ECj��]Cj��>Cj���g�)��8�S	j��4�p��J�[Cj��$�)��>S
j�*��-�=�l�5�<
�ICk �^��l��hCj��(
�8�l�
�
�f�P�$�%� �l�$Cj����w�r��)�m�f	�"�E�4�L��P�	�S
kE�C�q�v��@�E�@�=CIkP��.�&�f�D�Q�Z�,�H�>��A�"����J�]�X�f�)�(�)��[��l�5�.�3�2��	�T�h�$� �=�&��L�n�2�[
�W�R�{�v�D��@�Y�<�'�&�!��P�l�\��2�CkQ�$�H�~�h�N�+�w�D�~�8�3�b�@CkR�n��L�3�b�x?�K�N�iS
k��O�4��H�e�fCl�d�V��q�p��$�J�pCl�s�0:�,��L��rCl	��h�N�<�D�nS	l3��=����	��8��o��f�T��Cl>�T�p
�?
�H�=�RC
l?�-�*�v
Cl=���i�'�"S
lm�:����'�C�>�3�B�A����Clx�0��:��u�XSl}�[����-,�,�E�4C	l��W/C	l��b�]�0�%�JCl��	�2Sl���o��	�2�G�������P��M�tCl��S�S�R�GS	l��6�^�k�N�Y�NSlɎp�m��j�����P��K)*�e�L�Q"SlٳR�v�+�D��b��1Cl�%S
l�V�k���>��{�+��%�P�yCl���VSl���c����0��-��0��)-�9��g�*!Cm�y
Cm�Y*Cm�Sm$�C����z��e��!&�01�	��O�a�8��8�1�"�1�C	mA�t�4
Cm?�K"
�H
	#CmN��X�K�P�
�[�=S%mt��A�U�6@
��B��Q�s�R�s
�n�s�R�+��v��3�W'��.��3Cm��'&Cm��FCm���v�{�zS
m��M���x��9�}�\��hCm¾aS
mĖ
+�2"�G
�lS
mϾb�U�F/ &�C1*�&S	mޕ|�,�C�4:�o�D�Sm��~���'�n�6�+�6�o�V�5��Cm��}	��}�VCm��?�1/�
Cm���"�Q�y�\�Sn�B�-	�n�e�Z��<�q��s�
�>�a�v�c�J�/Cn8�&�p�K�0�+�
��	�1�HSnM�9��b�G�.�w��k��8��?��h�q�}�N��(#��D��X�#Cnn�4�(Cnk�^!�	�:K�N50Cnv�S�	�.�ySn��_�&�3���.��g�]�.�=�H�)��}Cn��c
S)n��t�G����0��W��X�i�y��@��E�~;��"����p&�g�>����'�v��n�hCn�Cn��h�>)Cnف�i�*�!��
Sn��F@�[��t�:	�
�j�S�>�j�3�s�jC�x��Q�yCo�{�i"���Z�+*S
o������X�)�|�mSo)�	"�j�5�T�c#��~�}�;�r/�x��q�|�r�RCoC�
�jSoN�u��k�f�}�^�&�!�SSoZ�9�Q��^�y�(�{�T#4�uCof�
Cog�J�Cot��CS
ov�;,���V��S�|��|5>�'���So���X�S�@�-�2�@�1�?Co��K3"-�ESo���9�9�P��7�)�e�z�o�r�\�S
#���
��t��}S
o��(��t�.�I�V�C��pS
o��1�H �p�C)Coը


�jCo��

�|	�K�J	CoӁ�H�)��"
�TS
p�G�q�:
�G�2�;�T�O��N��W�@Cp(�� 
�:�Cp$�4�Q�<�x��n�^�bCp,���u�d�oSpZ�#�^��Q�J���-�\�5Cpk�f��f�2
�
�.Cpq�
�p�d

�Z	Cpl��q�@���@��v�z�
�A�]�N��!��0�u�[�P�s�S��Spʒ���r��e����#
Cpؒ

�lC	p׺!!

�"Cp߁�[�'Sp�t	��^��I�S�2�
�y�C��p�/Cq�~�^�W	"S	q�|�[��n�/�G�TCq%�v�Y�^�\�nS	q.�s�M�P�6�-�#��ACqF�rCq:�{�~
)Cq;�E�/�R�mSqI�j��r�?�-�6�K���?��8��[ %CqV�t��V��;�9�HSq\�l���*		�!�.���1Cqn�N�(C
qp��<	
	Cqq�@�L�u�$�S��_�t	�*�9� �lSq���_�}��v��w�n
��t�>�s�QCq��!�$
	
�	
�L�@�
�6C*q��d�,	�:
	�f�P�M�L�TCq����X�;�c�t
�o	�X�)	��S�j�k�l�A*S
r"�$�N�R�z�G�F��D��E��Cr0��t�I�<�6�"��Z��J�@�|�h�+�G�:�h�[�x�/�XCrP�ICr.��ySrU��}��7��6��W�Q�$�%��Z��M�,�1�p�4C
rg�I���f�r�s�nCrj�z�l�x�"�|Crf�;��sSr����l�q�Q�z�H�M�y�t�C*r��#�`�N���5�4	�4	�w�|�9�2�7�<�h�l�s�o�h	
�[�\�a�h�r
	�E�H�zCr��g�.�p�0�Cr���:
�
�}�&�gS
r��5���%��,�5��BCs�Cs��v�	�x�	�rCs�5�hS
s%�T�p��~��i��2�(�'�M��|S
s0��i�r�y�F��f�?�mCs>�z�`�e�V�s�r�&Cs@��$��"�~�*	
CsA��l�e�SS	sp������$�b��C�B�G�DSsz���.�T�Z�
�6��#�L�'�h�\�o�2���nCs��o�DCs���	�z	Cs���e�_�&�_S
s���c�f��|��m��x��u�n	Ss���L�W�f��b��5�DCs��	�USs¶��P��e����o�f�E��|�`�kCsޒ0C	sҺBCsՁ�sSs�.����I��J��U�|�y�#�,Cs�+��FSs�+������P�E�(�)	�a�h��N��3�^�e�b�c�h�r�kCt�`

Ct�
Ct��l�
�e�L�]�
�-S
t(�b�t��v
�e�f

�
Ct7�aN�(����St?��<�A�F�j��x�e�|�_�6CtU�}
CtM�&CtL��,�w�yS	t]��F����F�R��C�}��(Stg�0�z���q��p���q��m�f�z�x�w�nCt~�)C
ty�|�$�)�Z�3Ctz�Q�4	��vSt��%�S��l�O�z��!��$���S	t��{��h�	�5�B�C�0Ct���
�E
�D�Ct��*�b�L�Y�D�;�JCt��%����/�StӁ�z��y�&�+�,�+���k�hS
t�&��S�2�b���k��H��a�]Cu�1�/�����s�^�
���t�p�o�,�HC&t�� ����(�<�Z�Q��<�o�=��� 	��|�z������&�<�/Ct�f��(-�0
���c�$�?�"��|��X�
���x�y�x�_�US	u��L�U�^��X�u��-��	�	Cu��V
�	�Cu���~�x
Cu���G�5�4Su��?�4�/�>�)��(���;C	uǒ8�����V	Su��+��t�{��J��M�y�x�y�p��Cu��'�,Su��0�Q�Z�U�X	�O
��~�/Cv	�6�l�{�r
�(� Cv�8���	�
�6Cv�$�t�5�jS	vL���V�!�2�S�N��%�T�COvV�w�Z�[�R
�	�P�Q��|�}�6�H�-���4�4��)�t�T�e�"�4�
�E�H���3�h�b�
�y�t�y���0�T�^�&�6��e�^�'�`��|��P��&��/�h�*�J�K�V�1�h�b�g�n�m�t���v� �.�3�d�CCvs�kCvf�*�|�F�j�e�		��jS
v��i�&��/��B�w�r�s�vC
v��4	�"Cv��
�
�"��
Cw�b�v�m�l�YSw3�0�q�X�Y�h�q�j�oCwO�t�:�N�ACwD�1�	Cw@��`�R�OSwe��f�Y�T�_
�b�nCwr�kSww��)�S��S��~�'�	������Cw��7��"Cw���<��<
Cw���S	w���e���H	�G�JCwۆg�$�%��L�VCw��z�D�B�HZCw��s��J�O��S�p� �)�F��J�|��-�&�hS	w��9�	�P�~�D��p��3��~Cw��^�8� 
Cw���
�
Cw��U	���Sx%�P�T�a�@	
�/�6�C�P�?�8�G�6Cx7�O
�f
�hCxE�<	�Cx9��V�y�a�M�L�R�Q�P��U�VS
xh�D��n���Cx|�%			�.�E�@�v��zCxy�W�b�i	��y�z		�C	xz�i��X!�.���Sxǁ�m�+�;�0�#�$��.�/�/CxڦCx��o
Cxׁ�r�M�ZS
x�
�H���?��.��m
�C��"Cy:�j�hCx���4
�@��`�C$x��J	�
�A�8�n	�:���f�O�9�V�g��B�oS	yD�/�_�TSyO�v�A�F	�?���d��w�.�9�X�p�w	�yCyh�C�H�4Cyg�?��.�~Cyq��h��*�
��Sy��_�z��L�A�<Cy��^�Sy��u�P���(�R�d��9�N�L�iCy��k�Cy�� �\�D�$�1�fCy��P�p��r�v�nS	yȌc��\����V����hCyؒd�B�n�nCyշ0�		���4�|Cyԁ�
�[�g�vS	z*��C��G���+��'�6�5��>C	z7�G�6�z��3	����S
zC�l�m�t��?�J�v�%�CzW�&�6�>�k�H�/�N�{�|�$�]�X�Y�6�@
�I�<CzT�S	�J��Y�X�
Czr��=Sz��J����%��N��G�	�p�"Cz��l�v�9Sz��J��`�}�1�*C+z���"��,�%��&�I�l�H��#��6�E�:�;�8�g�^�z�3��O�P	�G�V��� �)��
Cz��9�I	�R
��� �!�b�-Y�_�k�^�o�8�&
�MCz���!�c�f �bS{"�R�	��$��-��|�e����C{8�]� �A�S{B�mn�!
�-��'������
C{T�		�fC{X�Z�:
C{U�G�>S
{l��(��q�o��$�+	�C�R�	�C{{��)�a�E�8�=�&S	{��l�;�B�?�2�HC{��S�~��z�s�=�2	��C{��o
�b
	
�6C{��K�
�^�Q� 	�+�S{���m�Z�U�^�A�%�Z�M�R�M�D�S�V�FC{��ZS
{��+�t�A� �u��q�{�bC|�X�S
|	�c�i�h"�)��$C|��a�SS	|��Y�G
�i�b�_�h�mS	|%��o�n�a� �7C|s�wZ�-Cv|0��"�
��e�^�]��f��c��^��M�(��l��*�I�D��]`�P�V���%��t��u����O�r�(���{���}�

�����a�Z�X�c�����#��v�!�"�s�+���c��L��������4�H�6�O�`�a�\���<�$�o���;��J��U		��`��/�B�V�tC|t���  
�~)�-S	|��!�m�p�	�����K��~C|��{��b���S#}�O�d�k�d�]	�^�c�:�r�c�R
�i�X
�e�\�m�h�U�P�k	C}+��{
		C
}(�r
C}%�^S
}M�]�[�/�:�5�"C}[�C�i
�	�~�H
C}X�`

�F	�C}Z��U+�T	�H�QS	}��]�P�K�\�a�f�]�4�*S}��'�:�#�R���N
�a����
C}��i	C}��C}́�n��S�NS}��o�o�?	�,�,��c�B�Y�FC	}�]��(�u�7�*�9S
}��@�S�N�7�H�e��:�oC}��d�,�G��>�S�+S~�M��W�|
�q�b
C~�#�,C~��&	C~'�S~-�n�s�h�{	�n�c�p�	�t�e�x�g
�b�o�`�m���~�k�dC~R�	�0C~L�y�
	
�@��.�9C~[��	��<S~r�a�g�f�c�h�f�G�H�v�u�IC~��p	�Y�Z�.��S~��"�v��7�L�Q��
�C6�y�4��M�N�HCC�&�0C	~���;�A
&0SG���_��*�	�c�|�8�Y�{�V��F��#�YC_�z��R�O�:�CX�C�R��8	��V�L�0�JCq���S��/�n�x�{�Z��7�^�6�a��C��{�N�NC	��D�T��
��z�>C���[�+S��C�,��k�8�J��F�T�Y�;�n�m�L�4�_���a�6�k�fCŒ|�N�Cǿ}�r�BC�^S���`�<�=�G�>�3�zC���{�v�v�W��r��s�S
���K�q������)�$��Ck���A�2�5���b�a�~�7�2�0��e��|�0�\��i�.���`�R�c�^�_�|�7�2�;� �5�V�L�R�U�2�@�E��u�(�\��#�T�=�]�X�L�e�`�h��=�4�9�2�1�6�=�`�v�{�v	�i
�Y�^�p��H
�o�x�u�lC��+� ��@�w�g�C	� ��<"+��n��#S���}�K�{�~�q�f�aC��a�Z�2�lC��N
�4���(		�t�P�KC�過D�7	�k���$� �9��*�~�d�u
�~S
�J��9
�4�?�6��#�6�-��TC�[�

�
�S
�k�U��.�S��c�$�-�"��SC
�x�Q�P	�n�	C�v�$�
�\	�B

	
C�|��P���_�eS���2�E�j
�q����	�n�y�x�D��3�fC�ͪ�2��<C���C����0�^�2�^S	����� ��A���4�j��C����>�*�h�U�\�<�4�N�H�h�H��#�\�<�(�$C	����f�;�<��D�7�"�^C�Q�a!�oS	��a��/�D�v�S�k�D�`�oC�%�?����g��*��+��h��%S	�2��K�@�?�@C�G�Z�L�h�U�n�(�C�?��:	�^	�n�
�4����+��PC�>���(S
�t��j����W�H����7�i��C����jC���`�x�[C����2S���z�|�w�`
�}�t�}��`��{
�z��a�t�i
S���y�
�u	�t�=�6�w����3C�´?��B�lS�ρ�d�G��
�\�m��L��uC�ۏmS�ޏz�r�6��;���vS ��t��<��7�j�}�p���t��&
��v�7��a��v��q�a	$�J�V�=C��6��
�gS	��S�S��>��|�i	��iC� �US
�$�T,�g�T�e�;C�/�WS
�1�,�0�7�X�k�~�N��0C�?�G�9�8�=�&.S�G�Y�}�|	�O	�B�M�z�{C�w�C�V�F�0&9
'C�W��]
�O�h��S���S���b�e�^	�O�V�"��]�L�S�p�y�X�&��>�C�kC���S���z�@ 
�a�(
�c��0C���9��bS���|��8�s�?�v��P01�W�L�U��.�[/C���6�B��%�X*7�RS���B�}�:�O+�f2CS����O�38�`�a�
	�d�u�b!C���0&C���BKR=	C���_S�	�,�e
 �T%&3B�.C
���7�{�s�b�=�	�}��z�s��S�+�c�o�J�4�;+�;�X&1�C�n3�	�&*�o���cS
�B�Q0-�C�b��t��m���+72S�P�ga@�H�1�J�5�q��H�?�5��l�3 �{��d��e��p��W�-�2J�oS�k�a�v��d�%�C�$�-�Z/(C�}�=F�X�t��]����M��PS���5JO�M�|��.�e�b�.�A:�}�\�m�\�c�B:?$�D�oS���9�<�=$-�]C���1&S����H
�W��x�.��C�J
C�ġ�(�-�pS�ɡ�f4�O�r�{�vC��{C���C
�V	&�-�~
C��u��t�ar�@S����h�?;�y�BS��}���A��[��NJ?���a�z��j��.�5C��g&7H�!S	�#�x�F�A�
9&5C
�.�| �L�i�f��Y�:S�@�F�0��O�
�+
����S�Q�L�8�M.
-�F�=���F�S�]�Z�7��t�G'

&C�j�>�<C�k�A6�6'C�s�xS���.7�m�$�	��w�d��#<�)S����J��W�j���u	�,;'C���e�p�
�~C���C���&
S
����H�G
�C�ͪ�0C���"�:�3� C�ց�	�5S���w��v
�B�%S
���%�W��	��0�M�<C��;�C��&�t�K�	�
�3�.C���B�@�KS
���A�4�#�2��	C$�-�
�
���j�F�g�0�f�g�H�n�/�j�u�d�u�t�d�u�x�q�bC�,�*�Y�>�Y���"�U��Q�Q�~��(�p�`�hC�r��oS���x!����}�	C���7C���wC����zS����`�e����5�w�bS���=�e�h��f�e�r�cC�˗�n�s��wS
���m�;�u�|�y�~�wC��N�F
C���E�>

	C�qS��i�}
��s�\	C��pC	��
	
C��n()
45
0
!,

S�L��L�`	���{�j����&
�?�,$S
�e�(�K�R�Q�P!��"��tS	�s��)�T�U� ��L�
C�}��ES	���h�S��b	�n�uC���DC
���c
'"	C����b�g�OS���l�m��
�*��I�:��-&�E��(�w�C
�Ȩo��-�"�T���S���)� �?�o�P��X�8
&C��$�0C���3�"�/�C���.S	���?�$��&�J��g�$C�
�=C
����E�4	C��zS���v�{�~�P�.�3�U�V�M�D�:�)E>�Y�XC)�6�(:�o�Z�(��b�v��w���P��r�H�1�Z�i�j�{�n�������]�rC�Z�/C�5��^.�p���PS�T�v�
�]�P�OS	�����
������?�}�S���Q�q�	
�~���-���	C����x�ZC���^	�		�*
C����u�I�c� �S�ԝ|�f�A�1�~�}�t�C	���� �E��0�#�(S����R�#��&�+�$��y��3�	C��
�[�L�T�C%��0
�|�"
	�d
�g�`�&C�$��w�C��S�\��3�0	�6�a��"�CC@�i�
����|�q�6�;�J�V��
�b�:��T�3��t�	��-�(����8�y�l�|�c��2��7�T�>�_�n�&�c��H��3�J�W��r�{�>�D�EC�w�j�|
��JC����	�=�/�k�DS�����z
�>�OC��
�x�v
�L

�|C����~��7�n�$�c��H�=�H�R�E
�	�4�s�P�QC�)��eS
�8��z��������h�C�#C"�F�`		
	�v	
�0
�T�M�/�2C�H�s
�@	
�r	C�I�p�E,�m��S	���(	����!�*C���A��I	��D��9S	���D�G�\�o�|
�_�^C�¤

C���O��C�́�iS
�֤�V�^��A�^�n
C��M
		��j	�XC���

�C��Y�S�
�{�H�*�!�_�T�G�<�?�0�K�F�G�b�c�\�Y�:�6��wC�(�*C�"�a�X
C�-�S�9�+�f�H�+�C�N�
S	�E���� �9�8��aS�P�"�Q
	C�\�E�b���a�X�<�7S	�e�:�i�h�|��[	�lC�o�G��S
�}��H�G��h�+�d�iC�7�C���@�m�h���'L��V��M�8��)�T�b��%�>�C����?�u�B#S
�E�A��k�x��:�b�k�`�a��}��C�U�
C�T�c�H��E��'�j�~�Q�P�q�p�F�H�9��F��'�l��2�RC�o�p�d��7��2S
���d��$�E��+�v��D��EC���.�xC���
�F
�C���qS���"�s� ���	�"�	�pC	�ʞ��p��-���e�|�{S	���X�l��G�X�i�^�cC ��)	�`�J�T�:�x��8H�A�@��l�C�x�	�L
��lC2���V
�(�Z�	�.�}�b�N�S��@��x�o�H�d�[R�I�(�]��F�Z�_��N�S�B�F� C�����uS��� �,�-�3���?��4C	����x�F�C����H�0
�NC���	
�@�.S����z�q
�%��#C�ߞ'�
���
����
������S���=�,
�R�i
C�	�S�
����M�f�w
�L�M	�TC�)�9�ZC	�.�e�
C�(��)�l�0S
�D�a��V�1�_�\��C�S�w�|��nS�Y�0�
�u�l�s�v�{�z�8�1�C�l�S�zC�m�:C�q��g�=�H�PS���@�e�^�[�Z�g���
�C
���A�M�H�I�B�
�	�B�1S���k@�T�}'���x��C���/��p	�G�r���BC�Ѽ�P	�4	C����i��.�/�g�$�>�w�D��S	����#�$�'C���2�C���:	C�
� S	��v�V�W�q
�nC�%�F	�H�L�L�<C�#�y	�t	��|��|C�-�c�&��S
�X�E	�*�(�)��Z�O�N�~C���C����d��RC�n��k$��=�]�S���X�,�N��z�B��h�I��-��D�-��$C����F��;��F��oS�ɱh��J��	�����}��T���=�p�y�|C	��C��

C��KS��Y�x
�
��&��3���r���{�~�}C�
�6	C��C��`	�%S��5�~�	�
�	�C�.��jC�-��JC�3���5�tS�I�E���2���~���*C
�X�D

�C�g�C�a�y�\�nS�r�p����U�F��	��
�B�T�YC�����C���:�M�H� �$	�	C���}���hS	����]��x�C������F�C����r�}�	�V�{�zC���D�
S���(� �!�?��2
�E�J�;C��M
��S��K���4�1�,
C�&��q�XS
�+��?�-�r
�k�j�h�C�9��
�}�|�
�J��vC�P�)�R	�<	�bC�8��|���*�N	�	�F�S�jS	�r�,�9�����8C���S�BC����j	C�|�o�x�=S����O�T�Y�R	�QC�Ɗ!�f�h�Q�z�P��q� �J	C���\�h��.2"��'�,�.�XC����������F�K�,�L��8�L�p�2�6�i�T�sS���?�\�a�U��D��O�DC�
�	C��;��oC����$�U�O��QS�0�S$3��� ��1�tC	�<��R��	��t���S�H�Q�w���4�-�&�+�z2�>�3�E��C�Z�[S�]��W���L�G�+�C�k����s�N�GS�v�1�d��#�N�5�L�Q��V��Q�>��G�b�^�]%�xC���^S���Z�4�U�F�K�d#�/�fS
���4"$�_�b1�MC���_	
C���0	1	C�����_��T�rS�ŤT�4�%�
�d�o
4���j�yC	����p�c#
S
�く�����

����qC���?�:��n�6S����Y�C�>"�Y��F�w1���UC���@�?
C
��F@!
;C��S��T#�!�"�[�]��I�|�I��*�����
C�3�x�@
�S�$()S�@��U�B�{�J��E�K�z�&��e�j�y�C�R�S	�T�)�d!.��RS�^�#�.�M�
�	3�U��4�/����r��]�`�*C�p�'�nS�s��
���b�0�?�%�0'�,��S���� ���V�.�I�{�A��C����k�H�K#S���?� ����~�F�G�:�S�
�2��#	�t�S
���E��8��=�
�	��+�./C�ë`C����,!!C����L�Q�Q�
�S���W��'�[�l!�e����o�X�C�߫_C���K	C��� �<�TS���b�N�a�%�#,�'�~"�pC��S��jS��k��/2&#�
��'�
C���H�:�+S�$�C�g[�R�m����v�����u
�	S�5��
��������
C�D�_\C�B��r�Q	�:�5�~�J�#�-	�,�MC�T��*S�j�1�f�0�#�E�D�I�H6�s�n�:C�w����JC�|�_C�{���G��z�U�
6M�$0�o��O�P�L�`�L�Q�S
����b�[�f�e	C���U��q��j�?S
���r�f��������|�`C����3�%�&�)��|�Q�F
�r�}�r�w�x��=�$�+�>��1��6��+�4�X��� ������b�y�t�w�t�y�x	�k�j�K���z��@�(���'�"��'	���X�W�L�_�*�5��V�F�v�U�N��`�i�w�|�#�&�z�n���V�Y�V�i���.C�{�,
C�8�Q�)�-*�<
�QC����=�h�H(�-�,}#);�_S�ҡi��t�U�N�^�+�"�#�,�)�"C��N�<��v�"�p��n�N	C���e�
�!�H�2�L�r�zC����S��I��/���|���)�*��w�r�qC�0�V��	�%��
����V�z�S
�C�X�U�L�%�\�JC�1�@�|�� �!�<�B���
�f��x�D�u�D�A
��fS�z�W�c�T�cC	����@��(�/�+�,�+�S���x�K�F�|���6��C��p�9�2�9C�ː(!
C���V�� @�O�
�v�	�R�4�b�e�v�|�^�_�X	CE���,�T�<���
�P�I���6�o�I�H�:����@�0�;�p�L�M�:�4�<�a�E���[�K�t�.�G�`�H�� �-�5�J�~�w�^�g�X�k�L�Q�S
�C�C	���>�I��J��C�S�\�.C�W�t�C�V��nS	�d�5�������:�_�[��C�o�H,6�C��vC�r�r6�+�L�
C�u��
>�5S���7��n�k�z��7�2��~�C�ĬW�U�
�|�t��p�u�L�W�\��S�N�,C&���7�<�&�o�r�b
�L�
�M�F	�\����h�~�
C��x��>
����9��+�
������,�D��
�OS
�P�{�E�N�*�_�$�'�"�yC���/C�[�W�v�Hl5�{�@�K�4��[�nC�]��>�_7�v�S����Y��m���'�"�'�$C���v�xC
���k	
�NC���K�,�(�S	�դw�B�C�����8�AS	�ߤt���m��G�L�<�3C����I�D�KS	���S�V�Q
C���:�.�JS	�	�@���=�4��C��
�
S��z�
�F�O�,�1�b�YS
�)��U�P
�*���LS�4��I�N�S�V�b
�~���#�,�1�*�-�(�-�(C���vC�M�iXB>C�H�]�\�f�g��U�B�,�w�"�u�J��)�`S
����g��[�R�P�L�I�W��b�u��r�I�LC�ϭ,�V]�H�1���Z
�|C�߼3C���
�l�<��t�C�|�/�X�R�u��p�o�bS��J�*�'�)�$�j�mC�%�x	C	��k�.�'�T���SC��
��6
��E��k�R�{�y�v���^�q�8�^�a�zS	�J�O�O��X��M�4C�T�-��1�>C�U�g��G�T�j	C�i��ES
�z� �@�K�4�+�xC���)�&�d�&�!C0����&�'	��P�~
�tC���n�h��&y7�1�j�q
���*�^
�[S
��

�8�S���y�pC��_� zC���`	�H	C	��:�F�x�s�N�I�	�7�S	�9�l�"���~��v�'�,C�D�k�
�S	�H�i�{�T	�0�AC�T�>��1�,	���T		�"�\CO�R�T�D�;
�F��<��V�dj�3�`�V�p
����3�&�/
	�4�+�,
�4�1
�n�a�t�g�P
�R�EC�|���L�t�)S�V�e	�]��z�	�s�S
�g�q��_�T�U��N��	
�K�bC�w�"S�{�5��#�n�Y#$�^�)�M�FC���$� �S���3	#!�TC���1C���.�pC���0S���]�t�m�S��9�G�P��S���W�*�C�@�'
�@
�
��Z�7�!�

C��C���f�c�F�W�D
�D
C����QS
�	�?�|����^�]62S	��1�$��1��4�}�9��PC�u�c�z�{�*�C	�z�j�3�T�f�"�X�W�RC�{��x�S�L�	S���1�i�q�h�j���m��@�c�f�m��z��-�Jr�lS���J��K��<���p��a��`�6�B���g�C����2�D�xS
���f�0��*����b�u�t���m��0C�̮Q�+��t��f�*�\�`�$C
����X�k��P��`�0C�Ɓ�V�?�@S���>2�m��n��u�,��n�[��v�
�P�C��{C��?
��C��u��2�C�r�$�+��S�"��R�8��S���z�5�0�h�,�-�(�b�C�;��p�eS�B�
�^�@�#�tb2����^��^��C�R�	��l�i�XS
�Y�C�>�I���9�4�3��"��'�C�j�t����p�C�i�P��b�����b�*���~C����>�#�|S	����*�fP>C#��:�4�a�E�h�:��G�!(�M�mb�r�15P<�j�I2�J�'�i
��t�w�E�s�n:�C2�}-�pL��$�y��p�T��WC�@�h��M�m�^�.�m�2H�k�Ch�����=�N0z��Y�#
�

+�}�|��4
�1�0V�U�T�y	�xR�$�	�	 �������c
�b�3�2�Y�^
�	�k��%:�c�b8��4"2/@D<�7�6G�/�]�2�oS	酁�J�>���?�]��e�dC�0m6���-��(�3>�Y�X�U�2
�/�X�Y�:{��<�g~�$~C��.�%�C즋f�+C�(�(C��_C�^�C��C랥iC�ɨC�B�%�>�BC�|�C뺴dC놸C��C���C���$�=�8C�n�6�)sC�[�AC�n�YC��.C�@�PC�E�mC���jC���zx�@�kC�4�a
�T�_	�XH�#�V�W�V�]�$�'�T�U�T
�S�hv&�
�h�i�b�c�b ��f�g�h
	�k�h	�i�H�M		
�&�#�b�W

	
	
#6-D�J�K�|�{�N�O	\�"	
(>(�o7��-I�[O�8K	�T�S2�!_	A
�A4h.-�v�
V;�37826`�e�{�w!�F�+�7�q�kk�ZZ��3�H�I��!�1AK�0���7�"�3�d�V�N�j��#�P�h��{�G$�~$�	6�*�%�@�6&�w!�n�c)��=�G
�fE�}!��)�X�t�;H�Fj�$
��5�@�c
�f%�:�K�=%��7�-"�XG�'�2�;F�u�"�`�B0��<�y>�
c�x�Z�V�U�0�Z�K�d
��
�<�{��|�3�k�7� !��\�E�)�5���m��&�	�|�
"�K�,�~�
�a���M�XW��S�t�K�K �,&�x�?�f#�Q�)�&�iO*�s4�`�x�4!��[�29
z�	�W�X),�?+�8�M�B���^�4�VS�
	�{U(�^�-&�V�]� �t
�;-�2�5�S�$�k1�D�m���zs�O�N*�L"�=0�@%8�
�L�1�9�	�k�B�\�9�W�%���X�-�D�
�Q�j��$�+�?�z���6��
��N��]�#T�a�\����0�F�B#�
�N�j�S��N�L�O�T���F�:��E�z��e��}�V�J�7�$�>�U�% �1
�.
�K�h�;�U�2�{�H�)�(#�'�(�d
�D�R�@�x�N�x�C�`�j�)�)�$�
�*"�n�oQ�?�D�uf�4Z�6�3�'�ZP�,�(4�{-�`H�}�_H�D������>�x� �n"[�q%
��2#�8�!�g\
�H�_<�&/�i\�i�!��cU�x�m2�f�*���kL�0�;5z.�sD�+��e�<hR�% �&�_�J�W�m���j�}8�s$�z�*�E@�o<�B"�%%�Z�i�n(A�i�F'�I�/�#`�\�1�G�6(�;F�\y�y�0�
�!/�T�)�,	�*��y��}-�)&�� ��b�i$@
;:�	�9�)�v�a��q��*>���l�7r�43�@�):��A�L0���0�;�r��"�7F��*1
�
�:)�5�{�y&�%�r �[�`�M�p�b���<��m�!k�(�>�{�/�E��z�v5�G@�p�"�Y�.'��[�)�"�{�@%�i:4�z�!��Y�O�3��W �L�`�G�!�j8�W�J4��Z�W�d14�#��<%�
Q�i�'�j�` �'��3��W�z�-�j�
�0�7��="�F
�E/�X�=��)�Z���&�
�9�y:�,��0��s�2�U�L�T�C�z/�a�!-�[�j��7�Z&�w�2 �m�o'�x�u�6�<��	#�t�M�/�h�N0�G� �Y�8M�
V�O�pO��e:�1�P<�YP�fiP�j�3�^�Sm�T��'�B�t�K
�F	�;�C�Z�2�o
=�>�q�F�0�h�M�D�X�q�g��1��o�\�O�x�E�V�����R	�~�]�j�z�	�>
�
�O�b�Q��#�l�a�%�Z�$�!C�F锁�q 	(B=d"<"	
""8$	

	

#6
)
'"



��s=j*)2
2�	�|2

_�-�Z�@�K���~ef�5��X�_��o�$�}�m�YL �D�rFs�Z��k�t�!�,?�\�
�^��#�Rm�%��0�7�i
?���	�=�,/�m/�l�"�#��}�|Y�O�%�W&�q�p;�~��:q �
�1��('
�B*�q�K!�O1��=%[�b�Gb�^-�@*a�s�	�
�~�
(�e�"�]i$:� �g�C�[0��TQ�OB9�R>� �H�B��!��!� U�1�bV��w�n6��}�$;L�i�H�4%�E�F�,�G�>�]�+�Tg�E1�8	�9��$�H
�W�S�X�0��
�w?��yI�\�8�T�\�#E�u�f�:�#�Z"j�HJ��x�-�+�9]�$�b�sk�`�;Mem�<�G�=�<+�A�;�:%0��2�.�]>�^	9�oH�p
��m�`6�S��,�#��"Wf2�4[�b�[�,�jk��"�Q��w�>�A��h�W�z�CS�7�#�P�Q\�z�|S�_[�I	���R�O�|�H�oQ�{�4�Z/�7O�R
�k�[s0�+=�I��b-��|�N_�,�=4�D�b�+�,�'�os�\.�A&am�Q.�4�n�3��6(�7�b�V�3�r�>�"$#�+��d^�i�^%�2��1S
J!��F�L��b�#�2c��M�"C�>J>�.�)� �/>�8�o�[��Q	��Y��e�^���@
��\�"�	�J�O�N�;� �K
�(	�!
�6	�'���?��L9�m,�6�$�1P�Hi��A�4�F%�%���7�nJ�a�
bQch�E�w8�D�I.�6v�8F�]�l��	��;�D�n�F�a�2�a�0�/
)�c�j�e�@$�sK�1�<_�w��c�T�)L�l�5��^u�:�SU�
�Md�>e�9�4�O�99T�I?�i�Z�;�5�o�G.�1<!!"��J:�*8�M�q"�N]�(0�3��,�F�g�*�]2��y|�K8�D�Q�.�z�o�n�d��}l�X6�4�e�G�`�V�.�K�#
�P��
x��
�(�B
�S-�R*�[r�I�d�;�H�G��N�#�E<�T;��-��26
�.�p(�X��2!��p�Q3�"�/6�)�i�>=���:��4
�Q�`�+�}
�2Cbj��z�`�F�>CM끋td�-d��t��d�6�0�U�L`�)O�38CJO��Q
�kQ�n�|�p�/��B4�a'CSJw��%�7,�s	�6�X,�`^��/W�o�Jw�Y"�n+i�E�����K�+w���
,'+�R��
�7�N�j_�2�1����j$�kR�f�}�@@�_�K�Z^edC�pY��h�*�G�=U�8�)�H�fa�g~�uV�S �'�8�:�`�5�5G9�	�,�o�F�GF��6�^�$�l3�k^�@2�`�&
H7�qA�#�V(]�S	m"���)�&�u�c�.�2�Cv�8C�UmQ�%N�^��7�q�
�{�<�mB�7�x���o
�?/�a�� (�2�&��TM�D��V�,�
�N�>L�r�2*�^�-<�9�3r�{�84�_&�D�A*�2��L�)=�E�I�%�,��6�g�'��a���m�@.�<�[�T�>
�%�_�
�S�7�&�D
�W�z%��j�,���WU�n%�Lm�y/�i�*��T
�8�w�5�H���W��
�>�+�
�p�@�	:$�Bp�<�5�^�j�J[�M�<�B��z)�w�$s'�W�n2�[�&=�rD�F�g5�7!�;*�,�[���E`�H�n�9�.{�r��_
�)�o�|,�+
Q,�p�&����R�Z�
�"�i�@�w��c��8��s��w�N`�/�n
�g�P�\�3�H�<�75�.=�*��2�C#�f��Lt��P�d�MA�F�G�f�{�6
��e�l�6�-�c�:�9�V(�V�-�F'��E�v�}�H�Z�}��=
�z	�y�*�W�u�i�.�g�j�3�d��a�yd�x��g�V+�W0�R�b3�G%�p
�5� �#��@�e�\�I�N#�{�k� �p?�Y"�R+��/,�)�1$�.�Z��>��
� �q��
�L�+��3���wL�.E�i�z�C)�S�<?�Y+�@��n�7�G�O�W���}=�X�=m�(&�{:�AC�n�
�m&�t�(�1�2'�J�{�f$�;�nA��J*�G�4*�)k��V�I*�n�T���:�0B�'���*�K���}.a�R&�'
�L,�J�O�._�S�	�
�C�`Cp��}��� ��|o�h�;	�8p�It0P���"�}�
�e�T�L�P�o�M�	�&�k�?��N�%Cn���&�oP�w=�:�i��R��D�sq�!b�$ECyg��yC�;��kCsmt��9�-�r�+��3D�sz�l�B��!a�rG#�$:?V�eh�1��2�3*�X/�Yk�D�,�0P�u���B�u�Y���Y�^�	�}|#D%i�g6�;�<��=$:c9 _j�k(Z�]�A�@\��}�8H�u�$J�%*�T+�#"�f'���<@�M�'�D�%T,�X�%�
4�/�.��/�3�r�#+!��6�8�9�y�(�v�qA�&��! Xd�e�d�T�f�U@��G�*��V�V�-��]�s&�~D�}Q�P�y�Q�]^�U�8��@�Sc ^c�s���m�!�2�9�J�N�&�(�B	�W	��y��e;�Y��2@	�z
	��o�>�J
�"Y�F���[��d�z�|����'��*�P�V�Z�a�e�j�V�.�c�k�v�z�~����� ��
�&�u�y�a�;�{1e�������*�a�j�J$�Q�d����n2�pW�.�$�#�&�z�~+�}>� �:�3�$�A�9�,��S�7A��`�1��;I�s3�?7�P�5�
8�/��\�;�X��`�[(��t�y
�d���G� �4�6���w��?�+�A\�;#�/4��"��\2�	�_�	�9��5 �p�=Q�N�8X�;��B�7�T�(�Q��0�B~�_�nR�Q�A�g'�(�0�V�rg�E�Z��0i�\
��d�J"�=��u���i:��"�p�j'�O3�'~�{�=�g���H��E�2��S�\$���'-�c�\�^�r,�+2�W�(�&�9 �0�%�n	�+D�C�?�F�k�=�	�!�'�`w�C�E�L�4�H�Ei��5�6�^�4�V�/��O���v�}�:>�R�?�n��-�L$��K��5�#�k�W
�#
�^0�+�b�7�_�0	�k�k�s��S��]�z��*�R���
������G�K�Ipt� �M�_���	�M�m���c�/���2�!�(��-�h��6l�*�='�#�8[���=�5�0�U�6��P�[�O�?)�
�>�x�M�p�5�4�`W�A�L�J�~�s��lJ���{/�&�u�;=�V{�o	�a?�A�J��Hd�FO�8g�M�z�b��R�.�D)���v�z�v�S�k�'�{�w6�nL�oi��]�OB�G��J�L�1��K�T��1N�#���c���|*�(�"�{�D[�{`��(�7�:�8�D�Ov�#8�;�=;�Y��-�L�^���,�u�o�+�Ha�I�(�j�:�4)�e�'�t:�9z�8��F�%�?�|��m�VK�I��8�,�*(�Y�i��Y���<�/�bs�`*�]�)�` �*�Z�]�c0�!�b��:�n�%�^��S�w"��{Q�z�4�`��;��=^�D��9cPc�J��[�L�z0�}�b�s�*c^��Q��^���H�=T�A�M�W���N�#�d�-�6�@��H�F�n�\�u,�x�{�i�J�M��m��~�	�
�
�v�cFN��a����n�x�#	�	�K�8�~�r�o�U��N�uN��)�t�>�'i�$�T�e"�h-�<�L�I�8�l�E�@K�T��v�m��@��!�1�3�B�/�6�B��;�S��V��a��g��
�q�q%�s�7�v9��k�z�&�B��h�h�%�y"�q�g�gh�p�n��
��b�!$�%�(�R��>��\�O��P�t�c�~����c��t �v��yce��ucs^�j�(�t��	�$�c.V��B�c�Y�t�6�a�`���[���5�@�A�B�G�E�#� �.�5�4�X�d�;�s�p�E�C�V�.�i�^��M��Z�'�X�<���z�+�o��4�z�%�G�����k�(�X�z��
�;J��	�>�1��t�l�[�O�;�U�1�1M�58�~cgQ-��z�p��J�~�	�|���[�M�%��O�P�Q�+� ��U�1�Z��G�:6�b���L�r�C�/�X�]�[�	�G�(�t	�
��;�"�F��S�9�m��>�Q����m�n��i�R�
�]���J�'�_�-�
�,�>�I��bw�Y����g�l�3��B��%�w�C�B�b�|�l�L���v�N�I�&�9�P��c�O�g�o��L�n�sQ�7�v�'L�yh�Ey�|U�YT�Y�-��K�E�Q�I�<�O��s�y0�/�+�4�`E�=�p�sL�A�D�~�Fc_�j�q��h�-�K�:9R�A	�7U��5����2�;�|��y���m���[�+"�0�V�Y�_�d�f�k�p�y�}�
���(�1�8�Y�^�g	�l�w���!�&�3�<�V�l
�s	��
�"�'�K$�`����*�G9�P��+�4�S�i
�r�}�3��E�d!�{�� �"0�<�n&�u?��^�jC�o�3(�F�p	�r�}F��]
�vk��h�3�:�n�J(�a�r���p�2c�#p���Y�d�k��C����+��2��T��g��o0��	��1��<��W��j������
$����8��>��M��Q��S��c��m ��t��
����'	��=��G��J��`)����-&��0��X��f����!��)1��8��k��x�o6�q(�*�T&�_�c��N��U��A��
�!�#�%�+�4
�=�A�I�M�Q�S�U�X�]�e�m�s�w�{�}��	����!�$	�'�1�4�6�F5�P�U�\�^�b�e�j!�m���1�4�:�>�A�F�H�L�P�R�T�Z�]�_�u�w�z�~�����#!�*�L�Q�U�`�h
�l�w����#�1�:�D�G�K�O	�l�v�{����	���%	������#��'��,��:��@��G��O��R��X��a��o��t��~����
����9��5Q��2�]��Pc�H#����*����!>��.��1�C��F�#��I��R��X��]�O��O�H���4��t�9��M���k�u��i����8������F�K��R�Z��]J���)��/�M��N/��Q��S��U
��[��f��n	��s��}������	���������� ��'��+��2��9��>��B��G��I��R��Y��\��_��b��q��s��x��z������
����&��2��8��>��F��N��Q��T��[��b��k��t��z������������������'

��.��<��>��B��E��G��I	��N��V��X��\	��a��d��h��m��r��z����������"����������#��'��)��0��C��G	��M��O��S��X��Z��]	��`K��h��k��r��y	��}��������	�������������y��0��7��:�K��I0��a��T��4��7��<1��?
��s
��v��	��������
��#��1s��B��E	��R��\��b��e��i��n��r4��t��_��|��#��*��5��=��@��D��L��g��l��}��
��F��)��+��0��3��:��>��D��F��R5��^���m��F����%��-	����5��\����V����p��"PK
!<�B����3chrome/pdfjs/content/web/cmaps/UniCNS-UTF32-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�UniCNS-UTF32-HC x��)�����Wc0������
��r�PK
!< �T�}�}�2chrome/pdfjs/content/web/cmaps/UniCNS-UTF8-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE���?���?����? A ��%�%�D�(�	�C�_�<�O�H�g�^�j�g�hA¨�3�Z�Y�[��x�x�@A3À��d
.%
.%�CU "!"�_:a*.)�aA %*�s;<
�M	B4–y
'
�"�D����U�m�,
�{
Y*!�U
�s(�o�y�q�F�^�7	B№�f
�wBẾ��n<�@;<��*�R═�B��%%%%%��i��%��[��%%%��e��%B(╯�A�X!
�w
�H�&�_�t�v��}���"��	�}�$�b		OFB⼀�Ss|}�����$�+�@�A�Q�\�S�`�z�e��UBB⾢�B⾮�"B⾾�xB⿒�	B⼐�n
�R$H�>�QB⾘�gB⾽�3B⿋�`B⿌�B⿕�
B╴�0���~B�_▓���{�C�!j��.�J�R#�E�~�Z�X�T��D��K��F�!��J�V��\
��S�6�Z�K�}�b	�h�M�\�sz�)�l�
�^�g�	��<�;�I���A�^�_�e��\�Z��x�D�u�>�A� �9��t�0�-�u�@�p�5�I�K6��U�D�u��o��T��_�|�E�V���}���s�s�D�`�;�]�
�|��g�m�b�g�F�C��P�
� �p?�\�_	�f��y�p�J�w�|�'�(hD�B��,�O�|�c�	�r�]�
��5�{�(�,��E�r�t�%�n�R���"�A�C��
	� �<�/�C��)���X��o�Q�~�N�x�7�:��,��g�I	���P�5�,�1
�\�w�"
��Q�e�0�
�-�G�&�k�y�%�:�M���
�B�(��]�;�s�B��1���~�B��G�X��0�b�;�!�j���u�d��g�@�.�j�Y�"�>	�G�\�P�9<��I�
��c
�Z����S�H�=�8�)�L�H�0�w	�#�y�o�h�<�J��~�y	�t�W�n�!)�F�
�t�U�F�w�#�h�n�n�S��B�^C�k�O�+�7�	�V�w
�Z�k��1�H�5��[�
���X�=���E�T�z�y
H�%��6�	���C�?
�`���g
�p�Z�&�6�|�{�e�Z�M��E�.��C�2$�7�]�Y�&�u�h-�_�i�*�s�^�^(�VD����i�
�[�B�p�N��{�K�{��f���T�L�)��V��6�}�o0
�P�x�t�r�s�X�-�w�l�w�'�5� �x�O�'�P�)��B%��A���<	�W,�^�_�G!�g�p�I�6�i�"�n�[�+�#�E�\�~B�~⺀��H�&
�r�s�G�d�e�X�	�{��W)���'0��f��[�;$�l���R�r���e�Aef+�Z�o�_�?�MX�v�/
�R�-�Q�Q�]�f�_�M#�6�s�N�O�z�#,��~�#�g�=�~��x���&��^%�/�M�6�M*�`�P�[.�i���J��%�r�P�M��c�#�o�
��H�w�Y����e��r�
��#�b�L��S�%#
�O����X��)�#�R�'��I�b��� �.�`	�h�c	�:�;�L�z�y�(�V�g�t�x�
K���O�L�k�K
�-�0�^����|�r�I
�1'�:�u�7�<�D��!�>�Q
�P�u��|�-�2�3�Q
��g�8�#�)�h�`�4�f�+�^��P��a
 �b�Ewr��a�a��B]�[�\�lD�	
�	�'�T�m�f �6�X�e�I�	�z���{�~�7�M
��W��R�K6�P�u�v�b���M<�T-�T��|�}�!�|� �n�x��u�k���`���@�l��&�7�A�P�C	�4D�E�yD�a�h�
�J�d"�.�.�[�Q�T��grR万�q���=�J�KB且�r	�p�!B両�mB业��};�'R
个�W��Y����=�J��,��y�n�s�<B"丶��$T�8�5�Cx,P�8�d��;0	�	�n
� �C��Gz�� �#.P�,�pB丼��EG
"��M�B丷��>�C��<�;�f�
�/�G�~�%�$�1� R	亞�-�j��,V��^��7B
亨�
�6�/�E�


�&3B
亶�c�K�M,	B	亷��<��\�k�<�N�]�P�u�7R	仰��l�u�~�o�t��`B任��A	�dB伀�;	�nB仾���D�'��/
��R
伶�.��F��]������{�B佃�%.5*+B佁�-�2B佂�K��7R佒�+	����&�+
+� ���y�|B佧�*�[��3�2�?R佲�r�����~��n�qR侀�y�'����}���n��}B侍�2�%�BB侐��$
�D
B侢�G�l�9�1�>�-R俇�g�����Y�X�^�k�CB俓�\R俖�h�k�d�W�Z	�k	B修�L-%��A(�B俬�l�n��O
$B俤�f�-
�~�Q�k�d��E�^�eR們�A�<�7���\�A�DB倞��=�Z�K�0�WR
倥�8�j�Q&�,�5��|�;B	倳�%�T�E��t��I�C��R偅�F
��~�y�~�w�|�uB偑�Q-�i��H��9�R	做��~�	�|	B健���R	偨�8�a#"����~��PBH側�	�B�\		�:

�F
�|
�z�D
��R�z��?�@.j�2��V��jBG偲�> �N�V

�f

�A	�T�V�C�v�z���n�B傁�N��a�[�f�u�i�x�D�i�f��/�D�>�Y�Z�
�j�VB傼��QR兙��0�1�h�g�B�x��;�X�YB入�]��@�t�S��<�P�$��E�k
�`�^�$�e� ��*�m�L��vB冀��R�r��g���j�Z�8B兪��<	��c�L� �S�X�8
�5��B�i�+�*l�J�]R准�X��"��e��$��-�9�D��$��u��t�A�B�#B�=凜��
�e�F�>�2�D�}�0��E�/�B����h�g	�H�U�T�{�(�
	�	�E�X�V�/�!�N�Y�v�A�D�K�@�=�6
��G���<�0�B����c�R��f��(���A�2�K�J�O�p�[���~��s�D�,	���F� �S�*z��K�W�F�_�l�S�N�9�&�.��(P��t�g�d��8�r��v�[�,�D�*�)�@�q$���2�d�e�f�X�)�,�0�L�A�s�>�W�J�s��H�%�B��g���,�O��q�@�EB凗�u� B
凘�"�D�e��$�#�J�h�{�3�<B刂�3��o����%��B剙�d�
�|B7凛����l��[�F�D�#�F�,��s��-��O�>�R�(�'�i�^
�-�>�U�I�:��m�2�O�K��N�?�p�`�U"��U�Q�2�8�,�F���	�5�D�{�X�
�	�T�3R厤�A�����s��v�B�5�|�;�3��lB厲��_�>�W�G�
�q"��>�Z�&�J�!�@B厴�~�l�oB厰��"�P�/�4�5�-�����o�/�2R叨�	B
叵�"��f���O��@�B��C
	R吆�(�J�S	
��\�CB吖�R
吘�G��>��'�f�.��PR吣��#��/
�{�~�s�\�G5 ��(��[�E��.��O��B吸�V
'�A&B吷�@�C	B呋�Q�N�%R呠�.�u�x�{��z�_��?��hR
呯�,�0�-�2�O��
	B呻�h�BB呺�%�A	B咅��YR咋�u	�r�i��R��g�d�m��v���e��J��7B咚�r��f�R咠���@�
��Q�I% !�P
�A�V�U��D��[��"R
咶��S��(��g�+�F�Y�Z�GB哀�Q
B哃�|B哋�#�|�E�T	�nR哠�W�O�(��v��o�C�&�I�6B哭�o	B哱�RB哯��,R唁�f��J��g�M�.�;�B���7�A'�T��B��yB	唗�F��L��E��8�
�K�����rR唪�a��p��S�z��r�}��o�X�{��|B唻�p'�#��8�@��q�{R	啃�&������y��r���B問�*�.�/�2&�A-

�$B啍�^	�6�AB啓��J	�U��c�.��0�pR喆��.��Q��q)��f�_����
�� B喘�&R喡�W
�/��T��W	�j�yB喱�		
B喵�M	�n�@	-B喰�u��J�7�
�D�R嗇�Y�d�S�6"�o�`�G�V�c�X��N���9�c�8�G�N��H��%B嗡�_�o�h�
	�A
B嗢�

�R	
�A
B嗪�|�U�j��A�I�IR	嘏�.�!��\�C�9�(�R	嘛��8�f����@�^�
�>B嘥�x��!����!R
嘲����n�e��0�7���B嘿��N�pB嘽��A	
�
�

�rB噃�~	�z�"��'�m�OR
器�	�Z	�W�P�U�`	�]BD噶�"�F�D	��V�Y��C�:� nP�*��~�H�M�H�Y�X�]�R�Y� �y�Z�H��<��M��+�<�C�@�?	�J�K�A	�R�SB圇�t�J�nB@噷��f�bj�M�A�F�?��M�K�"��9�D�G�		�l��o�J�%�C�)�^����
�			�t�-�B�:��/�f�^�{�f
��c�B�"-R
坉�T�_�^�W
�^B坡�}
�E�L�]�\

�S��#�B)坢�A�B�&


�2�A
�H�3B坔���@�I�x�)�l�
�+�H��!�k�>�B�6�^�b�m���E�\�^�R埜�y��v���%������B埩�R埬������L�S�m�x�y	�j*��"R堀�{$��� ��{�5���v�B堐��z�)��
��AR堛�e�
�|�p��o������`�m
�B堬�m�%R
堲�^���1�L��	B堽��q�@�4�pR	塇�B�A�h�Q�PB塑�g�@�O�ZR	塗�i�f�e�f��T��]�k��pB塢�p	�f
�A�z�{�~
�>�C
�~�BB*塣� �:
	�A

!
�J	
�j�BB塩�e���\�^����T�1�V�g�6��%�O�:R	壎�B����/�����B@壘�M�Z�&�"�K�
�Z�V��q�@�:�B���@�"�g�>�.��Y�X�c�b�Z�7��N�]����R�Z�[
�F�I�A�N�'�~��T�w�X�/�P�&�"�g��U�Z�v�:�G�zB壚���
�i�B壜��Z�^�C�]�B�H�Z��	�/��3�P�p�R
奰��F�,��)�d��p��q�N�CB好�;�C�
B奻�X�@
�
B妉�0
�/�SR妝�q��t��)	�S�8�]B妬�R妮������m���>�;��
�x��
���HB姀�SR姃�O��h��]���.��5�t�y�r�u	B姘�uB姖�P�4	#B姙��qR姨�x�f�c��:���N%��.�
��!��>�Z��)�K�L�uB威�~�,	�p
B娀�!	�&
�H�@�E$B娂�
�v�#�K�\�O���M��c�`R婀�K	�Z!�\���D�EB婌�/R婐�(��@��W6!�8��M�4!�8��M��Z��1�/��:�
�O��n�C	�BB婰�0�R	婷�#�$�T�] ���YB	媁��&�a��B��IR
媎�+�8�Z�|���D1����R
媙�
��M�>+��~�-�QB媤��C����4�l��R媮�|�r�Y�n	�i�fB嫀�0�y��4�eR
嫆�.�o�v�_�z�&�mB嫑��F�	R嫕�P�9�L
�O�J�A���o�=B嫨�]�W�<
�0R
嫲�u�A�*�}��v��B
孓���X��2�N� B嬈�/�r�8�5�H�G���t�>�~B1嫽�,�`�g�A
�����V�&�+��
�2�7�d�a�>�c		��D�^�v�q�J�K��u�\�\�8��}R	孰�Q�0������w��$�W�kB孺�F��\�@�7�p�U�T�9�z�B�A�@�hB孻�XB孼���E�
�HR	宕�Z��n��q��pB>客��l�
��G	�x�@�F�I�z�C�>�G�@�I�@�P��>�M�D�E
�h�0�1�n�{��,�!��F��j�C�|B宪��{�X�-�V�R�Mq�EtB宷��G�c
R尌��#�T�S���0��r���/B:尚� �2��m��*��E��E��i�&^�.�Y�$�D�&�w�P�R�W�(�B�C�>�W�P�A�L�>�(�_�J��Y�	�8�7�.�G��=�4�I�HB屭�B尜��j��>�<�e���5
�7�f�L�u�T�wR岜��Z��a��~��
�u��w���t�	B岳�*
�Y�J�.	�C
�p	
	B)岵�`�O�0


�n�w�x�@
�p	B岺��+�F��Y���C�+�
�<�S�C�"�#�cR
崮�K�����4�U�J-$
B嵇�5�B崼�(�@"),
�.�M
�<�[�TB崾��)�G��
�a�a�k��kR	嵷�i��h��]��J��?B嶄�C�dB嶀�d
�B嶅�4�^�1�@R嶧�,
��!	
	BJ嶸�L�K��6�$j�#�@��4��/� �G�^�,�4�:��w��v�'�U�v�D6�7�P�I�H��~���	�
��<�x��n�9��<�A��&�A�'�h�i�d�m�"���X�WB嶴�5�Z�H�~��P��^�*B嶶��X�*�G�_�X��S�<�m�F��S�T�iR	幛�E��J��e���eB1幫�M��Z��0�5�L��3�H�^�(�R�'�@�x|�+�L�I�D�S�F�>�=�:�|��2�4�?�>�?�8�u�|�9�:�9B幦�8�i�0�G�&B広����#�=R廄�?����$�D�qR廐��1
�e�d�_�V
���!�w����!B.廥�>�{��3��K��
|�A�^�P�'�J�6��l�{�x�l�g�\�P�5�X�M�:�q�6�~�l�?�:�/�:�;�:�)�C� �Z�cB廱�P�^�\�S�B廹��C�P�L�E�BR
彈�A��?�"�c��,��E�$��Y��|Bj彔�v�@�A�n�W��I�T��l�+����|�e�f�h�`�&�K�F�G�J�t�w�B�r�=�|�y	
�&�L�7�0�)	�2�/�b�Y�Z�[����t��x�}�i�b�c��^�C��}�^�L���&�'�5���`�t�u��j�u�C�B
�W�J���U�N���@�B彜�Z�n�y�G�0 ��T	���I��A�HB忂��\R怉��M�?�
�]�X��tB怙�~�m��a�JR怠�!�-�x��x��=��4�c�.�/	�p�B怵�G�M�D	�	B怲��.�F�Z�[		�L�]�X�eB怱��a	�b�N�?�R恢�(���
�j�L�U� ���f�e�'B
恿��D�
���B恲�\�M�p��{��s�r�
B恵�V�\�}�_�D�S�KR悰�\�=�����t�<�'�>�/�#� B惀�l�,R
惃�j��[����C�Z���/B惑�E�iR惗��5��K�"�� �1�(��,��O�j�^�|�)B惦�	��t��DR惰�N�N�_�w�Z�U�~�)�g��b�L�M
B愀�V�nR愃�D�K��8��M�u�J�x��a�	�h�|B愒�W�r��~���x�D��z����pB愓�?

�x�I�T�O�p�D�e�F�Y�^B愙�v�n
�o�|�D�
�R
慘�X��B��G�p�j��}�x�_��j��qB慤��s�E��1�k��
�zR
慮�G��p��i�r
�i�V�eB
慼�K�C	�L�?�<�3	B慹�P��E�
�|�a	�Z�_�X�SB慽�=�C�r
�5��ZR憧�O�f�'�T����~�G�`�L�2�[��
�[�d�kB憼�a�[�,�@���o�R	懅�E�k�9�2�7�<�h�/B	懏��:�s�1�$��n�yR
懢�E��k�(�#��	B懰�$�IR懵��(�|�}�r�?�@�A�$B"戀�x�O�,�O���^�����d�y�z���<�:�v�w�	�t���@�4B戁��6B戬��<
�E�1R	扆�!�?�,�~�b�q��0��7�B扑�E�"	�(
�PB扐�)�
		�L	
B扨�R技�&�b��Z��q�W��X��5�8
�cB抌�R	抐��@��
�:�?B抝��[�.R
抨�c�t�s$�B�gB抳��U�Z���q�b�{R拁��Q��o��h��o��b��5
��q"�v��\�sB拕����?
�$��V�@R拫�t�q���P�?��d��z�
�B	持�5��%�BB挀�z�X�K�`B挘��A�IR挱�e�q�m
��H��]�t�S�;�j�-�=B捀�<�M�<R捆�K�T�a�>�/
�RB捕�I�.&B捔�3,
�j"	"B捤�G�R	捵�~�+��J��@��c�#B捿�K�@��3�*1��6R掇�z�
���0��[�\%���3B掔�L�_�0�)R掛�(�,"�W&!�v�P�IR控��&��	� B掹��W�VR揀�]����	#��
���l ��Z�g����	�R揕�Y�g��{�f��
�e��6��=��J����7�g�g�xB援�j�L�t	
B揯�R,�z�
�R�$$ ?
+	45&�<�'	B揸���^�M�H�HR
搲��Y��W��.�+��8�=�R�eB搽�!	�R��k�^B摀�B搿�.�KR
摘�\�(� �O���B摧�c�j�i�hB摥�2��}�bB摱�[R
摴�	(�)�v�u��
�B
撇�[�
��B摿�-�B�|�\�U�'�0�B撍��#�>R
撫�h��t����%��$B撾�E�A
�0�=�*�)�(�9B	撽�S�EB撶�d�I�@�	�4R擠�Z��|��u��Z��}���w	�@�?�B擰�[�h
	�A�Z�[�Z�&B擯�h�#�&	�A	�L	�.�%B攊��6R攛�R�?��(��}�i�*�1B攩��?L�D�
��k�,�X��[�T�<�{�P�]��A�~��x�	�$�	�j�s�p�|��v��:�E��]�1��L�@�(�N���8�}�*�H�w�N����R�T���f��	�Z�Z�Z�@��~�d��l��/�b��e��]��~�A�W�R�S�J��
�d�$�C��|�N���W�6�	�W�X�"�>�L�I�L�Y�d	�B�cB攰�b��W�
�s�n�W�H�{�+�[�Y�R�\��
�6B斋��^R昂�{�\	�g�L	�I�T�e�d�a�b�_B昘��1R
昜�	
����z������B昫�
R
昭�G�������rB晁�bB昹��L�nB晄��4�7�:��]�aR晙�����n��F��5�x��
����X��]B
晪���#����t��R	晶�z�(�-��z��B��;B暄�3	
�s�j	
�p�
B暀�|��J�B暅��b�3��!	�xR暭��H��/��	�
�t����6�1�_�6BZ曄�J�6�l�T�(�6��C�>�j�E�&�T�_�C� �
�5��&�_	�F�R�d�0�|�{�V	�0�<�A�0�A�2�E�H�=�<�I�N�G�<�I
�8
�U�N�M	�"�6�a"!�d�Q�D�[�h�C�X�e�A�h�_
B朁��p�K���0B 暽�c�v�@�w�"��+���J�j�m��L�P�~	�E��Z�F�Q�I�n�B�M�2�k�H�r��S�HR枋��p����]�Z�A�b�B
林�
�V�[�f��r�,�]R枮�4�;������
���}�'B	枿�z�@�;(�?�"5R
柋�<�/R
柖�X�j��e�o�.;�e�~B柢�f���*R柩�Y�B7��8��&�9��g�"�;��t�?B柿�P�S�VB柼�0�U�XB柾��U�A��i��=�V�+�KR	栚�K��L��=��J��q4�}��PB
栥�d�c�6R栴�[
��0�\��k�l)�/	��$R桀�w#�@�?#�����D�����=�^�7B桓�k!�PB
桫�)A@"B桕��y��R
梀�,�7(��n��i�l��@��	
B梓�T%B
梏�%>!/2'(B梘�*R梦��d���z��x���i�N���B梹��NR棃��\���� �'*���&��3���"R	棐��9�fR�Q�
�B棚��X�w	���x4�9�&M
R森��� �E�s�.�?� �*�
R椀��_�8�?��1�w�D1����kA�]�f�	�t$B椰�>�YB椗�$
U�p+$C.1�@$) )4;BB	椘�"�z�A��t	�\�#�D��UR+楑�<�u���S�"#�S�`�g	�&�9��P��%��$��c)�|��)�X!�9�H�_�?�,�3��"�Z�M�q�h�q�b�O�:8B
榀�>���^��Z��/��Z�W*1R
榓�W�A�p� 4��E�?R榞�Z��;2�,��a��E��3�4�A���2
�r�A�;�JE�B榻�w�E
B榹�R	�B !$B槀�]R	槉�>(�?�(�7��hB槓�s�B
槔�Q�Z		&#&B槕�<�B
�
�R
槶��TM�c�;� R樀�~�
��P��5�zB
�9��4��sB樏��7R樓�}�$>G6�E��F�)�:�;�%��B
樣�u�F	�F	B樥�'�L		'�@�I�H;B権��*�Q�P��w�O�x�c�R
橏�|�b�s�_�,���HR橝�o$�Q�J�U��R��J�o"
�QB橾�l�AB	橭�p�$�AB橱��b�i�G�j�[R檌�+�8��[�(
�/�0�R檙��e�?�!���o���%�,�:�EB檬�]�FB檭�>B檫��g�)��8�R	櫅�4�p��J�[B櫏�$�)��>R
櫘�*��-�=�l�5�<
�IB欠�^��l��hB櫥��(
�8�H�l�
�
�f�P�$�%� �l�A�$B櫧���w�r��)�m�f�C	�"�E�4�L��P�	�R
歅�C�q�v��@�E�@�=B~歐��>�H�]�Z�5��7��A�Q�Z�,�H�>��A�,�����J�]�@�X�f�)�(�)��[��l�5�.�3�2��<�A�<�D�~�5�h��_� �=�&��L�n�2�[�@��b�@���R�{�v�D��@�Y�<�'�&�!��P�l�\��2��y�4��@�H�e�f�?�0�h�N�<��I�V��Z:�	�4�E�Z�=�$�Z�_�|�3�p�2B歗�.��B歒�n�L�3�b�F�x��K�N�i�v-R	氳��=����	��8��o��f�T��B氾�T�A�p
�?
�H�=�RB
氿�-�C�*�v
B氽���K�i�'�"R
汭�:����'�C�>�3�B�A����B決�T�B!,/B汸�0 �D�D�c�L�]�0�%�JB汹���Q�_�D�2R沪��o��	�2�G�������P��M�tB沶�S�S�R�G�@�^�k�N�Y�NR泉�p�m��j�����P��K)*�e�L�Q"R泙�R�v�+�D��b��1B泥�%R
泧�V�k���>��{�+��%�P�yB泵��V��nR洀�U��0��-��0��)-�9��g�*!B洗�y
B洘�Y*B洖�R洤�C����z��e��!&�01�	��O�a�8��8�1�"�1�B	流�t�4
B洿�K�@"
�H
	#B济��X�K�P�
�[�=R浴��A�U�6@
R涀���B��Q�s�R�s
�n�s�R�+��v��3�W'��.��3B涪�'&B涫�FB涤��v�{�zR	涷�M���x��9�}�\�B淀�E6R
淄�
+�2"�G
�lR
淏�b�U�F/ &�C1*�&R	淞�|�,�C�4:�o�D�R淨��~���'�n�6�+�6�o�V�5��B淹�}�I��}�VB淼�?�1�B/�
B淾��"�C�Q�y�\�R渟�B�-	�n�e�Z��<�q��s�
�>�a�v�c�J�/B游�&�p�K�0�+��@
��	�1�HR湍�9��b�G�.�w��k��8��?��h�q�}�N��(#��D��X�#B湮�4�V�(B湫�^!�	�:�NK�N50B湶�S��I�.�yR準�_�&�3���.��g�]�.�=�H�)��}B溪�c
R溮�t�G����0��W��X�i�y��@��E�~R滀�R��"����p&�g�>����'�v��n�hB滬�B滘�h�>)B滙��i�*�!��
R滴��F@�[��t�:	�
R漀�F�S�>�j�3�s�jC�x��Q�yB漒�{�i"���Z�+*R
漞������X�)�|�mR漩�	"�j�5�T�c#��~�}�;�r/�x��q�|B潀��R�G�jR潎�u��k�f�}�^�&�!�SR潚�9�Q��^�y�(�{�T#4�uB潦�
B潧�J�B潴��CR
潶�;,���V��S�|��|5>B澀�|���R澄��X�S�@�-�2�@�1�?B澐�K3"-�ER澝��9�9�P��7�)�e�z�o�r�\�S
#���
��t��}B澹�h�FB澸�(�DB澻�;�R
濆�1�H �p�C)B濕�


�j��GB濔�

�|	��@�K�J	B濓��H�)��"��J�TR
瀔�G�q�:
�G�2�;�T�O��N��W�@B瀨�� �M�:�B瀤�4�Q�<�x��n�@�^�bB瀬���^�u�d�oR灚�#�^��Q�J���-�\�5B火�f��f�Q�2
��M�.B灱�
�p�G�d

�Z	B灬��q�@���@��v�z�A�
�A�]�N��!��0�u�[�P�s�S�B��R烊����r��e����#
B烘�

�lB	烗�!!

�"B烟��[�'R
烳�t	��^��I�S�2�
�y�C��p�/B焀�z�^�W	"R	焛�|�[��n�/�G�TB焥�v�Y�^�\�nR	焮�s�M�P�6�-�#��AB煆�rB焺�{�F�~
)B焻�E�/�A�R�mR煉�j��r�?�-�6�K���?��8��[ %B煖�t��V��;�9�HR煜�l���*		�!�.���1B煮�N�(�FB
煰��<�D	
	B煱�@�L�u�$�S��_�t�I�*�9� �lR熖��_�}��v��w�n
��t�>�s�QB熨�!�$
�I
�	
�L�G�@�
�6B*熧�d�,�B	�:
	�f�A�P�M�L�TB熭���X�;�V�c�t
�o	�X�)�I��S�j�k�l�A*R
爢�$�N�R�z�G�F��D��E��B爰��t�I�<�6�"��Z��J�A�@�|�h�+�G�:�h�[�x�/�XB牐�IB爮��y�QR牕��}��7��6��W�Q�$�%��Z��M�,�1�p�4B
牧�I���f�B�r�s�nB牪�z�l�x�"�E�|B牦�;�[��sR犋���l�q�Q�z�H�M�y�t�B*犛�#�`�N���5�4	�4	�@�w�|�9�2�7�<�h�l�s�o�h	
�[�\�a�h�r
	�E�H�zB犚�g�.�p�0�B犟��:
�
�}�Z�&�gR
狶�5���%��,B猓�B猀�1��v�	�x�	�rB猂��K�hR
猥�T�p��~��i��2�(�'�M��|R
猰��i�r�y�F��f�?�mB猾�z�D�`�e�V�s�r�&B獀��$��"�~�*	
B獁��l�e�SR	獰������$�b��C�B�G�DB獺���.�T�ZR
玀�J�6��#�L�'�h�\�o�2���nB玖�o�DB玎��	�z	B玏��e�_�&�_R
玤��c�f��|��m��x��u�n	R玲��L�W�f��b��5�DB玾�	�@�UR珂���P��e����o�f�E��|�`�kB珞�0B	珒�BB珕��sR珠�.����I��J��U�|�y�#�,B班�+��FR
珳�+������P�E�(�)	�a�hR琀���N��3�^�e�b�c�h�r�kB琛�`

B琖�
B琑��l�
�e�L�]�
�-R
琨�b�t��v
�e�f

�
B琷�aN�(������=R
瑀� �A�F�j��x�e�|�_�6B瑕�}
B瑍�&B瑌��,�w�yR	瑝��F����F�R��C�}��(R瑧�0�z���q��p���q��m�f�z�x�w�nB瑾�)�AB
瑹�|�$�A�)�Z�3B瑺�Q�G�4	��vR璗�%�S��l�O�z��!��$���R	璣�{��h�	�5�B�C�0B環��
�E�J�D�B璭�*�b�L�Y�D�;�A�JB璴�%��L���/�R瓓��z��y�&�+�,�+���k�hR
瓠�&��S�2�b���k��H��a�]B甄�1�/�����s�^��M���t�p�o�,�HB(瓬�� ��@���(�<�Z�Q��<�o�=��� 	��|�@�|������A�&�<�/B瓰�f��(�L-�0
���c�$�?�F�"��|��X�
���x�y�x�_�H�UR	疊�L�U�^��X�u��-��	�	B
疝�V
�	��g�C�tB疘��~�x
�@	�	B疞��G�5�4�N�k�ZR痗�+��t�{��J��M�y�x�y�p��B痦�'�,R痯�0�Q�Z�U�X	�OB瘀��X�l�{�r
�(�G� B瘃�2�
���	�
�E�6B瘂��I�u$�t�5�D�jR	癌���V�!�2�S�N��%�T�BO癖�w�Z�[�R
�	�P�Q��|�}�6�H�-���4�4��)�t�T�e�"�4�A�
�E�H���3�h�b�
�y�t�y���0�T�^�&�6��e�^�'�`��|��P��&��/�B�h�*�J�K�V�1�h�b�g�n�m�t���v� �.�3�d�CB癳�kB癦�*�|�F�j�E�e�	�T	��jR
盬�i�&��/��B�w�r�s�vB
相�4�B	�"B盷��L
�
�"��
B県�b�v�m�l�YR眳�0�q�X�Y�h�q�j�oB睏�t�:�N�AB睄�1�	B着��`�R�OR睥��f�Y�T�_
�b�nB睲�kR	睷��)�S��S��~�'�	���B瞄�:�x��"B瞀�D
��<��<
B瞘��R	瞯��e���H	�G�JB矛�g�$�%��L�VB瞽�z�G�D�B�HZB瞹�s��J�C�O��S�p� �)�F��J�|��-�&�hR	矬�9�	�P�~�D��p��3��~B矽�^�D�8� 
B矷��F�
�
B矾�U�I���R砥�P�T�a�@	
�/�6�C�P�?�8�G�6B砷�O�J�f
�hB硅�<	�B砹��V�y�a�D�M�L�R�Q�P��U�VR
硨�D��n���B硼�%�I		�.�E�@�v�B��zB硹�W�b�A�i	��y�z		�B	硺�i�F��X!�.���R磇��m�+�;�0�#�$��.�/�/B磚�B磛�o
B磗��r�M�ZR
磬�
�H���?��.��m
�C��"B示�j�hB磷��F�4
�@��`��BB$磹�J	�B�
�A�8�n	�:���f�O�9�V�g��B�oR	祄�/�_�TR祏�v�A�F	�?���d��w�.�9�X�p�w	�yB票�C�H�A�4B祧�?��.�D�~B祱��h��A�*�
��R禐�_�z��L�A�<B禟�^�R禤�u�P���(�R�d��9�N�L�iB禾�k��@B禱� �\�D�$�1�G�fB禰�P�p��r�L�v�nR	秈�c��\����V����hB秘�d�B�D�n�nB秕�0�		��G��4�|B秔��
�[�g�Q�vR	稪��C��G���+��'�6�5��>B	稷�G�6�z��3	�����@R
穃�l�m�t��?�J�v�%�B穗�&�6�>�k�H�/�N�{�|�$�]�@�X�Y�6�@
�I�<B穔�S	�J��Y�X�E�
B穲��=R窏�J����%��N��G�	�p�"B窞�l�v�9R窮�J��`�}�1�*B+窺��"�G��,�%��&�I�l�H��#��6�E�:�;�8�g�^�z�3�D��O�P	�G�V��� �)��
B窻�9�I�I�R
��� �!�b�-Y�_�k�^�L�o�8�&
�MB窼��!�L�c�f �bR笢�R�	��$��-��|�e����B笸�]� �A�D�R筂�mn�!
�-��'������
B答�		�fB筘�Z�:
B筕�G�>R
筬��(��q�o��$�+	�C�R�	�B筻��)�F�a�E�8�=�&R	箊�l�;�B�?�2�HB箔�S�~��z�s�G�=�2	��B箖�o
�b
	�K
�6B箢�K�
�^�Q�R� 	�+�R篘��m�Z�U�^�A�%�Z�M�R�M�D�S�V�FB篲�ZR	篷�+�t�A� �u��q�{�bB簀�[�R
簉�c�i�h"�)��$B簕��a�SR	簛��Y�G
�i�b�_�h�mR	簥��o�n�a� �7B米�w�Z�-Bw簰��"�
��e�^�]�B��f��c��^��M�(��l��*�I�D��]`�P�V���%��t��u����O�r�(���{����;�C�<�

�����a�Z�X�c�����#��v�!�"�s�+���c��L��������4�H�B�6�O�`�a�\���<�$�o���;��J��U		��`��/�B�V�tB籴��� �`�J�~)�-R	糱�!�m�p�	�����K��~B系�{��b���R#紀�O�d�k�d�]	�^�c�:�r�c�R
�i�X
�e�\�m�h�U�P�k	B紫��{
		�CB
紨�r
�CB紥�^R
絍�]�[�/�:�5�"B絛�C�i
�	�~�H�G
B絘�`

�F�@	�B絚��U�k�T	�H�QR	綜�]�P�K�\�a�f�]�4�*R綦�'�:�#�R���N
�a����
B綸�i	�GB綷��CB緍��n��S�NR緖�o�o�?	�,�,��c�B�Y�FB	緣�]��(�u�7�*�9R
緮�@�S�N�7�H�e��:�oB緹�d�,�G��>�S�D�+R縇�M��W�|
�q�b
B縛�#�,B縚��&	B縧�R縭�n�s�h�{	�n�c�p�	�t�e�x�g
R
繀�v�o�`�m���~�k�dB繒�	�0B繌�y�
	
�@��.�9B繛��	��<R繲�a�g�f�c�h�f�G�H�vB
纀��I�z	�Y�Z�.��R纑�"�v��7�L�Q��
�B缶�y�4��M�N�F�HB罃�&�0B	纟��;�A
�L�f0R罇���_��*�	�c�|�8�Y�{�V��F��#�YB罟�z��R�O�:�B罘�C�R��8	��V�L�0�C�JB罱���R羅�/�n�x�{�Z��7�^�6�a��B羔�{�N�NB	羑�D�T��
��z�>B羓��[�+R羬�C�,��k�8�J��F�T�Y�;�n�m�L�4�_���aB翁�}�N�B	翀�2��r�BB翝�^R翥��`�<�=�G�>�3�zB	翲��{�v�v�W��r��s�� R	耀�4�q������)�$��Bk耋��A�2�5���b�a�~�7�2�0��e��|�0�\��i�.���`�R�c�^�_�C�|�7�2�;� �5�V�L�R�U�2�@�E��u�(�\��#�B�T�=�]�X�L�e�`�h��=�4�9�2�1�6�=�`�v�{�v	�i
�Y�^�p��G�H
�o�x�u�lB耝��k� ����w�g�H�B	耠��<�b�k��n��#R胔�}�K�{�~�q�f�aB胡�a�Z�D�2�l�GB胠�N
�4��A��(		�t�E�P�KB胩��D�7	�k�L���$� �9��*�~�d�u�J�~R
腊��9
�4�?�6��#�6�-��TB腛�

�
�R
腫�U��.�S��c�$�-�"��SB腸�Q�P�@	�n�	�@B腶�$�K�
�\	�B

	
�&
B腼��P�G���_�eR臀�R����	�n�y�x�D��3�fB臍��2��<B臌�B臐��0�^�2�^R	臢���� ��A���4�j��B臬���>�*�h�U�\�A�<�4�N�H�h�H��#�\�<�(�$B	臮��f�;�A�<��D�7�"�^B臯��Q�a�a�oR	舚�a��/�D�v�S�k�D�`�oB舥�?����g��*��+��h��%R	舲��K�@�?�@B艇�Z�L�h�U�n�(�B舿��@�:	�^	�n�
�4����+��PB舾���U�(R艴��j����W�H����7�i��B芋��jB芀���x�[B芇��2R芝�z�|�w�`
�}�t�}��`��{
�z��a�t�i
R
芳�y�
�u	�t�=�6�w��B苀�0��B�lR苏��d�G��
�\�m��L��uB苛�mR苞�z�r�6��;���vR苪�t��<��7�j�}�p���t��&
��v�7��a��vR
茀�f�a	$�J�V�=B茋�6��
�gR	茖�S�S��>��|�i	��iB茠�UR
茤�T,�g�T�e�;B茯�WR
茱�,�0�7�X�k�~�N��0B茿�G�@�9�8�=�&.R荇�Y�}�|	�O	�B�M�z�{B荷�B荖�F�0&9
�A'B荗��]
�O�h��R莅�S���b�e�^	�O�V�"��]�L�S�p�y�X�&��>�C�kB莠�R莢�z�@ 
�a�(
�c��0B莮�9��b��W��8�sR菀��?�v��P01�W�L�U��.�[/B菑�6�B��%�X*7�RR菛�B�}�:�O+�f2CR菧��O�38�`�a�
	�d�u�b!B菸�0�E&B菹�BKR=�A	B萅��_R萉�,�e
 �T%&3B�.B
萖��7�{�s�b�=�	�}��z�s��R萫�c�o�J�4�;+�;�X&1�C�n3�	�&*�o���cB葀�8R
葂�Q0-�C�b��t��m���+72R葐�ga@�H�1�J�5�q��H�?�5��l�3 �{��d��e��p��W�-�2J�oR葫�a�v��d�%�C�$�-�Z/(B葽�=F�X�@�t��]����M��PR蒍�5JO�M�|��.�e�b�.�A:�}�\�m�\�c�B:?$�D�oR蒧�9�<�=$-�]B蒸��@B	蒴�1&+
�E
�%�DB蒽�L�.R蓉��f4�O�r�{�vB蓬�{B蓖�C
�V	&�-�~
B蓚�u��t�ar�@R
蓶��h�?;�yB蔀�_R蔂�}���A��[��NJ?���a�z��j��.�5B蔜�g&7H�!R	蔣�x�F�A�
9&5B
蔮�| �L�i�f��Y�:R蕀�F�0��O�
�+
����R蕑�L�8�M.
-�F�=���F�R蕝�Z�7��t�G'

&B蕪�>�<B蕫�A6�6'B蕳�xR薀�.7�m�$�	��w�d��#<�)R薓��J��W�j���u	�,;'B薦�e�p�
�~B薧�B
薱�&
R
藀��H�G
�B藍��0B藋�"�:�3� B藖��	�5R藨�w��v
�B�%R
藶�%�W��	��0�MB蘆�;�B蘀�$�<�5�t�K�	�
�3�.B蘏��B�@�KR
蘞��A�4�#�2��	B$蘭�
��M���j�F�g�0�f�g�H�n�/�j�u�d�u�t�H�d�u�x�q�bB蘬�*�Y�>�Y���A�"�U��Q�Q�~��(�p�`�hB虲��oR蚐�x!����}�	B蚜�7B蚝�wB蚠��zR蚯��`�e����5�w�bB蚿�=R
蛀��h��f�e�r�cB蛋��n�s��wR
蛖�m�;�u�|�y�~�wB蛤�N�F
�AB蛢�E�>

	�FB蛯��qR蜄�i�}
��s�\	B蜓�pB	蜘�
	
B蜑�n()
45
0
!,

�AR蝌��L�`	���{�j����&
�?�,$R
蝥�(�K�R�Q�P!��"��tR	蝳��)�T�U� ��L�
B蝽��ER	螁�h�S��b	�n�uB融�DB
螏�c
'"	B螋��b�g�OR螩�l�m��
�*��I�:��-&B蟀�gB	蟂�A	�nB蟁�|R蟖��)� �?�o�P��X�8
&B蟬�$�0�FB蟪�3�"�/��@��&
��E�4	B蟵�.�N��R蠟��v�{�~�P�.�3�U�V�M�D�:�)E>�Y�XB)蠶�(:�D�o�Z�(��b�v��w���P��r�H�1�Z�i�j�{�n�������@�]�rB衚�/B蠵��^.�B�p���PS�T�v�
�]�U�P�OR	袋���
������?�}�R袕�Q�q�	
�~���-���	B被��x�X�ZB袤�^	�		�A�*
B袮��u�I�E�c� �R裔�|�f�A�1�~�}�t�B	裡��� �E��0�#�(R
裳���R�#��&�+�$�B褂��^�[�L�T�F�B&褁�a�
�|�"
	�B�d
�g�`�&B褀��z#�x�^�C��R襜��3�0	�6�a��"�CB@襩�
����|�q�6�;�J�V��
�A�b�:��T�3��t�	��-�(����8�y�l�|�c��2��7�T�>�_�n�&�c��H��3�J�@�W��r�{�>�D�EB襷�j�Q�|
��JB覀��	�=��o�k�DR觟���z
�>�OBB觴�)�2�&�C��x�v
�L

�|�z�B�{		
	�v	
�H�0
�T�M�/�2B)觫��~��R�
��B��D�@�C

�@�A	
�r	B觶�!�$�6�H��R�\�x��)�P�1�K��E,�Q�m��R	誣�(	����!�*B
誰�	�B

B誯�A�{
�A��B誴��|�W�XR
論��V�^��A�^�n
B諦�M
		��j�A	�XB諤�

�E�B諪�Y�R謊�{�H�*�!�_�T�G�<�?�0�K�F�G�b�c�\�Y�:�6��wB謨�*�G�0B謢�a�X
�H�@�+B謭��U�0R	譅���� �9�8��aR譐�"�Q
	B譜�E�b���a�X�<�7R	譥�:�i�h�|��[	�lB谷�B譯�G��B�	bBL
�"�3�AB譸��H�A�"�+�d�i���
�=8�
�B�c���{�T�b��C��R
豅�A��k�x��:�b�k�`�a��}��B豕�
B豔�c�H��E��'�j�~�Q�P�q�p�F�H�9��F�B��'�l��2�RB豯�p�d��W�7��2R
貗�d��$�E��+�v��D��EB貧�.�xB貣�
�F
�B貭�qR貲�"�s� ���	�"�B賀�m�p�0B賌� �XB賍��H�gR	賙�X�l��G�X�i�^�cB 賤�)	�`�J�G�T�:�x��8��A�@��l�C�x��I�L
��lB2賥�V
�(�Z�	�.�}�b�A�N�S��@��x�o�H�d�[��I�(�]��F�Z�_�B��N�S�B�F� B賲���X���uR趥� �,�-�3���?��4B	足��x�F�G�B趵��H�A�0
�NB趲�	�M�@�.R跓��z�q
�%��#B
跟�'
�"B跠�-	�6
�@
B踁�R踍����M�f�w
�L�M	�TB踩�9�Z�HB	踮�e�
�@B踨��)�l�0R
蹄�a��V�1�_�\��B蹓�w�|��nR蹙�0�
�u�l�s�v�{�z�8�1�B蹬�S�D�zB蹭�:B蹱��g�=�H�A�PR躄�@�e�^�[�Z�g���
�B
躐�A�M�H�I�B�
�	�B�1R躣�k@�T�}'���x��B躲�/��E�p	�G�r���BB軑��P	�4	B躰��i��.�/�g�D�$�>�w�D��R	軴��#�$�'B軾�2�D�B軿�:�@	B輍� R	輘�v�V�W�q
�nB輥�F	�H�L�B�L�<B輣�y	�t	�F��|��|B輭�c�&�P��R
轘�E	�*�(�)��Z�O�N�~B辛�B辜��d��RB轮��k�d��=�]�R辭�X�,�N��z�B��h�I��-��D�-��$B达��F��;�A��F��oR迉�h��J��	�����}��T���=�p�y�|B	迢�B迠�

B迚�KR迴�Y�x
�
��&��3���rB退�W
�@	B适��LB逈�`	�%R這�5�~�	�
�	�B逮��j�BB逭��J�DB逳���5�N�tR遉�E���2���~���*B
遘�D

�B遧�B遡�y�\�nR遲�p����U�F��	��
�B�T�YB邑���B邀��D�M�H� �$	�	B邅�}���hR	邮��]��x�B
邸��H���F���B邽��E�r�}�	�V�{�z�<� �!B邻�D�H�
R
鄀�+�E�J�;B鄋�M
��R鄖�K���4�1�,
B鄦��q�XR
鄫��?�-�r
�k�j�h�B鄹��N�
�}�|�
�J��vB酐�)�R	�<	�bB鄸��|��@��*�N	�	�F�S�jR	酲�,�9�����8B醃�S�BB醀��j	B酼�o�O�x�=R醙��O�T�Y�R	�QB釆�!�f�h�Q�z�P��q� �J	B醮�\�h��E�.2"��'�,�.�X�FB醥��������F�K�,�L��8�C�L�p�2�6�i�T�s�R鈀�<�U��D��O�DB鈍�	B鈌�;��oB鈎���$�U�O��QR鈰�S$3��� ��1�tB	鈼��R��	�@��t���R鉈�Q�w���4�-�&�+�z2�>�3�E��B鉚�[R鉝��W���L�G�+�B鉫����s�N�GR
鉶�1�d��#�N�5�LR銀�U��V��Q�>��G�b�^�]%�xB銑�^R銓�Z�4�U�F�K�d#�/�fR
銠�4"$�_�b1�MB銲�_	
�DB銴�0	1�IB銮���_��T�rR鋅�T�4�%�
�d�o
4���j�yB	鋗��p�c#
R
鋣�������

����qB鋮�?�:��n�6R
鋶��Y�C�>"�Y��F�wB錄�}�@�?
B錀�f1)@!
;B錃�g�RR錝�T#�!�"�[�]��I�|�I��*�����
B錳�x�@
�S�$()R鍀��U�B�{�J��E�K�z�&��e�j�y�B鍒�R	鍔�)�d!.��RR鍞�#�.�M�
�	3�U��4�/����r��]�`�*B鍰�'�nR鍳��
���b�0�?�%�0'�,��R鎀�� ���V�.�I�{�A��B鎌��k�H�K#R鎔�?� ����~�F�G�:�S�
�2��#	�t�R
鎬�E��8��=�
�	��+�./B鏃�`B鎷��H�,!!B鎸��L�Q�Q�
�F�R鏊�W��'�[�l!�e����o�X�B鏟�_B鏞�K	B鏠� �<�TR
鏳�b�N�a�%�#,�'�~"B鐀�[�p����jR鐆�k��/2&#�
��'�
B鐛��H�:�+R鐤�C�g[�R�m����v�����u
�	R鐵��
��������
B鑄�_\B鑀�,�
�Q	�:�5�~�J�#�-	�,�MB鑔��*R鑪�1�f�0�#�E�D�I�H6�s�n�:B長��H���JB鑼�_B鑻���F�G��z�U�^�
�vM�Y�$0�o��O�P�L�`�L�E�Q�R
開��b�[�f�e	B閖�U��q��j�?R
閞�r�f��������|�`B�閫�3�%�&�)��|�Q�F
�@�r�}�r�w�x��=�$�+�>��1���v��+�4�X��� ������b�y�t�w�@�t�y�x	�k�j�K���z��@�(���'�"��'	���X�A�W�L�_�*�5��V�F�v�U�N��`�i�w�|�#�&�z�n�@���V�Y�V�i���.B陻�,�Z
B阸�Q�H�)�-�j�<
�QB閪���}�h�H�h�-�,�Q}#)�{�_R雒�i��t�U�N�^�+�"�#�,�)�"B離�N�<��v�"�p�D��n�N	B雡�e�
�!�H�2�B�L�r�zB雴��N�R霛�I��/���|���)�*��w�r�qB青�!�B霰�V��O�
�	�z��=�z�O��H�vB-霱�@�|�� �!�<�@�B���
�f��x�D�u�o�L�d�A
�K�\�z�y��`���2
�@�D��(�/�tR鞗�x�K�F�|���6��C��p�9�2�9B韋�(!�MB鞭�V�S�� @�O�
�v�	�R�4�b�H�e�v�|�^�_�X	BE鞨�,�T�<���
�P�I���6�o�I�@�H�:����@�0�;�p�L�M�:�4�<�a�E���[�K�t�E�.�G�`�H�� �-�5�J�~�w�^�g�X�k�L�Q�K�R
顃�C	���>�I��J��B顓�\�.B顗�t�B顖��nR	顤�5�������:�_�[��B顯�H,�v�C��vB顲�r�v�+�L�
B页��
�~�5R
颶�7��n�k�z��7�2��~B飄�W�U�
�|�t�F��p�u�L�W�\��S�N�G�,B'飀�8�<�&�o�r�b
�L�
�E�M�F	�\����h�D�~�
B飃�x��>
����9��+�X�
������,�D�D��
�OR
饐�{�E�N�*�_�$�'�"�yB首�/B饛�W�v�Hl�u�{�@�K�4��[�nB饝��>�_�w�v�R馪��Y��m���'�"�'�$B駁�v�xB
馹�k�D	
�NB馸�K�,�G�(�R	駕�w�B�C�����8�AR	駟�t���m��G�L�<�3B駩��I�D�KR	駴�S�V�Q
B駿�:�A�.�JR	騉�@���=�4��B騔�
�
R騙�z�
�F�O�,�1�b�YR
騩��U�P
�*���LR騴��I�N�S�V�b
�~���#�,B骨�vB
驀�*�XB>B驁�@�>�\�f�g��U�B�,�w��b�u�J��)�`R
骶��g��[�R�P�L�I�W��b�u�B髁�d
��V]�H�1���Z
�G�|B髟�3B髀�H���l�<��t�C�|�/�X�R�u��p�A�o�bR鬈�J�*�'�)�$�j�mB鬥�x	B	鬚�k�.�'�T���R�SB鬔�
��6
��E��k�R�{�y�v���^�q�8�D�^�a�zR	魊�O�O��X��M�4B魚�aB魔�-��q�Y�P�&�Q�d�&�!BZ魕�g��G�T�j�.�-	�j�@�@�K�4�+�x��&�'��h��&����^�c�0�#�
�;�A�R�G�~
�R�e�T�M�v��\�k��BR
鰅�

�8�S���y�pB鰓�_� z	B鰒��`	�H		�@�'�,B鰐�:�F�x�s�N�I�	�7���J�dR	鱈�i�{�T	�0�AB鱔�>��1�,	��L��T		�"�Q�\BO鱒�T�D�;
�F��<��V�d�j�3�`�V�p
�@����3�&�/
	�4�+�,
�4�1
�n�a�A�t�g�P
�R�EB鱼���L�t�)R鵖�e	�]��z�	�s�R
鵧�q��_�T�U��N��	
�K�bB鵷�"��#�n�YR鶀�($�^�)�M�FB鶐�$� �R鶖�3	#!�TB鶯�1�IB鶨�.�p�S�A�H
��B鶫�0�>�B�=R鷇�W�*�C�@�'
�@
�
��Z�7�!�

B鷥�B鷡�f�c�F�W�D
�D�B
B鷼��QR
鸉�?�|����^�]62R	鸗�1�$��1��4�}�9��PB鹵�c�z�{�B�*�B	鹺�j�E�3�T�f�"�X�W�RB鹻��x�E�S�L�	R麐�1�i�q�h�j���m��@�c�f�m��z��-�Jr�lR麤�J��K��<���p��a��`�6�B���g�B麴��2�y�0��D��*�+��t��f�*�\�`�$B麶��x�j�@�u�t��Y�X�k��P��`�0B麽��U�F���?�@R黴��>2�m��n��u�,��n�[��v�
B鼎�{B鼇�?
��B鼀�q����2�C�r�$�+��R鼢��R�8��S���z�5�0�h�,�-�(�b�B鼻��p�eR齂�
�^�@�#�tb2����^��^��B齒�	��l�i�XR
齙�C�>�I���9�4�3��"��'�B齪�t��U���p�B齩�P��b���O���b�*���~B齿���V�>�#�|R	龤��*�fP>B#�:��t�a�E�h���z��Z���!(�M�N�m�"�r�1�uP�|�j�I�r��
�'�T�i
��t�w��E�s�n�z�C�r�}-�p�L��$�y��p�T��WB�h��M�m�^�.�m�rH�k�Bh龯���=�N0�Sz���#
�
�Z�M+�}�O�|���t�J�_�1�0��^�U�T�y	�x��@�$�	�K��[	�`�����T���c
�b�X�3�2�Y�^�J�	��k���e�z�c�b�x��\��t�W"�Y2�o��D<�7�L�6��X�/��2�o�KR	��J�>���?�]��e�dB#�.�e��#�
6�@'�T�X�-��(�3>�Y�X�U�2
�/�X�Y�:{�[��<�g�~�$~B�_�#�s���i�mB���jB�i�j�4B�%��\3�a�F�;�"�&�'B�$�'�|�^���is��t�yB�Y�z�(B�PB��a����K�h�c�_�R�@�U�T�U�T�U�`�	H�#�d�e�d��W�V�]�"�E�#�:����'�T�U�T�D�(�)
�S��3�2�=�<�;&�
�X�Y�L�h��y�b���b��}���)�g�h�8�=
	�k�D�<�=�<�=�<�U	�i�\�]�l�m�H�M�B��		�,�-�,�-�,�V�!�a�d�>�7�2
�%�*�+�*���+�*�+�G�(�)�(�)�,�K�J�#�,�!� �!	�$���E���
���
�	���}����N�Y�`
�B�%�(�_�K�8�3�2�3�6
�;�{�D�E�N�O�D�v�o�n�i�n�o�n�o�v�o�v�q�t�i�[�c�f�g�p�q�v�s�r�s�r�g�j�c�f�e�I�p�_�f�c�j�5�D�*��f	�.�b��
�K�K�J

�/�>�H�n
��I��
��M	�T��w�z�qS�r���
��M�	A�t�s�r�s�t�s
�A�K�X�w�v�q�p�o�n�G
�E�D�C�B�X�m�p�S���������� �!V;�F�:�t�G2
�@
��378�d�B�e�x�y2�v`�}�H�G�y�
�5�w�a�F�+�w�q��@�Z�r�b3�K�c�XZC𠂊�2�C�f𠀡�u�I�G���o�|���7�U�"�3�d�Q�V���j���c�P�h��{�@�G$�~�d�	�v�*�%�@�6�A&�w�a�n�c)��]�=�G
�f��}�a��)���t�;�H��j�$
��V�5�@�c
�f�e�:�K�=�e��w�-"�X��'�r�;��u�b�`�B�p��|�y�~�
�#�x�Z�V�U�]�0�Z�K�d�M��
�<�{�F��|�3�k�7�]� !��Z�\�E�)�5�U���m��S�&�	�|�
"�K�,�~�_�
��a���M�X���S�t�[�K�K �,�f�x�?�f�c�Q�)�&�P�i��j�s��t�� �x�4�a��[�R�29
z�	��X�R),�?�k�8��
�B�C���^�4�V�S�
	�{U�h�^�-&�V�]�T� �t
�;�m�2��u�S�$�k�q�D�m�D��z�s�O�N*�L�b�=�p�@%8�G�
�L�1�9�	�k�B�\�9�W�%���N�X�-�D�
�Q�j�Z��$�+�?�z����v��
��N��]��cT�a�\����0�F�B�c�
�N�j�S��F�N�L�O�T���F�^�:��E�z��e��}�V�J�Q�7�$�>�U�% �1�J�.
�K�h�;�U�2�{�H�)�\�(#�'�F�(�d
���R���x�V�N�x�C� �j�)�)�V�$�
�*�b�n�o�Q�?�D�u�f�4���v�3��g�Z���l�(4�{�m�`��}�_�H��������>�R�x� �Y�n"��q�e
��2�c��x�!�g�\
�H�_�|�&�o�i��i�!�]��c��x�K�m2�f�X�*���k��0�S�;5z�n�s��+�T��%�<�h��%�`�&��_�J���m���X�j�}�x�s$�z�*�O�E��o�|�B"�%�e�Z��i�n(��i�R�F'�I�o�#��`�\�1�G�P�6(�;�F�\�9�y�D�0�T�
�!/�T�U�)�,	�*��y��}�m�)�f�� ��b�i�d@
;:�^�	�9�)�v�W�a��q��A�*>��P��l�7�r�4�s�@��i�z��A�L�p����p�;�r�C��"�7���*�q
�
�T�:)�5�{�y�f�%�Z�r �[�E�`�M�p��"��G�<��m�!�k�(�~�{�/�E��P�z�v5�G�@�p�"�Y�P�.'��[�)�"�{�@�e�i:�t�z�!��Y��O�3��W�W �L�`�G�!�j�x�W�T�J4��@�Z�W�d14�#�H��<%�
Q�i�'�j�`��`�'��3��W�z�Y��m�j�
�0��w��=�b�F
�E/�X�@�=���i�Z���W�&�
�9�^�y�z�,��0��K�s�2�U�L�[�T�C�z/�a�_�!�m�[�j��7�Z�f�w�2 �m�C�o'�x�u�6��|��	�c�t�M�/�S�h�N0�G� �Q�Y�8�
�
���O�p���e�z��q�P�|�Y��f�i��j��s��^�S�-�T��'�B�@�t�K
�F	�;�C�Z�2�o
=�@�>�q�F�0�h�M�D�X�B�q�g��1��o�\�O�x�E�V�����R	�~�]�j�z��I�>
�
�O�b�Q��#�l�a�%�Z�$�R�!C[𠂆��df���z��|���L�P�a��$�{��Q�e�V���a7��v�}�pO�
+�xb�zq*@�P�('
�l�!�O�r'%�[b��x�'~�GOFi$�<�G�C�[�p��%�	�,���F�i��P��*�	���r�D��u��j�[�9�>�Me�5�lw�n�"�s;�
���	5��v��
��v���|�zk�K?�
H��'�+�S�>���r�oQ�w�g�^Cg𠂔��\�"�h�v�}�� �K
�1�K/��W�P'�g���U�{	�$�c��b+��R�Q
(�o�g��~��a� U�HT�%�|��W�|��'���{�[���o�6�Z"�o��]�k��e0�L�{.�P	�!�J�1�W��&�r�x�~���f�{��i�'��_��^&�a�m���z�b�(�YC_𠃮��=�u�R��{�'�s�Fv�5�)�u��Z��W�|�Y�l8�.�"G�~8�}'*��S)�(��B����B�l�Q�����{L�i�H�4�{��"�z�~��cl�H
���S�l�����f�B�P�#�U�
��x�D�'�=�]�b��n�	�;���6�[����k���b�Q�3��h�W�z�)�\�[�M��U�V�|S�_�eV�y���o�c�a4�Z/�w�O�R%�M�[s�.n�i�N��j�K���a�w�b��Y��n�E���{�p$�d�d�V�U�S
𤨡��F�L��b�#�2c��M�"C�>𤨾�.�C�)� �/>�8�o�[��Q	��Y��e�Q�^���@
��\�"�	�J�O�N�;� �K�J�(	�!
�6	�'���S�?��L�y�m�l�6�$��q��H�i��A�t�F�e�%����w�n�
�a�
b�c�h�E�w�x�D�I�n�6�v�8��]�l��	���{�D�n�F�a�2�a�[�0�/
)�c�[�j�e�@�d�s���q�<��w��O�c�T�)���l�5��^�u�:�S�U�
�M�d�>�%�9�_�4��9�yT�	?�i�Q�Z�;�\�5��o�G�n�1<!�a�b��
�z�*�x�M�q"�N�]�(�p�3��,����g�^�*�]�r��y�|�K�x�D����n��z�o��n��$��[�}�,�X�v�4�e�W�G�`���.�_�K�#
�P��
�x��
��h�B
�S�m�R�j�[�2�I�W�d�{�H�G��N�#�E�|�T�{��S�-��r6
�.�p�h�X��2!��L�p�Q3�"�L�/6��i�i�^�>=���@�:��4
�Q�`�B�+�}
�2C𦉪��z�`��~C𤷫��t�d��-�d��t��$�v��p�U�`�iO�s8C𤩏��Q
�kQ�.��|�p�o���4�a'CS𤩷��%�[�7�l�s	�6�X�l�`���oW�o�J�w�Y"�n�X+��)�E�[�����+�w���
�l'�k�R��
�7�^��j�_�2�q����j�d�k��f�}�@��_�K�Z�e�]d��p����h�*�G�=��8�)���f�a�g�~�u��� �'�x�:�`�u�5��y�	�,�o�F�G��\�6�R^��d�l�s�k���r�`�&
H�w�q��c�V(��S	𦴢���)�&�u�c�.�2�C𧘇�8C�U𦵑�%��^��7�q�
�{�<�m��7�x��O��o
�?�o�a�� (�2�&��L�T�
�D��V�,�
���>��r�2�j�^�-�|�9�3��r�{�8�t�_&�D�]�A*�2�S��L�)�}�E�I�e�,��6�g�'���a���-�@�n��|�[�T�>�M�%�_�
�]�S�w�&�D�J�W�z�e��*��l���W��n�e�L�m�y/�i�T�*��T�J�8�w��u�H�Q���W���M�>�+�
�]�p��	�z$�^�B�p�<�5��j�
[��H�M�<�B��z�i�w�$�s'�W�Y�n2��[�&�}�r����g�u�7!�;�j�,�[��T�E� �H�N�n�9��n�{�r��K�_
�)��o�|�l�+
Q�l�p�&���T��R�Z��J�"�i�@�w��_�c��8��s��w�N�Y`�/�n�M�g�P�\�3�H�<�7�u�.=�*��2�C�c�f��Lt��P�d�MA�F�G�f�O�{�6
��e�l�\�6�-�c�:�9�V�h�V�-�F�g��E�v�}�H�Z�}��=
�z	�y�C�*�W�u�i�.�g�j�3�d�N��!�y�d�x��g�V�k�W�p�R�b3�G�e�p
�5��`�#��@�e�\�I�N�N#�{�k� �_�p��Y"�R�k��/�l�)�q$�.�H�Z��>��
� �q��
�L�k��s���w��.��i�z�S�C)��XS�<��Y�k�@��n�w�G�����}�}�X�[�=�m�(&�{�z�A��n�
�m�f�t��h�1�2�g�J�{�f�d�;�[�n���J�j�G�4�j�)�k��V�I�j�n���Q��z�0B�W�'���j�K���}.�a�R�f�'
�L�l�J�O�.�_�S�	�
�W���`C𧀎��}��� ��|o�U�h�{	��8p�I�40�P���b�}��
�%�T�L�P��/�M�	�&�k�?���%C𦺙��&��oP��w=�:�Ci��R�����sq�!b�dEC𧥧��yC吆��kCs𦵴���y��m�r�k��3��sz�,�B��L�!�!�rG�c�$�z?��e�h�1��2�X�3*�X�o�Y�k�D�,�0�P�u���B�u�Y���L���^�	�}�|#�%�S�)�g�v�;�<��U�=�d�z�#�y�`��j�k(�Z���@�\��}�8��u�$�
�%�j�T�k�#"�f�g���<��M�'�D�X�%T�l�X�H�%�
�t�/�.�K�/��3��r��c+�a��v�8�9�y�^(�v�H�q��&��! �X�$�e�$�T�f�U������j���V�V�-��]�s&�~��}�Q�P�y�Q�^��8��@�S` ^a
¢��m�!�2�9�@�H�J��j�q	��@�b�n‘�&�(�	�W	��\�y��e�{�Y�X�2�	�z
	��o�>�J�M�"��F�]��[��d�z�|����'��*�P�B�V�Z�a�e�j�V�.�c�k�v�z�~�@������ ��
�&�u�C�y�a�;�{��qe�������*�a�j�J$�Q�d�����n�r�p��.�$�W�#�&�z�~�k�}�~� ��z�3�$�A�y�,�Z��S�7���E�`�1��;�	�s�s�?�w�P�5�E�
�)��\�{�X�N�`�[�h��t�y
�d���� �^�4�6�N���w��?��k�A��;�c�/�t��"��\�r�	��	�N�9��5 �p�H�=��N�8�X�;��B��w�T�h�Q���p�B�~�_�n��Q��g�g�(�p�V�r�g�E�Z�Y�0�)�\
��T�d�J�b�=��u���i�z��"�]�p�j�g�O3�'�~�{��}�g�K��H��E�r���\�d���'�m�c�\�^�r�l�+�r�W�(�&�9�`�0�%�n�I�+��C���F�k�=�	�!�g�`�7�C�V�E��4��E�i���u��v�^�4�V�o����E�v�}�:�~�R���.��m�L�d�����u�#�k�W�M�#
�^�p�+�b�w�_�0	�k�+�s��S����z�@�	��*����
���B���G�K�Ipt� �M�_��\�	�M�m�F���b\⿐��2��s�-�h�y��S�#�8�[���=���q���?�i�x��4�`�f�s��i��+�;�}�V�{�o�I�a�4�F�t�b�U���.�p���v��b�w6�n�w��]�O�w���T��1��#�k��|�Q�D�}���a���O�6�#�}�Y�V��-��^�U��,�u�o�+���I�h�j�z�4�i�e�'�t�z�9�z�8�n�?�|��m�V��I��8�,�*�h�Y��i��Y���<�o�b�s�`*�]�i�`�`�*���]�c0�!�b�\��z�n�%�^�Z���w�b��{�Q�z�4�`��;��=�^�D��9b&俶�(��L�6��=�e�0�U�H�[��
�
�p�d�A��J��^�l��{�o�&�}�A�
��H�u�8�'�M�l�D��v���k�O�'��o��G��J�L�Q��z��c���h�"��{�^��s�8�6�;�F�>���H��Fb偣�J��[��L�z0�}��b��s�*b庲�Q��_�^����=�T�A�
�W����N�#�$�-�v�@��H��n�\�u�l�x�{��)�J�M�]�m��~��	�
�
�v�bF亃�a�����.�x�C�#	�	��8�~�r��o�U���u���i�t�~�'�i�$��e�b�h-�<�L�I�x�l�E�H�@��T��v��m����!�q�3��/�6�B��;�_�S��V��a��g�^�
�1�q�e�s��w�v�y����k�z�&����h�h�e�y"�q�g�g�h�p�n��
�Z��"�!�d�%�(���^�>����O�X�P�t�#�~�T���c��t�`�v��yb斲�ub獞�j�h�t��	�d�b.嚌�B�#�Y�t�6��a�`������T�5�@��B��E�#�`�.�u�4��d�{�s�p�E�C�V�.�i�^��
���'��X�<���z��k�o��4�z�e�G��[���k�(�X�z���
�;�J��	�>�q��4�l�[�O�{�U�q�1�
�5�x�~bg儭��z�p��J�~�	�K�|���[�M�%���O�P�Q��+� �T���1�Z���:�v�b�X���L��r�C�/�_�X�]�[�	�G�h�t	�
��;�b�F��S�y�m��~�Q���U�m�.��i��R�
�]���
�'�P�_�-�
�X�,�~�I��b�w�Y���X��g�l�3��B��e�w�C�B��"�|�H��,�L�\��v�N�I�&�V�9���c��g�o�T�L�n�s�Q�7�v�'�L�y�(�E�y�|��Y�T�Y�-�\�K�E�Q�	��|�O��s�R�y0�/�U�+�4�`�E�=�0�s�L�A�D�~�Fb�↸�-��K�:�y>�A�@�	�7��@6�3�5��V��Y�w��r�;�|���9��H��-���k��C����+��2�@��K��T��g��o���@$��	��1��<�@
��K��W��j�����@��
$����8
��>�@��I��M��Q��S��c��m��t�@����
����'��=�@��A��G��J��`�B)����-��0�@��?��X��f�@��~����!��)��8�@.��;��k�B��x�+"�0�V�A�Y�_�d�f�k�p�y�}�
��A��(�1�8�@�R�Y�^�g	�l�w��@��!�&�3�<�@�L�V�l
�s��@��
��'�@�E�K$�`�@����*�@�A�G0�P�@���+�4�@�A�S�i
�r�@�}�)��@	�;�E�d�@�y!�{�� �"�@�60�<�n�@&�u��@(�5�^�j�o�@?�s�A�3(�F�p�@	�r�}��@*�2�]�@�q
�v+��@?�1�@6�q�*�@!�1�T�f�_�c��N�Z��U������J�!�#�%�+�4
�=�F�A�I�M�Q�S�U�X�]�e�@�g�m�s�w�{�}��	��C���!�$	�'�1�4�6�Q�F�u�P�U�\�^�b�e�j�m�@�q���A�1�4�:�>�A�F�H�L�P�C�R�T�Z�]�_�u�w�z�~�B�����#�*�@�;�L�Q�U�`�h
�l�A�w����#�B�2�:�D�G�K�O�B�m�v�{����	����%�V	������#��'��,��:��@��G�@��K��O��R��X��a��o��t��~���[��
���}�3�:�@?�J�@?�
�@?�J�@.�
�h�a�r�b�I⏚���j����!�~��.��1����F�c��I��R��X��]����O������t��t��y��M���k�u��i�����x����X��F���R���]�
���i��/�
��N�o��Q��S��U
��[�A��f��n	��s��}������	���I������ ��'��+��2��9��>�B��B��G��I��R��Y��\��_��b��q��s�B��x��z������
����&�@��,��2��8��>��F��N��Q��T��[�@��]��b��k��t��z���������B����������'

��.��<�C��>��B��E��G��I	��N��V�C��X��\	��a��d��h��m�Q��r��z�������R�����c����������#��'��)��0��C�@��E��G	��M��O��S��X��Z��]	��`�K��h��k��r��y	��}�����B����	���������Q�����y��0��7��:��K��I&��a�@	�������4��7��<
��?�@#��M
��s
��v�B��	��������
��#��1�s��B�P��E	��R��\��b��e��i��n�@��r4��t��_�@��g��|��#�D��*��5��=��@��D��L�_��l��}��
�����)��+�D��1��3��:��>��D��F��R	��^�@+��h���m��F����%��-�E��c𠵾��\��]������L�2����5���2��]��P����"PK
!<��<ܝ�2chrome/pdfjs/content/web/cmaps/UniCNS-UTF8-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�
UniCNS-UTF8-HB–x��i�����Wb〈������
��r�PK
!<gYYf�f�1chrome/pdfjs/content/web/cmaps/UniGB-UCS2-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE����!a ^A	��'�vG%�O9A�
&

1!bA���Ra��
��%�67�`�Z�a���m�7�Tikmol	�*�#�8�G�%�,I�z$�:�9�<a��3
�5�6�"�6�7�8�9�O�:�;� N	�)�<a ���QA"�	��t��k
��F��g��f��ka"'�A")�9
��\
��Wa"d��)��Il"��M	�f
�R�>��5�4�7�6�3�2�/�1�05�.�-9�!� a%K�ba	"f�D-�)�F�#�G
�k�z&�}$�#�q.���������#����&��"��'��,	��$��-��.q.���0��:��<��=��>��@��A��B��E��C��G��H
��J��R(��XQ�V/�B�@��r��-�
�;�]��a��l�I�Z��.��-:�*�+��&�
��C�)�V�C�9�
� �5�;�p��z�	���g�`�}�<�1�^� �u��/�6�(��a�6�i�#�U��X��K�p�D�m�;��M�$�{��I�$������|�j���=�z�!�j�f�%�:��H�~��]�N��Z�h�M��M��<��;�@�R�c�:��j����M��)���k�b�r�7��&�%�H�C�9��c��>�|�}��q��z��e�K�q�Z��$��
�}�H�9�S�8t�T�m�l�5�(�_�U��`��i�(��F��}�3�f���v��U��4��
���D�9����UF�B�8��
�"�F�k�w�5�d�;�V�Q� �C��j��{�q����g�n�X�5�*��`��a/��L`gq	0h�(�s}��=q{a0AR�d
U�7c	$�<�v	�pa0���K\�$�)]�#�&�2�!q��j���"�	���a03��{��~��UT��gb��v/��s��qj4F����%*��H��$���s�����)`��
��~��+
����*�d�����|���|��|��/T����._��i��1��4
��I��2��Wr��w��3���jM��v��7�0��Dm��u��8p��c��9��T���s���s���sU��s��>�(��I^��r��?���Q���q6��q��As��(��G����F*�� ��I!��K�U��m��J(��C���lK��l��L��8��K���L"��j��N��
��OQ����P��d��Qq��t���fF��f��S1��-��T��_��U��a��V��e��X��f��[��z��Z��}��]��\G�����[���[v��[��d&��R��a��e��`[��y��U��f���h��m��|aN�B�`�X�~�YQN�#��%^�L�P�-�������1�m��
��c��.�K�$��TaN�_QN"�i��p�9�b�T�����g�j�h�aN.�eQN0�~��P��m��l�
��o�n�#�)�f�x�h��3�8aN@�lQNC�.��{�z��]�d��	�	�<�9�S�z�`�5��`�\��
����	�PqNZ�u�u�t��9��T�w��{�v�}��y��D	��)��Y�QN���~���t�y�9�2�1�w�"���x��/aN���^��p�t�QN��q�}��9�A�(�!�a��M�*�����x�q	N���T��!��"�&��&QN��a�1�I�xI�k�H�/��&��?�^�w�3aN��+Q	Nє�Z��!����x�y�aN��.Q	Nݥn�������k�r�Fq'N��3�#�5�N�%�L�6��9�X�(�:�+�q�N�;�)�>�z�?�_�@��A�.�H�H�x�2��B�(�J�Y�*��v�'�OQ
O�Z�H�#��D�Q�P��N�]�\aO(�S�@�/�UQO/�u�F��|�C�B��K��J�W�V�m�l�E�D�E�xaO>�^�I�c��eQOM�J�9��$�3�d�g�f��O��N�or�W�Z�M��>	�_qOa�n��*�q�p�1�q�&�r�;�t��f�v�D�:�x�9�y�@�B�}�?�P�~QO��=�������p��7�6�C�B���qO���D�	�]�p�
��
�G��-�Q
O��i�P�{�n�M�:���"�!� �7�)qO���S��L�#��\�,�H�%�.�)�r�K�i�L�,Q
OכL�e��,�Q�P�O��r��N�K�D�SqO��4�E�6�F�r�G�7�f�)�$�9��:�Q�;�N�?�>�@�V�C�L�D��IQP	�b�P�W��s��X�s�r��K��f�YqP�O��)�W�R�W�S�9�U�P�VQ
P%�Y��|���2�qlP/
�Z�O�e�4�f�`�g�\�k�L�^��n�P�p�_�J�q�]�v�S�z�T�{�r��`�	�e��}�f��a��[�J���0��g����M�'�
�>�(��y�0�b�1�c�%�d�2�b�.�4�L��K�l�8��9�e�n�;�h�=�T�@�9�B�X�E�^�J�_�K�B�f�N��Q��T�,�j�g�S�Z�i�h�k�]�P�a�z�b��e�F�fQQ��2�7�6�1�0��n�)�8�qQ�n�V�r�@�s�P�N�t��v�v�|�G�}�1����R��W�U�Q�QQ?�8�@���O��1�\�8�A�u��4�}�|�aqQN���e��t������t� �S�%�F�'QQh��<���'�Z�z�f�f��9��>��u�H�k�B�k��>����6q"Q~�-�0�z��/�=�1�O��3�(��5�5�9�f��:�;��<�+�j�=�3�@��A�l��B��k�GQ	Q���M�Z�
�4��F�
�h�SaQ��J�r���M�j�OQQė�$�s�r�~�#�"���:�:�
��[��Z��}q
Q��W�&�`�`�a�+�O�c��eQQ��T����e�5�p�&�����+�o���G�&�`��i����!x�n�CqR�q�s�M�
�s�<�t���v��C�x�n��>�5�P�7�{��|�w�~��Q	R(��i�:��W��V�m�Q��<��QaR1���	Q	R6�E�]��@�9���b��}��|q?R?��&�4�=�H�
�c��d���-��)�t��1��i���������	� ��!�:�g�"�+�,�%�i�'�J��(��/��0���O�1�O�=�6�:�4�I��5��u�7��8Q	R��o��i�)���r��cqR��@�n�y�m�*��D�c�Q�8	�G��]�Q�>�R�q�S�'�V�h�W�T�X��\�@�]QRսM�u���C�T�l�u�t�u�I�Iq8R��b�2�d�W
�e��#�p��s�u�|��u�
���x��U�|�$�}�}��5���f�D���|����	��|��(����h��E��^��F��D�u�D�Q
S>���N�U�.�s�r���S�~��waSK� Q
SN�?�p�T���NK�f��(�,aSX�$QSZ�p��j��Q��P��3�V�;�Q�:�n�g��=�O�
�/�.�7aSl�+Q
Sn�j�o�$�)�t��
�N�/�j��%�H�Z�9qS{�0�a�4�L�6�1�O�e�7�b�9�b�
�}�:�%�A�P�A�(�CQ
S��~��+�f�B�q�`���!�.��W�0q$S��I�y��L��O�B�P��Q��U�E�X�Y��Z�Y�;�-�#�6� �Q�]�?�`�|�b�|�s�M�;�c��dQSߧ�m����w��h�D��b�U��3�0�O��f�H�~�[�P�b�I���l�:�0�W�]�TaS��kQT�m�'�&��k�p��x�[�Z��U�,���4�)�;�-�h�&�V�@��~���2�UaT�sQ	T�,�����(��)�<�Z�WaT$�xQT&��u��6����/�X�_��'�N�)�J�\��3aT6�}Q	T8��'���S�_��0�!� �7q3TA����w��o��a���(���|��
�t��
�7�g��;������[���"�E��~�����$�+��!��#��$��%��&QT��G���J��_�L�O��^���;�q�8q
T��-�E��/��0�#�-�5���&�+Q
T��g�s�V�6�����3�2��G�aT��9�9�<QT��%��u��1�i�F��F��G��F��E�Z�k�qBT��A�'�Y�0�/��o�� �$�C�>�D�(�E�*�.�0��F�G�G�b�w�2��q�'�J�k�e�L�}�6�O��U��4�V��W�
�X�l�Z�i�@�[�1�\�9��:�a�5�c�1�i�3�m�7�R�n�;�pQ
U*�<�j��u��t���*�}��H�QaU4�u�G�xQU<�F�j�e�r�p�w�v��C��:�uq UG��@�,���E��I��C�H�	�N��p��e��@�o��=����z�A�Y��I�p�Q
U{�Z�G�L����Y�B���%qU�� ��T�$�g�O�"�>�%�Y�&�^�(�0�_�+���-�V�.�E�5��7�^�8�*�9QU��R�2�]�x�y�x�)�(�U�T�E�DqU��\�@�j�,�X�@�E�b�F�o�o�H�d�P�]�g�J�X�KQU��!�X�_��J�i�h�m�l�3�t	�R�C��3q-U��e�S�k�U�m�W�r�r�a�X�I�{�]�|�x�^�j�b�v�A�c�w�|�=�{�e�0�i�z�t�5�j��w�l�u�c�m�z�y�p�}�r�L�sQV,�	�r�S�0�g�A��>�Y�N��
��S�l�[qV<�x��{�t	�|���4�������
��o�Q
V`�p�@����V�>�{��i�:�YaVm�QVo��,��P�D�E�D��9��8�}�,�Nq6V{�
��n���	�7�"�u�&�v�:�'��*��+��.�f�7�k�9�9�:���?�}�u�B���C��G�{�~�L�J�[�L�j�	�5�N��O�!�S��U��WQVژq��>��O�|�g�6�?�F��qV��[��a�s�b��T�d�6��f��%�i�S�m��kQW�s�L�&�s���4�B�b�Aq9W�p�F�H�t�~�v��w��z�r�|�<���'�)��(�@�,�9��*��+�F�/��
�
�6��0��(��X�� ��?�J�U�`��v��T��.�^�z�u��a�QWd����'�&�S
�1�2�Z�S�R��UqWp�#�:�&�9�M�(�6�5�+�<��I�2�4�0��=�4�H�B�9�!�@QW��CC�#�8����M�B��I�H����~��qTW��H�>�K�A�L�I�O�z�.�X��_�l�?�a�H�c�q�D�d�E�G�F�f�N�h�W��*�i�M�k�R�s�K�t�J�x��L�!��z�Q�|�S�~�����L��%�O��P��2��(�,�
�U��.�T��W�
��-��=����8��s�
��V�+QXJ�c�*��f���
�A�6��quXU�3���5�V�:�'�=�/�X�>�A�Y�C�}�K�V�N�#�R�]�Z�S�b�T��U�[�B�X���_�:�\�d�2�e�9�1�f���n�_�r�y�v�Y�$�}�]�~�C��%��g��&�
�m��u���&�)��R�;�(��'����+��V�$���.�2��T��/� �C�I�"�8�$�&��(�)�)�!�+�/�0�1�1�{���5���7�@�8��:QY"��t�C�F�������_��^�/�)�E����q;Y2�A�h�C�K�q�C� �E�/�F�|�G�0��I��J��1��L�y�M�	�=�O��P�%�2��*�4�R�3�S�M�X��Y�M�[�[�^�^�"�_�z�`�o�l�d�/�e�\�r�f�z�iQ
Y��u�g�f��z�=�+�*��:��q�XY��n�w�p�	�>�t�&�{�v�s�w�-�~�z�z���~�^�x�}��@�����H�
�}���
���|�S��4��� �>���
��-��{���	�$�<�%�S�'��*��6�}�7�(�:�/�;��M����
�<��>��B��C�R�G�+�J��(�L��N�/�O�i�R�N��Y��Z�}�[��`��?�c�d�g�"�	�i��s�~�w��~����"��y��	��-����
�A��B�#�I�
�*�|�5��7�!��>�$�?�$�A�i��E�Q�b�G�(�M�@�O�"�T�#�U�(�w�*�V�+�Y�%�6�\�&�]�)�_�>�a� �b�'�g�v�j�}�o�x�s�z�,�z�~�(�{�.�-��������!��/����0�"��1
�$�{�/Q[P�0��t�u�)�\�>���t��w��}���Aq[`�5�9�1��5�P�7�Y�v�Z�a�9��j�<�6�=��>�e�@�A�AQ[}�X��T�Y�S���D��'�z�E�n�e�I�e�����.a[��G�!�IQ[��1�4�9�8�-��d�}�r�`��x�����<�%�V�;��7q [��O�I��R��V�]��i�E�X�3�8�Y�z�_�S�\�8�^�7��a�i�_�6�c�G�f�e�e�gQ[ݘ[��?��x��
�t��u�M�:��'� � �=�_��w�v�uq[��u�
�7�y�j��7�Y��z�p�|��}Q\���B���B��3�~�4�.�MC��(��_a\�Q
\�:�n�;��\��C��B��y��x�'�Yq\��6��-�7��)�
�8��|��9�Q\7�\�3�~��B�E�i�`�Y�!��;q\C����y�>�=�l��Q�`�h���Z�"�i�%Q\^���:��q��F�K�v�y��z�mq�M\i�+�>�.�t�~�/�v�0�`�,�7�Q�;�_��<�0�A�+�-�D�.�3�F�4�H�/�U�1�4��5�I��7�<�M�{�R�:�9�4�S�;�V��W�6�X�>�A�Y�8�]��^�=�_�?�a�B�g�@�m�D�s�,�z�A�C�t�|�*�}�]�~�J����	��7��-�
�4��
�D��K������a��k���L��J�!�I�&�G�F�'�)�(��Y�H�*�O�/�P�N�7�"�?�W�V�3�A�
�D�M�K�R�M�T
�N�X�Y�U�Q�Z�S�\�Y�`��Z�l�q�m��o��~�[��\��
���
�^���3��<��_�#�i�(�0�)�U�-��.��4�+�\�5�[�W�7�}�P�F�
��9�f�:�5��$�-�X�=�X�<�\�>Q]��w�s�����V�I����S�)��z��a^	�G��J��k�!�LQ
^� �=���~��)�H�{�Y��q0^�O�C�{� �V�)�Y��z�Z�"�\�w�]�'�%�H�_�#�6�a��c�b��&��d�d�j�'��q���r�(�t�)��v�*�4�D�w�2��~Q^r�D�.���j��5��4��O�j��w�F'��o�hq	^�����i�<�	�5�3�Q^��=�Q�8�6��?�6�'�v�S����;�u��.��O��N��{�Q�q?^���5��V��`��V��:��8��B��7� �9	�$�w�3�.�=�<��4��6�>�:��x�.�v�1�=�?�A�@�B�E�C�x��I�|�S�J�y�L�-�[�7��s��k�O�.�R�@�v�SQ
_�T���;�:��#��"��)��(��s�q
_�Z��\�=�f�_�[�?�6�c�o�n�dQ	_-�p�j��#��"��/��.�g�f�iq_6�j��<�m�r�n�>�q�n�v�0�wQ_L��l�e�d�3�2��u�a��T��5��,q_Z�~�T��n���_�����T��[�.�	�d���`��a��b��^�4�Q
_��*�Z��b��_�K��(�e�!� �;�P�hqK_���7�E�n��f��$��g��h��!���^� �i��V�&�j�1�(�k�.�#�/�|�0�l�j�2��B�A�k�7�C�9�<�'�;�E���w�<�D�=�-�+�?��B�Q�I�D�J�F�,�H�N�y�J�O�G�K�e�M�Q�P�P�RQ
_����
��O�:�!��l�
q?`�L�U�Y�X�V�8�U�Z�n�\����]�Q�_�T�x�1�`��[�b�$�S��|�W�/�X�e�P�h�R�m�9�
�r�Z�t��`�h�u�~�|�e�}�c�����������k��?�Q
`g��w�?�T�a�$�	����}�cq)`q��_��0��\�_�^�8�b��&��g�A����i�n��h�!�o�"�c�#�d�j�&�f�'��+�(�Q�*��,�]�-Q`����:���k��j��+��0���m�f��{q`��3�l� �5�f�p�=�Y� �@��E�;�HQ
`جo�4�3�2��o�2�:��)�
�`a`��MQ`�L��w�0��<��_��'�h�T�#�Z�J�G�<�4��cq�
`��S�"��9�U�x��Y��]�_�I�^��y�i�a�u�f�C�(�=�j�K�s�l�v�|�n�t�
�o�z�r�5�z�:�|�3�{��K�
�{�b�Z��0���� ��^��]����7��$�/�_��p�;��2�N��R��N��<�!�|�k�C	�"�2�,�?�X�3�:�5��<�>�6�~�7�.�<��>�{�D��`��F�	�}�G�1�H�D�K���N�m�T�m�W�^��#�N��6�[��,�_��`��^�i�`�l�t�n�8�o��xQa���v�[��0���[�Z�=�}�ab�}Qb���y��8���z��E�?�d�m�d�Y���A��/�d�9��J���|ab�Q	b�|������Mab(�Qb*�)��F����S��R�9�8�A�Yjqb5��<��t�s�\�n�K��u���v��m�:��=��j�R�u�.���"�E�$�h�+Qbf�!�"��r��3�L�5�l�g�g�P�qbq�.�m�0��2�;�4�2�6�s�u�/�7�R�:� �\�>Qb��]�G�����;�e� �j����G�jqb��F�<�f�|�=�I�9��J�+�L�<�z�N��R�7�U��X�=�c��Y�w�ZQb����S�_��>h�i�J�<��U�"�9��h��a��`��)�z�a����A�~��6�F��qb��a�E�c�J�c�e��$�b�m�4�fQb���L�I����?��>��C�*���O�sqb��k�S��G�&�o�{�+�p�7�N�?�t�s�x�G�z��~Qc�>�����I�Q���G�\�Y�Y�q@c&��/��s�o����O�
�D�W���Q��u�F��b�����E�Q�k��V���}��7�e�%�d�P��&�6��P�)�]�*�M��+�I��-�:�.�0�J�P�2�3�6�A�
�Z�7Qc���&�9�r�R��%��$�_�W���:��kac��<�;�?�!�@Qc��z����o�g��T��I��H��I�"��+��6�y�r�wac��L�W�L�NQ
c��?�o����+��*��~�m�l�
q�c��T�W�W�[�X�;�Z�>�Y�m�4�]�,�^�E�a��d�X�g�U�d�h�*�i�8�m�n�#�X�p�S�s�q�t�H�u�T�v�*�z�V�:�d�N�{��}�_�b�[��r�'��m�_��6��`�F�
�^��a�d��c����l�q��6��[��z��2� �Q�!�^�"��]�e�(�`�$��%�`�Z�+��,�K�/�a�2�e�@�3�`�:�	�;�g�>��?�_�B�^�D��i�j�E��K�f��L�Q��M�j�Q��U�/�h�V�l�X��_�\�,�\�T�_Qd����*�5��9�~��l��7��6�I�B���-��,ad��jQd��g�u�d�m��z���A�|�I��>�E���Z�ad��kQdʾg�k�t�������~��-��,�ad��q�p�R�s�L�tQ	dޛH��\��
�
�����eqd��{���R�$����m��e��-�R�f��z���	�c���
�s��<�Qe���l�A�@�g�f��y��^�e�d�{q$e��Y�V�c�t��)��|�#�u�&�$�	�f�l�(�2�?�)�~�*�b��,�#�1�r�K�3�1�5�y�7�w�8Q
eU�g�?�J���=��<������}�q	e_�>�M�[�A��C�h�c�GQ	ep���j������_�>���y�bq	ey�M��C�V�S�Y�L�\�\Q
e��A�/��h�9�8����W��V������5ae��d��f�b�gQ
e��@�s��B������'��&��
�C��:��}�<q�ae��m�W�s�Z�t�D�@�v��y�G�F�j�E�z�H�H�~�I�B��J��K���m��;�
�0�
�x�;�*��W�<�
�l��H���+����E�����
�"�D�#�|�}�$�^���'��*��-���.�=�2�F�O�3�r�7�^�8��9����;�f�@�A�A��-�c�C�K��J��L�r�k����!�N��P��R��S� �T�}�V�s�W�d�X�t�_�	�]�d�`�V�"�a�F�c�	�f� �i�#�j�+�V�(�l�$�n��r�p�@�v�&�{�y��%�]��Z������[��'��'���&��N��)�'��)�(�w�*�g�,�*�1�+�3��5�~�8�w�>�w�9��<�#�;�#�=�;�9�K�>�\�@��5�s�4�D��E��G�,��J�5�K�,�N�T�O�y�PQg&�,�.�G��h��'�o�K�0��,������[q*g2�Y�r�#�[��_�+�a�j�c���d�F�f�T�u�g�Q�i�Y���h�j�R�k�i�m��r�S��W�H�s��v��U�X�xQgm�s����I�,�<�{�m�B�?�>�7qgx�}�b��>�p������W��(��`�	��
Q
g������e�t�n��/��.��� �pag��Qg��w�b��Z���{�v��G�m��2�w�v��c�zqg���m��k�O�B�q��t�'�p�c	�(�c��G���<�2Qgذe���W�F�9��F��1��0�qg��8�0�:�g�=�`�?�O�c�x�A�A�8�B�u�J�X�o�KQh�E����y���o�r�L�;�!��Z������kqh�S�{�`�U��Z�n�]��i�d�v�k�"��t�nQh<�l�T�Z�T�Y�s�6�,��m��b��]��V��+��*�s�r��5�}��0��Q�<�&ahV	�v�xQ
hb�{�K�L�w�|����h�eq�yhl���	�a��
���~��<�{��
��
� �|�j�#�u�'�m�(��*�S�o�Q�-�h�1�0��2�x�4�		�5�_�?��@�c�F��G�P�H��L�5�k�_��N�0�O�N��S��U�r�X�f�^��M�_�P�b��0�e��f�i��j�U�m��r�1��s��s��t�������3�
�&����-��>����~�!�~�s�)��+�#�-� �N�/��0�-�2�,�4�u�5�"�7�
��8��:�o�=�9�%�.�>�L�@�#�C�@�D�!�E�A�(�+�F�;�I�1�9�O�$�Q�/�|	�S�0�F�]�h�2�^�3��_�5�a�:�c��e�1�h�v�k�6�l�7�'�'�W�*�t�F�u�4�x�j�~�8�:��|��>��B�!��"��<��*���l��K�!�>�)�?�L�,�:�b�0��1��2�L�6��7�=�;��<�H�?�C�@�w�A�K�D�C�C�H�>�t�J�]�L�F�P�M�m�W�G�X�h�[�]��\�A
�^��i�J�p�N�r��u��x�r�|�I��P�O��5��R��x�	�E����
�Q��S�O������!����8�%��4��n�:�{��q�;��p�B�i�C��D�o�J��N� �P�T�u�X�t�]�}�a�y�c�~�f�z�m�4�Q�K��1�n�x	�p�`�z�2�~�3�z��c�{��4��5�u��5��c�
�-�
�6�Q	k_�K���75�!��\�[q
kh����Y�$��3�x�(�5�R�)�VQk���
�&��c�6�*��%�G�E��.�'q-k��0�Z�4�2�6�\�7�]�8��:�_�<��>�`�
�C�	��E��=�z�
�G�z�H��[�J�O�M�n�?�N�j�/�P�r�T��U�D�VQ
kҋ}�b�}���0��?��>��Sqk��Z�R�_�T�x�g�
�j�U�m�W�n�X�q�V�t�@�u�Y�[�Z�m�zQl�A�K�@��/��k����)�X�
�a��"al�^�r�Ql���V�E��T�O�@��S��E�J��4���u�F�j��@�C�B��I�vq0l6��%�
�o��R�,�p��t�x��q�"���� ���p��]�!�E��~�}�j� ��"�(�%�%��*�&�+�*�,�+�-�%�/��w�2Q	l��\�h�Y����!�����q
l��6�'�8��%�:��&�<�l�@�'�AQl����j��	����E�K�#�$�E��N��3�e�(��l��/�Oq.l��H�;�5�
�J�m�2�N�M�t��U��O��s�S��{�V�_�W�.�Z�<�/�E�7�4�\�W�_�c�a�6��o��b�C�c��e�L�9�fQ	l�:��l��h����
��{�fal��1Q	l��j��e�g�G�&���X��wq*m�m�E�o�D�q��A�t�G�u�R�x�!�|�F�L��}�s��C��@�� �H��J�
�<�]�P��K�
�?�����
�QmA�@��$��#��"��I�g �h��"��c�w��X���'�,����/��K�Jq-mU���0��X��U��Y��[�!�u�#�}�6�%��R�(�d�,�Z�O�\�.�Z�0�p�5�\�7�[�h��8�$�:�>�;�Q�=�V�<�>Qm��!��B��	�&�5�4�k��H��]�E�� ��o�-�^���q,m��F�j�m�H�A�J�d�L��N�a�N�T�*�^�j�]�W�%��Z�~�^�:�b�'�f�c�e�_�f�b��g�>�i�d�j�^��f�m�I�nQm�'����_��^�[�:�C��`��y�:�)�@qdm��s�v�b�{�k��i�2�`��v�c��K��g�1��\��J��{��
�?�z�u���Q�
�k�f�K��V��t��W��3��q��c��	�v��u�m�%�r�)�5�*�S�+�`�-�]�p
�/�n�:�l�k
�<��A�J��M�w�q�N�C�P��w�U�5�Z�y�[�8�&�^��_�n�a�}�c�~�d��fQ	n��R��.��e��v��Q��P��k�qn��m�J��o��p�!��s�c��t�B�uQ
nˤ)�&�t�o�{��h��q��p����
��$��san��|�O�~QnޣL�T�3�4����������I�Lq$n���K����$�	�/�
�H�`�
��w��:�N������P��W����1�#��(�A���)Qo)��[�$�{��b��?��>��3�@�#q�o4�/��1��2�#��7�J�8�	�<��?�:�@�>�C�4�E��W�H�.�J�a�K��N��O��P�	�V�a�l�V��W��X�j��Z��[��\�c��^�s�a�&��_��b���-�d��l�(�p�e�+�s�d�u��w�b�x�^�|�D�~�O�� �����6�!�)�	����]��+��%����$�v�%�"��m���%�#�&��P�'��/�Z�0�S�1�2�i�d�7�U�9�f�:�_�=�x�>�O�AQ	p�Y�R��9��4�a�`�3qIp!�F�(�H�X�n�K��R�m�S�+�U�*�X�z�\�V�h�`�9�g�,�i��j��m�f�o�o�-�s�	�X�w�g�y��o�u�|�'�g�}�2�1��s��#����L���
�V�9�
���Y��
�H�N��J��M�O�Q	p��S�}�x�o�a��r��#��"��qp��'�R�I�D�*�P���+�Q�-�.�Q�-��1�V�2�#�6��>�>�q�?�u�BQp�K��t��m�z��=����Q�#�(��H��eq�p��I�z�M��O�,�P��S�U�r�o�^�W�c�X�e�U�Y�f�p�#�v�g�%�m�
�q�Z��[�y�
�}�_��"�a��b��X��V��]��p��:� �?�!�q�v�^�n�"�U�$�r�%�`�\�(�b�,�c�-�x�/��5�'�:�U�>�(�@�6�A�W�d��C��h�H�g�O�I�R�|�V�e�W�f�X�r�[��_�X�a��b�:�f��k��n�j�	�o�l�r�Y�t�'�i�v�2�_�k�z����]��l��<�
�Z�����I��F	���$�m	�%�n�/��0��1�l�9��4Qr5�#�a��+�p�Z�?����+��q	r@�7�K�q�>��i�A�j�EQ
rV�k��8��5��>��K��J��
����m��l��i�?qOrc�M�E�$�P�~�Q�B�F�U�5�W� �Y�+�
�\�L�G��R�^�I�`�~�J�e�K�f�L�g�M�i�@�l�C�t�Q�v�,�z�	�o�~�U�p��q���r�1��/�u�~�t�3�	� ��w�v��3��x������_�����y�{�"Q	r��\�_�t�C�q�4qEr��$�~�U�6��2�=�&�(��)��*��0��3��7�
�E��8�R�	�	��;���=�
�?��l�-��B�D�C��E�&�G�5�T���H��I�@�v�K��L�?�*�N��U��W�U�X��\��dQsh�R�0�+��C��n��-���ass�nQsu�+��9�7�n�%�B�0�.��7��6�5qVs��t�I�w�q�y�V�z�&�{��}���p�����
�����:���L�C��*�!�� ��,�j��"��� �$�#�s�%��'��F��(�&�/�+�3�h�4�%�8�*�9�)�:�k�=�-
�A�@�L�M�o�P�0�X�Q�9�(�S�.�k�W�8�7�`��fQt%�2��l��k��j��u��t��i��h��m��l��qqt1�m��V�>��o�j�t�`�v�9�w�\�QtU�<������������ ��.q5t`��]��%�j��?��>��'�>��@�#�C�$�p�%�E�(�a�)�F�*�B�,�J
�-�A�8�G�9�[�=�b�K�H�?�T�E�M�N�_�P�l�\�]�`�L�b�c�cQ
t��d�������o��n��o��n��9�5�H��"��9q"t��m�\��t��x�^�L�y�����%������
���E�x�.��C��3��p��4�Qu(�(���~��u�$���`��Q�e�V�I�0��r��+��*��W�S��(��O�mq1u<� ��"�y�#�N�'�|�)�{�8�+�z�F�,��0�=��}�>��4�B�8�{��:�I�N�=�~�C��D�1�E���F��K��N���P�F�SQu����,��
���l��m��l��m�'����c�(au��ZQ	u��d��n��U�`�����o��Fqu��^�f�e�c�2��l�k�B�g�F�d�i��f�.��%�hQ	u¶n������	�o�@au��l�p�nQ
uҠ ��"��O�s�8����s��Z��au��u�E�wQ
u�j���
��	����Q�Bq!u��|��x��z��=��y��z�|�d�.�w�
�}����~���{���������Qv�2�E�n��!��"��5���l�\��M�5�R��,��#q	v.�����o�
� ��"Q	v;�3�[��:��+��6�{�5qvD�&�/�(�1�)�4��+��1��2�,�3��6��N�7Qvb��o�>�K�d���	���K�q:vm�4��/�e�7�<�H�A�(�i�b�d�C�0�F�%�G�%�_�-�H�Y�J�.�L�3�N�/�O��Q�0�%�S�1�\�C�e�d�H�g�I�h�@�k��l�x�p�@�r�P�)�t��u�m�vQv͵���?�I���`��g��f��o�A�Tav��z�9�|Q
v��n�=��Z���w�Z�M��f�{qiv���"�a��X�)��0�	�M�[�������8��Y��\�A��A��Z
��`�^�"��d�&�_�'�b�*�D�L�+�a�.�U�/�c���d�4�J�5��6�"��9�h�:�g�=��e�D�i�U�L�o�N�$�k�|�P�l�#�Q�j�R�/��T��`�o�p�m�p�c�n��f�q�n�&�l�r��n�n�s�t�s�x��yQ	w��R��P��q��p�����$��-�>qw��~�u��\��v��Q�p�w��~�	�
����&�� �1�J� Qw�O�����y��x������A��@��)��!�+�hqnw��)�F�,�"�.�#�/��L��$��i�3�%�9��@�;�(�9�>�y�?��&�@��A�+�B�0�C�4�F�2�:�	�G�5�H�3�*�J�*�L�e�O�"��-�1�Q�d�R�$�S�(�U�A�V�<�W�=�[�6�\�;�]��^�+�8�`�b�e�i�k�e�n�>�;��7��z�o�b�v�1	�z�B���P���\�8�^�����	�@��
��C��
�A�?�C��Qx��[��h��������������=��<�q�x����d���7�a�F��1���f�G�w���k���N���H��!���$�K�E�k��'���+�I�Q��.�J�'��1�l��9�k���<�L��?�c��A�M��C�O��D� ��F�\��H�j��J��L�	��M�N��P���X�P��\�'��a�R��e�Q��j�f��k�g��l��n�h��r�S��t�U�x�\��y� ��z�y�
��{�z����{���~��	�C�����|��*�c���
����V����P���3��������������L��#�b���%���)�f�\�	��+�
��5�"�	��@�]��B���E�[�8��F�
��GQy��R�"���j��e�/����?�y��6��Gqy���O�L��Q�e��S�l��T�U��U�S�p��X���Z�]��\�?��bQy��O��X��!�����?��>����������/�Oq%y���j�&��q�M��s�u��t���v�$�#��w�!��y�q��}�
�%��~�&���i���H���%��
����	��
�������W��Q
z7�(��h��m��l���Zy�����,qzA�� �%��%�$�i�+��*�)��,�`��1��[��:���@�}��A�,��E�Q��H�n�k��b��IQ
z�,�^�a��@��y�x��~��c��b��cqz���P�L��T�+�N��W��"�3�l��Y�_�`��\�l�!��]Q
z���0����}�x5�6���qFz���c�%��g�9��i�j��l�G��p�$�a��t�8�`��u�b�k	��v����_���i�h�c���/���`���	�A���0���
���G���p�c���d���H���f���U���e���i�f�� �k��#�q��$�3�h��&�o��)�2��*�w��,�r��-Q{$�t�7��.������	�_��l��q{/��3�q��5�u��6�m��:�V��;�F��<�v��=�z�A�l��C�)��D�?�|��EQ{O�A�v�~�5��F��)��(��E��D��#��"��#���2��q<{^��L�~��N���O�d��S���Y����[�{��]�w��^���_���`�&��b�e��c���g���l���m���o�t���r�X��s�
�	��w�5���z��
�����p��}�Q���������3���Q	{Ľo��<��	���
�W�V�Q�+q�{�������O���X�������^�� �c���"����$�%��'�T��)�f��*�2��-���1���2�g��3�[��9���A��<���=���C�j���J���K�l��L�!�^��N� �k��P�"��\�P���$�#��`�w��a�
�!
��i�i��w�n�:��y���|�e�m���	���e���#�\�u���K�J�.
���x���L�l���h���Y�T�� �N�M�O�O��#�P��%��_��&�}��*���-��Q�[��/�e��4�R�Z��6�T��8�Y��;�U�X��<�4��=�V��>�]��Z��E�!��F�X�u�|�&�D��H�$�[��NQ
|�u�J�p�C�B�y�x�E�D��a�f�x��ma|���YQ	|��H��$��A��@��~��#�	�&q	}���X���^���_�'��aQ
}�#�����'��&��/�9�s�PqL}��e�M�s�l��h�E��l�,��o�F�1�&�%� ��r��$��w�(��x�*�)��z���{�>��|�1��}�,���5���+�/���h�d���!������@���|�J�-���T�h�� �Q��!�3��(�/��)�.��+�0�[��,�X��1�B��4�9��<�6��=��9��@�i��BQ
}��E�b�K�Q��6��C�d��dqN}���G�_�8�2�t��J��1���K�5��O�;��Q�7�3��S�T�^��X�4��Y�=�	�<��[�n�X��^�?�E��_�I��`�A��b�G�V��c���e���g�C�?��h�#��i�B��j�>��l�&��m��q�N�F�K�D��y�'�L��}����G�I�H���B��
�n��
����J�\��Q~1�;�(����L�K���
���5��4��9�q8~?���N���R�Q�k	���U��&�	���'�T��+�{��.�@��1�&�a���7�
�V�Y�X��8�.��:���?�z�M�W��A�H��D�S��I� ��J�_���K�n��L�$�p��O�:��P�Z��Q�l���SQ	~��S�I�H�c�b�;�:�/�-a~��WQ~��@�X�(�,��v��'�.�[�3�.��R��9�s���O�iq~���W�g�[�L�\��F�)�%�.�T�_�x�`Q~͚�@�5�
�I�t��`���n��k�S��2�.�/�O��a~��fQ~��$��R��}��I�P��L�L��b��1q~��j�E�l�7�;�e�o�*���\�p�8�a�r�s��s�]�j�-�v���]�x�n��z��^�{Q�O�!�Z�I�f��
�	�
�m��*��=�q:!���j�	��
���^��`�R��a���b�_��i�`��j�b��p���q�7�)��r��h��t����u�@��v���z��`��{��w�L���B���j��������,������
���	��Q
��b��>�?�>��o��n��5��4��qP����\���0���D�*�E��!�H��"��r��$�F��%���&�G��+�I��,��y��1�m�U��3�^��4�[��5���8���<�u��=�_��>�.��A�U�`��B�I��D�5�g�b��J�a�c��M�d��O�e��S�i�J��T�f��U�`��Z�J�t��[�2�:��^�I�S����_�W�7���cQ
��0��h��5��4��5�-�,�%��Wq)���g�N��i�O��l�P��m�Q�U�T���o�B��p�A��q�V��s�9��t�X�5�1�?��u���w���x�{��y�W�Y��{�Z��}�
�D�/�[��Q	�R�\��N��#��"��;��:��_��^��{a�[���	���]�R��Q�o��o��N���+�O�X���/a�z���y�8����Q
���U��^�?���L��G��F��/�ma����"�q��(Q���o��t������K��8�O��D��{�I�,�����9Ua����.Q���Z��4��t��u�g�.��*��{�'�� ��wa����4�y��6Q
���k����
��L�Q�c��q�p��p��aa����:���x��?Q	�֗�V�����C�:���a����G�1��I�m�G��K�zQ���Y�\����/�2�n�K�@��������W��V�������q����Q��S�+��U�R�c��W��,��Y�
Q
��#�5�%�.�O�@��"��G��F�������`q���^���`���m�'��o����p���s�|�y��t�A��xQ�F�F��p��S��R��K�F�.��R��=��<��1��4��W�A�V�@a�W�������I��Q
�e���e��V��a��`��s��r��_���x��Y�lq)�r������w�4�F�H��-�y�}���&���%���y���[��"�'��#�U��)������*�%��,�)��.�0��0�3��*��1�~��9Q
���A�X�� ��%�4�A�S�^�W�.q&����@�c��B��-��D�+�E��E�J��F�p��N�F��O�]�^��S�a��U��,��X�1��Y�)���Z�@��_�a�
�y��d�%��e�/�&Q	���f��}�]��l�E���:q����h�b��j�}��k�E��l��)��r�w�Z��s�+��tQ�(�0����	�x��~�����y�/��:q8�3�4�%�w�<�2�/��{�3��|�6��~�7���X���8���9���:��
�G�a�;���<���o������=���p���\��[��d��q�� �x��"�a�\��#�b�7�c��%�+��'�g��(Q���x��d��
���&��;�0	����	�q����.�j�s�k��2�n�$��4�}��5�{��6Q�����4��J��%$!�{�?�H�Y�J�e��v��A�@q����:�t�m�X��<�"��=�h�n��>�x��@�~��B�<�p��D�q�vQ�͉�F�k��t����)�4��b��5�:a����GQ
�ےN�p����	��%����1"�k�E�q����K�t��N�&��Q���R���T���V�Z��W���XQ
���\��{�W�C�t�A�B$/q�
��]���_�T���`���a���c�&��d��.�	��f�^�G��pQ�+�(�u�r����%��$��-��,���K���
���T����7q�=��w�%��z�#��|�R�"��~�$���!�i��Y���^���G�Q�]��
��W�%��j��/�q�^�7�8�s�nq,�m�2�0����5���B�=�@���<�m���]�+���h��!��:��$�?��&�C��'��;��)�D��*�8��+�}��,�3�E�?�9�
��0�4��t�6Q	����4��m�q�t�i�p��\��]�7qe����6�^�H��8�]��;���<���>�)��?���A�V��E�Q��F�K��G��O��J�W�k�a��K�J��N�o�\��Q�M�`�+�c��U�Z��Z�P��]��_�I��`�Y�$��a�U�4��b�q�N�M�[�=�X��d�T��e�M��k�R���n��_�a�S��t���w�r��{�I��~�S���l����m�����
�c�F���w���e�d�k�b���n���l�G�����Q�i�n��d��c�f���\������!�� ��}��|��;�L�d�o��t��{q�{��'�:��.�g�j��2��p��3�q��4�M�H��8���:�N��;�W��=�~��A���N�p��P���QQ
����R��.��i�Q��8��5��#qr����V�9��X�3�z���\�u��]�v�g��_�y��`�6��b�!��h�.�}��i���k��w��l�/��q��5�
��r��<��}�
��~���
�t���"�o������D���Q�	������ ���8���K�v����;���
��5�7��'�B��*���+�O��-���J��.�=��1�D�1��4�K���5�;���6���:�P��=���>��4�&��A�K��C���G���H�U��J�����K�D��MQ	���
�*��b��o�D��(��?��>��Iqw����U���W���X�Z��Z�O���`�J��b���f���g�����h���#��k�"��q�!�?��r�%��x�6���t�C��o���$���{���&����,����G�Y���T���2���&�W�(�� �'��#�X�)�B�S��'�;��*���/�+��3�Z��6�V��9�7��<�(��@�*��A�y	��D�[��N�S��O�,��P�a��R�p�7�Y�q�n��_�q���a�4��e�I��f�B�B��g�m��i�h��m��r��p�s��q�D��sQ
�y��,�;���S��qZ����z�R�z��y���������j�|��
�~�,���}�x�����{���	�������������z��!���$�v���%�
����&������(�l��*��
��-�j��.����0�m��4�
��8�� ��;�~�����<��!�Z��?�)��B���C����D�W������F���HQ
��z�0��p��%��$��_���J��S��R��_��^��]q�H���Q��.� ��S�#��T�*��W�$��[�)��]�(��_�+��a�!��d�&�,��f��'�<��m�4��o�v��p�7��t�.��u�]��v�;��w�2��}�8�:�<�2���5���3���2��
�\������1�9�/���-�F�\�C���B���6���5���=���E���l�}��$�&��&�?��+�G�I�D��-�>��.�K��1�J��2��`��6�N��7�P��9�{��<�A��=�^��>�Q�L��?�a��A�O�@�M��C�R��J�T��N�U��P�H��Q�S��U�k��V�U�[��W�	��Y�Y��\���^���_�X��a��b�
��e�)�V��f�Y��i�]�_��n�W��o�?�V��p�Z��t�o��w�[�C�X��x�)�
���[���\��
�v�]���S���*������������?�;���'���l����������m�I�&���"Q�h�V� ��x��W�!��v��?��>������a�s��(�S��,���-Q	�}�)�(�)��
���v��
��M�fq_����2�@��4���6���7���;�/��>���C�,��H���I�$��O���P���S�.��X�/��\�
���`�
�-��b�0�q��d�2���g�
��i�h�f��j�A���l�X�,��n�B��o�1�3�p�4��q�8��s�:�5��z�
�"��{�
�U��~�7�'�9�����,���v���?��	�
����=���<�6�>���;���G���{�A�� Q	�0�9��T���2�w��H��G��F��Qq@�9��'�]��)�C��.�R��0�=
��2�*��@�<�C�D�:��F�?��I�D��J���M�k��N���P�-��R�E��Z���]�1��^�_��_�-��a���e�2��h���k�6��m�8��n�:��v�<��z�d��}�9��~�=���>���?���R��Q
���}�������u��,���<����q"���;���>���
�W���Y���Z���X���3���[�(���\���]��!�@�	��$�
��%���&�}�_�K�
��-Q
��~�p��p����
��!�� ������'q;���4�j��6�b�K��7�x��8�b��:�r��;�P��<�S��?�e��@�r��D���F�]��I�T��K�g��L���O�^��R�f��T�h��X���Y�d��^�j��_�k��`�i�=��a�j��c�=��e���g�v�g���i�7��kQ
�i�,������w�v��t���?��n��L��}q�v��p�S��s�p��u�n��w�m��l�b��{�c�/��|�~��~�y�[���/���6���w��Q���=����
�f��$��K
��O��p��Uq�����G���E���\������u������F������4��Q���i��l��G��F��o��n��]��\��Q��P��Q�+q����%�`���(�~�G��+���,�u��/���0�
�S��1���4����5�_��7�	��8Q����}�0��8����~��g��f��!�� ��_�l�]��L���L��L��a���@�W��B���E���FQ
������u�5��(��s�|�[��N��kq8���L���O�_��S�
��T��
���V�c��[�:��`�x	��d���n��k��r���x�0��h��y�D��z�^����H������	�U���J���@���S���I�����!�m�x��$�{���%Q
�����6��'��&��)�=�#��d�%a���Q���)��v�}�����"�#�K��d��U�a���Q���q�V�K��&��V��U�u�,��
�xa���Q�ė_�H�o��B��K�m�v���#�(����}q���#�k�&�B�'�t�n�)�E��*�G�X�o�,�>�W�6�-Q'�ꁖ1��k�\�P�4�[�Z�m��l�4�X���
�{�m�f��b�2�)�(�?�>�[��@�]��L���;�J�9��(�{qU��<�L�>�>�C�A��B�Z�D��3�E��,��H�(�I�H�&�J��
�L�)�v�N�1�P�!��4���=�x�v�H�w��A���C�}��F���J���N�`�T��T�w�h��[�]��b�P��9��f�Q��m�S��o��R��r�7��s�U��z�T��}�;�����Y�$���:�e�p�d�0�a��Q���"���8��3�� �����N���B��=�k�|�l��:��!�m�H�?9�+�*��6��+�q����Qa�����2��Q���4��Q���3��J��u��t������O�t�i��;����%�Vq������5�x�T���s��%�)�7�(�	�G��*��+�.�1��/Q	��f����-�P��Z��;��:���a���4�=��9Q�����Z�-����g�{�J�s�'��E�|�k�\�]���N��y�\a�2�,Q�4�L��B�a�r��@�>�q�p�O�+�x�n��q�E�2�6�4��.��7���P��>Q�S�;�1�(�1��>��W�F�a�e�b
�E��>����*����x��O�0qp�h��C���F�u��G�<��I�o�+�u�J���K�h��T�p�Q��V��t��Z�r��_�q���a�z��d���i���l�n��p�s�x�@����x���z���{���|�9���}�� ���(���Z�Y���%������!���#�'��
��$�u���,�/���t���K��)���^���K���l���*�-�$�.���1�������$�0� ��'���(�w���*�2��-�3��4��5�2�7��7Q	�)��L��
��������
��q!�2��?�0�<��A�;�9��D�=��F�:�@��H�6��I�;��?�D�v�}��K�B��O���Q���T���]�-��_���aQ�l�&�w��n��7��<������9��8��Aq	�w��g���j�G��k�]��m�-��nQ���J��L��'��&��m�a�j��`��M��L��S�l
�oqf����u���z�N��{�O��|���~�#�����&����x���"������p���{�g�6�V�x���`�� ���!�
��'���*���.���4���:�������?���@��A�1��D���E���F�X���G���N�	�f��O�&��Y���T�;���Y�:�[��[�p��_� ��b�;��f���h�p�7��j�R��l�3�E�!��m�(��o�|�0��r�"	��wQ�_�F��t���(��X��e��d��S�L�y�|��~��G�5�4�K��[q
�q�c�`�f�i�h�j�^�l��m����nQ
��� ���F�R�-��7�F��&a���sQ������L���p���Z�:�o�n�W�o��d��}�a����	����0���OQ	������}��|��g�%�Q�@�Lq#�����=���H����r���%�=����a���V������"��M��#�>�R�?��$�N���&�I�y�4�u�~��'Q��I��>��O��J��I�'�l�����F��T��?q����-�E�{��/�Y�.���1���4�|�B�a�&�H�G�I��6Q�	�J*�P��\��Y��X��!�
�6���v����K�)����{�K�9��|��A�M�.�@�<��"q'�$��=�J��?�Q��R��E�;��G�O��I�d��J��/��L�G��M��a��O�S��P�[��R�S��U�R�>�3�V�T�!�&���V�L�X��WQ�[�Z�"��X��7��6��!�� ���U��X��ea�f��]�W�3��_�%�{��bQ�r���@�����.��/�$����1��0�������I�<��+�ha����j�q�_��l�F�a��mQ���c��)�� ��	����5��4��������������a����v�l��*��y�y��{Q���{����'��&��!�� �	��"��%�3�4��"��!�m�h�qH�����u���Y���w�t���x��	�
���4�s�v�"���y���}���~�|�y������1�{���z�
�������R������`�3��"���$�{���(��)�4��+���,� ��-�G��/�<��7���8�Q��9�2��;���?����B�;��EQ
�-������J�O�H��>��]��\��[q �7��M����O�	��W�
��Y�!�4�x�r� �%�z�y��Z�u��[�:��_�p��a��}��c�~�}�_�|�K��eQ�i����T��3��2���J�u�|��J��K�O�<��N�^a�z��k��S��nQ	�����T��i��h��U��T��7�Xq����v�	����x�U��{�V����~������$���	����
���Q����x��z��9��8��=��<������A�+��5�x�A�_��"��3qx���s�x���w�H�v���%����E�{��#�z��%�m
��&�}��1�y��2�~�M��3����8���:����=�]�%��@����A�z�&��C�U��K���O�����P���V���W��1��Y����	��[��4����]���a����c���f�V��i�
��l�S��m�z�N��o���q�U��t�
��w���|�k���*�������-���	�2���{������/���,���(������/���<�����Q���5��V��)�0��v��]"+����Eq�����$�%��&�M��+�Z��,�r�>��-�O��1�C��2�W��3�<���4�)��7�!��9�	��;�?��E�D��F�#��:�E��I�=��J�A��K�� �<�;�8��M�@��Q�M��R���U��X�K��Y�G�O��]�0��d�F��e�L��g��R�0�I��i�Q�J��l�R��m�8��p�y��q�+��s�M�%��u�L���w�m��y�7��z����J�8���N���|�T���<���U���V��
�h���V���W�S���H����\���Y��!��U��%�}��(�~��+�|��.�Z��/�^��1�4��3���7�^��=�`��>�"�T��GQ���cy��B��7R��b��_��^������Qqv����M� ��O�e��P�a��R�b��X�l��[�m� ��^���_�_�j��`�n��c��9�\��g�h�k�o��j�D�g��k�[��l���o�+��{�q��|�w��}���"���3��
�*���6�s�o����x���p���u���A�]�����$�z���'�w��(���*�$��+��{��.�#��2�y��7�f��8���=�|
��>���I�}��K�r��L���Q�t��R�N��T�F��U���Y�v��Z�d�?��^��\���c����"�Q	����a��)�m�~��������Ea�����S�R�)��Q�Q
����a�Q�z�9�&�wq	���#�+�%�,�&�n�'�G�,Q
���K�D�}�G�5�t���c��bq;���0��e�7��f�:�>�=��h�?�^�h�@�B�C� �D��i�F�I�H�J�p��K��F�@�M��P�|�Q�k��j�R�}�T�g�m�V�U��Q�W�-�Y��
�[�L�3�^��c�p�/�d��k�eQ
� ��l��A�6�C��.���L��s�lq=�*�i�l�f�o��Q�m��n�o�w�p�?���s��o�t��C�v��p�x���q�{�Z��r�|�[�}�_����s���t��d�	���u�
���C��v��v���w�O�u��y�I�}���?����@��Q���E��~��[�V����	�}���������vq5����	�i���C����y�k���5�
���G�I�F���O���K���s��!�w�O�N�J�M��"�P��%�l��'�A��(�n�Q�x��)�S��,�R�T���/�,��4�U��5�q���7�E��9Q
��J�t����h��{�4�G�,�r���ca���	Q���K�n��k��l�!� ��^��q�R�4q%��
����=��t����B�&���>����?���@�2�T��A���B�V�U��C�X��J�I��M�W�]��|��(��OQ�;�E�(��r��M�,����s��r��;��L3�I�h��r����a�N��W�K��Y�]�{��\���^Q
�[�F��2����������W�
��Ea�e��c�_��B�$��e�`��fQ	�p������8�9�_�h�B�a�y��j���nQ���L�	��X��%��$��;���8��'��/�Ra����xQ	���o�D��P��-�W�P��0���a��	��}�b�W���?��Q���f�p��Z���p�� ��A��@��u�9��,��Wq�7���z�4�������&�n�"�&������I��|� ���}���R�V���D����W�����!�
���"�O�a�R��&�c��*�P�b��,�,�B��.���/�C��0�|��2�3�e��3�c��5�d��f�>��6�	�h�g��9�A��<�!��>���C�)��D�(��L�i��N�j��P�k��R�]��S�_�:��X�
�l��[���^���_�2��b�]�`��k�g��m�a��F��o�d��q�L�W�k��r���t�C�j��v�J�N���[���H���I��
�w���;���#���J���L���G���~�� �O��"���$�N��(�G��)�M��*�P��,�Q��-�^��5�]��:���<�_��>�
�}��?�@��E�e��J�g��L�f��M�3�(��T�b�N�q��U�{��Z����[Q���I��2��5��>��t��5�	�|�S��������/��.q5��F�A�
�/�\��h�?�4��k�I��n�H��r���t�K��u�}��{�~��}�L�C���9������Z���s�a�M���N�v���K�Q���@������P�� �R��"�*��#�r�S��&Q�o�>�*�!��F��3�9�s�-��p��g��`��i���e�"�@�w�'?�Z���7��@a���dQ����*��	�9�m��,��g�\����-�	�D��nq|���h�~�-�j�@�l��.�C�n����/�L��4���5�x��6�N��8�O��=�8��D�P��E�Q��F���K�7��M�:��N�M�;�t��P�f�J���R�~��S��g����W�!��X�q��Y���[�"��\�K�6�5��c��d�-��g�S��h�#���k�g�h��l��$��d��n�8��r�%��t���v�C��x�.��~�i������&��
����5�'���(���u���)�k�,���j�y���k�j���f���g���l�� �-���#���$�Q�m�V�V��^�/�Z��P���S�F�
q
�x��&���(�f�&��)�5�'�<��*Q���(�C�#�d�;��>���[��^������eq���,��.�n�n�y�P
��/�+��:�^��<�m�B���?���A��i��B�W
��N�%��J��\����]Q���	��4��]��\��9��8��=��<��i�8q:��	��c�?��m�g��p�W��}���~�����G�
������7���p��!����"�p��#�����(�f��-�R����q��.�
��2�	��4���5�'��:�|��;�A���=���@�N��B����CQ
�j���t��g�(�I�(�5�r�����R�&�Qq	�w�;��=���?�P�>�@Q�����f��u�d�k�_�H����
��4q*����K�2�E��F��M�H�L�I��O�`�J�5�M�	�O�g�Q��P�R� ��Q�)�S�R��W�V�T�X��[�W��\�X��]�Z�Y�\��`�[��b�]��gQ
���a�������,�\����!q4����k�l��q�m��r�n��v�p��z�r�u�o��{�s���q����	�4���L���t���z���u���v��� ���w��!�h�x��#�P��$���%�L��&�H���*�>�`��+�s�F��3Q	�A�
��B�w�r��,��+q_�J��8�e�e�I��;�f��<�
��>�d��A�9��B�J��V���Z���\� ��e�!	��f�"��p�'�9�%��q�)��x�.�+
��z�*�-���(�9���6�0���8���
�7���E��&�:��'�C��+�@��,�=�A��/�>��0�?�@��2�;��3�B��7�D��:�9��B�L��C�G�K��K�I��N�H��P�J���Q�M��]�4��_�Q��`�P�R��b�O��eQ�1�26��(��#��"��i��h��o��n��+q�:�<��l�T��n�X��t�U�M��v�[��~�Z���Y�.�/���F���&���\���,	���#���1���H���	���Z�
�� ���"���$����%���&���'��7��+��[���,�$��-�%��.�'�W��/�/��0�U�3��5�7��7�>�W�A��9�D�$�F��;�G��=���?���B�\��D����J���K��T����Z�n
��[���f�C��k���l���m���p�}��r�o��s�
�	��y�G������

���P������`������ ���(�6��)���+�v��-���A���B���K���O�	��P���Z���]�L��b�D��f�`� ��l���o���p�l
��q�"���.����#	���	�%��
�$���&����'�"�)���� �(��,���4Q��!����Q�u�@�9�8�q��t���T��*a�(�7Q�-�e��*��M��L���%��$��u�tq�T�9�A��>�f�C���@�D��F�+�G��K��A�M�5��B�N��C�O��F�P�U��G�Q��H�T��I�U���M�V�^��N�
�]��P�_��Q���R��{��U��t��`��V�|��X�}��\���^��[��d���g���l���m�q���r�w��t���y�n���}�m������y���[���x���Z�	�S�*��
����*�������A�����	�������3�
�
������j���
�� �i��"�U��#�V��v���$���-�w��.�x�e��/�t��0���2�s��3�q��4�_��5�
��;���<���E���G���N���O�0��P���Q���V�F�k��[�S�
�B���^���_���c���e���0��g�q��h���j���m���n�r��s���w��m��~�n�-�o�~�u�7���3���"�m���C��T�4���C�s��
a���&X�
j��(�c�	a�0�	�+�5
�9�G�v��a��� ��(��0��5��;��E��M��^��n�G���`��������������PK
!<�aZ��1chrome/pdfjs/content/web/cmaps/UniGB-UCS2-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�UniGB-UCS2-Ha �V�W�~��[�?�>�H�R��F�P��i�B�D�=��@A	���3�2�
	��PK
!<Vd�6�6�2chrome/pdfjs/content/web/cmaps/UniGB-UTF16-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE���������!A^��J�vG98�<
&
�Q�Z1&#bW	e�-�7�vO@�mj�T�	z
!�<�%�e{tg

9
7	IA"D�
J59A��!�/�&�6�X�Y�O�H�}�r�:	.B�)}�IxA���R��S��{

Q�V/�B�@��r��-�
�;�]��a��l�I�Z��.��-:�*�+��&�
��C�)�V�C�9�
� �5�;�p��z�	���g�`�}�<�1�^� �u��/�6�(��a�6�i�#�U��X��K�p�D�m�;��M�$�{��I�$������|�j���=�z�!�j�f�%�:��H�~��]�N��Z�h�M��M��<��;�@�R�c�:��j����M��)���k�b�r�7��&�%�H�C�9��c��>�|�}��q��z��e�K�q�Z��$��
�}�H�9�S�8t�T�m�l�5�(�_�U��`��i�(��F��}�3�f���v��U��4��
���D�9����UF�B�8��
�"�F�k�w�5�d�;�V�Q� �C��j��{�q����g�n�X�5�*��`��A0g��~�#
�Z��*���<��Q��~�4q!�}
"	�*�5�E��n�5A.0?��UT"`2�L�)+�*o�}U`��Z�q�u��Wt
+�x�u�AR�92
�?*�
AI���eQN�#��%^�L�P�-�������1�m��
��c��.�K�$��TQN"�i��p�9�b�T�����g�j�h�QN0�~��P��m��l�
��o�n�#�)�f�x�h��3�8QNC�.��{�z��]�d��	�	�<�9�S�z�`�5��`�\��
����	�PAN\�u��8�N�9l�)�j�;A�
�7AN��YANr�QN���~���t�y�9�2�1�w�"���x��/AN��^�t�S�yQN��q�}��9�A�(�!�a��M�*�����x�AN��T�`�6�3�E�pQN��a�1�I�xI�k�H�/��&��?�^�w�3Q	Nє�Z��!����x�y�Q	Nݥn�������k�r�FA	N��qG�)7�$�3�/�AN�#�+�,�3�$�p���/�6�M�!�^�`�_�xAN��5Q
O�Z�H�#��D�Q�P��N�]�\AO*�@�\QO/�u�F��|�C�B��K��J�W�V�m�l�E�D�E�xAOC�I{QOM�J�9��$�3�d�g�f��O��N�or�W�Z�M��>	�_AOp�fAOc������W�:�+�j�_AOf�pQO��=�������p��7�6�C�B���AO��D��Y�[�"���
�5Q
O��i�P�{�n�M�:���"�!� �7�)AO��S	�����8�EAOťHRMAO��Q
OכL�e��,�Q�P�O��r��N�K�D�SAO�r���Z�)AO�E�=�NAO��6�o�mQP	�b�P�W��s��X�s�r��K��f�YAP��e�Z�t�w�=�6��Q
P%�Y��|���2�A+P:�O�H�)�v�!�"�+�$���E�Z	�G�P�t
�#��7�l�I�j�P�}�|��-�0�o�^��1�6���7�-�API�*�_�6 ����^�1�h�?�+_�V�7�<�s;�$�S�L�%AP;�e
;X
�Q�`"#,QQ��2�7�6�1�0��n�)�8�AQ�@�sA
Q�V
	�1
�t�=�|	AQ�r�e�tQQ?�8�@���O��1�\�8�A�u��4�}�|�aAQQ��D��a�>�<�dAQR�eAQS�QQh��<���'�Z�z�f�f��9��>��u�H�k�B�k��>����6AQ��0��]��a�`�&��|�o�A	Q��z�;
��n�+�*�S�RAQ��;Q	Q���M�Z�
�4��F�
�h�SAQ��r�>�OQQė�$�s�r�~�#�"���:�:�
��[��Z��}AQ۔&�r��k�9xQQ��T����e�5�p�&�����+�o���G�&�`��i����!x�n�CAR�s�2�~�#��{�o�)�*H�l�K�3;�8�MAR�nAR	�sQ	R(��i�:��W��V�m�Q��<��QAR3�Q	R6�E�]��@�9���b��}��|ARA�&���L�\�M�	��x��X���F�'�wx
�\�y�z�9�:A
RD�H�K�&�<�c
�j�~�L�y��aIA
RK�	�"




Q	R��o��i�)���r��cAR��n����p�%�L
�.�g�Y�j��V�z�%AR��>AR��QQRսM�u���C�T�l�u�t�u�I�IAR�W�x�1�Y��b��	���0�n�m�3�E�U�B��a�!�n�V�:�=�<
��`�AR��2�`�%�F4��O�NAR��d#.		
Q
S>���N�U�.�s�r���S�~��wQ
SN�?�p�T���NK�f��(�,QSZ�p��j��Q��P��3�V�;�Q�:�n�g��=�O�
�/�.�7Q
Sn�j�o�$�)�t��
�N�/�j��%�H�Z�9AS�a�+�H�:�U�~N�d�1�T�QAS��AAS��6Q
S��~��+�f�B�q�`���!�.��W�0AS���4�9��Y�	�J��j�$�-�%�x��l�M�Z�SAS��y�S�b�QAS��O
QSߧ�m����w��h�D��b�U��3�0�O��f�H�~�[�P�b�I���l�:�0�W�]�TQT�m�'�&��k�p��x�[�Z��U�,���4�)�;�-�h�&�V�@��~���2�UQ	T�,�����(��)�<�Z�WQT&��u��6����/�X�_��'�N�)�J�\��3Q	T8��'���S�_��0�!� �7ATF�w��b��&�n�{�^�Y�>
�-��@��s�*�w�$A
TC�



 
ATG�
QT��G���J��_�L�O��^���;�q�8A	T��E��R�/��-�qQ
T��g�s�V�6�����3�2��G�AT��9QT��%��u��1�i�F��F��G��F��E�Z�k�A'T̪'��S�|-�U�Z�M�V�/�o�(�t�?�E�j�y�r�.�p�o�,�:�G�	�I�S���A�@	�C�PAT��G"�HA
T��C"Q
U*�<�j��u��t���*�}��H�QAU7�GQU<�F�j�e�r�p�w�v��C��:�uAUI�@�)�0
�S�4�\�%�>�}�t��Q	�,AUO��'
�LAUT�

Q
U{�Z�G�L����Y�B���%AU���t�a{�N#4�]�\��A�,�#AU��[�iAU��%
QU��R�2�]�x�y�x�)�(�U�T�E�DAUĪj�}�j)�g�AU�X�N�#AU��EQU��!�X�_��J�i�h�m�l�3�t	�R�C��3AU�k#�1�b	�k�j�u�z�
�?�<%,AU��r��"���
��WAU��W




QV,�	�r�S�0�g�A��>�Y�N��
��S�l�[AV?�AVL�	��&
AV@�{��Q
V`�p�@����V�>�{��i�:�YQVo��,��P�D�E�D��9��8�}�,�NAV��v��V��
�FAV��
$AV��n�o�z�`�Q�I����J�E���	
�e���<��o�mQVژq��>��O�|�g�6�?�F��AV��}� ZAV��I�{�>�W�VAV��aQW�s�L�&�s���4�B�b�AA!W��W�m�@��Q��`�K�P�E�P�r��!��?��k��k*�E�b�N�!�6��@�kAW�F�AW�v	QWd����'�&�S
�1�2�Z�S�R��UAWw�M
�#�4�b
��OAW��AWs�:QW��CC�#�8����M�B��I�H����~��A*W��>	���R�Y�$�/�$�o�-��W�'��{�
�v�{�=��X��>����(A	W��*�?
�
�>�S�tAW��K.

QXJ�c�*��f���
�A�6��A,XX��}��/�.mr�q�^��1�O�:�B�G��G�u��C�B�c�J�t�e�V�=�=�K�&�0�g�����Q��Z�E�@�YAXW�
N��e�0�)�^�'2�>�y��
�M�<�/�P�X�y�
�K�\AXc�=(.


QY"��t�C�F�������_��^�/�)�E����AY4�h�D�5�]�G��I��{�)�M%��f�S�_��a�N��M��
�B{�'�*�qAY>�|*�<�{��QAY;�E
	
Q
Y��u�g�f��z�=�+�*��:��AjY��w�]�h�N�(��	�o�U�<�{��s�h��U�B�S�?�1�F�A�E��;�e���,�s�`��T�sA��{�n�O�%��P�E�r�H�~�%�2	�_�z�d
�1�2	�e	�L��Y�^�;��$
��
��� �u�.�B
�_�^�W�B�G�FAY��-�#�]$�c�T9�'��-�0�?��0AY��v!&$
"~�F*0
(Q[P�0��t�u�)�\�>���t��w��}���AA
[b�9��-�2�K�o�8�T�?�N��I�\A[k�Z�A[r�<Q[}�X��T�Y�S���D��'�z�E�n�e�I�e�����.A[��!Q[��1�4�9�8�-��d�}�r�`��x�����<�%�V�;��7A[��I�	�(�{�d�O�I�d�}�7�f�7��A�q�g� �E�~A[��3A[��X

Q[ݘ[��?��x��
�t��u�M�:��'� � �=�_��w�v�uA[��j�P�F�B�y�$�cA[��
�'A[��yQ\���B���B��3�~�4�.�MC��(��_Q
\�:�n�;��\��C��B��y��x�'�YA	\"�6�(�=��(�G��y�xQ\7�\�3�~��B�E�i�`�Y�!��;A\E��A\H�y��Z�7�c�L�$A\P�hQ\^���:��q��F�K�v�y��z�mAb\n�t�m��R��7�e��>��t�5�<�3�2��|���q�d�{�l�e�l�y�1�(���d�'�
�T
�:��v�O��@�;�^�\�&��G�2	


�_
�H�U�`��	�J�$��s�
�<�c�R�e�T�7�K�A\l�>4�QR�d�%�x�4	�k�`#0�A�,
�;��'�V�x�]A\m�.&: 3J
$" 4
Q]��w�s�����V�I����S�)��z��A^���g�jQ
^� �=���~��)�H�{�Y��A^&�{�H�	�E�N�;�ZV��!�����mA^%�C5��!	�:�|�m�%�$A^,�Y	Q^r�D�.���j��5��4��O�j��w�F'��o�hA^���v��?�[�p�zQ^��=�Q�8�6��?�6�'�v�S����;�u��.��O��N��{�Q�A"^��5�]���F�K�#�6�0
��	��O��L	�Q�9�S�5�f�%�I�J�U�5�]��]�jA^��`3�L��j�qt�&�dA^��
4B	Q
_�T���;�:��#��"��)��(��s�A	_��z�P��F���X�iQ	_-�p�j��#��"��/��.�g�f�iA_9��V��`�w��^��
Q_L��l�e�d�3�2��u�a��T��5��,A_i�{��UA_]�T�2�+�u�\�A�j���A_c�Q
_��*�Z��b��_�K��(�e�!� �;�P�hA+_��7��n��F�<���'�&�s�r����H�d��R�.��T�:��)���!��.�e�n�=�B�+�*�E�V�|A_��E�I�b��A_��Q
_����
��O�:�!��l�
A`�8��X�1�y�`�p�_�B�t�8��@�P�#�U�3�z��v�W�&A`
�Y			
�t�g�l�c�bA`�\%(Q
`g��w�?�T�a�$�	����}�cA
`s�_�_��Z�K��G�
�"�R�5�h�SA
`z�_
	A`w�Q`����:���k��j��+��0���m�f��{A`��l��
��/��1�dQ
`جo�4�3�2��o�2�:��)�
�`Q`�L��w�0��<��_��'�h�T�#�Z�J�G�<�4��cA`��"�d��-�A
�2�q�� �Z�a��*%�}�y�i��$��W�B�5��*�k�:�A9`��9��8�;	�d�(�
�t�q�L�	�
�>��^�[�I�y�R��\��g�6�@���^�+�B�y�0�`��f�[��)�:���6�7�5	��]�Q	�oAa�] #6
&%.Qa���v�[��0���[�Z�=�}�Qb���y��8���z��E�?�d�m�d�Y���A��/�d�9��J���|Q	b�|������MQb*�)��F����S��R�9�8�A�YjAb7�<�A�"�G�j�V��]�`�'�1�D�p�$�yDAb=�tAbB�Qbf�!�"��r��3�L�5�l�g�g�P�A	bs�m�J�N��{�r�;��	Ab~�sAb}�6Qb��]�G�����;�e� �j����G�jAb��f�*��	�Q�2 �z�U�L�&�.�+�PAb��<Ab��IQb����S�_��>h�i�J�<��U�"�9��h��a��`��)�z�a����A�~��6�F��A	bߖE�w�O�����m�z�k�Qb���L�I����?��>��C�*���O�sAb��S�`�{�C�W�^��q�f�&�Ac�NAc�oQc�>�����I�Q���G�\�Y�Y�A$c(�/��	�D�x��[�
�.� �I�#��T�i�2�@�m��5�r�Z��)�q�:�2��x��f�6�u�D�eAc>�Q)�k��H�k�gAc)� 	Qc���&�9�r�R��%��$�_�W���:��kAc��;����=Qc��z����o�g��T��I��H��I�"��+��6�y�r�wAc��W�hQ
c��?�o����+��*��~�m�l�
AGcĩW�A��4�Y���0�$�c�u����^�E�,�U�V�9�R�R�y��a�h�
�b��5�Q���S���y��<�g���q���B�.��b�2��P�:�)� ��d	�O�L�:�p�;�gAc���O�	�K�H�
�C��_�|�i�.
� ��g�1Ac��W

	
		


Qd����*�5��9�~��l��7��6�I�B���-��,Qd��g�u�d�m��z���A�|�I��>�E���Z�Qdʾg�k�t�������~��-��,�Adשp�B�@�OQ	dޛH��\��
�
�����eAe�Ad��
�"��d�E�n�q�H�&�Y�P��O�Ad��	Qe���l�A�@�g�f��y��^�e�d�{Ae%�t�+�*��u�
�g|�9�x�y����Ae"�Y���u�$�g�8�HAe-�#	Q
eU�g�?�J���=��<������}�Aeb�M��	� �Q	ep���j������_�>���y�bAe���7�a�p�aQ
e��A�/��h�9�8����W��V������5Ae����>��	Q
e��@�s��B������'��&��
�C��:��}�<APe��Z�J�W�W�:�r�o�B�e�h�M�Z��#�'7�!�6�
�P�M���n��@�7
��
�7���;�)��K�=�N�v+�p�J�g	�<����+�R�K��^�{�,�k	�M�+�a�8�$�f������)�~�.��"�
�!��3� �o�l�1�HA&e��D)�s



	��'
/�< Ae��W�
�*�Z*��"�_�9�x��<�A�1�4	��TR
�AAe��s	

	
 "

A^	
Qg&�,�.�G��h��'�o�K�0��,������[Ag4�r��V�6��V�'�{�\�9��'�8��K��q�`�S�nAgH�T

AgA�c
Qgm�s����I�,�<�{�m�B�?�>�7Ag~�>��Z��*�8Ag|�b
Ag}�Q
g������e�t�n��/��.��� �pQg��w�b��Z���{�v��G�m��2�w�v��c�zA	g��O�?
��d��r�.DAg��m
	Ag��
Qgذe���W�F�9��F��1��0�Ag�0�^�\�P�o�
�>Ag�g	"
Ag��AQh�E����y���o�r�L�;�!��Z������kA	h�{�7�<�#�2�?��A�Qh<�l�T�Z�T�Y�s�6�,��m��b��]��V��+��*�s�r��5�}��0��Q�<�&Q
hb�{�K�L�w�|����h�eAHhv�a
�=�p��
��q�6�=�,����3��@�i�#�j�-� �L
�c�3�{�A
�H�6�@��|�V�
 �h�!�T�+�$��
�!�k���,�
�F!��P�,�9�]�^��#�^��T�$'�s�H�G�r�~�'
�N�M��r��%AKht�!	
 		
 
%+		!

(
	
x�:	A!h��u9�R�#�Fb�i���78� �3�^%�m
��Kp�e�Z���x�G�k�81�!
�*�*��57�N�W�|2#mAh��j68XA'
!4
@
%+"'*!"			Aj��A,hu�	$,F .	


#..H*)6.H
1N3R Q	k_�K���75�!��\�[Akj���
�A�w��^��g�GQk���
�&��c�6�*��%�G�E��.�'A
k��2�q�[�X�W�<�v�q�/�/Ak��\�V�S�N�O�R��
�"�i�9�^Ak��6	
Q
kҋ}�b�}���0��?��>��SAk�R��9�(�Ak��@�'Ak��m	Ql�A�K�@��/��k����)�X�
�a��"Al�rQl���V�E��T�O�@��S��E�J��4���u�F�j��@�C�B��I�vAl8�%��;�2�y��y��L�1�!�X�1�{�j��X�/�"�K�5�|��GAlJ�"	AlH�
Q	l��\�h�Y����!�����Al��'�K�D�r�s�u��&��3Ql����j��	����E�K�#�$�E��N��3�e�(��l��/�OAl��
�;��4�3�5�t�)�{�#"��F�5"�t�_�V�1�v�gA	l��;

Al��NQ	l�:��l��h����
��{�fQ	l��j��e�g�G�&���X��wA
m����+�3��G�I�@�5��_�+Am�E



Am
�t


QmA�@��$��#��"��I�g �h��"��c�w��X���'�,����/��K�JAmY��)�N�M�q�p�5�j�]��h��U�)����M�$�+Amy�\
Am[�&0Qm��!��B��	�&�5�4�k��H��]�E�� ��o�-�^���Am��j�{�&�;L�*�'�6�f�i�d�q�n�?�	�X�|
�)�^�J�I�5�DAm�^Am��bQm�'����_��^�[�:�C��`��y�:�)�@A2n�b��z�o�Z�U�X�1�6�m�T�%��g�l��t�I�2�A�k�:�;�6��.�X��n�q�:���-�+�g�/�r�]������3��OAn&��s�x�r�e&��An�&,DQ	n��R��.��e��v��Q��P��k�An��J�S�@An���8�HAn��oQ
nˤ)�&�t�o�{��h��q��p����
��$��sAnڎOQnޣL�T�3�4����������I�LAn�/�:�b�$��s�\�

�w�rA
n�K��]�9�.�S���a�4An��	 ,Qo)��[�$�{��b��?��>��3�@�#A'o6��Y�X�-
�]�B��L�k�j�g��O)�j�S
���d�� ��	��!�]A'o8��T���D
�%
�6�k�z�+�Q�:�O���z�	�5�b���
�J�+�l�Y����z��*�� �!Ao7�1
	
"*	H`
Q	p�Y�R��9��4�a�`�3Ap#�(�c�d�Q�R�u��~��|�}�^���b�s�{�;��O�f��
�	�Ap'�X*�>�C�g�6"�E�w�4��M�c�Ap1�R,$Q	p��S�}�x�o�a��r��#��"��Ap��I��t�o�H��D�m�}�@�d�yAp��R	�Ap��*Qp�K��t��m�z��=����Q�#�(��H��eA"p���]�M�V�
�J�e�$�&	��s�i�j�.�c�l�v�u�8�"Z�?���u�
��p�P�F�#�Z��A6p��z�G,�h
����h	�s�8�'��L�k	
��\�g�(�/�(� �=	�<�_��X�e�}�j�i�f��c��:�#�
�
�'
��FAp��O*3N&*
)60Qr5�#�a��+�p�Z�?����+��ArG�K�5�*�BQ
rV�k��8��5��>��K��J��
����m��l��i�?A)rf�E�C�2��#�+��B��l���u�J�5�4�A�A��o�j��'�,���Q�^�<���BAr}�L�f�_�4�A	rh�P#(.Q	r��\�_�t�C�q�4A#r��~�SG�
�U�N��|�e��~�s�.��@���U�Z���j
�6��Ar��6�s8�n�B�-�+As�(Qsh�R�0�+��C��n��-���Qsu�+��9�7�n�%�B�0�.��7��6�5A/s��I�1�H�a�b�S�@��	�;�>��l�3�l��n	
�a�"�.�/�0��x�}��}�N�@�\
�	��3As��@As��y.$Qt%�2��l��k��j��u��t��i��h��m��l��qA	t3��n�1u�J�j�*��{	�DQtU�<������������ ��.Atp�>�P�oAtc�]��w�W	�6�7	
�&�/�	��"�g�`�#�,At�#
5RQ
t��d�������o��n��o��n��9�5�H��"��9At�\�L�M�%�p�{�x���d��(�!�x�yAu�%At��x$
Qu(�(���~��u�$���`��Q�e�V�I�0��r��+��*��W�S��(��O�mA
uE�N�R��m�'�@�A���}	�s�~
�\Au>��D�"���!�d�e�W�V��Au?�"
%.Qu����,��
���l��m��l��m�'����c�(Q	u��d��n��U�`�����o��FA	u��f�i�$�x�y��C�[�9Au��eAu��cQ	u¶n������	�o�@AuͶpQ
uҠ ��"��O�s�8����s��Z��AuޗEQ
u�j���
��	����Q�BAu���{�LAu�x
�b�o
����	Av�
Qv�2�E�n��!��"��5���l�\��M�5�R��,��#Av0��C�:�Q	v;�3�[��:��+��6�{�5AvL�4AvI�1�A�,�1�vAvH�(Qvb��o�>�K�d���	���K�Avx�H�A�
���~�
�
�M�V�9�@
�Y���O�TOAvo��.��"	��y�h	�i
�,�l�MAv��F 0
Qv͵���?�I���`��g��f��o�A�TAvۚ9Q
v��n�=��Z���w�Z�M��f�{A9v�"��l�_��G���i�8�@�1��0
�/�G�t�=�q�(���E�M��5�r�:�� �)���_�^���w�C�f�X��x��/�2���)�lAwO��+9�uAv�	
	

!,9LQ	w��R��P��q��p�����$��-�>Aw��\�h�
	�c�Aw��u�r�s���[�-Aw��	
Qw�O�����y��x������A��@��)��!�+�hA#w�F	��y�f�"�,	�)�+�p�~��w�z�?t�x�d�c��8�
�9�l
�O�6��a��C��I�J���3Aw��"
�l�Y�Z	�O��qAw��. *	Qx��[��h��������������=��<�A!x��7�-�Z�=	�8�
���5�D�{�l�Q�	�X8��s��'
�l��-�p�d�
�;	�/�\�~��*
�	A5x��d�=�>�?�^��A
�8��?�,�-�4�9���,�+�(��+�M�v�+�7	

�<�j�)�2�3�&���G�]Ax���5H$:
5NQy��R�"���j��e�/����?�y��6��GAyƍL�O��P��8'�BAyնAyʁ�SQy��O��X��!�����?��>����������/�OAy��&�L�1�0
�;�6�.�y�<�m�Az�$
	�R
�
�+Ay���s
Q
z7�(��h��m��l���Zy�����,AzF�%�t�`�G�x�A	zL�$�w���*�I�~�B�#Azj��@
Q
z�,�^�a��@��y�x��~��c��b��cA	z��L�<�D�Y�_�p���iAz��
Az���\Q
z���0����}�x5�6���Az��G�9
�J��m�t�i�`�a�M	�J�Q�g� ��}�|Az��%�&�	�
�x�,�1�}��N�u�I
AzƁ�t$	
	
Q{$�t�7��.������	�_��l��A{:�V�^�;�*A{1�q��+A{2��5
Q{O�A�v�~�5��F��)��(��E��D��#��"��#���2��A{w�w�G�"���I�<�	�G�$�1�@A{`�~�D�K	�t�		�V�CA{a��N	
Q	{Ľo��<��	���
�W�V�Q�+A#{ӔO�o�u�\�:�/��3
�6%�t�0�U�D�m�f�v��t��h�4�E��S�m�h�M
�/�x�s�l��EA6{ѹ��%�s���
��N�K���^�8����Z�g�s�&�U�6�i�0�]�I�6�z	�3
	�4�s�N�A{ҁ�&
((>(:
"*Q
|�u�J�p�C�B�y�x�E�D��a�f�x��mQ	|��H��$��A��@��~��#�	�&A}	��O�����*Q
}�#�����'��&��/�9�s�PA} �M<�O�L�M>�rA'}!�s�z�h�
��+R��t�o�j�y�p�v��>�te�D�3�Y�P�=�v�+��-�l�x�Q�T�!A
};��w	"	 Q
}��E�b�K�Q��6��C�d��dA/}��_�0
�}�1�(�Y�^
�	�v�?��*�i�d��-3�
�y�n�u��^��X	�9�<	�a�n;H	�{�n�
�)�W��]A}��&A}���J
!2Q~1�;�(����L�K���
���5��4��9�A~A�NA!~E�R�M
�R��g�z�3��5��v�'��U�V�A�$��g��J�/�j��m�>�]�$A~S��& "Q	~��S�I�H�c�b�;�:�/�-Q~��@�X�(�,��v��'�.�[�3�.��R��9�s���O�iA~ʇxA~��g�H��
�;�v�JA~��[
Q~͚�@�5�
�I�t��`���n��k�S��2�.�/�O��Q~��$��R��}��I�P��L�L��b��1A~�E�b��-�w�V�B�/�]�F�u��{�#�A~��o	A~���\Q�O�!�Z�I�f��
�	�
�m��*��=�A(��E�Q�Q�n���r�$
�Q�b�h�m�8�v���E�H��(��N�G��|�J�IAL�b�nA7��`		Q
��b��>�?�>��o��n��5��4��A��\�&�r�E�g�p�+�6�u�+�\�A��)���?	�~�R��q�c�l��@A��D�X�Y��	�9�`
�
����r
�s�xA
���!

"
Q
��0��h��5��4��5�-�,�%��WA��N�}�~�;�<�G�v�e�}��n�6��l�T�VA�,�BA�!��l	Q	�R�\��N��#��"��;��:��_��^��{A�^�
�y�Q�o��o��N���+�O�X���/A�}�y��5Q
���U��^�?���L��G��F��/�mA���qQ���o��t������K��8�O��D��{�I�,�����9UQ���Z��4��t��u�g�.��*��{�'�� ��wA���yQ
���k����
��L�Q�c��q�p��p��aA�̈�J�Q	�֗�V�����C�:���A��1�v�2��Q���Y�\����/�2�n�K�@��������W��V�������A��+�L�_�r�cQ
��#�5�%�.�O�@��"��G��F�������`A�/�'�4�
�A��
	�NA�0��oQ�F�F��p��S��R��K�F�.��R��=��<��1��4��W�A�V�@A�`��VQ
�e���e��V��a��`��s��r��_���x��Y�lA�y�4�"�}�H��y	�v�B�
��#�
�?
�A�t��:	�#�<�<�y�P�_A����

Q
���A�X�� ��%�4�A�S�^�W�.A��a	n�>A�Ơc�v�e�2��5�*�S��9�R�w�n
�n�/�4�mA�ˁ�D
Q	���f��}�]��l�E���:A��}��$�_��DA��bA���j	Q�(�0����	�x��~�����y�/��:A�5�%�]��
�P�#��U��D�R�!��
�W�V�q�vA�8�2	�L�M�d�/�7�d�)�V�^A�:��{
&
Q���x��d��
���&��;�0	����	�A	���j�{�j����o��n��uQ�����4��J��%$!�{�?�H�Y�J�e��v��A�@A
���t�+��

��fA���"A����<Q�͉�F�k��t����)�4��b��5�:Q
�ےN�p����	��%����1"�k�E�A
��t���T�����$��y��x��Q
���\��{�W�C�t�A�B$/A��T�m�/A��
./<3A�
��_Q�+�(�u�r����%��$��-��,���K���
���T����7A�F�R�]��!��/A�@�%A�H��~

Q�]��
��W�%��j��/�q�^�7�8�s�nA�o�0�a�h	��!��b>K8

�Y�L�-�*A���+�Y��jA����&Q	����4��m�q�t�i�p��\��]�7A3���^-(��ZH�Q�(
�s�x�Y�j/�7�X�k�
�X�k�`���B�4	��6�}��<?0�C�B
�Y�Z�s�l�i�A	��M�L�!�2�z�}�t�iA�Ɓ�;	!(
Gf	Q�i�n��d��c�f���\������!�� ��}��|��;�L�d�o��t��{A���:�^�U�^�g��L
�O�O�A���MA����2
*Q
����R��.��i�Q��8��5��#A1�ğ9�
���"��T�g�
��.�!�%�>�}��m	�n�T
�w��/��<
���	
���)A�˾"�
�8
�d	�=�c�.�;�L%'2!	(�9�bH	�A�́�\
 
$Q	���
�*��b��o�D��(��?��>��IA%�����
�%�*�5�<�_�/��h�S�T�u�K�@�|�I�J�� �k�B�.��
��o�pA"����M
�^�s�j�n���c�"	��:��`	�-� �1�<	?
�}�2�e
�c�F�f��+�t�mA����W$ 02D*Q
�y��,�;���S��A���R�#�(�&���	�B�s��4�@�8�{	�d�:�;�q�<�A'���z					

	
�|��z�{A����Q
��z�0��p��%��$��_���J��S��R��_��^��]A�!�%�l�+�r�3W�-�0�q�s>�z	���y���R��)��'�&�I�A[�"�.

�l�g
�j�{�T�[0�*�3�L�r�O
�*�%�T�u�8��(�%���.�-�
�%���s� �X�{���(�)�x
�_�9���2�3��X�p�#�0�!�EA�$��S(6!.
"
$Q�h�V� ��x��W�!��v��?��>������A�w�S��0��AQ	�}�)�(�)��
���v��
��M�fA����8�b�@�=�j�G�l�y�+��d�x�E�z�%
�I�2�Q�1�kS
�@��|�fA���@�)	���2��,�&�U�#
�^�]�x�o
	
A����6"&2

Q	�0�9��T���2�w��H��G��F��QA�D�R���T�	A�;�]�5�r�'�"�s�l�w��F�Y�Z�Q2�l�I�"�b��1�(�[A�e��IQ
���}�������u��,���<����A�ґ4��(A�ֺW�;��m��)�A�́�
Q
��~�p��p����
��!�� ������'A�H�A��j�n�/�'�-��E�{�"�g�X�z��$�K�I�	�u��Y�X[�?�.�\�?A
���6
Q
�i�,������w�v��t���?��n��L��}A�y�S�a�i�)A
�|�p����6���A����{
Q���=����
�f��$��K
��O��p��UA���\�O_A���G�v�S�N�A����Q���i��l��G��F��o��n��]��\��Q��P��Q�+A��`�3�.A���/ �o�^A�݁�+Q����}�0��8����~��g��f��!�� ��_�l�]��L���L��L��A�
�W�T����iQ
������u�5��(��s�|�[��N��kA�f�^�RA�!��O�Z��
a�S�z
���W�P�I�H�[�I�&����j�[	��"�]��{�:A�'��S/H8TQ
�����6��'��&��)�=�#��d�%Q���)��v�}�����"�#�K��d��U�Q���q�V�K��&��V��U�u�,��
�xQ�ė_�H�o��B��K�m�v���#�(����}A�ډt� �m�n��A�՚kSA�֦&�q�t�#�,�x�+Q'�ꁖ1��k�\�P�4�[�Z�m��l�4�X���
�{�m�f��b�2�)�(�?�>�[��@�]��L���;�J�9��(�{A"��L���a�`�Q�R�}�J�Q��A�@��:�6��C�t�!	�@��6���;�`�h��J��\A�G�v�"�#�
�Q��~		�L��g�g��*�+�k�f�i�`A���3l|Q���"���8��3�� �����N���B��=�k�|�l��:��!�m�H�?9�+�*��6��+�q����QA���2�/�����AQ���3��J��u��t������O�t�i��;����%�VA	���3�I�<�j��@�z-A���5A����*Q	��f����-�P��Z��;��:���A��=Q�����Z�-����g�{�J�s�'��E�|�k�\�]���N��y�\Q�4�L��B�a�r��@�>�q�p�O�+�x�n��A�G�6�?�S�W�f�m�w�mQ�S�;�1�(�1��>��W�F�a�e�b
�E��>����*����x��O�0A �k��^�#��W�	�O/�\��R�?��	�p�q�O�|���5�|�S�$�X�?��v��J�O�BA"�m�u
�D�J��w��9/ 1,�F�EA
�l��FLf

Q	�)��L��
��������
��A�D�6�;�8�A�4�0�i���p�M	�V�WA�C��HQ�l�&�w��n��7��<������9��8��AA�z���T��G�U����Q���J��L��'��&��m�a�j��`��M��L��S�l
�oA���&�G�h�-�rA7����g�j�E�H�/	�k�V�c�>�=�N�V��"�k�n�M�P��~�%�8��p�
=�@�E�@�W�^�K�i�Ps�4�?�"�6�sY��bA
����z5H)<
/:Q�_�F��t���(��X��e��d��S�L�y�|��~��G�5�4�K��[A	�t�`����#�"���JQ
��� ���F�R�-��7�F��&Q������L���p���Z�:�o�n�W�o��d��}�A����&��:Q	������}��|��g�%�Q�@�LA���H��A�d�.�K���
�j�v��w�v�h�u�^���oA���=A�Á�
Q��I��>��O��J��I�'�l�����F��T��?A	���Y�W�N�z�1�u�C��;A��EA���{Q�	�J*�P��\��Y��X��!�
�6���v����K�)����{�K�9��|��A�M�.�@�<��"A�&�J�!� �]�=�/�.�W��b�q��@�D�k�T�A�2�;�.�+�1�NA�7��I
	Q�[�Z�"��X��7��6��!�� ���U��X��eA�h�W�6��*Q�r���@�����.��/�$����1��0�������I�<��+�hA���q�%����M�KQ���c��)�� ��	����5��4��������������A���l�%�a�cQ���{����'��&��!�� �	��"��%�3�4��"��!�m�h�A!���u�9�:�W�L�|�)�,��n��a�	�p�[�Z�A�L�f�
���!�"A����W�X�3�h�>��-A�Ɓ�


Q
�-������J�O�H��>��]��\��[A�9�	�-��Z�w�a���>�'�=�VA�:�
�SA�G��Y	Q�i����T��3��2���J�u�|��J��K�O�<��N�^A��SQ	�����T��i��h��U��T��7�XA���V�ZA���A
���	��

��z�Q�#Q����x��z��9��8��=��<������A�+��5�x�A�_��"��3A�܍%��
AD���x�_�Z�m�w�j��	�c�t	�_�g��:��V�#�	�j&	�7�2#�5�<�'�*��l�u�3�&��w�h�A�f���!�(�o�w�\�x	�9�D�g�@A�ց�

&*"0


Q���5��V��)�0��v��]"+����EA'���M�Q�G��7�u	�x�_�`�
�m�k�.�-^��5��b�
�Lf
�{�h	�y�O
�$�%�%�.��J���]�.��bA)���%
05
:C,;6
	

E0	�>�m$�%�.	 	PIA����+"

	
, Q���cy��B��7R��b��_��^������QA����S�|A>��� �	��7�2�[�nD�y�(�D��e�

	�+ ��x	�9�H1��>�i�`�F�;�f�1�*'�'�P�Q�P�U�V�M��O�,�%�4�8�}
�A����O("*

Q	����a��)�m�~��������EA�����|�S��dQ
����a�Q�z�9�&�wA���+�r�s�r�q�0Q
���K�D�}�G�5�t���c��bA�ܜ^�m�2�E�a��Y�d�
�|�x�#�]�R�u��b�6�U��}�L��#�|A�ص>

A�ρ�e
 Q
� ��l��A�6�C��.���L��s�lA�-�f��B�~�Z�g�z��s�y�;
�B�b	�'d�0�
A�,�lBM�>���Y�XA
�3��n		
Q���E��~��[�V����	�}���������vA���i�MKg��(�x�o�v�1�.	�'�D�3�4�y�&�P�I�&A���y!HA����
Q
��J�t����h��{�4�G�,�r���cQ���K�n��k��l�!� ��^��q�R�4A���'�{�F!�I�B�����u�~�<�.�YA��A���=Q�;�E�(��r��M�,����s��r��;��L3�I�h��r����A�P�K�"�E�<Q
�[�F��2����������W�
��EA�g�_�'��=����Q	�p������8�9�_�h�B�A�}�Q���L�	��X��%��$��;���8��'��/�RQ	���o�D��P��-�W�P��0���A���b�h��^��Q���f�p��Z���p�� ��A��@��u�9��,��WA6���4�<�p�M���y�;�*��b�x�{�M�U�o�`�l�!�Z�'�k�n�A�z���}�4�D�m
�7�:�N�&�,�1	�>��g�6�y
���o8�V��
���,�A4�ɶ�>�0%�g�
�1���c�X�]�@�=���B�C�>�9
�%�T
��w	�2�/�
��hg�%�!�{�HA����$$$"

Q���I��2��5��>��t��5�	�|�S��������/��.A��A�o�7�X�D��2�#���j��O�0�%�(�(���=�qA
��IA�%��t (Q�o�>�*�!��F��3�9�s�-��p��g��`��i���e�"�@�w�'?�Z���7��@Q����*��	�9�m��,��g�\����-�	�D��nA���~�#�[�{�|&�}	��O�F0�mT�A7����.A�U�/�.�/�r�L�D�I��j�a�X��/�+�|��5��A�9��v��g�&�X��P�+��d�S�b�g������v�w��!A����.

1DQ�m�V�V��^�/�Z��P���S�F�
A�z������~����i�b�WQ���(�C�#�d�;��>���[��^������eA�����~��S�A
���+��(���}�F�%��B�{�n�A����A 4Q���	��4��]��\��9��8��=��<��i�8A��A ��?�N
�!�n��
�7�p@�I�(�s��_�V��?�0�c�(�w��U��A���}+F
Q
�j���t��g�(�I�(�5�r�����R�&�QA�y��r�y�w�p�_�ZQ�����f��u�d�k�_�H����
��4A���2�$�o�r�y�x�S�(�Y�:�R�enA���)�-A����OQ
���a�������,�\����!A�<�FA�߻l��
	�{�.�1�
��R�S�`�a�.�
�j	�[�D�=�$A	��q
Q	�A�
��B�w�r��,��+A�O�I�A3�M�e�~��z�W�_�(	
�]�V�_�x
�W�R	��$��38A
�P��;
(F(,Q�1�26��(��#��"��i��h��o��n��+A	�|�H�]�
�D�9�	
�z�}�AR�>�T���W�,Aja
�Q
$3B�(�F�I�/�
�j�)�Y��	��4
�U�^�{��i�`�C�B�E�&	
�%��6�	�g�j�i�8.
56'&��EA�S��~,		;4 
Cj0'>"Q��!����Q�u�@�9�8�q��t���T��*Q�-�e��*��M��L���%��$��u�tA�=�f�p�p�[�H�7�O�"|�k�Z�m��0�75�U�n�X�5�2.��T� 
�w��A�p�
X�ZF�	�Z��YAU�>�C	��	�P*�G�_�N�=�J���Z�
�<�Q�@�E�X�a�,�s�N�S�6>C�)��E�D	�G�t�I	
�^�H�N�#���E��;�G�H�U��v�#��3�>A%�@��@",($
Ht  C�@܇�� B��<T�� ��9G��7��n2A疄W0�>�0�m�$�g��90��
���R�aYy���f��w��v��-A����SA�l��Q�?��D��a, ^��
��%�6F�Z�a��Hmo�#�,�z$�:��;���p	�f
�R�>dK�b�4`s}q{)R�d
U�7$�<�v	�p��c�=�P�}�@�P�X�D�T�F�R�J�H�L�<�X�
��(a���Oadx�a��a�$�a���@aR?�aR����,��vG�E��2D�6C�Z�
�g�����#�'�*�-'�<�>�E�J�L�N�Pa�>TR�E�
�+(� �(G�7Q�IE�\
�o�e�m��
�,�xZ���1h�D�7�#�H�Lq�\�N�v�m�j�5�
�j�(�U�v,�x�=�f�(��?�h�V�Z�]|�^M�(�
�1��V�>�r�q�	�6�G7�rA���C&�H�	�g�R�\G�eM�sv�&�+�-�8��|���	�f�!�=��U�R�V�Z�W�\�`�f�j�l�p�s�v�x�{��	�
�`i�Gt�Q�?�z"�
J�!���&�+�4�<��
�/�K�;�>�`�,�2�4��%�!�*H�@�B��c�f�j�n
�s�%�O�T�{�z-�#�	��U���	��
�#�'�,
�0�7�:�@�D�F�K�M�R�W�Y�[�^�e
�i�m�p�t�x�}����
����	�
����!�z�S�JW�N��d�h�j�l�n*�7�;��#�,�.�a�;�@�F�J�M�O'�]�8�������%�'�/�3�7�>�A�D�J�7�A�D�G�K
�Q	�V�}4�y��V@�F�oa���ya���\�)a}��:�a
vF�/%�4�/��2�s����O�S�hH�A�f�F��La���;a�;��3�$	�)�<�L�D�h#�G
�k�z&�}$��P��C�8�L!��q�$�)^�&���2��/��V��X���aq��f�j�Y�_�e�l�u�w�{�}	�������"�&�+	�.	�3�6�;�A�H�J�O
�S�U�^�c�e�n�r�t�v�y�~��
��
���#�%�)�,
�4�7�;�@�D�I�O�S�V

�Z�g�k�n�q�v�{��	�������(�2�4�9�;�=�@�B�E�K�N�Q�T�Z�]�b�f�n�t�v�}������� �%�'�-�/�1�3�5�9�=�B�G	�J�M�O�W�a�c�e�q�t�v�x�|�~�	��	�
�������"�%�(�1�5�8	�@�D	�G�S�X�]�b
�e�p�s�u�x�}���	�����
� 
�$�+
�0�4�7�:�A�C
�I�L�Q�U�Y�]�`�d�k�s	�x�}	���
����!�&�-�0�9�<�A�G�J�L�O�X�\�a�c�i�n�p
�u�x����	�����
� �"�&�(�+�.�5�9�@�F�H�K�S�U�X�^�c�e�j�m�p�s�x	�|���
�
����"�'�+�.�7�:�?�C�G�J�L�O�S�U�W�[�b�d�f�i�k�p�t�w�z�|�	���������#�&�(�+�0�4�9�@�H�L�O�X�_�a�d�f�i�k�t�x�z�|�����
�����
��+�3�5�:�>�C�K�N�U�X�_�f�n�r�v�~�������� �"�$�&�)�+�1�5�8�:�A�C�G�J�M�P�S�Y�[�`�f�i
�n�p�t�w�z�~�����
�����%�'�*�7�<�>�C�G�J�L�O�R�[�`�c�g	�i�s�w�~�����
��#
�*�5�7�?�A�E�G�M�O�V�Y�]�_�b�g�j�o�s�{�����
�$�/�5�7�9�>�A�G�I�O�R�V�Y�\�_�c�g�u�z�}�
��
��������"�%�+�0�7�<�A�D�F	�I�M�S�Y�_�a�g�m�s�z�~�����
������!�(�*�/�7�?�A�D�K
�N�Z�\�`�m�o����
��#�)�.�5�7�:�<�>�G�J�L
�O�V�Z�]�_�a�d�j�r�t�w�~��	����� 	�$�.�4�6�:�=�C�J�L�O�S
�Z�\�_�d	�j�n�q�w�~���	��
���� �&�(�0�2�7�9�<�?�B�D�F�H�K�M�P�R�U�X�Z�]�`�b�e�h�m�r�u�}�����
��
�����#�(�*�-�3�5�=�@�E�H
�M�S�U�Y�^�a�f�j�l�o�r�z�|���������	�"�,�3�7�<�>�D�H�K�N�T�W�[�`�i�l�o�x�}�	�������"�$�+�.�0�2�4�7�:�>�F�J�L�N�R�U�Z�a�c�f�k�p�t�x�z�~����
������&�+�-�0�2�7�<�@�L�N
�T�X�Z�^�a�d�i�n�p�v�{�}���������"�%�,�/�3�;�?�B�E�M�V�X�\�_�k�q�t	�{����	�
�����$�*�,�1�3�5�8
�>�A�C�G	�M�V�Y�\
�d�g
�m�t�v�z����
�������$�'�*�.�3�9�;�A�C�J�L�N�P�T�X�]�a�c�f�j�l�n�r�v�{���������'�*�,�1�3�5�9�;�>�@�E�G�K�P�Y�[�_�a�d�g�k�m�s�v�x�}���	�
���	�(�2�8�:�=�?�B�K�S�U�Z�]�d�k�n	�v��
��� �#�(�*�-�2	�5�@�H�L�O�S�U�X�_�b�f�j�m�t����
�����!�)�+�-�0�2�5�8�:�>�@
�F�I�O�Q	�S�_�a�c�e�h�l�u�x�~�������!�)�,�2�7�<�?�A�C�H�J�L�P�X�\
�^�i�p�r�u�x�|����	�
������%�4�;�D�J�N�P�T�X�]�a�c�f�n	�p�z�����
�
�	���$�)
�0�4�8�:�<�>�C�E�H�J�N�P�V
�Z�_�g�j�n�q�u�z���
������ �"�%�-�/�2	�6�8�:�<�A�H�J�O�S�W�Z�\�_�c�f�m�o�q�u�x�}����
�
����!�#�%�(�,�.�0�5�8�;�>�F�H�J�L�N�T�W�Z�^�c�g�j�n�s�v�{�����������%�+�-
�/�:
�<�J�N�P�U�[�_�a�d�f	�m�p�u
�|�~����
�������#�)�/�2�8�<�@�C�E�H�K�P�X�\�^�b�d�l�p�s�u�x�|����	��������%�'�2�7�:�>�A	�F�H�K�S�U�X�\�`�g�j�m�o�s�w�y�}���
�
����	�'�+�-�2�6�?�B�I�M�P�S�U�^�c�g�m�q�y
�}�����"�%�(�-�/�5�:�>�A�C�H�O�R�X�[�_�b�f�k�o�r�t�v�z������	�	�%�1�4�7�>�A�E
�M�Q�U�W�Y�\�^�`�g�i�l�t�v�z���	�����"	�$�&�*�0�3�8�;�=�?�C�E�I�L�N�U�X�\�d�n�t�w�{�}����
������ �%�(�/�4�:�=
�A�M�Q�S�W�`�f�m�o�t�w��������%�*
�-�9�=�?�E�N�P�\�`�c
�m�t�y������� �#�'�)�,�0�4�8�:�=�F�K�N�P�S�Z	�^�d�f�h	�l�n
�u�w
�|����������� �"	�&�)�+�3�7�<�A�C�H�J�L�O�Q�S�\�e�h�l�p�r�v�z�|
��	���
��"�'�+�/�6�:�=�D�L�N�R�T�`�c�f�l�n�s�y	�~���	���� �)�,�/�3�9�;�C�H�J�L�O
�S�W�^�`�e�k�o�v	�z������
������������!��$��'��+��.��1��9��<��?��A��D��F��H��J��M��P��X��\��a��e��k��n��r��t��{����	��	����������#��%��)	��+
��5��@��B��G��O��Q��U��X��Z��\��b��j��q��t��w��y��~��������	��
����
�� ��%��*��,��1��:��A��E��I
��P��T��W��Y��]
��c��g��i��l��p	��v��������	����������	�� ��$��&��*��-��3��6��=��E��L��O��S��Y��[��`��c��g��m��o��s��w��z��}��������	���������� ��"��$��'��*��-��3��9��=��C��L��N��P��\��a
��i��w��y��|����
�������� ��#��&��*��-��/��4��6��8	��>��F��H��N
��Y��_��a
��e��h��l��o��r��x��}��������������!��)��,��1��4��=��@��B
��G��K��O��Q��S��Y��[��`��c��e��j��m��q��y��}����
��
������	����'��+��.��1��8��:��?��A��D��L��Q��S��W~��b��j��r��v��{����������
��������"��&��,��1��5��8��>��B��D��J��M��O��U��[��_��c
��g��i��m
��q��u��y��{��}��	��	��������
��"��(��.��4��6
��:��?	��G��I��Q��S��U��W��Y��^��`��m��p��t��x������
����������#��*��,��.��1��9
��@��B��F��O��S��U��Z��_��h��l	��t��|��~������
���������� ��#��%��(��.��2��6��:��>��@��B��G
��K��N��R��T��X
��]��a��d	��f��p��w��z��|����������������!��$��'��,��0��6��8��<��?��A��G��K��N��Q��U��Z��]��b��e��k��n��t��w��{��~��������
��������'��.��4��8��;��=��A��N��Q
��V��X��]��`��b��i��l
��r��~��
��������������'��+��.��1��6��:��>��A��C��H��K��M	��U��X��Z��`��b��h��k��r��x���������������� ��#��'��*��/��3��6��9��<��A	��D��P��R��_��a��g��i��m��q��s
��z������
������������!��&��(��*��.��0��4��8��<��?��D��F��H
��Q��T��W��[��]��_��a��d��f��m��p��w��}��������������������$��&��+��.��2��7��9��?��A��C��J��N��Q��W��Y��\��_��b��f��i��p��t��x������������������"��(��-	��2��4��7��;��>��C��I��P��S��X��\��`��b��d��g��j��l��o��q��s��{��~��������	���������� 	��'��)��.��0
��2��@��F��J��N��P��R��Z��_��a��e��h��k��n��v��z��~����������
����������!��&��-
��4��8��<��@��D��F��I��L��O��R��T��Y��a��c��e��g��i��k
��p��s��u��w��|��~����������������������%��(��,��1��5��8��@��B��F
��L��O��T��V��[��`	��d��n��r��z������	������������!��%���4��=��A��C��F��J��N��T��[��b��f��m��o��s��z��}��������������%��+��/	��4��96��>��C��G��I��K��T��V��Z��_��a��d��i��l��p��x��}��������������������$��(��*��-��5��7	��?��A��D��F��I��K��O��Q��T��]��_��a��g��k��n��u��|��~����������������!��'��*��.��4��:��A��G��O��T��Y��[��_��b��f��h��j��m��o��r	��wA��	��
������������$��'��-��/��1��4��6��=��?��E��G��J��M��P��R��W��]��_��b��j��m��v��y��{��������	��������������"��$��)��-��/��9��;��?��B��E
��M��O��W
��[��_��a��c��e��k��n	��v��x��{��~����	��
��������#
��&��3��8��:��=��A��C��K��P��W��Y��[��]��a��c��f��i��m��o��q��t��w��|������	������������$��&��-��4��7��9	��;��F��K��M��R��U��Y��]��e��g��i��m��q��s��u��w��z��������
����������!��%��(��+��/��1��3��7��>��G��M��P��R��X��[��`��c��g	��l��o��}������
������������$��(��+��.��2��8
��>��I��L��R��U��Z��\N��f���w��y������	������������"��%��)��,��/��5��7��9;��C��J��M��O��W��Y��\��^
��c��f	��j��n��x		��}����������������"��&��*��,��0��3��6��9��<��>��D��L��N��P��S��X��[��_��b��k��m��o��r��t��v������
������ ��"��$��*��-��5��:��<��?��E��J��M��U��[��h��k��n��r��u��{��}���������������� ��#��&:��/��6��8��=��F��K��N��P��S	��Y��\��d��h��l��n��r��t��v��x��~������
���������� ��&��*
��/��:��<��?��B
��N��]	��c��m��p��~����������#��(��.��2��5��;��=��@��C#��K��M��Q��W��]��`��b��g
��k��r��v��{����	������������!��&��+��3	��8��<��>��B��V��Z��\	��f��q��x
��z��������'��,��0��3��7��:��C��K��N��Q��]��`��b��e��l��n��t��v������	������ ��"��'$��0��5��7��9��;��=��?��B��D��K��T
��[��f��m��p��s	��y����
�������� ��)��+��-��B��K	��P��Z��]��b��f��l
��q��	����
�������� ��,��4!��>��C
��I��N��R��V��X��\��^��d��g��m��r��t��y��}���������������������� 	��$��0��5��<��E��G��Q��V��[��_��c��e��h��j��n��s��w��~����������
��5��/�K!O���(����
�������	�+�5
�9�Ga.�������"	��$��.��0��<��>��A��E��G
��J(��X�?��{��~�o��s���HF��*��H���sa5���a5�`��
��~
���d��a7���|a8���|a9��|T��_��i
��I��Wr��wa:t���ja;M��v�0��Da<m��ua<op��ca<���T���sa>���sa?���sa@U��s�(��IaA^��r���QaB���qaC6��qs��(aC���*�� !��K�U��maD�(��C���laFK��l��8���LaG"��j��
Q����dq��taH���fa	IF��f1��-��_��a��f��z��}G�����[aK���[aLv��[aLx&��R[��y��U���haM���|��J���a����a����a����a����a��6��PK
!<1#�X��2chrome/pdfjs/content/web/cmaps/UniGB-UTF16-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�
UniGB-UTF16-HA �V�Z1�6��m�1
�:�3�2�
	��a0�H�R�F�P��p�D�@PK
!<1T縪���2chrome/pdfjs/content/web/cmaps/UniGB-UTF32-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�#C^��J�vG98�<
&
�Q�Z1&#bW	e�-�7�vO@�mj�T�	z
!�<�%�e{tg

9
7	IA"D�
J59C��!�/�&�6�X�Y�O�H�}�r�:	.B�)}�IxC���R��S��{

S�V/�B�@��r��-�
�;�]��a��l�I�Z��.��-:�*�+��&�
��C�)�V�C�9�
� �5�;�p��z�	���g�`�}�<�1�^� �u��/�6�(��a�6�i�#�U��X��K�p�D�m�;��M�$�{��I�$������|�j���=�z�!�j�f�%�:��H�~��]�N��Z�h�M��M��<��;�@�R�c�:��j����M��)���k�b�r�7��&�%�H�C�9��c��>�|�}��q��z��e�K�q�Z��$��
�}�H�9�S�8t�T�m�l�5�(�_�U��`��i�(��F��}�3�f���v��U��4��
���D�9����UF�B�8��
�"�F�k�w�5�d�;�V�Q� �C��j��{�q����g�n�X�5�*��`��C0g��~�#
�Z��*���<��Q��~�4q!�}
"	�*�5�E��n�5C.0?��UT"`2�L�)+�*o�}U`��Z�q�u��Wt
+�x�u�AR�92
�?*�
CI���eSN�#��%^�L�P�-�������1�m��
��c��.�K�$��TSN"�i��p�9�b�T�����g�j�h�SN0�~��P��m��l�
��o�n�#�)�f�x�h��3�8SNC�.��{�z��]�d��	�	�<�9�S�z�`�5��`�\��
����	�PCN\�u��8�N�9l�)�j�;A�
�7CN��YCNr�SN���~���t�y�9�2�1�w�"���x��/CN��^�t�S�ySN��q�}��9�A�(�!�a��M�*�����x�CN��T�`�6�3�E�pSN��a�1�I�xI�k�H�/��&��?�^�w�3S	Nє�Z��!����x�y�S	Nݥn�������k�r�FC	N��qG�)7�$�3�/�CN�#�+�,�3�$�p���/�6�M�!�^�`�_�xCN��5S
O�Z�H�#��D�Q�P��N�]�\CO*�@�\SO/�u�F��|�C�B��K��J�W�V�m�l�E�D�E�xCOC�I{SOM�J�9��$�3�d�g�f��O��N�or�W�Z�M��>	�_COp�fCOc������W�:�+�j�_COf�pSO��=�������p��7�6�C�B���CO��D��Y�[�"���
�5S
O��i�P�{�n�M�:���"�!� �7�)CO��S	�����8�ECOťHRMCO��S
OכL�e��,�Q�P�O��r��N�K�D�SCO�r���Z�)CO�E�=�NCO��6�o�mSP	�b�P�W��s��X�s�r��K��f�YCP��e�Z�t�w�=�6��S
P%�Y��|���2�C+P:�O�H�)�v�!�"�+�$���E�Z	�G�P�t
�#��7�l�I�j�P�}�|��-�0�o�^��1�6���7�-�CPI�*�_�6 ����^�1�h�?�+_�V�7�<�s;�$�S�L�%CP;�e
;X
�Q�`"#,SQ��2�7�6�1�0��n�)�8�CQ�@�sC
Q�V
	�1
�t�=�|	CQ�r�e�tSQ?�8�@���O��1�\�8�A�u��4�}�|�aCQQ��D��a�>�<�dCQR�eCQS�SQh��<���'�Z�z�f�f��9��>��u�H�k�B�k��>����6CQ��0��]��a�`�&��|�o�C	Q��z�;
��n�+�*�S�RCQ��;S	Q���M�Z�
�4��F�
�h�SCQ��r�>�OSQė�$�s�r�~�#�"���:�:�
��[��Z��}CQ۔&�r��k�9xSQ��T����e�5�p�&�����+�o���G�&�`��i����!x�n�CCR�s�2�~�#��{�o�)�*H�l�K�3;�8�MCR�nCR	�sS	R(��i�:��W��V�m�Q��<��QCR3�S	R6�E�]��@�9���b��}��|CRA�&���L�\�M�	��x��X���F�'�wx
�\�y�z�9�:C
RD�H�K�&�<�c
�j�~�L�y��aIC
RK�	�"




S	R��o��i�)���r��cCR��n����p�%�L
�.�g�Y�j��V�z�%CR��>CR��QSRսM�u���C�T�l�u�t�u�I�ICR�W�x�1�Y��b��	���0�n�m�3�E�U�B��a�!�n�V�:�=�<
��`�CR��2�`�%�F4��O�NCR��d#.		
S
S>���N�U�.�s�r���S�~��wS
SN�?�p�T���NK�f��(�,SSZ�p��j��Q��P��3�V�;�Q�:�n�g��=�O�
�/�.�7S
Sn�j�o�$�)�t��
�N�/�j��%�H�Z�9CS�a�+�H�:�U�~N�d�1�T�QCS��ACS��6S
S��~��+�f�B�q�`���!�.��W�0CS���4�9��Y�	�J��j�$�-�%�x��l�M�Z�SCS��y�S�b�QCS��O
SSߧ�m����w��h�D��b�U��3�0�O��f�H�~�[�P�b�I���l�:�0�W�]�TST�m�'�&��k�p��x�[�Z��U�,���4�)�;�-�h�&�V�@��~���2�US	T�,�����(��)�<�Z�WST&��u��6����/�X�_��'�N�)�J�\��3S	T8��'���S�_��0�!� �7CTF�w��b��&�n�{�^�Y�>
�-��@��s�*�w�$C
TC�



 
CTG�
ST��G���J��_�L�O��^���;�q�8C	T��E��R�/��-�qS
T��g�s�V�6�����3�2��G�CT��9ST��%��u��1�i�F��F��G��F��E�Z�k�C'T̪'��S�|-�U�Z�M�V�/�o�(�t�?�E�j�y�r�.�p�o�,�:�G�	�I�S���A�@	�C�PCT��G"�HC
T��C"S
U*�<�j��u��t���*�}��H�QCU7�GSU<�F�j�e�r�p�w�v��C��:�uCUI�@�)�0
�S�4�\�%�>�}�t��Q	�,CUO��'
�LCUT�

S
U{�Z�G�L����Y�B���%CU���t�a{�N#4�]�\��A�,�#CU��[�iCU��%
SU��R�2�]�x�y�x�)�(�U�T�E�DCUĪj�}�j)�g�CU�X�N�#CU��ESU��!�X�_��J�i�h�m�l�3�t	�R�C��3CU�k#�1�b	�k�j�u�z�
�?�<%,CU��r��"���
��WCU��W




SV,�	�r�S�0�g�A��>�Y�N��
��S�l�[CV?�CVL�	��&
CV@�{��S
V`�p�@����V�>�{��i�:�YSVo��,��P�D�E�D��9��8�}�,�NCV��v��V��
�FCV��
$CV��n�o�z�`�Q�I����J�E���	
�e���<��o�mSVژq��>��O�|�g�6�?�F��CV��}� ZCV��I�{�>�W�VCV��aSW�s�L�&�s���4�B�b�AC!W��W�m�@��Q��`�K�P�E�P�r��!��?��k��k*�E�b�N�!�6��@�kCW�F�CW�v	SWd����'�&�S
�1�2�Z�S�R��UCWw�M
�#�4�b
��OCW��CWs�:SW��CC�#�8����M�B��I�H����~��C*W��>	���R�Y�$�/�$�o�-��W�'��{�
�v�{�=��X��>����(C	W��*�?
�
�>�S�tCW��K.

SXJ�c�*��f���
�A�6��C,XX��}��/�.mr�q�^��1�O�:�B�G��G�u��C�B�c�J�t�e�V�=�=�K�&�0�g�����Q��Z�E�@�YCXW�
N��e�0�)�^�'2�>�y��
�M�<�/�P�X�y�
�K�\CXc�=(.


SY"��t�C�F�������_��^�/�)�E����CY4�h�D�5�]�G��I��{�)�M%��f�S�_��a�N��M��
�B{�'�*�qCY>�|*�<�{��QCY;�E
	
S
Y��u�g�f��z�=�+�*��:��CjY��w�]�h�N�(��	�o�U�<�{��s�h��U�B�S�?�1�F�A�E��;�e���,�s�`��T�sA��{�n�O�%��P�E�r�H�~�%�2	�_�z�d
�1�2	�e	�L��Y�^�;��$
��
��� �u�.�B
�_�^�W�B�G�FCY��-�#�]$�c�T9�'��-�0�?��0CY��v!&$
"~�F*0
(S[P�0��t�u�)�\�>���t��w��}���AC
[b�9��-�2�K�o�8�T�?�N��I�\C[k�Z�C[r�<S[}�X��T�Y�S���D��'�z�E�n�e�I�e�����.C[��!S[��1�4�9�8�-��d�}�r�`��x�����<�%�V�;��7C[��I�	�(�{�d�O�I�d�}�7�f�7��A�q�g� �E�~C[��3C[��X

S[ݘ[��?��x��
�t��u�M�:��'� � �=�_��w�v�uC[��j�P�F�B�y�$�cC[��
�'C[��yS\���B���B��3�~�4�.�MC��(��_S
\�:�n�;��\��C��B��y��x�'�YC	\"�6�(�=��(�G��y�xS\7�\�3�~��B�E�i�`�Y�!��;C\E��C\H�y��Z�7�c�L�$C\P�hS\^���:��q��F�K�v�y��z�mCb\n�t�m��R��7�e��>��t�5�<�3�2��|���q�d�{�l�e�l�y�1�(���d�'�
�T
�:��v�O��@�;�^�\�&��G�2	


�_
�H�U�`��	�J�$��s�
�<�c�R�e�T�7�K�C\l�>4�QR�d�%�x�4	�k�`#0�A�,
�;��'�V�x�]C\m�.&: 3J
$" 4
S]��w�s�����V�I����S�)��z��C^���g�jS
^� �=���~��)�H�{�Y��C^&�{�H�	�E�N�;�ZV��!�����mC^%�C5��!	�:�|�m�%�$C^,�Y	S^r�D�.���j��5��4��O�j��w�F'��o�hC^���v��?�[�p�zS^��=�Q�8�6��?�6�'�v�S����;�u��.��O��N��{�Q�C"^��5�]���F�K�#�6�0
��	��O��L	�Q�9�S�5�f�%�I�J�U�5�]��]�jC^��`3�L��j�qt�&�dC^��
4B	S
_�T���;�:��#��"��)��(��s�C	_��z�P��F���X�iS	_-�p�j��#��"��/��.�g�f�iC_9��V��`�w��^��
S_L��l�e�d�3�2��u�a��T��5��,C_i�{��UC_]�T�2�+�u�\�A�j���C_c�S
_��*�Z��b��_�K��(�e�!� �;�P�hC+_��7��n��F�<���'�&�s�r����H�d��R�.��T�:��)���!��.�e�n�=�B�+�*�E�V�|C_��E�I�b��C_��S
_����
��O�:�!��l�
C`�8��X�1�y�`�p�_�B�t�8��@�P�#�U�3�z��v�W�&C`
�Y			
�t�g�l�c�bC`�\%(S
`g��w�?�T�a�$�	����}�cC
`s�_�_��Z�K��G�
�"�R�5�h�SC
`z�_
	C`w�S`����:���k��j��+��0���m�f��{C`��l��
��/��1�dS
`جo�4�3�2��o�2�:��)�
�`S`�L��w�0��<��_��'�h�T�#�Z�J�G�<�4��cC`��"�d��-�A
�2�q�� �Z�a��*%�}�y�i��$��W�B�5��*�k�:�C9`��9��8�;	�d�(�
�t�q�L�	�
�>��^�[�I�y�R��\��g�6�@���^�+�B�y�0�`��f�[��)�:���6�7�5	��]�Q	�oCa�] #6
&%.Sa���v�[��0���[�Z�=�}�Sb���y��8���z��E�?�d�m�d�Y���A��/�d�9��J���|S	b�|������MSb*�)��F����S��R�9�8�A�YjCb7�<�A�"�G�j�V��]�`�'�1�D�p�$�yDCb=�tCbB�Sbf�!�"��r��3�L�5�l�g�g�P�C	bs�m�J�N��{�r�;��	Cb~�sCb}�6Sb��]�G�����;�e� �j����G�jCb��f�*��	�Q�2 �z�U�L�&�.�+�PCb��<Cb��ISb����S�_��>h�i�J�<��U�"�9��h��a��`��)�z�a����A�~��6�F��C	bߖE�w�O�����m�z�k�Sb���L�I����?��>��C�*���O�sCb��S�`�{�C�W�^��q�f�&�Cc�NCc�oSc�>�����I�Q���G�\�Y�Y�C$c(�/��	�D�x��[�
�.� �I�#��T�i�2�@�m��5�r�Z��)�q�:�2��x��f�6�u�D�eCc>�Q)�k��H�k�gCc)� 	Sc���&�9�r�R��%��$�_�W���:��kCc��;����=Sc��z����o�g��T��I��H��I�"��+��6�y�r�wCc��W�hS
c��?�o����+��*��~�m�l�
CGcĩW�A��4�Y���0�$�c�u����^�E�,�U�V�9�R�R�y��a�h�
�b��5�Q���S���y��<�g���q���B�.��b�2��P�:�)� ��d	�O�L�:�p�;�gCc���O�	�K�H�
�C��_�|�i�.
� ��g�1Cc��W

	
		


Sd����*�5��9�~��l��7��6�I�B���-��,Sd��g�u�d�m��z���A�|�I��>�E���Z�Sdʾg�k�t�������~��-��,�Cdשp�B�@�OS	dޛH��\��
�
�����eCe�Cd��
�"��d�E�n�q�H�&�Y�P��O�Cd��	Se���l�A�@�g�f��y��^�e�d�{Ce%�t�+�*��u�
�g|�9�x�y����Ce"�Y���u�$�g�8�HCe-�#	S
eU�g�?�J���=��<������}�Ceb�M��	� �S	ep���j������_�>���y�bCe���7�a�p�aS
e��A�/��h�9�8����W��V������5Ce����>��	S
e��@�s��B������'��&��
�C��:��}�<CPe��Z�J�W�W�:�r�o�B�e�h�M�Z��#�'7�!�6�
�P�M���n��@�7
��
�7���;�)��K�=�N�v+�p�J�g	�<����+�R�K��^�{�,�k	�M�+�a�8�$�f������)�~�.��"�
�!��3� �o�l�1�HC&e��D)�s



	��'
/�< Ce��W�
�*�Z*��"�_�9�x��<�A�1�4	��TR
�ACe��s	

	
 "

A^	
Sg&�,�.�G��h��'�o�K�0��,������[Cg4�r��V�6��V�'�{�\�9��'�8��K��q�`�S�nCgH�T

CgA�c
Sgm�s����I�,�<�{�m�B�?�>�7Cg~�>��Z��*�8Cg|�b
Cg}�S
g������e�t�n��/��.��� �pSg��w�b��Z���{�v��G�m��2�w�v��c�zC	g��O�?
��d��r�.DCg��m
	Cg��
Sgذe���W�F�9��F��1��0�Cg�0�^�\�P�o�
�>Cg�g	"
Cg��ASh�E����y���o�r�L�;�!��Z������kC	h�{�7�<�#�2�?��A�Sh<�l�T�Z�T�Y�s�6�,��m��b��]��V��+��*�s�r��5�}��0��Q�<�&S
hb�{�K�L�w�|����h�eCHhv�a
�=�p��
��q�6�=�,����3��@�i�#�j�-� �L
�c�3�{�A
�H�6�@��|�V�
 �h�!�T�+�$��
�!�k���,�
�F!��P�,�9�]�^��#�^��T�$'�s�H�G�r�~�'
�N�M��r��%CKht�!	
 		
 
%+		!

(
	
x�:	C!h��u9�R�#�Fb�i���78� �3�^%�m
��Kp�e�Z���x�G�k�81�!
�*�*��57�N�W�|2#mCh��j68XA'
!4
@
%+"'*!"			Cj��C,hu�	$,F .	


#..H*)6.H
1N3R S	k_�K���75�!��\�[Ckj���
�A�w��^��g�GSk���
�&��c�6�*��%�G�E��.�'C
k��2�q�[�X�W�<�v�q�/�/Ck��\�V�S�N�O�R��
�"�i�9�^Ck��6	
S
kҋ}�b�}���0��?��>��SCk�R��9�(�Ck��@�'Ck��m	Sl�A�K�@��/��k����)�X�
�a��"Cl�rSl���V�E��T�O�@��S��E�J��4���u�F�j��@�C�B��I�vCl8�%��;�2�y��y��L�1�!�X�1�{�j��X�/�"�K�5�|��GClJ�"	ClH�
S	l��\�h�Y����!�����Cl��'�K�D�r�s�u��&��3Sl����j��	����E�K�#�$�E��N��3�e�(��l��/�OCl��
�;��4�3�5�t�)�{�#"��F�5"�t�_�V�1�v�gC	l��;

Cl��NS	l�:��l��h����
��{�fS	l��j��e�g�G�&���X��wC
m����+�3��G�I�@�5��_�+Cm�E



Cm
�t


SmA�@��$��#��"��I�g �h��"��c�w��X���'�,����/��K�JCmY��)�N�M�q�p�5�j�]��h��U�)����M�$�+Cmy�\
Cm[�&0Sm��!��B��	�&�5�4�k��H��]�E�� ��o�-�^���Cm��j�{�&�;L�*�'�6�f�i�d�q�n�?�	�X�|
�)�^�J�I�5�DCm�^Cm��bSm�'����_��^�[�:�C��`��y�:�)�@C2n�b��z�o�Z�U�X�1�6�m�T�%��g�l��t�I�2�A�k�:�;�6��.�X��n�q�:���-�+�g�/�r�]������3��OCn&��s�x�r�e&��Cn�&,DS	n��R��.��e��v��Q��P��k�Cn��J�S�@Cn���8�HCn��oS
nˤ)�&�t�o�{��h��q��p����
��$��sCnڎOSnޣL�T�3�4����������I�LCn�/�:�b�$��s�\�

�w�rC
n�K��]�9�.�S���a�4Cn��	 ,So)��[�$�{��b��?��>��3�@�#C'o6��Y�X�-
�]�B��L�k�j�g��O)�j�S
���d�� ��	��!�]C'o8��T���D
�%
�6�k�z�+�Q�:�O���z�	�5�b���
�J�+�l�Y����z��*�� �!Co7�1
	
"*	H`
S	p�Y�R��9��4�a�`�3Cp#�(�c�d�Q�R�u��~��|�}�^���b�s�{�;��O�f��
�	�Cp'�X*�>�C�g�6"�E�w�4��M�c�Cp1�R,$S	p��S�}�x�o�a��r��#��"��Cp��I��t�o�H��D�m�}�@�d�yCp��R	�Cp��*Sp�K��t��m�z��=����Q�#�(��H��eC"p���]�M�V�
�J�e�$�&	��s�i�j�.�c�l�v�u�8�"Z�?���u�
��p�P�F�#�Z��C6p��z�G,�h
����h	�s�8�'��L�k	
��\�g�(�/�(� �=	�<�_��X�e�}�j�i�f��c��:�#�
�
�'
��FCp��O*3N&*
)60Sr5�#�a��+�p�Z�?����+��CrG�K�5�*�BS
rV�k��8��5��>��K��J��
����m��l��i�?C)rf�E�C�2��#�+��B��l���u�J�5�4�A�A��o�j��'�,���Q�^�<���BCr}�L�f�_�4�C	rh�P#(.S	r��\�_�t�C�q�4C#r��~�SG�
�U�N��|�e��~�s�.��@���U�Z���j
�6��Cr��6�s8�n�B�-�+Cs�(Ssh�R�0�+��C��n��-���Ssu�+��9�7�n�%�B�0�.��7��6�5C/s��I�1�H�a�b�S�@��	�;�>��l�3�l��n	
�a�"�.�/�0��x�}��}�N�@�\
�	��3Cs��@Cs��y.$St%�2��l��k��j��u��t��i��h��m��l��qC	t3��n�1u�J�j�*��{	�DStU�<������������ ��.Ctp�>�P�oCtc�]��w�W	�6�7	
�&�/�	��"�g�`�#�,Ct�#
5RS
t��d�������o��n��o��n��9�5�H��"��9Ct�\�L�M�%�p�{�x���d��(�!�x�yCu�%Ct��x$
Su(�(���~��u�$���`��Q�e�V�I�0��r��+��*��W�S��(��O�mC
uE�N�R��m�'�@�A���}	�s�~
�\Cu>��D�"���!�d�e�W�V��Cu?�"
%.Su����,��
���l��m��l��m�'����c�(S	u��d��n��U�`�����o��FC	u��f�i�$�x�y��C�[�9Cu��eCu��cS	u¶n������	�o�@CuͶpS
uҠ ��"��O�s�8����s��Z��CuޗES
u�j���
��	����Q�BCu���{�LCu�x
�b�o
����	Cv�
Sv�2�E�n��!��"��5���l�\��M�5�R��,��#Cv0��C�:�S	v;�3�[��:��+��6�{�5CvL�4CvI�1�A�,�1�vCvH�(Svb��o�>�K�d���	���K�Cvx�H�A�
���~�
�
�M�V�9�@
�Y���O�TOCvo��.��"	��y�h	�i
�,�l�MCv��F 0
Sv͵���?�I���`��g��f��o�A�TCvۚ9S
v��n�=��Z���w�Z�M��f�{C9v�"��l�_��G���i�8�@�1��0
�/�G�t�=�q�(���E�M��5�r�:�� �)���_�^���w�C�f�X��x��/�2���)�lCwO��+9�uCv�	
	

!,9LS	w��R��P��q��p�����$��-�>Cw��\�h�
	�c�Cw��u�r�s���[�-Cw��	
Sw�O�����y��x������A��@��)��!�+�hC#w�F	��y�f�"�,	�)�+�p�~��w�z�?t�x�d�c��8�
�9�l
�O�6��a��C��I�J���3Cw��"
�l�Y�Z	�O��qCw��. *	Sx��[��h��������������=��<�C!x��7�-�Z�=	�8�
���5�D�{�l�Q�	�X8��s��'
�l��-�p�d�
�;	�/�\�~��*
�	C5x��d�=�>�?�^��A
�8��?�,�-�4�9���,�+�(��+�M�v�+�7	

�<�j�)�2�3�&���G�]Cx���5H$:
5NSy��R�"���j��e�/����?�y��6��GCyƍL�O��P��8'�BCyնCyʁ�SSy��O��X��!�����?��>����������/�OCy��&�L�1�0
�;�6�.�y�<�m�Cz�$
	�R
�
�+Cy���s
S
z7�(��h��m��l���Zy�����,CzF�%�t�`�G�x�C	zL�$�w���*�I�~�B�#Czj��@
S
z�,�^�a��@��y�x��~��c��b��cC	z��L�<�D�Y�_�p���iCz��
Cz���\S
z���0����}�x5�6���Cz��G�9
�J��m�t�i�`�a�M	�J�Q�g� ��}�|Cz��%�&�	�
�x�,�1�}��N�u�I
CzƁ�t$	
	
S{$�t�7��.������	�_��l��C{:�V�^�;�*C{1�q��+C{2��5
S{O�A�v�~�5��F��)��(��E��D��#��"��#���2��C{w�w�G�"���I�<�	�G�$�1�@C{`�~�D�K	�t�		�V�CC{a��N	
S	{Ľo��<��	���
�W�V�Q�+C#{ӔO�o�u�\�:�/��3
�6%�t�0�U�D�m�f�v��t��h�4�E��S�m�h�M
�/�x�s�l��EC6{ѹ��%�s���
��N�K���^�8����Z�g�s�&�U�6�i�0�]�I�6�z	�3
	�4�s�N�C{ҁ�&
((>(:
"*S
|�u�J�p�C�B�y�x�E�D��a�f�x��mS	|��H��$��A��@��~��#�	�&C}	��O�����*S
}�#�����'��&��/�9�s�PC} �M<�O�L�M>�rC'}!�s�z�h�
��+R��t�o�j�y�p�v��>�te�D�3�Y�P�=�v�+��-�l�x�Q�T�!C
};��w	"	 S
}��E�b�K�Q��6��C�d��dC/}��_�0
�}�1�(�Y�^
�	�v�?��*�i�d��-3�
�y�n�u��^��X	�9�<	�a�n;H	�{�n�
�)�W��]C}��&C}���J
!2S~1�;�(����L�K���
���5��4��9�C~A�NC!~E�R�M
�R��g�z�3��5��v�'��U�V�A�$��g��J�/�j��m�>�]�$C~S��& "S	~��S�I�H�c�b�;�:�/�-S~��@�X�(�,��v��'�.�[�3�.��R��9�s���O�iC~ʇxC~��g�H��
�;�v�JC~��[
S~͚�@�5�
�I�t��`���n��k�S��2�.�/�O��S~��$��R��}��I�P��L�L��b��1C~�E�b��-�w�V�B�/�]�F�u��{�#�C~��o	C~���\S�O�!�Z�I�f��
�	�
�m��*��=�C(��E�Q�Q�n���r�$
�Q�b�h�m�8�v���E�H��(��N�G��|�J�ICL�b�nC7��`		S
��b��>�?�>��o��n��5��4��C��\�&�r�E�g�p�+�6�u�+�\�A��)���?	�~�R��q�c�l��@C��D�X�Y��	�9�`
�
����r
�s�xC
���!

"
S
��0��h��5��4��5�-�,�%��WC��N�}�~�;�<�G�v�e�}��n�6��l�T�VC�,�BC�!��l	S	�R�\��N��#��"��;��:��_��^��{C�^�
�y�S�o��o��N���+�O�X���/C�}�y��5S
���U��^�?���L��G��F��/�mC���qS���o��t������K��8�O��D��{�I�,�����9US���Z��4��t��u�g�.��*��{�'�� ��wC���yS
���k����
��L�Q�c��q�p��p��aC�̈�J�S	�֗�V�����C�:���C��1�v�2��S���Y�\����/�2�n�K�@��������W��V�������C��+�L�_�r�cS
��#�5�%�.�O�@��"��G��F�������`C�/�'�4�
�C��
	�NC�0��oS�F�F��p��S��R��K�F�.��R��=��<��1��4��W�A�V�@C�`��VS
�e���e��V��a��`��s��r��_���x��Y�lC�y�4�"�}�H��y	�v�B�
��#�
�?
�C�t��:	�#�<�<�y�P�_C����

S
���A�X�� ��%�4�A�S�^�W�.C��a	n�>C�Ơc�v�e�2��5�*�S��9�R�w�n
�n�/�4�mC�ˁ�D
S	���f��}�]��l�E���:C��}��$�_��DC��bC���j	S�(�0����	�x��~�����y�/��:C�5�%�]��
�P�#��U��D�R�!��
�W�V�q�vC�8�2	�L�M�d�/�7�d�)�V�^C�:��{
&
S���x��d��
���&��;�0	����	�C	���j�{�j����o��n��uS�����4��J��%$!�{�?�H�Y�J�e��v��A�@C
���t�+��

��fC���"C����<S�͉�F�k��t����)�4��b��5�:S
�ےN�p����	��%����1"�k�E�C
��t���T�����$��y��x��S
���\��{�W�C�t�A�B$/C��T�m�/C��
./<3C�
��_S�+�(�u�r����%��$��-��,���K���
���T����7C�F�R�]��!��/C�@�%C�H��~

S�]��
��W�%��j��/�q�^�7�8�s�nC�o�0�a�h	��!��b>K8

�Y�L�-�*C���+�Y��jC����&S	����4��m�q�t�i�p��\��]�7C3���^-(��ZH�Q�(
�s�x�Y�j/�7�X�k�
�X�k�`���B�4	��6�}��<?0�C�B
�Y�Z�s�l�i�C	��M�L�!�2�z�}�t�iC�Ɓ�;	!(
Gf	S�i�n��d��c�f���\������!�� ��}��|��;�L�d�o��t��{C���:�^�U�^�g��L
�O�O�C���MC����2
*S
����R��.��i�Q��8��5��#C1�ğ9�
���"��T�g�
��.�!�%�>�}��m	�n�T
�w��/��<
���	
���)C�˾"�
�8
�d	�=�c�.�;�L%'2!	(�9�bH	�C�́�\
 
$S	���
�*��b��o�D��(��?��>��IC%�����
�%�*�5�<�_�/��h�S�T�u�K�@�|�I�J�� �k�B�.��
��o�pC"����M
�^�s�j�n���c�"	��:��`	�-� �1�<	?
�}�2�e
�c�F�f��+�t�mC����W$ 02D*S
�y��,�;���S��C���R�#�(�&���	�B�s��4�@�8�{	�d�:�;�q�<�C'���z					

	
�|��z�{C����S
��z�0��p��%��$��_���J��S��R��_��^��]C�!�%�l�+�r�3W�-�0�q�s>�z	���y���R��)��'�&�I�C[�"�.

�l�g
�j�{�T�[0�*�3�L�r�O
�*�%�T�u�8��(�%���.�-�
�%���s� �X�{���(�)�x
�_�9���2�3��X�p�#�0�!�EC�$��S(6!.
"
$S�h�V� ��x��W�!��v��?��>������C�w�S��0��AS	�}�)�(�)��
���v��
��M�fC����8�b�@�=�j�G�l�y�+��d�x�E�z�%
�I�2�Q�1�kS
�@��|�fC���@�)	���2��,�&�U�#
�^�]�x�o
	
C����6"&2

S	�0�9��T���2�w��H��G��F��QC�D�R���T�	C�;�]�5�r�'�"�s�l�w��F�Y�Z�Q2�l�I�"�b��1�(�[C�e��IS
���}�������u��,���<����C�ґ4��(C�ֺW�;��m��)�C�́�
S
��~�p��p����
��!�� ������'C�H�C��j�n�/�'�-��E�{�"�g�X�z��$�K�I�	�u��Y�X[�?�.�\�?C
���6
S
�i�,������w�v��t���?��n��L��}C�y�S�a�i�)C
�|�p����6���C����{
S���=����
�f��$��K
��O��p��UC���\�O_C���G�v�S�N�C����S���i��l��G��F��o��n��]��\��Q��P��Q�+C��`�3�.C���/ �o�^C�݁�+S����}�0��8����~��g��f��!�� ��_�l�]��L���L��L��C�
�W�T����iS
������u�5��(��s�|�[��N��kC�f�^�RC�!��O�Z��
a�S�z
���W�P�I�H�[�I�&����j�[	��"�]��{�:C�'��S/H8TS
�����6��'��&��)�=�#��d�%S���)��v�}�����"�#�K��d��U�S���q�V�K��&��V��U�u�,��
�xS�ė_�H�o��B��K�m�v���#�(����}C�ډt� �m�n��C�՚kSC�֦&�q�t�#�,�x�+S'�ꁖ1��k�\�P�4�[�Z�m��l�4�X���
�{�m�f��b�2�)�(�?�>�[��@�]��L���;�J�9��(�{C"��L���a�`�Q�R�}�J�Q��A�@��:�6��C�t�!	�@��6���;�`�h��J��\C�G�v�"�#�
�Q��~		�L��g�g��*�+�k�f�i�`C���3l|S���"���8��3�� �����N���B��=�k�|�l��:��!�m�H�?9�+�*��6��+�q����QC���2�/�����AS���3��J��u��t������O�t�i��;����%�VC	���3�I�<�j��@�z-C���5C����*S	��f����-�P��Z��;��:���C��=S�����Z�-����g�{�J�s�'��E�|�k�\�]���N��y�\S�4�L��B�a�r��@�>�q�p�O�+�x�n��C�G�6�?�S�W�f�m�w�mS�S�;�1�(�1��>��W�F�a�e�b
�E��>����*����x��O�0C �k��^�#��W�	�O/�\��R�?��	�p�q�O�|���5�|�S�$�X�?��v��J�O�BC"�m�u
�D�J��w��9/ 1,�F�EC
�l��FLf

S	�)��L��
��������
��C�D�6�;�8�C�4�0�i���p�M	�V�WC�C��HS�l�&�w��n��7��<������9��8��AC�z���T��G�U����S���J��L��'��&��m�a�j��`��M��L��S�l
�oC���&�G�h�-�rC7����g�j�E�H�/	�k�V�c�>�=�N�V��"�k�n�M�P��~�%�8��p�
=�@�E�@�W�^�K�i�Ps�4�?�"�6�sY��bC
����z5H)<
/:S�_�F��t���(��X��e��d��S�L�y�|��~��G�5�4�K��[C	�t�`����#�"���JS
��� ���F�R�-��7�F��&S������L���p���Z�:�o�n�W�o��d��}�C����&��:S	������}��|��g�%�Q�@�LC���H��A�d�.�K���
�j�v��w�v�h�u�^���oC���=C�Á�
S��I��>��O��J��I�'�l�����F��T��?C	���Y�W�N�z�1�u�C��;C��EC���{S�	�J*�P��\��Y��X��!�
�6���v����K�)����{�K�9��|��A�M�.�@�<��"C�&�J�!� �]�=�/�.�W��b�q��@�D�k�T�C�2�;�.�+�1�NC�7��I
	S�[�Z�"��X��7��6��!�� ���U��X��eC�h�W�6��*S�r���@�����.��/�$����1��0�������I�<��+�hC���q�%����M�KS���c��)�� ��	����5��4��������������C���l�%�a�cS���{����'��&��!�� �	��"��%�3�4��"��!�m�h�C!���u�9�:�W�L�|�)�,��n��a�	�p�[�Z�A�L�f�
���!�"C����W�X�3�h�>��-C�Ɓ�


S
�-������J�O�H��>��]��\��[C�9�	�-��Z�w�a���>�'�=�VC�:�
�SC�G��Y	S�i����T��3��2���J�u�|��J��K�O�<��N�^C��SS	�����T��i��h��U��T��7�XC���V�ZC���C
���	��

��z�Q�#S����x��z��9��8��=��<������A�+��5�x�A�_��"��3C�܍%��
CD���x�_�Z�m�w�j��	�c�t	�_�g��:��V�#�	�j&	�7�2#�5�<�'�*��l�u�3�&��w�h�A�f���!�(�o�w�\�x	�9�D�g�@C�ց�

&*"0


S���5��V��)�0��v��]"+����EC'���M�Q�G��7�u	�x�_�`�
�m�k�.�-^��5��b�
�Lf
�{�h	�y�O
�$�%�%�.��J���]�.��bC)���%
05
:C,;6
	

E0	�>�m$�%�.	 	PIC����+"

	
, S���cy��B��7R��b��_��^������QC����S�|C>��� �	��7�2�[�nD�y�(�D��e�

	�+ ��x	�9�H1��>�i�`�F�;�f�1�*'�'�P�Q�P�U�V�M��O�,�%�4�8�}
�C����O("*

S	����a��)�m�~��������EC�����|�S��dS
����a�Q�z�9�&�wC���+�r�s�r�q�0S
���K�D�}�G�5�t���c��bC�ܜ^�m�2�E�a��Y�d�
�|�x�#�]�R�u��b�6�U��}�L��#�|C�ص>

C�ρ�e
 S
� ��l��A�6�C��.���L��s�lC�-�f��B�~�Z�g�z��s�y�;
�B�b	�'d�0�
C�,�lBM�>���Y�XC
�3��n		
S���E��~��[�V����	�}���������vC���i�MKg��(�x�o�v�1�.	�'�D�3�4�y�&�P�I�&C���y!HC����
S
��J�t����h��{�4�G�,�r���cS���K�n��k��l�!� ��^��q�R�4C���'�{�F!�I�B�����u�~�<�.�YC��C���=S�;�E�(��r��M�,����s��r��;��L3�I�h��r����C�P�K�"�E�<S
�[�F��2����������W�
��EC�g�_�'��=����S	�p������8�9�_�h�B�C�}�S���L�	��X��%��$��;���8��'��/�RS	���o�D��P��-�W�P��0���C���b�h��^��S���f�p��Z���p�� ��A��@��u�9��,��WC6���4�<�p�M���y�;�*��b�x�{�M�U�o�`�l�!�Z�'�k�n�A�z���}�4�D�m
�7�:�N�&�,�1	�>��g�6�y
���o8�V��
���,�C4�ɶ�>�0%�g�
�1���c�X�]�@�=���B�C�>�9
�%�T
��w	�2�/�
��hg�%�!�{�HC����$$$"

S���I��2��5��>��t��5�	�|�S��������/��.C��A�o�7�X�D��2�#���j��O�0�%�(�(���=�qC
��IC�%��t (S�o�>�*�!��F��3�9�s�-��p��g��`��i���e�"�@�w�'?�Z���7��@S����*��	�9�m��,��g�\����-�	�D��nC���~�#�[�{�|&�}	��O�F0�mT�C7����.A�U�/�.�/�r�L�D�I��j�a�X��/�+�|��5��A�9��v��g�&�X��P�+��d�S�b�g������v�w��!C����.

1DS�m�V�V��^�/�Z��P���S�F�
C�z������~����i�b�WS���(�C�#�d�;��>���[��^������eC�����~��S�C
���+��(���}�F�%��B�{�n�C����A 4S���	��4��]��\��9��8��=��<��i�8C��C ��?�N
�!�n��
�7�p@�I�(�s��_�V��?�0�c�(�w��U��C���}+F
S
�j���t��g�(�I�(�5�r�����R�&�QC�y��r�y�w�p�_�ZS�����f��u�d�k�_�H����
��4C���2�$�o�r�y�x�S�(�Y�:�R�enC���)�-C����OS
���a�������,�\����!C�<�FC�߻l��
	�{�.�1�
��R�S�`�a�.�
�j	�[�D�=�$C	��q
S	�A�
��B�w�r��,��+C�O�I�C3�M�e�~��z�W�_�(	
�]�V�_�x
�W�R	��$��38C
�P��;
(F(,S�1�26��(��#��"��i��h��o��n��+C	�|�H�]�
�D�9�	
�z�}�CR�>�T���W�,Aja
�Q
$3B�(�F�I�/�
�j�)�Y��	��4
�U�^�{��i�`�C�B�E�&	
�%��6�	�g�j�i�8.
56'&��EC�S��~,		;4 
Cj0'>"S��!����Q�u�@�9�8�q��t���T��*S�-�e��*��M��L���%��$��u�tC0�=�f�p�p�[�H�7�O�v�U|�k�8�_�m��0�75�U��/�&�Z�+�5�2.��T� 
�w���^��u�90�>�0���,��90�R�aYy��p�5CY�>�C	��	�P*�G�_�N�=�J���Z�
�<�Q�@�E�X�a�,�s�N�S�6>C�)��E�D	�G�t�I	
�^�H�N�#���E��;�G�H�U��v�#��3�>��J�.�H� �1C����SC2�@��@",($
Ht  ��0�D�?��D����nB�<T� �9G�7�n2c, ^��
��%�6F�Z�a��Hmo�#�,�z$�:��;���p	�f
�R�>dK�b�4`s}q{)R�d
U�7$�<�v	�p��c�=�P�}�@�P�X�D�T�F�R�J�H�L�<�X�
��(c���Ocdx�c��c�$�c���@cR?�cR����,��vG�E��2D�6C�Z�
�g�����#�'�*�-'�<�>�E�J�L�N�Pc�>TR�E�
�+(� �(G�7Q�IE�\
�o�e�m��
�,�xZ���1h�D�7�#�H�Lq�\�N�v�m�j�5�
�j�(�U�v,�x�=�f�(��?�h�V�Z�]|�^M�(�
�1��V�>�r�q�	�6�G7�rA���C&�H�	�g�R�\G�eM�sv�&�+�-�8��|���	�f�!�=��U�R�V�Z�W�\�`�f�j�l�p�s�v�x�{��	�
�`i�Gt�Q�?�z"�
J�!���&�+�4�<��
�/�K�;�>�`�,�2�4��%�!�*H�@�B��c�f�j�n
�s�%�O�T�{�z-�#�	��U���	��
�#�'�,
�0�7�:�@�D�F�K�M�R�W�Y�[�^�e
�i�m�p�t�x�}����
����	�
����!�z�S�JW�N��d�h�j�l�n*�7�;��#�,�.�a�;�@�F�J�M�O'�]�8�������%�'�/�3�7�>�A�D�J�7�A�D�G�K
�Q	�V�}4�y��V@�F�oc���yc���\�)c}��:�c
vF�/%�4�/��2�s����O�S�hH�A�f�F��Lc���;c�;��3�$	�)�<�L�D�h#�G
�k�z&�}$��P��C�8�L!��q�$�)^�&���2��/��V��X���aq��f�j�Y�_�e�l�u�w�{�}	�������"�&�+	�.	�3�6�;�A�H�J�O
�S�U�^�c�e�n�r�t�v�y�~��
��
���#�%�)�,
�4�7�;�@�D�I�O�S�V

�Z�g�k�n�q�v�{��	�������(�2�4�9�;�=�@�B�E�K�N�Q�T�Z�]�b�f�n�t�v�}������� �%�'�-�/�1�3�5�9�=�B�G	�J�M�O�W�a�c�e�q�t�v�x�|�~�	��	�
�������"�%�(�1�5�8	�@�D	�G�S�X�]�b
�e�p�s�u�x�}���	�����
� 
�$�+
�0�4�7�:�A�C
�I�L�Q�U�Y�]�`�d�k�s	�x�}	���
����!�&�-�0�9�<�A�G�J�L�O�X�\�a�c�i�n�p
�u�x����	�����
� �"�&�(�+�.�5�9�@�F�H�K�S�U�X�^�c�e�j�m�p�s�x	�|���
�
����"�'�+�.�7�:�?�C�G�J�L�O�S�U�W�[�b�d�f�i�k�p�t�w�z�|�	���������#�&�(�+�0�4�9�@�H�L�O�X�_�a�d�f�i�k�t�x�z�|�����
�����
��+�3�5�:�>�C�K�N�U�X�_�f�n�r�v�~�������� �"�$�&�)�+�1�5�8�:�A�C�G�J�M�P�S�Y�[�`�f�i
�n�p�t�w�z�~�����
�����%�'�*�7�<�>�C�G�J�L�O�R�[�`�c�g	�i�s�w�~�����
��#
�*�5�7�?�A�E�G�M�O�V�Y�]�_�b�g�j�o�s�{�����
�$�/�5�7�9�>�A�G�I�O�R�V�Y�\�_�c�g�u�z�}�
��
��������"�%�+�0�7�<�A�D�F	�I�M�S�Y�_�a�g�m�s�z�~�����
������!�(�*�/�7�?�A�D�K
�N�Z�\�`�m�o����
��#�)�.�5�7�:�<�>�G�J�L
�O�V�Z�]�_�a�d�j�r�t�w�~��	����� 	�$�.�4�6�:�=�C�J�L�O�S
�Z�\�_�d	�j�n�q�w�~���	��
���� �&�(�0�2�7�9�<�?�B�D�F�H�K�M�P�R�U�X�Z�]�`�b�e�h�m�r�u�}�����
��
�����#�(�*�-�3�5�=�@�E�H
�M�S�U�Y�^�a�f�j�l�o�r�z�|���������	�"�,�3�7�<�>�D�H�K�N�T�W�[�`�i�l�o�x�}�	�������"�$�+�.�0�2�4�7�:�>�F�J�L�N�R�U�Z�a�c�f�k�p�t�x�z�~����
������&�+�-�0�2�7�<�@�L�N
�T�X�Z�^�a�d�i�n�p�v�{�}���������"�%�,�/�3�;�?�B�E�M�V�X�\�_�k�q�t	�{����	�
�����$�*�,�1�3�5�8
�>�A�C�G	�M�V�Y�\
�d�g
�m�t�v�z����
�������$�'�*�.�3�9�;�A�C�J�L�N�P�T�X�]�a�c�f�j�l�n�r�v�{���������'�*�,�1�3�5�9�;�>�@�E�G�K�P�Y�[�_�a�d�g�k�m�s�v�x�}���	�
���	�(�2�8�:�=�?�B�K�S�U�Z�]�d�k�n	�v��
��� �#�(�*�-�2	�5�@�H�L�O�S�U�X�_�b�f�j�m�t����
�����!�)�+�-�0�2�5�8�:�>�@
�F�I�O�Q	�S�_�a�c�e�h�l�u�x�~�������!�)�,�2�7�<�?�A�C�H�J�L�P�X�\
�^�i�p�r�u�x�|����	�
������%�4�;�D�J�N�P�T�X�]�a�c�f�n	�p�z�����
�
�	���$�)
�0�4�8�:�<�>�C�E�H�J�N�P�V
�Z�_�g�j�n�q�u�z���
������ �"�%�-�/�2	�6�8�:�<�A�H�J�O�S�W�Z�\�_�c�f�m�o�q�u�x�}����
�
����!�#�%�(�,�.�0�5�8�;�>�F�H�J�L�N�T�W�Z�^�c�g�j�n�s�v�{�����������%�+�-
�/�:
�<�J�N�P�U�[�_�a�d�f	�m�p�u
�|�~����
�������#�)�/�2�8�<�@�C�E�H�K�P�X�\�^�b�d�l�p�s�u�x�|����	��������%�'�2�7�:�>�A	�F�H�K�S�U�X�\�`�g�j�m�o�s�w�y�}���
�
����	�'�+�-�2�6�?�B�I�M�P�S�U�^�c�g�m�q�y
�}�����"�%�(�-�/�5�:�>�A�C�H�O�R�X�[�_�b�f�k�o�r�t�v�z������	�	�%�1�4�7�>�A�E
�M�Q�U�W�Y�\�^�`�g�i�l�t�v�z���	�����"	�$�&�*�0�3�8�;�=�?�C�E�I�L�N�U�X�\�d�n�t�w�{�}����
������ �%�(�/�4�:�=
�A�M�Q�S�W�`�f�m�o�t�w��������%�*
�-�9�=�?�E�N�P�\�`�c
�m�t�y������� �#�'�)�,�0�4�8�:�=�F�K�N�P�S�Z	�^�d�f�h	�l�n
�u�w
�|����������� �"	�&�)�+�3�7�<�A�C�H�J�L�O�Q�S�\�e�h�l�p�r�v�z�|
��	���
��"�'�+�/�6�:�=�D�L�N�R�T�`�c�f�l�n�s�y	�~���	���� �)�,�/�3�9�;�C�H�J�L�O
�S�W�^�`�e�k�o�v	�z������
������������!��$��'��+��.��1��9��<��?��A��D��F��H��J��M��P��X��\��a��e��k��n��r��t��{����	��	����������#��%��)	��+
��5��@��B��G��O��Q��U��X��Z��\��b��j��q��t��w��y��~��������	��
����
�� ��%��*��,��1��:��A��E��I
��P��T��W��Y��]
��c��g��i��l��p	��v��������	����������	�� ��$��&��*��-��3��6��=��E��L��O��S��Y��[��`��c��g��m��o��s��w��z��}��������	���������� ��"��$��'��*��-��3��9��=��C��L��N��P��\��a
��i��w��y��|����
�������� ��#��&��*��-��/��4��6��8	��>��F��H��N
��Y��_��a
��e��h��l��o��r��x��}��������������!��)��,��1��4��=��@��B
��G��K��O��Q��S��Y��[��`��c��e��j��m��q��y��}����
��
������	����'��+��.��1��8��:��?��A��D��L��Q��S��W~��b��j��r��v��{����������
��������"��&��,��1��5��8��>��B��D��J��M��O��U��[��_��c
��g��i��m
��q��u��y��{��}��	��	��������
��"��(��.��4��6
��:��?	��G��I��Q��S��U��W��Y��^��`��m��p��t��x������
����������#��*��,��.��1��9
��@��B��F��O��S��U��Z��_��h��l	��t��|��~������
���������� ��#��%��(��.��2��6��:��>��@��B��G
��K��N��R��T��X
��]��a��d	��f��p��w��z��|����������������!��$��'��,��0��6��8��<��?��A��G��K��N��Q��U��Z��]��b��e��k��n��t��w��{��~��������
��������'��.��4��8��;��=��A��N��Q
��V��X��]��`��b��i��l
��r��~��
��������������'��+��.��1��6��:��>��A��C��H��K��M	��U��X��Z��`��b��h��k��r��x���������������� ��#��'��*��/��3��6��9��<��A	��D��P��R��_��a��g��i��m��q��s
��z������
������������!��&��(��*��.��0��4��8��<��?��D��F��H
��Q��T��W��[��]��_��a��d��f��m��p��w��}��������������������$��&��+��.��2��7��9��?��A��C��J��N��Q��W��Y��\��_��b��f��i��p��t��x������������������"��(��-	��2��4��7��;��>��C��I��P��S��X��\��`��b��d��g��j��l��o��q��s��{��~��������	���������� 	��'��)��.��0
��2��@��F��J��N��P��R��Z��_��a��e��h��k��n��v��z��~����������
����������!��&��-
��4��8��<��@��D��F��I��L��O��R��T��Y��a��c��e��g��i��k
��p��s��u��w��|��~����������������������%��(��,��1��5��8��@��B��F
��L��O��T��V��[��`	��d��n��r��z������	������������!��%���4��=��A��C��F��J��N��T��[��b��f��m��o��s��z��}��������������%��+��/	��4��96��>��C��G��I��K��T��V��Z��_��a��d��i��l��p��x��}��������������������$��(��*��-��5��7	��?��A��D��F��I��K��O��Q��T��]��_��a��g��k��n��u��|��~����������������!��'��*��.��4��:��A��G��O��T��Y��[��_��b��f��h��j��m��o��r	��wA��	��
������������$��'��-��/��1��4��6��=��?��E��G��J��M��P��R��W��]��_��b��j��m��v��y��{��������	��������������"��$��)��-��/��9��;��?��B��E
��M��O��W
��[��_��a��c��e��k��n	��v��x��{��~����	��
��������#
��&��3��8��:��=��A��C��K��P��W��Y��[��]��a��c��f��i��m��o��q��t��w��|������	������������$��&��-��4��7��9	��;��F��K��M��R��U��Y��]��e��g��i��m��q��s��u��w��z��������
����������!��%��(��+��/��1��3��7��>��G��M��P��R��X��[��`��c��g	��l��o��}������
������������$��(��+��.��2��8
��>��I��L��R��U��Z��\N��f���w��y������	������������"��%��)��,��/��5��7��9;��C��J��M��O��W��Y��\��^
��c��f	��j��n��x		��}����������������"��&��*��,��0��3��6��9��<��>��D��L��N��P��S��X��[��_��b��k��m��o��r��t��v������
������ ��"��$��*��-��5��:��<��?��E��J��M��U��[��h��k��n��r��u��{��}���������������� ��#��&:��/��6��8��=��F��K��N��P��S	��Y��\��d��h��l��n��r��t��v��x��~������
���������� ��&��*
��/��:��<��?��B
��N��]	��c��m��p��~����������#��(��.��2��5��;��=��@��C#��K��M��Q��W��]��`��b��g
��k��r��v��{����	������������!��&��+��3	��8��<��>��B��V��Z��\	��f��q��x
��z��������'��,��0��3��7��:��C��K��N��Q��]��`��b��e��l��n��t��v������	������ ��"��'$��0��5��7��9��;��=��?��B��D��K��T
��[��f��m��p��s	��y����
�������� ��)��+��-��B��K	��P��Z��]��b��f��l
��q��	����
�������� ��,��4!��>��C
��I��N��R��V��X��\��^��d��g��m��r��t��y��}���������������������� 	��$��0��5��<��E��G��Q��V��[��_��c��e��h��j��n��s��w��~����������
��5��/�K!O���(����
�������	�+�5
�9�Gc.�������"	��$��.��0��<��>��A��E��G
��J(��X�?��{��~�o��s���HF��*��H���sc5���c5�`��
��~
���d��c7���|c8���|c9��|T��_��i
��I��Wr��wc:t���jc;M��v�0��Dc<m��uc<op��cc<���T���sc>���sc?���sc@U��s�(��IcA^��r���QcB���qcC6��qs��(cC���*�� !��K�U��mcD�(��C���lcFK��l��8���LcG"��j��
Q����dq��tcH���fc	IF��f1��-��_��a��f��z��}G�����[cK���[cLv��[cLx&��R[��y��U���hcM���|��J���c����c����c����c����c��6��PK
!<�9�L��2chrome/pdfjs/content/web/cmaps/UniGB-UTF32-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�
UniGB-UTF32-HC �V�Z1�6��m�1
�:�3�2�
	��c0�H�R�F�P��p�D�@PK
!<F`PO����1chrome/pdfjs/content/web/cmaps/UniGB-UTF8-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE���?���?����? A, �J�vG9�_8�<
&
�Q�Z�D1�X&#�b�W	�e�-�w�v�@A©�!�o�&A¥��RB2ḿ�7��T�	z
�a�<�e�e�{�tg

9�J�w	�	A�bD�
J�R�u�yB‐�"�Y�O�H�}�r�:	�n��)}�IxB€��Q�S��{

�NR@⼀�B�@��r��-�
�;�]��a��l�I�Z��.��-:�*�+��&�
��C�)�V�C�9�
� �5�;�p��z�	���g�`�}�<�1�^� �u��/�6�(��a�6�i�#�U��X��KR@⽀�&�D�m�;��M�$�{��I�$������|�j���=�z�!�j�f�%�:��H�~��]�N��Z�h�M��M��<��;�@�R�c�:��j����M��)���k�b�r�7��&�%R@⾀��C�9��c��>�|�}��q��z��e�K�q�Z��$��
�}�H�9�S�8t�T�m�l�5�(�_�U��`��i�(��F��}�3�f���v��U��4��
���D�9����UF�B�8��
�"�F�kR⿀�`�5�d�;�V�Q� �C��j��{�q����g�n�X�5�*��`��B〃g��~�#
�Z��*���|��Q��~�t�q!�}
�b	��j�5�E��n�5B.〿��U�T"� 2�L�)+�j�o�}��`��Z��q��u���t
�k�x�u���P�y2�]
��j�
B䦄��eR万�#��%^�L�P�-�������1�m��
��c��.�K�$��TR丢�i��p�9�b�T�����g�j�h�R丰�~��P��m��l�
��o�n�#�)�f�x�h��3�8R乃�.��{�z��]�d��	�	�<�9�S�z�`�5��`�\��
����	�PB乜�u��8�N�9l�)�j�;A�
�7B亂�YB乲�R了��~���t�y�9�2�1�w�"���x��/B亘�^�t�S�yR亞�q�}��9�A�(�!�a��M�*�����x�B亲�T�`�6�3�E�p�}R什�I�I�xI�k�H�/��&��?�^�w�3R	仑��Z��!����x�y�R	仝�n�������k�r�FB	件�qG�)7�N�$�3�/�B仨�#�+�,�3�$�p���E�/�6�M�!�^�`�_�xB仩�5�AR
伞�Z�H�#��D�Q�P��N�]�\B伪�@�\R伯�u�F��|�C�B��K��J�W�V�m�l�E�D�E�xB佃�I{R位�J�9��$�3�d�g�f��O��N�or�W�Z�M��>	�_B佰�fB佣������W�:�+�j�_B佦�pR侃�=�������p��7�6�C�B���B侔�D��Y�[�"���
�5R
侣�i�P�{�n�M�:���"�!� �7�)B侵�S	��C����8�EB俅�HRMB係�R
俗�L�e��,�Q�P�O��r��N�K�D�SB俩�r���Z�)B俦�E�=�NB俧�6��@�o�mR倉�b�P�W��s��X�s�r��K��f�YB倘��e�Z�t�w�=�6��R
倥�Y��|���2�B+债�O�H�)�D�v�!�"�+�$���E�Z	�G�P�t
�#�@��7�l�I�j�P�}�|��-�0�o�S�^��1�6���7�-�B偉�*�_�6�`����^�1�h�?�+_�C�V�7�<�s;�$�S�L�%B倻�e�M
�{X
�Q�`�P#,R儀��2�7�6�1�0��n�)�8�B儒�@�s�B
儐�V
	�1
�t�=�|	B儑�r�e�tR兀�Y���O��1�\�8�A�u��4�}�|�aB兑��D��a�>�<�dB兒�eB兓�R全��<���'�Z�z�f�f��9��>��u�H�k�B�k��>����6B冀�0��]��a�`�&��|�o�B	冁�z�;
��n�+�*�S�RB冘�;R	冯��M�Z�
�4��F�
�h�SB冻�r�>�B�OR凄��$�s�r�~�#�"���:�:�
��[��Z��}B凛�&�r��k�9xR凫��T����e�5�p�&�����+�o���G�&�`��i���B刀�x�n�C�q�2�~�#��{�o�)�*H�l�K�3;�8�MB刖�nB刉�sR	刨��i�:��W��V�m�Q��<��QB刳�R
制�E�]��@�9���b��}��|�wB剀��Y���L�\�M�	��x��X���F�'�wx
�\�y�A�z�9�:B
剄�H�K�&�<�c�M�j�~�L�y��aIB
剋�	�"



�A
R	力�o��i�)���r��cB动�n����p�%�L
�.�g�C�Y�j��V�z�%B勁�>B勀�QR動�M�u���C�T�l�u�t�u�I�IB勤�W�x�1�Y��b�@��	���0�n�m�3�E�U�B��a�!�n�V�:�=�<
��`���B勢�2�`�%�F�t��O�NB勣�d�c.		
R區�o�U�.�s�r���S�~��wR
华�?�p�T���NK�f��(�,R博�p��j��Q��P��3�V�;�Q�:�n�g��=�O�
�/�.�7R
卮�j�o�$�)�t��
�N�/�j��%�H�Z�9B卿�a�B�+�H�:�U�~N�d�1�T�QB厙�AB厃�6R
厝�~��+�f�B�q�`���!�.��W�0B厮��4�9��A�Y�	�J��j�$�-�%�x��l�M�Z�SB厭�y�S�b�N�QB厳�O�JR叟��m����w��h�D��b�U��3�0�O��f�H�~�[�P�b�I���l�:�0�W�]�TR吀�m�'�&��k�p��x�[�Z��U�,���4�)�;�-�h�&�V�@��~���2�UR	君�,�����(��)�<�Z�WR否��u��6����/�X�_��'�N�)�J�\��3B吸��'�:�_��A�j�_��b��&�n�{�^�Y�>
�-��@��s�*�B�w�$B
呃�



 
�JB吺��G
�LR咋�G���J��_�L�O��^���;�q�8B	咙�E��R�/��-�qR
咫�g�s�V�6�����3�2��G�B咸�9�V��u��1R
哀�2�F��F��G��F��E�Z�k�B'哌�'��S�|-�U�Z�M�V�/�o�(�t�?�E�j�y�r�.�p�o�,�:�G�A�	�I�S���A�@	�C�PB員�G�b�HB
哖�C�A"R
唪�<�j��u��t���*�}��H�QB.唷�G�y�r�A�K��9�t�)�0
�S�4�\�%�>�}�t��Q	�,�R�G�>�A�I�B���%�M�t�a{�N#4�]�\��A�,�#B問��'
�L��K[�iB
唽�|�B

�P

R喱�R�2�]�x�y�x�)�(�U�T�E�DB嗄�j�}�j)�g�B嗆�X�N�#B喿�@�HR嗚�!�X�_��J�i�h�m�l�3�t	�R�C��3B嗯�k#�1�b�A	�k�j�u�z�
�?�<%,B嗶�r�O��"���
��WB嗴�W

�G


R嘬�	�r�S�0�g�A��>�Y�N��
��S�l�[B嘿�B噌�	��&
B噀�{��R
噠�p�@����V�>�{��i�:�YR噯��,��P�D�E�D��9��8�}�,�NB嚎�v��V���M�FB嚅�
�dB嚀�n�o�z�`�Q�I����J�E����I
�e���<��o�mR囚�q��>��O�|�g�6�?�F��B困��}� ZB囫��I�{�>�W�VB囬�aR圃�s�L�&�s���4�B�b�AB!圜��W�m�@��Q��`�K�P�E�A�P�r��!��?��k��k*�E�b�N�!�6��@�kB園�F�B圗�v�A	R坤����'�&�S
�1�2�Z�S�R��UB坷�M�J�#�4�b
��OB型�B坳�:�HR垠�CC�#�8����M�B��I�H����~��B*垲�>�I���R�Y�$�/�$�o�-��W�'��B�{�
�v�{�=��X��>����N�(B	埡�*�?�M�
�>�S�tB垳�K�_.
�R
R塊�c�*��f���
�A�6��B,塘��}��/�.m�Ar�q�^��1�O�:�B�G��G�u��D�C�B�c�J�t�e�V�=�=�K�&�H�0�g�����Q��Z�E�@�YB塗�
N��e�0�P�)�^�'2�>�y�H��
�M�<�/�P�X�y�
�K�\B塣�=(�B.�@
�U
R夢��t�C�F�������_��^�/�)�E����B头�hR	夷�K�5�]�G��H�-�,��B奁�0�I��{�)�M%��f�S�_��a�N��M��
�B{�'�*�qB奩�[�{��QB
奀�H
	
R妀�k�m�g�f��z�=�+�*��:��Bj妍�w�]�h�N�(��	�o�U�<�{��s�h��G�U�B�S�?�1�F�A�E��;�e���,�s�`��T�A�sA��{�n�O�%��P�E�r�H�~�%�2�C	�_�z�d
�1�2	�e	�L��Y�^�;�R��$
��
��B�� �u�.�B
�_�^�W�B�W�G�F�HB妝�-�c�]$�c�T�y�'�Y��D�-�0�?��S�0B妘�v!&�Y
"�E�~�F
�@�_0
(R子�0��t�u�)�\�>���t��w��}���AB孢�9��-�2�K�o�8�T�?�N��I�\�SB孫�Z��B孲�<R
宀�.���D��'�z�E�n�e�I�e�����.B宏�!R宓�1�4�9�8�-��d�}�r�`��x�����<�%�V�;��7B宪�I�	�(�{�d�O�I�d�}�7�f�B�7��A�q�g� �E�~B宸�3B宷�X�K

R寝�[��?��x��
�t��u�M�:��'� � �=�_��w�v�uB寸�j�P�F�B�y�$�A�cB寵�
�'B寷�y�HR射���B���B��3�~�4�.�MC��(��_R
尔�:�n�;��\��C��B��y��x�'�YB	尢�6�(�=��(�G��y�xR	尷�\�3�~��B�E�i�`�YB层�*B局����l���\�7�c�L�$B屐�hR属���:��q��F�K�v�y��z�mBc屮�t�m��R��7�A�e��>��t�5�<�3�2��|���q�d�{�l�e�A�l�y�1�(���d�'�
�T�J�:��v�O��@�;�^�\�K�G�2	
�M�M�_
�H�U�`��	�J�$��s�
�<�c�R�e�T�7�K��4�sB嵇�"B屬�>�t�Q��d�%�x�O�4	�k�`�c�p�A�,
�;��N�'�V�x�]B屭�.�O�A&:�X �\*
�U"" 4�P
R	帀�D���V�I����S�)��z��B希���g�jR
帔� �=���~��)�H�{�Y��B带�{�H�	�E�N�;�Z�DV��!�����mB帥�C5��!�I�:�|�m�%�$B帬�Y�N	R干�D�.���j��5��4��O�j��w�F'��oB庀�1�K�v��?�[�p�zR序�=�Q�8�6��?�6�'�v�S����;�u��.��O��N��{�Q�B"庥�5�]���F�K�#�6�0�J��	��O��L	�Q�9�S�5�f�%�@�I�J�U�5�]��]�jB庫�`�s�L��j�qt�&�dB庬�

�i8	�SR
式�T���;�:��#��"��)��(��s�B	弛��z�P��F���X�iR	弭�p�j��#��"��/��.�g�f�iB弹��V��`�w�C��^��
R彌��l�e�d�3�2��u�a��T��5��,B彩�{��UB彝�T�2�+�u�\�A�j���B彣�R
往�*�Z��b��_�K��(�e�!� �;�P�hB\徐�7��n��F�<���'�&�s�r����E�H�d��R�.��T�:��)���!��.�e�n�=�B�+�*�E�V�|��C�:�!��@��l�
 �=�8�O�X�1�y��9�p�_�0�o�\�i�8�4�Q�P���3�&�g�A�l�c�q�U�3�z��v�v�G�N�)B徑�E�I�b��B
徖��U��V�e(R
恧��w�?�T�a�$�	����}�cB
恳�_�_��Z�D�K��G�
�"�R�5�h�SB
恺�_�E
	B恷��TR悫���:���k��j��+��0���m�f��{B悻�l��H�
��/��1�dR
惘�o�4�3�2��o�2�:��)�
�`R惦�L��w�0��<��_��'�h�T�#�Z�J�G�<�4��cB惹�"�d�F��-�A
�2�q�� �Z�H�a��*%�}�y�i��$��S�W�B�5�C��*�k�:�B9惻�9�D��8�;	�d�(�
�t�K�q�L�	�
�>��^�[�I�y�R��\�K��g�6�@���^�+�B�y�0�`��f�[��)�O�:���6�7�5	��]�Q	�oB愇�] �c6
�Z&�SR懵��v�[��0���[�Z�=�}B戀�!R戆���y��8���z��E�?�d�m�d�Y���A��/�d�9��J���|R	戟�|������MR截�)��F����S��R�9�8�A�YjB户�<�A�@�"�G�j�V��]�`�'�1�D�p�$�yDB戽�t�DB扂�R扦�!�"��r��3�L�5�l�g�g�P�B	扳�m�J�N��{�@�r�;��	B找�sB扽�6R抑�]�G�����;�e� �j����G�jB抠�f�*��	�Q�2 �z�U�L�&�.�+�PB抟�<B抣�IR拂�����S�_��>h�i�J�<��U�"�9��h��a��`��)�z�a����A�~��6�F��B	拟�E�w�O�����m�z�k�R括���L�I����?��>��C�*���O�sB拼�S�`�{�C�A�W�^��q�f�&�B挈�NB挀�oR挚�>�����I�Q���G�\�Y�Y�B$挨�/��	�D�x��[�
�D�.� �I�#��T�i�2�@�m��5�r�Z��)�q�:�2��x��f�6�u�D�D�eB挾�Q�i�k��H�P�k�gB挩��D	�TR掇��&�9�r�R��%��$�_�W���:��kB掖�;����=R掠�z����o�g��T��I��H��I�"��+��6�y�r�wB掳�W�hR	掷�?�o����+��*��~�m�lBG揄�W�A��4�Y���0�$�c�u����^�E�,�U�V�@�9�R�R�y��a�h�
�b��5�Q���S���y��<�A�g���q���B�.��b�2��P�:�G�)� ��d	�O�L�:�p�;�gB揀�
�b�O�^�	�K�H�Z�
�C��_�|�i�L�.
� ��g�1B揅�W

�O	
�A		

�K
R撩���*�5��9�~��l��7��6�I�B���-��,B撺�m��C��K�dB撻�`x��A�<�M�4B撹�g�ER擊�g�k�t�������~��-��,�B擗�p�B�@�OR	擞�H��\��
�
�����eB攀�B擬��
�"��d�E�n�q�H�&�Y�E�P��O�B擳�	�ER攒���l�A�@�g�f��y��^�e�d�{B攥�t�+�*��u�
�g|�9�x�E�y����B攢�Y���u�$�g�8�S�HB攭�#	�UR
敕�g�?�J���=��<������}�B敢�M��	� �R	数���j������_�>���y�bB斂��7�a�p�aR
斐�A�/��h�9�8����W��V������5B斟���>��	R
斤�@�s��B������'��&��
�C��:��}�<BP方�Z�J�C�W�W�:�r�o�B�e�h�M�Z��#�'7�!�6�
�P�G�M���n��@�7
��
�7���;�)��K�D�=�N�v+�p�J�g	�<����+�R�K��^�{�C�,�k	�M�+�a�8��d�f������)�~�.��@�"�
�!��3� �o�l�1�HB&於�D�F�i�s
�J

�L	���g
/�R�< B斷�W�J�*�Z�j��"�_�9�x�X��<�A�1�4	��TR�J�AB 斸�s�G
�_	
�@
	
�C;T�E�N	
R朦�,�.�G��h��'�o�K�0��,������[B朴�r��V�6�B��V�'�{�\�9��'�8��K��q�`�S�nB杈�T

B杁�c
R杭�s����I�,�<�{�m�B�?�>�7B松�>��A�Z��*�8B杼�b�JB杽��BR
枕�����e�t�n��/��.��� �pR枢�w�b��Z���{�v��G�m��2�w�v��c�zB	架�O�L�?
��d��r�.DB枳�m
�H	B枴��KR柘�e���W�F�9��F��1��0�B查�0�^�\�P�o�
�>B柩�g	"�B
B柲�AR栅�E����y���o�r�L�;�!��Z������kB	栖�{�7	��t��A��TB栝�B栿�pR桀��s�6�,��m��b��]��V��+��*�s�r��5�}��0��Q�<�&R
桢�{�K�L�w�|����h�eBH桶�a�J�=�p��
��q�6�=�,����L�3��@�i�#�j�-� �L
�c�3�{�A�J�H�6�@��|�V�
 �T�h�!�T�+�$�E��
�!�k���,�X�
�F!��L�P�,�9�]�^�U��#�^��N�T�$'��s�H�G�r�~�'
�N�M��H�r��%BK桴��K!	�L
 	�D	
 �H
%�F+		�E!

�h�E
	
�Q�x�:�IB!條�u�y�R�#�F�b�i���7�x� �\�3�^%�m�M��Kp�e�Z���x�Q�G�k�8�q�!
�*�*��G�57�N�H�W�|2#�RmB梘�j�v�x���g
�]!4�M�
�_%+"'*!"�V			B檯�B/桵�	�J$�L�D,F�`.	
�J
#.�S.H�A*�Z&�nH
�qN�sR �H
R	歟�K���75�!��\�[B	歪���
�A�w��^��g�G�C��2R殃��
�&��c�6�*��%�G�E��.�'B
殖�2�q�[�X�W�A�<�v�q�/�/B殘�\�V�S�N�O�R��
�"�i�F�9�^B殗�6�I
R
毒�}�b�}���0��?��>��SB毡�R��9�(��GB毿�@�H�'B毴�m	R氌�A�K�@��/��k����)�X�
�a��"B氛�rR氟���V�E��T�O�@��S��E�J��4���u�F�j��@�C�B��I�vB永�%��B�;�2�y��y��L�1�!�X�1�{�j��X�/�"�K�5�|��GB汊�"	B汈�
	
R
沀�3��/�h�Y����!�����B沌�'�K�D�r�s�u��&��3R沟���j��	����E�K�#�$�E��N��3�e�(��l��/�OB河�
�;��4�3�5�t�)�D�{�#"��F�5"�t�_�V�1�v�gB	沱�;
�]
B沺�N�PR	泮�:��l��h����
��{�fB泻��G�&�C�	�T���+�3��G�I�@�5��_�+B泺�8
�E



B	泹�j�@


R浀���%��$��#��"��I�g �h��"��c�w��X���'�,����/��K�JB浙��)�N�M�q�p�5�j�]��h��E�U�)����M�$�+B浹�\�MB浛��f0R涛�!��B��	�&�5�4�k��H��]�E�� ��o�-�^���B涮�j�{�&�;L�*�@�'�6�f�i�d�q�n�?�	�X�|
�)�^�J�I�5�DB淪�^B淗�bR深�'����_��^�[�:�C��`��y�:�)�@B2清�b��z�o�Z�U�X�1�6�m�T�%��g�l��t�I�2�A�k�:�;�6��H�.�X��n�q�:���-�+�C�g�/�r�]������3��OB渦��s�x�_�r�e�f��B渋��U�lDR	溯�R��.��e��v��Q��P��k�B溺�J�F�S�@B溻��D�8�HB溼�o�CR
滋�)�&�t�o�{��h��q��p����
��$��sB滚�OR滞�L�T�3�4����������I�LB滴�/�:�H�b�$��s�\�

�w�rB
滬�K��]�9�.�S�A���a�4B滳�	�L ,R漩��[�$�{��b��?��>��3�@�#B'漶��Y�L�X�-
�]�B��L�k�j�g��G�O)�j�S
���d�� �F��	����a�]B'漸��A�T���D
�%
�6�k�z�@�+�Q�:�O���z�	�5�O�b���
�J�+�l�Y����z���F�*�� �!B漷�1�H
�I
"*	�`��JR	瀘�Y�R��9��4�a�`�3B瀣�(�R�c�d�Q�R�u��~��|�}�^���@�b�s�{�;��O�f��
�	�B瀧�X*�>�C�g�D�6"�E�w�4��M�c�B瀱�R
�P$�UR	炫�S�}�x�o�a��r��#��"��B炸�I��t�o�C�H��D�m�}�@�d�yB炷�R�D	�B為�*�NR烤�K��t��m�z��=����Q�#�(��H��eB"烷��]�M�K�V�
�J�e�$�&	��s�U�i�j�.�c�l�v�u�F�8�"Z�?���u�V�
��p�P�F�W�#�Z��B6烴�z�[�G,�h
��S���h	�s�8�'��L�k	
�W��\�g�(�/�(�B� �=	�<�_��X�e�}�j�i�f��c��:�N�#�
�
�'
��FB烸�O�[*�sN�Y&*
�@)6�\0R爵�#�a��+�p�Z�?����+��B片�K�5�*�BR
牖�k��8��5��>��K��J��
����m��l��i�?B)牦�E�C�2��#�+��B�E��l���u�J�5�4�A�A�G��o�j��'�,���Q�^�<���BB牽�L�X�f�_�4�X�B	牨�P�c(.�YR	独��\�_�t�C�q�4B#狷�~�SG�D�
�U�N��|�e��~�s�.��@���U�Z���j�M�6��B狹�6�s�x�n�B�E�-�+B猀�(�ER獨�R�0�+��C��n��-���R獵�+��9�7�n�%�B�0�.��7��6B/玄�I�1�H�a�b�S�@��	�;�>��l�3�l��D�n	
�a�"�.�/�0��x�}��P�}�N�@�\
�	��3B玀��=�3B玈�y.�O$�DR琥�2��l��k��j��u��t��i��h��m��l��qB	琳��n�1u�J�j�@�*��{	�DR瑕�<������������ ��.B瑰�>�P�L�oB瑣�]��w�W	�H�6�7	
�&�/�	��"�L�g�`�#�,B瑿�#�B
�uRR
瓚�d�������o��n��o��n��9�5�H��"��9B瓮�\�L�M�%�p�D�{�x���d��(�!�x�yB甌�%B瓵�x�X$
R用�(���~��u�$���`��Q�e�V�I�0��r��+��*��W�S��(��O�mB
畅�N�R��m�'�@�A���}	�s�~�M�\B甾��A�D�"���!�d�e�W�C�V��B甿�"�M%.R疏���,��
���l��m��l��m�'����c�(R	疝�d��n��U�`�����o��FB	疫�f�i�$�x�y��C�[�9B疬�eB疭�cR	痂�n������	�o�@B痍�pR
痒� ��"��O�s�8����s��Z��B痞�ER
痢�j���
��	����Q�BB痰���{�G�LB痱�x
�@�b�o
����	B瘄�
R瘞�2�E�n��!��"��5���l�\��M�5�R��,��#B瘴�o�C�S�5B瘰��J�[�@�<�5�D�A�,�1�vB瘽�$�CR癢��o�>�K�d���	���K�B癸�H�A�
�C���~�
�
�M�V�9�@
�Y�B���O�TOB癯��.��"	��K�y�h	�i
�,�l�MB癿�C�C 0�MR盍����?�I���`��g��f��o�A�TB盛�9R
盞�n�=��Z���w�Z�M��f�{B9目�"��l�_��G���i�B�8�@�1��0
�/�G�t�=�q�(���E�M��5�r�C�:�� �)���_�^���w�C�f�X��x�@��/�2���)�lB睏��+�y�uB盰�	
�@	

�F!,�yLR	瞥�R��P��q��p�����$��-�>B瞳�\�h�Q�
	�c�B瞰�u�r�s��G��[�-B瞴�	
R矢�O�����y��x������A��@��)��!�+�hB#石�F	��y�f�A�"�,	�)�+�p�~��w�z�?t�x�d�c�A��8�
�9�l
�O�6��a��L�C��I�J���3B矶�"�G
�F�l�Y�Z	�O��W�qB矷�.�[�D �^*	R碟�[��h��������������=��<�B1碰�7�-�Z�=	�8�B�
���5�D�{�l�Q�	�F�X8��s��B�'
�l��-�p�d�
�;	�/�\�G�~��*
�	)�f�"���/�A�T�y�p�?�O��P��8'�BB6碭�d�=�>�?�^��L�A
�8��?�,�-�4�G�9���,�+�(��+�M�v�+�7�D	
�J�<�j�)�2�3�&���G�]�a�$B碻���@2D�L$:�G
�uN	
�BR秣��O��X��!�����?��>����������/�OB秸�&�L�1�B�0
�;�6�.�y�<�m�B稂�$
	�R
�
�+B秼��s�D
R	稷�(��h��m��l���Zy����B穆�%�t�`�G�x��mB
穀�w�'�w���*�I�~�B�#B穪��@
R	窀��a��@��y�x��~��c��b��cB	窍�L�<�D�Y�_�p���iB窕�
B窞��\R
窥��0����}�x5�6���B窿�G�K�9
�J��m�t�i�`�a�M	�J�Q�F�g� ��}�|B窳�%�&��I�
�x�,�1�}��N�u�I�JB竆��t$	
�F	
R笤�t�7��.������	�_��l��B笺�V�^�L�;�*B笱�q�F��+B笲��5�H
R筏�A�v�~�5��F��)��(��E��D��#��"��#���2��B筷�w�G�"�A���I�<�	�G�$�1�@B筠�~�D�K�I�t�		�G�V�CB筡��N�L	
R	範�o��<��	���
�W�V�Q�+B#篓�O�o�u�\�:�O�/��3�M�6%�t�0�U�D�K�m�f�v��t��h�4�E��S�m�h�K�M
�/�x�s�l��EB6篑���%�s���A�
��N�K���^�8����Z�g�A�s�&�U�6�i�0�]�I�6�z	�3�T
�N	�4�s�N�B篒��&
�N�_((>�h:
 �@R
糯�u�J�p�C�B�y�x�E�D��a�f�x��mB紊�XB
糾�H�A��B�#�	�&�;�4�*B糿��[�AR
紓�#�����'��&��/�9�s�PB素�M<�O�L�M�~�rB'紡�s�z�h�
��@�+R��t�o�j�y�p�v��>�te�D�3�Y�P�G�=�v�+��-�l�x�Q�T�!B紻��w�E	"�G R
綫�E�b�K�Q��6��C�d��dB/綸�_�0
�}�1�(�Y�D�^
�	�v�?��*�i�d��-3�
�y�n�u��^��X	�9�<	�N�a�n;H	�{�n�
�)�W��]B緻�&B綼��J�V
�a2R縱�;�(����L�K���
���5��4��9���RB繁�NB!繅�R�M
�R��g�z�3��5��v�'��U�V�A�$�C��g��J�/�j��m�>�]�$B	繀�� �IR	纟�S�I�H�c�b�;�:�/�-R纪�@�X�(�,��v��'�.�[�3�.��R��9�s���O�iB绊�xB纽�g�H�C��
�;�v�JB纾�[�JR绍��@�5�
�I�t��`���n��k�S��2�.�/�O��R绢��$��R��}��I�P��L�L��b��1B绰�E�b��-�w�V�B�/�]�@�F�u��{�#�B绶�o�N	B绹��\�PR缓�O�!�Z�I�f��
�	�
�m��*��=�B缨��E�Q�Q�n���r�G�$
�Q�b�h�m�8�v���E�H��(��N�G��|�J�I�BB罌�b�nB
缷��`�I	�@R
羅�b��>�?�>��o��n��5��4��B羔�\�&�r�E�g�p�+�6�C�u�+�\�A��)���?	�~�R��C��q�c�l��@B羝�D�X�Y��	�9�`�J�
����r��J�s�xB
羠��!
�A
"
��GR
耐�0��h��5��4��5�-�,�%��WB耜�N�}�~�;�<�G�v�e�}��n�B�6��l�T�VB耬�BB耡��l	R	聒�\��N��#��"��;��:��_��^��{B聞�
�y�R聯��o��N���+�O�X���/B聽�y��5�@R
肃�U��^�?���L��G��F��/�mB肓�qR肖�o��t������K��8�O��D��{�I�,�����9UR肩�Z��4��t��u�g�.��*��{�'�� ��wB	肺�k��L�@�Q�c�m�}�[�6B肷�y�DB肻��8�IR	胖��V�����C�:���B胡�1�v�2��R胫��Y�\����/�2�n�K�@��������W��V�������B脂�+�L�_�r�cR
脏�#�5�%�.�O�@��"��G��F�������`B脯�'�4�
�B脞�
	�NB脰��oR腆�F��p��S��R��K�F�.��R��=��<��1��4��W�A�V�@B腠��VR
腥���e��V��a��`��s��r��_���x��Y�lB腹�4�"�}�H��@�y	�v�B�
��#�
�?
��L��w�.s���1
�`�#�C�jB腴��:�I�#�<�<�y�P�_�S�X�|�4�A�A�s�^�e�2��5�*�S��9�R�
�n�yB
膁��

�N
R	舃��f��}�]��l�E���:B舒�}��$�_��DB舐�bB舑��j	R舨�0����	�x��~�����y�/��:B舵�%�]���M�P�#��U��D�R�!��
�W�V�B�q�vB舸�2�E	�L�M�d�/�7�d�)�V�^B舺��{�H&�JR芈�x��d��
���&��;�0	����	�B	芗�j�{�j����o��n��uR芤���4��J��%$!�{�?�H�Y�J�e��v��A�@R	芷�t�+��F��5��4��+�
��B苀��?��
��f��&��'R苍��F�k��t����)�4��b��5�:R
苛�N�p����	��%����1"�k�E�B
苫�t���T�����$��y��x��R
茀��\��{�W�C�t�A�B$/B茎�T�m�/B茌�
./<3B茍��_R茫�(�u�r����%��$��-��,���K���
���T����7B荆�R�]��!��/B荀�%B荈��~

R荝��
��W�%��j��/�q�^�7�8�s�nB药�0�a�h�G	��!��b>K8

�Y�L�-�*B莊�+�Y��jB莗��&R	莵��4��m�q�t�i�p��\��]�7B3菀�^-(��ZH�Q�(
�s�x�Y�j/�7�X�k�
�X�C�k�`���B�4	��6�}��<?0�C�H�B
�Y�Z�s�l�i�B	華�M�W�L�!�2�z�S�}�t�iB菆��;	�a(
�f	R葩�n��d��c�f���\������!�� ��}��|��;�L�d�o��t��{BC蒂�:�^�U�^�g��L
�O�O��W�R	�A�w�
���"��T�g�
��.�L�!�%�>�}��m	�n�T
�w��/��E�<
���	
���)B蒓�M'�Q�C�H
�g"�
�8
�V�d	�=�c�.�;�S�L%'2!	(�9�bH	�B蒊��2
*�M
�S 
�]"R	薄�
�*��b��o�D��(��?��>��IB.薛�O��	�Y��/���`�K�@�U�G&�*�U�I�B�.��
��o�p�M�t��A�9���|�#�(�&��[��	�B�s��4�@�8�{	�d�:�;�q�A�<�B薏�
�G"�EBM薑��M
�^�s�j�n�R���c�"	��:��`�I�-� �1�<	?
�}�2�e�M�c�F�f��+�t�m�K
			�I	

	
�|�H��z�{B薐��W�G
 0�rD�_*�f*�Y�AR
蜒�z�0��p��%��$��_���J��S��R��_��^��]B蜡��e�l�+�r�3W�-�V�0�q�s�~�z�I���y�]���R��)��'�&�I�B[蜢�.

�H�l�g
�j�{�T�[�E0�*�3�L�r�O
�*�%�B�T�u�8��(�%���.�-�
�%���s�F� �X�{���(�)�x
�_�9���2�3�G��X�p�#�0�!�EB蜤��S�h6�L!.�H
"�D
$�PR表�V� ��x��W�!��v��?��>������B衷�S�I�I�I�f�8�b�@�=�j�G�l�y�O�+��d�x�E�z�%
�I�2�Q�1�k�HS
�@��|�fB$衹��G�(�)�B�&�)�I���2��,�&�U�#
�^�]�H�x�o
	
B衸��,�G"�S

�QR	褰�9��T���2�w��H��G��F��QB襄�R���A�T�	B褻�]�E�5�r�'�"�s�l�w��F�Y�Z�Q�G2�l�I�"�b��1�(�[T�zB襀��-$6�TR
觀�-��u��,���<����B角�4��Y�(B觖�W�;��m�H��)�B觍��
R
訇�~�p��p����
��!�� ������'B詈�B訓�j�n�/�'�-��E�{�"�g�X�z��$�K�I�B�	�u��Y�X[�?�.�\�?B訔��6	�@
R
詩�,������w�v��t���?��n��L��}B詹�S�O�a�i�)B
詼�p�D����6���B誈��{
R語�=����
�f��$��K
��O��p��UB課�\�O_B説�G�v�S�E�N�B誱���QR談�i��l��G��F��o��n��]��\��Q��P��Q�+B論�`�3�.B諗�/ �o�^B諝��+R
諶��}�0��8����~��g��f��!�� B謇�<B謀��l�]M�L��T�B謃��>
R
謔�����u�5��(��s�|�[��N��kB警�^�RB謡��O�Z��
a�S�z�J���W�P�I�H�[�I�&����j�B�[	��"�]��{�:B謧��S �W&�xTR
讜���6��'��&��)�=�#��d�%R讨�)��v�}�����"�#�K��d��U�R
讶�q�V�K��&��V��U�u�,�B诀�&�xR评�_�H�o��B��K�m�v���#�(����}B诚�t� �m�n��B试�kSB诖�&�q�t�#�,�x�+R诪��1��k�\�P�4�[�Z�m��l�4�X���
�{�m�f��b�2R谀�6�)�(�?�>�[��@�]��L���;�J�9��(�{B"谓�L���a�`�Q�R�}�J�Q��A�@��:�6��C�t�!�I�@��6���;�`�h��J�N��\B豇�v�"�#�
�Q��~�I	�L��g�g��*�+�k�f�i�`B谞��3�a�
jR貯�"���8��3�� �����N���B��=�k�|�l��:��!R賀�E�H�?9�+�*��6��+�q����QB賑�2�/�����AR賚�3��J��u��t������O�t�i��;����%�VB	質�3�I�<�j��@�z�F-B賫�5�HB賿��*R	贈�f����-�P��Z��;��:���B贖�=R贛����Z�-����g�{�J�s�'��E�|�k�\�]���N��y�\R贴�L��B�a�r��@�>�q�p�O�+B赁�'n�n�S�W�z�w�mB赃�"�B赀�1R赓�;�1�(�1��>��W�F�a�e�b
�E��>����*����x��O�0B 赫��^�#��W��I�O/�\��R�?��	�p�D�q�O�|���5�|�S�$�X�?��O�v��J�O�BB"赭�u
�P�D�J��w��9�D/ �G1,�F�EB赬��F�S8L�G

�NR	踩��L��
��������
��B蹄�6�;�8�B踴�0�i�B���p�M	�V�WB蹃��HR蹬�&�w��n��7��<������9��8��AB蹺���T��G�U�@����R躅�J��L��'��&��m�a�j��`��M��L��S�l
�oB身�&�G�h�-�rB7躚��g�j�E�H�U�/	�k�V�c�>�=�N�V��"�k�n�D�M�P��~�%�8��p�
=�@�E�@�W�^�K�i�Ps�B�4�?�"�6�sY��bB
躛��z�uH)<�D
�o:R轟�F��t���(��X��e��d��S�L�y�|��~��G�5�4�K��[B	轴�`����#�"��@��JR
较� ���F�R�-��7�F��&R辐����L���p���Z�:�o�n�W�o��d��}�B辣��&��:R	辪����}��|��g�%�Q�@�LB辶�=�k��A��LR	迀����k�.��:�����t��
�
B迎��v��w�v�h�u�^���oB迓�>B迏��"R迢�I��>��O��J��I�'�l�����F��T��?B	迷�Y�W�N�z��B�1�u�C��;B迳�E��PB迴�{R选�J*�P��\��Y��X��!�
�6���v����K�)����{�K�9��|��A�M�.�@�<��"B逦�J�!� �]�=�/�.�B�W��b�q��@�D�k�T�B進�;�X�.�+�1�NB逷��I�@
	R遛�Z�"��X��7��6��!�� ���U��X��eB遨�W�6��*R遲���@�����.��/�$����1��0�������IB
邀�$��+�h�6�%����M�KR邏�c��)�� ��	����5��4��������������B邡�l�%�a�cR邪�{����'��&��!�� �	��"��%�3�4��"��!�m�h�B!邾�u�B�9�:�W�L�|�)�,��n��a�	�p�[�Z�A�L�f�
�C���!�"B郟��W�P�X�3�h�>��-B
邿���@

�C
R
鄭������J�O�H��>��]��\��[B鄹��I�-��Z�w�a���>�'�=�VB鄺��M
�SB酇��Y	R酩����T��3��2���J�u�|��J��K�O�<��N�^B酿�SR	醅���T��i��h��U��T��7�XB醚�V�ZB醒�B
醐�	��

��z�Q�#R釀��x��z��9��8��=��<������A�+��5�x�A�_��"��3B釜�%��
BD釕�x�_�Z�m�w�j��	�c�E�t	�_�g��:��V�#�	�j&	�7�2#�5�<�@�'�*��l�u�3�&��w�h�A�f���@�!�(�o�w�\�x	�9�D�g�@B釖��
�V
�f*"0�J

R銣�5��V��)�0��v��]"+����EB銱�%�1��:��#��"��u�R	鋀��0��C��B��]��\��7��6��o�uB 鋒��_�`�
�m�k�S�.�-^��5��b�
�Lf
�{�B�h	�y�O
�$�%�%�.��J�G���]�.��bB&鋌�)
:C,;6�J	

E0	�>�m�G$�%�.	 	P�EIB鋞��E
�H
�B	
,�CR鎦�cy��B��7R��b��_��^������QB针��S�|B>鎳� �	�C��7�2�[�nD�y�(�D��e�

�I�+ ��x	�9�H1��>�i�`�F�;�f�D�1�*'�'�P�Q�P�U�V�M��O�,�%�4�8�}�J�B鎴��O�T�X("*�Q

R	钑��a��)�m�~��������EB钝���|�S��dR
钥��a�Q�z�9�&�wB$钱�+�}�0�y�A�H�G�5�t�h�c�V�m�2�E�a��Y�d�
�|�x�#�]�A�R�u��b�6�U��}�L��#�|B钲�%
�D

�BB铇��d
�`R
锠��l��A�6�C��.���L��s�lB锭�f��B�~�Z�g�D�z��s�y�;
�B�b	�'d�0�
B锬�lBM�E�>�H���Y�XB
锳��n	�E	
�RR閉�E��~��[�V����	�}���������vB閘�i�MKg��(�x�o�v�1�.	�C�'�D�3�4�y�&�P�I�&B閤�y�aHB関���L
R
门�J�t����h��{�4�G�,�r���cR	闷�K�n��k��l�!� ��^B阀�D�R�4�z�'�{�F!�I�B�����u�~�<�.�Y�8�(�YB阆�B阇��= R陀�����s��r��;��L3�I�h��r����B限�K�"�E�<R
陛�F��2����������W�
��EB陧�_�'��=����R	陰������8�9�_�h�B�B陽�R隅�L�	��X��%��$��;���8��'��/�RR	隔�o�D��P��-�W�P��0���B隧�b�h��^��R隰�f�p��Z���p�� ��A��@��u�9��,��WB6难�4�A�<�p�M���y�;�*��b�x�{�M�U�o�`�A�l�!�Z�'�k�n�A�z���}�4�X�D�m
�7�:�N�&�,�1	�>��g�T�6�y
���o�x�V��
���,�B5雉��>�0%�g�
�1���c�X�]�@�E�=���B�C�C�>�9
�%�T
��w�H	�2�/�
���Ehg�%�!�{�H �;B隿���H�E
�C$�V$$�V"

R頀��c��5��>��t��5�	�|�S��������/��.B預�A�o�7�X�D��2�#���j��H�O�0�%�(�(���=�qB
頜�I�VB頥��t �](R顯�>�*�!��F��3�9�s�-��p��g��`��i�R颀�a�e�"�@�w�'?�Z���7��@R颎��*��	�9�m��,��g�\����-�	�D��nB颜�~�#�[�{�|�f�}	��O�F�p�m��B7風��.A�U�G�/�.�/�r�L�D�I��j�a�X��/�+�|�D��5��A�9��v��g�&�X��P�+��d�S�b�C�g������v�w��!B颣��.
�R
�D1D�FR饭�V�V��^�/�Z��P���S�F�
B饺������~����i�@�b�WR馄�(�C�#�d�;��>���[��^������eB馒���~��S�B
馥�+��(���}�F�L�%��B�{�n�B馲��A�MR駘�	��4��]��\��9��8��=��<��i�8B騁�B 駭�?�N
�!�E�n��
�7�p@�I�(�s��_�A�V��?�0�c�(�w��U��B騀��}+F�N
R
驪���t��g�(�I�(�5�r�����R�&�QB驹��r�y�w�p�_�ZR骂���f��u�d�k�_�H����
��4B骏�2�$�o�r�y�x�S�(�Y�:�R�enB骯�)�-�CB骙��OR
髏�a�������,�\����!B鬼�FB髟�l��
	�G�{�.�1�
��R�S�`�a�.�
�j	�[�D�=�$B	髠��q
�LR
魀��6��Y��B�w�r��,��+B魏�I�B3魍�e�~��z�W�_�(�I
�]�V�_�Q�x
�W�R	��$�H��38B魐��;
�fD(�[,�RR鰱�26��(��#��"��i��h��o��n��+B	鱼�H�D�]�
�D�9�	�J�z�}�BR鰾�T�F���W�,Aja
�Q�B
$�sB�(�F�I�/�
�j�O�)�Y��	��4
�U�B�^�{��i�`�C�B�E�T�&	
�%��6��C	�g�j�i�8.
56'&���QEB鰿��n�S,�D		�{4�R 
�j�\0�g>"R鸚�!����Q�u�@�9�8�q��t���T��*R鸭�e��*��M��L���%��$��u�tB2鸽�f�p�C�p�[�H�7�O�v�U�S|�H�k�8�_�m��0�7�u�U��/�&�Z�+�5�T�2.��D�T� 
�w���^��u�9�p�>��0��R�,��90
�@��G�y��p�5BY鸾�C�E	��	�P*�G�_�N�=�C�J���Z�
�<�Q�@�E�S�X�a�,�s�N�S�6>C�)��E�D�I�G�t�I	�J�^�H�N�#���E��;�G�H�H�U��v�#��3�>��
�.�� �qB秊��SB,鹀��@�b,(�P�O$�S
�t  ��p�D������_�C𠂇�� ��|T� ��yG��w��.2` ^a
ˊ�3��
��%�@�4�6��Z�a	��@�b‘mo�#�l�z�d�:���{���p	�f
�R�@�^�>��d`s}q{�i>�d�@�#
�7�@6�V�i	�p��W�:�
�@�E��(b�=�P��}�@�P�X�D�T�F�R�J�B�Lb─?�b�@�"��y$�<b辨�Ob摸�b舌�b萤�b至�@b劬���,��[�v�����2��6��Z�M�g����L��#�'�*�-�g�<�>�E�J�L�N�Pb�>呒���
�+�h� �(��7��I��\�M�o�e�m��
�l�x�Z���1�h�D�7�#��H�L�1�\�N�v�-�j�u�
�j�(��v�l�x�}��f�(��?�h�V�Z�]�|�^�
�(�
�1�Z�V�~�r�q�	��6�G�w�r���D�C&�H�	�g�R�\��e�
�s�v�&�+�-�Z�8��|���I�f�!��}���R�N�V��W�V�\�`�f�j�l�p�E�s�v�x�{��	�
�P�`�)�G��t�Q��z�b�
�
�!��C�&�+�4�|��
�/�K�;�>� �,�Q�2�4��%�!�*�H�@�B��c�f�j�F�n
�s��%�O�Z�T�{�z-��c�	��U���	��
�#�'�,�J�0�7�:�@�D�F�K�M�R�H�W�Y�[�^�e
�i�m�p�t�D�x�}����
����	�L�
����!�z�S�J��N�_�d�h�j�l�n�j�7�;���c�,�.�!�;�G�@�F�J�M�O�g�]�x�������%�'�/�F�3�7�>�A�D�
�7�A�F�D�G�K
�Q	�V�Z�}4�y�\��V���F�ob誑�yb飪��\�)b紇���z�b
癆�/%�4��o���r�s���Y�O��h��A�f�F��Lb鯪�;b�Iⅰ	�)�\�<��D�h#�G�M�k�z&�}�d����C�x�L��a���q�$�)��&�O���r���o��V��X���a�q��f�j�Y�_�e�P�l�u�w�{�}	��B������"�&�P�+	�.	�3�6�;�G�A�H�J�O
�S�U�^�@�`�c�e�n�r�t�v�y�E�~��
��
���A�#�%�)�,
�4�7�;�@��C�D�I�O�S�V

�Z�F�h�k�n�q�v�{��	���C�����(�2�4�9�;�B�>�@�B�E�K�N�Q�T�Z�]�b�f�L�n�t�v�}�����O��� �%�'�-�C�/�1�3�5�9�=�B�G	�J�M�A�O�W�a�c�e�Y�q�t�v�x�|�~�	��	�O�
�������"�%�(�G�1�5�8	�@�D	�G�F�S�X�]�b
�e�p�s�u�C�x�}���	������M� 
�$�+
�0�A�4�7�:�A�C
�I�L�Q�U�E�Y�]�`�d�k�X�s	�x�}�I���
����!�G�&�-�0�9�<�O�A�G�J�L�O�H�X�\�a�c�i�n�p
�u�x�K����	������J� �"�&�(�+�.�5�9�O�A�F�H�K�S�U�X�E�^�c�e�j�m�p�s�x�C	�|���
�
���A��"�'�+�.�7�:�?�C�G�C�J�L�O�S�U�W�[�b�d�f�i�C�k�p�t�w�z�|�	����E������#�&�(�+�@�.�0�4�9�@�H�L�O�@�V�X�_�a�d�f�i�k�t�x�z�|�C�����
�����	��@�'�+�3�5�:�>�C�K�N�H�U�X�_�f�n�r�v�G�������� �"�$�@�&�)�+�1�5�8�:�A�C�N�J�M�P�S�Y�[�`�f�i�K�n�p�t�w�z�~�����B������%�'�*�7�K�<�>�C�G�J�L�O�R�[�`�B�c�g	�i�s�w�~�����
�A��#
�*�5�7�?�A�F�G�M�O�V�Y�]�_�b�g�j�o�A�s�{������A
�$�/�5�7�9�>�A�P�G�I�O�R�V�Y�C�\�_�c�g�u�z�C�}�
��
����L�����"�%�+�0�7�D�<�A�D�F	�I�M�S�Y�E�_�a�g�m�s�z�~�����@�
������!�(�*�/�C�8�?�A�D�K
�N�Z�\
�`�E�m�o����
��@��#�)�.�5�7�:�<�>�L�G�J�L
�O�V�Z�]�_�a�F�d�j�r�t�w�~�O��	����� �B�%�.�4�6�:�=�C�J�L�G�O�S
�Z�\�_�d	�j�n�A�q�w�~���	���M���� �&�(�0�2�@�4�7�9�<�?�B�D�F�H�K�M�P�R��L�U�X�Z�]�`�b�e�h�m�r�E�u�}�����
���J�����#�(�*�-�3�5�@�8�=�@�E�H
�M�S�U�B�Y�^�a�f�j�l�o�r�z�|�B����������"�@�*�,�3�7�<�>�D�H�K�N�B�U�W�[�`�i�l�o�x�L�}�	����G����"�$�+�.�0�2�4�E�7�:�>�F�J�L�N�R�U�G�Z�a�c�f�k�G�p�t�x�z�~����
��C�����&�+�-�0�2�E�7�<�@�L�N�J�T�X�Z�^�a�d�i�n�p�v�F�{�}���������E�"�%�,�/�3�;�?�B�E�@�I�M�V�X�\�_�_�k�q�t	�{����G�	�
�����$�*�B�,�1�3�5�8
�>�A�C�G	�M�@�T�V�Y�\
�d�g
�m�t�v�G�z����
�����D���$�'�*�.�3�9�;�H�C�J�L�N�P�T�X�]�a�c�B�g�j�l�n�r�v�{�������B���'�*�,�1�3�5�9�;�E�>�@�E�G�K�P�Y�[�_�a�D�d�g�k�m�s�v�x�}�F���	�
����E	�(�2�8�:�=�?�B�D�K�S�U�Z�]�d�k�n�Z	�v��
�G��� �#�(�*�-�2	�5�C�@�H�L�O�S�U�X�_�b�f�B�j�m�t����
���C���!�)�+�-�0�2�5�8�:�>�@�J�F�I�O�Q	�S�_�a�c�e�h�C�l�u�x�~�����F���!�)�,�2�7�<�?�A�C�D�H�J�L�P�X�\
�^�i�p�r�A�u�x�|����	�
������@�"�%�4�;�D�J�N�P�@�T�X�]�a�c�f�n	�p�z��C����
�
�	���$�)�N�0�4�8�:�<�>�C�E�H�J�D�N�P�V
�Z�_�g�j�n�q�C�u�z���
��C����� �"�%�-�/�M�6�8�:�<�A�H�J�H�O�S�W�Z�\�_�c�f�T�m�o�q�u�x�}����
�
�\���!�#�%�(�,�.�0�@�3�5�8�;�>�F�H�J�L�N�B�T�W�Z�^�c�g�j�n�s�@�v�{���������B���%�+�-
�/�:
�<�B�J�N�P�U�[�_�a�d�f	�m�p�H�u
�|�~����
��D������#�)�/�2�D�8�<�@�C�E�H�K�P�X�\�B�^�b�d�l�p�s�u�x�|����D�	��������%�'��F�2�7�:�>�A	�F�H�K�S�U�X�B�]�`�g�j�m�o�s�w�y�}��E��
�
����	�'�+�D�-�2�6�?�B�I�M�P�S�@�U�^�c�g�m�q�y�}�@������"�%�(�-�/�@�1�5�:�>�A�C�H�O�R�X�[�F�b�f�k�o�r�t�v�z����D���	�	�%�1�4�K�7�>�A�E
�M�Q�U�W�Y�\�E�^�`�g�i�l�t�v�z���E�	�����"	�$�&�D�*�0�3�8�;�=�?�C�E�I�D�L�N�U�X�\�d�n�L�t�w�{�}����
������C� �%�(�/�4�:�=
�A�B�M�Q�S�W�`�f�m�o�t�C�w��������F�%�*
�-�9�=�?�E�N�P�@	�R�\�`�c
�m�t�y�A������� �C�#�'�)�,�0�4�8�:�=�F�A�K�N�P�S�Z	�^�d�f�D�i	�l�n
�u�w
�|�����G������� �"�I�&�)�+�3�7�<�A�E�D�H�J�L�O�Q�S�\�e�h�l�A�p�r�v�z�|
��	��F��
��"�'�+�/�6�D�:�=�D�L�N�R�T�`�D�c�f�l�n�s�y	�~���E�	���� �)�,�/�F�3�9�;�C�H�J�L�O�J�S�W�^�`�e�k�o�v�z�@�}������
���������J��!��$��'��+��.��1��9��<��?��A��D��F��H��J�B��M��P��X��\��a��e��k��n��r��t�H��{����	��	���������B��#��%��)	��+
��5��@��B��G�K��O��Q��U��X��Z��\��b��j��q��t�D��w��y��~��������	��
�����J�� ��%��*��,��1��:��A��E��I�J��P��T��W��Y��]
��c��g��i��l�A��p	��v��������	���������C��	�� ��$��&��*��-��3��6�H��>��E��L��O��S��Y��[��`�C��c��g��m��o��s��w��z��}�������B��	���������� ��"��$��'��*��-�E��3��9��=��C��L��N��P��\�G��a
��i��w��y��|�������@��
������ ��#��&��*��-��/��4�F��8	��>��F��H��N
��Y�P��_��a
��e��h��l��o��r��x�H��}��������������!�D��)��,��1��4��=��@��B
��G�H��K��O��Q��S��Y��[��`��c��e��j��m�@��q��y��}����
��
���Q��	����'��+��.��1��8��:��?�D��B��D��L��Q��S��W�~��b�@��g��j��r��v��{�������D��
��������"��&��,��1�F��5��8��>��B��D��J��M��O��U��[��G��_��c
��g��i��m
��q��u�E��y��{��}��	��	�������D��
��"��(��.��4��6�M��:��?	��G��I��Q�@��S��U��W��Y��^��`��m��p��t�B��y������
�����K������#��*��,��.��1��9�J��@��B��F��O��S��U��Z��_�T��h��l	��t��|�B��������
���������� ��#�C��%��(��.��2��6��:�K��@��B��G
��K��N��R��T��X�J��]��a��d	��f��p��w�A��z��|�����������@��������!��$��'��,��0��6�B��8��<��?��A��G��K��N��Q��U��Z��]�H��b��e��k��n��t��w��{��~�����@������
��������'�@��,��.��4��8��;��=��A��N��Q�J��V��X��]��`��b��i��l
��r�D��~��
��������������'�D��+��.��1��6��:��>��A��C��H��K�B��N	��U��X��Z��`��b��h��k��r�B��x���������������� �A��#��'��*��/��3��6��9��<��A	��D��P�A��R��_��a��g��i��m��q��s�J��z������
�����������A��!��&��(��*��.��0��4��8��<��?�F��D��F��H
��Q��T��W��[��]��_��a��d�B��f��m��p��w��}�������E��������������$��&��+��.��2��7�A��9��?��A��C��J��N��Q��W��Y��\��_�B��b��f��i��p��t��x�������A������������"��(��-�I��2��4��7��;��>��C��I��P��S��X��\�C��`��b��d��g��j��l��o��q��s��{��~�D��������	���������� 	��'��)�B��.��0
��2��@��F��J��N��P��R��Z�E��_��a��e��h��k��n��v��z��~���������V��
����������!��&�D��-
��4��8��<��@��D��F��I��L��O�D��T��Y��a��c��e��g��i��k
��p��s��u�A��w��|��~�����������������A������%��(��,��1��5��8�R��@��B��F
��L��O��T��V��[��`�B��e��n��r��z������	�������A������!��%���4�B��=��A��C��F��J��N��T��[��b��f�@��k��m��o��s��z��}�����\����������%�F��+��/	��4��9�v��>��C��G��I��K�B��T��V��Z��_��a��d��i��l��p��x�H��}�����������������@����$��(��*��-��5��7	��?��A��D��F�E��I��K��O��Q��T��]��_��a��g��k�C��n��u��|��~�����������A������!��'��*��.��4��:�H��A��G��O��T��Y��[��_��b��f��h�B��j��m��o��r	��w���	��
�������L����$��'��-��/��1��4��G��6��=��?��E��G��J�J��P��R��W��]��_��b�S��j��m��v��y��{���D����	��������������"��$�C��)��-��/��9��;��?��B��E
��M��O�@��T��W
��[��_��a��c��e��k�C��n	��v��x��{��~����	��
���Z������#
��&��3�B��8��:��=��A��C��K��P��W��Y��[�D��]��a��c��f��i��m��o��q��t��w��|�����B��	������������$��&��-�I��4��7��9	��;��F��K��M��R��U�B��Y��]��e��g��i��m��q��s��u��w��z�F��������
����������!��%��(�B��,��/��1��3��7��>��G��M��P��R�A��X��[��`��c��g	��l��o��}�@������
������������$��(��+�B��.��2��8
��>��I��L��R��U��Z�D��\���f���w��y�E������	�����������G��"��%��)��,��/��5��7��9�{��C��J��M��O�S��W��Y��\��^
��c��f	��j��n�@��p��x		��}���R��������������"��&��*��,��0�D��3��6��9��<��>��D��L��N��P��S��X�C��\��_��b��k��m��o��r��t��v�����@����
������ ��"��$��*��-��5�@��7��:��<��?��E��J��M��U��[�U��h��k��n��r��u��{��}�����@�������������� ��#��&�z��/��6��8��=�@��@��F��K��N��P��S	��Y��\�D��d��h��l��n��r��t��v��x��~������
�D���������� ��&�H��*
��/��:��<��?
��B�B
��N��]	��c��m��p�C��~����������#��(�H��.��2��5��;��=��@��C�c��K��M��Q��W��]�C��`��b��g
��k��r��v��{���@��	������������!��&��+��3�J��8��<��>��B��V��Z��\�D	��f��q��x
��z���B������'��,��0��3��7��:�C��C��K��N��Q��]��`��b��e��l�B��o��t��v������	�����D�� ��"��'$��0�@��2��5��7��9��;��=��?��B��D��K�@��T
��[��f��m��p��s	��y���A��
�������� ��)��+��-�@��:��B��K	��P��Z��]��b��f��l�E
��q��	����
�������@�� ��,��4!��>�U��C
��I��N��R�G��V��X��\��^��d��g��m��r��t��y��}���E�������������������� �I��$��0��5��<��E��G�I��R��V��[��_��c��e��h��j��n��s��w�B��~����������
��5��o�K�a*���@$��J��h����
�������_	�+�5
�9�Gb�3⺂������"	��$��.��0��<��>��A��E��G��J�@	��N(��X����{��~�o���s���H?���@��A*��H��s�@?���@?��?�@?���@?��?�@�� ���@?��>�@
��~
��$���@?��<�@?��|�@?��<�@?��|�@?��<�@?��|�@?��<�@?��|�@?��<�@?��|�@?��<�@��|&���@-��;��i�@?��z�@��:
��I��W�@?��w�@2��7��j�@?��v�@?��6�@?��v�@
��60��D�@?��u�@?��5�@?��u�@-��5��c�@?��t�@��4��T�@?��s�@?��3�@?��s�@?��3�@?��s�@?��3�@?��s�@?��3�@?��s�@?��3�@?��s�@?��3��@?��s�@��3(��I�@?��r�@?��2�@?��r�@��2��Q�@?��q�@?��1�@?��q�@?��1�@?��q�@?��1�@6��q��(�@?��0�@+��p��
�� �@��.!��K�@?��m�@?��-�@?��m�@��-(��C�@?��l�@?��,�@?��l�@?��,�@?��l�@��,��8��L�@?��j�@?��*�@"��j��
���@;��(��d�@��g1��t�@?��&�@?��f�@?��&�@?��f�@?��&�@?��f�@��&1��-��_��a�@��c��f��z��}���@?���@?��[�@?���@?��[�@?���@?��[�@?���@?��[�@?���@?��[�@6����R�@��Z��y�@?���@��U%��h�@?���@-��N��|��J?���@?��H�@?���@?��H�@?���@?��H�@?���@?��H�@?���@?��H�@?���@?��H�@?���@?��H�@?���@?��H�@?���@?��H�@��/���@��EPK
!<�P�A��1chrome/pdfjs/content/web/cmaps/UniGB-UTF8-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�UniGB-UTF8-HB—�V��Z1�6��m�1
�:�3�2�
�[	��b〈�H�R�F�P��p�D�@PK
!<��_c_c2chrome/pdfjs/content/web/cmaps/UniJIS-UCS2-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE����!a ;a!>"eQ�k]J�X��_?V3�q�����
u�� �{�!~�$��*�7��;��A��G�8��XA1�
	
�A��?EA&�<[��NcP]-(
S
	=A'�<a_��b����	��	@�
a	a�v/�s���7�)�#�*�Ka��:�@	�?A
Q�J�0�G�"8��F.�A��5A[�<a �x� y�w��	��Dn�^�Eha p�!�"	�(a <�O�PA ݆%��E�<A ��
\�3�/�?
�x�&A!�O	�P%a!��a�`�ca!S��	��!	��j�oD�va!��)�7�,�*A!҅p+	'c��h�5A!�u�Q'�
A!��.
4!�;4a"'�m�l�k�A
"-��w�~�O�H��2��=
�|a"f�=�{�Y�i�gA"��uI�3"�2A"��N.�A
"��_D�B�Oa$`��K�7a$t��~�04� e�;
�<�>�=�7�:�9�E
�&�4�3�2�1�0�/�.�5a$��c�IA%��YA%��O	�A%��O
U	�9a%���R�Qa%��?�B�A�
�x
�a%��^+�A&@�BA&a�QA&�@a&j���a'v�^"�a&h�B�D���Q;�c�,�a� ����	A	.��q�h�i� N�.:�K�A.��j�M�A.��a.��aA.��TA	.��M�Y����_(�A�KA.����U�4�=�<a.��uA.���,�d�[�5�J��|�O�r�]A.��.�y�.�{�A.�
	�@�7�Q/�0��i�� �w��w�8�5�R�t
�{�v�\a/�MQ/�S��h�>�
�c�r�/�qa/!�:Q�/#�&�?�
�C�Z��c��c�T�c{�i�7�&&�S�����o���(�_�4�u��w��$��n��B�S�^�n�}�H�a�$�/�7�R�4��	�w�>� .�Y�O�@�4�<�{�0��P��S�_�(��VKp��A��X��]�X�`�-�t�G�W��-�e�"��N�O�x���e?�K�j8�]�/���8�G�U�F��
��a��h�(�.��\�z(����$��X��[�o�p��%�t�:�+�T�}�R�a� �G���6]�C�3�!�Y�"�o5�n a/��Q/��N���<�7�J�a��r��C�8��w��z�a0�y��	�*�_�d�$�$R�J��
U��~��a/��~a/��!�0.�t�8�9�z�:�L�y]�b�y�%	����BQ23�Op}��$
lya2�	�]�hQ2��g�

�D�5�,�=��2
���^q2��v�{�z�=�Z�]�_�c�Y�d�[�Xa2�.�-�p�bQ3�j�r�[�Z�q3�q�"�i�g�x�h�z�|�s��f�k���,�&��t�.q 3-���q����(���n��o��� ��m�#�r�'�!�/�(�$��*�)�.�2�/�3�la3|�G�F�E�4�1�7a
3{��v�_��c�e�X�Z�z�T�U�{�Va3q�U�X
�YA3Ļ6A	3��|	�=�L�A�&�%A3��Pa3��]AN�0��+A3��KA4��0!�`�vR#C�1�h�N�	T� ��(.e�y��jO�V�i�a�no�`�_.�^�xF��&�.�jH�9aN�XAN�*�9�!��#��D�"�U�t�^�i��O�2�w�l�i�T�3AN(�3AN�Z�aN/�\A	N1�~����:��	�(�7�XaN@�^A$NB��/�u�ve�	�R�B�g�f��N�� �z�3�
�i�Z�j��9�{�:�	��~�W�d�Q���}ANU�PAND�`�e�d$
aN���b��&�+� ��APN��~�
������I�@�G�F�5�K��r�#�y�w��-�F�7��9�z�P�A�,��g�j���Y��M�'����L�x��2�o�=^��X���'�\�u�C�R�A�_�D�u�j���b
�U�Q��0�AN�45A
N��e��
	%

aO}�qA%O��i��u�K�>����3	�$�[��q���/�5�l�C�p�(�C�F���{�XAO��;.!"�%AO��s&�raO��8A
O�.�.��B�0����~��c�vaP�wA
P�?�i�9�2�Z�s�a�H�%� �iAP�BAP�G
�b
QP!�B�z��i�d�o��f��
�I�T�;��(A%P6�^�N	��y��X��q��A�(�t�'�F���e�O����?�n�R	�E�$�q��)AP@�?
)#AP;�$aP��XAP͙��G�>�@�/�.����AP�HAP���V�W�RaQ�eAQ�/
�`�NA
Q	�d
qt�9U�AQ�
aQ?�pAQA�8QQC�i�1�h�d�;�^�C�:��d�?�)AQP�G�T�9��B�?�AAQd�JAQ_�
�aQi�wAQk�@�)�h�.�7�Z�m�[�
�M�`��s�x�#����#�"AQ��[AQy�yaQ��A
Q��Q�3�$�Q��T���w�v�saQ���Y�
�A!Q��
�o�t�1�j�D���Y�X�/��[�~��+�*��5�R�p�3�2���s���z�4�F�}AQ��L�a�`AQ���D�EaR�AR���=�}�A�j���C��:���y�x�Q�P�)�?�I�.��-��B�;�QAR�NAR�5aRK�%A#RM�2�`�	�]�`�9��(��y�7�>�a�R�3�8��&�x�}�r�3�U�U�p�{�$�7AR]�^>�`	ARa�,aR��7AR��-F�P���Y�Z�o�Y�n�O�$�i�h�/�0�m�h���i�`�o�n�3
�8�|AR��}�Y�G�FAR���e�daR��EA
R���1�t�P�?�K�b�
��aS
�AS
�J�!�x��B�g�#�S�Z�A�@�{�m�o�Z�x��
AS$�UAS>�Q	SE�V��m��
����
A<SQ�p�9r�]�J�/�h�.�<��� �+�^�<�;�^��S�N�{�|�I�H��U�
�p��H�J�y�x�A�n�P��{�c�8���UN{�<�&�.��Z�Eq�5ASr�V *ASa�!
�[�4!�$.QS�p�#�w�P�H�W��E��#AS��&��1�R�>�2�!�Q
T�y�/�0�i���/�^�E�AT��l�C�8���j��Y�CaT,�vA<T.�y��b�*�t	�;�:�W��}�]�V�t�
��/�B�'��� �v�W��.	@�1��|�=�8�]�$�0	
�5�@�=�LAT��\AT3�@
�0��

aT��AT�
���m�n����J���o��5�}�:���+�2�7�.�m�=AT��^AU�4	aUV�*�(�.�Q�8A*Uc�%	����z�o�t�
��O�f	�O�`�M�c~�&��

�4pi
�M�P�1�WAU��_AU��:yaV�=AV�E�o�w�h
�Y�l�c�X	�g	�6� 
�k�v�S�V	AVS�A	V ��=	aVq�GAVt�~�#�<��k�l�Y�\AV��fAVv�3�UaV��KA.V��_�_�)��Q�o�

�R�1�� ��e��V�}�~�O��d�K�J��*�y�rAVʼZA	V��M%
�%�$aW3�U�{A W;�~�W��6�.��y��_�6����;�4�c�b�%�1�V�y�AWY�`FAWL�W#aW��A	Wԣ�K��6�����qaW��^ASW��e�Z�O�\�V�{�y�@�D	�c�b�)�7�O�~�_�Z�Y��%�.�c�L�?�B�54�\�I�a�@��.�V	����g�v
�S�s�D��M�L�U�P
�#�� �O��>�,�c�d�)�t�|�:�)�_�P�G�|AX/�21�(<�>AW��`	I($aX��7AY�:�3�2�k�j�-�1����:�
o�|�q�R�#�PAY�iAY�laY,�@A:Y.���6�]�\��P����c�^�c�d�x�O�T�[�\�y�z�Y�T���&�t�3�2�u�4�T��|��8�i�z�O�a��E�"��3�.��s�A�|�$�uAYS�j@AY/�n	�'�\aY��[�Y�s�tA	Y�Z� �� �B�_��?AY�X��]�z�/�2�gAY��-��m�laZ5�fAZ<��|�!�W�d�x�g�%�;�>�1�$AZ@�c-
!A
ZG�y

	,aZ��uA[	�.�	�G�J�	�#�$
�A�B���w�A[V�pA[�a[Z�A[\�(�_�o�t��DA	[_�^�NNOA[|�a[~�	AH[�������$�[�h�O�J�k�}�p�>��,�b�/��D�O�R�z�5��W�����C�;���G�t��:���e��J�O��B�W�M��o�X�+�D�)�(�G�B�/�}�L���/�/�L�A[��qA[���ta\�!A\	�����1�k��'�s��u�t�S�ZA\�uA\�*�?	�a\8�(Ad\:��R�2�e�b�i�/�P�C�B�k�`�o�`��s�n�w�E�h�#�J���	�[�d�
��� �;�2�
��w��G�>�[�_��:��K�F�O�J	�� �_�h
�s�r	�!��w�r	�'�>�e�d�u�H8		A	\[��C�P:1A\c�N�O
�N/G�a]��~A#]��.�n���5�K��l�U�X�Y�e�D�	�W�^�y�s�j�e�+�x�7�.�T�q�4��A]��A]���[�Z�}�|a^�#A^�=��=�
���o�A^�rA^�%a^6�uA^8�W��q�0 �;�"A
^@�y
A^X�'a^k�(�o��e�A^x�>��v�7�C���2�2�31�
��-�g�	�w�C�g���`�o�^A^z� A^��0�ra^��,�A
^��&��4��G��H���La^����2����
�A^����5A
^����]�\��.�`�A_�0Q	_	����M��L��+�`�&A_�w���F�Y�i�a_�2A_��^�$�D
�3�^�5�z��3�B��~�wA_!��!A_"�4
�=�<a_V�,A7_Y�. �1��;�X�O�t�d�)�i��i���-�`�F�7�6���-�R�h�e�}��F�G�F�!��2�e�i�-�L�*�w�h�F�W�P�H�;A_g�OA_X�:�#���a_��hAG_܊ �J�?�6��|c�?�:�m�v

�m�t��v�C���&�#�4���
�+�2�S�J
�7�8�g�0��>!�!�F�#�o�A�>�o�:��(�~	���I�2�kA_��~'A	_��D-a`��lA#`��i�!�p�G�m�f����"Z����&�?�>�	�z�
�[�B�/�4�+��K�F�>�S�FA`��
A`��Ma`��~A
`��R���^�#�&
�kaa
���
�SAa�|�%���"���/�e�l��J�QAa �Aa�VaaX�A=a]� �i�q�R����5�6��
��v�e��S��P	�Q�i�a�
�7�A���z�5�.�y�z�i�n�=�PAa��Aa|�Wab�BAb
�:��w��Y*���X�,�#�"�m�l�o�nAb�	�[Ab�]ab2�MA!b4�7�m�V��m�4�w�B�A�3�$�S��M��)�
�(�3�<�y����r�=�AbH�Ab6�=�_� .�=ab��ZACb��_�w�n�/�x�T�K�m�J�r�Q�{�*�|����s�d	�	�z�M�F�-���5�p�A��A�y�M
<�B�	�=�D�
�k�h�f�3�N���Y�L�U�q<�
���
�"�2��1Ab��Ab��d7

�c�bacB�jAjcI�
�#�~	�
�1�w
�X�#���U�V�?�<L�,�?�w�<�G�8�s�d��y��#�k�h���s�D�E�j��A�z��2�I�L�g�p�5�I�|�q6�{��2�+�W�
�9�>�q�|
=	4��c�X�q��>�8	�}�t����)��V	�C�:�q�7�E�l��M��F�P�/�Ac���O<,� <Ace�l
2
�1	�N�`1
ad��|A	d��4�n	����Ad��Ad��ad��0Ad�!�"��v�1�:�E�@MPAB�Ae"�Ad��~ae4�BAe6�E����s�#�
��� ���?�@�/��V�#�4�(�w��\�|AeN�AeG�ae��QAe���=�&�m�`E�!����"�H�@��h�9�&��i� �,�E�d�9A
e���	Ae��<ae��`Ae�7�8�A�]�H�n�{�e�Z���;�6�����P�e��,�p�-�Y�j�h�e��L�<Af�	�[Ae���9af5�hA
f<�)�>5�G��D�a�Z�_Af;��<AfL�af[�
A	f]�q��2��3�����V�=afg�sA-fi�i�R�g�,�C�c�T�#�p�c�Z�� �{�f�q�d�Q�R^�2���!� �%��2��1�@�Afs�"%Afk�,!�{�z#Q
f���'�P��m�	�[�p�Ag�K��$��=�<�Y�n�h�w�S�K��3Ag�(Ag�
ag&�Ag(�fm5�/�F�N����J�1���'�"�X�{�c�A
g.�Ag3��A�@agc�ALge�R�$�3�|�,�y�x�}�&�L�}�b�"�9�$
�g�N�~�
��>��t�]�X�A�V�A�:

��P�N�K�F�%��m�F�#�H�P�6��2�%�u�d�}��n�o�T}�1�v�_�g�F�Y�bAgf�*T@�OA
gn�q�P4$2ah0�#AJh2�A�U�]�d�i�,�~�	5�
�*�g�j�{x��X�>�9�j�5��I�^�?�& !�}��u��%�&�}�~	�[�,�E��H�w��6�J�)�"�[�]�p�=��C�N�g�f�i��
�I�ZAhD�/
;�!9�$�Ah[�%	 ah��0A9h�W�&�e�x�D��|�+�2�q�t�d�M�^�C�
�8+& �5�|,�
��� ��(�}�h�w�r���q�t�)�\�L�u�b�=�=�Aih�2Ah��2!ai�:Ai���c��1�*�}��1�,8=$��(�}�LAi��4.�Ai��<	ai��?A+i؞7�?�n�i��l
�A�J
�k�n�d�{�d�:�-�g�2�,�W��'�+�6��Ai��5MA	i��o�
�
	ajP�HAjX�f��u�N� 
�i��j�=�8
�Ajk�7
AjT�4�U'aj��O�<�>A:j��K	�o�j/0
�{�~�3�(��"�c�bad�y�z�1���S
�H���l�)�B��L�P�G�H�m�V�� ��n�O��rAj��;Aj��R
1	�!��akx�d�.�fAk���=�!�
�,Ak��iAk��[ak��oAk��q��D�X����u�@�A�r�y�2�o�2�(��4�#�Ak��=Ak��\
�1�q� al�AJl�>��3�~��X��
�!�<��X��E�^�v�m��l�Q�K�(�'����]�(�2�3��z�@��c� �j��P� �(��4�X
�'�H�H���&�4����%��B�i�lAl?�>SAl3�`	
�?%�>	al��mA^l� �O�6�Q��h�%��'��(��g�D�(�k�r�E�@�Q�l�5�x�2�e�\�)�j�
�{�p�l��|�`��\�2
��sX�
�;��g{�l
�A�J�]�^�=�6
��"
��~��3��/�2�%	�5�'�r�T��]�U�,*�1��^A	m�Cj""Al��o�
�(�5��v�w�Q�P-�mQ	n�X��t��f��U�S�@�rA4n)�9�6��~�w��E�>�/�P��2�q�t�C�F
�?���f	�=�R�{�t�n�W�G�4�{�t
�a�T�y�z
�+��+�X�g�8An9�LbAn4�
�H�sY�e�dan��jA9nݖL%
�p
�)�41��0��E��.�e�8
��e��+���
�

�c�~��S�7�R�D��v��bM

��r�	�$AoQ�`6�`An�8)�E&
�%�$ao��A.o���1�"�5�>�f�9�D
�5�B�.�1�r��
+2	�C�P�y�z�o�M�	�B
uv�_Ao��R?�_�^�	� Ao��#?
	ap]�Apc�/�?�:�W�t����C�A�H�y�|�w�;
�>�v�_Ap��W%�WApd�!ap��"Ap٫;�E�V
�_�b�{�v�{�%�
�Aq�Z
 �{Ap��$*aqF�\A>qI�C��Q�F�q�j�[�P�'�h�B��@�V���� 	�e�d�S�T�O�u�D�o�f�	�|���Y�G� �Y��R#�W�V
�K�L�1��DAq\�[<�}'�<A
qJ�)
 )
ar=�X�P�"�[�
�Iar;�d�f�h�iarA�6�7�8A&rX�j��>�!��J�5�
�?��!�	�(�Q�R
	�3�2�7��0�1�,�	�
�i�h�3�@�gAr��aAr\�:0�%�$ar��|A
r�{�E��_	��+�&����I�as�@As
��C�D�s�x�)�\��ZAs$�cAs(�Bas6��
��D��0�Asc�M�E�A	sW��"�'Asl�Eas��H�p�D�&	�J�b�KAs���$�L����*�t�����|4+�c�e�>�q As��fAs��N�`�a	�O�N	
�H�I
at�ZA
t!�:��I��
��w��^�]�^�]at/�_At2�#����!�F	��_�t�i�`�����Atb�q�yAt9�b
at��mAt��1)(�g� �D�3�4���3�2At��rA
t��p		�Wat��>ABt�=�q�v
�w�z��.�8�&�]�)�[�^�!��0��<��Z�
�~��E�H����c�>>�4�4�q�!�h�>�l�E�#�f�W�Z�u�|�~�-�(�5�@Au�t-�?�Au"�^+�:�|�U�Va	u��\�`�_��D�C��Y�Au��x��q�1���?�g�"���}Au��b
	
Au��/�cau��a
u��{�z�w�u�x��}�~��au���	�
��	�
Av$�	
�w�zAv&�-Av-�
avL�avG�		�
��
����avK��S�Avl��+�s��c�z�f�o�?� �
�O�PAv��vAvm�av��}a	v��!�y�w�x�z	�#�$��%av��Av��|���;�`��^��P	�x��]��
�<��2�N�pAv­(
�%�&Av��aw:�K��#aw%�9�6
�<
�|�>�?�Baw4��%��Awa�-�\�L��|
�[�{�Z�z�{�zAwb�EAwj�U�
��aw��PAw��O	�'�&�7�6��&�{�*�>�O��J�[	�Z��T
�0���=Ax!�~Aw��#	ax-�(A)x2�R�#�a�G�b�[
��G�y�6�T���#�&�#��G�Y��6�X��=�V�y��9�:
��|AxN�A	x0�*��:�!5)ax��2Ax�t�K�F�	����g�h=:
�Q�8�g�~�c�g3�^�X�J�{�rAy0�A	x��1
5ay[�<Ay]�U�`�t�?�W�p�O	�T�$*��$�Y��k��:
�,�i�h�[�ZAy���uAy��>
ay��Ay��Q�}��W�	��S�Tm�q�.�1�.�S
�-�D
� �S��IAyɮAy��B(�%�$az�Az��v�i�O	�>��fV�b���	��A	z�
Az�Haza�$Azc�_�e�$��X�d��C�?
�t�g�k
�w�t
�YAzi�&
Azm�O
'az��TA
z��6�E�@���m�nAz��	Az��Vaz��>�}�@��7�AAz�a�\�d�;�:���Q�0�7�y�7���
�Az��
A{�Wa{�KA	{�%�S�F�j��;�h�M�A{�M	
A{'�Y	Q
{K�R�����'��k�0��N��SA	{V�f���f��	��������a{p�\An{t�Z
�g�� �}��U�.	�;�j�5�(�p�o�g�L�����"
��Z�.�-�<	��

�{�r�u�v����#�&/.�y�x�K�Qp�(�!�-��0�5�#�E�B�'�'�*�s�6�B��A{��
�%A{r�`'1


�C@�Ba|��rA
|ʏ�f�v��/�+�l�;��C��t�KA|ү&A|��t
a}�xA5}
�3���d�q��g�I�0�c��T�Q�L����x��=�Z�_�`��5�.�	��.�R
��,��t�S�t�/��{�r���k��Y�TA}H�A	}�{	a}��AQ}��F�9�V�h�1�H	
�S��P�s��a�2��"��(�l�u�X�#�P�?�M�k�`��	�k�;��k�r�/�*��d�2
	�c�w�Z
�%��{�	
5�3�N���w��j��tA}��{A}��y�x
&�Oa~Y�uA~m�(�O�A~]�r�8�Q�|
�	A~b�a~��5�&�a~���'��	�����
���
����������a~�����+�
��
�A"j�Q�Q�T�d���z�4�M�L�!��G�H�!� �?�6���?�6A��A��-a��/A̞L�-
�p
�Aʰ1	A�a�� A�	
���<�}�@W�6�'�A�7
A��"��a��>A�3�V�D�y�=�R�1A
��@A�$�&#Q	�o�F���6��;���>��9�IA�~�^�K�Q�$�O	��@�%�z�Z��I�$�#�i�T�l�Y�4��'A
�y�QA���-a���0A�ޜT����=�~�y��n�~�e���l��O�e�v�k�,A�ٰ_	
 A��2�5�4��a�e�tA�k��%��F��,�3
�h��
�.
�s�v��A�n�s		
A���7/a���A	��	�A�T�k��>��8��Ja����S��A��5�r�M�%�"�7�-��	��W
�)�v�o��q�f
�\�A��A��<a�j�-�!��0�m��1A���6
�D�M�Y�#��u�i�|�E�?�U�h��<�k�p�?	�n�W� �f�m�P�	��A�~�3
	�(�-A�y�u�7a��UA��o�

�V�
�S�����5W"�:�J�@A�#�]

��m�p�
;4	
*A�C�I
0a���QA-�ŔA�C�h�u�@�e�	�d�I�^� ���e�x��v3d�y�[�4�W�	�|�Z�i�v�[�	�C��W�I��(�N�D�U�B�C�^�1�\�A;���p�T�a	
�f�W��!
%	�*�!	
-�4�!	
�,�C
NA��S$8+1-a��A��X�0�_�~� �M�_���h��w�T�s�1�P�+A�� 
�~�w�*�#�h�i�r�CJ�v�_	
A�4�_)0Q	���1�y�*��r��F��?��LA���t�+�I�J
�I�pW%�( �+�;�J��d�/�
�J�)�HA���>?@�m��5�6

	A���b%�G�H(��
�Ka
���Y�l�m�U��n
�V�x�o�WA�ǒ�t�'�

�`�V�	�#�e�X��>,�Y�(	�_�)�d�
�A1���b
	

				
 


				A���q
	2a���|A���PA���
 
��A���~ �va���A����&D�J�n�)�h�	�G��1����x�/j�b
S�Z�y�A+���+�8��@�=��	��

			A��93a���K�>�w	�j��4�0�ka���D�F�H�I�K
�Pa���
�	����A��@� �a;�\�S�A
��Q�.�7A��a���(�a�W�X��YA�V�(�\�F�L�<��q�;�3�d�z��d�+�A�6�^
�^�[A�0�
<�[a���y�/���{�|A-��r�v�u�D�U�\�>�k��!�r�}�,�/�g�`�k��@�Y�)�f�?�X�m��p��n����#�V�q�:�[�>�s�6�>���A��~
�H�M�@	�A		�4�9	A	���G�F+0a���A����4�i�!�L�O�d�
�K�/�$�`�&�+	��d�	�~��H�0�o�-�,	�
�=��0�
�7�A"���+�-�,���	��	
A���8I�Z*a�C�)A�I�=�C�D�K�N�'�"�M���F�G�F�w��h�A�S�0+A�T�+a���J�L�MA	�7�i�(�8�S�[��
�-�A�:�O	A���,�(a���_A���a�1�n�.��S��Q
���A�E���/�R�V�g�na���hA���H� �5�
��z��B�a��Y�"�O�J�b�kd�
�(�u�t�_�<A
���k	
 �A��2a���r�j�q�5�tA���CX�.����H�)
��+�>�<
� )�y�R�]A��w�r�qP�b
�c A
��6Ma���O�S�+�C�a��������a���@A�D��;���i�S�8�q�|�0�J�!�1�a��4
�P�U���E�<�A�@�]�AG��

�`�a�j�k
A� �A* 	��
$a�b�_A����m�8�'�c�A���b	�M�LA���Na���OA���%�k�A�,�7�2��S�7��
�Z�<��#�bA���g"A��Q�q�pa���pA���e�K���#�"�G�vd�O�l�k��N�/�4
��� �;�Z�]��J�O�`��
�
A��o!

A��U)a�O�~A5�S��e�u�j��p�W�@�=�Y�o�V�z�	�T��m�"�S�D�/
�*�[�x�h�-�6�i�`�Y�,/e��Wi��E�#�&
�j�1�+0��B�0�M��8��A0�V��Z�[�d�e�-�jH%�>�G�>�?
�>�=
�ZA���X""
)	a
���m�.�a�o�1�0�b�2�p�3A�ƚd�C��<�;�T�2�u���t�TA�ɶ5A���@a���D��t�B�q�=A	�
�7�-%�"�K�>�1�T�s
�7A���<�
�	
���
��z��|�}�~���u�A
���r 

a�w�RA�~�v�5�f��K�>�w�MA	���*
�3

��wA���T�Na���}A�Ҝ]�1�R�s���=�8	�	�p�#�1�V�S�t�C�x�I�e�9�e�}�@�H
�e	�.��T�A&�϶T�	
�{�0�/��	���x�
�
��z�
�z�
�tA��$�K�J	�Y�X-
a���iA���_"�A���h	
�h�eA���
a���mA��86�"�n�U��h�#�c�:��F��W�hA,�ضq�h�e
�r
�_	�V�W�L	�S��,�-A
��$
�a���A	����C�x�2��g�tC�.
�A���;A�����
Ba�.�1A �2�w�U�K��f�]�w
�<�%�4�1�c��1�6�h�%�T��>�)��}��q�T��
�$�z�
�6A�B�3
��$�^	�Y�s�n�Z�UA�A�+a���E�H�I�4�JA4���m�B��A��o�(�g�|���-�8��O�]�&�I�I�y�r�K�H�K�$�A�|�+�1�Z�	��C�6�C�5�v�5�4�!��A�3�rA��$
a�=�bA
�R�h�4�5�:� �G�R�K�S�A�B�g��!���6�7A�Z�+
a���xA
���L��D�k�3���g��a���~�1A�ӌ��=�k�d�:��	A�|�`����e�e�&�K��k�%� � �A�ø�H�GA
��3
��a�M�9��<A�T�%���$�\�1A�W�y�c
�b�KA�Y�>a�o�A"�s�3�a�b�3�`��G�*�E�0�g�Q���6�q�z�s�r�?�E�T�,�q�pA���A���@a�(�.�:>��s����Ca��*�-�)�,�}�.�/�0�2�4�3�6�9�5�7�:A�;��=�>a�2�E�G�H�I�JQ�K�LA���3��e�V���sA���@A��Na���F�HA���c�-�^�-�
A���K		A��O�+
�*
a�-�SA�0�)�M�$A�7�R
�L�#�/AA�6�Ua����7�ta	���e�f�g�h�i���k�ma���YA��E�$A��o
A���[	a�"�{�}�~��T��)�N��"���1�a�-�<a�3�^A�T��w�j��pA
�O��p�q�l
�k�l�mA�s�`a����A����C#��A	����b�c�\�cA���ba���A�֐x���}��b�o�		�*�i�"	�d��
��f	�&�`�Q%�Y'���k� �MA:�Թ 

�H�3		
		+,n		
A��e�M�L

�
?a�]�TA�`��|
�K"�]$�=�b�I�]A�d�V�f�Y�T�_	)2
�`�e�2A
�c�w# a��sA
���V���
��;�:a���{A���bW)�_�k�2��d�t�-�}�|A����x�d�eA����ya���A�Ґ�>�IA���
��}�|A��a�����	�A����z�A
��
�/�`��6A
��
a�_�%�8�$�(A���~A�j�+�����n�2�1�)A�h�
q��e�{�!�1�3�^�d�{������"�$�.�6�8�<�X�_�o�{�~�a�pa
�0�Z�T�R�[�a�]�k�e�c�ga���q!���G�L�H�K�M�F�"�N�4�|�5�}�	���;�9�<��O��&��'��
��0�(��)�a�a>�G@�I�o�	�E�G�CPK
!< ��_ww5chrome/pdfjs/content/web/cmaps/UniJIS-UCS2-HW-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�
UniJIS-UCS2-Ha ;�g�!�$&�#PK
!<bq%��5chrome/pdfjs/content/web/cmaps/UniJIS-UCS2-HW-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�
UniJIS-UCS2-Ha
 ;�g�!�$&�#
�M�_�U�T�W�Y�WA!��bA %�Z�n�6�Y;�m�2A!��6a%�9�7�=�;�A�?Q%�G(q%#�j�o�m�l�n�q�p�r�[�]�\�`�S�U�T�Xa%=�wQ	%@�ua%I�~a	&�������_�O	�c�N�]a'�A0�Vz!O�0�/a0���S���6Q3��J�W�V�mq3�E���
�L��N�P�!�T�	��W�Y��	�\�$�q 3-�_�b�&�e��h��n�p��q��j�r�t�s��w�%�{���|���~������a3����[A��L
�K�PK
!<�RP͘�2chrome/pdfjs/content/web/cmaps/UniJIS-UCS2-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�
UniJIS-UCS2-Ha��M�_�U�T�W�Y�WA!��bA %�Z�n�6�Y;�m�2A!��6a%�9�7�=�;�A�?Q%�G(q%#�j�o�m�l�n�q�p�r�[�]�\�`�S�U�T�Xa%=�wQ	%@�ua%I�~a	&�������_�O	�c�N�]a'�A0�Vz!O�0�/a0���S���6Q3��J�W�V�mq3�E���
�L��N�P�!�T�	��W�Y��	�\�$�q 3-�_�b�&�e��h��n�p��q��j�r�t�s��w�%�{���|���~������a3����[A��L
�K�PK
!<�i����3chrome/pdfjs/content/web/cmaps/UniJIS-UTF16-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE���������!A\a
!�GQ�k]>�d��_?V3�A���M�T�+ 2!+�V�S�L�IQ��tA
�~$�c���
Q
��n$�c�U�P
�&%$)A$�)
�f�7�![Q .��s"'��A��"�}
�";�9�6"��B��;
��|�,"�m����Q/P���'
��j�>"�kOL
i��O��fO�>��_c�0
�m��("��[��<��SA�i��P.Q��2��j���	�
���A�����k9Q
P�X��"��"O(
��zA
^�W-*'0#rQl�6B''�$�c�bAx��1TI�6��7�ZQ��94
M�-�|3��)F�Am�b	>M#=:B�@�;	���J�<O@�0�G�"8��F.��q.�J	�N
M�y�u�>�:�E�<fG>+	'c���h	W~�9�2	
�|C	l�
;ls9'�
��V�h�i� N�.:�K� �e�,�d�[�5�J��|�O�r�]A3 Z�Z�+�.�/�?
�x�vC	F
�Q'�

�j�w�
�*�"e4
�?	�^(��D@�����
�9�zAA��c��,
�`��2�H�j&�_;/1� !�f	�P�c�Z�w
65
�h6

4!�;4/Q�fB��.+�T��*�
U	�9�.��53�6�?C��$<(�#��Aa��J��#�"k
l	�)�@�]VI�b0#
8%#
#'`�p�|�k�`-�k]�FI�[-�&�3�w;<;� y�e�5�@T�
K�9G�$��+��C�U��p�t�m�Z�$�;�M�
�v��3�\�7��V�=�/�j�U�_(�A�K�v�9�y�.�@�7��#�A.���eA.���:A���K
�_�!�W�:�r�Q/�0��i�� �w��w�8�5�R�t
�{�v�\Q/�S��h�>�
�c�r�/�qQ�/#�&�?�
�C�Z��c��c�T�c{�i�7�&&�S�����o���(�_�4�u��w��$��n��B�S�^�n�}�H�a�$�/�7�R�4��	�w�>� .�Y�O�@�4�<�{�0��P��S�_�(��VKp��A��X��]�X�`�-�t�G�W��-�e�"��N�O�x���e?�K�j8�]�/���8�G�U�F��
��a��h�(�.��\�z(����$��X��[�o�p��%�t�:�+�T�}�R�a� �G���6]�C�3�!�Y�"�o5�n Q/��N���<�7�J�a��r��C�8��w��z�A
/Ӟ~/�_�H
�+��<��D�w*�3�`A00�:
�a�aA0<�Bc^Q23�Op}��$
ly�nA2Q�Q2��g�

�D�5�,�=��2
���^A3�pA2��ZA2��{Q3�j�r�[�Z�AN�0��+A23�"��
��
��*�?�
�#�F�?�#�,�y�`'�>�M�;�:�J	�=�K��A�&�w�P�y�{A3�x


	�5
&A4�$�&�b9�S�6�m�h�N�	T� ���
�`�(��2�n$�Q�@"�Q�cA4��lR#�.�g
O�8o��x�m�jH�9A�'4��Z%=,'0"�k�z�p�k�H
7(
�p�a�5�5F>5$�LK&&.	?��i�GT4�\�#�]�r>
�K4##P\	8
�9	�P" �Z�A��8>F�2�i5%"�$�09�g��
�B^�g�6"
�\�_�,HBP
&�7J$(�*V
8�C�n
!4
3<	5&Y�1��<&��C$�`�j0�Q_ 
\(�pD�G��$%�	�f@�z�y6�d	$�.~.B�dC�D��&�r�}v%Qo�
�3QN�*�9�!��#����}�D��R��1�U��AVN�L�^�i��O�2�w�l�i�@�m�3�2���M�(�7�X�/�u�ve�	�R�\��g�f��N�� �z�3�
�i�Z�j��9�{�:�	��~�W�d�Q���}�x�X�S�w�h��N�
������I�@�G�F�5�K��r�#�yA
N�Z�
�a��e�d$
"��
AN)��U�x�y	
�t�u�T�"�y�x�w�nQ	N݅����g��N��/�F�7AN��9�z�P�A�,AN끆b�F�GAN聤]Q	N��Z�4�d�l�m�g���p��SQO��a��	���4�<��k�Y��M�'��\AO�q���L�xAO�kAO��f�x�3QO3��d��)��(��w��z��k��G��.��Q���PAO@��f��u����N�;QOM��\�u�C�R��6��y����a�r�/�����j���b��p�AAOi�)�Q��0��\�i�AOz�pAOc��k�	�5�:��>�?Q
O��%����k�!��
��W��V���������r��oAO��.����3	�$�[��q���/�5�l�CAO��<A
O��s��M�L��<�L��z�Q
Oȁ�~��%���*��������}��|���AOז�F���{�X
�g�.��B�0���
AO�l�(AO܁��#	�"�?�$	���(�u�dQP	�T��`����2����x����p��e�a��>��wQ
P�*��6�m��O� ��~�z����yQP!�B�z��i�d�o��f��
�I�T�;��(A,P6�^�N	��y��X��q��A�(�t�'�F���e�O����?�n�R	�E�$�q��)�~��G�>�@�/�.AP@�?
)#CA2P0���� �t�u�5�{�$�y�x�y�5�{�&�'�&�}	�|�7�6��-�0�5�}�0�1�(�;�:��)��
�o� �1�9�lQ	P၇
�4��������L��k���+Q
P�_��J�R�T��j���I��AP��b���E�h�	�qt�E�D�9U�AQ�
AP���+�����8	�)�(�C��(QQA�8����!�1�h�d�;�^�C�:��d�?�)��r��gAQR�r�9��B�?�A��)�h�.�7�Z�m�[�
�M�`��sAQd�JAQS��;�I�F�_��T�_�
��W�>�H�S�QQ��~�#��P��3������6�7�#�"A!Q��Q�3�+�D�w�v�s�g�f�o�t�1�j�D���Y�X�/��[�~��+�*��5�R�p�3�2AQ��K �a�`AQ�����	
��1�`��j���.�-�<�=�'�P�gQQ����p�Q���u�s��L��E��zATQ��5�F�}�
��=�}�A�j���C��:���y�x�Q�P�)�?�I�.��-��B�;�Q�$�`�	�]�`�9��(��y�7�>�a�R�3�8��&�x�}�r�3�U�U�p�{�$�7�jF�P���Y�Z�o�Y�n�O�$�i�h�/�0AR�NG�a>�`	�X�Y�GA1R��o�#���N	�1	�$�g���Z�'�+�P�'�C��*�+�N�M�N�#�I��P	�O�N��$�!�z�g�
�Z�}QR�R����)�h����v��a�`�o��*��=�3��n�A�@AR��|�{�1�t�K�b�
AS�SAR쁥a�
�H�B�g�#�B��,�E�DQ
S�M�!�x��V��u�B��0������K��s�SAS#�O�A�@AS$�UAS%��i�K�T�U�)QS8�S�{�m�o���U�Q���x���T��KQ	SE�V��m��
����
ASQ�p�9r�]�J�/�h�.�<��� ASa�!ASY��q�#�J�%Q
Sl�"�J��W�+�^�X��;�^A#Sw�`�S�N�{�|�I�H��U�
�p��H�J�y�x�A�n�P��{�c�8���UN{�<�&AS��WASy��	�'��(�R�I�L�Y�U�*��*�U�T�-���~�W�V��(
�V�{�7QS�Y��J��'��L��]�Z�Eq�5��
�6��#�#�w�P�H�W��E��#��p�7��1�R�>AS��r����?�QT�y�/�0�i���/�^�E���`�`A&T��l�C�8���C�<��b�*�t	�;�:�W��}�]�V�t�
��/�B�'��� �vA	T��4�O
�0��
AT����"�^�_�#��]�\����G�TQT���%�X��m�y�����Y����AT���H�7�%��p��7��CQT����t������z��	��<�6��5AT������QT���L��}��B�8����=�8��2���$�0��p��{A%TǢ
�5�@�=�L�+���m�n����J���o��5�}�:���+�2�7�.�m�=�*
AT��^^�A'T��2�D�E�T�n	�o���o�n�o��E�\�a�J�K�^�j��
���O�N�s��p�Q�6�q�Q
U{�4��H��S��Z��g��l�n��K�A)U��<��z�o�t�
��O�f	�O�`�M�c~�&��

�4pi
�M�P�1�W
�~�o�w�h
�YAU��_A,U��:�Z���r�s�r�u�	����s����}�|
������j�k��i�b�m��z�%� �QV/�U��8���X��<��C��X��Y��\��[��TAVB�_	�6� 
�k�v�S�V	�1�#�<��k�l�Y�\AVS�E�kA*V;��*��{�i�h�x��k�v�w�v	��i�f�g�d�

��u��~��g�d�%�*�r�}�%� �a�b�%�*�t�}Q
VȢg��L�i��H�w���3��8�t��/AVӢjQVעk�_����=���J�e��9����
��ACV�
�R�1�� ��e��V�}�~�O��d�K�J��*�y�r
�`�W��6�.��y��_�6����;�4�c�b�%�1�V�y��v�K��6��_AWY�`FAFV��P�d��u�t�t�k�x�p���m�h��y�{�p�m�h��u�t���t��o�r�I�H�y�	�I�H�{��x��z��y�z���	����W�R�����c�-�~���Q
W��e��x���O��l�����v���3A9X�
�y�@�D	�c�b�)�7�O�~�_�Z�Y��%�.�c�L�?�B�54�\�I�a�@��.�V	����g�v
�S�s�D��M�L�U�P
�#�� �O��>AX/�21�(<�>A2X��a�y�x���
���~�	�v�o�~�	��~	�}�f�	��]�p�|�
���|�}�|�a�t��z��e���
�`���z��#QX��h��y������z��_��z����vA7Xܣ1�)�t�|�:�)�_�P�G�|�3�2�k�j�-�1����:�
o�|�q�R�#�P�Y��6�]�\��P����c�^�c�d�x�O�T�[AY�iGAX݁�y����"�"�|�/�.�/�w�$�%�$�;�8����=�<��1Q	Yg�M�y�z�Y��J��w��B��[��AsYs��&�t�3�2�u�4�T��|��8�i�z�O�a��E�"��3�.��s�A�|�$�u��� �X��]
�o� �B�_�r�/�2�O�J�g�%�t��#�!�D�� �=�<�;�g�*�
 ��K�L��
�=�V	����	�G�J�	�#�$
�A�B���w��a�_�H�9�t��!� �]�\NOAY��n�AY��q
�
��m�l#

	,4�?�>A;Yy��j�?�%�`�?�<�?�'�&�8�9�6�3�7�67�=�:	�;�:�����K�J��������
�aA)Yr��	*

!
-IQ[����H�G����H��I��J��[������e�[�h��*��{Q[���C�l��5��*���}�p�>��,�bA[��n��D�O�R�z�5��W�����CA[���w
�A[���=
Q	[��S�:��"��g��G�t��:A�![ɤ���e��J�O��B�W�M��o�X�+�D�)�(�G�B�/�}�L���/�/�L��}����1�k��'�s��u�t�S�ZI�R�2�e�b�i�/�P�C�B�k�`�o�`��s�n�w�E�h�#�J���	�[�d�
��� �;�2�
��w��G�>�[�_��:��K�F�O�J	�� �_�h
�s�r	�!��w�r	�'�>�e�d�u�H8		�o�n���5�KA[�sE<�G�C�P:1bA\�*�?	�2N�O
�N/G�%�A-[΁�z�{�z5	�
��������5���

�%�&�-�0�5�2�5�6A[ׁ�z�$2�%
	"o#2$CA&[́�D
$ 	

"#


Q
]����c��������o�X�Y�e��PA0]�m�	�W�^�y�s�j�e�+�x�7�.�T�q�4���m�h�[�=�
���o��O��"�� �q�b�G�B���a�`
�!�>�UA]�� �}�|)A]���
�P�}�|�U	�-
�z
�y�&�T�M	�v�RQ^w����)��$�/�7�C�$����)A&^��?�2�2�31�
��-�g��n�g�C�g���`�o�^
�U��&�n�K�L�9�/�l	A^��0�r �d�_A^����N�}�z�U�'�&�'�x�U�'�zQ^������t���5�j��2��W�����W��V��w�Q_���-�,��U���M��L��+�`�&A
_�w���F�Y�i����s��t��}Q	_!��d�F�^���$��h��%A_-�$
�3�^�5�z��3�B��~�w�z �1��;�X�O�t�d�)�i��iA_4��!!� A_.���-�=�<�B�f�+�*�	��C�@�A�(��!�	�H��Q
_|�t��L�b��1�-�`�F�7�6A_��9��-�R�h�e�}��F�GA_��<�"�#�"�!A_���Q	_��>���!���f�}��9�e��dA_���-�L�*�w�h�F�W�P�H�;�	�J�?�6��|c�?�:�m�v

A_��&A_��8����3��� ��5�,��5�4���9
�*��Q`�V��������p�*��'��z��s�C�A5` �5�&�#�4���
�+�2�S�J
�7�8�g�0��>!�!�F�#�o�A�>�o�:��(�~	���I�2�k�^�!�pA`]�'A`��&���;�(
��������)�(	��E�D�-�,�)�)Q	`���7��
��8�R��{�f��^��_�Q	`��O�"��Y�"Z�����A`��c�&�?�>�	�z�A`��
A`���!��.�1��'�"Q`إz��R��/����E��f��5�c�4����GA`�g�K�F�>�S�F�_�^�#�&
�k�]�D�%���"A`��A`偨@�7�)�\�=�<�?�>�;�z�>�;�%�$�8�A��D�C�B�?QaD���j�;��I�����}�/�e�l�A
aS�!�Q�L�i�q�R���Aa^��(�u�tAal��LQan��6����\��q
��v��nA5a~�Ge��S��P	�Q�i�a�
�7�A���z�5�.�y�z�i�n�=�P	���w��Y*��Aa��zA%a{��*�'�&�L�w	�2�B�O�)�v�E�3�v�E
�r���4�p�q�F��~�Y�V�W�V�S�R�a
�/Q
b�p�,��8���9�#��Z��9��:�$A?b&��l�o�n�+�m�V��m�4�w�
�I�A�3�$�S��M��)�
�(�3�<�y����r�=��,�w�n�/�x�T�K�m�~�5�r�Q�{�*�|����s�d	A
b)�^�C�_� .�=>�<Ab'��f�e�`�_�^���a�-�,�n�u�\�i�q�p
�lQb̦i�	��"��)�M�F�-����H�1���5�p��v��9��AA;b��M
<�B�	�=�D�
�k�h�f�3�N���Y�L�U�q<�
���
�"�2��1	R�#�~	�
�1�w
�X�#���U�V�?�<L�,�?�w�<�G�8�s�d��yAb��e

�c�b+A*bꁨx�{�q�h�k�j��~��~�k�h
�e�b�|�}�e�`�}�|�c�b
�a�`���7�2	�a�^�e�dQc��r��]�#�k�h����������D�E�j��A��VA�9c����2�I�L�g�p�5�I�|�q6�{��2�+�W�
�9�>�q�|
=	4��c�X�q��>�8	�}�t����)��V	�C�:�q�7�E�l��M��F�P�/��c�n	������"��v�1�:�E�@MPAB��:����s�#�
��� ���?�@�/��V�#�4�(�w��\�|���E�&�m�`E�!���1�0�7�"�H�@��
�h�9�&�2��i� �^�5�.�u�x���O�P�O�8�A�]�H�n�{�e�Z���;�6�����P�e��,Ac���O<,� <0S�+�~�1Ac��s
�1	�N�`1
N�=�@-< ��Ac偃l�JXsA&c���"+*			
5		'	
$AcꁗJ'
9*1		A-c���%!	#
"4
Qf�k��6�o��w�Y��<�+�(��W�}��R��k�e��N��+�S��O�l�!��R��Af4�j�*�/�>5�G��D�a�Z�_�BAf3��v	
�.�'�W�~�Af7��L
Q
f]�q��6���3�����V�=A-fi�i�R�g�,�C�c�T�#�p�c�Z�� �{�f�q�d�Q�R^�2���!� �%��2��1�@�Afs�"%A fj��Q�	����S�L�}�5�4����(�)�?�f�7�z��[�Z�E�D�E�D��Qf���'�P��m�	�[�p���NA.g�K��$��=�<�Y�n�h�w�S�K��3�Zm5�/�B�}�N�.�I��6�9�J�1���'�"��-�&�#�"���P�$Ag�(WAg��
�����i
�7�6�f�a�`�S�O���Qgl��i��;��H��M�,�y�x������`��;A	g|�$�}�b�"�9�$
�gAg{��2�k
�j��YAg���j	Q	g���� ��#��L�T��1��Z��o�>Ag��n�t�]��x��!Qg����0�8�9��]����[�:

Ag��(�P�N�K�F�%��m�F�#�H�P�6Ag��+A
g���{�h��~��~��)�&�|Q
g�6��h��o����e�%���$�Ag��*�d�}��n�o�T}�1�v�_�g�F�Y�b�U�]�dAg��
�'Ag���J�%�z���~�i�h�~�}�5�0�~�}Qh;��~��-�,��~�x��{�	5�
��� ��A"hH�
�j�{x��X�>�9�j�5��I�^�?�& !�}��u��%�&�}�~	�[�,�E�AhR�,;�!AhI���v�w�r�3�@�C�4�5�4�w�A�&��u�h��9�6Q
h��K��v��o��6����Q�����)A	h��Q�[�]�p�=��C�NAh�/Ah���Qh��0�v�6��5��4��O������|��gAhߘi�
�I�Z�-�&�e�x�DAh��.Ahꁪ��A�@��]�pQi�� ��s���d�]��
��h����2�q�t�d��t�=����A@i�@�c�
� 
�e
���}�S�

��L�\�'�$�=K��1�.�//�c�(�}
� �?!�Y�
�B�/�n��d��g�2	�V�	�+�6
�}�W�N�K�
�UD�\�3��B�
�J���S
�H�e�l�)
�Y�L�uAqi�p+& 7,

 8=$	


&
18

��		�o�j/0
	
	ad	Aih�2/.��M$
cA i5�5�s�v	2�[�Z
�
	�X�U'J
1A2iB��
�4�;�4
�?
�>	�E�D�I	�L�Q�P�U�Z	�g�h�i�j	

�
�
Ai��"	B5X
	=H�R
�O7Ai��72#

Q	k_�b��n��o�m�V���p��Q��nAki�2�n�O��r��t�E�=�!�
�.��A	kl��c������
�Akk��?Q
k���I�[���o��H��G��DA5k��u����u�@�A�r�y�2�o�2�(��4�#����3�~��X��
�!�<��X��E�^�v�m��l�Q�K�(�'��Ak��=hA/k���j�
�1�q� � �#�`
�;�'�p�u�&�L�u�t�O�N�O�+�^�k�p�k�&�>��s�r�A�@�u�y�("��A�3�y�j�u�wQ	l��
���^�)�M��4����<Al��1�2Ql���3���-��u�� �;��m�@��2Al���c� �j��P� �(��4�X
�'�H�H���&Al��IA	l���\�w�E�:�y
�E�;�vQ	lρ�`�o�n�}����&��7��6��'A6l٪!��%��B�i�l�O�6�Q��h�%��'��(��g�D�(�k�r�E�@�Q�l�5�x�2�e�\�)�j�
�{�p�l��|�`��\�2Al��B)jA*l܁��-�j�?�+�f��F�#�^�k�h�i�h	�C�B�m�.�O�r�s�L�M�e�d�J�1�h�9�*�]�h�9�*�]��NQ	m����.��{��p��g� ��L�1��_Am��mX�
�;��g{�l
�A�J�]�^�=�6
��"
��~Am��G"Am���)�`�"���o�H�I�J�K�J�U�w�Q�,�j��mQm�K��N���K��C������X��	�T�#�%�,An�Z�'�r�T��]�U�,*�1�An�FAn��uQn�^��
����t��f��U�S�@�rA/n)�9�6��~�w��E�>�/�P��2�q�t�C�F
�?���f	�=�R�{�t�n�W�G�4�{�t
�a�T�y�z
�+An9�LbA"n2��&�)� �&��h�v	�{�[�r������
�%�����M�B�$���+�*�)�Q	n�����C��8��e�X���	�A#nыH�8	�3%
�p
�)�41��0��E��.�e�8
��e��+���
�

�c�@�CAn�8)�"
�m�l���q
�p�f�[�X�Y�\A	o��	
QoW��A��o��L�U���S��j������!�SAHod�k	�8X�a��|�	�>�.�k�B	�~�%�T�)�s�M�	+�j�_�:�W�t����C�A�V��;
�>�g)��	�h�|�%�
��(�M
�
��O�h�Q�@�C	�x
�C��u�Z�	�|�K�G�F��}#�m
�_��D�A��"���	Agof�DM




�A�B+2	
uv.
/		

#

Ao��Q,?�_�^�	� \%�WH�X
 �{+�|<�}'�<A(o}�

�%�$#�?�! *
 �n�m)
Ao~��3

%]&.?9"2	PA.oh��C'	*	" 
!6*,Ao���3U(�0M�1�lA"#A(ol��	">E
!
	1

	
Q
rW����+���<����d��Q��Z��}����'�J��JAErg��
�?�~�	�X�Y�~���[
�(�I�@�g
���_
�p�`���V
�1�\���n�N�8�E��~�Y�=
�v��$�?�j�*�	�
��Y�e�N �E����!#�E�_��Z�q�v,�Q� �i���H�u��.�8AWrt�k	

		7	4+	
)("
Ar��aeRE5�w$�xaA8r��;�%�$3,(�`�a	�O�N	
�H�I
�@�=
		�WAr���I�(;�
85A6rx��
&
	,	#	Arh��HD
*
�
</A/rk��A	/ 
	@ 	Qu�J�]��V�*���-����]�^��d�����k��/�0A
u0�>�<��Z�
�~��E�HAu/�rAu6���E�!�dQ
uF�S��^�g�������X��=��d��'��&AuT�^��JQ
uY�y�4�q�!��*�H��
����OA	ud�V�E�#�f�W�Z��D��EQuo�u�-��h��k�~��d��m�-�(��D�.AGu�@�@��'�U��[�j�e�V�Q�P��t�k��|�?�g�"��������O�N	
�w�z�[�V�a�d
�+�s��c�zABu��~
�2�3�B���
�5�4	�
�	�����	��K�D�)�(���I�(����!�+�H��C�D�E�P�]�\�S�B�o�U�B�s�Y�B�i�h�E�w�P	�h�+��Z�[A
u���	WQ	v������O�7�o��`��!� �
AHv��w�
����;�`��^��P	�x��]��
�<��2�N�p�g�Y� �m�\�H>�I�{�Z�'�fp�a�&�{�*����'�T���=
�R�#�a�G�
��G�y�6�1�d�{�P�Y��6�;�=�#�L�0�D�A�� AVv��

�%�&	
	


$.	
		=:Av��y�=��>�o�>�p,�5A v��7%
��
��;	
��:�!5)
5Av���^
`Kb*>OA'v���

."#?	"	
	A)v���r
	
E$!	8A4v���!		=
" +


Q	y:�T��H������y��@��C�c��$A1yG�Z3�^�X�J�{�r�[�`�t�?�W�p�O	�T�$*��$�Y��k��:�(��i�h�[�\�	�y�}��W��:�;�S�T�6�%AyE�;�v�	�N�O�J�K��T�K	�~��|�F�G��~�D�E��m	�n��AyD��Y
Qy݁�g�W��k��@��)��(��)�m����G�Ay��f�;
�-�D�$���Y��I��v�
���SAyꁙ&�S�P�E�%�$��8�C���>Ay�k
Q
z7� ��\��>�����t�VAzB�6�T�?�@�]�	���b���s�t�Q��P�yAzE�K
A
zD��+�7�H�K��@�E
Q
z}�*����1������d��)�?��2�ZAz��+��Z�>Q	z���Z�>��5�g����A	z��x	�n�i�w�t�j�G�JAz��QAz���{
�A�&�#�"�#Q
zËT�@��B��=�������^��O����
A
z��	�	�n�������#�-Q	z��d���.�6��1�J��d��wAz�z��Q�0�7�y�7���
�	�X�N�#Az�c�C�\�5
�V�WAz�	#Q	{%� �j������h�9���U��vA{3�J�K�V�o�M�A{1�Z	�T�B�'A{0��Q{K�R�����'��k�0��N��S��v��A{]�i!
�g�� A
{`�]�(�+�L�M�&�|�	�-A{j��	Q{��)���2��	�.��$��)	�X�W��"��_A{��{�5�(�p�o�g�LA{��bA	{����3�0�+�*�+�.Q	{Ěc��0�����b��y��F��%AQ{Ϯn
��Z�.�-�<	��
�z�q�{�r�u�v����#�&/.�y�x�K�Qp�(�!�-��0�5�#�E�B�'�'�*�s�6�B���;�fA{��c1


�C@�BA'{ԁ�
�h�i�f�i�z�l�m�l�m�l�q���n�u�t	�w	�t�u��n	�g�f�eA{ہ�&		)
Q	|ү&��H�H�C��3��z�����,A|ܯ'�E�/�+��1�2�u�t��C��V�c�K�(��A|��v�H�I	�J
A|遭1Q}��2�����'�K�������k�I�0�c��T���HA} �I�L���*�nQ
}*��6�����z����=����3�_A}5�9��5��XQ
}?�8��Z�Q����.�R
��X��1A }K�5��,��t�S�t�/��{�r���k��Y�T�9�V�h�1�H�x�
A}M��N���
�T�5�!�T	�W�X�>�{�z��Z�>�A�<��BA}Q��8
Q}���6��O�S��P�s���b��E�2��T��M�{�����A!}��N��(�l�u�X�#�P�?��*�y�k�`��	�k�;��k�r�/�*��d�2
	A}���8�I	���;	�a��9�i�h�i�fA}G	Q	~�1��2����Z��P��Y
Ai~&�N��{�	
5�3�N���w��<�S��t�8�Q�|��O�j�m�b�T�I
�)����
����Q�T�d���z�4�M�L�!��G�H�!� �b�_�?�6���?�6��K�-�x
���+1�Z�[�A~G��O�N�+
/-
A#~'��B�[�Z�,
�1)�0�1�0�1�0�+� �&�%�"�����
�
���A~-��L/�)	&	Q	��>�<��j��i�6�w�r��b�Q�
��c��O�O��.����g��d��O��N�������A��@
�Y�D���
�Q�P���3�2A
���n��|��*��|�}�|A���f	Q	�o�F���6��;���>��9�IA+�y�Q�i�h�9�Q�$�O�4�;�@�x��z�Z��I�$�T�u�i�T�l�Y�z�G��'�N	����8A�{��j�

�
��~��~��~�/�.�
�/�.A����p7Q�큙t���!�������5����F�����������}��A%��7��n�~�e���l��O�e�v�k�,�;�%��F��,�3
�h��
�.
�s�v���q�A�j��>��8�A+�	�d 		
A6���y	�����
�G�F�z��	���
�
�?�F�C�8
���t�u�t�w��N�L�O�@���u�O�N�v�u�p��(�p��,A���y
	Q
���M�r�X��\���&�M��0��eA��	Q
���c�-��x�p��Q�	��\��]�WA�!�=�0�v��e�y�x��Q	�2�>��7��6��-��q�\�w��NA�@�$���?�@��^�<��7�@�{�z�9�M��a�#A�<���n�m�5�0�1�6�w�v	�^�W�c�\�V�Y�I� �Y
�A�C��
Q����+�R��?�k��~��u������(��-��,Q	���E��%�?��0��k��j��	��6�JA	�ŋ_��R��k�p�$�e�^A��G�JA�Ё�	�Q�ޱE��L��S��h��i��P��+�W� ��tA��)�m�
	�(�y�J�U��T�=�B�;�Z�QA�ꁚ�S��M�L��A�큮%3Q�+�T��,�u�t��3��H��U��>��E�S����'�h��BA�@�O�J�
�A�<��9�a�`�A�=��/Q
�O�c�Z��p��K��,��U��A��A�w�YWA�b��m�p�
;4A�Z�z�h��/�Q�~�UQ	���O�N��?��P�l�m��m�A7���[�O�@�*�_�2�w�h�*�!�@�b�I��N�k�V�!�^� �0�f�W�7�>��!�3�<�#��+�6�Y�\%�M�\�*�=d�y�[A����5�K�d�������i
��+�*�K�M�f!�m�l�"�A����9	)0Q	�_��;���E�P�)���g��?��$Q	�i���������d��5A�u�n�$�A�4��	�C��W�,-�]�A
�s��=�M�z�3
�B��5�4��A�t��JQ���`�Z��b��[�=��X�������	��A8�ė�r�/��m�v�;�>�,�)�^�(�Y�
�9��f��
�~�S�Z�;�~� �B�#�w�r�S��h�i�P�i��t�r�CJ�a�w�T�v�_	
A�Ӂ�_���z�
��G�J�K�G��u�n�
�H
�J
�K�TA�́�R%	Q	���,��t�e����r��q��p�EA����PA���:A���a�(�b�c�b�EQ
���-��z������w�y�*��r��F��?��LAO���>���9�:�
��C�&�q��'�"?@��r�m�
�W� �6
�	�;�D�{��d�/�L�O�N��)�H�b�
��=�R
	
�5�6�C�'���
�	�3�,�Y��t��*	�	�A���b%�G�H(��
�K�JaA-����|
�k�n�s�r�s�r�o�n�m�l�s�t�/�`�5�
�x'��(�%
�A"����m	
"
#	Q
��X�*��V�s��m��l��m��|A���>,�Y�(	�_�)�d�
�,T>��&A-�
�r
 


				
 
��+�8AP���#�n�o�a�n�o�\��
�a�l�Z�i�h�]�
�c�b�h�k��b
�U�l��
�3�=�t�v�h�c�b�]�t�}�|���d�[�x�z�y�\�e�d�_�^�_�^�i�h�c�b�a�`�]�	�b�o�n�k�*�)�f�=�+Q
�
���|��q��,�k���CA���=��	�U�V�g�)�z��)A��A���<�x�}�|	�m�n�o�j�k�j��m�j��k�j�Q�U��C�w��c��h��9��&��!�� ��
�b��.�x��?����HA/�h�/��x�/j�b
S�Z�y�	�
�d�	�d��W�	�t�W� �a;�\�S�C�c�\�F�L�<��q�;�3�d�z��d�+��={�v�u�D�U�\AI�k�3

				
	�.�7�$�

�^�[
A�����
* 
<�[S�Z�G�FA,�i��O�j�i0�h�[�r�d�e$�h�a�`�i�y�x�`�a�`�e�
��`
�a�`�a�`�c��A$�q��G#

 Q��|��H��=��T��
�C��N���A�W�!A��Q
����X��e��d����A��b��]��\A�*�Z�/�g�`�k��@�YA�6��@	�AA�/��y������#�j�I�#Q	�P�.�f�P��b��u�X���I�IA5�[�
�!��6�G��n�8�M���#�V�J�9�
�:�[�>�r�g�6�>�r�u��d�u��-��4�i�!�L�O��9��%�K�/��y�`�&A�]��d�j
�k�j�o�n	�K�J�M�|�N�M�L�s�r�OA�\��	Q	�ڴ�3�� ��s��A��#���+��J���U��H�0�o�-�,�\�[�bA��&A�쁚c�h�i�S�:�7�L�Q�8�7�LQ	��`����.����2�i��k�aAF�&�5���7�<�h�e�C�D�K�N�'�"�M���F�G�F�T�U�w��h��� �G�J�%�&�o�n�I�[�"�5�� �Q�P�U�X�1�n�.��SA+�-��	�3
�~�
�4�J�}�z�4
�5�~�K�J�O�N�O�7�|�}���K�C�z��A�@��B�I�H�G��9��HA�0��
�(Q
���A�E���/�R�V�g�nA	���H�D�%�5��r�
��]��^Q
���u���?�a����
��w�X����I��RA"�Ε*�J�b�kd�
�(�u�t�_�<�E�0�CX�.����H�)
��+�>�<
� )�y�R�]��y�Q�.�	4�MA+�ʹ}��	
�r�qP�b
�c 
A8�ҁ�v�7�Q
��5�4���9�6�
�L�
�
���		��	��'�&��F�G�`�&�'�b
�G��	��E�D�E�D�M	�Q�^�0�=	�<�9�}A�ρ�<

e
	
 !Q
�G� ��D��A��V����YA�_�|��A�U�*
A�T��1�V��?�>��^�a���7�Z
�D�#�"�G�F��]�\�Q	������G�U�V��B�+��E�*A�ʒ�q�|�0�J�!�1�a��4
�P�UA���>�`�a�j�k
A���H�\�i��D�]�\	�C�@��"���G�T�
�f�#�E
�j�O�&�S�|Q�%��!�����(��O���:�H�W�~��uQ	�3�S��J�+�/�X��]�����#Q
�>�X�[��&�v��I����7���A9�I�Z�[�^�A�9�&�$���]�^�M�L�	�c��`��k�A�,�7�2��S�7����Z�8�}��|�!�"�A�\�1�K���\����G�&A�J��@�N�_�^�QK�R�?
�j�]�^
�u�N� �u�t�u�0�c�$A�O��|7
Q	��d�J�#��H��]�k��AP��f�N�/�4
��� �;�Z�]��J�O�`��
�
�b�e�u�j��p�W�@�=�Y�o�V�z�	�T��m�"�S�D�/
�*�[�x�h�-�6�i�`�Y�,/e��Wi��E�#�&
�j�1�+0��B�0�M��8��4
��C��<�;�T�2�uA@��v

�Z�[�d�e�-�jH%�>�G�>�?
�>�=
�Z�[�d�aA�,�VC""
)			A�-��~i
'$NA8�/��0�?�:�=�<�=�<�=�:�=�8�9�8�9�6�5�(�/	�$�%� �!���
����A�?��"!&	"	
Q
�ֶ8��G��.��m�	�K�t�^�A��|��%��-%�"�KA��:�
�	
���
��zA�쁒}�t��6�7��
��a���,�b
�i�/
�&Q
�B������0���'��_��t��mA:�N�L�}�~���u�{�\�	���	�G��5�f��*�w�B
��w�z
��G	�N��u�z	�+��{�Q�s���=�:�/��_�b�S�:�K�NA�O�zR�O�N$�K�JA2�M��M�������	��������z�w���{����	������)�vQ���o���B��?�r��� ��)�#��n����L�$��G�tA�/�h�x�I�e�9�eA
�.�[
�
��z�A�3��W�4�5�4�_��m��6�'�9�^�9�o�&Q	�i��x�c�6��A����E��$��+��~A�u�d�H
�e	�.��T��
"��_6�"�n�U��h�#�c�:��F��W�hA5�|�e
�t�}	
�h�e�h�e
�r
�_	�V�W�L	�S��,�-A5�s��%�h�e�d�e�E�D
�+�
�$�#�H�I� �Y�A�	� �Y	�d�#�N�O� ��@�G�$�Y�I��L�M
���#
���L�M���L�L�A"�t��z

		
�yQ������}����'�C�x�2���w�xA?����A�D�5�.�I�H6�� �7�B�U�K���)�,���
�<�D�q�4�1�c�L�A�B�u�p�;�h�%�f���>�)��}��R�$�k�TA&����Y���
�~���K�J�����4�`�"�S�R�O�N�)�(�)�V�P�U�T�1�0A����&>
Q
���4���r��s�
��*���z�HA)���E�6�D�s�n�Z�U�+��B��A��o�(�g�|���-�8��O�]�&�I�I�y�r�K�H�K�$�A�|�+�1A���"�|�}�0�S�c�f�i
�t�uA����8#Q	��)�&��Q��P��Q�	��$��iA=�
�R�C�5�v�5�4�!�B�=��"���!���4�5�~�E�D�6�]�$�q�r�!� �m�l�G�@�W
�~�Z�3�6���&A"���\�B�C�F�G
�}�l�q�o�n�o�,�?�o�*�E��>�1�p�>�1	�0�I
�H�I��	A���?,
"
 Q	�Ƹ������J�N����P�<�
A�ӌ��=�k�d�:��	A�|�`A�ܸA�Ё�R�K�H�I�y�j�T�A�.�9�H
��9�y�D	�M�6�1Q	����L�y�Y�?���e��~AE��B�&�K��k�%� � ��K���$�\�1@�02�C�3�G
�h�5�Q���6�q��4�E�T�C�P.�D>�H�Y�/�P�[�T�^��e�V���s)�f�-�^�-�
�.)�M�$�i�8�� �$N���D�C�w�j��pA�ܰ9�?�B
	�(Ao�!��H�G�J�c
�b�K5"	� �	H��			
�L�#�/A�0	�'

�p�q�l
�k�l�mA�9�3�&�7�'�BA��8KZQ8
�"	RA�V��*�p�*�=
+$0A<���u!D
(B
V	
"
A�&���<	�*�/
�>�A	AM���YC
?
L

Q	����P�]���H��;�C��*��O�A�ɏ%�$���}��bA����c�\�c


�H�3		
		A%����2�?�L�M�L�M�:�!��s��/�b�3�x�-�d�e�f	�]�(�~�)���$�A�.�)�8Q
�!�4��^�M�����+�*�;�qA��-�;�h�s�z�U�(��>�	�d��.l�O�N�i�p	�E�`�Q�8�a�X�1�F�m�f�_�Z�S�k��6
��l�|� �f�U�z�T�_�S
�Z	)2�M�R
�`�e��|�G�]�(�2�/V���
��;�:�7�6���;�:���x�d�e�	�2��d�t�-�}�6�;�D�
��}�M�I�A���A�6�n
�
?# �:A�e��A�]7&�A?�1��g� �!� �!
���	�k�x�o
�n�m�h�e�d�a	�L	�M]�H�I�F�E!�@�E�>A&�=��C
{"

p	Q
����\��`�a��u��x��c��<�aA� �O�
���/�`��6
�W�V�]�`��A'��
���
��8�;�8�=�<�D�C
�A�B�<�=�G�n��}�|�q�L���FA���}
C
�F����5��#�Q��h;�>��^)N�"�s=2�ZC�<��}C�<��i4LC�~�E�-C�B��SCB�@��~�%�H�q�A����Q�R�{��[�_�E�:^�C���l���t��q�0?���o�r��%�?��j���W�3��J�OG�b��E�3�� �
G�N���Y��O�D����g�/��K�	���V��_�[
�*%�)�2�).����A�d���	��t�Z�
�>2�8
�t�C&�>�e�h�
$�,�I/�^@�
!�W%X$�
(�}:�C�I�	�S��t+��;;��I�o�oC��@܉��Q�n�,Ew
��P�>�L�x��72~
�B �5I��'$��'�I��!,S
	[�W�X
��(��h�ol�Q�;��z�)��f���V		(R��i�2W�����B��9���E.��gO
�c�(�
_��v�<.��TB�-0�,�|"]	��e~2u9/�5s�R
�D("�Y#�l���$
�Z��f�
�(1�	�%�N��l�B7�=(��l{
�K�{��$�$�64���&�wB(2 ��>
�L�#8-��a_�iJ�v4�q���I�w7�
����6��@���<�m��'\:8w��
H��&��{�{"���?�m@93*G�w��)�<I�i�b�%$�<�.c��{�U�v+� H"��8�JR�g�nz.��2�~�.
��p
�go��y0
�;�s
�p�Y���>�H��k"��K0!
�$
-'3�\-�� `�	#RaF�E
��'��^ �>� (6��u �+"��h093@9
�d&��B
Rv���{�2��*�9�/C,�@���Yb"��X�:�[X��M��iB�(
��*��Y��8�]�m��Yh�����Q�S������,�6�A��I�|��t�g��g�f��8�I�*�,F��	��}�0R>�xA���)(uA�z�W�	�[�7�f�e(��g�N-�w&�dA�(��OQ
��Z�G��c��>��_,A����"��6Q+�0�F-8	_N�?�$Cd5�&�oD
_",a�w�~��e�?nA5Z9:P�L�}/Q�]�w�I3"P!�l�GjK�[�~�8�A���	5qpq2Fw�S?	B!��*�Mz�	A��L�g�N�h�\A��a ;>%e��!�$�*�;�A�G�X���0na��s���F�#�*�K�P��D�\�a��m�9�=�{�i�g�!�*�U�y�	�*�$+R�J��
U��aN���� �9a�'!`	��v��K�7�H�M�:���!�0�[�Bq�=�e�4�1���C�8�c�X6�e<�p(�w*�����%_�7J�E�2�v�7�o�*�_�{���'�7.�@�+�[Z�f�C�u^��+�!/�(�|�u=�J���l�,��h�<�l^�~�I��-�B(�M^�Z�L�0R�BL�Q\�`S�h0�s�=�;��=�>�T�d�f)�og��?�j�e�d�f��|T��� �>��\j�{
�u �$�	��.�!�%j�9�<��P�{�]�G�$v�>�A5�KV�\�g�u-�	��
�/��
��[�/Q�>�K�tx���g�-�1��U�~��r�Y�W�K��K�F�IP�Y��y�|�G��k�J�M�r�_#�hF�r�t�d����C�_��pT�~�m�33�=�5�i(�m�@���1��E�J��bP�x"�~�:��+�*�0�7D�;�>>�F�H�`�i	�k�m>�{�~��G��0��
�T�;�so�{A�%��	�V�%�(a!j�!	��jH�v�a�r�n�F��~�0�7�7�E
�&�5L�?���X�^����`�y�/�"	�&�)�_��V��~��\��D�B��Ra t�"	�(�I��a!V�9a
$��c�I	��+	�2� 	�]�h�v�.�-a!��,�*�X�Y�)�p�x�C�B�.�a�L�K�b�q�|����� �#�(�*�3��]a.��	aW��sh�|��k.���v���q�e�O��p�rN�m)�v��a�u�C�X)�\�^�;�q��w������=a�G9�K��U�@�^�l�t��	��#U�(Q�,\�2�t�S�-�j�z�|��
�S�#�>�0�
�:U�?x�HK�O�K�m�:��4�t�"��8�-�@�H�Ky�Z�_S�m�
����
��9��L�(�1�2y�<�^�T��r=�x���E��� �g�0�f�Q�f�o��|��
1�&��(�)�p�O�v�m��}�h�1��<�^�E��Lr�S��Y|�^a G��g�8�}�(	�_�Q�B�iZ�:�6�I�D�O��E��F�u�P	�l�wa�5]��=���x�@��E�R��l���~&��P��_�D��k\��sE��z@��+���n���J��T���9�c��"��M!��R��
,��'���k��-��0b��:��<n��I�(��_�F�����=e���|��R��V�"��p�'��q��H$������K���Y1��*���R	��y��m���
��8�����5B��?���'��T��Zk��H��k��o�>��X���F��'��)��,���.�l��K���<�W��u���D���(�� ��R2���M��M>��V@��^��d:��p�
���$��g=��P�����2,���#��D�`��i�"��@��BX�����$�6��X���G�[��S��V��e|��i�g��t��4�����)��)^��2���?�;��N��Ps��]T��g;��nu��*��L����F��.1��4L��B�3��Fq��L����[��.��*���^��*�!��8��h"��"��p{��yQ��d��	=��P���#���'��b��5�t��@C��k`��H�l�����i��x.��W��3��
$���������"N��'\��L	��N� ��`.��l��?��t��w�(����BW���
��E�%��R���da
���]��L!��T��Z4
��_��jB
��l��w����+��ka�=Nt��W�U��hn��x��{p��	��0����
��+��%>��.	��1��3�g��N��Un��j��mZ��uI��}���
���L��!{��)j��3��6���B��E!��J�a��m��qo��|!���
��"�J��8��;��?&��F���Q{��\l��d��h�U��x�Y��	G��:�������+q��;��=�0��O��Q;��W��Y��_���n&��r��u�)��$��(1��/�6��?��A��D���V%��[�x��n�1��j��
������((��+��.��1/��6���D��F��J	��La��R���>���P��3G��8q��CX��H���R���jH��q�H���<��Q����p��"@��)��,�k��@�R��s�	��y��8����>��$���@��D�N��U���g�O��
�
����n��&��(-��1�Z��N��P9��U[��e\��pM��yI����R��
���E��$�A��A���NH��T���hg��q%��wI����W���
��-��0��3�|��V1��Z$��^#��a?��hJ��q��t0��}	���I�����#���9��->��3��6��;��>��A��C��F��I%��O��R3��[6��e�"��~2��
���W��$���,��/��2���DK��M1��V�q��z��}���s���� 3��&��)��.�D��L��Q=��W��Z;��`:��g{��l\��vC����L��
c�<�����A��E��ā%��z��P��U��p��a
�`�T�/����$�{/�%�$p���O�Tq	�5�[�a�]�k�e�c�g�_�_a��"	����0�c>�G@�IPK
!<<؃�3chrome/pdfjs/content/web/cmaps/UniJIS-UTF16-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�UniJIS-UTF16-HA!��bA	 �U�?�dA��M��6Q#��t&+"'�+�B�9Q%�G(A%%�o/Q	%@�uA &���z�wz!O�0�/
]��A'�A0��KQ3��J�W�V�mA3�


��K?�A3�1$1
)')��q
A3�L


	a%�9�7�=�;�A�?�j�]�`�U�X�w	�~�6�O	�c�]�H^����[a0�k�g�6�E�P�T�\�b�h�t�w�|�~����ma#��^�d�I�X	�M�XPK
!<���`[�[�3chrome/pdfjs/content/web/cmaps/UniJIS-UTF32-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�#C\a
!�GS�k]>�d��_?V3�C���M�T�+ 2!+�V�S�L�IS��tA
�~$�c���
S
��n$�c�U�P
�&%$)C$�)
�f�7�![S .��s"'��A��"�}
�";�9�6"��B��;
��|�,"�m����S/P���'
��j�>"�kOL
i��O��fO�>��_c�0
�m��("��[��<��SC�i��P.S��2��j���	�
���C�����k9S
P�X��"��"O(
��zC
^�W-*'0#rSl�6B''�$�c�bCx��1TI�6��7�ZS��94
M�-�|3��)F�Cm�b	>M#=:B�@�;	���J�<O@�0�G�"8��F.��q.�J	�N
M�y�u�>�:�E�<fG>+	'c���h	W~�9�2	
�|C	l�
;ls9'�
��V�h�i� N�.:�K� �e�,�d�[�5�J��|�O�r�]C3 Z�Z�+�.�/�?
�x�vC	F
�Q'�

�j�w�
�*�"e4
�?	�^(��D@�����
�9�zCA��c��,
�`��2�H�j&�_;/1� !�f	�P�c�Z�w
65
�h6

4!�;4/Q�fB��.+�T��*�
U	�9�.��53�6�?C��$<(�#��Ca��J��#�"k
l	�)�@�]VI�b0#
8%#
#'`�p�|�k�`-�k]�FI�[-�&�3�w;<;� y�e�5�@T�
K�9G�$��+��C�U��p�t�m�Z�$�;�M�
�v��3�\�7��V�=�/�j�U�_(�A�K�v�9�y�.�@�7��#�C.���eC.���:C���K
�_�!�W�:�r�S/�0��i�� �w��w�8�5�R�t
�{�v�\S/�S��h�>�
�c�r�/�qS�/#�&�?�
�C�Z��c��c�T�c{�i�7�&&�S�����o���(�_�4�u��w��$��n��B�S�^�n�}�H�a�$�/�7�R�4��	�w�>� .�Y�O�@�4�<�{�0��P��S�_�(��VKp��A��X��]�X�`�-�t�G�W��-�e�"��N�O�x���e?�K�j8�]�/���8�G�U�F��
��a��h�(�.��\�z(����$��X��[�o�p��%�t�:�+�T�}�R�a� �G���6]�C�3�!�Y�"�o5�n S/��N���<�7�J�a��r��C�8��w��z�C
/Ӟ~/�_�H
�+��<��D�w*�3�`C00�:
�a�aC0<�Bc^S23�Op}��$
ly�nC2Q�S2��g�

�D�5�,�=��2
���^C3�pC2��ZC2��{S3�j�r�[�Z�CN�0��+C23�"��
��
��*�?�
�#�F�?�#�,�y�`'�>�M�;�:�J	�=�K��A�&�w�P�y�{C3�x


	�5
&C4�$�&�b9�S�6�m�h�N�	T� ���
�`�(��2�n$�Q�@"�Q�cC4��lR#�.�g
O�8o��x�m�jH�9C�'4��Z%=,'0"�k�z�p�k�H
7(
�p�a�5�5F>5$�LK&&.	?��i�GT4�\�#�]�r>
�K4##P\	8
�9	�P" �Z�A��8>F�2�i5%"�$�09�g��
�B^�g�6"
�\�_�,HBP
&�7J$(�*V
8�C�n
!4
3<	5&Y�1��<&��C$�`�j0�Q_ 
\(�pD�G��$%�	�f@�z�y6�d	$�.~.B�dC�D��&�r�}v%Qo�
�3SN�*�9�!��#����}�D��R��1�U��CVN�L�^�i��O�2�w�l�i�@�m�3�2���M�(�7�X�/�u�ve�	�R�\��g�f��N�� �z�3�
�i�Z�j��9�{�:�	��~�W�d�Q���}�x�X�S�w�h��N�
������I�@�G�F�5�K��r�#�yC
N�Z�
�a��e�d$
"��
CN)��U�x�y	
�t�u�T�"�y�x�w�nS	N݅����g��N��/�F�7CN��9�z�P�A�,CN끆b�F�GCN聤]S	N��Z�4�d�l�m�g���p��SSO��a��	���4�<��k�Y��M�'��\CO�q���L�xCO�kCO��f�x�3SO3��d��)��(��w��z��k��G��.��Q���PCO@��f��u����N�;SOM��\�u�C�R��6��y����a�r�/�����j���b��p�ACOi�)�Q��0��\�i�COz�pCOc��k�	�5�:��>�?S
O��%����k�!��
��W��V���������r��oCO��.����3	�$�[��q���/�5�l�CCO��<C
O��s��M�L��<�L��z�S
Oȁ�~��%���*��������}��|���COז�F���{�X
�g�.��B�0���
CO�l�(CO܁��#	�"�?�$	���(�u�dSP	�T��`����2����x����p��e�a��>��wS
P�*��6�m��O� ��~�z����ySP!�B�z��i�d�o��f��
�I�T�;��(C,P6�^�N	��y��X��q��A�(�t�'�F���e�O����?�n�R	�E�$�q��)�~��G�>�@�/�.CP@�?
)#CC2P0���� �t�u�5�{�$�y�x�y�5�{�&�'�&�}	�|�7�6��-�0�5�}�0�1�(�;�:��)��
�o� �1�9�lS	P၇
�4��������L��k���+S
P�_��J�R�T��j���I��CP��b���E�h�	�qt�E�D�9U�CQ�
CP���+�����8	�)�(�C��(SQA�8����!�1�h�d�;�^�C�:��d�?�)��r��gCQR�r�9��B�?�A��)�h�.�7�Z�m�[�
�M�`��sCQd�JCQS��;�I�F�_��T�_�
��W�>�H�S�SQ��~�#��P��3������6�7�#�"C!Q��Q�3�+�D�w�v�s�g�f�o�t�1�j�D���Y�X�/��[�~��+�*��5�R�p�3�2CQ��K �a�`CQ�����	
��1�`��j���.�-�<�=�'�P�gSQ����p�Q���u�s��L��E��zCTQ��5�F�}�
��=�}�A�j���C��:���y�x�Q�P�)�?�I�.��-��B�;�Q�$�`�	�]�`�9��(��y�7�>�a�R�3�8��&�x�}�r�3�U�U�p�{�$�7�jF�P���Y�Z�o�Y�n�O�$�i�h�/�0CR�NG�a>�`	�X�Y�GC1R��o�#���N	�1	�$�g���Z�'�+�P�'�C��*�+�N�M�N�#�I��P	�O�N��$�!�z�g�
�Z�}SR�R����)�h����v��a�`�o��*��=�3��n�A�@CR��|�{�1�t�K�b�
CS�SCR쁥a�
�H�B�g�#�B��,�E�DS
S�M�!�x��V��u�B��0������K��s�SCS#�O�A�@CS$�UCS%��i�K�T�U�)SS8�S�{�m�o���U�Q���x���T��KS	SE�V��m��
����
CSQ�p�9r�]�J�/�h�.�<��� CSa�!CSY��q�#�J�%S
Sl�"�J��W�+�^�X��;�^C#Sw�`�S�N�{�|�I�H��U�
�p��H�J�y�x�A�n�P��{�c�8���UN{�<�&CS��WCSy��	�'��(�R�I�L�Y�U�*��*�U�T�-���~�W�V��(
�V�{�7SS�Y��J��'��L��]�Z�Eq�5��
�6��#�#�w�P�H�W��E��#��p�7��1�R�>CS��r����?�ST�y�/�0�i���/�^�E���`�`C&T��l�C�8���C�<��b�*�t	�;�:�W��}�]�V�t�
��/�B�'��� �vC	T��4�O
�0��
CT����"�^�_�#��]�\����G�TST���%�X��m�y�����Y����CT���H�7�%��p��7��CST����t������z��	��<�6��5CT������ST���L��}��B�8����=�8��2���$�0��p��{C%TǢ
�5�@�=�L�+���m�n����J���o��5�}�:���+�2�7�.�m�=�*
CT��^^�C'T��2�D�E�T�n	�o���o�n�o��E�\�a�J�K�^�j��
���O�N�s��p�Q�6�q�S
U{�4��H��S��Z��g��l�n��K�C)U��<��z�o�t�
��O�f	�O�`�M�c~�&��

�4pi
�M�P�1�W
�~�o�w�h
�YCU��_C,U��:�Z���r�s�r�u�	����s����}�|
������j�k��i�b�m��z�%� �SV/�U��8���X��<��C��X��Y��\��[��TCVB�_	�6� 
�k�v�S�V	�1�#�<��k�l�Y�\CVS�E�kC*V;��*��{�i�h�x��k�v�w�v	��i�f�g�d�

��u��~��g�d�%�*�r�}�%� �a�b�%�*�t�}S
VȢg��L�i��H�w���3��8�t��/CVӢjSVעk�_����=���J�e��9����
��CCV�
�R�1�� ��e��V�}�~�O��d�K�J��*�y�r
�`�W��6�.��y��_�6����;�4�c�b�%�1�V�y��v�K��6��_CWY�`FCFV��P�d��u�t�t�k�x�p���m�h��y�{�p�m�h��u�t���t��o�r�I�H�y�	�I�H�{��x��z��y�z���	����W�R�����c�-�~���S
W��e��x���O��l�����v���3C9X�
�y�@�D	�c�b�)�7�O�~�_�Z�Y��%�.�c�L�?�B�54�\�I�a�@��.�V	����g�v
�S�s�D��M�L�U�P
�#�� �O��>CX/�21�(<�>C2X��a�y�x���
���~�	�v�o�~�	��~	�}�f�	��]�p�|�
���|�}�|�a�t��z��e���
�`���z��#SX��h��y������z��_��z����vC7Xܣ1�)�t�|�:�)�_�P�G�|�3�2�k�j�-�1����:�
o�|�q�R�#�P�Y��6�]�\��P����c�^�c�d�x�O�T�[CY�iGCX݁�y����"�"�|�/�.�/�w�$�%�$�;�8����=�<��1S	Yg�M�y�z�Y��J��w��B��[��CsYs��&�t�3�2�u�4�T��|��8�i�z�O�a��E�"��3�.��s�A�|�$�u��� �X��]
�o� �B�_�r�/�2�O�J�g�%�t��#�!�D�� �=�<�;�g�*�
 ��K�L��
�=�V	����	�G�J�	�#�$
�A�B���w��a�_�H�9�t��!� �]�\NOCY��n�CY��q
�
��m�l#

	,4�?�>C;Yy��j�?�%�`�?�<�?�'�&�8�9�6�3�7�67�=�:	�;�:�����K�J��������
�aC)Yr��	*

!
-IS[����H�G����H��I��J��[������e�[�h��*��{S[���C�l��5��*���}�p�>��,�bC[��n��D�O�R�z�5��W�����CC[���w
�C[���=
S	[��S�:��"��g��G�t��:C�![ɤ���e��J�O��B�W�M��o�X�+�D�)�(�G�B�/�}�L���/�/�L��}����1�k��'�s��u�t�S�ZI�R�2�e�b�i�/�P�C�B�k�`�o�`��s�n�w�E�h�#�J���	�[�d�
��� �;�2�
��w��G�>�[�_��:��K�F�O�J	�� �_�h
�s�r	�!��w�r	�'�>�e�d�u�H8		�o�n���5�KC[�sE<�G�C�P:1bC\�*�?	�2N�O
�N/G�%�C-[΁�z�{�z5	�
��������5���

�%�&�-�0�5�2�5�6C[ׁ�z�$2�%
	"o#2$CC&[́�D
$ 	

"#


S
]����c��������o�X�Y�e��PC0]�m�	�W�^�y�s�j�e�+�x�7�.�T�q�4���m�h�[�=�
���o��O��"�� �q�b�G�B���a�`
�!�>�UC]�� �}�|)C]���
�P�}�|�U	�-
�z
�y�&�T�M	�v�RS^w����)��$�/�7�C�$����)C&^��?�2�2�31�
��-�g��n�g�C�g���`�o�^
�U��&�n�K�L�9�/�l	C^��0�r �d�_C^����N�}�z�U�'�&�'�x�U�'�zS^������t���5�j��2��W�����W��V��w�S_���-�,��U���M��L��+�`�&C
_�w���F�Y�i����s��t��}S	_!��d�F�^���$��h��%C_-�$
�3�^�5�z��3�B��~�w�z �1��;�X�O�t�d�)�i��iC_4��!!� C_.���-�=�<�B�f�+�*�	��C�@�A�(��!�	�H��S
_|�t��L�b��1�-�`�F�7�6C_��9��-�R�h�e�}��F�GC_��<�"�#�"�!C_���S	_��>���!���f�}��9�e��dC_���-�L�*�w�h�F�W�P�H�;�	�J�?�6��|c�?�:�m�v

C_��&C_��8����3��� ��5�,��5�4���9
�*��S`�V��������p�*��'��z��s�C�C5` �5�&�#�4���
�+�2�S�J
�7�8�g�0��>!�!�F�#�o�A�>�o�:��(�~	���I�2�k�^�!�pC`]�'C`��&���;�(
��������)�(	��E�D�-�,�)�)S	`���7��
��8�R��{�f��^��_�S	`��O�"��Y�"Z�����C`��c�&�?�>�	�z�C`��
C`���!��.�1��'�"S`إz��R��/����E��f��5�c�4����GC`�g�K�F�>�S�F�_�^�#�&
�k�]�D�%���"C`��C`偨@�7�)�\�=�<�?�>�;�z�>�;�%�$�8�A��D�C�B�?SaD���j�;��I�����}�/�e�l�C
aS�!�Q�L�i�q�R���Ca^��(�u�tCal��LSan��6����\��q
��v��nC5a~�Ge��S��P	�Q�i�a�
�7�A���z�5�.�y�z�i�n�=�P	���w��Y*��Ca��zC%a{��*�'�&�L�w	�2�B�O�)�v�E�3�v�E
�r���4�p�q�F��~�Y�V�W�V�S�R�a
�/S
b�p�,��8���9�#��Z��9��:�$C?b&��l�o�n�+�m�V��m�4�w�
�I�A�3�$�S��M��)�
�(�3�<�y����r�=��,�w�n�/�x�T�K�m�~�5�r�Q�{�*�|����s�d	C
b)�^�C�_� .�=>�<Cb'��f�e�`�_�^���a�-�,�n�u�\�i�q�p
�lSb̦i�	��"��)�M�F�-����H�1���5�p��v��9��AC;b��M
<�B�	�=�D�
�k�h�f�3�N���Y�L�U�q<�
���
�"�2��1	R�#�~	�
�1�w
�X�#���U�V�?�<L�,�?�w�<�G�8�s�d��yCb��e

�c�b+C*bꁨx�{�q�h�k�j��~��~�k�h
�e�b�|�}�e�`�}�|�c�b
�a�`���7�2	�a�^�e�dSc��r��]�#�k�h����������D�E�j��A��VC�9c����2�I�L�g�p�5�I�|�q6�{��2�+�W�
�9�>�q�|
=	4��c�X�q��>�8	�}�t����)��V	�C�:�q�7�E�l��M��F�P�/��c�n	������"��v�1�:�E�@MPAB��:����s�#�
��� ���?�@�/��V�#�4�(�w��\�|���E�&�m�`E�!���1�0�7�"�H�@��
�h�9�&�2��i� �^�5�.�u�x���O�P�O�8�A�]�H�n�{�e�Z���;�6�����P�e��,Cc���O<,� <0S�+�~�1Cc��s
�1	�N�`1
N�=�@-< ��Cc偃l�JXsC&c���"+*			
5		'	
$CcꁗJ'
9*1		C-c���%!	#
"4
Sf�k��6�o��w�Y��<�+�(��W�}��R��k�e��N��+�S��O�l�!��R��Cf4�j�*�/�>5�G��D�a�Z�_�BCf3��v	
�.�'�W�~�Cf7��L
S
f]�q��6���3�����V�=C-fi�i�R�g�,�C�c�T�#�p�c�Z�� �{�f�q�d�Q�R^�2���!� �%��2��1�@�Cfs�"%C fj��Q�	����S�L�}�5�4����(�)�?�f�7�z��[�Z�E�D�E�D��Sf���'�P��m�	�[�p���NC.g�K��$��=�<�Y�n�h�w�S�K��3�Zm5�/�B�}�N�.�I��6�9�J�1���'�"��-�&�#�"���P�$Cg�(WCg��
�����i
�7�6�f�a�`�S�O���Sgl��i��;��H��M�,�y�x������`��;C	g|�$�}�b�"�9�$
�gCg{��2�k
�j��YCg���j	S	g���� ��#��L�T��1��Z��o�>Cg��n�t�]��x��!Sg����0�8�9��]����[�:

Cg��(�P�N�K�F�%��m�F�#�H�P�6Cg��+C
g���{�h��~��~��)�&�|S
g�6��h��o����e�%���$�Cg��*�d�}��n�o�T}�1�v�_�g�F�Y�b�U�]�dCg��
�'Cg���J�%�z���~�i�h�~�}�5�0�~�}Sh;��~��-�,��~�x��{�	5�
��� ��C"hH�
�j�{x��X�>�9�j�5��I�^�?�& !�}��u��%�&�}�~	�[�,�E�ChR�,;�!ChI���v�w�r�3�@�C�4�5�4�w�A�&��u�h��9�6S
h��K��v��o��6����Q�����)C	h��Q�[�]�p�=��C�NCh�/Ch���Sh��0�v�6��5��4��O������|��gChߘi�
�I�Z�-�&�e�x�DCh��.Chꁪ��A�@��]�pSi�� ��s���d�]��
��h����2�q�t�d��t�=����C@i�@�c�
� 
�e
���}�S�

��L�\�'�$�=K��1�.�//�c�(�}
� �?!�Y�
�B�/�n��d��g�2	�V�	�+�6
�}�W�N�K�
�UD�\�3��B�
�J���S
�H�e�l�)
�Y�L�uCqi�p+& 7,

 8=$	


&
18

��		�o�j/0
	
	ad	Cih�2/.��M$
cC i5�5�s�v	2�[�Z
�
	�X�U'J
1C2iB��
�4�;�4
�?
�>	�E�D�I	�L�Q�P�U�Z	�g�h�i�j	

�
�
Ci��"	B5X
	=H�R
�O7Ci��72#

S	k_�b��n��o�m�V���p��Q��nCki�2�n�O��r��t�E�=�!�
�.��C	kl��c������
�Ckk��?S
k���I�[���o��H��G��DC5k��u����u�@�A�r�y�2�o�2�(��4�#����3�~��X��
�!�<��X��E�^�v�m��l�Q�K�(�'��Ck��=hC/k���j�
�1�q� � �#�`
�;�'�p�u�&�L�u�t�O�N�O�+�^�k�p�k�&�>��s�r�A�@�u�y�("��A�3�y�j�u�wS	l��
���^�)�M��4����<Cl��1�2Sl���3���-��u�� �;��m�@��2Cl���c� �j��P� �(��4�X
�'�H�H���&Cl��IC	l���\�w�E�:�y
�E�;�vS	lρ�`�o�n�}����&��7��6��'C6l٪!��%��B�i�l�O�6�Q��h�%��'��(��g�D�(�k�r�E�@�Q�l�5�x�2�e�\�)�j�
�{�p�l��|�`��\�2Cl��B)jC*l܁��-�j�?�+�f��F�#�^�k�h�i�h	�C�B�m�.�O�r�s�L�M�e�d�J�1�h�9�*�]�h�9�*�]��NS	m����.��{��p��g� ��L�1��_Cm��mX�
�;��g{�l
�A�J�]�^�=�6
��"
��~Cm��G"Cm���)�`�"���o�H�I�J�K�J�U�w�Q�,�j��mSm�K��N���K��C������X��	�T�#�%�,Cn�Z�'�r�T��]�U�,*�1�Cn�FCn��uSn�^��
����t��f��U�S�@�rC/n)�9�6��~�w��E�>�/�P��2�q�t�C�F
�?���f	�=�R�{�t�n�W�G�4�{�t
�a�T�y�z
�+Cn9�LbC"n2��&�)� �&��h�v	�{�[�r������
�%�����M�B�$���+�*�)�S	n�����C��8��e�X���	�C#nыH�8	�3%
�p
�)�41��0��E��.�e�8
��e��+���
�

�c�@�CCn�8)�"
�m�l���q
�p�f�[�X�Y�\C	o��	
SoW��A��o��L�U���S��j������!�SCHod�k	�8X�a��|�	�>�.�k�B	�~�%�T�)�s�M�	+�j�_�:�W�t����C�A�V��;
�>�g)��	�h�|�%�
��(�M
�
��O�h�Q�@�C	�x
�C��u�Z�	�|�K�G�F��}#�m
�_��D�A��"���	Cgof�DM




�A�B+2	
uv.
/		

#

Co��Q,?�_�^�	� \%�WH�X
 �{+�|<�}'�<C(o}�

�%�$#�?�! *
 �n�m)
Co~��3

%]&.?9"2	PC.oh��C'	*	" 
!6*,Co���3U(�0M�1�lA"#C(ol��	">E
!
	1

	
S
rW����+���<����d��Q��Z��}����'�J��JCErg��
�?�~�	�X�Y�~���[
�(�I�@�g
���_
�p�`���V
�1�\���n�N�8�E��~�Y�=
�v��$�?�j�*�	�
��Y�e�N �E����!#�E�_��Z�q�v,�Q� �i���H�u��.�8CWrt�k	

		7	4+	
)("
Cr��aeRE5�w$�xaC8r��;�%�$3,(�`�a	�O�N	
�H�I
�@�=
		�WCr���I�(;�
85C6rx��
&
	,	#	Crh��HD
*
�
</C/rk��A	/ 
	@ 	Su�J�]��V�*���-����]�^��d�����k��/�0C
u0�>�<��Z�
�~��E�HCu/�rCu6���E�!�dS
uF�S��^�g�������X��=��d��'��&CuT�^��JS
uY�y�4�q�!��*�H��
����OC	ud�V�E�#�f�W�Z��D��ESuo�u�-��h��k�~��d��m�-�(��D�.CGu�@�@��'�U��[�j�e�V�Q�P��t�k��|�?�g�"��������O�N	
�w�z�[�V�a�d
�+�s��c�zCBu��~
�2�3�B���
�5�4	�
�	�����	��K�D�)�(���I�(����!�+�H��C�D�E�P�]�\�S�B�o�U�B�s�Y�B�i�h�E�w�P	�h�+��Z�[C
u���	WS	v������O�7�o��`��!� �
CHv��w�
����;�`��^��P	�x��]��
�<��2�N�p�g�Y� �m�\�H>�I�{�Z�'�fp�a�&�{�*����'�T���=
�R�#�a�G�
��G�y�6�1�d�{�P�Y��6�;�=�#�L�0�D�A�� CVv��

�%�&	
	


$.	
		=:Cv��y�=��>�o�>�p,�5C v��7%
��
��;	
��:�!5)
5Cv���^
`Kb*>OC'v���

."#?	"	
	C)v���r
	
E$!	8C4v���!		=
" +


S	y:�T��H������y��@��C�c��$C1yG�Z3�^�X�J�{�r�[�`�t�?�W�p�O	�T�$*��$�Y��k��:�(��i�h�[�\�	�y�}��W��:�;�S�T�6�%CyE�;�v�	�N�O�J�K��T�K	�~��|�F�G��~�D�E��m	�n��CyD��Y
Sy݁�g�W��k��@��)��(��)�m����G�Cy��f�;
�-�D�$���Y��I��v�
���SCyꁙ&�S�P�E�%�$��8�C���>Cy�k
S
z7� ��\��>�����t�VCzB�6�T�?�@�]�	���b���s�t�Q��P�yCzE�K
C
zD��+�7�H�K��@�E
S
z}�*����1������d��)�?��2�ZCz��+��Z�>S	z���Z�>��5�g����C	z��x	�n�i�w�t�j�G�JCz��QCz���{
�A�&�#�"�#S
zËT�@��B��=�������^��O����
C
z��	�	�n�������#�-S	z��d���.�6��1�J��d��wCz�z��Q�0�7�y�7���
�	�X�N�#Cz�c�C�\�5
�V�WCz�	#S	{%� �j������h�9���U��vC{3�J�K�V�o�M�C{1�Z	�T�B�'C{0��S{K�R�����'��k�0��N��S��v��C{]�i!
�g�� C
{`�]�(�+�L�M�&�|�	�-C{j��	S{��)���2��	�.��$��)	�X�W��"��_C{��{�5�(�p�o�g�LC{��bC	{����3�0�+�*�+�.S	{Ěc��0�����b��y��F��%CQ{Ϯn
��Z�.�-�<	��
�z�q�{�r�u�v����#�&/.�y�x�K�Qp�(�!�-��0�5�#�E�B�'�'�*�s�6�B���;�fC{��c1


�C@�BC'{ԁ�
�h�i�f�i�z�l�m�l�m�l�q���n�u�t	�w	�t�u��n	�g�f�eC{ہ�&		)
S	|ү&��H�H�C��3��z�����,C|ܯ'�E�/�+��1�2�u�t��C��V�c�K�(��C|��v�H�I	�J
C|遭1S}��2�����'�K�������k�I�0�c��T���HC} �I�L���*�nS
}*��6�����z����=����3�_C}5�9��5��XS
}?�8��Z�Q����.�R
��X��1C }K�5��,��t�S�t�/��{�r���k��Y�T�9�V�h�1�H�x�
C}M��N���
�T�5�!�T	�W�X�>�{�z��Z�>�A�<��BC}Q��8
S}���6��O�S��P�s���b��E�2��T��M�{�����C!}��N��(�l�u�X�#�P�?��*�y�k�`��	�k�;��k�r�/�*��d�2
	C}���8�I	���;	�a��9�i�h�i�fC}G	S	~�1��2����Z��P��Y
Ci~&�N��{�	
5�3�N���w��<�S��t�8�Q�|��O�j�m�b�T�I
�)����
����Q�T�d���z�4�M�L�!��G�H�!� �b�_�?�6���?�6��K�-�x
���+1�Z�[�C~G��O�N�+
/-
C#~'��B�[�Z�,
�1)�0�1�0�1�0�+� �&�%�"�����
�
���C~-��L/�)	&	S	��>�<��j��i�6�w�r��b�S�
��c��O�O��.����g��d��O��N�������C��@
�Y�D���
�Q�P���3�2C
���n��|��*��|�}�|C���f	S	�o�F���6��;���>��9�IC+�y�Q�i�h�9�Q�$�O�4�;�@�x��z�Z��I�$�T�u�i�T�l�Y�z�G��'�N	����8C�{��j�

�
��~��~��~�/�.�
�/�.C����p7S�큙t���!�������5����F�����������}��C%��7��n�~�e���l��O�e�v�k�,�;�%��F��,�3
�h��
�.
�s�v���q�A�j��>��8�C+�	�d 		
C6���y	�����
�G�F�z��	���
�
�?�F�C�8
���t�u�t�w��N�L�O�@���u�O�N�v�u�p��(�p��,C���y
	S
���M�r�X��\���&�M��0��eC��	S
���c�-��x�p��Q�	��\��]�WC�!�=�0�v��e�y�x��S	�2�>��7��6��-��q�\�w��NC�@�$���?�@��^�<��7�@�{�z�9�M��a�#C�<���n�m�5�0�1�6�w�v	�^�W�c�\�V�Y�I� �Y
�C�C��
S����+�R��?�k��~��u������(��-��,S	���E��%�?��0��k��j��	��6�JC	�ŋ_��R��k�p�$�e�^C��G�JC�Ё�	�S�ޱE��L��S��h��i��P��+�W� ��tC��)�m�
	�(�y�J�U��T�=�B�;�Z�QC�ꁚ�S��M�L��C�큮%3S�+�T��,�u�t��3��H��U��>��E�S����'�h��BC�@�O�J�
�C�<��9�a�`�C�=��/S
�O�c�Z��p��K��,��U��A��C�w�YWC�b��m�p�
;4C�Z�z�h��/�Q�~�US	���O�N��?��P�l�m��m�C7���[�O�@�*�_�2�w�h�*�!�@�b�I��N�k�V�!�^� �0�f�W�7�>��!�3�<�#��+�6�Y�\%�M�\�*�=d�y�[C����5�K�d�������i
��+�*�K�M�f!�m�l�"�C����9	)0S	�_��;���E�P�)���g��?��$S	�i���������d��5C�u�n�$�A�4��	�C��W�,-�]�C
�s��=�M�z�3
�B��5�4��C�t��JS���`�Z��b��[�=��X�������	��C8�ė�r�/��m�v�;�>�,�)�^�(�Y�
�9��f��
�~�S�Z�;�~� �B�#�w�r�S��h�i�P�i��t�r�CJ�a�w�T�v�_	
C�Ӂ�_���z�
��G�J�K�G��u�n�
�H
�J
�K�TC�́�R%	S	���,��t�e����r��q��p�EC����PC���:C���a�(�b�c�b�ES
���-��z������w�y�*��r��F��?��LCO���>���9�:�
��C�&�q��'�"?@��r�m�
�W� �6
�	�;�D�{��d�/�L�O�N��)�H�b�
��=�R
	
�5�6�C�'���
�	�3�,�Y��t��*	�	�C���b%�G�H(��
�K�JaC-����|
�k�n�s�r�s�r�o�n�m�l�s�t�/�`�5�
�x'��(�%
�C"����m	
"
#	S
��X�*��V�s��m��l��m��|C���>,�Y�(	�_�)�d�
�,T>��&C-�
�r
 


				
 
��+�8CP���#�n�o�a�n�o�\��
�a�l�Z�i�h�]�
�c�b�h�k��b
�U�l��
�3�=�t�v�h�c�b�]�t�}�|���d�[�x�z�y�\�e�d�_�^�_�^�i�h�c�b�a�`�]�	�b�o�n�k�*�)�f�=�+S
�
���|��q��,�k���CC���=��	�U�V�g�)�z��)C��C���<�x�}�|	�m�n�o�j�k�j��m�j��k�j�S�U��C�w��c��h��9��&��!�� ��
�b��.�x��?����HC/�h�/��x�/j�b
S�Z�y�	�
�d�	�d��W�	�t�W� �a;�\�S�C�c�\�F�L�<��q�;�3�d�z��d�+��={�v�u�D�U�\CI�k�3

				
	�.�7�$�

�^�[
C�����
* 
<�[S�Z�G�FC,�i��O�j�i0�h�[�r�d�e$�h�a�`�i�y�x�`�a�`�e�
��`
�a�`�a�`�c��C$�q��G#

 S��|��H��=��T��
�C��N���A�W�!C��S
����X��e��d����A��b��]��\C�*�Z�/�g�`�k��@�YC�6��@	�AC�/��y������#�j�I�#S	�P�.�f�P��b��u�X���I�IC5�[�
�!��6�G��n�8�M���#�V�J�9�
�:�[�>�r�g�6�>�r�u��d�u��-��4�i�!�L�O��9��%�K�/��y�`�&C�]��d�j
�k�j�o�n	�K�J�M�|�N�M�L�s�r�OC�\��	S	�ڴ�3�� ��s��C��#���+��J���U��H�0�o�-�,�\�[�bC��&C�쁚c�h�i�S�:�7�L�Q�8�7�LS	��`����.����2�i��k�aCF�&�5���7�<�h�e�C�D�K�N�'�"�M���F�G�F�T�U�w��h��� �G�J�%�&�o�n�I�[�"�5�� �Q�P�U�X�1�n�.��SC+�-��	�3
�~�
�4�J�}�z�4
�5�~�K�J�O�N�O�7�|�}���K�C�z��A�@��B�I�H�G��9��HC�0��
�(S
���A�E���/�R�V�g�nC	���H�D�%�5��r�
��]��^S
���u���?�a����
��w�X����I��RC"�Ε*�J�b�kd�
�(�u�t�_�<�E�0�CX�.����H�)
��+�>�<
� )�y�R�]��y�Q�.�	4�MC+�ʹ}��	
�r�qP�b
�c 
C8�ҁ�v�7�Q
��5�4���9�6�
�L�
�
���		��	��'�&��F�G�`�&�'�b
�G��	��E�D�E�D�M	�Q�^�0�=	�<�9�}C�ρ�<

e
	
 !S
�G� ��D��A��V����YC�_�|��C�U�*
C�T��1�V��?�>��^�a���7�Z
�D�#�"�G�F��]�\�S	������G�U�V��B�+��E�*C�ʒ�q�|�0�J�!�1�a��4
�P�UC���>�`�a�j�k
C���H�\�i��D�]�\	�C�@��"���G�T�
�f�#�E
�j�O�&�S�|S�%��!�����(��O���:�H�W�~��uS	�3�S��J�+�/�X��]�����#S
�>�X�[��&�v��I����7���C9�I�Z�[�^�A�9�&�$���]�^�M�L�	�c��`��k�A�,�7�2��S�7����Z�8�}��|�!�"�A�\�1�K���\����G�&C�J��@�N�_�^�QK�R�?
�j�]�^
�u�N� �u�t�u�0�c�$C�O��|7
S	��d�J�#��H��]�k��CP��f�N�/�4
��� �;�Z�]��J�O�`��
�
�b�e�u�j��p�W�@�=�Y�o�V�z�	�T��m�"�S�D�/
�*�[�x�h�-�6�i�`�Y�,/e��Wi��E�#�&
�j�1�+0��B�0�M��8��4
��C��<�;�T�2�uC@��v

�Z�[�d�e�-�jH%�>�G�>�?
�>�=
�Z�[�d�aC�,�VC""
)			C�-��~i
'$NC8�/��0�?�:�=�<�=�<�=�:�=�8�9�8�9�6�5�(�/	�$�%� �!���
����C�?��"!&	"	
S
�ֶ8��G��.��m�	�K�t�^�C��|��%��-%�"�KC��:�
�	
���
��zC�쁒}�t��6�7��
��a���,�b
�i�/
�&S
�B������0���'��_��t��mC:�N�L�}�~���u�{�\�	���	�G��5�f��*�w�B
��w�z
��G	�N��u�z	�+��{�Q�s���=�:�/��_�b�S�:�K�NC�O�zR�O�N$�K�JC2�M��M�������	��������z�w���{����	������)�vS���o���B��?�r��� ��)�#��n����L�$��G�tC�/�h�x�I�e�9�eC
�.�[
�
��z�C�3��W�4�5�4�_��m��6�'�9�^�9�o�&S	�i��x�c�6��A����E��$��+��~C�u�d�H
�e	�.��T��
"��_6�"�n�U��h�#�c�:��F��W�hC5�|�e
�t�}	
�h�e�h�e
�r
�_	�V�W�L	�S��,�-C5�s��%�h�e�d�e�E�D
�+�
�$�#�H�I� �Y�A�	� �Y	�d�#�N�O� ��@�G�$�Y�I��L�M
���#
���L�M���L�L�C"�t��z

		
�yS������}����'�C�x�2���w�xC?����A�D�5�.�I�H6�� �7�B�U�K���)�,���
�<�D�q�4�1�c�L�A�B�u�p�;�h�%�f���>�)��}��R�$�k�TC&����Y���
�~���K�J�����4�`�"�S�R�O�N�)�(�)�V�P�U�T�1�0C����&>
S
���4���r��s�
��*���z�HC)���E�6�D�s�n�Z�U�+��B��A��o�(�g�|���-�8��O�]�&�I�I�y�r�K�H�K�$�A�|�+�1C���"�|�}�0�S�c�f�i
�t�uC����8#S	��)�&��Q��P��Q�	��$��iC=�
�R�C�5�v�5�4�!�B�=��"���!���4�5�~�E�D�6�]�$�q�r�!� �m�l�G�@�W
�~�Z�3�6���&C"���\�B�C�F�G
�}�l�q�o�n�o�,�?�o�*�E��>�1�p�>�1	�0�I
�H�I��	C���?,
"
 S	�Ƹ������J�N����P�<�
C�ӌ��=�k�d�:��	A�|�`C�ܸC�Ё�R�K�H�I�y�j�T�A�.�9�H
��9�y�D	�M�6�1S	����L�y�Y�?���e��~CE��B�&�K��k�%� � ��K���$�\�1@�02�C�3�G
�h�5�Q���6�q��4�E�T�C�P.�D>�H�Y�/�P�[�T�^��e�V���s)�f�-�^�-�
�.)�M�$�i�8�� �$N���D�C�w�j��pC�ܰ9�?�B
	�(Co�!��H�G�J�c
�b�K5"	� �	H��			
�L�#�/A�0	�'

�p�q�l
�k�l�mC�9�3�&�7�'�BC��8KZQ8
�"	RC�V��*�p�*�=
+$0C<���u!D
(B
V	
"
C�&���<	�*�/
�>�A	CM���YC
?
L

S	����P�]���H��;�C��*��O�C�ɏ%�$���}��bC����c�\�c


�H�3		
		C%����2�?�L�M�L�M�:�!��s��/�b�3�x�-�d�e�f	�]�(�~�)���$�A�.�)�8S
�!�4��^�M�����+�*�;�qC��-�;�h�s�z�U�(��>�	�d��.l�O�N�i�p	�E�`�Q�8�a�X�1�F�m�f�_�Z�S�k��6
��l�|� �f�U�z�T�_�S
�Z	)2�M�R
�`�e��|�G�]�(�2�/V���
��;�:�7�6���;�:���x�d�e�	�2��d�t�-�}�6�;�D�
��}�M�I�C���C�6�n
�
?# �:C�e��A�]7&�C?�1��g� �!� �!
���	�k�x�o
�n�m�h�e�d�a	�L	�M]�H�I�F�E!�@�E�>C&�=��C
{"

p	S
����\��`�a��u��x��c��<�aC� �O�
���/�`��6
�W�V�]�`����x�")(uC7��
���
��8�;�8�=�<�D�C
�A�B�<�=�G�n��}�|�q�L���F��-�K�	�[�7�f��(��g�N-�w&�dC���}
S
��Z�G��c��>��_,C����"��6S+�0�F-8	_N�?�$Cd5�&�oD
_",a�w�~��e�?nA5Z9:P�L�}/S�]�w�I3"P!�l�GjK�[�~�8�C��CC���	5qpq2Fw�S?	B!��*�MzC��Y�N�h�\��n��Y�5�#�Q�h;�>���^)N�"�s=2�ZC��LC��C��i4LCD�~�%�H�q�A���Q�R�{�e�
u��_�E�:^�C��l��t�q�0?��o�r�%�?�j��W�3�J�OG�b�E�3� �
G�N��Y�O�D��g�/�K�	��V�_�[
�*%�)�2�).��A�d��	��t�Z�
�>2�8
�t�C&�>�e�h�
$�,�I/�^@�
!�W#�	�`$�
(�}:�C&	�S�t+�;;��I�o�oC����Q�n�,Ew
P�>�L�x�72~
�B �5I�'$��'�I�!,S
	[�W�X
�(��h�ol�Q�;�z�)��f��V		(R��i�2W����B��9��E.�gO
�c�(�
_v�<.�TB�-0�,�|"]	e~2u9/�5s�R
�D("�Y#�l�$
�Z�f�
�(1�	�%�N�l�B7�=(�l{
�K�{�$�$�64��&�wB(2 �>
�L�#8-�a_�iJ�v4�q���I�w7�
���6��@��<�m��'\:8w
H��&�{�{"��?�m@93*G�w�)�<I�i�b�%$�<�.c�{�U�v+� H"8�JR�g�nz.�2�~�.
�p
�go�y0
�;�s
�p�Y�>�H��k"�K0!
�$
-'3�\-� `�	#RaF�E
�'�^ �>� (6�u �+"�h093@9
�d&B
Rv���{�2�*�9�/C,���Yb"�X�:�[X�M�iB�(
�*�Y�8�]�m�Yh���Q�S����,�6�A�I�|�t�g�g�f�8�I�*�,F	�}�0R>�xc ;>%e��!�$�*�;�A�G�X���0n��Fpc��s���F�#�*�K�P��D�\�a��m�9�=�{�i�g�!�*�U�y�	�*�$+R�J��
U��cN���� �9c�K!`	��!	��jH�v�a�r�n�2���~�0JK�7!�7�E
�&�5L�?���X�^����M�:���!�0�>�`�y�/��B	�&H�=�\�_��4�1�V�x��C�8�c�X6�e<�p(�w*�����%_�7J�E�2�v�7�o�*�_�{���'�7.�@�+�[Z�f�C�u^��+�!/�(�~�~|�u=�J���l�,��h�<�l^�~�I��-�B(�M^�Z�L�0R�BL�Q\�`S�h0�s�=�;��=�>�T�d�f)�og��?�j�p�\�s�d�f��|T��� �>��\j�{
�u �$�	��.�!�%j�9�<��P�{�]�G�$v�>�A5�KV�\�g�u-�	��
�/��
��[�/Q�>�K�tx���g�-�1��U�~��r�Y�W�K��K�F�IP�Y��y�|�G��k�J�M�r�_#�hF�r�t�d����C�_��pT�~�m�3"�D�B�=��R�3�i(�m�@���1��E�J��bP�x"�~�:��+�*�0�7D�;�>>�F�H�`�i	�k�m>�{�~��G��0��
�T�;�so�{A�%��	�V�%�(��*����$�{�e�O�Tc t�"	�(�I����$�c!V�9c
$��c�I	��+	�2� 	�]�h�v�.�-c!��,�*�X�Y�)�p�x�C�B�.�a�L�K�b�q�|����� �#�(�*�3��]c�[�%c.��	cX��sh�|��k.���v���q�e�O��p�rN�m)�v��a�u�C�X)�\�^�;�q��w������=a�G9�K��U�@�^�l�t��	��#U�(Q�,\�2�t�S�-�j�z�|��
�S�#�>�0�
�:U�?x�HK�O�K�m�:��4�t�"��8�-�@�H�Ky�Z�_S�m�
����
��9��L�(�1�2y�<�^�T��r=�x���E��� �g�0�f�Q�f�o��|��
1�&��(�)�p�O�v�m��}�h�1��<�^�E��Lr�S��Y|�^��+�Tc G��g�8�}�(	�_�Q�B�iZ�:�6�I�D�O��E��F�u�P	�l�wc�5]��=���x�@��E�R��l���~&��P��_�D��k\��sE��z@��+���n���J��T���9�c��"��M!��R��
,��'���k��-��0b��:��<n��I�(��_�F�����=e���|��R��V�"��p�'��q��H$������K���Y1��*���R	��y��m���
��8�����5B��?���'��T��Zk��H��k��o�>��X���F��'��)��,���.�l��K���<�W��u���D���(�� ��R2���M��M>��V@��^��d:��p�
���$��g=��P�����2,���#��D�`��i�"��@��BX�����$�6��X���G�[��S��V��e|��i�g��t��4�����)��)^��2���?�;��N��Ps��]T��g;��nu��*��L����F��.1��4L��B�3��Fq��L����[��.��*���^��*�!��8��h"��"��p{��yQ��d��	=��P���#���'��b��5�t��@C��k`��H�l�����i��x.��W��3��
$���������"N��'\��L	��N� ��`.��l��?��t��w�(����BW���
��E�%��R���dc
���]��L!��T��Z4
��_��jB
��l��w����+��kc�=Nt��W�U��hn��x��{p��	��0����
��+��%>��.	��1��3�g��N��Un��j��mZ��uI��}���
���L��!{��)j��3��6���B��E!��J�a��m��qo��|!���
��"�J��8��;��?&��F���Q{��\l��d��h�U��x�Y��	G��:�������+q��;��=�0��O��Q;��W��Y��_���n&��r��u�)��$��(1��/�6��?��A��D���V%��[�x��n�1��j��
������((��+��.��1/��6���D��F��J	��La��R���>���P��3G��8q��CX��H���R���jH��q�H���<��Q����p��"@��)��,�k��@�R��s�	��y��8����>��$���@��D�N��U���g�O��
�
����n��&��(-��1�Z��N��P9��U[��e\��pM��yI����R��
���E��$�A��A���NH��T���hg��q%��wI����W���
��-��0��3�|��V1��Z$��^#��a?��hJ��q��t0��}	���I�����#���9��->��3��6��;��>��A��C��F��I%��O��R3��[6��e�"��~2��
���W��$���,��/��2���DK��M1��V�q��z��}���s���� 3��&��)��.�D��L��Q=��W��Z;��`:��g{��l\��vC����L��
s	�5�[�a�]�k�e�c�g�_�_c��"	����0>�G@�Ic�_�c��/����Ac+O����%��z�P��U�p��PK
!<��?��3chrome/pdfjs/content/web/cmaps/UniJIS-UTF32-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�UniJIS-UTF32-HC!��bC	 �U�?�dC��M��6S#��t&+"'�+�B�9S%�G(C%%�o/S	%@�uC &���z�wz!O�0�/
]��C'�C0��KS3��J�W�V�mC3�


��K?�C3�1$1
)')��q
C3�L


	c%�9�7�=�;�A�?�j�]�`�U�X�w	�~�6�O	�c�]�H^����[c0�k�g�6�E�P�T�\�b�h�t�w�|�~����mc#��^�d�I�X	�M�XPK
!<���ߢߢ2chrome/pdfjs/content/web/cmaps/UniJIS-UTF8-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE���?���?����? @\a
A Q¤k]>�d��_?V3�A´��M�T�+ 2!+�F�V�S�L�IQĀ��tA
�~$�c���
Q
��n$�c�U�P
�&%$)AĤ�)
�f�7�![QĮ��s"'��A��"�}
�";�9�6"��BQŀ��j��;
��|�,"�m����Q/���'
��j�>"�kOL
i��O��fO�>��_c�0
�m��("��[��<��SAƒi��P�nQǍ�2��j���	�
���Aǵ����k�yQ
ɐ�X��"��"O(
��zA
ɞ�W-*'0#rQɬ�6B''�$�c�bAɸ��1TI�A�6��7�ZQʈ�94
M�-�|3��)F�Aʻb�I>M�c=:B�@�;	���J�<�@A+ʔ�J��#�"k
l�I�)�@�	��]VI�b�p#
8%#
�c'�`�p
�I
AΆ��K
�YB[ �g�"8��F.��q.�J	�N
M�y�E�u�~�:�E�<�fG�~�k	'c���h	W~�9�2�T	
�|�	�l�
;�Hl�Us�y'�
�^�V�h�i� N�.:�K��`�e�,�d�[�5�J��|�O�r�]BoẼ�5�r���j�L�m��{/�q� �a�f�E��G�?
�x�&�Z�
�T�
,�?�I�
�h�q�i�r
�7�[��;�e��
�j�w�T�����G�X��.+��O�L�P�
�X�?�~
U�3�H�.�3��f�	�8�g�M�D������
�f�s�6�?�C��$�@<(�#�a�	�x�B<‿�i�B�`-�k�]�F�	�[�m�&�s�w�{<�{� �y�e�u�@��
K�yG�d��+��C�U��p�t�m�Z�$�;�M�
�v��3�\�7��V�=�/�j�U�_(�A�K�B�P�$�9�y�.�@�7��#�B⺞��eB⺬��:B℮���W�z�2�R⼀�0��i�� �w��w�8�5�R�t
�{�v�\R⼖�S��h�>�
�c�r�/�qR⼣�&�?�
�C�Z��c��c�T�c{�i�7�&&�S�����o���(R@⽀�'�4�u��w��$��n��B�S�^�n�}�H�a�$�/�7�R�4��	�w�>� .�Y�O�@�4�<�{�0��P��S�_�(��VKp��A��X��]�X�`�-�t�G�W��-�e�"��N�OR@⾀�S���e?�K�j8�]�/���8�G�U�F��
��a��h�(�.��\�z(����$��X��[�o�p��%�t�:�+�T�}�R�a� �G���6]�C�3�!�Y�"�o5�n 
R⿀��m���<�7�J�a��r��C�8��w��z�B
⿓�~��o�_�H
�+��<���w*�s�`B〰�:
�a��aB〼�B�c�R
㈳�Op}��$
B㉀�Vly�n�OR㊐�g�

�D�5�,�=��2
���^B㌀�pB㊩�ZB㊢�{R㌃�j�r�[�Z�B一�0��+B2㌔�"��
��
��*�?�
�F�#�F�?�#�,�y�`�P'�>�M�;�:�J	�=�P�K��A�&�w�P�y�{B㌗�x


�B	�5�[
�fB㐂�$�&�b�y�S�v�m�h�N�	�� ���M�`�(���r�n$��"��cB㐅��l�#��n�g
���x�o��x�-�j��yB�'㐆��Z%�},�g0"�k�z�D�p�k�H
�w(�\
�p�a�P��u�5�>�u$�L�K&&�B.�I����i�G��t�\�c�]�Q�r�~
�K4#�c�\	�O8
�9�L	�P" �Z�[�A��\8�~F��r�i�u�e"�$��p9�g�@���Z
�^�g��v"�Z
����_�^�lH��
�f�S�wJ�d(�W�*V�W
�x�C�n�V
!4
�U�s<	�u&��1��<�f��C�d�`�j�p�Q� 
�\(�p��G��X�$�e�	��&@�z�y�v�d�I$�.�~.��d�C����&�r�K}v�eQ�/�J�3R万�*�9�!��#����}�D��R��1�U��BV且�L�^�i��O�2�w�l�i�@�m�3�2���M�(�7�X�B�/�u�ve�	�R�\��g�f��N�� �z�3�
�i�A�Z�j��9�{�:�	��~�W�d�Q���}�x�X�S�w�h��N�
���E����I�@�G�F�5�K��r�#�yB
丟�Z�
�a�L��e�d$�M"��T�
B丩��U�x�Q�y	
�t�u�T�]�"�y�x�w�F�nR	仝�����g��N��/�F�7B仭��9�z�P�A�,��E�gB仼�5�CB
仨��]�w�v�u�F�G�{�l�m�B�lR伈��a��	���4�<��k�Y��M�'��\B会�q���L�xB伕�kB伖��f�x�3R伳��d��)��(��w��z��k��G��.��Q���PB佀��f��u����N�;R位��\�u�C�R��6��y����a�r�/�����j���b��p�AB佩�)�Q��0��\�C�i�B佺�pB佣��k�	�5�:�G��>�?R
侈�%����k�!��
��W��V���������r��oB侖�.����3	�$�[��q���/�B�5�l�CB侚�<B
侗�s��M�L��<�L��F�z�R
俈��~��%���*��������}��|���B俗��F���{�X
�g�.��B�0�����F
B俠�l�(B俜���#	�"�?�$	����D�(�u�dR倉�T��`����2����x����p��e�a��>��wR
倖�*��6�m��O� ��~�z����yR倡�B�z��i�d�o��f��
�I�T�;��(B,倶�^�N�I��y��X��q��A�(�t�'�F��B��e�O����?�n�R	�E�$�q�C��)�~��G�>�@�/�.B偀�?
)�c�B2倰����E� �t�u�5�{�$�y�x�y�5�{�&�'�&�}�I�|�7�6��-�0�5�}�0�1�(�;�:�B��)��
�o� �1�9�lR	僡��
�4��������L��k���+R
僭�_��J�R�T��j���I��B價�b��D��E�h�	�qt�E�D�9U�B儈�
B僺��+��D����8	�)�(�C��(R兀�q�s����!�1�h�d�;�^�C�:��d�?�)��r��gB兒�r�9��B�?�A��)�h�.�7�Z�m�[�
�M�C�`��sB兤�JB兓��;�I�F�_��T�_�
��W�>�H�D�S�R冉�~�#��P��3������6�7�#�"B!冗�Q�3�+�D�w�v�s�g�f�o�t�1�j�D�F���Y�X�/��[�~��+�*��5�R�p�3�2B冝�K �]�a�`B冘����	
��1�`�B��j���.�-�<�=�'�P�gR凰����p�Q���u�s��L��E��zBT函�5�F�A�}�
��=�}�A�j���C��:���y�x�Q�P�)�?�I�.��-�G��B�;�Q�$�`�	�]�`�9��(��y�7�>�a�R�3�8�C��&�x�}�r�3�U�U�p�{�$�7�jF�P���Y�B�Z�o�Y�n�O�$�i�h�/�0B刕�N��a�~�`	�X�P�Y�GB1刅��o�#���N	�1	�H�$�g���Z�'�+�P�'�C��*�+�H�N�M�N�#�I��P	�O�N�F��$�!�z�g�
�Z�}R勛�R����)�h����v��a�`�o��*��=�3��n�A�@B勲��|�{�1�t�A�K�b�
B匀�SB勬��a�
�H�B�g�#�G�B��,�E�DR
匕�M�!�x��V��u�B��0������K��s�SB
匣�O�A�@�{�m�o�Z�@�x��
B匤�UB	匥��i�K�T�U�)�|�U�Q�C�&R	卅�V��m��
����
B卑�p�9r�]�J�/�h�.�<��� B卡�!B卙��q�#�J�%R
卬�"�J��W�+�^�X��;�^B#卷�`�S�N�{�B�|�I�H��U�
�p��H�J�y�x�A�F�n�P��{�c�8���UN{�<�&B厓�WB卹��	�'��(�D�R�I�L�Y�U�*��*�U�T�-���~�W�V�B��(
�V�{�7R叝�Y��J��'��L��]�Z�Eq�5��
�6��#�#�w�P�H�W��E��#��p�7��1�R�>B叺�r�F����?�R合�y�/�0�i���/�^�E���`�`B&君��l�C�8���C�<��b�*�t	�;�A�:�W��}�]�V�t�
��/�B�'��� �B�vB	吞��4�O�M�0��
B吚����"�^�_�Q�#��]�\����G�A�TR咈��%�X��m�y�����Y����B咕��H�7�%��p��7��CR咤���t������z��	��<�6��5B咱�����R	咷��L��}��B�8����=�8��2BX哀�i�$�0	
�5�@�=�L�+���m�n�F����J���o��5�}�:��A��+�2�7�.�m�=�*
	�A����z�o�t�
��O�f	�O�`�M�c~�&��
�M�4pi
�M�P�G�1�W
�~�o�w�h
�YB哿�^�^��g�B'哃��N�9�8�U�E	�@�D�G�\�]�F�C�o�n�K�H�q�x�I�}�|
�]�i�h�)� B1哊���o�n	�o�n�o�n�F�o�j�k�j�K�q�p��Y�t�O�u�r�s�r�u
�N�r�s�p�q�r
�s�j�E�k�l�m�l�iR嘯�U��8���X��<��C��X��Y��\��[��TB噂�_	�6� 
�k�v�S�V	�1�#�<�E��k�l�Y�\�CB噓���kB*嘻��*��{�i�@�h�x��k�v�w�v	��i�f�g�d�
�M��u��~��g�d�%�*�r�}�%� �a�b�%�*�t�F�}R
囈�g��L�i��H�w���3��8�t��/B囓�jR囗�k�_����=���J�e��9����
��BC団�
�R�1�� ��e��V�@�}�~�O��d�K�J��*�y�r
�`�D�W��6�.��y��_�6����B�;�4�c�b�%�1�V�L�y��v�K��6��_B坙�`��ZBF囤�P�d��U�u�t�t�k�x�p���m�h��y�{�p�L�m�h��u�t���t��o�r�I�H�y�	�I�H�B�{��x��z��y�z���	���N��W�R�����c�-�~���R	執�e��x���O��l�����v�B:堀��{�y�@�D	�c�b�)�7�O�~�_�Z�Y��B�%�.�c�L�?�B�54�\�I�a�@��.�V	����D�g�v
�S�s�D��M�L�U�P
�#�B�� �O��>B堯�2�q�(�|�>B2堃��a�y�x���
���~�	�v�o�~�	��~�I�}�f�	��]�p�|�
���|�@�}�|�a�t��z��e���
�`�E���z��#R壐�h��y������z��_��z����vB7壜�1�)�t�|�:�)�_�P�G�|�G�3�2�k�j�-�1����:�
o�|�q�R�#�P�Y��6�]�\�E��P����c�^�c�d�x�O�T�[B夋�i�B壝��y�����b�"�|�/�.�/�w�$�%�$�;�8��C���=�<��1R	奧�M�y�z�Y��J��w��B��[��Bs女��&�t�3�C�2�u�4�T��|��8�i�z�O�a��E�"��3�.�G��s�A�|�$�u��� �X��]
�o� �B�A�_�r�/�2�O�J�g�%�t�C��#�!�D�� �=�<�;�R�g�*�
 ��B�K�L��
�=�V	���_��	�G�J�	�#�$�A
�A�B���w��a�_�H�9�t��!� �]�\NOB妤�n�[B姊�q
�
�W��m�l�c

	�_�l�_�S4�?�>B;她��j�?�N�%�`�?�<�?�F�'�&�8�9�6�T�3�7�6�w�=�:�I�;�:�����D�K�J����Q�����S�
�aB)奲���P�I*�H
�W
�T!�J-�C�	R宀���H�G����H��I��J��[������e�[�h��*��{R宓��C�l��5��*���}�p�>��,�bB�6実�n��D�O�R�z�5��W�����C�;�B���G�t��:���e��J�O��B�W�M��o�X�+�D�)�(�G�B�/�}�L��A��/�/�L��}����1�k��'�s��u�t�S�ZI�R�2�e�b�i�@�/�P�C�B�k�`�o�`��s�n�w�E�h�#�J���	�[�d�R�
��� �;�2�
��w��G�>�F�[�_��:��K�F�O�J�I�� �_�h
�s�r	��a��w�r	�'�>�e�d�K�u�H8		�o�n�K���5�KB
寀�q��|�G���P�z�q�Z�bB尔�*�?	��r�N�O
�N�o�\���e�B.宬��w�a�E�{�z�u	��M��������S��u���
�M�%�&�P�-�0�5�2�V�5�6B宷��y�_�~
	�b�o�c2�[$�D�B寬��NB+宭��=
�D�W
�d �I

�b�c�H�J�W

R
巠���c��������o�X�Y�e��PB0巫�m�	�W�^�y�s�j�e�+�x�7�C�.�T�q�4���m�h�[�=�
���o��O��B�"�� �q�b�G�B���a�`
�!�>�UB巸� �}�@�|�iB巹���M�P�}�|�U	�-�J�z
�y�&�T�M	�v�RR	幷����)��$�/�7�C�$B'庁�?��2�2�31�
��-�g��n�g�C�g���`�o�^�J�U��&�n�K�L�9�/�l	B庙�0�r�`�d�_B庀��
�N�}�z�U�'�&�H�'�x�U�'�zR
延�����t���5�j��2��W�B开��V��W��V��w�R弆���-�,��U���M��L��+�`�&B
弓�w���F�Y�i����s��t��}R	弡��d�F�^���$��h��%B6弭�$
�3�^�5�z��3�B�B��~�w�z �1��;�X�O�t�d�)�i��i���@�-�`�F�7�6���-�R�h�e�}��F�GB弴��P�!!� B!弮���-�=�<�B�f�@�+�*�	��C�@�A�(��!�	�H���u�b�J�#�2���5�4��!R	徧�>���!���f�}��9�e��dB徳��-�L�*�w�E�h�F�W�P�H�;�	�J�?�6��|c�?�:�m�v��N

B德��fB徵�8��K���3��� ��5�,��5�4���9��J�*��R怒�V��������p�*��'��z��s�C�B5怠�5�&�#�4���
�+�2�S�J
�F�7�8�g�0��>!�!�F�#�o�A�>�o�:��(�~�I���I�2�k�^�!�pB恝��gB怟��&���;�(�J��������)�(	��E�B�D�-�,�)�)R	悢��7��
��8�R��{�f��^��_�R	悰�O�"��Y�"Z�����B悼�c�&�G�?�>�	�z�B惕�
B悻��!��C�.�1��'�"R惘�z��R��/����E��f��5�c�4����GB惧�g�K�F�>�S�F�_�^�D�#�&
�k�]�D�%���"�BB惲��^B惥��@�7�)�\�=�D�<�?�>�;�z�>�;�%�$�8�A��D�C�B�?�FR慄���j�;��I�����}�/�e�l�B
慓�!�Q�L�i�q�R���B慞��(�u�tB慬��LR慮��6����\��q
��v��nB5慾�G�Ce��S��P	�Q�i�a�
�7�A���D�z�5�.�y�z�i�n�=�P�@	���w��Y*��B憘��zB%慻��*�'�&�K�L�w	�2�B�O�)�v�E�3�v�E
�r��E��4�p�q�F��~�Y�V�W�V�S�K�R�a
�/R
戚�p�,��8���9�#��Z��9��:�$B?戦��l�o�n�+�m�V��@�m�4�w�
�I�A�3�$�S��M��)�
�(�3�<�y�@����r�=��,�w�n�/�x�T�K�m�~�5�r�Q�{�*�|���D��s�d	B
戩�^�C�_�D� �n�=�~�<B戧��f�e�D�`�_�^���a�-�,�n�u�\�R�i�q�p
�lR拌�i�	��"��)�M�F�-����H�1���5�p��v��9��AB;拠��M
<�B�	�=�D�
�k�h�f�A�3�N���Y�L�U�q<�
���
�"�2��1�IR�#�~	�
�1�w
�X�#���U�V�?�<L�D�,�?�w�<�G�8�s�d��yB拼�e�M
�c�b�k�FB*拪��x�{�q�E�h�k�j��~��~�k�h�J�e�b�|�}�e�`�}�|�c�b
�a�`���7�2�B	�a�^�e�dR掞�r��]�#�k�h����������D�E�j��A��VB�9掲���2�I�L�A�g�p�5�I�|�q6�{��2�+�W�
�9�K�>�q�|
=	4��c�X�q��C�>�8	�}�t����)�H��V	�C�:�q�7�E�l��M��F�P�/��D�c�n	������"��v�1�:�E�A�@MPAB��:����s�E�#�
��� ���?�@�/��V�#�4�(�w��\�|�N���E�&�m�`E�!���1�0�7�"�H�@��
�h�9�&�2��i� �C�^�5�.�u�x���O�P�O�8�A�]�H�n�{�e�Z���F�;�6�����P�e��,B揵��^�O�|,� �|�p���k�~�qB揑�s
�1	�N�Q�`�q
�N�=�@�H�m�Y�| ��B揥��l��J��sB&掽���b�k�j	�I	
�u�O	�Q�I'�I
$�GB揪��J�g
�R�[�y�j�Z�q	�IB-揁���V%�Z�a	�K#�K
�H�Z�b�t
R昜�k��6�o��w�Y��<�+�(��W�}��R��k�e��N��+�S��O�l�!��R��B昴�j�*�/�>�A5�G��D�a�Z�_�BB昳��v	�J�.�'�W�~�B昷��L�F
R
晝�q��6���3�����V�=B-晩�i�R�g�,�C�c�T�F�#�p�c�Z�� �{�f�q�d�Q�R^�2�B���!� �%��2��1�@�B晳�"�eB 晪��Q�	����S�L�@�}�5�4����(�)�?�D�f�7�z��[�Z�E�D�E�D��R	曷��'�P��m�	�[�pB/最�7�&��$��=�<�Y�n�h�w�S�K��3�Zm5�/�B�}�N�.�I��6�A�9�J�1���'�"��-�&�#�"���P�$B朎�(�B朁��_��
�����i
�7�6�f�D�a�`�S�O���R杬��i��;��H��M�,�y�x������`��;B	杼�$�}�b�E�"�9�$
�gB杻��D�2�k
�j��YB构��j	R	枕��� ��#��L�T��1��Z��o�>B枠�n�t�]��x��!R枯���0�8�9��]����[�:

B柁�(�P�N�K�F�%��m�F�#�H�P�6B枻�+�DB
枾��{�C�h��~��~��)�&�|R
柮�6��h��o����e�%���$�B<査�*�d�}�B��n�o�T}�1�v�_�g�F�Y�b�U�]�d�i�,�B�~�	5�
�*�g�j�{x��X�>�9�j�5��I�^�?�& !�A�}��u��%�&�}�~	�[�,�E�B柺��J�'�~�N
�{�!B-柹��J�D�%�z���~�i�h�~�}�5�0�~�}�x�E�y�v�w�r�3�@�C�4�5�4�w�E�A�&��u�h��9�6R
梭�K��v��o��6����Q�����)B	梹�Q�[�G�]�p�=��C�NB棈�/B梻���GR棏�0�v�6��5��4��O������|��gB棟�i�
�I�Z�-�&�e�E�x�DB棨�.B棪����A�@��]�pR椃�� ��s���d�]��
��h����2�q�t�d��t�=����B%椙�@�^�C�
�8+& �5�|,��J��� ��(�}�h�w�r���q�t�)�\�L�uB楨�2B椖���%�$�%����F�&��_�;��=���$�p�"�a�]R	楷�u��f�`���=���~B�榁��c��1�*�}��1�,8=$�A��(�}�L�)�?�n�i��l�B
�A�J
�k�n�d�{�d�:�-�g�2�,�W��'�+�6�F�����u�N� 
�i�@��j�=�8
��		�o�j�E/0
�{�~�3�(��"�H�c�bad�y�z�1���S
�H���l�)�D�B��L�P�G�HB
榘�4�n���
�U$
�cB榀�;�w�v	�r�[�Z�J�
	�F�X�U�g�

�q�PB+榒��)�?
�>	�C�E�D�I	�L�Q�P�R�U�Z�I�P�g�h�i�j�H	
�M�
��Z
B榨���u�X
	�}��R�J�O7B榍���L�w�r�V�W#�E
�V
R	歟�b��n��o�m�V���p��Q��nB歩�2�n�O��r��n�@�E�=�!�
�.��B	歬��c�����F��
�B歫��?�TR
殬��I�[���o��H��G��DB5殷�u����u�@�@�A�r�y�2�o�2�(��4�T�#����3�~��X�A��
�!�<��X��E�^�v�m��l�Q�K�(�'��B毖�=�(�\B/殽��j��M�1�q� � �#�`
�;�'�p�u�B�&�L�u�t�O�N�O�+�^�k�p�k�&�>��N�s�r�A�@�u�y�("��A�3�y�j�u�wR	沁�
���^�)�M��4����<B沌�1�2R沒��3���-��u�� �;��m�@��2B没��c� �j��P� �(��4�X
�'�A�H�H���&B沪�IB	沟��\�w�E�:�y�M�E�;�vR	泏��`�o�n�}����&��7��6��'B6泙�!��%��B�i�l�O�6�Q�W��h�%��'��(��g�D�(�k�r�E�@�Q�B�l�5�x�2�e�\�)�j�
�{�p�l��|�`�K��\�2B泚�B�i�*�WB*泜���-�j�?�+�f��D�F�#�^�k�h�i�h	�C�B�m�.�O�r�s�L�M�W�e�d�J�1�h�9�*�]�h�9�*�E�]��NR	涑���.��{��p��g� ��L�1��_B涛�mX�
�;��g�C{�l
�A�J�]�^�=�6
��"
��~B涬�G�bB涪��)�`�"���o�B�H�I�J�K�J�U�w�Q�,�j��mR淮�K��N���K��C������X��	�T�#�%�,B清�Z�'�r�T��]�U�,*�1�B渗�FB渀��uR渝�^��
����t��f��U�S�@�rB/温�9�6��~�w��D�E�>�/�P��2�q�t�C�F
�?��@��f	�=�R�{�t�n�W�G�4�{�t
�a�T�y�z�D
�+B渹�L�_�"B"渲��&�)� �&�F��h�v	�{�[�r������
�%�K�����M�B�$���+�C�*�)�R	滇����C��8��e�X���	�B#滑�H�8	�3%
�p
�)�41�A��0��E��.�e�8
��e��+���
�
�A
�c�@�CB滙�8)�"
�m�L�l���q
�p�f�[�X�Y�\�UB	漄��	
�SR潗��A��o��L�U���S��j������!�SBH潤�k	�8X�S�a��|�N�	�>�.�k�B	�~���e�T�)�s�M�	�k�j�_�:�W�t���K��C�A�V��;�M�>�g)��Z�	�h�|�%�
��L�(�M
�
��O�h�Q�@�L�C	�x
�C��Q�u�Z�	�|�K�G�F��W�}#�m
�_��D�A��H�"���	Bi潦�DM�C


�H

�A�B+2��B	�Muv�n�R
�K�o		�W
�J#�M
�@B澈�Q,��_��N�^�	� �\%�W�H�X
 �{�k�|�|�}�g�<B(潽�
�M�%�$�c�����W�B!�`�j�X
�X �n�W�m�i
�HB潾��3�M�J%��]&�n��y�b�\2�I�B.潨��C�g	�j��C	�H�O�X�V"�`
�a�v�j,�HB澇��3��U�h�0�
�1�T�,��b�cB*潬���V	�T"��Y�L�~��J!
�H	�q
�@
�Z	
�UR
牗����+���<����d��Q��Z��}����'�J��JBE牧��
�?�~�	�B�X�Y�~���K�[
�(�I�@�g
���_
�p�`��]��V
�1�\���D�n�N�8�E�Q��~�Y�=
�v��$�O�?�j�*�	�
��Y�D�e�N �E����!�c�E�_��Z�q�O�v,�Q� �^�i���H�Z�u��.�8BW牴�k	�B

�I�P	�X�w�D	4+�L�A	
�L)(�b�JB犱�a�e���K�Q�u�w�d�x�aB8犍�;�K�%�$3�l�Q(�F�`�a	�O�N�R	
�H�I
�F�@�=�J�N		�H�WB犛��I��h�{��Z
�x�_5�WB6牸���N�P
&�L
�T�O	�E�F�l	�c	�ZB牨��H�L��M�j
�
�|�o�ZB/牫��A�W	�o�N�`�F
�X	�@��`�]�IR甞�J�]��V�*���-����]�^��d�����k��/�0B
田�>�<��Z�
�~��E�H�GB甯�rB甶���E�@�!�dR
畆�S��^�g�������X��=��d��'��&B畔�^��JR
留�y�4�q�!��*�H��
����OB	畤�V�E�#�f�W�Z��D��ER畯�u�-��h��k�~��d��m�-�(��D�.BG畿�@�B�@��'�U��[�j�e�V�Q�P��C�t�k��|�?�g�"��������O�N�D	
�F�w�z�[�V�a�d
�+�s��c�zBD疁�~
�2�3�B���
�5�4�@��	�����	��K�D�)�(���I�F�(����!�+�H��C�D�E�P�]�\�S�B�o�U�B�s�D�Y�B�i�h�E�w�P	�h�+��Z�[B
疅��	�W�VR	皀�����O�7�o��`��!� �
Bi皐�w�
��C���;�`��^��P	�x��]��B�
�<��2�N�p�g�Y�C� �m�\�H�~�I�{�Z��g�fp�a�&�{�*���N��'�T���=
�R�#�a�G�]�
��G�y�6�N�1�d�{�P�Y��6�;�=�#�F�L�0�D�A�Q�� +�c�8�g�B�c�g3�^�X�1�c�`�2�W�p�O	�T�I�E�$�Y�K��:�;	���}��@�W�	��S�TmBh皋��M
�%�&�H�]	
�K�G	

�O
�d.�I�F
�I	=:�S

�@*	+	�UB皛�y�=�N�>�o�~�p�l�u�c�uB硏�B睪�U�~B'盅��w�e
�W�{	�Q
�[� �!�u�i
�u	�G�
�NB皝��^�W
�X� �E��b*�~�O�Q2�NB2皕��

�Y�n�b#�]��I�Z"�_	�Y�Q
	�d

�b
�MB0皭��r�[�F
	�F�W�J��d!�Y�U	�x�B�n�XBB皟���a	�O	�D�}�M"�U�`�k
�G

�F�N�M�_R秝��g�W��k��@��)��(��)�m����G�B秩��f�;
�-�D�D�$���Y��I��v�
���SB秪��&�S�T�P�E�%�$��8�C���>B秱��k�JR	稷� ��\��>�����t�B穀��b���	���\�e�$��X�d�A��C�?B穃�!
�JB穄��+�A��H�S�R�K�
���@�E����N�A��)�3�Z�)�>R	窐��Z�>��5�g����B	窟�x	�n�i�w�t�j�G�JB窠�QB窞��{
�A�&�#�"�#R
竃�T�@��B��=�������^��O����
B
竑�	�	�n�������#�-R	童��d���.�6��1�J��d��wB端�z��Q�0�7�y�7�B���
�	�X�N�#B竴��c�C�\�H�5
�V�WB竱��	�cR	笥� �j������h�9���U��vB笳�J�K�K�V�o�M�B笱�Z	�T�B�C�'B笰��
�@R筋�R�����'��k�0��N��S��v��B筝�i!
�K�g�� B
筠�]�(�+�L�M�&�|�	�P�-B筪��	�YR箔�)���2��	�.��$��)	�X�W��"��_B箪�{�5�(�p�o�G�g�LB箯�bB	箥���3�0�+�*�+�.�DR	範�c��0�����b��y��F��%BQ篏�n
��Z�.�-�<�H	��
�z�q�{�r�u�v���@��#�&/.�y�x�K�B�Qp�(�!�-��0�5�#�E�B�'�'�*�s�6�B��A��;�fB篗�c�q
�Z

�C��BB'篔��
�h�i�f�B�i�z�l�m�l�m�N�l�q���n�u�t�I�w	�t�u��n�I�g�f�eB篛���f	�Q�I)
�SR	糒�&��H�H�C��3��z�����,B糜�'�E�/�+��1�2�u�t��C�A��V�c�K�(��B糝�v�H�I	�J�MB糩��1R紏��2�����'�K�������k�I�0�c��T���HB素�I�L���*�nR
紪��6�����z����=����3�_B紵�9��5��X��+R	絀��&�Q����.�R
��X��1B 絋�5��,��t�S�t�/��{�r���k��Y�T�K�9�V�h�1�H�x�
B絍��N���
�T�5�!�T	�W�X�B�>�{�z��Z�>�A�<��BB絑��8
�ER綪��6��O�S��P�s���b��E�2��T��M�{�����B!綽�N��(�G�l�u�X�#�P�?��*�y�k�`��	�k�;��k�r�/�*�E��d�2
	B緀��8�I	���;	�a��9�i�N�h�i�fB緢��G�E	R	縛�1��2����Z��P��Y
Bi縦�N��{�	
5�3�B�N���w��<�S��t�8�Q�|��O�j�B�m�b�T�I
�)�����J����Q�T�d���z�4�M�H�L�!��G�H�!� �b�_�?�6���?�C�6��K�-�x
���+1�Z�[�B繇��O�N�Y�k
�o-�N
B%縧��B�[�Z�,�M�1)�0�1�0�1�B�0�+� �&�%�D�"��
�@
���
�
��U��B縭��L�N�o�)�\	�f	�CR	耀�>�<��j��i�6�w�r��b�R耊��c��O�O��.����g��d��O��N�������B耜�@
�Y�D���
�F�Q�P���3�2B
耞��n��|��*��F�|�}�|B耝��f	�AR	聯�F���6��;���>��9�IB+聹�Q�i�h�D�9�Q�$�O�4�;�@�x��z�Z��I�$�T�u�i�T�l�H�Y�z�G��'�N	����8B聻��j�L�

�
��~��~��~�L�/�.�
�/�.B肁��p7�NR胭��t���!�������5����F�����������}��B%脂�7��n�~�e���l��O�T�e�v�k�,�;�%��F��,�O�3
�h��
�.
�s�v�F���q�A�j��>��8�B+脉�d�G �K		
�A�FB6脃��y	�����
�G�F�z��	��C��
�
�?�F�C�8�M���t�u�t�w��N�E�L�O�@���u�O�N�v�u�p��(�@�p��,B脋��y�H
�N�V	R
與��M�r�X��\���&�M��0��eB舒�	R
舖��c�-��x�p��Q�	��\��]�WB舡�=�0�v��e�y�x��R	舲�>��7��6��-��q�\�w��NB艀�$���?�@��^�<��7�@�L�{�z�9�M��a�#B舼���G�n�m�5�0�1�6�w�v	�^�W�c�\�C�V�Y�I� �Y
�B艃��
�KR芩��+�R��?�k��~��u������(��-��,R	芷�E��%�?��0��k��j��	��6�JB	苅�_��R��k�p�$�e�^B苆�G�JB苐��	�R苞�E��L��S��h��i��P��+�W� ��tB苫�)�m�
	�E�(�y�J�U��T�=�B�;�Z�QB苪���S��M�A�L��B苭��%�sR茫�T��,�u�t��3��H��U��>��E�S����'�h��BB荀�O�J�
�B茼��9�F�a�`�B茽��/�DR
荏�c�Z��p��K��,��U��A��B荷�YWB荢��m�p�E�
;4B荚�z�h��/�Q�B�~�UR	莘�O�N��?��P�l�m��m�B7莢�[�O�@�*�C�_�2�w�h�*�!�@�b�I��N�k�V�!�^� �0�f�W�E�7�>��!�3�<�#��+�6�Y�\%�M�H�\�*�=d�y�[B莧��5�K�@�`�������i
��+�*�D�K�M�f!�P�m�l�"�B莦��9	�i�p�UR	葟��;���E�P�)���g��?��$R	葩���������d��5BN葵�n�$�A�G�4��	�C��W�,-�]��}�Z�=�2�A�s�r�/��m�v�;�>�,�)�^�(�@�Y�
�9��f��
�~�S�Z�;�~� �B�A�#�w�r�S��h�i�P�i��t�r�CJ�a�w�T�v�_	
�AB/葳��=�M�z�D�3
�B��5�4���C�B��A�����z�
�N��G�J�K�G�Q��u�n�
�H
�J
�K�T�AB葴��J�T�v%�O�P	R	薄�,��t�e����r��q��p�EB薗��PB薐�:B薏�a�(�b�c�b�ER
薤�-��z������w�y�*��r��F��?��LBO薹�>�F���9�:�
��C�&�q��'�"�C?@��r�m�
�W� �6�M�	�;�D�{��d�/�L�O�N��)�N�H�b�
��=�R
	�M�5�6�C�'���
�	�3�,�Y��t��*	�	�A�B薷�b�V%�G�]�H�h��
�K�O�J�!B-薼��|�J�k�n�s�r�s�@�r�o�n�m�l�s�t�C�/�`�5�
�x�O'��G�(�%
�B"薴��m�I
�\�_�Q"
#	�ER
蜂�X�*��V�s��m��l��m��|B蜘��>�l�Y�(	�_�)�d��M�,T�~��&B-蜍�r
 

�L
			�I
�B 
���F+�8BP蜎��#�n�o�a�n�o�\��
�a�l�Z�i�h�]�
�c�A�b�h�k��b
�U�l���J�3�=�t�v�h�c�b�]�t�}�|���d�[�x�z�A�y�\�e�d�_�^�_�^�i�h�c�b�a�`�]�	�b�o�n�A�k�*�)�f�=�+R
蠍���|��q��,�k���CB蠟��=��	�D�U�V�g�)�z��)B蠜�B蠘��<�x�}�|	�m�n�o�j�k�j��m�D�j��k�j�R衕��C�w��c��h��9��&��!�� ��
�b��.�x��?����HB/表�/��x�/�Hj�b
S�Z�y��I�
�d�	�d��W�	�t�W� �a;�H�\�S���c�\�F�L�A�<��q�;�3�d�z��d�+��^�={�v�Y�u�D�U�\BK衫�3�B

				�T
	�.�7�E�$�
�E
�^�[�D�@�JB袘��[��
�j 
�|�D�[��Z�O�G�FB,衩��O�j�i�p�C�h�[�r�d�Q�e$�h�a�`�i�y�F�x�`�a�`�L�e�
��`
�a�`�a�`�X�c��B$衱��G�D�c�U�K
�O
�`R討�|��H��=��T��
�C��N���A�W�!B訛�R
訝���X��e��d����A��b��]��\B訪�Z�/�g�`�k��@�YB訶��@�I�AB訯��y����A���#�j�I�#R	詐�.�f�P��b��u�X���I�IB5詛�
�!��6�G��n�8�M���#�V�J�9�E�
�:�[�>�r�g�6�>�r�u��d�u��-��4�i�!�L�O��9�B��%�K�/��y�`�&B詝��d�j
�k�j�o�F�n	�K�J�M�|�N�M�Q�L�s�r�OB詜���Z	�GR	諚��3�� ��s��B諤�#���+��J���U��H�A�0�o�-�,�\�[�bB諴�&B諬��c�h�i�S�:�E�7�L�Q�8�7�LR	謙�`����.����2�i��k�aBF謦�5���7�<�B�h�e�C�D�K�N�'�"�M���F�G�F�T�@�U�w��h��� �G�J�A�%�&�o�n�I�[�"�5�� �Q�P�E�U�X�1�n�.��SB+謭��	�3�M�~�
�4�J�}�z�4
�5�~�K�J�B�O�N�O�7�|�}���K�G�C�z��A�@��B�I�H�G��V�9��HB謰���E�J�(�R�LR
貧�A�E���/�R�V�g�nB
貴�H�D�%�5��r�
��]��^�eR賀�l��?�a����
��w�X����I��RB"賎�*�J�b�kd�
�(�u�t�_�<�E�K�0�C��.����H�)�M��+�>�<
� �i�y�R�]��y�Q�T�.�	�t�MB+賍�}���I
�r�q��b�J�c �C�P
�LB8賒��v�7�Q
��5�4��B��9�6�
���
�
�U���		��	��'�&�S��F�G�`�&�'�b
�G��	��E�C�D�E�D�M	�Q�^�0�=	�<�9�}B賏��<

�e�B
�C	
�`!R
蹇� ��D��A��V����YB蹟�|��X�B蹕�*
�DB蹔��1�V��?�>��^�a���7�Z�M�D�#�"�G�F��]�\�R	躩����G�U�V��B�+��E�*B車��q�|�0�J�!�1�a�E��4
�P�UB躾�>�A�`�a�j�k�F
B躳�H�\�i��D�]�E�\	�C�@��"���G�T�E�
�f�#�E
�j�O�&�S�|R輥��!�����(��O���:�H�W�~��uR	輳�S��J�+�/�X��]�����#B?輾�X�[�B�V���[�^�A��y�&�$���]�^�M�L�	�c��`��k�A�,�B�7�2��S�7����Z�8�}��|�!�"�A�\�1��B�K���\����G�&B轀��?�L�O�N�_�^�Q��R�?
�j�L�]�^
�u�N� �u�t��F�u�0�c�$B轁��{
�w�A
R	逓�d�J�#��H��]�k��BP逝�f�N�/�4
��� �;�Z�D�]��J�O�`��
�
�b�e�u�j��p�W�@�=�Y�o�V�z�	�T��D�m�"�S�D�/
�*�H�[�x�h�-�6�i�`�Y�,�oe�[��Wi��E�#�&
�j�1�+0��N�B�0�M��8��4�M��C��<�;�T�2�uB@逞�v
�J�Z�[�d�e�B�-�jH�e�>�G�]�>�?
�>�=�W
�N�Z�[�d�a�NB逬�V�W��b�b�J�i			B逭��~�i
�H'�d�NB8逯��0�?�K�:�=�<�K�=�<�=�:�=�8�9�8�E�9�6�5�(�/�I�$�%� �!�]����J�����DB逿���b�a�f	�b	�F
�ER
釖�8��G��.��m�	�K�t�^�B釣�|���e��-%�"�KB釡�:�F�
�	
���
��@�zB釬��}�t��6�7��
��B�a���,�b
�i�/
�&R
鉂������0���'��_��t��mB:鉎�L�}�~���u�{�\�	���	�G��A�5�f��*�w�B
��w�z
��G	�N�U��u�z	�+��{�Q�s���=�:�/��_�b�B�S�:�K�NB鉏�z��O�N�N�d�K�JB4鉍��M��������I��������z�w���@�v�{����	�����H��)�vR錜��o���B��?�r��� ��)�#��n����L�$��G�tB錯�h�x�X�I�e�9�eB
錮�[
�H�
��z�B錳��W�4�5�4�O�_��m��6�'�9�^�9�o�&R	鍩��x�c�6��A����E��$��+��~B鍵�d�H�M�e	�.��T��
�b��_�v�"�n�U�X��h�#�c�H�:��F��W�hB5鍼�e�W
�t�}�I
�h�e�h�e
�r�J�_	�V�W�F�L	�S�A��,�-B5鍳��%�h�B�e�d�e�E�D
�+�
�$�E�#�H�I� �Y�A�	� �Y	�d�#�N�O�D� ��@�G�$�Y�I��L�M�J���#
���L�M���L�L�L�B"鍴��z�O
�]
�I	
�E�T�9�ER閟����}����'�C�x�2���w�xB?閭��A�D�D�5�.�I�H�v�� �7�B�U�K�@���)�,���
�<�D�q�4�1�c�L�A�B�u�p�;�h�%�f���>�G�)��}��R�$�k�TB&閫��Y���
�~���F�K�J������t�`�"�S�R�O�N�C�)�(�)�V�P�A�U�T�1�0B閺��&�^�~�Z
�RR
隔�4���r��s�
��*���z�HB)隠�E�6�D�s�n�Z�U�+��B�C��A��o�(�g�|���-�8��O�]�&�I�I�y�r�K�H�K�$�A�|�+�D�1B隤�"�|�}�0�S�T�c�f�i
�t�uB隟��8�cR	霂�)�&��Q��P��Q�	��$��iB=霍�R�C�5�v�5�4�!�B�=��"�F���!���4�5�~�E�D�6�]�$�q�r�!� �m�l�D�G�@�W
�~�Z�3�6���U�&B"霔��\�B�C�F�G�J�}�l�q�o�n�o�,�?�o�*�U�E��>�1�p�>�1	�0�I
�H�I��B�	B霚��?�l
"�D
 R	韆�������J�N����P�<�
B韓���=�k�d�A�:��	A�|�`B韜�B韐��R�K�H�I�y�j�T�A�.�9�H
��9�y�D�I�M�6�1R	頌���L�y�Y�?���e��~BE頗�B�&�K��k�%� � �O��K���$�\�1��0�r�C�3�G
�h�5�Q���6�q�F��4�E�T�C�P�n�D�~�H�Y�/�P�[�T�L�^��e�V���s)�f�N�-�^�-�
�.�i��
�$�i�K�8�� �$���D��D�C�w�j�^��pB飜�9�?�
	�(Bo頡��H�G�H�J�c
�b�K�u�L"�W	� ��S	����T	�E		�A
�L�#�/��C�0	�'�J�P
�p�q�l�M�k�l�mB頹�3�f�7�g�BB頞�8�Z��H�Z��W�x
�Q�"�I�B顖��*��p�j�}
�k$�p�]B<頖��u�a��J�T(�O��C�M�R��L	
�b�MB頦����P�|�R	��j�]�o
�>�A�R�W	BM頙��Y�N��]�P�M��]
�\�Q��H�@
�T
�ER	鮦��P�]���H��;�C��*��O�B鯉�%�$���}�W��bB鮱��c�\�D�c

�M�H�3		
		B%鮰��2�?�L�M�L�M�:�A�!��s��/�b�3�x�-�d�e�f	�]�(�~�)��B��$�A�.�)�8R
鰡�4��^�M�����+�*�;�qB�鰭�;�h�s�z�U�(��>�G�	�d��.�l�O�N�i�p	�E�`�Q�L�8�a�X�1�F�m�f�_�Z�S�k��6�A
��l�|� �f�U�z�T�_�L�S
�Z	)2�M�R�E
�`�e��|�G�]�(�[�2�/����
��;�A�:�7�6���;�:���x�d�e�	�2��d�t�-�D�}�6�;�D�
��}�M�I�B黃�B鰶�n�J�J��N#�Q�`��zB鱥��A�]�w�C&�_�B?鰱��g� �!�L� �!
���	��k�X�x�o
�n�L�m�h�e�d�H�a�I�L	�N�M��H�I�D�F�E�a�@�E�B�>B&鰽��C�E
�{�b
�H�]�[
�O�p�R	R
鼎���\��`�a��u��x��c��<�aB鼠�O�
���K�/�`��6
�W�V�U�]�`����x�"�i(uB8鼙�
���
��8�C�;�8�=�<�D�C
�A�B�<�=�G�Z�n��}�|�q�L�2�D�7��F��-�K�I�[�7�f���h��g�R�N�m�w�f�dB鼚��}�G�Q
R
﨎�Z�G��c��>��_,B福���"��6R侮�F-8	_N�?�$Cd5�&�oDR懲�9_",a�w�~��e�?nA5Z9:P�L�}/R艹�w�I3"P!�l�GjK�[�~�8�B!��	5qpq2Fw�S?�@	B!��*�Mz�	B	︐�L�g �@��D�Bff�C
𡨚���u��#�Q��h;�>��X���L)��"�s=�r�ZC🄀�}C🈂�i4LC眞�-C𠤎�SCB𠀋��~�%�H�q�A���Q�R�{�[��E�z^�C���l��N�t�1�0����o�r�e�?��*��W�3��
�O��b��3�`�
��N���Y��O�D���'�/�K�	���V��_�[
�*%�)�Q�2�).��Z���d���	��t�Z�
�>�R2�8
�t�C�f�>�e�h�
�d�,�I/�F�^@�
!�W�eX$�
�h�}�z�C𢘉�S��4+�{;��I�/�oC�𠂉��Q�n�,��O�7
�P�~��x�w2�~
��`�u�	�g$���g�	��a,�
	�[�]�W�X�M��(��h��o�,��Q��{�:�)�E�f���W�Y		�h���i�2�����\�B��9��.�g�
�c�(�M��v�|.��B�L�-0�,�|"�]	�e�~2�u�y�o�5�3�R�M�("�Y�c�l��d
�Z�&�@
�(�q�	�%�N��W�l�7�}(�l�{
��{�d�$�v4��f�wB(�r�`��~
���c�x�m�!�_�i�
�v�t�q���	�w�w�
�O��6���X�|�m���g�\�z�x�w��P
���&�U�{�;"�^���m��y�s*��w�i�<�	��i�b�%$�<�n�#��{�U�X�v+�`H"�Q�x�
�R�g�n�z�n�r�~�.
�H�0
�g�o��y0
�{�s�J��0�Y��~����k"�K0!
�\�$
�Y-�g�s�\-�`� �	�c�R�!��
��g�^ �~� �h�v�u �+"�h0�y�s��y
�d&��
�R�6�V��{�2�*�y�P�/C,𠃵��Y�b"��z�[X�M��iB�(
��*��Y�x�]�-��Yh�����S�����,�v�A��	�|��t�g�g�f��x�I�*��E,�YF�\	��}��p��~�x` ;>a¡e��!�A�$�*�;�A�G�X�A���s���@����#�*	�K�@�UaǸ�s�h��|��k�n���v�a΄��]��L!��T�\��Z�t
��_��j�
��l��wb†��Dn�U�a��m��y�=�{�V�i�g�!�*��U�y�	�*�$�k>�J�@�	��
��@6�<���Bp��"	����F�0�G�@�f��Ib享���`�9b�OⅠ	��!	��j�H�v�!�r�n�r���@��~�0�J?�7�@�w!�7�E�M�&�5��?�\����^�F���M�:�n�!�0��~�� �y�o��B�_	�&��=�\�_��4�1�V��x���8��c�X�v�e�&�w�j����X�%��7�
�E�r�v�w��o�*��{�Y��'�7�n�@�k�[��f��u�^��k�!/�(�~�~�|�u�}��J����l�,��h��|�l��~�U��	��m�B(�M�^�Z��0��B�L�Q��`��h�p�s�}��{��}�>��d�p�o�g���j��p�\�3�d�c�|���V�� �>�T�\�*�{
�u�`��d�	���n�!�%�j�9�<��P�{��]���$�v�>�A�u�K��\�g�u�m�	��
�o��
���[�/���>��t�x���E��'�-�1�^�U�~��r�Y�W���K�F�I��Y�p�|���k�J�M�r�_#�h��r�H�t�$��\����_��p��T�~�m�3"�D�B�=��R�s�i�h�m����1��E�J��b�P�x"�~�z��k�*�0�R�7��;�>�~�F�H�`�i	�k�m�~�{�~��]�����p��
�T�{�s�o�{��%���I���%�(��j����$�{�e�O�T�[�a�]�k�e�B�g�_b⁴�"�F	�(�	����d�b⅖�9bⒶ	�c�@�m�I	���k	��r��`	�]�h�v��P.�-b↖�,�*��Y�i�p�x��B��n�a�L�K�b�q�|����� �C�#�(�*�3��]���cb﹅�_b者�%b⺌�	bNḾ�q��e����L�p�r��m�i�v��a�u��D�X)�\�O�^�;�q��A�w������=�!�G�y�K��U��^�l�t��	�T�#��(��,�\�2��t�S�m�j�:�|�[�
��#�~�0�d�?�x�H��O��m�z���t��t�"��8�m�@��H�K�y�Z�_�S�m�
����
��y���(�q�2�y�<��T�K�r�}�x������ ��g�0��o��|��
�q��f��h�)�0�O��v�m�\�}�(�1�[�<��E��L�r�S��Y�|�^��k�Tb⁇��'�8�}�@��(	�_�Q���i��:�v�I��O��Z�E��F�u��	�l�wb�㕝��=���x����E���l��Y��~&���P��_���k���s���z���+�H���.���J��T���9�#���b��M!��R�F��
,���g���k��-��0�"��:�P��<�.��I�h��_������=�e���|��R��V��b��p�'���q��H�d������K�^��Y�q��*���R	��y�R��m���
���x�����5���?�T��'��T�[��Z�+���H��k��o�~���X�����'��)��,���.�l��K����<���u�_��D���h�� ��R�r���
��M�~��V�`��d�z��p�
���d��g�}��������2�l���c��D� ��i�b��@��B������$�v��X�O��G���S��V�~��i��g���t���t�����)��)�^��2���?�{��N��P�s��]���g�{��n�u���j��L������.�q��4���B�s��F�q��L�������n��*�]�����*��a��8��h�b���b��p�{��y����$��	�}��P�g���g��b��5�t��@���k� ��H�l�����i���x�n������s��
�d���������"�N��'���L	��N�`��`�n��l��?��t��w�h����B����J��E�%��R�T��eb≤���k��kb�;乴��W�U��h�.��x�B��{��0��	�]���p����
���k��%�~��.	��1��3�g��N��U�n��j��m�Z��u�	��}���
�]�����!�{��)�j��3��6���B�Y��E!��J�a��m��q�o��|!���M��"�
��8��;��?�f��F���Q�{��\�l��d��h�U��x���	����z���^������+�q��;��=�p��O��Q�{��W��Y��_�_��n&��r��u�i��$�]��(1��/�v��?��A�Q��D���V�e��[�8��n�q���j��
����T��((��+��.�U��1�o��6���D��F��J�I��L�!��R����~����Y��8�1��C�X��H���R���j���q����|���Q�����p��"���)��,�k��@���s����x�����~��$�H��@��D���U����g���
�M�����n��&��(�m��1���N�\��P�y��U���e���p�M��y�	�������
����E��$���A���N���T���h�g��q%��w�	�����W���
��-��0�O��3�|��V1��Z�d��^�c��a���h�
��q��t�p��}	����	�����c���y��-�~��3��6�Y��;��>��A��C��F��I�e��O��R�s��[�v��e�b��~2���M�����$���,��/�[��2���D�K��M1��V�q��z��}���s���P�� 3��&�[��)��.���L�^��Q�}��W��Z�{��`�z��g�{��l���v����P�����
c
🄐���@	�%��A�@	�Q������%��z��P��U�0��PK
!<���}��2chrome/pdfjs/content/web/cmaps/UniJIS-UTF8-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�
UniJIS-UTF8-HA°�MB←�bB	‐�U��dB′�Q�P6R⎛�t&+"'�+�B�9R┌�G(B┥�o/R	╀�uB ☜����z�wz�aO�_�0�/�Y
]�C�B✂�B゠�KR㌃��J�W�V�mB㌔�


�R��?�[�B㌕�1$1
�F)')��Lq
B㌗�L


�B	b─�9�7�=�;�A�?�j�]�`�U�X�w�I�~��v�O	�c�]��H����Q�[b〘�k�g�6�E�P�T�\�b�h�t�C�w�|�~����mb⎰�^��d�I�	�M�XPK
!<��Sbn�n�7chrome/pdfjs/content/web/cmaps/UniJIS2004-UTF16-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE���������!A\a
!�GQ�k]>�d��_?V3�A���M�T�+ 2!+�V�S�L�IQ��tA
�~$�c���
Q
��n$�c�U�P
�&%$)A$�)
�f�7�![Q .��s"'��A��"�}
�";�9�6"��B��;
��|�,"�m����Q/P���'
��j�>"�kOL
i��O��fO�>��_c�0
�m��("��[��<��SA�i��P.Q��2��j���	�
���A�����k9Q
P�X��"��"O(
��zA
^�W-*'0#rQl�6B''�$�c�bAx��1TI�6��7�ZQ��94
M�-�|3��)F�Am�b	>M#=:B�@�;	���J�<O@�0�G�"8��F.��q.�J	�N
M�y�u�>�:�E�<fG>+	'c���h	W~�9�2	
�|C	l�
;ls9'�
��V�h�i� N�.:�K� �e�,�d�[�5�J��|�O�r�]A3 Z�Z�+�.�/�?
�x�vC	F
�Q'�

�j�w�
�*�"e4
�?	�^(��D@�����
�9�zAA��c��,
�`��2�H�j&�_;/1� !�f	�P�c�Z�w
65
�h6

4!�;4/Q�fB��.+�T��*�
U	�9�.��53�6�?C��$<(�#��Aa��J��#�"k
l	�)�@�]VI�b0#
8%#
#'`�p�|�k�`-�k]�FI�[-�&�3�w;<;� y�e�5�@T�
K�9G�$��+��C�U��p�t�m�Z�$�;�M�
�v��3�\�7��V�=�/�j�U�_(�A�K�v�9�y�.�@�7��#�A.���eA.���:A���K
�_�!�W�:�r�Q/�0��i�� �w��w�8�5�R�t
�{�v�\Q/�S��h�>�
�c�r�/�qQ�/#�&�?�
�C�Z��c��c�T�c{�i�7�&&�S�����o���(�_�4�u��w��$��n��B�S�^�n�}�H�a�$�/�7�R�4��	�t�-� .�Y�O�@�4�<�{�0��P��S�_�(��VKp��A��X��]�X�`�-�t�G�W��-�e�"��N�O�x���e?�K�j8�]�/���8�G�U�F��
��a��h�(�.��\�z(����$��X��[�o�p��%�t�:�+�T�}�R�a� �G���6]�C�3�!�Y�"�o5�n Q/��N���<�7�J�a��r��C�8��w��z�A
/Ӟ~/�_�H
�+��<��D�w*�3�`A00�:
�a�aA0<�Bc^Q23�Op}��$
ly�nA2Q�Q2��g�

�D�5�,�=��2
���^A3�pA2��ZA2��{Q3�j�r�[�Z�AN�0��+A23�"��
��
��*�?�
�#�F�?�#�,�y�`'�>�M�;�:�J	�=�K��A�&�w�P�y�{A3�x


	�5
&A4�$�&�b9�S�6�m�h�N�	T� ���
�`�(��2�n$�Q�@"�Q�cA4��lR#�.�g
O�8o��x�m�jH�9A�'4��Z%=,'0"�k�z�p�k�H
7(
�p�a�5�5F>5$�LK&&.	?��i�GT4�\�#�]�r>
�K4##P\	8
�9	�P" �Z�A��8>F�2�i5%"�$�09�g��
�B^�g�6"
�\�_�,HBP
&�7J$(�*V
8�C�n
!4
3<	5&Y�1��<&��C$�`�j0�Q_ 
\(�pD�G��$%�	�f@�z�y6�d	$�.~.B�dC�D��&�r�}v%Qo�
�3QN�*�9�!��#����}�D��R��1�U��AVN�L�^�i��O�2�w�l�i�@�m�3�2���M�(�7�X�/�u�ve�	�R�\��g�f��N�� �z�3�
�i�Z�j��9�{�:�	��~�W�d�Q���}�x�X�S�w�h��N�
������I�@�G�F�5�K��r�#�yA
N�Z�
�a��e�d$
"��
AN)��U�x�y	
�t�u�T�"�y�x�w�nQ	N݅����g��N��/�F�7AN��9�z�P�A�,AN끆b�F�GAN聤]Q	N��Z�4�d�l�m�g���p��SQO��a��	���4�<��k�Y��M�'��\AO�q���L�xAO�kAO��f�x�3QO3��d��)��(��w��z��k��G��.��Q���PAO@��f��u����N�;QOM��\�u�C�R��6��y����a�r�/�����j���b��p�AAOi�)�Q��0��\�i�AOz�pAOc��k�	�5�:��>�?Q
O��%����k�!��
��W��V���������r��oAO��.����3	�$�[��q���/�5�l�CAO��<A
O��s��M�L��<�L��z�Q
Oȁ�~��%���*��������}��|���AOז�F���{�X
�g�.��B�0���
AO�l�(AO܁��#	�"�?�$	���(�u�dQP	�T��`����2����x����p��e�a��>��wQ
P�*��6�m��O� ��~�z����yQP!�B�z��i�d�v���
�I�T�;��(A+P6�^�N	��y��X��q��A�(�t�'�F���e�O����?�n�R	�E�$�q�
�)��G�>�@�/�.AP@�?
)#0�1�2A2P0���� �t�u�5�{�$�y�x�y�5�{�&�'�&�}	�|�7�6��-�0�5�}�0�1�(�;�:��)��
�o� �1�9�lQ	P၇
�4��������L��k���+Q
P�_��J�R�T��j���I��AP��b���E�h�	�qt�E�D�9�:AQ2�vAP���+����-�
�8�E�F�)�(�C��(QQA�8����!�1�h�d�;�^�C�:��d�?��P�x��gAQR�r�9��B�?�A��)�h�.�7�Z�m�[�
�M�`��sAQd�JAQS��;�I�F�_��T�_�
��W�>�H�S�QQ��~�#��P��3������6�7�#�"A Q��Q�3�+�D�{�v�s�g�f�o�t�1�j�D���Y�X�/��[�~��+�*��5�R�p�3�2AQ��K���a�`AQ�����	
��1�`��j���.�-�<�=�'�P�gQQ����p�Q���u�s��L��E��zATQ��5�F�}�
��=�}�A�j���C��:���y�x�Q�P�)�?�I�.��-��B�;�Q�$�`�	�]�`�9��(��y�7�>�a�R�3�8��&�x�}�r�3�U�U�p�{�$�7�jF�P���Y�Z�o�Y�n�O�$�i�h�/�0AR�NG�a>�`	�X�Y�GA1R��o�#���N	�1	�$�g���Z�'�+�P�'�C��*�+�N�M�N�#�I��P	�O�N��$�!�z�g�
�Z�}QR�R����)�h����v��a�`�o��*��=�3��n�A�@AR��|�{�1�t�K�b�
AS�SAR쁥a�
�H�B�g�#�B��,�E�DQ
S�M�!�x��V��u�B��0������K��s�SAS#�O�A�@AS$�UAS%��i�K�T�U�)QS8�S�{�m�o���U�Q���x���T��KQ	SE�V��m��
����
ASQ�p�9r�]�J�/�h�.�<��� ASa�!ASY��q�#�J�%Q
Sl�"�J��W�+�^�X��;�^ASw�`�S�N�I�H��U�
�p��K�J�y�x�A�n�P��]�8���UN{�<AS�m�R(�]ASy��	�'��(�R�I�L�Y�U�*��n�E�U�T�-���~�W�V��(�P��{�7QS�Y��J�A�f��]�Z�Eq�5��
�6��#�#�w�P�H�W��E��#��p�7��1�R�>AS��r����?�QT�y�/�0�i���/�^�E���`�`A&T��l�C�8���C�<��b�*�t	�;�:�W��}�]�V�t�
��/�B�'��� �vA	T��4�O
�0��
AT����"�^�_�#��]�\����G�TQT���%�X��m�y�����Y����AT���H�7�%��p��7��CQT����t������z����@�
�6��5AT������QT���L��}��B�8����=�8��2���$�0��p��{A$TǢ
�5�@�=�L���m�n����J���o��5�}�:���+�2�7�.�m�=�*
AT��^�A'T��2�D�E�T�n	�o���o�n�o��E�\�a�J�K�^�j��
���O�N�s��p�Q�6�q�Q
U{�4��H��S��Z��g��l�n��K�A(U��<��z�o�t�
��O�f	�O�`�M�c~�&�%
�4pi
�M�P�1�W
�~�o�w�h
�YAU��_)�_A,U��:�Z���r�s�r�u�	����s����}�|
������j�k��i�b�m��z�%� �QV/�U��8���P��D��C��X��Y��\��[��TAVN�L
�k�v�S�V	�1�#�<��k�l�Y�\AVB�Z	��bE�kA+V;��*��{�i�h�x��k�v�w�v	��i�f�g�d�

��u��~��g�d�%�*�r�}�%� �a�b�%�*�t�5�6Q
VȢg��L�i��H�w���3��8�t��/AVӢjQVעk�_����=���J�e��9����
��ACV�
�R�1�� ��e��V�}�~�O��d�K�J��*�y�r
�`�W��6�.��y��_�6����;�4�c�b�%�1�V�y��v�K��6��_AWY�`FAFV��P�d��u�t�t�k�x�p���m�h��y�{�p�m�h��u�t���t��o�r�I�H�y�	�I�H�{��x��z��y�z���	����W�R�����c�-�~���Q
W��e��x���O��l�����v���3A8X�
�y�@�D	�c�b�)�7�O�~�_�}��%�.�c�L�?�B�54�\�I�a�@��.�V	����g�v
�S�s�D��M�L�U�P
�#�� �O��>AX/�2�,+<�>A2X��a�y�x���
���~�	�v�o�~�	��~	�}�f�	��]�p�|�
���|�}�|�a�t��z��e���
�`���z��#QX��h��y������z��_��z����vA7Xܣ1�)�t�|�:�)�_�P�G�|�3�2�k�j�-�1����:�
o�|�q�R�#�P�Y��6�]�\��P����c�^�c�d�x�O�T�[AY�iGAX݁�y����� �|��"�|�/�.�/�w�$�%�$�;�8����=�<��1Q	Yg�M�y�z�Y��J��w��B��[��ArYs��&�t�3�2�u�4�T��|��8�i�z�O�a��E�"��3�.��s�A�|�$�u��� �X��]
�o� �B�_�r�/�2�O�J	��t��#�!�D�� �=�<�;�g�*�
 ��K�L��
�=�V	����	�G�J�	�#�$
�A�B���w��a�_�H�9�t��!� �]�\NOAY��nn��,�AY��q
�
��m�l#

	,4�?�>A;Yy��j�?�%�`�?�<�?�'�&�8�9�6�3�7�67�=�:	�;�:�����K�J��������
�aA)Yr��	*

!
-IQ[����H�G����H��I��J��[������e�[�h��*��{Q[���C�l��5��*���}�p�>��,�bA[��n��D�O�R�z�5��W�����CA[���w
�A[���=
Q	[��S�:��"��g��G�t��:A�[ɤ���e��J�O��B�W�M��o�X�+�D�)�(�G�B�/�}�L���/�/�L��}����1�k��'�s��u�t�S�ZI�R�2�e�b�i�/�P�C�B�k�`�o�`��w�E�9�J���	�[�d�
��� �;�2�
��w��G�>�[�_��:��K�F�O�J	�� �_�h
�s�r	�!��w�r	�'�>�e�d�u�H8		�o�n���5�KA[�sE2�	�>�{C�P:1bA\�*�?	�2N�O
�N/G�%�A-[΁�z�{�z5	�
��������5���

�%�&�-�0�5�2�5�6A[ׁ�z�$2�%
	"o#2$CA&[́�D
$ 	

"#


Q
]����c��������o�X�Y�e��PA/]�m�	�W�^�y�s�j��x�7�.�T�q�4���m�h�[�=�
���o��O��"�� �q�b�G�B���a�`
�!�>�UA]��A]�� �T�S�|�$�'�"�|�U�M�L�-
�z
�y�)�N�T�M	�v�RQ^w����)��$�/�7�C�$����)A$^��?�2�2�31�
T�g��n�g�C�g���`�o�^
�U��&�n�K�L�g�l	A^��pH
A^����N
���}�-�*�z�U�'�&�'�K�B�U�Q�(�zQ^������t����0��{��2��W�����W��V��w�Q_���-�,��U���M��L��+�`�&A
_�w���F�Y�i����s��t��}Q	_!��d�F�^���$��h��%A_-�$
�3�^�5�z��3�B��~�w�z �1��;�X�O�t�d�)�i��iA_4��!!� A_.���-�=�<�B�f�+�*�	��C�@�A�(��!�	�H��Q
_|�t��L�b��1�-�`�F�7�6A_��9��-�R�h�e�}��D�GA_��<�"�Y�4�"�!A_���Q	_��>���!���f�}��9�e��dA_���-�L�*�
�F�W�P�H�;�	�J�?�6��|c�?�:�m�v

A_���5 �4A_��8����3��� ��5�,��5�4���9
�*��Q`�V��������p�*��'��z��s�C�A4` �5�&�#�4���
�+�2�S�J
�7�8�g�0��>!&�#�o�A�>�o�:��(�~	���I�2�k�^�!�pA`]�'A`��&���;�(
��������)�B�	��E�D�-�,�)�(Q	`���7��
��8�R��{�f��^��_�Q	`��O�"��Y�"Z�����A`��c�&�?�>�	�z�A`��
A`���!��.�1��'�"Q`إz��R��/����E��f��5�c�4����GA`�g�K�F�>�S�F�_�^�#�&
�m�]�D�%���"A`���%�$A`偨@�7�)�\�=�<�?�>�;�z�>�;�%�$�8�A��D�C�B�?QaD���j�;��I�����}�/�e�l�A
aS�!�Q�L�i�q�R���Aa^��(�u�tAal��LQan��6����\��q
��v��nA5a~�Ge��S��P	�Q�i�a�
�7�A���z�5�.�y�z�i�n�=�P	���w��Y*��Aa��zA%a{��*�'�&�L�w	�2�B�O�)�v�E�3�v�E
�r���4�p�q�F��~�Y�V�W�V�S�R�a
�/Q
b�p�,��8���9�#��Z��9��:�$A?b&��l�o�n�+�m�V��m��G�
�I�A�3�$�S��M��)�
�(�3�<�y����r�=��,�w�n�/�x�T�K�m�~�5�r�Q�{�*�|����s�d	A
b)�^�C�_� .�=>�<Ab'��f�e�`�_�^���a�-�,�n�u�\�i�q�p
�lQb̦i�	��"��)�M�F�-����H�1���5�p��v��9��AA7b��M
<�B�	�=�D�
�k�h�f�3�N���Y�L�U�q<�
����v�1	R�#�~	�
�'
�X�#����?�<L�,�?�w�<�G�8�s�d��yAc=�bG�A7bꁨx�{�/�<�h�)�(�)�<�j��+�c
�
��~�k�h�-�(�c�b�|�}�e�`�}�|�c�b	�/�.�a�`��-�,��7�{�,�/�,�-�J�^�e�dQc��r��]�#�k�h����������D�E�j��A��VA�3c����2�I�L
�5�I�|�q6�{��2�+�W�
�9�>�q�|
=	4��c�X�q��>�8	�}�t���
�E�V	�C�:�q�7�E�l��M���/��c�n	������v�1�:�E�@MPAB��:����s�#�
��� ���?�@�/��V�#�4�(�w��\�|���E�&�m�`E�!���1�0�7�"�H�H�
�h�9�&�2��i� �^�5�.�u�x���O�P�O�8�A�]�H�n�{�e�Z���;�6�����P�e��,Acþ'1�V�O<,� �g"�f�c�b�#?�"+�~�1Ac��s
�1	�N�`1
N�=�@-< ��A,c���"�_�b+*�q�p		
$�w�x	
��
	��	
$AcꁗJ'
9*1		Ado��(�70A-c���%!	#
"4
Qf�k��6�o��w�Y��<�+�(��W�}��R��k�e��N��+�S��O�l�!��R��Af4�j�*�/�>5�G��D�a�Z�_�BAf3��v	
�.�'�W�~�Af7��L
Q
f]�q��6���3�����V�yA-fi�i�R�g�,�C�c�T�#�p�c�Z�� �{�f�q�d�Q�R^�2���!� �%��2��1�@�Afs�"%A fj��Q�	����S�L�}�5�4����(�T�=�f�7�z��[�Z�E�D�E�D��Qf���'�P��m�	�[�p���NA,g�K��$��=�<�Y�n�h�w�S�K��3�Zm5�/�B�}�N�.�I��6�9�J�1����-�&�#�"���P�$Ag�(D�3�4Ag��
�����i
�7�6�f�a�`�S�O���
�'Qgl��i��;��H��M�,�y�x������`��;A	g|�$�}�b�"�9�$
�gAg{��2�k
�j��YAg���j	Q	g���� ��#��L�T��1��Z��o�>Ag��n�t�]��x��!Qg����0�8�9��]����[�:

Ag��(�P�N�K�F�%��m�F�#�H�P�6Ag��+A
g���{�h��~��~��)�&�|Q
g�6��h��o����e�%���$�Ag��*�d�}��n�o�T}�1�v�_�g�F�Y�b�U�]�dAg��
�'Ag���J�%�z���~�i�h�~�}�5�0�~�}Qh;��~��-�,��~�x��{�	5�
��� ��A!hH�
�j�{x��X�>�9�j�5��I�^�?�& !�}��u��%�&	�[�,�E�AhR�,;�!AhI���v�w�r�3�@�C�4�5�4�w�A�&��u�V�O�h��9�6Q
h��K��v��o��6����Q�����)A	h��Q�[�]�p�=��C�NAh�/Ah���Qh��0�v�6��5��4��O������|��gAhߘi�
�I�Z�-�&�e�x�DAh��.Ahꁪ��A�@��]�pQi�� ��s���d�]��
��h����2�q�t�d��t�=����A7i�@�c�
� 
�e
���}�S�
��*�'�$�=K�)�/�c�,
� �?!�Y���n��d��g�2	�V�	�+�:�W�N�K�
�UD�\�#�B�
�J���S
�H�e�l�)
�Y�XAqi�p+& 7,

 8=$	


&
18

��		�o�j/0
	
	ad	Aib�X�2!�Y	�z�^.���h(�#$�"�y�z$
\��A"i5�5�I�H�s�v	2�[�Z
�
	�X�U'J
1�{�zA2iB��
�4�;�4
�?
�>	�E�D�I	�L�Q�P�U�Z	�g�h�i�j	

�
�
Ai��"	B5X
	=H�R
�O7Ai��72#

Q	k_�b��n��o�m�V���p��Q��nAki�2�n�O��r��t�E�=�!�
�.��A	kl��c������
�Akk��?Q
k���I�[���o��H��G��DA4k��u����u�@�A�r�y�2�o�2�(��4�#����3�~��X��
�!�<��X��E�^�v�m��l�Q�^�'��Ak��=h�GA/k���j�
�1�q� � �#�`
�;�'�p�u�&�L�u�t�O�N�O�+�^�k�p�k�&�>��s�r�A�@�u�y�("��A�3�y�j�u�wQ	l��
���^�)�M��4����<Al��1�2Ql���3���-��u�� �;��m�@��2Al���c� �j��P� �(��4�X
�'�H�H���&Al��IA	l���\�w�E�:�y
�E�;�vQ	lρ�`�o�n�}����&��7��6��'A6l٪!��%��B�i�l�O�6�Q��h�%��'��(��g�D�(�k�r�E�@�Q�l�5�x�2�e�\�)�j�
�{�p�l��|�`��\�2Al��B)jA*l܁��-�j�?�+�f��F�#�^�k�h�i�h	�C�B�m�.�O�r�s�L�M�e�d�J�1�h�9�*�]�h�9�*�]��NQ	m����.��{��p��g� ��L�1��_Am��mX�X�g�;��g{�l
�A�J�
�i�^�=�6
��"
��+Am���)�`��]�H�I�J�K�J�U�w�Q�,�j	�KAm���n"Qm�K��N���K��C������X��	�T�#�%�,An�Z�'�r�T��]�U�,*�1�An�FAn��uQn�^��
����t��f��U�S�@�rA3n)�9�6��~�w��X�e�h�{�E�>�/�P��*�y�q�t�C�F
�?���f	�=�R�{�t�n�W�G�4�F�M
�a�T�B�A�R�E
�+An2��&�)� �Y�h�v	�{�[�^���r	�w�M�B��r�}�An;��w
$Q	n�����C��8��e�X���	�A#nыH�8	�3%
�p
�)�41��0��E��.�e�8
��e��)���
�

�c�@�CAn�8)�"
�m�l���q
�p�f�[�X�Y�\A	o��	
QoW��A��o��L�U���S��j������!�SA=od�k	�8X�a��|�	�>�.�k�B	�~�7�	>�s�:�W�|��C�A�V��;
�>�g)��	�h�|�%�
��A
��O�h�Q�	�x
�C��u�Z�	�|�K�G�F��}#�m
�_��{��"�	�%Agof�DM




�A�B+2	
uv.
/		

#

Ao��Q,?�_�^�	�
�U5)�0/�#��%�WH�X
 �{�J�9
�h �o�p'�<M�A'o}�

�%�$#�?�! * �n�m)
Ao~��3

%]&.?9"2	PA.oh��C'	*	" 
!6*,Ao���3U(�0-I�i�lA"��A)ol��	">E
!
	1

	
Q
rW����+�d��P����d��Q��Z��}����'�J��JAErg��
�?�~�	�X�Y�~���[
�(�I�@�g
���_
�p�`���V
�1�\����n�N�8�E��~�Y�=
�v��$�?�j�*�	�
��Y�e�N �E����!#�E�_��Z�q�v,�Q� �i���H!�	�.�8AWrt�k	

	
	7	4+	
)("
Ar��aeRE5�w$�xaA9r��;�%�$3,��(�`�a	�O�N	
�H�I
�@�=
		�WAr���I�(;�
85A6rx��
&
	,	#	Arh��HD�T�U
*
�
</�>#�A/rk��A	/ 
	@ 	Qu�J�]��V�*���-����]��>������k��/�0A
u0�>�<��Z�
�~��E�HAu/�rAu6���E�!�dQ
uF�S��^�g�������X��=��d��'��&AuT�^��JQ
uY�y�4�q�!��*�H��
����OA	ud�V�E�#�f�W�Z��D��EQuo�u�-��h��k�~��d��m�-�(��D�.AFu�@�@��'�U��[�j�e�V�Q�L��t�k��|�?�g�"��������O�N	
�w�z�[�V�a�d
�+�s��c�zAu��~#/�c�b	��

�w�vA8u���	�c�b�S�R�c��
�\�{�Y�	�����	���������!���@�=��B�7�4�E�o�n�s�8�3�2�I�i�h
�i�.�G�+	�BQ	v������O�7�o��`��!� �
AGv��w�
����;�`��^��P	�x��]��
�<��2�N�p�g�Y� �m�\�HE�C�Z�'�fp�a�&�{�*����'�T���=
�R�#�a�G�
��G�y�6�1�d�{�P�Y��6�;�=�#�L�0�D�A�� AVv��

�%�&	
	


$.	
		=:A
v��y�=��>�oB�/{�,�5A v��7%
��
��;	
��:�!5)
5Av���^
`Kb*>OA'v���

."#?	"	
	A)v���r
	
E$!	8A4v���!		=
" +


Q	y:�T��H������y��@��C�j��VA1yG�k�U�^�X�J�{�r�[�`�t�?�W�p�O	�T�$*��$�Y��k��:�(��i�h��	�y�}��W��:�;�S�T�6�%AyE�;�v�	�N�O�J�K��T�K	�~��|�F�G��~�D�E��m	�n��AyD��Y
Qy݁�g�W��k��@��)��(��)�����G�Ay��f�;
�-�D�$���Y��X�'�v�
���SAyꁙ&�S�P�E�%�$��8�C���>Ay�k
Q
z7� ��\��>�����t�VAzB�6�T�?�@�]�	���b���s�t�Q��P�yAzE�K
A
zD��+�7�H�K��@�E
Q
z}�*����'������d��)�?��2�ZAz��+��Z�>Q	z���Z�>��5�g����A	z��x	�n�i�w�t�j�G�JAz��QAz���{
�A�&�#�"�#Q
zËT�@��B��=���|�����^��O����
A
z��	�	�n�������#�-Q	z��d���.�6��1�J��d��wAz�z��Q�0�7�y�7��	�X�N�#A{�WA	z��;�:��\	�j	�I�|Q	{%� �j������h�9���U��vA{3�J�K�V�o�M�A{1�Z	�T�B�'A{0��Q{K�R�����'��k�0��N��S��v��A{���A{]�i!&A{`�]�(�+�T�U�L�M�&�j�o�	�8�+�-Q{��)���2��	�.��$��)	�X�W��"��_A{��{�T�a�p�^�5�LA{��bA	{����3�0�+�*�+�.Q	{Ěc��0���t��R��y��F��%AN{Ϯn
���Z�.�-�<	��
�z�q�{�r�u�v���#�&/.�y�x���!�-��0�5�#�E�B�'�'�*�s�6�B���;�fA{��c1


�C@�BA*{ԁ�
�h�i�f�i�z�l�m�l�m
�@�U�q���n�u�t�|+�S�w	�t�u��n	�g�f�eA{ہ�&		)
Q	|ү&��H�H�C��3��z�����,A|ܯ'�E�/�+��1�2�u�t��C��V�c�K�(��A|��v�H�I	�J
A|遭1Q}��2�����'�K�������k�I�0�c��T���HA} �I�L���*�nQ
}*��6�����z����=����3�_A}5�9��5��XQ
}?�8��Z�Q����.�R
��X��1A }K�5��,��t�S�t�/��{�r���k��Y�T�9�V�h�1�H�x�
A}M��N���
�T�5�!�T	�W�X�>�{�z��Z�>�A�<��BA}Q��8
Q}���6��O�S��P�s���b��E�2��T��M�{�����A!}��N��(�l�u�X�#�P�?��*�y�k�`��	�k�;��k�r�/�*��d�2
	A}���8�I	���;	�a��9�i�h�i�fA}G	Q	~�1��2����Z��P��Y
Ai~&�N��{�	
5�3�N���w��<�S��t�8�Q�|��O�j�m�b�T�I
�)����
����Q�T�d���z�4�M�L�!��G�H�!� �b�_�?�6���?�6��K�-�x
���d�c�[�A~G��O�N�+
/-
A#~'��B�[�Z�,
�1)�0�1�0�1�0�+� �&�%�"�����
�
���A~-��L/�)	&	Q	��>�<��j��i�6�w�r��b�Q�
��c��O�O��.����g��d��O��N�������A��@
�Y�D���
�Q�P���3�2A
���n��|��*��|�}�|A���f	Q	�o�F���6��;���>��9�IA+�y�Q�i�h�9�Q�$�O�4�;�@�x��z�Z��I�$�T�u�i�T�l�Y�z�G��'�N	����8A�{��j�

�
��~��~��~�/�.�
�/�.A����p7Q�큙t���!�������5����F�����������}��A$��7��n�~�e���l��O�e�v�k�,�;�%��F��
�h��
�.
�s�v���q�A�j��>��8�A+�	�d 
�r�m		
A
��2�5�4��4/)	A=���y�~�	�
�~����z���{�z
��?�D��C�8�j�k�
�{�z�����}�t���M�L�z�K�@��	�u�O�N�v�u�v�u�p��	��}�p�oQ
���M�r�X��\���&�M��0��eA��	Q
���c�-��x�p��Q�	��\��]�WA�!�=�0�v��e�y�x��Q	�2�>��7��6��-��q�\�w��NA
�G���*�^�I�7�9
�D�M�YA
�@�$�BA%�<���t��m�5�0�1�$�%�$�q�U�V�w�v�j�
�W�c�D�i�V���Y�I� �^�9�f�sQ����+�R��?�k��~��u������(��-��,Q	���E��%�?��0��k��j��	��6�JA	�ŋ_��R��k�p�$�e�^A��G�JA�Ё�	�Q�ޱE��L��S��h��i��P��+�W� ��tA��)�m�
	�(�y�J�U��T�=�B�;�Z�xA�ꁚ�S��M�L��A�큮%3Q�+�T��,�u�t��3��H��U��>��E�S����'�h��BA�@�O�J�
�A�<��9�a�`�A�=��/Q
�O�c�Z��p��K��,��U��A��A�w�YWA�b��m�p�
;4A�Z�z�h��/�Q�~�UQ	���O�N��?��P�l�m��m�A7���[�O�@�*�_�2�w�h�*�!�@�b�I��N�k�V�!�^� �0�f�W�7�>��!�3�<�#��+�6�Y�\%�M�\�*�=d�y�ZA����5�K�d�������i
��+�*�K�M�f!�m�l�"�A����9	)0Q	�_��;���E�P�)���g��?��$Q	�i���������d��5A�u�n�$�A�4��	�C��W�,-�]�A
�s��=�M�z�3
�B��5�4��A�t��JQ���`�Z��b��[�=��X�������	��A7�ė�r�/��m�v�;�>�,�s�W�(�Y���f��
�~�S�Z�;�~�T��#�w�r�S��h�i�P�i��t�r�CJ�a�w�T�v�_	
A�Ӂ�_���z�
�C	�P�G�J�K�G��u�n�
�H
�J
�K�TA�́�R%	Q	���,��t�e����r��q��p�EA����PA���:A���a�(�b�c�b�EQ
���-��z������w�,�{��r��F��?��AN���>���9�:�
��C�&�@�%�'�"?@��r�m�
�W� �6
�	�;�J��d�/�L�O�N��)�H�b�
��=�R
	
�5�6�C�'���
�	�3�,�Y��t��	�	�A���b%�G�H(��
�K�JaA.����|
�k�n�s�r�s�r�o�n�m�l�s�t�/�`�5�B�C�
�x'��(�%
�A"����m	
"
#	Q
��X��^�"�s��m��l��m��|A���>,�Y�(��d�
�,T>��&A-�
�r
 


�,�7			
 
���AQ���#�n�o�a�n�o�\��
�a�l�Z�i�h�]�
�c�b�h�k��b
�U�l��
�3�=�t�v�h�c�b�]�t�}�|���d�[�x�z�y�\�e�d�_�^�_�^�i�h�c�b�a�`�]�	�b�o�n�k�*#��f�=�+Q
�
���|��q��,�k���CA���=��	�U�V�g�)�z��)A��A���<�x�}�|	�m�n�o�j�k�j��m�j��k�j�Q�U��C�w��c��h��9��&��!�� ��
�b��.�x��?����HA-�h�/��x�/j�b
S�Z�y�	�
�d�	�d��W�	�t�W� �a;�\�S�L��F�L�<��q�;�3�d�z��d�+��={�v�u�D�UAJ�k�3

				
	�.�7�$�

�^��u
A�����
* 
<�[S�Z�G�F�yA,�i��O�j�i0�h�[�r�d�e$�h�a�`�i�y�x�`�a�`�e�
��`
�a�`�a�`�c��A$�q��G#

 Q��|��H��=��T��
�C��N���A�W�!A��Q
���,���e��d����A��b��]��\A�*�Z�/�g�`�k��A�6��@�[�gA�/��y������#�j�I�#Q	�P�.�f�P��b��u�X���I�IA4�[�
�!��6�G��n�8�0���#�V�J�9�
�:�[�>�r�g�6�>�r�u��d�u��-��4�i�!	�~��9��%�K�/��y�`�&A�]��d�j
�k�j�o�n	�K�J�M�|�N�M��P�s�r�OA�\��	Q	�ڴ�3�� ��s��A��#���+��J���U�~�I�0�o�-�,�\�T�MA��&A�쁚c�h�i�S�:�7�L�Q�8�7�LQ	��`����.����2�i��k�aAD�&�5�d�m�7�<�h�e�C�D�K�N�'�"�M���F�G�F�w��h��� �G�J�%�&�o�n�I�[�"�5�� �U�X�1�n�.��SA	�7�(G�(+�I�HA:�-���4�m��l�;�J��4�8�o�~�K�J�l�m�H��8�k�O�8
�u���l�9�4�1�G��A�.�o�A�.��H�T��~�A�<Q
���A�E���/�R�V�g�nA	���H�D�%�5��r�
��]��^Q
���u���?�a����
��w�X����I��RA!�Ε*�J�b�kd�
�(�u�t�_��0�CX�.����H�)
��+�>�<
� )�y�R�]��y�Q�.�	4�MA,�ʹ}�:�J�	
�r�qP�b
�c 
A8�ҁ�v�7�Q
��5�4���9�6�
�L�
�
���		��	��'�&��F�G�`�&�'�b
�G��	��E�D�E�D�M	�Q�^�0�=	�<�9�}A�ρ�<

e
	
 !Q
�G� ��D��A��V����YA�_�|��A�U�*
A�T��1�V��?�>��^�a���7�Z
�D�#�"�G�F��]�\�Q	������G�U�V��B�+��E�*A�ʒ�q�|�0�J�!�1�a��4
�P�UA���>�`�a�j�k
A���H�\�i��D�]�\	�C�@��"���G�T�
�f�#�E
�j�O�&�S�|Q�%��!�����(��O���:�H�W�~��uQ	�3�S��J�+�/�X��]�����#Q
�>�X�[��&�v��I����7���A9�I�Z�[�^�A�9�&�$���]�^�M�L�	�c��`��J�w�L�C�*��S�7���j�#�8�}��|�!�"�A�\�1�K���\����G�&A�J��@�N�_�^�QK�R�?
�j�]�^
�u�N� �u�t�u�0�c�$A�O��|7
Q	��d�J�N��V��_�i��AI��f�N�
��� �;	�z�J�O�`��
�
�b�e�u
�Z��@�=�Y�o�V�z�	�T��m�"�S�D�/
�*�[�x�h�-�6�i�`�Y�,K�8�?��E�#�&
�j�1�+0��B�0�M��8��4
��C��<�;�T�2�uAF��v�*�#
�T�G
�Z�r�O�D��e�-�jH%�>�G�>�?
�>�u�I�`
�a
�Z�[�d�aA�,�VC""
)			A�-��~i
'$NA9�/��0�?	�Z�Y�:�=�<�=�<�=�:�=�8�9�8�9�6�5�(�/	�$�%� �!���
����A�?��"!&	"	
Q
�ֶ8��G��.��m�	����c�^�A��|��%��-%�"�KA��:�
�	
���
��zA�쁒}�t��6�7��
��a���,�b
�i�/
�&Q
�B������0���'��_��t��mA:�N�L�}�~���u�{�\�	���	�G��5�f��*�w�B
��w�z
��G	�N��u�z	�+��{�Q�s���=�:�/��_�b�3�e�K�NA�O�zR�O�N$�K�JA2�M��M�������	��������z�w���{����	������)�vQ���o���B��?�r��� ��)�#��n����L�$��G�tA�/�h�x�I�e�9�eA
�.�[
�
��z�A�3��W�4�5�4�_��m��6�'�9�^�9�o�&Q	�i��x�c�6��A����E��$��+��~A�~�I
�e	�.��E�
"��_6�"�n�U��%��:��F��W�hA7�|�e
�*	�H�}	
�h�e�h�e
�r
�_	�V�W�L	�S�j�U��,�-A6�s��%�	�5�e�d�e�E�D
�+�
�$�#�H�I� �Y�A�	� �Y	�d�#�N�O� ��@�G�$�Y�I��L�M
���#
���L�M���L�L�A"�t��z

		
�yQ������}����'�C�x�2���w�xA?����A�D�5�.�I�H6�� �7�B�U�K���)�,���
�<�D�q�4�1�c�L�A�B�u�p�;�h�%�f���>�)��}��R�$�k�TA&����Y���
�~���K�J�����4�`�"�S�R�O�N�)�(�)�V�P�U�T�1�0A����&>
Q
���4���r��s��`�C���z�HA)���E�6�D�s�n�Z�U�+��B��A��o�(�g�|���-�8��O�]�&�I�I�y�r�K�H�K�$�A�|�+�1A���"�|�}�0�S�c�f�i
�t�uA����8#Q	��)�&��Q��P��Q�	��$��iA;�
�R�C�5�v�5�4�!�B�=��"���!���4�5�~�E�D�6�]�$�q�r�!�K�l�`�g�W
��E�3�6��A$���\�B�C�F�G
�}�l�q�o�n�o�,�?�o�k�j�*�E��>�1�p�>�1	�0�r�=
�H�I��	A���?,
"
 Q	�Ƹ������J�N����P�<�
A�ӌ��=�k�d�:��	A�|�`A�ܸA�Ё�R�K�H�I�y�j�T�A�.�9�H
��9�y�D	�M�6�1Q	����L�y�Y�?���e��~A?��B�&�K��k�%� � ��K���$�\�1@�02�C�3�G
�h�5�Q	��6�q�>�1�Pm��Y�/�P�[�T�/�e�V���s)�f�-�^�-�
�.)�M�$�i�8�� �$N���D�C�w�j��pA�ܰ9�?�B
	�(As�!��H�G�J�c
�b�K5"�\�]�$�%�x�y	� �	�JF�<�			�j�e
�L�#�/A�0	�'

�p�q�l
�k�l�mA�9�3�&�7+�
�{�NA��8KZQ8
�"	RA�V��*�p�*�=
+$0A\���u�(�+D�&�%
��������{�x�y�x	�{B�:�;�j�i
�b�e�`
�a�`�a�^�_V	
�F�K�H�I�
���A�G
�F�E	�D
�E�B	AL���YC
?
L

Q	����P�]���H��;��6�O��O�A�ɏ%�<��}��bA����c�\�c
�P
�Q

�H�3		
		A%����2�?�L�M�L�M�:�!��s��/�b�3�x�-�d�e�f	�]�(�~�)���$�A�.�)�8Q
�!�4��^�M�����+�*�;�qA+�-�;�]�i�"�l�
��f	�&�`�Q%�Y'���k� �M�j
�K"�]$�=�b�I�]}�k�d�W)�_�k�2��d�t�-�}�|�8�
�M�IAb�.�0�F�?+,
�z�un			
�R�[�f�Y�T�_	)2
�`�e�2�/V�
��x�d�e�
�A�6�n
�
�i8�h# ��y#�xA�e��A�]7&�A?�1��g� �!� �!
���	�k�x�o
�n�m�h�e�d�a	�L	�M]�H�I�F�E!�@�E�>A&�=��C
{"

p	Q
����\��`�a��u��x��c��<�aA� �O�
���/�`��6
�W�V�]�`��A'��
���
��8�;�8�=�<�D�C
�A�B�<�=�G�n��}�|�q�L���FA���}
C
�F����5��#�Q��h;�>��^)N�"�s=2�ZC�<��}C�<��i4LC�~�E�-C�B��SCB�@��~�%�H�q�A����Q�R�{��[�_�E�:^�C���l���t��q�0?���o�r��%�?��j���W�3��J�OG�b��E�3�� �
G�N���Y��O�D����g�/��K�	���V��_�[
�*%�)�2�).����A�d���	��t�Z�
�>2�8
�t�C&�>�e�h�
$�,�I/�^@�
!�W%X$�
(�}:�C�I�	�S��t+��;;��I�o�oC��@܉��Q�n�,Ew
��P�>�L�x��72~
�B �5I��'$��'�I��!,S
	[�W�X
��(��h�ol�Q�;��z�)��f���V		(R��i�2W�����B��9���E.��gO
�c�(�
_��v�<.��TB�-0�,�|"]	��e~2u9/�5s�R
�D("�Y#�l���$
�Z��f�
�(1�	�%�N��l�B7�=(��l{
�K�{��$�$�64���&�wB(2 ��>
�L�#8-��a_�iJ�v4�q���I�w7�
����6��@���<�m��'\:8w��
H��&��{�{"���?�m@93*G�w��)�<I�i�b�%$�<�.c��{�U�v+� H"��8�JR�g�nz.��2�~�.
��p
�go��y0
�;�s
�p�Y���>�H��k"��K0!
�$
-'3�\-�� `�	#RaF�E
��'��^ �>� (6��u �+"��h093@9
�d&��B
Rv���{�2��*�9�/C,�@���Yb"��X�:�[X��M��iB�(
��*��Y��8�]�m��Yh�����Q�S������,�6�A��I�|��t�g��g�f��8�I�*�,F��	��}�0R>�xA���)(uA�z�W�	�[�7�f�e(��g�N-�w&�dA�(��OQ
��Z�G��c��>��_,Q����\��Y��"��6A�,�~Q+�0�F-8	_N�?�$Cd5�&�oD�8�_",a�w�~��e�?nA5Z9:P�L�}/Q�]�w�I3"P!�l�GjK�[�~�8�A���	5qpq2Fw�S?	B!��*�Mz�	A��L�g�N�h�\A��a ;>%e��!�$�*�;�A�G�X���0na��s���F�#�*�K�P��D�\�a��m�9�=�{�i�g�!�*�U�y�	�*�$+R�J��
U��aN���� �9a_��ha�$!`	��v��K�7�H�M�:���!�0�[�Bq�=�e�4�1���C�8�c�X6�e<�p(�w*�����%_�7J�E�2�v�7�o�*�_�{���'�7.�@�+�[Z�f�C�u^��+�!/�(�|�u=�J���l�,�>�l^�~�I��-�B(�M^�Z�L�0R�BL�Q\�`S�h0�s�=�;��=�>�T�d�f)�og��?�j�e�d�f�
�� �>��\j�{
�u �$�	��.�!�%j�9�<��P�{�]�G�$v�>�A5�KV�\�g�u-�	��
�/��
��[�/Q�>�K�tx���g�-�1��U�~��r�Y�W�K��K�F�IP�Y��y�|�G��k�J�M�r�_#�hF�r�t�d����C�_��pT�~�m�33�=�5�i(�m�@���1��E�J��bP�x"�~�:��+�*�0�7D�;�>>�F�H�`�i	�k�m>�{�~��G��0��
�T�;�so�{A�%��	�V�%�(a!j�!	��jH�v�a�r�n�F��~�0�7�7�E
�&�5L�?���X�^����`�y�/�"	�&�)�_��V��~��\��D�B��Ra t�"	�(�I��a!V�9a
$��c�I	��+	�2� 	�]�h�v�.�-a!��,�*�X�Y�)�p�x�C�B�.�a�L�K�b�q�|����� �#�(�*�3��]a.��	aW��sh�|��k.���v���q�e�O��p�rN�m)�v��a�u�C�X)�\�^�;�q��w������=a�G9�K��U�@�^�l�t��	��#U�(Q�,\�2�t�S�-�j�z�|��
�S�#�>�0�
�:U�?x�HK�O�K�m�:��4�t�"��8�-�@�H�Ky�Z�_S�m�
����
��9��L�(�1�2y�<�^�T��r=�x���E��� �g�0�f�Q�f�o��|��
1�&��(�)�p�O�v�m��}�h�1��<�^�E��Lr�S��Y|�^a G��g�8�}�(	�_�Q�B�iZ�:�6�I�D�O��E��F�u�P	�l�wa�$���]��L!��T��Z4
��_��jB
��l��w����+��k�J��=���x�@��E�R��l���~&��P��_�D��k\��sE��z@��+���n���J��T���9�c��"��M!��R��
,��'���k��-��0b��:���I�(��_�F�����=e���|��R��V�"��p�'��q��H$������K���Y1��*���R	��y��m���
��8�����5B��?���'��T��Zk��H��k��o�>��X���F��'��)��,���.�l��K���<�W��u���D���(�� ��R2���M��M>��V@��^��d:��p�
���$��g=��P�����2,���#��D�`��i�"��@��BX�����$�6��X���G�[��S��V��e|��i�g��t��4�����)��)^��2���?�;��N��Ps��]T��g;��nu��*��L����F��.1��4L��B�3��Fq��L����[��.��*���^��*�!��8��h"��"��p{��yQ��d��	=��P���#���'��b��5�t��@C��k`��H�l�����i��x.��W��3��
$���������"N��'\��L	��N� ��`.��l��?��t��w�(����BW���
��E�%��R���da�>Nt��W�U��hn��x��{p��	��0����
��+��%>��.	��1��3�g��N��Un��j��mZ��uI��}���
���L��!{��)j��3��6���B��E!��J�a��m��qo��|!���
��"�J��8��;��?&��F���Q{��\l��d��h�U��x�Y��	G��:�������+q��;��=�0��O��Q;��W��Y��_���n&��r��u�)��$��(1��/�6��?��A��D���V%��[�x��n�1��j��
������((��+��.��1/��6���D��F��J	��La��R���>���P��3G��8q��CX��H���R���jH��q�H���<��Q����p��"@��)��,�k��@�R��s�	��y��8����>��$���@��D�N��U���g�O��
�
����n��&��(-��1�Z��N��P9��U[��e\��pM��yI����R��
���E��$�A��A���NH��T���hg��q%��wI����W���
��-��0��3�|��V1��Z$��^#��a?��hJ��q��t0��}	���I�����#���9��->��3��6��;��>��A��C��F��I%��O��R3��[6��e�"��~2��
���W��$���,��/��2���DK��M1��V�q��z��}��K���&���� 3��&��)��.�D��L��Q=��W��Z;��`:��g{��l\��vC����L��
c�<�����A��E��ā%��z��P��U��p��a	�`�T�/����{/�%�$p���O�Tq	�5�[�a�]�k�e�c�g�_�_a��"	����0�c>�G@�IPK
!<��Er��7chrome/pdfjs/content/web/cmaps/UniJIS2004-UTF16-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�UniJIS2004-UTF16-HA!��bA	 �U�?�dA��M��6Q#��t&+"'�+�B�9Q%�G(A%%�o/Q	%@�uA &���z�wz!O�0�/
]��A'�A0��KQ3��J�W�V�mA3�


��K?�A3�1$1
)')��q
A3�L


	a%�9�7�=�;�A�?�j�]�`�U�X�w	�~�6�O	�c�]�H^����[a0�k�g�6�E�P�T�\�b�h�t�w�|�~����ma#��^�d�I�X	�M�XPK
!<:�嶞��7chrome/pdfjs/content/web/cmaps/UniJIS2004-UTF32-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�#C\a
!�GS�k]>�d��_?V3�C���M�T�+ 2!+�V�S�L�IS��tA
�~$�c���
S
��n$�c�U�P
�&%$)C$�)
�f�7�![S .��s"'��A��"�}
�";�9�6"��B��;
��|�,"�m����S/P���'
��j�>"�kOL
i��O��fO�>��_c�0
�m��("��[��<��SC�i��P.S��2��j���	�
���C�����k9S
P�X��"��"O(
��zC
^�W-*'0#rSl�6B''�$�c�bCx��1TI�6��7�ZS��94
M�-�|3��)F�Cm�b	>M#=:B�@�;	���J�<O@�0�G�"8��F.��q.�J	�N
M�y�u�>�:�E�<fG>+	'c���h	W~�9�2	
�|C	l�
;ls9'�
��V�h�i� N�.:�K� �e�,�d�[�5�J��|�O�r�]C3 Z�Z�+�.�/�?
�x�vC	F
�Q'�

�j�w�
�*�"e4
�?	�^(��D@�����
�9�zCA��c��,
�`��2�H�j&�_;/1� !�f	�P�c�Z�w
65
�h6

4!�;4/Q�fB��.+�T��*�
U	�9�.��53�6�?C��$<(�#��Ca��J��#�"k
l	�)�@�]VI�b0#
8%#
#'`�p�|�k�`-�k]�FI�[-�&�3�w;<;� y�e�5�@T�
K�9G�$��+��C�U��p�t�m�Z�$�;�M�
�v��3�\�7��V�=�/�j�U�_(�A�K�v�9�y�.�@�7��#�C.���eC.���:C���K
�_�!�W�:�r�S/�0��i�� �w��w�8�5�R�t
�{�v�\S/�S��h�>�
�c�r�/�qS�/#�&�?�
�C�Z��c��c�T�c{�i�7�&&�S�����o���(�_�4�u��w��$��n��B�S�^�n�}�H�a�$�/�7�R�4��	�t�-� .�Y�O�@�4�<�{�0��P��S�_�(��VKp��A��X��]�X�`�-�t�G�W��-�e�"��N�O�x���e?�K�j8�]�/���8�G�U�F��
��a��h�(�.��\�z(����$��X��[�o�p��%�t�:�+�T�}�R�a� �G���6]�C�3�!�Y�"�o5�n S/��N���<�7�J�a��r��C�8��w��z�C
/Ӟ~/�_�H
�+��<��D�w*�3�`C00�:
�a�aC0<�Bc^S23�Op}��$
ly�nC2Q�S2��g�

�D�5�,�=��2
���^C3�pC2��ZC2��{S3�j�r�[�Z�CN�0��+C23�"��
��
��*�?�
�#�F�?�#�,�y�`'�>�M�;�:�J	�=�K��A�&�w�P�y�{C3�x


	�5
&C4�$�&�b9�S�6�m�h�N�	T� ���
�`�(��2�n$�Q�@"�Q�cC4��lR#�.�g
O�8o��x�m�jH�9C�'4��Z%=,'0"�k�z�p�k�H
7(
�p�a�5�5F>5$�LK&&.	?��i�GT4�\�#�]�r>
�K4##P\	8
�9	�P" �Z�A��8>F�2�i5%"�$�09�g��
�B^�g�6"
�\�_�,HBP
&�7J$(�*V
8�C�n
!4
3<	5&Y�1��<&��C$�`�j0�Q_ 
\(�pD�G��$%�	�f@�z�y6�d	$�.~.B�dC�D��&�r�}v%Qo�
�3SN�*�9�!��#����}�D��R��1�U��CVN�L�^�i��O�2�w�l�i�@�m�3�2���M�(�7�X�/�u�ve�	�R�\��g�f��N�� �z�3�
�i�Z�j��9�{�:�	��~�W�d�Q���}�x�X�S�w�h��N�
������I�@�G�F�5�K��r�#�yC
N�Z�
�a��e�d$
"��
CN)��U�x�y	
�t�u�T�"�y�x�w�nS	N݅����g��N��/�F�7CN��9�z�P�A�,CN끆b�F�GCN聤]S	N��Z�4�d�l�m�g���p��SSO��a��	���4�<��k�Y��M�'��\CO�q���L�xCO�kCO��f�x�3SO3��d��)��(��w��z��k��G��.��Q���PCO@��f��u����N�;SOM��\�u�C�R��6��y����a�r�/�����j���b��p�ACOi�)�Q��0��\�i�COz�pCOc��k�	�5�:��>�?S
O��%����k�!��
��W��V���������r��oCO��.����3	�$�[��q���/�5�l�CCO��<C
O��s��M�L��<�L��z�S
Oȁ�~��%���*��������}��|���COז�F���{�X
�g�.��B�0���
CO�l�(CO܁��#	�"�?�$	���(�u�dSP	�T��`����2����x����p��e�a��>��wS
P�*��6�m��O� ��~�z����ySP!�B�z��i�d�v���
�I�T�;��(C+P6�^�N	��y��X��q��A�(�t�'�F���e�O����?�n�R	�E�$�q�
�)��G�>�@�/�.CP@�?
)#0�1�2C2P0���� �t�u�5�{�$�y�x�y�5�{�&�'�&�}	�|�7�6��-�0�5�}�0�1�(�;�:��)��
�o� �1�9�lS	P၇
�4��������L��k���+S
P�_��J�R�T��j���I��CP��b���E�h�	�qt�E�D�9�:CQ2�vCP���+����-�
�8�E�F�)�(�C��(SQA�8����!�1�h�d�;�^�C�:��d�?��P�x��gCQR�r�9��B�?�A��)�h�.�7�Z�m�[�
�M�`��sCQd�JCQS��;�I�F�_��T�_�
��W�>�H�S�SQ��~�#��P��3������6�7�#�"C Q��Q�3�+�D�{�v�s�g�f�o�t�1�j�D���Y�X�/��[�~��+�*��5�R�p�3�2CQ��K���a�`CQ�����	
��1�`��j���.�-�<�=�'�P�gSQ����p�Q���u�s��L��E��zCTQ��5�F�}�
��=�}�A�j���C��:���y�x�Q�P�)�?�I�.��-��B�;�Q�$�`�	�]�`�9��(��y�7�>�a�R�3�8��&�x�}�r�3�U�U�p�{�$�7�jF�P���Y�Z�o�Y�n�O�$�i�h�/�0CR�NG�a>�`	�X�Y�GC1R��o�#���N	�1	�$�g���Z�'�+�P�'�C��*�+�N�M�N�#�I��P	�O�N��$�!�z�g�
�Z�}SR�R����)�h����v��a�`�o��*��=�3��n�A�@CR��|�{�1�t�K�b�
CS�SCR쁥a�
�H�B�g�#�B��,�E�DS
S�M�!�x��V��u�B��0������K��s�SCS#�O�A�@CS$�UCS%��i�K�T�U�)SS8�S�{�m�o���U�Q���x���T��KS	SE�V��m��
����
CSQ�p�9r�]�J�/�h�.�<��� CSa�!CSY��q�#�J�%S
Sl�"�J��W�+�^�X��;�^CSw�`�S�N�I�H��U�
�p��K�J�y�x�A�n�P��]�8���UN{�<CS�m�R(�]CSy��	�'��(�R�I�L�Y�U�*��n�E�U�T�-���~�W�V��(�P��{�7SS�Y��J�A�f��]�Z�Eq�5��
�6��#�#�w�P�H�W��E��#��p�7��1�R�>CS��r����?�ST�y�/�0�i���/�^�E���`�`C&T��l�C�8���C�<��b�*�t	�;�:�W��}�]�V�t�
��/�B�'��� �vC	T��4�O
�0��
CT����"�^�_�#��]�\����G�TST���%�X��m�y�����Y����CT���H�7�%��p��7��CST����t������z����@�
�6��5CT������ST���L��}��B�8����=�8��2���$�0��p��{C$TǢ
�5�@�=�L���m�n����J���o��5�}�:���+�2�7�.�m�=�*
CT��^�C'T��2�D�E�T�n	�o���o�n�o��E�\�a�J�K�^�j��
���O�N�s��p�Q�6�q�S
U{�4��H��S��Z��g��l�n��K�C(U��<��z�o�t�
��O�f	�O�`�M�c~�&�%
�4pi
�M�P�1�W
�~�o�w�h
�YCU��_)�_C,U��:�Z���r�s�r�u�	����s����}�|
������j�k��i�b�m��z�%� �SV/�U��8���P��D��C��X��Y��\��[��TCVN�L
�k�v�S�V	�1�#�<��k�l�Y�\CVB�Z	��bE�kC+V;��*��{�i�h�x��k�v�w�v	��i�f�g�d�

��u��~��g�d�%�*�r�}�%� �a�b�%�*�t�5�6S
VȢg��L�i��H�w���3��8�t��/CVӢjSVעk�_����=���J�e��9����
��CCV�
�R�1�� ��e��V�}�~�O��d�K�J��*�y�r
�`�W��6�.��y��_�6����;�4�c�b�%�1�V�y��v�K��6��_CWY�`FCFV��P�d��u�t�t�k�x�p���m�h��y�{�p�m�h��u�t���t��o�r�I�H�y�	�I�H�{��x��z��y�z���	����W�R�����c�-�~���S
W��e��x���O��l�����v���3C8X�
�y�@�D	�c�b�)�7�O�~�_�}��%�.�c�L�?�B�54�\�I�a�@��.�V	����g�v
�S�s�D��M�L�U�P
�#�� �O��>CX/�2�,+<�>C2X��a�y�x���
���~�	�v�o�~�	��~	�}�f�	��]�p�|�
���|�}�|�a�t��z��e���
�`���z��#SX��h��y������z��_��z����vC7Xܣ1�)�t�|�:�)�_�P�G�|�3�2�k�j�-�1����:�
o�|�q�R�#�P�Y��6�]�\��P����c�^�c�d�x�O�T�[CY�iGCX݁�y����� �|��"�|�/�.�/�w�$�%�$�;�8����=�<��1S	Yg�M�y�z�Y��J��w��B��[��CrYs��&�t�3�2�u�4�T��|��8�i�z�O�a��E�"��3�.��s�A�|�$�u��� �X��]
�o� �B�_�r�/�2�O�J	��t��#�!�D�� �=�<�;�g�*�
 ��K�L��
�=�V	����	�G�J�	�#�$
�A�B���w��a�_�H�9�t��!� �]�\NOCY��nn��,�CY��q
�
��m�l#

	,4�?�>C;Yy��j�?�%�`�?�<�?�'�&�8�9�6�3�7�67�=�:	�;�:�����K�J��������
�aC)Yr��	*

!
-IS[����H�G����H��I��J��[������e�[�h��*��{S[���C�l��5��*���}�p�>��,�bC[��n��D�O�R�z�5��W�����CC[���w
�C[���=
S	[��S�:��"��g��G�t��:C�[ɤ���e��J�O��B�W�M��o�X�+�D�)�(�G�B�/�}�L���/�/�L��}����1�k��'�s��u�t�S�ZI�R�2�e�b�i�/�P�C�B�k�`�o�`��w�E�9�J���	�[�d�
��� �;�2�
��w��G�>�[�_��:��K�F�O�J	�� �_�h
�s�r	�!��w�r	�'�>�e�d�u�H8		�o�n���5�KC[�sE2�	�>�{C�P:1bC\�*�?	�2N�O
�N/G�%�C-[΁�z�{�z5	�
��������5���

�%�&�-�0�5�2�5�6C[ׁ�z�$2�%
	"o#2$CC&[́�D
$ 	

"#


S
]����c��������o�X�Y�e��PC/]�m�	�W�^�y�s�j��x�7�.�T�q�4���m�h�[�=�
���o��O��"�� �q�b�G�B���a�`
�!�>�UC]��C]�� �T�S�|�$�'�"�|�U�M�L�-
�z
�y�)�N�T�M	�v�RS^w����)��$�/�7�C�$����)C$^��?�2�2�31�
T�g��n�g�C�g���`�o�^
�U��&�n�K�L�g�l	C^��pH
C^����N
���}�-�*�z�U�'�&�'�K�B�U�Q�(�zS^������t����0��{��2��W�����W��V��w�S_���-�,��U���M��L��+�`�&C
_�w���F�Y�i����s��t��}S	_!��d�F�^���$��h��%C_-�$
�3�^�5�z��3�B��~�w�z �1��;�X�O�t�d�)�i��iC_4��!!� C_.���-�=�<�B�f�+�*�	��C�@�A�(��!�	�H��S
_|�t��L�b��1�-�`�F�7�6C_��9��-�R�h�e�}��D�GC_��<�"�Y�4�"�!C_���S	_��>���!���f�}��9�e��dC_���-�L�*�
�F�W�P�H�;�	�J�?�6��|c�?�:�m�v

C_���5 �4C_��8����3��� ��5�,��5�4���9
�*��S`�V��������p�*��'��z��s�C�C4` �5�&�#�4���
�+�2�S�J
�7�8�g�0��>!&�#�o�A�>�o�:��(�~	���I�2�k�^�!�pC`]�'C`��&���;�(
��������)�B�	��E�D�-�,�)�(S	`���7��
��8�R��{�f��^��_�S	`��O�"��Y�"Z�����C`��c�&�?�>�	�z�C`��
C`���!��.�1��'�"S`إz��R��/����E��f��5�c�4����GC`�g�K�F�>�S�F�_�^�#�&
�m�]�D�%���"C`���%�$C`偨@�7�)�\�=�<�?�>�;�z�>�;�%�$�8�A��D�C�B�?SaD���j�;��I�����}�/�e�l�C
aS�!�Q�L�i�q�R���Ca^��(�u�tCal��LSan��6����\��q
��v��nC5a~�Ge��S��P	�Q�i�a�
�7�A���z�5�.�y�z�i�n�=�P	���w��Y*��Ca��zC%a{��*�'�&�L�w	�2�B�O�)�v�E�3�v�E
�r���4�p�q�F��~�Y�V�W�V�S�R�a
�/S
b�p�,��8���9�#��Z��9��:�$C?b&��l�o�n�+�m�V��m��G�
�I�A�3�$�S��M��)�
�(�3�<�y����r�=��,�w�n�/�x�T�K�m�~�5�r�Q�{�*�|����s�d	C
b)�^�C�_� .�=>�<Cb'��f�e�`�_�^���a�-�,�n�u�\�i�q�p
�lSb̦i�	��"��)�M�F�-����H�1���5�p��v��9��AC7b��M
<�B�	�=�D�
�k�h�f�3�N���Y�L�U�q<�
����v�1	R�#�~	�
�'
�X�#����?�<L�,�?�w�<�G�8�s�d��yCc=�bG�C7bꁨx�{�/�<�h�)�(�)�<�j��+�c
�
��~�k�h�-�(�c�b�|�}�e�`�}�|�c�b	�/�.�a�`��-�,��7�{�,�/�,�-�J�^�e�dSc��r��]�#�k�h����������D�E�j��A��VC�3c����2�I�L
�5�I�|�q6�{��2�+�W�
�9�>�q�|
=	4��c�X�q��>�8	�}�t���
�E�V	�C�:�q�7�E�l��M���/��c�n	������v�1�:�E�@MPAB��:����s�#�
��� ���?�@�/��V�#�4�(�w��\�|���E�&�m�`E�!���1�0�7�"�H�H�
�h�9�&�2��i� �^�5�.�u�x���O�P�O�8�A�]�H�n�{�e�Z���;�6�����P�e��,Ccþ'1�V�O<,� �g"�f�c�b�#?�"+�~�1Cc��s
�1	�N�`1
N�=�@-< ��C,c���"�_�b+*�q�p		
$�w�x	
��
	��	
$CcꁗJ'
9*1		Cdo��(�70C-c���%!	#
"4
Sf�k��6�o��w�Y��<�+�(��W�}��R��k�e��N��+�S��O�l�!��R��Cf4�j�*�/�>5�G��D�a�Z�_�BCf3��v	
�.�'�W�~�Cf7��L
S
f]�q��6���3�����V�yC-fi�i�R�g�,�C�c�T�#�p�c�Z�� �{�f�q�d�Q�R^�2���!� �%��2��1�@�Cfs�"%C fj��Q�	����S�L�}�5�4����(�T�=�f�7�z��[�Z�E�D�E�D��Sf���'�P��m�	�[�p���NC,g�K��$��=�<�Y�n�h�w�S�K��3�Zm5�/�B�}�N�.�I��6�9�J�1����-�&�#�"���P�$Cg�(D�3�4Cg��
�����i
�7�6�f�a�`�S�O���
�'Sgl��i��;��H��M�,�y�x������`��;C	g|�$�}�b�"�9�$
�gCg{��2�k
�j��YCg���j	S	g���� ��#��L�T��1��Z��o�>Cg��n�t�]��x��!Sg����0�8�9��]����[�:

Cg��(�P�N�K�F�%��m�F�#�H�P�6Cg��+C
g���{�h��~��~��)�&�|S
g�6��h��o����e�%���$�Cg��*�d�}��n�o�T}�1�v�_�g�F�Y�b�U�]�dCg��
�'Cg���J�%�z���~�i�h�~�}�5�0�~�}Sh;��~��-�,��~�x��{�	5�
��� ��C!hH�
�j�{x��X�>�9�j�5��I�^�?�& !�}��u��%�&	�[�,�E�ChR�,;�!ChI���v�w�r�3�@�C�4�5�4�w�A�&��u�V�O�h��9�6S
h��K��v��o��6����Q�����)C	h��Q�[�]�p�=��C�NCh�/Ch���Sh��0�v�6��5��4��O������|��gChߘi�
�I�Z�-�&�e�x�DCh��.Chꁪ��A�@��]�pSi�� ��s���d�]��
��h����2�q�t�d��t�=����C7i�@�c�
� 
�e
���}�S�
��*�'�$�=K�)�/�c�,
� �?!�Y���n��d��g�2	�V�	�+�:�W�N�K�
�UD�\�#�B�
�J���S
�H�e�l�)
�Y�XCqi�p+& 7,

 8=$	


&
18

��		�o�j/0
	
	ad	Cib�X�2!�Y	�z�^.���h(�#$�"�y�z$
\��C"i5�5�I�H�s�v	2�[�Z
�
	�X�U'J
1�{�zC2iB��
�4�;�4
�?
�>	�E�D�I	�L�Q�P�U�Z	�g�h�i�j	

�
�
Ci��"	B5X
	=H�R
�O7Ci��72#

S	k_�b��n��o�m�V���p��Q��nCki�2�n�O��r��t�E�=�!�
�.��C	kl��c������
�Ckk��?S
k���I�[���o��H��G��DC4k��u����u�@�A�r�y�2�o�2�(��4�#����3�~��X��
�!�<��X��E�^�v�m��l�Q�^�'��Ck��=h�GC/k���j�
�1�q� � �#�`
�;�'�p�u�&�L�u�t�O�N�O�+�^�k�p�k�&�>��s�r�A�@�u�y�("��A�3�y�j�u�wS	l��
���^�)�M��4����<Cl��1�2Sl���3���-��u�� �;��m�@��2Cl���c� �j��P� �(��4�X
�'�H�H���&Cl��IC	l���\�w�E�:�y
�E�;�vS	lρ�`�o�n�}����&��7��6��'C6l٪!��%��B�i�l�O�6�Q��h�%��'��(��g�D�(�k�r�E�@�Q�l�5�x�2�e�\�)�j�
�{�p�l��|�`��\�2Cl��B)jC*l܁��-�j�?�+�f��F�#�^�k�h�i�h	�C�B�m�.�O�r�s�L�M�e�d�J�1�h�9�*�]�h�9�*�]��NS	m����.��{��p��g� ��L�1��_Cm��mX�X�g�;��g{�l
�A�J�
�i�^�=�6
��"
��+Cm���)�`��]�H�I�J�K�J�U�w�Q�,�j	�KCm���n"Sm�K��N���K��C������X��	�T�#�%�,Cn�Z�'�r�T��]�U�,*�1�Cn�FCn��uSn�^��
����t��f��U�S�@�rC3n)�9�6��~�w��X�e�h�{�E�>�/�P��*�y�q�t�C�F
�?���f	�=�R�{�t�n�W�G�4�F�M
�a�T�B�A�R�E
�+Cn2��&�)� �Y�h�v	�{�[�^���r	�w�M�B��r�}�Cn;��w
$S	n�����C��8��e�X���	�C#nыH�8	�3%
�p
�)�41��0��E��.�e�8
��e��)���
�

�c�@�CCn�8)�"
�m�l���q
�p�f�[�X�Y�\C	o��	
SoW��A��o��L�U���S��j������!�SC=od�k	�8X�a��|�	�>�.�k�B	�~�7�	>�s�:�W�|��C�A�V��;
�>�g)��	�h�|�%�
��A
��O�h�Q�	�x
�C��u�Z�	�|�K�G�F��}#�m
�_��{��"�	�%Cgof�DM




�A�B+2	
uv.
/		

#

Co��Q,?�_�^�	�
�U5)�0/�#��%�WH�X
 �{�J�9
�h �o�p'�<M�C'o}�

�%�$#�?�! * �n�m)
Co~��3

%]&.?9"2	PC.oh��C'	*	" 
!6*,Co���3U(�0-I�i�lA"��C)ol��	">E
!
	1

	
S
rW����+�d��P����d��Q��Z��}����'�J��JCErg��
�?�~�	�X�Y�~���[
�(�I�@�g
���_
�p�`���V
�1�\����n�N�8�E��~�Y�=
�v��$�?�j�*�	�
��Y�e�N �E����!#�E�_��Z�q�v,�Q� �i���H!�	�.�8CWrt�k	

	
	7	4+	
)("
Cr��aeRE5�w$�xaC9r��;�%�$3,��(�`�a	�O�N	
�H�I
�@�=
		�WCr���I�(;�
85C6rx��
&
	,	#	Crh��HD�T�U
*
�
</�>#�C/rk��A	/ 
	@ 	Su�J�]��V�*���-����]��>������k��/�0C
u0�>�<��Z�
�~��E�HCu/�rCu6���E�!�dS
uF�S��^�g�������X��=��d��'��&CuT�^��JS
uY�y�4�q�!��*�H��
����OC	ud�V�E�#�f�W�Z��D��ESuo�u�-��h��k�~��d��m�-�(��D�.CFu�@�@��'�U��[�j�e�V�Q�L��t�k��|�?�g�"��������O�N	
�w�z�[�V�a�d
�+�s��c�zCu��~#/�c�b	��

�w�vC8u���	�c�b�S�R�c��
�\�{�Y�	�����	���������!���@�=��B�7�4�E�o�n�s�8�3�2�I�i�h
�i�.�G�+	�BS	v������O�7�o��`��!� �
CGv��w�
����;�`��^��P	�x��]��
�<��2�N�p�g�Y� �m�\�HE�C�Z�'�fp�a�&�{�*����'�T���=
�R�#�a�G�
��G�y�6�1�d�{�P�Y��6�;�=�#�L�0�D�A�� CVv��

�%�&	
	


$.	
		=:C
v��y�=��>�oB�/{�,�5C v��7%
��
��;	
��:�!5)
5Cv���^
`Kb*>OC'v���

."#?	"	
	C)v���r
	
E$!	8C4v���!		=
" +


S	y:�T��H������y��@��C�j��VC1yG�k�U�^�X�J�{�r�[�`�t�?�W�p�O	�T�$*��$�Y��k��:�(��i�h��	�y�}��W��:�;�S�T�6�%CyE�;�v�	�N�O�J�K��T�K	�~��|�F�G��~�D�E��m	�n��CyD��Y
Sy݁�g�W��k��@��)��(��)�����G�Cy��f�;
�-�D�$���Y��X�'�v�
���SCyꁙ&�S�P�E�%�$��8�C���>Cy�k
S
z7� ��\��>�����t�VCzB�6�T�?�@�]�	���b���s�t�Q��P�yCzE�K
C
zD��+�7�H�K��@�E
S
z}�*����'������d��)�?��2�ZCz��+��Z�>S	z���Z�>��5�g����C	z��x	�n�i�w�t�j�G�JCz��QCz���{
�A�&�#�"�#S
zËT�@��B��=���|�����^��O����
C
z��	�	�n�������#�-S	z��d���.�6��1�J��d��wCz�z��Q�0�7�y�7��	�X�N�#C{�WC	z��;�:��\	�j	�I�|S	{%� �j������h�9���U��vC{3�J�K�V�o�M�C{1�Z	�T�B�'C{0��S{K�R�����'��k�0��N��S��v��C{���C{]�i!&C{`�]�(�+�T�U�L�M�&�j�o�	�8�+�-S{��)���2��	�.��$��)	�X�W��"��_C{��{�T�a�p�^�5�LC{��bC	{����3�0�+�*�+�.S	{Ěc��0���t��R��y��F��%CN{Ϯn
���Z�.�-�<	��
�z�q�{�r�u�v���#�&/.�y�x���!�-��0�5�#�E�B�'�'�*�s�6�B���;�fC{��c1


�C@�BC*{ԁ�
�h�i�f�i�z�l�m�l�m
�@�U�q���n�u�t�|+�S�w	�t�u��n	�g�f�eC{ہ�&		)
S	|ү&��H�H�C��3��z�����,C|ܯ'�E�/�+��1�2�u�t��C��V�c�K�(��C|��v�H�I	�J
C|遭1S}��2�����'�K�������k�I�0�c��T���HC} �I�L���*�nS
}*��6�����z����=����3�_C}5�9��5��XS
}?�8��Z�Q����.�R
��X��1C }K�5��,��t�S�t�/��{�r���k��Y�T�9�V�h�1�H�x�
C}M��N���
�T�5�!�T	�W�X�>�{�z��Z�>�A�<��BC}Q��8
S}���6��O�S��P�s���b��E�2��T��M�{�����C!}��N��(�l�u�X�#�P�?��*�y�k�`��	�k�;��k�r�/�*��d�2
	C}���8�I	���;	�a��9�i�h�i�fC}G	S	~�1��2����Z��P��Y
Ci~&�N��{�	
5�3�N���w��<�S��t�8�Q�|��O�j�m�b�T�I
�)����
����Q�T�d���z�4�M�L�!��G�H�!� �b�_�?�6���?�6��K�-�x
���d�c�[�C~G��O�N�+
/-
C#~'��B�[�Z�,
�1)�0�1�0�1�0�+� �&�%�"�����
�
���C~-��L/�)	&	S	��>�<��j��i�6�w�r��b�S�
��c��O�O��.����g��d��O��N�������C��@
�Y�D���
�Q�P���3�2C
���n��|��*��|�}�|C���f	S	�o�F���6��;���>��9�IC+�y�Q�i�h�9�Q�$�O�4�;�@�x��z�Z��I�$�T�u�i�T�l�Y�z�G��'�N	����8C�{��j�

�
��~��~��~�/�.�
�/�.C����p7S�큙t���!�������5����F�����������}��C$��7��n�~�e���l��O�e�v�k�,�;�%��F��
�h��
�.
�s�v���q�A�j��>��8�C+�	�d 
�r�m		
C
��2�5�4��4/)	C=���y�~�	�
�~����z���{�z
��?�D��C�8�j�k�
�{�z�����}�t���M�L�z�K�@��	�u�O�N�v�u�v�u�p��	��}�p�oS
���M�r�X��\���&�M��0��eC��	S
���c�-��x�p��Q�	��\��]�WC�!�=�0�v��e�y�x��S	�2�>��7��6��-��q�\�w��NC
�G���*�^�I�7�9
�D�M�YC
�@�$�BC%�<���t��m�5�0�1�$�%�$�q�U�V�w�v�j�
�W�c�D�i�V���Y�I� �^�9�f�sS����+�R��?�k��~��u������(��-��,S	���E��%�?��0��k��j��	��6�JC	�ŋ_��R��k�p�$�e�^C��G�JC�Ё�	�S�ޱE��L��S��h��i��P��+�W� ��tC��)�m�
	�(�y�J�U��T�=�B�;�Z�xC�ꁚ�S��M�L��C�큮%3S�+�T��,�u�t��3��H��U��>��E�S����'�h��BC�@�O�J�
�C�<��9�a�`�C�=��/S
�O�c�Z��p��K��,��U��A��C�w�YWC�b��m�p�
;4C�Z�z�h��/�Q�~�US	���O�N��?��P�l�m��m�C7���[�O�@�*�_�2�w�h�*�!�@�b�I��N�k�V�!�^� �0�f�W�7�>��!�3�<�#��+�6�Y�\%�M�\�*�=d�y�ZC����5�K�d�������i
��+�*�K�M�f!�m�l�"�C����9	)0S	�_��;���E�P�)���g��?��$S	�i���������d��5C�u�n�$�A�4��	�C��W�,-�]�C
�s��=�M�z�3
�B��5�4��C�t��JS���`�Z��b��[�=��X�������	��C7�ė�r�/��m�v�;�>�,�s�W�(�Y���f��
�~�S�Z�;�~�T��#�w�r�S��h�i�P�i��t�r�CJ�a�w�T�v�_	
C�Ӂ�_���z�
�C	�P�G�J�K�G��u�n�
�H
�J
�K�TC�́�R%	S	���,��t�e����r��q��p�EC����PC���:C���a�(�b�c�b�ES
���-��z������w�,�{��r��F��?��CN���>���9�:�
��C�&�@�%�'�"?@��r�m�
�W� �6
�	�;�J��d�/�L�O�N��)�H�b�
��=�R
	
�5�6�C�'���
�	�3�,�Y��t��	�	�C���b%�G�H(��
�K�JaC.����|
�k�n�s�r�s�r�o�n�m�l�s�t�/�`�5�B�C�
�x'��(�%
�C"����m	
"
#	S
��X��^�"�s��m��l��m��|C���>,�Y�(��d�
�,T>��&C-�
�r
 


�,�7			
 
���CQ���#�n�o�a�n�o�\��
�a�l�Z�i�h�]�
�c�b�h�k��b
�U�l��
�3�=�t�v�h�c�b�]�t�}�|���d�[�x�z�y�\�e�d�_�^�_�^�i�h�c�b�a�`�]�	�b�o�n�k�*#��f�=�+S
�
���|��q��,�k���CC���=��	�U�V�g�)�z��)C��C���<�x�}�|	�m�n�o�j�k�j��m�j��k�j�S�U��C�w��c��h��9��&��!�� ��
�b��.�x��?����HC-�h�/��x�/j�b
S�Z�y�	�
�d�	�d��W�	�t�W� �a;�\�S�L��F�L�<��q�;�3�d�z��d�+��={�v�u�D�UCJ�k�3

				
	�.�7�$�

�^��u
C�����
* 
<�[S�Z�G�F�yC,�i��O�j�i0�h�[�r�d�e$�h�a�`�i�y�x�`�a�`�e�
��`
�a�`�a�`�c��C$�q��G#

 S��|��H��=��T��
�C��N���A�W�!C��S
���,���e��d����A��b��]��\C�*�Z�/�g�`�k��C�6��@�[�gC�/��y������#�j�I�#S	�P�.�f�P��b��u�X���I�IC4�[�
�!��6�G��n�8�0���#�V�J�9�
�:�[�>�r�g�6�>�r�u��d�u��-��4�i�!	�~��9��%�K�/��y�`�&C�]��d�j
�k�j�o�n	�K�J�M�|�N�M��P�s�r�OC�\��	S	�ڴ�3�� ��s��C��#���+��J���U�~�I�0�o�-�,�\�T�MC��&C�쁚c�h�i�S�:�7�L�Q�8�7�LS	��`����.����2�i��k�aCD�&�5�d�m�7�<�h�e�C�D�K�N�'�"�M���F�G�F�w��h��� �G�J�%�&�o�n�I�[�"�5�� �U�X�1�n�.��SC	�7�(G�(+�I�HC:�-���4�m��l�;�J��4�8�o�~�K�J�l�m�H��8�k�O�8
�u���l�9�4�1�G��A�.�o�A�.��H�T��~�A�<S
���A�E���/�R�V�g�nC	���H�D�%�5��r�
��]��^S
���u���?�a����
��w�X����I��RC!�Ε*�J�b�kd�
�(�u�t�_��0�CX�.����H�)
��+�>�<
� )�y�R�]��y�Q�.�	4�MC,�ʹ}�:�J�	
�r�qP�b
�c 
C8�ҁ�v�7�Q
��5�4���9�6�
�L�
�
���		��	��'�&��F�G�`�&�'�b
�G��	��E�D�E�D�M	�Q�^�0�=	�<�9�}C�ρ�<

e
	
 !S
�G� ��D��A��V����YC�_�|��C�U�*
C�T��1�V��?�>��^�a���7�Z
�D�#�"�G�F��]�\�S	������G�U�V��B�+��E�*C�ʒ�q�|�0�J�!�1�a��4
�P�UC���>�`�a�j�k
C���H�\�i��D�]�\	�C�@��"���G�T�
�f�#�E
�j�O�&�S�|S�%��!�����(��O���:�H�W�~��uS	�3�S��J�+�/�X��]�����#S
�>�X�[��&�v��I����7���C9�I�Z�[�^�A�9�&�$���]�^�M�L�	�c��`��J�w�L�C�*��S�7���j�#�8�}��|�!�"�A�\�1�K���\����G�&C�J��@�N�_�^�QK�R�?
�j�]�^
�u�N� �u�t�u�0�c�$C�O��|7
S	��d�J�N��V��_�i��CI��f�N�
��� �;	�z�J�O�`��
�
�b�e�u
�Z��@�=�Y�o�V�z�	�T��m�"�S�D�/
�*�[�x�h�-�6�i�`�Y�,K�8�?��E�#�&
�j�1�+0��B�0�M��8��4
��C��<�;�T�2�uCF��v�*�#
�T�G
�Z�r�O�D��e�-�jH%�>�G�>�?
�>�u�I�`
�a
�Z�[�d�aC�,�VC""
)			C�-��~i
'$NC9�/��0�?	�Z�Y�:�=�<�=�<�=�:�=�8�9�8�9�6�5�(�/	�$�%� �!���
����C�?��"!&	"	
S
�ֶ8��G��.��m�	����c�^�C��|��%��-%�"�KC��:�
�	
���
��zC�쁒}�t��6�7��
��a���,�b
�i�/
�&S
�B������0���'��_��t��mC:�N�L�}�~���u�{�\�	���	�G��5�f��*�w�B
��w�z
��G	�N��u�z	�+��{�Q�s���=�:�/��_�b�3�e�K�NC�O�zR�O�N$�K�JC2�M��M�������	��������z�w���{����	������)�vS���o���B��?�r��� ��)�#��n����L�$��G�tC�/�h�x�I�e�9�eC
�.�[
�
��z�C�3��W�4�5�4�_��m��6�'�9�^�9�o�&S	�i��x�c�6��A����E��$��+��~C�~�I
�e	�.��E�
"��_6�"�n�U��%��:��F��W�hC7�|�e
�*	�H�}	
�h�e�h�e
�r
�_	�V�W�L	�S�j�U��,�-C6�s��%�	�5�e�d�e�E�D
�+�
�$�#�H�I� �Y�A�	� �Y	�d�#�N�O� ��@�G�$�Y�I��L�M
���#
���L�M���L�L�C"�t��z

		
�yS������}����'�C�x�2���w�xC?����A�D�5�.�I�H6�� �7�B�U�K���)�,���
�<�D�q�4�1�c�L�A�B�u�p�;�h�%�f���>�)��}��R�$�k�TC&����Y���
�~���K�J�����4�`�"�S�R�O�N�)�(�)�V�P�U�T�1�0C����&>
S
���4���r��s��`�C���z�HC)���E�6�D�s�n�Z�U�+��B��A��o�(�g�|���-�8��O�]�&�I�I�y�r�K�H�K�$�A�|�+�1C���"�|�}�0�S�c�f�i
�t�uC����8#S	��)�&��Q��P��Q�	��$��iC;�
�R�C�5�v�5�4�!�B�=��"���!���4�5�~�E�D�6�]�$�q�r�!�K�l�`�g�W
��E�3�6��C$���\�B�C�F�G
�}�l�q�o�n�o�,�?�o�k�j�*�E��>�1�p�>�1	�0�r�=
�H�I��	C���?,
"
 S	�Ƹ������J�N����P�<�
C�ӌ��=�k�d�:��	A�|�`C�ܸC�Ё�R�K�H�I�y�j�T�A�.�9�H
��9�y�D	�M�6�1S	����L�y�Y�?���e��~C?��B�&�K��k�%� � ��K���$�\�1@�02�C�3�G
�h�5�Q	��6�q�>�1�Pm��Y�/�P�[�T�/�e�V���s)�f�-�^�-�
�.)�M�$�i�8�� �$N���D�C�w�j��pC�ܰ9�?�B
	�(Cs�!��H�G�J�c
�b�K5"�\�]�$�%�x�y	� �	�JF�<�			�j�e
�L�#�/A�0	�'

�p�q�l
�k�l�mC�9�3�&�7+�
�{�NC��8KZQ8
�"	RC�V��*�p�*�=
+$0C\���u�(�+D�&�%
��������{�x�y�x	�{B�:�;�j�i
�b�e�`
�a�`�a�^�_V	
�F�K�H�I�
���A�G
�F�E	�D
�E�B	CL���YC
?
L

S	����P�]���H��;��6�O��O�C�ɏ%�<��}��bC����c�\�c
�P
�Q

�H�3		
		C%����2�?�L�M�L�M�:�!��s��/�b�3�x�-�d�e�f	�]�(�~�)���$�A�.�)�8S
�!�4��^�M�����+�*�;�qC+�-�;�]�i�"�l�
��f	�&�`�Q%�Y'���k� �M�j
�K"�]$�=�b�I�]}�k�d�W)�_�k�2��d�t�-�}�|�8�
�M�ICb�.�0�F�?+,
�z�un			
�R�[�f�Y�T�_	)2
�`�e�2�/V�
��x�d�e�
�C�6�n
�
�i8�h# ��y#�xC�e��A�]7&�C?�1��g� �!� �!
���	�k�x�o
�n�m�h�e�d�a	�L	�M]�H�I�F�E!�@�E�>C&�=��C
{"

p	S
����\��`�a��u��x��c��<�aC� �O�
���/�`��6
�W�V�]�`����x�")(uC7��
���
��8�;�8�=�<�D�C
�A�B�<�=�G�n��}�|�q�L���F��-�K�	�[�7�f��(��g�N-�w&�dC���}
S
��Z�G��c��>��_,S����\��Y��"��6C�,�~S+�0�F-8	_N�?�$Cd5�&�oD�8�_",a�w�~��e�?nA5Z9:P�L�}/S�]�w�I3"P!�l�GjK�[�~�8�C��CC���	5qpq2Fw�S?	B!��*�MzC��Y�N�h�\��n��Y�5�#�Q�h;�>���^)N�"�s=2�ZC��LC��C��i4LCD�~�%�H�q�A���Q�R�{�e�
u��_�E�:^�C��l��t�q�0?��o�r�%�?�j��W�3�J�OG�b�E�3� �
G�N��Y�O�D��g�/�K�	��V�_�[
�*%�)�2�).��A�d��	��t�Z�
�>2�8
�t�C&�>�e�h�
$�,�I/�^@�
!�W#�	�`$�
(�}:�C&	�S�t+�;;��I�o�oC����Q�n�,Ew
P�>�L�x�72~
�B �5I�'$��'�I�!,S
	[�W�X
�(��h�ol�Q�;�z�)��f��V		(R��i�2W����B��9��E.�gO
�c�(�
_v�<.�TB�-0�,�|"]	e~2u9/�5s�R
�D("�Y#�l�$
�Z�f�
�(1�	�%�N�l�B7�=(�l{
�K�{�$�$�64��&�wB(2 �>
�L�#8-�a_�iJ�v4�q���I�w7�
���6��@��<�m��'\:8w
H��&�{�{"��?�m@93*G�w�)�<I�i�b�%$�<�.c�{�U�v+� H"8�JR�g�nz.�2�~�.
�p
�go�y0
�;�s
�p�Y�>�H��k"�K0!
�$
-'3�\-� `�	#RaF�E
�'�^ �>� (6�u �+"�h093@9
�d&B
Rv���{�2�*�9�/C,���Yb"�X�:�[X�M�iB�(
�*�Y�8�]�m�Yh���Q�S����,�6�A�I�|�t�g�g�f�8�I�*�,F	�}�0R>�xc ;>%e��!�$�*�;�A�G�X���0n��Fpc��s���F�#�*�K�P��D�\�a��m�9�=�{�i�g�!�*�U�y�	�*�$+R�J��
U��cN���� �9c_��hc�G!`	��!	��jH�v�a�r�n�2���~�0JK�7!�7�E
�&�5L�?���X�^����M�:���!�0�>�`�y�/��B	�&H�=�\�_��4�1�V�x��C�8�c�X6�e<�p(�w*�����%_�7J�E�2�v�7�o�*�_�{���'�7.�@�+�[Z�f�C�u^��+�!/�(�~�~|�u=�J���l�,�>�l^�~�I��-�B(�M^�Z�L�0R�BL�Q\�`S�h0�s�=�;��=�>�T�d�f)�og��?�j�p�\�s�d�f�
�� �>��\j�{
�u �$�	��.�!�%j�9�<��P�{�]�G�$v�>�A5�KV�\�g�u-�	��
�/��
��[�/Q�>�K�tx���g�-�1��U�~��r�Y�W�K��K�F�IP�Y��y�|�G��k�J�M�r�_#�hF�r�t�d����C�_��pT�~�m�3"�D�B�=��R�3�i(�m�@���1��E�J��bP�x"�~�:��+�*�0�7D�;�>>�F�H�`�i	�k�m>�{�~��G��0��
�T�;�so�{A�%��	�V�%�(��*����{�e�O�Tc t�"	�(�I����$�c!V�9c
$��c�I	��+	�2� 	�]�h�v�.�-c!��,�*�X�Y�)�p�x�C�B�.�a�L�K�b�q�|����� �#�(�*�3��]c�[�%c.��	cX��sh�|��k.���v���q�e�O��p�rN�m)�v��a�u�C�X)�\�^�;�q��w������=a�G9�K��U�@�^�l�t��	��#U�(Q�,\�2�t�S�-�j�z�|��
�S�#�>�0�
�:U�?x�HK�O�K�m�:��4�t�"��8�-�@�H�Ky�Z�_S�m�
����
��9��L�(�1�2y�<�^�T��r=�x���E��� �g�0�f�Q�f�o��|��
1�&��(�)�p�O�v�m��}�h�1��<�^�E��Lr�S��Y|�^��+�Tc G��g�8�}�(	�_�Q�B�iZ�:�6�I�D�O��E��F�u�P	�l�wc�$���]��L!��T��Z4
��_��jB
��l��w����+��k�J��=���x�@��E�R��l���~&��P��_�D��k\��sE��z@��+���n���J��T���9�c��"��M!��R��
,��'���k��-��0b��:���I�(��_�F�����=e���|��R��V�"��p�'��q��H$������K���Y1��*���R	��y��m���
��8�����5B��?���'��T��Zk��H��k��o�>��X���F��'��)��,���.�l��K���<�W��u���D���(�� ��R2���M��M>��V@��^��d:��p�
���$��g=��P�����2,���#��D�`��i�"��@��BX�����$�6��X���G�[��S��V��e|��i�g��t��4�����)��)^��2���?�;��N��Ps��]T��g;��nu��*��L����F��.1��4L��B�3��Fq��L����[��.��*���^��*�!��8��h"��"��p{��yQ��d��	=��P���#���'��b��5�t��@C��k`��H�l�����i��x.��W��3��
$���������"N��'\��L	��N� ��`.��l��?��t��w�(����BW���
��E�%��R���dc�>Nt��W�U��hn��x��{p��	��0����
��+��%>��.	��1��3�g��N��Un��j��mZ��uI��}���
���L��!{��)j��3��6���B��E!��J�a��m��qo��|!���
��"�J��8��;��?&��F���Q{��\l��d��h�U��x�Y��	G��:�������+q��;��=�0��O��Q;��W��Y��_���n&��r��u�)��$��(1��/�6��?��A��D���V%��[�x��n�1��j��
������((��+��.��1/��6���D��F��J	��La��R���>���P��3G��8q��CX��H���R���jH��q�H���<��Q����p��"@��)��,�k��@�R��s�	��y��8����>��$���@��D�N��U���g�O��
�
����n��&��(-��1�Z��N��P9��U[��e\��pM��yI����R��
���E��$�A��A���NH��T���hg��q%��wI����W���
��-��0��3�|��V1��Z$��^#��a?��hJ��q��t0��}	���I�����#���9��->��3��6��;��>��A��C��F��I%��O��R3��[6��e�"��~2��
���W��$���,��/��2���DK��M1��V�q��z��}��K���&���� 3��&��)��.�D��L��Q=��W��Z;��`:��g{��l\��vC����L��
s	�5�[�a�]�k�e�c�g�_�_c��"	����0>�G@�Ic�_�c��/����Ac+O����%��z�P��U�p��PK
!<>z���7chrome/pdfjs/content/web/cmaps/UniJIS2004-UTF32-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�UniJIS2004-UTF32-HC!��bC	 �U�?�dC��M��6S#��t&+"'�+�B�9S%�G(C%%�o/S	%@�uC &���z�wz!O�0�/
]��C'�C0��KS3��J�W�V�mC3�


��K?�C3�1$1
)')��q
C3�L


	c%�9�7�=�;�A�?�j�]�`�U�X�w	�~�6�O	�c�]�H^����[c0�k�g�6�E�P�T�\�b�h�t�w�|�~����mc#��^�d�I�X	�M�XPK
!<���3�3�6chrome/pdfjs/content/web/cmaps/UniJIS2004-UTF8-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE���?���?����? @\a
A Q¤k]>�d��_?V3�A´��M�T�+ 2!+�F�V�S�L�IQĀ��tA
�~$�c���
Q
��n$�c�U�P
�&%$)AĤ�)
�f�7�![QĮ��s"'��A��"�}
�";�9�6"��BQŀ��j��;
��|�,"�m����Q/���'
��j�>"�kOL
i��O��fO�>��_c�0
�m��("��[��<��SAƒi��P�nQǍ�2��j���	�
���Aǵ����k�yQ
ɐ�X��"��"O(
��zA
ɞ�W-*'0#rQɬ�6B''�$�c�bAɸ��1TI�A�6��7�ZQʈ�94
M�-�|3��)F�Aʻb�I>M�c=:B�@�;	���J�<�@A+ʔ�J��#�"k
l�I�)�@�	��]VI�b�p#
8%#
�c'�`�p
�I
AΆ��K
�YB[ �g�"8��F.��q.�J	�N
M�y�E�u�~�:�E�<�fG�~�k	'c���h	W~�9�2�T	
�|�	�l�
;�Hl�Us�y'�
�^�V�h�i� N�.:�K��`�e�,�d�[�5�J��|�O�r�]BoẼ�5�r���j�L�m��{/�q� �a�f�E��G�?
�x�&�Z�
�T�
,�?�I�
�h�q�i�r
�7�[��;�e��
�j�w�T�����G�X��.+��O�L�P�
�X�?�~
U�3�H�.�3��f�	�8�g�M�D������
�f�s�6�?�C��$�@<(�#�a�	�x�B<‿�i�B�`-�k�]�F�	�[�m�&�s�w�{<�{� �y�e�u�@��
K�yG�d��+��C�U��p�t�m�Z�$�;�M�
�v��3�\�7��V�=�/�j�U�_(�A�K�B�P�$�9�y�.�@�7��#�B⺞��eB⺬��:B℮���W�z�2�R⼀�0��i�� �w��w�8�5�R�t
�{�v�\R⼖�S��h�>�
�c�r�/�qR⼣�&�?�
�C�Z��c��c�T�c{�i�7�&&�S�����o���(R@⽀�'�4�u��w��$��n��B�S�^�n�}�H�a�$�/�7�R�4��	�t�-� .�Y�O�@�4�<�{�0��P��S�_�(��VKp��A��X��]�X�`�-�t�G�W��-�e�"��N�OR@⾀�S���e?�K�j8�]�/���8�G�U�F��
��a��h�(�.��\�z(����$��X��[�o�p��%�t�:�+�T�}�R�a� �G���6]�C�3�!�Y�"�o5�n 
R⿀��m���<�7�J�a��r��C�8��w��z�B
⿓�~��o�_�H
�+��<���w*�s�`B〰�:
�a��aB〼�B�c�R
㈳�Op}��$
B㉀�Vly�n�OR㊐�g�

�D�5�,�=��2
���^B㌀�pB㊩�ZB㊢�{R㌃�j�r�[�Z�B一�0��+B2㌔�"��
��
��*�?�
�F�#�F�?�#�,�y�`�P'�>�M�;�:�J	�=�P�K��A�&�w�P�y�{B㌗�x


�B	�5�[
�fB㐂�$�&�b�y�S�v�m�h�N�	�� ���M�`�(���r�n$��"��cB㐅��l�#��n�g
���x�o��x�-�j��yB�'㐆��Z%�},�g0"�k�z�D�p�k�H
�w(�\
�p�a�P��u�5�>�u$�L�K&&�B.�I����i�G��t�\�c�]�Q�r�~
�K4#�c�\	�O8
�9�L	�P" �Z�[�A��\8�~F��r�i�u�e"�$��p9�g�@���Z
�^�g��v"�Z
����_�^�lH��
�f�S�wJ�d(�W�*V�W
�x�C�n�V
!4
�U�s<	�u&��1��<�f��C�d�`�j�p�Q� 
�\(�p��G��X�$�e�	��&@�z�y�v�d�I$�.�~.��d�C����&�r�K}v�eQ�/�J�3R万�*�9�!��#����}�D��R��1�U��BV且�L�^�i��O�2�w�l�i�@�m�3�2���M�(�7�X�B�/�u�ve�	�R�\��g�f��N�� �z�3�
�i�A�Z�j��9�{�:�	��~�W�d�Q���}�x�X�S�w�h��N�
���E����I�@�G�F�5�K��r�#�yB
丟�Z�
�a�L��e�d$�M"��T�
B丩��U�x�Q�y	
�t�u�T�]�"�y�x�w�F�nR	仝�����g��N��/�F�7B仭��9�z�P�A�,��E�gB仼�5�CB
仨��]�w�v�u�F�G�{�l�m�B�lR伈��a��	���4�<��k�Y��M�'��\B会�q���L�xB伕�kB伖��f�x�3R伳��d��)��(��w��z��k��G��.��Q���PB佀��f��u����N�;R位��\�u�C�R��6��y����a�r�/�����j���b��p�AB佩�)�Q��0��\�C�i�B佺�pB佣��k�	�5�:�G��>�?R
侈�%����k�!��
��W��V���������r��oB侖�.����3	�$�[��q���/�B�5�l�CB侚�<B
侗�s��M�L��<�L��F�z�R
俈��~��%���*��������}��|���B俗��F���{�X
�g�.��B�0�����F
B俠�l�(B俜���#	�"�?�$	����D�(�u�dR倉�T��`����2����x����p��e�a��>��wR
倖�*��6�m��O� ��~�z����yR倡�B�z��i�d�v���
�I�T�;��(B+倶�^�N�I��y��X��q��A�(�t�'�F��B��e�O����?�n�R	�E�$�q�C�
�)��G�>�@�/�.B偀�?
)�c�p�1�2B2倰����E� �t�u�5�{�$�y�x�y�5�{�&�'�&�}�I�|�7�6��-�0�5�}�0�1�(�;�:�B��)��
�o� �1�9�lR	僡��
�4��������L��k���+R
僭�_��J�R�T��j���I��B價�b��D��E�h�	�qt�E�D�9�:B儲�vB僺��+��D���-�
�8�E�F�)�(�C��(R兀�q�s����!�1�h�d�;�^�C�:��d�?��P�x��gB兒�r�9��B�?�A��)�h�.�7�Z�m�[�
�M�C�`��sB兤�JB兓��;�I�F�_��T�_�
��W�>�H�D�S�R冉�~�#��P��3������6�7�#�"B 冗�Q�3�+�D�{�v�s�g�f�o�t�1�j�D�F���Y�X�/��[�~��+�*��5�R�p�3�2B冝�K���]�a�`B冘����	
��1�`�B��j���.�-�<�=�'�P�gR凰����p�Q���u�s��L��E��zBT函�5�F�A�}�
��=�}�A�j���C��:���y�x�Q�P�)�?�I�.��-�G��B�;�Q�$�`�	�]�`�9��(��y�7�>�a�R�3�8�C��&�x�}�r�3�U�U�p�{�$�7�jF�P���Y�B�Z�o�Y�n�O�$�i�h�/�0B刕�N��a�~�`	�X�P�Y�GB1刅��o�#���N	�1	�H�$�g���Z�'�+�P�'�C��*�+�H�N�M�N�#�I��P	�O�N�F��$�!�z�g�
�Z�}R勛�R����)�h����v��a�`�o��*��=�3��n�A�@B勲��|�{�1�t�A�K�b�
B匀�SB勬��a�
�H�B�g�#�G�B��,�E�DR
匕�M�!�x��V��u�B��0������K��s�SB
匣�O�A�@�{�m�o�Z�@�x��
B匤�UB	匥��i�K�T�U�)�|�U�Q�C�&R	卅�V��m��
����
B卑�p�9r�]�J�/�h�.�<��� B卡�!B卙��q�#�J�%R
卬�"�J��W�+�^�X��;�^B卷�`�S�N�F�I�H��U�
�p��K�J�y�x�A�F�n�P��]�8���UN{�<B卿�m�S�R�h�]B卹��	�'��(�D�R�I�L�Y�U�*��n�E�U�T�-���~�W�V�B��(�P��{�7R叝�Y��J�A�f��]�Z�Eq�5��
�6��#�#�w�P�H�W��E��#��p�7��1�R�>B叺�r�F����?�R合�y�/�0�i���/�^�E���`�`B&君��l�C�8���C�<��b�*�t	�;�A�:�W��}�]�V�t�
��/�B�'��� �B�vB	吞��4�O�M�0��
B吚����"�^�_�Q�#��]�\����G�A�TR咈��%�X��m�y�����Y����B咕��H�7�%��p��7��CR咤���t������z����@�
�6��5B咱�����R	咷��L��}��B�8����=�8��2BV哀�i�$�0	
�5�@�=�L���m�n�F����J���o��5�}�:��A��+�2�7�.�m�=�*
	�A����z�o�t�
��O�f	�O�`�M�c~�&�%�M�4pi
�M�P�G�1�W
�~�o�w�h
�YB哨���^��g�)�_B'哃��N�9�8�U�E	�@�D�G�\�]�F�C�o�n�K�H�q�x�I�}�|
�]�i�h�)� B1哊���o�n	�o�n�o�n�F�o�j�k�j�K�q�p��Y�t�O�u�r�s�r�u
�N�r�s�p�q�r
�s�j�E�k�l�m�l�iR嘯�U��8���P��D��C��X��Y��\��[��TB噎�L
�k�v�S�V	�1�#�<�E��k�l�Y�\�DB噂�Z	��b��kB+嘻��*��{�i�@�h�x��k�v�w�v	��i�f�g�d�
�M��u��~��g�d�%�*�r�}�%� �a�b�%�*�t�A�5�6R
囈�g��L�i��H�w���3��8�t��/B囓�jR囗�k�_����=���J�e��9����
��BC団�
�R�1�� ��e��V�@�}�~�O��d�K�J��*�y�r
�`�D�W��6�.��y��_�6����B�;�4�c�b�%�1�V�L�y��v�K��6��_B坙�`��ZBF囤�P�d��U�u�t�t�k�x�p���m�h��y�{�p�L�m�h��u�t���t��o�r�I�H�y�	�I�H�B�{��x��z��y�z���	���N��W�R�����c�-�~���R	執�e��x���O��l�����v�B9堀��{�y�@�D	�c�b�)�7�O�~�_�}��B�%�.�c�L�?�B�54�\�I�a�@��.�V	����D�g�v
�S�s�D��M�L�U�P
�#�B�� �O��>B堯�2�,�k�|�>B2堃��a�y�x���
���~�	�v�o�~�	��~�I�}�f�	��]�p�|�
���|�@�}�|�a�t��z��e���
�`�E���z��#R壐�h��y������z��_��z����vB7壜�1�)�t�|�:�)�_�P�G�|�G�3�2�k�j�-�1����:�
o�|�q�R�#�P�Y��6�]�\�E��P����c�^�c�d�x�O�T�[B夋�i�B壝��y�����[� �|��"�|�/�.�/�w�$�%�$�;�8��C���=�<��1R	奧�M�y�z�Y��J��w��B��[��Br女��&�t�3�C�2�u�4�T��|��8�i�z�O�a��E�"��3�.�G��s�A�|�$�u��� �X��]
�o� �B�A�_�r�/�2�O�J	��t�C��#�!�D�� �=�<�;�R�g�*�
 ��B�K�L��
�=�V	���_��	�G�J�	�#�$�A
�A�B���w��a�_�H�9�t��!� �]�\NOB妤�n�n��l�B姊�q
�
�W��m�l�c

	�_�l�_�S4�?�>B;她��j�?�N�%�`�?�<�?�F�'�&�8�9�6�T�3�7�6�w�=�:�I�;�:�����D�K�J����Q�����S�
�aB)奲���P�I*�H
�W
�T!�J-�C�	R宀���H�G����H��I��J��[������e�[�h��*��{R宓��C�l��5��*���}�p�>��,�bB�4実�n��D�O�R�z�5��W�����C�;�B���G�t��:���e��J�O��B�W�M��o�X�+�D�)�(�G�B�/�}�L��A��/�/�L��}����1�k��'�s��u�t�S�ZI�R�2�e�b�i�@�/�P�C�B�k�`�o�`��w�E�9�J���	�[�d�R�
��� �;�2�
��w��G�>�F�[�_��:��K�F�O�J�I�� �_�h
�s�r	��a��w�r	�'�>�e�d�K�u�H8		�o�n�K���5�KB寀�q��r�	�>�{��P�z�q�Z�bB尔�*�?	��r�N�O
�N�o�\���e�B.宬��w�a�E�{�z�u	��M��������S��u���
�M�%�&�P�-�0�5�2�V�5�6B宷��y�_�~
	�b�o�c2�[$�D�B寬��NB+宭��=
�D�W
�d �I

�b�c�H�J�W

R
巠���c��������o�X�Y�e��PB/巫�m�	�W�^�y�s�j��x�7�C�.�T�q�4���m�h�[�=�
���o��O��B�"�� �q�b�G�B���a�`
�!�>�UB巷�B巸� �T�S�@�|�$�'�"�|�U�M�L�-�J�z
�y�)�N�T�M	�v�RR	幷����)��$�/�7�C�$B%庁�?��2�2�31�
T�g��n�g�C�g���`�o�^�J�U��&�n�K�L�g�l	B庖�p�
B庀��
�N
���}�-�*�z�U�'�&�H�'�K�B�U�Q�(�zR
延�����t����0��{��2��W�B开��V��W��V��w�R弆���-�,��U���M��L��+�`�&B
弓�w���F�Y�i����s��t��}R	弡��d�F�^���$��h��%B5弭�$
�3�^�5�z��3�B�B��~�w�z �1��;�X�O�t�d�)�i��i���@�-�`�F�7�6���-�R�h�e�}��D�GB弴��P�!!� B"弮���-�=�<�B�f�@�+�*�	��C�@�A�(��!�	�H���u�b�J�#�2���k�4�4��!R	徧�>���!���f�}��9�e��dB徳��-�L�*�F�
�F�W�P�H�;�	�J�?�6��|c�?�:�m�v��N

B德��5�`�4B徵�8��K���3��� ��5�,��5�4���9��J�*��R怒�V��������p�*��'��z��s�C�B4怠�5�&�#�4���
�+�2�S�J
�F�7�8�g�0��>!&�#�o�A�>�o�:��(�~�I���I�2�k�^�!�pB恝��gB怟��&���;�(�J��������)�B�	��E�B�D�-�,�)�(R	悢��7��
��8�R��{�f��^��_�R	悰�O�"��Y�"Z�����B悼�c�&�G�?�>�	�z�B惕�
B悻��!��C�.�1��'�"R惘�z��R��/����E��f��5�c�4����GB惧�g�K�F�>�S�F�_�^�D�#�&
�m�]�D�%���"�BB惲��U�%�$B惥��@�7�)�\�=�D�<�?�>�;�z�>�;�%�$�8�A��D�C�B�?�FR慄���j�;��I�����}�/�e�l�B
慓�!�Q�L�i�q�R���B慞��(�u�tB慬��LR慮��6����\��q
��v��nB5慾�G�Ce��S��P	�Q�i�a�
�7�A���D�z�5�.�y�z�i�n�=�P�@	���w��Y*��B憘��zB%慻��*�'�&�K�L�w	�2�B�O�)�v�E�3�v�E
�r��E��4�p�q�F��~�Y�V�W�V�S�K�R�a
�/R
戚�p�,��8���9�#��Z��9��:�$B?戦��l�o�n�+�m�V��@�m��G�
�I�A�3�$�S��M��)�
�(�3�<�y�@����r�=��,�w�n�/�x�T�K�m�~�5�r�Q�{�*�|���D��s�d	B
戩�^�C�_�D� �n�=�~�<B戧��f�e�D�`�_�^���a�-�,�n�u�\�R�i�q�p
�lR拌�i�	��"��)�M�F�-����H�1���5�p��v��9��AB7拠��M
<�B�	�=�D�
�k�h�f�A�3�N���Y�L�U�q<�
����v�1�IR�#�~	�
�'
�X�#����?�<L�D�,�?�w�<�G�8�s�d��yB挽�b�YG�B7拪��x�{�/�<�E�h�)�(�)�<�j��+�c
�
��~�k�h�-�(�F�c�b�|�}�e�`�}�|�c�b	�/�.�a�`��-�,��7�{�,�B�/�,�-�J�^�e�dR掞�r��]�#�k�h����������D�E�j��A��VB�3掲���2�I�L�A
�5�I�|�q6�{��2�+�W�
�9�K�>�q�|
=	4��c�X�q��C�>�8	�}�t����J�E�V	�C�:�q�7�E�l��M���/��D�c�n	������v�1�:�E�A�@MPAB��:����s�E�#�
��� ���?�@�/��V�#�4�(�w��\�|�N���E�&�m�`E�!���1�0�7�"�H�H�
�h�9�&�2��i� �C�^�5�.�u�x���O�P�O�8�A�]�H�n�{�e�Z���F�;�6�����P�e��,B揃�'1�V�^�O�|,� �g�b�f�c�]�b�#��"�k�~�qB揑�s
�1	�N�Q�`�q
�N�=�@�H�m�Y�| ��B,掽���b�_�b�k�j�q�p�I	
�d�w�x�O	
��
�Q�I���I
$�GB揪��J�g
�R�[�y�j�Z�q	�IB摯��(�w0B-揁���V%�Z�a	�K#�K
�H�Z�b�t
R昜�k��6�o��w�Y��<�+�(��W�}��R��k�e��N��+�S��O�l�!��R��B昴�j�*�/�>�A5�G��D�a�Z�_�BB昳��v	�J�.�'�W�~�B昷��L�F
R
晝�q��6���3�����V�yB-晩�i�R�g�,�C�c�T�F�#�p�c�Z�� �{�f�q�d�Q�R^�2�B���!� �%��2��1�@�B晳�"�eB 晪��Q�	����S�L�@�}�5�4����(�T�=�D�f�7�z��[�Z�E�D�E�D��R	曷��'�P��m�	�[�pB-最�7�&��$��=�<�Y�n�h�w�S�K��3�Zm5�/�B�}�N�.�I��6�A�9�J�1����-�&�#�"���P�$B朎�(��3�4B朁��_��
�����i
�7�6�f�D�a�`�S�O���
�'R杬��i��;��H��M�,�y�x������`��;B	杼�$�}�b�E�"�9�$
�gB杻��D�2�k
�j��YB构��j	R	枕��� ��#��L�T��1��Z��o�>B枠�n�t�]��x��!R枯���0�8�9��]����[�:

B柁�(�P�N�K�F�%��m�F�#�H�P�6B枻�+�DB
枾��{�C�h��~��~��)�&�|R
柮�6��h��o����e�%���$�B;査�*�d�}�B��n�o�T}�1�v�_�g�F�Y�b�U�]�d�i�,�B�~�	5�
�*�g�j�{x��X�>�9�j�5��I�^�?�& !�A�}��u��%�&	�[�,�E�B柺��J�'�~�N
�{�!B.柹��J�D�%�z���~�i�h�~�}�5�0�~�}�x�E�y�v�w�r�3�@�C�4�5�4�w�E�A�&��u�V�O�h��9�6R
梭�K��v��o��6����Q�����)B	梹�Q�[�G�]�p�=��C�NB棈�/B梻���GR棏�0�v�6��5��4��O������|��gB棟�i�
�I�Z�-�&�e�E�x�DB棨�.B棪����A�@��]�pR椃�� ��s���d�]��
��h����2�q�t�d��t�=����B#椙�@�^�C�
�8+& �5�|,��J��� ��(�}�h�w�r�q�t�K�L�uB楢�X�2B椖���%�$�%����F�&��_�;��=���$�p�"�a�'�HR	楷�u��f�`���=���~B榁��c�)�*�1�,8=$�A��,�L�)�?�n�i��l�B


�k�n�d�{�d�:�-�g�2�,�W��'�+�I�:����u�N� 
�i�@��j�=�8
��		�o�j�E/0
�{�~	��"�H�c�bad�y�z�1���S
�H���l�)�D�B���G�HB榊�	�z�^�n���h�h�#$�"�y�H�z$
�\��B榀�;�w�v	�r�[�Z�J�
	�F�X�U�g�

�q�P�{�zB+榒��)�?
�>	�C�E�D�I	�L�Q�P�R�U�Z�I�P�g�h�i�j�H	
�M�
��Z
B榨���u�X
	�}��R�J�O7B榍���L�w�r�V�W#�E
�V
R	歟�b��n��o�m�V���p��Q��nB歩�2�n�O��r��n�@�E�=�!�
�.��B	歬��c�����F��
�B歫��?�TR
殬��I�[���o��H��G��DB4殷�u����u�@�@�A�r�y�2�o�2�(��4�T�#����3�~��X�A��
�!�<��X��E�^�v�m��l�Q�^�'��B毖�=�(�\�GB/殽��j��M�1�q� � �#�`
�;�'�p�u�B�&�L�u�t�O�N�O�+�^�k�p�k�&�>��N�s�r�A�@�u�y�("��A�3�y�j�u�wR	沁�
���^�)�M��4����<B沌�1�2R沒��3���-��u�� �;��m�@��2B没��c� �j��P� �(��4�X
�'�A�H�H���&B沪�IB	沟��\�w�E�:�y�M�E�;�vR	泏��`�o�n�}����&��7��6��'B6泙�!��%��B�i�l�O�6�Q�W��h�%��'��(��g�D�(�k�r�E�@�Q�B�l�5�x�2�e�\�)�j�
�{�p�l��|�`�K��\�2B泚�B�i�*�WB*泜���-�j�?�+�f��D�F�#�^�k�h�i�h	�C�B�m�.�O�r�s�L�M�W�e�d�J�1�h�9�*�]�h�9�*�E�]��NR	涑���.��{��p��g� ��L�1��_B涛�mX�X�g�;��g�C{�l
�A�J�
�i�^�=�6
��"
��+B涪��)�`��]�B�H�I�J�K�J�U�w�Q�,�j	�KB涷��n�bR淮�K��N���K��C������X��	�T�#�%�,B清�Z�'�r�T��]�U�,*�1�B渗�FB渀��uR渝�^��
����t��f��U�S�@�rB3温�9�6��~�w��X�e�h�{�D�E�>�/�P��*�y�q�t�C�F
�?��@��f	�=�R�{�t�n�W�G�4�F�M
�a�T�B�A�R�B�E
�+B渲��&�)� �K�Y�h�v	�{�[�^���r�K	�w�M�B��r�D�}�B渻��w�V
�d�DR	滇����C��8��e�X���	�B#滑�H�8	�3%
�p
�)�41�A��0��E��.�e�8
��e��)���
�
�A
�c�@�CB滙�8)�"
�m�L�l���q
�p�f�[�X�Y�\�UB	漄��	
�SR潗��A��o��L�U���S��j������!�SB=潤�k	�8X�S�a��|�N�	�>�.�k�B	�~���w�	�~�s�:�W�|�K��C�A�V��;�M�>�g)��Z�	�h�|�%�
��\�A
��O�h�Q�[�	�x
�C��Q�u�Z�	�|�K�G�F��W�}#�m
�_��{��H�"�	�%Bi潦�DM�C


�H

�A�B+2��B	�Muv�n�R
�K�o		�W
�J#�M
�@B澈�Q,��_��N�^�	�
�U5)�0�o�#��H�%�W�H�X
 �{�X�J�9
�h �o�[�p�g�<�M�B'潽�
�M�%�$�c�����W�B!�`�j�X�X �n�W�m�i
�HB潾��3�M�J%��]&�n��y�b�\2�I�B.潨��C�g	�j��C	�H�O�X�V"�`
�a�v�j,�HB澇��3��U�h�0-I�_�i�T�,��b��H�B+潬���V	�T"��Y�L�~��J!
�H	�q
�@
�Z	
�UR
牗����+�d��P����d��Q��Z��}����'�J��JBE牧��
�?�~�	�B�X�Y�~���K�[
�(�I�@�g
���_
�p�`��]��V
�1�\����D�n�N�8�E�Q��~�Y�=
�v��$�O�?�j�*�	�
��Y�D�e�N �E����!�c�E�_��Z�q�O�v,�Q� �^�i���H�a�	�.�8BW牴�k	�B

�I
�P	�X�w�D	4+�L�A	
�L)(�b�JB犱�a�e���K�Q�u�w�d�x�aB9犍�;�K�%�$3�l��K�(�F�`�a	�O�N�R	
�H�I
�F�@�=�J�N		�H�WB犛��I��h�{��Z
�x�_5�WB6牸���N�P
&�L
�T�O	�E�F�l	�c	�ZB牨��H�L��T�U�M�j
�
�|�o�U�>#�B/牫��A�W	�o�N�`�F
�X	�@��`�]�IR甞�J�]��V�*���-����]��>������k��/�0B
田�>�<��Z�
�~��E�H�GB甯�rB甶���E�@�!�dR
畆�S��^�g�������X��=��d��'��&B畔�^��JR
留�y�4�q�!��*�H��
����OB	畤�V�E�#�f�W�Z��D��ER畯�u�-��h��k�~��d��m�-�(��D�.BF畿�@�B�@��'�U��[�j�e�V�Q�L��C�t�k��|�?�g�"��������O�N�D	
�F�w�z�[�V�a�d
�+�s��c�zB疁�~#�o�c�b�F	��
�M�w�vB:疅��	�c�b�S�R�c��
�\�{�c�@��	�����	�����P�����!���@�=��B�7�4�E�o�n�s�C�8�3�2�I�i�h
�i�.�G�+	�BR	皀�����O�7�o��`��!� �
Be皐�w�
��C���;�`��^��P	�x��]��B�
�<��2�N�p�g�Y�C� �m�\�H��C�Z��g�fp�a�&�{�*���N��'�T���=
�R�#�a�G�]�
��G�y�6�N�1�d�{�P�Y��6�;�=�#�F�L�0�D�A�Q�� +�c�8�g�I�{�^�X�1�c�`�2�W�p�O	�T�I�E�$�Y�K��:�;x�}��@�W�	��S�TmBh皋��M
�%�&�H�]	
�K�G	

�O
�d.�I�F
�I	=:�S

�@*	+	�UB皛�y�=�N�>�o��/�{��l�u�P���4�_B硏�B睪�U�~B'盅��w�e
�W�{	�Q
�[� �!�u�i
�u	�G�
�NB皝��^�W
�X� �E��b*�~�O�Q2�NB2皕��

�Y�n�b#�]��I�Z"�_	�Y�Q
	�d

�b
�MB0皭��r�[�F
	�F�W�J��d!�Y�U	�x�B�n�XBB皟���a	�O	�D�}�M"�U�`�k
�G

�F�N�M�_R秝��g�W��k��@��)��(��)�����G�B秩��f�;
�-�D�D�$���Y��X�'�v�
���SB秪��&�S�T�P�E�%�$��8�C���>B秱��k�JR	稷� ��\��>�����t�B穀��b���	���\�e�$��X�F�n�C�?B	穃�!
�t�H�uB穄��+�A��H�S�R�K�
���@�E����N�A��)�3�Z�)�>R	窐��Z�>��5�g����B	窟�x	�n�i�w�t�j�G�JB窠�QB窞��{
�A�&�#�"�#R
竃�T�@��B��=���|�����^��O����
B
竑�	�	�n�������#�-R	童��d���.�6��1�J��d��wB端�z��Q�0�7�y�7�B��	�X�N�#B笇�WB	竱���;�:��\�I�j	�I�|R	笥� �j������h�9���U��vB笳�J�K�K�V�o�M�B笱�Z	�T�B�C�'B笰��
�@R筋�R�����'��k�0��N��S��v��B箆��B筝�i!�P&B筠�]�(�+�T�U�L�M�&�j�o�	�D�8�+�-R箔�)���2��	�.��$��)	�X�W��"��_B箪�{�T�a�p�^�G�5�LB箯�bB	箥���3�0�+�*�+�.�DR	範�c��0���t��R��y��F��%BN篏�n
���Z�.�-�<�H	��
�z�q�{�r�u�v��@��#�&/.�y�x�K���!�-��0�5�#�E�B�'�'�*�s�6�B��A��;�fB篗�c�q
�Z

�C��BB*篔��
�h�i�f�B�i�z�l�m�l�m
�@�C�U�q���n�u�t�|�C+�S�w	�t�u��n�I�g�f�eB篛���f	�Q�I)
�SR	糒�&��H�H�C��3��z�����,B糜�'�E�/�+��1�2�u�t��C�A��V�c�K�(��B糝�v�H�I	�J�MB糩��1R紏��2�����'�K�������k�I�0�c��T���HB素�I�L���*�nR
紪��6�����z����=����3�_B紵�9��5��X��+R	絀��&�Q����.�R
��X��1B 絋�5��,��t�S�t�/��{�r���k��Y�T�K�9�V�h�1�H�x�
B絍��N���
�T�5�!�T	�W�X�B�>�{�z��Z�>�A�<��BB絑��8
�ER綪��6��O�S��P�s���b��E�2��T��M�{�����B!綽�N��(�G�l�u�X�#�P�?��*�y�k�`��	�k�;��k�r�/�*�E��d�2
	B緀��8�I	���;	�a��9�i�N�h�i�fB緢��G�E	R	縛�1��2����Z��P��Y
Bi縦�N��{�	
5�3�B�N���w��<�S��t�8�Q�|��O�j�B�m�b�T�I
�)�����J����Q�T�d���z�4�M�H�L�!��G�H�!� �b�_�?�6���?�C�6��K�-�x
���d�c�[�B繇��O�N�Y�k
�o-�N
B%縧��B�[�Z�,�M�1)�0�1�0�1�B�0�+� �&�%�D�"��
�@
���
�
��U��B縭��L�N�o�)�\	�f	�CR	耀�>�<��j��i�6�w�r��b�R耊��c��O�O��.����g��d��O��N�������B耜�@
�Y�D���
�F�Q�P���3�2B
耞��n��|��*��F�|�}�|B耝��f	�AR	聯�F���6��;���>��9�IB+聹�Q�i�h�D�9�Q�$�O�4�;�@�x��z�Z��I�$�T�u�i�T�l�H�Y�z�G��'�N	����8B聻��j�L�

�
��~��~��~�L�/�.�
�/�.B肁��p7�NR胭��t���!�������5����F�����������}��B$脂�7��n�~�e���l��O�T�e�v�k�,�;�%��F��T�
�h��
�.
�s�v�F���q�A�j��>��8�B+脉�d�G 
�r�@�m		
�A�FB
脖�2�5�4�F���t/�Z)�IB=脃��y�~�	�
�~����z���{�G�z
��?�D��C�8�j�k��M�{�z�����}�t���M�E�L�z�K�@��	�u�O�N�v�u�v�u�p��	��}�@�p�oR
與��M�r�X��\���&�M��0��eB舒�	R
舖��c�-��x�p��Q�	��\��]�WB舡�=�0�v��e�y�x��R	舲�>��7��6��-��q�\�w��NB
艇���*�^�I�7�T�9
�D�M�YB
艀�$�N�BB%舼���F�t��m�5�0�1�$�%�$�q�U�V�w�v�j�
�W�c�D�i�C�V���Y�I� �^�9�f�sR芩��+�R��?�k��~��u������(��-��,R	芷�E��%�?��0��k��j��	��6�JB	苅�_��R��k�p�$�e�^B苆�G�JB苐��	�R苞�E��L��S��h��i��P��+�W� ��tB苫�)�m�
	�E�(�y�J�U��T�=�B�;�Z�xB苪���S��M�A�L��B苭��%�sR茫�T��,�u�t��3��H��U��>��E�S����'�h��BB荀�O�J�
�B茼��9�F�a�`�B茽��/�DR
荏�c�Z��p��K��,��U��A��B荷�YWB荢��m�p�E�
;4B荚�z�h��/�Q�B�~�UR	莘�O�N��?��P�l�m��m�B7莢�[�O�@�*�C�_�2�w�h�*�!�@�b�I��N�k�V�!�^� �0�f�W�E�7�>��!�3�<�#��+�6�Y�\%�M�H�\�*�=d�y�ZB莧��5�K�@�`�������i
��+�*�D�K�M�f!�P�m�l�"�B莦��9	�i�p�UR	葟��;���E�P�)���g��?��$R	葩���������d��5BM葵�n�$�A�G�4��	�C��W�,-�]��}�Z�=�2�A�s�r�/��m�v�;�>�,�s�W�(�@�Y���f��
�~�S�Z�;�~�T��A�#�w�r�S��h�i�P�i��t�r�CJ�a�w�T�v�_	
�AB0葳��=�M�z�D�3
�B��5�4���C�B��A�����z�
�N�C	�P�G�J�K�G�Q��u�n�
�H
�J
�K�T�AB葴��J�T�v%�O�P	R	薄�,��t�e����r��q��p�EB薗��PB薐�:B薏�a�(�b�c�b�ER
薤�-��z������w�,�{��r��F��?��BN薹�>�F���9�:�
��C�&�@�%�'�"�C?@��r�m�
�W� �6�M�	�;�J��d�/�L�O�N��)�N�H�b�
��=�R
	�M�5�6�C�'���
�	�3�,�Y��t��	�	�A�B薷�b�V%�G�]�H�h��
�K�O�J�!B.薼��|�J�k�n�s�r�s�@�r�o�n�m�l�s�t�C�/�`�5�B�C�
�x�O'��G�(�%
�B"薴��m�I
�\�_�Q"
#	�ER
蜂�X��^�"�s��m��l��m��|B蜘��>�l�Y�(��d��M�,T�~��&B-蜍�r
 

�L
�,�7		�I
�B 
���H�BQ蜎��#�n�o�a�n�o�\��
�a�l�Z�i�h�]�
�c�A�b�h�k��b
�U�l���J�3�=�t�v�h�c�b�]�t�}�|���d�[�x�z�A�y�\�e�d�_�^�_�^�i�h�c�b�a�`�]�	�b�o�n�A�k�*#��f�=�+R
蠍���|��q��,�k���CB蠟��=��	�D�U�V�g�)�z��)B蠜�B蠘��<�x�}�|	�m�n�o�j�k�j��m�D�j��k�j�R衕��C�w��c��h��9��&��!�� ��
�b��.�x��?����HB-表�/��x�/�Hj�b
S�Z�y��I�
�d�	�d��W�	�t�W� �a;�H�\�S����F�L�A�<��q�;�3�d�z��d�+��^�={�v�Y�u�D�UBL衫�3�B

				�T
	�.�7�E�$�
�E
�^��u�D�@�JB袘��[��
�j 
�|�D�[��Z�O�G�F�yB,衩��O�j�i�p�C�h�[�r�d�Q�e$�h�a�`�i�y�F�x�`�a�`�L�e�
��`
�a�`�a�`�X�c��B$衱��G�D�c�U�K
�O
�`R討�|��H��=��T��
�C��N���A�W�!B訛�R
訝��,���e��d����A��b��]��\B訪�Z�/�g�`�k��B訶��@�[�E�gB訯��y����A���#�j�I�#R	詐�.�f�P��b��u�X���I�IB4詛�
�!��6�G��n�8�0���#�V�J�9�E�
�:�[�>�r�g�6�>�r�u��d�u��-��4�i�!	�~��9�B��%�K�/��y�`�&B詝��d�j
�k�j�o�F�n	�K�J�M�|�N�M��O�P�s�r�OB詜���Z	�GR	諚��3�� ��s��B諤�#���+��J���U�~�I�A�0�o�-�,�\�T�MB諴�&B諬��c�h�i�S�:�E�7�L�Q�8�7�LR	謙�`����.����2�i��k�aBD謦�5�d�m�7�<�B�h�e�C�D�K�N�'�"�M���F�G�F�B�w��h��� �G�J�A�%�&�o�n�I�[�"�5�� �E�U�X�1�n�.��SB	謷�(�\��h�k�I�HB:謭���4�E�m��l�;�J��4�8�o�~�K�J�l�m�H�A��8�k�O�8
�u���l�9�4�F�1�G��A�.�o�A�.��H�T��~�L�A�<R
貧�A�E���/�R�V�g�nB
貴�H�D�%�5��r�
��]��^�eR賀�l��?�a����
��w�X����I��RB!賎�*�J�b�kd�
�(�u�t�_��K�0�C��.����H�)�M��+�>�<
� �i�y�R�]��y�Q�T�.�	�t�MB,賍�}�:�J��I
�r�q��b�J�c �C�P
�LB8賒��v�7�Q
��5�4��B��9�6�
���
�
�U���		��	��'�&�S��F�G�`�&�'�b
�G��	��E�C�D�E�D�M	�Q�^�0�=	�<�9�}B賏��<

�e�B
�C	
�`!R
蹇� ��D��A��V����YB蹟�|��X�B蹕�*
�DB蹔��1�V��?�>��^�a���7�Z�M�D�#�"�G�F��]�\�R	躩����G�U�V��B�+��E�*B車��q�|�0�J�!�1�a�E��4
�P�UB躾�>�A�`�a�j�k�F
B躳�H�\�i��D�]�E�\	�C�@��"���G�T�E�
�f�#�E
�j�O�&�S�|R輥��!�����(��O���:�H�W�~��uR	輳�S��J�+�/�X��]�����#B?輾�X�[�B�V���[�^�A��y�&�$���]�^�M�L�	�c��`��J�w�L�B�C�*��S�7���j�#�8�}��|�!�"�A�\�1��B�K���\����G�&B轀��?�L�O�N�_�^�Q��R�?
�j�L�]�^
�u�N� �u�t��F�u�0�c�$B轁��{
�w�A
R	逓�d�J�N��V��_�i��BI逝�f�N�
��� �;�I�z�J�O�`��
�
�b�e�u
�Z��@�=�Y�o�V�z�	�T��D�m�"�S�D�/
�*�H�[�x�h�-�6�i�`�Y�,�K�8�?��E�#�&
�j�1�+0��N�B�0�M��8��4�M��C��<�;�T�2�uBF逞�v�*�#
�T�G�J�Z�r�O�D��e�B�-�jH�e�>�G�]�>�?
�>�u�I�W�`
�a
�N�Z�[�d�a�NB逬�V�W��b�b�J�i			B逭��~�i
�H'�d�NB9逯��0�?�I�Z�Y�:�=�<�K�=�<�=�:�=�8�9�8�E�9�6�5�(�/�I�$�%� �!�]����J�����DB逿���b�a�f	�b	�F
�ER
釖�8��G��.��m�	����c�^�B釣�|���e��-%�"�KB釡�:�F�
�	
���
��@�zB釬��}�t��6�7��
��B�a���,�b
�i�/
�&R
鉂������0���'��_��t��mB:鉎�L�}�~���u�{�\�	���	�G��A�5�f��*�w�B
��w�z
��G	�N�U��u�z	�+��{�Q�s���=�:�/��_�b�B�3�e�K�NB鉏�z��O�N�N�d�K�JB4鉍��M��������I��������z�w���@�v�{����	�����H��)�vR錜��o���B��?�r��� ��)�#��n����L�$��G�tB錯�h�x�X�I�e�9�eB
錮�[
�H�
��z�B錳��W�4�5�4�O�_��m��6�'�9�^�9�o�&R	鍩��x�c�6��A����E��$��+��~B鍾�I�M�e	�.��E�
�b��_�v�"�n�U�X��%��H�:��F��W�hB7鍼�e�W
�*	�H�}�I
�h�e�h�e
�r�J�_	�V�W�F�L	�S�j�U�A��,�-B6鍳��%�	�5�B�e�d�e�E�D
�+�
�$�E�#�H�I� �Y�A�	� �Y	�d�#�N�O�D� ��@�G�$�Y�I��L�M�J���#
���L�M���L�L�L�B"鍴��z�O
�]
�I	
�E�T�9�ER閟����}����'�C�x�2���w�xB?閭��A�D�D�5�.�I�H�v�� �7�B�U�K�@���)�,���
�<�D�q�4�1�c�L�A�B�u�p�;�h�%�f���>�G�)��}��R�$�k�TB&閫��Y���
�~���F�K�J������t�`�"�S�R�O�N�C�)�(�)�V�P�A�U�T�1�0B閺��&�^�~�Z
�RR
隔�4���r��s��`�C���z�HB)隠�E�6�D�s�n�Z�U�+��B�C��A��o�(�g�|���-�8��O�]�&�I�I�y�r�K�H�K�$�A�|�+�D�1B隤�"�|�}�0�S�T�c�f�i
�t�uB隟��8�cR	霂�)�&��Q��P��Q�	��$��iB;霍�R�C�5�v�5�4�!�B�=��"�F���!���4�5�~�E�D�6�]�$�q�r�!�K�l�D�`�g�W
��E�3�6��W�B$霔��\�B�C�F�G�J�}�l�q�o�n�o�,�?�o�k�j�*�U�E��>�1�p�>�1	�0�r�=
�H�I��B�	B霚��?�l
"�D
 R	韆�������J�N����P�<�
B韓���=�k�d�A�:��	A�|�`B韜�B韐��R�K�H�I�y�j�T�A�.�9�H
��9�y�D�I�M�6�1R	頌���L�y�Y�?���e��~B?頗�B�&�K��k�%� � �O��K���$�\�1��0�r�C�3�G
�h�5�Q	��6�q�K�>�1�P�m��Y�/�P�[�T�O�/�e�V���s)�f�N�-�^�-�
�.�i��
�$�i�K�8�� �$���D��D�C�w�j�^��pB飜�9�?�
	�(Bs頡��H�G�H�J�c
�b�K�u�L"�\�N�]�$�%�x�y	� ��S	�J��<��T	�E		�j�e�A
�L�#�/��C�0	�'�J�P
�p�q�l�M�k�l�mB頹�3�f�7�k�
�{�NB頞�8�Z��H�Z��W�x
�]�"�I�B顖��*��p�j�}
�k$�p�]B\頖��u�(�X�+��&�%�J���N������K�{�x�y�x	�{��A�:�;�j�i�M�b�e�`
�a�O�`�a�^�_��L	
�W�F�K�H�I�
���A�G�J�F�E	�D
�E�B�W	BL頙��Y�N��]�P�M��]
�\�Q��H�@
�T
�ER	鮦��P�]���H��;��6�O��O�B鯉�%�<��}�W��bB鮱��c�\�D�c
�P
�Q
�M�H�3		
		B%鮰��2�?�L�M�L�M�:�A�!��s��/�b�3�x�-�d�e�f	�]�(�~�)��B��$�A�.�)�8R
鰡�4��^�M�����+�*�;�qB+鰭�;�]�i�L�"�l�
��f	�&�`�Q�P%�Y'���k��`�M�j
�K�b�]$��}�b�I�]�}�k�d�S�W)�_�k�2��d�t�-�D�}�|�8�
�M�IBb鰮�0�F�?+,�G
�z�u�n	�R		�A
�R�[�f�Y�T�_�L	)2�E
�`�e�[�2�/��
��C�x�d�e�S�
�B鰶�n�J�J�i�x�h�N#�Q�`��V�y#�xB鱥��A�]�w�C&�_�B?鰱��g� �!�L� �!
���	��k�X�x�o
�n�L�m�h�e�d�H�a�I�L	�N�M��H�I�D�F�E�a�@�E�B�>B&鰽��C�E
�{�b
�H�]�[
�O�p�R	R
鼎���\��`�a��u��x��c��<�aB鼠�O�
���K�/�`��6
�W�V�U�]�`����x�"�i(uB8鼙�
���
��8�C�;�8�=�<�D�C
�A�B�<�=�G�Z�n��}�|�q�L�2�D�7��F��-�K�I�[�7�f���h��g�R�N�m�w�f�dB鼚��}�G�Q
R
﨎�Z�G��c��>��_,R福���\��Y��"��6B館�~R侮�F-8	_N�?�$Cd5�&�oDR懲��P�_",a�w�~��e�?nA5Z9:P�L�}/R艹�w�I3"P!�l�GjK�[�~�8�B!��	5qpq2Fw�S?�@	B!��*�Mz�	B	︐�L�g �@��D�Bff�C
𡨚���u��#�Q��h;�>��X���L)��"�s=�r�ZC🄀�}C🈂�i4LC眞�-C𠤎�SCB𠀋��~�%�H�q�A���Q�R�{�[��E�z^�C���l��N�t�1�0����o�r�e�?��*��W�3��
�O��b��3�`�
��N���Y��O�D���'�/�K�	���V��_�[
�*%�)�Q�2�).��Z���d���	��t�Z�
�>�R2�8
�t�C�f�>�e�h�
�d�,�I/�F�^@�
!�W�eX$�
�h�}�z�C𢘉�S��4+�{;��I�/�oC�𠂉��Q�n�,��O�7
�P�~��x�w2�~
��`�u�	�g$���g�	��a,�
	�[�]�W�X�M��(��h��o�,��Q��{�:�)�E�f���W�Y		�h���i�2�����\�B��9��.�g�
�c�(�M��v�|.��B�L�-0�,�|"�]	�e�~2�u�y�o�5�3�R�M�("�Y�c�l��d
�Z�&�@
�(�q�	�%�N��W�l�7�}(�l�{
��{�d�$�v4��f�wB(�r�`��~
���c�x�m�!�_�i�
�v�t�q���	�w�w�
�O��6���X�|�m���g�\�z�x�w��P
���&�U�{�;"�^���m��y�s*��w�i�<�	��i�b�%$�<�n�#��{�U�X�v+�`H"�Q�x�
�R�g�n�z�n�r�~�.
�H�0
�g�o��y0
�{�s�J��0�Y��~����k"�K0!
�\�$
�Y-�g�s�\-�`� �	�c�R�!��
��g�^ �~� �h�v�u �+"�h0�y�s��y
�d&��
�R�6�V��{�2�*�y�P�/C,𠃵��Y�b"��z�[X�M��iB�(
��*��Y�x�]�-��Yh�����S�����,�v�A��	�|��t�g�g�f��x�I�*��E,�YF�\	��}��p��~�x` ;>a¡e��!�A�$�*�;�A�G�X�A���s���@����#�*	�K�@�UaǸ�s�h��|��k�n���v�a΄��]��L!��T�\��Z�t
��_��j�
��l��wb†��Dn�U�a��m��y�=�{�V�i�g�!�*��U�y�	�*�$�k>�J�@�	��
��@6�<���Bp��"	����F�0�G�@�f��Ib享���`�9b忘�hb�KⅠ	��!	��j�H�v�!�r�n�r���@��~�0�J?�7�@�w!�7�E�M�&�5��?�\����^�F���M�:�n�!�0��~�� �y�o��B�_	�&��=�\�_��4�1�V��x���8��c�X�v�e�&�w�j����X�%��7�
�E�r�v�w��o�*��{�Y��'�7�n�@�k�[��f��u�^��k�!/�(�~�~�|�u�}��J����l�,��~�l��~�U��	��m�B(�M�^�Z��0��B�L�Q��`��h�p�s�}��{��}�>��d�p�o�g���j��p�\�3�d�Q�� �>�T�\�*�{
�u�`��d�	���n�!�%�j�9�<��P�{��]���$�v�>�A�u�K��\�g�u�m�	��
�o��
���[�/���>��t�x���E��'�-�1�^�U�~��r�Y�W���K�F�I��Y�p�|���k�J�M�r�_#�h��r�H�t�$��\����_��p��T�~�m�3"�D�B�=��R�s�i�h�m����1��E�J��b�P�x"�~�z��k�*�0�R�7��;�>�~�F�H�`�i	�k�m�~�{�~��]�����p��
�T�{�s�o�{��%���I���%�(��j����{�e�O�T�[�a�]�k�e�B�g�_b⁴�"�F	�(�	����d�b⅖�9bⒶ	�c�@�m�I	���k	��r��`	�]�h�v��P.�-b↖�,�*��Y�i�p�x��B��n�a�L�K�b�q�|����� �C�#�(�*�3��]���cb﹅�_b者�%b⺌�	bNḾ�q��e����L�p�r��m�i�v��a�u��D�X)�\�O�^�;�q��A�w������=�!�G�y�K��U��^�l�t��	�T�#��(��,�\�2��t�S�m�j�:�|�[�
��#�~�0�d�?�x�H��O��m�z���t��t�"��8�m�@��H�K�y�Z�_�S�m�
����
��y���(�q�2�y�<��T�K�r�}�x������ ��g�0��o��|��
�q��f��h�)�0�O��v�m�\�}�(�1�[�<��E��L�r�S��Y�|�^��k�Tb⁇��'�8�}�@��(	�_�Q���i��:�v�I��O��Z�E��F�u��	�l�wb�≤���k��k��
��=���x����E���l��Y��~&���P��_���k���s���z���+�H���.���J��T���9�#���b��M!��R�F��
,���g���k��-��0�"��:���I�h��_������=�e���|��R��V��b��p�'���q��H�d������K�^��Y�q��*���R	��y�R��m���
���x�����5���?�T��'��T�[��Z�+���H��k��o�~���X�����'��)��,���.�l��K����<���u�_��D���h�� ��R�r���
��M�~��V�`��d�z��p�
���d��g�}��������2�l���c��D� ��i�b��@��B������$�v��X�O��G���S��V�~��i��g���t���t�����)��)�^��2���?�{��N��P�s��]���g�{��n�u���j��L������.�q��4���B�s��F�q��L�������n��*�]�����*��a��8��h�b���b��p�{��y����$��	�}��P�g���g��b��5�t��@���k� ��H�l�����i���x�n������s��
�d���������"�N��'���L	��N�`��`�n��l��?��t��w�h����B����J��E�%��R�T��eb�<乴��W�U��h�.��x�B��{��0��	�]���p����
���k��%�~��.	��1��3�g��N��U�n��j��m�Z��u�	��}���
�]�����!�{��)�j��3��6���B�Y��E!��J�a��m��q�o��|!���M��"�
��8��;��?�f��F���Q�{��\�l��d��h�U��x���	����z���^������+�q��;��=�p��O��Q�{��W��Y��_�_��n&��r��u�i��$�]��(1��/�v��?��A�Q��D���V�e��[�8��n�q���j��
����T��((��+��.�U��1�o��6���D��F��J�I��L�!��R����~����Y��8�1��C�X��H���R���j���q����|���Q�����p��"���)��,�k��@���s����x�����~��$�H��@��D���U����g���
�M�����n��&��(�m��1���N�\��P�y��U���e���p�M��y�	�������
����E��$���A���N���T���h�g��q%��w�	�����W���
��-��0�O��3�|��V1��Z�d��^�c��a���h�
��q��t�p��}	����	�����c���y��-�~��3��6�Y��;��>��A��C��F��I�e��O��R�s��[�v��e�b��~2���M�����$���,��/�[��2���D�K��M1��V�q��z��}���K���&���P�� 3��&�[��)��.���L�^��Q�}��W��Z�{��`�z��g�{��l���v����P�����
c
🄐���@	�%��A�@	�Q������%��z��P��U�0��PK
!<9�Ǿ��6chrome/pdfjs/content/web/cmaps/UniJIS2004-UTF8-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�UniJIS2004-UTF8-HA°�MB←�bB	‐�U��dB′�Q�P6R⎛�t&+"'�+�B�9R┌�G(B┥�o/R	╀�uB ☜����z�wz�aO�_�0�/�Y
]�C�B✂�B゠�KR㌃��J�W�V�mB㌔�


�R��?�[�B㌕�1$1
�F)')��Lq
B㌗�L


�B	b─�9�7�=�;�A�?�j�]�`�U�X�w�I�~��v�O	�c�]��H����Q�[b〘�k�g�6�E�P�T�\�b�h�t�C�w�|�~����mb⎰�^��d�I�	�M�XPK
!<&h:���8chrome/pdfjs/content/web/cmaps/UniJISPro-UCS2-HW-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�
UniJIS-UCS2-Ha
 ;�g�!�$&�#
�M�_�U�T�W�
�A!��bA %�Z�n�6�Y;�m�2A!��6a%�9�7�=�;�A�?Q%�G(q%#�j�o�m�l�n�q�p�r�[�]�\�`�S�U�T�Xa%=�wQ	%@�ua%I�~a	&�������_�O	�c�N�]a'�A0�Vz!O�0�/a0���S���6Q3��J�W�V�mq3�E���
�L��N�P�!�T�	��W�Y��	�\�$�q 3-�_�b�&�e��h��n�p��q��j�r�t�s��w�%�{���|���~������a3{���
�	����[A	��m�A��L
A��EPK
!<��O��5chrome/pdfjs/content/web/cmaps/UniJISPro-UCS2-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�
UniJIS-UCS2-Ha��M�_�U�T�W�
�A!��bA %�Z�n�6�Y;�m�2A!��6a%�9�7�=�;�A�?Q%�G(q%#�j�o�m�l�n�q�p�r�[�]�\�`�S�U�T�Xa%=�wQ	%@�ua%I�~a	&�������_�O	�c�N�]a'�A0�Vz!O�0�/a0���S���6Q3��J�W�V�mq3�E���
�L��N�P�!�T�	��W�Y��	�\�$�q 3-�_�b�&�e��h��n�p��q��j�r�t�s��w�%�{���|���~������a3{���
�	����[A	��m�A��L
A��EPK
!<-M���5chrome/pdfjs/content/web/cmaps/UniJISPro-UTF8-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�
UniJIS-UTF8-Ha°�Mb‐�U�T�W�
�B←�bB‥�Z�n�P6�Y�{�m�rB↕�6b─�9�7�=�;�A�?R┌�G(r┣�j�o�m�l�n�q�p�r�[�]�\�`�S�U�T�Xb┽�wR	╀�ub╉�~b	☜��������_�O	�c�N�]b✂�B〜�Vz�aO�_�0�/�Ybヵ��S�C��6R㌃��J�W�V�mr㌎�E���
�L��N�P�!�T�	��W�Y��	�\�$�r㌭�_�b�&�e��h��n�p��q��j�r�tr㍀�v�s��w�%�{���|���~������b㍻���
�	���H�[B	=�m�[�B,�L
B:�EPK
!<5�P2E�E�8chrome/pdfjs/content/web/cmaps/UniJISX0213-UTF32-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�#C\a
!�GS�k]>2&?V3C�>S 2!+S��tA
�~$�c���
S
��n$�c�U�P
�&%$)C$�)
�f�7�![S .��s"'��A��"�}
�";�9�6"��B��;
��|�,"�m����S/P���'
��j�>"�kOL
i��O��fO�>��_c�0
�m��("��[��<��SC�i��P.S��2��j���	�
���C�����k9S
P�X��"��"O(
��zC
^�W-*'0#rSl�6B''�$�c�bCx��1TI�6��7�ZS��94
M�-�|3��)F�CX�b	>M#=:B�@�;	���J�<O@�0�G�"�W�F.��q.�J	�N
M�y�u�>�:�E�<fG~�E"#�U�"�
;ls9'�
��V�h�i� N�.:�K� �e�,�d�[�5�J��|�O�r�]C. Z�Z�+�.�/�?
�x�vC	F
�Q��(e4
�?	�^(��D@�����
�9�zC5��c��,
�`��2�H�j&�_;/1� !�f �)�d
65A�t
�6$��.+�T��*�
U	�9�.��53�6�?C��$<(�#��C���J��#�"k
l	�)�@�]VI�b0#
8%#
#'`�p�|�k�`-�k]�F1�]
�*>�1=;+ =�N�g�f�U�J�
)�^-�IR
	
QL5��?�
X�5�5�@T�
K�9G�$��+��C�U��p�t�m�Z�$�;�M�
�v��3�\�7��V�=�/�j�U�_(�A�K�v�9�y�.�@�7��#�C.���eC.���:C���K
�_�!�W�:�r�S/�0��i�� �w��w�8�5�R�t
�{�v�\S/�S��h�>�
�c�r�/�qS�/#�&�?�
�C�Z��c��c�T�c{�i�7�&&�S�����o���(�_�4�u��w��$��n��B�S�^�n�}�H�a�$�/�7�R�4��	�w�>� .�Y�O�@�4�<�{�0��P��S�_�(��VKp��A��X��]�X�`�-�t�G�W��-�e�"��N�O�x���e?�K�j8�]�/���8�G�U�F��
��a��h�(�.��\�z(����$��X��[�o�p��%�t�:�+�T�}�R�a� �G���6]�C�3�!�Y�"�o5�n S/��N���<�7�J�a��r��C�8��w��z�C
/Ӟ~/�_�H
�+��<��D�w*�3�`C00�:
�a�aC0<�Bc^S23�Op}��$
ly�nC2Q�S2��g�

�D�5�,�=��2
���^C3�pC2��ZC2��{S3�j�r�[�Z�CN�0��+C23�"��
��
��*�?�
�#�F�?�#�,�y�`'�>�M�;�:�J	�=�K��A�&�w�P�y�{C3�x


	�5
&C4�$�&�b9�S�6�m�h�N�	T� ���
�`�(��2�n$�Q�@"�Q�cC4��lR#�.�g
O�8o��x�m�jH�9C�'4��Z%=,'0"�k�z�p�k�H
7(
�p�a�5�5F>5$�LK&&.	?��i�GT4�\�#�]�r>
�K4##P\	8
�9	�P" �Z�A��8>F�2�i5%"�$�09�g��
�B^�g�6"
�\�_�,HBP
&�7J$(�*V
8�C�n
!4
3<	5&Y�1��<&��C$�`�j0�Q_ 
\(�pD�G��$%�	�f@�z�y6�d	$�.~.B�dC�D��&�r�}v%Qo�
�3SN�*�9�!��#����}�D��R��1�U��CVN�L�^�i��O�2�w�l�i�@�m�3�2���M�(�7�X�/�u�ve�	�R�\��g�f��N�� �z�3�
�i�Z�j��9�{�:�	��~�W�d�Q���}�x�X�S�w�h��N�
������I�@�G�F�5�K��r�#�yC
N�Z�
�a��e�d$
"��
CN)��U�x�y	
�t�u�T�"�y�x�w�nS	N݅����g��N��/�F�7CN��9�z�P�A�,CN끆b�F�GCN聤]S	N��Z�4�d�l�m�g���p��SSO��a��	���4�<��k�Y��M�'��\CO�q���L�xCO�kCO��f�x�3SO3��d��)��(��w��z��k��G��.��Q���PCO@��f��u����N�;SOM��\�u�C�R��6��y����a�r�/�����j���b��p�ACOi�)�Q��0��\�i�COz�pCOc��k�	�5�:��>�?S
O��%����k�!��
��W��V���������r��oCO��.����3	�$�[��q���/�5�l�CCO��<C
O��s��M�L��<�L��z�S
Oȁ�~��%���*��������}��|���COז�F���{�X
�g�.��B�0���
CO�l�(CO܁��#	�"�?�$	���(�u�dSP	�T��`����2����x����p��e�a��>��wS
P�*��6�m��O� ��~�z����ySP!�B�z��i�d�o��f��
�I�T�;��(C,P6�^�N	��y��X��q��A�(�t�'�F���e�O����?�n�R	�E�$�q��)�~��G�>�@�/�.CP@�?
)#CC2P0���� �t�u�5�{�$�y�x�y�5�{�&�'�&�}	�|�7�6��-�0�5�}�0�1�(�;�:��)��
�o� �1�9�lS	P၇
�4��������L��k���+S
P�_��J�R�T��j���I��CP��b���E�h�	�qt�E�D�9U�CQ�
CP���+�����8	�)�(�C��(SQA�8����!�1�h�d�;�^�C�:��d�?�)��r��gCQR�r�9��B�?�A��)�h�.�7�Z�m�[�
�M�`��sCQd�JCQS��;�I�F�_��T�_�
��W�>�H�S�SQ��~�#��P��3������6�7�#�"C!Q��Q�3�+�D�w�v�s�g�f�o�t�1�j�D���Y�X�/��[�~��+�*��5�R�p�3�2CQ��K �a�`CQ�����	
��1�`��j���.�-�<�=�'�P�gSQ����p�Q���u�s��L��E��zCTQ��5�F�}�
��=�}�A�j���C��:���y�x�Q�P�)�?�I�.��-��B�;�Q�$�`�	�]�`�9��(��y�7�>�a�R�3�8��&�x�}�r�3�U�U�p�{�$�7�jF�P���Y�Z�o�Y�n�O�$�i�h�/�0CR�NG�a>�`	�X�Y�GC1R��o�#���N	�1	�$�g���Z�'�+�P�'�C��*�+�N�M�N�#�I��P	�O�N��$�!�z�g�
�Z�}SR�R����)�h����v��a�`�o��*��=�3��n�A�@CR��|�{�1�t�K�b�
CS�SCR쁥a�
�H�B�g�#�B��,�E�DS
S�M�!�x��V��u�B��0������K��s�SCS#�O�A�@CS$�UCS%��i�K�T�U�)SS8�S�{�m�o���U�Q���x���T��KS	SE�V��m��
����
CSQ�p�9r�]�J�/�h�.�<��� CSa�!CSY��q�#�J�%S
Sl�"�J��W�+�^�X��;�^C#Sw�`�S�N�{�|�I�H��U�
�p��H�J�y�x�A�n�P��{�c�8���UN{�<�&CS��WCSy��	�'��(�R�I�L�Y�U�*��*�U�T�-���~�W�V��(
�V�{�7SS�Y��J��'��L��]�Z�Eq�5��
�6��#�#�w�P�H�W��E��#��p�7��1�R�>CS��r����?�ST�y�/�0�i���/�^�E���`�`C&T��l�C�8���C�<��b�*�t	�;�:�W��}�]�V�t�
��/�B�'��� �vC	T��4�O
�0��
CT����"�^�_�#��]�\����G�TST���%�X��m�y�����Y����CT���H�7�%��p��7��CST����t������z��	��<�6��5CT������ST���L��}��B�8����=�8��2���$�0��p��{C%TǢ
�5�@�=�L�+���m�n����J���o��5�}�:���+�2�7�.�m�=�*
CT��^^�C'T��2�D�E�T�n	�o���o�n�o��E�\�a�J�K�^�j��
���O�N�s��p�Q�6�q�S
U{�4��H��S��Z��g��l�n��K�C)U��<��z�o�t�
��O�f	�O�`�M�c~�&��

�4pi
�M�P�1�W
�~�o�w�h
�YCU��_C,U��:�Z���r�s�r�u�	����s����}�|
������j�k��i�b�m��z�%� �SV/�U��8���X��<��C��X��Y��\��[��TCVB�_	�6� 
�k�v�S�V	�1�#�<��k�l�Y�\CVS�E�kC*V;��*��{�i�h�x��k�v�w�v	��i�f�g�d�

��u��~��g�d�%�*�r�}�%� �a�b�%�*�t�}S
VȢg��L�i��H�w���3��8�t��/CVӢjSVעk�_����=���J�e��9����
��CCV�
�R�1�� ��e��V�}�~�O��d�K�J��*�y�r
�`�W��6�.��y��_�6����;�4�c�b�%�1�V�y��v�K��6��_CWY�`FCFV��P�d��u�t�t�k�x�p���m�h��y�{�p�m�h��u�t���t��o�r�I�H�y�	�I�H�{��x��z��y�z���	����W�R�����c�-�~���S
W��e��x���O��l�����v���3C9X�
�y�@�D	�c�b�)�7�O�~�_�Z�Y��%�.�c�L�?�B�54�\�I�a�@��.�V	����g�v
�S�s�D��M�L�U�P
�#�� �O��>CX/�21�(<�>C2X��a�y�x���
���~�	�v�o�~�	��~	�}�f�	��]�p�|�
���|�}�|�a�t��z��e���
�`���z��#SX��h��y������z��_��z����vC7Xܣ1�)�t�|�:�)�_�P�G�|�3�2�k�j�-�1����:�
o�|�q�R�#�P�Y��6�]�\��P����c�^�c�d�x�O�T�[CY�iGCX݁�y����"�"�|�/�.�/�w�$�%�$�;�8����=�<��1S	Yg�M�y�z�Y��J��w��B��[��CsYs��&�t�3�2�u�4�T��|��8�i�z�O�a��E�"��3�.��s�A�|�$�u��� �X��]
�o� �B�_�r�/�2�O�J�g�%�t��#�!�D�� �=�<�;�g�*�
 ��K�L��
�=�V	����	�G�J�	�#�$
�A�B���w��a�_�H�9�t��!� �]�\NOCY��n�CY��q
�
��m�l#

	,4�?�>C;Yy��j�?�%�`�?�<�?�'�&�8�9�6�3�7�67�=�:	�;�:�����K�J��������
�aC)Yr��	*

!
-IS[����H�G����H��I��J��[������e�[�h��*��{S[���C�l��5��*���}�p�>��,�bC[��n��D�O�R�z�5��W�����CC[���w
�C[���=
S	[��S�:��"��g��G�t��:C�![ɤ���e��J�O��B�W�M��o�X�+�D�)�(�G�B�/�}�L���/�/�L��}����1�k��'�s��u�t�S�ZI�R�2�e�b�i�/�P�C�B�k�`�o�`��s�n�w�E�h�#�J���	�[�d�
��� �;�2�
��w��G�>�[�_��:��K�F�O�J	�� �_�h
�s�r	�!��w�r	�'�>�e�d�u�H8		�o�n���5�KC[�sE<�G�C�P:1bC\�*�?	�2N�O
�N/G�%�C-[΁�z�{�z5	�
��������5���

�%�&�-�0�5�2�5�6C[ׁ�z�$2�%
	"o#2$CC&[́�D
$ 	

"#


S
]����c��������o�X�Y�e��PC0]�m�	�W�^�y�s�j�e�+�x�7�.�T�q�4���m�h�[�=�
���o��O��"�� �q�b�G�B���a�`
�!�>�UC]�� �}�|)C]���
�P�}�|�U	�-
�z
�y�&�T�M	�v�RS^w����)��$�/�7�C�$����)C&^��?�2�2�31�
��-�g��n�g�C�g���`�o�^
�U��&�n�K�L�9�/�l	C^��0�r �d�_C^����N�}�z�U�'�&�'�x�U�'�zS^������t���5�j��2��W�����W��V��w�S_���-�,��U���M��L��+�`�&C
_�w���F�Y�i����s��t��}S	_!��d�F�^���$��h��%C_-�$
�3�^�5�z��3�B��~�w�z �1��;�X�O�t�d�)�i��iC_4��!!� C_.���-�=�<�B�f�+�*�	��C�@�A�(��!�	�H��S
_|�t��L�b��1�-�`�F�7�6C_��9��-�R�h�e�}��F�GC_��<�"�#�"�!C_���S	_��>���!���f�}��9�e��dC_���-�L�*�w�h�F�W�P�H�;�	�J�?�6��|c�?�:�m�v

C_��&C_��8����3��� ��5�,��5�4���9
�*��S`�V��������p�*��'��z��s�C�C5` �5�&�#�4���
�+�2�S�J
�7�8�g�0��>!�!�F�#�o�A�>�o�:��(�~	���I�2�k�^�!�pC`]�'C`��&���;�(
��������)�(	��E�D�-�,�)�)S	`���7��
��8�R��{�f��^��_�S	`��O�"��Y�"Z�����C`��c�&�?�>�	�z�C`��
C`���!��.�1��'�"S`إz��R��/����E��f��5�c�4����GC`�g�K�F�>�S�F�_�^�#�&
�k�]�D�%���"C`��C`偨@�7�)�\�=�<�?�>�;�z�>�;�%�$�8�A��D�C�B�?SaD���j�;��I�����}�/�e�l�C
aS�!�Q�L�i�q�R���Ca^��(�u�tCal��LSan��6����\��q
��v��nC5a~�Ge��S��P	�Q�i�a�
�7�A���z�5�.�y�z�i�n�=�P	���w��Y*��Ca��zC%a{��*�'�&�L�w	�2�B�O�)�v�E�3�v�E
�r���4�p�q�F��~�Y�V�W�V�S�R�a
�/S
b�p�,��8���9�#��Z��9��:�$C?b&��l�o�n�+�m�V��m�4�w�
�I�A�3�$�S��M��)�
�(�3�<�y����r�=��,�w�n�/�x�T�K�m�~�5�r�Q�{�*�|����s�d	C
b)�^�C�_� .�=>�<Cb'��f�e�`�_�^���a�-�,�n�u�\�i�q�p
�lSb̦i�	��"��)�M�F�-����H�1���5�p��v��9��AC;b��M
<�B�	�=�D�
�k�h�f�3�N���Y�L�U�q<�
���
�"�2��1	R�#�~	�
�1�w
�X�#���U�V�?�<L�,�?�w�<�G�8�s�d��yCb��e

�c�b+C*bꁨx�{�q�h�k�j��~��~�k�h
�e�b�|�}�e�`�}�|�c�b
�a�`���7�2	�a�^�e�dSc��r��]�#�k�h����������D�E�j��A��VC�9c����2�I�L�g�p�5�I�|�q6�{��2�+�W�
�9�>�q�|
=	4��c�X�q��>�8	�}�t����)��V	�C�:�q�7�E�l��M��F�P�/��c�n	������"��v�1�:�E�@MPAB��:����s�#�
��� ���?�@�/��V�#�4�(�w��\�|���E�&�m�`E�!���1�0�7�"�H�@��
�h�9�&�2��i� �^�5�.�u�x���O�P�O�8�A�]�H�n�{�e�Z���;�6�����P�e��,Cc���O<,� <0S�+�~�1Cc��s
�1	�N�`1
N�=�@-< ��Cc偃l�JXsC&c���"+*			
5		'	
$CcꁗJ'
9*1		C-c���%!	#
"4
Sf�k��6�o��w�Y��<�+�(��W�}��R��k�e��N��+�S��O�l�!��R��Cf4�j�*�/�>5�G��D�a�Z�_�BCf3��v	
�.�'�W�~�Cf7��L
S
f]�q��6���3�����V�=C-fi�i�R�g�,�C�c�T�#�p�c�Z�� �{�f�q�d�Q�R^�2���!� �%��2��1�@�Cfs�"%C fj��Q�	����S�L�}�5�4����(�)�?�f�7�z��[�Z�E�D�E�D��Sf���'�P��m�	�[�p���NC.g�K��$��=�<�Y�n�h�w�S�K��3�Zm5�/�B�}�N�.�I��6�9�J�1���'�"��-�&�#�"���P�$Cg�(WCg��
�����i
�7�6�f�a�`�S�O���Sgl��i��;��H��M�,�y�x������`��;C	g|�$�}�b�"�9�$
�gCg{��2�k
�j��YCg���j	S	g���� ��#��L�T��1��Z��o�>Cg��n�t�]��x��!Sg����0�8�9��]����[�:

Cg��(�P�N�K�F�%��m�F�#�H�P�6Cg��+C
g���{�h��~��~��)�&�|S
g�6��h��o����e�%���$�Cg��*�d�}��n�o�T}�1�v�_�g�F�Y�b�U�]�dCg��
�'Cg���J�%�z���~�i�h�~�}�5�0�~�}Sh;��~��-�,��~�x��{�	5�
��� ��C"hH�
�j�{x��X�>�9�j�5��I�^�?�& !�}��u��%�&�}�~	�[�,�E�ChR�,;�!ChI���v�w�r�3�@�C�4�5�4�w�A�&��u�h��9�6S
h��K��v��o��6����Q�����)C	h��Q�[�]�p�=��C�NCh�/Ch���Sh��0�v�6��5��4��O������|��gChߘi�
�I�Z�-�&�e�x�DCh��.Chꁪ��A�@��]�pSi�� ��s���d�]��
��h����2�q�t�d��t�=����C@i�@�c�
� 
�e
���}�S�

��L�\�'�$�=K��1�.�//�c�(�}
� �?!�Y�
�B�/�n��d��g�2	�V�	�+�6
�}�W�N�K�
�UD�\�3��B�
�J���S
�H�e�l�)
�Y�L�uCqi�p+& 7,

 8=$	


&
18

��		�o�j/0
	
	ad	Cih�2/.��M$
cC i5�5�s�v	2�[�Z
�
	�X�U'J
1C2iB��
�4�;�4
�?
�>	�E�D�I	�L�Q�P�U�Z	�g�h�i�j	

�
�
Ci��"	B5X
	=H�R
�O7Ci��72#

S	k_�b��n��o�m�V���p��Q��nCki�2�n�O��r��t�E�=�!�
�.��C	kl��c������
�Ckk��?S
k���I�[���o��H��G��DC5k��u����u�@�A�r�y�2�o�2�(��4�#����3�~��X��
�!�<��X��E�^�v�m��l�Q�K�(�'��Ck��=hC/k���j�
�1�q� � �#�`
�;�'�p�u�&�L�u�t�O�N�O�+�^�k�p�k�&�>��s�r�A�@�u�y�("��A�3�y�j�u�wS	l��
���^�)�M��4����<Cl��1�2Sl���3���-��u�� �;��m�@��2Cl���c� �j��P� �(��4�X
�'�H�H���&Cl��IC	l���\�w�E�:�y
�E�;�vS	lρ�`�o�n�}����&��7��6��'C6l٪!��%��B�i�l�O�6�Q��h�%��'��(��g�D�(�k�r�E�@�Q�l�5�x�2�e�\�)�j�
�{�p�l��|�`��\�2Cl��B)jC*l܁��-�j�?�+�f��F�#�^�k�h�i�h	�C�B�m�.�O�r�s�L�M�e�d�J�1�h�9�*�]�h�9�*�]��NS	m����.��{��p��g� ��L�1��_Cm��mX�
�;��g{�l
�A�J�]�^�=�6
��"
��~Cm��G"Cm���)�`�"���o�H�I�J�K�J�U�w�Q�,�j��mSm�K��N���K��C������X��	�T�#�%�,Cn�Z�'�r�T��]�U�,*�1�Cn�FCn��uSn�^��
����t��f��U�S�@�rC/n)�9�6��~�w��E�>�/�P��2�q�t�C�F
�?���f	�=�R�{�t�n�W�G�4�{�t
�a�T�y�z
�+Cn9�LbC"n2��&�)� �&��h�v	�{�[�r������
�%�����M�B�$���+�*�)�S	n�����C��8��e�X���	�C#nыH�8	�3%
�p
�)�41��0��E��.�e�8
��e��+���
�

�c�@�CCn�8)�"
�m�l���q
�p�f�[�X�Y�\C	o��	
SoW��A��o��L�U���S��j������!�SCHod�k	�8X�a��|�	�>�.�k�B	�~�%�T�)�s�M�	+�j�_�:�W�t����C�A�V��;
�>�g)��	�h�|�%�
��(�M
�
��O�h�Q�@�C	�x
�C��u�Z�	�|�K�G�F��}#�m
�_��D�A��"���	Cgof�DM




�A�B+2	
uv.
/		

#

Co��Q,?�_�^�	� \%�WH�X
 �{+�|<�}'�<C(o}�

�%�$#�?�! *
 �n�m)
Co~��3

%]&.?9"2	PC.oh��C'	*	" 
!6*,Co���3U(�0M�1�lA"#C(ol��	">E
!
	1

	
S
rW����+���<����d��Q��Z��}����'�J��JCErg��
�?�~�	�X�Y�~���[
�(�I�@�g
���_
�p�`���V
�1�\���n�N�8�E��~�Y�=
�v��$�?�j�*�	�
��Y�e�N �E����!#�E�_��Z�q�v,�Q� �i���H�u��.�8CWrt�k	

		7	4+	
)("
Cr��aeRE5�w$�xaC8r��;�%�$3,(�`�a	�O�N	
�H�I
�@�=
		�WCr���I�(;�
85C6rx��
&
	,	#	Crh��HD
*
�
</C/rk��A	/ 
	@ 	Su�J�]��V�*���-����]�^��d�����k��/�0C
u0�>�<��Z�
�~��E�HCu/�rCu6���E�!�dS
uF�S��^�g�������X��=��d��'��&CuT�^��JS
uY�y�4�q�!��*�H��
����OC	ud�V�E�#�f�W�Z��D��ESuo�u�-��h��k�~��d��m�-�(��D�.CGu�@�@��'�U��[�j�e�V�Q�P��t�k��|�?�g�"��������O�N	
�w�z�[�V�a�d
�+�s��c�zCBu��~
�2�3�B���
�5�4	�
�	�����	��K�D�)�(���I�(����!�+�H��C�D�E�P�]�\�S�B�o�U�B�s�Y�B�i�h�E�w�P	�h�+��Z�[C
u���	WS	v������O�7�o��`��!� �
CHv��w�
����;�`��^��P	�x��]��
�<��2�N�p�g�Y� �m�\�H>�I�{�Z�'�fp�a�&�{�*����'�T���=
�R�#�a�G�
��G�y�6�1�d�{�P�Y��6�;�=�#�L�0�D�A�� CVv��

�%�&	
	


$.	
		=:Cv��y�=��>�o�>�p,�5C v��7%
��
��;	
��:�!5)
5Cv���^
`Kb*>OC'v���

."#?	"	
	C)v���r
	
E$!	8C4v���!		=
" +


S	y:�T��H������y��@��C�c��$C1yG�Z3�^�X�J�{�r�[�`�t�?�W�p�O	�T�$*��$�Y��k��:�(��i�h�[�\�	�y�}��W��:�;�S�T�6�%CyE�;�v�	�N�O�J�K��T�K	�~��|�F�G��~�D�E��m	�n��CyD��Y
Sy݁�g�W��k��@��)��(��)�m����G�Cy��f�;
�-�D�$���Y��I��v�
���SCyꁙ&�S�P�E�%�$��8�C���>Cy�k
S
z7� ��\��>�����t�VCzB�6�T�?�@�]�	���b���s�t�Q��P�yCzE�K
C
zD��+�7�H�K��@�E
S
z}�*����1������d��)�?��2�ZCz��+��Z�>S	z���Z�>��5�g����C	z��x	�n�i�w�t�j�G�JCz��QCz���{
�A�&�#�"�#S
zËT�@��B��=�������^��O����
C
z��	�	�n�������#�-S	z��d���.�6��1�J��d��wCz�z��Q�0�7�y�7���
�	�X�N�#Cz�c�C�\�5
�V�WCz�	#S	{%� �j������h�9���U��vC{3�J�K�V�o�M�C{1�Z	�T�B�'C{0��S{K�R�����'��k�0��N��S��v��C{]�i!
�g�� C
{`�]�(�+�L�M�&�|�	�-C{j��	S{��)���2��	�.��$��)	�X�W��"��_C{��{�5�(�p�o�g�LC{��bC	{����3�0�+�*�+�.S	{Ěc��0�����b��y��F��%CQ{Ϯn
��Z�.�-�<	��
�z�q�{�r�u�v����#�&/.�y�x�K�Qp�(�!�-��0�5�#�E�B�'�'�*�s�6�B���;�fC{��c1


�C@�BC'{ԁ�
�h�i�f�i�z�l�m�l�m�l�q���n�u�t	�w	�t�u��n	�g�f�eC{ہ�&		)
S	|ү&��H�H�C��3��z�����,C|ܯ'�E�/�+��1�2�u�t��C��V�c�K�(��C|��v�H�I	�J
C|遭1S}��2�����'�K�������k�I�0�c��T���HC} �I�L���*�nS
}*��6�����z����=����3�_C}5�9��5��XS
}?�8��Z�Q����.�R
��X��1C }K�5��,��t�S�t�/��{�r���k��Y�T�9�V�h�1�H�x�
C}M��N���
�T�5�!�T	�W�X�>�{�z��Z�>�A�<��BC}Q��8
S}���6��O�S��P�s���b��E�2��T��M�{�����C!}��N��(�l�u�X�#�P�?��*�y�k�`��	�k�;��k�r�/�*��d�2
	C}���8�I	���;	�a��9�i�h�i�fC}G	S	~�1��2����Z��P��Y
Ci~&�N��{�	
5�3�N���w��<�S��t�8�Q�|��O�j�m�b�T�I
�)����
����Q�T�d���z�4�M�L�!��G�H�!� �b�_�?�6���?�6��K�-�x
���+1�Z�[�C~G��O�N�+
/-
C#~'��B�[�Z�,
�1)�0�1�0�1�0�+� �&�%�"�����
�
���C~-��L/�)	&	S	��>�<��j��i�6�w�r��b�S�
��c��O�O��.����g��d��O��N�������C��@
�Y�D���
�Q�P���3�2C
���n��|��*��|�}�|C���f	S	�o�F���6��;���>��9�IC+�y�Q�i�h�9�Q�$�O�4�;�@�x��z�Z��I�$�T�u�i�T�l�Y�z�G��'�N	����8C�{��j�

�
��~��~��~�/�.�
�/�.C����p7S�큙t���!�������5����F�����������}��C%��7��n�~�e���l��O�e�v�k�,�;�%��F��,�3
�h��
�.
�s�v���q�A�j��>��8�C+�	�d 		
C6���y	�����
�G�F�z��	���
�
�?�F�C�8
���t�u�t�w��N�L�O�@���u�O�N�v�u�p��(�p��,C���y
	S
���M�r�X��\���&�M��0��eC��	S
���c�-��x�p��Q�	��\��]�WC�!�=�0�v��e�y�x��S	�2�>��7��6��-��q�\�w��NC�@�$���?�@��^�<��7�@�{�z�9�M��a�#C�<���n�m�5�0�1�6�w�v	�^�W�c�\�V�Y�I� �Y
�C�C��
S����+�R��?�k��~��u������(��-��,S	���E��%�?��0��k��j��	��6�JC	�ŋ_��R��k�p�$�e�^C��G�JC�Ё�	�S�ޱE��L��S��h��i��P��+�W� ��tC��)�m�
	�(�y�J�U��T�=�B�;�Z�QC�ꁚ�S��M�L��C�큮%3S�+�T��,�u�t��3��H��U��>��E�S����'�h��BC�@�O�J�
�C�<��9�a�`�C�=��/S
�O�c�Z��p��K��,��U��A��C�w�YWC�b��m�p�
;4C�Z�z�h��/�Q�~�US	���O�N��?��P�l�m��m�C7���[�O�@�*�_�2�w�h�*�!�@�b�I��N�k�V�!�^� �0�f�W�7�>��!�3�<�#��+�6�Y�\%�M�\�*�=d�y�[C����5�K�d�������i
��+�*�K�M�f!�m�l�"�C����9	)0S	�_��;���E�P�)���g��?��$S	�i���������d��5C�u�n�$�A�4��	�C��W�,-�]�C
�s��=�M�z�3
�B��5�4��C�t��JS���`�Z��b��[�=��X�������	��C8�ė�r�/��m�v�;�>�,�)�^�(�Y�
�9��f��
�~�S�Z�;�~� �B�#�w�r�S��h�i�P�i��t�r�CJ�a�w�T�v�_	
C�Ӂ�_���z�
��G�J�K�G��u�n�
�H
�J
�K�TC�́�R%	S	���,��t�e����r��q��p�EC����PC���:C���a�(�b�c�b�ES
���-��z������w�y�*��r��F��?��LCO���>���9�:�
��C�&�q��'�"?@��r�m�
�W� �6
�	�;�D�{��d�/�L�O�N��)�H�b�
��=�R
	
�5�6�C�'���
�	�3�,�Y��t��*	�	�C���b%�G�H(��
�K�JaC-����|
�k�n�s�r�s�r�o�n�m�l�s�t�/�`�5�
�x'��(�%
�C"����m	
"
#	S
��X�*��V�s��m��l��m��|C���>,�Y�(	�_�)�d�
�,T>��&C-�
�r
 


				
 
��+�8CP���#�n�o�a�n�o�\��
�a�l�Z�i�h�]�
�c�b�h�k��b
�U�l��
�3�=�t�v�h�c�b�]�t�}�|���d�[�x�z�y�\�e�d�_�^�_�^�i�h�c�b�a�`�]�	�b�o�n�k�*�)�f�=�+S
�
���|��q��,�k���CC���=��	�U�V�g�)�z��)C��C���<�x�}�|	�m�n�o�j�k�j��m�j��k�j�S�U��C�w��c��h��9��&��!�� ��
�b��.�x��?����HC/�h�/��x�/j�b
S�Z�y�	�
�d�	�d��W�	�t�W� �a;�\�S�C�c�\�F�L�<��q�;�3�d�z��d�+��={�v�u�D�U�\CI�k�3

				
	�.�7�$�

�^�[
C�����
* 
<�[S�Z�G�FC,�i��O�j�i0�h�[�r�d�e$�h�a�`�i�y�x�`�a�`�e�
��`
�a�`�a�`�c��C$�q��G#

 S��|��H��=��T��
�C��N���A�W�!C��S
����X��e��d����A��b��]��\C�*�Z�/�g�`�k��@�YC�6��@	�AC�/��y������#�j�I�#S	�P�.�f�P��b��u�X���I�IC5�[�
�!��6�G��n�8�M���#�V�J�9�
�:�[�>�r�g�6�>�r�u��d�u��-��4�i�!�L�O��9��%�K�/��y�`�&C�]��d�j
�k�j�o�n	�K�J�M�|�N�M�L�s�r�OC�\��	S	�ڴ�3�� ��s��C��#���+��J���U��H�0�o�-�,�\�[�bC��&C�쁚c�h�i�S�:�7�L�Q�8�7�LS	��`����.����2�i��k�aCF�&�5���7�<�h�e�C�D�K�N�'�"�M���F�G�F�T�U�w��h��� �G�J�%�&�o�n�I�[�"�5�� �Q�P�U�X�1�n�.��SC+�-��	�3
�~�
�4�J�}�z�4
�5�~�K�J�O�N�O�7�|�}���K�C�z��A�@��B�I�H�G��9��HC�0��
�(S
���A�E���/�R�V�g�nC	���H�D�%�5��r�
��]��^S
���u���?�a����
��w�X����I��RC"�Ε*�J�b�kd�
�(�u�t�_�<�E�0�CX�.����H�)
��+�>�<
� )�y�R�]��y�Q�.�	4�MC+�ʹ}��	
�r�qP�b
�c 
C8�ҁ�v�7�Q
��5�4���9�6�
�L�
�
���		��	��'�&��F�G�`�&�'�b
�G��	��E�D�E�D�M	�Q�^�0�=	�<�9�}C�ρ�<

e
	
 !S
�G� ��D��A��V����YC�_�|��C�U�*
C�T��1�V��?�>��^�a���7�Z
�D�#�"�G�F��]�\�S	������G�U�V��B�+��E�*C�ʒ�q�|�0�J�!�1�a��4
�P�UC���>�`�a�j�k
C���H�\�i��D�]�\	�C�@��"���G�T�
�f�#�E
�j�O�&�S�|S�%��!�����(��O���:�H�W�~��uS	�3�S��J�+�/�X��]�����#S
�>�X�[��&�v��I����7���C9�I�Z�[�^�A�9�&�$���]�^�M�L�	�c��`��k�A�,�7�2��S�7����Z�8�}��|�!�"�A�\�1�K���\����G�&C�J��@�N�_�^�QK�R�?
�j�]�^
�u�N� �u�t�u�0�c�$C�O��|7
S	��d�J�#��H��]�k��CP��f�N�/�4
��� �;�Z�]��J�O�`��
�
�b�e�u�j��p�W�@�=�Y�o�V�z�	�T��m�"�S�D�/
�*�[�x�h�-�6�i�`�Y�,/e��Wi��E�#�&
�j�1�+0��B�0�M��8��4
��C��<�;�T�2�uC@��v

�Z�[�d�e�-�jH%�>�G�>�?
�>�=
�Z�[�d�aC�,�VC""
)			C�-��~i
'$NC8�/��0�?�:�=�<�=�<�=�:�=�8�9�8�9�6�5�(�/	�$�%� �!���
����C�?��"!&	"	
S
�ֶ8��G��.��m�	�K�t�^�C��|��%��-%�"�KC��:�
�	
���
��zC�쁒}�t��6�7��
��a���,�b
�i�/
�&S
�B������0���'��_��t��mC:�N�L�}�~���u�{�\�	���	�G��5�f��*�w�B
��w�z
��G	�N��u�z	�+��{�Q�s���=�:�/��_�b�S�:�K�NC�O�zR�O�N$�K�JC2�M��M�������	��������z�w���{����	������)�vS���o���B��?�r��� ��)�#��n����L�$��G�tC�/�h�x�I�e�9�eC
�.�[
�
��z�C�3��W�4�5�4�_��m��6�'�9�^�9�o�&S	�i��x�c�6��A����E��$��+��~C�u�d�H
�e	�.��T��
"��_6�"�n�U��h�#�c�:��F��W�hC5�|�e
�t�}	
�h�e�h�e
�r
�_	�V�W�L	�S��,�-C5�s��%�h�e�d�e�E�D
�+�
�$�#�H�I� �Y�A�	� �Y	�d�#�N�O� ��@�G�$�Y�I��L�M
���#
���L�M���L�L�C"�t��z

		
�yS������}����'�C�x�2���w�xC?����A�D�5�.�I�H6�� �7�B�U�K���)�,���
�<�D�q�4�1�c�L�A�B�u�p�;�h�%�f���>�)��}��R�$�k�TC&����Y���
�~���K�J�����4�`�"�S�R�O�N�)�(�)�V�P�U�T�1�0C����&>
S
���4���r��s�
��*���z�HC)���E�6�D�s�n�Z�U�+��B��A��o�(�g�|���-�8��O�]�&�I�I�y�r�K�H�K�$�A�|�+�1C���"�|�}�0�S�c�f�i
�t�uC����8#S	��)�&��Q��P��Q�	��$��iC=�
�R�C�5�v�5�4�!�B�=��"���!���4�5�~�E�D�6�]�$�q�r�!� �m�l�G�@�W
�~�Z�3�6���&C"���\�B�C�F�G
�}�l�q�o�n�o�,�?�o�*�E��>�1�p�>�1	�0�I
�H�I��	C���?,
"
 S	�Ƹ������J�N����P�<�
C�ӌ��=�k�d�:��	A�|�`C�ܸC�Ё�R�K�H�I�y�j�T�A�.�9�H
��9�y�D	�M�6�1S	����L�y�Y�?���e��~CE��B�&�K��k�%� � ��K���$�\�1@�02�C�3�G
�h�5�Q���6�q��4�E�T�C�P.�D>�H�Y�/�P�[�T�^��e�V���s)�f�-�^�-�
�.)�M�$�i�8�� �$N���D�C�w�j��pC�ܰ9�?�B
	�(Co�!��H�G�J�c
�b�K5"	� �	H��			
�L�#�/A�0	�'

�p�q�l
�k�l�mC�9�3�&�7�'�BC��8KZQ8
�"	RC�V��*�p�*�=
+$0C<���u!D
(B
V	
"
C�&���<	�*�/
�>�A	CM���YC
?
L

S	����P�]���H��;�C��*��O�C�ɏ%�$���}��bC����c�\�c


�H�3		
		C%����2�?�L�M�L�M�:�!��s��/�b�3�x�-�d�e�f	�]�(�~�)���$�A�.�)�8S
�!�4��^�M�����+�*�;�qC��-�;�h�s�z�U�(��>�	�d��.l�O�N�i�p	�E�`�Q�8�a�X�1�F�m�f�_�Z�S�k��6
��l�|� �f�U�z�T�_�S
�Z	)2�M�R
�`�e��|�G�]�(�2�/V���
��;�:�7�6���;�:���x�d�e�	�2��d�t�-�}�6�;�D�
��}�M�I�C���C�6�n
�
?# �:C�e��A�]7&�C?�1��g� �!� �!
���	�k�x�o
�n�m�h�e�d�a	�L	�M]�H�I�F�E!�@�E�>C&�=��C
{"

p	S
����\��`�a��u��x��c��<�aC� �O�
���/�`��6
�W�V�]�`����x�")(uC7��
���
��8�;�8�=�<�D�C
�A�B�<�=�G�n��}�|�q�L���F��-�K�	�[�7�f��(��g�N-�w&�dC���}
S
��Z�G��c��>��_,C����"��6S+�0�F-8	_N�?�$Cd5�&�oD
_",a�w�~��e�?nA5Z9:P�L�}/S�]�w�I3"P!�l�GjK�[�~�8�C��CC���	5qpq2Fw�S?	B!��*�MzC��Y�N�h�\��n��Y�5�#�Q�h;�>���^)N�"�s=2�ZC��LC��C��i4LCD�~�%�H�q�A���Q�R�{�e�
u��_�E�:^�C��l��t�q�0?��o�r�%�?�j��W�3�J�OG�b�E�3� �
G�N��Y�O�D��g�/�K�	��V�_�[
�*%�)�2�).��A�d��	��t�Z�
�>2�8
�t�C&�>�e�h�
$�,�I/�^@�
!�W#�	�`$�
(�}:�C&	�S�t+�;;��I�o�oC����Q�n�,Ew
P�>�L�x�72~
�B �5I�'$��'�I�!,S
	[�W�X
�(��h�ol�Q�;�z�)��f��V		(R��i�2W����B��9��E.�gO
�c�(�
_v�<.�TB�-0�,�|"]	e~2u9/�5s�R
�D("�Y#�l�$
�Z�f�
�(1�	�%�N�l�B7�=(�l{
�K�{�$�$�64��&�wB(2 �>
�L�#8-�a_�iJ�v4�q���I�w7�
���6��@��<�m��'\:8w
H��&�{�{"��?�m@93*G�w�)�<I�i�b�%$�<�.c�{�U�v+� H"8�JR�g�nz.�2�~�.
�p
�go�y0
�;�s
�p�Y�>�H��k"�K0!
�$
-'3�\-� `�	#RaF�E
�'�^ �>� (6�u �+"�h093@9
�d&B
Rv���{�2�*�9�/C,���Yb"�X�:�[X�M�iB�(
�*�Y�8�]�m�Yh���Q�S����,�6�A�I�|�t�g�g�f�8�I�*�,F	�}�0R>�xc ;>%e��!�$�*�;�A�G�X���0n��Fpc��s���F�#�*�K�P��n�a��*�U�y�	�*�$+R�J��
U��cN���� �9c�K!`	��!	��jH�v�a�r�n�2���~�0JK�7!�7�E
�&�5L�?���X�^����M�:���!�0�>�`�y�/��B	�&H�=�\�_��4�1�V�x��C�8�c�X6�e<�p(�w*�����%_�7J�E�2�v�7�o�*�_�{���'�7.�@�+�[Z�f�C�u^��+�!/�(�~�~|�u=�J���l�,��h�<�l^�~�I��-�B(�M^�Z�L�0R�BL�Q\�`S�h0�s�=�;��=�>�T�d�f)�og��?�j�p�\�s�d�f��|T��� �>��\j�{
�u �$�	��.�!�%j�9�<��P�{�]�G�$v�>�A5�KV�\�g�u-�	��
�/��
��[�/Q�>�K�tx���g�-�1��U�~��r�Y�W�K��K�F�IP�Y��y�|�G��k�J�M�r�_#�hF�r�t�d����C�_��pT�~�m�3"�D�B�=��R�3�i(�m�@���1��E�J��bP�x"�~�:��+�*�0�7D�;�>>�F�H�`�i	�k�m>�{�~��G��0��
�T�;�so�{A�%��	�V�%�(��*����$�{�e�O�Tc 2�@�"	�(�I����$�c!V�9c
$��c�I	��+	�2� 	�]�h�v�.�-c!��,�*��p�x�C�B�.�a�L�K�b�q�|����� �#�(�*�3��]c�[�%c.��	c`��sh�|��k.���v���q�e��x�9�a�	��
�l�p�j�r	�~B�m)�v��a�u�C�X)�\�^�;�q��w������=a�G9�K��U�@�^�l�t��	��#U�(Q�,\�2�t�S�-�j�z�|��
�S�#�>�0�
�:U�?x�HK�O�K�m�:��4�t�"��8�-�@�H�Ky�Z�_S�m�
����
��9��L�(�1�2y�<�^�T��r=�x���E��� �g�0�f�Q�f�o��|��
1�&��(�)�p�O�v�m��}�h�1��<�^�E��Lr�S��Y|�^��+�Tc G��g�8�}�(	�_�Q�B�iZ�:�6�I�D�O��E��F�u�P	�l�wc�5]��=���x�@��E�R��l���~&��P��_�D��k\��sE��z@��+���n���J��T���9�c��"��M!��R��
,��'���k��-��0b��:��<n��I�(��_�F�����=e���|��R��V�"��p�'��q��H$������K���Y1��*���R	��y��m���
��8�����5B��?���'��T��Zk��H��k��o�>��X���F��'��)��,���.�l��K���<�W��u���D���(�� ��R2���M��M>��V@��^��d:��p�
���$��g=��P�����2,���#��D�`��i�"��@��BX�����$�6��X���G�[��S��V��e|��i�g��t��4�����)��)^��2���?�;��N��Ps��]T��g;��nu��*��L����F��.1��4L��B�3��Fq��L����[��.��*���^��*�!��8��h"��"��p{��yQ��d��	=��P���#���'��b��5�t��@C��k`��H�l�����i��x.��W��3��
$���������"N��'\��L	��N� ��`.��l��?��t��w�(����BW���
��E�%��R���dc
���]��L!��T��Z4
��_��jB
��l��w����+��kc�=Nt��W�U��hn��x��{p��	��0����
��+��%>��.	��1��3�g��N��Un��j��mZ��uI��}���
���L��!{��)j��3��6���B��E!��J�a��m��qo��|!���
��"�J��8��;��?&��F���Q{��\l��d��h�U��x�Y��	G��:�������+q��;��=�0��O��Q;��W��Y��_���n&��r��u�)��$��(1��/�6��?��A��D���V%��[�x��n�1��j��
������((��+��.��1/��6���D��F��J	��La��R���>���P��3G��8q��CX��H���R���jH��q�H���<��Q����p��"@��)��,�k��@�R��s�	��y��8����>��$���@��D�N��U���g�O��
�
����n��&��(-��1�Z��N��P9��U[��e\��pM��yI����R��
���E��$�A��A���NH��T���hg��q%��wI����W���
��-��0��3�|��V1��Z$��^#��a?��hJ��q��t0��}	���I�����#���9��->��3��6��;��>��A��C��F��I%��O��R3��[6��e�"��~2��
���W��$���,��/��2���DK��M1��V�q��z��}���s���� 3��&��)��.�D��L��Q=��W��Z;��`:��g{��l\��vC����L��
s	�5�[�a�]�k�e�c�g�_�_c��"	����0>�G@�Ic�_�c��/����Ac+O����%��z�P��U�p��PK
!<UybK��8chrome/pdfjs/content/web/cmaps/UniJISX0213-UTF32-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�UniJISX0213-UTF32-HC!��bC �T�?�dC!�wS#��t&+"'�+�B�9S%�G(C%%�o/S	%@�uC &���z�wz!O�0�/
]��C'�C0��KS3��J�W�V�mC3�


��K?�C3�1$1
)')��q
C3�L


	&��vc%�9�7�=�;�A�?�j�]�`�U�X�w	�~�6�O	�c�]�H^����[c0�k�g�6�E�P�T�\�b�h�t�w�|�~����mc#��^�d�I�X	�M�XPK
!<��j�����<chrome/pdfjs/content/web/cmaps/UniJISX02132004-UTF32-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�#C\a
!�GS�k]>2&?V3C�>S 2!+S��tA
�~$�c���
S
��n$�c�U�P
�&%$)C$�)
�f�7�![S .��s"'��A��"�}
�";�9�6"��B��;
��|�,"�m����S/P���'
��j�>"�kOL
i��O��fO�>��_c�0
�m��("��[��<��SC�i��P.S��2��j���	�
���C�����k9S
P�X��"��"O(
��zC
^�W-*'0#rSl�6B''�$�c�bCx��1TI�6��7�ZS��94
M�-�|3��)F�CX�b	>M#=:B�@�;	���J�<O@�0�G�"�W�F.��q.�J	�N
M�y�u�>�:�E�<fG~�E"#�U�"�
;ls9'�
��V�h�i� N�.:�K� �e�,�d�[�5�J��|�O�r�]C. Z�Z�+�.�/�?
�x�vC	F
�Q��(e4
�?	�^(��D@�����
�9�zC5��c��,
�`��2�H�j&�_;/1� !�f �)�d
65A�t
�6$��.+�T��*�
U	�9�.��53�6�?C��$<(�#��C���J��#�"k
l	�)�@�]VI�b0#
8%#
#'`�p�|�k�`-�k]�F1�]
�*>�1=;+ =�N�g�f�U�J�
)�^-�IR
	
QL5��?�
X�5�5�@T�
K�9G�$��+��C�U��p�t�m�Z�$�;�M�
�v��3�\�7��V�=�/�j�U�_(�A�K�v�9�y�.�@�7��#�C.���eC.���:C���K
�_�!�W�:�r�S/�0��i�� �w��w�8�5�R�t
�{�v�\S/�S��h�>�
�c�r�/�qS�/#�&�?�
�C�Z��c��c�T�c{�i�7�&&�S�����o���(�_�4�u��w��$��n��B�S�^�n�}�H�a�$�/�7�R�4��	�t�-� .�Y�O�@�4�<�{�0��P��S�_�(��VKp��A��X��]�X�`�-�t�G�W��-�e�"��N�O�x���e?�K�j8�]�/���8�G�U�F��
��a��h�(�.��\�z(����$��X��[�o�p��%�t�:�+�T�}�R�a� �G���6]�C�3�!�Y�"�o5�n S/��N���<�7�J�a��r��C�8��w��z�C
/Ӟ~/�_�H
�+��<��D�w*�3�`C00�:
�a�aC0<�Bc^S23�Op}��$
ly�nC2Q�S2��g�

�D�5�,�=��2
���^C3�pC2��ZC2��{S3�j�r�[�Z�CN�0��+C23�"��
��
��*�?�
�#�F�?�#�,�y�`'�>�M�;�:�J	�=�K��A�&�w�P�y�{C3�x


	�5
&C4�$�&�b9�S�6�m�h�N�	T� ���
�`�(��2�n$�Q�@"�Q�cC4��lR#�.�g
O�8o��x�m�jH�9C�'4��Z%=,'0"�k�z�p�k�H
7(
�p�a�5�5F>5$�LK&&.	?��i�GT4�\�#�]�r>
�K4##P\	8
�9	�P" �Z�A��8>F�2�i5%"�$�09�g��
�B^�g�6"
�\�_�,HBP
&�7J$(�*V
8�C�n
!4
3<	5&Y�1��<&��C$�`�j0�Q_ 
\(�pD�G��$%�	�f@�z�y6�d	$�.~.B�dC�D��&�r�}v%Qo�
�3SN�*�9�!��#����}�D��R��1�U��CVN�L�^�i��O�2�w�l�i�@�m�3�2���M�(�7�X�/�u�ve�	�R�\��g�f��N�� �z�3�
�i�Z�j��9�{�:�	��~�W�d�Q���}�x�X�S�w�h��N�
������I�@�G�F�5�K��r�#�yC
N�Z�
�a��e�d$
"��
CN)��U�x�y	
�t�u�T�"�y�x�w�nS	N݅����g��N��/�F�7CN��9�z�P�A�,CN끆b�F�GCN聤]S	N��Z�4�d�l�m�g���p��SSO��a��	���4�<��k�Y��M�'��\CO�q���L�xCO�kCO��f�x�3SO3��d��)��(��w��z��k��G��.��Q���PCO@��f��u����N�;SOM��\�u�C�R��6��y����a�r�/�����j���b��p�ACOi�)�Q��0��\�i�COz�pCOc��k�	�5�:��>�?S
O��%����k�!��
��W��V���������r��oCO��.����3	�$�[��q���/�5�l�CCO��<C
O��s��M�L��<�L��z�S
Oȁ�~��%���*��������}��|���COז�F���{�X
�g�.��B�0���
CO�l�(CO܁��#	�"�?�$	���(�u�dSP	�T��`����2����x����p��e�a��>��wS
P�*��6�m��O� ��~�z����ySP!�B�z��i�d�v���
�I�T�;��(C+P6�^�N	��y��X��q��A�(�t�'�F���e�O����?�n�R	�E�$�q�
�)��G�>�@�/�.CP@�?
)#0�1�2C2P0���� �t�u�5�{�$�y�x�y�5�{�&�'�&�}	�|�7�6��-�0�5�}�0�1�(�;�:��)��
�o� �1�9�lS	P၇
�4��������L��k���+S
P�_��J�R�T��j���I��CP��b���E�h�	�qt�E�D�9�:CQ2�vCP���+����-�
�8�E�F�)�(�C��(SQA�8����!�1�h�d�;�^�C�:��d�?��P�x��gCQR�r�9��B�?�A��)�h�.�7�Z�m�[�
�M�`��sCQd�JCQS��;�I�F�_��T�_�
��W�>�H�S�SQ��~�#��P��3������6�7�#�"C Q��Q�3�+�D�{�v�s�g�f�o�t�1�j�D���Y�X�/��[�~��+�*��5�R�p�3�2CQ��K���a�`CQ�����	
��1�`��j���.�-�<�=�'�P�gSQ����p�Q���u�s��L��E��zCTQ��5�F�}�
��=�}�A�j���C��:���y�x�Q�P�)�?�I�.��-��B�;�Q�$�`�	�]�`�9��(��y�7�>�a�R�3�8��&�x�}�r�3�U�U�p�{�$�7�jF�P���Y�Z�o�Y�n�O�$�i�h�/�0CR�NG�a>�`	�X�Y�GC1R��o�#���N	�1	�$�g���Z�'�+�P�'�C��*�+�N�M�N�#�I��P	�O�N��$�!�z�g�
�Z�}SR�R����)�h����v��a�`�o��*��=�3��n�A�@CR��|�{�1�t�K�b�
CS�SCR쁥a�
�H�B�g�#�B��,�E�DS
S�M�!�x��V��u�B��0������K��s�SCS#�O�A�@CS$�UCS%��i�K�T�U�)SS8�S�{�m�o���U�Q���x���T��KS	SE�V��m��
����
CSQ�p�9r�]�J�/�h�.�<��� CSa�!CSY��q�#�J�%S
Sl�"�J��W�+�^�X��;�^CSw�`�S�N�I�H��U�
�p��K�J�y�x�A�n�P��]�8���UN{�<CS�m�R(�]CSy��	�'��(�R�I�L�Y�U�*��n�E�U�T�-���~�W�V��(�P��{�7SS�Y��J�A�f��]�Z�Eq�5��
�6��#�#�w�P�H�W��E��#��p�7��1�R�>CS��r����?�ST�y�/�0�i���/�^�E���`�`C&T��l�C�8���C�<��b�*�t	�;�:�W��}�]�V�t�
��/�B�'��� �vC	T��4�O
�0��
CT����"�^�_�#��]�\����G�TST���%�X��m�y�����Y����CT���H�7�%��p��7��CST����t������z����@�
�6��5CT������ST���L��}��B�8����=�8��2���$�0��p��{C$TǢ
�5�@�=�L���m�n����J���o��5�}�:���+�2�7�.�m�=�*
CT��^�C'T��2�D�E�T�n	�o���o�n�o��E�\�a�J�K�^�j��
���O�N�s��p�Q�6�q�S
U{�4��H��S��Z��g��l�n��K�C(U��<��z�o�t�
��O�f	�O�`�M�c~�&�%
�4pi
�M�P�1�W
�~�o�w�h
�YCU��_)�_C,U��:�Z���r�s�r�u�	����s����}�|
������j�k��i�b�m��z�%� �SV/�U��8���P��D��C��X��Y��\��[��TCVN�L
�k�v�S�V	�1�#�<��k�l�Y�\CVB�Z	��bE�kC+V;��*��{�i�h�x��k�v�w�v	��i�f�g�d�

��u��~��g�d�%�*�r�}�%� �a�b�%�*�t�5�6S
VȢg��L�i��H�w���3��8�t��/CVӢjSVעk�_����=���J�e��9����
��CCV�
�R�1�� ��e��V�}�~�O��d�K�J��*�y�r
�`�W��6�.��y��_�6����;�4�c�b�%�1�V�y��v�K��6��_CWY�`FCFV��P�d��u�t�t�k�x�p���m�h��y�{�p�m�h��u�t���t��o�r�I�H�y�	�I�H�{��x��z��y�z���	����W�R�����c�-�~���S
W��e��x���O��l�����v���3C8X�
�y�@�D	�c�b�)�7�O�~�_�}��%�.�c�L�?�B�54�\�I�a�@��.�V	����g�v
�S�s�D��M�L�U�P
�#�� �O��>CX/�2�,+<�>C2X��a�y�x���
���~�	�v�o�~�	��~	�}�f�	��]�p�|�
���|�}�|�a�t��z��e���
�`���z��#SX��h��y������z��_��z����vC7Xܣ1�)�t�|�:�)�_�P�G�|�3�2�k�j�-�1����:�
o�|�q�R�#�P�Y��6�]�\��P����c�^�c�d�x�O�T�[CY�iGCX݁�y����� �|��"�|�/�.�/�w�$�%�$�;�8����=�<��1S	Yg�M�y�z�Y��J��w��B��[��CrYs��&�t�3�2�u�4�T��|��8�i�z�O�a��E�"��3�.��s�A�|�$�u��� �X��]
�o� �B�_�r�/�2�O�J	��t��#�!�D�� �=�<�;�g�*�
 ��K�L��
�=�V	����	�G�J�	�#�$
�A�B���w��a�_�H�9�t��!� �]�\NOCY��nn��,�CY��q
�
��m�l#

	,4�?�>C;Yy��j�?�%�`�?�<�?�'�&�8�9�6�3�7�67�=�:	�;�:�����K�J��������
�aC)Yr��	*

!
-IS[����H�G����H��I��J��[������e�[�h��*��{S[���C�l��5��*���}�p�>��,�bC[��n��D�O�R�z�5��W�����CC[���w
�C[���=
S	[��S�:��"��g��G�t��:C�[ɤ���e��J�O��B�W�M��o�X�+�D�)�(�G�B�/�}�L���/�/�L��}����1�k��'�s��u�t�S�ZI�R�2�e�b�i�/�P�C�B�k�`�o�`��w�E�9�J���	�[�d�
��� �;�2�
��w��G�>�[�_��:��K�F�O�J	�� �_�h
�s�r	�!��w�r	�'�>�e�d�u�H8		�o�n���5�KC[�sE2�	�>�{C�P:1bC\�*�?	�2N�O
�N/G�%�C-[΁�z�{�z5	�
��������5���

�%�&�-�0�5�2�5�6C[ׁ�z�$2�%
	"o#2$CC&[́�D
$ 	

"#


S
]����c��������o�X�Y�e��PC/]�m�	�W�^�y�s�j��x�7�.�T�q�4���m�h�[�=�
���o��O��"�� �q�b�G�B���a�`
�!�>�UC]��C]�� �T�S�|�$�'�"�|�U�M�L�-
�z
�y�)�N�T�M	�v�RS^w����)��$�/�7�C�$����)C$^��?�2�2�31�
T�g��n�g�C�g���`�o�^
�U��&�n�K�L�g�l	C^��pH
C^����N
���}�-�*�z�U�'�&�'�K�B�U�Q�(�zS^������t����0��{��2��W�����W��V��w�S_���-�,��U���M��L��+�`�&C
_�w���F�Y�i����s��t��}S	_!��d�F�^���$��h��%C_-�$
�3�^�5�z��3�B��~�w�z �1��;�X�O�t�d�)�i��iC_4��!!� C_.���-�=�<�B�f�+�*�	��C�@�A�(��!�	�H��S
_|�t��L�b��1�-�`�F�7�6C_��9��-�R�h�e�}��D�GC_��<�"�Y�4�"�!C_���S	_��>���!���f�}��9�e��dC_���-�L�*�
�F�W�P�H�;�	�J�?�6��|c�?�:�m�v

C_���5 �4C_��8����3��� ��5�,��5�4���9
�*��S`�V��������p�*��'��z��s�C�C4` �5�&�#�4���
�+�2�S�J
�7�8�g�0��>!&�#�o�A�>�o�:��(�~	���I�2�k�^�!�pC`]�'C`��&���;�(
��������)�B�	��E�D�-�,�)�(S	`���7��
��8�R��{�f��^��_�S	`��O�"��Y�"Z�����C`��c�&�?�>�	�z�C`��
C`���!��.�1��'�"S`إz��R��/����E��f��5�c�4����GC`�g�K�F�>�S�F�_�^�#�&
�m�]�D�%���"C`���%�$C`偨@�7�)�\�=�<�?�>�;�z�>�;�%�$�8�A��D�C�B�?SaD���j�;��I�����}�/�e�l�C
aS�!�Q�L�i�q�R���Ca^��(�u�tCal��LSan��6����\��q
��v��nC5a~�Ge��S��P	�Q�i�a�
�7�A���z�5�.�y�z�i�n�=�P	���w��Y*��Ca��zC%a{��*�'�&�L�w	�2�B�O�)�v�E�3�v�E
�r���4�p�q�F��~�Y�V�W�V�S�R�a
�/S
b�p�,��8���9�#��Z��9��:�$C?b&��l�o�n�+�m�V��m��G�
�I�A�3�$�S��M��)�
�(�3�<�y����r�=��,�w�n�/�x�T�K�m�~�5�r�Q�{�*�|����s�d	C
b)�^�C�_� .�=>�<Cb'��f�e�`�_�^���a�-�,�n�u�\�i�q�p
�lSb̦i�	��"��)�M�F�-����H�1���5�p��v��9��AC7b��M
<�B�	�=�D�
�k�h�f�3�N���Y�L�U�q<�
����v�1	R�#�~	�
�'
�X�#����?�<L�,�?�w�<�G�8�s�d��yCc=�bG�C7bꁨx�{�/�<�h�)�(�)�<�j��+�c
�
��~�k�h�-�(�c�b�|�}�e�`�}�|�c�b	�/�.�a�`��-�,��7�{�,�/�,�-�J�^�e�dSc��r��]�#�k�h����������D�E�j��A��VC�3c����2�I�L
�5�I�|�q6�{��2�+�W�
�9�>�q�|
=	4��c�X�q��>�8	�}�t���
�E�V	�C�:�q�7�E�l��M���/��c�n	������v�1�:�E�@MPAB��:����s�#�
��� ���?�@�/��V�#�4�(�w��\�|���E�&�m�`E�!���1�0�7�"�H�H�
�h�9�&�2��i� �^�5�.�u�x���O�P�O�8�A�]�H�n�{�e�Z���;�6�����P�e��,Ccþ'1�V�O<,� �g"�f�c�b�#?�"+�~�1Cc��s
�1	�N�`1
N�=�@-< ��C,c���"�_�b+*�q�p		
$�w�x	
��
	��	
$CcꁗJ'
9*1		Cdo��(�70C-c���%!	#
"4
Sf�k��6�o��w�Y��<�+�(��W�}��R��k�e��N��+�S��O�l�!��R��Cf4�j�*�/�>5�G��D�a�Z�_�BCf3��v	
�.�'�W�~�Cf7��L
S
f]�q��6���3�����V�yC-fi�i�R�g�,�C�c�T�#�p�c�Z�� �{�f�q�d�Q�R^�2���!� �%��2��1�@�Cfs�"%C fj��Q�	����S�L�}�5�4����(�T�=�f�7�z��[�Z�E�D�E�D��Sf���'�P��m�	�[�p���NC,g�K��$��=�<�Y�n�h�w�S�K��3�Zm5�/�B�}�N�.�I��6�9�J�1����-�&�#�"���P�$Cg�(D�3�4Cg��
�����i
�7�6�f�a�`�S�O���
�'Sgl��i��;��H��M�,�y�x������`��;C	g|�$�}�b�"�9�$
�gCg{��2�k
�j��YCg���j	S	g���� ��#��L�T��1��Z��o�>Cg��n�t�]��x��!Sg����0�8�9��]����[�:

Cg��(�P�N�K�F�%��m�F�#�H�P�6Cg��+C
g���{�h��~��~��)�&�|S
g�6��h��o����e�%���$�Cg��*�d�}��n�o�T}�1�v�_�g�F�Y�b�U�]�dCg��
�'Cg���J�%�z���~�i�h�~�}�5�0�~�}Sh;��~��-�,��~�x��{�	5�
��� ��C!hH�
�j�{x��X�>�9�j�5��I�^�?�& !�}��u��%�&	�[�,�E�ChR�,;�!ChI���v�w�r�3�@�C�4�5�4�w�A�&��u�V�O�h��9�6S
h��K��v��o��6����Q�����)C	h��Q�[�]�p�=��C�NCh�/Ch���Sh��0�v�6��5��4��O������|��gChߘi�
�I�Z�-�&�e�x�DCh��.Chꁪ��A�@��]�pSi�� ��s���d�]��
��h����2�q�t�d��t�=����C7i�@�c�
� 
�e
���}�S�
��*�'�$�=K�)�/�c�,
� �?!�Y���n��d��g�2	�V�	�+�:�W�N�K�
�UD�\�#�B�
�J���S
�H�e�l�)
�Y�XCqi�p+& 7,

 8=$	


&
18

��		�o�j/0
	
	ad	Cib�X�2!�Y	�z�^.���h(�#$�"�y�z$
\��C"i5�5�I�H�s�v	2�[�Z
�
	�X�U'J
1�{�zC2iB��
�4�;�4
�?
�>	�E�D�I	�L�Q�P�U�Z	�g�h�i�j	

�
�
Ci��"	B5X
	=H�R
�O7Ci��72#

S	k_�b��n��o�m�V���p��Q��nCki�2�n�O��r��t�E�=�!�
�.��C	kl��c������
�Ckk��?S
k���I�[���o��H��G��DC4k��u����u�@�A�r�y�2�o�2�(��4�#����3�~��X��
�!�<��X��E�^�v�m��l�Q�^�'��Ck��=h�GC/k���j�
�1�q� � �#�`
�;�'�p�u�&�L�u�t�O�N�O�+�^�k�p�k�&�>��s�r�A�@�u�y�("��A�3�y�j�u�wS	l��
���^�)�M��4����<Cl��1�2Sl���3���-��u�� �;��m�@��2Cl���c� �j��P� �(��4�X
�'�H�H���&Cl��IC	l���\�w�E�:�y
�E�;�vS	lρ�`�o�n�}����&��7��6��'C6l٪!��%��B�i�l�O�6�Q��h�%��'��(��g�D�(�k�r�E�@�Q�l�5�x�2�e�\�)�j�
�{�p�l��|�`��\�2Cl��B)jC*l܁��-�j�?�+�f��F�#�^�k�h�i�h	�C�B�m�.�O�r�s�L�M�e�d�J�1�h�9�*�]�h�9�*�]��NS	m����.��{��p��g� ��L�1��_Cm��mX�X�g�;��g{�l
�A�J�
�i�^�=�6
��"
��+Cm���)�`��]�H�I�J�K�J�U�w�Q�,�j	�KCm���n"Sm�K��N���K��C������X��	�T�#�%�,Cn�Z�'�r�T��]�U�,*�1�Cn�FCn��uSn�^��
����t��f��U�S�@�rC3n)�9�6��~�w��X�e�h�{�E�>�/�P��*�y�q�t�C�F
�?���f	�=�R�{�t�n�W�G�4�F�M
�a�T�B�A�R�E
�+Cn2��&�)� �Y�h�v	�{�[�^���r	�w�M�B��r�}�Cn;��w
$S	n�����C��8��e�X���	�C#nыH�8	�3%
�p
�)�41��0��E��.�e�8
��e��)���
�

�c�@�CCn�8)�"
�m�l���q
�p�f�[�X�Y�\C	o��	
SoW��A��o��L�U���S��j������!�SC=od�k	�8X�a��|�	�>�.�k�B	�~�7�	>�s�:�W�|��C�A�V��;
�>�g)��	�h�|�%�
��A
��O�h�Q�	�x
�C��u�Z�	�|�K�G�F��}#�m
�_��{��"�	�%Cgof�DM




�A�B+2	
uv.
/		

#

Co��Q,?�_�^�	�
�U5)�0/�#��%�WH�X
 �{�J�9
�h �o�p'�<M�C'o}�

�%�$#�?�! * �n�m)
Co~��3

%]&.?9"2	PC.oh��C'	*	" 
!6*,Co���3U(�0-I�i�lA"��C)ol��	">E
!
	1

	
S
rW����+�d��P����d��Q��Z��}����'�J��JCErg��
�?�~�	�X�Y�~���[
�(�I�@�g
���_
�p�`���V
�1�\����n�N�8�E��~�Y�=
�v��$�?�j�*�	�
��Y�e�N �E����!#�E�_��Z�q�v,�Q� �i���H!�	�.�8CWrt�k	

	
	7	4+	
)("
Cr��aeRE5�w$�xaC9r��;�%�$3,��(�`�a	�O�N	
�H�I
�@�=
		�WCr���I�(;�
85C6rx��
&
	,	#	Crh��HD�T�U
*
�
</�>#�C/rk��A	/ 
	@ 	Su�J�]��V�*���-����]��>������k��/�0C
u0�>�<��Z�
�~��E�HCu/�rCu6���E�!�dS
uF�S��^�g�������X��=��d��'��&CuT�^��JS
uY�y�4�q�!��*�H��
����OC	ud�V�E�#�f�W�Z��D��ESuo�u�-��h��k�~��d��m�-�(��D�.CFu�@�@��'�U��[�j�e�V�Q�L��t�k��|�?�g�"��������O�N	
�w�z�[�V�a�d
�+�s��c�zCu��~#/�c�b	��

�w�vC8u���	�c�b�S�R�c��
�\�{�Y�	�����	���������!���@�=��B�7�4�E�o�n�s�8�3�2�I�i�h
�i�.�G�+	�BS	v������O�7�o��`��!� �
CGv��w�
����;�`��^��P	�x��]��
�<��2�N�p�g�Y� �m�\�HE�C�Z�'�fp�a�&�{�*����'�T���=
�R�#�a�G�
��G�y�6�1�d�{�P�Y��6�;�=�#�L�0�D�A�� CVv��

�%�&	
	


$.	
		=:C
v��y�=��>�oB�/{�,�5C v��7%
��
��;	
��:�!5)
5Cv���^
`Kb*>OC'v���

."#?	"	
	C)v���r
	
E$!	8C4v���!		=
" +


S	y:�T��H������y��@��C�j��VC1yG�k�U�^�X�J�{�r�[�`�t�?�W�p�O	�T�$*��$�Y��k��:�(��i�h��	�y�}��W��:�;�S�T�6�%CyE�;�v�	�N�O�J�K��T�K	�~��|�F�G��~�D�E��m	�n��CyD��Y
Sy݁�g�W��k��@��)��(��)�����G�Cy��f�;
�-�D�$���Y��X�'�v�
���SCyꁙ&�S�P�E�%�$��8�C���>Cy�k
S
z7� ��\��>�����t�VCzB�6�T�?�@�]�	���b���s�t�Q��P�yCzE�K
C
zD��+�7�H�K��@�E
S
z}�*����'������d��)�?��2�ZCz��+��Z�>S	z���Z�>��5�g����C	z��x	�n�i�w�t�j�G�JCz��QCz���{
�A�&�#�"�#S
zËT�@��B��=���|�����^��O����
C
z��	�	�n�������#�-S	z��d���.�6��1�J��d��wCz�z��Q�0�7�y�7��	�X�N�#C{�WC	z��;�:��\	�j	�I�|S	{%� �j������h�9���U��vC{3�J�K�V�o�M�C{1�Z	�T�B�'C{0��S{K�R�����'��k�0��N��S��v��C{���C{]�i!&C{`�]�(�+�T�U�L�M�&�j�o�	�8�+�-S{��)���2��	�.��$��)	�X�W��"��_C{��{�T�a�p�^�5�LC{��bC	{����3�0�+�*�+�.S	{Ěc��0���t��R��y��F��%CN{Ϯn
���Z�.�-�<	��
�z�q�{�r�u�v���#�&/.�y�x���!�-��0�5�#�E�B�'�'�*�s�6�B���;�fC{��c1


�C@�BC*{ԁ�
�h�i�f�i�z�l�m�l�m
�@�U�q���n�u�t�|+�S�w	�t�u��n	�g�f�eC{ہ�&		)
S	|ү&��H�H�C��3��z�����,C|ܯ'�E�/�+��1�2�u�t��C��V�c�K�(��C|��v�H�I	�J
C|遭1S}��2�����'�K�������k�I�0�c��T���HC} �I�L���*�nS
}*��6�����z����=����3�_C}5�9��5��XS
}?�8��Z�Q����.�R
��X��1C }K�5��,��t�S�t�/��{�r���k��Y�T�9�V�h�1�H�x�
C}M��N���
�T�5�!�T	�W�X�>�{�z��Z�>�A�<��BC}Q��8
S}���6��O�S��P�s���b��E�2��T��M�{�����C!}��N��(�l�u�X�#�P�?��*�y�k�`��	�k�;��k�r�/�*��d�2
	C}���8�I	���;	�a��9�i�h�i�fC}G	S	~�1��2����Z��P��Y
Ci~&�N��{�	
5�3�N���w��<�S��t�8�Q�|��O�j�m�b�T�I
�)����
����Q�T�d���z�4�M�L�!��G�H�!� �b�_�?�6���?�6��K�-�x
���d�c�[�C~G��O�N�+
/-
C#~'��B�[�Z�,
�1)�0�1�0�1�0�+� �&�%�"�����
�
���C~-��L/�)	&	S	��>�<��j��i�6�w�r��b�S�
��c��O�O��.����g��d��O��N�������C��@
�Y�D���
�Q�P���3�2C
���n��|��*��|�}�|C���f	S	�o�F���6��;���>��9�IC+�y�Q�i�h�9�Q�$�O�4�;�@�x��z�Z��I�$�T�u�i�T�l�Y�z�G��'�N	����8C�{��j�

�
��~��~��~�/�.�
�/�.C����p7S�큙t���!�������5����F�����������}��C$��7��n�~�e���l��O�e�v�k�,�;�%��F��
�h��
�.
�s�v���q�A�j��>��8�C+�	�d 
�r�m		
C
��2�5�4��4/)	C=���y�~�	�
�~����z���{�z
��?�D��C�8�j�k�
�{�z�����}�t���M�L�z�K�@��	�u�O�N�v�u�v�u�p��	��}�p�oS
���M�r�X��\���&�M��0��eC��	S
���c�-��x�p��Q�	��\��]�WC�!�=�0�v��e�y�x��S	�2�>��7��6��-��q�\�w��NC
�G���*�^�I�7�9
�D�M�YC
�@�$�BC%�<���t��m�5�0�1�$�%�$�q�U�V�w�v�j�
�W�c�D�i�V���Y�I� �^�9�f�sS����+�R��?�k��~��u������(��-��,S	���E��%�?��0��k��j��	��6�JC	�ŋ_��R��k�p�$�e�^C��G�JC�Ё�	�S�ޱE��L��S��h��i��P��+�W� ��tC��)�m�
	�(�y�J�U��T�=�B�;�Z�xC�ꁚ�S��M�L��C�큮%3S�+�T��,�u�t��3��H��U��>��E�S����'�h��BC�@�O�J�
�C�<��9�a�`�C�=��/S
�O�c�Z��p��K��,��U��A��C�w�YWC�b��m�p�
;4C�Z�z�h��/�Q�~�US	���O�N��?��P�l�m��m�C7���[�O�@�*�_�2�w�h�*�!�@�b�I��N�k�V�!�^� �0�f�W�7�>��!�3�<�#��+�6�Y�\%�M�\�*�=d�y�ZC����5�K�d�������i
��+�*�K�M�f!�m�l�"�C����9	)0S	�_��;���E�P�)���g��?��$S	�i���������d��5C�u�n�$�A�4��	�C��W�,-�]�C
�s��=�M�z�3
�B��5�4��C�t��JS���`�Z��b��[�=��X�������	��C7�ė�r�/��m�v�;�>�,�s�W�(�Y���f��
�~�S�Z�;�~�T��#�w�r�S��h�i�P�i��t�r�CJ�a�w�T�v�_	
C�Ӂ�_���z�
�C	�P�G�J�K�G��u�n�
�H
�J
�K�TC�́�R%	S	���,��t�e����r��q��p�EC����PC���:C���a�(�b�c�b�ES
���-��z������w�,�{��r��F��?��CN���>���9�:�
��C�&�@�%�'�"?@��r�m�
�W� �6
�	�;�J��d�/�L�O�N��)�H�b�
��=�R
	
�5�6�C�'���
�	�3�,�Y��t��	�	�C���b%�G�H(��
�K�JaC.����|
�k�n�s�r�s�r�o�n�m�l�s�t�/�`�5�B�C�
�x'��(�%
�C"����m	
"
#	S
��X��^�"�s��m��l��m��|C���>,�Y�(��d�
�,T>��&C-�
�r
 


�,�7			
 
���CQ���#�n�o�a�n�o�\��
�a�l�Z�i�h�]�
�c�b�h�k��b
�U�l��
�3�=�t�v�h�c�b�]�t�}�|���d�[�x�z�y�\�e�d�_�^�_�^�i�h�c�b�a�`�]�	�b�o�n�k�*#��f�=�+S
�
���|��q��,�k���CC���=��	�U�V�g�)�z��)C��C���<�x�}�|	�m�n�o�j�k�j��m�j��k�j�S�U��C�w��c��h��9��&��!�� ��
�b��.�x��?����HC-�h�/��x�/j�b
S�Z�y�	�
�d�	�d��W�	�t�W� �a;�\�S�L��F�L�<��q�;�3�d�z��d�+��={�v�u�D�UCJ�k�3

				
	�.�7�$�

�^��u
C�����
* 
<�[S�Z�G�F�yC,�i��O�j�i0�h�[�r�d�e$�h�a�`�i�y�x�`�a�`�e�
��`
�a�`�a�`�c��C$�q��G#

 S��|��H��=��T��
�C��N���A�W�!C��S
���,���e��d����A��b��]��\C�*�Z�/�g�`�k��C�6��@�[�gC�/��y������#�j�I�#S	�P�.�f�P��b��u�X���I�IC4�[�
�!��6�G��n�8�0���#�V�J�9�
�:�[�>�r�g�6�>�r�u��d�u��-��4�i�!	�~��9��%�K�/��y�`�&C�]��d�j
�k�j�o�n	�K�J�M�|�N�M��P�s�r�OC�\��	S	�ڴ�3�� ��s��C��#���+��J���U�~�I�0�o�-�,�\�T�MC��&C�쁚c�h�i�S�:�7�L�Q�8�7�LS	��`����.����2�i��k�aCD�&�5�d�m�7�<�h�e�C�D�K�N�'�"�M���F�G�F�w��h��� �G�J�%�&�o�n�I�[�"�5�� �U�X�1�n�.��SC	�7�(G�(+�I�HC:�-���4�m��l�;�J��4�8�o�~�K�J�l�m�H��8�k�O�8
�u���l�9�4�1�G��A�.�o�A�.��H�T��~�A�<S
���A�E���/�R�V�g�nC	���H�D�%�5��r�
��]��^S
���u���?�a����
��w�X����I��RC!�Ε*�J�b�kd�
�(�u�t�_��0�CX�.����H�)
��+�>�<
� )�y�R�]��y�Q�.�	4�MC,�ʹ}�:�J�	
�r�qP�b
�c 
C8�ҁ�v�7�Q
��5�4���9�6�
�L�
�
���		��	��'�&��F�G�`�&�'�b
�G��	��E�D�E�D�M	�Q�^�0�=	�<�9�}C�ρ�<

e
	
 !S
�G� ��D��A��V����YC�_�|��C�U�*
C�T��1�V��?�>��^�a���7�Z
�D�#�"�G�F��]�\�S	������G�U�V��B�+��E�*C�ʒ�q�|�0�J�!�1�a��4
�P�UC���>�`�a�j�k
C���H�\�i��D�]�\	�C�@��"���G�T�
�f�#�E
�j�O�&�S�|S�%��!�����(��O���:�H�W�~��uS	�3�S��J�+�/�X��]�����#S
�>�X�[��&�v��I����7���C9�I�Z�[�^�A�9�&�$���]�^�M�L�	�c��`��J�w�L�C�*��S�7���j�#�8�}��|�!�"�A�\�1�K���\����G�&C�J��@�N�_�^�QK�R�?
�j�]�^
�u�N� �u�t�u�0�c�$C�O��|7
S	��d�J�N��V��_�i��CI��f�N�
��� �;	�z�J�O�`��
�
�b�e�u
�Z��@�=�Y�o�V�z�	�T��m�"�S�D�/
�*�[�x�h�-�6�i�`�Y�,K�8�?��E�#�&
�j�1�+0��B�0�M��8��4
��C��<�;�T�2�uCF��v�*�#
�T�G
�Z�r�O�D��e�-�jH%�>�G�>�?
�>�u�I�`
�a
�Z�[�d�aC�,�VC""
)			C�-��~i
'$NC9�/��0�?	�Z�Y�:�=�<�=�<�=�:�=�8�9�8�9�6�5�(�/	�$�%� �!���
����C�?��"!&	"	
S
�ֶ8��G��.��m�	����c�^�C��|��%��-%�"�KC��:�
�	
���
��zC�쁒}�t��6�7��
��a���,�b
�i�/
�&S
�B������0���'��_��t��mC:�N�L�}�~���u�{�\�	���	�G��5�f��*�w�B
��w�z
��G	�N��u�z	�+��{�Q�s���=�:�/��_�b�3�e�K�NC�O�zR�O�N$�K�JC2�M��M�������	��������z�w���{����	������)�vS���o���B��?�r��� ��)�#��n����L�$��G�tC�/�h�x�I�e�9�eC
�.�[
�
��z�C�3��W�4�5�4�_��m��6�'�9�^�9�o�&S	�i��x�c�6��A����E��$��+��~C�~�I
�e	�.��E�
"��_6�"�n�U��%��:��F��W�hC7�|�e
�*	�H�}	
�h�e�h�e
�r
�_	�V�W�L	�S�j�U��,�-C6�s��%�	�5�e�d�e�E�D
�+�
�$�#�H�I� �Y�A�	� �Y	�d�#�N�O� ��@�G�$�Y�I��L�M
���#
���L�M���L�L�C"�t��z

		
�yS������}����'�C�x�2���w�xC?����A�D�5�.�I�H6�� �7�B�U�K���)�,���
�<�D�q�4�1�c�L�A�B�u�p�;�h�%�f���>�)��}��R�$�k�TC&����Y���
�~���K�J�����4�`�"�S�R�O�N�)�(�)�V�P�U�T�1�0C����&>
S
���4���r��s��`�C���z�HC)���E�6�D�s�n�Z�U�+��B��A��o�(�g�|���-�8��O�]�&�I�I�y�r�K�H�K�$�A�|�+�1C���"�|�}�0�S�c�f�i
�t�uC����8#S	��)�&��Q��P��Q�	��$��iC;�
�R�C�5�v�5�4�!�B�=��"���!���4�5�~�E�D�6�]�$�q�r�!�K�l�`�g�W
��E�3�6��C$���\�B�C�F�G
�}�l�q�o�n�o�,�?�o�k�j�*�E��>�1�p�>�1	�0�r�=
�H�I��	C���?,
"
 S	�Ƹ������J�N����P�<�
C�ӌ��=�k�d�:��	A�|�`C�ܸC�Ё�R�K�H�I�y�j�T�A�.�9�H
��9�y�D	�M�6�1S	����L�y�Y�?���e��~C?��B�&�K��k�%� � ��K���$�\�1@�02�C�3�G
�h�5�Q	��6�q�>�1�Pm��Y�/�P�[�T�/�e�V���s)�f�-�^�-�
�.)�M�$�i�8�� �$N���D�C�w�j��pC�ܰ9�?�B
	�(Cs�!��H�G�J�c
�b�K5"�\�]�$�%�x�y	� �	�JF�<�			�j�e
�L�#�/A�0	�'

�p�q�l
�k�l�mC�9�3�&�7+�
�{�NC��8KZQ8
�"	RC�V��*�p�*�=
+$0C\���u�(�+D�&�%
��������{�x�y�x	�{B�:�;�j�i
�b�e�`
�a�`�a�^�_V	
�F�K�H�I�
���A�G
�F�E	�D
�E�B	CL���YC
?
L

S	����P�]���H��;��6�O��O�C�ɏ%�<��}��bC����c�\�c
�P
�Q

�H�3		
		C%����2�?�L�M�L�M�:�!��s��/�b�3�x�-�d�e�f	�]�(�~�)���$�A�.�)�8S
�!�4��^�M�����+�*�;�qC+�-�;�]�i�"�l�
��f	�&�`�Q%�Y'���k� �M�j
�K"�]$�=�b�I�]}�k�d�W)�_�k�2��d�t�-�}�|�8�
�M�ICb�.�0�F�?+,
�z�un			
�R�[�f�Y�T�_	)2
�`�e�2�/V�
��x�d�e�
�C�6�n
�
�i8�h# ��y#�xC�e��A�]7&�C?�1��g� �!� �!
���	�k�x�o
�n�m�h�e�d�a	�L	�M]�H�I�F�E!�@�E�>C&�=��C
{"

p	S
����\��`�a��u��x��c��<�aC� �O�
���/�`��6
�W�V�]�`����x�")(uC7��
���
��8�;�8�=�<�D�C
�A�B�<�=�G�n��}�|�q�L���F��-�K�	�[�7�f��(��g�N-�w&�dC���}
S
��Z�G��c��>��_,S����\��Y��"��6C�,�~S+�0�F-8	_N�?�$Cd5�&�oD�8�_",a�w�~��e�?nA5Z9:P�L�}/S�]�w�I3"P!�l�GjK�[�~�8�C��CC���	5qpq2Fw�S?	B!��*�MzC��Y�N�h�\��n��Y�5�#�Q�h;�>���^)N�"�s=2�ZC��LC��C��i4LCD�~�%�H�q�A���Q�R�{�e�
u��_�E�:^�C��l��t�q�0?��o�r�%�?�j��W�3�J�OG�b�E�3� �
G�N��Y�O�D��g�/�K�	��V�_�[
�*%�)�2�).��A�d��	��t�Z�
�>2�8
�t�C&�>�e�h�
$�,�I/�^@�
!�W#�	�`$�
(�}:�C&	�S�t+�;;��I�o�oC����Q�n�,Ew
P�>�L�x�72~
�B �5I�'$��'�I�!,S
	[�W�X
�(��h�ol�Q�;�z�)��f��V		(R��i�2W����B��9��E.�gO
�c�(�
_v�<.�TB�-0�,�|"]	e~2u9/�5s�R
�D("�Y#�l�$
�Z�f�
�(1�	�%�N�l�B7�=(�l{
�K�{�$�$�64��&�wB(2 �>
�L�#8-�a_�iJ�v4�q���I�w7�
���6��@��<�m��'\:8w
H��&�{�{"��?�m@93*G�w�)�<I�i�b�%$�<�.c�{�U�v+� H"8�JR�g�nz.�2�~�.
�p
�go�y0
�;�s
�p�Y�>�H��k"�K0!
�$
-'3�\-� `�	#RaF�E
�'�^ �>� (6�u �+"�h093@9
�d&B
Rv���{�2�*�9�/C,���Yb"�X�:�[X�M�iB�(
�*�Y�8�]�m�Yh���Q�S����,�6�A�I�|�t�g�g�f�8�I�*�,F	�}�0R>�xc ;>%e��!�$�*�;�A�G�X���0n��Fpc��s���F�#�*�K�P��n�a��*�U�y�	�*�$+R�J��
U��cN���� �9c_��hc�G!`	��!	��jH�v�a�r�n�2���~�0JK�7!�7�E
�&�5L�?���X�^����M�:���!�0�>�`�y�/��B	�&H�=�\�_��4�1�V�x��C�8�c�X6�e<�p(�w*�����%_�7J�E�2�v�7�o�*�_�{���'�7.�@�+�[Z�f�C�u^��+�!/�(�~�~|�u=�J���l�,�>�l^�~�I��-�B(�M^�Z�L�0R�BL�Q\�`S�h0�s�=�;��=�>�T�d�f)�og��?�j�p�\�s�d�f�
�� �>��\j�{
�u �$�	��.�!�%j�9�<��P�{�]�G�$v�>�A5�KV�\�g�u-�	��
�/��
��[�/Q�>�K�tx���g�-�1��U�~��r�Y�W�K��K�F�IP�Y��y�|�G��k�J�M�r�_#�hF�r�t�d����C�_��pT�~�m�3"�D�B�=��R�3�i(�m�@���1��E�J��bP�x"�~�:��+�*�0�7D�;�>>�F�H�`�i	�k�m>�{�~��G��0��
�T�;�so�{A�%��	�V�%�(��*����{�e�O�Tc 2�@�"	�(�I����$�c!V�9c
$��c�I	��+	�2� 	�]�h�v�.�-c!��,�*��p�x�C�B�.�a�L�K�b�q�|����� �#�(�*�3��]c�[�%c.��	c`��sh�|��k.���v���q�e��x�9�a�	��
�l�p�j�r	�~B�m)�v��a�u�C�X)�\�^�;�q��w������=a�G9�K��U�@�^�l�t��	��#U�(Q�,\�2�t�S�-�j�z�|��
�S�#�>�0�
�:U�?x�HK�O�K�m�:��4�t�"��8�-�@�H�Ky�Z�_S�m�
����
��9��L�(�1�2y�<�^�T��r=�x���E��� �g�0�f�Q�f�o��|��
1�&��(�)�p�O�v�m��}�h�1��<�^�E��Lr�S��Y|�^��+�Tc G��g�8�}�(	�_�Q�B�iZ�:�6�I�D�O��E��F�u�P	�l�wc�$���]��L!��T��Z4
��_��jB
��l��w����+��k�J��=���x�@��E�R��l���~&��P��_�D��k\��sE��z@��+���n���J��T���9�c��"��M!��R��
,��'���k��-��0b��:���I�(��_�F�����=e���|��R��V�"��p�'��q��H$������K���Y1��*���R	��y��m���
��8�����5B��?���'��T��Zk��H��k��o�>��X���F��'��)��,���.�l��K���<�W��u���D���(�� ��R2���M��M>��V@��^��d:��p�
���$��g=��P�����2,���#��D�`��i�"��@��BX�����$�6��X���G�[��S��V��e|��i�g��t��4�����)��)^��2���?�;��N��Ps��]T��g;��nu��*��L����F��.1��4L��B�3��Fq��L����[��.��*���^��*�!��8��h"��"��p{��yQ��d��	=��P���#���'��b��5�t��@C��k`��H�l�����i��x.��W��3��
$���������"N��'\��L	��N� ��`.��l��?��t��w�(����BW���
��E�%��R���dc�>Nt��W�U��hn��x��{p��	��0����
��+��%>��.	��1��3�g��N��Un��j��mZ��uI��}���
���L��!{��)j��3��6���B��E!��J�a��m��qo��|!���
��"�J��8��;��?&��F���Q{��\l��d��h�U��x�Y��	G��:�������+q��;��=�0��O��Q;��W��Y��_���n&��r��u�)��$��(1��/�6��?��A��D���V%��[�x��n�1��j��
������((��+��.��1/��6���D��F��J	��La��R���>���P��3G��8q��CX��H���R���jH��q�H���<��Q����p��"@��)��,�k��@�R��s�	��y��8����>��$���@��D�N��U���g�O��
�
����n��&��(-��1�Z��N��P9��U[��e\��pM��yI����R��
���E��$�A��A���NH��T���hg��q%��wI����W���
��-��0��3�|��V1��Z$��^#��a?��hJ��q��t0��}	���I�����#���9��->��3��6��;��>��A��C��F��I%��O��R3��[6��e�"��~2��
���W��$���,��/��2���DK��M1��V�q��z��}��K���&���� 3��&��)��.�D��L��Q=��W��Z;��`:��g{��l\��vC����L��
s	�5�[�a�]�k�e�c�g�_�_c��"	����0>�G@�Ic�_�c��/����Ac+O����%��z�P��U�p��PK
!<Z�o��<chrome/pdfjs/content/web/cmaps/UniJISX02132004-UTF32-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�UniJISX02132004-UTF32-HC!��bC �T�?�dC!�wS#��t&+"'�+�B�9S%�G(C%%�o/S	%@�uC &���z�wz!O�0�/
]��C'�C0��KS3��J�W�V�mC3�


��K?�C3�1$1
)')��q
C3�L


	&��vc%�9�7�=�;�A�?�j�]�`�U�X�w	�~�6�O	�c�]�H^����[c0�k�g�6�E�P�T�\�b�h�t�w�|�~����mc#��^�d�I�X	�M�XPK
!<��B��d�d1chrome/pdfjs/content/web/cmaps/UniKS-UCS2-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE����!a
 ^"�P�V�k��0�
��KA��GXM;>��O�A ��J�O��-	
�6	�3�4	�7�6�9�8�9�8�=�8�C�8�5�8A��Sa$��W�h�o�7��|��$�#�Amort�ui	�X��8�M
�N�O~�
�W	�N�
����'�m�q	�M	�C�+�*�-a
 �.�-���$�;�7�/�8�:a 5�n�lA!��w��T a!��@A!ҁC+]\]>6EYA!��pN2�B1(A!��fa"%��@�?�>�6A".�S�V8?3
+&A"7��dA"P�#!<'a"d��<�:�Ij"�a$`�]�;�!�Ca"f��)�!�)�+���,�+�&�(	��&�7�/�
�,��2
��Z�W�R.�DA%�Q%�	H3:%>)<'(q%�/��1���$�3��5���7��#�9���;� �%�=���?�!�A�&�C�a%��h
�%�$�e�iA%��m��b��s��J��a��b��I��D��c��h��W���^��i��>��=a%��fA&��&!�AA%��G�?<A&�a&`�^�b�]�`�a�o�}��|�el	x�/v+R�S
U�&h52�fe)�q��C�'���sa&o��&�7�6"��=	�a
	��~�|��_�}�z�T�Ud�9a0�j�\�r�
�U�T�x�q3��d��`�~�K�O	�U�Q�g��	�i�x�Q
3‚� �
r�-�D`q�lA3؂A3τc`A�LN�<�f�&�
��}�X���o�	�l��v��.�a�^�U�V��'�	�N�K�|�u�_�F�p�k�5��u�x�v��<�e�&�B�L�i�$�1l�4�1��/�@�&�o�:�5�P��>{�%�J�O�U��A�@�l�W��=�L�/�.��#�f���%�\�
���sS�6�O�0�Q�y���d��n��'�?�8�U�%��#�8�p�Y�f��!��%���r�@�/�u�!�b�(�m��P�)�$�G�F�S�4�B�"�Q�i��q�^�=��/�_�
�K��s�$�$�q�A�#�P�+�9�x�z��M�U��}�`�Z�g�~�g�M�N��|��o�V�
�I�D�!�hb�Q�6�Q�f�i��7�\�_
�j�_�x�B�"�L�o�9�A��]�{�b�3�N�U�a�0�s�>��w� �(�m��N�"�=�-�q�j��7�
�%��j�w�X�H�t�t�?�_�
�#�/�p��Q�i��D��Q�X�%���C�Y�^�,��2�	�+�C�b�%�t�S�4�D��F�~�G�L�'�M�x�c�4��P�Y�;�>�k�@�#	�v�Ah�8�E�U�b��%� �n�Z�'��k�D����6�k�|��v��u��n�?�^�&�+�O�b�{�f�n�]�o�D�#��6�4��	�v��L�M�S�
��p�P��L�W�D�U�8�5�T�y��
���;�;����q�C�\�
�`�|�A�A�_�R��L�?�*�<�m�-�|�,�J�E�d�%�/�T��]�h��*�+�O�T�n�1�:���o�j�n	�	�1�A��@�?�1�\�(�!�W��`�`�X�?�J�z]�x�1�z��Z�?��6�E��w���0�a�f�3�l�;
�G�P�e��a�J�� �.�)�)�F��-�Y�X�Y���B��7�,�A�M�p�q
�&��F��v�[�:�&�3�"�	�]�X���gJ��{�l��_�v�y��2�E�8�;�}�<��n��Y�f�r�v�s�x�0����PE�m�=�(��{��u�}�x�s�2�#�x	]��~�w�u��.��u�0��a�[�|�0�&�{�]�b�&�W��y
�s��a�2�q	���%�i����_��;�
�D��H�S�$�8�{
��0�F��&�1�P�	��	�^��;�p�s�
�o�\�;��y�d��e��m�P�a�5�H;���l�_�#�

�{�z�@�3�H�+����h�g�d�l���X�F�E�Q��-�G�G�b�E��,�+�z�2���g�>��
�_�0�7�Z�%��e�b�!�L�K��
�A�5��1�m�*�@�
�"��C��E��#��O�J�c�}�Q�N�J	�E�>��(�
��n�i��>�i��V�K��A��j�O�%�J�~�c�|�	��x�}�
�|�9�f�6�s�A��Y�A�"�2���k��"��S��&�R��p�l�!�N���<��i�D��E�f�a�o�M�g�J��]�S�Z�=�,�s�t�*��4�
�jp�=�/�l�/��[��Z�l��_�z�q�p�.�|�a	�e�TJ��=
�W�!�d�
�@�9N�'�,�?�G�:�1��C�	+�3���k���`�f�F�c�6�I�F�^�w�	���b�<�<�b�y��,�a�2�p�9�u2�d�A�(�t�s��I��s�"�m�P�}�T�	�z�X��1��R�?�5�>��d��X�@�S�S�b�:����j�n�I��]�O���?�!�|�7�V�-�I�#�^�Z��X�7�(��s�
��q�;�6�`�4�!��	�I�H��^�U�:�]�K(���_
�D�[��/�.����4�C�Q�S�t�r�
�w�o�d���B� �;�.�9�d�<�[�&�Y�T�o�y�&��6�q�R�I�0�;�D��na\��hA]�T47��s�wA2\��5�b�E�3�Y�$�?
�f�c!���r��i�b�,�Y�B�1
��M�b�F�*�q�z
�o�
�g�$�T�A�>	�:�0�e)	��w��{�x�{��%
�3��e�%�hA\�7�y�,�gZ�3m�R�3�|�k�B�
�w�T�M�_�U��;�,�[�4��k�
�^�fa^|�-A/^~�n�*�2B
�U�X�6�Q�=
�N�Y
��;��o�=�D�p��0
��$�_�{�n�S�k�>�k�9�
�-�B�)�,��W�@�&�>�D�� �?��yA*^��h�B�D�!�s�H��P�3�r�F�)�`�A+�z�!A�0�y�>%�y
�k�l�n��j�
o�`��e�Z�Q�
�f�	�}	�^�6�[A^ȻD�'��
�v
�m����x��=���&
�T�	�6�a_��ZA_��C	��s��U �LV�a�n�g9�r�?�4��r
L)��yA_��&�H
�w��a�$� �}t��l	�@�k��
�A�V
�w��%���g�l�=�$��	��	�FA_�
��,�U	�m,�0�Q���;�L���N�[�|�sr�i�u�1��aa�2Aa�:�2�O�0Aa
�6��NAa�]�?�1�0�W� �U���'��h�\�4�aaX�-A7ac�n�C�B�Z���0��	X
�B��|�?��^�#�V�?�o	�9�d�o�J	�v2�2�a>��f�#�'�!���F��f�Y�� =��S��P*
�h��Y�v�m�o�&�a)�Aab�CH�
w>#�A�Fy�
�|�6+TR`�ST�R<�U?�((<�}A4ap�
�yN�u\��b�:�4��9�1�*�u�Z.�%�'�S�"�II��&�K�D�-)��!��Q]
�1�2�A�L�f�|�
���|�q�)�$��9�8O�~�oAEa]�/%�(�s�N�j��8�t�o�l?)��|���d��9�V�[
�A�@�'�V+�i�
��=�DXY�{�8��n�Q�~�;#�7�x
��*
�	�zj!�[��z�X��j�=%/
�B�\3�o
�p��m�c�p�eadx�"A%d�� �?�$A�X�gV�Z�f�k�6��Q;�T�!�
�-�W�F	q�F��F�
��L���'��V
�n��c�jAIdz�.�P�'��x��e�F�3��y�`�J�F�
�O	���O�F�$�a�1�P�9�;��1�J�A�P
�q�y�J�@�+�	���?�7��?���C�3
�B�F�{�V�?�R�~�;�4�1�r��a	�4��V�3�X�N�C�w��|�+�%Ad�E6�<ae��Ae��b�C
�Ae��1�s�A�H�]�2�(�?	�S�b���9��W���'�p��(�v�Af
�D�Z�afC�gAfi�E�S�r�?�h�F�e
�
��
��]�s��7�a�.��<A
f_�i�G�23'� �!�#�A�:A/fI�I�1	��z�C�(�y�>X�y�)�p��;�L	���<�)x	��"�@�?�K�L��d��2	��9�p�� r�>�oX��Yag�sAg&�>�	�r��M����g�V�{
���Z�i�!�"�(�_�6�S�c�O�\�E�N��}Ag��pAg-�(�>�]����Y�B���R�o�b�w�?��ag��xAg��I���YI�0�1�,��@�vh�!*��^
�+�7D~�h�g
�`�|�=�%�t�\�1�6AEg��J�%
s�5�>�?� �`��q�N���Z�m�h�q�!
�
�6��|�Q���p��P�$�i
��x�E�]�*��M��0�Q�i�3�|�o�P�[�)��c�
�&�B�I�_�I�4�>�p
�
�P�9� ��B�mAhS�SK�aia�9Alic�{���I�4�s�3�^�]�^�H�e
�`�X�)�	� �M�h�U]�o�X�^�#�T�G�k��`�:�%�]�2�l�7�h�V�}��G�p�!�A�<�-�T�u�s�(�U�#�*
�y�`�U�Y�]� ��\�[�w�?��]
�*�_�A�
�k� �H�t�M���e�>���+�~�9�"�~�{}��:�9�7�L�X�_�z�G�o�h��k�_y�-�|�A	iw�
C�BL�p�!�:�P�u7,A
i��i�_0�(0��2�_�Tak��MA�kۥ(�6�m�j�m��+�}�0�;��K�d���
��O�6�G��x�-�&�K�P�?�t�m�"�Q�r�\�4���v��e�x�M� �e�p�Z�4�i�*�F�}�h�x�m�T�f�O�O�R�{�6��'�H�+�2�p�-�|��Z���3��:�I�~�)�s���\�8�mU�2�%�]�D�'��|��l�<��M�+�r�<�#�d�H�~���p�$�f����c��PS��^��W�V�,�#�
�C��L��j�9�T�:���t�G�6�m��)�p�&�y�i�|�(��k�0�������n�0�+�F�R�	�,�!�R��	���mh�j�h�o�g�B�i�6�G�K�>�V�>��z�e����(�h��3�-��\�s�	�|�s��K��z�J�o�B���t�t�S�c�J��L�	����1�[�O�>�x
�+�:�I�&�L�5�X�7�!�7�m�	�l�
��|�=���'�5��}	�T��(�W�`�^��I�r�s�N�S�p��w�N�Y�%�!�i�	�p�I�o�
�M��d�2��w��1��T�{��@�>���V�A�\�O��j�9�F�k�<���)�|�J�y
�q�b�S�v�*��b��g�@��'�n�e� �8�o�1���^�e�X�X�U�L�]�h�O�
�#�v�'�n�	�c�p�_���@�!�r�
�b�B	�#)�X����Q�
�'�N��)�5�P�$�/��x�6�W�V����u��[�-	��)aq��qAr}�6.��	WA:q��p5�S�J�&�X�
�	R� �y�k
��Y�
�K
�w�P�1�y.�8�W�F��`�a�b��|�V�%��S�hv�
$�{ �o�|��t	�7
��i��#
�(�7�$�N�u�!�B�	Aq��1'�3�d
�J�}�9%�9�<�s9�{&�0-�(�M(q��%as6�?A�os>�]�;��I
�x�C�Y��V�����E�l�;�a� `�6�J�C�^�� ��u�a�E�W�2�A�E�&�V�v�Q��pg�&���������(	�9�$�F�^��C�V� ��c	�r�R���N�B��u�t�Z�h�0��l��=�
�T��%��I�3�4�!�B	�\�
�i���z�i�8��C���*�m�a��� �H��F�s��!�y�\�!�=�x�n���y�0�]�%��h��,��#�L�y�b�c�3�e�(	�M��>�����'{z�$�<�C�B�2�
�m�H�y�&��K�M�j���R��P�J�A�>�_�z��r�U�F�M�'�S�R�S��h�B�U�f����P�[�>�W�8�=�,�@�c�|�8��+�Z�F�-�5�p�
�#�^��C�a�^�i�f�BG���^�	�h�C�B�C
�"�)��@�(�x���lav}�Av��t�f�E�]��'C+�N+�)�E1�A�d�%�^��
�*�U�l&�s�:!�+A.v���b�'�r�0�3�c�v�#� {�Z�a�6�O�S�r�sL�$�1�I�8���F�L�-�<�>�P�#���O"�V�Y�X�?��L�9�O���Av��c��?�C����g�,-�z�O
�R�W�v�^
�a��/�lz��V?�<�x
�r�(�)�Qax��GAx��3�P�5�w�b���:�G�,�r�%�
�$�a�1Ax��cAx˷
5�`ayG�Ay[��|�L�K,��+�u�
�3�G6�w�8�s��8*�z/�EJ�O�&�qAyP�F
�)�%�)�!�Z�i�3���W�y�I�.�n�-0�D�+�Q	�Q�>^��A)yI�2�9�8�9�t�1
�J�_��U�@�m�+Y�K�p�s,�+�4
�C�R�i��-�h�b�
F�(�#�h�%�H�}�m�>�G
E�(�Waz��UAGz��~�
�Y
�<�V�6�h�+��L�]�v���X�-��6�u�P�Q�%�,����A��A��<	��R�+�f�o�V�j�%
�
��o�
4�i�	�Z���/��,�T�.U�}�b	��)�)�B��,�%�l��2�P�H�.A(zķ�u��-�N� �-�R
�;��_�^�F�4��#
��{���&'�$�K=�!
�\�3��W�}�R2�k�e��3
�� �
"�A{�,�1�e�"�?:>�J�a}
�jA!}�!�)
�J
Q
�L��6�<�Y��9�V�!� �=��`0 �_���i?�V	�	�b�Y)]���[�<)��x(�u�<A`}
�6B�|�i��,�J�q�2�9�<�p�;�5�7��j�W�x�_�^��O�S�h�
�Q�r
�nT��B
��!&�T��'	�X�b�C�B�L�*"�'	��{�.��,�]�`��I�1�y��b�_�$	�O�O�p�{�8�	�%�e�1	\�^�=�d�v�>�q��i�n�e��C�Q�Y��y��*

�:�3�A
�0�Jg��0Al}�4�!���5�\��K�J�A�$�T�}f
�NN
�G�i�L�	��d�a�\���}�f�<
�?�u
�N�$�r�s�2�
�
�E�F�0�e�3
�N	�g��=�8���.��B�=�n�!�Z��m�x�w�w�.�~�b��}�j�g�3�(��q�p�]��G��W�&�8�x���R���?�:�5�l	�|�n��s�2�>�u�t�_
��&�	��MA~j��G�^"+�^�
Cja�P�
A�g�T�`��o�=�Z�a�s���F�L�
�[�`�u
�J�)�^���T
�;�"�3�i��o�l�(�~�,
�s�F�S�B��k�v�l�M��+�j��T�U�2�X�
�f�U�X�D�;�}���B�GG�@�u�@��a�J�B�\
��H��V�W��b�*�Q�d�T�o�>�b��^�Y�+�<��B��Q��j�=�.�+�I�R�w�N�t�!�/�J�!�o���[��Y��J	��;� �g�[�x�3�^�7
~�"��t��B�#�1� �s�;��5�J�;Y�& �c�
�?
�%��s�o�
�d�\�
�
�9H�~�W�x�
~��N���|�)�,�2�J�����z�h�^�#��A�Z�g�I�
�@�A�0�#�B�t�m�]��Z�W�P�#��=�R�c�O�(��W�/�P�1��s�0�S�~�q�o�B��t���7�8��j�c�p�	�.�X
�0�1�1��p�-�n�	�1��p�S�t�gI�
�K��%
�J�z�]�^�G�:	�}��v�g��V�1�S�h�
�D�U�:�R�c�z�o�^�c�`�\�I��8�-#�|��o����F��\�+��j�(�C��2�%�s�0�c� �w���3�f�9���~�9�1�A�Z�"�-�T�;�\�Y�-	�/
�B��"�#�	�h�%���t���[�<���A
�m�*�o�|�� ��P�E�G�0��\�6��T�.�'�\�
�.a�~�OA����3�Fk�p�I/
�$�*	�I�2�6�#�

�A����2)�O�9�X�:A���
Fa���uA�F����0	�1�U�4�� �S�H��m�%�f�_�J�q�J�<�4�9�z��.�p�7�P�0�&
�?�,�p��c�L��K�|�I�@�[�n�
��Z�t�3�,L�Y�9���M�a� ��\�,�!�/�4�i��l��3�Q�8�Y�z���z�J�m�_�I�(��M��2��G�D�c�i�E�K� �8��
�&�m�,��z�RC�r�\�i�/�4�K�\�/�B�"� ��(�P�s�a�>�>�.�q�<�g�)�o�
��L�	�\$�v�U��9�@�
��L�E�n�{��F�|�M�\�q��7�	�?�^�>���<��}	���
�Q�P'��I]��7�_�*��)�_��Z�
���X�#~�H�{��D�?�2� �y���@�	�5�J���u��t�p�w��T�3�]��|�A�O�L�f�Y�_�R��q��1��G�:�Q�r� �m�&��,�m�~����}M��{���
�`�
�^�w�2	�rc
��-�H�(��V�_�t�i�@�S�C��=�J�K�V
�Y�h�o�n�o�T�;�d�|�P�k�%�t�.�g
�,��,�o�'�.�w?	�V�%�O?�p�!��B�y�:�k�v��F���?�F�J��C�F���
������b�5�R�=��f�/��=�&8���x�2�=�
��j�	��x�#��
�t�q�N���!�w�0�X�z�p�a�s��6�Q�P�9�i�"�U�C�X�9� �$����(�x�<�'�o
�^�y�d�w�4�x�c��H�@�z�O�|��L�R�K�1�R��O�A�:�O�S�=�d�j�L�	�fq�f�k��}��F�9�M�F�K�P�m�K�\��.�K�[���v�V�C�W��a�r�t�y�W�"��D�_�J��0��d�
���U�:��$�s�{�j�(�]�k��V�s�E�:�~�1��	�z�K�4�@�Z�[�Y�;
����S��d�=�1��q�4�>�	�f�M�$�U
���K�D�q��a�L�X�"�
��p
�}�(�V��M��p�k�b�C���I�1�$��s�p�&�Z�p�M�H�n
�W��z�k�=�/�.��~���p�:�o�@���/�X�0�q�<�o�f�
�s�N�o�n�p�i�H�w�A�V
�{�}��T�8
�&��}�	�`�L�{a�Q�GA	�[�!�,�w�Y�u�\�0�y�Ha���]A
���+F
y�(,3�-p�'@�P�7�A����z�W4�~9�j�i�h�y�&�
�o�c�:
�'�vA$���s�	�a
�4	�W�
�k?��?�z���&
V
�Q�z�/�8�\�I�:�W��h��C�
�?����@�#a���tA�ǝ3A���*�z�]A���0�	�~�g��p�#�>�~�m�7�7�� �i�,�N�aa���SA���5�,[A]��U�M��j�%^�@��N�i�@�7
�#�B�-�r�m�R���A�y
�b	�I�<�;�(
�f��8�%��i�VS�H�K�N��Y�1�^�'
��z
��tk��3�uG�:+�[�3�0�m�/�2�c�B�c�f�i�4�,�
�6�u�
�(�a�d�_�z�A�3�1�Y�>�g�B�9�5�Q�l�*��&�oA1���
��	�y�B�#�N�� 
��!�#�Q�B+��v�%�Q�<��0�e�Y
�G
��
�\8�=�/�%�C��$�
�-�
�A�S
�`��E��Z�f�a��A[�+��p��W�i�y��;��=�=*�`��n�E+�^�S�s��hl�%w�>�S/��	�(�Y�-�'�h��2�Y�S�
���p�m	��5�
�^(��@�G�F
�4<��R�)�0"��l�sV��|�s�1�0�����
�0�%�B�a��v�u!�V�n�
�u�F�i%��x�
�S�7�(�?�@A
�1�kp>��V�~5�GV�`���d�d/r;,�	>A��g�c�\	�n	�Y�#	�pN��SU��tN�/D�]6�$�*��Ix�I=�c�T��=D�*�
�4
�uj�Uq�3��>�u�@�w�A�y�E�~�M��R��S��T��V��Y��[��\	��]�!�^�"�_�(�`�+�a�.�b�A�d�C�e�E�g�F�h�K�j�L�n�N�r�Q�s�T�t�[�v�\�y�b�|�c�}�e��l��m��p��r��u��x���	��
�	���
�
��������������������#��$��%� �+�!�.�"�1�#�9�$�;�&�A�(�C�)�F�*�M�,�N�-�O�.�U�/�X�0�[�1�c�2�d�3�l�5�n�6�p�:�t�=�u�>�v�@�{�B�}�C
��D��F��G�#�H�+�J�-�K�0�L�7�N�8�O�@�P�C�Q�F�R�Y�T�[�U�V�]�X�c�Z�d�[�e�\�k�]��_��`�
�b��c��e��f��h��i��l��m��n��o� �q�!�t�$�u�&�w�(�x�+�y�2�{�3�~�9�.�?��n��o��r��y��z���
���
�������������&��*��,��G��I��J��K��R��S� �T�#�X�%
�^�&�i�(�o�*��+��,��-��.��0��1��2�9�4�;�5�>�6�D�9�E�:�F�<�K�=
�R�>�]�@�c�B�e�C�h�D�o�F�q�G�x�H�{�I�~�J	��L��M�*�O�,�P�-�Q�.�R�/�S�3�V�4�W�5�X�8�Y�V�[�X�\�[�]�b�_�c�`�d�a�j�d�k�e�m�i�r�k�s�p�t�q�u�t�w�u�z�v��x��{��}�
�~�
�����2��:��;��>��?��C�
�D�
�I��K��N��U��V��\��^��a��h��j��l� �o�!�r�"�	�$��%��&��'��)��*��+��.��/
� �0"�+�1�N�2�Q�3�T�4�[�6�\�7�d�9�f�:�i�;�q�<�r�=�s�u�>�y�@�{�A�}�C��E��F��G��H��I�&�J�A�K�D�L�G�M	�N�O�X�Q�^�R�e�T�h�U�n�W�p�X�s�[�x�]�y�^�z�`�}�a�~�b��c��d��f��g��h��i�"�k�#�l�$�m�(�n�)�q�*�r�,�w�/�z�0���3��5��8��?��@�	6�F�
�}�
�~��������������
����
�������!��"�!�#
�$�$�/�&�5�'�8�(�O�*�Q�+�S�-�T�.�U�/�X�1�Y�2�Z�3�[�4�\�5�^�6�a�7�d�8�w�z�9�
�:��;��<��=��?��@�'�A�B�C�D�D�G�E�N�G�O�H�P�I�V�J�i�K�p�L��M�
�N�
�O��P��Q��R��S� �T�#�U�&�V�-�W�1�X�7�Z�9�[�;�]�<�^�A�`�B�a�C�b�I�P�c�d�e�f�f�h�h�o�j�p�n�u�p�w�q�z�r��t��w��z�	�{��|��~=���R��T��W��X��\�	�]��b��d��g��n��o��u�����+��-��0��<��B��I��\� �w�!2�z�"�-�$�/�%�2�&�8�(!�<�)�^�*�y�+�|�,��-��/!�	�0�+�2�-�3�/�5�6�7�7�8�?�9�B�:�E�;	�L�=�V�>�Y�?�\�@�c�B�d�C�e�D�k�F�m�G�p�H�w�J�x�N�{�R�}�S��T��V��Y��[
��\��]!��^�A�`�C�a�F�b�M�d�e�N�g�S�j�U�k�X�l�_�n�`�o�a�p�g�r�i�s�l�t�s�v�t�y�z�z�}�{�	�|�
�}����������� ��&��)��9��S�	�Z�
�]��`��g��h��i��o��r��u��}��~��������
�����������,� �3�!�N�#�P�$�S�%�Z�&�\�'�]�(�c�*�e�+�h�,�o�.�p�/�q�0�w�2�y�3�|�4��6��7��9��;�$�=�&�>�)�?�0�A�1�B�2�C�8�E�:�F�;�K�@�M�A�N�B�P�D�Q�E�T�G�U�J�V�Q�X�R�\�W�^�]�_!�i�`��b�
�c��d��e��g��h��j��m��n�!�o�(�q�)�t�/�v�1�w	�4�x�>�{�?�|�C�}�^��_��b��c��h��i��j��p��s�	!���$��'�
�*��2��3��4��:��=��@��H��I��Q��R��T��Y� �Z�!�[�"�^�#�_�%�b�&�e�'�m�(�n�)�v�*��+��,��-�*�.�-�/�0�0�7�1�9�2�A�3�D�4�G�5�N�6#�P�7�t�9�v�:�;�x�<�y�=�~�?��B��C��D��I��N�	�P�
�Q��R��S��U��V��W��Y��\� �]�"�_�$�`%�0�a�V�c�X�d�Z�f�[�g�`�i�a�j�b�l�g�n�i�o�k�q�r�s�s�v�y�x�{�y�~�z��{��~�
������&��'��*�-��1��2�	�3�
�9��<��K�
�R��e��l��n��q�	�x���������� ��%� �&�!�'�"�*�$�+�%
�2�&�=�'�D�(�_�*�a�+�d�,�p�-�v�.�y�/�|�0��1��2��3��5��6��7��9#��:�=�<�?�=�B�>�C�?�H�A�I�B�J�E�N�G�P�H�S�I�T�J�Y�L�Z�O�_�R�a�S�d�T�k�V�l�Y
�r�[&��\�'�^�)�_�+�a�2�b�4�e�:�M�f�N�g
�T�i�b�k!�c�n��p��q�
�r��t=��u�R�v�m�w��x��z�	�{��|��}��~�;�4��p����
��
���%���=��?�	�B�
�I��J�
�K��Q��R��T��Y��Z��]��_��a��d� �k�"�l�%�r�'�t�(�w�)�~�+��,��-��.�	�/��0��1��2��7��9� �;�$�=�%�@�)�A�*�C�,�D�/�E�6�G�7�J�=�L�?�M�B�N�I�P�J�S�P�T�S�U�V�V�b�W�h�Z�i�[�l�\�m�]�r�_�s�`�t�a�w�b�y�d�{�e�~�f�
�g��h��i��j��k��m�&�n�)�o�,�p�3�r�4�s�<�u�>�v�A�w�H�y�I�z�J�{�P�}�R�~�T��[��\��]��^��_��`��s�	�z��|���
���
��������������$��*��1��3��4��:��<� �?�"�E�$�F�%!�G�&�i�(�k�)�m�+�s�.�t�/�u�0�y�1�z�3�{�5�~�6��8��:��=��>��?��A��C!�5�D�W�F�Y�G�\�H�]�I�b�K�d�M�j�N�m�O2�p�P�#�Q�:�S�<�T�>�V�?�W�D�Y�G�Z�M�\�O�V�]�^�^�e�_�x�`��a��b��c	��e��f�1�h�3�i�6�j�=�l�@�m�F�n�Y�o�`�p�{�q+�~�r�*�s�0�u�2�v�5�w�6�x�y	�:�{�D�|�G�}�J�~
�Q��\��^��a��h��i��j��p�	�r��s��w��x��{��|��~������	� ��"��#��$��%��'��(��)��*� �,�#�-�&�.	�.�/�8�1�:�3�;�7�@�>�A�?�B�@�C�B�E�C�H�D�O�F�P�G�Q�H�W�K�X�L�[�M�\�O�S�`�U�c�Y�f�Z�i�[�p�]�q�_�x�a�z�b�}�e�~�f��i��j��k��l��n�	�o��p��r��u��w
��x�'�y�)�z�*�{�0�}�2�~�5��<��=��>��D��F��I��P�	�Q�
�R��X�
�Z��]��b��c��d��j��l��o��v��x��~��� ��!�
�#�
��$��&��'��(��*� �+�!�,�'�.�)�/�,�0�3�2�4�3�5�4�6�5�:�7�<�8�?�9�D�:�E�<�F�=�G�E�J�F�M�G�T�H�V�I�^�K�`�L�c�O�g�R�h�V�k�W�l�Y�n�Z�o�]�p�^�u�`�v�d�{�f�}�g��h��j��m��o��p��q��r��s��t�#�u�&�v�)�w�<�y�>�z�A�{�B�|�G�~�H��I��N��P��S��Z��[��\�	�b�
�e��h��o��q��w����������������"��(� �0�!�1�"�2�#�8�$�K�&�Q�'�T�(�W�)�^�+�_�,�`�-�f�/�h�0�x�1�~�3��4��7��9�	�:�
�;��<�#�=�*�>�E�@�G�A�J�B�Q�D�R�E�Z�F�]�G�`�H
�g�I�r�K�t�L�w�M�~�O��P!��Q�"�S�$�T�&�V�'�W�,�Y�-�Z�.�\�0�^�1�`�3�a�4�b�5�c�7�d�;�f�<�i�B�k�D�l�G�m�N�o�O�r�U�s�X�t!�h�u�
�w��x��y��{��~���1��7�"�J��m��o��r��y��z�	�{�
�|���
����
��#��+��2��5��8��?��^��d��f��i��p��s��y�!��!�.�"�I�#�d�$�s�%�u�&�v��'��)��*��+�$�-�'�/�+�2�-�3�.�4�/�5�6�7�7�;�<�=�>�>�A�?�H�A�I�D�O�E�R�F�S�G�T�H�[�I!�_�J��L��M��N�
�P��S��U��V��W� �Y�!�Z�"�[�(�\�+�]�:�^�A�_�D�S�`�T�a�Z�c�\�d�_�e�f�g�g�h�h�i�n�j�q�k�t�l!��m�"�n�%�o�(�p�/�r�0�s�1�t�7�u
�F�v�Q�x�S�y�V�z�]�|�^�}�_�~�e��x������������&��'��(�*�	�.�
�1��4��;�
�?��E��G��J��Q��R�!�S��u��w��y������� ��"�	�#��$��&��'��(��*��+� �,�'�.�(�1�.�3!�A�4�c�6�e�7�g�9�n�;�o�>�u�@�w�A�z�B���D���E���F��	�G���H���I���K���N���O��8�Q��:�R��=�S��D�U��E�V��F�W��L�Y��N�Z��Q�[��X�\��\�]��b�^��v�_��|�`���a���b��1�d��3�e��6�f��=�h��>�i��?�j��E�k��H�l��K�m��W�n��]�o��q�p��w�r��y�s��|�t���v���w���x���y���z���{
���|��#�~��%���(���/�!��2���T���V���Y���`�	��a�
��b���h�
��j���m���s���t���z���|������������
���!��!� ��C�"��E�#��H�$��I�%��N�'��O�*��U�,��W�-��Z�.��a�0��b�1��c�2��i�3��l�4��{�5���6���7���9���:��!�;��(�=��)�>��*�?��.�@��/�A��2�B��I�C��d�D��g��n�E��u�F��v�G��|�H���J���K���L��#�N��$�O��%�P��+�Q��>�R��E�S��`�U��b�V��e�W��l�Y��o�Z��u�[��x�\��{�]���^���_���a���b���d���e���g���h���i��"�j��%�k	��,�m��6�o��8�p��;�q��B�s��C�t��D�u��J�x��K�y��N�z��O�{��T�}��U���X���Z���\���_���f���g�
5��m���#���%���(���/���0���6���8���;���B���C���D���J���M���P� ��W�"��Y�$��_�%��f�&��n�'��o�(��w�*��y�+��|�,���.���/���0���1!���2��A�3��D�4��[�5��^�6��a�7��i�8��j�9��r�;��t�<��v�>��w�?��|�A��}�B��~�C���D!���E��:�F��=�G��@�H��G�I��I�J��Q�K��T�L��W�M��^�N��`�O��a��c�P��g�Q��j�R��m�S��t�U#��u�V���X���Y���Z��%�\��&�]��'�^��-�`��/�a��2�b��6�c��8�e��9�f��:�g��@�i��B�j��E�k��L�m��M�p��S�q!��g�r��	�t���u���v���w���y���z���{���}���~��!���(���)���*���0���2���5���<�	��=���C�
��F���I�	��Q���[���]���`���d���f���g���h���k���m���o�	��r���|���}� ���"
���#���$���%���'���(���)��'�*��(�+��)�,��/�-��2�.��5�/��=�0��>�1��F�3��H�4��K�5��O�6��Q�7��S�8��T�9��Z�:��]�;��`�<��g�=��k�>��q�@��s�A��v�B���C���E��
�F��
�G���I���J���K���M���N��!�O��(�P��*�Q��+�R��1�T��3�U��4�Y��:�[��;�\��<�]��?�^��A�_��D�`��G�a��N�c��Q�d��W�f��Y�g��\�h��c�j��d�k��eQ
���m�\��.��FV�]�L�d�5q�
��(��"�*�/�3�6�9�C�H��!�%�,�0�7�=�A�F�T�V�[�`�|�Q�Z�^�m�~�{h3�F�N�s�K�F�:��O�3�f�s�t��;��6�P�9�qO�u�I�M�S�W�Z�?�\�_�a�e�g�o�@�q�A�t�B�w�z�y�{�~�/��T��C�F����G�
�g������%�G�T�K�(�L�P�S�4�k�a�W�c�e�h�j�l�X�n�s�w�0�{���
�>��[������\�� �$�*�-Q���g�l�wN�Y�h��K�V�$��>�i�x�o�*�Ta	�:�p �Dq���B�e��CPK
!<�e�Ʋ�1chrome/pdfjs/content/web/cmaps/UniKS-UCS2-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�UniKS-UCS2-Ha �{�}�z�[�x	�����k��
��������~��PK
!<�W���f�f2chrome/pdfjs/content/web/cmaps/UniKS-UTF16-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE���������!A��
wa�r�YM�Q
��t��J�v�O�e�z�?AZƅ	�1�:�6	�g�r�3�4	�7�6�9�8�9�8�=�8�C�8�5�8�_�s�#�lO@�>��P
y8�`
)�]YX�	�l�
�|�kf6�8m+]\]>6EYT(�V8?3
+&C	IW"V�m�`A-��S�^K�b3�h�I9�?�3�V �N2�B1(�C�d�Z!<'�g
���z�_I�o�b
/A
 5�n�)�
Q%�	H3:%>)<'(A8% �(F�i
���jebm��	5�&!�A �A!/�<�!@:B�z�;�v�(��o�F�y�:�)�&�}�=�0�l����U�:�3�w�D��Q�F�i�4A%�� V�)��-��_?�
�&H"5@Q/B�h�W��4��X�'�6�+�L�_A	/O�V�j�I�J�w�N�Q�� Q
/Z�a��V��
�y�G�j�'�p�d�4�bA/i��]F�>�Q�P�C�"��v��G�fA/u�tA/j�0��gQ
/�y�A�`� �;�j�J����A/��<�U�RA/���,�o��X��9A/��L_�)�Q���Y�~}�g�"�#�H�YK�.�+��"�%Q
/��q�6�E�
�~�Y�X�c�Y)Q
/���x�(�y
��~�=�pA0l��g�h�7�6b��A/Ν6�F�i�'�SA
/ʹf,�?�x
;:�E�h�4�=V�|�9	��Q
3‚� �
r�-�D`q�lA3؂A3τc`A�|N�<�f�&�
��}�X���o�	�l��v��.�a�^�U�V��'�	�N�K�|�u�_�F�p�k�5��u�x�v��<�e�&�B�L�i�$�1l�4�1��/�@�&�o�:�5�P��>{�%�J�O�U��A�@�l�W��=�L�/�.��#�f���%�\�
���sS�6�O�0�Q�y���d��n��'�?�8�U�%��#�8�p�Y�f��!��%���r�@�/�u�!�b�(�m��P�)�$�G�F�S�4�B�"�Q�i��q�^�=��/�_�
�K��s�$�$�q�A�#�P�+�9�x�z��M�U��}�`�Z�g�~�g�M�N��|��o�V�
�I�D�!�hb�Q�6�Q�f�i��7�\�_
�j�_�x�B�"�L�o�9�A��]�{�b�3�N�U�a�0�s�>��w� �(�m��N�"�=�-�q�j��7�
�%��j�w�X�H�t�t�?�_�
�#�/�p��Q�i��D��Q�X�%���C�Y�^�,��2�	�+�C�b�%�t�S�4�D��F�~�G�L�'�M�x�c�4��P�Y�;�>�k�@�#	�v�Ah�8�E�U�b��%� �n�Z�'��k�D����6�k�|��v��u��n�?�^�&�+�O�b�{�f�n�]�o�D�#��6�4��	�v��L�M�S�
��p�P��L�W�D�U�8�5�T�y��
���;�;����q�C�\�
�`�|�A�A�_�R��L�?�*�<�m�-�|�,�J�E�d�%�/�T��]�h��*�+�O�T�n�1�:���o�j�n	�	�1�A��@�?�1�\�(�!�W��`�`�X�?�J�z]�x�1�z��Z�?��6�E��w���0�a�f�3�l�;
�G�P�e��a�J�� �.�)�)�F��-�Y�X�Y���B��7�,�A�M�p�q
�&��F��v�[�:�&�3�"�	�]�X���gJ��{�l��_�v�y��2�E�8�;�}�<��n��Y�f�r�v�s�x�0����PE�m�=�(��{��u�}�x�s�2�#�x	]��~�w�u��.��u�0��a�[�|�0�&�{�]�b�&�W��y
�s��a�2�q	���%�i����_��;�
�D��H�S�$�8�{
��0�F��&�1�P�	��	�^��;�p�s�
�o�\�;��y�d��e��m�P�a�5�H;���l�_�#�

�{�z�@�3�H�+����h�g�d�l���X�F�E�Q��-�G�G�b�E��,�+�z�2���g�>��
�_�0�7�Z�%��e�b�!�L�K��
�A�5��1�m�*�@�
�"��C��E��#��O�J�c�}�Q�N�J	�E�>��(�
��n�i��>�i��V�K��A��j�O�%�J�~�c�|�	��x�}�
�|�9�f�6�s�A��Y�A�"�2���k��"��S��&�R��p�l�!�N���<��i�D��E�f�a�o�M�g�J��]�S�Z�=�,�s�t�*��4�
�jp�=�/�l�/��[��Z�l��_�z�q�p�.�|�a	�e�TJ��=
�W�!�d�
�@�9N�'�,�?�G�:�1��C�	+�3���k���`�f�F�c�6�I�F�^�w�	���b�<�<�b�y��,�a�2�p�9�u2�d�A�(�t�s��I��s�"�m�P�}�T�	�z�X��1��R�?�5�>��d��X�@�S�S�b�:����j�n�I��]�O���?�!�|�7�V�-�I�#�^�Z��X�7�(��s�
��q�;�6�`�4�!��	�I�H��^�U�:�]�K(���_
�D�[��/�.����4�C�Q�S�t�r�
�w�o�d���B� �;�.�9�d�<�[�&�Y�T�o�y�&��6�q�R�I�0�;�D��n�D��
�,	�W�E�3�Y�<���>
�f�c!��{�j�r����b�,�Y�B�1
��M�b�F�*�q�z�`�3��
�g�p�M�=��r�5�H��L�
��/�^�t�[�.�M�_�U�[�X�E�w��`�]�x�{��[�)�\���Q	�d�Q�e�:�/� �U�h�H��f�A�@��2B��B�!�X�6�Q�"�a�>�s��Y����F���o�=�D��3�y�j�n�]�0��)�`�K�H�%�_�p�1+���!��r�7�0���i�H�>�u�<��9�>�k�9���S���l�n��-�7�^�?�
�C�R�^�q�p��5�N�b��Z�I�W�@�&�>�D�N�c��$��d�%�
�'�,�v�u�b���6�[�N
�	�!�L�	�H��f�e��r�k���$�K�U�>�}t	�8�'�T�q�l�}�~�C�\�;�L�]�5�n�g���}�A�V�N�N�[�=��%��<�s��X�g�3�F���r�D�A�9	���$�~��f����u�	�|�7�i�b�(�?�1�0�W�5�2� �U��~���'�[�Z�h�+��4��u�%�5�+�C�H���9�l�y����|�/�u��G��v�t�o	��o�	�8�a�h�8�%�=��|�?�`�+��A�Q�.�Q�L���S�p��?�o�f�$�1�*�u�Z�+�B��_�o�@�[� �V��:�E�j�\�8�a� �u��B�V�}�%�Z��&�K���e�h�7�-�N��=�DXY�{�g�f�6�Y�K��f�f+�a�'�!�:�A�.�/�{�V�~�
�a�<�w�h�	�f�Y�&��t�i�o��x
��e��
�	�z�=�4�p�3�L����M*�|�|��%�*�G�\�I�Y�v�
�X��5��u�6�S�.�/�L�R�w�q�(�S�u�"���9�8�'�V�~��"�r	�5�$��m�c�p�Q�j
�q
��l�'���$�\��e�F�3��y�M�,�U��F�O�.�Y�Z�f�D�1�6��o�l�?�n�F�@��a�1�P�9�;�Y�!�D�+�j�1�i�2��F��P�E�n��E�J��N�@�+��x�3�
�B�A�B�?�7��?�[�\�B�[�C�3�g��'�2�F�{�}�R�?�R�~�;�4��\�r��a�q�$��V��c�B�C��1�~�C�w��|�+�%���a�l�A�R��]�*�y�A�h�?�f�;�b���9��W��x�[�'�p��(�v��~�1	��z�C�(���>X�y�)�U�D�E�U��z�;�W�V�L	���x��<��7�(x�]�e�@��9�Z�@�?�y�w�
��	�]�0���~��#�T	��i�.�g�a�4���� r�>�oX�}�A�:��u�<�\��	�r��M���>�]����,��V�{��Y�B���+��Z
�2��!�"�(�~�b�C�6� �u�J�/�O�\�E��E�������E��%
s�5�O��?� �`�?�YI��:�q�N���Z�m�h�q�!	�+�4�6��|�9���!�:�{�v�j�p�u�^�`�c�P�$��^�4	�a�l�x��X��s�*��M��0�[~��i�3
���o��g�N�[�3��
�=�J�q�����^�B�I�_�I�4�>�p
�
�P�9� �
�x�B�m�����I�4�s�3�.�Q�]�^�H�e��=�X�)�:�E� �M�0�I�U]�o�X�^�#�T�G�k��`�(�o�%�]�2�l�7�h�P�{�}��G�p�!�A�<�-�T	�V�M�s�(�U�#�*�@�;�`�U�x�S�]� ��\�8��w�?��]	�P�'��w�A�D�Y�X�]�i�r�S�H�>�K�M���e�>���+�~�9�"�~�|�y}��:�9�7�L�X�_�z�G�o�h�p�u�k�_y�-�|��I�6�m�j�m��+�}�0�;��K�d���
��O�6�G��x�-�&�K�P�?�t�m�"�Q�r�\�4���v��e�x�M� �e�p�Z�4�i�*�F�}�h�x�m�T�f�O�O�R�{�6��'�H�+�2�p�-�|��Z���3��:�I�~�)�s���\�8�mU�2�%�]�D�'��|��l�<��M�+�r�<�#�d�H�~���p�$�f����c��PS��^��W�V�,�#�
�C��L��j�9�T�:���t�G�6�m��)�p�&�y�i�|�(��k�0�������n�0�+�F�R�	�,�!�R��	���mh�j�h�o�g�B�i�6�G�K�>�V�>��z�e����(�h��3�-��\�s�	�|�s��K��z�J�o�B���t�t�S�c�J��L�	����1�[�O�>�x
�+�:�I�&�L�5�X�7�!�7�m�	�l�
��|�=���'�5��}	�T��(�W�`�^��I�r�s�N�S�p��w�N�Y�%�!�i�	�p�I�o�
�M��d�2��w��1��T�{��@�>���V�A�\�O��j�9�F�k�<���)�|�J�y
�q�b�S�v�*��b��g�@��'�n�e� �8�o�1���^�e�X�X�U�L�]�h�O�
�#�v�'�n�	�c�p�_���@�!�r�
�b�B	�#)�X����Q�
�'�N��)�5�P�$�/��x�6�W�V����u��[�-	��)��5�S�J�&�X�
�	R� �
��h�U���Y�
�P�9	�e
�w�P�N��y.�8�W�F�X�k�`�a�b�9�{��|�V�%��S�hv��l�u�*�{ �o�4�y�>��A�4	�7�
�w�p�I�^���-�*��\��$�N�u�!�B�	�(�;��I
�x�C�Y��V�����E�l�;�a� `�6�J�C�^�� ��u�a�E�W�2�A�E�&�V�v�Q��pg�&���������(	�9�$�F�^��C�V� ��c	�r�R���N�B��u�t�Z�h�0��l��=�
�T��%��I�3�4�!�B	�\�
�i���z�i�8��C���*�m�a��� �H��F�s��!�y�\�!�=�x�n���y�0�]�%��h��,��#�L�y�b�c�3�e�(	�M��>�����'{z�$�<�C�B�2�
�m�H�y�&��K�M�j���R��P�J�A�>�_�z��r�U�F�M�'�S�R�S��h�B�U�f����P�[�>�W�8�=�,�@�c�|�8��+�Z�F�-�5�p�
�#�^��C�a�^�i�f�BG���^�	�h�C�B�C
�"�)��@�(�x���l�6�_�v���Z�C��+�b�'�r�0�@��y�l�O�p��2�%�f�#� {��/�a�6�O�S�p��9�f�#�t
�m�B�P�C�8�^
�a�k��:�@
�'�E�`����F�L���d�,�S�^�l�?�#�`�l�V�5�*���v���:�<�x�C�4��;	�n
�B
���b�?�k��E�L�9�O��i�d��g�^��5���b���:�G�,���%�
�$�a�1�T�Y��8�9���1��%��l�_�O�L�K�`�U�u�Z�i�@�u�=�+�|��w�`�G��y�I�w�L��:�Q��p�s��@�+�4�{�)�^�R�i��-�h�i�J�
F�(�o�J�s��8*�z�x�%�=�Q	�Q�>^����iJ�O�|�>��N�
	�F�(�W�Q�
�Y	�r�s�<�>�i�6�N�g�+�b�N��L�]�v����-��5�-�P�R�C�6�N�E�P��U�%��m���d�F�)�	�Y��A��<	��,�#�9�+�f���V�2�I�.��&��3�l�$�K�9
��o�
�
�\�]���_�_�t���/�^�}�S�$�y�T�.U�}�b�r�e�	��3���A�B�j�x�y�,�Z��l��2�P�0�i��U���CB�|�i��4�9�,�J�q�2�X����<�p�6�s�#�p��K�e� 	�s
�L�l�B�U��$�O�"�{��
�U�<�|�m�k�$��h	��B�M�|�Ni�7�S�T��g��`�3�r
�n�2�_�|��r�1���}�=�"�E�
�?�u
�N�K�n�r�s�2��+��E�F�I�x�e�=�b�C�f
�N�
�$�Y�*��y�!�F�=�k�"	�o�R��[���.��,�h�{�<�]�P�x�!�{�T�U��I� �S�y��r�S�@�5�T�$�c���`�A�/��w��.�-�8�	�j	�s�b���}�j�c�z�3�S��z�-�m��;�#��h�'��]��!�(�v�>�q�F�_�i�B�U�|�&�8�E�Bx�/�C�r��1�Y	�Z��7���7�?�D���v��O�5�l�=�8�9�O�t��s���\�.�q�8�>�u�t�K�j
��/�<�Pg�*�	��W��Y�)��o�=�Z�a�s���F�L�
�[�`�u
�J�)�^���T
�;�"�3�i��o�l�(�~�,
�s�F�S�B��k�v�l�M��+�j��T�U�2�X�
�f�U�X�D�;�}���B�GG�@�u�@��a�J�B�\
��H��V�W��b�*�Q�d�T�o�>�b��^�Y�+�<��B��Q��j�=�.�+�I�R�w�N�t�!�/�J�!�o���[��Y��J	��;� �g�[�x�3�^�7
~�"��t��B�#�1� �s�;��5�J�;Y�& �c�
�?
�%��s�o�
�d�\�
�
�9H�~�W�x�
~��N���|�)�,�2�J�����z�h�^�#��A�Z�g�I�
�@�A�0�#�B�t�m�]��Z�W�P�#��=�R�c�O�(��W�/�P�1��s�0�S�~�q�o�B��t���7�8��j�c�p�	�.�X
�0�1�1��p�-�n�	�1��p�S�t�gI�
�K��%
�J�z�]�^�G�:	�}��v�g��V�1�S�h�
�D�U�:�R�c�z�o�^�c�`�\�I��8�-#�|��o����F��\�+��j�(�C��2�%�s�0�c� �w���3�f�9���~�9�1�A�Z�"�-�T�;�\�Y�-	�/
�B��"�#�	�h�%���t���[�<���A
�m�*�o�|�� ��P�E�G�0��\�6��T�.�'�\�
�.	��Q�3�F�L�9�p�d�/�T���i�*��X�'�2�,�w�#�

��!�0	�1�U�4�� �S�H��m�%�f�_�J�q�J�<�4�9�z��.�p�7�P�0�&
�?�,�p��c�L��K�|�I�@�[�n�
��Z�t�3�,L�Y�9���M�a� ��\�,�!�/�4�i��l��3�Q�8�Y�z���z�J�m�_�I�(��M��2��G�D�c�i�E�K� �8��
�&�m�,��z�RC�r�\�i�/�4�K�\�/�B�"� ��(�P�s�a�>�>�.�q�<�g�)�o�
��L�	�\$�v�U��9�@�
��L�E�n�{��F�|�M�\�q��7�	�?�^�>���<��}	���
�Q�P'��I]��7�_�*��)�_��Z�
���X�#~�H�{��D�?�2� �y���@�	�5�J���u��t�p�w��T�3�]��|�A�O�L�f�Y�_�R��q��1��G�:�Q�r� �m�&��,�m�~����}M��{���
�`�
�^�w�2	�rc
��-�H�(��V�_�t�i�@�S�C��=�J�K�V
�Y�h�o�n�o�T�;�d�|�P�k�%�t�.�g
�,��,�o�'�.�w?	�V�%�O?�p�!��B�y�:�k�v��F���?�F�J��C�F���
������b�5�R�=��f�/��=�&8���x�2�=�
��j�	��x�#��
�t�q�N���!�w�0�X�z�p�a�s��6�Q�P�9�i�"�U�C�X�9� �$����(�x�<�'�o
�^�y�d�w�4�x�c��H�@�z�O�|��L�R�K�1�R��O�A�:�O�S�=�d�j�L�	�fq�f�k��}��F�9�M�F�K�P�m�K�\��.�K�[���v�V�C�W��a�r�t�y�W�"��D�_�J��0��d�
���U�:��$�s�{�j�(�]�k��V�s�E�:�~�1��	�z�K�4�@�Z�[�Y�;
����S��d�=�1��q�4�>�	�f�M�$�U
���K�D�q��a�L�X�"�
��p
�}�(�V��M��p�k�b�C���I�1�$��s�p�&�Z�p�M�H�n
�W��z�k�=�/�.��~���p�:�o�@���/�X�0�q�<�o�f�
�s�N�o�n�p�i�H�w�A�V
�{�}��T�8
�&��}�	�`�L�{�O!�,�w�Y�u�\�0�y�H�i�L�
y�>�G�Q�x�|	�a
�4	�W�K�B�q��/9�j�B��?�	����&�/�y�&��p
�Q�z�/�8�\�I�:�W�}�h�h�C�c�2�y�4�
�/�'���O�7�d��G��#������g��p�#�>���m�7�7�� �i�,�N�a�S�]�L�M��j�%^�@�`�C�T�	�y��i�@�7�4�Y�B�-�r�(��R�����A�y�X�w���I�>��;�(�s�r�l�Q�7�x��8�%��r�v�%�Q�a��GS�H�K�N��Y�1�0�S�'�(�I�b�i
��*�7k��"�W�b�Y�J�\8�=�3���}�3�0�m�/
�R�!�c�B�c�<�W�i�b�/�,�|��6�@�7�
�(�a��9�_�z��$>�Y�X�S�9��s�>�,�E�9�B�9��K�Q�=�(��]
�B�c
�F�!�o�J��p�@�-�W�^�}�j�U�z�u��8�u��x�e=�=��9�d�q�n�E+�^�S�6�
�%��"�;l�%	�d�]�>�S�z�L
�5�	�(�Y�
�;�$�|���T��=�Y�S�
��
�
��m	��5�
��P
�s�Z�o�@�G�F
�4�x6��R�Z��0�n�T�'��l�sV��|��s�1�0�����
�0�%��	�=�a�P�W�v�u�>�;	�'�V�n��j�a�F�i�,�	��K��x�|�}
�S�7�(�?�@�g�U"



3

(
"

*
#




;



O
'7"&

&












(

&
#3(

*


(
#+B>
Z3

 







&
	(&3

,

&$"



















&
&
'
(
7&
&


"




&



(&






&


&






O

""
(



&













A'��~*,F
&
5P( Q�7V,J

"Dv*Dr
A���DA�I�,5Z
Cp"Jp
Q�
*"A%���x%<

DzJvM�7^Q�,B
5X""J�"A0�:�67X$,5\"$*(Q�($S�$$5V
J|
 *#>
Q�(A��x
"Jz
 &(>A
���%h�4$.R

"q�B(A���A#���I	"$,>$(<
Q~("Jx*"5Xv�Bo�BAL��5@d
"#4

$( *$$ Qv$5@
"9^
"Hv
"5X5PQ�7X,L

$S�o�DA ��uDn
"Br$7b$Q�5XQ�5R5X$$S�$&7ZA��=Q�m�6"$J�
$Q�
L�5ZA���
7V

"S�$5T(A!�n��i
o�D&7`"$J�
$$7R(,&(*Q�(5NQ
���m�\��.��FV�]�L�d�5A��(�s(�,LQ�Z�^�m�~�{h3�F�N�s�K�F�:��O�3�f�s�t��;��6�P�9�A)�~�W�1�8
�_�n��|
�	��
�l���)�D�_�jA���/�7� ��w�A���g�Q���g�l�wN�Y�h��K�V�$��>�i�x�o�*�TA�.�C�4��]!�� �D�)�ba ^a�u��K�]�W�h�o�F�|��$�Cmrt�ui�M�O�N�m�q	�M	�C�+�-��@�67��<�:�X�]�;�!�C4�/�1�3�5�7�9�;�=�?�A�CY�i*�f��^�}�e	xv+R�S
U�&:2�f)�q�D�'��s�d��`�~�K	�U�Q�g��	�i�x��t�x��G�-�>�A�E�M�T�V�Y6�b�e�h�j�n�t�v�y�}��"�
�
�����$�&�*&�3�6�:�>�@�D"�H�L&�R�V�X&�]�`�c�f�i
�o�q�u�y�{�~6�
���-�#��� �#�&�(*�.&�2
�6�:�>�@�D�J&�M
�S&�Y�]
�a�e�i�k�q�v�x�{6�����
�
�����"�"�'�+G�4
�7�>�A�CR�M
�O�R
�U�X�[�^!�d�i
�n�r�w�z���>�
���
����$"�(�+�/R�=&�A�Ez�X�[�^&�c�f�h�j�n�r�t�w�|�~>����	���>��
�"
�&S�-&�0�3�5�;�@
�D�H�J�N�T�V�Y6�^�b�e�g�l
�p�t�v"�}�R�&��B�!�(�,
�0�4�7�9�;�?
�C�F�K�N�Q�V�X�\6�`�e�h�j�o�q�t�x"�}���	Z���	�#�'�7�=�?�D�I�N
�S�W�Y�]6�a�d�g�j�l�o�q�s�v�{"�
�B��&����"9�(6�3�7&�:�?�B�E�J�L�O�T�V�Y6�\�_
�b"�g�i�k"�n�rz�x��&��

������� �"�%�)&�2�7�9�;�=�A�E�G�J�N�P"�W
�]
�b-�k�p
�s�w
�{�~���	��
��� �"&�&�)�+
�1�3�6�8�:�?�A>�D�I�KZ�Q�T�W
�ZF�c&�f�jz�s�y&��
��	�������� �%	�*�/�1�3�7�@�D
�H�M�O�S�U�[�]�_�b�f
�l�p�r�u�{�
��
���
�����!
�$�(
�,�0
�5�:�=�I�L�O�R�W�Z�^�`�d�h�j�m6�w�|�����"����.�$�)
�-�1�4�7B�>�B&�I�M&�Q�T�W�Z�\�^�d�f�i�m�o>�u�y�{Z��
�F�&�����'�+�-�/�5�7�;�?�A>�J�N�P�S�WB�a�eR�p&�v�zR�&��&���
� �$
�(�,�.�16�4�7�9�;�>�B�I�K"�O�S
�Wn�b�fB�p�t&�|�&��
�������>� �%�'�*�.B�7�;z�H�LB�S�W&�_�b�e�k
�m�q
�u
�{�}����
6������ �""�(�,z�9�<�?�
�S&�V�Z
�^�c
�g�k�m>�r�w
�{�
���	"��
�� �%6�16�>�C�G
�K�R�U�Y�a
�d�h�g:� �D��ag�s�'��5�O����[�\a2_��Z��"���
�j�����"�*�6�9�C�!�%�,�0�7�=�A�V�[�|��I�M�S�Z�a�g�q�t�w�{�~���
���G�L�P�e�n�s�w��� �$�-av}��u�ua\��h�e�M�w�
a���taz��Ua^|�-�
�2�W�9�S�?�0�SaaX�-a���]afC�g�u�Gaq��qa z�/�:�q�!�)��L�
�,�Z�D�:	���a 9�$�j�?��)%�#�/�2�W�	�aa!��@a$��Ra�_0�j�j�U��r�u�w�y������	��"�(�+�.�A�C�F�L�N�Q�T�\�c�e�m�p�r�u�x��	�������%�+�.�1�9�;�A�C�F�O�U�X�[�d�l�n�p�v�{�}
����#�+�-�0�8�@�C�F�Y�[�]�e�k�������!�$�&�(�+�3�9.�?�o�r�z�
��
������&�*�,�G�K�T�X
�^�i�o��������9�;�>�F�K
�R�]�c�e�h�o�q�x�{�~	���*�/�5�8�V�X�[�d�k�m�u�w�z���
�
���2�;�?�D�I�K�N�V�\�^�a�h�j�l�o�r�	�����
� "�+�N�Q�T�\�d�f�i�s�u�y�{�}����&�A�D�G	�N�X�^�e�h�n�p�s�z�~������$�*�,	�0�3�5�8�@6�F�~����
����!
�$�/�5�8�O�Q�U
�\�^�a�d�w�z�
�����'�B�D�G�P�V�i�p��
�
��� �#�&�-�1�7�9�<�C�I�P�d�f�h�p�u�w�z���	�=��R�T�X�]�b�d�g�o�u���+�-�0�<�B�I�\�w2�z�-�/�2�8!�<�^�y�|��!�	�+�-�/�7�?�B�E	�L�V�Y�\�e�k�m�p�x�{�}���
�!��A�C�F�N�S�U�X�a�g�i�l�t�z�}�
���� �&�)�9�S�Z�]�`�i�o�r�u����
����,�3�N�P�S�Z�]�c�e�h�q�w�y�|���$�&�)�2�8�;�B�E�G�J�R�W�]!�i��
�����!�)�/�1	�4�?�C�_�c�j�p�s!��$�'�*�4�:�=�@�I�R�T�[�_�b�e�n�v����*�-�0�7�9�A�D�G�N#�P�t�v�y
���������� �"�$%�0�V�X�[�b�g�i�k�s�y�{�~��
���'�*�-�3�9�<�K�R�e�l�n�q	�x����� �'�+
�2�=�D�_�a�d�p�v�y�|�����#��=�?�C�J�N�P�T�Z�_�a�d�l
�r&��'�)�+�2�4�:�N
�T!�c���
�=��R�m���	���;�4�p��
�
�%��=�?�B�K�R�T�Z�]�_�a�d�l�r�t�w���	����� �%�*�,�/�7�=�?�B�J�P�S�V�b�i�m�t�w�y�{�~�
������&�)�,�4�<�>�A�J�P�R�T
�`�s�z�|���
�����$�*�1�4�:�<�?!�G�i�k�m�u�{�~�������!�5�W�Y�]�b�d�j�m2�p�#�:�<�?�D�G�M�O�V�^�e�x���	���1�3�6�=�@�F�Y�`�{+�~�*�0�2�6	�:�D�G�J
�Q�\�^�a�j�p�s�x�|�~��	������ �#�&	�.�8�;�C�E�H�Q�X�\�`�c�f�i�q�x�z�~	���	���
��'�*�0�2�5�>�D�F�I�R�X�Z�]�d�j�l�o�v�x�~���
�
�����!�'�)�,�6�:�<�?�G�J�M�T�V�^�`�c�h�l�p�v�{�}�������#�&�)�<�>�B�I�N�P�S�\�b�e�h�o�q�w�����"�(�2�8�K�Q�T�W�`�f�h�x�~���
��#�*�E�G�J�R�Z�]�`
�g�r�t�w!��"�$�'�.�1�5�7�<�B�D�G�O�U�X!�h�
�����1�7"�J�m�o�r�|�
��
��#�+�2�5�8�?�^�d�f�i�p�s�y!��.�I�d�s�v�����$�'�+�/�7�<�>�A�I�O�T�[!�_��������"�(�+�:�A�D�T�Z�\�_�h�n�q�t!��"�%�(�1�7
�F�Q�S�V�_�e�x������(�*�.�1�4�;�?�E�G�J!�S�u�w�y���	����� �(�.!�A�c�e�g�o�u�w�z����	����������8��:��=��F��L��N��Q��X��\��b��v��|������1��3��6��?��E��H��K��W��]��q��w��y��|��������
����#��%��(��/!��2��T��V��Y��b��h��j��m��t��z��|������
��!��!��C��E��I��O��U��W��Z��c��i��l��{����������!��*��/��2��I��d��g��n��v��|��������%��+��>��E��`��b��e��l��o��u��x��{����������������"��%	��,��6��8��;��D��K��O��U��X��Z��\��_��g5��m��#��%��(��0��6��8��;��D��J��M��P��W��Y��_��f��o��w��y��|����!����A��D��[��^��a��j��r��t��w��~��!����:��=��@��G��I��Q��T��W��^��a��c��g��j��m#��u��������'��-��/��2��6��:��@��B��E��M��S!��g��	������������!��*��0��2��5��=��C��F��I	��Q��[��]��`��d��h��k��m��o	��r��}��
������������)��/��2��5��>��F��H��K��O��Q��T��Z��]��`��g��k��q��s��v������
��
��������!��(��+��1��4��<��?��A��D��G��N��Q��W��Y��\��ePK
!<.}�<��2chrome/pdfjs/content/web/cmaps/UniKS-UTF16-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�
UniKS-UTF16-HA �}�m ��m
,7�<a �{�l�x	����r�
�;�PK
!<���SgSg2chrome/pdfjs/content/web/cmaps/UniKS-UTF32-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�#C��
wa�r�YM�S
��t��J�v�O�e�z�?CZƅ	�1�:�6	�g�r�3�4	�7�6�9�8�9�8�=�8�C�8�5�8�_�s�#�lO@�>��P
y8�`
)�]YX�	�l�
�|�kf6�8m+]\]>6EYT(�V8?3
+&C	IW"V�m�`C-��S�^K�b3�h�I9�?�3�V �N2�B1(�C�d�Z!<'�g
���z�_I�o�b
/C
 5�n�)�
S%�	H3:%>)<'(C8% �(F�i
���jebm��	5�&!�A �C!/�<�!@:B�z�;�v�(��o�F�y�:�)�&�}�=�0�l����U�:�3�w�D��Q�F�i�4C%�� V�)��-��_?�
�&H"5@S/B�h�W��4��X�'�6�+�L�_C	/O�V�j�I�J�w�N�Q�� S
/Z�a��V��
�y�G�j�'�p�d�4�bC/i��]F�>�Q�P�C�"��v��G�fC/u�tC/j�0��gS
/�y�A�`� �;�j�J����C/��<�U�RC/���,�o��X��9C/��L_�)�Q���Y�~}�g�"�#�H�YK�.�+��"�%S
/��q�6�E�
�~�Y�X�c�Y)S
/���x�(�y
��~�=�pC0l��g�h�7�6b��C/Ν6�F�i�'�SC
/ʹf,�?�x
;:�E�h�4�=V�|�9	��S
3‚� �
r�-�D`q�lC3؂C3τc`C�|N�<�f�&�
��}�X���o�	�l��v��.�a�^�U�V��'�	�N�K�|�u�_�F�p�k�5��u�x�v��<�e�&�B�L�i�$�1l�4�1��/�@�&�o�:�5�P��>{�%�J�O�U��A�@�l�W��=�L�/�.��#�f���%�\�
���sS�6�O�0�Q�y���d��n��'�?�8�U�%��#�8�p�Y�f��!��%���r�@�/�u�!�b�(�m��P�)�$�G�F�S�4�B�"�Q�i��q�^�=��/�_�
�K��s�$�$�q�A�#�P�+�9�x�z��M�U��}�`�Z�g�~�g�M�N��|��o�V�
�I�D�!�hb�Q�6�Q�f�i��7�\�_
�j�_�x�B�"�L�o�9�A��]�{�b�3�N�U�a�0�s�>��w� �(�m��N�"�=�-�q�j��7�
�%��j�w�X�H�t�t�?�_�
�#�/�p��Q�i��D��Q�X�%���C�Y�^�,��2�	�+�C�b�%�t�S�4�D��F�~�G�L�'�M�x�c�4��P�Y�;�>�k�@�#	�v�Ah�8�E�U�b��%� �n�Z�'��k�D����6�k�|��v��u��n�?�^�&�+�O�b�{�f�n�]�o�D�#��6�4��	�v��L�M�S�
��p�P��L�W�D�U�8�5�T�y��
���;�;����q�C�\�
�`�|�A�A�_�R��L�?�*�<�m�-�|�,�J�E�d�%�/�T��]�h��*�+�O�T�n�1�:���o�j�n	�	�1�A��@�?�1�\�(�!�W��`�`�X�?�J�z]�x�1�z��Z�?��6�E��w���0�a�f�3�l�;
�G�P�e��a�J�� �.�)�)�F��-�Y�X�Y���B��7�,�A�M�p�q
�&��F��v�[�:�&�3�"�	�]�X���gJ��{�l��_�v�y��2�E�8�;�}�<��n��Y�f�r�v�s�x�0����PE�m�=�(��{��u�}�x�s�2�#�x	]��~�w�u��.��u�0��a�[�|�0�&�{�]�b�&�W��y
�s��a�2�q	���%�i����_��;�
�D��H�S�$�8�{
��0�F��&�1�P�	��	�^��;�p�s�
�o�\�;��y�d��e��m�P�a�5�H;���l�_�#�

�{�z�@�3�H�+����h�g�d�l���X�F�E�Q��-�G�G�b�E��,�+�z�2���g�>��
�_�0�7�Z�%��e�b�!�L�K��
�A�5��1�m�*�@�
�"��C��E��#��O�J�c�}�Q�N�J	�E�>��(�
��n�i��>�i��V�K��A��j�O�%�J�~�c�|�	��x�}�
�|�9�f�6�s�A��Y�A�"�2���k��"��S��&�R��p�l�!�N���<��i�D��E�f�a�o�M�g�J��]�S�Z�=�,�s�t�*��4�
�jp�=�/�l�/��[��Z�l��_�z�q�p�.�|�a	�e�TJ��=
�W�!�d�
�@�9N�'�,�?�G�:�1��C�	+�3���k���`�f�F�c�6�I�F�^�w�	���b�<�<�b�y��,�a�2�p�9�u2�d�A�(�t�s��I��s�"�m�P�}�T�	�z�X��1��R�?�5�>��d��X�@�S�S�b�:����j�n�I��]�O���?�!�|�7�V�-�I�#�^�Z��X�7�(��s�
��q�;�6�`�4�!��	�I�H��^�U�:�]�K(���_
�D�[��/�.����4�C�Q�S�t�r�
�w�o�d���B� �;�.�9�d�<�[�&�Y�T�o�y�&��6�q�R�I�0�;�D��n�D��
�,	�W�E�3�Y�<���>
�f�c!��{�j�r����b�,�Y�B�1
��M�b�F�*�q�z�`�3��
�g�p�M�=��r�5�H��L�
��/�^�t�[�.�M�_�U�[�X�E�w��`�]�x�{��[�)�\���Q	�d�Q�e�:�/� �U�h�H��f�A�@��2B��B�!�X�6�Q�"�a�>�s��Y����F���o�=�D��3�y�j�n�]�0��)�`�K�H�%�_�p�1+���!��r�7�0���i�H�>�u�<��9�>�k�9���S���l�n��-�7�^�?�
�C�R�^�q�p��5�N�b��Z�I�W�@�&�>�D�N�c��$��d�%�
�'�,�v�u�b���6�[�N
�	�!�L�	�H��f�e��r�k���$�K�U�>�}t	�8�'�T�q�l�}�~�C�\�;�L�]�5�n�g���}�A�V�N�N�[�=��%��<�s��X�g�3�F���r�D�A�9	���$�~��f����u�	�|�7�i�b�(�?�1�0�W�5�2� �U��~���'�[�Z�h�+��4��u�%�5�+�C�H���9�l�y����|�/�u��G��v�t�o	��o�	�8�a�h�8�%�=��|�?�`�+��A�Q�.�Q�L���S�p��?�o�f�$�1�*�u�Z�+�B��_�o�@�[� �V��:�E�j�\�8�a� �u��B�V�}�%�Z��&�K���e�h�7�-�N��=�DXY�{�g�f�6�Y�K��f�f+�a�'�!�:�A�.�/�{�V�~�
�a�<�w�h�	�f�Y�&��t�i�o��x
��e��
�	�z�=�4�p�3�L����M*�|�|��%�*�G�\�I�Y�v�
�X��5��u�6�S�.�/�L�R�w�q�(�S�u�"���9�8�'�V�~��"�r	�5�$��m�c�p�Q�j
�q
��l�'���$�\��e�F�3��y�M�,�U��F�O�.�Y�Z�f�D�1�6��o�l�?�n�F�@��a�1�P�9�;�Y�!�D�+�j�1�i�2��F��P�E�n��E�J��N�@�+��x�3�
�B�A�B�?�7��?�[�\�B�[�C�3�g��'�2�F�{�}�R�?�R�~�;�4��\�r��a�q�$��V��c�B�C��1�~�C�w��|�+�%���a�l�A�R��]�*�y�A�h�?�f�;�b���9��W��x�[�'�p��(�v��~�1	��z�C�(���>X�y�)�U�D�E�U��z�;�W�V�L	���x��<��7�(x�]�e�@��9�Z�@�?�y�w�
��	�]�0���~��#�T	��i�.�g�a�4���� r�>�oX�}�A�:��u�<�\��	�r��M���>�]����,��V�{��Y�B���+��Z
�2��!�"�(�~�b�C�6� �u�J�/�O�\�E��E�������E��%
s�5�O��?� �`�?�YI��:�q�N���Z�m�h�q�!	�+�4�6��|�9���!�:�{�v�j�p�u�^�`�c�P�$��^�4	�a�l�x��X��s�*��M��0�[~��i�3
���o��g�N�[�3��
�=�J�q�����^�B�I�_�I�4�>�p
�
�P�9� �
�x�B�m�����I�4�s�3�.�Q�]�^�H�e��=�X�)�:�E� �M�0�I�U]�o�X�^�#�T�G�k��`�(�o�%�]�2�l�7�h�P�{�}��G�p�!�A�<�-�T	�V�M�s�(�U�#�*�@�;�`�U�x�S�]� ��\�8��w�?��]	�P�'��w�A�D�Y�X�]�i�r�S�H�>�K�M���e�>���+�~�9�"�~�|�y}��:�9�7�L�X�_�z�G�o�h�p�u�k�_y�-�|��I�6�m�j�m��+�}�0�;��K�d���
��O�6�G��x�-�&�K�P�?�t�m�"�Q�r�\�4���v��e�x�M� �e�p�Z�4�i�*�F�}�h�x�m�T�f�O�O�R�{�6��'�H�+�2�p�-�|��Z���3��:�I�~�)�s���\�8�mU�2�%�]�D�'��|��l�<��M�+�r�<�#�d�H�~���p�$�f����c��PS��^��W�V�,�#�
�C��L��j�9�T�:���t�G�6�m��)�p�&�y�i�|�(��k�0�������n�0�+�F�R�	�,�!�R��	���mh�j�h�o�g�B�i�6�G�K�>�V�>��z�e����(�h��3�-��\�s�	�|�s��K��z�J�o�B���t�t�S�c�J��L�	����1�[�O�>�x
�+�:�I�&�L�5�X�7�!�7�m�	�l�
��|�=���'�5��}	�T��(�W�`�^��I�r�s�N�S�p��w�N�Y�%�!�i�	�p�I�o�
�M��d�2��w��1��T�{��@�>���V�A�\�O��j�9�F�k�<���)�|�J�y
�q�b�S�v�*��b��g�@��'�n�e� �8�o�1���^�e�X�X�U�L�]�h�O�
�#�v�'�n�	�c�p�_���@�!�r�
�b�B	�#)�X����Q�
�'�N��)�5�P�$�/��x�6�W�V����u��[�-	��)��5�S�J�&�X�
�	R� �
��h�U���Y�
�P�9	�e
�w�P�N��y.�8�W�F�X�k�`�a�b�9�{��|�V�%��S�hv��l�u�*�{ �o�4�y�>��A�4	�7�
�w�p�I�^���-�*��\��$�N�u�!�B�	�(�;��I
�x�C�Y��V�����E�l�;�a� `�6�J�C�^�� ��u�a�E�W�2�A�E�&�V�v�Q��pg�&���������(	�9�$�F�^��C�V� ��c	�r�R���N�B��u�t�Z�h�0��l��=�
�T��%��I�3�4�!�B	�\�
�i���z�i�8��C���*�m�a��� �H��F�s��!�y�\�!�=�x�n���y�0�]�%��h��,��#�L�y�b�c�3�e�(	�M��>�����'{z�$�<�C�B�2�
�m�H�y�&��K�M�j���R��P�J�A�>�_�z��r�U�F�M�'�S�R�S��h�B�U�f����P�[�>�W�8�=�,�@�c�|�8��+�Z�F�-�5�p�
�#�^��C�a�^�i�f�BG���^�	�h�C�B�C
�"�)��@�(�x���l�6�_�v���Z�C��+�b�'�r�0�@��y�l�O�p��2�%�f�#� {��/�a�6�O�S�p��9�f�#�t
�m�B�P�C�8�^
�a�k��:�@
�'�E�`����F�L���d�,�S�^�l�?�#�`�l�V�5�*���v���:�<�x�C�4��;	�n
�B
���b�?�k��E�L�9�O��i�d��g�^��5���b���:�G�,���%�
�$�a�1�T�Y��8�9���1��%��l�_�O�L�K�`�U�u�Z�i�@�u�=�+�|��w�`�G��y�I�w�L��:�Q��p�s��@�+�4�{�)�^�R�i��-�h�i�J�
F�(�o�J�s��8*�z�x�%�=�Q	�Q�>^����iJ�O�|�>��N�
	�F�(�W�Q�
�Y	�r�s�<�>�i�6�N�g�+�b�N��L�]�v����-��5�-�P�R�C�6�N�E�P��U�%��m���d�F�)�	�Y��A��<	��,�#�9�+�f���V�2�I�.��&��3�l�$�K�9
��o�
�
�\�]���_�_�t���/�^�}�S�$�y�T�.U�}�b�r�e�	��3���A�B�j�x�y�,�Z��l��2�P�0�i��U���CB�|�i��4�9�,�J�q�2�X����<�p�6�s�#�p��K�e� 	�s
�L�l�B�U��$�O�"�{��
�U�<�|�m�k�$��h	��B�M�|�Ni�7�S�T��g��`�3�r
�n�2�_�|��r�1���}�=�"�E�
�?�u
�N�K�n�r�s�2��+��E�F�I�x�e�=�b�C�f
�N�
�$�Y�*��y�!�F�=�k�"	�o�R��[���.��,�h�{�<�]�P�x�!�{�T�U��I� �S�y��r�S�@�5�T�$�c���`�A�/��w��.�-�8�	�j	�s�b���}�j�c�z�3�S��z�-�m��;�#��h�'��]��!�(�v�>�q�F�_�i�B�U�|�&�8�E�Bx�/�C�r��1�Y	�Z��7���7�?�D���v��O�5�l�=�8�9�O�t��s���\�.�q�8�>�u�t�K�j
��/�<�Pg�*�	��W��Y�)��o�=�Z�a�s���F�L�
�[�`�u
�J�)�^���T
�;�"�3�i��o�l�(�~�,
�s�F�S�B��k�v�l�M��+�j��T�U�2�X�
�f�U�X�D�;�}���B�GG�@�u�@��a�J�B�\
��H��V�W��b�*�Q�d�T�o�>�b��^�Y�+�<��B��Q��j�=�.�+�I�R�w�N�t�!�/�J�!�o���[��Y��J	��;� �g�[�x�3�^�7
~�"��t��B�#�1� �s�;��5�J�;Y�& �c�
�?
�%��s�o�
�d�\�
�
�9H�~�W�x�
~��N���|�)�,�2�J�����z�h�^�#��A�Z�g�I�
�@�A�0�#�B�t�m�]��Z�W�P�#��=�R�c�O�(��W�/�P�1��s�0�S�~�q�o�B��t���7�8��j�c�p�	�.�X
�0�1�1��p�-�n�	�1��p�S�t�gI�
�K��%
�J�z�]�^�G�:	�}��v�g��V�1�S�h�
�D�U�:�R�c�z�o�^�c�`�\�I��8�-#�|��o����F��\�+��j�(�C��2�%�s�0�c� �w���3�f�9���~�9�1�A�Z�"�-�T�;�\�Y�-	�/
�B��"�#�	�h�%���t���[�<���A
�m�*�o�|�� ��P�E�G�0��\�6��T�.�'�\�
�.	��Q�3�F�L�9�p�d�/�T���i�*��X�'�2�,�w�#�

��!�0	�1�U�4�� �S�H��m�%�f�_�J�q�J�<�4�9�z��.�p�7�P�0�&
�?�,�p��c�L��K�|�I�@�[�n�
��Z�t�3�,L�Y�9���M�a� ��\�,�!�/�4�i��l��3�Q�8�Y�z���z�J�m�_�I�(��M��2��G�D�c�i�E�K� �8��
�&�m�,��z�RC�r�\�i�/�4�K�\�/�B�"� ��(�P�s�a�>�>�.�q�<�g�)�o�
��L�	�\$�v�U��9�@�
��L�E�n�{��F�|�M�\�q��7�	�?�^�>���<��}	���
�Q�P'��I]��7�_�*��)�_��Z�
���X�#~�H�{��D�?�2� �y���@�	�5�J���u��t�p�w��T�3�]��|�A�O�L�f�Y�_�R��q��1��G�:�Q�r� �m�&��,�m�~����}M��{���
�`�
�^�w�2	�rc
��-�H�(��V�_�t�i�@�S�C��=�J�K�V
�Y�h�o�n�o�T�;�d�|�P�k�%�t�.�g
�,��,�o�'�.�w?	�V�%�O?�p�!��B�y�:�k�v��F���?�F�J��C�F���
������b�5�R�=��f�/��=�&8���x�2�=�
��j�	��x�#��
�t�q�N���!�w�0�X�z�p�a�s��6�Q�P�9�i�"�U�C�X�9� �$����(�x�<�'�o
�^�y�d�w�4�x�c��H�@�z�O�|��L�R�K�1�R��O�A�:�O�S�=�d�j�L�	�fq�f�k��}��F�9�M�F�K�P�m�K�\��.�K�[���v�V�C�W��a�r�t�y�W�"��D�_�J��0��d�
���U�:��$�s�{�j�(�]�k��V�s�E�:�~�1��	�z�K�4�@�Z�[�Y�;
����S��d�=�1��q�4�>�	�f�M�$�U
���K�D�q��a�L�X�"�
��p
�}�(�V��M��p�k�b�C���I�1�$��s�p�&�Z�p�M�H�n
�W��z�k�=�/�.��~���p�:�o�@���/�X�0�q�<�o�f�
�s�N�o�n�p�i�H�w�A�V
�{�}��T�8
�&��}�	�`�L�{�O!�,�w�Y�u�\�0�y�H�i�L�
y�>�G�Q�x�|	�a
�4	�W�K�B�q��/9�j�B��?�	����&�/�y�&��p
�Q�z�/�8�\�I�:�W�}�h�h�C�c�2�y�4�
�/�'���O�7�d��G��#������g��p�#�>���m�7�7�� �i�,�N�a�S�]�L�M��j�%^�@�`�C�T�	�y��i�@�7�4�Y�B�-�r�(��R�����A�y�X�w���I�>��;�(�s�r�l�Q�7�x��8�%��r�v�%�Q�a��GS�H�K�N��Y�1�0�S�'�(�I�b�i
��*�7k��"�W�b�Y�J�\8�=�3���}�3�0�m�/
�R�!�c�B�c�<�W�i�b�/�,�|��6�@�7�
�(�a��9�_�z��$>�Y�X�S�9��s�>�,�E�9�B�9��K�Q�=�(��]
�B�c
�F�!�o�J��p�@�-�W�^�}�j�U�z�u��8�u��x�e=�=��9�d�q�n�E+�^�S�6�
�%��"�;l�%	�d�]�>�S�z�L
�5�	�(�Y�
�;�$�|���T��=�Y�S�
��
�
��m	��5�
��P
�s�Z�o�@�G�F
�4�x6��R�Z��0�n�T�'��l�sV��|��s�1�0�����
�0�%��	�=�a�P�W�v�u�>�;	�'�V�n��j�a�F�i�,�	��K��x�|�}
�S�7�(�?�@�g�U"



3

(
"

*
#




;



O
'7"&

&












(

&
#3(

*


(
#+B>
Z3

 







&
	(&3

,

&$"



















&
&
'
(
7&
&


"




&



(&






&


&






O

""
(



&













C'��~*,F
&
5P( Q�7V,J

"Dv*Dr
C���DC�I�,5Z
Cp"Jp
Q�
*"C%���x%<

DzJvM�7^Q�,B
5X""J�"C0�:�67X$,5\"$*(Q�($S�$$5V
J|
 *#>
Q�(C��x
"Jz
 &(>C
���%h�4$.R

"q�B(C���C#���I	"$,>$(<
Q~("Jx*"5Xv�Bo�BCL��5@d
"#4

$( *$$ Qv$5@
"9^
"Hv
"5X5PQ�7X,L

$S�o�DC ��uDn
"Br$7b$Q�5XQ�5R5X$$S�$&7ZC��=Q�m�6"$J�
$Q�
L�5ZC���
7V

"S�$5T(C!�n��i
o�D&7`"$J�
$$7R(,&(*Q�(5NS
���m�\��.��FV�]�L�d�5C��(�s(�,LS�Z�^�m�~�{h3�F�N�s�K�F�:��O�3�f�s�t��;��6�P�9�C)�~�W�1�8
�_�n��|
�	��
�l���)�D�_�jC���/�7� ��w�C���g�S���g�l�wN�Y�h��K�V�$��>�i�x�o�*�TC�.�C�4��]!�� �D�)�bc ^c�u��K�]�W�h�o�F�|��$�Cmrt�ui�M�O�N�m�q	�M	�C�+�-��@�67��<�:�X�]�;�!�C4�/�1�3�5�7�9�;�=�?�A�CY�i*�f��^�}�e	xv+R�S
U�&:2�f)�q�D�'��s�d��`�~�K	�U�Q�g��	�i�x��t�x��G�-�>�A�E�M�T�V�Y6�b�e�h�j�n�t�v�y�}��"�
�
�����$�&�*&�3�6�:�>�@�D"�H�L&�R�V�X&�]�`�c�f�i
�o�q�u�y�{�~6�
���-�#��� �#�&�(*�.&�2
�6�:�>�@�D�J&�M
�S&�Y�]
�a�e�i�k�q�v�x�{6�����
�
�����"�"�'�+G�4
�7�>�A�CR�M
�O�R
�U�X�[�^!�d�i
�n�r�w�z���>�
���
����$"�(�+�/R�=&�A�Ez�X�[�^&�c�f�h�j�n�r�t�w�|�~>����	���>��
�"
�&S�-&�0�3�5�;�@
�D�H�J�N�T�V�Y6�^�b�e�g�l
�p�t�v"�}�R�&��B�!�(�,
�0�4�7�9�;�?
�C�F�K�N�Q�V�X�\6�`�e�h�j�o�q�t�x"�}���	Z���	�#�'�7�=�?�D�I�N
�S�W�Y�]6�a�d�g�j�l�o�q�s�v�{"�
�B��&����"9�(6�3�7&�:�?�B�E�J�L�O�T�V�Y6�\�_
�b"�g�i�k"�n�rz�x��&��

������� �"�%�)&�2�7�9�;�=�A�E�G�J�N�P"�W
�]
�b-�k�p
�s�w
�{�~���	��
��� �"&�&�)�+
�1�3�6�8�:�?�A>�D�I�KZ�Q�T�W
�ZF�c&�f�jz�s�y&��
��	�������� �%	�*�/�1�3�7�@�D
�H�M�O�S�U�[�]�_�b�f
�l�p�r�u�{�
��
���
�����!
�$�(
�,�0
�5�:�=�I�L�O�R�W�Z�^�`�d�h�j�m6�w�|�����"����.�$�)
�-�1�4�7B�>�B&�I�M&�Q�T�W�Z�\�^�d�f�i�m�o>�u�y�{Z��
�F�&�����'�+�-�/�5�7�;�?�A>�J�N�P�S�WB�a�eR�p&�v�zR�&��&���
� �$
�(�,�.�16�4�7�9�;�>�B�I�K"�O�S
�Wn�b�fB�p�t&�|�&��
�������>� �%�'�*�.B�7�;z�H�LB�S�W&�_�b�e�k
�m�q
�u
�{�}����
6������ �""�(�,z�9�<�?�
�S&�V�Z
�^�c
�g�k�m>�r�w
�{�
���	"��
�� �%6�16�>�C�G
�K�R�U�Y�a
�d�h�g:� �D��cg�s�'��5�O����[�\c2_��Z��"���
�j�����"�*�6�9�C�!�%�,�0�7�=�A�V�[�|��I�M�S�Z�a�g�q�t�w�{�~���
���G�L�P�e�n�s�w��� �$�-cv}��u�uc\��h�e�M�w�
c���tcz��Uc^|�-�
�2�W�9�S�?�0�ScaX�-c���]cfC�g�u�Gcq��qc z�/�:�q�!�)��L�
�,�Z�D�:	���c 9�$�j�?��)%�#�/�2�W�	�ac!��@c$��Rc�_0�j�j�U��r�u�w�y������	��"�(�+�.�A�C�F�L�N�Q�T�\�c�e�m�p�r�u�x��	�������%�+�.�1�9�;�A�C�F�O�U�X�[�d�l�n�p�v�{�}
����#�+�-�0�8�@�C�F�Y�[�]�e�k�������!�$�&�(�+�3�9.�?�o�r�z�
��
������&�*�,�G�K�T�X
�^�i�o��������9�;�>�F�K
�R�]�c�e�h�o�q�x�{�~	���*�/�5�8�V�X�[�d�k�m�u�w�z���
�
���2�;�?�D�I�K�N�V�\�^�a�h�j�l�o�r�	�����
� "�+�N�Q�T�\�d�f�i�s�u�y�{�}����&�A�D�G	�N�X�^�e�h�n�p�s�z�~������$�*�,	�0�3�5�8�@6�F�~����
����!
�$�/�5�8�O�Q�U
�\�^�a�d�w�z�
�����'�B�D�G�P�V�i�p��
�
��� �#�&�-�1�7�9�<�C�I�P�d�f�h�p�u�w�z���	�=��R�T�X�]�b�d�g�o�u���+�-�0�<�B�I�\�w2�z�-�/�2�8!�<�^�y�|��!�	�+�-�/�7�?�B�E	�L�V�Y�\�e�k�m�p�x�{�}���
�!��A�C�F�N�S�U�X�a�g�i�l�t�z�}�
���� �&�)�9�S�Z�]�`�i�o�r�u����
����,�3�N�P�S�Z�]�c�e�h�q�w�y�|���$�&�)�2�8�;�B�E�G�J�R�W�]!�i��
�����!�)�/�1	�4�?�C�_�c�j�p�s!��$�'�*�4�:�=�@�I�R�T�[�_�b�e�n�v����*�-�0�7�9�A�D�G�N#�P�t�v�y
���������� �"�$%�0�V�X�[�b�g�i�k�s�y�{�~��
���'�*�-�3�9�<�K�R�e�l�n�q	�x����� �'�+
�2�=�D�_�a�d�p�v�y�|�����#��=�?�C�J�N�P�T�Z�_�a�d�l
�r&��'�)�+�2�4�:�N
�T!�c���
�=��R�m���	���;�4�p��
�
�%��=�?�B�K�R�T�Z�]�_�a�d�l�r�t�w���	����� �%�*�,�/�7�=�?�B�J�P�S�V�b�i�m�t�w�y�{�~�
������&�)�,�4�<�>�A�J�P�R�T
�`�s�z�|���
�����$�*�1�4�:�<�?!�G�i�k�m�u�{�~�������!�5�W�Y�]�b�d�j�m2�p�#�:�<�?�D�G�M�O�V�^�e�x���	���1�3�6�=�@�F�Y�`�{+�~�*�0�2�6	�:�D�G�J
�Q�\�^�a�j�p�s�x�|�~��	������ �#�&	�.�8�;�C�E�H�Q�X�\�`�c�f�i�q�x�z�~	���	���
��'�*�0�2�5�>�D�F�I�R�X�Z�]�d�j�l�o�v�x�~���
�
�����!�'�)�,�6�:�<�?�G�J�M�T�V�^�`�c�h�l�p�v�{�}�������#�&�)�<�>�B�I�N�P�S�\�b�e�h�o�q�w�����"�(�2�8�K�Q�T�W�`�f�h�x�~���
��#�*�E�G�J�R�Z�]�`
�g�r�t�w!��"�$�'�.�1�5�7�<�B�D�G�O�U�X!�h�
�����1�7"�J�m�o�r�|�
��
��#�+�2�5�8�?�^�d�f�i�p�s�y!��.�I�d�s�v�����$�'�+�/�7�<�>�A�I�O�T�[!�_��������"�(�+�:�A�D�T�Z�\�_�h�n�q�t!��"�%�(�1�7
�F�Q�S�V�_�e�x������(�*�.�1�4�;�?�E�G�J!�S�u�w�y���	����� �(�.!�A�c�e�g�o�u�w�z����	����������8��:��=��F��L��N��Q��X��\��b��v��|������1��3��6��?��E��H��K��W��]��q��w��y��|��������
����#��%��(��/!��2��T��V��Y��b��h��j��m��t��z��|������
��!��!��C��E��I��O��U��W��Z��c��i��l��{����������!��*��/��2��I��d��g��n��v��|��������%��+��>��E��`��b��e��l��o��u��x��{����������������"��%	��,��6��8��;��D��K��O��U��X��Z��\��_��g5��m��#��%��(��0��6��8��;��D��J��M��P��W��Y��_��f��o��w��y��|����!����A��D��[��^��a��j��r��t��w��~��!����:��=��@��G��I��Q��T��W��^��a��c��g��j��m#��u��������'��-��/��2��6��:��@��B��E��M��S!��g��	������������!��*��0��2��5��=��C��F��I	��Q��[��]��`��d��h��k��m��o	��r��}��
������������)��/��2��5��>��F��H��K��O��Q��T��Z��]��`��g��k��q��s��v������
��
��������!��(��+��1��4��<��?��A��D��G��N��Q��W��Y��\��ePK
!<>9Ѩ�2chrome/pdfjs/content/web/cmaps/UniKS-UTF32-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�
UniKS-UTF32-HC �}�m ��m
,7�<c �{�l�x	����r�
�;�PK
!<>�`�l�l1chrome/pdfjs/content/web/cmaps/UniKS-UTF8-H.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE���?���?����? A �
wa�r�YM�Q
¶�t��J�v�O�e�z�?A	×��O�AÆ�	
�6	�R�3�4	�7�6�9�@�8�9�8�=�8�C�8�5�8�Y�~�@Aʼ�SB4‐a�P
y�x�`
�i�]�YX�	�l�
�|�k�f6��xm�k]\]>6EYT(�V8?�T3
+&�	�	W�bV�m�`B,‛�.�b3�h�E�I9�?�s�V �C�N�r�B1(�C�K�d�Z!<'�g�J���z�_�	�o�W�b
/B
‵�n�i�
R┌�	H3:%>)<'(B8┠�(�B��i
���je�Bbm��	�u�&�a�A �B!⼀�<�!@:B�z�;�v�(��o�F�y�:�)�&�}�=�0�l����U�:�3�w�D��Q�F�i�@�4B▱� V�C�)���m��_��
�fH"5�X@R⽂�h�W��4��X�'�6�+�L�_B	⽏�V�j�I�J�w�N�Q�� R
⽚�a��V��
�y�G�j�'�p�d�4�bB⽩��]F�>�Q�P�C�"��v��G�fB⽵�tB⽪�0��gR⾀�`�A�`� �;�j�J����B⾒�<�U�RB⾖��,�o��X��9B⾎�L_�)�Q���Y�~}�g�"�#�H�YK�.�+��"�%R
⾳�q�6�E�
�~�Y�X�c�Y)R
⿁��x�(�y
��~�=�pB〃l��'��h�w�6�"�X�B⿎�6�F�i�'�SB
⿍�f,����x
;:��h�t�=�V�|�9	��R
㏂�� �
r�-�D`q�lB㏘�B㏏�c`B�一�<�f�&�
��}�X���o�	�l��v��.�a�^�U�V�F��'�	�N�K�|�u�_�F�p�k�5��u�x�v��<�e�C�&�B�L�i�$�1l�4�1��/�@�&�o�:�5�P��>{�%�J�O�U��E�A�@�l�W��=�L�/�.��#�f���%�\�
���sS�6�E�O�0�Q�y���d��n��'�?�8�U�%�E��#�8�p�Y�f��!��%���r�@�/�u�!�b�(�m��A�P�)�$�G�F�S�4�B�"�Q�i��q�^�=��/�_�
�B�K��s�$�$�q�A�#�P�+�9�x�z��M�U��}�`�Z�g�~��G�g�M�N��|��o�V�
�I�D�!�hb�Q�6�Q�f�i��7�\�_
��Gj�_�x�B�"�L�o�9�A��]�{�b�G�3�N�U�a�0�s�>��w� �(�m��F�N�"�=�-�q�j��7�
�%��j�w�X�H�@�t�t�?�_�
�#�/�p��Q�i��C�D��Q�X�%���C�Y�^�,��2�	�+�C�b�%�t�S�4�D��F�~�G�L�'�M�x�c�C�4��P�Y�;�>�k�@�#	�v�Ah�F�8�E�U�b��%� �n�Z�'��k�D����6�B�k�|��v��u��n�?�^�&�+�O�b�{�f�n�]�o�D�#��G�6�4��	�v��L�M�S�
��p�P��L�W�D�U�C�8�5�T�y��
���;�;����q�C�\�B�
�`�|�A�A�_�R��L�?�*�<�m�-�|�,�J�E�d�%�/�T��]�h�E��*�+�O�T�n�1�:���o�j�n	�	�1�@�A��@�?�1�\�(�!�W��`�`�X�?�J�z]�x�1�z��Z�?��6�E��w�D���0�a�f�3�l�;
�G�G�P�e��a�J�� �.�)�)�F��-�Y�X�Y���B��7�,�A�M�p�q�J�&��F��v�[�:�&�3�"�	�]�X���gJ��{�l��_�v�y�C��2�E�8�;�}�<��n��Y�B�f�r�v�s�x�0����PE�m�B�=�(��{��u�}�x�s�2�#�x�I]��~�w�u��.��u�0�E��a�[�|�0�&�{�C�]�b�&�W��y
�s��a�2�q	���%�i���V��_��;�
�D��H�S�$�G�8�{
��0�F��&�1�\�P�	��	�Q�^��;�p�s��M�o�\�;��y�d��e��m�H�P�a�5�H;���l�_�#�

�{�D�z�@�3�H�+����h�g�d�l���X�F�E�E�Q��-�G�G�b�_�E��,�+�z�2���g�>��
�C�_�0�7�Z�%��e�b�!�L�K��
�A�5��1�m�O�*�@�
�"��C��E��#��O�J�c�}�D�Q�N�J	�E�>��(�
��n�i��>�i�B��V�K��A��j�O�%�J�~�c�|�	��x�}�
�Q�|�9�f�6�s�A��Y�A�"�2���k��"��E�S��&�R��p�l�!�N���<��i�D��E�f�a�o�M�g�J�D��]�S�Z�=�,�s�t�*��4�
�jp�D�=�/�l�/��[��Z�l��_�z�q�p�.�|�a	�e�TJ�A��=
�W�!�d�
�@�9N�'�,�?�D�G�:�1��C�	�k�3���k�\���`�f�F�c�6�I�[�F�^�w�	���U�b�<�<�b�y��,�a�2�p�9�u2�d�A�(�t�s��I��H�s�"�m�P�}�T�	�z�X��1��R�?�5�>��d��X�@�S�S�b�:����j�n�I�@��]�O���?�!�|�7�V�-�I�#�^�Z��X�7�(��s�
��q�;�6�`�4�!��	�F�I�H��^�U�:�]�K(���_
�D�[��/�.����4�C�@�Q�S�t�r�
�w�o�d���B� �;�.�9�d�<�V�[�&�Y�T�o�y�&��6�q�R�I�0�A�;�D��n�D��
�,�I�W�E�3�Y�<���>
�f�c�a��{�j�r���W��b�,�Y�B�1
��M�b�F�O�*�q�z�`�3��
�g�p�M�=��r�5�H��L�
��/�C�^�t�[�.�M�_�U�[�X�E�w��`�]�x�{��@�[�)�\���Q	�d�Q�e�:�/� �U�h�H��f�A�E�@��2B��B�!�X�6�Q�"�a�>�s��Y���C��F���o�=�D��3�y�j�n�]�0��)�`�K�H�E�%�_�p�1+���!��r�7�0���i�H�>�u�<�K��9�>�k�9���S���l�n��-�7�^�?�
�C�R�^�q�@�p��5�N�b��Z�I�W�@�&�>�D�N�c��$��d�%�
�'�,�v�E�u�b���6�[�N
�	�!�L�	��O�H��f�e��r�k���$�K�U�Q�>�}t	�8�'�T�q�l�}�~�C�\�;�L�]�5�n�g���T�}�A�V�N�N�[�=��%��<�s��X�g�3�F���r�D�G�A�9	���$�~��f����u�	�|�E�7�i�b�(�?�1�0�W�5�2� �U��~���B�'�[�Z�h�+��4��u�%�5�+�C�H���9�l�y����B�|�/�u��G��v�t�o	��o�	�8�a�h�8�%�=��D�|�?�`�+��A�Q�.�Q�L���S�p�@��?�o�f�$�1�*�u�Z�+�B��_�o�@�[� �V��:�E�j�@�\�8�a� �u��B�V�}�%�Z��&�K�@���e�h�7�-�N��=�DXY�{�g�f�6�D�Y�K��f�f+�a�'�!�:�A�.�/�{�V�~�
�a�<�w�h�	�f�Y�&��A�t�i�o��x
��e���J�	�z�=�4�p�3�L����M*�|�C�|��%�*�G�\�I�Y�v�
�X��5��u�6�U�S�.�/�L�R�w�q�(�S�u�"���9�U�8�'�V�~��"�r	�5�$��m�Y�c�p�Q�j
�q�M��l�'���$�\��e�F�3��y�M�E�,�U��F�O�.�Y�Z�f�D�1�6��o�l�?�n�F�@��a�A�1�P�9�;�Y�!�D�+�j�1�i�2��F��P�E�E�n��E�J��N�@�+��x�3�
�B�A�B�?�7��?�C�[�\�B�[�C�3�g��'�2�F�{�}�R�?�R�~�;�4��\�r��C�a�q�$��V��c�B�C��1�~�C�w��|�+�%���F�a�l�A�R��]�*�y�A�h�?�f�;�b���9��W��x�[�'�p��(�E�v��~�1	��z�C�(���>X�y�)�U�D�E�U��z�;�W�V�L�I���x��<��7�(x�]�e�@��9�Z�@�?�y�w�
�E��	�]�0���~��#�T	��i�.�g�a�4���� �@r�>�oX�}�A�:��u�<�\��	�r��M���>�]����,�H��V�{��Y�B���+��Z
�2��!�"�(�~�b�C�6� �G�u�J�/�O�\�E��E�������E��E�%
s�5�O��?� �`�?�YI��:�q�N���Z�S�m�h�q�!	�+�4�6��|�9���B�!�:�{�v�j�p�u�^�`�c�P�$��A�^�4	�a�l�x��X��s�*��M��0�[~��i�3�M���o��g�N�[�3��
�=�J�q�����E�^�B�I�_�I�4�>�p�J�
�P�9� �
�x�B�m�����I�4�s�3�.�Q�]�[�^�H�e��=�X�)�:�E�E� �M�0�I�U]�o�X�^�#�B�T�G�k��`�(�o�%�]�2�l�7�h�P�{�F�}��G�p�!�A�<�-�T	�V�T�M�s�(�U�#�*�@�;�`�U�x�F�S�]� ��\�8��H�w�?��]	�P�'��w�A�D�Y�G�X�]�i�r�S�H�>�K�M���e�>���+�~�9�C�"�~�|�y}��:�9�7�L�X�_�z�G�o�h�A�p�u�k�_y�-�|��I�6�m�[�j�m��+�}�0�;��K�A�d���
��O�6�G��x�-�&�K�P�?�t�m�"�Q�r�\�B�4���v��e�x�M� �e�p�Z�4�i�*�F�}�h�x�m�T�f�O�O�R�{�A�6��'�H�+�2�p�-�|��Z���3��:�I�~�)�W�s���\�8�mU�2�%�]�D�'��|��l�<��M�B�+�r�<�#�d�H�~���p�$�f����E�c��PS��^��W�V�,�#��J�C��L��j�9�T�:���t�G�6�m��)�p�&�y�i�|�(��k�0�[�������n�0�+�F�R�	�,�!�R��	���mh�j�h�D�o�g�B�i�6�G�K�>�V�>��z�e����(�U�h��3�-��\�s�	�|�s��K��G�z�J�o�B���t�t�S�c�J��L�	��A���1�[�O�>�x
�+�:�I�&�L�5�X�7�!�A�7�m�	�l�
��|�=���'�5��}	�T��(�W�B�`�^��I�r�s�N�S�p��w�N�Y�%�!�i�A�	�p�I�o�
�M��d�2��w��1��T�{���B�@�>���V�A�\�O��j�9�F�k�<���)�|�J�y�M�q�b�S�v�*��b��g�@��G�'�n�e� �8�o�1���^�e�X�X�U�L�]�h�N�O�
�#�v�'�n�	�c�p�_�F���@�!�r�
�b�B	�#)�P�X����Q�
�'�N��)�5�P�$�/��x�6�W�F�V����u��[�-	��)��B�5�S�J�&�X�
�	R� �
��h�U���Y�
�@�P�9	�e
�w�P�N��y.�8�W�F�X�k�`�A�a�b�9�{��|�V�%��S�hv��l�u�B�*�{ �o�4�y�>�P��A�4	�7�
�w�p�I�^���-�*�L��\��$�N�u�!�B�	�(�;�D��I
�x�C�Y��V�����E�l�H�;�a� `�6�J�C�^�� ��u�a�E�W�2�F�A�E�&�V�v�Q��pg�&���������(	�9�$�F�^�B��C�V� ��c	�r�R���N�B��u�t�Z�h�0��l��=�
�T��%��@�I�3�4�!�B	�\�
�i���z�i�8��C���*�m�a��C�� �H��F�s��!�y�\�!�=�x�n���y�0�]�%��F�h��,��#�L�y�b�c�3�e�(�I�M��>�����'{z�$�<�C�B�2�
�m�H�y�&��L�K�M�j���R��P�J�A�>�_�z��r�U�F�M�'�S�R�F�S��h�B�U�f����P�[�>�W�8�=�,�@�C�c�|�8��+�Z�F�-�5�p�
�#�^��C�a�C�^�i�f�BG���^��F	�h�C�B�C
�"�)��@�(�x���l�G�6�_�v���Z�C��+�B�b�'�r�0�@��y�l�O�p��2�%�f�#� {��/�a�6�B�O�S�p��9�f�#�t
�m�B�P�C�C�8�^
�a�k��:�@
�'�E�`�K����F�L���d�,�S�^�W�l�?�#�`�l�V�5�*���v�N���:�<�x�C�4��P�;	�n
�B
���b�?�D�k��E�L�9�O��i�d��g�D�^��5���b���:�G�,�E���%�
�$�a�A�1�T�Y��8�9���1��%��l�_�O�L�A�K�`�U�u�Z�i�@�u�=�+�|��w�`�G�@��y�I�w�L��:�Q��p�s��@�+�D�4�{�)�^�R�i��-�h�i�J�
F�(�o�J�s��8�@*�z�x�%�=�Q	�Q�>^����iJ�O�|�>�A��N�
	�F�(�W�Q�
�Y�I�r�s�<�>�i�6�N�g�+�b�N��L�]�v����-�O��5�-�P�R�C�6�N�E�P�L��U�%��m���d�F�)�	�Y��A��<�I��,�#�9�+�f���V�2�I�.��G�&��3�l�$�K�9
��o�
�]�
�\�]���_�_�t���C�/�^�}�S�$�y�T�O�.U�}�b�r�e�	��3���A�B�j�K�x�y�,�Z��l��2�A�P�0�i��U���CB�|�i��4�9�,�J�q�2�X����<�p�6�s�#�G�p��K�e� 	�s
�L�l�B�U��$�O�"�{��
�U�<�N�|�m�k�$��h	��B�M�|�Ni�7�S�T��g��G�`�3�r
�n�2�_�|��r�1���}�=�"�E��M�?�u
�N�K�n�r�s�2��+��E�F�I�x�e�B�=�b�C�f
�N�
�$�Y�*��y�!�F�=�k�E�"	�o�R��[���.��,�h�{�Q�<�]�P�x�!�{�T�U��I� �S�K�y��r�S�@�5�T�$�c���`�A�/��C�w��.�-�8�	�j	�s�b���}�j�c�z��C�3�S��z�-�m��;�#��h�'��]��!�C�(�v�>�q�F�_�i�B�U�|�&�8�E�B�Dx�/�C�r��1�Y	�Z��7���7�?�D���v��O�H�5�l�=�8�9�O�t��s���\�.�q�D�8�>�u�t�K�j
��/�<�Pg�*�	��W�L��Y�)��o�=�Z�a�s���F�L�@�
�[�`�u
�J�)�^���T
�;�"�3�i��o�@�l�(�~�,
�s�F�S�B��k�v�l�M��+�j��T�F�U�2�X�
�f�U�X�D�;�}���B�GG�@�u�@��F�a�J�B�\
��H��V�W��b�*�Q�L�d�T�o�>�b��^�Y�+�<��B��Q��j�=�.�+�I�R�w�N�t�Q�!�/�J�!�o���[��Y��J	��;� �B�g�[�x�3�^�7
~�"��t��B�#�1� �s�F�;��5�J�;Y�& �c�
�?�M�%��s�o�
�d�\�
�
�9H�C�~�W�x�
~��N���|�)�,�2�J���E���z�h�^�#��A�Z�g�I�K�
�@�A�0�#�B�t�m�]��Z�U�W�P�#��=�R�c�O�(��@�W�/�P�1��s�0�S�~�q�o�B��T�t���7�8��j�c�p�	�.�X
�0�1�A�1��p�-�n�	�1��p�S�t�V�gI�
�K��%
�J�z�]�^�G�:	�}�F��v�g��V�1�S�h�
�D�U�:�R�c�z�o�B�^�c�`�\�I��8�-#�N�|��o����F��\�+�P��j�(�C��2�]�%�s�0�c� �w���3�A�f�9���~�9��q�A�Z�"�-�T�;�\�Y�-�I�/
�B��"�#��I�h�%���t���[�F�<���A
�m�*�o�D�|�� ��P�E�G�0��\�6��T�.�'�\�
�.�I��Q�3�F�L�9�p�d�/�T��L��i�*��X�'�2�,�w�#�

��!�0�I�1�U�4�� �S�H��m�%�f�E�_�J�q�J�<�4�9�A�z��.�p�7�P�0�&
�?�,�p��c�L�B��K�|�I�@�G�[�n�
��Z�t�3�,L�Y�9���M�a� ��\�,�!�/�4�i�T��l��3�Q�8�Y�z���z�J�m�_�I�(��K�M��2��G�D�c�i�E�K� �8��
�&�m�,��z�RC�r�B�\�i�/�4�K�\�/�B�"� ��(�P�s�a�>�>�.�q�<�g�)�o�A�
��L�	�\$�v�U��9�@�
��L�E�n�{��F�G�|�M�\�q��7�	�?�^�>���<��B�}	���
�Q�P'��I]�A��7�_�*��)�_��Z�
���G�X�#~�H�{��D�?�2� �y���@�	�5�J���u��t�p�w��@�T�3�]��|�A�O�L�f�Y�_�R��q��1��G�:�Q�r� �m�&�F��,�m�~����}�
��{���
�`��M�^�w�2	�rc
��-�H�G�(��V�_�t�i�@�S�C��=�V�J�K�V
�Y�h�o�n�L�o�T�;�d�|�P�k�%�t�.�g�J�,��,�o�'�.�w�S?	�V�%�O?�p�!��B�y�:�D�k�v��F���?�F�J��C�F���
������b�5�D�R�=��f�/��=�&�x���x�2�=�
��j�	��O�x�#��
�t�q�N���!�w�0�X�z��B�p�a�s��6�Q�P�9�i�"�U�C�X�9� �$����(�x�<�'�o
�^�y�d�w�4�x�c�B��H�@�z�O�|��L�R�K�1�R��O�A�:�O�S�=�d�j�L�	�fq�f�k��}��F�@�9�M�F�K�P�m�K�\��.�K�[���v�V�C�W��a�r�t�H�y�W�"��D�_�J��D�0��d�
���U�V�:��$�s�{�j�(�]�k��V�s�E�N�:�~�1��	�z�K�4�@�Z�[�Y�;�J����S��d�=�1��q�4�>�	�f�M�$�U
��W��K�D�q��a�L�X�"�@�
��p
�}�(�V��M��p�Y�k�b�C���I�1�$��s�p�&�R�Z�p�M�H�n
�W�G��z�k�=�/�.��~���p�:�o�@�X���/�X�0�q�<�o�f��M�s�N�o�n�p�i�H�w�A�R�V
�{�}��T�8�M�&��}�	�`�L�K�{�O!�,�w�Y�H�u�\�0�y�H�i�L�
y�>�G�Q�x�|	�a�J�4	�W�K�B�q��/�y�j�B��?�	���@��&�/�y�&��p
�Q�z�/�8�\�I�:�W�}�h�h�C�c�2�y�4�G�
�/�'���O�7�d��G��#������D�g��p�#�>���m�7�7�� �i�,�N�a�S�]�L�M��j�%^�@�`�D�C�T�	�y��i�@�7�4�Y�B�-�r�(�D��R�����A�y�X�w���I�P�>��;�(�s�r�l�X�Q�7�x��8�%��r�@�v�%�Q�a��GS�H�K�N��Y�1�0�S�'�(�I�b�i�J��*�7k��"�W�b�Y�J�\�x�=�3��R��}�3�0�m�/
�R�!�c�B�c�<�W�D�i�b�/�,�|��6�@�7�
�\�(�a��9�_�z��$�~�Y�X�S�9��s�>�,�E�9�B�G�9��K�Q�=�(��]
�B�c
�F�A�!�o�J��p�@�-�H�W�^�}�j�U�z�u��8�u��x�e�}�=��Z�9�d�q�n�E�k�^�S�6�
�%�D��"�;l�%	�d�]�>�S�^�z�L
�5�	�(�Z�Y�
�;��d�|���T��=�Y�K�S�
��
�
��m	��5�
�R��P
�s�Z�U�o�@�G�F
�4�x�v��R�Z�O��0�n�T�'��_�l�s���|��s�R�1�0�����
�0�%��D�	�=�a�P�W�v�u�>�;	�'�V�R�n��j�a�F�i�N�,�	��K��x�|�}
�S�U�7�(�?�@��g�U"�C
�U�T�L�J�L�L
�K�H
�[�s�P
�P
(�F�C
��K�_�b

�W�j�W�C
�c
�J�[�L

�W�D
�{�S
�[�S
�_
�S�F
�_�W��W
�g�S�w�b�L&�H
�J�N&
�L


�S

�P�J

�F�_


�J(
�A
�[&�C
�N#�F3�C�N�[�H(�@
�P
�j�[�J�S�[�_�G

�h
�U#�k�T��~�T
���T�s
�E
�F �C
�L
�F
�L

�S

�A
�f
�I�h&�Q�s

�O�S�F�l
�L
�f$�W�b

�W�H
�C



�N

�J


�H�U
�L�W
�C�H�[

�J
�_
�O
�f�C
�f
�F�g
�S�h
�w�[�f�W
�f�J�P

�b
�O
�S
�F

�f

�J
�h&�[

�_

�G�S
��C
�J�L&
�J
�T&�U
�G
�C�N
�A�_
�@�J

�O
�S
�A�b�@�b�F
�h


�S
�f

�J
�C

�N
�L�J


�G
�L

B(갘�~�]*,F�H
�E �P
�uP( �A���wV,J�H

"�v�N*�r
�AB꿊�DB끉�,�uZ
�S�p"�Jp
��
*�Y"B%늢�x�e<

�[�z�R�
v�
��w^���lB
�uX�[""�
��["B1뜺�6�wX�Y$�],5\�["$�[*(�Q�($�S�$�Y$5V�A
�J|
 �_*#>
�Q�(�FB밁�x
�Y"�
z
 �[&(>�NB
붒�%�h�4$�nR

�["�q�B(B뿠�B#삢�I	�["$�l>�R$�h<
�~�[("�
x�A*"�uX�v�B�o�BBO쓹�5�d�N
�["#4�K
�d(�W *�Y$$�Y �v$�T.8�F
"�N�y^
"�v�H
"�uX�uP���wX�lL

�[$���o�D�m�BB"쯨�u�n
�V �r�[$�wb$���uX���uR�uX�Y$$�]05Z�[$&�wZB쿲��=��Q��wX5\�Y"$�
��H
$��
�L�5Z�AB틁��
�wV

"�S�$�uT(�AB"푮��i�R
�/�D�Y&�w`"�Y$�
�
$�Y$�wR(�[,&(�]*���[(�[($R
豈��m�\��.��FV�]�L�d�5B	樂�(�s(�,�P* R讀�^�m�~�{h3�F�N�s�K�F�:��O�3�f�s�t��;��6�P�9�B,量�W�@�9�8
�_�n��|
�	��
�l�E���)�D�_�j�B咽�/�7� �F��w��/�l�wN�B瑩�g�V�R切��K�V�$��>�i�x�o�*�TB郞�C�4��]�a�� �D�)�b` ^a
²�K�]�W�h�o�@�~���|�	�$�@�.bE–mrt�ui��M�O��m�q	�M	�C�V�+�-��@�6�w��\�<�:��]�;�@�G�!�Z�C�t�/�1�3�5�7�9�;�=�?�A�A�C��i�j�f��^�}��Ue	xv�k>�S�@�
�&�@6�E�z�f�@#�u��@�4�q���'�D�s�d��`�~�K	�U�Q�g��	�i�x�@���:��D�@�G��b�,架�x��Y�G��m�>�A�E�M�T�V�Y�v�b�e�G�h�j�n�t�v�y�}���b�
�
����N��$�&�N�*&�3�6�:�C�>�@�D�b�H�L&�R�F�V�X&�]�`�G�c�f�i
�o�q�u�y�{�F�~6��M����m�#���C� �#�&�(�j�.&�2�M�6�:�>�@��N�D�J�f�M
�S&�Y�N�]
�a�e�i�k�q�N�v�x�{�v�����
�
���F���"�"�N�'�+��4
�7�Z�>�A�C��M
�O�N�R
�U�X�[�^�a�d�i
�n�r�w�z�D����~�
���
��N���$�b�(�+�/��=�f�A�E�z�X�[�^�f�c�f�h�j�n�N�r�t�w�|�~�~����	��N���~��
�"
�&�S�-&�0�3�5�Z�;�@
�D�N�H�J�N�T�V�Y�v�^�b�B�e�g�l
�p�t�v�b�}��R�&����!�Z�(�,
�0�N�4�7�9�;�?�J�C�F�K�N�Q�V�X�E�\6�`�N�e�h�j�o�q�t�Q�x"�}���^�	����E�	�#�'�7�N�=�?�D�I�N
�S�N�W�Y�]�v�a�d�g�j�l�o�q�s�v�Q�{"��M�����f����"�y�(�v�3�7�f�:�?�B�E�J�L�O�N�T�V�Y�v�\�_
�b�b�g�i�k�b�n�r�z�x����f��

�����B��� �"�%�)�f�2�7�9�;�=�A�N�E�G�J�N�P�b�W
�]
�b�m�k�p
�s�N�w
�{�~���]�	���J��� �"�f�&�)�+
�1�3�K�6�8�:�?�A�~�D�I�K�Z�Q�T�W
�Z��c�f�f�j�z�s�N�y&���J��	�������� �N�%	�*�/�1�3�E�7�@�D
�H�M�O�@�S�U�[�]�_�b�f�J�l�p�r�u�{�N�
��
���E�
�����!�J�$�(
�,�0
�5�N�:�=�I�L�D�O�R�W�Z�^�`�d�h�B�k�m�v�w�|����Z��"��N���.�$�V�)
�-�1�F�4�7��>�B�f�I�M�f�Q�T�W�Z�\�^�d�f�i�N�m�o�~�u�y�{���N�
����f����R�'�+�-�/�5�7�E�;�?�A�~�J�N�P�S�N�W��a�e��p�f�v�z���f���f���
� �N�$
�(�,�.�1�v�4�7�9�;�F�>�B�I�K�b�O�S
�W�n�b�f���p�N�t&�|��f���J��������~� �N�%�'�*�.��7�;�z�H�L�B�S�W&�_�N�e�k
�m�N�q
�u
�{�}����F�
6��N�����Z� �""�(�,�z�9�N�?�
�S&�V�N�Z
�^�c
�g�N�k�m�~�r�w
�{��J���	"��N�
�� �Z�%�v�16�>�Z�C�G
�K�Z�R�U�Y�a�J�d�hb朞�s��g���5�O��N����\b1忘�Z���"����M�j�����"�*�6�9�C�!�%�,�0�A�7�=�A�V�[�|��I�M�S�F�a�g�q�t�w�{�~���
���G�C�L�P�e�n�s�w��� �$�-b白���u�ub峯�h��e�M��w�
b隧�tb窩�Ub幼�-��J�2��9���?��p�Sb慘�-b閑�]b晃�g��5�Gb熹�qb⁺�/�:�q�!�)��D���
�,�	�D�@�N�z	����b‹�$�j����)�e�#�/�r�W�	�ab⇄�@b⒃�Rb� 〞�j�j�U��r�u�w�y�������A	��"�(�+�.�A�C�F�G�L�N�Q�T�\�c�e�m�B�p�r�u�x��	�����A���%�+�.�1�9�;�A�C�F�@�I�O�U�X�[�d�l�n�p�H�v�{�}
����#�B�+�-�0�8�@�C�F�Y�[�C�]�e�k����G����!�$�&�(�+�3�@�5�9.�?�E�o�r�z�
��
�����@��&�*�,�G�K�H�T�X
�^�i�o��A�������9�;�A�>�F�K
�R�]�c�e�h��B�o�q�x�{�~	���@�&�*�/�5�8�V�A�X�[�d�k�m�u�w�z�@�}���
�
���@�2�;�?�D�I�K�N�V�B�\�^�a�h�j�l�o�r�	��C����
� �+�@�B�N�Q�T�\�d�f�i�s�@�u�y�{�}����&�@�)�A�D�G	�N�X�^�@�a�e�h�n�p�s�z�~�
��@�����$�*�,	�0�C�3�5�8�@"�F�@�i�~����
��F���!
�$�/�5�8�@�K�O�Q�U
�\�^�a�d�w�@�z�
����
�'�@�2�B�D�G�P�V�A�i�p��
�
���A� �#�&�-�1�7�9�<�C�I�@�P�d�f�h�p�u�w�z�F���	���@�2�R�T�X�]�b�A�d�g�o�u��@��+�-�0�<�B
�I�@�T�\�w�z�@��-�/�2�8
�<�@�J�^�y�|��B�!�	�+�-�/�7�@�;�?�B�E	�L�V�Y�\�e�k�A�m�p�x�{�}�����@�!��A�C�F�F�N�S�U�X�a�g�i�l�t�A�z�}�
���� �&�)�@�,�9�S�Z�]�`�F�i�o�r�u����
��@���,�3�N�A�P�S�Z�]�c�e�h�q�w�y�|�@����$�&�)�F�2�8�;�B�E�G�J�R�B�W�]!�i��
�C�����!�)�/�1�4�@�;�?�C�_�c�j�@�l�p�s!��$�A�'�*�4�:�=�@�I�R�E�U�[�_�b�e�n�v�@�	����*�-�0�7�9�A�A�D�G�N#�P�t�v�D�y
���������F�� �"�$!�0�@�R�V�X�[�b�g�i�k�s�y�A�{�~��
���'�*�@�-�3�9�<�K�R�A�e�l�n�q	�x���B��� �'�+
�2�=�D�@�K�_�a�d�p�v�y�|�A�������@�9�=�?�C�J�N�P�T�Z�_�A�a�d�l
�r��@��'�)�+�2�4�:�B�N
�T!�c�B���
�)��@�>�R�m�@�|���	�����@;�4�p�@�s��
�
���@�-�=�?�B�K�R�T�F�Z�]�_�a�d�l�r�t�w��A��	����� �%�*�,�A�/�7�=�?�B�J�P�S�V�@�]�b�i�m�t�w�y�{�~�
�@�������&�)�,�4�<�A�>�A�J�P�R�T
�`�@�k�s�z�|���
����F��$�*�1�4�:�<�?�G�@�M�i�k�m�u�{�A�~��������@�0!�5�W�Y�]�b�d�@�f�j�m2�p�A�#�:�<�?�D�G�M�O�@�V�^�e�x�����@���1�3�6�=�@�A�F�Y�`�{�~�@(��*�0�2�6�C	�:�D�G�J
�Q�\�^�a�j�@�l�p�s�x�|�~��	��A����� �#�&	�.�8�H�<�C�E�H�Q�X�\�F�`�c�f�i�q�x�z�~	��B��	���
��'�*�0�2�A�5�>�D�F�I�R�X�Z�]�F�d�j�l�o�v�x�~���
�
�@�����!�'�)�,�6�:�A�<�?�G�J�M�T�V�^�`�D�d�h�l�p�v�{�}��F������#�&�)�B�<�>�B�I�N�P�S�\�b�e�A�h�o�q�w����H��"�(�2�8�K�@�M�Q�T�W�`�f�h�x�~�A���
��#
�*�@�5�E�G�J�R�Z�]�`�g�@�j�r�t�w!��B�"�$�'�.�1�5�7�<�B�D�A�G�O�U�X�h�@�~�
�����1�@�3�7"�J�m�A�o�r�|�
��
��@�#�+�2�5�8�?�B�^�d�f�i�p�s�y��@��.�I�@�P�d�s�v�@�����$�'�+�/�H�8�<�>�A�I�O�T�[	�_�@�i��������@��"�(�+�:�A�D�B�T�Z�\�_�h�n�q�t��@��"�%�(�1�7�@�>
�F�Q�S�V�_�e�@�t�x������(�@�*�.�1�4�;�?�E�G�J	�S�@�]�u�w�y���	��@����� �(�.�B!�A�c�e�g�o�B�u�w�z����	���������@��$��8��:��=��F��L��N��Q�A��X��\��b��v��|���@������1��3��6��?��E��A��H��K��W��]��q��w��y��|�@����������
����#��%��(��/�B!��2��T��V��Y��b�B��h��j��m��t��z��|������
�@��!��!��C��E��I�F��O��U��W��Z��c��i��l��{�@��~����������!��*��/�A��2��I��d��g�@��n��v��|�������F��%��+��>��E�B��`��b��e��l��o��u��x��{���������D��������"��%	��,��6��8��;�F��D��K��O��U��X��Z��\��_��g�@��i5��m��#�A��%��(��0��6��8��;��D��J��M��P�@��S��W��Y��_��f��o��w��y��|�F����!���A��A��D��[��^��a��j��r��t�D��w��~�����@��.��:��=��@��G��I��Q��T��W��^��a�@��c��g��j��m#��u���A������'��-��/��2��6��:��@��B��E�@��H��M��S��g�@����	������������!��*�B��0��2��5��=��C��F��I	��Q��[��]�A��`��d��h��k��m��o	��r��}��
���@������������)��/��2��5��>�@��B��F��H��K��O��Q��T��Z��]��`��g��k��q�A��s��v������
��
��������!�@��$��(��+��1��4��<��?��A��D��G��N�B��Q��W��Y��\��ePK
!<���B��1chrome/pdfjs/content/web/cmaps/UniKS-UTF8-V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�UniKS-UTF8-HB‖�}��m ��m
,�^7�<b–�{��l�x	����r�
��{�PK
!<��5e��&chrome/pdfjs/content/web/cmaps/V.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�Ha!"�O
�Q	�S�V�[A!a�m�?�2a%u�PK
!<�!�ų�.chrome/pdfjs/content/web/cmaps/WP-Symbol.bcmap�RCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE�p�x�z���}	��z�T�{�V�|�e�X�}��[�V��c�[��0��W`a��y�PK
!<���>kKkK$chrome/pdfjs/content/web/debugger.js/* Copyright 2012 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.
 */
/* eslint-disable no-var */

'use strict';

var FontInspector = (function FontInspectorClosure() {
  var fonts;
  var active = false;
  var fontAttribute = 'data-font-name';
  function removeSelection() {
    var divs = document.querySelectorAll('div[' + fontAttribute + ']');
    for (var i = 0, ii = divs.length; i < ii; ++i) {
      var div = divs[i];
      div.className = '';
    }
  }
  function resetSelection() {
    var divs = document.querySelectorAll('div[' + fontAttribute + ']');
    for (var i = 0, ii = divs.length; i < ii; ++i) {
      var div = divs[i];
      div.className = 'debuggerHideText';
    }
  }
  function selectFont(fontName, show) {
    var divs = document.querySelectorAll('div[' + fontAttribute + '=' +
                                         fontName + ']');
    for (var i = 0, ii = divs.length; i < ii; ++i) {
      var div = divs[i];
      div.className = show ? 'debuggerShowText' : 'debuggerHideText';
    }
  }
  function textLayerClick(e) {
    if (!e.target.dataset.fontName ||
        e.target.tagName.toUpperCase() !== 'DIV') {
      return;
    }
    var fontName = e.target.dataset.fontName;
    var selects = document.getElementsByTagName('input');
    for (var i = 0; i < selects.length; ++i) {
      var select = selects[i];
      if (select.dataset.fontName !== fontName) {
        continue;
      }
      select.checked = !select.checked;
      selectFont(fontName, select.checked);
      select.scrollIntoView();
    }
  }
  return {
    // Properties/functions needed by PDFBug.
    id: 'FontInspector',
    name: 'Font Inspector',
    panel: null,
    manager: null,
    init: function init(pdfjsLib) {
      var panel = this.panel;
      panel.setAttribute('style', 'padding: 5px;');
      var tmp = document.createElement('button');
      tmp.addEventListener('click', resetSelection);
      tmp.textContent = 'Refresh';
      panel.appendChild(tmp);

      fonts = document.createElement('div');
      panel.appendChild(fonts);
    },
    cleanup: function cleanup() {
      fonts.textContent = '';
    },
    enabled: false,
    get active() {
      return active;
    },
    set active(value) {
      active = value;
      if (active) {
        document.body.addEventListener('click', textLayerClick, true);
        resetSelection();
      } else {
        document.body.removeEventListener('click', textLayerClick, true);
        removeSelection();
      }
    },
    // FontInspector specific functions.
    fontAdded: function fontAdded(fontObj, url) {
      function properties(obj, list) {
        var moreInfo = document.createElement('table');
        for (var i = 0; i < list.length; i++) {
          var tr = document.createElement('tr');
          var td1 = document.createElement('td');
          td1.textContent = list[i];
          tr.appendChild(td1);
          var td2 = document.createElement('td');
          td2.textContent = obj[list[i]].toString();
          tr.appendChild(td2);
          moreInfo.appendChild(tr);
        }
        return moreInfo;
      }
      var moreInfo = properties(fontObj, ['name', 'type']);
      var fontName = fontObj.loadedName;
      var font = document.createElement('div');
      var name = document.createElement('span');
      name.textContent = fontName;
      var download = document.createElement('a');
      if (url) {
        url = /url\(['"]?([^\)"']+)/.exec(url);
        download.href = url[1];
      } else if (fontObj.data) {
        url = URL.createObjectURL(new Blob([fontObj.data], {
          type: fontObj.mimeType,
        }));
        download.href = url;
      }
      download.textContent = 'Download';
      var logIt = document.createElement('a');
      logIt.href = '';
      logIt.textContent = 'Log';
      logIt.addEventListener('click', function(event) {
        event.preventDefault();
        console.log(fontObj);
      });
      var select = document.createElement('input');
      select.setAttribute('type', 'checkbox');
      select.dataset.fontName = fontName;
      select.addEventListener('click', (function(select, fontName) {
        return (function() {
           selectFont(fontName, select.checked);
        });
      })(select, fontName));
      font.appendChild(select);
      font.appendChild(name);
      font.appendChild(document.createTextNode(' '));
      font.appendChild(download);
      font.appendChild(document.createTextNode(' '));
      font.appendChild(logIt);
      font.appendChild(moreInfo);
      fonts.appendChild(font);
      // Somewhat of a hack, should probably add a hook for when the text layer
      // is done rendering.
      setTimeout(() => {
        if (this.active) {
          resetSelection();
        }
      }, 2000);
    },
  };
})();

var opMap;

// Manages all the page steppers.
var StepperManager = (function StepperManagerClosure() {
  var steppers = [];
  var stepperDiv = null;
  var stepperControls = null;
  var stepperChooser = null;
  var breakPoints = Object.create(null);
  return {
    // Properties/functions needed by PDFBug.
    id: 'Stepper',
    name: 'Stepper',
    panel: null,
    manager: null,
    init: function init(pdfjsLib) {
      var self = this;
      this.panel.setAttribute('style', 'padding: 5px;');
      stepperControls = document.createElement('div');
      stepperChooser = document.createElement('select');
      stepperChooser.addEventListener('change', function(event) {
        self.selectStepper(this.value);
      });
      stepperControls.appendChild(stepperChooser);
      stepperDiv = document.createElement('div');
      this.panel.appendChild(stepperControls);
      this.panel.appendChild(stepperDiv);
      if (sessionStorage.getItem('pdfjsBreakPoints')) {
        breakPoints = JSON.parse(sessionStorage.getItem('pdfjsBreakPoints'));
      }

      opMap = Object.create(null);
      for (var key in pdfjsLib.OPS) {
        opMap[pdfjsLib.OPS[key]] = key;
      }
    },
    cleanup: function cleanup() {
      stepperChooser.textContent = '';
      stepperDiv.textContent = '';
      steppers = [];
    },
    enabled: false,
    active: false,
    // Stepper specific functions.
    create: function create(pageIndex) {
      var debug = document.createElement('div');
      debug.id = 'stepper' + pageIndex;
      debug.setAttribute('hidden', true);
      debug.className = 'stepper';
      stepperDiv.appendChild(debug);
      var b = document.createElement('option');
      b.textContent = 'Page ' + (pageIndex + 1);
      b.value = pageIndex;
      stepperChooser.appendChild(b);
      var initBreakPoints = breakPoints[pageIndex] || [];
      var stepper = new Stepper(debug, pageIndex, initBreakPoints);
      steppers.push(stepper);
      if (steppers.length === 1) {
        this.selectStepper(pageIndex, false);
      }
      return stepper;
    },
    selectStepper: function selectStepper(pageIndex, selectPanel) {
      var i;
      pageIndex = pageIndex | 0;
      if (selectPanel) {
        this.manager.selectPanel(this);
      }
      for (i = 0; i < steppers.length; ++i) {
        var stepper = steppers[i];
        if (stepper.pageIndex === pageIndex) {
          stepper.panel.removeAttribute('hidden');
        } else {
          stepper.panel.setAttribute('hidden', true);
        }
      }
      var options = stepperChooser.options;
      for (i = 0; i < options.length; ++i) {
        var option = options[i];
        option.selected = (option.value | 0) === pageIndex;
      }
    },
    saveBreakPoints: function saveBreakPoints(pageIndex, bps) {
      breakPoints[pageIndex] = bps;
      sessionStorage.setItem('pdfjsBreakPoints', JSON.stringify(breakPoints));
    },
  };
})();

// The stepper for each page's IRQueue.
var Stepper = (function StepperClosure() {
  // Shorter way to create element and optionally set textContent.
  function c(tag, textContent) {
    var d = document.createElement(tag);
    if (textContent) {
      d.textContent = textContent;
    }
    return d;
  }

  function simplifyArgs(args) {
    if (typeof args === 'string') {
      var MAX_STRING_LENGTH = 75;
      return args.length <= MAX_STRING_LENGTH ? args :
        args.substr(0, MAX_STRING_LENGTH) + '...';
    }
    if (typeof args !== 'object' || args === null) {
      return args;
    }
    if ('length' in args) { // array
      var simpleArgs = [], i, ii;
      var MAX_ITEMS = 10;
      for (i = 0, ii = Math.min(MAX_ITEMS, args.length); i < ii; i++) {
        simpleArgs.push(simplifyArgs(args[i]));
      }
      if (i < args.length) {
        simpleArgs.push('...');
      }
      return simpleArgs;
    }
    var simpleObj = {};
    for (var key in args) {
      simpleObj[key] = simplifyArgs(args[key]);
    }
    return simpleObj;
  }

  function Stepper(panel, pageIndex, initialBreakPoints) {
    this.panel = panel;
    this.breakPoint = 0;
    this.nextBreakPoint = null;
    this.pageIndex = pageIndex;
    this.breakPoints = initialBreakPoints;
    this.currentIdx = -1;
    this.operatorListIdx = 0;
  }
  Stepper.prototype = {
    init: function init(operatorList) {
      var panel = this.panel;
      var content = c('div', 'c=continue, s=step');
      var table = c('table');
      content.appendChild(table);
      table.cellSpacing = 0;
      var headerRow = c('tr');
      table.appendChild(headerRow);
      headerRow.appendChild(c('th', 'Break'));
      headerRow.appendChild(c('th', 'Idx'));
      headerRow.appendChild(c('th', 'fn'));
      headerRow.appendChild(c('th', 'args'));
      panel.appendChild(content);
      this.table = table;
      this.updateOperatorList(operatorList);
    },
    updateOperatorList: function updateOperatorList(operatorList) {
      var self = this;

      function cboxOnClick() {
        var x = +this.dataset.idx;
        if (this.checked) {
          self.breakPoints.push(x);
        } else {
          self.breakPoints.splice(self.breakPoints.indexOf(x), 1);
        }
        StepperManager.saveBreakPoints(self.pageIndex, self.breakPoints);
      }

      var MAX_OPERATORS_COUNT = 15000;
      if (this.operatorListIdx > MAX_OPERATORS_COUNT) {
        return;
      }

      var chunk = document.createDocumentFragment();
      var operatorsToDisplay = Math.min(MAX_OPERATORS_COUNT,
                                        operatorList.fnArray.length);
      for (var i = this.operatorListIdx; i < operatorsToDisplay; i++) {
        var line = c('tr');
        line.className = 'line';
        line.dataset.idx = i;
        chunk.appendChild(line);
        var checked = this.breakPoints.includes(i);
        var args = operatorList.argsArray[i] || [];

        var breakCell = c('td');
        var cbox = c('input');
        cbox.type = 'checkbox';
        cbox.className = 'points';
        cbox.checked = checked;
        cbox.dataset.idx = i;
        cbox.onclick = cboxOnClick;

        breakCell.appendChild(cbox);
        line.appendChild(breakCell);
        line.appendChild(c('td', i.toString()));
        var fn = opMap[operatorList.fnArray[i]];
        var decArgs = args;
        if (fn === 'showText') {
          var glyphs = args[0];
          var newArgs = [];
          var str = [];
          for (var j = 0; j < glyphs.length; j++) {
            var glyph = glyphs[j];
            if (typeof glyph === 'object' && glyph !== null) {
              str.push(glyph.fontChar);
            } else {
              if (str.length > 0) {
                newArgs.push(str.join(''));
                str = [];
              }
              newArgs.push(glyph); // null or number
            }
          }
          if (str.length > 0) {
            newArgs.push(str.join(''));
          }
          decArgs = [newArgs];
        }
        line.appendChild(c('td', fn));
        line.appendChild(c('td', JSON.stringify(simplifyArgs(decArgs))));
      }
      if (operatorsToDisplay < operatorList.fnArray.length) {
        line = c('tr');
        var lastCell = c('td', '...');
        lastCell.colspan = 4;
        chunk.appendChild(lastCell);
      }
      this.operatorListIdx = operatorList.fnArray.length;
      this.table.appendChild(chunk);
    },
    getNextBreakPoint: function getNextBreakPoint() {
      this.breakPoints.sort(function(a, b) {
        return a - b;
      });
      for (var i = 0; i < this.breakPoints.length; i++) {
        if (this.breakPoints[i] > this.currentIdx) {
          return this.breakPoints[i];
        }
      }
      return null;
    },
    breakIt: function breakIt(idx, callback) {
      StepperManager.selectStepper(this.pageIndex, true);
      var self = this;
      var dom = document;
      self.currentIdx = idx;
      var listener = function(e) {
        switch (e.keyCode) {
          case 83: // step
            dom.removeEventListener('keydown', listener);
            self.nextBreakPoint = self.currentIdx + 1;
            self.goTo(-1);
            callback();
            break;
          case 67: // continue
            dom.removeEventListener('keydown', listener);
            var breakPoint = self.getNextBreakPoint();
            self.nextBreakPoint = breakPoint;
            self.goTo(-1);
            callback();
            break;
        }
      };
      dom.addEventListener('keydown', listener);
      self.goTo(idx);
    },
    goTo: function goTo(idx) {
      var allRows = this.panel.getElementsByClassName('line');
      for (var x = 0, xx = allRows.length; x < xx; ++x) {
        var row = allRows[x];
        if ((row.dataset.idx | 0) === idx) {
          row.style.backgroundColor = 'rgb(251,250,207)';
          row.scrollIntoView();
        } else {
          row.style.backgroundColor = null;
        }
      }
    },
  };
  return Stepper;
})();

var Stats = (function Stats() {
  var stats = [];
  function clear(node) {
    while (node.hasChildNodes()) {
      node.removeChild(node.lastChild);
    }
  }
  function getStatIndex(pageNumber) {
    for (var i = 0, ii = stats.length; i < ii; ++i) {
      if (stats[i].pageNumber === pageNumber) {
        return i;
      }
    }
    return false;
  }
  return {
    // Properties/functions needed by PDFBug.
    id: 'Stats',
    name: 'Stats',
    panel: null,
    manager: null,
    init(pdfjsLib) {
      this.panel.setAttribute('style', 'padding: 5px;');
    },
    enabled: false,
    active: false,
    // Stats specific functions.
    add(pageNumber, stat) {
      if (!stat) {
        return;
      }
      var statsIndex = getStatIndex(pageNumber);
      if (statsIndex !== false) {
        var b = stats[statsIndex];
        this.panel.removeChild(b.div);
        stats.splice(statsIndex, 1);
      }
      var wrapper = document.createElement('div');
      wrapper.className = 'stats';
      var title = document.createElement('div');
      title.className = 'title';
      title.textContent = 'Page: ' + pageNumber;
      var statsDiv = document.createElement('div');
      statsDiv.textContent = stat.toString();
      wrapper.appendChild(title);
      wrapper.appendChild(statsDiv);
      stats.push({ pageNumber, div: wrapper, });
      stats.sort(function(a, b) {
        return a.pageNumber - b.pageNumber;
      });
      clear(this.panel);
      for (var i = 0, ii = stats.length; i < ii; ++i) {
        this.panel.appendChild(stats[i].div);
      }
    },
    cleanup() {
      stats = [];
      clear(this.panel);
    },
  };
})();

// Manages all the debugging tools.
window.PDFBug = (function PDFBugClosure() {
  var panelWidth = 300;
  var buttons = [];
  var activePanel = null;

  return {
    tools: [
      FontInspector,
      StepperManager,
      Stats
    ],
    enable(ids) {
      var all = false, tools = this.tools;
      if (ids.length === 1 && ids[0] === 'all') {
        all = true;
      }
      for (var i = 0; i < tools.length; ++i) {
        var tool = tools[i];
        if (all || ids.includes(tool.id)) {
          tool.enabled = true;
        }
      }
      if (!all) {
        // Sort the tools by the order they are enabled.
        tools.sort(function(a, b) {
          var indexA = ids.indexOf(a.id);
          indexA = indexA < 0 ? tools.length : indexA;
          var indexB = ids.indexOf(b.id);
          indexB = indexB < 0 ? tools.length : indexB;
          return indexA - indexB;
        });
      }
    },
    init(pdfjsLib, container) {
      /*
       * Basic Layout:
       * PDFBug
       *  Controls
       *  Panels
       *    Panel
       *    Panel
       *    ...
       */
      var ui = document.createElement('div');
      ui.id = 'PDFBug';

      var controls = document.createElement('div');
      controls.setAttribute('class', 'controls');
      ui.appendChild(controls);

      var panels = document.createElement('div');
      panels.setAttribute('class', 'panels');
      ui.appendChild(panels);

      container.appendChild(ui);
      container.style.right = panelWidth + 'px';

      // Initialize all the debugging tools.
      var tools = this.tools;
      var self = this;
      for (var i = 0; i < tools.length; ++i) {
        var tool = tools[i];
        var panel = document.createElement('div');
        var panelButton = document.createElement('button');
        panelButton.textContent = tool.name;
        panelButton.addEventListener('click', (function(selected) {
          return function(event) {
            event.preventDefault();
            self.selectPanel(selected);
          };
        })(i));
        controls.appendChild(panelButton);
        panels.appendChild(panel);
        tool.panel = panel;
        tool.manager = this;
        if (tool.enabled) {
          tool.init(pdfjsLib);
        } else {
          panel.textContent = tool.name + ' is disabled. To enable add ' +
                              ' "' + tool.id + '" to the pdfBug parameter ' +
                              'and refresh (separate multiple by commas).';
        }
        buttons.push(panelButton);
      }
      this.selectPanel(0);
    },
    cleanup() {
      for (var i = 0, ii = this.tools.length; i < ii; i++) {
        if (this.tools[i].enabled) {
          this.tools[i].cleanup();
        }
      }
    },
    selectPanel(index) {
      if (typeof index !== 'number') {
        index = this.tools.indexOf(index);
      }
      if (index === activePanel) {
        return;
      }
      activePanel = index;
      var tools = this.tools;
      for (var j = 0; j < tools.length; ++j) {
        if (j === index) {
          buttons[j].setAttribute('class', 'active');
          tools[j].active = true;
          tools[j].panel.removeAttribute('hidden');
        } else {
          buttons[j].setAttribute('class', '');
          tools[j].active = false;
          tools[j].panel.setAttribute('hidden', 'true');
        }
      }
    },
  };
})();
PK
!<,ͽ���4chrome/pdfjs/content/web/images/annotation-check.svg<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
   xmlns="http://www.w3.org/2000/svg"
   width="40"
   height="40"
   viewBox="0 0 40 40">
  <path
     d="M 1.5006714,23.536225 6.8925879,18.994244 14.585721,26.037937 34.019683,4.5410479 38.499329,9.2235032 14.585721,35.458952 z"
     id="path4"
     style="fill:#ffff00;fill-opacity:1;stroke:#000000;stroke-width:1.25402856;stroke-opacity:1" />
</svg>
PK
!<h�ss6chrome/pdfjs/content/web/images/annotation-comment.svg<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
   xmlns="http://www.w3.org/2000/svg"
   height="40"
   width="40"
   viewBox="0 0 40 40">
  <rect
     style="fill:#ffff00;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
     width="33.76017"
     height="33.76017"
     x="3.119915"
     y="3.119915" />
  <path
     d="m 20.677967,8.54499 c -7.342801,0 -13.295293,4.954293 -13.295293,11.065751 0,2.088793 0.3647173,3.484376 1.575539,5.150563 L 6.0267418,31.45501 13.560595,29.011117 c 2.221262,1.387962 4.125932,1.665377 7.117372,1.665377 7.3428,0 13.295291,-4.954295 13.295291,-11.065753 0,-6.111458 -5.952491,-11.065751 -13.295291,-11.065751 z"
     style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.93031836;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"/>
</svg>
PK
!<�ڰjxx3chrome/pdfjs/content/web/images/annotation-help.svg<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
   xmlns="http://www.w3.org/2000/svg"
   width="40"
   height="40"
   viewBox="0 0 40 40">
  <g
     transform="translate(0,-60)"
     id="layer1">
    <rect
       width="36.460953"
       height="34.805603"
       x="1.7695236"
       y="62.597198"
       style="fill:#ffff00;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.30826771;stroke-opacity:1" />
    <g
       transform="matrix(0.88763677,0,0,0.88763677,2.2472646,8.9890584)">
      <path
         d="M 20,64.526342 C 11.454135,64.526342 4.5263421,71.454135 4.5263421,80 4.5263421,88.545865 11.454135,95.473658 20,95.473658 28.545865,95.473658 35.473658,88.545865 35.473658,80 35.473658,71.454135 28.545865,64.526342 20,64.526342 z m -0.408738,9.488564 c 3.527079,0 6.393832,2.84061 6.393832,6.335441 0,3.494831 -2.866753,6.335441 -6.393832,6.335441 -3.527079,0 -6.393832,-2.84061 -6.393832,-6.335441 0,-3.494831 2.866753,-6.335441 6.393832,-6.335441 z"
         style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.02768445;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
      <path
         d="m 7.2335209,71.819938 4.9702591,4.161823 c -1.679956,2.581606 -1.443939,6.069592 0.159325,8.677725 l -5.1263071,3.424463 c 0.67516,1.231452 3.0166401,3.547686 4.2331971,4.194757 l 3.907728,-4.567277 c 2.541952,1.45975 5.730694,1.392161 8.438683,-0.12614 l 3.469517,6.108336 c 1.129779,-0.44367 4.742234,-3.449633 5.416358,-5.003859 l -5.46204,-4.415541 c 1.44319,-2.424098 1.651175,-5.267515 0.557303,-7.748623 l 5.903195,-3.833951 C 33.14257,71.704996 30.616217,69.018606 29.02952,67.99296 l -4.118813,4.981678 C 22.411934,71.205099 18.900853,70.937534 16.041319,72.32916 l -3.595408,-5.322091 c -1.345962,0.579488 -4.1293881,2.921233 -5.2123901,4.812869 z m 8.1010311,3.426672 c 2.75284,-2.446266 6.769149,-2.144694 9.048998,0.420874 2.279848,2.56557 2.113919,6.596919 -0.638924,9.043185 -2.752841,2.446267 -6.775754,2.13726 -9.055604,-0.428308 -2.279851,-2.565568 -2.107313,-6.589485 0.64553,-9.035751 z"
         style="fill:#000000;fill-opacity:1;stroke:none" />
    </g>
  </g>
</svg>
PK
!<�w2���5chrome/pdfjs/content/web/images/annotation-insert.svg<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
   xmlns="http://www.w3.org/2000/svg"
   width="64"
   height="64"
   viewBox="0 0 64 64">
  <path
     d="M 32.003143,1.4044602 57.432701,62.632577 6.5672991,62.627924 z"
     style="fill:#ffff00;fill-opacity:0.94117647;fill-rule:nonzero;stroke:#000000;stroke-width:1.00493038;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
</svg>
PK
!<����2chrome/pdfjs/content/web/images/annotation-key.svg<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
   xmlns="http://www.w3.org/2000/svg"
   width="64"
   height="64"
   viewBox="0 0 64 64">
  <path
     d="M 25.470843,9.4933766 C 25.30219,12.141818 30.139101,14.445969 34.704831,13.529144 40.62635,12.541995 41.398833,7.3856498 35.97505,5.777863 31.400921,4.1549155 25.157674,6.5445892 25.470843,9.4933766 z M 4.5246282,17.652051 C 4.068249,11.832873 9.2742983,5.9270407 18.437379,3.0977088 29.751911,-0.87185184 45.495663,1.4008022 53.603953,7.1104009 c 9.275765,6.1889221 7.158128,16.2079421 -3.171076,21.5939521 -1.784316,1.635815 -6.380222,1.21421 -7.068351,3.186186 -1.04003,0.972427 -1.288046,2.050158 -1.232864,3.168203 1.015111,2.000108 -3.831548,1.633216 -3.270553,3.759574 0.589477,5.264544 -0.179276,10.53738 -0.362842,15.806257 -0.492006,2.184998 1.163456,4.574232 -0.734888,6.610642 -2.482919,2.325184 -7.30604,2.189143 -9.193497,-0.274767 -2.733688,-1.740626 -8.254447,-3.615254 -6.104247,-6.339626 3.468112,-1.708686 -2.116197,-3.449897 0.431242,-5.080274 5.058402,-1.39256 -2.393215,-2.304318 -0.146889,-4.334645 3.069198,-0.977415 2.056986,-2.518352 -0.219121,-3.540397 1.876567,-1.807151 1.484149,-4.868919 -2.565455,-5.942205 0.150866,-1.805474 2.905737,-4.136876 -1.679967,-5.20493 C 10.260902,27.882167 4.6872697,22.95045 4.5245945,17.652051 z"
     id="path604"
     style="fill:#ffff00;fill-opacity:1;stroke:#000000;stroke-width:1.72665179;stroke-opacity:1" />
</svg>
PK
!<�g{��;chrome/pdfjs/content/web/images/annotation-newparagraph.svg<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
   xmlns="http://www.w3.org/2000/svg"
   width="64"
   height="64"
   viewBox="0 0 64 64">
  <path
     d="M 32.003143,10.913072 57.432701,53.086929 6.567299,53.083723 z"
     id="path2985"
     style="fill:#ffff00;fill-opacity:0.94117647;fill-rule:nonzero;stroke:#000000;stroke-width:0.83403099;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
</svg>
PK
!<FJcX��5chrome/pdfjs/content/web/images/annotation-noicon.svg<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
   xmlns="http://www.w3.org/2000/svg"
   width="40"
   height="40"
   viewBox="0 0 40 40">
</svg>
PK
!<�ZS3chrome/pdfjs/content/web/images/annotation-note.svg<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
   xmlns="http://www.w3.org/2000/svg"
   width="40"
   height="40"
   viewBox="0 0 40 40">
  <rect
     width="36.075428"
     height="31.096582"
     x="1.962286"
     y="4.4517088"
     id="rect4"
     style="fill:#ffff00;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.23004246;stroke-opacity:1" />
  <rect
     width="27.96859"
     height="1.5012145"
     x="6.0157046"
     y="10.285"
     id="rect6"
     style="fill:#000000;fill-opacity:1;stroke:none" />
  <rect
     width="27.96859"
     height="0.85783684"
     x="6.0157056"
     y="23.21689"
     id="rect8"
     style="fill:#000000;fill-opacity:1;stroke:none" />
  <rect
     width="27.96859"
     height="0.85783684"
     x="5.8130345"
     y="28.964394"
     id="rect10"
     style="fill:#000000;fill-opacity:1;stroke:none" />
  <rect
     width="27.96859"
     height="0.85783684"
     x="6.0157046"
     y="17.426493"
     id="rect12"
     style="fill:#000000;fill-opacity:1;stroke:none" />
</svg>
PK
!<	]J�ww8chrome/pdfjs/content/web/images/annotation-paragraph.svg<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
   xmlns="http://www.w3.org/2000/svg"
   width="40"
   height="40"
   viewBox="0 0 40 40">
  <rect
     width="33.76017"
     height="33.76017"
     x="3.119915"
     y="3.119915"
     style="fill:#ffff00;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
  <path
     d="m 17.692678,34.50206 0,-16.182224 c -1.930515,-0.103225 -3.455824,-0.730383 -4.57593,-1.881473 -1.12011,-1.151067 -1.680164,-2.619596 -1.680164,-4.405591 0,-1.992435 0.621995,-3.5796849 1.865988,-4.7617553 1.243989,-1.1820288 3.06352,-1.7730536 5.458598,-1.7730764 l 9.802246,0 0,2.6789711 -2.229895,0 0,26.3251486 -2.632515,0 0,-26.3251486 -3.45324,0 0,26.3251486 z"
     style="font-size:29.42051125px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.07795751;stroke-opacity:1;font-family:Arial;-inkscape-font-specification:Arial" />
</svg>
PK
!<
�]��:chrome/pdfjs/content/web/images/findbarButton-next-rtl.png�PNG


IHDR��7��IDATx��A
�0��w�9���R"�^��B*��BR��I��TG�}��Wş�(��_��7^�`�/��<%��{5�h�`�σ!Oǽ)�Psg��v��'_y�'_qa�d�0�Z�R�Wv�%ÀR���Ho��IEND�B`�PK
!<�V00=chrome/pdfjs/content/web/images/[email protected]�PNG


IHDR  �s��IDATx��m.Q��Y��m�{��J|�/cJ�R-}��&����='��?o���>�H}B]m>��~���wFӜ@ʄa���1�r-^y��@��(#�dHo����Mt��h�̣��A�c����X�˝=�k�zR@��}n�)������]�l)����P���b�ʃRJ�2.[t�dynK��QD��$g��
Pb��*#�D�q�J���<��y%r���D�z�{��IEND�B`�PK
!<�<���6chrome/pdfjs/content/web/images/findbarButton-next.png�PNG


IHDR��7��IDATx���
�P���x/+�E3�^+J�P����u��&�ӱ��.�����;�xPqc�oK?X��[2�J�op�Q0��>6���n&�P���笽�
4r�5�����ʹ��ؚI��8zK�t���k�IEND�B`�PK
!<JMa((9chrome/pdfjs/content/web/images/[email protected]�PNG


IHDR  �s��IDATx��k.a��;��q{��J|���!BDH����8ԴN+�<�痼���?���2P�Jx�އ7&~�����
tF^�x��//\umaO��@���ZS�\�)�b���
<�<q�{�>��D�݅=ri2pkhrQ�G�^�����1���**?��/c�}��ED����G<�*��\7��Y�O�v4�x�ĘL����i��e��e��`��ԟ<N7��Μ�y��$]�JXE"jIEND�B`�PK
!<�<���>chrome/pdfjs/content/web/images/findbarButton-previous-rtl.png�PNG


IHDR��7��IDATx���
�P���x/+�E3�^+J�P����u��&�ӱ��.�����;�xPqc�oK?X��[2�J�op�Q0��>6���n&�P���笽�
4r�5�����ʹ��ؚI��8zK�t���k�IEND�B`�PK
!<JMa((Achrome/pdfjs/content/web/images/[email protected]�PNG


IHDR  �s��IDATx��k.a��;��q{��J|���!BDH����8ԴN+�<�痼���?���2P�Jx�އ7&~�����
tF^�x��//\umaO��@���ZS�\�)�b���
<�<q�{�>��D�݅=ri2pkhrQ�G�^�����1���**?��/c�}��ED����G<�*��\7��Y�O�v4�x�ĘL����i��e��e��`��ԟ<N7��Μ�y��$]�JXE"jIEND�B`�PK
!<
�]��:chrome/pdfjs/content/web/images/findbarButton-previous.png�PNG


IHDR��7��IDATx��A
�0��w�9���R"�^��B*��BR��I��TG�}��Wş�(��_��7^�`�/��<%��{5�h�`�σ!Oǽ)�Psg��v��'_y�'_qa�d�0�Z�R�Wv�%ÀR���Ho��IEND�B`�PK
!<�V00=chrome/pdfjs/content/web/images/[email protected]�PNG


IHDR  �s��IDATx��m.Q��Y��m�{��J|�/cJ�R-}��&����='��?o���>�H}B]m>��~���wFӜ@ʄa���1�r-^y��@��(#�dHo����Mt��h�̣��A�c����X�˝=�k�zR@��}n�)������]�l)����P���b�ʃRJ�2.[t�dynK��QD��$g��
Pb��*#�D�q�J���<��y%r���D�z�{��IEND�B`�PK
!<�ܝpFF(chrome/pdfjs/content/web/images/grab.cur  0( @��������?�?�����ld3`3`������������������������������������������������������������� ������������������������������������������������PK
!<b�<�FF,chrome/pdfjs/content/web/images/grabbing.cur  0( @��������?�?����`��������������������������������������������������������������������������������������������������������������������PK
!<����	�	0chrome/pdfjs/content/web/images/loading-icon.gifGIF89a�����������ర���莎���Ȝ����ب��������vvv������hhh!�NETSCAPE2.0!�Created with ajaxload.info!�	,�  �$AeZ
�<䠒�ÌQ46�<�A�
��Ha��:��ID0�F��a\xG�3��!�	�O:-��Rj��TJ��*		�
t
��������~�"ds]�
�)t��-"�i;H>�n�Qg]_*�
�R�3��GI?
�˴�v$ý�j3!!�	,�  �$�0eZy�0��q ��P�УW�)";�qX�^�D50���	Ո<H3!��k-na� �(�i���d�$P@yw`�J��#
?�
y
�����o���g��
f���'8��{'Cp`jn�"�
2�{�`x�jy�4�C,�4��o#n�$��ß��!!�	,�  �$� eZ�$�2����q���ҢE� ҉�p$H@D/���G�DÄj8v#��P((Dƶ�� �N�(3ܴ�#y�(@	gUx*
kK�)?K

���������$��"
�*���K��
�W����x��?�G��#�Wк��n�h�K,����+���*!!�	,�  �$ eZY$1��Q(c����Ң�O'"�������� 1
��q؍d"�A
�V�x8p��4988MRC�@	e*3@
iI�)
'
?I���@�������,����#
�����5�,�����"�E��z�?��@�E���@�����)�����*!!�	,�  �$�(e�$ÐĠ�
C�E1
	;���('2$��!��DS%!����)e[��TE50�p�Ũ� �F�
�{V8,�%`3
gI�w�3
���*������h�"��)	q4��)#�g��#S�$��"�$
��>��%�`���r�J{
1���$ʈ��!!�	,�  �$@e�6$��Ơ��`�3*�=��
��� P�\"F�����`P�-����d5V�"�2�|?n"!(
�����)e��4xyc?		
��3������
�#wyJl%
o�^[b_0	VT[0m�
$�4�>�'VZ
�c��3��$X���%!!�	,�  �$`e�����:D3
�H0�,'j0�Q�s��L(2HM��j#�ȉ�B�\O�i`u��=Y���EVL=I		
��>������
�suIWJm|	\"_�b0B��cV"d]*K1"	H|@B?�I4��#�S$�-|�|!!�	,�  �$�4e��a��:D�h�����I ��/�K�$W-�	0(`3���F��=��pf@�t�Q����
{f~*���y��S*mg)	�enu		E^Z^	g@	kw(b&	-w#"�xW"�t
#�#�%U$�`�t�o!!�	,�  �$�4e��a��:*����1����v/�Kd��z���<�p6%tP5���S|���H(�F���c���`05xz*|~�v�G�0t#
	Fh�0
#Cd
1
�I�#(i	-��	�	uEL	q��"	h%�$�$<��q!!�	,�  �$�4e��a��:*����1����v/�Kd��z����p6%tP��t����5ũ3��n��G$�
�@a���wy{hoFS>k#	FY"�%E
Cb
A�I4$	(z�:2��
mI		L�l#�#
�F�#�#�>�F!!�	,�  �$�4e��a��:*����1����v/�Kd���V�tKG��22��7�D"����$)����Q����qp8y
l
|~���6z�w2j#F
�"	�%
V�C�
]�6a$�
Q�:2	\�EF	I�&�x	�"͓�F4$�]�#�x!!�	,�  �$�4eZi䠒J�16�<���B�
�?$r��T�HzDP'"l�(�1�5�y���8tg���p,�q�M��*		
q
��������"}�#	b?y{		{)�s	-s�:�9>e�E,C\3
��^�3[�
������S��|���?!;PK
!<dpor��1chrome/pdfjs/content/web/images/loading-small.png�PNG


IHDRԯ,�acTLL�-fcTLd�3�G�,IDATxڅRM�a�
�D�-I��:x�6��bKK�D���mK����A��=���N7��!�@0�ޥ�(�
��3A�w@��z^�K{��}�y���}�q0Ǝ[�G�ŷ	Ӏ�{�w���8��e�.26�ag'6(��kH�j��S�u
�Ok��L��s���ހtt:���?�ár�~�4훮_Qq�Pؠ�s�,/>�tFEy0��f�A�m*�Z@x��]�|~=Ӡej�Ml�L&2�1���K|�"�A�S�V��XU�@4}@�<��r�|�v�B�Т0^��J2�\s87�:ӷ����;�^�9mğ��*���;B�يA�&6���T*�Ǽ�~e]0j��t��>B��;9������>��Z��@���ޚ�+^��#6zB�ŋ�`��a#�RIN$ס��4�d�����Zc$y(��*��v��Z�6G�D�����I�V���u�3慝�66Վp�����'�b�|ʡ\�0\�e�5�(�}�G�k���b~4�r�K�̈�x�I�X�J{{���&O�͢��1+~Oՙ��@�PfcTLd��ϭ+fdATxڅRAhA]�EXP�%�A��VD���*�Hl�=X��f%xj$��P�`�rH�RP��=�CA6[�!db�6�nhIv�qc�Ǽ?���g���1Ɔ<�
����[ ��w>������8V�o��z�&k�m�a=��N��z��W��v{Tj4����_�V�����h�3��ˢ(��3<�0��
�W�ً�l������t:}�qB.������T*��nP���L&=��[���f���( ܈��7(ٲ,%�H�Q.�
���H$rU�c�P�N6/I�-�'�+t�Qu���	�������}:���·ZMyN����j���خn/�~#pl�?9��]
��r(����4��
 6E�.�>��|�Q@��G���$�5���L{�TjB�h�3���9Y�g��`�3$0�
�`?��#
iE��>� At���2-%��$�8��ICZ`���bq�f��i�=�C|��q�W��ya����៤R����Q���:6�����q�
�%�l�>w~fcTLd�EY~�,fdATxڅSMhQ���l��'�-�^�S$�i
6m)�M$FR�\r2F��BzR��Ab�/�"���ò!,�Ĥ��������o�>Y�>�|3�o��oa�u�Y`�a����pն?E[_f��¹�Z�R�\������q�4�[(��eY�q~Ue�_����)�b�^�%�[�Ng����f���-�0>
�0=<��G�'@X�d2���i"��Ax����g�;�w�ryݲ�'�~M��K�R+�t=�z�պ�4 \�z����0�5$IzE��pFś��U���mF�l�v�V{B0O�+,1�q��^�]D����L|�Ѹ�����GTU��WT��A�{��`�P"pr�;�,l�K9��S����߭b7�s��(ǹ�G���ald�5���L�t:}]��0q휱���R$��^�v��D�4m��Dд�1����h4�\�$ �t��a�F���v�M:��_I���X*�.�̌%�IzL��I��Q<V,/���$��H��j�v�?6��ή��\bC�
�����.��fcTLd����;fdATxڅR_hRa��T��Q��
4[���E��jy�ݲ�K�(A�=
$�ǡ�$���K��+��1��AEa����%�����;�s�=�9���9�c�2��pǀe���3{��x�՚��<�7A4�h�����}�3J��4����\�W�PK&�&�{+�ʹ\.�����z�>���b��N�a�I`�O��n������drq~a��'�3�l�U,[b����3o�4�{)���ߨV�nJ����n݈��j���|�4�K*�zA����b��o9��B���t���f���!��@f�=(
7��}Ll�Zۜ���|6���P����]�?�h4�{{��f���5m6���H�	�{r`%����)�bu�	`����~	�H�k�(�g�ʚ��C]�Q5ce��Bv,����Sg��g^��9��Ri�����T�'q�|�!���3�q��6�4���j��T™8�;�o,0��c"�8�8�}>�yH�q�6��r<?%���$�LF����җ��C�f2�qB��`��𘠓�E�fcTLd�E�~6fdATxڅ�ߋQ�c�P��i)�v��Jv�2�
��ǔ��L7�-����^|�/(%_�@�G#�%LV��e@*	D��U��{�	I��{�9�ܹ��_�u�4�o����fH8$����|Џ��EM�^��X �Q�Î��H���j(}���>��:��]�t�qH>��{�Y�ٴ�J%�R�0EQ���\��g���>��xX�nl\N��� �Dx��$����'�Hĭi�&�ނ�`�j���[QZ��jW�ff`i;��r�N'�H$��H8���p8|��r��&c̕���t87�5�p0z�z��f�<�̪�����g�v��֩�[���c�v�������>Ԋ�j]�F���2'��r����\�=��,����Z�K�
�z=z���~���w���|�/P��!c#iZ�B�UU�
�|��6�|�;�(ކ^j4��,����4��d2���l��h�O�H�PZ�d�����46�ҘXpd쎩Tʡ�����!��	(Β��in�͙��kƐ�E����1�m����4����؈�|♧(���fcTL	d��v�"7fdAT
xڅS_hRQ���zՆ����"!�m��Zkx��=������|����D���'�@d*�0Cq�/�ܐ��{�}r��]�8����9��ia���9�:�\<���{	*�d�x�
��x|J�e_Oy^"N����R�i����B��&�v��(W��r��&N{�R�-ـO�5�s���ivum�Q&�Yr�\�p|�@|wgg9<��k`��,��<�	�B����P�z��{�hB��h4�a�A슑��M��IQ����'�h��鼫W#��f�mm��V�3��	e��5�͗j6=1��v?�2���@�ppx����`���?��~fLކ�6q�Z�Y��4��hp��1'c�\��f[ �W�x;��C��$m�淢(?�.�q��������2ߔ��=��A؅��\����y�"δZ-�V�ST`�(�Q�(�cI�I6�Db=�L�l�:a��
�#x�v�D��Љc��X$[������\��C��u�x>���֨�|gui�h4�I�X��/h�f�X���2�.�/�����k��fcTLd�E�=�.fdATxڅS�k�PS�hk�8,l>�TPP*+����c��u�������@��QX�4���n�6��@��6X�h����.$��#�,�>�{�sNN�	�B�|0?r�b(�H&m_@�|�c5�	
�B�4�
]�x^G�	�k��#G�[�q��j�ʡ���j�W��9�x&�2��w1�_�ەfߧR�R�������b����;���0s����7�����V�5
g!��<�h4b��}~.@��R���;#I�R4}ĸX$y��f��~�sp���ẙ��鼥�]���(k���d�3�''+�����U�?�n�EBL	�r�P��g.����ܼdO<�ry-,b�K�����>sK0Eb�/&17A;52F�*s�Lf
�S�
G�eq��n7�.$ځ�տcL&��y����(qL�l6���|ibY�u]�A��և|>�r��go�8N����`p�_�e�	�6��b������� ��Kp���8E^�T>��Xf.1˲/�\\h�4Mۣ�v��[��]���F��
���i߻�fcTL
d��*O�"fdATxڅRAkA^,��C�x�.
)��P(�C
B�B�B��4�dザ=�AZT�/���z�X��МDj��(V�A�ŝ�`�He��ͼキ3��9w��-�2�w�A�]k�
����<�r�ܪi��'����4
@��
�]��$��r9c��Tk�@�V@c�R��!���|��:m��F7�����G�u�P(D)��
�;q���]]h��Oh�C\"
@{���<��c��r�a��v?M��{>�oM����}�L&���-qUqa�n���l��߽�.��~v�/6ꧧ��h4��%�6].�V&���\_0'7���,�
r�\1$\�_��vF��K��qc��=c�\	$�G����e"_�/Sw��j} ��&?B.�p8��i�6�S6�����߈���0�y��"	O*u��R���g��	S����Hd�q��3v����x���'��ěsg�t:o��)�Jiy��O*rM��ΜN�4�Ϥ�	�Ő����W1lN�S��1����%�е��pM��fcTLd�E��X(fdATxڅRA�a^̈��-E�e�=$
2B�u%,�0K.h;��/{J�ݹ.]›,'/^$;��d`E<4ت˸�����̿H2����{�ͼ���5Ƙk�������� 	X��7��2FR�q,P*��QJ�&��	�Kȱrk2�D���7���F������Z���%��[��L��
���)�� e2��rY�F�k ������jlm��ĕ��b�y��@��{
o~�2�r��YG�i��l6�s����{V(�P�ӌ���|>G-��/D�*�0�W���-��c|;g+�D����K�1]��m���V�΂;��){a�b��{@��٩�����H���xX<�2�`SfX'֏z�����ō��sY�k�����R�u��j�����n�;pJȷ)c����۳B����P`�ʰ��J�O�ӱ�5���b:e-rJ���x��P`_�5�:��/n�pu���.^��J��
�E�_(c�ȋ�bV�[��-��
L8d0|�*��<��;/2�ؘ�q�;v��5}�fcTLd���+H-fdATxڅSϋq3�� ��l��A��@���n]�m3�%����R�����Rѩ���L"�*�$�`�`�B쀩�ut��y�L�؃|޼�{��7��1f7��<�2�"���w	:_`l'�e�|>QӴ{�` @x�8 >PU�8i�:����r���N�]�Uc�Z-F��U*�i�OQ��|�띂���b���D�#�%/
�	�H�k�ƛ�S�Ԫ�tE
4��%|s`qw��X$��(b2��A���$��׳�l4\�Y��络�d���;�_a�	U��h�U�fe�/Y^5�s0⡪~f�go8~�����Q@�K�6����'*I�3���=9��b;�r�B�s�kZ�o�UU_Y�v��H;f���������/�Jw�3����q �N���.L�3�Ć=v8�}u�����~�����6dY�1M{�n�itAVDQ\1�q�1�%$|�5ކ���/�ƶ�~��^��~8CZ����Q���ـ{�9N�E<���¿���4kJsN�s�X�V�����l���3�n�^�T�d����f fcTLd�D+��1fdATxڅRMhQ%*.�(�Ei�`�
�T��`�a�⡵Y�D�P! M@1'	HK���r
c*ޫ��K��,䇄@vI��9#� tM��7�̼�f,g�27����٨��,~a�%B�0j��d2˚�=�2�ȱry<搣�_�yUU�u��
{���Z����Z9����j�/�Mh�E�)���<]+
�,�ބ�]�|>��h�ƭ��O掏��u��F��'8s5�f���Ƀ����bḀ��>���T*R���2�<��d2���p<�
}�@��E�F;O1��ڠ�;bI���N��$i���k4��@�Q߲�w��R���3��vpx����\��_���zZ�~�;�D��Ţ����n�?f�Y��K���D"���`c:�~)��պ2��o5B�N�s(�89���I�n��
�q�<ϯO�q�ذ;D#��P~�zo@�]�w�N�uH�Q�lz#���=a_���_A���x��X$��ݚ5�y���K"�bTF�1�v�e�]t�s���a���f� fcTLd����1fdAT8�c`@���gGb��?��*���@�姁0������T ��Ӏ{��%<����] -����t�!���J�/^l����?��_�|��?j��t�R¥�W޿o�|���� [���@|����[��z�r�9s���� ��~��؜�w�_��Raa��Tt�����߿O�1�#Al����H���SJQQQ(H/L����Y��������q�����YCCC����'�+�B��
?~���f3:�z��U,v�Q����	���_
�,����10n޾�
(_UUՠ]�v��MO:�0y�����HT/X��������X\|��	d��A	���;@ܹs������b>d�A�TVVڃ�F4���ė��Ų��X�(���>|h
$~��H�۷o���CRRRB��Q
��~��������j`����jN�ڬRĒ~ܶm�����R�M�H�����"(��f�C|�$����K$.\H�Xb��I�j�<o
�*L90ɦ�ߐ+ptEXtSoftwareJapng r119'�aIEND�B`�PK
!<��}�??4chrome/pdfjs/content/web/images/[email protected]�PNG


IHDR ">��acTLL�-fcTL "d�۠�f�IDATxڵ�mL[U�k`#�`��4��(��D�/F���.Y�
���1(-K�D�MR>8�.1~1cS�D�M��'?�1"f�M›��.0Vz���O9oϊ�m<�/��<���Z�&�y-�~�nma>P6؝�|��Ư#���~'ؑ^pUm�8��ƀ79�6�i$@#Q)�#*W��[��tz��~"4�͂?	��P�4�V��I�0-�!8��~��
�f6`Ճ�n-֛
�z1��}~O'	���>
N�"��������Y����)�w���(��9��׸v]ӴkB@�Q���8
�v�0>p�8{��I|
��HP�`0�W�����91e-z�)��l���VWWF�<���AF�9��3�.��P]>99y�߀��H�=av861eG���(ڏ�Pm<`�Q�������"��3�
����KYYY������������0+"l⛒�����n�A����0���\MlѝI�=44tC�Q�	�(F#���z[
6K�z������Fb�����9F1��J���z�����c�7��2��	�Jy��#�K#�����5K@�؁����iaa�-Ue����������D{h(���W��1:v+e����a τ�<��|�b�;�Q�k��挽����z������&1�fSi]]]�͛K��. �A!	x������M�ߒf�@��K|-~���Z|��t����(��`���s-//��W��3�Gc����^_�3]���U�h�#�D"��]-�h2�4�o�����:᜚��X[c/B�4濜��F�
����nP
�t��MO>�X"��]�<N�.C��A;�_��+�
(����w܆Y�1��S�;`�rrrG+������0��.#��(����^c�<�>wLOOW�;����\233c%[�A���*^�Ŝ�S���r[8n���t�ǧ��y�^�	�4�`��^P�(�$d���ۏ��͵����OyڥU����,8�Kq��S����q�������A�$��n�C��%/"��QCu�D9�Q�s��:�B�(�泜���m��_8�0)qK�]�	gu�(�ļ%%%��,��Lmb1ū���aZ��﯁�I���s��ܢ��qfe�^��1�]��K�PnnnU~~~-�c��Y�DZ�G��l�V̹�T*	y�\����
O8��wj;�J3e[�r�M�$�/0��ǀ}_>��fcTL "d�@�+�fdATxڥ�mL[U�k��d!��L
��l�%|b_T�V��V@4P
--00E�5���-:�"�,����e��DB�? �-6F$-�{��s���	���s�����眞��kc���M�xzG��~�\U�/ ���lTU�-����ʃ�O4�\���0�!������f�(�_uAשL��x��껁|ăxD��E1��Y��LM��$I�H?�>G߂6P�y���y�~����>Ø���|�qG�PtЏqY�ĶDV������+1����xUaJS�5��~��666>�{�(��0�K��sp�ÿ����qЪ(�U�{ ~���ڎ�{�A\Wq���zqsC�����=O2��<7�@��;::��!�#��
pT�>�:x\o3E9��0��GF~z�wbb�"W{X9f [}|�~��S�hQ� T)�RM=p�{�����H�.c�������qqqֆ�Gcc�cWK�A���Ds��]�zzz�'$$���s�C����xߖ�noo�X__���H&���l���=%�6ܭej����>�9	I������|n	����*��C?7���\3v�ɜ�Z2�z�7n����ȗ>j�XK��l���gq�^z111F���z�/H�����q��by�g��}Em�,pXb	 Y��m����5 ݍ艳�ܹS_SSS�5֖YRRR����"����Q	<��)��C��d��#ѱ>��)��h�B��h�$�E�"�]fr8�KKK$8�-��qM pӜ�����K�<��v{��F�c6�W�S��O��ݎ"Cۓm`��Ɇ��+'�Q������~�&�{v�����#��:;*3��+`mm�a�՟~���m�ppo+��݄���ҡ�0�/@W��4J��C�c���\H�555��i�����sMzz�钍q\ŢmI���sK1jii)Z]]u���;=O�v}�\�����J��Y(�EdH��b�ծuO]]���l%���(Ր"�� I{�Ѐ��(7\)�fff���Z�d��
���m�[5<	r��(���Z�<������{~���\��vѰ������2����Cp�쁁�.h���N�QH
�322��L2��3�!nM.K�Ikkk���T555�zn�2 fAP�jz��XQ$�v�M6L�ɤ������<:8���m���T�^j,�C�d�H�)Bpl6�s��m��B�E��z���`�'v�r�=�oQ����D{�k~fcTL "d��E�[�fdATxڵ�]L�U�k�ᒁ	�A�Rp	�b^hRbj�tv�)�в~`�K�@LQ���ʅK0ꜢK���IIu�tcL\W��Ѿ�Y�5am_���<�y{l�Ɠ�rN�9�y�G���d�����|�7�0�����e�$��Uu���U�E9���0Cd�rN�Pf�>Ǎ�(�r��]����,�j�ؗ�0��ׁ�ؙ˪�\�p�n�������:G�
?)�z���(ʏ��UUyLw��uL�@���b^��˨�b�Z(:j8���Җ��E?�]Nಝ@������z&�͞a|.�+`a}}=D[=�'���$yP7���]���х�Qv�D�揍�y�g������a�?`�%Կ��LLL�i��ü��9��
��A�Ű9
��86???�̞6��:VVV>RZ�rmii���a�0����8��A}�(0��a��k� $l%%%6��y���ώ���հ ,�m��@ 0�9\VVf�d2�@c��v%���z
ܼ�ީ�9���Ѯ����!L>�G0��n�M�Pi���A-��+�z��L|nn�c��������l7Y ��#�blV�ش���1H�N�c]��J=���j}��\��g�:��*�����q��Ʋ�����ԛ��|���v��M�Hd�i���J�������I��n����hi"�d2�"MܯW4}���l��@�d*����1�)-��ZO:-��$�ޱ�(B��}�	?(�	n+6�H�����h�_4K����_�O��a4��y��h�E���4�Ѩ�4�8��
v�V�}�>�4�|g���]�3-�UwI��fE�T�0��v�{�6��'����+**�(bK+d3
���K>sұ(�S7)�k��b�O煏����T2�ۙk�HǭdK�H�^[�D/��IF###��Tʻ����|�����x��v�=P.�%��a{�+&[�Y�
v�{�M>�W�lh�ni3��P�>z��N{1���h�h�$Im'
�ⴭ������d��{a��H��wP���hL��?JcL�����-��[�����^��>w��L"\��&�o�Ks���mlll�R3�S�R0�A�j�4���/�vA+��],������l��-�,��풗4LE�=�������))Y���&n��J6d���p�{	��tww��H���b���쬬]T)%6�r��v�[�,	�r��fcTL "d�@��!�fdATxڵ�mL[U�k@�/0��`�퓂�	~�a_�j\�vY��2�@;�ju$
�9ԡ��ɾ�ũ�
L�	a1!�:�[FJQc�./#ҦpsϽ�w�v{l��Փ�rO����s�{Ω-Ӣ����i�Gw��$��x�1�f����}��ooo��Hx<�耽��-�}��3�S��ZY���\�.S�렇���^�#*!���D��F�q���;�?���RU�hoo�����>�����x>.����i�OYI�����fFy���z������"�p�V��5}pmm�����t�wx^Uu'����v���_���G�����~cc��������&jC�G�x��]�t�뎎/�@�]��\�w�O�����:(��]����W����/������̱S���d'һ�
��д_Q�
����ET���#�����s��ը��y ++�,K��,�b�ħqv^�a�=��ݭ999r<�<C.|���ӟ�u�.s�����nm���?�|O�V[R�rc\__�18����|��
�]�26�6�L���8FFFDQ]/Fg�R@`�>p�V
�7�_Y^��r�ɗ(��^^Y����p�)W��P�^p�V���}pjj�<�-���;��<:����*� d"��6���C���b�7�כ��^3-��Aƴ0lN�U��Î	Pc��y��'�S�p�F4M�����,�_lq%�&g�@4X���2ra���u��'D�js���k�����R�Y044tZc��y��n"ҿ��K��D���e�HD�a$�s#�Rڞ+-��J��D�d3��"q�� ә��8/N��l���/�L�U�@�, �=Ds��"��^���0b�;a^^^� �Z9�9�����A��τ�XQ�h7�]�����t�-Cz����gff�\�ƶn�����|&F]]]u��x��~��}z<�}Ft��,저_�^�i����}������A��������A>]���a���`Ž9;;��6��ە�l�-����4<��԰�������8��(�Nm�DŽwn�ށ*���(t?4�~tt����f>;>�rxx�Q�eZp�k6��𖖖5�f�S5�`C��]������8 �Vr2��͈�9
�i�`�8c
�]��
��r(??�������|�0`+KK-¥F�94�lX�&��q:��VW�<��K���'񺥒M�p��S�ߊ�� ��#�|;l�fcTL "d��Y�7fdATxڵ�kH\G�'d�>PkS�B��>�?�PZ�
�&k$�a}F��U����b�DҴ+����T���X���i��~(��LVݍ���{�O����>�.=����9s�3�,Y��z`�c�%C�N��{ہ��[�Ɲ��$����FpX�
<%�^
�Q�J�g8�:����p8� -��Z�@��io 3�{����Ym8�4�h���ƻ�������ł�3���RN�%Lg��j�r�(����<����g,,�R<33S�q��k �Q�|>�����z	
�t�np~yy��d2�wZUy��A����c�X������k7o6z�+�V��0��}�@x�f�U�h��<T���s�AħfݑjU-���U����q� ���0��'�N���v��0�q�QV>�m��9�?���p��t���E�w�>�X�rEQ�1�H�� �F���b�۫���kѶ��6���7�����0�eh"m;�������w�ʴ��<��|[�E?Ԑ��|���V�s�%�Uׯ|52���h�sss���'j�6Q���3�+���A��_�c������,K%�� �
�P�������}pp�J8�IOOhzz����[��wtt4�Q_Q�>pA2�i��i�!�j��j9���
�����;OR�/^��
�c��;�=p��88�ȹ��mm�
K��u��M�����at�����Y4P5���Lb�ߠ���94�;|����J}kk6ٹg�c%`�0<<|&
ъg���?

��;����,��ϖ;�g8;�G����=�����/��?�����y��E���^Q���X7�2�"�%�x���?�r�X8M=���
�Á���ˀ%<�40��0##c�D�@D?��xF� �`x�ї��`O0�n@�\����������p`�1AM�����%4����nWWE6t�O�2�Ӱ�����Ɔ����yRl��T<+�clP
 r%��8�62�4Vl듓��9��oijj��h��"Ui!�4�`���b%`�X�v̅��E?ʖ��2�Js+�N�@�n3O�u��0�<b�fdӤM���Wb1����D��yv0H���X�tEҿ��`�p�3Þ��wT��L�����=B<W1`�`�]]]e�V�p�\���HY`䠫�uJӂ����i�b���233K���J�p"/�ت�c�0�(4���,��	�p¿�|���=6j&��W�b"�r��7�$)��ƫ���fcTL	 "d�@jh�!fdAT
x��kLUǯ�TMxY	1T�B���kb�,��)˾ڂ�4� m�A1_l�|��	��GUD�6���/bY�BKVvYf�匬�}���$��;s�s��;��a�
�P'�6�jD$��ߗe��&m���>��&p����l��؅&X�d.[�Oē@cP3H
N@#�@��3��x���)���x�
?
}���d<w�
���V�=�������p�HFH���I��`ϺE�v�̀�
�v�~ii�&|��B�Q�y
�c�A��©��f�jjj*=���v�E��o�΀�c�J*��΂6�~&�L�rt�������ONNVy	`�Fs�<11ш[G���"��뀭�<qop�����y�hBBB�d�*$I��S	ɠ$�"�Ɗ���*�1�\�m��N�솾��^�f/���d_{{g�����z����
z�\Nr��:;;�C�7�Mp^��o{g����!���4������e��3���Xݰ "=��E�.���~��zAAA���O@H��Q�5�~�/`;EN�J������}ugg��y��e���ht$�R���5bI@��(OR��U����FSmm�I��X%��������aM|-���b�V>ij����|�@�`C�Z�d���A�@=`��2�V�
�|L�Zh������u�h��.�p�X<ڶ}�/�s\����_�+�400`��hz ^��?C����_������{@���q{����t���/"	c��.�wW�y�+�p�y"���eN�ě6����?r�_��z�X�b��Q�)))(	u؋�������#h_����4"_�����@@�X��hg1_pT���GY��U���>}���[p��
��g0�r�;��j(�N������զ���jm��X,�h�CT�_��ava�h�1>>^#|��<55u!\�U������P�Pҭ�# M;fi����&+���%rz655��K�[
W
_G�c���\�.p�({�R����p�׃E�µ�	L���"����m333��6,�vRx�֭��QPW�b�1��5i�P3�999'�̆<��	��vM��U$�;`�`�����eHB������%��L�(0$~~�L�M�����.`�`����������R����Dps�\f�􌔟�_���WEs�d���¡��u�ښ�"ڀ��$j�%��HU���#��������ȌgafcTL "d����}�fdATxڵWmL�U�a3�@�Ą�_h��~Le5Y^Vʇ��`�-]I�֐U��̏����+R��daI3��%hB���Jm�B	]�~��r���Z�VO��ܜ�s�s�=キ��(�A���`v���O�vQ�`wh�ϓ����N���sK�ԫ���DEt1_c.Ψ	X�--4���Du�u���\��G P�&'��N�|�s�|"�ٽМ�F|>_�!�[ZZ�����D$u�����{���R��i�3��ؠ�g�r���B�T�O'��l�F"�z�������6'�R�K$�`���2f���� �M�e��
�T���,%#y03��@s��&''O�t�b
1����o�E�絺��񼼼��Yh��S`ne����I�������/���,�������y��Ym5��T���z�/k�+!1�7�LzGGGm�s�������չ.�@|��K�I	A�Ex$$��]9�Fc3��	�<��k�.�����.����r`jj�+�Hx�޹�nnnnc�X���Nf:	�p1(�ʑ�V�˾q�����^��x<�X�
�<������u)�lLs��m��$)�/�@�V1�i�Nn�)Y&��r% �$Z�E,�"i4$��֩T�n�7Fq����8�h.W������c��.��۱Xl��Ɵh<L@��=~�#�o��4;�ku��x?_�afϐ��w*��,���uxW���/�LF���#�Q�)gG�i��3|]�B���d��$ �����0}���/G����=��_!Nz����\��u'��k�cbq��������bJ�����[[[#�;00�F���%Ud>��D���hpp�}ccõ������oW�O��I+ri%��]�埡oM;6�YV`�����	��lƛ��ǯ`����i_��P��P�?�VVV:�I~��O�V�E��������c�EEEOS,.���mx8�}F�}a6��j���0��A�F����~Y�Q��nߨ�a1�(˕]�Эbnn��V�*�& YYf.�x�VA�zƠW�ɤC]9�Wee�E��a��*���z��=�hH��ncccM A�w����	�D�4��5n�C��%$`�1???d�AN7���6���@@�~��
�`Y�5BMMMۡ��V�纅����7����*s7��n���=�6�I���r�,~���t���I}U�/�fcTL
 "d�@6��fdATxڵ�[L\E�׀.�&�Ĵ$FHA��IiLJ��X�E(�ܗS*��J�K5RLx�
i��ڄ���A}c���RKw]���g�9����ð���%����|���vp���~P����~���#�^�z�Oԗ�Ɇl
�<���N��HB;�3]嶹�$�$pg)��X�)�E$��4�N���V��H�0G'�����N����J9xb�h̪�:;;+	H�|������q��"����Ƭ	�-�x�
פ���\���� g"�G���Լ�[}�5����X7'ْ�B�Н�+�Q��G���fb����~||���.��4&捌���:D>�q�ԴRL�̪4(���ۇ���J�R�(
����2yN�'QJJJ*hِ��3��*�-�gϞ�
��i�.��۟���|���ymmmω�V���vuuUS�	����������e��{;?�O2�D7'(Œ 9����a����NX���������Qx���(��%��@��|R����\�͍�����
��v%�|lll�ț��+J�]��I��|X�?����Ԃ�bP{�>�4Mk���c����/p���p��X�	��Z�����o�Sܢ�y<�2�F�q�%��Ly���_��^ds�
����⩪��1�g�F)�^�]O������`�]��r�� f�:�-R�	䀏��B/x�!�]hiN���^�fiiit��^���)�3 /55u����[8v}�!3�П=�xt�s�i���cbn����p8|�1�/�dw���$Ё;�*�OT��Ir///��j���	I������TWWW[:::��966ր�~p���O���`�`|	�����H���>���;J>�4����[���`8|a``��5T�]z(==�q8�>p,�b��3�i��?B��� ���%�}\EɇQ��A�$��^��1pX|F���*�588x���~@"3h�I����Hf�W�
�<Oc�g1���J�@�T�e�h7���
�C�=���Sܤhii�c^�$r{�î��-^���w���(�f�~���2p������}����N`vv�"��a������K
-;~�4�hh�������͘q
�b�;�cyyyUt*�DŽ�|��ߡ238�7��%;;�"++����m9Jy���D"���������=��OH��8��3�� �6������D�fcTL "d�����fdATxڵWmL[Uƀ&&"0(�K�@b2�����,�����AˠE0��D21+#b�P�ԅt&��D��h�C�b�	��ni��.m��K���vO��޾�����羷If���9@-C�����튢��L�{q�|�7Q�@��sͪ���"��=��8���D�,X�Q�{*��";�M���Z+�99au�f�i'+D"��xh�h#�������㪎��w��4�pp�@ ��؍ȗb�M�S��'����ZN���o�UWWW�AG �o�{v2���I������-�!���m8oBQJq��t����Qn�dn�%V2�_�z��TD1�pj�)�Y'M��ky�W���|X*�*$I����[&ae�4$��VN�ȇ|Řt��9�wKʩS�����h����gO[�|���z��!G"ή����?[^^^���7u��j��3v|�x"_O�q�h��P
E�r��A�z<H�Yv��Xl�JGn<���}�aZ�aR@��b>E�v51]�B�ƚ��r��fG��n5�Ž�E�fK�|Q��1�Ģ�$9"7p��@]��H�^$�D����x���~��h.Q#�]|��Q__o�h"�	��Q�Lq���PW8��i�j����B�
��䙢�����u\�@@�ua5A��bv��kf@�[!5�ë�
�C���^�F�f<
���VhNPF��-���Oĺ�s�eM�\ZZ�����~�w��KKK���
ІlĄ�*e�7�M�t�-���_��|��������_���������A@�t�����/�c���b�!.:��s���̻(�?�����ɳh��ϑ��#D�B�O��}#�>������d��s��3������������k][[suwwWo�
%�J����ik.��p�L˲|��}�:���ٮ�Bo��6�4R�%Ė�u�C@1��Rt�N����?��t|!$�c�����FU�����7�'i��<@�k�����>� ?b{�����i�`�LTd">a.�sss��,r��>���U���H��eee���}	�^��(**�
��$�,���j�u����z@<�`W���{X?�	�!O���m���r���Ujh�,KE~~~%��c"�b����Ee
5RNNNiVV�����b���r��?�x<�q�$v��H!���q���!�/09�F����+ϊfcTL "d�A���fdATxڵ�[LU�׀&&H�!��X			}�
/�I3����;e*[ں$�"VR7�Q�IC�$F	���"*h��>�z��
i��Xu�v�e�3�ߞ�񄕲O�˙=g���}�i�4m/����6��Ѓ�!�\Q��7�,�SŚ���2��&�X�nB��P4�c3���xc1FF�1�n�����@�Z�އ�.N+�p�N��B�p��XF\.W��D:455�4�?�]D^�v]hyy��(2�Xjc'�^Q�
���d��VJ��P���x��ڗ���F��Xj���>�#
��j����FQ��'�:����J/#1JO���
�No``�
Ey�(v��4�=�Jq{ݺ�����#R�T&Iͧ���	�$����{C�b���s�l�v�_<��^q:�Oz�¥,������tQ#��hoo/'�������׈A�{+?��]J �AL|:QK�%��$rOC�-V���s�Fw�vQ����A[|�
�FJ���A8
t"4� �Am�'
q����O��P�udd�,��HSVeee���?..n�%�����S��7-&	�EU�~�Tm	?�T�qTB�~4k@��ZQXG��������	w4U���6F�6�f����f
���҉)�/..�kkk��M�fR���O_���u���]�L�mx �ϥ�azz��oCQ<QHo����|�v,.D,�[AL��f.6n�����777swj`uuu	���Fdcc#S�S�
7����_�����o&''���`�vӁ�=Dlll��[��2GTU�ޒ�W0p��tuu�����z�e����(���R=xv~~�J��M@&F�";�/��+W�z0<ׂ���FGG;q���Ͼ��bc�2x���x	d�3��Mc'�|�n�?�맜�򅅅Ft���ѱ�m(	�4/..�Q4�����.�*���σ�lii�z�hŢs"/Aq<�ږ�݆��A��E
�'`����s�����p��Q�im4
(;	Z�>��m"
��}B/�E������;�{c���`��WxH���d3�S�a*��iRU�Z

�%4�o�Z��W3�.�|t	���%��LN����Mw)�r	�pp���s�� d�����xjhd�Z���M�/8���Z���VJ�1����ɀ��;+|�HIIIE�wRf��|B\8���
���[9���V&N�ϦR4��-g��%�A��/���X��V�fcTL "d��7~fdATxڵ�[L\E�i�&Fn�(X|�\"���`�>���X ��Pv���.�4�U�T,�����x#����1bB���	�,�®������_����z6N��fv�f�?�&UU�+��-���$ա�A�|=������;p�<��,�oh&��<I���m~,4�Y�ހ]y"h�bv�6�Z9��(�ĉ`p�j؀��Z��Ǣ�:}p��U��������������T��;4!��kg��-�q�nˊ��A�b�Yf���:�H����y���9��;}��a�'��>�ngg�(�N�����S�������u|�DnBu����mT$F�%`a����ߍ���A�q��s���
<J�
f��F�u�~qW���/,B� ��kfb&�웒PRRRI��6w�zN��a/�$'�$����UTe��"����OOO�u�-�R��;���&�'$�Ύ��j��PRWW������(�WP���/��� �&�l999����
`���o���IQ��=ϗIII�vP:Ł
9oc7��0��h��@)��H3` ��<G�(?�;��_���������E��FSAmmme �����-J~D�B�,0n#g�bL$��M�I�􃪨�����9
�w��D��k��X744t>���X���bX�l��@}}}�����ʊ�f���'�9F��o�����
6F�t&\����l��&���=,��I0��gX�?��H^�5�߂몲'�
���̃���z���������œH�qMQ�q�_�����.eee��i��;r����F'�ww�G%2Z+_Ө���>innn����>EGW1��k�5����bjKoz��
����_�	����
��
��:��===/�8���+�/1���-`�BOFʸ���x^;�[������훛�����ކ��SRR��	>��o��f��TkCp��	�@eKKK��[s/b�n�T��[�v�'@y�1���k��k���ۭ�r����X,��)�yN2
h�H�tp\�h/��κ�hP'_��[�@��5)����q��u/x?,��
���e��m�7T���[
�N��F�FFF!�#�p����}�3r�f�I��X��Ӳ,"�%gWW�x:e2�*233�A������0����&��222�RSS�ؚN٠����t�଀���j�VGi'��R��-g���K���_]��Bo�fcTL !d�P�ffdATx��{H[��SL+Bu��(��v̿���uWC,՟�X�m���MV�=|છ���1�(a��D� L�(ݣ+B�!H}�f�ؖF�p�=���\w{H.19�����9'$�@)-�EQy��"���C@
���C�ai ��?�
t��M�G@��LEF�T:�V�6��L���z�A�L���^G�L��
"�}xI��*p��� ������}��F�
�M�A��I�wuuUI��ݴX,����Wb�@�ڃ^�dssC�Y__o�t��r���5Bʓ����*�@=���CCCl".²a.����Sr��4`�h���V������
��3Ɖi�8W�7�-����-]ZY��Q�za�~
�e�qV SSS���|��T���J�q���o�@�̻�W�p8�qff�m||�kll�+##C���+a�i���
�$I�x�ruu��JUa����E|`���f�,�kzo��7&''�
�-�D�D����y�`&
{.\�9��$�`�
��^>ǃWxE~�ߡ�s�?g�"����49XYbX(	l����;w�\V4���O������#l�B!�͹��lhx�H���!N@%�t�-7�	�A%<Y��W
_a�a���&%z����k�����ι������`�~��7���"�}�MIS��`#������bT��P�O��#�D]z����N#q�����	�O`�cl���Ue\�u��A����.�h"+���?_|���ݵ��֋�~5�H
J�0??�b�c����/"d�0|�xች.l�*�H^TUz�0fl�`��\c=�8���z,ΧJ��b��"7�"��7==�e+!҅p4��CNN�a�%_}�ٯPa3�L�
��/..��t:k�x��Jv(E�:�`6�z����wٙ�L�}x;F�����D"�wq�y��w�	�il����,c�C��
ВF}En�˲���������F�w/t��'�n(	����:d�	��wee�vW8���4Y,��p�@�ȅ2!o)�n�*�ԏQ�}���Ȕ��5���b<�p�f�9�5a���,�e&NTw�ދC���&���?�o�צ��-��o���\qqq��5���	�Ǟ	�C���ѨH"�����ժmE����]�s��������O���@!�71^������<;Ғ�0��������H��.�C!T0�ĉ�.�ܔ�
�8�{O�,��.g5}�$��M�[6�9�tEXtSoftwareJapng r119'�aIEND�B`�PK
!<k���Mchrome/pdfjs/content/web/images/secondaryToolbarButton-documentProperties.png�PNG


IHDR��7�ZIDATM��J��v	�Э��Jvr����v�A*p���eB�E$��0�:��AAbnu����6G��F�=�hPc�Q����7���{ÞoS��?�Xd��jc�H ���+vyD	c^��wz4����ԝ�g/�q>9�YFx�3�	��S�#�f���
����{�|p�T�D0�����T�ݾ"�c� d��[���G�m�z+|��~�V�E!�����.�T`�u��y�u�7�y��p
N�y�PW���6MnQ�X
F(r�fs3brG��.!�$�iv�	aNid�[�0�+l���6�@@@��Z��>K��O������X���_���T,v+IEND�B`�PK
!<�����Pchrome/pdfjs/content/web/images/[email protected]�PNG


IHDR  �s�lIDATx��_hSg�3/0w�m腤�ۺ+W/�^X��Sq��ވ��![iz���Nqk��p[Kgl�4G�4-f�61�[g(��5Jr��fz����w8|y���B�ٰ�~�r�).)9�:��a�`�I�OZ,˧��Aq�\��*,�͑��p6Z�l>�������P���ݡ���kq�,�%ռق6�7���/Nc�؅��]r��}:=�,��B�m��f�9Ea��I��	�����”��ts����YEv�e?�h4��!+)
�j�E�0�|h�^8��8v�}>����.�P�\���.�Z۱�>̓�����BH��!@��q�K��c����*X��PW�����X�6S|�gK���^~��b�����tK9�t��!S�����%����
�NZ������ZM��Mt�}��ӴV�B���$����'���*��I�R:fN���I\R�u��0n:L*O�Й?�<sf�	&RgS��:���1c�L�K�B:�X@'wA9�0^�a�����c��~�	� g9[�(CG�'��b�c>f����HC��S8�NӠQaGt�)�^�>��'�f�ӂQM��ғ����@5�#՟�����y�C�$U>�u���5s��B���kЎ��P7��cT�g��k|��a���
o�㔄����>�>)p|t�4�I!�;l#�&nT�J��o��q}v�t��0�o�ˢ|͘��)��.�f��#��Q1�9��+�a�pFNc��X����#�}����jN�����#��Ӥ�����;�B�q��ӿ�[��_u��z;�h�Ԗ��󉞗7�#�9^yy3�3p�{{�g�WS7�iƧIEND�B`�PK
!<�Q��Dchrome/pdfjs/content/web/images/secondaryToolbarButton-firstPage.png�PNG


IHDR��7�zIDATx��1
�@��w�xB ��6),l�@,RX[���w
�aٵ�7�_��2�<
NH�9rϋc��*�vJ������g�j6�\б���ti�2���
�K"�|���s�D�ķ?T��^	�}�}IEND�B`�PK
!<�_�/

Gchrome/pdfjs/content/web/images/[email protected]�PNG


IHDR  �s��IDATx��QJ�0��Ivw�ݥٲ-�O_ED\Y)e�1�Q���g��c�iD|�{�d���	�Dp8��3xO�=�7�ų�V��u�B&	��q鈏�cxuL�p��9`RK��
�,cvT'v,bs��g��C���E�h6�6���g���o0��Gɴk�(xP���5�֨�AF�}���t���.ҪY4��L��w���< "|MIEND�B`�PK
!<�--Cchrome/pdfjs/content/web/images/secondaryToolbarButton-handTool.png�PNG


IHDR��7��IDATxmʿJA��#"X�R��`a'>���0�`�k�o���J!�(bQ\#qc�� X�7�&��e�0�d��=(~<�1��>�����]Az�w�4q�]�?HT�r#/�/>+��|�'�U^������k\!�V5�7jGu��H�;r�y�T�u쁭AcT���nd�e����]F��B�M����}^Oү���6׆g��;1+��m��ok�
`�R9��9�'Iec3�	_#�L�[IEND�B`�PK
!<ZŤeGGFchrome/pdfjs/content/web/images/[email protected]�PNG


IHDR  �s�IDATx��?ha��[�Y$
ֹ��K
��P�ݺ�� �Pt1� V��C%.�J�`[Qijk����O��%RM�������C�I���y�����3��
�02�䦑1�
���7Ee�����"L+�7�jsѺ��h�M����>1R�~{�]�ߠ 4�_���/�&4�_�U�0q���>M�[���tЁ7�гsv}�~�5��v�*#��̬�z?sV��[hA�3���I���&�P�ƴ=�q�>_G`/��q/a[����i�n�r�1�@�\�s\�k%��XgV�-˜S��*���C��V;/�K�r|�^-^C
H\�Hw
H�P�UP��Ÿ$R</�Ce�^�=#g�L8�%)ʓe�a;�k\��`�Ɍ�CP�qY`9}��`

(@�=����"�#����� ��C���p�)�������}�y&F&`Q���W9�G�>����=svv��Dj�3�fe���ۧ�����8�����|��IEND�B`�PK
!<��3���Cchrome/pdfjs/content/web/images/secondaryToolbarButton-lastPage.png�PNG


IHDR��7�vIDATx��1!D�Jr+�$?ل+PhcaaG�W�[���n����_F�.�N3?G4�a_O6�Gt3�K��e41mI�s�%�CR#K���I���4�hz>�	zc��^���N��ͣ�����IEND�B`�PK
!<l�+Fchrome/pdfjs/content/web/images/[email protected]�PNG


IHDR  �s��IDATx�KJ�0F�Z�]�+��Җ>�c���I"� ""
u�>���WD�߁;;��B����ǫ>D��t�g;tM}��j<��~蜨�h��X�&J<l�J�g���:�`}9��n�����p;��6uN�7SDj�s"��wDb�s��q9��v:�"�#"k�m
݆n:'�6p�9��Ǹv���?@�s�1p��G�����G=�D�٣�i�����_%>��IEND�B`�PK
!<��[�hhDchrome/pdfjs/content/web/images/secondaryToolbarButton-rotateCcw.png�PNG


IHDR��7�/IDAT}�1K���Eh��%�Z�
��"7[�4E�G�]
������B����H80D�<��:�74-	�y$#���đ�$O�y���-�F�&66MZ^�5�Q�N��s�1�cӱ���aUߨR�z�֌F�O���JS�*���
*�q8Vߤ}ȓ�P���۩~�c�s�STQEռ~lNP�|�p��[
����v��$H�wfJ���5&�`p��ߘU�נ�I�D�bR�5�WOh;�sC�2eJܸ��q�4 ��RH�xn�e��Z\QH�5��������W�l��W��OIEND�B`�PK
!<4ʠ��Gchrome/pdfjs/content/web/images/[email protected]�PNG


IHDR  �s��IDATx��_Hsu�Ǖ؄hIvc7�A��Ex!%�O¨]�0/�.�� �F1ɋ�V"�[I�4	���sլte���\nP��ؙ�y�?�u;�w������s�3�}~��<Q>�O���a�`?�,�����sr[���a��a&�/���p,OI�uKjYphzZ~�;��'���D���Ӊ82��$����`��������|�ž���~=ff�!;
����4�����P-��W^��/r}�
������`G�T�)8����yڮ�.-
ÅR�
����ͫ_��c�7D!��|�{�UV\�ei�(6Щ�n�Z�
��K@1�w�0��Gi���H�G��l
V��y��2@F����|��Jn=�d��y�������6W�ߔ��3�A�\w\�Eɮd��l���%�eIȖ+�)V��QD!k��/�s��x\��?*�W}�AV��r���D��`XI�98 ����\�D��y#�̼7EV�y"�皢�T�tMI�k�k�����E����'t��X�UE[���A���'��΅�_b=�r���F�_���q��i\1�K�&�Ħ��/�>8Qb��ߗ����
c�n��Ns���w��v�:u�����@>�f���������=�O��x^��Y����S
��;IEND�B`�PK
!<��ggCchrome/pdfjs/content/web/images/secondaryToolbarButton-rotateCw.png�PNG


IHDR��7�.IDATu�1K���Eh� Z��%�\��l�4E�
G�tm(�j�E)� :��C4�!�F!�޸�����"MZ0�R�G�*Y	_?�zx��x�����F�'�X���x�`�E����U#hѾ�kN1MhB1�]�iӦ��`�G�T3��.-Z��q���뭄����|���͍i�YPJ)��ҢB6��I��V��'5���m�@��gC����*wX��\_N�K�Т��	�DI��M��RDC���

�4ip3��o��3�Z˽����q-�\��"�ՒV��%�*��O�C�E7��IEND�B`�PK
!<}�/��Fchrome/pdfjs/content/web/images/[email protected]�PNG


IHDR  �s��IDATx��AH�a�וԤHD�/�!4C�<�A�$!�vQ�;;J�T�<�W�6��YN1�Y���j��
��E��z_����-��||��{���2x��l"���<Z0�9���|*�[��c���&X�A߰���u>���)��1����?YUX��e8c�$T@��/�q��b���*_�ݧ9�����T��j��׬9tV��@_���G�uTH9t�Lt�2xWXsYsH/���4h�o����L{���u�8q��O��Q>%�|W|q����8>�凪�x������(�aX�?�*m�HR� Ԃ9��̤Δ����I�n�H%��4Ap HA"Y“�=g{H���B����z�J�"Y~Nი=2K�"Y���N�N�(U$K��ؼ�I�"Yf��V���7�k��Ah�qU0�	,33���>���\�d�'7�ZA�qꅛy���I�²O���*��N��#;/��"��#���]�+~�N�Ed����v��1wY��x%����<dy���Ǟ�4:�eU��:#C���ḭi�M��G�:)��L��6h���vK�<O>��m������2Ӿ��^z������vF]Q׶���n�Ր^J��ǓI'����+�b>�3���������IEND�B`�PK
!<n����Echrome/pdfjs/content/web/images/secondaryToolbarButton-selectTool.png�PNG


IHDR�a�IDATxc@�����@Z�Y�̵���W�^�oeee	�s�%�;;;�>|�{�����L��0H�X���t߿��͛7?޽{�u�…3����r�DĊ���2jȷ�gϞ����&�%��رc�N�4�~��'�}��;�Ъ��,b�Ŀm۶�0`�/�V��T#�/�x6o޼� ���ƍ�+**�py�k���@���>}��Uyyy�nnn��ƒ���>xЀ��}	������lAN�y����0Þ?�>  ��P�2���im|����k�;w�2��������F~~~�ŋ��9

⁚�@
�Z^^�g��
�?aic��ݻ@.�k׮]����OX���_�|���Ç�f̘Q�EWD�E�@�����@67�������1IEND�B`�PK
!<!��++Hchrome/pdfjs/content/web/images/[email protected]�PNG


IHDR  szz��IDATx���-9��5��g۶m��������ڶm{l�.����d�5���T}��������	@n�����y���~��x�a��
z��w����?Y�v�|����"fyxAA�E��b��t=��ٹ�m>sÃ�$h�@��'Κ���h4��|��G�����X�UHx�nڴ�&6Á�RSSS�t��#�����Z�5k����w__�?���F����EQj	zzz��ɸ.�� aX%�n��q��eMMM�4!M���/�C���P3���,$�CB"8+��ꫯ>#۹�А O�����,$��
26?:����^{�y�Y�B�GE�ʸ������V-�34$Yݔ)S:^�":t��nd#�ﺲ��r�_�hH�R۸q�q[CW	�?���70�jc�^�� ������^R�f͚�+������÷��x-����G{��lny���Ob<O{��dY^�C�`��;;;u�~���7C^�z��뗑��: _�����[�ݲdɒ�g7��A�n�$���s���Q/EX��lQ��O>G��;�|����k��������y��	$g�Q�ɓ'���jmm���S2��j����Pa+V�8L͗�m��v?~?x�M7=Hw��-I�C=t���@r�\�Z�K�H"����V�`}]]]�:?ȕ���6��]���9GJ��ԩ��V���oq$#k#A�r���4LhɅ7�|�E\3�9zV�Wdd��'>/`�{W&b����H.О�s��3t��5��<�۰a
�yf�A�qt;���3bB���_�H&VvT���X�[+Cb�j�믿�,_��s��,�L.�ў!$I�����L�������k��-�0���.�����?�x�w@�G9�����{���{������AF]���wg�/�O���r�O>��O���F�0�c˙�?�	�iz��IEND�B`�PK
!<8r~�""*chrome/pdfjs/content/web/images/shadow.png�PNG


IHDR�Y�G�IDATx^]��j�0ק����^/�+#B3,{��Mz�R��㢔ЬE�3)�k�f��X�5�V�b��]�,Z�0�Z2��
�]I���[L���o�{g��h,�xM�+ؙ넡���e��"�6ADZR�3��n9Yr��EW�f�W(+r �薳F��4�H���=p
 ��f�"8��3��v�=�������o�6��{)�ª�'k%"���Y���NR�du����IEND�B`�PK
!<Z#��q	q	+chrome/pdfjs/content/web/images/texture.png�PNG


IHDR@@�����PLTE,,,222...%%%444???'''000!!!)))666###999


;;;===AAAFFFJJJHHHLLL			DDDNNNWWWQQQ^^^UUU\\\SSSYYY```bbbiiimmmdddqqqfffooovvvxxxkkk���tttzzz'��:tRNS~^�8IDATx����H�#���U������G�'�B����{i>�@�7q~"�Nk@�xK�RO5J�16tw�C-��7Ĕ�j�+�x�u�ǭ�؍n�"W��MC�E��t4�c6��d�p�j�0!Hyl�����]>�U�JiX@���
Ns7*6r�
JIi��<�
Цn��hdAz{r�x�C��Tu-�Do�
b��/�9+��<�x[�Kɚ.�T'��O͈Q�zF�?����x���|���!��$@��N̠��c��'�|8�Y1��s׸��=���XSV��H�au+ƒ[P���y�l�.q�f��>�@���&}Iݔ�[��C�$�\�rK�P[4U�+V�S��>���tp�2-�����0_h]�~U���$�A(�J�Q�t�Ӯ��a�����圆�µm�4��|��5�\��⍀��+���Jm�լ1�@�*|�aVW�K���	[G:Add����
KYV�
�빤ň��r�\�
�߼�O�f��>�7ր+*1[��{��	~��^b''�Vq�Q<�&+�x��܂m͆�bt("Mg/�&ے�u�N]�
=G��*kX[uشy�3"�)gUkF�z�p�"e�96���LbmX(�%��{k.e�o#VϘz�&"�&վ��N=i��ECD��ώ��¬�)�vp�	
gUtw�&��+��U��5��Y2����=���/ޞJ��_�D��#¡Zr�p�҉���~ٟ+�ӧ������m�����O�Y���YK<.�੔�8=���Oӱ�%��p�n]�\$L���J��n�%�@#�~H��_Y=����q3�)P]r� *T��a�GO&�2��݃V� ��%��>��	�`�Kҏ�$%���}<΂�MQ����q��n��3��9�fJCé>L��e�*q�����}i��]=y���$sEuA*��
¢��O>��ތ�>�ۉ#B�)O��<.X��)�Β��5,y)>�AC����Nj�nĩ`a�BY�N�M��d�]�_��6ȏ<�һ~�ɲFת[�1̼c`�
_|Ű���F:��ح�0�N:���4�+�jh������g-�u�r6�QK|���N��Z|4��p& �ii�=c�.�^��)|�G;�#0x�������=��*+\W�����4q��#|t?'�-yOS*�����te��B�M��G]�&��(��R�v���`ph_���Y���~{�� �,�0��t�]`�+�dN�w��ߴ�W_Z�N"B*��ei��-؝���S&�Ǡ�hȵ�S(�k)k}ެ�؉1Zӕ��iQrPc��Tup�m���	K�Չ�lc��Ჷ�-!�|ӷSc`F1Z���-�Ual��X��V��>���g����uw,=D���,)�׿����<������n:o��	}�1��xMNBC�*h������l_��yY^2�Od5/~�P����$��(���FG�4��&�L�+��C��)c�CRq>_�xkZ\�ΞpG!K�7�t��T����H1|�|��T���n"��~����� {����5�V͚���YCD��]��͘���F}�€�F2`��G�H*뷮��_6;F7v�������2Mث�
A�e���H�c��0�Z��I���Tb'1��|�tl���1�H�d�<��=/߯�?F�M�'k6��ͬ��4�*LY��Ζ�� 1�'Q�J����(���O���������c`7���Q�I#�n�m��^�\
w��Ky�	!CU@�jl���1zh��t+}]5w�
�yN-���:a����(���ͼ�6|���u�,,���Ww�s�6��G{�h1C\�^53��z� �'���Iu	~���$���ٶ:-���^��(��J�(�O�q��{��xHsk��y�l��}����f8(�$ίC{;e�1�T	
-]�F7��hH�*V�T'�G#ں�^��6�<WM���S^#�?Z򫖽<�N�-�&M
��LdIEND�B`�PK
!<��K��:chrome/pdfjs/content/web/images/toolbarButton-bookmark.png�PNG


IHDR��7�uIDATxcb �
��C��c0|C�&0�C�d(��
1�A�t0�\+��t���L3�v�8v�?h1��&�M���b	�c����oY�9v�2����M�g=Ϛy�ȝ�IEND�B`�PK
!<�|�=chrome/pdfjs/content/web/images/[email protected]�PNG


IHDR  �s��IDATx��ʂ@���~Dj�I�D�����4�h�D[��M���P�?A|�y6��y8�,����Az7�
�*!���k���
�
���*Tb�m�J̬P0׿���~q�qA��]ßO
��9��G���]��	x6A�S��{y:R@���P qD�#4!�Nv�Ib�P���P§)IL`�}]W����P���^IEND�B`�PK
!<]̪�:chrome/pdfjs/content/web/images/toolbarButton-download.png�PNG


IHDR��7��IDATx��=��@����鴳P���^������F�%$E@�,5l�	>v�ɸY,��y�}~���A���-+*��)p��5���������,t{�e�
'����
G��0K�x�9���1��;2���&Dnŝ
	���Gi�qʝ
���-��M�Б4^�S*	�t��$;�`�G��@�h�4�WfIEND�B`�PK
!<��^4��=chrome/pdfjs/content/web/images/[email protected]�PNG


IHDR  �s�pIDATx�ͻJ+Q��ov)�FDQ|���[h�'�b-*��.�+X���'C�E�����ư�d6�-�D�p~����3��%���!Z��T�@=���9a�u�"���	�|��G��c��i:�-t�@���!��E������`�y�C
4-�~n¶��	q�x�`M��G��39�wT���mB��RD�;ʊ(��@I�w(*��E�U o,̊�I}毋s�;xF{� ��z��X���K1��@&p�N �M�W8�P�D����
NC.�'��=uA�����8&9�c#���x��ɢ��yi�Đ�#�l��?��t2-��!��OS��O}���f֫^�IEND�B`�PK
!<�nϠkk<chrome/pdfjs/content/web/images/toolbarButton-menuArrows.png�PNG


IHDRځe�2IDATxcH�~ ^��%X�x-?��,ȒK���|72���0�2�1�[�K�G�IEND�B`�PK
!<�܉��?chrome/pdfjs/content/web/images/[email protected]�PNG


IHDR ��_IDATxc��<2  ��# ��&�����g@x�B��(�@?�$��@A8�X��PAl�H�4�$� �^���G8�3���0�?g~�;3{ IEND�B`�PK
!<�)>'':chrome/pdfjs/content/web/images/toolbarButton-openFile.png�PNG


IHDR��7��IDATxc ���n�����g��@ �5��GH���H�XAG:.ؗ�Tph4nx���7a��Ɋ���o'"��3ܟ��~;�������L��2�+�/��C@iN��9�JAD�t3���k# �%u��sy��A��n�d݀�
f�o�� D�1�P�a
"�����?��`i	��t1�U<��?�@w_‹0���,Lv8�-��A�^8ylIEND�B`�PK
!<�/.&&=chrome/pdfjs/content/web/images/[email protected]�PNG


IHDR  �s��IDATx��?ha���bHpZW�`��� �P�.U������A:�"��� E�$��F���Ssr�i��`��b򪁒�;m�����^>���cb�7�����o�"e�J����_��B����[;PrQn^o�g�%_\U䫫����h�
�{M^�:ѻȹ�l�5��N�W<�K��j_�R�,�b�o�9���{7Ź�Xh�c̽��1���f���o`�5��DY:#�T���!4��!kF]�.?(o6�p��	�K��䎦.�n杮��/hܔ�ӣa�֒��3�|A4��?`LI�[x'ajqY�0��?.ƴ�]_�o���hw7F�m�a
jX���1�j�j3�m�v	J�����p�r�eYE��!O洼P�A��:�,����&=��4�YR�A�$' ���<S�6^U3�o�I�E�q�	Ϗ��&�;1b��BF��梵�����Dzл��:u���!��a�1?\��:I��IEND�B`�PK
!<����>chrome/pdfjs/content/web/images/toolbarButton-pageDown-rtl.png�PNG


IHDR��7��IDATx��A
�P�����Ct
��}…� ƒ:�-��EPDY�Oڷ��z�L��4��_���� ���1BD�#�gd/���n���X�Ա�w8��O�����z1^6�Zd���~Tuy%�/���
K�V�P5N�`�T&�Jc.��sEG�1�1WB�Hu(�]	t ���w���ӗ�֙IEND�B`�PK
!<�z���Achrome/pdfjs/content/web/images/[email protected]�PNG


IHDR  �s�UIDATx��;JQ��#�(�1KH�b�bg�S��(����&�F��D4bQ�������ǁuf����߯����8�En��{>z<p�s�2Æ=��-o�^���"�1���}���sseS~�4��W~���&t�K���ZG����K��il:7��
�$�M�B��aWRY��;�r�/����Cqx!.�#�;�)���e���LH��Ҕy^%���K<��z	���U�>���݉��Xg�O�y��;s1a�x0��SϝD�����B”	s9Q���e乜Hs߭�V�古���L̥���Ð��$��I���П+���O�}/����IEND�B`�PK
!<�e3Q��:chrome/pdfjs/content/web/images/toolbarButton-pageDown.png�PNG


IHDR��7��IDATx��A
�@��A'�E���	:D�:P�D�H�"ZE���8�ޅ}���٢�-��?�� ��H0�$Dm�OǸ y��H턿�ř��Xŭ�Yy,�UǨ����-����
���Ć29ZaӤ9�yNb]��2�$�Y���h�7�۳�x�iO�f�jW������A�IEND�B`�PK
!<��2��=chrome/pdfjs/content/web/images/[email protected]�PNG


IHDR  �s�SIDATx�̻JA��#�(�1��666>�o�����`��6�K!v�(��m��n$F!�s	h�F#Y?�us�c!8?�������=T��L�$ꛠ �w6_j莸�z(c�7����s��[���������)߀Bg-��puH;,ξ^�N��'��k{h�J�4��54tܺK��ƭ�8����,@c��s-0�Ɓ�@D�������9\�N1G!��BN本�{��q2j�i�~��\��S8�g��\N$���$�k��4g�BWH�s���ԫ��BŽ����bŠ�X���4Ey+*���s!��o�;M��p�%[FIEND�B`�PK
!<b�����<chrome/pdfjs/content/web/images/toolbarButton-pageUp-rtl.png�PNG


IHDR��7��IDATx�ϱ
�P೴��=�Cc��A��#�6����"AC �H����w.�[G���]��/��,I)č��
rd,G�TXpt�2�j).B
_<$�kx�0�� �TǦ����98

W�q��.vF�)�x�<l�bV��?b���#�~�0�8R��}�ɢʴ�C}}X��j��a~��IEND�B`�PK
!<r�	���?chrome/pdfjs/content/web/images/[email protected]�PNG


IHDR  �s�\IDATx��.Q��CX��H����h-t�x�̴��<����J72´ED#D�����Z����H?������緺's����>�H� ��s@��w�J���5����ܐFT"��GՀ�'�D��P	�`G�`��p� �B�t8(��@�
h��^A�`�N�
�7�F��PA:H���_*#��iJ���Up5��ʗ<�@���O1О�9O1�3�_�IĬ^7p�i�H$,��P�8���Ň�"
s�$Q����9�\b&A���:�8���L�$�a"�c���
��NG��J�p���=W��]_�Ig��h���E��<M���|�dH�����IEND�B`�PK
!<Q���8chrome/pdfjs/content/web/images/toolbarButton-pageUp.png�PNG


IHDR��7��IDATx��1��@��i,,s�`a��-,-��x[[	���FTP!�$�$M���K\�f�?��?�Hl[�E߂R���%��Jʭ����؁�qM_�W��/"���p1�_�g�}���t�-��R�h�9�Z1)��Ѧl�U���B���5+�̈��	T�ú��?iIC�
Ĝ!����IEND�B`�PK
!<����;chrome/pdfjs/content/web/images/[email protected]�PNG


IHDR  �s�ZIDATx�ҽJBa��_Q�EKP��AtڐK�Pw��i������s
�(͌H��(4_�A�4X�_��?�Gh��_�}����'η>�Ĩ��>)���f�G����-���^�MVMں�
V
Z:�u�<�i�
����G��_�d���4��|�P�!����DC
�X�#�DY�Ø
��Dd�e�	�k���ʷ2�@^���0���9�Tpn�W��F*t�1s�n����>���4Z� ��Q�g�0=Ax�I2#q/���L&6?SLK�����[�G<d�CR@�����N��#�S�
I3�=�9���V�:"P�`S��}ɺ��9~;�IEND�B`�PK
!<א�WAABchrome/pdfjs/content/web/images/toolbarButton-presentationMode.png�PNG


IHDR��7�IDAT(Sc` ����:�HL�}���2(��(]�
�(x�ߎ�����mD�}pi����;Q��������<���ӓTܜ>��G:��=���C<T�2����iG��/��(���V�4Ȃ�I�y���a�����A����?*}��������[��̧�
z ~��
�O����E�����?�i��;�i�����?	=
Qp�4#���tD�� ���wU�SQ�
��&�����z:mIIEND�B`�PK
!<*=+JJEchrome/pdfjs/content/web/images/[email protected]�PNG


IHDR  �s�IDATx�=kQ�OjXH%����B�"��T�¦3V� +�|`B���������d��&V0nf7Y;���Ν���B�<���}�a���L1J0�����{��2�RrF�ɜ�Eq��uQ8K��
k�(�WѬz�(�K�:Ѭc�0�YC7���Oo�;����3���J�����Z�s/fx��M4�c���{{�ΫE��O-�OWx>�mx>��ٰ~J�=�~�
��S-�j>��e�C����C@��9��/��:ż"E�4a�(���ske����=�{��L�u����
��iN�_���0(M�5+x.&S�	���y�h��᠕PL��nL1)�~��P�5.΄ѓ�17�wq��V�+�(�z[�|�f��gp���}�FQ�
Ku����7Ӊ���t
���x��z���A-���Rœ)���� '����׉�(Vo�F4+�
�������(���uA9aE�"�C�(�P��'�3E��\(��)F(9#��1��zX�G���IEND�B`�PK
!<���7chrome/pdfjs/content/web/images/toolbarButton-print.png�PNG


IHDR��7��IDATx��=J�@�9��*Ϗlg��R�=�x�T����6E���AD�"��V�?Oa���+��?�g~/�3𗽹��/i��#
vsD�'0����>B~�9�gD��|���M`�[;�e_��ҫ@-z�ׁo��m�nY��e�Cy}n�y��c�4a�E�4��	渂5���+��P+�����3~cYE�IEND�B`�PK
!<��u��:chrome/pdfjs/content/web/images/[email protected]�PNG


IHDR  �s��IDATx��?o�@���du��W�� �\u�:vu_DGD�Nݪ"�-�mDC����A�=�E�%.?�܅Nlc���#ey�ztr�
�0���`�0D��Q�X�Ǎ�]��m��
�往#��lX�k�N���x�!��P4V��rk����@B�����7�7
%2�Y|�P"әE_C�Lg_4��t�&�I�v�(�����knB�����ĚܪN	E�2�C��M93u���Q��Æ���M5����}���~5���ؗz�?���?���lՃ�6�����rWG�R&��|���J�3�`�96f���I��⡺//�xqw�����	/�:t�`f�3�t�w�Q`'m��sH4�g��$��ޯ�)k(������KN^�u���_�Oz��|�S�IEND�B`�PK
!<N'�558chrome/pdfjs/content/web/images/toolbarButton-search.png�PNG


IHDR��7��IDAT���J���4*�&m
N�ИkX�����m9�9��8�c
E�?��=$E�{����ۢxQ
��HC����w1�f��1��-l�W�o���U�n���&%�՜�ZҲ��9����\�N*��T��k��1iwš���vL��^�|�h�#Ms|%��CL�E��^�"���]�X�B�#�5�O��Qѩ9�Q�@k�c�g��w��VS\q�҂wÃs{�RP?{9{��ԯ"�TP��	���H��7�IEND�B`�PK
!< ݚߍ�;chrome/pdfjs/content/web/images/[email protected]�PNG


IHDR  �s�TIDATx��OH��!��S�C�iT?�@���C�١�x���Yx�C�������H��i�okl�����s[y����9�}�|�\l�m������y>c�Ӌ�y��8�E�ϳ�X,�A/��1FEL���ނr^��(��D���ܶ�N�1^�h�	�8����������j�����(O�ZP�����h�
����u���t����肵S�a��ܴ��/��%7�2�d�e���ٷ��O	��U��ij(��͖�8�8`�r�tշ�UH�e���8�N`թ,)+�d����Q�E�FЀi��(����%Р��xiH��1����P@�N�q�!m����>"�͐D�{�Y�5��~em����	������C�lqg�>}T��^a=w�z^�7�N��(Gᢅn��;j^�g�'�������G���RB=5j�I�G�1"'�֒�?��/ң��eEN7��I-q��yױ�K��ˣ�}�ɜ����X���n�/��ݦ㨔��8���W��z{P^|�_�⸖��Ʊ��us�/���n��8�}�1-1��q`��ϕ��Ɓ�G����-�2vM�G�[����4PcaIEND�B`�PK
!<nI���Lchrome/pdfjs/content/web/images/toolbarButton-secondaryToolbarToggle-rtl.png�PNG


IHDR��7��IDATx��1+Eq���w�6��v��`�*��E�Jw�I)VI���A�n9����NO=���˧���_	X8`�I�m��X�O�f�Ύg�;
Xʎ��|{����K_|��e����p��o��`ja�����Ζ6���Տ�w>��m<ef�>��{w���t��=��u+[���u�����w�%��IEND�B`�PK
!<��~��Ochrome/pdfjs/content/web/images/[email protected]�PNG


IHDR  �s��IDATx��M�Lq��g�M�hJ�R�,&eᖒd�H)��Qbc#�S�.��‚�l$+5Eq_�s�u�ACs��1fHI4"��_�N����,,�9�ŷ�SG��
G�K|8��R�fk1W�D�)�˨��z�X�
>�Q�}�����9nӥ�B��y����p팬�+C�w4U�v����k�M����R�B�����Ò���BL���7yd��n�Xx�j���={%������*�+G#�$һ�O��џSTQ��cV#*�[�+��G�@U�4�q�9�$wp߷̡�Q�9�$[*}��j��-~7�,ݺ��*���l��9�dIq[�&Oψ��we�hѺ��u�����,�\�ʄb��c�C?�+�<I0�)��d����L�7��<�)8cIEND�B`�PK
!<�<����Hchrome/pdfjs/content/web/images/toolbarButton-secondaryToolbarToggle.png�PNG


IHDR��7��IDATxc��?�; ���?
AH����O�?���?32�Bh�Y��7�gEfC�)�ۋ������l�����_��ߎ��(�p�|�K�/}�~X�0��0��������X��0���?���fL�l�t���>Y�e��̆I7�?���ɬ@i$6"(��P�ME�ɶ�$8��IEND�B`�PK
!<�|fC��Kchrome/pdfjs/content/web/images/[email protected]�PNG


IHDR  �s��IDATx��O��q���7M�I�a��%�I9�RR� ��p��8�^������&� '�"�<�����i�Ƙ!%ш�������g�{S�y��|���eK���.�@"�=F�_�IK�)�3�{~���>�4��*���듮FC��&)�w�y_9�B�������>�\<�)QC�q��]���<��@Us3���<2t��y*��؄݉�ԑ��Y^��.d�Յ>$91�%�+����]�kn�=͋H'W�U�H�k�a�{�K��{��QI�n`��F��Ȣ����!��72. dA}�wp����m.G�y����$�.��zwk�nYiwo�u!�x��72;d��q��<!��W7��2���yf�Qũ�[%av�v<���I�=�Q�ӵ��K�����sA��4IEND�B`�PK
!<K�P���Cchrome/pdfjs/content/web/images/toolbarButton-sidebarToggle-rtl.png�PNG


IHDR��7��IDATx�б
�0��y�Ύ�(���oQ�8u�쬃���Aq�5�ԥSs�i(hq� p�s �?1����1&T�`�Y��YI�$�j0� ��@�"-FDa��/O0�p�3�Z	W��Z	W�
�LL��Q[���Q��Q��P�}){�K�E���,�
�:�m�{��IEND�B`�PK
!<j��XXFchrome/pdfjs/content/web/images/[email protected]�PNG


IHDR  �s�IDATx픱JAEO�N�$���G(����6bce!�2������),,$X��F����DRld�y���XI��\����QS �җa"��
��MjL�����~��G��(	�|���G�qc��m	��rz1 �)�=���k	�ѓ��p׎\�(��\���ڋ-��E4gZ�W�"2Yl��+3��'̿��	f�ʯԣ���0�_��?�[�gK�h(�-Ԣ�x���teDa
>�H:�{N�v~5����-Z�`Yg�r��R#�dd�`���H�!�7�"_@�OMIEND�B`�PK
!<x�����?chrome/pdfjs/content/web/images/toolbarButton-sidebarToggle.png�PNG


IHDR��7��IDATx�С
�P��o�%�m"�d�1V��aZ\7
�F�Gv��!ð�{��N�a�߁k�3��_.�aA3`h��))��V���2�eeV"��KH�n���7.�N��k���0��Wa�$X�����C�BM����K�G�oq����m���	IEND�B`�PK
!<+oh�KKBchrome/pdfjs/content/web/images/[email protected]�PNG


IHDR  �s�IDATx��=�SA�gr/A������`���`��*����H�� 
�b!i\���B�(3c#	d�F��ӟ�y_�.@�n�������	q�i|�N�.6�|��\$$8q�K� ����p�6�̍�����̞ޞ�	H�W2�{���a���.���=������∔*�|A.)��Ee�/���?�O8�p���6��Ѷ"��{U*T��:��x#���B���\������^B�������^��������j�w��_7�}j�=IEND�B`�PK
!<�Cj���Achrome/pdfjs/content/web/images/toolbarButton-viewAttachments.png�PNG


IHDR��7�GIDAT(Sc` �W�������K�w����l�b������>���{vV*�P��ɚ���o͜����dA����[M�{oM����$������3��z���&����>��&'���_���}�#���y{r�˿#����6E}���^͌�	��Gs��~�[���w�1�S��&�P���vx20��+��
�*��J�@������@��]�-���:�CŷM66pi `z��K)��b0d0�T�m��#�4L��w���F����sp``e@�m�_V�?��ԓ	����`%��.�إ!����0����4a�IEND�B`�PK
!<s%��[[Dchrome/pdfjs/content/web/images/[email protected]�PNG


IHDR  �s�"IDATx��C����m۶m۶m۶m۶�ƶm��k;�H�s��� �wY�و���'zJ��>N�A����|���?,Ai�'��Y7-д�@��t�3s�����	:��� ��N3�_t䨔zО�3��c�o��@L�?��]�`$�E;��?�S^����kD/�xH�p�p$�mim�t�Y���1T�Pm�� )ޝִt�+s�m�w��e	}BK��FH�[���N��?J�o�<g�A0���Va�	M& -i�p[�Gh�%���i�N��A���
P�n�M�n)�0->�Å[4�};��ķ�R��7
 ��4���M��i�1�IS�7�;���m4�O�BA@}�U4���u��M>��7h�J�[i&螚�€����o�LÍ��������4�}�Y�45���>(.j�kw�LCdbg��5�>��	لU45��8&o
u��5j�L���N͍9��*�}�ɜ�hĿW�ԅ�{˿_6"�=���_�A
�&\����hn ���%�0�����u}e��'����p�:>�����}}I��e9�⯻��AYQ��C#��}�/��_��H��X�O��u�"zu�/�9g�床-��W�4�-��u~����J�.�2s15��pe
��������	�$��__�G$_y�}�p���\ſ��z�#�2-��?�/\��l�N�'��o~~��w�����_�Gj$�E|�`���X�J힭r�&~�G~{itp� �A�ʌrh\�K��dm��ȅ�9�
iN���l0fIEND�B`�PK
!<�҆��Achrome/pdfjs/content/web/images/toolbarButton-viewOutline-rtl.png�PNG


IHDR��7�xIDATx��-A�������@��"8��ܠh�Ɋ&����g�LȒ =	�+��s�4�-3�L�w��u'�����?<Gk{ �F���Me����'�+A�&��,]���c��ME_iIEND�B`�PK
!<��c��Dchrome/pdfjs/content/web/images/[email protected]�PNG


IHDR  �s�QIDATx���JQ������!^� u9.�pcmrUbEXZ�ؗfR��J�v3���9�)cxvE�������._5���A�R��:�b7�f�nذ>i��v#7q% ��!��P�V��V=��D�Af�13�ǜ�_4�fGC��y�
���
-Bq�l��T8��5Agr��;�gy7]�euI�X��b	@�wn�5A��Xq�?P��b�:��J��6*������M�� �M�ԯ��A�Ǭs?��O%����?\筆
�d��A��ʄ��Xd�ɍ��#MP�Y��& �ba�n�ňZsˈ0�o���..e�r�IEND�B`�PK
!<�nβ�=chrome/pdfjs/content/web/images/toolbarButton-viewOutline.png�PNG


IHDR��7�yIDATx��-�A���c�0(?<������V�7����ɡ#O��)	���5|'@�c�2ѕ;��| q�c�L�wC�Gi^3ऎu[�x�'閮\K�H��U
o��?|�Uэ��}IEND�B`�PK
!<�7�,KK@chrome/pdfjs/content/web/images/[email protected]�PNG


IHDR  �s�IDATx���JQ����1���.G�E�CH��HC�ʴ�CC��l�n�8��7g��H�f�,>���L�:Q�V:E�ER@�S�~�9��<Ε|�p��?t:�P�la��뷫�Ac��N�(})�#�	g�ى���[�L��@0U�}s�_���|U��ʑS%J�rĿVCN9]
W�����@p
D�<;��oB��ؠ�žS!J/J��6�[^*���}��n����y7;i5Z�)���Z�$�B���IEND�B`�PK
!<tݾ~��?chrome/pdfjs/content/web/images/toolbarButton-viewThumbnail.png�PNG


IHDR��7��IDATx��1
Q�Ṓ���j�x�"����ł�,ĀW�%l��X�Wߔ���_v&�9���l�8�wIë$�a,�A|�K��l�˶ԫ�#��D�s�@q��8�[Iõ$a{^��`�ΰ�?̥�9,��K�IEND�B`�PK
!<�i[��Bchrome/pdfjs/content/web/images/[email protected]�PNG


IHDR  szz��IDATx�G�A�pv�	��p�Jf%�F|�Ir�ծ��a����qfi=V�)W��A��4��p6�O���ģ���8:J<z��w�ɤi{��l$�'Ec��Rp�q("R1�,��� Y���HP��Ά���%ԟ������a�6�F}��^�'k���9��XIEND�B`�PK
!<��,~��8chrome/pdfjs/content/web/images/toolbarButton-zoomIn.png�PNG


IHDR��7�OIDATxc�;���!�T�е����j�c(��@ٲ�&�l��<�/���"|�<	�hޤ��28!���+#� �IEND�B`�PK
!<�L?͠�;chrome/pdfjs/content/web/images/[email protected]�PNG


IHDR  �s�gIDATxc`0���­��?>�v^���tF
x�&���,.>���.��.M��QhcZB샊���NC�$g�Ѥ�0`��z�Q����8IEND�B`�PK
!<�xXX9chrome/pdfjs/content/web/images/toolbarButton-zoomOut.png�PNG


IHDR��7�IDATxcQ�;,��/aBX�0[3�v�IEND�B`�PK
!<4�mm<chrome/pdfjs/content/web/images/[email protected]�PNG


IHDR  �s�4IDATxc�`��Q����	�#�6�q�lj�8
`p R��Q0
r
�eKW�dIEND�B`�PK
!<ڈ�l��:chrome/pdfjs/content/web/images/treeitem-collapsed-rtl.png�PNG


IHDR		�VIDATxcx��.�ē@ll�"@\�'���"v N�CP�3Ȋ����� $0�<�n�#�ǧ�Y�8���V��@\��;��ۅ��OՆIEND�B`�PK
!<5�����=chrome/pdfjs/content/web/images/[email protected]�PNG


IHDRVΎWnIDATxcx���X��`|r`�4 >
�g�5��7@
8C�Ao 4cD�7�3��
"�
���az���x#���:��~jDȻ�O�8�K�A��K�b��wa|Kܢ��*<IEND�B`�PK
!<�J]��6chrome/pdfjs/content/web/images/treeitem-collapsed.png�PNG


IHDR		J�ΛGIDAT�c``x1�*xq���5/DP�@�Ћ��B ���&T!,BZ��Y�1/X�m,!�l#��?�GFs�z2IEND�B`�PK
!<~*�X��9chrome/pdfjs/content/web/images/[email protected]�PNG


IHDR��F�\IDATxc�}/��g^}�����"0����"YLP�b���X���c1B
��	lV������As8�%&�L���Ҥ�h�����IEND�B`�PK
!<+��}}5chrome/pdfjs/content/web/images/treeitem-expanded.png�PNG


IHDR		J�ΛDIDATxc ��}q�1�{qI`���0$!7�V��}�T_�
�!�lI6P��^�/!l��?�6�5IEND�B`�PK
!<����8chrome/pdfjs/content/web/images/[email protected]�PNG


IHDR��F�sIDATxc�3x1��p����)�J��Jǫ(
b!ۋM��e�`�éȖ��'��Q�@)���dtE��#����E�
����Er �c:��S�G7Nj�/6b8��N�
Ax�IEND�B`�PK
!<�T�d�d�#chrome/pdfjs/content/web/viewer.css/* Copyright 2014 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.
 */

.textLayer {
  position: absolute;
  left: 0;
  top: 0;
  right: 0;
  bottom: 0;
  overflow: hidden;
  opacity: 0.2;
  line-height: 1.0;
}

.textLayer > div {
  color: transparent;
  position: absolute;
  white-space: pre;
  cursor: text;
  -moz-transform-origin: 0% 0%;
  transform-origin: 0% 0%;
}

.textLayer .highlight {
  margin: -1px;
  padding: 1px;

  background-color: rgb(180, 0, 170);
  border-radius: 4px;
}

.textLayer .highlight.begin {
  border-radius: 4px 0px 0px 4px;
}

.textLayer .highlight.end {
  border-radius: 0px 4px 4px 0px;
}

.textLayer .highlight.middle {
  border-radius: 0px;
}

.textLayer .highlight.selected {
  background-color: rgb(0, 100, 0);
}

.textLayer ::selection { background: rgb(0,0,255); }
.textLayer ::-moz-selection { background: rgb(0,0,255); }

.textLayer .endOfContent {
  display: block;
  position: absolute;
  left: 0px;
  top: 100%;
  right: 0px;
  bottom: 0px;
  z-index: -1;
  cursor: default;
  -moz-user-select: none;
}

.textLayer .endOfContent.active {
  top: 0px;
}


.annotationLayer section {
  position: absolute;
}

.annotationLayer .linkAnnotation > a,
.annotationLayer .buttonWidgetAnnotation.pushButton > a {
  position: absolute;
  font-size: 1em;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

.annotationLayer .linkAnnotation > a:hover,
.annotationLayer .buttonWidgetAnnotation.pushButton > a:hover {
  opacity: 0.2;
  background: #ff0;
  box-shadow: 0px 2px 10px #ff0;
}

.annotationLayer .textAnnotation img {
  position: absolute;
  cursor: pointer;
}

.annotationLayer .textWidgetAnnotation input,
.annotationLayer .textWidgetAnnotation textarea,
.annotationLayer .choiceWidgetAnnotation select,
.annotationLayer .buttonWidgetAnnotation.checkBox input,
.annotationLayer .buttonWidgetAnnotation.radioButton input {
  background-color: rgba(0, 54, 255, 0.13);
  border: 1px solid transparent;
  box-sizing: border-box;
  font-size: 9px;
  height: 100%;
  margin: 0;
  padding: 0 3px;
  vertical-align: top;
  width: 100%;
}

.annotationLayer .choiceWidgetAnnotation select option {
  padding: 0;
}

.annotationLayer .buttonWidgetAnnotation.radioButton input {
  border-radius: 50%;
}

.annotationLayer .textWidgetAnnotation textarea {
  font: message-box;
  font-size: 9px;
  resize: none;
}

.annotationLayer .textWidgetAnnotation input[disabled],
.annotationLayer .textWidgetAnnotation textarea[disabled],
.annotationLayer .choiceWidgetAnnotation select[disabled],
.annotationLayer .buttonWidgetAnnotation.checkBox input[disabled],
.annotationLayer .buttonWidgetAnnotation.radioButton input[disabled] {
  background: none;
  border: 1px solid transparent;
  cursor: not-allowed;
}

.annotationLayer .textWidgetAnnotation input:hover,
.annotationLayer .textWidgetAnnotation textarea:hover,
.annotationLayer .choiceWidgetAnnotation select:hover,
.annotationLayer .buttonWidgetAnnotation.checkBox input:hover,
.annotationLayer .buttonWidgetAnnotation.radioButton input:hover {
  border: 1px solid #000;
}

.annotationLayer .textWidgetAnnotation input:focus,
.annotationLayer .textWidgetAnnotation textarea:focus,
.annotationLayer .choiceWidgetAnnotation select:focus {
  background: none;
  border: 1px solid transparent;
}

.annotationLayer .buttonWidgetAnnotation.checkBox input:checked:before,
.annotationLayer .buttonWidgetAnnotation.checkBox input:checked:after,
.annotationLayer .buttonWidgetAnnotation.radioButton input:checked:before {
  background-color: #000;
  content: '';
  display: block;
  position: absolute;
}

.annotationLayer .buttonWidgetAnnotation.checkBox input:checked:before,
.annotationLayer .buttonWidgetAnnotation.checkBox input:checked:after {
  height: 80%;
  left: 45%;
  width: 1px;
}

.annotationLayer .buttonWidgetAnnotation.checkBox input:checked:before {
  transform: rotate(45deg);
}

.annotationLayer .buttonWidgetAnnotation.checkBox input:checked:after {
  transform: rotate(-45deg);
}

.annotationLayer .buttonWidgetAnnotation.radioButton input:checked:before {
  border-radius: 50%;
  height: 50%;
  left: 30%;
  top: 20%;
  width: 50%;
}

.annotationLayer .textWidgetAnnotation input.comb {
  font-family: monospace;
  padding-left: 2px;
  padding-right: 0;
}

.annotationLayer .textWidgetAnnotation input.comb:focus {
  /*
   * Letter spacing is placed on the right side of each character. Hence, the
   * letter spacing of the last character may be placed outside the visible
   * area, causing horizontal scrolling. We avoid this by extending the width
   * when the element has focus and revert this when it loses focus.
   */
  width: 115%;
}

.annotationLayer .buttonWidgetAnnotation.checkBox input,
.annotationLayer .buttonWidgetAnnotation.radioButton input {
  -moz-appearance: none;
  appearance: none;
  padding: 0;
}

.annotationLayer .popupWrapper {
  position: absolute;
  width: 20em;
}

.annotationLayer .popup {
  position: absolute;
  z-index: 200;
  max-width: 20em;
  background-color: #FFFF99;
  box-shadow: 0px 2px 5px #333;
  border-radius: 2px;
  padding: 0.6em;
  margin-left: 5px;
  cursor: pointer;
  font: message-box;
  word-wrap: break-word;
}

.annotationLayer .popup h1 {
  font-size: 1em;
  border-bottom: 1px solid #000000;
  margin: 0;
  padding-bottom: 0.2em;
}

.annotationLayer .popup p {
  margin: 0;
  padding-top: 0.2em;
}

.annotationLayer .highlightAnnotation,
.annotationLayer .underlineAnnotation,
.annotationLayer .squigglyAnnotation,
.annotationLayer .strikeoutAnnotation,
.annotationLayer .lineAnnotation svg line,
.annotationLayer .squareAnnotation svg rect,
.annotationLayer .circleAnnotation svg ellipse,
.annotationLayer .polylineAnnotation svg polyline,
.annotationLayer .polygonAnnotation svg polygon,
.annotationLayer .stampAnnotation,
.annotationLayer .fileAttachmentAnnotation {
  cursor: pointer;
}

.pdfViewer .canvasWrapper {
  overflow: hidden;
}

.pdfViewer .page {
  direction: ltr;
  width: 816px;
  height: 1056px;
  margin: 1px auto -8px auto;
  position: relative;
  overflow: visible;
  border: 9px solid transparent;
  background-clip: content-box;
  border-image: url(images/shadow.png) 9 9 repeat;
  background-color: white;
}

.pdfViewer.removePageBorders .page {
  margin: 0px auto 10px auto;
  border: none;
}

.pdfViewer.singlePageView {
  display: inline-block;
}

.pdfViewer.singlePageView .page {
  margin: 0;
  border: none;
}

.pdfViewer .page canvas {
  margin: 0;
  display: block;
}

.pdfViewer .page canvas[hidden] {
  display: none;
}

.pdfViewer .page .loadingIcon {
  position: absolute;
  display: block;
  left: 0;
  top: 0;
  right: 0;
  bottom: 0;
  background: url('images/loading-icon.gif') center no-repeat;
}

.pdfPresentationMode:-moz-full-screen .pdfViewer .page {
  margin-bottom: 100%;
  border: 0;
}

.pdfPresentationMode:fullscreen .pdfViewer .page {
  margin-bottom: 100%;
  border: 0;
}

:root {
  --sidebar-width: 200px;
}

* {
  padding: 0;
  margin: 0;
}

html {
  height: 100%;
  width: 100%;
  /* Font size is needed to make the activity bar the correct size. */
  font-size: 10px;
}

body {
  height: 100%;
  width: 100%;
  background-color: #404040;
  background-image: url(images/texture.png);
}

body,
input,
button,
select {
  font: message-box;
  outline: none;
}

.hidden {
  display: none !important;
}
[hidden] {
  display: none !important;
}

#viewerContainer.pdfPresentationMode:-moz-full-screen {
  top: 0px;
  border-top: 2px solid transparent;
  background-color: #000;
  width: 100%;
  height: 100%;
  overflow: hidden;
  cursor: none;
  -moz-user-select: none;
}

#viewerContainer.pdfPresentationMode:fullscreen {
  top: 0px;
  border-top: 2px solid transparent;
  background-color: #000;
  width: 100%;
  height: 100%;
  overflow: hidden;
  cursor: none;
  -moz-user-select: none;
}

.pdfPresentationMode:-moz-full-screen a:not(.internalLink) {
  display: none;
}

.pdfPresentationMode:fullscreen a:not(.internalLink) {
  display: none;
}

.pdfPresentationMode:-moz-full-screen .textLayer > div {
  cursor: none;
}

.pdfPresentationMode:fullscreen .textLayer > div {
  cursor: none;
}

.pdfPresentationMode.pdfPresentationModeControls > *,
.pdfPresentationMode.pdfPresentationModeControls .textLayer > div {
  cursor: default;
}

#outerContainer {
  width: 100%;
  height: 100%;
  position: relative;
}

#sidebarContainer {
  position: absolute;
  top: 32px;
  bottom: 0;
  width: 200px; /* Here, and elsewhere below, keep the constant value for compatibility
                   with older browsers that lack support for CSS variables. */
  width: var(--sidebar-width);
  visibility: hidden;
  z-index: 100;
  border-top: 1px solid #333;

  transition-duration: 200ms;
  transition-timing-function: ease;
}
html[dir='ltr'] #sidebarContainer {
  transition-property: left;
  left: -200px;
  left: calc(-1 * var(--sidebar-width));
}
html[dir='rtl'] #sidebarContainer {
  transition-property: right;
  right: -200px;
  right: calc(-1 * var(--sidebar-width));
}

.loadingInProgress #sidebarContainer {
  top: 36px;
}

#outerContainer.sidebarResizing #sidebarContainer {
  /* Improve responsiveness and avoid visual glitches when the sidebar is resized. */
  transition-duration: 0s;
  /* Prevent e.g. the thumbnails being selected when the sidebar is resized. */
  -moz-user-select: none;
}

#outerContainer.sidebarMoving #sidebarContainer,
#outerContainer.sidebarOpen #sidebarContainer {
  visibility: visible;
}
html[dir='ltr'] #outerContainer.sidebarOpen #sidebarContainer {
  left: 0px;
}
html[dir='rtl'] #outerContainer.sidebarOpen #sidebarContainer {
  right: 0px;
}

#mainContainer {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  min-width: 320px;
}

#sidebarContent {
  top: 32px;
  bottom: 0;
  overflow: auto;
  position: absolute;
  width: 100%;
  background-color: hsla(0,0%,0%,.1);
}
html[dir='ltr'] #sidebarContent {
  left: 0;
  box-shadow: inset -1px 0 0 hsla(0,0%,0%,.25);
}
html[dir='rtl'] #sidebarContent {
  right: 0;
  box-shadow: inset 1px 0 0 hsla(0,0%,0%,.25);
}

#viewerContainer {
  overflow: auto;
  position: absolute;
  top: 32px;
  right: 0;
  bottom: 0;
  left: 0;
  outline: none;

  transition-duration: 200ms;
  transition-timing-function: ease;
}
html[dir='ltr'] #viewerContainer {
  box-shadow: inset 1px 0 0 hsla(0,0%,100%,.05);
}
html[dir='rtl'] #viewerContainer {
  box-shadow: inset -1px 0 0 hsla(0,0%,100%,.05);
}

#outerContainer.sidebarResizing #viewerContainer {
  /* Improve responsiveness and avoid visual glitches when the sidebar is resized. */
  transition-duration: 0s;
}

html[dir='ltr'] #outerContainer.sidebarOpen #viewerContainer {
  transition-property: left;
  left: 200px;
  left: var(--sidebar-width);
}
html[dir='rtl'] #outerContainer.sidebarOpen #viewerContainer {
  transition-property: right;
  right: 200px;
  right: var(--sidebar-width);
}

.toolbar {
  position: relative;
  left: 0;
  right: 0;
  z-index: 9999;
  cursor: default;
}

#toolbarContainer {
  width: 100%;
}

#toolbarSidebar {
  width: 100%;
  height: 32px;
  background-color: #424242; /* fallback */
  background-image: url(images/texture.png),
                    linear-gradient(hsla(0,0%,30%,.99), hsla(0,0%,25%,.95));
}
html[dir='ltr'] #toolbarSidebar {
  box-shadow: inset -1px 0 0 rgba(0, 0, 0, 0.25),
              inset 0 -1px 0 hsla(0,0%,100%,.05),
              0 1px 0 hsla(0,0%,0%,.15),
              0 0 1px hsla(0,0%,0%,.1);
}
html[dir='rtl'] #toolbarSidebar {
  box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.25),
              inset 0 1px 0 hsla(0,0%,100%,.05),
              0 1px 0 hsla(0,0%,0%,.15),
              0 0 1px hsla(0,0%,0%,.1);
}

#sidebarResizer {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 6px;
  z-index: 200;
  cursor: ew-resize;
}
html[dir='ltr'] #sidebarResizer {
  right: -6px;
}
html[dir='rtl'] #sidebarResizer {
  left: -6px;
}

#toolbarContainer, .findbar, .secondaryToolbar {
  position: relative;
  height: 32px;
  background-color: #474747; /* fallback */
  background-image: url(images/texture.png),
                    linear-gradient(hsla(0,0%,32%,.99), hsla(0,0%,27%,.95));
}
html[dir='ltr'] #toolbarContainer, .findbar, .secondaryToolbar {
  box-shadow: inset 0 1px 1px hsla(0,0%,0%,.15),
              inset 0 -1px 0 hsla(0,0%,100%,.05),
              0 1px 0 hsla(0,0%,0%,.15),
              0 1px 1px hsla(0,0%,0%,.1);
}
html[dir='rtl'] #toolbarContainer, .findbar, .secondaryToolbar {
  box-shadow: inset 0 1px 1px hsla(0,0%,0%,.15),
              inset 0 -1px 0 hsla(0,0%,100%,.05),
              0 1px 0 hsla(0,0%,0%,.15),
              0 1px 1px hsla(0,0%,0%,.1);
}

#toolbarViewer {
  height: 32px;
}

#loadingBar {
  position: relative;
  width: 100%;
  height: 4px;
  background-color: #333;
  border-bottom: 1px solid #333;
}

#loadingBar .progress {
  position: absolute;
  top: 0;
  left: 0;
  width: 0%;
  height: 100%;
  background-color: #ddd;
  overflow: hidden;
  transition: width 200ms;
}

@keyframes progressIndeterminate {
  0% { left: -142px; }
  100% { left: 0; }
}

#loadingBar .progress.indeterminate {
  background-color: #999;
  transition: none;
}

#loadingBar .progress.indeterminate .glimmer {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  width: calc(100% + 150px);

  background: repeating-linear-gradient(135deg,
                                        #bbb 0, #999 5px,
                                        #999 45px, #ddd 55px,
                                        #ddd 95px, #bbb 100px);

  animation: progressIndeterminate 950ms linear infinite;
}

.findbar, .secondaryToolbar {
  top: 32px;
  position: absolute;
  z-index: 10000;
  height: auto;
  min-width: 16px;
  padding: 0px 6px 0px 6px;
  margin: 4px 2px 4px 2px;
  color: hsl(0,0%,85%);
  font-size: 12px;
  line-height: 14px;
  text-align: left;
  cursor: default;
}

.findbar {
  min-width: 300px;
}
.findbar > div {
  height: 32px;
}
.findbar.wrapContainers > div {
  clear: both;
}
.findbar.wrapContainers > div#findbarMessageContainer {
  height: auto;
}
html[dir='ltr'] .findbar {
  left: 68px;
}
html[dir='rtl'] .findbar {
  right: 68px;
}

.findbar label {
  -moz-user-select: none;
}

#findInput {
  width: 200px;
}
#findInput::-moz-placeholder {
  font-style: italic;
}
#findInput::placeholder {
  font-style: italic;
}
#findInput[data-status="pending"] {
  background-image: url(images/loading-small.png);
  background-repeat: no-repeat;
  background-position: right;
}
html[dir='rtl'] #findInput[data-status="pending"] {
  background-position: left;
}

.secondaryToolbar {
  padding: 6px;
  height: auto;
  z-index: 30000;
}
html[dir='ltr'] .secondaryToolbar {
  right: 4px;
}
html[dir='rtl'] .secondaryToolbar {
  left: 4px;
}

#secondaryToolbarButtonContainer {
  max-width: 200px;
  max-height: 400px;
  overflow-y: auto;
  margin-bottom: -4px;
}

.doorHanger,
.doorHangerRight {
  border: 1px solid hsla(0,0%,0%,.5);
  border-radius: 2px;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
}
.doorHanger:after, .doorHanger:before,
.doorHangerRight:after, .doorHangerRight:before {
  bottom: 100%;
  border: solid transparent;
  content: " ";
  height: 0;
  width: 0;
  position: absolute;
  pointer-events: none;
}
.doorHanger:after,
.doorHangerRight:after {
  border-bottom-color: hsla(0,0%,32%,.99);
  border-width: 8px;
}
.doorHanger:before,
.doorHangerRight:before {
  border-bottom-color: hsla(0,0%,0%,.5);
  border-width: 9px;
}

html[dir='ltr'] .doorHanger:after,
html[dir='rtl'] .doorHangerRight:after {
  left: 13px;
  margin-left: -8px;
}

html[dir='ltr'] .doorHanger:before,
html[dir='rtl'] .doorHangerRight:before {
  left: 13px;
  margin-left: -9px;
}

html[dir='rtl'] .doorHanger:after,
html[dir='ltr'] .doorHangerRight:after {
  right: 13px;
  margin-right: -8px;
}

html[dir='rtl'] .doorHanger:before,
html[dir='ltr'] .doorHangerRight:before {
  right: 13px;
  margin-right: -9px;
}

#findResultsCount {
  background-color: hsl(0, 0%, 85%);
  color: hsl(0, 0%, 32%);
  text-align: center;
  padding: 3px 4px;
}

#findMsg {
  font-style: italic;
  color: #A6B7D0;
}
#findMsg:empty {
  display: none;
}

#findInput.notFound {
  background-color: rgb(255, 102, 102);
}

#toolbarViewerMiddle {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
}

html[dir='ltr'] #toolbarViewerLeft,
html[dir='rtl'] #toolbarViewerRight {
  float: left;
}
html[dir='ltr'] #toolbarViewerRight,
html[dir='rtl'] #toolbarViewerLeft {
  float: right;
}
html[dir='ltr'] #toolbarViewerLeft > *,
html[dir='ltr'] #toolbarViewerMiddle > *,
html[dir='ltr'] #toolbarViewerRight > *,
html[dir='ltr'] .findbar * {
  position: relative;
  float: left;
}
html[dir='rtl'] #toolbarViewerLeft > *,
html[dir='rtl'] #toolbarViewerMiddle > *,
html[dir='rtl'] #toolbarViewerRight > *,
html[dir='rtl'] .findbar * {
  position: relative;
  float: right;
}

html[dir='ltr'] .splitToolbarButton {
  margin: 3px 2px 4px 0;
  display: inline-block;
}
html[dir='rtl'] .splitToolbarButton {
  margin: 3px 0 4px 2px;
  display: inline-block;
}
html[dir='ltr'] .splitToolbarButton > .toolbarButton {
  border-radius: 0;
  float: left;
}
html[dir='rtl'] .splitToolbarButton > .toolbarButton {
  border-radius: 0;
  float: right;
}

.toolbarButton,
.secondaryToolbarButton,
.overlayButton {
  border: 0 none;
  background: none;
  width: 32px;
  height: 25px;
}

.toolbarButton > span {
  display: inline-block;
  width: 0;
  height: 0;
  overflow: hidden;
}

.toolbarButton[disabled],
.secondaryToolbarButton[disabled],
.overlayButton[disabled] {
  opacity: .5;
}

.splitToolbarButton.toggled .toolbarButton {
  margin: 0;
}

.splitToolbarButton:hover > .toolbarButton,
.splitToolbarButton:focus > .toolbarButton,
.splitToolbarButton.toggled > .toolbarButton,
.toolbarButton.textButton {
  background-color: hsla(0,0%,0%,.12);
  background-image: linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0));
  background-clip: padding-box;
  border: 1px solid hsla(0,0%,0%,.35);
  border-color: hsla(0,0%,0%,.32) hsla(0,0%,0%,.38) hsla(0,0%,0%,.42);
  box-shadow: 0 1px 0 hsla(0,0%,100%,.05) inset,
              0 0 1px hsla(0,0%,100%,.15) inset,
              0 1px 0 hsla(0,0%,100%,.05);
  transition-property: background-color, border-color, box-shadow;
  transition-duration: 150ms;
  transition-timing-function: ease;

}
.splitToolbarButton > .toolbarButton:hover,
.splitToolbarButton > .toolbarButton:focus,
.dropdownToolbarButton:hover,
.overlayButton:hover,
.overlayButton:focus,
.toolbarButton.textButton:hover,
.toolbarButton.textButton:focus {
  background-color: hsla(0,0%,0%,.2);
  box-shadow: 0 1px 0 hsla(0,0%,100%,.05) inset,
              0 0 1px hsla(0,0%,100%,.15) inset,
              0 0 1px hsla(0,0%,0%,.05);
  z-index: 199;
}
.splitToolbarButton > .toolbarButton {
  position: relative;
}
html[dir='ltr'] .splitToolbarButton > .toolbarButton:first-child,
html[dir='rtl'] .splitToolbarButton > .toolbarButton:last-child {
  position: relative;
  margin: 0;
  margin-right: -1px;
  border-top-left-radius: 2px;
  border-bottom-left-radius: 2px;
  border-right-color: transparent;
}
html[dir='ltr'] .splitToolbarButton > .toolbarButton:last-child,
html[dir='rtl'] .splitToolbarButton > .toolbarButton:first-child {
  position: relative;
  margin: 0;
  margin-left: -1px;
  border-top-right-radius: 2px;
  border-bottom-right-radius: 2px;
  border-left-color: transparent;
}
.splitToolbarButtonSeparator {
  padding: 8px 0;
  width: 1px;
  background-color: hsla(0,0%,0%,.5);
  z-index: 99;
  box-shadow: 0 0 0 1px hsla(0,0%,100%,.08);
  display: inline-block;
  margin: 5px 0;
}
html[dir='ltr'] .splitToolbarButtonSeparator {
  float: left;
}
html[dir='rtl'] .splitToolbarButtonSeparator {
  float: right;
}
.splitToolbarButton:hover > .splitToolbarButtonSeparator,
.splitToolbarButton.toggled > .splitToolbarButtonSeparator {
  padding: 12px 0;
  margin: 1px 0;
  box-shadow: 0 0 0 1px hsla(0,0%,100%,.03);
  transition-property: padding;
  transition-duration: 10ms;
  transition-timing-function: ease;
}

.toolbarButton,
.dropdownToolbarButton,
.secondaryToolbarButton,
.overlayButton {
  min-width: 16px;
  padding: 2px 6px 0;
  border: 1px solid transparent;
  border-radius: 2px;
  color: hsla(0,0%,100%,.8);
  font-size: 12px;
  line-height: 14px;
  -moz-user-select: none;
  /* Opera does not support user-select, use <... unselectable="on"> instead */
  cursor: default;
  transition-property: background-color, border-color, box-shadow;
  transition-duration: 150ms;
  transition-timing-function: ease;
}

html[dir='ltr'] .toolbarButton,
html[dir='ltr'] .overlayButton,
html[dir='ltr'] .dropdownToolbarButton {
  margin: 3px 2px 4px 0;
}
html[dir='rtl'] .toolbarButton,
html[dir='rtl'] .overlayButton,
html[dir='rtl'] .dropdownToolbarButton {
  margin: 3px 0 4px 2px;
}

.toolbarButton:hover,
.toolbarButton:focus,
.dropdownToolbarButton,
.overlayButton,
.secondaryToolbarButton:hover,
.secondaryToolbarButton:focus {
  background-color: hsla(0,0%,0%,.12);
  background-image: linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0));
  background-clip: padding-box;
  border: 1px solid hsla(0,0%,0%,.35);
  border-color: hsla(0,0%,0%,.32) hsla(0,0%,0%,.38) hsla(0,0%,0%,.42);
  box-shadow: 0 1px 0 hsla(0,0%,100%,.05) inset,
              0 0 1px hsla(0,0%,100%,.15) inset,
              0 1px 0 hsla(0,0%,100%,.05);
}

.toolbarButton:hover:active,
.overlayButton:hover:active,
.dropdownToolbarButton:hover:active,
.secondaryToolbarButton:hover:active {
  background-color: hsla(0,0%,0%,.2);
  background-image: linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0));
  border-color: hsla(0,0%,0%,.35) hsla(0,0%,0%,.4) hsla(0,0%,0%,.45);
  box-shadow: 0 1px 1px hsla(0,0%,0%,.1) inset,
              0 0 1px hsla(0,0%,0%,.2) inset,
              0 1px 0 hsla(0,0%,100%,.05);
  transition-property: background-color, border-color, box-shadow;
  transition-duration: 10ms;
  transition-timing-function: linear;
}

.toolbarButton.toggled,
.splitToolbarButton.toggled > .toolbarButton.toggled,
.secondaryToolbarButton.toggled {
  background-color: hsla(0,0%,0%,.3);
  background-image: linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0));
  border-color: hsla(0,0%,0%,.4) hsla(0,0%,0%,.45) hsla(0,0%,0%,.5);
  box-shadow: 0 1px 1px hsla(0,0%,0%,.1) inset,
              0 0 1px hsla(0,0%,0%,.2) inset,
              0 1px 0 hsla(0,0%,100%,.05);
  transition-property: background-color, border-color, box-shadow;
  transition-duration: 10ms;
  transition-timing-function: linear;
}

.toolbarButton.toggled:hover:active,
.splitToolbarButton.toggled > .toolbarButton.toggled:hover:active,
.secondaryToolbarButton.toggled:hover:active {
  background-color: hsla(0,0%,0%,.4);
  border-color: hsla(0,0%,0%,.4) hsla(0,0%,0%,.5) hsla(0,0%,0%,.55);
  box-shadow: 0 1px 1px hsla(0,0%,0%,.2) inset,
              0 0 1px hsla(0,0%,0%,.3) inset,
              0 1px 0 hsla(0,0%,100%,.05);
}

.dropdownToolbarButton {
  width: 120px;
  max-width: 120px;
  padding: 0;
  overflow: hidden;
  background: url(images/toolbarButton-menuArrows.png) no-repeat;
}
html[dir='ltr'] .dropdownToolbarButton {
  background-position: 95%;
}
html[dir='rtl'] .dropdownToolbarButton {
  background-position: 5%;
}

.dropdownToolbarButton > select {
  min-width: 140px;
  font-size: 12px;
  color: hsl(0,0%,95%);
  margin: 0;
  padding: 3px 2px 2px;
  border: none;
  background: rgba(0,0,0,0); /* Opera does not support 'transparent' <select> background */
}

.dropdownToolbarButton > select > option {
  background: hsl(0,0%,24%);
}

#customScaleOption {
  display: none;
}

#pageWidthOption {
  border-bottom: 1px rgba(255, 255, 255, .5) solid;
}

html[dir='ltr'] .splitToolbarButton:first-child,
html[dir='ltr'] .toolbarButton:first-child,
html[dir='rtl'] .splitToolbarButton:last-child,
html[dir='rtl'] .toolbarButton:last-child {
  margin-left: 4px;
}
html[dir='ltr'] .splitToolbarButton:last-child,
html[dir='ltr'] .toolbarButton:last-child,
html[dir='rtl'] .splitToolbarButton:first-child,
html[dir='rtl'] .toolbarButton:first-child {
  margin-right: 4px;
}

.toolbarButtonSpacer {
  width: 30px;
  display: inline-block;
  height: 1px;
}

html[dir='ltr'] #findPrevious {
  margin-left: 3px;
}
html[dir='ltr'] #findNext {
  margin-right: 3px;
}

html[dir='rtl'] #findPrevious {
  margin-right: 3px;
}
html[dir='rtl'] #findNext {
  margin-left: 3px;
}

.toolbarButton::before,
.secondaryToolbarButton::before {
  /* All matching images have a size of 16x16
   * All relevant containers have a size of 32x25 */
  position: absolute;
  display: inline-block;
  top: 4px;
  left: 7px;
}

html[dir="ltr"] .secondaryToolbarButton::before {
  left: 4px;
}
html[dir="rtl"] .secondaryToolbarButton::before {
  right: 4px;
}

html[dir='ltr'] .toolbarButton#sidebarToggle::before {
  content: url(images/toolbarButton-sidebarToggle.png);
}
html[dir='rtl'] .toolbarButton#sidebarToggle::before {
  content: url(images/toolbarButton-sidebarToggle-rtl.png);
}

html[dir='ltr'] .toolbarButton#secondaryToolbarToggle::before {
  content: url(images/toolbarButton-secondaryToolbarToggle.png);
}
html[dir='rtl'] .toolbarButton#secondaryToolbarToggle::before {
  content: url(images/toolbarButton-secondaryToolbarToggle-rtl.png);
}

html[dir='ltr'] .toolbarButton.findPrevious::before {
  content: url(images/findbarButton-previous.png);
}
html[dir='rtl'] .toolbarButton.findPrevious::before {
  content: url(images/findbarButton-previous-rtl.png);
}

html[dir='ltr'] .toolbarButton.findNext::before {
  content: url(images/findbarButton-next.png);
}
html[dir='rtl'] .toolbarButton.findNext::before {
  content: url(images/findbarButton-next-rtl.png);
}

html[dir='ltr'] .toolbarButton.pageUp::before {
  content: url(images/toolbarButton-pageUp.png);
}
html[dir='rtl'] .toolbarButton.pageUp::before {
  content: url(images/toolbarButton-pageUp-rtl.png);
}

html[dir='ltr'] .toolbarButton.pageDown::before {
  content: url(images/toolbarButton-pageDown.png);
}
html[dir='rtl'] .toolbarButton.pageDown::before {
  content: url(images/toolbarButton-pageDown-rtl.png);
}

.toolbarButton.zoomOut::before {
  content: url(images/toolbarButton-zoomOut.png);
}

.toolbarButton.zoomIn::before {
  content: url(images/toolbarButton-zoomIn.png);
}

.toolbarButton.presentationMode::before,
.secondaryToolbarButton.presentationMode::before {
  content: url(images/toolbarButton-presentationMode.png);
}

.toolbarButton.print::before,
.secondaryToolbarButton.print::before {
  content: url(images/toolbarButton-print.png);
}

.toolbarButton.openFile::before,
.secondaryToolbarButton.openFile::before {
  content: url(images/toolbarButton-openFile.png);
}

.toolbarButton.download::before,
.secondaryToolbarButton.download::before {
  content: url(images/toolbarButton-download.png);
}

.toolbarButton.bookmark,
.secondaryToolbarButton.bookmark {
  box-sizing: border-box;
  outline: none;
  padding-top: 4px;
  text-decoration: none;
}
.secondaryToolbarButton.bookmark {
  padding-top: 5px;
}

.bookmark[href='#'] {
  opacity: .5;
  pointer-events: none;
}

.toolbarButton.bookmark::before,
.secondaryToolbarButton.bookmark::before {
  content: url(images/toolbarButton-bookmark.png);
}

#viewThumbnail.toolbarButton::before {
  content: url(images/toolbarButton-viewThumbnail.png);
}

html[dir="ltr"] #viewOutline.toolbarButton::before {
  content: url(images/toolbarButton-viewOutline.png);
}
html[dir="rtl"] #viewOutline.toolbarButton::before {
  content: url(images/toolbarButton-viewOutline-rtl.png);
}

#viewAttachments.toolbarButton::before {
  content: url(images/toolbarButton-viewAttachments.png);
}

#viewFind.toolbarButton::before {
  content: url(images/toolbarButton-search.png);
}

.toolbarButton.pdfSidebarNotification::after {
  position: absolute;
  display: inline-block;
  top: 1px;
  /* Create a filled circle, with a diameter of 9 pixels, using only CSS: */
  content: '';
  background-color: #70DB55;
  height: 9px;
  width: 9px;
  border-radius: 50%;
}
html[dir='ltr'] .toolbarButton.pdfSidebarNotification::after {
  left: 17px;
}
html[dir='rtl'] .toolbarButton.pdfSidebarNotification::after {
  right: 17px;
}

.secondaryToolbarButton {
  position: relative;
  margin: 0 0 4px 0;
  padding: 3px 0 1px 0;
  height: auto;
  min-height: 25px;
  width: auto;
  min-width: 100%;
  white-space: normal;
}
html[dir="ltr"] .secondaryToolbarButton {
  padding-left: 24px;
  text-align: left;
}
html[dir="rtl"] .secondaryToolbarButton {
  padding-right: 24px;
  text-align: right;
}
html[dir="ltr"] .secondaryToolbarButton.bookmark {
  padding-left: 27px;
}
html[dir="rtl"] .secondaryToolbarButton.bookmark {
  padding-right: 27px;
}

html[dir="ltr"] .secondaryToolbarButton > span {
  padding-right: 4px;
}
html[dir="rtl"] .secondaryToolbarButton > span {
  padding-left: 4px;
}

.secondaryToolbarButton.firstPage::before {
  content: url(images/secondaryToolbarButton-firstPage.png);
}

.secondaryToolbarButton.lastPage::before {
  content: url(images/secondaryToolbarButton-lastPage.png);
}

.secondaryToolbarButton.rotateCcw::before {
  content: url(images/secondaryToolbarButton-rotateCcw.png);
}

.secondaryToolbarButton.rotateCw::before {
  content: url(images/secondaryToolbarButton-rotateCw.png);
}

.secondaryToolbarButton.selectTool::before {
  content: url(images/secondaryToolbarButton-selectTool.png);
}

.secondaryToolbarButton.handTool::before {
  content: url(images/secondaryToolbarButton-handTool.png);
}

.secondaryToolbarButton.documentProperties::before {
  content: url(images/secondaryToolbarButton-documentProperties.png);
}

.verticalToolbarSeparator {
  display: block;
  padding: 8px 0;
  margin: 8px 4px;
  width: 1px;
  background-color: hsla(0,0%,0%,.5);
  box-shadow: 0 0 0 1px hsla(0,0%,100%,.08);
}
html[dir='ltr'] .verticalToolbarSeparator {
  margin-left: 2px;
}
html[dir='rtl'] .verticalToolbarSeparator {
  margin-right: 2px;
}

.horizontalToolbarSeparator {
  display: block;
  margin: 0 0 4px 0;
  height: 1px;
  width: 100%;
  background-color: hsla(0,0%,0%,.5);
  box-shadow: 0 0 0 1px hsla(0,0%,100%,.08);
}

.toolbarField {
  padding: 3px 6px;
  margin: 4px 0 4px 0;
  border: 1px solid transparent;
  border-radius: 2px;
  background-color: hsla(0,0%,100%,.09);
  background-image: linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0));
  background-clip: padding-box;
  border: 1px solid hsla(0,0%,0%,.35);
  border-color: hsla(0,0%,0%,.32) hsla(0,0%,0%,.38) hsla(0,0%,0%,.42);
  box-shadow: 0 1px 0 hsla(0,0%,0%,.05) inset,
              0 1px 0 hsla(0,0%,100%,.05);
  color: hsl(0,0%,95%);
  font-size: 12px;
  line-height: 14px;
  outline-style: none;
  transition-property: background-color, border-color, box-shadow;
  transition-duration: 150ms;
  transition-timing-function: ease;
}

.toolbarField[type=checkbox] {
  display: inline-block;
  margin: 8px 0px;
}

.toolbarField.pageNumber {
  -moz-appearance: textfield; /* hides the spinner in moz */
  min-width: 16px;
  text-align: right;
  width: 40px;
}

.toolbarField.pageNumber.visiblePageIsLoading {
  background-image: url(images/loading-small.png);
  background-repeat: no-repeat;
  background-position: 1px;
}

.toolbarField:hover {
  background-color: hsla(0,0%,100%,.11);
  border-color: hsla(0,0%,0%,.4) hsla(0,0%,0%,.43) hsla(0,0%,0%,.45);
}

.toolbarField:focus {
  background-color: hsla(0,0%,100%,.15);
  border-color: hsla(204,100%,65%,.8) hsla(204,100%,65%,.85) hsla(204,100%,65%,.9);
}

.toolbarLabel {
  min-width: 16px;
  padding: 3px 6px 3px 2px;
  margin: 4px 2px 4px 0;
  border: 1px solid transparent;
  border-radius: 2px;
  color: hsl(0,0%,85%);
  font-size: 12px;
  line-height: 14px;
  text-align: left;
  -moz-user-select: none;
  cursor: default;
}

#thumbnailView {
  position: absolute;
  width: calc(100% - 60px);
  top: 0;
  bottom: 0;
  padding: 10px 30px 0;
  overflow: auto;
}

.thumbnail {
  margin: 0 10px 5px 10px;
}
html[dir='ltr'] .thumbnail {
  float: left;
}
html[dir='rtl'] .thumbnail {
  float: right;
}

#thumbnailView > a:last-of-type > .thumbnail {
  margin-bottom: 10px;
}

#thumbnailView > a:last-of-type > .thumbnail:not([data-loaded]) {
  margin-bottom: 9px;
}

.thumbnail:not([data-loaded]) {
  border: 1px dashed rgba(255, 255, 255, 0.5);
  margin: -1px 9px 4px 9px;
}

.thumbnailImage {
  border: 1px solid transparent;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.5), 0 2px 8px rgba(0, 0, 0, 0.3);
  opacity: 0.8;
  z-index: 99;
  background-color: white;
  background-clip: content-box;
}

.thumbnailSelectionRing {
  border-radius: 2px;
  padding: 7px;
}

a:focus > .thumbnail > .thumbnailSelectionRing > .thumbnailImage,
.thumbnail:hover > .thumbnailSelectionRing > .thumbnailImage {
  opacity: .9;
}

a:focus > .thumbnail > .thumbnailSelectionRing,
.thumbnail:hover > .thumbnailSelectionRing {
  background-color: hsla(0,0%,100%,.15);
  background-image: linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0));
  background-clip: padding-box;
  box-shadow: 0 1px 0 hsla(0,0%,100%,.05) inset,
              0 0 1px hsla(0,0%,100%,.2) inset,
              0 0 1px hsla(0,0%,0%,.2);
  color: hsla(0,0%,100%,.9);
}

.thumbnail.selected > .thumbnailSelectionRing > .thumbnailImage {
  box-shadow: 0 0 0 1px hsla(0,0%,0%,.5);
  opacity: 1;
}

.thumbnail.selected > .thumbnailSelectionRing {
  background-color: hsla(0,0%,100%,.3);
  background-image: linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0));
  background-clip: padding-box;
  box-shadow: 0 1px 0 hsla(0,0%,100%,.05) inset,
              0 0 1px hsla(0,0%,100%,.1) inset,
              0 0 1px hsla(0,0%,0%,.2);
  color: hsla(0,0%,100%,1);
}

#outlineView,
#attachmentsView {
  position: absolute;
  width: calc(100% - 8px);
  top: 0;
  bottom: 0;
  overflow: auto;
  -moz-user-select: none;
}

#outlineView {
  padding: 4px 4px 0;
}
#attachmentsView {
  padding: 3px 4px 0;
}

html[dir='ltr'] .outlineWithDeepNesting > .outlineItem,
html[dir='ltr'] .outlineItem > .outlineItems {
  margin-left: 20px;
}

html[dir='rtl'] .outlineWithDeepNesting > .outlineItem,
html[dir='rtl'] .outlineItem > .outlineItems {
  margin-right: 20px;
}

.outlineItem > a,
.attachmentsItem > button {
  text-decoration: none;
  display: inline-block;
  min-width: 95%;
  min-width: calc(100% - 4px); /* Subtract the right padding (left, in RTL mode)
                                  of the container. */
  height: auto;
  margin-bottom: 1px;
  border-radius: 2px;
  color: hsla(0,0%,100%,.8);
  font-size: 13px;
  line-height: 15px;
  -moz-user-select: none;
  white-space: normal;
}

.attachmentsItem > button {
  border: 0 none;
  background: none;
  cursor: pointer;
  width: 100%;
}

html[dir='ltr'] .outlineItem > a {
  padding: 2px 0 5px 4px;
}
html[dir='ltr'] .attachmentsItem > button {
  padding: 2px 0 3px 7px;
  text-align: left;
}

html[dir='rtl'] .outlineItem > a {
  padding: 2px 4px 5px 0;
}
html[dir='rtl'] .attachmentsItem > button {
  padding: 2px 7px 3px 0;
  text-align: right;
}

.outlineItemToggler {
  position: relative;
  height: 0;
  width: 0;
  color: hsla(0,0%,100%,.5);
}
.outlineItemToggler::before {
  content: url(images/treeitem-expanded.png);
  display: inline-block;
  position: absolute;
}
html[dir='ltr'] .outlineItemToggler.outlineItemsHidden::before {
  content: url(images/treeitem-collapsed.png);
}
html[dir='rtl'] .outlineItemToggler.outlineItemsHidden::before {
  content: url(images/treeitem-collapsed-rtl.png);
}
.outlineItemToggler.outlineItemsHidden ~ .outlineItems {
  display: none;
}
html[dir='ltr'] .outlineItemToggler {
  float: left;
}
html[dir='rtl'] .outlineItemToggler {
  float: right;
}
html[dir='ltr'] .outlineItemToggler::before {
  right: 4px;
}
html[dir='rtl'] .outlineItemToggler::before {
  left: 4px;
}

.outlineItemToggler:hover,
.outlineItemToggler:hover + a,
.outlineItemToggler:hover ~ .outlineItems,
.outlineItem > a:hover,
.attachmentsItem > button:hover {
  background-color: hsla(0,0%,100%,.02);
  background-image: linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0));
  background-clip: padding-box;
  box-shadow: 0 1px 0 hsla(0,0%,100%,.05) inset,
              0 0 1px hsla(0,0%,100%,.2) inset,
              0 0 1px hsla(0,0%,0%,.2);
  border-radius: 2px;
  color: hsla(0,0%,100%,.9);
}

.outlineItem.selected {
  background-color: hsla(0,0%,100%,.08);
  background-image: linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0));
  background-clip: padding-box;
  box-shadow: 0 1px 0 hsla(0,0%,100%,.05) inset,
              0 0 1px hsla(0,0%,100%,.1) inset,
              0 0 1px hsla(0,0%,0%,.2);
  color: hsla(0,0%,100%,1);
}

.noResults {
  font-size: 12px;
  color: hsla(0,0%,100%,.8);
  font-style: italic;
  cursor: default;
}

/* TODO: file FF bug to support ::-moz-selection:window-inactive
   so we can override the opaque grey background when the window is inactive;
   see https://bugzilla.mozilla.org/show_bug.cgi?id=706209 */
::selection { background: rgba(0,0,255,0.3); }
::-moz-selection { background: rgba(0,0,255,0.3); }

#errorWrapper {
  background: none repeat scroll 0 0 #FF5555;
  color: white;
  left: 0;
  position: absolute;
  right: 0;
  z-index: 1000;
  padding: 3px;
  font-size: 0.8em;
}
.loadingInProgress #errorWrapper {
  top: 37px;
}

#errorMessageLeft {
  float: left;
}

#errorMessageRight {
  float: right;
}

#errorMoreInfo {
  background-color: #FFFFFF;
  color: black;
  padding: 3px;
  margin: 3px;
  width: 98%;
}

.overlayButton {
  width: auto;
  margin: 3px 4px 2px 4px !important;
  padding: 2px 6px 3px 6px;
}

#overlayContainer {
  display: table;
  position: absolute;
  width: 100%;
  height: 100%;
  background-color: hsla(0,0%,0%,.2);
  z-index: 40000;
}
#overlayContainer > * {
  overflow: auto;
}

#overlayContainer > .container {
  display: table-cell;
  vertical-align: middle;
  text-align: center;
}

#overlayContainer > .container > .dialog {
  display: inline-block;
  padding: 15px;
  border-spacing: 4px;
  color: hsl(0,0%,85%);
  font-size: 12px;
  line-height: 14px;
  background-color: #474747; /* fallback */
  background-image: url(images/texture.png),
                    linear-gradient(hsla(0,0%,32%,.99), hsla(0,0%,27%,.95));
  box-shadow: inset 1px 0 0 hsla(0,0%,100%,.08),
              inset 0 1px 1px hsla(0,0%,0%,.15),
              inset 0 -1px 0 hsla(0,0%,100%,.05),
              0 1px 0 hsla(0,0%,0%,.15),
              0 1px 1px hsla(0,0%,0%,.1);
  border: 1px solid hsla(0,0%,0%,.5);
  border-radius: 4px;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
}

.dialog > .row {
  display: table-row;
}

.dialog > .row > * {
  display: table-cell;
}

.dialog .toolbarField {
  margin: 5px 0;
}

.dialog .separator {
  display: block;
  margin: 4px 0 4px 0;
  height: 1px;
  width: 100%;
  background-color: hsla(0,0%,0%,.5);
  box-shadow: 0 0 0 1px hsla(0,0%,100%,.08);
}

.dialog .buttonRow {
  text-align: center;
  vertical-align: middle;
}

.dialog :link {
  color: white;
}

#passwordOverlay > .dialog {
  text-align: center;
}
#passwordOverlay .toolbarField {
  width: 200px;
}

#documentPropertiesOverlay > .dialog {
  text-align: left;
}
#documentPropertiesOverlay .row > * {
  min-width: 100px;
}
html[dir='ltr'] #documentPropertiesOverlay .row > * {
  text-align: left;
}
html[dir='rtl'] #documentPropertiesOverlay .row > * {
  text-align: right;
}
#documentPropertiesOverlay .row > span {
  width: 125px;
  word-wrap: break-word;
}
#documentPropertiesOverlay .row > p {
  max-width: 225px;
  word-wrap: break-word;
}
#documentPropertiesOverlay .buttonRow {
  margin-top: 10px;
}

.clearBoth {
  clear: both;
}

.fileInput {
  background: white;
  color: black;
  margin-top: 5px;
  visibility: hidden;
  position: fixed;
  right: 0;
  top: 0;
}

#PDFBug {
  background: none repeat scroll 0 0 white;
  border: 1px solid #666666;
  position: fixed;
  top: 32px;
  right: 0;
  bottom: 0;
  font-size: 10px;
  padding: 0;
  width: 300px;
}
#PDFBug .controls {
    background:#EEEEEE;
    border-bottom: 1px solid #666666;
    padding: 3px;
}
#PDFBug .panels {
  bottom: 0;
  left: 0;
  overflow: auto;
  position: absolute;
  right: 0;
  top: 27px;
}
#PDFBug button.active {
  font-weight: bold;
}
.debuggerShowText {
  background: none repeat scroll 0 0 yellow;
  color: blue;
}
.debuggerHideText:hover {
  background: none repeat scroll 0 0 yellow;
}
#PDFBug .stats {
  font-family: courier;
  font-size: 10px;
  white-space: pre;
}
#PDFBug .stats .title {
    font-weight: bold;
}
#PDFBug table {
  font-size: 10px;
}

#viewer.textLayer-visible .textLayer {
  opacity: 1.0;
}

#viewer.textLayer-visible .canvasWrapper {
  background-color: rgb(128,255,128);
}

#viewer.textLayer-visible .canvasWrapper canvas {
  mix-blend-mode: screen;
}

#viewer.textLayer-visible .textLayer > div {
  background-color: rgba(255, 255, 0, 0.1);
  color: black;
  border: solid 1px rgba(255, 0, 0, 0.5);
  box-sizing: border-box;
}

#viewer.textLayer-hover .textLayer > div:hover {
  background-color: white;
  color: black;
}

#viewer.textLayer-shadow .textLayer > div {
  background-color: rgba(255,255,255, .6);
  color: black;
}

.grab-to-pan-grab {
  cursor: url("images/grab.cur"), move !important;
  cursor: grab !important;
}
.grab-to-pan-grab *:not(input):not(textarea):not(button):not(select):not(:link) {
  cursor: inherit !important;
}
.grab-to-pan-grab:active,
.grab-to-pan-grabbing {
  cursor: url("images/grabbing.cur"), move !important;
  cursor: grabbing !important;

  position: fixed;
  background: transparent;
  display: block;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  overflow: hidden;
  z-index: 50000; /* should be higher than anything else in PDF.js! */
}

@page {
  margin: 0;
}

#printContainer {
  display: none;
}

@media screen and (min-resolution: 2dppx) {
  /* Rules for Retina screens */
  .toolbarButton::before {
    transform: scale(0.5);
    top: -5px;
  }

  .secondaryToolbarButton::before {
    transform: scale(0.5);
    top: -4px;
  }

  html[dir='ltr'] .toolbarButton::before,
  html[dir='rtl'] .toolbarButton::before {
    left: -1px;
  }

  html[dir='ltr'] .secondaryToolbarButton::before {
    left: -2px;
  }
  html[dir='rtl'] .secondaryToolbarButton::before {
    left: 186px;
  }

  .toolbarField.pageNumber.visiblePageIsLoading,
  #findInput[data-status="pending"] {
    background-image: url(images/[email protected]);
    background-size: 16px 17px;
  }

  .dropdownToolbarButton {
    background: url(images/[email protected]) no-repeat;
    background-size: 7px 16px;
  }

  html[dir='ltr'] .toolbarButton#sidebarToggle::before {
    content: url(images/[email protected]);
  }
  html[dir='rtl'] .toolbarButton#sidebarToggle::before {
    content: url(images/[email protected]);
  }

  html[dir='ltr'] .toolbarButton#secondaryToolbarToggle::before {
    content: url(images/[email protected]);
  }
  html[dir='rtl'] .toolbarButton#secondaryToolbarToggle::before {
    content: url(images/[email protected]);
  }

  html[dir='ltr'] .toolbarButton.findPrevious::before {
    content: url(images/[email protected]);
  }
  html[dir='rtl'] .toolbarButton.findPrevious::before {
    content: url(images/[email protected]);
  }

  html[dir='ltr'] .toolbarButton.findNext::before {
    content: url(images/[email protected]);
  }
  html[dir='rtl'] .toolbarButton.findNext::before {
    content: url(images/[email protected]);
  }

  html[dir='ltr'] .toolbarButton.pageUp::before {
    content: url(images/[email protected]);
  }
  html[dir='rtl'] .toolbarButton.pageUp::before {
    content: url(images/[email protected]);
  }

  html[dir='ltr'] .toolbarButton.pageDown::before {
    content: url(images/[email protected]);
  }
  html[dir='rtl'] .toolbarButton.pageDown::before {
    content: url(images/[email protected]);
  }

  .toolbarButton.zoomIn::before {
    content: url(images/[email protected]);
  }

  .toolbarButton.zoomOut::before {
    content: url(images/[email protected]);
  }

  .toolbarButton.presentationMode::before,
  .secondaryToolbarButton.presentationMode::before {
    content: url(images/[email protected]);
  }

  .toolbarButton.print::before,
  .secondaryToolbarButton.print::before {
    content: url(images/[email protected]);
  }

  .toolbarButton.openFile::before,
  .secondaryToolbarButton.openFile::before {
    content: url(images/[email protected]);
  }

  .toolbarButton.download::before,
  .secondaryToolbarButton.download::before {
    content: url(images/[email protected]);
  }

  .toolbarButton.bookmark::before,
  .secondaryToolbarButton.bookmark::before {
    content: url(images/[email protected]);
  }

  #viewThumbnail.toolbarButton::before {
    content: url(images/[email protected]);
  }

  html[dir="ltr"] #viewOutline.toolbarButton::before {
    content: url(images/[email protected]);
  }
  html[dir="rtl"] #viewOutline.toolbarButton::before {
    content: url(images/[email protected]);
  }

  #viewAttachments.toolbarButton::before {
    content: url(images/[email protected]);
  }

  #viewFind.toolbarButton::before {
    content: url(images/[email protected]);
  }

  .secondaryToolbarButton.firstPage::before {
    content: url(images/[email protected]);
  }

  .secondaryToolbarButton.lastPage::before {
    content: url(images/[email protected]);
  }

  .secondaryToolbarButton.rotateCcw::before {
    content: url(images/[email protected]);
  }

  .secondaryToolbarButton.rotateCw::before {
    content: url(images/[email protected]);
  }

  .secondaryToolbarButton.selectTool::before {
    content: url(images/[email protected]);
  }

  .secondaryToolbarButton.handTool::before {
    content: url(images/[email protected]);
  }

  .secondaryToolbarButton.documentProperties::before {
    content: url(images/[email protected]);
  }

  .outlineItemToggler::before {
    transform: scale(0.5);
    top: -1px;
    content: url(images/[email protected]);
  }
  html[dir='ltr'] .outlineItemToggler.outlineItemsHidden::before {
    content: url(images/[email protected]);
  }
  html[dir='rtl'] .outlineItemToggler.outlineItemsHidden::before {
    content: url(images/[email protected]);
  }
  html[dir='ltr'] .outlineItemToggler::before {
    right: 0;
  }
  html[dir='rtl'] .outlineItemToggler::before {
    left: 0;
  }
}

@media print {
  /* General rules for printing. */
  body {
    background: transparent none;
  }

  /* Rules for browsers that don't support mozPrintCallback. */
  #sidebarContainer, #secondaryToolbar, .toolbar, #loadingBox, #errorWrapper, .textLayer {
    display: none;
  }
  #viewerContainer {
    overflow: visible;
  }

  #mainContainer, #viewerContainer, .page, .page canvas {
    position: static;
    padding: 0;
    margin: 0;
  }

  .page {
    float: left;
    display: none;
    border: none;
    box-shadow: none;
    background-clip: content-box;
    background-color: white;
  }

  .page[data-loaded] {
    display: block;
  }

  .fileInput {
    display: none;
  }

  /* Rules for browsers that support PDF.js printing */
  body[data-pdfjsprinting] #outerContainer {
    display: none;
  }
  body[data-pdfjsprinting] #printContainer {
    display: block;
  }
  #printContainer {
    height: 100%;
  }
  /* wrapper around (scaled) print canvas elements */
  #printContainer > div {
    position: relative;
    top: 0;
    left: 0;
    width: 1px;
    height: 1px;
    overflow: visible;
    page-break-after: always;
    page-break-inside: avoid;
  }
  #printContainer canvas,
  #printContainer img {
    display: block;
  }
}

.visibleLargeView,
.visibleMediumView,
.visibleSmallView {
  display: none;
}

@media all and (max-width: 900px) {
  #toolbarViewerMiddle {
    display: table;
    margin: auto;
    left: auto;
    position: inherit;
    transform: none;
  }
}

@media all and (max-width: 840px) {
  #sidebarContent {
    background-color: hsla(0,0%,0%,.7);
  }

  html[dir='ltr'] #outerContainer.sidebarOpen #viewerContainer {
    left: 0px;
  }
  html[dir='rtl'] #outerContainer.sidebarOpen #viewerContainer {
    right: 0px;
  }

  #outerContainer .hiddenLargeView,
  #outerContainer .hiddenMediumView {
    display: inherit;
  }
  #outerContainer .visibleLargeView,
  #outerContainer .visibleMediumView {
    display: none;
  }
}

@media all and (max-width: 770px) {
  #outerContainer .hiddenLargeView {
    display: none;
  }
  #outerContainer .visibleLargeView {
    display: inherit;
  }
}

@media all and (max-width: 700px) {
  #outerContainer .hiddenMediumView {
    display: none;
  }
  #outerContainer .visibleMediumView {
    display: inherit;
  }
}

@media all and (max-width: 640px) {
  .hiddenSmallView, .hiddenSmallView * {
    display: none;
  }
  .visibleSmallView {
    display: inherit;
  }
  .toolbarButtonSpacer {
    width: 0;
  }
  html[dir='ltr'] .findbar {
    left: 38px;
  }
  html[dir='rtl'] .findbar {
    right: 38px;
  }
}

@media all and (max-width: 535px) {
  #scaleSelectContainer {
    display: none;
  }
}
PK
!<���&�G�G$chrome/pdfjs/content/web/viewer.html<!DOCTYPE html>
<!--
Copyright 2012 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.

Adobe CMap resources are covered by their own copyright but the same license:

    Copyright 1990-2015 Adobe Systems Incorporated.

See https://github.com/adobe-type-tools/cmap-resources
-->
<html dir="ltr" mozdisallowselectionprint>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <title>PDF.js viewer</title>

<!-- This snippet is used in the Firefox extension (included from viewer.html) -->
<base href="resource://pdf.js/web/">
<script src="../build/pdf.js"></script>


    <link rel="stylesheet" href="viewer.css">



    <script src="viewer.js"></script>

  </head>

  <body tabindex="1" class="loadingInProgress">
    <div id="outerContainer">

      <div id="sidebarContainer">
        <div id="toolbarSidebar">
          <div class="splitToolbarButton toggled">
            <button id="viewThumbnail" class="toolbarButton toggled" title="Show Thumbnails" tabindex="2" data-l10n-id="thumbs">
               <span data-l10n-id="thumbs_label">Thumbnails</span>
            </button>
            <button id="viewOutline" class="toolbarButton" title="Show Document Outline (double-click to expand/collapse all items)" tabindex="3" data-l10n-id="document_outline">
               <span data-l10n-id="document_outline_label">Document Outline</span>
            </button>
            <button id="viewAttachments" class="toolbarButton" title="Show Attachments" tabindex="4" data-l10n-id="attachments">
               <span data-l10n-id="attachments_label">Attachments</span>
            </button>
          </div>
        </div>
        <div id="sidebarContent">
          <div id="thumbnailView">
          </div>
          <div id="outlineView" class="hidden">
          </div>
          <div id="attachmentsView" class="hidden">
          </div>
        </div>
        <div id="sidebarResizer" class="hidden"></div>
      </div>  <!-- sidebarContainer -->

      <div id="mainContainer">
        <div class="findbar hidden doorHanger" id="findbar">
          <div id="findbarInputContainer">
            <input id="findInput" class="toolbarField" title="Find" placeholder="Find in document…" tabindex="91" data-l10n-id="find_input">
            <div class="splitToolbarButton">
              <button id="findPrevious" class="toolbarButton findPrevious" title="Find the previous occurrence of the phrase" tabindex="92" data-l10n-id="find_previous">
                <span data-l10n-id="find_previous_label">Previous</span>
              </button>
              <div class="splitToolbarButtonSeparator"></div>
              <button id="findNext" class="toolbarButton findNext" title="Find the next occurrence of the phrase" tabindex="93" data-l10n-id="find_next">
                <span data-l10n-id="find_next_label">Next</span>
              </button>
            </div>
          </div>

          <div id="findbarOptionsContainer">
            <input type="checkbox" id="findHighlightAll" class="toolbarField" tabindex="94">
            <label for="findHighlightAll" class="toolbarLabel" data-l10n-id="find_highlight">Highlight all</label>
            <input type="checkbox" id="findMatchCase" class="toolbarField" tabindex="95">
            <label for="findMatchCase" class="toolbarLabel" data-l10n-id="find_match_case_label">Match case</label>
            <span id="findResultsCount" class="toolbarLabel hidden"></span>
          </div>

          <div id="findbarMessageContainer">
            <span id="findMsg" class="toolbarLabel"></span>
          </div>
        </div>  <!-- findbar -->

        <div id="secondaryToolbar" class="secondaryToolbar hidden doorHangerRight">
          <div id="secondaryToolbarButtonContainer">
            <button id="secondaryPresentationMode" class="secondaryToolbarButton presentationMode visibleLargeView" title="Switch to Presentation Mode" tabindex="51" data-l10n-id="presentation_mode">
              <span data-l10n-id="presentation_mode_label">Presentation Mode</span>
            </button>

            <button id="secondaryOpenFile" class="secondaryToolbarButton openFile visibleLargeView" title="Open File" tabindex="52" data-l10n-id="open_file">
              <span data-l10n-id="open_file_label">Open</span>
            </button>

            <button id="secondaryPrint" class="secondaryToolbarButton print visibleMediumView" title="Print" tabindex="53" data-l10n-id="print">
              <span data-l10n-id="print_label">Print</span>
            </button>

            <button id="secondaryDownload" class="secondaryToolbarButton download visibleMediumView" title="Download" tabindex="54" data-l10n-id="download">
              <span data-l10n-id="download_label">Download</span>
            </button>

            <a href="#" id="secondaryViewBookmark" class="secondaryToolbarButton bookmark visibleSmallView" title="Current view (copy or open in new window)" tabindex="55" data-l10n-id="bookmark">
              <span data-l10n-id="bookmark_label">Current View</span>
            </a>

            <div class="horizontalToolbarSeparator visibleLargeView"></div>

            <button id="firstPage" class="secondaryToolbarButton firstPage" title="Go to First Page" tabindex="56" data-l10n-id="first_page">
              <span data-l10n-id="first_page_label">Go to First Page</span>
            </button>
            <button id="lastPage" class="secondaryToolbarButton lastPage" title="Go to Last Page" tabindex="57" data-l10n-id="last_page">
              <span data-l10n-id="last_page_label">Go to Last Page</span>
            </button>

            <div class="horizontalToolbarSeparator"></div>

            <button id="pageRotateCw" class="secondaryToolbarButton rotateCw" title="Rotate Clockwise" tabindex="58" data-l10n-id="page_rotate_cw">
              <span data-l10n-id="page_rotate_cw_label">Rotate Clockwise</span>
            </button>
            <button id="pageRotateCcw" class="secondaryToolbarButton rotateCcw" title="Rotate Counterclockwise" tabindex="59" data-l10n-id="page_rotate_ccw">
              <span data-l10n-id="page_rotate_ccw_label">Rotate Counterclockwise</span>
            </button>

            <div class="horizontalToolbarSeparator"></div>

            <button id="cursorSelectTool" class="secondaryToolbarButton selectTool toggled" title="Enable Text Selection Tool" tabindex="60" data-l10n-id="cursor_text_select_tool">
              <span data-l10n-id="cursor_text_select_tool_label">Text Selection Tool</span>
            </button>
            <button id="cursorHandTool" class="secondaryToolbarButton handTool" title="Enable Hand Tool" tabindex="61" data-l10n-id="cursor_hand_tool">
              <span data-l10n-id="cursor_hand_tool_label">Hand Tool</span>
            </button>

            <div class="horizontalToolbarSeparator"></div>

            <button id="documentProperties" class="secondaryToolbarButton documentProperties" title="Document Properties…" tabindex="62" data-l10n-id="document_properties">
              <span data-l10n-id="document_properties_label">Document Properties…</span>
            </button>
          </div>
        </div>  <!-- secondaryToolbar -->

        <div class="toolbar">
          <div id="toolbarContainer">
            <div id="toolbarViewer">
              <div id="toolbarViewerLeft">
                <button id="sidebarToggle" class="toolbarButton" title="Toggle Sidebar" tabindex="11" data-l10n-id="toggle_sidebar">
                  <span data-l10n-id="toggle_sidebar_label">Toggle Sidebar</span>
                </button>
                <div class="toolbarButtonSpacer"></div>
                <button id="viewFind" class="toolbarButton" title="Find in Document" tabindex="12" data-l10n-id="findbar">
                  <span data-l10n-id="findbar_label">Find</span>
                </button>
                <div class="splitToolbarButton hiddenSmallView">
                  <button class="toolbarButton pageUp" title="Previous Page" id="previous" tabindex="13" data-l10n-id="previous">
                    <span data-l10n-id="previous_label">Previous</span>
                  </button>
                  <div class="splitToolbarButtonSeparator"></div>
                  <button class="toolbarButton pageDown" title="Next Page" id="next" tabindex="14" data-l10n-id="next">
                    <span data-l10n-id="next_label">Next</span>
                  </button>
                </div>
                <input type="number" id="pageNumber" class="toolbarField pageNumber" title="Page" value="1" size="4" min="1" tabindex="15" data-l10n-id="page">
                <span id="numPages" class="toolbarLabel"></span>
              </div>
              <div id="toolbarViewerRight">
                <button id="presentationMode" class="toolbarButton presentationMode hiddenLargeView" title="Switch to Presentation Mode" tabindex="31" data-l10n-id="presentation_mode">
                  <span data-l10n-id="presentation_mode_label">Presentation Mode</span>
                </button>

                <button id="openFile" class="toolbarButton openFile hiddenLargeView" title="Open File" tabindex="32" data-l10n-id="open_file">
                  <span data-l10n-id="open_file_label">Open</span>
                </button>

                <button id="print" class="toolbarButton print hiddenMediumView" title="Print" tabindex="33" data-l10n-id="print">
                  <span data-l10n-id="print_label">Print</span>
                </button>

                <button id="download" class="toolbarButton download hiddenMediumView" title="Download" tabindex="34" data-l10n-id="download">
                  <span data-l10n-id="download_label">Download</span>
                </button>
                <a href="#" id="viewBookmark" class="toolbarButton bookmark hiddenSmallView" title="Current view (copy or open in new window)" tabindex="35" data-l10n-id="bookmark">
                  <span data-l10n-id="bookmark_label">Current View</span>
                </a>

                <div class="verticalToolbarSeparator hiddenSmallView"></div>

                <button id="secondaryToolbarToggle" class="toolbarButton" title="Tools" tabindex="36" data-l10n-id="tools">
                  <span data-l10n-id="tools_label">Tools</span>
                </button>
              </div>
              <div id="toolbarViewerMiddle">
                <div class="splitToolbarButton">
                  <button id="zoomOut" class="toolbarButton zoomOut" title="Zoom Out" tabindex="21" data-l10n-id="zoom_out">
                    <span data-l10n-id="zoom_out_label">Zoom Out</span>
                  </button>
                  <div class="splitToolbarButtonSeparator"></div>
                  <button id="zoomIn" class="toolbarButton zoomIn" title="Zoom In" tabindex="22" data-l10n-id="zoom_in">
                    <span data-l10n-id="zoom_in_label">Zoom In</span>
                   </button>
                </div>
                <span id="scaleSelectContainer" class="dropdownToolbarButton">
                  <select id="scaleSelect" title="Zoom" tabindex="23" data-l10n-id="zoom">
                    <option id="pageAutoOption" title="" value="auto" selected="selected" data-l10n-id="page_scale_auto">Automatic Zoom</option>
                    <option id="pageActualOption" title="" value="page-actual" data-l10n-id="page_scale_actual">Actual Size</option>
                    <option id="pageFitOption" title="" value="page-fit" data-l10n-id="page_scale_fit">Page Fit</option>
                    <option id="pageWidthOption" title="" value="page-width" data-l10n-id="page_scale_width">Page Width</option>
                    <option id="customScaleOption" title="" value="custom" disabled="disabled" hidden="true"></option>
                    <option title="" value="0.5" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 50 }'>50%</option>
                    <option title="" value="0.75" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 75 }'>75%</option>
                    <option title="" value="1" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 100 }'>100%</option>
                    <option title="" value="1.25" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 125 }'>125%</option>
                    <option title="" value="1.5" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 150 }'>150%</option>
                    <option title="" value="2" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 200 }'>200%</option>
                    <option title="" value="3" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 300 }'>300%</option>
                    <option title="" value="4" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 400 }'>400%</option>
                  </select>
                </span>
              </div>
            </div>
            <div id="loadingBar">
              <div class="progress">
                <div class="glimmer">
                </div>
              </div>
            </div>
          </div>
        </div>

        <menu type="context" id="viewerContextMenu">
          <menuitem id="contextFirstPage" label="First Page"
                    data-l10n-id="first_page"></menuitem>
          <menuitem id="contextLastPage" label="Last Page"
                    data-l10n-id="last_page"></menuitem>
          <menuitem id="contextPageRotateCw" label="Rotate Clockwise"
                    data-l10n-id="page_rotate_cw"></menuitem>
          <menuitem id="contextPageRotateCcw" label="Rotate Counter-Clockwise"
                    data-l10n-id="page_rotate_ccw"></menuitem>
        </menu>

        <div id="viewerContainer" tabindex="0">
          <div id="viewer" class="pdfViewer"></div>
        </div>

        <div id="errorWrapper" hidden='true'>
          <div id="errorMessageLeft">
            <span id="errorMessage"></span>
            <button id="errorShowMore" data-l10n-id="error_more_info">
              More Information
            </button>
            <button id="errorShowLess" data-l10n-id="error_less_info" hidden='true'>
              Less Information
            </button>
          </div>
          <div id="errorMessageRight">
            <button id="errorClose" data-l10n-id="error_close">
              Close
            </button>
          </div>
          <div class="clearBoth"></div>
          <textarea id="errorMoreInfo" hidden='true' readonly="readonly"></textarea>
        </div>
      </div> <!-- mainContainer -->

      <div id="overlayContainer" class="hidden">
        <div id="passwordOverlay" class="container hidden">
          <div class="dialog">
            <div class="row">
              <p id="passwordText" data-l10n-id="password_label">Enter the password to open this PDF file:</p>
            </div>
            <div class="row">
              <input type="password" id="password" class="toolbarField">
            </div>
            <div class="buttonRow">
              <button id="passwordCancel" class="overlayButton"><span data-l10n-id="password_cancel">Cancel</span></button>
              <button id="passwordSubmit" class="overlayButton"><span data-l10n-id="password_ok">OK</span></button>
            </div>
          </div>
        </div>
        <div id="documentPropertiesOverlay" class="container hidden">
          <div class="dialog">
            <div class="row">
              <span data-l10n-id="document_properties_file_name">File name:</span> <p id="fileNameField">-</p>
            </div>
            <div class="row">
              <span data-l10n-id="document_properties_file_size">File size:</span> <p id="fileSizeField">-</p>
            </div>
            <div class="separator"></div>
            <div class="row">
              <span data-l10n-id="document_properties_title">Title:</span> <p id="titleField">-</p>
            </div>
            <div class="row">
              <span data-l10n-id="document_properties_author">Author:</span> <p id="authorField">-</p>
            </div>
            <div class="row">
              <span data-l10n-id="document_properties_subject">Subject:</span> <p id="subjectField">-</p>
            </div>
            <div class="row">
              <span data-l10n-id="document_properties_keywords">Keywords:</span> <p id="keywordsField">-</p>
            </div>
            <div class="row">
              <span data-l10n-id="document_properties_creation_date">Creation Date:</span> <p id="creationDateField">-</p>
            </div>
            <div class="row">
              <span data-l10n-id="document_properties_modification_date">Modification Date:</span> <p id="modificationDateField">-</p>
            </div>
            <div class="row">
              <span data-l10n-id="document_properties_creator">Creator:</span> <p id="creatorField">-</p>
            </div>
            <div class="separator"></div>
            <div class="row">
              <span data-l10n-id="document_properties_producer">PDF Producer:</span> <p id="producerField">-</p>
            </div>
            <div class="row">
              <span data-l10n-id="document_properties_version">PDF Version:</span> <p id="versionField">-</p>
            </div>
            <div class="row">
              <span data-l10n-id="document_properties_page_count">Page Count:</span> <p id="pageCountField">-</p>
            </div>
            <div class="buttonRow">
              <button id="documentPropertiesClose" class="overlayButton"><span data-l10n-id="document_properties_close">Close</span></button>
            </div>
          </div>
        </div>
      </div>  <!-- overlayContainer -->

    </div> <!-- outerContainer -->
    <div id="printContainer"></div>
  </body>
</html>

PK
!<z��77"chrome/pdfjs/content/web/viewer.js/**
 * @licstart The following is the entire license notice for the
 * Javascript code in this page
 *
 * Copyright 2017 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.
 *
 * @licend The above is the entire license notice for the
 * Javascript code in this page
 */

/******/ (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, {
/******/ 				configurable: false,
/******/ 				enumerable: true,
/******/ 				get: getter
/******/ 			});
/******/ 		}
/******/ 	};
/******/
/******/ 	// 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 = 9);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.waitOnEventOrTimeout = exports.WaitOnType = exports.localized = exports.animationStarted = exports.normalizeWheelEventDelta = exports.binarySearchFirstItem = exports.watchScroll = exports.scrollIntoView = exports.getOutputScale = exports.approximateFraction = exports.roundToDivide = exports.getVisibleElements = exports.parseQueryString = exports.noContextMenuHandler = exports.getPDFFileNameFromURL = exports.ProgressBar = exports.EventBus = exports.NullL10n = exports.mozL10n = exports.TextLayerMode = exports.RendererType = exports.PresentationModeState = exports.cloneObj = exports.isFileSchema = exports.isValidRotation = exports.VERTICAL_PADDING = exports.SCROLLBAR_PADDING = exports.MAX_AUTO_SCALE = exports.UNKNOWN_SCALE = exports.MAX_SCALE = exports.MIN_SCALE = exports.DEFAULT_SCALE = exports.DEFAULT_SCALE_VALUE = exports.CSS_UNITS = undefined;

var _pdfjsLib = __webpack_require__(1);

const CSS_UNITS = 96.0 / 72.0;
const DEFAULT_SCALE_VALUE = 'auto';
const DEFAULT_SCALE = 1.0;
const MIN_SCALE = 0.10;
const MAX_SCALE = 10.0;
const UNKNOWN_SCALE = 0;
const MAX_AUTO_SCALE = 1.25;
const SCROLLBAR_PADDING = 40;
const VERTICAL_PADDING = 5;
const PresentationModeState = {
  UNKNOWN: 0,
  NORMAL: 1,
  CHANGING: 2,
  FULLSCREEN: 3
};
const RendererType = {
  CANVAS: 'canvas',
  SVG: 'svg'
};
const TextLayerMode = {
  DISABLE: 0,
  ENABLE: 1,
  ENABLE_ENHANCE: 2
};
function formatL10nValue(text, args) {
  if (!args) {
    return text;
  }
  return text.replace(/\{\{\s*(\w+)\s*\}\}/g, (all, name) => {
    return name in args ? args[name] : '{{' + name + '}}';
  });
}
let NullL10n = {
  getDirection() {
    return Promise.resolve('ltr');
  },
  get(property, args, fallback) {
    return Promise.resolve(formatL10nValue(fallback, args));
  },
  translate(element) {
    return Promise.resolve();
  }
};
_pdfjsLib.PDFJS.useOnlyCssZoom = _pdfjsLib.PDFJS.useOnlyCssZoom === undefined ? false : _pdfjsLib.PDFJS.useOnlyCssZoom;
_pdfjsLib.PDFJS.maxCanvasPixels = _pdfjsLib.PDFJS.maxCanvasPixels === undefined ? 16777216 : _pdfjsLib.PDFJS.maxCanvasPixels;
_pdfjsLib.PDFJS.disableHistory = _pdfjsLib.PDFJS.disableHistory === undefined ? false : _pdfjsLib.PDFJS.disableHistory;
;
function getOutputScale(ctx) {
  let devicePixelRatio = window.devicePixelRatio || 1;
  let backingStoreRatio = ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1;
  let pixelRatio = devicePixelRatio / backingStoreRatio;
  return {
    sx: pixelRatio,
    sy: pixelRatio,
    scaled: pixelRatio !== 1
  };
}
function scrollIntoView(element, spot, skipOverflowHiddenElements = false) {
  let parent = element.offsetParent;
  if (!parent) {
    console.error('offsetParent is not set -- cannot scroll');
    return;
  }
  let offsetY = element.offsetTop + element.clientTop;
  let offsetX = element.offsetLeft + element.clientLeft;
  while (parent.clientHeight === parent.scrollHeight || skipOverflowHiddenElements && getComputedStyle(parent).overflow === 'hidden') {
    if (parent.dataset._scaleY) {
      offsetY /= parent.dataset._scaleY;
      offsetX /= parent.dataset._scaleX;
    }
    offsetY += parent.offsetTop;
    offsetX += parent.offsetLeft;
    parent = parent.offsetParent;
    if (!parent) {
      return;
    }
  }
  if (spot) {
    if (spot.top !== undefined) {
      offsetY += spot.top;
    }
    if (spot.left !== undefined) {
      offsetX += spot.left;
      parent.scrollLeft = offsetX;
    }
  }
  parent.scrollTop = offsetY;
}
function watchScroll(viewAreaElement, callback) {
  let debounceScroll = function (evt) {
    if (rAF) {
      return;
    }
    rAF = window.requestAnimationFrame(function viewAreaElementScrolled() {
      rAF = null;
      let currentY = viewAreaElement.scrollTop;
      let lastY = state.lastY;
      if (currentY !== lastY) {
        state.down = currentY > lastY;
      }
      state.lastY = currentY;
      callback(state);
    });
  };
  let state = {
    down: true,
    lastY: viewAreaElement.scrollTop,
    _eventHandler: debounceScroll
  };
  let rAF = null;
  viewAreaElement.addEventListener('scroll', debounceScroll, true);
  return state;
}
function parseQueryString(query) {
  let parts = query.split('&');
  let params = Object.create(null);
  for (let i = 0, ii = parts.length; i < ii; ++i) {
    let param = parts[i].split('=');
    let key = param[0].toLowerCase();
    let value = param.length > 1 ? param[1] : null;
    params[decodeURIComponent(key)] = decodeURIComponent(value);
  }
  return params;
}
function binarySearchFirstItem(items, condition) {
  let minIndex = 0;
  let maxIndex = items.length - 1;
  if (items.length === 0 || !condition(items[maxIndex])) {
    return items.length;
  }
  if (condition(items[minIndex])) {
    return minIndex;
  }
  while (minIndex < maxIndex) {
    let currentIndex = minIndex + maxIndex >> 1;
    let currentItem = items[currentIndex];
    if (condition(currentItem)) {
      maxIndex = currentIndex;
    } else {
      minIndex = currentIndex + 1;
    }
  }
  return minIndex;
}
function approximateFraction(x) {
  if (Math.floor(x) === x) {
    return [x, 1];
  }
  let xinv = 1 / x;
  let limit = 8;
  if (xinv > limit) {
    return [1, limit];
  } else if (Math.floor(xinv) === xinv) {
    return [1, xinv];
  }
  let x_ = x > 1 ? xinv : x;
  let a = 0,
      b = 1,
      c = 1,
      d = 1;
  while (true) {
    let p = a + c,
        q = b + d;
    if (q > limit) {
      break;
    }
    if (x_ <= p / q) {
      c = p;
      d = q;
    } else {
      a = p;
      b = q;
    }
  }
  let result;
  if (x_ - a / b < c / d - x_) {
    result = x_ === x ? [a, b] : [b, a];
  } else {
    result = x_ === x ? [c, d] : [d, c];
  }
  return result;
}
function roundToDivide(x, div) {
  let r = x % div;
  return r === 0 ? x : Math.round(x - r + div);
}
function getVisibleElements(scrollEl, views, sortByVisibility = false) {
  let top = scrollEl.scrollTop,
      bottom = top + scrollEl.clientHeight;
  let left = scrollEl.scrollLeft,
      right = left + scrollEl.clientWidth;
  function isElementBottomBelowViewTop(view) {
    let element = view.div;
    let elementBottom = element.offsetTop + element.clientTop + element.clientHeight;
    return elementBottom > top;
  }
  let visible = [],
      view,
      element;
  let currentHeight, viewHeight, hiddenHeight, percentHeight;
  let currentWidth, viewWidth;
  let firstVisibleElementInd = views.length === 0 ? 0 : binarySearchFirstItem(views, isElementBottomBelowViewTop);
  for (let i = firstVisibleElementInd, ii = views.length; i < ii; i++) {
    view = views[i];
    element = view.div;
    currentHeight = element.offsetTop + element.clientTop;
    viewHeight = element.clientHeight;
    if (currentHeight > bottom) {
      break;
    }
    currentWidth = element.offsetLeft + element.clientLeft;
    viewWidth = element.clientWidth;
    if (currentWidth + viewWidth < left || currentWidth > right) {
      continue;
    }
    hiddenHeight = Math.max(0, top - currentHeight) + Math.max(0, currentHeight + viewHeight - bottom);
    percentHeight = (viewHeight - hiddenHeight) * 100 / viewHeight | 0;
    visible.push({
      id: view.id,
      x: currentWidth,
      y: currentHeight,
      view,
      percent: percentHeight
    });
  }
  let first = visible[0];
  let last = visible[visible.length - 1];
  if (sortByVisibility) {
    visible.sort(function (a, b) {
      let pc = a.percent - b.percent;
      if (Math.abs(pc) > 0.001) {
        return -pc;
      }
      return a.id - b.id;
    });
  }
  return {
    first,
    last,
    views: visible
  };
}
function noContextMenuHandler(evt) {
  evt.preventDefault();
}
function isFileSchema(url) {
  let i = 0,
      ii = url.length;
  while (i < ii && url[i].trim() === '') {
    i++;
  }
  return url.substr(i, 7).toLowerCase() === 'file://';
}
function isDataSchema(url) {
  let i = 0,
      ii = url.length;
  while (i < ii && url[i].trim() === '') {
    i++;
  }
  return url.substr(i, 5).toLowerCase() === 'data:';
}
function getPDFFileNameFromURL(url, defaultFilename = 'document.pdf') {
  if (isDataSchema(url)) {
    console.warn('getPDFFileNameFromURL: ' + 'ignoring "data:" URL for performance reasons.');
    return defaultFilename;
  }
  const reURI = /^(?:(?:[^:]+:)?\/\/[^\/]+)?([^?#]*)(\?[^#]*)?(#.*)?$/;
  const reFilename = /[^\/?#=]+\.pdf\b(?!.*\.pdf\b)/i;
  let splitURI = reURI.exec(url);
  let suggestedFilename = reFilename.exec(splitURI[1]) || reFilename.exec(splitURI[2]) || reFilename.exec(splitURI[3]);
  if (suggestedFilename) {
    suggestedFilename = suggestedFilename[0];
    if (suggestedFilename.includes('%')) {
      try {
        suggestedFilename = reFilename.exec(decodeURIComponent(suggestedFilename))[0];
      } catch (ex) {}
    }
  }
  return suggestedFilename || defaultFilename;
}
function normalizeWheelEventDelta(evt) {
  let delta = Math.sqrt(evt.deltaX * evt.deltaX + evt.deltaY * evt.deltaY);
  let angle = Math.atan2(evt.deltaY, evt.deltaX);
  if (-0.25 * Math.PI < angle && angle < 0.75 * Math.PI) {
    delta = -delta;
  }
  const MOUSE_DOM_DELTA_PIXEL_MODE = 0;
  const MOUSE_DOM_DELTA_LINE_MODE = 1;
  const MOUSE_PIXELS_PER_LINE = 30;
  const MOUSE_LINES_PER_PAGE = 30;
  if (evt.deltaMode === MOUSE_DOM_DELTA_PIXEL_MODE) {
    delta /= MOUSE_PIXELS_PER_LINE * MOUSE_LINES_PER_PAGE;
  } else if (evt.deltaMode === MOUSE_DOM_DELTA_LINE_MODE) {
    delta /= MOUSE_LINES_PER_PAGE;
  }
  return delta;
}
function isValidRotation(angle) {
  return Number.isInteger(angle) && angle % 90 === 0;
}
function cloneObj(obj) {
  let result = Object.create(null);
  for (let i in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, i)) {
      result[i] = obj[i];
    }
  }
  return result;
}
const WaitOnType = {
  EVENT: 'event',
  TIMEOUT: 'timeout'
};
function waitOnEventOrTimeout({ target, name, delay = 0 }) {
  if (typeof target !== 'object' || !(name && typeof name === 'string') || !(Number.isInteger(delay) && delay >= 0)) {
    return Promise.reject(new Error('waitOnEventOrTimeout - invalid paramaters.'));
  }
  let capability = (0, _pdfjsLib.createPromiseCapability)();
  function handler(type) {
    if (target instanceof EventBus) {
      target.off(name, eventHandler);
    } else {
      target.removeEventListener(name, eventHandler);
    }
    if (timeout) {
      clearTimeout(timeout);
    }
    capability.resolve(type);
  }
  let eventHandler = handler.bind(null, WaitOnType.EVENT);
  if (target instanceof EventBus) {
    target.on(name, eventHandler);
  } else {
    target.addEventListener(name, eventHandler);
  }
  let timeoutHandler = handler.bind(null, WaitOnType.TIMEOUT);
  let timeout = setTimeout(timeoutHandler, delay);
  return capability.promise;
}
let animationStarted = new Promise(function (resolve) {
  window.requestAnimationFrame(resolve);
});
let mozL10n;
let localized = Promise.resolve();
class EventBus {
  constructor() {
    this._listeners = Object.create(null);
  }
  on(eventName, listener) {
    let eventListeners = this._listeners[eventName];
    if (!eventListeners) {
      eventListeners = [];
      this._listeners[eventName] = eventListeners;
    }
    eventListeners.push(listener);
  }
  off(eventName, listener) {
    let eventListeners = this._listeners[eventName];
    let i;
    if (!eventListeners || (i = eventListeners.indexOf(listener)) < 0) {
      return;
    }
    eventListeners.splice(i, 1);
  }
  dispatch(eventName) {
    let eventListeners = this._listeners[eventName];
    if (!eventListeners || eventListeners.length === 0) {
      return;
    }
    let args = Array.prototype.slice.call(arguments, 1);
    eventListeners.slice(0).forEach(function (listener) {
      listener.apply(null, args);
    });
  }
}
function clamp(v, min, max) {
  return Math.min(Math.max(v, min), max);
}
class ProgressBar {
  constructor(id, { height, width, units } = {}) {
    this.visible = true;
    this.div = document.querySelector(id + ' .progress');
    this.bar = this.div.parentNode;
    this.height = height || 100;
    this.width = width || 100;
    this.units = units || '%';
    this.div.style.height = this.height + this.units;
    this.percent = 0;
  }
  _updateBar() {
    if (this._indeterminate) {
      this.div.classList.add('indeterminate');
      this.div.style.width = this.width + this.units;
      return;
    }
    this.div.classList.remove('indeterminate');
    let progressSize = this.width * this._percent / 100;
    this.div.style.width = progressSize + this.units;
  }
  get percent() {
    return this._percent;
  }
  set percent(val) {
    this._indeterminate = isNaN(val);
    this._percent = clamp(val, 0, 100);
    this._updateBar();
  }
  setWidth(viewer) {
    if (!viewer) {
      return;
    }
    let container = viewer.parentNode;
    let scrollbarWidth = container.offsetWidth - viewer.offsetWidth;
    if (scrollbarWidth > 0) {
      this.bar.setAttribute('style', 'width: calc(100% - ' + scrollbarWidth + 'px);');
    }
  }
  hide() {
    if (!this.visible) {
      return;
    }
    this.visible = false;
    this.bar.classList.add('hidden');
    document.body.classList.remove('loadingInProgress');
  }
  show() {
    if (this.visible) {
      return;
    }
    this.visible = true;
    document.body.classList.add('loadingInProgress');
    this.bar.classList.remove('hidden');
  }
}
exports.CSS_UNITS = CSS_UNITS;
exports.DEFAULT_SCALE_VALUE = DEFAULT_SCALE_VALUE;
exports.DEFAULT_SCALE = DEFAULT_SCALE;
exports.MIN_SCALE = MIN_SCALE;
exports.MAX_SCALE = MAX_SCALE;
exports.UNKNOWN_SCALE = UNKNOWN_SCALE;
exports.MAX_AUTO_SCALE = MAX_AUTO_SCALE;
exports.SCROLLBAR_PADDING = SCROLLBAR_PADDING;
exports.VERTICAL_PADDING = VERTICAL_PADDING;
exports.isValidRotation = isValidRotation;
exports.isFileSchema = isFileSchema;
exports.cloneObj = cloneObj;
exports.PresentationModeState = PresentationModeState;
exports.RendererType = RendererType;
exports.TextLayerMode = TextLayerMode;
exports.mozL10n = mozL10n;
exports.NullL10n = NullL10n;
exports.EventBus = EventBus;
exports.ProgressBar = ProgressBar;
exports.getPDFFileNameFromURL = getPDFFileNameFromURL;
exports.noContextMenuHandler = noContextMenuHandler;
exports.parseQueryString = parseQueryString;
exports.getVisibleElements = getVisibleElements;
exports.roundToDivide = roundToDivide;
exports.approximateFraction = approximateFraction;
exports.getOutputScale = getOutputScale;
exports.scrollIntoView = scrollIntoView;
exports.watchScroll = watchScroll;
exports.binarySearchFirstItem = binarySearchFirstItem;
exports.normalizeWheelEventDelta = normalizeWheelEventDelta;
exports.animationStarted = animationStarted;
exports.localized = localized;
exports.WaitOnType = WaitOnType;
exports.waitOnEventOrTimeout = waitOnEventOrTimeout;

/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


let pdfjsLib;
if (typeof window !== 'undefined' && window['pdfjs-dist/build/pdf']) {
  pdfjsLib = window['pdfjs-dist/build/pdf'];
} else {
  pdfjsLib = require('../build/pdf.js');
}
module.exports = pdfjsLib;

/***/ }),
/* 2 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getGlobalEventBus = exports.attachDOMEventsToEventBus = undefined;

var _ui_utils = __webpack_require__(0);

function attachDOMEventsToEventBus(eventBus) {
  eventBus.on('documentload', function () {
    let event = document.createEvent('CustomEvent');
    event.initCustomEvent('documentload', true, true, {});
    window.dispatchEvent(event);
  });
  eventBus.on('pagerendered', function (evt) {
    let event = document.createEvent('CustomEvent');
    event.initCustomEvent('pagerendered', true, true, {
      pageNumber: evt.pageNumber,
      cssTransform: evt.cssTransform
    });
    evt.source.div.dispatchEvent(event);
  });
  eventBus.on('textlayerrendered', function (evt) {
    let event = document.createEvent('CustomEvent');
    event.initCustomEvent('textlayerrendered', true, true, { pageNumber: evt.pageNumber });
    evt.source.textLayerDiv.dispatchEvent(event);
  });
  eventBus.on('pagechange', function (evt) {
    let event = document.createEvent('UIEvents');
    event.initUIEvent('pagechange', true, true, window, 0);
    event.pageNumber = evt.pageNumber;
    evt.source.container.dispatchEvent(event);
  });
  eventBus.on('pagesinit', function (evt) {
    let event = document.createEvent('CustomEvent');
    event.initCustomEvent('pagesinit', true, true, null);
    evt.source.container.dispatchEvent(event);
  });
  eventBus.on('pagesloaded', function (evt) {
    let event = document.createEvent('CustomEvent');
    event.initCustomEvent('pagesloaded', true, true, { pagesCount: evt.pagesCount });
    evt.source.container.dispatchEvent(event);
  });
  eventBus.on('scalechange', function (evt) {
    let event = document.createEvent('UIEvents');
    event.initUIEvent('scalechange', true, true, window, 0);
    event.scale = evt.scale;
    event.presetValue = evt.presetValue;
    evt.source.container.dispatchEvent(event);
  });
  eventBus.on('updateviewarea', function (evt) {
    let event = document.createEvent('UIEvents');
    event.initUIEvent('updateviewarea', true, true, window, 0);
    event.location = evt.location;
    evt.source.container.dispatchEvent(event);
  });
  eventBus.on('find', function (evt) {
    if (evt.source === window) {
      return;
    }
    let event = document.createEvent('CustomEvent');
    event.initCustomEvent('find' + evt.type, true, true, {
      query: evt.query,
      phraseSearch: evt.phraseSearch,
      caseSensitive: evt.caseSensitive,
      highlightAll: evt.highlightAll,
      findPrevious: evt.findPrevious
    });
    window.dispatchEvent(event);
  });
  eventBus.on('attachmentsloaded', function (evt) {
    let event = document.createEvent('CustomEvent');
    event.initCustomEvent('attachmentsloaded', true, true, { attachmentsCount: evt.attachmentsCount });
    evt.source.container.dispatchEvent(event);
  });
  eventBus.on('sidebarviewchanged', function (evt) {
    let event = document.createEvent('CustomEvent');
    event.initCustomEvent('sidebarviewchanged', true, true, { view: evt.view });
    evt.source.outerContainer.dispatchEvent(event);
  });
  eventBus.on('pagemode', function (evt) {
    let event = document.createEvent('CustomEvent');
    event.initCustomEvent('pagemode', true, true, { mode: evt.mode });
    evt.source.pdfViewer.container.dispatchEvent(event);
  });
  eventBus.on('namedaction', function (evt) {
    let event = document.createEvent('CustomEvent');
    event.initCustomEvent('namedaction', true, true, { action: evt.action });
    evt.source.pdfViewer.container.dispatchEvent(event);
  });
  eventBus.on('presentationmodechanged', function (evt) {
    let event = document.createEvent('CustomEvent');
    event.initCustomEvent('presentationmodechanged', true, true, {
      active: evt.active,
      switchInProgress: evt.switchInProgress
    });
    window.dispatchEvent(event);
  });
  eventBus.on('outlineloaded', function (evt) {
    let event = document.createEvent('CustomEvent');
    event.initCustomEvent('outlineloaded', true, true, { outlineCount: evt.outlineCount });
    evt.source.container.dispatchEvent(event);
  });
}
let globalEventBus = null;
function getGlobalEventBus() {
  if (globalEventBus) {
    return globalEventBus;
  }
  globalEventBus = new _ui_utils.EventBus();
  attachDOMEventsToEventBus(globalEventBus);
  return globalEventBus;
}
exports.attachDOMEventsToEventBus = attachDOMEventsToEventBus;
exports.getGlobalEventBus = getGlobalEventBus;

/***/ }),
/* 3 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
const CLEANUP_TIMEOUT = 30000;
const RenderingStates = {
  INITIAL: 0,
  RUNNING: 1,
  PAUSED: 2,
  FINISHED: 3
};
class PDFRenderingQueue {
  constructor() {
    this.pdfViewer = null;
    this.pdfThumbnailViewer = null;
    this.onIdle = null;
    this.highestPriorityPage = null;
    this.idleTimeout = null;
    this.printing = false;
    this.isThumbnailViewEnabled = false;
  }
  setViewer(pdfViewer) {
    this.pdfViewer = pdfViewer;
  }
  setThumbnailViewer(pdfThumbnailViewer) {
    this.pdfThumbnailViewer = pdfThumbnailViewer;
  }
  isHighestPriority(view) {
    return this.highestPriorityPage === view.renderingId;
  }
  renderHighestPriority(currentlyVisiblePages) {
    if (this.idleTimeout) {
      clearTimeout(this.idleTimeout);
      this.idleTimeout = null;
    }
    if (this.pdfViewer.forceRendering(currentlyVisiblePages)) {
      return;
    }
    if (this.pdfThumbnailViewer && this.isThumbnailViewEnabled) {
      if (this.pdfThumbnailViewer.forceRendering()) {
        return;
      }
    }
    if (this.printing) {
      return;
    }
    if (this.onIdle) {
      this.idleTimeout = setTimeout(this.onIdle.bind(this), CLEANUP_TIMEOUT);
    }
  }
  getHighestPriority(visible, views, scrolledDown) {
    let visibleViews = visible.views;
    let numVisible = visibleViews.length;
    if (numVisible === 0) {
      return false;
    }
    for (let i = 0; i < numVisible; ++i) {
      let view = visibleViews[i].view;
      if (!this.isViewFinished(view)) {
        return view;
      }
    }
    if (scrolledDown) {
      let nextPageIndex = visible.last.id;
      if (views[nextPageIndex] && !this.isViewFinished(views[nextPageIndex])) {
        return views[nextPageIndex];
      }
    } else {
      let previousPageIndex = visible.first.id - 2;
      if (views[previousPageIndex] && !this.isViewFinished(views[previousPageIndex])) {
        return views[previousPageIndex];
      }
    }
    return null;
  }
  isViewFinished(view) {
    return view.renderingState === RenderingStates.FINISHED;
  }
  renderView(view) {
    switch (view.renderingState) {
      case RenderingStates.FINISHED:
        return false;
      case RenderingStates.PAUSED:
        this.highestPriorityPage = view.renderingId;
        view.resume();
        break;
      case RenderingStates.RUNNING:
        this.highestPriorityPage = view.renderingId;
        break;
      case RenderingStates.INITIAL:
        this.highestPriorityPage = view.renderingId;
        let continueRendering = () => {
          this.renderHighestPriority();
        };
        view.draw().then(continueRendering, continueRendering);
        break;
    }
    return true;
  }
}
exports.RenderingStates = RenderingStates;
exports.PDFRenderingQueue = PDFRenderingQueue;

/***/ }),
/* 4 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.PDFPrintServiceFactory = exports.DefaultExternalServices = exports.PDFViewerApplication = undefined;

var _ui_utils = __webpack_require__(0);

var _pdfjsLib = __webpack_require__(1);

var _pdf_cursor_tools = __webpack_require__(6);

var _pdf_rendering_queue = __webpack_require__(3);

var _pdf_sidebar = __webpack_require__(11);

var _dom_events = __webpack_require__(2);

var _overlay_manager = __webpack_require__(12);

var _password_prompt = __webpack_require__(13);

var _pdf_attachment_viewer = __webpack_require__(14);

var _pdf_document_properties = __webpack_require__(15);

var _pdf_find_bar = __webpack_require__(16);

var _pdf_find_controller = __webpack_require__(7);

var _pdf_history = __webpack_require__(17);

var _pdf_link_service = __webpack_require__(5);

var _pdf_outline_viewer = __webpack_require__(18);

var _pdf_presentation_mode = __webpack_require__(19);

var _pdf_sidebar_resizer = __webpack_require__(20);

var _pdf_thumbnail_viewer = __webpack_require__(21);

var _pdf_viewer = __webpack_require__(23);

var _secondary_toolbar = __webpack_require__(28);

var _toolbar = __webpack_require__(29);

var _viewer_compatibility = __webpack_require__(8);

var _view_history = __webpack_require__(30);

const DEFAULT_SCALE_DELTA = 1.1;
const DISABLE_AUTO_FETCH_LOADING_BAR_TIMEOUT = 5000;
function configure(PDFJS) {
  PDFJS.imageResourcesPath = './images/';
  _pdfjsLib.GlobalWorkerOptions.workerSrc = '../build/pdf.worker.js';
  PDFJS.cMapUrl = '../web/cmaps/';
  PDFJS.cMapPacked = true;
}
const DefaultExternalServices = {
  updateFindControlState(data) {},
  initPassiveLoading(callbacks) {},
  fallback(data, callback) {},
  reportTelemetry(data) {},
  createDownloadManager() {
    throw new Error('Not implemented: createDownloadManager');
  },
  createPreferences() {
    throw new Error('Not implemented: createPreferences');
  },
  createL10n() {
    throw new Error('Not implemented: createL10n');
  },
  supportsIntegratedFind: false,
  supportsDocumentFonts: true,
  supportsDocumentColors: true,
  supportedMouseWheelZoomModifierKeys: {
    ctrlKey: true,
    metaKey: true
  }
};
let PDFViewerApplication = {
  initialBookmark: document.location.hash.substring(1),
  initialized: false,
  fellback: false,
  appConfig: null,
  pdfDocument: null,
  pdfLoadingTask: null,
  printService: null,
  pdfViewer: null,
  pdfThumbnailViewer: null,
  pdfRenderingQueue: null,
  pdfPresentationMode: null,
  pdfDocumentProperties: null,
  pdfLinkService: null,
  pdfHistory: null,
  pdfSidebar: null,
  pdfSidebarResizer: null,
  pdfOutlineViewer: null,
  pdfAttachmentViewer: null,
  pdfCursorTools: null,
  store: null,
  downloadManager: null,
  overlayManager: null,
  preferences: null,
  toolbar: null,
  secondaryToolbar: null,
  eventBus: null,
  l10n: null,
  isInitialViewSet: false,
  downloadComplete: false,
  viewerPrefs: {
    enableWebGL: false,
    sidebarViewOnLoad: _pdf_sidebar.SidebarView.NONE,
    pdfBugEnabled: false,
    showPreviousViewOnLoad: true,
    defaultZoomValue: '',
    disablePageMode: false,
    disablePageLabels: false,
    renderer: 'canvas',
    textLayerMode: _ui_utils.TextLayerMode.ENABLE,
    renderInteractiveForms: false,
    enablePrintAutoRotate: false
  },
  isViewerEmbedded: window.parent !== window,
  url: '',
  baseUrl: '',
  externalServices: DefaultExternalServices,
  _boundEvents: {},
  contentDispositionFilename: null,
  initialize(appConfig) {
    this.preferences = this.externalServices.createPreferences();
    configure(_pdfjsLib.PDFJS);
    this.appConfig = appConfig;
    return this._readPreferences().then(() => {
      return this._parseHashParameters();
    }).then(() => {
      return this._initializeL10n();
    }).then(() => {
      return this._initializeViewerComponents();
    }).then(() => {
      this.bindEvents();
      this.bindWindowEvents();
      let appContainer = appConfig.appContainer || document.documentElement;
      this.l10n.translate(appContainer).then(() => {
        this.eventBus.dispatch('localized');
      });
      if (this.isViewerEmbedded && _pdfjsLib.PDFJS.externalLinkTarget === _pdfjsLib.LinkTarget.NONE) {
        _pdfjsLib.PDFJS.externalLinkTarget = _pdfjsLib.LinkTarget.TOP;
      }
      this.initialized = true;
    });
  },
  _readPreferences() {
    let { preferences, viewerPrefs } = this;
    return Promise.all([preferences.get('enableWebGL').then(function resolved(value) {
      viewerPrefs['enableWebGL'] = value;
    }), preferences.get('sidebarViewOnLoad').then(function resolved(value) {
      viewerPrefs['sidebarViewOnLoad'] = value;
    }), preferences.get('pdfBugEnabled').then(function resolved(value) {
      viewerPrefs['pdfBugEnabled'] = value;
    }), preferences.get('showPreviousViewOnLoad').then(function resolved(value) {
      viewerPrefs['showPreviousViewOnLoad'] = value;
    }), preferences.get('defaultZoomValue').then(function resolved(value) {
      viewerPrefs['defaultZoomValue'] = value;
    }), preferences.get('textLayerMode').then(function resolved(value) {
      if (viewerPrefs['textLayerMode'] === _ui_utils.TextLayerMode.DISABLE) {
        return;
      }
      viewerPrefs['textLayerMode'] = value;
    }), preferences.get('disableRange').then(function resolved(value) {
      if (_pdfjsLib.PDFJS.disableRange === true) {
        return;
      }
      _pdfjsLib.PDFJS.disableRange = value;
    }), preferences.get('disableStream').then(function resolved(value) {
      if (_pdfjsLib.PDFJS.disableStream === true) {
        return;
      }
      _pdfjsLib.PDFJS.disableStream = value;
    }), preferences.get('disableAutoFetch').then(function resolved(value) {
      _pdfjsLib.PDFJS.disableAutoFetch = value;
    }), preferences.get('disableFontFace').then(function resolved(value) {
      if (_pdfjsLib.PDFJS.disableFontFace === true) {
        return;
      }
      _pdfjsLib.PDFJS.disableFontFace = value;
    }), preferences.get('useOnlyCssZoom').then(function resolved(value) {
      _pdfjsLib.PDFJS.useOnlyCssZoom = value;
    }), preferences.get('externalLinkTarget').then(function resolved(value) {
      if (_pdfjsLib.PDFJS.externalLinkTarget !== _pdfjsLib.LinkTarget.NONE) {
        return;
      }
      _pdfjsLib.PDFJS.externalLinkTarget = value;
    }), preferences.get('renderer').then(function resolved(value) {
      viewerPrefs['renderer'] = value;
    }), preferences.get('renderInteractiveForms').then(function resolved(value) {
      viewerPrefs['renderInteractiveForms'] = value;
    }), preferences.get('disablePageMode').then(function resolved(value) {
      viewerPrefs['disablePageMode'] = value;
    }), preferences.get('disablePageLabels').then(function resolved(value) {
      viewerPrefs['disablePageLabels'] = value;
    }), preferences.get('enablePrintAutoRotate').then(function resolved(value) {
      viewerPrefs['enablePrintAutoRotate'] = value;
    })]).catch(function (reason) {});
  },
  _parseHashParameters() {
    let { appConfig, viewerPrefs } = this;
    let waitOn = [];
    if (viewerPrefs['pdfBugEnabled']) {
      let hash = document.location.hash.substring(1);
      let hashParams = (0, _ui_utils.parseQueryString)(hash);
      if ('disableworker' in hashParams && hashParams['disableworker'] === 'true') {
        waitOn.push(loadFakeWorker());
      }
      if ('disablerange' in hashParams) {
        _pdfjsLib.PDFJS.disableRange = hashParams['disablerange'] === 'true';
      }
      if ('disablestream' in hashParams) {
        _pdfjsLib.PDFJS.disableStream = hashParams['disablestream'] === 'true';
      }
      if ('disableautofetch' in hashParams) {
        _pdfjsLib.PDFJS.disableAutoFetch = hashParams['disableautofetch'] === 'true';
      }
      if ('disablefontface' in hashParams) {
        _pdfjsLib.PDFJS.disableFontFace = hashParams['disablefontface'] === 'true';
      }
      if ('disablehistory' in hashParams) {
        _pdfjsLib.PDFJS.disableHistory = hashParams['disablehistory'] === 'true';
      }
      if ('webgl' in hashParams) {
        viewerPrefs['enableWebGL'] = hashParams['webgl'] === 'true';
      }
      if ('useonlycsszoom' in hashParams) {
        _pdfjsLib.PDFJS.useOnlyCssZoom = hashParams['useonlycsszoom'] === 'true';
      }
      if ('verbosity' in hashParams) {
        _pdfjsLib.PDFJS.verbosity = hashParams['verbosity'] | 0;
      }
      if ('textlayer' in hashParams) {
        switch (hashParams['textlayer']) {
          case 'off':
            viewerPrefs['textLayerMode'] = _ui_utils.TextLayerMode.DISABLE;
            break;
          case 'visible':
          case 'shadow':
          case 'hover':
            let viewer = appConfig.viewerContainer;
            viewer.classList.add('textLayer-' + hashParams['textlayer']);
            break;
        }
      }
      if ('pdfbug' in hashParams) {
        _pdfjsLib.PDFJS.pdfBug = true;
        let enabled = hashParams['pdfbug'].split(',');
        waitOn.push(loadAndEnablePDFBug(enabled));
      }
    }
    return Promise.all(waitOn);
  },
  _initializeL10n() {
    this.l10n = this.externalServices.createL10n({ locale: _pdfjsLib.PDFJS.locale });
    return this.l10n.getDirection().then(dir => {
      document.getElementsByTagName('html')[0].dir = dir;
    });
  },
  _initializeViewerComponents() {
    let { appConfig, viewerPrefs } = this;
    return new Promise((resolve, reject) => {
      this.overlayManager = new _overlay_manager.OverlayManager();
      let eventBus = appConfig.eventBus || (0, _dom_events.getGlobalEventBus)();
      this.eventBus = eventBus;
      let pdfRenderingQueue = new _pdf_rendering_queue.PDFRenderingQueue();
      pdfRenderingQueue.onIdle = this.cleanup.bind(this);
      this.pdfRenderingQueue = pdfRenderingQueue;
      let pdfLinkService = new _pdf_link_service.PDFLinkService({
        eventBus,
        externalLinkTarget: _pdfjsLib.PDFJS.externalLinkTarget,
        externalLinkRel: _pdfjsLib.PDFJS.externalLinkRel
      });
      this.pdfLinkService = pdfLinkService;
      let downloadManager = this.externalServices.createDownloadManager();
      this.downloadManager = downloadManager;
      let container = appConfig.mainContainer;
      let viewer = appConfig.viewerContainer;
      this.pdfViewer = new _pdf_viewer.PDFViewer({
        container,
        viewer,
        eventBus,
        renderingQueue: pdfRenderingQueue,
        linkService: pdfLinkService,
        downloadManager,
        renderer: viewerPrefs['renderer'],
        enableWebGL: viewerPrefs['enableWebGL'],
        l10n: this.l10n,
        textLayerMode: viewerPrefs['textLayerMode'],
        imageResourcesPath: _pdfjsLib.PDFJS.imageResourcesPath,
        renderInteractiveForms: viewerPrefs['renderInteractiveForms'],
        enablePrintAutoRotate: viewerPrefs['enablePrintAutoRotate'],
        useOnlyCssZoom: _pdfjsLib.PDFJS.useOnlyCssZoom,
        maxCanvasPixels: _pdfjsLib.PDFJS.maxCanvasPixels
      });
      pdfRenderingQueue.setViewer(this.pdfViewer);
      pdfLinkService.setViewer(this.pdfViewer);
      let thumbnailContainer = appConfig.sidebar.thumbnailView;
      this.pdfThumbnailViewer = new _pdf_thumbnail_viewer.PDFThumbnailViewer({
        container: thumbnailContainer,
        renderingQueue: pdfRenderingQueue,
        linkService: pdfLinkService,
        l10n: this.l10n
      });
      pdfRenderingQueue.setThumbnailViewer(this.pdfThumbnailViewer);
      this.pdfHistory = new _pdf_history.PDFHistory({
        linkService: pdfLinkService,
        eventBus
      });
      pdfLinkService.setHistory(this.pdfHistory);
      this.findController = new _pdf_find_controller.PDFFindController({ pdfViewer: this.pdfViewer });
      this.findController.onUpdateResultsCount = matchCount => {
        if (this.supportsIntegratedFind) {
          return;
        }
        this.findBar.updateResultsCount(matchCount);
      };
      this.findController.onUpdateState = (state, previous, matchCount) => {
        if (this.supportsIntegratedFind) {
          this.externalServices.updateFindControlState({
            result: state,
            findPrevious: previous
          });
        } else {
          this.findBar.updateUIState(state, previous, matchCount);
        }
      };
      this.pdfViewer.setFindController(this.findController);
      let findBarConfig = Object.create(appConfig.findBar);
      findBarConfig.findController = this.findController;
      findBarConfig.eventBus = eventBus;
      this.findBar = new _pdf_find_bar.PDFFindBar(findBarConfig, this.l10n);
      this.pdfDocumentProperties = new _pdf_document_properties.PDFDocumentProperties(appConfig.documentProperties, this.overlayManager, this.l10n);
      this.pdfCursorTools = new _pdf_cursor_tools.PDFCursorTools({
        container,
        eventBus,
        preferences: this.preferences
      });
      this.toolbar = new _toolbar.Toolbar(appConfig.toolbar, container, eventBus, this.l10n);
      this.secondaryToolbar = new _secondary_toolbar.SecondaryToolbar(appConfig.secondaryToolbar, container, eventBus);
      if (this.supportsFullscreen) {
        this.pdfPresentationMode = new _pdf_presentation_mode.PDFPresentationMode({
          container,
          viewer,
          pdfViewer: this.pdfViewer,
          eventBus,
          contextMenuItems: appConfig.fullscreen
        });
      }
      this.passwordPrompt = new _password_prompt.PasswordPrompt(appConfig.passwordOverlay, this.overlayManager, this.l10n);
      this.pdfOutlineViewer = new _pdf_outline_viewer.PDFOutlineViewer({
        container: appConfig.sidebar.outlineView,
        eventBus,
        linkService: pdfLinkService
      });
      this.pdfAttachmentViewer = new _pdf_attachment_viewer.PDFAttachmentViewer({
        container: appConfig.sidebar.attachmentsView,
        eventBus,
        downloadManager
      });
      let sidebarConfig = Object.create(appConfig.sidebar);
      sidebarConfig.pdfViewer = this.pdfViewer;
      sidebarConfig.pdfThumbnailViewer = this.pdfThumbnailViewer;
      sidebarConfig.pdfOutlineViewer = this.pdfOutlineViewer;
      sidebarConfig.eventBus = eventBus;
      this.pdfSidebar = new _pdf_sidebar.PDFSidebar(sidebarConfig, this.l10n);
      this.pdfSidebar.onToggled = this.forceRendering.bind(this);
      this.pdfSidebarResizer = new _pdf_sidebar_resizer.PDFSidebarResizer(appConfig.sidebarResizer, eventBus, this.l10n);
      resolve(undefined);
    });
  },
  run(config) {
    this.initialize(config).then(webViewerInitialized);
  },
  zoomIn(ticks) {
    let newScale = this.pdfViewer.currentScale;
    do {
      newScale = (newScale * DEFAULT_SCALE_DELTA).toFixed(2);
      newScale = Math.ceil(newScale * 10) / 10;
      newScale = Math.min(_ui_utils.MAX_SCALE, newScale);
    } while (--ticks > 0 && newScale < _ui_utils.MAX_SCALE);
    this.pdfViewer.currentScaleValue = newScale;
  },
  zoomOut(ticks) {
    let newScale = this.pdfViewer.currentScale;
    do {
      newScale = (newScale / DEFAULT_SCALE_DELTA).toFixed(2);
      newScale = Math.floor(newScale * 10) / 10;
      newScale = Math.max(_ui_utils.MIN_SCALE, newScale);
    } while (--ticks > 0 && newScale > _ui_utils.MIN_SCALE);
    this.pdfViewer.currentScaleValue = newScale;
  },
  get pagesCount() {
    return this.pdfDocument ? this.pdfDocument.numPages : 0;
  },
  set page(val) {
    this.pdfViewer.currentPageNumber = val;
  },
  get page() {
    return this.pdfViewer.currentPageNumber;
  },
  get printing() {
    return !!this.printService;
  },
  get supportsPrinting() {
    return PDFPrintServiceFactory.instance.supportsPrinting;
  },
  get supportsFullscreen() {
    let support;
    support = document.fullscreenEnabled === true || document.mozFullScreenEnabled === true;
    if (support && _viewer_compatibility.viewerCompatibilityParams['disableFullscreen'] === true) {
      support = false;
    }
    return (0, _pdfjsLib.shadow)(this, 'supportsFullscreen', support);
  },
  get supportsIntegratedFind() {
    return this.externalServices.supportsIntegratedFind;
  },
  get supportsDocumentFonts() {
    return this.externalServices.supportsDocumentFonts;
  },
  get supportsDocumentColors() {
    return this.externalServices.supportsDocumentColors;
  },
  get loadingBar() {
    let bar = new _ui_utils.ProgressBar('#loadingBar');
    return (0, _pdfjsLib.shadow)(this, 'loadingBar', bar);
  },
  get supportedMouseWheelZoomModifierKeys() {
    return this.externalServices.supportedMouseWheelZoomModifierKeys;
  },
  initPassiveLoading() {
    this.externalServices.initPassiveLoading({
      onOpenWithTransport(url, length, transport) {
        PDFViewerApplication.open(url, { range: transport });
        if (length) {
          PDFViewerApplication.pdfDocumentProperties.setFileSize(length);
        }
      },
      onOpenWithData(data) {
        PDFViewerApplication.open(data);
      },
      onOpenWithURL(url, length, originalURL) {
        let file = url,
            args = null;
        if (length !== undefined) {
          args = { length };
        }
        if (originalURL !== undefined) {
          file = {
            file: url,
            originalURL
          };
        }
        PDFViewerApplication.open(file, args);
      },
      onError(err) {
        PDFViewerApplication.l10n.get('loading_error', null, 'An error occurred while loading the PDF.').then(msg => {
          PDFViewerApplication.error(msg, err);
        });
      },
      onProgress(loaded, total) {
        PDFViewerApplication.progress(loaded / total);
      }
    });
  },
  setTitleUsingUrl(url) {
    this.url = url;
    this.baseUrl = url.split('#')[0];
    let title = (0, _ui_utils.getPDFFileNameFromURL)(url, '');
    if (!title) {
      try {
        title = decodeURIComponent((0, _pdfjsLib.getFilenameFromUrl)(url)) || url;
      } catch (ex) {
        title = url;
      }
    }
    this.setTitle(title);
  },
  setTitle(title) {
    if (this.isViewerEmbedded) {
      return;
    }
    document.title = title;
  },
  close() {
    let errorWrapper = this.appConfig.errorWrapper.container;
    errorWrapper.setAttribute('hidden', 'true');
    if (!this.pdfLoadingTask) {
      return Promise.resolve();
    }
    let promise = this.pdfLoadingTask.destroy();
    this.pdfLoadingTask = null;
    if (this.pdfDocument) {
      this.pdfDocument = null;
      this.pdfThumbnailViewer.setDocument(null);
      this.pdfViewer.setDocument(null);
      this.pdfLinkService.setDocument(null, null);
      this.pdfDocumentProperties.setDocument(null, null);
    }
    this.store = null;
    this.isInitialViewSet = false;
    this.downloadComplete = false;
    this.url = '';
    this.baseUrl = '';
    this.contentDispositionFilename = null;
    this.pdfSidebar.reset();
    this.pdfOutlineViewer.reset();
    this.pdfAttachmentViewer.reset();
    this.findController.reset();
    this.findBar.reset();
    this.toolbar.reset();
    this.secondaryToolbar.reset();
    if (typeof PDFBug !== 'undefined') {
      PDFBug.cleanup();
    }
    return promise;
  },
  open(file, args) {
    if (this.pdfLoadingTask) {
      return this.close().then(() => {
        this.preferences.reload();
        return this.open(file, args);
      });
    }
    let parameters = Object.create(null);
    if (typeof file === 'string') {
      this.setTitleUsingUrl(file);
      parameters.url = file;
    } else if (file && 'byteLength' in file) {
      parameters.data = file;
    } else if (file.url && file.originalUrl) {
      this.setTitleUsingUrl(file.originalUrl);
      parameters.url = file.url;
    }
    parameters.docBaseUrl = this.baseUrl;
    parameters.verbosity = _pdfjsLib.PDFJS.verbosity;
    if (args) {
      for (let prop in args) {
        if (prop === 'length') {
          this.pdfDocumentProperties.setFileSize(args[prop]);
        }
        parameters[prop] = args[prop];
      }
    }
    if (this.url && (0, _ui_utils.isFileSchema)(this.url)) {
      let appConfig = this.appConfig;
      appConfig.toolbar.download.setAttribute('hidden', 'true');
      appConfig.secondaryToolbar.downloadButton.setAttribute('hidden', 'true');
    }
    let loadingTask = (0, _pdfjsLib.getDocument)(parameters);
    this.pdfLoadingTask = loadingTask;
    loadingTask.onPassword = (updateCallback, reason) => {
      this.passwordPrompt.setUpdateCallback(updateCallback, reason);
      this.passwordPrompt.open();
    };
    loadingTask.onProgress = ({ loaded, total }) => {
      this.progress(loaded / total);
    };
    loadingTask.onUnsupportedFeature = this.fallback.bind(this);
    return loadingTask.promise.then(pdfDocument => {
      this.load(pdfDocument);
    }, exception => {
      if (loadingTask !== this.pdfLoadingTask) {
        return;
      }
      let message = exception && exception.message;
      let loadingErrorMessage;
      if (exception instanceof _pdfjsLib.InvalidPDFException) {
        loadingErrorMessage = this.l10n.get('invalid_file_error', null, 'Invalid or corrupted PDF file.');
      } else if (exception instanceof _pdfjsLib.MissingPDFException) {
        loadingErrorMessage = this.l10n.get('missing_file_error', null, 'Missing PDF file.');
      } else if (exception instanceof _pdfjsLib.UnexpectedResponseException) {
        loadingErrorMessage = this.l10n.get('unexpected_response_error', null, 'Unexpected server response.');
      } else {
        loadingErrorMessage = this.l10n.get('loading_error', null, 'An error occurred while loading the PDF.');
      }
      return loadingErrorMessage.then(msg => {
        this.error(msg, { message });
        throw new Error(msg);
      });
    });
  },
  download() {
    function downloadByUrl() {
      downloadManager.downloadUrl(url, filename);
    }
    let url = this.baseUrl;
    let filename = this.contentDispositionFilename || (0, _ui_utils.getPDFFileNameFromURL)(this.url);
    let downloadManager = this.downloadManager;
    downloadManager.onerror = err => {
      this.error(`PDF failed to download: ${err}`);
    };
    if (!this.pdfDocument || !this.downloadComplete) {
      downloadByUrl();
      return;
    }
    this.pdfDocument.getData().then(function (data) {
      let blob = (0, _pdfjsLib.createBlob)(data, 'application/pdf');
      downloadManager.download(blob, url, filename);
    }).catch(downloadByUrl);
  },
  fallback(featureId) {
    if (this.fellback) {
      return;
    }
    this.fellback = true;
    this.externalServices.fallback({
      featureId,
      url: this.baseUrl
    }, function response(download) {
      if (!download) {
        return;
      }
      PDFViewerApplication.download();
    });
  },
  error(message, moreInfo) {
    let moreInfoText = [this.l10n.get('error_version_info', {
      version: _pdfjsLib.version || '?',
      build: _pdfjsLib.build || '?'
    }, 'PDF.js v{{version}} (build: {{build}})')];
    if (moreInfo) {
      moreInfoText.push(this.l10n.get('error_message', { message: moreInfo.message }, 'Message: {{message}}'));
      if (moreInfo.stack) {
        moreInfoText.push(this.l10n.get('error_stack', { stack: moreInfo.stack }, 'Stack: {{stack}}'));
      } else {
        if (moreInfo.filename) {
          moreInfoText.push(this.l10n.get('error_file', { file: moreInfo.filename }, 'File: {{file}}'));
        }
        if (moreInfo.lineNumber) {
          moreInfoText.push(this.l10n.get('error_line', { line: moreInfo.lineNumber }, 'Line: {{line}}'));
        }
      }
    }
    Promise.all(moreInfoText).then(parts => {
      console.error(message + '\n' + parts.join('\n'));
    });
    this.fallback();
  },
  progress(level) {
    if (this.downloadComplete) {
      return;
    }
    let percent = Math.round(level * 100);
    if (percent > this.loadingBar.percent || isNaN(percent)) {
      this.loadingBar.percent = percent;
      if (_pdfjsLib.PDFJS.disableAutoFetch && percent) {
        if (this.disableAutoFetchLoadingBarTimeout) {
          clearTimeout(this.disableAutoFetchLoadingBarTimeout);
          this.disableAutoFetchLoadingBarTimeout = null;
        }
        this.loadingBar.show();
        this.disableAutoFetchLoadingBarTimeout = setTimeout(() => {
          this.loadingBar.hide();
          this.disableAutoFetchLoadingBarTimeout = null;
        }, DISABLE_AUTO_FETCH_LOADING_BAR_TIMEOUT);
      }
    }
  },
  load(pdfDocument) {
    this.pdfDocument = pdfDocument;
    pdfDocument.getDownloadInfo().then(() => {
      this.downloadComplete = true;
      this.loadingBar.hide();
      firstPagePromise.then(() => {
        this.eventBus.dispatch('documentload', { source: this });
      });
    });
    let pageModePromise = pdfDocument.getPageMode().catch(function () {});
    this.toolbar.setPagesCount(pdfDocument.numPages, false);
    this.secondaryToolbar.setPagesCount(pdfDocument.numPages);
    let id = this.documentFingerprint = pdfDocument.fingerprint;
    let store = this.store = new _view_history.ViewHistory(id);
    let baseDocumentUrl;
    baseDocumentUrl = this.baseUrl;
    this.pdfLinkService.setDocument(pdfDocument, baseDocumentUrl);
    this.pdfDocumentProperties.setDocument(pdfDocument, this.url);
    let pdfViewer = this.pdfViewer;
    pdfViewer.setDocument(pdfDocument);
    let firstPagePromise = pdfViewer.firstPagePromise;
    let pagesPromise = pdfViewer.pagesPromise;
    let onePageRendered = pdfViewer.onePageRendered;
    let pdfThumbnailViewer = this.pdfThumbnailViewer;
    pdfThumbnailViewer.setDocument(pdfDocument);
    firstPagePromise.then(pdfPage => {
      this.loadingBar.setWidth(this.appConfig.viewerContainer);
      if (!_pdfjsLib.PDFJS.disableHistory && !this.isViewerEmbedded) {
        let resetHistory = !this.viewerPrefs['showPreviousViewOnLoad'];
        this.pdfHistory.initialize(id, resetHistory);
        if (this.pdfHistory.initialBookmark) {
          this.initialBookmark = this.pdfHistory.initialBookmark;
          this.initialRotation = this.pdfHistory.initialRotation;
        }
      }
      let initialParams = {
        bookmark: null,
        hash: null
      };
      let storePromise = store.getMultiple({
        exists: false,
        page: '1',
        zoom: _ui_utils.DEFAULT_SCALE_VALUE,
        scrollLeft: '0',
        scrollTop: '0',
        rotation: null,
        sidebarView: _pdf_sidebar.SidebarView.NONE
      }).catch(() => {});
      Promise.all([storePromise, pageModePromise]).then(([values = {}, pageMode]) => {
        let hash = this.viewerPrefs['defaultZoomValue'] ? 'zoom=' + this.viewerPrefs['defaultZoomValue'] : null;
        let rotation = null;
        let sidebarView = this.viewerPrefs['sidebarViewOnLoad'];
        if (values.exists && this.viewerPrefs['showPreviousViewOnLoad']) {
          hash = 'page=' + values.page + '&zoom=' + (this.viewerPrefs['defaultZoomValue'] || values.zoom) + ',' + values.scrollLeft + ',' + values.scrollTop;
          rotation = parseInt(values.rotation, 10);
          sidebarView = sidebarView || values.sidebarView | 0;
        }
        if (pageMode && !this.viewerPrefs['disablePageMode']) {
          sidebarView = sidebarView || apiPageModeToSidebarView(pageMode);
        }
        return {
          hash,
          rotation,
          sidebarView
        };
      }).then(({ hash, rotation, sidebarView }) => {
        initialParams.bookmark = this.initialBookmark;
        initialParams.hash = hash;
        this.setInitialView(hash, {
          rotation,
          sidebarView
        });
        if (!this.isViewerEmbedded) {
          pdfViewer.focus();
        }
        return pagesPromise;
      }).then(() => {
        if (!initialParams.bookmark && !initialParams.hash) {
          return;
        }
        if (pdfViewer.hasEqualPageSizes) {
          return;
        }
        this.initialBookmark = initialParams.bookmark;
        pdfViewer.currentScaleValue = pdfViewer.currentScaleValue;
        this.setInitialView(initialParams.hash);
      }).then(function () {
        pdfViewer.update();
      });
    });
    pdfDocument.getPageLabels().then(labels => {
      if (!labels || this.viewerPrefs['disablePageLabels']) {
        return;
      }
      let i = 0,
          numLabels = labels.length;
      if (numLabels !== this.pagesCount) {
        console.error('The number of Page Labels does not match ' + 'the number of pages in the document.');
        return;
      }
      while (i < numLabels && labels[i] === (i + 1).toString()) {
        i++;
      }
      if (i === numLabels) {
        return;
      }
      pdfViewer.setPageLabels(labels);
      pdfThumbnailViewer.setPageLabels(labels);
      this.toolbar.setPagesCount(pdfDocument.numPages, true);
      this.toolbar.setPageNumber(pdfViewer.currentPageNumber, pdfViewer.currentPageLabel);
    });
    pagesPromise.then(() => {
      if (!this.supportsPrinting) {
        return;
      }
      pdfDocument.getJavaScript().then(javaScript => {
        if (!javaScript) {
          return;
        }
        javaScript.some(js => {
          if (!js) {
            return false;
          }
          console.warn('Warning: JavaScript is not supported');
          this.fallback(_pdfjsLib.UNSUPPORTED_FEATURES.javaScript);
          return true;
        });
        let regex = /\bprint\s*\(/;
        for (let i = 0, ii = javaScript.length; i < ii; i++) {
          let js = javaScript[i];
          if (js && regex.test(js)) {
            setTimeout(function () {
              window.print();
            });
            return;
          }
        }
      });
    });
    Promise.all([onePageRendered, _ui_utils.animationStarted]).then(() => {
      pdfDocument.getOutline().then(outline => {
        this.pdfOutlineViewer.render({ outline });
      });
      pdfDocument.getAttachments().then(attachments => {
        this.pdfAttachmentViewer.render({ attachments });
      });
    });
    pdfDocument.getMetadata().then(({ info, metadata, contentDispositionFilename }) => {
      this.documentInfo = info;
      this.metadata = metadata;
      this.contentDispositionFilename = contentDispositionFilename;
      console.log('PDF ' + pdfDocument.fingerprint + ' [' + info.PDFFormatVersion + ' ' + (info.Producer || '-').trim() + ' / ' + (info.Creator || '-').trim() + ']' + ' (PDF.js: ' + (_pdfjsLib.version || '-') + (this.viewerPrefs['enableWebGL'] ? ' [WebGL]' : '') + ')');
      let pdfTitle;
      if (metadata && metadata.has('dc:title')) {
        let title = metadata.get('dc:title');
        if (title !== 'Untitled') {
          pdfTitle = title;
        }
      }
      if (!pdfTitle && info && info['Title']) {
        pdfTitle = info['Title'];
      }
      if (pdfTitle) {
        this.setTitle(`${pdfTitle} - ${contentDispositionFilename || document.title}`);
      } else if (contentDispositionFilename) {
        this.setTitle(contentDispositionFilename);
      }
      if (info.IsAcroFormPresent) {
        console.warn('Warning: AcroForm/XFA is not supported');
        this.fallback(_pdfjsLib.UNSUPPORTED_FEATURES.forms);
      }
      let versionId = String(info.PDFFormatVersion).slice(-1) | 0;
      let generatorId = 0;
      const KNOWN_GENERATORS = ['acrobat distiller', 'acrobat pdfwriter', 'adobe livecycle', 'adobe pdf library', 'adobe photoshop', 'ghostscript', 'tcpdf', 'cairo', 'dvipdfm', 'dvips', 'pdftex', 'pdfkit', 'itext', 'prince', 'quarkxpress', 'mac os x', 'microsoft', 'openoffice', 'oracle', 'luradocument', 'pdf-xchange', 'antenna house', 'aspose.cells', 'fpdf'];
      if (info.Producer) {
        KNOWN_GENERATORS.some(function (generator, s, i) {
          if (!generator.includes(s)) {
            return false;
          }
          generatorId = i + 1;
          return true;
        }.bind(null, info.Producer.toLowerCase()));
      }
      let formType = !info.IsAcroFormPresent ? null : info.IsXFAPresent ? 'xfa' : 'acroform';
      this.externalServices.reportTelemetry({
        type: 'documentInfo',
        version: versionId,
        generator: generatorId,
        formType
      });
    });
  },
  setInitialView(storedHash, { rotation, sidebarView } = {}) {
    let setRotation = angle => {
      if ((0, _ui_utils.isValidRotation)(angle)) {
        this.pdfViewer.pagesRotation = angle;
      }
    };
    this.isInitialViewSet = true;
    this.pdfSidebar.setInitialView(sidebarView);
    if (this.initialBookmark) {
      setRotation(this.initialRotation);
      delete this.initialRotation;
      this.pdfLinkService.setHash(this.initialBookmark);
      this.initialBookmark = null;
    } else if (storedHash) {
      setRotation(rotation);
      this.pdfLinkService.setHash(storedHash);
    }
    this.toolbar.setPageNumber(this.pdfViewer.currentPageNumber, this.pdfViewer.currentPageLabel);
    this.secondaryToolbar.setPageNumber(this.pdfViewer.currentPageNumber);
    if (!this.pdfViewer.currentScaleValue) {
      this.pdfViewer.currentScaleValue = _ui_utils.DEFAULT_SCALE_VALUE;
    }
  },
  cleanup() {
    if (!this.pdfDocument) {
      return;
    }
    this.pdfViewer.cleanup();
    this.pdfThumbnailViewer.cleanup();
    if (this.pdfViewer.renderer !== _ui_utils.RendererType.SVG) {
      this.pdfDocument.cleanup();
    }
  },
  forceRendering() {
    this.pdfRenderingQueue.printing = this.printing;
    this.pdfRenderingQueue.isThumbnailViewEnabled = this.pdfSidebar.isThumbnailViewVisible;
    this.pdfRenderingQueue.renderHighestPriority();
  },
  beforePrint() {
    if (this.printService) {
      return;
    }
    if (!this.supportsPrinting) {
      this.l10n.get('printing_not_supported', null, 'Warning: Printing is not fully supported by ' + 'this browser.').then(printMessage => {
        this.error(printMessage);
      });
      return;
    }
    if (!this.pdfViewer.pageViewsReady) {
      this.l10n.get('printing_not_ready', null, 'Warning: The PDF is not fully loaded for printing.').then(notReadyMessage => {
        window.alert(notReadyMessage);
      });
      return;
    }
    let pagesOverview = this.pdfViewer.getPagesOverview();
    let printContainer = this.appConfig.printContainer;
    let printService = PDFPrintServiceFactory.instance.createPrintService(this.pdfDocument, pagesOverview, printContainer, this.l10n);
    this.printService = printService;
    this.forceRendering();
    printService.layout();
    this.externalServices.reportTelemetry({ type: 'print' });
  },
  afterPrint: function pdfViewSetupAfterPrint() {
    if (this.printService) {
      this.printService.destroy();
      this.printService = null;
    }
    this.forceRendering();
  },
  rotatePages(delta) {
    if (!this.pdfDocument) {
      return;
    }
    let newRotation = (this.pdfViewer.pagesRotation + 360 + delta) % 360;
    this.pdfViewer.pagesRotation = newRotation;
  },
  requestPresentationMode() {
    if (!this.pdfPresentationMode) {
      return;
    }
    this.pdfPresentationMode.request();
  },
  bindEvents() {
    let { eventBus, _boundEvents } = this;
    _boundEvents.beforePrint = this.beforePrint.bind(this);
    _boundEvents.afterPrint = this.afterPrint.bind(this);
    eventBus.on('resize', webViewerResize);
    eventBus.on('hashchange', webViewerHashchange);
    eventBus.on('beforeprint', _boundEvents.beforePrint);
    eventBus.on('afterprint', _boundEvents.afterPrint);
    eventBus.on('pagerendered', webViewerPageRendered);
    eventBus.on('textlayerrendered', webViewerTextLayerRendered);
    eventBus.on('updateviewarea', webViewerUpdateViewarea);
    eventBus.on('pagechanging', webViewerPageChanging);
    eventBus.on('scalechanging', webViewerScaleChanging);
    eventBus.on('rotationchanging', webViewerRotationChanging);
    eventBus.on('sidebarviewchanged', webViewerSidebarViewChanged);
    eventBus.on('pagemode', webViewerPageMode);
    eventBus.on('namedaction', webViewerNamedAction);
    eventBus.on('presentationmodechanged', webViewerPresentationModeChanged);
    eventBus.on('presentationmode', webViewerPresentationMode);
    eventBus.on('openfile', webViewerOpenFile);
    eventBus.on('print', webViewerPrint);
    eventBus.on('download', webViewerDownload);
    eventBus.on('firstpage', webViewerFirstPage);
    eventBus.on('lastpage', webViewerLastPage);
    eventBus.on('nextpage', webViewerNextPage);
    eventBus.on('previouspage', webViewerPreviousPage);
    eventBus.on('zoomin', webViewerZoomIn);
    eventBus.on('zoomout', webViewerZoomOut);
    eventBus.on('pagenumberchanged', webViewerPageNumberChanged);
    eventBus.on('scalechanged', webViewerScaleChanged);
    eventBus.on('rotatecw', webViewerRotateCw);
    eventBus.on('rotateccw', webViewerRotateCcw);
    eventBus.on('documentproperties', webViewerDocumentProperties);
    eventBus.on('find', webViewerFind);
    eventBus.on('findfromurlhash', webViewerFindFromUrlHash);
  },
  bindWindowEvents() {
    let { eventBus, _boundEvents } = this;
    _boundEvents.windowResize = () => {
      eventBus.dispatch('resize', { source: window });
    };
    _boundEvents.windowHashChange = () => {
      eventBus.dispatch('hashchange', { hash: document.location.hash.substring(1) });
    };
    _boundEvents.windowBeforePrint = () => {
      eventBus.dispatch('beforeprint');
    };
    _boundEvents.windowAfterPrint = () => {
      eventBus.dispatch('afterprint');
    };
    window.addEventListener('wheel', webViewerWheel);
    window.addEventListener('click', webViewerClick);
    window.addEventListener('keydown', webViewerKeyDown);
    window.addEventListener('resize', _boundEvents.windowResize);
    window.addEventListener('hashchange', _boundEvents.windowHashChange);
    window.addEventListener('beforeprint', _boundEvents.windowBeforePrint);
    window.addEventListener('afterprint', _boundEvents.windowAfterPrint);
  },
  unbindEvents() {
    let { eventBus, _boundEvents } = this;
    eventBus.off('resize', webViewerResize);
    eventBus.off('hashchange', webViewerHashchange);
    eventBus.off('beforeprint', _boundEvents.beforePrint);
    eventBus.off('afterprint', _boundEvents.afterPrint);
    eventBus.off('pagerendered', webViewerPageRendered);
    eventBus.off('textlayerrendered', webViewerTextLayerRendered);
    eventBus.off('updateviewarea', webViewerUpdateViewarea);
    eventBus.off('pagechanging', webViewerPageChanging);
    eventBus.off('scalechanging', webViewerScaleChanging);
    eventBus.off('rotationchanging', webViewerRotationChanging);
    eventBus.off('sidebarviewchanged', webViewerSidebarViewChanged);
    eventBus.off('pagemode', webViewerPageMode);
    eventBus.off('namedaction', webViewerNamedAction);
    eventBus.off('presentationmodechanged', webViewerPresentationModeChanged);
    eventBus.off('presentationmode', webViewerPresentationMode);
    eventBus.off('openfile', webViewerOpenFile);
    eventBus.off('print', webViewerPrint);
    eventBus.off('download', webViewerDownload);
    eventBus.off('firstpage', webViewerFirstPage);
    eventBus.off('lastpage', webViewerLastPage);
    eventBus.off('nextpage', webViewerNextPage);
    eventBus.off('previouspage', webViewerPreviousPage);
    eventBus.off('zoomin', webViewerZoomIn);
    eventBus.off('zoomout', webViewerZoomOut);
    eventBus.off('pagenumberchanged', webViewerPageNumberChanged);
    eventBus.off('scalechanged', webViewerScaleChanged);
    eventBus.off('rotatecw', webViewerRotateCw);
    eventBus.off('rotateccw', webViewerRotateCcw);
    eventBus.off('documentproperties', webViewerDocumentProperties);
    eventBus.off('find', webViewerFind);
    eventBus.off('findfromurlhash', webViewerFindFromUrlHash);
    _boundEvents.beforePrint = null;
    _boundEvents.afterPrint = null;
  },
  unbindWindowEvents() {
    let { _boundEvents } = this;
    window.removeEventListener('wheel', webViewerWheel);
    window.removeEventListener('click', webViewerClick);
    window.removeEventListener('keydown', webViewerKeyDown);
    window.removeEventListener('resize', _boundEvents.windowResize);
    window.removeEventListener('hashchange', _boundEvents.windowHashChange);
    window.removeEventListener('beforeprint', _boundEvents.windowBeforePrint);
    window.removeEventListener('afterprint', _boundEvents.windowAfterPrint);
    _boundEvents.windowResize = null;
    _boundEvents.windowHashChange = null;
    _boundEvents.windowBeforePrint = null;
    _boundEvents.windowAfterPrint = null;
  }
};
let validateFileURL;
;
function loadFakeWorker() {
  return new Promise(function (resolve, reject) {
    let script = document.createElement('script');
    script.src = _pdfjsLib.PDFWorker.getWorkerSrc();
    script.onload = function () {
      resolve();
    };
    script.onerror = function () {
      reject(new Error(`Cannot load fake worker at: ${script.src}`));
    };
    (document.head || document.documentElement).appendChild(script);
  });
}
function loadAndEnablePDFBug(enabledTabs) {
  return new Promise(function (resolve, reject) {
    let appConfig = PDFViewerApplication.appConfig;
    let script = document.createElement('script');
    script.src = appConfig.debuggerScriptPath;
    script.onload = function () {
      PDFBug.enable(enabledTabs);
      PDFBug.init({ OPS: _pdfjsLib.OPS }, appConfig.mainContainer);
      resolve();
    };
    script.onerror = function () {
      reject(new Error('Cannot load debugger at ' + script.src));
    };
    (document.getElementsByTagName('head')[0] || document.body).appendChild(script);
  });
}
function webViewerInitialized() {
  let appConfig = PDFViewerApplication.appConfig;
  let file;
  file = window.location.href.split('#')[0];
  appConfig.toolbar.openFile.setAttribute('hidden', 'true');
  appConfig.secondaryToolbar.openFileButton.setAttribute('hidden', 'true');
  if (!PDFViewerApplication.supportsDocumentFonts) {
    _pdfjsLib.PDFJS.disableFontFace = true;
    PDFViewerApplication.l10n.get('web_fonts_disabled', null, 'Web fonts are disabled: unable to use embedded PDF fonts.').then(msg => {
      console.warn(msg);
    });
  }
  if (!PDFViewerApplication.supportsPrinting) {
    appConfig.toolbar.print.classList.add('hidden');
    appConfig.secondaryToolbar.printButton.classList.add('hidden');
  }
  if (!PDFViewerApplication.supportsFullscreen) {
    appConfig.toolbar.presentationModeButton.classList.add('hidden');
    appConfig.secondaryToolbar.presentationModeButton.classList.add('hidden');
  }
  if (PDFViewerApplication.supportsIntegratedFind) {
    appConfig.toolbar.viewFind.classList.add('hidden');
  }
  appConfig.mainContainer.addEventListener('transitionend', function (evt) {
    if (evt.target === this) {
      PDFViewerApplication.eventBus.dispatch('resize', { source: this });
    }
  }, true);
  appConfig.sidebar.toggleButton.addEventListener('click', function () {
    PDFViewerApplication.pdfSidebar.toggle();
  });
  Promise.resolve().then(function () {
    webViewerOpenFileViaURL(file);
  }).catch(function (reason) {
    PDFViewerApplication.l10n.get('loading_error', null, 'An error occurred while loading the PDF.').then(msg => {
      PDFViewerApplication.error(msg, reason);
    });
  });
}
let webViewerOpenFileViaURL;
{
  webViewerOpenFileViaURL = function webViewerOpenFileViaURL(file) {
    PDFViewerApplication.setTitleUsingUrl(file);
    PDFViewerApplication.initPassiveLoading();
  };
}
function webViewerPageRendered(evt) {
  let pageNumber = evt.pageNumber;
  let pageIndex = pageNumber - 1;
  let pageView = PDFViewerApplication.pdfViewer.getPageView(pageIndex);
  if (pageNumber === PDFViewerApplication.page) {
    PDFViewerApplication.toolbar.updateLoadingIndicatorState(false);
  }
  if (!pageView) {
    return;
  }
  if (PDFViewerApplication.pdfSidebar.isThumbnailViewVisible) {
    let thumbnailView = PDFViewerApplication.pdfThumbnailViewer.getThumbnail(pageIndex);
    thumbnailView.setImage(pageView);
  }
  if (typeof Stats !== 'undefined' && Stats.enabled && pageView.stats) {
    Stats.add(pageNumber, pageView.stats);
  }
  if (pageView.error) {
    PDFViewerApplication.l10n.get('rendering_error', null, 'An error occurred while rendering the page.').then(msg => {
      PDFViewerApplication.error(msg, pageView.error);
    });
  }
  PDFViewerApplication.externalServices.reportTelemetry({ type: 'pageInfo' });
  PDFViewerApplication.pdfDocument.getStats().then(function (stats) {
    PDFViewerApplication.externalServices.reportTelemetry({
      type: 'documentStats',
      stats
    });
  });
}
function webViewerTextLayerRendered(evt) {
  if (evt.numTextDivs > 0 && !PDFViewerApplication.supportsDocumentColors) {
    PDFViewerApplication.l10n.get('document_colors_not_allowed', null, 'PDF documents are not allowed to use their own colors: ' + '\'Allow pages to choose their own colors\' ' + 'is deactivated in the browser.').then(msg => {
      console.error(msg);
    });
    PDFViewerApplication.fallback();
  }
}
function webViewerPageMode(evt) {
  let mode = evt.mode,
      view;
  switch (mode) {
    case 'thumbs':
      view = _pdf_sidebar.SidebarView.THUMBS;
      break;
    case 'bookmarks':
    case 'outline':
      view = _pdf_sidebar.SidebarView.OUTLINE;
      break;
    case 'attachments':
      view = _pdf_sidebar.SidebarView.ATTACHMENTS;
      break;
    case 'none':
      view = _pdf_sidebar.SidebarView.NONE;
      break;
    default:
      console.error('Invalid "pagemode" hash parameter: ' + mode);
      return;
  }
  PDFViewerApplication.pdfSidebar.switchView(view, true);
}
function webViewerNamedAction(evt) {
  let action = evt.action;
  switch (action) {
    case 'GoToPage':
      PDFViewerApplication.appConfig.toolbar.pageNumber.select();
      break;
    case 'Find':
      if (!PDFViewerApplication.supportsIntegratedFind) {
        PDFViewerApplication.findBar.toggle();
      }
      break;
  }
}
function webViewerPresentationModeChanged(evt) {
  let { active, switchInProgress } = evt;
  PDFViewerApplication.pdfViewer.presentationModeState = switchInProgress ? _ui_utils.PresentationModeState.CHANGING : active ? _ui_utils.PresentationModeState.FULLSCREEN : _ui_utils.PresentationModeState.NORMAL;
}
function webViewerSidebarViewChanged(evt) {
  PDFViewerApplication.pdfRenderingQueue.isThumbnailViewEnabled = PDFViewerApplication.pdfSidebar.isThumbnailViewVisible;
  let store = PDFViewerApplication.store;
  if (store && PDFViewerApplication.isInitialViewSet) {
    store.set('sidebarView', evt.view).catch(function () {});
  }
}
function webViewerUpdateViewarea(evt) {
  let location = evt.location,
      store = PDFViewerApplication.store;
  if (store && PDFViewerApplication.isInitialViewSet) {
    store.setMultiple({
      'exists': true,
      'page': location.pageNumber,
      'zoom': location.scale,
      'scrollLeft': location.left,
      'scrollTop': location.top,
      'rotation': location.rotation
    }).catch(function () {});
  }
  let href = PDFViewerApplication.pdfLinkService.getAnchorUrl(location.pdfOpenParams);
  PDFViewerApplication.appConfig.toolbar.viewBookmark.href = href;
  PDFViewerApplication.appConfig.secondaryToolbar.viewBookmarkButton.href = href;
  let currentPage = PDFViewerApplication.pdfViewer.getPageView(PDFViewerApplication.page - 1);
  let loading = currentPage.renderingState !== _pdf_rendering_queue.RenderingStates.FINISHED;
  PDFViewerApplication.toolbar.updateLoadingIndicatorState(loading);
}
function webViewerResize() {
  let { pdfDocument, pdfViewer } = PDFViewerApplication;
  if (!pdfDocument) {
    return;
  }
  let currentScaleValue = pdfViewer.currentScaleValue;
  if (currentScaleValue === 'auto' || currentScaleValue === 'page-fit' || currentScaleValue === 'page-width') {
    pdfViewer.currentScaleValue = currentScaleValue;
  }
  pdfViewer.update();
}
function webViewerHashchange(evt) {
  let hash = evt.hash;
  if (!hash) {
    return;
  }
  if (!PDFViewerApplication.isInitialViewSet) {
    PDFViewerApplication.initialBookmark = hash;
  } else if (!PDFViewerApplication.pdfHistory.popStateInProgress) {
    PDFViewerApplication.pdfLinkService.setHash(hash);
  }
}
let webViewerFileInputChange;
;
function webViewerPresentationMode() {
  PDFViewerApplication.requestPresentationMode();
}
function webViewerOpenFile() {}
function webViewerPrint() {
  window.print();
}
function webViewerDownload() {
  PDFViewerApplication.download();
}
function webViewerFirstPage() {
  if (PDFViewerApplication.pdfDocument) {
    PDFViewerApplication.page = 1;
  }
}
function webViewerLastPage() {
  if (PDFViewerApplication.pdfDocument) {
    PDFViewerApplication.page = PDFViewerApplication.pagesCount;
  }
}
function webViewerNextPage() {
  PDFViewerApplication.page++;
}
function webViewerPreviousPage() {
  PDFViewerApplication.page--;
}
function webViewerZoomIn() {
  PDFViewerApplication.zoomIn();
}
function webViewerZoomOut() {
  PDFViewerApplication.zoomOut();
}
function webViewerPageNumberChanged(evt) {
  let pdfViewer = PDFViewerApplication.pdfViewer;
  pdfViewer.currentPageLabel = evt.value;
  if (evt.value !== pdfViewer.currentPageNumber.toString() && evt.value !== pdfViewer.currentPageLabel) {
    PDFViewerApplication.toolbar.setPageNumber(pdfViewer.currentPageNumber, pdfViewer.currentPageLabel);
  }
}
function webViewerScaleChanged(evt) {
  PDFViewerApplication.pdfViewer.currentScaleValue = evt.value;
}
function webViewerRotateCw() {
  PDFViewerApplication.rotatePages(90);
}
function webViewerRotateCcw() {
  PDFViewerApplication.rotatePages(-90);
}
function webViewerDocumentProperties() {
  PDFViewerApplication.pdfDocumentProperties.open();
}
function webViewerFind(evt) {
  PDFViewerApplication.findController.executeCommand('find' + evt.type, {
    query: evt.query,
    phraseSearch: evt.phraseSearch,
    caseSensitive: evt.caseSensitive,
    highlightAll: evt.highlightAll,
    findPrevious: evt.findPrevious
  });
}
function webViewerFindFromUrlHash(evt) {
  PDFViewerApplication.findController.executeCommand('find', {
    query: evt.query,
    phraseSearch: evt.phraseSearch,
    caseSensitive: false,
    highlightAll: true,
    findPrevious: false
  });
}
function webViewerScaleChanging(evt) {
  PDFViewerApplication.toolbar.setPageScale(evt.presetValue, evt.scale);
  PDFViewerApplication.pdfViewer.update();
}
function webViewerRotationChanging(evt) {
  PDFViewerApplication.pdfThumbnailViewer.pagesRotation = evt.pagesRotation;
  PDFViewerApplication.forceRendering();
  PDFViewerApplication.pdfViewer.currentPageNumber = evt.pageNumber;
}
function webViewerPageChanging(evt) {
  let page = evt.pageNumber;
  PDFViewerApplication.toolbar.setPageNumber(page, evt.pageLabel || null);
  PDFViewerApplication.secondaryToolbar.setPageNumber(page);
  if (PDFViewerApplication.pdfSidebar.isThumbnailViewVisible) {
    PDFViewerApplication.pdfThumbnailViewer.scrollThumbnailIntoView(page);
  }
  if (typeof Stats !== 'undefined' && Stats.enabled) {
    let pageView = PDFViewerApplication.pdfViewer.getPageView(page - 1);
    if (pageView && pageView.stats) {
      Stats.add(page, pageView.stats);
    }
  }
}
let zoomDisabled = false,
    zoomDisabledTimeout;
function webViewerWheel(evt) {
  let pdfViewer = PDFViewerApplication.pdfViewer;
  if (pdfViewer.isInPresentationMode) {
    return;
  }
  if (evt.ctrlKey || evt.metaKey) {
    let support = PDFViewerApplication.supportedMouseWheelZoomModifierKeys;
    if (evt.ctrlKey && !support.ctrlKey || evt.metaKey && !support.metaKey) {
      return;
    }
    evt.preventDefault();
    if (zoomDisabled) {
      return;
    }
    let previousScale = pdfViewer.currentScale;
    let delta = (0, _ui_utils.normalizeWheelEventDelta)(evt);
    const MOUSE_WHEEL_DELTA_PER_PAGE_SCALE = 3.0;
    let ticks = delta * MOUSE_WHEEL_DELTA_PER_PAGE_SCALE;
    if (ticks < 0) {
      PDFViewerApplication.zoomOut(-ticks);
    } else {
      PDFViewerApplication.zoomIn(ticks);
    }
    let currentScale = pdfViewer.currentScale;
    if (previousScale !== currentScale) {
      let scaleCorrectionFactor = currentScale / previousScale - 1;
      let rect = pdfViewer.container.getBoundingClientRect();
      let dx = evt.clientX - rect.left;
      let dy = evt.clientY - rect.top;
      pdfViewer.container.scrollLeft += dx * scaleCorrectionFactor;
      pdfViewer.container.scrollTop += dy * scaleCorrectionFactor;
    }
  } else {
    zoomDisabled = true;
    clearTimeout(zoomDisabledTimeout);
    zoomDisabledTimeout = setTimeout(function () {
      zoomDisabled = false;
    }, 1000);
  }
}
function webViewerClick(evt) {
  if (!PDFViewerApplication.secondaryToolbar.isOpen) {
    return;
  }
  let appConfig = PDFViewerApplication.appConfig;
  if (PDFViewerApplication.pdfViewer.containsElement(evt.target) || appConfig.toolbar.container.contains(evt.target) && evt.target !== appConfig.secondaryToolbar.toggleButton) {
    PDFViewerApplication.secondaryToolbar.close();
  }
}
function webViewerKeyDown(evt) {
  if (PDFViewerApplication.overlayManager.active) {
    return;
  }
  let handled = false,
      ensureViewerFocused = false;
  let cmd = (evt.ctrlKey ? 1 : 0) | (evt.altKey ? 2 : 0) | (evt.shiftKey ? 4 : 0) | (evt.metaKey ? 8 : 0);
  let pdfViewer = PDFViewerApplication.pdfViewer;
  let isViewerInPresentationMode = pdfViewer && pdfViewer.isInPresentationMode;
  if (cmd === 1 || cmd === 8 || cmd === 5 || cmd === 12) {
    switch (evt.keyCode) {
      case 70:
        if (!PDFViewerApplication.supportsIntegratedFind) {
          PDFViewerApplication.findBar.open();
          handled = true;
        }
        break;
      case 71:
        if (!PDFViewerApplication.supportsIntegratedFind) {
          let findState = PDFViewerApplication.findController.state;
          if (findState) {
            PDFViewerApplication.findController.executeCommand('findagain', {
              query: findState.query,
              phraseSearch: findState.phraseSearch,
              caseSensitive: findState.caseSensitive,
              highlightAll: findState.highlightAll,
              findPrevious: cmd === 5 || cmd === 12
            });
          }
          handled = true;
        }
        break;
      case 61:
      case 107:
      case 187:
      case 171:
        if (!isViewerInPresentationMode) {
          PDFViewerApplication.zoomIn();
        }
        handled = true;
        break;
      case 173:
      case 109:
      case 189:
        if (!isViewerInPresentationMode) {
          PDFViewerApplication.zoomOut();
        }
        handled = true;
        break;
      case 48:
      case 96:
        if (!isViewerInPresentationMode) {
          setTimeout(function () {
            pdfViewer.currentScaleValue = _ui_utils.DEFAULT_SCALE_VALUE;
          });
          handled = false;
        }
        break;
      case 38:
        if (isViewerInPresentationMode || PDFViewerApplication.page > 1) {
          PDFViewerApplication.page = 1;
          handled = true;
          ensureViewerFocused = true;
        }
        break;
      case 40:
        if (isViewerInPresentationMode || PDFViewerApplication.page < PDFViewerApplication.pagesCount) {
          PDFViewerApplication.page = PDFViewerApplication.pagesCount;
          handled = true;
          ensureViewerFocused = true;
        }
        break;
    }
  }
  if (cmd === 3 || cmd === 10) {
    switch (evt.keyCode) {
      case 80:
        PDFViewerApplication.requestPresentationMode();
        handled = true;
        break;
      case 71:
        PDFViewerApplication.appConfig.toolbar.pageNumber.select();
        handled = true;
        break;
    }
  }
  if (handled) {
    if (ensureViewerFocused && !isViewerInPresentationMode) {
      pdfViewer.focus();
    }
    evt.preventDefault();
    return;
  }
  let curElement = document.activeElement || document.querySelector(':focus');
  let curElementTagName = curElement && curElement.tagName.toUpperCase();
  if (curElementTagName === 'INPUT' || curElementTagName === 'TEXTAREA' || curElementTagName === 'SELECT') {
    if (evt.keyCode !== 27) {
      return;
    }
  }
  if (cmd === 0) {
    switch (evt.keyCode) {
      case 38:
      case 33:
      case 8:
        if (!isViewerInPresentationMode && pdfViewer.currentScaleValue !== 'page-fit') {
          break;
        }
      case 37:
        if (pdfViewer.isHorizontalScrollbarEnabled) {
          break;
        }
      case 75:
      case 80:
        if (PDFViewerApplication.page > 1) {
          PDFViewerApplication.page--;
        }
        handled = true;
        break;
      case 27:
        if (PDFViewerApplication.secondaryToolbar.isOpen) {
          PDFViewerApplication.secondaryToolbar.close();
          handled = true;
        }
        if (!PDFViewerApplication.supportsIntegratedFind && PDFViewerApplication.findBar.opened) {
          PDFViewerApplication.findBar.close();
          handled = true;
        }
        break;
      case 13:
      case 40:
      case 34:
      case 32:
        if (!isViewerInPresentationMode && pdfViewer.currentScaleValue !== 'page-fit') {
          break;
        }
      case 39:
        if (pdfViewer.isHorizontalScrollbarEnabled) {
          break;
        }
      case 74:
      case 78:
        if (PDFViewerApplication.page < PDFViewerApplication.pagesCount) {
          PDFViewerApplication.page++;
        }
        handled = true;
        break;
      case 36:
        if (isViewerInPresentationMode || PDFViewerApplication.page > 1) {
          PDFViewerApplication.page = 1;
          handled = true;
          ensureViewerFocused = true;
        }
        break;
      case 35:
        if (isViewerInPresentationMode || PDFViewerApplication.page < PDFViewerApplication.pagesCount) {
          PDFViewerApplication.page = PDFViewerApplication.pagesCount;
          handled = true;
          ensureViewerFocused = true;
        }
        break;
      case 83:
        PDFViewerApplication.pdfCursorTools.switchTool(_pdf_cursor_tools.CursorTool.SELECT);
        break;
      case 72:
        PDFViewerApplication.pdfCursorTools.switchTool(_pdf_cursor_tools.CursorTool.HAND);
        break;
      case 82:
        PDFViewerApplication.rotatePages(90);
        break;
    }
  }
  if (cmd === 4) {
    switch (evt.keyCode) {
      case 13:
      case 32:
        if (!isViewerInPresentationMode && pdfViewer.currentScaleValue !== 'page-fit') {
          break;
        }
        if (PDFViewerApplication.page > 1) {
          PDFViewerApplication.page--;
        }
        handled = true;
        break;
      case 82:
        PDFViewerApplication.rotatePages(-90);
        break;
    }
  }
  if (!handled && !isViewerInPresentationMode) {
    if (evt.keyCode >= 33 && evt.keyCode <= 40 || evt.keyCode === 32 && curElementTagName !== 'BUTTON') {
      ensureViewerFocused = true;
    }
  }
  if (ensureViewerFocused && !pdfViewer.containsElement(curElement)) {
    pdfViewer.focus();
  }
  if (handled) {
    evt.preventDefault();
  }
}
function apiPageModeToSidebarView(mode) {
  switch (mode) {
    case 'UseNone':
      return _pdf_sidebar.SidebarView.NONE;
    case 'UseThumbs':
      return _pdf_sidebar.SidebarView.THUMBS;
    case 'UseOutlines':
      return _pdf_sidebar.SidebarView.OUTLINE;
    case 'UseAttachments':
      return _pdf_sidebar.SidebarView.ATTACHMENTS;
    case 'UseOC':
  }
  return _pdf_sidebar.SidebarView.NONE;
}
let PDFPrintServiceFactory = {
  instance: {
    supportsPrinting: false,
    createPrintService() {
      throw new Error('Not implemented: createPrintService');
    }
  }
};
exports.PDFViewerApplication = PDFViewerApplication;
exports.DefaultExternalServices = DefaultExternalServices;
exports.PDFPrintServiceFactory = PDFPrintServiceFactory;

/***/ }),
/* 5 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.SimpleLinkService = exports.PDFLinkService = undefined;

var _dom_events = __webpack_require__(2);

var _ui_utils = __webpack_require__(0);

class PDFLinkService {
  constructor({ eventBus, externalLinkTarget = null, externalLinkRel = null } = {}) {
    this.eventBus = eventBus || (0, _dom_events.getGlobalEventBus)();
    this.externalLinkTarget = externalLinkTarget;
    this.externalLinkRel = externalLinkRel;
    this.baseUrl = null;
    this.pdfDocument = null;
    this.pdfViewer = null;
    this.pdfHistory = null;
    this._pagesRefCache = null;
  }
  setDocument(pdfDocument, baseUrl) {
    this.baseUrl = baseUrl;
    this.pdfDocument = pdfDocument;
    this._pagesRefCache = Object.create(null);
  }
  setViewer(pdfViewer) {
    this.pdfViewer = pdfViewer;
  }
  setHistory(pdfHistory) {
    this.pdfHistory = pdfHistory;
  }
  get pagesCount() {
    return this.pdfDocument ? this.pdfDocument.numPages : 0;
  }
  get page() {
    return this.pdfViewer.currentPageNumber;
  }
  set page(value) {
    this.pdfViewer.currentPageNumber = value;
  }
  get rotation() {
    return this.pdfViewer.pagesRotation;
  }
  set rotation(value) {
    this.pdfViewer.pagesRotation = value;
  }
  navigateTo(dest) {
    let goToDestination = ({ namedDest, explicitDest }) => {
      let destRef = explicitDest[0],
          pageNumber;
      if (destRef instanceof Object) {
        pageNumber = this._cachedPageNumber(destRef);
        if (pageNumber === null) {
          this.pdfDocument.getPageIndex(destRef).then(pageIndex => {
            this.cachePageRef(pageIndex + 1, destRef);
            goToDestination({
              namedDest,
              explicitDest
            });
          }).catch(() => {
            console.error(`PDFLinkService.navigateTo: "${destRef}" is not ` + `a valid page reference, for dest="${dest}".`);
          });
          return;
        }
      } else if (Number.isInteger(destRef)) {
        pageNumber = destRef + 1;
      } else {
        console.error(`PDFLinkService.navigateTo: "${destRef}" is not ` + `a valid destination reference, for dest="${dest}".`);
        return;
      }
      if (!pageNumber || pageNumber < 1 || pageNumber > this.pagesCount) {
        console.error(`PDFLinkService.navigateTo: "${pageNumber}" is not ` + `a valid page number, for dest="${dest}".`);
        return;
      }
      if (this.pdfHistory) {
        this.pdfHistory.pushCurrentPosition();
        this.pdfHistory.push({
          namedDest,
          explicitDest,
          pageNumber
        });
      }
      this.pdfViewer.scrollPageIntoView({
        pageNumber,
        destArray: explicitDest
      });
    };
    new Promise((resolve, reject) => {
      if (typeof dest === 'string') {
        this.pdfDocument.getDestination(dest).then(destArray => {
          resolve({
            namedDest: dest,
            explicitDest: destArray
          });
        });
        return;
      }
      resolve({
        namedDest: '',
        explicitDest: dest
      });
    }).then(data => {
      if (!(data.explicitDest instanceof Array)) {
        console.error(`PDFLinkService.navigateTo: "${data.explicitDest}" is` + ` not a valid destination array, for dest="${dest}".`);
        return;
      }
      goToDestination(data);
    });
  }
  getDestinationHash(dest) {
    if (typeof dest === 'string') {
      return this.getAnchorUrl('#' + escape(dest));
    }
    if (dest instanceof Array) {
      let str = JSON.stringify(dest);
      return this.getAnchorUrl('#' + escape(str));
    }
    return this.getAnchorUrl('');
  }
  getAnchorUrl(anchor) {
    return (this.baseUrl || '') + anchor;
  }
  setHash(hash) {
    let pageNumber, dest;
    if (hash.includes('=')) {
      let params = (0, _ui_utils.parseQueryString)(hash);
      if ('search' in params) {
        this.eventBus.dispatch('findfromurlhash', {
          source: this,
          query: params['search'].replace(/"/g, ''),
          phraseSearch: params['phrase'] === 'true'
        });
      }
      if ('nameddest' in params) {
        this.navigateTo(params.nameddest);
        return;
      }
      if ('page' in params) {
        pageNumber = params.page | 0 || 1;
      }
      if ('zoom' in params) {
        let zoomArgs = params.zoom.split(',');
        let zoomArg = zoomArgs[0];
        let zoomArgNumber = parseFloat(zoomArg);
        if (!zoomArg.includes('Fit')) {
          dest = [null, { name: 'XYZ' }, zoomArgs.length > 1 ? zoomArgs[1] | 0 : null, zoomArgs.length > 2 ? zoomArgs[2] | 0 : null, zoomArgNumber ? zoomArgNumber / 100 : zoomArg];
        } else {
          if (zoomArg === 'Fit' || zoomArg === 'FitB') {
            dest = [null, { name: zoomArg }];
          } else if (zoomArg === 'FitH' || zoomArg === 'FitBH' || zoomArg === 'FitV' || zoomArg === 'FitBV') {
            dest = [null, { name: zoomArg }, zoomArgs.length > 1 ? zoomArgs[1] | 0 : null];
          } else if (zoomArg === 'FitR') {
            if (zoomArgs.length !== 5) {
              console.error('PDFLinkService.setHash: Not enough parameters for "FitR".');
            } else {
              dest = [null, { name: zoomArg }, zoomArgs[1] | 0, zoomArgs[2] | 0, zoomArgs[3] | 0, zoomArgs[4] | 0];
            }
          } else {
            console.error(`PDFLinkService.setHash: "${zoomArg}" is not ` + 'a valid zoom value.');
          }
        }
      }
      if (dest) {
        this.pdfViewer.scrollPageIntoView({
          pageNumber: pageNumber || this.page,
          destArray: dest,
          allowNegativeOffset: true
        });
      } else if (pageNumber) {
        this.page = pageNumber;
      }
      if ('pagemode' in params) {
        this.eventBus.dispatch('pagemode', {
          source: this,
          mode: params.pagemode
        });
      }
    } else {
      dest = unescape(hash);
      try {
        dest = JSON.parse(dest);
        if (!(dest instanceof Array)) {
          dest = dest.toString();
        }
      } catch (ex) {}
      if (typeof dest === 'string' || isValidExplicitDestination(dest)) {
        this.navigateTo(dest);
        return;
      }
      console.error(`PDFLinkService.setHash: "${unescape(hash)}" is not ` + 'a valid destination.');
    }
  }
  executeNamedAction(action) {
    switch (action) {
      case 'GoBack':
        if (this.pdfHistory) {
          this.pdfHistory.back();
        }
        break;
      case 'GoForward':
        if (this.pdfHistory) {
          this.pdfHistory.forward();
        }
        break;
      case 'NextPage':
        if (this.page < this.pagesCount) {
          this.page++;
        }
        break;
      case 'PrevPage':
        if (this.page > 1) {
          this.page--;
        }
        break;
      case 'LastPage':
        this.page = this.pagesCount;
        break;
      case 'FirstPage':
        this.page = 1;
        break;
      default:
        break;
    }
    this.eventBus.dispatch('namedaction', {
      source: this,
      action
    });
  }
  onFileAttachmentAnnotation({ id, filename, content }) {
    this.eventBus.dispatch('fileattachmentannotation', {
      source: this,
      id,
      filename,
      content
    });
  }
  cachePageRef(pageNum, pageRef) {
    let refStr = pageRef.num + ' ' + pageRef.gen + ' R';
    this._pagesRefCache[refStr] = pageNum;
  }
  _cachedPageNumber(pageRef) {
    let refStr = pageRef.num + ' ' + pageRef.gen + ' R';
    return this._pagesRefCache && this._pagesRefCache[refStr] || null;
  }
}
function isValidExplicitDestination(dest) {
  if (!(dest instanceof Array)) {
    return false;
  }
  let destLength = dest.length,
      allowNull = true;
  if (destLength < 2) {
    return false;
  }
  let page = dest[0];
  if (!(typeof page === 'object' && Number.isInteger(page.num) && Number.isInteger(page.gen)) && !(Number.isInteger(page) && page >= 0)) {
    return false;
  }
  let zoom = dest[1];
  if (!(typeof zoom === 'object' && typeof zoom.name === 'string')) {
    return false;
  }
  switch (zoom.name) {
    case 'XYZ':
      if (destLength !== 5) {
        return false;
      }
      break;
    case 'Fit':
    case 'FitB':
      return destLength === 2;
    case 'FitH':
    case 'FitBH':
    case 'FitV':
    case 'FitBV':
      if (destLength !== 3) {
        return false;
      }
      break;
    case 'FitR':
      if (destLength !== 6) {
        return false;
      }
      allowNull = false;
      break;
    default:
      return false;
  }
  for (let i = 2; i < destLength; i++) {
    let param = dest[i];
    if (!(typeof param === 'number' || allowNull && param === null)) {
      return false;
    }
  }
  return true;
}
class SimpleLinkService {
  constructor() {
    this.externalLinkTarget = null;
    this.externalLinkRel = null;
  }
  get page() {
    return 0;
  }
  set page(value) {}
  get rotation() {
    return 0;
  }
  set rotation(value) {}
  navigateTo(dest) {}
  getDestinationHash(dest) {
    return '#';
  }
  getAnchorUrl(hash) {
    return '#';
  }
  setHash(hash) {}
  executeNamedAction(action) {}
  onFileAttachmentAnnotation({ id, filename, content }) {}
  cachePageRef(pageNum, pageRef) {}
}
exports.PDFLinkService = PDFLinkService;
exports.SimpleLinkService = SimpleLinkService;

/***/ }),
/* 6 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.PDFCursorTools = exports.CursorTool = undefined;

var _grab_to_pan = __webpack_require__(10);

const CursorTool = {
  SELECT: 0,
  HAND: 1,
  ZOOM: 2
};
class PDFCursorTools {
  constructor({ container, eventBus, preferences }) {
    this.container = container;
    this.eventBus = eventBus;
    this.active = CursorTool.SELECT;
    this.activeBeforePresentationMode = null;
    this.handTool = new _grab_to_pan.GrabToPan({ element: this.container });
    this._addEventListeners();
    preferences.get('cursorToolOnLoad').then(value => {
      this.switchTool(value);
    }).catch(() => {});
  }
  get activeTool() {
    return this.active;
  }
  switchTool(tool) {
    if (this.activeBeforePresentationMode !== null) {
      return;
    }
    if (tool === this.active) {
      return;
    }
    let disableActiveTool = () => {
      switch (this.active) {
        case CursorTool.SELECT:
          break;
        case CursorTool.HAND:
          this.handTool.deactivate();
          break;
        case CursorTool.ZOOM:
      }
    };
    switch (tool) {
      case CursorTool.SELECT:
        disableActiveTool();
        break;
      case CursorTool.HAND:
        disableActiveTool();
        this.handTool.activate();
        break;
      case CursorTool.ZOOM:
      default:
        console.error(`switchTool: "${tool}" is an unsupported value.`);
        return;
    }
    this.active = tool;
    this._dispatchEvent();
  }
  _dispatchEvent() {
    this.eventBus.dispatch('cursortoolchanged', {
      source: this,
      tool: this.active
    });
  }
  _addEventListeners() {
    this.eventBus.on('switchcursortool', evt => {
      this.switchTool(evt.tool);
    });
    this.eventBus.on('presentationmodechanged', evt => {
      if (evt.switchInProgress) {
        return;
      }
      let previouslyActive;
      if (evt.active) {
        previouslyActive = this.active;
        this.switchTool(CursorTool.SELECT);
        this.activeBeforePresentationMode = previouslyActive;
      } else {
        previouslyActive = this.activeBeforePresentationMode;
        this.activeBeforePresentationMode = null;
        this.switchTool(previouslyActive);
      }
    });
  }
}
exports.CursorTool = CursorTool;
exports.PDFCursorTools = PDFCursorTools;

/***/ }),
/* 7 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.PDFFindController = exports.FindState = undefined;

var _pdfjsLib = __webpack_require__(1);

var _ui_utils = __webpack_require__(0);

const FindState = {
  FOUND: 0,
  NOT_FOUND: 1,
  WRAPPED: 2,
  PENDING: 3
};
const FIND_SCROLL_OFFSET_TOP = -50;
const FIND_SCROLL_OFFSET_LEFT = -400;
const FIND_TIMEOUT = 250;
const CHARACTERS_TO_NORMALIZE = {
  '\u2018': '\'',
  '\u2019': '\'',
  '\u201A': '\'',
  '\u201B': '\'',
  '\u201C': '"',
  '\u201D': '"',
  '\u201E': '"',
  '\u201F': '"',
  '\u00BC': '1/4',
  '\u00BD': '1/2',
  '\u00BE': '3/4'
};
class PDFFindController {
  constructor({ pdfViewer }) {
    this.pdfViewer = pdfViewer;
    this.onUpdateResultsCount = null;
    this.onUpdateState = null;
    this.reset();
    let replace = Object.keys(CHARACTERS_TO_NORMALIZE).join('');
    this.normalizationRegex = new RegExp('[' + replace + ']', 'g');
  }
  reset() {
    this.startedTextExtraction = false;
    this.extractTextPromises = [];
    this.pendingFindMatches = Object.create(null);
    this.active = false;
    this.pageContents = [];
    this.pageMatches = [];
    this.pageMatchesLength = null;
    this.matchCount = 0;
    this.selected = {
      pageIdx: -1,
      matchIdx: -1
    };
    this.offset = {
      pageIdx: null,
      matchIdx: null
    };
    this.pagesToSearch = null;
    this.resumePageIdx = null;
    this.state = null;
    this.dirtyMatch = false;
    this.findTimeout = null;
    this._firstPagePromise = new Promise(resolve => {
      this.resolveFirstPage = resolve;
    });
  }
  executeCommand(cmd, state) {
    if (this.state === null || cmd !== 'findagain') {
      this.dirtyMatch = true;
    }
    this.state = state;
    this._updateUIState(FindState.PENDING);
    this._firstPagePromise.then(() => {
      this._extractText();
      clearTimeout(this.findTimeout);
      if (cmd === 'find') {
        this.findTimeout = setTimeout(this._nextMatch.bind(this), FIND_TIMEOUT);
      } else {
        this._nextMatch();
      }
    });
  }
  updateMatchPosition(pageIndex, matchIndex, elements, beginIdx) {
    if (this.selected.matchIdx === matchIndex && this.selected.pageIdx === pageIndex) {
      let spot = {
        top: FIND_SCROLL_OFFSET_TOP,
        left: FIND_SCROLL_OFFSET_LEFT
      };
      (0, _ui_utils.scrollIntoView)(elements[beginIdx], spot, true);
    }
  }
  _normalize(text) {
    return text.replace(this.normalizationRegex, function (ch) {
      return CHARACTERS_TO_NORMALIZE[ch];
    });
  }
  _prepareMatches(matchesWithLength, matches, matchesLength) {
    function isSubTerm(matchesWithLength, currentIndex) {
      let currentElem = matchesWithLength[currentIndex];
      let nextElem = matchesWithLength[currentIndex + 1];
      if (currentIndex < matchesWithLength.length - 1 && currentElem.match === nextElem.match) {
        currentElem.skipped = true;
        return true;
      }
      for (let i = currentIndex - 1; i >= 0; i--) {
        let prevElem = matchesWithLength[i];
        if (prevElem.skipped) {
          continue;
        }
        if (prevElem.match + prevElem.matchLength < currentElem.match) {
          break;
        }
        if (prevElem.match + prevElem.matchLength >= currentElem.match + currentElem.matchLength) {
          currentElem.skipped = true;
          return true;
        }
      }
      return false;
    }
    matchesWithLength.sort(function (a, b) {
      return a.match === b.match ? a.matchLength - b.matchLength : a.match - b.match;
    });
    for (let i = 0, len = matchesWithLength.length; i < len; i++) {
      if (isSubTerm(matchesWithLength, i)) {
        continue;
      }
      matches.push(matchesWithLength[i].match);
      matchesLength.push(matchesWithLength[i].matchLength);
    }
  }
  _calculatePhraseMatch(query, pageIndex, pageContent) {
    let matches = [];
    let queryLen = query.length;
    let matchIdx = -queryLen;
    while (true) {
      matchIdx = pageContent.indexOf(query, matchIdx + queryLen);
      if (matchIdx === -1) {
        break;
      }
      matches.push(matchIdx);
    }
    this.pageMatches[pageIndex] = matches;
  }
  _calculateWordMatch(query, pageIndex, pageContent) {
    let matchesWithLength = [];
    let queryArray = query.match(/\S+/g);
    for (let i = 0, len = queryArray.length; i < len; i++) {
      let subquery = queryArray[i];
      let subqueryLen = subquery.length;
      let matchIdx = -subqueryLen;
      while (true) {
        matchIdx = pageContent.indexOf(subquery, matchIdx + subqueryLen);
        if (matchIdx === -1) {
          break;
        }
        matchesWithLength.push({
          match: matchIdx,
          matchLength: subqueryLen,
          skipped: false
        });
      }
    }
    if (!this.pageMatchesLength) {
      this.pageMatchesLength = [];
    }
    this.pageMatchesLength[pageIndex] = [];
    this.pageMatches[pageIndex] = [];
    this._prepareMatches(matchesWithLength, this.pageMatches[pageIndex], this.pageMatchesLength[pageIndex]);
  }
  _calculateMatch(pageIndex) {
    let pageContent = this._normalize(this.pageContents[pageIndex]);
    let query = this._normalize(this.state.query);
    let caseSensitive = this.state.caseSensitive;
    let phraseSearch = this.state.phraseSearch;
    let queryLen = query.length;
    if (queryLen === 0) {
      return;
    }
    if (!caseSensitive) {
      pageContent = pageContent.toLowerCase();
      query = query.toLowerCase();
    }
    if (phraseSearch) {
      this._calculatePhraseMatch(query, pageIndex, pageContent);
    } else {
      this._calculateWordMatch(query, pageIndex, pageContent);
    }
    this._updatePage(pageIndex);
    if (this.resumePageIdx === pageIndex) {
      this.resumePageIdx = null;
      this._nextPageMatch();
    }
    if (this.pageMatches[pageIndex].length > 0) {
      this.matchCount += this.pageMatches[pageIndex].length;
      this._updateUIResultsCount();
    }
  }
  _extractText() {
    if (this.startedTextExtraction) {
      return;
    }
    this.startedTextExtraction = true;
    this.pageContents.length = 0;
    let promise = Promise.resolve();
    for (let i = 0, ii = this.pdfViewer.pagesCount; i < ii; i++) {
      let extractTextCapability = (0, _pdfjsLib.createPromiseCapability)();
      this.extractTextPromises[i] = extractTextCapability.promise;
      promise = promise.then(() => {
        return this.pdfViewer.getPageTextContent(i).then(textContent => {
          let textItems = textContent.items;
          let strBuf = [];
          for (let j = 0, jj = textItems.length; j < jj; j++) {
            strBuf.push(textItems[j].str);
          }
          this.pageContents[i] = strBuf.join('');
          extractTextCapability.resolve(i);
        }, reason => {
          console.error(`Unable to get page ${i + 1} text content`, reason);
          this.pageContents[i] = '';
          extractTextCapability.resolve(i);
        });
      });
    }
  }
  _updatePage(index) {
    if (this.selected.pageIdx === index) {
      this.pdfViewer.currentPageNumber = index + 1;
    }
    let page = this.pdfViewer.getPageView(index);
    if (page.textLayer) {
      page.textLayer.updateMatches();
    }
  }
  _nextMatch() {
    let previous = this.state.findPrevious;
    let currentPageIndex = this.pdfViewer.currentPageNumber - 1;
    let numPages = this.pdfViewer.pagesCount;
    this.active = true;
    if (this.dirtyMatch) {
      this.dirtyMatch = false;
      this.selected.pageIdx = this.selected.matchIdx = -1;
      this.offset.pageIdx = currentPageIndex;
      this.offset.matchIdx = null;
      this.hadMatch = false;
      this.resumePageIdx = null;
      this.pageMatches = [];
      this.matchCount = 0;
      this.pageMatchesLength = null;
      for (let i = 0; i < numPages; i++) {
        this._updatePage(i);
        if (!(i in this.pendingFindMatches)) {
          this.pendingFindMatches[i] = true;
          this.extractTextPromises[i].then(pageIdx => {
            delete this.pendingFindMatches[pageIdx];
            this._calculateMatch(pageIdx);
          });
        }
      }
    }
    if (this.state.query === '') {
      this._updateUIState(FindState.FOUND);
      return;
    }
    if (this.resumePageIdx) {
      return;
    }
    let offset = this.offset;
    this.pagesToSearch = numPages;
    if (offset.matchIdx !== null) {
      let numPageMatches = this.pageMatches[offset.pageIdx].length;
      if (!previous && offset.matchIdx + 1 < numPageMatches || previous && offset.matchIdx > 0) {
        this.hadMatch = true;
        offset.matchIdx = previous ? offset.matchIdx - 1 : offset.matchIdx + 1;
        this._updateMatch(true);
        return;
      }
      this._advanceOffsetPage(previous);
    }
    this._nextPageMatch();
  }
  _matchesReady(matches) {
    let offset = this.offset;
    let numMatches = matches.length;
    let previous = this.state.findPrevious;
    if (numMatches) {
      this.hadMatch = true;
      offset.matchIdx = previous ? numMatches - 1 : 0;
      this._updateMatch(true);
      return true;
    }
    this._advanceOffsetPage(previous);
    if (offset.wrapped) {
      offset.matchIdx = null;
      if (this.pagesToSearch < 0) {
        this._updateMatch(false);
        return true;
      }
    }
    return false;
  }
  _nextPageMatch() {
    if (this.resumePageIdx !== null) {
      console.error('There can only be one pending page.');
    }
    let matches = null;
    do {
      let pageIdx = this.offset.pageIdx;
      matches = this.pageMatches[pageIdx];
      if (!matches) {
        this.resumePageIdx = pageIdx;
        break;
      }
    } while (!this._matchesReady(matches));
  }
  _advanceOffsetPage(previous) {
    let offset = this.offset;
    let numPages = this.extractTextPromises.length;
    offset.pageIdx = previous ? offset.pageIdx - 1 : offset.pageIdx + 1;
    offset.matchIdx = null;
    this.pagesToSearch--;
    if (offset.pageIdx >= numPages || offset.pageIdx < 0) {
      offset.pageIdx = previous ? numPages - 1 : 0;
      offset.wrapped = true;
    }
  }
  _updateMatch(found = false) {
    let state = FindState.NOT_FOUND;
    let wrapped = this.offset.wrapped;
    this.offset.wrapped = false;
    if (found) {
      let previousPage = this.selected.pageIdx;
      this.selected.pageIdx = this.offset.pageIdx;
      this.selected.matchIdx = this.offset.matchIdx;
      state = wrapped ? FindState.WRAPPED : FindState.FOUND;
      if (previousPage !== -1 && previousPage !== this.selected.pageIdx) {
        this._updatePage(previousPage);
      }
    }
    this._updateUIState(state, this.state.findPrevious);
    if (this.selected.pageIdx !== -1) {
      this._updatePage(this.selected.pageIdx);
    }
  }
  _updateUIResultsCount() {
    if (this.onUpdateResultsCount) {
      this.onUpdateResultsCount(this.matchCount);
    }
  }
  _updateUIState(state, previous) {
    if (this.onUpdateState) {
      this.onUpdateState(state, previous, this.matchCount);
    }
  }
}
exports.FindState = FindState;
exports.PDFFindController = PDFFindController;

/***/ }),
/* 8 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
let compatibilityParams = Object.create(null);
;
const viewerCompatibilityParams = Object.freeze(compatibilityParams);
exports.viewerCompatibilityParams = viewerCompatibilityParams;

/***/ }),
/* 9 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


let DEFAULT_URL = 'compressed.tracemonkey-pldi-09.pdf';
;
let pdfjsWebApp;
{
  pdfjsWebApp = __webpack_require__(4);
}
{
  __webpack_require__(31);
  __webpack_require__(34);
}
;
;
;
function getViewerConfiguration() {
  return {
    appContainer: document.body,
    mainContainer: document.getElementById('viewerContainer'),
    viewerContainer: document.getElementById('viewer'),
    eventBus: null,
    toolbar: {
      container: document.getElementById('toolbarViewer'),
      numPages: document.getElementById('numPages'),
      pageNumber: document.getElementById('pageNumber'),
      scaleSelectContainer: document.getElementById('scaleSelectContainer'),
      scaleSelect: document.getElementById('scaleSelect'),
      customScaleOption: document.getElementById('customScaleOption'),
      previous: document.getElementById('previous'),
      next: document.getElementById('next'),
      zoomIn: document.getElementById('zoomIn'),
      zoomOut: document.getElementById('zoomOut'),
      viewFind: document.getElementById('viewFind'),
      openFile: document.getElementById('openFile'),
      print: document.getElementById('print'),
      presentationModeButton: document.getElementById('presentationMode'),
      download: document.getElementById('download'),
      viewBookmark: document.getElementById('viewBookmark')
    },
    secondaryToolbar: {
      toolbar: document.getElementById('secondaryToolbar'),
      toggleButton: document.getElementById('secondaryToolbarToggle'),
      toolbarButtonContainer: document.getElementById('secondaryToolbarButtonContainer'),
      presentationModeButton: document.getElementById('secondaryPresentationMode'),
      openFileButton: document.getElementById('secondaryOpenFile'),
      printButton: document.getElementById('secondaryPrint'),
      downloadButton: document.getElementById('secondaryDownload'),
      viewBookmarkButton: document.getElementById('secondaryViewBookmark'),
      firstPageButton: document.getElementById('firstPage'),
      lastPageButton: document.getElementById('lastPage'),
      pageRotateCwButton: document.getElementById('pageRotateCw'),
      pageRotateCcwButton: document.getElementById('pageRotateCcw'),
      cursorSelectToolButton: document.getElementById('cursorSelectTool'),
      cursorHandToolButton: document.getElementById('cursorHandTool'),
      documentPropertiesButton: document.getElementById('documentProperties')
    },
    fullscreen: {
      contextFirstPage: document.getElementById('contextFirstPage'),
      contextLastPage: document.getElementById('contextLastPage'),
      contextPageRotateCw: document.getElementById('contextPageRotateCw'),
      contextPageRotateCcw: document.getElementById('contextPageRotateCcw')
    },
    sidebar: {
      outerContainer: document.getElementById('outerContainer'),
      viewerContainer: document.getElementById('viewerContainer'),
      toggleButton: document.getElementById('sidebarToggle'),
      thumbnailButton: document.getElementById('viewThumbnail'),
      outlineButton: document.getElementById('viewOutline'),
      attachmentsButton: document.getElementById('viewAttachments'),
      thumbnailView: document.getElementById('thumbnailView'),
      outlineView: document.getElementById('outlineView'),
      attachmentsView: document.getElementById('attachmentsView')
    },
    sidebarResizer: {
      outerContainer: document.getElementById('outerContainer'),
      resizer: document.getElementById('sidebarResizer')
    },
    findBar: {
      bar: document.getElementById('findbar'),
      toggleButton: document.getElementById('viewFind'),
      findField: document.getElementById('findInput'),
      highlightAllCheckbox: document.getElementById('findHighlightAll'),
      caseSensitiveCheckbox: document.getElementById('findMatchCase'),
      findMsg: document.getElementById('findMsg'),
      findResultsCount: document.getElementById('findResultsCount'),
      findStatusIcon: document.getElementById('findStatusIcon'),
      findPreviousButton: document.getElementById('findPrevious'),
      findNextButton: document.getElementById('findNext')
    },
    passwordOverlay: {
      overlayName: 'passwordOverlay',
      container: document.getElementById('passwordOverlay'),
      label: document.getElementById('passwordText'),
      input: document.getElementById('password'),
      submitButton: document.getElementById('passwordSubmit'),
      cancelButton: document.getElementById('passwordCancel')
    },
    documentProperties: {
      overlayName: 'documentPropertiesOverlay',
      container: document.getElementById('documentPropertiesOverlay'),
      closeButton: document.getElementById('documentPropertiesClose'),
      fields: {
        'fileName': document.getElementById('fileNameField'),
        'fileSize': document.getElementById('fileSizeField'),
        'title': document.getElementById('titleField'),
        'author': document.getElementById('authorField'),
        'subject': document.getElementById('subjectField'),
        'keywords': document.getElementById('keywordsField'),
        'creationDate': document.getElementById('creationDateField'),
        'modificationDate': document.getElementById('modificationDateField'),
        'creator': document.getElementById('creatorField'),
        'producer': document.getElementById('producerField'),
        'version': document.getElementById('versionField'),
        'pageCount': document.getElementById('pageCountField')
      }
    },
    errorWrapper: {
      container: document.getElementById('errorWrapper'),
      errorMessage: document.getElementById('errorMessage'),
      closeButton: document.getElementById('errorClose'),
      errorMoreInfo: document.getElementById('errorMoreInfo'),
      moreInfoButton: document.getElementById('errorShowMore'),
      lessInfoButton: document.getElementById('errorShowLess')
    },
    printContainer: document.getElementById('printContainer'),
    openFileInputName: 'fileInput',
    debuggerScriptPath: './debugger.js',
    defaultUrl: DEFAULT_URL
  };
}
function webViewerLoad() {
  let config = getViewerConfiguration();
  window.PDFViewerApplication = pdfjsWebApp.PDFViewerApplication;
  pdfjsWebApp.PDFViewerApplication.run(config);
}
if (document.readyState === 'interactive' || document.readyState === 'complete') {
  webViewerLoad();
} else {
  document.addEventListener('DOMContentLoaded', webViewerLoad, true);
}

/***/ }),
/* 10 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
function GrabToPan(options) {
  this.element = options.element;
  this.document = options.element.ownerDocument;
  if (typeof options.ignoreTarget === 'function') {
    this.ignoreTarget = options.ignoreTarget;
  }
  this.onActiveChanged = options.onActiveChanged;
  this.activate = this.activate.bind(this);
  this.deactivate = this.deactivate.bind(this);
  this.toggle = this.toggle.bind(this);
  this._onmousedown = this._onmousedown.bind(this);
  this._onmousemove = this._onmousemove.bind(this);
  this._endPan = this._endPan.bind(this);
  var overlay = this.overlay = document.createElement('div');
  overlay.className = 'grab-to-pan-grabbing';
}
GrabToPan.prototype = {
  CSS_CLASS_GRAB: 'grab-to-pan-grab',
  activate: function GrabToPan_activate() {
    if (!this.active) {
      this.active = true;
      this.element.addEventListener('mousedown', this._onmousedown, true);
      this.element.classList.add(this.CSS_CLASS_GRAB);
      if (this.onActiveChanged) {
        this.onActiveChanged(true);
      }
    }
  },
  deactivate: function GrabToPan_deactivate() {
    if (this.active) {
      this.active = false;
      this.element.removeEventListener('mousedown', this._onmousedown, true);
      this._endPan();
      this.element.classList.remove(this.CSS_CLASS_GRAB);
      if (this.onActiveChanged) {
        this.onActiveChanged(false);
      }
    }
  },
  toggle: function GrabToPan_toggle() {
    if (this.active) {
      this.deactivate();
    } else {
      this.activate();
    }
  },
  ignoreTarget: function GrabToPan_ignoreTarget(node) {
    return node[matchesSelector]('a[href], a[href] *, input, textarea, button, button *, select, option');
  },
  _onmousedown: function GrabToPan__onmousedown(event) {
    if (event.button !== 0 || this.ignoreTarget(event.target)) {
      return;
    }
    if (event.originalTarget) {
      try {
        event.originalTarget.tagName;
      } catch (e) {
        return;
      }
    }
    this.scrollLeftStart = this.element.scrollLeft;
    this.scrollTopStart = this.element.scrollTop;
    this.clientXStart = event.clientX;
    this.clientYStart = event.clientY;
    this.document.addEventListener('mousemove', this._onmousemove, true);
    this.document.addEventListener('mouseup', this._endPan, true);
    this.element.addEventListener('scroll', this._endPan, true);
    event.preventDefault();
    event.stopPropagation();
    var focusedElement = document.activeElement;
    if (focusedElement && !focusedElement.contains(event.target)) {
      focusedElement.blur();
    }
  },
  _onmousemove: function GrabToPan__onmousemove(event) {
    this.element.removeEventListener('scroll', this._endPan, true);
    if (isLeftMouseReleased(event)) {
      this._endPan();
      return;
    }
    var xDiff = event.clientX - this.clientXStart;
    var yDiff = event.clientY - this.clientYStart;
    var scrollTop = this.scrollTopStart - yDiff;
    var scrollLeft = this.scrollLeftStart - xDiff;
    if (this.element.scrollTo) {
      this.element.scrollTo({
        top: scrollTop,
        left: scrollLeft,
        behavior: 'instant'
      });
    } else {
      this.element.scrollTop = scrollTop;
      this.element.scrollLeft = scrollLeft;
    }
    if (!this.overlay.parentNode) {
      document.body.appendChild(this.overlay);
    }
  },
  _endPan: function GrabToPan__endPan() {
    this.element.removeEventListener('scroll', this._endPan, true);
    this.document.removeEventListener('mousemove', this._onmousemove, true);
    this.document.removeEventListener('mouseup', this._endPan, true);
    this.overlay.remove();
  }
};
var matchesSelector;
['webkitM', 'mozM', 'msM', 'oM', 'm'].some(function (prefix) {
  var name = prefix + 'atches';
  if (name in document.documentElement) {
    matchesSelector = name;
  }
  name += 'Selector';
  if (name in document.documentElement) {
    matchesSelector = name;
  }
  return matchesSelector;
});
var isNotIEorIsIE10plus = !document.documentMode || document.documentMode > 9;
var chrome = window.chrome;
var isChrome15OrOpera15plus = chrome && (chrome.webstore || chrome.app);
var isSafari6plus = /Apple/.test(navigator.vendor) && /Version\/([6-9]\d*|[1-5]\d+)/.test(navigator.userAgent);
function isLeftMouseReleased(event) {
  if ('buttons' in event && isNotIEorIsIE10plus) {
    return !(event.buttons & 1);
  }
  if (isChrome15OrOpera15plus || isSafari6plus) {
    return event.which === 0;
  }
}
exports.GrabToPan = GrabToPan;

/***/ }),
/* 11 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.PDFSidebar = exports.SidebarView = undefined;

var _ui_utils = __webpack_require__(0);

var _pdf_rendering_queue = __webpack_require__(3);

const UI_NOTIFICATION_CLASS = 'pdfSidebarNotification';
const SidebarView = {
  NONE: 0,
  THUMBS: 1,
  OUTLINE: 2,
  ATTACHMENTS: 3
};
class PDFSidebar {
  constructor(options, l10n = _ui_utils.NullL10n) {
    this.isOpen = false;
    this.active = SidebarView.THUMBS;
    this.isInitialViewSet = false;
    this.onToggled = null;
    this.pdfViewer = options.pdfViewer;
    this.pdfThumbnailViewer = options.pdfThumbnailViewer;
    this.pdfOutlineViewer = options.pdfOutlineViewer;
    this.outerContainer = options.outerContainer;
    this.viewerContainer = options.viewerContainer;
    this.eventBus = options.eventBus;
    this.toggleButton = options.toggleButton;
    this.thumbnailButton = options.thumbnailButton;
    this.outlineButton = options.outlineButton;
    this.attachmentsButton = options.attachmentsButton;
    this.thumbnailView = options.thumbnailView;
    this.outlineView = options.outlineView;
    this.attachmentsView = options.attachmentsView;
    this.disableNotification = options.disableNotification || false;
    this.l10n = l10n;
    this._addEventListeners();
  }
  reset() {
    this.isInitialViewSet = false;
    this._hideUINotification(null);
    this.switchView(SidebarView.THUMBS);
    this.outlineButton.disabled = false;
    this.attachmentsButton.disabled = false;
  }
  get visibleView() {
    return this.isOpen ? this.active : SidebarView.NONE;
  }
  get isThumbnailViewVisible() {
    return this.isOpen && this.active === SidebarView.THUMBS;
  }
  get isOutlineViewVisible() {
    return this.isOpen && this.active === SidebarView.OUTLINE;
  }
  get isAttachmentsViewVisible() {
    return this.isOpen && this.active === SidebarView.ATTACHMENTS;
  }
  setInitialView(view = SidebarView.NONE) {
    if (this.isInitialViewSet) {
      return;
    }
    this.isInitialViewSet = true;
    if (this.isOpen && view === SidebarView.NONE) {
      this._dispatchEvent();
      return;
    }
    let isViewPreserved = view === this.visibleView;
    this.switchView(view, true);
    if (isViewPreserved) {
      this._dispatchEvent();
    }
  }
  switchView(view, forceOpen = false) {
    if (view === SidebarView.NONE) {
      this.close();
      return;
    }
    let isViewChanged = view !== this.active;
    let shouldForceRendering = false;
    switch (view) {
      case SidebarView.THUMBS:
        this.thumbnailButton.classList.add('toggled');
        this.outlineButton.classList.remove('toggled');
        this.attachmentsButton.classList.remove('toggled');
        this.thumbnailView.classList.remove('hidden');
        this.outlineView.classList.add('hidden');
        this.attachmentsView.classList.add('hidden');
        if (this.isOpen && isViewChanged) {
          this._updateThumbnailViewer();
          shouldForceRendering = true;
        }
        break;
      case SidebarView.OUTLINE:
        if (this.outlineButton.disabled) {
          return;
        }
        this.thumbnailButton.classList.remove('toggled');
        this.outlineButton.classList.add('toggled');
        this.attachmentsButton.classList.remove('toggled');
        this.thumbnailView.classList.add('hidden');
        this.outlineView.classList.remove('hidden');
        this.attachmentsView.classList.add('hidden');
        break;
      case SidebarView.ATTACHMENTS:
        if (this.attachmentsButton.disabled) {
          return;
        }
        this.thumbnailButton.classList.remove('toggled');
        this.outlineButton.classList.remove('toggled');
        this.attachmentsButton.classList.add('toggled');
        this.thumbnailView.classList.add('hidden');
        this.outlineView.classList.add('hidden');
        this.attachmentsView.classList.remove('hidden');
        break;
      default:
        console.error('PDFSidebar_switchView: "' + view + '" is an unsupported value.');
        return;
    }
    this.active = view | 0;
    if (forceOpen && !this.isOpen) {
      this.open();
      return;
    }
    if (shouldForceRendering) {
      this._forceRendering();
    }
    if (isViewChanged) {
      this._dispatchEvent();
    }
    this._hideUINotification(this.active);
  }
  open() {
    if (this.isOpen) {
      return;
    }
    this.isOpen = true;
    this.toggleButton.classList.add('toggled');
    this.outerContainer.classList.add('sidebarMoving');
    this.outerContainer.classList.add('sidebarOpen');
    if (this.active === SidebarView.THUMBS) {
      this._updateThumbnailViewer();
    }
    this._forceRendering();
    this._dispatchEvent();
    this._hideUINotification(this.active);
  }
  close() {
    if (!this.isOpen) {
      return;
    }
    this.isOpen = false;
    this.toggleButton.classList.remove('toggled');
    this.outerContainer.classList.add('sidebarMoving');
    this.outerContainer.classList.remove('sidebarOpen');
    this._forceRendering();
    this._dispatchEvent();
  }
  toggle() {
    if (this.isOpen) {
      this.close();
    } else {
      this.open();
    }
  }
  _dispatchEvent() {
    this.eventBus.dispatch('sidebarviewchanged', {
      source: this,
      view: this.visibleView
    });
  }
  _forceRendering() {
    if (this.onToggled) {
      this.onToggled();
    } else {
      this.pdfViewer.forceRendering();
      this.pdfThumbnailViewer.forceRendering();
    }
  }
  _updateThumbnailViewer() {
    let { pdfViewer, pdfThumbnailViewer } = this;
    let pagesCount = pdfViewer.pagesCount;
    for (let pageIndex = 0; pageIndex < pagesCount; pageIndex++) {
      let pageView = pdfViewer.getPageView(pageIndex);
      if (pageView && pageView.renderingState === _pdf_rendering_queue.RenderingStates.FINISHED) {
        let thumbnailView = pdfThumbnailViewer.getThumbnail(pageIndex);
        thumbnailView.setImage(pageView);
      }
    }
    pdfThumbnailViewer.scrollThumbnailIntoView(pdfViewer.currentPageNumber);
  }
  _showUINotification(view) {
    if (this.disableNotification) {
      return;
    }
    this.l10n.get('toggle_sidebar_notification.title', null, 'Toggle Sidebar (document contains outline/attachments)').then(msg => {
      this.toggleButton.title = msg;
    });
    if (!this.isOpen) {
      this.toggleButton.classList.add(UI_NOTIFICATION_CLASS);
    } else if (view === this.active) {
      return;
    }
    switch (view) {
      case SidebarView.OUTLINE:
        this.outlineButton.classList.add(UI_NOTIFICATION_CLASS);
        break;
      case SidebarView.ATTACHMENTS:
        this.attachmentsButton.classList.add(UI_NOTIFICATION_CLASS);
        break;
    }
  }
  _hideUINotification(view) {
    if (this.disableNotification) {
      return;
    }
    let removeNotification = view => {
      switch (view) {
        case SidebarView.OUTLINE:
          this.outlineButton.classList.remove(UI_NOTIFICATION_CLASS);
          break;
        case SidebarView.ATTACHMENTS:
          this.attachmentsButton.classList.remove(UI_NOTIFICATION_CLASS);
          break;
      }
    };
    if (!this.isOpen && view !== null) {
      return;
    }
    this.toggleButton.classList.remove(UI_NOTIFICATION_CLASS);
    if (view !== null) {
      removeNotification(view);
      return;
    }
    for (view in SidebarView) {
      removeNotification(SidebarView[view]);
    }
    this.l10n.get('toggle_sidebar.title', null, 'Toggle Sidebar').then(msg => {
      this.toggleButton.title = msg;
    });
  }
  _addEventListeners() {
    this.viewerContainer.addEventListener('transitionend', evt => {
      if (evt.target === this.viewerContainer) {
        this.outerContainer.classList.remove('sidebarMoving');
      }
    });
    this.thumbnailButton.addEventListener('click', () => {
      this.switchView(SidebarView.THUMBS);
    });
    this.outlineButton.addEventListener('click', () => {
      this.switchView(SidebarView.OUTLINE);
    });
    this.outlineButton.addEventListener('dblclick', () => {
      this.pdfOutlineViewer.toggleOutlineTree();
    });
    this.attachmentsButton.addEventListener('click', () => {
      this.switchView(SidebarView.ATTACHMENTS);
    });
    this.eventBus.on('outlineloaded', evt => {
      let outlineCount = evt.outlineCount;
      this.outlineButton.disabled = !outlineCount;
      if (outlineCount) {
        this._showUINotification(SidebarView.OUTLINE);
      } else if (this.active === SidebarView.OUTLINE) {
        this.switchView(SidebarView.THUMBS);
      }
    });
    this.eventBus.on('attachmentsloaded', evt => {
      if (evt.attachmentsCount) {
        this.attachmentsButton.disabled = false;
        this._showUINotification(SidebarView.ATTACHMENTS);
        return;
      }
      Promise.resolve().then(() => {
        if (this.attachmentsView.hasChildNodes()) {
          return;
        }
        this.attachmentsButton.disabled = true;
        if (this.active === SidebarView.ATTACHMENTS) {
          this.switchView(SidebarView.THUMBS);
        }
      });
    });
    this.eventBus.on('presentationmodechanged', evt => {
      if (!evt.active && !evt.switchInProgress && this.isThumbnailViewVisible) {
        this._updateThumbnailViewer();
      }
    });
  }
}
exports.SidebarView = SidebarView;
exports.PDFSidebar = PDFSidebar;

/***/ }),
/* 12 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
class OverlayManager {
  constructor() {
    this._overlays = {};
    this._active = null;
    this._keyDownBound = this._keyDown.bind(this);
  }
  get active() {
    return this._active;
  }
  register(name, element, callerCloseMethod = null, canForceClose = false) {
    return new Promise(resolve => {
      let container;
      if (!name || !element || !(container = element.parentNode)) {
        throw new Error('Not enough parameters.');
      } else if (this._overlays[name]) {
        throw new Error('The overlay is already registered.');
      }
      this._overlays[name] = {
        element,
        container,
        callerCloseMethod,
        canForceClose
      };
      resolve();
    });
  }
  unregister(name) {
    return new Promise(resolve => {
      if (!this._overlays[name]) {
        throw new Error('The overlay does not exist.');
      } else if (this._active === name) {
        throw new Error('The overlay cannot be removed while it is active.');
      }
      delete this._overlays[name];
      resolve();
    });
  }
  open(name) {
    return new Promise(resolve => {
      if (!this._overlays[name]) {
        throw new Error('The overlay does not exist.');
      } else if (this._active) {
        if (this._overlays[name].canForceClose) {
          this._closeThroughCaller();
        } else if (this._active === name) {
          throw new Error('The overlay is already active.');
        } else {
          throw new Error('Another overlay is currently active.');
        }
      }
      this._active = name;
      this._overlays[this._active].element.classList.remove('hidden');
      this._overlays[this._active].container.classList.remove('hidden');
      window.addEventListener('keydown', this._keyDownBound);
      resolve();
    });
  }
  close(name) {
    return new Promise(resolve => {
      if (!this._overlays[name]) {
        throw new Error('The overlay does not exist.');
      } else if (!this._active) {
        throw new Error('The overlay is currently not active.');
      } else if (this._active !== name) {
        throw new Error('Another overlay is currently active.');
      }
      this._overlays[this._active].container.classList.add('hidden');
      this._overlays[this._active].element.classList.add('hidden');
      this._active = null;
      window.removeEventListener('keydown', this._keyDownBound);
      resolve();
    });
  }
  _keyDown(evt) {
    if (this._active && evt.keyCode === 27) {
      this._closeThroughCaller();
      evt.preventDefault();
    }
  }
  _closeThroughCaller() {
    if (this._overlays[this._active].callerCloseMethod) {
      this._overlays[this._active].callerCloseMethod();
    }
    if (this._active) {
      this.close(this._active);
    }
  }
}
exports.OverlayManager = OverlayManager;

/***/ }),
/* 13 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.PasswordPrompt = undefined;

var _ui_utils = __webpack_require__(0);

var _pdfjsLib = __webpack_require__(1);

class PasswordPrompt {
  constructor(options, overlayManager, l10n = _ui_utils.NullL10n) {
    this.overlayName = options.overlayName;
    this.container = options.container;
    this.label = options.label;
    this.input = options.input;
    this.submitButton = options.submitButton;
    this.cancelButton = options.cancelButton;
    this.overlayManager = overlayManager;
    this.l10n = l10n;
    this.updateCallback = null;
    this.reason = null;
    this.submitButton.addEventListener('click', this.verify.bind(this));
    this.cancelButton.addEventListener('click', this.close.bind(this));
    this.input.addEventListener('keydown', e => {
      if (e.keyCode === 13) {
        this.verify();
      }
    });
    this.overlayManager.register(this.overlayName, this.container, this.close.bind(this), true);
  }
  open() {
    this.overlayManager.open(this.overlayName).then(() => {
      this.input.focus();
      let promptString;
      if (this.reason === _pdfjsLib.PasswordResponses.INCORRECT_PASSWORD) {
        promptString = this.l10n.get('password_invalid', null, 'Invalid password. Please try again.');
      } else {
        promptString = this.l10n.get('password_label', null, 'Enter the password to open this PDF file.');
      }
      promptString.then(msg => {
        this.label.textContent = msg;
      });
    });
  }
  close() {
    this.overlayManager.close(this.overlayName).then(() => {
      this.input.value = '';
    });
  }
  verify() {
    let password = this.input.value;
    if (password && password.length > 0) {
      this.close();
      return this.updateCallback(password);
    }
  }
  setUpdateCallback(updateCallback, reason) {
    this.updateCallback = updateCallback;
    this.reason = reason;
  }
}
exports.PasswordPrompt = PasswordPrompt;

/***/ }),
/* 14 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.PDFAttachmentViewer = undefined;

var _pdfjsLib = __webpack_require__(1);

class PDFAttachmentViewer {
  constructor({ container, eventBus, downloadManager }) {
    this.container = container;
    this.eventBus = eventBus;
    this.downloadManager = downloadManager;
    this.reset();
    this.eventBus.on('fileattachmentannotation', this._appendAttachment.bind(this));
  }
  reset(keepRenderedCapability = false) {
    this.attachments = null;
    this.container.textContent = '';
    if (!keepRenderedCapability) {
      this._renderedCapability = (0, _pdfjsLib.createPromiseCapability)();
    }
  }
  _dispatchEvent(attachmentsCount) {
    this._renderedCapability.resolve();
    this.eventBus.dispatch('attachmentsloaded', {
      source: this,
      attachmentsCount
    });
  }
  _bindPdfLink(button, content, filename) {
    if (_pdfjsLib.PDFJS.disableCreateObjectURL) {
      throw new Error('bindPdfLink: ' + 'Unsupported "PDFJS.disableCreateObjectURL" value.');
    }
    let blobUrl;
    button.onclick = function () {
      if (!blobUrl) {
        blobUrl = (0, _pdfjsLib.createObjectURL)(content, 'application/pdf');
      }
      let viewerUrl;
      viewerUrl = blobUrl + '?' + encodeURIComponent(filename);
      window.open(viewerUrl);
      return false;
    };
  }
  _bindLink(button, content, filename) {
    button.onclick = () => {
      this.downloadManager.downloadData(content, filename, '');
      return false;
    };
  }
  render({ attachments, keepRenderedCapability = false }) {
    let attachmentsCount = 0;
    if (this.attachments) {
      this.reset(keepRenderedCapability === true);
    }
    this.attachments = attachments || null;
    if (!attachments) {
      this._dispatchEvent(attachmentsCount);
      return;
    }
    let names = Object.keys(attachments).sort(function (a, b) {
      return a.toLowerCase().localeCompare(b.toLowerCase());
    });
    attachmentsCount = names.length;
    for (let i = 0; i < attachmentsCount; i++) {
      let item = attachments[names[i]];
      let filename = (0, _pdfjsLib.removeNullCharacters)((0, _pdfjsLib.getFilenameFromUrl)(item.filename));
      let div = document.createElement('div');
      div.className = 'attachmentsItem';
      let button = document.createElement('button');
      button.textContent = filename;
      if (/\.pdf$/i.test(filename) && !_pdfjsLib.PDFJS.disableCreateObjectURL) {
        this._bindPdfLink(button, item.content, filename);
      } else {
        this._bindLink(button, item.content, filename);
      }
      div.appendChild(button);
      this.container.appendChild(div);
    }
    this._dispatchEvent(attachmentsCount);
  }
  _appendAttachment({ id, filename, content }) {
    this._renderedCapability.promise.then(() => {
      let attachments = this.attachments;
      if (!attachments) {
        attachments = Object.create(null);
      } else {
        for (let name in attachments) {
          if (id === name) {
            return;
          }
        }
      }
      attachments[id] = {
        filename,
        content
      };
      this.render({
        attachments,
        keepRenderedCapability: true
      });
    });
  }
}
exports.PDFAttachmentViewer = PDFAttachmentViewer;

/***/ }),
/* 15 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.PDFDocumentProperties = undefined;

var _ui_utils = __webpack_require__(0);

var _pdfjsLib = __webpack_require__(1);

const DEFAULT_FIELD_CONTENT = '-';
class PDFDocumentProperties {
  constructor({ overlayName, fields, container, closeButton }, overlayManager, l10n = _ui_utils.NullL10n) {
    this.overlayName = overlayName;
    this.fields = fields;
    this.container = container;
    this.overlayManager = overlayManager;
    this.l10n = l10n;
    this._reset();
    if (closeButton) {
      closeButton.addEventListener('click', this.close.bind(this));
    }
    this.overlayManager.register(this.overlayName, this.container, this.close.bind(this));
  }
  open() {
    let freezeFieldData = data => {
      Object.defineProperty(this, 'fieldData', {
        value: Object.freeze(data),
        writable: false,
        enumerable: true,
        configurable: true
      });
    };
    Promise.all([this.overlayManager.open(this.overlayName), this._dataAvailableCapability.promise]).then(() => {
      if (this.fieldData) {
        this._updateUI();
        return;
      }
      this.pdfDocument.getMetadata().then(({ info, metadata, contentDispositionFilename }) => {
        return Promise.all([info, metadata, contentDispositionFilename || (0, _ui_utils.getPDFFileNameFromURL)(this.url), this._parseFileSize(this.maybeFileSize), this._parseDate(info.CreationDate), this._parseDate(info.ModDate)]);
      }).then(([info, metadata, fileName, fileSize, creationDate, modDate]) => {
        freezeFieldData({
          'fileName': fileName,
          'fileSize': fileSize,
          'title': info.Title,
          'author': info.Author,
          'subject': info.Subject,
          'keywords': info.Keywords,
          'creationDate': creationDate,
          'modificationDate': modDate,
          'creator': info.Creator,
          'producer': info.Producer,
          'version': info.PDFFormatVersion,
          'pageCount': this.pdfDocument.numPages
        });
        this._updateUI();
        return this.pdfDocument.getDownloadInfo();
      }).then(({ length }) => {
        return this._parseFileSize(length);
      }).then(fileSize => {
        let data = (0, _ui_utils.cloneObj)(this.fieldData);
        data['fileSize'] = fileSize;
        freezeFieldData(data);
        this._updateUI();
      });
    });
  }
  close() {
    this.overlayManager.close(this.overlayName);
  }
  setDocument(pdfDocument, url) {
    if (this.pdfDocument) {
      this._reset();
      this._updateUI(true);
    }
    if (!pdfDocument) {
      return;
    }
    this.pdfDocument = pdfDocument;
    this.url = url;
    this._dataAvailableCapability.resolve();
  }
  setFileSize(fileSize) {
    if (typeof fileSize === 'number' && fileSize > 0) {
      this.maybeFileSize = fileSize;
    }
  }
  _reset() {
    this.pdfDocument = null;
    this.url = null;
    this.maybeFileSize = 0;
    delete this.fieldData;
    this._dataAvailableCapability = (0, _pdfjsLib.createPromiseCapability)();
  }
  _updateUI(reset = false) {
    if (reset || !this.fieldData) {
      for (let id in this.fields) {
        this.fields[id].textContent = DEFAULT_FIELD_CONTENT;
      }
      return;
    }
    if (this.overlayManager.active !== this.overlayName) {
      return;
    }
    for (let id in this.fields) {
      let content = this.fieldData[id];
      this.fields[id].textContent = content || content === 0 ? content : DEFAULT_FIELD_CONTENT;
    }
  }
  _parseFileSize(fileSize = 0) {
    let kb = fileSize / 1024;
    if (!kb) {
      return Promise.resolve(undefined);
    } else if (kb < 1024) {
      return this.l10n.get('document_properties_kb', {
        size_kb: (+kb.toPrecision(3)).toLocaleString(),
        size_b: fileSize.toLocaleString()
      }, '{{size_kb}} KB ({{size_b}} bytes)');
    }
    return this.l10n.get('document_properties_mb', {
      size_mb: (+(kb / 1024).toPrecision(3)).toLocaleString(),
      size_b: fileSize.toLocaleString()
    }, '{{size_mb}} MB ({{size_b}} bytes)');
  }
  _parseDate(inputDate) {
    if (!inputDate) {
      return;
    }
    let dateToParse = inputDate;
    if (dateToParse.substring(0, 2) === 'D:') {
      dateToParse = dateToParse.substring(2);
    }
    let year = parseInt(dateToParse.substring(0, 4), 10);
    let month = parseInt(dateToParse.substring(4, 6), 10) - 1;
    let day = parseInt(dateToParse.substring(6, 8), 10);
    let hours = parseInt(dateToParse.substring(8, 10), 10);
    let minutes = parseInt(dateToParse.substring(10, 12), 10);
    let seconds = parseInt(dateToParse.substring(12, 14), 10);
    let utRel = dateToParse.substring(14, 15);
    let offsetHours = parseInt(dateToParse.substring(15, 17), 10);
    let offsetMinutes = parseInt(dateToParse.substring(18, 20), 10);
    if (utRel === '-') {
      hours += offsetHours;
      minutes += offsetMinutes;
    } else if (utRel === '+') {
      hours -= offsetHours;
      minutes -= offsetMinutes;
    }
    let date = new Date(Date.UTC(year, month, day, hours, minutes, seconds));
    let dateString = date.toLocaleDateString();
    let timeString = date.toLocaleTimeString();
    return this.l10n.get('document_properties_date_string', {
      date: dateString,
      time: timeString
    }, '{{date}}, {{time}}');
  }
}
exports.PDFDocumentProperties = PDFDocumentProperties;

/***/ }),
/* 16 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.PDFFindBar = undefined;

var _pdf_find_controller = __webpack_require__(7);

var _ui_utils = __webpack_require__(0);

class PDFFindBar {
  constructor(options, l10n = _ui_utils.NullL10n) {
    this.opened = false;
    this.bar = options.bar || null;
    this.toggleButton = options.toggleButton || null;
    this.findField = options.findField || null;
    this.highlightAll = options.highlightAllCheckbox || null;
    this.caseSensitive = options.caseSensitiveCheckbox || null;
    this.findMsg = options.findMsg || null;
    this.findResultsCount = options.findResultsCount || null;
    this.findStatusIcon = options.findStatusIcon || null;
    this.findPreviousButton = options.findPreviousButton || null;
    this.findNextButton = options.findNextButton || null;
    this.findController = options.findController || null;
    this.eventBus = options.eventBus;
    this.l10n = l10n;
    if (this.findController === null) {
      throw new Error('PDFFindBar cannot be used without a ' + 'PDFFindController instance.');
    }
    this.toggleButton.addEventListener('click', () => {
      this.toggle();
    });
    this.findField.addEventListener('input', () => {
      this.dispatchEvent('');
    });
    this.bar.addEventListener('keydown', e => {
      switch (e.keyCode) {
        case 13:
          if (e.target === this.findField) {
            this.dispatchEvent('again', e.shiftKey);
          }
          break;
        case 27:
          this.close();
          break;
      }
    });
    this.findPreviousButton.addEventListener('click', () => {
      this.dispatchEvent('again', true);
    });
    this.findNextButton.addEventListener('click', () => {
      this.dispatchEvent('again', false);
    });
    this.highlightAll.addEventListener('click', () => {
      this.dispatchEvent('highlightallchange');
    });
    this.caseSensitive.addEventListener('click', () => {
      this.dispatchEvent('casesensitivitychange');
    });
    this.eventBus.on('resize', this._adjustWidth.bind(this));
  }
  reset() {
    this.updateUIState();
  }
  dispatchEvent(type, findPrev) {
    this.eventBus.dispatch('find', {
      source: this,
      type,
      query: this.findField.value,
      caseSensitive: this.caseSensitive.checked,
      phraseSearch: true,
      highlightAll: this.highlightAll.checked,
      findPrevious: findPrev
    });
  }
  updateUIState(state, previous, matchCount) {
    let notFound = false;
    let findMsg = '';
    let status = '';
    switch (state) {
      case _pdf_find_controller.FindState.FOUND:
        break;
      case _pdf_find_controller.FindState.PENDING:
        status = 'pending';
        break;
      case _pdf_find_controller.FindState.NOT_FOUND:
        findMsg = this.l10n.get('find_not_found', null, 'Phrase not found');
        notFound = true;
        break;
      case _pdf_find_controller.FindState.WRAPPED:
        if (previous) {
          findMsg = this.l10n.get('find_reached_top', null, 'Reached top of document, continued from bottom');
        } else {
          findMsg = this.l10n.get('find_reached_bottom', null, 'Reached end of document, continued from top');
        }
        break;
    }
    if (notFound) {
      this.findField.classList.add('notFound');
    } else {
      this.findField.classList.remove('notFound');
    }
    this.findField.setAttribute('data-status', status);
    Promise.resolve(findMsg).then(msg => {
      this.findMsg.textContent = msg;
      this._adjustWidth();
    });
    this.updateResultsCount(matchCount);
  }
  updateResultsCount(matchCount) {
    if (!this.findResultsCount) {
      return;
    }
    if (!matchCount) {
      this.findResultsCount.classList.add('hidden');
      this.findResultsCount.textContent = '';
    } else {
      this.findResultsCount.textContent = matchCount.toLocaleString();
      this.findResultsCount.classList.remove('hidden');
    }
    this._adjustWidth();
  }
  open() {
    if (!this.opened) {
      this.opened = true;
      this.toggleButton.classList.add('toggled');
      this.bar.classList.remove('hidden');
    }
    this.findField.select();
    this.findField.focus();
    this._adjustWidth();
  }
  close() {
    if (!this.opened) {
      return;
    }
    this.opened = false;
    this.toggleButton.classList.remove('toggled');
    this.bar.classList.add('hidden');
    this.findController.active = false;
  }
  toggle() {
    if (this.opened) {
      this.close();
    } else {
      this.open();
    }
  }
  _adjustWidth() {
    if (!this.opened) {
      return;
    }
    this.bar.classList.remove('wrapContainers');
    let findbarHeight = this.bar.clientHeight;
    let inputContainerHeight = this.bar.firstElementChild.clientHeight;
    if (findbarHeight > inputContainerHeight) {
      this.bar.classList.add('wrapContainers');
    }
  }
}
exports.PDFFindBar = PDFFindBar;

/***/ }),
/* 17 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.isDestArraysEqual = exports.isDestHashesEqual = exports.PDFHistory = undefined;

var _ui_utils = __webpack_require__(0);

var _dom_events = __webpack_require__(2);

const HASH_CHANGE_TIMEOUT = 1000;
const POSITION_UPDATED_THRESHOLD = 50;
const UPDATE_VIEWAREA_TIMEOUT = 1000;
function getCurrentHash() {
  return document.location.hash;
}
function parseCurrentHash(linkService) {
  let hash = unescape(getCurrentHash()).substring(1);
  let params = (0, _ui_utils.parseQueryString)(hash);
  let page = params.page | 0;
  if (!(Number.isInteger(page) && page > 0 && page <= linkService.pagesCount)) {
    page = null;
  }
  return {
    hash,
    page,
    rotation: linkService.rotation
  };
}
class PDFHistory {
  constructor({ linkService, eventBus }) {
    this.linkService = linkService;
    this.eventBus = eventBus || (0, _dom_events.getGlobalEventBus)();
    this.initialized = false;
    this.initialBookmark = null;
    this.initialRotation = null;
    this._boundEvents = Object.create(null);
    this._isViewerInPresentationMode = false;
    this._isPagesLoaded = false;
    this.eventBus.on('presentationmodechanged', evt => {
      this._isViewerInPresentationMode = evt.active || evt.switchInProgress;
    });
    this.eventBus.on('pagesloaded', evt => {
      this._isPagesLoaded = !!evt.pagesCount;
    });
  }
  initialize(fingerprint, resetHistory = false) {
    if (!fingerprint || typeof fingerprint !== 'string') {
      console.error('PDFHistory.initialize: The "fingerprint" must be a non-empty string.');
      return;
    }
    let reInitialized = this.initialized && this.fingerprint !== fingerprint;
    this.fingerprint = fingerprint;
    if (!this.initialized) {
      this._bindEvents();
    }
    let state = window.history.state;
    this.initialized = true;
    this.initialBookmark = null;
    this.initialRotation = null;
    this._popStateInProgress = false;
    this._blockHashChange = 0;
    this._currentHash = getCurrentHash();
    this._numPositionUpdates = 0;
    this._uid = this._maxUid = 0;
    this._destination = null;
    this._position = null;
    if (!this._isValidState(state) || resetHistory) {
      let { hash, page, rotation } = parseCurrentHash(this.linkService);
      if (!hash || reInitialized || resetHistory) {
        this._pushOrReplaceState(null, true);
        return;
      }
      this._pushOrReplaceState({
        hash,
        page,
        rotation
      }, true);
      return;
    }
    let destination = state.destination;
    this._updateInternalState(destination, state.uid, true);
    if (this._uid > this._maxUid) {
      this._maxUid = this._uid;
    }
    if (destination.rotation !== undefined) {
      this.initialRotation = destination.rotation;
    }
    if (destination.dest) {
      this.initialBookmark = JSON.stringify(destination.dest);
      this._destination.page = null;
    } else if (destination.hash) {
      this.initialBookmark = destination.hash;
    } else if (destination.page) {
      this.initialBookmark = `page=${destination.page}`;
    }
  }
  push({ namedDest, explicitDest, pageNumber }) {
    if (!this.initialized) {
      return;
    }
    if (namedDest && typeof namedDest !== 'string' || !(explicitDest instanceof Array) || !(Number.isInteger(pageNumber) && pageNumber > 0 && pageNumber <= this.linkService.pagesCount)) {
      console.error('PDFHistory.push: Invalid parameters.');
      return;
    }
    let hash = namedDest || JSON.stringify(explicitDest);
    if (!hash) {
      return;
    }
    let forceReplace = false;
    if (this._destination && (isDestHashesEqual(this._destination.hash, hash) || isDestArraysEqual(this._destination.dest, explicitDest))) {
      if (this._destination.page) {
        return;
      }
      forceReplace = true;
    }
    if (this._popStateInProgress && !forceReplace) {
      return;
    }
    this._pushOrReplaceState({
      dest: explicitDest,
      hash,
      page: pageNumber,
      rotation: this.linkService.rotation
    }, forceReplace);
    if (!this._popStateInProgress) {
      this._popStateInProgress = true;
      Promise.resolve().then(() => {
        this._popStateInProgress = false;
      });
    }
  }
  pushCurrentPosition() {
    if (!this.initialized || this._popStateInProgress) {
      return;
    }
    this._tryPushCurrentPosition();
  }
  back() {
    if (!this.initialized || this._popStateInProgress) {
      return;
    }
    let state = window.history.state;
    if (this._isValidState(state) && state.uid > 0) {
      window.history.back();
    }
  }
  forward() {
    if (!this.initialized || this._popStateInProgress) {
      return;
    }
    let state = window.history.state;
    if (this._isValidState(state) && state.uid < this._maxUid) {
      window.history.forward();
    }
  }
  get popStateInProgress() {
    return this.initialized && (this._popStateInProgress || this._blockHashChange > 0);
  }
  _pushOrReplaceState(destination, forceReplace = false) {
    let shouldReplace = forceReplace || !this._destination;
    let newState = {
      fingerprint: this.fingerprint,
      uid: shouldReplace ? this._uid : this._uid + 1,
      destination
    };
    this._updateInternalState(destination, newState.uid);
    if (shouldReplace) {
      window.history.replaceState(newState, '');
    } else {
      this._maxUid = this._uid;
      window.history.pushState(newState, '');
    }
  }
  _tryPushCurrentPosition(temporary = false) {
    if (!this._position) {
      return;
    }
    let position = this._position;
    if (temporary) {
      position = (0, _ui_utils.cloneObj)(this._position);
      position.temporary = true;
    }
    if (!this._destination) {
      this._pushOrReplaceState(position);
      return;
    }
    if (this._destination.temporary) {
      this._pushOrReplaceState(position, true);
      return;
    }
    if (this._destination.hash === position.hash) {
      return;
    }
    if (!this._destination.page && (POSITION_UPDATED_THRESHOLD <= 0 || this._numPositionUpdates <= POSITION_UPDATED_THRESHOLD)) {
      return;
    }
    let forceReplace = false;
    if (this._destination.page === position.first || this._destination.page === position.page) {
      if (this._destination.dest || !this._destination.first) {
        return;
      }
      forceReplace = true;
    }
    this._pushOrReplaceState(position, forceReplace);
  }
  _isValidState(state) {
    if (!state) {
      return false;
    }
    if (state.fingerprint !== this.fingerprint) {
      return false;
    }
    if (!Number.isInteger(state.uid) || state.uid < 0) {
      return false;
    }
    if (state.destination === null || typeof state.destination !== 'object') {
      return false;
    }
    return true;
  }
  _updateInternalState(destination, uid, removeTemporary = false) {
    if (this._updateViewareaTimeout) {
      clearTimeout(this._updateViewareaTimeout);
      this._updateViewareaTimeout = null;
    }
    if (removeTemporary && destination && destination.temporary) {
      delete destination.temporary;
    }
    this._destination = destination;
    this._uid = uid;
    this._numPositionUpdates = 0;
  }
  _updateViewarea({ location }) {
    if (this._updateViewareaTimeout) {
      clearTimeout(this._updateViewareaTimeout);
      this._updateViewareaTimeout = null;
    }
    this._position = {
      hash: this._isViewerInPresentationMode ? `page=${location.pageNumber}` : location.pdfOpenParams.substring(1),
      page: this.linkService.page,
      first: location.pageNumber,
      rotation: location.rotation
    };
    if (this._popStateInProgress) {
      return;
    }
    if (POSITION_UPDATED_THRESHOLD > 0 && this._isPagesLoaded && this._destination && !this._destination.page) {
      this._numPositionUpdates++;
    }
    if (UPDATE_VIEWAREA_TIMEOUT > 0) {
      this._updateViewareaTimeout = setTimeout(() => {
        if (!this._popStateInProgress) {
          this._tryPushCurrentPosition(true);
        }
        this._updateViewareaTimeout = null;
      }, UPDATE_VIEWAREA_TIMEOUT);
    }
  }
  _popState({ state }) {
    let newHash = getCurrentHash(),
        hashChanged = this._currentHash !== newHash;
    this._currentHash = newHash;
    if (!state || false) {
      this._uid++;
      let { hash, page, rotation } = parseCurrentHash(this.linkService);
      this._pushOrReplaceState({
        hash,
        page,
        rotation
      }, true);
      return;
    }
    if (!this._isValidState(state)) {
      return;
    }
    this._popStateInProgress = true;
    if (hashChanged) {
      this._blockHashChange++;
      (0, _ui_utils.waitOnEventOrTimeout)({
        target: window,
        name: 'hashchange',
        delay: HASH_CHANGE_TIMEOUT
      }).then(() => {
        this._blockHashChange--;
      });
    }
    let destination = state.destination;
    this._updateInternalState(destination, state.uid, true);
    if (this._uid > this._maxUid) {
      this._maxUid = this._uid;
    }
    if ((0, _ui_utils.isValidRotation)(destination.rotation)) {
      this.linkService.rotation = destination.rotation;
    }
    if (destination.dest) {
      this.linkService.navigateTo(destination.dest);
    } else if (destination.hash) {
      this.linkService.setHash(destination.hash);
    } else if (destination.page) {
      this.linkService.page = destination.page;
    }
    Promise.resolve().then(() => {
      this._popStateInProgress = false;
    });
  }
  _bindEvents() {
    let { _boundEvents, eventBus } = this;
    _boundEvents.updateViewarea = this._updateViewarea.bind(this);
    _boundEvents.popState = this._popState.bind(this);
    _boundEvents.pageHide = evt => {
      if (!this._destination || this._destination.temporary) {
        this._tryPushCurrentPosition();
      }
    };
    eventBus.on('updateviewarea', _boundEvents.updateViewarea);
    window.addEventListener('popstate', _boundEvents.popState);
    window.addEventListener('pagehide', _boundEvents.pageHide);
  }
}
function isDestHashesEqual(destHash, pushHash) {
  if (typeof destHash !== 'string' || typeof pushHash !== 'string') {
    return false;
  }
  if (destHash === pushHash) {
    return true;
  }
  let { nameddest } = (0, _ui_utils.parseQueryString)(destHash);
  if (nameddest === pushHash) {
    return true;
  }
  return false;
}
function isDestArraysEqual(firstDest, secondDest) {
  function isEntryEqual(first, second) {
    if (typeof first !== typeof second) {
      return false;
    }
    if (first instanceof Array || second instanceof Array) {
      return false;
    }
    if (first !== null && typeof first === 'object' && second !== null) {
      if (Object.keys(first).length !== Object.keys(second).length) {
        return false;
      }
      for (let key in first) {
        if (!isEntryEqual(first[key], second[key])) {
          return false;
        }
      }
      return true;
    }
    return first === second || Number.isNaN(first) && Number.isNaN(second);
  }
  if (!(firstDest instanceof Array && secondDest instanceof Array)) {
    return false;
  }
  if (firstDest.length !== secondDest.length) {
    return false;
  }
  for (let i = 0, ii = firstDest.length; i < ii; i++) {
    if (!isEntryEqual(firstDest[i], secondDest[i])) {
      return false;
    }
  }
  return true;
}
exports.PDFHistory = PDFHistory;
exports.isDestHashesEqual = isDestHashesEqual;
exports.isDestArraysEqual = isDestArraysEqual;

/***/ }),
/* 18 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.PDFOutlineViewer = undefined;

var _pdfjsLib = __webpack_require__(1);

const DEFAULT_TITLE = '\u2013';
class PDFOutlineViewer {
  constructor({ container, linkService, eventBus }) {
    this.container = container;
    this.linkService = linkService;
    this.eventBus = eventBus;
    this.reset();
  }
  reset() {
    this.outline = null;
    this.lastToggleIsShow = true;
    this.container.textContent = '';
    this.container.classList.remove('outlineWithDeepNesting');
  }
  _dispatchEvent(outlineCount) {
    this.eventBus.dispatch('outlineloaded', {
      source: this,
      outlineCount
    });
  }
  _bindLink(element, { url, newWindow, dest }) {
    let { linkService } = this;
    if (url) {
      (0, _pdfjsLib.addLinkAttributes)(element, {
        url,
        target: newWindow ? _pdfjsLib.LinkTarget.BLANK : linkService.externalLinkTarget,
        rel: linkService.externalLinkRel
      });
      return;
    }
    element.href = this.linkService.getDestinationHash(dest);
    element.onclick = () => {
      if (dest) {
        this.linkService.navigateTo(dest);
      }
      return false;
    };
  }
  _setStyles(element, { bold, italic }) {
    let styleStr = '';
    if (bold) {
      styleStr += 'font-weight: bold;';
    }
    if (italic) {
      styleStr += 'font-style: italic;';
    }
    if (styleStr) {
      element.setAttribute('style', styleStr);
    }
  }
  _addToggleButton(div) {
    let toggler = document.createElement('div');
    toggler.className = 'outlineItemToggler';
    toggler.onclick = evt => {
      evt.stopPropagation();
      toggler.classList.toggle('outlineItemsHidden');
      if (evt.shiftKey) {
        let shouldShowAll = !toggler.classList.contains('outlineItemsHidden');
        this._toggleOutlineItem(div, shouldShowAll);
      }
    };
    div.insertBefore(toggler, div.firstChild);
  }
  _toggleOutlineItem(root, show) {
    this.lastToggleIsShow = show;
    let togglers = root.querySelectorAll('.outlineItemToggler');
    for (let i = 0, ii = togglers.length; i < ii; ++i) {
      togglers[i].classList[show ? 'remove' : 'add']('outlineItemsHidden');
    }
  }
  toggleOutlineTree() {
    if (!this.outline) {
      return;
    }
    this._toggleOutlineItem(this.container, !this.lastToggleIsShow);
  }
  render({ outline }) {
    let outlineCount = 0;
    if (this.outline) {
      this.reset();
    }
    this.outline = outline || null;
    if (!outline) {
      this._dispatchEvent(outlineCount);
      return;
    }
    let fragment = document.createDocumentFragment();
    let queue = [{
      parent: fragment,
      items: this.outline
    }];
    let hasAnyNesting = false;
    while (queue.length > 0) {
      let levelData = queue.shift();
      for (let i = 0, len = levelData.items.length; i < len; i++) {
        let item = levelData.items[i];
        let div = document.createElement('div');
        div.className = 'outlineItem';
        let element = document.createElement('a');
        this._bindLink(element, item);
        this._setStyles(element, item);
        element.textContent = (0, _pdfjsLib.removeNullCharacters)(item.title) || DEFAULT_TITLE;
        div.appendChild(element);
        if (item.items.length > 0) {
          hasAnyNesting = true;
          this._addToggleButton(div);
          let itemsDiv = document.createElement('div');
          itemsDiv.className = 'outlineItems';
          div.appendChild(itemsDiv);
          queue.push({
            parent: itemsDiv,
            items: item.items
          });
        }
        levelData.parent.appendChild(div);
        outlineCount++;
      }
    }
    if (hasAnyNesting) {
      this.container.classList.add('outlineWithDeepNesting');
    }
    this.container.appendChild(fragment);
    this._dispatchEvent(outlineCount);
  }
}
exports.PDFOutlineViewer = PDFOutlineViewer;

/***/ }),
/* 19 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.PDFPresentationMode = undefined;

var _ui_utils = __webpack_require__(0);

const DELAY_BEFORE_RESETTING_SWITCH_IN_PROGRESS = 1500;
const DELAY_BEFORE_HIDING_CONTROLS = 3000;
const ACTIVE_SELECTOR = 'pdfPresentationMode';
const CONTROLS_SELECTOR = 'pdfPresentationModeControls';
const MOUSE_SCROLL_COOLDOWN_TIME = 50;
const PAGE_SWITCH_THRESHOLD = 0.1;
const SWIPE_MIN_DISTANCE_THRESHOLD = 50;
const SWIPE_ANGLE_THRESHOLD = Math.PI / 6;
class PDFPresentationMode {
  constructor({ container, viewer = null, pdfViewer, eventBus, contextMenuItems = null }) {
    this.container = container;
    this.viewer = viewer || container.firstElementChild;
    this.pdfViewer = pdfViewer;
    this.eventBus = eventBus;
    this.active = false;
    this.args = null;
    this.contextMenuOpen = false;
    this.mouseScrollTimeStamp = 0;
    this.mouseScrollDelta = 0;
    this.touchSwipeState = null;
    if (contextMenuItems) {
      contextMenuItems.contextFirstPage.addEventListener('click', () => {
        this.contextMenuOpen = false;
        this.eventBus.dispatch('firstpage');
      });
      contextMenuItems.contextLastPage.addEventListener('click', () => {
        this.contextMenuOpen = false;
        this.eventBus.dispatch('lastpage');
      });
      contextMenuItems.contextPageRotateCw.addEventListener('click', () => {
        this.contextMenuOpen = false;
        this.eventBus.dispatch('rotatecw');
      });
      contextMenuItems.contextPageRotateCcw.addEventListener('click', () => {
        this.contextMenuOpen = false;
        this.eventBus.dispatch('rotateccw');
      });
    }
  }
  request() {
    if (this.switchInProgress || this.active || !this.viewer.hasChildNodes()) {
      return false;
    }
    this._addFullscreenChangeListeners();
    this._setSwitchInProgress();
    this._notifyStateChange();
    if (this.container.requestFullscreen) {
      this.container.requestFullscreen();
    } else if (this.container.mozRequestFullScreen) {
      this.container.mozRequestFullScreen();
    } else if (this.container.webkitRequestFullscreen) {
      this.container.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
    } else if (this.container.msRequestFullscreen) {
      this.container.msRequestFullscreen();
    } else {
      return false;
    }
    this.args = {
      page: this.pdfViewer.currentPageNumber,
      previousScale: this.pdfViewer.currentScaleValue
    };
    return true;
  }
  _mouseWheel(evt) {
    if (!this.active) {
      return;
    }
    evt.preventDefault();
    let delta = (0, _ui_utils.normalizeWheelEventDelta)(evt);
    let currentTime = new Date().getTime();
    let storedTime = this.mouseScrollTimeStamp;
    if (currentTime > storedTime && currentTime - storedTime < MOUSE_SCROLL_COOLDOWN_TIME) {
      return;
    }
    if (this.mouseScrollDelta > 0 && delta < 0 || this.mouseScrollDelta < 0 && delta > 0) {
      this._resetMouseScrollState();
    }
    this.mouseScrollDelta += delta;
    if (Math.abs(this.mouseScrollDelta) >= PAGE_SWITCH_THRESHOLD) {
      let totalDelta = this.mouseScrollDelta;
      this._resetMouseScrollState();
      let success = totalDelta > 0 ? this._goToPreviousPage() : this._goToNextPage();
      if (success) {
        this.mouseScrollTimeStamp = currentTime;
      }
    }
  }
  get isFullscreen() {
    return !!(document.fullscreenElement || document.mozFullScreen || document.webkitIsFullScreen || document.msFullscreenElement);
  }
  _goToPreviousPage() {
    let page = this.pdfViewer.currentPageNumber;
    if (page <= 1) {
      return false;
    }
    this.pdfViewer.currentPageNumber = page - 1;
    return true;
  }
  _goToNextPage() {
    let page = this.pdfViewer.currentPageNumber;
    if (page >= this.pdfViewer.pagesCount) {
      return false;
    }
    this.pdfViewer.currentPageNumber = page + 1;
    return true;
  }
  _notifyStateChange() {
    this.eventBus.dispatch('presentationmodechanged', {
      source: this,
      active: this.active,
      switchInProgress: !!this.switchInProgress
    });
  }
  _setSwitchInProgress() {
    if (this.switchInProgress) {
      clearTimeout(this.switchInProgress);
    }
    this.switchInProgress = setTimeout(() => {
      this._removeFullscreenChangeListeners();
      delete this.switchInProgress;
      this._notifyStateChange();
    }, DELAY_BEFORE_RESETTING_SWITCH_IN_PROGRESS);
  }
  _resetSwitchInProgress() {
    if (this.switchInProgress) {
      clearTimeout(this.switchInProgress);
      delete this.switchInProgress;
    }
  }
  _enter() {
    this.active = true;
    this._resetSwitchInProgress();
    this._notifyStateChange();
    this.container.classList.add(ACTIVE_SELECTOR);
    setTimeout(() => {
      this.pdfViewer.currentPageNumber = this.args.page;
      this.pdfViewer.currentScaleValue = 'page-fit';
    }, 0);
    this._addWindowListeners();
    this._showControls();
    this.contextMenuOpen = false;
    this.container.setAttribute('contextmenu', 'viewerContextMenu');
    window.getSelection().removeAllRanges();
  }
  _exit() {
    let page = this.pdfViewer.currentPageNumber;
    this.container.classList.remove(ACTIVE_SELECTOR);
    setTimeout(() => {
      this.active = false;
      this._removeFullscreenChangeListeners();
      this._notifyStateChange();
      this.pdfViewer.currentScaleValue = this.args.previousScale;
      this.pdfViewer.currentPageNumber = page;
      this.args = null;
    }, 0);
    this._removeWindowListeners();
    this._hideControls();
    this._resetMouseScrollState();
    this.container.removeAttribute('contextmenu');
    this.contextMenuOpen = false;
  }
  _mouseDown(evt) {
    if (this.contextMenuOpen) {
      this.contextMenuOpen = false;
      evt.preventDefault();
      return;
    }
    if (evt.button === 0) {
      let isInternalLink = evt.target.href && evt.target.classList.contains('internalLink');
      if (!isInternalLink) {
        evt.preventDefault();
        if (evt.shiftKey) {
          this._goToPreviousPage();
        } else {
          this._goToNextPage();
        }
      }
    }
  }
  _contextMenu() {
    this.contextMenuOpen = true;
  }
  _showControls() {
    if (this.controlsTimeout) {
      clearTimeout(this.controlsTimeout);
    } else {
      this.container.classList.add(CONTROLS_SELECTOR);
    }
    this.controlsTimeout = setTimeout(() => {
      this.container.classList.remove(CONTROLS_SELECTOR);
      delete this.controlsTimeout;
    }, DELAY_BEFORE_HIDING_CONTROLS);
  }
  _hideControls() {
    if (!this.controlsTimeout) {
      return;
    }
    clearTimeout(this.controlsTimeout);
    this.container.classList.remove(CONTROLS_SELECTOR);
    delete this.controlsTimeout;
  }
  _resetMouseScrollState() {
    this.mouseScrollTimeStamp = 0;
    this.mouseScrollDelta = 0;
  }
  _touchSwipe(evt) {
    if (!this.active) {
      return;
    }
    if (evt.touches.length > 1) {
      this.touchSwipeState = null;
      return;
    }
    switch (evt.type) {
      case 'touchstart':
        this.touchSwipeState = {
          startX: evt.touches[0].pageX,
          startY: evt.touches[0].pageY,
          endX: evt.touches[0].pageX,
          endY: evt.touches[0].pageY
        };
        break;
      case 'touchmove':
        if (this.touchSwipeState === null) {
          return;
        }
        this.touchSwipeState.endX = evt.touches[0].pageX;
        this.touchSwipeState.endY = evt.touches[0].pageY;
        evt.preventDefault();
        break;
      case 'touchend':
        if (this.touchSwipeState === null) {
          return;
        }
        let delta = 0;
        let dx = this.touchSwipeState.endX - this.touchSwipeState.startX;
        let dy = this.touchSwipeState.endY - this.touchSwipeState.startY;
        let absAngle = Math.abs(Math.atan2(dy, dx));
        if (Math.abs(dx) > SWIPE_MIN_DISTANCE_THRESHOLD && (absAngle <= SWIPE_ANGLE_THRESHOLD || absAngle >= Math.PI - SWIPE_ANGLE_THRESHOLD)) {
          delta = dx;
        } else if (Math.abs(dy) > SWIPE_MIN_DISTANCE_THRESHOLD && Math.abs(absAngle - Math.PI / 2) <= SWIPE_ANGLE_THRESHOLD) {
          delta = dy;
        }
        if (delta > 0) {
          this._goToPreviousPage();
        } else if (delta < 0) {
          this._goToNextPage();
        }
        break;
    }
  }
  _addWindowListeners() {
    this.showControlsBind = this._showControls.bind(this);
    this.mouseDownBind = this._mouseDown.bind(this);
    this.mouseWheelBind = this._mouseWheel.bind(this);
    this.resetMouseScrollStateBind = this._resetMouseScrollState.bind(this);
    this.contextMenuBind = this._contextMenu.bind(this);
    this.touchSwipeBind = this._touchSwipe.bind(this);
    window.addEventListener('mousemove', this.showControlsBind);
    window.addEventListener('mousedown', this.mouseDownBind);
    window.addEventListener('wheel', this.mouseWheelBind);
    window.addEventListener('keydown', this.resetMouseScrollStateBind);
    window.addEventListener('contextmenu', this.contextMenuBind);
    window.addEventListener('touchstart', this.touchSwipeBind);
    window.addEventListener('touchmove', this.touchSwipeBind);
    window.addEventListener('touchend', this.touchSwipeBind);
  }
  _removeWindowListeners() {
    window.removeEventListener('mousemove', this.showControlsBind);
    window.removeEventListener('mousedown', this.mouseDownBind);
    window.removeEventListener('wheel', this.mouseWheelBind);
    window.removeEventListener('keydown', this.resetMouseScrollStateBind);
    window.removeEventListener('contextmenu', this.contextMenuBind);
    window.removeEventListener('touchstart', this.touchSwipeBind);
    window.removeEventListener('touchmove', this.touchSwipeBind);
    window.removeEventListener('touchend', this.touchSwipeBind);
    delete this.showControlsBind;
    delete this.mouseDownBind;
    delete this.mouseWheelBind;
    delete this.resetMouseScrollStateBind;
    delete this.contextMenuBind;
    delete this.touchSwipeBind;
  }
  _fullscreenChange() {
    if (this.isFullscreen) {
      this._enter();
    } else {
      this._exit();
    }
  }
  _addFullscreenChangeListeners() {
    this.fullscreenChangeBind = this._fullscreenChange.bind(this);
    window.addEventListener('fullscreenchange', this.fullscreenChangeBind);
    window.addEventListener('mozfullscreenchange', this.fullscreenChangeBind);
  }
  _removeFullscreenChangeListeners() {
    window.removeEventListener('fullscreenchange', this.fullscreenChangeBind);
    window.removeEventListener('mozfullscreenchange', this.fullscreenChangeBind);
    delete this.fullscreenChangeBind;
  }
}
exports.PDFPresentationMode = PDFPresentationMode;

/***/ }),
/* 20 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.PDFSidebarResizer = undefined;

var _ui_utils = __webpack_require__(0);

const SIDEBAR_WIDTH_VAR = '--sidebar-width';
const SIDEBAR_MIN_WIDTH = 200;
const SIDEBAR_RESIZING_CLASS = 'sidebarResizing';
class PDFSidebarResizer {
  constructor(options, eventBus, l10n = _ui_utils.NullL10n) {
    this.enabled = false;
    this.isRTL = false;
    this.sidebarOpen = false;
    this.doc = document.documentElement;
    this._width = null;
    this._outerContainerWidth = null;
    this._boundEvents = Object.create(null);
    this.outerContainer = options.outerContainer;
    this.resizer = options.resizer;
    this.eventBus = eventBus;
    this.l10n = l10n;
    if (typeof CSS === 'undefined' || typeof CSS.supports !== 'function' || !CSS.supports(SIDEBAR_WIDTH_VAR, `calc(-1 * ${SIDEBAR_MIN_WIDTH}px)`)) {
      console.warn('PDFSidebarResizer: ' + 'The browser does not support resizing of the sidebar.');
      return;
    }
    this.enabled = true;
    this.resizer.classList.remove('hidden');
    this.l10n.getDirection().then(dir => {
      this.isRTL = dir === 'rtl';
    });
    this._addEventListeners();
  }
  get outerContainerWidth() {
    if (!this._outerContainerWidth) {
      this._outerContainerWidth = this.outerContainer.clientWidth;
    }
    return this._outerContainerWidth;
  }
  _updateWidth(width = 0) {
    if (!this.enabled) {
      return false;
    }
    const maxWidth = Math.floor(this.outerContainerWidth / 2);
    if (width > maxWidth) {
      width = maxWidth;
    }
    if (width < SIDEBAR_MIN_WIDTH) {
      width = SIDEBAR_MIN_WIDTH;
    }
    if (width === this._width) {
      return false;
    }
    this._width = width;
    this.doc.style.setProperty(SIDEBAR_WIDTH_VAR, `${width}px`);
    return true;
  }
  _mouseMove(evt) {
    let width = evt.clientX;
    if (this.isRTL) {
      width = this.outerContainerWidth - width;
    }
    this._updateWidth(width);
  }
  _mouseUp(evt) {
    this.outerContainer.classList.remove(SIDEBAR_RESIZING_CLASS);
    this.eventBus.dispatch('resize', { source: this });
    let _boundEvents = this._boundEvents;
    window.removeEventListener('mousemove', _boundEvents.mouseMove);
    window.removeEventListener('mouseup', _boundEvents.mouseUp);
  }
  _addEventListeners() {
    if (!this.enabled) {
      return;
    }
    let _boundEvents = this._boundEvents;
    _boundEvents.mouseMove = this._mouseMove.bind(this);
    _boundEvents.mouseUp = this._mouseUp.bind(this);
    this.resizer.addEventListener('mousedown', evt => {
      if (evt.button !== 0) {
        return;
      }
      this.outerContainer.classList.add(SIDEBAR_RESIZING_CLASS);
      window.addEventListener('mousemove', _boundEvents.mouseMove);
      window.addEventListener('mouseup', _boundEvents.mouseUp);
    });
    this.eventBus.on('sidebarviewchanged', evt => {
      this.sidebarOpen = !!(evt && evt.view);
    });
    this.eventBus.on('resize', evt => {
      if (evt && evt.source === window) {
        this._outerContainerWidth = null;
        if (this._width) {
          if (this.sidebarOpen) {
            this.outerContainer.classList.add(SIDEBAR_RESIZING_CLASS);
            let updated = this._updateWidth(this._width);
            Promise.resolve().then(() => {
              this.outerContainer.classList.remove(SIDEBAR_RESIZING_CLASS);
              if (updated) {
                this.eventBus.dispatch('resize', { source: this });
              }
            });
          } else {
            this._updateWidth(this._width);
          }
        }
      }
    });
  }
}
exports.PDFSidebarResizer = PDFSidebarResizer;

/***/ }),
/* 21 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.PDFThumbnailViewer = undefined;

var _ui_utils = __webpack_require__(0);

var _pdf_thumbnail_view = __webpack_require__(22);

const THUMBNAIL_SCROLL_MARGIN = -19;
const THUMBNAIL_SELECTED_CLASS = 'selected';
class PDFThumbnailViewer {
  constructor({ container, linkService, renderingQueue, l10n = _ui_utils.NullL10n }) {
    this.container = container;
    this.linkService = linkService;
    this.renderingQueue = renderingQueue;
    this.l10n = l10n;
    this.scroll = (0, _ui_utils.watchScroll)(this.container, this._scrollUpdated.bind(this));
    this._resetView();
  }
  _scrollUpdated() {
    this.renderingQueue.renderHighestPriority();
  }
  getThumbnail(index) {
    return this._thumbnails[index];
  }
  _getVisibleThumbs() {
    return (0, _ui_utils.getVisibleElements)(this.container, this._thumbnails);
  }
  scrollThumbnailIntoView(pageNumber) {
    if (!this.pdfDocument) {
      return;
    }
    const thumbnailView = this._thumbnails[pageNumber - 1];
    if (!thumbnailView) {
      console.error('scrollThumbnailIntoView: Invalid "pageNumber" parameter.');
      return;
    }
    if (pageNumber !== this._currentPageNumber) {
      const prevThumbnailView = this._thumbnails[this._currentPageNumber - 1];
      prevThumbnailView.div.classList.remove(THUMBNAIL_SELECTED_CLASS);
      thumbnailView.div.classList.add(THUMBNAIL_SELECTED_CLASS);
    }
    let visibleThumbs = this._getVisibleThumbs();
    let numVisibleThumbs = visibleThumbs.views.length;
    if (numVisibleThumbs > 0) {
      let first = visibleThumbs.first.id;
      let last = numVisibleThumbs > 1 ? visibleThumbs.last.id : first;
      let shouldScroll = false;
      if (pageNumber <= first || pageNumber >= last) {
        shouldScroll = true;
      } else {
        visibleThumbs.views.some(function (view) {
          if (view.id !== pageNumber) {
            return false;
          }
          shouldScroll = view.percent < 100;
          return true;
        });
      }
      if (shouldScroll) {
        (0, _ui_utils.scrollIntoView)(thumbnailView.div, { top: THUMBNAIL_SCROLL_MARGIN });
      }
    }
    this._currentPageNumber = pageNumber;
  }
  get pagesRotation() {
    return this._pagesRotation;
  }
  set pagesRotation(rotation) {
    if (!(0, _ui_utils.isValidRotation)(rotation)) {
      throw new Error('Invalid thumbnails rotation angle.');
    }
    if (!this.pdfDocument) {
      return;
    }
    if (this._pagesRotation === rotation) {
      return;
    }
    this._pagesRotation = rotation;
    for (let i = 0, ii = this._thumbnails.length; i < ii; i++) {
      this._thumbnails[i].update(rotation);
    }
  }
  cleanup() {
    _pdf_thumbnail_view.PDFThumbnailView.cleanup();
  }
  _resetView() {
    this._thumbnails = [];
    this._currentPageNumber = 1;
    this._pageLabels = null;
    this._pagesRotation = 0;
    this._pagesRequests = [];
    this.container.textContent = '';
  }
  setDocument(pdfDocument) {
    if (this.pdfDocument) {
      this._cancelRendering();
      this._resetView();
    }
    this.pdfDocument = pdfDocument;
    if (!pdfDocument) {
      return;
    }
    pdfDocument.getPage(1).then(firstPage => {
      let pagesCount = pdfDocument.numPages;
      let viewport = firstPage.getViewport(1.0);
      for (let pageNum = 1; pageNum <= pagesCount; ++pageNum) {
        let thumbnail = new _pdf_thumbnail_view.PDFThumbnailView({
          container: this.container,
          id: pageNum,
          defaultViewport: viewport.clone(),
          linkService: this.linkService,
          renderingQueue: this.renderingQueue,
          disableCanvasToImageConversion: false,
          l10n: this.l10n
        });
        this._thumbnails.push(thumbnail);
      }
      const thumbnailView = this._thumbnails[this._currentPageNumber - 1];
      thumbnailView.div.classList.add(THUMBNAIL_SELECTED_CLASS);
    }).catch(reason => {
      console.error('Unable to initialize thumbnail viewer', reason);
    });
  }
  _cancelRendering() {
    for (let i = 0, ii = this._thumbnails.length; i < ii; i++) {
      if (this._thumbnails[i]) {
        this._thumbnails[i].cancelRendering();
      }
    }
  }
  setPageLabels(labels) {
    if (!this.pdfDocument) {
      return;
    }
    if (!labels) {
      this._pageLabels = null;
    } else if (!(labels instanceof Array && this.pdfDocument.numPages === labels.length)) {
      this._pageLabels = null;
      console.error('PDFThumbnailViewer_setPageLabels: Invalid page labels.');
    } else {
      this._pageLabels = labels;
    }
    for (let i = 0, ii = this._thumbnails.length; i < ii; i++) {
      let label = this._pageLabels && this._pageLabels[i];
      this._thumbnails[i].setPageLabel(label);
    }
  }
  _ensurePdfPageLoaded(thumbView) {
    if (thumbView.pdfPage) {
      return Promise.resolve(thumbView.pdfPage);
    }
    let pageNumber = thumbView.id;
    if (this._pagesRequests[pageNumber]) {
      return this._pagesRequests[pageNumber];
    }
    let promise = this.pdfDocument.getPage(pageNumber).then(pdfPage => {
      thumbView.setPdfPage(pdfPage);
      this._pagesRequests[pageNumber] = null;
      return pdfPage;
    }).catch(reason => {
      console.error('Unable to get page for thumb view', reason);
      this._pagesRequests[pageNumber] = null;
    });
    this._pagesRequests[pageNumber] = promise;
    return promise;
  }
  forceRendering() {
    let visibleThumbs = this._getVisibleThumbs();
    let thumbView = this.renderingQueue.getHighestPriority(visibleThumbs, this._thumbnails, this.scroll.down);
    if (thumbView) {
      this._ensurePdfPageLoaded(thumbView).then(() => {
        this.renderingQueue.renderView(thumbView);
      });
      return true;
    }
    return false;
  }
}
exports.PDFThumbnailViewer = PDFThumbnailViewer;

/***/ }),
/* 22 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.PDFThumbnailView = undefined;

var _pdfjsLib = __webpack_require__(1);

var _ui_utils = __webpack_require__(0);

var _pdf_rendering_queue = __webpack_require__(3);

const MAX_NUM_SCALING_STEPS = 3;
const THUMBNAIL_CANVAS_BORDER_WIDTH = 1;
const THUMBNAIL_WIDTH = 98;
const TempImageFactory = function TempImageFactoryClosure() {
  let tempCanvasCache = null;
  return {
    getCanvas(width, height) {
      let tempCanvas = tempCanvasCache;
      if (!tempCanvas) {
        tempCanvas = document.createElement('canvas');
        tempCanvasCache = tempCanvas;
      }
      tempCanvas.width = width;
      tempCanvas.height = height;
      tempCanvas.mozOpaque = true;
      let ctx = tempCanvas.getContext('2d', { alpha: false });
      ctx.save();
      ctx.fillStyle = 'rgb(255, 255, 255)';
      ctx.fillRect(0, 0, width, height);
      ctx.restore();
      return tempCanvas;
    },
    destroyCanvas() {
      let tempCanvas = tempCanvasCache;
      if (tempCanvas) {
        tempCanvas.width = 0;
        tempCanvas.height = 0;
      }
      tempCanvasCache = null;
    }
  };
}();
class PDFThumbnailView {
  constructor({ container, id, defaultViewport, linkService, renderingQueue, disableCanvasToImageConversion = false, l10n = _ui_utils.NullL10n }) {
    this.id = id;
    this.renderingId = 'thumbnail' + id;
    this.pageLabel = null;
    this.pdfPage = null;
    this.rotation = 0;
    this.viewport = defaultViewport;
    this.pdfPageRotate = defaultViewport.rotation;
    this.linkService = linkService;
    this.renderingQueue = renderingQueue;
    this.renderTask = null;
    this.renderingState = _pdf_rendering_queue.RenderingStates.INITIAL;
    this.resume = null;
    this.disableCanvasToImageConversion = disableCanvasToImageConversion;
    this.pageWidth = this.viewport.width;
    this.pageHeight = this.viewport.height;
    this.pageRatio = this.pageWidth / this.pageHeight;
    this.canvasWidth = THUMBNAIL_WIDTH;
    this.canvasHeight = this.canvasWidth / this.pageRatio | 0;
    this.scale = this.canvasWidth / this.pageWidth;
    this.l10n = l10n;
    let anchor = document.createElement('a');
    anchor.href = linkService.getAnchorUrl('#page=' + id);
    this.l10n.get('thumb_page_title', { page: id }, 'Page {{page}}').then(msg => {
      anchor.title = msg;
    });
    anchor.onclick = function () {
      linkService.page = id;
      return false;
    };
    this.anchor = anchor;
    let div = document.createElement('div');
    div.className = 'thumbnail';
    div.setAttribute('data-page-number', this.id);
    this.div = div;
    let ring = document.createElement('div');
    ring.className = 'thumbnailSelectionRing';
    let borderAdjustment = 2 * THUMBNAIL_CANVAS_BORDER_WIDTH;
    ring.style.width = this.canvasWidth + borderAdjustment + 'px';
    ring.style.height = this.canvasHeight + borderAdjustment + 'px';
    this.ring = ring;
    div.appendChild(ring);
    anchor.appendChild(div);
    container.appendChild(anchor);
  }
  setPdfPage(pdfPage) {
    this.pdfPage = pdfPage;
    this.pdfPageRotate = pdfPage.rotate;
    let totalRotation = (this.rotation + this.pdfPageRotate) % 360;
    this.viewport = pdfPage.getViewport(1, totalRotation);
    this.reset();
  }
  reset() {
    this.cancelRendering();
    this.pageWidth = this.viewport.width;
    this.pageHeight = this.viewport.height;
    this.pageRatio = this.pageWidth / this.pageHeight;
    this.canvasHeight = this.canvasWidth / this.pageRatio | 0;
    this.scale = this.canvasWidth / this.pageWidth;
    this.div.removeAttribute('data-loaded');
    let ring = this.ring;
    let childNodes = ring.childNodes;
    for (let i = childNodes.length - 1; i >= 0; i--) {
      ring.removeChild(childNodes[i]);
    }
    let borderAdjustment = 2 * THUMBNAIL_CANVAS_BORDER_WIDTH;
    ring.style.width = this.canvasWidth + borderAdjustment + 'px';
    ring.style.height = this.canvasHeight + borderAdjustment + 'px';
    if (this.canvas) {
      this.canvas.width = 0;
      this.canvas.height = 0;
      delete this.canvas;
    }
    if (this.image) {
      this.image.removeAttribute('src');
      delete this.image;
    }
  }
  update(rotation) {
    if (typeof rotation !== 'undefined') {
      this.rotation = rotation;
    }
    let totalRotation = (this.rotation + this.pdfPageRotate) % 360;
    this.viewport = this.viewport.clone({
      scale: 1,
      rotation: totalRotation
    });
    this.reset();
  }
  cancelRendering() {
    if (this.renderTask) {
      this.renderTask.cancel();
      this.renderTask = null;
    }
    this.renderingState = _pdf_rendering_queue.RenderingStates.INITIAL;
    this.resume = null;
  }
  _getPageDrawContext(noCtxScale = false) {
    let canvas = document.createElement('canvas');
    this.canvas = canvas;
    canvas.mozOpaque = true;
    let ctx = canvas.getContext('2d', { alpha: false });
    let outputScale = (0, _ui_utils.getOutputScale)(ctx);
    canvas.width = this.canvasWidth * outputScale.sx | 0;
    canvas.height = this.canvasHeight * outputScale.sy | 0;
    canvas.style.width = this.canvasWidth + 'px';
    canvas.style.height = this.canvasHeight + 'px';
    if (!noCtxScale && outputScale.scaled) {
      ctx.scale(outputScale.sx, outputScale.sy);
    }
    return ctx;
  }
  _convertCanvasToImage() {
    if (!this.canvas) {
      return;
    }
    if (this.renderingState !== _pdf_rendering_queue.RenderingStates.FINISHED) {
      return;
    }
    let id = this.renderingId;
    let className = 'thumbnailImage';
    if (this.disableCanvasToImageConversion) {
      this.canvas.id = id;
      this.canvas.className = className;
      this.l10n.get('thumb_page_canvas', { page: this.pageId }, 'Thumbnail of Page {{page}}').then(msg => {
        this.canvas.setAttribute('aria-label', msg);
      });
      this.div.setAttribute('data-loaded', true);
      this.ring.appendChild(this.canvas);
      return;
    }
    let image = document.createElement('img');
    image.id = id;
    image.className = className;
    this.l10n.get('thumb_page_canvas', { page: this.pageId }, 'Thumbnail of Page {{page}}').then(msg => {
      image.setAttribute('aria-label', msg);
    });
    image.style.width = this.canvasWidth + 'px';
    image.style.height = this.canvasHeight + 'px';
    image.src = this.canvas.toDataURL();
    this.image = image;
    this.div.setAttribute('data-loaded', true);
    this.ring.appendChild(image);
    this.canvas.width = 0;
    this.canvas.height = 0;
    delete this.canvas;
  }
  draw() {
    if (this.renderingState !== _pdf_rendering_queue.RenderingStates.INITIAL) {
      console.error('Must be in new state before drawing');
      return Promise.resolve(undefined);
    }
    this.renderingState = _pdf_rendering_queue.RenderingStates.RUNNING;
    let renderCapability = (0, _pdfjsLib.createPromiseCapability)();
    let finishRenderTask = error => {
      if (renderTask === this.renderTask) {
        this.renderTask = null;
      }
      if (error instanceof _pdfjsLib.RenderingCancelledException) {
        renderCapability.resolve(undefined);
        return;
      }
      this.renderingState = _pdf_rendering_queue.RenderingStates.FINISHED;
      this._convertCanvasToImage();
      if (!error) {
        renderCapability.resolve(undefined);
      } else {
        renderCapability.reject(error);
      }
    };
    let ctx = this._getPageDrawContext();
    let drawViewport = this.viewport.clone({ scale: this.scale });
    let renderContinueCallback = cont => {
      if (!this.renderingQueue.isHighestPriority(this)) {
        this.renderingState = _pdf_rendering_queue.RenderingStates.PAUSED;
        this.resume = () => {
          this.renderingState = _pdf_rendering_queue.RenderingStates.RUNNING;
          cont();
        };
        return;
      }
      cont();
    };
    let renderContext = {
      canvasContext: ctx,
      viewport: drawViewport
    };
    let renderTask = this.renderTask = this.pdfPage.render(renderContext);
    renderTask.onContinue = renderContinueCallback;
    renderTask.promise.then(function () {
      finishRenderTask(null);
    }, function (error) {
      finishRenderTask(error);
    });
    return renderCapability.promise;
  }
  setImage(pageView) {
    if (this.renderingState !== _pdf_rendering_queue.RenderingStates.INITIAL) {
      return;
    }
    let img = pageView.canvas;
    if (!img) {
      return;
    }
    if (!this.pdfPage) {
      this.setPdfPage(pageView.pdfPage);
    }
    this.renderingState = _pdf_rendering_queue.RenderingStates.FINISHED;
    let ctx = this._getPageDrawContext(true);
    let canvas = ctx.canvas;
    if (img.width <= 2 * canvas.width) {
      ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height);
      this._convertCanvasToImage();
      return;
    }
    let reducedWidth = canvas.width << MAX_NUM_SCALING_STEPS;
    let reducedHeight = canvas.height << MAX_NUM_SCALING_STEPS;
    let reducedImage = TempImageFactory.getCanvas(reducedWidth, reducedHeight);
    let reducedImageCtx = reducedImage.getContext('2d');
    while (reducedWidth > img.width || reducedHeight > img.height) {
      reducedWidth >>= 1;
      reducedHeight >>= 1;
    }
    reducedImageCtx.drawImage(img, 0, 0, img.width, img.height, 0, 0, reducedWidth, reducedHeight);
    while (reducedWidth > 2 * canvas.width) {
      reducedImageCtx.drawImage(reducedImage, 0, 0, reducedWidth, reducedHeight, 0, 0, reducedWidth >> 1, reducedHeight >> 1);
      reducedWidth >>= 1;
      reducedHeight >>= 1;
    }
    ctx.drawImage(reducedImage, 0, 0, reducedWidth, reducedHeight, 0, 0, canvas.width, canvas.height);
    this._convertCanvasToImage();
  }
  get pageId() {
    return this.pageLabel !== null ? this.pageLabel : this.id;
  }
  setPageLabel(label) {
    this.pageLabel = typeof label === 'string' ? label : null;
    this.l10n.get('thumb_page_title', { page: this.pageId }, 'Page {{page}}').then(msg => {
      this.anchor.title = msg;
    });
    if (this.renderingState !== _pdf_rendering_queue.RenderingStates.FINISHED) {
      return;
    }
    this.l10n.get('thumb_page_canvas', { page: this.pageId }, 'Thumbnail of Page {{page}}').then(ariaLabel => {
      if (this.image) {
        this.image.setAttribute('aria-label', ariaLabel);
      } else if (this.disableCanvasToImageConversion && this.canvas) {
        this.canvas.setAttribute('aria-label', ariaLabel);
      }
    });
  }
  static cleanup() {
    TempImageFactory.destroyCanvas();
  }
}
exports.PDFThumbnailView = PDFThumbnailView;

/***/ }),
/* 23 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.PDFViewer = undefined;

var _ui_utils = __webpack_require__(0);

var _base_viewer = __webpack_require__(24);

var _pdfjsLib = __webpack_require__(1);

class PDFViewer extends _base_viewer.BaseViewer {
  get _setDocumentViewerElement() {
    return (0, _pdfjsLib.shadow)(this, '_setDocumentViewerElement', this.viewer);
  }
  _scrollIntoView({ pageDiv, pageSpot = null }) {
    (0, _ui_utils.scrollIntoView)(pageDiv, pageSpot);
  }
  _getVisiblePages() {
    if (!this.isInPresentationMode) {
      return (0, _ui_utils.getVisibleElements)(this.container, this._pages, true);
    }
    let currentPage = this._pages[this._currentPageNumber - 1];
    let visible = [{
      id: currentPage.id,
      view: currentPage
    }];
    return {
      first: currentPage,
      last: currentPage,
      views: visible
    };
  }
  update() {
    let visible = this._getVisiblePages();
    let visiblePages = visible.views,
        numVisiblePages = visiblePages.length;
    if (numVisiblePages === 0) {
      return;
    }
    this._resizeBuffer(numVisiblePages);
    this.renderingQueue.renderHighestPriority(visible);
    let currentId = this._currentPageNumber;
    let stillFullyVisible = false;
    for (let i = 0; i < numVisiblePages; ++i) {
      let page = visiblePages[i];
      if (page.percent < 100) {
        break;
      }
      if (page.id === currentId) {
        stillFullyVisible = true;
        break;
      }
    }
    if (!stillFullyVisible) {
      currentId = visiblePages[0].id;
    }
    if (!this.isInPresentationMode) {
      this._setCurrentPageNumber(currentId);
    }
    this._updateLocation(visible.first);
    this.eventBus.dispatch('updateviewarea', {
      source: this,
      location: this._location
    });
  }
}
exports.PDFViewer = PDFViewer;

/***/ }),
/* 24 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.BaseViewer = undefined;

var _pdfjsLib = __webpack_require__(1);

var _ui_utils = __webpack_require__(0);

var _pdf_rendering_queue = __webpack_require__(3);

var _annotation_layer_builder = __webpack_require__(25);

var _dom_events = __webpack_require__(2);

var _pdf_page_view = __webpack_require__(26);

var _pdf_link_service = __webpack_require__(5);

var _text_layer_builder = __webpack_require__(27);

const DEFAULT_CACHE_SIZE = 10;
function PDFPageViewBuffer(size) {
  let data = [];
  this.push = function (view) {
    let i = data.indexOf(view);
    if (i >= 0) {
      data.splice(i, 1);
    }
    data.push(view);
    if (data.length > size) {
      data.shift().destroy();
    }
  };
  this.resize = function (newSize) {
    size = newSize;
    while (data.length > size) {
      data.shift().destroy();
    }
  };
}
function isSameScale(oldScale, newScale) {
  if (newScale === oldScale) {
    return true;
  }
  if (Math.abs(newScale - oldScale) < 1e-15) {
    return true;
  }
  return false;
}
function isPortraitOrientation(size) {
  return size.width <= size.height;
}
class BaseViewer {
  constructor(options) {
    if (this.constructor === BaseViewer) {
      throw new Error('Cannot initialize BaseViewer.');
    }
    this._name = this.constructor.name;
    this.container = options.container;
    this.viewer = options.viewer || options.container.firstElementChild;
    this.eventBus = options.eventBus || (0, _dom_events.getGlobalEventBus)();
    this.linkService = options.linkService || new _pdf_link_service.SimpleLinkService();
    this.downloadManager = options.downloadManager || null;
    this.removePageBorders = options.removePageBorders || false;
    this.textLayerMode = Number.isInteger(options.textLayerMode) ? options.textLayerMode : _ui_utils.TextLayerMode.ENABLE;
    this.enhanceTextSelection = options.enhanceTextSelection || false;
    this.imageResourcesPath = options.imageResourcesPath || '';
    this.renderInteractiveForms = options.renderInteractiveForms || false;
    this.enablePrintAutoRotate = options.enablePrintAutoRotate || false;
    this.renderer = options.renderer || _ui_utils.RendererType.CANVAS;
    this.enableWebGL = options.enableWebGL || false;
    this.useOnlyCssZoom = options.useOnlyCssZoom || false;
    this.maxCanvasPixels = options.maxCanvasPixels;
    this.l10n = options.l10n || _ui_utils.NullL10n;
    this.defaultRenderingQueue = !options.renderingQueue;
    if (this.defaultRenderingQueue) {
      this.renderingQueue = new _pdf_rendering_queue.PDFRenderingQueue();
      this.renderingQueue.setViewer(this);
    } else {
      this.renderingQueue = options.renderingQueue;
    }
    this.scroll = (0, _ui_utils.watchScroll)(this.container, this._scrollUpdate.bind(this));
    this.presentationModeState = _ui_utils.PresentationModeState.UNKNOWN;
    this._resetView();
    if (this.removePageBorders) {
      this.viewer.classList.add('removePageBorders');
    }
  }
  get pagesCount() {
    return this._pages.length;
  }
  getPageView(index) {
    return this._pages[index];
  }
  get pageViewsReady() {
    return this._pageViewsReady;
  }
  get currentPageNumber() {
    return this._currentPageNumber;
  }
  set currentPageNumber(val) {
    if (!Number.isInteger(val)) {
      throw new Error('Invalid page number.');
    }
    if (!this.pdfDocument) {
      return;
    }
    this._setCurrentPageNumber(val, true);
  }
  _setCurrentPageNumber(val, resetCurrentPageView = false) {
    if (this._currentPageNumber === val) {
      if (resetCurrentPageView) {
        this._resetCurrentPageView();
      }
      return;
    }
    if (!(0 < val && val <= this.pagesCount)) {
      console.error(`${this._name}._setCurrentPageNumber: "${val}" is out of bounds.`);
      return;
    }
    let arg = {
      source: this,
      pageNumber: val,
      pageLabel: this._pageLabels && this._pageLabels[val - 1]
    };
    this._currentPageNumber = val;
    this.eventBus.dispatch('pagechanging', arg);
    this.eventBus.dispatch('pagechange', arg);
    if (resetCurrentPageView) {
      this._resetCurrentPageView();
    }
  }
  get currentPageLabel() {
    return this._pageLabels && this._pageLabels[this._currentPageNumber - 1];
  }
  set currentPageLabel(val) {
    let pageNumber = val | 0;
    if (this._pageLabels) {
      let i = this._pageLabels.indexOf(val);
      if (i >= 0) {
        pageNumber = i + 1;
      }
    }
    this.currentPageNumber = pageNumber;
  }
  get currentScale() {
    return this._currentScale !== _ui_utils.UNKNOWN_SCALE ? this._currentScale : _ui_utils.DEFAULT_SCALE;
  }
  set currentScale(val) {
    if (isNaN(val)) {
      throw new Error('Invalid numeric scale');
    }
    if (!this.pdfDocument) {
      return;
    }
    this._setScale(val, false);
  }
  get currentScaleValue() {
    return this._currentScaleValue;
  }
  set currentScaleValue(val) {
    if (!this.pdfDocument) {
      return;
    }
    this._setScale(val, false);
  }
  get pagesRotation() {
    return this._pagesRotation;
  }
  set pagesRotation(rotation) {
    if (!(0, _ui_utils.isValidRotation)(rotation)) {
      throw new Error('Invalid pages rotation angle.');
    }
    if (!this.pdfDocument) {
      return;
    }
    if (this._pagesRotation === rotation) {
      return;
    }
    this._pagesRotation = rotation;
    let pageNumber = this._currentPageNumber;
    for (let i = 0, ii = this._pages.length; i < ii; i++) {
      let pageView = this._pages[i];
      pageView.update(pageView.scale, rotation);
    }
    if (this._currentScaleValue) {
      this._setScale(this._currentScaleValue, true);
    }
    this.eventBus.dispatch('rotationchanging', {
      source: this,
      pagesRotation: rotation,
      pageNumber
    });
    if (this.defaultRenderingQueue) {
      this.update();
    }
  }
  get _setDocumentViewerElement() {
    throw new Error('Not implemented: _setDocumentViewerElement');
  }
  setDocument(pdfDocument) {
    if (this.pdfDocument) {
      this._cancelRendering();
      this._resetView();
    }
    this.pdfDocument = pdfDocument;
    if (!pdfDocument) {
      return;
    }
    let pagesCount = pdfDocument.numPages;
    let pagesCapability = (0, _pdfjsLib.createPromiseCapability)();
    this.pagesPromise = pagesCapability.promise;
    pagesCapability.promise.then(() => {
      this._pageViewsReady = true;
      this.eventBus.dispatch('pagesloaded', {
        source: this,
        pagesCount
      });
    });
    let isOnePageRenderedResolved = false;
    let onePageRenderedCapability = (0, _pdfjsLib.createPromiseCapability)();
    this.onePageRendered = onePageRenderedCapability.promise;
    let bindOnAfterAndBeforeDraw = pageView => {
      pageView.onBeforeDraw = () => {
        this._buffer.push(pageView);
      };
      pageView.onAfterDraw = () => {
        if (!isOnePageRenderedResolved) {
          isOnePageRenderedResolved = true;
          onePageRenderedCapability.resolve();
        }
      };
    };
    let firstPagePromise = pdfDocument.getPage(1);
    this.firstPagePromise = firstPagePromise;
    firstPagePromise.then(pdfPage => {
      let scale = this.currentScale;
      let viewport = pdfPage.getViewport(scale * _ui_utils.CSS_UNITS);
      for (let pageNum = 1; pageNum <= pagesCount; ++pageNum) {
        let textLayerFactory = null;
        if (this.textLayerMode !== _ui_utils.TextLayerMode.DISABLE) {
          textLayerFactory = this;
        }
        let pageView = new _pdf_page_view.PDFPageView({
          container: this._setDocumentViewerElement,
          eventBus: this.eventBus,
          id: pageNum,
          scale,
          defaultViewport: viewport.clone(),
          renderingQueue: this.renderingQueue,
          textLayerFactory,
          textLayerMode: this.textLayerMode,
          annotationLayerFactory: this,
          imageResourcesPath: this.imageResourcesPath,
          renderInteractiveForms: this.renderInteractiveForms,
          renderer: this.renderer,
          enableWebGL: this.enableWebGL,
          useOnlyCssZoom: this.useOnlyCssZoom,
          maxCanvasPixels: this.maxCanvasPixels,
          l10n: this.l10n
        });
        bindOnAfterAndBeforeDraw(pageView);
        this._pages.push(pageView);
      }
      onePageRenderedCapability.promise.then(() => {
        if (_pdfjsLib.PDFJS.disableAutoFetch) {
          pagesCapability.resolve();
          return;
        }
        let getPagesLeft = pagesCount;
        for (let pageNum = 1; pageNum <= pagesCount; ++pageNum) {
          pdfDocument.getPage(pageNum).then(pdfPage => {
            let pageView = this._pages[pageNum - 1];
            if (!pageView.pdfPage) {
              pageView.setPdfPage(pdfPage);
            }
            this.linkService.cachePageRef(pageNum, pdfPage.ref);
            if (--getPagesLeft === 0) {
              pagesCapability.resolve();
            }
          }, reason => {
            console.error(`Unable to get page ${pageNum} to initialize viewer`, reason);
            if (--getPagesLeft === 0) {
              pagesCapability.resolve();
            }
          });
        }
      });
      this.eventBus.dispatch('pagesinit', { source: this });
      if (this.defaultRenderingQueue) {
        this.update();
      }
      if (this.findController) {
        this.findController.resolveFirstPage();
      }
    }).catch(reason => {
      console.error('Unable to initialize viewer', reason);
    });
  }
  setPageLabels(labels) {
    if (!this.pdfDocument) {
      return;
    }
    if (!labels) {
      this._pageLabels = null;
    } else if (!(labels instanceof Array && this.pdfDocument.numPages === labels.length)) {
      this._pageLabels = null;
      console.error(`${this._name}.setPageLabels: Invalid page labels.`);
    } else {
      this._pageLabels = labels;
    }
    for (let i = 0, ii = this._pages.length; i < ii; i++) {
      let pageView = this._pages[i];
      let label = this._pageLabels && this._pageLabels[i];
      pageView.setPageLabel(label);
    }
  }
  _resetView() {
    this._pages = [];
    this._currentPageNumber = 1;
    this._currentScale = _ui_utils.UNKNOWN_SCALE;
    this._currentScaleValue = null;
    this._pageLabels = null;
    this._buffer = new PDFPageViewBuffer(DEFAULT_CACHE_SIZE);
    this._location = null;
    this._pagesRotation = 0;
    this._pagesRequests = [];
    this._pageViewsReady = false;
    this.viewer.textContent = '';
  }
  _scrollUpdate() {
    if (this.pagesCount === 0) {
      return;
    }
    this.update();
  }
  _scrollIntoView({ pageDiv, pageSpot = null, pageNumber = null }) {
    throw new Error('Not implemented: _scrollIntoView');
  }
  _setScaleDispatchEvent(newScale, newValue, preset = false) {
    let arg = {
      source: this,
      scale: newScale,
      presetValue: preset ? newValue : undefined
    };
    this.eventBus.dispatch('scalechanging', arg);
    this.eventBus.dispatch('scalechange', arg);
  }
  _setScaleUpdatePages(newScale, newValue, noScroll = false, preset = false) {
    this._currentScaleValue = newValue.toString();
    if (isSameScale(this._currentScale, newScale)) {
      if (preset) {
        this._setScaleDispatchEvent(newScale, newValue, true);
      }
      return;
    }
    for (let i = 0, ii = this._pages.length; i < ii; i++) {
      this._pages[i].update(newScale);
    }
    this._currentScale = newScale;
    if (!noScroll) {
      let page = this._currentPageNumber,
          dest;
      if (this._location && !(this.isInPresentationMode || this.isChangingPresentationMode)) {
        page = this._location.pageNumber;
        dest = [null, { name: 'XYZ' }, this._location.left, this._location.top, null];
      }
      this.scrollPageIntoView({
        pageNumber: page,
        destArray: dest,
        allowNegativeOffset: true
      });
    }
    this._setScaleDispatchEvent(newScale, newValue, preset);
    if (this.defaultRenderingQueue) {
      this.update();
    }
  }
  _setScale(value, noScroll = false) {
    let scale = parseFloat(value);
    if (scale > 0) {
      this._setScaleUpdatePages(scale, value, noScroll, false);
    } else {
      let currentPage = this._pages[this._currentPageNumber - 1];
      if (!currentPage) {
        return;
      }
      let hPadding = this.isInPresentationMode || this.removePageBorders ? 0 : _ui_utils.SCROLLBAR_PADDING;
      let vPadding = this.isInPresentationMode || this.removePageBorders ? 0 : _ui_utils.VERTICAL_PADDING;
      let pageWidthScale = (this.container.clientWidth - hPadding) / currentPage.width * currentPage.scale;
      let pageHeightScale = (this.container.clientHeight - vPadding) / currentPage.height * currentPage.scale;
      switch (value) {
        case 'page-actual':
          scale = 1;
          break;
        case 'page-width':
          scale = pageWidthScale;
          break;
        case 'page-height':
          scale = pageHeightScale;
          break;
        case 'page-fit':
          scale = Math.min(pageWidthScale, pageHeightScale);
          break;
        case 'auto':
          let isLandscape = currentPage.width > currentPage.height;
          let horizontalScale = isLandscape ? Math.min(pageHeightScale, pageWidthScale) : pageWidthScale;
          scale = Math.min(_ui_utils.MAX_AUTO_SCALE, horizontalScale);
          break;
        default:
          console.error(`${this._name}._setScale: "${value}" is an unknown zoom value.`);
          return;
      }
      this._setScaleUpdatePages(scale, value, noScroll, true);
    }
  }
  _resetCurrentPageView() {
    if (this.isInPresentationMode) {
      this._setScale(this._currentScaleValue, true);
    }
    let pageView = this._pages[this._currentPageNumber - 1];
    this._scrollIntoView({ pageDiv: pageView.div });
  }
  scrollPageIntoView(params) {
    if (!this.pdfDocument) {
      return;
    }
    let pageNumber = params.pageNumber || 0;
    let dest = params.destArray || null;
    let allowNegativeOffset = params.allowNegativeOffset || false;
    if (this.isInPresentationMode || !dest) {
      this._setCurrentPageNumber(pageNumber, true);
      return;
    }
    let pageView = this._pages[pageNumber - 1];
    if (!pageView) {
      console.error(`${this._name}.scrollPageIntoView: Invalid "pageNumber" parameter.`);
      return;
    }
    let x = 0,
        y = 0;
    let width = 0,
        height = 0,
        widthScale,
        heightScale;
    let changeOrientation = pageView.rotation % 180 === 0 ? false : true;
    let pageWidth = (changeOrientation ? pageView.height : pageView.width) / pageView.scale / _ui_utils.CSS_UNITS;
    let pageHeight = (changeOrientation ? pageView.width : pageView.height) / pageView.scale / _ui_utils.CSS_UNITS;
    let scale = 0;
    switch (dest[1].name) {
      case 'XYZ':
        x = dest[2];
        y = dest[3];
        scale = dest[4];
        x = x !== null ? x : 0;
        y = y !== null ? y : pageHeight;
        break;
      case 'Fit':
      case 'FitB':
        scale = 'page-fit';
        break;
      case 'FitH':
      case 'FitBH':
        y = dest[2];
        scale = 'page-width';
        if (y === null && this._location) {
          x = this._location.left;
          y = this._location.top;
        }
        break;
      case 'FitV':
      case 'FitBV':
        x = dest[2];
        width = pageWidth;
        height = pageHeight;
        scale = 'page-height';
        break;
      case 'FitR':
        x = dest[2];
        y = dest[3];
        width = dest[4] - x;
        height = dest[5] - y;
        let hPadding = this.removePageBorders ? 0 : _ui_utils.SCROLLBAR_PADDING;
        let vPadding = this.removePageBorders ? 0 : _ui_utils.VERTICAL_PADDING;
        widthScale = (this.container.clientWidth - hPadding) / width / _ui_utils.CSS_UNITS;
        heightScale = (this.container.clientHeight - vPadding) / height / _ui_utils.CSS_UNITS;
        scale = Math.min(Math.abs(widthScale), Math.abs(heightScale));
        break;
      default:
        console.error(`${this._name}.scrollPageIntoView: "${dest[1].name}" ` + 'is not a valid destination type.');
        return;
    }
    if (scale && scale !== this._currentScale) {
      this.currentScaleValue = scale;
    } else if (this._currentScale === _ui_utils.UNKNOWN_SCALE) {
      this.currentScaleValue = _ui_utils.DEFAULT_SCALE_VALUE;
    }
    if (scale === 'page-fit' && !dest[4]) {
      this._scrollIntoView({
        pageDiv: pageView.div,
        pageNumber
      });
      return;
    }
    let boundingRect = [pageView.viewport.convertToViewportPoint(x, y), pageView.viewport.convertToViewportPoint(x + width, y + height)];
    let left = Math.min(boundingRect[0][0], boundingRect[1][0]);
    let top = Math.min(boundingRect[0][1], boundingRect[1][1]);
    if (!allowNegativeOffset) {
      left = Math.max(left, 0);
      top = Math.max(top, 0);
    }
    this._scrollIntoView({
      pageDiv: pageView.div,
      pageSpot: {
        left,
        top
      },
      pageNumber
    });
  }
  _resizeBuffer(numVisiblePages) {
    let suggestedCacheSize = Math.max(DEFAULT_CACHE_SIZE, 2 * numVisiblePages + 1);
    this._buffer.resize(suggestedCacheSize);
  }
  _updateLocation(firstPage) {
    let currentScale = this._currentScale;
    let currentScaleValue = this._currentScaleValue;
    let normalizedScaleValue = parseFloat(currentScaleValue) === currentScale ? Math.round(currentScale * 10000) / 100 : currentScaleValue;
    let pageNumber = firstPage.id;
    let pdfOpenParams = '#page=' + pageNumber;
    pdfOpenParams += '&zoom=' + normalizedScaleValue;
    let currentPageView = this._pages[pageNumber - 1];
    let container = this.container;
    let topLeft = currentPageView.getPagePoint(container.scrollLeft - firstPage.x, container.scrollTop - firstPage.y);
    let intLeft = Math.round(topLeft[0]);
    let intTop = Math.round(topLeft[1]);
    pdfOpenParams += ',' + intLeft + ',' + intTop;
    this._location = {
      pageNumber,
      scale: normalizedScaleValue,
      top: intTop,
      left: intLeft,
      rotation: this._pagesRotation,
      pdfOpenParams
    };
  }
  update() {
    throw new Error('Not implemented: update');
  }
  containsElement(element) {
    return this.container.contains(element);
  }
  focus() {
    this.container.focus();
  }
  get isInPresentationMode() {
    return this.presentationModeState === _ui_utils.PresentationModeState.FULLSCREEN;
  }
  get isChangingPresentationMode() {
    return this.presentationModeState === _ui_utils.PresentationModeState.CHANGING;
  }
  get isHorizontalScrollbarEnabled() {
    return this.isInPresentationMode ? false : this.container.scrollWidth > this.container.clientWidth;
  }
  _getVisiblePages() {
    throw new Error('Not implemented: _getVisiblePages');
  }
  cleanup() {
    for (let i = 0, ii = this._pages.length; i < ii; i++) {
      if (this._pages[i] && this._pages[i].renderingState !== _pdf_rendering_queue.RenderingStates.FINISHED) {
        this._pages[i].reset();
      }
    }
  }
  _cancelRendering() {
    for (let i = 0, ii = this._pages.length; i < ii; i++) {
      if (this._pages[i]) {
        this._pages[i].cancelRendering();
      }
    }
  }
  _ensurePdfPageLoaded(pageView) {
    if (pageView.pdfPage) {
      return Promise.resolve(pageView.pdfPage);
    }
    let pageNumber = pageView.id;
    if (this._pagesRequests[pageNumber]) {
      return this._pagesRequests[pageNumber];
    }
    let promise = this.pdfDocument.getPage(pageNumber).then(pdfPage => {
      if (!pageView.pdfPage) {
        pageView.setPdfPage(pdfPage);
      }
      this._pagesRequests[pageNumber] = null;
      return pdfPage;
    }).catch(reason => {
      console.error('Unable to get page for page view', reason);
      this._pagesRequests[pageNumber] = null;
    });
    this._pagesRequests[pageNumber] = promise;
    return promise;
  }
  forceRendering(currentlyVisiblePages) {
    let visiblePages = currentlyVisiblePages || this._getVisiblePages();
    let pageView = this.renderingQueue.getHighestPriority(visiblePages, this._pages, this.scroll.down);
    if (pageView) {
      this._ensurePdfPageLoaded(pageView).then(() => {
        this.renderingQueue.renderView(pageView);
      });
      return true;
    }
    return false;
  }
  getPageTextContent(pageIndex) {
    return this.pdfDocument.getPage(pageIndex + 1).then(function (page) {
      return page.getTextContent({ normalizeWhitespace: true });
    });
  }
  createTextLayerBuilder(textLayerDiv, pageIndex, viewport, enhanceTextSelection = false) {
    return new _text_layer_builder.TextLayerBuilder({
      textLayerDiv,
      eventBus: this.eventBus,
      pageIndex,
      viewport,
      findController: this.isInPresentationMode ? null : this.findController,
      enhanceTextSelection: this.isInPresentationMode ? false : enhanceTextSelection
    });
  }
  createAnnotationLayerBuilder(pageDiv, pdfPage, imageResourcesPath = '', renderInteractiveForms = false, l10n = _ui_utils.NullL10n) {
    return new _annotation_layer_builder.AnnotationLayerBuilder({
      pageDiv,
      pdfPage,
      imageResourcesPath,
      renderInteractiveForms,
      linkService: this.linkService,
      downloadManager: this.downloadManager,
      l10n
    });
  }
  setFindController(findController) {
    this.findController = findController;
  }
  get hasEqualPageSizes() {
    let firstPageView = this._pages[0];
    for (let i = 1, ii = this._pages.length; i < ii; ++i) {
      let pageView = this._pages[i];
      if (pageView.width !== firstPageView.width || pageView.height !== firstPageView.height) {
        return false;
      }
    }
    return true;
  }
  getPagesOverview() {
    let pagesOverview = this._pages.map(function (pageView) {
      let viewport = pageView.pdfPage.getViewport(1);
      return {
        width: viewport.width,
        height: viewport.height,
        rotation: viewport.rotation
      };
    });
    if (!this.enablePrintAutoRotate) {
      return pagesOverview;
    }
    let isFirstPagePortrait = isPortraitOrientation(pagesOverview[0]);
    return pagesOverview.map(function (size) {
      if (isFirstPagePortrait === isPortraitOrientation(size)) {
        return size;
      }
      return {
        width: size.height,
        height: size.width,
        rotation: (size.rotation + 90) % 360
      };
    });
  }
}
exports.BaseViewer = BaseViewer;

/***/ }),
/* 25 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.DefaultAnnotationLayerFactory = exports.AnnotationLayerBuilder = undefined;

var _pdfjsLib = __webpack_require__(1);

var _ui_utils = __webpack_require__(0);

var _pdf_link_service = __webpack_require__(5);

class AnnotationLayerBuilder {
  constructor({ pageDiv, pdfPage, linkService, downloadManager, imageResourcesPath = '', renderInteractiveForms = false, l10n = _ui_utils.NullL10n }) {
    this.pageDiv = pageDiv;
    this.pdfPage = pdfPage;
    this.linkService = linkService;
    this.downloadManager = downloadManager;
    this.imageResourcesPath = imageResourcesPath;
    this.renderInteractiveForms = renderInteractiveForms;
    this.l10n = l10n;
    this.div = null;
    this._cancelled = false;
  }
  render(viewport, intent = 'display') {
    this.pdfPage.getAnnotations({ intent }).then(annotations => {
      if (this._cancelled) {
        return;
      }
      let parameters = {
        viewport: viewport.clone({ dontFlip: true }),
        div: this.div,
        annotations,
        page: this.pdfPage,
        imageResourcesPath: this.imageResourcesPath,
        renderInteractiveForms: this.renderInteractiveForms,
        linkService: this.linkService,
        downloadManager: this.downloadManager
      };
      if (this.div) {
        _pdfjsLib.AnnotationLayer.update(parameters);
      } else {
        if (annotations.length === 0) {
          return;
        }
        this.div = document.createElement('div');
        this.div.className = 'annotationLayer';
        this.pageDiv.appendChild(this.div);
        parameters.div = this.div;
        _pdfjsLib.AnnotationLayer.render(parameters);
        this.l10n.translate(this.div);
      }
    });
  }
  cancel() {
    this._cancelled = true;
  }
  hide() {
    if (!this.div) {
      return;
    }
    this.div.setAttribute('hidden', 'true');
  }
}
class DefaultAnnotationLayerFactory {
  createAnnotationLayerBuilder(pageDiv, pdfPage, imageResourcesPath = '', renderInteractiveForms = false, l10n = _ui_utils.NullL10n) {
    return new AnnotationLayerBuilder({
      pageDiv,
      pdfPage,
      imageResourcesPath,
      renderInteractiveForms,
      linkService: new _pdf_link_service.SimpleLinkService(),
      l10n
    });
  }
}
exports.AnnotationLayerBuilder = AnnotationLayerBuilder;
exports.DefaultAnnotationLayerFactory = DefaultAnnotationLayerFactory;

/***/ }),
/* 26 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.PDFPageView = undefined;

var _ui_utils = __webpack_require__(0);

var _pdfjsLib = __webpack_require__(1);

var _dom_events = __webpack_require__(2);

var _pdf_rendering_queue = __webpack_require__(3);

var _viewer_compatibility = __webpack_require__(8);

const MAX_CANVAS_PIXELS = _viewer_compatibility.viewerCompatibilityParams.maxCanvasPixels || 16777216;
class PDFPageView {
  constructor(options) {
    let container = options.container;
    let defaultViewport = options.defaultViewport;
    this.id = options.id;
    this.renderingId = 'page' + this.id;
    this.pdfPage = null;
    this.pageLabel = null;
    this.rotation = 0;
    this.scale = options.scale || _ui_utils.DEFAULT_SCALE;
    this.viewport = defaultViewport;
    this.pdfPageRotate = defaultViewport.rotation;
    this.hasRestrictedScaling = false;
    this.textLayerMode = Number.isInteger(options.textLayerMode) ? options.textLayerMode : _ui_utils.TextLayerMode.ENABLE;
    this.imageResourcesPath = options.imageResourcesPath || '';
    this.renderInteractiveForms = options.renderInteractiveForms || false;
    this.useOnlyCssZoom = options.useOnlyCssZoom || false;
    this.maxCanvasPixels = options.maxCanvasPixels || MAX_CANVAS_PIXELS;
    this.eventBus = options.eventBus || (0, _dom_events.getGlobalEventBus)();
    this.renderingQueue = options.renderingQueue;
    this.textLayerFactory = options.textLayerFactory;
    this.annotationLayerFactory = options.annotationLayerFactory;
    this.renderer = options.renderer || _ui_utils.RendererType.CANVAS;
    this.enableWebGL = options.enableWebGL || false;
    this.l10n = options.l10n || _ui_utils.NullL10n;
    this.paintTask = null;
    this.paintedViewportMap = new WeakMap();
    this.renderingState = _pdf_rendering_queue.RenderingStates.INITIAL;
    this.resume = null;
    this.error = null;
    this.onBeforeDraw = null;
    this.onAfterDraw = null;
    this.annotationLayer = null;
    this.textLayer = null;
    this.zoomLayer = null;
    let div = document.createElement('div');
    div.className = 'page';
    div.style.width = Math.floor(this.viewport.width) + 'px';
    div.style.height = Math.floor(this.viewport.height) + 'px';
    div.setAttribute('data-page-number', this.id);
    this.div = div;
    container.appendChild(div);
  }
  setPdfPage(pdfPage) {
    this.pdfPage = pdfPage;
    this.pdfPageRotate = pdfPage.rotate;
    let totalRotation = (this.rotation + this.pdfPageRotate) % 360;
    this.viewport = pdfPage.getViewport(this.scale * _ui_utils.CSS_UNITS, totalRotation);
    this.stats = pdfPage.stats;
    this.reset();
  }
  destroy() {
    this.reset();
    if (this.pdfPage) {
      this.pdfPage.cleanup();
    }
  }
  _resetZoomLayer(removeFromDOM = false) {
    if (!this.zoomLayer) {
      return;
    }
    let zoomLayerCanvas = this.zoomLayer.firstChild;
    this.paintedViewportMap.delete(zoomLayerCanvas);
    zoomLayerCanvas.width = 0;
    zoomLayerCanvas.height = 0;
    if (removeFromDOM) {
      this.zoomLayer.remove();
    }
    this.zoomLayer = null;
  }
  reset(keepZoomLayer = false, keepAnnotations = false) {
    this.cancelRendering(keepAnnotations);
    let div = this.div;
    div.style.width = Math.floor(this.viewport.width) + 'px';
    div.style.height = Math.floor(this.viewport.height) + 'px';
    let childNodes = div.childNodes;
    let currentZoomLayerNode = keepZoomLayer && this.zoomLayer || null;
    let currentAnnotationNode = keepAnnotations && this.annotationLayer && this.annotationLayer.div || null;
    for (let i = childNodes.length - 1; i >= 0; i--) {
      let node = childNodes[i];
      if (currentZoomLayerNode === node || currentAnnotationNode === node) {
        continue;
      }
      div.removeChild(node);
    }
    div.removeAttribute('data-loaded');
    if (currentAnnotationNode) {
      this.annotationLayer.hide();
    } else if (this.annotationLayer) {
      this.annotationLayer.cancel();
      this.annotationLayer = null;
    }
    if (!currentZoomLayerNode) {
      if (this.canvas) {
        this.paintedViewportMap.delete(this.canvas);
        this.canvas.width = 0;
        this.canvas.height = 0;
        delete this.canvas;
      }
      this._resetZoomLayer();
    }
    if (this.svg) {
      this.paintedViewportMap.delete(this.svg);
      delete this.svg;
    }
    this.loadingIconDiv = document.createElement('div');
    this.loadingIconDiv.className = 'loadingIcon';
    div.appendChild(this.loadingIconDiv);
  }
  update(scale, rotation) {
    this.scale = scale || this.scale;
    if (typeof rotation !== 'undefined') {
      this.rotation = rotation;
    }
    let totalRotation = (this.rotation + this.pdfPageRotate) % 360;
    this.viewport = this.viewport.clone({
      scale: this.scale * _ui_utils.CSS_UNITS,
      rotation: totalRotation
    });
    if (this.svg) {
      this.cssTransform(this.svg, true);
      this.eventBus.dispatch('pagerendered', {
        source: this,
        pageNumber: this.id,
        cssTransform: true
      });
      return;
    }
    let isScalingRestricted = false;
    if (this.canvas && this.maxCanvasPixels > 0) {
      let outputScale = this.outputScale;
      if ((Math.floor(this.viewport.width) * outputScale.sx | 0) * (Math.floor(this.viewport.height) * outputScale.sy | 0) > this.maxCanvasPixels) {
        isScalingRestricted = true;
      }
    }
    if (this.canvas) {
      if (this.useOnlyCssZoom || this.hasRestrictedScaling && isScalingRestricted) {
        this.cssTransform(this.canvas, true);
        this.eventBus.dispatch('pagerendered', {
          source: this,
          pageNumber: this.id,
          cssTransform: true
        });
        return;
      }
      if (!this.zoomLayer && !this.canvas.hasAttribute('hidden')) {
        this.zoomLayer = this.canvas.parentNode;
        this.zoomLayer.style.position = 'absolute';
      }
    }
    if (this.zoomLayer) {
      this.cssTransform(this.zoomLayer.firstChild);
    }
    this.reset(true, true);
  }
  cancelRendering(keepAnnotations = false) {
    if (this.paintTask) {
      this.paintTask.cancel();
      this.paintTask = null;
    }
    this.renderingState = _pdf_rendering_queue.RenderingStates.INITIAL;
    this.resume = null;
    if (this.textLayer) {
      this.textLayer.cancel();
      this.textLayer = null;
    }
    if (!keepAnnotations && this.annotationLayer) {
      this.annotationLayer.cancel();
      this.annotationLayer = null;
    }
  }
  cssTransform(target, redrawAnnotations = false) {
    let width = this.viewport.width;
    let height = this.viewport.height;
    let div = this.div;
    target.style.width = target.parentNode.style.width = div.style.width = Math.floor(width) + 'px';
    target.style.height = target.parentNode.style.height = div.style.height = Math.floor(height) + 'px';
    let relativeRotation = this.viewport.rotation - this.paintedViewportMap.get(target).rotation;
    let absRotation = Math.abs(relativeRotation);
    let scaleX = 1,
        scaleY = 1;
    if (absRotation === 90 || absRotation === 270) {
      scaleX = height / width;
      scaleY = width / height;
    }
    let cssTransform = 'rotate(' + relativeRotation + 'deg) ' + 'scale(' + scaleX + ',' + scaleY + ')';
    target.style.transform = cssTransform;
    if (this.textLayer) {
      let textLayerViewport = this.textLayer.viewport;
      let textRelativeRotation = this.viewport.rotation - textLayerViewport.rotation;
      let textAbsRotation = Math.abs(textRelativeRotation);
      let scale = width / textLayerViewport.width;
      if (textAbsRotation === 90 || textAbsRotation === 270) {
        scale = width / textLayerViewport.height;
      }
      let textLayerDiv = this.textLayer.textLayerDiv;
      let transX, transY;
      switch (textAbsRotation) {
        case 0:
          transX = transY = 0;
          break;
        case 90:
          transX = 0;
          transY = '-' + textLayerDiv.style.height;
          break;
        case 180:
          transX = '-' + textLayerDiv.style.width;
          transY = '-' + textLayerDiv.style.height;
          break;
        case 270:
          transX = '-' + textLayerDiv.style.width;
          transY = 0;
          break;
        default:
          console.error('Bad rotation value.');
          break;
      }
      textLayerDiv.style.transform = 'rotate(' + textAbsRotation + 'deg) ' + 'scale(' + scale + ', ' + scale + ') ' + 'translate(' + transX + ', ' + transY + ')';
      textLayerDiv.style.transformOrigin = '0% 0%';
    }
    if (redrawAnnotations && this.annotationLayer) {
      this.annotationLayer.render(this.viewport, 'display');
    }
  }
  get width() {
    return this.viewport.width;
  }
  get height() {
    return this.viewport.height;
  }
  getPagePoint(x, y) {
    return this.viewport.convertToPdfPoint(x, y);
  }
  draw() {
    if (this.renderingState !== _pdf_rendering_queue.RenderingStates.INITIAL) {
      console.error('Must be in new state before drawing');
      this.reset();
    }
    if (!this.pdfPage) {
      this.renderingState = _pdf_rendering_queue.RenderingStates.FINISHED;
      return Promise.reject(new Error('Page is not loaded'));
    }
    this.renderingState = _pdf_rendering_queue.RenderingStates.RUNNING;
    let pdfPage = this.pdfPage;
    let div = this.div;
    let canvasWrapper = document.createElement('div');
    canvasWrapper.style.width = div.style.width;
    canvasWrapper.style.height = div.style.height;
    canvasWrapper.classList.add('canvasWrapper');
    if (this.annotationLayer && this.annotationLayer.div) {
      div.insertBefore(canvasWrapper, this.annotationLayer.div);
    } else {
      div.appendChild(canvasWrapper);
    }
    let textLayer = null;
    if (this.textLayerMode !== _ui_utils.TextLayerMode.DISABLE && this.textLayerFactory) {
      let textLayerDiv = document.createElement('div');
      textLayerDiv.className = 'textLayer';
      textLayerDiv.style.width = canvasWrapper.style.width;
      textLayerDiv.style.height = canvasWrapper.style.height;
      if (this.annotationLayer && this.annotationLayer.div) {
        div.insertBefore(textLayerDiv, this.annotationLayer.div);
      } else {
        div.appendChild(textLayerDiv);
      }
      textLayer = this.textLayerFactory.createTextLayerBuilder(textLayerDiv, this.id - 1, this.viewport, this.textLayerMode === _ui_utils.TextLayerMode.ENABLE_ENHANCE);
    }
    this.textLayer = textLayer;
    let renderContinueCallback = null;
    if (this.renderingQueue) {
      renderContinueCallback = cont => {
        if (!this.renderingQueue.isHighestPriority(this)) {
          this.renderingState = _pdf_rendering_queue.RenderingStates.PAUSED;
          this.resume = () => {
            this.renderingState = _pdf_rendering_queue.RenderingStates.RUNNING;
            cont();
          };
          return;
        }
        cont();
      };
    }
    let finishPaintTask = error => {
      if (paintTask === this.paintTask) {
        this.paintTask = null;
      }
      if (error instanceof _pdfjsLib.RenderingCancelledException) {
        this.error = null;
        return Promise.resolve(undefined);
      }
      this.renderingState = _pdf_rendering_queue.RenderingStates.FINISHED;
      if (this.loadingIconDiv) {
        div.removeChild(this.loadingIconDiv);
        delete this.loadingIconDiv;
      }
      this._resetZoomLayer(true);
      this.error = error;
      this.stats = pdfPage.stats;
      if (this.onAfterDraw) {
        this.onAfterDraw();
      }
      this.eventBus.dispatch('pagerendered', {
        source: this,
        pageNumber: this.id,
        cssTransform: false
      });
      if (error) {
        return Promise.reject(error);
      }
      return Promise.resolve(undefined);
    };
    let paintTask = this.renderer === _ui_utils.RendererType.SVG ? this.paintOnSvg(canvasWrapper) : this.paintOnCanvas(canvasWrapper);
    paintTask.onRenderContinue = renderContinueCallback;
    this.paintTask = paintTask;
    let resultPromise = paintTask.promise.then(function () {
      return finishPaintTask(null).then(function () {
        if (textLayer) {
          let readableStream = pdfPage.streamTextContent({ normalizeWhitespace: true });
          textLayer.setTextContentStream(readableStream);
          textLayer.render();
        }
      });
    }, function (reason) {
      return finishPaintTask(reason);
    });
    if (this.annotationLayerFactory) {
      if (!this.annotationLayer) {
        this.annotationLayer = this.annotationLayerFactory.createAnnotationLayerBuilder(div, pdfPage, this.imageResourcesPath, this.renderInteractiveForms, this.l10n);
      }
      this.annotationLayer.render(this.viewport, 'display');
    }
    div.setAttribute('data-loaded', true);
    if (this.onBeforeDraw) {
      this.onBeforeDraw();
    }
    return resultPromise;
  }
  paintOnCanvas(canvasWrapper) {
    let renderCapability = (0, _pdfjsLib.createPromiseCapability)();
    let result = {
      promise: renderCapability.promise,
      onRenderContinue(cont) {
        cont();
      },
      cancel() {
        renderTask.cancel();
      }
    };
    let viewport = this.viewport;
    let canvas = document.createElement('canvas');
    canvas.id = this.renderingId;
    canvas.setAttribute('hidden', 'hidden');
    let isCanvasHidden = true;
    let showCanvas = function () {
      if (isCanvasHidden) {
        canvas.removeAttribute('hidden');
        isCanvasHidden = false;
      }
    };
    canvasWrapper.appendChild(canvas);
    this.canvas = canvas;
    canvas.mozOpaque = true;
    let ctx = canvas.getContext('2d', { alpha: false });
    let outputScale = (0, _ui_utils.getOutputScale)(ctx);
    this.outputScale = outputScale;
    if (this.useOnlyCssZoom) {
      let actualSizeViewport = viewport.clone({ scale: _ui_utils.CSS_UNITS });
      outputScale.sx *= actualSizeViewport.width / viewport.width;
      outputScale.sy *= actualSizeViewport.height / viewport.height;
      outputScale.scaled = true;
    }
    if (this.maxCanvasPixels > 0) {
      let pixelsInViewport = viewport.width * viewport.height;
      let maxScale = Math.sqrt(this.maxCanvasPixels / pixelsInViewport);
      if (outputScale.sx > maxScale || outputScale.sy > maxScale) {
        outputScale.sx = maxScale;
        outputScale.sy = maxScale;
        outputScale.scaled = true;
        this.hasRestrictedScaling = true;
      } else {
        this.hasRestrictedScaling = false;
      }
    }
    let sfx = (0, _ui_utils.approximateFraction)(outputScale.sx);
    let sfy = (0, _ui_utils.approximateFraction)(outputScale.sy);
    canvas.width = (0, _ui_utils.roundToDivide)(viewport.width * outputScale.sx, sfx[0]);
    canvas.height = (0, _ui_utils.roundToDivide)(viewport.height * outputScale.sy, sfy[0]);
    canvas.style.width = (0, _ui_utils.roundToDivide)(viewport.width, sfx[1]) + 'px';
    canvas.style.height = (0, _ui_utils.roundToDivide)(viewport.height, sfy[1]) + 'px';
    this.paintedViewportMap.set(canvas, viewport);
    let transform = !outputScale.scaled ? null : [outputScale.sx, 0, 0, outputScale.sy, 0, 0];
    let renderContext = {
      canvasContext: ctx,
      transform,
      viewport: this.viewport,
      enableWebGL: this.enableWebGL,
      renderInteractiveForms: this.renderInteractiveForms
    };
    let renderTask = this.pdfPage.render(renderContext);
    renderTask.onContinue = function (cont) {
      showCanvas();
      if (result.onRenderContinue) {
        result.onRenderContinue(cont);
      } else {
        cont();
      }
    };
    renderTask.promise.then(function () {
      showCanvas();
      renderCapability.resolve(undefined);
    }, function (error) {
      showCanvas();
      renderCapability.reject(error);
    });
    return result;
  }
  paintOnSvg(wrapper) {
    return {
      promise: Promise.reject(new Error('SVG rendering is not supported.')),
      onRenderContinue(cont) {},
      cancel() {}
    };
  }
  setPageLabel(label) {
    this.pageLabel = typeof label === 'string' ? label : null;
    if (this.pageLabel !== null) {
      this.div.setAttribute('data-page-label', this.pageLabel);
    } else {
      this.div.removeAttribute('data-page-label');
    }
  }
}
exports.PDFPageView = PDFPageView;

/***/ }),
/* 27 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.DefaultTextLayerFactory = exports.TextLayerBuilder = undefined;

var _dom_events = __webpack_require__(2);

var _pdfjsLib = __webpack_require__(1);

const EXPAND_DIVS_TIMEOUT = 300;
class TextLayerBuilder {
  constructor({ textLayerDiv, eventBus, pageIndex, viewport, findController = null, enhanceTextSelection = false }) {
    this.textLayerDiv = textLayerDiv;
    this.eventBus = eventBus || (0, _dom_events.getGlobalEventBus)();
    this.textContent = null;
    this.textContentItemsStr = [];
    this.textContentStream = null;
    this.renderingDone = false;
    this.pageIdx = pageIndex;
    this.pageNumber = this.pageIdx + 1;
    this.matches = [];
    this.viewport = viewport;
    this.textDivs = [];
    this.findController = findController;
    this.textLayerRenderTask = null;
    this.enhanceTextSelection = enhanceTextSelection;
    this._bindMouse();
  }
  _finishRendering() {
    this.renderingDone = true;
    if (!this.enhanceTextSelection) {
      let endOfContent = document.createElement('div');
      endOfContent.className = 'endOfContent';
      this.textLayerDiv.appendChild(endOfContent);
    }
    this.eventBus.dispatch('textlayerrendered', {
      source: this,
      pageNumber: this.pageNumber,
      numTextDivs: this.textDivs.length
    });
  }
  render(timeout = 0) {
    if (!(this.textContent || this.textContentStream) || this.renderingDone) {
      return;
    }
    this.cancel();
    this.textDivs = [];
    let textLayerFrag = document.createDocumentFragment();
    this.textLayerRenderTask = (0, _pdfjsLib.renderTextLayer)({
      textContent: this.textContent,
      textContentStream: this.textContentStream,
      container: textLayerFrag,
      viewport: this.viewport,
      textDivs: this.textDivs,
      textContentItemsStr: this.textContentItemsStr,
      timeout,
      enhanceTextSelection: this.enhanceTextSelection
    });
    this.textLayerRenderTask.promise.then(() => {
      this.textLayerDiv.appendChild(textLayerFrag);
      this._finishRendering();
      this.updateMatches();
    }, function (reason) {});
  }
  cancel() {
    if (this.textLayerRenderTask) {
      this.textLayerRenderTask.cancel();
      this.textLayerRenderTask = null;
    }
  }
  setTextContentStream(readableStream) {
    this.cancel();
    this.textContentStream = readableStream;
  }
  setTextContent(textContent) {
    this.cancel();
    this.textContent = textContent;
  }
  convertMatches(matches, matchesLength) {
    let i = 0;
    let iIndex = 0;
    let textContentItemsStr = this.textContentItemsStr;
    let end = textContentItemsStr.length - 1;
    let queryLen = this.findController === null ? 0 : this.findController.state.query.length;
    let ret = [];
    if (!matches) {
      return ret;
    }
    for (let m = 0, len = matches.length; m < len; m++) {
      let matchIdx = matches[m];
      while (i !== end && matchIdx >= iIndex + textContentItemsStr[i].length) {
        iIndex += textContentItemsStr[i].length;
        i++;
      }
      if (i === textContentItemsStr.length) {
        console.error('Could not find a matching mapping');
      }
      let match = {
        begin: {
          divIdx: i,
          offset: matchIdx - iIndex
        }
      };
      if (matchesLength) {
        matchIdx += matchesLength[m];
      } else {
        matchIdx += queryLen;
      }
      while (i !== end && matchIdx > iIndex + textContentItemsStr[i].length) {
        iIndex += textContentItemsStr[i].length;
        i++;
      }
      match.end = {
        divIdx: i,
        offset: matchIdx - iIndex
      };
      ret.push(match);
    }
    return ret;
  }
  renderMatches(matches) {
    if (matches.length === 0) {
      return;
    }
    let textContentItemsStr = this.textContentItemsStr;
    let textDivs = this.textDivs;
    let prevEnd = null;
    let pageIdx = this.pageIdx;
    let isSelectedPage = this.findController === null ? false : pageIdx === this.findController.selected.pageIdx;
    let selectedMatchIdx = this.findController === null ? -1 : this.findController.selected.matchIdx;
    let highlightAll = this.findController === null ? false : this.findController.state.highlightAll;
    let infinity = {
      divIdx: -1,
      offset: undefined
    };
    function beginText(begin, className) {
      let divIdx = begin.divIdx;
      textDivs[divIdx].textContent = '';
      appendTextToDiv(divIdx, 0, begin.offset, className);
    }
    function appendTextToDiv(divIdx, fromOffset, toOffset, className) {
      let div = textDivs[divIdx];
      let content = textContentItemsStr[divIdx].substring(fromOffset, toOffset);
      let node = document.createTextNode(content);
      if (className) {
        let span = document.createElement('span');
        span.className = className;
        span.appendChild(node);
        div.appendChild(span);
        return;
      }
      div.appendChild(node);
    }
    let i0 = selectedMatchIdx,
        i1 = i0 + 1;
    if (highlightAll) {
      i0 = 0;
      i1 = matches.length;
    } else if (!isSelectedPage) {
      return;
    }
    for (let i = i0; i < i1; i++) {
      let match = matches[i];
      let begin = match.begin;
      let end = match.end;
      let isSelected = isSelectedPage && i === selectedMatchIdx;
      let highlightSuffix = isSelected ? ' selected' : '';
      if (this.findController) {
        this.findController.updateMatchPosition(pageIdx, i, textDivs, begin.divIdx);
      }
      if (!prevEnd || begin.divIdx !== prevEnd.divIdx) {
        if (prevEnd !== null) {
          appendTextToDiv(prevEnd.divIdx, prevEnd.offset, infinity.offset);
        }
        beginText(begin);
      } else {
        appendTextToDiv(prevEnd.divIdx, prevEnd.offset, begin.offset);
      }
      if (begin.divIdx === end.divIdx) {
        appendTextToDiv(begin.divIdx, begin.offset, end.offset, 'highlight' + highlightSuffix);
      } else {
        appendTextToDiv(begin.divIdx, begin.offset, infinity.offset, 'highlight begin' + highlightSuffix);
        for (let n0 = begin.divIdx + 1, n1 = end.divIdx; n0 < n1; n0++) {
          textDivs[n0].className = 'highlight middle' + highlightSuffix;
        }
        beginText(end, 'highlight end' + highlightSuffix);
      }
      prevEnd = end;
    }
    if (prevEnd) {
      appendTextToDiv(prevEnd.divIdx, prevEnd.offset, infinity.offset);
    }
  }
  updateMatches() {
    if (!this.renderingDone) {
      return;
    }
    let matches = this.matches;
    let textDivs = this.textDivs;
    let textContentItemsStr = this.textContentItemsStr;
    let clearedUntilDivIdx = -1;
    for (let i = 0, len = matches.length; i < len; i++) {
      let match = matches[i];
      let begin = Math.max(clearedUntilDivIdx, match.begin.divIdx);
      for (let n = begin, end = match.end.divIdx; n <= end; n++) {
        let div = textDivs[n];
        div.textContent = textContentItemsStr[n];
        div.className = '';
      }
      clearedUntilDivIdx = match.end.divIdx + 1;
    }
    if (this.findController === null || !this.findController.active) {
      return;
    }
    let pageMatches, pageMatchesLength;
    if (this.findController !== null) {
      pageMatches = this.findController.pageMatches[this.pageIdx] || null;
      pageMatchesLength = this.findController.pageMatchesLength ? this.findController.pageMatchesLength[this.pageIdx] || null : null;
    }
    this.matches = this.convertMatches(pageMatches, pageMatchesLength);
    this.renderMatches(this.matches);
  }
  _bindMouse() {
    let div = this.textLayerDiv;
    let expandDivsTimer = null;
    div.addEventListener('mousedown', evt => {
      if (this.enhanceTextSelection && this.textLayerRenderTask) {
        this.textLayerRenderTask.expandTextDivs(true);
        return;
      }
      let end = div.querySelector('.endOfContent');
      if (!end) {
        return;
      }
      end.classList.add('active');
    });
    div.addEventListener('mouseup', () => {
      if (this.enhanceTextSelection && this.textLayerRenderTask) {
        this.textLayerRenderTask.expandTextDivs(false);
        return;
      }
      let end = div.querySelector('.endOfContent');
      if (!end) {
        return;
      }
      end.classList.remove('active');
    });
  }
}
class DefaultTextLayerFactory {
  createTextLayerBuilder(textLayerDiv, pageIndex, viewport, enhanceTextSelection = false) {
    return new TextLayerBuilder({
      textLayerDiv,
      pageIndex,
      viewport,
      enhanceTextSelection
    });
  }
}
exports.TextLayerBuilder = TextLayerBuilder;
exports.DefaultTextLayerFactory = DefaultTextLayerFactory;

/***/ }),
/* 28 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.SecondaryToolbar = undefined;

var _pdf_cursor_tools = __webpack_require__(6);

var _ui_utils = __webpack_require__(0);

class SecondaryToolbar {
  constructor(options, mainContainer, eventBus) {
    this.toolbar = options.toolbar;
    this.toggleButton = options.toggleButton;
    this.toolbarButtonContainer = options.toolbarButtonContainer;
    this.buttons = [{
      element: options.presentationModeButton,
      eventName: 'presentationmode',
      close: true
    }, {
      element: options.openFileButton,
      eventName: 'openfile',
      close: true
    }, {
      element: options.printButton,
      eventName: 'print',
      close: true
    }, {
      element: options.downloadButton,
      eventName: 'download',
      close: true
    }, {
      element: options.viewBookmarkButton,
      eventName: null,
      close: true
    }, {
      element: options.firstPageButton,
      eventName: 'firstpage',
      close: true
    }, {
      element: options.lastPageButton,
      eventName: 'lastpage',
      close: true
    }, {
      element: options.pageRotateCwButton,
      eventName: 'rotatecw',
      close: false
    }, {
      element: options.pageRotateCcwButton,
      eventName: 'rotateccw',
      close: false
    }, {
      element: options.cursorSelectToolButton,
      eventName: 'switchcursortool',
      eventDetails: { tool: _pdf_cursor_tools.CursorTool.SELECT },
      close: true
    }, {
      element: options.cursorHandToolButton,
      eventName: 'switchcursortool',
      eventDetails: { tool: _pdf_cursor_tools.CursorTool.HAND },
      close: true
    }, {
      element: options.documentPropertiesButton,
      eventName: 'documentproperties',
      close: true
    }];
    this.items = {
      firstPage: options.firstPageButton,
      lastPage: options.lastPageButton,
      pageRotateCw: options.pageRotateCwButton,
      pageRotateCcw: options.pageRotateCcwButton
    };
    this.mainContainer = mainContainer;
    this.eventBus = eventBus;
    this.opened = false;
    this.containerHeight = null;
    this.previousContainerHeight = null;
    this.reset();
    this._bindClickListeners();
    this._bindCursorToolsListener(options);
    this.eventBus.on('resize', this._setMaxHeight.bind(this));
  }
  get isOpen() {
    return this.opened;
  }
  setPageNumber(pageNumber) {
    this.pageNumber = pageNumber;
    this._updateUIState();
  }
  setPagesCount(pagesCount) {
    this.pagesCount = pagesCount;
    this._updateUIState();
  }
  reset() {
    this.pageNumber = 0;
    this.pagesCount = 0;
    this._updateUIState();
  }
  _updateUIState() {
    this.items.firstPage.disabled = this.pageNumber <= 1;
    this.items.lastPage.disabled = this.pageNumber >= this.pagesCount;
    this.items.pageRotateCw.disabled = this.pagesCount === 0;
    this.items.pageRotateCcw.disabled = this.pagesCount === 0;
  }
  _bindClickListeners() {
    this.toggleButton.addEventListener('click', this.toggle.bind(this));
    for (let button in this.buttons) {
      let { element, eventName, close, eventDetails } = this.buttons[button];
      element.addEventListener('click', evt => {
        if (eventName !== null) {
          let details = { source: this };
          for (let property in eventDetails) {
            details[property] = eventDetails[property];
          }
          this.eventBus.dispatch(eventName, details);
        }
        if (close) {
          this.close();
        }
      });
    }
  }
  _bindCursorToolsListener(buttons) {
    this.eventBus.on('cursortoolchanged', function (evt) {
      buttons.cursorSelectToolButton.classList.remove('toggled');
      buttons.cursorHandToolButton.classList.remove('toggled');
      switch (evt.tool) {
        case _pdf_cursor_tools.CursorTool.SELECT:
          buttons.cursorSelectToolButton.classList.add('toggled');
          break;
        case _pdf_cursor_tools.CursorTool.HAND:
          buttons.cursorHandToolButton.classList.add('toggled');
          break;
      }
    });
  }
  open() {
    if (this.opened) {
      return;
    }
    this.opened = true;
    this._setMaxHeight();
    this.toggleButton.classList.add('toggled');
    this.toolbar.classList.remove('hidden');
  }
  close() {
    if (!this.opened) {
      return;
    }
    this.opened = false;
    this.toolbar.classList.add('hidden');
    this.toggleButton.classList.remove('toggled');
  }
  toggle() {
    if (this.opened) {
      this.close();
    } else {
      this.open();
    }
  }
  _setMaxHeight() {
    if (!this.opened) {
      return;
    }
    this.containerHeight = this.mainContainer.clientHeight;
    if (this.containerHeight === this.previousContainerHeight) {
      return;
    }
    this.toolbarButtonContainer.setAttribute('style', 'max-height: ' + (this.containerHeight - _ui_utils.SCROLLBAR_PADDING) + 'px;');
    this.previousContainerHeight = this.containerHeight;
  }
}
exports.SecondaryToolbar = SecondaryToolbar;

/***/ }),
/* 29 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.Toolbar = undefined;

var _ui_utils = __webpack_require__(0);

const PAGE_NUMBER_LOADING_INDICATOR = 'visiblePageIsLoading';
const SCALE_SELECT_CONTAINER_PADDING = 8;
const SCALE_SELECT_PADDING = 22;
class Toolbar {
  constructor(options, mainContainer, eventBus, l10n = _ui_utils.NullL10n) {
    this.toolbar = options.container;
    this.mainContainer = mainContainer;
    this.eventBus = eventBus;
    this.l10n = l10n;
    this.items = options;
    this._wasLocalized = false;
    this.reset();
    this._bindListeners();
  }
  setPageNumber(pageNumber, pageLabel) {
    this.pageNumber = pageNumber;
    this.pageLabel = pageLabel;
    this._updateUIState(false);
  }
  setPagesCount(pagesCount, hasPageLabels) {
    this.pagesCount = pagesCount;
    this.hasPageLabels = hasPageLabels;
    this._updateUIState(true);
  }
  setPageScale(pageScaleValue, pageScale) {
    this.pageScaleValue = pageScaleValue;
    this.pageScale = pageScale;
    this._updateUIState(false);
  }
  reset() {
    this.pageNumber = 0;
    this.pageLabel = null;
    this.hasPageLabels = false;
    this.pagesCount = 0;
    this.pageScaleValue = _ui_utils.DEFAULT_SCALE_VALUE;
    this.pageScale = _ui_utils.DEFAULT_SCALE;
    this._updateUIState(true);
  }
  _bindListeners() {
    let { eventBus, items } = this;
    let self = this;
    items.previous.addEventListener('click', function () {
      eventBus.dispatch('previouspage');
    });
    items.next.addEventListener('click', function () {
      eventBus.dispatch('nextpage');
    });
    items.zoomIn.addEventListener('click', function () {
      eventBus.dispatch('zoomin');
    });
    items.zoomOut.addEventListener('click', function () {
      eventBus.dispatch('zoomout');
    });
    items.pageNumber.addEventListener('click', function () {
      this.select();
    });
    items.pageNumber.addEventListener('change', function () {
      eventBus.dispatch('pagenumberchanged', {
        source: self,
        value: this.value
      });
    });
    items.scaleSelect.addEventListener('change', function () {
      if (this.value === 'custom') {
        return;
      }
      eventBus.dispatch('scalechanged', {
        source: self,
        value: this.value
      });
    });
    items.presentationModeButton.addEventListener('click', function () {
      eventBus.dispatch('presentationmode');
    });
    items.openFile.addEventListener('click', function () {
      eventBus.dispatch('openfile');
    });
    items.print.addEventListener('click', function () {
      eventBus.dispatch('print');
    });
    items.download.addEventListener('click', function () {
      eventBus.dispatch('download');
    });
    items.scaleSelect.oncontextmenu = _ui_utils.noContextMenuHandler;
    eventBus.on('localized', () => {
      this._localized();
    });
  }
  _localized() {
    this._wasLocalized = true;
    this._adjustScaleWidth();
    this._updateUIState(true);
  }
  _updateUIState(resetNumPages = false) {
    if (!this._wasLocalized) {
      return;
    }
    let { pageNumber, pagesCount, items } = this;
    let scaleValue = (this.pageScaleValue || this.pageScale).toString();
    let scale = this.pageScale;
    if (resetNumPages) {
      if (this.hasPageLabels) {
        items.pageNumber.type = 'text';
      } else {
        items.pageNumber.type = 'number';
        this.l10n.get('of_pages', { pagesCount }, 'of {{pagesCount}}').then(msg => {
          items.numPages.textContent = msg;
        });
      }
      items.pageNumber.max = pagesCount;
    }
    if (this.hasPageLabels) {
      items.pageNumber.value = this.pageLabel;
      this.l10n.get('page_of_pages', {
        pageNumber,
        pagesCount
      }, '({{pageNumber}} of {{pagesCount}})').then(msg => {
        items.numPages.textContent = msg;
      });
    } else {
      items.pageNumber.value = pageNumber;
    }
    items.previous.disabled = pageNumber <= 1;
    items.next.disabled = pageNumber >= pagesCount;
    items.zoomOut.disabled = scale <= _ui_utils.MIN_SCALE;
    items.zoomIn.disabled = scale >= _ui_utils.MAX_SCALE;
    let customScale = Math.round(scale * 10000) / 100;
    this.l10n.get('page_scale_percent', { scale: customScale }, '{{scale}}%').then(msg => {
      let options = items.scaleSelect.options;
      let predefinedValueFound = false;
      for (let i = 0, ii = options.length; i < ii; i++) {
        let option = options[i];
        if (option.value !== scaleValue) {
          option.selected = false;
          continue;
        }
        option.selected = true;
        predefinedValueFound = true;
      }
      if (!predefinedValueFound) {
        items.customScaleOption.textContent = msg;
        items.customScaleOption.selected = true;
      }
    });
  }
  updateLoadingIndicatorState(loading = false) {
    let pageNumberInput = this.items.pageNumber;
    if (loading) {
      pageNumberInput.classList.add(PAGE_NUMBER_LOADING_INDICATOR);
    } else {
      pageNumberInput.classList.remove(PAGE_NUMBER_LOADING_INDICATOR);
    }
  }
  _adjustScaleWidth() {
    let container = this.items.scaleSelectContainer;
    let select = this.items.scaleSelect;
    _ui_utils.animationStarted.then(function () {
      if (container.clientWidth === 0) {
        container.setAttribute('style', 'display: inherit;');
      }
      if (container.clientWidth > 0) {
        select.setAttribute('style', 'min-width: inherit;');
        let width = select.clientWidth + SCALE_SELECT_CONTAINER_PADDING;
        select.setAttribute('style', 'min-width: ' + (width + SCALE_SELECT_PADDING) + 'px;');
        container.setAttribute('style', 'min-width: ' + width + 'px; ' + 'max-width: ' + width + 'px;');
      }
    });
  }
}
exports.Toolbar = Toolbar;

/***/ }),
/* 30 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
const DEFAULT_VIEW_HISTORY_CACHE_SIZE = 20;
class ViewHistory {
  constructor(fingerprint, cacheSize = DEFAULT_VIEW_HISTORY_CACHE_SIZE) {
    this.fingerprint = fingerprint;
    this.cacheSize = cacheSize;
    this._initializedPromise = this._readFromStorage().then(databaseStr => {
      let database = JSON.parse(databaseStr || '{}');
      if (!('files' in database)) {
        database.files = [];
      }
      if (database.files.length >= this.cacheSize) {
        database.files.shift();
      }
      let index;
      for (let i = 0, length = database.files.length; i < length; i++) {
        let branch = database.files[i];
        if (branch.fingerprint === this.fingerprint) {
          index = i;
          break;
        }
      }
      if (typeof index !== 'number') {
        index = database.files.push({ fingerprint: this.fingerprint }) - 1;
      }
      this.file = database.files[index];
      this.database = database;
    });
  }
  _writeToStorage() {
    return new Promise(resolve => {
      let databaseStr = JSON.stringify(this.database);
      sessionStorage.setItem('pdfjs.history', databaseStr);
      resolve();
    });
  }
  _readFromStorage() {
    return new Promise(function (resolve) {
      resolve(sessionStorage.getItem('pdfjs.history'));
    });
  }
  set(name, val) {
    return this._initializedPromise.then(() => {
      this.file[name] = val;
      return this._writeToStorage();
    });
  }
  setMultiple(properties) {
    return this._initializedPromise.then(() => {
      for (let name in properties) {
        this.file[name] = properties[name];
      }
      return this._writeToStorage();
    });
  }
  get(name, defaultValue) {
    return this._initializedPromise.then(() => {
      let val = this.file[name];
      return val !== undefined ? val : defaultValue;
    });
  }
  getMultiple(properties) {
    return this._initializedPromise.then(() => {
      let values = Object.create(null);
      for (let name in properties) {
        let val = this.file[name];
        values[name] = val !== undefined ? val : properties[name];
      }
      return values;
    });
  }
}
exports.ViewHistory = ViewHistory;

/***/ }),
/* 31 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.FirefoxCom = exports.DownloadManager = undefined;

__webpack_require__(32);

var _pdfjsLib = __webpack_require__(1);

var _preferences = __webpack_require__(33);

var _app = __webpack_require__(4);

;
let FirefoxCom = function FirefoxComClosure() {
  return {
    requestSync(action, data) {
      let request = document.createTextNode('');
      document.documentElement.appendChild(request);
      let sender = document.createEvent('CustomEvent');
      sender.initCustomEvent('pdf.js.message', true, false, {
        action,
        data,
        sync: true
      });
      request.dispatchEvent(sender);
      let response = sender.detail.response;
      document.documentElement.removeChild(request);
      return response;
    },
    request(action, data, callback) {
      let request = document.createTextNode('');
      if (callback) {
        document.addEventListener('pdf.js.response', function listener(event) {
          let node = event.target;
          let response = event.detail.response;
          document.documentElement.removeChild(node);
          document.removeEventListener('pdf.js.response', listener);
          return callback(response);
        });
      }
      document.documentElement.appendChild(request);
      let sender = document.createEvent('CustomEvent');
      sender.initCustomEvent('pdf.js.message', true, false, {
        action,
        data,
        sync: false,
        responseExpected: !!callback
      });
      return request.dispatchEvent(sender);
    }
  };
}();
class DownloadManager {
  downloadUrl(url, filename) {
    FirefoxCom.request('download', {
      originalUrl: url,
      filename
    });
  }
  downloadData(data, filename, contentType) {
    let blobUrl = (0, _pdfjsLib.createObjectURL)(data, contentType, false);
    FirefoxCom.request('download', {
      blobUrl,
      originalUrl: blobUrl,
      filename,
      isAttachment: true
    });
  }
  download(blob, url, filename) {
    let blobUrl = URL.createObjectURL(blob);
    let onResponse = err => {
      if (err && this.onerror) {
        this.onerror(err);
      }
      URL.revokeObjectURL(blobUrl);
    };
    FirefoxCom.request('download', {
      blobUrl,
      originalUrl: url,
      filename
    }, onResponse);
  }
}
class FirefoxPreferences extends _preferences.BasePreferences {
  _writeToStorage(prefObj) {
    return new Promise(function (resolve) {
      FirefoxCom.request('setPreferences', prefObj, resolve);
    });
  }
  _readFromStorage(prefObj) {
    return new Promise(function (resolve) {
      FirefoxCom.request('getPreferences', prefObj, function (prefStr) {
        let readPrefs = JSON.parse(prefStr);
        resolve(readPrefs);
      });
    });
  }
}
class MozL10n {
  constructor(mozL10n) {
    this.mozL10n = mozL10n;
  }
  getDirection() {
    return Promise.resolve(this.mozL10n.getDirection());
  }
  get(property, args, fallback) {
    return Promise.resolve(this.mozL10n.get(property, args, fallback));
  }
  translate(element) {
    this.mozL10n.translate(element);
    return Promise.resolve();
  }
}
(function listenFindEvents() {
  const events = ['find', 'findagain', 'findhighlightallchange', 'findcasesensitivitychange'];
  let handleEvent = function (evt) {
    if (!_app.PDFViewerApplication.initialized) {
      return;
    }
    _app.PDFViewerApplication.eventBus.dispatch('find', {
      source: window,
      type: evt.type.substring('find'.length),
      query: evt.detail.query,
      phraseSearch: true,
      caseSensitive: !!evt.detail.caseSensitive,
      highlightAll: !!evt.detail.highlightAll,
      findPrevious: !!evt.detail.findPrevious
    });
  };
  for (let i = 0, len = events.length; i < len; i++) {
    window.addEventListener(events[i], handleEvent);
  }
})();
function FirefoxComDataRangeTransport(length, initialData) {
  _pdfjsLib.PDFDataRangeTransport.call(this, length, initialData);
}
FirefoxComDataRangeTransport.prototype = Object.create(_pdfjsLib.PDFDataRangeTransport.prototype);
FirefoxComDataRangeTransport.prototype.requestDataRange = function FirefoxComDataRangeTransport_requestDataRange(begin, end) {
  FirefoxCom.request('requestDataRange', {
    begin,
    end
  });
};
FirefoxComDataRangeTransport.prototype.abort = function FirefoxComDataRangeTransport_abort() {
  FirefoxCom.requestSync('abortLoading', null);
};
_app.PDFViewerApplication.externalServices = {
  updateFindControlState(data) {
    FirefoxCom.request('updateFindControlState', data);
  },
  initPassiveLoading(callbacks) {
    let pdfDataRangeTransport;
    window.addEventListener('message', function windowMessage(e) {
      if (e.source !== null) {
        console.warn('Rejected untrusted message from ' + e.origin);
        return;
      }
      let args = e.data;
      if (typeof args !== 'object' || !('pdfjsLoadAction' in args)) {
        return;
      }
      switch (args.pdfjsLoadAction) {
        case 'supportsRangedLoading':
          pdfDataRangeTransport = new FirefoxComDataRangeTransport(args.length, args.data);
          callbacks.onOpenWithTransport(args.pdfUrl, args.length, pdfDataRangeTransport);
          break;
        case 'range':
          pdfDataRangeTransport.onDataRange(args.begin, args.chunk);
          break;
        case 'rangeProgress':
          pdfDataRangeTransport.onDataProgress(args.loaded);
          break;
        case 'progressiveRead':
          pdfDataRangeTransport.onDataProgressiveRead(args.chunk);
          break;
        case 'progress':
          callbacks.onProgress(args.loaded, args.total);
          break;
        case 'complete':
          if (!args.data) {
            callbacks.onError(args.errorCode);
            break;
          }
          callbacks.onOpenWithData(args.data);
          break;
      }
    });
    FirefoxCom.requestSync('initPassiveLoading', null);
  },
  fallback(data, callback) {
    FirefoxCom.request('fallback', data, callback);
  },
  reportTelemetry(data) {
    FirefoxCom.request('reportTelemetry', JSON.stringify(data));
  },
  createDownloadManager() {
    return new DownloadManager();
  },
  createPreferences() {
    return new FirefoxPreferences();
  },
  createL10n(options) {
    let mozL10n = document.mozL10n;
    return new MozL10n(mozL10n);
  },
  get supportsIntegratedFind() {
    let support = FirefoxCom.requestSync('supportsIntegratedFind');
    return (0, _pdfjsLib.shadow)(this, 'supportsIntegratedFind', support);
  },
  get supportsDocumentFonts() {
    let support = FirefoxCom.requestSync('supportsDocumentFonts');
    return (0, _pdfjsLib.shadow)(this, 'supportsDocumentFonts', support);
  },
  get supportsDocumentColors() {
    let support = FirefoxCom.requestSync('supportsDocumentColors');
    return (0, _pdfjsLib.shadow)(this, 'supportsDocumentColors', support);
  },
  get supportedMouseWheelZoomModifierKeys() {
    let support = FirefoxCom.requestSync('supportedMouseWheelZoomModifierKeys');
    return (0, _pdfjsLib.shadow)(this, 'supportedMouseWheelZoomModifierKeys', support);
  }
};
document.mozL10n.setExternalLocalizerServices({
  getLocale() {
    return FirefoxCom.requestSync('getLocale', null);
  },
  getStrings(key) {
    return FirefoxCom.requestSync('getStrings', key);
  }
});
exports.DownloadManager = DownloadManager;
exports.FirefoxCom = FirefoxCom;

/***/ }),
/* 32 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


(function (window) {
  var gLanguage = "";
  var gExternalLocalizerServices = null;
  var gReadyState = "loading";
  function getL10nData(key) {
    var response = gExternalLocalizerServices.getStrings(key);
    var data = JSON.parse(response);
    if (!data) {
      console.warn("[l10n] #" + key + " missing for [" + gLanguage + "]");
    }
    return data;
  }
  function substArguments(text, args) {
    if (!args) {
      return text;
    }
    return text.replace(/\{\{\s*(\w+)\s*\}\}/g, function (all, name) {
      return name in args ? args[name] : "{{" + name + "}}";
    });
  }
  function translateString(key, args, fallback) {
    var i = key.lastIndexOf(".");
    var name, property;
    if (i >= 0) {
      name = key.substring(0, i);
      property = key.substring(i + 1);
    } else {
      name = key;
      property = "textContent";
    }
    var data = getL10nData(name);
    var value = data && data[property] || fallback;
    if (!value) {
      return "{{" + key + "}}";
    }
    return substArguments(value, args);
  }
  function translateElement(element) {
    if (!element || !element.dataset) {
      return;
    }
    var key = element.dataset.l10nId;
    var data = getL10nData(key);
    if (!data) {
      return;
    }
    var args;
    if (element.dataset.l10nArgs) {
      try {
        args = JSON.parse(element.dataset.l10nArgs);
      } catch (e) {
        console.warn("[l10n] could not parse arguments for #" + key + "");
      }
    }
    for (var k in data) {
      element[k] = substArguments(data[k], args);
    }
  }
  function translateFragment(element) {
    element = element || document.querySelector("html");
    var children = element.querySelectorAll("*[data-l10n-id]");
    var elementCount = children.length;
    for (var i = 0; i < elementCount; i++) {
      translateElement(children[i]);
    }
    if (element.dataset.l10nId) {
      translateElement(element);
    }
  }
  document.mozL10n = {
    get: translateString,
    getLanguage() {
      return gLanguage;
    },
    getDirection() {
      var rtlList = ["ar", "he", "fa", "ps", "ur"];
      var shortCode = gLanguage.split("-")[0];
      return rtlList.includes(shortCode) ? "rtl" : "ltr";
    },
    getReadyState() {
      return gReadyState;
    },
    setExternalLocalizerServices(externalLocalizerServices) {
      gExternalLocalizerServices = externalLocalizerServices;
      gLanguage = gExternalLocalizerServices.getLocale();
      gReadyState = "complete";
    },
    translate: translateFragment
  };
})(undefined);

/***/ }),
/* 33 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.BasePreferences = undefined;

var _ui_utils = __webpack_require__(0);

let defaultPreferences = null;
function getDefaultPreferences() {
  if (!defaultPreferences) {
    defaultPreferences = Promise.resolve({
      "showPreviousViewOnLoad": true,
      "defaultZoomValue": "",
      "sidebarViewOnLoad": 0,
      "cursorToolOnLoad": 0,
      "enableWebGL": false,
      "pdfBugEnabled": false,
      "disableRange": false,
      "disableStream": false,
      "disableAutoFetch": false,
      "disableFontFace": false,
      "textLayerMode": 1,
      "useOnlyCssZoom": false,
      "externalLinkTarget": 0,
      "renderer": "canvas",
      "renderInteractiveForms": false,
      "enablePrintAutoRotate": false,
      "disablePageMode": false,
      "disablePageLabels": false
    });
  }
  return defaultPreferences;
}
class BasePreferences {
  constructor() {
    if (this.constructor === BasePreferences) {
      throw new Error('Cannot initialize BasePreferences.');
    }
    this.prefs = null;
    this._initializedPromise = getDefaultPreferences().then(defaults => {
      Object.defineProperty(this, 'defaults', {
        value: Object.freeze(defaults),
        writable: false,
        enumerable: true,
        configurable: false
      });
      this.prefs = (0, _ui_utils.cloneObj)(defaults);
      return this._readFromStorage(defaults);
    }).then(prefObj => {
      if (prefObj) {
        this.prefs = prefObj;
      }
    });
  }
  _writeToStorage(prefObj) {
    return Promise.reject(new Error('Not implemented: _writeToStorage'));
  }
  _readFromStorage(prefObj) {
    return Promise.reject(new Error('Not implemented: _readFromStorage'));
  }
  reset() {
    return this._initializedPromise.then(() => {
      this.prefs = (0, _ui_utils.cloneObj)(this.defaults);
      return this._writeToStorage(this.defaults);
    });
  }
  reload() {
    return this._initializedPromise.then(() => {
      return this._readFromStorage(this.defaults);
    }).then(prefObj => {
      if (prefObj) {
        this.prefs = prefObj;
      }
    });
  }
  set(name, value) {
    return this._initializedPromise.then(() => {
      if (this.defaults[name] === undefined) {
        throw new Error(`Set preference: "${name}" is undefined.`);
      } else if (value === undefined) {
        throw new Error('Set preference: no value is specified.');
      }
      let valueType = typeof value;
      let defaultType = typeof this.defaults[name];
      if (valueType !== defaultType) {
        if (valueType === 'number' && defaultType === 'string') {
          value = value.toString();
        } else {
          throw new Error(`Set preference: "${value}" is a ${valueType}, ` + `expected a ${defaultType}.`);
        }
      } else {
        if (valueType === 'number' && !Number.isInteger(value)) {
          throw new Error(`Set preference: "${value}" must be an integer.`);
        }
      }
      this.prefs[name] = value;
      return this._writeToStorage(this.prefs);
    });
  }
  get(name) {
    return this._initializedPromise.then(() => {
      let defaultValue = this.defaults[name];
      if (defaultValue === undefined) {
        throw new Error(`Get preference: "${name}" is undefined.`);
      } else {
        let prefValue = this.prefs[name];
        if (prefValue !== undefined) {
          return prefValue;
        }
      }
      return defaultValue;
    });
  }
}
exports.BasePreferences = BasePreferences;

/***/ }),
/* 34 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.FirefoxPrintService = undefined;

var _ui_utils = __webpack_require__(0);

var _app = __webpack_require__(4);

var _pdfjsLib = __webpack_require__(1);

function composePage(pdfDocument, pageNumber, size, printContainer) {
  let canvas = document.createElement('canvas');
  const PRINT_RESOLUTION = 150;
  const PRINT_UNITS = PRINT_RESOLUTION / 72.0;
  canvas.width = Math.floor(size.width * PRINT_UNITS);
  canvas.height = Math.floor(size.height * PRINT_UNITS);
  canvas.style.width = Math.floor(size.width * _ui_utils.CSS_UNITS) + 'px';
  canvas.style.height = Math.floor(size.height * _ui_utils.CSS_UNITS) + 'px';
  let canvasWrapper = document.createElement('div');
  canvasWrapper.appendChild(canvas);
  printContainer.appendChild(canvasWrapper);
  canvas.mozPrintCallback = function (obj) {
    let ctx = obj.context;
    ctx.save();
    ctx.fillStyle = 'rgb(255, 255, 255)';
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.restore();
    pdfDocument.getPage(pageNumber).then(function (pdfPage) {
      let renderContext = {
        canvasContext: ctx,
        transform: [PRINT_UNITS, 0, 0, PRINT_UNITS, 0, 0],
        viewport: pdfPage.getViewport(1, size.rotation),
        intent: 'print'
      };
      return pdfPage.render(renderContext).promise;
    }).then(function () {
      obj.done();
    }, function (error) {
      console.error(error);
      if ('abort' in obj) {
        obj.abort();
      } else {
        obj.done();
      }
    });
  };
}
function FirefoxPrintService(pdfDocument, pagesOverview, printContainer) {
  this.pdfDocument = pdfDocument;
  this.pagesOverview = pagesOverview;
  this.printContainer = printContainer;
}
FirefoxPrintService.prototype = {
  layout() {
    let pdfDocument = this.pdfDocument;
    let printContainer = this.printContainer;
    let body = document.querySelector('body');
    body.setAttribute('data-pdfjsprinting', true);
    for (let i = 0, ii = this.pagesOverview.length; i < ii; ++i) {
      composePage(pdfDocument, i + 1, this.pagesOverview[i], printContainer);
    }
  },
  destroy() {
    this.printContainer.textContent = '';
  }
};
_app.PDFPrintServiceFactory.instance = {
  get supportsPrinting() {
    let canvas = document.createElement('canvas');
    let value = 'mozPrintCallback' in canvas;
    return (0, _pdfjsLib.shadow)(this, 'supportsPrinting', value);
  },
  createPrintService(pdfDocument, pagesOverview, printContainer) {
    return new FirefoxPrintService(pdfDocument, pagesOverview, printContainer);
  }
};
exports.FirefoxPrintService = FirefoxPrintService;

/***/ })
/******/ ]);PK
!<:n���
�
chrome/webide/content/addons.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
const Services = require("Services");
const {gDevTools} = require("devtools/client/framework/devtools");
const {GetAvailableAddons, ForgetAddonsList} = require("devtools/client/webide/modules/addons");
const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties");

window.addEventListener("load", function () {
  document.querySelector("#aboutaddons").onclick = function () {
    let browserWin = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
    if (browserWin && browserWin.BrowserOpenAddonsMgr) {
      browserWin.BrowserOpenAddonsMgr("addons://list/extension");
    }
  };
  document.querySelector("#close").onclick = CloseUI;
  BuildUI(GetAvailableAddons());
}, {capture: true, once: true});

window.addEventListener("unload", function () {
  ForgetAddonsList();
}, {capture: true, once: true});

function CloseUI() {
  window.parent.UI.openProject();
}

function BuildUI(addons) {
  BuildItem(addons.adb, "adb");
}

function BuildItem(addon, type) {

  function onAddonUpdate(arg) {
    progress.removeAttribute("value");
    li.setAttribute("status", addon.status);
    status.textContent = Strings.GetStringFromName("addons_status_" + addon.status);
  }

  function onAddonFailure(arg) {
    window.parent.UI.reportError("error_operationFail", arg);
  }

  function onAddonProgress(arg) {
    if (arg == -1) {
      progress.removeAttribute("value");
    } else {
      progress.value = arg;
    }
  }

  addon.on("update", onAddonUpdate);
  addon.on("failure", onAddonFailure);
  addon.on("progress", onAddonProgress);

  window.addEventListener("unload", function () {
    addon.off("update", onAddonUpdate);
    addon.off("failure", onAddonFailure);
    addon.off("progress", onAddonProgress);
  }, {once: true});

  let li = document.createElement("li");
  li.setAttribute("status", addon.status);

  let name = document.createElement("span");
  name.className = "name";

  switch (type) {
    case "adb":
      li.setAttribute("addon", type);
      name.textContent = Strings.GetStringFromName("addons_adb_label");
      break;
  }

  li.appendChild(name);

  let status = document.createElement("span");
  status.className = "status";
  status.textContent = Strings.GetStringFromName("addons_status_" + addon.status);
  li.appendChild(status);

  let installButton = document.createElement("button");
  installButton.className = "install-button";
  installButton.onclick = () => addon.install();
  installButton.textContent = Strings.GetStringFromName("addons_install_button");
  li.appendChild(installButton);

  let uninstallButton = document.createElement("button");
  uninstallButton.className = "uninstall-button";
  uninstallButton.onclick = () => addon.uninstall();
  uninstallButton.textContent = Strings.GetStringFromName("addons_uninstall_button");
  li.appendChild(uninstallButton);

  let progress = document.createElement("progress");
  li.appendChild(progress);

  if (type == "adb") {
    let warning = document.createElement("p");
    warning.textContent = Strings.GetStringFromName("addons_adb_warning");
    warning.className = "warning";
    li.appendChild(warning);
  }

  document.querySelector("ul").appendChild(li);
}
PK
!<n!���"chrome/webide/content/addons.xhtml<?xml version="1.0" encoding="UTF-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/. -->

<!DOCTYPE html [
  <!ENTITY % webideDTD SYSTEM "chrome://devtools/locale/webide.dtd" >
  %webideDTD;
]>

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta charset="utf8"/>
    <link rel="stylesheet" href="chrome://webide/skin/deck.css" type="text/css"/>
    <link rel="stylesheet" href="chrome://webide/skin/addons.css" type="text/css"/>
    <script type="application/javascript" src="chrome://webide/content/addons.js"></script>
  </head>
  <body>

    <div id="controls">
      <a id="aboutaddons">&addons_aboutaddons;</a>
      <a id="close">&deck_close;</a>
    </div>

    <h1>&addons_title;</h1>

    <ul></ul>

  </body>
</html>
PK
!<�*�gHH chrome/webide/content/details.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
const Services = require("Services");
const {AppManager} = require("devtools/client/webide/modules/app-manager");

window.addEventListener("load", function () {
  document.addEventListener("visibilitychange", updateUI, true);
  AppManager.on("app-manager-update", onAppManagerUpdate);
  updateUI();
}, {capture: true, once: true});

window.addEventListener("unload", function () {
  AppManager.off("app-manager-update", onAppManagerUpdate);
}, {capture: true, once: true});

function onAppManagerUpdate(what, details) {
  if (what == "project" ||
      what == "project-validated") {
    updateUI();
  }
}

function resetUI() {
  document.querySelector("#toolbar").classList.add("hidden");
  document.querySelector("#type").classList.add("hidden");
  document.querySelector("#descriptionHeader").classList.add("hidden");
  document.querySelector("#manifestURLHeader").classList.add("hidden");
  document.querySelector("#locationHeader").classList.add("hidden");

  document.body.className = "";
  document.querySelector("#icon").src = "";
  document.querySelector("h1").textContent = "";
  document.querySelector("#description").textContent = "";
  document.querySelector("#type").textContent = "";
  document.querySelector("#manifestURL").textContent = "";
  document.querySelector("#location").textContent = "";

  document.querySelector("#errorslist").innerHTML = "";
  document.querySelector("#warningslist").innerHTML = "";

}

function updateUI() {
  resetUI();

  let project = AppManager.selectedProject;
  if (!project) {
    return;
  }

  if (project.type != "runtimeApp" && project.type != "mainProcess") {
    document.querySelector("#toolbar").classList.remove("hidden");
    document.querySelector("#locationHeader").classList.remove("hidden");
    document.querySelector("#location").textContent = project.location;
  }

  document.body.className = project.validationStatus;
  document.querySelector("#icon").src = project.icon;
  document.querySelector("h1").textContent = project.name;

  let manifest;
  if (project.type == "runtimeApp") {
    manifest = project.app.manifest;
  } else {
    manifest = project.manifest;
  }

  if (manifest) {
    if (manifest.description) {
      document.querySelector("#descriptionHeader").classList.remove("hidden");
      document.querySelector("#description").textContent = manifest.description;
    }

    document.querySelector("#type").classList.remove("hidden");

    if (project.type == "runtimeApp") {
      let manifestURL = AppManager.getProjectManifestURL(project);
      document.querySelector("#type").textContent = manifest.type || "web";
      document.querySelector("#manifestURLHeader").classList.remove("hidden");
      document.querySelector("#manifestURL").textContent = manifestURL;
    } else if (project.type == "mainProcess") {
      document.querySelector("#type").textContent = project.name;
    } else {
      document.querySelector("#type").textContent = project.type + " " + (manifest.type || "web");
    }

    if (project.type == "packaged") {
      let manifestURL = AppManager.getProjectManifestURL(project);
      if (manifestURL) {
        document.querySelector("#manifestURLHeader").classList.remove("hidden");
        document.querySelector("#manifestURL").textContent = manifestURL;
      }
    }
  }

  let errorsNode = document.querySelector("#errorslist");
  let warningsNode = document.querySelector("#warningslist");

  if (project.errors) {
    for (let e of project.errors) {
      let li = document.createElement("li");
      li.textContent = e;
      errorsNode.appendChild(li);
    }
  }

  if (project.warnings) {
    for (let w of project.warnings) {
      let li = document.createElement("li");
      li.textContent = w;
      warningsNode.appendChild(li);
    }
  }

  AppManager.update("details");
}

function removeProject() {
  AppManager.removeSelectedProject();
}
PK
!<r���#chrome/webide/content/details.xhtml<?xml version="1.0" encoding="UTF-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/. -->

<!DOCTYPE html [
  <!ENTITY % webideDTD SYSTEM "chrome://devtools/locale/webide.dtd" >
  %webideDTD;
]>

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta charset="utf8"/>
    <link rel="stylesheet" href="chrome://webide/skin/details.css" type="text/css"/>
    <script type="application/javascript" src="chrome://webide/content/details.js"></script>
  </head>
  <body>

    <div id="toolbar">
      <button onclick="removeProject()">&details_removeProject_button;</button>
      <p id="validation_status">
        <span class="valid">&details_valid_header;</span>
        <span class="warning">&details_warning_header;</span>
        <span class="error">&details_error_header;</span>
      </p>
    </div>

    <header>
      <img id="icon"></img>
      <div>
        <h1></h1>
        <p id="type"></p>
      </div>
    </header>

    <main>
      <h3 id="descriptionHeader">&details_description;</h3>
      <p id="description"></p>

      <h3 id="locationHeader">&details_location;</h3>
      <p id="location"></p>

      <h3 id="manifestURLHeader">&details_manifestURL;</h3>
      <p id="manifestURL"></p>
    </main>

    <ul class="validation_messages" id="errorslist"></ul>
    <ul class="validation_messages" id="warningslist"></ul>

  </body>
</html>
PK
!<����|	|	*chrome/webide/content/devicepreferences.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
const {AppManager} = require("devtools/client/webide/modules/app-manager");
const {Connection} = require("devtools/shared/client/connection-manager");
const ConfigView = require("devtools/client/webide/modules/config-view");

var configView = new ConfigView(window);

window.addEventListener("load", function () {
  AppManager.on("app-manager-update", OnAppManagerUpdate);
  document.getElementById("close").onclick = CloseUI;
  document.getElementById("device-fields").onchange = UpdateField;
  document.getElementById("device-fields").onclick = CheckReset;
  document.getElementById("search-bar").onkeyup = document.getElementById("search-bar").onclick = SearchField;
  document.getElementById("custom-value").onclick = UpdateNewField;
  document.getElementById("custom-value-type").onchange = ClearNewFields;
  document.getElementById("add-custom-field").onkeyup = CheckNewFieldSubmit;
  BuildUI();
}, {capture: true, once: true});

window.addEventListener("unload", function () {
  AppManager.off("app-manager-update", OnAppManagerUpdate);
}, {once: true});

function CloseUI() {
  window.parent.UI.openProject();
}

function OnAppManagerUpdate(what) {
  if (what == "connection" || what == "runtime-global-actors") {
    BuildUI();
  }
}

function CheckNewFieldSubmit(event) {
  configView.checkNewFieldSubmit(event);
}

function UpdateNewField() {
  configView.updateNewField();
}

function ClearNewFields() {
  configView.clearNewFields();
}

function CheckReset(event) {
  configView.checkReset(event);
}

function UpdateField(event) {
  configView.updateField(event);
}

function SearchField(event) {
  configView.search(event);
}

var getAllPrefs; // Used by tests
function BuildUI() {
  configView.resetTable();

  if (AppManager.connection &&
      AppManager.connection.status == Connection.Status.CONNECTED &&
      AppManager.preferenceFront) {
    configView.front = AppManager.preferenceFront;
    configView.kind = "Pref";
    configView.includeTypeName = true;

    getAllPrefs = AppManager.preferenceFront.getAllPrefs()
                  .then(json => configView.generateDisplay(json));
  } else {
    CloseUI();
  }
}
PK
!<8�˗-chrome/webide/content/devicepreferences.xhtml<?xml version="1.0" encoding="UTF-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/. -->

<!DOCTYPE html [
  <!ENTITY % webideDTD SYSTEM "chrome://devtools/locale/webide.dtd" >
  %webideDTD;
]>

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta charset="utf8"/>
    <link rel="stylesheet" href="chrome://webide/skin/deck.css" type="text/css"/>
    <link rel="stylesheet" href="chrome://webide/skin/config-view.css" type="text/css"/>
    <script type="application/javascript" src="chrome://webide/content/devicepreferences.js"></script>
  </head>
  <body>
    <header>
      <div id="controls">
        <a id="close">&deck_close;</a>
      </div>
      <h1>&devicepreference_title;</h1>
      <div id="search">
        <input type="text" id="search-bar" placeholder="&devicepreference_search;"/>
      </div>
    </header>
    <table id="device-fields">
      <tr id="add-custom-field">
        <td>
          <select id="custom-value-type">
            <option value="" selected="selected">&device_typenone;</option>
            <option value="boolean">&device_typeboolean;</option>
            <option value="number">&device_typenumber;</option>
            <option value="string">&device_typestring;</option>
          </select>
          <input type="text" id="custom-value-name" placeholder="&devicepreference_newname;"/>
        </td>
        <td class="custom-input">
          <input type="text" id="custom-value-text" placeholder="&devicepreference_newtext;"/>
        </td>
        <td>
          <button id="custom-value" class="new-editable">&devicepreference_addnew;</button>
        </td>
      </tr>
    </table>
  </body>
</html>
PK
!<���chrome/webide/content/newapp.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
const Services = require("Services");
const {FileUtils} = require("resource://gre/modules/FileUtils.jsm");
const {AppProjects} = require("devtools/client/webide/modules/app-projects");
const {AppManager} = require("devtools/client/webide/modules/app-manager");
const {getJSON} = require("devtools/client/shared/getjson");

ChromeUtils.defineModuleGetter(this, "ZipUtils", "resource://gre/modules/ZipUtils.jsm");
ChromeUtils.defineModuleGetter(this, "Downloads", "resource://gre/modules/Downloads.jsm");

const TEMPLATES_URL = "devtools.webide.templatesURL";

var gTemplateList = null;

// See bug 989619
console.log = console.log.bind(console);
console.warn = console.warn.bind(console);
console.error = console.error.bind(console);

window.addEventListener("load", function () {
  let projectNameNode = document.querySelector("#project-name");
  projectNameNode.addEventListener("input", canValidate, true);
  getTemplatesJSON();
}, {capture: true, once: true});

function getTemplatesJSON() {
  getJSON(TEMPLATES_URL).then(list => {
    if (!Array.isArray(list)) {
      throw new Error("JSON response not an array");
    }
    if (list.length == 0) {
      throw new Error("JSON response is an empty array");
    }
    gTemplateList = list;
    let templatelistNode = document.querySelector("#templatelist");
    templatelistNode.innerHTML = "";
    for (let template of list) {
      let richlistitemNode = document.createElement("richlistitem");
      let imageNode = document.createElement("image");
      imageNode.setAttribute("src", template.icon);
      let labelNode = document.createElement("label");
      labelNode.setAttribute("value", template.name);
      let descriptionNode = document.createElement("description");
      descriptionNode.textContent = template.description;
      let vboxNode = document.createElement("vbox");
      vboxNode.setAttribute("flex", "1");
      richlistitemNode.appendChild(imageNode);
      vboxNode.appendChild(labelNode);
      vboxNode.appendChild(descriptionNode);
      richlistitemNode.appendChild(vboxNode);
      templatelistNode.appendChild(richlistitemNode);
    }
    templatelistNode.selectedIndex = 0;

    /* Chrome mochitest support */
    let testOptions = window.arguments[0].testOptions;
    if (testOptions) {
      templatelistNode.selectedIndex = testOptions.index;
      document.querySelector("#project-name").value = testOptions.name;
      doOK();
    }
  }, (e) => {
    failAndBail("Can't download app templates: " + e);
  });
}

function failAndBail(msg) {
  let promptService = Cc["@mozilla.org/embedcomp/prompt-service;1"].getService(Ci.nsIPromptService);
  promptService.alert(window, "error", msg);
  window.close();
}

function canValidate() {
  let projectNameNode = document.querySelector("#project-name");
  let dialogNode = document.querySelector("dialog");
  if (projectNameNode.value.length > 0) {
    dialogNode.removeAttribute("buttondisabledaccept");
  } else {
    dialogNode.setAttribute("buttondisabledaccept", "true");
  }
}

function doOK() {
  let projectName = document.querySelector("#project-name").value;

  if (!projectName) {
    console.error("No project name");
    return false;
  }

  if (!gTemplateList) {
    console.error("No template index");
    return false;
  }

  let templatelistNode = document.querySelector("#templatelist");
  if (templatelistNode.selectedIndex < 0) {
    console.error("No template selected");
    return false;
  }

  /* Chrome mochitest support */
  let promise = new Promise((resolve, reject) => {
    let testOptions = window.arguments[0].testOptions;
    if (testOptions) {
      resolve(testOptions.folder);
    } else {
      let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
      fp.init(window, "Select directory where to create app directory", Ci.nsIFilePicker.modeGetFolder);
      fp.open(res => {
        if (res == Ci.nsIFilePicker.returnCancel) {
          console.error("No directory selected");
          reject(null);
        } else {
          resolve(fp.file);
        }
      });
    }
  });

  let bail = (e) => {
    console.error(e);
    window.close();
  };

  promise.then(folder => {
    // Create subfolder with fs-friendly name of project
    let subfolder = projectName.replace(/[\\/:*?"<>|]/g, "").toLowerCase();
    let win = Services.wm.getMostRecentWindow("devtools:webide");
    folder.append(subfolder);

    try {
      folder.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
    } catch (e) {
      win.UI.reportError("error_folderCreationFailed");
      window.close();
      return;
    }

    // Download boilerplate zip
    let template = gTemplateList[templatelistNode.selectedIndex];
    let source = template.file;
    let target = folder.clone();
    target.append(subfolder + ".zip");
    Downloads.fetch(source, target).then(() => {
      ZipUtils.extractFiles(target, folder);
      target.remove(false);
      AppProjects.addPackaged(folder).then((project) => {
        window.arguments[0].location = project.location;
        AppManager.validateAndUpdateProject(project).then(() => {
          if (project.manifest) {
            project.manifest.name = projectName;
            AppManager.writeManifest(project).then(() => {
              AppManager.validateAndUpdateProject(project).then(
                () => {window.close();}, bail);
            }, bail);
          } else {
            bail("Manifest not found");
          }
        }, bail);
      }, bail);
    }, bail);
  }, bail);

  return false;
}
PK
!<s�?MAA chrome/webide/content/newapp.xul<?xml version="1.0"?>

<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 window [
  <!ENTITY % webideDTD SYSTEM "chrome://devtools/locale/webide.dtd" >
  %webideDTD;
]>

<?xml-stylesheet href="chrome://global/skin/global.css"?>
<?xml-stylesheet href="chrome://webide/skin/newapp.css"?>

<dialog id="webide:newapp" title="&newAppWindowTitle;"
  width="600" height="400"
  buttons="accept,cancel"
  ondialogaccept="return doOK();"
  buttondisabledaccept="true"
  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

  <script type="application/javascript" src="newapp.js"></script>
  <label class="header-name" value="&newAppHeader;"/>

  <richlistbox id="templatelist" flex="1">
    <description>&newAppLoadingTemplate;</description>
  </richlistbox>
  <vbox>
    <label class="header-name" control="project-name" value="&newAppProjectName;"/>
    <textbox id="project-name"/>
  </vbox>

</dialog>
PK
!<
�%chrome/webide/content/prefs.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm", {});

window.addEventListener("load", function () {
  // Listen to preference changes
  let inputs = document.querySelectorAll("[data-pref]");
  for (let i of inputs) {
    let pref = i.dataset.pref;
    Services.prefs.addObserver(pref, FillForm);
    i.addEventListener("change", SaveForm);
  }

  // Buttons
  document.querySelector("#close").onclick = CloseUI;
  document.querySelector("#restore").onclick = RestoreDefaults;
  document.querySelector("#manageComponents").onclick = ShowAddons;

  // Initialize the controls
  FillForm();

}, {capture: true, once: true});

window.addEventListener("unload", function () {
  let inputs = document.querySelectorAll("[data-pref]");
  for (let i of inputs) {
    let pref = i.dataset.pref;
    i.removeEventListener("change", SaveForm);
    Services.prefs.removeObserver(pref, FillForm);
  }
}, {capture: true, once: true});

function CloseUI() {
  window.parent.UI.openProject();
}

function ShowAddons() {
  window.parent.Cmds.showAddons();
}

function FillForm() {
  let inputs = document.querySelectorAll("[data-pref]");
  for (let i of inputs) {
    let pref = i.dataset.pref;
    let val = GetPref(pref);
    if (i.type == "checkbox") {
      i.checked = val;
    } else {
      i.value = val;
    }
  }
}

function SaveForm(e) {
  let inputs = document.querySelectorAll("[data-pref]");
  for (let i of inputs) {
    let pref = i.dataset.pref;
    if (i.type == "checkbox") {
      SetPref(pref, i.checked);
    } else {
      SetPref(pref, i.value);
    }
  }
}

function GetPref(name) {
  let type = Services.prefs.getPrefType(name);
  switch (type) {
    case Services.prefs.PREF_STRING:
      return Services.prefs.getCharPref(name);
    case Services.prefs.PREF_INT:
      return Services.prefs.getIntPref(name);
    case Services.prefs.PREF_BOOL:
      return Services.prefs.getBoolPref(name);
    default:
      throw new Error("Unknown type");
  }
}

function SetPref(name, value) {
  let type = Services.prefs.getPrefType(name);
  switch (type) {
    case Services.prefs.PREF_STRING:
      return Services.prefs.setCharPref(name, value);
    case Services.prefs.PREF_INT:
      return Services.prefs.setIntPref(name, value);
    case Services.prefs.PREF_BOOL:
      return Services.prefs.setBoolPref(name, value);
    default:
      throw new Error("Unknown type");
  }
}

function RestoreDefaults() {
  let inputs = document.querySelectorAll("[data-pref]");
  for (let i of inputs) {
    let pref = i.dataset.pref;
    Services.prefs.clearUserPref(pref);
  }
}
PK
!<;P7�ee!chrome/webide/content/prefs.xhtml<?xml version="1.0" encoding="UTF-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/. -->

<!DOCTYPE html [
  <!ENTITY % webideDTD SYSTEM "chrome://devtools/locale/webide.dtd" >
  %webideDTD;
]>

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta charset="utf8"/>
    <link rel="stylesheet" href="chrome://webide/skin/deck.css" type="text/css"/>
    <script type="application/javascript" src="chrome://webide/content/prefs.js"></script>
  </head>
  <body>

    <div id="controls">
      <a id="restore">&prefs_restore;</a>
      <a id="manageComponents">&prefs_manage_components;</a>
      <a id="close">&deck_close;</a>
    </div>

    <h1>&prefs_title;</h1>

    <h2>&prefs_general_title;</h2>

    <ul>
      <li>
        <label title="&prefs_options_rememberlastproject_tooltip;">
          <input type="checkbox" data-pref="devtools.webide.restoreLastProject"/>
          <span>&prefs_options_rememberlastproject;</span>
        </label>
      </li>
      <li>
        <label title="&prefs_options_autoconnectruntime_tooltip;">
          <input type="checkbox" data-pref="devtools.webide.autoConnectRuntime"/>
          <span>&prefs_options_autoconnectruntime;</span>
        </label>
      </li>
      <li>
        <label class="text-input" title="&prefs_options_templatesurl_tooltip;">
          <span>&prefs_options_templatesurl;</span>
          <input data-pref="devtools.webide.templatesURL"/>
        </label>
      </li>
    </ul>

  </body>
</html>
PK
!<���ݢ�(chrome/webide/content/project-listing.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 */

const {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
const ProjectList = require("devtools/client/webide/modules/project-list");

var projectList = new ProjectList(window, window.parent);

window.addEventListener("load", function () {
  document.getElementById("new-app").onclick = CreateNewApp;
  document.getElementById("hosted-app").onclick = ImportHostedApp;
  document.getElementById("packaged-app").onclick = ImportPackagedApp;
  document.getElementById("refresh-tabs").onclick = RefreshTabs;
  projectList.update();
  projectList.updateCommands();
}, {capture: true, once: true});

window.addEventListener("unload", function () {
  projectList.destroy();
}, {once: true});

function RefreshTabs() {
  projectList.refreshTabs();
}

function CreateNewApp() {
  projectList.newApp();
}

function ImportHostedApp() {
  projectList.importHostedApp();
}

function ImportPackagedApp() {
  projectList.importPackagedApp();
}
PK
!<#�:_��+chrome/webide/content/project-listing.xhtml<?xml version="1.0" encoding="UTF-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/. -->

<!DOCTYPE html [
  <!ENTITY % webideDTD SYSTEM "chrome://devtools/locale/webide.dtd" >
  %webideDTD;
]>

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta charset="utf8"/>
    <link rel="stylesheet" href="chrome://webide/skin/panel-listing.css" type="text/css"/>
    <script type="application/javascript" src="chrome://webide/content/project-listing.js"></script>
  </head>
  <body>
    <div id="project-panel">
      <div id="project-panel-box">
        <button class="panel-item project-panel-item-newapp" id="new-app">&projectMenu_newApp_label;</button>
        <button class="panel-item project-panel-item-openpackaged" id="packaged-app">&projectMenu_importPackagedApp_label;</button>
        <button class="panel-item project-panel-item-openhosted" id="hosted-app">&projectMenu_importHostedApp_label;</button>
        <label class="panel-header">&projectPanel_myProjects;</label>
        <div id="project-panel-projects"></div>
        <label class="panel-header" id="panel-header-runtimeapps" hidden="true">&projectPanel_runtimeApps;</label>
        <div id="project-panel-runtimeapps"/>
        <label class="panel-header" id="panel-header-tabs" hidden="true">&projectPanel_tabs;
          <button class="project-panel-item-refreshtabs refresh-icon" id="refresh-tabs" title="&projectMenu_refreshTabs_label;"></button>
        </label>
        <div id="project-panel-tabs"/>
      </div>
    </div>
  </body>
</html>
PK
!<�؏��&chrome/webide/content/project-panel.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

var ProjectPanel = {
  // TODO: Expand function to save toggle state.
  toggleSidebar: function () {
    document.querySelector("#project-listing-panel").setAttribute("sidebar-displayed", true);
    document.querySelector("#project-listing-splitter").setAttribute("sidebar-displayed", true);
  }
};
PK
!<[q��(chrome/webide/content/runtime-listing.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
const RuntimeList = require("devtools/client/webide/modules/runtime-list");

var runtimeList = new RuntimeList(window, window.parent);

window.addEventListener("load", function () {
  document.getElementById("runtime-screenshot").onclick = TakeScreenshot;
  document.getElementById("runtime-details").onclick = ShowRuntimeDetails;
  document.getElementById("runtime-disconnect").onclick = DisconnectRuntime;
  document.getElementById("runtime-preferences").onclick = ShowDevicePreferences;
  document.getElementById("runtime-settings").onclick = ShowSettings;
  document.getElementById("runtime-panel-noadbhelper").onclick = ShowAddons;
  document.getElementById("runtime-panel-nousbdevice").onclick = ShowTroubleShooting;
  document.getElementById("refresh-devices").onclick = RefreshScanners;
  runtimeList.update();
  runtimeList.updateCommands();
}, {capture: true, once: true});

window.addEventListener("unload", function () {
  runtimeList.destroy();
}, {once: true});

function TakeScreenshot() {
  runtimeList.takeScreenshot();
}

function ShowRuntimeDetails() {
  runtimeList.showRuntimeDetails();
}

function ShowDevicePreferences() {
  runtimeList.showDevicePreferences();
}

function ShowSettings() {
  runtimeList.showSettings();
}

function RefreshScanners() {
  runtimeList.refreshScanners();
}

function DisconnectRuntime() {
  window.parent.Cmds.disconnectRuntime();
}

function ShowAddons() {
  runtimeList.showAddons();
}

function ShowTroubleShooting() {
  runtimeList.showTroubleShooting();
}
PK
!<�o����+chrome/webide/content/runtime-listing.xhtml<?xml version="1.0" encoding="UTF-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/. -->

<!DOCTYPE html [
  <!ENTITY % webideDTD SYSTEM "chrome://devtools/locale/webide.dtd" >
  %webideDTD;
]>

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta charset="utf8"/>
    <link rel="stylesheet" href="chrome://webide/skin/panel-listing.css" type="text/css"/>
    <script type="application/javascript" src="chrome://webide/content/runtime-listing.js"></script>
  </head>
  <body>
    <div id="runtime-panel">
      <div id="runtime-panel-box">
        <label class="panel-header">&runtimePanel_usb;
          <button class="runtime-panel-item-refreshdevices refresh-icon" id="refresh-devices" title="&runtimePanel_refreshDevices_label;"></button>
        </label>
        <button class="panel-item" id="runtime-panel-nousbdevice">&runtimePanel_nousbdevice;</button>
        <button class="panel-item" id="runtime-panel-noadbhelper">&runtimePanel_noadbhelper;</button>
        <div id="runtime-panel-usb"></div>
        <label class="panel-header" id="runtime-header-wifi">&runtimePanel_wifi;</label>
        <div id="runtime-panel-wifi"></div>
        <label class="panel-header">&runtimePanel_other;</label>
        <div id="runtime-panel-other"></div>
        <div id="runtime-actions">
          <button class="panel-item" id="runtime-details">&runtimeMenu_showDetails_label;</button>
          <button class="panel-item" id="runtime-preferences">&runtimeMenu_showDevicePrefs_label;</button>
          <button class="panel-item" id="runtime-settings">&runtimeMenu_showSettings_label;</button>
          <button class="panel-item" id="runtime-screenshot">&runtimeMenu_takeScreenshot_label;</button>
          <button class="panel-item" id="runtime-disconnect">&runtimeMenu_disconnect_label;</button>
        </div>
      </div>
    </div>
  </body>
</html>
PK
!<�K��&chrome/webide/content/runtime-panel.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

var RuntimePanel = {
  // TODO: Expand function to save toggle state.
  toggleSidebar: function () {
    document.querySelector("#runtime-listing-panel").setAttribute("sidebar-displayed", true);
    document.querySelector("#runtime-listing-splitter").setAttribute("sidebar-displayed", true);
  }
};
PK
!<���i��'chrome/webide/content/runtimedetails.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
const Services = require("Services");
const {AppManager} = require("devtools/client/webide/modules/app-manager");
const {Connection} = require("devtools/shared/client/connection-manager");
const {RuntimeTypes} = require("devtools/client/webide/modules/runtimes");
const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties");

const UNRESTRICTED_HELP_URL = "https://developer.mozilla.org/docs/Tools/WebIDE/Running_and_debugging_apps#Unrestricted_app_debugging_%28including_certified_apps_main_process_etc.%29";

window.addEventListener("load", function () {
  document.querySelector("#close").onclick = CloseUI;
  document.querySelector("#devtools-check button").onclick = EnableCertApps;
  document.querySelector("#adb-check button").onclick = RootADB;
  document.querySelector("#unrestricted-privileges").onclick = function () {
    window.parent.UI.openInBrowser(UNRESTRICTED_HELP_URL);
  };
  AppManager.on("app-manager-update", OnAppManagerUpdate);
  BuildUI();
  CheckLockState();
}, {capture: true, once: true});

window.addEventListener("unload", function () {
  AppManager.off("app-manager-update", OnAppManagerUpdate);
}, {once: true});

function CloseUI() {
  window.parent.UI.openProject();
}

function OnAppManagerUpdate(what) {
  if (what == "connection" || what == "runtime-global-actors") {
    BuildUI();
    CheckLockState();
  }
}

function generateFields(json) {
  let table = document.querySelector("table");
  for (let name in json) {
    let tr = document.createElement("tr");
    let td = document.createElement("td");
    td.textContent = name;
    tr.appendChild(td);
    td = document.createElement("td");
    td.textContent = json[name];
    tr.appendChild(td);
    table.appendChild(tr);
  }
}

var getDescriptionPromise; // Used by tests
function BuildUI() {
  let table = document.querySelector("table");
  table.innerHTML = "";
  if (AppManager.connection &&
      AppManager.connection.status == Connection.Status.CONNECTED &&
      AppManager.deviceFront) {
    getDescriptionPromise = AppManager.deviceFront.getDescription()
                            .then(json => generateFields(json));
  } else {
    CloseUI();
  }
}

function CheckLockState() {
  let adbCheckResult = document.querySelector("#adb-check > .yesno");
  let devtoolsCheckResult = document.querySelector("#devtools-check > .yesno");
  let flipCertPerfButton = document.querySelector("#devtools-check button");
  let adbRootButton = document.querySelector("#adb-check button");
  let flipCertPerfAction = document.querySelector("#devtools-check > .action");
  let adbRootAction = document.querySelector("#adb-check > .action");

  let sYes = Strings.GetStringFromName("runtimedetails_checkyes");
  let sNo = Strings.GetStringFromName("runtimedetails_checkno");
  let sUnknown = Strings.GetStringFromName("runtimedetails_checkunknown");
  let sNotUSB = Strings.GetStringFromName("runtimedetails_notUSBDevice");

  flipCertPerfButton.setAttribute("disabled", "true");
  flipCertPerfAction.setAttribute("hidden", "true");
  adbRootAction.setAttribute("hidden", "true");

  adbCheckResult.textContent = sUnknown;
  devtoolsCheckResult.textContent = sUnknown;

  if (AppManager.connection &&
      AppManager.connection.status == Connection.Status.CONNECTED) {

    // ADB check
    if (AppManager.selectedRuntime.type === RuntimeTypes.USB) {
      let device = AppManager.selectedRuntime.device;
      if (device && device.summonRoot) {
        device.isRoot().then(isRoot => {
          if (isRoot) {
            adbCheckResult.textContent = sYes;
            flipCertPerfButton.removeAttribute("disabled");
          } else {
            adbCheckResult.textContent = sNo;
            adbRootAction.removeAttribute("hidden");
          }
        }, console.error);
      } else {
        adbCheckResult.textContent = sUnknown;
      }
    } else {
      adbCheckResult.textContent = sNotUSB;
    }

    // forbid-certified-apps check
    try {
      let prefFront = AppManager.preferenceFront;
      prefFront.getBoolPref("devtools.debugger.forbid-certified-apps").then(isForbidden => {
        if (isForbidden) {
          devtoolsCheckResult.textContent = sNo;
          flipCertPerfAction.removeAttribute("hidden");
        } else {
          devtoolsCheckResult.textContent = sYes;
        }
      }, console.error);
    } catch (e) {
      // Exception. pref actor is only accessible if forbird-certified-apps is false
      devtoolsCheckResult.textContent = sNo;
      flipCertPerfAction.removeAttribute("hidden");
    }

  }

}

function EnableCertApps() {
  let device = AppManager.selectedRuntime.device;
  // TODO: Remove `network.disable.ipc.security` once bug 1125916 is fixed.
  device.shell(
    "stop b2g && " +
    "cd /data/b2g/mozilla/*.default/ && " +
    "echo 'user_pref(\"devtools.debugger.forbid-certified-apps\", false);' >> prefs.js && " +
    "echo 'user_pref(\"dom.apps.developer_mode\", true);' >> prefs.js && " +
    "echo 'user_pref(\"network.disable.ipc.security\", true);' >> prefs.js && " +
    "echo 'user_pref(\"dom.webcomponents.shadowdom.enabled\", true);' >> prefs.js && " +
    "start b2g"
  );
}

function RootADB() {
  let device = AppManager.selectedRuntime.device;
  device.summonRoot().then(CheckLockState, console.error);
}
PK
!<��
��*chrome/webide/content/runtimedetails.xhtml<?xml version="1.0" encoding="UTF-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/. -->

<!DOCTYPE html [
  <!ENTITY % webideDTD SYSTEM "chrome://devtools/locale/webide.dtd" >
  %webideDTD;
]>

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta charset="utf8"/>
    <link rel="stylesheet" href="chrome://webide/skin/deck.css" type="text/css"/>
    <link rel="stylesheet" href="chrome://webide/skin/runtimedetails.css" type="text/css"/>
    <script type="application/javascript" src="chrome://webide/content/runtimedetails.js"></script>
  </head>
  <body>

    <div id="controls">
      <a id="close">&deck_close;</a>
    </div>

    <h1>&runtimedetails_title;</h1>

    <div id="devicePrivileges">
      <p id="adb-check">
        &runtimedetails_adbIsRoot;<span class="yesno"></span>
        <div class="action">
          <button>&runtimedetails_summonADBRoot;</button>
          <em>&runtimedetails_ADBRootWarning;</em>
        </div>
      </p>
      <p id="devtools-check">
        <a id="unrestricted-privileges">&runtimedetails_unrestrictedPrivileges;</a><span class="yesno"></span>
        <div class="action">
          <button>&runtimedetails_requestPrivileges;</button>
          <em>&runtimedetails_privilegesWarning;</em>
        </div>
      </p>
    </div>

    <table></table>
  </body>
</html>
PK
!<��
,wwchrome/webide/content/webide.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
const {gDevTools} = require("devtools/client/framework/devtools");
const {gDevToolsBrowser} = require("devtools/client/framework/devtools-browser");
const {Toolbox} = require("devtools/client/framework/toolbox");
const Services = require("Services");
const {AppProjects} = require("devtools/client/webide/modules/app-projects");
const {Connection} = require("devtools/shared/client/connection-manager");
const {AppManager} = require("devtools/client/webide/modules/app-manager");
const EventEmitter = require("devtools/shared/event-emitter");
const promise = require("promise");
const {GetAvailableAddons} = require("devtools/client/webide/modules/addons");
const {getJSON} = require("devtools/client/shared/getjson");
const utils = require("devtools/client/webide/modules/utils");
const Telemetry = require("devtools/client/shared/telemetry");
const {RuntimeScanners} = require("devtools/client/webide/modules/runtimes");
const {showDoorhanger} = require("devtools/client/shared/doorhanger");
const {Task} = require("devtools/shared/task");

const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties");

const HTML = "http://www.w3.org/1999/xhtml";
const HELP_URL = "https://developer.mozilla.org/docs/Tools/WebIDE/Troubleshooting";

const MAX_ZOOM = 1.4;
const MIN_ZOOM = 0.6;

[["AppManager", AppManager],
 ["AppProjects", AppProjects],
 ["Connection", Connection]].forEach(([key, value]) => {
   Object.defineProperty(this, key, {
     value: value,
     enumerable: true,
     writable: false
   });
 });

// Download remote resources early
getJSON("devtools.webide.templatesURL");
getJSON("devtools.devices.url");

// See bug 989619
console.log = console.log.bind(console);
console.warn = console.warn.bind(console);
console.error = console.error.bind(console);

window.addEventListener("load", function () {
  UI.init();
}, {once: true});

window.addEventListener("unload", function () {
  UI.destroy();
}, {once: true});

var UI = {
  init: function () {
    this._telemetry = new Telemetry();
    this._telemetry.toolOpened("webide");

    AppManager.init();

    this.appManagerUpdate = this.appManagerUpdate.bind(this);
    AppManager.on("app-manager-update", this.appManagerUpdate);

    Cmds.showProjectPanel();
    Cmds.showRuntimePanel();

    this.updateCommands();

    this.onfocus = this.onfocus.bind(this);
    window.addEventListener("focus", this.onfocus, true);

    AppProjects.load().then(() => {
      this.autoSelectProject();
    }, e => {
      console.error(e);
      this.reportError("error_appProjectsLoadFailed");
    });

    // Auto install the ADB Addon Helper. Only once.
    // If the user decides to uninstall any of this addon, we won't install it again.
    let autoinstallADBHelper = Services.prefs.getBoolPref("devtools.webide.autoinstallADBHelper");
    if (autoinstallADBHelper) {
      let addons = GetAvailableAddons();
      addons.adb.install();
    }

    Services.prefs.setBoolPref("devtools.webide.autoinstallADBHelper", false);

    this.setupDeck();

    this.contentViewer = window.QueryInterface(Ci.nsIInterfaceRequestor)
                               .getInterface(Ci.nsIWebNavigation)
                               .QueryInterface(Ci.nsIDocShell)
                               .contentViewer;
    this.contentViewer.fullZoom = Services.prefs.getCharPref("devtools.webide.zoom");

    gDevToolsBrowser.isWebIDEInitialized.resolve();
  },

  destroy: function () {
    window.removeEventListener("focus", this.onfocus, true);
    AppManager.off("app-manager-update", this.appManagerUpdate);
    AppManager.destroy();
    this.updateConnectionTelemetry();
    this._telemetry.toolClosed("webide");
    this._telemetry.destroy();
  },

  onfocus: function () {
    // Because we can't track the activity in the folder project,
    // we need to validate the project regularly. Let's assume that
    // if a modification happened, it happened when the window was
    // not focused.
    if (AppManager.selectedProject &&
        AppManager.selectedProject.type != "mainProcess" &&
        AppManager.selectedProject.type != "runtimeApp" &&
        AppManager.selectedProject.type != "tab") {
      AppManager.validateAndUpdateProject(AppManager.selectedProject);
    }

    // Hook to display promotional Developer Edition doorhanger. Only displayed once.
    // Hooked into the `onfocus` event because sometimes does not work
    // when run at the end of `init`. ¯\(°_o)/¯
    showDoorhanger({ window, type: "deveditionpromo", anchor: document.querySelector("#deck") });
  },

  appManagerUpdate: function (what, details) {
    // Got a message from app-manager.js
    // See AppManager.update() for descriptions of what these events mean.
    switch (what) {
      case "runtime-list":
        this.autoConnectRuntime();
        break;
      case "connection":
        this.updateRuntimeButton();
        this.updateCommands();
        this.updateConnectionTelemetry();
        break;
      case "project":
        this._updatePromise = Task.spawn(function* () {
          UI.updateTitle();
          yield UI.destroyToolbox();
          UI.updateCommands();
          UI.openProject();
          yield UI.autoStartProject();
          UI.autoOpenToolbox();
          UI.saveLastSelectedProject();
          UI.updateRemoveProjectButton();
        });
        return;
      case "project-started":
        this.updateCommands();
        UI.autoOpenToolbox();
        break;
      case "project-stopped":
        UI.destroyToolbox();
        this.updateCommands();
        break;
      case "runtime-global-actors":
        // Check runtime version only on runtime-global-actors,
        // as we expect to use device actor
        this.checkRuntimeVersion();
        this.updateCommands();
        break;
      case "runtime-details":
        this.updateRuntimeButton();
        break;
      case "runtime":
        this.updateRuntimeButton();
        this.saveLastConnectedRuntime();
        break;
      case "project-validated":
        this.updateTitle();
        this.updateCommands();
        break;
      case "runtime-targets":
        this.autoSelectProject();
        break;
    }
    this._updatePromise = promise.resolve();
  },

  openInBrowser: function (url) {
    // Open a URL in a Firefox window
    let mainWindow = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
    if (mainWindow) {
      mainWindow.openUILinkIn(url, "tab");
      mainWindow.focus()
    } else {
      window.open(url);
    }
  },

  updateTitle: function () {
    let project = AppManager.selectedProject;
    if (project) {
      window.document.title = Strings.formatStringFromName("title_app", [project.name], 1);
    } else {
      window.document.title = Strings.GetStringFromName("title_noApp");
    }
  },

  /** ******** BUSY UI **********/

  _busyTimeout: null,
  _busyOperationDescription: null,
  _busyPromise: null,

  busy: function () {
    let win = document.querySelector("window");
    win.classList.add("busy");
    win.classList.add("busy-undetermined");
    this.updateCommands();
    this.update("busy");
  },

  unbusy: function () {
    let win = document.querySelector("window");
    win.classList.remove("busy");
    win.classList.remove("busy-determined");
    win.classList.remove("busy-undetermined");
    this.updateCommands();
    this.update("unbusy");
    this._busyPromise = null;
  },

  setupBusyTimeout: function () {
    this.cancelBusyTimeout();
    this._busyTimeout = setTimeout(() => {
      this.unbusy();
      UI.reportError("error_operationTimeout", this._busyOperationDescription);
    }, Services.prefs.getIntPref("devtools.webide.busyTimeout"));
  },

  cancelBusyTimeout: function () {
    clearTimeout(this._busyTimeout);
  },

  busyWithProgressUntil: function (promise, operationDescription) {
    let busy = this.busyUntil(promise, operationDescription);
    let win = document.querySelector("window");
    let progress = document.querySelector("#action-busy-determined");
    progress.mode = "undetermined";
    win.classList.add("busy-determined");
    win.classList.remove("busy-undetermined");
    return busy;
  },

  busyUntil: function (promise, operationDescription) {
    // Freeze the UI until the promise is resolved. A timeout will unfreeze the
    // UI, just in case the promise never gets resolved.
    this._busyPromise = promise;
    this._busyOperationDescription = operationDescription;
    this.setupBusyTimeout();
    this.busy();
    promise.then(() => {
      this.cancelBusyTimeout();
      this.unbusy();
    }, (e) => {
      let message;
      if (e && e.error && e.message) {
        // Some errors come from fronts that are not based on protocol.js.
        // Errors are not translated to strings.
        message = operationDescription + " (" + e.error + "): " + e.message;
      } else {
        message = operationDescription + (e ? (": " + e) : "");
      }
      this.cancelBusyTimeout();
      let operationCanceled = e && e.canceled;
      if (!operationCanceled) {
        UI.reportError("error_operationFail", message);
        if (e) {
          console.error(e);
        }
      }
      this.unbusy();
    });
    return promise;
  },

  reportError: function (l10nProperty, ...l10nArgs) {
    let text;

    if (l10nArgs.length > 0) {
      text = Strings.formatStringFromName(l10nProperty, l10nArgs, l10nArgs.length);
    } else {
      text = Strings.GetStringFromName(l10nProperty);
    }

    console.error(text);

    let buttons = [{
      label: Strings.GetStringFromName("notification_showTroubleShooting_label"),
      accessKey: Strings.GetStringFromName("notification_showTroubleShooting_accesskey"),
      callback: function () {
        Cmds.showTroubleShooting();
      }
    }];

    let nbox = document.querySelector("#notificationbox");
    nbox.removeAllNotifications(true);
    nbox.appendNotification(text, "webide:errornotification", null,
                            nbox.PRIORITY_WARNING_LOW, buttons);
  },

  dismissErrorNotification: function () {
    let nbox = document.querySelector("#notificationbox");
    nbox.removeAllNotifications(true);
  },

  /** ******** COMMANDS **********/

  /**
   * This module emits various events when state changes occur.
   *
   * The events this module may emit include:
   *   busy:
   *     The window is currently busy and certain UI functions may be disabled.
   *   unbusy:
   *     The window is not busy and certain UI functions may be re-enabled.
   */
  update: function (what, details) {
    this.emit("webide-update", what, details);
  },

  updateCommands: function () {
    // Action commands
    let playCmd = document.querySelector("#cmd_play");
    let stopCmd = document.querySelector("#cmd_stop");
    let debugCmd = document.querySelector("#cmd_toggleToolbox");
    let playButton = document.querySelector("#action-button-play");
    let projectPanelCmd = document.querySelector("#cmd_showProjectPanel");

    if (document.querySelector("window").classList.contains("busy")) {
      playCmd.setAttribute("disabled", "true");
      stopCmd.setAttribute("disabled", "true");
      debugCmd.setAttribute("disabled", "true");
      projectPanelCmd.setAttribute("disabled", "true");
      return;
    }

    if (!AppManager.selectedProject || !AppManager.connected) {
      playCmd.setAttribute("disabled", "true");
      stopCmd.setAttribute("disabled", "true");
      debugCmd.setAttribute("disabled", "true");
    } else {
      let isProjectRunning = AppManager.isProjectRunning();
      if (isProjectRunning) {
        playButton.classList.add("reload");
        stopCmd.removeAttribute("disabled");
        debugCmd.removeAttribute("disabled");
      } else {
        playButton.classList.remove("reload");
        stopCmd.setAttribute("disabled", "true");
        debugCmd.setAttribute("disabled", "true");
      }

      // If connected and a project is selected
      if (AppManager.selectedProject.type == "runtimeApp") {
        playCmd.removeAttribute("disabled");
      } else if (AppManager.selectedProject.type == "tab") {
        playCmd.removeAttribute("disabled");
        stopCmd.setAttribute("disabled", "true");
      } else if (AppManager.selectedProject.type == "mainProcess") {
        playCmd.setAttribute("disabled", "true");
        stopCmd.setAttribute("disabled", "true");
      } else {
        if (AppManager.selectedProject.errorsCount == 0 &&
            AppManager.runtimeCanHandleApps()) {
          playCmd.removeAttribute("disabled");
        } else {
          playCmd.setAttribute("disabled", "true");
        }
      }
    }

    // Runtime commands
    let screenshotCmd = document.querySelector("#cmd_takeScreenshot");
    let detailsCmd = document.querySelector("#cmd_showRuntimeDetails");
    let disconnectCmd = document.querySelector("#cmd_disconnectRuntime");
    let devicePrefsCmd = document.querySelector("#cmd_showDevicePrefs");
    let settingsCmd = document.querySelector("#cmd_showSettings");

    if (AppManager.connected) {
      if (AppManager.deviceFront) {
        detailsCmd.removeAttribute("disabled");
        screenshotCmd.removeAttribute("disabled");
      }
      if (AppManager.preferenceFront) {
        devicePrefsCmd.removeAttribute("disabled");
      }
      disconnectCmd.removeAttribute("disabled");
    } else {
      detailsCmd.setAttribute("disabled", "true");
      screenshotCmd.setAttribute("disabled", "true");
      disconnectCmd.setAttribute("disabled", "true");
      devicePrefsCmd.setAttribute("disabled", "true");
      settingsCmd.setAttribute("disabled", "true");
    }

    let runtimePanelButton = document.querySelector("#runtime-panel-button");

    if (AppManager.connected) {
      runtimePanelButton.setAttribute("active", "true");
      runtimePanelButton.removeAttribute("hidden");
    } else {
      runtimePanelButton.removeAttribute("active");
      runtimePanelButton.setAttribute("hidden", "true");
    }

    projectPanelCmd.removeAttribute("disabled");
  },

  updateRemoveProjectButton: function () {
    // Remove command
    let removeCmdNode = document.querySelector("#cmd_removeProject");
    if (AppManager.selectedProject) {
      removeCmdNode.removeAttribute("disabled");
    } else {
      removeCmdNode.setAttribute("disabled", "true");
    }
  },

  /** ******** RUNTIME **********/

  get lastConnectedRuntime() {
    return Services.prefs.getCharPref("devtools.webide.lastConnectedRuntime");
  },

  set lastConnectedRuntime(runtime) {
    Services.prefs.setCharPref("devtools.webide.lastConnectedRuntime", runtime);
  },

  autoConnectRuntime: function () {
    // Automatically reconnect to the previously selected runtime,
    // if available and has an ID and feature is enabled
    if (AppManager.selectedRuntime ||
        !Services.prefs.getBoolPref("devtools.webide.autoConnectRuntime") ||
        !this.lastConnectedRuntime) {
      return;
    }
    let [_, type, id] = this.lastConnectedRuntime.match(/^(\w+):(.+)$/);

    type = type.toLowerCase();

    // Local connection is mapped to AppManager.runtimeList.other array
    if (type == "local") {
      type = "other";
    }

    // We support most runtimes except simulator, that needs to be manually
    // launched
    if (type == "usb" || type == "wifi" || type == "other") {
      for (let runtime of AppManager.runtimeList[type]) {
        // Some runtimes do not expose an id and don't support autoconnect (like
        // remote connection)
        if (runtime.id == id) {
          // Only want one auto-connect attempt, so clear last runtime value
          this.lastConnectedRuntime = "";
          this.connectToRuntime(runtime);
        }
      }
    }
  },

  connectToRuntime: function (runtime) {
    let name = runtime.name;
    let promise = AppManager.connectToRuntime(runtime);
    promise.then(() => this.initConnectionTelemetry())
           .catch(() => {
             // Empty rejection handler to silence uncaught rejection warnings
             // |busyUntil| will listen for rejections.
             // Bug 1121100 may find a better way to silence these.
           });
    promise = this.busyUntil(promise, "Connecting to " + name);
    // Stop busy timeout for runtimes that take unknown or long amounts of time
    // to connect.
    if (runtime.prolongedConnection) {
      this.cancelBusyTimeout();
    }
    return promise;
  },

  updateRuntimeButton: function () {
    let labelNode = document.querySelector("#runtime-panel-button > .panel-button-label");
    if (!AppManager.selectedRuntime) {
      labelNode.setAttribute("value", Strings.GetStringFromName("runtimeButton_label"));
    } else {
      let name = AppManager.selectedRuntime.name;
      labelNode.setAttribute("value", name);
    }
  },

  saveLastConnectedRuntime: function () {
    if (AppManager.selectedRuntime &&
        AppManager.selectedRuntime.id !== undefined) {
      this.lastConnectedRuntime = AppManager.selectedRuntime.type + ":" +
                                  AppManager.selectedRuntime.id;
    } else {
      this.lastConnectedRuntime = "";
    }
  },

  /** ******** ACTIONS **********/

  _actionsToLog: new Set(),

  /**
   * For each new connection, track whether play and debug were ever used.  Only
   * one value is collected for each button, even if they are used multiple
   * times during a connection.
   */
  initConnectionTelemetry: function () {
    this._actionsToLog.add("play");
    this._actionsToLog.add("debug");
  },

  /**
   * Action occurred.  Log that it happened, and remove it from the loggable
   * set.
   */
  onAction: function (action) {
    if (!this._actionsToLog.has(action)) {
      return;
    }
    this.logActionState(action, true);
    this._actionsToLog.delete(action);
  },

  /**
   * Connection status changed or we are shutting down.  Record any loggable
   * actions as having not occurred.
   */
  updateConnectionTelemetry: function () {
    for (let action of this._actionsToLog.values()) {
      this.logActionState(action, false);
    }
    this._actionsToLog.clear();
  },

  logActionState: function (action, state) {
    let histogramId = "DEVTOOLS_WEBIDE_CONNECTION_" +
                      action.toUpperCase() + "_USED";
    this._telemetry.log(histogramId, state);
  },

  /** ******** PROJECTS **********/

  openProject: function () {
    let project = AppManager.selectedProject;

    if (!project) {
      this.resetDeck();
      return;
    }

    this.selectDeckPanel("details");
  },

  autoStartProject: Task.async(function* () {
    let project = AppManager.selectedProject;

    if (!project) {
      return;
    }
    if (!(project.type == "runtimeApp" ||
          project.type == "mainProcess" ||
          project.type == "tab")) {
      return; // For something that is not an editable app, we're done.
    }

    // Do not force opening apps that are already running, as they may have
    // some activity being opened and don't want to dismiss them.
    if (project.type == "runtimeApp" && !AppManager.isProjectRunning()) {
      yield UI.busyUntil(AppManager.launchRuntimeApp(), "running app");
    }
  }),

  autoOpenToolbox: Task.async(function* () {
    let project = AppManager.selectedProject;

    if (!project) {
      return;
    }
    if (!(project.type == "runtimeApp" ||
          project.type == "mainProcess" ||
          project.type == "tab")) {
      return; // For something that is not an editable app, we're done.
    }

    yield UI.createToolbox();
  }),

  importAndSelectApp: Task.async(function* (source) {
    let isPackaged = !!source.path;
    let project;
    try {
      project = yield AppProjects[isPackaged ? "addPackaged" : "addHosted"](source);
    } catch (e) {
      if (e === "Already added") {
        // Select project that's already been added,
        // and allow it to be revalidated and selected
        project = AppProjects.get(isPackaged ? source.path : source);
      } else {
        throw e;
      }
    }

    // Select project
    AppManager.selectedProject = project;

    this._telemetry.actionOccurred("webideImportProject");
  }),

  // Remember the last selected project on the runtime
  saveLastSelectedProject: function () {
    let shouldRestore = Services.prefs.getBoolPref("devtools.webide.restoreLastProject");
    if (!shouldRestore) {
      return;
    }

    // Ignore unselection of project on runtime disconnection
    if (!AppManager.connected) {
      return;
    }

    let project = "", type = "";
    let selected = AppManager.selectedProject;
    if (selected) {
      if (selected.type == "runtimeApp") {
        type = "runtimeApp";
        project = selected.app.manifestURL;
      } else if (selected.type == "mainProcess") {
        type = "mainProcess";
      } else if (selected.type == "packaged" ||
                 selected.type == "hosted") {
        type = "local";
        project = selected.location;
      }
    }
    if (type) {
      Services.prefs.setCharPref("devtools.webide.lastSelectedProject",
                                 type + ":" + project);
    } else {
      Services.prefs.clearUserPref("devtools.webide.lastSelectedProject");
    }
  },

  autoSelectProject: function () {
    if (AppManager.selectedProject) {
      return;
    }
    let shouldRestore = Services.prefs.getBoolPref("devtools.webide.restoreLastProject");
    if (!shouldRestore) {
      return;
    }
    let pref = Services.prefs.getCharPref("devtools.webide.lastSelectedProject");
    if (!pref) {
      return;
    }
    let m = pref.match(/^(\w+):(.*)$/);
    if (!m) {
      return;
    }
    let [_, type, project] = m;

    if (type == "local") {
      let lastProject = AppProjects.get(project);
      if (lastProject) {
        AppManager.selectedProject = lastProject;
      }
    }

    // For other project types, we need to be connected to the runtime
    if (!AppManager.connected) {
      return;
    }

    if (type == "mainProcess" && AppManager.isMainProcessDebuggable()) {
      AppManager.selectedProject = {
        type: "mainProcess",
        name: Strings.GetStringFromName("mainProcess_label"),
        icon: AppManager.DEFAULT_PROJECT_ICON
      };
    } else if (type == "runtimeApp") {
      let app = AppManager.apps.get(project);
      if (app) {
        AppManager.selectedProject = {
          type: "runtimeApp",
          app: app.manifest,
          icon: app.iconURL,
          name: app.manifest.name
        };
      }
    }
  },

  /** ******** DECK **********/

  setupDeck: function () {
    let iframes = document.querySelectorAll("#deck > iframe");
    for (let iframe of iframes) {
      iframe.tooltip = "aHTMLTooltip";
    }
  },

  resetFocus: function () {
    document.commandDispatcher.focusedElement = document.documentElement;
  },

  selectDeckPanel: function (id) {
    let deck = document.querySelector("#deck");
    if (deck.selectedPanel && deck.selectedPanel.id === "deck-panel-" + id) {
      // This panel is already displayed.
      return;
    }
    this.resetFocus();
    let panel = deck.querySelector("#deck-panel-" + id);
    let lazysrc = panel.getAttribute("lazysrc");
    if (lazysrc) {
      panel.removeAttribute("lazysrc");
      panel.setAttribute("src", lazysrc);
    }
    deck.selectedPanel = panel;
  },

  resetDeck: function () {
    this.resetFocus();
    let deck = document.querySelector("#deck");
    deck.selectedPanel = null;
  },

  checkRuntimeVersion: Task.async(function* () {
    if (AppManager.connected) {
      let { client } = AppManager.connection;
      let report = yield client.checkRuntimeVersion(AppManager.listTabsForm);
      if (report.incompatible == "too-recent") {
        this.reportError("error_runtimeVersionTooRecent", report.runtimeID,
          report.localID);
      }
      if (report.incompatible == "too-old") {
        this.reportError("error_runtimeVersionTooOld", report.runtimeVersion,
          report.minVersion);
      }
    }
  }),

  /** ******** TOOLBOX **********/

  /**
   * There are many ways to close a toolbox:
   *   * Close button inside the toolbox
   *   * Toggle toolbox wrench in WebIDE
   *   * Disconnect the current runtime gracefully
   *   * Yank cord out of device
   *   * Close or crash the app/tab
   * We can't know for sure which one was used here, so reset the
   * |toolboxPromise| since someone must be destroying it to reach here,
   * and call our own close method.
   */
  _onToolboxClosed: function (promise, iframe) {
    // Only save toolbox size, disable wrench button, workaround focus issue...
    // if we are closing the last toolbox:
    //  - toolboxPromise is nullified by destroyToolbox and is still null here
    //    if no other toolbox has been opened in between,
    //  - having two distinct promise means we are receiving closed event
    //    for a previous, non-current, toolbox.
    if (!this.toolboxPromise || this.toolboxPromise === promise) {
      this.toolboxPromise = null;
      this.resetFocus();
      Services.prefs.setIntPref("devtools.toolbox.footer.height", iframe.height);

      let splitter = document.querySelector(".devtools-horizontal-splitter");
      splitter.setAttribute("hidden", "true");
      document.querySelector("#action-button-debug").removeAttribute("active");
    }
    // We have to destroy the iframe, otherwise, the keybindings of webide don't work
    // properly anymore.
    iframe.remove();
  },

  destroyToolbox: function () {
    // Only have a live toolbox if |this.toolboxPromise| exists
    if (this.toolboxPromise) {
      let toolboxPromise = this.toolboxPromise;
      this.toolboxPromise = null;
      return toolboxPromise.then(toolbox => toolbox.destroy());
    }
    return promise.resolve();
  },

  createToolbox: function () {
    // If |this.toolboxPromise| exists, there is already a live toolbox
    if (this.toolboxPromise) {
      return this.toolboxPromise;
    }

    let iframe = document.createElement("iframe");
    iframe.id = "toolbox";

    // Compute a uid on the iframe in order to identify toolbox iframe
    // when receiving toolbox-close event
    iframe.uid = new Date().getTime();

    let height = Services.prefs.getIntPref("devtools.toolbox.footer.height");
    iframe.height = height;

    let promise = this.toolboxPromise = AppManager.getTarget().then(target => {
      return this._showToolbox(target, iframe);
    }).then(toolbox => {
      // Destroy the toolbox on WebIDE side before
      // toolbox.destroy's promise resolves.
      toolbox.once("destroyed", this._onToolboxClosed.bind(this, promise, iframe));
      return toolbox;
    }, console.error);

    return this.busyUntil(this.toolboxPromise, "opening toolbox");
  },

  _showToolbox: function (target, iframe) {
    let splitter = document.querySelector(".devtools-horizontal-splitter");
    splitter.removeAttribute("hidden");

    document.querySelector("notificationbox").insertBefore(iframe, splitter.nextSibling);
    let host = Toolbox.HostType.CUSTOM;
    let options = { customIframe: iframe, zoom: false, uid: iframe.uid };

    document.querySelector("#action-button-debug").setAttribute("active", "true");

    return gDevTools.showToolbox(target, null, host, options);
  },
};

EventEmitter.decorate(UI);

var Cmds = {
  quit: function () {
    window.close();
  },

  showProjectPanel: function () {
    ProjectPanel.toggleSidebar();
    return promise.resolve();
  },

  showRuntimePanel: function () {
    RuntimeScanners.scan();
    RuntimePanel.toggleSidebar();
  },

  disconnectRuntime: function () {
    let disconnecting = Task.spawn(function* () {
      yield UI.destroyToolbox();
      yield AppManager.disconnectRuntime();
    });
    return UI.busyUntil(disconnecting, "disconnecting from runtime");
  },

  takeScreenshot: function () {
    let url = AppManager.deviceFront.screenshotToDataURL();
    return UI.busyUntil(url.then(longstr => {
      return longstr.string().then(dataURL => {
        longstr.release().catch(console.error);
        UI.openInBrowser(dataURL);
      });
    }), "taking screenshot");
  },

  showRuntimeDetails: function () {
    UI.selectDeckPanel("runtimedetails");
  },

  showDevicePrefs: function () {
    UI.selectDeckPanel("devicepreferences");
  },

  play: Task.async(function* () {
    let busy;
    switch (AppManager.selectedProject.type) {
      case "packaged":
        busy = UI.busyWithProgressUntil(AppManager.installAndRunProject(),
                                        "installing and running app");
        break;
      case "hosted":
        busy = UI.busyUntil(AppManager.installAndRunProject(),
                            "installing and running app");
        break;
      case "runtimeApp":
        busy = UI.busyUntil(AppManager.launchOrReloadRuntimeApp(), "launching / reloading app");
        break;
      case "tab":
        busy = UI.busyUntil(AppManager.reloadTab(), "reloading tab");
        break;
    }
    if (!busy) {
      return promise.reject();
    }
    UI.onAction("play");
    return busy;
  }),

  stop: function () {
    return UI.busyUntil(AppManager.stopRunningApp(), "stopping app");
  },

  toggleToolbox: function () {
    UI.onAction("debug");
    if (UI.toolboxPromise) {
      UI.destroyToolbox();
      return promise.resolve();
    } else {
      return UI.createToolbox();
    }
  },

  removeProject: function () {
    AppManager.removeSelectedProject();
  },

  showTroubleShooting: function () {
    UI.openInBrowser(HELP_URL);
  },

  showAddons: function () {
    UI.selectDeckPanel("addons");
  },

  showPrefs: function () {
    UI.selectDeckPanel("prefs");
  },

  zoomIn: function () {
    if (UI.contentViewer.fullZoom < MAX_ZOOM) {
      UI.contentViewer.fullZoom += 0.1;
      Services.prefs.setCharPref("devtools.webide.zoom", UI.contentViewer.fullZoom);
    }
  },

  zoomOut: function () {
    if (UI.contentViewer.fullZoom > MIN_ZOOM) {
      UI.contentViewer.fullZoom -= 0.1;
      Services.prefs.setCharPref("devtools.webide.zoom", UI.contentViewer.fullZoom);
    }
  },

  resetZoom: function () {
    UI.contentViewer.fullZoom = 1;
    Services.prefs.setCharPref("devtools.webide.zoom", 1);
  }
};
PK
!<Խ���(�( chrome/webide/content/webide.xul<?xml version="1.0"?>

<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 window [
  <!ENTITY % webideDTD SYSTEM "chrome://devtools/locale/webide.dtd" >
  %webideDTD;
]>

<?xml-stylesheet href="chrome://global/skin/global.css"?>
<?xml-stylesheet href="resource://devtools/client/themes/common.css"?>
<?xml-stylesheet href="chrome://webide/skin/webide.css"?>

<window id="webide" 
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        xmlns:html="http://www.w3.org/1999/xhtml"
        title="&windowTitle;"
        windowtype="devtools:webide"
        macanimationtype="document"
        fullscreenbutton="true"
        screenX="4" screenY="4"
        width="800" height="600"
        persist="screenX screenY width height sizemode">

  <script type="application/javascript" src="chrome://global/content/globalOverlay.js"></script>
  <script type="application/javascript" src="project-panel.js"></script>
  <script type="application/javascript" src="runtime-panel.js"></script>
  <script type="application/javascript" src="webide.js"></script>

<script type="application/javascript" src="chrome://global/content/editMenuOverlay.js"/>
<commandset id="editMenuCommands">
  <commandset id="editMenuCommandSetAll" commandupdater="true" events="focus,select"
              oncommandupdate="goUpdateGlobalEditMenuItems()"/>
  <commandset id="editMenuCommandSetUndo" commandupdater="true" events="undo"
              oncommandupdate="goUpdateUndoEditMenuItems()"/>
  <commandset id="editMenuCommandSetPaste" commandupdater="true" events="clipboard"
              oncommandupdate="goUpdatePasteMenuItems()"/>
  <command id="cmd_undo" oncommand="goDoCommand('cmd_undo')"/>
  <command id="cmd_redo" oncommand="goDoCommand('cmd_redo')"/>
  <command id="cmd_cut" oncommand="goDoCommand('cmd_cut')"/>
  <command id="cmd_copy" oncommand="goDoCommand('cmd_copy')"/>
  <command id="cmd_paste" oncommand="goDoCommand('cmd_paste')"/>
  <command id="cmd_delete" oncommand="goDoCommand('cmd_delete')"/>
  <command id="cmd_selectAll" oncommand="goDoCommand('cmd_selectAll')"/>
  <command id="cmd_switchTextDirection" oncommand="goDoCommand('cmd_switchTextDirection');"/>
</commandset>

  <commandset id="mainCommandSet">
    <commandset id="webideCommands">
      <command id="cmd_quit" oncommand="Cmds.quit()"/>
      <command id="cmd_newApp" oncommand="Cmds.newApp()" label="&projectMenu_newApp_label;"/>
      <command id="cmd_importPackagedApp" oncommand="Cmds.importPackagedApp()" label="&projectMenu_importPackagedApp_label;"/>
      <command id="cmd_importHostedApp" oncommand="Cmds.importHostedApp()" label="&projectMenu_importHostedApp_label;"/>
      <command id="cmd_showDevicePrefs" label="&runtimeMenu_showDevicePrefs_label;" oncommand="Cmds.showDevicePrefs()"/>
      <command id="cmd_showSettings" label="&runtimeMenu_showSettings_label;" oncommand="Cmds.showSettings()"/>
      <command id="cmd_removeProject" oncommand="Cmds.removeProject()" label="&projectMenu_remove_label;"/>
      <command id="cmd_showProjectPanel" oncommand="Cmds.showProjectPanel()"/>
      <command id="cmd_showRuntimePanel" oncommand="Cmds.showRuntimePanel()"/>
      <command id="cmd_disconnectRuntime" oncommand="Cmds.disconnectRuntime()" label="&runtimeMenu_disconnect_label;"/>
      <command id="cmd_showRuntimeDetails" oncommand="Cmds.showRuntimeDetails()" label="&runtimeMenu_showDetails_label;"/>
      <command id="cmd_takeScreenshot" oncommand="Cmds.takeScreenshot()" label="&runtimeMenu_takeScreenshot_label;"/>
      <command id="cmd_showAddons" oncommand="Cmds.showAddons()"/>
      <command id="cmd_showPrefs" oncommand="Cmds.showPrefs()"/>
      <command id="cmd_showTroubleShooting" oncommand="Cmds.showTroubleShooting()"/>
      <command id="cmd_play" oncommand="Cmds.play()"/>
      <command id="cmd_stop" oncommand="Cmds.stop()" label="&projectMenu_stop_label;"/>
      <command id="cmd_toggleToolbox" oncommand="Cmds.toggleToolbox()"/>
      <command id="cmd_zoomin" label="&viewMenu_zoomin_label;" oncommand="Cmds.zoomIn()"/>
      <command id="cmd_zoomout" label="&viewMenu_zoomout_label;" oncommand="Cmds.zoomOut()"/>
      <command id="cmd_resetzoom" label="&viewMenu_resetzoom_label;" oncommand="Cmds.resetZoom()"/>
    </commandset>
  </commandset>

  <menubar id="main-menubar">
    <menu id="menu-project" label="&projectMenu_label;" accesskey="&projectMenu_accesskey;">
      <menupopup id="menu-project-popup">
        <menuitem command="cmd_newApp" accesskey="&projectMenu_newApp_accesskey;"/>
        <menuitem command="cmd_importPackagedApp" accesskey="&projectMenu_importPackagedApp_accesskey;"/>
        <menuitem command="cmd_importHostedApp" accesskey="&projectMenu_importHostedApp_accesskey;"/>
        <menuitem id="menuitem-show_projectPanel" command="cmd_showProjectPanel" key="key_showProjectPanel" label="&projectMenu_selectApp_label;" accesskey="&projectMenu_selectApp_accesskey;"/>
        <menuseparator/>
        <menuitem command="cmd_play" key="key_play" label="&projectMenu_play_label;" accesskey="&projectMenu_play_accesskey;"/>
        <menuitem command="cmd_stop" accesskey="&projectMenu_stop_accesskey;"/>
        <menuitem command="cmd_toggleToolbox" key="key_toggleToolbox" label="&projectMenu_debug_label;" accesskey="&projectMenu_debug_accesskey;"/>
        <menuseparator/>
        <menuitem command="cmd_removeProject" accesskey="&projectMenu_remove_accesskey;"/>
        <menuseparator/>
        <menuitem command="cmd_showPrefs" label="&projectMenu_showPrefs_label;" accesskey="&projectMenu_showPrefs_accesskey;"/>
        <menuitem command="cmd_showAddons" label="&projectMenu_manageComponents_label;" accesskey="&projectMenu_manageComponents_accesskey;"/>
      </menupopup>
    </menu>

    <menu id="menu-runtime" label="&runtimeMenu_label;" accesskey="&runtimeMenu_accesskey;">
      <menupopup id="menu-runtime-popup">
        <menuitem command="cmd_takeScreenshot" accesskey="&runtimeMenu_takeScreenshot_accesskey;"/>
        <menuitem command="cmd_showRuntimeDetails" accesskey="&runtimeMenu_showDetails_accesskey;"/>
        <menuitem command="cmd_showDevicePrefs" accesskey="&runtimeMenu_showDevicePrefs_accesskey;"/>
        <menuitem command="cmd_showSettings" accesskey="&runtimeMenu_showSettings_accesskey;"/>
        <menuseparator/>
        <menuitem command="cmd_disconnectRuntime" accesskey="&runtimeMenu_disconnect_accesskey;"/>
      </menupopup>
    </menu>

    <menu id="menu-view" label="&viewMenu_label;" accesskey="&viewMenu_accesskey;">
      <menupopup id="menu-ViewPopup">
        <menuseparator/>
        <menuitem command="cmd_zoomin" key="key_zoomin" accesskey="&viewMenu_zoomin_accesskey;"/>
        <menuitem command="cmd_zoomout" key="key_zoomout" accesskey="&viewMenu_zoomout_accesskey;"/>
        <menuitem command="cmd_resetzoom" key="key_resetzoom" accesskey="&viewMenu_resetzoom_accesskey;"/>
      </menupopup>
    </menu>

  </menubar>

  <keyset id="mainKeyset">
    <key key="&key_quit;" id="key_quit" command="cmd_quit" modifiers="accel"/>
    <key key="&key_showProjectPanel;" id="key_showProjectPanel" command="cmd_showProjectPanel" modifiers="accel"/>
    <key key="&key_play;" id="key_play" command="cmd_play" modifiers="accel"/>
    <key keycode="&key_toggleToolbox;" id="key_toggleToolbox" command="cmd_toggleToolbox"/>
    <key key="&key_zoomin;" id="key_zoomin" command="cmd_zoomin" modifiers="accel"/>
    <key key="&key_zoomin2;" id="key_zoomin2" command="cmd_zoomin" modifiers="accel"/>
    <key key="&key_zoomout;" id="key_zoomout" command="cmd_zoomout" modifiers="accel"/>
    <key key="&key_resetzoom;" id="key_resetzoom" command="cmd_resetzoom" modifiers="accel"/>
  </keyset>

  <tooltip id="aHTMLTooltip" page="true"/>

  <toolbar id="main-toolbar">

    <vbox flex="1">
      <hbox id="action-buttons-container" class="busy">
        <toolbarbutton id="action-button-play"  class="action-button" command="cmd_play" tooltiptext="&projectMenu_play_label;"/>
        <toolbarbutton id="action-button-stop"  class="action-button" command="cmd_stop" tooltiptext="&projectMenu_stop_label;"/>
        <toolbarbutton id="action-button-debug" class="action-button" command="cmd_toggleToolbox" tooltiptext="&projectMenu_debug_label;"/>
        <hbox id="action-busy" align="center">
          <html:img id="action-busy-undetermined" src="chrome://webide/skin/throbber.svg"/>
          <progressmeter id="action-busy-determined"/>
        </hbox>
      </hbox>

      <hbox id="panel-buttons-container">
        <spacer flex="1"/>
        <toolbarbutton id="runtime-panel-button" class="panel-button">
          <image class="panel-button-image"/>
          <label class="panel-button-label" value="&runtimeButton_label;"/>
        </toolbarbutton>
      </hbox>

    </vbox>
  </toolbar>

  <notificationbox flex="1" id="notificationbox">
    <div flex="1" id="deck-panels">
      <vbox id="project-listing-panel" class="project-listing panel-list" flex="1">
        <div id="project-listing-wrapper" class="panel-list-wrapper">
          <iframe id="project-listing-panel-details" flex="1" src="project-listing.xhtml" tooltip="aHTMLTooltip"/>
        </div>
      </vbox>
      <splitter class="devtools-side-splitter" id="project-listing-splitter"/>
      <deck flex="1" id="deck" selectedIndex="-1">
        <iframe id="deck-panel-details" flex="1" src="details.xhtml"/>
        <iframe id="deck-panel-addons" flex="1" src="addons.xhtml"/>
        <iframe id="deck-panel-prefs" flex="1" src="prefs.xhtml"/>
        <iframe id="deck-panel-runtimedetails" flex="1" lazysrc="runtimedetails.xhtml"/>
        <iframe id="deck-panel-devicepreferences" flex="1" lazysrc="devicepreferences.xhtml"/>
      </deck>
      <splitter class="devtools-side-splitter" id="runtime-listing-splitter"/>
      <vbox id="runtime-listing-panel" class="runtime-listing panel-list" flex="1">
        <div id="runtime-listing-wrapper" class="panel-list-wrapper">
          <iframe id="runtime-listing-panel-details" flex="1" src="runtime-listing.xhtml" tooltip="aHTMLTooltip"/>
        </div>
      </vbox>
    </div>
    <splitter hidden="true" class="devtools-horizontal-splitter" orient="vertical"/>
    <!-- toolbox iframe will be inserted here -->
  </notificationbox>

</window>
PK
!<�+		"chrome/webide/content/wifi-auth.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { require } =
  ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
const Services = require("Services");
const QR = require("devtools/shared/qrcode/index");

window.addEventListener("load", function () {
  document.getElementById("close").onclick = () => window.close();
  document.getElementById("no-scanner").onclick = showToken;
  document.getElementById("yes-scanner").onclick = hideToken;
  buildUI();
}, {once: true});

function buildUI() {
  let { oob } = window.arguments[0];
  createQR(oob);
  createToken(oob);
}

function createQR(oob) {
  let oobData = JSON.stringify(oob);
  let imgData = QR.encodeToDataURI(oobData, "L" /* low quality */);
  document.querySelector("#qr-code img").src = imgData.src;
}

function createToken(oob) {
  let token = oob.sha256.replace(/:/g, "").toLowerCase() + oob.k;
  document.querySelector("#token pre").textContent = token;
}

function showToken() {
  document.querySelector("body").setAttribute("token", "true");
}

function hideToken() {
  document.querySelector("body").removeAttribute("token");
}
PK
!<�(��cc%chrome/webide/content/wifi-auth.xhtml<?xml version="1.0" encoding="UTF-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/. -->

<!DOCTYPE html [
  <!ENTITY % webideDTD SYSTEM "chrome://devtools/locale/webide.dtd" >
  %webideDTD;
]>

<html id="devtools:wifi-auth" xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta charset="utf8"/>
    <link rel="stylesheet" href="chrome://webide/skin/deck.css" type="text/css"/>
    <link rel="stylesheet" href="chrome://webide/skin/wifi-auth.css" type="text/css"/>
    <script type="application/javascript" src="chrome://webide/content/wifi-auth.js"></script>
  </head>
  <body>

    <div id="controls">
      <a id="close">&deck_close;</a>
    </div>

    <h3 id="header">&wifi_auth_header;</h3>
    <div id="scan-request">&wifi_auth_scan_request;</div>

    <div id="qr-code">
      <div id="qr-code-wrapper">
        <img/>
      </div>
      <a id="no-scanner" class="toggle-scanner">&wifi_auth_no_scanner;</a>
      <div id="qr-size-note">
        <h5>&wifi_auth_qr_size_note;</h5>
      </div>
    </div>

    <div id="token">
      <div>&wifi_auth_token_request;</div>
      <pre id="token-value"/>
      <a id="yes-scanner" class="toggle-scanner">&wifi_auth_yes_scanner;</a>
    </div>

  </body>
</html>
PK
!<�/���chrome/webide/skin/addons.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

button {
  line-height: 20px;
  font-size: 1em;
  height: 30px;
  max-height: 30px;
  min-width: 120px;
  padding: 3px;
  color: #737980;
  border: 1px solid rgba(23,50,77,.4);
  border-radius: 5px;
  background-color: #f1f1f1;
  background-image: linear-gradient(#fff, rgba(255,255,255,.1));
  box-shadow: 0 1px 1px 0 #fff, inset 0 2px 2px 0 #fff;
  text-shadow: 0 1px 1px #fefffe;
  -moz-appearance: none;
}

button:hover {
  background-image: linear-gradient(#fff, rgba(255,255,255,.6));
  cursor: pointer;
}

button:hover:active {
  background-image: linear-gradient(rgba(255,255,255,.1), rgba(255,255,255,.6));
}

progress {
  height: 30px;
  vertical-align: middle;
  padding: 0;
  width: 120px;
}

li {
  margin: 20px 0;
}

.name {
  display: inline-block;
  min-width: 280px;
}

.status {
  display: inline-block;
  min-width: 120px;
}

.warning {
  color: #F06;
  margin: 0;
  font-size: 0.9em;
}

li[status="unknown"],
li > .uninstall-button,
li > .install-button,
li > progress {
  display: none;
}

li[status="installed"] > .uninstall-button,
li[status="uninstalled"] > .install-button,
li[status="preparing"] > progress,
li[status="downloading"] > progress,
li[status="installing"] > progress {
  display: inline;
}

li:not([status="uninstalled"]) > .warning {
  display: none;
}
PK
!<���00"chrome/webide/skin/config-view.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

html, body {
  background: white;
}

.action {
  display: inline;
}

.action[hidden] {
  display: none;
}

#device-fields {
  font-family: sans-serif;
  padding-left: 6px;
  width: 100%;
  table-layout: auto;
  margin-top: 110px;
}

#custom-value-name {
  width: 50%;
}

header {
  background-color: rgba(255, 255, 255, 0.8);
  border-bottom: 1px solid #EEE;
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 90px;
  padding: 10px 20px;
}

#device-fields td {
  background-color: #F9F9F9;
  border-bottom: 1px solid #CCC;
  border-right: 1px solid #FFF;
  font-size: 0.75em;
}

#device-fields td:first-child {
  max-width: 250px;
  min-width: 150px;
}

#device-fields td.preference-name, #device-fields td.setting-name {
  width: 50%;
  min-width: 400px;
  word-break: break-all;
}

#device-fields button {
  display: inline-block;
  font-family: sans-serif;
  font-size: 0.7rem;
  white-space: nowrap;
}

#device-fields tr.hide, #device-fields button.hide {
  display: none;
}

#device-fields .custom-input {
  width: 130px;
}

#search {
  margin-bottom: 20px;
  width: 100%;
}

#search-bar {
  width: 80%;
}
PK
!</��&&chrome/webide/skin/deck.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

html {
  font: message-box;
  font-size: 0.9em;
  font-weight: normal;
  margin: 0;
  height: 100%;
  color: #737980;
  background-color: #ededed;
}

body {
  margin: 0;
  padding: 20px;
  background-image: linear-gradient(#fff, #ededed 100px);
}

.text-input {
  display: flex;
}

.text-input input {
  flex: 0.5;
  margin-left: 5px;
}

h1 {
  font-size: 2em;
  font-weight: lighter;
  line-height: 1.2;
  margin: 0;
  margin-bottom: .5em;
}

#controls {
  float: right;
  position: relative;
  top: -10px;
  right: -10px;
}

#controls > a {
  color: #4C9ED9;
  font-size: small;
  cursor: pointer;
  border-bottom: 1px dotted;
  margin-left: 10px;
}

table {
  font-family: monospace;
  border-collapse: collapse;
}

th, td {
  padding: 5px;
  border: 1px solid #eee;
}

th {
  min-width: 100px;
}

th:first-of-type, td:first-of-type {
  text-align: left;
}

li {
  list-style: none;
  padding: 2px;
}

li > label:hover {
  background-color: rgba(0,0,0,0.02);
}

li > label > span {
  display: inline-block;
}

input, select {
  box-sizing: border-box;
}

select {
  padding-top: 2px;
  padding-bottom: 2px;
}
PK
!<�j�
�
'chrome/webide/skin/default-app-icon.png�PNG


IHDRxx�m�Y
�IDATx��li���?���ѭ������u'.,3333sZ/eO��殴\�V|��||'8�ɒ�J�~�:����ݩc�3Y���]�Ig���������}ƒ�?�!Gݔ�h��A?\�H�T�'�N˽��?�V�`:�Π���~E3�O���S�2��!@9y�ELC4Ks���цLG�m�{B�^�������8/�;�<��
���9*��X���L�}�`�НTԱ!��򌑏	LCt�����t���L'��:�|�]F0�)˃�SzY��������!�)F�&��|h�6�B�C�8����nH`JS�eಒeT�t`����X)�<]����\F2�6F0�IH�s�	�N&0]�q�|	�24�P�k�2��L@0�T��8M�A.�L1���0_���Yde�b�EJ���L���$3�l�X�v��LN�}�"���.��ӰCB�v�-�Ĝ%W�ǧ��!�<{��U�-cT��z=V#���O"ɼT=������`Uj&�����w��g���"�ӥLX����0
���<{S��ގt�]w�<�D�qr	$�v���w7xٰ�ʜ�Y���+�td�t:Ve#Qܹ�s�SH2�/ᑪ�G@��T.���~�e�3�]Bg�d̂+�N�
�9�)��cK��`lV2I�sЉ�����=�*�E�~��c�Pc�v�p��W�@��x[bK���������7���ɞ@V�*��J籂M5�w��s�H�a�&�;�18��g��x��ʦap��q��g�`J�S�{)�W�J�#�
���h�LpX�P�>�}>����������G���8�A��!
���ʮ����vɔw>���q1?�S�>�[��TV��>�Lk`��`�v�>����/w�~�Y#������`�
���Wf��C����V�:lw;B�����$1��qo���S���
��aw/_�J���i���S��݄u��A�/_�M���ku.���Yf0������ ��`�k�ԧ�U��V��������a��3Xk�Qkk%ۼ�ylF@��k4�W��Z�Y��%؁mz��\��r������8�
|��?�a^��.쯱��-��{��~�ʝ���Ov�=�?����z���˫Y2A�?����"	+x>�/��U�>��z�#��z+��,��W<7�?׻l
�[�5�#p9�L󗟼�4��р��}��a?KO�|��;}�{��3�	�:�����op�ύ"�V_������E�a��j�zТ��OF0�U(x�!�x|0������	8��O-��ì?�;��/Ȁs�?��(����e�C�<)v����,��y`�f�w+��?oΏ!�~�a��A0����۴fn��i�gw��:80s%���(+xg�M�0g�/O`,F�nvp{�u�)�._�1�8#���w�`��[��?�0�֗��x�*�Q�	����4~y)6�+��}h��x���ۆ�Ò_G[���`����q�AL���ڈ�OK��܉x	���1�6a�	��ƭ�Gf���3?��եi�	�Fy����z1�n�+6�]%�u0��5��D�ܥ�<�:X�s)���wc=hYTOy�xe�V�EK�,�-��}���3�	8��+������e-:�ܮ�h��|�Y�p`�]��O_~�Ô>�rO�?9xf�<Z{��&`�ނs*�
hֺ���;oK��=�z�ƭ���Eh��6PI�U2?p�:�Vc��_V��-�j���̟� 4����<�q��c�֥e޹�-��ܳ��V�%ô4UG��8F��*�s+��71�}�S��A3Z���2 v�����K������ہ��>��#fn#6w�I�;�˖��Z�Po��*�%ΌT+�Iw�I�#�'�61׫���5<���5(��:�BL��륍���
&�]�Q8j��0�Npbzhҟ2-�4�r�`���7�5�%9ca�u���;x�y|׀`�H�q��i$���J�.^Ľ+Y���JP+8b|oH����}�b��½1VnX0�gD�Kr�ȧ0Q����j��ܛ���m��fos�[�Cfq\����mXq��e+X��Ūh���\�������ʩ�p`���K�ۘ�&������]�
j����bƝ&ke��.K���f��)�=6&dv
�e�� ������$��Y>������p�G#c7a��<���v�Q+s&�y���Vl\��'�VT�\�t�e�����ȷؼćv[h�&j�~�`�U��	�6ƱCU�yuHc�G-�O�e�ݲH����	�!�
��\syUy��5�b���"�0�vV�
������P[C��	Lb�a��ײa��#3 �я�""�1��H��H�pb�膥�Eq��)~�xN�1�����tw@�*�(QS"�/l!����$ƱM‡a�o@�N�JtA�vN���-G����-aR�3E�<�;k����(Q�~/��0/]�/d��ail�M`I4KDuf���+�׽��x`o)ˈ�șHH�d�@�$�O֋x�.l��a(DN!#�凴k�bF�J���Kf��8�%�HկČ�S�S��>�U��i�=�C��T�~�дܻU�5�K�}�r���BxfIEND�B`�PK
!<�(��chrome/webide/skin/details.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

body {
  margin: 0;
  background-color: white;
  font: message-box;
}

.hidden {
  display: none;
}

h1, h3, p {
  margin: 0;
}

#toolbar {
  background-color: #D8D8D8;
  border-bottom: 1px solid #AAA;
}

#toolbar > button {
  -moz-appearance: none;
  background-color: transparent;
  border-width: 0 1px 0 0;
  border-color: #AAA;
  border-style: solid;
  margin: 0;
  padding: 0 12px;
  font-family: inherit;
  font-weight: bold;
  height: 24px;
}

#toolbar > button:hover {
  background-color: #CCC;
  cursor: pointer;
}

#validation_status {
  float: right;
  text-transform: uppercase;
  font-size: 10px;
  line-height: 24px;
  padding: 0 12px;
  color: white;
}


header {
  padding: 20px 0;
}

header > div {
  vertical-align: top;
  display: flex;
  flex-direction: column;
}

#icon {
  height: 48px;
  width: 48px;
  float: left;
  margin: 0 20px;
}

h1, #type {
  line-height: 24px;
  height: 24px; /* avoid collapsing if empty */
  display: block;
}

h1 {
  font-size: 20px;
  overflow-x: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

#type {
  font-size: 10px;
  text-transform: uppercase;
  color: #777;
}

main {
  padding-left: 88px;
}

h3 {
  color: #999;
  font-size: 10px;
  font-weight: normal;
}

main > p {
  margin-bottom: 20px;
}

.validation_messages {
  margin-left: 74px;
  list-style: none;
  border-left: 4px solid transparent;
  padding: 0 10px;;
}


body.valid #validation_status {
  background-color: #81D135;
}

body.warning #validation_status {
  background-color: #FFAC00;
}

body.error #validation_status {
  background-color: #ED4C62;
}

#warningslist {
  border-color: #FFAC00
}

#errorslist {
  border-color: #ED4C62;
}

#validation_status > span {
  display: none;
}

body.valid #validation_status > .valid,
body.warning #validation_status > .warning,
body.error #validation_status > .error {
  display: inline;
}
PK
!<E���c�cchrome/webide/skin/icons.png�PNG


IHDR.,�j8Ic]IDATx�P$g��'Xvf!F��fq���Va��X����ݓbT8t���Թ�}��]����0����zk��~���o{�����6m�qN��矪��k����1p~�����qײ�����%�w�2��i_o5Gt��q4�bVs~�� .�I)�Ù�U�Ҫ�")��w8���]�؇��h�������HZ�$�|�E.�q	.�-�!r�����uI��ƛ��$��2�%؇� �8-���pN�87A\V:����8�@�4ZY��CD��&66�rss)==�BCC�X4<���������C���B^L,-׸�=C���j���}GK������{��U�?#]A��<!��ć��U�8u1^���uM+Q\@Z����|)tPZ����|Q{�SRR�


����233),,l'�ƑvJ��Jvv�z���|��7s��K���g�ж��n򸢇Vm�B��w�d����%������Ut���Ǖ����N:"�f�q������m�d������Q������,�iE� d�CR�q�����CR�q��������Kذa�n��DEEE���E�b��r���d��a!-���TRRB|ݶ�����]�{f�+޾ۣr�<��n�Vb��e	9v��r�[��úM��Y��G��l�ҦL��]���	�z��B���%q2g�:m����.�����VV�����r�B++i%Fg��9���&�d�	y�!�%d�e����g���,�"�\:-�rg��Z�g�3�	y�!VL���d9�Fi9�-�	}A-y��xT�ɥ՝������%��O��A�M]��	IZ:YZ�� .V���Tst��}�K�H5G�ɰ�g��v�ܙ���o6�U���22�Rr�"�r>�7-�d����ݸqc���h,���O�f�Tұ��~��FY>I�_Cn	���,�������i}A�ܐ*-'�����7oWsss���{�$/c�K�|δ�������uA\���C6�J��C6D�K^:::RY^!/BB����L���3��xGEE�H����ء(J�,rWC�D�1�:Eȋ������XN����nI���ԓ�X����5;��.�USS����H�2ř����앤eG{{��qY��"b�9UvH�,Gg�;XD#��i����CDp�y��!kya9��%�BIZ�x�ﬥ�n?FI ��	�K�sÐ�Uc��)qKz�{��%i	bi������Q��s�v�6wY�eH���9�]� �����t�xfFꂸ,#X��F�Z@Z�9�Q,=��&��2,���XV�-�����<��JM����?_��U�6��en�"�D��������[�%�=�?���8���

���,�k˖-i...Ò���#��?Y��!-R�e�@�Mq4�Ғ���Xzl����7�,#��\r�*/_|1���1$$䚸���XK�9�_1k�H�]���{��F�fB�}a��>�G����/�E_�	�Q��\���vw}�����Σ����s
�/�����qY~)I�,��)I�,��%���|����2.�Eʺu��w��_�^�.--%>���ݝLD������7�,/�B^��苿!݆-�/i!UZ��c�u�_�t���w��`y���mll�� .����1��#ϰ8�7��8�


�,'B^.��R�w
��iQ�L��"!w�۬dej�8�I�f�<*�S|�z��-�Wx&F��7�x#�C��GZ���� .����j��b\�{�f5Zi1:z�����fI��RVV��oZ�e
�{;�7�den�>��i:��?��r��~��E�W^y%��e�ZZx�w��Eꂸ,O��F�bX�9����4����۶m[.ϼ̈��Cmmm����Jm_����v]/S�Pm��O��s��|�3b���qY�ĥU��}g��Y\Z%	�wqf��i��&��_��n���AW-EB�za{Oމ�o�m�n�'�zꩧ��k�?����<*z�S��Y�7�(/���#���4�����o�Q^�G{��
�Fyy�����qy�K�[�Ŧqy1��\-�Ÿ��8� .�V�B-:lx��M=����⒡��<���,� .�<3.
GS��g�EY����Uf�
�s��� .���pN�)z�/`|�cw����~���h� .����ٗ¹'88�������ȿs���'y_�8�	�:-��&���@\���������T��Ƞ�����U#>_t�E�1q����"������HN+q�����鼒���D����PK�7o���2��TZZ�F|�


��BBB������~������ɔ��@?q�n�_B��	��?)7�8��G���q�F2�Lt�e�ѕW^I���TQQA���j�粲2***����7<<JD���/\9�Z\���v!-����������;()�u�_7d-��\�����]dC��`w���f�B�!�K��]�Op�,���NOΰ��]���}�|���ꮡ���W�TU��i�f'&if��	�����/�|6��|���U5�w��.�����)�v�K��� �O�.�/}����s�K�v���u���ZU�g�i�x���9���[{^e����4�ebZ�����a5��֤I�b�Cڪ�o��O���&�i����o��jBq���k��c�u
��t1p�֭��۷�cx�)��O��Q�F7�A�w�6��b�d���VՔ��ﳻ�=Զm��={��
�%8IZ^���o�:�3hI���h ~D`��I�\4���G�(�����2�z��R
����&��ɱ��}�zխ�?UܡC�~`.-�s�:v��P��V��S�Ũ���\�nE�v�y�i��v�bW��LW��`��d1p�UGʋ�L��;餓x(��ż���i::A�A�~����}v���H$�*툕%Ehya͚5m<h1n�A�g��?@�$'L�b㈸���G��'�e9�s�`� H��2�2�K�.������Ү];��9�zA���/�C� Jh
�ݥ��}β����P��Sor5"#3��b�‹�4^�>}���:^̖-[~�{�_L���*��.~�ݥ���c"���<�nݺ֩@n
s�;&��^�3�aIL�8��v�e(�9�K�4�`d�yRu����R��-�\�a��s.D�Ν;�g\m�0<�[�������O��y�ۧ`*�����������@��@�K����8���j��Yd����(U���>���ɥ�\��RW�s�,��=��i)�D��\�kb��eb�\"��CK��C������ի��w��D�{��;wY�z�<q��^��{-���wРAt��^x(~Pu�w���[�7��w/���M���`0e�KZ������e�1^g�L��Ze"�=�b�$^�s�Z�&�&F�w���0��@����5d�JXK�;����qNaW]C�RH=&.�_ܘ��Jd�����3
\L��ә�'Hq�_�lв���
��u5�uԕa��k�T\���6�B�Ъ���w���F'�:Z\�"����kN���TV�$��;u ��Lmb�b"K�����WX47�����ιCR������ԡ�^�0k�<� \6�K���s}�wQ~��kYM>D��+��`�~���*������Ŀ�O�K~~�ſ����IAA��dn"�
�<Eh����wb�%z
յ6����=��e��b���͍�/��Y�"���矷/,,$�[֛-����#.&.������ؙ��K��ι�%�����w��(���A]�":P�l`ă��}��^z��͛7������G{�����W��k�W`�x1p1p	��X�Kr1p19��v2�&�{1�`]O����ԡc;3!�En�'	���6�`�+:z@r���4�Ϫ��~��?�M9G׾�v΄w�����H
.��[�.�\��K��f��(..�X\�ڕժ�30���$����{�x�e���t�İ�ƃ��T{�K=����A�*,əbq�M����X�,���%��e��y������>�A�4�%`��fH��m�i���� �m\�I���Ii�\ZN��P\hA�Cu���g-�^�5qhG,-[�Aˁ@H���I�<s�LxG�v��	K�˲����?�]<�Z��I�uX��p/���z���~rz�i�c��3nsC�Z�����a/�5��Ly\L�7�$iWܭd��W�
�����
�Dno
��C�E���eק
�0amY�A˾-:W��{(ޑ��{/_1As��h����T�l�Z���ٳgN��/}�U~�!w��:�⬓�����_S�W�[�X�D�zL\��B����t:�)U4�ų�Q1p	�L�8���%%��]�,��XYxoR�p�
�����-r�
+PG[����tPS�t�?����v���������^4bĈ��#G�Ç/R��j��e4Y-�*�c\��n���(�qޒ�m�>\�f��'w�qˋZ��OzOQ�1��˔y\e-j=��:��S�����	�&.I�_��'1)2�\����˗-�(M�RI6����
,�h�r˖-�����%2���5jԨι��@Kh��Ek� +.��k6}3�a%�U�g���Ɵ$a�-`c���5�0�=+\���a��:��}��.�]n�g]^��]^�7bU�?��|l։��u@�'^Ƚ��2et�E/���s�=���f�2���h6�8�01pIc�:oVi�/.a$͑�ubS�}WR\͕2L���ֶT�rG���1p�[�vmsR�@�x[`ZB���JR_n���d�b
�Z��*PC_�~u�2���K9�Eʯ��Nwc��]�ߗ%h��q�#�9G��9g?�r�)�E�{�'���e�|��>c��<X���{�Q�=����}�C{��v��	�����
�_e��dT�rm̀?ںu�^�L(Em9k�Tۂ�e�s�A��4�A9Z2��R�#i辞��9��z������ſ��D7?|����R��	��mw�uWo�o��p���Ț6m��gI�>�Z|�?��Nz�4RIz<���'@��Tl���?שS9����It�C,�1K�W�ׂ��t�̙#�sDŽ�ڂ��i��&>O=��Qiާ�M�6����\q�T��511p)���T7n�������懕���닊�z���%�m�>a�O{��� �=:g�ʲT��511p	>�$p�`�
h,0p1p��ae��ĵ�����Ow�5��eB��]�ˮ�w����zq� ���X̵ߏ?~��!Zc�*�.6?L�� u�!0�f�^�z��'L�0�����4c�S�3���w�L~Q�����N�'T�P6h=Z��Z��Z���P)C�*�8..������]Y�˱դS6p�N���� Т,��)S��]�pa�M��m�k(%(��:}S�n�̎;Zx����?S��x�Pq5��k׎�fҚ���U�Q��A�եБޜu�R�@_粺_����(C�U��T,��Bǭ�]o��FW�Cn$�_M

\Zh��B����GȠ��Ck����c��UP&�	k��,X��Y�e(���w�܋_u��~.n=�e��T��$^֜���ȓ=�Й�����m5��.-{	Z>
-^6P3��liC�;ߣ��#8F�Lno��t��pX�pɯ��LA����4e(����wy����P6@&.�,��.��5�\s��P6��]��jt
\Z��|���L�E����6��I�VMv��٬�~�x�"k�
�D�S'ɊW�c��l�z�.|I�����P6h=&.Aɢs��Wj��11$}p��.ᇖ}5H~ZJ-���2Q���x�B�?�Z-^Y��p�+��_	�k<۬q�M��w�e9�s�ʽ"��5%{���1t������ʟ�\��e(�9�����K�f
�;6�)����11p���~��π�m��4��>gΜ���W�ɻ�{�Ҟ����G�N��'x
&������Gyfy�}�c�r��sKR�GP�#��]��k�E%��S�ʱh9�ѹ����TJ����L!3��Tch�_в"�Ӳ}����
Zʷ���q9q.~�ho濎E���N���>J��W9-�ߜ�rP�2�����{�V(��_�*Ǣ�8�s�NU���yx�B;�z��b3��8�%��r��eePK˂�ZL؇hƌ�p���5K�7k��>�/�k���Ӷ��~�M9(�����]��K�R�ѹ~����R��ԃ>��2�\B-
[�j�*(��,˟S�_bRZZz�Rp?�����%=W�.+��������=��NvQ�O"�4��'b�11p	�)�ά,��)�{\�9��]в: �lcѸ��sM�<yYD�?~�.:F�(�2
਀g�w��"R���*������B���s��11p	�)���Bcc2��L!��@�?-�3�-[-Z�AK��b����w^�-��D�u���5i��Q��c��8 �sϚ=��ẏ��?�As����yc�%Sh̘13,S�%� $h�"(�,^��b�����`R}�f&P	\��2O����5Ҹ���cee�t�)�p-�Y��{��m۶%���*�>2p11p��R�$&S�8�2p	��hq�O6$W(F�$	�l^�d�E�dUڏ���I�&���q�L#�W�xq�J�^���UZ�o%d�I�5�Z��	�
)�\�{��%�q,??�	^>��%�E��ֽ쇴��stnl=&.�3�^z�g�B.!;��Zx�,,,�矷W��}���0�-^�W��J�`�D;3�,
���.\G��z��2��+���y3��y ��<�@i��Q�5��ؼy��Ү��ژ�uѧ=p�mL��:g߿��%[��:6;��_���K���L���?6Sh�2p	�θ+�D���=SHk��RZ�Dn�����>�x� �/�	��)���i@�^��||:�EY��o��~��yl|��P}�R�I�ei3�ws�V͋o^�ǐO��2��c�'���R�����DX6S�����^���W�$bQ ��h�租~ڂL&i��o�ݚ��@��o���bq^�W��"X^�d�ļij�pϰ"'��|�;�1S6ق�;'L�0����=׬Y�/Ӧ�<�b]��ャe9�sS���["�
�kY���ldż~oJ>vuƭ�	V��w�e9�seb���Gg�Sh�O�����D���u*����ք��1y3�1*��k�1�]����r�M7���B�u��F�2��]$�E�(���"�wZ�g��7�ƶ,��J���SmX�z�(��9Q=&.�6�����ѣGό�:�:��/��� �x3�����3����θO0��A��*_�V���o3�����e��7���Z��/6��ב�{�4�����G�P|�M]w��o��ų@����T4���h{�9�e9�s�[O�E��.|NX�����"F,S�/�9R��Q���n8[��Rf�lG�������:��z�yj��-U(���N�:F`���FXK���ba�m�e�E�'fZ�;�?�v�seՓ�?�.|NV���P/�l�_<S���*/'�B�_q��	R~P�"�Bg�/?��ҥK���Z�}Ydǎ-�͛7D���u��_�Y����D]E�Ŀ���9[��#پ1����gr�@e�F���b����H$���ٲe���l{��c�+�T�~���F�v6h����y��G�o����2��r�`�)YPVB�I�KKq�IW
T�����wߵ�ܘ6�,a�r��]l��b�bbb/p�%�թ��R����҃��2Mm�YX<W�%Tvp~��w|_g�ZW�}g:������'��H��s�6�WAH�Ns�H��1�<8i�YЎ��L����8r�q�r�Z�ƋB\Չ����i�n݊��#�r�ꨵG�ijb=�˅�u�_<1��o��2��`a^v�d�_r�������������?����O�����_��#���/�?�?�}..�c�ضr��y�?�]�6�m�c�� \�@�4��;�f����.�16K�@�ә�[�;�.Ǟ0ٷ�wf�.��»)M�>o�sn�*�Mib�y۞s�>�K�F��
�p@�4�u���&���^�t	��q�k��e�}r��"\.�� \.L�pA� \��p.��%\.c@� \��p��25��.��@� \��pA� \��y�6��K�"6�ܦ.�Kߎ�k�å�c�z9\.�&__�.M���4\.�E��ˈ�h�y)�����˶'J6�YlY����p�D�&6�-q3O�@��/��ǖi�'9^�����p>6��wanc�y��Y�D�@�S鹗w����!��9s�)__%z˼�������k��:�W��R��]t�!��w��.��ϝ��ڹ�.o.��u�GB���6��9v��!u!\ ����8��%��I�	|��#��Y�q�sl�(h�^��nb�ا�p�4�r�~v��u�)�{h�;��i)�5���@/�e�����c�s.98N��u⤴�p�ʄ˗����K�k�$��$\��O�]1Z�1�����e�<��x�7Z��u�c��"��x�p��\�����ny�6�e���p). \�+�ˋw\>$��pi���C�p�g\�c�������=�nZ�<Լ&!\�2��:�gw	���	��9��s�!\.��q��߅)�1ҝh�>��p)}c�>^�c.����"\�c�,��Ȳ>~ƾ��Ӟ ���ef
��n!�I����,���]��$8{�w���2�$:�]}�9����*^ի���l�˟�Y�]��1���of�XXX0�H���HL�R=�h����88��	}�B�m�2Z����M>�Yg��ť�LP�Nr)�d���X\4UF<�7Y*��'�ҽ���+������]��)��ㄶ�\�o�BZ&C�uP|��[��T#���O��蹮xi�ëDW�s����fj��/G|���0��.����1b��7[%|��}�U��L�I~���aaah�ƻT����S��E�0�������0�� �.C���)��7O�ez'y��3���BK/m�§�Dᆽ���-�����V�n��EE�>�-����E�r����@�vI��3t#�!t�	����d�M7Z����m�k*
�K
^̱g�o�·�]�����A�DR����⫐Ż�_�UL�<�9�7�����7mݙ��_�,�mpaaap1�� �z?���
0�Nׅ��	a�$t�gM���9���L���eMš�p.�8���RNn1U��a�h���E���#�;̿�k��du�(��H�y�ڽ�B��I�S�c�S�����ō�kx������UyD�V*��ޥzm�uFҺ
H
�����,@��[B�������x���~O�;^!3�a���ޥ{#�%}�,�e�^F��	p��8���R���M�Q�
![�
t	/2�"-;.޸_F�"haXy��]��jM���p��5�oGD�+xs��3����J�:�Z��O)aa�F
���v���N�ޛ���o�;V_�1b_f�K�q«x�4��p�бX����I���^���ϒb��{|�
A0���`~e-�W���o=(-AK�����\S6�4D�d��&�[�}L�#��9�:]��3���ɱ��6ք���	���c�/�7���?Ђ�6�����Er�Fk����_�*�2���H�ى>w}�D�:���]Fq<8.�%eB�$P�x	�
�%a۵􇥲-pkX]x-?�+h;�#�-V��`�m*��/X�mqD2�w�a��m2,��Ԁ ��7�>�ľȥ4(Z�C�j�E�4J!Mu�sI�]�Y\�\$4Ŝ���̪�(�a�
��aΉa1�����:>e.�w5������=�-h�:"q�h1R�����b_l�
�2������(0�6�P����7@
���Ƨ��{��C�j�ϑ������,"=�r�����������:Ӝ��"�WT�7e����Է�-�_���MD�&	Z~�����^X@��K��������pm�)x���0azv���	b���ӽ�=��
\R�@�,wuap�)��0Faapa!�� X� }����F&oQ-f��ه�.���&�Jv�a��W@�)p�"��|гc����H�Ѻ�q�2����cw�#���cH��Ti�$I�Ʊ�u��ER�apaq,ƚcv��ο�a�g��{;�_�,��!��rЂ`SԀ1Ǟ����;7U�Q�Ϸ�MX�C��%	7�)r��|ۯ~��8�>_�w��S�W�`���n?GP,�T䂦��wYapQ��>_.�(�F�n�M�1���:9����]6��
Z�@��oо�<�JmKkr�!�����7b۵�q	3:MM��v��Z�z�ҝr1�wD�IX���n;XXT:G���$e���%��ӑNx�n���`p�414_�ܯ��W����h��,��ϻ\�À3��۠��}�����<KmI�D�Ch�mX�߬���%�@��?I@���lˏ^�]�d��~mO<��E��"��iv.�1
`'�ƀ���a�n�ښ(��Z��;�D\<E-��;taHj&K����Z����)�W�"�d��)�g�!d3�C���@ 폭��K� ɿH�I�rV-�#Փ~EZ��u�2Xl<\,z.��,��c���"��5=3�0|
:Q:῟R��(dD��B`pa�m����ť�d���iNe�%�w��GQCGZ$��o`B��ĩ�k�H��.�䡥���;�c,yH�Qp��c���.��=\Ƽ�/Y]��3�1��G\d�_n��	���!�8���E��i�%�S��
�!����_�
�N���#h�b��Cꯉ3�q-2@�%�c��+�J��- D9������8p������먔
h\�#.��#[:!Z��
��_\T/��Ϟ�iVp�@�x	��P]�$M����5'��A�Y���>~	�5K�5q*�,G���#k臨�d	Z>�a�om][E�4ܷ<\�H˃ǽH`��ap�2��.ǿ0�x�JB���ݪ�Q�������dr��)��!��{Dk90!D�cI1>����c�{J�ִ��+�����W�(p	r`m����$�ǿ0���]w������ن���Q����h����/8���_�7�%V��7+Xr��W��!��!��@Q���*�#.,.Ώ���..nH�7���)
�i���q���b�T�b �bT;�+�%ۂ��oI����R�s;�
�b��'-��*rÒ*-׫�U�����#B�t\G�\\ܸ�������ELF��@��!�O�{��/)sF��S�gc�AY�@ݨ�Y:_�/��E[g�<�x.��ǰĿ-v֖8��Kέ/0���n�����\�����Ej~�G�l�T�~�@b�,;�^~����V�����,�q0��Ò�P�E!�t�BK�K�����R?� ��v@����.
.VkK�y�ݬ�[�@��e!w[�C]�U�%���(���i��M�����17��j�t]v,#�dˉ�)��ޥ{
-��
�[�Ѳ��֞��t)��l������q���6[v=,�q���K�����U�F�懹-��'�۱w�����Y��*�,I��I6+x���!>!
�
U	���\�$��W�0�(�(��'ti}�k̪��\�&M���y�����Q�Cg��@ �'�IǦ�=Y_Q����E��4h|�/x�%�A�H�&
�pp��\���0,.ٯU�ȋ�7LN>�P��E������<3��d�бb=�<t`yD������g��.C-�|a.�곦ߣ'ϊÕ�IVIcˤ'�Ul�Q�j�%��"Ů������u3apq\�����O��}ƞ0�.�_�~���E�#��bhA`m��-�n�7yWd�v+f�[��c�v�\�P|�"b�~Z6+n��ȧ�^��M�?yVϧ�ێ,.�	wr��/����N�x����0��޵�Q~d�p6p���v�������‚>B����14?T-�&
���B�?<Օ�?p�G1.�V�W�.ܴ�����y�B�z'�2�H�J������%?A��BK���3���_���i�Γ�����,�U�Kx��Z��Jgd
Mx)S�ߊ\�-j�8!ul�+���O,���L�?X~
�>���|�6Dž�B?N�u�)�4I�*J�4U�.NJ�fqcap�ؖ)��Bk�]�����mUB��-���{V)�d��*�bi�۲V�/���5��7#��9'-��e��@\��J�B�D&n���x#�bpQ)�|B�r:Z�)�k������N���=��<z�[SȦǎ:C�7��mr���"r�q<��/.�L����߲�L����"h�TSa�C�d�����q��m1-�uS�oY��89k/|jNL����퀡e�A�W\�2��[3���e=���B.*���4����y3~[4C�k�Tߵ�Xm�>*�SȷK�0�lޥ{/�uvK+�,�%�H�KI@�R�n#.�a�`2���J̙B.-�-�r
-ve���w$,O�}�	u�FQ>���B�x	���u�~B��H�U-�^�!�v��R\�j.���)���0����v�B��C˗-�@�K��(nҎH�纋Z^�YD�JĨ0��?[x��p��O 4Χ�p�)����2pIu��������tͳ0��2���[3�v_���B.-_i*�h\�Z��&������8��
�\|�2��Ko���t�l<@�2Gx�����k@�'ޥ{��5�+�A����&��v[M˾Vq0n�������b�)$�)4>�2�>�L!�C��-��-=dZ�"Rt��˒k��7Wӌ���Zv�G���,ǨCB�t��T�N�C�X4^�c��TqO�l%�g֦�;'���B�d��2*ëH۾��>z��g��`�T9 q��(5����e��2�>��9ęBr��rh�S�Sp�%Ń���W@�.SS�%h���A�
����wŋ�R^`y�9%�t�j����CnS��X�j�yd`%?iM��&i*
��#��!h�0�M��{��[��
![���4A�r��Er�,�r��2
�Ls}�\�	�}��I�B��d
qa'����}�MWdvc��v9f������+*_T�ez��@K����}�n�~�\�����\������b^`)1F$�Y/t�
�Z���ʿY��QM�����*s_Su��֞B���	X6
@���߹�ߙ(X�t�}�a*�U���q2�������fS��J�X���!v�B�L!O��#��blsL��=ھ�a̸mѦ��2в�^��{BZޜH=���f�|�,/���������{�!t�����j�k��Ѳ}�6v`�meW���|�e#�Ɨ�y@��4���8�\�b��)������r����y�u
�gZ���gnG&�{���G.��/A�ݾs�Z�߬H����	b^�����s��^k�J��P|�wX&YX2^DŽd��$���TW�E�m�w-i
�3N��7���)�vŷ�›x ����M�R�-��G@#ŵ����j��~n1������e��y�Z1��՛��f�7t'����$�3�����N��-7�&��C�(�/oi��6oU\X\GB����J�e����e�y��n��<���]o�t͚M��=�_����?пE�u��EcE	Z���-��w�,R�c��
��}���>�#�����/~�Z������:���B��.��Z�A|u2�0�x�H5r\X�y��g�ln��o׆��B[w��� ���4-�o\p PQ��ťb^���6u�(|�izI����z�x�.�v�K��Hu*�-��A�BKM�2\<\��1�0���Ԩ��Q�5��a۞��Cx�0o���$�ӚkÖl!A��r�я�O��(9f[��י�w>F�-�Y`=A�\?6W\K���k]ײ��O�W��mξ��[�TT57EA�K��6\���`��(��G˲d�V�7
��O�J�X0fɌ-��Vq}h�"u��ڀ��{e���)�b�w��6~d�x&O�tja��dIy
�߇�Mg��Lڊu<o�/c,U[���ݴ,
�q`��O`_��+�N^<Jnq
���ΥB-B���]��^C�E~�U������pB���+&e�������RZ�g�ߓ�*��"ҹ-$���QZǀm<al���;�q\�0l
C�B�J!J�J!J!J!r�p�TYۭ��Ǒ�}�QW�j�9��V�u�\S�����M���M��K_�˴��<<"jN�Ё�|��sO�&��(��:�s�1����E�,�Cs�6$�F����sG#\ �_N�]b���u�S�%��`���$`�.}��E���p�!t�3�cA���e��s9<�@��r.���.�&�0�|�}|��q�o3٬ۀp��p�����/�=S}��8	��h�ᎣS���M�S�-�c�
�%Շp�-���/{���>���2�L/;�%B��]��<��y/E��`��@�=
z����h�ة�@'�@�e�?Zb�T��D�|-�T��+f�R-
6�"\���gQ�D��"�6�f3os��Y��C�У�(q�G3�.t`�c����R}���Q�s�_&\/î�,�&5�p@��"L�썡9~�A!\���O:0�V]�8����mN� .����R���z�U��*Z>��;�.��2g12�w�m.��^�F�ྡྷ1��gɱ�S��@^VZ�m��[��ذ;���@�{Z��oy��9/]��9Շp��}-K�*I�ʸ���3�o��.��ڲls.=����瘇U�����󛍵�s�Q{��N�]�����<Se:�f��1S����.t.���<f*�S��@�sY�O�t���b#n��>�@����&��͡9~�b�揊.t b����ӛ�,Ki���.tr?�a������H�!\�@�^��6C�X��sM�!\��c�*`[9��~,3���ى0'q�9y�.
ʌ�(�23	��+|��o:���0��H�v��Սw�sbRҿ���SRR��Ru����nV��(�7�t��Q�m\�����Wl���%�H�؈���OI�"��)�P���0)�2A��`0��l&��BV��TU@e&���z=1����ȫ�x�ut;���?COOj}�ռ0�����E)RNqI���^���d�$33�l6�������᠜����D���D�Ly���;�S��%Hwl|;���1�4�$.�X�0~b��;Z��9�ȃ&�q��-���A.��|>SI��J�A*-
RqI1��~r�(��طo�sD���L����lƹ�1{��V��)=Ƞ��N��1ƿCǴ����-����T�\�ʠ;�{^�N�ƅ�"�w�(
A7E��9`>��F�Ojiz�(�J������t���d����jjj������������*��誫��q���h�)�$���o��Cr��W���l6��d�2��%1m5�L_p�G�z���\8��z�͖-<dNg�:ri�S��h�7��.R�I��e~ka8������I�����Kuί���f�ZIXqj�`���T��Qi�py`�|��H٘c �C=�ii¼0?��i��kk?BB���QEE%���QKK���S��r%��:;;����n����iY+I˔��AV�����q����A ��A6���6;�,++�222�������<��ժ�F8:�z��C�fBz���n4�e<9��1�i��eޑm��Z��8;/b�PL�`����V��R��#�4��D��C~~>�p�(@��)���@�k�_�/�h��#�_���8�@����ya~RKS&-�a���n'��M�`�jjk��4S�p8D�H�����]]��;��q����5��L�����&!��;��L&#������r����@x@e\�m�{����7s�Ę�?�-���k�wq�Z��
K����|	-�G�������U�L�$.ܨ�����r��v:��7*�р�E�	�����g�������QEE�'�W0��9e��<���z�m��q��s��6���q���|?���I-M��CV����Wjoo�P(D]�]���C����D�=�ܳ��ѣ -��o�����˞ �V&_�sc{�XK~�����uuU5���
 4 _�D���
mD[����<��ǏؘK�w��0�@ZЧ�����������%�F4s-�[�� +0���H���T,�j,�������",\x`�^ŢE��_�R�s'���8��shnn����������(�'�4�'ML���ʌ!��HKGGE"Q��f���K����O}���eժUC�e��>V��t����5,�Pt[U�HΙx������k���Lmmm��#
�܌C�utv�
�g�9��ϣ�$f{c^�W��
�o�0_����cŜN�ҭ���5��ʮ]�w�H������Zx�����Vm]-�,�O�hau�m
�2��IUϾ����N�B!����y�0ʴ44��im�@xT�#騱FC��<0�1|��ɋ����!�����AZ�ea��G -ԫ��իWo�H˽��N𢳋����j6*|���oޓ��Y�c?��������:�c}�繻���V�c%����]�BB��[���]k��h���׋�[�I���ww�{>�[�$�@�3�|��\���ޙ�|6�@�@�(�hMH�K�OS)M�AL4^�KA�T�grp�(q����:�ˆ�J`j�=�"�1I�Ya���>\ߑL0�����"�N�=�*�p�d��������N�9%��@�G�>qU�$�fڢ��04!0��Jbv�7R9�[��n9����X��;���{�}D4�܃K-��/蘙���**.J�d�,
�%N��E��ǂ�/���J4��;�W����;�v��]�S-�X���՚�(.)H�'���OL�>=1cƌDyyy��b�HE(�����˵��N�2�g�$@��Ə����R� )��4�U�}��S�xpA�2V�-t�t��:x��'�3�:^Xf2t¨I�,�,��|[:Ѹ�91�Զ�#�6��ɴ"�K�~�ݖFΗ�yfd6�@���T�ҷo���D�2�i]��Ɉ:>�s��G�}�~M�E���2�;�ߑ�/�WeR�J��K&R��蛘d��ݡ���??	,�IyE9[@���+��?{��]�UW]�_@��s*�ý@�bp��˓	]. �>@O��u�k�\F4Bpq�B��5�W�Ń��‡�z ��%ev1r$�g����m0D!����GR^Wj��>�3�H13I�>|���|+ռȻ��Nx��q�wŒ��?�5Դ|Q޷�ا�v�F��>2��� h�ڽ{���8��Z,@
�+W\qŁ��֭[��i��X���W�Z���k�}���.�T��Q>�g�^�엖�8������\�j�t��ھxp1�%΂I����%�[������aƧ�/-��ŃK�FmD�I��x0B����j.��;R�&�+��;�7����?��K�.~���%��u@�@��D�Gu�Di��:؋�%++KM��Sԭ�&��K.���ŋ7TVV>h@�������9��%��÷�r�K\pA%�@x�N���	h�9sf.y���ǥ�����uH
!�'�k��%?Q�q�p����lbn����q���u�"���9>pqmOH-�&��K��"��.�8�Xy����F��th�����4��i���<_EIgP��C�-�C�۹��*�.����o�ػw��H�)R(�>�VK�}X��H������?��U�S>#0/!�Xn��$�%��ˋ���R�{|�t�*[��t�%N�h_B����׽�!cd��/��w�(|�A�mA}�D ��6�[�N��ئ��xJ���qωj\��Km�h'��I�g1�@8%��!��բ1�X��=)"g�"вA�����-�"����j�)9ZƝvZ4L��}RYiY�a{뭷���&��"�E����w0TV���A
rAJ\�ƥ	9��{*.Rv�c�W�Nx��'9���
�~��ܥ������s�]u�"5��`�>b�&���)@D���k '�G���ι^�R�"���g@c ��YRRLȺ�!�z�ڛ���-��h9+�f���+�	�e� �.a�hFw4q����Yz=^&K��!��T~'7�|�B�e"�^M[ ry_����s�w^T*����tޤ��.K�\^���"Y�9�D����>O�`����|��0�|0<�|$�s{�8��^�i��������wm����-��\X�(�%��&0F�"�$�{��*�U���
�=j*Bs�٧o�~,��
�/�/�wf��^�M�Y��hIFʠ]XX��Ѽy�o�u)&�!h��l�T=e��"��L??p>��/~��i�E���3�?����Q����I7�
`���q�-}⤼�.�rD�.���R���qA�26�����u���R"�����-�zr�����f�_���W��ԟ7��p1q�j\49��y��c!��9�Ӿz-�7���`o*�UY�hѭd�*�o��i.
ј�2^�,��H3��A�z��.I!'(��Ƴ��gQ�4?J��nk��a�̲$�Ah"*+%�
ЂI��]�)_�狉���a�\�)�3p����")*^�����{���)�w"[Ieb�\3�$9��5.�T�,?nD3@F�p��b	�.(�\��QI��A��/��p�V��mּY���%�s�Xո$�	ۀ�ukL>�����@Ѥp�t�<q���qcѸ��T���͛/�@���snL�/����J�#d@P��I.�"����@��N`AFH2a U=��><��j[:�̳�:��@2�Ɲ�?���;�|V�����|IdwM�������;H"�X��ueJRB`�AI��=x_&m�Կe�q<o��s��Ҡ������"#ۤ�`G���83:V�2)�a�0�Ko�����1%���i�D�0� &*�q�)C����<�~�>ǁK>�2�k\j�!�LvN��#ʼn47�0°�b�/VXH{'a_�JD�
��Y3m(��)S��,�<ǘ�&�_���]�j]��,�Cے�h[в��j�ƃRn�:��J�b���	�m&Y��Q�ϐ�N¢!î�����Vp)"\c�Eu~^�e�^���w��M���2���3'[݆׋�5?x��ly��%��Q`F��:��9��Q��ι��Hո�S8ӆ�*�F�-b�ƈu��6�jc����i\|�С�g��l����a��"̫�]���;���D�P~,�(N��[�1�3�48��hv���\$�i��)k����[4�<�I:G�����k^ݶ�]�vI�~��D*�r4 H�$@$ξea2;�ZŸez��![d:E��p6�K��K�j\|���Y5��438"fp4�l-����~QU8�����K�G�5��d��˪��%��_��Dj=�>.,��X�Iς��iz�>_��>�b��j.��l���␾�4�מr����E�j�̻��-��9l
p¥
��I���{�u�/���S��ה�4';��v���Ѹ;��G}�1��J�Ҧ�e]�Xh,R�ZJ]^�T2#�\�pɝ8���˨��1s�J�{#�=�R<���_��K$���md�j�D��N-�%�_&�/���}z��_Z�j�]�G�3�T�>.%ő�g$��`b�d��L�D��[s�{�	�sb���|a!�
�d5uR�"�pg�-4&@G<
��]q�]��;��pNS�KU�<��jy�/Ҥ�
���
V�&�?}��2�`��_��6ϓ>撎�:�-�\}�9ى�cƊƋv*Ԭ��H[$�l˒��QN�5��(V�������G��Ro���_�Q:�i)M+���;��Œ�(/y����|$~�}�D�w33�H��]ID�a����*QMs](��1��B��⇚(nPV�o���v�1�ѶJ�6�q:��Ў��.]�B��{E��N�4ᜦ	�.��K��!��nu�'�,�i�:�y\|���c�ybZI+z_)1�/��f�g��E�N.41�~�\�ME�i�0][��>7z�F-_$�Jr�`�b|���;6X�4�>ոm�#綾^�k˖-Oʻ�H�W��U�R/�B=b&�w�+n﫭S�\?��pp����;�6l��X-��4�TǏ�9s.4���h_��
��ѐ���Xp�l�:J,
'��'*\����h��m�V?�<�Ǡ��}(q������ȚZ/��}��{�ƪ�*ޏ�D:eJKf�*�fK}8�\�FQ�{����I��ZE�xp��`�չ�ѯ�t�L��gV�x�/�ٳGD�V;�+��"R���y<�:�7�2v����]G�@�0-�Z�o�mգ�>�vժU+����M�\s��Z9(�T!�Ϛ5k���^�i�ʕ+���uF�[��dR=���!��*��P��<j�A�K<�N�V�D��d�)�N�/\ƌ�,���p�x,���Z��ڴHQ�o�ۭ۶m{�,�v�.����B��H}�ߦ]b���O	�����j5\"-_h٨>-���-ԥ]��:�z�����q�� ��d��pVpAt�J����˖�8A�/_�
\��E#�*�c��9���l5b�DM.|K��>�:�{���G|: R����!m�D�-v?f�;E�R�LR�R��2ƃ�����m���@��ThI��
h)�wXא�ª�|��Q9C��H��/Dn�q�?)��L,^x��cF�v���G��p�"���p�/'4Kme�75l��2fL.�)1u&�j�ZĪ��lf]3kǚ5k�9`Y$2C�ө�қ��(H���6��E�	�p�6����B����E4.Z�.��\�n]Z�HjZ���P���Y[hY�~�}j���,X�`�h��v��? �Ԑ�*��|-_�5��(��5��ś���%�T�b�'�A4:(�E�=t��:Y��/�#9'T5�X�����@�q������hs�:zj;h��c������e��k\��o
��Э���$zh��1Y�!���O�ZM�ƪ'�xbU�|���;�&~.���"�J��M��+��\wv������^��C5��"�|͝��s���������q7�gB�1	�`x��w?[YY� ��T�5M��}\������b��m�(�X�_�5��[Ě��T4&��h�����$�����e�0����� ����Z�
΅ԡB�Ez��}پ}{L�s+I�O:~~�S�N!ܜu�H�G�
_S'`�&����/�O0w
o*��1�k�)s�+̠wZ�ʿ���/�ݻ�F
�^����1"Cܘݷp�`"`����[�}�N�E�_����m۶{�+>-Zp�Z�P��6|��v�|��W:��u��S�&QWo�
N��7�й�eU���$z�꽽C�_�Uo�ӧ���/^���X �3�и�})��N��������w�}����"�%�c�����㲵~-VSf��(�X@	ϩ���ɦ"��;�\��\N���(�Z��C���*����<�����u��S���?�!�,B������d
�"��U��t�����/^�R\5�A/Õ���ϟ?��H�P������B�‡
{���m1�)X�me�om�Z-��"D�3W��|W�C��y��倖�WS����ʥ˖�p�r�ju_$��eee�&N�e] �8NB�s��$���,Ү}��'�s��o�����o~�?���_�s���4�bMC�[��\����F
��#�N����E C(Q1����{B9ڹx�ݺ�e��Ks)-��Y��2sE�iZ�f24!�˗/Wh�|b��7���kp��<7U�U�s'�ʚP��Z���d�VV�_��}�5/������|��b}Bd��<e*�_<��,�H1���]��C"�����F
���	�G�q).���)����5b`I��~��!���%�¡[4o�B�ekwմ(��� u8��Ӣ�-�A��-ˁ���w����%�\�-7']��)��!�MH:<ѿ�D�>}�;wN�n��.��/��T�ռE�%-[�d�@ؗ��+�Z6!�#�F���.6��}�")$�6R��)�ME
�IuM$׎=�7Ԫ�g�ƥ�i\2#�?���-���A!�`�p�#4��p(�Ò�h�W�É�f���/��'�xX�(�/�+�.cG����K���xA�Y�f3ES�~�\��f͚_!���}���N�:'$�Z�K�s�N	�mE`f�F��Z�j��q��xp1�|�P��~�����#�Bg�WEz�K����c�K
.��xj�M�{�Md
�-�G�07��CC�
ŏ"p-,(�yZ���e�!���z��.��ZXZd��t���z2d������d����ES2T`��k�������/�*�z�"+o⬳�
��=z�t��{y^@���iN�@��/���q���"��Bo������B�UD�G ��H�kc�W��:�6}p��h6�i����_�ܮܷo����ɶ��|�ލ(��^�z���v��
}a�I��d�ɗdEy�\�����Fh�
�/IMI��@L�E��:^�,�{8�9��Z�|W3!�
>o0�����nݻ�5R�K��2��N��%�8��	�Y��y)�iӦ�L�Ф���%5D"�$�����b�et��F�`f8�s-+�"�K�.]I$����+9�v�Z��.��
�E}'{&m�_�j��A+FR:B�srrB�@S���A["�^��N�;�z��\�=�;DhĈ</"���/�`�����|����4�t��x��(��������jg�H��6xmzpI
%h�҂��#�>�%��i�a5.YM\z��Y�,}���]�v��� ��t��7Q��D�P?�ׯ_�hiX?/_�$j�u�]���K�m���Kt�@�p�/@��{Ф�1P�k;��
ˠA���{y D�;�9���S�M7ݴ�$d7���_��%+�z�ȑ��Y1s��B2R�����G���FJj�])�m�Q�-��ւ��1�D�r�)׿̴ʺ7�o˜9sJ��xa��-����?��N�V'��ζTd	>/2�
v�x��n���d�`�	�d��m��?(+�shiаp��<kR"oR�<�l$�
-#�/�e��5zpiP�9s�;S��|��ϙH�_�d×ŋ��4)�`�x(РR����=V�m��ٷ��n��Bw�>�c���ɟ|��ה뿢��-|#�=w�ȹ���OV���}fvN�&�M4S<�aÆg��C����^�u��'f͚�1�#���;8X3))&�d��d�Ĩ���G�L�k��<�d�yol۶�Q��D���E�=�4�<`4,ן�H!_���SXnn��bŊeF^%��	����/!��53�'-�O�/�ߛ���nɃK���Y1sZ��|$?�1	 &�r��{��sLp�k� ��iRB���p!�KD������\|���#�d�s�#����'pp�B�v�&^�W�~���$��
�|]���*f�7n|Z���C�)�v_�#�D�X��O��;w�S&O����O F��4*�9���@W.X��
q�^jl^.�)>����R��EF�F�/ЃK��/��ſ��D���ӗ_~��~P��e����?��&Y<S�m\�k��MR'�g��?���O����Wfϙ���c�A񉩔�Z�(UTT��~��<x��/�Z�X`i��_|�/p_��E��O��Zj�t�J�?u�.�#riSu�>
�|�d�]$��su�l@�.���p�L7�,r��B�O��itm�!��"�]��_Dnu@�ș���d�5x>��g��h��۹a8��W�,�U�
˲,V0�*���|�
~��X��"\����E�e.--�u.y���L�����~;�p�.ff¥�/-S��4�bf&\F=�2���>&\„���p���a\.����D�	33{[�.�.�.�p.�p.�.�@��@�@���Gݶ\8�1�IEND�B`�PK
!<N?���chrome/webide/skin/newapp.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

dialog {
  -moz-appearance: none;
  background-image: linear-gradient(rgb(255, 255, 255), rgb(237, 237, 237) 100px);
  font-family: "Clear Sans", sans-serif;
  color: #424E5A;
  overflow-y: scroll;
}

.header-name {
  font-size: 1.5rem;
  font-weight: normal;
  margin: 15px 0;
}

richlistbox {
  -moz-appearance: none;
  overflow-y: auto;
  border: 1px solid #424E5A;
}

richlistitem {
  padding: 6px 0;
}

richlistitem:not([selected="true"]):hover {
  background-color: rgba(0,0,0,0.04);
}

richlistitem > vbox > label {
  margin: 0;
  font-size: 1.1em;
}

richlistbox > description {
  margin: 8px;
}

richlistitem {
  -moz-box-align: start;
}

richlistitem > image {
  height: 24px;
  width: 24px;
  margin: 0 6px;
}

textbox {
  font-size: 1.2rem;
}
PK
!<qv����chrome/webide/skin/noise.png�PNG


IHDRdd�̈gPLTE������������������������`���IDATx
;c�:4��+H�U+�$����\A����߿w9�YiǮ�4�M��e��Q�l��/�m�{�@Q��	�|�"3�e��3��e�r*�eX��Y�)0��'7��kx9I�C&��	� 8�KJ
~�0����mHۊ�.wH�I�Fv����Q����=*���>�c9u��V��9Mn�ڨ����J̀�	�*!]�D
�s�LDur�LN�^��b�
�6��E��@�P�4�nWG�h$�9�0��j$
�ik�9q�1����.ч;�5��k�_����/��m6�����]�ct��������Ѣ*�]�|�ޢ�����frr��ޠl�F���5��!	v��[��u�
�
p�t�:�1q�=���y6C��<ʢ���8��Yx�C@O-���h�CƲP9ag�jU�����aJ���IkC�����Bz�RIC���B��m_��koY,�n��F��WQQ�zM�
�c�aI�G�U�F��i�SJ�b�R�9�7�$��*a���'��̛xм|A<��G��%g7D5�����ӶR�٭q�紥P&��U���h���"�?�Da�^�i��AC*�s���x�ޑ��7Q�ǂ���l��+�vȽ��\�����Ԇ�O֠_-��%��N~51��=(j�W�SR��bO?��+���l��/
�ޢ�3f,S�v��5V��~��o�]���]���l�c��	~�㾈����԰ܖ.� |7���)���u�Ź����g�.��������J�������
���A%��@17�N��.���!XF�;ox������t,�MCs�xP�Y�r�]&)+c��+�bg�����b`�7Iu��>=��+;���B�8‰M��
Vb����D
KA�vO��B����bX9Ԁ�Jh@d,�n:�c�����K�}F�ӛؚ�ͿGŔ�����]RC[��t�`P�sT��W��SvL*�s�׋��7z�#��U�AU�>�7�yz��y��;�/�l A��L�_�Wy��w� ���I���:}x7<K���7�������x�
D��=����}dXA3�WWᝰ�B�H,A��	(\c�6?�U�ʁj���V�]t^��eu���k�4��fL��2�K?��v�т�6kW!TYBM��cC�ͣ�Q�Ϝ�PI�?�&29"��-��=��Α�:�q�w�zo��H��ݰ=й��1\cK��@��s�Yz�&o�I�ecs
�i�;O[ �vD�9�^��i͵b�]3�|d���v�z�.�ñ�I#s��`Yhz�$jg_Fڕf�0'��
xI����֠8ι���ɟ�fr08~�dz3�L����8��^0�?!/�4K��9�'�EBh�%(�J#E���|��䞤!�t�_6�Nw������=�k�U��(��SZ�*���:P$���^
H\�*u=�N�[�Y��L�f�Hy�77�B�f��4��k�~��*z9��
�l�TQ��G��;��qlB4h�}��c���������q��>���:Ɛ�.?�S;�.���u���ʜt�|�x�vD�7A��MF7��	5��ѻTM�M������,	*��I�x�ȩJ�jL��.x��񂡷�9�+�`9�l�2�S3�!��E���vzا(t׀�ʩcQ�{Nz:�;h�f�=V���vɥ�������@Ճ
~��emf����x,A*[<���i�[~
O����1}�-�%�[��`��rB��L�wFr����Pl藕�9V�:Ļs�UU\�0 �J��d��@'�!�Ă�y	1ٝN�oM�W��+KE�zž��ڼ)�u��|�{������C�#�L�=oP\M����
0)�J���E�0�2?�a���ɎJ�If����t��B&�4믙Wi�M�
�����?���{v�e�Z�=�G�S)$��܇C�l�6��	Nc�K�bb�{P�{�
G�Q��j��c'�w��j�=n��>a�O0�Ӓ� �a�)�����(�_�&�Ę@����ǰ:��r'E�
�R�ҧ�%�ky�7�5�
�)���!K���mF�m���v<	5�����C8�eX�m?�+!��xu����Xx})${�Ȼ����MB��o��.�c�U���~����H��DX��V�FJ�a�B>/��~�o��W�Ӭ�q'	�]b�b� ��e��~��lK}��cj�����a�HȆ'֐���/����EBcB�K_1����cq���΁DOg��*��/�G�`����!�.�'�^�śy����Nr����$V�Ĝ�E[���}_�g��bh~1�j''4����Ez�M^s��g �7T���-e�C�|'ӵA8�����g4C8#�2���x����ݼ��*/��vyq�֋���)���&4���k܃]�s2B��
�t���O�&7Zڿ!�`$ї�<�8��^�ȪL�p��jk{�'����Y�5EQ���*E�1�r���s~���~�F�1L�����4�-�1j��Cpg��fd�J������I;�E���x�>��&���Pt��J^=N��ň��6��T>���i]t�\�r������<,3��nɢk�`�qq�U�^�eTW[�Cqm����R`%����O
��)�N�O��"-���*��R�_��>�if>��
�ᙆ�S�uWs?�V �A[�w���HQnߓ7�ݺ��~�z�6�8�WL�up�T��mq��X�-��M��X�%ƌ��/v�'�Q>�R�@���WM�*�NyÉ>o���z$_1��&D�Z��	Eam~u$d(��d����zQ�
\�~�U9ͤ�&��"��!Yp��<�6d�d�?���\�@ɥX�1i��i���Kܭ�ɸ?rs��GSk�﨨7j���؆��!��6B���#��%��oh?��ss���
Q�,�:pBśi(C�y+27�Nn����X�	�&ѽ�7�,R�pt\Y5���mݮәh�;���U��9��E��G�u�>6N�6`%CuUBp�C��0�6T��R�j�g�.�O��V���D=*��ɢ�{�q���uWo�j٘�!���q���HP�ܭ{��%�Bs3�`L�Þ����v �����׍v�x���.�Y!�
����X��\��"˩�;��$��	�� ؐ��)s����bB$�LNCc?70��2��W��tߝ]^��������V�/���[
�)�\���g:�B��mM̠�C�{/VС�������ɷ����Cm�*�َu/ռ�3�g捏�W����ʷ��{N�����E;��
�����|�w����"�̾`����H��7��P�I#�P���h���я?��P�b��Ȃ7C� aK*p��}"	QBKPDU��z<6�^i�S� �������]���[�b��
�� "��P=��u*G����j���P�`{��؇^�X+e��m�z��kI�Q��X��
��_��?�!��,���'X���"B1�4Z�hdBw�
�p֝�J�ْM~����k8p1��lb\��*>Q&�� ��}w7j�X�$=�֏j������ �1���G�q�I�RW|���y�~��U?�T��Յ��kS�h�m�;��IEND�B`�PK
!<',!

$chrome/webide/skin/panel-listing.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

html {
  font: message-box;
  font-size: 11px;
  font-weight: 400;
}

label,
.panel-item,
#project-panel-projects,
#runtime-panel-projects {
  display: block;
  float: left;
  width: 100%;
  text-align: left;
}

.project-image,
.panel-item span {
  display: inline-block;
  float: left;
  line-height: 20px;
}

.project-image {
  margin-right: 10px;
  max-height: 20px;
}

.panel-header {
  color: #ACACAC;
  text-transform: uppercase;
  line-height: 200%;
  margin: 5px 5px 0 5px;
  font-weight: 700;
  width: 100%;
}

.panel-header:first-child {
  margin-top: 0;
}

.panel-header[hidden], .panel-item[hidden] {
  display: none;
}

.panel-item-complex {
  clear: both;
  position: relative;
}

.panel-item span {
  display: block;
  float: left;
  overflow: hidden;
  text-overflow: ellipsis;
  width: 75%;
  white-space: nowrap;
}

.panel-item {
  -moz-appearance: none;
  -moz-box-align: center;
  padding: 3%;
  display: block;
  width: 94%;
  cursor: pointer;
  border-top: 1px solid transparent;
  border-left: 0;
  border-bottom: 1px solid #CCC;
  border-right: 0;
  background-color: transparent;
}

button.panel-item {
  background-position: 5px 5px;
  background-repeat: no-repeat;
  background-size: 14px 14px;
  padding-left: 25px;
  width: 100%;
}

.panel-item:disabled {
  background-color: #FFF;
  color: #5A5A5A;
  opacity: 0.5;
  cursor: default;
}

.refresh-icon {
  background-image: url("chrome://devtools/skin/images/reload.svg");
  height: 14px;
  width: 14px;
  border: 0;
  opacity: 0.6;
  display: inline-block;
  margin: 3px;
  float: right;
}

.panel-item:not(:disabled):hover,
button.panel-item:not(:disabled):hover {
  background-color: #CCF0FD;
  border-top: 1px solid #EDEDED;
}

.configure-button {
  display: inline-block;
  height: 30px;
  width: 30px;
  background-color: transparent;
  background-image: -moz-image-rect(url("icons.png"), 104, 462, 129, 438);
  background-position: center center;
  background-repeat: no-repeat;
  background-size: 14px 14px;
  position: absolute;
  top: -2px;
  right: 0;
  border: 0;
}

.configure-button:hover {
  cursor: pointer;
}

.project-panel-item-openpackaged  { background-image: -moz-image-rect(url("icons.png"), 260, 438, 286, 412); }
.runtime-panel-item-other         { background-image: -moz-image-rect(url("icons.png"), 26, 438, 52, 412); }
#runtime-screenshot               { background-image: -moz-image-rect(url("icons.png"), 131, 438, 156, 412); }

#runtime-preferences,
#runtime-settings                 { background-image: -moz-image-rect(url("icons.png"), 105, 464, 131, 438); }

#runtime-panel-nousbdevice,
#runtime-details                  { background-image: -moz-image-rect(url("icons.png"), 156, 438, 182, 412);  }

.runtime-panel-item-usb,
#runtime-disconnect               { background-image: -moz-image-rect(url("icons.png"), 52, 438, 78, 412); }

.runtime-panel-item-wifi,
.project-panel-item-openhosted    { background-image: -moz-image-rect(url("icons.png"), 208, 438, 234, 412); }

.project-panel-item-newapp,
#runtime-panel-noadbhelper        { background-image: -moz-image-rect(url("icons.png"), 234, 438, 260, 412); }
PK
!<rEoI==chrome/webide/skin/rocket.svg<?xml version="1.0" encoding="utf-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 xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" viewBox="0 0 24 24">
  <g opacity="0.1">
    <path fill="#fff" d="M12,2.3c-1.127,0-3.333,3.721-4.084,7.411l-2.535,2.535v6.619l1.767,0l2.464-2.464 c0.252,0.264,0.529,0.486,0.827,0.662h3.118c0.299-0.175,0.579-0.397,0.831-0.662l2.464,2.464l1.767,0v-6.619l-2.535-2.535 C15.333,6.021,13.127,2.3,12,2.3z M12.003,6.181c0.393,0,1.084,1.103,1.515,2.423c-0.466-0.087-0.963-0.135-1.481-0.135 c-0.545,0-1.066,0.054-1.553,0.15C10.914,7.292,11.608,6.181,12.003,6.181z"/>
    <path fill="#fff" d="M12.792,18.755c0,0.778-0.603,1.408-0.805,1.408c-0.201,0-0.805-0.631-0.805-1.408 c0-0.301,0.055-0.579,0.147-0.809h-0.932c-0.109,0.403-0.171,0.854-0.171,1.33c0,1.714,1.33,3.104,1.774,3.104 s1.774-1.389,1.774-3.103c0-0.477-0.062-0.927-0.171-1.331l-0.957,0C12.738,18.175,12.792,18.453,12.792,18.755z"/>
    <path fill="#414042" d="M12,2c-1.127,0-3.333,3.721-4.084,7.411l-2.535,2.535v6.619l1.767,0l2.464-2.464 c0.252,0.264,0.529,0.486,0.827,0.662h3.118c0.299-0.175,0.579-0.397,0.831-0.662l2.464,2.464l1.767,0v-6.619l-2.535-2.535 C15.333,5.721,13.127,2,12,2z M12.003,5.881c0.393,0,1.084,1.103,1.515,2.423c-0.466-0.087-0.963-0.135-1.481-0.135 c-0.545,0-1.066,0.054-1.553,0.15C10.914,6.992,11.608,5.881,12.003,5.881z"/>
    <path fill="#414042" d="M12.792,18.455c0,0.778-0.603,1.408-0.805,1.408c-0.201,0-0.805-0.631-0.805-1.408 c0-0.301,0.055-0.579,0.147-0.809h-0.932c-0.109,0.403-0.171,0.854-0.171,1.33c0,1.714,1.33,3.104,1.774,3.104 s1.774-1.389,1.774-3.103c0-0.477-0.062-0.927-0.171-1.331l-0.957,0C12.738,17.875,12.792,18.153,12.792,18.455z"/>
  </g>
</svg>
PK
!<�����%chrome/webide/skin/runtimedetails.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

html, body {
  background: white;
}

#devicePrivileges {
  font-family: monospace;
  padding-left: 6px;
}

#devtools-check > a {
  color: #4C9ED9;
  cursor: pointer;
}

.action {
  display: inline;
}

.action[hidden] {
  display: none;
}
PK
!<ދ6�wwchrome/webide/skin/throbber.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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"
      width="24" height="24" viewBox="0 0 64 64">
  <g>
    <rect x="30" y="4" width="4" height="15" transform="rotate(0, 32, 32)" fill="#BBB"/>
    <rect x="30" y="4" width="4" height="15" transform="rotate(30, 32, 32)" fill="#AAA"/>
    <rect x="30" y="4" width="4" height="15" transform="rotate(60, 32, 32)" fill="#999"/>
    <rect x="30" y="4" width="4" height="15" transform="rotate(90, 32, 32)" fill="#888"/>
    <rect x="30" y="4" width="4" height="15" transform="rotate(120, 32, 32)" fill="#777"/>
    <rect x="30" y="4" width="4" height="15" transform="rotate(150, 32, 32)" fill="#666"/>
    <rect x="30" y="4" width="4" height="15" transform="rotate(180, 32, 32)" fill="#555"/>
    <rect x="30" y="4" width="4" height="15" transform="rotate(210, 32, 32)" fill="#444"/>
    <rect x="30" y="4" width="4" height="15" transform="rotate(240, 32, 32)" fill="#333"/>
    <rect x="30" y="4" width="4" height="15" transform="rotate(270, 32, 32)" fill="#222"/>
    <rect x="30" y="4" width="4" height="15" transform="rotate(300, 32, 32)" fill="#111"/>
    <rect x="30" y="4" width="4" height="15" transform="rotate(330, 32, 32)" fill="#000"/>
    <animateTransform attributeName="transform" type="rotate" calcMode="discrete" values="0 32 32;30 32 32;60 32 32;90 32 32;120 32 32;150 32 32;180 32 32;210 32 32;240 32 32;270 32 32;300 32 32;330 32 32" dur="0.8s" repeatCount="indefinite"/>
  </g>
</svg>
PK
!<��VVchrome/webide/skin/webide.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/*
 *
 * Icons.png:
 *
 *  actions icons: 100x100. Starts at 0x0.
 *  menu icons: 26x26. Starts at 312x0.
 *  anchors icons: 27x16. Starts at 364x0.
 *
 */

#main-toolbar {
  padding: 0 12px;
}

#action-buttons-container {
  -moz-box-pack: center;
  height: 50px;
}

#panel-buttons-container {
  height: 50px;
  margin-top: -50px;
  pointer-events: none;
}

#panel-buttons-container > .panel-button {
  pointer-events: auto;
}

#action-busy-undetermined {
  height: 24px;
  width: 24px;
}

window.busy .action-button,
window:not(.busy) #action-busy,
window.busy-undetermined #action-busy-determined,
window.busy-determined #action-busy-undetermined {
  display: none;
}

/* Panel buttons - runtime */

#runtime-panel-button > .panel-button-image {
  list-style-image: url('icons.png');
  -moz-image-region: rect(78px,438px,104px,412px);
  width: 13px;
  height: 13px;
}

#runtime-panel-button[active="true"] > .panel-button-image {
  -moz-image-region: rect(78px,464px,104px,438px);
}

/* Action buttons */

.action-button {
  -moz-appearance: none;
  border-width: 0;
  margin: 0;
  padding: 0;
  list-style-image: url('icons.png');
}

.action-button[disabled="true"] {
  opacity: 0.4;
}

.action-button > .toolbarbutton-icon {
  width: 40px;
  height: 40px;
}

.action-button > .toolbarbutton-text {
  display: none;
}

#action-button-play  { -moz-image-region: rect(0,100px,100px,0) }
#action-button-stop  { -moz-image-region: rect(0,200px,100px,100px) }
#action-button-debug { -moz-image-region: rect(0,300px,100px,200px) }

#action-button-play:not([disabled="true"]):hover  { -moz-image-region: rect(200px,100px,300px,0) }
#action-button-stop:not([disabled="true"]):hover  { -moz-image-region: rect(200px,200px,300px,100px) }
#action-button-debug:not([disabled="true"]):not([active="true"]):hover { -moz-image-region: rect(200px,300px,300px,200px) }

#action-button-play.reload { -moz-image-region: rect(0,400px,100px,303px) }
#action-button-play.reload:hover { -moz-image-region: rect(200px,400px,300px,303px) }

#action-button-debug[active="true"] { -moz-image-region: rect(100px,300px,200px,200px) }

/* Panels */

.panel-list {
  display: none;
  position: relative;
  max-width: 190px;
  overflow: hidden;
}

#project-listing-panel {
  max-width: 165px;
}

.panel-list-wrapper {
  height: 100%;
  width: 100%;
  min-width: 100px;
  position: absolute;
  top: 0;
  bottom: 0;
  right: 0;
  left: 0;
}

.panel-list-wrapper > iframe {
  height: inherit;
  width: 100%;
  position: absolute;
  top: 0;
  bottom: 0;
  right: 0;
  left: 0;
}

[sidebar-displayed] {
  display: block;
}

/* Main view */

#deck {
  background-color: rgb(225, 225, 225);
  background-image: url('rocket.svg'), url('noise.png');
  background-repeat: no-repeat, repeat;
  background-size: 35%, auto;
  background-position: center center, top left;
  border-top: 1px solid #AAA;
}

.devtools-horizontal-splitter {
  position: relative;
  border-bottom: 1px solid #aaa;
}
PK
!<�딟{{ chrome/webide/skin/wifi-auth.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

html, body {
  background: white;
}

body {
  display: flex;
  flex-direction: column;
  height: 90%;
}

div {
  margin-bottom: 1em;
}

#qr-code {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
}

#qr-code-wrapper {
  flex: 1;
  width: 100%;
  margin: 2em 0;
  text-align: center;
}

#qr-code img {
  height: 100%;
}

.toggle-scanner {
  color: #4C9ED9;
  font-size: small;
  cursor: pointer;
  border-bottom: 1px dotted;
}

#token {
  display: none;
}

body[token] > #token {
  display: flex;
  flex-direction: column;
}

body[token] > #qr-code {
  display: none;
}

#token pre,
#token a {
  align-self: center;
}

#qr-size-note {
  text-align: center
}
PK
!<O2���<chrome/devtools-shim/content/aboutdevtools/aboutdevtools.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 {
  /* Photon color variables used on the aboutdevtools page */
  --blue-50: #0a84ff;
  --blue-50-alpha20: rgba(10, 132, 255, 0.2);
  --blue-60: #0060df;
  --blue-70: #003eaa;
  --blue-80: #002275;
  --grey-30: #d7d7db;
  --grey-90: #0c0c0d;
  --grey-90-alpha-10: rgba(12, 12, 13, 0.1);
  --grey-90-alpha-20: rgba(12, 12, 13, 0.2);
  --grey-90-alpha-30: rgba(12, 12, 13, 0.3);
  --grey-90-alpha-40: rgba(12, 12, 13, 0.4);
  --grey-90-alpha-50: rgba(12, 12, 13, 0.5);
  --red-50: #ff0039;
  --white: #ffffff;

  /* Shared variables */
  --line-height: 1.5em;
}

html, body {
  min-width: 600px;
}

body {
  margin-top: 17vh;
}

p {
  line-height: var(--line-height);
}

.box {
  max-width: 850px;
  display: flex;
  flex-shrink: 0;
  padding: 0 0 50px 0;
  /* Compensate for the optional scrollbar. */
  margin-right: calc(100% - 100vw);
}

.wrapper {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.left-pane {
  width: 300px;
  height: 300px;
  margin-inline-end: 20px;
  background-image: url(images/otter.svg);
  background-size: 100%;
  background-position: 50%;
  background-repeat: no-repeat;
  flex-shrink: 0;
}

.features {
  max-width: 980px;
  border-top: 1px solid var(--grey-30);
}

.features-list {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-gap: 40px 20px;
  margin: 60px 20px;
  padding: 0;
}

.feature-icon {
  width: 55%;
}

.feature {
  list-style: none;
  text-align: center;
  margin: 10px 0;
}

.feature-name {
  font-size: 28px;
  font-weight: 300;
  margin: 10px 0;
}

.feature-desc {
  margin: 1em 20px;
}

.feature-link {
  display: block;
  margin-top: 10px;
}

.external,
.external:hover,
.external:visited,
.external:hover:active {
  color: var(--blue-60);
}

.external::after {
  content: "";

  display: inline-block;
  height: 16px;
  width: 16px;

  margin: -.3rem .15rem 0 0.25rem;
  vertical-align: middle;

  background-image: url(images/external-link.svg);
  background-repeat: no-repeat;
  background-size: 16px 16px;

  -moz-context-properties: fill;
  fill: var(--blue-60);
}

.title {
  font-weight: 300;
  font-size: 32px;
  margin-top: 16px;
  line-height: 44px;
}

.buttons-container {
  display: flex;
  margin-top: 5px;
}

.buttons-container button:not(:last-child) {
  margin-right: 10px;
}

button {
  margin: 20px 0 0 0;
  padding: 10px 20px;

  border: none;
  border-radius: 2px;

  font-size: 15px;
  font-weight: 400;
  line-height: 21px;
  cursor: pointer;

  box-shadow: 0 1px 0 rgba(0,0,0,0.23);
}

/* Remove light gray outline when clicking on the button */
button::-moz-focus-inner {
  border: 0;
}

.primary-button {
  background-color: var(--blue-60);
  color: var(--white);
}

.primary-button:enabled:hover {
  background-color: var(--blue-70)
}

.primary-button:active,
.primary-button:hover:active,
.primary-button:enabled:hover:active {
  background-color: var(--blue-80);
}

.default-button {
  background-color: var(--grey-90-alpha-10);
}

.default-button:enabled:hover {
  background-color: var(--grey-90-alpha-20)
}

.default-button:active,
.default-button:hover:active,
.default-button:enabled:hover:active {
  background-color: var(--grey-90-alpha-30);
}

[hidden="true"] {
  display: none;
}

footer {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 300px;
  flex-grow: 1;
  padding-bottom: 15px;
  color: var(--white);
  background: linear-gradient(0, var(--blue-60), var(--blue-80));
}

.dev-edition-logo {
  flex-shrink: 0;
  width: 165px;
  margin: 20px 50px 0 0;
}

.footer-message {
  max-width: 460px;
}

.footer-message-title {
  color: var(--white);
}

.footer-link {
  display: block;
  margin-top: 10px;
}

.footer-link::after {
  fill: var(--white);
}

.footer-link,
.footer-link:hover,
.footer-link:visited,
.footer-link:hover:active {
  color: var(--white);
}
PK
!<*�y�$�$;chrome/devtools-shim/content/aboutdevtools/aboutdevtools.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm", {});

const DEVTOOLS_ENABLED_PREF = "devtools.enabled";

const TELEMETRY_OPENED_KEY = "DEVTOOLS_ABOUT_DEVTOOLS_OPENED_KEY";
const TELEMETRY_OPENED_REASON = "DEVTOOLS_ABOUT_DEVTOOLS_OPENED_REASON";
const TELEMETRY_OPENED = "devtools.aboutdevtools.opened";
const TELEMETRY_INSTALLED = "devtools.aboutdevtools.installed";
const TELEMETRY_NOINSTALL_EXITS = "devtools.aboutdevtools.noinstall_exits";

const MESSAGES = {
  AboutDebugging: "about-debugging-message",
  ContextMenu: "inspect-element-message",
  HamburgerMenu: "menu-message",
  KeyShortcut: "key-shortcut-message",
  SystemMenu: "menu-message",
};

// Google analytics parameters that should be added to all outgoing links.
const GA_PARAMETERS = [
  ["utm_source", "devtools"],
  ["utm_medium", "onboarding"],
];

const ABOUTDEVTOOLS_STRINGS = "chrome://devtools-shim/locale/aboutdevtools.properties";
const aboutDevtoolsBundle = Services.strings.createBundle(ABOUTDEVTOOLS_STRINGS);

const KEY_SHORTCUTS_STRINGS = "chrome://devtools-shim/locale/key-shortcuts.properties";
const keyShortcutsBundle = Services.strings.createBundle(KEY_SHORTCUTS_STRINGS);

// URL constructor doesn't support about: scheme,
// we have to use http in order to have working searchParams.
let url = new URL(window.location.href.replace("about:", "http://"));
let reason = url.searchParams.get("reason");
let keyid = url.searchParams.get("keyid");
let tabid = parseInt(url.searchParams.get("tabid"), 10);

// Keep track of the initial devtools.enabled value to track exits in telemetry.
let isEnabledOnLoad;

function getToolboxShortcut() {
  const modifier = Services.appinfo.OS == "Darwin" ? "Cmd+Opt+" : "Ctrl+Shift+";
  return modifier + keyShortcutsBundle.GetStringFromName("toggleToolbox.commandkey");
}

function onInstallButtonClick() {
  Services.prefs.setBoolPref("devtools.enabled", true);

  try {
    Services.telemetry.scalarAdd(TELEMETRY_INSTALLED, 1);
  } catch (e) {
    dump("about:devtools oninstall telemetry failed: " + e + "\n");
  }
}

function onCloseButtonClick() {
  window.close();
}

function updatePage() {
  const installPage = document.getElementById("install-page");
  const welcomePage = document.getElementById("welcome-page");
  const isEnabled = Services.prefs.getBoolPref("devtools.enabled");
  if (isEnabled) {
    installPage.setAttribute("hidden", "true");
    welcomePage.removeAttribute("hidden");
  } else {
    welcomePage.setAttribute("hidden", "true");
    installPage.removeAttribute("hidden");
  }
}

/**
 * Array of descriptors for features displayed on about:devtools.
 * Each feature should contain:
 * - icon: the name of the image to use
 * - title: the key of the localized title (from aboutdevtools.properties)
 * - desc: the key of the localized description (from aboutdevtools.properties)
 * - link: the MDN documentation link
 */
const features = [
  {
    icon: "chrome://devtools-shim/content/aboutdevtools/images/feature-inspector.svg",
    title: "features.inspector.title",
    desc: "features.inspector.desc",
    link: "https://developer.mozilla.org/docs/Tools/Page_Inspector",
  }, {
    icon: "chrome://devtools-shim/content/aboutdevtools/images/feature-console.svg",
    title: "features.console.title",
    desc: "features.console.desc",
    link: "https://developer.mozilla.org/docs/Tools/Web_Console",
  }, {
    icon: "chrome://devtools-shim/content/aboutdevtools/images/feature-debugger.svg",
    title: "features.debugger.title",
    desc: "features.debugger.desc",
    link: "https://developer.mozilla.org/docs/Tools/Debugger",
  }, {
    icon: "chrome://devtools-shim/content/aboutdevtools/images/feature-network.svg",
    title: "features.network.title",
    desc: "features.network.desc",
    link: "https://developer.mozilla.org/docs/Tools/Network_Monitor",
  }, {
    icon: "chrome://devtools-shim/content/aboutdevtools/images/feature-storage.svg",
    title: "features.storage.title",
    desc: "features.storage.desc",
    link: "https://developer.mozilla.org/docs/Tools/Storage_Inspector",
  }, {
    icon: "chrome://devtools-shim/content/aboutdevtools/images/feature-responsive.svg",
    title: "features.responsive.title",
    desc: "features.responsive.desc",
    link: "https://developer.mozilla.org/docs/Tools/Responsive_Design_Mode",
  }, {
    icon: "chrome://devtools-shim/content/aboutdevtools/images/feature-visualediting.svg",
    title: "features.visualediting.title",
    desc: "features.visualediting.desc",
    link: "https://developer.mozilla.org/docs/Tools/Style_Editor",
  }, {
    icon: "chrome://devtools-shim/content/aboutdevtools/images/feature-performance.svg",
    title: "features.performance.title",
    desc: "features.performance.desc",
    link: "https://developer.mozilla.org/docs/Tools/Performance",
  }, {
    icon: "chrome://devtools-shim/content/aboutdevtools/images/feature-memory.svg",
    title: "features.memory.title",
    desc: "features.memory.desc",
    link: "https://developer.mozilla.org/docs/Tools/Memory",
  },
];

/**
 * Helper to create a DOM element to represent a DevTools feature.
 */
function createFeatureEl(feature) {
  let li = document.createElement("li");
  li.classList.add("feature");
  let learnMore = aboutDevtoolsBundle.GetStringFromName("features.learnMore");

  let {icon, link, title, desc} = feature;
  title = aboutDevtoolsBundle.GetStringFromName(title);
  desc = aboutDevtoolsBundle.GetStringFromName(desc);
  // eslint-disable-next-line no-unsanitized/property
  li.innerHTML =
    `<a class="feature-link" href="${link}" target="_blank">
       <img class="feature-icon" src="${icon}"/>
     </a>
     <h3 class="feature-name">${title}</h3>
     <p class="feature-desc">
       ${desc}
       <a class="external feature-link" href="${link}" target="_blank">${learnMore}</a>
     </p>`;

  return li;
}

window.addEventListener("load", function () {
  const inspectorShortcut = getToolboxShortcut();
  const welcomeMessage = document.getElementById("welcome-message");

  // Set the welcome message content with the correct keyboard sortcut for the current
  // platform.
  welcomeMessage.textContent = aboutDevtoolsBundle.formatStringFromName("welcome.message",
    [inspectorShortcut], 1);

  // Set the appropriate title message.
  if (reason == "ContextMenu") {
    document.getElementById("inspect-title").removeAttribute("hidden");
  } else {
    document.getElementById("common-title").removeAttribute("hidden");
  }

  // Display the message specific to the reason
  let id = MESSAGES[reason];
  if (id) {
    let message = document.getElementById(id);
    message.removeAttribute("hidden");
  }

  // Attach event listeners
  document.getElementById("install").addEventListener("click", onInstallButtonClick);
  document.getElementById("close").addEventListener("click", onCloseButtonClick);
  Services.prefs.addObserver(DEVTOOLS_ENABLED_PREF, updatePage);

  let featuresContainer = document.querySelector(".features-list");
  for (let feature of features) {
    featuresContainer.appendChild(createFeatureEl(feature));
  }

  // Add Google Analytics parameters to all the external links.
  let externalLinks = [...document.querySelectorAll("a.external")];
  for (let link of externalLinks) {
    let linkUrl = new URL(link.getAttribute("href"));
    GA_PARAMETERS.forEach(([key, value]) => linkUrl.searchParams.set(key, value));
    link.setAttribute("href", linkUrl.href);
  }

  // Update the current page based on the current value of DEVTOOLS_ENABLED_PREF.
  updatePage();

  try {
    if (reason) {
      Services.telemetry.getHistogramById(TELEMETRY_OPENED_REASON).add(reason);
    }

    if (keyid) {
      Services.telemetry.getHistogramById(TELEMETRY_OPENED_KEY).add(keyid);
    }

    Services.telemetry.scalarAdd(TELEMETRY_OPENED, 1);
  } catch (e) {
    dump("about:devtools onload telemetry failed: " + e + "\n");
  }
}, { once: true });

window.addEventListener("beforeunload", function () {
  // Focus the tab that triggered the DevTools onboarding.
  if (document.visibilityState != "visible") {
    // Only try to focus the correct tab if the current tab is the about:devtools page.
    return;
  }

  // Retrieve the original tab if it is still available.
  let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
  let { gBrowser } = browserWindow;
  let originalBrowser = gBrowser.getBrowserForOuterWindowID(tabid);
  let originalTab = gBrowser.getTabForBrowser(originalBrowser);

  if (originalTab) {
    // If the original tab was found, select it.
    gBrowser.selectedTab = originalTab;
  }
}, {once: true});

window.addEventListener("unload", function () {
  document.getElementById("install").removeEventListener("click", onInstallButtonClick);
  document.getElementById("close").removeEventListener("click", onCloseButtonClick);
  Services.prefs.removeObserver(DEVTOOLS_ENABLED_PREF, updatePage);

  const isEnabled = Services.prefs.getBoolPref("devtools.enabled");
  if (!isEnabledOnLoad && !isEnabled) {
    try {
      Services.telemetry.scalarAdd(TELEMETRY_NOINSTALL_EXITS, 1);
    } catch (e) {
      dump("about:devtools onunload telemetry failed: " + e + "\n");
    }
  }
}, {once: true});
PK
!<`R�>chrome/devtools-shim/content/aboutdevtools/aboutdevtools.xhtml<?xml version="1.0" encoding="utf-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/. -->
<!DOCTYPE html [
<!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd"> %htmlDTD;
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd"> %globalDTD;
<!ENTITY % aboutdevtoolsDTD SYSTEM "chrome://devtools-shim/locale/aboutdevtools.dtd"> %aboutdevtoolsDTD;
]>

<html xmlns="http://www.w3.org/1999/xhtml" dir="&locale.dir;">
<head>
  <title>&aboutDevtools.headTitle;</title>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>a
  <link rel="stylesheet" href="chrome://global/skin/in-content/common.css" type="text/css"/>
  <link rel="stylesheet" href="chrome://devtools-shim/content/aboutdevtools/aboutdevtools.css"  type="text/css"/>
  <link rel="stylesheet" href="chrome://devtools-shim/content/aboutdevtools/subscribe.css"  type="text/css"/>
  <script type="application/javascript" src="chrome://devtools-shim/content/aboutdevtools/aboutdevtools.js"></script>
  <script type="application/javascript" src="chrome://devtools-shim/content/aboutdevtools/subscribe.js"></script>
</head>
<body>
  <div id="install-page" class="wrapper" hidden="true">
    <div class="box">
      <div class="left-pane" />
      <div class="right-pane">
        <h1 class="title" id="common-title" hidden="true">&aboutDevtools.enable.title;</h1>
        <h1 class="title" id="inspect-title" hidden="true">&aboutDevtools.enable.inspectElementTitle;</h1>

        <!-- Include all the possible message, hidden by default
             as we can't lazily load localized strings from dtd -->
        <p id="about-debugging-message" hidden="true">&aboutDevtools.enable.aboutDebuggingMessage;</p>
        <p id="menu-message" hidden="true">&aboutDevtools.enable.menuMessage2;</p>
        <p id="key-shortcut-message" hidden="true">&aboutDevtools.enable.keyShortcutMessage;</p>
        <p id="inspect-element-message" hidden="true">&aboutDevtools.enable.inspectElementMessage;</p>

        <p>&aboutDevtools.enable.commonMessage;</p>
        <a class="external installpage-link" href="https://developer.mozilla.org/docs/Tools" target="_blank">&aboutDevtools.enable.learnMoreLink;</a>
        <div class="buttons-container">
          <button class="primary-button" id="install">&aboutDevtools.enable.enableButton;</button>
          <button class="default-button" id="close">&aboutDevtools.enable.closeButton2;</button>
        </div>
      </div>
    </div>
  </div>

  <!-- This page, hidden by default is displayed once the add-on is installed -->
  <div id="welcome-page" class="wrapper" hidden="true">
    <div class="box">
      <div class="left-pane" />
      <div class="right-pane">
        <h1 class="title" >&aboutDevtools.welcome.title;</h1>
        <!-- The welcome message is dynamically updated with a keyboard shortcut at
             runtime and added in aboutdevtools.js -->
        <p id="welcome-message"></p>

        <!-- Form dedicated to the newsletter subscription -->
        <div class="newsletter">
          <h2 class="newsletter-title">&aboutDevtools.newsletter.title;</h2>
          <p>&aboutDevtools.newsletter.message;</p>

          <form id="newsletter-form" name="newsletter-form" action="https://www.mozilla.org/en-US/newsletter/" method="post">
            <!-- "H" stands for the HTML format (->fmt). Alternative is T for text. -->
            <input type="hidden" id="fmt" name="fmt" value="H" />
            <!-- "app-dev" is the id of the Mozilla Developper newsletter -->
            <input type="hidden" id="newsletters" name="newsletters" value="app-dev" />
            <div id="newsletter-errors"></div>
            <section id="newsletter-email" class="newsletter-form-section">
              <input type="email" id="email" name="email" required="true" placeholder="&aboutDevtools.newsletter.email.placeholder;" />
            </section>

            <section id="newsletter-privacy" class="newsletter-form-section">
              <input type="checkbox" id="privacy" name="privacy" required="true" />
              <label for="privacy">&aboutDevtools.newsletter.privacy.label;</label>
            </section>
            <button type="submit" id="newsletter-submit" class="primary-button">&aboutDevtools.newsletter.subscribeButton;</button>
          </form>
          <div id="newsletter-thanks">
            <h2 class="newsletter-title">&aboutDevtools.newsletter.thanks.title;</h2>
            <p>&aboutDevtools.newsletter.thanks.message;</p>
          </div>
        </div>
      </div>
    </div>

    <div class="features">
      <ul class="features-list">
      </ul>
    </div>

    <footer>
      <img class="dev-edition-logo"
           src="chrome://devtools-shim/content/aboutdevtools/images/dev-edition-logo.svg"
           alt="Firefox Developer Edition logo"/>
      <div class="footer-message">
        <h1 class="footer-message-title">&aboutDevtools.footer.title;</h1>
        <p>&aboutDevtools.footer.message;</p>
        <a class="external footer-link"
           href="https://www.mozilla.org/firefox/developer/"
           target="_blank">&aboutDevtools.footer.learnMoreLink;</a>
      </div>
    </footer>
  </div>

</body>
</html>
PK
!<�.��.�.Fchrome/devtools-shim/content/aboutdevtools/images/dev-edition-logo.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1024 1024">
  <defs>
    <linearGradient id="linear-gradient" x1="-14359.352" y1="-8276.5" x2="-14096.11" y2="-8276.5" gradientTransform="matrix(0.762, 0.034, -0.05, 1.12, 11303.447, 10105.832)" gradientUnits="userSpaceOnUse">
      <stop offset="0" stop-color="#0083ff"/>
      <stop offset="0.176" stop-color="#0096f6"/>
      <stop offset="0.508" stop-color="#00b3e6"/>
      <stop offset="0.794" stop-color="#00c6dd"/>
      <stop offset="0.999" stop-color="#00ccda"/>
    </linearGradient>
    <linearGradient id="linear-gradient-2" x1="-504.949" y1="731.188" x2="-696.386" y2="104.96" gradientTransform="translate(1081.935)" gradientUnits="userSpaceOnUse">
      <stop offset="0" stop-color="#000f40" stop-opacity="0.6"/>
      <stop offset="0.611" stop-color="#000f40" stop-opacity="0.182"/>
      <stop offset="1" stop-color="#000f40" stop-opacity="0"/>
    </linearGradient>
    <linearGradient id="linear-gradient-3" x1="576.987" y1="731.188" x2="385.549" y2="104.96" gradientUnits="userSpaceOnUse">
      <stop offset="0" stop-color="#000f43" stop-opacity="0.4"/>
      <stop offset="0.485" stop-color="#001962" stop-opacity="0.173"/>
      <stop offset="1" stop-color="#002079" stop-opacity="0"/>
    </linearGradient>
    <linearGradient id="linear-gradient-4" x1="-8493.547" y1="-6472.091" x2="-8608.791" y2="-6480.987" gradientTransform="matrix(1.219, 0.123, -0.123, 1.219, 10533.536, 9729.872)" gradientUnits="userSpaceOnUse">
      <stop offset="0" stop-color="#002275"/>
      <stop offset="1" stop-color="#005fe7"/>
    </linearGradient>
    <linearGradient id="linear-gradient-5" x1="-8578.519" y1="-6637.149" x2="-8470.569" y2="-6749.273" gradientTransform="matrix(1.219, 0.123, -0.123, 1.219, 10533.536, 9729.872)" gradientUnits="userSpaceOnUse">
      <stop offset="0.049" stop-color="#005fe7"/>
      <stop offset="0.187" stop-color="#005ce1"/>
      <stop offset="0.378" stop-color="#0052cf"/>
      <stop offset="0.599" stop-color="#0043b2"/>
      <stop offset="0.84" stop-color="#002d8a"/>
      <stop offset="0.951" stop-color="#002275"/>
    </linearGradient>
    <linearGradient id="linear-gradient-6" x1="-8778.807" y1="-6369.407" x2="-8547.919" y2="-6368.865" gradientTransform="matrix(1.219, 0.123, -0.123, 1.219, 10533.536, 9729.872)" gradientUnits="userSpaceOnUse">
      <stop offset="0" stop-color="#002079"/>
      <stop offset="0.995" stop-color="#000f40"/>
    </linearGradient>
    <radialGradient id="radial-gradient" cx="620.422" cy="387.176" fx="753.8316036518333" fy="264.9254135147812" r="751.569" gradientUnits="userSpaceOnUse">
      <stop offset="0.156" stop-color="#58c1ff"/>
      <stop offset="0.241" stop-color="#4cb8ff"/>
      <stop offset="0.394" stop-color="#2ea0ff"/>
      <stop offset="0.541" stop-color="#0a84ff"/>
      <stop offset="0.763" stop-color="#0060df"/>
      <stop offset="0.798" stop-color="#0067de"/>
      <stop offset="0.85" stop-color="#007bdd"/>
      <stop offset="0.913" stop-color="#009cda"/>
      <stop offset="0.982" stop-color="#00c8d7"/>
    </radialGradient>
    <linearGradient id="linear-gradient-7" x1="-7625.135" y1="-8135.348" x2="-7696.164" y2="-7336.961" gradientTransform="translate(10165.893 9899.103) scale(1.225 1.225)" gradientUnits="userSpaceOnUse">
      <stop offset="0" stop-color="#00feff"/>
      <stop offset="0.298" stop-color="#04c9ff"/>
      <stop offset="0.613" stop-color="#0897ff"/>
      <stop offset="0.769" stop-color="#0a84ff"/>
      <stop offset="0.791" stop-color="#0a84ff" stop-opacity="0.934"/>
      <stop offset="0.837" stop-color="#0a84ff" stop-opacity="0.763"/>
      <stop offset="0.9" stop-color="#0a84ff" stop-opacity="0.487"/>
      <stop offset="0.978" stop-color="#0a84ff" stop-opacity="0.111"/>
      <stop offset="1" stop-color="#0a84ff" stop-opacity="0"/>
    </linearGradient>
    <linearGradient id="linear-gradient-8" x1="-9051.981" y1="-6804.517" x2="-8915.513" y2="-6804.517" gradientTransform="matrix(1.219, 0.123, -0.123, 1.219, 10533.536, 9729.872)" gradientUnits="userSpaceOnUse">
      <stop offset="0" stop-color="#003eaa"/>
      <stop offset="0.296" stop-color="#0054c5" stop-opacity="0.683"/>
      <stop offset="0.779" stop-color="#0076ef" stop-opacity="0.194"/>
      <stop offset="1" stop-color="#0083ff" stop-opacity="0"/>
    </linearGradient>
    <linearGradient id="linear-gradient-9" x1="589.737" y1="337.635" x2="394.718" y2="334.634" gradientUnits="userSpaceOnUse">
      <stop offset="0" stop-color="#00feff" stop-opacity="0.25"/>
      <stop offset="0.541" stop-color="#2ddfff" stop-opacity="0.122"/>
      <stop offset="1" stop-color="#58c1ff" stop-opacity="0"/>
    </linearGradient>
    <linearGradient id="linear-gradient-10" x1="266.139" y1="-1141.252" x2="148.317" y2="-1393.089" gradientTransform="matrix(0.995, 0.1, -0.1, 0.995, -179.129, 1422.24)" gradientUnits="userSpaceOnUse">
      <stop offset="0.19" stop-color="#002275" stop-opacity="0.7"/>
      <stop offset="0.633" stop-color="#009dc2" stop-opacity="0.364"/>
      <stop offset="0.944" stop-color="#00feff" stop-opacity="0.1"/>
    </linearGradient>
    <linearGradient id="linear-gradient-11" x1="17.917" y1="610.396" x2="112.025" y2="717.284" gradientTransform="matrix(0.995, 0.1, -0.1, 0.995, 300.005, -138.172)" gradientUnits="userSpaceOnUse">
      <stop offset="0.003" stop-color="#002275" stop-opacity="0.8"/>
      <stop offset="0.07" stop-color="#002479" stop-opacity="0.742"/>
      <stop offset="0.947" stop-color="#003eaa" stop-opacity="0"/>
    </linearGradient>
    <linearGradient id="linear-gradient-12" x1="195.983" y1="793.749" x2="196.031" y2="740.325" gradientTransform="matrix(0.995, 0.1, -0.1, 0.995, 300.005, -138.172)" gradientUnits="userSpaceOnUse">
      <stop offset="0" stop-color="#002275" stop-opacity="0.6"/>
      <stop offset="0.245" stop-color="#002275" stop-opacity="0.341"/>
      <stop offset="0.486" stop-color="#002275" stop-opacity="0.152"/>
      <stop offset="0.717" stop-color="#002275" stop-opacity="0.039"/>
      <stop offset="0.926" stop-color="#002275" stop-opacity="0"/>
    </linearGradient>
    <linearGradient id="linear-gradient-13" x1="605.651" y1="-76.365" x2="913.995" y2="613.142" gradientUnits="userSpaceOnUse">
      <stop offset="0" stop-color="#00feff"/>
      <stop offset="0.283" stop-color="#03dfff"/>
      <stop offset="0.897" stop-color="#0992ff"/>
      <stop offset="1" stop-color="#0a84ff"/>
    </linearGradient>
    <linearGradient id="linear-gradient-14" x1="717.202" y1="431.21" x2="572.416" y2="819.96" gradientUnits="userSpaceOnUse">
      <stop offset="0" stop-color="#0060df"/>
      <stop offset="0.093" stop-color="#0067e5" stop-opacity="0.806"/>
      <stop offset="0.211" stop-color="#006eec" stop-opacity="0.594"/>
      <stop offset="0.333" stop-color="#0075f2" stop-opacity="0.411"/>
      <stop offset="0.456" stop-color="#007af7" stop-opacity="0.262"/>
      <stop offset="0.583" stop-color="#007efa" stop-opacity="0.147"/>
      <stop offset="0.713" stop-color="#0081fd" stop-opacity="0.065"/>
      <stop offset="0.848" stop-color="#0082fe" stop-opacity="0.016"/>
      <stop offset="1" stop-color="#0083ff" stop-opacity="0"/>
    </linearGradient>
    <linearGradient id="linear-gradient-15" x1="676.293" y1="-32.525" x2="784.288" y2="877.93" gradientUnits="userSpaceOnUse">
      <stop offset="0" stop-color="#75feff"/>
      <stop offset="0.283" stop-color="#5adfff"/>
      <stop offset="0.897" stop-color="#1692ff"/>
      <stop offset="1" stop-color="#0a84ff"/>
    </linearGradient>
    <linearGradient id="linear-gradient-16" x1="771.892" y1="409.329" x2="689.396" y2="757.312" gradientUnits="userSpaceOnUse">
      <stop offset="0" stop-color="#9ee7ff"/>
      <stop offset="0.144" stop-color="#8cdbff" stop-opacity="0.879"/>
      <stop offset="0.461" stop-color="#5ebdff" stop-opacity="0.571"/>
      <stop offset="0.924" stop-color="#168cff" stop-opacity="0.083"/>
      <stop offset="1" stop-color="#0a84ff" stop-opacity="0"/>
    </linearGradient>
    <linearGradient id="linear-gradient-17" x1="685.293" y1="-14.837" x2="760.868" y2="558.271" gradientUnits="userSpaceOnUse">
      <stop offset="0" stop-color="#00feff"/>
      <stop offset="0.144" stop-color="#0bf7ff" stop-opacity="0.879"/>
      <stop offset="0.461" stop-color="#26e4ff" stop-opacity="0.571"/>
      <stop offset="0.924" stop-color="#51c6ff" stop-opacity="0.083"/>
      <stop offset="1" stop-color="#58c1ff" stop-opacity="0"/>
    </linearGradient>
    <linearGradient id="linear-gradient-18" x1="699.105" y1="19.682" x2="1097.246" y2="642.156" gradientUnits="userSpaceOnUse">
      <stop offset="0" stop-color="#00feff"/>
      <stop offset="0.144" stop-color="#01efff" stop-opacity="0.879"/>
      <stop offset="0.461" stop-color="#04caff" stop-opacity="0.571"/>
      <stop offset="0.924" stop-color="#098eff" stop-opacity="0.083"/>
      <stop offset="1" stop-color="#0a84ff" stop-opacity="0"/>
    </linearGradient>
    <linearGradient id="linear-gradient-19" x1="790.79" y1="466.207" x2="350.208" y2="809.78" xlink:href="#linear-gradient-16"/>
    <linearGradient id="linear-gradient-20" x1="394.976" y1="233.658" x2="79.697" y2="474.16" xlink:href="#linear-gradient-18"/>
    <linearGradient id="linear-gradient-21" x1="182.03" y1="128.544" x2="166.823" y2="260.843" xlink:href="#linear-gradient-18"/>
    <linearGradient id="linear-gradient-22" x1="328.879" y1="424.921" x2="231.871" y2="618.939" xlink:href="#linear-gradient-18"/>
  </defs>
  <title>firefox-logo-developer-edition</title>
  <g style="isolation: isolate">
    <g id="Globe">
      <polygon points="953.092 438.13 932.78 370.541 874.689 260.624 808.243 190.392 737.702 139.891 676.453 115.784 602.961 89.57 481.956 81.846 405.656 95.655 343.398 114.847 232.926 179.679 163.881 256.916 100.219 360.601 75.176 448.838 70.495 582.715 94.368 673.995 147.029 777.914 184.17 828.107 235.529 875.325 299.671 918.834 378.764 953.331 511.131 968.911 580.245 970.841 682.539 941.95 773.97 891.629 832.795 841.308 878.41 786.737 942.897 660.151 957.135 573.558 953.092 438.13" style="fill: #061a58"/>
    </g>
    <g id="Globe_copy" data-name="Globe copy">
      <polygon points="484.172 142.682 429.984 196.594 518.379 232.527 484.172 142.682" style="fill: #133691"/>
      <polygon points="518.379 232.527 484.172 142.682 584.136 150.756 518.379 232.527" style="fill: #1943ae"/>
      <polygon points="518.379 232.527 689.517 227.32 584.136 150.756 518.379 232.527" style="fill: #2053cf"/>
      <polygon points="689.517 227.32 584.136 150.756 676.804 150.18 689.517 227.32" style="fill: #2660eb"/>
      <polygon points="689.517 227.32 786.965 220.939 676.804 150.18 689.517 227.32" style="fill: #306dff"/>
      <polygon points="786.965 220.939 676.804 150.18 731.567 144.337 786.965 220.939" style="fill: #235adb"/>
      <polygon points="786.965 220.939 731.567 144.337 808.243 190.392 786.965 220.939" style="fill: #2660eb"/>
      <polygon points="860.33 267.848 786.965 220.939 808.243 190.392 860.33 267.848" style="fill: #2053cf"/>
      <polygon points="786.965 220.939 774.694 273.417 841.793 300.309 786.965 220.939" style="fill: #1c4abd"/>
      <polygon points="786.965 220.939 860.33 267.848 841.793 300.309 786.965 220.939" style="fill: #1a46b4"/>
      <polygon points="689.517 227.32 786.965 220.939 774.694 273.417 689.517 227.32" style="fill: #2660eb"/>
      <polygon points="689.517 227.32 774.694 273.417 711.618 345.869 689.517 227.32" style="fill: #235adb"/>
      <polygon points="689.517 227.32 612.524 297.207 711.618 345.869 689.517 227.32" style="fill: #1f51ca"/>
      <polygon points="518.379 232.527 689.517 227.32 612.524 297.207 518.379 232.527" style="fill: #1f50c9"/>
      <polygon points="711.618 345.869 774.694 273.417 821.295 364.042 711.618 345.869" style="fill: #2053cf"/>
      <polygon points="774.694 273.417 821.295 364.042 841.793 300.309 774.694 273.417" style="fill: #1b48b9"/>
      <polygon points="821.295 364.042 841.793 300.309 890.615 416.753 821.295 364.042" style="fill: #173fa5"/>
      <polygon points="841.793 300.309 916.202 354.955 890.615 416.753 841.793 300.309" style="fill: #12348d"/>
      <polygon points="841.793 300.309 916.202 354.955 860.33 267.848 841.793 300.309" style="fill: #143895"/>
      <polygon points="860.33 267.848 874.689 260.624 916.202 354.955 860.33 267.848" style="fill: #0f2f82"/>
      <polygon points="916.202 354.955 932.78 370.541 874.689 260.624 916.202 354.955" style="fill: #05154c"/>
      <polygon points="860.33 267.848 874.689 260.624 808.243 190.392 860.33 267.848" style="fill: #163c9e"/>
      <polygon points="949.359 490.939 932.78 370.541 953.092 438.131 949.089 490.939 949.359 490.939" style="fill: #051753"/>
      <polygon points="916.202 354.955 949.359 490.939 932.78 370.541 916.202 354.955" style="fill: #061a57"/>
      <polygon points="890.615 416.753 949.359 490.939 916.202 354.955 890.615 416.753" style="fill: #05154c"/>
      <polygon points="949.089 490.939 953.092 438.131 957.135 573.558 949.359 490.939 949.089 490.939" style="fill: #081f63"/>
      <polygon points="939.515 597.966 957.135 573.558 949.359 490.939 939.515 597.966" style="fill: #12348d"/>
      <polygon points="942.898 660.151 928.808 666.958 957.135 573.558 942.898 660.151" style="fill: #123392"/>
      <polygon points="928.808 666.958 957.135 573.558 939.515 597.966 928.808 666.958" style="fill: #0c2674"/>
      <polygon points="939.515 597.966 949.359 490.939 919.18 526.639 939.515 597.966" style="fill: #081f63"/>
      <polygon points="928.808 666.958 939.515 597.966 881.303 696.064 928.808 666.958" style="fill: #0f2f82"/>
      <polygon points="890.615 416.753 949.359 490.939 919.18 526.639 890.615 416.753" style="fill: #061a57"/>
      <polygon points="939.515 597.966 919.18 526.639 888.49 614.496 939.515 597.966" style="fill: #081e5f"/>
      <polygon points="881.303 696.064 939.515 597.966 888.49 614.496 881.303 696.064" style="fill: #092167"/>
      <polygon points="878.41 786.737 942.898 660.151 928.808 666.958 878.41 786.737" style="fill: #163aa2"/>
      <polygon points="878.41 786.737 928.808 666.958 881.656 754.277 878.41 786.737" style="fill: #163aa2"/>
      <polygon points="881.656 754.277 928.808 666.958 881.303 696.064 881.656 754.277" style="fill: #0e2b7e"/>
      <polygon points="832.795 841.308 881.656 754.277 878.41 786.737 832.795 841.308" style="fill: #123392"/>
      <polyline points="770.629 869.98 832.795 841.308 773.97 891.629 770.629 869.98" style="fill: #1a45b2"/>
      <polygon points="832.795 841.308 881.656 754.277 770.629 869.98 832.795 841.308" style="fill: #123392"/>
      <polygon points="770.629 869.98 773.97 891.629 722.119 907.709 770.629 869.98" style="fill: #133493"/>
      <polygon points="682.539 941.95 722.119 907.709 773.97 891.629 682.539 941.95" style="fill: #1a45b2"/>
      <polygon points="682.539 941.95 722.119 907.709 593.481 955.816 682.539 941.95" style="fill: #1a45b2"/>
      <polygon points="580.245 970.841 682.539 941.95 593.481 955.816 580.245 970.841" style="fill: #1a45b2"/>
      <polygon points="511.131 968.911 580.245 970.841 593.481 955.816 511.131 968.911" style="fill: #1a45b2"/>
      <polygon points="378.764 953.331 371.214 941.261 511.131 968.911 378.764 953.331" style="fill: #1f51ca"/>
      <polygon points="378.764 953.331 371.214 941.261 299.671 918.834 378.764 953.331" style="fill: #173fad"/>
      <polygon points="235.529 875.325 184.17 828.107 299.671 918.834 235.529 875.325" style="fill: #173fad"/>
      <polygon points="184.17 828.107 147.029 777.914 171.697 786.057 184.17 828.107" style="fill: #1b47b5"/>
      <polygon points="184.17 828.107 299.671 918.834 261.885 870.53 184.17 828.107" style="fill: #163aa3"/>
      <polygon points="184.17 828.107 261.885 870.53 171.697 786.057 184.17 828.107" style="fill: #173fad"/>
      <polygon points="261.885 870.53 371.214 941.261 299.671 918.834 261.885 870.53" style="fill: #133595"/>
      <polygon points="261.885 870.53 371.214 941.261 373.026 899.669 261.885 870.53" style="fill: #163ba4"/>
      <polygon points="430.646 926.684 371.214 941.261 373.026 899.669 430.646 926.684" style="fill: #163aa2"/>
      <polygon points="171.697 786.057 147.029 777.914 130.529 703.837 171.697 786.057" style="fill: #2053cf"/>
      <polygon points="147.029 777.914 94.368 673.995 130.529 703.837 147.029 777.914" style="fill: #306dff"/>
      <polyline points="77.048 562.119 130.529 703.837 94.368 673.995" style="fill: #1f51ca"/>
      <polygon points="94.368 673.995 70.495 582.715 77.048 562.119 94.368 673.995" style="fill: #2257d7"/>
      <polygon points="77.048 562.119 70.495 582.715 75.176 448.838 77.048 562.119" style="fill: #11318d"/>
      <polygon points="100.219 360.601 116.603 354.516 75.176 448.838 100.219 360.601" style="fill: #0c2674"/>
      <polygon points="75.176 448.838 106.539 442.987 116.603 354.516 75.176 448.838" style="fill: #102f88"/>
      <polygon points="77.048 562.119 75.176 448.838 106.539 442.987 77.048 562.119" style="fill: #133595"/>
      <polygon points="106.539 442.987 104.432 539.65 77.048 562.119 106.539 442.987" style="fill: #12328e"/>
      <polygon points="163.881 256.916 116.603 354.516 100.219 360.601 163.881 256.916" style="fill: #0b2572"/>
      <polygon points="163.881 256.916 224.266 199.106 232.926 179.679 163.881 256.916" style="fill: #020b2e"/>
      <polygon points="232.926 179.679 304.78 143.87 224.266 199.106 232.926 179.679" style="fill: #092064"/>
      <polyline points="232.926 179.679 343.398 114.847 304.78 143.87 232.926 179.679" style="fill: #030f41"/>
      <polygon points="304.78 143.87 405.656 95.655 343.398 114.847 304.78 143.87" style="fill: #081e5f"/>
      <polygon points="405.656 95.655 366.218 138.252 304.78 143.87 405.656 95.655" style="fill: #081f61"/>
      <polygon points="366.218 138.252 460.19 100.57 405.656 95.655 366.218 138.252" style="fill: #0d2a78"/>
      <polygon points="481.956 81.846 405.656 95.655 460.19 100.57 481.956 81.846" style="fill: #11338b"/>
      <polyline points="554.122 99.634 460.19 100.57 481.956 81.846" style="fill: #153a99"/>
      <polygon points="481.956 81.846 602.961 89.57 554.122 99.634 481.956 81.846" style="fill: #0d2a78"/>
      <polygon points="554.122 99.634 602.961 89.57 676.453 115.784 554.122 99.634" style="fill: #2053ce"/>
      <polygon points="731.567 144.337 676.453 115.784 737.702 139.891 731.567 144.337" style="fill: #1e4dc3"/>
      <polygon points="731.567 144.337 737.702 139.891 808.243 190.392 731.567 144.337" style="fill: #1e4fc7"/>
      <polygon points="371.214 941.261 511.131 968.911 430.646 926.684 371.214 941.261" style="fill: #1a45b2"/>
      <polygon points="511.131 968.911 430.646 926.684 509.411 911.971 511.131 968.911" style="fill: #163aa2"/>
      <polygon points="593.481 955.816 511.131 968.911 509.411 911.971 593.481 955.816" style="fill: #1a45b2"/>
      <polygon points="593.481 955.816 509.411 911.971 625.27 880.76 593.481 955.816" style="fill: #1741aa"/>
      <polyline points="722.119 907.709 593.481 955.816 625.27 880.76" style="fill: #1a45b2"/>
      <polygon points="722.119 907.709 727.999 832.609 625.27 880.76 722.119 907.709" style="fill: #133493"/>
      <polygon points="722.119 907.709 727.999 832.609 770.629 869.98 722.119 907.709" style="fill: #163aa2"/>
      <polygon points="770.629 869.98 727.999 832.609 830.997 763.977 770.629 869.98" style="fill: #133493"/>
      <polygon points="770.629 869.98 830.997 763.977 881.656 754.277 770.629 869.98" style="fill: #1a45b2"/>
      <polygon points="881.656 754.277 881.303 696.064 830.997 763.977 881.656 754.277" style="fill: #092167"/>
      <polyline points="676.453 115.784 676.804 150.18 731.567 144.337" style="fill: #2258d9"/>
      <polygon points="676.804 150.18 676.453 115.784 554.122 99.634 676.804 150.18" style="fill: #2257d7"/>
      <polygon points="554.122 99.634 584.136 150.756 676.804 150.18 554.122 99.634" style="fill: #2257d7"/>
      <polygon points="554.122 99.634 584.136 150.756 484.172 142.682 554.122 99.634" style="fill: #1a46b3"/>
      <polygon points="460.19 100.57 484.172 142.682 554.122 99.634 460.19 100.57" style="fill: #163c9f"/>
      <polygon points="366.218 138.252 460.19 100.57 484.172 142.682 366.218 138.252" style="fill: #103186"/>
      <polygon points="429.984 196.594 366.218 138.252 484.172 142.682 429.984 196.594" style="fill: #163c9e"/>
      <polygon points="304.78 143.87 224.266 199.106 262.417 235.852 304.78 143.87" style="fill: #031043"/>
      <polygon points="366.218 138.252 262.417 235.852 304.78 143.87 366.218 138.252" style="fill: #030f41"/>
      <polygon points="262.417 235.852 328.652 246.9 366.218 138.252 262.417 235.852" style="fill: #05164e"/>
      <polygon points="328.652 246.9 366.218 138.252 429.984 196.594 328.652 246.9" style="fill: #092064"/>
      <polygon points="163.881 256.916 167.918 296.178 224.266 199.106 163.881 256.916" style="fill: #030f41"/>
      <polygon points="116.603 354.516 163.881 256.916 167.918 296.178 116.603 354.516" style="fill: #091f64"/>
      <polygon points="167.918 296.178 224.266 199.106 262.417 235.852 167.918 296.178" style="fill: #041345"/>
      <polygon points="164.505 380.339 167.918 296.178 116.603 354.516 164.505 380.339" style="fill: #0a226b"/>
      <polygon points="164.505 380.339 167.918 296.178 234.508 350.387 164.505 380.339" style="fill: #091f64"/>
      <polygon points="234.508 350.387 262.417 235.852 167.918 296.178 234.508 350.387" style="fill: #061853"/>
      <polygon points="234.508 350.387 262.417 235.852 328.652 246.9 234.508 350.387" style="fill: #030f42"/>
      <polygon points="106.539 442.987 116.603 354.516 164.505 380.339 106.539 442.987" style="fill: #0b2572"/>
      <polygon points="130.529 703.837 104.432 539.65 77.048 562.119 130.529 703.837" style="fill: #173da9"/>
      <polygon points="171.697 786.057 130.529 703.837 174.647 710.039 171.697 786.057" style="fill: #1a45b2"/>
      <polygon points="130.529 703.837 174.647 710.039 143.753 637.717 130.529 703.837" style="fill: #1741aa"/>
      <polygon points="130.529 703.837 104.432 539.65 143.753 637.717 130.529 703.837" style="fill: #10308b"/>
      <polygon points="171.697 786.057 218.064 795 174.647 710.039 171.697 786.057" style="fill: #123390"/>
      <polygon points="171.697 786.057 261.885 870.53 218.064 795 171.697 786.057" style="fill: #133493"/>
      <polygon points="261.885 870.53 294.517 839.069 218.064 795 261.885 870.53" style="fill: #1a45b2"/>
      <polygon points="261.885 870.53 373.026 899.669 294.517 839.069 261.885 870.53" style="fill: #163aa2"/>
      <polygon points="373.026 899.669 430.646 926.684 509.411 911.971 373.026 899.669" style="fill: #1a45b2"/>
      <polygon points="373.026 899.669 410.7 820.591 509.411 911.971 373.026 899.669" style="fill: #123392"/>
      <polygon points="104.432 539.65 168.796 539.558 106.539 442.987 104.432 539.65" style="fill: #102f87"/>
      <polygon points="106.539 442.987 168.796 539.558 164.505 380.339 106.539 442.987" style="fill: #081f63"/>
      <polygon points="104.432 539.65 143.753 637.717 168.796 539.558 104.432 539.65" style="fill: #163ba4"/>
      <polygon points="143.753 637.717 168.796 539.558 199.925 617.121 143.753 637.717" style="fill: #11318c"/>
      <polygon points="174.647 710.039 199.925 617.121 143.753 637.717 174.647 710.039" style="fill: #123390"/>
      <polygon points="174.647 710.039 294.014 784.213 199.925 617.121 174.647 710.039" style="fill: #123392"/>
      <polygon points="218.064 795 294.014 784.213 174.647 710.039 218.064 795" style="fill: #163aa2"/>
      <polygon points="294.517 839.069 294.014 784.213 218.064 795 294.517 839.069" style="fill: #1741aa"/>
      <polygon points="294.517 839.069 294.014 784.213 410.7 820.591 294.517 839.069" style="fill: #123392"/>
      <polygon points="294.517 839.069 373.026 899.669 410.7 820.591 294.517 839.069" style="fill: #123392"/>
      <polygon points="509.411 911.971 519.319 821.829 410.7 820.591 509.411 911.971" style="fill: #0e2b80"/>
      <polygon points="509.411 911.971 519.319 821.829 625.27 880.76 509.411 911.971" style="fill: #143698"/>
      <polygon points="625.27 880.76 621.148 779.428 519.319 821.829 625.27 880.76" style="fill: #0d2878"/>
      <polygon points="625.27 880.76 621.148 779.428 727.999 832.609 625.27 880.76" style="fill: #123392"/>
      <polygon points="621.148 779.428 687.259 719.396 727.999 832.609 621.148 779.428" style="fill: #081f63"/>
      <polygon points="727.999 832.609 777.975 680.837 687.259 719.396 727.999 832.609" style="fill: #061a57"/>
      <polygon points="830.997 763.977 727.999 832.609 777.975 680.837 830.997 763.977" style="fill: #0f2f82"/>
      <polygon points="830.997 763.977 881.303 696.064 777.975 680.837 830.997 763.977" style="fill: #092167"/>
      <polygon points="881.303 696.064 777.975 680.837 888.49 614.496 881.303 696.064" style="fill: #05164f"/>
      <polyline points="919.18 526.639 847.27 475.313 888.49 614.496 919.18 526.639" style="fill: #051753"/>
      <polygon points="919.18 526.639 890.615 416.753 847.27 475.313 919.18 526.639" style="fill: #031043"/>
      <polygon points="777.975 680.837 888.49 614.496 781.377 566.682 777.975 680.837" style="fill: #031043"/>
      <polygon points="777.975 680.837 677.818 579.356 687.259 719.396 777.975 680.837" style="fill: #030f41"/>
      <polygon points="621.148 779.428 544.251 711.156 687.259 719.396 621.148 779.428" style="fill: #092167"/>
      <polygon points="621.148 779.428 519.319 821.829 544.251 711.156 621.148 779.428" style="fill: #0f2f82"/>
      <polygon points="410.7 820.591 519.319 821.829 385.067 733.793 410.7 820.591" style="fill: #123390"/>
      <polygon points="410.7 820.591 385.067 733.793 294.014 784.213 410.7 820.591" style="fill: #0f2d84"/>
      <polygon points="294.014 784.213 311.045 696.064 385.067 733.793 294.014 784.213" style="fill: #0e2b7e"/>
      <polygon points="311.045 696.064 199.925 617.121 294.014 784.213 311.045 696.064" style="fill: #0f2e86"/>
      <polygon points="519.319 821.829 544.251 711.156 385.067 733.793 519.319 821.829" style="fill: #081f63"/>
      <polygon points="168.796 539.558 200.731 437.346 164.505 380.339 168.796 539.558" style="fill: #0a236c"/>
      <polygon points="164.505 380.339 200.731 437.346 234.508 350.387 164.505 380.339" style="fill: #081f63"/>
      <polygon points="234.508 350.387 374.647 309.424 328.652 246.9 234.508 350.387" style="fill: #05164e"/>
      <polygon points="234.508 350.387 374.647 309.424 300.625 461.061 234.508 350.387" style="fill: #030f41"/>
      <polygon points="777.975 680.837 677.818 579.356 781.377 566.682 777.975 680.837" style="fill: #05164f"/>
      <polygon points="888.49 614.496 847.27 475.313 781.377 566.682 888.49 614.496" style="fill: #030f42"/>
      <polygon points="687.259 719.396 572.721 608.341 677.818 579.356 687.259 719.396" style="fill: #030f41"/>
      <polygon points="544.251 711.156 687.259 719.396 572.721 608.341 544.251 711.156" style="fill: #05164f"/>
      <polygon points="544.251 711.156 465.917 647.195 572.721 608.341 544.251 711.156" style="fill: #031042"/>
      <polygon points="847.27 475.313 890.615 416.753 821.295 364.042 847.27 475.313" style="fill: #0f2e82"/>
      <polygon points="234.508 350.387 300.625 461.061 200.731 437.346 234.508 350.387" style="fill: #061853"/>
      <polygon points="200.731 437.346 300.625 461.061 233.07 567.76 200.731 437.346" style="fill: #081f63"/>
      <polygon points="374.647 309.424 429.984 196.594 328.652 246.9 374.647 309.424" style="fill: #081e5f"/>
      <polyline points="233.07 567.76 200.731 437.346 168.796 539.558" style="fill: #091f64"/>
      <polygon points="168.796 539.558 233.07 567.76 199.925 617.121 168.796 539.558" style="fill: #11318c"/>
      <polygon points="233.07 567.76 311.045 696.064 199.925 617.121 233.07 567.76" style="fill: #0e2b7e"/>
      <polygon points="311.045 696.064 321.466 540.833 233.07 567.76 311.045 696.064" style="fill: #092167"/>
      <polygon points="311.045 696.064 465.917 647.195 321.466 540.833 311.045 696.064" style="fill: #061a57"/>
      <polygon points="465.917 647.195 450.825 527.178 321.466 540.833 465.917 647.195" style="fill: #05164f"/>
      <polygon points="572.721 608.341 465.917 647.195 450.825 527.178 572.721 608.341" style="fill: #041245"/>
      <polygon points="450.825 527.178 560.061 486.214 572.721 608.341 450.825 527.178" style="fill: #030f42"/>
      <polygon points="385.067 733.793 544.251 711.156 465.917 647.195 385.067 733.793" style="fill: #092167"/>
      <polygon points="385.067 733.793 311.045 696.064 465.917 647.195 385.067 733.793" style="fill: #0c2674"/>
      <polygon points="374.647 309.424 469.51 319.485 429.984 196.594 374.647 309.424" style="fill: #0c2771"/>
      <polygon points="518.379 232.527 429.984 196.594 469.51 319.485 518.379 232.527" style="fill: #153a99"/>
      <polygon points="518.379 232.527 612.524 297.207 469.51 319.485 518.379 232.527" style="fill: #163da0"/>
      <polygon points="469.51 319.485 609.793 394.082 560.061 486.214 469.51 319.485" style="fill: #11338b"/>
      <polygon points="609.793 394.082 612.524 297.207 469.51 319.485 609.793 394.082" style="fill: #163ca0"/>
      <polygon points="609.793 394.082 612.524 297.207 711.618 345.869 609.793 394.082" style="fill: #1a45b2"/>
      <polygon points="469.51 319.485 560.061 486.214 401.237 406.443 469.51 319.485" style="fill: #0c266e"/>
      <polygon points="450.825 527.178 560.061 486.214 401.237 406.443 450.825 527.178" style="fill: #071b5c"/>
      <polygon points="560.061 486.214 700.138 460.558 609.793 394.082 560.061 486.214" style="fill: #0f2f82"/>
      <polygon points="711.618 345.869 700.138 460.558 609.793 394.082 711.618 345.869" style="fill: #1741aa"/>
      <polygon points="711.618 345.869 777.734 435.908 700.138 460.558 711.618 345.869" style="fill: #153a99"/>
      <polygon points="821.295 364.042 711.618 345.869 777.734 435.908 821.295 364.042" style="fill: #1945b1"/>
      <polygon points="777.734 435.908 847.27 475.313 821.295 364.042 777.734 435.908" style="fill: #12338c"/>
      <polygon points="781.377 566.682 847.27 475.313 777.734 435.908 781.377 566.682" style="fill: #081e60"/>
      <polygon points="781.377 566.682 777.734 435.908 700.138 460.558 781.377 566.682" style="fill: #081e5f"/>
      <polyline points="700.138 460.558 677.818 579.356 781.377 566.682" style="fill: #061a57"/>
      <polygon points="560.061 486.214 700.138 460.558 677.818 579.356 560.061 486.214" style="fill: #031042"/>
      <polygon points="572.721 608.341 677.818 579.356 560.061 486.214 572.721 608.341" style="fill: #05164f"/>
      <polygon points="374.647 309.424 401.237 406.443 469.51 319.485 374.647 309.424" style="fill: #05174f"/>
      <polygon points="374.647 309.424 300.625 461.061 401.237 406.443 374.647 309.424" style="fill: #061853"/>
      <polygon points="401.237 406.443 321.466 540.833 450.825 527.178 401.237 406.443" style="fill: #030f41"/>
      <polygon points="321.466 540.833 401.237 406.443 300.625 461.061 321.466 540.833" style="fill: #041245"/>
      <polygon points="233.07 567.76 300.625 461.061 321.466 540.833 233.07 567.76" style="fill: #061a58"/>
    </g>
    <g id="Wireframe_-_Expanded" data-name="Wireframe - Expanded">
      <path d="M959.127,573.683c0-.043,0-.086,0-.129,0-.019,0-.037,0-.056v-.024l-4.042-135.4c0-.019-.006-.037-.007-.057s0-.065-.006-.1-.016-.093-.024-.14-.022-.12-.038-.179c0-.014,0-.028-.008-.043l-20.3-67.565,0-.016,0-.008c0-.015-.013-.028-.018-.043-.012-.037-.026-.073-.041-.109s-.04-.1-.063-.145c-.01-.02-.014-.041-.025-.061L876.457,259.689c-.01-.02-.025-.035-.036-.054-.022-.038-.045-.076-.069-.113s-.043-.065-.066-.1-.027-.039-.042-.058-.042-.044-.062-.067-.025-.035-.04-.051L809.7,189.019c-.01-.01-.022-.018-.032-.029l-.046-.046c-.038-.037-.083-.068-.125-.1s-.055-.054-.088-.077l-.011-.008-.013-.009-70.518-50.485c-.011-.008-.023-.012-.034-.019s-.044-.031-.068-.046c-.047-.03-.1-.052-.146-.077s-.1-.054-.151-.076c-.012,0-.021-.012-.033-.017l-61.206-24.09-.005,0-.037-.015c-.01,0-.02,0-.03-.008s-.02-.011-.031-.014l-.012,0-.007,0L603.632,87.686c-.023-.008-.047-.01-.07-.017-.047-.015-.093-.028-.141-.04s-.086-.02-.13-.027-.089-.012-.135-.016c-.024,0-.046-.01-.07-.012l-121-7.724h-.026l-.032,0c-.049,0-.1.006-.149.008a1.987,1.987,0,0,0-.2.013l-.05.006-.026,0L405.3,93.687c-.016,0-.03.01-.045.013s-.046.006-.069.012-.033.011-.05.016c-.046.012-.092.022-.136.038l-62.19,19.171-.021.009c-.046.015-.091.037-.137.055s-.107.043-.16.07c-.026.013-.053.025-.079.039-.009.005-.018.007-.027.012L231.913,177.955c-.008,0-.013.011-.021.015-.032.019-.061.043-.092.064s-.057.036-.085.057-.05.043-.075.064-.045.034-.066.053-.052.057-.079.084-.042.035-.061.055l-.027.03h0L162.39,255.583c-.01.012-.017.025-.027.037s-.033.038-.049.057-.03.042-.046.063-.041.059-.061.089c-.009.014-.021.025-.03.04l-.023.038,0,0L98.515,359.555c-.012.019-.018.04-.029.06-.021.036-.041.072-.06.109s-.036.083-.053.125-.037.1-.053.144c-.007.022-.018.04-.025.062L73.252,448.292c0,.012,0,.024-.007.037s-.013.042-.018.064l-.007.024,0,.027c-.009.046-.011.093-.017.14s-.016.1-.018.147c0,.013,0,.025-.005.038L68.5,582.645c0,.029.005.056.006.085s0,.083,0,.125.01.083.015.125.018.109.03.163c.006.026.005.052.012.078l.015.056v0L92.433,674.5l0,0,.01.036c.007.023.016.046.023.069a1.953,1.953,0,0,0,.077.191v0c.014.032.021.065.037.1l.008.016,0,.009,52.649,103.893.01.015c.009.018.022.034.032.051.033.058.067.115.1.17.011.016.018.034.029.049l37.117,50.162,0,0,0,0,.008.011.01.014c.005.007.012.011.017.018s.03.04.046.06c.033.04.073.076.11.114s.051.063.081.091L234.175,876.8c.04.037.086.064.128.1s.065.06.1.086l64.142,43.509c.014.009.03.012.044.021l.015.008c.05.032.1.063.154.09.027.014.053.029.08.041.01,0,.019.012.03.017l79.094,34.5c.026.011.052.015.079.025.058.022.116.042.176.059.031.009.06.018.091.026.047.011.094.018.141.026.027,0,.052.014.08.018L510.9,970.9c.048.006.1.006.144.008.012,0,.022,0,.034,0l69.114,1.93h.055l.019,0a2,2,0,0,0,.387-.04c.028-.006.053-.018.081-.025h0c.019,0,.038,0,.056-.008l102.294-28.891c.021-.006.038-.017.059-.023s.061-.015.091-.026.053-.026.08-.038l.055-.026c.04-.018.081-.034.12-.054l.016-.006.042-.023.006,0,91.377-50.291.027-.016.038-.023c.053-.032.1-.07.149-.106h0c.041-.03.085-.054.123-.086l.005,0L834.1,842.827c.023-.019.039-.043.061-.063s.052-.046.077-.071.05-.053.074-.08c.007-.008.016-.014.023-.022l45.605-54.559.009-.01h0c.007-.008.011-.018.018-.026a2,2,0,0,0,.161-.238c.017-.029.039-.056.054-.085l.014-.024h0L944.68,661.06c.015-.03.022-.062.036-.093.023-.05.049-.1.067-.151.008-.023.011-.047.019-.071.017-.054.03-.108.042-.163.008-.036.022-.07.028-.107L959.105,573.9v0l0-.018c0-.017,0-.034,0-.051s.009-.048.012-.073S959.125,573.708,959.127,573.683ZM236.773,873.752l-13.3-12.232,29.922,23.5ZM430.911,924.6l-46.749-21.918,110.653,9.981Zm17.05-399.131L325.22,538.425l75.69-127.514ZM405.118,410.63l149.957,75.318L451.937,524.624Zm43.967,118.743,14.266,113.449L326.8,542.281Zm12.477,117.1-148.325,46.8,9.979-148.669Zm-8.208-115.206L568.32,607.813,467.588,644.458Zm1.832-3.587L558.339,489l11.955,115.326Zm158.49-133.223,95.6-45.266L698.5,456.868Zm81.548,64.969L563.8,483.5l46.659-86.44ZM560.056,482.014,474.441,324.372,607.077,394.9Zm-4.8-.449L404.32,405.754,469.2,323.113Zm-233.07,54.13-19.235-73.623,92.857-50.409ZM309.473,689.626l-73.356-120.7,83.158-25.332Zm149.788-38.234-74.629,79.935L316.307,696.5Zm110.5-39.844-26.617,96.12-73.234-59.8Zm-7.218-120.822,110.944,87.753-99.016,27.307Zm2.227-3.339L697.635,463.05,676.463,575.735Zm137.665-29.655L713.09,351.252l61.383,83.594Zm73.378-19.113,3.4,121.937L703.456,461.6ZM611.883,390.879l2.552-90.506,92.579,45.462Zm-4-.077L475.749,320.537l134.708-20.985ZM402.116,402.084,377.35,311.722l88.355,9.371Zm-3.227,3.357-93.725,50.88,68.956-141.258ZM319.045,539.48l-81.4,24.8,62.209-98.258ZM304.3,688.82l-101.631-72.2,30.315-45.146Zm8.2,10.233,68.3,34.813L296.79,780.39Zm153.6-49.126,73.349,59.89L390.4,731.014Zm107.648-37.8L682.007,717.089,546.84,709.3Zm2.874-2.785,99.364-27.4,8.926,132.4Zm124.652-144,76.387,99.786-97.375,11.918ZM779.832,439.4l64.51,36.558-61.132,84.765Zm.68-4.213,39.949-65.91,23.823,102.05Zm-2.937-2.871-61.437-83.668,101.914,16.887Zm-161.528-135.6,72.16-65.5,20.714,111.107ZM473.271,316.875l45.78-81.46,88.193,60.591ZM377.7,307.737,429.6,201.925l37.068,115.247ZM300.447,456.866l-62.9-105.284,133.314-38.969ZM233.885,562.734,203.462,440.05l93.973,22.31Zm-33.512,50.132-27.9-69.51,57.6,25.274Zm108.464,84.082-15.753,81.536L206.055,623.929Zm75.018,39.8,23.867,80.818L299.076,783.7Zm157.807-23.207L518,818.574,390.6,735.027Zm140.724,7.575-61.254,55.622-71.248-63.257Zm-2.212-136.526,94.309,95.555-85.419,36.307Zm1.925-3.745,97.208-11.9L776.115,676.1Zm164.409-101.06L885.338,610.9l-100.9-45.041ZM824.5,369l63.329,48.155-39.6,53.5ZM715.479,344.481l58.786-67.524L817.7,361.419Zm-2.653-3.046L692.251,231.074l79.294,42.914ZM612.352,294.662l-87.8-60.322,159.607-4.856Zm-142.34,19.849L433.2,200.06l82.325,33.465ZM374.3,305.581l-42.628-57.948,93.916-46.625Zm-2.962,2.724L240.773,346.473l87.716-96.42ZM296.479,458.022,203.426,435.93l31.463-81ZM230.151,564.295l-58.911-25.849,29.27-93.684ZM197.3,615.953l-50.672,18.58,22.592-88.548ZM288.443,778.4l-111.474-69.27,23.606-86.775Zm113.5,41.562L296.5,836.729l-.457-49.789Zm-13.419-81.5,123.974,81.3-100.3-1.143ZM545.457,714.9l71.979,63.907L522.12,818.5Zm140.933,7.986L724.43,828.6l-99.772-49.657Zm3.391-2.389L774.695,684.4,727.915,826.47Zm93.506-150.773,100.882,45.032L780.083,677.239Zm67.552-89.407L916.8,527.4l-28.151,80.586Zm-.75-5.45,39.645-53.561,26.126,100.5ZM823.632,363.307l18.408-57.234,43.845,104.571Zm-2.765-4.469-41.945-81.571,60.4,24.205Zm-124.3-129.976,87.834-5.751-11.06,47.3ZM522.66,230.4l61.842-76.9,99.106,72Zm-3.653-1.841-31.84-83.624,93.043,7.515Zm-4.132.389L433.537,195.88,483.4,146.272ZM332.14,242.936l34.974-101.153L426.481,196.1Zm-93.832,100.3L263.92,238.13,324.7,248.269Zm-38.01,89.7-32.843-51.683L230.921,354.1Zm-1.754,4.7-28.073,89.853L166.7,387.522ZM166.222,541.561l-22.892,89.727-35.942-89.642Zm-59.745-3.914,1.919-88.089,56.735,88Zm90.5,82.684-22.751,83.633L146.419,638.87Zm91.3,162.676L219.2,792.819,179.7,715.535Zm3.751,3.508.45,49.074-68.4-39.425Zm115.223,36.649-34.972,73.407-72.879-56.254Zm4.078.742,92.409,85.545L376.068,897.935Zm4.532-1.255L517.089,823.8l-9.233,84Zm203.4-40.272,3.86,94.9L523.9,822.088Zm4.017.347L723.4,832.555l-96.256,45.117Zm155.272-97.258,49.7,77.933L731.71,827.733Zm3.46-2.013,95.693,14.1-46.589,62.9Zm1.783-3.78,102.354-61.443L879.5,693.776ZM918.986,533.256l18.075,63.4-45.355,14.692ZM894.939,425.435,946.776,490.9l-26.631,31.5ZM846.43,306.2l67.324,49.443L890.6,411.553Zm-69.4-34,10.852-46.41,48.489,70.193ZM691.2,225.206l-11.686-70.911,101.265,65.046Zm-4.445-2.368L590.24,152.718l84.869-.528ZM490.426,141.181l63.005-38.774,27.033,46.046Zm-60.5,52.65-58.338-53.375L479.5,144.508ZM327.314,244.65l-60.587-10.106,94.948-89.274Zm-94,102.188-61.986-50.463,87.967-56.157Zm-2.635,3.013-64.047,27.4,3.123-77ZM166.6,532.467l-57.545-89.26L162.639,385.3ZM141.674,637.91l-10.92,54.6L109.2,556.924Zm2.774,6.527,26.968,63.128-38.511-5.414ZM214.417,792.26l-40.656-7.842,2.588-66.654Zm76.8,47.215L262.333,867.32,223.549,800.47Zm3.446,2.232,69.352,53.532L265.835,869.5Zm226.316-16.667,99.012,55.071L511.719,909.278Zm204.758,10.838-5.421,69.253-89.31-24.851Zm99.345-65.556L770.1,866.86,731.28,832.826Zm54.258-68.247.306,50.551-43.991,8.423Zm11.026-86.086L935.091,601.5l-51.025,85.987Zm31.03-88.874,25.391-30.034L938.5,587.125ZM892.913,416.432l22.8-55.057,29.541,121.151Zm-48.5-116.679,16.016-28.046,48.272,75.26Zm-2.781-3.195-47.377-68.583,63.395,40.533ZM682.663,151.566l47.963-5.118,48.519,67.091Zm-97.386-2.817-26.42-45L666.85,148.242Zm-100.391-8.855-21.274-37.358,83.329-.831Zm-4.231.654L375.7,136.607l83.62-33.531ZM268.53,227.358l37.583-81.6,54.505-4.983Zm-6.752,5.1-34.356-33.092,72.507-49.743Zm-2.576,3.073L173.78,290.064l50.935-87.749ZM162.637,377.06l-42.871-23.112,45.926-52.211Zm-1.352,3.815-52.069,56.274,9.041-79.47ZM102.452,538.688,80.437,556.752l23.708-95.775Zm.552,4.721,22.47,141.369L79.426,562.755ZM172.58,711.768,170,778.2l-35.978-71.855Zm44.212,85.024,36.547,62.994L178.12,789.333Zm154.166,104.4-1.589,36.491-95.92-62.056Zm3.936,1.559L424.67,926.09l-51.341,12.594Zm132.589,11.612,1.546,51.183-72.347-37.958Zm114.346-30.607-29.344,69.283-77.6-40.473Zm4.6-.6,89.237,24.831L597.136,952.314Zm103.254-46.414,37.809,33.143-43.023,33.462Zm102.6-70.976,43.588-8.346-95.528,99.551Zm51.33-19.316-.3-49.273,40.209-24.635Zm3.535-56.312,48.806-82.248-8.976,57.845Zm62.239-177.616L955.073,573l-12.9,17.866Zm-29.641-151.5L930.9,371.514l11.155,81Zm-56.53-92.392,10.495-5.279L904.045,332.3Zm-73.417-48.261,18.42-26.442L853.3,260.981Zm-2.86-2.891-48.02-66.4,66.465,39.923ZM678.782,147.957l-.3-28.867,46.256,23.963Zm-4.008-.777-105.72-43.558L674.47,117.539ZM465.645,98.516l16.834-14.481,55.812,13.757Zm-91.919,34.571,32.729-35.352,45.258,4.079Zm-8.455,3.244-49.561,4.531,81.374-38.893ZM228.979,193.446l5.482-12.3,45.483-22.668Zm-59.7,96.406-3.308-32.167,49.473-47.365Zm-3.438,5.661-41.026,46.64,37.8-78.029ZM104.718,441.292l-26.189,4.886,34.594-78.765Zm-.868,4.232L78.795,546.741,77.2,450.495ZM126,697.508,96.227,672.936,81.967,580.817Zm41.751,85.141-19.019-6.278-12.723-57.116Zm81.285,78.591-63.187-34.493-10.142-34.19Zm11.525,10.849,27.359,34.974L204.3,841.372ZM270.8,878.68l86.987,56.277-56.923-17.844Zm159.588,50.127,67.6,35.469L380.466,941.052Zm81.126-13.484,75.677,39.468-74.129,11.787Zm200.875-1.839-30.723,26.578-69.128,10.763Zm56.784-39.836,2.559,16.582-39.715,12.317ZM872.218,767l-40.85,72.762-51.974,23.971Zm11.388-12.127,27.355-50.658-29.238,69.49Zm57.811-156.126,10.326-14.3-16.6,54.736Zm9.937-107.962h0l0-.034,0-.017-.005-.043c0-.009,0-.017,0-.025l-.146-1.061,1.028-13.561,1.048,35.115Zm-2.746-19.968-10.335-75.052,12.8,42.582ZM917.869,353.778l-18.859-42.856L925.4,360.859Zm-56.864-88.509-37.662-56.006,48.045,50.783ZM735.191,144.18l2.518-1.825,28.954,20.729Zm-3.8-2.187L711.814,131.85l21.757,8.563ZM566.13,99.2l36.686-7.56,55.206,19.691Zm-11.968-1.618L504.207,85.271l83.762,5.346Zm-94.635.919L420.56,94.991l54.52-9.867Zm-137.5,34.907,22.293-16.754,35.94-11.079Zm-18.3,8.75L284.391,151.8l29.729-17.447ZM222.6,197.933l-22.939,21.961,26.229-29.341ZM115.153,352.92l-10.2,3.788,39.624-64.534Zm-13.288,9.2,11.007-4.088L85.041,421.4ZM74.46,526.554l.583,35.27L73,568.237Zm1.914,44.282,10.036,64.83-13.834-52.9Zm52.353,134.108L141.615,762.8l-41.134-81.17Zm41.351,82.685,8.948,30.163-26.641-36ZM369.911,942.949l3.732,5.967-39.1-17.055Zm10.039,8.508-4.59-7.337,85.058,16.809Zm208.355,7.208-8.943,10.151-46.694-1.3Zm6.2-.983,55.659-8.666-63.932,18.056ZM723.112,909.5l32.642-10.123-57.56,31.679Zm52.287-21.72-2.563-16.61,47.7-22ZM878.713,763.6l-2.232,22.323-31.37,37.528Zm51.646-95.175,8.061-3.894-36.892,72.418Zm10.734-9.628-9.047,4.37,18.188-59.97Z" style="fill: #58c1ff;fill-opacity: 0.30000000000000004"/>
    </g>
    <g id="Fox">
      <path d="M116.357,728.467c51.475,39.42,209.8,68.076,396.9,68.076,187.036,0,345.085-28.617,396.959-67.945" style="fill: none;stroke: #f9f9fa;stroke-linecap: round;stroke-miterlimit: 10;stroke-width: 6px"/>
      <path d="M806.6,93.558c-23.947,27.884-35.1,90.639-10.818,154.256s61.5,49.8,84.7,114.673c30.623,85.6,16.369,200.589,16.369,200.589s36.814,106.609,62.468-6.633C1016.1,343.74,806.6,145.937,806.6,93.558Z" style="fill: url(#linear-gradient)"/>
      <g>
        <path d="M846.993,805.636a246.888,246.888,0,0,1-30.123,18.183,704,704,0,0,0,38.3-62.961c9.46-10.47,18.126-20.648,25.188-31.653,3.437-5.407,7.313-12.079,11.416-19.819,24.921-44.9,52.4-117.563,53.179-192.2l0-.18c.018-1.837.029-3.675.014-5.515a257.6,257.6,0,0,0-5.712-55.748c.2,1.431.376,2.861.556,4.291-.221-1.1-.407-2.212-.644-3.314.366,2.034.66,4,.976,5.983,5.094,43.217,1.466,85.372-16.682,116.45-.286.454-.58.879-.869,1.323,9.41-47.234,12.558-99.387,2.088-151.6,0,0-4.187-25.383-35.376-102.441-17.952-44.353-49.833-80.716-78-107.207-24.69-30.546-47.114-51.045-59.475-64.06C726,127.971,715.179,107.591,710.735,94.293c-3.853-1.932-53.135-49.807-57.05-51.631C632.174,76.015,564.523,180.337,596.7,277.809c14.584,44.172,51.467,90.017,90.067,115.736,1.695,1.936,22.969,25.042,33.086,77.157,10.453,53.848,4.958,95.862-16.543,157.989-25.286,54.505-90.067,108.388-150.724,113.9-129.672,11.782-177.145-65.107-177.145-65.107,46.336,18.525,97.568,14.65,128.724-4.556,31.4-19.425,50.394-33.827,65.81-28.148,15.2,5.653,27.3-10.756,16.442-27.767a78.5,78.5,0,0,0-79.4-34.573c-31.433,5.109-60.226,30.009-101.412,5.894a85.528,85.528,0,0,1-7.729-5.062c-2.715-1.785,8.826,2.717,6.133.688-8.015-4.354-22.2-13.842-25.88-17.217-.613-.562,6.219,2.179,5.606,1.616-38.51-31.712-33.7-53.134-32.49-66.57.969-10.749,7.964-24.523,19.751-30.11,5.693,3.107,9.24,5.479,9.24,5.479s-2.428-4.961-3.741-7.576c.46-.2.9-.147,1.358-.342,4.664,2.251,14.977,8.1,20.407,11.667,7.072,4.988,9.326,9.436,9.326,9.436s1.862-1.025.485-5.375c-.5-1.784-2.647-7.453-9.652-13.174l.438.049a81.2,81.2,0,0,1,11.867,8.241c1.975-7.181,5.527-14.679,4.746-28.092-.477-9.431-.257-11.873-1.919-15.515-1.488-3.128.834-4.348,3.418-1.1a32.826,32.826,0,0,0-2.208-7.4l.022-.244c3.225-11.236,68.248-40.461,72.981-43.877a67.355,67.355,0,0,0,19.132-20.8c3.617-5.763,6.337-13.848,7-26.112.355-8.844-3.759-14.734-69.507-21.618-17.981-1.771-28.531-14.8-34.526-26.823-1.094-2.59-2.209-4.937-3.325-7.282a58,58,0,0,1-2.558-8.431c10.749-30.873,28.81-56.977,55.367-76.7,1.447-1.318-5.782.338-4.336-.98,1.687-1.538,12.706-5.977,14.787-6.977,2.542-1.2-10.882-6.9-22.732-5.512-12.069,1.36-14.632,2.8-21.073,5.531,2.673-2.661,11.173-6.149,9.18-6.126-13.011,1.995-29.179,9.558-43,18.124A10.731,10.731,0,0,1,393,195.8c-6.441,2.732-22.26,13.788-26.865,23.142a44.329,44.329,0,0,0,.266-5.4,84.483,84.483,0,0,0-13.193,13.819l-.241.219c-37.358-15.05-70.233-16.024-98.053-9.276-6.086-6.112-9.059-1.644-22.907-32.069-.941-1.832.722,1.809,0,0-2.278-5.9,1.389,7.872,0,0-23.284,18.372-53.92,39.193-68.626,53.89-.182.586,17.156-4.9,0,0-6.008,1.716-5.6,5.281-6.513,37.5-.22,2.443-.025,5.179-.223,7.378-11.748,14.968-19.749,27.643-22.775,34.211-15.193,26.18-31.926,66.992-48.146,131.551a334.364,334.364,0,0,1,25.784-50.407c-13.488,34.267-26.527,88.078-29.134,170.945a483.614,483.614,0,0,1,12.533-50.66,473.038,473.038,0,0,0,34.74,201.061c9.329,22.821,24.76,57.455,51.029,95.4,82.533,86.839,198.945,140.9,327.852,140.9C643.109,958.012,763.953,899.133,846.993,805.636Z" style="fill: url(#linear-gradient-2)"/>
        <path d="M846.993,805.636a246.888,246.888,0,0,1-30.123,18.183,704,704,0,0,0,38.3-62.961c9.46-10.47,18.126-20.648,25.188-31.653,3.437-5.407,7.313-12.079,11.416-19.819,24.921-44.9,52.4-117.563,53.179-192.2l0-.18c.018-1.837.029-3.675.014-5.515a257.6,257.6,0,0,0-5.712-55.748c.2,1.431.376,2.861.556,4.291-.221-1.1-.407-2.212-.644-3.314.366,2.034.66,4,.976,5.983,5.094,43.217,1.466,85.372-16.682,116.45-.286.454-.58.879-.869,1.323,9.41-47.234,12.558-99.387,2.088-151.6,0,0-4.187-25.383-35.376-102.441-17.952-44.353-49.833-80.716-78-107.207-24.69-30.546-47.114-51.045-59.475-64.06C726,127.971,715.179,107.591,710.735,94.293c-3.853-1.932-53.135-49.807-57.05-51.631C632.174,76.015,564.523,180.337,596.7,277.809c14.584,44.172,51.467,90.017,90.067,115.736,1.695,1.936,22.969,25.042,33.086,77.157,10.453,53.848,4.958,95.862-16.543,157.989-25.286,54.505-90.067,108.388-150.724,113.9-129.672,11.782-177.145-65.107-177.145-65.107,46.336,18.525,97.568,14.65,128.724-4.556,31.4-19.425,50.394-33.827,65.81-28.148,15.2,5.653,27.3-10.756,16.442-27.767a78.5,78.5,0,0,0-79.4-34.573c-31.433,5.109-60.226,30.009-101.412,5.894a85.528,85.528,0,0,1-7.729-5.062c-2.715-1.785,8.826,2.717,6.133.688-8.015-4.354-22.2-13.842-25.88-17.217-.613-.562,6.219,2.179,5.606,1.616-38.51-31.712-33.7-53.134-32.49-66.57.969-10.749,7.964-24.523,19.751-30.11,5.693,3.107,9.24,5.479,9.24,5.479s-2.428-4.961-3.741-7.576c.46-.2.9-.147,1.358-.342,4.664,2.251,14.977,8.1,20.407,11.667,7.072,4.988,9.326,9.436,9.326,9.436s1.862-1.025.485-5.375c-.5-1.784-2.647-7.453-9.652-13.174l.438.049a81.2,81.2,0,0,1,11.867,8.241c1.975-7.181,5.527-14.679,4.746-28.092-.477-9.431-.257-11.873-1.919-15.515-1.488-3.128.834-4.348,3.418-1.1a32.826,32.826,0,0,0-2.208-7.4l.022-.244c3.225-11.236,68.248-40.461,72.981-43.877a67.355,67.355,0,0,0,19.132-20.8c3.617-5.763,6.337-13.848,7-26.112.355-8.844-3.759-14.734-69.507-21.618-17.981-1.771-28.531-14.8-34.526-26.823-1.094-2.59-2.209-4.937-3.325-7.282a58,58,0,0,1-2.558-8.431c10.749-30.873,28.81-56.977,55.367-76.7,1.447-1.318-5.782.338-4.336-.98,1.687-1.538,12.706-5.977,14.787-6.977,2.542-1.2-10.882-6.9-22.732-5.512-12.069,1.36-14.632,2.8-21.073,5.531,2.673-2.661,11.173-6.149,9.18-6.126-13.011,1.995-29.179,9.558-43,18.124A10.731,10.731,0,0,1,393,195.8c-6.441,2.732-22.26,13.788-26.865,23.142a44.329,44.329,0,0,0,.266-5.4,84.483,84.483,0,0,0-13.193,13.819l-.241.219c-37.358-15.05-70.233-16.024-98.053-9.276-6.086-6.112-9.059-1.644-22.907-32.069-.941-1.832.722,1.809,0,0-2.278-5.9,1.389,7.872,0,0-23.284,18.372-53.92,39.193-68.626,53.89-.182.586,17.156-4.9,0,0-6.008,1.716-5.6,5.281-6.513,37.5-.22,2.443-.025,5.179-.223,7.378-11.748,14.968-19.749,27.643-22.775,34.211-15.193,26.18-31.926,66.992-48.146,131.551a334.364,334.364,0,0,1,25.784-50.407c-13.488,34.267-26.527,88.078-29.134,170.945a483.614,483.614,0,0,1,12.533-50.66,473.038,473.038,0,0,0,34.74,201.061c9.329,22.821,24.76,57.455,51.029,95.4,82.533,86.839,198.945,140.9,327.852,140.9C643.109,958.012,763.953,899.133,846.993,805.636Z" style="fill: url(#linear-gradient-3)"/>
      </g>
      <path d="M747.417,868.708c162.87-18.86,234.969-186.7,142.381-190-83.633-2.673-219.527,198.9-142.381,190Z" style="fill: url(#linear-gradient-4)"/>
      <path d="M901.524,644.424c112.056-65.214,82.839-206.08,82.839-206.08s-43.247,50.239-72.625,130.321C882.713,647.963,834.155,683.812,901.524,644.424Z" style="fill: url(#linear-gradient-5)"/>
      <path d="M545.792,952.062c156.227,49.851,290.5-73.22,207.733-114.32C678.274,800.676,471.642,928.49,545.792,952.062Z" style="fill: url(#linear-gradient-6)"/>
      <path d="M674.9,23.42c.347-.379.531-.573.531-.573A1.784,1.784,0,0,0,674.9,23.42Zm238.27,680.814c3.8-5.363,8.94-22.535,13.48-30.211C954.228,629.5,954.423,594,954.423,593.187c16.659-83.219,15.148-117.2,4.9-180.043-8.251-50.6-44.322-123.092-75.57-158-32.2-35.965-9.514-24.247-40.692-50.518-27.327-30.293-53.823-60.291-68.253-72.355C670.533,45.094,672.888,26.577,674.9,23.42c-.339.371-.837.922-1.472,1.643C672.189,20.14,671.318,16,671.318,16s-57,57-69,152c-7.834,62.021,15.383,126.676,49,168a381.367,381.367,0,0,0,59,58l0-.026c25.4,36.477,39.38,81.489,39.38,129.909,0,121.244-98.341,219.531-219.652,219.531A220.455,220.455,0,0,1,480.91,737.9c-57.236-10.908-90.288-39.781-106.772-59.382-9.447-11.234-13.464-19.423-13.464-19.423,51.283,18.371,107.986,14.528,142.468-4.518,34.749-19.265,55.774-33.546,72.837-27.915,16.82,5.606,30.211-10.667,18.2-27.536-11.771-16.845-42.4-40.97-87.878-34.286-34.789,5.067-66.657,29.76-112.24,5.845a96.313,96.313,0,0,1-8.554-5.02c-3.005-1.771,9.768,2.695,6.787.682-8.87-4.318-24.573-13.728-28.643-17.074-.679-.557,6.883,2.161,6.2,1.6-42.621-31.449-37.3-52.693-35.959-66.018,1.073-10.66,8.814-24.32,21.859-29.86,6.3,3.081,10.226,5.433,10.226,5.433s-2.688-4.92-4.141-7.513c.509-.194.994-.145,1.5-.339,5.162,2.232,16.576,8.029,22.586,11.57,7.827,4.947,10.322,9.358,10.322,9.358s2.061-1.016.536-5.33c-.557-1.769-2.929-7.391-10.682-13.065l.485.049a90.851,90.851,0,0,1,13.134,8.173c2.186-7.121,6.118-14.557,5.253-27.859-.528-9.352-.284-11.775-2.124-15.386-1.647-3.1.924-4.312,3.782-1.088a30.038,30.038,0,0,0-2.444-7.343l.025-.242c3.57-11.143,75.535-40.125,80.773-43.513a70.31,70.31,0,0,0,21.175-20.63c4-5.715,7.014-13.733,7.748-25.895.246-5.48-1.441-9.817-20.5-13.965-11.439-2.49-29.136-4.913-56.433-7.474-19.9-1.756-31.577-14.676-38.212-26.6-1.211-2.569-2.445-4.9-3.68-7.222a53.108,53.108,0,0,1-2.831-8.36,158.47,158.47,0,0,1,61.279-76.063c1.6-1.308-6.4.335-4.8-.972,1.868-1.525,14.063-5.927,16.367-6.919,2.813-1.186-12.044-6.84-25.159-5.466-13.357,1.349-16.194,2.776-23.323,5.486,2.959-2.639,12.366-6.1,10.161-6.075-14.4,1.978-32.3,9.479-47.6,17.973a9.676,9.676,0,0,1,.924-4.312c-7.129,2.709-24.637,13.674-29.734,22.95A39.488,39.488,0,0,0,350.67,199a88.678,88.678,0,0,0-14.6,13.7l-.267.218c-41.347-14.925-77.732-15.891-108.522-9.2-6.736-6.061-17.566-15.226-32.892-45.4-1.041-1.817-1.6,3.755-2.4,1.961-5.955-13.813-9.553-36.444-8.962-52.046,0,0-12.318,5.614-22.513,29.06-1.893,4.215-3.107,6.54-4.32,8.865-.558.678,1.265-7.7.974-7.244-1.772,3-6.356,7.192-8.371,12.618-1.384,4.021-3.325,6.273-4.564,11.288l-.291.46c-.1-1.478.367-6.081.027-5.137a236.1,236.1,0,0,0-12.308,29.841c-5.489,18.046-11.879,42.61-12.892,74.565-.244,2.423-.027,5.136-.247,7.317-13,14.844-21.858,27.413-25.207,33.928C76.5,329.762,57.978,370.236,40.025,434.259A319.143,319.143,0,0,1,68.562,384.27C53.634,418.253,39.2,471.618,36.318,553.8a438.522,438.522,0,0,1,13.872-50.24c-2.578,54.8,3.805,122.74,38.449,199.393,20.57,45.087,67.921,136.649,183.62,208.1l-.01.007s39.36,29.3,107.017,51.259c5,1.814,10.065,3.6,15.234,5.333-1.622-.655-3.191-1.344-4.715-2.052a484.883,484.883,0,0,0,138.8,20.4c175.18.149,226.846-70.2,226.846-70.2s-.182.137-.513.377q3.708-3.491,7.143-7.257c-27.639,26.1-90.715,27.832-114.3,25.949,40.223-11.813,66.691-21.806,118.175-41.519q9.032-3.355,18.478-7.645c.681-.3,1.368-.625,2.052-.936,1.249-.582,2.486-1.132,3.747-1.744a349.205,349.205,0,0,0,70.262-43.987c51.7-41.3,62.949-81.557,68.829-108.1-.82,2.544-3.375,8.475-5.17,12.316-13.315,28.482-42.841,45.959-74.908,60.948a688.993,688.993,0,0,0,42.384-62.438C902.085,731.39,905.351,715.148,913.166,704.234Z" style="fill: url(#radial-gradient)"/>
      <path d="M850.239,802.982c21.08-23.241,40.01-49.824,54.349-79.968,36.9-77.582,93.952-206.58,49.018-341.306C918.092,275.225,869.333,217,807.424,160.122,706.881,67.756,678.8,26.521,678.8,2c0,0-116.089,129.409-65.742,264.381S766.52,396.405,834.737,537.255C915.011,703,769.788,883.869,649.693,934.493c7.353-1.633,266.97-60.383,280.614-208.882C930,728.342,924.148,769.414,850.239,802.982Z" style="fill: url(#linear-gradient-7)"/>
      <path d="M513.91,321.373c.393-8.77-4.156-14.66-76.682-21.463-29.839-2.755-41.265-30.334-44.748-41.941-10.61,27.565-14.991,56.49-12.636,91.477,1.61,22.92,17.009,47.521,24.37,61.965,0,0,1.636-2.126,2.391-2.912,13.861-14.431,71.936-36.419,77.392-39.541C490.011,365.159,512.9,348.435,513.91,321.373Z" style="fill: url(#linear-gradient-8)"/>
      <path d="M513.91,321.373c.393-8.77-4.156-14.66-76.682-21.463-29.839-2.755-41.265-30.334-44.748-41.941-10.61,27.565-14.991,56.49-12.636,91.477,1.61,22.92,17.009,47.521,24.37,61.965,0,0,1.636-2.126,2.391-2.912,13.861-14.431,71.936-36.419,77.392-39.541C490.011,365.159,512.9,348.435,513.91,321.373Z" style="fill: url(#linear-gradient-9)"/>
      <path d="M194.632,158.471c-1.041-1.818-1.6,3.754-2.4,1.961-5.955-13.813-9.578-36.2-8.72-52.022,0,0-12.318,5.614-22.513,29.06-1.893,4.215-3.107,6.54-4.32,8.865-.558.678,1.265-7.7.974-7.244-1.771,3-6.355,7.192-8.346,12.375-1.651,4.239-3.349,6.515-4.613,11.772-.389,1.429.392-6.323.052-5.379-23.711,45.825-28.237,115.114-25.738,112.184,50.478-53.9,108.325-66.683,108.325-66.683-6.155-4.534-19.528-17.625-32.7-44.889Z" style="fill: url(#linear-gradient-10)"/>
      <path d="M386.162,722.1c-69.724-29.769-148.992-71.755-146-167.138C244.235,429.347,357.3,454.175,357.3,454.175c-4.267,1.039-15.675,9.162-19.718,17.82-4.273,10.827-12.069,35.276,11.551,60.9,37.089,40.192-76.2,95.356,98.663,199.567,4.41,2.4-40.991-1.43-61.639-10.358Z" style="fill: url(#linear-gradient-11)"/>
      <path d="M361.391,659.5c49.442,17.207,107.04,14.189,141.523-4.858,23.085-12.851,52.7-33.434,70.922-28.353-15.777-6.236-27.726-9.15-42.1-9.862-2.448,0-5.382-.052-8.048-.32a136.228,136.228,0,0,0-15.757.863c-8.9.818-18.766,6.433-27.735,5.532-.485-.049,8.7-3.774,7.953-3.605-4.752.991-9.915,1.205-15.37,1.881-3.467.386-6.449.82-9.891.963-102.982,8.733-190.017-55.813-190.017-55.813-7.408,24.95,33.166,74.3,88.521,93.572Z" style="fill: url(#linear-gradient-12)"/>
      <path d="M850.055,803.646c104.164-102.269,156.858-226.585,134.584-366.018,0,0,8.9,71.5-24.848,144.628,16.213-71.393,18.1-160.114-24.975-251.988C877.323,207.636,782.768,143.109,746.669,116.189,691.974,75.4,669.3,33.87,668.892,25.3c-16.339,33.477-65.766,148.2-5.31,247,56.64,92.564,145.865,120.039,208.328,204.947C986.984,633.667,850.055,803.646,850.055,803.646Z" style="fill: url(#linear-gradient-13)"/>
      <path d="M835.132,537.551c-36.4-75.2-81.837-108-124.814-143.551,5,7,6.232,9.473,9,14,37.827,40.316,93.607,138.656,53.11,262.107C696.2,902.482,391.284,793.057,359.318,762.319c12.932,134.489,238.036,198.845,384.6,111.63C827.318,795,894.786,660.788,835.132,537.551Z" style="fill: url(#linear-gradient-14)"/>
    </g>
    <g id="Fox_Lines_-_Outlined" data-name="Fox Lines - Outlined">
      <path d="M850.438,688.38l.941-4.853c.318-1.746.752-3.6,1.024-5.605.606-4,1.381-8.467,1.868-13.429a237.936,237.936,0,0,0,1.478-34.99,223.032,223.032,0,0,0-5.827-43.81A232.044,232.044,0,0,0,832.5,536.941a336.9,336.9,0,0,0-29.593-49.806c-5.8-8.242-12.31-16.176-19.092-24.059-6.877-7.808-14.231-15.386-21.916-22.794-7.746-7.347-15.838-14.5-24.144-21.541s-16.845-13.95-25.358-20.984-17.047-14.138-25.3-21.609-16.227-15.277-23.726-23.509a245.976,245.976,0,0,1-20.751-26.026l-2.351-3.413c-.792-1.131-1.568-2.271-2.279-3.453l-4.373-7c-1.44-2.339-2.718-4.769-4.08-7.142l-2.01-3.575c-.657-1.2-1.24-2.431-1.862-3.643L622,291.1c-1.143-2.459-2.225-4.939-3.336-7.4-2.307-4.883-4.125-9.939-6.129-14.883l-.736-1.859-.64-1.9-1.266-3.786c-.831-2.524-1.708-5.024-2.491-7.543l-2-7.632-.995-3.8c-.358-1.257-.528-2.553-.8-3.824a212.571,212.571,0,0,1-3.8-59.827A261.122,261.122,0,0,1,610,125.2a310.659,310.659,0,0,1,16.986-44.062,372,372,0,0,1,18.166-33.806A366.158,366.158,0,0,1,669.837,11.36c2.556-3.267,5.114-6.433,7.656-9.355A.951.951,0,0,1,678.8,2a.946.946,0,0,1,.17,1.29c-2.425,2.812-4.981,5.991-7.554,9.3A375.787,375.787,0,0,0,628.4,83.345a308.148,308.148,0,0,0-16.536,43.944,256.434,256.434,0,0,0-9.661,52.953,213.065,213.065,0,0,0,4.268,58.875c.127.626.239,1.257.379,1.881l.493,1.858.989,3.727c.682,2.483,1.275,5,2.034,7.479l2.495,7.4,1.253,3.716c.407,1.244.952,2.446,1.422,3.674l2.936,7.354.736,1.845.817,1.813,1.64,3.632,1.643,3.641c.552,1.213,1.076,2.442,1.7,3.622l3.633,7.164a248.018,248.018,0,0,0,37.41,53.145c7.427,8.06,15.325,15.719,23.5,23.066s16.657,14.361,25.129,21.328,17,13.823,25.316,20.839,16.438,14.157,24.229,21.508c7.73,7.416,15.15,15.005,22.086,22.845,6.855,7.9,13.43,15.88,19.3,24.155a340.258,340.258,0,0,1,29.978,50.05l.789,1.548.724,1.578,1.437,3.142c.941,2.094,1.933,4.148,2.814,6.232l2.513,6.255,1.242,3.092c.391,1.035.734,2.083,1.1,3.117l2.148,6.171c.373,1.014.682,2.044.979,3.073l.908,3.067c.592,2.039,1.226,4.047,1.767,6.061l1.479,6.02a227.372,227.372,0,0,1,6.136,44.563,242.006,242.006,0,0,1-1.365,35.77c-1.056,10.174-2.693,18.441-3.917,24.655a283.86,283.86,0,0,1-9.883,35.225,300.4,300.4,0,0,1-12.738,30.909c-4.586,9.513-9.213,18.33-13.967,26.258l-1.73,2.965-1.775,2.86-3.452,5.539c-2.344,3.555-4.565,6.968-6.771,10.151-4.418,6.363-8.568,11.946-12.3,16.734l-5.268,6.593-4.568,5.375-1.947,2.263L778.185,836q-1.643,1.787-2.789,2.984c-1.524,1.591-2.366,2.373-2.47,2.282s.574-1.072,1.944-2.878c1.349-1.824,3.493-4.387,6.079-7.843,5.3-6.8,12.8-16.839,21.331-29.834,2.081-3.282,4.3-6.7,6.511-10.335,2.172-3.657,4.5-7.426,6.7-11.457,4.553-7.977,9.037-16.744,13.383-26.173,1.056-2.371,2.2-4.74,3.226-7.2l3.121-7.458c1.97-5.083,4.011-10.275,5.8-15.674A292.746,292.746,0,0,0,850.438,688.38Z" style="fill: url(#linear-gradient-15)"/>
      <path d="M775.678,651.742c.525-2.058,1.324-4.8,2.025-8.215.723-3.409,1.74-7.406,2.486-12,.4-2.293.869-4.713,1.287-7.264l1.115-8.035.294-2.115.217-2.164.436-4.446c.139-1.508.318-3.037.432-4.595l.275-4.747a261.509,261.509,0,0,0-.931-41.929l-.269-2.785-.366-2.787-.751-5.61c-.23-1.881-.615-3.744-.916-5.627-.334-1.877-.614-3.77-1-5.647l-1.17-5.638c-.2-.94-.38-1.885-.6-2.823l-.684-2.805-1.369-5.615-1.555-5.563a271.656,271.656,0,0,0-15.87-42.078,267.354,267.354,0,0,0-19.655-34.4c-3.273-4.919-6.585-9.3-9.61-13.256-3.1-3.9-5.95-7.337-8.536-10.2l-1.863-2.062-.454-.5a1.61,1.61,0,0,1-.3-.4l-.176-.292-.644-1.076-2.227-3.748c-1.317-2.179-2.34-3.714-2.924-4.591-.8-1.192-1.6-2.354-2.365-3.449a.964.964,0,0,1,.3-1.269.932.932,0,0,1,1.3.138c.768,1.065,1.595,2.238,2.427,3.463.653.963,1.751,2.589,3.138,4.852l2.31,3.817.641,1.048a.5.5,0,0,0,.089.113l.224.244.456.5,1.924,2.1a221.734,221.734,0,0,1,18.546,23.673,269.509,269.509,0,0,1,20.016,34.533A273.9,273.9,0,0,1,777.635,510.7l1.6,5.588,1.422,5.641.71,2.819.618,2.839,1.222,5.669c.391,1.889.693,3.792,1.043,5.681.317,1.894.721,3.769.964,5.662l.8,5.648.389,2.807.292,2.805a264.108,264.108,0,0,1,1.259,42.321,258.544,258.544,0,0,1-3.874,33.841c-.737,4.681-1.735,8.764-2.47,12.256-.706,3.5-1.511,6.328-2.062,8.471l-1.664,6.3-1.859,5.987-1.8,5.717q-.945,2.77-1.9,5.375c-2.542,6.942-5.144,12.947-7.488,17.841-4.71,9.788-8.325,15.135-8.773,14.889-.492-.262,2.331-6.262,6.348-16.336,2-5.041,4.285-11.106,6.575-17.983.569-1.72,1.15-3.487,1.714-5.305l1.658-5.6,1.736-5.819Z" style="fill: url(#linear-gradient-16)"/>
      <path d="M753.208,370.436c-2.346-1.893-5.545-4.369-9.346-7.543l-6.236-5.144c-2.238-1.857-4.578-3.923-7.092-6.074a403.8,403.8,0,0,1-34.4-33.243,293.345,293.345,0,0,1-35.565-47.974c-1.311-2.31-2.658-4.617-3.957-6.963l-3.62-7.224-.909-1.815c-.286-.613-.546-1.24-.82-1.86l-1.627-3.737L648,245.112c-.513-1.263-.967-2.552-1.453-3.829l-1.438-3.844c-.488-1.28-.968-2.563-1.356-3.879l-2.485-7.85-2.1-7.968c-.373-1.322-.66-2.664-.935-4.009l-.852-4.025a224.951,224.951,0,0,1-4.1-32.305,249.481,249.481,0,0,1,.42-31.434,280.1,280.1,0,0,1,3.724-29.265c1.689-9.273,3.706-18.017,5.856-26.112a361.979,361.979,0,0,1,13.283-39.912c4.068-10.167,7.256-16.973,8.513-19.686.861-1.846,1.724-3.65,2.561-5.35a.956.956,0,0,1,1.255-.349.94.94,0,0,1,.505,1.2c-.794,1.653-1.646,3.465-2.507,5.339-1.349,2.949-4.544,9.907-8.561,20.149A362.639,362.639,0,0,0,645.38,91.9a298.354,298.354,0,0,0-9.038,54.862,252.1,252.1,0,0,0-.18,30.955,222.011,222.011,0,0,0,4.274,31.683l.857,3.941.428,1.971.523,1.949,2.108,7.79,2.475,7.672c.385,1.287.88,2.534,1.357,3.786l1.428,3.754c.486,1.246.926,2.509,1.448,3.736l1.617,3.657,1.611,3.646c.273.6.524,1.219.815,1.813l.9,1.77,3.578,7.046,3.909,6.788A290.41,290.41,0,0,0,698.7,315.745a402.744,402.744,0,0,0,34.173,32.775c2.511,2.134,4.854,4.177,7.09,6.035l6.264,5.135c3.824,3.175,7.068,5.683,9.469,7.618l26.814,21.21c8.079,6.44,15.148,12.246,20.915,17.2l7.67,6.718c2.2,2.013,4.05,3.777,5.545,5.219,2.985,2.892,4.512,4.557,4.328,4.765s-2.1-1.138-5.468-3.729l-14.229-11.116-21.461-16.5C771.668,384.835,762.635,377.95,753.208,370.436Z" style="fill: url(#linear-gradient-17)"/>
      <path d="M962.046,403.463c-.5-1.867-1.026-4.069-1.726-6.545-.725-2.469-1.492-5.249-2.421-8.28l-1.513-4.734c-.517-1.649-1.07-3.357-1.705-5.1l-3.963-11.237-2.174-6.141-2.5-6.4-2.617-6.707c-.915-2.277-1.748-4.643-2.8-6.977l-6.3-14.628c-2.289-5-4.764-10.1-7.247-15.382l-.94-1.983-1.018-1.961-2.057-3.96-4.19-8.063c-.686-1.37-1.453-2.707-2.222-4.051l-2.307-4.061-4.684-8.242-5.077-8.209-2.572-4.153c-.434-.692-.853-1.4-1.3-2.083l-1.376-2.046c-3.7-5.447-7.305-11.076-11.365-16.472-1.995-2.722-3.957-5.49-6.006-8.213l-6.325-8.084c-1.064-1.347-2.1-2.719-3.194-4.053l-3.324-3.968-6.689-7.955-6.945-7.785c-1.167-1.292-2.3-2.615-3.5-3.885l-3.59-3.8c-2.4-2.527-4.77-5.092-7.2-7.589L833.84,183.3c-2.431-2.493-4.95-4.89-7.448-7.3s-4.975-4.831-7.5-7.191L803.8,154.829c-10-9.2-19.769-18.32-29.116-27.287-18.7-17.928-35.792-35.19-50.028-50.993C710.4,60.776,699.1,46.356,691.8,34.591a104.876,104.876,0,0,1-8.026-15.281,54.814,54.814,0,0,1-2.995-9.2,36.256,36.256,0,0,1-.969-7.279A1,1,0,0,0,678.8,2a.9.9,0,0,0-.946.886,37.714,37.714,0,0,0,.974,7.666,57.41,57.41,0,0,0,3.22,9.944,108.052,108.052,0,0,0,8.333,15.827A232.761,232.761,0,0,0,704.4,56.144c5.573,7.129,12,14.724,19.12,22.677,14.242,15.909,31.271,33.227,49.859,51.193,9.29,8.989,18.989,18.121,28.923,27.332L817.23,171.3l7.393,7.168c2.472,2.392,4.944,4.793,7.342,7.271l7.263,7.378,7.09,7.547,3.536,3.778c1.164,1.271,2.29,2.576,3.435,3.862l6.834,7.727,6.572,7.894,3.268,3.935c1.065,1.327,2.091,2.683,3.135,4.019l6.214,8.011c2.01,2.7,3.938,5.439,5.9,8.136,3.987,5.345,7.523,10.918,11.157,16.311,1.852,2.675,3.455,5.483,5.155,8.2l4.987,8.125,4.6,8.161c1.494,2.707,3.072,5.334,4.453,8.03l4.112,7.989,2.02,3.924c.676,1.3,1.338,2.589,1.933,3.9a543.106,543.106,0,0,1,23.284,55.869l3.922,11.225.9,2.581.793,2.5,1.5,4.718c.932,3.021,1.684,5.8,2.411,8.281.706,2.483,1.233,4.7,1.738,6.6l.733,2.834.653,2.827,1.287,5.578c.729,3.708,1.567,7.321,2.159,10.9l.951,5.293.8,5.19c.568,3.412.932,6.764,1.375,10.01,1.6,13.01,2.236,24.605,2.545,34.242.516,19.286-.2,30.754.45,30.8h0c.253.019.709-2.93,1.264-8.34.473-5.413,1.027-13.293,1.087-23.122A307.721,307.721,0,0,0,962.046,403.463Z" style="fill: url(#linear-gradient-18)"/>
      <path d="M613.6,724.777c1.421-.515,3-1.315,4.846-2.12l6.145-2.778,7.212-3.678,1.981-1.009,2-1.142,4.186-2.4c2.914-1.581,5.823-3.539,8.933-5.5,3.15-1.907,6.221-4.243,9.515-6.531,1.662-1.126,3.26-2.4,4.9-3.687l5.009-3.915,4.985-4.276c1.685-1.451,3.415-2.9,5.044-4.508l5.046-4.784c1.686-1.632,3.285-3.393,4.962-5.113l2.509-2.616c.841-.876,1.612-1.826,2.427-2.742l4.883-5.607A243.278,243.278,0,0,0,716.2,637.217a239.966,239.966,0,0,0,14.821-28.689,235.768,235.768,0,0,0,10.5-31.1,233.232,233.232,0,0,0,5.676-32.059c.408-5.347.936-10.644.968-15.9.229-5.252.107-10.452-.031-15.567a237,237,0,0,0-2.934-29.445c-.694-4.681-1.68-9.194-2.584-13.586l-1.553-6.444c-.484-2.122-1.14-4.162-1.686-6.193l-.847-3.011c-.285-.993-.623-1.96-.927-2.927l-1.835-5.674-1.962-5.368-.952-2.592-1.022-2.493-1.965-4.8c-.666-1.543-1.366-3.019-2.016-4.463-.667-1.436-1.27-2.848-1.931-4.174l-1.93-3.829a210.187,210.187,0,0,0-10.726-18.7l-3.293-4.974a1,1,0,0,1,.146-1.114l.435-.159h0a1.032,1.032,0,0,1-.235.048,1.085,1.085,0,0,1-.249,0h0l.451-.165a.916.916,0,0,1,1.064.281l3.361,5a211.37,211.37,0,0,1,11.2,19.336,218.3,218.3,0,0,1,15.714,39.837c1.242,4.068,2.221,8.357,3.31,12.751l1.4,6.742c.251,1.136.462,2.288.647,3.452l.593,3.506A247.212,247.212,0,0,1,750.86,514.4q.088,3.865.177,7.786c.049,1.307.023,2.619-.015,3.936s-.059,2.637-.089,3.962c0,5.3-.554,10.645-.914,16.042a220.44,220.44,0,0,1-16.08,63.8,221.782,221.782,0,0,1-32.917,54.594l-4.908,5.705c-.818.935-1.6,1.891-2.438,2.795l-2.527,2.662c-1.686,1.755-3.31,3.534-4.995,5.213l-5.09,4.874c-.846.793-1.652,1.619-2.515,2.369l-2.571,2.237-5.03,4.373-5.067,4c-1.667,1.3-3.267,2.631-4.952,3.781l-4.88,3.484c-.8.563-1.577,1.144-2.374,1.676l-2.39,1.539c-3.165,2-6.109,4.036-9.077,5.659l-4.267,2.462-2.037,1.175c-.674.374-1.357.706-2.017,1.051l-7.386,3.795c-2.3,1.089-4.432,2.022-6.324,2.889-1.906.835-3.55,1.67-5.036,2.21A205.76,205.76,0,0,1,581.87,739.1c-2.577.562-5.074,1.131-7.5,1.628l-7.089,1.164-3.352.541-3.24.362-6.061.64c-1.919.239-3.758.287-5.486.376l-4.868.216c-3.026.183-5.625.049-7.733,0-4.219-.133-6.5-.339-6.5-.616,0-.258,2.359-.572,6.639-.917,2.14-.177,4.763-.3,7.8-.7l4.872-.526,2.663-.276c.911-.1,1.84-.272,2.8-.409,3.813-.62,8.028-1.075,12.489-2.049l6.944-1.36,7.312-1.778A231.948,231.948,0,0,0,613.6,724.777Z" style="fill: url(#linear-gradient-19)"/>
      <path d="M388.256,264.7c.97-2.739,2.181-5.972,3.6-9.69,1.574-3.657,3.21-7.863,5.467-12.36a162.209,162.209,0,0,1,19.1-30.7A152.2,152.2,0,0,1,451.3,179.519c1.844-1.249,3.77-2.43,5.74-3.6l1.493-.861.538-.307.194-.1.322-.171.095-.045c.122-.034,0-.049-.057-.068l-.1-.027-.416-.093q-3.4-.721-6.931-1.229a125.416,125.416,0,0,0-29.7-.78,112.589,112.589,0,0,0-26.446,5.733,101.37,101.37,0,0,0-13.722,5.986c-4.563,2.423-9.255,5.058-13.985,7.774-4.726,2.739-9.529,5.589-14.281,8.675-1.2.758-2.382,1.545-3.559,2.35-1.191.788-2.361,1.609-3.522,2.448a74.862,74.862,0,0,0-6.733,5.316c-.5.483-1.042.959-1.484,1.458l-.347.371-.393.472c-.094.147-.25.223-.3.421-.045.007-.137-.036-.167-.018l-.095.05-.19.1-.69.372a3.523,3.523,0,0,1-.636.26c-.275.07-.562.328-.863.115l-1.515-.559-2.09-.74c-2.8-.982-5.65-1.882-8.52-2.773q-8.626-2.634-17.614-4.665l-.793-.183-.851-.2a190.2,190.2,0,0,0-29.576-4.161,131.879,131.879,0,0,0-30.216,2.234c-5.034.869-10.041,1.976-15.016,3.222-4.98,1.228-9.911,2.675-14.8,4.234a238.453,238.453,0,0,0-28.573,11.232,209.521,209.521,0,0,0-50.181,32.579A176.689,176.689,0,0,0,115.424,275c-1.532,1.775-2.885,3.674-4.3,5.506-1.438,1.812-2.707,3.751-4.022,5.632l-3.891,5.629c-1.285,1.866-2.6,3.695-3.748,5.612L92.45,308.564l-6.342,11.093c-1.065,1.8-2.016,3.643-2.965,5.463l-2.83,5.4c-.922,1.787-1.885,3.53-2.744,5.3l-2.538,5.258c-3.429,6.89-6.324,13.631-9.142,19.95-2.646,6.394-5.18,12.388-7.29,18.051-2.229,5.618-4.021,10.9-5.758,15.658-1.593,4.806-3.112,9.095-4.269,12.906l-1.665,5.287-1.321,4.465c-.808,2.682-1.385,4.788-1.789,6.25-.966,3.42-1.838,6.8-2.623,10a.969.969,0,0,1-1.149.614.928.928,0,0,1-.756-1.063c.757-3.155,1.62-6.58,2.6-10.082.435-1.6,1.047-3.838,1.88-6.637l1.364-4.633,1.694-5.442c.592-1.953,1.274-4.023,2.01-6.215s1.484-4.52,2.328-6.949c1.729-4.843,3.57-10.185,5.8-15.87,2.115-5.729,4.679-11.772,7.331-18.216l4.35-9.835c.726-1.692,1.521-3.38,2.347-5.078l2.476-5.172,2.54-5.29,2.767-5.322,2.825-5.433c.482-.911.939-1.841,1.448-2.749l1.548-2.729c2.089-3.646,4.148-7.4,6.368-11.14l7-11.26c.576-.963,1.216-1.89,1.86-2.819l1.925-2.805,3.889-5.667,1.964-2.861,2.106-2.8,2.125-2.812c.723-.93,1.493-1.827,2.241-2.746a180.164,180.164,0,0,1,20.133-20.839,203.279,203.279,0,0,1,23.854-18.066,228.83,228.83,0,0,1,26.749-14.8A239.716,239.716,0,0,1,243.61,200c5.085-.9,10.2-1.616,15.338-2.051,1.284-.114,2.569-.213,3.86-.267,1.286-.083,2.578-.122,3.87-.136,2.582-.043,5.14.08,7.7.13a193.275,193.275,0,0,1,29.992,4.121l.863.2.805.183q9.083,2.015,17.827,4.668c2.907.893,5.793,1.8,8.638,2.784l2.13.746.619.221c.11.033.132.063.184.052a.864.864,0,0,1,.1-.143l.279-.342.409-.441c.541-.608,1.11-1.11,1.667-1.657a78.046,78.046,0,0,1,7.012-5.584c1.19-.862,2.382-1.708,3.589-2.512,1.2-.821,2.4-1.629,3.608-2.4,4.81-3.146,9.641-6.03,14.394-8.806,4.773-2.758,9.43-5.391,14.086-7.886a104.253,104.253,0,0,1,14.185-6.249,116.2,116.2,0,0,1,27.363-6.033,129.172,129.172,0,0,1,30.388.7q3.618.507,7.127,1.233c.346.073,1.771.371,2.785.624,1.063.272,2.167.529,3.134.829l2.931.873,1.453.441.3.1.157.052c.049.008.057.021,0,.039l-.107.048-.855.384-3.375,1.527-.837.38c-.283.127-.568.255-.768.367l-1.332.7-1.328.695-.663.347-.319.168-.179.1-1.431.83c-.958.538-1.881,1.129-2.806,1.7s-1.837,1.155-2.728,1.761a148.244,148.244,0,0,0-33.917,31.52,159.277,159.277,0,0,0-18.709,30.047c-2.216,4.436-3.858,8.6-5.409,12.242-1.42,3.7-2.619,6.94-3.6,9.7a230.6,230.6,0,0,0-8.083,29.976,198.206,198.206,0,0,0-3.695,28.119,128.132,128.132,0,0,0,.918,25.089l.473,2.848c.146.942.3,1.873.541,2.786.438,1.829.8,3.635,1.315,5.371.481,1.742.924,3.455,1.463,5.1.516,1.652,1,3.273,1.553,4.828A275.33,275.33,0,0,0,398.3,398.8c3.091,6.561,4.875,10.075,4.631,10.218-.218.129-2.532-3.268-6.142-9.812a213.184,213.184,0,0,1-13.21-28.973c-1.153-3.187-2.267-6.57-3.3-10.148-.52-1.787-.947-3.646-1.438-5.524-.39-1.909-.732-3.874-1.107-5.867a131.259,131.259,0,0,1-1.087-25.759,190.381,190.381,0,0,1,3.588-28.367A227.323,227.323,0,0,1,388.256,264.7Z" style="fill: url(#linear-gradient-20)"/>
      <g style="opacity: 0.25;mix-blend-mode: overlay">
        <path d="M951.385,659.377l2.853-6.3,3.969-9.2,4.752-11.725,2.518-6.711c.861-2.318,1.8-4.7,2.619-7.189,1.7-4.966,3.557-10.18,5.18-15.684.821-2.748,1.767-5.516,2.526-8.378l2.359-8.686c.849-2.917,1.457-5.924,2.173-8.929l1.047-4.53c.358-1.513.709-3.032.974-4.572l1.74-9.238.43-2.316.343-2.331.687-4.658c1.018-6.193,1.547-12.423,2.238-18.527.44-6.127.966-12.146,1.115-18,.459-11.7.343-22.7-.222-32.317a284.213,284.213,0,0,0-4.085-36.279c-.207-1.152-.431-2.275-.649-3.336a.975.975,0,0,1,.77-1.05.923.923,0,0,1,1.142.633c.234,1.049.478,2.2.7,3.377a287.522,287.522,0,0,1,4.509,36.937c.125,2.421.324,4.923.4,7.5s.16,5.236.243,7.955c.021,5.439.119,11.141-.131,17.017-.091,5.882-.579,11.926-.955,18.08-.641,6.132-1.119,12.389-2.082,18.616l-.645,4.684-.323,2.344-.416,2.329-1.659,9.3c-.251,1.55-.6,3.077-.938,4.6l-1.009,4.56c-.691,3.025-1.276,6.055-2.1,8.99l-2.291,8.746c-.358,1.445-.786,2.858-1.21,4.261l-1.25,4.177-1.229,4.1-.606,2.022-.661,1.981-2.571,7.7a446.991,446.991,0,0,1-16.618,41.7c-11.906,26.192-23.652,43.637-24.6,43.022C929.579,703.538,939.921,684.661,951.385,659.377Z" style="fill: #fff"/>
      </g>
      <g style="opacity: 0.25;mix-blend-mode: overlay">
        <path d="M815.728,867.058c4.985-2.98,16.853-10.048,31.164-20.3l5.493-3.958,5.634-4.284,2.9-2.2,2.875-2.324,5.868-4.745,5.84-5.028c1.966-1.69,3.95-3.388,5.825-5.185,1.938-1.74,3.734-3.641,5.593-5.483,1.767-1.93,3.595-3.81,5.266-5.822a141.682,141.682,0,0,0,9.532-12.293,148.966,148.966,0,0,0,8-12.744A170.215,170.215,0,0,0,921.032,758.2a159.471,159.471,0,0,0,7.913-29.449c.151-.964.3-1.913.411-2.807a.975.975,0,0,1,1.068-.74.923.923,0,0,1,.875.972c-.1.9-.231,1.877-.377,2.88a161.517,161.517,0,0,1-7.786,30.284,171.547,171.547,0,0,1-11.212,24.911,150.627,150.627,0,0,1-7.975,13,145.349,145.349,0,0,1-9.544,12.573c-1.675,2.062-3.513,3.991-5.29,5.969-1.864,1.9-3.676,3.845-5.626,5.638-1.9,1.858-3.87,3.579-5.822,5.3l-5.811,5.116-5.842,4.831-2.864,2.367-2.887,2.244-5.619,4.365c-1.853,1.4-3.7,2.724-5.481,4.039-14.293,10.462-26.216,17.713-31.386,20.8-20.719,12.393-37.244,18.661-37.673,17.607-.2-.473,3.727-2.718,10.188-6.372S805.744,873.017,815.728,867.058Z" style="fill: #fff"/>
      </g>
      <g style="opacity: 0.25;mix-blend-mode: overlay">
        <path d="M554.408,983.558l6.732-.274,9.744-.6c7.4-.511,16.5-1.284,26.656-2.518,1.27-.153,2.563-.271,3.861-.459l3.943-.572,8.146-1.181c2.77-.391,5.557-.974,8.4-1.463,2.838-.53,5.731-.995,8.627-1.6l8.747-1.934,4.426-.978c1.475-.346,2.933-.772,4.406-1.156l8.849-2.369c.737-.2,1.483-.38,2.212-.609l2.183-.7,4.364-1.406c2.9-.958,5.823-1.823,8.681-2.849l8.494-3.142,4.194-1.56,4.1-1.671,8.039-3.294a272.236,272.236,0,0,0,28.135-14.04,119.139,119.139,0,0,0,19.1-13.549,50.12,50.12,0,0,0,7.965-8.693c.3-.463.609-.916.888-1.37.241-.462.528-.916.722-1.356a1.054,1.054,0,0,1,1.22-.393c.553.147.707.641.563,1.2-.222.525-.514,1-.784,1.53-.3.5-.627,1-.951,1.508a52.771,52.771,0,0,1-8.442,9.365,125,125,0,0,1-19.415,14.013,281.5,281.5,0,0,1-28.232,14.4l-8.036,3.375-4.095,1.717-4.188,1.6-8.477,3.232c-2.862,1.039-5.791,1.954-8.7,2.941l-4.375,1.457-2.189.729c-.735.224-1.479.417-2.218.627l-8.867,2.467c-1.476.4-2.938.844-4.417,1.2l-4.433,1.025-8.767,2.025c-2.9.632-5.8,1.125-8.645,1.684-2.852.519-5.649,1.125-8.425,1.549l-8.169,1.26c-2.667.417-5.276.828-7.832,1.108-10.2,1.329-19.35,2.185-26.818,2.757l-9.868.67-6.89.3c-28,1.167-48.348-1.163-48.243-2.288C506.4,984.276,527.373,984.711,554.408,983.558Z" style="fill: #fff"/>
      </g>
      <path d="M192.176,162.509l-1.921-3.166c-.672-1.316-1.535-2.791-2.334-4.536a76.324,76.324,0,0,1-4.609-13.428,117.306,117.306,0,0,1-3.017-19.592q-.212-2.843-.305-5.885-.018-.761-.037-1.535l-.006-.328,0-.057.011-.252a.632.632,0,0,0,0-.065l-.042.041-.088.088-.094.1c-.059.064.068-.085-.45.5-1.5,1.678-3.009,3.431-4.558,5.243-4.076,4.772-8.428,10.154-12.831,16s-8.844,12.156-13.019,18.82c-2.141,3.3-4.08,6.765-6.064,10.214l-2.776,5.281c-.48.872-.9,1.773-1.31,2.675l-1.253,2.7a150.465,150.465,0,0,0-7.862,21.923c-2.038,7.277-3.655,14.347-4.927,20.919-1.232,6.58-2.164,12.651-2.853,17.951-1.334,10.607-1.783,18.1-1.911,20.486l-.16,3.35a.971.971,0,0,1-1,.832.926.926,0,0,1-.953-.891c.021-1.058.068-2.207.118-3.391.12-2.627.5-10.286,1.76-21.02.636-5.366,1.531-11.494,2.713-18.134,1.205-6.636,2.785-13.768,4.773-21.125a149.731,149.731,0,0,1,7.776-22.295l1.244-2.753.623-1.378.687-1.345,2.755-5.379c1.976-3.5,3.9-7.027,6.026-10.378,4.148-6.767,8.537-13.188,12.925-19.108s8.7-11.4,12.766-16.271c1.544-1.828,3.052-3.6,4.547-5.3l.682-.773,1.063-1.171,2.108-2.321,2.072-2.231,1.034-1.109.248-.26.116-.117a1.1,1.1,0,0,1,0,.182l-.028.812c-.062,2.148-.183,4.377-.156,6.334q0,.754,0,1.5l0,.747.013.568q.023.747.045,1.483.108,2.943.326,5.7a113.589,113.589,0,0,0,3,18.892,72.281,72.281,0,0,0,4.427,12.758c.335.846.786,1.565,1.148,2.276l1.052,1.977,1.9,3.132c3.671,5.905,7.494,11.2,10.961,15.9,3.482,4.7,6.662,8.8,9.306,12.2,5.3,6.8,8.446,10.861,8.037,11.244-.373.351-4.4-3.189-10.359-9.649-2.975-3.233-6.428-7.2-10.1-11.815A170.553,170.553,0,0,1,192.176,162.509Z" style="fill: url(#linear-gradient-21)"/>
      <path d="M241.415,585.844c-.354-1.579-.669-3.734-1.134-6.351-.251-1.305-.429-2.745-.605-4.3l-.6-4.977a142.479,142.479,0,0,1,.09-25.646,151.87,151.87,0,0,1,6.162-31.3,99.667,99.667,0,0,1,6.587-16.038,81.86,81.86,0,0,1,22.317-26.874l3.426-2.586c1.143-.847,2.351-1.587,3.513-2.378l1.751-1.163c.589-.374,1.2-.711,1.792-1.067l3.558-2.089a81.378,81.378,0,0,1,28.769-8.817,120.8,120.8,0,0,1,22.473-.447c5.835.422,9.8,1.069,11.382,1.32,1.076.185,2.128.366,3.128.579a.986.986,0,0,1-.385,1.918c-.942-.183-1.988-.35-3.061-.522-1.691-.254-5.7-.852-11.5-1.194a118.583,118.583,0,0,0-22.116.736,78.629,78.629,0,0,0-27.641,8.936c-2.262,1.392-4.631,2.694-6.845,4.284-1.112.784-2.291,1.494-3.367,2.351l-3.281,2.531a78.823,78.823,0,0,0-21.077,26.031,96.877,96.877,0,0,0-6.171,15.5,148.827,148.827,0,0,0-5.707,30.512,139.537,139.537,0,0,0,.094,24.893l.611,4.859c.187,1.517.357,2.929.612,4.212.472,2.571.789,4.7,1.142,6.284,1.314,6.353,2.86,12.2,4.4,17.409,1.605,5.183,3.115,9.752,4.523,13.494,2.728,7.525,4.666,11.872,4.166,12.152-.433.254-3.373-3.857-6.854-11.315a132.11,132.11,0,0,1-5.422-13.491c-.858-2.619-1.776-5.376-2.534-8.306C242.763,592.085,242.12,589,241.415,585.844Z" style="fill: url(#linear-gradient-22)"/>
    </g>
  </g>
</svg>PK
!<�:e wwCchrome/devtools-shim/content/aboutdevtools/images/external-link.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill #003eaa" d="M14.923 1.618A1 1 0 0 0 14 1H9a1 1 0 0 0 0 2h2.586L8.293 6.293a1 1 0 1 0 1.414 1.414L13 4.414V7a1 1 0 0 0 2 0V2a1 1 0 0 0-.077-.382z"/>
  <path fill="context-fill #0060df" d="M14 10a1 1 0 0 0-1 1v2H3V3h2a1 1 0 0 0 0-2H2a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-3a1 1 0 0 0-1-1z"/>
</svg>
PK
!<�:�LLEchrome/devtools-shim/content/aboutdevtools/images/feature-console.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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="222px" height="122px" viewBox="0 0 222 122" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <!-- Generator: Sketch 45.2 (43514) - http://www.bohemiancoding.com/sketch -->
    <title>Console</title>
    <desc>Created with Sketch.</desc>
    <defs></defs>
    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
        <g id="2017.06.07.Firefox.DevEdition-[white]" transform="translate(-502.000000, -2865.000000)">
            <g id="Group-6" transform="translate(145.000000, 2865.000000)"></g>
            <g id="Page-1" transform="translate(555.000000, 2887.000000)">
                <path d="M116.75859,15.6454747 L116.75859,74.4046313 C116.75859,78.9611253 112.762065,82.6539446 107.832465,82.6539446 L8.926515,82.6539446 C3.996915,82.6539446 0.00039,78.9611253 0.00039,74.4046313 L0.00039,15.6454747" id="Stroke-1" stroke="#0080FF" stroke-width="5"></path>
                <path d="M114.359603,0.99990241 L2.3993775,0.99990241 C1.0743525,0.99990241 -9.75000003e-05,2.07534819 -9.75000003e-05,3.4016012 L-9.75000003e-05,14.9621554 L116.758103,14.9621554 L116.758103,3.4016012 C116.758103,2.07534819 115.684628,0.99990241 114.359603,0.99990241" id="Fill-3" fill="#0080FF"></path>
                <path d="M114.359603,-9.75903613e-05 L78.8554687,-9.75903613e-05 L2.3993775,-9.75903613e-05 C1.0743525,-9.75903613e-05 -9.75000003e-05,1.07534819 -9.75000003e-05,2.4016012 L-9.75000003e-05,13.9621554 L116.758103,13.9621554 L116.758103,2.4016012 C116.758103,1.07534819 115.684628,-9.75903613e-05 114.359603,-9.75903613e-05 Z" id="Stroke-5" stroke="#0080FF" stroke-width="5"></path>
                <path d="M9.117225,7.88080361 C9.117225,9.64230964 7.689825,11.0710325 5.92995,11.0710325 C4.170075,11.0710325 2.742675,9.64230964 2.742675,7.88080361 C2.742675,6.11832169 4.170075,4.6895988 5.92995,4.6895988 C7.689825,4.6895988 9.117225,6.11832169 9.117225,7.88080361" id="Fill-7" fill="#FFFFFF"></path>
                <path d="M19.7446275,7.88080361 C19.7446275,9.64230964 18.3182025,11.0710325 16.5573525,11.0710325 C14.7974775,11.0710325 13.3700775,9.64230964 13.3700775,7.88080361 C13.3700775,6.11832169 14.7974775,4.6895988 16.5573525,4.6895988 C18.3182025,4.6895988 19.7446275,6.11832169 19.7446275,7.88080361" id="Fill-9" fill="#FFFFFF"></path>
                <path d="M30.13023,7.88080361 C30.13023,9.64230964 28.70283,11.0710325 26.942955,11.0710325 C25.18308,11.0710325 23.75568,9.64230964 23.75568,7.88080361 C23.75568,6.11832169 25.18308,4.6895988 26.942955,4.6895988 C28.70283,4.6895988 30.13023,6.11832169 30.13023,7.88080361" id="Fill-11" fill="#FFFFFF"></path>
                <polyline id="Stroke-13" stroke="#00C7D8" stroke-width="3" stroke-linecap="round" points="13.863242 53.3297773 19.986242 59.4574761 13.863242 65.5871267"></polyline>
                <path d="M25.574659,67 L38.160934,67" id="Stroke-15" stroke="#00C7D8" stroke-width="3" stroke-linecap="round"></path>
            </g>
            <rect id="Rectangle-5" fill="#1C2142" x="615" y="2913" width="46" height="3" rx="1.5"></rect>
            <rect id="Rectangle-5" fill="#1C2142" x="567" y="2913" width="40" height="3" rx="1.5"></rect>
            <rect id="Rectangle-5" fill="#1C2142" x="623" y="2925" width="26" height="3" rx="1.5"></rect>
            <rect id="Rectangle-5" fill="#1C2142" x="567" y="2925" width="50" height="3" rx="1.5"></rect>
        </g>
    </g>
</svg>PK
!<�a�L�
�
Fchrome/devtools-shim/content/aboutdevtools/images/feature-debugger.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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="222px" height="122px" viewBox="0 0 222 122" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <!-- Generator: Sketch 45.2 (43514) - http://www.bohemiancoding.com/sketch -->
    <title>Debugger</title>
    <desc>Created with Sketch.</desc>
    <defs></defs>
    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
        <g id="2017.06.07.Firefox.DevEdition-[white]" transform="translate(-843.000000, -2865.000000)">
            <g id="Group-11" transform="translate(892.000000, 2893.000000)">
                <g id="Page-1" transform="translate(4.032520, 0.000000)" stroke="#0080FF" stroke-width="4" stroke-linecap="round">
                    <path d="M101.173069,18.4397779 L101.173069,14.4680499 C101.173069,11.4317399 103.618565,8.9694856 106.635539,8.9694856 L117.258565,8.9694856" id="Stroke-1"></path>
                    <polyline id="Stroke-3" points="111.688269 2.54689767 118.069417 8.97002819 111.688269 15.3931587"></polyline>
                    <path d="M89.3390484,28 L97.1187005,28" id="Stroke-5"></path>
                    <path d="M104.283097,28 L112.063828,28" id="Stroke-7"></path>
                    <path d="M44.237984,28 L53.1153058,28" id="Stroke-16"></path>
                    <path d="M61.2894936,28 L70.1668153,28" id="Stroke-18"></path>
                    <path d="M47.537031,0.000325551471 L52.0409266,0.000325551471 C55.4827353,0.000325551471 58.2743527,2.80874957 58.2743527,6.27261722 L58.2743527,18.4710308" id="Stroke-20"></path>
                    <polyline id="Stroke-22" points="65.5557405 12.0750296 58.2742449 19.4031932 50.991671 12.0750296"></polyline>
                    <path d="M47.537031,0.000325551471 L52.0409266,0.000325551471 C55.4827353,0.000325551471 58.2743527,2.80874957 58.2743527,6.27261722 L58.2743527,18.4710308" id="Stroke-24"></path>
                    <polyline id="Stroke-26" points="65.5557405 12.0750296 58.2742449 19.4031932 50.991671 12.0750296"></polyline>
                    <path d="M3.81743446,28 L7.27973011,28" id="Stroke-28"></path>
                    <path d="M0.057215327,16.1793655 L2.74855446,12.0274991 C4.8058762,8.85445741 9.03050228,7.96027604 12.1833371,10.0307834 L23.2861892,17.3220512" id="Stroke-30"></path>
                    <polyline id="Stroke-32" points="21.8156571 6.78590337 24.1339179 17.8785271 13.1119353 20.2105608"></polyline>
                    <path d="M0.057215327,16.1793655 L2.74855446,12.0274991 C4.8058762,8.85445741 9.03050228,7.96027604 12.1833371,10.0307834 L23.2861892,17.3220512" id="Stroke-34"></path>
                    <polyline id="Stroke-36" points="21.8156571 6.78590337 24.1339179 17.8785271 13.1119353 20.2105608"></polyline>
                </g>
                <rect id="Rectangle-10" fill="#00C7D8" x="0" y="46.6764706" width="124" height="5" rx="2.5"></rect>
                <rect id="Rectangle-10" fill="#0080FF" x="1" y="62.9117647" width="35.2299995" height="5" rx="2.5"></rect>
                <rect id="Rectangle-10" fill="#1C2142" x="45.2764228" y="62.9117647" width="45.3658537" height="5" rx="2.5"></rect>
            </g>
            <g id="Group-6" transform="translate(145.000000, 2865.000000)"></g>
        </g>
    </g>
</svg>PK
!<�O�
__Gchrome/devtools-shim/content/aboutdevtools/images/feature-inspector.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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="222px" height="122px" viewBox="0 0 222 122" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <!-- Generator: Sketch 45.2 (43514) - http://www.bohemiancoding.com/sketch -->
    <title>Inspector</title>
    <desc>Created with Sketch.</desc>
    <defs>
        <polygon id="path-1" points="82.3351726 27.0750512 82.3351726 54.149836 0.391390693 54.149836 0.391390693 0.000266310669 82.3351726 0.000266310669 82.3351726 27.0750512"></polygon>
    </defs>
    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
        <g id="2017.06.07.Firefox.DevEdition-[white]" transform="translate(-145.000000, -2868.000000)">
            <g id="Group-18" transform="translate(176.000000, 2877.000000)">
                <g id="Group-6">
                    <g id="Page-1">
                        <g id="Group-14">
                            <path d="M104.972689,69.5181903 L40.5788803,69.5181903 C35.675454,69.5181903 31.6986882,65.5085545 31.6986882,60.5606493 L31.6986882,36.4173153 C31.6986882,31.4694101 35.675454,27.4597744 40.5788803,27.4597744 L104.972689,27.4597744 C109.877327,27.4597744 113.854092,31.4694101 113.854092,36.4173153 L113.854092,60.5606493 C113.854092,65.5085545 109.877327,69.5181903 104.972689,69.5181903" id="Fill-1" fill="#0080FF"></path>
                            <path d="M19.6363636,0.611219006 L19.6363636,97.7367656" id="Stroke-3" stroke="#0080FF" stroke-width="4" stroke-linecap="round"></path>
                            <path d="M125.181818,0.611219006 L125.181818,97.7367656" id="Stroke-5" stroke="#0080FF" stroke-width="4" stroke-linecap="round"></path>
                            <path d="M145.397489,13.5581395 L0.605902617,13.5581395" id="Stroke-7" stroke="#0080FF" stroke-width="4" stroke-linecap="round"></path>
                            <path d="M145.397489,82.5813953 L0.605902617,82.5813953" id="Stroke-9" stroke="#0080FF" stroke-width="4" stroke-linecap="round"></path>
                        </g>
                        <g id="Group-13" transform="translate(77.331874, 50.959169)">
                            <mask id="mask-2" fill="white">
                                <use xlink:href="#path-1"></use>
                            </mask>
                            <g id="Clip-12"></g>
                            <path d="M15.6361907,54.1501023 C7.21643156,54.1501023 0.391390693,47.2917149 0.391390693,38.8319126 L0.391390693,0.000266310669 L67.0894891,0.000266310669 C75.5092482,0.000266310669 82.3351726,6.85865373 82.3351726,15.318456 L82.3351726,38.8319126 C82.3351726,47.2917149 75.5092482,54.1501023 67.0894891,54.1501023 L15.6361907,54.1501023 Z" id="Fill-11" fill="#1C2142" mask="url(#mask-2)"></path>
                        </g>
                    </g>
                    <g id="Group-25" transform="translate(85.139034, 64.361849)" fill="#00C7D8">
                        <rect id="Rectangle-5" x="49.840151" y="0" width="15.6361258" height="2.94439966" rx="1.47219983"></rect>
                        <rect id="Rectangle-5" x="0" y="24.5366638" width="15.6361258" height="2.94439966" rx="1.47219983"></rect>
                        <rect id="Rectangle-5" x="20.5224151" y="24.5366638" width="15.6361258" height="2.94439966" rx="1.47219983"></rect>
                        <rect id="Rectangle-5" x="0" y="0" width="43.9766038" height="2.94439966" rx="1.47219983"></rect>
                        <rect id="Rectangle-5" x="0" y="12.7590652" width="65.4762768" height="2.94439966" rx="1.47219983"></rect>
                    </g>
                </g>
            </g>
            <g id="Group-6" transform="translate(145.000000, 2865.000000)"></g>
        </g>
    </g>
</svg>PK
!<��;�TTDchrome/devtools-shim/content/aboutdevtools/images/feature-memory.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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="222px" height="122px" viewBox="0 0 222 122" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <!-- Generator: Sketch 45.2 (43514) - http://www.bohemiancoding.com/sketch -->
    <title>Memory</title>
    <desc>Created with Sketch.</desc>
    <defs></defs>
    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
        <g id="2017.06.07.Firefox.DevEdition-[white]" transform="translate(-846.000000, -3508.000000)">
            <path d="M918,3609.05462 C918,3611.74049 920.178731,3613.9179 922.866215,3613.9179 L994.621505,3613.9179 C997.308989,3613.9179 999.48772,3611.74049 999.48772,3609.05462 L999.48772,3574.73675 C995.606387,3572.77699 992.938301,3569.64701 992.938301,3565.00403 C992.938301,3560.36017 995.606387,3557.23107 999.48772,3555.27131 L999.48772,3530.86328 C999.48772,3528.17742 997.308989,3526 994.621505,3526 L922.866215,3526 C920.178731,3526 918,3528.17742 918,3530.86328" id="Stroke-1" stroke="#0080FF" stroke-width="5" stroke-linecap="round"></path>
            <path d="M918,3532 L918,3554.51876 C921.882215,3556.47764 924.549419,3559.60762 924.549419,3564.25149 C924.549419,3568.89358 921.882215,3572.02356 918,3573.98421 L918,3610.19047" id="Stroke-3" stroke="#0080FF" stroke-width="5" stroke-linecap="round"></path>
            <path d="M965,3594 L965,3599.21751" id="Stroke-5" stroke="#00C7D8" stroke-width="3" stroke-linecap="round"></path>
            <path d="M976,3594 L976,3599.21751" id="Stroke-7" stroke="#00C7D8" stroke-width="3" stroke-linecap="round"></path>
            <path d="M954,3594 L954,3599.21751" id="Stroke-9" stroke="#00C7D8" stroke-width="3" stroke-linecap="round"></path>
            <path d="M943,3594 L943,3599.21751" id="Stroke-11" stroke="#00C7D8" stroke-width="3" stroke-linecap="round"></path>
            <path d="M972.908182,3546 L946.753278,3546 C944.688851,3546 943,3547.68615 943,3549.74628 L943,3573.58554 C943,3575.64566 944.688851,3577.33181 946.753278,3577.33181 L972.908182,3577.33181 C974.972609,3577.33181 976.662285,3575.64566 976.662285,3573.58554 L976.662285,3549.74628 C976.662285,3547.68615 974.972609,3546 972.908182,3546 Z" id="Stroke-31" stroke="#0022A9" stroke-width="4" stroke-linecap="round"></path>
            <g id="Group-19" transform="translate(938.000000, 3541.000000)" stroke="#0022A9" stroke-width="4" stroke-linecap="round">
                <path d="M39.3191489,13 L43.9659236,13" id="Stroke-19"></path>
                <path d="M39.3191489,21 L43.9659236,21" id="Stroke-21"></path>
                <path d="M39.3191489,30 L43.9659236,30" id="Stroke-23"></path>
                <path d="M0,13 L4.64677463,13" id="Stroke-25"></path>
                <path d="M0,21 L4.64677463,21" id="Stroke-27"></path>
                <path d="M0,30 L4.64677463,30" id="Stroke-29"></path>
                <path d="M13,5.07816341 L13,0.445188366" id="Stroke-33"></path>
                <path d="M21.5,5.07816341 L21.5,0.445188366" id="Stroke-35"></path>
                <path d="M30,5.07816341 L30,0.445188366" id="Stroke-37"></path>
                <path d="M13,42.2908441 L13,37.6578691" id="Stroke-39"></path>
                <path d="M21.5,42.2908441 L21.5,37.6578691" id="Stroke-41"></path>
                <path d="M30,42.2908441 L30,37.6578691" id="Stroke-43"></path>
            </g>
            <g id="Group-6" transform="translate(145.000000, 2865.000000)"></g>
        </g>
    </g>
</svg>PK
!<��[�	�	Echrome/devtools-shim/content/aboutdevtools/images/feature-network.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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="222px" height="122px" viewBox="0 0 222 122" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <!-- Generator: Sketch 45.2 (43514) - http://www.bohemiancoding.com/sketch -->
    <title>Network</title>
    <desc>Created with Sketch.</desc>
    <defs></defs>
    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
        <g id="2017.06.07.Firefox.DevEdition-[white]" transform="translate(-149.000000, -3171.000000)">
            <path d="M198.474304,3207.33333 L320.101069,3207.33333" id="Stroke-7" stroke="#031537" stroke-width="5" stroke-linecap="round" transform="translate(259.500000, 3206.500000) scale(1, -1) translate(-259.500000, -3206.500000) "></path>
            <g id="Group-20" transform="translate(207.000000, 3202.000000)" stroke="#031537" stroke-width="5" stroke-linecap="round">
                <path d="M107,0 L107,8.69585809" id="Stroke-5" transform="translate(109.500000, 4.347929) scale(1, -1) translate(-109.500000, -4.347929) "></path>
                <path d="M71,0 L71,8.69585809" id="Stroke-7" transform="translate(73.500000, 4.347929) scale(1, -1) translate(-73.500000, -4.347929) "></path>
                <path d="M34,0 L34,8.69585809" id="Stroke-9" transform="translate(36.500000, 4.347929) scale(1, -1) translate(-36.500000, -4.347929) "></path>
                <path d="M0,0 L0,8.69585809" id="Stroke-11" transform="translate(2.500000, 4.347929) scale(1, -1) translate(-2.500000, -4.347929) "></path>
            </g>
            <rect id="Rectangle-9" fill="#0080FF" x="199" y="3227" width="56.0699997" height="5" rx="2.5"></rect>
            <rect id="Rectangle-9" fill="#00C7D8" x="283" y="3259" width="33.0764463" height="5" rx="2.5"></rect>
            <rect id="Rectangle-9" fill="#0080FF" x="264" y="3227" width="33.0764463" height="5" rx="2.5"></rect>
            <path d="M239,3245.5 C239,3244.11929 240.110137,3243 241.509621,3243 L324.292032,3243 C325.678057,3243 326.801653,3244.10966 326.801653,3245.5 L326.801653,3245.5 C326.801653,3246.88071 325.691516,3248 324.292032,3248 L241.509621,3248 C240.123596,3248 239,3246.89034 239,3245.5 L239,3245.5 Z" id="Rectangle-9" fill="#1C2142"></path>
            <g id="Group-6" transform="translate(145.000000, 2865.000000)"></g>
        </g>
    </g>
</svg>PK
!<��W""Ichrome/devtools-shim/content/aboutdevtools/images/feature-performance.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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="222px" height="122px" viewBox="0 0 222 122" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <!-- Generator: Sketch 45.2 (43514) - http://www.bohemiancoding.com/sketch -->
    <title>Performance</title>
    <desc>Created with Sketch.</desc>
    <defs></defs>
    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
        <g id="2017.06.07.Firefox.DevEdition-[white]" transform="translate(-490.000000, -3508.000000)">
            <g id="Page-1-Copy" transform="translate(540.000000, 3527.000000)">
                <path d="M107.539403,14.3339002 L107.539403,72.0477183 C107.539403,76.1024983 104.090172,79.3891087 99.8359389,79.3891087 L8.15514924,79.3891087 C3.9000141,79.3891087 0.450782609,76.1024983 0.450782609,72.0477183 L0.450782609,14.3339002" id="Stroke-9" stroke="#0080FF" stroke-width="5"></path>
                <path d="M105.436112,0.450972419 L2.55479577,0.450972419 C1.39241739,0.450972419 0.451143596,1.39335674 0.451143596,2.55508913 L0.451143596,12.8770859 L107.539764,12.8770859 L107.539764,2.55508913 C107.539764,1.39335674 106.597588,0.450972419 105.436112,0.450972419" id="Fill-11" fill="#0080FF"></path>
                <path d="M105.436112,0.450972419 L2.55479577,0.450972419 C1.39241739,0.450972419 0.451143596,1.39335674 0.451143596,2.55508913 L0.451143596,12.8770859 L107.539764,12.8770859 L107.539764,2.55508913 C107.539764,1.39335674 106.597588,0.450972419 105.436112,0.450972419 Z" id="Stroke-13" stroke="#0080FF" stroke-width="5"></path>
                <path d="M8.3194886,7.44402371 C8.3194886,9.01195623 7.08762021,10.283814 5.5687671,10.283814 C4.04991398,10.283814 2.81804559,9.01195623 2.81804559,7.44402371 C2.81804559,5.87609118 4.04991398,4.60423342 5.5687671,4.60423342 C7.08762021,4.60423342 8.3194886,5.87609118 8.3194886,7.44402371" id="Fill-15" fill="#FFFFFF"></path>
                <path d="M17.4913579,7.44402371 C17.4913579,9.01195623 16.260392,10.283814 14.7406364,10.283814 C13.2217833,10.283814 11.9899149,9.01195623 11.9899149,7.44402371 C11.9899149,5.87609118 13.2217833,4.60423342 14.7406364,4.60423342 C16.260392,4.60423342 17.4913579,5.87609118 17.4913579,7.44402371" id="Fill-17" fill="#FFFFFF"></path>
                <path d="M26.454306,7.44402371 C26.454306,9.01195623 25.2224376,10.283814 23.7035845,10.283814 C22.1847314,10.283814 20.952863,9.01195623 20.952863,7.44402371 C20.952863,5.87609118 22.1847314,4.60423342 23.7035845,4.60423342 C25.2224376,4.60423342 26.454306,5.87609118 26.454306,7.44402371" id="Fill-19" fill="#FFFFFF"></path>
            </g>
            <g id="Group-15" transform="translate(603.000000, 3564.000000)">
                <path d="M16.7889599,48.6338083 L60.0183852,48.6338083 L66.7547698,48.6338083 C68.5906179,44.4244314 69.6087997,39.7787973 69.6087997,34.8959824 C69.6087997,15.8507488 54.117612,0.410664266 35.0085629,0.410664266 L34.8400362,0.410664266 C15.7309871,0.410664266 0.239799373,15.8507488 0.239799373,34.8959824 C0.239799373,39.7787973 1.25798119,44.4244314 3.09382933,48.6338083 L16.7889599,48.6338083 Z" id="Fill-13" fill="#FFFFFF"></path>
                <path d="M16.7889599,48.6338083 L60.0183852,48.6338083 L66.7547698,48.6338083 C68.5906179,44.4244314 69.6087997,39.7787973 69.6087997,34.8959824 C69.6087997,15.8507488 54.117612,0.410664266 35.0085629,0.410664266 L34.8400362,0.410664266 C15.7309871,0.410664266 0.239799373,15.8507488 0.239799373,34.8959824 C0.239799373,39.7787973 1.25798119,44.4244314 3.09382933,48.6338083 L16.7889599,48.6338083 Z" id="Stroke-15" stroke="#00C7D8" stroke-width="5" stroke-linecap="round"></path>
                <path d="M34.5454545,11.0442206 L34.5454545,17.5328666" id="Stroke-17" stroke="#0080FF" stroke-width="3" stroke-linecap="round"></path>
                <path d="M17.041906,19.3658468 L21.3104674,23.6203269" id="Stroke-19" stroke="#0080FF" stroke-width="3" stroke-linecap="round"></path>
                <path d="M12.0840237,36.2962963 L18.1197743,36.2962963" id="Stroke-21" stroke="#0080FF" stroke-width="3" stroke-linecap="round"></path>
                <path d="M43.1928722,32.7635321 C43.1928722,37.3158492 39.4907475,41.0049834 34.9241435,41.0049834 C30.3575395,41.0049834 26.6554148,37.3158492 26.6554148,32.7635321 C26.6554148,28.2112151 30.3575395,24.5220808 34.9241435,24.5220808 C39.4907475,24.5220808 43.1928722,28.2112151 43.1928722,32.7635321" id="Fill-23" fill="#0022A9"></path>
                <g id="Group-28" transform="translate(34.890282, 14.806064)">
                    <path d="M0.0340954371,17.9574677 L17.4001421,0.647931427" id="Fill-25" fill="#005482"></path>
                    <path d="M0.0340954371,17.9574677 L17.4001421,0.647931427" id="Stroke-27" stroke="#137EFF" stroke-width="3" stroke-linecap="round"></path>
                </g>
            </g>
            <g id="Group-6" transform="translate(145.000000, 2865.000000)"></g>
        </g>
    </g>
</svg>PK
!<��M

Hchrome/devtools-shim/content/aboutdevtools/images/feature-responsive.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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="222px" height="122px" viewBox="0 0 222 122" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <!-- Generator: Sketch 45.2 (43514) - http://www.bohemiancoding.com/sketch -->
    <title>Responsive design mode</title>
    <desc>Created with Sketch.</desc>
    <defs></defs>
    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
        <g id="2017.06.07.Firefox.DevEdition-[white]" transform="translate(-843.000000, -3171.000000)">
            <g id="Group-23" transform="translate(902.000000, 3212.000000)" fill="#00C7D8">
                <rect id="Rectangle-5" x="0" y="12" width="63" height="3" rx="1.5"></rect>
                <rect id="Rectangle-5" x="0" y="25" width="58" height="3" rx="1.5"></rect>
                <rect id="Rectangle-5" x="0" y="0" width="41" height="3" rx="1.5"></rect>
                <rect id="Rectangle-5" x="0" y="37" width="41" height="3" rx="1.5"></rect>
            </g>
            <g id="Page-1" transform="translate(890.000000, 3186.000000)">
                <path d="M110.526609,14.6922477 L110.526609,73.8489112 C110.526609,78.0050607 106.981565,81.3738364 102.609159,81.3738364 L8.38168116,81.3738364 C4.00834783,81.3738364 0.463304348,78.0050607 0.463304348,73.8489112 L0.463304348,14.6922477" id="Stroke-9" stroke="#0080FF" stroke-width="5"></path>
                <path d="M108.364893,0.462246729 L2.62576232,0.462246729 C1.43109565,0.462246729 0.463675362,1.42819065 0.463675362,2.61896636 L0.463675362,13.1990131 L110.52698,13.1990131 L110.52698,2.61896636 C110.52698,1.42819065 109.558632,0.462246729 108.364893,0.462246729" id="Fill-11" fill="#0080FF"></path>
                <path d="M108.364893,0.462246729 L2.62576232,0.462246729 C1.43109565,0.462246729 0.463675362,1.42819065 0.463675362,2.61896636 L0.463675362,13.1990131 L110.52698,13.1990131 L110.52698,2.61896636 C110.52698,1.42819065 109.558632,0.462246729 108.364893,0.462246729 Z" id="Stroke-13" stroke="#0080FF" stroke-width="5"></path>
                <path d="M8.55058551,7.6301243 C8.55058551,9.23725514 7.28449855,10.5409093 5.72345507,10.5409093 C4.16241159,10.5409093 2.89632464,9.23725514 2.89632464,7.6301243 C2.89632464,6.02299346 4.16241159,4.71933925 5.72345507,4.71933925 C7.28449855,4.71933925 8.55058551,6.02299346 8.55058551,7.6301243" id="Fill-15" fill="#FFFFFF"></path>
                <path d="M17.977229,7.6301243 C17.977229,9.23725514 16.7120696,10.5409093 15.1500986,10.5409093 C13.5890551,10.5409093 12.3229681,9.23725514 12.3229681,7.6301243 C12.3229681,6.02299346 13.5890551,4.71933925 15.1500986,4.71933925 C16.7120696,4.71933925 17.977229,6.02299346 17.977229,7.6301243" id="Fill-17" fill="#FFFFFF"></path>
                <path d="M27.1891478,7.6301243 C27.1891478,9.23725514 25.9230609,10.5409093 24.3620174,10.5409093 C22.8009739,10.5409093 21.534887,9.23725514 21.534887,7.6301243 C21.534887,6.02299346 22.8009739,4.71933925 24.3620174,4.71933925 C25.9230609,4.71933925 27.1891478,6.02299346 27.1891478,7.6301243" id="Fill-19" fill="#FFFFFF"></path>
            </g>
            <g id="Page-1" transform="translate(974.000000, 3196.000000)">
                <rect id="Rectangle-6" fill="#FFFFFF" x="1.85507246" y="1.85046729" width="51.0144928" height="82.5319977"></rect>
                <g id="Group-23" transform="translate(9.797101, 13.271028)" fill="#1C2142">
                    <rect id="Rectangle-5" x="0" y="10" width="35.2000008" height="3" rx="1.5"></rect>
                    <rect id="Rectangle-5" x="0" y="19" width="30.2999992" height="3" rx="1.5"></rect>
                    <rect id="Rectangle-5" x="0" y="0" width="26.0200005" height="3" rx="1.5"></rect>
                    <rect id="Rectangle-5" x="0" y="29" width="22.0200005" height="3" rx="1.5"></rect>
                </g>
                <path d="M44.1492738,82.932179 L9.66292295,82.932179 C4.56906009,82.932179 0.401792083,78.7766344 0.401792083,73.6971017 L0.401792083,9.63525863 C0.401792083,4.55572592 4.56906009,0.400181344 9.66292295,0.400181344 L44.1492738,0.400181344 C49.2431367,0.400181344 53.4112076,4.55572592 53.4112076,9.63525863 L53.4112076,73.6971017 C53.4112076,78.7766344 49.2431367,82.932179 44.1492738,82.932179 Z" id="Stroke-1" stroke="#0E59E1" stroke-width="5" stroke-linecap="round"></path>
                <path d="M53.4109667,58 L0.401551201,58" id="Stroke-3" stroke="#0E59E1" stroke-width="4" stroke-linecap="round"></path>
                <path d="M26.4057603,73.678527 C24.2272275,73.678527 22.4607604,71.9170293 22.4607604,69.7439667 C22.4607604,67.5715627 24.2272275,65.810065 26.4057603,65.810065 C28.5842931,65.810065 30.3507602,67.5715627 30.3507602,69.7439667 C30.3507602,71.9170293 28.5842931,73.678527 26.4057603,73.678527 Z" id="Fill-5" fill="#0080FF"></path>
            </g>
            <g id="Group-6" transform="translate(145.000000, 2865.000000)"></g>
        </g>
    </g>
</svg>PK
!<��	11Echrome/devtools-shim/content/aboutdevtools/images/feature-storage.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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="222px" height="122px" viewBox="0 0 222 122" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <!-- Generator: Sketch 45.2 (43514) - http://www.bohemiancoding.com/sketch -->
    <title>Storage</title>
    <desc>Created with Sketch.</desc>
    <defs></defs>
    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
        <g id="2017.06.07.Firefox.DevEdition-[white]" transform="translate(-494.000000, -3171.000000)">
            <g id="Group-16" transform="translate(562.000000, 3192.000000)" stroke-width="5" stroke-linecap="round">
                <path d="M79.0179219,81.9119141 L9.41614704,81.9119141 C4.47517472,81.9119141 0.43303223,78.6943644 0.43303223,74.7621705 L0.43303223,61.8828944 C0.43303223,57.9507005 4.47517472,54.7331509 9.41614704,54.7331509 L79.0179219,54.7331509 C83.9578575,54.7331509 88,57.9507005 88,61.8828944 L88,74.7621705 C88,78.6943644 83.9578575,81.9119141 79.0179219,81.9119141 Z" id="Stroke-1" stroke="#0022A9"></path>
                <path d="M79.0179219,54.7335635 L9.41614704,54.7335635 C4.47517472,54.7335635 0.43303223,51.5160138 0.43303223,47.5838199 L0.43303223,34.7045439 C0.43303223,30.7723499 4.47517472,27.5548003 9.41614704,27.5548003 L79.0179219,27.5548003 C83.9578575,27.5548003 88,30.7723499 88,34.7045439 L88,47.5838199 C88,51.5160138 83.9578575,54.7335635 79.0179219,54.7335635 Z" id="Stroke-3" stroke="#137EFF"></path>
                <path d="M79.0179219,27.5551304 L9.41614704,27.5551304 C4.47517472,27.5551304 0.43303223,24.3375807 0.43303223,20.4053868 L0.43303223,7.52611075 C0.43303223,3.59391684 4.47517472,0.376367187 9.41614704,0.376367187 L79.0179219,0.376367187 C83.9578575,0.376367187 88,3.59391684 88,7.52611075 L88,20.4053868 C88,24.3375807 83.9578575,27.5551304 79.0179219,27.5551304 Z" id="Stroke-5" stroke="#00C7D8"></path>
            </g>
            <path d="M576.8016,3209.62875 C574.701741,3209.62875 573,3207.92195 573,3205.8148 C573,3203.70765 574.701741,3202 576.8016,3202 C578.900612,3202 580.602353,3203.70765 580.602353,3205.8148 C580.602353,3207.92195 578.900612,3209.62875 576.8016,3209.62875 Z" id="Fill-7" fill="#1C2142"></path>
            <path d="M580.602353,3232.8148 C580.602353,3234.92195 578.900612,3236.62875 576.8016,3236.62875 C574.701741,3236.62875 573,3234.92195 573,3232.8148 C573,3230.70765 574.701741,3229 576.8016,3229 C578.900612,3229 580.602353,3230.70765 580.602353,3232.8148" id="Fill-9" fill="#1C2142"></path>
            <path d="M576.8016,3263.62875 C574.701741,3263.62875 573,3261.9211 573,3259.8148 C573,3257.70765 574.701741,3256 576.8016,3256 C578.900612,3256 580.602353,3257.70765 580.602353,3259.8148 C580.602353,3261.9211 578.900612,3263.62875 576.8016,3263.62875 Z" id="Fill-11" fill="#1C2142"></path>
            <g id="Group-6" transform="translate(145.000000, 2865.000000)"></g>
        </g>
    </g>
</svg>PK
!<S^��Kchrome/devtools-shim/content/aboutdevtools/images/feature-visualediting.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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="222px" height="122px" viewBox="0 0 222 122" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <!-- Generator: Sketch 45.2 (43514) - http://www.bohemiancoding.com/sketch -->
    <title>Visual Editing</title>
    <desc>Created with Sketch.</desc>
    <defs></defs>
    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
        <g id="2017.06.07.Firefox.DevEdition-[white]" transform="translate(-148.000000, -3508.000000)">
            <g id="Page-1" transform="translate(223.000000, 3529.000000)" stroke-linecap="round">
                <path d="M0.89919464,57.0148819 L77.7462535,23.4916261" id="Stroke-1" stroke="#00C7D8" stroke-width="3"></path>
                <path d="M45.0950384,7 L29.3065911,7" id="Stroke-7" stroke="#0022A9" stroke-width="4"></path>
                <path d="M27.8236668,7.37685859 C27.8236668,3.30266194 24.5180803,0 20.4416475,0 C16.3638431,0 13.0582566,3.30266194 13.0582566,7.37685859 C13.0582566,11.4510552 16.3638431,14.7537172 20.4416475,14.7537172 C24.5180803,14.7537172 27.8236668,11.4510552 27.8236668,7.37685859 Z" id="Stroke-9" stroke="#0022A9" stroke-width="4"></path>
                <g id="Group-22" transform="translate(50.605882, 72.383721) scale(-1, 1) translate(-50.605882, -72.383721) translate(34.105882, 64.883721)" stroke="#0022A9" stroke-width="4">
                    <path d="M32.271509,6 L16.4830617,6" id="Stroke-7"></path>
                    <path d="M15.0001374,7.37685859 C15.0001374,3.30266194 11.6945509,-8.52651283e-14 7.61811806,-8.52651283e-14 C3.54031364,-8.52651283e-14 0.234727144,3.30266194 0.234727144,7.37685859 C0.234727144,11.4510552 3.54031364,14.7537172 7.61811806,14.7537172 C11.6945509,14.7537172 15.0001374,11.4510552 15.0001374,7.37685859 Z" id="Stroke-9"></path>
                </g>
            </g>
            <path d="M228.208485,3610.05653 C246.820825,3610.05653 261.908498,3594.86648 261.908498,3576.12979 L261.908498,3572.0866 L261.908498,3563.26842 C261.908498,3544.53086 271.883376,3529.97961 290.495715,3529.97961" id="Stroke-10" stroke="#0080FF" stroke-width="5" stroke-linecap="round" transform="translate(259.352100, 3570.018067) rotate(13.000000) translate(-259.352100, -3570.018067) "></path>
            <ellipse id="Oval" fill="#0080FF" cx="220.155067" cy="3602.14962" rx="6.15506733" ry="6.14962174"></ellipse>
            <ellipse id="Oval" fill="#0080FF" cx="299.155067" cy="3536.14962" rx="6.15506733" ry="6.14962174"></ellipse>
            <g id="Group-6" transform="translate(145.000000, 2865.000000)"></g>
        </g>
    </g>
</svg>PK
!<=�-^{d{d;chrome/devtools-shim/content/aboutdevtools/images/otter.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 283 184"><defs><style>.cls-1{fill:#fff;}.cls-2{fill:none;}.cls-3{fill:#eaeaee;}.cls-4{fill:url(#linear-gradient);}.cls-5{fill:url(#New_Gradient_Swatch_1);}.cls-6{fill:url(#New_Gradient_Swatch_1-2);}.cls-7{fill:url(#New_Gradient_Swatch_1-3);}.cls-8{fill:url(#New_Gradient_Swatch_1-4);}.cls-9{fill:url(#New_Gradient_Swatch_1-5);}.cls-10{fill:url(#New_Gradient_Swatch_1-6);}.cls-11{fill:url(#New_Gradient_Swatch_1-7);}.cls-12{fill:url(#linear-gradient-2);}.cls-13{fill:url(#New_Gradient_Swatch_1-8);}.cls-14{fill:url(#linear-gradient-3);}.cls-15{fill:url(#linear-gradient-4);}.cls-16{fill:#00feff;}.cls-17{fill:url(#linear-gradient-5);}.cls-18{fill:url(#New_Gradient_Swatch_1-9);}</style><linearGradient id="linear-gradient" x1="41.48" y1="25.02" x2="220.85" y2="204.38" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#ccfbff"/><stop offset="1" stop-color="#c9e4ff"/></linearGradient><linearGradient id="New_Gradient_Swatch_1" x1="28.89" y1="8.61" x2="232.86" y2="212.57" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#00c8d7"/><stop offset="1" stop-color="#0a84ff"/></linearGradient><linearGradient id="New_Gradient_Swatch_1-2" x1="28.33" y1="9.17" x2="232.3" y2="213.13" xlink:href="#New_Gradient_Swatch_1"/><linearGradient id="New_Gradient_Swatch_1-3" x1="21.57" y1="15.93" x2="225.54" y2="219.89" xlink:href="#New_Gradient_Swatch_1"/><linearGradient id="New_Gradient_Swatch_1-4" x1="42.38" y1="-4.88" x2="246.34" y2="199.09" xlink:href="#New_Gradient_Swatch_1"/><linearGradient id="New_Gradient_Swatch_1-5" x1="20.17" y1="17.33" x2="224.13" y2="221.3" xlink:href="#New_Gradient_Swatch_1"/><linearGradient id="New_Gradient_Swatch_1-6" x1="41.18" y1="-3.68" x2="245.14" y2="200.29" xlink:href="#New_Gradient_Swatch_1"/><linearGradient id="New_Gradient_Swatch_1-7" x1="13.58" y1="23.92" x2="217.55" y2="227.88" xlink:href="#New_Gradient_Swatch_1"/><linearGradient id="linear-gradient-2" x1="76.52" y1="-10.02" x2="255.88" y2="169.35" xlink:href="#linear-gradient"/><linearGradient id="New_Gradient_Swatch_1-8" x1="62.22" y1="-24.72" x2="266.19" y2="179.24" xlink:href="#New_Gradient_Swatch_1"/><linearGradient id="linear-gradient-3" x1="73.09" y1="-6.59" x2="252.46" y2="172.78" xlink:href="#linear-gradient"/><linearGradient id="linear-gradient-4" x1="60.49" y1="6.01" x2="239.85" y2="185.38" xlink:href="#linear-gradient"/><linearGradient id="linear-gradient-5" x1="68.95" y1="-2.45" x2="248.31" y2="176.92" xlink:href="#linear-gradient"/><linearGradient id="New_Gradient_Swatch_1-9" x1="31.51" y1="5.99" x2="235.48" y2="209.95" xlink:href="#New_Gradient_Swatch_1"/></defs><title>Asset 6</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><ellipse class="cls-1" cx="169.02" cy="97.23" rx="8.56" ry="10.44"/><path class="cls-2" d="M161,110.89c.12-.18.23-.36.36-.54l-.69.3Z"/><path class="cls-3" d="M57.62,92.12a17.9,17.9,0,0,0-8.89,2.64,16.12,16.12,0,0,1-8.89,2.41,16.11,16.11,0,0,1-8.89-2.41A18,18,0,0,0,21.1,92.1a18,18,0,0,0-9.84,2.65,16.11,16.11,0,0,1-8.89,2.41,1,1,0,1,0,0,2,18,18,0,0,0,9.84-2.65A16.11,16.11,0,0,1,21.1,94.1,16.11,16.11,0,0,1,30,96.51a18,18,0,0,0,9.84,2.65,18,18,0,0,0,9.84-2.65,16.11,16.11,0,0,1,8.89-2.41h.38c0-.13,0-.26,0-.38A15.72,15.72,0,0,1,57.62,92.12Z"/><path class="cls-3" d="M46.37,90.34a.5.5,0,0,0,.2,0l.92-.44A.5.5,0,0,0,47,89l-.88.42a.5.5,0,0,0,.2,1Z"/><path class="cls-3" d="M20.51,86.67a16.52,16.52,0,0,1,9.13,2.47c.63.34,1.27.69,1.95,1a.5.5,0,0,0,.21,0,.5.5,0,0,0,.21-1c-.66-.31-1.28-.65-1.9-1a17.58,17.58,0,0,0-9.6-2.59.5.5,0,0,0,0,1Z"/><path class="cls-3" d="M39.56,90.74a.5.5,0,0,0,0,1h0a20.35,20.35,0,0,0,3.06-.26.5.5,0,1,0-.16-1A19.34,19.34,0,0,1,39.56,90.74Z"/><path class="cls-3" d="M58.41,99.85a.5.5,0,1,0,0,1c.69,0,1.31,0,1.9.09a4,4,0,0,1-.69-1C59.22,99.87,58.84,99.85,58.41,99.85Z"/><path class="cls-3" d="M205.64,113.6a18,18,0,0,0-9.84,2.65,16.11,16.11,0,0,1-8.89,2.41,1,1,0,0,0,0,2,18,18,0,0,0,9.84-2.65,16.11,16.11,0,0,1,8.8-2.41c.14-.68.29-1.35.46-2Z"/><path class="cls-3" d="M280.56,113.6a18,18,0,0,0-9.84,2.65,17.58,17.58,0,0,1-17.78,0,18,18,0,0,0-9.84-2.65,17.32,17.32,0,0,0-8.05,1.72c.16.68.31,1.36.44,2a15.37,15.37,0,0,1,7.61-1.74A16.11,16.11,0,0,1,252,118a19.56,19.56,0,0,0,19.68,0,16.11,16.11,0,0,1,8.89-2.41,1,1,0,0,0,0-2Z"/><path class="cls-3" d="M240.11,107.3a.5.5,0,0,0,.05,1h.05a21.53,21.53,0,0,1,2.28-.12.5.5,0,0,0,0-1A22.53,22.53,0,0,0,240.11,107.3Z"/><path class="cls-3" d="M205,107.17a.5.5,0,0,0,0,1,20.45,20.45,0,0,1,2.77.18c.14-.34.28-.65.42-1A21.17,21.17,0,0,0,205,107.17Z"/><path class="cls-3" d="M254.45,124.92c-.66-.31-1.28-.65-1.9-1a17.58,17.58,0,0,0-9.6-2.59.5.5,0,0,0,0,1,16.53,16.53,0,0,1,9.13,2.47c.63.34,1.27.69,1.95,1a.5.5,0,0,0,.21,0,.5.5,0,0,0,.21-1Z"/><path class="cls-3" d="M280.39,121.35a22.35,22.35,0,0,0-2.37.12.5.5,0,0,0,.05,1h.05a19.28,19.28,0,0,1,2.28-.11.5.5,0,1,0,0-1Z"/><path class="cls-3" d="M269.49,124.64l-.88.42a.5.5,0,0,0,.41.91l.92-.44a.5.5,0,1,0-.45-.89Z"/><path class="cls-3" d="M264.91,126.17a19.34,19.34,0,0,1-2.91.24.5.5,0,0,0,0,1h0a20.35,20.35,0,0,0,3.06-.26.5.5,0,1,0-.16-1Z"/><path class="cls-3" d="M75.17,131.86A32.14,32.14,0,0,1,69,110.77c-11.76,4-19.15,9.74-19.15,16.15,0,12,26,21.75,58,21.75q2.81,0,5.56-.1c-.76-.44-1.5-.89-2.2-1.35C95.78,147.66,82.69,142.13,75.17,131.86Z"/><path class="cls-3" d="M157.22,167.25a18,18,0,0,0-9.84,2.65,17.58,17.58,0,0,1-17.78,0,19.56,19.56,0,0,0-19.68,0,17.58,17.58,0,0,1-17.78,0,18,18,0,0,0-9.84-2.65,18,18,0,0,0-9.84,2.65,16.11,16.11,0,0,1-8.89,2.41,1,1,0,1,0,0,2,18,18,0,0,0,9.84-2.65,17.57,17.57,0,0,1,17.78,0,19.57,19.57,0,0,0,19.68,0,17.58,17.58,0,0,1,17.78,0,19.56,19.56,0,0,0,19.68,0,16.11,16.11,0,0,1,8.89-2.41,1,1,0,0,0,0-2Z"/><path class="cls-3" d="M116.88,161.94a21.53,21.53,0,0,1,2.28-.12.5.5,0,0,0,0-1,22.53,22.53,0,0,0-2.39.12.5.5,0,0,0,.05,1Z"/><path class="cls-3" d="M107.56,165.5a.5.5,0,0,0,.2,0l.92-.44a.5.5,0,1,0-.45-.89l-.88.42a.5.5,0,0,0,.2,1Z"/><path class="cls-3" d="M100.77,166.9a20.35,20.35,0,0,0,3.06-.26.5.5,0,1,0-.16-1,19.34,19.34,0,0,1-2.91.24.5.5,0,0,0,0,1Z"/><path class="cls-3" d="M90.83,164.3c.63.34,1.27.69,1.95,1a.5.5,0,0,0,.21,0,.5.5,0,0,0,.21-1c-.66-.31-1.28-.65-1.9-1a17.58,17.58,0,0,0-9.6-2.59.5.5,0,0,0,0,1A16.52,16.52,0,0,1,90.83,164.3Z"/><path class="cls-3" d="M141.57,179.83a19.34,19.34,0,0,1-2.91.24.5.5,0,0,0,0,1h0a20.35,20.35,0,0,0,3.06-.26.5.5,0,1,0-.16-1Z"/><path class="cls-3" d="M131.11,178.58c-.66-.31-1.28-.65-1.9-1a17.58,17.58,0,0,0-9.6-2.59.5.5,0,1,0,0,1,16.53,16.53,0,0,1,9.13,2.47c.63.34,1.27.69,1.95,1a.5.5,0,0,0,.21,0,.5.5,0,0,0,.21-1Z"/><path class="cls-3" d="M146.15,178.3l-.88.42a.5.5,0,0,0,.41.91l.92-.44a.5.5,0,1,0-.45-.89Z"/><path class="cls-3" d="M157,175a22.35,22.35,0,0,0-2.37.12.5.5,0,0,0,.05,1h.05a19.28,19.28,0,0,1,2.28-.11.5.5,0,1,0,0-1Z"/><path class="cls-1" d="M160.6,111.54c.12-.22.26-.43.39-.64l-.33-.24.69-.3a10.15,10.15,0,0,1,2.29-2.29l-.65-2.15c-2.47,2-6.69,3.12-9.78,3.69a39.81,39.81,0,0,1,6.55,4A10.23,10.23,0,0,1,160.6,111.54Z"/><path class="cls-1" d="M167.92,116.92h0a1,1,0,0,0-.68,1.28l1,3.42c5.22,6,10.75,14.51,15.51,26.68a1,1,0,0,1-.4,1.21c-8.92,5.6-19.08,7.25-27.94,7.25a77.84,77.84,0,0,1-10-.67c-12.25-1.6-24.68-6-33.34-11.91-14.84.68-27.38-4.43-34.46-14.1-5.54-7.57-7-17-4.33-26.77a6,6,0,0,1-4.14,1.09,1,1,0,0,1-.7-.43,6.6,6.6,0,0,1-1.29-5.56,35.1,35.1,0,0,1-3.81.83,1,1,0,0,1-1-.71,10.92,10.92,0,0,1-.17-5.39c0-.22.08-.43.1-.61-1.81-1.3-5.63-5.48-5.88-13.55a11.29,11.29,0,0,1-7.73-6.6,6.72,6.72,0,0,1,.94-6.84C53.28,60.89,59.77,63,62,63.93c1.76-2.25,8.17-9.37,20.8-12.84a4.26,4.26,0,0,1-1-.92,3.6,3.6,0,0,1-.73-2.67,1,1,0,0,1,.68-.78c.32-.1,6.38-2,10.73.22a17.78,17.78,0,0,1-.18-2.77,1,1,0,0,1,1-1c3.78,0,6,1.49,8.19,3.92h.79A35.87,35.87,0,0,1,121,51.85c1.42-2,5.49-6.65,11-4.78a6.72,6.72,0,0,1,4.48,5.26,11.3,11.3,0,0,1-3,9.81,27.88,27.88,0,0,1,2.32,7.4c.87,5.44-.28,10.12-3.33,13.65,1.77,1.21,1.84,2.38,1.69,3A2.53,2.53,0,0,1,132,88c2.46,3.55,6.63,6.18,11.92,7.48,6.59,1.63,12.74.75,15.6-1l-.88-2.92a4,4,0,0,0-.53.27c-2.17,1.45-7.8,2.11-13.47.71a19.57,19.57,0,0,1-8.27-4.12A5.09,5.09,0,0,0,137,86.9a5.16,5.16,0,0,0-.69-4c2.41-3.86,3.23-8.58,2.38-13.87a31,31,0,0,0-1.8-6.43,14.26,14.26,0,0,0,2.5-10.91,9.74,9.74,0,0,0-6.45-7.5c-5.37-1.82-9.88.84-12.75,3.85a37.61,37.61,0,0,0-17.41-4,12.67,12.67,0,0,0-9.48-3.93A4,4,0,0,0,89.51,43a20.93,20.93,0,0,0-8.66.87A4,4,0,0,0,78.14,47a6.25,6.25,0,0,0,.09,2.41,43.55,43.55,0,0,0-17.14,11C56.81,59.14,51,59,47.21,63.67a9.74,9.74,0,0,0-1.4,9.79,14.22,14.22,0,0,0,7.68,7.72,22,22,0,0,0,4.12,10.94A15.72,15.72,0,0,0,59,93.73c0,.12,0,.25,0,.38a13.07,13.07,0,0,0,.47,5.28,4.3,4.3,0,0,0,.89,1.55,4,4,0,0,0,3.25,1.3c.19,0,.44,0,.72-.1A11.51,11.51,0,0,0,66,105.68a4,4,0,0,0,2.78,1.7l.59.06q-.24,1.68-.34,3.33a32.14,32.14,0,0,0,6.18,21.09c7.53,10.27,20.62,15.81,36,15.36.7.45,1.44.9,2.2,1.35a87.71,87.71,0,0,0,42.06,11.21c10.35,0,20.7-2.19,29.51-7.72a4,4,0,0,0,1.6-4.85C180.91,132.77,174.1,123.21,167.92,116.92Z"/><path class="cls-4" d="M159.84,119.3a10.16,10.16,0,0,1-.43-3.28c-5.91-4.68-10.23-5.86-10.32-5.88a1,1,0,0,1-.4-.23,31.8,31.8,0,0,0-8.85-1.74.5.5,0,0,1,0-1,32.52,32.52,0,0,1,8.59,1.61,1,1,0,0,1,.82-.6c2.81-.26,10.35-1.5,13-4.22l-2.44-7.31c-3.9,1.9-10.35,2.24-16.32.77-6.46-1.59-11.43-5.06-14-9.76a1,1,0,0,1,.88-1.48c1.31,0,1.79-.35,1.84-.44s-.32-.69-1.79-1.42a1,1,0,0,1-.25-1.61c7.87-7.63,1.38-19.93,1.32-20.06a1,1,0,0,1-.08-.2l-.08-.27a1,1,0,0,1,.29-1,9.39,9.39,0,0,0,2.94-8.42A4.77,4.77,0,0,0,131.37,49c-5.22-1.77-9.14,4.65-9.18,4.72a1,1,0,0,1-.16.18,23.55,23.55,0,0,1,3.15,2.46.5.5,0,0,1-.71.71,32.66,32.66,0,0,0-6.33-4.39,33.14,33.14,0,0,0-15.86-3.54l-1.25,0a1,1,0,0,1-.73-.34c-1.81-2.06-3.37-3.28-6-3.52a10.06,10.06,0,0,0,1.27,4.72,1,1,0,0,1-1.75,1h0c-2.32-4.12-8.57-3-10.73-2.49a1.77,1.77,0,0,0,.3.57,4.55,4.55,0,0,0,3.15,1.26,1,1,0,0,1,.1,2c-12.52,2.52-19.56,8.94-22.25,11.93a.49.49,0,0,1-.16.39,11.88,11.88,0,0,0-1.13,1.18h0a21,21,0,0,0-3.83,8,.5.5,0,0,1-.49.4h-.1a.5.5,0,0,1-.39-.59A22.21,22.21,0,0,1,61.69,66c-1.13-.51-7.44-3.11-10.58.82a4.77,4.77,0,0,0-.63,4.88,9.39,9.39,0,0,0,7,5.51,1,1,0,0,1,.84,1c0,8.62,4.46,12.52,5.34,12.93a1,1,0,0,1,.57.83,6.55,6.55,0,0,1-.16,1.57A9.36,9.36,0,0,0,64,97.11c1.28-.27,3.54-.85,5-1.24a1,1,0,0,1,.89,1.75c-.2.17-1.94,1.77-.06,4.84,2.42,0,4.34-2.74,4.36-2.76a1,1,0,0,1,.58-.41l.33-.08a1,1,0,0,1,1.18,1.32c-3.9,10.4-2.86,20.48,2.93,28.38,6.73,9.18,18.79,14,33.08,13.26h.05a1,1,0,0,1,.57.18c15.46,10.63,47.72,18.45,68.69,5.91a90.43,90.43,0,0,0-12.39-22.2.49.49,0,0,1-.23.08.53.53,0,0,1,0,.07.51.51,0,0,1-.3.25.5.5,0,0,1-.2,0A10.19,10.19,0,0,1,159.84,119.3Zm12.5,20.87c1.15,3.83-4,8.55-16,8-22-1-31.32-6.9-35-9a72.42,72.42,0,0,1-12-8.12,11.59,11.59,0,0,0,0,3.92,1,1,0,0,1-.78,1.18l-.2,0a1,1,0,0,1-1-.8A14.91,14.91,0,0,1,111,123.07c1.82-1.86,5.13-3.87,10.49-2.89l.1,0h0a.5.5,0,0,1,.67.22,8.67,8.67,0,0,0,6.16,4.61,2.16,2.16,0,0,0,2-.68c2.71-3.61-.8-7.2-1-7.35-4.83-4.08-14.1-6.58-25.56-1.27-6.33,2.94-10.59,7.32-10.59,10.92a.5.5,0,0,1-1,0c0-2.67,1.92-5.6,5.18-8.19-4.81-6.57-8.65-12.38-7.18-15.31,0,0,7.5,1.5,18.5-3,5.51-2.25,8.64-6.77,10.34-9.85a43.49,43.49,0,0,1-16.79,7.14c-17.5,3.53-33.64-3.29-36-15.23s9.82-24.49,27.32-28,33.64,3.29,36,15.23c1.38,6.82-2,13.83-8.45,19.25,1.26,4.25,5.16,16.21,10.07,19.48,6,4,9,3,18,6S169.33,130.17,172.33,140.17Z"/><path class="cls-5" d="M143.93,95.46c-5.28-1.3-9.45-3.93-11.92-7.48a2.53,2.53,0,0,0,2.1-1.78c.15-.64.08-1.8-1.69-3,3.05-3.52,4.21-8.21,3.33-13.65a27.88,27.88,0,0,0-2.32-7.4,11.3,11.3,0,0,0,3-9.81A6.72,6.72,0,0,0,132,47.07c-5.51-1.87-9.58,2.81-11,4.78a35.87,35.87,0,0,0-18.7-4.76h-.79c-2.21-2.43-4.41-3.92-8.19-3.92a1,1,0,0,0-1,1,17.78,17.78,0,0,0,.18,2.77c-4.36-2.23-10.41-.32-10.73-.22a1,1,0,0,0-.68.78,3.6,3.6,0,0,0,.73,2.67,4.26,4.26,0,0,0,1,.92c-12.63,3.47-19,10.6-20.8,12.84-2.24-.92-8.73-3-12.46,1.62a6.72,6.72,0,0,0-.94,6.84A11.29,11.29,0,0,0,56.35,79c.24,8.08,4.06,12.25,5.88,13.55,0,.18-.06.39-.1.61a10.92,10.92,0,0,0,.17,5.39,1,1,0,0,0,1,.71,35.1,35.1,0,0,0,3.81-.83A6.6,6.6,0,0,0,68.43,104a1,1,0,0,0,.7.43,6,6,0,0,0,4.14-1.09c-2.71,9.8-1.21,19.2,4.33,26.77,7.08,9.66,19.62,14.77,34.46,14.1,8.67,5.87,21.1,10.32,33.34,11.91a77.84,77.84,0,0,0,10,.67c8.86,0,19-1.65,27.94-7.25a1,1,0,0,0,.4-1.21c-4.76-12.17-10.3-20.71-15.51-26.68l-1-3.42a1,1,0,0,1,.68-1.28h0l4.47-1.36a1.51,1.51,0,0,1,.68.86l2.26,7.47a.51.51,0,0,0,.64.34c.06,0,.11-.08.18-.1a2.2,2.2,0,0,0,1.09-.52.48.48,0,0,0,.29-.15,10.73,10.73,0,0,0,1.47-2.09,10.69,10.69,0,0,0-8.62-15.7l-.16-.52h.1a1,1,0,0,0,.5-.13c4.54-2.59,5.18-8.55,2.92-12.49a6.09,6.09,0,0,0-3.23-2.86,5.59,5.59,0,0,0-4.69.62,1,1,0,0,0-.13.12l-.54-1.77a10.76,10.76,0,0,0,2.9-3.43,10.69,10.69,0,0,0-6.77-15.39.48.48,0,0,0-.22,0c-.33,0-.45.07-.86,0a5.46,5.46,0,0,0-.65.22.51.51,0,0,0-.34.64l2.26,7.47a1.51,1.51,0,0,1-.09,1.1l-4.5,1.36a1,1,0,0,1-1.28-.68l-2.21-7.28a.47.47,0,0,0-.71-.28.49.49,0,0,0-.65,0,10.2,10.2,0,0,0,6.22,18l.36,1.2.88,2.92C156.67,96.21,150.52,97.09,143.93,95.46Zm24.53,31a.5.5,0,0,0,.2,0,.51.51,0,0,0,.3-.25.53.53,0,0,0,0-.07.49.49,0,0,0,.23-.08,90.43,90.43,0,0,1,12.39,22.2c-21,12.54-53.23,4.72-68.69-5.91a1,1,0,0,0-.57-.18h-.05c-14.29.76-26.35-4.08-33.08-13.26-5.79-7.9-6.83-18-2.93-28.38a1,1,0,0,0-1.18-1.32l-.33.08a1,1,0,0,0-.58.41s-1.94,2.75-4.36,2.76c-1.88-3.07-.14-4.67.06-4.84A1,1,0,0,0,69,95.87c-1.44.39-3.71,1-5,1.24a9.36,9.36,0,0,1,.09-3.62,6.55,6.55,0,0,0,.16-1.57,1,1,0,0,0-.57-.83c-.88-.41-5.34-4.3-5.34-12.93a1,1,0,0,0-.84-1,9.39,9.39,0,0,1-7-5.51,4.77,4.77,0,0,1,.63-4.88c3.14-3.93,9.45-1.33,10.58-.82a22.21,22.21,0,0,0-3.35,7.59.5.5,0,0,0,.39.59h.1a.5.5,0,0,0,.49-.4,21,21,0,0,1,3.83-8h0a11.88,11.88,0,0,1,1.13-1.18.49.49,0,0,0,.16-.39c2.69-3,9.72-9.42,22.25-11.93a1,1,0,0,0-.1-2A4.55,4.55,0,0,1,83.45,49a1.77,1.77,0,0,1-.3-.57c2.17-.53,8.42-1.64,10.73,2.49h0a1,1,0,0,0,1.75-1,10.06,10.06,0,0,1-1.27-4.72c2.59.25,4.15,1.47,6,3.52a1,1,0,0,0,.73.34l1.25,0a33.14,33.14,0,0,1,15.86,3.54A32.66,32.66,0,0,1,124.48,57a.5.5,0,0,0,.71-.71A23.55,23.55,0,0,0,122,53.85a1,1,0,0,0,.16-.18c0-.07,4-6.49,9.18-4.72a4.77,4.77,0,0,1,3.16,3.77,9.39,9.39,0,0,1-2.94,8.42,1,1,0,0,0-.29,1l.08.27a1,1,0,0,0,.08.2c.07.12,6.55,12.42-1.32,20.06a1,1,0,0,0,.25,1.61c1.46.73,1.77,1.33,1.79,1.42s-.53.44-1.84.44a1,1,0,0,0-.88,1.48c2.56,4.7,7.53,8.17,14,9.76,6,1.47,12.42,1.13,16.32-.77l2.44,7.31c-2.61,2.72-10.16,4-13,4.22a1,1,0,0,0-.82.6,32.52,32.52,0,0,0-8.59-1.61.5.5,0,0,0,0,1,31.8,31.8,0,0,1,8.85,1.74,1,1,0,0,0,.4.23c.09,0,4.41,1.2,10.32,5.88a10.17,10.17,0,0,0,9,10.45ZM163,105.92l.65,2.15a10.15,10.15,0,0,0-2.29,2.29c-.13.17-.24.35-.36.54s-.27.42-.39.64a10.23,10.23,0,0,0-.84,2.12,39.81,39.81,0,0,0-6.55-4C156.3,109,160.52,107.92,163,105.92Zm15.87,7.61a9.62,9.62,0,0,1-.73,7.38,9.29,9.29,0,0,1-.76,1.18l-1.94-6.39a2,2,0,0,0-2.52-1.35L167.61,116a2,2,0,0,0-1.35,2.52l1.94,6.39a9.69,9.69,0,0,1-5.26-13.68,9.75,9.75,0,0,1,2.89-3.3.5.5,0,0,0,.19-.55l-1.36-4.48a12.91,12.91,0,0,0,4.3,1.49l.6,1.94a.5.5,0,0,0,.46.35A9.68,9.68,0,0,1,178.86,113.53ZM167.33,91.8a3.38,3.38,0,0,1,2.54-.22,4.13,4.13,0,0,1,2.15,2c1.31,2.3,1.54,6.41-1,8.84a1,1,0,0,0-.54,0c-.77.26-3.62-.25-5.79-1.81a3.92,3.92,0,0,1-1.9-2.91C162.83,94.65,166.56,92.26,167.33,91.8Zm-8.4-11.25.3-.09-.3.09Zm-7.69,1.87A9.63,9.63,0,0,1,152,75a9.35,9.35,0,0,1,.76-1.18l1.94,6.39a2,2,0,0,0,2.52,1.35L162.5,80a2,2,0,0,0,1.35-2.52l-1.94-6.39a9.69,9.69,0,0,1,5.26,13.68,9.73,9.73,0,0,1-2.89,3.3.5.5,0,0,0-.19.55l.76,2.46a12.5,12.5,0,0,0-2.87,3l-1.34-4.43a.5.5,0,0,0-.46-.36A9.67,9.67,0,0,1,151.24,82.42Z"/><path class="cls-6" d="M113.57,84.2a1,1,0,0,0-1.21.73,4.38,4.38,0,0,1-1.89,2.63,3.1,3.1,0,0,1-2.69-.29,1,1,0,0,0-1.37.52,3.23,3.23,0,0,1-1.86,2,5.3,5.3,0,0,1-3.72-.54,1,1,0,0,0-1,1.74,8.2,8.2,0,0,0,3.64,1,5.17,5.17,0,0,0,1.71-.28,4.85,4.85,0,0,0,2.63-2.27,4.56,4.56,0,0,0,3.5-.08c2.25-1,3-3.85,3-4A1,1,0,0,0,113.57,84.2Z"/><path class="cls-7" d="M82.14,79.68A1.5,1.5,0,1,0,85,78.62s-.6-1.59-1.46-3.94a1.5,1.5,0,0,0-2.82,1C81.54,78.08,82.14,79.68,82.14,79.68Z"/><path class="cls-8" d="M115,70.74a1.5,1.5,0,0,0,1.43,1.05,1.48,1.48,0,0,0,.45-.07,1.5,1.5,0,0,0,1-1.88l-1.05-3.37a1.5,1.5,0,0,0-2.86.89Z"/><path class="cls-9" d="M87.91,79.35a1,1,0,0,0-1.14.83,2.9,2.9,0,0,1-1.29,2.27,4.74,4.74,0,0,1-3.58-.12,1,1,0,0,0-.79,1.84,8.22,8.22,0,0,0,3,.6,4.87,4.87,0,0,0,2.37-.56,4.8,4.8,0,0,0,2.31-3.71A1,1,0,0,0,87.91,79.35Z"/><path class="cls-10" d="M121.16,70.69a1,1,0,0,0-1.22.71,2.9,2.9,0,0,1-1.52,2.13,4.74,4.74,0,0,1-3.55-.48,1,1,0,0,0-1,1.75,8,8,0,0,0,3.49.92,4.61,4.61,0,0,0,1.81-.35,4.81,4.81,0,0,0,2.68-3.46A1,1,0,0,0,121.16,70.69Z"/><path class="cls-1" d="M149.33,114.17c-9-3-12-2-18-6-4.91-3.27-8.81-15.23-10.07-19.48,6.42-5.42,9.82-12.43,8.45-19.25-2.41-11.94-18.55-18.76-36-15.23s-29.73,16.07-27.32,28,18.55,18.76,36,15.23a43.49,43.49,0,0,0,16.79-7.14c-1.7,3.08-4.83,7.6-10.34,9.85-11,4.5-18.5,3-18.5,3-1.47,2.93,2.37,8.74,7.18,15.31a29.76,29.76,0,0,1,6-3.64c7.79-3.61,18.89-5.13,26.66,1.44.07.07,4.3,4.38,1.08,8.68a3.08,3.08,0,0,1-2.88,1.08,9.48,9.48,0,0,1-6.36-4.14,1,1,0,0,1-.83.24c-3.62-.66-6.55.13-8.71,2.32a12.2,12.2,0,0,0-3.1,6.57,72.42,72.42,0,0,0,12,8.12c3.68,2.1,13,8,35,9,12,.55,17.15-4.17,16-8C169.33,130.17,158.33,117.17,149.33,114.17ZM81.57,73.78a1.5,1.5,0,0,1,1.92.89c.86,2.35,1.46,3.94,1.46,3.94a1.5,1.5,0,0,1-2.81,1.05s-.6-1.6-1.47-4A1.5,1.5,0,0,1,81.57,73.78ZM86.43,84.2a4.87,4.87,0,0,1-2.37.56,8.22,8.22,0,0,1-3-.6,1,1,0,0,1,.79-1.84,4.74,4.74,0,0,0,3.58.12,2.9,2.9,0,0,0,1.29-2.27,1,1,0,0,1,2,.31A4.8,4.8,0,0,1,86.43,84.2Zm28.46-18.72a1.5,1.5,0,0,1,1.88,1l1.05,3.37a1.5,1.5,0,0,1-1,1.88,1.48,1.48,0,0,1-.45.07A1.5,1.5,0,0,1,115,70.74l-1.05-3.37A1.5,1.5,0,0,1,114.89,65.49ZM97.76,83.11A2,2,0,0,1,98,80.87c3.34-4,11-5.84,13.58-5.26a2,2,0,0,1,1.58,1.33,3.89,3.89,0,0,1-.36,3A16.76,16.76,0,0,1,108.06,85a6.81,6.81,0,0,1-2,1.12,1.56,1.56,0,0,1-.34,0,6.39,6.39,0,0,1-.72,0C102.87,85.9,98.57,84.72,97.76,83.11Zm16.55,2.29c0,.12-.73,2.92-3,4a4.56,4.56,0,0,1-3.5.08,4.85,4.85,0,0,1-2.63,2.27,5.17,5.17,0,0,1-1.71.28,8.2,8.2,0,0,1-3.64-1,1,1,0,0,1,1-1.74,5.3,5.3,0,0,0,3.72.54,3.23,3.23,0,0,0,1.86-2,1,1,0,0,1,1.37-.52,3.1,3.1,0,0,0,2.69.29,4.38,4.38,0,0,0,1.89-2.63,1,1,0,0,1,1.94.47Zm4.9-10a4.61,4.61,0,0,1-1.81.35,8,8,0,0,1-3.49-.92,1,1,0,0,1,1-1.75,4.74,4.74,0,0,0,3.55.48,2.9,2.9,0,0,0,1.52-2.13,1,1,0,0,1,1.93.51A4.81,4.81,0,0,1,119.2,75.38Z"/><path class="cls-11" d="M121.15,122.15a1,1,0,0,0,.83-.24,9.48,9.48,0,0,0,6.36,4.14,3.08,3.08,0,0,0,2.88-1.08c3.22-4.3-1-8.61-1.08-8.68-7.77-6.58-18.86-5.06-26.66-1.44a29.76,29.76,0,0,0-6,3.64c-3.26,2.59-5.18,5.52-5.18,8.19a.5.5,0,0,0,1,0c0-3.59,4.25-8,10.59-10.92,11.46-5.31,20.73-2.81,25.56,1.27.15.15,3.66,3.74,1,7.35a2.16,2.16,0,0,1-2,.68,8.67,8.67,0,0,1-6.16-4.61.5.5,0,0,0-.67-.22h0l-.1,0c-5.36-1-8.67,1-10.49,2.89a14.91,14.91,0,0,0-3.67,12.29,1,1,0,0,0,1,.8l.2,0a1,1,0,0,0,.78-1.18,11.59,11.59,0,0,1,0-3.92,12.2,12.2,0,0,1,3.1-6.57C114.61,122.28,117.54,121.49,121.15,122.15Z"/><path class="cls-1" d="M226,88.63a4,4,0,0,0-6.77-1,77,77,0,0,0-8.5,12.77,8.91,8.91,0,0,0-.92,3.85c-.21.36-.85,1.51-1.6,3.17-.14.3-.28.62-.42,1A39.77,39.77,0,0,0,206,113.6c-.17.65-.33,1.31-.46,2-.06.3-.12.6-.17.91a10.48,10.48,0,0,0,0,3.27,17.27,17.27,0,0,0-1.61,5c-.1.46-.22,1-.34,1.51-.8,3.4.7,5.59,1.79,7.19a10.82,10.82,0,0,1,2.14,5.23c-.34,1.74-7.13,5.53-15.06,7.63a4,4,0,0,0-2.7,2.38c-1.06,2.65,0,5.49,2.61,7.24s7.1,2.66,12.06,2.66c7.17,0,15.28-2,19.87-6.59,8.42-8.42,12.17-17,12.17-27.83a35.69,35.69,0,0,0-.84-6.83c-.13-.66-.28-1.33-.44-2C232.48,104.3,226.84,90.57,226,88.63Zm-4,61.25c-4.08,4.08-11.33,5.68-17.62,5.68-4.52,0-8.55-.82-10.54-2.14s-1.73-3-1.48-3.62a1,1,0,0,1,.67-.6c4.71-1.24,18-5.9,17.26-10.89a13.83,13.83,0,0,0-2.64-6.56c-.95-1.39-1.84-2.7-1.35-4.81.13-.56.25-1.07.35-1.53.54-2.41.87-3.87,1.88-4.91a8.41,8.41,0,0,1-.24-3.49,36.06,36.06,0,0,1,4.13-11.35,1,1,0,0,1,.39-.37,6.43,6.43,0,0,1,.57-3.56,73.68,73.68,0,0,1,8.13-12.19,1,1,0,0,1,.92-.35,1,1,0,0,1,.77.6c.41,1,10.08,24.3,10.08,34.38S229.85,142.06,222,149.87Z"/><path class="cls-12" d="M215.23,102.61c-2.2,4.39,4.89,12.93,7.81,15.85a1,1,0,0,1-1.41,1.41c-.34-.34-6.14-6.2-8.13-11.9a34.18,34.18,0,0,0-3.18,9.36,8.81,8.81,0,0,0,1,4.91l6.79,9.34a1,1,0,0,1-1.52,1.29,40.28,40.28,0,0,1-7-9.61l-.22-.3a25.4,25.4,0,0,0-.74,2.88c-.11.47-.22,1-.35,1.55-.28,1.21.12,1.86,1.05,3.23a15.77,15.77,0,0,1,3,7.4c1,7.13-14.3,11.87-18.09,12.93a1.69,1.69,0,0,0,.75.79c4.24,2.79,19.58,2.77,25.64-3.29,7.4-7.41,10.71-14.9,10.71-24.29,0-8.23-7.09-26.56-9.28-32A69,69,0,0,0,215.23,102.61Z"/><path class="cls-13" d="M222.48,89.18a1,1,0,0,0-.92.35,73.68,73.68,0,0,0-8.13,12.19,6.43,6.43,0,0,0-.57,3.56,1,1,0,0,0-.39.37A36.06,36.06,0,0,0,208.35,117a8.41,8.41,0,0,0,.24,3.49c-1,1-1.34,2.5-1.88,4.91-.11.47-.22,1-.35,1.53-.5,2.11.4,3.42,1.35,4.81a13.83,13.83,0,0,1,2.64,6.56c.71,5-12.56,9.65-17.26,10.89a1,1,0,0,0-.67.6c-.25.63-.61,2.24,1.48,3.62s6,2.14,10.54,2.14c6.29,0,13.54-1.59,17.62-5.68,7.81-7.81,11.29-15.74,11.29-25.71s-9.67-33.4-10.08-34.38A1,1,0,0,0,222.48,89.18Zm-1.86,59.28c-6.06,6.06-21.41,6.08-25.64,3.29a1.69,1.69,0,0,1-.75-.79c3.79-1.06,19.11-5.81,18.09-12.93a15.77,15.77,0,0,0-3-7.4c-.93-1.37-1.33-2-1.05-3.23.13-.56.25-1.08.35-1.55a25.4,25.4,0,0,1,.74-2.88l.22.3a40.28,40.28,0,0,0,7,9.61,1,1,0,0,0,1.52-1.29l-6.79-9.34a8.81,8.81,0,0,1-1-4.91A34.18,34.18,0,0,1,213.5,108c2,5.71,7.79,11.56,8.13,11.9a1,1,0,0,0,1.41-1.41c-2.92-2.92-10-11.45-7.81-15.85a69,69,0,0,1,6.82-10.49c2.2,5.48,9.28,23.81,9.28,32C231.33,133.56,228,141.05,220.63,148.46Z"/><path class="cls-3" d="M174.11,19.71a.59.59,0,0,0,.39-.15,13.28,13.28,0,0,1,7.25-3,18.26,18.26,0,0,1,5.6.07.59.59,0,1,0,.2-1.16,19.35,19.35,0,0,0-6-.08,14.42,14.42,0,0,0-7.88,3.25.59.59,0,0,0,.39,1Z"/><path class="cls-3" d="M197.92,22.94a.59.59,0,0,0,.89-.77,23.25,23.25,0,0,0-2.58-2.54.59.59,0,0,0-.75.9A21.94,21.94,0,0,1,197.92,22.94Z"/><path class="cls-3" d="M147.32,36.68h14.12a.59.59,0,1,0,0-1.18H147.32a.59.59,0,1,0,0,1.18Z"/><path class="cls-3" d="M227.85,36.31a.59.59,0,1,0,1-.57c-.39-.69-1-1.78-1.88-3.05a.59.59,0,1,0-1,.65C226.85,34.58,227.47,35.64,227.85,36.31Z"/><path class="cls-3" d="M244.38,37.39h6.54a.59.59,0,1,0,0-1.18h-6.54a.59.59,0,1,0,0,1.18Z"/><path class="cls-3" d="M207.69,23.4a8.58,8.58,0,0,1,5.63-.61A14.72,14.72,0,0,1,220,26.33a.59.59,0,1,0,.77-.89,15.85,15.85,0,0,0-7.23-3.8,9.75,9.75,0,0,0-6.39.72.59.59,0,0,0,.54,1Z"/><path class="cls-3" d="M232.61,37.39h1.18a.59.59,0,0,0,0-1.18h-1.18a.59.59,0,0,0,0,1.18Z"/><path class="cls-3" d="M201.19,27.67a.59.59,0,1,0,1-.56c-.17-.32-.37-.68-.6-1a.59.59,0,0,0-1,.6C200.83,27,201,27.36,201.19,27.67Z"/><path class="cls-3" d="M171.06,30.57h.08a.59.59,0,0,0,.5-.66c-.05-.37-.09-.75-.13-1.14a.59.59,0,0,0-1.17.09c0,.37.07.73.12,1.08,0,0,0,.08,0,.13A.59.59,0,0,0,171.06,30.57Z"/><path class="cls-3" d="M173.23,36.41a.59.59,0,0,0,0-.56s-.25-.57-.56-1.48a.59.59,0,0,0-1.11.38c.1.29.19.54.27.76h-1a.59.59,0,0,0,0,1.18h1.89A.59.59,0,0,0,173.23,36.41Z"/><path class="cls-1" d="M146.88,41.9H251.65a1.18,1.18,0,0,0,0-2.35H227.31c-2.17-4-7.57-12.57-14.31-13.86-9-1.71-10.74,7.22-10.74,7.22s-6-15.48-21-13.43c-7.2,1-9.88,4.78-10.59,8.8a.59.59,0,0,1,.87.47c0,.39.07.77.13,1.14a.59.59,0,0,1-.5.66h-.08a.59.59,0,0,1-.58-.51s0-.08,0-.13A20.23,20.23,0,0,0,171,35.5h.86c-.08-.21-.17-.47-.27-.76a.59.59,0,0,1,1.11-.38c.31.91.56,1.47.56,1.48a.59.59,0,0,1-.54.83h-1.43a23.12,23.12,0,0,0,1,2.87H146.88a1.18,1.18,0,0,0,0,2.35Z"/><path class="cls-14" d="M160.64,89.66,162,94.09a12.5,12.5,0,0,1,2.87-3l-.76-2.46a.5.5,0,0,1,.19-.55,9.73,9.73,0,0,0,2.89-3.3,9.69,9.69,0,0,0-5.26-13.68l1.94,6.39A2,2,0,0,1,162.5,80l-5.31,1.61a2,2,0,0,1-2.52-1.35l-1.94-6.39A9.35,9.35,0,0,0,152,75a9.7,9.7,0,0,0,8.21,14.26A.5.5,0,0,1,160.64,89.66Z"/><path class="cls-15" d="M169.54,106.31l-.6-1.94a12.91,12.91,0,0,1-4.3-1.49l1.36,4.48a.5.5,0,0,1-.19.55,9.75,9.75,0,0,0-2.89,3.3,9.69,9.69,0,0,0,5.26,13.68l-1.94-6.39a2,2,0,0,1,1.35-2.52l5.31-1.61a2,2,0,0,1,2.52,1.35l1.94,6.39a9.29,9.29,0,0,0,.76-1.18A9.69,9.69,0,0,0,170,106.66.5.5,0,0,1,169.54,106.31Z"/><polygon class="cls-16" points="158.93 80.54 158.93 80.54 159.23 80.45 158.93 80.54"/><path class="cls-17" d="M162.83,97.67a3.92,3.92,0,0,0,1.9,2.91c2.16,1.56,5,2.07,5.79,1.81a1,1,0,0,1,.54,0c2.5-2.43,2.27-6.54,1-8.84a4.13,4.13,0,0,0-2.15-2,3.38,3.38,0,0,0-2.54.22C166.56,92.26,162.83,94.65,162.83,97.67Z"/><path class="cls-3" d="M92.87,13.95h.55a.59.59,0,1,0,0-1.18h-.55a.59.59,0,1,0,0,1.18Z"/><path class="cls-3" d="M50.08,5.15a.59.59,0,0,0,.45-.21,5.63,5.63,0,0,1,2.69-1.66.59.59,0,0,0-.35-1.13,6.8,6.8,0,0,0-3.24,2,.59.59,0,0,0,.45,1Z"/><path class="cls-3" d="M57.63,2.92a9.06,9.06,0,0,1,1.08.22l.16,0A.59.59,0,0,0,59,2a10.37,10.37,0,0,0-1.22-.25.6.6,0,0,0-.67.5A.59.59,0,0,0,57.63,2.92Z"/><path class="cls-3" d="M84.61,13.95h3.54a.59.59,0,1,0,0-1.18H84.61a.59.59,0,1,0,0,1.18Z"/><path class="cls-3" d="M50.36,13.54a.59.59,0,0,0-.59-.59H35.6a.59.59,0,0,0,0,1.18H49.77A.59.59,0,0,0,50.36,13.54Z"/><path class="cls-3" d="M67.08,9.95s0-.11.06-.24a4.38,4.38,0,0,1,4.13-3.43,5.93,5.93,0,0,1,1.11.11,9.48,9.48,0,0,1,4.67,2.73.59.59,0,0,0,.82-.85,10.62,10.62,0,0,0-5.27-3,7.11,7.11,0,0,0-1.33-.13,5.48,5.48,0,0,0-5.19,4,.59.59,0,0,0-.23.74l.09.21a.61.61,0,0,0,.55.38.59.59,0,0,0,.58-.47Z"/><path class="cls-1" d="M35.43,18.09H93.9a1.18,1.18,0,0,0,0-2.36H80.06c-1.2-2.21-4.22-7-8-7.75A4.62,4.62,0,0,0,67.14,9.7c0,.13-.06.22-.06.24a.59.59,0,0,1-.38.44A6.4,6.4,0,0,0,66.07,12S62.74,3.38,54.34,4.52c-6.18.84-6.4,5.4-5.82,8.43h1.25a.59.59,0,1,1,0,1.18h-1a12.79,12.79,0,0,0,.55,1.59H35.43a1.18,1.18,0,0,0,0,2.36Z"/><path class="cls-18" d="M105.71,86.16a1.56,1.56,0,0,0,.34,0,6.81,6.81,0,0,0,2-1.12,16.76,16.76,0,0,0,4.73-5.11,3.89,3.89,0,0,0,.36-3,2,2,0,0,0-1.58-1.33C109,75,101.34,76.84,98,80.87a2,2,0,0,0-.24,2.24c.82,1.61,5.11,2.79,7.24,3A6.39,6.39,0,0,0,105.71,86.16Z"/><rect id="_Slice_" data-name="&lt;Slice&gt;" class="cls-2" width="283" height="184"/></g></g></svg>PK
!<�i��8chrome/devtools-shim/content/aboutdevtools/subscribe.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 file contains the styles for the newsletter subscription form on about:devtools.
 * It is largely inspired from https://mozilla.github.io/basket-example/
 */

.newsletter-title {
  font-size: 17px;
  font-weight: 500;
  margin-top: 26px;
  margin-bottom: -4px;
}

#newsletter-errors {
  /* Hidden by default */
  display: none;

  margin-bottom: 20px;
  padding: 10px;
  border-radius: 2px;

  background-color: var(--red-50);
  color: var(--white);
}

#newsletter-errors.show {
  display: block;
}

#newsletter-errors .error {
  margin: 0;
  margin-bottom: 10px;
}

#newsletter-errors .error:last-child {
  margin-bottom: 0;
}

#newsletter-thanks {
  /* Hidden by default */
  display: none;
}

#newsletter-thanks.show {
  display: block;
}

.newsletter-form-section {
  display: block;
  margin-bottom: 20px;
  width: 320px;
}

#newsletter-privacy {
  display: flex;

  /* The privacy section is hidden by default and only displayed on focus */
  height: 0;
  overflow: hidden;

  padding: 3px 0 0 3px;
  margin: -3px 0 -20px -3px;
}

#newsletter-privacy.animate {
  transition: all 0.25s cubic-bezier(.15,.75,.35,.9);
}

#newsletter-privacy label {
  line-height: var(--line-height);
}

#privacy {
  width: 20px;
  height: 20px;
  margin-top: 2px;
  margin-inline-end: 10px;
  flex-shrink: 0;
}

#email {
  width: 100%;
  box-sizing: border-box;
  padding: 8px;
}

#newsletter-form input {
  border-color: var(--grey-90-alpha-30);
}

#newsletter-form input:hover {
  border-color: var(--grey-90-alpha-50);
}

#newsletter-form input:focus {
  border-color: var(--blue-50);
  box-shadow: 0 0 0px 3px var(--blue-50-alpha20);
}

#newsletter-form::placeholder {
  color: var(--grey-90-alpha-40);
}

#newsletter-submit {
  display: block;
  padding: 8px 20px;
}
PK
!<�.@��7chrome/devtools-shim/content/aboutdevtools/subscribe.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 handles the newsletter subscription form on about:devtools.
 * It is largely inspired from https://mozilla.github.io/basket-example/
 */

window.addEventListener("load", function () {
  const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm", {});

  // Timeout for the subscribe XHR.
  const REQUEST_TIMEOUT = 5000;

  const ABOUTDEVTOOLS_STRINGS = "chrome://devtools-shim/locale/aboutdevtools.properties";
  const aboutDevtoolsBundle = Services.strings.createBundle(ABOUTDEVTOOLS_STRINGS);

  let emailInput = document.getElementById("email");
  let newsletterErrors = document.getElementById("newsletter-errors");
  let newsletterForm = document.getElementById("newsletter-form");
  let newsletterPrivacySection = document.getElementById("newsletter-privacy");
  let newsletterThanks = document.getElementById("newsletter-thanks");

  /**
   * Update the error panel to display the provided errors. If the argument is null or
   * empty, a default error message will be displayed.
   *
   * @param {Array} errors
   *        Array of strings, each item being an error message to display.
   */
  function updateErrorPanel(errors) {
    clearErrorPanel();

    if (!errors || errors.length == 0) {
      errors = [aboutDevtoolsBundle.GetStringFromName("newsletter.error.unknown")];
    }

    // Create errors markup.
    let fragment = document.createDocumentFragment();
    for (let error of errors) {
      let item = document.createElement("p");
      item.classList.add("error");
      item.appendChild(document.createTextNode(error));
      fragment.appendChild(item);
    }

    newsletterErrors.appendChild(fragment);
    newsletterErrors.classList.add("show");
  }

  /**
   * Hide the error panel and remove all errors.
   */
  function clearErrorPanel() {
    newsletterErrors.classList.remove("show");
    newsletterErrors.innerHTML = "";
  }

  // Show the additional form fields on focus of the email input.
  function onEmailInputFocus() {
    // Create a hidden measuring container, append it to the parent of the privacy section
    let container = document.createElement("div");
    container.style.cssText = "visibility: hidden; overflow: hidden; position: absolute";
    newsletterPrivacySection.parentNode.appendChild(container);

    // Clone the privacy section, append the clone to the measuring container.
    let clone = newsletterPrivacySection.cloneNode(true);
    container.appendChild(clone);

    // Measure the target height of the privacy section.
    clone.style.height = "auto";
    let height = clone.offsetHeight;

    // Cleanup the measuring container.
    container.remove();

    // Set the animate class and set the height to the measured height.
    newsletterPrivacySection.classList.add("animate");
    newsletterPrivacySection.style.cssText = `height: ${height}px; margin-bottom: 0;`;
  }

  // XHR subscribe; handle errors; display thanks message on success.
  function onFormSubmit(evt) {
    evt.preventDefault();
    evt.stopPropagation();

    // New submission, clear old errors
    clearErrorPanel();

    let xhr = new XMLHttpRequest();

    xhr.onload = function (r) {
      if (r.target.status >= 200 && r.target.status < 300) {
        let {response} = r.target;

        if (response.success === true) {
          // Hide form and show success message.
          newsletterForm.style.display = "none";
          newsletterThanks.classList.add("show");
        } else {
          // We trust the error messages from the service to be meaningful for the user.
          updateErrorPanel(response.errors);
        }
      } else {
        let {status, statusText} = r.target;
        let statusInfo = `${status} - ${statusText}`;
        let error = aboutDevtoolsBundle
          .formatStringFromName("newsletter.error.common", [statusInfo], 1);
        updateErrorPanel([error]);
      }
    };

    xhr.onerror = () => {
      updateErrorPanel();
    };

    xhr.ontimeout = () => {
      let error = aboutDevtoolsBundle.GetStringFromName("newsletter.error.timeout");
      updateErrorPanel([error]);
    };

    let url = newsletterForm.getAttribute("action");

    xhr.open("POST", url, true);
    xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
    xhr.timeout = REQUEST_TIMEOUT;
    xhr.responseType = "json";

    // Create form data.
    let formData = new FormData(newsletterForm);
    formData.append("source_url", document.location.href);

    let params = new URLSearchParams(formData);

    // Send the request.
    xhr.send(params.toString());
  }

  // Attach event listeners.
  newsletterForm.addEventListener("submit", onFormSubmit);
  emailInput.addEventListener("focus", onEmailInputFocus);
}, { once: true });
PK
!<��Q=]]9chrome/devtools/content/aboutdebugging/aboutdebugging.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

html, body {
  height: 100%;
  width: 100%;
}

h2, h3, h4 {
  margin-bottom: 10px;
}

button {
  padding-left: 20px;
  padding-right: 20px;
  min-width: 100px;
  margin: 0 4px;
}

/* Category panels */

#categories {
  display: flex;
  flex-direction: column;
}

.category {
  border: 0 solid transparent;
  display: flex;
  flex-direction: row;
  align-items: center;
}

.category-name {
  cursor: default;
}

.app {
  height: 100%;
  width: 100%;
  display: flex;
  flex-direction: row;
}

.main-content {
  flex: 1;
}

.panel {
  max-width: 800px;
  margin-bottom: 35px;
}

/* Targets */

.target-list {
  margin: 0;
  padding: 0;
}

.target-container {
  margin-top: 5px;
  min-height: 34px;
  display: flex;
  flex-direction: row;
  align-items: start;
}

.target-icon {
  height: 24px;
  width: 24px;
  margin-inline-end: 5px;
}

.target-icon:not([src]) {
  display: none;
}

.inverted-icons .target-icon {
  filter: invert(30%);
}

.target {
  flex: 1;
  margin-top: 2px;
  /* This is silly: https://bugzilla.mozilla.org/show_bug.cgi?id=1086218#c4. */
  min-width: 0;
}

.target-details {
  margin: 0;
  padding: 0;
  list-style-type: none
}

.target-detail {
  display: flex;
  font-size: 12px;
  margin-top: 7px;
  margin-bottom: 7px;
}

.target-detail a {
  cursor: pointer;
  white-space: nowrap;
}

.target-detail strong {
  white-space: nowrap;
}

.target-detail span {
  /* Truncate items that are too long (e.g. URLs that would break the UI). */
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.target-detail > :not(:first-child) {
  margin-inline-start: 8px;
}

.target-status {
  box-sizing: border-box;
  display: inline-block;

  min-width: 50px;
  margin-top: 4px;
  margin-inline-end: 5px;
  padding: 2px;

  border-width: 1px;
  border-style: solid;

  font-size: 0.6em;
  text-align: center;
}

.target-status-stopped {
  border-color: grey;
  background-color: lightgrey;
}

.target-status-running {
  border-color: limegreen;
  background-color: palegreen;
}

.target-name {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.addons-controls {
  display: flex;
  flex-direction: row;
}

.addons-install-error,
.service-worker-multi-process {
  padding: 5px 10px;
  margin-top: 5px;
  margin-inline-end: 4px;
}

.addons-install-error {
  align-items: center;
  background-color: #f3b0b0;
  display: flex;
  justify-content: space-between;
}

.service-worker-multi-process {
  background-color: #ffeebb;
  line-height: 1.5em;
}

.service-worker-multi-process .update-button {
  margin: 5px 0;
}

.warning {
  background-image: url(chrome://devtools/skin/images/alerticon-warning.png);
  background-size: 13px 12px;
  display: inline-block;
  width: 13px;
  height: 12px;
  margin-inline-end: 10px;
}

@media (min-resolution: 1.1dppx) {
  .warning {
    background-image: url(chrome://devtools/skin/images/[email protected]);
  }
}

.addons-install-error .warning,
.service-worker-multi-process .warning {
  /* The warning icon can be hard to see on red / yellow backgrounds, this turns the icon
  to a black icon. */
  filter: brightness(0%);
}

.addons-options {
  flex: 1;
}

.service-worker-disabled-label,
.addons-debugging-label,
.addons-web-ext-tip {
  display: inline-block;
  margin-inline-end: 1ch;
}

.addons-tip {
  display: flex;
  align-items: center;
  margin-top: 1em;
  margin-bottom: 1em;
  height: 24px;
}

.addons-tip-icon {
  width: 24px;
  height: 24px;
  background-image: url(chrome://devtools/skin/images/help.svg);
  background-repeat: no-repeat;
  background-size: 24px;
  margin-inline-end: 8px;
}

.error-page {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  width: 100%;
  height: 100%;
}

.error-page .error-page-details {
  color: gray;
}

#addons-panel h2 {
  font-size: 1.5rem;
  font-weight: bold;
}

.addon-target-container {
  background: #fff;
  box-shadow: 0 0 1px rgba(0, 0, 0, 0.12);
  list-style-type: none;
  font-size: 13px;
  margin: 0 0 8px;
  padding: 4px 16px;
  transition: box-shadow 150ms;
}

.addon-target-container:hover {
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.24);
}

.addon-target-container .target {
  align-items: center;
  display: flex;
  margin: 0;
  padding: 16px 0;
}

.addon-target-name {
  font-size: 15px;
  font-weight: 600;
}

.addon-target-actions {
  border-top: 1px solid rgba(0, 0, 0, 0.2);
}

.addon-target-container .target-icon {
  margin-inline-end: 16px;
}

.addon-target-container .name {
  align-self: center;
  font-size: 16px;
  font-weight: 600;
}

.addon-target-info {
  display: grid;
  grid-template-columns: 128px 1fr;
}

.addon-target-info-label {
  padding-inline-end: 8px;
  padding-bottom: 8px;
}

.addon-target-info-label:last-of-type {
  padding-bottom: 16px;
}

.addon-target-info-content {
  margin: 0;
}

.addon-target-info-more {
  padding-left: 1ch;
}

.addon-target-button {
  background: none;
  border: none;
  color: #0087ff;
  font-size: 13px;
  margin: 12px;
  min-width: auto;
  padding: 4px;
  transition: color 150ms;
}

.addon-target-button:active,
.addon-target-button:hover,
.addon-target-button:enabled:hover:active {
  background: none;
}

.addon-target-button:disabled {
  color: #999;
  opacity: 1;
}

.addon-target-button:enabled:focus,
.addon-target-button:enabled:hover {
  background: none;
  color: #0052cc;
  cursor: pointer;
  text-decoration: underline;
}

.addon-target-button:enabled:hover:active {
  color: #003399;
  text-decoration: none;
}

.addon-target-button:first-of-type {
  /* Subtract the start padding so the button is still a bigger click target but
   * lines up with the icon. */
  margin-inline-start: -4px;
}

.addon-target-messages {
  border-bottom: 1px solid rgba(0, 0, 0, 0.2);
  margin-bottom: 16px;
  padding: 0 0 12px 0;
}

.addon-target-message {
  list-style-type: none;
  padding: 4px 0;
}

.addon-target-message:first-of-type {
  padding-top: 0;
}

.addon-target-warning-message {
  color: #a47f00;
}

/* We want the ellipsis on the left-hand-side, so make the parent RTL
 * with an ellipsis and the child can be LTR. */
.file-path {
  direction: rtl;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.file-path-inner {
  direction: ltr;
  unicode-bidi: plaintext;
}
PK
!<K�@�;chrome/devtools/content/aboutdebugging/aboutdebugging.xhtml<?xml version="1.0" encoding="UTF-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/. -->

<!DOCTYPE html [
<!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd"> %htmlDTD;
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd"> %globalDTD;
<!ENTITY % aboutdebuggingDTD SYSTEM "chrome://devtools/locale/aboutdebugging.dtd"> %aboutdebuggingDTD;
]>

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>&aboutDebugging.fullTitle;</title>
    <link rel="stylesheet" href="chrome://global/skin/in-content/common.css" type="text/css"/>
    <link rel="stylesheet" href="chrome://devtools/content/aboutdebugging/aboutdebugging.css"  type="text/css"/>
    <script type="application/javascript" src="chrome://devtools/content/aboutdebugging/initializer.js"></script>
  </head>
  <body id="body" dir="&locale.dir;">
  </body>
</html>
PK
!<�� 995chrome/devtools/content/aboutdebugging/initializer.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 */
/* globals Telemetry */

"use strict";

const { loader } = ChromeUtils.import(
  "resource://devtools/shared/Loader.jsm", {});
const { BrowserLoader } = ChromeUtils.import(
  "resource://devtools/client/shared/browser-loader.js", {});
const { Services } = ChromeUtils.import(
  "resource://gre/modules/Services.jsm", {});

loader.lazyRequireGetter(this, "Telemetry",
  "devtools/client/shared/telemetry");

const { require } = BrowserLoader({
  baseURI: "resource://devtools/client/aboutdebugging/",
  window
});

const { createFactory } = require("devtools/client/shared/vendor/react");
const { render, unmountComponentAtNode } = require("devtools/client/shared/vendor/react-dom");
const EventEmitter = require("devtools/shared/event-emitter");

const AboutDebuggingApp = createFactory(require("./components/Aboutdebugging"));
const { createClient } = require("./modules/connect");

var AboutDebugging = {
  async init() {
    if (!Services.prefs.getBoolPref("devtools.enabled", true)) {
      // If DevTools are disabled, navigate to about:devtools.
      window.location = "about:devtools?reason=AboutDebugging";
      return;
    }

    let {connect, client} = await createClient();

    this.client = client;
    await this.client.connect();

    let telemetry = new Telemetry();

    render(AboutDebuggingApp({ client, connect, telemetry }),
      document.querySelector("#body"));
  },

  destroy() {
    unmountComponentAtNode(document.querySelector("#body"));

    if (this.client) {
      this.client.close();
      this.client = null;
    }
  },
};

// Used to track async requests in tests.  See bug 1444424 for better ideas.
EventEmitter.decorate(AboutDebugging);

window.addEventListener("DOMContentLoaded", function () {
  AboutDebugging.init();
}, {once: true});

window.addEventListener("unload", function () {
  AboutDebugging.destroy();
}, {once: true});
PK
!<����4�4Bchrome/devtools/content/animationinspector/animation-controller.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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/. */

/* animation-panel.js is loaded in the same scope but we don't use
   import-globals-from to avoid infinite loops since animation-panel.js already
   imports globals from animation-controller.js */
/* globals AnimationsPanel */
/* eslint no-unused-vars: [2, {"vars": "local", "args": "none"}] */

"use strict";

var { loader, require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
var { Task } = require("devtools/shared/task");

loader.lazyRequireGetter(this, "promise");
loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
loader.lazyRequireGetter(this, "AnimationsFront", "devtools/shared/fronts/animation", true);

const { LocalizationHelper } = require("devtools/shared/l10n");
const L10N =
      new LocalizationHelper("devtools/client/locales/animationinspector.properties");

// Global toolbox/inspector, set when startup is called.
var gToolbox, gInspector;

/**
 * Startup the animationinspector controller and view, called by the sidebar
 * widget when loading/unloading the iframe into the tab.
 */
var startup = Task.async(function* (inspector) {
  gInspector = inspector;
  gToolbox = inspector.toolbox;

  // Don't assume that AnimationsPanel is defined here, it's in another file.
  if (!typeof AnimationsPanel === "undefined") {
    throw new Error("AnimationsPanel was not loaded in the " +
                    "animationinspector window");
  }

  // Startup first initalizes the controller and then the panel, in sequence.
  // If you want to know when everything's ready, do:
  // AnimationsPanel.once(AnimationsPanel.PANEL_INITIALIZED)
  yield AnimationsController.initialize();
  yield AnimationsPanel.initialize();
});

/**
 * Shutdown the animationinspector controller and view, called by the sidebar
 * widget when loading/unloading the iframe into the tab.
 */
var shutdown = Task.async(function* () {
  yield AnimationsController.destroy();
  // Don't assume that AnimationsPanel is defined here, it's in another file.
  if (typeof AnimationsPanel !== "undefined") {
    yield AnimationsPanel.destroy();
  }
  gToolbox = gInspector = null;
});

// This is what makes the sidebar widget able to load/unload the panel.
function setPanel(panel) {
  return startup(panel).catch(console.error);
}
function destroy() {
  return shutdown().catch(console.error);
}

/**
 * Get all the server-side capabilities (traits) so the UI knows whether or not
 * features should be enabled/disabled.
 * @param {Target} target The current toolbox target.
 * @return {Object} An object with boolean properties.
 */
var getServerTraits = Task.async(function* (target) {
  let config = [
    { name: "hasToggleAll", actor: "animations",
      method: "toggleAll" },
    { name: "hasToggleSeveral", actor: "animations",
      method: "toggleSeveral" },
    { name: "hasSetCurrentTime", actor: "animationplayer",
      method: "setCurrentTime" },
    { name: "hasMutationEvents", actor: "animations",
      method: "stopAnimationPlayerUpdates" },
    { name: "hasSetPlaybackRate", actor: "animationplayer",
      method: "setPlaybackRate" },
    { name: "hasSetPlaybackRates", actor: "animations",
      method: "setPlaybackRates" },
    { name: "hasTargetNode", actor: "domwalker",
      method: "getNodeFromActor" },
    { name: "hasSetCurrentTimes", actor: "animations",
      method: "setCurrentTimes" },
    { name: "hasGetFrames", actor: "animationplayer",
      method: "getFrames" },
    { name: "hasGetProperties", actor: "animationplayer",
      method: "getProperties" },
    { name: "hasSetWalkerActor", actor: "animations",
      method: "setWalkerActor" },
    { name: "hasGetAnimationTypes", actor: "animationplayer",
      method: "getAnimationTypes" },
  ];

  let traits = {};
  for (let {name, actor, method} of config) {
    traits[name] = yield target.actorHasMethod(actor, method);
  }

  return traits;
});

/**
 * The animationinspector controller's job is to retrieve AnimationPlayerFronts
 * from the server. It is also responsible for keeping the list of players up to
 * date when the node selection changes in the inspector, as well as making sure
 * no updates are done when the animationinspector sidebar panel is not visible.
 *
 * AnimationPlayerFronts are available in AnimationsController.animationPlayers.
 *
 * Usage example:
 *
 * AnimationsController.on(AnimationsController.PLAYERS_UPDATED_EVENT,
 *                         onPlayers);
 * function onPlayers() {
 *   for (let player of AnimationsController.animationPlayers) {
 *     // do something with player
 *   }
 * }
 */
var AnimationsController = {
  PLAYERS_UPDATED_EVENT: "players-updated",
  ALL_ANIMATIONS_TOGGLED_EVENT: "all-animations-toggled",

  initialize: Task.async(function* () {
    if (this.initialized) {
      yield this.initialized;
      return;
    }

    let resolver;
    this.initialized = new Promise(resolve => {
      resolver = resolve;
    });

    this.onPanelVisibilityChange = this.onPanelVisibilityChange.bind(this);
    this.onNewNodeFront = this.onNewNodeFront.bind(this);
    this.onAnimationMutations = this.onAnimationMutations.bind(this);

    let target = gInspector.target;
    this.animationsFront = new AnimationsFront(target.client, target.form);

    // Expose actor capabilities.
    this.traits = yield getServerTraits(target);

    if (this.destroyed) {
      console.warn("Could not fully initialize the AnimationsController");
      return;
    }

    // Let the AnimationsActor know what WalkerActor we're using. This will
    // come in handy later to return references to DOM Nodes.
    if (this.traits.hasSetWalkerActor) {
      yield this.animationsFront.setWalkerActor(gInspector.walker);
    }

    this.startListeners();
    yield this.onNewNodeFront();

    resolver();
  }),

  destroy: Task.async(function* () {
    if (!this.initialized) {
      return;
    }

    if (this.destroyed) {
      yield this.destroyed;
      return;
    }

    let resolver;
    this.destroyed = new Promise(resolve => {
      resolver = resolve;
    });

    this.stopListeners();
    this.destroyAnimationPlayers();
    this.nodeFront = null;

    if (this.animationsFront) {
      this.animationsFront.destroy();
      this.animationsFront = null;
    }
    resolver();
  }),

  startListeners: function () {
    // Re-create the list of players when a new node is selected, except if the
    // sidebar isn't visible.
    gInspector.selection.on("new-node-front", this.onNewNodeFront);
    gInspector.sidebar.on("select", this.onPanelVisibilityChange);
    gToolbox.on("select", this.onPanelVisibilityChange);
  },

  stopListeners: function () {
    gInspector.selection.off("new-node-front", this.onNewNodeFront);
    gInspector.sidebar.off("select", this.onPanelVisibilityChange);
    gToolbox.off("select", this.onPanelVisibilityChange);
    if (this.isListeningToMutations) {
      this.animationsFront.off("mutations", this.onAnimationMutations);
    }
  },

  isPanelVisible: function () {
    return gToolbox.currentToolId === "inspector" &&
           gInspector.sidebar &&
           gInspector.sidebar.getCurrentTabID() == "animationinspector";
  },

  onPanelVisibilityChange: Task.async(function* () {
    if (this.isPanelVisible()) {
      this.onNewNodeFront();
    }
  }),

  onNewNodeFront: Task.async(function* () {
    // Ignore if the panel isn't visible.
    // Or the node selection hasn't changed and no animation mutations event occurs during
    // hidden.
    if (!this.isPanelVisible() || (this.nodeFront === gInspector.selection.nodeFront &&
                                   !this.mutationsDetectedWhileHidden)) {
      return;
    }

    this.mutationsDetectedWhileHidden = false;

    this.nodeFront = gInspector.selection.nodeFront;
    let done = gInspector.updating("animationscontroller");

    if (!gInspector.selection.isConnected() ||
        !gInspector.selection.isElementNode()) {
      this.destroyAnimationPlayers();
      this.emit(this.PLAYERS_UPDATED_EVENT);
      done();
      return;
    }

    yield this.refreshAnimationPlayers(this.nodeFront);
    this.emit(this.PLAYERS_UPDATED_EVENT, this.animationPlayers);

    done();
  }),

  /**
   * Toggle (pause/play) all animations in the current target.
   */
  toggleAll: function () {
    if (!this.traits.hasToggleAll) {
      return promise.resolve();
    }

    return this.animationsFront.toggleAll()
      .then(() => this.emit(this.ALL_ANIMATIONS_TOGGLED_EVENT, this))
      .catch(console.error);
  },

  /**
   * Similar to toggleAll except that it only plays/pauses the currently known
   * animations (those listed in this.animationPlayers).
   * @param {Boolean} shouldPause True if the animations should be paused, false
   * if they should be played.
   * @return {Promise} Resolves when the playState has been changed.
   */
  toggleCurrentAnimations: Task.async(function* (shouldPause) {
    if (this.traits.hasToggleSeveral) {
      yield this.animationsFront.toggleSeveral(this.animationPlayers,
                                               shouldPause);
    } else {
      // Fall back to pausing/playing the players one by one, which is bound to
      // introduce some de-synchronization.
      for (let player of this.animationPlayers) {
        if (shouldPause) {
          yield player.pause();
        } else {
          yield player.play();
        }
      }
    }
  }),

  /**
   * Set all known animations' currentTimes to the provided time.
   * @param {Number} time.
   * @param {Boolean} shouldPause Should the animations be paused too.
   * @return {Promise} Resolves when the current time has been set.
   */
  setCurrentTimeAll: Task.async(function* (time, shouldPause) {
    if (this.traits.hasSetCurrentTimes) {
      yield this.animationsFront.setCurrentTimes(this.animationPlayers, time,
                                                 shouldPause);
    } else {
      // Fall back to pausing and setting the current time on each player, one
      // by one, which is bound to introduce some de-synchronization.
      for (let animation of this.animationPlayers) {
        if (shouldPause) {
          yield animation.pause();
        }
        yield animation.setCurrentTime(time);
      }
    }
  }),

  /**
   * Set all known animations' playback rates to the provided rate.
   * @param {Number} rate.
   * @return {Promise} Resolves when the rate has been set.
   */
  setPlaybackRateAll: Task.async(function* (rate) {
    if (this.traits.hasSetPlaybackRates) {
      // If the backend can set all playback rates at the same time, use that.
      yield this.animationsFront.setPlaybackRates(this.animationPlayers, rate);
    } else if (this.traits.hasSetPlaybackRate) {
      // Otherwise, fall back to setting each rate individually.
      for (let animation of this.animationPlayers) {
        yield animation.setPlaybackRate(rate);
      }
    }
  }),

  // AnimationPlayerFront objects are managed by this controller. They are
  // retrieved when refreshAnimationPlayers is called, stored in the
  // animationPlayers array, and destroyed when refreshAnimationPlayers is
  // called again.
  animationPlayers: [],

  refreshAnimationPlayers: Task.async(function* (nodeFront) {
    this.destroyAnimationPlayers();

    this.animationPlayers = yield this.animationsFront
                                      .getAnimationPlayersForNode(nodeFront);

    // Start listening for animation mutations only after the first method call
    // otherwise events won't be sent.
    if (!this.isListeningToMutations && this.traits.hasMutationEvents) {
      this.animationsFront.on("mutations", this.onAnimationMutations);
      this.isListeningToMutations = true;
    }
  }),

  onAnimationMutations: function (changes) {
    // Insert new players into this.animationPlayers when new animations are
    // added.
    for (let {type, player} of changes) {
      if (type === "added") {
        this.animationPlayers.push(player);
      }

      if (type === "removed") {
        let index = this.animationPlayers.indexOf(player);
        this.animationPlayers.splice(index, 1);
      }
    }

    if (this.isPanelVisible()) {
      // Let the UI know the list has been updated.
      this.emit(this.PLAYERS_UPDATED_EVENT, this.animationPlayers);
    } else {
      // Avoid updating the UI while the panel is hidden.
      // This avoids unnecessary work.
      this.mutationsDetectedWhileHidden = true;
    }
  },

  /**
   * Get the latest known current time of document.timeline.
   * This value is sent along with all AnimationPlayerActors' states, but it
   * isn't updated after that, so this function loops over all know animations
   * to find the highest value.
   * @return {Number|Boolean} False is returned if this server version doesn't
   * provide document's current time.
   */
  get documentCurrentTime() {
    let time = 0;
    for (let {state} of this.animationPlayers) {
      if (!state.documentCurrentTime) {
        return false;
      }
      time = Math.max(time, state.documentCurrentTime);
    }
    return time;
  },

  destroyAnimationPlayers: function () {
    this.animationPlayers = [];
  }
};

EventEmitter.decorate(AnimationsController);
PK
!<��9\\Dchrome/devtools/content/animationinspector/animation-inspector.xhtml<?xml version="1.0" encoding="UTF-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/. -->
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <link rel="stylesheet" href="chrome://devtools/skin/animationinspector.css" type="text/css"/>
    <link rel="stylesheet" href="resource://devtools/client/shared/components/splitter/SplitBox.css"/>
    <script type="application/javascript" src="chrome://devtools/content/shared/theme-switching.js"/>
  </head>
  <body class="theme-sidebar" role="application" empty="true">
    <div id="global-toolbar" class="theme-toolbar">
      <span id="all-animations-label" class="label"></span>
      <button id="toggle-all" class="devtools-button pause-button"></button>
    </div>
    <div id="timeline-toolbar" class="theme-toolbar">
      <button id="rewind-timeline" class="devtools-button"></button>
      <button id="pause-resume-timeline" class="devtools-button pause-button paused"></button>
      <span id="timeline-rate" class="devtools-button"></span>
      <span id="timeline-current-time" class="label"></span>
    </div>
    <div id="players"></div>
    <div id="error-message" class="devtools-sidepanel-no-result">
      <p id="error-type"></p>
      <p id="error-hint"></p>
      <button id="element-picker" data-standalone="true" class="devtools-button"></button>
    </div>
    <script type="text/javascript">
      /* eslint-disable */
      var isInChrome = window.location.href.includes("chrome:");
      if (isInChrome) {
        var exports = {};
        var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
        var { BrowserLoader } = Cu.import("resource://devtools/client/shared/browser-loader.js", {});
      }
    </script>
    <script type="application/javascript" src="animation-controller.js"></script>
    <script type="application/javascript" src="animation-panel.js"></script>
  </body>
</html>
PK
!<V���,�,=chrome/devtools/content/animationinspector/animation-panel.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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/. */

/* import-globals-from animation-controller.js */
/* globals document */

"use strict";

const {AnimationsTimeline} = require("devtools/client/animationinspector/components/animation-timeline");
const {RateSelector} = require("devtools/client/animationinspector/components/rate-selector");
const {formatStopwatchTime} = require("devtools/client/animationinspector/utils");
const {KeyCodes} = require("devtools/client/shared/keycodes");

var $ = (selector, target = document) => target.querySelector(selector);

/**
 * The main animations panel UI.
 */
var AnimationsPanel = {
  UI_UPDATED_EVENT: "ui-updated",
  PANEL_INITIALIZED: "panel-initialized",

  initialize: Task.async(function* () {
    if (AnimationsController.destroyed) {
      console.warn("Could not initialize the animation-panel, controller " +
                   "was destroyed");
      return;
    }
    if (this.initialized) {
      yield this.initialized;
      return;
    }

    let resolver;
    this.initialized = new Promise(resolve => {
      resolver = resolve;
    });

    this.playersEl = $("#players");
    this.errorMessageEl = $("#error-message");
    this.pickerButtonEl = $("#element-picker");
    this.toggleAllButtonEl = $("#toggle-all");
    this.playTimelineButtonEl = $("#pause-resume-timeline");
    this.rewindTimelineButtonEl = $("#rewind-timeline");
    this.timelineCurrentTimeEl = $("#timeline-current-time");
    this.rateSelectorEl = $("#timeline-rate");

    this.rewindTimelineButtonEl.setAttribute("title",
      L10N.getStr("timeline.rewindButtonTooltip"));

    $("#all-animations-label").textContent = L10N.getStr("panel.allAnimations");

    // If the server doesn't support toggling all animations at once, hide the
    // whole global toolbar.
    if (!AnimationsController.traits.hasToggleAll) {
      $("#global-toolbar").style.display = "none";
    }

    // Binding functions that need to be called in scope.
    for (let functionName of [
      "onKeyDown", "onPickerStarted",
      "onPickerStopped", "refreshAnimationsUI", "onToggleAllClicked",
      "onTabNavigated", "onTimelineDataChanged", "onTimelinePlayClicked",
      "onTimelineRewindClicked", "onRateChanged"]) {
      this[functionName] = this[functionName].bind(this);
    }
    let hUtils = gToolbox.highlighterUtils;
    this.togglePicker = hUtils.togglePicker.bind(hUtils);

    this.animationsTimelineComponent = new AnimationsTimeline(gInspector,
      AnimationsController.traits);
    this.animationsTimelineComponent.init(this.playersEl);

    if (AnimationsController.traits.hasSetPlaybackRate) {
      this.rateSelectorComponent = new RateSelector();
      this.rateSelectorComponent.init(this.rateSelectorEl);
    }

    this.startListeners();

    yield this.refreshAnimationsUI();

    resolver();
    this.emit(this.PANEL_INITIALIZED);
  }),

  destroy: Task.async(function* () {
    if (!this.initialized) {
      return;
    }

    if (this.destroyed) {
      yield this.destroyed;
      return;
    }

    let resolver;
    this.destroyed = new Promise(resolve => {
      resolver = resolve;
    });

    this.stopListeners();

    this.animationsTimelineComponent.destroy();
    this.animationsTimelineComponent = null;

    if (this.rateSelectorComponent) {
      this.rateSelectorComponent.destroy();
      this.rateSelectorComponent = null;
    }

    this.playersEl = this.errorMessageEl = null;
    this.toggleAllButtonEl = this.pickerButtonEl = null;
    this.playTimelineButtonEl = this.rewindTimelineButtonEl = null;
    this.timelineCurrentTimeEl = this.rateSelectorEl = null;

    resolver();
  }),

  startListeners: function () {
    AnimationsController.on(AnimationsController.PLAYERS_UPDATED_EVENT,
      this.refreshAnimationsUI);

    this.pickerButtonEl.addEventListener("click", this.togglePicker);
    gToolbox.on("picker-started", this.onPickerStarted);
    gToolbox.on("picker-stopped", this.onPickerStopped);

    this.toggleAllButtonEl.addEventListener("click", this.onToggleAllClicked);
    this.playTimelineButtonEl.addEventListener(
      "click", this.onTimelinePlayClicked);
    this.rewindTimelineButtonEl.addEventListener(
      "click", this.onTimelineRewindClicked);

    document.addEventListener("keydown", this.onKeyDown);

    gToolbox.target.on("navigate", this.onTabNavigated);

    this.animationsTimelineComponent.on("timeline-data-changed",
      this.onTimelineDataChanged);

    if (this.rateSelectorComponent) {
      this.rateSelectorComponent.on("rate-changed", this.onRateChanged);
    }
  },

  stopListeners: function () {
    AnimationsController.off(AnimationsController.PLAYERS_UPDATED_EVENT,
      this.refreshAnimationsUI);

    this.pickerButtonEl.removeEventListener("click", this.togglePicker);
    gToolbox.off("picker-started", this.onPickerStarted);
    gToolbox.off("picker-stopped", this.onPickerStopped);

    this.toggleAllButtonEl.removeEventListener("click",
      this.onToggleAllClicked);
    this.playTimelineButtonEl.removeEventListener("click",
      this.onTimelinePlayClicked);
    this.rewindTimelineButtonEl.removeEventListener("click",
      this.onTimelineRewindClicked);

    document.removeEventListener("keydown", this.onKeyDown);

    gToolbox.target.off("navigate", this.onTabNavigated);

    this.animationsTimelineComponent.off("timeline-data-changed",
      this.onTimelineDataChanged);

    if (this.rateSelectorComponent) {
      this.rateSelectorComponent.off("rate-changed", this.onRateChanged);
    }
  },

  onKeyDown: function (event) {
    // If the space key is pressed, it should toggle the play state of
    // the animations displayed in the panel, or of all the animations on
    // the page if the selected node does not have any animation on it.
    if (event.keyCode === KeyCodes.DOM_VK_SPACE) {
      if (AnimationsController.animationPlayers.length > 0) {
        this.playPauseTimeline().catch(console.error);
      } else {
        this.toggleAll().catch(console.error);
      }
      event.preventDefault();
    }
  },

  togglePlayers: function (isVisible) {
    if (isVisible) {
      document.body.removeAttribute("empty");
      document.body.setAttribute("timeline", "true");
    } else {
      document.body.setAttribute("empty", "true");
      document.body.removeAttribute("timeline");
      $("#error-type").textContent = L10N.getStr("panel.invalidElementSelected");
      $("#error-hint").textContent = L10N.getStr("panel.selectElement");
    }
  },

  onPickerStarted: function () {
    this.pickerButtonEl.classList.add("checked");
  },

  onPickerStopped: function () {
    this.pickerButtonEl.classList.remove("checked");
  },

  onToggleAllClicked: function () {
    this.toggleAll().catch(console.error);
  },

  /**
   * Toggle (pause/play) all animations in the current target
   * and update the UI the toggleAll button.
   */
  toggleAll: Task.async(function* () {
    this.toggleAllButtonEl.classList.toggle("paused");
    yield AnimationsController.toggleAll();
  }),

  onTimelinePlayClicked: function () {
    this.playPauseTimeline().catch(console.error);
  },

  /**
   * Depending on the state of the timeline either pause or play the animations
   * displayed in it.
   * If the animations are finished, this will play them from the start again.
   * If the animations are playing, this will pause them.
   * If the animations are paused, this will resume them.
   *
   * @return {Promise} Resolves when the playState is changed and the UI
   * is refreshed
   */
  playPauseTimeline: function () {
    return AnimationsController
      .toggleCurrentAnimations(this.timelineData.isMoving)
      .then(() => this.refreshAnimationsStateAndUI());
  },

  onTimelineRewindClicked: function () {
    this.rewindTimeline().catch(console.error);
  },

  /**
   * Reset the startTime of all current animations shown in the timeline and
   * pause them.
   *
   * @return {Promise} Resolves when currentTime is set and the UI is refreshed
   */
  rewindTimeline: function () {
    return AnimationsController
      .setCurrentTimeAll(0, true)
      .then(() => this.refreshAnimationsStateAndUI());
  },

  /**
   * Set the playback rate of all current animations shown in the timeline to
   * the value of this.rateSelectorEl.
   */
  onRateChanged: function (rate) {
    AnimationsController.setPlaybackRateAll(rate)
                        .then(() => this.refreshAnimationsStateAndUI())
                        .catch(console.error);
  },

  onTabNavigated: function () {
    this.toggleAllButtonEl.classList.remove("paused");
  },

  onTimelineDataChanged: function (data) {
    this.timelineData = data;
    let {isMoving, isUserDrag, time} = data;

    this.playTimelineButtonEl.classList.toggle("paused", !isMoving);

    let l10nPlayProperty = isMoving ? "timeline.resumedButtonTooltip" :
                                      "timeline.pausedButtonTooltip";

    this.playTimelineButtonEl.setAttribute("title",
      L10N.getStr(l10nPlayProperty));

    // If the timeline data changed as a result of the user dragging the
    // scrubber, then pause all animations and set their currentTimes.
    // (Note that we want server-side requests to be sequenced, so we only do
    // this after the previous currentTime setting was done).
    if (isUserDrag && !this.setCurrentTimeAllPromise) {
      this.setCurrentTimeAllPromise =
        AnimationsController.setCurrentTimeAll(time, true)
                            .catch(console.error)
                            .then(() => {
                              this.setCurrentTimeAllPromise = null;
                            });
    }

    this.displayTimelineCurrentTime();
  },

  displayTimelineCurrentTime: function () {
    let {time} = this.timelineData;
    this.timelineCurrentTimeEl.textContent = formatStopwatchTime(time);
  },

  /**
   * Make sure all known animations have their states up to date (which is
   * useful after the playState or currentTime has been changed and in case the
   * animations aren't auto-refreshing), and then refresh the UI.
   */
  refreshAnimationsStateAndUI: Task.async(function* () {
    for (let player of AnimationsController.animationPlayers) {
      yield player.refreshState();
    }
    yield this.refreshAnimationsUI();
  }),

  /**
   * Refresh the list of animations UI. This will empty the panel and re-render
   * the various components again.
   */
  refreshAnimationsUI: Task.async(function* () {
    // Empty the whole panel first.
    this.togglePlayers(true);

    // Re-render the timeline component.
    this.animationsTimelineComponent.render(
      AnimationsController.animationPlayers,
      AnimationsController.documentCurrentTime);

    // Re-render the rate selector component.
    if (this.rateSelectorComponent) {
      this.rateSelectorComponent.render(AnimationsController.animationPlayers);
    }

    // If there are no players to show, show the error message instead and
    // return.
    if (!AnimationsController.animationPlayers.length) {
      this.togglePlayers(false);
      this.emit(this.UI_UPDATED_EVENT);
      return;
    }

    this.emit(this.UI_UPDATED_EVENT);
  })
};

EventEmitter.decorate(AnimationsPanel);
PK
!<u�,�;H;H3chrome/devtools/content/canvasdebugger/callslist.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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-globals-from canvasdebugger.js */
/* globals window, document */
"use strict";

/**
 * Functions handling details about a single recorded animation frame snapshot
 * (the calls list, rendering preview, thumbnails filmstrip etc.).
 */
var CallsListView = extend(WidgetMethods, {
  /**
   * Initialization function, called when the tool is started.
   */
  initialize: function () {
    this.widget = new SideMenuWidget($("#calls-list"));
    this._slider = $("#calls-slider");
    this._searchbox = $("#calls-searchbox");
    this._filmstrip = $("#snapshot-filmstrip");

    this._onSelect = this._onSelect.bind(this);
    this._onSlideMouseDown = this._onSlideMouseDown.bind(this);
    this._onSlideMouseUp = this._onSlideMouseUp.bind(this);
    this._onSlide = this._onSlide.bind(this);
    this._onSearch = this._onSearch.bind(this);
    this._onScroll = this._onScroll.bind(this);
    this._onExpand = this._onExpand.bind(this);
    this._onStackFileClick = this._onStackFileClick.bind(this);
    this._onThumbnailClick = this._onThumbnailClick.bind(this);

    this.widget.addEventListener("select", this._onSelect);
    this._slider.addEventListener("mousedown", this._onSlideMouseDown);
    this._slider.addEventListener("mouseup", this._onSlideMouseUp);
    this._slider.addEventListener("change", this._onSlide);
    this._searchbox.addEventListener("input", this._onSearch);
    this._filmstrip.addEventListener("wheel", this._onScroll);
  },

  /**
   * Destruction function, called when the tool is closed.
   */
  destroy: function () {
    this.widget.removeEventListener("select", this._onSelect);
    this._slider.removeEventListener("mousedown", this._onSlideMouseDown);
    this._slider.removeEventListener("mouseup", this._onSlideMouseUp);
    this._slider.removeEventListener("change", this._onSlide);
    this._searchbox.removeEventListener("input", this._onSearch);
    this._filmstrip.removeEventListener("wheel", this._onScroll);
  },

  /**
   * Populates this container with a list of function calls.
   *
   * @param array functionCalls
   *        A list of function call actors received from the backend.
   */
  showCalls: function (functionCalls) {
    this.empty();

    for (let i = 0, len = functionCalls.length; i < len; i++) {
      let call = functionCalls[i];

      let view = document.createElement("vbox");
      view.className = "call-item-view devtools-monospace";
      view.setAttribute("flex", "1");

      let contents = document.createElement("hbox");
      contents.className = "call-item-contents";
      contents.setAttribute("align", "center");
      contents.addEventListener("dblclick", this._onExpand);
      view.appendChild(contents);

      let index = document.createElement("label");
      index.className = "plain call-item-index";
      index.setAttribute("flex", "1");
      index.setAttribute("value", i + 1);

      let gutter = document.createElement("hbox");
      gutter.className = "call-item-gutter";
      gutter.appendChild(index);
      contents.appendChild(gutter);

      if (call.callerPreview) {
        let context = document.createElement("label");
        context.className = "plain call-item-context";
        context.setAttribute("value", call.callerPreview);
        contents.appendChild(context);

        let separator = document.createElement("label");
        separator.className = "plain call-item-separator";
        separator.setAttribute("value", ".");
        contents.appendChild(separator);
      }

      let name = document.createElement("label");
      name.className = "plain call-item-name";
      name.setAttribute("value", call.name);
      contents.appendChild(name);

      let argsPreview = document.createElement("label");
      argsPreview.className = "plain call-item-args";
      argsPreview.setAttribute("crop", "end");
      argsPreview.setAttribute("flex", "100");
      // Getters and setters are displayed differently from regular methods.
      if (call.type == CallWatcherFront.METHOD_FUNCTION) {
        argsPreview.setAttribute("value", "(" + call.argsPreview + ")");
      } else {
        argsPreview.setAttribute("value", " = " + call.argsPreview);
      }
      contents.appendChild(argsPreview);

      let location = document.createElement("label");
      location.className = "plain call-item-location";
      location.setAttribute("value", getFileName(call.file) + ":" + call.line);
      location.setAttribute("crop", "start");
      location.setAttribute("flex", "1");
      location.addEventListener("mousedown", this._onExpand);
      contents.appendChild(location);

      // Append a function call item to this container.
      this.push([view], {
        staged: true,
        attachment: {
          actor: call
        }
      });

      // Highlight certain calls that are probably more interesting than
      // everything else, making it easier to quickly glance over them.
      if (CanvasFront.DRAW_CALLS.has(call.name)) {
        view.setAttribute("draw-call", "");
      }
      if (CanvasFront.INTERESTING_CALLS.has(call.name)) {
        view.setAttribute("interesting-call", "");
      }
    }

    // Flushes all the prepared function call items into this container.
    this.commit();
    window.emit(EVENTS.CALL_LIST_POPULATED);

    // Resetting the function selection slider's value (shown in this
    // container's toolbar) would trigger a selection event, which should be
    // ignored in this case.
    this._ignoreSliderChanges = true;
    this._slider.value = 0;
    this._slider.max = functionCalls.length - 1;
    this._ignoreSliderChanges = false;
  },

  /**
   * Displays an image in the rendering preview of this container, generated
   * for the specified draw call in the recorded animation frame snapshot.
   *
   * @param array screenshot
   *        A single "snapshot-image" instance received from the backend.
   */
  showScreenshot: function (screenshot) {
    let { index, width, height, scaling, flipped, pixels } = screenshot;

    let screenshotNode = $("#screenshot-image");
    screenshotNode.setAttribute("flipped", flipped);
    drawBackground("screenshot-rendering", width, height, pixels);

    let dimensionsNode = $("#screenshot-dimensions");
    let actualWidth = (width / scaling) | 0;
    let actualHeight = (height / scaling) | 0;
    dimensionsNode.setAttribute("value",
      SHARED_L10N.getFormatStr("dimensions", actualWidth, actualHeight));

    window.emit(EVENTS.CALL_SCREENSHOT_DISPLAYED);
  },

  /**
   * Populates this container's footer with a list of thumbnails, one generated
   * for each draw call in the recorded animation frame snapshot.
   *
   * @param array thumbnails
   *        An array of "snapshot-image" instances received from the backend.
   */
  showThumbnails: function (thumbnails) {
    while (this._filmstrip.hasChildNodes()) {
      this._filmstrip.firstChild.remove();
    }
    for (let thumbnail of thumbnails) {
      this.appendThumbnail(thumbnail);
    }

    window.emit(EVENTS.THUMBNAILS_DISPLAYED);
  },

  /**
   * Displays an image in the thumbnails list of this container, generated
   * for the specified draw call in the recorded animation frame snapshot.
   *
   * @param array thumbnail
   *        A single "snapshot-image" instance received from the backend.
   */
  appendThumbnail: function (thumbnail) {
    let { index, width, height, flipped, pixels } = thumbnail;

    let thumbnailNode = document.createElementNS(HTML_NS, "canvas");
    thumbnailNode.setAttribute("flipped", flipped);
    thumbnailNode.width = Math.max(CanvasFront.THUMBNAIL_SIZE, width);
    thumbnailNode.height = Math.max(CanvasFront.THUMBNAIL_SIZE, height);
    drawImage(thumbnailNode, width, height, pixels, { centered: true });

    thumbnailNode.className = "filmstrip-thumbnail";
    thumbnailNode.onmousedown = e => this._onThumbnailClick(e, index);
    thumbnailNode.setAttribute("index", index);
    this._filmstrip.appendChild(thumbnailNode);
  },

  /**
   * Sets the currently highlighted thumbnail in this container.
   * A screenshot will always correlate to a thumbnail in the filmstrip,
   * both being identified by the same 'index' of the context function call.
   *
   * @param number index
   *        The context function call's index.
   */
  set highlightedThumbnail(index) {
    let currHighlightedThumbnail = $(".filmstrip-thumbnail[index='" + index + "']");
    if (currHighlightedThumbnail == null) {
      return;
    }

    let prevIndex = this._highlightedThumbnailIndex;
    let prevHighlightedThumbnail = $(".filmstrip-thumbnail[index='" + prevIndex + "']");
    if (prevHighlightedThumbnail) {
      prevHighlightedThumbnail.removeAttribute("highlighted");
    }

    currHighlightedThumbnail.setAttribute("highlighted", "");
    currHighlightedThumbnail.scrollIntoView();
    this._highlightedThumbnailIndex = index;
  },

  /**
   * Gets the currently highlighted thumbnail in this container.
   * @return number
   */
  get highlightedThumbnail() {
    return this._highlightedThumbnailIndex;
  },

  /**
   * The select listener for this container.
   */
  _onSelect: function ({ detail: callItem }) {
    if (!callItem) {
      return;
    }

    // Some of the stepping buttons don't make sense specifically while the
    // last function call is selected.
    if (this.selectedIndex == this.itemCount - 1) {
      $("#resume").setAttribute("disabled", "true");
      $("#step-over").setAttribute("disabled", "true");
      $("#step-out").setAttribute("disabled", "true");
    } else {
      $("#resume").removeAttribute("disabled");
      $("#step-over").removeAttribute("disabled");
      $("#step-out").removeAttribute("disabled");
    }

    // Correlate the currently selected item with the function selection
    // slider's value. Avoid triggering a redundant selection event.
    this._ignoreSliderChanges = true;
    this._slider.value = this.selectedIndex;
    this._ignoreSliderChanges = false;

    // Can't generate screenshots for function call actors loaded from disk.
    // XXX: Bug 984844.
    if (callItem.attachment.actor.isLoadedFromDisk) {
      return;
    }

    // To keep continuous selection buttery smooth (for example, while pressing
    // the DOWN key or moving the slider), only display the screenshot after
    // any kind of user input stops.
    setConditionalTimeout("screenshot-display", SCREENSHOT_DISPLAY_DELAY, () => {
      return !this._isSliding;
    }, () => {
      let frameSnapshot = SnapshotsListView.selectedItem.attachment.actor;
      let functionCall = callItem.attachment.actor;
      frameSnapshot.generateScreenshotFor(functionCall).then(screenshot => {
        this.showScreenshot(screenshot);
        this.highlightedThumbnail = screenshot.index;
      }).catch(console.error);
    });
  },

  /**
   * The mousedown listener for the call selection slider.
   */
  _onSlideMouseDown: function () {
    this._isSliding = true;
  },

  /**
   * The mouseup listener for the call selection slider.
   */
  _onSlideMouseUp: function () {
    this._isSliding = false;
  },

  /**
   * The change listener for the call selection slider.
   */
  _onSlide: function () {
    // Avoid performing any operations when programatically changing the value.
    if (this._ignoreSliderChanges) {
      return;
    }
    let selectedFunctionCallIndex = this.selectedIndex = this._slider.value;

    // While sliding, immediately show the most relevant thumbnail for a
    // function call, for a nice diff-like animation effect between draws.
    let thumbnails = SnapshotsListView.selectedItem.attachment.thumbnails;
    let thumbnail = getThumbnailForCall(thumbnails, selectedFunctionCallIndex);

    // Avoid drawing and highlighting if the selected function call has the
    // same thumbnail as the last one.
    if (thumbnail.index == this.highlightedThumbnail) {
      return;
    }
    // If a thumbnail wasn't found (e.g. the backend avoids creating thumbnails
    // when rendering offscreen), simply defer to the first available one.
    if (thumbnail.index == -1) {
      thumbnail = thumbnails[0];
    }

    let { index, width, height, flipped, pixels } = thumbnail;
    this.highlightedThumbnail = index;

    let screenshotNode = $("#screenshot-image");
    screenshotNode.setAttribute("flipped", flipped);
    drawBackground("screenshot-rendering", width, height, pixels);
  },

  /**
   * The input listener for the calls searchbox.
   */
  _onSearch: function (e) {
    let lowerCaseSearchToken = this._searchbox.value.toLowerCase();

    this.filterContents(e => {
      let call = e.attachment.actor;
      let name = call.name.toLowerCase();
      let file = call.file.toLowerCase();
      let line = call.line.toString().toLowerCase();
      let args = call.argsPreview.toLowerCase();

      return name.includes(lowerCaseSearchToken) ||
             file.includes(lowerCaseSearchToken) ||
             line.includes(lowerCaseSearchToken) ||
             args.includes(lowerCaseSearchToken);
    });
  },

  /**
   * The wheel listener for the filmstrip that contains all the thumbnails.
   */
  _onScroll: function (e) {
    this._filmstrip.scrollLeft += e.deltaX;
  },

  /**
   * The click/dblclick listener for an item or location url in this container.
   * When expanding an item, it's corresponding call stack will be displayed.
   */
  _onExpand: function (e) {
    let callItem = this.getItemForElement(e.target);
    let view = $(".call-item-view", callItem.target);

    // If the call stack nodes were already created, simply re-show them
    // or jump to the corresponding file and line in the Debugger if a
    // location link was clicked.
    if (view.hasAttribute("call-stack-populated")) {
      let isExpanded = view.getAttribute("call-stack-expanded") == "true";

      // If clicking on the location, jump to the Debugger.
      if (e.target.classList.contains("call-item-location")) {
        let { file, line } = callItem.attachment.actor;
        this._viewSourceInDebugger(file, line);
        return;
      }
      // Otherwise hide the call stack.
      else {
        view.setAttribute("call-stack-expanded", !isExpanded);
        $(".call-item-stack", view).hidden = isExpanded;
        return;
      }
    }

    let list = document.createElement("vbox");
    list.className = "call-item-stack";
    view.setAttribute("call-stack-populated", "");
    view.setAttribute("call-stack-expanded", "true");
    view.appendChild(list);

    /**
     * Creates a function call nodes in this container for a stack.
     */
    let display = stack => {
      for (let i = 1; i < stack.length; i++) {
        let call = stack[i];

        let contents = document.createElement("hbox");
        contents.className = "call-item-stack-fn";
        contents.style.paddingInlineStart = (i * STACK_FUNC_INDENTATION) + "px";

        let name = document.createElement("label");
        name.className = "plain call-item-stack-fn-name";
        name.setAttribute("value", "↳ " + call.name + "()");
        contents.appendChild(name);

        let spacer = document.createElement("spacer");
        spacer.setAttribute("flex", "100");
        contents.appendChild(spacer);

        let location = document.createElement("label");
        location.className = "plain call-item-stack-fn-location";
        location.setAttribute("value", getFileName(call.file) + ":" + call.line);
        location.setAttribute("crop", "start");
        location.setAttribute("flex", "1");
        location.addEventListener("mousedown", e => this._onStackFileClick(e, call));
        contents.appendChild(location);

        list.appendChild(contents);
      }

      window.emit(EVENTS.CALL_STACK_DISPLAYED);
    };

    // If this animation snapshot is loaded from disk, there are no corresponding
    // backend actors available and the data is immediately available.
    let functionCall = callItem.attachment.actor;
    if (functionCall.isLoadedFromDisk) {
      display(functionCall.stack);
    }
    // ..otherwise we need to request the function call stack from the backend.
    else {
      callItem.attachment.actor.getDetails().then(fn => display(fn.stack));
    }
  },

  /**
   * The click listener for a location link in the call stack.
   *
   * @param string file
   *        The url of the source owning the function.
   * @param number line
   *        The line of the respective function.
   */
  _onStackFileClick: function (e, { file, line }) {
    this._viewSourceInDebugger(file, line);
  },

  /**
   * The click listener for a thumbnail in the filmstrip.
   *
   * @param number index
   *        The function index in the recorded animation frame snapshot.
   */
  _onThumbnailClick: function (e, index) {
    this.selectedIndex = index;
  },

  /**
   * The click listener for the "resume" button in this container's toolbar.
   */
  _onResume: function () {
    // Jump to the next draw call in the recorded animation frame snapshot.
    let drawCall = getNextDrawCall(this.items, this.selectedItem);
    if (drawCall) {
      this.selectedItem = drawCall;
      return;
    }

    // If there are no more draw calls, just jump to the last context call.
    this._onStepOut();
  },

  /**
   * The click listener for the "step over" button in this container's toolbar.
   */
  _onStepOver: function () {
    this.selectedIndex++;
  },

  /**
   * The click listener for the "step in" button in this container's toolbar.
   */
  _onStepIn: function () {
    if (this.selectedIndex == -1) {
      this._onResume();
      return;
    }
    let callItem = this.selectedItem;
    let { file, line } = callItem.attachment.actor;
    this._viewSourceInDebugger(file, line);
  },

  /**
   * The click listener for the "step out" button in this container's toolbar.
   */
  _onStepOut: function () {
    this.selectedIndex = this.itemCount - 1;
  },

  /**
   * Opens the specified file and line in the debugger. Falls back to Firefox's View Source.
   */
  _viewSourceInDebugger: function (file, line) {
    gToolbox.viewSourceInDebugger(file, line).then(success => {
      if (success) {
        window.emit(EVENTS.SOURCE_SHOWN_IN_JS_DEBUGGER);
      } else {
        window.emit(EVENTS.SOURCE_NOT_FOUND_IN_JS_DEBUGGER);
      }
    });
  }
});
PK
!<7�yR--8chrome/devtools/content/canvasdebugger/canvasdebugger.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
const { SideMenuWidget } = require("resource://devtools/client/shared/widgets/SideMenuWidget.jsm");
const promise = require("promise");
const Services = require("Services");
const EventEmitter = require("devtools/shared/event-emitter");
const { CallWatcherFront } = require("devtools/shared/fronts/call-watcher");
const { CanvasFront } = require("devtools/shared/fronts/canvas");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const { extend } = require("devtools/shared/extend");
const flags = require("devtools/shared/flags");
const { LocalizationHelper } = require("devtools/shared/l10n");
const { PluralForm } = require("devtools/shared/plural-form");
const { WidgetMethods, setNamedTimeout, clearNamedTimeout,
        setConditionalTimeout } = require("devtools/client/shared/widgets/view-helpers");

// Use privileged promise in panel documents to prevent having them to freeze
// during toolbox destruction. See bug 1402779.
const Promise = require("Promise");

const CANVAS_ACTOR_RECORDING_ATTEMPT = flags.testing ? 500 : 5000;

const { Task } = require("devtools/shared/task");

ChromeUtils.defineModuleGetter(this, "FileUtils",
  "resource://gre/modules/FileUtils.jsm");

ChromeUtils.defineModuleGetter(this, "NetUtil",
  "resource://gre/modules/NetUtil.jsm");

XPCOMUtils.defineLazyGetter(this, "NetworkHelper", function () {
  return require("devtools/shared/webconsole/network-helper");
});

// The panel's window global is an EventEmitter firing the following events:
const EVENTS = {
  // When the UI is reset from tab navigation.
  UI_RESET: "CanvasDebugger:UIReset",

  // When all the animation frame snapshots are removed by the user.
  SNAPSHOTS_LIST_CLEARED: "CanvasDebugger:SnapshotsListCleared",

  // When an animation frame snapshot starts/finishes being recorded, and
  // whether it was completed succesfully or cancelled.
  SNAPSHOT_RECORDING_STARTED: "CanvasDebugger:SnapshotRecordingStarted",
  SNAPSHOT_RECORDING_FINISHED: "CanvasDebugger:SnapshotRecordingFinished",
  SNAPSHOT_RECORDING_COMPLETED: "CanvasDebugger:SnapshotRecordingCompleted",
  SNAPSHOT_RECORDING_CANCELLED: "CanvasDebugger:SnapshotRecordingCancelled",

  // When an animation frame snapshot was selected and all its data displayed.
  SNAPSHOT_RECORDING_SELECTED: "CanvasDebugger:SnapshotRecordingSelected",

  // After all the function calls associated with an animation frame snapshot
  // are displayed in the UI.
  CALL_LIST_POPULATED: "CanvasDebugger:CallListPopulated",

  // After the stack associated with a call in an animation frame snapshot
  // is displayed in the UI.
  CALL_STACK_DISPLAYED: "CanvasDebugger:CallStackDisplayed",

  // After a screenshot associated with a call in an animation frame snapshot
  // is displayed in the UI.
  CALL_SCREENSHOT_DISPLAYED: "CanvasDebugger:ScreenshotDisplayed",

  // After all the thumbnails associated with an animation frame snapshot
  // are displayed in the UI.
  THUMBNAILS_DISPLAYED: "CanvasDebugger:ThumbnailsDisplayed",

  // When a source is shown in the JavaScript Debugger at a specific location.
  SOURCE_SHOWN_IN_JS_DEBUGGER: "CanvasDebugger:SourceShownInJsDebugger",
  SOURCE_NOT_FOUND_IN_JS_DEBUGGER: "CanvasDebugger:SourceNotFoundInJsDebugger"
};
XPCOMUtils.defineConstant(this, "EVENTS", EVENTS);

const HTML_NS = "http://www.w3.org/1999/xhtml";
const STRINGS_URI = "devtools/client/locales/canvasdebugger.properties";
const SHARED_STRINGS_URI = "devtools/client/locales/shared.properties";

const SNAPSHOT_START_RECORDING_DELAY = 10; // ms
const SNAPSHOT_DATA_EXPORT_MAX_BLOCK = 1000; // ms
const SNAPSHOT_DATA_DISPLAY_DELAY = 10; // ms
const SCREENSHOT_DISPLAY_DELAY = 100; // ms
const STACK_FUNC_INDENTATION = 14; // px

// This identifier string is simply used to tentatively ascertain whether or not
// a JSON loaded from disk is actually something generated by this tool or not.
// It isn't, of course, a definitive verification, but a Good Enough™
// approximation before continuing the import. Don't localize this.
const CALLS_LIST_SERIALIZER_IDENTIFIER = "Recorded Animation Frame Snapshot";
const CALLS_LIST_SERIALIZER_VERSION = 1;
const CALLS_LIST_SLOW_SAVE_DELAY = 100; // ms

/**
 * The current target and the Canvas front, set by this tool's host.
 */
var gToolbox, gTarget, gFront;

/**
 * Initializes the canvas debugger controller and views.
 */
function startupCanvasDebugger() {
  return promise.all([
    EventsHandler.initialize(),
    SnapshotsListView.initialize(),
    CallsListView.initialize()
  ]);
}

/**
 * Destroys the canvas debugger controller and views.
 */
function shutdownCanvasDebugger() {
  return promise.all([
    EventsHandler.destroy(),
    SnapshotsListView.destroy(),
    CallsListView.destroy()
  ]);
}

/**
 * Functions handling target-related lifetime events.
 */
var EventsHandler = {
  /**
   * Listen for events emitted by the current tab target.
   */
  initialize: function () {
    // Make sure the backend is prepared to handle <canvas> contexts.
    // Since actors are created lazily on the first request to them, we need to send an
    // early request to ensure the CallWatcherActor is running and watching for new window
    // globals.
    gFront.setup({ reload: false });

    this._onTabNavigated = this._onTabNavigated.bind(this);
    gTarget.on("will-navigate", this._onTabNavigated);
    gTarget.on("navigate", this._onTabNavigated);
  },

  /**
   * Remove events emitted by the current tab target.
   */
  destroy: function () {
    gTarget.off("will-navigate", this._onTabNavigated);
    gTarget.off("navigate", this._onTabNavigated);
  },

  /**
   * Called for each location change in the debugged tab.
   */
  _onTabNavigated: function (event) {
    if (event != "will-navigate") {
      return;
    }

    // Reset UI.
    SnapshotsListView.empty();
    CallsListView.empty();

    $("#record-snapshot").removeAttribute("checked");
    $("#record-snapshot").removeAttribute("disabled");
    $("#record-snapshot").hidden = false;

    $("#reload-notice").hidden = true;
    $("#empty-notice").hidden = false;
    $("#waiting-notice").hidden = true;

    $("#debugging-pane-contents").hidden = true;
    $("#screenshot-container").hidden = true;
    $("#snapshot-filmstrip").hidden = true;

    window.emit(EVENTS.UI_RESET);
  }
};

/**
 * Localization convenience methods.
 */
var L10N = new LocalizationHelper(STRINGS_URI);
var SHARED_L10N = new LocalizationHelper(SHARED_STRINGS_URI);

/**
 * Convenient way of emitting events from the panel window.
 */
EventEmitter.decorate(this);

/**
 * DOM query helpers.
 */
var $ = (selector, target = document) => target.querySelector(selector);
var $all = (selector, target = document) => target.querySelectorAll(selector);

/**
 * Gets the fileName part of a string which happens to be an URL.
 */
function getFileName(url) {
  try {
    let { fileName } = NetworkHelper.nsIURL(url);
    return fileName || "/";
  } catch (e) {
    // This doesn't look like a url, or nsIURL can't handle it.
    return "";
  }
}

/**
 * Gets an image data object containing a buffer large enough to hold
 * width * height pixels.
 *
 * This method avoids allocating memory and tries to reuse a common buffer
 * as much as possible.
 *
 * @param number w
 *        The desired image data storage width.
 * @param number h
 *        The desired image data storage height.
 * @return ImageData
 *         The requested image data buffer.
 */
function getImageDataStorage(ctx, w, h) {
  let storage = getImageDataStorage.cache;
  if (storage && storage.width == w && storage.height == h) {
    return storage;
  }
  return getImageDataStorage.cache = ctx.createImageData(w, h);
}

// The cache used in the `getImageDataStorage` function.
getImageDataStorage.cache = null;

/**
 * Draws image data into a canvas.
 *
 * This method makes absolutely no assumptions about the canvas element
 * dimensions, or pre-existing rendering. It's a dumb proxy that copies pixels.
 *
 * @param HTMLCanvasElement canvas
 *        The canvas element to put the image data into.
 * @param number width
 *        The image data width.
 * @param number height
 *        The image data height.
 * @param array pixels
 *        An array buffer view of the image data.
 * @param object options
 *        Additional options supported by this operation:
 *          - centered: specifies whether the image data should be centered
 *                      when copied in the canvas; this is useful when the
 *                      supplied pixels don't completely cover the canvas.
 */
function drawImage(canvas, width, height, pixels, options = {}) {
  let ctx = canvas.getContext("2d");

  // FrameSnapshot actors return "snapshot-image" type instances with just an
  // empty pixel array if the source image is completely transparent.
  if (pixels.length <= 1) {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    return;
  }

  let imageData = getImageDataStorage(ctx, width, height);
  imageData.data.set(pixels);

  if (options.centered) {
    let left = (canvas.width - width) / 2;
    let top = (canvas.height - height) / 2;
    ctx.putImageData(imageData, left, top);
  } else {
    ctx.putImageData(imageData, 0, 0);
  }
}

/**
 * Draws image data into a canvas, and sets that as the rendering source for
 * an element with the specified id as the -moz-element background image.
 *
 * @param string id
 *        The id of the -moz-element background image.
 * @param number width
 *        The image data width.
 * @param number height
 *        The image data height.
 * @param array pixels
 *        An array buffer view of the image data.
 */
function drawBackground(id, width, height, pixels) {
  let canvas = document.createElementNS(HTML_NS, "canvas");
  canvas.width = width;
  canvas.height = height;

  drawImage(canvas, width, height, pixels);
  document.mozSetImageElement(id, canvas);

  // Used in tests. Not emitting an event because this shouldn't be "interesting".
  if (window._onMozSetImageElement) {
    window._onMozSetImageElement(pixels);
  }
}

/**
 * Iterates forward to find the next draw call in a snapshot.
 */
function getNextDrawCall(calls, call) {
  for (let i = calls.indexOf(call) + 1, len = calls.length; i < len; i++) {
    let nextCall = calls[i];
    let name = nextCall.attachment.actor.name;
    if (CanvasFront.DRAW_CALLS.has(name)) {
      return nextCall;
    }
  }
  return null;
}

/**
 * Iterates backwards to find the most recent screenshot for a function call
 * in a snapshot loaded from disk.
 */
function getScreenshotFromCallLoadedFromDisk(calls, call) {
  for (let i = calls.indexOf(call); i >= 0; i--) {
    let prevCall = calls[i];
    let screenshot = prevCall.screenshot;
    if (screenshot) {
      return screenshot;
    }
  }
  return CanvasFront.INVALID_SNAPSHOT_IMAGE;
}

/**
 * Iterates backwards to find the most recent thumbnail for a function call.
 */
function getThumbnailForCall(thumbnails, index) {
  for (let i = thumbnails.length - 1; i >= 0; i--) {
    let thumbnail = thumbnails[i];
    if (thumbnail.index <= index) {
      return thumbnail;
    }
  }
  return CanvasFront.INVALID_SNAPSHOT_IMAGE;
}
PK
!<D�����9chrome/devtools/content/canvasdebugger/canvasdebugger.xul<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/content/shared/widgets/widgets.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/widgets.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/canvasdebugger.css" type="text/css"?>
<!DOCTYPE window [
  <!ENTITY % canvasDebuggerDTD SYSTEM "chrome://devtools/locale/canvasdebugger.dtd">
  %canvasDebuggerDTD;
]>

<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
  <script src="chrome://devtools/content/shared/theme-switching.js"/>
  <script type="application/javascript" src="canvasdebugger.js"/>
  <script type="application/javascript" src="callslist.js"/>
  <script type="application/javascript" src="snapshotslist.js"/>

  <hbox class="theme-body" flex="1">
    <vbox id="snapshots-pane">
      <toolbar id="snapshots-toolbar"
               class="devtools-toolbar">
        <hbox id="snapshots-controls">
          <toolbarbutton id="clear-snapshots"
                         class="devtools-toolbarbutton devtools-clear-icon"
                         oncommand="SnapshotsListView._onClearButtonClick()"
                         tooltiptext="&canvasDebuggerUI.clearSnapshots;"/>
          <toolbarbutton id="record-snapshot"
                         class="devtools-toolbarbutton"
                         oncommand="SnapshotsListView._onRecordButtonClick()"
                         tooltiptext="&canvasDebuggerUI.recordSnapshot.tooltip;"
                         hidden="true"/>
          <toolbarbutton id="import-snapshot"
                         class="devtools-toolbarbutton"
                         oncommand="SnapshotsListView._onImportButtonClick()"
                         tooltiptext="&canvasDebuggerUI.importSnapshot;"/>
        </hbox>
      </toolbar>
      <vbox id="snapshots-list" flex="1"/>
    </vbox>

    <vbox id="debugging-pane" class="devtools-main-content" flex="1">
      <hbox id="reload-notice"
            class="notice-container"
            align="center"
            pack="center"
            flex="1">
        <button id="reload-notice-button"
                class="devtools-toolbarbutton"
                standalone="true"
                label="&canvasDebuggerUI.reloadNotice1;"
                oncommand="gFront.setup({ reload: true })"/>
        <label id="reload-notice-label"
               class="plain"
               value="&canvasDebuggerUI.reloadNotice2;"/>
      </hbox>

      <hbox id="empty-notice"
            class="notice-container"
            align="center"
            pack="center"
            flex="1"
            hidden="true">
        <label value="&canvasDebuggerUI.emptyNotice1;"/>
        <button id="canvas-debugging-empty-notice-button"
                class="devtools-toolbarbutton"
                standalone="true"
                oncommand="SnapshotsListView._onRecordButtonClick()"/>
        <label value="&canvasDebuggerUI.emptyNotice2;"/>
      </hbox>

      <hbox id="waiting-notice"
            class="notice-container devtools-throbber"
            align="center"
            pack="center"
            flex="1"
            hidden="true">
        <label id="requests-menu-waiting-notice-label"
               class="plain"
               value="&canvasDebuggerUI.waitingNotice;"/>
      </hbox>

      <box id="debugging-pane-contents"
           class="devtools-responsive-container"
           flex="1"
           hidden="true">
        <vbox id="calls-list-container" flex="1">
          <toolbar id="debugging-toolbar"
                   class="devtools-toolbar">
            <hbox id="debugging-controls"
                  class="devtools-toolbarbutton-group">
              <toolbarbutton id="resume"
                             class="devtools-toolbarbutton"
                             oncommand="CallsListView._onResume()"/>
              <toolbarbutton id="step-over"
                             class="devtools-toolbarbutton"
                             oncommand="CallsListView._onStepOver()"/>
              <toolbarbutton id="step-in"
                             class="devtools-toolbarbutton"
                             oncommand="CallsListView._onStepIn()"/>
              <toolbarbutton id="step-out"
                             class="devtools-toolbarbutton"
                             oncommand="CallsListView._onStepOut()"/>
            </hbox>
            <toolbarbutton id="debugging-toolbar-sizer-button"
                           class="devtools-toolbarbutton"
                           label=""/>
            <scale id="calls-slider"
                   movetoclick="true"
                   flex="100"/>
            <textbox id="calls-searchbox"
                     class="devtools-filterinput"
                     placeholder="&canvasDebuggerUI.searchboxPlaceholder;"
                     type="search"
                     flex="1"/>
          </toolbar>
          <vbox id="calls-list" flex="1"/>
        </vbox>

        <splitter class="devtools-side-splitter"/>

        <vbox id="screenshot-container"
              hidden="true">
          <vbox id="screenshot-image" flex="1"/>
          <label id="screenshot-dimensions" class="plain"/>
        </vbox>
      </box>

      <hbox id="snapshot-filmstrip"
            hidden="true"/>
    </vbox>

  </hbox>
</window>
PK
!<�0#�HFHF7chrome/devtools/content/canvasdebugger/snapshotslist.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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-globals-from canvasdebugger.js */
/* globals window, document */
"use strict";

/**
 * Functions handling the recorded animation frame snapshots UI.
 */
var SnapshotsListView = extend(WidgetMethods, {
  /**
   * Initialization function, called when the tool is started.
   */
  initialize: function () {
    this.widget = new SideMenuWidget($("#snapshots-list"), {
      showArrows: true
    });

    this._onSelect = this._onSelect.bind(this);
    this._onClearButtonClick = this._onClearButtonClick.bind(this);
    this._onRecordButtonClick = this._onRecordButtonClick.bind(this);
    this._onImportButtonClick = this._onImportButtonClick.bind(this);
    this._onSaveButtonClick = this._onSaveButtonClick.bind(this);
    this._onRecordSuccess = this._onRecordSuccess.bind(this);
    this._onRecordFailure = this._onRecordFailure.bind(this);
    this._stopRecordingAnimation = this._stopRecordingAnimation.bind(this);

    window.on(EVENTS.SNAPSHOT_RECORDING_FINISHED, this._enableRecordButton);
    this.emptyText = L10N.getStr("noSnapshotsText");
    this.widget.addEventListener("select", this._onSelect);
  },

  /**
   * Destruction function, called when the tool is closed.
   */
  destroy: function () {
    clearNamedTimeout("canvas-actor-recording");
    window.off(EVENTS.SNAPSHOT_RECORDING_FINISHED, this._enableRecordButton);
    this.widget.removeEventListener("select", this._onSelect);
  },

  /**
   * Adds a snapshot entry to this container.
   *
   * @return object
   *         The newly inserted item.
   */
  addSnapshot: function () {
    let contents = document.createElement("hbox");
    contents.className = "snapshot-item";

    let thumbnail = document.createElementNS(HTML_NS, "canvas");
    thumbnail.className = "snapshot-item-thumbnail";
    thumbnail.width = CanvasFront.THUMBNAIL_SIZE;
    thumbnail.height = CanvasFront.THUMBNAIL_SIZE;

    let title = document.createElement("label");
    title.className = "plain snapshot-item-title";
    title.setAttribute("value",
      L10N.getFormatStr("snapshotsList.itemLabel", this.itemCount + 1));

    let calls = document.createElement("label");
    calls.className = "plain snapshot-item-calls";
    calls.setAttribute("value",
      L10N.getStr("snapshotsList.loadingLabel"));

    let save = document.createElement("label");
    save.className = "plain snapshot-item-save";
    save.addEventListener("click", this._onSaveButtonClick);

    let spacer = document.createElement("spacer");
    spacer.setAttribute("flex", "1");

    let footer = document.createElement("hbox");
    footer.className = "snapshot-item-footer";
    footer.appendChild(save);

    let details = document.createElement("vbox");
    details.className = "snapshot-item-details";
    details.appendChild(title);
    details.appendChild(calls);
    details.appendChild(spacer);
    details.appendChild(footer);

    contents.appendChild(thumbnail);
    contents.appendChild(details);

    // Append a recorded snapshot item to this container.
    return this.push([contents], {
      attachment: {
        // The snapshot and function call actors, along with the thumbnails
        // will be available as soon as recording finishes.
        actor: null,
        calls: null,
        thumbnails: null,
        screenshot: null
      }
    });
  },

  /**
   * Removes the last snapshot added, in the event no requestAnimationFrame loop was found.
   */
  removeLastSnapshot: function () {
    this.removeAt(this.itemCount - 1);
    // If this is the only item, revert back to the empty notice
    if (this.itemCount === 0) {
      $("#empty-notice").hidden = false;
      $("#waiting-notice").hidden = true;
    }
  },

  /**
   * Customizes a shapshot in this container.
   *
   * @param Item snapshotItem
   *        An item inserted via `SnapshotsListView.addSnapshot`.
   * @param object snapshotActor
   *        The frame snapshot actor received from the backend.
   * @param object snapshotOverview
   *        Additional data about the snapshot received from the backend.
   */
  customizeSnapshot: function (snapshotItem, snapshotActor, snapshotOverview) {
    // Make sure the function call actors are stored on the item,
    // to be used when populating the CallsListView.
    snapshotItem.attachment.actor = snapshotActor;
    let functionCalls = snapshotItem.attachment.calls = snapshotOverview.calls;
    let thumbnails = snapshotItem.attachment.thumbnails = snapshotOverview.thumbnails;
    let screenshot = snapshotItem.attachment.screenshot = snapshotOverview.screenshot;

    let lastThumbnail = thumbnails[thumbnails.length - 1];
    let { width, height, flipped, pixels } = lastThumbnail;

    let thumbnailNode = $(".snapshot-item-thumbnail", snapshotItem.target);
    thumbnailNode.setAttribute("flipped", flipped);
    drawImage(thumbnailNode, width, height, pixels, { centered: true });

    let callsNode = $(".snapshot-item-calls", snapshotItem.target);
    let drawCalls = functionCalls.filter(e => CanvasFront.DRAW_CALLS.has(e.name));

    let drawCallsStr = PluralForm.get(drawCalls.length,
      L10N.getStr("snapshotsList.drawCallsLabel"));
    let funcCallsStr = PluralForm.get(functionCalls.length,
      L10N.getStr("snapshotsList.functionCallsLabel"));

    callsNode.setAttribute("value",
      drawCallsStr.replace("#1", drawCalls.length) + ", " +
      funcCallsStr.replace("#1", functionCalls.length));

    let saveNode = $(".snapshot-item-save", snapshotItem.target);
    saveNode.setAttribute("disabled", !!snapshotItem.isLoadedFromDisk);
    saveNode.setAttribute("value", snapshotItem.isLoadedFromDisk
      ? L10N.getStr("snapshotsList.loadedLabel")
      : L10N.getStr("snapshotsList.saveLabel"));

    // Make sure there's always a selected item available.
    if (!this.selectedItem) {
      this.selectedIndex = 0;
    }
  },

  /**
   * The select listener for this container.
   */
  _onSelect: function ({ detail: snapshotItem }) {
    // Check to ensure the attachment has an actor, like
    // an in-progress recording.
    if (!snapshotItem || !snapshotItem.attachment.actor) {
      return;
    }
    let { calls, thumbnails, screenshot } = snapshotItem.attachment;

    $("#reload-notice").hidden = true;
    $("#empty-notice").hidden = true;
    $("#waiting-notice").hidden = false;

    $("#debugging-pane-contents").hidden = true;
    $("#screenshot-container").hidden = true;
    $("#snapshot-filmstrip").hidden = true;

    Task.spawn(function* () {
      // Wait for a few milliseconds between presenting the function calls,
      // screenshot and thumbnails, to allow each component being
      // sequentially drawn. This gives the illusion of snappiness.

      yield DevToolsUtils.waitForTime(SNAPSHOT_DATA_DISPLAY_DELAY);
      CallsListView.showCalls(calls);
      $("#debugging-pane-contents").hidden = false;
      $("#waiting-notice").hidden = true;

      yield DevToolsUtils.waitForTime(SNAPSHOT_DATA_DISPLAY_DELAY);
      CallsListView.showThumbnails(thumbnails);
      $("#snapshot-filmstrip").hidden = false;

      yield DevToolsUtils.waitForTime(SNAPSHOT_DATA_DISPLAY_DELAY);
      CallsListView.showScreenshot(screenshot);
      $("#screenshot-container").hidden = false;

      window.emit(EVENTS.SNAPSHOT_RECORDING_SELECTED);
    });
  },

  /**
   * The click listener for the "clear" button in this container.
   */
  _onClearButtonClick: function () {
    Task.spawn(function* () {
      SnapshotsListView.empty();
      CallsListView.empty();

      $("#reload-notice").hidden = true;
      $("#empty-notice").hidden = true;
      $("#waiting-notice").hidden = true;

      if (yield gFront.isInitialized()) {
        $("#empty-notice").hidden = false;
      } else {
        $("#reload-notice").hidden = false;
      }

      $("#debugging-pane-contents").hidden = true;
      $("#screenshot-container").hidden = true;
      $("#snapshot-filmstrip").hidden = true;

      window.emit(EVENTS.SNAPSHOTS_LIST_CLEARED);
    });
  },

  /**
   * The click listener for the "record" button in this container.
   */
  _onRecordButtonClick: function () {
    this._disableRecordButton();

    if (this._recording) {
      this._stopRecordingAnimation();
      return;
    }

    // Insert a "dummy" snapshot item in the view, to hint that recording
    // has now started. However, wait for a few milliseconds before actually
    // starting the recording, since that might block rendering and prevent
    // the dummy snapshot item from being drawn.
    this.addSnapshot();

    // If this is the first item, immediately show the "Loading…" notice.
    if (this.itemCount == 1) {
      $("#empty-notice").hidden = true;
      $("#waiting-notice").hidden = false;
    }

    this._recordAnimation();
  },

  /**
   * Makes the record button able to be clicked again.
   */
  _enableRecordButton: function () {
    $("#record-snapshot").removeAttribute("disabled");
  },

  /**
   * Makes the record button unable to be clicked.
   */
  _disableRecordButton: function () {
    $("#record-snapshot").setAttribute("disabled", true);
  },

  /**
   * Begins recording an animation.
   */
  _recordAnimation: Task.async(function* () {
    if (this._recording) {
      return;
    }
    this._recording = true;
    $("#record-snapshot").setAttribute("checked", "true");

    setNamedTimeout("canvas-actor-recording", CANVAS_ACTOR_RECORDING_ATTEMPT, this._stopRecordingAnimation);

    yield DevToolsUtils.waitForTime(SNAPSHOT_START_RECORDING_DELAY);
    window.emit(EVENTS.SNAPSHOT_RECORDING_STARTED);

    gFront.recordAnimationFrame().then(snapshot => {
      if (snapshot) {
        this._onRecordSuccess(snapshot);
      } else {
        this._onRecordFailure();
      }
    });

    // Wait another delay before reenabling the button to stop the recording
    // if a recording is not found.
    yield DevToolsUtils.waitForTime(SNAPSHOT_START_RECORDING_DELAY);
    this._enableRecordButton();
  }),

  /**
   * Stops recording animation. Called when a click on the stopwatch occurs during a recording,
   * or if a recording times out.
   */
  _stopRecordingAnimation: Task.async(function* () {
    clearNamedTimeout("canvas-actor-recording");
    let actorCanStop = yield gTarget.actorHasMethod("canvas", "stopRecordingAnimationFrame");

    if (actorCanStop) {
      yield gFront.stopRecordingAnimationFrame();
    }
    // If actor does not have the method to stop recording (Fx39+),
    // manually call the record failure method. This will call a connection failure
    // on disconnect as a result of `gFront.recordAnimationFrame()` never resolving,
    // but this is better than it hanging when there is no requestAnimationFrame anyway.
    else {
      this._onRecordFailure();
    }

    this._recording = false;
    $("#record-snapshot").removeAttribute("checked");
    this._enableRecordButton();
  }),

  /**
   * Resolves from the front's recordAnimationFrame to setup the interface with the screenshots.
   */
  _onRecordSuccess: Task.async(function* (snapshotActor) {
    // Clear bail-out case if frame found in CANVAS_ACTOR_RECORDING_ATTEMPT milliseconds
    clearNamedTimeout("canvas-actor-recording");
    let snapshotItem = this.getItemAtIndex(this.itemCount - 1);
    let snapshotOverview = yield snapshotActor.getOverview();
    this.customizeSnapshot(snapshotItem, snapshotActor, snapshotOverview);

    this._recording = false;
    $("#record-snapshot").removeAttribute("checked");

    window.emit(EVENTS.SNAPSHOT_RECORDING_COMPLETED);
    window.emit(EVENTS.SNAPSHOT_RECORDING_FINISHED);
  }),

  /**
   * Called as a reject from the front's recordAnimationFrame.
   */
  _onRecordFailure: function () {
    clearNamedTimeout("canvas-actor-recording");
    showNotification(gToolbox, "canvas-debugger-timeout", L10N.getStr("recordingTimeoutFailure"));
    window.emit(EVENTS.SNAPSHOT_RECORDING_CANCELLED);
    window.emit(EVENTS.SNAPSHOT_RECORDING_FINISHED);
    this.removeLastSnapshot();
  },

  /**
   * The click listener for the "import" button in this container.
   */
  _onImportButtonClick: function () {
    let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
    fp.init(window, L10N.getStr("snapshotsList.saveDialogTitle"), Ci.nsIFilePicker.modeOpen);
    fp.appendFilter(L10N.getStr("snapshotsList.saveDialogJSONFilter"), "*.json");
    fp.appendFilter(L10N.getStr("snapshotsList.saveDialogAllFilter"), "*.*");

    fp.open(rv => {
      if (rv != Ci.nsIFilePicker.returnOK) {
        return;
      }

      let channel = NetUtil.newChannel({
        uri: NetUtil.newURI(fp.file), loadUsingSystemPrincipal: true});
      channel.contentType = "text/plain";

      NetUtil.asyncFetch(channel, (inputStream, status) => {
        if (!Components.isSuccessCode(status)) {
          console.error("Could not import recorded animation frame snapshot file.");
          return;
        }
        try {
          let string = NetUtil.readInputStreamToString(inputStream, inputStream.available());
          var data = JSON.parse(string);
        } catch (e) {
          console.error("Could not read animation frame snapshot file.");
          return;
        }
        if (data.fileType != CALLS_LIST_SERIALIZER_IDENTIFIER) {
          console.error("Unrecognized animation frame snapshot file.");
          return;
        }

        // Add a `isLoadedFromDisk` flag on everything to avoid sending invalid
        // requests to the backend, since we're not dealing with actors anymore.
        let snapshotItem = this.addSnapshot();
        snapshotItem.isLoadedFromDisk = true;
        data.calls.forEach(e => e.isLoadedFromDisk = true);

        this.customizeSnapshot(snapshotItem, data.calls, data);
      });
    });
  },

  /**
   * The click listener for the "save" button of each item in this container.
   */
  _onSaveButtonClick: function (e) {
    let snapshotItem = this.getItemForElement(e.target);

    let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
    fp.init(window, L10N.getStr("snapshotsList.saveDialogTitle"), Ci.nsIFilePicker.modeSave);
    fp.appendFilter(L10N.getStr("snapshotsList.saveDialogJSONFilter"), "*.json");
    fp.appendFilter(L10N.getStr("snapshotsList.saveDialogAllFilter"), "*.*");
    fp.defaultString = "snapshot.json";

    // Start serializing all the function call actors for the specified snapshot,
    // while the nsIFilePicker dialog is being opened. Snappy.
    let serialized = Task.spawn(function* () {
      let data = {
        fileType: CALLS_LIST_SERIALIZER_IDENTIFIER,
        version: CALLS_LIST_SERIALIZER_VERSION,
        calls: [],
        thumbnails: [],
        screenshot: null
      };
      let functionCalls = snapshotItem.attachment.calls;
      let thumbnails = snapshotItem.attachment.thumbnails;
      let screenshot = snapshotItem.attachment.screenshot;

      // Prepare all the function calls for serialization.
      yield DevToolsUtils.yieldingEach(functionCalls, (call, i) => {
        let { type, name, file, line, timestamp, argsPreview, callerPreview } = call;
        return call.getDetails().then(({ stack }) => {
          data.calls[i] = {
            type: type,
            name: name,
            file: file,
            line: line,
            stack: stack,
            timestamp: timestamp,
            argsPreview: argsPreview,
            callerPreview: callerPreview
          };
        });
      });

      // Prepare all the thumbnails for serialization.
      yield DevToolsUtils.yieldingEach(thumbnails, (thumbnail, i) => {
        let { index, width, height, flipped, pixels } = thumbnail;
        data.thumbnails.push({ index, width, height, flipped, pixels });
      });

      // Prepare the screenshot for serialization.
      let { index, width, height, flipped, pixels } = screenshot;
      data.screenshot = { index, width, height, flipped, pixels };

      let string = JSON.stringify(data);
      let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
        createInstance(Ci.nsIScriptableUnicodeConverter);

      converter.charset = "UTF-8";
      return converter.convertToInputStream(string);
    });

    // Open the nsIFilePicker and wait for the function call actors to finish
    // being serialized, in order to save the generated JSON data to disk.
    fp.open({ done: result => {
      if (result == Ci.nsIFilePicker.returnCancel) {
        return;
      }
      let footer = $(".snapshot-item-footer", snapshotItem.target);
      let save = $(".snapshot-item-save", snapshotItem.target);

      // Show a throbber and a "Saving…" label if serializing isn't immediate.
      setNamedTimeout("call-list-save", CALLS_LIST_SLOW_SAVE_DELAY, () => {
        footer.classList.add("devtools-throbber");
        save.setAttribute("disabled", "true");
        save.setAttribute("value", L10N.getStr("snapshotsList.savingLabel"));
      });

      serialized.then(inputStream => {
        let outputStream = FileUtils.openSafeFileOutputStream(fp.file);

        NetUtil.asyncCopy(inputStream, outputStream, status => {
          if (!Components.isSuccessCode(status)) {
            console.error("Could not save recorded animation frame snapshot file.");
          }
          clearNamedTimeout("call-list-save");
          footer.classList.remove("devtools-throbber");
          save.removeAttribute("disabled");
          save.setAttribute("value", L10N.getStr("snapshotsList.saveLabel"));
        });
      });
    }});
  }
});

function showNotification(toolbox, name, message) {
  let notificationBox = toolbox.getNotificationBox();
  let notification = notificationBox.getNotificationWithValue(name);
  if (!notification) {
    notificationBox.appendNotification(message, name, "", notificationBox.PRIORITY_WARNING_HIGH);
  }
}
PK
!<�k����;chrome/devtools/content/commandline/commandlineoutput.xhtml<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
  <link rel="stylesheet" href="chrome://devtools/skin/commandline.css" type="text/css"/>
</head>
<body class="gcli-body">
<div id="gcli-output-root"></div>
</body>
</html>
PK
!<M H���<chrome/devtools/content/commandline/commandlinetooltip.xhtml<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
  <link rel="stylesheet" href="chrome://devtools/skin/commandline.css" type="text/css"/>
</head>
<body class="gcli-body">
<div id="gcli-tooltip-root"></div>
<div id="gcli-tooltip-connector"></div>
</body>
</html>
PK
!<<R擾���7chrome/devtools/content/debugger/debugger-controller.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 DBG_STRINGS_URI = "devtools/client/locales/debugger.properties";
const NEW_SOURCE_IGNORED_URLS = ["debugger eval code", "XStringBundle"];
const NEW_SOURCE_DISPLAY_DELAY = 200; // ms
const FETCH_SOURCE_RESPONSE_DELAY = 200; // ms
const FRAME_STEP_CLEAR_DELAY = 100; // ms
const CALL_STACK_PAGE_SIZE = 25; // frames

// The panel's window global is an EventEmitter firing the following events:
const EVENTS = {
  // When the debugger's source editor instance finishes loading or unloading.
  EDITOR_LOADED: "Debugger:EditorLoaded",
  EDITOR_UNLOADED: "Debugger:EditorUnloaded",

  // When new sources are received from the debugger server.
  NEW_SOURCE: "Debugger:NewSource",
  SOURCES_ADDED: "Debugger:SourcesAdded",

  // When a source is shown in the source editor.
  SOURCE_SHOWN: "Debugger:EditorSourceShown",
  SOURCE_ERROR_SHOWN: "Debugger:EditorSourceErrorShown",

  // When the editor has shown a source and set the line / column position
  EDITOR_LOCATION_SET: "Debugger:EditorLocationSet",

  // When scopes, variables, properties and watch expressions are fetched and
  // displayed in the variables view.
  FETCHED_SCOPES: "Debugger:FetchedScopes",
  FETCHED_VARIABLES: "Debugger:FetchedVariables",
  FETCHED_PROPERTIES: "Debugger:FetchedProperties",
  FETCHED_BUBBLE_PROPERTIES: "Debugger:FetchedBubbleProperties",
  FETCHED_WATCH_EXPRESSIONS: "Debugger:FetchedWatchExpressions",

  // When a breakpoint has been added or removed on the debugger server.
  BREAKPOINT_ADDED: "Debugger:BreakpointAdded",
  BREAKPOINT_REMOVED: "Debugger:BreakpointRemoved",
  BREAKPOINT_CLICKED: "Debugger:BreakpointClicked",

  // When a breakpoint has been shown or hidden in the source editor
  // or the pane.
  BREAKPOINT_SHOWN_IN_EDITOR: "Debugger:BreakpointShownInEditor",
  BREAKPOINT_SHOWN_IN_PANE: "Debugger:BreakpointShownInPane",
  BREAKPOINT_HIDDEN_IN_EDITOR: "Debugger:BreakpointHiddenInEditor",
  BREAKPOINT_HIDDEN_IN_PANE: "Debugger:BreakpointHiddenInPane",

  // When a conditional breakpoint's popup is shown/hidden.
  CONDITIONAL_BREAKPOINT_POPUP_SHOWN: "Debugger:ConditionalBreakpointPopupShown",
  CONDITIONAL_BREAKPOINT_POPUP_HIDDEN: "Debugger:ConditionalBreakpointPopupHidden",

  // When event listeners are fetched or event breakpoints are updated.
  EVENT_LISTENERS_FETCHED: "Debugger:EventListenersFetched",
  EVENT_BREAKPOINTS_UPDATED: "Debugger:EventBreakpointsUpdated",

  // When a file search was performed.
  FILE_SEARCH_MATCH_FOUND: "Debugger:FileSearch:MatchFound",
  FILE_SEARCH_MATCH_NOT_FOUND: "Debugger:FileSearch:MatchNotFound",

  // When a function search was performed.
  FUNCTION_SEARCH_MATCH_FOUND: "Debugger:FunctionSearch:MatchFound",
  FUNCTION_SEARCH_MATCH_NOT_FOUND: "Debugger:FunctionSearch:MatchNotFound",

  // When a global text search was performed.
  GLOBAL_SEARCH_MATCH_FOUND: "Debugger:GlobalSearch:MatchFound",
  GLOBAL_SEARCH_MATCH_NOT_FOUND: "Debugger:GlobalSearch:MatchNotFound",

  // After the the StackFrames object has been filled with frames
  AFTER_FRAMES_REFILLED: "Debugger:AfterFramesRefilled",

  // After the stackframes are cleared and debugger won't pause anymore.
  AFTER_FRAMES_CLEARED: "Debugger:AfterFramesCleared",

  // When the options popup is showing or hiding.
  OPTIONS_POPUP_SHOWING: "Debugger:OptionsPopupShowing",
  OPTIONS_POPUP_HIDDEN: "Debugger:OptionsPopupHidden",

  // When the widgets layout has been changed.
  LAYOUT_CHANGED: "Debugger:LayoutChanged",

  // When a worker has been selected.
  WORKER_SELECTED: "Debugger::WorkerSelected"
};

// Descriptions for what a stack frame represents after the debugger pauses.
const FRAME_TYPE = {
  NORMAL: 0,
  CONDITIONAL_BREAKPOINT_EVAL: 1,
  WATCH_EXPRESSIONS_EVAL: 2,
  PUBLIC_CLIENT_EVAL: 3
};

const { BrowserLoader } = ChromeUtils.import("resource://devtools/client/shared/browser-loader.js", {});
const { require } = BrowserLoader({
  baseURI: "resource://devtools/client/debugger/",
  window,
});
const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineConstant(this, "require", require);
const { SimpleListWidget } = require("resource://devtools/client/shared/widgets/SimpleListWidget.jsm");
const { BreadcrumbsWidget } = require("resource://devtools/client/shared/widgets/BreadcrumbsWidget.jsm");
const { SideMenuWidget } = require("resource://devtools/client/shared/widgets/SideMenuWidget.jsm");
const { VariablesView } = require("resource://devtools/client/shared/widgets/VariablesView.jsm");
const { VariablesViewController, StackFrameUtils } = require("resource://devtools/client/shared/widgets/VariablesViewController.jsm");
const EventEmitter = require("devtools/shared/old-event-emitter");
const { extend } = require("devtools/shared/extend");
const { gDevTools } = require("devtools/client/framework/devtools");
const { ViewHelpers, WidgetMethods, setNamedTimeout,
        clearNamedTimeout } = require("devtools/client/shared/widgets/view-helpers");

// Use privileged promise in panel documents to prevent having them to freeze
// during toolbox destruction. See bug 1402779.
const Promise = require("Promise");

// React
const React = require("devtools/client/shared/vendor/react");
const ReactDOM = require("devtools/client/shared/vendor/react-dom");
const { Provider } = require("devtools/client/shared/vendor/react-redux");

// Used to create the Redux store
const createStore = require("devtools/client/shared/redux/create-store")({
  getTargetClient: () => DebuggerController.client,
  log: false
});
const {
  makeStateBroadcaster,
  enhanceStoreWithBroadcaster,
  combineBroadcastingReducers
} = require("devtools/client/shared/redux/non-react-subscriber");
const { bindActionCreators } = require("devtools/client/shared/vendor/redux");
const reducers = require("./content/reducers/index");
const { onReducerEvents } = require("./content/utils");

const waitUntilService = require("devtools/client/shared/redux/middleware/wait-service");
var services = {
  WAIT_UNTIL: waitUntilService.NAME
};

var Services = require("Services");
var {TargetFactory} = require("devtools/client/framework/target");
var {Toolbox} = require("devtools/client/framework/toolbox");
var DevToolsUtils = require("devtools/shared/DevToolsUtils");
var promise = require("devtools/shared/deprecated-sync-thenables");
var Editor = require("devtools/client/sourceeditor/editor");
var DebuggerEditor = require("devtools/client/sourceeditor/debugger");
var Tooltip = require("devtools/client/shared/widgets/tooltip/Tooltip");
var FastListWidget = require("devtools/client/shared/widgets/FastListWidget");
var {LocalizationHelper, ELLIPSIS} = require("devtools/shared/l10n");
var {PrefsHelper} = require("devtools/client/shared/prefs");
var {Task} = require("devtools/shared/task");

XPCOMUtils.defineConstant(this, "EVENTS", EVENTS);

ChromeUtils.defineModuleGetter(this, "Parser",
  "resource://devtools/shared/Parser.jsm");

ChromeUtils.defineModuleGetter(this, "ShortcutUtils",
  "resource://gre/modules/ShortcutUtils.jsm");

XPCOMUtils.defineLazyServiceGetter(this, "clipboardHelper",
  "@mozilla.org/widget/clipboardhelper;1", "nsIClipboardHelper");

Object.defineProperty(this, "NetworkHelper", {
  get: function () {
    return require("devtools/shared/webconsole/network-helper");
  },
  configurable: true,
  enumerable: true
});

/**
 * Localization convenience methods.
 */
var L10N = new LocalizationHelper(DBG_STRINGS_URI);

/**
 * Object defining the debugger controller components.
 */
var DebuggerController = {
  /**
   * Initializes the debugger controller.
   */
  initialize: function () {
    dumpn("Initializing the DebuggerController");

    this.startupDebugger = this.startupDebugger.bind(this);
    this.shutdownDebugger = this.shutdownDebugger.bind(this);
    this._onNavigate = this._onNavigate.bind(this);
    this._onWillNavigate = this._onWillNavigate.bind(this);
    this._onTabDetached = this._onTabDetached.bind(this);

    const broadcaster = makeStateBroadcaster(() => !!this.activeThread);
    const reducer = combineBroadcastingReducers(
      reducers,
      broadcaster.emitChange
    );
    // TODO: Bug 1228867, clean this up and probably abstract it out
    // better.
    //
    // We only want to process async event that are appropriate for
    // this page. The devtools are open across page reloads, so async
    // requests from the last page might bleed through if reloading
    // fast enough. We check to make sure the async action is part of
    // a current request, and ignore it if not.
    let store = createStore((state, action) => {
      if (action.seqId &&
         (action.status === "done" || action.status === "error") &&
         state && !state.asyncRequests.includes(action.seqId)) {
        return state;
      }
      return reducer(state, action);
    });
    store = enhanceStoreWithBroadcaster(store, broadcaster);

    // This controller right now acts as the store that's globally
    // available, so just copy the Redux API onto it.
    Object.keys(store).forEach(name => {
      this[name] = store[name];
    });
  },

  /**
   * Initializes the view.
   *
   * @return object
   *         A promise that is resolved when the debugger finishes startup.
   */
  startupDebugger: Task.async(function* () {
    if (this._startup) {
      return;
    }

    yield DebuggerView.initialize(this._target.isWorkerTarget);
    this._startup = true;
  }),

  /**
   * Destroys the view and disconnects the debugger client from the server.
   *
   * @return object
   *         A promise that is resolved when the debugger finishes shutdown.
   */
  shutdownDebugger: Task.async(function* () {
    if (this._shutdown) {
      return;
    }

    DebuggerView.destroy();
    this.StackFrames.disconnect();
    this.ThreadState.disconnect();
    if (this._target.isTabActor) {
      this.Workers.disconnect();
    }

    this.disconnect();

    this._shutdown = true;
  }),

  /**
   * Initiates remote debugging based on the current target, wiring event
   * handlers as necessary.
   *
   * @return object
   *         A promise that is resolved when the debugger finishes connecting.
   */
  connect: Task.async(function* () {
    let target = this._target;

    let { client } = target;
    target.on("close", this._onTabDetached);
    target.on("navigate", this._onNavigate);
    target.on("will-navigate", this._onWillNavigate);
    this.client = client;
    this.activeThread = this._toolbox.threadClient;

    let wasmBinarySource = !!this.client.mainRoot.traits.wasmBinarySource;

    // Disable asm.js so that we can set breakpoints and other things
    // on asm.js scripts. For WebAssembly modules allow using of binary
    // source if supported.
    yield this.reconfigureThread({ observeAsmJS: true, wasmBinarySource, });
    yield this.connectThread();

    // We need to call this to sync the state of the resume
    // button in the toolbar with the state of the thread.
    this.ThreadState._update();

    this._hideUnsupportedFeatures();
  }),

  connectThread: function () {
    const { newSource, fetchEventListeners } = bindActionCreators(actions, this.dispatch);

    // TODO: bug 806775, update the globals list using aPacket.hostAnnotations
    // from bug 801084.
    // this.client.addListener("newGlobal", ...);

    this.activeThread.addListener("newSource", (event, packet) => {
      newSource(packet.source);

      // Make sure the events listeners are up to date.
      if (DebuggerView.instrumentsPaneTab == "events-tab") {
        fetchEventListeners();
      }
    });

    if (this._target.isTabActor) {
      this.Workers.connect();
    }
    this.ThreadState.connect();
    this.StackFrames.connect();

    // Load all of the sources. Note that the server will actually
    // emit individual `newSource` notifications, which trigger
    // separate actions, so this won't do anything other than force
    // the server to traverse sources.
    this.dispatch(actions.loadSources()).then(() => {
      // If the engine is already paused, update the UI to represent the
      // paused state
      if (this.activeThread) {
        const pausedPacket = this.activeThread.getLastPausePacket();
        DebuggerView.Toolbar.toggleResumeButtonState(
          this.activeThread.state,
          !!pausedPacket
        );
        if (pausedPacket) {
          this.StackFrames._onPaused("paused", pausedPacket);
        }
      }
    });
  },

  /**
   * Disconnects the debugger client and removes event handlers as necessary.
   */
  disconnect: function () {
    // Return early if the client didn't even have a chance to instantiate.
    if (!this.client) {
      return;
    }

    this.client.removeListener("newGlobal");
    this.activeThread.removeListener("newSource");
    this.activeThread.removeListener("blackboxchange");

    this._connected = false;
    this.client = null;
    this.activeThread = null;
  },

  _hideUnsupportedFeatures: function () {
    if (this.client.mainRoot.traits.noPrettyPrinting) {
      DebuggerView.Sources.hidePrettyPrinting();
    }

    if (this.client.mainRoot.traits.noBlackBoxing) {
      DebuggerView.Sources.hideBlackBoxing();
    }
  },

  _onWillNavigate: function (opts = {}) {
    // Reset UI.
    DebuggerView.handleTabNavigation();
    if (!opts.noUnload) {
      this.dispatch(actions.unload());
    }

    // Discard all the cached parsed sources *before* the target
    // starts navigating. Sources may be fetched during navigation, in
    // which case we don't want to hang on to the old source contents.
    DebuggerController.Parser.clearCache();
    SourceUtils.clearCache();

    // Prevent performing any actions that were scheduled before
    // navigation.
    clearNamedTimeout("new-source");
    clearNamedTimeout("event-breakpoints-update");
    clearNamedTimeout("event-listeners-fetch");
  },

  _onNavigate: function () {
    this.ThreadState.handleTabNavigation();
    this.StackFrames.handleTabNavigation();
  },

  /**
   * Called when the debugged tab is closed.
   */
  _onTabDetached: function () {
    this.shutdownDebugger();
  },

  /**
   * Warn if resuming execution produced a wrongOrder error.
   */
  _ensureResumptionOrder: function (aResponse) {
    if (aResponse.error == "wrongOrder") {
      DebuggerView.Toolbar.showResumeWarning(aResponse.lastPausedUrl);
    }
  },

  /**
   * Detach and reattach to the thread actor with useSourceMaps true, blow
   * away old sources and get them again.
   */
  reconfigureThread: function (opts) {
    const deferred = promise.defer();
    this.activeThread.reconfigure(
      opts,
      aResponse => {
        if (aResponse.error) {
          deferred.reject(aResponse.error);
          return;
        }

        if (("useSourceMaps" in opts) || ("autoBlackBox" in opts)) {
          // Reset the view and fetch all the sources again.
          DebuggerView.handleTabNavigation();
          this.dispatch(actions.unload());
          this.dispatch(actions.loadSources());

          // Update the stack frame list.
          if (this.activeThread.paused) {
            this.activeThread._clearFrames();
            this.activeThread.fillFrames(CALL_STACK_PAGE_SIZE);
          }
        }

        deferred.resolve();
      }
    );
    return deferred.promise;
  },

  waitForSourcesLoaded: function () {
    const deferred = promise.defer();
    this.dispatch({
      type: services.WAIT_UNTIL,
      predicate: action => (action.type === constants.LOAD_SOURCES &&
                            action.status === "done"),
      run: deferred.resolve
    });
    return deferred.promise;
  },

  waitForSourceShown: function (name) {
    const deferred = promise.defer();
    window.on(EVENTS.SOURCE_SHOWN, function onShown(_, source) {
      if (source.url.includes(name)) {
        window.off(EVENTS.SOURCE_SHOWN, onShown);
        deferred.resolve();
      }
    });
    return deferred.promise;
  },

  _startup: false,
  _shutdown: false,
  _connected: false,
  client: null,
  activeThread: null
};

function Workers() {
  this._workerForms = Object.create(null);
  this._onWorkerListChanged = this._onWorkerListChanged.bind(this);
  this._onWorkerSelect = this._onWorkerSelect.bind(this);
}

Workers.prototype = {
  get _tabClient() {
    return DebuggerController._target.activeTab;
  },

  connect: function () {
    if (!Prefs.workersEnabled) {
      return;
    }

    this._updateWorkerList();
    this._tabClient.addListener("workerListChanged", this._onWorkerListChanged);
  },

  disconnect: function () {
    this._tabClient.removeListener("workerListChanged", this._onWorkerListChanged);
  },

  _updateWorkerList: function () {
    if (!this._tabClient.listWorkers) {
      return;
    }

    this._tabClient.listWorkers((response) => {
      let workerForms = Object.create(null);
      for (let worker of response.workers) {
        workerForms[worker.actor] = worker;
      }

      for (let workerActor in this._workerForms) {
        if (!(workerActor in workerForms)) {
          DebuggerView.Workers.removeWorker(this._workerForms[workerActor]);
          delete this._workerForms[workerActor];
        }
      }

      for (let workerActor in workerForms) {
        if (!(workerActor in this._workerForms)) {
          let workerForm = workerForms[workerActor];
          this._workerForms[workerActor] = workerForm;
          DebuggerView.Workers.addWorker(workerForm);
        }
      }
    });
  },

  _onWorkerListChanged: function () {
    this._updateWorkerList();
  },

  _onWorkerSelect: function (workerForm) {
    DebuggerController.client.attachWorker(workerForm.actor, (response, workerClient) => {
      let toolbox = gDevTools.showToolbox(TargetFactory.forWorker(workerClient),
                                          "jsdebugger", Toolbox.HostType.WINDOW);
      window.emit(EVENTS.WORKER_SELECTED, toolbox);
    });
  }
};

/**
 * ThreadState keeps the UI up to date with the state of the
 * thread (paused/attached/etc.).
 */
function ThreadState() {
  this._update = this._update.bind(this);
  this.interruptedByResumeButton = false;
}

ThreadState.prototype = {
  get activeThread() {
    return DebuggerController.activeThread;
  },

  /**
   * Connect to the current thread client.
   */
  connect: function () {
    dumpn("ThreadState is connecting...");
    this.activeThread.addListener("paused", this._update);
    this.activeThread.addListener("resumed", this._update);
  },

  /**
   * Disconnect from the client.
   */
  disconnect: function () {
    if (!this.activeThread) {
      return;
    }
    dumpn("ThreadState is disconnecting...");
    this.activeThread.removeListener("paused", this._update);
    this.activeThread.removeListener("resumed", this._update);
  },

  /**
   * Handles any initialization on a tab navigation event issued by the client.
   */
  handleTabNavigation: function () {
    if (!this.activeThread) {
      return;
    }
    dumpn("Handling tab navigation in the ThreadState");
    this._update();
  },

  /**
   * Update the UI after a thread state change.
   */
  _update: function (aEvent, aPacket) {
    if (aEvent == "paused") {
      if (aPacket.why.type == "interrupted" &&
          this.interruptedByResumeButton) {
        // Interrupt requests suppressed by default, but if this is an
        // explicit interrupt by the pause button we want to emit it.
        gTarget.emit("thread-paused", aPacket);
      } else if (aPacket.why.type == "breakpointConditionThrown" && aPacket.why.message) {
        let where = aPacket.frame.where;
        let aLocation = {
          line: where.line,
          column: where.column,
          actor: where.source ? where.source.actor : null
        };
        DebuggerView.Sources.showBreakpointConditionThrownMessage(
          aLocation,
          aPacket.why.message
        );
      }
    }

    this.interruptedByResumeButton = false;
    DebuggerView.Toolbar.toggleResumeButtonState(
      this.activeThread.state,
      aPacket ? aPacket.frame : false
    );
  }
};

/**
 * Keeps the stack frame list up-to-date, using the thread client's
 * stack frame cache.
 */
function StackFrames() {
  this._onPaused = this._onPaused.bind(this);
  this._onResumed = this._onResumed.bind(this);
  this._onFrames = this._onFrames.bind(this);
  this._onFramesCleared = this._onFramesCleared.bind(this);
  this._onBlackBoxChange = this._onBlackBoxChange.bind(this);
  this._onPrettyPrintChange = this._onPrettyPrintChange.bind(this);
  this._afterFramesCleared = this._afterFramesCleared.bind(this);
  this.evaluate = this.evaluate.bind(this);
}

StackFrames.prototype = {
  get activeThread() {
    return DebuggerController.activeThread;
  },

  currentFrameDepth: -1,
  _currentFrameDescription: FRAME_TYPE.NORMAL,
  _syncedWatchExpressions: null,
  _currentWatchExpressions: null,
  _currentBreakpointLocation: null,
  _currentEvaluation: null,
  _currentException: null,
  _currentReturnedValue: null,

  /**
   * Connect to the current thread client.
   */
  connect: function () {
    dumpn("StackFrames is connecting...");
    this.activeThread.addListener("paused", this._onPaused);
    this.activeThread.addListener("resumed", this._onResumed);
    this.activeThread.addListener("framesadded", this._onFrames);
    this.activeThread.addListener("framescleared", this._onFramesCleared);
    this.activeThread.addListener("blackboxchange", this._onBlackBoxChange);
    this.activeThread.addListener("prettyprintchange", this._onPrettyPrintChange);
    this.handleTabNavigation();
  },

  /**
   * Disconnect from the client.
   */
  disconnect: function () {
    if (!this.activeThread) {
      return;
    }
    dumpn("StackFrames is disconnecting...");
    this.activeThread.removeListener("paused", this._onPaused);
    this.activeThread.removeListener("resumed", this._onResumed);
    this.activeThread.removeListener("framesadded", this._onFrames);
    this.activeThread.removeListener("framescleared", this._onFramesCleared);
    this.activeThread.removeListener("blackboxchange", this._onBlackBoxChange);
    this.activeThread.removeListener("prettyprintchange", this._onPrettyPrintChange);
    clearNamedTimeout("frames-cleared");
  },

  /**
   * Handles any initialization on a tab navigation event issued by the client.
   */
  handleTabNavigation: function () {
    dumpn("Handling tab navigation in the StackFrames");
    // Nothing to do here yet.
  },

  /**
   * Handler for the thread client's paused notification.
   *
   * @param string aEvent
   *        The name of the notification ("paused" in this case).
   * @param object aPacket
   *        The response packet.
   */
  _onPaused: function (aEvent, aPacket) {
    switch (aPacket.why.type) {
      // If paused by a breakpoint, store the breakpoint location.
      case "breakpoint":
        this._currentBreakpointLocation = aPacket.frame.where;
        break;
      case "breakpointConditionThrown":
        this._currentBreakpointLocation = aPacket.frame.where;
        this._conditionThrowMessage = aPacket.why.message;
        break;
      // If paused by a client evaluation, store the evaluated value.
      case "clientEvaluated":
        this._currentEvaluation = aPacket.why.frameFinished;
        break;
      // If paused by an exception, store the exception value.
      case "exception":
        this._currentException = aPacket.why.exception;
        break;
      // If paused while stepping out of a frame, store the returned value or
      // thrown exception.
      case "resumeLimit":
        if (!aPacket.why.frameFinished) {
          break;
        } else if (aPacket.why.frameFinished.throw) {
          this._currentException = aPacket.why.frameFinished.throw;
        } else if (aPacket.why.frameFinished.return) {
          this._currentReturnedValue = aPacket.why.frameFinished.return;
        }
        break;
      // If paused by an explicit interrupt, which are generated by the slow
      // script dialog and internal events such as setting breakpoints, ignore
      // the event to avoid UI flicker.
      case "interrupted":
        if (!aPacket.why.onNext) {
          return;
        }
        break;
    }

    this.activeThread.fillFrames(CALL_STACK_PAGE_SIZE);
    // Focus the editor, but don't steal focus from the split console.
    if (!DebuggerController._toolbox.isSplitConsoleFocused()) {
      DebuggerView.editor.focus();
    }
  },

  /**
   * Handler for the thread client's resumed notification.
   */
  _onResumed: function () {
    // Prepare the watch expression evaluation string for the next pause.
    if (this._currentFrameDescription != FRAME_TYPE.WATCH_EXPRESSIONS_EVAL) {
      this._currentWatchExpressions = this._syncedWatchExpressions;
    }
  },

  /**
   * Handler for the thread client's framesadded notification.
   */
  _onFrames: Task.async(function* () {
    // Ignore useless notifications.
    if (!this.activeThread || !this.activeThread.cachedFrames.length) {
      return;
    }
    if (this._currentFrameDescription != FRAME_TYPE.NORMAL &&
        this._currentFrameDescription != FRAME_TYPE.PUBLIC_CLIENT_EVAL) {
      return;
    }

    // TODO: remove all of this deprecated code: Bug 990137.
    yield this._handleConditionalBreakpoint();

    // TODO: handle all of this server-side: Bug 832470, comment 14.
    yield this._handleWatchExpressions();

    // Make sure the debugger view panes are visible, then refill the frames.
    DebuggerView.showInstrumentsPane();
    this._refillFrames();

    // No additional processing is necessary for this stack frame.
    if (this._currentFrameDescription != FRAME_TYPE.NORMAL) {
      this._currentFrameDescription = FRAME_TYPE.NORMAL;
    }
  }),

  /**
   * Fill the StackFrames view with the frames we have in the cache, compressing
   * frames which have black boxed sources into single frames.
   */
  _refillFrames: function () {
    // Make sure all the previous stackframes are removed before re-adding them.
    DebuggerView.StackFrames.empty();

    for (let frame of this.activeThread.cachedFrames) {
      let { depth, source, where: { line, column } } = frame;

      let isBlackBoxed = source ? this.activeThread.source(source).isBlackBoxed : false;
      DebuggerView.StackFrames.addFrame(frame, line, column, depth, isBlackBoxed);
    }

    DebuggerView.StackFrames.selectedDepth = Math.max(this.currentFrameDepth, 0);
    DebuggerView.StackFrames.dirty = this.activeThread.moreFrames;

    DebuggerView.StackFrames.addCopyContextMenu();

    window.emit(EVENTS.AFTER_FRAMES_REFILLED);
  },

  /**
   * Handler for the thread client's framescleared notification.
   */
  _onFramesCleared: function () {
    switch (this._currentFrameDescription) {
      case FRAME_TYPE.NORMAL:
        this._currentEvaluation = null;
        this._currentException = null;
        this._currentReturnedValue = null;
        break;
      case FRAME_TYPE.CONDITIONAL_BREAKPOINT_EVAL:
        this._currentBreakpointLocation = null;
        break;
      case FRAME_TYPE.WATCH_EXPRESSIONS_EVAL:
        this._currentWatchExpressions = null;
        break;
    }

    // After each frame step (in, over, out), framescleared is fired, which
    // forces the UI to be emptied and rebuilt on framesadded. Most of the times
    // this is not necessary, and will result in a brief redraw flicker.
    // To avoid it, invalidate the UI only after a short time if necessary.
    setNamedTimeout("frames-cleared", FRAME_STEP_CLEAR_DELAY, this._afterFramesCleared);
  },

  /**
   * Handler for the debugger's blackboxchange notification.
   */
  _onBlackBoxChange: function () {
    if (this.activeThread.state == "paused") {
      // Hack to avoid selecting the topmost frame after blackboxing a source.
      this.currentFrameDepth = NaN;
      this._refillFrames();
    }
  },

  /**
   * Handler for the debugger's prettyprintchange notification.
   */
  _onPrettyPrintChange: function () {
    if (this.activeThread.state != "paused") {
      return;
    }
    // Makes sure the selected source remains selected
    // after the fillFrames is called.
    const source = DebuggerView.Sources.selectedValue;

    this.activeThread.fillFrames(CALL_STACK_PAGE_SIZE, () => {
      DebuggerView.Sources.selectedValue = source;
    });
  },

  /**
   * Called soon after the thread client's framescleared notification.
   */
  _afterFramesCleared: function () {
    // Ignore useless notifications.
    if (this.activeThread.cachedFrames.length) {
      return;
    }
    DebuggerView.editor.clearDebugLocation();
    DebuggerView.StackFrames.empty();
    DebuggerView.Sources.unhighlightBreakpoint();
    DebuggerView.WatchExpressions.toggleContents(true);
    DebuggerView.Variables.empty(0);

    window.emit(EVENTS.AFTER_FRAMES_CLEARED);
  },

  /**
   * Marks the stack frame at the specified depth as selected and updates the
   * properties view with the stack frame's data.
   *
   * @param number aDepth
   *        The depth of the frame in the stack.
   */
  selectFrame: function (aDepth) {
    // Make sure the frame at the specified depth exists first.
    let frame = this.activeThread.cachedFrames[this.currentFrameDepth = aDepth];
    if (!frame) {
      return;
    }

    // Check if the frame does not represent the evaluation of debuggee code.
    let { environment, where, source } = frame;
    if (!environment) {
      return;
    }

    // Don't change the editor's location if the execution was paused by a
    // public client evaluation. This is useful for adding overlays on
    // top of the editor, like a variable inspection popup.
    let isClientEval = this._currentFrameDescription == FRAME_TYPE.PUBLIC_CLIENT_EVAL;
    let isPopupShown = DebuggerView.VariableBubble.contentsShown();
    if (!isClientEval && !isPopupShown) {
      // Move the editor's caret to the proper url and line.
      DebuggerView.setEditorLocation(source.actor, where.line);
    } else {
      // Highlight the line where the execution is paused in the editor.
      DebuggerView.setEditorLocation(source.actor, where.line, { noCaret: true });
    }

    // Highlight the breakpoint at the line and column if it exists.
    DebuggerView.Sources.highlightBreakpointAtCursor();

    // Don't display the watch expressions textbox inputs in the pane.
    DebuggerView.WatchExpressions.toggleContents(false);

    // Start recording any added variables or properties in any scope and
    // clear existing scopes to create each one dynamically.
    DebuggerView.Variables.empty();

    // If watch expressions evaluation results are available, create a scope
    // to contain all the values.
    if (this._syncedWatchExpressions && aDepth == 0) {
      let label = L10N.getStr("watchExpressionsScopeLabel");
      let scope = DebuggerView.Variables.addScope(label,
        "variables-view-watch-expressions");

      // Customize the scope for holding watch expressions evaluations.
      scope.descriptorTooltip = false;
      scope.contextMenuId = "debuggerWatchExpressionsContextMenu";
      scope.separatorStr = L10N.getStr("watchExpressionsSeparatorLabel2");
      scope.switch = DebuggerView.WatchExpressions.switchExpression;
      scope.delete = DebuggerView.WatchExpressions.deleteExpression;

      // The evaluation hasn't thrown, so fetch and add the returned results.
      this._fetchWatchExpressions(scope, this._currentEvaluation.return);

      // The watch expressions scope is always automatically expanded.
      scope.expand();
    }

    do {
      // Create a scope to contain all the inspected variables in the
      // current environment.
      let label = StackFrameUtils.getScopeLabel(environment);
      let scope = DebuggerView.Variables.addScope(label);
      let innermost = environment == frame.environment;

      // Handle special additions to the innermost scope.
      if (innermost) {
        this._insertScopeFrameReferences(scope, frame);
      }

      // Handle the expansion of the scope, lazily populating it with the
      // variables in the current environment.
      DebuggerView.Variables.controller.addExpander(scope, environment);

      // The innermost scope is always automatically expanded, because it
      // contains the variables in the current stack frame which are likely to
      // be inspected. The previously expanded scopes are also reexpanded here.
      if (innermost || DebuggerView.Variables.wasExpanded(scope)) {
        scope.expand();
      }
    } while ((environment = environment.parent));

    // Signal that scope environments have been shown.
    window.emit(EVENTS.FETCHED_SCOPES);
  },

  /**
   * Loads more stack frames from the debugger server cache.
   */
  addMoreFrames: function () {
    this.activeThread.fillFrames(
      this.activeThread.cachedFrames.length + CALL_STACK_PAGE_SIZE);
  },

  /**
   * Evaluate an expression in the context of the selected frame.
   *
   * @param string expression
   *        The expression to evaluate.
   * @param object options [optional]
   *        Additional options for this client evaluation:
   *          - depth: the frame depth used for evaluation, 0 being the topmost.
   *          - meta: some meta-description for what this evaluation represents.
   * @return object
   *         A promise that is resolved when the evaluation finishes,
   *         or rejected if there was no stack frame available or some
   *         other error occurred.
   */
  evaluate: async function (expression, options = {}) {
    let depth = "depth" in options
      ? options.depth
      : this.currentFrameDepth;
    let frame = this.activeThread.cachedFrames[depth];
    if (frame == null) {
      throw new Error("No stack frame available.");
    }

    const onThreadPaused = this.activeThread.addOneTimeListener("paused");

    let meta = "meta" in options
      ? options.meta
      : FRAME_TYPE.PUBLIC_CLIENT_EVAL;
    this._currentFrameDescription = meta;
    this.activeThread.eval(frame.actor, expression);

    const packet = await onThreadPaused;

    let { type, frameFinished } = packet.why;
    if (type !== "clientEvaluated") {
      throw new Error("Active thread paused unexpectedly.");
    }

    return frameFinished;
  },

  /**
   * Add nodes for special frame references in the innermost scope.
   *
   * @param Scope aScope
   *        The scope where the references will be placed into.
   * @param object aFrame
   *        The frame to get some references from.
   */
  _insertScopeFrameReferences: function (aScope, aFrame) {
    // Add any thrown exception.
    if (this._currentException) {
      let excRef = aScope.addItem("<exception>", { value: this._currentException },
                                  { internalItem: true });
      DebuggerView.Variables.controller.addExpander(excRef, this._currentException);
    }
    // Add any returned value.
    if (this._currentReturnedValue) {
      let retRef = aScope.addItem("<return>",
                                  { value: this._currentReturnedValue },
                                  { internalItem: true });
      DebuggerView.Variables.controller.addExpander(retRef, this._currentReturnedValue);
    }
    // Add "this".
    if (aFrame.this) {
      let thisRef = aScope.addItem("this", { value: aFrame.this });
      DebuggerView.Variables.controller.addExpander(thisRef, aFrame.this);
    }
  },

  /**
   * Handles conditional breakpoints when the debugger pauses and the
   * stackframes are received.
   *
   * We moved conditional breakpoint handling to the server, but
   * need to support it in the client for a while until most of the
   * server code in production is updated with it.
   * TODO: remove all of this deprecated code: Bug 990137.
   *
   * @return object
   *         A promise that is resolved after a potential breakpoint's
   *         conditional expression is evaluated. If there's no breakpoint
   *         where the debugger is paused, the promise is resolved immediately.
   */
  _handleConditionalBreakpoint: Task.async(function* () {
    if (gClient.mainRoot.traits.conditionalBreakpoints) {
      return;
    }
    let breakLocation = this._currentBreakpointLocation;
    if (!breakLocation) {
      return;
    }

    let bp = queries.getBreakpoint(DebuggerController.getState(), {
      actor: breakLocation.source.actor,
      line: breakLocation.line
    });
    let conditionalExpression = bp.condition;
    if (!conditionalExpression) {
      return;
    }

    // Evaluating the current breakpoint's conditional expression will
    // cause the stack frames to be cleared and active thread to pause,
    // sending a 'clientEvaluated' packed and adding the frames again.
    let evaluationOptions = { depth: 0, meta: FRAME_TYPE.CONDITIONAL_BREAKPOINT_EVAL };
    yield this.evaluate(conditionalExpression, evaluationOptions);
    this._currentFrameDescription = FRAME_TYPE.NORMAL;

    // If the breakpoint's conditional expression evaluation is falsy
    // and there is no exception, automatically resume execution.
    if (!this._currentEvaluation.throw &&
        VariablesView.isFalsy({ value: this._currentEvaluation.return })) {
      this.activeThread.resume(DebuggerController._ensureResumptionOrder);
    }
  }),

  /**
   * Handles watch expressions when the debugger pauses and the stackframes
   * are received.
   *
   * @return object
   *         A promise that is resolved after the potential watch expressions
   *         are evaluated. If there are no watch expressions where the debugger
   *         is paused, the promise is resolved immediately.
   */
  _handleWatchExpressions: Task.async(function* () {
    // Ignore useless notifications.
    if (!this.activeThread || !this.activeThread.cachedFrames.length) {
      return;
    }

    let watchExpressions = this._currentWatchExpressions;
    if (!watchExpressions) {
      return;
    }

    // Evaluation causes the stack frames to be cleared and active thread to
    // pause, sending a 'clientEvaluated' packet and adding the frames again.
    let evaluationOptions = { depth: 0, meta: FRAME_TYPE.WATCH_EXPRESSIONS_EVAL };
    yield this.evaluate(watchExpressions, evaluationOptions);
    this._currentFrameDescription = FRAME_TYPE.NORMAL;

    // If an error was thrown during the evaluation of the watch expressions
    // or the evaluation was terminated from the slow script dialog, then at
    // least one expression evaluation could not be performed. So remove the
    // most recent watch expression and try again.
    if (this._currentEvaluation.throw || this._currentEvaluation.terminated) {
      DebuggerView.WatchExpressions.removeAt(0);
      yield DebuggerController.StackFrames.syncWatchExpressions();
    }
  }),

  /**
   * Adds the watch expressions evaluation results to a scope in the view.
   *
   * @param Scope aScope
   *        The scope where the watch expressions will be placed into.
   * @param object aExp
   *        The grip of the evaluation results.
   */
  _fetchWatchExpressions: function (aScope, aExp) {
    // Fetch the expressions only once.
    if (aScope._fetched) {
      return;
    }
    aScope._fetched = true;

    // Add nodes for every watch expression in scope.
    this.activeThread.pauseGrip(aExp).getPrototypeAndProperties(aResponse => {
      let ownProperties = aResponse.ownProperties;
      let totalExpressions = DebuggerView.WatchExpressions.itemCount;

      for (let i = 0; i < totalExpressions; i++) {
        let name = DebuggerView.WatchExpressions.getString(i);
        let expVal = ownProperties[i].value;
        let expRef = aScope.addItem(name, ownProperties[i]);
        DebuggerView.Variables.controller.addExpander(expRef, expVal);

        // Revert some of the custom watch expressions scope presentation flags,
        // so that they don't propagate to child items.
        expRef.switch = null;
        expRef.delete = null;
        expRef.descriptorTooltip = true;
        expRef.separatorStr = L10N.getStr("variablesSeparatorLabel");
      }

      // Signal that watch expressions have been fetched.
      window.emit(EVENTS.FETCHED_WATCH_EXPRESSIONS);
    });
  },

  /**
   * Updates a list of watch expressions to evaluate on each pause.
   * TODO: handle all of this server-side: Bug 832470, comment 14.
   */
  syncWatchExpressions: function () {
    let list = DebuggerView.WatchExpressions.getAllStrings();

    // Sanity check all watch expressions before syncing them. To avoid
    // having the whole watch expressions array throw because of a single
    // faulty expression, simply convert it to a string describing the error.
    // There's no other information necessary to be offered in such cases.
    let sanitizedExpressions = list.map(aString => {
      // Reflect.parse throws when it encounters a syntax error.
      try {
        Parser.reflectionAPI.parse(aString);
        return aString; // Watch expression can be executed safely.
      } catch (e) {
        function safelyEscape(aString) {
          // Convert `str`, a string, to JSON -- that is, to a string beginning
          // and ending with double-quotes, followed by string contents escaped
          // such that the overall string contents are a JSON string literal.
          let str = JSON.stringify(aString);

          // Remove the leading and trailing double-quotes.
          str = str.substring(1, str.length - 1);

          // JSON string literals are not a subset of JS string literals in this
          // one weird case: JSON string literals can directly contain U+2028
          // LINE SEPARATOR and U+2029 PARAGRAPH SEPARATOR.  Replace these code
          // points with escaped forms.
          str = str.replace(/\u2028/g, "\\u2028");
          str = str.replace(/\u2029/g, "\\u2029");

          return str;
        }
        return "\"" +
               safelyEscape(e.name) + ": " +
               safelyEscape(e.message) +
               "\""; // Syntax error.
      }
    });

    if (!sanitizedExpressions.length) {
      this._currentWatchExpressions = null;
      this._syncedWatchExpressions = null;
    } else {
      this._syncedWatchExpressions =
      this._currentWatchExpressions = "[" +
        sanitizedExpressions.map(aString =>
          "eval(\"" +
            "try {" +
              // Make sure all quotes are escaped in the expression's syntax,
              // and add a newline after the statement to avoid comments
              // breaking the code integrity inside the eval block.
              aString.replace(/\\/g, "\\\\").replace(/"/g, "\\$&") + "\" + " + "'\\n'" + " + \"" +
            "} catch (e) {" +
              "e.name + ': ' + e.message;" + // TODO: Bug 812765, 812764.
            "}" +
          "\")"
        ).join(",") +
      "]";
    }

    this.currentFrameDepth = -1;
    return this._onFrames();
  }
};

/**
 * Shortcuts for accessing various debugger preferences.
 */
var Prefs = new PrefsHelper("devtools", {
  workersAndSourcesWidth: ["Int", "debugger.ui.panes-workers-and-sources-width"],
  instrumentsWidth: ["Int", "debugger.ui.panes-instruments-width"],
  panesVisibleOnStartup: ["Bool", "debugger.ui.panes-visible-on-startup"],
  variablesSortingEnabled: ["Bool", "debugger.ui.variables-sorting-enabled"],
  variablesOnlyEnumVisible: ["Bool", "debugger.ui.variables-only-enum-visible"],
  variablesSearchboxVisible: ["Bool", "debugger.ui.variables-searchbox-visible"],
  pauseOnExceptions: ["Bool", "debugger.pause-on-exceptions"],
  ignoreCaughtExceptions: ["Bool", "debugger.ignore-caught-exceptions"],
  sourceMapsEnabled: ["Bool", "debugger.source-maps-enabled"],
  prettyPrintEnabled: ["Bool", "debugger.pretty-print-enabled"],
  autoPrettyPrint: ["Bool", "debugger.auto-pretty-print"],
  workersEnabled: ["Bool", "debugger.workers"],
  editorTabSize: ["Int", "editor.tabsize"],
  autoBlackBox: ["Bool", "debugger.auto-black-box"],
});

/**
 * Convenient way of emitting events from the panel window.
 */
EventEmitter.decorate(this);

/**
 * Preliminary setup for the DebuggerController object.
 */
DebuggerController.initialize();
DebuggerController.Parser = new Parser();
DebuggerController.Workers = new Workers();
DebuggerController.ThreadState = new ThreadState();
DebuggerController.StackFrames = new StackFrames();

/**
 * Export some properties to the global scope for easier access.
 */
Object.defineProperties(window, {
  "gTarget": {
    get: function () {
      return DebuggerController._target;
    },
    configurable: true
  },
  "gHostType": {
    get: function () {
      return DebuggerView._hostType;
    },
    configurable: true
  },
  "gClient": {
    get: function () {
      return DebuggerController.client;
    },
    configurable: true
  },
  "gThreadClient": {
    get: function () {
      return DebuggerController.activeThread;
    },
    configurable: true
  },
  "gCallStackPageSize": {
    get: function () {
      return CALL_STACK_PAGE_SIZE;
    },
    configurable: true
  }
});

/**
 * Helper method for debugging.
 * @param string
 */
function dumpn(str) {
  if (wantLogging) {
    dump("DBG-FRONTEND: " + str + "\n");
  }
}

var wantLogging = Services.prefs.getBoolPref("devtools.debugger.log");
PK
!<s���zz1chrome/devtools/content/debugger/debugger-view.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 SOURCE_URL_DEFAULT_MAX_LENGTH = 64; // chars
const STACK_FRAMES_SOURCE_URL_MAX_LENGTH = 15; // chars
const STACK_FRAMES_SOURCE_URL_TRIM_SECTION = "center";
const STACK_FRAMES_SCROLL_DELAY = 100; // ms
const BREAKPOINT_SMALL_WINDOW_WIDTH = 850; // px
const RESULTS_PANEL_POPUP_POSITION = "before_end";
const RESULTS_PANEL_MAX_RESULTS = 10;
const FILE_SEARCH_ACTION_MAX_DELAY = 300; // ms
const GLOBAL_SEARCH_EXPAND_MAX_RESULTS = 50;
const GLOBAL_SEARCH_LINE_MAX_LENGTH = 300; // chars
const GLOBAL_SEARCH_ACTION_MAX_DELAY = 1500; // ms
const FUNCTION_SEARCH_ACTION_MAX_DELAY = 400; // ms
const SEARCH_GLOBAL_FLAG = "!";
const SEARCH_FUNCTION_FLAG = "@";
const SEARCH_TOKEN_FLAG = "#";
const SEARCH_LINE_FLAG = ":";
const SEARCH_VARIABLE_FLAG = "*";
const SEARCH_AUTOFILL = [SEARCH_GLOBAL_FLAG, SEARCH_FUNCTION_FLAG, SEARCH_TOKEN_FLAG];
const TOOLBAR_ORDER_POPUP_POSITION = "topcenter bottomleft";
const RESIZE_REFRESH_RATE = 50; // ms

const EventListenersView = require("./content/views/event-listeners-view");
const SourcesView = require("./content/views/sources-view");
var actions = Object.assign(
  {},
  require("./content/globalActions"),
  require("./content/actions/breakpoints"),
  require("./content/actions/sources"),
  require("./content/actions/event-listeners")
);
var queries = require("./content/queries");
var constants = require("./content/constants");

/**
 * Object defining the debugger view components.
 */
var DebuggerView = {

  /**
   * This is attached so tests can change it without needing to load an
   * actual large file in automation
   */
  LARGE_FILE_SIZE: 1048576, // 1 MB in bytes

  /**
   * Initializes the debugger view.
   *
   * @return object
   *         A promise that is resolved when the view finishes initializing.
   */
  initialize: function (isWorker) {
    if (this._startup) {
      return this._startup;
    }
    const deferred = promise.defer();
    this._startup = deferred.promise;

    this._initializePanes();
    this._initializeEditor(deferred.resolve);
    this.Toolbar.initialize();
    this.Options.initialize();
    this.Filtering.initialize();
    this.StackFrames.initialize();
    this.StackFramesClassicList.initialize();
    this.Workers.initialize();
    this.Sources.initialize(isWorker);
    this.VariableBubble.initialize();
    this.WatchExpressions.initialize();
    this.EventListeners.initialize();
    this.GlobalSearch.initialize();
    this._initializeVariablesView();

    this._editorSource = {};
    this._editorDocuments = {};

    this.editor.on("cursorActivity", this.Sources._onEditorCursorActivity);

    this.controller = DebuggerController;
    const getState = this.controller.getState;

    onReducerEvents(this.controller, {
      "source-text-loaded": this.renderSourceText,
      "source-selected": this.renderSourceText,
      "blackboxed": this.renderBlackBoxed,
      "prettyprinted": this.renderPrettyPrinted,
      "breakpoint-added": this.addEditorBreakpoint,
      "breakpoint-enabled": this.addEditorBreakpoint,
      "breakpoint-disabled": this.removeEditorBreakpoint,
      "breakpoint-removed": this.removeEditorBreakpoint,
      "breakpoint-condition-updated": this.renderEditorBreakpointCondition,
      "breakpoint-moved": ({ breakpoint, prevLocation }) => {
        const selectedSource = queries.getSelectedSource(getState());
        const { location } = breakpoint;

        if (selectedSource &&
           selectedSource.actor === location.actor) {
          this.editor.moveBreakpoint(this.toEditorLine(prevLocation.line),
                                     this.toEditorLine(location.line));
        }
      }
    }, this);

    return deferred.promise;
  },

  /**
   * Destroys the debugger view.
   *
   * @return object
   *         A promise that is resolved when the view finishes destroying.
   */
  destroy: function () {
    if (this._hasShutdown) {
      return;
    }
    this._hasShutdown = true;

    window.removeEventListener("resize", this._onResize);
    this.editor.off("cursorActivity", this.Sources._onEditorCursorActivity);

    this.Toolbar.destroy();
    this.Options.destroy();
    this.Filtering.destroy();
    this.StackFrames.destroy();
    this.StackFramesClassicList.destroy();
    this.Sources.destroy();
    this.VariableBubble.destroy();
    this.WatchExpressions.destroy();
    this.EventListeners.destroy();
    this.GlobalSearch.destroy();
    this._destroyPanes();

    this.editor.destroy();
    this.editor = null;

    this.controller.dispatch(actions.removeAllBreakpoints());
  },

  /**
   * Initializes the UI for all the displayed panes.
   */
  _initializePanes: function () {
    dumpn("Initializing the DebuggerView panes");

    this._body = document.getElementById("body");
    this._editorDeck = document.getElementById("editor-deck");
    this._workersAndSourcesPane = document.getElementById("workers-and-sources-pane");
    this._instrumentsPane = document.getElementById("instruments-pane");
    this._instrumentsPaneToggleButton = document.getElementById("instruments-pane-toggle");

    this.showEditor = this.showEditor.bind(this);
    this.showBlackBoxMessage = this.showBlackBoxMessage.bind(this);
    this.showProgressBar = this.showProgressBar.bind(this);

    this._onTabSelect = this._onInstrumentsPaneTabSelect.bind(this);
    this._instrumentsPane.tabpanels.addEventListener("select", this._onTabSelect);

    this._collapsePaneString = L10N.getStr("collapsePanes");
    this._expandPaneString = L10N.getStr("expandPanes");

    this._workersAndSourcesPane.setAttribute("width", Prefs.workersAndSourcesWidth);
    this._instrumentsPane.setAttribute("width", Prefs.instrumentsWidth);
    this.toggleInstrumentsPane({ visible: Prefs.panesVisibleOnStartup });

    this.updateLayoutMode();

    this._onResize = this._onResize.bind(this);
    window.addEventListener("resize", this._onResize);
  },

  /**
   * Destroys the UI for all the displayed panes.
   */
  _destroyPanes: function () {
    dumpn("Destroying the DebuggerView panes");

    if (gHostType != "side") {
      Prefs.workersAndSourcesWidth = this._workersAndSourcesPane.getAttribute("width");
      Prefs.instrumentsWidth = this._instrumentsPane.getAttribute("width");
    }

    this._workersAndSourcesPane = null;
    this._instrumentsPane = null;
    this._instrumentsPaneToggleButton = null;
  },

  /**
   * Initializes the VariablesView instance and attaches a controller.
   */
  _initializeVariablesView: function () {
    this.Variables = new VariablesView(document.getElementById("variables"), {
      searchPlaceholder: L10N.getStr("emptyVariablesFilterText"),
      emptyText: L10N.getStr("emptyVariablesText"),
      onlyEnumVisible: Prefs.variablesOnlyEnumVisible,
      searchEnabled: Prefs.variablesSearchboxVisible,
      eval: (variable, value) => {
        let string = variable.evaluationMacro(variable, value);
        DebuggerController.StackFrames.evaluate(string);
      },
      lazyEmpty: true
    });

    // Attach the current toolbox to the VView so it can link DOMNodes to
    // the inspector/highlighter
    this.Variables.toolbox = DebuggerController._toolbox;

    // Attach a controller that handles interfacing with the debugger protocol.
    VariablesViewController.attach(this.Variables, {
      getEnvironmentClient: aObject => gThreadClient.environment(aObject),
      getObjectClient: aObject => {
        return gThreadClient.pauseGrip(aObject);
      }
    });

    // Relay events from the VariablesView.
    this.Variables.on("fetched", aType => {
      switch (aType) {
        case "scopes":
          window.emit(EVENTS.FETCHED_SCOPES);
          break;
        case "variables":
          window.emit(EVENTS.FETCHED_VARIABLES);
          break;
        case "properties":
          window.emit(EVENTS.FETCHED_PROPERTIES);
          break;
      }
    });
  },

  /**
   * Initializes the Editor instance.
   *
   * @param function aCallback
   *        Called after the editor finishes initializing.
   */
  _initializeEditor: function (callback) {
    dumpn("Initializing the DebuggerView editor");

    let extraKeys = {};
    bindKey("_doTokenSearch", "tokenSearchKey");
    bindKey("_doGlobalSearch", "globalSearchKey", { alt: true });
    bindKey("_doFunctionSearch", "functionSearchKey");
    extraKeys[Editor.keyFor("jumpToLine")] = false;
    extraKeys["Esc"] = false;

    function bindKey(func, key, modifiers = {}) {
      key = document.getElementById(key).getAttribute("key");
      let shortcut = Editor.accel(key, modifiers);
      extraKeys[shortcut] = () => DebuggerView.Filtering[func]();
    }

    let gutters = ["breakpoints"];

    this.editor = new Editor({
      mode: Editor.modes.text,
      readOnly: true,
      lineNumbers: true,
      showAnnotationRuler: true,
      gutters: gutters,
      extraKeys: extraKeys,
      contextMenu: "sourceEditorContextMenu",
      enableCodeFolding: false
    });

    this.editor.appendTo(document.getElementById("editor")).then(() => {
      this.editor.extend(DebuggerEditor);
      this._loadingText = L10N.getStr("loadingText");
      callback();
    });

    this.editor.on("gutterClick", (ev, line, button) => {
      // A right-click shouldn't do anything but keep track of where
      // it was clicked.
      if (button == 2) {
        this.clickedLine = line;
      }
      else {
        const source = queries.getSelectedSource(this.controller.getState());
        if (source) {
          const location = { actor: source.actor, line: this.toSourceLine(line) };
          if (this.editor.hasBreakpoint(line)) {
            this.controller.dispatch(actions.removeBreakpoint(location));
          } else {
            this.controller.dispatch(actions.addBreakpoint(location));
          }
        }
      }
    });

    this.editor.on("cursorActivity", () => {
      this.clickedLine = null;
    });
  },

  toEditorLine: function (line) {
    return this.editor.isWasm ? line : line - 1;
  },

  toSourceLine: function (line) {
    return this.editor.isWasm ? line : line + 1;
  },

  updateEditorBreakpoints: function (source) {
    const breakpoints = queries.getBreakpoints(this.controller.getState());
    const sources = queries.getSources(this.controller.getState());

    for (let bp of breakpoints) {
      if (sources[bp.location.actor] && !bp.disabled) {
        this.addEditorBreakpoint(bp);
      }
      else {
        this.removeEditorBreakpoint(bp);
      }
    }
  },

  addEditorBreakpoint: function (breakpoint) {
    const { location, condition } = breakpoint;
    const source = queries.getSelectedSource(this.controller.getState());

    if (source &&
       source.actor === location.actor &&
       !breakpoint.disabled) {
      this.editor.addBreakpoint(this.toEditorLine(location.line), condition);
    }
  },

  removeEditorBreakpoint: function (breakpoint) {
    const { location } = breakpoint;
    const source = queries.getSelectedSource(this.controller.getState());

    if (source && source.actor === location.actor) {
      let line = this.toEditorLine(location.line);
      this.editor.removeBreakpoint(line);
      this.editor.removeBreakpointCondition(line);
    }
  },

  renderEditorBreakpointCondition: function (breakpoint) {
    const { location, condition, disabled } = breakpoint;
    const source = queries.getSelectedSource(this.controller.getState());

    if (source && source.actor === location.actor && !disabled) {
      let line = this.toEditorLine(location.line);
      if (condition) {
        this.editor.setBreakpointCondition(line);
      } else {
        this.editor.removeBreakpointCondition(line);
      }
    }
  },

  /**
   * Display the source editor.
   */
  showEditor: function () {
    this._editorDeck.selectedIndex = 0;
  },

  /**
   * Display the black box message.
   */
  showBlackBoxMessage: function () {
    this._editorDeck.selectedIndex = 1;
  },

  /**
   * Display the progress bar.
   */
  showProgressBar: function () {
    this._editorDeck.selectedIndex = 2;
  },

  /**
   * Sets the currently displayed text contents in the source editor.
   * This resets the mode and undo stack.
   *
   * @param string documentKey
   *        Key to get the correct editor document
   *
   * @param string aTextContent
   *        The source text content.
   *
   * @param boolean shouldUpdateText
            Forces a text and mode reset
   */
  _setEditorText: function (documentKey, aTextContent = "", shouldUpdateText = false) {
    const isNew = this._setEditorDocument(documentKey);

    this.editor.clearDebugLocation();
    this.editor.clearHistory();
    this.editor.removeBreakpoints();

    // Only set editor's text and mode if it is a new document
    if (isNew || shouldUpdateText) {
      this.editor.setMode(Editor.modes.text);
      this.editor.setText(aTextContent);
    }
  },

  /**
   * Sets the proper editor mode (JS or HTML) according to the specified
   * content type, or by determining the type from the url or text content.
   *
   * @param string aUrl
   *        The source url.
   * @param string aContentType [optional]
   *        The source content type.
   * @param string aTextContent [optional]
   *        The source text content.
   */
  _setEditorMode: function (aUrl, aContentType = "", aTextContent = "") {
    // Use JS mode for files with .js and .jsm extensions.
    if (SourceUtils.isJavaScript(aUrl, aContentType)) {
      return void this.editor.setMode(Editor.modes.js);
    }

    if (aContentType === "text/wasm") {
      return void this.editor.setMode(Editor.modes.text);
    }

    // Use HTML mode for files in which the first non whitespace character is
    // &lt;, regardless of extension.
    if (typeof aTextContent === 'string' && aTextContent.match(/^\s*</)) {
      return void this.editor.setMode(Editor.modes.html);
    }

    // Unknown language, use text.
    this.editor.setMode(Editor.modes.text);
  },

  /**
   * Sets the editor's displayed document.
   * If there isn't a document for the source, create one
   *
   * @param string key - key used to access the editor document cache
   *
   * @return boolean isNew - was the document just created
   */
  _setEditorDocument: function (key) {
    let isNew;

    if (!this._editorDocuments[key]) {
      isNew = true;
      this._editorDocuments[key] = this.editor.createDocument();
    } else {
      isNew = false;
    }

    const doc = this._editorDocuments[key];
    this.editor.replaceDocument(doc);
    return isNew;
  },

  renderBlackBoxed: function (source) {
    this._renderSourceText(
      source,
      queries.getSourceText(this.controller.getState(), source.actor)
    );
  },

  renderPrettyPrinted: function (source) {
    this._renderSourceText(
      source,
      queries.getSourceText(this.controller.getState(), source.actor)
    );
  },

  renderSourceText: function (source) {
    this._renderSourceText(
      source,
      queries.getSourceText(this.controller.getState(), source.actor),
      queries.getSelectedSourceOpts(this.controller.getState())
    );
  },

  _renderSourceText: function (source, textInfo, opts = {}) {
    const selectedSource = queries.getSelectedSource(this.controller.getState());

    // Exit early if we're attempting to render an unselected source
    if (!selectedSource || selectedSource.actor !== source.actor) {
      return;
    }

    if (source.isBlackBoxed) {
      this.showBlackBoxMessage();
      setTimeout(() => {
        window.emit(EVENTS.SOURCE_SHOWN, source);
      }, 0);
      return;
    }
    else {
      this.showEditor();
    }

    if (textInfo.loading) {
      // TODO: bug 1228866, we need to update `_editorSource` here but
      // still make the editor be updated when the full text comes
      // through somehow.
      this._setEditorText("loading", L10N.getStr("loadingText"));
      return;
    }
    else if (textInfo.error) {
      let msg = L10N.getFormatStr("errorLoadingText3", textInfo.error);
      this._setEditorText("error", msg);
      console.error(new Error(msg));
      dumpn(msg);

      this.showEditor();
      window.emit(EVENTS.SOURCE_ERROR_SHOWN, source);
      return;
    }

    // If the line is not specified, default to the current frame's position,
    // if available and the frame's url corresponds to the requested url.
    if (!("line" in opts)) {
      let cachedFrames = DebuggerController.activeThread.cachedFrames;
      let currentDepth = DebuggerController.StackFrames.currentFrameDepth;
      let frame = cachedFrames[currentDepth];
      if (frame && frame.source.actor == source.actor) {
        opts.line = frame.where.line;
      }
    }

    if (this._editorSource.actor === source.actor &&
        this._editorSource.prettyPrinted === source.isPrettyPrinted &&
        this._editorSource.blackboxed === source.isBlackBoxed) {
      this.updateEditorPosition(opts);
      return;
    }

    let { text, contentType } = textInfo;
    let shouldUpdateText = this._editorSource.prettyPrinted != source.isPrettyPrinted;
    this._setEditorText(source.actor, text, shouldUpdateText);

    this._editorSource.actor = source.actor;
    this._editorSource.prettyPrinted = source.isPrettyPrinted;
    this._editorSource.blackboxed = source.isBlackBoxed;
    this._editorSource.prettyPrinted = source.isPrettyPrinted;

    this._setEditorMode(source.url, contentType, text);
    this.updateEditorBreakpoints(source);

    setTimeout(() => {
      window.emit(EVENTS.SOURCE_SHOWN, source);
    }, 0);

    this.updateEditorPosition(opts);
  },

  updateEditorPosition: function (opts) {
    let line = opts.line || 0;
    if (this.editor.isWasm && line > 0) {
      line = this.toSourceLine(this.editor.wasmOffsetToLine(line));
    }

    // Line numbers in the source editor should start from 1. If
    // invalid or not specified, then don't do anything.
    if (line < 1) {
      window.emit(EVENTS.EDITOR_LOCATION_SET);
      return;
    }

    if (opts.charOffset) {
      line += this.editor.getPosition(opts.charOffset).line;
    }
    if (opts.lineOffset) {
      line += opts.lineOffset;
    }
    line = this.toEditorLine(line);
    if (opts.moveCursor) {
      let location = { line: line, ch: opts.columnOffset || 0 };
      this.editor.setCursor(location);
    }
    if (!opts.noDebug) {
      this.editor.setDebugLocation(line);
    }
    window.emit(EVENTS.EDITOR_LOCATION_SET);
  },

  /**
   * Update the source editor's current caret and debug location based on
   * a requested url and line.
   *
   * @param string aActor
   *        The target actor id.
   * @param number aLine [optional]
   *        The target line in the source.
   * @param object aFlags [optional]
   *        Additional options for showing the source. Supported options:
   *          - charOffset: character offset for the caret or debug location
   *          - lineOffset: line offset for the caret or debug location
   *          - columnOffset: column offset for the caret or debug location
   *          - noCaret: don't set the caret location at the specified line
   *          - noDebug: don't set the debug location at the specified line
   *          - align: string specifying whether to align the specified line
   *                   at the "top", "center" or "bottom" of the editor
   *          - force: boolean forcing all text to be reshown in the editor
   * @return object
   *         A promise that is resolved after the source text has been set.
   */
  setEditorLocation: function (aActor, aLine, aFlags = {}) {
    // Avoid trying to set a source for a url that isn't known yet.
    if (!this.Sources.containsValue(aActor)) {
      throw new Error("Unknown source for the specified URL.");
    }

    let sourceItem = this.Sources.getItemByValue(aActor);
    let source = sourceItem.attachment.source;

    // Make sure the requested source client is shown in the editor,
    // then update the source editor's caret position and debug
    // location.
    this.controller.dispatch(actions.selectSource(source, {
      line: aLine,
      charOffset: aFlags.charOffset,
      lineOffset: aFlags.lineOffset,
      columnOffset: aFlags.columnOffset,
      moveCursor: !aFlags.noCaret,
      noDebug: aFlags.noDebug,
      forceUpdate: aFlags.force
    }));
  },

  /**
   * Gets the visibility state of the instruments pane.
   * @return boolean
   */
  get instrumentsPaneHidden() {
    return this._instrumentsPane.classList.contains("pane-collapsed");
  },

  /**
   * Gets the currently selected tab in the instruments pane.
   * @return string
   */
  get instrumentsPaneTab() {
    return this._instrumentsPane.selectedTab.id;
  },

  /**
   * Sets the instruments pane hidden or visible.
   *
   * @param object aFlags
   *        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 number aTabIndex [optional]
   *        The index of the intended selected tab in the details pane.
   */
  toggleInstrumentsPane: function (aFlags, aTabIndex) {
    let pane = this._instrumentsPane;
    let button = this._instrumentsPaneToggleButton;

    ViewHelpers.togglePane(aFlags, pane);

    if (aFlags.visible) {
      button.classList.remove("pane-collapsed");
      button.setAttribute("tooltiptext", this._collapsePaneString);
    } else {
      button.classList.add("pane-collapsed");
      button.setAttribute("tooltiptext", this._expandPaneString);
    }

    if (aTabIndex !== undefined) {
      pane.selectedIndex = aTabIndex;
    }
  },

  /**
   * Sets the instruments pane visible after a short period of time.
   *
   * @param function aCallback
   *        A function to invoke when the toggle finishes.
   */
  showInstrumentsPane: function (aCallback) {
    DebuggerView.toggleInstrumentsPane({
      visible: true,
      animated: true,
      delayed: true,
      callback: aCallback
    }, 0);
  },

  /**
   * Handles a tab selection event on the instruments pane.
   */
  _onInstrumentsPaneTabSelect: function () {
    if (this._instrumentsPane.selectedTab.id == "events-tab") {
      this.controller.dispatch(actions.fetchEventListeners());
    }
  },

  /**
   * Handles a host change event issued by the parent toolbox.
   *
   * @param string aType
   *        The host type, either "bottom", "side" or "window".
   */
  handleHostChanged: function (hostType) {
    this._hostType = hostType;
    this.updateLayoutMode();
  },

  /**
   * Resize handler for this container's window.
   */
  _onResize: function (evt) {
    // Allow requests to settle down first.
    setNamedTimeout(
      "resize-events", RESIZE_REFRESH_RATE, () => this.updateLayoutMode());
  },

  /**
   * Set the layout to "vertical" or "horizontal" depending on the host type.
   */
  updateLayoutMode: function () {
    if (this._isSmallWindowHost() || this._hostType == "side") {
      this._setLayoutMode("vertical");
    } else {
      this._setLayoutMode("horizontal");
    }
  },

  /**
   * Check if the current host is in window mode and is
   * too small for horizontal layout
   */
  _isSmallWindowHost: function () {
    if (this._hostType != "window") {
      return false;
    }

    return window.outerWidth <= BREAKPOINT_SMALL_WINDOW_WIDTH;
  },

  /**
   * Enter the provided layoutMode. Do nothing if the layout is the same as the current one.
   * @param {String} layoutMode new layout ("vertical" or "horizontal")
   */
  _setLayoutMode: function (layoutMode) {
    if (this._body.getAttribute("layout") == layoutMode) {
      return;
    }

    if (layoutMode == "vertical") {
      this._enterVerticalLayout();
    } else {
      this._enterHorizontalLayout();
    }

    this._body.setAttribute("layout", layoutMode);
    window.emit(EVENTS.LAYOUT_CHANGED, layoutMode);
  },

  /**
   * Switches the debugger widgets to a vertical layout.
   */
  _enterVerticalLayout: function () {
    let vertContainer = document.getElementById("vertical-layout-panes-container");

    // Move the soruces and instruments panes in a different container.
    let splitter = document.getElementById("sources-and-instruments-splitter");
    vertContainer.insertBefore(this._workersAndSourcesPane, splitter);
    vertContainer.appendChild(this._instrumentsPane);

    // Make sure the vertical layout container's height doesn't repeatedly
    // grow or shrink based on the displayed sources, variables etc.
    vertContainer.setAttribute("height",
      vertContainer.getBoundingClientRect().height);
  },

  /**
   * Switches the debugger widgets to a horizontal layout.
   */
  _enterHorizontalLayout: function () {
    let normContainer = document.getElementById("debugger-widgets");
    let editorPane = document.getElementById("editor-and-instruments-pane");

    // The sources and instruments pane need to be inserted at their
    // previous locations in their normal container.
    let splitter = document.getElementById("sources-and-editor-splitter");
    normContainer.insertBefore(this._workersAndSourcesPane, splitter);
    editorPane.appendChild(this._instrumentsPane);

    // Revert to the preferred sources and instruments widths, because
    // they flexed in the vertical layout.
    this._workersAndSourcesPane.setAttribute("width", Prefs.workersAndSourcesWidth);
    this._instrumentsPane.setAttribute("width", Prefs.instrumentsWidth);
  },

  /**
   * Handles any initialization on a tab navigation event issued by the client.
   */
  handleTabNavigation: function () {
    dumpn("Handling tab navigation in the DebuggerView");
    this.Filtering.clearSearch();
    this.GlobalSearch.clearView();
    this.StackFrames.empty();
    this.Sources.empty();
    this.Variables.empty();
    this.EventListeners.empty();

    if (this.editor) {
      this.editor.setMode(Editor.modes.text);
      this.editor.setText("");
      this.editor.clearHistory();
      this._editorSource = {};
      this._editorDocuments = {};
    }
  },

  Toolbar: null,
  Options: null,
  Filtering: null,
  GlobalSearch: null,
  StackFrames: null,
  Sources: null,
  Variables: null,
  VariableBubble: null,
  WatchExpressions: null,
  EventListeners: null,
  editor: null,
  _loadingText: "",
  _body: null,
  _editorDeck: null,
  _workersAndSourcesPane: null,
  _instrumentsPane: null,
  _instrumentsPaneToggleButton: null,
  _collapsePaneString: "",
  _expandPaneString: ""
};

/**
 * A custom items container, used for displaying views like the
 * FilteredSources, FilteredFunctions etc., inheriting the generic WidgetMethods.
 */
function ResultsPanelContainer() {
}

ResultsPanelContainer.prototype = extend(WidgetMethods, {
  /**
   * Sets the anchor node for this container panel.
   * @param nsIDOMNode aNode
   */
  set anchor(aNode) {
    this._anchor = aNode;

    // If the anchor node is not null, create a panel to attach to the anchor
    // when showing the popup.
    if (aNode) {
      if (!this._panel) {
        this._panel = document.createElement("panel");
        this._panel.id = "results-panel";
        this._panel.setAttribute("level", "top");
        this._panel.setAttribute("noautofocus", "true");
        this._panel.setAttribute("consumeoutsideclicks", "false");
        document.documentElement.appendChild(this._panel);
      }
      if (!this.widget) {
        this.widget = new SimpleListWidget(this._panel);
        this.autoFocusOnFirstItem = false;
        this.autoFocusOnSelection = false;
        this.maintainSelectionVisible = false;
      }
    }
    // Cleanup the anchor and remove the previously created panel.
    else {
      this._panel.remove();
      this._panel = null;
      this.widget = null;
    }
  },

  /**
   * Gets the anchor node for this container panel.
   * @return nsIDOMNode
   */
  get anchor() {
    return this._anchor;
  },

  /**
   * Sets the container panel hidden or visible. It's hidden by default.
   * @param boolean aFlag
   */
  set hidden(aFlag) {
    if (aFlag) {
      this._panel.hidden = true;
      this._panel.hidePopup();
    } else {
      this._panel.hidden = false;
      this._panel.openPopup(this._anchor, this.position, this.left, this.top);
    }
  },

  /**
   * Gets this container's visibility state.
   * @return boolean
   */
  get hidden() {
    return this._panel.state == "closed" ||
           this._panel.state == "hiding";
  },

  /**
   * Removes all items from this container and hides it.
   */
  clearView: function () {
    this.hidden = true;
    this.empty();
  },

  /**
   * Selects the next found item in this container.
   * Does not change the currently focused node.
   */
  selectNext: function () {
    let nextIndex = this.selectedIndex + 1;
    if (nextIndex >= this.itemCount) {
      nextIndex = 0;
    }
    this.selectedItem = this.getItemAtIndex(nextIndex);
  },

  /**
   * Selects the previously found item in this container.
   * Does not change the currently focused node.
   */
  selectPrev: function () {
    let prevIndex = this.selectedIndex - 1;
    if (prevIndex < 0) {
      prevIndex = this.itemCount - 1;
    }
    this.selectedItem = this.getItemAtIndex(prevIndex);
  },

  /**
   * Customization function for creating an item's UI.
   *
   * @param string aLabel
   *        The item's label string.
   * @param string aBeforeLabel
   *        An optional string shown before the label.
   * @param string aBelowLabel
   *        An optional string shown underneath the label.
   */
  _createItemView: function (aLabel, aBelowLabel, aBeforeLabel) {
    let container = document.createElement("vbox");
    container.className = "results-panel-item";

    let firstRowLabels = document.createElement("hbox");
    let secondRowLabels = document.createElement("hbox");

    if (aBeforeLabel) {
      let beforeLabelNode = document.createElement("label");
      beforeLabelNode.className = "plain results-panel-item-label-before";
      beforeLabelNode.setAttribute("value", aBeforeLabel);
      firstRowLabels.appendChild(beforeLabelNode);
    }

    let labelNode = document.createElement("label");
    labelNode.className = "plain results-panel-item-label";
    labelNode.setAttribute("value", aLabel);
    firstRowLabels.appendChild(labelNode);

    if (aBelowLabel) {
      let belowLabelNode = document.createElement("label");
      belowLabelNode.className = "plain results-panel-item-label-below";
      belowLabelNode.setAttribute("value", aBelowLabel);
      secondRowLabels.appendChild(belowLabelNode);
    }

    container.appendChild(firstRowLabels);
    container.appendChild(secondRowLabels);

    return container;
  },

  _anchor: null,
  _panel: null,
  position: RESULTS_PANEL_POPUP_POSITION,
  left: 0,
  top: 0
});

DebuggerView.EventListeners = new EventListenersView(DebuggerController);
DebuggerView.Sources = new SourcesView(DebuggerController, DebuggerView);
PK
!<�[0���-chrome/devtools/content/debugger/debugger.css/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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/. */

/* Side pane views */

#workers-pane > tabpanels > tabpanel,
#sources-pane > tabpanels > tabpanel,
#instruments-pane > tabpanels > tabpanel {
  -moz-box-orient: vertical;
}

/* Toolbar controls */

.devtools-toolbarbutton:not([label]) > .toolbarbutton-text {
  display: none;
}

/* Horizontal vs. vertical layout */

#body[layout=vertical] #debugger-widgets {
  -moz-box-orient: vertical;
}

#body[layout=vertical] #workers-and-sources-pane {
  -moz-box-flex: 1;
}

#body[layout=vertical] #instruments-pane {
  -moz-box-flex: 2;
}

#body[layout=vertical] #instruments-pane-toggle {
  display: none;
}

#body[layout=vertical] #sources-and-editor-splitter,
#body[layout=vertical] #editor-and-instruments-splitter {
  display: none;
}

#body[layout=horizontal] #vertical-layout-splitter,
#body[layout=horizontal] #vertical-layout-panes-container {
  display: none;
}

#body[layout=vertical] #stackframes {
  visibility: hidden;
}

#source-progress-container {
  display: flex;
  flex-flow: column;
  justify-content: center;
}

#source-progress {
  flex: none;
}

#redux-devtools * {
  display: block;
}

#redux-devtools span {
  display: inline
}
PK
!<k�I�S�S-chrome/devtools/content/debugger/debugger.xul<?xml version="1.0" encoding="utf-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/. -->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/content/shared/widgets/widgets.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/content/debugger/debugger.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/widgets.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/debugger.css" type="text/css"?>
<!DOCTYPE window [
  <!ENTITY % debuggerDTD SYSTEM "chrome://devtools/locale/debugger.dtd">
  %debuggerDTD;
  <!ENTITY % editMenuDTD SYSTEM "chrome://global/locale/editMenuOverlay.dtd">
  %editMenuDTD;
]>

<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        xmlns:html="http://www.w3.org/1999/xhtml"
        macanimationtype="document"
        fullscreenbutton="true"
        screenX="4" screenY="4"
        width="960" height="480"
        persist="screenX screenY width height sizemode">

  <script type="application/javascript"
          src="chrome://devtools/content/shared/theme-switching.js"/>
  <script type="text/javascript" src="chrome://global/content/globalOverlay.js"/>
  <script type="text/javascript" src="debugger-controller.js"/>
  <script type="text/javascript" src="debugger-view.js"/>
  <script type="text/javascript" src="utils.js"/>
  <script type="text/javascript" src="views/workers-view.js"/>
  <script type="text/javascript" src="views/variable-bubble-view.js"/>
  <script type="text/javascript" src="views/watch-expressions-view.js"/>
  <script type="text/javascript" src="views/global-search-view.js"/>
  <script type="text/javascript" src="views/toolbar-view.js"/>
  <script type="text/javascript" src="views/options-view.js"/>
  <script type="text/javascript" src="views/stack-frames-view.js"/>
  <script type="text/javascript" src="views/stack-frames-classic-view.js"/>
  <script type="text/javascript" src="views/filter-view.js"/>

<script type="application/javascript" src="chrome://global/content/editMenuOverlay.js"/>
<commandset id="editMenuCommands">
  <commandset id="editMenuCommandSetAll" commandupdater="true" events="focus,select"
              oncommandupdate="goUpdateGlobalEditMenuItems()"/>
  <commandset id="editMenuCommandSetUndo" commandupdater="true" events="undo"
              oncommandupdate="goUpdateUndoEditMenuItems()"/>
  <commandset id="editMenuCommandSetPaste" commandupdater="true" events="clipboard"
              oncommandupdate="goUpdatePasteMenuItems()"/>
  <command id="cmd_undo" oncommand="goDoCommand('cmd_undo')"/>
  <command id="cmd_redo" oncommand="goDoCommand('cmd_redo')"/>
  <command id="cmd_cut" oncommand="goDoCommand('cmd_cut')"/>
  <command id="cmd_copy" oncommand="goDoCommand('cmd_copy')"/>
  <command id="cmd_paste" oncommand="goDoCommand('cmd_paste')"/>
  <command id="cmd_delete" oncommand="goDoCommand('cmd_delete')"/>
  <command id="cmd_selectAll" oncommand="goDoCommand('cmd_selectAll')"/>
  <command id="cmd_switchTextDirection" oncommand="goDoCommand('cmd_switchTextDirection');"/>
</commandset>

  <commandset id="debuggerCommands"></commandset>

  <popupset id="debuggerPopupset">
    <menupopup id="sourceEditorContextMenu"
               onpopupshowing="goUpdateGlobalEditMenuItems()">
      <menuitem id="se-dbg-cMenu-addBreakpoint"
                label="&debuggerUI.seMenuBreak;"
                key="addBreakpointKey"
                command="addBreakpointCommand"/>
      <menuitem id="se-dbg-cMenu-addConditionalBreakpoint"
                label="&debuggerUI.seMenuCondBreak;"
                key="addConditionalBreakpointKey"
                command="addConditionalBreakpointCommand"/>
      <menuitem id="se-dbg-cMenu-editConditionalBreakpoint"
                label="&debuggerUI.seEditMenuCondBreak;"
                key="addConditionalBreakpointKey"
                command="addConditionalBreakpointCommand"/>
      <menuitem id="se-dbg-cMenu-addAsWatch"
                label="&debuggerUI.seMenuAddWatch;"
                key="addWatchExpressionKey"
                command="addWatchExpressionCommand"/>
      <menuseparator/>
      <menuitem id="cMenu_copy" label="&copyCmd.label;"
                accesskey="&copyCmd.accesskey;" command="cmd_copy"/>
      <menuseparator/>
      <menuitem id="cMenu_selectAll" label="&selectAllCmd.label;"
                accesskey="&selectAllCmd.accesskey;" command="cmd_selectAll"/>
      <menuseparator/>
      <menuitem id="se-dbg-cMenu-findFile"
                label="&debuggerUI.searchFile;"
                accesskey="&debuggerUI.searchFile.accesskey;"
                key="fileSearchKey"
                command="fileSearchCommand"/>
      <menuitem id="se-dbg-cMenu-findGlobal"
                label="&debuggerUI.searchGlobal;"
                accesskey="&debuggerUI.searchGlobal.accesskey;"
                key="globalSearchKey"
                command="globalSearchCommand"/>
      <menuitem id="se-dbg-cMenu-findFunction"
                label="&debuggerUI.searchFunction;"
                accesskey="&debuggerUI.searchFunction.accesskey;"
                key="functionSearchKey"
                command="functionSearchCommand"/>
      <menuseparator/>
      <menuitem id="se-dbg-cMenu-findToken"
                label="&debuggerUI.searchToken;"
                accesskey="&debuggerUI.searchToken.accesskey;"
                key="tokenSearchKey"
                command="tokenSearchCommand"/>
      <menuitem id="se-dbg-cMenu-findLine"
                label="&debuggerUI.searchGoToLine;"
                accesskey="&debuggerUI.searchGoToLine.accesskey;"
                key="lineSearchKey"
                command="lineSearchCommand"/>
      <menuseparator/>
      <menuitem id="se-dbg-cMenu-findVariable"
                label="&debuggerUI.searchVariable;"
                accesskey="&debuggerUI.searchVariable.accesskey;"
                key="variableSearchKey"
                command="variableSearchCommand"/>
      <menuitem id="se-dbg-cMenu-focusVariables"
                label="&debuggerUI.focusVariables;"
                accesskey="&debuggerUI.focusVariables.accesskey;"
                key="variablesFocusKey"
                command="variablesFocusCommand"/>
      <menuitem id="se-dbg-cMenu-prettyPrint"
                label="&debuggerUI.sources.prettyPrint;"
                command="prettyPrintCommand"/>
    </menupopup>
    <menupopup id="debuggerWatchExpressionsContextMenu">
      <menuitem id="add-watch-expression"
                label="&debuggerUI.addWatch;"
                accesskey="&debuggerUI.addWatch.accesskey;"
                key="addWatchExpressionKey"
                command="addWatchExpressionCommand"/>
      <menuitem id="removeAll-watch-expression"
                label="&debuggerUI.removeAllWatch;"
                accesskey="&debuggerUI.removeAllWatch.accesskey;"
                key="removeAllWatchExpressionsKey"
                command="removeAllWatchExpressionsCommand"/>
    </menupopup>
    <menupopup id="debuggerPrefsContextMenu"
               position="before_end"
               onpopupshowing="DebuggerView.Options._onPopupShowing()"
               onpopuphiding="DebuggerView.Options._onPopupHiding()"
               onpopuphidden="DebuggerView.Options._onPopupHidden()">
      <menuitem id="auto-pretty-print"
                type="checkbox"
                label="&debuggerUI.autoPrettyPrint;"
                accesskey="&debuggerUI.autoPrettyPrint.accesskey;"
                command="toggleAutoPrettyPrint"/>
      <menuitem id="pause-on-exceptions"
                type="checkbox"
                label="&debuggerUI.pauseExceptions;"
                accesskey="&debuggerUI.pauseExceptions.accesskey;"
                command="togglePauseOnExceptions"/>
      <menuitem id="ignore-caught-exceptions"
                type="checkbox"
                label="&debuggerUI.ignoreCaughtExceptions;"
                accesskey="&debuggerUI.ignoreCaughtExceptions.accesskey;"
                command="toggleIgnoreCaughtExceptions"/>
      <menuitem id="show-panes-on-startup"
                type="checkbox"
                label="&debuggerUI.showPanesOnInit;"
                accesskey="&debuggerUI.showPanesOnInit.accesskey;"
                command="toggleShowPanesOnStartup"/>
      <menuitem id="show-vars-only-enum"
                type="checkbox"
                label="&debuggerUI.showOnlyEnum;"
                accesskey="&debuggerUI.showOnlyEnum.accesskey;"
                command="toggleShowOnlyEnum"/>
      <menuitem id="show-vars-filter-box"
                type="checkbox"
                label="&debuggerUI.showVarsFilter;"
                accesskey="&debuggerUI.showVarsFilter.accesskey;"
                command="toggleShowVariablesFilterBox"/>
      <menuitem id="show-original-source"
                type="checkbox"
                label="&debuggerUI.showOriginalSource;"
                accesskey="&debuggerUI.showOriginalSource.accesskey;"
                command="toggleShowOriginalSource"/>
      <menuitem id="auto-black-box"
                type="checkbox"
                label="&debuggerUI.autoBlackBox;"
                accesskey="&debuggerUI.autoBlackBox.accesskey;"
                command="toggleAutoBlackBox"/>
    </menupopup>
  </popupset>

  <popupset id="debuggerSourcesPopupset">
    <menupopup id="debuggerSourcesContextMenu">
      <menuitem id="debugger-sources-context-newtab"
                label="&debuggerUI.context.newTab;"
                accesskey="&debuggerUI.context.newTab.accesskey;"/>
      <menuitem id="debugger-sources-context-copyurl"
                label="&debuggerUI.context.copyUrl;"
                accesskey="&debuggerUI.context.copyUrl.accesskey;"/>
    </menupopup>
  </popupset>

  <keyset id="debuggerKeys">
    <key id="nextSourceKey"
         keycode="VK_DOWN"
         modifiers="accel alt"
         command="nextSourceCommand"/>
    <key id="prevSourceKey"
         keycode="VK_UP"
         modifiers="accel alt"
         command="prevSourceCommand"/>
    <key id="resumeKey"
         keycode="&debuggerUI.stepping.resume1;"
         command="resumeCommand"/>
    <key id="stepOverKey"
         keycode="&debuggerUI.stepping.stepOver1;"
         command="stepOverCommand"/>
    <key id="stepInKey"
         keycode="&debuggerUI.stepping.stepIn1;"
         command="stepInCommand"/>
    <key id="stepOutKey"
         keycode="&debuggerUI.stepping.stepOut1;"
         modifiers="shift"
         command="stepOutCommand"/>
    <key id="fileSearchKey"
         key="&debuggerUI.searchFile.key;"
         modifiers="accel"
         command="fileSearchCommand"/>
    <key id="fileSearchKey"
         key="&debuggerUI.searchFile.altkey;"
         modifiers="accel"
         command="fileSearchCommand"/>
    <key id="globalSearchKey"
         key="&debuggerUI.searchGlobal.key;"
         modifiers="accel alt"
         command="globalSearchCommand"/>
    <key id="functionSearchKey"
         key="&debuggerUI.searchFunction.key;"
         modifiers="accel"
         command="functionSearchCommand"/>
    <key id="tokenSearchKey"
         key="&debuggerUI.searchToken.key;"
         modifiers="accel"
         command="tokenSearchCommand"/>
    <key id="lineSearchKey"
         key="&debuggerUI.searchGoToLine.key;"
         modifiers="accel"
         command="lineSearchCommand"/>
    <key id="variableSearchKey"
         key="&debuggerUI.searchVariable.key;"
         modifiers="accel alt"
         command="variableSearchCommand"/>
    <key id="variablesFocusKey"
         key="&debuggerUI.focusVariables.key;"
         modifiers="accel shift"
         command="variablesFocusCommand"/>
    <key id="addBreakpointKey"
         key="&debuggerUI.seMenuBreak.key;"
         modifiers="accel"
         command="addBreakpointCommand"/>
    <key id="addConditionalBreakpointKey"
         key="&debuggerUI.seMenuCondBreak.key;"
         modifiers="accel shift"
         command="addConditionalBreakpointCommand"/>
    <key id="addWatchExpressionKey"
         key="&debuggerUI.seMenuAddWatch.key;"
         modifiers="accel shift"
         command="addWatchExpressionCommand"/>
    <key id="removeAllWatchExpressionsKey"
         key="&debuggerUI.removeAllWatch.key;"
         modifiers="accel alt"
         command="removeAllWatchExpressionsCommand"/>
    <key id="debuggerSourcesCopyUrl"
         key="&debuggerUI.context.copyUrl.key;"
         modifiers="accel"
         oncommand="DebuggerView.Sources._onCopyUrlCommand()"/>
  </keyset>

  <vbox id="body"
        class="theme-body"
        layout="horizontal"
        flex="1">
    <toolbar id="debugger-toolbar"
             class="devtools-toolbar">
      <hbox id="debugger-controls"
            class="devtools-toolbarbutton-group">
        <toolbarbutton id="resume"
                       class="devtools-toolbarbutton"
                       tabindex="0"/>
        <toolbarbutton id="step-over"
                       class="devtools-toolbarbutton"
                       tabindex="0"/>
        <toolbarbutton id="step-in"
                       class="devtools-toolbarbutton"
                       tabindex="0"/>
        <toolbarbutton id="step-out"
                       class="devtools-toolbarbutton"
                       tabindex="0"/>
      </hbox>
      <vbox id="stackframes" flex="1"/>
      <textbox id="searchbox"
               class="devtools-searchinput" type="search"/>
      <toolbarbutton id="instruments-pane-toggle"
                     class="devtools-toolbarbutton"
                     tooltiptext="&debuggerUI.panesButton.tooltip;"
                     tabindex="0"/>
      <toolbarbutton id="debugger-options"
                     class="devtools-toolbarbutton devtools-option-toolbarbutton"
                     tooltiptext="&debuggerUI.optsButton.tooltip;"
                     popup="debuggerPrefsContextMenu"
                     tabindex="0"/>
    </toolbar>
    <vbox id="globalsearch" orient="vertical" hidden="true"/>
    <splitter class="devtools-horizontal-splitter" hidden="true"/>
    <hbox id="debugger-widgets" flex="1">
      <vbox id="workers-and-sources-pane">
        <tabbox id="workers-pane"
                class="devtools-sidebar-tabs"
                flex="0"
                hidden="true">
          <tabs>
            <tab id="workers-tab"
                 crop="end"
                 label="&debuggerUI.tabs.workers;"/>
          </tabs>
          <tabpanels flex="1">
            <tabpanel>
              <vbox id="workers" flex="1"/>
            </tabpanel>
          </tabpanels>
        </tabbox>
        <splitter id="workers-splitter"
                  class="devtools-horizontal-splitter"
                  hidden="true" />
        <tabbox id="sources-pane"
                class="devtools-sidebar-tabs"
                flex="1">
          <tabs>
            <tab id="sources-tab"
                 crop="end"
                 label="&debuggerUI.tabs.sources;"/>
            <tab id="callstack-tab"
                 crop="end"
                 label="&debuggerUI.tabs.callstack;"/>
          </tabs>
          <tabpanels flex="1">
            <tabpanel id="sources-tabpanel">
              <vbox id="sources" flex="1"/>
              <toolbar id="sources-toolbar" class="devtools-toolbar">
                <hbox id="sources-controls"
                      class="devtools-toolbarbutton-group">
                  <toolbarbutton id="black-box"
                                 class="devtools-toolbarbutton"
                                 tooltiptext="&debuggerUI.sources.blackBoxTooltip;"
                                 command="blackBoxCommand"/>
                  <toolbarbutton id="pretty-print"
                                 class="devtools-toolbarbutton"
                                 tooltiptext="&debuggerUI.sources.prettyPrint;"
                                 command="prettyPrintCommand"
                                 hidden="true"/>
                </hbox>
                <vbox class="devtools-separator"/>
                <toolbarbutton id="toggle-breakpoints"
                               class="devtools-toolbarbutton"
                               tooltiptext="&debuggerUI.sources.toggleBreakpoints;"
                               command="toggleBreakpointsCommand"/>
              </toolbar>
            </tabpanel>
            <tabpanel id="callstack-tabpanel">
              <vbox id="callstack-list" flex="1"/>
            </tabpanel>
          </tabpanels>
        </tabbox>
      </vbox>
      <splitter id="sources-and-editor-splitter"
                class="devtools-side-splitter"/>
      <vbox id="debugger-content" flex="1">
        <hbox id="editor-and-instruments-pane" flex="1">
          <deck id="editor-deck" flex="1" class="devtools-main-content">
            <vbox id="editor"/>
            <vbox id="black-boxed-message"
                  align="center"
                  pack="center">
              <description id="black-boxed-message-label">
                &debuggerUI.blackBoxMessage.label;
              </description>
              <button id="black-boxed-message-button"
                      class="devtools-toolbarbutton"
                      label="&debuggerUI.blackBoxMessage.unBlackBoxButton;"
                      command="unBlackBoxCommand"/>
            </vbox>
            <html:div id="source-progress-container"
                      align="center">
              <html:div id="hbox">
                <html:progress id="source-progress"></html:progress>
              </html:div>
            </html:div>
          </deck>
          <splitter id="editor-and-instruments-splitter"
                    class="devtools-side-splitter"/>
          <tabbox id="instruments-pane"
                  class="devtools-sidebar-tabs"
                  hidden="true">
            <tabs>
              <tab id="variables-tab"
                   crop="end"
                   label="&debuggerUI.tabs.variables;"/>
              <tab id="events-tab"
                   crop="end"
                   label="&debuggerUI.tabs.events;"/>
            </tabs>
            <tabpanels flex="1">
              <tabpanel id="variables-tabpanel">
                <vbox id="expressions"/>
                <splitter class="devtools-horizontal-splitter"/>
                <vbox id="variables" flex="1"/>
              </tabpanel>
              <tabpanel id="events-tabpanel">
                <vbox id="event-listeners" flex="1"/>
              </tabpanel>
            </tabpanels>
          </tabbox>
        </hbox>
      </vbox>
      <splitter id="vertical-layout-splitter"
                class="devtools-horizontal-splitter"/>
      <hbox id="vertical-layout-panes-container">
        <splitter id="sources-and-instruments-splitter"
                  class="devtools-side-splitter"/>
        <!-- The sources-pane and instruments-pane will be moved in this
             container if the toolbox's host requires it. -->
      </hbox>
    </hbox>
  </vbox>

  <panel id="searchbox-help-panel"
         level="top"
         type="arrow"
         position="before_start"
         noautofocus="true"
         consumeoutsideclicks="false">
    <vbox>
      <hbox>
        <label id="filter-label"/>
      </hbox>
      <label id="searchbox-panel-operators"
             value="&debuggerUI.searchPanelOperators;"/>
      <hbox align="center">
        <button id="global-operator-button"
                class="searchbox-panel-operator-button devtools-monospace"
                command="globalSearchCommand"/>
        <label id="global-operator-label"
               class="plain searchbox-panel-operator-label"/>
      </hbox>
      <hbox align="center">
        <button id="function-operator-button"
                class="searchbox-panel-operator-button devtools-monospace"
                command="functionSearchCommand"/>
        <label id="function-operator-label"
               class="plain searchbox-panel-operator-label"/>
      </hbox>
      <hbox align="center">
        <button id="token-operator-button"
                class="searchbox-panel-operator-button devtools-monospace"
                command="tokenSearchCommand"/>
        <label id="token-operator-label"
               class="plain searchbox-panel-operator-label"/>
      </hbox>
      <hbox align="center">
        <button id="line-operator-button"
                class="searchbox-panel-operator-button devtools-monospace"
                command="lineSearchCommand"/>
        <label id="line-operator-label"
               class="plain searchbox-panel-operator-label"/>
      </hbox>
      <hbox align="center">
        <button id="variable-operator-button"
                class="searchbox-panel-operator-button devtools-monospace"
                command="variableSearchCommand"/>
        <label id="variable-operator-label"
               class="plain searchbox-panel-operator-label"/>
      </hbox>
    </vbox>
  </panel>

  <panel id="conditional-breakpoint-panel"
         level="top"
         type="arrow"
         noautofocus="true"
         consumeoutsideclicks="false">
    <vbox>
      <label id="conditional-breakpoint-panel-description"
             value="&debuggerUI.condBreakPanelTitle;"/>
      <textbox id="conditional-breakpoint-panel-textbox"/>
    </vbox>
  </panel>
</window>
PK
!<���,,/chrome/devtools/content/debugger/new/index.html<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 dir="">
  <head>
    <link rel="stylesheet"
          type="text/css"
          href="chrome://devtools/content/sourceeditor/codemirror/lib/codemirror.css" />
    <link rel="stylesheet"
          type="text/css"
          href="chrome://devtools/content/sourceeditor/codemirror/addon/dialog/dialog.css" />
    <link rel="stylesheet"
          type="text/css"
          href="chrome://devtools/content/sourceeditor/codemirror/mozilla.css" />
    <link rel="stylesheet" type="text/css" href="resource://devtools/client/debugger/new/debugger.css" />
  </head>
  <body>
    <div id="mount"></div>
    <script type="application/javascript"
            src="chrome://devtools/content/shared/theme-switching.js"></script>
    <script type="text/javascript">
      const { BrowserLoader } = ChromeUtils.import("resource://devtools/client/shared/browser-loader.js", {});
      const { require } = BrowserLoader({
        baseURI: "resource://devtools/client/debugger/new/",
        window,
      });
      Debugger = require("devtools/client/debugger/new/debugger");
    </script>
  </body>
</html>
PK
!<�H�^{,{,)chrome/devtools/content/debugger/utils.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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/. */
/* globals document, window */
/* import-globals-from ./debugger-controller.js */
"use strict";

// Maps known URLs to friendly source group names and put them at the
// bottom of source list.
var KNOWN_SOURCE_GROUPS = {
  "Add-on SDK": "resource://gre/modules/commonjs/",
};

KNOWN_SOURCE_GROUPS[L10N.getStr("anonymousSourcesLabel")] = "anonymous";

var XULUtils = {
  /**
   * Create <command> elements within `commandset` with event handlers
   * bound to the `command` event
   *
   * @param commandset HTML Element
   *        A <commandset> element
   * @param commands Object
   *        An object where keys specify <command> ids and values
   *        specify event handlers to be bound on the `command` event
   */
  addCommands: function (commandset, commands) {
    Object.keys(commands).forEach(name => {
      let node = document.createElement("command");
      node.id = name;
      // XXX bug 371900: the command element must have an oncommand
      // attribute as a string set by `setAttribute` for keys to use it
      node.setAttribute("oncommand", " ");
      node.addEventListener("command", commands[name]);
      commandset.appendChild(node);
    });
  }
};

// Used to detect minification for automatic pretty printing
const SAMPLE_SIZE = 50; // no of lines
const INDENT_COUNT_THRESHOLD = 5; // percentage
const CHARACTER_LIMIT = 250; // line character limit

/**
 * Utility functions for handling sources.
 */
var SourceUtils = {
  _labelsCache: new Map(), // Can't use WeakMaps because keys are strings.
  _groupsCache: new Map(),
  _minifiedCache: new Map(),

  /**
   * Returns true if the specified url and/or content type are specific to
   * javascript files.
   *
   * @return boolean
   *         True if the source is likely javascript.
   */
  isJavaScript: function (aUrl, aContentType = "") {
    return (aUrl && /\.jsm?$/.test(this.trimUrlQuery(aUrl))) ||
           aContentType.includes("javascript");
  },

  /**
   * Determines if the source text is minified by using
   * the percentage indented of a subset of lines
   *
   * @return object
   *         A promise that resolves to true if source text is minified.
   */
  isMinified: function (key, text) {
    if (this._minifiedCache.has(key)) {
      return this._minifiedCache.get(key);
    }

    let isMinified;
    let lineEndIndex = 0;
    let lineStartIndex = 0;
    let lines = 0;
    let indentCount = 0;
    let overCharLimit = false;

    // Strip comments.
    text = text.replace(/\/\*[\S\s]*?\*\/|\/\/(.+|\n)/g, "");

    while (lines++ < SAMPLE_SIZE) {
      lineEndIndex = text.indexOf("\n", lineStartIndex);
      if (lineEndIndex == -1) {
        break;
      }
      if (/^\s+/.test(text.slice(lineStartIndex, lineEndIndex))) {
        indentCount++;
      }
      // For files with no indents but are not minified.
      if ((lineEndIndex - lineStartIndex) > CHARACTER_LIMIT) {
        overCharLimit = true;
        break;
      }
      lineStartIndex = lineEndIndex + 1;
    }

    isMinified =
      ((indentCount / lines) * 100) < INDENT_COUNT_THRESHOLD || overCharLimit;

    this._minifiedCache.set(key, isMinified);
    return isMinified;
  },

  /**
   * Clears the labels, groups and minify cache, populated by methods like
   * SourceUtils.getSourceLabel or Source Utils.getSourceGroup.
   * This should be done every time the content location changes.
   */
  clearCache: function () {
    this._labelsCache.clear();
    this._groupsCache.clear();
    this._minifiedCache.clear();
  },

  /**
   * Gets a unique, simplified label from a source url.
   *
   * @param string aUrl
   *        The source url.
   * @return string
   *         The simplified label.
   */
  getSourceLabel: function (aUrl) {
    let cachedLabel = this._labelsCache.get(aUrl);
    if (cachedLabel) {
      return cachedLabel;
    }

    let sourceLabel = null;

    for (let name of Object.keys(KNOWN_SOURCE_GROUPS)) {
      if (aUrl.startsWith(KNOWN_SOURCE_GROUPS[name])) {
        sourceLabel = aUrl.substring(KNOWN_SOURCE_GROUPS[name].length);
      }
    }

    if (!sourceLabel) {
      sourceLabel = this.trimUrl(aUrl);
    }

    let unicodeLabel = NetworkHelper.convertToUnicode(unescape(sourceLabel));
    this._labelsCache.set(aUrl, unicodeLabel);
    return unicodeLabel;
  },

  /**
   * Gets as much information as possible about the hostname and directory paths
   * of an url to create a short url group identifier.
   *
   * @param string aUrl
   *        The source url.
   * @return string
   *         The simplified group.
   */
  getSourceGroup: function (aUrl) {
    let cachedGroup = this._groupsCache.get(aUrl);
    if (cachedGroup) {
      return cachedGroup;
    }

    try {
      // Use an nsIURL to parse all the url path parts.
      var uri = Services.io.newURI(aUrl).QueryInterface(Ci.nsIURL);
    } catch (e) {
      // This doesn't look like a url, or nsIURL can't handle it.
      return "";
    }

    let groupLabel = uri.prePath;

    for (let name of Object.keys(KNOWN_SOURCE_GROUPS)) {
      if (aUrl.startsWith(KNOWN_SOURCE_GROUPS[name])) {
        groupLabel = name;
      }
    }

    let unicodeLabel = NetworkHelper.convertToUnicode(unescape(groupLabel));
    this._groupsCache.set(aUrl, unicodeLabel);
    return unicodeLabel;
  },

  /**
   * Trims the url by shortening it if it exceeds a certain length, adding an
   * ellipsis at the end.
   *
   * @param string aUrl
   *        The source url.
   * @param number aLength [optional]
   *        The expected source url length.
   * @param number aSection [optional]
   *        The section to trim. Supported values: "start", "center", "end"
   * @return string
   *         The shortened url.
   */
  trimUrlLength: function (aUrl, aLength, aSection) {
    aLength = aLength || SOURCE_URL_DEFAULT_MAX_LENGTH;
    aSection = aSection || "end";

    if (aUrl.length > aLength) {
      switch (aSection) {
        case "start":
          return ELLIPSIS + aUrl.slice(-aLength);
          break;
        case "center":
          return aUrl.substr(0, aLength / 2 - 1) + ELLIPSIS + aUrl.slice(-aLength / 2 + 1);
          break;
        case "end":
          return aUrl.substr(0, aLength) + ELLIPSIS;
          break;
      }
    }
    return aUrl;
  },

  /**
   * Trims the query part or reference identifier of a url string, if necessary.
   *
   * @param string aUrl
   *        The source url.
   * @return string
   *         The shortened url.
   */
  trimUrlQuery: function (aUrl) {
    let length = aUrl.length;
    let q1 = aUrl.indexOf("?");
    let q2 = aUrl.indexOf("&");
    let q3 = aUrl.indexOf("#");
    let q = Math.min(q1 != -1 ? q1 : length,
                     q2 != -1 ? q2 : length,
                     q3 != -1 ? q3 : length);

    return aUrl.slice(0, q);
  },

  /**
   * Trims as much as possible from a url, while keeping the label unique
   * in the sources container.
   *
   * @param string | nsIURL aUrl
   *        The source url.
   * @param string aLabel [optional]
   *        The resulting label at each step.
   * @param number aSeq [optional]
   *        The current iteration step.
   * @return string
   *         The resulting label at the final step.
   */
  trimUrl: function (aUrl, aLabel, aSeq) {
    if (!(aUrl instanceof Ci.nsIURL)) {
      try {
        // Use an nsIURL to parse all the url path parts.
        aUrl = Services.io.newURI(aUrl).QueryInterface(Ci.nsIURL);
      } catch (e) {
        // This doesn't look like a url, or nsIURL can't handle it.
        return aUrl;
      }
    }
    if (!aSeq) {
      let name = aUrl.fileName;
      if (name) {
        // This is a regular file url, get only the file name (contains the
        // base name and extension if available).

        // If this url contains an invalid query, unfortunately nsIURL thinks
        // it's part of the file extension. It must be removed.
        aLabel = aUrl.fileName.replace(/\&.*/, "");
      } else {
        // This is not a file url, hence there is no base name, nor extension.
        // Proceed using other available information.
        aLabel = "";
      }
      aSeq = 1;
    }

    // If we have a label and it doesn't only contain a query...
    if (aLabel && aLabel.indexOf("?") != 0) {
      // A page may contain multiple requests to the same url but with different
      // queries. It is *not* redundant to show each one.
      if (!DebuggerView.Sources.getItemForAttachment(e => e.label == aLabel)) {
        return aLabel;
      }
    }

    // Append the url query.
    if (aSeq == 1) {
      let query = aUrl.query;
      if (query) {
        return this.trimUrl(aUrl, aLabel + "?" + query, aSeq + 1);
      }
      aSeq++;
    }
    // Append the url reference.
    if (aSeq == 2) {
      let ref = aUrl.ref;
      if (ref) {
        return this.trimUrl(aUrl, aLabel + "#" + aUrl.ref, aSeq + 1);
      }
      aSeq++;
    }
    // Prepend the url directory.
    if (aSeq == 3) {
      let dir = aUrl.directory;
      if (dir) {
        return this.trimUrl(aUrl, dir.replace(/^\//, "") + aLabel, aSeq + 1);
      }
      aSeq++;
    }
    // Prepend the hostname and port number.
    if (aSeq == 4) {
      let host;
      try {
        // Bug 1261860: jar: URLs throw when accessing `hostPost`
        host = aUrl.hostPort;
      } catch (e) {}
      if (host) {
        return this.trimUrl(aUrl, host + "/" + aLabel, aSeq + 1);
      }
      aSeq++;
    }
    // Use the whole url spec but ignoring the reference.
    if (aSeq == 5) {
      return this.trimUrl(aUrl, aUrl.specIgnoringRef, aSeq + 1);
    }
    // Give up.
    return aUrl.spec;
  },

  parseSource: function (aDebuggerView, aParser) {
    let editor = aDebuggerView.editor;

    let contents = editor.getText();
    let location = aDebuggerView.Sources.selectedValue;
    let parsedSource = aParser.get(contents, location);

    return parsedSource;
  },

  findIdentifier: function (aEditor, parsedSource, x, y) {
    let editor = aEditor;

    // Calculate the editor's line and column at the current x and y coords.
    let hoveredPos = editor.getPositionFromCoords({ left: x, top: y });
    let hoveredOffset = editor.getOffset(hoveredPos);
    let hoveredLine = hoveredPos.line;
    let hoveredColumn = hoveredPos.ch;

    let scriptInfo = parsedSource.getScriptInfo(hoveredOffset);

    // If the script length is negative, we're not hovering JS source code.
    if (scriptInfo.length == -1) {
      return;
    }

    // Using the script offset, determine the actual line and column inside the
    // script, to use when finding identifiers.
    let scriptStart = editor.getPosition(scriptInfo.start);
    let scriptLineOffset = scriptStart.line;
    let scriptColumnOffset = (hoveredLine == scriptStart.line ? scriptStart.ch : 0);

    let scriptLine = hoveredLine - scriptLineOffset;
    let scriptColumn = hoveredColumn - scriptColumnOffset;
    let identifierInfo = parsedSource.getIdentifierAt({
      line: scriptLine + 1,
      column: scriptColumn,
      scriptIndex: scriptInfo.index
    });

    return identifierInfo;
  }
};
PK
!<�7�w�z�z5chrome/devtools/content/debugger/views/filter-view.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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/. */
/* import-globals-from ../debugger-controller.js */
/* import-globals-from ../debugger-view.js */
/* import-globals-from ../utils.js */
/* globals document, window */
"use strict";

/**
 * Functions handling the filtering UI.
 */
function FilterView(DebuggerController, DebuggerView) {
  dumpn("FilterView was instantiated");

  this.Parser = DebuggerController.Parser;

  this.DebuggerView = DebuggerView;
  this.FilteredSources = new FilteredSourcesView(DebuggerView);
  this.FilteredFunctions = new FilteredFunctionsView(DebuggerController.SourceScripts,
                                                     DebuggerController.Parser,
                                                     DebuggerView);

  this._onClick = this._onClick.bind(this);
  this._onInput = this._onInput.bind(this);
  this._onKeyPress = this._onKeyPress.bind(this);
  this._onBlur = this._onBlur.bind(this);
}

FilterView.prototype = {
  /**
   * Initialization function, called when the debugger is started.
   */
  initialize: function () {
    dumpn("Initializing the FilterView");

    this._searchbox = document.getElementById("searchbox");
    this._searchboxHelpPanel = document.getElementById("searchbox-help-panel");
    this._filterLabel = document.getElementById("filter-label");
    this._globalOperatorButton = document.getElementById("global-operator-button");
    this._globalOperatorLabel = document.getElementById("global-operator-label");
    this._functionOperatorButton = document.getElementById("function-operator-button");
    this._functionOperatorLabel = document.getElementById("function-operator-label");
    this._tokenOperatorButton = document.getElementById("token-operator-button");
    this._tokenOperatorLabel = document.getElementById("token-operator-label");
    this._lineOperatorButton = document.getElementById("line-operator-button");
    this._lineOperatorLabel = document.getElementById("line-operator-label");
    this._variableOperatorButton = document.getElementById("variable-operator-button");
    this._variableOperatorLabel = document.getElementById("variable-operator-label");

    this._fileSearchKey = ShortcutUtils.prettifyShortcut(document.getElementById("fileSearchKey"));
    this._globalSearchKey = ShortcutUtils.prettifyShortcut(document.getElementById("globalSearchKey"));
    this._filteredFunctionsKey = ShortcutUtils.prettifyShortcut(document.getElementById("functionSearchKey"));
    this._tokenSearchKey = ShortcutUtils.prettifyShortcut(document.getElementById("tokenSearchKey"));
    this._lineSearchKey = ShortcutUtils.prettifyShortcut(document.getElementById("lineSearchKey"));
    this._variableSearchKey = ShortcutUtils.prettifyShortcut(document.getElementById("variableSearchKey"));

    this._searchbox.addEventListener("click", this._onClick);
    this._searchbox.addEventListener("select", this._onInput);
    this._searchbox.addEventListener("input", this._onInput);
    this._searchbox.addEventListener("keypress", this._onKeyPress);
    this._searchbox.addEventListener("blur", this._onBlur);

    let placeholder = L10N.getFormatStr("emptySearchText", this._fileSearchKey);
    this._searchbox.setAttribute("placeholder", placeholder);

    this._globalOperatorButton.setAttribute("label", SEARCH_GLOBAL_FLAG);
    this._functionOperatorButton.setAttribute("label", SEARCH_FUNCTION_FLAG);
    this._tokenOperatorButton.setAttribute("label", SEARCH_TOKEN_FLAG);
    this._lineOperatorButton.setAttribute("label", SEARCH_LINE_FLAG);
    this._variableOperatorButton.setAttribute("label", SEARCH_VARIABLE_FLAG);

    this._filterLabel.setAttribute("value",
      L10N.getFormatStr("searchPanelFilter", this._fileSearchKey));
    this._globalOperatorLabel.setAttribute("value",
      L10N.getFormatStr("searchPanelGlobal", this._globalSearchKey));
    this._functionOperatorLabel.setAttribute("value",
      L10N.getFormatStr("searchPanelFunction", this._filteredFunctionsKey));
    this._tokenOperatorLabel.setAttribute("value",
      L10N.getFormatStr("searchPanelToken", this._tokenSearchKey));
    this._lineOperatorLabel.setAttribute("value",
      L10N.getFormatStr("searchPanelGoToLine", this._lineSearchKey));
    this._variableOperatorLabel.setAttribute("value",
      L10N.getFormatStr("searchPanelVariable", this._variableSearchKey));

    this.FilteredSources.initialize();
    this.FilteredFunctions.initialize();

    this._addCommands();
  },

  /**
   * Destruction function, called when the debugger is closed.
   */
  destroy: function () {
    dumpn("Destroying the FilterView");

    this._searchbox.removeEventListener("click", this._onClick);
    this._searchbox.removeEventListener("select", this._onInput);
    this._searchbox.removeEventListener("input", this._onInput);
    this._searchbox.removeEventListener("keypress", this._onKeyPress);
    this._searchbox.removeEventListener("blur", this._onBlur);

    this.FilteredSources.destroy();
    this.FilteredFunctions.destroy();
  },

  /**
   * Add commands that XUL can fire.
   */
  _addCommands: function () {
    XULUtils.addCommands(document.getElementById("debuggerCommands"), {
      fileSearchCommand: () => this._doFileSearch(),
      globalSearchCommand: () => this._doGlobalSearch(),
      functionSearchCommand: () => this._doFunctionSearch(),
      tokenSearchCommand: () => this._doTokenSearch(),
      lineSearchCommand: () => this._doLineSearch(),
      variableSearchCommand: () => this._doVariableSearch(),
      variablesFocusCommand: () => this._doVariablesFocus()
    });
  },

  /**
   * Gets the entered operator and arguments in the searchbox.
   * @return array
   */
  get searchData() {
    let operator = "", args = [];

    let rawValue = this._searchbox.value;
    let rawLength = rawValue.length;
    let globalFlagIndex = rawValue.indexOf(SEARCH_GLOBAL_FLAG);
    let functionFlagIndex = rawValue.indexOf(SEARCH_FUNCTION_FLAG);
    let variableFlagIndex = rawValue.indexOf(SEARCH_VARIABLE_FLAG);
    let tokenFlagIndex = rawValue.lastIndexOf(SEARCH_TOKEN_FLAG);
    let lineFlagIndex = rawValue.lastIndexOf(SEARCH_LINE_FLAG);

    // This is not a global, function or variable search, allow file/line flags.
    if (globalFlagIndex != 0 && functionFlagIndex != 0 && variableFlagIndex != 0) {
      // Token search has precedence over line search.
      if (tokenFlagIndex != -1) {
        operator = SEARCH_TOKEN_FLAG;
        args.push(rawValue.slice(0, tokenFlagIndex)); // file
        args.push(rawValue.substr(tokenFlagIndex + 1, rawLength)); // token
      } else if (lineFlagIndex != -1) {
        operator = SEARCH_LINE_FLAG;
        args.push(rawValue.slice(0, lineFlagIndex)); // file
        args.push(+rawValue.substr(lineFlagIndex + 1, rawLength) || 0); // line
      } else {
        args.push(rawValue);
      }
    }
    // Global searches dissalow the use of file or line flags.
    else if (globalFlagIndex == 0) {
      operator = SEARCH_GLOBAL_FLAG;
      args.push(rawValue.slice(1));
    }
    // Function searches dissalow the use of file or line flags.
    else if (functionFlagIndex == 0) {
      operator = SEARCH_FUNCTION_FLAG;
      args.push(rawValue.slice(1));
    }
    // Variable searches dissalow the use of file or line flags.
    else if (variableFlagIndex == 0) {
      operator = SEARCH_VARIABLE_FLAG;
      args.push(rawValue.slice(1));
    }

    return [operator, args];
  },

  /**
   * Returns the current search operator.
   * @return string
   */
  get searchOperator() {
    return this.searchData[0];
  },

  /**
   * Returns the current search arguments.
   * @return array
   */
  get searchArguments() {
    return this.searchData[1];
  },

  /**
   * Clears the text from the searchbox and any changed views.
   */
  clearSearch: function () {
    this._searchbox.value = "";
    this.clearViews();

    this.FilteredSources.clearView();
    this.FilteredFunctions.clearView();
  },

  /**
   * Clears all the views that may pop up when searching.
   */
  clearViews: function () {
    this.DebuggerView.GlobalSearch.clearView();
    this.FilteredSources.clearView();
    this.FilteredFunctions.clearView();
    this._searchboxHelpPanel.hidePopup();
  },

  /**
   * Performs a line search if necessary.
   * (Jump to lines in the currently visible source).
   *
   * @param number aLine
   *        The source line number to jump to.
   */
  _performLineSearch: function (aLine) {
    // Make sure we're actually searching for a valid line.
    if (aLine) {
      this.DebuggerView.editor.setCursor({ line: aLine - 1, ch: 0 }, "center");
    }
  },

  /**
   * Performs a token search if necessary.
   * (Search for tokens in the currently visible source).
   *
   * @param string aToken
   *        The source token to find.
   */
  _performTokenSearch: function (aToken) {
    // Make sure we're actually searching for a valid token.
    if (!aToken) {
      return;
    }
    this.DebuggerView.editor.find(aToken);
  },

  /**
   * The click listener for the search container.
   */
  _onClick: function () {
    // If there's some text in the searchbox, displaying a panel would
    // interfere with double/triple click default behaviors.
    if (!this._searchbox.value) {
      this._searchboxHelpPanel.openPopup(this._searchbox);
    }
  },

  /**
   * The input listener for the search container.
   */
  _onInput: function () {
    this.clearViews();

    // Make sure we're actually searching for something.
    if (!this._searchbox.value) {
      return;
    }

    // Perform the required search based on the specified operator.
    switch (this.searchOperator) {
      case SEARCH_GLOBAL_FLAG:
        // Schedule a global search for when the user stops typing.
        this.DebuggerView.GlobalSearch.scheduleSearch(this.searchArguments[0]);
        break;
      case SEARCH_FUNCTION_FLAG:
      // Schedule a function search for when the user stops typing.
        this.FilteredFunctions.scheduleSearch(this.searchArguments[0]);
        break;
      case SEARCH_VARIABLE_FLAG:
        // Schedule a variable search for when the user stops typing.
        this.DebuggerView.Variables.scheduleSearch(this.searchArguments[0]);
        break;
      case SEARCH_TOKEN_FLAG:
        // Schedule a file+token search for when the user stops typing.
        this.FilteredSources.scheduleSearch(this.searchArguments[0]);
        this._performTokenSearch(this.searchArguments[1]);
        break;
      case SEARCH_LINE_FLAG:
        // Schedule a file+line search for when the user stops typing.
        this.FilteredSources.scheduleSearch(this.searchArguments[0]);
        this._performLineSearch(this.searchArguments[1]);
        break;
      default:
        // Schedule a file only search for when the user stops typing.
        this.FilteredSources.scheduleSearch(this.searchArguments[0]);
        break;
    }
  },

  /**
   * The key press listener for the search container.
   */
  _onKeyPress: function (e) {
    // This attribute is not implemented in Gecko at this time, see bug 680830.
    e.char = String.fromCharCode(e.charCode);

    // Perform the required action based on the specified operator.
    let [operator, args] = this.searchData;
    let isGlobalSearch = operator == SEARCH_GLOBAL_FLAG;
    let isFunctionSearch = operator == SEARCH_FUNCTION_FLAG;
    let isVariableSearch = operator == SEARCH_VARIABLE_FLAG;
    let isTokenSearch = operator == SEARCH_TOKEN_FLAG;
    let isLineSearch = operator == SEARCH_LINE_FLAG;
    let isFileOnlySearch = !operator && args.length == 1;

    // Depending on the pressed keys, determine to correct action to perform.
    let actionToPerform;

    // Meta+G and Ctrl+N focus next matches.
    if ((e.char == "g" && e.metaKey) || e.char == "n" && e.ctrlKey) {
      actionToPerform = "selectNext";
    }
    // Meta+Shift+G and Ctrl+P focus previous matches.
    else if ((e.char == "G" && e.metaKey) || e.char == "p" && e.ctrlKey) {
      actionToPerform = "selectPrev";
    }
    // Return, enter, down and up keys focus next or previous matches, while
    // the escape key switches focus from the search container.
    else switch (e.keyCode) {
      case KeyCodes.DOM_VK_RETURN:
        var isReturnKey = true;
        // If the shift key is pressed, focus on the previous result
        actionToPerform = e.shiftKey ? "selectPrev" : "selectNext";
        break;
      case KeyCodes.DOM_VK_DOWN:
        actionToPerform = "selectNext";
        break;
      case KeyCodes.DOM_VK_UP:
        actionToPerform = "selectPrev";
        break;
    }

    // If there's no action to perform, or no operator, file line or token
    // were specified, then this is either a broken or empty search.
    if (!actionToPerform || (!operator && !args.length)) {
      this.DebuggerView.editor.dropSelection();
      return;
    }

    e.preventDefault();
    e.stopPropagation();

    // Jump to the next/previous entry in the global search, or perform
    // a new global search immediately
    if (isGlobalSearch) {
      let targetView = this.DebuggerView.GlobalSearch;
      if (!isReturnKey) {
        targetView[actionToPerform]();
      } else if (targetView.hidden) {
        targetView.scheduleSearch(args[0], 0);
      }
      return;
    }

    // Jump to the next/previous entry in the function search, perform
    // a new function search immediately, or clear it.
    if (isFunctionSearch) {
      let targetView = this.FilteredFunctions;
      if (!isReturnKey) {
        targetView[actionToPerform]();
      } else if (targetView.hidden) {
        targetView.scheduleSearch(args[0], 0);
      } else {
        if (!targetView.selectedItem) {
          targetView.selectedIndex = 0;
        }
        this.clearSearch();
      }
      return;
    }

    // Perform a new variable search immediately.
    if (isVariableSearch) {
      let targetView = this.DebuggerView.Variables;
      if (isReturnKey) {
        targetView.scheduleSearch(args[0], 0);
      }
      return;
    }

    // Jump to the next/previous entry in the file search, perform
    // a new file search immediately, or clear it.
    if (isFileOnlySearch) {
      let targetView = this.FilteredSources;
      if (!isReturnKey) {
        targetView[actionToPerform]();
      } else if (targetView.hidden) {
        targetView.scheduleSearch(args[0], 0);
      } else {
        if (!targetView.selectedItem) {
          targetView.selectedIndex = 0;
        }
        this.clearSearch();
      }
      return;
    }

    // Jump to the next/previous instance of the currently searched token.
    if (isTokenSearch) {
      let methods = { selectNext: "findNext", selectPrev: "findPrev" };
      this.DebuggerView.editor[methods[actionToPerform]]();
      return;
    }

    // Increment/decrement the currently searched caret line.
    if (isLineSearch) {
      let [, line] = args;
      let amounts = { selectNext: 1, selectPrev: -1 };

      // Modify the line number and jump to it.
      line += !isReturnKey ? amounts[actionToPerform] : 0;
      let lineCount = this.DebuggerView.editor.lineCount();
      let lineTarget = line < 1 ? 1 : line > lineCount ? lineCount : line;
      this._doSearch(SEARCH_LINE_FLAG, lineTarget);
      return;
    }
  },

  /**
   * The blur listener for the search container.
   */
  _onBlur: function () {
    this.clearViews();
  },

  /**
   * Called when a filtering key sequence was pressed.
   *
   * @param string aOperator
   *        The operator to use for filtering.
   */
  _doSearch: function (aOperator = "", aText = "") {
    this._searchbox.focus();
    this._searchbox.value = ""; // Need to clear value beforehand. Bug 779738.

    if (aText) {
      this._searchbox.value = aOperator + aText;
      return;
    }
    if (this.DebuggerView.editor.somethingSelected()) {
      this._searchbox.value = aOperator + this.DebuggerView.editor.getSelection();
      return;
    }

    let content = this.DebuggerView.editor.getText();
    if (content.length < this.DebuggerView.LARGE_FILE_SIZE &&
        SEARCH_AUTOFILL.includes(aOperator)) {
      let cursor = this.DebuggerView.editor.getCursor();
      let location = this.DebuggerView.Sources.selectedItem.attachment.source.url;
      let source = this.Parser.get(content, location);
      let identifier = source.getIdentifierAt({ line: cursor.line + 1, column: cursor.ch });

      if (identifier && identifier.name) {
        this._searchbox.value = aOperator + identifier.name;
        this._searchbox.select();
        this._searchbox.selectionStart += aOperator.length;
        return;
      }
    }
    this._searchbox.value = aOperator;
  },

  /**
   * Called when the source location filter key sequence was pressed.
   */
  _doFileSearch: function () {
    this._doSearch();
    this._searchboxHelpPanel.openPopup(this._searchbox);
  },

  /**
   * Called when the global search filter key sequence was pressed.
   */
  _doGlobalSearch: function () {
    this._doSearch(SEARCH_GLOBAL_FLAG);
    this._searchboxHelpPanel.hidePopup();
  },

  /**
   * Called when the source function filter key sequence was pressed.
   */
  _doFunctionSearch: function () {
    this._doSearch(SEARCH_FUNCTION_FLAG);
    this._searchboxHelpPanel.hidePopup();
  },

  /**
   * Called when the source token filter key sequence was pressed.
   */
  _doTokenSearch: function () {
    this._doSearch(SEARCH_TOKEN_FLAG);
    this._searchboxHelpPanel.hidePopup();
  },

  /**
   * Called when the source line filter key sequence was pressed.
   */
  _doLineSearch: function () {
    this._doSearch(SEARCH_LINE_FLAG);
    this._searchboxHelpPanel.hidePopup();
  },

  /**
   * Called when the variable search filter key sequence was pressed.
   */
  _doVariableSearch: function () {
    this._doSearch(SEARCH_VARIABLE_FLAG);
    this._searchboxHelpPanel.hidePopup();
  },

  /**
   * Called when the variables focus key sequence was pressed.
   */
  _doVariablesFocus: function () {
    this.DebuggerView.showInstrumentsPane();
    this.DebuggerView.Variables.focusFirstVisibleItem();
  },

  _searchbox: null,
  _searchboxHelpPanel: null,
  _globalOperatorButton: null,
  _globalOperatorLabel: null,
  _functionOperatorButton: null,
  _functionOperatorLabel: null,
  _tokenOperatorButton: null,
  _tokenOperatorLabel: null,
  _lineOperatorButton: null,
  _lineOperatorLabel: null,
  _variableOperatorButton: null,
  _variableOperatorLabel: null,
  _fileSearchKey: "",
  _globalSearchKey: "",
  _filteredFunctionsKey: "",
  _tokenSearchKey: "",
  _lineSearchKey: "",
  _variableSearchKey: "",
};

/**
 * Functions handling the filtered sources UI.
 */
function FilteredSourcesView(DebuggerView) {
  dumpn("FilteredSourcesView was instantiated");

  this.DebuggerView = DebuggerView;

  this._onClick = this._onClick.bind(this);
  this._onSelect = this._onSelect.bind(this);
}

FilteredSourcesView.prototype = extend(ResultsPanelContainer.prototype, {
  /**
   * Initialization function, called when the debugger is started.
   */
  initialize: function () {
    dumpn("Initializing the FilteredSourcesView");

    this.anchor = document.getElementById("searchbox");
    this.widget.addEventListener("select", this._onSelect);
    this.widget.addEventListener("click", this._onClick);
  },

  /**
   * Destruction function, called when the debugger is closed.
   */
  destroy: function () {
    dumpn("Destroying the FilteredSourcesView");

    this.widget.removeEventListener("select", this._onSelect);
    this.widget.removeEventListener("click", this._onClick);
    this.anchor = null;
  },

  /**
   * Schedules searching for a source.
   *
   * @param string aToken
   *        The function to search for.
   * @param number aWait
   *        The amount of milliseconds to wait until draining.
   */
  scheduleSearch: function (aToken, aWait) {
    // The amount of time to wait for the requests to settle.
    let maxDelay = FILE_SEARCH_ACTION_MAX_DELAY;
    let delay = aWait === undefined ? maxDelay / aToken.length : aWait;

    // Allow requests to settle down first.
    setNamedTimeout("sources-search", delay, () => this._doSearch(aToken));
  },

  /**
   * Finds file matches in all the displayed sources.
   *
   * @param string aToken
   *        The string to search for.
   */
  _doSearch: function (aToken, aStore = []) {
    // Don't continue filtering if the searched token is an empty string.
    // In contrast with function searching, in this case we don't want to
    // show a list of all the files when no search token was supplied.
    if (!aToken) {
      return;
    }

    for (let item of this.DebuggerView.Sources.items) {
      let lowerCaseLabel = item.attachment.label.toLowerCase();
      let lowerCaseToken = aToken.toLowerCase();
      if (lowerCaseLabel.match(lowerCaseToken)) {
        aStore.push(item);
      }

      // Once the maximum allowed number of results is reached, proceed
      // with building the UI immediately.
      if (aStore.length >= RESULTS_PANEL_MAX_RESULTS) {
        this._syncView(aStore);
        return;
      }
    }

    // Couldn't reach the maximum allowed number of results, but that's ok,
    // continue building the UI.
    this._syncView(aStore);
  },

  /**
   * Updates the list of sources displayed in this container.
   *
   * @param array aSearchResults
   *        The results array, containing search details for each source.
   */
  _syncView: function (aSearchResults) {
    // If there are no matches found, keep the popup hidden and avoid
    // creating the view.
    if (!aSearchResults.length) {
      window.emit(EVENTS.FILE_SEARCH_MATCH_NOT_FOUND);
      return;
    }

    for (let item of aSearchResults) {
      let url = item.attachment.source.url;

      if (url) {
        // Create the element node for the location item.
        let itemView = this._createItemView(
          SourceUtils.trimUrlLength(item.attachment.label),
          SourceUtils.trimUrlLength(url, 0, "start")
        );

        // Append a location item to this container for each match.
        this.push([itemView], {
          index: -1, /* specifies on which position should the item be appended */
          attachment: {
            url: url
          }
        });
      }
    }

    // There's at least one item displayed in this container. Don't select it
    // automatically if not forced (by tests) or in tandem with an
    // operator.
    if (this._autoSelectFirstItem || this.DebuggerView.Filtering.searchOperator) {
      this.selectedIndex = 0;
    }
    this.hidden = false;

    // Signal that file search matches were found and displayed.
    window.emit(EVENTS.FILE_SEARCH_MATCH_FOUND);
  },

  /**
   * The click listener for this container.
   */
  _onClick: function (e) {
    let locationItem = this.getItemForElement(e.target);
    if (locationItem) {
      this.selectedItem = locationItem;
      this.DebuggerView.Filtering.clearSearch();
    }
  },

  /**
   * The select listener for this container.
   *
   * @param object aItem
   *        The item associated with the element to select.
   */
  _onSelect: function ({ detail: locationItem }) {
    if (locationItem) {
      let source = queries.getSourceByURL(DebuggerController.getState(),
                                          locationItem.attachment.url);
      this.DebuggerView.setEditorLocation(source.actor, undefined, {
        noCaret: true,
        noDebug: true
      });
    }
  }
});

/**
 * Functions handling the function search UI.
 */
function FilteredFunctionsView(SourceScripts, Parser, DebuggerView) {
  dumpn("FilteredFunctionsView was instantiated");

  this.SourceScripts = SourceScripts;
  this.Parser = Parser;
  this.DebuggerView = DebuggerView;

  this._onClick = this._onClick.bind(this);
  this._onSelect = this._onSelect.bind(this);
}

FilteredFunctionsView.prototype = extend(ResultsPanelContainer.prototype, {
  /**
   * Initialization function, called when the debugger is started.
   */
  initialize: function () {
    dumpn("Initializing the FilteredFunctionsView");

    this.anchor = document.getElementById("searchbox");
    this.widget.addEventListener("select", this._onSelect);
    this.widget.addEventListener("click", this._onClick);
  },

  /**
   * Destruction function, called when the debugger is closed.
   */
  destroy: function () {
    dumpn("Destroying the FilteredFunctionsView");

    this.widget.removeEventListener("select", this._onSelect);
    this.widget.removeEventListener("click", this._onClick);
    this.anchor = null;
  },

  /**
   * Schedules searching for a function in all of the sources.
   *
   * @param string aToken
   *        The function to search for.
   * @param number aWait
   *        The amount of milliseconds to wait until draining.
   */
  scheduleSearch: function (aToken, aWait) {
    // The amount of time to wait for the requests to settle.
    let maxDelay = FUNCTION_SEARCH_ACTION_MAX_DELAY;
    let delay = aWait === undefined ? maxDelay / aToken.length : aWait;

    // Allow requests to settle down first.
    setNamedTimeout("function-search", delay, () => {
      // Start fetching as many sources as possible, then perform the search.
      let actors = this.DebuggerView.Sources.values;
      let sourcesFetched = DebuggerController.dispatch(actions.getTextForSources(actors));
      sourcesFetched.then(aSources => this._doSearch(aToken, aSources));
    });
  },

  /**
   * Finds function matches in all the sources stored in the cache, and groups
   * them by location and line number.
   *
   * @param string aToken
   *        The string to search for.
   * @param array aSources
   *        An array of [url, text] tuples for each source.
   */
  _doSearch: function (aToken, aSources, aStore = []) {
    // Continue parsing even if the searched token is an empty string, to
    // cache the syntax tree nodes generated by the reflection API.

    // Make sure the currently displayed source is parsed first. Once the
    // maximum allowed number of results are found, parsing will be halted.
    let currentActor = this.DebuggerView.Sources.selectedValue;
    let currentSource = aSources.filter(([actor]) => actor == currentActor)[0];
    aSources.splice(aSources.indexOf(currentSource), 1);
    aSources.unshift(currentSource);

    // If not searching for a specific function, only parse the displayed source,
    // which is now the first item in the sources array.
    if (!aToken) {
      aSources.splice(1);
    }

    for (let [actor, contents] of aSources) {
      let item = this.DebuggerView.Sources.getItemByValue(actor);
      let url = item.attachment.source.url;
      if (!url) {
        continue;
      }

      let parsedSource = this.Parser.get(contents, url);
      let sourceResults = parsedSource.getNamedFunctionDefinitions(aToken);

      for (let scriptResult of sourceResults) {
        for (let parseResult of scriptResult) {
          aStore.push({
            sourceUrl: scriptResult.sourceUrl,
            scriptOffset: scriptResult.scriptOffset,
            functionName: parseResult.functionName,
            functionLocation: parseResult.functionLocation,
            inferredName: parseResult.inferredName,
            inferredChain: parseResult.inferredChain,
            inferredLocation: parseResult.inferredLocation
          });

          // Once the maximum allowed number of results is reached, proceed
          // with building the UI immediately.
          if (aStore.length >= RESULTS_PANEL_MAX_RESULTS) {
            this._syncView(aStore);
            return;
          }
        }
      }
    }

    // Couldn't reach the maximum allowed number of results, but that's ok,
    // continue building the UI.
    this._syncView(aStore);
  },

  /**
   * Updates the list of functions displayed in this container.
   *
   * @param array aSearchResults
   *        The results array, containing search details for each source.
   */
  _syncView: function (aSearchResults) {
    // If there are no matches found, keep the popup hidden and avoid
    // creating the view.
    if (!aSearchResults.length) {
      window.emit(EVENTS.FUNCTION_SEARCH_MATCH_NOT_FOUND);
      return;
    }

    for (let item of aSearchResults) {
      // Some function expressions don't necessarily have a name, but the
      // parser provides us with an inferred name from an enclosing
      // VariableDeclarator, AssignmentExpression, ObjectExpression node.
      if (item.functionName && item.inferredName &&
          item.functionName != item.inferredName) {
        let s = " " + L10N.getStr("functionSearchSeparatorLabel") + " ";
        item.displayedName = item.inferredName + s + item.functionName;
      }
      // The function doesn't have an explicit name, but it could be inferred.
      else if (item.inferredName) {
        item.displayedName = item.inferredName;
      }
      // The function only has an explicit name.
      else {
        item.displayedName = item.functionName;
      }

      // Some function expressions have unexpected bounds, since they may not
      // necessarily have an associated name defining them.
      if (item.inferredLocation) {
        item.actualLocation = item.inferredLocation;
      } else {
        item.actualLocation = item.functionLocation;
      }

      // Create the element node for the function item.
      let itemView = this._createItemView(
        SourceUtils.trimUrlLength(item.displayedName + "()"),
        SourceUtils.trimUrlLength(item.sourceUrl, 0, "start"),
        (item.inferredChain || []).join(".")
      );

      // Append a function item to this container for each match.
      this.push([itemView], {
        index: -1, /* specifies on which position should the item be appended */
        attachment: item
      });
    }

    // There's at least one item displayed in this container. Don't select it
    // automatically if not forced (by tests).
    if (this._autoSelectFirstItem) {
      this.selectedIndex = 0;
    }
    this.hidden = false;

    // Signal that function search matches were found and displayed.
    window.emit(EVENTS.FUNCTION_SEARCH_MATCH_FOUND);
  },

  /**
   * The click listener for this container.
   */
  _onClick: function (e) {
    let functionItem = this.getItemForElement(e.target);
    if (functionItem) {
      this.selectedItem = functionItem;
      this.DebuggerView.Filtering.clearSearch();
    }
  },

  /**
   * The select listener for this container.
   */
  _onSelect: function ({ detail: functionItem }) {
    if (functionItem) {
      let sourceUrl = functionItem.attachment.sourceUrl;
      let actor = queries.getSourceByURL(DebuggerController.getState(), sourceUrl).actor;
      let scriptOffset = functionItem.attachment.scriptOffset;
      let actualLocation = functionItem.attachment.actualLocation;

      this.DebuggerView.setEditorLocation(actor, actualLocation.start.line, {
        charOffset: scriptOffset,
        columnOffset: actualLocation.start.column,
        align: "center",
        noDebug: true
      });
    }
  },

  _searchTimeout: null,
  _searchFunction: null,
  _searchedToken: ""
});

DebuggerView.Filtering = new FilterView(DebuggerController, DebuggerView);
PK
!<nHA�X�X<chrome/devtools/content/debugger/views/global-search-view.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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/. */
/* import-globals-from ../debugger-controller.js */
/* import-globals-from ../debugger-view.js */
/* import-globals-from ../utils.js */
/* globals document, window */
"use strict";

/**
 * Functions handling the global search UI.
 */
function GlobalSearchView(DebuggerController, DebuggerView) {
  dumpn("GlobalSearchView was instantiated");

  this.SourceScripts = DebuggerController.SourceScripts;
  this.DebuggerView = DebuggerView;

  this._onHeaderClick = this._onHeaderClick.bind(this);
  this._onLineClick = this._onLineClick.bind(this);
  this._onMatchClick = this._onMatchClick.bind(this);
}

GlobalSearchView.prototype = extend(WidgetMethods, {
  /**
   * Initialization function, called when the debugger is started.
   */
  initialize: function () {
    dumpn("Initializing the GlobalSearchView");

    this.widget = new SimpleListWidget(document.getElementById("globalsearch"));
    this._splitter = document.querySelector("#globalsearch + .devtools-horizontal-splitter");

    this.emptyText = L10N.getStr("noMatchingStringsText");
  },

  /**
   * Destruction function, called when the debugger is closed.
   */
  destroy: function () {
    dumpn("Destroying the GlobalSearchView");
  },

  /**
   * Sets the results container hidden or visible. It's hidden by default.
   * @param boolean aFlag
   */
  set hidden(aFlag) {
    this.widget.setAttribute("hidden", aFlag);
    this._splitter.setAttribute("hidden", aFlag);
  },

  /**
   * Gets the visibility state of the global search container.
   * @return boolean
   */
  get hidden() {
    return this.widget.getAttribute("hidden") == "true" ||
           this._splitter.getAttribute("hidden") == "true";
  },

  /**
   * Hides and removes all items from this search container.
   */
  clearView: function () {
    this.hidden = true;
    this.empty();
  },

  /**
   * Selects the next found item in this container.
   * Does not change the currently focused node.
   */
  selectNext: function () {
    let totalLineResults = LineResults.size();
    if (!totalLineResults) {
      return;
    }
    if (++this._currentlyFocusedMatch >= totalLineResults) {
      this._currentlyFocusedMatch = 0;
    }
    this._onMatchClick({
      target: LineResults.getElementAtIndex(this._currentlyFocusedMatch)
    });
  },

  /**
   * Selects the previously found item in this container.
   * Does not change the currently focused node.
   */
  selectPrev: function () {
    let totalLineResults = LineResults.size();
    if (!totalLineResults) {
      return;
    }
    if (--this._currentlyFocusedMatch < 0) {
      this._currentlyFocusedMatch = totalLineResults - 1;
    }
    this._onMatchClick({
      target: LineResults.getElementAtIndex(this._currentlyFocusedMatch)
    });
  },

  /**
   * Schedules searching for a string in all of the sources.
   *
   * @param string aToken
   *        The string to search for.
   * @param number aWait
   *        The amount of milliseconds to wait until draining.
   */
  scheduleSearch: function (aToken, aWait) {
    // The amount of time to wait for the requests to settle.
    let maxDelay = GLOBAL_SEARCH_ACTION_MAX_DELAY;
    let delay = aWait === undefined ? maxDelay / aToken.length : aWait;

    // Allow requests to settle down first.
    setNamedTimeout("global-search", delay, () => {
      // Start fetching as many sources as possible, then perform the search.
      let actors = this.DebuggerView.Sources.values;
      let sourcesFetched = DebuggerController.dispatch(actions.getTextForSources(actors));
      sourcesFetched.then(aSources => this._doSearch(aToken, aSources));
    });
  },

  /**
   * Finds string matches in all the sources stored in the controller's cache,
   * and groups them by url and line number.
   *
   * @param string aToken
   *        The string to search for.
   * @param array aSources
   *        An array of [url, text] tuples for each source.
   */
  _doSearch: function (aToken, aSources) {
    // Don't continue filtering if the searched token is an empty string.
    if (!aToken) {
      this.clearView();
      return;
    }

    // Search is not case sensitive, prepare the actual searched token.
    let lowerCaseToken = aToken.toLowerCase();
    let tokenLength = aToken.length;

    // Create a Map containing search details for each source.
    let globalResults = new GlobalResults();

    // Search for the specified token in each source's text.
    for (let [actor, text] of aSources) {
      let item = this.DebuggerView.Sources.getItemByValue(actor);
      let url = item.attachment.source.url;
      if (!url) {
        continue;
      }

      // Verify that the search token is found anywhere in the source.
      if (!text.toLowerCase().includes(lowerCaseToken)) {
        continue;
      }
      // ...and if so, create a Map containing search details for each line.
      let sourceResults = new SourceResults(actor,
                                            globalResults,
                                            this.DebuggerView.Sources);

      // Search for the specified token in each line's text.
      text.split("\n").forEach((aString, aLine) => {
        // Search is not case sensitive, prepare the actual searched line.
        let lowerCaseLine = aString.toLowerCase();

        // Verify that the search token is found anywhere in this line.
        if (!lowerCaseLine.includes(lowerCaseToken)) {
          return;
        }
        // ...and if so, create a Map containing search details for each word.
        let lineResults = new LineResults(aLine, sourceResults);

        // Search for the specified token this line's text.
        lowerCaseLine.split(lowerCaseToken).reduce((aPrev, aCurr, aIndex, aArray) => {
          let prevLength = aPrev.length;
          let currLength = aCurr.length;

          // Everything before the token is unmatched.
          let unmatched = aString.substr(prevLength, currLength);
          lineResults.add(unmatched);

          // The lowered-case line was split by the lowered-case token. So,
          // get the actual matched text from the original line's text.
          if (aIndex != aArray.length - 1) {
            let matched = aString.substr(prevLength + currLength, tokenLength);
            let range = { start: prevLength + currLength, length: matched.length };
            lineResults.add(matched, range, true);
          }

          // Continue with the next sub-region in this line's text.
          return aPrev + aToken + aCurr;
        }, "");

        if (lineResults.matchCount) {
          sourceResults.add(lineResults);
        }
      });

      if (sourceResults.matchCount) {
        globalResults.add(sourceResults);
      }
    }

    // Rebuild the results, then signal if there are any matches.
    if (globalResults.matchCount) {
      this.hidden = false;
      this._currentlyFocusedMatch = -1;
      this._createGlobalResultsUI(globalResults);
      window.emit(EVENTS.GLOBAL_SEARCH_MATCH_FOUND);
    } else {
      window.emit(EVENTS.GLOBAL_SEARCH_MATCH_NOT_FOUND);
    }
  },

  /**
   * Creates global search results entries and adds them to this container.
   *
   * @param GlobalResults aGlobalResults
   *        An object containing all source results, grouped by source location.
   */
  _createGlobalResultsUI: function (aGlobalResults) {
    let i = 0;

    for (let sourceResults of aGlobalResults) {
      if (i++ == 0) {
        this._createSourceResultsUI(sourceResults);
      } else {
        // Dispatch subsequent document manipulation operations, to avoid
        // blocking the main thread when a large number of search results
        // is found, thus giving the impression of faster searching.
        Services.tm.dispatchToMainThread({ run:
          this._createSourceResultsUI.bind(this, sourceResults)
        });
      }
    }
  },

  /**
   * Creates source search results entries and adds them to this container.
   *
   * @param SourceResults aSourceResults
   *        An object containing all the matched lines for a specific source.
   */
  _createSourceResultsUI: function (aSourceResults) {
    // Create the element node for the source results item.
    let container = document.createElement("hbox");
    aSourceResults.createView(container, {
      onHeaderClick: this._onHeaderClick,
      onLineClick: this._onLineClick,
      onMatchClick: this._onMatchClick
    });

    // Append a source results item to this container.
    let item = this.push([container], {
      index: -1, /* specifies on which position should the item be appended */
      attachment: {
        sourceResults: aSourceResults
      }
    });
  },

  /**
   * The click listener for a results header.
   */
  _onHeaderClick: function (e) {
    let sourceResultsItem = SourceResults.getItemForElement(e.target);
    sourceResultsItem.instance.toggle(e);
  },

  /**
   * The click listener for a results line.
   */
  _onLineClick: function (e) {
    let lineResultsItem = LineResults.getItemForElement(e.target);
    this._onMatchClick({ target: lineResultsItem.firstMatch });
  },

  /**
   * The click listener for a result match.
   */
  _onMatchClick: function (e) {
    if (e instanceof Event) {
      e.preventDefault();
      e.stopPropagation();
    }

    let target = e.target;
    let sourceResultsItem = SourceResults.getItemForElement(target);
    let lineResultsItem = LineResults.getItemForElement(target);

    sourceResultsItem.instance.expand();
    this._currentlyFocusedMatch = LineResults.indexOfElement(target);
    this._scrollMatchIntoViewIfNeeded(target);
    this._bounceMatch(target);

    let actor = sourceResultsItem.instance.actor;
    let line = lineResultsItem.instance.line;

    this.DebuggerView.setEditorLocation(actor, line + 1, { noDebug: true });

    let range = lineResultsItem.lineData.range;
    let cursor = this.DebuggerView.editor.getOffset({ line: line, ch: 0 });
    let [ anchor, head ] = this.DebuggerView.editor.getPosition(
      cursor + range.start,
      cursor + range.start + range.length
    );

    this.DebuggerView.editor.setSelection(anchor, head);
  },

  /**
   * Scrolls a match into view if not already visible.
   *
   * @param nsIDOMNode aMatch
   *        The match to scroll into view.
   */
  _scrollMatchIntoViewIfNeeded: function (aMatch) {
    this.widget.ensureElementIsVisible(aMatch);
  },

  /**
   * Starts a bounce animation for a match.
   *
   * @param nsIDOMNode aMatch
   *        The match to start a bounce animation for.
   */
  _bounceMatch: function (aMatch) {
    Services.tm.dispatchToMainThread({ run: () => {
      aMatch.addEventListener("transitionend", function () {
        aMatch.removeAttribute("focused");
      }, {once: true});
      aMatch.setAttribute("focused", "");
    }});
    aMatch.setAttribute("focusing", "");
  },

  _splitter: null,
  _currentlyFocusedMatch: -1,
  _forceExpandResults: false
});

DebuggerView.GlobalSearch = new GlobalSearchView(DebuggerController, DebuggerView);

/**
 * An object containing all source results, grouped by source location.
 * Iterable via "for (let [location, sourceResults] of globalResults) { }".
 */
function GlobalResults() {
  this._store = [];
  SourceResults._itemsByElement = new Map();
  LineResults._itemsByElement = new Map();
}

GlobalResults.prototype = {
  /**
   * Adds source results to this store.
   *
   * @param SourceResults aSourceResults
   *        An object containing search results for a specific source.
   */
  add: function (aSourceResults) {
    this._store.push(aSourceResults);
  },

  /**
   * Gets the number of source results in this store.
   */
  get matchCount() {
    return this._store.length;
  }
};

/**
 * An object containing all the matched lines for a specific source.
 * Iterable via "for (let [lineNumber, lineResults] of sourceResults) { }".
 *
 * @param string aActor
 *        The target source actor id.
 * @param GlobalResults aGlobalResults
 *        An object containing all source results, grouped by source location.
 */
function SourceResults(aActor, aGlobalResults, sourcesView) {
  let item = sourcesView.getItemByValue(aActor);
  this.actor = aActor;
  this.label = item.attachment.source.url;
  this._globalResults = aGlobalResults;
  this._store = [];
}

SourceResults.prototype = {
  /**
   * Adds line results to this store.
   *
   * @param LineResults aLineResults
   *        An object containing search results for a specific line.
   */
  add: function (aLineResults) {
    this._store.push(aLineResults);
  },

  /**
   * Gets the number of line results in this store.
   */
  get matchCount() {
    return this._store.length;
  },

  /**
   * Expands the element, showing all the added details.
   */
  expand: function () {
    this._resultsContainer.removeAttribute("hidden");
    this._arrow.setAttribute("open", "");
  },

  /**
   * Collapses the element, hiding all the added details.
   */
  collapse: function () {
    this._resultsContainer.setAttribute("hidden", "true");
    this._arrow.removeAttribute("open");
  },

  /**
   * Toggles between the element collapse/expand state.
   */
  toggle: function (e) {
    this.expanded ^= 1;
  },

  /**
   * Gets this element's expanded state.
   * @return boolean
   */
  get expanded() {
    return this._resultsContainer.getAttribute("hidden") != "true" &&
           this._arrow.hasAttribute("open");
  },

  /**
   * Sets this element's expanded state.
   * @param boolean aFlag
   */
  set expanded(aFlag) {
    this[aFlag ? "expand" : "collapse"]();
  },

  /**
   * Gets the element associated with this item.
   * @return nsIDOMNode
   */
  get target() {
    return this._target;
  },

  /**
   * Customization function for creating this item's UI.
   *
   * @param nsIDOMNode aElementNode
   *        The element associated with the displayed item.
   * @param object aCallbacks
   *        An object containing all the necessary callback functions:
   *          - onHeaderClick
   *          - onMatchClick
   */
  createView: function (aElementNode, aCallbacks) {
    this._target = aElementNode;

    let arrow = this._arrow = document.createElement("box");
    arrow.className = "arrow";

    let locationNode = document.createElement("label");
    locationNode.className = "plain dbg-results-header-location";
    locationNode.setAttribute("value", this.label);

    let matchCountNode = document.createElement("label");
    matchCountNode.className = "plain dbg-results-header-match-count";
    matchCountNode.setAttribute("value", "(" + this.matchCount + ")");

    let resultsHeader = this._resultsHeader = document.createElement("hbox");
    resultsHeader.className = "dbg-results-header";
    resultsHeader.setAttribute("align", "center");
    resultsHeader.appendChild(arrow);
    resultsHeader.appendChild(locationNode);
    resultsHeader.appendChild(matchCountNode);
    resultsHeader.addEventListener("click", aCallbacks.onHeaderClick);

    let resultsContainer = this._resultsContainer = document.createElement("vbox");
    resultsContainer.className = "dbg-results-container";
    resultsContainer.setAttribute("hidden", "true");

    // Create lines search results entries and add them to this container.
    // Afterwards, if the number of matches is reasonable, expand this
    // container automatically.
    for (let lineResults of this._store) {
      lineResults.createView(resultsContainer, aCallbacks);
    }
    if (this.matchCount < GLOBAL_SEARCH_EXPAND_MAX_RESULTS) {
      this.expand();
    }

    let resultsBox = document.createElement("vbox");
    resultsBox.setAttribute("flex", "1");
    resultsBox.appendChild(resultsHeader);
    resultsBox.appendChild(resultsContainer);

    aElementNode.id = "source-results-" + this.actor;
    aElementNode.className = "dbg-source-results";
    aElementNode.appendChild(resultsBox);

    SourceResults._itemsByElement.set(aElementNode, { instance: this });
  },

  actor: "",
  _globalResults: null,
  _store: null,
  _target: null,
  _arrow: null,
  _resultsHeader: null,
  _resultsContainer: null
};

/**
 * An object containing all the matches for a specific line.
 * Iterable via "for (let chunk of lineResults) { }".
 *
 * @param number aLine
 *        The target line in the source.
 * @param SourceResults aSourceResults
 *        An object containing all the matched lines for a specific source.
 */
function LineResults(aLine, aSourceResults) {
  this.line = aLine;
  this._sourceResults = aSourceResults;
  this._store = [];
  this._matchCount = 0;
}

LineResults.prototype = {
  /**
   * Adds string details to this store.
   *
   * @param string aString
   *        The text contents chunk in the line.
   * @param object aRange
   *        An object containing the { start, length } of the chunk.
   * @param boolean aMatchFlag
   *        True if the chunk is a matched string, false if just text content.
   */
  add: function (aString, aRange, aMatchFlag) {
    this._store.push({ string: aString, range: aRange, match: !!aMatchFlag });
    this._matchCount += aMatchFlag ? 1 : 0;
  },

  /**
   * Gets the number of word results in this store.
   */
  get matchCount() {
    return this._matchCount;
  },

  /**
   * Gets the element associated with this item.
   * @return nsIDOMNode
   */
  get target() {
    return this._target;
  },

  /**
   * Customization function for creating this item's UI.
   *
   * @param nsIDOMNode aElementNode
   *        The element associated with the displayed item.
   * @param object aCallbacks
   *        An object containing all the necessary callback functions:
   *          - onMatchClick
   *          - onLineClick
   */
  createView: function (aElementNode, aCallbacks) {
    this._target = aElementNode;

    let lineNumberNode = document.createElement("label");
    lineNumberNode.className = "plain dbg-results-line-number";
    lineNumberNode.classList.add("devtools-monospace");
    lineNumberNode.setAttribute("value", this.line + 1);

    let lineContentsNode = document.createElement("hbox");
    lineContentsNode.className = "dbg-results-line-contents";
    lineContentsNode.classList.add("devtools-monospace");
    lineContentsNode.setAttribute("flex", "1");

    let lineString = "";
    let lineLength = 0;
    let firstMatch = null;

    for (let lineChunk of this._store) {
      let { string, range, match } = lineChunk;
      lineString = string.substr(0, GLOBAL_SEARCH_LINE_MAX_LENGTH - lineLength);
      lineLength += string.length;

      let lineChunkNode = document.createElement("label");
      lineChunkNode.className = "plain dbg-results-line-contents-string";
      lineChunkNode.setAttribute("value", lineString);
      lineChunkNode.setAttribute("match", match);
      lineContentsNode.appendChild(lineChunkNode);

      if (match) {
        this._entangleMatch(lineChunkNode, lineChunk);
        lineChunkNode.addEventListener("click", aCallbacks.onMatchClick);
        firstMatch = firstMatch || lineChunkNode;
      }
      if (lineLength >= GLOBAL_SEARCH_LINE_MAX_LENGTH) {
        lineContentsNode.appendChild(this._ellipsis.cloneNode(true));
        break;
      }
    }

    this._entangleLine(lineContentsNode, firstMatch);
    lineContentsNode.addEventListener("click", aCallbacks.onLineClick);

    let searchResult = document.createElement("hbox");
    searchResult.className = "dbg-search-result";
    searchResult.appendChild(lineNumberNode);
    searchResult.appendChild(lineContentsNode);

    aElementNode.appendChild(searchResult);
  },

  /**
   * Handles a match while creating the view.
   * @param nsIDOMNode aNode
   * @param object aMatchChunk
   */
  _entangleMatch: function (aNode, aMatchChunk) {
    LineResults._itemsByElement.set(aNode, {
      instance: this,
      lineData: aMatchChunk
    });
  },

  /**
   * Handles a line while creating the view.
   * @param nsIDOMNode aNode
   * @param nsIDOMNode aFirstMatch
   */
  _entangleLine: function (aNode, aFirstMatch) {
    LineResults._itemsByElement.set(aNode, {
      instance: this,
      firstMatch: aFirstMatch,
      ignored: true
    });
  },

  /**
   * An nsIDOMNode label with an ellipsis value.
   */
  _ellipsis: (function () {
    let label = document.createElement("label");
    label.className = "plain dbg-results-line-contents-string";
    label.setAttribute("value", ELLIPSIS);
    return label;
  })(),

  line: 0,
  _sourceResults: null,
  _store: null,
  _target: null
};

/**
 * A generator-iterator over the global, source or line results.
 */
GlobalResults.prototype[Symbol.iterator] =
SourceResults.prototype[Symbol.iterator] =
LineResults.prototype[Symbol.iterator] = function* () {
  yield* this._store;
};

/**
 * Gets the item associated with the specified element.
 *
 * @param nsIDOMNode aElement
 *        The element used to identify the item.
 * @return object
 *         The matched item, or null if nothing is found.
 */
SourceResults.getItemForElement =
LineResults.getItemForElement = function (aElement) {
  return WidgetMethods.getItemForElement.call(this, aElement, { noSiblings: true });
};

/**
 * Gets the element associated with a particular item at a specified index.
 *
 * @param number aIndex
 *        The index used to identify the item.
 * @return nsIDOMNode
 *         The matched element, or null if nothing is found.
 */
SourceResults.getElementAtIndex =
LineResults.getElementAtIndex = function (aIndex) {
  for (let [element, item] of this._itemsByElement) {
    if (!item.ignored && !aIndex--) {
      return element;
    }
  }
  return null;
};

/**
 * Gets the index of an item associated with the specified element.
 *
 * @param nsIDOMNode aElement
 *        The element to get the index for.
 * @return number
 *         The index of the matched element, or -1 if nothing is found.
 */
SourceResults.indexOfElement =
LineResults.indexOfElement = function (aElement) {
  let count = 0;
  for (let [element, item] of this._itemsByElement) {
    if (element == aElement) {
      return count;
    }
    if (!item.ignored) {
      count++;
    }
  }
  return -1;
};

/**
 * Gets the number of cached items associated with a specified element.
 *
 * @return number
 *         The number of key/value pairs in the corresponding map.
 */
SourceResults.size =
LineResults.size = function () {
  let count = 0;
  for (let [, item] of this._itemsByElement) {
    if (!item.ignored) {
      count++;
    }
  }
  return count;
};
PK
!<�R
���6chrome/devtools/content/debugger/views/options-view.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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/. */
/* import-globals-from ../debugger-controller.js */
/* import-globals-from ../debugger-view.js */
/* import-globals-from ../utils.js */
/* globals document, window */
"use strict";

// A time interval sufficient for the options popup panel to finish hiding
// itself.
const POPUP_HIDDEN_DELAY = 100; // ms

/**
 * Functions handling the options UI.
 */
function OptionsView(DebuggerController, DebuggerView) {
  dumpn("OptionsView was instantiated");

  this.DebuggerController = DebuggerController;
  this.DebuggerView = DebuggerView;

  this._toggleAutoPrettyPrint = this._toggleAutoPrettyPrint.bind(this);
  this._togglePauseOnExceptions = this._togglePauseOnExceptions.bind(this);
  this._toggleIgnoreCaughtExceptions = this._toggleIgnoreCaughtExceptions.bind(this);
  this._toggleShowPanesOnStartup = this._toggleShowPanesOnStartup.bind(this);
  this._toggleShowVariablesOnlyEnum = this._toggleShowVariablesOnlyEnum.bind(this);
  this._toggleShowVariablesFilterBox = this._toggleShowVariablesFilterBox.bind(this);
  this._toggleShowOriginalSource = this._toggleShowOriginalSource.bind(this);
  this._toggleAutoBlackBox = this._toggleAutoBlackBox.bind(this);
}

OptionsView.prototype = {
  /**
   * Initialization function, called when the debugger is started.
   */
  initialize: function () {
    dumpn("Initializing the OptionsView");

    this._button = document.getElementById("debugger-options");
    this._autoPrettyPrint = document.getElementById("auto-pretty-print");
    this._pauseOnExceptionsItem = document.getElementById("pause-on-exceptions");
    this._ignoreCaughtExceptionsItem = document.getElementById("ignore-caught-exceptions");
    this._showPanesOnStartupItem = document.getElementById("show-panes-on-startup");
    this._showVariablesOnlyEnumItem = document.getElementById("show-vars-only-enum");
    this._showVariablesFilterBoxItem = document.getElementById("show-vars-filter-box");
    this._showOriginalSourceItem = document.getElementById("show-original-source");
    this._autoBlackBoxItem = document.getElementById("auto-black-box");

    this._autoPrettyPrint.setAttribute("checked", Prefs.autoPrettyPrint);
    this._pauseOnExceptionsItem.setAttribute("checked", Prefs.pauseOnExceptions);
    this._ignoreCaughtExceptionsItem.setAttribute("checked", Prefs.ignoreCaughtExceptions);
    this._showPanesOnStartupItem.setAttribute("checked", Prefs.panesVisibleOnStartup);
    this._showVariablesOnlyEnumItem.setAttribute("checked", Prefs.variablesOnlyEnumVisible);
    this._showVariablesFilterBoxItem.setAttribute("checked", Prefs.variablesSearchboxVisible);
    this._showOriginalSourceItem.setAttribute("checked", Prefs.sourceMapsEnabled);
    this._autoBlackBoxItem.setAttribute("checked", Prefs.autoBlackBox);

    this._addCommands();
  },

  /**
   * Destruction function, called when the debugger is closed.
   */
  destroy: function () {
    dumpn("Destroying the OptionsView");
    // Nothing to do here yet.
  },

  /**
   * Add commands that XUL can fire.
   */
  _addCommands: function () {
    XULUtils.addCommands(document.getElementById("debuggerCommands"), {
      toggleAutoPrettyPrint: () => this._toggleAutoPrettyPrint(),
      togglePauseOnExceptions: () => this._togglePauseOnExceptions(),
      toggleIgnoreCaughtExceptions: () => this._toggleIgnoreCaughtExceptions(),
      toggleShowPanesOnStartup: () => this._toggleShowPanesOnStartup(),
      toggleShowOnlyEnum: () => this._toggleShowVariablesOnlyEnum(),
      toggleShowVariablesFilterBox: () => this._toggleShowVariablesFilterBox(),
      toggleShowOriginalSource: () => this._toggleShowOriginalSource(),
      toggleAutoBlackBox: () => this._toggleAutoBlackBox()
    });
  },

  /**
   * Listener handling the 'gear menu' popup showing event.
   */
  _onPopupShowing: function () {
    this._button.setAttribute("open", "true");
    window.emit(EVENTS.OPTIONS_POPUP_SHOWING);
  },

  /**
   * Listener handling the 'gear menu' popup hiding event.
   */
  _onPopupHiding: function () {
    this._button.removeAttribute("open");
  },

  /**
   * Listener handling the 'gear menu' popup hidden event.
   */
  _onPopupHidden: function () {
    window.emit(EVENTS.OPTIONS_POPUP_HIDDEN);
  },

  /**
   * Listener handling the 'auto pretty print' menuitem command.
   */
  _toggleAutoPrettyPrint: function () {
    Prefs.autoPrettyPrint =
      this._autoPrettyPrint.getAttribute("checked") == "true";
  },

  /**
   * Listener handling the 'pause on exceptions' menuitem command.
   */
  _togglePauseOnExceptions: function () {
    Prefs.pauseOnExceptions =
      this._pauseOnExceptionsItem.getAttribute("checked") == "true";

    this.DebuggerController.activeThread.pauseOnExceptions(
      Prefs.pauseOnExceptions,
      Prefs.ignoreCaughtExceptions);
  },

  _toggleIgnoreCaughtExceptions: function () {
    Prefs.ignoreCaughtExceptions =
      this._ignoreCaughtExceptionsItem.getAttribute("checked") == "true";

    this.DebuggerController.activeThread.pauseOnExceptions(
      Prefs.pauseOnExceptions,
      Prefs.ignoreCaughtExceptions);
  },

  /**
   * Listener handling the 'show panes on startup' menuitem command.
   */
  _toggleShowPanesOnStartup: function () {
    Prefs.panesVisibleOnStartup =
      this._showPanesOnStartupItem.getAttribute("checked") == "true";
  },

  /**
   * Listener handling the 'show non-enumerables' menuitem command.
   */
  _toggleShowVariablesOnlyEnum: function () {
    let pref = Prefs.variablesOnlyEnumVisible =
      this._showVariablesOnlyEnumItem.getAttribute("checked") == "true";

    this.DebuggerView.Variables.onlyEnumVisible = pref;
  },

  /**
   * Listener handling the 'show variables searchbox' menuitem command.
   */
  _toggleShowVariablesFilterBox: function () {
    let pref = Prefs.variablesSearchboxVisible =
      this._showVariablesFilterBoxItem.getAttribute("checked") == "true";

    this.DebuggerView.Variables.searchEnabled = pref;
  },

  /**
   * Listener handling the 'show original source' menuitem command.
   */
  _toggleShowOriginalSource: function () {
    let pref = Prefs.sourceMapsEnabled =
      this._showOriginalSourceItem.getAttribute("checked") == "true";

    // Don't block the UI while reconfiguring the server.
    window.once(EVENTS.OPTIONS_POPUP_HIDDEN, () => {
      // The popup panel needs more time to hide after triggering onpopuphidden.
      window.setTimeout(() => {
        this.DebuggerController.reconfigureThread({
          useSourceMaps: pref,
          autoBlackBox: Prefs.autoBlackBox
        });
      }, POPUP_HIDDEN_DELAY);
    });
  },

  /**
   * Listener handling the 'automatically black box minified sources' menuitem
   * command.
   */
  _toggleAutoBlackBox: function () {
    let pref = Prefs.autoBlackBox =
      this._autoBlackBoxItem.getAttribute("checked") == "true";

    // Don't block the UI while reconfiguring the server.
    window.once(EVENTS.OPTIONS_POPUP_HIDDEN, () => {
      // The popup panel needs more time to hide after triggering onpopuphidden.
      window.setTimeout(() => {
        this.DebuggerController.reconfigureThread({
          useSourceMaps: Prefs.sourceMapsEnabled,
          autoBlackBox: pref
        });
      }, POPUP_HIDDEN_DELAY);
    });
  },

  _button: null,
  _pauseOnExceptionsItem: null,
  _showPanesOnStartupItem: null,
  _showVariablesOnlyEnumItem: null,
  _showVariablesFilterBoxItem: null,
  _showOriginalSourceItem: null,
  _autoBlackBoxItem: null
};

DebuggerView.Options = new OptionsView(DebuggerController, DebuggerView);
PK
!<��ICchrome/devtools/content/debugger/views/stack-frames-classic-view.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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/. */
/* import-globals-from ../debugger-controller.js */
/* import-globals-from ../debugger-view.js */
/* import-globals-from ../utils.js */
/* globals document */
"use strict";

/*
 * Functions handling the stackframes classic list UI.
 * Controlled by the DebuggerView.StackFrames isntance.
 */
function StackFramesClassicListView(DebuggerController, DebuggerView) {
  dumpn("StackFramesClassicListView was instantiated");

  this.DebuggerView = DebuggerView;
  this._onSelect = this._onSelect.bind(this);
}

StackFramesClassicListView.prototype = extend(WidgetMethods, {
  /**
   * Initialization function, called when the debugger is started.
   */
  initialize: function () {
    dumpn("Initializing the StackFramesClassicListView");

    this.widget = new SideMenuWidget(document.getElementById("callstack-list"));
    this.widget.addEventListener("select", this._onSelect);

    this.emptyText = L10N.getStr("noStackFramesText");
    this.autoFocusOnFirstItem = false;
    this.autoFocusOnSelection = false;

    // This view's contents are also mirrored in a different container.
    this._mirror = this.DebuggerView.StackFrames;
  },

  /**
   * Destruction function, called when the debugger is closed.
   */
  destroy: function () {
    dumpn("Destroying the StackFramesClassicListView");

    this.widget.removeEventListener("select", this._onSelect);
  },

  /**
   * Adds a frame in this stackframes container.
   *
   * @param string aTitle
   *        The frame title (function name).
   * @param string aUrl
   *        The frame source url.
   * @param string aLine
   *        The frame line number.
   * @param number aDepth
   *        The frame depth in the stack.
   */
  addFrame: function (aTitle, aUrl, aLine, aDepth) {
    // Create the element node for the stack frame item.
    let frameView = this._createFrameView.apply(this, arguments);

    // Append a stack frame item to this container.
    this.push([frameView], {
      attachment: {
        depth: aDepth
      }
    });
  },

  /**
   * Customization function for creating an item's UI.
   *
   * @param string aTitle
   *        The frame title to be displayed in the list.
   * @param string aUrl
   *        The frame source url.
   * @param string aLine
   *        The frame line number.
   * @param number aDepth
   *        The frame depth in the stack.
   * @return nsIDOMNode
   *         The stack frame view.
   */
  _createFrameView: function (aTitle, aUrl, aLine, aDepth) {
    let container = document.createElement("hbox");
    container.id = "classic-stackframe-" + aDepth;
    container.className = "dbg-classic-stackframe";
    container.setAttribute("flex", "1");

    let frameTitleNode = document.createElement("label");
    frameTitleNode.className = "plain dbg-classic-stackframe-title";
    frameTitleNode.setAttribute("value", aTitle);
    frameTitleNode.setAttribute("crop", "center");

    let frameDetailsNode = document.createElement("hbox");
    frameDetailsNode.className = "plain dbg-classic-stackframe-details";

    let frameUrlNode = document.createElement("label");
    frameUrlNode.className = "plain dbg-classic-stackframe-details-url";
    frameUrlNode.setAttribute("value", SourceUtils.getSourceLabel(aUrl));
    frameUrlNode.setAttribute("crop", "center");
    frameDetailsNode.appendChild(frameUrlNode);

    let frameDetailsSeparator = document.createElement("label");
    frameDetailsSeparator.className = "plain dbg-classic-stackframe-details-sep";
    frameDetailsSeparator.setAttribute("value", SEARCH_LINE_FLAG);
    frameDetailsNode.appendChild(frameDetailsSeparator);

    let frameLineNode = document.createElement("label");
    frameLineNode.className = "plain dbg-classic-stackframe-details-line";
    frameLineNode.setAttribute("value", aLine);
    frameDetailsNode.appendChild(frameLineNode);

    container.appendChild(frameTitleNode);
    container.appendChild(frameDetailsNode);

    return container;
  },

  /**
   * The select listener for the stackframes container.
   */
  _onSelect: function (e) {
    let stackframeItem = this.selectedItem;
    if (stackframeItem) {
      // The container is not empty and an actual item was selected.
      // Mirror the selected item in the breadcrumbs list.
      let depth = stackframeItem.attachment.depth;
      this._mirror.selectedItem = e => e.attachment.depth == depth;
    }
  },

  _mirror: null
});

DebuggerView.StackFramesClassicList = new StackFramesClassicListView(DebuggerController,
                                                                     DebuggerView);
PK
!<�l�$�$�$;chrome/devtools/content/debugger/views/stack-frames-view.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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/. */
/* import-globals-from ../debugger-controller.js */
/* import-globals-from ../debugger-view.js */
/* import-globals-from ../utils.js */
/* globals document, window */
"use strict";

/**
 * Functions handling the stackframes UI.
 */
function StackFramesView(DebuggerController, DebuggerView) {
  dumpn("StackFramesView was instantiated");

  this.StackFrames = DebuggerController.StackFrames;
  this.DebuggerView = DebuggerView;

  this._onStackframeRemoved = this._onStackframeRemoved.bind(this);
  this._onSelect = this._onSelect.bind(this);
  this._onScroll = this._onScroll.bind(this);
  this._afterScroll = this._afterScroll.bind(this);
  this._getStackAsString = this._getStackAsString.bind(this);
}

StackFramesView.prototype = extend(WidgetMethods, {
  /**
   * Initialization function, called when the debugger is started.
   */
  initialize: function () {
    dumpn("Initializing the StackFramesView");

    this._popupset = document.getElementById("debuggerPopupset");

    this.widget = new BreadcrumbsWidget(document.getElementById("stackframes"));
    this.widget.addEventListener("select", this._onSelect);
    this.widget.addEventListener("scroll", this._onScroll, true);
    this.widget.setAttribute("context", "stackFramesContextMenu");
    window.addEventListener("resize", this._onScroll, true);

    this.autoFocusOnFirstItem = false;
    this.autoFocusOnSelection = false;

    // This view's contents are also mirrored in a different container.
    this._mirror = this.DebuggerView.StackFramesClassicList;
  },

  /**
   * Destruction function, called when the debugger is closed.
   */
  destroy: function () {
    dumpn("Destroying the StackFramesView");

    this.widget.removeEventListener("select", this._onSelect);
    this.widget.removeEventListener("scroll", this._onScroll, true);
    window.removeEventListener("resize", this._onScroll, true);
  },

  /**
   * Adds a frame in this stackframes container.
   *
   * @param string aTitle
   *        The frame title (function name).
   * @param string aUrl
   *        The frame source url.
   * @param string aLine
   *        The frame line number.
   * @param number aDepth
   *        The frame depth in the stack.
   * @param boolean aIsBlackBoxed
   *        Whether or not the frame is black boxed.
   */
  addFrame: function (aFrame, aLine, aColumn, aDepth, aIsBlackBoxed) {
    let { source } = aFrame;

    // The source may not exist in the source listing yet because it's
    // an unnamed eval source, which we hide, so we need to add it
    if (!DebuggerView.Sources.getItemByValue(source.actor)) {
      DebuggerView.Sources.addSource(source, { force: true });
    }

    let location = DebuggerView.Sources.getDisplayURL(source);
    let title = StackFrameUtils.getFrameTitle(aFrame);

    // Blackboxed stack frames are collapsed into a single entry in
    // the view. By convention, only the first frame is displayed.
    if (aIsBlackBoxed) {
      if (this._prevBlackBoxedUrl == location) {
        return;
      }
      this._prevBlackBoxedUrl = location;
    } else {
      this._prevBlackBoxedUrl = null;
    }

    // Create the element node for the stack frame item.
    let frameView = this._createFrameView(
      title, location, aLine, aDepth, aIsBlackBoxed
    );

    // Append a stack frame item to this container.
    this.push([frameView], {
      index: 0, /* specifies on which position should the item be appended */
      attachment: {
        title: title,
        url: location,
        line: aLine,
        depth: aDepth,
        column: aColumn
      },
      // Make sure that when the stack frame item is removed, the corresponding
      // mirrored item in the classic list is also removed.
      finalize: this._onStackframeRemoved
    });

    // Mirror this newly inserted item inside the "Call Stack" tab.
    this._mirror.addFrame(title, location, aLine, aDepth);
  },

  _getStackAsString: function () {
    return [...this].map(frameItem => {
      const { attachment: { title, url, line, column }} = frameItem;
      return title + "@" + url + ":" + line + ":" + column;
    }).join("\n");
  },

  addCopyContextMenu: function () {
    let menupopup = document.createElement("menupopup");
    let menuitem = document.createElement("menuitem");

    menupopup.id = "stackFramesContextMenu";
    menuitem.id = "copyStackMenuItem";

    menuitem.setAttribute("label", "Copy");
    menuitem.addEventListener("command", () => {
      let stack = this._getStackAsString();
      clipboardHelper.copyString(stack);
    });
    menupopup.appendChild(menuitem);
    this._popupset.appendChild(menupopup);
  },

  /**
   * Selects the frame at the specified depth in this container.
   * @param number aDepth
   */
  set selectedDepth(aDepth) {
    this.selectedItem = aItem => aItem.attachment.depth == aDepth;
  },

  /**
   * Gets the currently selected stack frame's depth in this container.
   * This will essentially be the opposite of |selectedIndex|, which deals
   * with the position in the view, where the last item added is actually
   * the bottommost, not topmost.
   * @return number
   */
  get selectedDepth() {
    return this.selectedItem.attachment.depth;
  },

  /**
   * Specifies if the active thread has more frames that need to be loaded.
   */
  dirty: false,

  /**
   * Customization function for creating an item's UI.
   *
   * @param string aTitle
   *        The frame title to be displayed in the list.
   * @param string aUrl
   *        The frame source url.
   * @param string aLine
   *        The frame line number.
   * @param number aDepth
   *        The frame depth in the stack.
   * @param boolean aIsBlackBoxed
   *        Whether or not the frame is black boxed.
   * @return nsIDOMNode
   *         The stack frame view.
   */
  _createFrameView: function (aTitle, aUrl, aLine, aDepth, aIsBlackBoxed) {
    let container = document.createElement("hbox");
    container.id = "stackframe-" + aDepth;
    container.className = "dbg-stackframe";

    let frameDetails = SourceUtils.trimUrlLength(
      SourceUtils.getSourceLabel(aUrl),
      STACK_FRAMES_SOURCE_URL_MAX_LENGTH,
      STACK_FRAMES_SOURCE_URL_TRIM_SECTION);

    if (aIsBlackBoxed) {
      container.classList.add("dbg-stackframe-black-boxed");
    } else {
      let frameTitleNode = document.createElement("label");
      frameTitleNode.className = "plain dbg-stackframe-title breadcrumbs-widget-item-tag";
      frameTitleNode.setAttribute("value", aTitle);
      container.appendChild(frameTitleNode);

      frameDetails += SEARCH_LINE_FLAG + aLine;
    }

    let frameDetailsNode = document.createElement("label");
    frameDetailsNode.className = "plain dbg-stackframe-details breadcrumbs-widget-item-id";
    frameDetailsNode.setAttribute("value", frameDetails);
    container.appendChild(frameDetailsNode);

    return container;
  },

  /**
   * Function called each time a stack frame item is removed.
   *
   * @param object aItem
   *        The corresponding item.
   */
  _onStackframeRemoved: function (aItem) {
    dumpn("Finalizing stackframe item: " + aItem.stringify());

    // Remove the mirrored item in the classic list.
    let depth = aItem.attachment.depth;
    this._mirror.remove(this._mirror.getItemForAttachment(e => e.depth == depth));

    // Forget the previously blackboxed stack frame url.
    this._prevBlackBoxedUrl = null;
  },

  /**
   * The select listener for the stackframes container.
   */
  _onSelect: function (e) {
    let stackframeItem = this.selectedItem;
    if (stackframeItem) {
      // The container is not empty and an actual item was selected.
      let depth = stackframeItem.attachment.depth;

      // Mirror the selected item in the classic list.
      this.suppressSelectionEvents = true;
      this._mirror.selectedItem = e => e.attachment.depth == depth;
      this.suppressSelectionEvents = false;

      DebuggerController.StackFrames.selectFrame(depth);
    }
  },

  /**
   * The scroll listener for the stackframes container.
   */
  _onScroll: function () {
    // Update the stackframes container only if we have to.
    if (!this.dirty) {
      return;
    }
    // Allow requests to settle down first.
    setNamedTimeout("stack-scroll", STACK_FRAMES_SCROLL_DELAY, this._afterScroll);
  },

  /**
   * Requests the addition of more frames from the controller.
   */
  _afterScroll: function () {
    let scrollPosition = this.widget.getAttribute("scrollPosition");
    let scrollWidth = this.widget.getAttribute("scrollWidth");

    // If the stackframes container scrolled almost to the end, with only
    // 1/10 of a breadcrumb remaining, load more content.
    if (scrollPosition - scrollWidth / 10 < 1) {
      this.ensureIndexIsVisible(CALL_STACK_PAGE_SIZE - 1);
      this.dirty = false;

      // Loads more stack frames from the debugger server cache.
      DebuggerController.StackFrames.addMoreFrames();
    }
  },

  _mirror: null,
  _prevBlackBoxedUrl: null
});

DebuggerView.StackFrames = new StackFramesView(DebuggerController, DebuggerView);
PK
!<�Rs((6chrome/devtools/content/debugger/views/toolbar-view.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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/. */
/* import-globals-from ../debugger-controller.js */
/* import-globals-from ../debugger-view.js */
/* import-globals-from ../utils.js */
/* globals document */
"use strict";

/**
 * Functions handling the toolbar view: close button, expand/collapse button,
 * pause/resume and stepping buttons etc.
 */
function ToolbarView(DebuggerController, DebuggerView) {
  dumpn("ToolbarView was instantiated");

  this.StackFrames = DebuggerController.StackFrames;
  this.ThreadState = DebuggerController.ThreadState;
  this.DebuggerController = DebuggerController;
  this.DebuggerView = DebuggerView;

  this._onTogglePanesActivated = this._onTogglePanesActivated.bind(this);
  this._onTogglePanesPressed = this._onTogglePanesPressed.bind(this);
  this._onResumePressed = this._onResumePressed.bind(this);
  this._onStepOverPressed = this._onStepOverPressed.bind(this);
  this._onStepInPressed = this._onStepInPressed.bind(this);
  this._onStepOutPressed = this._onStepOutPressed.bind(this);
}

ToolbarView.prototype = {
  get activeThread() {
    return this.DebuggerController.activeThread;
  },

  get resumptionWarnFunc() {
    return this.DebuggerController._ensureResumptionOrder;
  },

  /**
   * Initialization function, called when the debugger is started.
   */
  initialize: function () {
    dumpn("Initializing the ToolbarView");

    this._instrumentsPaneToggleButton = document.getElementById("instruments-pane-toggle");
    this._resumeButton = document.getElementById("resume");
    this._stepOverButton = document.getElementById("step-over");
    this._stepInButton = document.getElementById("step-in");
    this._stepOutButton = document.getElementById("step-out");
    this._resumeOrderTooltip = new Tooltip(document);
    this._resumeOrderTooltip.defaultPosition = TOOLBAR_ORDER_POPUP_POSITION;

    let resumeKey = ShortcutUtils.prettifyShortcut(document.getElementById("resumeKey"));
    let stepOverKey = ShortcutUtils.prettifyShortcut(document.getElementById("stepOverKey"));
    let stepInKey = ShortcutUtils.prettifyShortcut(document.getElementById("stepInKey"));
    let stepOutKey = ShortcutUtils.prettifyShortcut(document.getElementById("stepOutKey"));
    this._resumeTooltip = L10N.getFormatStr("resumeButtonTooltip", resumeKey);
    this._pauseTooltip = L10N.getFormatStr("pauseButtonTooltip", resumeKey);
    this._pausePendingTooltip = L10N.getStr("pausePendingButtonTooltip");
    this._stepOverTooltip = L10N.getFormatStr("stepOverTooltip", stepOverKey);
    this._stepInTooltip = L10N.getFormatStr("stepInTooltip", stepInKey);
    this._stepOutTooltip = L10N.getFormatStr("stepOutTooltip", stepOutKey);

    this._instrumentsPaneToggleButton.addEventListener("mousedown",
      this._onTogglePanesActivated);
    this._instrumentsPaneToggleButton.addEventListener("keydown",
      this._onTogglePanesPressed);
    this._resumeButton.addEventListener("mousedown", this._onResumePressed);
    this._stepOverButton.addEventListener("mousedown", this._onStepOverPressed);
    this._stepInButton.addEventListener("mousedown", this._onStepInPressed);
    this._stepOutButton.addEventListener("mousedown", this._onStepOutPressed);

    this._stepOverButton.setAttribute("tooltiptext", this._stepOverTooltip);
    this._stepInButton.setAttribute("tooltiptext", this._stepInTooltip);
    this._stepOutButton.setAttribute("tooltiptext", this._stepOutTooltip);
    this._toggleButtonsState({ enabled: false });

    this._addCommands();
  },

  /**
   * Destruction function, called when the debugger is closed.
   */
  destroy: function () {
    dumpn("Destroying the ToolbarView");

    this._instrumentsPaneToggleButton.removeEventListener("mousedown",
      this._onTogglePanesActivated);
    this._instrumentsPaneToggleButton.removeEventListener("keydown",
      this._onTogglePanesPressed);
    this._resumeButton.removeEventListener("mousedown", this._onResumePressed);
    this._stepOverButton.removeEventListener("mousedown", this._onStepOverPressed);
    this._stepInButton.removeEventListener("mousedown", this._onStepInPressed);
    this._stepOutButton.removeEventListener("mousedown", this._onStepOutPressed);
  },

  /**
   * Add commands that XUL can fire.
   */
  _addCommands: function () {
    XULUtils.addCommands(document.getElementById("debuggerCommands"), {
      resumeCommand: this.getCommandHandler("resumeCommand"),
      stepOverCommand: this.getCommandHandler("stepOverCommand"),
      stepInCommand: this.getCommandHandler("stepInCommand"),
      stepOutCommand: this.getCommandHandler("stepOutCommand")
    });
  },

  /**
   * Retrieve the callback associated with the provided  debugger command.
   *
   * @param {String} command
   *        The debugger command id.
   * @return {Function} the corresponding callback.
   */
  getCommandHandler: function (command) {
    switch (command) {
      case "resumeCommand":
        return () => this._onResumePressed();
      case "stepOverCommand":
        return () => this._onStepOverPressed();
      case "stepInCommand":
        return () => this._onStepInPressed();
      case "stepOutCommand":
        return () => this._onStepOutPressed();
      default:
        return () => {};
    }
  },

  /**
   * Display a warning when trying to resume a debuggee while another is paused.
   * Debuggees must be unpaused in a Last-In-First-Out order.
   *
   * @param string aPausedUrl
   *        The URL of the last paused debuggee.
   */
  showResumeWarning: function (aPausedUrl) {
    let label = L10N.getFormatStr("resumptionOrderPanelTitle", aPausedUrl);
    let defaultStyle = "default-tooltip-simple-text-colors";
    this._resumeOrderTooltip.setTextContent({ messages: [label] });
    this._resumeOrderTooltip.show(this._resumeButton);
  },

  /**
   * Sets the resume button state based on the debugger active thread.
   *
   * @param string aState
   *        Either "paused", "attached", or "breakOnNext".
   * @param boolean hasLocation
   *        True if we are paused at a specific JS location
   */
  toggleResumeButtonState: function (aState, hasLocation) {
    // Intermidiate state after pressing the pause button and waiting
    // for the next script execution to happen.
    if (aState == "breakOnNext") {
      this._resumeButton.setAttribute("break-on-next", "true");
      this._resumeButton.disabled = true;
      this._resumeButton.setAttribute("tooltiptext", this._pausePendingTooltip);
      return;
    }

    this._resumeButton.removeAttribute("break-on-next");
    this._resumeButton.disabled = false;

    // If we're paused, check and show a resume label on the button.
    if (aState == "paused") {
      this._resumeButton.setAttribute("checked", "true");
      this._resumeButton.setAttribute("tooltiptext", this._resumeTooltip);

      // Only enable the stepping buttons if we are paused at a
      // specific location. After bug 789430, we'll always be paused
      // at a location, but currently you can pause the entire engine
      // at any point without knowing the location.
      if (hasLocation) {
        this._toggleButtonsState({ enabled: true });
      }
    }
    // If we're attached, do the opposite.
    else if (aState == "attached") {
      this._resumeButton.removeAttribute("checked");
      this._resumeButton.setAttribute("tooltiptext", this._pauseTooltip);
      this._toggleButtonsState({ enabled: false });
    }
  },

  _toggleButtonsState: function ({ enabled }) {
    const buttons = [
      this._stepOutButton,
      this._stepInButton,
      this._stepOverButton
    ];
    for (let button of buttons) {
      button.disabled = !enabled;
    }
  },

  /**
  * Listener handling the toggle button space and return key event.
  */
  _onTogglePanesPressed: function (event) {
    if (ViewHelpers.isSpaceOrReturn(event)) {
      this._onTogglePanesActivated();
    }
  },

  /**
   * Listener handling the toggle button click event.
   */
  _onTogglePanesActivated: function() {
    DebuggerView.toggleInstrumentsPane({
      visible: DebuggerView.instrumentsPaneHidden,
      animated: true,
      delayed: true
    });
  },

  /**
   * Listener handling the pause/resume button click event.
   */
  _onResumePressed: function () {
    if (this.StackFrames._currentFrameDescription != FRAME_TYPE.NORMAL ||
        this._resumeButton.disabled) {
      return;
    }

    if (this.activeThread.paused) {
      this.StackFrames.currentFrameDepth = -1;
      this.activeThread.resume(this.resumptionWarnFunc);
    } else {
      this.ThreadState.interruptedByResumeButton = true;
      this.toggleResumeButtonState("breakOnNext");
      this.activeThread.breakOnNext();
    }
  },

  /**
   * Listener handling the step over button click event.
   */
  _onStepOverPressed: function () {
    if (this.activeThread.paused && !this._stepOverButton.disabled) {
      this.StackFrames.currentFrameDepth = -1;
      this.activeThread.stepOver(this.resumptionWarnFunc);
    }
  },

  /**
   * Listener handling the step in button click event.
   */
  _onStepInPressed: function () {
    if (this.StackFrames._currentFrameDescription != FRAME_TYPE.NORMAL ||
       this._stepInButton.disabled) {
      return;
    }

    if (this.activeThread.paused) {
      this.StackFrames.currentFrameDepth = -1;
      this.activeThread.stepIn(this.resumptionWarnFunc);
    }
  },

  /**
   * Listener handling the step out button click event.
   */
  _onStepOutPressed: function () {
    if (this.activeThread.paused && !this._stepOutButton.disabled) {
      this.StackFrames.currentFrameDepth = -1;
      this.activeThread.stepOut(this.resumptionWarnFunc);
    }
  },

  _instrumentsPaneToggleButton: null,
  _resumeButton: null,
  _stepOverButton: null,
  _stepInButton: null,
  _stepOutButton: null,
  _resumeOrderTooltip: null,
  _resumeTooltip: "",
  _pauseTooltip: "",
  _stepOverTooltip: "",
  _stepInTooltip: "",
  _stepOutTooltip: ""
};

DebuggerView.Toolbar = new ToolbarView(DebuggerController, DebuggerView);
PK
!<�����+�+>chrome/devtools/content/debugger/views/variable-bubble-view.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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/. */
/* import-globals-from ../debugger-controller.js */
/* import-globals-from ../debugger-view.js */
/* import-globals-from ../utils.js */
/* globals document, window */
"use strict";

const {setTooltipVariableContent} = require("devtools/client/shared/widgets/tooltip/VariableContentHelper");

/**
 * Functions handling the variables bubble UI.
 */
function VariableBubbleView(DebuggerController, DebuggerView) {
  dumpn("VariableBubbleView was instantiated");

  this.StackFrames = DebuggerController.StackFrames;
  this.Parser = DebuggerController.Parser;
  this.DebuggerView = DebuggerView;

  this._onMouseMove = this._onMouseMove.bind(this);
  this._onMouseOut = this._onMouseOut.bind(this);
  this._onPopupHiding = this._onPopupHiding.bind(this);
}

VariableBubbleView.prototype = {
  /**
   * Delay before showing the variables bubble tooltip when hovering a valid
   * target.
   */
  TOOLTIP_SHOW_DELAY: 750,

  /**
   * Tooltip position for the variables bubble tooltip.
   */
  TOOLTIP_POSITION: "topcenter bottomleft",

  /**
   * Initialization function, called when the debugger is started.
   */
  initialize: function () {
    dumpn("Initializing the VariableBubbleView");

    this._toolbox = DebuggerController._toolbox;
    this._editorContainer = document.getElementById("editor");
    this._editorContainer.addEventListener("mousemove", this._onMouseMove);
    this._editorContainer.addEventListener("mouseout", this._onMouseOut);

    this._tooltip = new Tooltip(document, {
      closeOnEvents: [{
        emitter: this._toolbox,
        event: "select"
      }, {
        emitter: this._editorContainer,
        event: "scroll",
        useCapture: true
      }, {
        emitter: document,
        event: "keydown"
      }]
    });
    this._tooltip.defaultPosition = this.TOOLTIP_POSITION;
    this._tooltip.panel.addEventListener("popuphiding", this._onPopupHiding);
  },

  /**
   * Destruction function, called when the debugger is closed.
   */
  destroy: function () {
    dumpn("Destroying the VariableBubbleView");

    this._tooltip.panel.removeEventListener("popuphiding", this._onPopupHiding);
    this._editorContainer.removeEventListener("mousemove", this._onMouseMove);
    this._editorContainer.removeEventListener("mouseout", this._onMouseOut);
  },

  /**
   * Specifies whether literals can be (redundantly) inspected in a popup.
   * This behavior is deprecated, but still tested in a few places.
   */
  _ignoreLiterals: true,

  /**
   * Searches for an identifier underneath the specified position in the
   * source editor, and if found, opens a VariablesView inspection popup.
   *
   * @param number x, y
   *        The left/top coordinates where to look for an identifier.
   */
  _findIdentifier: function (x, y) {
    let editor = this.DebuggerView.editor;

    // Calculate the editor's line and column at the current x and y coords.
    let hoveredPos = editor.getPositionFromCoords({ left: x, top: y });
    let hoveredOffset = editor.getOffset(hoveredPos);
    let hoveredLine = hoveredPos.line;
    let hoveredColumn = hoveredPos.ch;

    // A source contains multiple scripts. Find the start index of the script
    // containing the specified offset relative to its parent source.
    let contents = editor.getText();
    let location = this.DebuggerView.Sources.selectedValue;
    let parsedSource = this.Parser.get(contents, location);
    let scriptInfo = parsedSource.getScriptInfo(hoveredOffset);

    // If the script length is negative, we're not hovering JS source code.
    if (scriptInfo.length == -1) {
      return;
    }

    // Using the script offset, determine the actual line and column inside the
    // script, to use when finding identifiers.
    let scriptStart = editor.getPosition(scriptInfo.start);
    let scriptLineOffset = scriptStart.line;
    let scriptColumnOffset = (hoveredLine == scriptStart.line ? scriptStart.ch : 0);

    let scriptLine = hoveredLine - scriptLineOffset;
    let scriptColumn = hoveredColumn - scriptColumnOffset;
    let identifierInfo = parsedSource.getIdentifierAt({
      line: scriptLine + 1,
      column: scriptColumn,
      scriptIndex: scriptInfo.index,
      ignoreLiterals: this._ignoreLiterals
    });

    // If the info is null, we're not hovering any identifier.
    if (!identifierInfo) {
      return;
    }

    // Transform the line and column relative to the parsed script back
    // to the context of the parent source.
    let { start: identifierStart, end: identifierEnd } = identifierInfo.location;
    let identifierCoords = {
      line: identifierStart.line + scriptLineOffset,
      column: identifierStart.column + scriptColumnOffset,
      length: identifierEnd.column - identifierStart.column
    };

    // Evaluate the identifier in the current stack frame and show the
    // results in a VariablesView inspection popup.
    this.StackFrames.evaluate(identifierInfo.evalString)
      .then(frameFinished => {
        if ("return" in frameFinished) {
          this.showContents({
            coords: identifierCoords,
            evalPrefix: identifierInfo.evalString,
            objectActor: frameFinished.return
          });
        } else {
          let msg = "Evaluation has thrown for: " + identifierInfo.evalString;
          console.warn(msg);
          dumpn(msg);
        }
      })
      .catch(err => {
        let msg = "Couldn't evaluate: " + err.message;
        console.error(msg);
        dumpn(msg);
      });
  },

  /**
   * Shows an inspection popup for a specified object actor grip.
   *
   * @param string object
   *        An object containing the following properties:
   *          - coords: the inspected identifier coordinates in the editor,
   *                    containing the { line, column, length } properties.
   *          - evalPrefix: a prefix for the variables view evaluation macros.
   *          - objectActor: the value grip for the object actor.
   */
  showContents: function ({ coords, evalPrefix, objectActor }) {
    let editor = this.DebuggerView.editor;
    let { line, column, length } = coords;

    // Highlight the function found at the mouse position.
    this._markedText = editor.markText(
      { line: line - 1, ch: column },
      { line: line - 1, ch: column + length });

    // If the grip represents a primitive value, use a more lightweight
    // machinery to display it.
    if (VariablesView.isPrimitive({ value: objectActor })) {
      let className = VariablesView.getClass(objectActor);
      let textContent = VariablesView.getString(objectActor);
      this._tooltip.setTextContent({
        messages: [textContent],
        messagesClass: className,
        containerClass: "plain"
      }, [{
        label: L10N.getStr("addWatchExpressionButton"),
        className: "dbg-expression-button",
        command: () => {
          this.DebuggerView.VariableBubble.hideContents();
          this.DebuggerView.WatchExpressions.addExpression(evalPrefix, true);
        }
      }]);
    } else {
      setTooltipVariableContent(this._tooltip, objectActor, {
        searchPlaceholder: L10N.getStr("emptyPropertiesFilterText"),
        searchEnabled: Prefs.variablesSearchboxVisible,
        eval: (variable, value) => {
          let string = variable.evaluationMacro(variable, value);
          this.StackFrames.evaluate(string);
          this.DebuggerView.VariableBubble.hideContents();
        }
      }, {
        getEnvironmentClient: aObject => gThreadClient.environment(aObject),
        getObjectClient: aObject => gThreadClient.pauseGrip(aObject),
        simpleValueEvalMacro: this._getSimpleValueEvalMacro(evalPrefix),
        getterOrSetterEvalMacro: this._getGetterOrSetterEvalMacro(evalPrefix),
        overrideValueEvalMacro: this._getOverrideValueEvalMacro(evalPrefix)
      }, {
        fetched: aType => {
          if (aType == "properties") {
            window.emit(EVENTS.FETCHED_BUBBLE_PROPERTIES);
          }
        }
      }, [{
        label: L10N.getStr("addWatchExpressionButton"),
        className: "dbg-expression-button",
        command: () => {
          this.DebuggerView.VariableBubble.hideContents();
          this.DebuggerView.WatchExpressions.addExpression(evalPrefix, true);
        }
      }], this._toolbox);
    }

    this._tooltip.show(this._markedText.anchor);
  },

  /**
   * Hides the inspection popup.
   */
  hideContents: function () {
    clearNamedTimeout("editor-mouse-move");
    this._tooltip.hide();
  },

  /**
   * Checks whether the inspection popup is shown.
   *
   * @return boolean
   *         True if the panel is shown or showing, false otherwise.
   */
  contentsShown: function () {
    return this._tooltip.isShown();
  },

  /**
   * Functions for getting customized variables view evaluation macros.
   *
   * @param string aPrefix
   *        See the corresponding VariablesView.* functions.
   */
  _getSimpleValueEvalMacro: function (aPrefix) {
    return (item, string) =>
      VariablesView.simpleValueEvalMacro(item, string, aPrefix);
  },
  _getGetterOrSetterEvalMacro: function (aPrefix) {
    return (item, string) =>
      VariablesView.getterOrSetterEvalMacro(item, string, aPrefix);
  },
  _getOverrideValueEvalMacro: function (aPrefix) {
    return (item, string) =>
      VariablesView.overrideValueEvalMacro(item, string, aPrefix);
  },

  /**
   * The mousemove listener for the source editor.
   */
  _onMouseMove: function (e) {
    // Prevent the variable inspection popup from showing when the thread client
    // is not paused, or while a popup is already visible, or when the user tries
    // to select text in the editor.
    let isResumed = gThreadClient && gThreadClient.state != "paused";
    let isSelecting = this.DebuggerView.editor.somethingSelected() && e.buttons > 0;
    let isPopupVisible = !this._tooltip.isHidden();
    if (isResumed || isSelecting || isPopupVisible) {
      clearNamedTimeout("editor-mouse-move");
      return;
    }
    // Allow events to settle down first. If the mouse hovers over
    // a certain point in the editor long enough, try showing a variable bubble.
    setNamedTimeout("editor-mouse-move",
      this.TOOLTIP_SHOW_DELAY, () => this._findIdentifier(e.clientX, e.clientY));
  },

  /**
   * The mouseout listener for the source editor container node.
   */
  _onMouseOut: function () {
    clearNamedTimeout("editor-mouse-move");
  },

  /**
   * Listener handling the popup hiding event.
   */
  _onPopupHiding: function ({ target }) {
    if (this._tooltip.panel != target) {
      return;
    }
    if (this._markedText) {
      this._markedText.clear();
      this._markedText = null;
    }
    if (!this._tooltip.isEmpty()) {
      this._tooltip.empty();
    }
  },

  _editorContainer: null,
  _markedText: null,
  _tooltip: null
};

DebuggerView.VariableBubble = new VariableBubbleView(DebuggerController, DebuggerView);
PK
!<�#�&�&@chrome/devtools/content/debugger/views/watch-expressions-view.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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/. */
/* import-globals-from ../debugger-controller.js */
/* import-globals-from ../debugger-view.js */
/* import-globals-from ../utils.js */
/* globals document */
"use strict";

const {KeyCodes} = require("devtools/client/shared/keycodes");

/**
 * Functions handling the watch expressions UI.
 */
function WatchExpressionsView(DebuggerController, DebuggerView) {
  dumpn("WatchExpressionsView was instantiated");

  this.StackFrames = DebuggerController.StackFrames;
  this.DebuggerView = DebuggerView;

  this.switchExpression = this.switchExpression.bind(this);
  this.deleteExpression = this.deleteExpression.bind(this);
  this._createItemView = this._createItemView.bind(this);
  this._onClick = this._onClick.bind(this);
  this._onClose = this._onClose.bind(this);
  this._onBlur = this._onBlur.bind(this);
  this._onKeyPress = this._onKeyPress.bind(this);
}

WatchExpressionsView.prototype = extend(WidgetMethods, {
  /**
   * Initialization function, called when the debugger is started.
   */
  initialize: function () {
    dumpn("Initializing the WatchExpressionsView");

    this.widget = new SimpleListWidget(document.getElementById("expressions"));
    this.widget.setAttribute("context", "debuggerWatchExpressionsContextMenu");
    this.widget.addEventListener("click", this._onClick);

    this.headerText = L10N.getStr("addWatchExpressionText");
    this._addCommands();
  },

  /**
   * Destruction function, called when the debugger is closed.
   */
  destroy: function () {
    dumpn("Destroying the WatchExpressionsView");

    this.widget.removeEventListener("click", this._onClick);
  },

  /**
   * Add commands that XUL can fire.
   */
  _addCommands: function () {
    XULUtils.addCommands(document.getElementById("debuggerCommands"), {
      addWatchExpressionCommand: () => this._onCmdAddExpression(),
      removeAllWatchExpressionsCommand: () => this._onCmdRemoveAllExpressions()
    });
  },

  /**
   * Adds a watch expression in this container.
   *
   * @param string aExpression [optional]
   *        An optional initial watch expression text.
   * @param boolean aSkipUserInput [optional]
   *        Pass true to avoid waiting for additional user input
   *        on the watch expression.
   */
  addExpression: function (aExpression = "", aSkipUserInput = false) {
    // Watch expressions are UI elements which benefit from visible panes.
    this.DebuggerView.showInstrumentsPane();

    // Create the element node for the watch expression item.
    let itemView = this._createItemView(aExpression);

    // Append a watch expression item to this container.
    let expressionItem = this.push([itemView.container], {
      index: 0, /* specifies on which position should the item be appended */
      attachment: {
        view: itemView,
        initialExpression: aExpression,
        currentExpression: "",
      }
    });

    // Automatically focus the new watch expression input
    // if additional user input is desired.
    if (!aSkipUserInput) {
      expressionItem.attachment.view.inputNode.select();
      expressionItem.attachment.view.inputNode.focus();
      this.DebuggerView.Variables.parentNode.scrollTop = 0;
    }
    // Otherwise, add and evaluate the new watch expression immediately.
    else {
      this.toggleContents(false);
      this._onBlur({ target: expressionItem.attachment.view.inputNode });
    }
  },

  /**
   * Changes the watch expression corresponding to the specified variable item.
   * This function is called whenever a watch expression's code is edited in
   * the variables view container.
   *
   * @param Variable aVar
   *        The variable representing the watch expression evaluation.
   * @param string aExpression
   *        The new watch expression text.
   */
  switchExpression: function (aVar, aExpression) {
    let expressionItem =
      [...this].filter(i => i.attachment.currentExpression == aVar.name)[0];

    // Remove the watch expression if it's going to be empty or a duplicate.
    if (!aExpression || this.getAllStrings().includes(aExpression)) {
      this.deleteExpression(aVar);
      return;
    }

    // Save the watch expression code string.
    expressionItem.attachment.currentExpression = aExpression;
    expressionItem.attachment.view.inputNode.value = aExpression;

    // Synchronize with the controller's watch expressions store.
    this.StackFrames.syncWatchExpressions();
  },

  /**
   * Removes the watch expression corresponding to the specified variable item.
   * This function is called whenever a watch expression's value is edited in
   * the variables view container.
   *
   * @param Variable aVar
   *        The variable representing the watch expression evaluation.
   */
  deleteExpression: function (aVar) {
    let expressionItem =
      [...this].filter(i => i.attachment.currentExpression == aVar.name)[0];

    // Remove the watch expression.
    this.remove(expressionItem);

    // Synchronize with the controller's watch expressions store.
    this.StackFrames.syncWatchExpressions();
  },

  /**
   * Gets the watch expression code string for an item in this container.
   *
   * @param number aIndex
   *        The index used to identify the watch expression.
   * @return string
   *         The watch expression code string.
   */
  getString: function (aIndex) {
    return this.getItemAtIndex(aIndex).attachment.currentExpression;
  },

  /**
   * Gets the watch expressions code strings for all items in this container.
   *
   * @return array
   *         The watch expressions code strings.
   */
  getAllStrings: function () {
    return this.items.map(e => e.attachment.currentExpression);
  },

  /**
   * Customization function for creating an item's UI.
   *
   * @param string aExpression
   *        The watch expression string.
   */
  _createItemView: function (aExpression) {
    let container = document.createElement("hbox");
    container.className = "list-widget-item dbg-expression";
    container.setAttribute("align", "center");

    let arrowNode = document.createElement("hbox");
    arrowNode.className = "dbg-expression-arrow";

    let inputNode = document.createElement("textbox");
    inputNode.className = "plain dbg-expression-input devtools-monospace";
    inputNode.setAttribute("value", aExpression);
    inputNode.setAttribute("flex", "1");

    let closeNode = document.createElement("toolbarbutton");
    closeNode.className = "plain variables-view-delete";

    closeNode.addEventListener("click", this._onClose);
    inputNode.addEventListener("blur", this._onBlur);
    inputNode.addEventListener("keypress", this._onKeyPress);

    container.appendChild(arrowNode);
    container.appendChild(inputNode);
    container.appendChild(closeNode);

    return {
      container: container,
      arrowNode: arrowNode,
      inputNode: inputNode,
      closeNode: closeNode
    };
  },

  /**
   * Called when the add watch expression key sequence was pressed.
   */
  _onCmdAddExpression: function (aText) {
    // Only add a new expression if there's no pending input.
    if (!this.getAllStrings().includes("")) {
      this.addExpression(aText || this.DebuggerView.editor.getSelection());
    }
  },

  /**
   * Called when the remove all watch expressions key sequence was pressed.
   */
  _onCmdRemoveAllExpressions: function () {
    // Empty the view of all the watch expressions and clear the cache.
    this.empty();

    // Synchronize with the controller's watch expressions store.
    this.StackFrames.syncWatchExpressions();
  },

  /**
   * The click listener for this container.
   */
  _onClick: function (e) {
    if (e.button != 0) {
      // Only allow left-click to trigger this event.
      return;
    }
    let expressionItem = this.getItemForElement(e.target);
    if (!expressionItem) {
      // The container is empty or we didn't click on an actual item.
      this.addExpression();
    }
  },

  /**
   * The click listener for a watch expression's close button.
   */
  _onClose: function (e) {
    // Remove the watch expression.
    this.remove(this.getItemForElement(e.target));

    // Synchronize with the controller's watch expressions store.
    this.StackFrames.syncWatchExpressions();

    // Prevent clicking the expression element itself.
    e.preventDefault();
    e.stopPropagation();
  },

  /**
   * The blur listener for a watch expression's textbox.
   */
  _onBlur: function ({ target: textbox }) {
    let expressionItem = this.getItemForElement(textbox);
    let oldExpression = expressionItem.attachment.currentExpression;
    let newExpression = textbox.value.trim();

    // Remove the watch expression if it's empty.
    if (!newExpression) {
      this.remove(expressionItem);
    }
    // Remove the watch expression if it's a duplicate.
    else if (!oldExpression && this.getAllStrings().includes(newExpression)) {
      this.remove(expressionItem);
    }
    // Expression is eligible.
    else {
      expressionItem.attachment.currentExpression = newExpression;
    }

    // Synchronize with the controller's watch expressions store.
    this.StackFrames.syncWatchExpressions();
  },

  /**
   * The keypress listener for a watch expression's textbox.
   */
  _onKeyPress: function (e) {
    switch (e.keyCode) {
      case KeyCodes.DOM_VK_RETURN:
      case KeyCodes.DOM_VK_ESCAPE:
        e.stopPropagation();
        this.DebuggerView.editor.focus();
    }
  }
});

DebuggerView.WatchExpressions = new WatchExpressionsView(DebuggerController,
                                                         DebuggerView);
PK
!<K�B��6chrome/devtools/content/debugger/views/workers-view.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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/. */
/* import-globals-from ../debugger-controller.js */
/* import-globals-from ../debugger-view.js */
/* import-globals-from ../utils.js */
/* globals document */
"use strict";

function WorkersView() {
  this._onWorkerSelect = this._onWorkerSelect.bind(this);
}

WorkersView.prototype = extend(WidgetMethods, {
  initialize: function () {
    if (!Prefs.workersEnabled) {
      return;
    }

    document.getElementById("workers-pane").removeAttribute("hidden");
    document.getElementById("workers-splitter").removeAttribute("hidden");

    this.widget = new SideMenuWidget(document.getElementById("workers"), {
      showArrows: true,
    });
    this.emptyText = L10N.getStr("noWorkersText");
    this.widget.addEventListener("select", this._onWorkerSelect);
  },

  addWorker: function (workerForm) {
    let element = document.createElement("label");
    element.className = "plain dbg-worker-item";
    element.setAttribute("value", workerForm.url);
    element.setAttribute("flex", "1");

    this.push([element, workerForm.actor], {
      attachment: workerForm
    });
  },

  removeWorker: function (workerForm) {
    this.remove(this.getItemByValue(workerForm.actor));
  },

  _onWorkerSelect: function () {
    if (this.selectedItem !== null) {
      DebuggerController.Workers._onWorkerSelect(this.selectedItem.attachment);
      this.selectedItem = null;
    }
  }
});

DebuggerView.Workers = new WorkersView();
PK
!<�/ii$chrome/devtools/content/dom/dom.html<?xml version="1.0" encoding="UTF-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/. -->
<!DOCTYPE html>
<html dir="">
<head>
  <meta charset="utf-8"/>

  <link href="resource://devtools/client/dom/content/dom-view.css" rel="stylesheet" />
  <link href="resource://devtools/client/jsonview/css/toolbar.css" rel="stylesheet" />
  <link href="resource://devtools/client/shared/components/tree/TreeView.css" rel="stylesheet" />

  <script type="text/javascript"
          src="chrome://devtools/content/shared/theme-switching.js"></script>
</head>
<body class="theme-body devtools-monospace" role="application">
  <div id="content"></div>
  <script type="text/javascript" src="./main.js"></script>
</body>
</html>
PK
!<�ō�#chrome/devtools/content/dom/main.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", {});
const { BrowserLoader } = ChromeUtils.import("resource://devtools/client/shared/browser-loader.js", {});

// Module Loader
const require = BrowserLoader({
  baseURI: "resource://devtools/client/dom/",
  window
}).require;

XPCOMUtils.defineConstant(this, "require", require);

// Localization
const { LocalizationHelper } = require("devtools/shared/l10n");
this.l10n = new LocalizationHelper("devtools/client/locales/dom.properties");

// Load DOM panel content
require("./content/dom-view.js");
PK
!<$33�5chrome/devtools/content/framework/connect/connect.css:root {
  font: caption;
}

html {
  background-color: #111;
}

body {
  font-family: Arial, sans-serif;
  color: white;
  max-width: 600px;
  margin: 30px auto 0;
  box-shadow: 0 2px 3px black;
  background-color: #3C3E40;
}

h1 {
  margin: 0;
  padding: 20px;
  background-color: rgba(0,0,0,0.12);
  background-image: radial-gradient(ellipse farthest-corner at center top , rgb(159, 223, 255), rgba(101, 203, 255, 0.3)), radial-gradient(ellipse farthest-side at center top , rgba(101, 203, 255, 0.4), rgba(101, 203, 255, 0));
  background-size: 100% 2px, 100% 5px;
  background-repeat: no-repeat;
  border-bottom: 1px solid rgba(0,0,0,0.1);
}

form {
  display: inline-block;
}

label {
  display: block;
  margin: 10px;
}

label > span {
  display: inline-block;
  min-width: 150px;
  text-align: end;
  margin-inline-end: 10px;
}

#submit {
  float: right;
}

#submit:dir(rtl) {
  float: left;
}

input {
  direction: ltr;
}

input:invalid {
  box-shadow: 0 0 2px 2px #F06;
}

section {
  min-height: 160px;
  margin: 60px 20px;
  display: none; /* By default, hidden */
}

.error-message {
  color: red;
}

.error-message:not(.active) {
  display: none;
}

body:not(.actors-mode):not(.connecting) > #connection-form {
  display: block;
}

body.actors-mode > #actors-list {
  display: block;
}

body.connecting > #connecting {
  display: block;
}

#connecting {
  text-align: center;
}

#connecting > p > img {
  vertical-align: top;
}

.actors {
  padding-left: 0;
}

.actors > a {
  display: block;
  margin: 5px;
  padding: 5px;
  color: white;
}

.remote-process {
  font-style: italic;
  opacity: 0.8;
}

footer {
  padding: 10px;
  background-color: rgba(0,0,0,0.12);
  border-top: 1px solid rgba(0,0,0,0.1);
  font-size: small;
}

footer > a,
footer > a:visited {
  color: white;
}
PK
!<1��^^4chrome/devtools/content/framework/connect/connect.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

var {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
var Services = require("Services");
var {gDevTools} = require("devtools/client/framework/devtools");
var {TargetFactory} = require("devtools/client/framework/target");
var {Toolbox} = require("devtools/client/framework/toolbox");
var {DebuggerClient} = require("devtools/shared/client/debugger-client");
var {Task} = require("devtools/shared/task");
var {LocalizationHelper} = require("devtools/shared/l10n");
var L10N = new LocalizationHelper("devtools/client/locales/connection-screen.properties");

var gClient;
var gConnectionTimeout;

/**
 * Once DOM is ready, we prefil the host/port inputs with
 * pref-stored values.
 */
window.addEventListener("DOMContentLoaded", function () {
  let host = Services.prefs.getCharPref("devtools.debugger.remote-host");
  let port = Services.prefs.getIntPref("devtools.debugger.remote-port");

  if (host) {
    document.getElementById("host").value = host;
  }

  if (port) {
    document.getElementById("port").value = port;
  }

  let form = document.querySelector("#connection-form form");
  form.addEventListener("submit", function () {
    window.submit().catch(e => {
      console.error(e);
      // Bug 921850: catch rare exception from DebuggerClient.socketConnect
      showError("unexpected");
    });
  });
}, {capture: true, once: true});

/**
 * Called when the "connect" button is clicked.
 */
var submit = Task.async(function* () {
  // Show the "connecting" screen
  document.body.classList.add("connecting");

  let host = document.getElementById("host").value;
  let port = document.getElementById("port").value;

  // Save the host/port values
  try {
    Services.prefs.setCharPref("devtools.debugger.remote-host", host);
    Services.prefs.setIntPref("devtools.debugger.remote-port", port);
  } catch (e) {
    // Fails in e10s mode, but not a critical feature.
  }

  // Initiate the connection
  let transport = yield DebuggerClient.socketConnect({ host, port });
  gClient = new DebuggerClient(transport);
  let delay = Services.prefs.getIntPref("devtools.debugger.remote-timeout");
  gConnectionTimeout = setTimeout(handleConnectionTimeout, delay);
  let response = yield gClient.connect();
  yield onConnectionReady(...response);
});

/**
 * Connection is ready. List actors and build buttons.
 */
var onConnectionReady = Task.async(function* ([aType, aTraits]) {
  clearTimeout(gConnectionTimeout);

  let addons = [];
  try {
    let response = yield gClient.listAddons();
    if (!response.error && response.addons.length > 0) {
      addons = response.addons;
    }
  } catch(e) {
    // listAddons throws if the runtime doesn't support addons
  }

  let parent = document.getElementById("addonActors");
  if (addons.length > 0) {
    // Add one entry for each add-on.
    for (let addon of addons) {
      if (!addon.debuggable) {
        continue;
      }
      buildAddonLink(addon, parent);
    }
  }
  else {
    // Hide the section when there are no add-ons
    parent.previousElementSibling.remove();
    parent.remove();
  }

  let response = yield gClient.listTabs();

  parent = document.getElementById("tabActors");

  // Add Global Process debugging...
  let globals = Cu.cloneInto(response, {});
  delete globals.tabs;
  delete globals.selected;
  // ...only if there are appropriate actors (a 'from' property will always
  // be there).

  // Add one entry for each open tab.
  for (let i = 0; i < response.tabs.length; i++) {
    buildTabLink(response.tabs[i], parent, i == response.selected);
  }

  let gParent = document.getElementById("globalActors");

  // Build the Remote Process button
  // If Fx<39, tab actors were used to be exposed on RootActor
  // but in Fx>=39, chrome is debuggable via getProcess() and ChromeActor
  if (globals.consoleActor || gClient.mainRoot.traits.allowChromeProcess) {
    let a = document.createElement("a");
    a.onclick = function () {
      if (gClient.mainRoot.traits.allowChromeProcess) {
        gClient.getProcess()
               .then(aResponse => {
                 openToolbox(aResponse.form, true);
               });
      } else if (globals.consoleActor) {
        openToolbox(globals, true, "webconsole", false);
      }
    };
    a.title = a.textContent = L10N.getStr("mainProcess");
    a.className = "remote-process";
    a.href = "#";
    gParent.appendChild(a);
  }
  // Move the selected tab on top
  let selectedLink = parent.querySelector("a.selected");
  if (selectedLink) {
    parent.insertBefore(selectedLink, parent.firstChild);
  }

  document.body.classList.remove("connecting");
  document.body.classList.add("actors-mode");

  // Ensure the first link is focused
  let firstLink = parent.querySelector("a:first-of-type");
  if (firstLink) {
    firstLink.focus();
  }
});

/**
 * Build one button for an add-on actor.
 */
function buildAddonLink(addon, parent) {
  let a = document.createElement("a");
  a.onclick = async function () {
    const isTabActor = addon.isWebExtension;
    openToolbox(addon, true, "webconsole", isTabActor);
  };

  a.textContent = addon.name;
  a.title = addon.id;
  a.href = "#";

  parent.appendChild(a);
}

/**
 * Build one button for a tab actor.
 */
function buildTabLink(tab, parent, selected) {
  let a = document.createElement("a");
  a.onclick = function () {
    openToolbox(tab);
  };

  a.textContent = tab.title;
  a.title = tab.url;
  if (!a.textContent) {
    a.textContent = tab.url;
  }
  a.href = "#";

  if (selected) {
    a.classList.add("selected");
  }

  parent.appendChild(a);
}

/**
 * An error occured. Let's show it and return to the first screen.
 */
function showError(type) {
  document.body.className = "error";
  let activeError = document.querySelector(".error-message.active");
  if (activeError) {
    activeError.classList.remove("active");
  }
  activeError = document.querySelector(".error-" + type);
  if (activeError) {
    activeError.classList.add("active");
  }
}

/**
 * Connection timeout.
 */
function handleConnectionTimeout() {
  showError("timeout");
}

/**
 * The user clicked on one of the buttons.
 * Opens the toolbox.
 */
function openToolbox(form, chrome = false, tool = "webconsole", isTabActor) {
  let options = {
    form: form,
    client: gClient,
    chrome: chrome,
    isTabActor: isTabActor
  };
  TargetFactory.forRemoteTab(options).then((target) => {
    let hostType = Toolbox.HostType.WINDOW;
    gDevTools.showToolbox(target, tool, hostType).then((toolbox) => {
      toolbox.once("destroyed", function () {
        gClient.close();
      });
    }, console.error.bind(console));
    window.close();
  }, console.error.bind(console));
}
PK
!<‐���7chrome/devtools/content/framework/connect/connect.xhtml<?xml version="1.0" encoding="UTF-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/. -->

<!DOCTYPE html [
<!ENTITY % connectionDTD SYSTEM "chrome://devtools/locale/connection-screen.dtd" >
 %connectionDTD;
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
 %globalDTD;
]>

<html xmlns="http://www.w3.org/1999/xhtml" dir="&locale.dir;"
      xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
  <head>
    <title>&title;</title>
    <link rel="stylesheet" href="chrome://devtools/skin/dark-theme.css" type="text/css"/>
    <link rel="stylesheet" href="chrome://devtools/content/framework/connect/connect.css" type="text/css"/>
    <script type="application/javascript" src="connect.js"></script>
  </head>
  <body>
    <h1>&header;</h1>
    <section id="connection-form">
      <form validate="validate" action="#">
        <label>
          <span>&host;</span>
          <input required="required" class="devtools-textinput" id="host" type="text"></input>
        </label>
        <label>
          <span>&port;</span>
          <input required="required" class="devtools-textinput" id="port" type="number" pattern="\d+"></input>
        </label>
        <label>
          <input class="devtools-toolbarbutton" id="submit" standalone="true" type="submit" value="&connect;"></input>
        </label>
      </form>
      <p class="error-message error-timeout">&errorTimeout;</p>
      <p class="error-message error-refused">&errorRefused;</p>
      <p class="error-message error-unexpected">&errorUnexpected;</p>
    </section>
    <section id="actors-list">
      <p>&availableTabs;</p>
      <ul class="actors" id="tabActors"></ul>
      <p>&availableAddons;</p>
      <ul class="actors" id="addonActors"></ul>
      <p>&availableProcesses;</p>
      <ul class="actors" id="globalActors"></ul>
    </section>
    <section id="connecting">
      <p class="devtools-throbber">&connecting;</p>
    </section>
    <footer>&remoteHelp;<a target='_' href='https://developer.mozilla.org/docs/Tools/Remote_Debugging'>&remoteDocumentation;</a>&remoteHelpSuffix;</footer>
  </body>
</html>
PK
!<�3JJHchrome/devtools/content/framework/dev-edition-promo/dev-edition-logo.png�PNG


IHDR@@�iq�IDATx��9������1���;��1333333333�ݣe�zm��g���$}��qDŽ}�w��T�j �Je������&x��f-�勤�����ڜ*|�@`��]%�ֲ�g�
Ƙ;�ѻT�/�s�w���i�Y?	�'m^�s�2mp$:�)��"}�gH)�TAx�'T&��7k�3�͟�w�_r�;O�=&��&c�g���:��Q�I����"0ƒ�#l�Z=��!JI�1.� �	*�jXP!�F]%�o?\~�	��o�Z��	ߛg���� f0���X�$#��h��IB`
�U����Q���
xJ��h���(����S
�$�_���Z�x3 ��Yk�����a���e�C'�q��r��N�D*XT"=E�� � KQ���<�Z��q��9����@��E��_����^�����w��#��>�A�p0��1L3r�
��2|n��9��n�1��4A*��9�<f�Q�s��Fc�,A�^��C�������|�ߜ���+��?��m��{*@�o��u��;��l�]]c�dd~HǤ�F��Ly��TO�l(Dq+/�.Ű;���	P���	x���TQ�<����=���ۜe���f��ֽ��~�퇾���I�_|�:׳�����b���~�kOξ���"zVؿ��7fڗ���$ڒ{>�T$��h$YP�^�p۱}��On����q��Bt�_V�0 �u�ULᅵx���R��B�|�_o��Y�)3�����1���3f�t�����z�'�j~�� r�?�O�Ew����sw���o&��u��T{u��pD&=����Al$������`~�3�#���&��{����C�r��ݶ��AiM)��,f8�湫*����QT���g���9H'7|�tH�#��Z���%�,�L����/\��No�Is�����v��O?>�u�2�$gT��V��71�&#
�z�M�ѯqo'&��[�-��1T������fO��Iꮅ�����
ڋ~�.VCj�������^�Z��NX�$���8������½�9ħ��%�e���L����Ϭ���dUO���b	K� 5ֵ��
I#��sn�c��J�KB��z}Y�B�1X[�q!�Gw:�ݜߜB�J�1�G���0@�.`6V�������WS�p��{�G��C�OE��5�������Ͽ���L?ȸ�y�|ŵL�����o\���1]h�":>��#\�FN)�*,�ARp�p���b�����=v��"���9��uQ��������B:�H��ᅬJ���K_��CG�w�qN�����3�̀�s\|�̳�<�{�o�:&f\�+���� :I99_�Y��RfY�'��k-Cf5G>��&��7�Y��ÎNbd�¨ D���Q�:^s
U�M��#���O�,�2��s�昨V|�Sw�ɃZq��[x�ώ�k7a���.��~�S*��\y��V�U�d<���^���2p��`.�X��6]��^Tq�o�A���&�ܜ���Awۘ~�̵D��Q����CxGN�C��\Ů.c�Ć5ξ�^�1걦-��P�㍣��ř!��;{�p-�n���ݻq�4�F�ы�X�f2e��Ն����s��+9��-�c`�s�r|��Y�����y��%�Ǡ�4�Q��2����Q��O����f���-�4빦�lE!��c>z*<X�n��k��6ęN��7�Qz1!�� !v˔'Yj���or�SN �uGZc-���ow9we�u	�H!X�α�������$FX@簵�4�5K~�.�ݴ���y�L5Ϯx�g02�(�c�"l���A�1�ϱ�dq��`�ܕ\|w�jк�Z�����xm,�L8�m���<��yXK�5w�32:���7-[a2���~;�є�fR:�Ɣ,r}��m�w�CM���y��>�K�ye��ך���=[��A��2-�o�ݩ/�m�@0XB�bG	������-��h3Hr67��R\D	�`L��%Ls��*,@�~�Hd{������"k-˙fNI�5��Ȃan�W�n��k������,m�XD�&�`<��_���f1��75"NDGB�S��tx/�V�8��8����+�+$԰�v�m��c��,��"G\.?�rj�X�=En@X����F0S�$r���0��Kۃ
�X��� &�A��;"ĹaߞY���6���R���c77���#e0��R�zۨAa
����5�WϑK5��~ܰWV�s)դ�6erCn9Q�$r����t��*l�����N\��U��6NM���^�(�9X.�}�(&�A�-O�P�6�M%S�/F}l������#w-�Q���o�y�0#�Y��6�e�&цyO,N	�Z�9y��˛�;�6+��e��L�0��eR_�y�`�L����u���v+�,�5;�_��t��ه�Z(��S�
�ň�
im
`-3�y�G��Y�� 'F8>��~?�����0FN^�ӗ�F�0�M��ډ���U��\n\���'���p%���� q��*e�{^J0vB�49��K鄰B!7�13{�Zd�!;���D��?��ل^��4�j�X�
{}�������v�Y����T?o�Yך�.������e�sO�|�s����*MO�(�X�S��"�Y7�aL���ݘC!��(?���p��*��1�@}4bd!�,�>囵�T�_Z�e���
#'/fCub�ݷFlA٢v�)(�d�,���\����z���J@�`|ᯊ��<۹i�����o�z[da��{�Ed)�(;B�%�ZV���5�d	!HƋ��!%q���I��x�c��aw�H�5��X!�e�^���G)�,hD�뻏-o�x�O�Vx����–5EL�`����J��,&��؎ت���^@�VA�d^Ȱ:E
he���9�vL7��b%3H��z�K��1i^�I��O��|Yg�){���"Qn	%-����	Hɱf��O,sOwJr�����DX�=N�?B @)�<��E8�
��JSk�g��:'h��+��=�7�A���P���CṪ�iÙ8GKO{�̴ܸXT�������&��5Z[$`�@)(WF�`� Ns7j�ʃ�.k�P�{xJ!�LN�?*��8�'2@�fy檻�� �+��~w�|fѵ=+zj�Q����;]�3�|E
�q"d�*3���Zga��V��L�,&�^å�1��c&
�tX�[�\���(*��
����_z�Dk)��8��L}�E��w(
���]��!��>LP�Cʠ
�|I�-u}c�$+���z�T_*`�W�0���Cg��K��J�ӏ�u�{!�,��}�
�?����)��=:Q��M�/�L�2H H�-��	!uF�%�<uGߢݹ�q�[ $���dӋ�";��"y����2z��u)��O��|+���q&��'.� Y����(%����F`�@X��RQ�iq�.�Dx��e,�z+�=�ʹW0��m ��ȾY��gl]�Bs����>Ym�Yky}�% h�"��~#2ӄ���-67�C�f|d�N��7�5��o`¼b���_ݾP
��}ONR<�1�!���r����)R����~=�	O����,�3�[;�pvn��[n�?�*���N���{,�.���L�� ��x���6����0����T�!7�v���p&���
K�0(A �2a^1���,���zt���W�1e���I�A����T�\<)��,��5��;3O�=�)2M�u�A��4��r[!�`9IY-����~9�kӘd�)�3c[z�*����ۀa��"�r�KS�)��&U���hRd1���ج��k�kc�mV�����c���3SC
��[ᪧ�
��c���$7l�չ��(u�ƸsG����������O�B`�^1y����}�Q�~nq'WkQ�q�+kK_0��Q�w���_��x�c�Dutj!��'�X��"ϴ���f{��b'(����R"�\��Oo�������8��� P~�F��~����#�(�&`js�l�2�Ni_��-���F]t!d�����-�u%�'�-��
:��T�K��e���w�e^1ɤ�;����Wl��"�Je�ք�ȥt�F��6J�e�<jk�!�8sua��>\
���e�vo��(�d� �DM��y�V�]к��Yu�Jg�̝��n��������W�ЖR0�Y�2+���A��2��d�ZTV��{Z���[���6�5�j���/����R]cq����m0(�7�#��N��0�<g�
���[-6��Ɇ��+n���*�I}�-:̆�8D H-�<(�經$��e��6�0���C����'�����kUrW����H)PW0u�Y�C�
G��`z]r$!�D���}��t�<+��rewh?D��8��V@*O{.IƩ�M��,ucƦs�V�U���>
e9��k
p����}���C~���[n?��-��3����	s"X,�r��$c5�����ҥ���a�d��<E=)@��\��,�[�/|)����J���nn�y@)�Ť��2�..q�͇�\�dq���g���f�!��˗�|�Z%p
;��8?f�m�1%ڐ[�|�Y|��	���	i��q7�"��JV�~�<T�P=x�/|	�O}&�[�G�W���v�fUnCL1�l��هi��',��x�Z�_^���~�ҥ_H���M�?��9���oQeW� �i�H�
��q'na.RT���sy��AVib�A)��$�Ydgí��T��׸�n{��D�#:�������MG�;C�	�jO>� �n?��V������>��(�
���������w���1JIr�)M�;b��I�,i��ee�I�bז�B�5��
D:ğ�g�9�c�s�K����꿱�fj�F��¯-o��\!<�p�"�
4������^��|��7�q���,ڸ2m��?}nO.=r�A����O1VJ��*�R�UzY $B
+D9�0�(��уY�ѕvj������X:Y�����g��f9��"�r�8w��lu���)�b��S�
��0u�þ�S�DLM�s�ʓ�v����Kibr;��Qz;?>��4�5�
�*
t��	+Xk��F(�5J�s�Sc����͎����&A%`4HhwD�ǡ�S��l�S����f�k����t����yl��Ա���Yc��Xl�wՄ���҄-�N���Q�Ӭ�T�@)ڽq���;�W|�l��/�[�|�����6�A���!I�Ҭ�O�|��Q�BͿ���fѫK�6��w./�;)�R2�r��ҕ&�TB��g��.�6m�F��h�#
<�8s�����O=����6��$Ͱ�P<�Dx��ڮp=���Oޒ]�ޗ�g�Kb�Wy|_�|r�GK���7Q�J-r�FJ��@�@ =I�^e4J�1@HA��ܵb%���$թ&�>�X���z�c��h`��`bϗ�!o���S��~��_�kO���6�]��*��X'�W �cA�U!�JXQAy^���,��A�)��Q��+�����/��	���)h�ϲ��s�Xc(��m���.�ُ|kr����;��P@�7|�w<՟>����/;��VގB����`�8B�<ET�+R�����[n��܉n�l�"=�hv���{>��H�����o~�# 4`�x�D@u���=������'.<5ɴ /0�@J�T�ʠ��1�v��D�c)����~W���7f9wv�Z-"�c����/x�i]�@\
@��P�e(
�+g��?_�uO��~_�q��[z(��:l�g�)�)�ǩ\z����G!A��|T���b��5Թ����.=�������G����ꜭ{��pq�t���7��O�<�	�)[\ �20&���m��S��=��]P���#��Ϡ
�g1u.�����_�R ����by�ZT��ʟ;�1��qJ�}l���̅D�-��� VJ.�%��`'��1��p��/�I��/����o��G�=`8����w�r��@eRzqϞW|�G(��m�f4�����L@	Q
�+�T�O5sI���ʃ���?�e��Iv�{����1�]BD��(�Q}z���"O;���S�IM��M��#Ŏ��&�e��"d�*H���O����{o�7/�B�+�'Wݼ��mN��B��J��Gn�k�|�C7�(��3�0�?i�<ds� k5a��x��;1:6�n?�񹻖l�J����O���]b2;��U	x� ����d鳒r��)��7�㤘@^1�'͖`�v�{��^����������+�Z6&Ga��IEND�B`�PK
!<�ܫa99Ichrome/devtools/content/framework/dev-edition-promo/dev-edition-promo.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

window {
  -moz-appearance: none;
  background-color: transparent;
}

#doorhanger-container {
  width: 450px;
}

#top-panel {
  padding: 20px;
  background: #343c45; /* toolbars */
  color: #8fa1b2; /* body text */
/*
 * Sloppy preprocessing since UNIX_BUT_NOT_MAC is only defined
 * in `browser/app/profile/firefox.js`, which this file cannot
 * depend on. Must style font-size to target linux.
 */
  font-size: 13px;
  line-height: 19px;
  min-height: 100px;
}

#top-panel h1 {
  font-weight: bold;
  font-family: Open Sans, sans-serif;
  font-size: 1.1em;
}

#top-panel p {
  font-family: Open Sans, sans-serif;
  font-size: 0.9em;
  width: 300px;
  display: block;
  margin: 5px 0px 0px 0px;
}

#icon {
  background-image: url("chrome://devtools/content/framework/dev-edition-promo/dev-edition-logo.png");
  background-size: 64px 64px;
  background-repeat: no-repeat;
  width: 64px;
  height: 64px;
  margin-right: 20px;
}

#lower-panel {
  padding: 20px;
  background-color: #252c33; /* tab toolbars */
  min-height: 75px;
  border-top: 1px solid #292e33; /* text high contrast (light) */
}

#button-container {
  margin: auto 20px;
}

#button-container button {
  font: message-box !important;
  font-size: 16px !important;
  cursor: pointer;
  width: 125px;
  opacity: 1;
  position: static;
  -moz-appearance: none;
  border-radius: 5px;
  height: 30px;
  width: 450px;
  /* Override embossed borders on Windows/Linux */
  border: none;
}

#close {
  background-color: transparent;
  color: #8fa1b2; /* body text */
}

#go {
  margin-left: 100px;
  background-color: #70bf53; /* green */
  color: #f5f7fa; /* selection text color */
}
PK
!<���JJIchrome/devtools/content/framework/dev-edition-promo/dev-edition-promo.xul<?xml version="1.0" encoding="utf-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/. -->
<!DOCTYPE window [
<!ENTITY % toolboxDTD SYSTEM "chrome://devtools/locale/toolbox.dtd" >
 %toolboxDTD;
]>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet rel="stylesheet" href="chrome://devtools/content/framework/dev-edition-promo/dev-edition-promo.css" type="text/css"?>

<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="dev-edition-promo">
  <vbox id="doorhanger-container">
    <hbox flex="1" id="top-panel">
      <image id="icon" />
      <vbox id="info">
        <h1>Using Developer Tools in your browser?</h1>
        <p>Download Firefox Developer Edition, our first browser made just for you.</p>
      </vbox>
    </hbox>
    <hbox id="lower-panel" flex="1">
      <hbox id="button-container" flex="1">
        <button id="close"
                flex="1"
                standalone="true"
                label="No thanks">
        </button>
        <button id="go"
                flex="1"
                standalone="true"
                label="Learn more »">
        </button>
      </hbox>
    </hbox>
  </vbox>
</window>
PK
!<7�b.�	�	3chrome/devtools/content/framework/options-panel.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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{
  -moz-user-select: none;
}

#options-panel-container {
  overflow: auto;
}

#options-panel {
  display: block;
}

.options-vertical-pane {
  display: inline;
  float: left;
}

.options-vertical-pane:dir(rtl) {
  float: right;
}

.options-vertical-pane {
  margin: 5px;
  width: calc(100%/3 - 10px);
  min-width: 320px;
  padding-inline-start: 5px;
  box-sizing: border-box;
}

/* Snap to 50% width once there is not room for 3 columns anymore.
   This prevents having 2 columns showing in a row, but taking up
   only ~66% of the available space. */
@media (max-width: 1000px) {
  .options-vertical-pane {
    width: calc(100%/2 - 10px);
  }
}

.options-vertical-pane fieldset {
  border: none;
}

.options-vertical-pane fieldset legend {
  font-size: 1.4rem;
  margin-inline-start: -15px;
  margin-bottom: 3px;
  cursor: default;
}

.options-vertical-pane fieldset + fieldset {
  margin-top: 1rem;
}

.options-groupbox {
  margin-inline-start: 15px;
  padding: 2px;
}

.options-groupbox label {
  display: flex;
  padding: 4px 0;
  align-items: center;
}

/* Add padding for label of select inputs in order to
   align it with surrounding checkboxes */
.options-groupbox label span:first-child {
  padding-inline-start: 5px;
}

.options-groupbox label span + select {
  margin-inline-start: 4px;
}

.options-groupbox.horizontal-options-groupbox label {
  display: inline-flex;
  align-items: flex-end;
}

.options-groupbox.horizontal-options-groupbox label + label {
  margin-inline-start: 4px;
}

.options-groupbox > *,
.options-groupbox > .hidden-labels-box > checkbox {
  padding: 2px;
}

.options-groupbox > .hidden-labels-box {
  padding: 0;
}

.options-citation-label {
  display: inline-block;
  font-size: 1rem;
  font-style: italic;
   /* To align it with the checkbox */
  padding: 4px 0 0;
  padding-inline-end: 4px;
}

#devtools-sourceeditor-keybinding-select {
  min-width: 130px;
}

#devtools-sourceeditor-tabsize-select {
  min-width: 80px;
}

#screenshot-options legend::after {
  content: "";
  display: inline-block;
  background-image: url("chrome://devtools/skin/images/command-screenshot.svg");
  width: 16px;
  height: 16px;
  vertical-align: sub;
  margin-inline-start: 5px;
  -moz-context-properties: fill;
  fill: var(--theme-toolbar-color);
  opacity: 0.6;
}
PK
!<����221chrome/devtools/content/framework/toolbox-init.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 */
/* global XPCNativeWrapper */

"use strict";

// URL constructor doesn't support about: scheme
let href = window.location.href.replace("about:", "http://");
let url = new window.URL(href);

// Only use this method to attach the toolbox if some query parameters are given
if (url.search.length > 1) {
  const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
  const { gDevTools } = require("devtools/client/framework/devtools");
  const { targetFromURL } = require("devtools/client/framework/target-from-url");
  const { Toolbox } = require("devtools/client/framework/toolbox");
  const { TargetFactory } = require("devtools/client/framework/target");
  const { DebuggerServer } = require("devtools/server/main");
  const { DebuggerClient } = require("devtools/shared/client/debugger-client");
  const { Task } = require("devtools/shared/task");

  // `host` is the frame element loading the toolbox.
  let host = window.QueryInterface(Ci.nsIInterfaceRequestor)
                   .getInterface(Ci.nsIDOMWindowUtils)
                   .containerElement;

  // If there's no containerElement (which happens when loading about:devtools-toolbox as
  // a top level document), use the current window.
  if (!host) {
    host = {
      contentWindow: window,
      contentDocument: document,
      // toolbox-host-manager.js wants to set attributes on the frame that contains it,
      // but that is fine to skip and doesn't make sense when using the current window.
      setAttribute() {},
      ownerDocument: document,
      // toolbox-host-manager.js wants to listen for unload events from outside the frame,
      // but this is fine to skip since the toolbox code listens inside the frame as well,
      // and there is no outer document in this case.
      addEventListener() {},
    };
  }

  // Specify the default tool to open
  let tool = url.searchParams.get("tool");

  Task.spawn(function* () {
    let target;
    if (url.searchParams.has("target")) {
      // Attach toolbox to a given browser iframe (<xul:browser> or <html:iframe
      // mozbrowser>) whose reference is set on the host iframe.

      // `iframe` is the targeted document to debug
      let iframe = host.wrappedJSObject ? host.wrappedJSObject.target
                                        : host.target;
      if (!iframe) {
        throw new Error("Unable to find the targeted iframe to debug");
      }

      // Need to use a xray to have attributes and behavior expected by
      // devtools codebase
      iframe = XPCNativeWrapper(iframe);

      // Fake a xul:tab object as we don't have one.
      // linkedBrowser is the only one attribute being queried by client.getTab
      let tab = { linkedBrowser: iframe };

      DebuggerServer.init();
      DebuggerServer.registerAllActors();
      let client = new DebuggerClient(DebuggerServer.connectPipe());

      yield client.connect();
      // Creates a target for a given browser iframe.
      let response = yield client.getTab({ tab });
      let form = response.tab;
      target = yield TargetFactory.forRemoteTab({client, form, chrome: false});
    } else {
      target = yield targetFromURL(url);
    }
    let options = { customIframe: host };
    yield gDevTools.showToolbox(target, tool, Toolbox.HostType.CUSTOM, options);
  }).catch(error => {
    console.error("Exception while loading the toolbox", error);
  });
}
PK
!<���W7"7"7chrome/devtools/content/framework/toolbox-options.xhtml<?xml version="1.0" encoding="utf-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/. -->
<!DOCTYPE html [
<!ENTITY % toolboxDTD SYSTEM "chrome://devtools/locale/toolbox.dtd" >
 %toolboxDTD;
]>
<html xmlns="http://www.w3.org/1999/xhtml" dir="">
  <head>
    <title>Toolbox option</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <link rel="stylesheet" href="chrome://devtools/content/framework/options-panel.css" type="text/css"/>
    <script type="application/javascript" src="chrome://devtools/content/shared/theme-switching.js"/>
  </head>
  <body role="application" class="theme-body">
  <form id="options-panel">
    <div id="tools-box" class="options-vertical-pane">
      <fieldset id="default-tools-box" class="options-groupbox">
        <legend>&options.selectDefaultTools.label2;</legend>
      </fieldset>

      <fieldset id="additional-tools-box" class="options-groupbox">
        <legend>&options.selectAdditionalTools.label;</legend>
      </fieldset>

      <fieldset id="enabled-toolbox-buttons-box" class="options-groupbox">
        <legend>&options.selectEnabledToolboxButtons.label;</legend>
        <span id="tools-not-supported-label"
              class="options-citation-label theme-comment">
          &options.toolNotSupported.label;</span>
      </fieldset>
    </div>

    <div class="options-vertical-pane">
      <fieldset id="devtools-theme-box"
                class="options-groupbox
                       horizontal-options-groupbox
                       radiogroup"
                data-pref="devtools.theme">
        <legend>&options.selectDevToolsTheme.label2;</legend>
      </fieldset>

      <fieldset id="inspector-options" class="options-groupbox">
        <legend>&options.context.inspector;</legend>
        <label title="&options.showUserAgentStyles.tooltip;">
          <input type="checkbox"
                 data-pref="devtools.inspector.showUserAgentStyles"/>
          <span>&options.showUserAgentStyles.label;</span>
        </label>
        <label title="&options.collapseAttrs.tooltip;">
          <input type="checkbox"
                 data-pref="devtools.markup.collapseAttributes"/>
          <span>&options.collapseAttrs.label;</span>
        </label>
        <label>
          <span>&options.defaultColorUnit.label;</span>
          <select id="defaultColorUnitMenuList"
                  data-pref="devtools.defaultColorUnit">
            <option value="authored">&options.defaultColorUnit.authored;</option>
            <option value="hex">&options.defaultColorUnit.hex;</option>
            <option value="hsl">&options.defaultColorUnit.hsl;</option>
            <option value="rgb">&options.defaultColorUnit.rgb;</option>
            <option value="name">&options.defaultColorUnit.name;</option>
          </select>
        </label>
      </fieldset>

      <fieldset id="webconsole-options" class="options-groupbox">
        <legend>&options.webconsole.label;</legend>
        <label title="&options.timestampMessages.tooltip;">
          <input type="checkbox"
                 id="webconsole-timestamp-messages"
                 data-pref="devtools.webconsole.timestampMessages"/>
          <span>&options.timestampMessages.label;</span>
        </label>
      </fieldset>

      <fieldset id="debugger-options" class="options-groupbox" hidden="true">
        <legend>&options.debugger.label;</legend>
      </fieldset>

      <fieldset id="styleeditor-options" class="options-groupbox">
        <legend>&options.styleeditor.label;</legend>
        <label title="&options.stylesheetAutocompletion.tooltip;">
          <input type="checkbox"
                 data-pref="devtools.styleeditor.autocompletion-enabled"/>
          <span>&options.stylesheetAutocompletion.label;</span>
        </label>
      </fieldset>

      <fieldset id="screenshot-options" class="options-groupbox">
        <legend>&options.screenshot.label;</legend>
        <label title="&options.screenshot.clipboard.tooltip;">
          <input type="checkbox"
                 id="devtools-screenshot-clipboard"
                 data-pref="devtools.screenshot.clipboard.enabled"/>
          <span>&options.screenshot.clipboard.label;</span>
        </label>
        <label title="&options.screenshot.audio.tooltip;">
          <input type="checkbox"
                 id="devtools-screenshot-audio"
                 data-pref="devtools.screenshot.audio.enabled"/>
          <span>&options.screenshot.audio.label;</span>
        </label>
      </fieldset>
    </div>

    <div class="options-vertical-pane">
      <fieldset id="sourceeditor-options" class="options-groupbox">
        <legend>&options.sourceeditor.label;</legend>
        <label title="&options.sourceeditor.detectindentation.tooltip;">
          <input type="checkbox"
                 id="devtools-sourceeditor-detectindentation"
                 data-pref="devtools.editor.detectindentation"/>
          <span>&options.sourceeditor.detectindentation.label;</span>
        </label>
        <label title="&options.sourceeditor.autoclosebrackets.tooltip;">
          <input type="checkbox"
                 id="devtools-sourceeditor-autoclosebrackets"
                 data-pref="devtools.editor.autoclosebrackets"/>
          <span>&options.sourceeditor.autoclosebrackets.label;</span>
        </label>
        <label title="&options.sourceeditor.expandtab.tooltip;">
          <input type="checkbox"
                 id="devtools-sourceeditor-expandtab"
                 data-pref="devtools.editor.expandtab"/>
          <span>&options.sourceeditor.expandtab.label;</span>
        </label>
        <label>
          <span>&options.sourceeditor.tabsize.label;</span>
          <select id="devtools-sourceeditor-tabsize-select"
                  data-pref="devtools.editor.tabsize">
            <option label="2">2</option>
            <option label="4">4</option>
            <option label="8">8</option>
          </select>
        </label>
        <label>
          <span>&options.sourceeditor.keybinding.label;</span>
          <select id="devtools-sourceeditor-keybinding-select"
                  data-pref="devtools.editor.keymap">
            <option value="default">&options.sourceeditor.keybinding.default.label;</option>
            <option value="vim">Vim</option>
            <option value="emacs">Emacs</option>
            <option value="sublime">Sublime Text</option>
          </select>
        </label>
      </fieldset>

      <fieldset id="context-options" class="options-groupbox">
        <legend>&options.context.advancedSettings;</legend>
          <label title="&options.sourceMaps.tooltip1;">
            <input type="checkbox"
                   data-pref="devtools.source-map.client-service.enabled"/>
            <span>&options.sourceMaps.label;</span>
          </label>
         <label title="&options.showPlatformData.tooltip;">
            <input type="checkbox"
                   id="devtools-show-gecko-data"
                   data-pref="devtools.performance.ui.show-platform-data"/>
            <span>&options.showPlatformData.label;</span>
          </label>
          <label title="&options.disableHTTPCache.tooltip;">
            <input type="checkbox"
                   id="devtools-disable-cache"
                   data-pref="devtools.cache.disabled"/>
            <span>&options.disableHTTPCache.label;</span>
          </label>
          <label title="&options.disableJavaScript.tooltip;">
            <input type="checkbox"
                   id="devtools-disable-javascript"/>
            <span>&options.disableJavaScript.label;</span>
          </label>
          <label title="&options.enableServiceWorkersHTTP.tooltip;">
            <input type="checkbox"
                   id="devtools-enable-serviceWorkersTesting"
                   data-pref="devtools.serviceWorkers.testing.enabled"/>
            <span>&options.enableServiceWorkersHTTP.label;</span>
          </label>
          <label title="&options.enableChrome.tooltip3;">
            <input type="checkbox"
                   data-pref="devtools.chrome.enabled"/>
            <span>&options.enableChrome.label5;</span>
          </label>
          <label title="&options.enableRemote.tooltip2;">
            <input type="checkbox"
                   data-pref="devtools.debugger.remote-enabled"/>
            <span>&options.enableRemote.label3;</span>
          </label>
          <span class="options-citation-label theme-comment"
          >&options.context.triggersPageRefresh;</span>
      </fieldset>
    </div>

  </form>
  </body>
</html>
PK
!<(,�F�"�";chrome/devtools/content/framework/toolbox-process-window.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

var { loader, require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
// Require this module to setup core modules
loader.require("devtools/client/framework/devtools-browser");

var { gDevTools } = require("devtools/client/framework/devtools");
var { TargetFactory } = require("devtools/client/framework/target");
var { Toolbox } = require("devtools/client/framework/toolbox");
var Services = require("Services");
var { DebuggerClient } = require("devtools/shared/client/debugger-client");
var { PrefsHelper } = require("devtools/client/shared/prefs");

// Timeout to wait before we assume that a connect() timed out without an error.
// In milliseconds. (With the Debugger pane open, this has been reported to last
// more than 10 seconds!)
const STATUS_REVEAL_TIME = 15000;

/**
 * Shortcuts for accessing various debugger preferences.
 */
var Prefs = new PrefsHelper("devtools.debugger", {
  chromeDebuggingHost: ["Char", "chrome-debugging-host"],
  chromeDebuggingWebSocket: ["Bool", "chrome-debugging-websocket"],
});

var gToolbox, gClient;

function appendStatusMessage(msg) {
  let statusMessage = document.getElementById("status-message");
  statusMessage.value += msg + "\n";
  if (msg.stack) {
    statusMessage.value += msg.stack + "\n";
  }
}

function toggleStatusMessage(visible = true) {
  let statusMessageContainer = document.getElementById("status-message-container");
  statusMessageContainer.hidden = !visible;
}

function revealStatusMessage() {
  toggleStatusMessage(true);
}

function hideStatusMessage() {
  toggleStatusMessage(false);
}

var connect = async function () {
  // Initiate the connection
  let env = Cc["@mozilla.org/process/environment;1"]
    .getService(Ci.nsIEnvironment);
  let port = env.get("MOZ_BROWSER_TOOLBOX_PORT");
  let addonID = env.get("MOZ_BROWSER_TOOLBOX_ADDONID");

  // A port needs to be passed in from the environment, for instance:
  //    MOZ_BROWSER_TOOLBOX_PORT=6080 ./mach run -chrome \
  //      chrome://devtools/content/framework/toolbox-process-window.xul
  if (!port) {
    throw new Error("Must pass a port in an env variable with MOZ_BROWSER_TOOLBOX_PORT");
  }

  let host = Prefs.chromeDebuggingHost;
  let webSocket = Prefs.chromeDebuggingWebSocket;
  appendStatusMessage(`Connecting to ${host}:${port}, ws: ${webSocket}`);
  let transport = await DebuggerClient.socketConnect({
    host,
    port,
    webSocket,
  });
  gClient = new DebuggerClient(transport);
  appendStatusMessage("Start protocol client for connection");
  await gClient.connect();

  appendStatusMessage("Get root form for toolbox");
  if (addonID) {
    let { addons } = await gClient.listAddons();
    let addonActor = addons.filter(addon => addon.id === addonID).pop();
    let isTabActor = addonActor.isWebExtension;
    await openToolbox({form: addonActor, chrome: true, isTabActor});
  } else {
    let response = await gClient.getProcess();
    await openToolbox({form: response.form, chrome: true});
  }
};

// Certain options should be toggled since we can assume chrome debugging here
function setPrefDefaults() {
  Services.prefs.setBoolPref("devtools.inspector.showUserAgentStyles", true);
  Services.prefs.setBoolPref("devtools.performance.ui.show-platform-data", true);
  Services.prefs.setBoolPref("devtools.inspector.showAllAnonymousContent", true);
  Services.prefs.setBoolPref("browser.dom.window.dump.enabled", true);
  Services.prefs.setBoolPref("devtools.command-button-noautohide.enabled", true);
  Services.prefs.setBoolPref("devtools.scratchpad.enabled", true);
  // Bug 1225160 - Using source maps with browser debugging can lead to a crash
  Services.prefs.setBoolPref("devtools.debugger.source-maps-enabled", false);
  Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", true);
  Services.prefs.setBoolPref("devtools.webconsole.new-frontend-enabled", false);
  Services.prefs.setBoolPref("devtools.preference.new-panel-enabled", false);
  Services.prefs.setBoolPref("layout.css.emulate-moz-box-with-flex", false);
}
window.addEventListener("load", async function () {
  let cmdClose = document.getElementById("toolbox-cmd-close");
  cmdClose.addEventListener("command", onCloseCommand);
  setPrefDefaults();
  // Reveal status message if connecting is slow or if an error occurs.
  let delayedStatusReveal = setTimeout(revealStatusMessage, STATUS_REVEAL_TIME);
  try {
    await connect();
    clearTimeout(delayedStatusReveal);
    hideStatusMessage();
  } catch (e) {
    clearTimeout(delayedStatusReveal);
    appendStatusMessage(e);
    revealStatusMessage();
    console.error(e);
  }
}, { once: true });

function onCloseCommand(event) {
  window.close();
}

async function openToolbox({ form, chrome, isTabActor }) {
  let options = {
    form: form,
    client: gClient,
    chrome: chrome,
    isTabActor: isTabActor
  };
  appendStatusMessage(`Create toolbox target: ${JSON.stringify(arguments, null, 2)}`);
  let target = await TargetFactory.forRemoteTab(options);
  let frame = document.getElementById("toolbox-iframe");

  // Remember the last panel that was used inside of this profile.
  // But if we are testing, then it should always open the debugger panel.
  let selectedTool =
    Services.prefs.getCharPref("devtools.browsertoolbox.panel",
      Services.prefs.getCharPref("devtools.toolbox.selectedTool",
                                  "jsdebugger"));

  options = { customIframe: frame };
  appendStatusMessage(`Show toolbox with ${selectedTool} selected`);
  let toolbox = await gDevTools.showToolbox(
    target,
    selectedTool,
    Toolbox.HostType.CUSTOM,
    options
  );
  onNewToolbox(toolbox);
}

function onNewToolbox(toolbox) {
  gToolbox = toolbox;
  bindToolboxHandlers();
  raise();
  let env = Cc["@mozilla.org/process/environment;1"]
    .getService(Ci.nsIEnvironment);
  let testScript = env.get("MOZ_TOOLBOX_TEST_SCRIPT");
  if (testScript) {
    // Only allow executing random chrome scripts when a special
    // test-only pref is set
    let prefName = "devtools.browser-toolbox.allow-unsafe-script";
    if (Services.prefs.getPrefType(prefName) == Services.prefs.PREF_BOOL &&
        Services.prefs.getBoolPref(prefName) === true) {
      evaluateTestScript(testScript, toolbox);
    }
  }
}

function evaluateTestScript(script, toolbox) {
  let sandbox = Cu.Sandbox(window);
  sandbox.window = window;
  sandbox.toolbox = toolbox;
  sandbox.ChromeUtils = ChromeUtils;
  Cu.evalInSandbox(script, sandbox);
}

async function bindToolboxHandlers() {
  gToolbox.once("destroyed", quitApp);
  window.addEventListener("unload", onUnload);

  if (Services.appinfo.OS == "Darwin") {
    // Badge the dock icon to differentiate this process from the main application
    // process.
    updateBadgeText(false);

    // Once the debugger panel opens listen for thread pause / resume.
    let panel = await gToolbox.getPanelWhenReady("jsdebugger");
    setupThreadListeners(panel);
  }
}

function setupThreadListeners(panel) {
  updateBadgeText(panel.isPaused());

  let onPaused = updateBadgeText.bind(null, true);
  let onResumed = updateBadgeText.bind(null, false);
  gToolbox.target.on("thread-paused", onPaused);
  gToolbox.target.on("thread-resumed", onResumed);

  panel.once("destroyed", () => {
    gToolbox.target.off("thread-paused", onPaused);
    gToolbox.target.off("thread-resumed", onResumed);
  });
}

function updateBadgeText(paused) {
  let dockSupport = Cc["@mozilla.org/widget/macdocksupport;1"]
    .getService(Ci.nsIMacDockSupport);
  dockSupport.badgeText = paused ? "▐▐ " : " ▶";
}

function onUnload() {
  window.removeEventListener("unload", onUnload);
  window.removeEventListener("message", onMessage);
  let cmdClose = document.getElementById("toolbox-cmd-close");
  cmdClose.removeEventListener("command", onCloseCommand);
  gToolbox.destroy();
}

function onMessage(event) {
  try {
    let json = JSON.parse(event.data);
    switch (json.name) {
      case "toolbox-raise":
        raise();
        break;
      case "toolbox-title":
        setTitle(json.data.value);
        break;
    }
  } catch (e) {
    console.error(e);
  }
}

window.addEventListener("message", onMessage);

function raise() {
  window.focus();
}

function setTitle(title) {
  document.title = title;
}

function quitApp() {
  let quit = Cc["@mozilla.org/supports-PRBool;1"]
             .createInstance(Ci.nsISupportsPRBool);
  Services.obs.notifyObservers(quit, "quit-application-requested");

  let shouldProceed = !quit.data;
  if (shouldProceed) {
    Services.startup.quit(Ci.nsIAppStartup.eForceQuit);
  }
}
PK
!<�:�<chrome/devtools/content/framework/toolbox-process-window.xul<?xml version="1.0" encoding="utf-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/. -->
<!DOCTYPE window [
<!ENTITY % toolboxDTD SYSTEM "chrome://devtools/locale/toolbox.dtd" >
 %toolboxDTD;
]>

<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>

<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        id="devtools-toolbox-window"
        macanimationtype="document"
        fullscreenbutton="true"
        windowtype="devtools:toolbox"
        width="900" height="600"
        persist="screenX screenY width height sizemode">

  <script type="text/javascript" src="chrome://global/content/globalOverlay.js"/>
  <script type="text/javascript" src="toolbox-process-window.js"/>
  <script type="text/javascript" src="chrome://global/content/viewSourceUtils.js"/>
  <script type="text/javascript" src="chrome://browser/content/utilityOverlay.js"/>

  <commandset id="toolbox-commandset">
    <command id="toolbox-cmd-close"/>
  </commandset>

  <keyset id="toolbox-keyset">
    <key id="toolbox-key-close"
         key="&closeCmd.key;"
         command="toolbox-cmd-close"
         modifiers="accel"/>
  </keyset>

  <!-- This will be used by the Web Console to hold any popups it may create,
       for example when viewing network request details. -->
  <popupset id="mainPopupSet"></popupset>

  <vbox id="status-message-container" hidden="true" flex="1">
    <box>&browserToolboxStatusMessage;</box>
    <textbox multiline="true" id="status-message" flex="1"></textbox>
  </vbox>

  <tooltip id="aHTMLTooltip" page="true"/>
  <iframe id="toolbox-iframe" flex="1" tooltip="aHTMLTooltip"></iframe>
</window>
PK
!<&���4chrome/devtools/content/framework/toolbox-window.xul<?xml version="1.0" encoding="utf-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/. -->
<!DOCTYPE window [
<!ENTITY % toolboxDTD SYSTEM "chrome://devtools/locale/toolbox.dtd" >
 %toolboxDTD;
]>

<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>

<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        id="devtools-toolbox-window"
        macanimationtype="document"
        fullscreenbutton="true"
        windowtype="devtools:toolbox"
        width="900" height="320"
        persist="screenX screenY width height sizemode">

  <commandset id="toolbox-commandset">
    <command id="toolbox-cmd-close" oncommand="window.close();"/>
  </commandset>

  <keyset id="toolbox-keyset">
    <key id="toolbox-key-close"
         key="&closeCmd.key;"
         command="toolbox-cmd-close"
         modifiers="accel"/>
    <key id="toolbox-key-toggle"
         key="&toggleToolbox.key;"
         command="toolbox-cmd-close"
         modifiers="accel,shift"
         disabled="true"/>
    <key id="toolbox-key-toggle-osx"
         key="&toggleToolbox.key;"
         command="toolbox-cmd-close"
         modifiers="accel,alt"
         disabled="true"/>
    <key id="toolbox-key-toggle-F12"
         keycode="&toggleToolboxF12.keycode;"
         keytext="&toggleToolboxF12.keytext;"
         command="toolbox-cmd-close"/>
  </keyset>

  <tooltip id="aHTMLTooltip" page="true"/>
  <iframe id="toolbox-iframe" flex="1" forceOwnRefreshDriver="" tooltip="aHTMLTooltip"></iframe>
</window>
PK
!<�"__-chrome/devtools/content/framework/toolbox.xul<?xml version="1.0" encoding="utf-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/. -->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/toolbox.css" type="text/css"?>
<?xml-stylesheet href="resource://devtools/client/shared/components/NotificationBox.css" type="text/css"?>

<!DOCTYPE window [
<!ENTITY % toolboxDTD SYSTEM "chrome://devtools/locale/toolbox.dtd" >
%toolboxDTD;
<!ENTITY % editMenuStrings SYSTEM "chrome://global/locale/editMenuOverlay.dtd">
%editMenuStrings;
<!ENTITY % globalKeysDTD SYSTEM "chrome://global/locale/globalKeys.dtd">
%globalKeysDTD;
]>

<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        xmlns:html="http://www.w3.org/1999/xhtml">

  <script type="application/javascript"
          src="chrome://devtools/content/shared/theme-switching.js"/>
  <script type="application/javascript"
          src="chrome://global/content/viewSourceUtils.js"/>

  <script type="application/javascript" src="chrome://global/content/globalOverlay.js"/>
  <script type="application/javascript"
          src="chrome://devtools/content/framework/toolbox-init.js"/>

<script type="application/javascript" src="chrome://global/content/editMenuOverlay.js"/>
<commandset id="editMenuCommands">
  <commandset id="editMenuCommandSetAll" commandupdater="true" events="focus,select"
              oncommandupdate="goUpdateGlobalEditMenuItems()"/>
  <commandset id="editMenuCommandSetUndo" commandupdater="true" events="undo"
              oncommandupdate="goUpdateUndoEditMenuItems()"/>
  <commandset id="editMenuCommandSetPaste" commandupdater="true" events="clipboard"
              oncommandupdate="goUpdatePasteMenuItems()"/>
  <command id="cmd_undo" oncommand="goDoCommand('cmd_undo')"/>
  <command id="cmd_redo" oncommand="goDoCommand('cmd_redo')"/>
  <command id="cmd_cut" oncommand="goDoCommand('cmd_cut')"/>
  <command id="cmd_copy" oncommand="goDoCommand('cmd_copy')"/>
  <command id="cmd_paste" oncommand="goDoCommand('cmd_paste')"/>
  <command id="cmd_delete" oncommand="goDoCommand('cmd_delete')"/>
  <command id="cmd_selectAll" oncommand="goDoCommand('cmd_selectAll')"/>
  <command id="cmd_switchTextDirection" oncommand="goDoCommand('cmd_switchTextDirection');"/>
</commandset>
  <!-- These key nodes are here only for show. The real bindings come from
       XBL, in platformHTMLBindings.xml. See bugs 57078 and 71779. -->

  <keyset id="editMenuKeys">
    <key id="key_undo" key="&undoCmd.key;" modifiers="accel" command="cmd_undo"/>
    <key id="key_redo" key="&undoCmd.key;" modifiers="accel,shift" command="cmd_redo"/>
    <key id="key_cut" key="&cutCmd.key;" modifiers="accel" command="cmd_cut"/>
    <key id="key_copy" key="&copyCmd.key;" modifiers="accel" command="cmd_copy"/>
    <key id="key_paste" key="&pasteCmd.key;" modifiers="accel" command="cmd_paste"/>
    <key id="key_delete" keycode="VK_DELETE" command="cmd_delete"/>
    <key id="key_selectAll" key="&selectAllCmd.key;" modifiers="accel" command="cmd_selectAll"/>
    <key id="key_find" key="&findCmd.key;" modifiers="accel" command="cmd_find"/>
    <key id="key_findAgain" key="&findAgainCmd.key;" modifiers="accel" command="cmd_findAgain"/>
    <key id="key_findPrevious" key="&findAgainCmd.key;" modifiers="shift,accel" command="cmd_findPrevious"/>
    <key id="key_findAgain2" keycode="&findAgainCmd.key2;" command="cmd_findAgain"/>
    <key id="key_findPrevious2" keycode="&findAgainCmd.key2;" modifiers="shift" command="cmd_findPrevious"/>
  </keyset>

  <popupset>
    <menupopup id="toolbox-textbox-context-popup">
      <menuitem id="cMenu_undo" label="&undoCmd.label;"
                accesskey="&undoCmd.accesskey;" command="cmd_undo"/>
      <menuseparator/>
      <menuitem id="cMenu_cut" label="&cutCmd.label;"
                accesskey="&cutCmd.accesskey;" command="cmd_cut"/>
      <menuitem id="cMenu_copy" label="&copyCmd.label;"
                accesskey="&copyCmd.accesskey;" command="cmd_copy"/>
      <menuitem id="cMenu_paste" label="&pasteCmd.label;"
                accesskey="&pasteCmd.accesskey;" command="cmd_paste"/>
      <menuitem id="cMenu_delete" label="&deleteCmd.label;"
                accesskey="&deleteCmd.accesskey;" command="cmd_delete"/>
      <menuseparator/>
      <menuitem id="cMenu_selectAll" label="&selectAllCmd.label;"
                accesskey="&selectAllCmd.accesskey;" command="cmd_selectAll"/>
    </menupopup>
  </popupset>

  <vbox id="toolbox-container" flex="1">
    <div xmlns="http://www.w3.org/1999/xhtml" id="toolbox-notificationbox"/>
    <div xmlns="http://www.w3.org/1999/xhtml" id="toolbox-toolbar-mount"
         role="toolbar" />
    <vbox flex="1" class="theme-body">
      <!-- Set large flex to allow the toolbox-panel-webconsole to have a
           height set to a small value without flexing to fill up extra
           space. There must be a flex on both to ensure that the console
           panel itself is sized properly -->
      <box id="toolbox-deck" flex="1000" minheight="75" />
      <splitter id="toolbox-console-splitter" class="devtools-horizontal-splitter" hidden="true" />
      <box minheight="75" flex="1" id="toolbox-panel-webconsole" collapsed="true" />
    </vbox>
    <tooltip id="aHTMLTooltip" page="true" />
  </vbox>
</window>
PK
!<��a�<�<.chrome/devtools/content/inspector/inspector.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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/. */

/* global window, BrowserLoader */

"use strict";

const Services = require("Services");
const promise = require("promise");
const EventEmitter = require("devtools/shared/old-event-emitter");
const {executeSoon} = require("devtools/shared/DevToolsUtils");
const {Task} = require("devtools/shared/task");
const {PrefObserver} = require("devtools/client/shared/prefs");
const Telemetry = require("devtools/client/shared/telemetry");
const HighlightersOverlay = require("devtools/client/inspector/shared/highlighters-overlay");
const ReflowTracker = require("devtools/client/inspector/shared/reflow-tracker");
const Store = require("devtools/client/inspector/store");

// Use privileged promise in panel documents to prevent having them to freeze
// during toolbox destruction. See bug 1402779.
const Promise = require("Promise");

loader.lazyRequireGetter(this, "initCssProperties", "devtools/shared/fronts/css-properties", true);
loader.lazyRequireGetter(this, "HTMLBreadcrumbs", "devtools/client/inspector/breadcrumbs", true);
loader.lazyRequireGetter(this, "KeyShortcuts", "devtools/client/shared/key-shortcuts");
loader.lazyRequireGetter(this, "InspectorSearch", "devtools/client/inspector/inspector-search", true);
loader.lazyRequireGetter(this, "ToolSidebar", "devtools/client/inspector/toolsidebar", true);
loader.lazyRequireGetter(this, "MarkupView", "devtools/client/inspector/markup/markup");
loader.lazyRequireGetter(this, "nodeConstants", "devtools/shared/dom-node-constants");
loader.lazyRequireGetter(this, "Menu", "devtools/client/framework/menu");
loader.lazyRequireGetter(this, "MenuItem", "devtools/client/framework/menu-item");
loader.lazyRequireGetter(this, "ExtensionSidebar", "devtools/client/inspector/extensions/extension-sidebar");
loader.lazyRequireGetter(this, "CommandUtils", "devtools/client/shared/developer-toolbar", true);
loader.lazyRequireGetter(this, "clipboardHelper", "devtools/shared/platform/clipboard");

const {LocalizationHelper, localizeMarkup} = require("devtools/shared/l10n");
const INSPECTOR_L10N =
      new LocalizationHelper("devtools/client/locales/inspector.properties");
loader.lazyGetter(this, "TOOLBOX_L10N", function () {
  return new LocalizationHelper("devtools/client/locales/toolbox.properties");
});

// Sidebar dimensions
const INITIAL_SIDEBAR_SIZE = 350;

// If the toolbox width is smaller than given amount of pixels,
// the sidebar automatically switches from 'landscape' to 'portrait' mode.
const PORTRAIT_MODE_WIDTH = 700;

const SHOW_SPLIT_SIDEBAR_TOGGLE_PREF = "devtools.inspector.split-sidebar-toggle";
const SPLIT_RULE_VIEW_PREF = "devtools.inspector.split-rule-enabled";

/**
 * Represents an open instance of the Inspector for a tab.
 * The inspector controls the breadcrumbs, the markup view, and the sidebar
 * (computed view, rule view, font view and animation inspector).
 *
 * Events:
 * - ready
 *      Fired when the inspector panel is opened for the first time and ready to
 *      use
 * - new-root
 *      Fired after a new root (navigation to a new page) event was fired by
 *      the walker, and taken into account by the inspector (after the markup
 *      view has been reloaded)
 * - markuploaded
 *      Fired when the markup-view frame has loaded
 * - breadcrumbs-updated
 *      Fired when the breadcrumb widget updates to a new node
 * - boxmodel-view-updated
 *      Fired when the box model updates to a new node
 * - markupmutation
 *      Fired after markup mutations have been processed by the markup-view
 * - computed-view-refreshed
 *      Fired when the computed rules view updates to a new node
 * - computed-view-property-expanded
 *      Fired when a property is expanded in the computed rules view
 * - computed-view-property-collapsed
 *      Fired when a property is collapsed in the computed rules view
 * - computed-view-sourcelinks-updated
 *      Fired when the stylesheet source links have been updated (when switching
 *      to source-mapped files)
 * - rule-view-refreshed
 *      Fired when the rule view updates to a new node
 * - rule-view-sourcelinks-updated
 *      Fired when the stylesheet source links have been updated (when switching
 *      to source-mapped files)
 */
function Inspector(toolbox) {
  EventEmitter.decorate(this);

  this._toolbox = toolbox;
  this._target = toolbox.target;
  this.panelDoc = window.document;
  this.panelWin = window;
  this.panelWin.inspector = this;

  // Map [panel id => panel instance]
  // Stores all the instances of sidebar panels like rule view, computed view, ...
  this._panels = new Map();

  this.highlighters = new HighlightersOverlay(this);
  this.prefsObserver = new PrefObserver("devtools.");
  this.reflowTracker = new ReflowTracker(this._target);
  this.store = Store();
  this.telemetry = new Telemetry();

  // Store the URL of the target page prior to navigation in order to ensure
  // telemetry counts in the Grid Inspector are not double counted on reload.
  this.previousURL = this.target.url;

  this.showSplitSidebarToggle = Services.prefs.getBoolPref(
    SHOW_SPLIT_SIDEBAR_TOGGLE_PREF);
  this.isSplitRuleViewEnabled = Services.prefs.getBoolPref(SPLIT_RULE_VIEW_PREF);

  this.nodeMenuTriggerInfo = null;

  this._handleRejectionIfNotDestroyed = this._handleRejectionIfNotDestroyed.bind(this);
  this._onContextMenu = this._onContextMenu.bind(this);
  this._onBeforeNavigate = this._onBeforeNavigate.bind(this);
  this._onMarkupFrameLoad = this._onMarkupFrameLoad.bind(this);
  this._updateSearchResultsLabel = this._updateSearchResultsLabel.bind(this);

  this.onDetached = this.onDetached.bind(this);
  this.onMarkupLoaded = this.onMarkupLoaded.bind(this);
  this.onNewSelection = this.onNewSelection.bind(this);
  this.onNewRoot = this.onNewRoot.bind(this);
  this.onPanelWindowResize = this.onPanelWindowResize.bind(this);
  this.onShowBoxModelHighlighterForNode =
    this.onShowBoxModelHighlighterForNode.bind(this);
  this.onSidebarHidden = this.onSidebarHidden.bind(this);
  this.onSidebarResized = this.onSidebarResized.bind(this);
  this.onSidebarSelect = this.onSidebarSelect.bind(this);
  this.onSidebarShown = this.onSidebarShown.bind(this);
  this.onSidebarToggle = this.onSidebarToggle.bind(this);
  this.onSplitRuleViewPrefChanged = this.onSplitRuleViewPrefChanged.bind(this);

  this._target.on("will-navigate", this._onBeforeNavigate);
  this.prefsObserver.on(SPLIT_RULE_VIEW_PREF, this.onSplitRuleViewPrefChanged);
}

Inspector.prototype = {
  /**
   * open is effectively an asynchronous constructor
   */
  init: Task.async(function* () {
    // Localize all the nodes containing a data-localization attribute.
    localizeMarkup(this.panelDoc);

    this._cssProperties = yield initCssProperties(this.toolbox);
    yield this.target.makeRemote();
    yield this._getPageStyle();

    // This may throw if the document is still loading and we are
    // refering to a dead about:blank document
    let defaultSelection = yield this._getDefaultNodeForSelection()
      .catch(this._handleRejectionIfNotDestroyed);

    return yield this._deferredOpen(defaultSelection);
  }),

  get toolbox() {
    return this._toolbox;
  },

  get inspector() {
    return this._toolbox.inspector;
  },

  get walker() {
    return this._toolbox.walker;
  },

  get selection() {
    return this._toolbox.selection;
  },

  get highlighter() {
    return this._toolbox.highlighter;
  },

  get isOuterHTMLEditable() {
    return this._target.client.traits.editOuterHTML;
  },

  get hasUrlToImageDataResolver() {
    return this._target.client.traits.urlToImageDataResolver;
  },

  get canGetUniqueSelector() {
    return this._target.client.traits.getUniqueSelector;
  },

  get canGetCssPath() {
    return this._target.client.traits.getCssPath;
  },

  get canGetXPath() {
    return this._target.client.traits.getXPath;
  },

  get canGetUsedFontFaces() {
    return this._target.client.traits.getUsedFontFaces;
  },

  get canPasteInnerOrAdjacentHTML() {
    return this._target.client.traits.pasteHTML;
  },

  /**
   * Handle promise rejections for various asynchronous actions, and only log errors if
   * the inspector panel still exists.
   * This is useful to silence useless errors that happen when the inspector is closed
   * while still initializing (and making protocol requests).
   */
  _handleRejectionIfNotDestroyed: function (e) {
    if (!this._panelDestroyer) {
      console.error(e);
    }
  },

  _deferredOpen: async function (defaultSelection) {
    this.breadcrumbs = new HTMLBreadcrumbs(this);

    this.walker.on("new-root", this.onNewRoot);

    this.selection.on("new-node-front", this.onNewSelection);
    this.selection.on("detached-front", this.onDetached);

    if (this.target.isLocalTab) {
      // Show a warning when the debugger is paused.
      // We show the warning only when the inspector
      // is selected.
      this.updateDebuggerPausedWarning = () => {
        let notificationBox = this._toolbox.getNotificationBox();
        let notification =
          notificationBox.getNotificationWithValue("inspector-script-paused");
        if (!notification && this._toolbox.currentToolId == "inspector" &&
            this._toolbox.threadClient.paused) {
          let message = INSPECTOR_L10N.getStr("debuggerPausedWarning.message");
          notificationBox.appendNotification(message,
            "inspector-script-paused", "", notificationBox.PRIORITY_WARNING_HIGH);
        }

        if (notification && this._toolbox.currentToolId != "inspector") {
          notificationBox.removeNotification(notification);
        }

        if (notification && !this._toolbox.threadClient.paused) {
          notificationBox.removeNotification(notification);
        }
      };
      this.target.on("thread-paused", this.updateDebuggerPausedWarning);
      this.target.on("thread-resumed", this.updateDebuggerPausedWarning);
      this._toolbox.on("select", this.updateDebuggerPausedWarning);
      this.updateDebuggerPausedWarning();
    }

    this._initMarkup();
    this.isReady = false;

    this.setupSearchBox();

    // Setup the splitter before the sidebar is displayed so,
    // we don't miss any events.
    this.setupSplitter();

    // We can display right panel with: tab bar, markup view and breadbrumb. Right after
    // the splitter set the right and left panel sizes, in order to avoid resizing it
    // during load of the inspector.
    this.panelDoc.getElementById("inspector-main-content").style.visibility = "visible";

    this.setupSidebar();
    this.setupExtensionSidebars();

    await this.once("markuploaded");
    this.isReady = true;

    // All the components are initialized. Let's select a node.
    if (defaultSelection) {
      let onAllPanelsUpdated = this.once("inspector-updated");
      this.selection.setNodeFront(defaultSelection, "inspector-open");
      await onAllPanelsUpdated;
      await this.markup.expandNode(this.selection.nodeFront);
    }

    // And setup the toolbar only now because it may depend on the document.
    await this.setupToolbar();
    this.emit("ready");
    return this;
  },

  _onBeforeNavigate: function () {
    this._defaultNode = null;
    this.selection.setNodeFront(null);
    this._destroyMarkup();
    this._pendingSelection = null;
  },

  _getPageStyle: function () {
    return this.inspector.getPageStyle().then(pageStyle => {
      this.pageStyle = pageStyle;
    }, this._handleRejectionIfNotDestroyed);
  },

  /**
   * Return a promise that will resolve to the default node for selection.
   */
  _getDefaultNodeForSelection: function () {
    if (this._defaultNode) {
      return this._defaultNode;
    }
    let walker = this.walker;
    let rootNode = null;
    let pendingSelection = this._pendingSelection;

    // A helper to tell if the target has or is about to navigate.
    // this._pendingSelection changes on "will-navigate" and "new-root" events.
    let hasNavigated = () => pendingSelection !== this._pendingSelection;

    // If available, set either the previously selected node or the body
    // as default selected, else set documentElement
    return walker.getRootNode().then(node => {
      if (hasNavigated()) {
        return promise.reject("navigated; resolution of _defaultNode aborted");
      }

      rootNode = node;
      if (this.selectionCssSelector) {
        return walker.querySelector(rootNode, this.selectionCssSelector);
      }
      return null;
    }).then(front => {
      if (hasNavigated()) {
        return promise.reject("navigated; resolution of _defaultNode aborted");
      }

      if (front) {
        return front;
      }
      return walker.querySelector(rootNode, "body");
    }).then(front => {
      if (hasNavigated()) {
        return promise.reject("navigated; resolution of _defaultNode aborted");
      }

      if (front) {
        return front;
      }
      return this.walker.documentElement();
    }).then(node => {
      if (hasNavigated()) {
        return promise.reject("navigated; resolution of _defaultNode aborted");
      }
      this._defaultNode = node;
      return node;
    });
  },

  /**
   * Target getter.
   */
  get target() {
    return this._target;
  },

  /**
   * Target setter.
   */
  set target(value) {
    this._target = value;
  },

  /**
   * Hooks the searchbar to show result and auto completion suggestions.
   */
  setupSearchBox: function () {
    this.searchBox = this.panelDoc.getElementById("inspector-searchbox");
    this.searchClearButton = this.panelDoc.getElementById("inspector-searchinput-clear");
    this.searchResultsLabel = this.panelDoc.getElementById("inspector-searchlabel");

    this.search = new InspectorSearch(this, this.searchBox, this.searchClearButton);
    this.search.on("search-cleared", this._updateSearchResultsLabel);
    this.search.on("search-result", this._updateSearchResultsLabel);

    let shortcuts = new KeyShortcuts({
      window: this.panelDoc.defaultView,
    });
    let key = INSPECTOR_L10N.getStr("inspector.searchHTML.key");
    shortcuts.on(key, event => {
      // Prevent overriding same shortcut from the computed/rule views
      if (event.target.closest("#sidebar-panel-ruleview") ||
          event.target.closest("#sidebar-panel-computedview")) {
        return;
      }
      event.preventDefault();
      this.searchBox.focus();
    });
  },

  get searchSuggestions() {
    return this.search.autocompleter;
  },

  _updateSearchResultsLabel: function (event, result) {
    let str = "";
    if (event !== "search-cleared") {
      if (result) {
        str = INSPECTOR_L10N.getFormatStr(
          "inspector.searchResultsCount2", result.resultsIndex + 1, result.resultsLength);
      } else {
        str = INSPECTOR_L10N.getStr("inspector.searchResultsNone");
      }
    }

    this.searchResultsLabel.textContent = str;
  },

  get React() {
    return this._toolbox.React;
  },

  get ReactDOM() {
    return this._toolbox.ReactDOM;
  },

  get ReactRedux() {
    return this._toolbox.ReactRedux;
  },

  get browserRequire() {
    return this._toolbox.browserRequire;
  },

  get InspectorTabPanel() {
    if (!this._InspectorTabPanel) {
      this._InspectorTabPanel =
        this.React.createFactory(this.browserRequire(
        "devtools/client/inspector/components/InspectorTabPanel"));
    }
    return this._InspectorTabPanel;
  },

  /**
   * Check if the inspector should use the landscape mode.
   *
   * @return {Boolean} true if the inspector should be in landscape mode.
   */
  useLandscapeMode: function () {
    let { clientWidth } = this.panelDoc.getElementById("inspector-splitter-box");
    return clientWidth > PORTRAIT_MODE_WIDTH;
  },

  /**
   * Build Splitter located between the main and side area of
   * the Inspector panel.
   */
  setupSplitter: function () {
    let SplitBox = this.React.createFactory(this.browserRequire(
      "devtools/client/shared/components/splitter/SplitBox"));
    let { width, height, splitSidebarWidth } = this.getSidebarSize();

    let splitter = SplitBox({
      className: "inspector-sidebar-splitter",
      initialWidth: width,
      initialHeight: height,
      minSize: "10%",
      maxSize: "80%",
      splitterSize: 1,
      endPanelControl: true,
      startPanel: this.InspectorTabPanel({
        id: "inspector-main-content"
      }),
      endPanel: SplitBox({
        initialWidth: splitSidebarWidth,
        minSize: 10,
        maxSize: "80%",
        splitterSize: this.isSplitRuleViewEnabled ? 1 : 0,
        endPanelControl: false,
        startPanel: this.InspectorTabPanel({
          id: "inspector-rules-container"
        }),
        endPanel: this.InspectorTabPanel({
          id: "inspector-sidebar-container"
        }),
        ref: splitbox => {
          this.sidebarSplitBox = splitbox;
        },
      }),
      vert: this.useLandscapeMode(),
      onControlledPanelResized: this.onSidebarResized,
    });

    this.splitBox = this.ReactDOM.render(splitter,
      this.panelDoc.getElementById("inspector-splitter-box"));

    this.panelWin.addEventListener("resize", this.onPanelWindowResize, true);
  },

  /**
   * Splitter clean up.
   */
  teardownSplitter: function () {
    this.panelWin.removeEventListener("resize", this.onPanelWindowResize, true);

    this.sidebar.off("show", this.onSidebarShown);
    this.sidebar.off("hide", this.onSidebarHidden);
    this.sidebar.off("destroy", this.onSidebarHidden);
  },

  /**
   * If Toolbox width is less than 600 px, the splitter changes its mode
   * to `horizontal` to support portrait view.
   */
  onPanelWindowResize: function () {
    this.splitBox.setState({
      vert: this.useLandscapeMode(),
    });
  },

  getSidebarSize: function () {
    let width;
    let height;
    let splitSidebarWidth;

    // Initialize splitter size from preferences.
    try {
      width = Services.prefs.getIntPref("devtools.toolsidebar-width.inspector");
      height = Services.prefs.getIntPref("devtools.toolsidebar-height.inspector");
      splitSidebarWidth = Services.prefs.getIntPref(
        "devtools.toolsidebar-width.inspector.splitsidebar");
    } catch (e) {
      // Set width and height of the splitter. Only one
      // value is really useful at a time depending on the current
      // orientation (vertical/horizontal).
      // Having both is supported by the splitter component.
      width = this.isSplitRuleViewEnabled ?
        INITIAL_SIDEBAR_SIZE * 2 : INITIAL_SIDEBAR_SIZE;
      height = INITIAL_SIDEBAR_SIZE;
      splitSidebarWidth = INITIAL_SIDEBAR_SIZE;
    }

    return { width, height, splitSidebarWidth };
  },

  onSidebarHidden: function () {
    // Store the current splitter size to preferences.
    let state = this.splitBox.state;
    Services.prefs.setIntPref("devtools.toolsidebar-width.inspector", state.width);
    Services.prefs.setIntPref("devtools.toolsidebar-height.inspector", state.height);
    Services.prefs.setIntPref("devtools.toolsidebar-width.inspector.splitsidebar",
      this.sidebarSplitBox.state.width);
  },

  onSidebarResized: function (width, height) {
    this.toolbox.emit("inspector-sidebar-resized", { width, height });
  },

  onSidebarSelect: function (event, toolId) {
    // Save the currently selected sidebar panel
    Services.prefs.setCharPref("devtools.inspector.activeSidebar", toolId);

    // Then forces the panel creation by calling getPanel
    // (This allows lazy loading the panels only once we select them)
    this.getPanel(toolId);

    this.toolbox.emit("inspector-sidebar-select", toolId);
  },

  onSidebarShown: function () {
    let { width, height, splitSidebarWidth } = this.getSidebarSize();
    this.splitBox.setState({ width, height });
    this.sidebarSplitBox.setState({ width: splitSidebarWidth });
  },

  onSidebarToggle: function () {
    Services.prefs.setBoolPref(SPLIT_RULE_VIEW_PREF, !this.isSplitRuleViewEnabled);
  },

  async onSplitRuleViewPrefChanged() {
    // Update the stored value of the split rule view preference since it changed.
    this.isSplitRuleViewEnabled = Services.prefs.getBoolPref(SPLIT_RULE_VIEW_PREF);

    await this.setupToolbar();
    await this.addRuleView();
  },

  /**
   * Adds the rule view to the main or split sidebar depending on whether or not it is
   * split view mode. The default tab specifies whether or not the rule view should be
   * selected. The defaultTab defaults to the rule view when the rule view is being merged
   * back into the sidebar from the split sidebar. Otherwise, we specify the default tab
   * when handling the sidebar setup.
   *
   * @params {String} defaultTab
   *         Thie id of the default tab for the sidebar.
   */
  async addRuleView(defaultTab = "ruleview") {
    let ruleViewSidebar = this.sidebarSplitBox.startPanelContainer;

    if (this.isSplitRuleViewEnabled) {
      // Removes the rule view from the main sidebar and adds the rule view to the split
      // sidebar.
      ruleViewSidebar.style.display = "block";

      // The sidebar toggle might not be setup yet on the initial setup.
      if (this.sidebarToggle) {
        this.sidebarToggle.setState({ collapsed: false });
      }

      // Show the splitter inside the sidebar split box.
      this.sidebarSplitBox.setState({ splitterSize: 1 });

      // Force the rule view panel creation by calling getPanel
      this.getPanel("ruleview");

      await this.sidebar.removeTab("ruleview");

      this.ruleViewSideBar.addExistingTab(
        "ruleview",
        INSPECTOR_L10N.getStr("inspector.sidebar.ruleViewTitle"),
        true);

      this.ruleViewSideBar.show("ruleview");
    } else {
      // Removes the rule view from the split sidebar and adds the rule view to the main
      // sidebar.
      ruleViewSidebar.style.display = "none";

      // The sidebar toggle might not be setup yet on the initial setup.
      if (this.sidebarToggle) {
        this.sidebarToggle.setState({ collapsed: true });
      }

      // Hide the splitter to prevent any drag events in the sidebar split box.
      this.sidebarSplitBox.setState({ splitterSize: 0 });

      this.ruleViewSideBar.hide();
      await this.ruleViewSideBar.removeTab("ruleview");

      this.sidebar.addExistingTab(
        "ruleview",
        INSPECTOR_L10N.getStr("inspector.sidebar.ruleViewTitle"),
        defaultTab == "ruleview",
        0);
    }
  },

  /**
   * Lazily get and create panel instances displayed in the sidebar
   */
  getPanel: function (id) {
    if (this._panels.has(id)) {
      return this._panels.get(id);
    }
    let panel;
    switch (id) {
      case "computedview":
        const {ComputedViewTool} =
          this.browserRequire("devtools/client/inspector/computed/computed");
        panel = new ComputedViewTool(this, this.panelWin);
        break;
      case "ruleview":
        const {RuleViewTool} = require("devtools/client/inspector/rules/rules");
        panel = new RuleViewTool(this, this.panelWin);
        break;
      case "boxmodel":
        // box-model isn't a panel on its own, it used to, now it is being used by
        // computed view and layout which retrieves an instance via getPanel.
        const BoxModel = require("devtools/client/inspector/boxmodel/box-model");
        panel = new BoxModel(this, this.panelWin);
        break;
      default:
        // This is a custom panel or a non lazy-loaded one.
        return null;
    }
    this._panels.set(id, panel);
    return panel;
  },

  /**
   * Build the sidebar.
   */
  async setupSidebar() {
    let sidebar = this.panelDoc.getElementById("inspector-sidebar");
    this.sidebar = new ToolSidebar(sidebar, this, "inspector", {
      showAllTabsMenu: true
    });

    let ruleSideBar = this.panelDoc.getElementById("inspector-rules-sidebar");
    this.ruleViewSideBar = new ToolSidebar(ruleSideBar, this, "inspector", {
      hideTabstripe: true
    });

    this.sidebar.on("select", this.onSidebarSelect);

    let defaultTab = Services.prefs.getCharPref("devtools.inspector.activeSidebar");

    if (this.isSplitRuleViewEnabled && defaultTab === "ruleview") {
      defaultTab = "computedview";
    }

    // Append all side panels

    await this.addRuleView(defaultTab);

    // If the 3 Pane Inspector feature is disabled, use the old order:
    // Rules, Computed, Layout, etc.
    if (!this.showSplitSidebarToggle) {
      this.sidebar.addExistingTab(
        "computedview",
        INSPECTOR_L10N.getStr("inspector.sidebar.computedViewTitle"),
        defaultTab == "computedview");
    }

    // Inject a lazy loaded react tab by exposing a fake React object
    // with a lazy defined Tab thanks to `panel` being a function
    let layoutId = "layoutview";
    let layoutTitle = INSPECTOR_L10N.getStr("inspector.sidebar.layoutViewTitle2");
    this.sidebar.addTab(
      layoutId,
      layoutTitle,
      {
        props: {
          id: layoutId,
          title: layoutTitle
        },
        panel: () => {
          if (!this.layoutview) {
            const LayoutView =
              this.browserRequire("devtools/client/inspector/layout/layout");
            this.layoutview = new LayoutView(this, this.panelWin);
          }

          return this.layoutview.provider;
        }
      },
      defaultTab == layoutId);

    // If the 3 Pane Inspector feature is enabled, use the new order:
    // Rules, Layout, Computed, etc.
    if (this.showSplitSidebarToggle) {
      this.sidebar.addExistingTab(
        "computedview",
        INSPECTOR_L10N.getStr("inspector.sidebar.computedViewTitle"),
        defaultTab == "computedview");
    }

    if (Services.prefs.getBoolPref("devtools.changesview.enabled")) {
      // Inject a lazy loaded react tab by exposing a fake React object
      // with a lazy defined Tab thanks to `panel` being a function
      let changesId = "changesview";
      let changesTitle = INSPECTOR_L10N.getStr("inspector.sidebar.changesViewTitle");
      this.sidebar.addTab(
        changesId,
        changesTitle,
        {
          props: {
            id: changesId,
            title: changesTitle
          },
          panel: () => {
            if (!this.changesview) {
              const ChangesView =
                this.browserRequire("devtools/client/inspector/changes/changes");
              this.changesview = new ChangesView(this, this.panelWin);
            }

            return this.changesview.provider;
          }
        },
        defaultTab == changesId);
    }

    if (Services.prefs.getBoolPref("devtools.eventsview.enabled")) {
      // Inject a lazy loaded react tab by exposing a fake React object
      // with a lazy defined Tab thanks to `panel` being a function
      let eventsId = "eventsview";
      let eventsTitle = INSPECTOR_L10N.getStr("inspector.sidebar.eventsViewTitle");
      this.sidebar.addTab(
        eventsId,
        eventsTitle,
        {
          props: {
            id: eventsId,
            title: eventsTitle
          },
          panel: () => {
            if (!this.eventview) {
              const EventsView =
                this.browserRequire("devtools/client/inspector/events/events");
              this.eventsview = new EventsView(this, this.panelWin);
            }

            return this.eventsview.provider;
          }
        },
        defaultTab == eventsId);
    }

    if (this.target.form.animationsActor) {
      const animationTitle =
        INSPECTOR_L10N.getStr("inspector.sidebar.animationInspectorTitle");

      if (Services.prefs.getBoolPref("devtools.new-animationinspector.enabled")) {
        const animationId = "newanimationinspector";

        this.sidebar.addTab(
          animationId,
          animationTitle,
          {
            props: {
              id: animationId,
              title: animationTitle
            },
            panel: () => {
              const AnimationInspector =
                this.browserRequire("devtools/client/inspector/animation/animation");
              this.animationinspector = new AnimationInspector(this, this.panelWin);
              return this.animationinspector.provider;
            }
          },
          defaultTab == animationId);
      } else {
        this.sidebar.addFrameTab(
          "animationinspector",
          animationTitle,
          "chrome://devtools/content/animationinspector/animation-inspector.xhtml",
          defaultTab == "animationinspector");
      }
    }

    if (this.canGetUsedFontFaces) {
      // Inject a lazy loaded react tab by exposing a fake React object
      // with a lazy defined Tab thanks to `panel` being a function
      let fontId = "fontinspector";
      let fontTitle = INSPECTOR_L10N.getStr("inspector.sidebar.fontInspectorTitle");
      this.sidebar.addTab(
        fontId,
        fontTitle,
        {
          props: {
            id: fontId,
            title: fontTitle
          },
          panel: () => {
            if (!this.fontinspector) {
              const FontInspector =
                this.browserRequire("devtools/client/inspector/fonts/fonts");
              this.fontinspector = new FontInspector(this, this.panelWin);
            }

            return this.fontinspector.provider;
          }
        },
        defaultTab == fontId);
    }

    // Persist splitter state in preferences.
    this.sidebar.on("show", this.onSidebarShown);
    this.sidebar.on("hide", this.onSidebarHidden);
    this.sidebar.on("destroy", this.onSidebarHidden);

    this.sidebar.show(defaultTab);
  },

  /**
   * Setup any extension sidebar already registered to the toolbox when the inspector.
   * has been created for the first time.
   */
  setupExtensionSidebars() {
    for (const [sidebarId, {title}] of this.toolbox.inspectorExtensionSidebars) {
      this.addExtensionSidebar(sidebarId, {title});
    }
  },

  /**
   * Create a side-panel tab controlled by an extension
   * using the devtools.panels.elements.createSidebarPane and sidebar object API
   *
   * @param {String} id
   *        An unique id for the sidebar tab.
   * @param {Object} options
   * @param {String} options.title
   *        The tab title
   */
  addExtensionSidebar: function (id, {title}) {
    if (this._panels.has(id)) {
      throw new Error(`Cannot create an extension sidebar for the existent id: ${id}`);
    }

    const extensionSidebar = new ExtensionSidebar(this, {id, title});

    // TODO(rpl): pass some extension metadata (e.g. extension name and icon) to customize
    // the render of the extension title (e.g. use the icon in the sidebar and show the
    // extension name in a tooltip).
    this.addSidebarTab(id, title, extensionSidebar.provider, false);

    this._panels.set(id, extensionSidebar);

    // Emit the created ExtensionSidebar instance to the listeners registered
    // on the toolbox by the "devtools.panels.elements" WebExtensions API.
    this.toolbox.emit(`extension-sidebar-created-${id}`, extensionSidebar);
  },

  /**
   * Remove and destroy a side-panel tab controlled by an extension (e.g. when the
   * extension has been disable/uninstalled while the toolbox and inspector were
   * still open).
   *
   * @param {String} id
   *        The id of the sidebar tab to destroy.
   */
  removeExtensionSidebar: function (id) {
    if (!this._panels.has(id)) {
      throw new Error(`Unable to find a sidebar panel with id "${id}"`);
    }

    const panel = this._panels.get(id);

    if (!(panel instanceof ExtensionSidebar)) {
      throw new Error(`The sidebar panel with id "${id}" is not an ExtensionSidebar`);
    }

    this._panels.delete(id);
    this.sidebar.removeTab(id);
    panel.destroy();
  },

  /**
   * Register a side-panel tab. This API can be used outside of
   * DevTools (e.g. from an extension) as well as by DevTools
   * code base.
   *
   * @param {string} tab uniq id
   * @param {string} title tab title
   * @param {React.Component} panel component. See `InspectorPanelTab` as an example.
   * @param {boolean} selected true if the panel should be selected
   */
  addSidebarTab: function (id, title, panel, selected) {
    this.sidebar.addTab(id, title, panel, selected);
  },

  /**
   * Method to check whether the document is a HTML document and
   * pickColorFromPage method is available or not.
   *
   * @return {Boolean} true if the eyedropper highlighter is supported by the current
   *         document.
   */
  supportsEyeDropper: Task.async(function* () {
    try {
      let hasSupportsHighlighters =
        yield this.target.actorHasMethod("inspector", "supportsHighlighters");

      let supportsHighlighters;
      if (hasSupportsHighlighters) {
        supportsHighlighters = yield this.inspector.supportsHighlighters();
      } else {
        // If the actor does not provide the supportsHighlighter method, fallback to
        // check if the selected node's document is a HTML document.
        let { nodeFront } = this.selection;
        supportsHighlighters = nodeFront && nodeFront.isInHTMLDocument;
      }

      return supportsHighlighters;
    } catch (e) {
      console.error(e);
      return false;
    }
  }),

  setupToolbar: Task.async(function* () {
    this.teardownToolbar();

    // Setup the add-node button.
    this.addNode = this.addNode.bind(this);
    this.addNodeButton = this.panelDoc.getElementById("inspector-element-add-button");
    this.addNodeButton.addEventListener("click", this.addNode);

    // Setup the eye-dropper icon if we're in an HTML document and we have actor support.
    let canShowEyeDropper = yield this.supportsEyeDropper();

    // Bail out if the inspector was destroyed in the meantime and panelDoc is no longer
    // available.
    if (!this.panelDoc) {
      return;
    }

    if (canShowEyeDropper) {
      this.onEyeDropperDone = this.onEyeDropperDone.bind(this);
      this.onEyeDropperButtonClicked = this.onEyeDropperButtonClicked.bind(this);
      this.eyeDropperButton = this.panelDoc
                                    .getElementById("inspector-eyedropper-toggle");
      this.eyeDropperButton.disabled = false;
      this.eyeDropperButton.title = INSPECTOR_L10N.getStr("inspector.eyedropper.label");
      this.eyeDropperButton.addEventListener("click", this.onEyeDropperButtonClicked);
    } else {
      let eyeDropperButton = this.panelDoc.getElementById("inspector-eyedropper-toggle");
      eyeDropperButton.disabled = true;
      eyeDropperButton.title = INSPECTOR_L10N.getStr("eyedropper.disabled.title");
    }

    // Setup the sidebar toggle button if the split rule view is enabled.
    if (this.showSplitSidebarToggle && !this.sidebarToggle) {
      let SidebarToggle = this.React.createFactory(this.browserRequire(
        "devtools/client/shared/components/SidebarToggle"));

      let sidebarToggle = SidebarToggle({
        collapsed: !this.isSplitRuleViewEnabled,
        collapsePaneTitle: INSPECTOR_L10N.getStr("inspector.hideSplitRulesView"),
        expandPaneTitle: INSPECTOR_L10N.getStr("inspector.showSplitRulesView"),
        onClick: this.onSidebarToggle
      });

      let parentBox = this.panelDoc.getElementById("inspector-sidebar-toggle-box");
      this.sidebarToggle = this.ReactDOM.render(sidebarToggle, parentBox);
    }
  }),

  teardownToolbar: function () {
    if (this.addNodeButton) {
      this.addNodeButton.removeEventListener("click", this.addNode);
      this.addNodeButton = null;
    }

    if (this.eyeDropperButton) {
      this.eyeDropperButton.removeEventListener("click", this.onEyeDropperButtonClicked);
      this.eyeDropperButton = null;
    }
  },

  /**
   * Reset the inspector on new root mutation.
   */
  onNewRoot: function () {
    // Record new-root timing for telemetry
    this._newRootStart = this.panelWin.performance.now();

    this._defaultNode = null;
    this.selection.setNodeFront(null);
    this._destroyMarkup();

    let onNodeSelected = defaultNode => {
      // Cancel this promise resolution as a new one had
      // been queued up.
      if (this._pendingSelection != onNodeSelected) {
        return;
      }
      this._pendingSelection = null;
      this.selection.setNodeFront(defaultNode, "navigateaway");

      this._initMarkup();
      this.once("markuploaded", this.onMarkupLoaded);

      // Setup the toolbar again, since its content may depend on the current document.
      this.setupToolbar();
    };
    this._pendingSelection = onNodeSelected;
    this._getDefaultNodeForSelection()
        .then(onNodeSelected, this._handleRejectionIfNotDestroyed);
  },

  /**
   * Handler for "markuploaded" event fired on a new root mutation and after the markup
   * view is initialized. Expands the current selected node and restores the saved
   * highlighter state.
   */
  onMarkupLoaded: Task.async(function* () {
    if (!this.markup) {
      return;
    }

    let onExpand = this.markup.expandNode(this.selection.nodeFront);

    // Restore the highlighter states prior to emitting "new-root".
    yield Promise.all([
      this.highlighters.restoreFlexboxState(),
      this.highlighters.restoreGridState(),
      this.highlighters.restoreShapeState()
    ]);

    this.emit("new-root");

    // Wait for full expand of the selected node in order to ensure
    // the markup view is fully emitted before firing 'reloaded'.
    // 'reloaded' is used to know when the panel is fully updated
    // after a page reload.
    yield onExpand;

    this.emit("reloaded");

    // Record the time between new-root event and inspector fully loaded.
    if (this._newRootStart) {
      // Only log the timing when inspector is not destroyed and is in foreground.
      if (this.toolbox && this.toolbox.currentToolId == "inspector") {
        let delay = this.panelWin.performance.now() - this._newRootStart;
        let telemetryKey = "DEVTOOLS_INSPECTOR_NEW_ROOT_TO_RELOAD_DELAY_MS";
        let histogram = Services.telemetry.getHistogramById(telemetryKey);
        histogram.add(delay);
      }
      delete this._newRootStart;
    }
  }),

  _selectionCssSelector: null,

  /**
   * Set the currently selected node unique css selector.
   * Will store the current target url along with it to allow pre-selection at
   * reload
   */
  set selectionCssSelector(cssSelector = null) {
    if (this._panelDestroyer) {
      return;
    }

    this._selectionCssSelector = {
      selector: cssSelector,
      url: this._target.url
    };
  },

  /**
   * Get the current selection unique css selector if any, that is, if a node
   * is actually selected and that node has been selected while on the same url
   */
  get selectionCssSelector() {
    if (this._selectionCssSelector &&
        this._selectionCssSelector.url === this._target.url) {
      return this._selectionCssSelector.selector;
    }
    return null;
  },

  /**
   * Can a new HTML element be inserted into the currently selected element?
   * @return {Boolean}
   */
  canAddHTMLChild: function () {
    let selection = this.selection;

    // Don't allow to insert an element into these elements. This should only
    // contain elements where walker.insertAdjacentHTML has no effect.
    let invalidTagNames = ["html", "iframe"];

    return selection.isHTMLNode() &&
           selection.isElementNode() &&
           !selection.isPseudoElementNode() &&
           !selection.isAnonymousNode() &&
           !invalidTagNames.includes(
            selection.nodeFront.nodeName.toLowerCase());
  },

  /**
   * When a new node is selected.
   */
  onNewSelection: function (event, value, reason) {
    if (reason === "selection-destroy") {
      return;
    }

    // Wait for all the known tools to finish updating and then let the
    // client know.
    let selection = this.selection.nodeFront;

    // Update the state of the add button in the toolbar depending on the
    // current selection.
    let btn = this.panelDoc.querySelector("#inspector-element-add-button");
    if (this.canAddHTMLChild()) {
      btn.removeAttribute("disabled");
    } else {
      btn.setAttribute("disabled", "true");
    }

    // On any new selection made by the user, store the unique css selector
    // of the selected node so it can be restored after reload of the same page
    if (this.canGetUniqueSelector &&
        this.selection.isElementNode()) {
      selection.getUniqueSelector().then(selector => {
        this.selectionCssSelector = selector;
      }, this._handleRejectionIfNotDestroyed);
    }

    let selfUpdate = this.updating("inspector-panel");
    executeSoon(() => {
      try {
        selfUpdate(selection);
      } catch (ex) {
        console.error(ex);
      }
    });
  },

  /**
   * Delay the "inspector-updated" notification while a tool
   * is updating itself.  Returns a function that must be
   * invoked when the tool is done updating with the node
   * that the tool is viewing.
   */
  updating: function (name) {
    if (this._updateProgress && this._updateProgress.node != this.selection.nodeFront) {
      this.cancelUpdate();
    }

    if (!this._updateProgress) {
      // Start an update in progress.
      let self = this;
      this._updateProgress = {
        node: this.selection.nodeFront,
        outstanding: new Set(),
        checkDone: function () {
          if (this !== self._updateProgress) {
            return;
          }
          // Cancel update if there is no `selection` anymore.
          // It can happen if the inspector panel is already destroyed.
          if (!self.selection || (this.node !== self.selection.nodeFront)) {
            self.cancelUpdate();
            return;
          }
          if (this.outstanding.size !== 0) {
            return;
          }

          self._updateProgress = null;
          self.emit("inspector-updated", name);
        },
      };
    }

    let progress = this._updateProgress;
    let done = function () {
      progress.outstanding.delete(done);
      progress.checkDone();
    };
    progress.outstanding.add(done);
    return done;
  },

  /**
   * Cancel notification of inspector updates.
   */
  cancelUpdate: function () {
    this._updateProgress = null;
  },

  /**
   * When a node is deleted, select its parent node or the defaultNode if no
   * parent is found (may happen when deleting an iframe inside which the
   * node was selected).
   */
  onDetached: function (event, parentNode) {
    this.breadcrumbs.cutAfter(this.breadcrumbs.indexOf(parentNode));
    this.selection.setNodeFront(parentNode ? parentNode : this._defaultNode, "detached");
  },

  /**
   * Destroy the inspector.
   */
  destroy: function () {
    if (this._panelDestroyer) {
      return this._panelDestroyer;
    }

    if (this.walker) {
      this.walker.off("new-root", this.onNewRoot);
      this.pageStyle = null;
    }

    this.cancelUpdate();

    this.prefsObserver.off(SPLIT_RULE_VIEW_PREF, this.onSplitRuleViewPrefChanged);
    this.target.off("will-navigate", this._onBeforeNavigate);
    this.target.off("thread-paused", this.updateDebuggerPausedWarning);
    this.target.off("thread-resumed", this.updateDebuggerPausedWarning);
    this._toolbox.off("select", this.updateDebuggerPausedWarning);

    for (let [, panel] of this._panels) {
      panel.destroy();
    }
    this._panels.clear();

    if (this.layoutview) {
      this.layoutview.destroy();
    }

    if (this.fontinspector) {
      this.fontinspector.destroy();
    }

    if (this.animationinspector) {
      this.animationinspector.destroy();
    }

    let cssPropertiesDestroyer = this._cssProperties.front.destroy();

    this.sidebar.off("select", this.onSidebarSelect);
    let sidebarDestroyer = this.sidebar.destroy();

    let ruleViewSideBarDestroyer = this.ruleViewSideBar ?
      this.ruleViewSideBar.destroy() : null;

    this.teardownSplitter();

    this.teardownToolbar();
    this.breadcrumbs.destroy();
    this.selection.off("new-node-front", this.onNewSelection);
    this.selection.off("detached-front", this.onDetached);

    let markupDestroyer = this._destroyMarkup();

    this.highlighters.destroy();
    this.prefsObserver.destroy();
    this.reflowTracker.destroy();
    this.search.destroy();

    this._toolbox = null;
    this.breadcrumbs = null;
    this.highlighters = null;
    this.panelDoc = null;
    this.panelWin.inspector = null;
    this.panelWin = null;
    this.prefsObserver = null;
    this.resultsLength = null;
    this.search = null;
    this.searchBox = null;
    this.sidebar = null;
    this.store = null;
    this.target = null;

    this._panelDestroyer = promise.all([
      cssPropertiesDestroyer,
      markupDestroyer,
      sidebarDestroyer,
      ruleViewSideBarDestroyer
    ]);

    return this._panelDestroyer;
  },

  /**
   * Returns the clipboard content if it is appropriate for pasting
   * into the current node's outer HTML, otherwise returns null.
   */
  _getClipboardContentForPaste: function () {
    let content = clipboardHelper.getText();
    if (content && content.trim().length > 0) {
      return content;
    }
    return null;
  },

  _onContextMenu: function (e) {
    if (e.originalTarget.closest("input[type=text]") ||
        e.originalTarget.closest("input:not([type])") ||
        e.originalTarget.closest("textarea")) {
      return;
    }

    e.stopPropagation();
    e.preventDefault();

    this._openMenu({
      screenX: e.screenX,
      screenY: e.screenY,
      target: e.target,
    });
  },

  _openMenu: function ({ target, screenX = 0, screenY = 0 } = { }) {
    let markupContainer = this.markup.getContainer(this.selection.nodeFront);

    this.contextMenuTarget = target;
    this.nodeMenuTriggerInfo = markupContainer &&
      markupContainer.editor.getInfoAtNode(target);

    let isSelectionElement = this.selection.isElementNode() &&
                             !this.selection.isPseudoElementNode();
    let isEditableElement = isSelectionElement &&
                            !this.selection.isAnonymousNode();
    let isDuplicatableElement = isSelectionElement &&
                                !this.selection.isAnonymousNode() &&
                                !this.selection.isRoot();
    let isScreenshotable = isSelectionElement &&
                           this.canGetUniqueSelector &&
                           this.selection.nodeFront.isTreeDisplayed;

    let menu = new Menu();
    menu.append(new MenuItem({
      id: "node-menu-edithtml",
      label: INSPECTOR_L10N.getStr("inspectorHTMLEdit.label"),
      accesskey: INSPECTOR_L10N.getStr("inspectorHTMLEdit.accesskey"),
      disabled: !isEditableElement || !this.isOuterHTMLEditable,
      click: () => this.editHTML(),
    }));
    menu.append(new MenuItem({
      id: "node-menu-add",
      label: INSPECTOR_L10N.getStr("inspectorAddNode.label"),
      accesskey: INSPECTOR_L10N.getStr("inspectorAddNode.accesskey"),
      disabled: !this.canAddHTMLChild(),
      click: () => this.addNode(),
    }));
    menu.append(new MenuItem({
      id: "node-menu-duplicatenode",
      label: INSPECTOR_L10N.getStr("inspectorDuplicateNode.label"),
      disabled: !isDuplicatableElement,
      click: () => this.duplicateNode(),
    }));
    menu.append(new MenuItem({
      id: "node-menu-delete",
      label: INSPECTOR_L10N.getStr("inspectorHTMLDelete.label"),
      accesskey: INSPECTOR_L10N.getStr("inspectorHTMLDelete.accesskey"),
      disabled: !this.isDeletable(this.selection.nodeFront),
      click: () => this.deleteNode(),
    }));

    menu.append(new MenuItem({
      label: INSPECTOR_L10N.getStr("inspectorAttributesSubmenu.label"),
      accesskey:
        INSPECTOR_L10N.getStr("inspectorAttributesSubmenu.accesskey"),
      submenu: this._getAttributesSubmenu(isEditableElement),
    }));

    menu.append(new MenuItem({
      type: "separator",
    }));

    // Set the pseudo classes
    for (let name of ["hover", "active", "focus"]) {
      let menuitem = new MenuItem({
        id: "node-menu-pseudo-" + name,
        label: name,
        type: "checkbox",
        click: this.togglePseudoClass.bind(this, ":" + name),
      });

      if (isSelectionElement) {
        let checked = this.selection.nodeFront.hasPseudoClassLock(":" + name);
        menuitem.checked = checked;
      } else {
        menuitem.disabled = true;
      }

      menu.append(menuitem);
    }

    menu.append(new MenuItem({
      type: "separator",
    }));

    menu.append(new MenuItem({
      label: INSPECTOR_L10N.getStr("inspectorCopyHTMLSubmenu.label"),
      submenu: this._getCopySubmenu(markupContainer, isSelectionElement),
    }));

    menu.append(new MenuItem({
      label: INSPECTOR_L10N.getStr("inspectorPasteHTMLSubmenu.label"),
      submenu: this._getPasteSubmenu(isEditableElement),
    }));

    menu.append(new MenuItem({
      type: "separator",
    }));

    let isNodeWithChildren = this.selection.isNode() &&
                             markupContainer.hasChildren;
    menu.append(new MenuItem({
      id: "node-menu-expand",
      label: INSPECTOR_L10N.getStr("inspectorExpandNode.label"),
      disabled: !isNodeWithChildren,
      click: () => this.expandNode(),
    }));
    menu.append(new MenuItem({
      id: "node-menu-collapse",
      label: INSPECTOR_L10N.getStr("inspectorCollapseAll.label"),
      disabled: !isNodeWithChildren || !markupContainer.expanded,
      click: () => this.collapseAll(),
    }));

    menu.append(new MenuItem({
      type: "separator",
    }));

    menu.append(new MenuItem({
      id: "node-menu-scrollnodeintoview",
      label: INSPECTOR_L10N.getStr("inspectorScrollNodeIntoView.label"),
      accesskey:
        INSPECTOR_L10N.getStr("inspectorScrollNodeIntoView.accesskey"),
      disabled: !isSelectionElement,
      click: () => this.scrollNodeIntoView(),
    }));
    menu.append(new MenuItem({
      id: "node-menu-screenshotnode",
      label: INSPECTOR_L10N.getStr("inspectorScreenshotNode.label"),
      disabled: !isScreenshotable,
      click: () => this.screenshotNode().catch(console.error),
    }));
    menu.append(new MenuItem({
      id: "node-menu-useinconsole",
      label: INSPECTOR_L10N.getStr("inspectorUseInConsole.label"),
      click: () => this.useInConsole(),
    }));
    menu.append(new MenuItem({
      id: "node-menu-showdomproperties",
      label: INSPECTOR_L10N.getStr("inspectorShowDOMProperties.label"),
      click: () => this.showDOMProperties(),
    }));

    let nodeLinkMenuItems = this._getNodeLinkMenuItems();
    if (nodeLinkMenuItems.filter(item => item.visible).length > 0) {
      menu.append(new MenuItem({
        id: "node-menu-link-separator",
        type: "separator",
      }));
    }

    for (let menuitem of nodeLinkMenuItems) {
      menu.append(menuitem);
    }

    menu.popup(screenX, screenY, this._toolbox);
    return menu;
  },

  _getCopySubmenu: function (markupContainer, isSelectionElement) {
    let copySubmenu = new Menu();
    copySubmenu.append(new MenuItem({
      id: "node-menu-copyinner",
      label: INSPECTOR_L10N.getStr("inspectorCopyInnerHTML.label"),
      accesskey: INSPECTOR_L10N.getStr("inspectorCopyInnerHTML.accesskey"),
      disabled: !isSelectionElement,
      click: () => this.copyInnerHTML(),
    }));
    copySubmenu.append(new MenuItem({
      id: "node-menu-copyouter",
      label: INSPECTOR_L10N.getStr("inspectorCopyOuterHTML.label"),
      accesskey: INSPECTOR_L10N.getStr("inspectorCopyOuterHTML.accesskey"),
      disabled: !isSelectionElement,
      click: () => this.copyOuterHTML(),
    }));
    copySubmenu.append(new MenuItem({
      id: "node-menu-copyuniqueselector",
      label: INSPECTOR_L10N.getStr("inspectorCopyCSSSelector.label"),
      accesskey:
        INSPECTOR_L10N.getStr("inspectorCopyCSSSelector.accesskey"),
      disabled: !isSelectionElement,
      hidden: !this.canGetUniqueSelector,
      click: () => this.copyUniqueSelector(),
    }));
    copySubmenu.append(new MenuItem({
      id: "node-menu-copycsspath",
      label: INSPECTOR_L10N.getStr("inspectorCopyCSSPath.label"),
      accesskey:
        INSPECTOR_L10N.getStr("inspectorCopyCSSPath.accesskey"),
      disabled: !isSelectionElement,
      hidden: !this.canGetCssPath,
      click: () => this.copyCssPath(),
    }));
    copySubmenu.append(new MenuItem({
      id: "node-menu-copyxpath",
      label: INSPECTOR_L10N.getStr("inspectorCopyXPath.label"),
      accesskey:
        INSPECTOR_L10N.getStr("inspectorCopyXPath.accesskey"),
      disabled: !isSelectionElement,
      hidden: !this.canGetXPath,
      click: () => this.copyXPath(),
    }));
    copySubmenu.append(new MenuItem({
      id: "node-menu-copyimagedatauri",
      label: INSPECTOR_L10N.getStr("inspectorImageDataUri.label"),
      disabled: !isSelectionElement || !markupContainer ||
                !markupContainer.isPreviewable(),
      click: () => this.copyImageDataUri(),
    }));

    return copySubmenu;
  },

  _getPasteSubmenu: function (isEditableElement) {
    let isPasteable = isEditableElement && this._getClipboardContentForPaste();
    let disableAdjacentPaste = !isPasteable ||
          !this.canPasteInnerOrAdjacentHTML || this.selection.isRoot() ||
          this.selection.isBodyNode() || this.selection.isHeadNode();
    let disableFirstLastPaste = !isPasteable ||
          !this.canPasteInnerOrAdjacentHTML || (this.selection.isHTMLNode() &&
          this.selection.isRoot());

    let pasteSubmenu = new Menu();
    pasteSubmenu.append(new MenuItem({
      id: "node-menu-pasteinnerhtml",
      label: INSPECTOR_L10N.getStr("inspectorPasteInnerHTML.label"),
      accesskey: INSPECTOR_L10N.getStr("inspectorPasteInnerHTML.accesskey"),
      disabled: !isPasteable || !this.canPasteInnerOrAdjacentHTML,
      click: () => this.pasteInnerHTML(),
    }));
    pasteSubmenu.append(new MenuItem({
      id: "node-menu-pasteouterhtml",
      label: INSPECTOR_L10N.getStr("inspectorPasteOuterHTML.label"),
      accesskey: INSPECTOR_L10N.getStr("inspectorPasteOuterHTML.accesskey"),
      disabled: !isPasteable || !this.isOuterHTMLEditable,
      click: () => this.pasteOuterHTML(),
    }));
    pasteSubmenu.append(new MenuItem({
      id: "node-menu-pastebefore",
      label: INSPECTOR_L10N.getStr("inspectorHTMLPasteBefore.label"),
      accesskey:
        INSPECTOR_L10N.getStr("inspectorHTMLPasteBefore.accesskey"),
      disabled: disableAdjacentPaste,
      click: () => this.pasteAdjacentHTML("beforeBegin"),
    }));
    pasteSubmenu.append(new MenuItem({
      id: "node-menu-pasteafter",
      label: INSPECTOR_L10N.getStr("inspectorHTMLPasteAfter.label"),
      accesskey:
        INSPECTOR_L10N.getStr("inspectorHTMLPasteAfter.accesskey"),
      disabled: disableAdjacentPaste,
      click: () => this.pasteAdjacentHTML("afterEnd"),
    }));
    pasteSubmenu.append(new MenuItem({
      id: "node-menu-pastefirstchild",
      label: INSPECTOR_L10N.getStr("inspectorHTMLPasteFirstChild.label"),
      accesskey:
        INSPECTOR_L10N.getStr("inspectorHTMLPasteFirstChild.accesskey"),
      disabled: disableFirstLastPaste,
      click: () => this.pasteAdjacentHTML("afterBegin"),
    }));
    pasteSubmenu.append(new MenuItem({
      id: "node-menu-pastelastchild",
      label: INSPECTOR_L10N.getStr("inspectorHTMLPasteLastChild.label"),
      accesskey:
        INSPECTOR_L10N.getStr("inspectorHTMLPasteLastChild.accesskey"),
      disabled: disableFirstLastPaste,
      click: () => this.pasteAdjacentHTML("beforeEnd"),
    }));

    return pasteSubmenu;
  },

  _getAttributesSubmenu: function (isEditableElement) {
    let attributesSubmenu = new Menu();
    let nodeInfo = this.nodeMenuTriggerInfo;
    let isAttributeClicked = isEditableElement && nodeInfo &&
                              nodeInfo.type === "attribute";

    attributesSubmenu.append(new MenuItem({
      id: "node-menu-add-attribute",
      label: INSPECTOR_L10N.getStr("inspectorAddAttribute.label"),
      accesskey: INSPECTOR_L10N.getStr("inspectorAddAttribute.accesskey"),
      disabled: !isEditableElement,
      click: () => this.onAddAttribute(),
    }));
    attributesSubmenu.append(new MenuItem({
      id: "node-menu-copy-attribute",
      label: INSPECTOR_L10N.getFormatStr("inspectorCopyAttributeValue.label",
                                        isAttributeClicked ? `${nodeInfo.value}` : ""),
      accesskey: INSPECTOR_L10N.getStr("inspectorCopyAttributeValue.accesskey"),
      disabled: !isAttributeClicked,
      click: () => this.onCopyAttributeValue(),
    }));
    attributesSubmenu.append(new MenuItem({
      id: "node-menu-edit-attribute",
      label: INSPECTOR_L10N.getFormatStr("inspectorEditAttribute.label",
                                        isAttributeClicked ? `${nodeInfo.name}` : ""),
      accesskey: INSPECTOR_L10N.getStr("inspectorEditAttribute.accesskey"),
      disabled: !isAttributeClicked,
      click: () => this.onEditAttribute(),
    }));
    attributesSubmenu.append(new MenuItem({
      id: "node-menu-remove-attribute",
      label: INSPECTOR_L10N.getFormatStr("inspectorRemoveAttribute.label",
                                        isAttributeClicked ? `${nodeInfo.name}` : ""),
      accesskey: INSPECTOR_L10N.getStr("inspectorRemoveAttribute.accesskey"),
      disabled: !isAttributeClicked,
      click: () => this.onRemoveAttribute(),
    }));

    return attributesSubmenu;
  },

  /**
   * Link menu items can be shown or hidden depending on the context and
   * selected node, and their labels can vary.
   *
   * @return {Array} list of visible menu items related to links.
   */
  _getNodeLinkMenuItems: function () {
    let linkFollow = new MenuItem({
      id: "node-menu-link-follow",
      visible: false,
      click: () => this.onFollowLink(),
    });
    let linkCopy = new MenuItem({
      id: "node-menu-link-copy",
      visible: false,
      click: () => this.onCopyLink(),
    });

    // Get information about the right-clicked node.
    let popupNode = this.contextMenuTarget;
    if (!popupNode || !popupNode.classList.contains("link")) {
      return [linkFollow, linkCopy];
    }

    let type = popupNode.dataset.type;
    if ((type === "uri" || type === "cssresource" || type === "jsresource")) {
      // Links can't be opened in new tabs in the browser toolbox.
      if (type === "uri" && !this.target.chrome) {
        linkFollow.visible = true;
        linkFollow.label = INSPECTOR_L10N.getStr(
          "inspector.menu.openUrlInNewTab.label");
      } else if (type === "cssresource") {
        linkFollow.visible = true;
        linkFollow.label = TOOLBOX_L10N.getStr(
          "toolbox.viewCssSourceInStyleEditor.label");
      } else if (type === "jsresource") {
        linkFollow.visible = true;
        linkFollow.label = TOOLBOX_L10N.getStr(
          "toolbox.viewJsSourceInDebugger.label");
      }

      linkCopy.visible = true;
      linkCopy.label = INSPECTOR_L10N.getStr(
        "inspector.menu.copyUrlToClipboard.label");
    } else if (type === "idref") {
      linkFollow.visible = true;
      linkFollow.label = INSPECTOR_L10N.getFormatStr(
        "inspector.menu.selectElement.label", popupNode.dataset.link);
    }

    return [linkFollow, linkCopy];
  },

  _initMarkup: function () {
    let doc = this.panelDoc;

    this._markupBox = doc.getElementById("markup-box");

    // create tool iframe
    this._markupFrame = doc.createElement("iframe");
    this._markupFrame.setAttribute("flex", "1");
    // This is needed to enable tooltips inside the iframe document.
    this._markupFrame.setAttribute("tooltip", "aHTMLTooltip");
    this._markupFrame.addEventListener("contextmenu", this._onContextMenu);

    this._markupBox.style.visibility = "hidden";
    this._markupBox.appendChild(this._markupFrame);

    this._markupFrame.addEventListener("load", this._onMarkupFrameLoad, true);
    this._markupFrame.setAttribute("src", "markup/markup.xhtml");
    this._markupFrame.setAttribute("aria-label",
      INSPECTOR_L10N.getStr("inspector.panelLabel.markupView"));
  },

  _onMarkupFrameLoad: function () {
    this._markupFrame.removeEventListener("load", this._onMarkupFrameLoad, true);

    this._markupFrame.contentWindow.focus();

    this.markup = new MarkupView(this, this._markupFrame, this._toolbox.win);

    this._markupBox.style.visibility = "visible";
    this.emit("markuploaded");
  },

  _destroyMarkup: function () {
    let destroyPromise;

    if (this._markupFrame) {
      this._markupFrame.removeEventListener("load", this._onMarkupFrameLoad, true);
      this._markupFrame.removeEventListener("contextmenu", this._onContextMenu);
    }

    if (this.markup) {
      destroyPromise = this.markup.destroy();
      this.markup = null;
    } else {
      destroyPromise = promise.resolve();
    }

    if (this._markupFrame) {
      this._markupFrame.remove();
      this._markupFrame = null;
    }

    this._markupBox = null;

    return destroyPromise;
  },

  onEyeDropperButtonClicked: function () {
    this.eyeDropperButton.classList.contains("checked")
      ? this.hideEyeDropper()
      : this.showEyeDropper();
  },

  startEyeDropperListeners: function () {
    this.inspector.once("color-pick-canceled", this.onEyeDropperDone);
    this.inspector.once("color-picked", this.onEyeDropperDone);
    this.walker.once("new-root", this.onEyeDropperDone);
  },

  stopEyeDropperListeners: function () {
    this.inspector.off("color-pick-canceled", this.onEyeDropperDone);
    this.inspector.off("color-picked", this.onEyeDropperDone);
    this.walker.off("new-root", this.onEyeDropperDone);
  },

  onEyeDropperDone: function () {
    this.eyeDropperButton.classList.remove("checked");
    this.stopEyeDropperListeners();
  },

  /**
   * Show the eyedropper on the page.
   * @return {Promise} resolves when the eyedropper is visible.
   */
  showEyeDropper: function () {
    // The eyedropper button doesn't exist, most probably because the actor doesn't
    // support the pickColorFromPage, or because the page isn't HTML.
    if (!this.eyeDropperButton) {
      return null;
    }

    this.telemetry.toolOpened("toolbareyedropper");
    this.eyeDropperButton.classList.add("checked");
    this.startEyeDropperListeners();
    return this.inspector.pickColorFromPage(this.toolbox, {copyOnSelect: true})
                         .catch(console.error);
  },

  /**
   * Hide the eyedropper.
   * @return {Promise} resolves when the eyedropper is hidden.
   */
  hideEyeDropper: function () {
    // The eyedropper button doesn't exist, most probably  because the page isn't HTML.
    if (!this.eyeDropperButton) {
      return null;
    }

    this.eyeDropperButton.classList.remove("checked");
    this.stopEyeDropperListeners();
    return this.inspector.cancelPickColorFromPage()
                         .catch(console.error);
  },

  /**
   * Create a new node as the last child of the current selection, expand the
   * parent and select the new node.
   */
  addNode: Task.async(function* () {
    if (!this.canAddHTMLChild()) {
      return;
    }

    let html = "<div></div>";

    // Insert the html and expect a childList markup mutation.
    let onMutations = this.once("markupmutation");
    yield this.walker.insertAdjacentHTML(this.selection.nodeFront, "beforeEnd", html);
    yield onMutations;

    // Expand the parent node.
    this.markup.expandNode(this.selection.nodeFront);
  }),

  /**
   * Toggle a pseudo class.
   */
  togglePseudoClass: function (pseudo) {
    if (this.selection.isElementNode()) {
      let node = this.selection.nodeFront;
      if (node.hasPseudoClassLock(pseudo)) {
        return this.walker.removePseudoClassLock(node, pseudo, {parents: true});
      }

      let hierarchical = pseudo == ":hover" || pseudo == ":active";
      return this.walker.addPseudoClassLock(node, pseudo, {parents: hierarchical});
    }
    return promise.resolve();
  },

  /**
   * Show DOM properties
   */
  showDOMProperties: function () {
    this._toolbox.openSplitConsole().then(() => {
      let panel = this._toolbox.getPanel("webconsole");
      let jsterm = panel.hud.jsterm;

      jsterm.execute("inspect($0)");
      jsterm.focus();
    });
  },

  /**
   * Use in Console.
   *
   * Takes the currently selected node in the inspector and assigns it to a
   * temp variable on the content window.  Also opens the split console and
   * autofills it with the temp variable.
   */
  useInConsole: function () {
    this._toolbox.openSplitConsole().then(() => {
      let panel = this._toolbox.getPanel("webconsole");
      let jsterm = panel.hud.jsterm;

      let evalString = `{ let i = 0;
        while (window.hasOwnProperty("temp" + i) && i < 1000) {
          i++;
        }
        window["temp" + i] = $0;
        "temp" + i;
      }`;

      let options = {
        selectedNodeActor: this.selection.nodeFront.actorID,
      };
      jsterm.requestEvaluation(evalString, options).then((res) => {
        jsterm.setInputValue(res.result);
        this.emit("console-var-ready");
      });
    });
  },

  /**
   * Edit the outerHTML of the selected Node.
   */
  editHTML: function () {
    if (!this.selection.isNode()) {
      return;
    }
    if (this.markup) {
      this.markup.beginEditingOuterHTML(this.selection.nodeFront);
    }
  },

  /**
   * Paste the contents of the clipboard into the selected Node's outer HTML.
   */
  pasteOuterHTML: function () {
    let content = this._getClipboardContentForPaste();
    if (!content) {
      return promise.reject("No clipboard content for paste");
    }

    let node = this.selection.nodeFront;
    return this.markup.getNodeOuterHTML(node).then(oldContent => {
      this.markup.updateNodeOuterHTML(node, content, oldContent);
    });
  },

  /**
   * Paste the contents of the clipboard into the selected Node's inner HTML.
   */
  pasteInnerHTML: function () {
    let content = this._getClipboardContentForPaste();
    if (!content) {
      return promise.reject("No clipboard content for paste");
    }

    let node = this.selection.nodeFront;
    return this.markup.getNodeInnerHTML(node).then(oldContent => {
      this.markup.updateNodeInnerHTML(node, content, oldContent);
    });
  },

  /**
   * Paste the contents of the clipboard as adjacent HTML to the selected Node.
   * @param position
   *        The position as specified for Element.insertAdjacentHTML
   *        (i.e. "beforeBegin", "afterBegin", "beforeEnd", "afterEnd").
   */
  pasteAdjacentHTML: function (position) {
    let content = this._getClipboardContentForPaste();
    if (!content) {
      return promise.reject("No clipboard content for paste");
    }

    let node = this.selection.nodeFront;
    return this.markup.insertAdjacentHTMLToNode(node, position, content);
  },

  /**
   * Copy the innerHTML of the selected Node to the clipboard.
   */
  copyInnerHTML: function () {
    if (!this.selection.isNode()) {
      return;
    }
    this._copyLongString(this.walker.innerHTML(this.selection.nodeFront));
  },

  /**
   * Copy the outerHTML of the selected Node to the clipboard.
   */
  copyOuterHTML: function () {
    if (!this.selection.isNode()) {
      return;
    }
    let node = this.selection.nodeFront;

    switch (node.nodeType) {
      case nodeConstants.ELEMENT_NODE :
        this._copyLongString(this.walker.outerHTML(node));
        break;
      case nodeConstants.COMMENT_NODE :
        this._getLongString(node.getNodeValue()).then(comment => {
          clipboardHelper.copyString("<!--" + comment + "-->");
        });
        break;
      case nodeConstants.DOCUMENT_TYPE_NODE :
        clipboardHelper.copyString(node.doctypeString);
        break;
    }
  },

  /**
   * Copy the data-uri for the currently selected image in the clipboard.
   */
  copyImageDataUri: function () {
    let container = this.markup.getContainer(this.selection.nodeFront);
    if (container && container.isPreviewable()) {
      container.copyImageDataUri();
    }
  },

  /**
   * Copy the content of a longString (via a promise resolving a
   * LongStringActor) to the clipboard
   * @param  {Promise} longStringActorPromise
   *         promise expected to resolve a LongStringActor instance
   * @return {Promise} promise resolving (with no argument) when the
   *         string is sent to the clipboard
   */
  _copyLongString: function (longStringActorPromise) {
    return this._getLongString(longStringActorPromise).then(string => {
      clipboardHelper.copyString(string);
    }).catch(console.error);
  },

  /**
   * Retrieve the content of a longString (via a promise resolving a LongStringActor)
   * @param  {Promise} longStringActorPromise
   *         promise expected to resolve a LongStringActor instance
   * @return {Promise} promise resolving with the retrieved string as argument
   */
  _getLongString: function (longStringActorPromise) {
    return longStringActorPromise.then(longStringActor => {
      return longStringActor.string().then(string => {
        longStringActor.release().catch(console.error);
        return string;
      });
    }).catch(console.error);
  },

  /**
   * Copy a unique selector of the selected Node to the clipboard.
   */
  copyUniqueSelector: function () {
    if (!this.selection.isNode()) {
      return;
    }

    this.telemetry.toolOpened("copyuniquecssselector");
    this.selection.nodeFront.getUniqueSelector().then(selector => {
      clipboardHelper.copyString(selector);
    }).catch(console.error);
  },

  /**
   * Copy the full CSS Path of the selected Node to the clipboard.
   */
  copyCssPath: function () {
    if (!this.selection.isNode()) {
      return;
    }

    this.telemetry.toolOpened("copyfullcssselector");
    this.selection.nodeFront.getCssPath().then(path => {
      clipboardHelper.copyString(path);
    }).catch(console.error);
  },

  /**
   * Copy the XPath of the selected Node to the clipboard.
   */
  copyXPath: function () {
    if (!this.selection.isNode()) {
      return;
    }

    this.telemetry.toolOpened("copyxpath");
    this.selection.nodeFront.getXPath().then(path => {
      clipboardHelper.copyString(path);
    }).catch(console.error);
  },

  /**
   * Initiate gcli screenshot command on selected node.
   */
  screenshotNode: Task.async(function* () {
    const command = Services.prefs.getBoolPref("devtools.screenshot.clipboard.enabled") ?
      "screenshot --file --clipboard --selector" :
      "screenshot --file --selector";

    // Bug 1332936 - it's possible to call `screenshotNode` while the BoxModel highlighter
    // is still visible, therefore showing it in the picture.
    // To avoid that, we have to hide it before taking the screenshot. The `hideBoxModel`
    // will do that, calling `hide` for the highlighter only if previously shown.
    yield this.highlighter.hideBoxModel();

    // Bug 1180314 -  CssSelector might contain white space so need to make sure it is
    // passed to screenshot as a single parameter.  More work *might* be needed if
    // CssSelector could contain escaped single- or double-quotes, backslashes, etc.
    CommandUtils.executeOnTarget(this._target,
      `${command} '${this.selectionCssSelector}'`);
  }),

  /**
   * Scroll the node into view.
   */
  scrollNodeIntoView: function () {
    if (!this.selection.isNode()) {
      return;
    }

    this.selection.nodeFront.scrollIntoView();
  },

  /**
   * Duplicate the selected node
   */
  duplicateNode: function () {
    let selection = this.selection;
    if (!selection.isElementNode() ||
        selection.isRoot() ||
        selection.isAnonymousNode() ||
        selection.isPseudoElementNode()) {
      return;
    }
    this.walker.duplicateNode(selection.nodeFront).catch(console.error);
  },

  /**
   * Delete the selected node.
   */
  deleteNode: function () {
    if (!this.selection.isNode() ||
         this.selection.isRoot()) {
      return;
    }

    // If the markup panel is active, use the markup panel to delete
    // the node, making this an undoable action.
    if (this.markup) {
      this.markup.deleteNode(this.selection.nodeFront);
    } else {
      // remove the node from content
      this.walker.removeNode(this.selection.nodeFront);
    }
  },

  /**
   * Add attribute to node.
   * Used for node context menu and shouldn't be called directly.
   */
  onAddAttribute: function () {
    let container = this.markup.getContainer(this.selection.nodeFront);
    container.addAttribute();
  },

  /**
   * Copy attribute value for node.
   * Used for node context menu and shouldn't be called directly.
   */
  onCopyAttributeValue: function () {
    clipboardHelper.copyString(this.nodeMenuTriggerInfo.value);
  },

  /**
   * Edit attribute for node.
   * Used for node context menu and shouldn't be called directly.
   */
  onEditAttribute: function () {
    let container = this.markup.getContainer(this.selection.nodeFront);
    container.editAttribute(this.nodeMenuTriggerInfo.name);
  },

  /**
   * Remove attribute from node.
   * Used for node context menu and shouldn't be called directly.
   */
  onRemoveAttribute: function () {
    let container = this.markup.getContainer(this.selection.nodeFront);
    container.removeAttribute(this.nodeMenuTriggerInfo.name);
  },

  expandNode: function () {
    this.markup.expandAll(this.selection.nodeFront);
  },

  collapseAll: function () {
    this.markup.collapseAll(this.selection.nodeFront);
  },

  /**
   * This method is here for the benefit of the node-menu-link-follow menu item
   * in the inspector contextual-menu.
   */
  onFollowLink: function () {
    let type = this.contextMenuTarget.dataset.type;
    let link = this.contextMenuTarget.dataset.link;

    this.followAttributeLink(type, link);
  },

  /**
   * Given a type and link found in a node's attribute in the markup-view,
   * attempt to follow that link (which may result in opening a new tab, the
   * style editor or debugger).
   */
  followAttributeLink: function (type, link) {
    if (!type || !link) {
      return;
    }

    if (type === "uri" || type === "cssresource" || type === "jsresource") {
      // Open link in a new tab.
      this.inspector.resolveRelativeURL(
        link, this.selection.nodeFront).then(url => {
          if (type === "uri") {
            let browserWin = this.target.tab.ownerDocument.defaultView;
            browserWin.openUILinkIn(url, "tab");
          } else if (type === "cssresource") {
            return this.toolbox.viewSourceInStyleEditor(url);
          } else if (type === "jsresource") {
            return this.toolbox.viewSourceInDebugger(url);
          }
          return null;
        }).catch(console.error);
    } else if (type == "idref") {
      // Select the node in the same document.
      this.walker.document(this.selection.nodeFront).then(doc => {
        return this.walker.querySelector(doc, "#" + CSS.escape(link)).then(node => {
          if (!node) {
            this.emit("idref-attribute-link-failed");
            return;
          }
          this.selection.setNodeFront(node);
        });
      }).catch(console.error);
    }
  },

  /**
   * This method is here for the benefit of the node-menu-link-copy menu item
   * in the inspector contextual-menu.
   */
  onCopyLink: function () {
    let link = this.contextMenuTarget.dataset.link;

    this.copyAttributeLink(link);
  },

  /**
   * This method is here for the benefit of copying links.
   */
  copyAttributeLink: function (link) {
    this.inspector.resolveRelativeURL(link, this.selection.nodeFront).then(url => {
      clipboardHelper.copyString(url);
    }, console.error);
  },

  /**
   * Returns an object containing the shared handler functions used in the box
   * model and grid React components.
   */
  getCommonComponentProps() {
    return {
      setSelectedNode: this.selection.setNodeFront,
      onShowBoxModelHighlighterForNode: this.onShowBoxModelHighlighterForNode,
    };
  },

  /**
   * Shows the box-model highlighter on the element corresponding to the provided
   * NodeFront.
   *
   * @param  {NodeFront} nodeFront
   *         The node to highlight.
   * @param  {Object} options
   *         Options passed to the highlighter actor.
   */
  onShowBoxModelHighlighterForNode(nodeFront, options) {
    let toolbox = this.toolbox;
    toolbox.highlighterUtils.highlightNodeFront(nodeFront, options);
  },

  /**
   * Returns a value indicating whether a node can be deleted.
   *
   * @param {NodeFront} nodeFront
   *        The node to test for deletion
   */
  isDeletable(nodeFront) {
    return !(nodeFront.isDocumentElement ||
           nodeFront.nodeType == nodeConstants.DOCUMENT_TYPE_NODE ||
           nodeFront.isAnonymous);
  },

  async inspectNodeActor(nodeActor, inspectFromAnnotation) {
    const nodeFront = await this.walker.getNodeActorFromObjectActor(nodeActor);
    if (!nodeFront) {
      console.error("The object cannot be linked to the inspector, the " +
                    "corresponding nodeFront could not be found.");
      return false;
    }

    let isAttached = await this.walker.isInDOMTree(nodeFront);
    if (!isAttached) {
      console.error("Selected DOMNode is not attached to the document tree.");
      return false;
    }

    await this.selection.setNodeFront(nodeFront, inspectFromAnnotation);
    return true;
  },
};

/**
 * Create a fake toolbox when running the inspector standalone, either in a chrome tab or
 * in a content tab.
 *
 * @param {Target} target to debug
 * @param {Function} createThreadClient
 *        When supported the thread client needs a reference to the toolbox.
 *        This callback will be called right after the toolbox object is created.
 * @param {Object} dependencies
 *        - react
 *        - reactDOM
 *        - browserRequire
 */
const buildFakeToolbox = Task.async(function* (
  target, createThreadClient, {
    React,
    ReactDOM,
    browserRequire
  }) {
  const { InspectorFront } = require("devtools/shared/fronts/inspector");
  const { Selection } = require("devtools/client/framework/selection");
  const { getHighlighterUtils } = require("devtools/client/framework/toolbox-highlighter-utils");

  let notImplemented = function () {
    throw new Error("Not implemented in a tab");
  };
  let fakeToolbox = {
    target,
    hostType: "bottom",
    doc: window.document,
    win: window,
    on() {}, emit() {}, off() {},
    initInspector() {},
    browserRequire,
    React,
    ReactDOM,
    isToolRegistered() {
      return false;
    },
    currentToolId: "inspector",
    getCurrentPanel() {
      return "inspector";
    },
    get textboxContextMenuPopup() {
      notImplemented();
    },
    getPanel: notImplemented,
    openSplitConsole: notImplemented,
    viewCssSourceInStyleEditor: notImplemented,
    viewJsSourceInDebugger: notImplemented,
    viewSource: notImplemented,
    viewSourceInDebugger: notImplemented,
    viewSourceInStyleEditor: notImplemented,

    get inspectorExtensionSidebars() {
      notImplemented();
    },

    // For attachThread:
    highlightTool() {},
    unhighlightTool() {},
    selectTool() {},
    raise() {},
    getNotificationBox() {}
  };

  fakeToolbox.threadClient = yield createThreadClient(fakeToolbox);

  let inspector = InspectorFront(target.client, target.form);
  let showAllAnonymousContent =
    Services.prefs.getBoolPref("devtools.inspector.showAllAnonymousContent");
  let walker = yield inspector.getWalker({ showAllAnonymousContent });
  let selection = new Selection(walker);
  let highlighter = yield inspector.getHighlighter(false);
  fakeToolbox.highlighterUtils = getHighlighterUtils(fakeToolbox);

  fakeToolbox.inspector = inspector;
  fakeToolbox.walker = walker;
  fakeToolbox.selection = selection;
  fakeToolbox.highlighter = highlighter;
  return fakeToolbox;
});

// URL constructor doesn't support chrome: scheme
let href = window.location.href.replace(/chrome:/, "http://");
let url = new window.URL(href);

// If query parameters are given in a chrome tab, the inspector is running in standalone.
if (window.location.protocol === "chrome:" && url.search.length > 1) {
  const { targetFromURL } = require("devtools/client/framework/target-from-url");
  const { attachThread } = require("devtools/client/framework/attach-thread");

  const browserRequire = BrowserLoader({ window, useOnlyShared: true }).require;
  const React = browserRequire("devtools/client/shared/vendor/react");
  const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");

  Task.spawn(function* () {
    let target = yield targetFromURL(url);
    let fakeToolbox = yield buildFakeToolbox(
      target,
      (toolbox) => attachThread(toolbox),
      { React, ReactDOM, browserRequire }
    );
    let inspectorUI = new Inspector(fakeToolbox);
    inspectorUI.init();
  }).catch(e => {
    window.alert("Unable to start the inspector:" + e.message + "\n" + e.stack);
  });
}

exports.Inspector = Inspector;
exports.buildFakeToolbox = buildFakeToolbox;
PK
!< �ӨYY1chrome/devtools/content/inspector/inspector.xhtml<?xml version="1.0" encoding="utf-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/. -->
<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml" dir="">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>

  <link rel="stylesheet" href="chrome://devtools/skin/breadcrumbs.css"/>
  <link rel="stylesheet" href="chrome://devtools/skin/inspector.css"/>
  <link rel="stylesheet" href="chrome://devtools/skin/rules.css"/>
  <link rel="stylesheet" href="chrome://devtools/skin/computed.css"/>
  <link rel="stylesheet" href="chrome://devtools/skin/fonts.css"/>
  <link rel="stylesheet" href="chrome://devtools/skin/boxmodel.css"/>
  <link rel="stylesheet" href="chrome://devtools/skin/layout.css"/>
  <link rel="stylesheet" href="chrome://devtools/skin/animation.css"/>
  <link rel="stylesheet" href="resource://devtools/client/shared/components/tabs/Tabs.css"/>
  <link rel="stylesheet" href="resource://devtools/client/shared/components/tabs/TabBar.css"/>
  <link rel="stylesheet" href="resource://devtools/client/shared/components/SidebarToggle.css"/>
  <link rel="stylesheet" href="resource://devtools/client/inspector/components/InspectorTabPanel.css"/>
  <link rel="stylesheet" href="resource://devtools/client/shared/components/splitter/SplitBox.css"/>
  <link rel="stylesheet" href="resource://devtools/client/inspector/layout/components/Accordion.css"/>
  <link rel="stylesheet" href="resource://devtools/client/shared/components/reps/reps.css"/>
  <link rel="stylesheet" href="resource://devtools/client/shared/components/tree/TreeView.css"/>

  <script type="application/javascript"
          src="chrome://devtools/content/shared/theme-switching.js"></script>
  <script type="text/javascript">
    /* eslint-disable */
    var isInChrome = window.location.href.includes("chrome:");
    if (isInChrome) {
      var exports = {};
      var { require, loader } = Cu.import("resource://devtools/shared/Loader.jsm", {});
      var { BrowserLoader } = Cu.import("resource://devtools/client/shared/browser-loader.js", {});
    }
  </script>

  <!-- In content, inspector.js is mapped to the dynamically generated webpack bundle -->
  <script type="application/javascript" src="inspector.js" defer="true"></script>
</head>
<body class="theme-body" role="application">
  <div class="inspector-responsive-container theme-body inspector"
       data-localization-bundle="devtools/client/locales/inspector.properties">

    <!-- Main Panel Content -->
    <div id="inspector-main-content" class="devtools-main-content" style="visibility: hidden;">
      <!-- Toolbar -->
      <div id="inspector-toolbar" class="devtools-toolbar" nowindowdrag="true">
        <button id="inspector-element-add-button" class="devtools-button"
                data-localization="title=inspectorAddNode.label"></button>
        <div class="devtools-toolbar-spacer"></div>
        <span id="inspector-searchlabel"></span>
        <div id="inspector-search" class="devtools-searchbox has-clear-btn">
          <input id="inspector-searchbox" class="devtools-searchinput"
                 type="search"
                 data-localization="placeholder=inspectorSearchHTML.label3"/>
          <button id="inspector-searchinput-clear" class="devtools-searchinput-clear" tabindex="-1"></button>
        </div>
        <button id="inspector-eyedropper-toggle" class="devtools-button"></button>
        <div id="inspector-sidebar-toggle-box"></div>
      </div>

      <!-- Markup Container -->
      <div id="markup-box"></div>
      <div id="inspector-breadcrumbs-toolbar" class="devtools-toolbar">
        <div id="inspector-breadcrumbs" class="breadcrumbs-widget-container"
             role="toolbar" data-localization="aria-label=inspector.breadcrumbs.label" tabindex="0"></div>
      </div>
    </div>

    <!-- Splitter -->
    <div id="inspector-splitter-box"></div>

    <!-- Split Sidebar Container -->
    <div id="inspector-rules-container">
      <div id="inspector-rules-sidebar" hidden="true"></div>
    </div>

    <!-- Sidebar Container -->
    <div id="inspector-sidebar-container">
      <div id="inspector-sidebar" hidden="true"></div>
    </div>

    <!-- Sidebar Panel Definitions -->
    <div id="tabpanels" style="visibility: collapse">
      <div id="sidebar-panel-ruleview" class="theme-sidebar inspector-tabpanel">
        <div id="ruleview-toolbar-container" class="devtools-toolbar">
          <div id="ruleview-toolbar">
            <div class="devtools-searchbox has-clear-btn">
              <input id="ruleview-searchbox"
                     class="devtools-filterinput devtools-rule-searchbox"
                     type="search"
                     data-localization="placeholder=inspector.filterStyles.placeholder"/>
              <button id="ruleview-searchinput-clear" class="devtools-searchinput-clear"></button>
            </div>
            <div id="ruleview-command-toolbar">
              <button id="ruleview-add-rule-button" data-localization="title=inspector.addRule.tooltip" class="devtools-button"></button>
              <button id="pseudo-class-panel-toggle" data-localization="title=inspector.togglePseudo.tooltip" class="devtools-button"></button>
              <button id="class-panel-toggle" data-localization="title=inspector.classPanel.toggleClass.tooltip" class="devtools-button"></button>
            </div>
          </div>
          <div id="pseudo-class-panel" class="ruleview-reveal-panel" hidden="true">
            <label><input id="pseudo-hover-toggle" type="checkbox" value=":hover" tabindex="-1" />:hover</label>
            <label><input id="pseudo-active-toggle" type="checkbox" value=":active" tabindex="-1" />:active</label>
            <label><input id="pseudo-focus-toggle" type="checkbox" value=":focus" tabindex="-1" />:focus</label>
          </div>
          <div id="ruleview-class-panel" class="ruleview-reveal-panel" hidden="true"></div>
        </div>

        <div id="ruleview-container" class="ruleview">
          <div id="ruleview-container-focusable" tabindex="-1">
          </div>
        </div>
      </div>

      <div id="sidebar-panel-computedview" class="theme-sidebar inspector-tabpanel">
        <div id="computed-toolbar" class="devtools-toolbar">
          <div class="devtools-searchbox has-clear-btn">
            <input id="computed-searchbox"
                   class="devtools-filterinput devtools-rule-searchbox"
                   type="search"
                   data-localization="placeholder=inspector.filterStyles.placeholder"/>
            <button id="computed-searchinput-clear" class="devtools-searchinput-clear"></button>
          </div>
          <input id="browser-style-checkbox"
                 type="checkbox"
                 class="includebrowserstyles"/>
          <label id="browser-style-checkbox-label" for="browser-style-checkbox"
                 data-localization="content=inspector.browserStyles.label"></label>
        </div>

        <div id="computed-container">
          <div id="computed-container-focusable" tabindex="-1">
            <div id="boxmodel-wrapper"></div>
            <div id="computed-property-container" class="devtools-monospace" tabindex="0" dir="ltr"></div>
            <div id="computed-no-results" class="devtools-sidepanel-no-result" hidden="" data-localization="content=inspector.noProperties"></div>
          </div>
        </div>
      </div>

      <div id="sidebar-panel-animationinspector" class="theme-sidebar inspector-tabpanel">
        <iframe class="devtools-inspector-tab-frame"></iframe>
      </div>
    </div>

  </div>
</body>
</html>
PK
!<�ȝ>��5chrome/devtools/content/inspector/markup/markup.xhtml<?xml version="1.0" encoding="UTF-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/. -->
<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
  <link rel="stylesheet" href="chrome://devtools/skin/markup.css" type="text/css"/>
  <link rel="stylesheet" href="chrome://devtools/content/sourceeditor/codemirror/lib/codemirror.css" type="text/css"/>
  <link rel="stylesheet" href="chrome://devtools/content/sourceeditor/codemirror/addon/dialog/dialog.css" type="text/css"/>
  <link rel="stylesheet" href="chrome://devtools/content/sourceeditor/codemirror/mozilla.css" type="text/css"/>

  <script type="application/javascript"
          src="chrome://devtools/content/shared/theme-switching.js"></script>
  <script type="application/javascript"
          src="chrome://devtools/content/sourceeditor/codemirror/codemirror.bundle.js"></script>
</head>
<body class="theme-body devtools-monospace" role="application">
  <div id="root-wrapper" role="presentation">
    <div id="root" role="presentation"></div>
  </div>
</body>
</html>
PK
!<#A{R!	!	-chrome/devtools/content/memory/initializer.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 initialize, destroy, Promise */

"use strict";

const BrowserLoaderModule = {};
ChromeUtils.import("resource://devtools/client/shared/browser-loader.js", BrowserLoaderModule);
const { require } = BrowserLoaderModule.BrowserLoader({
  baseURI: "resource://devtools/client/memory/",
  window
});
const { Task } = require("devtools/shared/task");
const { createFactory, createElement } = require("devtools/client/shared/vendor/react");
const ReactDOM = require("devtools/client/shared/vendor/react-dom");
const { Provider } = require("devtools/client/shared/vendor/react-redux");
const App = createFactory(require("devtools/client/memory/app"));
const Store = require("devtools/client/memory/store");
const { assert } = require("devtools/shared/DevToolsUtils");
const Promise = require("Promise");

/**
 * The current target, toolbox, MemoryFront, and HeapAnalysesClient,
 * set by this tool's host.
 */
var gToolbox, gFront, gHeapAnalysesClient;

/**
 * Variables set by `initialize()`
 */
var gStore, gRoot, gApp, gProvider, unsubscribe, isHighlighted;

var initialize = Task.async(function* () {
  gRoot = document.querySelector("#app");
  gStore = Store();
  gApp = createElement(App,
    { toolbox: gToolbox, front: gFront, heapWorker: gHeapAnalysesClient });
  gProvider = createElement(Provider, { store: gStore }, gApp);
  ReactDOM.render(gProvider, gRoot);
  unsubscribe = gStore.subscribe(onStateChange);
});

var destroy = Task.async(function* () {
  const ok = ReactDOM.unmountComponentAtNode(gRoot);
  assert(ok, "Should successfully unmount the memory tool's top level React component");

  unsubscribe();

  gStore = gRoot = gApp = gProvider = unsubscribe = isHighlighted = null;
});

/**
 * Fired on any state change, currently only handles toggling
 * the highlighting of the tool when recording allocations.
 */
function onStateChange() {
  let isRecording = gStore.getState().allocations.recording;
  if (isRecording === isHighlighted) {
    return;
  }

  if (isRecording) {
    gToolbox.highlightTool("memory");
  } else {
    gToolbox.unhighlightTool("memory");
  }

  isHighlighted = isRecording;
}
PK
!<y=�E��+chrome/devtools/content/memory/memory.xhtml<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html [
  <!ENTITY % htmlDTD
    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "DTD/xhtml1-strict.dtd">
  %htmlDTD;
]>

<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<html xmlns="http://www.w3.org/1999/xhtml" dir="">
  <head>
    <link rel="stylesheet" href="chrome://devtools/skin/widgets.css" type="text/css"/>
    <link rel="stylesheet" href="chrome://devtools/skin/memory.css" type="text/css"/>
    <link rel="stylesheet" href="chrome://devtools/skin/components-frame.css" type="text/css"/>
    <link rel="stylesheet" href="chrome://devtools/skin/components-h-split-box.css" type="text/css"/>
  </head>
  <body class="theme-body">
    <div id="app"></div>

    <script type="application/javascript"
            src="chrome://devtools/content/shared/theme-switching.js"
            defer="true">
    </script>

    <script type="application/javascript"
            src="initializer.js"
            defer="true">
    </script>

    <script type="application/javascript"
            src="chrome://devtools/content/shared/vendor/d3.js"
            defer="true">
    </script>

    <script type="application/javascript"
            src="chrome://devtools/content/shared/vendor/dagre-d3.js"
            defer="true">
    </script>
  </body>
</html>
PK
!<-��FF-chrome/devtools/content/netmonitor/index.html<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 dir="">
  <head>
    <link rel="stylesheet" href="chrome://devtools/content/netmonitor/src/assets/styles/netmonitor.css"/>
    <script src="chrome://devtools/content/shared/theme-switching.js"></script>
  </head>
  <body class="theme-body" role="application">
    <div id="mount"></div>
    <script src="initializer.js"></script>
  </body>
</html>
PK
!<��enn1chrome/devtools/content/netmonitor/initializer.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 script is the entry point of Network monitor panel.
 * See README.md for more information.
 */
const { BrowserLoader } = ChromeUtils.import(
  "resource://devtools/client/shared/browser-loader.js", {});

const require = window.windowRequire = BrowserLoader({
  baseURI: "resource://devtools/client/netmonitor/",
  window,
}).require;

const EventEmitter = require("devtools/shared/old-event-emitter");
const { createFactory } = require("devtools/client/shared/vendor/react");
const { render, unmountComponentAtNode } = require("devtools/client/shared/vendor/react-dom");
const Provider = createFactory(require("devtools/client/shared/vendor/react-redux").Provider);
const { bindActionCreators } = require("devtools/client/shared/vendor/redux");
const { Connector } = require("./src/connector/index");
const { configureStore } = require("./src/create-store");
const App = createFactory(require("./src/components/App"));
const { EVENTS } = require("./src/constants");
const {
  getDisplayedRequestById,
  getSortedRequests
} = require("./src/selectors/index");

// Inject EventEmitter into global window.
EventEmitter.decorate(window);

// Configure store/state object.
let connector = new Connector();
const store = configureStore(connector);
const actions = bindActionCreators(require("./src/actions/index"), store.dispatch);

// Inject to global window for testing
window.store = store;
window.connector = connector;
window.actions = actions;

/**
 * Global Netmonitor object in this panel. This object can be consumed
 * by other panels (e.g. Console is using inspectRequest), by the
 * Launchpad (bootstrap), WebExtension API (getHAR), etc.
 */
window.Netmonitor = {
  bootstrap({ toolbox, panel }) {
    this.mount = document.querySelector("#mount");
    this.toolbox = toolbox;

    const connection = {
      tabConnection: {
        tabTarget: toolbox.target,
      },
      toolbox,
      panel,
    };

    const openLink = (link) => {
      let parentDoc = toolbox.doc;
      let iframe = parentDoc.getElementById("toolbox-panel-iframe-netmonitor");
      let top = iframe.ownerDocument.defaultView.top;
      top.openUILinkIn(link, "tab");
    };

    const openSplitConsole = (err) => {
      toolbox.openSplitConsole().then(() => {
        toolbox.target.logErrorInPage(err, "har");
      });
    };

    this.onRequestAdded = this.onRequestAdded.bind(this);
    window.on(EVENTS.REQUEST_ADDED, this.onRequestAdded);

    // Render the root Application component.
    const sourceMapService = toolbox.sourceMapURLService;
    const app = App({
      actions,
      connector,
      openLink,
      openSplitConsole,
      sourceMapService
    });
    render(Provider({ store }, app), this.mount);

    // Connect to the Firefox backend by default.
    return connector.connectFirefox(connection, actions, store.getState);
  },

  destroy() {
    unmountComponentAtNode(this.mount);
    window.off(EVENTS.REQUEST_ADDED, this.onRequestAdded);
    return connector.disconnect();
  },

  // Support for WebExtensions API

  /**
   * Support for `devtools.network.getHAR` (get collected data as HAR)
   */
  getHar() {
    let { HarExporter } = require("devtools/client/netmonitor/src/har/har-exporter");
    let state = store.getState();

    let options = {
      connector,
      items: getSortedRequests(state),
      // Always generate HAR log even if there are no requests.
      forceExport: true,
    };

    return HarExporter.getHar(options);
  },

  /**
   * Support for `devtools.network.onRequestFinished`. A hook for
   * every finished HTTP request used by WebExtensions API.
   */
  onRequestAdded(event, requestId) {
    let listeners = this.toolbox.getRequestFinishedListeners();
    if (!listeners.size) {
      return;
    }

    let { HarExporter } = require("devtools/client/netmonitor/src/har/har-exporter");
    let options = {
      connector,
      includeResponseBodies: false,
      items: [getDisplayedRequestById(store.getState(), requestId)],
    };

    // Build HAR for specified request only.
    HarExporter.getHar(options).then(har => {
      let harEntry = har.log.entries[0];
      delete harEntry.pageref;
      listeners.forEach(listener => listener({
        harEntry,
        requestId,
      }));
    });
  },

  /**
   * Support for `Request.getContent` WebExt API (lazy loading response body)
   */
  fetchResponseContent(requestId) {
    return connector.requestData(requestId, "responseContent");
  },

  /**
   * Selects the specified request in the waterfall and opens the details view.
   * This is a firefox toolbox specific API, which providing an ability to inspect
   * a network request directly from other internal toolbox panel.
   *
   * @param {string} requestId The actor ID of the request to inspect.
   * @return {object} A promise resolved once the task finishes.
   */
  inspectRequest(requestId) {
    // Look for the request in the existing ones or wait for it to appear, if
    // the network monitor is still loading.
    return new Promise((resolve) => {
      let request = null;
      let inspector = () => {
        request = getDisplayedRequestById(store.getState(), requestId);
        if (!request) {
          // Reset filters so that the request is visible.
          actions.toggleRequestFilterType("all");
          request = getDisplayedRequestById(store.getState(), requestId);
        }

        // If the request was found, select it. Otherwise this function will be
        // called again once new requests arrive.
        if (request) {
          window.off(EVENTS.REQUEST_ADDED, inspector);
          actions.selectRequest(request.id);
          resolve();
        }
      };

      inspector();

      if (!request) {
        window.on(EVENTS.REQUEST_ADDED, inspector);
      }
    });
  }
};

// Implement support for:
// chrome://devtools/content/netmonitor/index.html?type=tab&id=1234 URLs
// where 1234 is the tab id, you can retrieve from about:debugging#tabs links.
// Simply copy the id from about:devtools-toolbox?type=tab&id=1234 URLs.

// URL constructor doesn't support chrome: scheme
let href = window.location.href.replace(/chrome:/, "http://");
let url = new window.URL(href);

// If query parameters are given in a chrome tab, the inspector
// is running in standalone.
if (window.location.protocol === "chrome:" && url.search.length > 1) {
  const { targetFromURL } = require("devtools/client/framework/target-from-url");

  (async function () {
    try {
      let target = await targetFromURL(url);

      // Start the network event listening as it is done in the toolbox code
      await target.activeConsole.startListeners([
        "NetworkActivity",
      ]);

      // Create a fake toolbox object
      let toolbox = {
        target,
        viewSourceInDebugger() {
          throw new Error("toolbox.viewSourceInDebugger is not implement from a tab");
        }
      };

      window.Netmonitor.bootstrap({ toolbox });
    } catch (err) {
      window.alert("Unable to start the network monitor:" + err);
    }
  })();
}
PK
!<~g��<chrome/devtools/content/netmonitor/src/assets/icons/play.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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="10" height="12" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b">
    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
        <polygon id="Triangle-2" fill="#0A84FF" points="14 6 4 12 4 0"></polygon>
    </g>
</svg>
PK
!<N2S�44@chrome/devtools/content/netmonitor/src/assets/styles/MdnLink.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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::before {
  background-image: url(chrome://devtools/skin/images/help.svg);
}

.network-monitor .tree-container .treeTable tr .learn-more-link {
  position: absolute;
  top: 0;
  left: 0;
  padding: 0;
}

.network-monitor .tree-container .treeTable tr:not(:hover) .learn-more-link {
  opacity: 0.1;
}
PK
!<c��((Lchrome/devtools/content/netmonitor/src/assets/styles/NetworkDetailsPanel.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* Network details panel */

.network-monitor .network-details-panel {
  width: 100%;
  height: 100%;
  overflow: hidden;
}

.network-monitor .panel-container {
  height: 100%;
}

.network-monitor .panel-container,
.network-monitor .properties-view {
  display: flex;
  flex-direction: column;
  overflow-x: hidden;
  overflow-y: auto;
}

.network-monitor .panel-container .tree-container .objectBox {
  white-space: normal;
  word-wrap: break-word;
}

.network-monitor .properties-view {
  flex-grow: 1;
}

.network-monitor .properties-view .searchbox-section {
  flex: 0 1 auto;
}

.network-monitor .properties-view .devtools-searchbox {
  padding: 0;
}

.network-monitor .properties-view .devtools-searchbox input {
  margin: 1px 3px;
}

/* Empty notices in tab panels */

.network-monitor .empty-notice {
  color: var(--theme-body-color-inactive);
  padding: 3px 8px;
  text-align: center;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  font-size: 24px;
}

/* Text inputs in tab panels */

.network-monitor .textbox-input {
  text-overflow: ellipsis;
  border: none;
  background: none;
  color: inherit;
  width: 100%;
}

.network-monitor .textbox-input:focus {
  outline: 0;
  box-shadow: var(--theme-focus-box-shadow-textbox);
}

/* Tree table in tab panels */

.network-monitor .tree-container, .tree-container .treeTable {
  position: relative;
  height: 100%;
  width: 100%;
  overflow: auto;
  flex: 1;
}

.network-monitor .tree-container .treeTable,
.network-monitor .tree-container .treeTable tbody {
  display: flex;
  flex-direction: column;
}

.network-monitor .tree-container .treeTable tbody {
  /* Apply flex to table will create an anonymous table element outside of tbody
   * See also http://stackoverflow.com/a/30851678
   * Therefore, we set height with this magic number in order to remove the
   * redundant scrollbar when source editor appears.
   */
  height: calc(100% - 4px);
}

.network-monitor .tree-container .treeTable tr {
  display: block;
  position: relative;
}

/* Make right td fill available horizontal space */
.network-monitor .tree-container .treeTable td:last-child {
  width: 100%;
}

.network-monitor .properties-view .devtools-searchbox,
.network-monitor .tree-container .treeTable .tree-section {
  width: 100%;
  background-color: var(--theme-toolbar-background);
}

.network-monitor .tree-container .treeTable tr.tree-section:not(:first-child) td:not([class=""]) {
  border-top: 1px solid var(--theme-splitter-color);
}

.network-monitor .properties-view .devtools-searchbox,
.network-monitor .tree-container .treeTable tr.tree-section:not(:last-child) td:not([class=""]) {
  border-bottom: 1px solid var(--theme-splitter-color);
}

.network-monitor .tree-container .treeTable .tree-section > * {
  vertical-align: middle;
}

.network-monitor .tree-container .treeTable .treeRow.tree-section > .treeLabelCell > .treeLabel,
.network-monitor .tree-container .treeTable .treeRow.tree-section > .treeLabelCell > .treeLabel:hover,
.network-monitor .tree-container .treeTable .treeRow.tree-section > .treeValueCell:not(:hover) * {
  color: var(--theme-toolbar-color);
}

.network-monitor .tree-container .treeTable .treeValueCell {
  /* FIXME: Make value cell can be reduced to shorter width */
  max-width: 0;
  padding-inline-end: 5px;
}

.network-monitor .tree-container .treeTable .treeValueCell input{
  color: var(--string-color);
}

.network-monitor .tree-container .treeTable .treeValueCell input:focus {
  color: var(--theme-selection-color) !important;
}

.network-monitor .tree-container .treeTable .treeRow.hasChildren > .treeLabelCell > .treeLabel:hover {
  text-decoration: none;
}

/* Source editor in tab panels */

/* If there is a source editor shows up in the last row of TreeView,
 * it should occupy the available vertical space.
 */
.network-monitor .tree-container .treeTable .editor-row-container,
.network-monitor .tree-container .treeTable tr:last-child td[colspan="2"] {
  display: block;
  height: 100%;
  flex: 1;
  overflow-x: auto;
}

.network-monitor .source-editor-mount {
  width: 100%;
  height: 100%;
}

.network-monitor .headers-summary-label,
.network-monitor .tree-container .objectBox {
  white-space: nowrap;
}

/* Summary tabpanel */

.network-monitor .tabpanel-summary-container {
  padding: 1px;
}

.network-monitor .tabpanel-summary-label {
  display: inline-block;
  padding-inline-start: 4px;
  padding-inline-end: 3px;
  font-weight: 600;
}

.network-monitor .tabpanel-summary-value {
  color: inherit;
  padding-inline-start: 3px;
}

.theme-dark .network-monitor .tabpanel-summary-value {
  color: var(--theme-selection-color);
}

/* Headers tabpanel */

.network-monitor .headers-overview {
  background: var(--theme-toolbar-background);
}

.network-monitor .headers-summary,
.network-monitor .response-summary {
  display: flex;
  align-items: center;
}

.network-monitor .headers-summary .devtools-button {
  margin-inline-end: 6px;
}

.network-monitor .headers-summary .requests-list-status-icon {
  min-width: 10px;
}

.network-monitor .headers-summary .raw-headers-container {
  display: flex;
  width: 100%;
}

.network-monitor .headers-summary .raw-headers {
  width: 50%;
  padding: 0 4px;
}

.network-monitor .headers-summary .raw-headers textarea {
  width: 100%;
  height: 50vh;
  font: message-box;
  resize: none;
}

.network-monitor .headers-summary .raw-headers .tabpanel-summary-label {
  padding: 0 0 4px 0;
}

.headers-summary .textbox-input {
  margin-inline-end: 2px;
}

.network-monitor .headers-summary .status-text {
    width: auto!important;
}

/* Response tabpanel */

.network-monitor .response-error-header {
  margin: 0;
  padding: 3px 8px;
  background-color: var(--theme-highlight-red);
  color: var(--theme-selection-color);
}

.network-monitor .response-image-box {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  overflow-y: auto;
  padding: 10px;
}

.network-monitor .response-image {
  background: #fff;
  border: 1px dashed GrayText;
  margin-bottom: 10px;
  max-width: 300px;
  max-height: 100px;
}

.network-monitor .tree-container .treeTable tr.response-preview-container {
  flex: 1;
  min-height: 0;
}

.network-monitor .tree-container .treeTable tr.response-preview-container td {
  display: block;
  height: 100%;
}

.network-monitor .html-preview {
  height: 100%;
}

.network-monitor .html-preview iframe {
  background-color: #fff;
  border: none;
  height: 100%;
  width: 100%;
}

/* The editor container should only become a flex item when inside a container
 * with other flex items. In this case, the HTML preview is a flex item and we
 * can grow the editor. Otherwise, there may be overflow and
 * .editor-row-container will become 0px tall. */
.network-monitor .contains-html-preview .editor-row-container {
  flex: 1;
  min-height: 0;
}

/* Timings tabpanel */

.network-monitor .timings-container {
  display: flex;
}

.network-monitor .timings-label {
  width: 10em;
}

.network-monitor .requests-list-timings-container {
  display: flex;
  flex: 1;
  align-items: center;
}

.network-monitor .requests-list-timings-offset {
  transition: width 0.2s ease-out;
}

.network-monitor .requests-list-timings-box {
  border: none;
  min-width: 1px;
  transition: width 0.2s ease-out;
}

.theme-firebug .network-monitor .requests-list-timings-total {
  color: var(--theme-body-color);
}

/* Stack trace panel */

.network-monitor .stack-trace {
  font-family: var(--monospace-font-family);
  /* The markup contains extra whitespace to improve formatting of clipboard text.
     Make sure this whitespace doesn't affect the HTML rendering */
  white-space: normal;
  padding: 5px;
}

.network-monitor .stack-trace .frame-link-source,
.network-monitor .message-location .frame-link-source {
  /* Makes the file name truncated (and ellipsis shown) on the left side */
  direction: rtl;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.network-monitor .stack-trace .frame-link-source-inner,
.network-monitor .message-location .frame-link-source-inner {
  /* Enforce LTR direction for the file name - fixes bug 1290056 */
  direction: ltr;
  unicode-bidi: embed;
}

.network-monitor .stack-trace .frame-link-function-display-name {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  margin-inline-end: 1ch;
}

/* Security tabpanel */

/* Overwrite tree-view cell colon `:` for security panel and tree section */
.network-monitor .security-panel .treeTable .treeLabelCell::after,
.network-monitor .treeTable .tree-section .treeLabelCell::after {
  content: "";
}

/* Layout additional warning icon in tree value cell  */
.network-monitor .security-info-value {
  display: flex;
}

.network-monitor .security-warning-icon {
  background-image: url(chrome://devtools/skin/images/alerticon-warning.png);
  background-size: 13px 12px;
  margin-inline-start: 5px;
  vertical-align: top;
  width: 13px;
  height: 12px;
}

@media (min-resolution: 1.1dppx) {
  .network-monitor .security-warning-icon {
    background-image: url(chrome://devtools/skin/images/[email protected]);
  }
}

/* Custom request panel */

.network-monitor .custom-request-panel {
  height: 100%;
  overflow: auto;
  padding: 0 4px;
  background-color: var(--theme-sidebar-background);
}

.theme-dark .network-monitor .custom-request-panel {
  color: var(--theme-selection-color);
}

.network-monitor .custom-request-label {
  font-weight: 600;
}

.network-monitor .custom-request-panel textarea {
  resize: none;
  font: message-box;
}

.network-monitor .custom-header,
.network-monitor .custom-method-and-url,
.network-monitor .custom-request,
.network-monitor .custom-section {
  display: flex;
}

.network-monitor .custom-header {
  flex-grow: 1;
  font-size: 1.1em;
  padding-top: 4px;
}

.network-monitor .custom-section {
  flex-direction: column;
  margin-top: 0.5em;
}

.network-monitor .custom-method-value {
  width: 4.5em;
}

.network-monitor .custom-url-value {
  flex-grow: 1;
  margin-inline-start: 6px;
}
PK
!<�TϮ5�5Dchrome/devtools/content/netmonitor/src/assets/styles/RequestList.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* Request list empty panel */

.request-list-empty-notice {
  margin: 0;
  flex: 1;
  overflow: auto;
}

.empty-notice-element {
  padding-top: 12px;
  padding-left: 12px;
  padding-right: 12px;
  font-size: 1.2rem;
}

.notice-perf-message {
  margin-top: 2px;
  display: flex;
  align-items: center;
}

.requests-list-perf-notice-button {
  min-width: 30px;
  min-height: 26px;
  margin: 0 5px;
  vertical-align: middle;
}

.requests-list-perf-notice-button::before {
  background-image: url(chrome://devtools/skin/images/profiler-stopwatch.svg);
}

.requests-list-reload-notice-button {
  font-size: inherit;
  min-height: 26px;
  margin: 0 5px;
}

/* Requests list table */

.request-list-container {
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100%;
  overflow: hidden;
}

.requests-list-wrapper {
  width: 100%;
  height: 100%;
}

.requests-list-table {
  display: table;
  position: relative;
  width: 100%;
  height: 100%;
}

.requests-list-contents {
  display: table-row-group;
  position: absolute;
  overflow-x: hidden;
  overflow-y: auto;
  --timings-scale: 1;
  --timings-rev-scale: 1;
}

.requests-list-column {
  display: table-cell;
  cursor: default;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  vertical-align: middle;
  max-width: 50px;
  min-width: 50px;
}

.requests-list-column > * {
  display: inline-block;
}

.theme-firebug .requests-list-column {
  padding: 1px;
}

/* Requests list headers */

.requests-list-headers-wrapper {
  position: sticky;
  top: 0;
  z-index: 1;
  width: 100%;
  padding: 0;
}

.theme-firebug .requests-list-headers-wrapper {
  padding: 0 !important;
}

.requests-list-headers {
  display: table-header-group;
  height: 24px;
  padding: 0;
  width: 100%;
}

.requests-list-headers .requests-list-column:first-child .requests-list-header-button {
  border-width: 0;
}

.requests-list-header-button {
  background-color: transparent;
  border-image: linear-gradient(transparent 15%,
                                var(--theme-splitter-color) 15%,
                                var(--theme-splitter-color) 85%,
                                transparent 85%) 1 1;
  border-width: 0;
  border-inline-start-width: 1px;
  padding-inline-start: 16px;
  width: 100%;
  min-height: 23px;
  text-align: center;
  color: inherit;
}

.requests-list-header-button::-moz-focus-inner {
  border: 0;
  padding: 0;
}

.requests-list-header-button:hover {
  background-color: rgba(0, 0, 0, 0.1);
}

.requests-list-header-button > .button-text {
  display: inline-block;
  text-align: center;
  vertical-align: middle;
  /* Align button text to center */
  width: calc(100% - 8px);
  overflow: hidden;
  text-overflow: ellipsis;
}

.requests-list-header-button > .button-icon {
  display: inline-block;
  width: 7px;
  height: 4px;
  margin-inline-start: 3px;
  margin-inline-end: 6px;
  vertical-align: middle;
}

.requests-list-header-button[data-sorted=ascending] > .button-icon {
  background-image: var(--sort-ascending-image);
}

.requests-list-header-button[data-sorted=descending] > .button-icon {
  background-image: var(--sort-descending-image);
}

.requests-list-header-button[data-sorted],
.requests-list-header-button[data-sorted]:hover {
  background-color: var(--theme-selection-background);
  color: var(--theme-selection-color);
}

.requests-list-header-button[data-sorted],
.requests-list-column[data-active] + .requests-list-column .requests-list-header-button {
  border-image: linear-gradient(var(--theme-splitter-color), var(--theme-splitter-color)) 1 1;
}

.theme-firebug .requests-list-headers {
  padding: 0 !important;
  font-weight: bold;
  background: linear-gradient(rgba(255, 255, 255, 0.05),
                              rgba(0, 0, 0, 0.05)),
                              #C8D2DC;
}

.theme-firebug .requests-list-header-button {
  min-height: 17px;
}

.theme-firebug .requests-list-header-button > .button-icon {
  height: 7px;
}

.theme-firebug .requests-list-header-button[data-sorted] {
  background-color: #AAC3DC;
}

:root[platform="linux"].theme-firebug .requests-list-header-button[data-sorted] {
  background-color: #FAC8AF !important;
  color: inherit !important;
}

.theme-firebug .requests-list-header-button:hover:active {
  background-image: linear-gradient(rgba(0, 0, 0, 0.1),
                                    transparent);
}

/* Requests list column */

/* Status column */

.requests-list-status {
  min-width: 70px;
   /* Don't ellipsize status codes */
  text-overflow: initial;
}

.theme-firebug .requests-list-status {
  font-weight: bold;
}

.requests-list-status-code {
  margin-inline-start: 3px;
  padding: 0 2px;
  border-radius: 3px;
  font-family: var(--monospace-font-family);
  /* prevent status code from having full line height width from .request-list-item */
  line-height: normal;
}

.status-code[data-code^="1"] {
  background-color: var(--status-code-color-1xx);
}

.status-code[data-code^="2"] {
  background-color: var(--status-code-color-2xx);
}

.status-code[data-code^="3"] {
  background-color: var(--status-code-color-3xx);
}

.status-code[data-code^="4"] {
  background-color: var(--status-code-color-4xx);
}

.status-code[data-code^="5"] {
  background-color: var(--status-code-color-5xx);
}

.status-code:not([data-code^="3"]) {
  color: var(--theme-body-background);
}

.requests-list-status-icon {
  background: #fff;
  height: 10px;
  width: 10px;
  margin-inline-start: 5px;
  margin-inline-end: 5px;
  border-radius: 10px;
  transition: box-shadow 0.5s ease-in-out;
}

.request-list-item.selected .requests-list-status-icon {
  filter: brightness(1.3);
}

.requests-list-status-icon:not([data-code]) {
  background-color: var(--theme-content-color2);
}

.requests-list-status-icon[data-code="cached"] {
  border: 2px solid var(--theme-content-color2);
  background-color: transparent;
}

.requests-list-status-icon[data-code^="1"] {
  background-color: var(--status-code-color-1xx);
}

.requests-list-status-icon[data-code^="2"] {
  background-color: var(--status-code-color-2xx);
}

/* 3xx are triangles */
.requests-list-status-icon[data-code^="3"] {
  background-color: var(--status-code-color-3xx);
  width: 0;
  height: 0;
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
  border-bottom: 10px solid var(--theme-highlight-lightorange);
  border-radius: 0;
}

/* 4xx and 5xx are squares - error codes */
.requests-list-status-icon[data-code^="4"] {
  background-color: var(--status-code-color-4xx);
  border-radius: 0; /* squares */
}

.requests-list-status-icon[data-code^="5"] {
  background-color: var(--status-code-color-5xx);
  border-radius: 0;
  transform: rotate(45deg);
}

/* Method column */

.requests-list-method {
  min-width: 85px;
}

.theme-firebug .requests-list-method {
  color: rgb(128, 128, 128);
}

/* File column */

.requests-list-file {
  width: 40%;
}

.requests-list-file.requests-list-column {
  text-align: start;
}

/* Protocol column */

.requests-list-protocol {
  width: 8%;
}

/* Cookies column */

.requests-list-cookies {
  width: 6%;
}

/* Set Cookies column */

.requests-list-set-cookies {
  width: 8%;
}

/* Scheme column */

.requests-list-scheme {
  width: 8%;
}

/* Start Time column */

.requests-list-start-time {
  width: 8%;
}

/* End Time column */

.requests-list-end-time {
  width: 8%;
}

/* Response Time column */

.requests-list-response-time {
  width: 10%;
}

/* Duration column */

.requests-list-duration-time {
  width: 8%;
}

/* Latency column */

.requests-list-latency-time {
  width: 8%;
}

/* Response header columns */

.requests-list-response-header {
  width: 10%;
}

/* Domain column */

.requests-list-domain {
  width: 30%;
}

.requests-list-domain.requests-list-column {
  text-align: start;
}

.requests-security-state-icon {
  display: inline-block;
  width: 16px;
  height: 16px;
  margin: 0 4px;
  vertical-align: text-bottom;
}

.request-list-item.selected .requests-security-state-icon {
  filter: brightness(1.3);
}

.security-state-insecure {
  background-image: url(chrome://devtools/skin/images/security-state-insecure.svg);
}

.security-state-secure {
  background-image: url(chrome://devtools/skin/images/security-state-secure.svg);
}

.security-state-weak {
  background-image: url(chrome://devtools/skin/images/security-state-weak.svg);
}

.security-state-broken {
  background-image: url(chrome://devtools/skin/images/security-state-broken.svg);
}

.security-state-local {
  background-image: url(chrome://devtools/skin/images/globe.svg);
}

/* RemoteIP column */

.requests-list-remoteip {
  width: 9%;
}

/* Cause column */

.requests-list-cause {
  min-width: 75px;
}

.requests-list-cause-stack {
  display: inline-block;
  background-color: var(--theme-body-color-alt);
  color: var(--theme-body-background);
  font-size: 8px;
  font-weight: bold;
  line-height: 10px;
  border-radius: 3px;
  padding: 0 2px;
  margin: 0;
  margin-inline-end: 3px;
}

/* Type column */

.requests-list-type {
  min-width: 65px;
}

/* Transferred column */

.requests-list-transferred {
  min-width: 110px;
}

/* Size column */

.requests-list-size {
  min-width: 80px;
}

.theme-firebug .requests-list-size {
  justify-content: end;
  padding-inline-end: 4px;
}

/* Waterfall column */

.requests-list-waterfall {
  width: 25vw;
  max-width: 25vw;
  min-width: 25vw;
  background-repeat: repeat-y;
  background-position: left center;
  /* Background created on a <canvas> in js. */
  /* @see devtools/client/netmonitor/src/waterfall-background.js */
  background-image: -moz-element(#waterfall-background);
}

.requests-list-waterfall:dir(rtl) {
  background-position: right center;
}

.requests-list-waterfall > .requests-list-header-button {
  padding-inline-start: 0;
}

.requests-list-waterfall > .requests-list-header-button > .button-text {
  width: auto;
}

.requests-list-waterfall-label-wrapper:not(.requests-list-waterfall-visible) {
  padding-inline-start: 16px;
}

.requests-list-timings-division {
  display: inline-block;
  padding-inline-start: 4px;
  font-size: 75%;
  pointer-events: none;
  text-align: start;
}

:root[platform="win"] .requests-list-timings-division {
  padding-top: 1px;
  font-size: 90%;
}

.requests-list-timings-division:not(:first-child) {
  border-inline-start: 1px dashed;
}

.requests-list-timings-division:dir(ltr) {
  transform-origin: left center;
}

.requests-list-timings-division:dir(rtl) {
  transform-origin: right center;
}

.theme-dark .requests-list-timings-division {
  border-inline-start-color: #5a6169 !important;
}

.theme-light .requests-list-timings-division {
  border-inline-start-color: #585959 !important;
}

.requests-list-timings-division[data-division-scale=second],
.requests-list-timings-division[data-division-scale=minute] {
  font-weight: 600;
}

.requests-list-timings {
  transform: scaleX(var(--timings-scale));
}

.requests-list-timings:dir(ltr) {
  transform-origin: left center;
}

.requests-list-timings:dir(rtl) {
  transform-origin: right center;
}

.requests-list-timings-box {
  display: inline-block;
  height: 9px;
}

.theme-firebug .requests-list-timings-box {
  background-image: linear-gradient(rgba(255, 255, 255, 0.3), rgba(0, 0, 0, 0.2));
  height: 16px;
}

.requests-list-timings-box.blocked {
  background-color: var(--timing-blocked-color);
}

.requests-list-timings-box.dns {
  background-color: var(--timing-dns-color);
}

.requests-list-timings-box.connect {
  background-color: var(--timing-connect-color);
}

.requests-list-timings-box.ssl {
  background-color: var(--timing-ssl-color);
}

.requests-list-timings-box.send {
  background-color: var(--timing-send-color);
}

.requests-list-timings-box.wait {
  background-color: var(--timing-wait-color);
}

.requests-list-timings-box.receive {
  background-color: var(--timing-receive-color);
}

.requests-list-timings-total {
  display: inline-block;
  padding-inline-start: 4px;
  font-size: 85%;
  font-weight: 600;
  white-space: nowrap;
  /* This node should not be scaled - apply a reversed transformation */
  transform: scaleX(var(--timings-rev-scale));
  text-align: left;
}

.requests-list-timings-total:dir(ltr) {
  transform-origin: left center;
}

.requests-list-timings-total:dir(rtl) {
  transform-origin: right center;
}

/* Request list item */

.request-list-item {
  position: relative;
/*
  display: table-row;
  Bug 1431132: Disabling this rule is surprising as we no longer have "rows".
  But this helps preventing reflowing the whole requests list anytime we append
  a new request/row.
*/
  height: 24px;
  line-height: 24px;
}

.request-list-item.selected {
  background-color: var(--theme-selection-background);
  color: var(--theme-selection-color);
}

.request-list-item:not(.selected).odd {
  background-color: var(--table-zebra-background);
}

.request-list-item:not(.selected):hover {
  background-color: var(--theme-selection-background-hover);
}

.request-list-item.fromCache > .requests-list-column:not(.requests-list-waterfall) {
  opacity: 0.6;
}

.theme-firebug .request-list-item:not(.selected):hover {
  background: #EFEFEF;
}

/* Responsive web design support */

@media (max-width: 700px) {
  .requests-list-header-button {
    padding-inline-start: 8px;
  }

  .requests-list-status-code {
    width: auto;
  }

  .requests-list-size {
    /* Given a fix max-width to display all columns in RWD mode */
    max-width: 7%;
  }

  .requests-list-waterfall {
    display: none;
  }

  :root[platform="linux"] .requests-list-header-button {
    font-size: 85%;
  }
}
PK
!<
��JhhHchrome/devtools/content/netmonitor/src/assets/styles/StatisticsPanel.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* Statistics panel */

@import "chrome://devtools/skin/chart.css";

.statistics-panel {
  display: flex;
  height: 100vh;
}

.statistics-panel .devtools-toolbarbutton.back-button {
  min-width: 4em;
  margin: 0;
  padding: 0;
  border-radius: 0;
  border-top: none;
  border-bottom: none;
  border-inline-start: none;
}

.statistics-panel .splitter {
  border-color: rgba(0,0,0,0.2);
  cursor: default;
  pointer-events: none;
  height: 100%;
}

.statistics-panel .charts-container {
  display: flex;
  width: 100%;
}

.statistics-panel .charts,
.statistics-panel .pie-table-chart-container {
  width: 100%;
  height: 100%;
}

.statistics-panel .learn-more-link {
  font-weight: 400;
}

.statistics-panel .table-chart-title {
  display: flex;
}

.pie-table-chart-container {
  display: flex;
  justify-content: center;
  align-items: center;
}

.statistics-panel .pie-chart-container {
  margin-inline-start: 3vw;
  margin-inline-end: 1vw;
}

.statistics-panel .table-chart-container {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-width: 240px;
  margin-inline-start: 1vw;
  margin-inline-end: 3vw;
}

.chart-colored-blob[name=html] {
  fill: var(--theme-highlight-bluegrey);
  background: var(--theme-highlight-bluegrey);
}

.chart-colored-blob[name=css] {
  fill: var(--theme-highlight-blue);
  background: var(--theme-highlight-blue);
}

.chart-colored-blob[name=js] {
  fill: var(--theme-highlight-lightorange);
  background: var(--theme-highlight-lightorange);
}

.chart-colored-blob[name=xhr] {
  fill: var(--theme-highlight-orange);
  background: var(--theme-highlight-orange);
}

.chart-colored-blob[name=fonts] {
  fill: var(--theme-highlight-purple);
  background: var(--theme-highlight-purple);
}

.chart-colored-blob[name=images] {
  fill: var(--theme-highlight-pink);
  background: var(--theme-highlight-pink);
}

.chart-colored-blob[name=media] {
  fill: var(--theme-highlight-green);
  background: var(--theme-highlight-green);
}

.chart-colored-blob[name=flash] {
  fill: var(--theme-highlight-red);
  background: var(--theme-highlight-red);
}

.table-chart-row {
  display: flex;
}

.table-chart-row-label[name=cached] {
  display: none;
}

.table-chart-row-label[name=count] {
  width: 3em;
  text-align: end;
}

.table-chart-row-label[name=label] {
  width: 7em;
  text-align: start;
}

.table-chart-row-label[name=size] {
  width: 7em;
  text-align: start;
}

.table-chart-row-label[name=time] {
  width: 7em;
  text-align: start;
}

.table-chart-totals {
  display: flex;
  flex-direction: column;
}

/* Firebug theme support for statistics panel charts */

.theme-firebug .chart-colored-blob[name=html] {
  fill: rgba(94, 136, 176, 0.8); /* Blue-Grey highlight */
  background: rgba(94, 136, 176, 0.8);
}

.theme-firebug .chart-colored-blob[name=css] {
  fill: rgba(70, 175, 227, 0.8); /* light blue */
  background: rgba(70, 175, 227, 0.8);
}

.theme-firebug .chart-colored-blob[name=js] {
  fill: rgba(235, 83, 104, 0.8); /* red */
  background: rgba(235, 83, 104, 0.8);
}

.theme-firebug .chart-colored-blob[name=xhr] {
  fill: rgba(217, 102, 41, 0.8); /* orange  */
  background: rgba(217, 102, 41, 0.8);
}

.theme-firebug .chart-colored-blob[name=fonts] {
  fill: rgba(223, 128, 255, 0.8); /* pink */
  background: rgba(223, 128, 255, 0.8);
}

.theme-firebug .chart-colored-blob[name=images] {
  fill: rgba(112, 191, 83, 0.8); /* pink */
  background: rgba(112, 191, 83, 0.8);
}

.theme-firebug .chart-colored-blob[name=media] {
  fill: rgba(235, 235, 84, 0.8); /* yellow */
  background: rgba(235, 235, 84, 0.8);
}

.theme-firebug .chart-colored-blob[name=flash] {
  fill: rgba(84, 235, 159, 0.8); /* cyan */
  background: rgba(84, 235, 159, 0.8);
}

/* Responsive web design support */

@media (max-width: 700px) {
  .statistics-panel .charts-container {
    flex-direction: column;
    /* Minus 4em for statistics back button width */
    width: calc(100% - 4em);
  }

  .statistics-panel .splitter {
    width: 100%;
    height: 1px;
  }
}
PK
!<K���qqBchrome/devtools/content/netmonitor/src/assets/styles/StatusBar.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* Status bar */

.devtools-status-bar-label {
  flex: 0;
}

.status-bar-label {
  display: inline-flex;
  margin-inline-end: 10px;
  /* Status bar has just one line so, don't wrap labels */
  white-space: nowrap;
}

.status-bar-label:not(:first-of-type)::before {
  content: "";
  display: inline-block;
  margin-inline-end: 10px;
  margin-top: 4px;
  margin-bottom: 4px;
  width: 1px;
  background: var(--theme-splitter-color);
}

.status-bar-label.dom-content-loaded {
  color: var(--theme-highlight-blue);
}

.status-bar-label.load {
  color: var(--theme-highlight-red);
}

.requests-list-network-summary-button {
  display: inline-flex;
  cursor: pointer;
  height: 18px;
  background: none;
  box-shadow: none;
  border-color: transparent;
  padding-inline-end: 0;
  margin-top: 3px;
  margin-bottom: 3px;
  margin-inline-end: 1em;
}

.requests-list-network-summary-button > .summary-info-icon {
  background: url(chrome://devtools/skin/images/profiler-stopwatch.svg) no-repeat;
  -moz-context-properties: fill;
  fill: var(--theme-toolbar-color);
  width: 16px;
  height: 16px;
  opacity: 0.8;
}

.requests-list-network-summary-button:hover > .summary-info-icon {
  opacity: 1;
}
PK
!<9̑���@chrome/devtools/content/netmonitor/src/assets/styles/Toolbar.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* Toolbar */

.devtools-toolbar {
  display: flex;
}

.devtools-toolbar-container {
  height: auto;
  flex-wrap: wrap;
  justify-content: space-between;
}

.devtools-toolbar-group {
  display: flex;
  flex: 0 0 auto;
  flex-wrap: nowrap;
  align-items: center;
}

.requests-list-filter-buttons {
  display: flex;
  flex-wrap: wrap;
}

.devtools-button.devtools-pause-icon::before {
  background-image: var(--pause-icon-url);
}

.devtools-button.devtools-play-icon::before {
  background-image: var(--play-icon-url);
}

.devtools-checkbox {
  position: relative;
  vertical-align: middle;
  bottom: 1px;
}

.devtools-checkbox-label {
  margin-inline-start: 10px;
  margin-inline-end: 3px;
  white-space: nowrap;
}

/* Network details panel toggle */

.network-details-panel-toggle:dir(ltr)::before,
.network-details-panel-toggle.pane-collapsed:dir(rtl)::before {
  background-image: var(--theme-pane-collapse-image);
}

.network-details-panel-toggle.pane-collapsed:dir(ltr)::before,
.network-details-panel-toggle:dir(rtl)::before {
  background-image: var(--theme-pane-expand-image);
}

/* Responsive web design support */

@media (max-width: 700px) {
  .network-details-panel-toggle:dir(ltr)::before {
    transform: rotate(90deg);
  }

  .network-details-panel-toggle:dir(rtl)::before {
    transform: rotate(-90deg);
  }
}
PK
!<.τkhh>chrome/devtools/content/netmonitor/src/assets/styles/httpi.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 "chrome://devtools/skin/widgets.css";
@import "resource://devtools/client/shared/components/splitter/SplitBox.css";
@import "resource://devtools/client/shared/components/tree/TreeView.css";
@import "resource://devtools/client/shared/components/tabs/Tabs.css";
@import "resource://devtools/client/shared/components/tabs/TabBar.css";
@import "chrome://devtools/skin/components-frame.css";
@import "chrome://devtools/content/sourceeditor/codemirror/lib/codemirror.css";
@import "chrome://devtools/content/sourceeditor/codemirror/addon/dialog/dialog.css";
@import "chrome://devtools/content/sourceeditor/codemirror/mozilla.css";

/* Network panel components & styles */
@import "chrome://devtools/content/netmonitor/src/assets/styles/variables.css";
@import "chrome://devtools/content/netmonitor/src/assets/styles/MdnLink.css";
@import "chrome://devtools/content/netmonitor/src/assets/styles/NetworkDetailsPanel.css";
PK
!<^�<��Cchrome/devtools/content/netmonitor/src/assets/styles/netmonitor.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 "resource://devtools/client/shared/components/splitter/SplitBox.css";
@import "resource://devtools/client/shared/components/tree/TreeView.css";
@import "resource://devtools/client/shared/components/tabs/Tabs.css";
@import "resource://devtools/client/shared/components/tabs/TabBar.css";
@import "chrome://devtools/skin/components-frame.css";
@import "chrome://devtools/content/sourceeditor/codemirror/lib/codemirror.css";
@import "chrome://devtools/content/sourceeditor/codemirror/addon/dialog/dialog.css";
@import "chrome://devtools/content/sourceeditor/codemirror/mozilla.css";

/* Network panel components & styles */
@import "chrome://devtools/content/netmonitor/src/assets/styles/variables.css";
@import "chrome://devtools/content/netmonitor/src/assets/styles/MdnLink.css";
@import "chrome://devtools/content/netmonitor/src/assets/styles/Toolbar.css";
@import "chrome://devtools/content/netmonitor/src/assets/styles/StatusBar.css";
@import "chrome://devtools/content/netmonitor/src/assets/styles/RequestList.css";
@import "chrome://devtools/content/netmonitor/src/assets/styles/NetworkDetailsPanel.css";
@import "chrome://devtools/content/netmonitor/src/assets/styles/StatisticsPanel.css";

/* General */

* {
  box-sizing: border-box;
}

html,
body,
#mount,
.launchpad-root,
.network-monitor,
.monitor-panel {
  flex: initial;
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow: hidden;
}

.split-box {
  overflow: hidden;
}

/* Drag and drop HAR files */

.network-monitor > div {
  height: 100%;
}

.network-monitor .dropHarFiles {
  display: none;
  align-items: center;
  justify-content: center;
  position: absolute;
  text-align: center;
  top: 25px;
  right: 25px;
  bottom: 25px;
  left: 25px;
  z-index: 100;
  font-size: 3.5rem;
  color: gray;
  border: 4px dashed gray;
  pointer-events: none;
}

.network-monitor > div[dragging="true"] .dropHarFiles {
  display: flex;
}

.network-monitor > div[dragging="true"] {
  filter: opacity(50%);
}
PK
!<�Y�		Bchrome/devtools/content/netmonitor/src/assets/styles/variables.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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.theme-dark {
  --table-splitter-color: rgba(255,255,255,0.15);
  --table-zebra-background: rgba(255,255,255,0.05);

  --timing-blocked-color: rgba(235, 83, 104, 0.8);
  --timing-dns-color: rgba(223, 128, 255, 0.8); /* pink */
  --timing-ssl-color: rgba(217, 102, 41, 0.8); /* orange */
  --timing-connect-color: rgba(217, 102, 41, 0.8); /* orange */
  --timing-send-color: rgba(70, 175, 227, 0.8); /* light blue */
  --timing-wait-color: rgba(94, 136, 176, 0.8); /* blue grey */
  --timing-receive-color: rgba(112, 191, 83, 0.8); /* green */

  --sort-ascending-image: url(chrome://devtools/skin/images/sort-ascending-arrow.svg);
  --sort-descending-image: url(chrome://devtools/skin/images/sort-descending-arrow.svg);
}

:root.theme-light {
  --table-splitter-color: rgba(0,0,0,0.15);
  --table-zebra-background: rgba(0,0,0,0.05);

  --timing-blocked-color: rgba(235, 83, 104, 0.8);
  --timing-dns-color: rgba(223, 128, 255, 0.8); /* pink */
  --timing-ssl-color: rgba(217, 102, 41, 0.8); /* orange */
  --timing-connect-color: rgba(217, 102, 41, 0.8); /* orange */
  --timing-send-color: rgba(0, 136, 204, 0.8); /* blue */
  --timing-wait-color: rgba(95, 136, 176, 0.8); /* blue grey */
  --timing-receive-color: rgba(44, 187, 15, 0.8); /* green */

  --sort-ascending-image: url(chrome://devtools/skin/images/sort-ascending-arrow.svg);
  --sort-descending-image: url(chrome://devtools/skin/images/sort-descending-arrow.svg);
}

:root.theme-firebug {
  --sort-ascending-image: url(chrome://devtools/skin/images/firebug/arrow-up.svg);
  --sort-descending-image: url(chrome://devtools/skin/images/firebug/arrow-down.svg);
}

:root {
  /* Icons */
  --play-icon-url: url("chrome://devtools/content/netmonitor/src/assets/icons/play.svg");
  --pause-icon-url: url("chrome://devtools/skin/images/pause.svg");

  /* HTTP status codes */
  --status-code-color-1xx: var(--theme-highlight-blue);
  --status-code-color-2xx: var(--theme-highlight-green);
  --status-code-color-3xx: transparent;
  --status-code-color-4xx: var(--theme-highlight-pink);
  --status-code-color-5xx: var(--theme-highlight-red);
}
PK
!<(�]	�Q�Q=chrome/devtools/content/performance/performance-controller.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 window, document, PerformanceView, ToolbarView, RecordingsView, DetailsView */

/* exported Cc, Ci, Cu, Cr, loader, Promise */
var BrowserLoaderModule = {};
ChromeUtils.import("resource://devtools/client/shared/browser-loader.js", BrowserLoaderModule);
var { loader, require } = BrowserLoaderModule.BrowserLoader({
  baseURI: "resource://devtools/client/performance/",
  window
});
var { Task } = require("devtools/shared/task");
/* exported ViewHelpers, WidgetMethods, setNamedTimeout, clearNamedTimeout */
var { ViewHelpers, WidgetMethods, setNamedTimeout, clearNamedTimeout } = require("devtools/client/shared/widgets/view-helpers");
var { PrefObserver } = require("devtools/client/shared/prefs");
/* exported extend */
const { extend } = require("devtools/shared/extend");
// Use privileged promise in panel documents to prevent having them to freeze
// during toolbox destruction. See bug 1402779.
var Promise = require("Promise");

// Events emitted by various objects in the panel.
var EVENTS = require("devtools/client/performance/events");
Object.defineProperty(this, "EVENTS", {
  value: EVENTS,
  enumerable: true,
  writable: false
});

/* exported React, ReactDOM, JITOptimizationsView, RecordingControls, RecordingButton,
   RecordingList, RecordingListItem, Services, Waterfall, promise, EventEmitter,
   DevToolsUtils, system */
var React = require("devtools/client/shared/vendor/react");
var ReactDOM = require("devtools/client/shared/vendor/react-dom");
var Waterfall = React.createFactory(require("devtools/client/performance/components/waterfall"));
var JITOptimizationsView = React.createFactory(require("devtools/client/performance/components/jit-optimizations"));
var RecordingControls = React.createFactory(require("devtools/client/performance/components/recording-controls"));
var RecordingButton = React.createFactory(require("devtools/client/performance/components/recording-button"));
var RecordingList = React.createFactory(require("devtools/client/performance/components/recording-list"));
var RecordingListItem = React.createFactory(require("devtools/client/performance/components/recording-list-item"));

var Services = require("Services");
var promise = require("promise");
const defer = require("devtools/shared/defer");
var EventEmitter = require("devtools/shared/old-event-emitter");
var DevToolsUtils = require("devtools/shared/DevToolsUtils");
var flags = require("devtools/shared/flags");
var system = require("devtools/shared/system");

// Logic modules
/* exported L10N, PerformanceTelemetry, TIMELINE_BLUEPRINT, RecordingUtils,
   PerformanceUtils, OptimizationsGraph, GraphsController,
   MarkerDetails, MarkerBlueprintUtils, WaterfallUtils, FrameUtils, CallView, ThreadNode,
   FrameNode */
var { L10N } = require("devtools/client/performance/modules/global");
var { PerformanceTelemetry } = require("devtools/client/performance/modules/logic/telemetry");
var { TIMELINE_BLUEPRINT } = require("devtools/client/performance/modules/markers");
var RecordingUtils = require("devtools/shared/performance/recording-utils");
var PerformanceUtils = require("devtools/client/performance/modules/utils");
var { OptimizationsGraph, GraphsController } = require("devtools/client/performance/modules/widgets/graphs");
var { MarkerDetails } = require("devtools/client/performance/modules/widgets/marker-details");
var { MarkerBlueprintUtils } = require("devtools/client/performance/modules/marker-blueprint-utils");
var WaterfallUtils = require("devtools/client/performance/modules/logic/waterfall-utils");
var FrameUtils = require("devtools/client/performance/modules/logic/frame-utils");
var { CallView } = require("devtools/client/performance/modules/widgets/tree-view");
var { ThreadNode } = require("devtools/client/performance/modules/logic/tree-model");
var { FrameNode } = require("devtools/client/performance/modules/logic/tree-model");

// Widgets modules

/* exported OptionsView, FlameGraph, FlameGraphUtils, TreeWidget, SideMenuWidget */
var { OptionsView } = require("devtools/client/shared/options-view");
var { FlameGraph, FlameGraphUtils } = require("devtools/client/shared/widgets/FlameGraph");
var { TreeWidget } = require("devtools/client/shared/widgets/TreeWidget");
var { SideMenuWidget } = require("resource://devtools/client/shared/widgets/SideMenuWidget.jsm");

/* exported BRANCH_NAME */
var BRANCH_NAME = "devtools.performance.ui.";

/**
 * The current target, toolbox and PerformanceFront, set by this tool's host.
 */
/* exported gToolbox, gTarget, gFront */
var gToolbox, gTarget, gFront;

/* exported startupPerformance, shutdownPerformance, PerformanceController */

/**
 * Initializes the profiler controller and views.
 */
var startupPerformance = Task.async(function* () {
  yield PerformanceController.initialize();
  yield PerformanceView.initialize();
  PerformanceController.enableFrontEventListeners();
});

/**
 * Destroys the profiler controller and views.
 */
var shutdownPerformance = Task.async(function* () {
  yield PerformanceController.destroy();
  yield PerformanceView.destroy();
  PerformanceController.disableFrontEventListeners();
});

/**
 * Functions handling target-related lifetime events and
 * UI interaction.
 */
var PerformanceController = {
  _recordings: [],
  _currentRecording: null,

  /**
   * Listen for events emitted by the current tab target and
   * main UI events.
   */
  initialize: Task.async(function* () {
    this._telemetry = new PerformanceTelemetry(this);
    this.startRecording = this.startRecording.bind(this);
    this.stopRecording = this.stopRecording.bind(this);
    this.importRecording = this.importRecording.bind(this);
    this.exportRecording = this.exportRecording.bind(this);
    this.clearRecordings = this.clearRecordings.bind(this);
    this._onRecordingSelectFromView = this._onRecordingSelectFromView.bind(this);
    this._onPrefChanged = this._onPrefChanged.bind(this);
    this._onThemeChanged = this._onThemeChanged.bind(this);
    this._onFrontEvent = this._onFrontEvent.bind(this);
    this._pipe = this._pipe.bind(this);

    // Store data regarding if e10s is enabled.
    this._e10s = Services.appinfo.browserTabsRemoteAutostart;
    this._setMultiprocessAttributes();

    this._prefs = require("devtools/client/performance/modules/global").PREFS;
    this._prefs.registerObserver();
    this._prefs.on("pref-changed", this._onPrefChanged);

    ToolbarView.on(EVENTS.UI_PREF_CHANGED, this._onPrefChanged);
    PerformanceView.on(EVENTS.UI_START_RECORDING, this.startRecording);
    PerformanceView.on(EVENTS.UI_STOP_RECORDING, this.stopRecording);
    PerformanceView.on(EVENTS.UI_IMPORT_RECORDING, this.importRecording);
    PerformanceView.on(EVENTS.UI_CLEAR_RECORDINGS, this.clearRecordings);
    RecordingsView.on(EVENTS.UI_EXPORT_RECORDING, this.exportRecording);
    RecordingsView.on(EVENTS.UI_RECORDING_SELECTED, this._onRecordingSelectFromView);
    DetailsView.on(EVENTS.UI_DETAILS_VIEW_SELECTED, this._pipe);

    this._prefObserver = new PrefObserver("devtools.");
    this._prefObserver.on("devtools.theme", this._onThemeChanged);
  }),

  /**
   * Remove events handled by the PerformanceController
   */
  destroy: function () {
    this._telemetry.destroy();
    this._prefs.off("pref-changed", this._onPrefChanged);
    this._prefs.unregisterObserver();

    ToolbarView.off(EVENTS.UI_PREF_CHANGED, this._onPrefChanged);
    PerformanceView.off(EVENTS.UI_START_RECORDING, this.startRecording);
    PerformanceView.off(EVENTS.UI_STOP_RECORDING, this.stopRecording);
    PerformanceView.off(EVENTS.UI_IMPORT_RECORDING, this.importRecording);
    PerformanceView.off(EVENTS.UI_CLEAR_RECORDINGS, this.clearRecordings);
    RecordingsView.off(EVENTS.UI_EXPORT_RECORDING, this.exportRecording);
    RecordingsView.off(EVENTS.UI_RECORDING_SELECTED, this._onRecordingSelectFromView);
    DetailsView.off(EVENTS.UI_DETAILS_VIEW_SELECTED, this._pipe);

    this._prefObserver.off("devtools.theme", this._onThemeChanged);
    this._prefObserver.destroy();
  },

  /**
   * Enables front event listeners.
   *
   * The rationale behind this is given by the async intialization of all the
   * frontend components. Even though the panel is considered "open" only after
   * both the controller and the view are created, and even though their
   * initialization is sequential (controller, then view), the controller might
   * start handling backend events before the view finishes if the event
   * listeners are added too soon.
   */
  enableFrontEventListeners: function () {
    gFront.on("*", this._onFrontEvent);
  },

  /**
   * Disables front event listeners.
   */
  disableFrontEventListeners: function () {
    gFront.off("*", this._onFrontEvent);
  },

  /**
   * Returns the current devtools theme.
   */
  getTheme: function () {
    return Services.prefs.getCharPref("devtools.theme");
  },

  /**
   * Get a boolean preference setting from `prefName` via the underlying
   * OptionsView in the ToolbarView. This preference is guaranteed to be
   * displayed in the UI.
   *
   * @param string prefName
   * @return boolean
   */
  getOption: function (prefName) {
    return ToolbarView.optionsView.getPref(prefName);
  },

  /**
   * Get a preference setting from `prefName`. This preference can be of
   * any type and might not be displayed in the UI.
   *
   * @param string prefName
   * @return any
   */
  getPref: function (prefName) {
    return this._prefs[prefName];
  },

  /**
   * Set a preference setting from `prefName`. This preference can be of
   * any type and might not be displayed in the UI.
   *
   * @param string prefName
   * @param any prefValue
   */
  setPref: function (prefName, prefValue) {
    this._prefs[prefName] = prefValue;
  },

  /**
   * Checks whether or not a new recording is supported by the PerformanceFront.
   * @return Promise:boolean
   */
  canCurrentlyRecord: Task.async(function* () {
    let hasActor = yield gTarget.hasActor("performance");
    if (!hasActor) {
      return true;
    }
    let actorCanCheck = yield gTarget.actorHasMethod("performance", "canCurrentlyRecord");
    if (!actorCanCheck) {
      return true;
    }
    return (yield gFront.canCurrentlyRecord()).success;
  }),

  /**
   * Starts recording with the PerformanceFront.
   */
  startRecording: Task.async(function* () {
    let options = {
      withMarkers: true,
      withTicks: this.getOption("enable-framerate"),
      withMemory: this.getOption("enable-memory"),
      withFrames: true,
      withGCEvents: true,
      withAllocations: this.getOption("enable-allocations"),
      allocationsSampleProbability: this.getPref("memory-sample-probability"),
      allocationsMaxLogLength: this.getPref("memory-max-log-length"),
      bufferSize: this.getPref("profiler-buffer-size"),
      sampleFrequency: this.getPref("profiler-sample-frequency")
    };

    let recordingStarted = yield gFront.startRecording(options);

    // In some cases, like when the target has a private browsing tab,
    // recording is not currently supported because of the profiler module.
    // Present a notification in this case alerting the user of this issue.
    if (!recordingStarted) {
      this.emit(EVENTS.BACKEND_FAILED_AFTER_RECORDING_START);
      PerformanceView.setState("unavailable");
    } else {
      this.emit(EVENTS.BACKEND_READY_AFTER_RECORDING_START);
    }
  }),

  /**
   * Stops recording with the PerformanceFront.
   */
  stopRecording: Task.async(function* () {
    let recording = this.getLatestManualRecording();
    yield gFront.stopRecording(recording);
    this.emit(EVENTS.BACKEND_READY_AFTER_RECORDING_STOP);
  }),

  /**
   * Saves the given recording to a file. Emits `EVENTS.RECORDING_EXPORTED`
   * when the file was saved.
   *
   * @param PerformanceRecording recording
   *        The model that holds the recording data.
   * @param nsIFile file
   *        The file to stream the data into.
   */
  exportRecording: Task.async(function* (_, recording, file) {
    yield recording.exportRecording(file);
    this.emit(EVENTS.RECORDING_EXPORTED, recording, file);
  }),

   /**
   * Clears all completed recordings from the list as well as the current non-console
   * recording. Emits `EVENTS.RECORDING_DELETED` when complete so other components can
   * clean up.
   */
  clearRecordings: Task.async(function* () {
    for (let i = this._recordings.length - 1; i >= 0; i--) {
      let model = this._recordings[i];
      if (!model.isConsole() && model.isRecording()) {
        yield this.stopRecording();
      }
      // If last recording is not recording, but finalizing itself,
      // wait for that to finish
      if (!model.isRecording() && !model.isCompleted()) {
        yield this.waitForStateChangeOnRecording(model, "recording-stopped");
      }
      // If recording is completed,
      // clean it up from UI and remove it from the _recordings array.
      if (model.isCompleted()) {
        this.emit(EVENTS.RECORDING_DELETED, model);
        this._recordings.splice(i, 1);
      }
    }
    if (this._recordings.length > 0) {
      if (!this._recordings.includes(this.getCurrentRecording())) {
        this.setCurrentRecording(this._recordings[0]);
      }
    } else {
      this.setCurrentRecording(null);
    }
  }),

  /**
   * Loads a recording from a file, adding it to the recordings list. Emits
   * `EVENTS.RECORDING_IMPORTED` when the file was loaded.
   *
   * @param nsIFile file
   *        The file to import the data from.
   */
  importRecording: Task.async(function* (_, file) {
    let recording = yield gFront.importRecording(file);
    this._addRecordingIfUnknown(recording);

    this.emit(EVENTS.RECORDING_IMPORTED, recording);
  }),

  /**
   * Sets the currently active PerformanceRecording. Should rarely be called directly,
   * as RecordingsView handles this when manually selected a recording item. Exceptions
   * are when clearing the view.
   * @param PerformanceRecording recording
   */
  setCurrentRecording: function (recording) {
    if (this._currentRecording !== recording) {
      this._currentRecording = recording;
      this.emit(EVENTS.RECORDING_SELECTED, recording);
    }
  },

  /**
   * Gets the currently active PerformanceRecording.
   * @return PerformanceRecording
   */
  getCurrentRecording: function () {
    return this._currentRecording;
  },

  /**
   * Get most recently added recording that was triggered manually (via UI).
   * @return PerformanceRecording
   */
  getLatestManualRecording: function () {
    for (let i = this._recordings.length - 1; i >= 0; i--) {
      let model = this._recordings[i];
      if (!model.isConsole() && !model.isImported()) {
        return this._recordings[i];
      }
    }
    return null;
  },

  /**
   * Fired from RecordingsView, we listen on the PerformanceController so we can
   * set it here and re-emit on the controller, where all views can listen.
   */
  _onRecordingSelectFromView: function (_, recording) {
    this.setCurrentRecording(recording);
  },

  /**
   * Fired when the ToolbarView fires a PREF_CHANGED event.
   * with the value.
   */
  _onPrefChanged: function (_, prefName, prefValue) {
    this.emit(EVENTS.PREF_CHANGED, prefName, prefValue);
  },

  /*
   * Called when the developer tools theme changes.
   */
  _onThemeChanged: function () {
    let newValue = Services.prefs.getCharPref("devtools.theme");
    this.emit(EVENTS.THEME_CHANGED, newValue);
  },

  /**
   * Fired from the front on any event. Propagates to other handlers from here.
   */
  _onFrontEvent: function (eventName, ...data) {
    switch (eventName) {
      case "profiler-status":
        let [profilerStatus] = data;
        this.emit(EVENTS.RECORDING_PROFILER_STATUS_UPDATE, profilerStatus);
        break;
      case "recording-started":
      case "recording-stopping":
      case "recording-stopped":
        let [recordingModel] = data;
        this._addRecordingIfUnknown(recordingModel);
        this.emit(EVENTS.RECORDING_STATE_CHANGE, eventName, recordingModel);
        break;
    }
  },

  /**
   * Stores a recording internally.
   *
   * @param {PerformanceRecordingFront} recording
   */
  _addRecordingIfUnknown: function (recording) {
    if (!this._recordings.includes(recording)) {
      this._recordings.push(recording);
      this.emit(EVENTS.RECORDING_ADDED, recording);
    }
  },

  /**
   * Takes a recording and returns a value between 0 and 1 indicating how much
   * of the buffer is used.
   */
  getBufferUsageForRecording: function (recording) {
    return gFront.getBufferUsageForRecording(recording);
  },

  /**
   * Returns a boolean indicating if any recordings are currently in progress or not.
   */
  isRecording: function () {
    return this._recordings.some(r => r.isRecording());
  },

  /**
   * Returns the internal store of recording models.
   */
  getRecordings: function () {
    return this._recordings;
  },

  /**
   * Returns traits from the front.
   */
  getTraits: function () {
    return gFront.traits;
  },

  /**
   * Utility method taking a string or an array of strings of feature names (like
   * "withAllocations" or "withMarkers"), and returns whether or not the current
   * recording supports that feature, based off of UI preferences and server support.
   *
   * @option {Array<string>|string} features
   *         A string or array of strings indicating what configuration is needed on the
   *         recording model, like `withTicks`, or `withMemory`.
   *
   * @return boolean
   */
  isFeatureSupported: function (features) {
    if (!features) {
      return true;
    }

    let recording = this.getCurrentRecording();
    if (!recording) {
      return false;
    }

    let config = recording.getConfiguration();
    return [].concat(features).every(f => config[f]);
  },

  /**
   * Takes an array of PerformanceRecordingFronts and adds them to the internal
   * store of the UI. Used by the toolbox to lazily seed recordings that
   * were observed before the panel was loaded in the scenario where `console.profile()`
   * is used before the tool is loaded.
   *
   * @param {Array<PerformanceRecordingFront>} recordings
   */
  populateWithRecordings: function (recordings = []) {
    for (let recording of recordings) {
      PerformanceController._addRecordingIfUnknown(recording);
    }
    this.emit(EVENTS.RECORDINGS_SEEDED);
  },

  /**
   * Returns an object with `supported` and `enabled` properties indicating
   * whether or not the platform is capable of turning on e10s and whether or not
   * it's already enabled, respectively.
   *
   * @return {object}
   */
  getMultiprocessStatus: function () {
    // If testing, set enabled to true so we have realtime rendering tests
    // in non-e10s. This function is overridden wholesale in tests
    // when we want to test multiprocess support
    // specifically.
    if (flags.testing) {
      return { enabled: true };
    }
    // This is only checked on tool startup -- requires a restart if
    // e10s subsequently enabled.
    let enabled = this._e10s;
    return { enabled };
  },

  /**
   * Takes a PerformanceRecording and a state, and waits for
   * the event to be emitted from the front for that recording.
   *
   * @param {PerformanceRecordingFront} recording
   * @param {string} expectedState
   * @return {Promise}
   */
  waitForStateChangeOnRecording: Task.async(function* (recording, expectedState) {
    let deferred = defer();
    this.on(EVENTS.RECORDING_STATE_CHANGE, function handler(state, model) {
      if (state === expectedState && model === recording) {
        this.off(EVENTS.RECORDING_STATE_CHANGE, handler);
        deferred.resolve();
      }
    });
    yield deferred.promise;
  }),

  /**
   * Called on init, sets an `e10s` attribute on the main view container with
   * "disabled" if e10s is possible on the platform and just not on, or "unsupported"
   * if e10s is not possible on the platform. If e10s is on, no attribute is set.
   */
  _setMultiprocessAttributes: function () {
    let { enabled } = this.getMultiprocessStatus();
    if (!enabled) {
      $("#performance-view").setAttribute("e10s", "disabled");
    }
  },

  /**
   * Pipes an event from some source to the PerformanceController.
   */
  _pipe: function (eventName, ...data) {
    this.emit(eventName, ...data);
  },

  toString: () => "[object PerformanceController]"
};

/**
 * Convenient way of emitting events from the controller.
 */
EventEmitter.decorate(PerformanceController);

/**
 * DOM query helpers.
 */
/* exported $, $$ */
function $(selector, target = document) {
  return target.querySelector(selector);
}
function $$(selector, target = document) {
  return target.querySelectorAll(selector);
}
PK
!<}_�227chrome/devtools/content/performance/performance-view.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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-globals-from performance-controller.js */
/* globals OverviewView, window */
"use strict";
/**
 * Master view handler for the performance tool.
 */
var PerformanceView = {

  _state: null,

  // Set to true if the front emits a "buffer-status" event, indicating
  // that the server has support for determining buffer status.
  _bufferStatusSupported: false,

  // Mapping of state to selectors for different properties and their values,
  // from the main profiler view. Used in `PerformanceView.setState()`
  states: {
    "unavailable": [
      {
        sel: "#performance-view",
        opt: "selectedPanel",
        val: () => $("#unavailable-notice")
      },
      {
        sel: "#performance-view-content",
        opt: "hidden",
        val: () => true
      },
    ],
    "empty": [
      {
        sel: "#performance-view",
        opt: "selectedPanel",
        val: () => $("#empty-notice")
      },
      {
        sel: "#performance-view-content",
        opt: "hidden",
        val: () => true
      },
    ],
    "recording": [
      {
        sel: "#performance-view",
        opt: "selectedPanel",
        val: () => $("#performance-view-content")
      },
      {
        sel: "#performance-view-content",
        opt: "hidden",
        val: () => false
      },
      {
        sel: "#details-pane-container",
        opt: "selectedPanel",
        val: () => $("#recording-notice")
      },
    ],
    "console-recording": [
      {
        sel: "#performance-view",
        opt: "selectedPanel",
        val: () => $("#performance-view-content")
      },
      {
        sel: "#performance-view-content",
        opt: "hidden",
        val: () => false
      },
      {
        sel: "#details-pane-container",
        opt: "selectedPanel",
        val: () => $("#console-recording-notice")
      },
    ],
    "recorded": [
      {
        sel: "#performance-view",
        opt: "selectedPanel",
        val: () => $("#performance-view-content")
      },
      {
        sel: "#performance-view-content",
        opt: "hidden",
        val: () => false
      },
      {
        sel: "#details-pane-container",
        opt: "selectedPanel",
        val: () => $("#details-pane")
      },
    ],
    "loading": [
      {
        sel: "#performance-view",
        opt: "selectedPanel",
        val: () => $("#performance-view-content")
      },
      {
        sel: "#performance-view-content",
        opt: "hidden",
        val: () => false
      },
      {
        sel: "#details-pane-container",
        opt: "selectedPanel",
        val: () => $("#loading-notice")
      },
    ]
  },

  /**
   * Sets up the view with event binding and main subviews.
   */
  initialize: Task.async(function* () {
    this._onRecordButtonClick = this._onRecordButtonClick.bind(this);
    this._onImportButtonClick = this._onImportButtonClick.bind(this);
    this._onClearButtonClick = this._onClearButtonClick.bind(this);
    this._onRecordingSelected = this._onRecordingSelected.bind(this);
    this._onProfilerStatusUpdated = this._onProfilerStatusUpdated.bind(this);
    this._onRecordingStateChange = this._onRecordingStateChange.bind(this);
    this._onNewRecordingFailed = this._onNewRecordingFailed.bind(this);

    // Bind to controller events to unlock the record button
    PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
    PerformanceController.on(EVENTS.RECORDING_PROFILER_STATUS_UPDATE,
                             this._onProfilerStatusUpdated);
    PerformanceController.on(EVENTS.RECORDING_STATE_CHANGE, this._onRecordingStateChange);
    PerformanceController.on(EVENTS.RECORDING_ADDED, this._onRecordingStateChange);
    PerformanceController.on(EVENTS.BACKEND_FAILED_AFTER_RECORDING_START,
                             this._onNewRecordingFailed);

    if (yield PerformanceController.canCurrentlyRecord()) {
      this.setState("empty");
    } else {
      this.setState("unavailable");
    }

    // Initialize the ToolbarView first, because other views may need access
    // to the OptionsView via the controller, to read prefs.
    yield ToolbarView.initialize();
    yield RecordingsView.initialize();
    yield OverviewView.initialize();
    yield DetailsView.initialize();

    // DE-XUL: Begin migrating the toolbar to React. Temporarily hold state here.
    this._recordingControlsState = {
      onRecordButtonClick: this._onRecordButtonClick,
      onImportButtonClick: this._onImportButtonClick,
      onClearButtonClick: this._onClearButtonClick,
      isRecording: false,
      isDisabled: false
    };
    // Mount to an HTML element.
    const {createHtmlMount} = PerformanceUtils;
    this._recordingControlsMount = createHtmlMount($("#recording-controls-mount"));
    this._recordingButtonsMounts = Array.from($$(".recording-button-mount"))
                                        .map(createHtmlMount);

    this._renderRecordingControls();
  }),

  /**
   * DE-XUL: Render the recording controls and buttons using React.
   */
  _renderRecordingControls: function () {
    ReactDOM.render(RecordingControls(this._recordingControlsState),
                    this._recordingControlsMount);
    for (let button of this._recordingButtonsMounts) {
      ReactDOM.render(RecordingButton(this._recordingControlsState), button);
    }
  },

  /**
   * Unbinds events and destroys subviews.
   */
  destroy: Task.async(function* () {
    PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
    PerformanceController.off(EVENTS.RECORDING_PROFILER_STATUS_UPDATE,
                              this._onProfilerStatusUpdated);
    PerformanceController.off(EVENTS.RECORDING_STATE_CHANGE,
                              this._onRecordingStateChange);
    PerformanceController.off(EVENTS.RECORDING_ADDED, this._onRecordingStateChange);
    PerformanceController.off(EVENTS.BACKEND_FAILED_AFTER_RECORDING_START,
                              this._onNewRecordingFailed);

    yield ToolbarView.destroy();
    yield RecordingsView.destroy();
    yield OverviewView.destroy();
    yield DetailsView.destroy();
  }),

  /**
   * Sets the state of the profiler view. Possible options are "unavailable",
   * "empty", "recording", "console-recording", "recorded".
   */
  setState: function (state) {
    // Make sure that the focus isn't captured on a hidden iframe. This fixes a
    // XUL bug where shortcuts stop working.
    const iframes = window.document.querySelectorAll("iframe");
    for (let iframe of iframes) {
      iframe.blur();
    }
    window.focus();

    let viewConfig = this.states[state];
    if (!viewConfig) {
      throw new Error(`Invalid state for PerformanceView: ${state}`);
    }
    for (let { sel, opt, val } of viewConfig) {
      for (let el of $$(sel)) {
        el[opt] = val();
      }
    }

    this._state = state;

    if (state === "console-recording") {
      let recording = PerformanceController.getCurrentRecording();
      let label = recording.getLabel() || "";

      // Wrap the label in quotes if it exists for the commands.
      label = label ? `"${label}"` : "";

      let startCommand = $(".console-profile-recording-notice .console-profile-command");
      let stopCommand = $(".console-profile-stop-notice .console-profile-command");

      startCommand.value = `console.profile(${label})`;
      stopCommand.value = `console.profileEnd(${label})`;
    }

    this.updateBufferStatus();
    this.emit(EVENTS.UI_STATE_CHANGED, state);
  },

  /**
   * Returns the state of the PerformanceView.
   */
  getState: function () {
    return this._state;
  },

  /**
   * Updates the displayed buffer status.
   */
  updateBufferStatus: function () {
    // If we've never seen a "buffer-status" event from the front, ignore
    // and keep the buffer elements hidden.
    if (!this._bufferStatusSupported) {
      return;
    }

    let recording = PerformanceController.getCurrentRecording();
    if (!recording || !recording.isRecording()) {
      return;
    }

    let bufferUsage = PerformanceController.getBufferUsageForRecording(recording) || 0;

    // Normalize to a percentage value
    let percent = Math.floor(bufferUsage * 100);

    let $container = $("#details-pane-container");
    let $bufferLabel = $(".buffer-status-message", $container.selectedPanel);

    // Be a little flexible on the buffer status, although not sure how
    // this could happen, as RecordingModel clamps.
    if (percent >= 99) {
      $container.setAttribute("buffer-status", "full");
    } else {
      $container.setAttribute("buffer-status", "in-progress");
    }

    $bufferLabel.value = L10N.getFormatStr("profiler.bufferFull", percent);
    this.emit(EVENTS.UI_RECORDING_PROFILER_STATUS_RENDERED, percent);
  },

  /**
   * Toggles the `locked` attribute on the record buttons based
   * on `lock`.
   *
   * @param {boolean} lock
   */
  _lockRecordButtons: function (lock) {
    this._recordingControlsState.isLocked = lock;
    this._renderRecordingControls();
  },

  /*
   * Toggles the `checked` attribute on the record buttons based
   * on `activate`.
   *
   * @param {boolean} activate
   */
  _toggleRecordButtons: function (activate) {
    this._recordingControlsState.isRecording = activate;
    this._renderRecordingControls();
  },

  /**
   * When a recording has started.
   */
  _onRecordingStateChange: function () {
    let currentRecording = PerformanceController.getCurrentRecording();
    let recordings = PerformanceController.getRecordings();

    this._toggleRecordButtons(!!recordings.find(r => !r.isConsole() && r.isRecording()));
    this._lockRecordButtons(!!recordings.find(r => !r.isConsole() && r.isFinalizing()));

    if (currentRecording && currentRecording.isFinalizing()) {
      this.setState("loading");
    }
    if (currentRecording && currentRecording.isCompleted()) {
      this.setState("recorded");
    }
    if (currentRecording && currentRecording.isRecording()) {
      this.updateBufferStatus();
    }
  },

  /**
   * When starting a recording has failed.
   */
  _onNewRecordingFailed: function (e) {
    this._lockRecordButtons(false);
    this._toggleRecordButtons(false);
  },

  /**
   * Handler for clicking the clear button.
   */
  _onClearButtonClick: function (e) {
    this.emit(EVENTS.UI_CLEAR_RECORDINGS);
  },

  /**
   * Handler for clicking the record button.
   */
  _onRecordButtonClick: function (e) {
    if (this._recordingControlsState.isRecording) {
      this.emit(EVENTS.UI_STOP_RECORDING);
    } else {
      this._lockRecordButtons(true);
      this._toggleRecordButtons(true);
      this.emit(EVENTS.UI_START_RECORDING);
    }
  },

  /**
   * Handler for clicking the import button.
   */
  _onImportButtonClick: function (e) {
    let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
    fp.init(window, L10N.getStr("recordingsList.importDialogTitle"),
            Ci.nsIFilePicker.modeOpen);
    fp.appendFilter(L10N.getStr("recordingsList.saveDialogJSONFilter"), "*.json");
    fp.appendFilter(L10N.getStr("recordingsList.saveDialogAllFilter"), "*.*");

    fp.open(rv => {
      if (rv == Ci.nsIFilePicker.returnOK) {
        this.emit(EVENTS.UI_IMPORT_RECORDING, fp.file);
      }
    });
  },

  /**
   * Fired when a recording is selected. Used to toggle the profiler view state.
   */
  _onRecordingSelected: function (_, recording) {
    if (!recording) {
      this.setState("empty");
    } else if (recording.isRecording() && recording.isConsole()) {
      this.setState("console-recording");
    } else if (recording.isRecording()) {
      this.setState("recording");
    } else {
      this.setState("recorded");
    }
  },

  /**
   * Fired when the controller has updated information on the buffer's status.
   * Update the buffer status display if shown.
   */
  _onProfilerStatusUpdated: function (_, profilerStatus) {
    // We only care about buffer status here, so check to see
    // if it has position.
    if (!profilerStatus || profilerStatus.position === void 0) {
      return;
    }
    // If this is our first buffer event, set the status and add a class
    if (!this._bufferStatusSupported) {
      this._bufferStatusSupported = true;
      $("#details-pane-container").setAttribute("buffer-status", "in-progress");
    }

    if (!this.getState("recording") && !this.getState("console-recording")) {
      return;
    }

    this.updateBufferStatus();
  },

  toString: () => "[object PerformanceView]"
};

/**
 * Convenient way of emitting events from the view.
 */
EventEmitter.decorate(PerformanceView);
PK
!<��v��C�C3chrome/devtools/content/performance/performance.xul<?xml version="1.0" encoding="utf-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/. -->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/content/shared/widgets/widgets.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/widgets.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/performance.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/jit-optimizations.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/components-frame.css" type="text/css"?>
<!DOCTYPE window [
  <!ENTITY % performanceDTD SYSTEM "chrome://devtools/locale/performance.dtd">
  %performanceDTD;
]>

<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        xmlns:html="http://www.w3.org/1999/xhtml">
  <script src="chrome://devtools/content/shared/theme-switching.js"/>
  <script type="application/javascript" src="performance-controller.js"/>
  <script type="application/javascript" src="performance-view.js"/>
  <script type="application/javascript" src="views/overview.js"/>
  <script type="application/javascript" src="views/toolbar.js"/>
  <script type="application/javascript" src="views/details-abstract-subview.js"/>
  <script type="application/javascript" src="views/details-waterfall.js"/>
  <script type="application/javascript" src="views/details-js-call-tree.js"/>
  <script type="application/javascript" src="views/details-js-flamegraph.js"/>
  <script type="application/javascript" src="views/details-memory-call-tree.js"/>
  <script type="application/javascript" src="views/details-memory-flamegraph.js"/>
  <script type="application/javascript" src="views/details.js"/>
  <script type="application/javascript" src="views/recordings.js"/>

  <popupset id="performance-options-popupset">
    <menupopup id="performance-filter-menupopup" position="before_start"/>
    <menupopup id="performance-options-menupopup" position="before_end">
      <menuitem id="option-show-platform-data"
                type="checkbox"
                data-pref="show-platform-data"
                label="&performanceUI.showPlatformData;"
                tooltiptext="&performanceUI.showPlatformData.tooltiptext;"/>
      <menuitem id="option-show-jit-optimizations"
                class="experimental-option"
                type="checkbox"
                data-pref="show-jit-optimizations"
                label="&performanceUI.showJITOptimizations;"
                tooltiptext="&performanceUI.showJITOptimizations.tooltiptext;"/>
      <menuitem id="option-enable-memory"
                class="experimental-option"
                type="checkbox"
                data-pref="enable-memory"
                label="&performanceUI.enableMemory;"
                tooltiptext="&performanceUI.enableMemory.tooltiptext;"/>
      <menuitem id="option-enable-allocations"
                type="checkbox"
                data-pref="enable-allocations"
                label="&performanceUI.enableAllocations;"
                tooltiptext="&performanceUI.enableAllocations.tooltiptext;"/>
      <menuitem id="option-enable-framerate"
                type="checkbox"
                data-pref="enable-framerate"
                label="&performanceUI.enableFramerate;"
                tooltiptext="&performanceUI.enableFramerate.tooltiptext;"/>
      <menuitem id="option-invert-call-tree"
                type="checkbox"
                data-pref="invert-call-tree"
                label="&performanceUI.invertTree;"
                tooltiptext="&performanceUI.invertTree.tooltiptext;"/>
      <menuitem id="option-invert-flame-graph"
                type="checkbox"
                data-pref="invert-flame-graph"
                label="&performanceUI.invertFlameGraph;"
                tooltiptext="&performanceUI.invertFlameGraph.tooltiptext;"/>
      <menuitem id="option-flatten-tree-recursion"
                type="checkbox"
                data-pref="flatten-tree-recursion"
                label="&performanceUI.flattenTreeRecursion;"
                tooltiptext="&performanceUI.flattenTreeRecursion.tooltiptext;"/>
    </menupopup>
  </popupset>

  <hbox id="body" class="theme-body performance-tool" flex="1">

    <!-- Sidebar: controls and recording list -->
    <vbox id="recordings-pane">
      <hbox id="recordings-controls">
        <html:div id='recording-controls-mount'/>
      </hbox>
      <vbox id="recordings-list" class="theme-sidebar" flex="1">
        <html:div id="recording-list-mount"/>
      </vbox>
    </vbox>

    <!-- Main panel content -->
    <vbox id="performance-pane" flex="1">

      <!-- Top toolbar controls -->
      <toolbar id="performance-toolbar"
               class="devtools-toolbar">
        <hbox id="performance-toolbar-controls-other"
              class="devtools-toolbarbutton-group">
          <toolbarbutton id="filter-button"
                         class="devtools-toolbarbutton"
                         popup="performance-filter-menupopup"
                         tooltiptext="&performanceUI.options.filter.tooltiptext;"/>
        </hbox>
        <hbox id="performance-toolbar-controls-detail-views"
              class="devtools-toolbarbutton-group">
          <toolbarbutton id="select-waterfall-view"
                         class="devtools-toolbarbutton"
                         label="&performanceUI.toolbar.waterfall;"
                         hidden="true"
                         data-view="waterfall"
                         tooltiptext="&performanceUI.toolbar.waterfall.tooltiptext;" />
          <toolbarbutton id="select-js-calltree-view"
                         class="devtools-toolbarbutton"
                         label="&performanceUI.toolbar.js-calltree;"
                         hidden="true"
                         data-view="js-calltree"
                         tooltiptext="&performanceUI.toolbar.js-calltree.tooltiptext;" />
          <toolbarbutton id="select-js-flamegraph-view"
                         class="devtools-toolbarbutton"
                         label="&performanceUI.toolbar.js-flamegraph;"
                         hidden="true"
                         data-view="js-flamegraph"
                         tooltiptext="&performanceUI.toolbar.js-flamegraph.tooltiptext;" />
          <toolbarbutton id="select-memory-calltree-view"
                         class="devtools-toolbarbutton"
                         label="&performanceUI.toolbar.memory-calltree;"
                         hidden="true"
                         data-view="memory-calltree"
                         tooltiptext="&performanceUI.toolbar.allocations.tooltiptext;" />
          <toolbarbutton id="select-memory-flamegraph-view"
                         class="devtools-toolbarbutton"
                         label="&performanceUI.toolbar.memory-flamegraph;"
                         hidden="true"
                         data-view="memory-flamegraph" />
        </hbox>
        <spacer flex="1"></spacer>
        <hbox id="performance-toolbar-controls-options"
              class="devtools-toolbarbutton-group">
          <toolbarbutton id="performance-options-button"
                         class="devtools-toolbarbutton devtools-option-toolbarbutton"
                         popup="performance-options-menupopup"
                         tooltiptext="&performanceUI.options.gear.tooltiptext;"/>
        </hbox>
      </toolbar>

      <!-- Recording contents and general notice messages -->
      <deck id="performance-view" flex="1">

        <!-- A default notice, shown while initially opening the tool.
             Keep this element the first child of #performance-view. -->
        <hbox id="tool-loading-notice"
              class="notice-container"
              flex="1">
        </hbox>

        <!-- "Unavailable" notice, shown when the entire tool is disabled,
             for example, when in private browsing mode. -->
        <vbox id="unavailable-notice"
              class="notice-container"
              align="center"
              pack="center"
              flex="1">
          <hbox pack="center">
            <html:div class='recording-button-mount'/>
          </hbox>
          <description class="tool-disabled-message">
            &performanceUI.unavailableNoticePB;
          </description>
        </vbox>

        <!-- "Empty" notice, shown when there's no recordings available -->
        <hbox id="empty-notice"
              class="notice-container"
              align="center"
              pack="center"
              flex="1">
          <hbox pack="center">
            <html:div class='recording-button-mount'/>
          </hbox>
        </hbox>

        <!-- Recording contents -->
        <vbox id="performance-view-content" flex="1">

          <!-- Overview graphs -->
          <vbox id="overview-pane">
            <hbox id="markers-overview"/>
            <hbox id="memory-overview"/>
            <hbox id="time-framerate"/>
          </vbox>

          <!-- Detail views and specific notice messages -->
          <deck id="details-pane-container" flex="1">

            <!-- "Loading" notice, shown when a recording is being loaded -->
            <hbox id="loading-notice"
                  class="notice-container devtools-throbber"
                  align="center"
                  pack="center"
                  flex="1">
              <label value="&performanceUI.loadingNotice;"/>
            </hbox>

            <!-- "Recording" notice, shown when a recording is in progress -->
            <vbox id="recording-notice"
                  class="notice-container"
                  align="center"
                  pack="center"
                  flex="1">
              <hbox pack="center">
                <html:div class='recording-button-mount'/>
              </hbox>
              <label class="realtime-disabled-on-e10s-message"
                     value="&performanceUI.disabledRealTime.disabledE10S;"/>
              <label class="buffer-status-message"
                     tooltiptext="&performanceUI.bufferStatusTooltip;"/>
              <label class="buffer-status-message-full"
                     value="&performanceUI.bufferStatusFull;"/>
            </vbox>

            <!-- "Console" notice, shown when a console recording is in progress -->
            <vbox id="console-recording-notice"
                  class="notice-container"
                  align="center"
                  pack="center"
                  flex="1">
              <hbox class="console-profile-recording-notice">
                <label value="&performanceUI.console.recordingNoticeStart;" />
                <label class="console-profile-command" />
                <label value="&performanceUI.console.recordingNoticeEnd;" />
              </hbox>
              <hbox class="console-profile-stop-notice">
                <label value="&performanceUI.console.stopCommandStart;" />
                <label class="console-profile-command" />
                <label value="&performanceUI.console.stopCommandEnd;" />
              </hbox>
              <label class="realtime-disabled-on-e10s-message"
                     value="&performanceUI.disabledRealTime.disabledE10S;"/>
              <label class="buffer-status-message"
                     tooltiptext="&performanceUI.bufferStatusTooltip;"/>
              <label class="buffer-status-message-full"
                     value="&performanceUI.bufferStatusFull;"/>
            </vbox>

            <!-- Detail views -->
            <deck id="details-pane" flex="1">

              <!-- Waterfall -->
              <hbox id="waterfall-view" flex="1">
                <html:div xmlns="http://www.w3.org/1999/xhtml" id="waterfall-tree" />
                <splitter class="devtools-side-splitter"/>
                <vbox id="waterfall-details"
                      class="theme-sidebar"/>
              </hbox>

              <!-- JS Tree and JIT view -->
              <hbox id="js-profile-view" flex="1">
                <vbox id="js-calltree-view" flex="1">
                  <hbox class="call-tree-headers-container">
                    <label class="plain call-tree-header"
                           type="duration"
                           crop="end"
                           value="&performanceUI.table.totalDuration;"
                           tooltiptext="&performanceUI.table.totalDuration.tooltip;"/>
                    <label class="plain call-tree-header"
                           type="percentage"
                           crop="end"
                           value="&performanceUI.table.totalPercentage;"
                           tooltiptext="&performanceUI.table.totalPercentage.tooltip;"/>
                    <label class="plain call-tree-header"
                           type="self-duration"
                           crop="end"
                           value="&performanceUI.table.selfDuration;"
                           tooltiptext="&performanceUI.table.selfDuration.tooltip;"/>
                    <label class="plain call-tree-header"
                           type="self-percentage"
                           crop="end"
                           value="&performanceUI.table.selfPercentage;"
                           tooltiptext="&performanceUI.table.selfPercentage.tooltip;"/>
                    <label class="plain call-tree-header"
                           type="samples"
                           crop="end"
                           value="&performanceUI.table.samples;"
                           tooltiptext="&performanceUI.table.samples.tooltip;"/>
                    <label class="plain call-tree-header"
                           type="function"
                           crop="end"
                           value="&performanceUI.table.function;"
                           tooltiptext="&performanceUI.table.function.tooltip;"/>
                  </hbox>
                  <vbox class="call-tree-cells-container" flex="1"/>
                </vbox>
                <splitter class="devtools-side-splitter"/>
                <!-- Optimizations Panel -->
                <vbox id="jit-optimizations-view"
                  class="hidden">
                </vbox>
              </hbox>

              <!-- JS FlameChart -->
              <hbox id="js-flamegraph-view" flex="1">
              </hbox>

              <!-- Memory Tree -->
              <vbox id="memory-calltree-view" flex="1">
                <hbox class="call-tree-headers-container">
                  <label class="plain call-tree-header"
                         type="self-size"
                         crop="end"
                         value="Self Bytes"
                         tooltiptext="&performanceUI.table.totalAlloc.tooltip;"/>
                  <label class="plain call-tree-header"
                         type="self-size-percentage"
                         crop="end"
                         value="Self Bytes %"
                         tooltiptext="&performanceUI.table.totalAlloc.tooltip;"/>
                  <label class="plain call-tree-header"
                         type="self-count"
                         crop="end"
                         value="Self Count"
                         tooltiptext="&performanceUI.table.totalAlloc.tooltip;"/>
                  <label class="plain call-tree-header"
                         type="self-count-percentage"
                         crop="end"
                         value="Self Count %"
                         tooltiptext="&performanceUI.table.totalAlloc.tooltip;"/>
                  <label class="plain call-tree-header"
                         type="size"
                         crop="end"
                         value="Total Size"
                         tooltiptext="&performanceUI.table.totalAlloc.tooltip;"/>
                  <label class="plain call-tree-header"
                         type="size-percentage"
                         crop="end"
                         value="Total Size %"
                         tooltiptext="&performanceUI.table.totalAlloc.tooltip;"/>
                  <label class="plain call-tree-header"
                         type="count"
                         crop="end"
                         value="Total Count"
                         tooltiptext="&performanceUI.table.totalAlloc.tooltip;"/>
                  <label class="plain call-tree-header"
                         type="count-percentage"
                         crop="end"
                         value="Total Count %"
                         tooltiptext="&performanceUI.table.totalAlloc.tooltip;"/>
                  <label class="plain call-tree-header"
                         type="function"
                         crop="end"
                         value="&performanceUI.table.function;"/>
                </hbox>
                <vbox class="call-tree-cells-container" flex="1"/>
              </vbox>

              <!-- Memory FlameChart -->
              <hbox id="memory-flamegraph-view" flex="1"></hbox>
            </deck>
          </deck>
        </vbox>
      </deck>
    </vbox>
  </hbox>
</window>
PK
!<\�-(��Echrome/devtools/content/performance/views/details-abstract-subview.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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-globals-from ../performance-controller.js */
/* import-globals-from ../performance-view.js */
/* exported DetailsSubview */
"use strict";

/**
 * A base class from which all detail views inherit.
 */
var DetailsSubview = {
  /**
   * Sets up the view with event binding.
   */
  initialize: function () {
    this._onRecordingStoppedOrSelected = this._onRecordingStoppedOrSelected.bind(this);
    this._onOverviewRangeChange = this._onOverviewRangeChange.bind(this);
    this._onDetailsViewSelected = this._onDetailsViewSelected.bind(this);
    this._onPrefChanged = this._onPrefChanged.bind(this);

    PerformanceController.on(EVENTS.RECORDING_STATE_CHANGE,
                             this._onRecordingStoppedOrSelected);
    PerformanceController.on(EVENTS.RECORDING_SELECTED,
                             this._onRecordingStoppedOrSelected);
    PerformanceController.on(EVENTS.PREF_CHANGED, this._onPrefChanged);
    OverviewView.on(EVENTS.UI_OVERVIEW_RANGE_SELECTED, this._onOverviewRangeChange);
    DetailsView.on(EVENTS.UI_DETAILS_VIEW_SELECTED, this._onDetailsViewSelected);

    let self = this;
    let originalRenderFn = this.render;
    let afterRenderFn = () => {
      this._wasRendered = true;
    };

    this.render = Task.async(function* (...args) {
      let maybeRetval = yield originalRenderFn.apply(self, args);
      afterRenderFn();
      return maybeRetval;
    });
  },

  /**
   * Unbinds events.
   */
  destroy: function () {
    clearNamedTimeout("range-change-debounce");

    PerformanceController.off(EVENTS.RECORDING_STATE_CHANGE,
                              this._onRecordingStoppedOrSelected);
    PerformanceController.off(EVENTS.RECORDING_SELECTED,
                              this._onRecordingStoppedOrSelected);
    PerformanceController.off(EVENTS.PREF_CHANGED, this._onPrefChanged);
    OverviewView.off(EVENTS.UI_OVERVIEW_RANGE_SELECTED, this._onOverviewRangeChange);
    DetailsView.off(EVENTS.UI_DETAILS_VIEW_SELECTED, this._onDetailsViewSelected);
  },

  /**
   * Returns true if this view was rendered at least once.
   */
  get wasRenderedAtLeastOnce() {
    return !!this._wasRendered;
  },

  /**
   * Amount of time (in milliseconds) to wait until this view gets updated,
   * when the range is changed in the overview.
   */
  rangeChangeDebounceTime: 0,

  /**
   * When the overview range changes, all details views will require a
   * rerendering at a later point, determined by `shouldUpdateWhenShown` and
   * `canUpdateWhileHidden` and whether or not its the current view.
   * Set `requiresUpdateOnRangeChange` to false to not invalidate the view
   * when the range changes.
   */
  requiresUpdateOnRangeChange: true,

  /**
   * Flag specifying if this view should be updated when selected. This will
   * be set to true, for example, when the range changes in the overview and
   * this view is not currently visible.
   */
  shouldUpdateWhenShown: false,

  /**
   * Flag specifying if this view may get updated even when it's not selected.
   * Should only be used in tests.
   */
  canUpdateWhileHidden: false,

  /**
   * An array of preferences under `devtools.performance.ui.` that the view should
   * rerender and callback `this._onRerenderPrefChanged` upon change.
   */
  rerenderPrefs: [],

  /**
   * An array of preferences under `devtools.performance.` that the view should
   * observe and callback `this._onObservedPrefChange` upon change.
   */
  observedPrefs: [],

  /**
   * Flag specifying if this view should update while the overview selection
   * area is actively being dragged by the mouse.
   */
  shouldUpdateWhileMouseIsActive: false,

  /**
   * Called when recording stops or is selected.
   */
  _onRecordingStoppedOrSelected: function (_, state, recording) {
    if (typeof state !== "string") {
      recording = state;
    }
    if (arguments.length === 3 && state !== "recording-stopped") {
      return;
    }

    if (!recording || !recording.isCompleted()) {
      return;
    }
    if (DetailsView.isViewSelected(this) || this.canUpdateWhileHidden) {
      this.render(OverviewView.getTimeInterval());
    } else {
      this.shouldUpdateWhenShown = true;
    }
  },

  /**
   * Fired when a range is selected or cleared in the OverviewView.
   */
  _onOverviewRangeChange: function (_, interval) {
    if (!this.requiresUpdateOnRangeChange) {
      return;
    }
    if (DetailsView.isViewSelected(this)) {
      let debounced = () => {
        if (!this.shouldUpdateWhileMouseIsActive && OverviewView.isMouseActive) {
          // Don't render yet, while the selection is still being dragged.
          setNamedTimeout("range-change-debounce", this.rangeChangeDebounceTime,
                          debounced);
        } else {
          this.render(interval);
        }
      };
      setNamedTimeout("range-change-debounce", this.rangeChangeDebounceTime, debounced);
    } else {
      this.shouldUpdateWhenShown = true;
    }
  },

  /**
   * Fired when a view is selected in the DetailsView.
   */
  _onDetailsViewSelected: function () {
    if (DetailsView.isViewSelected(this) && this.shouldUpdateWhenShown) {
      this.render(OverviewView.getTimeInterval());
      this.shouldUpdateWhenShown = false;
    }
  },

  /**
   * Fired when a preference in `devtools.performance.ui.` is changed.
   */
  _onPrefChanged: function (_, prefName, prefValue) {
    if (~this.observedPrefs.indexOf(prefName) && this._onObservedPrefChange) {
      this._onObservedPrefChange(prefName);
    }

    // All detail views require a recording to be complete, so do not
    // attempt to render if recording is in progress or does not exist.
    let recording = PerformanceController.getCurrentRecording();
    if (!recording || !recording.isCompleted()) {
      return;
    }

    if (!~this.rerenderPrefs.indexOf(prefName)) {
      return;
    }

    if (this._onRerenderPrefChanged) {
      this._onRerenderPrefChanged(prefName);
    }

    if (DetailsView.isViewSelected(this) || this.canUpdateWhileHidden) {
      this.render(OverviewView.getTimeInterval());
    } else {
      this.shouldUpdateWhenShown = true;
    }
  }
};
PK
!<r��9//Achrome/devtools/content/performance/views/details-js-call-tree.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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-globals-from ../performance-controller.js */
/* import-globals-from ../performance-view.js */
/* globals DetailsSubview */
"use strict";

/**
 * CallTree view containing profiler call tree, controlled by DetailsView.
 */
var JsCallTreeView = extend(DetailsSubview, {

  rerenderPrefs: [
    "invert-call-tree",
    "show-platform-data",
    "flatten-tree-recursion",
    "show-jit-optimizations",
  ],

  // Units are in milliseconds.
  rangeChangeDebounceTime: 75,

  /**
   * Sets up the view with event binding.
   */
  initialize: function () {
    DetailsSubview.initialize.call(this);

    this._onLink = this._onLink.bind(this);
    this._onFocus = this._onFocus.bind(this);

    this.container = $("#js-calltree-view .call-tree-cells-container");

    this.optimizationsElement = $("#jit-optimizations-view");
  },

  /**
   * Unbinds events.
   */
  destroy: function () {
    ReactDOM.unmountComponentAtNode(this.optimizationsElement);
    this.optimizationsElement = null;
    this.container = null;
    this.threadNode = null;
    DetailsSubview.destroy.call(this);
  },

  /**
   * Method for handling all the set up for rendering a new call tree.
   *
   * @param object interval [optional]
   *        The { startTime, endTime }, in milliseconds.
   */
  render: function (interval = {}) {
    let recording = PerformanceController.getCurrentRecording();
    let profile = recording.getProfile();
    let showOptimizations = PerformanceController.getOption("show-jit-optimizations");

    let options = {
      contentOnly: !PerformanceController.getOption("show-platform-data"),
      invertTree: PerformanceController.getOption("invert-call-tree"),
      flattenRecursion: PerformanceController.getOption("flatten-tree-recursion"),
      showOptimizationHint: showOptimizations
    };
    let threadNode = this.threadNode = this._prepareCallTree(profile, interval, options);
    this._populateCallTree(threadNode, options);

    // For better or worse, re-rendering loses frame selection,
    // so we should always hide opts on rerender
    this.hideOptimizations();

    this.emit(EVENTS.UI_JS_CALL_TREE_RENDERED);
  },

  showOptimizations: function () {
    this.optimizationsElement.classList.remove("hidden");
  },

  hideOptimizations: function () {
    this.optimizationsElement.classList.add("hidden");
  },

  _onFocus: function (treeItem) {
    let showOptimizations = PerformanceController.getOption("show-jit-optimizations");
    let frameNode = treeItem.frame;
    let optimizationSites = frameNode && frameNode.hasOptimizations()
                            ? frameNode.getOptimizations().optimizationSites
                            : [];

    if (!showOptimizations || !frameNode || optimizationSites.length === 0) {
      this.hideOptimizations();
      this.emit("focus", treeItem);
      return;
    }

    this.showOptimizations();

    let frameData = frameNode.getInfo();
    let optimizations = JITOptimizationsView({
      frameData,
      optimizationSites,
      onViewSourceInDebugger: (url, line) => {
        gToolbox.viewSourceInDebugger(url, line).then(success => {
          if (success) {
            this.emit(EVENTS.SOURCE_SHOWN_IN_JS_DEBUGGER);
          } else {
            this.emit(EVENTS.SOURCE_NOT_FOUND_IN_JS_DEBUGGER);
          }
        });
      }
    });

    ReactDOM.render(optimizations, this.optimizationsElement);

    this.emit("focus", treeItem);
  },

  /**
   * Fired on the "link" event for the call tree in this container.
   */
  _onLink: function (_, treeItem) {
    let { url, line } = treeItem.frame.getInfo();
    gToolbox.viewSourceInDebugger(url, line).then(success => {
      if (success) {
        this.emit(EVENTS.SOURCE_SHOWN_IN_JS_DEBUGGER);
      } else {
        this.emit(EVENTS.SOURCE_NOT_FOUND_IN_JS_DEBUGGER);
      }
    });
  },

  /**
   * Called when the recording is stopped and prepares data to
   * populate the call tree.
   */
  _prepareCallTree: function (profile, { startTime, endTime }, options) {
    let thread = profile.threads[0];
    let { contentOnly, invertTree, flattenRecursion } = options;
    let threadNode = new ThreadNode(thread, { startTime, endTime, contentOnly, invertTree,
                                              flattenRecursion });

    // Real profiles from nsProfiler (i.e. not synthesized from allocation
    // logs) always have a (root) node. Go down one level in the uninverted
    // view to avoid displaying both the synthesized root node and the (root)
    // node from the profiler.
    if (!invertTree) {
      threadNode.calls = threadNode.calls[0].calls;
    }

    return threadNode;
  },

  /**
   * Renders the call tree.
   */
  _populateCallTree: function (frameNode, options = {}) {
    // If we have an empty profile (no samples), then don't invert the tree, as
    // it would hide the root node and a completely blank call tree space can be
    // mis-interpreted as an error.
    let inverted = options.invertTree && frameNode.samples > 0;

    let root = new CallView({
      frame: frameNode,
      inverted: inverted,
      // The synthesized root node is hidden in inverted call trees.
      hidden: inverted,
      // Call trees should only auto-expand when not inverted. Passing undefined
      // will default to the CALL_TREE_AUTO_EXPAND depth.
      autoExpandDepth: inverted ? 0 : undefined,
      showOptimizationHint: options.showOptimizationHint
    });

    // Bind events.
    root.on("link", this._onLink);
    root.on("focus", this._onFocus);

    // Clear out other call trees.
    this.container.innerHTML = "";
    root.attachTo(this.container);

    // When platform data isn't shown, hide the cateogry labels, since they're
    // only available for C++ frames. Pass *false* to make them invisible.
    root.toggleCategories(!options.contentOnly);

    // Return the CallView for tests
    return root;
  },

  toString: () => "[object JsCallTreeView]"
});

EventEmitter.decorate(JsCallTreeView);
PK
!<+�����Bchrome/devtools/content/performance/views/details-js-flamegraph.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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-globals-from ../performance-controller.js */
/* import-globals-from ../performance-view.js */
/* globals DetailsSubview */
"use strict";

/**
 * FlameGraph view containing a pyramid-like visualization of a profile,
 * controlled by DetailsView.
 */
var JsFlameGraphView = extend(DetailsSubview, {

  shouldUpdateWhileMouseIsActive: true,

  rerenderPrefs: [
    "invert-flame-graph",
    "flatten-tree-recursion",
    "show-platform-data",
    "show-idle-blocks"
  ],

  /**
   * Sets up the view with event binding.
   */
  initialize: Task.async(function* () {
    DetailsSubview.initialize.call(this);

    this.graph = new FlameGraph($("#js-flamegraph-view"));
    this.graph.timelineTickUnits = L10N.getStr("graphs.ms");
    this.graph.setTheme(PerformanceController.getTheme());
    yield this.graph.ready();

    this._onRangeChangeInGraph = this._onRangeChangeInGraph.bind(this);
    this._onThemeChanged = this._onThemeChanged.bind(this);

    PerformanceController.on(EVENTS.THEME_CHANGED, this._onThemeChanged);
    this.graph.on("selecting", this._onRangeChangeInGraph);
  }),

  /**
   * Unbinds events.
   */
  destroy: Task.async(function* () {
    DetailsSubview.destroy.call(this);

    PerformanceController.off(EVENTS.THEME_CHANGED, this._onThemeChanged);
    this.graph.off("selecting", this._onRangeChangeInGraph);

    yield this.graph.destroy();
  }),

  /**
   * Method for handling all the set up for rendering a new flamegraph.
   *
   * @param object interval [optional]
   *        The { startTime, endTime }, in milliseconds.
   */
  render: function (interval = {}) {
    let recording = PerformanceController.getCurrentRecording();
    let duration = recording.getDuration();
    let profile = recording.getProfile();
    let thread = profile.threads[0];

    let data = FlameGraphUtils.createFlameGraphDataFromThread(thread, {
      invertTree: PerformanceController.getOption("invert-flame-graph"),
      flattenRecursion: PerformanceController.getOption("flatten-tree-recursion"),
      contentOnly: !PerformanceController.getOption("show-platform-data"),
      showIdleBlocks: PerformanceController.getOption("show-idle-blocks")
                      && L10N.getStr("table.idle")
    });

    this.graph.setData({ data,
                         bounds: {
                           startTime: 0,
                           endTime: duration
                         },
                         visible: {
                           startTime: interval.startTime || 0,
                           endTime: interval.endTime || duration
                         }
    });

    this.graph.focus();

    this.emit(EVENTS.UI_JS_FLAMEGRAPH_RENDERED);
  },

  /**
   * Fired when a range is selected or cleared in the FlameGraph.
   */
  _onRangeChangeInGraph: function () {
    let interval = this.graph.getViewRange();

    // Squelch rerendering this view when we update the range here
    // to avoid recursion, as our FlameGraph handles rerendering itself
    // when originating from within the graph.
    this.requiresUpdateOnRangeChange = false;
    OverviewView.setTimeInterval(interval);
    this.requiresUpdateOnRangeChange = true;
  },

  /**
   * Called whenever a pref is changed and this view needs to be rerendered.
   */
  _onRerenderPrefChanged: function () {
    let recording = PerformanceController.getCurrentRecording();
    let profile = recording.getProfile();
    let thread = profile.threads[0];
    FlameGraphUtils.removeFromCache(thread);
  },

  /**
   * Called when `devtools.theme` changes.
   */
  _onThemeChanged: function (_, theme) {
    this.graph.setTheme(theme);
    this.graph.refresh({ force: true });
  },

  toString: () => "[object JsFlameGraphView]"
});

EventEmitter.decorate(JsFlameGraphView);
PK
!<�pVN]]Echrome/devtools/content/performance/views/details-memory-call-tree.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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-globals-from ../performance-controller.js */
/* import-globals-from ../performance-view.js */
/* globals DetailsSubview */
"use strict";

/**
 * CallTree view containing memory allocation sites, controlled by DetailsView.
 */
var MemoryCallTreeView = extend(DetailsSubview, {

  rerenderPrefs: [
    "invert-call-tree"
  ],

  // Units are in milliseconds.
  rangeChangeDebounceTime: 100,

  /**
   * Sets up the view with event binding.
   */
  initialize: function () {
    DetailsSubview.initialize.call(this);

    this._onLink = this._onLink.bind(this);

    this.container = $("#memory-calltree-view > .call-tree-cells-container");
  },

  /**
   * Unbinds events.
   */
  destroy: function () {
    DetailsSubview.destroy.call(this);
  },

  /**
   * Method for handling all the set up for rendering a new call tree.
   *
   * @param object interval [optional]
   *        The { startTime, endTime }, in milliseconds.
   */
  render: function (interval = {}) {
    let options = {
      invertTree: PerformanceController.getOption("invert-call-tree")
    };
    let recording = PerformanceController.getCurrentRecording();
    let allocations = recording.getAllocations();
    let threadNode = this._prepareCallTree(allocations, interval, options);
    this._populateCallTree(threadNode, options);
    this.emit(EVENTS.UI_MEMORY_CALL_TREE_RENDERED);
  },

  /**
   * Fired on the "link" event for the call tree in this container.
   */
  _onLink: function (_, treeItem) {
    let { url, line } = treeItem.frame.getInfo();
    gToolbox.viewSourceInDebugger(url, line).then(success => {
      if (success) {
        this.emit(EVENTS.SOURCE_SHOWN_IN_JS_DEBUGGER);
      } else {
        this.emit(EVENTS.SOURCE_NOT_FOUND_IN_JS_DEBUGGER);
      }
    });
  },

  /**
   * Called when the recording is stopped and prepares data to
   * populate the call tree.
   */
  _prepareCallTree: function (allocations, { startTime, endTime }, options) {
    let thread = RecordingUtils.getProfileThreadFromAllocations(allocations);
    let { invertTree } = options;

    return new ThreadNode(thread, { startTime, endTime, invertTree });
  },

  /**
   * Renders the call tree.
   */
  _populateCallTree: function (frameNode, options = {}) {
    // If we have an empty profile (no samples), then don't invert the tree, as
    // it would hide the root node and a completely blank call tree space can be
    // mis-interpreted as an error.
    let inverted = options.invertTree && frameNode.samples > 0;

    let root = new CallView({
      frame: frameNode,
      inverted: inverted,
      // Root nodes are hidden in inverted call trees.
      hidden: inverted,
      // Call trees should only auto-expand when not inverted. Passing undefined
      // will default to the CALL_TREE_AUTO_EXPAND depth.
      autoExpandDepth: inverted ? 0 : undefined,
      // Some cells like the time duration and cost percentage don't make sense
      // for a memory allocations call tree.
      visibleCells: {
        selfCount: true,
        count: true,
        selfSize: true,
        size: true,
        selfCountPercentage: true,
        countPercentage: true,
        selfSizePercentage: true,
        sizePercentage: true,
        function: true
      }
    });

    // Bind events.
    root.on("link", this._onLink);

    // Pipe "focus" events to the view, mostly for tests
    root.on("focus", () => this.emit("focus"));

    // Clear out other call trees.
    this.container.innerHTML = "";
    root.attachTo(this.container);

    // Memory allocation samples don't contain cateogry labels.
    root.toggleCategories(false);
  },

  toString: () => "[object MemoryCallTreeView]"
});

EventEmitter.decorate(MemoryCallTreeView);
PK
!<k��Fchrome/devtools/content/performance/views/details-memory-flamegraph.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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-globals-from ../performance-controller.js */
/* import-globals-from ../performance-view.js */
/* globals DetailsSubview */
"use strict";

/**
 * FlameGraph view containing a pyramid-like visualization of memory allocation
 * sites, controlled by DetailsView.
 */
var MemoryFlameGraphView = extend(DetailsSubview, {

  shouldUpdateWhileMouseIsActive: true,

  rerenderPrefs: [
    "invert-flame-graph",
    "flatten-tree-recursion",
    "show-idle-blocks"
  ],

  /**
   * Sets up the view with event binding.
   */
  initialize: Task.async(function* () {
    DetailsSubview.initialize.call(this);

    this.graph = new FlameGraph($("#memory-flamegraph-view"));
    this.graph.timelineTickUnits = L10N.getStr("graphs.ms");
    this.graph.setTheme(PerformanceController.getTheme());
    yield this.graph.ready();

    this._onRangeChangeInGraph = this._onRangeChangeInGraph.bind(this);
    this._onThemeChanged = this._onThemeChanged.bind(this);

    PerformanceController.on(EVENTS.THEME_CHANGED, this._onThemeChanged);
    this.graph.on("selecting", this._onRangeChangeInGraph);
  }),

  /**
   * Unbinds events.
   */
  destroy: Task.async(function* () {
    DetailsSubview.destroy.call(this);

    PerformanceController.off(EVENTS.THEME_CHANGED, this._onThemeChanged);
    this.graph.off("selecting", this._onRangeChangeInGraph);

    yield this.graph.destroy();
  }),

  /**
   * Method for handling all the set up for rendering a new flamegraph.
   *
   * @param object interval [optional]
   *        The { startTime, endTime }, in milliseconds.
   */
  render: function (interval = {}) {
    let recording = PerformanceController.getCurrentRecording();
    let duration = recording.getDuration();
    let allocations = recording.getAllocations();

    let thread = RecordingUtils.getProfileThreadFromAllocations(allocations);
    let data = FlameGraphUtils.createFlameGraphDataFromThread(thread, {
      invertStack: PerformanceController.getOption("invert-flame-graph"),
      flattenRecursion: PerformanceController.getOption("flatten-tree-recursion"),
      showIdleBlocks: PerformanceController.getOption("show-idle-blocks")
                      && L10N.getStr("table.idle")
    });

    this.graph.setData({
      data,
      bounds: {
        startTime: 0,
        endTime: duration
      },
      visible: {
        startTime: interval.startTime || 0,
        endTime: interval.endTime || duration
      }
    });

    this.emit(EVENTS.UI_MEMORY_FLAMEGRAPH_RENDERED);
  },

  /**
   * Fired when a range is selected or cleared in the FlameGraph.
   */
  _onRangeChangeInGraph: function () {
    let interval = this.graph.getViewRange();

    // Squelch rerendering this view when we update the range here
    // to avoid recursion, as our FlameGraph handles rerendering itself
    // when originating from within the graph.
    this.requiresUpdateOnRangeChange = false;
    OverviewView.setTimeInterval(interval);
    this.requiresUpdateOnRangeChange = true;
  },

  /**
   * Called whenever a pref is changed and this view needs to be rerendered.
   */
  _onRerenderPrefChanged: function () {
    let recording = PerformanceController.getCurrentRecording();
    let allocations = recording.getAllocations();
    let thread = RecordingUtils.getProfileThreadFromAllocations(allocations);
    FlameGraphUtils.removeFromCache(thread);
  },

  /**
   * Called when `devtools.theme` changes.
   */
  _onThemeChanged: function (_, theme) {
    this.graph.setTheme(theme);
    this.graph.refresh({ force: true });
  },

  toString: () => "[object MemoryFlameGraphView]"
});

EventEmitter.decorate(MemoryFlameGraphView);
PK
!<kE��>chrome/devtools/content/performance/views/details-waterfall.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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-globals-from ../performance-controller.js */
/* import-globals-from ../performance-view.js */
/* globals window, DetailsSubview */
"use strict";

const MARKER_DETAILS_WIDTH = 200;
// Units are in milliseconds.
const WATERFALL_RESIZE_EVENTS_DRAIN = 100;

const { TickUtils } = require("devtools/client/performance/modules/waterfall-ticks");

/**
 * Waterfall view containing the timeline markers, controlled by DetailsView.
 */
var WaterfallView = extend(DetailsSubview, {

  // Smallest unit of time between two markers. Larger by 10x^3 than Number.EPSILON.
  MARKER_EPSILON: 0.000000000001,
  // px
  WATERFALL_MARKER_SIDEBAR_WIDTH: 175,
  // px
  WATERFALL_MARKER_SIDEBAR_SAFE_BOUNDS: 20,

  observedPrefs: [
    "hidden-markers"
  ],

  rerenderPrefs: [
    "hidden-markers"
  ],

  // Units are in milliseconds.
  rangeChangeDebounceTime: 75,

  /**
   * Sets up the view with event binding.
   */
  initialize: function () {
    DetailsSubview.initialize.call(this);

    this._cache = new WeakMap();

    this._onMarkerSelected = this._onMarkerSelected.bind(this);
    this._onResize = this._onResize.bind(this);
    this._onViewSource = this._onViewSource.bind(this);
    this._onShowAllocations = this._onShowAllocations.bind(this);
    this._hiddenMarkers = PerformanceController.getPref("hidden-markers");

    this.treeContainer = $("#waterfall-tree");
    this.detailsContainer = $("#waterfall-details");
    this.detailsSplitter = $("#waterfall-view > splitter");

    this.details = new MarkerDetails($("#waterfall-details"),
                                     $("#waterfall-view > splitter"));
    this.details.hidden = true;

    this.details.on("resize", this._onResize);
    this.details.on("view-source", this._onViewSource);
    this.details.on("show-allocations", this._onShowAllocations);
    window.addEventListener("resize", this._onResize);

    // TODO bug 1167093 save the previously set width, and ensure minimum width
    this.details.width = MARKER_DETAILS_WIDTH;
  },

  /**
   * Unbinds events.
   */
  destroy: function () {
    DetailsSubview.destroy.call(this);

    clearNamedTimeout("waterfall-resize");

    this._cache = null;

    this.details.off("resize", this._onResize);
    this.details.off("view-source", this._onViewSource);
    this.details.off("show-allocations", this._onShowAllocations);
    window.removeEventListener("resize", this._onResize);

    ReactDOM.unmountComponentAtNode(this.treeContainer);
  },

  /**
   * Method for handling all the set up for rendering a new waterfall.
   *
   * @param object interval [optional]
   *        The { startTime, endTime }, in milliseconds.
   */
  render: function (interval = {}) {
    let recording = PerformanceController.getCurrentRecording();
    if (recording.isRecording()) {
      return;
    }
    let startTime = interval.startTime || 0;
    let endTime = interval.endTime || recording.getDuration();
    let markers = recording.getMarkers();
    let rootMarkerNode = this._prepareWaterfallTree(markers);

    this._populateWaterfallTree(rootMarkerNode, { startTime, endTime });
    this.emit(EVENTS.UI_WATERFALL_RENDERED);
  },

  /**
   * Called when a marker is selected in the waterfall view,
   * updating the markers detail view.
   */
  _onMarkerSelected: function (event, marker) {
    let recording = PerformanceController.getCurrentRecording();
    let frames = recording.getFrames();
    let allocations = recording.getConfiguration().withAllocations;

    if (event === "selected") {
      this.details.render({ marker, frames, allocations });
      this.details.hidden = false;
    }
    if (event === "unselected") {
      this.details.empty();
    }
  },

  /**
   * Called when the marker details view is resized.
   */
  _onResize: function () {
    setNamedTimeout("waterfall-resize", WATERFALL_RESIZE_EVENTS_DRAIN, () => {
      this.render(OverviewView.getTimeInterval());
    });
  },

  /**
   * Called whenever an observed pref is changed.
   */
  _onObservedPrefChange: function (prefName) {
    this._hiddenMarkers = PerformanceController.getPref("hidden-markers");

    // Clear the cache as we'll need to recompute the collapsed
    // marker model
    this._cache = new WeakMap();
  },

  /**
   * Called when MarkerDetails view emits an event to view source.
   */
  _onViewSource: function (_, data) {
    gToolbox.viewSourceInDebugger(data.url, data.line);
  },

  /**
   * Called when MarkerDetails view emits an event to snap to allocations.
   */
  _onShowAllocations: function (_, data) {
    let { endTime } = data;
    let startTime = 0;
    let recording = PerformanceController.getCurrentRecording();
    let markers = recording.getMarkers();

    let lastGCMarkerFromPreviousCycle = null;
    let lastGCMarker = null;
    // Iterate over markers looking for the most recent GC marker
    // from the cycle before the marker's whose allocations we're interested in.
    for (let marker of markers) {
      // We found the marker whose allocations we're tracking; abort
      if (marker.start === endTime) {
        break;
      }

      if (marker.name === "GarbageCollection") {
        if (lastGCMarker && lastGCMarker.cycle !== marker.cycle) {
          lastGCMarkerFromPreviousCycle = lastGCMarker;
        }
        lastGCMarker = marker;
      }
    }

    if (lastGCMarkerFromPreviousCycle) {
      startTime = lastGCMarkerFromPreviousCycle.end;
    }

    // Adjust times so we don't include the range of these markers themselves.
    endTime -= this.MARKER_EPSILON;
    startTime += startTime !== 0 ? this.MARKER_EPSILON : 0;

    OverviewView.setTimeInterval({ startTime, endTime });
    DetailsView.selectView("memory-calltree");
  },

  /**
   * Called when the recording is stopped and prepares data to
   * populate the waterfall tree.
   */
  _prepareWaterfallTree: function (markers) {
    let cached = this._cache.get(markers);
    if (cached) {
      return cached;
    }

    let rootMarkerNode = WaterfallUtils.createParentNode({ name: "(root)" });

    WaterfallUtils.collapseMarkersIntoNode({
      rootNode: rootMarkerNode,
      markersList: markers,
      filter: this._hiddenMarkers
    });

    this._cache.set(markers, rootMarkerNode);
    return rootMarkerNode;
  },

  /**
   * Calculates the available width for the waterfall.
   * This should be invoked every time the container node is resized.
   */
  _recalculateBounds: function () {
    this.waterfallWidth = this.treeContainer.clientWidth
      - this.WATERFALL_MARKER_SIDEBAR_WIDTH
      - this.WATERFALL_MARKER_SIDEBAR_SAFE_BOUNDS;
  },

  /**
   * Renders the waterfall tree.
   */
  _populateWaterfallTree: function (rootMarkerNode, interval) {
    this._recalculateBounds();

    let doc = this.treeContainer.ownerDocument;
    let startTime = interval.startTime | 0;
    let endTime = interval.endTime | 0;
    let dataScale = this.waterfallWidth / (endTime - startTime);

    this.canvas = TickUtils.drawWaterfallBackground(doc, dataScale, this.waterfallWidth);

    let treeView = Waterfall({
      marker: rootMarkerNode,
      startTime,
      endTime,
      dataScale,
      sidebarWidth: this.WATERFALL_MARKER_SIDEBAR_WIDTH,
      waterfallWidth: this.waterfallWidth,
      onFocus: node => this._onMarkerSelected("selected", node)
    });

    ReactDOM.render(treeView, this.treeContainer);
  },

  toString: () => "[object WaterfallView]"
});

EventEmitter.decorate(WaterfallView);
PK
!<��̍R!R!4chrome/devtools/content/performance/views/details.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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-globals-from ../performance-controller.js */
/* import-globals-from ../performance-view.js */
/* globals WaterfallView, JsCallTreeView, JsFlameGraphView, MemoryCallTreeView,
           MemoryFlameGraphView */
"use strict";

/**
 * Details view containing call trees, flamegraphs and markers waterfall.
 * Manages subviews and toggles visibility between them.
 */
var DetailsView = {
  /**
   * Name to (node id, view object, actor requirements, pref killswitch)
   * mapping of subviews.
   */
  components: {
    "waterfall": {
      id: "waterfall-view",
      view: WaterfallView,
      features: ["withMarkers"]
    },
    "js-calltree": {
      id: "js-profile-view",
      view: JsCallTreeView
    },
    "js-flamegraph": {
      id: "js-flamegraph-view",
      view: JsFlameGraphView,
    },
    "memory-calltree": {
      id: "memory-calltree-view",
      view: MemoryCallTreeView,
      features: ["withAllocations"]
    },
    "memory-flamegraph": {
      id: "memory-flamegraph-view",
      view: MemoryFlameGraphView,
      features: ["withAllocations"],
      prefs: ["enable-memory-flame"],
    },
  },

  /**
   * Sets up the view with event binding, initializes subviews.
   */
  initialize: Task.async(function* () {
    this.el = $("#details-pane");
    this.toolbar = $("#performance-toolbar-controls-detail-views");

    this._onViewToggle = this._onViewToggle.bind(this);
    this._onRecordingStoppedOrSelected = this._onRecordingStoppedOrSelected.bind(this);
    this.setAvailableViews = this.setAvailableViews.bind(this);

    for (let button of $$("toolbarbutton[data-view]", this.toolbar)) {
      button.addEventListener("command", this._onViewToggle);
    }

    yield this.setAvailableViews();

    PerformanceController.on(EVENTS.RECORDING_STATE_CHANGE,
                             this._onRecordingStoppedOrSelected);
    PerformanceController.on(EVENTS.RECORDING_SELECTED,
                             this._onRecordingStoppedOrSelected);
    PerformanceController.on(EVENTS.PREF_CHANGED, this.setAvailableViews);
  }),

  /**
   * Unbinds events, destroys subviews.
   */
  destroy: Task.async(function* () {
    for (let button of $$("toolbarbutton[data-view]", this.toolbar)) {
      button.removeEventListener("command", this._onViewToggle);
    }

    for (let component of Object.values(this.components)) {
      component.initialized && (yield component.view.destroy());
    }

    PerformanceController.off(EVENTS.RECORDING_STATE_CHANGE,
                              this._onRecordingStoppedOrSelected);
    PerformanceController.off(EVENTS.RECORDING_SELECTED,
                              this._onRecordingStoppedOrSelected);
    PerformanceController.off(EVENTS.PREF_CHANGED, this.setAvailableViews);
  }),

  /**
   * Sets the possible views based off of recording features and server actor support
   * by hiding/showing the buttons that select them and going to default view
   * if currently selected. Called when a preference changes in
   * `devtools.performance.ui.`.
   */
  setAvailableViews: Task.async(function* () {
    let recording = PerformanceController.getCurrentRecording();
    let isCompleted = recording && recording.isCompleted();
    let invalidCurrentView = false;

    for (let [name, { view }] of Object.entries(this.components)) {
      let isSupported = this._isViewSupported(name);

      $(`toolbarbutton[data-view=${name}]`).hidden = !isSupported;

      // If the view is currently selected and not supported, go back to the
      // default view.
      if (!isSupported && this.isViewSelected(view)) {
        invalidCurrentView = true;
      }
    }

    // Two scenarios in which we select the default view.
    //
    // 1: If we currently have selected a view that is no longer valid due
    // to feature support, and this isn't the first view, and the current recording
    // is completed.
    //
    // 2. If we have a finished recording and no panel was selected yet,
    // use a default now that we have the recording configurations
    if ((this._initialized && isCompleted && invalidCurrentView) ||
        (!this._initialized && isCompleted && recording)) {
      yield this.selectDefaultView();
    }
  }),

  /**
   * Takes a view name and determines if the current recording
   * can support the view.
   *
   * @param {string} viewName
   * @return {boolean}
   */
  _isViewSupported: function (viewName) {
    let { features, prefs } = this.components[viewName];
    let recording = PerformanceController.getCurrentRecording();

    if (!recording || !recording.isCompleted()) {
      return false;
    }

    let prefSupported = (prefs && prefs.length) ?
                        prefs.every(p => PerformanceController.getPref(p)) :
                        true;
    return PerformanceController.isFeatureSupported(features) && prefSupported;
  },

  /**
   * Select one of the DetailView's subviews to be rendered,
   * hiding the others.
   *
   * @param String viewName
   *        Name of the view to be shown.
   */
  selectView: Task.async(function* (viewName) {
    let component = this.components[viewName];
    this.el.selectedPanel = $("#" + component.id);

    yield this._whenViewInitialized(component);

    for (let button of $$("toolbarbutton[data-view]", this.toolbar)) {
      if (button.getAttribute("data-view") === viewName) {
        button.setAttribute("checked", true);
      } else {
        button.removeAttribute("checked");
      }
    }

    // Set a flag indicating that a view was explicitly set based on a
    // recording's features.
    this._initialized = true;

    this.emit(EVENTS.UI_DETAILS_VIEW_SELECTED, viewName);
  }),

  /**
   * Selects a default view based off of protocol support
   * and preferences enabled.
   */
  selectDefaultView: function () {
    // We want the waterfall to be default view in almost all cases, except when
    // timeline actor isn't supported, or we have markers disabled (which should only
    // occur temporarily via bug 1156499
    if (this._isViewSupported("waterfall")) {
      return this.selectView("waterfall");
    }
    // The JS CallTree should always be supported since the profiler
    // actor is as old as the world.
    return this.selectView("js-calltree");
  },

  /**
   * Checks if the provided view is currently selected.
   *
   * @param object viewObject
   * @return boolean
   */
  isViewSelected: function (viewObject) {
    // If not initialized, and we have no recordings,
    // no views are selected (even though there's a selected panel)
    if (!this._initialized) {
      return false;
    }

    let selectedPanel = this.el.selectedPanel;
    let selectedId = selectedPanel.id;

    for (let { id, view } of Object.values(this.components)) {
      if (id == selectedId && view == viewObject) {
        return true;
      }
    }

    return false;
  },

  /**
   * Initializes a subview if it wasn't already set up, and makes sure
   * it's populated with recording data if there is some available.
   *
   * @param object component
   *        A component descriptor from DetailsView.components
   */
  _whenViewInitialized: Task.async(function* (component) {
    if (component.initialized) {
      return;
    }
    component.initialized = true;
    yield component.view.initialize();

    // If this view is initialized *after* a recording is shown, it won't display
    // any data. Make sure it's populated by setting `shouldUpdateWhenShown`.
    // All detail views require a recording to be complete, so do not
    // attempt to render if recording is in progress or does not exist.
    let recording = PerformanceController.getCurrentRecording();
    if (recording && recording.isCompleted()) {
      component.view.shouldUpdateWhenShown = true;
    }
  }),

  /**
   * Called when recording stops or is selected.
   */
  _onRecordingStoppedOrSelected: function (_, state, recording) {
    if (typeof state === "string" && state !== "recording-stopped") {
      return;
    }
    this.setAvailableViews();
  },

  /**
   * Called when a view button is clicked.
   */
  _onViewToggle: function (e) {
    this.selectView(e.target.getAttribute("data-view"));
  },

  toString: () => "[object DetailsView]"
};

/**
 * Convenient way of emitting events from the view.
 */
EventEmitter.decorate(DetailsView);
PK
!<�2�{6{65chrome/devtools/content/performance/views/overview.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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-globals-from ../performance-controller.js */
/* import-globals-from ../performance-view.js */
"use strict";

// No sense updating the overview more often than receiving data from the
// backend. Make sure this isn't lower than DEFAULT_TIMELINE_DATA_PULL_TIMEOUT
// in devtools/server/actors/timeline.js

// The following units are in milliseconds.
const OVERVIEW_UPDATE_INTERVAL = 200;
const FRAMERATE_GRAPH_LOW_RES_INTERVAL = 100;
const FRAMERATE_GRAPH_HIGH_RES_INTERVAL = 16;
const GRAPH_REQUIREMENTS = {
  timeline: {
    features: ["withMarkers"]
  },
  framerate: {
    features: ["withTicks"]
  },
  memory: {
    features: ["withMemory"]
  },
};

/**
 * View handler for the overview panel's time view, displaying
 * framerate, timeline and memory over time.
 */
var OverviewView = {

  /**
   * How frequently we attempt to render the graphs. Overridden
   * in tests.
   */
  OVERVIEW_UPDATE_INTERVAL: OVERVIEW_UPDATE_INTERVAL,

  /**
   * Sets up the view with event binding.
   */
  initialize: function () {
    this.graphs = new GraphsController({
      root: $("#overview-pane"),
      getFilter: () => PerformanceController.getPref("hidden-markers"),
      getTheme: () => PerformanceController.getTheme(),
    });

    // If no timeline support, shut it all down.
    if (!PerformanceController.getTraits().features.withMarkers) {
      this.disable();
      return;
    }

    // Store info on multiprocess support.
    this._multiprocessData = PerformanceController.getMultiprocessStatus();

    this._onRecordingStateChange = this._onRecordingStateChange.bind(this);
    this._onRecordingSelected = this._onRecordingSelected.bind(this);
    this._onRecordingTick = this._onRecordingTick.bind(this);
    this._onGraphSelecting = this._onGraphSelecting.bind(this);
    this._onGraphRendered = this._onGraphRendered.bind(this);
    this._onPrefChanged = this._onPrefChanged.bind(this);
    this._onThemeChanged = this._onThemeChanged.bind(this);

    // Toggle the initial visibility of memory and framerate graph containers
    // based off of prefs.
    PerformanceController.on(EVENTS.PREF_CHANGED, this._onPrefChanged);
    PerformanceController.on(EVENTS.THEME_CHANGED, this._onThemeChanged);
    PerformanceController.on(EVENTS.RECORDING_STATE_CHANGE, this._onRecordingStateChange);
    PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
    this.graphs.on("selecting", this._onGraphSelecting);
    this.graphs.on("rendered", this._onGraphRendered);
  },

  /**
   * Unbinds events.
   */
  destroy: Task.async(function* () {
    PerformanceController.off(EVENTS.PREF_CHANGED, this._onPrefChanged);
    PerformanceController.off(EVENTS.THEME_CHANGED, this._onThemeChanged);
    PerformanceController.off(EVENTS.RECORDING_STATE_CHANGE,
                              this._onRecordingStateChange);
    PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
    this.graphs.off("selecting", this._onGraphSelecting);
    this.graphs.off("rendered", this._onGraphRendered);
    yield this.graphs.destroy();
  }),

  /**
   * Returns true if any of the overview graphs have mouse dragging active,
   * false otherwise.
   */
  get isMouseActive() {
    // Fetch all graphs currently stored in the GraphsController.
    // These graphs are not necessarily active, but will not have
    // an active mouse, in that case.
    return !!this.graphs.getWidgets().some(e => e.isMouseActive);
  },

  /**
   * Disabled in the event we're using a Timeline mock, so we'll have no
   * timeline, ticks or memory data to show, so just block rendering and hide
   * the panel.
   */
  disable: function () {
    this._disabled = true;
    this.graphs.disableAll();
  },

  /**
   * Returns the disabled status.
   *
   * @return boolean
   */
  isDisabled: function () {
    return this._disabled;
  },

  /**
   * Sets the time interval selection for all graphs in this overview.
   *
   * @param object interval
   *        The { startTime, endTime }, in milliseconds.
   */
  setTimeInterval: function (interval, options = {}) {
    let recording = PerformanceController.getCurrentRecording();
    if (recording == null) {
      throw new Error("A recording should be available in order to set the selection.");
    }
    if (this.isDisabled()) {
      return;
    }
    let mapStart = () => 0;
    let mapEnd = () => recording.getDuration();
    let selection = { start: interval.startTime, end: interval.endTime };
    this._stopSelectionChangeEventPropagation = options.stopPropagation;
    this.graphs.setMappedSelection(selection, { mapStart, mapEnd });
    this._stopSelectionChangeEventPropagation = false;
  },

  /**
   * Gets the time interval selection for all graphs in this overview.
   *
   * @return object
   *         The { startTime, endTime }, in milliseconds.
   */
  getTimeInterval: function () {
    let recording = PerformanceController.getCurrentRecording();
    if (recording == null) {
      throw new Error("A recording should be available in order to get the selection.");
    }
    if (this.isDisabled()) {
      return { startTime: 0, endTime: recording.getDuration() };
    }
    let mapStart = () => 0;
    let mapEnd = () => recording.getDuration();
    let selection = this.graphs.getMappedSelection({ mapStart, mapEnd });
    // If no selection returned, this means the overview graphs have not been rendered
    // yet, so act as if we have no selection (the full recording). Also
    // if the selection range distance is tiny, assume the range was cleared or just
    // clicked, and we do not have a range.
    if (!selection || (selection.max - selection.min) < 1) {
      return { startTime: 0, endTime: recording.getDuration() };
    }
    return { startTime: selection.min, endTime: selection.max };
  },

  /**
   * Method for handling all the set up for rendering the overview graphs.
   *
   * @param number resolution
   *        The fps graph resolution. @see Graphs.js
   */
  render: Task.async(function* (resolution) {
    if (this.isDisabled()) {
      return;
    }

    let recording = PerformanceController.getCurrentRecording();
    yield this.graphs.render(recording.getAllData(), resolution);

    // Finished rendering all graphs in this overview.
    this.emit(EVENTS.UI_OVERVIEW_RENDERED, resolution);
  }),

  /**
   * Called at most every OVERVIEW_UPDATE_INTERVAL milliseconds
   * and uses data fetched from the controller to render
   * data into all the corresponding overview graphs.
   */
  _onRecordingTick: Task.async(function* () {
    yield this.render(FRAMERATE_GRAPH_LOW_RES_INTERVAL);
    this._prepareNextTick();
  }),

  /**
   * Called to refresh the timer to keep firing _onRecordingTick.
   */
  _prepareNextTick: function () {
    // Check here to see if there's still a _timeoutId, incase
    // `stop` was called before the _prepareNextTick call was executed.
    if (this.isRendering()) {
      this._timeoutId = setTimeout(this._onRecordingTick, this.OVERVIEW_UPDATE_INTERVAL);
    }
  },

  /**
   * Called when recording state changes.
   */
  _onRecordingStateChange: OverviewViewOnStateChange(Task.async(
    function* (_, state, recording) {
      if (state !== "recording-stopped") {
        return;
      }
      // Check to see if the recording that just stopped is the current recording.
      // If it is, render the high-res graphs. For manual recordings, it will also
      // be the current recording, but profiles generated by `console.profile` can stop
      // while having another profile selected -- in this case, OverviewView should keep
      // rendering the current recording.
      if (recording !== PerformanceController.getCurrentRecording()) {
        return;
      }
      this.render(FRAMERATE_GRAPH_HIGH_RES_INTERVAL);
      yield this._checkSelection(recording);
    })),

  /**
   * Called when a new recording is selected.
   */
  _onRecordingSelected: OverviewViewOnStateChange(Task.async(function* (_, recording) {
    this._setGraphVisibilityFromRecordingFeatures(recording);

    // If this recording is complete, render the high res graph
    if (recording.isCompleted()) {
      yield this.render(FRAMERATE_GRAPH_HIGH_RES_INTERVAL);
    }
    yield this._checkSelection(recording);
    this.graphs.dropSelection();
  })),

  /**
   * Start the polling for rendering the overview graph.
   */
  _startPolling: function () {
    this._timeoutId = setTimeout(this._onRecordingTick, this.OVERVIEW_UPDATE_INTERVAL);
  },

  /**
   * Stop the polling for rendering the overview graph.
   */
  _stopPolling: function () {
    clearTimeout(this._timeoutId);
    this._timeoutId = null;
  },

  /**
   * Whether or not the overview view is in a state of polling rendering.
   */
  isRendering: function () {
    return !!this._timeoutId;
  },

  /**
   * Makes sure the selection is enabled or disabled in all the graphs,
   * based on whether a recording currently exists and is not in progress.
   */
  _checkSelection: Task.async(function* (recording) {
    let isEnabled = recording ? recording.isCompleted() : false;
    yield this.graphs.selectionEnabled(isEnabled);
  }),

  /**
   * Fired when the graph selection has changed. Called by
   * mouseup and scroll events.
   */
  _onGraphSelecting: function () {
    if (this._stopSelectionChangeEventPropagation) {
      return;
    }

    this.emit(EVENTS.UI_OVERVIEW_RANGE_SELECTED, this.getTimeInterval());
  },

  _onGraphRendered: function (_, graphName) {
    switch (graphName) {
      case "timeline":
        this.emit(EVENTS.UI_MARKERS_GRAPH_RENDERED);
        break;
      case "memory":
        this.emit(EVENTS.UI_MEMORY_GRAPH_RENDERED);
        break;
      case "framerate":
        this.emit(EVENTS.UI_FRAMERATE_GRAPH_RENDERED);
        break;
    }
  },

  /**
   * Called whenever a preference in `devtools.performance.ui.` changes.
   * Does not care about the enabling of memory/framerate graphs,
   * because those will set values on a recording model, and
   * the graphs will render based on the existence.
   */
  _onPrefChanged: Task.async(function* (_, prefName, prefValue) {
    switch (prefName) {
      case "hidden-markers": {
        let graph = yield this.graphs.isAvailable("timeline");
        if (graph) {
          let filter = PerformanceController.getPref("hidden-markers");
          graph.setFilter(filter);
          graph.refresh({ force: true });
        }
        break;
      }
    }
  }),

  _setGraphVisibilityFromRecordingFeatures: function (recording) {
    for (let [graphName, requirements] of Object.entries(GRAPH_REQUIREMENTS)) {
      this.graphs.enable(graphName,
                         PerformanceController.isFeatureSupported(requirements.features));
    }
  },

  /**
   * Fetch the multiprocess status and if e10s is not currently on, disable
   * realtime rendering.
   *
   * @return {boolean}
   */
  isRealtimeRenderingEnabled: function () {
    return this._multiprocessData.enabled;
  },

  /**
   * Show the graphs overview panel when a recording is finished
   * when non-realtime graphs are enabled. Also set the graph visibility
   * so the performance graphs know which graphs to render.
   *
   * @param {RecordingModel} recording
   */
  _showGraphsPanel: function (recording) {
    this._setGraphVisibilityFromRecordingFeatures(recording);
    $("#overview-pane").classList.remove("hidden");
  },

  /**
   * Hide the graphs container completely.
   */
  _hideGraphsPanel: function () {
    $("#overview-pane").classList.add("hidden");
  },

  /**
   * Called when `devtools.theme` changes.
   */
  _onThemeChanged: function (_, theme) {
    this.graphs.setTheme({ theme, redraw: true });
  },

  toString: () => "[object OverviewView]"
};

/**
 * Utility that can wrap a method of OverviewView that
 * handles a recording state change like when a recording is starting,
 * stopping, or about to start/stop, and determines whether or not
 * the polling for rendering the overview graphs needs to start or stop.
 * Must be called with the OverviewView context.
 *
 * @param {function?} fn
 * @return {function}
 */
function OverviewViewOnStateChange(fn) {
  return function _onRecordingStateChange(eventName, recording) {
    // Normalize arguments for the RECORDING_STATE_CHANGE event,
    // as it also has a `state` argument.
    if (typeof recording === "string") {
      recording = arguments[2];
    }

    let currentRecording = PerformanceController.getCurrentRecording();

    // All these methods require a recording to exist selected and
    // from the event name, since there is a delay between starting
    // a recording and changing the selection.
    if (!currentRecording || !recording) {
      // If no recording (this can occur when having a console.profile recording, and
      // we do not stop it from the backend), and we are still rendering updates,
      // stop that.
      if (this.isRendering()) {
        this._stopPolling();
      }
      return;
    }

    // If realtime rendering is not enabed (e10s not on), then
    // show the disabled message, or the full graphs if the recording is completed
    if (!this.isRealtimeRenderingEnabled()) {
      if (recording.isRecording()) {
        this._hideGraphsPanel();
        // Abort, as we do not want to change polling status.
        return;
      }
      this._showGraphsPanel(recording);
    }

    if (this.isRendering() && !currentRecording.isRecording()) {
      this._stopPolling();
    } else if (currentRecording.isRecording() && !this.isRendering()) {
      this._startPolling();
    }

    if (fn) {
      fn.apply(this, arguments);
    }
  };
}

// Decorates the OverviewView as an EventEmitter
EventEmitter.decorate(OverviewView);
PK
!<p!pnn7chrome/devtools/content/performance/views/recordings.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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-globals-from ../performance-controller.js */
/* import-globals-from ../performance-view.js */
/* globals document, window */
"use strict";

/**
 * Functions handling the recordings UI.
 */
var RecordingsView = {
  /**
   * Initialization function, called when the tool is started.
   */
  initialize: function () {
    this._onSelect = this._onSelect.bind(this);
    this._onRecordingStateChange = this._onRecordingStateChange.bind(this);
    this._onNewRecording = this._onNewRecording.bind(this);
    this._onSaveButtonClick = this._onSaveButtonClick.bind(this);
    this._onRecordingDeleted = this._onRecordingDeleted.bind(this);
    this._onRecordingExported = this._onRecordingExported.bind(this);

    PerformanceController.on(EVENTS.RECORDING_STATE_CHANGE, this._onRecordingStateChange);
    PerformanceController.on(EVENTS.RECORDING_ADDED, this._onNewRecording);
    PerformanceController.on(EVENTS.RECORDING_DELETED, this._onRecordingDeleted);
    PerformanceController.on(EVENTS.RECORDING_EXPORTED, this._onRecordingExported);

    // DE-XUL: Begin migrating the recording sidebar to React. Temporarily hold state
    // here.
    this._listState = {
      recordings: [],
      labels: new WeakMap(),
      selected: null,
    };
    this._listMount = PerformanceUtils.createHtmlMount($("#recording-list-mount"));
    this._renderList();
  },

  /**
   * Get the index of the currently selected recording. Only used by tests.
   * @return {integer} index
   */
  getSelectedIndex() {
    const { recordings, selected } = this._listState;
    return recordings.indexOf(selected);
  },

  /**
   * Set the currently selected recording via its index. Only used by tests.
   * @param {integer} index
   */
  setSelectedByIndex(index) {
    this._onSelect(this._listState.recordings[index]);
    this._renderList();
  },

  /**
   * DE-XUL: During the migration, this getter will access the selected recording from
   * the private _listState object so that tests will continue to pass.
   */
  get selected() {
    return this._listState.selected;
  },

  /**
   * DE-XUL: During the migration, this getter will access the number of recordings.
   */
  get itemCount() {
    return this._listState.recordings.length;
  },

  /**
   * DE-XUL: Render the recording list using React.
   */
  _renderList: function () {
    const {recordings, labels, selected} = this._listState;

    const recordingList = RecordingList({
      itemComponent: RecordingListItem,
      items: recordings.map(recording => ({
        onSelect: () => this._onSelect(recording),
        onSave: () => this._onSaveButtonClick(recording),
        isLoading: !recording.isRecording() && !recording.isCompleted(),
        isRecording: recording.isRecording(),
        isSelected: recording === selected,
        duration: recording.getDuration().toFixed(0),
        label: labels.get(recording),
      }))
    });

    ReactDOM.render(recordingList, this._listMount);
  },

  /**
   * Destruction function, called when the tool is closed.
   */
  destroy: function () {
    PerformanceController.off(EVENTS.RECORDING_STATE_CHANGE,
                              this._onRecordingStateChange);
    PerformanceController.off(EVENTS.RECORDING_ADDED, this._onNewRecording);
    PerformanceController.off(EVENTS.RECORDING_DELETED, this._onRecordingDeleted);
    PerformanceController.off(EVENTS.RECORDING_EXPORTED, this._onRecordingExported);
  },

  /**
   * Called when a new recording is stored in the UI. This handles
   * when recordings are lazily loaded (like a console.profile occurring
   * before the tool is loaded) or imported. In normal manual recording cases,
   * this will also be fired.
   */
  _onNewRecording: function (_, recording) {
    this._onRecordingStateChange(_, null, recording);
  },

  /**
   * Signals that a recording has changed state.
   *
   * @param string state
   *        Can be "recording-started", "recording-stopped", "recording-stopping"
   * @param RecordingModel recording
   *        Model of the recording that was started.
   */
  _onRecordingStateChange: function (_, state, recording) {
    const { recordings, labels } = this._listState;

    if (!recordings.includes(recording)) {
      recordings.push(recording);
      labels.set(recording, recording.getLabel() ||
        L10N.getFormatStr("recordingsList.itemLabel", recordings.length));

      // If this is a manual recording, immediately select it, or
      // select a console profile if its the only one
      if (!recording.isConsole() || !this._listState.selected) {
        this._onSelect(recording);
      }
    }

    // Determine if the recording needs to be selected.
    const isCompletedManualRecording = !recording.isConsole() && recording.isCompleted();
    if (recording.isImported() || isCompletedManualRecording) {
      this._onSelect(recording);
    }

    this._renderList();
  },

  /**
   * Clears out all non-console recordings.
   */
  _onRecordingDeleted: function (_, recording) {
    const { recordings } = this._listState;
    const index = recordings.indexOf(recording);
    if (index === -1) {
      throw new Error("Attempting to remove a recording that doesn't exist.");
    }
    recordings.splice(index, 1);
    this._renderList();
  },

  /**
   * The select listener for this container.
   */
  _onSelect: Task.async(function* (recording) {
    this._listState.selected = recording;
    this.emit(EVENTS.UI_RECORDING_SELECTED, recording);
    this._renderList();
  }),

  /**
   * The click listener for the "save" button of each item in this container.
   */
  _onSaveButtonClick: function (recording) {
    let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
    fp.init(window, L10N.getStr("recordingsList.saveDialogTitle"),
            Ci.nsIFilePicker.modeSave);
    fp.appendFilter(L10N.getStr("recordingsList.saveDialogJSONFilter"), "*.json");
    fp.appendFilter(L10N.getStr("recordingsList.saveDialogAllFilter"), "*.*");
    fp.defaultString = "profile.json";

    fp.open({ done: result => {
      if (result == Ci.nsIFilePicker.returnCancel) {
        return;
      }
      this.emit(EVENTS.UI_EXPORT_RECORDING, recording, fp.file);
    }});
  },

  _onRecordingExported: function (_, recording, file) {
    if (recording.isConsole()) {
      return;
    }
    const name = file.leafName.replace(/\..+$/, "");
    this._listState.labels.set(recording, name);
    this._renderList();
  }
};

/**
 * Convenient way of emitting events from the RecordingsView.
 */
EventEmitter.decorate(RecordingsView);
PK
!<�I>H__4chrome/devtools/content/performance/views/toolbar.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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-globals-from ../performance-controller.js */
/* import-globals-from ../performance-view.js */
/* globals document */
"use strict";

/**
 * View handler for toolbar events (mostly option toggling and triggering)
 */
var ToolbarView = {
  /**
   * Sets up the view with event binding.
   */
  initialize: Task.async(function* () {
    this._onFilterPopupShowing = this._onFilterPopupShowing.bind(this);
    this._onFilterPopupHiding = this._onFilterPopupHiding.bind(this);
    this._onHiddenMarkersChanged = this._onHiddenMarkersChanged.bind(this);
    this._onPrefChanged = this._onPrefChanged.bind(this);
    this._popup = $("#performance-options-menupopup");

    this.optionsView = new OptionsView({
      branchName: BRANCH_NAME,
      menupopup: this._popup
    });

    // Set the visibility of experimental UI options on load
    // based off of `devtools.performance.ui.experimental` preference
    let experimentalEnabled = PerformanceController.getOption("experimental");
    this._toggleExperimentalUI(experimentalEnabled);

    yield this.optionsView.initialize();
    this.optionsView.on("pref-changed", this._onPrefChanged);

    this._buildMarkersFilterPopup();
    this._updateHiddenMarkersPopup();
    $("#performance-filter-menupopup").addEventListener("popupshowing",
                                                        this._onFilterPopupShowing);
    $("#performance-filter-menupopup").addEventListener("popuphiding",
                                                        this._onFilterPopupHiding);
  }),

  /**
   * Unbinds events and cleans up view.
   */
  destroy: function () {
    $("#performance-filter-menupopup").removeEventListener("popupshowing",
                                                           this._onFilterPopupShowing);
    $("#performance-filter-menupopup").removeEventListener("popuphiding",
                                                           this._onFilterPopupHiding);
    this._popup = null;

    this.optionsView.off("pref-changed", this._onPrefChanged);
    this.optionsView.destroy();
  },

  /**
   * Creates the timeline markers filter popup.
   */
  _buildMarkersFilterPopup: function () {
    for (let [markerName, markerDetails] of Object.entries(TIMELINE_BLUEPRINT)) {
      let menuitem = document.createElement("menuitem");
      menuitem.setAttribute("closemenu", "none");
      menuitem.setAttribute("type", "checkbox");
      menuitem.setAttribute("align", "center");
      menuitem.setAttribute("flex", "1");
      menuitem.setAttribute("label",
                            MarkerBlueprintUtils.getMarkerGenericName(markerName));
      menuitem.setAttribute("marker-type", markerName);
      menuitem.className = `marker-color-${markerDetails.colorName}`;

      menuitem.addEventListener("command", this._onHiddenMarkersChanged);

      $("#performance-filter-menupopup").appendChild(menuitem);
    }
  },

  /**
   * Updates the menu items checked state in the timeline markers filter popup.
   */
  _updateHiddenMarkersPopup: function () {
    let menuItems = $$("#performance-filter-menupopup menuitem[marker-type]");
    let hiddenMarkers = PerformanceController.getPref("hidden-markers");

    for (let menuitem of menuItems) {
      if (~hiddenMarkers.indexOf(menuitem.getAttribute("marker-type"))) {
        menuitem.removeAttribute("checked");
      } else {
        menuitem.setAttribute("checked", "true");
      }
    }
  },

  /**
   * Fired when `devtools.performance.ui.experimental` is changed, or
   * during init. Toggles the visibility of experimental performance tool options
   * in the UI options.
   *
   * Sets or removes "experimental-enabled" on the menu and main elements,
   * hiding or showing all elements with class "experimental-option".
   *
   * TODO re-enable "#option-enable-memory" permanently once stable in bug 1163350
   * TODO re-enable "#option-show-jit-optimizations" permanently once stable in
   *      bug 1163351
   *
   * @param {boolean} isEnabled
   */
  _toggleExperimentalUI: function (isEnabled) {
    if (isEnabled) {
      $(".theme-body").classList.add("experimental-enabled");
      this._popup.classList.add("experimental-enabled");
    } else {
      $(".theme-body").classList.remove("experimental-enabled");
      this._popup.classList.remove("experimental-enabled");
    }
  },

  /**
   * Fired when the markers filter popup starts to show.
   */
  _onFilterPopupShowing: function () {
    $("#filter-button").setAttribute("open", "true");
  },

  /**
   * Fired when the markers filter popup starts to hide.
   */
  _onFilterPopupHiding: function () {
    $("#filter-button").removeAttribute("open");
  },

  /**
   * Fired when a menu item in the markers filter popup is checked or unchecked.
   */
  _onHiddenMarkersChanged: function () {
    let checkedMenuItems =
      $$("#performance-filter-menupopup menuitem[marker-type]:not([checked])");
    let hiddenMarkers = Array.map(checkedMenuItems, e => e.getAttribute("marker-type"));
    PerformanceController.setPref("hidden-markers", hiddenMarkers);
  },

  /**
   * Fired when a preference changes in the underlying OptionsView.
   * Propogated by the PerformanceController.
   */
  _onPrefChanged: function (prefName) {
    let value = PerformanceController.getOption(prefName);

    if (prefName === "experimental") {
      this._toggleExperimentalUI(value);
    }

    this.emit(EVENTS.UI_PREF_CHANGED, prefName, value);
  },

  toString: () => "[object ToolbarView]"
};

EventEmitter.decorate(ToolbarView);
PK
!<�FŬ�7chrome/devtools/content/performance-new/frame-script.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 addMessageListener, addEventListener, content */

/**
 * This frame script injects itself into perf-html.io and injects the profile
 * into the page. It is mostly taken from the Gecko Profiler Addon implementation.
 */

const TRANSFER_EVENT = "devtools:perf-html-transfer-profile";

let gProfile = null;

addMessageListener(TRANSFER_EVENT, e => {
  gProfile = e.data;
  // Eagerly try and see if the framescript was evaluated after perf loaded its scripts.
  connectToPage();
  // If not try again at DOMContentLoaded which should be called after the script
  // tag was synchronously loaded in.
  addEventListener("DOMContentLoaded", connectToPage);
});

function connectToPage() {
  const unsafeWindow = content.wrappedJSObject;
  if (unsafeWindow.connectToGeckoProfiler) {
    unsafeWindow.connectToGeckoProfiler(makeAccessibleToPage({
      getProfile: () => Promise.resolve(gProfile),
      getSymbolTable: (debugName, breakpadId) => getSymbolTable(debugName, breakpadId),
    }, unsafeWindow));
  }
}

/**
 * For now, do not try to symbolicate. Reject any attempt.
 */
function getSymbolTable(debugName, breakpadId) {
  // Errors will not properly clone into the content page as they bring privileged
  // stacks information into the page. In this case provide a mock object to maintain
  // the Error type object shape.
  const error = {
    message: `The DevTools' "perf" actor does not support symbolication.`
  };
  return Promise.reject(error);
}

// The following functions handle the security of cloning the object into the page.
// The code was taken from the original Gecko Profiler Add-on to maintain
// compatibility with the existing profile importing mechanism:
// See: https://github.com/devtools-html/Gecko-Profiler-Addon/blob/78138190b42565f54ce4022a5b28583406489ed2/data/tab-framescript.js

/**
 * Create a promise that can be used in the page.
 */
function createPromiseInPage(fun, contentGlobal) {
  function funThatClonesObjects(resolve, reject) {
    return fun(result => resolve(Cu.cloneInto(result, contentGlobal)),
               error => reject(Cu.cloneInto(error, contentGlobal)));
  }
  return new contentGlobal.Promise(Cu.exportFunction(funThatClonesObjects,
                                                     contentGlobal));
}

/**
 * Returns a function that calls the original function and tries to make the
 * return value available to the page.
 */
function wrapFunction(fun, contentGlobal) {
  return function () {
    let result = fun.apply(this, arguments);
    if (typeof result === "object") {
      if (("then" in result) && (typeof result.then === "function")) {
        // fun returned a promise.
        return createPromiseInPage((resolve, reject) =>
          result.then(resolve, reject), contentGlobal);
      }
      return Cu.cloneInto(result, contentGlobal);
    }
    return result;
  };
}

/**
 * Pass a simple object containing values that are objects or functions.
 * The objects or functions are wrapped in such a way that they can be
 * consumed by the page.
 */
function makeAccessibleToPage(obj, contentGlobal) {
  let result = Cu.createObjectIn(contentGlobal);
  for (let field in obj) {
    switch (typeof obj[field]) {
      case "function":
        Cu.exportFunction(
          wrapFunction(obj[field], contentGlobal), result, { defineAs: field });
        break;
      case "object":
        Cu.cloneInto(obj[field], result, { defineAs: field });
        break;
      default:
        result[field] = obj[field];
        break;
    }
  }
  return result;
}
PK
!<�u��OO6chrome/devtools/content/performance-new/initializer.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";

/* exported gInit, gDestroy */

const BrowserLoaderModule = {};
ChromeUtils.import("resource://devtools/client/shared/browser-loader.js", BrowserLoaderModule);
const { require } = BrowserLoaderModule.BrowserLoader({
  baseURI: "resource://devtools/client/memory/",
  window
});
const Perf = require("devtools/client/performance-new/components/Perf");
const { render, unmountComponentAtNode } = require("devtools/client/shared/vendor/react-dom");
const { createElement } = require("devtools/client/shared/vendor/react");

/**
 * Perform a simple initialization on the panel. Hook up event listeners.
 *
 * @param perfFront - The Perf actor's front. Used to start and stop recordings.
 */
function gInit(perfFront) {
  const props = {
    perfFront,
    receiveProfile: profile => {
      // Open up a new tab and send a message with the profile.
      const browser = top.gBrowser;
      const tab = browser.addTab("https://perf-html.io/from-addon");
      browser.selectedTab = tab;
      const mm = tab.linkedBrowser.messageManager;
      mm.loadFrameScript(
        "chrome://devtools/content/performance-new/frame-script.js",
        false
      );
      mm.sendAsyncMessage("devtools:perf-html-transfer-profile", profile);
    }
  };
  render(createElement(Perf, props), document.querySelector("#root"));
}

function gDestroy() {
  unmountComponentAtNode(document.querySelector("#root"));
}
PK
!<�Ƌ��2chrome/devtools/content/performance-new/perf.xhtml<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html [
  <!ENTITY % htmlDTD
    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "DTD/xhtml1-strict.dtd">
  %htmlDTD;
]>

<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<html xmlns="http://www.w3.org/1999/xhtml" dir="">
  <head>
    <link rel="stylesheet" href="chrome://devtools/skin/widgets.css" type="text/css"/>
    <link rel="stylesheet" href="chrome://devtools/skin/perf.css" type="text/css"/>
  </head>
  <body class="theme-body">
    <div id="root"></div>
    <script type="application/javascript" src="initializer.js"></script>
    <script type="application/javascript"
            src="chrome://devtools/content/shared/theme-switching.js"
            defer="true">
    </script>
  </body>
</html>
PK
!<��;uu0chrome/devtools/content/responsive.html/index.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 { BrowserLoader } =
  ChromeUtils.import("resource://devtools/client/shared/browser-loader.js", {});
const { require } = BrowserLoader({
  baseURI: "resource://devtools/client/responsive.html/",
  window
});
const Telemetry = require("devtools/client/shared/telemetry");
const { loadAgentSheet } = require("./utils/css");

const { createFactory, createElement } =
  require("devtools/client/shared/vendor/react");
const ReactDOM = require("devtools/client/shared/vendor/react-dom");
const { Provider } = require("devtools/client/shared/vendor/react-redux");

const message = require("./utils/message");
const App = createFactory(require("./app"));
const Store = require("./store");
const { loadDevices } = require("./actions/devices");
const { changeDisplayPixelRatio } = require("./actions/display-pixel-ratio");
const { changeLocation } = require("./actions/location");
const { loadReloadConditions } = require("./actions/reload-conditions");
const { addViewport, resizeViewport } = require("./actions/viewports");

// Exposed for use by tests
window.require = require;

let bootstrap = {

  telemetry: new Telemetry(),

  store: null,

  async init() {
    // Load a special UA stylesheet to reset certain styles such as dropdown
    // lists.
    loadAgentSheet(
      window,
      "resource://devtools/client/responsive.html/responsive-ua.css"
    );
    this.telemetry.toolOpened("responsive");
    let store = this.store = Store();
    let provider = createElement(Provider, { store }, App());
    ReactDOM.render(provider, document.querySelector("#root"));
    message.post(window, "init:done");
  },

  destroy() {
    this.store = null;
    this.telemetry.toolClosed("responsive");
    this.telemetry = null;
  },

  /**
   * While most actions will be dispatched by React components, some external
   * APIs that coordinate with the larger browser UI may also have actions to
   * to dispatch.  They can do so here.
   */
  dispatch(action) {
    if (!this.store) {
      // If actions are dispatched after store is destroyed, ignore them.  This
      // can happen in tests that close the tool quickly while async tasks like
      // initDevices() below are still pending.
      return;
    }
    this.store.dispatch(action);
  },

};

// manager.js sends a message to signal init
message.wait(window, "init").then(() => bootstrap.init());

// manager.js sends a message to signal init is done, which can be used for delayed
// startup work that shouldn't block initial load
message.wait(window, "post-init").then(() => {
  bootstrap.dispatch(loadDevices());
  bootstrap.dispatch(loadReloadConditions());
});

window.addEventListener("unload", function () {
  bootstrap.destroy();
}, {once: true});

// Allows quick testing of actions from the console
window.dispatch = action => bootstrap.dispatch(action);

// Expose the store on window for testing
Object.defineProperty(window, "store", {
  get: () => bootstrap.store,
  enumerable: true,
});

// Dispatch a `changeDisplayPixelRatio` action when the browser's pixel ratio is changing.
// This is usually triggered when the user changes the monitor resolution, or when the
// browser's window is dragged to a different display with a different pixel ratio.
// TODO: It would be better to move this watching into the actor, so that it can be
// better synchronized with any overrides that might be applied.  Also, reading a single
// value like this makes less sense with multiple viewports.
function onDevicePixelRatioChange() {
  let dpr = window.devicePixelRatio;
  let mql = window.matchMedia(`(resolution: ${dpr}dppx)`);

  function listener() {
    bootstrap.dispatch(changeDisplayPixelRatio(window.devicePixelRatio));
    mql.removeListener(listener);
    onDevicePixelRatioChange();
  }

  mql.addListener(listener);
}

/**
 * Called by manager.js to add the initial viewport based on the original page.
 */
window.addInitialViewport = contentURI => {
  try {
    onDevicePixelRatioChange();
    bootstrap.dispatch(changeLocation(contentURI));
    bootstrap.dispatch(changeDisplayPixelRatio(window.devicePixelRatio));
    bootstrap.dispatch(addViewport());
  } catch (e) {
    console.error(e);
  }
};

/**
 * Called by manager.js when tests want to check the viewport size.
 */
window.getViewportSize = () => {
  let { width, height } = bootstrap.store.getState().viewports[0];
  return { width, height };
};

/**
 * Called by manager.js to set viewport size from tests, GCLI, etc.
 */
window.setViewportSize = ({ width, height }) => {
  try {
    bootstrap.dispatch(resizeViewport(0, width, height));
  } catch (e) {
    console.error(e);
  }
};

/**
 * Called by manager.js to access the viewport's browser, either for testing
 * purposes or to reload it when touch simulation is enabled.
 * A messageManager getter is added on the object to provide an easy access
 * to the message manager without pulling the frame loader.
 */
window.getViewportBrowser = () => {
  let browser = document.querySelector("iframe.browser");
  if (!browser.messageManager) {
    Object.defineProperty(browser, "messageManager", {
      get() {
        return this.frameLoader.messageManager;
      },
      configurable: true,
      enumerable: true,
    });
  }
  return browser;
};
PK
!<�4�''3chrome/devtools/content/responsive.html/index.xhtml<?xml version="1.0" encoding="UTF-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/. -->
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <link rel="stylesheet" type="text/css"
          href="resource://devtools/client/responsive.html/index.css"/>
    <script type="application/javascript"
            src="chrome://devtools/content/shared/theme-switching.js"></script>
    <script type="application/javascript"
            src="./index.js"></script>
  </head>
  <body class="theme-body" role="application">
    <div id="root"/>
  </body>
</html>
PK
!<���QSS0chrome/devtools/content/scratchpad/scratchpad.js/* vim:set ts=2 sw=2 sts=2 et:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/*
 * Original version history can be found here:
 * https://github.com/mozilla/workspace
 *
 * Copied and relicensed from the Public Domain.
 * See bug 653934 for details.
 * https://bugzilla.mozilla.org/show_bug.cgi?id=653934
 */

"use strict";

const SCRATCHPAD_CONTEXT_CONTENT = 1;
const SCRATCHPAD_CONTEXT_BROWSER = 2;
const BUTTON_POSITION_SAVE = 0;
const BUTTON_POSITION_CANCEL = 1;
const BUTTON_POSITION_DONT_SAVE = 2;
const BUTTON_POSITION_REVERT = 0;
const EVAL_FUNCTION_TIMEOUT = 1000; // milliseconds

const MAXIMUM_FONT_SIZE = 96;
const MINIMUM_FONT_SIZE = 6;
const NORMAL_FONT_SIZE = 12;

const SCRATCHPAD_L10N = "chrome://devtools/locale/scratchpad.properties";
const DEVTOOLS_CHROME_ENABLED = "devtools.chrome.enabled";
const PREF_RECENT_FILES_MAX = "devtools.scratchpad.recentFilesMax";
const SHOW_LINE_NUMBERS = "devtools.scratchpad.lineNumbers";
const WRAP_TEXT = "devtools.scratchpad.wrapText";
const SHOW_TRAILING_SPACE = "devtools.scratchpad.showTrailingSpace";
const EDITOR_FONT_SIZE = "devtools.scratchpad.editorFontSize";
const ENABLE_AUTOCOMPLETION = "devtools.scratchpad.enableAutocompletion";
const TAB_SIZE = "devtools.editor.tabsize";
const FALLBACK_CHARSET_LIST = "intl.fallbackCharsetList.ISO-8859-1";

const VARIABLES_VIEW_URL = "chrome://devtools/content/shared/widgets/VariablesView.xul";

const {require, loader} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});

const Editor = require("devtools/client/sourceeditor/editor");
const TargetFactory = require("devtools/client/framework/target").TargetFactory;
const EventEmitter = require("devtools/shared/event-emitter");
const {DevToolsWorker} = require("devtools/shared/worker/worker");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const flags = require("devtools/shared/flags");
const promise = require("promise");
const defer = require("devtools/shared/defer");
const Services = require("Services");
const {gDevTools} = require("devtools/client/framework/devtools");
const { extend } = require("devtools/shared/extend");

const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
const {NetUtil} = require("resource://gre/modules/NetUtil.jsm");
const {ScratchpadManager} = require("resource://devtools/client/scratchpad/scratchpad-manager.jsm");
const {addDebuggerToGlobal} = require("resource://gre/modules/jsdebugger.jsm");
const {OS} = require("resource://gre/modules/osfile.jsm");
const {Reflect} = require("resource://gre/modules/reflect.jsm");

// Use privileged promise in panel documents to prevent having them to freeze
// during toolbox destruction. See bug 1402779.
const Promise = require("Promise");

XPCOMUtils.defineConstant(this, "SCRATCHPAD_CONTEXT_CONTENT", SCRATCHPAD_CONTEXT_CONTENT);
XPCOMUtils.defineConstant(this, "SCRATCHPAD_CONTEXT_BROWSER", SCRATCHPAD_CONTEXT_BROWSER);
XPCOMUtils.defineConstant(this, "BUTTON_POSITION_SAVE", BUTTON_POSITION_SAVE);
XPCOMUtils.defineConstant(this, "BUTTON_POSITION_CANCEL", BUTTON_POSITION_CANCEL);
XPCOMUtils.defineConstant(this, "BUTTON_POSITION_DONT_SAVE", BUTTON_POSITION_DONT_SAVE);
XPCOMUtils.defineConstant(this, "BUTTON_POSITION_REVERT", BUTTON_POSITION_REVERT);

ChromeUtils.defineModuleGetter(this, "VariablesView",
  "resource://devtools/client/shared/widgets/VariablesView.jsm");

ChromeUtils.defineModuleGetter(this, "VariablesViewController",
  "resource://devtools/client/shared/widgets/VariablesViewController.jsm");

loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true);

loader.lazyRequireGetter(this, "DebuggerClient", "devtools/shared/client/debugger-client", true);
loader.lazyRequireGetter(this, "EnvironmentClient", "devtools/shared/client/environment-client");
loader.lazyRequireGetter(this, "ObjectClient", "devtools/shared/client/object-client");
loader.lazyRequireGetter(this, "HUDService", "devtools/client/webconsole/hudservice", true);

XPCOMUtils.defineLazyGetter(this, "REMOTE_TIMEOUT", () =>
  Services.prefs.getIntPref("devtools.debugger.remote-timeout"));

ChromeUtils.defineModuleGetter(this, "ShortcutUtils",
  "resource://gre/modules/ShortcutUtils.jsm");

ChromeUtils.defineModuleGetter(this, "Reflect",
  "resource://gre/modules/reflect.jsm");

var WebConsoleUtils = require("devtools/client/webconsole/utils").Utils;

/**
 * The scratchpad object handles the Scratchpad window functionality.
 */
var Scratchpad = {
  _instanceId: null,
  _initialWindowTitle: document.title,
  _dirty: false,

  /**
   * Check if provided string is a mode-line and, if it is, return an
   * object with its values.
   *
   * @param string aLine
   * @return string
   */
  _scanModeLine: function SP__scanModeLine(aLine = "")
  {
    aLine = aLine.trim();

    let obj = {};
    let ch1 = aLine.charAt(0);
    let ch2 = aLine.charAt(1);

    if (ch1 !== "/" || (ch2 !== "*" && ch2 !== "/")) {
      return obj;
    }

    aLine = aLine
      .replace(/^\/\//, "")
      .replace(/^\/\*/, "")
      .replace(/\*\/$/, "");

    aLine.split(",").forEach(pair => {
      let [key, val] = pair.split(":");

      if (key && val) {
        obj[key.trim()] = val.trim();
      }
    });

    return obj;
  },

  /**
   * Add the event listeners for popupshowing events.
   */
  _setupPopupShowingListeners: function SP_setupPopupShowing() {
    let elementIDs = ["sp-menu_editpopup", "scratchpad-text-popup"];

    for (let elementID of elementIDs) {
      let elem = document.getElementById(elementID);
      if (elem) {
        elem.addEventListener("popupshowing", function () {
          goUpdateGlobalEditMenuItems();
          let commands = ["cmd_undo", "cmd_redo", "cmd_delete", "cmd_findAgain"];
          commands.forEach(goUpdateCommand);
        });
      }
    }
  },

  /**
   * Add the event event listeners for command events.
   */
  _setupCommandListeners: function SP_setupCommands() {
    let commands = {
      "cmd_find": () => {
        goDoCommand("cmd_find");
      },
      "cmd_findAgain": () => {
        goDoCommand("cmd_findAgain");
      },
      "cmd_gotoLine": () => {
        goDoCommand("cmd_gotoLine");
      },
      "sp-cmd-newWindow": () => {
        Scratchpad.openScratchpad();
      },
      "sp-cmd-openFile": () => {
        Scratchpad.openFile();
      },
      "sp-cmd-clearRecentFiles": () => {
        Scratchpad.clearRecentFiles();
      },
      "sp-cmd-save": () => {
        Scratchpad.saveFile();
      },
      "sp-cmd-saveas": () => {
        Scratchpad.saveFileAs();
      },
      "sp-cmd-revert": () => {
        Scratchpad.promptRevert();
      },
      "sp-cmd-close": () => {
        Scratchpad.close();
      },
      "sp-cmd-run": () => {
        Scratchpad.run();
      },
      "sp-cmd-inspect": () => {
        Scratchpad.inspect();
      },
      "sp-cmd-display": () => {
        Scratchpad.display();
      },
      "sp-cmd-pprint": () => {
        Scratchpad.prettyPrint();
      },
      "sp-cmd-contentContext": () => {
        Scratchpad.setContentContext();
      },
      "sp-cmd-browserContext": () => {
        Scratchpad.setBrowserContext();
      },
      "sp-cmd-reloadAndRun": () => {
        Scratchpad.reloadAndRun();
      },
      "sp-cmd-evalFunction": () => {
        Scratchpad.evalTopLevelFunction();
      },
      "sp-cmd-errorConsole": () => {
        Scratchpad.openErrorConsole();
      },
      "sp-cmd-webConsole": () => {
        Scratchpad.openWebConsole();
      },
      "sp-cmd-documentationLink": () => {
        Scratchpad.openDocumentationPage();
      },
      "sp-cmd-hideSidebar": () => {
        Scratchpad.sidebar.hide();
      },
      "sp-cmd-line-numbers": () => {
        Scratchpad.toggleEditorOption("lineNumbers", SHOW_LINE_NUMBERS);
      },
      "sp-cmd-wrap-text": () => {
        Scratchpad.toggleEditorOption("lineWrapping", WRAP_TEXT);
      },
      "sp-cmd-highlight-trailing-space": () => {
        Scratchpad.toggleEditorOption("showTrailingSpace", SHOW_TRAILING_SPACE);
      },
      "sp-cmd-larger-font": () => {
        Scratchpad.increaseFontSize();
      },
      "sp-cmd-smaller-font": () => {
        Scratchpad.decreaseFontSize();
      },
      "sp-cmd-normal-font": () => {
        Scratchpad.normalFontSize();
      },
    };

    for (let command in commands) {
      let elem = document.getElementById(command);
      if (elem) {
        elem.addEventListener("command", commands[command]);
      }
    }
  },

  /**
   * Check or uncheck view menu items according to stored preferences.
   */
  _updateViewMenuItems: function SP_updateViewMenuItems() {
    this._updateViewMenuItem(SHOW_LINE_NUMBERS, "sp-menu-line-numbers");
    this._updateViewMenuItem(WRAP_TEXT, "sp-menu-word-wrap");
    this._updateViewMenuItem(SHOW_TRAILING_SPACE, "sp-menu-highlight-trailing-space");
    this._updateViewFontMenuItem(MINIMUM_FONT_SIZE, "sp-cmd-smaller-font");
    this._updateViewFontMenuItem(MAXIMUM_FONT_SIZE, "sp-cmd-larger-font");
  },

  /**
   * Check or uncheck view menu item according to stored preferences.
   */
  _updateViewMenuItem: function SP_updateViewMenuItem(preferenceName, menuId) {
    let checked = Services.prefs.getBoolPref(preferenceName);
    if (checked) {
      document.getElementById(menuId).setAttribute("checked", true);
    } else {
      document.getElementById(menuId).removeAttribute("checked");
    }
  },

  /**
   * Disable view menu item if the stored font size is equals to the given one.
   */
  _updateViewFontMenuItem: function SP_updateViewFontMenuItem(fontSize, commandId) {
    let prefFontSize = Services.prefs.getIntPref(EDITOR_FONT_SIZE);
    if (prefFontSize === fontSize) {
      document.getElementById(commandId).setAttribute("disabled", true);
    }
  },

  /**
   * The script execution context. This tells Scratchpad in which context the
   * script shall execute.
   *
   * Possible values:
   *   - SCRATCHPAD_CONTEXT_CONTENT to execute code in the context of the current
   *   tab content window object.
   *   - SCRATCHPAD_CONTEXT_BROWSER to execute code in the context of the
   *   currently active chrome window object.
   */
  executionContext: SCRATCHPAD_CONTEXT_CONTENT,

  /**
   * Tells if this Scratchpad is initialized and ready for use.
   * @boolean
   * @see addObserver
   */
  initialized: false,

  /**
   * Returns the 'dirty' state of this Scratchpad.
   */
  get dirty()
  {
    let clean = this.editor && this.editor.isClean();
    return this._dirty || !clean;
  },

  /**
   * Sets the 'dirty' state of this Scratchpad.
   */
  set dirty(aValue)
  {
    this._dirty = aValue;
    if (!aValue && this.editor)
      this.editor.setClean();
    this._updateTitle();
  },

  /**
   * Retrieve the xul:notificationbox DOM element. It notifies the user when
   * the current code execution context is SCRATCHPAD_CONTEXT_BROWSER.
   */
  get notificationBox()
  {
    return document.getElementById("scratchpad-notificationbox");
  },

  /**
   * Hide the menu bar.
   */
  hideMenu: function SP_hideMenu()
  {
    document.getElementById("sp-menubar").style.display = "none";
  },

  /**
   * Show the menu bar.
   */
  showMenu: function SP_showMenu()
  {
    document.getElementById("sp-menubar").style.display = "";
  },

  /**
   * Get the editor content, in the given range. If no range is given you get
   * the entire editor content.
   *
   * @param number [aStart=0]
   *        Optional, start from the given offset.
   * @param number [aEnd=content char count]
   *        Optional, end offset for the text you want. If this parameter is not
   *        given, then the text returned goes until the end of the editor
   *        content.
   * @return string
   *         The text in the given range.
   */
  getText: function SP_getText(aStart, aEnd)
  {
    var value = this.editor.getText();
    return value.slice(aStart || 0, aEnd || value.length);
  },

  /**
   * Set the filename in the scratchpad UI and object
   *
   * @param string aFilename
   *        The new filename
   */
  setFilename: function SP_setFilename(aFilename)
  {
    this.filename = aFilename;
    this._updateTitle();
  },

  /**
   * Update the Scratchpad window title based on the current state.
   * @private
   */
  _updateTitle: function SP__updateTitle()
  {
    let title = this.filename || this._initialWindowTitle;

    if (this.dirty)
      title = "*" + title;

    document.title = title;
  },

  /**
   * Get the current state of the scratchpad. Called by the
   * Scratchpad Manager for session storing.
   *
   * @return object
   *        An object with 3 properties: filename, text, and
   *        executionContext.
   */
  getState: function SP_getState()
  {
    return {
      filename: this.filename,
      text: this.getText(),
      executionContext: this.executionContext,
      saved: !this.dirty
    };
  },

  /**
   * Set the filename and execution context using the given state. Called
   * when scratchpad is being restored from a previous session.
   *
   * @param object aState
   *        An object with filename and executionContext properties.
   */
  setState: function SP_setState(aState)
  {
    if (aState.filename)
      this.setFilename(aState.filename);

    this.dirty = !aState.saved;

    if (aState.executionContext == SCRATCHPAD_CONTEXT_BROWSER)
      this.setBrowserContext();
    else
      this.setContentContext();
  },

  /**
   * Get the most recent main chrome browser window
   */
  get browserWindow()
  {
    return Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
  },

  /**
   * Get the gBrowser object of the most recent browser window.
   */
  get gBrowser()
  {
    let recentWin = this.browserWindow;
    return recentWin ? recentWin.gBrowser : null;
  },

  /**
   * Unique name for the current Scratchpad instance. Used to distinguish
   * Scratchpad windows between each other. See bug 661762.
   */
  get uniqueName()
  {
    return "Scratchpad/" + this._instanceId;
  },


  /**
   * Sidebar that contains the VariablesView for object inspection.
   */
  get sidebar()
  {
    if (!this._sidebar) {
      this._sidebar = new ScratchpadSidebar(this);
    }
    return this._sidebar;
  },

  /**
   * Replaces context of an editor with provided value (a string).
   * Note: this method is simply a shortcut to editor.setText.
   */
  setText: function SP_setText(value)
  {
    return this.editor.setText(value);
  },

  /**
   * Evaluate a string in the currently desired context, that is either the
   * chrome window or the tab content window object.
   *
   * @param string aString
   *        The script you want to evaluate.
   * @return Promise
   *         The promise for the script evaluation result.
   */
  evaluate: function SP_evaluate(aString)
  {
    let connection;
    if (this.target) {
      connection = ScratchpadTarget.consoleFor(this.target);
    }
    else if (this.executionContext == SCRATCHPAD_CONTEXT_CONTENT) {
      connection = ScratchpadTab.consoleFor(this.gBrowser.selectedTab);
    }
    else {
      connection = ScratchpadWindow.consoleFor(this.browserWindow);
    }

    let evalOptions = { url: this.uniqueName };

    return connection.then(({ debuggerClient, webConsoleClient }) => {
      let deferred = defer();

      webConsoleClient.evaluateJSAsync(aString, aResponse => {
        this.debuggerClient = debuggerClient;
        this.webConsoleClient = webConsoleClient;
        if (aResponse.error) {
          deferred.reject(aResponse);
        }
        else if (aResponse.exception !== null) {
          deferred.resolve([aString, aResponse]);
        }
        else {
          deferred.resolve([aString, undefined, aResponse.result]);
        }
      }, evalOptions);

      return deferred.promise;
    });
  },

  /**
   * Execute the selected text (if any) or the entire editor content in the
   * current context.
   *
   * @return Promise
   *         The promise for the script evaluation result.
   */
  execute: function SP_execute()
  {
    WebConsoleUtils.usageCount++;
    let selection = this.editor.getSelection() || this.getText();
    return this.evaluate(selection);
  },

  /**
   * Execute the selected text (if any) or the entire editor content in the
   * current context.
   *
   * @return Promise
   *         The promise for the script evaluation result.
   */
  run: function SP_run()
  {
    let deferred = defer();
    let reject = aReason => deferred.reject(aReason);

    this.execute().then(([aString, aError, aResult]) => {
      let resolve = () => deferred.resolve([aString, aError, aResult]);

      if (aError) {
        this.writeAsErrorComment(aError).then(resolve, reject);
      }
      else {
        this.editor.dropSelection();
        resolve();
      }
    }, reject);

    return deferred.promise;
  },

  /**
   * Execute the selected text (if any) or the entire editor content in the
   * current context. The resulting object is inspected up in the sidebar.
   *
   * @return Promise
   *         The promise for the script evaluation result.
   */
  inspect: function SP_inspect()
  {
    let deferred = defer();
    let reject = aReason => deferred.reject(aReason);

    this.execute().then(([aString, aError, aResult]) => {
      let resolve = () => deferred.resolve([aString, aError, aResult]);

      if (aError) {
        this.writeAsErrorComment(aError).then(resolve, reject);
      }
      else {
        this.editor.dropSelection();
        this.sidebar.open(aString, aResult).then(resolve, reject);
      }
    }, reject);

    return deferred.promise;
  },

  /**
   * Reload the current page and execute the entire editor content when
   * the page finishes loading. Note that this operation should be available
   * only in the content context.
   *
   * @return Promise
   *         The promise for the script evaluation result.
   */
  reloadAndRun: function SP_reloadAndRun()
  {
    let deferred = defer();

    if (this.executionContext !== SCRATCHPAD_CONTEXT_CONTENT) {
      console.error(this.strings.
                    GetStringFromName("scratchpadContext.invalid"));
      return;
    }

    let target = TargetFactory.forTab(this.gBrowser.selectedTab);
    target.once("navigate", () => {
      this.run().then(results => deferred.resolve(results));
    });
    target.makeRemote().then(() => target.activeTab.reload());

    return deferred.promise;
  },

  /**
   * Execute the selected text (if any) or the entire editor content in the
   * current context. The evaluation result is inserted into the editor after
   * the selected text, or at the end of the editor content if there is no
   * selected text.
   *
   * @return Promise
   *         The promise for the script evaluation result.
   */
  display: function SP_display()
  {
    let deferred = defer();
    let reject = aReason => deferred.reject(aReason);

    this.execute().then(([aString, aError, aResult]) => {
      let resolve = () => deferred.resolve([aString, aError, aResult]);

      if (aError) {
        this.writeAsErrorComment(aError).then(resolve, reject);
      }
      else if (VariablesView.isPrimitive({ value: aResult })) {
        this._writePrimitiveAsComment(aResult).then(resolve, reject);
      }
      else {
        let objectClient = new ObjectClient(this.debuggerClient, aResult);
        objectClient.getDisplayString(aResponse => {
          if (aResponse.error) {
            reportError("display", aResponse);
            reject(aResponse);
          }
          else {
            this.writeAsComment(aResponse.displayString);
            resolve();
          }
        });
      }
    }, reject);

    return deferred.promise;
  },

  _prettyPrintWorker: null,

  /**
   * Get or create the worker that handles pretty printing.
   */
  get prettyPrintWorker() {
    if (!this._prettyPrintWorker) {
      this._prettyPrintWorker = new DevToolsWorker(
        "resource://devtools/server/actors/pretty-print-worker.js",
        { name: "pretty-print",
          verbose: flags.wantLogging }
      );
    }
    return this._prettyPrintWorker;
  },

  /**
   * Pretty print the source text inside the scratchpad.
   *
   * @return Promise
   *         A promise resolved with the pretty printed code, or rejected with
   *         an error.
   */
  prettyPrint: function SP_prettyPrint() {
    const uglyText = this.getText();
    const tabsize = Services.prefs.getIntPref(TAB_SIZE);

    return this.prettyPrintWorker.performTask("pretty-print", {
      url: "(scratchpad)",
      indent: tabsize,
      source: uglyText
    }).then(data => {
      this.editor.setText(data.code);
    }).catch(error => {
      this.writeAsErrorComment({ exception: error });
      throw error;
    });
  },

  /**
   * Parse the text and return an AST. If we can't parse it, write an error
   * comment and return false.
   */
  _parseText: function SP__parseText(aText) {
    try {
      return Reflect.parse(aText);
    } catch (e) {
      this.writeAsErrorComment({ exception: DevToolsUtils.safeErrorString(e) });
      return false;
    }
  },

  /**
   * Determine if the given AST node location contains the given cursor
   * position.
   *
   * @returns Boolean
   */
  _containsCursor: function (aLoc, aCursorPos) {
    // Our line numbers are 1-based, while CodeMirror's are 0-based.
    const lineNumber = aCursorPos.line + 1;
    const columnNumber = aCursorPos.ch;

    if (aLoc.start.line <= lineNumber && aLoc.end.line >= lineNumber) {
      if (aLoc.start.line === aLoc.end.line) {
        return aLoc.start.column <= columnNumber
          && aLoc.end.column >= columnNumber;
      }

      if (aLoc.start.line == lineNumber) {
        return columnNumber >= aLoc.start.column;
      }

      if (aLoc.end.line == lineNumber) {
        return columnNumber <= aLoc.end.column;
      }

      return true;
    }

    return false;
  },

  /**
   * Find the top level function AST node that the cursor is within.
   *
   * @returns Object|null
   */
  _findTopLevelFunction: function SP__findTopLevelFunction(aAst, aCursorPos) {
    for (let statement of aAst.body) {
      switch (statement.type) {
        case "FunctionDeclaration":
          if (this._containsCursor(statement.loc, aCursorPos)) {
            return statement;
          }
          break;

        case "VariableDeclaration":
          for (let decl of statement.declarations) {
            if (!decl.init) {
              continue;
            }
            if ((decl.init.type == "FunctionExpression"
               || decl.init.type == "ArrowFunctionExpression")
              && this._containsCursor(decl.loc, aCursorPos)) {
              return decl;
            }
          }
          break;
      }
    }

    return null;
  },

  /**
   * Get the source text associated with the given function statement.
   *
   * @param Object aFunction
   * @param String aFullText
   * @returns String
   */
  _getFunctionText: function SP__getFunctionText(aFunction, aFullText) {
    let functionText = "";
    // Initially set to 0, but incremented first thing in the loop below because
    // line numbers are 1 based, not 0 based.
    let lineNumber = 0;
    const { start, end } = aFunction.loc;
    const singleLine = start.line === end.line;

    for (let line of aFullText.split(/\n/g)) {
      lineNumber++;

      if (singleLine && start.line === lineNumber) {
        functionText = line.slice(start.column, end.column);
        break;
      }

      if (start.line === lineNumber) {
        functionText += line.slice(start.column) + "\n";
        continue;
      }

      if (end.line === lineNumber) {
        functionText += line.slice(0, end.column);
        break;
      }

      if (start.line < lineNumber && end.line > lineNumber) {
        functionText += line + "\n";
      }
    }

    return functionText;
  },

  /**
   * Evaluate the top level function that the cursor is resting in.
   *
   * @returns Promise [text, error, result]
   */
  evalTopLevelFunction: function SP_evalTopLevelFunction() {
    const text = this.getText();
    const ast = this._parseText(text);
    if (!ast) {
      return promise.resolve([text, undefined, undefined]);
    }

    const cursorPos = this.editor.getCursor();
    const funcStatement = this._findTopLevelFunction(ast, cursorPos);
    if (!funcStatement) {
      return promise.resolve([text, undefined, undefined]);
    }

    let functionText = this._getFunctionText(funcStatement, text);

    // TODO: This is a work around for bug 940086. It should be removed when
    // that is fixed.
    if (funcStatement.type == "FunctionDeclaration"
        && !functionText.startsWith("function ")) {
      functionText = "function " + functionText;
      funcStatement.loc.start.column -= 9;
    }

    // The decrement by one is because our line numbers are 1-based, while
    // CodeMirror's are 0-based.
    const from = {
      line: funcStatement.loc.start.line - 1,
      ch: funcStatement.loc.start.column
    };
    const to = {
      line: funcStatement.loc.end.line - 1,
      ch: funcStatement.loc.end.column
    };

    const marker = this.editor.markText(from, to, "eval-text");
    setTimeout(() => marker.clear(), EVAL_FUNCTION_TIMEOUT);

    return this.evaluate(functionText);
  },

  /**
   * Writes out a primitive value as a comment. This handles values which are
   * to be printed directly (number, string) as well as grips to values
   * (null, undefined, longString).
   *
   * @param any aValue
   *        The value to print.
   * @return Promise
   *         The promise that resolves after the value has been printed.
   */
  _writePrimitiveAsComment: function SP__writePrimitiveAsComment(aValue)
  {
    let deferred = defer();

    if (aValue.type == "longString") {
      let client = this.webConsoleClient;
      client.longString(aValue).substring(0, aValue.length, aResponse => {
        if (aResponse.error) {
          reportError("display", aResponse);
          deferred.reject(aResponse);
        }
        else {
          deferred.resolve(aResponse.substring);
        }
      });
    }
    else {
      deferred.resolve(aValue.type || aValue);
    }

    return deferred.promise.then(aComment => {
      this.writeAsComment(aComment);
    });
  },

  /**
   * Write out a value at the next line from the current insertion point.
   * The comment block will always be preceded by a newline character.
   * @param object aValue
   *        The Object to write out as a string
   */
  writeAsComment: function SP_writeAsComment(aValue)
  {
    let value = "\n/*\n" + aValue + "\n*/";

    if (this.editor.somethingSelected()) {
      let from = this.editor.getCursor("end");
      this.editor.replaceSelection(this.editor.getSelection() + value);
      let to = this.editor.getPosition(this.editor.getOffset(from) + value.length);
      this.editor.setSelection(from, to);
      return;
    }

    let text = this.editor.getText();
    this.editor.setText(text + value);

    let [ from, to ] = this.editor.getPosition(text.length, (text + value).length);
    this.editor.setSelection(from, to);
  },

  /**
   * Write out an error at the current insertion point as a block comment
   * @param object aValue
   *        The error object to write out the message and stack trace. It must
   *        contain an |exception| property with the actual error thrown, but it
   *        will often be the entire response of an evaluateJS request.
   * @return Promise
   *         The promise that indicates when writing the comment completes.
   */
  writeAsErrorComment: function SP_writeAsErrorComment(aError)
  {
    let deferred = defer();

    if (VariablesView.isPrimitive({ value: aError.exception })) {
      let error = aError.exception;
      let type = error.type;
      if (type == "undefined" ||
          type == "null" ||
          type == "Infinity" ||
          type == "-Infinity" ||
          type == "NaN" ||
          type == "-0") {
        deferred.resolve(type);
      }
      else if (type == "longString") {
        deferred.resolve(error.initial + "\u2026");
      }
      else {
        deferred.resolve(error);
      }
    } else if ("preview" in aError.exception) {
      let error = aError.exception;
      let stack = this._constructErrorStack(error.preview);
      if (typeof aError.exceptionMessage == "string") {
        deferred.resolve(aError.exceptionMessage + stack);
      } else {
        deferred.resolve(stack);
      }
    } else {
      // If there is no preview information, we need to ask the server for more.
      let objectClient = new ObjectClient(this.debuggerClient, aError.exception);
      objectClient.getPrototypeAndProperties(aResponse => {
        if (aResponse.error) {
          deferred.reject(aResponse);
          return;
        }

        let { ownProperties, safeGetterValues } = aResponse;
        let error = Object.create(null);

        // Combine all the property descriptor/getter values into one object.
        for (let key of Object.keys(safeGetterValues)) {
          error[key] = safeGetterValues[key].getterValue;
        }

        for (let key of Object.keys(ownProperties)) {
          error[key] = ownProperties[key].value;
        }

        let stack = this._constructErrorStack(error);

        if (typeof error.message == "string") {
          deferred.resolve(error.message + stack);
        }
        else {
          objectClient.getDisplayString(aResponse => {
            if (aResponse.error) {
              deferred.reject(aResponse);
            }
            else if (typeof aResponse.displayString == "string") {
              deferred.resolve(aResponse.displayString + stack);
            }
            else {
              deferred.resolve(stack);
            }
          });
        }
      });
    }

    return deferred.promise.then(aMessage => {
      console.error(aMessage);
      this.writeAsComment("Exception: " + aMessage);
    });
  },

  /**
   * Assembles the best possible stack from the properties of the provided
   * error.
   */
  _constructErrorStack(error) {
    let stack;
    if (typeof error.stack == "string" && error.stack) {
      stack = error.stack;
    } else if (typeof error.fileName == "string") {
      stack = "@" + error.fileName;
      if (typeof error.lineNumber == "number") {
        stack += ":" + error.lineNumber;
      }
    } else if (typeof error.filename == "string") {
      stack = "@" + error.filename;
      if (typeof error.lineNumber == "number") {
        stack += ":" + error.lineNumber;
        if (typeof error.columnNumber == "number") {
          stack += ":" + error.columnNumber;
        }
      }
    } else if (typeof error.lineNumber == "number") {
      stack = "@" + error.lineNumber;
      if (typeof error.columnNumber == "number") {
        stack += ":" + error.columnNumber;
      }
    }

    return stack ? "\n" + stack.replace(/\n$/, "") : "";
  },

  // Menu Operations

  /**
   * Open a new Scratchpad window.
   *
   * @return nsIWindow
   */
  openScratchpad: function SP_openScratchpad()
  {
    return ScratchpadManager.openScratchpad();
  },

  /**
   * Export the textbox content to a file.
   *
   * @param nsIFile aFile
   *        The file where you want to save the textbox content.
   * @param boolean aNoConfirmation
   *        If the file already exists, ask for confirmation?
   * @param boolean aSilentError
   *        True if you do not want to display an error when file save fails,
   *        false otherwise.
   * @param function aCallback
   *        Optional function you want to call when file save completes. It will
   *        get the following arguments:
   *        1) the nsresult status code for the export operation.
   */
  exportToFile: function SP_exportToFile(aFile, aNoConfirmation, aSilentError,
                                         aCallback)
  {
    if (!aNoConfirmation && aFile.exists() &&
        !window.confirm(this.strings.
                        GetStringFromName("export.fileOverwriteConfirmation"))) {
      return;
    }

    let encoder = new TextEncoder();
    let buffer = encoder.encode(this.getText());
    let writePromise = OS.File.writeAtomic(aFile.path, buffer, {tmpPath: aFile.path + ".tmp"});
    writePromise.then(value => {
      if (aCallback) {
        aCallback.call(this, Cr.NS_OK);
      }
    }, reason => {
      if (!aSilentError) {
        window.alert(this.strings.GetStringFromName("saveFile.failed"));
      }
      if (aCallback) {
        aCallback.call(this, Cr.NS_ERROR_UNEXPECTED);
      }
    });

  },

  /**
   * Get a list of applicable charsets.
   * The best charset, defaulting to "UTF-8"
   *
   * @param string aBestCharset
   * @return array of strings
   */
  _getApplicableCharsets: function SP__getApplicableCharsets(aBestCharset = "UTF-8") {
    let charsets = Services.prefs.getCharPref(
      FALLBACK_CHARSET_LIST).split(",").filter(function (value) {
        return value.length;
      });
    charsets.unshift(aBestCharset);
    return charsets;
  },

  /**
   * Get content converted to unicode, using a list of input charset to try.
   *
   * @param string aContent
   * @param array of string aCharsetArray
   * @return string
   */
  _getUnicodeContent: function SP__getUnicodeContent(aContent, aCharsetArray) {
    let content = null,
      converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Ci.nsIScriptableUnicodeConverter),
      success = aCharsetArray.some(charset => {
        try {
          converter.charset = charset;
          content = converter.ConvertToUnicode(aContent);
          return true;
        } catch (e) {
          this.notificationBox.appendNotification(
              this.strings.formatStringFromName("importFromFile.convert.failed",
                                                [ charset ], 1),
              "file-import-convert-failed",
              null,
              this.notificationBox.PRIORITY_WARNING_HIGH,
              null);
        }
      });
    return content;
  },

  /**
   * Read the content of a file and put it into the textbox.
   *
   * @param nsIFile aFile
   *        The file you want to save the textbox content into.
   * @param boolean aSilentError
   *        True if you do not want to display an error when file load fails,
   *        false otherwise.
   * @param function aCallback
   *        Optional function you want to call when file load completes. It will
   *        get the following arguments:
   *        1) the nsresult status code for the import operation.
   *        2) the data that was read from the file, if any.
   */
  importFromFile: function SP_importFromFile(aFile, aSilentError, aCallback)
  {
    // Prevent file type detection.
    let channel = NetUtil.newChannel({
      uri: NetUtil.newURI(aFile),
      loadingNode: window.document,
      securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS,
      contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER});
    channel.contentType = "application/javascript";

    this.notificationBox.removeAllNotifications(false);

    NetUtil.asyncFetch(channel, (aInputStream, aStatus) => {
      let content = null;

      if (Components.isSuccessCode(aStatus)) {
        let charsets = this._getApplicableCharsets();
        content = NetUtil.readInputStreamToString(aInputStream,
                                                  aInputStream.available());
        content = this._getUnicodeContent(content, charsets);
        if (!content) {
          let message = this.strings.formatStringFromName(
            "importFromFile.convert.failed",
            [ charsets.join(", ") ],
            1);
          this.notificationBox.appendNotification(
            message,
            "file-import-convert-failed",
            null,
            this.notificationBox.PRIORITY_CRITICAL_MEDIUM,
            null);
          if (aCallback) {
            aCallback.call(this, aStatus, content);
          }
          return;
        }
        // Check to see if the first line is a mode-line comment.
        let line = content.split("\n")[0];
        let modeline = this._scanModeLine(line);
        let chrome = Services.prefs.getBoolPref(DEVTOOLS_CHROME_ENABLED);

        if (chrome && modeline["-sp-context"] === "browser") {
          this.setBrowserContext();
        }

        this.editor.setText(content);
        this.editor.clearHistory();
        this.dirty = false;
        document.getElementById("sp-cmd-revert").setAttribute("disabled", true);
      }
      else if (!aSilentError) {
        window.alert(this.strings.GetStringFromName("openFile.failed"));
      }
      this.setFilename(aFile.path);
      this.setRecentFile(aFile);
      if (aCallback) {
        aCallback.call(this, aStatus, content);
      }
    });
  },

  /**
   * Open a file to edit in the Scratchpad.
   *
   * @param integer aIndex
   *        Optional integer: clicked menuitem in the 'Open Recent'-menu.
   */
  openFile: function SP_openFile(aIndex)
  {
    let promptCallback = aFile => {
      this.promptSave((aCloseFile, aSaved, aStatus) => {
        let shouldOpen = aCloseFile;
        if (aSaved && !Components.isSuccessCode(aStatus)) {
          shouldOpen = false;
        }

        if (shouldOpen) {
          let file;
          if (aFile) {
            file = aFile;
          } else {
            file = Cc["@mozilla.org/file/local;1"].
                   createInstance(Ci.nsIFile);
            let filePath = this.getRecentFiles()[aIndex];
            file.initWithPath(filePath);
          }

          if (!file.exists()) {
            this.notificationBox.appendNotification(
              this.strings.GetStringFromName("fileNoLongerExists.notification"),
              "file-no-longer-exists",
              null,
              this.notificationBox.PRIORITY_WARNING_HIGH,
              null);

            this.clearFiles(aIndex, 1);
            return;
          }

          this.importFromFile(file, false);
        }
      });
    };

    if (aIndex > -1) {
      promptCallback();
    } else {
      let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
      fp.init(window, this.strings.GetStringFromName("openFile.title"),
              Ci.nsIFilePicker.modeOpen);
      fp.defaultString = "";
      fp.appendFilter("JavaScript Files", "*.js; *.jsm; *.json");
      fp.appendFilter("All Files", "*.*");
      fp.open(aResult => {
        if (aResult != Ci.nsIFilePicker.returnCancel) {
          promptCallback(fp.file);
        }
      });
    }
  },

  /**
   * Get recent files.
   *
   * @return Array
   *         File paths.
   */
  getRecentFiles: function SP_getRecentFiles()
  {
    let branch = Services.prefs.getBranch("devtools.scratchpad.");
    let filePaths = [];

    // WARNING: Do not use getCharPref here, it doesn't play nicely with
    // Unicode strings.

    if (branch.prefHasUserValue("recentFilePaths")) {
      let data = branch.getStringPref("recentFilePaths");
      filePaths = JSON.parse(data);
    }

    return filePaths;
  },

  /**
   * Save a recent file in a JSON parsable string.
   *
   * @param nsIFile aFile
   *        The nsIFile we want to save as a recent file.
   */
  setRecentFile: function SP_setRecentFile(aFile)
  {
    let maxRecent = Services.prefs.getIntPref(PREF_RECENT_FILES_MAX);
    if (maxRecent < 1) {
      return;
    }

    let filePaths = this.getRecentFiles();
    let filesCount = filePaths.length;
    let pathIndex = filePaths.indexOf(aFile.path);

    // We are already storing this file in the list of recent files.
    if (pathIndex > -1) {
      // If it's already the most recent file, we don't have to do anything.
      if (pathIndex === (filesCount - 1)) {
        // Updating the menu to clear the disabled state from the wrong menuitem
        // in rare cases when two or more Scratchpad windows are open and the
        // same file has been opened in two or more windows.
        this.populateRecentFilesMenu();
        return;
      }

      // It is not the most recent file. Remove it from the list, we add it as
      // the most recent farther down.
      filePaths.splice(pathIndex, 1);
    }
    // If we are not storing the file and the 'recent files'-list is full,
    // remove the oldest file from the list.
    else if (filesCount === maxRecent) {
      filePaths.shift();
    }

    filePaths.push(aFile.path);

    Services.prefs.getBranch("devtools.scratchpad.")
            .setStringPref("recentFilePaths", JSON.stringify(filePaths));
  },

  /**
   * Populates the 'Open Recent'-menu.
   */
  populateRecentFilesMenu: function SP_populateRecentFilesMenu()
  {
    let maxRecent = Services.prefs.getIntPref(PREF_RECENT_FILES_MAX);
    let recentFilesMenu = document.getElementById("sp-open_recent-menu");

    if (maxRecent < 1) {
      recentFilesMenu.setAttribute("hidden", true);
      return;
    }

    let recentFilesPopup = recentFilesMenu.firstChild;
    let filePaths = this.getRecentFiles();
    let filename = this.getState().filename;

    recentFilesMenu.setAttribute("disabled", true);
    while (recentFilesPopup.hasChildNodes()) {
      recentFilesPopup.firstChild.remove();
    }

    if (filePaths.length > 0) {
      recentFilesMenu.removeAttribute("disabled");

      // Print out menuitems with the most recent file first.
      for (let i = filePaths.length - 1; i >= 0; --i) {
        let menuitem = document.createElement("menuitem");
        menuitem.setAttribute("type", "radio");
        menuitem.setAttribute("label", filePaths[i]);

        if (filePaths[i] === filename) {
          menuitem.setAttribute("checked", true);
          menuitem.setAttribute("disabled", true);
        }

        menuitem.addEventListener("command", Scratchpad.openFile.bind(Scratchpad, i));
        recentFilesPopup.appendChild(menuitem);
      }

      recentFilesPopup.appendChild(document.createElement("menuseparator"));
      let clearItems = document.createElement("menuitem");
      clearItems.setAttribute("id", "sp-menu-clear_recent");
      clearItems.setAttribute("label",
                              this.strings.
                              GetStringFromName("clearRecentMenuItems.label"));
      clearItems.setAttribute("command", "sp-cmd-clearRecentFiles");
      recentFilesPopup.appendChild(clearItems);
    }
  },

  /**
   * Clear a range of files from the list.
   *
   * @param integer aIndex
   *        Index of file in menu to remove.
   * @param integer aLength
   *        Number of files from the index 'aIndex' to remove.
   */
  clearFiles: function SP_clearFile(aIndex, aLength)
  {
    let filePaths = this.getRecentFiles();
    filePaths.splice(aIndex, aLength);

    Services.prefs.getBranch("devtools.scratchpad.")
            .setStringPref("recentFilePaths", JSON.stringify(filePaths));
  },

  /**
   * Clear all recent files.
   */
  clearRecentFiles: function SP_clearRecentFiles()
  {
    Services.prefs.clearUserPref("devtools.scratchpad.recentFilePaths");
  },

  /**
   * Handle changes to the 'PREF_RECENT_FILES_MAX'-preference.
   */
  handleRecentFileMaxChange: function SP_handleRecentFileMaxChange()
  {
    let maxRecent = Services.prefs.getIntPref(PREF_RECENT_FILES_MAX);
    let menu = document.getElementById("sp-open_recent-menu");

    // Hide the menu if the 'PREF_RECENT_FILES_MAX'-pref is set to zero or less.
    if (maxRecent < 1) {
      menu.setAttribute("hidden", true);
    } else {
      if (menu.hasAttribute("hidden")) {
        if (!menu.firstChild.hasChildNodes()) {
          this.populateRecentFilesMenu();
        }

        menu.removeAttribute("hidden");
      }

      let filePaths = this.getRecentFiles();
      if (maxRecent < filePaths.length) {
        let diff = filePaths.length - maxRecent;
        this.clearFiles(0, diff);
      }
    }
  },
  /**
   * Save the textbox content to the currently open file.
   *
   * @param function aCallback
   *        Optional function you want to call when file is saved
   */
  saveFile: function SP_saveFile(aCallback)
  {
    if (!this.filename) {
      return this.saveFileAs(aCallback);
    }

    let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
    file.initWithPath(this.filename);

    this.exportToFile(file, true, false, aStatus => {
      if (Components.isSuccessCode(aStatus)) {
        this.dirty = false;
        document.getElementById("sp-cmd-revert").setAttribute("disabled", true);
        this.setRecentFile(file);
      }
      if (aCallback) {
        aCallback(aStatus);
      }
    });
  },

  /**
   * Save the textbox content to a new file.
   *
   * @param function aCallback
   *        Optional function you want to call when file is saved
   */
  saveFileAs: function SP_saveFileAs(aCallback)
  {
    let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
    let fpCallback = aResult => {
      if (aResult != Ci.nsIFilePicker.returnCancel) {
        this.setFilename(fp.file.path);
        this.exportToFile(fp.file, true, false, aStatus => {
          if (Components.isSuccessCode(aStatus)) {
            this.dirty = false;
            this.setRecentFile(fp.file);
          }
          if (aCallback) {
            aCallback(aStatus);
          }
        });
      }
    };

    fp.init(window, this.strings.GetStringFromName("saveFileAs"),
            Ci.nsIFilePicker.modeSave);
    fp.defaultString = "scratchpad.js";
    fp.appendFilter("JavaScript Files", "*.js; *.jsm; *.json");
    fp.appendFilter("All Files", "*.*");
    fp.open(fpCallback);
  },

  /**
   * Restore content from saved version of current file.
   *
   * @param function aCallback
   *        Optional function you want to call when file is saved
   */
  revertFile: function SP_revertFile(aCallback)
  {
    let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
    file.initWithPath(this.filename);

    if (!file.exists()) {
      return;
    }

    this.importFromFile(file, false, (aStatus, aContent) => {
      if (aCallback) {
        aCallback(aStatus);
      }
    });
  },

  /**
   * Prompt to revert scratchpad if it has unsaved changes.
   *
   * @param function aCallback
   *        Optional function you want to call when file is saved. The callback
   *        receives three arguments:
   *          - aRevert (boolean) - tells if the file has been reverted.
   *          - status (number) - the file revert status result (if the file was
   *          saved).
   */
  promptRevert: function SP_promptRervert(aCallback)
  {
    if (this.filename) {
      let ps = Services.prompt;
      let flags = ps.BUTTON_POS_0 * ps.BUTTON_TITLE_REVERT +
                  ps.BUTTON_POS_1 * ps.BUTTON_TITLE_CANCEL;

      let button = ps.confirmEx(window,
                          this.strings.GetStringFromName("confirmRevert.title"),
                          this.strings.GetStringFromName("confirmRevert"),
                          flags, null, null, null, null, {});
      if (button == BUTTON_POSITION_CANCEL) {
        if (aCallback) {
          aCallback(false);
        }

        return;
      }
      if (button == BUTTON_POSITION_REVERT) {
        this.revertFile(aStatus => {
          if (aCallback) {
            aCallback(true, aStatus);
          }
        });

        return;
      }
    }
    if (aCallback) {
      aCallback(false);
    }
  },

  /**
   * Open the Error Console.
   */
  openErrorConsole: function SP_openErrorConsole()
  {
    HUDService.toggleBrowserConsole();
  },

  /**
   * Open the Web Console.
   */
  openWebConsole: function SP_openWebConsole()
  {
    let target = TargetFactory.forTab(this.gBrowser.selectedTab);
    gDevTools.showToolbox(target, "webconsole");
    this.browserWindow.focus();
  },

  /**
   * Set the current execution context to be the active tab content window.
   */
  setContentContext: function SP_setContentContext()
  {
    if (this.executionContext == SCRATCHPAD_CONTEXT_CONTENT) {
      return;
    }

    let content = document.getElementById("sp-menu-content");
    document.getElementById("sp-menu-browser").removeAttribute("checked");
    document.getElementById("sp-cmd-reloadAndRun").removeAttribute("disabled");
    content.setAttribute("checked", true);
    this.executionContext = SCRATCHPAD_CONTEXT_CONTENT;
    this.notificationBox.removeAllNotifications(false);
  },

  /**
   * Set the current execution context to be the most recent chrome window.
   */
  setBrowserContext: function SP_setBrowserContext()
  {
    if (this.executionContext == SCRATCHPAD_CONTEXT_BROWSER) {
      return;
    }

    let browser = document.getElementById("sp-menu-browser");
    let reloadAndRun = document.getElementById("sp-cmd-reloadAndRun");

    document.getElementById("sp-menu-content").removeAttribute("checked");
    reloadAndRun.setAttribute("disabled", true);
    browser.setAttribute("checked", true);

    this.executionContext = SCRATCHPAD_CONTEXT_BROWSER;
    this.notificationBox.appendNotification(
      this.strings.GetStringFromName("browserContext.notification"),
      SCRATCHPAD_CONTEXT_BROWSER,
      null,
      this.notificationBox.PRIORITY_WARNING_HIGH,
      null);
  },

  /**
   * Gets the ID of the inner window of the given DOM window object.
   *
   * @param nsIDOMWindow aWindow
   * @return integer
   *         the inner window ID
   */
  getInnerWindowId: function SP_getInnerWindowId(aWindow)
  {
    return aWindow.QueryInterface(Ci.nsIInterfaceRequestor).
           getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
  },

  updateStatusBar: function SP_updateStatusBar(aEventType)
  {
    var statusBarField = document.getElementById("statusbar-line-col");
    let { line, ch } = this.editor.getCursor();
    statusBarField.textContent = this.strings.formatStringFromName(
      "scratchpad.statusBarLineCol", [ line + 1, ch + 1], 2);
  },

  /**
   * The Scratchpad window load event handler. This method
   * initializes the Scratchpad window and source editor.
   *
   * @param nsIDOMEvent aEvent
   */
  onLoad: function SP_onLoad(aEvent)
  {
    if (aEvent.target != document) {
      return;
    }

    let chrome = Services.prefs.getBoolPref(DEVTOOLS_CHROME_ENABLED);
    if (chrome) {
      let environmentMenu = document.getElementById("sp-environment-menu");
      let errorConsoleCommand = document.getElementById("sp-cmd-errorConsole");
      let chromeContextCommand = document.getElementById("sp-cmd-browserContext");
      environmentMenu.removeAttribute("hidden");
      chromeContextCommand.removeAttribute("disabled");
      errorConsoleCommand.removeAttribute("disabled");
    }

    let initialText = this.strings.formatStringFromName(
      "scratchpadIntro1",
      [ShortcutUtils.prettifyShortcut(document.getElementById("sp-key-run"), true),
       ShortcutUtils.prettifyShortcut(document.getElementById("sp-key-inspect"), true),
       ShortcutUtils.prettifyShortcut(document.getElementById("sp-key-display"), true)],
      3);

    let args = window.arguments;
    let state = null;

    if (args && args[0] instanceof Ci.nsIDialogParamBlock) {
      args = args[0];
      this._instanceId = args.GetString(0);

      state = args.GetString(1) || null;
      if (state) {
        state = JSON.parse(state);
        this.setState(state);
        if ("text" in state) {
          initialText = state.text;
        }
      }
    } else {
      this._instanceId = ScratchpadManager.createUid();
    }

    let config = {
      mode: Editor.modes.js,
      value: initialText,
      lineNumbers: Services.prefs.getBoolPref(SHOW_LINE_NUMBERS),
      contextMenu: "scratchpad-text-popup",
      showTrailingSpace: Services.prefs.getBoolPref(SHOW_TRAILING_SPACE),
      autocomplete: Services.prefs.getBoolPref(ENABLE_AUTOCOMPLETION),
      lineWrapping: Services.prefs.getBoolPref(WRAP_TEXT),
    };

    this.editor = new Editor(config);
    let editorElement = document.querySelector("#scratchpad-editor");
    this.editor.appendTo(editorElement).then(() => {
      var lines = initialText.split("\n");

      this.editor.setFontSize(Services.prefs.getIntPref(EDITOR_FONT_SIZE));

      this.editor.on("change", this._onChanged);
      // Keep a reference to the bound version for use in onUnload.
      this.updateStatusBar = Scratchpad.updateStatusBar.bind(this);
      this.editor.on("cursorActivity", this.updateStatusBar);
      let okstring = this.strings.GetStringFromName("selfxss.okstring");
      let msg = this.strings.formatStringFromName("selfxss.msg", [okstring], 1);
      this._onPaste = WebConsoleUtils.pasteHandlerGen(this.editor.container.contentDocument.body,
                                                      document.querySelector("#scratchpad-notificationbox"),
                                                      msg, okstring);
      editorElement.addEventListener("paste", this._onPaste, true);
      editorElement.addEventListener("drop", this._onPaste);
      this.editor.on("saveRequested", () => this.saveFile());
      this.editor.focus();
      this.editor.setCursor({ line: lines.length, ch: lines.pop().length });

      // Add the commands controller for the source-editor.
      this.editor.insertCommandsController();

      if (state)
        this.dirty = !state.saved;

      this.initialized = true;
      this._triggerObservers("Ready");
      this.populateRecentFilesMenu();
      PreferenceObserver.init();
      CloseObserver.init();
    }).catch(console.error);
    this._setupCommandListeners();
    this._updateViewMenuItems();
    this._setupPopupShowingListeners();

    // Change the accesskey for the help menu as it can be specific on Windows
    // some localizations of Windows (ex:french, german) use "?"
    //  for the help button in the menubar but Gnome does not.
    if (Services.appinfo.OS == "WINNT") {
      let helpMenu = document.getElementById("sp-help-menu");
      helpMenu.setAttribute("accesskey", helpMenu.getAttribute("accesskeywindows"));
    }
  },

  /**
   * The Source Editor "change" event handler. This function updates the
   * Scratchpad window title to show an asterisk when there are unsaved changes.
   *
   * @private
   */
  _onChanged: function SP__onChanged()
  {
    Scratchpad._updateTitle();

    if (Scratchpad.filename) {
      if (Scratchpad.dirty)
        document.getElementById("sp-cmd-revert").removeAttribute("disabled");
      else
        document.getElementById("sp-cmd-revert").setAttribute("disabled", true);
    }
  },

  /**
   * Undo the last action of the user.
   */
  undo: function SP_undo()
  {
    this.editor.undo();
  },

  /**
   * Redo the previously undone action.
   */
  redo: function SP_redo()
  {
    this.editor.redo();
  },

  /**
   * The Scratchpad window unload event handler. This method unloads/destroys
   * the source editor.
   *
   * @param nsIDOMEvent aEvent
   */
  onUnload: function SP_onUnload(aEvent)
  {
    if (aEvent.target != document) {
      return;
    }

    // This event is created only after user uses 'reload and run' feature.
    if (this._reloadAndRunEvent && this.gBrowser) {
      this.gBrowser.selectedBrowser.removeEventListener("load",
          this._reloadAndRunEvent, true);
    }

    PreferenceObserver.uninit();
    CloseObserver.uninit();
    if (this._onPaste) {
      let editorElement = document.querySelector("#scratchpad-editor");
      editorElement.removeEventListener("paste", this._onPaste, true);
      editorElement.removeEventListener("drop", this._onPaste);
      this._onPaste = null;
    }
    this.editor.off("change", this._onChanged);
    this.editor.off("cursorActivity", this.updateStatusBar);
    this.editor.destroy();
    this.editor = null;

    if (this._sidebar) {
      this._sidebar.destroy();
      this._sidebar = null;
    }

    if (this._prettyPrintWorker) {
      this._prettyPrintWorker.destroy();
      this._prettyPrintWorker = null;
    }

    scratchpadTargets = null;
    this.webConsoleClient = null;
    this.debuggerClient = null;
    this.initialized = false;
  },

  /**
   * Prompt to save scratchpad if it has unsaved changes.
   *
   * @param function aCallback
   *        Optional function you want to call when file is saved. The callback
   *        receives three arguments:
   *          - toClose (boolean) - tells if the window should be closed.
   *          - saved (boolen) - tells if the file has been saved.
   *          - status (number) - the file save status result (if the file was
   *          saved).
   * @return boolean
   *         Whether the window should be closed
   */
  promptSave: function SP_promptSave(aCallback)
  {
    if (this.dirty) {
      let ps = Services.prompt;
      let flags = ps.BUTTON_POS_0 * ps.BUTTON_TITLE_SAVE +
                  ps.BUTTON_POS_1 * ps.BUTTON_TITLE_CANCEL +
                  ps.BUTTON_POS_2 * ps.BUTTON_TITLE_DONT_SAVE;

      let button = ps.confirmEx(window,
                          this.strings.GetStringFromName("confirmClose.title"),
                          this.strings.GetStringFromName("confirmClose"),
                          flags, null, null, null, null, {});

      if (button == BUTTON_POSITION_CANCEL) {
        if (aCallback) {
          aCallback(false, false);
        }
        return false;
      }

      if (button == BUTTON_POSITION_SAVE) {
        this.saveFile(aStatus => {
          if (aCallback) {
            aCallback(true, true, aStatus);
          }
        });
        return true;
      }
    }

    if (aCallback) {
      aCallback(true, false);
    }
    return true;
  },

  /**
   * Handler for window close event. Prompts to save scratchpad if
   * there are unsaved changes.
   *
   * @param nsIDOMEvent aEvent
   * @param function aCallback
   *        Optional function you want to call when file is saved/closed.
   *        Used mainly for tests.
   */
  onClose: function SP_onClose(aEvent, aCallback)
  {
    aEvent.preventDefault();
    this.close(aCallback);
  },

  /**
   * Close the scratchpad window. Prompts before closing if the scratchpad
   * has unsaved changes.
   *
   * @param function aCallback
   *        Optional function you want to call when file is saved
   */
  close: function SP_close(aCallback)
  {
    let shouldClose;

    this.promptSave((aShouldClose, aSaved, aStatus) => {
      shouldClose = aShouldClose;
      if (aSaved && !Components.isSuccessCode(aStatus)) {
        shouldClose = false;
      }

      if (shouldClose) {
        window.close();
      }

      if (aCallback) {
        aCallback(shouldClose);
      }
    });

    return shouldClose;
  },

  /**
   * Toggle a editor's boolean option.
   */
  toggleEditorOption: function SP_toggleEditorOption(optionName, optionPreference)
  {
    let newOptionValue = !this.editor.getOption(optionName);
    this.editor.setOption(optionName, newOptionValue);
    Services.prefs.setBoolPref(optionPreference, newOptionValue);
  },

  /**
   * Increase the editor's font size by 1 px.
   */
  increaseFontSize: function SP_increaseFontSize()
  {
    let size = this.editor.getFontSize();

    if (size < MAXIMUM_FONT_SIZE) {
      let newFontSize = size + 1;
      this.editor.setFontSize(newFontSize);
      Services.prefs.setIntPref(EDITOR_FONT_SIZE, newFontSize);

      if (newFontSize === MAXIMUM_FONT_SIZE) {
        document.getElementById("sp-cmd-larger-font").setAttribute("disabled", true);
      }

      document.getElementById("sp-cmd-smaller-font").removeAttribute("disabled");
    }
  },

  /**
   * Decrease the editor's font size by 1 px.
   */
  decreaseFontSize: function SP_decreaseFontSize()
  {
    let size = this.editor.getFontSize();

    if (size > MINIMUM_FONT_SIZE) {
      let newFontSize = size - 1;
      this.editor.setFontSize(newFontSize);
      Services.prefs.setIntPref(EDITOR_FONT_SIZE, newFontSize);

      if (newFontSize === MINIMUM_FONT_SIZE) {
        document.getElementById("sp-cmd-smaller-font").setAttribute("disabled", true);
      }
    }

    document.getElementById("sp-cmd-larger-font").removeAttribute("disabled");
  },

  /**
   * Restore the editor's original font size.
   */
  normalFontSize: function SP_normalFontSize()
  {
    this.editor.setFontSize(NORMAL_FONT_SIZE);
    Services.prefs.setIntPref(EDITOR_FONT_SIZE, NORMAL_FONT_SIZE);

    document.getElementById("sp-cmd-larger-font").removeAttribute("disabled");
    document.getElementById("sp-cmd-smaller-font").removeAttribute("disabled");
  },

  _observers: [],

  /**
   * Add an observer for Scratchpad events.
   *
   * The observer implements IScratchpadObserver := {
   *   onReady:      Called when the Scratchpad and its Editor are ready.
   *                 Arguments: (Scratchpad aScratchpad)
   * }
   *
   * All observer handlers are optional.
   *
   * @param IScratchpadObserver aObserver
   * @see removeObserver
   */
  addObserver: function SP_addObserver(aObserver)
  {
    this._observers.push(aObserver);
  },

  /**
   * Remove an observer for Scratchpad events.
   *
   * @param IScratchpadObserver aObserver
   * @see addObserver
   */
  removeObserver: function SP_removeObserver(aObserver)
  {
    let index = this._observers.indexOf(aObserver);
    if (index != -1) {
      this._observers.splice(index, 1);
    }
  },

  /**
   * Trigger named handlers in Scratchpad observers.
   *
   * @param string aName
   *        Name of the handler to trigger.
   * @param Array aArgs
   *        Optional array of arguments to pass to the observer(s).
   * @see addObserver
   */
  _triggerObservers: function SP_triggerObservers(aName, aArgs)
  {
    // insert this Scratchpad instance as the first argument
    if (!aArgs) {
      aArgs = [this];
    } else {
      aArgs.unshift(this);
    }

    // trigger all observers that implement this named handler
    for (let i = 0; i < this._observers.length; ++i) {
      let observer = this._observers[i];
      let handler = observer["on" + aName];
      if (handler) {
        handler.apply(observer, aArgs);
      }
    }
  },

  /**
   * Opens the MDN documentation page for Scratchpad.
   */
  openDocumentationPage: function SP_openDocumentationPage()
  {
    let url = this.strings.GetStringFromName("help.openDocumentationPage");
    this.browserWindow.openUILinkIn(url,"tab");
    this.browserWindow.focus();
  },
};


/**
 * Represents the DebuggerClient connection to a specific tab as used by the
 * Scratchpad.
 *
 * @param object aTab
 *              The tab to connect to.
 */
function ScratchpadTab(aTab)
{
  this._tab = aTab;
}

var scratchpadTargets = new WeakMap();

/**
 * Returns the object containing the DebuggerClient and WebConsoleClient for a
 * given tab or window.
 *
 * @param object aSubject
 *        The tab or window to obtain the connection for.
 * @return Promise
 *         The promise for the connection information.
 */
ScratchpadTab.consoleFor = function consoleFor(aSubject)
{
  if (!scratchpadTargets.has(aSubject)) {
    scratchpadTargets.set(aSubject, new this(aSubject));
  }
  return scratchpadTargets.get(aSubject).connect(aSubject);
};


ScratchpadTab.prototype = {
  /**
   * The promise for the connection.
   */
  _connector: null,

  /**
   * Initialize a debugger client and connect it to the debugger server.
   *
   * @param object aSubject
   *        The tab or window to obtain the connection for.
   * @return Promise
   *         The promise for the result of connecting to this tab or window.
   */
  connect: function ST_connect(aSubject)
  {
    if (this._connector) {
      return this._connector;
    }

    let deferred = defer();
    this._connector = deferred.promise;

    let connectTimer = setTimeout(() => {
      deferred.reject({
        error: "timeout",
        message: Scratchpad.strings.GetStringFromName("connectionTimeout"),
      });
    }, REMOTE_TIMEOUT);

    deferred.promise.then(() => clearTimeout(connectTimer));

    this._attach(aSubject).then(aTarget => {
      let consoleActor = aTarget.form.consoleActor;
      let client = aTarget.client;
      client.attachConsole(consoleActor, [], (aResponse, aWebConsoleClient) => {
        if (aResponse.error) {
          reportError("attachConsole", aResponse);
          deferred.reject(aResponse);
        }
        else {
          deferred.resolve({
            webConsoleClient: aWebConsoleClient,
            debuggerClient: client
          });
        }
      });
    });

    return deferred.promise;
  },

  /**
   * Attach to this tab.
   *
   * @param object aSubject
   *        The tab or window to obtain the connection for.
   * @return Promise
   *         The promise for the TabTarget for this tab.
   */
  _attach: function ST__attach(aSubject)
  {
    let target = TargetFactory.forTab(this._tab);
    target.once("close", () => {
      if (scratchpadTargets) {
        scratchpadTargets.delete(aSubject);
      }
    });
    return target.makeRemote().then(() => target);
  },
};


/**
 * Represents the DebuggerClient connection to a specific window as used by the
 * Scratchpad.
 */
function ScratchpadWindow() {}

ScratchpadWindow.consoleFor = ScratchpadTab.consoleFor;

ScratchpadWindow.prototype = extend(ScratchpadTab.prototype, {
  /**
   * Attach to this window.
   *
   * @return Promise
   *         The promise for the target for this window.
   */
  _attach: function SW__attach()
  {
    DebuggerServer.init();
    DebuggerServer.registerAllActors();
    DebuggerServer.allowChromeProcess = true;

    let client = new DebuggerClient(DebuggerServer.connectPipe());
    return client.connect()
      .then(() => client.getProcess())
      .then(aResponse => {
        return { form: aResponse.form, client: client };
      });
  }
});


function ScratchpadTarget(aTarget)
{
  this._target = aTarget;
}

ScratchpadTarget.consoleFor = ScratchpadTab.consoleFor;

ScratchpadTarget.prototype = extend(ScratchpadTab.prototype, {
  _attach: function ST__attach()
  {
    if (this._target.isRemote) {
      return promise.resolve(this._target);
    }
    return this._target.makeRemote().then(() => this._target);
  }
});


/**
 * Encapsulates management of the sidebar containing the VariablesView for
 * object inspection.
 */
function ScratchpadSidebar(aScratchpad)
{
  // Make sure to decorate this object. ToolSidebar requires the parent
  // panel to support event (emit) API.
  EventEmitter.decorate(this);

  let ToolSidebar = require("devtools/client/framework/sidebar").ToolSidebar;
  let tabbox = document.querySelector("#scratchpad-sidebar");
  this._sidebar = new ToolSidebar(tabbox, this, "scratchpad");
  this._scratchpad = aScratchpad;
}

ScratchpadSidebar.prototype = {
  /*
   * The ToolSidebar for this sidebar.
   */
  _sidebar: null,

  /*
   * The VariablesView for this sidebar.
   */
  variablesView: null,

  /*
   * Whether the sidebar is currently shown.
   */
  visible: false,

  /**
   * Open the sidebar, if not open already, and populate it with the properties
   * of the given object.
   *
   * @param string aString
   *        The string that was evaluated.
   * @param object aObject
   *        The object to inspect, which is the aEvalString evaluation result.
   * @return Promise
   *         A promise that will resolve once the sidebar is open.
   */
  open: function SS_open(aEvalString, aObject)
  {
    this.show();

    let deferred = defer();

    let onTabReady = () => {
      if (this.variablesView) {
        this.variablesView.controller.releaseActors();
      }
      else {
        let window = this._sidebar.getWindowForTab("variablesview");
        let container = window.document.querySelector("#variables");

        this.variablesView = new VariablesView(container, {
          searchEnabled: true,
          searchPlaceholder: this._scratchpad.strings
                             .GetStringFromName("propertiesFilterPlaceholder")
        });

        VariablesViewController.attach(this.variablesView, {
          getEnvironmentClient: aGrip => {
            return new EnvironmentClient(this._scratchpad.debuggerClient, aGrip);
          },
          getObjectClient: aGrip => {
            return new ObjectClient(this._scratchpad.debuggerClient, aGrip);
          },
          getLongStringClient: aActor => {
            return this._scratchpad.webConsoleClient.longString(aActor);
          },
          releaseActor: aActor => {
            this._scratchpad.debuggerClient.release(aActor);
          }
        });
      }
      this._update(aObject).then(() => deferred.resolve());
    };

    if (this._sidebar.getCurrentTabID() == "variablesview") {
      onTabReady();
    }
    else {
      this._sidebar.once("variablesview-ready", onTabReady);
      this._sidebar.addTab("variablesview", VARIABLES_VIEW_URL, {selected: true});
    }

    return deferred.promise;
  },

  /**
   * Show the sidebar.
   */
  show: function SS_show()
  {
    if (!this.visible) {
      this.visible = true;
      this._sidebar.show();
    }
  },

  /**
   * Hide the sidebar.
   */
  hide: function SS_hide()
  {
    if (this.visible) {
      this.visible = false;
      this._sidebar.hide();
    }
  },

  /**
   * Destroy the sidebar.
   *
   * @return Promise
   *         The promise that resolves when the sidebar is destroyed.
   */
  destroy: function SS_destroy()
  {
    if (this.variablesView) {
      this.variablesView.controller.releaseActors();
      this.variablesView = null;
    }
    return this._sidebar.destroy();
  },

  /**
   * Update the object currently inspected by the sidebar.
   *
   * @param any aValue
   *        The JS value to inspect in the sidebar.
   * @return Promise
   *         A promise that resolves when the update completes.
   */
  _update: function SS__update(aValue)
  {
    let options, onlyEnumVisible;
    if (VariablesView.isPrimitive({ value: aValue })) {
      options = { rawObject: { value: aValue } };
      onlyEnumVisible = true;
    } else {
      options = { objectActor: aValue };
      onlyEnumVisible = false;
    }
    let view = this.variablesView;
    view.onlyEnumVisible = onlyEnumVisible;
    view.empty();
    return view.controller.setSingleVariable(options).expanded;
  }
};


/**
 * Report an error coming over the remote debugger protocol.
 *
 * @param string aAction
 *        The name of the action or method that failed.
 * @param object aResponse
 *        The response packet that contains the error.
 */
function reportError(aAction, aResponse)
{
  console.error(aAction + " failed: " + aResponse.error + " " +
                aResponse.message);
}


/**
 * The PreferenceObserver listens for preference changes while Scratchpad is
 * running.
 */
var PreferenceObserver = {
  _initialized: false,

  init: function PO_init()
  {
    if (this._initialized) {
      return;
    }

    this.branch = Services.prefs.getBranch("devtools.scratchpad.");
    this.branch.addObserver("", this);
    this._initialized = true;
  },

  observe: function PO_observe(aMessage, aTopic, aData)
  {
    if (aTopic != "nsPref:changed") {
      return;
    }

    if (aData == "recentFilesMax") {
      Scratchpad.handleRecentFileMaxChange();
    }
    else if (aData == "recentFilePaths") {
      Scratchpad.populateRecentFilesMenu();
    }
  },

  uninit: function PO_uninit() {
    if (!this.branch) {
      return;
    }

    this.branch.removeObserver("", this);
    this.branch = null;
  }
};


/**
 * The CloseObserver listens for the last browser window closing and attempts to
 * close the Scratchpad.
 */
var CloseObserver = {
  init: function CO_init()
  {
    Services.obs.addObserver(this, "browser-lastwindow-close-requested");
  },

  observe: function CO_observe(aSubject)
  {
    if (Scratchpad.close()) {
      this.uninit();
    }
    else {
      aSubject.QueryInterface(Ci.nsISupportsPRBool);
      aSubject.data = true;
    }
  },

  uninit: function CO_uninit()
  {
    // Will throw exception if removeObserver is called twice.
    if (this._uninited) {
      return;
    }

    this._uninited = true;
    Services.obs.removeObserver(this, "browser-lastwindow-close-requested");
  },
};

XPCOMUtils.defineLazyGetter(Scratchpad, "strings", function () {
  return Services.strings.createBundle(SCRATCHPAD_L10N);
});

addEventListener("load", Scratchpad.onLoad.bind(Scratchpad), false);
addEventListener("unload", Scratchpad.onUnload.bind(Scratchpad), false);
addEventListener("close", Scratchpad.onClose.bind(Scratchpad), false);
PK
!<(x.�1H1H1chrome/devtools/content/scratchpad/scratchpad.xul<?xml version="1.0"?>

<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 window [
<!ENTITY % scratchpadDTD SYSTEM "chrome://devtools/locale/scratchpad.dtd" >
 %scratchpadDTD;
<!ENTITY % editMenuStrings SYSTEM "chrome://global/locale/editMenuOverlay.dtd">
%editMenuStrings;
<!ENTITY % sourceEditorStrings SYSTEM "chrome://devtools/locale/sourceeditor.dtd">
%sourceEditorStrings;
]>

<?xml-stylesheet href="chrome://global/skin/global.css"?>
<?xml-stylesheet href="chrome://devtools/skin/scratchpad.css"?>

<window id="main-window"
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        title="&window.title;"
        windowtype="devtools:scratchpad"
        macanimationtype="document"
        fullscreenbutton="true"
        screenX="4" screenY="4"
        width="640" height="480"
        persist="screenX screenY width height sizemode">

<script type="application/javascript"
        src="chrome://devtools/content/shared/theme-switching.js"/>
<script type="application/javascript" src="chrome://global/content/globalOverlay.js"/>
<script type="application/javascript" src="chrome://devtools/content/scratchpad/scratchpad.js"/>

<script type="application/javascript" src="chrome://global/content/editMenuOverlay.js"/>
<commandset id="editMenuCommands">
  <commandset id="editMenuCommandSetAll" commandupdater="true" events="focus,select"
              oncommandupdate="goUpdateGlobalEditMenuItems()"/>
  <commandset id="editMenuCommandSetUndo" commandupdater="true" events="undo"
              oncommandupdate="goUpdateUndoEditMenuItems()"/>
  <commandset id="editMenuCommandSetPaste" commandupdater="true" events="clipboard"
              oncommandupdate="goUpdatePasteMenuItems()"/>
  <command id="cmd_undo" oncommand="goDoCommand('cmd_undo')"/>
  <command id="cmd_redo" oncommand="goDoCommand('cmd_redo')"/>
  <command id="cmd_cut" oncommand="goDoCommand('cmd_cut')"/>
  <command id="cmd_copy" oncommand="goDoCommand('cmd_copy')"/>
  <command id="cmd_paste" oncommand="goDoCommand('cmd_paste')"/>
  <command id="cmd_delete" oncommand="goDoCommand('cmd_delete')"/>
  <command id="cmd_selectAll" oncommand="goDoCommand('cmd_selectAll')"/>
  <command id="cmd_switchTextDirection" oncommand="goDoCommand('cmd_switchTextDirection');"/>
</commandset>

<commandset id="sourceEditorCommands">
  <command id="cmd_find" oncommand=";"/>
  <command id="cmd_findAgain" oncommand=";"/>
  <command id="cmd_gotoLine" oncommand=";"/>
</commandset>

<commandset id="sp-commandset">
  <command id="sp-cmd-newWindow" oncommand=";"/>
  <command id="sp-cmd-openFile" oncommand=";"/>
  <command id="sp-cmd-clearRecentFiles" oncommand=";"/>
  <command id="sp-cmd-save" oncommand=";"/>
  <command id="sp-cmd-saveas" oncommand=";"/>
  <command id="sp-cmd-revert" oncommand=";" disabled="true"/>
  <command id="sp-cmd-close" oncommand=";"/>
  <command id="sp-cmd-line-numbers" oncommand=";"/>
  <command id="sp-cmd-wrap-text" oncommand=";"/>
  <command id="sp-cmd-highlight-trailing-space" oncommand=";"/>
  <command id="sp-cmd-larger-font" oncommand=";"/>
  <command id="sp-cmd-smaller-font" oncommand=";"/>
  <command id="sp-cmd-normal-font" oncommand=";"/>
  <command id="sp-cmd-run" oncommand=";"/>
  <command id="sp-cmd-inspect" oncommand=";"/>
  <command id="sp-cmd-display" oncommand=";"/>
  <command id="sp-cmd-pprint" oncommand=";"/>
  <command id="sp-cmd-contentContext" oncommand=";"/>
  <command id="sp-cmd-browserContext" oncommand=";" disabled="true"/>
  <command id="sp-cmd-reloadAndRun" oncommand=";"/>
  <command id="sp-cmd-evalFunction" oncommand=";"/>
  <command id="sp-cmd-errorConsole" oncommand=";" disabled="true"/>
  <command id="sp-cmd-webConsole" oncommand=";"/>
  <command id="sp-cmd-documentationLink" oncommand=";"/>
  <command id="sp-cmd-hideSidebar" oncommand=";"/>
</commandset>

  <!-- These key nodes are here only for show. The real bindings come from
       XBL, in platformHTMLBindings.xml. See bugs 57078 and 71779. -->

  <keyset id="editMenuKeys">
    <key id="key_undo" key="&undoCmd.key;" modifiers="accel" command="cmd_undo"/>
    <key id="key_redo" key="&undoCmd.key;" modifiers="accel,shift" command="cmd_redo"/>
    <key id="key_cut" key="&cutCmd.key;" modifiers="accel" command="cmd_cut"/>
    <key id="key_copy" key="&copyCmd.key;" modifiers="accel" command="cmd_copy"/>
    <key id="key_paste" key="&pasteCmd.key;" modifiers="accel" command="cmd_paste"/>
    <key id="key_delete" keycode="VK_DELETE" command="cmd_delete"/>
    <key id="key_selectAll" key="&selectAllCmd.key;" modifiers="accel" command="cmd_selectAll"/>
    <key id="key_find" key="&findCmd.key;" modifiers="accel" command="cmd_find"/>
    <key id="key_findAgain" key="&findAgainCmd.key;" modifiers="accel" command="cmd_findAgain"/>
    <key id="key_findPrevious" key="&findAgainCmd.key;" modifiers="shift,accel" command="cmd_findPrevious"/>
    <key id="key_findAgain2" keycode="&findAgainCmd.key2;" command="cmd_findAgain"/>
    <key id="key_findPrevious2" keycode="&findAgainCmd.key2;" modifiers="shift" command="cmd_findPrevious"/>
  </keyset>
<keyset id="sp-keyset">
  <key id="sp-key-window"
       key="&newWindowCmd.commandkey;"
       command="sp-cmd-newWindow"
       modifiers="accel"/>
  <key id="sp-key-open"
       key="&openFileCmd.commandkey;"
       command="sp-cmd-openFile"
       modifiers="accel"/>
  <key id="sp-key-save"
       key="&saveFileCmd.commandkey;"
       command="sp-cmd-save"
       modifiers="accel"/>
  <key id="sp-key-close"
       key="&closeCmd.key;"
       command="sp-cmd-close"
       modifiers="accel"/>
  <key id="sp-key-larger-font"
       key="&largerFont.commandkey;"
       command="sp-cmd-larger-font"
       modifiers="accel"/>
  <key key="&largerFont.commandkey2;"
       command="sp-cmd-larger-font"
       modifiers="accel"/>
  <key id="sp-key-smaller-font"
       key="&smallerFont.commandkey;"
       command="sp-cmd-smaller-font"
       modifiers="accel"/>
  <key id="sp-key-normal-size-font"
       key="&normalSize.commandkey;"
       command="sp-cmd-normal-font"
       modifiers="accel"/>
  <key id="sp-key-run"
       key="&run.key;"
       command="sp-cmd-run"
       modifiers="accel"/>
  <key id="sp-key-inspect"
       key="&inspect.key;"
       command="sp-cmd-inspect"
       modifiers="accel"/>
  <key id="sp-key-display"
       key="&display.key;"
       command="sp-cmd-display"
       modifiers="accel"/>
  <key id="sp-key-pprint"
       key="&pprint.key;"
       command="sp-cmd-pprint"
       modifiers="accel"/>
  <key id="sp-key-reloadAndRun"
       key="&reloadAndRun.key;"
       command="sp-cmd-reloadAndRun"
       modifiers="accel,shift"/>
  <key id="sp-key-evalFunction"
       key="&evalFunction.key;"
       command="sp-cmd-evalFunction"
       modifiers="accel"/>
  <key id="sp-key-errorConsole"
       key="&errorConsoleCmd.commandkey;"
       command="sp-cmd-errorConsole"
       modifiers="accel,shift"/>
  <key id="sp-key-hideSidebar"
       keycode="VK_ESCAPE"
       command="sp-cmd-hideSidebar"/>
  <key id="key_openHelp"
       keycode="VK_F1"
       command="sp-cmd-documentationLink"/>
  <key id="key_gotoLine"
       key="&gotoLineCmd.key;"
       command="key_gotoLine"
       modifiers="accel"/>

</keyset>

<menubar id="sp-menubar">
  <menu id="sp-file-menu" label="&fileMenu.label;" accesskey="&fileMenu.accesskey;">
    <menupopup id="sp-menu-filepopup">
      <menuitem id="sp-menu-newscratchpad"
                label="&newWindowCmd.label;"
                accesskey="&newWindowCmd.accesskey;"
                key="sp-key-window"
                command="sp-cmd-newWindow"/>
      <menuseparator/>

      <menuitem id="sp-menu-open"
                label="&openFileCmd.label;"
                command="sp-cmd-openFile"
                key="sp-key-open"
                accesskey="&openFileCmd.accesskey;"/>

      <menu id="sp-open_recent-menu" label="&openRecentMenu.label;"
            accesskey="&openRecentMenu.accesskey;"
            disabled="true">
        <menupopup id="sp-menu-open_recentPopup"/>
      </menu>

      <menuitem id="sp-menu-save"
                label="&saveFileCmd.label;"
                accesskey="&saveFileCmd.accesskey;"
                key="sp-key-save"
                command="sp-cmd-save"/>
      <menuitem id="sp-menu-saveas"
                label="&saveFileAsCmd.label;"
                accesskey="&saveFileAsCmd.accesskey;"
                command="sp-cmd-saveas"/>
      <menuitem id="sp-menu-revert"
                label="&revertCmd.label;"
                accesskey="&revertCmd.accesskey;"
                command="sp-cmd-revert"/>
      <menuseparator/>

      <menuitem id="sp-menu-close"
                label="&closeCmd.label;"
                key="sp-key-close"
                accesskey="&closeCmd.accesskey;"
                command="sp-cmd-close"/>
    </menupopup>
  </menu>

  <menu id="sp-edit-menu" label="&editMenu.label;"
        accesskey="&editMenu.accesskey;">
    <menupopup id="sp-menu_editpopup">
      <menuitem id="menu_undo" label="&undoCmd.label;"
                key="key_undo" accesskey="&undoCmd.accesskey;"
                command="cmd_undo"/>
      <menuitem id="menu_redo" label="&redoCmd.label;"
                key="key_redo" accesskey="&redoCmd.accesskey;"
                command="cmd_redo"/>
      <menuseparator/>
      <menuitem id="menu_cut" label="&cutCmd.label;"
                key="key_cut" accesskey="&cutCmd.accesskey;"
                command="cmd_cut"/>
      <menuitem id="menu_copy" label="&copyCmd.label;"
                key="key_copy" accesskey="&copyCmd.accesskey;"
                command="cmd_copy"/>
      <menuitem id="menu_paste" label="&pasteCmd.label;"
                key="key_paste" accesskey="&pasteCmd.accesskey;"
                command="cmd_paste"/>
      <menuseparator/>
      <menuitem id="menu_selectAll" label="&selectAllCmd.label;"
                key="key_selectAll" accesskey="&selectAllCmd.accesskey;"
                command="cmd_selectAll"/>
      <menuseparator/>
      <menuitem id="menu_find" label="&findCmd.label;"
                key="key_find" accesskey="&findCmd.accesskey;"
                command="cmd_find"/>
      <menuitem id="menu_findAgain" label="&findAgainCmd.label;"
                key="key_findAgain" accesskey="&findAgainCmd.accesskey;"
                command="cmd_findAgain"/>
      <menuseparator/>
      <menuitem id="se-menu-gotoLine"
          label="&gotoLineCmd.label;"
          accesskey="&gotoLineCmd.accesskey;"
          key="key_gotoLine"
          command="cmd_gotoLine"/>
      <menuitem id="sp-menu-pprint"
          label="&pprint.label;"
          accesskey="&pprint.accesskey;"
          key="sp-key-pprint"
          command="sp-cmd-pprint"/>
    </menupopup>
  </menu>

  <menu id="sp-view-menu" label="&viewMenu.label;" accesskey="&viewMenu.accesskey;">
    <menupopup id="sp-menu-viewpopup">
      <menuitem id="sp-menu-line-numbers"
                label="&lineNumbers.label;"
                accesskey="&lineNumbers.accesskey;"
                type="checkbox"
                command="sp-cmd-line-numbers"/>
      <menuitem id="sp-menu-word-wrap"
                label="&wordWrap.label;"
                accesskey="&wordWrap.accesskey;"
                type="checkbox"
                command="sp-cmd-wrap-text"/>
      <menuitem id="sp-menu-highlight-trailing-space"
                label="&highlightTrailingSpace.label;"
                accesskey="&highlightTrailingSpace.accesskey;"
                type="checkbox"
                command="sp-cmd-highlight-trailing-space"/>
      <menuseparator/>
      <menuitem id="sp-menu-larger-font"
                label="&largerFont.label;"
                key="sp-key-larger-font"
                accesskey="&largerFont.accesskey;"
                command="sp-cmd-larger-font"/>
      <menuitem id="sp-menu-smaller-font"
                label="&smallerFont.label;"
                key="sp-key-smaller-font"
                accesskey="&smallerFont.accesskey;"
                command="sp-cmd-smaller-font"/>
      <menuitem id="sp-menu-normal-size-font"
                label="&normalSize.label;"
                key="sp-menu-normal-font"
                accesskey="&normalSize.accesskey;"
                command="sp-cmd-normal-font"/>
    </menupopup>
  </menu>

  <menu id="sp-execute-menu" label="&executeMenu.label;"
        accesskey="&executeMenu.accesskey;">
    <menupopup id="sp-menu_executepopup">
      <menuitem id="sp-text-run"
                label="&run.label;"
                accesskey="&run.accesskey;"
                key="sp-key-run"
                command="sp-cmd-run"/>
      <menuitem id="sp-text-inspect"
                label="&inspect.label;"
                accesskey="&inspect.accesskey;"
                key="sp-key-inspect"
                command="sp-cmd-inspect"/>
      <menuitem id="sp-text-display"
                label="&display.label;"
                accesskey="&display.accesskey;"
                key="sp-key-display"
                command="sp-cmd-display"/>
      <menuseparator/>
      <menuitem id="sp-text-reloadAndRun"
                label="&reloadAndRun.label;"
                key="sp-key-reloadAndRun"
                accesskey="&reloadAndRun.accesskey;"
                command="sp-cmd-reloadAndRun"/>
      <menuitem id="sp-text-evalFunction"
                label="&evalFunction.label;"
                key="sp-key-evalFunction"
                accesskey="&evalFunction.accesskey;"
                command="sp-cmd-evalFunction"/>
    </menupopup>
  </menu>

  <menu id="sp-environment-menu"
        label="&environmentMenu.label;"
        accesskey="&environmentMenu.accesskey;"
        hidden="true">
    <menupopup id="sp-menu-environment">
      <menuitem id="sp-menu-content"
                label="&contentContext.label;"
                accesskey="&contentContext.accesskey;"
                command="sp-cmd-contentContext"
                checked="true"
                type="radio"/>
      <menuitem id="sp-menu-browser"
                command="sp-cmd-browserContext"
                label="&browserContext.label;"
                accesskey="&browserContext.accesskey;"
                type="radio"/>
    </menupopup>
  </menu>

  <menu id="sp-help-menu"
        label="&helpMenu.label;"
        accesskey="&helpMenu.accesskey;"
        accesskeywindows="&helpMenuWin.accesskey;">
    <menupopup id="sp-menu-help">
      <menuitem id="sp-menu-documentation"
                label="&documentationLink.label;"
                accesskey="&documentationLink.accesskey;"
                command="sp-cmd-documentationLink"
                key="key_openHelp"/>
    </menupopup>
  </menu>
</menubar>

<toolbar id="sp-toolbar"
         class="devtools-toolbar">
  <toolbarbutton id="sp-toolbar-open"
                 class="devtools-toolbarbutton"
                 label="&openFileCmd.label;"
                 command="sp-cmd-openFile"/>
  <toolbarbutton id="sp-toolbar-save"
                 class="devtools-toolbarbutton"
                 label="&saveFileCmd.label;"
                 command="sp-cmd-save"/>
  <toolbarbutton id="sp-toolbar-saveAs"
                 class="devtools-toolbarbutton"
                 label="&saveFileAsCmd.label;"
                 command="sp-cmd-saveas"/>
  <toolbarspacer/>
  <toolbarbutton id="sp-toolbar-run"
                 class="devtools-toolbarbutton"
                 label="&run.label;"
                 command="sp-cmd-run"/>
  <toolbarbutton id="sp-toolbar-inspect"
                 class="devtools-toolbarbutton"
                 label="&inspect.label;"
                 command="sp-cmd-inspect"/>
  <toolbarbutton id="sp-toolbar-display"
                 class="devtools-toolbarbutton"
                 label="&display.label;"
                 command="sp-cmd-display"/>
  <toolbarspacer/>
  <toolbarbutton id="sp-toolbar-pprint"
                 class="devtools-toolbarbutton"
                 label="&pprint.label;"
                 command="sp-cmd-pprint"/>
</toolbar>


<popupset id="scratchpad-popups">
  <menupopup id="scratchpad-text-popup">
    <menuitem id="cMenu_cut" label="&cutCmd.label;"
              accesskey="&cutCmd.accesskey;" command="cmd_cut"/>
    <menuitem id="cMenu_copy" label="&copyCmd.label;"
              accesskey="&copyCmd.accesskey;" command="cmd_copy"/>
    <menuitem id="cMenu_paste" label="&pasteCmd.label;"
              accesskey="&pasteCmd.accesskey;" command="cmd_paste"/>
    <menuitem id="cMenu_delete" label="&deleteCmd.label;"
              accesskey="&deleteCmd.accesskey;" command="cmd_delete"/>
    <menuseparator/>
    <menuitem id="cMenu_selectAll" label="&selectAllCmd.label;"
              accesskey="&selectAllCmd.accesskey;" command="cmd_selectAll"/>
    <menuseparator/>
    <menuitem id="sp-text-run"
              label="&run.label;"
              accesskey="&run.accesskey;"
              key="sp-key-run"
              command="sp-cmd-run"/>
    <menuitem id="sp-text-inspect"
              label="&inspect.label;"
              accesskey="&inspect.accesskey;"
              key="sp-key-inspect"
              command="sp-cmd-inspect"/>
    <menuitem id="sp-text-display"
              label="&display.label;"
              accesskey="&display.accesskey;"
              key="sp-key-display"
              command="sp-cmd-display"/>
    <menuitem id="sp-text-evalFunction"
              label="&evalFunction.label;"
              key="sp-key-evalFunction"
              accesskey="&evalFunction.accesskey;"
              command="sp-cmd-evalFunction"/>
    <menuseparator/>
    <menuitem id="sp-text-reloadAndRun"
              label="&reloadAndRun.label;"
              key="sp-key-reloadAndRun"
              accesskey="&reloadAndRun.accesskey;"
              command="sp-cmd-reloadAndRun"/>
  </menupopup>
</popupset>

<notificationbox id="scratchpad-notificationbox" flex="1">
  <hbox flex="1">
    <vbox id="scratchpad-editor" flex="1"/>
    <splitter class="devtools-side-splitter"/>
    <tabbox id="scratchpad-sidebar" class="devtools-sidebar-tabs"
                                    width="300"
                                    hidden="true">
      <tabs/>
      <tabpanels flex="1"/>
    </tabbox>
  </hbox>
  <toolbar id="statusbar-line-col" class="devtools-toolbar"/>
</notificationbox>

</window>
PK
!<#h�P�P4chrome/devtools/content/shadereditor/shadereditor.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
const {SideMenuWidget} = require("resource://devtools/client/shared/widgets/SideMenuWidget.jsm");
const promise = require("promise");
const defer = require("devtools/shared/defer");
const Services = require("Services");
const EventEmitter = require("devtools/shared/old-event-emitter");
const Tooltip = require("devtools/client/shared/widgets/tooltip/Tooltip");
const Editor = require("devtools/client/sourceeditor/editor");
const {LocalizationHelper} = require("devtools/shared/l10n");
const {extend} = require("devtools/shared/extend");
const {WidgetMethods, setNamedTimeout} =
  require("devtools/client/shared/widgets/view-helpers");
const {Task} = require("devtools/shared/task");

// Use privileged promise in panel documents to prevent having them to freeze
// during toolbox destruction. See bug 1402779.
const Promise = require("Promise");

// The panel's window global is an EventEmitter firing the following events:
const EVENTS = {
  // When new programs are received from the server.
  NEW_PROGRAM: "ShaderEditor:NewProgram",
  PROGRAMS_ADDED: "ShaderEditor:ProgramsAdded",

  // When the vertex and fragment sources were shown in the editor.
  SOURCES_SHOWN: "ShaderEditor:SourcesShown",

  // When a shader's source was edited and compiled via the editor.
  SHADER_COMPILED: "ShaderEditor:ShaderCompiled",

  // When the UI is reset from tab navigation
  UI_RESET: "ShaderEditor:UIReset",

  // When the editor's error markers are all removed
  EDITOR_ERROR_MARKERS_REMOVED: "ShaderEditor:EditorCleaned"
};
XPCOMUtils.defineConstant(this, "EVENTS", EVENTS);

const STRINGS_URI = "devtools/client/locales/shadereditor.properties";
const HIGHLIGHT_TINT = [1, 0, 0.25, 1]; // rgba
const TYPING_MAX_DELAY = 500; // ms
const SHADERS_AUTOGROW_ITEMS = 4;
const GUTTER_ERROR_PANEL_OFFSET_X = 7; // px
const GUTTER_ERROR_PANEL_DELAY = 100; // ms
const DEFAULT_EDITOR_CONFIG = {
  gutters: ["errors"],
  lineNumbers: true,
  showAnnotationRuler: true
};

/**
 * The current target and the WebGL Editor front, set by this tool's host.
 */
var gToolbox, gTarget, gFront;

/**
 * Initializes the shader editor controller and views.
 */
function startupShaderEditor() {
  return promise.all([
    EventsHandler.initialize(),
    ShadersListView.initialize(),
    ShadersEditorsView.initialize()
  ]);
}

/**
 * Destroys the shader editor controller and views.
 */
function shutdownShaderEditor() {
  return promise.all([
    EventsHandler.destroy(),
    ShadersListView.destroy(),
    ShadersEditorsView.destroy()
  ]);
}

/**
 * Functions handling target-related lifetime events.
 */
var EventsHandler = {
  /**
   * Listen for events emitted by the current tab target.
   */
  initialize: function () {
    this._onHostChanged = this._onHostChanged.bind(this);
    this._onTabNavigated = this._onTabNavigated.bind(this);
    this._onProgramLinked = this._onProgramLinked.bind(this);
    this._onProgramsAdded = this._onProgramsAdded.bind(this);
    gToolbox.on("host-changed", this._onHostChanged);
    gTarget.on("will-navigate", this._onTabNavigated);
    gTarget.on("navigate", this._onTabNavigated);
    gFront.on("program-linked", this._onProgramLinked);
    this.reloadButton = $("#requests-menu-reload-notice-button");
    this.reloadButton.addEventListener("command", this._onReloadCommand);
  },

  /**
   * Remove events emitted by the current tab target.
   */
  destroy: function () {
    gToolbox.off("host-changed", this._onHostChanged);
    gTarget.off("will-navigate", this._onTabNavigated);
    gTarget.off("navigate", this._onTabNavigated);
    gFront.off("program-linked", this._onProgramLinked);
    this.reloadButton.removeEventListener("command", this._onReloadCommand);
  },

  /**
   * Handles a command event on reload button
   */
  _onReloadCommand() {
    gFront.setup({ reload: true });
  },

  /**
   * Handles a host change event on the parent toolbox.
   */
  _onHostChanged: function () {
    if (gToolbox.hostType == "side") {
      $("#shaders-pane").removeAttribute("height");
    }
  },

  /**
   * Called for each location change in the debugged tab.
   */
  _onTabNavigated: function (event, {isFrameSwitching}) {
    switch (event) {
      case "will-navigate": {
        // Make sure the backend is prepared to handle WebGL contexts.
        if (!isFrameSwitching) {
          gFront.setup({ reload: false });
        }

        // Reset UI.
        ShadersListView.empty();
        // When switching to an iframe, ensure displaying the reload button.
        // As the document has already been loaded without being hooked.
        if (isFrameSwitching) {
          $("#reload-notice").hidden = false;
          $("#waiting-notice").hidden = true;
        } else {
          $("#reload-notice").hidden = true;
          $("#waiting-notice").hidden = false;
        }

        $("#content").hidden = true;
        window.emit(EVENTS.UI_RESET);

        break;
      }
      case "navigate": {
        // Manually retrieve the list of program actors known to the server,
        // because the backend won't emit "program-linked" notifications
        // in the case of a bfcache navigation (since no new programs are
        // actually linked).
        gFront.getPrograms().then(this._onProgramsAdded);
        break;
      }
    }
  },

  /**
   * Called every time a program was linked in the debugged tab.
   */
  _onProgramLinked: function (programActor) {
    this._addProgram(programActor);
    window.emit(EVENTS.NEW_PROGRAM);
  },

  /**
   * Callback for the front's getPrograms() method.
   */
  _onProgramsAdded: function (programActors) {
    programActors.forEach(this._addProgram);
    window.emit(EVENTS.PROGRAMS_ADDED);
  },

  /**
   * Adds a program to the shaders list and unhides any modal notices.
   */
  _addProgram: function (programActor) {
    $("#waiting-notice").hidden = true;
    $("#reload-notice").hidden = true;
    $("#content").hidden = false;
    ShadersListView.addProgram(programActor);
  }
};

/**
 * Functions handling the sources UI.
 */
var ShadersListView = extend(WidgetMethods, {
  /**
   * Initialization function, called when the tool is started.
   */
  initialize: function () {
    this.widget = new SideMenuWidget(this._pane = $("#shaders-pane"), {
      showArrows: true,
      showItemCheckboxes: true
    });

    this._onProgramSelect = this._onProgramSelect.bind(this);
    this._onProgramCheck = this._onProgramCheck.bind(this);
    this._onProgramMouseOver = this._onProgramMouseOver.bind(this);
    this._onProgramMouseOut = this._onProgramMouseOut.bind(this);

    this.widget.addEventListener("select", this._onProgramSelect);
    this.widget.addEventListener("check", this._onProgramCheck);
    this.widget.addEventListener("mouseover", this._onProgramMouseOver, true);
    this.widget.addEventListener("mouseout", this._onProgramMouseOut, true);
  },

  /**
   * Destruction function, called when the tool is closed.
   */
  destroy: function () {
    this.widget.removeEventListener("select", this._onProgramSelect);
    this.widget.removeEventListener("check", this._onProgramCheck);
    this.widget.removeEventListener("mouseover", this._onProgramMouseOver, true);
    this.widget.removeEventListener("mouseout", this._onProgramMouseOut, true);
  },

  /**
   * Adds a program to this programs container.
   *
   * @param object programActor
   *        The program actor coming from the active thread.
   */
  addProgram: function (programActor) {
    if (this.hasProgram(programActor)) {
      return;
    }

    // Currently, there's no good way of differentiating between programs
    // in a way that helps humans. It will be a good idea to implement a
    // standard of allowing debuggees to add some identifiable metadata to their
    // program sources or instances.
    let label = L10N.getFormatStr("shadersList.programLabel", this.itemCount);
    let contents = document.createElement("label");
    contents.className = "plain program-item";
    contents.setAttribute("value", label);
    contents.setAttribute("crop", "start");
    contents.setAttribute("flex", "1");

    // Append a program item to this container.
    this.push([contents], {
      index: -1, /* specifies on which position should the item be appended */
      attachment: {
        label: label,
        programActor: programActor,
        checkboxState: true,
        checkboxTooltip: L10N.getStr("shadersList.blackboxLabel")
      }
    });

    // Make sure there's always a selected item available.
    if (!this.selectedItem) {
      this.selectedIndex = 0;
    }

    // Prevent this container from growing indefinitely in height when the
    // toolbox is docked to the side.
    if (gToolbox.hostType == "side" && this.itemCount == SHADERS_AUTOGROW_ITEMS) {
      this._pane.setAttribute("height", this._pane.getBoundingClientRect().height);
    }
  },

  /**
   * Returns whether a program was already added to this programs container.
   *
   * @param object programActor
   *        The program actor coming from the active thread.
   * @param boolean
   *        True if the program was added, false otherwise.
   */
  hasProgram: function (programActor) {
    return !!this.attachments.filter(e => e.programActor == programActor).length;
  },

  /**
   * The select listener for the programs container.
   */
  _onProgramSelect: function ({ detail: sourceItem }) {
    if (!sourceItem) {
      return;
    }
    // The container is not empty and an actual item was selected.
    let attachment = sourceItem.attachment;

    function getShaders() {
      return promise.all([
        attachment.vs || (attachment.vs = attachment.programActor.getVertexShader()),
        attachment.fs || (attachment.fs = attachment.programActor.getFragmentShader())
      ]);
    }
    function getSources([vertexShaderActor, fragmentShaderActor]) {
      return promise.all([
        vertexShaderActor.getText(),
        fragmentShaderActor.getText()
      ]);
    }
    function showSources([vertexShaderText, fragmentShaderText]) {
      return ShadersEditorsView.setText({
        vs: vertexShaderText,
        fs: fragmentShaderText
      });
    }

    getShaders()
      .then(getSources)
      .then(showSources)
      .catch(console.error);
  },

  /**
   * The check listener for the programs container.
   */
  _onProgramCheck: function ({ detail: { checked }, target }) {
    let sourceItem = this.getItemForElement(target);
    let attachment = sourceItem.attachment;
    attachment.isBlackBoxed = !checked;
    attachment.programActor[checked ? "unblackbox" : "blackbox"]();
  },

  /**
   * The mouseover listener for the programs container.
   */
  _onProgramMouseOver: function (e) {
    let sourceItem = this.getItemForElement(e.target, { noSiblings: true });
    if (sourceItem && !sourceItem.attachment.isBlackBoxed) {
      sourceItem.attachment.programActor.highlight(HIGHLIGHT_TINT);

      if (e instanceof Event) {
        e.preventDefault();
        e.stopPropagation();
      }
    }
  },

  /**
   * The mouseout listener for the programs container.
   */
  _onProgramMouseOut: function (e) {
    let sourceItem = this.getItemForElement(e.target, { noSiblings: true });
    if (sourceItem && !sourceItem.attachment.isBlackBoxed) {
      sourceItem.attachment.programActor.unhighlight();

      if (e instanceof Event) {
        e.preventDefault();
        e.stopPropagation();
      }
    }
  }
});

/**
 * Functions handling the editors displaying the vertex and fragment shaders.
 */
var ShadersEditorsView = {
  /**
   * Initialization function, called when the tool is started.
   */
  initialize: function () {
    XPCOMUtils.defineLazyGetter(this, "_editorPromises", () => new Map());
    this._vsFocused = this._onFocused.bind(this, "vs", "fs");
    this._fsFocused = this._onFocused.bind(this, "fs", "vs");
    this._vsChanged = this._onChanged.bind(this, "vs");
    this._fsChanged = this._onChanged.bind(this, "fs");
  },

  /**
   * Destruction function, called when the tool is closed.
   */
  destroy: Task.async(function* () {
    this._destroyed = true;
    yield this._toggleListeners("off");
    for (let p of this._editorPromises.values()) {
      let editor = yield p;
      editor.destroy();
    }
  }),

  /**
   * Sets the text displayed in the vertex and fragment shader editors.
   *
   * @param object sources
   *        An object containing the following properties
   *          - vs: the vertex shader source code
   *          - fs: the fragment shader source code
   * @return object
   *        A promise resolving upon completion of text setting.
   */
  setText: function (sources) {
    let view = this;
    function setTextAndClearHistory(editor, text) {
      editor.setText(text);
      editor.clearHistory();
    }

    return Task.spawn(function* () {
      yield view._toggleListeners("off");
      yield promise.all([
        view._getEditor("vs").then(e => setTextAndClearHistory(e, sources.vs)),
        view._getEditor("fs").then(e => setTextAndClearHistory(e, sources.fs))
      ]);
      yield view._toggleListeners("on");
    }).then(() => window.emit(EVENTS.SOURCES_SHOWN, sources));
  },

  /**
   * Lazily initializes and returns a promise for an Editor instance.
   *
   * @param string type
   *        Specifies for which shader type should an editor be retrieved,
   *        either are "vs" for a vertex, or "fs" for a fragment shader.
   * @return object
   *        Returns a promise that resolves to an editor instance
   */
  _getEditor: function (type) {
    if (this._editorPromises.has(type)) {
      return this._editorPromises.get(type);
    }

    let deferred = defer();
    this._editorPromises.set(type, deferred.promise);

    // Initialize the source editor and store the newly created instance
    // in the ether of a resolved promise's value.
    let parent = $("#" + type + "-editor");
    let editor = new Editor(DEFAULT_EDITOR_CONFIG);
    editor.config.mode = Editor.modes[type];

    if (this._destroyed) {
      deferred.resolve(editor);
    } else {
      editor.appendTo(parent).then(() => deferred.resolve(editor));
    }

    return deferred.promise;
  },

  /**
   * Toggles all the event listeners for the editors either on or off.
   *
   * @param string flag
   *        Either "on" to enable the event listeners, "off" to disable them.
   * @return object
   *        A promise resolving upon completion of toggling the listeners.
   */
  _toggleListeners: function (flag) {
    return promise.all(["vs", "fs"].map(type => {
      return this._getEditor(type).then(editor => {
        editor[flag]("focus", this["_" + type + "Focused"]);
        editor[flag]("change", this["_" + type + "Changed"]);
      });
    }));
  },

  /**
   * The focus listener for a source editor.
   *
   * @param string focused
   *        The corresponding shader type for the focused editor (e.g. "vs").
   * @param string focused
   *        The corresponding shader type for the other editor (e.g. "fs").
   */
  _onFocused: function (focused, unfocused) {
    $("#" + focused + "-editor-label").setAttribute("selected", "");
    $("#" + unfocused + "-editor-label").removeAttribute("selected");
  },

  /**
   * The change listener for a source editor.
   *
   * @param string type
   *        The corresponding shader type for the focused editor (e.g. "vs").
   */
  _onChanged: function (type) {
    setNamedTimeout("gl-typed", TYPING_MAX_DELAY, () => this._doCompile(type));

    // Remove all the gutter markers and line classes from the editor.
    this._cleanEditor(type);
  },

  /**
   * Recompiles the source code for the shader being edited.
   * This function is fired at a certain delay after the user stops typing.
   *
   * @param string type
   *        The corresponding shader type for the focused editor (e.g. "vs").
   */
  _doCompile: function (type) {
    Task.spawn(function* () {
      let editor = yield this._getEditor(type);
      let shaderActor = yield ShadersListView.selectedAttachment[type];

      try {
        yield shaderActor.compile(editor.getText());
        this._onSuccessfulCompilation();
      } catch (e) {
        this._onFailedCompilation(type, editor, e);
      }
    }.bind(this));
  },

  /**
   * Called uppon a successful shader compilation.
   */
  _onSuccessfulCompilation: function () {
    // Signal that the shader was compiled successfully.
    window.emit(EVENTS.SHADER_COMPILED, null);
  },

  /**
   * Called uppon an unsuccessful shader compilation.
   */
  _onFailedCompilation: function (type, editor, errors) {
    let lineCount = editor.lineCount();
    let currentLine = editor.getCursor().line;
    let listeners = { mouseover: this._onMarkerMouseOver };

    function matchLinesAndMessages(string) {
      return {
        // First number that is not equal to 0.
        lineMatch: string.match(/\d{2,}|[1-9]/),
        // The string after all the numbers, semicolons and spaces.
        textMatch: string.match(/[^\s\d:][^\r\n|]*/)
      };
    }
    function discardInvalidMatches(e) {
      // Discard empty line and text matches.
      return e.lineMatch && e.textMatch;
    }
    function sanitizeValidMatches(e) {
      return {
        // Drivers might yield confusing line numbers under some obscure
        // circumstances. Don't throw the errors away in those cases,
        // just display them on the currently edited line.
        line: e.lineMatch[0] > lineCount ? currentLine : e.lineMatch[0] - 1,
        // Trim whitespace from the beginning and the end of the message,
        // and replace all other occurences of double spaces to a single space.
        text: e.textMatch[0].trim().replace(/\s{2,}/g, " ")
      };
    }
    function sortByLine(first, second) {
      // Sort all the errors ascending by their corresponding line number.
      return first.line > second.line ? 1 : -1;
    }
    function groupSameLineMessages(accumulator, current) {
      // Group errors corresponding to the same line number to a single object.
      let previous = accumulator[accumulator.length - 1];
      if (!previous || previous.line != current.line) {
        return [...accumulator, {
          line: current.line,
          messages: [current.text]
        }];
      } else {
        previous.messages.push(current.text);
        return accumulator;
      }
    }
    function displayErrors({ line, messages }) {
      // Add gutter markers and line classes for every error in the source.
      editor.addMarker(line, "errors", "error");
      editor.setMarkerListeners(line, "errors", "error", listeners, messages);
      editor.addLineClass(line, "error-line");
    }

    (this._errors[type] = errors.link
      .split("ERROR")
      .map(matchLinesAndMessages)
      .filter(discardInvalidMatches)
      .map(sanitizeValidMatches)
      .sort(sortByLine)
      .reduce(groupSameLineMessages, []))
      .forEach(displayErrors);

    // Signal that the shader wasn't compiled successfully.
    window.emit(EVENTS.SHADER_COMPILED, errors);
  },

  /**
   * Event listener for the 'mouseover' event on a marker in the editor gutter.
   */
  _onMarkerMouseOver: function (line, node, messages) {
    if (node._markerErrorsTooltip) {
      return;
    }

    let tooltip = node._markerErrorsTooltip = new Tooltip(document);
    tooltip.defaultOffsetX = GUTTER_ERROR_PANEL_OFFSET_X;
    tooltip.setTextContent({ messages: messages });
    tooltip.startTogglingOnHover(node, () => true, {
      toggleDelay: GUTTER_ERROR_PANEL_DELAY
    });
  },

  /**
   * Removes all the gutter markers and line classes from the editor.
   */
  _cleanEditor: function (type) {
    this._getEditor(type).then(editor => {
      editor.removeAllMarkers("errors");
      this._errors[type].forEach(e => editor.removeLineClass(e.line));
      this._errors[type].length = 0;
      window.emit(EVENTS.EDITOR_ERROR_MARKERS_REMOVED);
    });
  },

  _errors: {
    vs: [],
    fs: []
  }
};

/**
 * Localization convenience methods.
 */
var L10N = new LocalizationHelper(STRINGS_URI);

/**
 * Convenient way of emitting events from the panel window.
 */
EventEmitter.decorate(this);

/**
 * DOM query helper.
 */
var $ = (selector, target = document) => target.querySelector(selector);
PK
!<��Z

5chrome/devtools/content/shadereditor/shadereditor.xul<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/widgets.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/shadereditor.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/content/shared/widgets/widgets.css" type="text/css"?>
<!DOCTYPE window [
  <!ENTITY % debuggerDTD SYSTEM "chrome://devtools/locale/shadereditor.dtd">
  %debuggerDTD;
]>

<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

  <script type="application/javascript"
          src="chrome://devtools/content/shared/theme-switching.js"/>

  <script type="application/javascript" src="shadereditor.js"/>

  <vbox class="theme-body" flex="1">
    <hbox id="reload-notice"
          class="notice-container"
          align="center"
          pack="center"
          flex="1">
      <button id="requests-menu-reload-notice-button"
              class="devtools-toolbarbutton"
              standalone="true"
              label="&shaderEditorUI.reloadNotice1;"/>
      <label id="requests-menu-reload-notice-label"
             class="plain"
             value="&shaderEditorUI.reloadNotice2;"/>
    </hbox>
    <hbox id="waiting-notice"
          class="notice-container devtools-throbber"
          align="center"
          pack="center"
          flex="1"
          hidden="true">
      <label id="requests-menu-waiting-notice-label"
             class="plain"
             value="&shaderEditorUI.emptyNotice;"/>
    </hbox>

    <box id="content"
         class="devtools-responsive-container"
         flex="1"
         hidden="true">
      <vbox id="shaders-pane"/>
      <splitter class="devtools-side-splitter"/>
      <box id="shaders-editors" class="devtools-responsive-container" flex="1">
        <vbox flex="1">
          <vbox id="vs-editor" flex="1"/>
          <label id="vs-editor-label"
                 class="plain editor-label"
                 value="&shaderEditorUI.vertexShader;"/>
        </vbox>
        <splitter id="editors-splitter" class="devtools-side-splitter"/>
        <vbox flex="1">
          <vbox id="fs-editor" flex="1"/>
          <label id="fs-editor-label"
                 class="plain editor-label"
                 value="&shaderEditorUI.fragmentShader;"/>
        </vbox>
      </box>
    </box>
  </vbox>

</window>
PK
!<�Q�*��,chrome/devtools/content/shared/splitview.css/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

box,
.splitview-nav {
  -moz-box-flex: 1;
  -moz-box-orient: vertical;
}

.splitview-nav-container {
  -moz-box-pack: center;
}

.loading .splitview-nav-container > .placeholder {
  display: none !important;
}

.splitview-controller,
.splitview-main {
  -moz-box-flex: 0;
}

.splitview-controller {
  min-height: 3em;
  max-height: 14em;
  max-width: 400px;
  min-width: 200px;
}

.splitview-nav {
  display: -moz-box;
  overflow-x: hidden;
  overflow-y: auto;
}

/* only the active details pane is shown */
.splitview-side-details > * {
  display: none;
}
.splitview-side-details > .splitview-active {
  display: -moz-box;
}

/* this is to keep in sync with SplitView.jsm's LANDSCAPE_MEDIA_QUERY */
@media (min-width: 701px) {
  .splitview-root {
    -moz-box-orient: horizontal;
  }
  .splitview-controller {
    max-height: none;
  }
  .splitview-details {
    display: none;
  }
  .splitview-details.splitview-active {
    display: -moz-box;
  }
}

/* filtered items are hidden */
ol.splitview-nav > li.splitview-filtered {
  display: none;
}

/* "empty list" and "all filtered" placeholders are hidden */
.splitview-nav:empty,
.splitview-nav.splitview-all-filtered,
.splitview-nav + .splitview-nav.placeholder {
  display: none;
}
.splitview-nav.splitview-all-filtered ~ .splitview-nav.placeholder.all-filtered,
.splitview-nav:empty ~ .splitview-nav.placeholder.empty {
  display: -moz-box;
}

/* portrait mode */
@media (max-width: 700px) {
  .splitview-controller {
    max-width: none;
  }
}
PK
!<aǹ��1chrome/devtools/content/shared/theme-switching.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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.import("resource://devtools/shared/Loader.jsm", {});
  const Services = require("Services");
  const { gDevTools } = require("devtools/client/framework/devtools");
  const { appendStyleSheet } = require("devtools/client/shared/stylesheet-utils");

  let documentElement = document.documentElement;

  let os;
  let 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;
  }

  let devtoolsStyleSheets = new WeakMap();
  let gOldTheme = "";

  function forceStyle() {
    let computedStyle = window.getComputedStyle(documentElement);
    if (!computedStyle) {
      // Null when documentElement is not ready. This method is anyways not
      // required then as scrollbars would be in their state without flushing.
      return;
    }
    // Save display value
    let display = computedStyle.display;
    documentElement.style.display = "none";
    // Flush
    window.getComputedStyle(documentElement).display;
    // Restore
    documentElement.style.display = display;
  }

  /*
   * 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;
    }
    let oldTheme = gOldTheme;
    gOldTheme = newTheme;

    let 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("light");
    }

    // 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, []);

    let loadEvents = [];
    for (let url of newThemeDef.stylesheets) {
      let {styleSheet, loadPromise} = appendStyleSheet(document, url);
      devtoolsStyleSheets.get(newThemeDef).push(styleSheet);
      loadEvents.push(loadPromise);
    }

    try {
      const StylesheetUtils = require("devtools/shared/layout/utils");
      const SCROLLBARS_URL = "chrome://devtools/skin/floating-scrollbars-dark-theme.css";

      // TODO: extensions might want to customize scrollbar styles too.
      if (!Services.appShell.hiddenDOMWindow
        .matchMedia("(-moz-overlay-scrollbars)").matches) {
        if (newTheme == "dark") {
          StylesheetUtils.loadSheet(window, SCROLLBARS_URL, "agent");
        } else if (oldTheme == "dark") {
          StylesheetUtils.removeSheet(window, SCROLLBARS_URL, "agent");
        }
        forceStyle();
      }
    } catch (e) {
      console.warn("customize scrollbar styles is only supported in firefox");
    }

    Promise.all(loadEvents).then(() => {
      // Unload all stylesheets and classes from the old theme.
      if (oldThemeDef) {
        for (let name of oldThemeDef.classList) {
          documentElement.classList.remove(name);
        }

        for (let sheet of devtoolsStyleSheets.get(oldThemeDef) || []) {
          sheet.remove();
        }

        if (oldThemeDef.onUnapply) {
          oldThemeDef.onUnapply(window, newTheme);
        }
      }

      // Load all stylesheets and classes from the new theme.
      for (let 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.bind(console));
  }

  function handlePrefChange() {
    switchTheme(Services.prefs.getCharPref("devtools.theme"));
  }

  if (documentElement.hasAttribute("force-theme")) {
    switchTheme(documentElement.getAttribute("force-theme"));
  } else {
    switchTheme(Services.prefs.getCharPref("devtools.theme"));

    Services.prefs.addObserver("devtools.theme", handlePrefChange);
    window.addEventListener("unload", function () {
      Services.prefs.removeObserver("devtools.theme", handlePrefChange);
    }, { once: true });
  }
})();
PK
!<��>���+chrome/devtools/content/shared/vendor/d3.js!function() {
  var d3 = {
    version: "3.4.2"
  };
  if (!Date.now) Date.now = function() {
    return +new Date();
  };
  var d3_arraySlice = [].slice, d3_array = function(list) {
    return d3_arraySlice.call(list);
  };
  var d3_document = document, d3_documentElement = d3_document.documentElement, d3_window = window;
  try {
    d3_array(d3_documentElement.childNodes)[0].nodeType;
  } catch (e) {
    d3_array = function(list) {
      var i = list.length, array = new Array(i);
      while (i--) array[i] = list[i];
      return array;
    };
  }
  try {
    d3_document.createElement("div").style.setProperty("opacity", 0, "");
  } catch (error) {
    var d3_element_prototype = d3_window.Element.prototype, d3_element_setAttribute = d3_element_prototype.setAttribute, d3_element_setAttributeNS = d3_element_prototype.setAttributeNS, d3_style_prototype = d3_window.CSSStyleDeclaration.prototype, d3_style_setProperty = d3_style_prototype.setProperty;
    d3_element_prototype.setAttribute = function(name, value) {
      d3_element_setAttribute.call(this, name, value + "");
    };
    d3_element_prototype.setAttributeNS = function(space, local, value) {
      d3_element_setAttributeNS.call(this, space, local, value + "");
    };
    d3_style_prototype.setProperty = function(name, value, priority) {
      d3_style_setProperty.call(this, name, value + "", priority);
    };
  }
  d3.ascending = function(a, b) {
    return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
  };
  d3.descending = function(a, b) {
    return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
  };
  d3.min = function(array, f) {
    var i = -1, n = array.length, a, b;
    if (arguments.length === 1) {
      while (++i < n && !((a = array[i]) != null && a <= a)) a = undefined;
      while (++i < n) if ((b = array[i]) != null && a > b) a = b;
    } else {
      while (++i < n && !((a = f.call(array, array[i], i)) != null && a <= a)) a = undefined;
      while (++i < n) if ((b = f.call(array, array[i], i)) != null && a > b) a = b;
    }
    return a;
  };
  d3.max = function(array, f) {
    var i = -1, n = array.length, a, b;
    if (arguments.length === 1) {
      while (++i < n && !((a = array[i]) != null && a <= a)) a = undefined;
      while (++i < n) if ((b = array[i]) != null && b > a) a = b;
    } else {
      while (++i < n && !((a = f.call(array, array[i], i)) != null && a <= a)) a = undefined;
      while (++i < n) if ((b = f.call(array, array[i], i)) != null && b > a) a = b;
    }
    return a;
  };
  d3.extent = function(array, f) {
    var i = -1, n = array.length, a, b, c;
    if (arguments.length === 1) {
      while (++i < n && !((a = c = array[i]) != null && a <= a)) a = c = undefined;
      while (++i < n) if ((b = array[i]) != null) {
        if (a > b) a = b;
        if (c < b) c = b;
      }
    } else {
      while (++i < n && !((a = c = f.call(array, array[i], i)) != null && a <= a)) a = undefined;
      while (++i < n) if ((b = f.call(array, array[i], i)) != null) {
        if (a > b) a = b;
        if (c < b) c = b;
      }
    }
    return [ a, c ];
  };
  d3.sum = function(array, f) {
    var s = 0, n = array.length, a, i = -1;
    if (arguments.length === 1) {
      while (++i < n) if (!isNaN(a = +array[i])) s += a;
    } else {
      while (++i < n) if (!isNaN(a = +f.call(array, array[i], i))) s += a;
    }
    return s;
  };
  function d3_number(x) {
    return x != null && !isNaN(x);
  }
  d3.mean = function(array, f) {
    var n = array.length, a, m = 0, i = -1, j = 0;
    if (arguments.length === 1) {
      while (++i < n) if (d3_number(a = array[i])) m += (a - m) / ++j;
    } else {
      while (++i < n) if (d3_number(a = f.call(array, array[i], i))) m += (a - m) / ++j;
    }
    return j ? m : undefined;
  };
  d3.quantile = function(values, p) {
    var H = (values.length - 1) * p + 1, h = Math.floor(H), v = +values[h - 1], e = H - h;
    return e ? v + e * (values[h] - v) : v;
  };
  d3.median = function(array, f) {
    if (arguments.length > 1) array = array.map(f);
    array = array.filter(d3_number);
    return array.length ? d3.quantile(array.sort(d3.ascending), .5) : undefined;
  };
  d3.bisector = function(f) {
    return {
      left: function(a, x, lo, hi) {
        if (arguments.length < 3) lo = 0;
        if (arguments.length < 4) hi = a.length;
        while (lo < hi) {
          var mid = lo + hi >>> 1;
          if (f.call(a, a[mid], mid) < x) lo = mid + 1; else hi = mid;
        }
        return lo;
      },
      right: function(a, x, lo, hi) {
        if (arguments.length < 3) lo = 0;
        if (arguments.length < 4) hi = a.length;
        while (lo < hi) {
          var mid = lo + hi >>> 1;
          if (x < f.call(a, a[mid], mid)) hi = mid; else lo = mid + 1;
        }
        return lo;
      }
    };
  };
  var d3_bisector = d3.bisector(function(d) {
    return d;
  });
  d3.bisectLeft = d3_bisector.left;
  d3.bisect = d3.bisectRight = d3_bisector.right;
  d3.shuffle = function(array) {
    var m = array.length, t, i;
    while (m) {
      i = Math.random() * m-- | 0;
      t = array[m], array[m] = array[i], array[i] = t;
    }
    return array;
  };
  d3.permute = function(array, indexes) {
    var i = indexes.length, permutes = new Array(i);
    while (i--) permutes[i] = array[indexes[i]];
    return permutes;
  };
  d3.pairs = function(array) {
    var i = 0, n = array.length - 1, p0, p1 = array[0], pairs = new Array(n < 0 ? 0 : n);
    while (i < n) pairs[i] = [ p0 = p1, p1 = array[++i] ];
    return pairs;
  };
  d3.zip = function() {
    if (!(n = arguments.length)) return [];
    for (var i = -1, m = d3.min(arguments, d3_zipLength), zips = new Array(m); ++i < m; ) {
      for (var j = -1, n, zip = zips[i] = new Array(n); ++j < n; ) {
        zip[j] = arguments[j][i];
      }
    }
    return zips;
  };
  function d3_zipLength(d) {
    return d.length;
  }
  d3.transpose = function(matrix) {
    return d3.zip.apply(d3, matrix);
  };
  d3.keys = function(map) {
    var keys = [];
    for (var key in map) keys.push(key);
    return keys;
  };
  d3.values = function(map) {
    var values = [];
    for (var key in map) values.push(map[key]);
    return values;
  };
  d3.entries = function(map) {
    var entries = [];
    for (var key in map) entries.push({
      key: key,
      value: map[key]
    });
    return entries;
  };
  d3.merge = function(arrays) {
    var n = arrays.length, m, i = -1, j = 0, merged, array;
    while (++i < n) j += arrays[i].length;
    merged = new Array(j);
    while (--n >= 0) {
      array = arrays[n];
      m = array.length;
      while (--m >= 0) {
        merged[--j] = array[m];
      }
    }
    return merged;
  };
  var abs = Math.abs;
  d3.range = function(start, stop, step) {
    if (arguments.length < 3) {
      step = 1;
      if (arguments.length < 2) {
        stop = start;
        start = 0;
      }
    }
    if ((stop - start) / step === Infinity) throw new Error("infinite range");
    var range = [], k = d3_range_integerScale(abs(step)), i = -1, j;
    start *= k, stop *= k, step *= k;
    if (step < 0) while ((j = start + step * ++i) > stop) range.push(j / k); else while ((j = start + step * ++i) < stop) range.push(j / k);
    return range;
  };
  function d3_range_integerScale(x) {
    var k = 1;
    while (x * k % 1) k *= 10;
    return k;
  }
  function d3_class(ctor, properties) {
    try {
      for (var key in properties) {
        Object.defineProperty(ctor.prototype, key, {
          value: properties[key],
          enumerable: false
        });
      }
    } catch (e) {
      ctor.prototype = properties;
    }
  }
  d3.map = function(object) {
    var map = new d3_Map();
    if (object instanceof d3_Map) object.forEach(function(key, value) {
      map.set(key, value);
    }); else for (var key in object) map.set(key, object[key]);
    return map;
  };
  function d3_Map() {}
  d3_class(d3_Map, {
    has: d3_map_has,
    get: function(key) {
      return this[d3_map_prefix + key];
    },
    set: function(key, value) {
      return this[d3_map_prefix + key] = value;
    },
    remove: d3_map_remove,
    keys: d3_map_keys,
    values: function() {
      var values = [];
      this.forEach(function(key, value) {
        values.push(value);
      });
      return values;
    },
    entries: function() {
      var entries = [];
      this.forEach(function(key, value) {
        entries.push({
          key: key,
          value: value
        });
      });
      return entries;
    },
    size: d3_map_size,
    empty: d3_map_empty,
    forEach: function(f) {
      for (var key in this) if (key.charCodeAt(0) === d3_map_prefixCode) f.call(this, key.substring(1), this[key]);
    }
  });
  var d3_map_prefix = "\x00", d3_map_prefixCode = d3_map_prefix.charCodeAt(0);
  function d3_map_has(key) {
    return d3_map_prefix + key in this;
  }
  function d3_map_remove(key) {
    key = d3_map_prefix + key;
    return key in this && delete this[key];
  }
  function d3_map_keys() {
    var keys = [];
    this.forEach(function(key) {
      keys.push(key);
    });
    return keys;
  }
  function d3_map_size() {
    var size = 0;
    for (var key in this) if (key.charCodeAt(0) === d3_map_prefixCode) ++size;
    return size;
  }
  function d3_map_empty() {
    for (var key in this) if (key.charCodeAt(0) === d3_map_prefixCode) return false;
    return true;
  }
  d3.nest = function() {
    var nest = {}, keys = [], sortKeys = [], sortValues, rollup;
    function map(mapType, array, depth) {
      if (depth >= keys.length) return rollup ? rollup.call(nest, array) : sortValues ? array.sort(sortValues) : array;
      var i = -1, n = array.length, key = keys[depth++], keyValue, object, setter, valuesByKey = new d3_Map(), values;
      while (++i < n) {
        if (values = valuesByKey.get(keyValue = key(object = array[i]))) {
          values.push(object);
        } else {
          valuesByKey.set(keyValue, [ object ]);
        }
      }
      if (mapType) {
        object = mapType();
        setter = function(keyValue, values) {
          object.set(keyValue, map(mapType, values, depth));
        };
      } else {
        object = {};
        setter = function(keyValue, values) {
          object[keyValue] = map(mapType, values, depth);
        };
      }
      valuesByKey.forEach(setter);
      return object;
    }
    function entries(map, depth) {
      if (depth >= keys.length) return map;
      var array = [], sortKey = sortKeys[depth++];
      map.forEach(function(key, keyMap) {
        array.push({
          key: key,
          values: entries(keyMap, depth)
        });
      });
      return sortKey ? array.sort(function(a, b) {
        return sortKey(a.key, b.key);
      }) : array;
    }
    nest.map = function(array, mapType) {
      return map(mapType, array, 0);
    };
    nest.entries = function(array) {
      return entries(map(d3.map, array, 0), 0);
    };
    nest.key = function(d) {
      keys.push(d);
      return nest;
    };
    nest.sortKeys = function(order) {
      sortKeys[keys.length - 1] = order;
      return nest;
    };
    nest.sortValues = function(order) {
      sortValues = order;
      return nest;
    };
    nest.rollup = function(f) {
      rollup = f;
      return nest;
    };
    return nest;
  };
  d3.set = function(array) {
    var set = new d3_Set();
    if (array) for (var i = 0, n = array.length; i < n; ++i) set.add(array[i]);
    return set;
  };
  function d3_Set() {}
  d3_class(d3_Set, {
    has: d3_map_has,
    add: function(value) {
      this[d3_map_prefix + value] = true;
      return value;
    },
    remove: function(value) {
      value = d3_map_prefix + value;
      return value in this && delete this[value];
    },
    values: d3_map_keys,
    size: d3_map_size,
    empty: d3_map_empty,
    forEach: function(f) {
      for (var value in this) if (value.charCodeAt(0) === d3_map_prefixCode) f.call(this, value.substring(1));
    }
  });
  d3.behavior = {};
  d3.rebind = function(target, source) {
    var i = 1, n = arguments.length, method;
    while (++i < n) target[method = arguments[i]] = d3_rebind(target, source, source[method]);
    return target;
  };
  function d3_rebind(target, source, method) {
    return function() {
      var value = method.apply(source, arguments);
      return value === source ? target : value;
    };
  }
  function d3_vendorSymbol(object, name) {
    if (name in object) return name;
    name = name.charAt(0).toUpperCase() + name.substring(1);
    for (var i = 0, n = d3_vendorPrefixes.length; i < n; ++i) {
      var prefixName = d3_vendorPrefixes[i] + name;
      if (prefixName in object) return prefixName;
    }
  }
  var d3_vendorPrefixes = [ "webkit", "ms", "moz", "Moz", "o", "O" ];
  function d3_noop() {}
  d3.dispatch = function() {
    var dispatch = new d3_dispatch(), i = -1, n = arguments.length;
    while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch);
    return dispatch;
  };
  function d3_dispatch() {}
  d3_dispatch.prototype.on = function(type, listener) {
    var i = type.indexOf("."), name = "";
    if (i >= 0) {
      name = type.substring(i + 1);
      type = type.substring(0, i);
    }
    if (type) return arguments.length < 2 ? this[type].on(name) : this[type].on(name, listener);
    if (arguments.length === 2) {
      if (listener == null) for (type in this) {
        if (this.hasOwnProperty(type)) this[type].on(name, null);
      }
      return this;
    }
  };
  function d3_dispatch_event(dispatch) {
    var listeners = [], listenerByName = new d3_Map();
    function event() {
      var z = listeners, i = -1, n = z.length, l;
      while (++i < n) if (l = z[i].on) l.apply(this, arguments);
      return dispatch;
    }
    event.on = function(name, listener) {
      var l = listenerByName.get(name), i;
      if (arguments.length < 2) return l && l.on;
      if (l) {
        l.on = null;
        listeners = listeners.slice(0, i = listeners.indexOf(l)).concat(listeners.slice(i + 1));
        listenerByName.remove(name);
      }
      if (listener) listeners.push(listenerByName.set(name, {
        on: listener
      }));
      return dispatch;
    };
    return event;
  }
  d3.event = null;
  function d3_eventPreventDefault() {
    d3.event.preventDefault();
  }
  function d3_eventSource() {
    var e = d3.event, s;
    while (s = e.sourceEvent) e = s;
    return e;
  }
  function d3_eventDispatch(target) {
    var dispatch = new d3_dispatch(), i = 0, n = arguments.length;
    while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch);
    dispatch.of = function(thiz, argumentz) {
      return function(e1) {
        try {
          var e0 = e1.sourceEvent = d3.event;
          e1.target = target;
          d3.event = e1;
          dispatch[e1.type].apply(thiz, argumentz);
        } finally {
          d3.event = e0;
        }
      };
    };
    return dispatch;
  }
  d3.requote = function(s) {
    return s.replace(d3_requote_re, "\\$&");
  };
  var d3_requote_re = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;
  var d3_subclass = {}.__proto__ ? function(object, prototype) {
    object.__proto__ = prototype;
  } : function(object, prototype) {
    for (var property in prototype) object[property] = prototype[property];
  };
  function d3_selection(groups) {
    d3_subclass(groups, d3_selectionPrototype);
    return groups;
  }
  var d3_select = function(s, n) {
    return n.querySelector(s);
  }, d3_selectAll = function(s, n) {
    return n.querySelectorAll(s);
  }, d3_selectMatcher = d3_documentElement[d3_vendorSymbol(d3_documentElement, "matchesSelector")], d3_selectMatches = function(n, s) {
    return d3_selectMatcher.call(n, s);
  };
  if (typeof Sizzle === "function") {
    d3_select = function(s, n) {
      return Sizzle(s, n)[0] || null;
    };
    d3_selectAll = function(s, n) {
      return Sizzle.uniqueSort(Sizzle(s, n));
    };
    d3_selectMatches = Sizzle.matchesSelector;
  }
  d3.selection = function() {
    return d3_selectionRoot;
  };
  var d3_selectionPrototype = d3.selection.prototype = [];
  d3_selectionPrototype.select = function(selector) {
    var subgroups = [], subgroup, subnode, group, node;
    selector = d3_selection_selector(selector);
    for (var j = -1, m = this.length; ++j < m; ) {
      subgroups.push(subgroup = []);
      subgroup.parentNode = (group = this[j]).parentNode;
      for (var i = -1, n = group.length; ++i < n; ) {
        if (node = group[i]) {
          subgroup.push(subnode = selector.call(node, node.__data__, i, j));
          if (subnode && "__data__" in node) subnode.__data__ = node.__data__;
        } else {
          subgroup.push(null);
        }
      }
    }
    return d3_selection(subgroups);
  };
  function d3_selection_selector(selector) {
    return typeof selector === "function" ? selector : function() {
      return d3_select(selector, this);
    };
  }
  d3_selectionPrototype.selectAll = function(selector) {
    var subgroups = [], subgroup, node;
    selector = d3_selection_selectorAll(selector);
    for (var j = -1, m = this.length; ++j < m; ) {
      for (var group = this[j], i = -1, n = group.length; ++i < n; ) {
        if (node = group[i]) {
          subgroups.push(subgroup = d3_array(selector.call(node, node.__data__, i, j)));
          subgroup.parentNode = node;
        }
      }
    }
    return d3_selection(subgroups);
  };
  function d3_selection_selectorAll(selector) {
    return typeof selector === "function" ? selector : function() {
      return d3_selectAll(selector, this);
    };
  }
  var d3_nsPrefix = {
    svg: "http://www.w3.org/2000/svg",
    xhtml: "http://www.w3.org/1999/xhtml",
    xlink: "http://www.w3.org/1999/xlink",
    xml: "http://www.w3.org/XML/1998/namespace",
    xmlns: "http://www.w3.org/2000/xmlns/"
  };
  d3.ns = {
    prefix: d3_nsPrefix,
    qualify: function(name) {
      var i = name.indexOf(":"), prefix = name;
      if (i >= 0) {
        prefix = name.substring(0, i);
        name = name.substring(i + 1);
      }
      return d3_nsPrefix.hasOwnProperty(prefix) ? {
        space: d3_nsPrefix[prefix],
        local: name
      } : name;
    }
  };
  d3_selectionPrototype.attr = function(name, value) {
    if (arguments.length < 2) {
      if (typeof name === "string") {
        var node = this.node();
        name = d3.ns.qualify(name);
        return name.local ? node.getAttributeNS(name.space, name.local) : node.getAttribute(name);
      }
      for (value in name) this.each(d3_selection_attr(value, name[value]));
      return this;
    }
    return this.each(d3_selection_attr(name, value));
  };
  function d3_selection_attr(name, value) {
    name = d3.ns.qualify(name);
    function attrNull() {
      this.removeAttribute(name);
    }
    function attrNullNS() {
      this.removeAttributeNS(name.space, name.local);
    }
    function attrConstant() {
      this.setAttribute(name, value);
    }
    function attrConstantNS() {
      this.setAttributeNS(name.space, name.local, value);
    }
    function attrFunction() {
      var x = value.apply(this, arguments);
      if (x == null) this.removeAttribute(name); else this.setAttribute(name, x);
    }
    function attrFunctionNS() {
      var x = value.apply(this, arguments);
      if (x == null) this.removeAttributeNS(name.space, name.local); else this.setAttributeNS(name.space, name.local, x);
    }
    return value == null ? name.local ? attrNullNS : attrNull : typeof value === "function" ? name.local ? attrFunctionNS : attrFunction : name.local ? attrConstantNS : attrConstant;
  }
  function d3_collapse(s) {
    return s.trim().replace(/\s+/g, " ");
  }
  d3_selectionPrototype.classed = function(name, value) {
    if (arguments.length < 2) {
      if (typeof name === "string") {
        var node = this.node(), n = (name = d3_selection_classes(name)).length, i = -1;
        if (value = node.classList) {
          while (++i < n) if (!value.contains(name[i])) return false;
        } else {
          value = node.getAttribute("class");
          while (++i < n) if (!d3_selection_classedRe(name[i]).test(value)) return false;
        }
        return true;
      }
      for (value in name) this.each(d3_selection_classed(value, name[value]));
      return this;
    }
    return this.each(d3_selection_classed(name, value));
  };
  function d3_selection_classedRe(name) {
    return new RegExp("(?:^|\\s+)" + d3.requote(name) + "(?:\\s+|$)", "g");
  }
  function d3_selection_classes(name) {
    return name.trim().split(/^|\s+/);
  }
  function d3_selection_classed(name, value) {
    name = d3_selection_classes(name).map(d3_selection_classedName);
    var n = name.length;
    function classedConstant() {
      var i = -1;
      while (++i < n) name[i](this, value);
    }
    function classedFunction() {
      var i = -1, x = value.apply(this, arguments);
      while (++i < n) name[i](this, x);
    }
    return typeof value === "function" ? classedFunction : classedConstant;
  }
  function d3_selection_classedName(name) {
    var re = d3_selection_classedRe(name);
    return function(node, value) {
      if (c = node.classList) return value ? c.add(name) : c.remove(name);
      var c = node.getAttribute("class") || "";
      if (value) {
        re.lastIndex = 0;
        if (!re.test(c)) node.setAttribute("class", d3_collapse(c + " " + name));
      } else {
        node.setAttribute("class", d3_collapse(c.replace(re, " ")));
      }
    };
  }
  d3_selectionPrototype.style = function(name, value, priority) {
    var n = arguments.length;
    if (n < 3) {
      if (typeof name !== "string") {
        if (n < 2) value = "";
        for (priority in name) this.each(d3_selection_style(priority, name[priority], value));
        return this;
      }
      if (n < 2) return d3_window.getComputedStyle(this.node(), null).getPropertyValue(name);
      priority = "";
    }
    return this.each(d3_selection_style(name, value, priority));
  };
  function d3_selection_style(name, value, priority) {
    function styleNull() {
      this.style.removeProperty(name);
    }
    function styleConstant() {
      this.style.setProperty(name, value, priority);
    }
    function styleFunction() {
      var x = value.apply(this, arguments);
      if (x == null) this.style.removeProperty(name); else this.style.setProperty(name, x, priority);
    }
    return value == null ? styleNull : typeof value === "function" ? styleFunction : styleConstant;
  }
  d3_selectionPrototype.property = function(name, value) {
    if (arguments.length < 2) {
      if (typeof name === "string") return this.node()[name];
      for (value in name) this.each(d3_selection_property(value, name[value]));
      return this;
    }
    return this.each(d3_selection_property(name, value));
  };
  function d3_selection_property(name, value) {
    function propertyNull() {
      delete this[name];
    }
    function propertyConstant() {
      this[name] = value;
    }
    function propertyFunction() {
      var x = value.apply(this, arguments);
      if (x == null) delete this[name]; else this[name] = x;
    }
    return value == null ? propertyNull : typeof value === "function" ? propertyFunction : propertyConstant;
  }
  d3_selectionPrototype.text = function(value) {
    return arguments.length ? this.each(typeof value === "function" ? function() {
      var v = value.apply(this, arguments);
      this.textContent = v == null ? "" : v;
    } : value == null ? function() {
      this.textContent = "";
    } : function() {
      this.textContent = value;
    }) : this.node().textContent;
  };
  d3_selectionPrototype.html = function(value) {
    return arguments.length ? this.each(typeof value === "function" ? function() {
      var v = value.apply(this, arguments);
      this.innerHTML = v == null ? "" : v;
    } : value == null ? function() {
      this.innerHTML = "";
    } : function() {
      this.innerHTML = value;
    }) : this.node().innerHTML;
  };
  d3_selectionPrototype.append = function(name) {
    name = d3_selection_creator(name);
    return this.select(function() {
      return this.appendChild(name.apply(this, arguments));
    });
  };
  function d3_selection_creator(name) {
    return typeof name === "function" ? name : (name = d3.ns.qualify(name)).local ? function() {
      return this.ownerDocument.createElementNS(name.space, name.local);
    } : function() {
      return this.ownerDocument.createElementNS(this.namespaceURI, name);
    };
  }
  d3_selectionPrototype.insert = function(name, before) {
    name = d3_selection_creator(name);
    before = d3_selection_selector(before);
    return this.select(function() {
      return this.insertBefore(name.apply(this, arguments), before.apply(this, arguments) || null);
    });
  };
  d3_selectionPrototype.remove = function() {
    return this.each(function() {
      var parent = this.parentNode;
      if (parent) parent.removeChild(this);
    });
  };
  d3_selectionPrototype.data = function(value, key) {
    var i = -1, n = this.length, group, node;
    if (!arguments.length) {
      value = new Array(n = (group = this[0]).length);
      while (++i < n) {
        if (node = group[i]) {
          value[i] = node.__data__;
        }
      }
      return value;
    }
    function bind(group, groupData) {
      var i, n = group.length, m = groupData.length, n0 = Math.min(n, m), updateNodes = new Array(m), enterNodes = new Array(m), exitNodes = new Array(n), node, nodeData;
      if (key) {
        var nodeByKeyValue = new d3_Map(), dataByKeyValue = new d3_Map(), keyValues = [], keyValue;
        for (i = -1; ++i < n; ) {
          keyValue = key.call(node = group[i], node.__data__, i);
          if (nodeByKeyValue.has(keyValue)) {
            exitNodes[i] = node;
          } else {
            nodeByKeyValue.set(keyValue, node);
          }
          keyValues.push(keyValue);
        }
        for (i = -1; ++i < m; ) {
          keyValue = key.call(groupData, nodeData = groupData[i], i);
          if (node = nodeByKeyValue.get(keyValue)) {
            updateNodes[i] = node;
            node.__data__ = nodeData;
          } else if (!dataByKeyValue.has(keyValue)) {
            enterNodes[i] = d3_selection_dataNode(nodeData);
          }
          dataByKeyValue.set(keyValue, nodeData);
          nodeByKeyValue.remove(keyValue);
        }
        for (i = -1; ++i < n; ) {
          if (nodeByKeyValue.has(keyValues[i])) {
            exitNodes[i] = group[i];
          }
        }
      } else {
        for (i = -1; ++i < n0; ) {
          node = group[i];
          nodeData = groupData[i];
          if (node) {
            node.__data__ = nodeData;
            updateNodes[i] = node;
          } else {
            enterNodes[i] = d3_selection_dataNode(nodeData);
          }
        }
        for (;i < m; ++i) {
          enterNodes[i] = d3_selection_dataNode(groupData[i]);
        }
        for (;i < n; ++i) {
          exitNodes[i] = group[i];
        }
      }
      enterNodes.update = updateNodes;
      enterNodes.parentNode = updateNodes.parentNode = exitNodes.parentNode = group.parentNode;
      enter.push(enterNodes);
      update.push(updateNodes);
      exit.push(exitNodes);
    }
    var enter = d3_selection_enter([]), update = d3_selection([]), exit = d3_selection([]);
    if (typeof value === "function") {
      while (++i < n) {
        bind(group = this[i], value.call(group, group.parentNode.__data__, i));
      }
    } else {
      while (++i < n) {
        bind(group = this[i], value);
      }
    }
    update.enter = function() {
      return enter;
    };
    update.exit = function() {
      return exit;
    };
    return update;
  };
  function d3_selection_dataNode(data) {
    return {
      __data__: data
    };
  }
  d3_selectionPrototype.datum = function(value) {
    return arguments.length ? this.property("__data__", value) : this.property("__data__");
  };
  d3_selectionPrototype.filter = function(filter) {
    var subgroups = [], subgroup, group, node;
    if (typeof filter !== "function") filter = d3_selection_filter(filter);
    for (var j = 0, m = this.length; j < m; j++) {
      subgroups.push(subgroup = []);
      subgroup.parentNode = (group = this[j]).parentNode;
      for (var i = 0, n = group.length; i < n; i++) {
        if ((node = group[i]) && filter.call(node, node.__data__, i, j)) {
          subgroup.push(node);
        }
      }
    }
    return d3_selection(subgroups);
  };
  function d3_selection_filter(selector) {
    return function() {
      return d3_selectMatches(this, selector);
    };
  }
  d3_selectionPrototype.order = function() {
    for (var j = -1, m = this.length; ++j < m; ) {
      for (var group = this[j], i = group.length - 1, next = group[i], node; --i >= 0; ) {
        if (node = group[i]) {
          if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next);
          next = node;
        }
      }
    }
    return this;
  };
  d3_selectionPrototype.sort = function(comparator) {
    comparator = d3_selection_sortComparator.apply(this, arguments);
    for (var j = -1, m = this.length; ++j < m; ) this[j].sort(comparator);
    return this.order();
  };
  function d3_selection_sortComparator(comparator) {
    if (!arguments.length) comparator = d3.ascending;
    return function(a, b) {
      return a && b ? comparator(a.__data__, b.__data__) : !a - !b;
    };
  }
  d3_selectionPrototype.each = function(callback) {
    return d3_selection_each(this, function(node, i, j) {
      callback.call(node, node.__data__, i, j);
    });
  };
  function d3_selection_each(groups, callback) {
    for (var j = 0, m = groups.length; j < m; j++) {
      for (var group = groups[j], i = 0, n = group.length, node; i < n; i++) {
        if (node = group[i]) callback(node, i, j);
      }
    }
    return groups;
  }
  d3_selectionPrototype.call = function(callback) {
    var args = d3_array(arguments);
    callback.apply(args[0] = this, args);
    return this;
  };
  d3_selectionPrototype.empty = function() {
    return !this.node();
  };
  d3_selectionPrototype.node = function() {
    for (var j = 0, m = this.length; j < m; j++) {
      for (var group = this[j], i = 0, n = group.length; i < n; i++) {
        var node = group[i];
        if (node) return node;
      }
    }
    return null;
  };
  d3_selectionPrototype.size = function() {
    var n = 0;
    this.each(function() {
      ++n;
    });
    return n;
  };
  function d3_selection_enter(selection) {
    d3_subclass(selection, d3_selection_enterPrototype);
    return selection;
  }
  var d3_selection_enterPrototype = [];
  d3.selection.enter = d3_selection_enter;
  d3.selection.enter.prototype = d3_selection_enterPrototype;
  d3_selection_enterPrototype.append = d3_selectionPrototype.append;
  d3_selection_enterPrototype.empty = d3_selectionPrototype.empty;
  d3_selection_enterPrototype.node = d3_selectionPrototype.node;
  d3_selection_enterPrototype.call = d3_selectionPrototype.call;
  d3_selection_enterPrototype.size = d3_selectionPrototype.size;
  d3_selection_enterPrototype.select = function(selector) {
    var subgroups = [], subgroup, subnode, upgroup, group, node;
    for (var j = -1, m = this.length; ++j < m; ) {
      upgroup = (group = this[j]).update;
      subgroups.push(subgroup = []);
      subgroup.parentNode = group.parentNode;
      for (var i = -1, n = group.length; ++i < n; ) {
        if (node = group[i]) {
          subgroup.push(upgroup[i] = subnode = selector.call(group.parentNode, node.__data__, i, j));
          subnode.__data__ = node.__data__;
        } else {
          subgroup.push(null);
        }
      }
    }
    return d3_selection(subgroups);
  };
  d3_selection_enterPrototype.insert = function(name, before) {
    if (arguments.length < 2) before = d3_selection_enterInsertBefore(this);
    return d3_selectionPrototype.insert.call(this, name, before);
  };
  function d3_selection_enterInsertBefore(enter) {
    var i0, j0;
    return function(d, i, j) {
      var group = enter[j].update, n = group.length, node;
      if (j != j0) j0 = j, i0 = 0;
      if (i >= i0) i0 = i + 1;
      while (!(node = group[i0]) && ++i0 < n) ;
      return node;
    };
  }
  d3_selectionPrototype.transition = function() {
    var id = d3_transitionInheritId || ++d3_transitionId, subgroups = [], subgroup, node, transition = d3_transitionInherit || {
      time: Date.now(),
      ease: d3_ease_cubicInOut,
      delay: 0,
      duration: 250
    };
    for (var j = -1, m = this.length; ++j < m; ) {
      subgroups.push(subgroup = []);
      for (var group = this[j], i = -1, n = group.length; ++i < n; ) {
        if (node = group[i]) d3_transitionNode(node, i, id, transition);
        subgroup.push(node);
      }
    }
    return d3_transition(subgroups, id);
  };
  d3_selectionPrototype.interrupt = function() {
    return this.each(d3_selection_interrupt);
  };
  function d3_selection_interrupt() {
    var lock = this.__transition__;
    if (lock) ++lock.active;
  }
  d3.select = function(node) {
    var group = [ typeof node === "string" ? d3_select(node, d3_document) : node ];
    group.parentNode = d3_documentElement;
    return d3_selection([ group ]);
  };
  d3.selectAll = function(nodes) {
    var group = d3_array(typeof nodes === "string" ? d3_selectAll(nodes, d3_document) : nodes);
    group.parentNode = d3_documentElement;
    return d3_selection([ group ]);
  };
  var d3_selectionRoot = d3.select(d3_documentElement);
  d3_selectionPrototype.on = function(type, listener, capture) {
    var n = arguments.length;
    if (n < 3) {
      if (typeof type !== "string") {
        if (n < 2) listener = false;
        for (capture in type) this.each(d3_selection_on(capture, type[capture], listener));
        return this;
      }
      if (n < 2) return (n = this.node()["__on" + type]) && n._;
      capture = false;
    }
    return this.each(d3_selection_on(type, listener, capture));
  };
  function d3_selection_on(type, listener, capture) {
    var name = "__on" + type, i = type.indexOf("."), wrap = d3_selection_onListener;
    if (i > 0) type = type.substring(0, i);
    var filter = d3_selection_onFilters.get(type);
    if (filter) type = filter, wrap = d3_selection_onFilter;
    function onRemove() {
      var l = this[name];
      if (l) {
        this.removeEventListener(type, l, l.$);
        delete this[name];
      }
    }
    function onAdd() {
      var l = wrap(listener, d3_array(arguments));
      onRemove.call(this);
      this.addEventListener(type, this[name] = l, l.$ = capture);
      l._ = listener;
    }
    function removeAll() {
      var re = new RegExp("^__on([^.]+)" + d3.requote(type) + "$"), match;
      for (var name in this) {
        if (match = name.match(re)) {
          var l = this[name];
          this.removeEventListener(match[1], l, l.$);
          delete this[name];
        }
      }
    }
    return i ? listener ? onAdd : onRemove : listener ? d3_noop : removeAll;
  }
  var d3_selection_onFilters = d3.map({
    mouseenter: "mouseover",
    mouseleave: "mouseout"
  });
  d3_selection_onFilters.forEach(function(k) {
    if ("on" + k in d3_document) d3_selection_onFilters.remove(k);
  });
  function d3_selection_onListener(listener, argumentz) {
    return function(e) {
      var o = d3.event;
      d3.event = e;
      argumentz[0] = this.__data__;
      try {
        listener.apply(this, argumentz);
      } finally {
        d3.event = o;
      }
    };
  }
  function d3_selection_onFilter(listener, argumentz) {
    var l = d3_selection_onListener(listener, argumentz);
    return function(e) {
      var target = this, related = e.relatedTarget;
      if (!related || related !== target && !(related.compareDocumentPosition(target) & 8)) {
        l.call(target, e);
      }
    };
  }
  var d3_event_dragSelect = "onselectstart" in d3_document ? null : d3_vendorSymbol(d3_documentElement.style, "userSelect"), d3_event_dragId = 0;
  function d3_event_dragSuppress() {
    var name = ".dragsuppress-" + ++d3_event_dragId, click = "click" + name, w = d3.select(d3_window).on("touchmove" + name, d3_eventPreventDefault).on("dragstart" + name, d3_eventPreventDefault).on("selectstart" + name, d3_eventPreventDefault);
    if (d3_event_dragSelect) {
      var style = d3_documentElement.style, select = style[d3_event_dragSelect];
      style[d3_event_dragSelect] = "none";
    }
    return function(suppressClick) {
      w.on(name, null);
      if (d3_event_dragSelect) style[d3_event_dragSelect] = select;
      if (suppressClick) {
        function off() {
          w.on(click, null);
        }
        w.on(click, function() {
          d3_eventPreventDefault();
          off();
        }, true);
        setTimeout(off, 0);
      }
    };
  }
  d3.mouse = function(container) {
    return d3_mousePoint(container, d3_eventSource());
  };
  var d3_mouse_bug44083 = /WebKit/.test(d3_window.navigator.userAgent) ? -1 : 0;
  function d3_mousePoint(container, e) {
    if (e.changedTouches) e = e.changedTouches[0];
    var svg = container.ownerSVGElement || container;
    if (svg.createSVGPoint) {
      var point = svg.createSVGPoint();
      if (d3_mouse_bug44083 < 0 && (d3_window.scrollX || d3_window.scrollY)) {
        svg = d3.select("body").append("svg").style({
          position: "absolute",
          top: 0,
          left: 0,
          margin: 0,
          padding: 0,
          border: "none"
        }, "important");
        var ctm = svg[0][0].getScreenCTM();
        d3_mouse_bug44083 = !(ctm.f || ctm.e);
        svg.remove();
      }
      if (d3_mouse_bug44083) point.x = e.pageX, point.y = e.pageY; else point.x = e.clientX, 
      point.y = e.clientY;
      point = point.matrixTransform(container.getScreenCTM().inverse());
      return [ point.x, point.y ];
    }
    var rect = container.getBoundingClientRect();
    return [ e.clientX - rect.left - container.clientLeft, e.clientY - rect.top - container.clientTop ];
  }
  d3.touches = function(container, touches) {
    if (arguments.length < 2) touches = d3_eventSource().touches;
    return touches ? d3_array(touches).map(function(touch) {
      var point = d3_mousePoint(container, touch);
      point.identifier = touch.identifier;
      return point;
    }) : [];
  };
  d3.behavior.drag = function() {
    var event = d3_eventDispatch(drag, "drag", "dragstart", "dragend"), origin = null, mousedown = dragstart(d3_noop, d3.mouse, "mousemove", "mouseup"), touchstart = dragstart(touchid, touchposition, "touchmove", "touchend");
    function drag() {
      this.on("mousedown.drag", mousedown).on("touchstart.drag", touchstart);
    }
    function touchid() {
      return d3.event.changedTouches[0].identifier;
    }
    function touchposition(parent, id) {
      return d3.touches(parent).filter(function(p) {
        return p.identifier === id;
      })[0];
    }
    function dragstart(id, position, move, end) {
      return function() {
        var target = this, parent = target.parentNode, event_ = event.of(target, arguments), eventTarget = d3.event.target, eventId = id(), drag = eventId == null ? "drag" : "drag-" + eventId, origin_ = position(parent, eventId), dragged = 0, offset, w = d3.select(d3_window).on(move + "." + drag, moved).on(end + "." + drag, ended), dragRestore = d3_event_dragSuppress();
        if (origin) {
          offset = origin.apply(target, arguments);
          offset = [ offset.x - origin_[0], offset.y - origin_[1] ];
        } else {
          offset = [ 0, 0 ];
        }
        event_({
          type: "dragstart"
        });
        function moved() {
          var p = position(parent, eventId), dx = p[0] - origin_[0], dy = p[1] - origin_[1];
          dragged |= dx | dy;
          origin_ = p;
          event_({
            type: "drag",
            x: p[0] + offset[0],
            y: p[1] + offset[1],
            dx: dx,
            dy: dy
          });
        }
        function ended() {
          w.on(move + "." + drag, null).on(end + "." + drag, null);
          dragRestore(dragged && d3.event.target === eventTarget);
          event_({
            type: "dragend"
          });
        }
      };
    }
    drag.origin = function(x) {
      if (!arguments.length) return origin;
      origin = x;
      return drag;
    };
    return d3.rebind(drag, event, "on");
  };
  var π = Math.PI, τ = 2 * π, halfπ = π / 2, ε = 1e-6, ε2 = ε * ε, d3_radians = π / 180, d3_degrees = 180 / π;
  function d3_sgn(x) {
    return x > 0 ? 1 : x < 0 ? -1 : 0;
  }
  function d3_cross2d(a, b, c) {
    return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);
  }
  function d3_acos(x) {
    return x > 1 ? 0 : x < -1 ? π : Math.acos(x);
  }
  function d3_asin(x) {
    return x > 1 ? halfπ : x < -1 ? -halfπ : Math.asin(x);
  }
  function d3_sinh(x) {
    return ((x = Math.exp(x)) - 1 / x) / 2;
  }
  function d3_cosh(x) {
    return ((x = Math.exp(x)) + 1 / x) / 2;
  }
  function d3_tanh(x) {
    return ((x = Math.exp(2 * x)) - 1) / (x + 1);
  }
  function d3_haversin(x) {
    return (x = Math.sin(x / 2)) * x;
  }
  var ρ = Math.SQRT2, ρ2 = 2, ρ4 = 4;
  d3.interpolateZoom = function(p0, p1) {
    var ux0 = p0[0], uy0 = p0[1], w0 = p0[2], ux1 = p1[0], uy1 = p1[1], w1 = p1[2];
    var dx = ux1 - ux0, dy = uy1 - uy0, d2 = dx * dx + dy * dy, d1 = Math.sqrt(d2), b0 = (w1 * w1 - w0 * w0 + ρ4 * d2) / (2 * w0 * ρ2 * d1), b1 = (w1 * w1 - w0 * w0 - ρ4 * d2) / (2 * w1 * ρ2 * d1), r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0), r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1), dr = r1 - r0, S = (dr || Math.log(w1 / w0)) / ρ;
    function interpolate(t) {
      var s = t * S;
      if (dr) {
        var coshr0 = d3_cosh(r0), u = w0 / (ρ2 * d1) * (coshr0 * d3_tanh(ρ * s + r0) - d3_sinh(r0));
        return [ ux0 + u * dx, uy0 + u * dy, w0 * coshr0 / d3_cosh(ρ * s + r0) ];
      }
      return [ ux0 + t * dx, uy0 + t * dy, w0 * Math.exp(ρ * s) ];
    }
    interpolate.duration = S * 1e3;
    return interpolate;
  };
  d3.behavior.zoom = function() {
    var view = {
      x: 0,
      y: 0,
      k: 1
    }, translate0, center, size = [ 960, 500 ], scaleExtent = d3_behavior_zoomInfinity, mousedown = "mousedown.zoom", mousemove = "mousemove.zoom", mouseup = "mouseup.zoom", mousewheelTimer, touchstart = "touchstart.zoom", touchtime, event = d3_eventDispatch(zoom, "zoomstart", "zoom", "zoomend"), x0, x1, y0, y1;
    function zoom(g) {
      g.on(mousedown, mousedowned).on(d3_behavior_zoomWheel + ".zoom", mousewheeled).on(mousemove, mousewheelreset).on("dblclick.zoom", dblclicked).on(touchstart, touchstarted);
    }
    zoom.event = function(g) {
      g.each(function() {
        var event_ = event.of(this, arguments), view1 = view;
        if (d3_transitionInheritId) {
          d3.select(this).transition().each("start.zoom", function() {
            view = this.__chart__ || {
              x: 0,
              y: 0,
              k: 1
            };
            zoomstarted(event_);
          }).tween("zoom:zoom", function() {
            var dx = size[0], dy = size[1], cx = dx / 2, cy = dy / 2, i = d3.interpolateZoom([ (cx - view.x) / view.k, (cy - view.y) / view.k, dx / view.k ], [ (cx - view1.x) / view1.k, (cy - view1.y) / view1.k, dx / view1.k ]);
            return function(t) {
              var l = i(t), k = dx / l[2];
              this.__chart__ = view = {
                x: cx - l[0] * k,
                y: cy - l[1] * k,
                k: k
              };
              zoomed(event_);
            };
          }).each("end.zoom", function() {
            zoomended(event_);
          });
        } else {
          this.__chart__ = view;
          zoomstarted(event_);
          zoomed(event_);
          zoomended(event_);
        }
      });
    };
    zoom.translate = function(_) {
      if (!arguments.length) return [ view.x, view.y ];
      view = {
        x: +_[0],
        y: +_[1],
        k: view.k
      };
      rescale();
      return zoom;
    };
    zoom.scale = function(_) {
      if (!arguments.length) return view.k;
      view = {
        x: view.x,
        y: view.y,
        k: +_
      };
      rescale();
      return zoom;
    };
    zoom.scaleExtent = function(_) {
      if (!arguments.length) return scaleExtent;
      scaleExtent = _ == null ? d3_behavior_zoomInfinity : [ +_[0], +_[1] ];
      return zoom;
    };
    zoom.center = function(_) {
      if (!arguments.length) return center;
      center = _ && [ +_[0], +_[1] ];
      return zoom;
    };
    zoom.size = function(_) {
      if (!arguments.length) return size;
      size = _ && [ +_[0], +_[1] ];
      return zoom;
    };
    zoom.x = function(z) {
      if (!arguments.length) return x1;
      x1 = z;
      x0 = z.copy();
      view = {
        x: 0,
        y: 0,
        k: 1
      };
      return zoom;
    };
    zoom.y = function(z) {
      if (!arguments.length) return y1;
      y1 = z;
      y0 = z.copy();
      view = {
        x: 0,
        y: 0,
        k: 1
      };
      return zoom;
    };
    function location(p) {
      return [ (p[0] - view.x) / view.k, (p[1] - view.y) / view.k ];
    }
    function point(l) {
      return [ l[0] * view.k + view.x, l[1] * view.k + view.y ];
    }
    function scaleTo(s) {
      view.k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], s));
    }
    function translateTo(p, l) {
      l = point(l);
      view.x += p[0] - l[0];
      view.y += p[1] - l[1];
    }
    function rescale() {
      if (x1) x1.domain(x0.range().map(function(x) {
        return (x - view.x) / view.k;
      }).map(x0.invert));
      if (y1) y1.domain(y0.range().map(function(y) {
        return (y - view.y) / view.k;
      }).map(y0.invert));
    }
    function zoomstarted(event) {
      event({
        type: "zoomstart"
      });
    }
    function zoomed(event) {
      rescale();
      event({
        type: "zoom",
        scale: view.k,
        translate: [ view.x, view.y ]
      });
    }
    function zoomended(event) {
      event({
        type: "zoomend"
      });
    }
    function mousedowned() {
      var target = this, event_ = event.of(target, arguments), eventTarget = d3.event.target, dragged = 0, w = d3.select(d3_window).on(mousemove, moved).on(mouseup, ended), l = location(d3.mouse(target)), dragRestore = d3_event_dragSuppress();
      d3_selection_interrupt.call(target);
      zoomstarted(event_);
      function moved() {
        dragged = 1;
        translateTo(d3.mouse(target), l);
        zoomed(event_);
      }
      function ended() {
        w.on(mousemove, d3_window === target ? mousewheelreset : null).on(mouseup, null);
        dragRestore(dragged && d3.event.target === eventTarget);
        zoomended(event_);
      }
    }
    function touchstarted() {
      var target = this, event_ = event.of(target, arguments), locations0 = {}, distance0 = 0, scale0, eventId = d3.event.changedTouches[0].identifier, touchmove = "touchmove.zoom-" + eventId, touchend = "touchend.zoom-" + eventId, w = d3.select(d3_window).on(touchmove, moved).on(touchend, ended), t = d3.select(target).on(mousedown, null).on(touchstart, started), dragRestore = d3_event_dragSuppress();
      d3_selection_interrupt.call(target);
      started();
      zoomstarted(event_);
      function relocate() {
        var touches = d3.touches(target);
        scale0 = view.k;
        touches.forEach(function(t) {
          if (t.identifier in locations0) locations0[t.identifier] = location(t);
        });
        return touches;
      }
      function started() {
        var changed = d3.event.changedTouches;
        for (var i = 0, n = changed.length; i < n; ++i) {
          locations0[changed[i].identifier] = null;
        }
        var touches = relocate(), now = Date.now();
        if (touches.length === 1) {
          if (now - touchtime < 500) {
            var p = touches[0], l = locations0[p.identifier];
            scaleTo(view.k * 2);
            translateTo(p, l);
            d3_eventPreventDefault();
            zoomed(event_);
          }
          touchtime = now;
        } else if (touches.length > 1) {
          var p = touches[0], q = touches[1], dx = p[0] - q[0], dy = p[1] - q[1];
          distance0 = dx * dx + dy * dy;
        }
      }
      function moved() {
        var touches = d3.touches(target), p0, l0, p1, l1;
        for (var i = 0, n = touches.length; i < n; ++i, l1 = null) {
          p1 = touches[i];
          if (l1 = locations0[p1.identifier]) {
            if (l0) break;
            p0 = p1, l0 = l1;
          }
        }
        if (l1) {
          var distance1 = (distance1 = p1[0] - p0[0]) * distance1 + (distance1 = p1[1] - p0[1]) * distance1, scale1 = distance0 && Math.sqrt(distance1 / distance0);
          p0 = [ (p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2 ];
          l0 = [ (l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2 ];
          scaleTo(scale1 * scale0);
        }
        touchtime = null;
        translateTo(p0, l0);
        zoomed(event_);
      }
      function ended() {
        if (d3.event.touches.length) {
          var changed = d3.event.changedTouches;
          for (var i = 0, n = changed.length; i < n; ++i) {
            delete locations0[changed[i].identifier];
          }
          for (var identifier in locations0) {
            return void relocate();
          }
        }
        w.on(touchmove, null).on(touchend, null);
        t.on(mousedown, mousedowned).on(touchstart, touchstarted);
        dragRestore();
        zoomended(event_);
      }
    }
    function mousewheeled() {
      var event_ = event.of(this, arguments);
      if (mousewheelTimer) clearTimeout(mousewheelTimer); else d3_selection_interrupt.call(this), 
      zoomstarted(event_);
      mousewheelTimer = setTimeout(function() {
        mousewheelTimer = null;
        zoomended(event_);
      }, 50);
      d3_eventPreventDefault();
      var point = center || d3.mouse(this);
      if (!translate0) translate0 = location(point);
      scaleTo(Math.pow(2, d3_behavior_zoomDelta() * .002) * view.k);
      translateTo(point, translate0);
      zoomed(event_);
    }
    function mousewheelreset() {
      translate0 = null;
    }
    function dblclicked() {
      var event_ = event.of(this, arguments), p = d3.mouse(this), l = location(p), k = Math.log(view.k) / Math.LN2;
      zoomstarted(event_);
      scaleTo(Math.pow(2, d3.event.shiftKey ? Math.ceil(k) - 1 : Math.floor(k) + 1));
      translateTo(p, l);
      zoomed(event_);
      zoomended(event_);
    }
    return d3.rebind(zoom, event, "on");
  };
  var d3_behavior_zoomInfinity = [ 0, Infinity ];
  var d3_behavior_zoomDelta, d3_behavior_zoomWheel = "onwheel" in d3_document ? (d3_behavior_zoomDelta = function() {
    return -d3.event.deltaY * (d3.event.deltaMode ? 120 : 1);
  }, "wheel") : "onmousewheel" in d3_document ? (d3_behavior_zoomDelta = function() {
    return d3.event.wheelDelta;
  }, "mousewheel") : (d3_behavior_zoomDelta = function() {
    return -d3.event.detail;
  }, "MozMousePixelScroll");
  function d3_Color() {}
  d3_Color.prototype.toString = function() {
    return this.rgb() + "";
  };
  d3.hsl = function(h, s, l) {
    return arguments.length === 1 ? h instanceof d3_Hsl ? d3_hsl(h.h, h.s, h.l) : d3_rgb_parse("" + h, d3_rgb_hsl, d3_hsl) : d3_hsl(+h, +s, +l);
  };
  function d3_hsl(h, s, l) {
    return new d3_Hsl(h, s, l);
  }
  function d3_Hsl(h, s, l) {
    this.h = h;
    this.s = s;
    this.l = l;
  }
  var d3_hslPrototype = d3_Hsl.prototype = new d3_Color();
  d3_hslPrototype.brighter = function(k) {
    k = Math.pow(.7, arguments.length ? k : 1);
    return d3_hsl(this.h, this.s, this.l / k);
  };
  d3_hslPrototype.darker = function(k) {
    k = Math.pow(.7, arguments.length ? k : 1);
    return d3_hsl(this.h, this.s, k * this.l);
  };
  d3_hslPrototype.rgb = function() {
    return d3_hsl_rgb(this.h, this.s, this.l);
  };
  function d3_hsl_rgb(h, s, l) {
    var m1, m2;
    h = isNaN(h) ? 0 : (h %= 360) < 0 ? h + 360 : h;
    s = isNaN(s) ? 0 : s < 0 ? 0 : s > 1 ? 1 : s;
    l = l < 0 ? 0 : l > 1 ? 1 : l;
    m2 = l <= .5 ? l * (1 + s) : l + s - l * s;
    m1 = 2 * l - m2;
    function v(h) {
      if (h > 360) h -= 360; else if (h < 0) h += 360;
      if (h < 60) return m1 + (m2 - m1) * h / 60;
      if (h < 180) return m2;
      if (h < 240) return m1 + (m2 - m1) * (240 - h) / 60;
      return m1;
    }
    function vv(h) {
      return Math.round(v(h) * 255);
    }
    return d3_rgb(vv(h + 120), vv(h), vv(h - 120));
  }
  d3.hcl = function(h, c, l) {
    return arguments.length === 1 ? h instanceof d3_Hcl ? d3_hcl(h.h, h.c, h.l) : h instanceof d3_Lab ? d3_lab_hcl(h.l, h.a, h.b) : d3_lab_hcl((h = d3_rgb_lab((h = d3.rgb(h)).r, h.g, h.b)).l, h.a, h.b) : d3_hcl(+h, +c, +l);
  };
  function d3_hcl(h, c, l) {
    return new d3_Hcl(h, c, l);
  }
  function d3_Hcl(h, c, l) {
    this.h = h;
    this.c = c;
    this.l = l;
  }
  var d3_hclPrototype = d3_Hcl.prototype = new d3_Color();
  d3_hclPrototype.brighter = function(k) {
    return d3_hcl(this.h, this.c, Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)));
  };
  d3_hclPrototype.darker = function(k) {
    return d3_hcl(this.h, this.c, Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)));
  };
  d3_hclPrototype.rgb = function() {
    return d3_hcl_lab(this.h, this.c, this.l).rgb();
  };
  function d3_hcl_lab(h, c, l) {
    if (isNaN(h)) h = 0;
    if (isNaN(c)) c = 0;
    return d3_lab(l, Math.cos(h *= d3_radians) * c, Math.sin(h) * c);
  }
  d3.lab = function(l, a, b) {
    return arguments.length === 1 ? l instanceof d3_Lab ? d3_lab(l.l, l.a, l.b) : l instanceof d3_Hcl ? d3_hcl_lab(l.l, l.c, l.h) : d3_rgb_lab((l = d3.rgb(l)).r, l.g, l.b) : d3_lab(+l, +a, +b);
  };
  function d3_lab(l, a, b) {
    return new d3_Lab(l, a, b);
  }
  function d3_Lab(l, a, b) {
    this.l = l;
    this.a = a;
    this.b = b;
  }
  var d3_lab_K = 18;
  var d3_lab_X = .95047, d3_lab_Y = 1, d3_lab_Z = 1.08883;
  var d3_labPrototype = d3_Lab.prototype = new d3_Color();
  d3_labPrototype.brighter = function(k) {
    return d3_lab(Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)), this.a, this.b);
  };
  d3_labPrototype.darker = function(k) {
    return d3_lab(Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)), this.a, this.b);
  };
  d3_labPrototype.rgb = function() {
    return d3_lab_rgb(this.l, this.a, this.b);
  };
  function d3_lab_rgb(l, a, b) {
    var y = (l + 16) / 116, x = y + a / 500, z = y - b / 200;
    x = d3_lab_xyz(x) * d3_lab_X;
    y = d3_lab_xyz(y) * d3_lab_Y;
    z = d3_lab_xyz(z) * d3_lab_Z;
    return d3_rgb(d3_xyz_rgb(3.2404542 * x - 1.5371385 * y - .4985314 * z), d3_xyz_rgb(-.969266 * x + 1.8760108 * y + .041556 * z), d3_xyz_rgb(.0556434 * x - .2040259 * y + 1.0572252 * z));
  }
  function d3_lab_hcl(l, a, b) {
    return l > 0 ? d3_hcl(Math.atan2(b, a) * d3_degrees, Math.sqrt(a * a + b * b), l) : d3_hcl(NaN, NaN, l);
  }
  function d3_lab_xyz(x) {
    return x > .206893034 ? x * x * x : (x - 4 / 29) / 7.787037;
  }
  function d3_xyz_lab(x) {
    return x > .008856 ? Math.pow(x, 1 / 3) : 7.787037 * x + 4 / 29;
  }
  function d3_xyz_rgb(r) {
    return Math.round(255 * (r <= .00304 ? 12.92 * r : 1.055 * Math.pow(r, 1 / 2.4) - .055));
  }
  d3.rgb = function(r, g, b) {
    return arguments.length === 1 ? r instanceof d3_Rgb ? d3_rgb(r.r, r.g, r.b) : d3_rgb_parse("" + r, d3_rgb, d3_hsl_rgb) : d3_rgb(~~r, ~~g, ~~b);
  };
  function d3_rgbNumber(value) {
    return d3_rgb(value >> 16, value >> 8 & 255, value & 255);
  }
  function d3_rgbString(value) {
    return d3_rgbNumber(value) + "";
  }
  function d3_rgb(r, g, b) {
    return new d3_Rgb(r, g, b);
  }
  function d3_Rgb(r, g, b) {
    this.r = r;
    this.g = g;
    this.b = b;
  }
  var d3_rgbPrototype = d3_Rgb.prototype = new d3_Color();
  d3_rgbPrototype.brighter = function(k) {
    k = Math.pow(.7, arguments.length ? k : 1);
    var r = this.r, g = this.g, b = this.b, i = 30;
    if (!r && !g && !b) return d3_rgb(i, i, i);
    if (r && r < i) r = i;
    if (g && g < i) g = i;
    if (b && b < i) b = i;
    return d3_rgb(Math.min(255, ~~(r / k)), Math.min(255, ~~(g / k)), Math.min(255, ~~(b / k)));
  };
  d3_rgbPrototype.darker = function(k) {
    k = Math.pow(.7, arguments.length ? k : 1);
    return d3_rgb(~~(k * this.r), ~~(k * this.g), ~~(k * this.b));
  };
  d3_rgbPrototype.hsl = function() {
    return d3_rgb_hsl(this.r, this.g, this.b);
  };
  d3_rgbPrototype.toString = function() {
    return "#" + d3_rgb_hex(this.r) + d3_rgb_hex(this.g) + d3_rgb_hex(this.b);
  };
  function d3_rgb_hex(v) {
    return v < 16 ? "0" + Math.max(0, v).toString(16) : Math.min(255, v).toString(16);
  }
  function d3_rgb_parse(format, rgb, hsl) {
    var r = 0, g = 0, b = 0, m1, m2, name;
    m1 = /([a-z]+)\((.*)\)/i.exec(format);
    if (m1) {
      m2 = m1[2].split(",");
      switch (m1[1]) {
       case "hsl":
        {
          return hsl(parseFloat(m2[0]), parseFloat(m2[1]) / 100, parseFloat(m2[2]) / 100);
        }

       case "rgb":
        {
          return rgb(d3_rgb_parseNumber(m2[0]), d3_rgb_parseNumber(m2[1]), d3_rgb_parseNumber(m2[2]));
        }
      }
    }
    if (name = d3_rgb_names.get(format)) return rgb(name.r, name.g, name.b);
    if (format != null && format.charAt(0) === "#") {
      if (format.length === 4) {
        r = format.charAt(1);
        r += r;
        g = format.charAt(2);
        g += g;
        b = format.charAt(3);
        b += b;
      } else if (format.length === 7) {
        r = format.substring(1, 3);
        g = format.substring(3, 5);
        b = format.substring(5, 7);
      }
      r = parseInt(r, 16);
      g = parseInt(g, 16);
      b = parseInt(b, 16);
    }
    return rgb(r, g, b);
  }
  function d3_rgb_hsl(r, g, b) {
    var min = Math.min(r /= 255, g /= 255, b /= 255), max = Math.max(r, g, b), d = max - min, h, s, l = (max + min) / 2;
    if (d) {
      s = l < .5 ? d / (max + min) : d / (2 - max - min);
      if (r == max) h = (g - b) / d + (g < b ? 6 : 0); else if (g == max) h = (b - r) / d + 2; else h = (r - g) / d + 4;
      h *= 60;
    } else {
      h = NaN;
      s = l > 0 && l < 1 ? 0 : h;
    }
    return d3_hsl(h, s, l);
  }
  function d3_rgb_lab(r, g, b) {
    r = d3_rgb_xyz(r);
    g = d3_rgb_xyz(g);
    b = d3_rgb_xyz(b);
    var x = d3_xyz_lab((.4124564 * r + .3575761 * g + .1804375 * b) / d3_lab_X), y = d3_xyz_lab((.2126729 * r + .7151522 * g + .072175 * b) / d3_lab_Y), z = d3_xyz_lab((.0193339 * r + .119192 * g + .9503041 * b) / d3_lab_Z);
    return d3_lab(116 * y - 16, 500 * (x - y), 200 * (y - z));
  }
  function d3_rgb_xyz(r) {
    return (r /= 255) <= .04045 ? r / 12.92 : Math.pow((r + .055) / 1.055, 2.4);
  }
  function d3_rgb_parseNumber(c) {
    var f = parseFloat(c);
    return c.charAt(c.length - 1) === "%" ? Math.round(f * 2.55) : f;
  }
  var d3_rgb_names = d3.map({
    aliceblue: 15792383,
    antiquewhite: 16444375,
    aqua: 65535,
    aquamarine: 8388564,
    azure: 15794175,
    beige: 16119260,
    bisque: 16770244,
    black: 0,
    blanchedalmond: 16772045,
    blue: 255,
    blueviolet: 9055202,
    brown: 10824234,
    burlywood: 14596231,
    cadetblue: 6266528,
    chartreuse: 8388352,
    chocolate: 13789470,
    coral: 16744272,
    cornflowerblue: 6591981,
    cornsilk: 16775388,
    crimson: 14423100,
    cyan: 65535,
    darkblue: 139,
    darkcyan: 35723,
    darkgoldenrod: 12092939,
    darkgray: 11119017,
    darkgreen: 25600,
    darkgrey: 11119017,
    darkkhaki: 12433259,
    darkmagenta: 9109643,
    darkolivegreen: 5597999,
    darkorange: 16747520,
    darkorchid: 10040012,
    darkred: 9109504,
    darksalmon: 15308410,
    darkseagreen: 9419919,
    darkslateblue: 4734347,
    darkslategray: 3100495,
    darkslategrey: 3100495,
    darkturquoise: 52945,
    darkviolet: 9699539,
    deeppink: 16716947,
    deepskyblue: 49151,
    dimgray: 6908265,
    dimgrey: 6908265,
    dodgerblue: 2003199,
    firebrick: 11674146,
    floralwhite: 16775920,
    forestgreen: 2263842,
    fuchsia: 16711935,
    gainsboro: 14474460,
    ghostwhite: 16316671,
    gold: 16766720,
    goldenrod: 14329120,
    gray: 8421504,
    green: 32768,
    greenyellow: 11403055,
    grey: 8421504,
    honeydew: 15794160,
    hotpink: 16738740,
    indianred: 13458524,
    indigo: 4915330,
    ivory: 16777200,
    khaki: 15787660,
    lavender: 15132410,
    lavenderblush: 16773365,
    lawngreen: 8190976,
    lemonchiffon: 16775885,
    lightblue: 11393254,
    lightcoral: 15761536,
    lightcyan: 14745599,
    lightgoldenrodyellow: 16448210,
    lightgray: 13882323,
    lightgreen: 9498256,
    lightgrey: 13882323,
    lightpink: 16758465,
    lightsalmon: 16752762,
    lightseagreen: 2142890,
    lightskyblue: 8900346,
    lightslategray: 7833753,
    lightslategrey: 7833753,
    lightsteelblue: 11584734,
    lightyellow: 16777184,
    lime: 65280,
    limegreen: 3329330,
    linen: 16445670,
    magenta: 16711935,
    maroon: 8388608,
    mediumaquamarine: 6737322,
    mediumblue: 205,
    mediumorchid: 12211667,
    mediumpurple: 9662683,
    mediumseagreen: 3978097,
    mediumslateblue: 8087790,
    mediumspringgreen: 64154,
    mediumturquoise: 4772300,
    mediumvioletred: 13047173,
    midnightblue: 1644912,
    mintcream: 16121850,
    mistyrose: 16770273,
    moccasin: 16770229,
    navajowhite: 16768685,
    navy: 128,
    oldlace: 16643558,
    olive: 8421376,
    olivedrab: 7048739,
    orange: 16753920,
    orangered: 16729344,
    orchid: 14315734,
    palegoldenrod: 15657130,
    palegreen: 10025880,
    paleturquoise: 11529966,
    palevioletred: 14381203,
    papayawhip: 16773077,
    peachpuff: 16767673,
    peru: 13468991,
    pink: 16761035,
    plum: 14524637,
    powderblue: 11591910,
    purple: 8388736,
    red: 16711680,
    rosybrown: 12357519,
    royalblue: 4286945,
    saddlebrown: 9127187,
    salmon: 16416882,
    sandybrown: 16032864,
    seagreen: 3050327,
    seashell: 16774638,
    sienna: 10506797,
    silver: 12632256,
    skyblue: 8900331,
    slateblue: 6970061,
    slategray: 7372944,
    slategrey: 7372944,
    snow: 16775930,
    springgreen: 65407,
    steelblue: 4620980,
    tan: 13808780,
    teal: 32896,
    thistle: 14204888,
    tomato: 16737095,
    turquoise: 4251856,
    violet: 15631086,
    wheat: 16113331,
    white: 16777215,
    whitesmoke: 16119285,
    yellow: 16776960,
    yellowgreen: 10145074
  });
  d3_rgb_names.forEach(function(key, value) {
    d3_rgb_names.set(key, d3_rgbNumber(value));
  });
  function d3_functor(v) {
    return typeof v === "function" ? v : function() {
      return v;
    };
  }
  d3.functor = d3_functor;
  function d3_identity(d) {
    return d;
  }
  d3.xhr = d3_xhrType(d3_identity);
  function d3_xhrType(response) {
    return function(url, mimeType, callback) {
      if (arguments.length === 2 && typeof mimeType === "function") callback = mimeType, 
      mimeType = null;
      return d3_xhr(url, mimeType, response, callback);
    };
  }
  function d3_xhr(url, mimeType, response, callback) {
    var xhr = {}, dispatch = d3.dispatch("beforesend", "progress", "load", "error"), headers = {}, request = new XMLHttpRequest(), responseType = null;
    if (d3_window.XDomainRequest && !("withCredentials" in request) && /^(http(s)?:)?\/\//.test(url)) request = new XDomainRequest();
    "onload" in request ? request.onload = request.onerror = respond : request.onreadystatechange = function() {
      request.readyState > 3 && respond();
    };
    function respond() {
      var status = request.status, result;
      if (!status && request.responseText || status >= 200 && status < 300 || status === 304) {
        try {
          result = response.call(xhr, request);
        } catch (e) {
          dispatch.error.call(xhr, e);
          return;
        }
        dispatch.load.call(xhr, result);
      } else {
        dispatch.error.call(xhr, request);
      }
    }
    request.onprogress = function(event) {
      var o = d3.event;
      d3.event = event;
      try {
        dispatch.progress.call(xhr, request);
      } finally {
        d3.event = o;
      }
    };
    xhr.header = function(name, value) {
      name = (name + "").toLowerCase();
      if (arguments.length < 2) return headers[name];
      if (value == null) delete headers[name]; else headers[name] = value + "";
      return xhr;
    };
    xhr.mimeType = function(value) {
      if (!arguments.length) return mimeType;
      mimeType = value == null ? null : value + "";
      return xhr;
    };
    xhr.responseType = function(value) {
      if (!arguments.length) return responseType;
      responseType = value;
      return xhr;
    };
    xhr.response = function(value) {
      response = value;
      return xhr;
    };
    [ "get", "post" ].forEach(function(method) {
      xhr[method] = function() {
        return xhr.send.apply(xhr, [ method ].concat(d3_array(arguments)));
      };
    });
    xhr.send = function(method, data, callback) {
      if (arguments.length === 2 && typeof data === "function") callback = data, data = null;
      request.open(method, url, true);
      if (mimeType != null && !("accept" in headers)) headers["accept"] = mimeType + ",*/*";
      if (request.setRequestHeader) for (var name in headers) request.setRequestHeader(name, headers[name]);
      if (mimeType != null && request.overrideMimeType) request.overrideMimeType(mimeType);
      if (responseType != null) request.responseType = responseType;
      if (callback != null) xhr.on("error", callback).on("load", function(request) {
        callback(null, request);
      });
      dispatch.beforesend.call(xhr, request);
      request.send(data == null ? null : data);
      return xhr;
    };
    xhr.abort = function() {
      request.abort();
      return xhr;
    };
    d3.rebind(xhr, dispatch, "on");
    return callback == null ? xhr : xhr.get(d3_xhr_fixCallback(callback));
  }
  function d3_xhr_fixCallback(callback) {
    return callback.length === 1 ? function(error, request) {
      callback(error == null ? request : null);
    } : callback;
  }
  d3.dsv = function(delimiter, mimeType) {
    var reFormat = new RegExp('["' + delimiter + "\n]"), delimiterCode = delimiter.charCodeAt(0);
    function dsv(url, row, callback) {
      if (arguments.length < 3) callback = row, row = null;
      var xhr = d3_xhr(url, mimeType, row == null ? response : typedResponse(row), callback);
      xhr.row = function(_) {
        return arguments.length ? xhr.response((row = _) == null ? response : typedResponse(_)) : row;
      };
      return xhr;
    }
    function response(request) {
      return dsv.parse(request.responseText);
    }
    function typedResponse(f) {
      return function(request) {
        return dsv.parse(request.responseText, f);
      };
    }
    dsv.parse = function(text, f) {
      var o;
      return dsv.parseRows(text, function(row, i) {
        if (o) return o(row, i - 1);
        var a = new Function("d", "return {" + row.map(function(name, i) {
          return JSON.stringify(name) + ": d[" + i + "]";
        }).join(",") + "}");
        o = f ? function(row, i) {
          return f(a(row), i);
        } : a;
      });
    };
    dsv.parseRows = function(text, f) {
      var EOL = {}, EOF = {}, rows = [], N = text.length, I = 0, n = 0, t, eol;
      function token() {
        if (I >= N) return EOF;
        if (eol) return eol = false, EOL;
        var j = I;
        if (text.charCodeAt(j) === 34) {
          var i = j;
          while (i++ < N) {
            if (text.charCodeAt(i) === 34) {
              if (text.charCodeAt(i + 1) !== 34) break;
              ++i;
            }
          }
          I = i + 2;
          var c = text.charCodeAt(i + 1);
          if (c === 13) {
            eol = true;
            if (text.charCodeAt(i + 2) === 10) ++I;
          } else if (c === 10) {
            eol = true;
          }
          return text.substring(j + 1, i).replace(/""/g, '"');
        }
        while (I < N) {
          var c = text.charCodeAt(I++), k = 1;
          if (c === 10) eol = true; else if (c === 13) {
            eol = true;
            if (text.charCodeAt(I) === 10) ++I, ++k;
          } else if (c !== delimiterCode) continue;
          return text.substring(j, I - k);
        }
        return text.substring(j);
      }
      while ((t = token()) !== EOF) {
        var a = [];
        while (t !== EOL && t !== EOF) {
          a.push(t);
          t = token();
        }
        if (f && !(a = f(a, n++))) continue;
        rows.push(a);
      }
      return rows;
    };
    dsv.format = function(rows) {
      if (Array.isArray(rows[0])) return dsv.formatRows(rows);
      var fieldSet = new d3_Set(), fields = [];
      rows.forEach(function(row) {
        for (var field in row) {
          if (!fieldSet.has(field)) {
            fields.push(fieldSet.add(field));
          }
        }
      });
      return [ fields.map(formatValue).join(delimiter) ].concat(rows.map(function(row) {
        return fields.map(function(field) {
          return formatValue(row[field]);
        }).join(delimiter);
      })).join("\n");
    };
    dsv.formatRows = function(rows) {
      return rows.map(formatRow).join("\n");
    };
    function formatRow(row) {
      return row.map(formatValue).join(delimiter);
    }
    function formatValue(text) {
      return reFormat.test(text) ? '"' + text.replace(/\"/g, '""') + '"' : text;
    }
    return dsv;
  };
  d3.csv = d3.dsv(",", "text/csv");
  d3.tsv = d3.dsv("	", "text/tab-separated-values");
  var d3_timer_queueHead, d3_timer_queueTail, d3_timer_interval, d3_timer_timeout, d3_timer_active, d3_timer_frame = d3_window[d3_vendorSymbol(d3_window, "requestAnimationFrame")] || function(callback) {
    setTimeout(callback, 17);
  };
  d3.timer = function(callback, delay, then) {
    var n = arguments.length;
    if (n < 2) delay = 0;
    if (n < 3) then = Date.now();
    var time = then + delay, timer = {
      c: callback,
      t: time,
      f: false,
      n: null
    };
    if (d3_timer_queueTail) d3_timer_queueTail.n = timer; else d3_timer_queueHead = timer;
    d3_timer_queueTail = timer;
    if (!d3_timer_interval) {
      d3_timer_timeout = clearTimeout(d3_timer_timeout);
      d3_timer_interval = 1;
      d3_timer_frame(d3_timer_step);
    }
  };
  function d3_timer_step() {
    var now = d3_timer_mark(), delay = d3_timer_sweep() - now;
    if (delay > 24) {
      if (isFinite(delay)) {
        clearTimeout(d3_timer_timeout);
        d3_timer_timeout = setTimeout(d3_timer_step, delay);
      }
      d3_timer_interval = 0;
    } else {
      d3_timer_interval = 1;
      d3_timer_frame(d3_timer_step);
    }
  }
  d3.timer.flush = function() {
    d3_timer_mark();
    d3_timer_sweep();
  };
  function d3_timer_mark() {
    var now = Date.now();
    d3_timer_active = d3_timer_queueHead;
    while (d3_timer_active) {
      if (now >= d3_timer_active.t) d3_timer_active.f = d3_timer_active.c(now - d3_timer_active.t);
      d3_timer_active = d3_timer_active.n;
    }
    return now;
  }
  function d3_timer_sweep() {
    var t0, t1 = d3_timer_queueHead, time = Infinity;
    while (t1) {
      if (t1.f) {
        t1 = t0 ? t0.n = t1.n : d3_timer_queueHead = t1.n;
      } else {
        if (t1.t < time) time = t1.t;
        t1 = (t0 = t1).n;
      }
    }
    d3_timer_queueTail = t0;
    return time;
  }
  function d3_format_precision(x, p) {
    return p - (x ? Math.ceil(Math.log(x) / Math.LN10) : 1);
  }
  d3.round = function(x, n) {
    return n ? Math.round(x * (n = Math.pow(10, n))) / n : Math.round(x);
  };
  var d3_formatPrefixes = [ "y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y" ].map(d3_formatPrefix);
  d3.formatPrefix = function(value, precision) {
    var i = 0;
    if (value) {
      if (value < 0) value *= -1;
      if (precision) value = d3.round(value, d3_format_precision(value, precision));
      i = 1 + Math.floor(1e-12 + Math.log(value) / Math.LN10);
      i = Math.max(-24, Math.min(24, Math.floor((i <= 0 ? i + 1 : i - 1) / 3) * 3));
    }
    return d3_formatPrefixes[8 + i / 3];
  };
  function d3_formatPrefix(d, i) {
    var k = Math.pow(10, abs(8 - i) * 3);
    return {
      scale: i > 8 ? function(d) {
        return d / k;
      } : function(d) {
        return d * k;
      },
      symbol: d
    };
  }
  function d3_locale_numberFormat(locale) {
    var locale_decimal = locale.decimal, locale_thousands = locale.thousands, locale_grouping = locale.grouping, locale_currency = locale.currency, formatGroup = locale_grouping ? function(value) {
      var i = value.length, t = [], j = 0, g = locale_grouping[0];
      while (i > 0 && g > 0) {
        t.push(value.substring(i -= g, i + g));
        g = locale_grouping[j = (j + 1) % locale_grouping.length];
      }
      return t.reverse().join(locale_thousands);
    } : d3_identity;
    return function(specifier) {
      var match = d3_format_re.exec(specifier), fill = match[1] || " ", align = match[2] || ">", sign = match[3] || "", symbol = match[4] || "", zfill = match[5], width = +match[6], comma = match[7], precision = match[8], type = match[9], scale = 1, prefix = "", suffix = "", integer = false;
      if (precision) precision = +precision.substring(1);
      if (zfill || fill === "0" && align === "=") {
        zfill = fill = "0";
        align = "=";
        if (comma) width -= Math.floor((width - 1) / 4);
      }
      switch (type) {
       case "n":
        comma = true;
        type = "g";
        break;

       case "%":
        scale = 100;
        suffix = "%";
        type = "f";
        break;

       case "p":
        scale = 100;
        suffix = "%";
        type = "r";
        break;

       case "b":
       case "o":
       case "x":
       case "X":
        if (symbol === "#") prefix = "0" + type.toLowerCase();

       case "c":
       case "d":
        integer = true;
        precision = 0;
        break;

       case "s":
        scale = -1;
        type = "r";
        break;
      }
      if (symbol === "$") prefix = locale_currency[0], suffix = locale_currency[1];
      if (type == "r" && !precision) type = "g";
      if (precision != null) {
        if (type == "g") precision = Math.max(1, Math.min(21, precision)); else if (type == "e" || type == "f") precision = Math.max(0, Math.min(20, precision));
      }
      type = d3_format_types.get(type) || d3_format_typeDefault;
      var zcomma = zfill && comma;
      return function(value) {
        var fullSuffix = suffix;
        if (integer && value % 1) return "";
        var negative = value < 0 || value === 0 && 1 / value < 0 ? (value = -value, "-") : sign;
        if (scale < 0) {
          var unit = d3.formatPrefix(value, precision);
          value = unit.scale(value);
          fullSuffix = unit.symbol + suffix;
        } else {
          value *= scale;
        }
        value = type(value, precision);
        var i = value.lastIndexOf("."), before = i < 0 ? value : value.substring(0, i), after = i < 0 ? "" : locale_decimal + value.substring(i + 1);
        if (!zfill && comma) before = formatGroup(before);
        var length = prefix.length + before.length + after.length + (zcomma ? 0 : negative.length), padding = length < width ? new Array(length = width - length + 1).join(fill) : "";
        if (zcomma) before = formatGroup(padding + before);
        negative += prefix;
        value = before + after;
        return (align === "<" ? negative + value + padding : align === ">" ? padding + negative + value : align === "^" ? padding.substring(0, length >>= 1) + negative + value + padding.substring(length) : negative + (zcomma ? value : padding + value)) + fullSuffix;
      };
    };
  }
  var d3_format_re = /(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i;
  var d3_format_types = d3.map({
    b: function(x) {
      return x.toString(2);
    },
    c: function(x) {
      return String.fromCharCode(x);
    },
    o: function(x) {
      return x.toString(8);
    },
    x: function(x) {
      return x.toString(16);
    },
    X: function(x) {
      return x.toString(16).toUpperCase();
    },
    g: function(x, p) {
      return x.toPrecision(p);
    },
    e: function(x, p) {
      return x.toExponential(p);
    },
    f: function(x, p) {
      return x.toFixed(p);
    },
    r: function(x, p) {
      return (x = d3.round(x, d3_format_precision(x, p))).toFixed(Math.max(0, Math.min(20, d3_format_precision(x * (1 + 1e-15), p))));
    }
  });
  function d3_format_typeDefault(x) {
    return x + "";
  }
  var d3_time = d3.time = {}, d3_date = Date;
  function d3_date_utc() {
    this._ = new Date(arguments.length > 1 ? Date.UTC.apply(this, arguments) : arguments[0]);
  }
  d3_date_utc.prototype = {
    getDate: function() {
      return this._.getUTCDate();
    },
    getDay: function() {
      return this._.getUTCDay();
    },
    getFullYear: function() {
      return this._.getUTCFullYear();
    },
    getHours: function() {
      return this._.getUTCHours();
    },
    getMilliseconds: function() {
      return this._.getUTCMilliseconds();
    },
    getMinutes: function() {
      return this._.getUTCMinutes();
    },
    getMonth: function() {
      return this._.getUTCMonth();
    },
    getSeconds: function() {
      return this._.getUTCSeconds();
    },
    getTime: function() {
      return this._.getTime();
    },
    getTimezoneOffset: function() {
      return 0;
    },
    valueOf: function() {
      return this._.valueOf();
    },
    setDate: function() {
      d3_time_prototype.setUTCDate.apply(this._, arguments);
    },
    setDay: function() {
      d3_time_prototype.setUTCDay.apply(this._, arguments);
    },
    setFullYear: function() {
      d3_time_prototype.setUTCFullYear.apply(this._, arguments);
    },
    setHours: function() {
      d3_time_prototype.setUTCHours.apply(this._, arguments);
    },
    setMilliseconds: function() {
      d3_time_prototype.setUTCMilliseconds.apply(this._, arguments);
    },
    setMinutes: function() {
      d3_time_prototype.setUTCMinutes.apply(this._, arguments);
    },
    setMonth: function() {
      d3_time_prototype.setUTCMonth.apply(this._, arguments);
    },
    setSeconds: function() {
      d3_time_prototype.setUTCSeconds.apply(this._, arguments);
    },
    setTime: function() {
      d3_time_prototype.setTime.apply(this._, arguments);
    }
  };
  var d3_time_prototype = Date.prototype;
  function d3_time_interval(local, step, number) {
    function round(date) {
      var d0 = local(date), d1 = offset(d0, 1);
      return date - d0 < d1 - date ? d0 : d1;
    }
    function ceil(date) {
      step(date = local(new d3_date(date - 1)), 1);
      return date;
    }
    function offset(date, k) {
      step(date = new d3_date(+date), k);
      return date;
    }
    function range(t0, t1, dt) {
      var time = ceil(t0), times = [];
      if (dt > 1) {
        while (time < t1) {
          if (!(number(time) % dt)) times.push(new Date(+time));
          step(time, 1);
        }
      } else {
        while (time < t1) times.push(new Date(+time)), step(time, 1);
      }
      return times;
    }
    function range_utc(t0, t1, dt) {
      try {
        d3_date = d3_date_utc;
        var utc = new d3_date_utc();
        utc._ = t0;
        return range(utc, t1, dt);
      } finally {
        d3_date = Date;
      }
    }
    local.floor = local;
    local.round = round;
    local.ceil = ceil;
    local.offset = offset;
    local.range = range;
    var utc = local.utc = d3_time_interval_utc(local);
    utc.floor = utc;
    utc.round = d3_time_interval_utc(round);
    utc.ceil = d3_time_interval_utc(ceil);
    utc.offset = d3_time_interval_utc(offset);
    utc.range = range_utc;
    return local;
  }
  function d3_time_interval_utc(method) {
    return function(date, k) {
      try {
        d3_date = d3_date_utc;
        var utc = new d3_date_utc();
        utc._ = date;
        return method(utc, k)._;
      } finally {
        d3_date = Date;
      }
    };
  }
  d3_time.year = d3_time_interval(function(date) {
    date = d3_time.day(date);
    date.setMonth(0, 1);
    return date;
  }, function(date, offset) {
    date.setFullYear(date.getFullYear() + offset);
  }, function(date) {
    return date.getFullYear();
  });
  d3_time.years = d3_time.year.range;
  d3_time.years.utc = d3_time.year.utc.range;
  d3_time.day = d3_time_interval(function(date) {
    var day = new d3_date(2e3, 0);
    day.setFullYear(date.getFullYear(), date.getMonth(), date.getDate());
    return day;
  }, function(date, offset) {
    date.setDate(date.getDate() + offset);
  }, function(date) {
    return date.getDate() - 1;
  });
  d3_time.days = d3_time.day.range;
  d3_time.days.utc = d3_time.day.utc.range;
  d3_time.dayOfYear = function(date) {
    var year = d3_time.year(date);
    return Math.floor((date - year - (date.getTimezoneOffset() - year.getTimezoneOffset()) * 6e4) / 864e5);
  };
  [ "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday" ].forEach(function(day, i) {
    i = 7 - i;
    var interval = d3_time[day] = d3_time_interval(function(date) {
      (date = d3_time.day(date)).setDate(date.getDate() - (date.getDay() + i) % 7);
      return date;
    }, function(date, offset) {
      date.setDate(date.getDate() + Math.floor(offset) * 7);
    }, function(date) {
      var day = d3_time.year(date).getDay();
      return Math.floor((d3_time.dayOfYear(date) + (day + i) % 7) / 7) - (day !== i);
    });
    d3_time[day + "s"] = interval.range;
    d3_time[day + "s"].utc = interval.utc.range;
    d3_time[day + "OfYear"] = function(date) {
      var day = d3_time.year(date).getDay();
      return Math.floor((d3_time.dayOfYear(date) + (day + i) % 7) / 7);
    };
  });
  d3_time.week = d3_time.sunday;
  d3_time.weeks = d3_time.sunday.range;
  d3_time.weeks.utc = d3_time.sunday.utc.range;
  d3_time.weekOfYear = d3_time.sundayOfYear;
  function d3_locale_timeFormat(locale) {
    var locale_dateTime = locale.dateTime, locale_date = locale.date, locale_time = locale.time, locale_periods = locale.periods, locale_days = locale.days, locale_shortDays = locale.shortDays, locale_months = locale.months, locale_shortMonths = locale.shortMonths;
    function d3_time_format(template) {
      var n = template.length;
      function format(date) {
        var string = [], i = -1, j = 0, c, p, f;
        while (++i < n) {
          if (template.charCodeAt(i) === 37) {
            string.push(template.substring(j, i));
            if ((p = d3_time_formatPads[c = template.charAt(++i)]) != null) c = template.charAt(++i);
            if (f = d3_time_formats[c]) c = f(date, p == null ? c === "e" ? " " : "0" : p);
            string.push(c);
            j = i + 1;
          }
        }
        string.push(template.substring(j, i));
        return string.join("");
      }
      format.parse = function(string) {
        var d = {
          y: 1900,
          m: 0,
          d: 1,
          H: 0,
          M: 0,
          S: 0,
          L: 0,
          Z: null
        }, i = d3_time_parse(d, template, string, 0);
        if (i != string.length) return null;
        if ("p" in d) d.H = d.H % 12 + d.p * 12;
        var localZ = d.Z != null && d3_date !== d3_date_utc, date = new (localZ ? d3_date_utc : d3_date)();
        if ("j" in d) date.setFullYear(d.y, 0, d.j); else if ("w" in d && ("W" in d || "U" in d)) {
          date.setFullYear(d.y, 0, 1);
          date.setFullYear(d.y, 0, "W" in d ? (d.w + 6) % 7 + d.W * 7 - (date.getDay() + 5) % 7 : d.w + d.U * 7 - (date.getDay() + 6) % 7);
        } else date.setFullYear(d.y, d.m, d.d);
        date.setHours(d.H + Math.floor(d.Z / 100), d.M + d.Z % 100, d.S, d.L);
        return localZ ? date._ : date;
      };
      format.toString = function() {
        return template;
      };
      return format;
    }
    function d3_time_parse(date, template, string, j) {
      var c, p, t, i = 0, n = template.length, m = string.length;
      while (i < n) {
        if (j >= m) return -1;
        c = template.charCodeAt(i++);
        if (c === 37) {
          t = template.charAt(i++);
          p = d3_time_parsers[t in d3_time_formatPads ? template.charAt(i++) : t];
          if (!p || (j = p(date, string, j)) < 0) return -1;
        } else if (c != string.charCodeAt(j++)) {
          return -1;
        }
      }
      return j;
    }
    d3_time_format.utc = function(template) {
      var local = d3_time_format(template);
      function format(date) {
        try {
          d3_date = d3_date_utc;
          var utc = new d3_date();
          utc._ = date;
          return local(utc);
        } finally {
          d3_date = Date;
        }
      }
      format.parse = function(string) {
        try {
          d3_date = d3_date_utc;
          var date = local.parse(string);
          return date && date._;
        } finally {
          d3_date = Date;
        }
      };
      format.toString = local.toString;
      return format;
    };
    d3_time_format.multi = d3_time_format.utc.multi = d3_time_formatMulti;
    var d3_time_periodLookup = d3.map(), d3_time_dayRe = d3_time_formatRe(locale_days), d3_time_dayLookup = d3_time_formatLookup(locale_days), d3_time_dayAbbrevRe = d3_time_formatRe(locale_shortDays), d3_time_dayAbbrevLookup = d3_time_formatLookup(locale_shortDays), d3_time_monthRe = d3_time_formatRe(locale_months), d3_time_monthLookup = d3_time_formatLookup(locale_months), d3_time_monthAbbrevRe = d3_time_formatRe(locale_shortMonths), d3_time_monthAbbrevLookup = d3_time_formatLookup(locale_shortMonths);
    locale_periods.forEach(function(p, i) {
      d3_time_periodLookup.set(p.toLowerCase(), i);
    });
    var d3_time_formats = {
      a: function(d) {
        return locale_shortDays[d.getDay()];
      },
      A: function(d) {
        return locale_days[d.getDay()];
      },
      b: function(d) {
        return locale_shortMonths[d.getMonth()];
      },
      B: function(d) {
        return locale_months[d.getMonth()];
      },
      c: d3_time_format(locale_dateTime),
      d: function(d, p) {
        return d3_time_formatPad(d.getDate(), p, 2);
      },
      e: function(d, p) {
        return d3_time_formatPad(d.getDate(), p, 2);
      },
      H: function(d, p) {
        return d3_time_formatPad(d.getHours(), p, 2);
      },
      I: function(d, p) {
        return d3_time_formatPad(d.getHours() % 12 || 12, p, 2);
      },
      j: function(d, p) {
        return d3_time_formatPad(1 + d3_time.dayOfYear(d), p, 3);
      },
      L: function(d, p) {
        return d3_time_formatPad(d.getMilliseconds(), p, 3);
      },
      m: function(d, p) {
        return d3_time_formatPad(d.getMonth() + 1, p, 2);
      },
      M: function(d, p) {
        return d3_time_formatPad(d.getMinutes(), p, 2);
      },
      p: function(d) {
        return locale_periods[+(d.getHours() >= 12)];
      },
      S: function(d, p) {
        return d3_time_formatPad(d.getSeconds(), p, 2);
      },
      U: function(d, p) {
        return d3_time_formatPad(d3_time.sundayOfYear(d), p, 2);
      },
      w: function(d) {
        return d.getDay();
      },
      W: function(d, p) {
        return d3_time_formatPad(d3_time.mondayOfYear(d), p, 2);
      },
      x: d3_time_format(locale_date),
      X: d3_time_format(locale_time),
      y: function(d, p) {
        return d3_time_formatPad(d.getFullYear() % 100, p, 2);
      },
      Y: function(d, p) {
        return d3_time_formatPad(d.getFullYear() % 1e4, p, 4);
      },
      Z: d3_time_zone,
      "%": function() {
        return "%";
      }
    };
    var d3_time_parsers = {
      a: d3_time_parseWeekdayAbbrev,
      A: d3_time_parseWeekday,
      b: d3_time_parseMonthAbbrev,
      B: d3_time_parseMonth,
      c: d3_time_parseLocaleFull,
      d: d3_time_parseDay,
      e: d3_time_parseDay,
      H: d3_time_parseHour24,
      I: d3_time_parseHour24,
      j: d3_time_parseDayOfYear,
      L: d3_time_parseMilliseconds,
      m: d3_time_parseMonthNumber,
      M: d3_time_parseMinutes,
      p: d3_time_parseAmPm,
      S: d3_time_parseSeconds,
      U: d3_time_parseWeekNumberSunday,
      w: d3_time_parseWeekdayNumber,
      W: d3_time_parseWeekNumberMonday,
      x: d3_time_parseLocaleDate,
      X: d3_time_parseLocaleTime,
      y: d3_time_parseYear,
      Y: d3_time_parseFullYear,
      Z: d3_time_parseZone,
      "%": d3_time_parseLiteralPercent
    };
    function d3_time_parseWeekdayAbbrev(date, string, i) {
      d3_time_dayAbbrevRe.lastIndex = 0;
      var n = d3_time_dayAbbrevRe.exec(string.substring(i));
      return n ? (date.w = d3_time_dayAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
    }
    function d3_time_parseWeekday(date, string, i) {
      d3_time_dayRe.lastIndex = 0;
      var n = d3_time_dayRe.exec(string.substring(i));
      return n ? (date.w = d3_time_dayLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
    }
    function d3_time_parseMonthAbbrev(date, string, i) {
      d3_time_monthAbbrevRe.lastIndex = 0;
      var n = d3_time_monthAbbrevRe.exec(string.substring(i));
      return n ? (date.m = d3_time_monthAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
    }
    function d3_time_parseMonth(date, string, i) {
      d3_time_monthRe.lastIndex = 0;
      var n = d3_time_monthRe.exec(string.substring(i));
      return n ? (date.m = d3_time_monthLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
    }
    function d3_time_parseLocaleFull(date, string, i) {
      return d3_time_parse(date, d3_time_formats.c.toString(), string, i);
    }
    function d3_time_parseLocaleDate(date, string, i) {
      return d3_time_parse(date, d3_time_formats.x.toString(), string, i);
    }
    function d3_time_parseLocaleTime(date, string, i) {
      return d3_time_parse(date, d3_time_formats.X.toString(), string, i);
    }
    function d3_time_parseAmPm(date, string, i) {
      var n = d3_time_periodLookup.get(string.substring(i, i += 2).toLowerCase());
      return n == null ? -1 : (date.p = n, i);
    }
    return d3_time_format;
  }
  var d3_time_formatPads = {
    "-": "",
    _: " ",
    "0": "0"
  }, d3_time_numberRe = /^\s*\d+/, d3_time_percentRe = /^%/;
  function d3_time_formatPad(value, fill, width) {
    var sign = value < 0 ? "-" : "", string = (sign ? -value : value) + "", length = string.length;
    return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string);
  }
  function d3_time_formatRe(names) {
    return new RegExp("^(?:" + names.map(d3.requote).join("|") + ")", "i");
  }
  function d3_time_formatLookup(names) {
    var map = new d3_Map(), i = -1, n = names.length;
    while (++i < n) map.set(names[i].toLowerCase(), i);
    return map;
  }
  function d3_time_parseWeekdayNumber(date, string, i) {
    d3_time_numberRe.lastIndex = 0;
    var n = d3_time_numberRe.exec(string.substring(i, i + 1));
    return n ? (date.w = +n[0], i + n[0].length) : -1;
  }
  function d3_time_parseWeekNumberSunday(date, string, i) {
    d3_time_numberRe.lastIndex = 0;
    var n = d3_time_numberRe.exec(string.substring(i));
    return n ? (date.U = +n[0], i + n[0].length) : -1;
  }
  function d3_time_parseWeekNumberMonday(date, string, i) {
    d3_time_numberRe.lastIndex = 0;
    var n = d3_time_numberRe.exec(string.substring(i));
    return n ? (date.W = +n[0], i + n[0].length) : -1;
  }
  function d3_time_parseFullYear(date, string, i) {
    d3_time_numberRe.lastIndex = 0;
    var n = d3_time_numberRe.exec(string.substring(i, i + 4));
    return n ? (date.y = +n[0], i + n[0].length) : -1;
  }
  function d3_time_parseYear(date, string, i) {
    d3_time_numberRe.lastIndex = 0;
    var n = d3_time_numberRe.exec(string.substring(i, i + 2));
    return n ? (date.y = d3_time_expandYear(+n[0]), i + n[0].length) : -1;
  }
  function d3_time_parseZone(date, string, i) {
    return /^[+-]\d{4}$/.test(string = string.substring(i, i + 5)) ? (date.Z = +string, 
    i + 5) : -1;
  }
  function d3_time_expandYear(d) {
    return d + (d > 68 ? 1900 : 2e3);
  }
  function d3_time_parseMonthNumber(date, string, i) {
    d3_time_numberRe.lastIndex = 0;
    var n = d3_time_numberRe.exec(string.substring(i, i + 2));
    return n ? (date.m = n[0] - 1, i + n[0].length) : -1;
  }
  function d3_time_parseDay(date, string, i) {
    d3_time_numberRe.lastIndex = 0;
    var n = d3_time_numberRe.exec(string.substring(i, i + 2));
    return n ? (date.d = +n[0], i + n[0].length) : -1;
  }
  function d3_time_parseDayOfYear(date, string, i) {
    d3_time_numberRe.lastIndex = 0;
    var n = d3_time_numberRe.exec(string.substring(i, i + 3));
    return n ? (date.j = +n[0], i + n[0].length) : -1;
  }
  function d3_time_parseHour24(date, string, i) {
    d3_time_numberRe.lastIndex = 0;
    var n = d3_time_numberRe.exec(string.substring(i, i + 2));
    return n ? (date.H = +n[0], i + n[0].length) : -1;
  }
  function d3_time_parseMinutes(date, string, i) {
    d3_time_numberRe.lastIndex = 0;
    var n = d3_time_numberRe.exec(string.substring(i, i + 2));
    return n ? (date.M = +n[0], i + n[0].length) : -1;
  }
  function d3_time_parseSeconds(date, string, i) {
    d3_time_numberRe.lastIndex = 0;
    var n = d3_time_numberRe.exec(string.substring(i, i + 2));
    return n ? (date.S = +n[0], i + n[0].length) : -1;
  }
  function d3_time_parseMilliseconds(date, string, i) {
    d3_time_numberRe.lastIndex = 0;
    var n = d3_time_numberRe.exec(string.substring(i, i + 3));
    return n ? (date.L = +n[0], i + n[0].length) : -1;
  }
  function d3_time_zone(d) {
    var z = d.getTimezoneOffset(), zs = z > 0 ? "-" : "+", zh = ~~(abs(z) / 60), zm = abs(z) % 60;
    return zs + d3_time_formatPad(zh, "0", 2) + d3_time_formatPad(zm, "0", 2);
  }
  function d3_time_parseLiteralPercent(date, string, i) {
    d3_time_percentRe.lastIndex = 0;
    var n = d3_time_percentRe.exec(string.substring(i, i + 1));
    return n ? i + n[0].length : -1;
  }
  function d3_time_formatMulti(formats) {
    var n = formats.length, i = -1;
    while (++i < n) formats[i][0] = this(formats[i][0]);
    return function(date) {
      var i = 0, f = formats[i];
      while (!f[1](date)) f = formats[++i];
      return f[0](date);
    };
  }
  d3.locale = function(locale) {
    return {
      numberFormat: d3_locale_numberFormat(locale),
      timeFormat: d3_locale_timeFormat(locale)
    };
  };
  var d3_locale_enUS = d3.locale({
    decimal: ".",
    thousands: ",",
    grouping: [ 3 ],
    currency: [ "$", "" ],
    dateTime: "%a %b %e %X %Y",
    date: "%m/%d/%Y",
    time: "%H:%M:%S",
    periods: [ "AM", "PM" ],
    days: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ],
    shortDays: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ],
    months: [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ],
    shortMonths: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ]
  });
  d3.format = d3_locale_enUS.numberFormat;
  d3.geo = {};
  function d3_adder() {}
  d3_adder.prototype = {
    s: 0,
    t: 0,
    add: function(y) {
      d3_adderSum(y, this.t, d3_adderTemp);
      d3_adderSum(d3_adderTemp.s, this.s, this);
      if (this.s) this.t += d3_adderTemp.t; else this.s = d3_adderTemp.t;
    },
    reset: function() {
      this.s = this.t = 0;
    },
    valueOf: function() {
      return this.s;
    }
  };
  var d3_adderTemp = new d3_adder();
  function d3_adderSum(a, b, o) {
    var x = o.s = a + b, bv = x - a, av = x - bv;
    o.t = a - av + (b - bv);
  }
  d3.geo.stream = function(object, listener) {
    if (object && d3_geo_streamObjectType.hasOwnProperty(object.type)) {
      d3_geo_streamObjectType[object.type](object, listener);
    } else {
      d3_geo_streamGeometry(object, listener);
    }
  };
  function d3_geo_streamGeometry(geometry, listener) {
    if (geometry && d3_geo_streamGeometryType.hasOwnProperty(geometry.type)) {
      d3_geo_streamGeometryType[geometry.type](geometry, listener);
    }
  }
  var d3_geo_streamObjectType = {
    Feature: function(feature, listener) {
      d3_geo_streamGeometry(feature.geometry, listener);
    },
    FeatureCollection: function(object, listener) {
      var features = object.features, i = -1, n = features.length;
      while (++i < n) d3_geo_streamGeometry(features[i].geometry, listener);
    }
  };
  var d3_geo_streamGeometryType = {
    Sphere: function(object, listener) {
      listener.sphere();
    },
    Point: function(object, listener) {
      object = object.coordinates;
      listener.point(object[0], object[1], object[2]);
    },
    MultiPoint: function(object, listener) {
      var coordinates = object.coordinates, i = -1, n = coordinates.length;
      while (++i < n) object = coordinates[i], listener.point(object[0], object[1], object[2]);
    },
    LineString: function(object, listener) {
      d3_geo_streamLine(object.coordinates, listener, 0);
    },
    MultiLineString: function(object, listener) {
      var coordinates = object.coordinates, i = -1, n = coordinates.length;
      while (++i < n) d3_geo_streamLine(coordinates[i], listener, 0);
    },
    Polygon: function(object, listener) {
      d3_geo_streamPolygon(object.coordinates, listener);
    },
    MultiPolygon: function(object, listener) {
      var coordinates = object.coordinates, i = -1, n = coordinates.length;
      while (++i < n) d3_geo_streamPolygon(coordinates[i], listener);
    },
    GeometryCollection: function(object, listener) {
      var geometries = object.geometries, i = -1, n = geometries.length;
      while (++i < n) d3_geo_streamGeometry(geometries[i], listener);
    }
  };
  function d3_geo_streamLine(coordinates, listener, closed) {
    var i = -1, n = coordinates.length - closed, coordinate;
    listener.lineStart();
    while (++i < n) coordinate = coordinates[i], listener.point(coordinate[0], coordinate[1], coordinate[2]);
    listener.lineEnd();
  }
  function d3_geo_streamPolygon(coordinates, listener) {
    var i = -1, n = coordinates.length;
    listener.polygonStart();
    while (++i < n) d3_geo_streamLine(coordinates[i], listener, 1);
    listener.polygonEnd();
  }
  d3.geo.area = function(object) {
    d3_geo_areaSum = 0;
    d3.geo.stream(object, d3_geo_area);
    return d3_geo_areaSum;
  };
  var d3_geo_areaSum, d3_geo_areaRingSum = new d3_adder();
  var d3_geo_area = {
    sphere: function() {
      d3_geo_areaSum += 4 * π;
    },
    point: d3_noop,
    lineStart: d3_noop,
    lineEnd: d3_noop,
    polygonStart: function() {
      d3_geo_areaRingSum.reset();
      d3_geo_area.lineStart = d3_geo_areaRingStart;
    },
    polygonEnd: function() {
      var area = 2 * d3_geo_areaRingSum;
      d3_geo_areaSum += area < 0 ? 4 * π + area : area;
      d3_geo_area.lineStart = d3_geo_area.lineEnd = d3_geo_area.point = d3_noop;
    }
  };
  function d3_geo_areaRingStart() {
    var λ00, φ00, λ0, cosφ0, sinφ0;
    d3_geo_area.point = function(λ, φ) {
      d3_geo_area.point = nextPoint;
      λ0 = (λ00 = λ) * d3_radians, cosφ0 = Math.cos(φ = (φ00 = φ) * d3_radians / 2 + π / 4), 
      sinφ0 = Math.sin(φ);
    };
    function nextPoint(λ, φ) {
      λ *= d3_radians;
      φ = φ * d3_radians / 2 + π / 4;
      var dλ = λ - λ0, cosφ = Math.cos(φ), sinφ = Math.sin(φ), k = sinφ0 * sinφ, u = cosφ0 * cosφ + k * Math.cos(dλ), v = k * Math.sin(dλ);
      d3_geo_areaRingSum.add(Math.atan2(v, u));
      λ0 = λ, cosφ0 = cosφ, sinφ0 = sinφ;
    }
    d3_geo_area.lineEnd = function() {
      nextPoint(λ00, φ00);
    };
  }
  function d3_geo_cartesian(spherical) {
    var λ = spherical[0], φ = spherical[1], cosφ = Math.cos(φ);
    return [ cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ) ];
  }
  function d3_geo_cartesianDot(a, b) {
    return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
  }
  function d3_geo_cartesianCross(a, b) {
    return [ a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0] ];
  }
  function d3_geo_cartesianAdd(a, b) {
    a[0] += b[0];
    a[1] += b[1];
    a[2] += b[2];
  }
  function d3_geo_cartesianScale(vector, k) {
    return [ vector[0] * k, vector[1] * k, vector[2] * k ];
  }
  function d3_geo_cartesianNormalize(d) {
    var l = Math.sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]);
    d[0] /= l;
    d[1] /= l;
    d[2] /= l;
  }
  function d3_geo_spherical(cartesian) {
    return [ Math.atan2(cartesian[1], cartesian[0]), d3_asin(cartesian[2]) ];
  }
  function d3_geo_sphericalEqual(a, b) {
    return abs(a[0] - b[0]) < ε && abs(a[1] - b[1]) < ε;
  }
  d3.geo.bounds = function() {
    var λ0, φ0, λ1, φ1, λ_, λ__, φ__, p0, dλSum, ranges, range;
    var bound = {
      point: point,
      lineStart: lineStart,
      lineEnd: lineEnd,
      polygonStart: function() {
        bound.point = ringPoint;
        bound.lineStart = ringStart;
        bound.lineEnd = ringEnd;
        dλSum = 0;
        d3_geo_area.polygonStart();
      },
      polygonEnd: function() {
        d3_geo_area.polygonEnd();
        bound.point = point;
        bound.lineStart = lineStart;
        bound.lineEnd = lineEnd;
        if (d3_geo_areaRingSum < 0) λ0 = -(λ1 = 180), φ0 = -(φ1 = 90); else if (dλSum > ε) φ1 = 90; else if (dλSum < -ε) φ0 = -90;
        range[0] = λ0, range[1] = λ1;
      }
    };
    function point(λ, φ) {
      ranges.push(range = [ λ0 = λ, λ1 = λ ]);
      if (φ < φ0) φ0 = φ;
      if (φ > φ1) φ1 = φ;
    }
    function linePoint(λ, φ) {
      var p = d3_geo_cartesian([ λ * d3_radians, φ * d3_radians ]);
      if (p0) {
        var normal = d3_geo_cartesianCross(p0, p), equatorial = [ normal[1], -normal[0], 0 ], inflection = d3_geo_cartesianCross(equatorial, normal);
        d3_geo_cartesianNormalize(inflection);
        inflection = d3_geo_spherical(inflection);
        var dλ = λ - λ_, s = dλ > 0 ? 1 : -1, λi = inflection[0] * d3_degrees * s, antimeridian = abs(dλ) > 180;
        if (antimeridian ^ (s * λ_ < λi && λi < s * λ)) {
          var φi = inflection[1] * d3_degrees;
          if (φi > φ1) φ1 = φi;
        } else if (λi = (λi + 360) % 360 - 180, antimeridian ^ (s * λ_ < λi && λi < s * λ)) {
          var φi = -inflection[1] * d3_degrees;
          if (φi < φ0) φ0 = φi;
        } else {
          if (φ < φ0) φ0 = φ;
          if (φ > φ1) φ1 = φ;
        }
        if (antimeridian) {
          if (λ < λ_) {
            if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ;
          } else {
            if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ;
          }
        } else {
          if (λ1 >= λ0) {
            if (λ < λ0) λ0 = λ;
            if (λ > λ1) λ1 = λ;
          } else {
            if (λ > λ_) {
              if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ;
            } else {
              if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ;
            }
          }
        }
      } else {
        point(λ, φ);
      }
      p0 = p, λ_ = λ;
    }
    function lineStart() {
      bound.point = linePoint;
    }
    function lineEnd() {
      range[0] = λ0, range[1] = λ1;
      bound.point = point;
      p0 = null;
    }
    function ringPoint(λ, φ) {
      if (p0) {
        var dλ = λ - λ_;
        dλSum += abs(dλ) > 180 ? dλ + (dλ > 0 ? 360 : -360) : dλ;
      } else λ__ = λ, φ__ = φ;
      d3_geo_area.point(λ, φ);
      linePoint(λ, φ);
    }
    function ringStart() {
      d3_geo_area.lineStart();
    }
    function ringEnd() {
      ringPoint(λ__, φ__);
      d3_geo_area.lineEnd();
      if (abs(dλSum) > ε) λ0 = -(λ1 = 180);
      range[0] = λ0, range[1] = λ1;
      p0 = null;
    }
    function angle(λ0, λ1) {
      return (λ1 -= λ0) < 0 ? λ1 + 360 : λ1;
    }
    function compareRanges(a, b) {
      return a[0] - b[0];
    }
    function withinRange(x, range) {
      return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x;
    }
    return function(feature) {
      φ1 = λ1 = -(λ0 = φ0 = Infinity);
      ranges = [];
      d3.geo.stream(feature, bound);
      var n = ranges.length;
      if (n) {
        ranges.sort(compareRanges);
        for (var i = 1, a = ranges[0], b, merged = [ a ]; i < n; ++i) {
          b = ranges[i];
          if (withinRange(b[0], a) || withinRange(b[1], a)) {
            if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1];
            if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0];
          } else {
            merged.push(a = b);
          }
        }
        var best = -Infinity, dλ;
        for (var n = merged.length - 1, i = 0, a = merged[n], b; i <= n; a = b, ++i) {
          b = merged[i];
          if ((dλ = angle(a[1], b[0])) > best) best = dλ, λ0 = b[0], λ1 = a[1];
        }
      }
      ranges = range = null;
      return λ0 === Infinity || φ0 === Infinity ? [ [ NaN, NaN ], [ NaN, NaN ] ] : [ [ λ0, φ0 ], [ λ1, φ1 ] ];
    };
  }();
  d3.geo.centroid = function(object) {
    d3_geo_centroidW0 = d3_geo_centroidW1 = d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 = d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 = d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0;
    d3.geo.stream(object, d3_geo_centroid);
    var x = d3_geo_centroidX2, y = d3_geo_centroidY2, z = d3_geo_centroidZ2, m = x * x + y * y + z * z;
    if (m < ε2) {
      x = d3_geo_centroidX1, y = d3_geo_centroidY1, z = d3_geo_centroidZ1;
      if (d3_geo_centroidW1 < ε) x = d3_geo_centroidX0, y = d3_geo_centroidY0, z = d3_geo_centroidZ0;
      m = x * x + y * y + z * z;
      if (m < ε2) return [ NaN, NaN ];
    }
    return [ Math.atan2(y, x) * d3_degrees, d3_asin(z / Math.sqrt(m)) * d3_degrees ];
  };
  var d3_geo_centroidW0, d3_geo_centroidW1, d3_geo_centroidX0, d3_geo_centroidY0, d3_geo_centroidZ0, d3_geo_centroidX1, d3_geo_centroidY1, d3_geo_centroidZ1, d3_geo_centroidX2, d3_geo_centroidY2, d3_geo_centroidZ2;
  var d3_geo_centroid = {
    sphere: d3_noop,
    point: d3_geo_centroidPoint,
    lineStart: d3_geo_centroidLineStart,
    lineEnd: d3_geo_centroidLineEnd,
    polygonStart: function() {
      d3_geo_centroid.lineStart = d3_geo_centroidRingStart;
    },
    polygonEnd: function() {
      d3_geo_centroid.lineStart = d3_geo_centroidLineStart;
    }
  };
  function d3_geo_centroidPoint(λ, φ) {
    λ *= d3_radians;
    var cosφ = Math.cos(φ *= d3_radians);
    d3_geo_centroidPointXYZ(cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ));
  }
  function d3_geo_centroidPointXYZ(x, y, z) {
    ++d3_geo_centroidW0;
    d3_geo_centroidX0 += (x - d3_geo_centroidX0) / d3_geo_centroidW0;
    d3_geo_centroidY0 += (y - d3_geo_centroidY0) / d3_geo_centroidW0;
    d3_geo_centroidZ0 += (z - d3_geo_centroidZ0) / d3_geo_centroidW0;
  }
  function d3_geo_centroidLineStart() {
    var x0, y0, z0;
    d3_geo_centroid.point = function(λ, φ) {
      λ *= d3_radians;
      var cosφ = Math.cos(φ *= d3_radians);
      x0 = cosφ * Math.cos(λ);
      y0 = cosφ * Math.sin(λ);
      z0 = Math.sin(φ);
      d3_geo_centroid.point = nextPoint;
      d3_geo_centroidPointXYZ(x0, y0, z0);
    };
    function nextPoint(λ, φ) {
      λ *= d3_radians;
      var cosφ = Math.cos(φ *= d3_radians), x = cosφ * Math.cos(λ), y = cosφ * Math.sin(λ), z = Math.sin(φ), w = Math.atan2(Math.sqrt((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w), x0 * x + y0 * y + z0 * z);
      d3_geo_centroidW1 += w;
      d3_geo_centroidX1 += w * (x0 + (x0 = x));
      d3_geo_centroidY1 += w * (y0 + (y0 = y));
      d3_geo_centroidZ1 += w * (z0 + (z0 = z));
      d3_geo_centroidPointXYZ(x0, y0, z0);
    }
  }
  function d3_geo_centroidLineEnd() {
    d3_geo_centroid.point = d3_geo_centroidPoint;
  }
  function d3_geo_centroidRingStart() {
    var λ00, φ00, x0, y0, z0;
    d3_geo_centroid.point = function(λ, φ) {
      λ00 = λ, φ00 = φ;
      d3_geo_centroid.point = nextPoint;
      λ *= d3_radians;
      var cosφ = Math.cos(φ *= d3_radians);
      x0 = cosφ * Math.cos(λ);
      y0 = cosφ * Math.sin(λ);
      z0 = Math.sin(φ);
      d3_geo_centroidPointXYZ(x0, y0, z0);
    };
    d3_geo_centroid.lineEnd = function() {
      nextPoint(λ00, φ00);
      d3_geo_centroid.lineEnd = d3_geo_centroidLineEnd;
      d3_geo_centroid.point = d3_geo_centroidPoint;
    };
    function nextPoint(λ, φ) {
      λ *= d3_radians;
      var cosφ = Math.cos(φ *= d3_radians), x = cosφ * Math.cos(λ), y = cosφ * Math.sin(λ), z = Math.sin(φ), cx = y0 * z - z0 * y, cy = z0 * x - x0 * z, cz = x0 * y - y0 * x, m = Math.sqrt(cx * cx + cy * cy + cz * cz), u = x0 * x + y0 * y + z0 * z, v = m && -d3_acos(u) / m, w = Math.atan2(m, u);
      d3_geo_centroidX2 += v * cx;
      d3_geo_centroidY2 += v * cy;
      d3_geo_centroidZ2 += v * cz;
      d3_geo_centroidW1 += w;
      d3_geo_centroidX1 += w * (x0 + (x0 = x));
      d3_geo_centroidY1 += w * (y0 + (y0 = y));
      d3_geo_centroidZ1 += w * (z0 + (z0 = z));
      d3_geo_centroidPointXYZ(x0, y0, z0);
    }
  }
  function d3_true() {
    return true;
  }
  function d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener) {
    var subject = [], clip = [];
    segments.forEach(function(segment) {
      if ((n = segment.length - 1) <= 0) return;
      var n, p0 = segment[0], p1 = segment[n];
      if (d3_geo_sphericalEqual(p0, p1)) {
        listener.lineStart();
        for (var i = 0; i < n; ++i) listener.point((p0 = segment[i])[0], p0[1]);
        listener.lineEnd();
        return;
      }
      var a = new d3_geo_clipPolygonIntersection(p0, segment, null, true), b = new d3_geo_clipPolygonIntersection(p0, null, a, false);
      a.o = b;
      subject.push(a);
      clip.push(b);
      a = new d3_geo_clipPolygonIntersection(p1, segment, null, false);
      b = new d3_geo_clipPolygonIntersection(p1, null, a, true);
      a.o = b;
      subject.push(a);
      clip.push(b);
    });
    clip.sort(compare);
    d3_geo_clipPolygonLinkCircular(subject);
    d3_geo_clipPolygonLinkCircular(clip);
    if (!subject.length) return;
    for (var i = 0, entry = clipStartInside, n = clip.length; i < n; ++i) {
      clip[i].e = entry = !entry;
    }
    var start = subject[0], points, point;
    while (1) {
      var current = start, isSubject = true;
      while (current.v) if ((current = current.n) === start) return;
      points = current.z;
      listener.lineStart();
      do {
        current.v = current.o.v = true;
        if (current.e) {
          if (isSubject) {
            for (var i = 0, n = points.length; i < n; ++i) listener.point((point = points[i])[0], point[1]);
          } else {
            interpolate(current.x, current.n.x, 1, listener);
          }
          current = current.n;
        } else {
          if (isSubject) {
            points = current.p.z;
            for (var i = points.length - 1; i >= 0; --i) listener.point((point = points[i])[0], point[1]);
          } else {
            interpolate(current.x, current.p.x, -1, listener);
          }
          current = current.p;
        }
        current = current.o;
        points = current.z;
        isSubject = !isSubject;
      } while (!current.v);
      listener.lineEnd();
    }
  }
  function d3_geo_clipPolygonLinkCircular(array) {
    if (!(n = array.length)) return;
    var n, i = 0, a = array[0], b;
    while (++i < n) {
      a.n = b = array[i];
      b.p = a;
      a = b;
    }
    a.n = b = array[0];
    b.p = a;
  }
  function d3_geo_clipPolygonIntersection(point, points, other, entry) {
    this.x = point;
    this.z = points;
    this.o = other;
    this.e = entry;
    this.v = false;
    this.n = this.p = null;
  }
  function d3_geo_clip(pointVisible, clipLine, interpolate, clipStart) {
    return function(rotate, listener) {
      var line = clipLine(listener), rotatedClipStart = rotate.invert(clipStart[0], clipStart[1]);
      var clip = {
        point: point,
        lineStart: lineStart,
        lineEnd: lineEnd,
        polygonStart: function() {
          clip.point = pointRing;
          clip.lineStart = ringStart;
          clip.lineEnd = ringEnd;
          segments = [];
          polygon = [];
          listener.polygonStart();
        },
        polygonEnd: function() {
          clip.point = point;
          clip.lineStart = lineStart;
          clip.lineEnd = lineEnd;
          segments = d3.merge(segments);
          var clipStartInside = d3_geo_pointInPolygon(rotatedClipStart, polygon);
          if (segments.length) {
            d3_geo_clipPolygon(segments, d3_geo_clipSort, clipStartInside, interpolate, listener);
          } else if (clipStartInside) {
            listener.lineStart();
            interpolate(null, null, 1, listener);
            listener.lineEnd();
          }
          listener.polygonEnd();
          segments = polygon = null;
        },
        sphere: function() {
          listener.polygonStart();
          listener.lineStart();
          interpolate(null, null, 1, listener);
          listener.lineEnd();
          listener.polygonEnd();
        }
      };
      function point(λ, φ) {
        var point = rotate(λ, φ);
        if (pointVisible(λ = point[0], φ = point[1])) listener.point(λ, φ);
      }
      function pointLine(λ, φ) {
        var point = rotate(λ, φ);
        line.point(point[0], point[1]);
      }
      function lineStart() {
        clip.point = pointLine;
        line.lineStart();
      }
      function lineEnd() {
        clip.point = point;
        line.lineEnd();
      }
      var segments;
      var buffer = d3_geo_clipBufferListener(), ringListener = clipLine(buffer), polygon, ring;
      function pointRing(λ, φ) {
        ring.push([ λ, φ ]);
        var point = rotate(λ, φ);
        ringListener.point(point[0], point[1]);
      }
      function ringStart() {
        ringListener.lineStart();
        ring = [];
      }
      function ringEnd() {
        pointRing(ring[0][0], ring[0][1]);
        ringListener.lineEnd();
        var clean = ringListener.clean(), ringSegments = buffer.buffer(), segment, n = ringSegments.length;
        ring.pop();
        polygon.push(ring);
        ring = null;
        if (!n) return;
        if (clean & 1) {
          segment = ringSegments[0];
          var n = segment.length - 1, i = -1, point;
          listener.lineStart();
          while (++i < n) listener.point((point = segment[i])[0], point[1]);
          listener.lineEnd();
          return;
        }
        if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift()));
        segments.push(ringSegments.filter(d3_geo_clipSegmentLength1));
      }
      return clip;
    };
  }
  function d3_geo_clipSegmentLength1(segment) {
    return segment.length > 1;
  }
  function d3_geo_clipBufferListener() {
    var lines = [], line;
    return {
      lineStart: function() {
        lines.push(line = []);
      },
      point: function(λ, φ) {
        line.push([ λ, φ ]);
      },
      lineEnd: d3_noop,
      buffer: function() {
        var buffer = lines;
        lines = [];
        line = null;
        return buffer;
      },
      rejoin: function() {
        if (lines.length > 1) lines.push(lines.pop().concat(lines.shift()));
      }
    };
  }
  function d3_geo_clipSort(a, b) {
    return ((a = a.x)[0] < 0 ? a[1] - halfπ - ε : halfπ - a[1]) - ((b = b.x)[0] < 0 ? b[1] - halfπ - ε : halfπ - b[1]);
  }
  function d3_geo_pointInPolygon(point, polygon) {
    var meridian = point[0], parallel = point[1], meridianNormal = [ Math.sin(meridian), -Math.cos(meridian), 0 ], polarAngle = 0, winding = 0;
    d3_geo_areaRingSum.reset();
    for (var i = 0, n = polygon.length; i < n; ++i) {
      var ring = polygon[i], m = ring.length;
      if (!m) continue;
      var point0 = ring[0], λ0 = point0[0], φ0 = point0[1] / 2 + π / 4, sinφ0 = Math.sin(φ0), cosφ0 = Math.cos(φ0), j = 1;
      while (true) {
        if (j === m) j = 0;
        point = ring[j];
        var λ = point[0], φ = point[1] / 2 + π / 4, sinφ = Math.sin(φ), cosφ = Math.cos(φ), dλ = λ - λ0, antimeridian = abs(dλ) > π, k = sinφ0 * sinφ;
        d3_geo_areaRingSum.add(Math.atan2(k * Math.sin(dλ), cosφ0 * cosφ + k * Math.cos(dλ)));
        polarAngle += antimeridian ? dλ + (dλ >= 0 ? τ : -τ) : dλ;
        if (antimeridian ^ λ0 >= meridian ^ λ >= meridian) {
          var arc = d3_geo_cartesianCross(d3_geo_cartesian(point0), d3_geo_cartesian(point));
          d3_geo_cartesianNormalize(arc);
          var intersection = d3_geo_cartesianCross(meridianNormal, arc);
          d3_geo_cartesianNormalize(intersection);
          var φarc = (antimeridian ^ dλ >= 0 ? -1 : 1) * d3_asin(intersection[2]);
          if (parallel > φarc || parallel === φarc && (arc[0] || arc[1])) {
            winding += antimeridian ^ dλ >= 0 ? 1 : -1;
          }
        }
        if (!j++) break;
        λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ, point0 = point;
      }
    }
    return (polarAngle < -ε || polarAngle < ε && d3_geo_areaRingSum < 0) ^ winding & 1;
  }
  var d3_geo_clipAntimeridian = d3_geo_clip(d3_true, d3_geo_clipAntimeridianLine, d3_geo_clipAntimeridianInterpolate, [ -π, -π / 2 ]);
  function d3_geo_clipAntimeridianLine(listener) {
    var λ0 = NaN, φ0 = NaN, sλ0 = NaN, clean;
    return {
      lineStart: function() {
        listener.lineStart();
        clean = 1;
      },
      point: function(λ1, φ1) {
        var sλ1 = λ1 > 0 ? π : -π, dλ = abs(λ1 - λ0);
        if (abs(dλ - π) < ε) {
          listener.point(λ0, φ0 = (φ0 + φ1) / 2 > 0 ? halfπ : -halfπ);
          listener.point(sλ0, φ0);
          listener.lineEnd();
          listener.lineStart();
          listener.point(sλ1, φ0);
          listener.point(λ1, φ0);
          clean = 0;
        } else if (sλ0 !== sλ1 && dλ >= π) {
          if (abs(λ0 - sλ0) < ε) λ0 -= sλ0 * ε;
          if (abs(λ1 - sλ1) < ε) λ1 -= sλ1 * ε;
          φ0 = d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1);
          listener.point(sλ0, φ0);
          listener.lineEnd();
          listener.lineStart();
          listener.point(sλ1, φ0);
          clean = 0;
        }
        listener.point(λ0 = λ1, φ0 = φ1);
        sλ0 = sλ1;
      },
      lineEnd: function() {
        listener.lineEnd();
        λ0 = φ0 = NaN;
      },
      clean: function() {
        return 2 - clean;
      }
    };
  }
  function d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1) {
    var cosφ0, cosφ1, sinλ0_λ1 = Math.sin(λ0 - λ1);
    return abs(sinλ0_λ1) > ε ? Math.atan((Math.sin(φ0) * (cosφ1 = Math.cos(φ1)) * Math.sin(λ1) - Math.sin(φ1) * (cosφ0 = Math.cos(φ0)) * Math.sin(λ0)) / (cosφ0 * cosφ1 * sinλ0_λ1)) : (φ0 + φ1) / 2;
  }
  function d3_geo_clipAntimeridianInterpolate(from, to, direction, listener) {
    var φ;
    if (from == null) {
      φ = direction * halfπ;
      listener.point(-π, φ);
      listener.point(0, φ);
      listener.point(π, φ);
      listener.point(π, 0);
      listener.point(π, -φ);
      listener.point(0, -φ);
      listener.point(-π, -φ);
      listener.point(-π, 0);
      listener.point(-π, φ);
    } else if (abs(from[0] - to[0]) > ε) {
      var s = from[0] < to[0] ? π : -π;
      φ = direction * s / 2;
      listener.point(-s, φ);
      listener.point(0, φ);
      listener.point(s, φ);
    } else {
      listener.point(to[0], to[1]);
    }
  }
  function d3_geo_clipCircle(radius) {
    var cr = Math.cos(radius), smallRadius = cr > 0, notHemisphere = abs(cr) > ε, interpolate = d3_geo_circleInterpolate(radius, 6 * d3_radians);
    return d3_geo_clip(visible, clipLine, interpolate, smallRadius ? [ 0, -radius ] : [ -π, radius - π ]);
    function visible(λ, φ) {
      return Math.cos(λ) * Math.cos(φ) > cr;
    }
    function clipLine(listener) {
      var point0, c0, v0, v00, clean;
      return {
        lineStart: function() {
          v00 = v0 = false;
          clean = 1;
        },
        point: function(λ, φ) {
          var point1 = [ λ, φ ], point2, v = visible(λ, φ), c = smallRadius ? v ? 0 : code(λ, φ) : v ? code(λ + (λ < 0 ? π : -π), φ) : 0;
          if (!point0 && (v00 = v0 = v)) listener.lineStart();
          if (v !== v0) {
            point2 = intersect(point0, point1);
            if (d3_geo_sphericalEqual(point0, point2) || d3_geo_sphericalEqual(point1, point2)) {
              point1[0] += ε;
              point1[1] += ε;
              v = visible(point1[0], point1[1]);
            }
          }
          if (v !== v0) {
            clean = 0;
            if (v) {
              listener.lineStart();
              point2 = intersect(point1, point0);
              listener.point(point2[0], point2[1]);
            } else {
              point2 = intersect(point0, point1);
              listener.point(point2[0], point2[1]);
              listener.lineEnd();
            }
            point0 = point2;
          } else if (notHemisphere && point0 && smallRadius ^ v) {
            var t;
            if (!(c & c0) && (t = intersect(point1, point0, true))) {
              clean = 0;
              if (smallRadius) {
                listener.lineStart();
                listener.point(t[0][0], t[0][1]);
                listener.point(t[1][0], t[1][1]);
                listener.lineEnd();
              } else {
                listener.point(t[1][0], t[1][1]);
                listener.lineEnd();
                listener.lineStart();
                listener.point(t[0][0], t[0][1]);
              }
            }
          }
          if (v && (!point0 || !d3_geo_sphericalEqual(point0, point1))) {
            listener.point(point1[0], point1[1]);
          }
          point0 = point1, v0 = v, c0 = c;
        },
        lineEnd: function() {
          if (v0) listener.lineEnd();
          point0 = null;
        },
        clean: function() {
          return clean | (v00 && v0) << 1;
        }
      };
    }
    function intersect(a, b, two) {
      var pa = d3_geo_cartesian(a), pb = d3_geo_cartesian(b);
      var n1 = [ 1, 0, 0 ], n2 = d3_geo_cartesianCross(pa, pb), n2n2 = d3_geo_cartesianDot(n2, n2), n1n2 = n2[0], determinant = n2n2 - n1n2 * n1n2;
      if (!determinant) return !two && a;
      var c1 = cr * n2n2 / determinant, c2 = -cr * n1n2 / determinant, n1xn2 = d3_geo_cartesianCross(n1, n2), A = d3_geo_cartesianScale(n1, c1), B = d3_geo_cartesianScale(n2, c2);
      d3_geo_cartesianAdd(A, B);
      var u = n1xn2, w = d3_geo_cartesianDot(A, u), uu = d3_geo_cartesianDot(u, u), t2 = w * w - uu * (d3_geo_cartesianDot(A, A) - 1);
      if (t2 < 0) return;
      var t = Math.sqrt(t2), q = d3_geo_cartesianScale(u, (-w - t) / uu);
      d3_geo_cartesianAdd(q, A);
      q = d3_geo_spherical(q);
      if (!two) return q;
      var λ0 = a[0], λ1 = b[0], φ0 = a[1], φ1 = b[1], z;
      if (λ1 < λ0) z = λ0, λ0 = λ1, λ1 = z;
      var δλ = λ1 - λ0, polar = abs(δλ - π) < ε, meridian = polar || δλ < ε;
      if (!polar && φ1 < φ0) z = φ0, φ0 = φ1, φ1 = z;
      if (meridian ? polar ? φ0 + φ1 > 0 ^ q[1] < (abs(q[0] - λ0) < ε ? φ0 : φ1) : φ0 <= q[1] && q[1] <= φ1 : δλ > π ^ (λ0 <= q[0] && q[0] <= λ1)) {
        var q1 = d3_geo_cartesianScale(u, (-w + t) / uu);
        d3_geo_cartesianAdd(q1, A);
        return [ q, d3_geo_spherical(q1) ];
      }
    }
    function code(λ, φ) {
      var r = smallRadius ? radius : π - radius, code = 0;
      if (λ < -r) code |= 1; else if (λ > r) code |= 2;
      if (φ < -r) code |= 4; else if (φ > r) code |= 8;
      return code;
    }
  }
  function d3_geom_clipLine(x0, y0, x1, y1) {
    return function(line) {
      var a = line.a, b = line.b, ax = a.x, ay = a.y, bx = b.x, by = b.y, t0 = 0, t1 = 1, dx = bx - ax, dy = by - ay, r;
      r = x0 - ax;
      if (!dx && r > 0) return;
      r /= dx;
      if (dx < 0) {
        if (r < t0) return;
        if (r < t1) t1 = r;
      } else if (dx > 0) {
        if (r > t1) return;
        if (r > t0) t0 = r;
      }
      r = x1 - ax;
      if (!dx && r < 0) return;
      r /= dx;
      if (dx < 0) {
        if (r > t1) return;
        if (r > t0) t0 = r;
      } else if (dx > 0) {
        if (r < t0) return;
        if (r < t1) t1 = r;
      }
      r = y0 - ay;
      if (!dy && r > 0) return;
      r /= dy;
      if (dy < 0) {
        if (r < t0) return;
        if (r < t1) t1 = r;
      } else if (dy > 0) {
        if (r > t1) return;
        if (r > t0) t0 = r;
      }
      r = y1 - ay;
      if (!dy && r < 0) return;
      r /= dy;
      if (dy < 0) {
        if (r > t1) return;
        if (r > t0) t0 = r;
      } else if (dy > 0) {
        if (r < t0) return;
        if (r < t1) t1 = r;
      }
      if (t0 > 0) line.a = {
        x: ax + t0 * dx,
        y: ay + t0 * dy
      };
      if (t1 < 1) line.b = {
        x: ax + t1 * dx,
        y: ay + t1 * dy
      };
      return line;
    };
  }
  var d3_geo_clipExtentMAX = 1e9;
  d3.geo.clipExtent = function() {
    var x0, y0, x1, y1, stream, clip, clipExtent = {
      stream: function(output) {
        if (stream) stream.valid = false;
        stream = clip(output);
        stream.valid = true;
        return stream;
      },
      extent: function(_) {
        if (!arguments.length) return [ [ x0, y0 ], [ x1, y1 ] ];
        clip = d3_geo_clipExtent(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]);
        if (stream) stream.valid = false, stream = null;
        return clipExtent;
      }
    };
    return clipExtent.extent([ [ 0, 0 ], [ 960, 500 ] ]);
  };
  function d3_geo_clipExtent(x0, y0, x1, y1) {
    return function(listener) {
      var listener_ = listener, bufferListener = d3_geo_clipBufferListener(), clipLine = d3_geom_clipLine(x0, y0, x1, y1), segments, polygon, ring;
      var clip = {
        point: point,
        lineStart: lineStart,
        lineEnd: lineEnd,
        polygonStart: function() {
          listener = bufferListener;
          segments = [];
          polygon = [];
          clean = true;
        },
        polygonEnd: function() {
          listener = listener_;
          segments = d3.merge(segments);
          var clipStartInside = insidePolygon([ x0, y1 ]), inside = clean && clipStartInside, visible = segments.length;
          if (inside || visible) {
            listener.polygonStart();
            if (inside) {
              listener.lineStart();
              interpolate(null, null, 1, listener);
              listener.lineEnd();
            }
            if (visible) {
              d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener);
            }
            listener.polygonEnd();
          }
          segments = polygon = ring = null;
        }
      };
      function insidePolygon(p) {
        var wn = 0, n = polygon.length, y = p[1];
        for (var i = 0; i < n; ++i) {
          for (var j = 1, v = polygon[i], m = v.length, a = v[0], b; j < m; ++j) {
            b = v[j];
            if (a[1] <= y) {
              if (b[1] > y && d3_cross2d(a, b, p) > 0) ++wn;
            } else {
              if (b[1] <= y && d3_cross2d(a, b, p) < 0) --wn;
            }
            a = b;
          }
        }
        return wn !== 0;
      }
      function interpolate(from, to, direction, listener) {
        var a = 0, a1 = 0;
        if (from == null || (a = corner(from, direction)) !== (a1 = corner(to, direction)) || comparePoints(from, to) < 0 ^ direction > 0) {
          do {
            listener.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0);
          } while ((a = (a + direction + 4) % 4) !== a1);
        } else {
          listener.point(to[0], to[1]);
        }
      }
      function pointVisible(x, y) {
        return x0 <= x && x <= x1 && y0 <= y && y <= y1;
      }
      function point(x, y) {
        if (pointVisible(x, y)) listener.point(x, y);
      }
      var x__, y__, v__, x_, y_, v_, first, clean;
      function lineStart() {
        clip.point = linePoint;
        if (polygon) polygon.push(ring = []);
        first = true;
        v_ = false;
        x_ = y_ = NaN;
      }
      function lineEnd() {
        if (segments) {
          linePoint(x__, y__);
          if (v__ && v_) bufferListener.rejoin();
          segments.push(bufferListener.buffer());
        }
        clip.point = point;
        if (v_) listener.lineEnd();
      }
      function linePoint(x, y) {
        x = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, x));
        y = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, y));
        var v = pointVisible(x, y);
        if (polygon) ring.push([ x, y ]);
        if (first) {
          x__ = x, y__ = y, v__ = v;
          first = false;
          if (v) {
            listener.lineStart();
            listener.point(x, y);
          }
        } else {
          if (v && v_) listener.point(x, y); else {
            var l = {
              a: {
                x: x_,
                y: y_
              },
              b: {
                x: x,
                y: y
              }
            };
            if (clipLine(l)) {
              if (!v_) {
                listener.lineStart();
                listener.point(l.a.x, l.a.y);
              }
              listener.point(l.b.x, l.b.y);
              if (!v) listener.lineEnd();
              clean = false;
            } else if (v) {
              listener.lineStart();
              listener.point(x, y);
              clean = false;
            }
          }
        }
        x_ = x, y_ = y, v_ = v;
      }
      return clip;
    };
    function corner(p, direction) {
      return abs(p[0] - x0) < ε ? direction > 0 ? 0 : 3 : abs(p[0] - x1) < ε ? direction > 0 ? 2 : 1 : abs(p[1] - y0) < ε ? direction > 0 ? 1 : 0 : direction > 0 ? 3 : 2;
    }
    function compare(a, b) {
      return comparePoints(a.x, b.x);
    }
    function comparePoints(a, b) {
      var ca = corner(a, 1), cb = corner(b, 1);
      return ca !== cb ? ca - cb : ca === 0 ? b[1] - a[1] : ca === 1 ? a[0] - b[0] : ca === 2 ? a[1] - b[1] : b[0] - a[0];
    }
  }
  function d3_geo_compose(a, b) {
    function compose(x, y) {
      return x = a(x, y), b(x[0], x[1]);
    }
    if (a.invert && b.invert) compose.invert = function(x, y) {
      return x = b.invert(x, y), x && a.invert(x[0], x[1]);
    };
    return compose;
  }
  function d3_geo_conic(projectAt) {
    var φ0 = 0, φ1 = π / 3, m = d3_geo_projectionMutator(projectAt), p = m(φ0, φ1);
    p.parallels = function(_) {
      if (!arguments.length) return [ φ0 / π * 180, φ1 / π * 180 ];
      return m(φ0 = _[0] * π / 180, φ1 = _[1] * π / 180);
    };
    return p;
  }
  function d3_geo_conicEqualArea(φ0, φ1) {
    var sinφ0 = Math.sin(φ0), n = (sinφ0 + Math.sin(φ1)) / 2, C = 1 + sinφ0 * (2 * n - sinφ0), ρ0 = Math.sqrt(C) / n;
    function forward(λ, φ) {
      var ρ = Math.sqrt(C - 2 * n * Math.sin(φ)) / n;
      return [ ρ * Math.sin(λ *= n), ρ0 - ρ * Math.cos(λ) ];
    }
    forward.invert = function(x, y) {
      var ρ0_y = ρ0 - y;
      return [ Math.atan2(x, ρ0_y) / n, d3_asin((C - (x * x + ρ0_y * ρ0_y) * n * n) / (2 * n)) ];
    };
    return forward;
  }
  (d3.geo.conicEqualArea = function() {
    return d3_geo_conic(d3_geo_conicEqualArea);
  }).raw = d3_geo_conicEqualArea;
  d3.geo.albers = function() {
    return d3.geo.conicEqualArea().rotate([ 96, 0 ]).center([ -.6, 38.7 ]).parallels([ 29.5, 45.5 ]).scale(1070);
  };
  d3.geo.albersUsa = function() {
    var lower48 = d3.geo.albers();
    var alaska = d3.geo.conicEqualArea().rotate([ 154, 0 ]).center([ -2, 58.5 ]).parallels([ 55, 65 ]);
    var hawaii = d3.geo.conicEqualArea().rotate([ 157, 0 ]).center([ -3, 19.9 ]).parallels([ 8, 18 ]);
    var point, pointStream = {
      point: function(x, y) {
        point = [ x, y ];
      }
    }, lower48Point, alaskaPoint, hawaiiPoint;
    function albersUsa(coordinates) {
      var x = coordinates[0], y = coordinates[1];
      point = null;
      (lower48Point(x, y), point) || (alaskaPoint(x, y), point) || hawaiiPoint(x, y);
      return point;
    }
    albersUsa.invert = function(coordinates) {
      var k = lower48.scale(), t = lower48.translate(), x = (coordinates[0] - t[0]) / k, y = (coordinates[1] - t[1]) / k;
      return (y >= .12 && y < .234 && x >= -.425 && x < -.214 ? alaska : y >= .166 && y < .234 && x >= -.214 && x < -.115 ? hawaii : lower48).invert(coordinates);
    };
    albersUsa.stream = function(stream) {
      var lower48Stream = lower48.stream(stream), alaskaStream = alaska.stream(stream), hawaiiStream = hawaii.stream(stream);
      return {
        point: function(x, y) {
          lower48Stream.point(x, y);
          alaskaStream.point(x, y);
          hawaiiStream.point(x, y);
        },
        sphere: function() {
          lower48Stream.sphere();
          alaskaStream.sphere();
          hawaiiStream.sphere();
        },
        lineStart: function() {
          lower48Stream.lineStart();
          alaskaStream.lineStart();
          hawaiiStream.lineStart();
        },
        lineEnd: function() {
          lower48Stream.lineEnd();
          alaskaStream.lineEnd();
          hawaiiStream.lineEnd();
        },
        polygonStart: function() {
          lower48Stream.polygonStart();
          alaskaStream.polygonStart();
          hawaiiStream.polygonStart();
        },
        polygonEnd: function() {
          lower48Stream.polygonEnd();
          alaskaStream.polygonEnd();
          hawaiiStream.polygonEnd();
        }
      };
    };
    albersUsa.precision = function(_) {
      if (!arguments.length) return lower48.precision();
      lower48.precision(_);
      alaska.precision(_);
      hawaii.precision(_);
      return albersUsa;
    };
    albersUsa.scale = function(_) {
      if (!arguments.length) return lower48.scale();
      lower48.scale(_);
      alaska.scale(_ * .35);
      hawaii.scale(_);
      return albersUsa.translate(lower48.translate());
    };
    albersUsa.translate = function(_) {
      if (!arguments.length) return lower48.translate();
      var k = lower48.scale(), x = +_[0], y = +_[1];
      lower48Point = lower48.translate(_).clipExtent([ [ x - .455 * k, y - .238 * k ], [ x + .455 * k, y + .238 * k ] ]).stream(pointStream).point;
      alaskaPoint = alaska.translate([ x - .307 * k, y + .201 * k ]).clipExtent([ [ x - .425 * k + ε, y + .12 * k + ε ], [ x - .214 * k - ε, y + .234 * k - ε ] ]).stream(pointStream).point;
      hawaiiPoint = hawaii.translate([ x - .205 * k, y + .212 * k ]).clipExtent([ [ x - .214 * k + ε, y + .166 * k + ε ], [ x - .115 * k - ε, y + .234 * k - ε ] ]).stream(pointStream).point;
      return albersUsa;
    };
    return albersUsa.scale(1070);
  };
  var d3_geo_pathAreaSum, d3_geo_pathAreaPolygon, d3_geo_pathArea = {
    point: d3_noop,
    lineStart: d3_noop,
    lineEnd: d3_noop,
    polygonStart: function() {
      d3_geo_pathAreaPolygon = 0;
      d3_geo_pathArea.lineStart = d3_geo_pathAreaRingStart;
    },
    polygonEnd: function() {
      d3_geo_pathArea.lineStart = d3_geo_pathArea.lineEnd = d3_geo_pathArea.point = d3_noop;
      d3_geo_pathAreaSum += abs(d3_geo_pathAreaPolygon / 2);
    }
  };
  function d3_geo_pathAreaRingStart() {
    var x00, y00, x0, y0;
    d3_geo_pathArea.point = function(x, y) {
      d3_geo_pathArea.point = nextPoint;
      x00 = x0 = x, y00 = y0 = y;
    };
    function nextPoint(x, y) {
      d3_geo_pathAreaPolygon += y0 * x - x0 * y;
      x0 = x, y0 = y;
    }
    d3_geo_pathArea.lineEnd = function() {
      nextPoint(x00, y00);
    };
  }
  var d3_geo_pathBoundsX0, d3_geo_pathBoundsY0, d3_geo_pathBoundsX1, d3_geo_pathBoundsY1;
  var d3_geo_pathBounds = {
    point: d3_geo_pathBoundsPoint,
    lineStart: d3_noop,
    lineEnd: d3_noop,
    polygonStart: d3_noop,
    polygonEnd: d3_noop
  };
  function d3_geo_pathBoundsPoint(x, y) {
    if (x < d3_geo_pathBoundsX0) d3_geo_pathBoundsX0 = x;
    if (x > d3_geo_pathBoundsX1) d3_geo_pathBoundsX1 = x;
    if (y < d3_geo_pathBoundsY0) d3_geo_pathBoundsY0 = y;
    if (y > d3_geo_pathBoundsY1) d3_geo_pathBoundsY1 = y;
  }
  function d3_geo_pathBuffer() {
    var pointCircle = d3_geo_pathBufferCircle(4.5), buffer = [];
    var stream = {
      point: point,
      lineStart: function() {
        stream.point = pointLineStart;
      },
      lineEnd: lineEnd,
      polygonStart: function() {
        stream.lineEnd = lineEndPolygon;
      },
      polygonEnd: function() {
        stream.lineEnd = lineEnd;
        stream.point = point;
      },
      pointRadius: function(_) {
        pointCircle = d3_geo_pathBufferCircle(_);
        return stream;
      },
      result: function() {
        if (buffer.length) {
          var result = buffer.join("");
          buffer = [];
          return result;
        }
      }
    };
    function point(x, y) {
      buffer.push("M", x, ",", y, pointCircle);
    }
    function pointLineStart(x, y) {
      buffer.push("M", x, ",", y);
      stream.point = pointLine;
    }
    function pointLine(x, y) {
      buffer.push("L", x, ",", y);
    }
    function lineEnd() {
      stream.point = point;
    }
    function lineEndPolygon() {
      buffer.push("Z");
    }
    return stream;
  }
  function d3_geo_pathBufferCircle(radius) {
    return "m0," + radius + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius + "z";
  }
  var d3_geo_pathCentroid = {
    point: d3_geo_pathCentroidPoint,
    lineStart: d3_geo_pathCentroidLineStart,
    lineEnd: d3_geo_pathCentroidLineEnd,
    polygonStart: function() {
      d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidRingStart;
    },
    polygonEnd: function() {
      d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint;
      d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidLineStart;
      d3_geo_pathCentroid.lineEnd = d3_geo_pathCentroidLineEnd;
    }
  };
  function d3_geo_pathCentroidPoint(x, y) {
    d3_geo_centroidX0 += x;
    d3_geo_centroidY0 += y;
    ++d3_geo_centroidZ0;
  }
  function d3_geo_pathCentroidLineStart() {
    var x0, y0;
    d3_geo_pathCentroid.point = function(x, y) {
      d3_geo_pathCentroid.point = nextPoint;
      d3_geo_pathCentroidPoint(x0 = x, y0 = y);
    };
    function nextPoint(x, y) {
      var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy);
      d3_geo_centroidX1 += z * (x0 + x) / 2;
      d3_geo_centroidY1 += z * (y0 + y) / 2;
      d3_geo_centroidZ1 += z;
      d3_geo_pathCentroidPoint(x0 = x, y0 = y);
    }
  }
  function d3_geo_pathCentroidLineEnd() {
    d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint;
  }
  function d3_geo_pathCentroidRingStart() {
    var x00, y00, x0, y0;
    d3_geo_pathCentroid.point = function(x, y) {
      d3_geo_pathCentroid.point = nextPoint;
      d3_geo_pathCentroidPoint(x00 = x0 = x, y00 = y0 = y);
    };
    function nextPoint(x, y) {
      var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy);
      d3_geo_centroidX1 += z * (x0 + x) / 2;
      d3_geo_centroidY1 += z * (y0 + y) / 2;
      d3_geo_centroidZ1 += z;
      z = y0 * x - x0 * y;
      d3_geo_centroidX2 += z * (x0 + x);
      d3_geo_centroidY2 += z * (y0 + y);
      d3_geo_centroidZ2 += z * 3;
      d3_geo_pathCentroidPoint(x0 = x, y0 = y);
    }
    d3_geo_pathCentroid.lineEnd = function() {
      nextPoint(x00, y00);
    };
  }
  function d3_geo_pathContext(context) {
    var pointRadius = 4.5;
    var stream = {
      point: point,
      lineStart: function() {
        stream.point = pointLineStart;
      },
      lineEnd: lineEnd,
      polygonStart: function() {
        stream.lineEnd = lineEndPolygon;
      },
      polygonEnd: function() {
        stream.lineEnd = lineEnd;
        stream.point = point;
      },
      pointRadius: function(_) {
        pointRadius = _;
        return stream;
      },
      result: d3_noop
    };
    function point(x, y) {
      context.moveTo(x, y);
      context.arc(x, y, pointRadius, 0, τ);
    }
    function pointLineStart(x, y) {
      context.moveTo(x, y);
      stream.point = pointLine;
    }
    function pointLine(x, y) {
      context.lineTo(x, y);
    }
    function lineEnd() {
      stream.point = point;
    }
    function lineEndPolygon() {
      context.closePath();
    }
    return stream;
  }
  function d3_geo_resample(project) {
    var δ2 = .5, cosMinDistance = Math.cos(30 * d3_radians), maxDepth = 16;
    function resample(stream) {
      return (maxDepth ? resampleRecursive : resampleNone)(stream);
    }
    function resampleNone(stream) {
      return d3_geo_transformPoint(stream, function(x, y) {
        x = project(x, y);
        stream.point(x[0], x[1]);
      });
    }
    function resampleRecursive(stream) {
      var λ00, φ00, x00, y00, a00, b00, c00, λ0, x0, y0, a0, b0, c0;
      var resample = {
        point: point,
        lineStart: lineStart,
        lineEnd: lineEnd,
        polygonStart: function() {
          stream.polygonStart();
          resample.lineStart = ringStart;
        },
        polygonEnd: function() {
          stream.polygonEnd();
          resample.lineStart = lineStart;
        }
      };
      function point(x, y) {
        x = project(x, y);
        stream.point(x[0], x[1]);
      }
      function lineStart() {
        x0 = NaN;
        resample.point = linePoint;
        stream.lineStart();
      }
      function linePoint(λ, φ) {
        var c = d3_geo_cartesian([ λ, φ ]), p = project(λ, φ);
        resampleLineTo(x0, y0, λ0, a0, b0, c0, x0 = p[0], y0 = p[1], λ0 = λ, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream);
        stream.point(x0, y0);
      }
      function lineEnd() {
        resample.point = point;
        stream.lineEnd();
      }
      function ringStart() {
        lineStart();
        resample.point = ringPoint;
        resample.lineEnd = ringEnd;
      }
      function ringPoint(λ, φ) {
        linePoint(λ00 = λ, φ00 = φ), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0;
        resample.point = linePoint;
      }
      function ringEnd() {
        resampleLineTo(x0, y0, λ0, a0, b0, c0, x00, y00, λ00, a00, b00, c00, maxDepth, stream);
        resample.lineEnd = lineEnd;
        lineEnd();
      }
      return resample;
    }
    function resampleLineTo(x0, y0, λ0, a0, b0, c0, x1, y1, λ1, a1, b1, c1, depth, stream) {
      var dx = x1 - x0, dy = y1 - y0, d2 = dx * dx + dy * dy;
      if (d2 > 4 * δ2 && depth--) {
        var a = a0 + a1, b = b0 + b1, c = c0 + c1, m = Math.sqrt(a * a + b * b + c * c), φ2 = Math.asin(c /= m), λ2 = abs(abs(c) - 1) < ε || abs(λ0 - λ1) < ε ? (λ0 + λ1) / 2 : Math.atan2(b, a), p = project(λ2, φ2), x2 = p[0], y2 = p[1], dx2 = x2 - x0, dy2 = y2 - y0, dz = dy * dx2 - dx * dy2;
        if (dz * dz / d2 > δ2 || abs((dx * dx2 + dy * dy2) / d2 - .5) > .3 || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) {
          resampleLineTo(x0, y0, λ0, a0, b0, c0, x2, y2, λ2, a /= m, b /= m, c, depth, stream);
          stream.point(x2, y2);
          resampleLineTo(x2, y2, λ2, a, b, c, x1, y1, λ1, a1, b1, c1, depth, stream);
        }
      }
    }
    resample.precision = function(_) {
      if (!arguments.length) return Math.sqrt(δ2);
      maxDepth = (δ2 = _ * _) > 0 && 16;
      return resample;
    };
    return resample;
  }
  d3.geo.path = function() {
    var pointRadius = 4.5, projection, context, projectStream, contextStream, cacheStream;
    function path(object) {
      if (object) {
        if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments));
        if (!cacheStream || !cacheStream.valid) cacheStream = projectStream(contextStream);
        d3.geo.stream(object, cacheStream);
      }
      return contextStream.result();
    }
    path.area = function(object) {
      d3_geo_pathAreaSum = 0;
      d3.geo.stream(object, projectStream(d3_geo_pathArea));
      return d3_geo_pathAreaSum;
    };
    path.centroid = function(object) {
      d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 = d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 = d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0;
      d3.geo.stream(object, projectStream(d3_geo_pathCentroid));
      return d3_geo_centroidZ2 ? [ d3_geo_centroidX2 / d3_geo_centroidZ2, d3_geo_centroidY2 / d3_geo_centroidZ2 ] : d3_geo_centroidZ1 ? [ d3_geo_centroidX1 / d3_geo_centroidZ1, d3_geo_centroidY1 / d3_geo_centroidZ1 ] : d3_geo_centroidZ0 ? [ d3_geo_centroidX0 / d3_geo_centroidZ0, d3_geo_centroidY0 / d3_geo_centroidZ0 ] : [ NaN, NaN ];
    };
    path.bounds = function(object) {
      d3_geo_pathBoundsX1 = d3_geo_pathBoundsY1 = -(d3_geo_pathBoundsX0 = d3_geo_pathBoundsY0 = Infinity);
      d3.geo.stream(object, projectStream(d3_geo_pathBounds));
      return [ [ d3_geo_pathBoundsX0, d3_geo_pathBoundsY0 ], [ d3_geo_pathBoundsX1, d3_geo_pathBoundsY1 ] ];
    };
    path.projection = function(_) {
      if (!arguments.length) return projection;
      projectStream = (projection = _) ? _.stream || d3_geo_pathProjectStream(_) : d3_identity;
      return reset();
    };
    path.context = function(_) {
      if (!arguments.length) return context;
      contextStream = (context = _) == null ? new d3_geo_pathBuffer() : new d3_geo_pathContext(_);
      if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius);
      return reset();
    };
    path.pointRadius = function(_) {
      if (!arguments.length) return pointRadius;
      pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_);
      return path;
    };
    function reset() {
      cacheStream = null;
      return path;
    }
    return path.projection(d3.geo.albersUsa()).context(null);
  };
  function d3_geo_pathProjectStream(project) {
    var resample = d3_geo_resample(function(x, y) {
      return project([ x * d3_degrees, y * d3_degrees ]);
    });
    return function(stream) {
      return d3_geo_projectionRadians(resample(stream));
    };
  }
  d3.geo.transform = function(methods) {
    return {
      stream: function(stream) {
        var transform = new d3_geo_transform(stream);
        for (var k in methods) transform[k] = methods[k];
        return transform;
      }
    };
  };
  function d3_geo_transform(stream) {
    this.stream = stream;
  }
  d3_geo_transform.prototype = {
    point: function(x, y) {
      this.stream.point(x, y);
    },
    sphere: function() {
      this.stream.sphere();
    },
    lineStart: function() {
      this.stream.lineStart();
    },
    lineEnd: function() {
      this.stream.lineEnd();
    },
    polygonStart: function() {
      this.stream.polygonStart();
    },
    polygonEnd: function() {
      this.stream.polygonEnd();
    }
  };
  function d3_geo_transformPoint(stream, point) {
    return {
      point: point,
      sphere: function() {
        stream.sphere();
      },
      lineStart: function() {
        stream.lineStart();
      },
      lineEnd: function() {
        stream.lineEnd();
      },
      polygonStart: function() {
        stream.polygonStart();
      },
      polygonEnd: function() {
        stream.polygonEnd();
      }
    };
  }
  d3.geo.projection = d3_geo_projection;
  d3.geo.projectionMutator = d3_geo_projectionMutator;
  function d3_geo_projection(project) {
    return d3_geo_projectionMutator(function() {
      return project;
    })();
  }
  function d3_geo_projectionMutator(projectAt) {
    var project, rotate, projectRotate, projectResample = d3_geo_resample(function(x, y) {
      x = project(x, y);
      return [ x[0] * k + δx, δy - x[1] * k ];
    }), k = 150, x = 480, y = 250, λ = 0, φ = 0, δλ = 0, δφ = 0, δγ = 0, δx, δy, preclip = d3_geo_clipAntimeridian, postclip = d3_identity, clipAngle = null, clipExtent = null, stream;
    function projection(point) {
      point = projectRotate(point[0] * d3_radians, point[1] * d3_radians);
      return [ point[0] * k + δx, δy - point[1] * k ];
    }
    function invert(point) {
      point = projectRotate.invert((point[0] - δx) / k, (δy - point[1]) / k);
      return point && [ point[0] * d3_degrees, point[1] * d3_degrees ];
    }
    projection.stream = function(output) {
      if (stream) stream.valid = false;
      stream = d3_geo_projectionRadians(preclip(rotate, projectResample(postclip(output))));
      stream.valid = true;
      return stream;
    };
    projection.clipAngle = function(_) {
      if (!arguments.length) return clipAngle;
      preclip = _ == null ? (clipAngle = _, d3_geo_clipAntimeridian) : d3_geo_clipCircle((clipAngle = +_) * d3_radians);
      return invalidate();
    };
    projection.clipExtent = function(_) {
      if (!arguments.length) return clipExtent;
      clipExtent = _;
      postclip = _ ? d3_geo_clipExtent(_[0][0], _[0][1], _[1][0], _[1][1]) : d3_identity;
      return invalidate();
    };
    projection.scale = function(_) {
      if (!arguments.length) return k;
      k = +_;
      return reset();
    };
    projection.translate = function(_) {
      if (!arguments.length) return [ x, y ];
      x = +_[0];
      y = +_[1];
      return reset();
    };
    projection.center = function(_) {
      if (!arguments.length) return [ λ * d3_degrees, φ * d3_degrees ];
      λ = _[0] % 360 * d3_radians;
      φ = _[1] % 360 * d3_radians;
      return reset();
    };
    projection.rotate = function(_) {
      if (!arguments.length) return [ δλ * d3_degrees, δφ * d3_degrees, δγ * d3_degrees ];
      δλ = _[0] % 360 * d3_radians;
      δφ = _[1] % 360 * d3_radians;
      δγ = _.length > 2 ? _[2] % 360 * d3_radians : 0;
      return reset();
    };
    d3.rebind(projection, projectResample, "precision");
    function reset() {
      projectRotate = d3_geo_compose(rotate = d3_geo_rotation(δλ, δφ, δγ), project);
      var center = project(λ, φ);
      δx = x - center[0] * k;
      δy = y + center[1] * k;
      return invalidate();
    }
    function invalidate() {
      if (stream) stream.valid = false, stream = null;
      return projection;
    }
    return function() {
      project = projectAt.apply(this, arguments);
      projection.invert = project.invert && invert;
      return reset();
    };
  }
  function d3_geo_projectionRadians(stream) {
    return d3_geo_transformPoint(stream, function(x, y) {
      stream.point(x * d3_radians, y * d3_radians);
    });
  }
  function d3_geo_equirectangular(λ, φ) {
    return [ λ, φ ];
  }
  (d3.geo.equirectangular = function() {
    return d3_geo_projection(d3_geo_equirectangular);
  }).raw = d3_geo_equirectangular.invert = d3_geo_equirectangular;
  d3.geo.rotation = function(rotate) {
    rotate = d3_geo_rotation(rotate[0] % 360 * d3_radians, rotate[1] * d3_radians, rotate.length > 2 ? rotate[2] * d3_radians : 0);
    function forward(coordinates) {
      coordinates = rotate(coordinates[0] * d3_radians, coordinates[1] * d3_radians);
      return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates;
    }
    forward.invert = function(coordinates) {
      coordinates = rotate.invert(coordinates[0] * d3_radians, coordinates[1] * d3_radians);
      return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates;
    };
    return forward;
  };
  function d3_geo_identityRotation(λ, φ) {
    return [ λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ ];
  }
  d3_geo_identityRotation.invert = d3_geo_equirectangular;
  function d3_geo_rotation(δλ, δφ, δγ) {
    return δλ ? δφ || δγ ? d3_geo_compose(d3_geo_rotationλ(δλ), d3_geo_rotationφγ(δφ, δγ)) : d3_geo_rotationλ(δλ) : δφ || δγ ? d3_geo_rotationφγ(δφ, δγ) : d3_geo_identityRotation;
  }
  function d3_geo_forwardRotationλ(δλ) {
    return function(λ, φ) {
      return λ += δλ, [ λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ ];
    };
  }
  function d3_geo_rotationλ(δλ) {
    var rotation = d3_geo_forwardRotationλ(δλ);
    rotation.invert = d3_geo_forwardRotationλ(-δλ);
    return rotation;
  }
  function d3_geo_rotationφγ(δφ, δγ) {
    var cosδφ = Math.cos(δφ), sinδφ = Math.sin(δφ), cosδγ = Math.cos(δγ), sinδγ = Math.sin(δγ);
    function rotation(λ, φ) {
      var cosφ = Math.cos(φ), x = Math.cos(λ) * cosφ, y = Math.sin(λ) * cosφ, z = Math.sin(φ), k = z * cosδφ + x * sinδφ;
      return [ Math.atan2(y * cosδγ - k * sinδγ, x * cosδφ - z * sinδφ), d3_asin(k * cosδγ + y * sinδγ) ];
    }
    rotation.invert = function(λ, φ) {
      var cosφ = Math.cos(φ), x = Math.cos(λ) * cosφ, y = Math.sin(λ) * cosφ, z = Math.sin(φ), k = z * cosδγ - y * sinδγ;
      return [ Math.atan2(y * cosδγ + z * sinδγ, x * cosδφ + k * sinδφ), d3_asin(k * cosδφ - x * sinδφ) ];
    };
    return rotation;
  }
  d3.geo.circle = function() {
    var origin = [ 0, 0 ], angle, precision = 6, interpolate;
    function circle() {
      var center = typeof origin === "function" ? origin.apply(this, arguments) : origin, rotate = d3_geo_rotation(-center[0] * d3_radians, -center[1] * d3_radians, 0).invert, ring = [];
      interpolate(null, null, 1, {
        point: function(x, y) {
          ring.push(x = rotate(x, y));
          x[0] *= d3_degrees, x[1] *= d3_degrees;
        }
      });
      return {
        type: "Polygon",
        coordinates: [ ring ]
      };
    }
    circle.origin = function(x) {
      if (!arguments.length) return origin;
      origin = x;
      return circle;
    };
    circle.angle = function(x) {
      if (!arguments.length) return angle;
      interpolate = d3_geo_circleInterpolate((angle = +x) * d3_radians, precision * d3_radians);
      return circle;
    };
    circle.precision = function(_) {
      if (!arguments.length) return precision;
      interpolate = d3_geo_circleInterpolate(angle * d3_radians, (precision = +_) * d3_radians);
      return circle;
    };
    return circle.angle(90);
  };
  function d3_geo_circleInterpolate(radius, precision) {
    var cr = Math.cos(radius), sr = Math.sin(radius);
    return function(from, to, direction, listener) {
      var step = direction * precision;
      if (from != null) {
        from = d3_geo_circleAngle(cr, from);
        to = d3_geo_circleAngle(cr, to);
        if (direction > 0 ? from < to : from > to) from += direction * τ;
      } else {
        from = radius + direction * τ;
        to = radius - .5 * step;
      }
      for (var point, t = from; direction > 0 ? t > to : t < to; t -= step) {
        listener.point((point = d3_geo_spherical([ cr, -sr * Math.cos(t), -sr * Math.sin(t) ]))[0], point[1]);
      }
    };
  }
  function d3_geo_circleAngle(cr, point) {
    var a = d3_geo_cartesian(point);
    a[0] -= cr;
    d3_geo_cartesianNormalize(a);
    var angle = d3_acos(-a[1]);
    return ((-a[2] < 0 ? -angle : angle) + 2 * Math.PI - ε) % (2 * Math.PI);
  }
  d3.geo.distance = function(a, b) {
    var Δλ = (b[0] - a[0]) * d3_radians, φ0 = a[1] * d3_radians, φ1 = b[1] * d3_radians, sinΔλ = Math.sin(Δλ), cosΔλ = Math.cos(Δλ), sinφ0 = Math.sin(φ0), cosφ0 = Math.cos(φ0), sinφ1 = Math.sin(φ1), cosφ1 = Math.cos(φ1), t;
    return Math.atan2(Math.sqrt((t = cosφ1 * sinΔλ) * t + (t = cosφ0 * sinφ1 - sinφ0 * cosφ1 * cosΔλ) * t), sinφ0 * sinφ1 + cosφ0 * cosφ1 * cosΔλ);
  };
  d3.geo.graticule = function() {
    var x1, x0, X1, X0, y1, y0, Y1, Y0, dx = 10, dy = dx, DX = 90, DY = 360, x, y, X, Y, precision = 2.5;
    function graticule() {
      return {
        type: "MultiLineString",
        coordinates: lines()
      };
    }
    function lines() {
      return d3.range(Math.ceil(X0 / DX) * DX, X1, DX).map(X).concat(d3.range(Math.ceil(Y0 / DY) * DY, Y1, DY).map(Y)).concat(d3.range(Math.ceil(x0 / dx) * dx, x1, dx).filter(function(x) {
        return abs(x % DX) > ε;
      }).map(x)).concat(d3.range(Math.ceil(y0 / dy) * dy, y1, dy).filter(function(y) {
        return abs(y % DY) > ε;
      }).map(y));
    }
    graticule.lines = function() {
      return lines().map(function(coordinates) {
        return {
          type: "LineString",
          coordinates: coordinates
        };
      });
    };
    graticule.outline = function() {
      return {
        type: "Polygon",
        coordinates: [ X(X0).concat(Y(Y1).slice(1), X(X1).reverse().slice(1), Y(Y0).reverse().slice(1)) ]
      };
    };
    graticule.extent = function(_) {
      if (!arguments.length) return graticule.minorExtent();
      return graticule.majorExtent(_).minorExtent(_);
    };
    graticule.majorExtent = function(_) {
      if (!arguments.length) return [ [ X0, Y0 ], [ X1, Y1 ] ];
      X0 = +_[0][0], X1 = +_[1][0];
      Y0 = +_[0][1], Y1 = +_[1][1];
      if (X0 > X1) _ = X0, X0 = X1, X1 = _;
      if (Y0 > Y1) _ = Y0, Y0 = Y1, Y1 = _;
      return graticule.precision(precision);
    };
    graticule.minorExtent = function(_) {
      if (!arguments.length) return [ [ x0, y0 ], [ x1, y1 ] ];
      x0 = +_[0][0], x1 = +_[1][0];
      y0 = +_[0][1], y1 = +_[1][1];
      if (x0 > x1) _ = x0, x0 = x1, x1 = _;
      if (y0 > y1) _ = y0, y0 = y1, y1 = _;
      return graticule.precision(precision);
    };
    graticule.step = function(_) {
      if (!arguments.length) return graticule.minorStep();
      return graticule.majorStep(_).minorStep(_);
    };
    graticule.majorStep = function(_) {
      if (!arguments.length) return [ DX, DY ];
      DX = +_[0], DY = +_[1];
      return graticule;
    };
    graticule.minorStep = function(_) {
      if (!arguments.length) return [ dx, dy ];
      dx = +_[0], dy = +_[1];
      return graticule;
    };
    graticule.precision = function(_) {
      if (!arguments.length) return precision;
      precision = +_;
      x = d3_geo_graticuleX(y0, y1, 90);
      y = d3_geo_graticuleY(x0, x1, precision);
      X = d3_geo_graticuleX(Y0, Y1, 90);
      Y = d3_geo_graticuleY(X0, X1, precision);
      return graticule;
    };
    return graticule.majorExtent([ [ -180, -90 + ε ], [ 180, 90 - ε ] ]).minorExtent([ [ -180, -80 - ε ], [ 180, 80 + ε ] ]);
  };
  function d3_geo_graticuleX(y0, y1, dy) {
    var y = d3.range(y0, y1 - ε, dy).concat(y1);
    return function(x) {
      return y.map(function(y) {
        return [ x, y ];
      });
    };
  }
  function d3_geo_graticuleY(x0, x1, dx) {
    var x = d3.range(x0, x1 - ε, dx).concat(x1);
    return function(y) {
      return x.map(function(x) {
        return [ x, y ];
      });
    };
  }
  function d3_source(d) {
    return d.source;
  }
  function d3_target(d) {
    return d.target;
  }
  d3.geo.greatArc = function() {
    var source = d3_source, source_, target = d3_target, target_;
    function greatArc() {
      return {
        type: "LineString",
        coordinates: [ source_ || source.apply(this, arguments), target_ || target.apply(this, arguments) ]
      };
    }
    greatArc.distance = function() {
      return d3.geo.distance(source_ || source.apply(this, arguments), target_ || target.apply(this, arguments));
    };
    greatArc.source = function(_) {
      if (!arguments.length) return source;
      source = _, source_ = typeof _ === "function" ? null : _;
      return greatArc;
    };
    greatArc.target = function(_) {
      if (!arguments.length) return target;
      target = _, target_ = typeof _ === "function" ? null : _;
      return greatArc;
    };
    greatArc.precision = function() {
      return arguments.length ? greatArc : 0;
    };
    return greatArc;
  };
  d3.geo.interpolate = function(source, target) {
    return d3_geo_interpolate(source[0] * d3_radians, source[1] * d3_radians, target[0] * d3_radians, target[1] * d3_radians);
  };
  function d3_geo_interpolate(x0, y0, x1, y1) {
    var cy0 = Math.cos(y0), sy0 = Math.sin(y0), cy1 = Math.cos(y1), sy1 = Math.sin(y1), kx0 = cy0 * Math.cos(x0), ky0 = cy0 * Math.sin(x0), kx1 = cy1 * Math.cos(x1), ky1 = cy1 * Math.sin(x1), d = 2 * Math.asin(Math.sqrt(d3_haversin(y1 - y0) + cy0 * cy1 * d3_haversin(x1 - x0))), k = 1 / Math.sin(d);
    var interpolate = d ? function(t) {
      var B = Math.sin(t *= d) * k, A = Math.sin(d - t) * k, x = A * kx0 + B * kx1, y = A * ky0 + B * ky1, z = A * sy0 + B * sy1;
      return [ Math.atan2(y, x) * d3_degrees, Math.atan2(z, Math.sqrt(x * x + y * y)) * d3_degrees ];
    } : function() {
      return [ x0 * d3_degrees, y0 * d3_degrees ];
    };
    interpolate.distance = d;
    return interpolate;
  }
  d3.geo.length = function(object) {
    d3_geo_lengthSum = 0;
    d3.geo.stream(object, d3_geo_length);
    return d3_geo_lengthSum;
  };
  var d3_geo_lengthSum;
  var d3_geo_length = {
    sphere: d3_noop,
    point: d3_noop,
    lineStart: d3_geo_lengthLineStart,
    lineEnd: d3_noop,
    polygonStart: d3_noop,
    polygonEnd: d3_noop
  };
  function d3_geo_lengthLineStart() {
    var λ0, sinφ0, cosφ0;
    d3_geo_length.point = function(λ, φ) {
      λ0 = λ * d3_radians, sinφ0 = Math.sin(φ *= d3_radians), cosφ0 = Math.cos(φ);
      d3_geo_length.point = nextPoint;
    };
    d3_geo_length.lineEnd = function() {
      d3_geo_length.point = d3_geo_length.lineEnd = d3_noop;
    };
    function nextPoint(λ, φ) {
      var sinφ = Math.sin(φ *= d3_radians), cosφ = Math.cos(φ), t = abs((λ *= d3_radians) - λ0), cosΔλ = Math.cos(t);
      d3_geo_lengthSum += Math.atan2(Math.sqrt((t = cosφ * Math.sin(t)) * t + (t = cosφ0 * sinφ - sinφ0 * cosφ * cosΔλ) * t), sinφ0 * sinφ + cosφ0 * cosφ * cosΔλ);
      λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ;
    }
  }
  function d3_geo_azimuthal(scale, angle) {
    function azimuthal(λ, φ) {
      var cosλ = Math.cos(λ), cosφ = Math.cos(φ), k = scale(cosλ * cosφ);
      return [ k * cosφ * Math.sin(λ), k * Math.sin(φ) ];
    }
    azimuthal.invert = function(x, y) {
      var ρ = Math.sqrt(x * x + y * y), c = angle(ρ), sinc = Math.sin(c), cosc = Math.cos(c);
      return [ Math.atan2(x * sinc, ρ * cosc), Math.asin(ρ && y * sinc / ρ) ];
    };
    return azimuthal;
  }
  var d3_geo_azimuthalEqualArea = d3_geo_azimuthal(function(cosλcosφ) {
    return Math.sqrt(2 / (1 + cosλcosφ));
  }, function(ρ) {
    return 2 * Math.asin(ρ / 2);
  });
  (d3.geo.azimuthalEqualArea = function() {
    return d3_geo_projection(d3_geo_azimuthalEqualArea);
  }).raw = d3_geo_azimuthalEqualArea;
  var d3_geo_azimuthalEquidistant = d3_geo_azimuthal(function(cosλcosφ) {
    var c = Math.acos(cosλcosφ);
    return c && c / Math.sin(c);
  }, d3_identity);
  (d3.geo.azimuthalEquidistant = function() {
    return d3_geo_projection(d3_geo_azimuthalEquidistant);
  }).raw = d3_geo_azimuthalEquidistant;
  function d3_geo_conicConformal(φ0, φ1) {
    var cosφ0 = Math.cos(φ0), t = function(φ) {
      return Math.tan(π / 4 + φ / 2);
    }, n = φ0 === φ1 ? Math.sin(φ0) : Math.log(cosφ0 / Math.cos(φ1)) / Math.log(t(φ1) / t(φ0)), F = cosφ0 * Math.pow(t(φ0), n) / n;
    if (!n) return d3_geo_mercator;
    function forward(λ, φ) {
      var ρ = abs(abs(φ) - halfπ) < ε ? 0 : F / Math.pow(t(φ), n);
      return [ ρ * Math.sin(n * λ), F - ρ * Math.cos(n * λ) ];
    }
    forward.invert = function(x, y) {
      var ρ0_y = F - y, ρ = d3_sgn(n) * Math.sqrt(x * x + ρ0_y * ρ0_y);
      return [ Math.atan2(x, ρ0_y) / n, 2 * Math.atan(Math.pow(F / ρ, 1 / n)) - halfπ ];
    };
    return forward;
  }
  (d3.geo.conicConformal = function() {
    return d3_geo_conic(d3_geo_conicConformal);
  }).raw = d3_geo_conicConformal;
  function d3_geo_conicEquidistant(φ0, φ1) {
    var cosφ0 = Math.cos(φ0), n = φ0 === φ1 ? Math.sin(φ0) : (cosφ0 - Math.cos(φ1)) / (φ1 - φ0), G = cosφ0 / n + φ0;
    if (abs(n) < ε) return d3_geo_equirectangular;
    function forward(λ, φ) {
      var ρ = G - φ;
      return [ ρ * Math.sin(n * λ), G - ρ * Math.cos(n * λ) ];
    }
    forward.invert = function(x, y) {
      var ρ0_y = G - y;
      return [ Math.atan2(x, ρ0_y) / n, G - d3_sgn(n) * Math.sqrt(x * x + ρ0_y * ρ0_y) ];
    };
    return forward;
  }
  (d3.geo.conicEquidistant = function() {
    return d3_geo_conic(d3_geo_conicEquidistant);
  }).raw = d3_geo_conicEquidistant;
  var d3_geo_gnomonic = d3_geo_azimuthal(function(cosλcosφ) {
    return 1 / cosλcosφ;
  }, Math.atan);
  (d3.geo.gnomonic = function() {
    return d3_geo_projection(d3_geo_gnomonic);
  }).raw = d3_geo_gnomonic;
  function d3_geo_mercator(λ, φ) {
    return [ λ, Math.log(Math.tan(π / 4 + φ / 2)) ];
  }
  d3_geo_mercator.invert = function(x, y) {
    return [ x, 2 * Math.atan(Math.exp(y)) - halfπ ];
  };
  function d3_geo_mercatorProjection(project) {
    var m = d3_geo_projection(project), scale = m.scale, translate = m.translate, clipExtent = m.clipExtent, clipAuto;
    m.scale = function() {
      var v = scale.apply(m, arguments);
      return v === m ? clipAuto ? m.clipExtent(null) : m : v;
    };
    m.translate = function() {
      var v = translate.apply(m, arguments);
      return v === m ? clipAuto ? m.clipExtent(null) : m : v;
    };
    m.clipExtent = function(_) {
      var v = clipExtent.apply(m, arguments);
      if (v === m) {
        if (clipAuto = _ == null) {
          var k = π * scale(), t = translate();
          clipExtent([ [ t[0] - k, t[1] - k ], [ t[0] + k, t[1] + k ] ]);
        }
      } else if (clipAuto) {
        v = null;
      }
      return v;
    };
    return m.clipExtent(null);
  }
  (d3.geo.mercator = function() {
    return d3_geo_mercatorProjection(d3_geo_mercator);
  }).raw = d3_geo_mercator;
  var d3_geo_orthographic = d3_geo_azimuthal(function() {
    return 1;
  }, Math.asin);
  (d3.geo.orthographic = function() {
    return d3_geo_projection(d3_geo_orthographic);
  }).raw = d3_geo_orthographic;
  var d3_geo_stereographic = d3_geo_azimuthal(function(cosλcosφ) {
    return 1 / (1 + cosλcosφ);
  }, function(ρ) {
    return 2 * Math.atan(ρ);
  });
  (d3.geo.stereographic = function() {
    return d3_geo_projection(d3_geo_stereographic);
  }).raw = d3_geo_stereographic;
  function d3_geo_transverseMercator(λ, φ) {
    return [ Math.log(Math.tan(π / 4 + φ / 2)), -λ ];
  }
  d3_geo_transverseMercator.invert = function(x, y) {
    return [ -y, 2 * Math.atan(Math.exp(x)) - halfπ ];
  };
  (d3.geo.transverseMercator = function() {
    var projection = d3_geo_mercatorProjection(d3_geo_transverseMercator), center = projection.center, rotate = projection.rotate;
    projection.center = function(_) {
      return _ ? center([ -_[1], _[0] ]) : (_ = center(), [ -_[1], _[0] ]);
    };
    projection.rotate = function(_) {
      return _ ? rotate([ _[0], _[1], _.length > 2 ? _[2] + 90 : 90 ]) : (_ = rotate(), 
      [ _[0], _[1], _[2] - 90 ]);
    };
    return projection.rotate([ 0, 0 ]);
  }).raw = d3_geo_transverseMercator;
  d3.geom = {};
  function d3_geom_pointX(d) {
    return d[0];
  }
  function d3_geom_pointY(d) {
    return d[1];
  }
  d3.geom.hull = function(vertices) {
    var x = d3_geom_pointX, y = d3_geom_pointY;
    if (arguments.length) return hull(vertices);
    function hull(data) {
      if (data.length < 3) return [];
      var fx = d3_functor(x), fy = d3_functor(y), i, n = data.length, points = [], flippedPoints = [];
      for (i = 0; i < n; i++) {
        points.push([ +fx.call(this, data[i], i), +fy.call(this, data[i], i), i ]);
      }
      points.sort(d3_geom_hullOrder);
      for (i = 0; i < n; i++) flippedPoints.push([ points[i][0], -points[i][1] ]);
      var upper = d3_geom_hullUpper(points), lower = d3_geom_hullUpper(flippedPoints);
      var skipLeft = lower[0] === upper[0], skipRight = lower[lower.length - 1] === upper[upper.length - 1], polygon = [];
      for (i = upper.length - 1; i >= 0; --i) polygon.push(data[points[upper[i]][2]]);
      for (i = +skipLeft; i < lower.length - skipRight; ++i) polygon.push(data[points[lower[i]][2]]);
      return polygon;
    }
    hull.x = function(_) {
      return arguments.length ? (x = _, hull) : x;
    };
    hull.y = function(_) {
      return arguments.length ? (y = _, hull) : y;
    };
    return hull;
  };
  function d3_geom_hullUpper(points) {
    var n = points.length, hull = [ 0, 1 ], hs = 2;
    for (var i = 2; i < n; i++) {
      while (hs > 1 && d3_cross2d(points[hull[hs - 2]], points[hull[hs - 1]], points[i]) <= 0) --hs;
      hull[hs++] = i;
    }
    return hull.slice(0, hs);
  }
  function d3_geom_hullOrder(a, b) {
    return a[0] - b[0] || a[1] - b[1];
  }
  d3.geom.polygon = function(coordinates) {
    d3_subclass(coordinates, d3_geom_polygonPrototype);
    return coordinates;
  };
  var d3_geom_polygonPrototype = d3.geom.polygon.prototype = [];
  d3_geom_polygonPrototype.area = function() {
    var i = -1, n = this.length, a, b = this[n - 1], area = 0;
    while (++i < n) {
      a = b;
      b = this[i];
      area += a[1] * b[0] - a[0] * b[1];
    }
    return area * .5;
  };
  d3_geom_polygonPrototype.centroid = function(k) {
    var i = -1, n = this.length, x = 0, y = 0, a, b = this[n - 1], c;
    if (!arguments.length) k = -1 / (6 * this.area());
    while (++i < n) {
      a = b;
      b = this[i];
      c = a[0] * b[1] - b[0] * a[1];
      x += (a[0] + b[0]) * c;
      y += (a[1] + b[1]) * c;
    }
    return [ x * k, y * k ];
  };
  d3_geom_polygonPrototype.clip = function(subject) {
    var input, closed = d3_geom_polygonClosed(subject), i = -1, n = this.length - d3_geom_polygonClosed(this), j, m, a = this[n - 1], b, c, d;
    while (++i < n) {
      input = subject.slice();
      subject.length = 0;
      b = this[i];
      c = input[(m = input.length - closed) - 1];
      j = -1;
      while (++j < m) {
        d = input[j];
        if (d3_geom_polygonInside(d, a, b)) {
          if (!d3_geom_polygonInside(c, a, b)) {
            subject.push(d3_geom_polygonIntersect(c, d, a, b));
          }
          subject.push(d);
        } else if (d3_geom_polygonInside(c, a, b)) {
          subject.push(d3_geom_polygonIntersect(c, d, a, b));
        }
        c = d;
      }
      if (closed) subject.push(subject[0]);
      a = b;
    }
    return subject;
  };
  function d3_geom_polygonInside(p, a, b) {
    return (b[0] - a[0]) * (p[1] - a[1]) < (b[1] - a[1]) * (p[0] - a[0]);
  }
  function d3_geom_polygonIntersect(c, d, a, b) {
    var x1 = c[0], x3 = a[0], x21 = d[0] - x1, x43 = b[0] - x3, y1 = c[1], y3 = a[1], y21 = d[1] - y1, y43 = b[1] - y3, ua = (x43 * (y1 - y3) - y43 * (x1 - x3)) / (y43 * x21 - x43 * y21);
    return [ x1 + ua * x21, y1 + ua * y21 ];
  }
  function d3_geom_polygonClosed(coordinates) {
    var a = coordinates[0], b = coordinates[coordinates.length - 1];
    return !(a[0] - b[0] || a[1] - b[1]);
  }
  var d3_geom_voronoiEdges, d3_geom_voronoiCells, d3_geom_voronoiBeaches, d3_geom_voronoiBeachPool = [], d3_geom_voronoiFirstCircle, d3_geom_voronoiCircles, d3_geom_voronoiCirclePool = [];
  function d3_geom_voronoiBeach() {
    d3_geom_voronoiRedBlackNode(this);
    this.edge = this.site = this.circle = null;
  }
  function d3_geom_voronoiCreateBeach(site) {
    var beach = d3_geom_voronoiBeachPool.pop() || new d3_geom_voronoiBeach();
    beach.site = site;
    return beach;
  }
  function d3_geom_voronoiDetachBeach(beach) {
    d3_geom_voronoiDetachCircle(beach);
    d3_geom_voronoiBeaches.remove(beach);
    d3_geom_voronoiBeachPool.push(beach);
    d3_geom_voronoiRedBlackNode(beach);
  }
  function d3_geom_voronoiRemoveBeach(beach) {
    var circle = beach.circle, x = circle.x, y = circle.cy, vertex = {
      x: x,
      y: y
    }, previous = beach.P, next = beach.N, disappearing = [ beach ];
    d3_geom_voronoiDetachBeach(beach);
    var lArc = previous;
    while (lArc.circle && abs(x - lArc.circle.x) < ε && abs(y - lArc.circle.cy) < ε) {
      previous = lArc.P;
      disappearing.unshift(lArc);
      d3_geom_voronoiDetachBeach(lArc);
      lArc = previous;
    }
    disappearing.unshift(lArc);
    d3_geom_voronoiDetachCircle(lArc);
    var rArc = next;
    while (rArc.circle && abs(x - rArc.circle.x) < ε && abs(y - rArc.circle.cy) < ε) {
      next = rArc.N;
      disappearing.push(rArc);
      d3_geom_voronoiDetachBeach(rArc);
      rArc = next;
    }
    disappearing.push(rArc);
    d3_geom_voronoiDetachCircle(rArc);
    var nArcs = disappearing.length, iArc;
    for (iArc = 1; iArc < nArcs; ++iArc) {
      rArc = disappearing[iArc];
      lArc = disappearing[iArc - 1];
      d3_geom_voronoiSetEdgeEnd(rArc.edge, lArc.site, rArc.site, vertex);
    }
    lArc = disappearing[0];
    rArc = disappearing[nArcs - 1];
    rArc.edge = d3_geom_voronoiCreateEdge(lArc.site, rArc.site, null, vertex);
    d3_geom_voronoiAttachCircle(lArc);
    d3_geom_voronoiAttachCircle(rArc);
  }
  function d3_geom_voronoiAddBeach(site) {
    var x = site.x, directrix = site.y, lArc, rArc, dxl, dxr, node = d3_geom_voronoiBeaches._;
    while (node) {
      dxl = d3_geom_voronoiLeftBreakPoint(node, directrix) - x;
      if (dxl > ε) node = node.L; else {
        dxr = x - d3_geom_voronoiRightBreakPoint(node, directrix);
        if (dxr > ε) {
          if (!node.R) {
            lArc = node;
            break;
          }
          node = node.R;
        } else {
          if (dxl > -ε) {
            lArc = node.P;
            rArc = node;
          } else if (dxr > -ε) {
            lArc = node;
            rArc = node.N;
          } else {
            lArc = rArc = node;
          }
          break;
        }
      }
    }
    var newArc = d3_geom_voronoiCreateBeach(site);
    d3_geom_voronoiBeaches.insert(lArc, newArc);
    if (!lArc && !rArc) return;
    if (lArc === rArc) {
      d3_geom_voronoiDetachCircle(lArc);
      rArc = d3_geom_voronoiCreateBeach(lArc.site);
      d3_geom_voronoiBeaches.insert(newArc, rArc);
      newArc.edge = rArc.edge = d3_geom_voronoiCreateEdge(lArc.site, newArc.site);
      d3_geom_voronoiAttachCircle(lArc);
      d3_geom_voronoiAttachCircle(rArc);
      return;
    }
    if (!rArc) {
      newArc.edge = d3_geom_voronoiCreateEdge(lArc.site, newArc.site);
      return;
    }
    d3_geom_voronoiDetachCircle(lArc);
    d3_geom_voronoiDetachCircle(rArc);
    var lSite = lArc.site, ax = lSite.x, ay = lSite.y, bx = site.x - ax, by = site.y - ay, rSite = rArc.site, cx = rSite.x - ax, cy = rSite.y - ay, d = 2 * (bx * cy - by * cx), hb = bx * bx + by * by, hc = cx * cx + cy * cy, vertex = {
      x: (cy * hb - by * hc) / d + ax,
      y: (bx * hc - cx * hb) / d + ay
    };
    d3_geom_voronoiSetEdgeEnd(rArc.edge, lSite, rSite, vertex);
    newArc.edge = d3_geom_voronoiCreateEdge(lSite, site, null, vertex);
    rArc.edge = d3_geom_voronoiCreateEdge(site, rSite, null, vertex);
    d3_geom_voronoiAttachCircle(lArc);
    d3_geom_voronoiAttachCircle(rArc);
  }
  function d3_geom_voronoiLeftBreakPoint(arc, directrix) {
    var site = arc.site, rfocx = site.x, rfocy = site.y, pby2 = rfocy - directrix;
    if (!pby2) return rfocx;
    var lArc = arc.P;
    if (!lArc) return -Infinity;
    site = lArc.site;
    var lfocx = site.x, lfocy = site.y, plby2 = lfocy - directrix;
    if (!plby2) return lfocx;
    var hl = lfocx - rfocx, aby2 = 1 / pby2 - 1 / plby2, b = hl / plby2;
    if (aby2) return (-b + Math.sqrt(b * b - 2 * aby2 * (hl * hl / (-2 * plby2) - lfocy + plby2 / 2 + rfocy - pby2 / 2))) / aby2 + rfocx;
    return (rfocx + lfocx) / 2;
  }
  function d3_geom_voronoiRightBreakPoint(arc, directrix) {
    var rArc = arc.N;
    if (rArc) return d3_geom_voronoiLeftBreakPoint(rArc, directrix);
    var site = arc.site;
    return site.y === directrix ? site.x : Infinity;
  }
  function d3_geom_voronoiCell(site) {
    this.site = site;
    this.edges = [];
  }
  d3_geom_voronoiCell.prototype.prepare = function() {
    var halfEdges = this.edges, iHalfEdge = halfEdges.length, edge;
    while (iHalfEdge--) {
      edge = halfEdges[iHalfEdge].edge;
      if (!edge.b || !edge.a) halfEdges.splice(iHalfEdge, 1);
    }
    halfEdges.sort(d3_geom_voronoiHalfEdgeOrder);
    return halfEdges.length;
  };
  function d3_geom_voronoiCloseCells(extent) {
    var x0 = extent[0][0], x1 = extent[1][0], y0 = extent[0][1], y1 = extent[1][1], x2, y2, x3, y3, cells = d3_geom_voronoiCells, iCell = cells.length, cell, iHalfEdge, halfEdges, nHalfEdges, start, end;
    while (iCell--) {
      cell = cells[iCell];
      if (!cell || !cell.prepare()) continue;
      halfEdges = cell.edges;
      nHalfEdges = halfEdges.length;
      iHalfEdge = 0;
      while (iHalfEdge < nHalfEdges) {
        end = halfEdges[iHalfEdge].end(), x3 = end.x, y3 = end.y;
        start = halfEdges[++iHalfEdge % nHalfEdges].start(), x2 = start.x, y2 = start.y;
        if (abs(x3 - x2) > ε || abs(y3 - y2) > ε) {
          halfEdges.splice(iHalfEdge, 0, new d3_geom_voronoiHalfEdge(d3_geom_voronoiCreateBorderEdge(cell.site, end, abs(x3 - x0) < ε && y1 - y3 > ε ? {
            x: x0,
            y: abs(x2 - x0) < ε ? y2 : y1
          } : abs(y3 - y1) < ε && x1 - x3 > ε ? {
            x: abs(y2 - y1) < ε ? x2 : x1,
            y: y1
          } : abs(x3 - x1) < ε && y3 - y0 > ε ? {
            x: x1,
            y: abs(x2 - x1) < ε ? y2 : y0
          } : abs(y3 - y0) < ε && x3 - x0 > ε ? {
            x: abs(y2 - y0) < ε ? x2 : x0,
            y: y0
          } : null), cell.site, null));
          ++nHalfEdges;
        }
      }
    }
  }
  function d3_geom_voronoiHalfEdgeOrder(a, b) {
    return b.angle - a.angle;
  }
  function d3_geom_voronoiCircle() {
    d3_geom_voronoiRedBlackNode(this);
    this.x = this.y = this.arc = this.site = this.cy = null;
  }
  function d3_geom_voronoiAttachCircle(arc) {
    var lArc = arc.P, rArc = arc.N;
    if (!lArc || !rArc) return;
    var lSite = lArc.site, cSite = arc.site, rSite = rArc.site;
    if (lSite === rSite) return;
    var bx = cSite.x, by = cSite.y, ax = lSite.x - bx, ay = lSite.y - by, cx = rSite.x - bx, cy = rSite.y - by;
    var d = 2 * (ax * cy - ay * cx);
    if (d >= -ε2) return;
    var ha = ax * ax + ay * ay, hc = cx * cx + cy * cy, x = (cy * ha - ay * hc) / d, y = (ax * hc - cx * ha) / d, cy = y + by;
    var circle = d3_geom_voronoiCirclePool.pop() || new d3_geom_voronoiCircle();
    circle.arc = arc;
    circle.site = cSite;
    circle.x = x + bx;
    circle.y = cy + Math.sqrt(x * x + y * y);
    circle.cy = cy;
    arc.circle = circle;
    var before = null, node = d3_geom_voronoiCircles._;
    while (node) {
      if (circle.y < node.y || circle.y === node.y && circle.x <= node.x) {
        if (node.L) node = node.L; else {
          before = node.P;
          break;
        }
      } else {
        if (node.R) node = node.R; else {
          before = node;
          break;
        }
      }
    }
    d3_geom_voronoiCircles.insert(before, circle);
    if (!before) d3_geom_voronoiFirstCircle = circle;
  }
  function d3_geom_voronoiDetachCircle(arc) {
    var circle = arc.circle;
    if (circle) {
      if (!circle.P) d3_geom_voronoiFirstCircle = circle.N;
      d3_geom_voronoiCircles.remove(circle);
      d3_geom_voronoiCirclePool.push(circle);
      d3_geom_voronoiRedBlackNode(circle);
      arc.circle = null;
    }
  }
  function d3_geom_voronoiClipEdges(extent) {
    var edges = d3_geom_voronoiEdges, clip = d3_geom_clipLine(extent[0][0], extent[0][1], extent[1][0], extent[1][1]), i = edges.length, e;
    while (i--) {
      e = edges[i];
      if (!d3_geom_voronoiConnectEdge(e, extent) || !clip(e) || abs(e.a.x - e.b.x) < ε && abs(e.a.y - e.b.y) < ε) {
        e.a = e.b = null;
        edges.splice(i, 1);
      }
    }
  }
  function d3_geom_voronoiConnectEdge(edge, extent) {
    var vb = edge.b;
    if (vb) return true;
    var va = edge.a, x0 = extent[0][0], x1 = extent[1][0], y0 = extent[0][1], y1 = extent[1][1], lSite = edge.l, rSite = edge.r, lx = lSite.x, ly = lSite.y, rx = rSite.x, ry = rSite.y, fx = (lx + rx) / 2, fy = (ly + ry) / 2, fm, fb;
    if (ry === ly) {
      if (fx < x0 || fx >= x1) return;
      if (lx > rx) {
        if (!va) va = {
          x: fx,
          y: y0
        }; else if (va.y >= y1) return;
        vb = {
          x: fx,
          y: y1
        };
      } else {
        if (!va) va = {
          x: fx,
          y: y1
        }; else if (va.y < y0) return;
        vb = {
          x: fx,
          y: y0
        };
      }
    } else {
      fm = (lx - rx) / (ry - ly);
      fb = fy - fm * fx;
      if (fm < -1 || fm > 1) {
        if (lx > rx) {
          if (!va) va = {
            x: (y0 - fb) / fm,
            y: y0
          }; else if (va.y >= y1) return;
          vb = {
            x: (y1 - fb) / fm,
            y: y1
          };
        } else {
          if (!va) va = {
            x: (y1 - fb) / fm,
            y: y1
          }; else if (va.y < y0) return;
          vb = {
            x: (y0 - fb) / fm,
            y: y0
          };
        }
      } else {
        if (ly < ry) {
          if (!va) va = {
            x: x0,
            y: fm * x0 + fb
          }; else if (va.x >= x1) return;
          vb = {
            x: x1,
            y: fm * x1 + fb
          };
        } else {
          if (!va) va = {
            x: x1,
            y: fm * x1 + fb
          }; else if (va.x < x0) return;
          vb = {
            x: x0,
            y: fm * x0 + fb
          };
        }
      }
    }
    edge.a = va;
    edge.b = vb;
    return true;
  }
  function d3_geom_voronoiEdge(lSite, rSite) {
    this.l = lSite;
    this.r = rSite;
    this.a = this.b = null;
  }
  function d3_geom_voronoiCreateEdge(lSite, rSite, va, vb) {
    var edge = new d3_geom_voronoiEdge(lSite, rSite);
    d3_geom_voronoiEdges.push(edge);
    if (va) d3_geom_voronoiSetEdgeEnd(edge, lSite, rSite, va);
    if (vb) d3_geom_voronoiSetEdgeEnd(edge, rSite, lSite, vb);
    d3_geom_voronoiCells[lSite.i].edges.push(new d3_geom_voronoiHalfEdge(edge, lSite, rSite));
    d3_geom_voronoiCells[rSite.i].edges.push(new d3_geom_voronoiHalfEdge(edge, rSite, lSite));
    return edge;
  }
  function d3_geom_voronoiCreateBorderEdge(lSite, va, vb) {
    var edge = new d3_geom_voronoiEdge(lSite, null);
    edge.a = va;
    edge.b = vb;
    d3_geom_voronoiEdges.push(edge);
    return edge;
  }
  function d3_geom_voronoiSetEdgeEnd(edge, lSite, rSite, vertex) {
    if (!edge.a && !edge.b) {
      edge.a = vertex;
      edge.l = lSite;
      edge.r = rSite;
    } else if (edge.l === rSite) {
      edge.b = vertex;
    } else {
      edge.a = vertex;
    }
  }
  function d3_geom_voronoiHalfEdge(edge, lSite, rSite) {
    var va = edge.a, vb = edge.b;
    this.edge = edge;
    this.site = lSite;
    this.angle = rSite ? Math.atan2(rSite.y - lSite.y, rSite.x - lSite.x) : edge.l === lSite ? Math.atan2(vb.x - va.x, va.y - vb.y) : Math.atan2(va.x - vb.x, vb.y - va.y);
  }
  d3_geom_voronoiHalfEdge.prototype = {
    start: function() {
      return this.edge.l === this.site ? this.edge.a : this.edge.b;
    },
    end: function() {
      return this.edge.l === this.site ? this.edge.b : this.edge.a;
    }
  };
  function d3_geom_voronoiRedBlackTree() {
    this._ = null;
  }
  function d3_geom_voronoiRedBlackNode(node) {
    node.U = node.C = node.L = node.R = node.P = node.N = null;
  }
  d3_geom_voronoiRedBlackTree.prototype = {
    insert: function(after, node) {
      var parent, grandpa, uncle;
      if (after) {
        node.P = after;
        node.N = after.N;
        if (after.N) after.N.P = node;
        after.N = node;
        if (after.R) {
          after = after.R;
          while (after.L) after = after.L;
          after.L = node;
        } else {
          after.R = node;
        }
        parent = after;
      } else if (this._) {
        after = d3_geom_voronoiRedBlackFirst(this._);
        node.P = null;
        node.N = after;
        after.P = after.L = node;
        parent = after;
      } else {
        node.P = node.N = null;
        this._ = node;
        parent = null;
      }
      node.L = node.R = null;
      node.U = parent;
      node.C = true;
      after = node;
      while (parent && parent.C) {
        grandpa = parent.U;
        if (parent === grandpa.L) {
          uncle = grandpa.R;
          if (uncle && uncle.C) {
            parent.C = uncle.C = false;
            grandpa.C = true;
            after = grandpa;
          } else {
            if (after === parent.R) {
              d3_geom_voronoiRedBlackRotateLeft(this, parent);
              after = parent;
              parent = after.U;
            }
            parent.C = false;
            grandpa.C = true;
            d3_geom_voronoiRedBlackRotateRight(this, grandpa);
          }
        } else {
          uncle = grandpa.L;
          if (uncle && uncle.C) {
            parent.C = uncle.C = false;
            grandpa.C = true;
            after = grandpa;
          } else {
            if (after === parent.L) {
              d3_geom_voronoiRedBlackRotateRight(this, parent);
              after = parent;
              parent = after.U;
            }
            parent.C = false;
            grandpa.C = true;
            d3_geom_voronoiRedBlackRotateLeft(this, grandpa);
          }
        }
        parent = after.U;
      }
      this._.C = false;
    },
    remove: function(node) {
      if (node.N) node.N.P = node.P;
      if (node.P) node.P.N = node.N;
      node.N = node.P = null;
      var parent = node.U, sibling, left = node.L, right = node.R, next, red;
      if (!left) next = right; else if (!right) next = left; else next = d3_geom_voronoiRedBlackFirst(right);
      if (parent) {
        if (parent.L === node) parent.L = next; else parent.R = next;
      } else {
        this._ = next;
      }
      if (left && right) {
        red = next.C;
        next.C = node.C;
        next.L = left;
        left.U = next;
        if (next !== right) {
          parent = next.U;
          next.U = node.U;
          node = next.R;
          parent.L = node;
          next.R = right;
          right.U = next;
        } else {
          next.U = parent;
          parent = next;
          node = next.R;
        }
      } else {
        red = node.C;
        node = next;
      }
      if (node) node.U = parent;
      if (red) return;
      if (node && node.C) {
        node.C = false;
        return;
      }
      do {
        if (node === this._) break;
        if (node === parent.L) {
          sibling = parent.R;
          if (sibling.C) {
            sibling.C = false;
            parent.C = true;
            d3_geom_voronoiRedBlackRotateLeft(this, parent);
            sibling = parent.R;
          }
          if (sibling.L && sibling.L.C || sibling.R && sibling.R.C) {
            if (!sibling.R || !sibling.R.C) {
              sibling.L.C = false;
              sibling.C = true;
              d3_geom_voronoiRedBlackRotateRight(this, sibling);
              sibling = parent.R;
            }
            sibling.C = parent.C;
            parent.C = sibling.R.C = false;
            d3_geom_voronoiRedBlackRotateLeft(this, parent);
            node = this._;
            break;
          }
        } else {
          sibling = parent.L;
          if (sibling.C) {
            sibling.C = false;
            parent.C = true;
            d3_geom_voronoiRedBlackRotateRight(this, parent);
            sibling = parent.L;
          }
          if (sibling.L && sibling.L.C || sibling.R && sibling.R.C) {
            if (!sibling.L || !sibling.L.C) {
              sibling.R.C = false;
              sibling.C = true;
              d3_geom_voronoiRedBlackRotateLeft(this, sibling);
              sibling = parent.L;
            }
            sibling.C = parent.C;
            parent.C = sibling.L.C = false;
            d3_geom_voronoiRedBlackRotateRight(this, parent);
            node = this._;
            break;
          }
        }
        sibling.C = true;
        node = parent;
        parent = parent.U;
      } while (!node.C);
      if (node) node.C = false;
    }
  };
  function d3_geom_voronoiRedBlackRotateLeft(tree, node) {
    var p = node, q = node.R, parent = p.U;
    if (parent) {
      if (parent.L === p) parent.L = q; else parent.R = q;
    } else {
      tree._ = q;
    }
    q.U = parent;
    p.U = q;
    p.R = q.L;
    if (p.R) p.R.U = p;
    q.L = p;
  }
  function d3_geom_voronoiRedBlackRotateRight(tree, node) {
    var p = node, q = node.L, parent = p.U;
    if (parent) {
      if (parent.L === p) parent.L = q; else parent.R = q;
    } else {
      tree._ = q;
    }
    q.U = parent;
    p.U = q;
    p.L = q.R;
    if (p.L) p.L.U = p;
    q.R = p;
  }
  function d3_geom_voronoiRedBlackFirst(node) {
    while (node.L) node = node.L;
    return node;
  }
  function d3_geom_voronoi(sites, bbox) {
    var site = sites.sort(d3_geom_voronoiVertexOrder).pop(), x0, y0, circle;
    d3_geom_voronoiEdges = [];
    d3_geom_voronoiCells = new Array(sites.length);
    d3_geom_voronoiBeaches = new d3_geom_voronoiRedBlackTree();
    d3_geom_voronoiCircles = new d3_geom_voronoiRedBlackTree();
    while (true) {
      circle = d3_geom_voronoiFirstCircle;
      if (site && (!circle || site.y < circle.y || site.y === circle.y && site.x < circle.x)) {
        if (site.x !== x0 || site.y !== y0) {
          d3_geom_voronoiCells[site.i] = new d3_geom_voronoiCell(site);
          d3_geom_voronoiAddBeach(site);
          x0 = site.x, y0 = site.y;
        }
        site = sites.pop();
      } else if (circle) {
        d3_geom_voronoiRemoveBeach(circle.arc);
      } else {
        break;
      }
    }
    if (bbox) d3_geom_voronoiClipEdges(bbox), d3_geom_voronoiCloseCells(bbox);
    var diagram = {
      cells: d3_geom_voronoiCells,
      edges: d3_geom_voronoiEdges
    };
    d3_geom_voronoiBeaches = d3_geom_voronoiCircles = d3_geom_voronoiEdges = d3_geom_voronoiCells = null;
    return diagram;
  }
  function d3_geom_voronoiVertexOrder(a, b) {
    return b.y - a.y || b.x - a.x;
  }
  d3.geom.voronoi = function(points) {
    var x = d3_geom_pointX, y = d3_geom_pointY, fx = x, fy = y, clipExtent = d3_geom_voronoiClipExtent;
    if (points) return voronoi(points);
    function voronoi(data) {
      var polygons = new Array(data.length), x0 = clipExtent[0][0], y0 = clipExtent[0][1], x1 = clipExtent[1][0], y1 = clipExtent[1][1];
      d3_geom_voronoi(sites(data), clipExtent).cells.forEach(function(cell, i) {
        var edges = cell.edges, site = cell.site, polygon = polygons[i] = edges.length ? edges.map(function(e) {
          var s = e.start();
          return [ s.x, s.y ];
        }) : site.x >= x0 && site.x <= x1 && site.y >= y0 && site.y <= y1 ? [ [ x0, y1 ], [ x1, y1 ], [ x1, y0 ], [ x0, y0 ] ] : [];
        polygon.point = data[i];
      });
      return polygons;
    }
    function sites(data) {
      return data.map(function(d, i) {
        return {
          x: Math.round(fx(d, i) / ε) * ε,
          y: Math.round(fy(d, i) / ε) * ε,
          i: i
        };
      });
    }
    voronoi.links = function(data) {
      return d3_geom_voronoi(sites(data)).edges.filter(function(edge) {
        return edge.l && edge.r;
      }).map(function(edge) {
        return {
          source: data[edge.l.i],
          target: data[edge.r.i]
        };
      });
    };
    voronoi.triangles = function(data) {
      var triangles = [];
      d3_geom_voronoi(sites(data)).cells.forEach(function(cell, i) {
        var site = cell.site, edges = cell.edges.sort(d3_geom_voronoiHalfEdgeOrder), j = -1, m = edges.length, e0, s0, e1 = edges[m - 1].edge, s1 = e1.l === site ? e1.r : e1.l;
        while (++j < m) {
          e0 = e1;
          s0 = s1;
          e1 = edges[j].edge;
          s1 = e1.l === site ? e1.r : e1.l;
          if (i < s0.i && i < s1.i && d3_geom_voronoiTriangleArea(site, s0, s1) < 0) {
            triangles.push([ data[i], data[s0.i], data[s1.i] ]);
          }
        }
      });
      return triangles;
    };
    voronoi.x = function(_) {
      return arguments.length ? (fx = d3_functor(x = _), voronoi) : x;
    };
    voronoi.y = function(_) {
      return arguments.length ? (fy = d3_functor(y = _), voronoi) : y;
    };
    voronoi.clipExtent = function(_) {
      if (!arguments.length) return clipExtent === d3_geom_voronoiClipExtent ? null : clipExtent;
      clipExtent = _ == null ? d3_geom_voronoiClipExtent : _;
      return voronoi;
    };
    voronoi.size = function(_) {
      if (!arguments.length) return clipExtent === d3_geom_voronoiClipExtent ? null : clipExtent && clipExtent[1];
      return voronoi.clipExtent(_ && [ [ 0, 0 ], _ ]);
    };
    return voronoi;
  };
  var d3_geom_voronoiClipExtent = [ [ -1e6, -1e6 ], [ 1e6, 1e6 ] ];
  function d3_geom_voronoiTriangleArea(a, b, c) {
    return (a.x - c.x) * (b.y - a.y) - (a.x - b.x) * (c.y - a.y);
  }
  d3.geom.delaunay = function(vertices) {
    return d3.geom.voronoi().triangles(vertices);
  };
  d3.geom.quadtree = function(points, x1, y1, x2, y2) {
    var x = d3_geom_pointX, y = d3_geom_pointY, compat;
    if (compat = arguments.length) {
      x = d3_geom_quadtreeCompatX;
      y = d3_geom_quadtreeCompatY;
      if (compat === 3) {
        y2 = y1;
        x2 = x1;
        y1 = x1 = 0;
      }
      return quadtree(points);
    }
    function quadtree(data) {
      var d, fx = d3_functor(x), fy = d3_functor(y), xs, ys, i, n, x1_, y1_, x2_, y2_;
      if (x1 != null) {
        x1_ = x1, y1_ = y1, x2_ = x2, y2_ = y2;
      } else {
        x2_ = y2_ = -(x1_ = y1_ = Infinity);
        xs = [], ys = [];
        n = data.length;
        if (compat) for (i = 0; i < n; ++i) {
          d = data[i];
          if (d.x < x1_) x1_ = d.x;
          if (d.y < y1_) y1_ = d.y;
          if (d.x > x2_) x2_ = d.x;
          if (d.y > y2_) y2_ = d.y;
          xs.push(d.x);
          ys.push(d.y);
        } else for (i = 0; i < n; ++i) {
          var x_ = +fx(d = data[i], i), y_ = +fy(d, i);
          if (x_ < x1_) x1_ = x_;
          if (y_ < y1_) y1_ = y_;
          if (x_ > x2_) x2_ = x_;
          if (y_ > y2_) y2_ = y_;
          xs.push(x_);
          ys.push(y_);
        }
      }
      var dx = x2_ - x1_, dy = y2_ - y1_;
      if (dx > dy) y2_ = y1_ + dx; else x2_ = x1_ + dy;
      function insert(n, d, x, y, x1, y1, x2, y2) {
        if (isNaN(x) || isNaN(y)) return;
        if (n.leaf) {
          var nx = n.x, ny = n.y;
          if (nx != null) {
            if (abs(nx - x) + abs(ny - y) < .01) {
              insertChild(n, d, x, y, x1, y1, x2, y2);
            } else {
              var nPoint = n.point;
              n.x = n.y = n.point = null;
              insertChild(n, nPoint, nx, ny, x1, y1, x2, y2);
              insertChild(n, d, x, y, x1, y1, x2, y2);
            }
          } else {
            n.x = x, n.y = y, n.point = d;
          }
        } else {
          insertChild(n, d, x, y, x1, y1, x2, y2);
        }
      }
      function insertChild(n, d, x, y, x1, y1, x2, y2) {
        var sx = (x1 + x2) * .5, sy = (y1 + y2) * .5, right = x >= sx, bottom = y >= sy, i = (bottom << 1) + right;
        n.leaf = false;
        n = n.nodes[i] || (n.nodes[i] = d3_geom_quadtreeNode());
        if (right) x1 = sx; else x2 = sx;
        if (bottom) y1 = sy; else y2 = sy;
        insert(n, d, x, y, x1, y1, x2, y2);
      }
      var root = d3_geom_quadtreeNode();
      root.add = function(d) {
        insert(root, d, +fx(d, ++i), +fy(d, i), x1_, y1_, x2_, y2_);
      };
      root.visit = function(f) {
        d3_geom_quadtreeVisit(f, root, x1_, y1_, x2_, y2_);
      };
      i = -1;
      if (x1 == null) {
        while (++i < n) {
          insert(root, data[i], xs[i], ys[i], x1_, y1_, x2_, y2_);
        }
        --i;
      } else data.forEach(root.add);
      xs = ys = data = d = null;
      return root;
    }
    quadtree.x = function(_) {
      return arguments.length ? (x = _, quadtree) : x;
    };
    quadtree.y = function(_) {
      return arguments.length ? (y = _, quadtree) : y;
    };
    quadtree.extent = function(_) {
      if (!arguments.length) return x1 == null ? null : [ [ x1, y1 ], [ x2, y2 ] ];
      if (_ == null) x1 = y1 = x2 = y2 = null; else x1 = +_[0][0], y1 = +_[0][1], x2 = +_[1][0], 
      y2 = +_[1][1];
      return quadtree;
    };
    quadtree.size = function(_) {
      if (!arguments.length) return x1 == null ? null : [ x2 - x1, y2 - y1 ];
      if (_ == null) x1 = y1 = x2 = y2 = null; else x1 = y1 = 0, x2 = +_[0], y2 = +_[1];
      return quadtree;
    };
    return quadtree;
  };
  function d3_geom_quadtreeCompatX(d) {
    return d.x;
  }
  function d3_geom_quadtreeCompatY(d) {
    return d.y;
  }
  function d3_geom_quadtreeNode() {
    return {
      leaf: true,
      nodes: [],
      point: null,
      x: null,
      y: null
    };
  }
  function d3_geom_quadtreeVisit(f, node, x1, y1, x2, y2) {
    if (!f(node, x1, y1, x2, y2)) {
      var sx = (x1 + x2) * .5, sy = (y1 + y2) * .5, children = node.nodes;
      if (children[0]) d3_geom_quadtreeVisit(f, children[0], x1, y1, sx, sy);
      if (children[1]) d3_geom_quadtreeVisit(f, children[1], sx, y1, x2, sy);
      if (children[2]) d3_geom_quadtreeVisit(f, children[2], x1, sy, sx, y2);
      if (children[3]) d3_geom_quadtreeVisit(f, children[3], sx, sy, x2, y2);
    }
  }
  d3.interpolateRgb = d3_interpolateRgb;
  function d3_interpolateRgb(a, b) {
    a = d3.rgb(a);
    b = d3.rgb(b);
    var ar = a.r, ag = a.g, ab = a.b, br = b.r - ar, bg = b.g - ag, bb = b.b - ab;
    return function(t) {
      return "#" + d3_rgb_hex(Math.round(ar + br * t)) + d3_rgb_hex(Math.round(ag + bg * t)) + d3_rgb_hex(Math.round(ab + bb * t));
    };
  }
  d3.interpolateObject = d3_interpolateObject;
  function d3_interpolateObject(a, b) {
    var i = {}, c = {}, k;
    for (k in a) {
      if (k in b) {
        i[k] = d3_interpolate(a[k], b[k]);
      } else {
        c[k] = a[k];
      }
    }
    for (k in b) {
      if (!(k in a)) {
        c[k] = b[k];
      }
    }
    return function(t) {
      for (k in i) c[k] = i[k](t);
      return c;
    };
  }
  d3.interpolateNumber = d3_interpolateNumber;
  function d3_interpolateNumber(a, b) {
    b -= a = +a;
    return function(t) {
      return a + b * t;
    };
  }
  d3.interpolateString = d3_interpolateString;
  function d3_interpolateString(a, b) {
    var m, i, j, s0 = 0, s1 = 0, s = [], q = [], n, o;
    a = a + "", b = b + "";
    d3_interpolate_number.lastIndex = 0;
    for (i = 0; m = d3_interpolate_number.exec(b); ++i) {
      if (m.index) s.push(b.substring(s0, s1 = m.index));
      q.push({
        i: s.length,
        x: m[0]
      });
      s.push(null);
      s0 = d3_interpolate_number.lastIndex;
    }
    if (s0 < b.length) s.push(b.substring(s0));
    for (i = 0, n = q.length; (m = d3_interpolate_number.exec(a)) && i < n; ++i) {
      o = q[i];
      if (o.x == m[0]) {
        if (o.i) {
          if (s[o.i + 1] == null) {
            s[o.i - 1] += o.x;
            s.splice(o.i, 1);
            for (j = i + 1; j < n; ++j) q[j].i--;
          } else {
            s[o.i - 1] += o.x + s[o.i + 1];
            s.splice(o.i, 2);
            for (j = i + 1; j < n; ++j) q[j].i -= 2;
          }
        } else {
          if (s[o.i + 1] == null) {
            s[o.i] = o.x;
          } else {
            s[o.i] = o.x + s[o.i + 1];
            s.splice(o.i + 1, 1);
            for (j = i + 1; j < n; ++j) q[j].i--;
          }
        }
        q.splice(i, 1);
        n--;
        i--;
      } else {
        o.x = d3_interpolateNumber(parseFloat(m[0]), parseFloat(o.x));
      }
    }
    while (i < n) {
      o = q.pop();
      if (s[o.i + 1] == null) {
        s[o.i] = o.x;
      } else {
        s[o.i] = o.x + s[o.i + 1];
        s.splice(o.i + 1, 1);
      }
      n--;
    }
    if (s.length === 1) {
      return s[0] == null ? (o = q[0].x, function(t) {
        return o(t) + "";
      }) : function() {
        return b;
      };
    }
    return function(t) {
      for (i = 0; i < n; ++i) s[(o = q[i]).i] = o.x(t);
      return s.join("");
    };
  }
  var d3_interpolate_number = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g;
  d3.interpolate = d3_interpolate;
  function d3_interpolate(a, b) {
    var i = d3.interpolators.length, f;
    while (--i >= 0 && !(f = d3.interpolators[i](a, b))) ;
    return f;
  }
  d3.interpolators = [ function(a, b) {
    var t = typeof b;
    return (t === "string" ? d3_rgb_names.has(b) || /^(#|rgb\(|hsl\()/.test(b) ? d3_interpolateRgb : d3_interpolateString : b instanceof d3_Color ? d3_interpolateRgb : t === "object" ? Array.isArray(b) ? d3_interpolateArray : d3_interpolateObject : d3_interpolateNumber)(a, b);
  } ];
  d3.interpolateArray = d3_interpolateArray;
  function d3_interpolateArray(a, b) {
    var x = [], c = [], na = a.length, nb = b.length, n0 = Math.min(a.length, b.length), i;
    for (i = 0; i < n0; ++i) x.push(d3_interpolate(a[i], b[i]));
    for (;i < na; ++i) c[i] = a[i];
    for (;i < nb; ++i) c[i] = b[i];
    return function(t) {
      for (i = 0; i < n0; ++i) c[i] = x[i](t);
      return c;
    };
  }
  var d3_ease_default = function() {
    return d3_identity;
  };
  var d3_ease = d3.map({
    linear: d3_ease_default,
    poly: d3_ease_poly,
    quad: function() {
      return d3_ease_quad;
    },
    cubic: function() {
      return d3_ease_cubic;
    },
    sin: function() {
      return d3_ease_sin;
    },
    exp: function() {
      return d3_ease_exp;
    },
    circle: function() {
      return d3_ease_circle;
    },
    elastic: d3_ease_elastic,
    back: d3_ease_back,
    bounce: function() {
      return d3_ease_bounce;
    }
  });
  var d3_ease_mode = d3.map({
    "in": d3_identity,
    out: d3_ease_reverse,
    "in-out": d3_ease_reflect,
    "out-in": function(f) {
      return d3_ease_reflect(d3_ease_reverse(f));
    }
  });
  d3.ease = function(name) {
    var i = name.indexOf("-"), t = i >= 0 ? name.substring(0, i) : name, m = i >= 0 ? name.substring(i + 1) : "in";
    t = d3_ease.get(t) || d3_ease_default;
    m = d3_ease_mode.get(m) || d3_identity;
    return d3_ease_clamp(m(t.apply(null, d3_arraySlice.call(arguments, 1))));
  };
  function d3_ease_clamp(f) {
    return function(t) {
      return t <= 0 ? 0 : t >= 1 ? 1 : f(t);
    };
  }
  function d3_ease_reverse(f) {
    return function(t) {
      return 1 - f(1 - t);
    };
  }
  function d3_ease_reflect(f) {
    return function(t) {
      return .5 * (t < .5 ? f(2 * t) : 2 - f(2 - 2 * t));
    };
  }
  function d3_ease_quad(t) {
    return t * t;
  }
  function d3_ease_cubic(t) {
    return t * t * t;
  }
  function d3_ease_cubicInOut(t) {
    if (t <= 0) return 0;
    if (t >= 1) return 1;
    var t2 = t * t, t3 = t2 * t;
    return 4 * (t < .5 ? t3 : 3 * (t - t2) + t3 - .75);
  }
  function d3_ease_poly(e) {
    return function(t) {
      return Math.pow(t, e);
    };
  }
  function d3_ease_sin(t) {
    return 1 - Math.cos(t * halfπ);
  }
  function d3_ease_exp(t) {
    return Math.pow(2, 10 * (t - 1));
  }
  function d3_ease_circle(t) {
    return 1 - Math.sqrt(1 - t * t);
  }
  function d3_ease_elastic(a, p) {
    var s;
    if (arguments.length < 2) p = .45;
    if (arguments.length) s = p / τ * Math.asin(1 / a); else a = 1, s = p / 4;
    return function(t) {
      return 1 + a * Math.pow(2, -10 * t) * Math.sin((t - s) * τ / p);
    };
  }
  function d3_ease_back(s) {
    if (!s) s = 1.70158;
    return function(t) {
      return t * t * ((s + 1) * t - s);
    };
  }
  function d3_ease_bounce(t) {
    return t < 1 / 2.75 ? 7.5625 * t * t : t < 2 / 2.75 ? 7.5625 * (t -= 1.5 / 2.75) * t + .75 : t < 2.5 / 2.75 ? 7.5625 * (t -= 2.25 / 2.75) * t + .9375 : 7.5625 * (t -= 2.625 / 2.75) * t + .984375;
  }
  d3.interpolateHcl = d3_interpolateHcl;
  function d3_interpolateHcl(a, b) {
    a = d3.hcl(a);
    b = d3.hcl(b);
    var ah = a.h, ac = a.c, al = a.l, bh = b.h - ah, bc = b.c - ac, bl = b.l - al;
    if (isNaN(bc)) bc = 0, ac = isNaN(ac) ? b.c : ac;
    if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah; else if (bh > 180) bh -= 360; else if (bh < -180) bh += 360;
    return function(t) {
      return d3_hcl_lab(ah + bh * t, ac + bc * t, al + bl * t) + "";
    };
  }
  d3.interpolateHsl = d3_interpolateHsl;
  function d3_interpolateHsl(a, b) {
    a = d3.hsl(a);
    b = d3.hsl(b);
    var ah = a.h, as = a.s, al = a.l, bh = b.h - ah, bs = b.s - as, bl = b.l - al;
    if (isNaN(bs)) bs = 0, as = isNaN(as) ? b.s : as;
    if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah; else if (bh > 180) bh -= 360; else if (bh < -180) bh += 360;
    return function(t) {
      return d3_hsl_rgb(ah + bh * t, as + bs * t, al + bl * t) + "";
    };
  }
  d3.interpolateLab = d3_interpolateLab;
  function d3_interpolateLab(a, b) {
    a = d3.lab(a);
    b = d3.lab(b);
    var al = a.l, aa = a.a, ab = a.b, bl = b.l - al, ba = b.a - aa, bb = b.b - ab;
    return function(t) {
      return d3_lab_rgb(al + bl * t, aa + ba * t, ab + bb * t) + "";
    };
  }
  d3.interpolateRound = d3_interpolateRound;
  function d3_interpolateRound(a, b) {
    b -= a;
    return function(t) {
      return Math.round(a + b * t);
    };
  }
  d3.transform = function(string) {
    var g = d3_document.createElementNS(d3.ns.prefix.svg, "g");
    return (d3.transform = function(string) {
      if (string != null) {
        g.setAttribute("transform", string);
        var t = g.transform.baseVal.consolidate();
      }
      return new d3_transform(t ? t.matrix : d3_transformIdentity);
    })(string);
  };
  function d3_transform(m) {
    var r0 = [ m.a, m.b ], r1 = [ m.c, m.d ], kx = d3_transformNormalize(r0), kz = d3_transformDot(r0, r1), ky = d3_transformNormalize(d3_transformCombine(r1, r0, -kz)) || 0;
    if (r0[0] * r1[1] < r1[0] * r0[1]) {
      r0[0] *= -1;
      r0[1] *= -1;
      kx *= -1;
      kz *= -1;
    }
    this.rotate = (kx ? Math.atan2(r0[1], r0[0]) : Math.atan2(-r1[0], r1[1])) * d3_degrees;
    this.translate = [ m.e, m.f ];
    this.scale = [ kx, ky ];
    this.skew = ky ? Math.atan2(kz, ky) * d3_degrees : 0;
  }
  d3_transform.prototype.toString = function() {
    return "translate(" + this.translate + ")rotate(" + this.rotate + ")skewX(" + this.skew + ")scale(" + this.scale + ")";
  };
  function d3_transformDot(a, b) {
    return a[0] * b[0] + a[1] * b[1];
  }
  function d3_transformNormalize(a) {
    var k = Math.sqrt(d3_transformDot(a, a));
    if (k) {
      a[0] /= k;
      a[1] /= k;
    }
    return k;
  }
  function d3_transformCombine(a, b, k) {
    a[0] += k * b[0];
    a[1] += k * b[1];
    return a;
  }
  var d3_transformIdentity = {
    a: 1,
    b: 0,
    c: 0,
    d: 1,
    e: 0,
    f: 0
  };
  d3.interpolateTransform = d3_interpolateTransform;
  function d3_interpolateTransform(a, b) {
    var s = [], q = [], n, A = d3.transform(a), B = d3.transform(b), ta = A.translate, tb = B.translate, ra = A.rotate, rb = B.rotate, wa = A.skew, wb = B.skew, ka = A.scale, kb = B.scale;
    if (ta[0] != tb[0] || ta[1] != tb[1]) {
      s.push("translate(", null, ",", null, ")");
      q.push({
        i: 1,
        x: d3_interpolateNumber(ta[0], tb[0])
      }, {
        i: 3,
        x: d3_interpolateNumber(ta[1], tb[1])
      });
    } else if (tb[0] || tb[1]) {
      s.push("translate(" + tb + ")");
    } else {
      s.push("");
    }
    if (ra != rb) {
      if (ra - rb > 180) rb += 360; else if (rb - ra > 180) ra += 360;
      q.push({
        i: s.push(s.pop() + "rotate(", null, ")") - 2,
        x: d3_interpolateNumber(ra, rb)
      });
    } else if (rb) {
      s.push(s.pop() + "rotate(" + rb + ")");
    }
    if (wa != wb) {
      q.push({
        i: s.push(s.pop() + "skewX(", null, ")") - 2,
        x: d3_interpolateNumber(wa, wb)
      });
    } else if (wb) {
      s.push(s.pop() + "skewX(" + wb + ")");
    }
    if (ka[0] != kb[0] || ka[1] != kb[1]) {
      n = s.push(s.pop() + "scale(", null, ",", null, ")");
      q.push({
        i: n - 4,
        x: d3_interpolateNumber(ka[0], kb[0])
      }, {
        i: n - 2,
        x: d3_interpolateNumber(ka[1], kb[1])
      });
    } else if (kb[0] != 1 || kb[1] != 1) {
      s.push(s.pop() + "scale(" + kb + ")");
    }
    n = q.length;
    return function(t) {
      var i = -1, o;
      while (++i < n) s[(o = q[i]).i] = o.x(t);
      return s.join("");
    };
  }
  function d3_uninterpolateNumber(a, b) {
    b = b - (a = +a) ? 1 / (b - a) : 0;
    return function(x) {
      return (x - a) * b;
    };
  }
  function d3_uninterpolateClamp(a, b) {
    b = b - (a = +a) ? 1 / (b - a) : 0;
    return function(x) {
      return Math.max(0, Math.min(1, (x - a) * b));
    };
  }
  d3.layout = {};
  d3.layout.bundle = function() {
    return function(links) {
      var paths = [], i = -1, n = links.length;
      while (++i < n) paths.push(d3_layout_bundlePath(links[i]));
      return paths;
    };
  };
  function d3_layout_bundlePath(link) {
    var start = link.source, end = link.target, lca = d3_layout_bundleLeastCommonAncestor(start, end), points = [ start ];
    while (start !== lca) {
      start = start.parent;
      points.push(start);
    }
    var k = points.length;
    while (end !== lca) {
      points.splice(k, 0, end);
      end = end.parent;
    }
    return points;
  }
  function d3_layout_bundleAncestors(node) {
    var ancestors = [], parent = node.parent;
    while (parent != null) {
      ancestors.push(node);
      node = parent;
      parent = parent.parent;
    }
    ancestors.push(node);
    return ancestors;
  }
  function d3_layout_bundleLeastCommonAncestor(a, b) {
    if (a === b) return a;
    var aNodes = d3_layout_bundleAncestors(a), bNodes = d3_layout_bundleAncestors(b), aNode = aNodes.pop(), bNode = bNodes.pop(), sharedNode = null;
    while (aNode === bNode) {
      sharedNode = aNode;
      aNode = aNodes.pop();
      bNode = bNodes.pop();
    }
    return sharedNode;
  }
  d3.layout.chord = function() {
    var chord = {}, chords, groups, matrix, n, padding = 0, sortGroups, sortSubgroups, sortChords;
    function relayout() {
      var subgroups = {}, groupSums = [], groupIndex = d3.range(n), subgroupIndex = [], k, x, x0, i, j;
      chords = [];
      groups = [];
      k = 0, i = -1;
      while (++i < n) {
        x = 0, j = -1;
        while (++j < n) {
          x += matrix[i][j];
        }
        groupSums.push(x);
        subgroupIndex.push(d3.range(n));
        k += x;
      }
      if (sortGroups) {
        groupIndex.sort(function(a, b) {
          return sortGroups(groupSums[a], groupSums[b]);
        });
      }
      if (sortSubgroups) {
        subgroupIndex.forEach(function(d, i) {
          d.sort(function(a, b) {
            return sortSubgroups(matrix[i][a], matrix[i][b]);
          });
        });
      }
      k = (τ - padding * n) / k;
      x = 0, i = -1;
      while (++i < n) {
        x0 = x, j = -1;
        while (++j < n) {
          var di = groupIndex[i], dj = subgroupIndex[di][j], v = matrix[di][dj], a0 = x, a1 = x += v * k;
          subgroups[di + "-" + dj] = {
            index: di,
            subindex: dj,
            startAngle: a0,
            endAngle: a1,
            value: v
          };
        }
        groups[di] = {
          index: di,
          startAngle: x0,
          endAngle: x,
          value: (x - x0) / k
        };
        x += padding;
      }
      i = -1;
      while (++i < n) {
        j = i - 1;
        while (++j < n) {
          var source = subgroups[i + "-" + j], target = subgroups[j + "-" + i];
          if (source.value || target.value) {
            chords.push(source.value < target.value ? {
              source: target,
              target: source
            } : {
              source: source,
              target: target
            });
          }
        }
      }
      if (sortChords) resort();
    }
    function resort() {
      chords.sort(function(a, b) {
        return sortChords((a.source.value + a.target.value) / 2, (b.source.value + b.target.value) / 2);
      });
    }
    chord.matrix = function(x) {
      if (!arguments.length) return matrix;
      n = (matrix = x) && matrix.length;
      chords = groups = null;
      return chord;
    };
    chord.padding = function(x) {
      if (!arguments.length) return padding;
      padding = x;
      chords = groups = null;
      return chord;
    };
    chord.sortGroups = function(x) {
      if (!arguments.length) return sortGroups;
      sortGroups = x;
      chords = groups = null;
      return chord;
    };
    chord.sortSubgroups = function(x) {
      if (!arguments.length) return sortSubgroups;
      sortSubgroups = x;
      chords = null;
      return chord;
    };
    chord.sortChords = function(x) {
      if (!arguments.length) return sortChords;
      sortChords = x;
      if (chords) resort();
      return chord;
    };
    chord.chords = function() {
      if (!chords) relayout();
      return chords;
    };
    chord.groups = function() {
      if (!groups) relayout();
      return groups;
    };
    return chord;
  };
  d3.layout.force = function() {
    var force = {}, event = d3.dispatch("start", "tick", "end"), size = [ 1, 1 ], drag, alpha, friction = .9, linkDistance = d3_layout_forceLinkDistance, linkStrength = d3_layout_forceLinkStrength, charge = -30, chargeDistance2 = d3_layout_forceChargeDistance2, gravity = .1, theta2 = .64, nodes = [], links = [], distances, strengths, charges;
    function repulse(node) {
      return function(quad, x1, _, x2) {
        if (quad.point !== node) {
          var dx = quad.cx - node.x, dy = quad.cy - node.y, dw = x2 - x1, dn = dx * dx + dy * dy;
          if (dw * dw / theta2 < dn) {
            if (dn < chargeDistance2) {
              var k = quad.charge / dn;
              node.px -= dx * k;
              node.py -= dy * k;
            }
            return true;
          }
          if (quad.point && dn && dn < chargeDistance2) {
            var k = quad.pointCharge / dn;
            node.px -= dx * k;
            node.py -= dy * k;
          }
        }
        return !quad.charge;
      };
    }
    force.tick = function() {
      if ((alpha *= .99) < .005) {
        event.end({
          type: "end",
          alpha: alpha = 0
        });
        return true;
      }
      var n = nodes.length, m = links.length, q, i, o, s, t, l, k, x, y;
      for (i = 0; i < m; ++i) {
        o = links[i];
        s = o.source;
        t = o.target;
        x = t.x - s.x;
        y = t.y - s.y;
        if (l = x * x + y * y) {
          l = alpha * strengths[i] * ((l = Math.sqrt(l)) - distances[i]) / l;
          x *= l;
          y *= l;
          t.x -= x * (k = s.weight / (t.weight + s.weight));
          t.y -= y * k;
          s.x += x * (k = 1 - k);
          s.y += y * k;
        }
      }
      if (k = alpha * gravity) {
        x = size[0] / 2;
        y = size[1] / 2;
        i = -1;
        if (k) while (++i < n) {
          o = nodes[i];
          o.x += (x - o.x) * k;
          o.y += (y - o.y) * k;
        }
      }
      if (charge) {
        d3_layout_forceAccumulate(q = d3.geom.quadtree(nodes), alpha, charges);
        i = -1;
        while (++i < n) {
          if (!(o = nodes[i]).fixed) {
            q.visit(repulse(o));
          }
        }
      }
      i = -1;
      while (++i < n) {
        o = nodes[i];
        if (o.fixed) {
          o.x = o.px;
          o.y = o.py;
        } else {
          o.x -= (o.px - (o.px = o.x)) * friction;
          o.y -= (o.py - (o.py = o.y)) * friction;
        }
      }
      event.tick({
        type: "tick",
        alpha: alpha
      });
    };
    force.nodes = function(x) {
      if (!arguments.length) return nodes;
      nodes = x;
      return force;
    };
    force.links = function(x) {
      if (!arguments.length) return links;
      links = x;
      return force;
    };
    force.size = function(x) {
      if (!arguments.length) return size;
      size = x;
      return force;
    };
    force.linkDistance = function(x) {
      if (!arguments.length) return linkDistance;
      linkDistance = typeof x === "function" ? x : +x;
      return force;
    };
    force.distance = force.linkDistance;
    force.linkStrength = function(x) {
      if (!arguments.length) return linkStrength;
      linkStrength = typeof x === "function" ? x : +x;
      return force;
    };
    force.friction = function(x) {
      if (!arguments.length) return friction;
      friction = +x;
      return force;
    };
    force.charge = function(x) {
      if (!arguments.length) return charge;
      charge = typeof x === "function" ? x : +x;
      return force;
    };
    force.chargeDistance = function(x) {
      if (!arguments.length) return Math.sqrt(chargeDistance2);
      chargeDistance2 = x * x;
      return force;
    };
    force.gravity = function(x) {
      if (!arguments.length) return gravity;
      gravity = +x;
      return force;
    };
    force.theta = function(x) {
      if (!arguments.length) return Math.sqrt(theta2);
      theta2 = x * x;
      return force;
    };
    force.alpha = function(x) {
      if (!arguments.length) return alpha;
      x = +x;
      if (alpha) {
        if (x > 0) alpha = x; else alpha = 0;
      } else if (x > 0) {
        event.start({
          type: "start",
          alpha: alpha = x
        });
        d3.timer(force.tick);
      }
      return force;
    };
    force.start = function() {
      var i, n = nodes.length, m = links.length, w = size[0], h = size[1], neighbors, o;
      for (i = 0; i < n; ++i) {
        (o = nodes[i]).index = i;
        o.weight = 0;
      }
      for (i = 0; i < m; ++i) {
        o = links[i];
        if (typeof o.source == "number") o.source = nodes[o.source];
        if (typeof o.target == "number") o.target = nodes[o.target];
        ++o.source.weight;
        ++o.target.weight;
      }
      for (i = 0; i < n; ++i) {
        o = nodes[i];
        if (isNaN(o.x)) o.x = position("x", w);
        if (isNaN(o.y)) o.y = position("y", h);
        if (isNaN(o.px)) o.px = o.x;
        if (isNaN(o.py)) o.py = o.y;
      }
      distances = [];
      if (typeof linkDistance === "function") for (i = 0; i < m; ++i) distances[i] = +linkDistance.call(this, links[i], i); else for (i = 0; i < m; ++i) distances[i] = linkDistance;
      strengths = [];
      if (typeof linkStrength === "function") for (i = 0; i < m; ++i) strengths[i] = +linkStrength.call(this, links[i], i); else for (i = 0; i < m; ++i) strengths[i] = linkStrength;
      charges = [];
      if (typeof charge === "function") for (i = 0; i < n; ++i) charges[i] = +charge.call(this, nodes[i], i); else for (i = 0; i < n; ++i) charges[i] = charge;
      function position(dimension, size) {
        if (!neighbors) {
          neighbors = new Array(n);
          for (j = 0; j < n; ++j) {
            neighbors[j] = [];
          }
          for (j = 0; j < m; ++j) {
            var o = links[j];
            neighbors[o.source.index].push(o.target);
            neighbors[o.target.index].push(o.source);
          }
        }
        var candidates = neighbors[i], j = -1, m = candidates.length, x;
        while (++j < m) if (!isNaN(x = candidates[j][dimension])) return x;
        return Math.random() * size;
      }
      return force.resume();
    };
    force.resume = function() {
      return force.alpha(.1);
    };
    force.stop = function() {
      return force.alpha(0);
    };
    force.drag = function() {
      if (!drag) drag = d3.behavior.drag().origin(d3_identity).on("dragstart.force", d3_layout_forceDragstart).on("drag.force", dragmove).on("dragend.force", d3_layout_forceDragend);
      if (!arguments.length) return drag;
      this.on("mouseover.force", d3_layout_forceMouseover).on("mouseout.force", d3_layout_forceMouseout).call(drag);
    };
    function dragmove(d) {
      d.px = d3.event.x, d.py = d3.event.y;
      force.resume();
    }
    return d3.rebind(force, event, "on");
  };
  function d3_layout_forceDragstart(d) {
    d.fixed |= 2;
  }
  function d3_layout_forceDragend(d) {
    d.fixed &= ~6;
  }
  function d3_layout_forceMouseover(d) {
    d.fixed |= 4;
    d.px = d.x, d.py = d.y;
  }
  function d3_layout_forceMouseout(d) {
    d.fixed &= ~4;
  }
  function d3_layout_forceAccumulate(quad, alpha, charges) {
    var cx = 0, cy = 0;
    quad.charge = 0;
    if (!quad.leaf) {
      var nodes = quad.nodes, n = nodes.length, i = -1, c;
      while (++i < n) {
        c = nodes[i];
        if (c == null) continue;
        d3_layout_forceAccumulate(c, alpha, charges);
        quad.charge += c.charge;
        cx += c.charge * c.cx;
        cy += c.charge * c.cy;
      }
    }
    if (quad.point) {
      if (!quad.leaf) {
        quad.point.x += Math.random() - .5;
        quad.point.y += Math.random() - .5;
      }
      var k = alpha * charges[quad.point.index];
      quad.charge += quad.pointCharge = k;
      cx += k * quad.point.x;
      cy += k * quad.point.y;
    }
    quad.cx = cx / quad.charge;
    quad.cy = cy / quad.charge;
  }
  var d3_layout_forceLinkDistance = 20, d3_layout_forceLinkStrength = 1, d3_layout_forceChargeDistance2 = Infinity;
  d3.layout.hierarchy = function() {
    var sort = d3_layout_hierarchySort, children = d3_layout_hierarchyChildren, value = d3_layout_hierarchyValue;
    function recurse(node, depth, nodes) {
      var childs = children.call(hierarchy, node, depth);
      node.depth = depth;
      nodes.push(node);
      if (childs && (n = childs.length)) {
        var i = -1, n, c = node.children = new Array(n), v = 0, j = depth + 1, d;
        while (++i < n) {
          d = c[i] = recurse(childs[i], j, nodes);
          d.parent = node;
          v += d.value;
        }
        if (sort) c.sort(sort);
        if (value) node.value = v;
      } else {
        delete node.children;
        if (value) {
          node.value = +value.call(hierarchy, node, depth) || 0;
        }
      }
      return node;
    }
    function revalue(node, depth) {
      var children = node.children, v = 0;
      if (children && (n = children.length)) {
        var i = -1, n, j = depth + 1;
        while (++i < n) v += revalue(children[i], j);
      } else if (value) {
        v = +value.call(hierarchy, node, depth) || 0;
      }
      if (value) node.value = v;
      return v;
    }
    function hierarchy(d) {
      var nodes = [];
      recurse(d, 0, nodes);
      return nodes;
    }
    hierarchy.sort = function(x) {
      if (!arguments.length) return sort;
      sort = x;
      return hierarchy;
    };
    hierarchy.children = function(x) {
      if (!arguments.length) return children;
      children = x;
      return hierarchy;
    };
    hierarchy.value = function(x) {
      if (!arguments.length) return value;
      value = x;
      return hierarchy;
    };
    hierarchy.revalue = function(root) {
      revalue(root, 0);
      return root;
    };
    return hierarchy;
  };
  function d3_layout_hierarchyRebind(object, hierarchy) {
    d3.rebind(object, hierarchy, "sort", "children", "value");
    object.nodes = object;
    object.links = d3_layout_hierarchyLinks;
    return object;
  }
  function d3_layout_hierarchyChildren(d) {
    return d.children;
  }
  function d3_layout_hierarchyValue(d) {
    return d.value;
  }
  function d3_layout_hierarchySort(a, b) {
    return b.value - a.value;
  }
  function d3_layout_hierarchyLinks(nodes) {
    return d3.merge(nodes.map(function(parent) {
      return (parent.children || []).map(function(child) {
        return {
          source: parent,
          target: child
        };
      });
    }));
  }
  d3.layout.partition = function() {
    var hierarchy = d3.layout.hierarchy(), size = [ 1, 1 ];
    function position(node, x, dx, dy) {
      var children = node.children;
      node.x = x;
      node.y = node.depth * dy;
      node.dx = dx;
      node.dy = dy;
      if (children && (n = children.length)) {
        var i = -1, n, c, d;
        dx = node.value ? dx / node.value : 0;
        while (++i < n) {
          position(c = children[i], x, d = c.value * dx, dy);
          x += d;
        }
      }
    }
    function depth(node) {
      var children = node.children, d = 0;
      if (children && (n = children.length)) {
        var i = -1, n;
        while (++i < n) d = Math.max(d, depth(children[i]));
      }
      return 1 + d;
    }
    function partition(d, i) {
      var nodes = hierarchy.call(this, d, i);
      position(nodes[0], 0, size[0], size[1] / depth(nodes[0]));
      return nodes;
    }
    partition.size = function(x) {
      if (!arguments.length) return size;
      size = x;
      return partition;
    };
    return d3_layout_hierarchyRebind(partition, hierarchy);
  };
  d3.layout.pie = function() {
    var value = Number, sort = d3_layout_pieSortByValue, startAngle = 0, endAngle = τ;
    function pie(data) {
      var values = data.map(function(d, i) {
        return +value.call(pie, d, i);
      });
      var a = +(typeof startAngle === "function" ? startAngle.apply(this, arguments) : startAngle);
      var k = ((typeof endAngle === "function" ? endAngle.apply(this, arguments) : endAngle) - a) / d3.sum(values);
      var index = d3.range(data.length);
      if (sort != null) index.sort(sort === d3_layout_pieSortByValue ? function(i, j) {
        return values[j] - values[i];
      } : function(i, j) {
        return sort(data[i], data[j]);
      });
      var arcs = [];
      index.forEach(function(i) {
        var d;
        arcs[i] = {
          data: data[i],
          value: d = values[i],
          startAngle: a,
          endAngle: a += d * k
        };
      });
      return arcs;
    }
    pie.value = function(x) {
      if (!arguments.length) return value;
      value = x;
      return pie;
    };
    pie.sort = function(x) {
      if (!arguments.length) return sort;
      sort = x;
      return pie;
    };
    pie.startAngle = function(x) {
      if (!arguments.length) return startAngle;
      startAngle = x;
      return pie;
    };
    pie.endAngle = function(x) {
      if (!arguments.length) return endAngle;
      endAngle = x;
      return pie;
    };
    return pie;
  };
  var d3_layout_pieSortByValue = {};
  d3.layout.stack = function() {
    var values = d3_identity, order = d3_layout_stackOrderDefault, offset = d3_layout_stackOffsetZero, out = d3_layout_stackOut, x = d3_layout_stackX, y = d3_layout_stackY;
    function stack(data, index) {
      var series = data.map(function(d, i) {
        return values.call(stack, d, i);
      });
      var points = series.map(function(d) {
        return d.map(function(v, i) {
          return [ x.call(stack, v, i), y.call(stack, v, i) ];
        });
      });
      var orders = order.call(stack, points, index);
      series = d3.permute(series, orders);
      points = d3.permute(points, orders);
      var offsets = offset.call(stack, points, index);
      var n = series.length, m = series[0].length, i, j, o;
      for (j = 0; j < m; ++j) {
        out.call(stack, series[0][j], o = offsets[j], points[0][j][1]);
        for (i = 1; i < n; ++i) {
          out.call(stack, series[i][j], o += points[i - 1][j][1], points[i][j][1]);
        }
      }
      return data;
    }
    stack.values = function(x) {
      if (!arguments.length) return values;
      values = x;
      return stack;
    };
    stack.order = function(x) {
      if (!arguments.length) return order;
      order = typeof x === "function" ? x : d3_layout_stackOrders.get(x) || d3_layout_stackOrderDefault;
      return stack;
    };
    stack.offset = function(x) {
      if (!arguments.length) return offset;
      offset = typeof x === "function" ? x : d3_layout_stackOffsets.get(x) || d3_layout_stackOffsetZero;
      return stack;
    };
    stack.x = function(z) {
      if (!arguments.length) return x;
      x = z;
      return stack;
    };
    stack.y = function(z) {
      if (!arguments.length) return y;
      y = z;
      return stack;
    };
    stack.out = function(z) {
      if (!arguments.length) return out;
      out = z;
      return stack;
    };
    return stack;
  };
  function d3_layout_stackX(d) {
    return d.x;
  }
  function d3_layout_stackY(d) {
    return d.y;
  }
  function d3_layout_stackOut(d, y0, y) {
    d.y0 = y0;
    d.y = y;
  }
  var d3_layout_stackOrders = d3.map({
    "inside-out": function(data) {
      var n = data.length, i, j, max = data.map(d3_layout_stackMaxIndex), sums = data.map(d3_layout_stackReduceSum), index = d3.range(n).sort(function(a, b) {
        return max[a] - max[b];
      }), top = 0, bottom = 0, tops = [], bottoms = [];
      for (i = 0; i < n; ++i) {
        j = index[i];
        if (top < bottom) {
          top += sums[j];
          tops.push(j);
        } else {
          bottom += sums[j];
          bottoms.push(j);
        }
      }
      return bottoms.reverse().concat(tops);
    },
    reverse: function(data) {
      return d3.range(data.length).reverse();
    },
    "default": d3_layout_stackOrderDefault
  });
  var d3_layout_stackOffsets = d3.map({
    silhouette: function(data) {
      var n = data.length, m = data[0].length, sums = [], max = 0, i, j, o, y0 = [];
      for (j = 0; j < m; ++j) {
        for (i = 0, o = 0; i < n; i++) o += data[i][j][1];
        if (o > max) max = o;
        sums.push(o);
      }
      for (j = 0; j < m; ++j) {
        y0[j] = (max - sums[j]) / 2;
      }
      return y0;
    },
    wiggle: function(data) {
      var n = data.length, x = data[0], m = x.length, i, j, k, s1, s2, s3, dx, o, o0, y0 = [];
      y0[0] = o = o0 = 0;
      for (j = 1; j < m; ++j) {
        for (i = 0, s1 = 0; i < n; ++i) s1 += data[i][j][1];
        for (i = 0, s2 = 0, dx = x[j][0] - x[j - 1][0]; i < n; ++i) {
          for (k = 0, s3 = (data[i][j][1] - data[i][j - 1][1]) / (2 * dx); k < i; ++k) {
            s3 += (data[k][j][1] - data[k][j - 1][1]) / dx;
          }
          s2 += s3 * data[i][j][1];
        }
        y0[j] = o -= s1 ? s2 / s1 * dx : 0;
        if (o < o0) o0 = o;
      }
      for (j = 0; j < m; ++j) y0[j] -= o0;
      return y0;
    },
    expand: function(data) {
      var n = data.length, m = data[0].length, k = 1 / n, i, j, o, y0 = [];
      for (j = 0; j < m; ++j) {
        for (i = 0, o = 0; i < n; i++) o += data[i][j][1];
        if (o) for (i = 0; i < n; i++) data[i][j][1] /= o; else for (i = 0; i < n; i++) data[i][j][1] = k;
      }
      for (j = 0; j < m; ++j) y0[j] = 0;
      return y0;
    },
    zero: d3_layout_stackOffsetZero
  });
  function d3_layout_stackOrderDefault(data) {
    return d3.range(data.length);
  }
  function d3_layout_stackOffsetZero(data) {
    var j = -1, m = data[0].length, y0 = [];
    while (++j < m) y0[j] = 0;
    return y0;
  }
  function d3_layout_stackMaxIndex(array) {
    var i = 1, j = 0, v = array[0][1], k, n = array.length;
    for (;i < n; ++i) {
      if ((k = array[i][1]) > v) {
        j = i;
        v = k;
      }
    }
    return j;
  }
  function d3_layout_stackReduceSum(d) {
    return d.reduce(d3_layout_stackSum, 0);
  }
  function d3_layout_stackSum(p, d) {
    return p + d[1];
  }
  d3.layout.histogram = function() {
    var frequency = true, valuer = Number, ranger = d3_layout_histogramRange, binner = d3_layout_histogramBinSturges;
    function histogram(data, i) {
      var bins = [], values = data.map(valuer, this), range = ranger.call(this, values, i), thresholds = binner.call(this, range, values, i), bin, i = -1, n = values.length, m = thresholds.length - 1, k = frequency ? 1 : 1 / n, x;
      while (++i < m) {
        bin = bins[i] = [];
        bin.dx = thresholds[i + 1] - (bin.x = thresholds[i]);
        bin.y = 0;
      }
      if (m > 0) {
        i = -1;
        while (++i < n) {
          x = values[i];
          if (x >= range[0] && x <= range[1]) {
            bin = bins[d3.bisect(thresholds, x, 1, m) - 1];
            bin.y += k;
            bin.push(data[i]);
          }
        }
      }
      return bins;
    }
    histogram.value = function(x) {
      if (!arguments.length) return valuer;
      valuer = x;
      return histogram;
    };
    histogram.range = function(x) {
      if (!arguments.length) return ranger;
      ranger = d3_functor(x);
      return histogram;
    };
    histogram.bins = function(x) {
      if (!arguments.length) return binner;
      binner = typeof x === "number" ? function(range) {
        return d3_layout_histogramBinFixed(range, x);
      } : d3_functor(x);
      return histogram;
    };
    histogram.frequency = function(x) {
      if (!arguments.length) return frequency;
      frequency = !!x;
      return histogram;
    };
    return histogram;
  };
  function d3_layout_histogramBinSturges(range, values) {
    return d3_layout_histogramBinFixed(range, Math.ceil(Math.log(values.length) / Math.LN2 + 1));
  }
  function d3_layout_histogramBinFixed(range, n) {
    var x = -1, b = +range[0], m = (range[1] - b) / n, f = [];
    while (++x <= n) f[x] = m * x + b;
    return f;
  }
  function d3_layout_histogramRange(values) {
    return [ d3.min(values), d3.max(values) ];
  }
  d3.layout.tree = function() {
    var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = false;
    function tree(d, i) {
      var nodes = hierarchy.call(this, d, i), root = nodes[0];
      function firstWalk(node, previousSibling) {
        var children = node.children, layout = node._tree;
        if (children && (n = children.length)) {
          var n, firstChild = children[0], previousChild, ancestor = firstChild, child, i = -1;
          while (++i < n) {
            child = children[i];
            firstWalk(child, previousChild);
            ancestor = apportion(child, previousChild, ancestor);
            previousChild = child;
          }
          d3_layout_treeShift(node);
          var midpoint = .5 * (firstChild._tree.prelim + child._tree.prelim);
          if (previousSibling) {
            layout.prelim = previousSibling._tree.prelim + separation(node, previousSibling);
            layout.mod = layout.prelim - midpoint;
          } else {
            layout.prelim = midpoint;
          }
        } else {
          if (previousSibling) {
            layout.prelim = previousSibling._tree.prelim + separation(node, previousSibling);
          }
        }
      }
      function secondWalk(node, x) {
        node.x = node._tree.prelim + x;
        var children = node.children;
        if (children && (n = children.length)) {
          var i = -1, n;
          x += node._tree.mod;
          while (++i < n) {
            secondWalk(children[i], x);
          }
        }
      }
      function apportion(node, previousSibling, ancestor) {
        if (previousSibling) {
          var vip = node, vop = node, vim = previousSibling, vom = node.parent.children[0], sip = vip._tree.mod, sop = vop._tree.mod, sim = vim._tree.mod, som = vom._tree.mod, shift;
          while (vim = d3_layout_treeRight(vim), vip = d3_layout_treeLeft(vip), vim && vip) {
            vom = d3_layout_treeLeft(vom);
            vop = d3_layout_treeRight(vop);
            vop._tree.ancestor = node;
            shift = vim._tree.prelim + sim - vip._tree.prelim - sip + separation(vim, vip);
            if (shift > 0) {
              d3_layout_treeMove(d3_layout_treeAncestor(vim, node, ancestor), node, shift);
              sip += shift;
              sop += shift;
            }
            sim += vim._tree.mod;
            sip += vip._tree.mod;
            som += vom._tree.mod;
            sop += vop._tree.mod;
          }
          if (vim && !d3_layout_treeRight(vop)) {
            vop._tree.thread = vim;
            vop._tree.mod += sim - sop;
          }
          if (vip && !d3_layout_treeLeft(vom)) {
            vom._tree.thread = vip;
            vom._tree.mod += sip - som;
            ancestor = node;
          }
        }
        return ancestor;
      }
      d3_layout_treeVisitAfter(root, function(node, previousSibling) {
        node._tree = {
          ancestor: node,
          prelim: 0,
          mod: 0,
          change: 0,
          shift: 0,
          number: previousSibling ? previousSibling._tree.number + 1 : 0
        };
      });
      firstWalk(root);
      secondWalk(root, -root._tree.prelim);
      var left = d3_layout_treeSearch(root, d3_layout_treeLeftmost), right = d3_layout_treeSearch(root, d3_layout_treeRightmost), deep = d3_layout_treeSearch(root, d3_layout_treeDeepest), x0 = left.x - separation(left, right) / 2, x1 = right.x + separation(right, left) / 2, y1 = deep.depth || 1;
      d3_layout_treeVisitAfter(root, nodeSize ? function(node) {
        node.x *= size[0];
        node.y = node.depth * size[1];
        delete node._tree;
      } : function(node) {
        node.x = (node.x - x0) / (x1 - x0) * size[0];
        node.y = node.depth / y1 * size[1];
        delete node._tree;
      });
      return nodes;
    }
    tree.separation = function(x) {
      if (!arguments.length) return separation;
      separation = x;
      return tree;
    };
    tree.size = function(x) {
      if (!arguments.length) return nodeSize ? null : size;
      nodeSize = (size = x) == null;
      return tree;
    };
    tree.nodeSize = function(x) {
      if (!arguments.length) return nodeSize ? size : null;
      nodeSize = (size = x) != null;
      return tree;
    };
    return d3_layout_hierarchyRebind(tree, hierarchy);
  };
  function d3_layout_treeSeparation(a, b) {
    return a.parent == b.parent ? 1 : 2;
  }
  function d3_layout_treeLeft(node) {
    var children = node.children;
    return children && children.length ? children[0] : node._tree.thread;
  }
  function d3_layout_treeRight(node) {
    var children = node.children, n;
    return children && (n = children.length) ? children[n - 1] : node._tree.thread;
  }
  function d3_layout_treeSearch(node, compare) {
    var children = node.children;
    if (children && (n = children.length)) {
      var child, n, i = -1;
      while (++i < n) {
        if (compare(child = d3_layout_treeSearch(children[i], compare), node) > 0) {
          node = child;
        }
      }
    }
    return node;
  }
  function d3_layout_treeRightmost(a, b) {
    return a.x - b.x;
  }
  function d3_layout_treeLeftmost(a, b) {
    return b.x - a.x;
  }
  function d3_layout_treeDeepest(a, b) {
    return a.depth - b.depth;
  }
  function d3_layout_treeVisitAfter(node, callback) {
    function visit(node, previousSibling) {
      var children = node.children;
      if (children && (n = children.length)) {
        var child, previousChild = null, i = -1, n;
        while (++i < n) {
          child = children[i];
          visit(child, previousChild);
          previousChild = child;
        }
      }
      callback(node, previousSibling);
    }
    visit(node, null);
  }
  function d3_layout_treeShift(node) {
    var shift = 0, change = 0, children = node.children, i = children.length, child;
    while (--i >= 0) {
      child = children[i]._tree;
      child.prelim += shift;
      child.mod += shift;
      shift += child.shift + (change += child.change);
    }
  }
  function d3_layout_treeMove(ancestor, node, shift) {
    ancestor = ancestor._tree;
    node = node._tree;
    var change = shift / (node.number - ancestor.number);
    ancestor.change += change;
    node.change -= change;
    node.shift += shift;
    node.prelim += shift;
    node.mod += shift;
  }
  function d3_layout_treeAncestor(vim, node, ancestor) {
    return vim._tree.ancestor.parent == node.parent ? vim._tree.ancestor : ancestor;
  }
  d3.layout.pack = function() {
    var hierarchy = d3.layout.hierarchy().sort(d3_layout_packSort), padding = 0, size = [ 1, 1 ], radius;
    function pack(d, i) {
      var nodes = hierarchy.call(this, d, i), root = nodes[0], w = size[0], h = size[1], r = radius == null ? Math.sqrt : typeof radius === "function" ? radius : function() {
        return radius;
      };
      root.x = root.y = 0;
      d3_layout_treeVisitAfter(root, function(d) {
        d.r = +r(d.value);
      });
      d3_layout_treeVisitAfter(root, d3_layout_packSiblings);
      if (padding) {
        var dr = padding * (radius ? 1 : Math.max(2 * root.r / w, 2 * root.r / h)) / 2;
        d3_layout_treeVisitAfter(root, function(d) {
          d.r += dr;
        });
        d3_layout_treeVisitAfter(root, d3_layout_packSiblings);
        d3_layout_treeVisitAfter(root, function(d) {
          d.r -= dr;
        });
      }
      d3_layout_packTransform(root, w / 2, h / 2, radius ? 1 : 1 / Math.max(2 * root.r / w, 2 * root.r / h));
      return nodes;
    }
    pack.size = function(_) {
      if (!arguments.length) return size;
      size = _;
      return pack;
    };
    pack.radius = function(_) {
      if (!arguments.length) return radius;
      radius = _ == null || typeof _ === "function" ? _ : +_;
      return pack;
    };
    pack.padding = function(_) {
      if (!arguments.length) return padding;
      padding = +_;
      return pack;
    };
    return d3_layout_hierarchyRebind(pack, hierarchy);
  };
  function d3_layout_packSort(a, b) {
    return a.value - b.value;
  }
  function d3_layout_packInsert(a, b) {
    var c = a._pack_next;
    a._pack_next = b;
    b._pack_prev = a;
    b._pack_next = c;
    c._pack_prev = b;
  }
  function d3_layout_packSplice(a, b) {
    a._pack_next = b;
    b._pack_prev = a;
  }
  function d3_layout_packIntersects(a, b) {
    var dx = b.x - a.x, dy = b.y - a.y, dr = a.r + b.r;
    return .999 * dr * dr > dx * dx + dy * dy;
  }
  function d3_layout_packSiblings(node) {
    if (!(nodes = node.children) || !(n = nodes.length)) return;
    var nodes, xMin = Infinity, xMax = -Infinity, yMin = Infinity, yMax = -Infinity, a, b, c, i, j, k, n;
    function bound(node) {
      xMin = Math.min(node.x - node.r, xMin);
      xMax = Math.max(node.x + node.r, xMax);
      yMin = Math.min(node.y - node.r, yMin);
      yMax = Math.max(node.y + node.r, yMax);
    }
    nodes.forEach(d3_layout_packLink);
    a = nodes[0];
    a.x = -a.r;
    a.y = 0;
    bound(a);
    if (n > 1) {
      b = nodes[1];
      b.x = b.r;
      b.y = 0;
      bound(b);
      if (n > 2) {
        c = nodes[2];
        d3_layout_packPlace(a, b, c);
        bound(c);
        d3_layout_packInsert(a, c);
        a._pack_prev = c;
        d3_layout_packInsert(c, b);
        b = a._pack_next;
        for (i = 3; i < n; i++) {
          d3_layout_packPlace(a, b, c = nodes[i]);
          var isect = 0, s1 = 1, s2 = 1;
          for (j = b._pack_next; j !== b; j = j._pack_next, s1++) {
            if (d3_layout_packIntersects(j, c)) {
              isect = 1;
              break;
            }
          }
          if (isect == 1) {
            for (k = a._pack_prev; k !== j._pack_prev; k = k._pack_prev, s2++) {
              if (d3_layout_packIntersects(k, c)) {
                break;
              }
            }
          }
          if (isect) {
            if (s1 < s2 || s1 == s2 && b.r < a.r) d3_layout_packSplice(a, b = j); else d3_layout_packSplice(a = k, b);
            i--;
          } else {
            d3_layout_packInsert(a, c);
            b = c;
            bound(c);
          }
        }
      }
    }
    var cx = (xMin + xMax) / 2, cy = (yMin + yMax) / 2, cr = 0;
    for (i = 0; i < n; i++) {
      c = nodes[i];
      c.x -= cx;
      c.y -= cy;
      cr = Math.max(cr, c.r + Math.sqrt(c.x * c.x + c.y * c.y));
    }
    node.r = cr;
    nodes.forEach(d3_layout_packUnlink);
  }
  function d3_layout_packLink(node) {
    node._pack_next = node._pack_prev = node;
  }
  function d3_layout_packUnlink(node) {
    delete node._pack_next;
    delete node._pack_prev;
  }
  function d3_layout_packTransform(node, x, y, k) {
    var children = node.children;
    node.x = x += k * node.x;
    node.y = y += k * node.y;
    node.r *= k;
    if (children) {
      var i = -1, n = children.length;
      while (++i < n) d3_layout_packTransform(children[i], x, y, k);
    }
  }
  function d3_layout_packPlace(a, b, c) {
    var db = a.r + c.r, dx = b.x - a.x, dy = b.y - a.y;
    if (db && (dx || dy)) {
      var da = b.r + c.r, dc = dx * dx + dy * dy;
      da *= da;
      db *= db;
      var x = .5 + (db - da) / (2 * dc), y = Math.sqrt(Math.max(0, 2 * da * (db + dc) - (db -= dc) * db - da * da)) / (2 * dc);
      c.x = a.x + x * dx + y * dy;
      c.y = a.y + x * dy - y * dx;
    } else {
      c.x = a.x + db;
      c.y = a.y;
    }
  }
  d3.layout.cluster = function() {
    var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = false;
    function cluster(d, i) {
      var nodes = hierarchy.call(this, d, i), root = nodes[0], previousNode, x = 0;
      d3_layout_treeVisitAfter(root, function(node) {
        var children = node.children;
        if (children && children.length) {
          node.x = d3_layout_clusterX(children);
          node.y = d3_layout_clusterY(children);
        } else {
          node.x = previousNode ? x += separation(node, previousNode) : 0;
          node.y = 0;
          previousNode = node;
        }
      });
      var left = d3_layout_clusterLeft(root), right = d3_layout_clusterRight(root), x0 = left.x - separation(left, right) / 2, x1 = right.x + separation(right, left) / 2;
      d3_layout_treeVisitAfter(root, nodeSize ? function(node) {
        node.x = (node.x - root.x) * size[0];
        node.y = (root.y - node.y) * size[1];
      } : function(node) {
        node.x = (node.x - x0) / (x1 - x0) * size[0];
        node.y = (1 - (root.y ? node.y / root.y : 1)) * size[1];
      });
      return nodes;
    }
    cluster.separation = function(x) {
      if (!arguments.length) return separation;
      separation = x;
      return cluster;
    };
    cluster.size = function(x) {
      if (!arguments.length) return nodeSize ? null : size;
      nodeSize = (size = x) == null;
      return cluster;
    };
    cluster.nodeSize = function(x) {
      if (!arguments.length) return nodeSize ? size : null;
      nodeSize = (size = x) != null;
      return cluster;
    };
    return d3_layout_hierarchyRebind(cluster, hierarchy);
  };
  function d3_layout_clusterY(children) {
    return 1 + d3.max(children, function(child) {
      return child.y;
    });
  }
  function d3_layout_clusterX(children) {
    return children.reduce(function(x, child) {
      return x + child.x;
    }, 0) / children.length;
  }
  function d3_layout_clusterLeft(node) {
    var children = node.children;
    return children && children.length ? d3_layout_clusterLeft(children[0]) : node;
  }
  function d3_layout_clusterRight(node) {
    var children = node.children, n;
    return children && (n = children.length) ? d3_layout_clusterRight(children[n - 1]) : node;
  }
  d3.layout.treemap = function() {
    var hierarchy = d3.layout.hierarchy(), round = Math.round, size = [ 1, 1 ], padding = null, pad = d3_layout_treemapPadNull, sticky = false, stickies, mode = "squarify", ratio = .5 * (1 + Math.sqrt(5));
    function scale(children, k) {
      var i = -1, n = children.length, child, area;
      while (++i < n) {
        area = (child = children[i]).value * (k < 0 ? 0 : k);
        child.area = isNaN(area) || area <= 0 ? 0 : area;
      }
    }
    function squarify(node) {
      var children = node.children;
      if (children && children.length) {
        var rect = pad(node), row = [], remaining = children.slice(), child, best = Infinity, score, u = mode === "slice" ? rect.dx : mode === "dice" ? rect.dy : mode === "slice-dice" ? node.depth & 1 ? rect.dy : rect.dx : Math.min(rect.dx, rect.dy), n;
        scale(remaining, rect.dx * rect.dy / node.value);
        row.area = 0;
        while ((n = remaining.length) > 0) {
          row.push(child = remaining[n - 1]);
          row.area += child.area;
          if (mode !== "squarify" || (score = worst(row, u)) <= best) {
            remaining.pop();
            best = score;
          } else {
            row.area -= row.pop().area;
            position(row, u, rect, false);
            u = Math.min(rect.dx, rect.dy);
            row.length = row.area = 0;
            best = Infinity;
          }
        }
        if (row.length) {
          position(row, u, rect, true);
          row.length = row.area = 0;
        }
        children.forEach(squarify);
      }
    }
    function stickify(node) {
      var children = node.children;
      if (children && children.length) {
        var rect = pad(node), remaining = children.slice(), child, row = [];
        scale(remaining, rect.dx * rect.dy / node.value);
        row.area = 0;
        while (child = remaining.pop()) {
          row.push(child);
          row.area += child.area;
          if (child.z != null) {
            position(row, child.z ? rect.dx : rect.dy, rect, !remaining.length);
            row.length = row.area = 0;
          }
        }
        children.forEach(stickify);
      }
    }
    function worst(row, u) {
      var s = row.area, r, rmax = 0, rmin = Infinity, i = -1, n = row.length;
      while (++i < n) {
        if (!(r = row[i].area)) continue;
        if (r < rmin) rmin = r;
        if (r > rmax) rmax = r;
      }
      s *= s;
      u *= u;
      return s ? Math.max(u * rmax * ratio / s, s / (u * rmin * ratio)) : Infinity;
    }
    function position(row, u, rect, flush) {
      var i = -1, n = row.length, x = rect.x, y = rect.y, v = u ? round(row.area / u) : 0, o;
      if (u == rect.dx) {
        if (flush || v > rect.dy) v = rect.dy;
        while (++i < n) {
          o = row[i];
          o.x = x;
          o.y = y;
          o.dy = v;
          x += o.dx = Math.min(rect.x + rect.dx - x, v ? round(o.area / v) : 0);
        }
        o.z = true;
        o.dx += rect.x + rect.dx - x;
        rect.y += v;
        rect.dy -= v;
      } else {
        if (flush || v > rect.dx) v = rect.dx;
        while (++i < n) {
          o = row[i];
          o.x = x;
          o.y = y;
          o.dx = v;
          y += o.dy = Math.min(rect.y + rect.dy - y, v ? round(o.area / v) : 0);
        }
        o.z = false;
        o.dy += rect.y + rect.dy - y;
        rect.x += v;
        rect.dx -= v;
      }
    }
    function treemap(d) {
      var nodes = stickies || hierarchy(d), root = nodes[0];
      root.x = 0;
      root.y = 0;
      root.dx = size[0];
      root.dy = size[1];
      if (stickies) hierarchy.revalue(root);
      scale([ root ], root.dx * root.dy / root.value);
      (stickies ? stickify : squarify)(root);
      if (sticky) stickies = nodes;
      return nodes;
    }
    treemap.size = function(x) {
      if (!arguments.length) return size;
      size = x;
      return treemap;
    };
    treemap.padding = function(x) {
      if (!arguments.length) return padding;
      function padFunction(node) {
        var p = x.call(treemap, node, node.depth);
        return p == null ? d3_layout_treemapPadNull(node) : d3_layout_treemapPad(node, typeof p === "number" ? [ p, p, p, p ] : p);
      }
      function padConstant(node) {
        return d3_layout_treemapPad(node, x);
      }
      var type;
      pad = (padding = x) == null ? d3_layout_treemapPadNull : (type = typeof x) === "function" ? padFunction : type === "number" ? (x = [ x, x, x, x ], 
      padConstant) : padConstant;
      return treemap;
    };
    treemap.round = function(x) {
      if (!arguments.length) return round != Number;
      round = x ? Math.round : Number;
      return treemap;
    };
    treemap.sticky = function(x) {
      if (!arguments.length) return sticky;
      sticky = x;
      stickies = null;
      return treemap;
    };
    treemap.ratio = function(x) {
      if (!arguments.length) return ratio;
      ratio = x;
      return treemap;
    };
    treemap.mode = function(x) {
      if (!arguments.length) return mode;
      mode = x + "";
      return treemap;
    };
    return d3_layout_hierarchyRebind(treemap, hierarchy);
  };
  function d3_layout_treemapPadNull(node) {
    return {
      x: node.x,
      y: node.y,
      dx: node.dx,
      dy: node.dy
    };
  }
  function d3_layout_treemapPad(node, padding) {
    var x = node.x + padding[3], y = node.y + padding[0], dx = node.dx - padding[1] - padding[3], dy = node.dy - padding[0] - padding[2];
    if (dx < 0) {
      x += dx / 2;
      dx = 0;
    }
    if (dy < 0) {
      y += dy / 2;
      dy = 0;
    }
    return {
      x: x,
      y: y,
      dx: dx,
      dy: dy
    };
  }
  d3.random = {
    normal: function(µ, σ) {
      var n = arguments.length;
      if (n < 2) σ = 1;
      if (n < 1) µ = 0;
      return function() {
        var x, y, r;
        do {
          x = Math.random() * 2 - 1;
          y = Math.random() * 2 - 1;
          r = x * x + y * y;
        } while (!r || r > 1);
        return µ + σ * x * Math.sqrt(-2 * Math.log(r) / r);
      };
    },
    logNormal: function() {
      var random = d3.random.normal.apply(d3, arguments);
      return function() {
        return Math.exp(random());
      };
    },
    bates: function(m) {
      var random = d3.random.irwinHall(m);
      return function() {
        return random() / m;
      };
    },
    irwinHall: function(m) {
      return function() {
        for (var s = 0, j = 0; j < m; j++) s += Math.random();
        return s;
      };
    }
  };
  d3.scale = {};
  function d3_scaleExtent(domain) {
    var start = domain[0], stop = domain[domain.length - 1];
    return start < stop ? [ start, stop ] : [ stop, start ];
  }
  function d3_scaleRange(scale) {
    return scale.rangeExtent ? scale.rangeExtent() : d3_scaleExtent(scale.range());
  }
  function d3_scale_bilinear(domain, range, uninterpolate, interpolate) {
    var u = uninterpolate(domain[0], domain[1]), i = interpolate(range[0], range[1]);
    return function(x) {
      return i(u(x));
    };
  }
  function d3_scale_nice(domain, nice) {
    var i0 = 0, i1 = domain.length - 1, x0 = domain[i0], x1 = domain[i1], dx;
    if (x1 < x0) {
      dx = i0, i0 = i1, i1 = dx;
      dx = x0, x0 = x1, x1 = dx;
    }
    domain[i0] = nice.floor(x0);
    domain[i1] = nice.ceil(x1);
    return domain;
  }
  function d3_scale_niceStep(step) {
    return step ? {
      floor: function(x) {
        return Math.floor(x / step) * step;
      },
      ceil: function(x) {
        return Math.ceil(x / step) * step;
      }
    } : d3_scale_niceIdentity;
  }
  var d3_scale_niceIdentity = {
    floor: d3_identity,
    ceil: d3_identity
  };
  function d3_scale_polylinear(domain, range, uninterpolate, interpolate) {
    var u = [], i = [], j = 0, k = Math.min(domain.length, range.length) - 1;
    if (domain[k] < domain[0]) {
      domain = domain.slice().reverse();
      range = range.slice().reverse();
    }
    while (++j <= k) {
      u.push(uninterpolate(domain[j - 1], domain[j]));
      i.push(interpolate(range[j - 1], range[j]));
    }
    return function(x) {
      var j = d3.bisect(domain, x, 1, k) - 1;
      return i[j](u[j](x));
    };
  }
  d3.scale.linear = function() {
    return d3_scale_linear([ 0, 1 ], [ 0, 1 ], d3_interpolate, false);
  };
  function d3_scale_linear(domain, range, interpolate, clamp) {
    var output, input;
    function rescale() {
      var linear = Math.min(domain.length, range.length) > 2 ? d3_scale_polylinear : d3_scale_bilinear, uninterpolate = clamp ? d3_uninterpolateClamp : d3_uninterpolateNumber;
      output = linear(domain, range, uninterpolate, interpolate);
      input = linear(range, domain, uninterpolate, d3_interpolate);
      return scale;
    }
    function scale(x) {
      return output(x);
    }
    scale.invert = function(y) {
      return input(y);
    };
    scale.domain = function(x) {
      if (!arguments.length) return domain;
      domain = x.map(Number);
      return rescale();
    };
    scale.range = function(x) {
      if (!arguments.length) return range;
      range = x;
      return rescale();
    };
    scale.rangeRound = function(x) {
      return scale.range(x).interpolate(d3_interpolateRound);
    };
    scale.clamp = function(x) {
      if (!arguments.length) return clamp;
      clamp = x;
      return rescale();
    };
    scale.interpolate = function(x) {
      if (!arguments.length) return interpolate;
      interpolate = x;
      return rescale();
    };
    scale.ticks = function(m) {
      return d3_scale_linearTicks(domain, m);
    };
    scale.tickFormat = function(m, format) {
      return d3_scale_linearTickFormat(domain, m, format);
    };
    scale.nice = function(m) {
      d3_scale_linearNice(domain, m);
      return rescale();
    };
    scale.copy = function() {
      return d3_scale_linear(domain, range, interpolate, clamp);
    };
    return rescale();
  }
  function d3_scale_linearRebind(scale, linear) {
    return d3.rebind(scale, linear, "range", "rangeRound", "interpolate", "clamp");
  }
  function d3_scale_linearNice(domain, m) {
    return d3_scale_nice(domain, d3_scale_niceStep(d3_scale_linearTickRange(domain, m)[2]));
  }
  function d3_scale_linearTickRange(domain, m) {
    if (m == null) m = 10;
    var extent = d3_scaleExtent(domain), span = extent[1] - extent[0], step = Math.pow(10, Math.floor(Math.log(span / m) / Math.LN10)), err = m / span * step;
    if (err <= .15) step *= 10; else if (err <= .35) step *= 5; else if (err <= .75) step *= 2;
    extent[0] = Math.ceil(extent[0] / step) * step;
    extent[1] = Math.floor(extent[1] / step) * step + step * .5;
    extent[2] = step;
    return extent;
  }
  function d3_scale_linearTicks(domain, m) {
    return d3.range.apply(d3, d3_scale_linearTickRange(domain, m));
  }
  function d3_scale_linearTickFormat(domain, m, format) {
    var range = d3_scale_linearTickRange(domain, m);
    return d3.format(format ? format.replace(d3_format_re, function(a, b, c, d, e, f, g, h, i, j) {
      return [ b, c, d, e, f, g, h, i || "." + d3_scale_linearFormatPrecision(j, range), j ].join("");
    }) : ",." + d3_scale_linearPrecision(range[2]) + "f");
  }
  var d3_scale_linearFormatSignificant = {
    s: 1,
    g: 1,
    p: 1,
    r: 1,
    e: 1
  };
  function d3_scale_linearPrecision(value) {
    return -Math.floor(Math.log(value) / Math.LN10 + .01);
  }
  function d3_scale_linearFormatPrecision(type, range) {
    var p = d3_scale_linearPrecision(range[2]);
    return type in d3_scale_linearFormatSignificant ? Math.abs(p - d3_scale_linearPrecision(Math.max(Math.abs(range[0]), Math.abs(range[1])))) + +(type !== "e") : p - (type === "%") * 2;
  }
  d3.scale.log = function() {
    return d3_scale_log(d3.scale.linear().domain([ 0, 1 ]), 10, true, [ 1, 10 ]);
  };
  function d3_scale_log(linear, base, positive, domain) {
    function log(x) {
      return (positive ? Math.log(x < 0 ? 0 : x) : -Math.log(x > 0 ? 0 : -x)) / Math.log(base);
    }
    function pow(x) {
      return positive ? Math.pow(base, x) : -Math.pow(base, -x);
    }
    function scale(x) {
      return linear(log(x));
    }
    scale.invert = function(x) {
      return pow(linear.invert(x));
    };
    scale.domain = function(x) {
      if (!arguments.length) return domain;
      positive = x[0] >= 0;
      linear.domain((domain = x.map(Number)).map(log));
      return scale;
    };
    scale.base = function(_) {
      if (!arguments.length) return base;
      base = +_;
      linear.domain(domain.map(log));
      return scale;
    };
    scale.nice = function() {
      var niced = d3_scale_nice(domain.map(log), positive ? Math : d3_scale_logNiceNegative);
      linear.domain(niced);
      domain = niced.map(pow);
      return scale;
    };
    scale.ticks = function() {
      var extent = d3_scaleExtent(domain), ticks = [], u = extent[0], v = extent[1], i = Math.floor(log(u)), j = Math.ceil(log(v)), n = base % 1 ? 2 : base;
      if (isFinite(j - i)) {
        if (positive) {
          for (;i < j; i++) for (var k = 1; k < n; k++) ticks.push(pow(i) * k);
          ticks.push(pow(i));
        } else {
          ticks.push(pow(i));
          for (;i++ < j; ) for (var k = n - 1; k > 0; k--) ticks.push(pow(i) * k);
        }
        for (i = 0; ticks[i] < u; i++) {}
        for (j = ticks.length; ticks[j - 1] > v; j--) {}
        ticks = ticks.slice(i, j);
      }
      return ticks;
    };
    scale.tickFormat = function(n, format) {
      if (!arguments.length) return d3_scale_logFormat;
      if (arguments.length < 2) format = d3_scale_logFormat; else if (typeof format !== "function") format = d3.format(format);
      var k = Math.max(.1, n / scale.ticks().length), f = positive ? (e = 1e-12, Math.ceil) : (e = -1e-12, 
      Math.floor), e;
      return function(d) {
        return d / pow(f(log(d) + e)) <= k ? format(d) : "";
      };
    };
    scale.copy = function() {
      return d3_scale_log(linear.copy(), base, positive, domain);
    };
    return d3_scale_linearRebind(scale, linear);
  }
  var d3_scale_logFormat = d3.format(".0e"), d3_scale_logNiceNegative = {
    floor: function(x) {
      return -Math.ceil(-x);
    },
    ceil: function(x) {
      return -Math.floor(-x);
    }
  };
  d3.scale.pow = function() {
    return d3_scale_pow(d3.scale.linear(), 1, [ 0, 1 ]);
  };
  function d3_scale_pow(linear, exponent, domain) {
    var powp = d3_scale_powPow(exponent), powb = d3_scale_powPow(1 / exponent);
    function scale(x) {
      return linear(powp(x));
    }
    scale.invert = function(x) {
      return powb(linear.invert(x));
    };
    scale.domain = function(x) {
      if (!arguments.length) return domain;
      linear.domain((domain = x.map(Number)).map(powp));
      return scale;
    };
    scale.ticks = function(m) {
      return d3_scale_linearTicks(domain, m);
    };
    scale.tickFormat = function(m, format) {
      return d3_scale_linearTickFormat(domain, m, format);
    };
    scale.nice = function(m) {
      return scale.domain(d3_scale_linearNice(domain, m));
    };
    scale.exponent = function(x) {
      if (!arguments.length) return exponent;
      powp = d3_scale_powPow(exponent = x);
      powb = d3_scale_powPow(1 / exponent);
      linear.domain(domain.map(powp));
      return scale;
    };
    scale.copy = function() {
      return d3_scale_pow(linear.copy(), exponent, domain);
    };
    return d3_scale_linearRebind(scale, linear);
  }
  function d3_scale_powPow(e) {
    return function(x) {
      return x < 0 ? -Math.pow(-x, e) : Math.pow(x, e);
    };
  }
  d3.scale.sqrt = function() {
    return d3.scale.pow().exponent(.5);
  };
  d3.scale.ordinal = function() {
    return d3_scale_ordinal([], {
      t: "range",
      a: [ [] ]
    });
  };
  function d3_scale_ordinal(domain, ranger) {
    var index, range, rangeBand;
    function scale(x) {
      return range[((index.get(x) || ranger.t === "range" && index.set(x, domain.push(x))) - 1) % range.length];
    }
    function steps(start, step) {
      return d3.range(domain.length).map(function(i) {
        return start + step * i;
      });
    }
    scale.domain = function(x) {
      if (!arguments.length) return domain;
      domain = [];
      index = new d3_Map();
      var i = -1, n = x.length, xi;
      while (++i < n) if (!index.has(xi = x[i])) index.set(xi, domain.push(xi));
      return scale[ranger.t].apply(scale, ranger.a);
    };
    scale.range = function(x) {
      if (!arguments.length) return range;
      range = x;
      rangeBand = 0;
      ranger = {
        t: "range",
        a: arguments
      };
      return scale;
    };
    scale.rangePoints = function(x, padding) {
      if (arguments.length < 2) padding = 0;
      var start = x[0], stop = x[1], step = (stop - start) / (Math.max(1, domain.length - 1) + padding);
      range = steps(domain.length < 2 ? (start + stop) / 2 : start + step * padding / 2, step);
      rangeBand = 0;
      ranger = {
        t: "rangePoints",
        a: arguments
      };
      return scale;
    };
    scale.rangeBands = function(x, padding, outerPadding) {
      if (arguments.length < 2) padding = 0;
      if (arguments.length < 3) outerPadding = padding;
      var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = (stop - start) / (domain.length - padding + 2 * outerPadding);
      range = steps(start + step * outerPadding, step);
      if (reverse) range.reverse();
      rangeBand = step * (1 - padding);
      ranger = {
        t: "rangeBands",
        a: arguments
      };
      return scale;
    };
    scale.rangeRoundBands = function(x, padding, outerPadding) {
      if (arguments.length < 2) padding = 0;
      if (arguments.length < 3) outerPadding = padding;
      var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = Math.floor((stop - start) / (domain.length - padding + 2 * outerPadding)), error = stop - start - (domain.length - padding) * step;
      range = steps(start + Math.round(error / 2), step);
      if (reverse) range.reverse();
      rangeBand = Math.round(step * (1 - padding));
      ranger = {
        t: "rangeRoundBands",
        a: arguments
      };
      return scale;
    };
    scale.rangeBand = function() {
      return rangeBand;
    };
    scale.rangeExtent = function() {
      return d3_scaleExtent(ranger.a[0]);
    };
    scale.copy = function() {
      return d3_scale_ordinal(domain, ranger);
    };
    return scale.domain(domain);
  }
  d3.scale.category10 = function() {
    return d3.scale.ordinal().range(d3_category10);
  };
  d3.scale.category20 = function() {
    return d3.scale.ordinal().range(d3_category20);
  };
  d3.scale.category20b = function() {
    return d3.scale.ordinal().range(d3_category20b);
  };
  d3.scale.category20c = function() {
    return d3.scale.ordinal().range(d3_category20c);
  };
  var d3_category10 = [ 2062260, 16744206, 2924588, 14034728, 9725885, 9197131, 14907330, 8355711, 12369186, 1556175 ].map(d3_rgbString);
  var d3_category20 = [ 2062260, 11454440, 16744206, 16759672, 2924588, 10018698, 14034728, 16750742, 9725885, 12955861, 9197131, 12885140, 14907330, 16234194, 8355711, 13092807, 12369186, 14408589, 1556175, 10410725 ].map(d3_rgbString);
  var d3_category20b = [ 3750777, 5395619, 7040719, 10264286, 6519097, 9216594, 11915115, 13556636, 9202993, 12426809, 15186514, 15190932, 8666169, 11356490, 14049643, 15177372, 8077683, 10834324, 13528509, 14589654 ].map(d3_rgbString);
  var d3_category20c = [ 3244733, 7057110, 10406625, 13032431, 15095053, 16616764, 16625259, 16634018, 3253076, 7652470, 10607003, 13101504, 7695281, 10394312, 12369372, 14342891, 6513507, 9868950, 12434877, 14277081 ].map(d3_rgbString);
  d3.scale.quantile = function() {
    return d3_scale_quantile([], []);
  };
  function d3_scale_quantile(domain, range) {
    var thresholds;
    function rescale() {
      var k = 0, q = range.length;
      thresholds = [];
      while (++k < q) thresholds[k - 1] = d3.quantile(domain, k / q);
      return scale;
    }
    function scale(x) {
      if (!isNaN(x = +x)) return range[d3.bisect(thresholds, x)];
    }
    scale.domain = function(x) {
      if (!arguments.length) return domain;
      domain = x.filter(function(d) {
        return !isNaN(d);
      }).sort(d3.ascending);
      return rescale();
    };
    scale.range = function(x) {
      if (!arguments.length) return range;
      range = x;
      return rescale();
    };
    scale.quantiles = function() {
      return thresholds;
    };
    scale.invertExtent = function(y) {
      y = range.indexOf(y);
      return y < 0 ? [ NaN, NaN ] : [ y > 0 ? thresholds[y - 1] : domain[0], y < thresholds.length ? thresholds[y] : domain[domain.length - 1] ];
    };
    scale.copy = function() {
      return d3_scale_quantile(domain, range);
    };
    return rescale();
  }
  d3.scale.quantize = function() {
    return d3_scale_quantize(0, 1, [ 0, 1 ]);
  };
  function d3_scale_quantize(x0, x1, range) {
    var kx, i;
    function scale(x) {
      return range[Math.max(0, Math.min(i, Math.floor(kx * (x - x0))))];
    }
    function rescale() {
      kx = range.length / (x1 - x0);
      i = range.length - 1;
      return scale;
    }
    scale.domain = function(x) {
      if (!arguments.length) return [ x0, x1 ];
      x0 = +x[0];
      x1 = +x[x.length - 1];
      return rescale();
    };
    scale.range = function(x) {
      if (!arguments.length) return range;
      range = x;
      return rescale();
    };
    scale.invertExtent = function(y) {
      y = range.indexOf(y);
      y = y < 0 ? NaN : y / kx + x0;
      return [ y, y + 1 / kx ];
    };
    scale.copy = function() {
      return d3_scale_quantize(x0, x1, range);
    };
    return rescale();
  }
  d3.scale.threshold = function() {
    return d3_scale_threshold([ .5 ], [ 0, 1 ]);
  };
  function d3_scale_threshold(domain, range) {
    function scale(x) {
      if (x <= x) return range[d3.bisect(domain, x)];
    }
    scale.domain = function(_) {
      if (!arguments.length) return domain;
      domain = _;
      return scale;
    };
    scale.range = function(_) {
      if (!arguments.length) return range;
      range = _;
      return scale;
    };
    scale.invertExtent = function(y) {
      y = range.indexOf(y);
      return [ domain[y - 1], domain[y] ];
    };
    scale.copy = function() {
      return d3_scale_threshold(domain, range);
    };
    return scale;
  }
  d3.scale.identity = function() {
    return d3_scale_identity([ 0, 1 ]);
  };
  function d3_scale_identity(domain) {
    function identity(x) {
      return +x;
    }
    identity.invert = identity;
    identity.domain = identity.range = function(x) {
      if (!arguments.length) return domain;
      domain = x.map(identity);
      return identity;
    };
    identity.ticks = function(m) {
      return d3_scale_linearTicks(domain, m);
    };
    identity.tickFormat = function(m, format) {
      return d3_scale_linearTickFormat(domain, m, format);
    };
    identity.copy = function() {
      return d3_scale_identity(domain);
    };
    return identity;
  }
  d3.svg = {};
  d3.svg.arc = function() {
    var innerRadius = d3_svg_arcInnerRadius, outerRadius = d3_svg_arcOuterRadius, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle;
    function arc() {
      var r0 = innerRadius.apply(this, arguments), r1 = outerRadius.apply(this, arguments), a0 = startAngle.apply(this, arguments) + d3_svg_arcOffset, a1 = endAngle.apply(this, arguments) + d3_svg_arcOffset, da = (a1 < a0 && (da = a0, 
      a0 = a1, a1 = da), a1 - a0), df = da < π ? "0" : "1", c0 = Math.cos(a0), s0 = Math.sin(a0), c1 = Math.cos(a1), s1 = Math.sin(a1);
      return da >= d3_svg_arcMax ? r0 ? "M0," + r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + -r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + r1 + "M0," + r0 + "A" + r0 + "," + r0 + " 0 1,0 0," + -r0 + "A" + r0 + "," + r0 + " 0 1,0 0," + r0 + "Z" : "M0," + r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + -r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + r1 + "Z" : r0 ? "M" + r1 * c0 + "," + r1 * s0 + "A" + r1 + "," + r1 + " 0 " + df + ",1 " + r1 * c1 + "," + r1 * s1 + "L" + r0 * c1 + "," + r0 * s1 + "A" + r0 + "," + r0 + " 0 " + df + ",0 " + r0 * c0 + "," + r0 * s0 + "Z" : "M" + r1 * c0 + "," + r1 * s0 + "A" + r1 + "," + r1 + " 0 " + df + ",1 " + r1 * c1 + "," + r1 * s1 + "L0,0" + "Z";
    }
    arc.innerRadius = function(v) {
      if (!arguments.length) return innerRadius;
      innerRadius = d3_functor(v);
      return arc;
    };
    arc.outerRadius = function(v) {
      if (!arguments.length) return outerRadius;
      outerRadius = d3_functor(v);
      return arc;
    };
    arc.startAngle = function(v) {
      if (!arguments.length) return startAngle;
      startAngle = d3_functor(v);
      return arc;
    };
    arc.endAngle = function(v) {
      if (!arguments.length) return endAngle;
      endAngle = d3_functor(v);
      return arc;
    };
    arc.centroid = function() {
      var r = (innerRadius.apply(this, arguments) + outerRadius.apply(this, arguments)) / 2, a = (startAngle.apply(this, arguments) + endAngle.apply(this, arguments)) / 2 + d3_svg_arcOffset;
      return [ Math.cos(a) * r, Math.sin(a) * r ];
    };
    return arc;
  };
  var d3_svg_arcOffset = -halfπ, d3_svg_arcMax = τ - ε;
  function d3_svg_arcInnerRadius(d) {
    return d.innerRadius;
  }
  function d3_svg_arcOuterRadius(d) {
    return d.outerRadius;
  }
  function d3_svg_arcStartAngle(d) {
    return d.startAngle;
  }
  function d3_svg_arcEndAngle(d) {
    return d.endAngle;
  }
  function d3_svg_line(projection) {
    var x = d3_geom_pointX, y = d3_geom_pointY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, tension = .7;
    function line(data) {
      var segments = [], points = [], i = -1, n = data.length, d, fx = d3_functor(x), fy = d3_functor(y);
      function segment() {
        segments.push("M", interpolate(projection(points), tension));
      }
      while (++i < n) {
        if (defined.call(this, d = data[i], i)) {
          points.push([ +fx.call(this, d, i), +fy.call(this, d, i) ]);
        } else if (points.length) {
          segment();
          points = [];
        }
      }
      if (points.length) segment();
      return segments.length ? segments.join("") : null;
    }
    line.x = function(_) {
      if (!arguments.length) return x;
      x = _;
      return line;
    };
    line.y = function(_) {
      if (!arguments.length) return y;
      y = _;
      return line;
    };
    line.defined = function(_) {
      if (!arguments.length) return defined;
      defined = _;
      return line;
    };
    line.interpolate = function(_) {
      if (!arguments.length) return interpolateKey;
      if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key;
      return line;
    };
    line.tension = function(_) {
      if (!arguments.length) return tension;
      tension = _;
      return line;
    };
    return line;
  }
  d3.svg.line = function() {
    return d3_svg_line(d3_identity);
  };
  var d3_svg_lineInterpolators = d3.map({
    linear: d3_svg_lineLinear,
    "linear-closed": d3_svg_lineLinearClosed,
    step: d3_svg_lineStep,
    "step-before": d3_svg_lineStepBefore,
    "step-after": d3_svg_lineStepAfter,
    basis: d3_svg_lineBasis,
    "basis-open": d3_svg_lineBasisOpen,
    "basis-closed": d3_svg_lineBasisClosed,
    bundle: d3_svg_lineBundle,
    cardinal: d3_svg_lineCardinal,
    "cardinal-open": d3_svg_lineCardinalOpen,
    "cardinal-closed": d3_svg_lineCardinalClosed,
    monotone: d3_svg_lineMonotone
  });
  d3_svg_lineInterpolators.forEach(function(key, value) {
    value.key = key;
    value.closed = /-closed$/.test(key);
  });
  function d3_svg_lineLinear(points) {
    return points.join("L");
  }
  function d3_svg_lineLinearClosed(points) {
    return d3_svg_lineLinear(points) + "Z";
  }
  function d3_svg_lineStep(points) {
    var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ];
    while (++i < n) path.push("H", (p[0] + (p = points[i])[0]) / 2, "V", p[1]);
    if (n > 1) path.push("H", p[0]);
    return path.join("");
  }
  function d3_svg_lineStepBefore(points) {
    var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ];
    while (++i < n) path.push("V", (p = points[i])[1], "H", p[0]);
    return path.join("");
  }
  function d3_svg_lineStepAfter(points) {
    var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ];
    while (++i < n) path.push("H", (p = points[i])[0], "V", p[1]);
    return path.join("");
  }
  function d3_svg_lineCardinalOpen(points, tension) {
    return points.length < 4 ? d3_svg_lineLinear(points) : points[1] + d3_svg_lineHermite(points.slice(1, points.length - 1), d3_svg_lineCardinalTangents(points, tension));
  }
  function d3_svg_lineCardinalClosed(points, tension) {
    return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite((points.push(points[0]), 
    points), d3_svg_lineCardinalTangents([ points[points.length - 2] ].concat(points, [ points[1] ]), tension));
  }
  function d3_svg_lineCardinal(points, tension) {
    return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineCardinalTangents(points, tension));
  }
  function d3_svg_lineHermite(points, tangents) {
    if (tangents.length < 1 || points.length != tangents.length && points.length != tangents.length + 2) {
      return d3_svg_lineLinear(points);
    }
    var quad = points.length != tangents.length, path = "", p0 = points[0], p = points[1], t0 = tangents[0], t = t0, pi = 1;
    if (quad) {
      path += "Q" + (p[0] - t0[0] * 2 / 3) + "," + (p[1] - t0[1] * 2 / 3) + "," + p[0] + "," + p[1];
      p0 = points[1];
      pi = 2;
    }
    if (tangents.length > 1) {
      t = tangents[1];
      p = points[pi];
      pi++;
      path += "C" + (p0[0] + t0[0]) + "," + (p0[1] + t0[1]) + "," + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1];
      for (var i = 2; i < tangents.length; i++, pi++) {
        p = points[pi];
        t = tangents[i];
        path += "S" + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1];
      }
    }
    if (quad) {
      var lp = points[pi];
      path += "Q" + (p[0] + t[0] * 2 / 3) + "," + (p[1] + t[1] * 2 / 3) + "," + lp[0] + "," + lp[1];
    }
    return path;
  }
  function d3_svg_lineCardinalTangents(points, tension) {
    var tangents = [], a = (1 - tension) / 2, p0, p1 = points[0], p2 = points[1], i = 1, n = points.length;
    while (++i < n) {
      p0 = p1;
      p1 = p2;
      p2 = points[i];
      tangents.push([ a * (p2[0] - p0[0]), a * (p2[1] - p0[1]) ]);
    }
    return tangents;
  }
  function d3_svg_lineBasis(points) {
    if (points.length < 3) return d3_svg_lineLinear(points);
    var i = 1, n = points.length, pi = points[0], x0 = pi[0], y0 = pi[1], px = [ x0, x0, x0, (pi = points[1])[0] ], py = [ y0, y0, y0, pi[1] ], path = [ x0, ",", y0, "L", d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, py) ];
    points.push(points[n - 1]);
    while (++i <= n) {
      pi = points[i];
      px.shift();
      px.push(pi[0]);
      py.shift();
      py.push(pi[1]);
      d3_svg_lineBasisBezier(path, px, py);
    }
    points.pop();
    path.push("L", pi);
    return path.join("");
  }
  function d3_svg_lineBasisOpen(points) {
    if (points.length < 4) return d3_svg_lineLinear(points);
    var path = [], i = -1, n = points.length, pi, px = [ 0 ], py = [ 0 ];
    while (++i < 3) {
      pi = points[i];
      px.push(pi[0]);
      py.push(pi[1]);
    }
    path.push(d3_svg_lineDot4(d3_svg_lineBasisBezier3, px) + "," + d3_svg_lineDot4(d3_svg_lineBasisBezier3, py));
    --i;
    while (++i < n) {
      pi = points[i];
      px.shift();
      px.push(pi[0]);
      py.shift();
      py.push(pi[1]);
      d3_svg_lineBasisBezier(path, px, py);
    }
    return path.join("");
  }
  function d3_svg_lineBasisClosed(points) {
    var path, i = -1, n = points.length, m = n + 4, pi, px = [], py = [];
    while (++i < 4) {
      pi = points[i % n];
      px.push(pi[0]);
      py.push(pi[1]);
    }
    path = [ d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, py) ];
    --i;
    while (++i < m) {
      pi = points[i % n];
      px.shift();
      px.push(pi[0]);
      py.shift();
      py.push(pi[1]);
      d3_svg_lineBasisBezier(path, px, py);
    }
    return path.join("");
  }
  function d3_svg_lineBundle(points, tension) {
    var n = points.length - 1;
    if (n) {
      var x0 = points[0][0], y0 = points[0][1], dx = points[n][0] - x0, dy = points[n][1] - y0, i = -1, p, t;
      while (++i <= n) {
        p = points[i];
        t = i / n;
        p[0] = tension * p[0] + (1 - tension) * (x0 + t * dx);
        p[1] = tension * p[1] + (1 - tension) * (y0 + t * dy);
      }
    }
    return d3_svg_lineBasis(points);
  }
  function d3_svg_lineDot4(a, b) {
    return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
  }
  var d3_svg_lineBasisBezier1 = [ 0, 2 / 3, 1 / 3, 0 ], d3_svg_lineBasisBezier2 = [ 0, 1 / 3, 2 / 3, 0 ], d3_svg_lineBasisBezier3 = [ 0, 1 / 6, 2 / 3, 1 / 6 ];
  function d3_svg_lineBasisBezier(path, x, y) {
    path.push("C", d3_svg_lineDot4(d3_svg_lineBasisBezier1, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier1, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, y));
  }
  function d3_svg_lineSlope(p0, p1) {
    return (p1[1] - p0[1]) / (p1[0] - p0[0]);
  }
  function d3_svg_lineFiniteDifferences(points) {
    var i = 0, j = points.length - 1, m = [], p0 = points[0], p1 = points[1], d = m[0] = d3_svg_lineSlope(p0, p1);
    while (++i < j) {
      m[i] = (d + (d = d3_svg_lineSlope(p0 = p1, p1 = points[i + 1]))) / 2;
    }
    m[i] = d;
    return m;
  }
  function d3_svg_lineMonotoneTangents(points) {
    var tangents = [], d, a, b, s, m = d3_svg_lineFiniteDifferences(points), i = -1, j = points.length - 1;
    while (++i < j) {
      d = d3_svg_lineSlope(points[i], points[i + 1]);
      if (abs(d) < ε) {
        m[i] = m[i + 1] = 0;
      } else {
        a = m[i] / d;
        b = m[i + 1] / d;
        s = a * a + b * b;
        if (s > 9) {
          s = d * 3 / Math.sqrt(s);
          m[i] = s * a;
          m[i + 1] = s * b;
        }
      }
    }
    i = -1;
    while (++i <= j) {
      s = (points[Math.min(j, i + 1)][0] - points[Math.max(0, i - 1)][0]) / (6 * (1 + m[i] * m[i]));
      tangents.push([ s || 0, m[i] * s || 0 ]);
    }
    return tangents;
  }
  function d3_svg_lineMonotone(points) {
    return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineMonotoneTangents(points));
  }
  d3.svg.line.radial = function() {
    var line = d3_svg_line(d3_svg_lineRadial);
    line.radius = line.x, delete line.x;
    line.angle = line.y, delete line.y;
    return line;
  };
  function d3_svg_lineRadial(points) {
    var point, i = -1, n = points.length, r, a;
    while (++i < n) {
      point = points[i];
      r = point[0];
      a = point[1] + d3_svg_arcOffset;
      point[0] = r * Math.cos(a);
      point[1] = r * Math.sin(a);
    }
    return points;
  }
  function d3_svg_area(projection) {
    var x0 = d3_geom_pointX, x1 = d3_geom_pointX, y0 = 0, y1 = d3_geom_pointY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, interpolateReverse = interpolate, L = "L", tension = .7;
    function area(data) {
      var segments = [], points0 = [], points1 = [], i = -1, n = data.length, d, fx0 = d3_functor(x0), fy0 = d3_functor(y0), fx1 = x0 === x1 ? function() {
        return x;
      } : d3_functor(x1), fy1 = y0 === y1 ? function() {
        return y;
      } : d3_functor(y1), x, y;
      function segment() {
        segments.push("M", interpolate(projection(points1), tension), L, interpolateReverse(projection(points0.reverse()), tension), "Z");
      }
      while (++i < n) {
        if (defined.call(this, d = data[i], i)) {
          points0.push([ x = +fx0.call(this, d, i), y = +fy0.call(this, d, i) ]);
          points1.push([ +fx1.call(this, d, i), +fy1.call(this, d, i) ]);
        } else if (points0.length) {
          segment();
          points0 = [];
          points1 = [];
        }
      }
      if (points0.length) segment();
      return segments.length ? segments.join("") : null;
    }
    area.x = function(_) {
      if (!arguments.length) return x1;
      x0 = x1 = _;
      return area;
    };
    area.x0 = function(_) {
      if (!arguments.length) return x0;
      x0 = _;
      return area;
    };
    area.x1 = function(_) {
      if (!arguments.length) return x1;
      x1 = _;
      return area;
    };
    area.y = function(_) {
      if (!arguments.length) return y1;
      y0 = y1 = _;
      return area;
    };
    area.y0 = function(_) {
      if (!arguments.length) return y0;
      y0 = _;
      return area;
    };
    area.y1 = function(_) {
      if (!arguments.length) return y1;
      y1 = _;
      return area;
    };
    area.defined = function(_) {
      if (!arguments.length) return defined;
      defined = _;
      return area;
    };
    area.interpolate = function(_) {
      if (!arguments.length) return interpolateKey;
      if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key;
      interpolateReverse = interpolate.reverse || interpolate;
      L = interpolate.closed ? "M" : "L";
      return area;
    };
    area.tension = function(_) {
      if (!arguments.length) return tension;
      tension = _;
      return area;
    };
    return area;
  }
  d3_svg_lineStepBefore.reverse = d3_svg_lineStepAfter;
  d3_svg_lineStepAfter.reverse = d3_svg_lineStepBefore;
  d3.svg.area = function() {
    return d3_svg_area(d3_identity);
  };
  d3.svg.area.radial = function() {
    var area = d3_svg_area(d3_svg_lineRadial);
    area.radius = area.x, delete area.x;
    area.innerRadius = area.x0, delete area.x0;
    area.outerRadius = area.x1, delete area.x1;
    area.angle = area.y, delete area.y;
    area.startAngle = area.y0, delete area.y0;
    area.endAngle = area.y1, delete area.y1;
    return area;
  };
  d3.svg.chord = function() {
    var source = d3_source, target = d3_target, radius = d3_svg_chordRadius, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle;
    function chord(d, i) {
      var s = subgroup(this, source, d, i), t = subgroup(this, target, d, i);
      return "M" + s.p0 + arc(s.r, s.p1, s.a1 - s.a0) + (equals(s, t) ? curve(s.r, s.p1, s.r, s.p0) : curve(s.r, s.p1, t.r, t.p0) + arc(t.r, t.p1, t.a1 - t.a0) + curve(t.r, t.p1, s.r, s.p0)) + "Z";
    }
    function subgroup(self, f, d, i) {
      var subgroup = f.call(self, d, i), r = radius.call(self, subgroup, i), a0 = startAngle.call(self, subgroup, i) + d3_svg_arcOffset, a1 = endAngle.call(self, subgroup, i) + d3_svg_arcOffset;
      return {
        r: r,
        a0: a0,
        a1: a1,
        p0: [ r * Math.cos(a0), r * Math.sin(a0) ],
        p1: [ r * Math.cos(a1), r * Math.sin(a1) ]
      };
    }
    function equals(a, b) {
      return a.a0 == b.a0 && a.a1 == b.a1;
    }
    function arc(r, p, a) {
      return "A" + r + "," + r + " 0 " + +(a > π) + ",1 " + p;
    }
    function curve(r0, p0, r1, p1) {
      return "Q 0,0 " + p1;
    }
    chord.radius = function(v) {
      if (!arguments.length) return radius;
      radius = d3_functor(v);
      return chord;
    };
    chord.source = function(v) {
      if (!arguments.length) return source;
      source = d3_functor(v);
      return chord;
    };
    chord.target = function(v) {
      if (!arguments.length) return target;
      target = d3_functor(v);
      return chord;
    };
    chord.startAngle = function(v) {
      if (!arguments.length) return startAngle;
      startAngle = d3_functor(v);
      return chord;
    };
    chord.endAngle = function(v) {
      if (!arguments.length) return endAngle;
      endAngle = d3_functor(v);
      return chord;
    };
    return chord;
  };
  function d3_svg_chordRadius(d) {
    return d.radius;
  }
  d3.svg.diagonal = function() {
    var source = d3_source, target = d3_target, projection = d3_svg_diagonalProjection;
    function diagonal(d, i) {
      var p0 = source.call(this, d, i), p3 = target.call(this, d, i), m = (p0.y + p3.y) / 2, p = [ p0, {
        x: p0.x,
        y: m
      }, {
        x: p3.x,
        y: m
      }, p3 ];
      p = p.map(projection);
      return "M" + p[0] + "C" + p[1] + " " + p[2] + " " + p[3];
    }
    diagonal.source = function(x) {
      if (!arguments.length) return source;
      source = d3_functor(x);
      return diagonal;
    };
    diagonal.target = function(x) {
      if (!arguments.length) return target;
      target = d3_functor(x);
      return diagonal;
    };
    diagonal.projection = function(x) {
      if (!arguments.length) return projection;
      projection = x;
      return diagonal;
    };
    return diagonal;
  };
  function d3_svg_diagonalProjection(d) {
    return [ d.x, d.y ];
  }
  d3.svg.diagonal.radial = function() {
    var diagonal = d3.svg.diagonal(), projection = d3_svg_diagonalProjection, projection_ = diagonal.projection;
    diagonal.projection = function(x) {
      return arguments.length ? projection_(d3_svg_diagonalRadialProjection(projection = x)) : projection;
    };
    return diagonal;
  };
  function d3_svg_diagonalRadialProjection(projection) {
    return function() {
      var d = projection.apply(this, arguments), r = d[0], a = d[1] + d3_svg_arcOffset;
      return [ r * Math.cos(a), r * Math.sin(a) ];
    };
  }
  d3.svg.symbol = function() {
    var type = d3_svg_symbolType, size = d3_svg_symbolSize;
    function symbol(d, i) {
      return (d3_svg_symbols.get(type.call(this, d, i)) || d3_svg_symbolCircle)(size.call(this, d, i));
    }
    symbol.type = function(x) {
      if (!arguments.length) return type;
      type = d3_functor(x);
      return symbol;
    };
    symbol.size = function(x) {
      if (!arguments.length) return size;
      size = d3_functor(x);
      return symbol;
    };
    return symbol;
  };
  function d3_svg_symbolSize() {
    return 64;
  }
  function d3_svg_symbolType() {
    return "circle";
  }
  function d3_svg_symbolCircle(size) {
    var r = Math.sqrt(size / π);
    return "M0," + r + "A" + r + "," + r + " 0 1,1 0," + -r + "A" + r + "," + r + " 0 1,1 0," + r + "Z";
  }
  var d3_svg_symbols = d3.map({
    circle: d3_svg_symbolCircle,
    cross: function(size) {
      var r = Math.sqrt(size / 5) / 2;
      return "M" + -3 * r + "," + -r + "H" + -r + "V" + -3 * r + "H" + r + "V" + -r + "H" + 3 * r + "V" + r + "H" + r + "V" + 3 * r + "H" + -r + "V" + r + "H" + -3 * r + "Z";
    },
    diamond: function(size) {
      var ry = Math.sqrt(size / (2 * d3_svg_symbolTan30)), rx = ry * d3_svg_symbolTan30;
      return "M0," + -ry + "L" + rx + ",0" + " 0," + ry + " " + -rx + ",0" + "Z";
    },
    square: function(size) {
      var r = Math.sqrt(size) / 2;
      return "M" + -r + "," + -r + "L" + r + "," + -r + " " + r + "," + r + " " + -r + "," + r + "Z";
    },
    "triangle-down": function(size) {
      var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2;
      return "M0," + ry + "L" + rx + "," + -ry + " " + -rx + "," + -ry + "Z";
    },
    "triangle-up": function(size) {
      var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2;
      return "M0," + -ry + "L" + rx + "," + ry + " " + -rx + "," + ry + "Z";
    }
  });
  d3.svg.symbolTypes = d3_svg_symbols.keys();
  var d3_svg_symbolSqrt3 = Math.sqrt(3), d3_svg_symbolTan30 = Math.tan(30 * d3_radians);
  function d3_transition(groups, id) {
    d3_subclass(groups, d3_transitionPrototype);
    groups.id = id;
    return groups;
  }
  var d3_transitionPrototype = [], d3_transitionId = 0, d3_transitionInheritId, d3_transitionInherit;
  d3_transitionPrototype.call = d3_selectionPrototype.call;
  d3_transitionPrototype.empty = d3_selectionPrototype.empty;
  d3_transitionPrototype.node = d3_selectionPrototype.node;
  d3_transitionPrototype.size = d3_selectionPrototype.size;
  d3.transition = function(selection) {
    return arguments.length ? d3_transitionInheritId ? selection.transition() : selection : d3_selectionRoot.transition();
  };
  d3.transition.prototype = d3_transitionPrototype;
  d3_transitionPrototype.select = function(selector) {
    var id = this.id, subgroups = [], subgroup, subnode, node;
    selector = d3_selection_selector(selector);
    for (var j = -1, m = this.length; ++j < m; ) {
      subgroups.push(subgroup = []);
      for (var group = this[j], i = -1, n = group.length; ++i < n; ) {
        if ((node = group[i]) && (subnode = selector.call(node, node.__data__, i, j))) {
          if ("__data__" in node) subnode.__data__ = node.__data__;
          d3_transitionNode(subnode, i, id, node.__transition__[id]);
          subgroup.push(subnode);
        } else {
          subgroup.push(null);
        }
      }
    }
    return d3_transition(subgroups, id);
  };
  d3_transitionPrototype.selectAll = function(selector) {
    var id = this.id, subgroups = [], subgroup, subnodes, node, subnode, transition;
    selector = d3_selection_selectorAll(selector);
    for (var j = -1, m = this.length; ++j < m; ) {
      for (var group = this[j], i = -1, n = group.length; ++i < n; ) {
        if (node = group[i]) {
          transition = node.__transition__[id];
          subnodes = selector.call(node, node.__data__, i, j);
          subgroups.push(subgroup = []);
          for (var k = -1, o = subnodes.length; ++k < o; ) {
            if (subnode = subnodes[k]) d3_transitionNode(subnode, k, id, transition);
            subgroup.push(subnode);
          }
        }
      }
    }
    return d3_transition(subgroups, id);
  };
  d3_transitionPrototype.filter = function(filter) {
    var subgroups = [], subgroup, group, node;
    if (typeof filter !== "function") filter = d3_selection_filter(filter);
    for (var j = 0, m = this.length; j < m; j++) {
      subgroups.push(subgroup = []);
      for (var group = this[j], i = 0, n = group.length; i < n; i++) {
        if ((node = group[i]) && filter.call(node, node.__data__, i, j)) {
          subgroup.push(node);
        }
      }
    }
    return d3_transition(subgroups, this.id);
  };
  d3_transitionPrototype.tween = function(name, tween) {
    var id = this.id;
    if (arguments.length < 2) return this.node().__transition__[id].tween.get(name);
    return d3_selection_each(this, tween == null ? function(node) {
      node.__transition__[id].tween.remove(name);
    } : function(node) {
      node.__transition__[id].tween.set(name, tween);
    });
  };
  function d3_transition_tween(groups, name, value, tween) {
    var id = groups.id;
    return d3_selection_each(groups, typeof value === "function" ? function(node, i, j) {
      node.__transition__[id].tween.set(name, tween(value.call(node, node.__data__, i, j)));
    } : (value = tween(value), function(node) {
      node.__transition__[id].tween.set(name, value);
    }));
  }
  d3_transitionPrototype.attr = function(nameNS, value) {
    if (arguments.length < 2) {
      for (value in nameNS) this.attr(value, nameNS[value]);
      return this;
    }
    var interpolate = nameNS == "transform" ? d3_interpolateTransform : d3_interpolate, name = d3.ns.qualify(nameNS);
    function attrNull() {
      this.removeAttribute(name);
    }
    function attrNullNS() {
      this.removeAttributeNS(name.space, name.local);
    }
    function attrTween(b) {
      return b == null ? attrNull : (b += "", function() {
        var a = this.getAttribute(name), i;
        return a !== b && (i = interpolate(a, b), function(t) {
          this.setAttribute(name, i(t));
        });
      });
    }
    function attrTweenNS(b) {
      return b == null ? attrNullNS : (b += "", function() {
        var a = this.getAttributeNS(name.space, name.local), i;
        return a !== b && (i = interpolate(a, b), function(t) {
          this.setAttributeNS(name.space, name.local, i(t));
        });
      });
    }
    return d3_transition_tween(this, "attr." + nameNS, value, name.local ? attrTweenNS : attrTween);
  };
  d3_transitionPrototype.attrTween = function(nameNS, tween) {
    var name = d3.ns.qualify(nameNS);
    function attrTween(d, i) {
      var f = tween.call(this, d, i, this.getAttribute(name));
      return f && function(t) {
        this.setAttribute(name, f(t));
      };
    }
    function attrTweenNS(d, i) {
      var f = tween.call(this, d, i, this.getAttributeNS(name.space, name.local));
      return f && function(t) {
        this.setAttributeNS(name.space, name.local, f(t));
      };
    }
    return this.tween("attr." + nameNS, name.local ? attrTweenNS : attrTween);
  };
  d3_transitionPrototype.style = function(name, value, priority) {
    var n = arguments.length;
    if (n < 3) {
      if (typeof name !== "string") {
        if (n < 2) value = "";
        for (priority in name) this.style(priority, name[priority], value);
        return this;
      }
      priority = "";
    }
    function styleNull() {
      this.style.removeProperty(name);
    }
    function styleString(b) {
      return b == null ? styleNull : (b += "", function() {
        var a = d3_window.getComputedStyle(this, null).getPropertyValue(name), i;
        return a !== b && (i = d3_interpolate(a, b), function(t) {
          this.style.setProperty(name, i(t), priority);
        });
      });
    }
    return d3_transition_tween(this, "style." + name, value, styleString);
  };
  d3_transitionPrototype.styleTween = function(name, tween, priority) {
    if (arguments.length < 3) priority = "";
    function styleTween(d, i) {
      var f = tween.call(this, d, i, d3_window.getComputedStyle(this, null).getPropertyValue(name));
      return f && function(t) {
        this.style.setProperty(name, f(t), priority);
      };
    }
    return this.tween("style." + name, styleTween);
  };
  d3_transitionPrototype.text = function(value) {
    return d3_transition_tween(this, "text", value, d3_transition_text);
  };
  function d3_transition_text(b) {
    if (b == null) b = "";
    return function() {
      this.textContent = b;
    };
  }
  d3_transitionPrototype.remove = function() {
    return this.each("end.transition", function() {
      var p;
      if (this.__transition__.count < 2 && (p = this.parentNode)) p.removeChild(this);
    });
  };
  d3_transitionPrototype.ease = function(value) {
    var id = this.id;
    if (arguments.length < 1) return this.node().__transition__[id].ease;
    if (typeof value !== "function") value = d3.ease.apply(d3, arguments);
    return d3_selection_each(this, function(node) {
      node.__transition__[id].ease = value;
    });
  };
  d3_transitionPrototype.delay = function(value) {
    var id = this.id;
    return d3_selection_each(this, typeof value === "function" ? function(node, i, j) {
      node.__transition__[id].delay = +value.call(node, node.__data__, i, j);
    } : (value = +value, function(node) {
      node.__transition__[id].delay = value;
    }));
  };
  d3_transitionPrototype.duration = function(value) {
    var id = this.id;
    return d3_selection_each(this, typeof value === "function" ? function(node, i, j) {
      node.__transition__[id].duration = Math.max(1, value.call(node, node.__data__, i, j));
    } : (value = Math.max(1, value), function(node) {
      node.__transition__[id].duration = value;
    }));
  };
  d3_transitionPrototype.each = function(type, listener) {
    var id = this.id;
    if (arguments.length < 2) {
      var inherit = d3_transitionInherit, inheritId = d3_transitionInheritId;
      d3_transitionInheritId = id;
      d3_selection_each(this, function(node, i, j) {
        d3_transitionInherit = node.__transition__[id];
        type.call(node, node.__data__, i, j);
      });
      d3_transitionInherit = inherit;
      d3_transitionInheritId = inheritId;
    } else {
      d3_selection_each(this, function(node) {
        var transition = node.__transition__[id];
        (transition.event || (transition.event = d3.dispatch("start", "end"))).on(type, listener);
      });
    }
    return this;
  };
  d3_transitionPrototype.transition = function() {
    var id0 = this.id, id1 = ++d3_transitionId, subgroups = [], subgroup, group, node, transition;
    for (var j = 0, m = this.length; j < m; j++) {
      subgroups.push(subgroup = []);
      for (var group = this[j], i = 0, n = group.length; i < n; i++) {
        if (node = group[i]) {
          transition = Object.create(node.__transition__[id0]);
          transition.delay += transition.duration;
          d3_transitionNode(node, i, id1, transition);
        }
        subgroup.push(node);
      }
    }
    return d3_transition(subgroups, id1);
  };
  function d3_transitionNode(node, i, id, inherit) {
    var lock = node.__transition__ || (node.__transition__ = {
      active: 0,
      count: 0
    }), transition = lock[id];
    if (!transition) {
      var time = inherit.time;
      transition = lock[id] = {
        tween: new d3_Map(),
        time: time,
        ease: inherit.ease,
        delay: inherit.delay,
        duration: inherit.duration
      };
      ++lock.count;
      d3.timer(function(elapsed) {
        var d = node.__data__, ease = transition.ease, delay = transition.delay, duration = transition.duration, timer = d3_timer_active, tweened = [];
        timer.t = delay + time;
        if (delay <= elapsed) return start(elapsed - delay);
        timer.c = start;
        function start(elapsed) {
          if (lock.active > id) return stop();
          lock.active = id;
          transition.event && transition.event.start.call(node, d, i);
          transition.tween.forEach(function(key, value) {
            if (value = value.call(node, d, i)) {
              tweened.push(value);
            }
          });
          d3.timer(function() {
            timer.c = tick(elapsed || 1) ? d3_true : tick;
            return 1;
          }, 0, time);
        }
        function tick(elapsed) {
          if (lock.active !== id) return stop();
          var t = elapsed / duration, e = ease(t), n = tweened.length;
          while (n > 0) {
            tweened[--n].call(node, e);
          }
          if (t >= 1) {
            transition.event && transition.event.end.call(node, d, i);
            return stop();
          }
        }
        function stop() {
          if (--lock.count) delete lock[id]; else delete node.__transition__;
          return 1;
        }
      }, 0, time);
    }
  }
  d3.svg.axis = function() {
    var scale = d3.scale.linear(), orient = d3_svg_axisDefaultOrient, innerTickSize = 6, outerTickSize = 6, tickPadding = 3, tickArguments_ = [ 10 ], tickValues = null, tickFormat_;
    function axis(g) {
      g.each(function() {
        var g = d3.select(this);
        var scale0 = this.__chart__ || scale, scale1 = this.__chart__ = scale.copy();
        var ticks = tickValues == null ? scale1.ticks ? scale1.ticks.apply(scale1, tickArguments_) : scale1.domain() : tickValues, tickFormat = tickFormat_ == null ? scale1.tickFormat ? scale1.tickFormat.apply(scale1, tickArguments_) : d3_identity : tickFormat_, tick = g.selectAll(".tick").data(ticks, scale1), tickEnter = tick.enter().insert("g", ".domain").attr("class", "tick").style("opacity", ε), tickExit = d3.transition(tick.exit()).style("opacity", ε).remove(), tickUpdate = d3.transition(tick).style("opacity", 1), tickTransform;
        var range = d3_scaleRange(scale1), path = g.selectAll(".domain").data([ 0 ]), pathUpdate = (path.enter().append("path").attr("class", "domain"), 
        d3.transition(path));
        tickEnter.append("line");
        tickEnter.append("text");
        var lineEnter = tickEnter.select("line"), lineUpdate = tickUpdate.select("line"), text = tick.select("text").text(tickFormat), textEnter = tickEnter.select("text"), textUpdate = tickUpdate.select("text");
        switch (orient) {
         case "bottom":
          {
            tickTransform = d3_svg_axisX;
            lineEnter.attr("y2", innerTickSize);
            textEnter.attr("y", Math.max(innerTickSize, 0) + tickPadding);
            lineUpdate.attr("x2", 0).attr("y2", innerTickSize);
            textUpdate.attr("x", 0).attr("y", Math.max(innerTickSize, 0) + tickPadding);
            text.attr("dy", ".71em").style("text-anchor", "middle");
            pathUpdate.attr("d", "M" + range[0] + "," + outerTickSize + "V0H" + range[1] + "V" + outerTickSize);
            break;
          }

         case "top":
          {
            tickTransform = d3_svg_axisX;
            lineEnter.attr("y2", -innerTickSize);
            textEnter.attr("y", -(Math.max(innerTickSize, 0) + tickPadding));
            lineUpdate.attr("x2", 0).attr("y2", -innerTickSize);
            textUpdate.attr("x", 0).attr("y", -(Math.max(innerTickSize, 0) + tickPadding));
            text.attr("dy", "0em").style("text-anchor", "middle");
            pathUpdate.attr("d", "M" + range[0] + "," + -outerTickSize + "V0H" + range[1] + "V" + -outerTickSize);
            break;
          }

         case "left":
          {
            tickTransform = d3_svg_axisY;
            lineEnter.attr("x2", -innerTickSize);
            textEnter.attr("x", -(Math.max(innerTickSize, 0) + tickPadding));
            lineUpdate.attr("x2", -innerTickSize).attr("y2", 0);
            textUpdate.attr("x", -(Math.max(innerTickSize, 0) + tickPadding)).attr("y", 0);
            text.attr("dy", ".32em").style("text-anchor", "end");
            pathUpdate.attr("d", "M" + -outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + -outerTickSize);
            break;
          }

         case "right":
          {
            tickTransform = d3_svg_axisY;
            lineEnter.attr("x2", innerTickSize);
            textEnter.attr("x", Math.max(innerTickSize, 0) + tickPadding);
            lineUpdate.attr("x2", innerTickSize).attr("y2", 0);
            textUpdate.attr("x", Math.max(innerTickSize, 0) + tickPadding).attr("y", 0);
            text.attr("dy", ".32em").style("text-anchor", "start");
            pathUpdate.attr("d", "M" + outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + outerTickSize);
            break;
          }
        }
        if (scale1.rangeBand) {
          var x = scale1, dx = x.rangeBand() / 2;
          scale0 = scale1 = function(d) {
            return x(d) + dx;
          };
        } else if (scale0.rangeBand) {
          scale0 = scale1;
        } else {
          tickExit.call(tickTransform, scale1);
        }
        tickEnter.call(tickTransform, scale0);
        tickUpdate.call(tickTransform, scale1);
      });
    }
    axis.scale = function(x) {
      if (!arguments.length) return scale;
      scale = x;
      return axis;
    };
    axis.orient = function(x) {
      if (!arguments.length) return orient;
      orient = x in d3_svg_axisOrients ? x + "" : d3_svg_axisDefaultOrient;
      return axis;
    };
    axis.ticks = function() {
      if (!arguments.length) return tickArguments_;
      tickArguments_ = arguments;
      return axis;
    };
    axis.tickValues = function(x) {
      if (!arguments.length) return tickValues;
      tickValues = x;
      return axis;
    };
    axis.tickFormat = function(x) {
      if (!arguments.length) return tickFormat_;
      tickFormat_ = x;
      return axis;
    };
    axis.tickSize = function(x) {
      var n = arguments.length;
      if (!n) return innerTickSize;
      innerTickSize = +x;
      outerTickSize = +arguments[n - 1];
      return axis;
    };
    axis.innerTickSize = function(x) {
      if (!arguments.length) return innerTickSize;
      innerTickSize = +x;
      return axis;
    };
    axis.outerTickSize = function(x) {
      if (!arguments.length) return outerTickSize;
      outerTickSize = +x;
      return axis;
    };
    axis.tickPadding = function(x) {
      if (!arguments.length) return tickPadding;
      tickPadding = +x;
      return axis;
    };
    axis.tickSubdivide = function() {
      return arguments.length && axis;
    };
    return axis;
  };
  var d3_svg_axisDefaultOrient = "bottom", d3_svg_axisOrients = {
    top: 1,
    right: 1,
    bottom: 1,
    left: 1
  };
  function d3_svg_axisX(selection, x) {
    selection.attr("transform", function(d) {
      return "translate(" + x(d) + ",0)";
    });
  }
  function d3_svg_axisY(selection, y) {
    selection.attr("transform", function(d) {
      return "translate(0," + y(d) + ")";
    });
  }
  d3.svg.brush = function() {
    var event = d3_eventDispatch(brush, "brushstart", "brush", "brushend"), x = null, y = null, xExtent = [ 0, 0 ], yExtent = [ 0, 0 ], xExtentDomain, yExtentDomain, xClamp = true, yClamp = true, resizes = d3_svg_brushResizes[0];
    function brush(g) {
      g.each(function() {
        var g = d3.select(this).style("pointer-events", "all").style("-webkit-tap-highlight-color", "rgba(0,0,0,0)").on("mousedown.brush", brushstart).on("touchstart.brush", brushstart);
        var background = g.selectAll(".background").data([ 0 ]);
        background.enter().append("rect").attr("class", "background").style("visibility", "hidden").style("cursor", "crosshair");
        g.selectAll(".extent").data([ 0 ]).enter().append("rect").attr("class", "extent").style("cursor", "move");
        var resize = g.selectAll(".resize").data(resizes, d3_identity);
        resize.exit().remove();
        resize.enter().append("g").attr("class", function(d) {
          return "resize " + d;
        }).style("cursor", function(d) {
          return d3_svg_brushCursor[d];
        }).append("rect").attr("x", function(d) {
          return /[ew]$/.test(d) ? -3 : null;
        }).attr("y", function(d) {
          return /^[ns]/.test(d) ? -3 : null;
        }).attr("width", 6).attr("height", 6).style("visibility", "hidden");
        resize.style("display", brush.empty() ? "none" : null);
        var gUpdate = d3.transition(g), backgroundUpdate = d3.transition(background), range;
        if (x) {
          range = d3_scaleRange(x);
          backgroundUpdate.attr("x", range[0]).attr("width", range[1] - range[0]);
          redrawX(gUpdate);
        }
        if (y) {
          range = d3_scaleRange(y);
          backgroundUpdate.attr("y", range[0]).attr("height", range[1] - range[0]);
          redrawY(gUpdate);
        }
        redraw(gUpdate);
      });
    }
    brush.event = function(g) {
      g.each(function() {
        var event_ = event.of(this, arguments), extent1 = {
          x: xExtent,
          y: yExtent,
          i: xExtentDomain,
          j: yExtentDomain
        }, extent0 = this.__chart__ || extent1;
        this.__chart__ = extent1;
        if (d3_transitionInheritId) {
          d3.select(this).transition().each("start.brush", function() {
            xExtentDomain = extent0.i;
            yExtentDomain = extent0.j;
            xExtent = extent0.x;
            yExtent = extent0.y;
            event_({
              type: "brushstart"
            });
          }).tween("brush:brush", function() {
            var xi = d3_interpolateArray(xExtent, extent1.x), yi = d3_interpolateArray(yExtent, extent1.y);
            xExtentDomain = yExtentDomain = null;
            return function(t) {
              xExtent = extent1.x = xi(t);
              yExtent = extent1.y = yi(t);
              event_({
                type: "brush",
                mode: "resize"
              });
            };
          }).each("end.brush", function() {
            xExtentDomain = extent1.i;
            yExtentDomain = extent1.j;
            event_({
              type: "brush",
              mode: "resize"
            });
            event_({
              type: "brushend"
            });
          });
        } else {
          event_({
            type: "brushstart"
          });
          event_({
            type: "brush",
            mode: "resize"
          });
          event_({
            type: "brushend"
          });
        }
      });
    };
    function redraw(g) {
      g.selectAll(".resize").attr("transform", function(d) {
        return "translate(" + xExtent[+/e$/.test(d)] + "," + yExtent[+/^s/.test(d)] + ")";
      });
    }
    function redrawX(g) {
      g.select(".extent").attr("x", xExtent[0]);
      g.selectAll(".extent,.n>rect,.s>rect").attr("width", xExtent[1] - xExtent[0]);
    }
    function redrawY(g) {
      g.select(".extent").attr("y", yExtent[0]);
      g.selectAll(".extent,.e>rect,.w>rect").attr("height", yExtent[1] - yExtent[0]);
    }
    function brushstart() {
      var target = this, eventTarget = d3.select(d3.event.target), event_ = event.of(target, arguments), g = d3.select(target), resizing = eventTarget.datum(), resizingX = !/^(n|s)$/.test(resizing) && x, resizingY = !/^(e|w)$/.test(resizing) && y, dragging = eventTarget.classed("extent"), dragRestore = d3_event_dragSuppress(), center, origin = d3.mouse(target), offset;
      var w = d3.select(d3_window).on("keydown.brush", keydown).on("keyup.brush", keyup);
      if (d3.event.changedTouches) {
        w.on("touchmove.brush", brushmove).on("touchend.brush", brushend);
      } else {
        w.on("mousemove.brush", brushmove).on("mouseup.brush", brushend);
      }
      g.interrupt().selectAll("*").interrupt();
      if (dragging) {
        origin[0] = xExtent[0] - origin[0];
        origin[1] = yExtent[0] - origin[1];
      } else if (resizing) {
        var ex = +/w$/.test(resizing), ey = +/^n/.test(resizing);
        offset = [ xExtent[1 - ex] - origin[0], yExtent[1 - ey] - origin[1] ];
        origin[0] = xExtent[ex];
        origin[1] = yExtent[ey];
      } else if (d3.event.altKey) center = origin.slice();
      g.style("pointer-events", "none").selectAll(".resize").style("display", null);
      d3.select("body").style("cursor", eventTarget.style("cursor"));
      event_({
        type: "brushstart"
      });
      brushmove();
      function keydown() {
        if (d3.event.keyCode == 32) {
          if (!dragging) {
            center = null;
            origin[0] -= xExtent[1];
            origin[1] -= yExtent[1];
            dragging = 2;
          }
          d3_eventPreventDefault();
        }
      }
      function keyup() {
        if (d3.event.keyCode == 32 && dragging == 2) {
          origin[0] += xExtent[1];
          origin[1] += yExtent[1];
          dragging = 0;
          d3_eventPreventDefault();
        }
      }
      function brushmove() {
        var point = d3.mouse(target), moved = false;
        if (offset) {
          point[0] += offset[0];
          point[1] += offset[1];
        }
        if (!dragging) {
          if (d3.event.altKey) {
            if (!center) center = [ (xExtent[0] + xExtent[1]) / 2, (yExtent[0] + yExtent[1]) / 2 ];
            origin[0] = xExtent[+(point[0] < center[0])];
            origin[1] = yExtent[+(point[1] < center[1])];
          } else center = null;
        }
        if (resizingX && move1(point, x, 0)) {
          redrawX(g);
          moved = true;
        }
        if (resizingY && move1(point, y, 1)) {
          redrawY(g);
          moved = true;
        }
        if (moved) {
          redraw(g);
          event_({
            type: "brush",
            mode: dragging ? "move" : "resize"
          });
        }
      }
      function move1(point, scale, i) {
        var range = d3_scaleRange(scale), r0 = range[0], r1 = range[1], position = origin[i], extent = i ? yExtent : xExtent, size = extent[1] - extent[0], min, max;
        if (dragging) {
          r0 -= position;
          r1 -= size + position;
        }
        min = (i ? yClamp : xClamp) ? Math.max(r0, Math.min(r1, point[i])) : point[i];
        if (dragging) {
          max = (min += position) + size;
        } else {
          if (center) position = Math.max(r0, Math.min(r1, 2 * center[i] - min));
          if (position < min) {
            max = min;
            min = position;
          } else {
            max = position;
          }
        }
        if (extent[0] != min || extent[1] != max) {
          if (i) yExtentDomain = null; else xExtentDomain = null;
          extent[0] = min;
          extent[1] = max;
          return true;
        }
      }
      function brushend() {
        brushmove();
        g.style("pointer-events", "all").selectAll(".resize").style("display", brush.empty() ? "none" : null);
        d3.select("body").style("cursor", null);
        w.on("mousemove.brush", null).on("mouseup.brush", null).on("touchmove.brush", null).on("touchend.brush", null).on("keydown.brush", null).on("keyup.brush", null);
        dragRestore();
        event_({
          type: "brushend"
        });
      }
    }
    brush.x = function(z) {
      if (!arguments.length) return x;
      x = z;
      resizes = d3_svg_brushResizes[!x << 1 | !y];
      return brush;
    };
    brush.y = function(z) {
      if (!arguments.length) return y;
      y = z;
      resizes = d3_svg_brushResizes[!x << 1 | !y];
      return brush;
    };
    brush.clamp = function(z) {
      if (!arguments.length) return x && y ? [ xClamp, yClamp ] : x ? xClamp : y ? yClamp : null;
      if (x && y) xClamp = !!z[0], yClamp = !!z[1]; else if (x) xClamp = !!z; else if (y) yClamp = !!z;
      return brush;
    };
    brush.extent = function(z) {
      var x0, x1, y0, y1, t;
      if (!arguments.length) {
        if (x) {
          if (xExtentDomain) {
            x0 = xExtentDomain[0], x1 = xExtentDomain[1];
          } else {
            x0 = xExtent[0], x1 = xExtent[1];
            if (x.invert) x0 = x.invert(x0), x1 = x.invert(x1);
            if (x1 < x0) t = x0, x0 = x1, x1 = t;
          }
        }
        if (y) {
          if (yExtentDomain) {
            y0 = yExtentDomain[0], y1 = yExtentDomain[1];
          } else {
            y0 = yExtent[0], y1 = yExtent[1];
            if (y.invert) y0 = y.invert(y0), y1 = y.invert(y1);
            if (y1 < y0) t = y0, y0 = y1, y1 = t;
          }
        }
        return x && y ? [ [ x0, y0 ], [ x1, y1 ] ] : x ? [ x0, x1 ] : y && [ y0, y1 ];
      }
      if (x) {
        x0 = z[0], x1 = z[1];
        if (y) x0 = x0[0], x1 = x1[0];
        xExtentDomain = [ x0, x1 ];
        if (x.invert) x0 = x(x0), x1 = x(x1);
        if (x1 < x0) t = x0, x0 = x1, x1 = t;
        if (x0 != xExtent[0] || x1 != xExtent[1]) xExtent = [ x0, x1 ];
      }
      if (y) {
        y0 = z[0], y1 = z[1];
        if (x) y0 = y0[1], y1 = y1[1];
        yExtentDomain = [ y0, y1 ];
        if (y.invert) y0 = y(y0), y1 = y(y1);
        if (y1 < y0) t = y0, y0 = y1, y1 = t;
        if (y0 != yExtent[0] || y1 != yExtent[1]) yExtent = [ y0, y1 ];
      }
      return brush;
    };
    brush.clear = function() {
      if (!brush.empty()) {
        xExtent = [ 0, 0 ], yExtent = [ 0, 0 ];
        xExtentDomain = yExtentDomain = null;
      }
      return brush;
    };
    brush.empty = function() {
      return !!x && xExtent[0] == xExtent[1] || !!y && yExtent[0] == yExtent[1];
    };
    return d3.rebind(brush, event, "on");
  };
  var d3_svg_brushCursor = {
    n: "ns-resize",
    e: "ew-resize",
    s: "ns-resize",
    w: "ew-resize",
    nw: "nwse-resize",
    ne: "nesw-resize",
    se: "nwse-resize",
    sw: "nesw-resize"
  };
  var d3_svg_brushResizes = [ [ "n", "e", "s", "w", "nw", "ne", "se", "sw" ], [ "e", "w" ], [ "n", "s" ], [] ];
  var d3_time_format = d3_time.format = d3_locale_enUS.timeFormat;
  var d3_time_formatUtc = d3_time_format.utc;
  var d3_time_formatIso = d3_time_formatUtc("%Y-%m-%dT%H:%M:%S.%LZ");
  d3_time_format.iso = Date.prototype.toISOString && +new Date("2000-01-01T00:00:00.000Z") ? d3_time_formatIsoNative : d3_time_formatIso;
  function d3_time_formatIsoNative(date) {
    return date.toISOString();
  }
  d3_time_formatIsoNative.parse = function(string) {
    var date = new Date(string);
    return isNaN(date) ? null : date;
  };
  d3_time_formatIsoNative.toString = d3_time_formatIso.toString;
  d3_time.second = d3_time_interval(function(date) {
    return new d3_date(Math.floor(date / 1e3) * 1e3);
  }, function(date, offset) {
    date.setTime(date.getTime() + Math.floor(offset) * 1e3);
  }, function(date) {
    return date.getSeconds();
  });
  d3_time.seconds = d3_time.second.range;
  d3_time.seconds.utc = d3_time.second.utc.range;
  d3_time.minute = d3_time_interval(function(date) {
    return new d3_date(Math.floor(date / 6e4) * 6e4);
  }, function(date, offset) {
    date.setTime(date.getTime() + Math.floor(offset) * 6e4);
  }, function(date) {
    return date.getMinutes();
  });
  d3_time.minutes = d3_time.minute.range;
  d3_time.minutes.utc = d3_time.minute.utc.range;
  d3_time.hour = d3_time_interval(function(date) {
    var timezone = date.getTimezoneOffset() / 60;
    return new d3_date((Math.floor(date / 36e5 - timezone) + timezone) * 36e5);
  }, function(date, offset) {
    date.setTime(date.getTime() + Math.floor(offset) * 36e5);
  }, function(date) {
    return date.getHours();
  });
  d3_time.hours = d3_time.hour.range;
  d3_time.hours.utc = d3_time.hour.utc.range;
  d3_time.month = d3_time_interval(function(date) {
    date = d3_time.day(date);
    date.setDate(1);
    return date;
  }, function(date, offset) {
    date.setMonth(date.getMonth() + offset);
  }, function(date) {
    return date.getMonth();
  });
  d3_time.months = d3_time.month.range;
  d3_time.months.utc = d3_time.month.utc.range;
  function d3_time_scale(linear, methods, format) {
    function scale(x) {
      return linear(x);
    }
    scale.invert = function(x) {
      return d3_time_scaleDate(linear.invert(x));
    };
    scale.domain = function(x) {
      if (!arguments.length) return linear.domain().map(d3_time_scaleDate);
      linear.domain(x);
      return scale;
    };
    function tickMethod(extent, count) {
      var span = extent[1] - extent[0], target = span / count, i = d3.bisect(d3_time_scaleSteps, target);
      return i == d3_time_scaleSteps.length ? [ methods.year, d3_scale_linearTickRange(extent.map(function(d) {
        return d / 31536e6;
      }), count)[2] ] : !i ? [ d3_time_scaleMilliseconds, d3_scale_linearTickRange(extent, count)[2] ] : methods[target / d3_time_scaleSteps[i - 1] < d3_time_scaleSteps[i] / target ? i - 1 : i];
    }
    scale.nice = function(interval, skip) {
      var domain = scale.domain(), extent = d3_scaleExtent(domain), method = interval == null ? tickMethod(extent, 10) : typeof interval === "number" && tickMethod(extent, interval);
      if (method) interval = method[0], skip = method[1];
      function skipped(date) {
        return !isNaN(date) && !interval.range(date, d3_time_scaleDate(+date + 1), skip).length;
      }
      return scale.domain(d3_scale_nice(domain, skip > 1 ? {
        floor: function(date) {
          while (skipped(date = interval.floor(date))) date = d3_time_scaleDate(date - 1);
          return date;
        },
        ceil: function(date) {
          while (skipped(date = interval.ceil(date))) date = d3_time_scaleDate(+date + 1);
          return date;
        }
      } : interval));
    };
    scale.ticks = function(interval, skip) {
      var extent = d3_scaleExtent(scale.domain()), method = interval == null ? tickMethod(extent, 10) : typeof interval === "number" ? tickMethod(extent, interval) : !interval.range && [ {
        range: interval
      }, skip ];
      if (method) interval = method[0], skip = method[1];
      return interval.range(extent[0], d3_time_scaleDate(+extent[1] + 1), skip < 1 ? 1 : skip);
    };
    scale.tickFormat = function() {
      return format;
    };
    scale.copy = function() {
      return d3_time_scale(linear.copy(), methods, format);
    };
    return d3_scale_linearRebind(scale, linear);
  }
  function d3_time_scaleDate(t) {
    return new Date(t);
  }
  var d3_time_scaleSteps = [ 1e3, 5e3, 15e3, 3e4, 6e4, 3e5, 9e5, 18e5, 36e5, 108e5, 216e5, 432e5, 864e5, 1728e5, 6048e5, 2592e6, 7776e6, 31536e6 ];
  var d3_time_scaleLocalMethods = [ [ d3_time.second, 1 ], [ d3_time.second, 5 ], [ d3_time.second, 15 ], [ d3_time.second, 30 ], [ d3_time.minute, 1 ], [ d3_time.minute, 5 ], [ d3_time.minute, 15 ], [ d3_time.minute, 30 ], [ d3_time.hour, 1 ], [ d3_time.hour, 3 ], [ d3_time.hour, 6 ], [ d3_time.hour, 12 ], [ d3_time.day, 1 ], [ d3_time.day, 2 ], [ d3_time.week, 1 ], [ d3_time.month, 1 ], [ d3_time.month, 3 ], [ d3_time.year, 1 ] ];
  var d3_time_scaleLocalFormat = d3_time_format.multi([ [ ".%L", function(d) {
    return d.getMilliseconds();
  } ], [ ":%S", function(d) {
    return d.getSeconds();
  } ], [ "%I:%M", function(d) {
    return d.getMinutes();
  } ], [ "%I %p", function(d) {
    return d.getHours();
  } ], [ "%a %d", function(d) {
    return d.getDay() && d.getDate() != 1;
  } ], [ "%b %d", function(d) {
    return d.getDate() != 1;
  } ], [ "%B", function(d) {
    return d.getMonth();
  } ], [ "%Y", d3_true ] ]);
  var d3_time_scaleMilliseconds = {
    range: function(start, stop, step) {
      return d3.range(+start, +stop, step).map(d3_time_scaleDate);
    },
    floor: d3_identity,
    ceil: d3_identity
  };
  d3_time_scaleLocalMethods.year = d3_time.year;
  d3_time.scale = function() {
    return d3_time_scale(d3.scale.linear(), d3_time_scaleLocalMethods, d3_time_scaleLocalFormat);
  };
  var d3_time_scaleUtcMethods = d3_time_scaleLocalMethods.map(function(m) {
    return [ m[0].utc, m[1] ];
  });
  var d3_time_scaleUtcFormat = d3_time_formatUtc.multi([ [ ".%L", function(d) {
    return d.getUTCMilliseconds();
  } ], [ ":%S", function(d) {
    return d.getUTCSeconds();
  } ], [ "%I:%M", function(d) {
    return d.getUTCMinutes();
  } ], [ "%I %p", function(d) {
    return d.getUTCHours();
  } ], [ "%a %d", function(d) {
    return d.getUTCDay() && d.getUTCDate() != 1;
  } ], [ "%b %d", function(d) {
    return d.getUTCDate() != 1;
  } ], [ "%B", function(d) {
    return d.getUTCMonth();
  } ], [ "%Y", d3_true ] ]);
  d3_time_scaleUtcMethods.year = d3_time.year.utc;
  d3_time.scale.utc = function() {
    return d3_time_scale(d3.scale.linear(), d3_time_scaleUtcMethods, d3_time_scaleUtcFormat);
  };
  d3.text = d3_xhrType(function(request) {
    return request.responseText;
  });
  d3.json = function(url, callback) {
    return d3_xhr(url, "application/json", d3_json, callback);
  };
  function d3_json(request) {
    return JSON.parse(request.responseText);
  }
  d3.html = function(url, callback) {
    return d3_xhr(url, "text/html", d3_html, callback);
  };
  function d3_html(request) {
    var range = d3_document.createRange();
    range.selectNode(d3_document.body);
    return range.createContextualFragment(request.responseText);
  }
  d3.xml = d3_xhrType(function(request) {
    return request.responseXML;
  });
  if (typeof define === "function" && define.amd) {
    define(d3);
  } else if (typeof module === "object" && module.exports) {
    module.exports = d3;
  } else {
    this.d3 = d3;
  }
}();PK
!<�۩����1chrome/devtools/content/shared/vendor/dagre-d3.js;(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])
;PK
!<b,RR8chrome/devtools/content/shared/widgets/VariablesView.xul<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<?xml-stylesheet href="chrome://global/skin/global.css"?>
<?xml-stylesheet href="chrome://devtools/content/shared/widgets/widgets.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/widgets.css" type="text/css"?>
<!DOCTYPE window [
  <!ENTITY % viewDTD SYSTEM "chrome://devtools/locale/VariablesView.dtd">
  %viewDTD;
]>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        title="&PropertiesViewWindowTitle;">

  <script type="application/javascript"
          src="chrome://devtools/content/shared/theme-switching.js"/>
  <vbox id="variables" flex="1"/>
</window>
PK
!<�Q!!7chrome/devtools/content/shared/widgets/color-widget.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#eyedropper-button {
  margin-inline-start: 5px;
  display: block;
}

#eyedropper-button::before {
  background-image: url(chrome://devtools/skin/images/command-eyedropper.svg);
}

/* Mix-in classes */

.colorwidget-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;
}

.colorwidget-slider-control {
  cursor: pointer;
  box-shadow: 0 0 2px rgba(0,0,0,.6);
  background: #fff;
  border-radius: 10px;
  opacity: .8;
}

.colorwidget-box {
  border: 1px solid rgba(0,0,0,0.2);
  border-radius: 2px;
  background-clip: content-box;
}

.colorwidget-colorswatch {
  background-color: transparent;
  color: transparent;
  border: 1px solid transparent;
}

/* Elements */

#colorwidget-tooltip {
  padding: 4px;
}

.colorwidget-container {
  position: relative;
  display: none;
  top: 0;
  left: 0;
  border-radius: 0;
  width: 200px;
  padding: 5px;
}

.colorwidget-show {
  display: inline-block;
}

/* Keep aspect ratio:
http://www.briangrinstead.com/blog/keep-aspect-ratio-with-html-and-css */
.colorwidget-top {
  position: relative;
  width: 100%;
  display: inline-block;
}

.colorwidget-top-inner {
  position: absolute;
  top:0;
  left:0;
  bottom:0;
  right:0;
}

.colorwidget-contrast {
  color: var(--theme-content-color1);
  padding-top: 4px;
}

.colorwidget-colorswatch, .colorwidget-contrast-ratio, .colorwidget-contrast-grade, .colorwidget-contrast-help {
  display: inline-block;
}

.colorwidget-colorswatch {
  width: 28%;
}

.colorwidget-contrast-ratio {
  font-family: Courier New, Courier, monospace;
  padding-left: 8px;
  width: 26%;
}

.colorwidget-contrast-grade {
  font-family: Courier New, Courier, monospace;
  width: 18%;
}

.colorwidget-contrast-help {
  margin-inline-start: 5px;
}

.colorwidget-contrast-help::before {
  background-image: url(chrome://devtools/skin/images/help.svg);
}

.colorwidget-colorswatch {
  text-align: center;
  color: transparent;
}

.colorwidget-color {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  right: 20%;
}

.colorwidget-hue {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 83%;
}

.colorwidget-fill {
  /* Same as colorwidget-color width */
  margin-top: 85%;
}

.colorwidget-sat, .colorwidget-val {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
}

.colorwidget-dragger, .colorwidget-slider {
  -moz-user-select: none;
}

.colorwidget-alpha {
  position: relative;
  height: 8px;
  margin-top: 3px;
}

.colorwidget-alpha-inner {
  height: 100%;
}

.colorwidget-alpha-handle {
  position: absolute;
  top: -3px;
  bottom: -3px;
  width: 5px;
  left: 50%;
}

.colorwidget-sat {
  background-image: linear-gradient(to right, #FFF, rgba(204, 154, 129, 0));
}

.colorwidget-val {
  background-image: linear-gradient(to top, #000000, rgba(204, 154, 129, 0));
}

.colorwidget-hue {
  background: linear-gradient(to bottom, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);
}

.colorwidget-dragger {
  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,.6);
}

.colorwidget-slider {
  position: absolute;
  top: 0;
  height: 5px;
  left: -3px;
  right: -3px;
}

/**
 * Color Widget Editor
 */

.colorwidget-value {
  position: relative;
  margin-top: 8px;
}

/**
 * Color Widget Select
 */

.colorwidget-select {
  width: 100%;
}

.colorwidget-select-spacing {
  letter-spacing: 40px;
}

.colorwidget-select-spacing option {
  letter-spacing: initial;
}

/**
 * Color Widget Inputs
 */

.colorwidget-hidden {
  display: none;
}

.colorwidget-hex,
.colorwidget-rgba,
.colorwidget-hsla {
  width: 200px;
  font-size: 0;
}

.colorwidget-hex-input {
  width: 192px;
}

.colorwidget-rgba-r,
.colorwidget-rgba-g,
.colorwidget-rgba-b,
.colorwidget-rgba-a,
.colorwidget-hsla-h,
.colorwidget-hsla-s,
.colorwidget-hsla-l,
.colorwidget-hsla-a {
  width: 42px;
  margin: 0;
}PK
!<�׺7��7chrome/devtools/content/shared/widgets/cubic-bezier.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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;

  -moz-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-body-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);
}
PK
!<^�㽇�8chrome/devtools/content/shared/widgets/filter-widget.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 {
  -moz-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;
}

#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 {
  -moz-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-body-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-toolbar-color);
}

.show-presets #toggle-presets {
  fill: var(--theme-toolbar-checked-color);
}
PK
!<���W339chrome/devtools/content/shared/widgets/graphs-frame.xhtml<?xml version="1.0" encoding="UTF-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/. -->
<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
  <link rel="stylesheet" href="chrome://devtools/skin/widgets.css" ype="text/css"/>
  <script type="application/javascript" src="chrome://devtools/content/shared/theme-switching.js"/>
  <style>
    body {
      overflow: hidden;
      margin: 0;
      padding: 0;
      font-size: 0;
    }
  </style>
</head>
<body role="application">
  <div id="graph-container">
    <canvas id="graph-canvas"></canvas>
  </div>
</body>
</html>
PK
!<���7�
�
3chrome/devtools/content/shared/widgets/spectrum.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#eyedropper-button {
  margin-inline-start: 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-slider-control {
  cursor: pointer;
  box-shadow: 0 0 2px rgba(0,0,0,.6);
  background: #fff;
  border-radius: 10px;
  opacity: .8;
}

.spectrum-box {
  border: 1px solid rgba(0,0,0,0.2);
  border-radius: 2px;
  background-clip: content-box;
}

/* Elements */

#spectrum-tooltip {
  padding: 4px;
}

.spectrum-container {
  position: relative;
  display: none;
  top: 0;
  left: 0;
  border-radius: 0;
  width: 200px;
  padding: 5px;
}

.spectrum-show {
  display: inline-block;
}

/* Keep aspect ratio:
http://www.briangrinstead.com/blog/keep-aspect-ratio-with-html-and-css */
.spectrum-top {
  position: relative;
  width: 100%;
  display: inline-block;
}

.spectrum-top-inner {
  position: absolute;
  top:0;
  left:0;
  bottom:0;
  right:0;
}

.spectrum-color {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  right: 20%;
}

.spectrum-hue {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 83%;
}

.spectrum-fill {
  /* Same as spectrum-color width */
  margin-top: 85%;
}

.spectrum-sat, .spectrum-val {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
}

.spectrum-dragger, .spectrum-slider {
  -moz-user-select: none;
}

.spectrum-alpha {
  position: relative;
  height: 8px;
  margin-top: 3px;
}

.spectrum-alpha-inner {
  height: 100%;
}

.spectrum-alpha-handle {
  position: absolute;
  top: -3px;
  bottom: -3px;
  width: 5px;
  left: 50%;
}

.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-hue {
  background: linear-gradient(to bottom, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);
}

.spectrum-dragger {
  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,.6);
}

.spectrum-slider {
  position: absolute;
  top: 0;
  height: 5px;
  left: -3px;
  right: -3px;
}
PK
!<+{d�
�
2chrome/devtools/content/shared/widgets/widgets.css/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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;
}

/* SimpleListWidget */

.simple-list-widget-container {
  overflow-x: hidden;
  overflow-y: auto;
}

/* FastListWidget */

.fast-list-widget-container {
  overflow: auto;
}

/* SideMenuWidget */

.side-menu-widget-container {
  overflow-x: hidden;
  overflow-y: auto;
}

.side-menu-widget-item-contents {
  -moz-user-focus: normal;
}

.side-menu-widget-group-checkbox .checkbox-label-box,
.side-menu-widget-item-checkbox .checkbox-label-box {
  display: none; /* See bug 669507 */
}

/* 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;
}
PK
!<�H[���Gchrome/devtools/content/sourceeditor/codemirror/addon/dialog/dialog.css.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%;
}
PK
!<S|��%>%>Gchrome/devtools/content/sourceeditor/codemirror/addon/hint/show-hint.js// 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("../../lib/codemirror"));
  else if (typeof define == "function" && define.amd) // AMD
    define(["../../lib/codemirror"], mod);
  else // Plain browser env
    mod(CodeMirror);
})(function(CodeMirror) {
  "use strict";

  var HINT_ELEMENT_CLASS        = "CodeMirror-hint";
  var ACTIVE_HINT_ELEMENT_CLASS = "CodeMirror-hint-active";

  // This is the old interface, kept around for now to stay
  // backwards-compatible.
  CodeMirror.showHint = function(cm, getHints, options) {
    if (!getHints) return cm.showHint(options);
    if (options && options.async) getHints.async = true;
    var newOpts = {hint: getHints};
    if (options) for (var prop in options) newOpts[prop] = options[prop];
    return cm.showHint(newOpts);
  };

  CodeMirror.defineExtension("showHint", function(options) {
    options = parseOptions(this, this.getCursor("start"), options);
    var selections = this.listSelections()
    if (selections.length > 1) return;
    // By default, don't allow completion when something is selected.
    // A hint function can have a `supportsSelection` property to
    // indicate that it can handle selections.
    if (this.somethingSelected()) {
      if (!options.hint.supportsSelection) return;
      // Don't try with cross-line selections
      for (var i = 0; i < selections.length; i++)
        if (selections[i].head.line != selections[i].anchor.line) return;
    }

    if (this.state.completionActive) this.state.completionActive.close();
    var completion = this.state.completionActive = new Completion(this, options);
    if (!completion.options.hint) return;

    CodeMirror.signal(this, "startCompletion", this);
    completion.update(true);
  });

  function Completion(cm, options) {
    this.cm = cm;
    this.options = options;
    this.widget = null;
    this.debounce = 0;
    this.tick = 0;
    this.startPos = this.cm.getCursor("start");
    this.startLen = this.cm.getLine(this.startPos.line).length - this.cm.getSelection().length;

    var self = this;
    cm.on("cursorActivity", this.activityFunc = function() { self.cursorActivity(); });
  }

  var requestAnimationFrame = window.requestAnimationFrame || function(fn) {
    return setTimeout(fn, 1000/60);
  };
  var cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout;

  Completion.prototype = {
    close: function() {
      if (!this.active()) return;
      this.cm.state.completionActive = null;
      this.tick = null;
      this.cm.off("cursorActivity", this.activityFunc);

      if (this.widget && this.data) CodeMirror.signal(this.data, "close");
      if (this.widget) this.widget.close();
      CodeMirror.signal(this.cm, "endCompletion", this.cm);
    },

    active: function() {
      return this.cm.state.completionActive == this;
    },

    pick: function(data, i) {
      var completion = data.list[i];
      if (completion.hint) completion.hint(this.cm, data, completion);
      else this.cm.replaceRange(getText(completion), completion.from || data.from,
                                completion.to || data.to, "complete");
      CodeMirror.signal(data, "pick", completion);
      this.close();
    },

    cursorActivity: function() {
      if (this.debounce) {
        cancelAnimationFrame(this.debounce);
        this.debounce = 0;
      }

      var pos = this.cm.getCursor(), line = this.cm.getLine(pos.line);
      if (pos.line != this.startPos.line || line.length - pos.ch != this.startLen - this.startPos.ch ||
          pos.ch < this.startPos.ch || this.cm.somethingSelected() ||
          (pos.ch && this.options.closeCharacters.test(line.charAt(pos.ch - 1)))) {
        this.close();
      } else {
        var self = this;
        this.debounce = requestAnimationFrame(function() {self.update();});
        if (this.widget) this.widget.disable();
      }
    },

    update: function(first) {
      if (this.tick == null) return
      var self = this, myTick = ++this.tick
      fetchHints(this.options.hint, this.cm, this.options, function(data) {
        if (self.tick == myTick) self.finishUpdate(data, first)
      })
    },

    finishUpdate: function(data, first) {
      if (this.data) CodeMirror.signal(this.data, "update");

      var picked = (this.widget && this.widget.picked) || (first && this.options.completeSingle);
      if (this.widget) this.widget.close();

      this.data = data;

      if (data && data.list.length) {
        if (picked && data.list.length == 1) {
          this.pick(data, 0);
        } else {
          this.widget = new Widget(this, data);
          CodeMirror.signal(data, "shown");
        }
      }
    }
  };

  function parseOptions(cm, pos, options) {
    var editor = cm.options.hintOptions;
    var out = {};
    for (var prop in defaultOptions) out[prop] = defaultOptions[prop];
    if (editor) for (var prop in editor)
      if (editor[prop] !== undefined) out[prop] = editor[prop];
    if (options) for (var prop in options)
      if (options[prop] !== undefined) out[prop] = options[prop];
    if (out.hint.resolve) out.hint = out.hint.resolve(cm, pos)
    return out;
  }

  function getText(completion) {
    if (typeof completion == "string") return completion;
    else return completion.text;
  }

  function buildKeyMap(completion, handle) {
    var baseMap = {
      Up: function() {handle.moveFocus(-1);},
      Down: function() {handle.moveFocus(1);},
      PageUp: function() {handle.moveFocus(-handle.menuSize() + 1, true);},
      PageDown: function() {handle.moveFocus(handle.menuSize() - 1, true);},
      Home: function() {handle.setFocus(0);},
      End: function() {handle.setFocus(handle.length - 1);},
      Enter: handle.pick,
      Tab: handle.pick,
      Esc: handle.close
    };
    var custom = completion.options.customKeys;
    var ourMap = custom ? {} : baseMap;
    function addBinding(key, val) {
      var bound;
      if (typeof val != "string")
        bound = function(cm) { return val(cm, handle); };
      // This mechanism is deprecated
      else if (baseMap.hasOwnProperty(val))
        bound = baseMap[val];
      else
        bound = val;
      ourMap[key] = bound;
    }
    if (custom)
      for (var key in custom) if (custom.hasOwnProperty(key))
        addBinding(key, custom[key]);
    var extra = completion.options.extraKeys;
    if (extra)
      for (var key in extra) if (extra.hasOwnProperty(key))
        addBinding(key, extra[key]);
    return ourMap;
  }

  function getHintElement(hintsElement, el) {
    while (el && el != hintsElement) {
      if (el.nodeName.toUpperCase() === "LI" && el.parentNode == hintsElement) return el;
      el = el.parentNode;
    }
  }

  function Widget(completion, data) {
    this.completion = completion;
    this.data = data;
    this.picked = false;
    var widget = this, cm = completion.cm;

    var hints = this.hints = document.createElement("ul");
    hints.className = "CodeMirror-hints";
    this.selectedHint = data.selectedHint || 0;

    var completions = data.list;
    for (var i = 0; i < completions.length; ++i) {
      var elt = hints.appendChild(document.createElement("li")), cur = completions[i];
      var className = HINT_ELEMENT_CLASS + (i != this.selectedHint ? "" : " " + ACTIVE_HINT_ELEMENT_CLASS);
      if (cur.className != null) className = cur.className + " " + className;
      elt.className = className;
      if (cur.render) cur.render(elt, data, cur);
      else elt.appendChild(document.createTextNode(cur.displayText || getText(cur)));
      elt.hintId = i;
    }

    var pos = cm.cursorCoords(completion.options.alignWithWord ? data.from : null);
    var left = pos.left, top = pos.bottom, below = true;
    hints.style.left = left + "px";
    hints.style.top = top + "px";
    // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor.
    var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth);
    var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight);
    (completion.options.container || document.body).appendChild(hints);
    var box = hints.getBoundingClientRect(), overlapY = box.bottom - winH;
    var scrolls = hints.scrollHeight > hints.clientHeight + 1
    var startScroll = cm.getScrollInfo();

    if (overlapY > 0) {
      var height = box.bottom - box.top, curTop = pos.top - (pos.bottom - box.top);
      if (curTop - height > 0) { // Fits above cursor
        hints.style.top = (top = pos.top - height) + "px";
        below = false;
      } else if (height > winH) {
        hints.style.height = (winH - 5) + "px";
        hints.style.top = (top = pos.bottom - box.top) + "px";
        var cursor = cm.getCursor();
        if (data.from.ch != cursor.ch) {
          pos = cm.cursorCoords(cursor);
          hints.style.left = (left = pos.left) + "px";
          box = hints.getBoundingClientRect();
        }
      }
    }
    var overlapX = box.right - winW;
    if (overlapX > 0) {
      if (box.right - box.left > winW) {
        hints.style.width = (winW - 5) + "px";
        overlapX -= (box.right - box.left) - winW;
      }
      hints.style.left = (left = pos.left - overlapX) + "px";
    }
    if (scrolls) for (var node = hints.firstChild; node; node = node.nextSibling)
      node.style.paddingRight = cm.display.nativeBarWidth + "px"

    cm.addKeyMap(this.keyMap = buildKeyMap(completion, {
      moveFocus: function(n, avoidWrap) { widget.changeActive(widget.selectedHint + n, avoidWrap); },
      setFocus: function(n) { widget.changeActive(n); },
      menuSize: function() { return widget.screenAmount(); },
      length: completions.length,
      close: function() { completion.close(); },
      pick: function() { widget.pick(); },
      data: data
    }));

    if (completion.options.closeOnUnfocus) {
      var closingOnBlur;
      cm.on("blur", this.onBlur = function() { closingOnBlur = setTimeout(function() { completion.close(); }, 100); });
      cm.on("focus", this.onFocus = function() { clearTimeout(closingOnBlur); });
    }

    cm.on("scroll", this.onScroll = function() {
      var curScroll = cm.getScrollInfo(), editor = cm.getWrapperElement().getBoundingClientRect();
      var newTop = top + startScroll.top - curScroll.top;
      var point = newTop - (window.pageYOffset || (document.documentElement || document.body).scrollTop);
      if (!below) point += hints.offsetHeight;
      if (point <= editor.top || point >= editor.bottom) return completion.close();
      hints.style.top = newTop + "px";
      hints.style.left = (left + startScroll.left - curScroll.left) + "px";
    });

    CodeMirror.on(hints, "dblclick", function(e) {
      var t = getHintElement(hints, e.target || e.srcElement);
      if (t && t.hintId != null) {widget.changeActive(t.hintId); widget.pick();}
    });

    CodeMirror.on(hints, "click", function(e) {
      var t = getHintElement(hints, e.target || e.srcElement);
      if (t && t.hintId != null) {
        widget.changeActive(t.hintId);
        if (completion.options.completeOnSingleClick) widget.pick();
      }
    });

    CodeMirror.on(hints, "mousedown", function() {
      setTimeout(function(){cm.focus();}, 20);
    });

    CodeMirror.signal(data, "select", completions[this.selectedHint], hints.childNodes[this.selectedHint]);
    return true;
  }

  Widget.prototype = {
    close: function() {
      if (this.completion.widget != this) return;
      this.completion.widget = null;
      this.hints.parentNode.removeChild(this.hints);
      this.completion.cm.removeKeyMap(this.keyMap);

      var cm = this.completion.cm;
      if (this.completion.options.closeOnUnfocus) {
        cm.off("blur", this.onBlur);
        cm.off("focus", this.onFocus);
      }
      cm.off("scroll", this.onScroll);
    },

    disable: function() {
      this.completion.cm.removeKeyMap(this.keyMap);
      var widget = this;
      this.keyMap = {Enter: function() { widget.picked = true; }};
      this.completion.cm.addKeyMap(this.keyMap);
    },

    pick: function() {
      this.completion.pick(this.data, this.selectedHint);
    },

    changeActive: function(i, avoidWrap) {
      if (i >= this.data.list.length)
        i = avoidWrap ? this.data.list.length - 1 : 0;
      else if (i < 0)
        i = avoidWrap ? 0  : this.data.list.length - 1;
      if (this.selectedHint == i) return;
      var node = this.hints.childNodes[this.selectedHint];
      node.className = node.className.replace(" " + ACTIVE_HINT_ELEMENT_CLASS, "");
      node = this.hints.childNodes[this.selectedHint = i];
      node.className += " " + ACTIVE_HINT_ELEMENT_CLASS;
      if (node.offsetTop < this.hints.scrollTop)
        this.hints.scrollTop = node.offsetTop - 3;
      else if (node.offsetTop + node.offsetHeight > this.hints.scrollTop + this.hints.clientHeight)
        this.hints.scrollTop = node.offsetTop + node.offsetHeight - this.hints.clientHeight + 3;
      CodeMirror.signal(this.data, "select", this.data.list[this.selectedHint], node);
    },

    screenAmount: function() {
      return Math.floor(this.hints.clientHeight / this.hints.firstChild.offsetHeight) || 1;
    }
  };

  function applicableHelpers(cm, helpers) {
    if (!cm.somethingSelected()) return helpers
    var result = []
    for (var i = 0; i < helpers.length; i++)
      if (helpers[i].supportsSelection) result.push(helpers[i])
    return result
  }

  function fetchHints(hint, cm, options, callback) {
    if (hint.async) {
      hint(cm, callback, options)
    } else {
      var result = hint(cm, options)
      if (result && result.then) result.then(callback)
      else callback(result)
    }
  }

  function resolveAutoHints(cm, pos) {
    var helpers = cm.getHelpers(pos, "hint"), words
    if (helpers.length) {
      var resolved = function(cm, callback, options) {
        var app = applicableHelpers(cm, helpers);
        function run(i) {
          if (i == app.length) return callback(null)
          fetchHints(app[i], cm, options, function(result) {
            if (result && result.list.length > 0) callback(result)
            else run(i + 1)
          })
        }
        run(0)
      }
      resolved.async = true
      resolved.supportsSelection = true
      return resolved
    } else if (words = cm.getHelper(cm.getCursor(), "hintWords")) {
      return function(cm) { return CodeMirror.hint.fromList(cm, {words: words}) }
    } else if (CodeMirror.hint.anyword) {
      return function(cm, options) { return CodeMirror.hint.anyword(cm, options) }
    } else {
      return function() {}
    }
  }

  CodeMirror.registerHelper("hint", "auto", {
    resolve: resolveAutoHints
  });

  CodeMirror.registerHelper("hint", "fromList", function(cm, options) {
    var cur = cm.getCursor(), token = cm.getTokenAt(cur);
    var to = CodeMirror.Pos(cur.line, token.end);
    if (token.string && /\w/.test(token.string[token.string.length - 1])) {
      var term = token.string, from = CodeMirror.Pos(cur.line, token.start);
    } else {
      var term = "", from = to;
    }
    var found = [];
    for (var i = 0; i < options.words.length; i++) {
      var word = options.words[i];
      if (word.slice(0, term.length) == term)
        found.push(word);
    }

    if (found.length) return {list: found, from: from, to: to};
  });

  CodeMirror.commands.autocomplete = CodeMirror.showHint;

  var defaultOptions = {
    hint: CodeMirror.hint.auto,
    completeSingle: true,
    alignWithWord: true,
    closeCharacters: /[\s()\[\]{};:>,]/,
    closeOnUnfocus: true,
    completeOnSingleClick: true,
    container: null,
    customKeys: null,
    extraKeys: null
  };

  CodeMirror.defineOption("hintOptions", null);
});
PK
!<�s>%�a�aBchrome/devtools/content/sourceeditor/codemirror/addon/tern/tern.js// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE

// Glue code between CodeMirror and Tern.
//
// Create a CodeMirror.TernServer to wrap an actual Tern server,
// register open documents (CodeMirror.Doc instances) with it, and
// call its methods to activate the assisting functions that Tern
// provides.
//
// Options supported (all optional):
// * defs: An array of JSON definition data structures.
// * plugins: An object mapping plugin names to configuration
//   options.
// * getFile: A function(name, c) that can be used to access files in
//   the project that haven't been loaded yet. Simply do c(null) to
//   indicate that a file is not available.
// * fileFilter: A function(value, docName, doc) that will be applied
//   to documents before passing them on to Tern.
// * switchToDoc: A function(name, doc) that should, when providing a
//   multi-file view, switch the view or focus to the named file.
// * showError: A function(editor, message) that can be used to
//   override the way errors are displayed.
// * completionTip: Customize the content in tooltips for completions.
//   Is passed a single argument—the completion's data as returned by
//   Tern—and may return a string, DOM node, or null to indicate that
//   no tip should be shown. By default the docstring is shown.
// * typeTip: Like completionTip, but for the tooltips shown for type
//   queries.
// * responseFilter: A function(doc, query, request, error, data) that
//   will be applied to the Tern responses before treating them
//
//
// It is possible to run the Tern server in a web worker by specifying
// these additional options:
// * useWorker: Set to true to enable web worker mode. You'll probably
//   want to feature detect the actual value you use here, for example
//   !!window.Worker.
// * workerScript: The main script of the worker. Point this to
//   wherever you are hosting worker.js from this directory.
// * workerDeps: An array of paths pointing (relative to workerScript)
//   to the Acorn and Tern libraries and any Tern plugins you want to
//   load. Or, if you minified those into a single script and included
//   them in the workerScript, simply leave this undefined.

(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";
  // declare global: tern

  CodeMirror.TernServer = function(options) {
    var self = this;
    this.options = options || {};
    var plugins = this.options.plugins || (this.options.plugins = {});
    if (!plugins.doc_comment) plugins.doc_comment = true;
    this.docs = Object.create(null);
    if (this.options.useWorker) {
      this.server = new WorkerServer(this);
    } else {
      this.server = new tern.Server({
        getFile: function(name, c) { return getFile(self, name, c); },
        async: true,
        defs: this.options.defs || [],
        plugins: plugins
      });
    }
    this.trackChange = function(doc, change) { trackChange(self, doc, change); };

    this.cachedArgHints = null;
    this.activeArgHints = null;
    this.jumpStack = [];

    this.getHint = function(cm, c) { return hint(self, cm, c); };
    this.getHint.async = true;
  };

  CodeMirror.TernServer.prototype = {
    addDoc: function(name, doc) {
      var data = {doc: doc, name: name, changed: null};
      this.server.addFile(name, docValue(this, data));
      CodeMirror.on(doc, "change", this.trackChange);
      return this.docs[name] = data;
    },

    delDoc: function(id) {
      var found = resolveDoc(this, id);
      if (!found) return;
      CodeMirror.off(found.doc, "change", this.trackChange);
      delete this.docs[found.name];
      this.server.delFile(found.name);
    },

    hideDoc: function(id) {
      closeArgHints(this);
      var found = resolveDoc(this, id);
      if (found && found.changed) sendDoc(this, found);
    },

    complete: function(cm) {
      cm.showHint({hint: this.getHint});
    },

    showType: function(cm, pos, c) { showContextInfo(this, cm, pos, "type", c); },

    showDocs: function(cm, pos, c) { showContextInfo(this, cm, pos, "documentation", c); },

    updateArgHints: function(cm) { updateArgHints(this, cm); },

    jumpToDef: function(cm) { jumpToDef(this, cm); },

    jumpBack: function(cm) { jumpBack(this, cm); },

    rename: function(cm) { rename(this, cm); },

    selectName: function(cm) { selectName(this, cm); },

    request: function (cm, query, c, pos) {
      var self = this;
      var doc = findDoc(this, cm.getDoc());
      var request = buildRequest(this, doc, query, pos);
      var extraOptions = request.query && this.options.queryOptions && this.options.queryOptions[request.query.type]
      if (extraOptions) for (var prop in extraOptions) request.query[prop] = extraOptions[prop];

      this.server.request(request, function (error, data) {
        if (!error && self.options.responseFilter)
          data = self.options.responseFilter(doc, query, request, error, data);
        c(error, data);
      });
    },

    destroy: function () {
      closeArgHints(this)
      if (this.worker) {
        this.worker.terminate();
        this.worker = null;
      }
    }
  };

  var Pos = CodeMirror.Pos;
  var cls = "CodeMirror-Tern-";
  var bigDoc = 250;

  function getFile(ts, name, c) {
    var buf = ts.docs[name];
    if (buf)
      c(docValue(ts, buf));
    else if (ts.options.getFile)
      ts.options.getFile(name, c);
    else
      c(null);
  }

  function findDoc(ts, doc, name) {
    for (var n in ts.docs) {
      var cur = ts.docs[n];
      if (cur.doc == doc) return cur;
    }
    if (!name) for (var i = 0;; ++i) {
      n = "[doc" + (i || "") + "]";
      if (!ts.docs[n]) { name = n; break; }
    }
    return ts.addDoc(name, doc);
  }

  function resolveDoc(ts, id) {
    if (typeof id == "string") return ts.docs[id];
    if (id instanceof CodeMirror) id = id.getDoc();
    if (id instanceof CodeMirror.Doc) return findDoc(ts, id);
  }

  function trackChange(ts, doc, change) {
    var data = findDoc(ts, doc);

    var argHints = ts.cachedArgHints;
    if (argHints && argHints.doc == doc && cmpPos(argHints.start, change.to) >= 0)
      ts.cachedArgHints = null;

    var changed = data.changed;
    if (changed == null)
      data.changed = changed = {from: change.from.line, to: change.from.line};
    var end = change.from.line + (change.text.length - 1);
    if (change.from.line < changed.to) changed.to = changed.to - (change.to.line - end);
    if (end >= changed.to) changed.to = end + 1;
    if (changed.from > change.from.line) changed.from = change.from.line;

    if (doc.lineCount() > bigDoc && change.to - changed.from > 100) setTimeout(function() {
      if (data.changed && data.changed.to - data.changed.from > 100) sendDoc(ts, data);
    }, 200);
  }

  function sendDoc(ts, doc) {
    ts.server.request({files: [{type: "full", name: doc.name, text: docValue(ts, doc)}]}, function(error) {
      if (error) window.console.error(error);
      else doc.changed = null;
    });
  }

  // Completion

  function hint(ts, cm, c) {
    ts.request(cm, {type: "completions", types: true, docs: true, urls: true}, function(error, data) {
      if (error) return showError(ts, cm, error);
      var completions = [], after = "";
      var from = data.start, to = data.end;
      if (cm.getRange(Pos(from.line, from.ch - 2), from) == "[\"" &&
          cm.getRange(to, Pos(to.line, to.ch + 2)) != "\"]")
        after = "\"]";

      for (var i = 0; i < data.completions.length; ++i) {
        var completion = data.completions[i], className = typeToIcon(completion.type);
        if (data.guess) className += " " + cls + "guess";
        completions.push({text: completion.name + after,
                          displayText: completion.displayName || completion.name,
                          className: className,
                          data: completion});
      }

      var obj = {from: from, to: to, list: completions};
      var tooltip = null;
      CodeMirror.on(obj, "close", function() { remove(tooltip); });
      CodeMirror.on(obj, "update", function() { remove(tooltip); });
      CodeMirror.on(obj, "select", function(cur, node) {
        remove(tooltip);
        var content = ts.options.completionTip ? ts.options.completionTip(cur.data) : cur.data.doc;
        if (content) {
          tooltip = makeTooltip(node.parentNode.getBoundingClientRect().right + window.pageXOffset,
                                node.getBoundingClientRect().top + window.pageYOffset, content);
          tooltip.className += " " + cls + "hint-doc";
        }
      });
      c(obj);
    });
  }

  function typeToIcon(type) {
    var suffix;
    if (type == "?") suffix = "unknown";
    else if (type == "number" || type == "string" || type == "bool") suffix = type;
    else if (/^fn\(/.test(type)) suffix = "fn";
    else if (/^\[/.test(type)) suffix = "array";
    else suffix = "object";
    return cls + "completion " + cls + "completion-" + suffix;
  }

  // Type queries

  function showContextInfo(ts, cm, pos, queryName, c) {
    ts.request(cm, queryName, function(error, data) {
      if (error) return showError(ts, cm, error);
      if (ts.options.typeTip) {
        var tip = ts.options.typeTip(data);
      } else {
        var tip = elt("span", null, elt("strong", null, data.type || "not found"));
        if (data.doc)
          tip.appendChild(document.createTextNode(" — " + data.doc));
        if (data.url) {
          tip.appendChild(document.createTextNode(" "));
          var child = tip.appendChild(elt("a", null, "[docs]"));
          child.href = data.url;
          child.target = "_blank";
        }
      }
      tempTooltip(cm, tip, ts);
      if (c) c();
    }, pos);
  }

  // Maintaining argument hints

  function updateArgHints(ts, cm) {
    closeArgHints(ts);

    if (cm.somethingSelected()) return;
    var state = cm.getTokenAt(cm.getCursor()).state;
    var inner = CodeMirror.innerMode(cm.getMode(), state);
    if (inner.mode.name != "javascript") return;
    var lex = inner.state.lexical;
    if (lex.info != "call") return;

    var ch, argPos = lex.pos || 0, tabSize = cm.getOption("tabSize");
    for (var line = cm.getCursor().line, e = Math.max(0, line - 9), found = false; line >= e; --line) {
      var str = cm.getLine(line), extra = 0;
      for (var pos = 0;;) {
        var tab = str.indexOf("\t", pos);
        if (tab == -1) break;
        extra += tabSize - (tab + extra) % tabSize - 1;
        pos = tab + 1;
      }
      ch = lex.column - extra;
      if (str.charAt(ch) == "(") {found = true; break;}
    }
    if (!found) return;

    var start = Pos(line, ch);
    var cache = ts.cachedArgHints;
    if (cache && cache.doc == cm.getDoc() && cmpPos(start, cache.start) == 0)
      return showArgHints(ts, cm, argPos);

    ts.request(cm, {type: "type", preferFunction: true, end: start}, function(error, data) {
      if (error || !data.type || !(/^fn\(/).test(data.type)) return;
      ts.cachedArgHints = {
        start: start,
        type: parseFnType(data.type),
        name: data.exprName || data.name || "fn",
        guess: data.guess,
        doc: cm.getDoc()
      };
      showArgHints(ts, cm, argPos);
    });
  }

  function showArgHints(ts, cm, pos) {
    closeArgHints(ts);

    var cache = ts.cachedArgHints, tp = cache.type;
    var tip = elt("span", cache.guess ? cls + "fhint-guess" : null,
                  elt("span", cls + "fname", cache.name), "(");
    for (var i = 0; i < tp.args.length; ++i) {
      if (i) tip.appendChild(document.createTextNode(", "));
      var arg = tp.args[i];
      tip.appendChild(elt("span", cls + "farg" + (i == pos ? " " + cls + "farg-current" : ""), arg.name || "?"));
      if (arg.type != "?") {
        tip.appendChild(document.createTextNode(":\u00a0"));
        tip.appendChild(elt("span", cls + "type", arg.type));
      }
    }
    tip.appendChild(document.createTextNode(tp.rettype ? ") ->\u00a0" : ")"));
    if (tp.rettype) tip.appendChild(elt("span", cls + "type", tp.rettype));
    var place = cm.cursorCoords(null, "page");
    var tooltip = ts.activeArgHints = makeTooltip(place.right + 1, place.bottom, tip)
    setTimeout(function() {
      tooltip.clear = onEditorActivity(cm, function() {
        if (ts.activeArgHints == tooltip) closeArgHints(ts) })
    }, 20)
  }

  function parseFnType(text) {
    var args = [], pos = 3;

    function skipMatching(upto) {
      var depth = 0, start = pos;
      for (;;) {
        var next = text.charAt(pos);
        if (upto.test(next) && !depth) return text.slice(start, pos);
        if (/[{\[\(]/.test(next)) ++depth;
        else if (/[}\]\)]/.test(next)) --depth;
        ++pos;
      }
    }

    // Parse arguments
    if (text.charAt(pos) != ")") for (;;) {
      var name = text.slice(pos).match(/^([^, \(\[\{]+): /);
      if (name) {
        pos += name[0].length;
        name = name[1];
      }
      args.push({name: name, type: skipMatching(/[\),]/)});
      if (text.charAt(pos) == ")") break;
      pos += 2;
    }

    var rettype = text.slice(pos).match(/^\) -> (.*)$/);

    return {args: args, rettype: rettype && rettype[1]};
  }

  // Moving to the definition of something

  function jumpToDef(ts, cm) {
    function inner(varName) {
      var req = {type: "definition", variable: varName || null};
      var doc = findDoc(ts, cm.getDoc());
      ts.server.request(buildRequest(ts, doc, req), function(error, data) {
        if (error) return showError(ts, cm, error);
        if (!data.file && data.url) { window.open(data.url); return; }

        if (data.file) {
          var localDoc = ts.docs[data.file], found;
          if (localDoc && (found = findContext(localDoc.doc, data))) {
            ts.jumpStack.push({file: doc.name,
                               start: cm.getCursor("from"),
                               end: cm.getCursor("to")});
            moveTo(ts, doc, localDoc, found.start, found.end);
            return;
          }
        }
        showError(ts, cm, "Could not find a definition.");
      });
    }

    if (!atInterestingExpression(cm))
      dialog(cm, "Jump to variable", function(name) { if (name) inner(name); });
    else
      inner();
  }

  function jumpBack(ts, cm) {
    var pos = ts.jumpStack.pop(), doc = pos && ts.docs[pos.file];
    if (!doc) return;
    moveTo(ts, findDoc(ts, cm.getDoc()), doc, pos.start, pos.end);
  }

  function moveTo(ts, curDoc, doc, start, end) {
    doc.doc.setSelection(start, end);
    if (curDoc != doc && ts.options.switchToDoc) {
      closeArgHints(ts);
      ts.options.switchToDoc(doc.name, doc.doc);
    }
  }

  // The {line,ch} representation of positions makes this rather awkward.
  function findContext(doc, data) {
    var before = data.context.slice(0, data.contextOffset).split("\n");
    var startLine = data.start.line - (before.length - 1);
    var start = Pos(startLine, (before.length == 1 ? data.start.ch : doc.getLine(startLine).length) - before[0].length);

    var text = doc.getLine(startLine).slice(start.ch);
    for (var cur = startLine + 1; cur < doc.lineCount() && text.length < data.context.length; ++cur)
      text += "\n" + doc.getLine(cur);
    if (text.slice(0, data.context.length) == data.context) return data;

    var cursor = doc.getSearchCursor(data.context, 0, false);
    var nearest, nearestDist = Infinity;
    while (cursor.findNext()) {
      var from = cursor.from(), dist = Math.abs(from.line - start.line) * 10000;
      if (!dist) dist = Math.abs(from.ch - start.ch);
      if (dist < nearestDist) { nearest = from; nearestDist = dist; }
    }
    if (!nearest) return null;

    if (before.length == 1)
      nearest.ch += before[0].length;
    else
      nearest = Pos(nearest.line + (before.length - 1), before[before.length - 1].length);
    if (data.start.line == data.end.line)
      var end = Pos(nearest.line, nearest.ch + (data.end.ch - data.start.ch));
    else
      var end = Pos(nearest.line + (data.end.line - data.start.line), data.end.ch);
    return {start: nearest, end: end};
  }

  function atInterestingExpression(cm) {
    var pos = cm.getCursor("end"), tok = cm.getTokenAt(pos);
    if (tok.start < pos.ch && tok.type == "comment") return false;
    return /[\w)\]]/.test(cm.getLine(pos.line).slice(Math.max(pos.ch - 1, 0), pos.ch + 1));
  }

  // Variable renaming

  function rename(ts, cm) {
    var token = cm.getTokenAt(cm.getCursor());
    if (!/\w/.test(token.string)) return showError(ts, cm, "Not at a variable");
    dialog(cm, "New name for " + token.string, function(newName) {
      ts.request(cm, {type: "rename", newName: newName, fullDocs: true}, function(error, data) {
        if (error) return showError(ts, cm, error);
        applyChanges(ts, data.changes);
      });
    });
  }

  function selectName(ts, cm) {
    var name = findDoc(ts, cm.doc).name;
    ts.request(cm, {type: "refs"}, function(error, data) {
      if (error) return showError(ts, cm, error);
      var ranges = [], cur = 0;
      var curPos = cm.getCursor();
      for (var i = 0; i < data.refs.length; i++) {
        var ref = data.refs[i];
        if (ref.file == name) {
          ranges.push({anchor: ref.start, head: ref.end});
          if (cmpPos(curPos, ref.start) >= 0 && cmpPos(curPos, ref.end) <= 0)
            cur = ranges.length - 1;
        }
      }
      cm.setSelections(ranges, cur);
    });
  }

  var nextChangeOrig = 0;
  function applyChanges(ts, changes) {
    var perFile = Object.create(null);
    for (var i = 0; i < changes.length; ++i) {
      var ch = changes[i];
      (perFile[ch.file] || (perFile[ch.file] = [])).push(ch);
    }
    for (var file in perFile) {
      var known = ts.docs[file], chs = perFile[file];;
      if (!known) continue;
      chs.sort(function(a, b) { return cmpPos(b.start, a.start); });
      var origin = "*rename" + (++nextChangeOrig);
      for (var i = 0; i < chs.length; ++i) {
        var ch = chs[i];
        known.doc.replaceRange(ch.text, ch.start, ch.end, origin);
      }
    }
  }

  // Generic request-building helper

  function buildRequest(ts, doc, query, pos) {
    var files = [], offsetLines = 0, allowFragments = !query.fullDocs;
    if (!allowFragments) delete query.fullDocs;
    if (typeof query == "string") query = {type: query};
    query.lineCharPositions = true;
    if (query.end == null) {
      query.end = pos || doc.doc.getCursor("end");
      if (doc.doc.somethingSelected())
        query.start = doc.doc.getCursor("start");
    }
    var startPos = query.start || query.end;

    if (doc.changed) {
      if (doc.doc.lineCount() > bigDoc && allowFragments !== false &&
          doc.changed.to - doc.changed.from < 100 &&
          doc.changed.from <= startPos.line && doc.changed.to > query.end.line) {
        files.push(getFragmentAround(doc, startPos, query.end));
        query.file = "#0";
        var offsetLines = files[0].offsetLines;
        if (query.start != null) query.start = Pos(query.start.line - -offsetLines, query.start.ch);
        query.end = Pos(query.end.line - offsetLines, query.end.ch);
      } else {
        files.push({type: "full",
                    name: doc.name,
                    text: docValue(ts, doc)});
        query.file = doc.name;
        doc.changed = null;
      }
    } else {
      query.file = doc.name;
    }
    for (var name in ts.docs) {
      var cur = ts.docs[name];
      if (cur.changed && cur != doc) {
        files.push({type: "full", name: cur.name, text: docValue(ts, cur)});
        cur.changed = null;
      }
    }

    return {query: query, files: files};
  }

  function getFragmentAround(data, start, end) {
    var doc = data.doc;
    var minIndent = null, minLine = null, endLine, tabSize = 4;
    for (var p = start.line - 1, min = Math.max(0, p - 50); p >= min; --p) {
      var line = doc.getLine(p), fn = line.search(/\bfunction\b/);
      if (fn < 0) continue;
      var indent = CodeMirror.countColumn(line, null, tabSize);
      if (minIndent != null && minIndent <= indent) continue;
      minIndent = indent;
      minLine = p;
    }
    if (minLine == null) minLine = min;
    var max = Math.min(doc.lastLine(), end.line + 20);
    if (minIndent == null || minIndent == CodeMirror.countColumn(doc.getLine(start.line), null, tabSize))
      endLine = max;
    else for (endLine = end.line + 1; endLine < max; ++endLine) {
      var indent = CodeMirror.countColumn(doc.getLine(endLine), null, tabSize);
      if (indent <= minIndent) break;
    }
    var from = Pos(minLine, 0);

    return {type: "part",
            name: data.name,
            offsetLines: from.line,
            text: doc.getRange(from, Pos(endLine, end.line == endLine ? null : 0))};
  }

  // Generic utilities

  var cmpPos = CodeMirror.cmpPos;

  function elt(tagname, cls /*, ... elts*/) {
    var e = document.createElement(tagname);
    if (cls) e.className = cls;
    for (var i = 2; i < arguments.length; ++i) {
      var elt = arguments[i];
      if (typeof elt == "string") elt = document.createTextNode(elt);
      e.appendChild(elt);
    }
    return e;
  }

  function dialog(cm, text, f) {
    if (cm.openDialog)
      cm.openDialog(text + ": <input type=text>", f);
    else
      f(prompt(text, ""));
  }

  // Tooltips

  function tempTooltip(cm, content, ts) {
    if (cm.state.ternTooltip) remove(cm.state.ternTooltip);
    var where = cm.cursorCoords();
    var tip = cm.state.ternTooltip = makeTooltip(where.right + 1, where.bottom, content);
    function maybeClear() {
      old = true;
      if (!mouseOnTip) clear();
    }
    function clear() {
      cm.state.ternTooltip = null;
      if (tip.parentNode) fadeOut(tip)
      clearActivity()
    }
    var mouseOnTip = false, old = false;
    CodeMirror.on(tip, "mousemove", function() { mouseOnTip = true; });
    CodeMirror.on(tip, "mouseout", function(e) {
      var related = e.relatedTarget || e.toElement
      if (!related || !CodeMirror.contains(tip, related)) {
        if (old) clear();
        else mouseOnTip = false;
      }
    });
    setTimeout(maybeClear, ts.options.hintDelay ? ts.options.hintDelay : 1700);
    var clearActivity = onEditorActivity(cm, clear)
  }

  function onEditorActivity(cm, f) {
    cm.on("cursorActivity", f)
    cm.on("blur", f)
    cm.on("scroll", f)
    cm.on("setDoc", f)
    return function() {
      cm.off("cursorActivity", f)
      cm.off("blur", f)
      cm.off("scroll", f)
      cm.off("setDoc", f)
    }
  }

  function makeTooltip(x, y, content) {
    var node = elt("div", cls + "tooltip", content);
    node.style.left = x + "px";
    node.style.top = y + "px";
    document.body.appendChild(node);
    return node;
  }

  function remove(node) {
    var p = node && node.parentNode;
    if (p) p.removeChild(node);
  }

  function fadeOut(tooltip) {
    tooltip.style.opacity = "0";
    setTimeout(function() { remove(tooltip); }, 1100);
  }

  function showError(ts, cm, msg) {
    if (ts.options.showError)
      ts.options.showError(cm, msg);
    else
      tempTooltip(cm, String(msg), ts);
  }

  function closeArgHints(ts) {
    if (ts.activeArgHints) {
      if (ts.activeArgHints.clear) ts.activeArgHints.clear()
      remove(ts.activeArgHints)
      ts.activeArgHints = null
    }
  }

  function docValue(ts, doc) {
    var val = doc.doc.getValue();
    if (ts.options.fileFilter) val = ts.options.fileFilter(val, doc.name, doc.doc);
    return val;
  }

  // Worker wrapper

  function WorkerServer(ts) {
    var worker = ts.worker = new Worker(ts.options.workerScript);
    worker.postMessage({type: "init",
                        defs: ts.options.defs,
                        plugins: ts.options.plugins,
                        scripts: ts.options.workerDeps});
    var msgId = 0, pending = {};

    function send(data, c) {
      if (c) {
        data.id = ++msgId;
        pending[msgId] = c;
      }
      worker.postMessage(data);
    }
    worker.onmessage = function(e) {
      var data = e.data;
      if (data.type == "getFile") {
        getFile(ts, data.name, function(err, text) {
          send({type: "getFile", err: String(err), text: text, id: data.id});
        });
      } else if (data.type == "debug") {
        window.console.log(data.message);
      } else if (data.id && pending[data.id]) {
        pending[data.id](data.err, data.body);
        delete pending[data.id];
      }
    };
    worker.onerror = function(e) {
      for (var id in pending) pending[id](e);
      pending = {};
    };

    this.addFile = function(name, text) { send({type: "add", name: name, text: text}); };
    this.delFile = function(name) { send({type: "del", name: name}); };
    this.request = function(body, c) { send({type: "req", body: body}, c); };
  }
});
PK
!<���%��=chrome/devtools/content/sourceeditor/codemirror/cmiframe.html<!DOCTYPE html>
<html dir='ltr'>
<head>
  <style id="cmBaseStyle">
    html, body { height: 100%; }
    body { margin: 0; overflow: hidden; }
    .CodeMirror { width: 100% !important; line-height: 1.25 !important; }
  </style>
  <link rel='stylesheet' href="chrome://devtools/content/sourceeditor/codemirror/lib/codemirror.css">
  <link rel='stylesheet' href="chrome://devtools/content/sourceeditor/codemirror/addon/dialog/dialog.css">
  <link rel='stylesheet' href="chrome://devtools/content/sourceeditor/codemirror/mozilla.css">
  <link rel='stylesheet' href="chrome://devtools/content/sourceeditor/codemirror/old-debugger.css">
</head>
<body class='theme-body devtools-monospace'></body>
</html>
PK
!<��E�-
�-
Dchrome/devtools/content/sourceeditor/codemirror/codemirror.bundle.jsvar CodeMirror =
/******/ (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__) {

	__webpack_require__(1);
	__webpack_require__(3);
	__webpack_require__(4);
	__webpack_require__(5);
	__webpack_require__(6);
	__webpack_require__(7);
	__webpack_require__(8);
	__webpack_require__(9);
	__webpack_require__(10);
	__webpack_require__(11);
	__webpack_require__(12);
	__webpack_require__(13);
	__webpack_require__(14);
	__webpack_require__(15);
	__webpack_require__(16);
	__webpack_require__(17);
	__webpack_require__(18);
	__webpack_require__(19);
	__webpack_require__(20);
	__webpack_require__(21);
	__webpack_require__(22);
	__webpack_require__(23);
	__webpack_require__(24);
	__webpack_require__(25);
	__webpack_require__(26);
	module.exports = __webpack_require__(2);


/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {

	// CodeMirror, copyright (c) by Marijn Haverbeke and others
	// Distributed under an MIT license: http://codemirror.net/LICENSE

	// Open simple dialogs on top of an editor. Relies on dialog.css.

	(function(mod) {
	  if (true) // CommonJS
	    mod(__webpack_require__(2));
	  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);
	    }
	    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;
	        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;
	      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);
	      dialog.parentNode.removeChild(dialog);
	    }

	    CodeMirror.on(dialog, 'click', function(e) {
	      CodeMirror.e_preventDefault(e);
	      close();
	    });

	    if (duration)
	      doneTimer = setTimeout(close, duration);

	    return close;
	  });
	});


/***/ }),
/* 2 */
/***/ (function(module, exports, __webpack_require__) {

	// CodeMirror, copyright (c) by Marijn Haverbeke and others
	// Distributed under an MIT license: http://codemirror.net/LICENSE

	// This is CodeMirror (http://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) {
	   true ? module.exports = factory() :
	  typeof define === 'function' && define.amd ? define(factory) :
	  (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};
	Delayed.prototype.set = function (ms, f) {
	  clearTimeout(this.id)
	  this.id = setTimeout(f, ms)
	};

	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 = 30

	// 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};
	var sel_mouse = {origin: "*mouse"};
	var 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 }
	  }
	}

	// 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) {
	  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

	  input.init(d)
	}

	// 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
	}

	// Optimize some code when these features are not used.
	var sawReadOnlySpans = false;
	var 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) }

	// 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
	    }
	  })
	}

	// 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
	        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)) }
	            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))
	            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
	}

	var modes = {};
	var 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 this$1 = this;

	  var start = this.pos
	  while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this$1.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)
	};

	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) {
	    var this$1 = this;

	  if (!this.baseTokens) { return null }
	  while (this.baseTokens[this.baseTokenPos] <= n)
	    { this$1.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)
	}

	// 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 = {};
	var 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: (ie || webkit) && 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, title, css) {
	  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) {
	    var fullStyle = style || ""
	    if (startStyle) { fullStyle += startStyle }
	    if (endStyle) { fullStyle += endStyle }
	    var token = elt("span", [content], fullStyle, css)
	    if (title) { token.title = title }
	    return builder.content.appendChild(token)
	  }
	  builder.content.appendChild(content)
	}

	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, title, css) {
	    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, title, css) }
	      inner(builder, text.slice(0, part.to - start), style, startStyle, null, title, css)
	      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, title, collapsed
	  for (;;) {
	    if (nextChange == pos) { // Update current marker set
	      spanStyle = spanEndStyle = spanStartStyle = title = css = ""
	      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) }
	          if (m.title && !title) { title = m.title }
	          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 : "", title, css)
	        }
	        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.options.gutters.length; ++k) {
	      var id = cm.options.gutters[k], 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 }
	  for (var node = lineView.node.firstChild, next = (void 0); node; node = next) {
	    next = node.nextSibling
	    if (node.className == "CodeMirror-linewidget")
	      { 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")
	    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"))
	  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 = true }
	  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, true, -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, true, 1) }
	  if (x < 0) { x = 0 }

	  var lineObj = getLine(doc, lineN)
	  for (;;) {
	    var found = coordsCharInner(cm, lineObj, lineN, x, y)
	    var merged = collapsedSpanAtEnd(lineObj)
	    var mergedPos = merged && merged.find(0, true)
	    if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0))
	      { lineN = lineNo(lineObj = mergedPos.to.line) }
	    else
	      { return found }
	  }
	}

	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 || y >= coords.bottom
	  }

	  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")
	    // 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])
	  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) {
	    left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft
	    width[cm.options.gutters[i]] = 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) { return null }
	  var coords = coordsChar(cm, x, y), line
	  if (forRect && coords.xRel == 1 && (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 }
	  }
	}

	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 () { return 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], height = (void 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
	    }
	    var diff = cur.line.height - height
	    if (height < 2) { height = textHeight(display) }
	    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]) } }
	    }
	  }
	}

	// 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)}
	}

	// 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)
	    return true
	  }
	  return false
	}

	// 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.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.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")
	  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: null,       // 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
	  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) }
	  }
	}

	// 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
	}

	// 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 () {
	    var this$1 = this;

	  for (var i = 0; i < this.events.length; i++)
	    { signal.apply(null, this$1.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 (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 }
	    }
	    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(cm) {
	  var width = cm.display.gutters.offsetWidth
	  cm.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"
	}

	// Rebuild the gutter elements, ensure the margin to the left of the
	// code matches their width.
	function updateGutters(cm) {
	  var gutters = cm.display.gutters, specs = cm.options.gutters
	  removeChildren(gutters)
	  var i = 0
	  for (; i < specs.length; ++i) {
	    var gutterClass = specs[i]
	    var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass))
	    if (gutterClass == "CodeMirror-linenumbers") {
	      cm.display.lineGutter = gElt
	      gElt.style.width = (cm.display.lineNumWidth || 1) + "px"
	    }
	  }
	  gutters.style.display = i ? "" : "none"
	  updateGutterSpace(cm)
	}

	// Make sure the gutters options contains the element
	// "CodeMirror-linenumbers" when the lineNumbers option is true.
	function setGuttersForLineNumbers(options) {
	  var found = indexOf(options.gutters, "CodeMirror-linenumbers")
	  if (found == -1 && options.lineNumbers) {
	    options.gutters = options.gutters.concat(["CodeMirror-linenumbers"])
	  } else if (found > -1 && !options.lineNumbers) {
	    options.gutters = options.gutters.slice(0)
	    options.gutters.splice(found, 1)
	  }
	}

	var wheelSamples = 0;
	var 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) {
	    var this$1 = this;

	  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$1.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 this$1 = this;

	  var out = []
	  for (var i = 0; i < this.ranges.length; i++)
	    { out[i] = new Range(copyPos(this$1.ranges[i].anchor), copyPos(this$1.ranges[i].head)) }
	  return new Selection(out, this.primIndex)
	};

	Selection.prototype.somethingSelected = function () {
	    var this$1 = this;

	  for (var i = 0; i < this.ranges.length; i++)
	    { if (!this$1.ranges[i].empty()) { return true } }
	  return false
	};

	Selection.prototype.contains = function (pos, end) {
	    var this$1 = this;

	  if (!end) { end = pos }
	  for (var i = 0; i < this.ranges.length; i++) {
	    var range = this$1.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(ranges, primIndex) {
	  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]
	    if (cmp(prev.to(), cur.from()) >= 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(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) == "+" && doc.cm && hist.lastModTime > time - doc.cm.options.historyEventDelay) ||
	        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(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(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) {
	      var this$1 = this;

	      this.ranges = []
	      for (var i = 0; i < ranges.length; i++)
	        { this$1.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(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 = 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(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
	    if ((sp.from == null || (m.inclusiveLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&
	        (sp.to == null || (m.inclusiveRight ? 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 ? m.inclusiveRight : m.inclusiveLeft)
	          { 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 ? m.inclusiveLeft : m.inclusiveRight)
	        { 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) { 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)
	}

	// 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) {
	  if (!to) { to = from }
	  if (cmp(to, from) < 0) { var assign;
	    (assign = [to, from], from = assign[0], to = assign[1], assign) }
	  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) {
	  var this$1 = this;

	  this.lines = lines
	  this.parent = null
	  var height = 0
	  for (var i = 0; i < lines.length; ++i) {
	    lines[i].parent = this$1
	    height += lines[i].height
	  }
	  this.height = height
	}

	LeafChunk.prototype = {
	  chunkSize: function chunkSize() { return this.lines.length },

	  // Remove the n lines at offset 'at'.
	  removeInner: function removeInner(at, n) {
	    var this$1 = this;

	    for (var i = at, e = at + n; i < e; ++i) {
	      var line = this$1.lines[i]
	      this$1.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 collapse(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 insertInner(at, lines, height) {
	    var this$1 = this;

	    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$1 }
	  },

	  // Used to iterate over a part of the tree.
	  iterN: function iterN(at, n, op) {
	    var this$1 = this;

	    for (var e = at + n; at < e; ++at)
	      { if (op(this$1.lines[at])) { return true } }
	  }
	}

	function BranchChunk(children) {
	  var this$1 = this;

	  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$1
	  }
	  this.size = size
	  this.height = height
	  this.parent = null
	}

	BranchChunk.prototype = {
	  chunkSize: function chunkSize() { return this.size },

	  removeInner: function removeInner(at, n) {
	    var this$1 = this;

	    this.size -= n
	    for (var i = 0; i < this.children.length; ++i) {
	      var child = this$1.children[i], sz = child.chunkSize()
	      if (at < sz) {
	        var rm = Math.min(n, sz - at), oldHeight = child.height
	        child.removeInner(at, rm)
	        this$1.height -= oldHeight - child.height
	        if (sz == rm) { this$1.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 collapse(lines) {
	    var this$1 = this;

	    for (var i = 0; i < this.children.length; ++i) { this$1.children[i].collapse(lines) }
	  },

	  insertInner: function insertInner(at, lines, height) {
	    var this$1 = this;

	    this.size += lines.length
	    this.height += height
	    for (var i = 0; i < this.children.length; ++i) {
	      var child = this$1.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$1.children.splice(++i, 0, leaf)
	            leaf.parent = this$1
	          }
	          child.lines = child.lines.slice(0, remaining)
	          this$1.maybeSpill()
	        }
	        break
	      }
	      at -= sz
	    }
	  },

	  // When a node has grown, check whether it should be split.
	  maybeSpill: function maybeSpill() {
	    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 iterN(at, n, op) {
	    var this$1 = this;

	    for (var i = 0; i < this.children.length; ++i) {
	      var child = this$1.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) {
	  var this$1 = this;

	  if (options) { for (var opt in options) { if (options.hasOwnProperty(opt))
	    { this$1[opt] = options[opt] } } }
	  this.doc = doc
	  this.node = node
	};

	LineWidget.prototype.clear = function () {
	    var this$1 = this;

	  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$1) { 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 }
	  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 () {
	    var this$1 = this;

	  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$1.lines[i]
	    var span = getMarkedSpanFor(line.markedSpans, this$1)
	    if (cm && !this$1.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$1.collapsed && !lineIsHidden(this$1.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$1.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) {
	    var this$1 = this;

	  if (side == null && this.type == "bookmark") { side = 1 }
	  var from, to
	  for (var i = 0; i < this.lines.length; ++i) {
	    var line = this$1.lines[i]
	    var span = getMarkedSpanFor(line.markedSpans, this$1)
	    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.title || marker.startStyle || marker.endStyle || marker.css)
	      { 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) {
	  var this$1 = this;

	  this.markers = markers
	  this.primary = primary
	  for (var i = 0; i < markers.length; ++i)
	    { markers[i].parent = this$1 }
	};

	SharedTextMarker.prototype.clear = function () {
	    var this$1 = this;

	  if (this.explicitlyCleared) { return }
	  this.explicitlyCleared = true
	  for (var i = 0; i < this.markers.length; ++i)
	    { this$1.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) {
	    var this$1 = this;

	    if (!ranges.length) { return }
	    var out = []
	    for (var i = 0; i < ranges.length; i++)
	      { out[i] = new Range(clipPos(this$1, ranges[i].anchor),
	                         clipPos(this$1, ranges[i].head)) }
	    if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex) }
	    setSelection(this, normalizeSelection(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(ranges, ranges.length - 1), options)
	  }),

	  getSelection: function(lineSep) {
	    var this$1 = this;

	    var ranges = this.sel.ranges, lines
	    for (var i = 0; i < ranges.length; i++) {
	      var sel = getBetween(this$1, 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 this$1 = this;

	    var parts = [], ranges = this.sel.ranges
	    for (var i = 0; i < ranges.length; i++) {
	      var sel = getBetween(this$1, ranges[i].from(), ranges[i].to())
	      if (lineSep !== false) { sel = sel.join(lineSep || this$1.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 this$1 = this;

	    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$1.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$1, 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() {this.history = new History(this.history.maxGeneration)},

	  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) {
	    var this$1 = this;

	    if (other instanceof CodeMirror) { other = other.doc }
	    if (this.linked) { for (var i = 0; i < this.linked.length; ++i) {
	      var link = this$1.linked[i]
	      if (link.doc != other) { continue }
	      this$1.linked.splice(i, 1)
	      other.unlinkDoc(this$1)
	      detachSharedMarkers(findSharedMarkers(this$1))
	      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 loadFile = function (file, i) {
	      if (cm.options.allowDropFileTypes &&
	          indexOf(cm.options.allowDropFileTypes, file.type) == -1)
	        { return }

	      var reader = new FileReader
	      reader.onload = operation(cm, function () {
	        var content = reader.result
	        if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) { content = "" }
	        text[i] = content
	        if (++read == n) {
	          pos = clipPos(cm.doc, pos)
	          var change = {from: pos, to: pos,
	                        text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())),
	                        origin: "paste"}
	          makeChange(cm.doc, change)
	          setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change)))
	        }
	      })
	      reader.readAsText(file)
	    }
	    for (var i = 0; i < n; ++i) { loadFile(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){}
	  }
	}

	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")
	  for (var i = 0; i < byClass.length; i++) {
	    var cm = byClass[i].CodeMirror
	    if (cm) { f(cm) }
	  }
	}

	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
	  if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth)
	    { return }
	  // 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: "/", 127: "Delete", 145: "ScrollLock",
	  173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
	  221: "]", 222: "'", 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 != "Cmd") { 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) {
	    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, "char"); },
	  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(0, 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
	  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") }
	  }

	  // 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 (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;
	var 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) { onContextMenu(cm, 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(document, "mouseup", dragEnd)
	    off(document, "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 || ie && ie_version == 9)
	        { setTimeout(function () {document.body.focus(); 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(document, "mouseup", dragEnd)
	  on(document, "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(ranges.concat([ourRange]), ourIndex),
	                 {scroll: false, origin: "*mouse"})
	  } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == "char" && !behavior.extend) {
	    setSelection(doc, normalizeSelection(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(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(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
	    e_preventDefault(e)
	    display.input.focus()
	    off(document, "mousemove", move)
	    off(document, "mouseup", up)
	    doc.history.lastSelOrigin = null
	  }

	  var move = operation(cm, function (e) {
	    if (!e_button(e)) { done(e) }
	    else { extend(e) }
	  })
	  var up = operation(cm, done)
	  cm.state.selectingText = up
	  on(document, "mousemove", move)
	  on(document, "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) { 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.options.gutters.length; ++i) {
	    var g = display.gutters.childNodes[i]
	    if (g && g.getBoundingClientRect().right >= mX) {
	      var line = lineAtHeight(cm.doc, mY)
	      var gutter = cm.options.gutters[i]
	      signal(cm, type, cm, line, gutter, 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 }
	  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-\u200f\u2028\u2029\ufeff]/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("rtlMoveVisually", !windows)
	  option("wholeLineUpdateBefore", true)

	  option("theme", "default", function (cm) {
	    themeChanged(cm)
	    guttersChanged(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) {
	    setGuttersForLineNumbers(cm.options)
	    guttersChanged(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) {
	    setGuttersForLineNumbers(cm.options)
	    guttersChanged(cm)
	  }, true)
	  option("firstLineNumber", 1, guttersChanged, true)
	  option("lineNumberFormatter", function (integer) { return integer; }, guttersChanged, true)
	  option("showCursorWhenSelecting", false, updateSelection, true)

	  option("resetSelectionOnContextMenu", true)
	  option("lineWiseCopyCut", true)
	  option("pasteLinesPerSelection", true)

	  option("readOnly", false, function (cm, val) {
	    if (val == "nocursor") {
	      onBlur(cm)
	      cm.display.input.blur()
	    }
	    cm.display.input.readOnlyChanged(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)
	}

	function guttersChanged(cm) {
	  updateGutters(cm)
	  regChange(cm)
	  alignHorizontally(cm)
	}

	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)
	  setGuttersForLineNumbers(options)

	  var doc = options.value
	  if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction) }
	  this.doc = doc

	  var input = new CodeMirror.inputStyles[options.inputStyle](this)
	  var display = this.display = new Display(place, doc, input)
	  display.wrapper.CodeMirror = this
	  updateGutters(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: false, cutIncoming: false, // 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(bind(onFocus, this), 20) }
	  else
	    { onBlur(this) }

	  for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt))
	    { optionHandlers[opt](this$1, options[opt], Init) } }
	  maybeUpdateLineNumberWidth(this)
	  if (options.finishInit) { options.finishInit(this) }
	  for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this$1) }
	  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.
	  if (!captureRightClick) { on(d.scroller, "contextmenu", function (e) { return 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 paste = cm.state.pasteIncoming || origin == "paste"
	  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
	  // 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 (lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted)
	        { from = to = Pos(from.line, 0) }
	    }
	    updateInput = cm.curOp.updateInput
	    var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines,
	                       origin: origin || (paste ? "paste" : cm.state.cutIncoming ? "cut" : "+input")}
	    makeChange(cm.doc, changeEvent)
	    signalLater(cm, "inputRead", cm, changeEvent)
	  }
	  if (inserted && !paste)
	    { triggerElectric(cm, inserted) }

	  ensureCursorVisible(cm)
	  cm.curOp.updateInput = updateInput
	  cm.curOp.typing = true
	  cm.state.pasteIncoming = cm.state.cutIncoming = false
	}

	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) {
	  field.setAttribute("autocorrect", "off")
	  field.setAttribute("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 this$1 = this;

	      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$1.state.modeGen++
	          regChange(this$1)
	          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 this$1 = this;

	      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$1.lastLine(), to.line - (to.ch ? 0 : 1)) + 1
	          for (var j = start; j < end; ++j)
	            { indentLine(this$1, j, how) }
	          var newRanges = this$1.doc.sel.ranges
	          if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)
	            { replaceOneSelection(this$1.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll) }
	        } else if (range.head.line > end) {
	          indentLine(this$1, range.head.line, how, true)
	          end = range.head.line
	          if (i == this$1.doc.sel.primIndex) { ensureCursorVisible(this$1) }
	        }
	      }
	    }),

	    // 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 this$1 = this;

	      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$1) && 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 this$1 = this;

	      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$1.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 this$1 = this;

	      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$1, cur, "div")
	        if (x == null) { x = coords.left }
	        else { coords.left = x }
	        cur = findPosV(this$1, 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)
	      if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)
	        { estimateLineHeights(this) }
	      signal(this, "refresh", this)
	    }),

	    swapDoc: methodOp(function(doc) {
	      var old = this.doc
	      old.cm = null
	      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
	    }),

	    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 "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)
	  function findNextLine() {
	    var l = pos.line + dir
	    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 (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, dir) }
	      else
	        { return false }
	    } else {
	      pos = next
	    }
	    return true
	  }

	  if (unit == "char") {
	    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)

	  on(div, "paste", function (e) {
	    if (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 (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.prepareSelection = function () {
	  var result = prepareSelection(this.cm, false)
	  result.focus = this.cm.state.focused
	  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.showPrimarySelection = function () {
	  var sel = window.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 = window.getSelection()
	  this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset
	  this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset
	};

	ContentEditableInput.prototype.selectionInEditor = function () {
	  var sel = window.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())
	      { 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 = window.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 = window.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.options.gutters.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) { 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()
	  function recognizeMarker(id) { return function (marker) { return marker.id == id; } }
	  function close() {
	    if (closing) {
	      text += lineSep
	      closing = false
	    }
	  }
	  function addText(str) {
	    if (str) {
	      close()
	      text += str
	    }
	  }
	  function walk(node) {
	    if (node.nodeType == 1) {
	      var cmText = node.getAttribute("cm-text")
	      if (cmText != null) {
	        addText(cmText || node.textContent.replace(/\u200b/g, ""))
	        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)$/i.test(node.nodeName)
	      if (isBlock) { close() }
	      for (var i = 0; i < node.childNodes.length; i++)
	        { walk(node.childNodes[i]) }
	      if (isBlock) { closing = true }
	    } else if (node.nodeType == 3) {
	      addText(node.nodeValue)
	    }
	  }
	  for (;;) {
	    walk(from)
	    if (from == to) { break }
	    from = from.nextSibling
	  }
	  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

	  // Wraps and hides input textarea
	  var div = this.wrapper = hiddenTextarea()
	  // The semihidden textarea that is focused when the editor is
	  // focused, and receives input.
	  var te = this.textarea = div.firstChild
	  display.wrapper.insertBefore(div, 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 = true
	    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 = true }
	  }
	  on(te, "cut", prepareCopyCut)
	  on(te, "copy", prepareCopyCut)

	  on(display.scroller, "paste", function (e) {
	    if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return }
	    cm.state.pasteIncoming = true
	    input.focus()
	  })

	  // 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.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
	  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
	  input.wrapper.style.cssText = "position: absolute"
	  var wrapperBox = input.wrapper.getBoundingClientRect()
	  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 = true
	  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() {
	    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"
	};

	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 (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)

	// INPUT HANDLING

	CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput}

	// MODE DEFINITION AND QUERYING

	// 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.35.0"

	return CodeMirror;

	})));

/***/ }),
/* 3 */
/***/ (function(module, exports, __webpack_require__) {

	// CodeMirror, copyright (c) by Marijn Haverbeke and others
	// Distributed under an MIT license: http://codemirror.net/LICENSE

	(function(mod) {
	  if (true) // CommonJS
	    mod(__webpack_require__(2))
	  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)
	  })
	});


/***/ }),
/* 4 */
/***/ (function(module, exports, __webpack_require__) {

	// CodeMirror, copyright (c) by Marijn Haverbeke and others
	// Distributed under an MIT license: http://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 (true) // CommonJS
	    mod(__webpack_require__(2), __webpack_require__(3), __webpack_require__(1));
	  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(/\\(.)/g, function(_, ch) {
	      if (ch == "n") return "\n"
	      if (ch == "r") return "\r"
	      return ch
	    })
	  }

	  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);};
	});


/***/ }),
/* 5 */
/***/ (function(module, exports, __webpack_require__) {

	// CodeMirror, copyright (c) by Marijn Haverbeke and others
	// Distributed under an MIT license: http://codemirror.net/LICENSE

	(function(mod) {
	  if (true) // CommonJS
	    mod(__webpack_require__(2));
	  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 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)

	    // 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 && matching[line.text.charAt(pos)]) ||
	        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 = config && config.bracketRegex ? config.bracketRegex : /[(){}[\]]/;
	    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.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);
	  });
	});


/***/ }),
/* 6 */
/***/ (function(module, exports, __webpack_require__) {

	// CodeMirror, copyright (c) by Marijn Haverbeke and others
	// Distributed under an MIT license: http://codemirror.net/LICENSE

	(function(mod) {
	  if (true) // CommonJS
	    mod(__webpack_require__(2));
	  else if (typeof define == "function" && define.amd) // AMD
	    define(["../../lib/codemirror"], mod);
	  else // Plain browser env
	    mod(CodeMirror);
	})(function(CodeMirror) {
	  var defaults = {
	    pairs: "()[]{}''\"\"",
	    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 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 && (cm.getLine(cur.line).length == cur.ch ||
	                             isClosingBracket(next, pairs) ||
	                             /\s/.test(next))) {
	        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 isClosingBracket(ch, pairs) {
	    var pos = pairs.lastIndexOf(ch);
	    return pos > -1 && pos % 2 == 1;
	  }

	  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)))
	  }
	});


/***/ }),
/* 7 */
/***/ (function(module, exports, __webpack_require__) {

	// CodeMirror, copyright (c) by Marijn Haverbeke and others
	// Distributed under an MIT license: http://codemirror.net/LICENSE

	(function(mod) {
	  if (true) // CommonJS
	    mod(__webpack_require__(2));
	  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;
	  });
	});


/***/ }),
/* 8 */
/***/ (function(module, exports, __webpack_require__) {

	// CodeMirror, copyright (c) by Marijn Haverbeke and others
	// Distributed under an MIT license: http://codemirror.net/LICENSE

	(function(mod) {
	  if (true) // CommonJS
	    mod(__webpack_require__(2));
	  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+(?:[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.eat(/x/i)) {
	      stream.eatWhile(/[\da-f]/i);
	      return ret("number", "number");
	    } else if (ch == "0" && stream.eat(/o/i)) {
	      stream.eatWhile(/[0-7]/i);
	      return ret("number", "number");
	    } else if (ch == "0" && stream.eat(/b/i)) {
	      stream.eatWhile(/[01]/i);
	      return ret("number", "number");
	    } else if (/\d/.test(ch)) {
	      stream.match(/^\d*(?:\.\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(([gimyu])(?![gimyu]*\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.skipToEnd();
	      return ret("error", "error");
	    } 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)
	        }
	      }
	      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)) {
	        return;
	      } 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 register(varname) {
	    function inList(list) {
	      for (var v = list; v; v = v.next)
	        if (v.name == varname) return true;
	      return false;
	    }
	    var state = cx.state;
	    cx.marked = "def";
	    if (state.context) {
	      if (inList(state.localVars)) return;
	      state.localVars = {name: varname, next: state.localVars};
	    } else {
	      if (inList(state.globalVars)) return;
	      if (parserConfig.globalVars)
	        state.globalVars = {name: varname, next: state.globalVars};
	    }
	  }

	  function isModifier(name) {
	    return name == "public" || name == "private" || name == "protected" || name == "abstract" || name == "readonly"
	  }

	  // Combinators

	  var defaultVars = {name: "this", next: {name: "arguments"}};
	  function pushcontext() {
	    cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
	    cx.state.localVars = defaultVars;
	  }
	  function popcontext() {
	    cx.state.localVars = cx.state.context.vars;
	    cx.state.context = cx.state.context.prev;
	  }
	  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 == ";") return pass();
	      else return cont(exp);
	    };
	    return exp;
	  }

	  function statement(type, value) {
	    if (type == "var") return cont(pushlex("vardef", value.length), 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("}"), block, poplex);
	    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"), 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(typeexpr, 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, block, poplex)
	      } else {
	        return cont(pushlex("stat"), maybelabel);
	      }
	    }
	    if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"),
	                                      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 == "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 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(")"), expression, 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(expression);
	    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();
	      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 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 (type == "variable" || value == "void") {
	      if (value == "keyof") {
	        cx.marked = "keyword"
	        return cont(typeexpr)
	      } else {
	        cx.marked = "type"
	        return cont(afterType)
	      }
	    }
	    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)
	  }
	  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 == "?") {
	      return cont(typeprop)
	    } else if (type == ":") {
	      return cont(typeexpr)
	    } else if (type == "[") {
	      return cont(expression, maybetype, expect("]"), typeprop)
	    }
	  }
	  function typearg(type) {
	    if (type == "variable") return cont(typearg)
	    else if (type == ":") return cont(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(expect("]"), afterType)
	    if (value == "extends" || value == "implements") { cx.marked = "keyword"; return cont(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(pattern, "]");
	    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();
	    return cont(expect(":"), 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, expect(")"), poplex);
	  }
	  function forspec1(type) {
	    if (type == "var") return cont(vardef, expect(";"), forspec2);
	    if (type == ";") return cont(forspec2);
	    if (type == "variable") return cont(formaybeinof);
	    return pass(expression, expect(";"), forspec2);
	  }
	  function formaybeinof(_type, value) {
	    if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
	    return cont(maybeoperatorComma, forspec2);
	  }
	  function forspec2(type, value) {
	    if (type == ";") return cont(forspec3);
	    if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
	    return pass(expression, expect(";"), forspec3);
	  }
	  function forspec3(type) {
	    if (type != ")") cont(expression);
	  }
	  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 funarg(type, value) {
	    if (value == "@") cont(expression, funarg)
	    if (type == "spread") return cont(funarg);
	    if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(funarg); }
	    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(isTS ? classfield : functiondef, classBody);
	    }
	    if (type == "[")
	      return cont(expression, maybetype, expect("]"), isTS ? classfield : functiondef, classBody)
	    if (value == "*") {
	      cx.marked = "keyword";
	      return cont(classBody);
	    }
	    if (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)
	    return pass(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 && {vars: parserConfig.localVars},
	        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) 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 + 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 });

	});


/***/ }),
/* 9 */
/***/ (function(module, exports, __webpack_require__) {

	// CodeMirror, copyright (c) by Marijn Haverbeke and others
	// Distributed under an MIT license: http://codemirror.net/LICENSE

	(function(mod) {
	  if (true) // CommonJS
	    mod(__webpack_require__(2));
	  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
	    }
	  };
	});

	CodeMirror.defineMIME("text/xml", "xml");
	CodeMirror.defineMIME("application/xml", "xml");
	if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
	  CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});

	});


/***/ }),
/* 10 */
/***/ (function(module, exports, __webpack_require__) {

	// CodeMirror, copyright (c) by Marijn Haverbeke and others
	// Distributed under an MIT license: http://codemirror.net/LICENSE

	(function(mod) {
	  if (true) // CommonJS
	    mod(__webpack_require__(2));
	  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 (((ch == "u" || ch == "U") && stream.match(/rl(-prefix)?\(/i)) ||
	               ((ch == "d" || ch == "D") && stream.match("omain(", true, true)) ||
	               ((ch == "r" || ch == "R") && stream.match("egexp(", true, true))) {
	      stream.backUp(1);
	      state.tokenize = tokenParenthesized;
	      return ret("property", "word");
	    } 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", "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", "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"
	  });

	});


/***/ }),
/* 11 */
/***/ (function(module, exports, __webpack_require__) {

	// CodeMirror, copyright (c) by Marijn Haverbeke and others
	// Distributed under an MIT license: http://codemirror.net/LICENSE

	(function(mod) {
	  if (true) // CommonJS
	    mod(__webpack_require__(2), __webpack_require__(9), __webpack_require__(8), __webpack_require__(10));
	  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);
	        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");
	});


/***/ }),
/* 12 */
/***/ (function(module, exports, __webpack_require__) {

	// CodeMirror, copyright (c) by Marijn Haverbeke and others
	// Distributed under an MIT license: http://codemirror.net/LICENSE

	(function(mod) {
	  if (true) // CommonJS
	    mod(__webpack_require__(2), __webpack_require__(9), __webpack_require__(8))
	  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}})
	});


/***/ }),
/* 13 */
/***/ (function(module, exports, __webpack_require__) {

	// CodeMirror, copyright (c) by Marijn Haverbeke and others
	// Distributed under an MIT license: http://codemirror.net/LICENSE

	/**
	 * Link to the project's GitHub page:
	 * https://github.com/pickhardt/coffeescript-codemirror-mode
	 */
	(function(mod) {
	  if (true) // CommonJS
	    mod(__webpack_require__(2));
	  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");

	});


/***/ }),
/* 14 */
/***/ (function(module, exports, __webpack_require__) {

	// CodeMirror, copyright (c) by Marijn Haverbeke and others
	// Distributed under an MIT license: http://codemirror.net/LICENSE

	(function(mod) {
	  if (true) // CommonJS
	    mod(__webpack_require__(2));
	  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");
	});


/***/ }),
/* 15 */
/***/ (function(module, exports, __webpack_require__) {

	// CodeMirror, copyright (c) by Marijn Haverbeke and others
	// Distributed under an MIT license: http://codemirror.net/LICENSE

	(function(mod) {
	  if (true) // CommonJS
	    mod(__webpack_require__(2));
	  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]/;

	  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)) {
	      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);
	      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);
	        if (typeof hook == "number") return hook
	      }
	      var closing = firstChar == ctx.type;
	      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";
	  var cTypes = "int long char short double float unsigned signed void size_t ptrdiff_t";

	  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;
	  }

	  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: words(cTypes + " bool _Complex _Bool float_t double_t intptr_t intmax_t " +
	                 "int8_t int16_t int32_t int64_t uintptr_t uintmax_t uint8_t uint16_t " +
	                 "uint32_t uint64_t"),
	    blockKeywords: words("case do else for if switch while struct"),
	    defKeywords: words("struct"),
	    typeFirstDefinitions: true,
	    atoms: words("NULL true false"),
	    hooks: {"#": cppHook, "*": pointerHook},
	    modeProps: {fold: ["brace", "include"]}
	  });

	  def(["text/x-c++src", "text/x-c++hdr"], {
	    name: "clike",
	    keywords: words(cKeywords + " asm dynamic_cast namespace reinterpret_cast try explicit new " +
	                    "static_cast typeid catch operator template typename class friend private " +
	                    "this using const_cast inline public throw virtual delete mutable protected " +
	                    "alignas alignof constexpr decltype nullptr noexcept thread_local final " +
	                    "static_assert override"),
	    types: words(cTypes + " bool wchar_t"),
	    blockKeywords: words("catch class do else finally for if struct switch try while"),
	    defKeywords: words("class namespace struct enum union"),
	    typeFirstDefinitions: true,
	    atoms: words("true false NULL"),
	    dontIndentStatements: /^template$/,
	    isIdentifierChar: /[\w\$_~\xa1-\uffff]/,
	    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 float 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: {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, state) {
	        state.tokenize = tokenKotlinString(stream.match('""'));
	        return state.tokenize(stream, state);
	      }
	    },
	    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: words(cTypes),
	    blockKeywords: words("case do else for if switch while struct"),
	    atoms: words("null true false"),
	    hooks: {"#": cppHook},
	    modeProps: {fold: ["brace", "include"]}
	  });

	  def("text/x-objectivec", {
	    name: "clike",
	    keywords: words(cKeywords + "inline restrict _Bool _Complex _Imaginary BOOL Class bycopy byref id IMP in " +
	                    "inout nil oneway out Protocol SEL self super atomic nonatomic retain copy readwrite readonly"),
	    types: words(cTypes),
	    atoms: words("YES NO NULL NILL ON OFF true false"),
	    hooks: {
	      "@": function(stream) {
	        stream.eatWhile(/[\w\$]/);
	        return "keyword";
	      },
	      "#": cppHook,
	      indent: function(_state, ctx, textAfter) {
	        if (ctx.type == "statement" && /^@\w/.test(textAfter)) return ctx.indented
	      }
	    },
	    modeProps: {fold: "brace"}
	  });

	  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: words(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: '"'}
	    }
	  });

	});


/***/ }),
/* 16 */
/***/ (function(module, exports, __webpack_require__) {

	var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/* This Source Code Form is subject to the terms of the Mozilla Public
	 * License, v. 2.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 (true) {
	    !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(2)], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
	  } else if (typeof exports !== 'undefined') {
	    factory(require("../../lib/codemirror"));
	  } 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");

	}));


/***/ }),
/* 17 */
/***/ (function(module, exports, __webpack_require__) {

	// CodeMirror, copyright (c) by Marijn Haverbeke and others
	// Distributed under an MIT license: http://codemirror.net/LICENSE

	(function(mod) {
	  if (true) // CommonJS
	    mod(__webpack_require__(2));
	  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);
	  }
	});


/***/ }),
/* 18 */
/***/ (function(module, exports, __webpack_require__) {

	// CodeMirror, copyright (c) by Marijn Haverbeke and others
	// Distributed under an MIT license: http://codemirror.net/LICENSE

	(function(mod) {
	  if (true) // CommonJS
	    mod(__webpack_require__(2));
	  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"
	      });
	  });
	});


/***/ }),
/* 19 */
/***/ (function(module, exports, __webpack_require__) {

	// CodeMirror, copyright (c) by Marijn Haverbeke and others
	// Distributed under an MIT license: http://codemirror.net/LICENSE

	(function(mod) {
	  if (true) // CommonJS
	    mod(__webpack_require__(2));
	  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-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-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("-");
	});


/***/ }),
/* 20 */
/***/ (function(module, exports, __webpack_require__) {

	// CodeMirror, copyright (c) by Marijn Haverbeke and others
	// Distributed under an MIT license: http://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 (true) // CommonJS
	    mod(__webpack_require__(2), __webpack_require__(3), __webpack_require__(1), __webpack_require__(5));
	  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: '<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: '<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: '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' },
	    // 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' }
	  ];

	  /**
	   * 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 fatCursorMarks(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}))
	          }
	        }
	      }
	      return result
	    }

	    function updateFatCursorMark(cm) {
	      var marks = cm.state.fatCursorMarks
	      if (marks) for (var i = 0; i < marks.length; i++) marks[i].clear()
	      cm.state.fatCursorMarks = fatCursorMarks(cm)
	    }

	    function enableFatCursorMark(cm) {
	      cm.state.fatCursorMarks = fatCursorMarks(cm)
	      cm.on("cursorActivity", updateFatCursorMark)
	    }

	    function disableFatCursorMark(cm) {
	      var marks = cm.state.fatCursorMarks
	      if (marks) for (var i = 0; i < marks.length; i++) marks[i].clear()
	      cm.state.fatCursorMarks = null
	      cm.off("cursorActivity", updateFatCursorMark)
	    }

	    // 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 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);
	      },
	      // 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 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;
	      }
	    };

	    /**
	     * 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);
	        }
	        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);
	      },
	      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) {
	        var cur = head;
	        vim.lastHPos = Infinity;
	        var retval= Pos(cur.line + motionArgs.repeat - 1, Infinity);
	        var end=cm.clipPos(retval);
	        end.ch--;
	        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) {
	          var matched = cm.findMatchingBracket(Pos(line, ch));
	          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.

	        // TODO: adding <> >< to this map doesn't work, presumably because
	        // they're operators
	        var mirroredPairs = {'(': ')', ')': '(',
	                             '{': '}', '}': '{',
	                             '[': ']', ']': '['};
	        var selfPaired = {'\'': 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;
	        vimGlobalState.macroModeState.lastInsertModeChanges.inVisualBlock = vim.visualBlock;
	        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);
	      },
	      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;
	        }
	        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.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.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 (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 {
	          if (blockwise) {
	            text = text.split('\n');
	            for (var i = 0; i < text.length; i++) {
	              text[i] = (text[i] == '') ? ' ' : text[i];
	            }
	          }
	          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 getOffset(anchor, head) {
	      return {
	        line: head.line - anchor.line,
	        ch: head.line - anchor.line
	      };
	    }
	    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 };
	    }

	    // 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, null, {'bracketRegex': bracketRegexp});
	      end = cm.scanForBracket(Pos(cur.line, cur.ch + offset), 1, null, {'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
	      };
	    }
	    function highlightSearchMatches(cm, query) {
	      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);
	      }
	    }
	    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];
	          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);
	            } 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', '');
	            }
	            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;
	      // In case of visual block, the insertModeChanges are not saved as a
	      // single word, so we convert them to a single word
	      // so as to update the ". register as expected in real vim.
	      var text = [];
	      if (!isPlaying) {
	        var selLength = lastChange.inVisualBlock && vim.lastSelection ?
	            vim.lastSelection.visualBlock.height : 1;
	        var changes = lastChange.changes;
	        var text = [];
	        var i = 0;
	        // In case of multiple selections in blockwise visual,
	        // the inserted text, for example: 'f<Backspace>oo', is stored as
	        // 'f', 'f', InsertModeKey 'o', 'o', 'o', 'o'. (if you have a block with 2 lines).
	        // We push the contents of the changes array as per the following:
	        // 1. In case of InsertModeKey, just increment by 1.
	        // 2. In case of a character, jump by selLength (2 in the example).
	        while (i < changes.length) {
	          // This loop will convert 'ff<bs>oooo' to 'f<bs>oo'.
	          text.push(changes[i]);
	          if (changes[i] instanceof InsertModeKey) {
	             i++;
	          } else {
	             i+= selLength;
	          }
	        }
	        lastChange.changes = text;
	        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 (changeObj.origin == '+input' || changeObj.origin == 'paste'
	              || changeObj.origin === undefined /* only in testing */) {
	            var text = changeObj.text.join('\n');
	            if (lastChange.maybeReset) {
	              lastChange.changes = [];
	              lastChange.maybeReset = false;
	            }
	            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 inVisualBlock = vimGlobalState.macroModeState.lastInsertModeChanges.inVisualBlock;
	      if (inVisualBlock) {
	        // Set up block selection again for repeating the changes.
	        var vim = cm.state.vim;
	        var lastSel = vim.lastSelection;
	        var offset = getOffset(lastSel.anchor, lastSel.head);
	        selectForInsert(cm, head, offset.line + 1);
	        repeat = cm.listSelections().length;
	        cm.setCursor(head);
	      }
	      for (var i = 0; i < repeat; i++) {
	        if (inVisualBlock) {
	          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 (inVisualBlock) {
	        cm.setCursor(offsetCursor(head, 0, 1));
	      }
	    }

	    resetVimGlobalState();
	    return vimApi;
	  };
	  // Initialize Vim and make it available as an API.
	  CodeMirror.Vim = Vim();
	});


/***/ }),
/* 21 */
/***/ (function(module, exports, __webpack_require__) {

	// CodeMirror, copyright (c) by Marijn Haverbeke and others
	// Distributed under an MIT license: http://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 (true) // CommonJS
	    mod(__webpack_require__(2), __webpack_require__(3), __webpack_require__(5));
	  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",
	    "F9": "sortLines",
	    "Cmd-F9": "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;
	});


/***/ }),
/* 22 */
/***/ (function(module, exports, __webpack_require__) {

	// CodeMirror, copyright (c) by Marijn Haverbeke and others
	// Distributed under an MIT license: http://codemirror.net/LICENSE

	(function(mod) {
	  if (true) // CommonJS
	    mod(__webpack_require__(2));
	  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);
	  });
	});


/***/ }),
/* 23 */
/***/ (function(module, exports, __webpack_require__) {

	// CodeMirror, copyright (c) by Marijn Haverbeke and others
	// Distributed under an MIT license: http://codemirror.net/LICENSE

	(function(mod) {
	  if (true) // CommonJS
	    mod(__webpack_require__(2));
	  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 && endCh == startCh) 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))};
	});

	});


/***/ }),
/* 24 */
/***/ (function(module, exports, __webpack_require__) {

	// CodeMirror, copyright (c) by Marijn Haverbeke and others
	// Distributed under an MIT license: http://codemirror.net/LICENSE

	(function(mod) {
	  if (true) // CommonJS
	    mod(__webpack_require__(2));
	  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)};
	});

	});


/***/ }),
/* 25 */
/***/ (function(module, exports, __webpack_require__) {

	// CodeMirror, copyright (c) by Marijn Haverbeke and others
	// Distributed under an MIT license: http://codemirror.net/LICENSE

	(function(mod) {
	  if (true) // CommonJS
	    mod(__webpack_require__(2));
	  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), end;
	      if (!openTag || !(end = toTagEnd(iter)) || iter.line != start.line) return;
	      if (!openTag[1] && end != "selfClose") {
	        var startPos = Pos(iter.line, iter.ch);
	        var endPos = findMatchingClose(iter, openTag[2]);
	        return endPos && {from: startPos, to: endPos.from};
	      }
	    }
	  });
	  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);
	  };
	});


/***/ }),
/* 26 */
/***/ (function(module, exports, __webpack_require__) {

	// CodeMirror, copyright (c) by Marijn Haverbeke and others
	// Distributed under an MIT license: http://codemirror.net/LICENSE

	(function(mod) {
	  if (true) // CommonJS
	    mod(__webpack_require__(2), __webpack_require__(22));
	  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("change", 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("change", 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 && marks[i].find().from.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.rangeFinder);
	  }

	  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);
	  }
	});


/***/ })
/******/ ]);PK
!<���6^!^!Bchrome/devtools/content/sourceeditor/codemirror/lib/codemirror.css/* 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 {
  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: -20px;
  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 */
  /* 30px is the magic margin used to hide the element's real scrollbars */
  /* See overflow: hidden in .CodeMirror */
  margin-bottom: -30px; margin-right: -30px;
  padding-bottom: 30px;
  height: 100%;
  outline: none; /* Prevent dragging from highlighting the element */
  position: relative;
}
.CodeMirror-sizer {
  position: relative;
  border-right: 30px 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;
}
.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: -30px;
}
.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 {
  /* 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 {
  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; }
PK
!<{�X��;chrome/devtools/content/sourceeditor/codemirror/mozilla.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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);
}

.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;
}

/* This is to avoid the fake horizontal scrollbar div of codemirror to go 0
height when floating scrollbars are active. Make sure that this value is equal
to the maximum of `min-height` specific to the `scrollbar[orient="horizontal"]`
selector in floating-scrollbar-light.css across all platforms. */
.CodeMirror-hscrollbar {
  min-height: 10px;
}

/* This is to avoid the fake vertical scrollbar div of codemirror to go 0
width when floating scrollbars are active. Make sure that this value is equal
to the maximum of `min-width` specific to the `scrollbar[orient="vertical"]`
selector in floating-scrollbar-light.css across all platforms. */
.CodeMirror-vscrollbar {
  min-width: 10px;
}

.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;
}

.cm-highlight {
  position: relative;
}

.cm-highlight:before {
  position: absolute;
  border-top-style: solid;
  border-bottom-style: solid;
  border-top-color: var(--theme-comment-alt);
  border-bottom-color: var(--theme-comment-alt);
  border-top-width: 1px;
  border-bottom-width: 1px;
  top: -1px;
  bottom: 0;
  left: 0;
  right: 0;
  content: "";
  margin-bottom: -1px;
}

.cm-highlight-full:before {
  border: 1px solid var(--theme-comment-alt);
}

.cm-highlight-start:before {
  border-left-width: 1px;
  border-left-style: solid;
  border-left-color: var(--theme-comment-alt);
  margin: 0 0 -1px -1px;
  border-top-left-radius: 2px;
  border-bottom-left-radius: 2px;
}

.cm-highlight-end:before {
  border-right-width: 1px;
  border-right-style: solid;
  border-right-color: var(--theme-comment-alt);
  margin: 0 -1px -1px 0;
  border-top-right-radius: 2px;
  border-bottom-right-radius: 2px;
}

/* CodeMirror dialogs styling */

.CodeMirror-dialog {
  padding: 4px 3px;
}

.CodeMirror-dialog,
.CodeMirror-dialog input {
  font: message-box;
}

/* Fold addon */

.CodeMirror-foldmarker {
  color: blue;
  text-shadow: #b9f 1px 1px 2px, #b9f -1px -1px 2px, #b9f 1px -1px 2px, #b9f -1px 1px 2px;
  font-family: sans-serif;
  line-height: .3;
  cursor: pointer;
}

.CodeMirror-foldgutter {
  width: 16px; /* Same as breakpoints gutter above */
}

.CodeMirror-foldgutter-open,
.CodeMirror-foldgutter-folded {
  color: #555;
  cursor: pointer;
}

.CodeMirror-foldgutter-open:after {
  font-size: 120%;
  content: "\25BE";
}

.CodeMirror-foldgutter-folded:after {
  font-size: 120%;
  content: "\25B8";
}

.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;
}
PK
!<�6���@chrome/devtools/content/sourceeditor/codemirror/old-debugger.css:root {
  --breakpoint-background: url("chrome://devtools/skin/images/breakpoint.svg#light");
  --breakpoint-conditional-background: url("chrome://devtools/skin/images/breakpoint.svg#light-conditional");
}

.theme-dark:root {
  --breakpoint-background: url("chrome://devtools/skin/images/breakpoint.svg#dark");
  --breakpoint-conditional-background: url("chrome://devtools/skin/images/breakpoint.svg#dark-conditional");
}

.breakpoint .CodeMirror-linenumber:before {
  background-image: var(--breakpoint-background) !important;
}

.conditional .CodeMirror-linenumber:before {
  background-image: var(--breakpoint-conditional-background) !important;
}

.CodeMirror .error {
  background-image: url("chrome://devtools/skin/images/editor-error.png");
}
PK
!</��d��+chrome/devtools/content/storage/storage.xul<?xml version="1.0" encoding="utf-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/. -->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/content/shared/widgets/widgets.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/widgets.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/storage.css" type="text/css"?>
<?xml-stylesheet href="resource://devtools/client/shared/components/SidebarToggle.css" type="text/css"?>

<!DOCTYPE window [
  <!ENTITY % storageDTD SYSTEM "chrome://devtools/locale/storage.dtd">
  %storageDTD;
]>

<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

  <script type="application/javascript"
          src="chrome://devtools/content/shared/theme-switching.js"/>
  <script type="text/javascript" src="chrome://global/content/globalOverlay.js"/>

<script type="application/javascript" src="chrome://global/content/editMenuOverlay.js"/>
<commandset id="editMenuCommands">
  <commandset id="editMenuCommandSetAll" commandupdater="true" events="focus,select"
              oncommandupdate="goUpdateGlobalEditMenuItems()"/>
  <commandset id="editMenuCommandSetUndo" commandupdater="true" events="undo"
              oncommandupdate="goUpdateUndoEditMenuItems()"/>
  <commandset id="editMenuCommandSetPaste" commandupdater="true" events="clipboard"
              oncommandupdate="goUpdatePasteMenuItems()"/>
  <command id="cmd_undo" oncommand="goDoCommand('cmd_undo')"/>
  <command id="cmd_redo" oncommand="goDoCommand('cmd_redo')"/>
  <command id="cmd_cut" oncommand="goDoCommand('cmd_cut')"/>
  <command id="cmd_copy" oncommand="goDoCommand('cmd_copy')"/>
  <command id="cmd_paste" oncommand="goDoCommand('cmd_paste')"/>
  <command id="cmd_delete" oncommand="goDoCommand('cmd_delete')"/>
  <command id="cmd_selectAll" oncommand="goDoCommand('cmd_selectAll')"/>
  <command id="cmd_switchTextDirection" oncommand="goDoCommand('cmd_switchTextDirection');"/>
</commandset>

  <popupset id="storagePopupSet">
    <menupopup id="storage-tree-popup">
      <menuitem id="storage-tree-popup-delete-all"
                label="&storage.popupMenu.deleteAllLabel;"/>
      <menuitem id="storage-tree-popup-delete-all-session-cookies"
                label="&storage.popupMenu.deleteAllSessionCookiesLabel;"/>
      <menuitem id="storage-tree-popup-delete"/>
    </menupopup>
    <menupopup id="storage-table-popup">
      <menuitem id="storage-table-popup-add"/>
      <menuitem id="storage-table-popup-delete"/>
      <menuitem id="storage-table-popup-delete-all-from"/>
      <menuitem id="storage-table-popup-delete-all"
                label="&storage.popupMenu.deleteAllLabel;"/>
      <menuitem id="storage-table-popup-delete-all-session-cookies"
                label="&storage.popupMenu.deleteAllSessionCookiesLabel;"/>
    </menupopup>
  </popupset>

  <box flex="1" class="devtools-responsive-container theme-body">
    <vbox id="storage-tree"/>
    <splitter class="devtools-side-splitter"/>
    <vbox flex="1">
      <hbox id="storage-toolbar" class="devtools-toolbar">
        <button id="add-button"
                class="devtools-button add-button"></button>
        <button id="refresh-button"
                class="devtools-button refresh-button"></button>
        <spacer flex="1"/>
        <textbox id="storage-searchbox"
                 class="devtools-filterinput"
                 type="search"
                 timeout="200"
                 placeholder="&searchBox.placeholder;"/>
        <button class="devtools-button sidebar-toggle" hidden="true"></button>
      </hbox>
      <vbox id="storage-table" class="theme-sidebar" flex="1"/>
    </vbox>
    <splitter class="devtools-side-splitter"/>
    <vbox id="storage-sidebar" class="devtools-sidebar-tabs" hidden="true">
      <vbox flex="1"/>
    </vbox>
  </box>

</window>
PK
!<�qk�--3chrome/devtools/content/styleeditor/styleeditor.xul<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 window [
<!ENTITY % styleEditorDTD SYSTEM "chrome://devtools/locale/styleeditor.dtd" >
 %styleEditorDTD;
<!ENTITY % editMenuStrings SYSTEM "chrome://global/locale/editMenuOverlay.dtd">
 %editMenuStrings;
<!ENTITY % sourceEditorStrings SYSTEM "chrome://devtools/locale/sourceeditor.dtd">
 %sourceEditorStrings;
<!ENTITY % csscoverageDTD SYSTEM "chrome://devtools-shared/locale/csscoverage.dtd">
 %csscoverageDTD;
]>

<?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/content/shared/widgets/widgets.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/content/shared/splitview.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/chart.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/widgets.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/splitview.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/styleeditor.css" type="text/css"?>

<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        xmlns:html="http://www.w3.org/1999/xhtml"
        id="style-editor-chrome-window">

  <script type="application/javascript"
          src="chrome://devtools/content/shared/theme-switching.js"/>
  <script type="application/javascript" src="chrome://global/content/globalOverlay.js"/>
  <script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>
  <script type="application/javascript">
    function goUpdateSourceEditorMenuItems() {
      goUpdateGlobalEditMenuItems();

      ['cmd_undo', 'cmd_redo', 'cmd_cut', 'cmd_paste',
       'cmd_delete', 'cmd_find', 'cmd_findAgain'].forEach(goUpdateCommand);
    }
  </script>

  <popupset id="style-editor-popups">
    <menupopup id="sourceEditorContextMenu"
               onpopupshowing="goUpdateSourceEditorMenuItems()">
      <menuitem id="cMenu_undo" label="&undoCmd.label;"
                accesskey="&undoCmd.accesskey;" command="cmd_undo"/>
      <menuseparator/>
      <menuitem id="cMenu_cut" label="&cutCmd.label;"
                accesskey="&cutCmd.accesskey;" command="cmd_cut"/>
      <menuitem id="cMenu_copy" label="&copyCmd.label;"
                accesskey="&copyCmd.accesskey;" command="cmd_copy"/>
      <menuitem id="cMenu_paste" label="&pasteCmd.label;"
                accesskey="&pasteCmd.accesskey;" command="cmd_paste"/>
      <menuitem id="cMenu_delete" label="&deleteCmd.label;"
                accesskey="&deleteCmd.accesskey;" command="cmd_delete"/>
      <menuseparator/>
      <menuitem id="cMenu_selectAll" label="&selectAllCmd.label;"
                accesskey="&selectAllCmd.accesskey;" command="cmd_selectAll"/>
      <menuseparator/>
      <menuitem id="se-menu-find"
        label="&findCmd.label;" accesskey="&findCmd.accesskey;" command="cmd_find"/>
      <menuitem id="cMenu_findAgain" label="&findAgainCmd.label;"
                accesskey="&findAgainCmd.accesskey;" command="cmd_findAgain"/>
      <menuseparator/>
      <menuitem id="se-menu-gotoLine"
          label="&gotoLineCmd.label;"
          accesskey="&gotoLineCmd.accesskey;"
          key="key_gotoLine"
          command="cmd_gotoLine"/>
    </menupopup>
    <menupopup id="sidebar-context">
      <menuitem id="context-openlinknewtab"
        label="&openLinkNewTab.label;"/>
    </menupopup>
    <menupopup id="style-editor-options-popup"
               position="before_start">
      <menuitem id="options-origsources"
                type="checkbox"
                label="&showOriginalSources.label;"
                accesskey="&showOriginalSources.accesskey;"/>
      <menuitem id="options-show-media"
                type="checkbox"
                label="&showMediaSidebar.label;"
                accesskey="&showMediaSidebar.accesskey;"/>
    </menupopup>
  </popupset>

<script type="application/javascript" src="chrome://global/content/editMenuOverlay.js"/>
<commandset id="editMenuCommands">
  <commandset id="editMenuCommandSetAll" commandupdater="true" events="focus,select"
              oncommandupdate="goUpdateGlobalEditMenuItems()"/>
  <commandset id="editMenuCommandSetUndo" commandupdater="true" events="undo"
              oncommandupdate="goUpdateUndoEditMenuItems()"/>
  <commandset id="editMenuCommandSetPaste" commandupdater="true" events="clipboard"
              oncommandupdate="goUpdatePasteMenuItems()"/>
  <command id="cmd_undo" oncommand="goDoCommand('cmd_undo')"/>
  <command id="cmd_redo" oncommand="goDoCommand('cmd_redo')"/>
  <command id="cmd_cut" oncommand="goDoCommand('cmd_cut')"/>
  <command id="cmd_copy" oncommand="goDoCommand('cmd_copy')"/>
  <command id="cmd_paste" oncommand="goDoCommand('cmd_paste')"/>
  <command id="cmd_delete" oncommand="goDoCommand('cmd_delete')"/>
  <command id="cmd_selectAll" oncommand="goDoCommand('cmd_selectAll')"/>
  <command id="cmd_switchTextDirection" oncommand="goDoCommand('cmd_switchTextDirection');"/>
</commandset>

  <commandset id="sourceEditorCommands">
    <command id="cmd_gotoLine" oncommand="goDoCommand('cmd_gotoLine')"/>
    <command id="cmd_find" oncommand="goDoCommand('cmd_find')"/>
    <command id="cmd_findAgain" oncommand="goDoCommand('cmd_findAgain')"/>
  </commandset>

  <keyset id="sourceEditorKeys"/>

  <stack id="style-editor-chrome" class="loading theme-body">

    <box class="splitview-root devtools-responsive-container" context="sidebar-context">
      <box class="splitview-controller">
        <box class="splitview-main">
          <toolbar class="devtools-toolbar">
             <hbox class="devtools-toolbarbutton-group">
              <toolbarbutton class="style-editor-newButton devtools-toolbarbutton"
                          accesskey="&newButton.accesskey;"
                          tooltiptext="&newButton.tooltip;"/>
              <toolbarbutton class="style-editor-importButton devtools-toolbarbutton"
                          accesskey="&importButton.accesskey;"
                          tooltiptext="&importButton.tooltip;"/>
            </hbox>
            <spacer/>
            <toolbarbutton id="style-editor-options"
                        class="devtools-toolbarbutton devtools-option-toolbarbutton"
                        tooltiptext="&optionsButton.tooltip;"
                        popup="style-editor-options-popup"/>
          </toolbar>
        </box>
        <box id="splitview-resizer-target" class="theme-sidebar splitview-nav-container"
                persist="height">
          <html:ol class="splitview-nav" tabindex="0"></html:ol>
          <html:div class="splitview-nav placeholder empty">
            <html:p><html:strong>&noStyleSheet.label;</html:strong></html:p>
            <html:p>&noStyleSheet-tip-start.label;
              <html:a href="#"
                class="style-editor-newButton">&noStyleSheet-tip-action.label;</html:a>
              &noStyleSheet-tip-end.label;</html:p>
          </html:div>
        </box> <!-- .splitview-nav-container -->
      </box>   <!-- .splitview-controller -->
      <splitter class="devtools-side-splitter devtools-invisible-splitter"/>
      <box class="splitview-side-details devtools-main-content"/>

      <html:div id="splitview-templates" hidden="true">
        <html:li id="splitview-tpl-summary-stylesheet" tabindex="0">
          <label class="stylesheet-enabled" tabindex="0"
            tooltiptext="&visibilityToggle.tooltip;"
            accesskey="&saveButton.accesskey;"></label>
          <html:hgroup class="stylesheet-info">
            <html:h1><html:a class="stylesheet-name" tabindex="0"><label crop="center"/></html:a></html:h1>
            <html:div class="stylesheet-more">
              <html:h3 class="stylesheet-title"></html:h3>
              <html:h3 class="stylesheet-linked-file"></html:h3>
              <html:h3 class="stylesheet-rule-count"></html:h3>
              <spacer/>
              <html:h3><label class="stylesheet-saveButton"
                    tooltiptext="&saveButton.tooltip;"
                    accesskey="&saveButton.accesskey;">&saveButton.label;</label></html:h3>
            </html:div>
          </html:hgroup>
        </html:li>

        <box id="splitview-tpl-details-stylesheet" class="splitview-details">
          <hbox class="stylesheet-details-container">
            <box class="stylesheet-editor-input textbox"
                 data-placeholder="&editorTextbox.placeholder;"/>
            <splitter class="devtools-side-splitter"/>
            <vbox class="stylesheet-sidebar theme-sidebar" hidden="true">
              <toolbar class="devtools-toolbar">
                &mediaRules.label;
              </toolbar>
              <vbox class="stylesheet-media-container" flex="1">
                <html:div class="stylesheet-media-list" />
              </vbox>
            </vbox>
          </hbox>
        </box>
      </html:div> <!-- #splitview-templates -->
    </box>   <!-- .splitview-root -->

    <box class="csscoverage-template" hidden="true">
      <toolbar class="devtools-toolbar csscoverage-toolbar">
        <button class="devtools-toolbarbutton csscoverage-toolbarbutton"
            label="&csscoverage.backButton;"
            onclick="${onback}"/>
      </toolbar>
      <!-- The data for this comes from CSSUsageActor.createPageReport -->
      <html:div class="csscoverage-report-container">
        <html:div class="csscoverage-report-content">
          <html:div class="csscoverage-report-summary">
            <html:div class="csscoverage-report-chart"/>
          </html:div>
          <html:div class="csscoverage-report-unused">
            <html:h2>&csscoverage.unused;</html:h2>
            <html:p>&csscoverage.noMatches;</html:p>
            <html:div foreach="page in ${unused}">
              <html:h3>${page.url}</html:h3>
              <html:code foreach="rule in ${page.rules}"
                         href="${rule.url}"
                         class="csscoverage-list">${rule.selectorText}</html:code>
            </html:div>
          </html:div>
          <html:div class="csscoverage-report-optimize">
            <html:h2>&csscoverage.optimize.header;</html:h2>
            <html:p>
              &csscoverage.optimize.body1;
              <html:code>&lt;link ...></html:code>
              &csscoverage.optimize.body2;
              <html:code>&lt;style>...</html:code>
              &csscoverage.optimize.body3;
            </html:p>
            <html:div if="${preload.length == 0}">&csscoverage.optimize.bodyX;</html:div>
            <html:div if="${preload.length > 0}">
              <html:div foreach="page in ${preload}">
                <html:h3>${page.url}</html:h3>
                <html:textarea>&lt;style>
<html:loop foreach="rule in ${page.rules}"
                      onclick="${rule.onclick}">${rule.formattedCssText}</html:loop>&lt;/style></html:textarea>
              </html:div>
            </html:div>
            <html:p>
              &csscoverage.footer1;
              <html:a target="_blank" href="&csscoverage.footer2a;">&csscoverage.footer3;</html:a>
              &csscoverage.footer4;
            </html:p>
          </html:div>
          <html:p>&#160;</html:p>
        </html:div>
      </html:div>
    </box>

    <box class="csscoverage-report" hidden="true">
    </box>

  </stack>

</window>
PK
!<�|�>>4chrome/devtools/content/webaudioeditor/controller.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 {PrefObserver} = require("devtools/client/shared/prefs");

/**
 * A collection of `AudioNodeModel`s used throughout the editor
 * to keep track of audio nodes within the audio context.
 */
var gAudioNodes = new AudioNodesCollection();

/**
 * Initializes the web audio editor views
 */
function startupWebAudioEditor() {
  return all([
    WebAudioEditorController.initialize(),
    ContextView.initialize(),
    InspectorView.initialize(),
    PropertiesView.initialize(),
    AutomationView.initialize()
  ]);
}

/**
 * Destroys the web audio editor controller and views.
 */
function shutdownWebAudioEditor() {
  return all([
    WebAudioEditorController.destroy(),
    ContextView.destroy(),
    InspectorView.destroy(),
    PropertiesView.destroy(),
    AutomationView.destroy()
  ]);
}

/**
 * Functions handling target-related lifetime events.
 */
var WebAudioEditorController = {
  /**
   * Listen for events emitted by the current tab target.
   */
  initialize: Task.async(function* () {
    this._onTabNavigated = this._onTabNavigated.bind(this);
    this._onThemeChange = this._onThemeChange.bind(this);

    gTarget.on("will-navigate", this._onTabNavigated);
    gTarget.on("navigate", this._onTabNavigated);
    gFront.on("start-context", this._onStartContext);
    gFront.on("create-node", this._onCreateNode);
    gFront.on("connect-node", this._onConnectNode);
    gFront.on("connect-param", this._onConnectParam);
    gFront.on("disconnect-node", this._onDisconnectNode);
    gFront.on("change-param", this._onChangeParam);
    gFront.on("destroy-node", this._onDestroyNode);

    // Hook into theme change so we can change
    // the graph's marker styling, since we can't do this
    // with CSS

    this._prefObserver = new PrefObserver("");
    this._prefObserver.on("devtools.theme", this._onThemeChange);

    // Store the AudioNode definitions from the WebAudioFront, if the method exists.
    // If not, get the JSON directly. Using the actor method is preferable so the client
    // knows exactly what methods are supported on the server.
    let actorHasDefinition = yield gTarget.actorHasMethod("webaudio", "getDefinition");
    if (actorHasDefinition) {
      AUDIO_NODE_DEFINITION = yield gFront.getDefinition();
    } else {
      AUDIO_NODE_DEFINITION = require("devtools/server/actors/utils/audionodes.json");
    }

    // Make sure the backend is prepared to handle audio contexts.
    // Since actors are created lazily on the first request to them, we need to send an
    // early request to ensure the CallWatcherActor is running and watching for new window
    // globals.
    gFront.setup({ reload: false });
  }),

  /**
   * Remove events emitted by the current tab target.
   */
  destroy: function () {
    gTarget.off("will-navigate", this._onTabNavigated);
    gTarget.off("navigate", this._onTabNavigated);
    gFront.off("start-context", this._onStartContext);
    gFront.off("create-node", this._onCreateNode);
    gFront.off("connect-node", this._onConnectNode);
    gFront.off("connect-param", this._onConnectParam);
    gFront.off("disconnect-node", this._onDisconnectNode);
    gFront.off("change-param", this._onChangeParam);
    gFront.off("destroy-node", this._onDestroyNode);
    this._prefObserver.off("devtools.theme", this._onThemeChange);
    this._prefObserver.destroy();
  },

  /**
   * Called when page is reloaded to show the reload notice and waiting
   * for an audio context notice.
   */
  reset: function () {
    $("#content").hidden = true;
    ContextView.resetUI();
    InspectorView.resetUI();
    PropertiesView.resetUI();
  },

  // Since node events (create, disconnect, connect) are all async,
  // we have to make sure to wait that the node has finished creating
  // before performing an operation on it.
  getNode: function* (nodeActor) {
    let id = nodeActor.actorID;
    let node = gAudioNodes.get(id);

    if (!node) {
      let { resolve, promise } = defer();
      gAudioNodes.on("add", function createNodeListener(createdNode) {
        if (createdNode.id === id) {
          gAudioNodes.off("add", createNodeListener);
          resolve(createdNode);
        }
      });
      node = yield promise;
    }
    return node;
  },

  /**
   * Fired when the devtools theme changes (light, dark, etc.)
   * so that the graph can update marker styling, as that
   * cannot currently be done with CSS.
   */
  _onThemeChange: function () {
    let newValue = Services.prefs.getCharPref("devtools.theme");
    window.emit(EVENTS.THEME_CHANGE, newValue);
  },

  /**
   * Called for each location change in the debugged tab.
   */
  _onTabNavigated: Task.async(function* (event, {isFrameSwitching}) {
    switch (event) {
      case "will-navigate": {
        // Clear out current UI.
        this.reset();

        // When switching to an iframe, ensure displaying the reload button.
        // As the document has already been loaded without being hooked.
        if (isFrameSwitching) {
          $("#reload-notice").hidden = false;
          $("#waiting-notice").hidden = true;
        } else {
          // Otherwise, we are loading a new top level document,
          // so we don't need to reload anymore and should receive
          // new node events.
          $("#reload-notice").hidden = true;
          $("#waiting-notice").hidden = false;
        }

        // Clear out stored audio nodes
        gAudioNodes.reset();

        window.emit(EVENTS.UI_RESET);
        break;
      }
      case "navigate": {
        // TODO Case of bfcache, needs investigating
        // bug 994250
        break;
      }
    }
  }),

  /**
   * Called after the first audio node is created in an audio context,
   * signaling that the audio context is being used.
   */
  _onStartContext: function () {
    $("#reload-notice").hidden = true;
    $("#waiting-notice").hidden = true;
    $("#content").hidden = false;
    window.emit(EVENTS.START_CONTEXT);
  },

  /**
   * Called when a new node is created. Creates an `AudioNodeView` instance
   * for tracking throughout the editor.
   */
  _onCreateNode: function (nodeActor) {
    gAudioNodes.add(nodeActor);
  },

  /**
   * Called on `destroy-node` when an AudioNode is GC'd. Removes
   * from the AudioNode array and fires an event indicating the removal.
   */
  _onDestroyNode: function (nodeActor) {
    gAudioNodes.remove(gAudioNodes.get(nodeActor.actorID));
  },

  /**
   * Called when a node is connected to another node.
   */
  _onConnectNode: Task.async(function* ({ source: sourceActor, dest: destActor }) {
    let source = yield WebAudioEditorController.getNode(sourceActor);
    let dest = yield WebAudioEditorController.getNode(destActor);
    source.connect(dest);
  }),

  /**
   * Called when a node is conneceted to another node's AudioParam.
   */
  _onConnectParam: Task.async(function* ({ source: sourceActor, dest: destActor, param }) {
    let source = yield WebAudioEditorController.getNode(sourceActor);
    let dest = yield WebAudioEditorController.getNode(destActor);
    source.connect(dest, param);
  }),

  /**
   * Called when a node is disconnected.
   */
  _onDisconnectNode: Task.async(function* (nodeActor) {
    let node = yield WebAudioEditorController.getNode(nodeActor);
    node.disconnect();
  }),

  /**
   * Called when a node param is changed.
   */
  _onChangeParam: Task.async(function* ({ actor, param, value }) {
    let node = yield WebAudioEditorController.getNode(actor);
    window.emit(EVENTS.CHANGE_PARAM, node, param, value);
  })
};
PK
!<�CMM2chrome/devtools/content/webaudioeditor/includes.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { loader, require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
const { Task } = require("devtools/shared/task");
const EventEmitter = require("devtools/shared/event-emitter");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const Services = require("Services");
const { gDevTools } = require("devtools/client/framework/devtools");
const { LocalizationHelper } = require("devtools/shared/l10n");
const { ViewHelpers } = require("devtools/client/shared/widgets/view-helpers");

// Use privileged promise in panel documents to prevent having them to freeze
// during toolbox destruction. See bug 1402779.
const Promise = require("Promise");

const STRINGS_URI = "devtools/client/locales/webaudioeditor.properties";
const L10N = new LocalizationHelper(STRINGS_URI);

loader.lazyRequireGetter(this, "LineGraphWidget",
  "devtools/client/shared/widgets/LineGraphWidget");

// `AUDIO_NODE_DEFINITION` defined in the controller's initialization,
// which describes all the properties of an AudioNode
var AUDIO_NODE_DEFINITION;

// Override DOM promises with Promise.jsm helpers
const { defer, all } = require("promise");

/* Events fired on `window` to indicate state or actions*/
const EVENTS = {
  // Fired when the first AudioNode has been created, signifying
  // that the AudioContext is being used and should be tracked via the editor.
  START_CONTEXT: "WebAudioEditor:StartContext",

  // When the devtools theme changes.
  THEME_CHANGE: "WebAudioEditor:ThemeChange",

  // When the UI is reset from tab navigation.
  UI_RESET: "WebAudioEditor:UIReset",

  // When a param has been changed via the UI and successfully
  // pushed via the actor to the raw audio node.
  UI_SET_PARAM: "WebAudioEditor:UISetParam",

  // When a node is to be set in the InspectorView.
  UI_SELECT_NODE: "WebAudioEditor:UISelectNode",

  // When the inspector is finished setting a new node.
  UI_INSPECTOR_NODE_SET: "WebAudioEditor:UIInspectorNodeSet",

  // When the inspector is finished rendering in or out of view.
  UI_INSPECTOR_TOGGLED: "WebAudioEditor:UIInspectorToggled",

  // When an audio node is finished loading in the Properties tab.
  UI_PROPERTIES_TAB_RENDERED: "WebAudioEditor:UIPropertiesTabRendered",

  // When an audio node is finished loading in the Automation tab.
  UI_AUTOMATION_TAB_RENDERED: "WebAudioEditor:UIAutomationTabRendered",

  // When the Audio Context graph finishes rendering.
  // Is called with two arguments, first representing number of nodes
  // rendered, second being the number of edge connections rendering (not counting
  // param edges), followed by the count of the param edges rendered.
  UI_GRAPH_RENDERED: "WebAudioEditor:UIGraphRendered",

  // Called when the inspector splitter is moved and resized.
  UI_INSPECTOR_RESIZE: "WebAudioEditor:UIInspectorResize"
};
XPCOMUtils.defineConstant(this, "EVENTS", EVENTS);

/**
 * The current target and the Web Audio Editor front, set by this tool's host.
 */
var gToolbox, gTarget, gFront;

/**
 * Convenient way of emitting events from the panel window.
 */
EventEmitter.decorate(this);

/**
 * DOM query helper.
 */
function $(selector, target = document) { return target.querySelector(selector); }
function $$(selector, target = document) { return target.querySelectorAll(selector); }

/**
 * Takes an iterable collection, and a hash. Return the first
 * object in the collection that matches the values in the hash.
 * From Backbone.Collection#findWhere
 * http://backbonejs.org/#Collection-findWhere
 */
function findWhere(collection, attrs) {
  let keys = Object.keys(attrs);
  for (let model of collection) {
    if (keys.every(key => model[key] === attrs[key])) {
      return model;
    }
  }
  return void 0;
}

function mixin(source, ...args) {
  args.forEach(obj => Object.keys(obj).forEach(prop => source[prop] = obj[prop]));
  return source;
}
PK
!<R^����0chrome/devtools/content/webaudioeditor/models.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";

/**
 * Representational wrapper around AudioNodeActors. Adding and destroying
 * AudioNodes should be performed through the AudioNodes collection.
 *
 * Events:
 * - `connect`: node, destinationNode, parameter
 * - `disconnect`: node
 */
class AudioNodeModel extends EventEmitter {
  constructor(actor) {
    super();

    // Will be added via AudioNodes `add`
    this.collection = null;

    this.actor = actor;
    this.id = actor.actorID;
    this.type = actor.type;
    this.bypassable = actor.bypassable;
    this._bypassed = false;
    this.connections = [];
  }

  /**
   * Stores connection data inside this instance of this audio node connecting
   * to another node (destination). If connecting to another node's AudioParam,
   * the second argument (param) must be populated with a string.
   *
   * Connecting nodes is idempotent. Upon new connection, emits "connect" event.
   *
   * @param AudioNodeModel destination
   * @param String param
   */
  connect(destination, param) {
    let edge = findWhere(this.connections, { destination: destination.id, param: param });

    if (!edge) {
      this.connections.push({ source: this.id, destination: destination.id, param: param });
      EventEmitter.emit(this, "connect", this, destination, param);
    }
  }

  /**
   * Clears out all internal connection data. Emits "disconnect" event.
   */
  disconnect() {
    this.connections.length = 0;
    EventEmitter.emit(this, "disconnect", this);
  }

  /**
   * Gets the bypass status of the audio node.
   *
   * @return Boolean
   */
  isBypassed() {
    return this._bypassed;
  }

  /**
   * Sets the bypass value of an AudioNode.
   *
   * @param Boolean enable
   * @return Promise
   */
  bypass(enable) {
    this._bypassed = enable;
    return this.actor.bypass(enable).then(() => EventEmitter.emit(this, "bypass", this, enable));
  }

  /**
   * Returns a promise that resolves to an array of objects containing
   * both a `param` name property and a `value` property.
   *
   * @return Promise->Object
   */
  getParams() {
    return this.actor.getParams();
  }

  /**
   * Returns a promise that resolves to an object containing an
   * array of event information and an array of automation data.
   *
   * @param String paramName
   * @return Promise->Array
   */
  getAutomationData(paramName) {
    return this.actor.getAutomationData(paramName);
  }

  /**
   * Takes a `dagreD3.Digraph` object and adds this node to
   * the graph to be rendered.
   *
   * @param dagreD3.Digraph
   */
  addToGraph(graph) {
    graph.addNode(this.id, {
      type: this.type,
      label: this.type.replace(/Node$/, ""),
      id: this.id,
      bypassed: this._bypassed
    });
  }

  /**
   * Takes a `dagreD3.Digraph` object and adds edges to
   * the graph to be rendered. Separate from `addToGraph`,
   * as while we depend on D3/Dagre's constraints, we cannot
   * add edges for nodes that have not yet been added to the graph.
   *
   * @param dagreD3.Digraph
   */
  addEdgesToGraph(graph) {
    for (let edge of this.connections) {
      let options = {
        source: this.id,
        target: edge.destination
      };

      // Only add `label` if `param` specified, as this is an AudioParam
      // connection then. `label` adds the magic to render with dagre-d3,
      // and `param` is just more explicitly the param, ignoring
      // implementation details.
      if (edge.param) {
        options.label = options.param = edge.param;
      }

      graph.addEdge(null, this.id, edge.destination, options);
    }
  }

  toString() {
    return "[object AudioNodeModel]";
  }
}


/**
 * Constructor for a Collection of `AudioNodeModel` models.
 *
 * Events:
 * - `add`: node
 * - `remove`: node
 * - `connect`: node, destinationNode, parameter
 * - `disconnect`: node
 */
class AudioNodesCollection extends EventEmitter {
  constructor() {
    super();

    this.model = AudioNodeModel;
    this.models = new Set();
    this._onModelEvent = this._onModelEvent.bind(this);
  }

  /**
   * Iterates over all models within the collection, calling `fn` with the
   * model as the first argument.
   *
   * @param Function fn
   */
  forEach(fn) {
    this.models.forEach(fn);
  }

  /**
   * Creates a new AudioNodeModel, passing through arguments into the AudioNodeModel
   * constructor, and adds the model to the internal collection store of this
   * instance.
   *
   * Emits "add" event on instance when completed.
   *
   * @param Object obj
   * @return AudioNodeModel
   */
  add(obj) {
    let node = new this.model(obj);
    node.collection = this;

    this.models.add(node);

    node.on("*", this._onModelEvent);
    EventEmitter.emit(this, "add", node);
    return node;
  }

  /**
   * Removes an AudioNodeModel from the internal collection. Calls `delete` method
   * on the model, and emits "remove" on this instance.
   *
   * @param AudioNodeModel node
   */
  remove(node) {
    this.models.delete(node);
    EventEmitter.emit(this, "remove", node);
  }

  /**
   * Empties out the internal collection of all AudioNodeModels.
   */
  reset() {
    this.models.clear();
  }

  /**
   * Takes an `id` from an AudioNodeModel and returns the corresponding
   * AudioNodeModel within the collection that matches that id. Returns `null`
   * if not found.
   *
   * @param Number id
   * @return AudioNodeModel|null
   */
  get(id) {
    return findWhere(this.models, { id: id });
  }

  /**
   * Returns the count for how many models are a part of this collection.
   *
   * @return Number
   */
  get length() {
    return this.models.size;
  }

  /**
   * Returns detailed information about the collection. used during tests
   * to query state. Returns an object with information on node count,
   * how many edges are within the data graph, as well as how many of those edges
   * are for AudioParams.
   *
   * @return Object
   */
  getInfo() {
    let info = {
      nodes: this.length,
      edges: 0,
      paramEdges: 0
    };

    this.models.forEach(node => {
      let paramEdgeCount = node.connections.filter(edge => edge.param).length;
      info.edges += node.connections.length - paramEdgeCount;
      info.paramEdges += paramEdgeCount;
    });
    return info;
  }

  /**
   * Adds all nodes within the collection to the passed in graph,
   * as well as their corresponding edges.
   *
   * @param dagreD3.Digraph
   */
  populateGraph(graph) {
    this.models.forEach(node => node.addToGraph(graph));
    this.models.forEach(node => node.addEdgesToGraph(graph));
  }

  /**
   * Called when a stored model emits any event. Used to manage
   * event propagation, or listening to model events to react, like
   * removing a model from the collection when it's destroyed.
   */
  _onModelEvent(eventName, node, ...args) {
    if (eventName === "remove") {
      // If a `remove` event from the model, remove it
      // from the collection, and let the method handle the emitting on
      // the collection
      this.remove(node);
    } else {
      // Pipe the event to the collection
      EventEmitter.emit(this, eventName, node, ...args);
    }
  }

  toString() {
    return "[object AudioNodeCollection]";
  }
}
PK
!<�+ź00:chrome/devtools/content/webaudioeditor/views/automation.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";

/**
 * Functions handling the audio node inspector UI.
 */

var AutomationView = {

  /**
   * Initialization function called when the tool starts up.
   */
  initialize: function () {
    this._buttons = $("#automation-param-toolbar-buttons");
    this.graph = new LineGraphWidget($("#automation-graph"), { avg: false });
    this.graph.selectionEnabled = false;

    this._onButtonClick = this._onButtonClick.bind(this);
    this._onNodeSet = this._onNodeSet.bind(this);
    this._onResize = this._onResize.bind(this);

    this._buttons.addEventListener("click", this._onButtonClick);
    window.on(EVENTS.UI_INSPECTOR_RESIZE, this._onResize);
    window.on(EVENTS.UI_INSPECTOR_NODE_SET, this._onNodeSet);
  },

  /**
   * Destruction function called when the tool cleans up.
   */
  destroy: function () {
    this._buttons.removeEventListener("click", this._onButtonClick);
    window.off(EVENTS.UI_INSPECTOR_RESIZE, this._onResize);
    window.off(EVENTS.UI_INSPECTOR_NODE_SET, this._onNodeSet);
  },

  /**
   * Empties out the props view.
   */
  resetUI: function () {
    this._currentNode = null;
  },

  /**
   * On a new node selection, create the Automation panel for
   * that specific node.
   */
  build: Task.async(function* () {
    let node = this._currentNode;

    let props = yield node.getParams();
    let params = props.filter(({ flags }) => flags && flags.param);

    this._createParamButtons(params);

    this._selectedParamName = params[0] ? params[0].param : null;
    this.render();
  }),

  /**
   * Renders the graph for specified `paramName`. Called when
   * the parameter view is changed, or when new param data events
   * are fired for the currently specified param.
   */
  render: Task.async(function* () {
    let node = this._currentNode;
    let paramName = this._selectedParamName;
    // Escape if either node or parameter name does not exist.
    if (!node || !paramName) {
      this._setState("no-params");
      window.emit(EVENTS.UI_AUTOMATION_TAB_RENDERED, null);
      return;
    }

    let { values, events } = yield node.getAutomationData(paramName);
    this._setState(events.length ? "show" : "no-events");
    yield this.graph.setDataWhenReady(values);
    window.emit(EVENTS.UI_AUTOMATION_TAB_RENDERED, node.id);
  }),

  /**
   * Create the buttons for each AudioParam, that when clicked,
   * render the graph for that AudioParam.
   */
  _createParamButtons: function (params) {
    this._buttons.innerHTML = "";
    params.forEach((param, i) => {
      let button = document.createElement("toolbarbutton");
      button.setAttribute("class", "devtools-toolbarbutton automation-param-button");
      button.setAttribute("data-param", param.param);
      // Set label to the parameter name, should not be L10N'd
      button.setAttribute("label", param.param);

      // If first button, set to 'selected' for styling
      if (i === 0) {
        button.setAttribute("selected", true);
      }

      this._buttons.appendChild(button);
    });
  },

  /**
   * Internally sets the current audio node and rebuilds appropriate
   * views.
   */
  _setAudioNode: function (node) {
    this._currentNode = node;
    if (this._currentNode) {
      this.build();
    }
  },

  /**
   * Toggles the subviews to display messages whether or not
   * the audio node has no AudioParams, no automation events, or
   * shows the graph.
   */
  _setState: function (state) {
    let contentView = $("#automation-content");
    let emptyView = $("#automation-empty");

    let graphView = $("#automation-graph-container");
    let noEventsView = $("#automation-no-events");

    contentView.hidden = state === "no-params";
    emptyView.hidden = state !== "no-params";

    graphView.hidden = state !== "show";
    noEventsView.hidden = state !== "no-events";
  },

  /**
   * Event handlers
   */

  _onButtonClick: function (e) {
    Array.forEach($$(".automation-param-button"), $btn => $btn.removeAttribute("selected"));
    let paramName = e.target.getAttribute("data-param");
    e.target.setAttribute("selected", true);
    this._selectedParamName = paramName;
    this.render();
  },

  /**
   * Called when the inspector is resized.
   */
  _onResize: function () {
    this.graph.refresh();
  },

  /**
   * Called when the inspector view determines a node is selected.
   */
  _onNodeSet: function (id) {
    this._setAudioNode(id != null ? gAudioNodes.get(id) : null);
  }
};
PK
!<
��''7chrome/devtools/content/webaudioeditor/views/context.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";

/* import-globals-from ../includes.js */

const { debounce } = require("devtools/shared/debounce");
const flags = require("devtools/shared/flags");

// Globals for d3 stuff
// Default properties of the graph on rerender
const GRAPH_DEFAULTS = {
  translate: [20, 20],
  scale: 1
};

// Sizes of SVG arrows in graph
const ARROW_HEIGHT = 5;
const ARROW_WIDTH = 8;

// Styles for markers as they cannot be done with CSS.
const MARKER_STYLING = {
  light: "#AAA",
  dark: "#CED3D9"
};
Object.defineProperty(this, "MARKER_STYLING", {
  value: MARKER_STYLING,
  enumerable: true,
  writable: false
});

const GRAPH_DEBOUNCE_TIMER = 100;

// `gAudioNodes` events that should require the graph
// to redraw
const GRAPH_REDRAW_EVENTS = ["add", "connect", "disconnect", "remove"];

/**
 * Functions handling the graph UI.
 */
var ContextView = {
  /**
   * Initialization function, called when the tool is started.
   */
  initialize: function () {
    this._onGraphClick = this._onGraphClick.bind(this);
    this._onThemeChange = this._onThemeChange.bind(this);
    this._onStartContext = this._onStartContext.bind(this);
    this._onEvent = this._onEvent.bind(this);

    this.draw = debounce(this.draw.bind(this), GRAPH_DEBOUNCE_TIMER);
    $("#graph-target").addEventListener("click", this._onGraphClick);

    window.on(EVENTS.THEME_CHANGE, this._onThemeChange);
    window.on(EVENTS.START_CONTEXT, this._onStartContext);
    gAudioNodes.on("*", this._onEvent);
  },

  /**
   * Destruction function, called when the tool is closed.
   */
  destroy: function () {
    // If the graph was rendered at all, then the handler
    // for zooming in will be set. We must remove it to prevent leaks.
    if (this._zoomBinding) {
      this._zoomBinding.on("zoom", null);
    }
    $("#graph-target").removeEventListener("click", this._onGraphClick);

    window.off(EVENTS.THEME_CHANGE, this._onThemeChange);
    window.off(EVENTS.START_CONTEXT, this._onStartContext);
    gAudioNodes.off("*", this._onEvent);
  },

  /**
   * Called when a page is reloaded and waiting for a "start-context" event
   * and clears out old content
   */
  resetUI: function () {
    this.clearGraph();
    this.resetGraphTransform();
  },

  /**
   * Clears out the rendered graph, called when resetting the SVG elements to draw again,
   * or when resetting the entire UI tool
   */
  clearGraph: function () {
    $("#graph-target").innerHTML = "";
  },

  /**
   * Moves the graph back to its original scale and translation.
   */
  resetGraphTransform: function () {
    // Only reset if the graph was ever drawn.
    if (this._zoomBinding) {
      let { translate, scale } = GRAPH_DEFAULTS;
      // Must set the `zoomBinding` so the next `zoom` event is in sync with
      // where the graph is visually (set by the `transform` attribute).
      this._zoomBinding.scale(scale);
      this._zoomBinding.translate(translate);
      d3.select("#graph-target")
        .attr("transform", "translate(" + translate + ") scale(" + scale + ")");
    }
  },

  getCurrentScale: function () {
    return this._zoomBinding ? this._zoomBinding.scale() : null;
  },

  getCurrentTranslation: function () {
    return this._zoomBinding ? this._zoomBinding.translate() : null;
  },

  /**
   * Makes the corresponding graph node appear "focused", removing
   * focused styles from all other nodes. If no `actorID` specified,
   * make all nodes appear unselected.
   */
  focusNode: function (actorID) {
    // Remove class "selected" from all nodes
    Array.forEach($$(".nodes > g"), $node => $node.classList.remove("selected"));
    // Add to "selected"
    if (actorID) {
      this._getNodeByID(actorID).classList.add("selected");
    }
  },

  /**
   * Takes an actorID and returns the corresponding DOM SVG element in the graph
   */
  _getNodeByID: function (actorID) {
    return $(".nodes > g[data-id='" + actorID + "']");
  },

  /**
   * Sets the appropriate class on an SVG node when its bypass
   * status is toggled.
   */
  _bypassNode: function (node, enabled) {
    let el = this._getNodeByID(node.id);
    el.classList[enabled ? "add" : "remove"]("bypassed");
  },

  /**
   * This method renders the nodes currently available in `gAudioNodes` and is
   * throttled to be called at most every `GRAPH_DEBOUNCE_TIMER` milliseconds.
   * It's called whenever the audio context routing changes, after being debounced.
   */
  draw: function () {
    // Clear out previous SVG information
    this.clearGraph();

    let graph = new dagreD3.Digraph();
    let renderer = new dagreD3.Renderer();
    gAudioNodes.populateGraph(graph);

    // Post-render manipulation of the nodes
    let oldDrawNodes = renderer.drawNodes();
    renderer.drawNodes(function (graph, root) {
      let svgNodes = oldDrawNodes(graph, root);
      svgNodes.each(function (n) {
        let node = graph.node(n);
        let classString = "audionode type-" + node.type + (node.bypassed ? " bypassed" : "");
        this.setAttribute("class", classString);
        this.setAttribute("data-id", node.id);
        this.setAttribute("data-type", node.type);
      });
      return svgNodes;
    });

    // Post-render manipulation of edges
    let oldDrawEdgePaths = renderer.drawEdgePaths();
    let defaultClasses = "edgePath enter";

    renderer.drawEdgePaths(function (graph, root) {
      let svgEdges = oldDrawEdgePaths(graph, root);
      svgEdges.each(function (e) {
        let edge = graph.edge(e);

        // We have to manually specify the default classes on the edges
        // as to not overwrite them
        let edgeClass = defaultClasses + (edge.param ? (" param-connection " + edge.param) : "");

        this.setAttribute("data-source", edge.source);
        this.setAttribute("data-target", edge.target);
        this.setAttribute("data-param", edge.param ? edge.param : null);
        this.setAttribute("class", edgeClass);
      });

      return svgEdges;
    });

    // Override Dagre-d3's post render function by passing in our own.
    // This way we can leave styles out of it.
    renderer.postRender((graph, root) => {
      // We have to manually set the marker styling since we cannot
      // do this currently with CSS, although it is in spec for SVG2
      // https://svgwg.org/svg2-draft/painting.html#VertexMarkerProperties
      // For now, manually set it on creation, and the `_onThemeChange`
      // function will fire when the devtools theme changes to update the
      // styling manually.
      let theme = Services.prefs.getCharPref("devtools.theme");
      let markerColor = MARKER_STYLING[theme];
      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", ARROW_WIDTH)
          .attr("refY", ARROW_HEIGHT)
          .attr("markerUnits", "strokewidth")
          .attr("markerWidth", ARROW_WIDTH)
          .attr("markerHeight", ARROW_HEIGHT)
          .attr("orient", "auto")
          .attr("style", "fill: " + markerColor)
          .append("svg:path")
          .attr("d", "M 0 0 L 10 5 L 0 10 z");
      }

      // Reselect the previously selected audio node
      let currentNode = InspectorView.getCurrentAudioNode();
      if (currentNode) {
        this.focusNode(currentNode.id);
      }

      // Fire an event upon completed rendering, with extra information
      // if in testing mode only.
      let info = {};
      if (flags.testing) {
        info = gAudioNodes.getInfo();
      }
      window.emit(EVENTS.UI_GRAPH_RENDERED, info.nodes, info.edges, info.paramEdges);
    });

    let layout = dagreD3.layout().rankDir("LR");
    renderer.layout(layout).run(graph, d3.select("#graph-target"));

    // Handle the sliding and zooming of the graph,
    // store as `this._zoomBinding` so we can unbind during destruction
    if (!this._zoomBinding) {
      this._zoomBinding = d3.behavior.zoom().on("zoom", function () {
        var ev = d3.event;
        d3.select("#graph-target")
          .attr("transform", "translate(" + ev.translate + ") scale(" + ev.scale + ")");
      });
      d3.select("svg").call(this._zoomBinding);

      // Set initial translation and scale -- this puts D3's awareness of
      // the graph in sync with what the user sees originally.
      this.resetGraphTransform();
    }
  },

  /**
   * Event handlers
   */

  /**
   * Called once "start-context" is fired, indicating that there is an audio
   * context being created to view so render the graph.
   */
  _onStartContext: function () {
    this.draw();
  },

  /**
   * Called when `gAudioNodes` fires an event -- most events (listed
   * in GRAPH_REDRAW_EVENTS) qualify as a redraw event.
   */
  _onEvent: function (eventName, ...args) {
    // If bypassing, just toggle the class on the SVG node
    // rather than rerendering everything
    if (eventName === "bypass") {
      this._bypassNode.apply(this, args);
    }
    if (~GRAPH_REDRAW_EVENTS.indexOf(eventName)) {
      this.draw();
    }
  },

  /**
   * Fired when the devtools theme changes.
   */
  _onThemeChange: function (theme) {
    let markerColor = MARKER_STYLING[theme];
    let marker = $("#arrowhead");
    if (marker) {
      marker.setAttribute("style", "fill: " + markerColor);
    }
  },

  /**
   * Fired when a click occurs in the graph.
   *
   * @param Event e
   *        Click event.
   */
  _onGraphClick: function (e) {
    let node = findGraphNodeParent(e.target);
    // If node not found (clicking outside of an audio node in the graph),
    // then ignore this event
    if (!node)
      return;

    let id = node.getAttribute("data-id");

    this.focusNode(id);
    window.emit(EVENTS.UI_SELECT_NODE, id);
  }
};
PK
!<P�jN��9chrome/devtools/content/webaudioeditor/views/inspector.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";

/* import-globals-from ../includes.js */

const MIN_INSPECTOR_WIDTH = 300;

// Strings for rendering
const EXPAND_INSPECTOR_STRING = L10N.getStr("expandInspector");
const COLLAPSE_INSPECTOR_STRING = L10N.getStr("collapseInspector");

/**
 * Functions handling the audio node inspector UI.
 */

var InspectorView = {
  _currentNode: null,

  // Set up config for view toggling
  _collapseString: COLLAPSE_INSPECTOR_STRING,
  _expandString: EXPAND_INSPECTOR_STRING,
  _toggleEvent: EVENTS.UI_INSPECTOR_TOGGLED,
  _animated: true,
  _delayed: true,

  /**
   * Initialization function called when the tool starts up.
   */
  initialize: function () {
    // Set up view controller
    this.el = $("#web-audio-inspector");
    this.splitter = $("#inspector-splitter");
    this.el.setAttribute("width", Services.prefs.getIntPref("devtools.webaudioeditor.inspectorWidth"));
    this.button = $("#inspector-pane-toggle");
    mixin(this, ToggleMixin);
    this.bindToggle();

    // Hide inspector view on startup
    this.hideImmediately();

    this._onNodeSelect = this._onNodeSelect.bind(this);
    this._onDestroyNode = this._onDestroyNode.bind(this);
    this._onResize = this._onResize.bind(this);
    this._onCommandClick = this._onCommandClick.bind(this);

    this.splitter.addEventListener("mouseup", this._onResize);
    for (let $el of $$("#audio-node-toolbar toolbarbutton")) {
      $el.addEventListener("command", this._onCommandClick);
    }
    window.on(EVENTS.UI_SELECT_NODE, this._onNodeSelect);
    gAudioNodes.on("remove", this._onDestroyNode);
  },

  /**
   * Destruction function called when the tool cleans up.
   */
  destroy: function () {
    this.unbindToggle();
    this.splitter.removeEventListener("mouseup", this._onResize);

    $("#audio-node-toolbar toolbarbutton").removeEventListener("command", this._onCommandClick);
    for (let $el of $$("#audio-node-toolbar toolbarbutton")) {
      $el.removeEventListener("command", this._onCommandClick);
    }
    window.off(EVENTS.UI_SELECT_NODE, this._onNodeSelect);
    gAudioNodes.off("remove", this._onDestroyNode);

    this.el = null;
    this.button = null;
    this.splitter = null;
  },

  /**
   * Takes a AudioNodeView `node` and sets it as the current
   * node and scaffolds the inspector view based off of the new node.
   */
  setCurrentAudioNode: Task.async(function* (node) {
    this._currentNode = node || null;

    // If no node selected, set the inspector back to "no AudioNode selected"
    // view.
    if (!node) {
      $("#web-audio-editor-details-pane-empty").removeAttribute("hidden");
      $("#web-audio-editor-tabs").setAttribute("hidden", "true");
      window.emit(EVENTS.UI_INSPECTOR_NODE_SET, null);
    }
    // Otherwise load up the tabs view and hide the empty placeholder
    else {
      $("#web-audio-editor-details-pane-empty").setAttribute("hidden", "true");
      $("#web-audio-editor-tabs").removeAttribute("hidden");
      this._buildToolbar();
      window.emit(EVENTS.UI_INSPECTOR_NODE_SET, this._currentNode.id);
    }
  }),

  /**
   * Returns the current AudioNodeView.
   */
  getCurrentAudioNode: function () {
    return this._currentNode;
  },

  /**
   * Empties out the props view.
   */
  resetUI: function () {
    // Set current node to empty to load empty view
    this.setCurrentAudioNode();

    // Reset AudioNode inspector and hide
    this.hideImmediately();
  },

  _buildToolbar: function () {
    let node = this.getCurrentAudioNode();

    let bypassable = node.bypassable;
    let bypassed = node.isBypassed();
    let button = $("#audio-node-toolbar .bypass");

    if (!bypassable) {
      button.setAttribute("disabled", true);
    } else {
      button.removeAttribute("disabled");
    }

    if (!bypassable || bypassed) {
      button.removeAttribute("checked");
    } else {
      button.setAttribute("checked", true);
    }
  },

  /**
   * Event handlers
   */

  /**
   * Called on EVENTS.UI_SELECT_NODE, and takes an actorID `id`
   * and calls `setCurrentAudioNode` to scaffold the inspector view.
   */
  _onNodeSelect: function (id) {
    this.setCurrentAudioNode(gAudioNodes.get(id));

    // Ensure inspector is visible when selecting a new node
    this.show();
  },

  _onResize: function () {
    if (this.el.getAttribute("width") < MIN_INSPECTOR_WIDTH) {
      this.el.setAttribute("width", MIN_INSPECTOR_WIDTH);
    }
    Services.prefs.setIntPref("devtools.webaudioeditor.inspectorWidth", this.el.getAttribute("width"));
    window.emit(EVENTS.UI_INSPECTOR_RESIZE);
  },

  /**
   * Called when `DESTROY_NODE` is fired to remove the node from props view if
   * it's currently selected.
   */
  _onDestroyNode: function (node) {
    if (this._currentNode && this._currentNode.id === node.id) {
      this.setCurrentAudioNode(null);
    }
  },

  _onCommandClick: function (e) {
    let node = this.getCurrentAudioNode();
    let button = e.target;
    let command = button.getAttribute("data-command");
    let checked = button.getAttribute("checked");

    if (button.getAttribute("disabled")) {
      return;
    }

    if (command === "bypass") {
      if (checked) {
        button.removeAttribute("checked");
        node.bypass(true);
      } else {
        button.setAttribute("checked", true);
        node.bypass(false);
      }
    }
  }
};
PK
!<S�,		:chrome/devtools/content/webaudioeditor/views/properties.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { VariablesView } = require("resource://devtools/client/shared/widgets/VariablesView.jsm");

const GENERIC_VARIABLES_VIEW_SETTINGS = {
  searchEnabled: false,
  editableValueTooltip: "",
  editableNameTooltip: "",
  preventDisableOnChange: true,
  preventDescriptorModifiers: false,
  eval: () => {}
};

/**
 * Functions handling the audio node inspector UI.
 */

var PropertiesView = {

  /**
   * Initialization function called when the tool starts up.
   */
  initialize: function () {
    this._onEval = this._onEval.bind(this);
    this._onNodeSet = this._onNodeSet.bind(this);

    window.on(EVENTS.UI_INSPECTOR_NODE_SET, this._onNodeSet);
    this._propsView = new VariablesView($("#properties-content"), GENERIC_VARIABLES_VIEW_SETTINGS);
    this._propsView.eval = this._onEval;
  },

  /**
   * Destruction function called when the tool cleans up.
   */
  destroy: function () {
    window.off(EVENTS.UI_INSPECTOR_NODE_SET, this._onNodeSet);
    this._propsView = null;
  },

  /**
   * Empties out the props view.
   */
  resetUI: function () {
    this._propsView.empty();
    this._currentNode = null;
  },

  /**
   * Internally sets the current audio node and rebuilds appropriate
   * views.
   */
  _setAudioNode: function (node) {
    this._currentNode = node;
    if (this._currentNode) {
      this._buildPropertiesView();
    }
  },

  /**
   * Reconstructs the `Properties` tab in the inspector
   * with the `this._currentNode` as it's source.
   */
  _buildPropertiesView: Task.async(function* () {
    let propsView = this._propsView;
    let node = this._currentNode;
    propsView.empty();

    let audioParamsScope = propsView.addScope("AudioParams");
    let props = yield node.getParams();

    // Disable AudioParams VariableView expansion
    // when there are no props i.e. AudioDestinationNode
    this._togglePropertiesView(!!props.length);

    props.forEach(({ param, value, flags }) => {
      let descriptor = {
        value: value,
        writable: !flags || !flags.readonly,
      };
      let item = audioParamsScope.addItem(param, descriptor);

      // No items should currently display a dropdown
      item.twisty = false;
    });

    audioParamsScope.expanded = true;

    window.emit(EVENTS.UI_PROPERTIES_TAB_RENDERED, node.id);
  }),

  /**
   * Toggles the display of the "empty" properties view when
   * node has no properties to display.
   */
  _togglePropertiesView: function (show) {
    let propsView = $("#properties-content");
    let emptyView = $("#properties-empty");
    (show ? propsView : emptyView).removeAttribute("hidden");
    (show ? emptyView : propsView).setAttribute("hidden", "true");
  },

  /**
   * Returns the scope for AudioParams in the
   * VariablesView.
   *
   * @return Scope
   */
  _getAudioPropertiesScope: function () {
    return this._propsView.getScopeAtIndex(0);
  },

  /**
   * Event handlers
   */

  /**
   * Called when the inspector view determines a node is selected.
   */
  _onNodeSet: function (id) {
    this._setAudioNode(gAudioNodes.get(id));
  },

  /**
   * Executed when an audio prop is changed in the UI.
   */
  _onEval: Task.async(function* (variable, value) {
    let ownerScope = variable.ownerView;
    let node = this._currentNode;
    let propName = variable.name;
    let error;

    if (!variable._initialDescriptor.writable) {
      error = new Error("Variable " + propName + " is not writable.");
    } else {
      // Cast value to proper type
      try {
        let number = parseFloat(value);
        if (!isNaN(number)) {
          value = number;
        } else {
          value = JSON.parse(value);
        }
        error = yield node.actor.setParam(propName, value);
      }
      catch (e) {
        error = e;
      }
    }

    // TODO figure out how to handle and display set prop errors
    // and enable `test/brorwser_wa_properties-view-edit.js`
    // Bug 994258
    if (!error) {
      ownerScope.get(propName).setGrip(value);
      window.emit(EVENTS.UI_SET_PARAM, node.id, propName, value);
    } else {
      window.emit(EVENTS.UI_SET_PARAM_ERROR, node.id, propName, value);
    }
  })
};
PK
!<C��
�
�
5chrome/devtools/content/webaudioeditor/views/utils.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";

/**
 * Takes an element in an SVG graph and iterates over
 * ancestors until it finds the graph node container. If not found,
 * returns null.
 */

function findGraphNodeParent(el) {
  // Some targets may not contain `classList` property
  if (!el.classList)
    return null;

  while (!el.classList.contains("nodes")) {
    if (el.classList.contains("audionode"))
      return el;
    else
      el = el.parentNode;
  }
  return null;
}

/**
 * Object for use with `mix` into a view.
 * Must have the following properties defined on the view:
 * - `el`
 * - `button`
 * - `_collapseString`
 * - `_expandString`
 * - `_toggleEvent`
 *
 * Optional properties on the view can be defined to specify default
 * visibility options.
 * - `_animated`
 * - `_delayed`
 */
var ToggleMixin = {

  bindToggle: function () {
    this._onToggle = this._onToggle.bind(this);
    this.button.addEventListener("mousedown", this._onToggle);
  },

  unbindToggle: function () {
    this.button.removeEventListener("mousedown", this._onToggle);
  },

  show: function () {
    this._viewController({ visible: true });
  },

  hide: function () {
    this._viewController({ visible: false });
  },

  hideImmediately: function () {
    this._viewController({ visible: false, delayed: false, animated: false });
  },

  /**
   * Returns a boolean indicating whether or not the view.
   * is currently being shown.
   */
  isVisible: function () {
    return !this.el.classList.contains("pane-collapsed");
  },

  /**
   * Toggles the visibility of the view.
   *
   * @param object visible
   *        - visible: boolean indicating whether the panel should be shown or not
   *        - animated: boolean indiciating whether the pane should be animated
   *        - delayed: boolean indicating whether the pane's opening should wait
   *                   a few cycles or not
   */
  _viewController: function ({ visible, animated, delayed }) {
    let flags = {
      visible: visible,
      animated: animated != null ? animated : !!this._animated,
      delayed: delayed != null ? delayed : !!this._delayed,
      callback: () => window.emit(this._toggleEvent, visible)
    };

    ViewHelpers.togglePane(flags, this.el);

    if (flags.visible) {
      this.button.classList.remove("pane-collapsed");
      this.button.setAttribute("tooltiptext", this._collapseString);
    }
    else {
      this.button.classList.add("pane-collapsed");
      this.button.setAttribute("tooltiptext", this._expandString);
    }
  },

  _onToggle: function () {
    this._viewController({ visible: !this.isVisible() });
  }
};
PK
!<5�0��9chrome/devtools/content/webaudioeditor/webaudioeditor.xul<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/content/shared/widgets/widgets.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/widgets.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/webaudioeditor.css" type="text/css"?>
<!DOCTYPE window [
  <!ENTITY % debuggerDTD SYSTEM "chrome://devtools/locale/webaudioeditor.dtd">
  %debuggerDTD;
]>

<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

  <script type="application/javascript"
          src="chrome://devtools/content/shared/theme-switching.js"/>

  <script type="application/javascript" src="chrome://devtools/content/shared/vendor/d3.js"/>
  <script type="application/javascript" src="chrome://devtools/content/shared/vendor/dagre-d3.js"/>
  <script type="application/javascript" src="includes.js"/>
  <script type="application/javascript" src="models.js"/>
  <script type="application/javascript" src="controller.js"/>
  <script type="application/javascript" src="views/utils.js"/>
  <script type="application/javascript" src="views/context.js"/>
  <script type="application/javascript" src="views/inspector.js"/>
  <script type="application/javascript" src="views/properties.js"/>
  <script type="application/javascript" src="views/automation.js"/>

  <vbox class="theme-body" flex="1">
    <hbox id="reload-notice"
          class="notice-container"
          align="center"
          pack="center"
          flex="1">
      <button id="requests-menu-reload-notice-button"
              class="devtools-toolbarbutton"
              standalone="true"
              label="&webAudioEditorUI.reloadNotice1;"
              oncommand="gFront.setup({ reload: true });"/>
      <label id="requests-menu-reload-notice-label"
             class="plain"
             value="&webAudioEditorUI.reloadNotice2;"/>
    </hbox>
    <hbox id="waiting-notice"
          class="notice-container devtools-throbber"
          align="center"
          pack="center"
          flex="1"
          hidden="true">
      <label id="requests-menu-waiting-notice-label"
             class="plain"
             value="&webAudioEditorUI.emptyNotice;"/>
    </hbox>

    <vbox id="content"
         flex="1"
         hidden="true">
      <toolbar id="web-audio-toolbar" class="devtools-toolbar">
        <spacer flex="1"></spacer>
        <toolbarbutton id="inspector-pane-toggle" class="devtools-toolbarbutton"
                       tabindex="0"/>
      </toolbar>
      <splitter class="devtools-horizontal-splitter"/>
      <box id="web-audio-content-pane"
           class="devtools-responsive-container"
           flex="1">
        <hbox flex="1">
          <box id="web-audio-graph" flex="1">
            <vbox flex="1">
              <svg id="graph-svg"
                  xmlns="http://www.w3.org/2000/svg"
                  xmlns:xlink="http://www.w3.org/1999/xlink">
                <g id="graph-target" transform="translate(20,20)"/>
              </svg>
            </vbox>
          </box>
        </hbox>
        <splitter id="inspector-splitter" class="devtools-side-splitter"/>
        <vbox id="web-audio-inspector" hidden="true">
          <deck id="web-audio-editor-details-pane" flex="1">
            <vbox id="web-audio-editor-details-pane-empty" flex="1">
              <label value="&webAudioEditorUI.inspectorEmpty;"></label>
            </vbox>
            <tabbox id="web-audio-editor-tabs"
                    class="devtools-sidebar-tabs"
                    handleCtrlTab="false">
              <toolbar id="audio-node-toolbar" class="devtools-toolbar">
                <hbox class="devtools-toolbarbutton-group">
                  <toolbarbutton class="bypass devtools-toolbarbutton"
                                 data-command="bypass"
                                 tabindex="0"/>
                </hbox>
              </toolbar>
              <tabs>
                <tab id="properties-tab"
                     label="&webAudioEditorUI.tab.properties2;"/>
                <!-- bug 1134036
                <tab id="automation-tab"
                     label="&webAudioEditorUI.tab.automation;"/>
                -->
              </tabs>
              <tabpanels flex="1">
                <!-- Properties Panel -->
                <tabpanel id="properties-tabpanel"
                          class="tabpanel-content">
                  <vbox id="properties-content" flex="1" hidden="true">
                  </vbox>
                  <vbox id="properties-empty" flex="1" hidden="true">
                    <label value="&webAudioEditorUI.propertiesEmpty;"></label>
                  </vbox>
                </tabpanel>

                <!-- Automation Panel -->
                <tabpanel id="automation-tabpanel"
                          class="tabpanel-content">
                  <vbox id="automation-content" flex="1" hidden="true">
                    <toolbar id="automation-param-toolbar" class="devtools-toolbar">
                      <hbox id="automation-param-toolbar-buttons" class="devtools-toolbarbutton-group">
                      </hbox>
                    </toolbar>
                    <box id="automation-graph-container" flex="1">
                      <canvas id="automation-graph"></canvas>
                    </box>
                    <vbox id="automation-no-events" flex="1" hidden="true">
                      <label value="&webAudioEditorUI.automationNoEvents;"></label>
                    </vbox>
                  </vbox>
                  <vbox id="automation-empty" flex="1" hidden="true">
                    <label value="&webAudioEditorUI.automationEmpty;"></label>
                  </vbox>
                </tabpanel>
              </tabpanels>
            </tabbox>
          </deck>
        </vbox>
      </box>
    </vbox>
  </vbox>

</window>
PK
!<�`��5chrome/devtools/content/webconsole/browserconsole.xul<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        id="devtools-webconsole"
        macanimationtype="document"
        fullscreenbutton="true"
        title=""
        windowtype="devtools:webconsole"
        width="900" height="350"
        persist="screenX screenY width height sizemode">
  <popupset></popupset>
  <iframe src="webconsole.html" flex="1"></iframe>
</window>PK
!<GJ|i��2chrome/devtools/content/webconsole/webconsole.html<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 dir="">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <link rel="stylesheet" href="chrome://devtools/skin/widgets.css"/>
    <link rel="stylesheet" href="chrome://devtools/skin/webconsole.css"/>
    <link rel="stylesheet" href="chrome://devtools/skin/components-frame.css"/>
    <link rel="stylesheet" href="resource://devtools/client/shared/components/reps/reps.css"/>
    <link rel="stylesheet" href="resource://devtools/client/shared/components/tabs/Tabs.css"/>
    <link rel="stylesheet" href="resource://devtools/client/shared/components/tabs/TabBar.css"/>
    <link rel="stylesheet" href="resource://devtools/client/shared/components/NotificationBox.css"/>
    <link rel="stylesheet" href="chrome://devtools/content/netmonitor/src/assets/styles/httpi.css"/>

    <script src="chrome://devtools/content/shared/theme-switching.js"></script>
    <script type="application/javascript"
            src="resource://devtools/client/webconsole/new-console-output/main.js"></script>
  </head>
  <body class="theme-sidebar" role="application">
    <div id="app-wrapper" class="theme-body">
      <div id="output-container" role="document" aria-live="polite"></div>
      <div id="jsterm-wrapper">
        <div id="webconsole-notificationbox"></div>
        <div class="jsterm-input-container" style="direction:ltr">
          <div class="jsterm-stack-node">
            <textarea class="jsterm-complete-node devtools-monospace"
                      tabindex="-1"></textarea>
            <textarea class="jsterm-input-node devtools-monospace"
                      rows="1" tabindex="0"
                      aria-autocomplete="list"></textarea>
          </div>
        </div>
      </div>
    </div>
  </body>
</html>
PK
!<��$4$41chrome/devtools/content/webconsole/webconsole.xul<?xml version="1.0" encoding="utf-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/. -->
<!DOCTYPE window [
<!ENTITY % webConsoleDTD SYSTEM "chrome://devtools/locale/webConsole.dtd">
%webConsoleDTD;
<!ENTITY % editMenuDTD SYSTEM "chrome://global/locale/editMenuOverlay.dtd">
%editMenuDTD;
]>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/widgets.css"
                 type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/webconsole.css"
                 type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/components-frame.css"
                 type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        id="devtools-webconsole"
        macanimationtype="document"
        fullscreenbutton="true"
        title="&window.title;"
        windowtype="devtools:webconsole"
        width="900" height="350"
        persist="screenX screenY width height sizemode">
  <script type="application/javascript"
          src="chrome://devtools/content/shared/theme-switching.js"/>
  <script type="application/javascript"
          src="resource://devtools/client/webconsole/new-console-output/main.js"/>
  <script type="text/javascript" src="chrome://global/content/globalOverlay.js"/>
  <script type="text/javascript" src="resource://devtools/client/webconsole/net/main.js"/>
  <script type="text/javascript"><![CDATA[
function goUpdateConsoleCommands() {
  goUpdateCommand("consoleCmd_openURL");
  goUpdateCommand("consoleCmd_copyURL");
}
  // ]]></script>

<script type="application/javascript" src="chrome://global/content/editMenuOverlay.js"/>
<commandset id="editMenuCommands">
  <commandset id="editMenuCommandSetAll" commandupdater="true" events="focus,select"
              oncommandupdate="goUpdateGlobalEditMenuItems()"/>
  <commandset id="editMenuCommandSetUndo" commandupdater="true" events="undo"
              oncommandupdate="goUpdateUndoEditMenuItems()"/>
  <commandset id="editMenuCommandSetPaste" commandupdater="true" events="clipboard"
              oncommandupdate="goUpdatePasteMenuItems()"/>
  <command id="cmd_undo" oncommand="goDoCommand('cmd_undo')"/>
  <command id="cmd_redo" oncommand="goDoCommand('cmd_redo')"/>
  <command id="cmd_cut" oncommand="goDoCommand('cmd_cut')"/>
  <command id="cmd_copy" oncommand="goDoCommand('cmd_copy')"/>
  <command id="cmd_paste" oncommand="goDoCommand('cmd_paste')"/>
  <command id="cmd_delete" oncommand="goDoCommand('cmd_delete')"/>
  <command id="cmd_selectAll" oncommand="goDoCommand('cmd_selectAll')"/>
  <command id="cmd_switchTextDirection" oncommand="goDoCommand('cmd_switchTextDirection');"/>
</commandset>

  <commandset id="consoleCommands"
              commandupdater="true"
              events="focus,select"
              oncommandupdate="goUpdateConsoleCommands();">
    <command id="consoleCmd_openURL"
             oncommand="goDoCommand('consoleCmd_openURL');"/>
    <command id="consoleCmd_copyURL"
             oncommand="goDoCommand('consoleCmd_copyURL');"/>
  </commandset>
  <keyset id="consoleKeys">
  </keyset>
  <!-- These key nodes are here only for show. The real bindings come from
       XBL, in platformHTMLBindings.xml. See bugs 57078 and 71779. -->

  <keyset id="editMenuKeys">
    <key id="key_undo" key="&undoCmd.key;" modifiers="accel" command="cmd_undo"/>
    <key id="key_redo" key="&undoCmd.key;" modifiers="accel,shift" command="cmd_redo"/>
    <key id="key_cut" key="&cutCmd.key;" modifiers="accel" command="cmd_cut"/>
    <key id="key_copy" key="&copyCmd.key;" modifiers="accel" command="cmd_copy"/>
    <key id="key_paste" key="&pasteCmd.key;" modifiers="accel" command="cmd_paste"/>
    <key id="key_delete" keycode="VK_DELETE" command="cmd_delete"/>
    <key id="key_selectAll" key="&selectAllCmd.key;" modifiers="accel" command="cmd_selectAll"/>
    <key id="key_find" key="&findCmd.key;" modifiers="accel" command="cmd_find"/>
    <key id="key_findAgain" key="&findAgainCmd.key;" modifiers="accel" command="cmd_findAgain"/>
    <key id="key_findPrevious" key="&findAgainCmd.key;" modifiers="shift,accel" command="cmd_findPrevious"/>
    <key id="key_findAgain2" keycode="&findAgainCmd.key2;" command="cmd_findAgain"/>
    <key id="key_findPrevious2" keycode="&findAgainCmd.key2;" modifiers="shift" command="cmd_findPrevious"/>
  </keyset>

  <popupset id="mainPopupSet">
    <menupopup id="output-contextmenu" onpopupshowing="goUpdateGlobalEditMenuItems()">
      <menuitem id="menu_openURL" label="&openURL.label;"
                accesskey="&openURL.accesskey;" command="consoleCmd_openURL"
                selection="network" selectionType="single"/>
      <menuitem id="menu_copyURL" label="&copyURLCmd.label;"
                accesskey="&copyURLCmd.accesskey;" command="consoleCmd_copyURL"
                selection="network" selectionType="single"/>
      <menuitem id="menu_openInVarView" label="&openInVarViewCmd.label;"
        accesskey="&openInVarViewCmd.accesskey;" disabled="true"/>
      <menuitem id="menu_storeAsGlobal" label="&storeAsGlobalVar.label;"
        accesskey="&storeAsGlobalVar.accesskey;"/>
      <menuitem id="cMenu_copy" label="&copyCmd.label;"
                accesskey="&copyCmd.accesskey;" command="cmd_copy"/>
      <menuitem id="cMenu_selectAll" label="&selectAllCmd.label;"
                accesskey="&selectAllCmd.accesskey;" command="cmd_selectAll"/>
    </menupopup>
  </popupset>

  <tooltip id="aHTMLTooltip" page="true"/>

  <box class="hud-outer-wrapper devtools-responsive-container theme-body" flex="1">
    <vbox class="hud-console-wrapper devtools-main-content" flex="1">
      <toolbar class="hud-console-filter-toolbar devtools-toolbar" mode="full">
        <toolbarbutton class="webconsole-clear-console-button devtools-toolbarbutton devtools-clear-icon"
                       tooltiptext="&btnClear.tooltip;"
                       accesskey="&btnClear.accesskey;"
                       tabindex="3"/>
        <hbox class="devtools-toolbarbutton-group">
          <toolbarbutton label="&btnPageNet.label;" type="menu-button"
                         category="net" class="devtools-toolbarbutton webconsole-filter-button"
                         tooltiptext="&btnPageNet.tooltip;"
                         accesskeyMacOSX="&btnPageNet.accesskeyMacOSX;"
                         accesskey="&btnPageNet.accesskey;"
                         tabindex="4">
            <menupopup id="net-contextmenu">
              <menuitem label="&btnConsoleErrors;" type="checkbox" autocheck="false"
                        prefKey="network"/>
              <menuitem label="&btnConsoleWarnings;" type="checkbox" autocheck="false"
                        prefKey="netwarn"/>
              <menuitem label="&btnConsoleXhr;" type="checkbox" autocheck="false"
                        prefKey="netxhr"/>
              <menuitem label="&btnConsoleLog;" type="checkbox" autocheck="false"
                        prefKey="networkinfo"/>
            </menupopup>
          </toolbarbutton>
          <toolbarbutton label="&btnPageCSS.label;" type="menu-button"
                         category="css" class="devtools-toolbarbutton webconsole-filter-button"
                         tooltiptext="&btnPageCSS.tooltip2;"
                         accesskey="&btnPageCSS.accesskey;"
                         tabindex="5">
            <menupopup id="css-contextmenu">
              <menuitem label="&btnConsoleErrors;" type="checkbox" autocheck="false"
                        prefKey="csserror"/>
              <menuitem label="&btnConsoleWarnings;" type="checkbox"
                        autocheck="false" prefKey="cssparser"/>
              <menuitem label="&btnConsoleReflows;" type="checkbox"
                        autocheck="false" prefKey="csslog"/>
            </menupopup>
          </toolbarbutton>
          <toolbarbutton label="&btnPageJS.label;" type="menu-button"
                         category="js" class="devtools-toolbarbutton webconsole-filter-button"
                         tooltiptext="&btnPageJS.tooltip;"
                         accesskey="&btnPageJS.accesskey;"
                         tabindex="6">
            <menupopup id="js-contextmenu">
              <menuitem label="&btnConsoleErrors;" type="checkbox"
                        autocheck="false" prefKey="exception"/>
              <menuitem label="&btnConsoleWarnings;" type="checkbox"
                        autocheck="false" prefKey="jswarn"/>
              <menuitem label="&btnConsoleLog;" type="checkbox"
                        autocheck="false" prefKey="jslog"/>
            </menupopup>
          </toolbarbutton>
          <toolbarbutton label="&btnPageSecurity.label;" type="menu-button"
                         category="security" class="devtools-toolbarbutton webconsole-filter-button"
                         tooltiptext="&btnPageSecurity.tooltip;"
                         accesskey="&btnPageSecurity.accesskey;"
                         tabindex="7">
            <menupopup id="security-contextmenu">
              <menuitem label="&btnConsoleErrors;" type="checkbox"
                        autocheck="false" prefKey="secerror"/>
              <menuitem label="&btnConsoleWarnings;" type="checkbox"
                        autocheck="false" prefKey="secwarn"/>
            </menupopup>
          </toolbarbutton>
          <toolbarbutton label="&btnPageLogging.label;" type="menu-button"
                         category="logging" class="devtools-toolbarbutton webconsole-filter-button"
                         tooltiptext="&btnPageLogging.tooltip;"
                         accesskey="&btnPageLogging.accesskey3;"
                         tabindex="8">
            <menupopup id="logging-contextmenu">
              <menuitem label="&btnConsoleErrors;" type="checkbox"
                        autocheck="false" prefKey="error"/>
              <menuitem label="&btnConsoleWarnings;" type="checkbox"
                        autocheck="false" prefKey="warn"/>
              <menuitem label="&btnConsoleInfo;" type="checkbox" autocheck="false"
                        prefKey="info"/>
              <menuitem label="&btnConsoleLog;" type="checkbox" autocheck="false"
                        prefKey="log"/>
              <menuseparator />
              <menuitem label="&btnConsoleSharedWorkers;" type="checkbox"
                        autocheck="false" prefKey="sharedworkers"/>
              <menuitem label="&btnConsoleServiceWorkers;" type="checkbox"
                        autocheck="false" prefKey="serviceworkers"/>
              <menuitem label="&btnConsoleWindowlessWorkers;" type="checkbox"
                        autocheck="false" prefKey="windowlessworkers"/>
            </menupopup>
          </toolbarbutton>
          <toolbarbutton label="&btnServerLogging.label;" type="menu-button"
                         category="server" class="devtools-toolbarbutton webconsole-filter-button"
                         tooltiptext="&btnServerLogging.tooltip;"
                         accesskey="&btnServerLogging.accesskey;"
                         tabindex="9">
            <menupopup id="server-logging-contextmenu">
              <menuitem label="&btnServerErrors;" type="checkbox"
                        autocheck="false" prefKey="servererror"/>
              <menuitem label="&btnServerWarnings;" type="checkbox"
                        autocheck="false" prefKey="serverwarn"/>
              <menuitem label="&btnServerInfo;" type="checkbox" autocheck="false"
                        prefKey="serverinfo"/>
              <menuitem label="&btnServerLog;" type="checkbox" autocheck="false"
                        prefKey="serverlog"/>
            </menupopup>
          </toolbarbutton>
        </hbox>

        <spacer flex="1"/>

        <textbox class="compact hud-filter-box devtools-filterinput" type="search"
                 placeholder="&filterOutput.placeholder;" tabindex="2"/>
      </toolbar>

      <hbox id="output-wrapper" flex="1" context="output-contextmenu" tooltip="aHTMLTooltip">
        <!-- Wrapper element to make scrolling in output-container much faster.
             See Bug 1237368 -->
        <div xmlns="http://www.w3.org/1999/xhtml">
          <div xmlns="http://www.w3.org/1999/xhtml" id="output-container"
               tabindex="0" role="document" aria-live="polite" />
        </div>
      </hbox>
      <notificationbox id="webconsole-notificationbox">
        <hbox class="jsterm-input-container" style="direction:ltr">
          <stack class="jsterm-stack-node" flex="1">
            <textbox class="jsterm-complete-node devtools-monospace"
                     multiline="true" rows="1" tabindex="-1"/>
            <textbox class="jsterm-input-node devtools-monospace"
                     multiline="true" rows="1" tabindex="0"
                     aria-autocomplete="list"/>
          </stack>
        </hbox>
      </notificationbox>
    </vbox>

    <splitter class="devtools-side-splitter"/>

    <tabbox id="webconsole-sidebar" class="devtools-sidebar-tabs" hidden="true" width="300">
      <tabs/>
      <tabpanels flex="1"/>
    </tabbox>
  </box>
</window>
PK
!<����
�
Schrome/devtools/modules/devtools/client/aboutdebugging/components/Aboutdebugging.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 { createFactory, 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 Services = require("Services");

const PanelMenu = createFactory(require("./PanelMenu"));

loader.lazyGetter(this, "AddonsPanel",
  () => createFactory(require("./addons/Panel")));
loader.lazyGetter(this, "TabsPanel",
  () => createFactory(require("./tabs/Panel")));
loader.lazyGetter(this, "WorkersPanel",
  () => createFactory(require("./workers/Panel")));

loader.lazyRequireGetter(this, "DebuggerClient",
  "devtools/shared/client/debugger-client", true);
loader.lazyRequireGetter(this, "Telemetry",
  "devtools/client/shared/telemetry");

const Strings = Services.strings.createBundle(
  "chrome://devtools/locale/aboutdebugging.properties");

const panels = [{
  id: "addons",
  name: Strings.GetStringFromName("addons"),
  icon: "chrome://devtools/skin/images/debugging-addons.svg",
  component: AddonsPanel
}, {
  id: "tabs",
  name: Strings.GetStringFromName("tabs"),
  icon: "chrome://devtools/skin/images/debugging-tabs.svg",
  component: TabsPanel
}, {
  id: "workers",
  name: Strings.GetStringFromName("workers"),
  icon: "chrome://devtools/skin/images/debugging-workers.svg",
  component: WorkersPanel
}];

const defaultPanelId = "addons";

class AboutDebuggingApp extends Component {
  static get propTypes() {
    return {
      client: PropTypes.instanceOf(DebuggerClient).isRequired,
      connect: PropTypes.object.isRequired,
      telemetry: PropTypes.instanceOf(Telemetry).isRequired
    };
  }

  constructor(props) {
    super(props);

    this.state = {
      selectedPanelId: defaultPanelId
    };

    this.onHashChange = this.onHashChange.bind(this);
    this.selectPanel = this.selectPanel.bind(this);
  }

  componentDidMount() {
    window.addEventListener("hashchange", this.onHashChange);
    this.onHashChange();
    this.props.telemetry.toolOpened("aboutdebugging");
  }

  componentWillUnmount() {
    window.removeEventListener("hashchange", this.onHashChange);
    this.props.telemetry.toolClosed("aboutdebugging");
    this.props.telemetry.destroy();
  }

  onHashChange() {
    this.setState({
      selectedPanelId: window.location.hash.substr(1) || defaultPanelId
    });
  }

  selectPanel(panelId) {
    window.location.hash = "#" + panelId;
  }

  render() {
    let { client, connect } = this.props;
    let { selectedPanelId } = this.state;
    let selectPanel = this.selectPanel;
    let selectedPanel = panels.find(p => p.id == selectedPanelId);
    let panel;

    if (selectedPanel) {
      panel = selectedPanel.component({ client, connect, id: selectedPanel.id });
    } else {
      panel = (
        dom.div({ className: "error-page" },
          dom.h1({ className: "header-name" },
            Strings.GetStringFromName("pageNotFound")
          ),
          dom.h4({ className: "error-page-details" },
            Strings.formatStringFromName("doesNotExist", [selectedPanelId], 1))
        )
      );
    }

    return dom.div({ className: "app" },
      PanelMenu({ panels, selectedPanelId, selectPanel }),
      dom.div({ className: "main-content" }, panel)
    );
  }
}

module.exports = AboutDebuggingApp;
PK
!<�k0�Pchrome/devtools/modules/devtools/client/aboutdebugging/components/PanelHeader.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");

class PanelHeader extends Component {
  static get propTypes() {
    return {
      id: PropTypes.string.isRequired,
      name: PropTypes.string.isRequired
    };
  }

  render() {
    let { name, id } = this.props;

    return dom.div({ className: "header" },
      dom.h1({ id, className: "header-name" }, name));
  }
}

module.exports = PanelHeader;
PK
!<f��{{Nchrome/devtools/modules/devtools/client/aboutdebugging/components/PanelMenu.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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 PanelMenuEntry = createFactory(require("./PanelMenuEntry"));

class PanelMenu extends Component {
  static get propTypes() {
    return {
      panels: PropTypes.arrayOf(PropTypes.shape({
        id: PropTypes.string.isRequired,
        name: PropTypes.string.isRequired,
        icon: PropTypes.string.isRequired,
        component: PropTypes.func.isRequired
      })).isRequired,
      selectPanel: PropTypes.func.isRequired,
      selectedPanelId: PropTypes.string
    };
  }

  render() {
    let { panels, selectedPanelId, selectPanel } = this.props;
    let panelLinks = panels.map(({ id, name, icon }) => {
      let selected = id == selectedPanelId;
      return PanelMenuEntry({
        id,
        name,
        icon,
        selected,
        selectPanel
      });
    });

    // "categories" id used for styling purposes
    return dom.div({ id: "categories", role: "tablist" }, panelLinks);
  }
}

module.exports = PanelMenu;
PK
!<̻Y���Schrome/devtools/modules/devtools/client/aboutdebugging/components/PanelMenuEntry.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");

class PanelMenuEntry extends Component {
  static get propTypes() {
    return {
      icon: PropTypes.string.isRequired,
      id: PropTypes.string.isRequired,
      name: PropTypes.string.isRequired,
      selected: PropTypes.bool,
      selectPanel: PropTypes.func.isRequired
    };
  }

  constructor(props) {
    super(props);
    this.onClick = this.onClick.bind(this);
  }

  onClick() {
    this.props.selectPanel(this.props.id);
  }

  render() {
    let { id, name, icon, selected } = this.props;

    // Here .category, .category-icon, .category-name classnames are used to
    // apply common styles defined.
    let className = "category" + (selected ? " selected" : "");
    return dom.button({
      "aria-selected": selected,
      "aria-controls": id + "-panel",
      className,
      onClick: this.onClick,
      tabIndex: "0",
      role: "tab" },
    dom.img({ className: "category-icon", src: icon, role: "presentation" }),
    dom.div({ className: "category-name" }, name));
  }
}

module.exports = PanelMenuEntry;
PK
!<Y��W��Ochrome/devtools/modules/devtools/client/aboutdebugging/components/TargetList.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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 Services = require("Services");

loader.lazyRequireGetter(this, "DebuggerClient",
  "devtools/shared/client/debugger-client", true);

const Strings = Services.strings.createBundle(
  "chrome://devtools/locale/aboutdebugging.properties");

const LocaleCompare = (a, b) => {
  return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
};

class TargetList extends Component {
  static get propTypes() {
    return {
      client: PropTypes.instanceOf(DebuggerClient).isRequired,
      connect: PropTypes.object,
      debugDisabled: PropTypes.bool,
      error: PropTypes.node,
      id: PropTypes.string.isRequired,
      name: PropTypes.string,
      sort: PropTypes.bool,
      targetClass: PropTypes.func.isRequired,
      targets: PropTypes.arrayOf(PropTypes.object).isRequired
    };
  }

  render() {
    let {
      client,
      connect,
      debugDisabled,
      error,
      targetClass,
      targets,
      sort
    } = this.props;

    if (sort) {
      targets = targets.sort(LocaleCompare);
    }
    targets = targets.map(target => {
      return targetClass({ client, connect, target, debugDisabled });
    });

    let content = "";
    if (error) {
      content = error;
    } else if (targets.length > 0) {
      content = dom.ul({ className: "target-list" }, targets);
    } else {
      content = dom.p(null, Strings.GetStringFromName("nothing"));
    }

    return dom.div({ id: this.props.id, className: "targets" },
      dom.h2(null, this.props.name), content);
  }
}

module.exports = TargetList;
PK
!<��T��Tchrome/devtools/modules/devtools/client/aboutdebugging/components/addons/Controls.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 */
/* globals AddonManager */

"use strict";

loader.lazyImporter(this, "AddonManager",
  "resource://gre/modules/AddonManager.jsm");

const { Cc, Ci } = require("chrome");
const { createFactory, 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 Services = require("Services");
const AddonsInstallError = createFactory(require("./InstallError"));

const Strings = Services.strings.createBundle(
  "chrome://devtools/locale/aboutdebugging.properties");

const MORE_INFO_URL = "https://developer.mozilla.org/docs/Tools" +
                      "/about:debugging#Enabling_add-on_debugging";

class AddonsControls extends Component {
  static get propTypes() {
    return {
      debugDisabled: PropTypes.bool
    };
  }

  constructor(props) {
    super(props);

    this.state = {
      installError: null,
    };

    this.onEnableAddonDebuggingChange = this.onEnableAddonDebuggingChange.bind(this);
    this.loadAddonFromFile = this.loadAddonFromFile.bind(this);
    this.retryInstall = this.retryInstall.bind(this);
    this.installAddon = this.installAddon.bind(this);
  }

  onEnableAddonDebuggingChange(event) {
    let enabled = event.target.checked;
    Services.prefs.setBoolPref("devtools.chrome.enabled", enabled);
    Services.prefs.setBoolPref("devtools.debugger.remote-enabled", enabled);
  }

  loadAddonFromFile() {
    this.setState({ installError: null });
    let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
    fp.init(window,
      Strings.GetStringFromName("selectAddonFromFile2"),
      Ci.nsIFilePicker.modeOpen);
    fp.open(res => {
      if (res == Ci.nsIFilePicker.returnCancel || !fp.file) {
        return;
      }
      let file = fp.file;
      // AddonManager.installTemporaryAddon accepts either
      // addon directory or final xpi file.
      if (!file.isDirectory() &&
          !file.leafName.endsWith(".xpi") && !file.leafName.endsWith(".zip")) {
        file = file.parent;
      }

      this.installAddon(file);
    });
  }

  retryInstall() {
    this.setState({ installError: null });
    this.installAddon(this.state.lastInstallErrorFile);
  }

  installAddon(file) {
    AddonManager.installTemporaryAddon(file)
      .then(() => {
        this.setState({ lastInstallErrorFile: null });
      })
      .catch(e => {
        console.error(e);
        this.setState({ installError: e.message, lastInstallErrorFile: file });
      });
  }

  render() {
    let { debugDisabled } = this.props;

    return dom.div({ className: "addons-top" },
      dom.div({ className: "addons-controls" },
        dom.div({ className: "addons-options toggle-container-with-text" },
          dom.input({
            id: "enable-addon-debugging",
            type: "checkbox",
            checked: !debugDisabled,
            onChange: this.onEnableAddonDebuggingChange,
            role: "checkbox",
          }),
          dom.label({
            className: "addons-debugging-label",
            htmlFor: "enable-addon-debugging",
            title: Strings.GetStringFromName("addonDebugging.tooltip")
          }, Strings.GetStringFromName("addonDebugging.label")),
          dom.a({ href: MORE_INFO_URL, target: "_blank" },
            Strings.GetStringFromName("addonDebugging.learnMore")
          ),
        ),
        dom.button({
          id: "load-addon-from-file",
          onClick: this.loadAddonFromFile,
        }, Strings.GetStringFromName("loadTemporaryAddon"))
      ),
      AddonsInstallError({
        error: this.state.installError,
        retryInstall: this.retryInstall,
      }));
  }
}

module.exports = AddonsControls;
PK
!<H�8�Xchrome/devtools/modules/devtools/client/aboutdebugging/components/addons/InstallError.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 } = 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 Services = require("Services");

const Strings = Services.strings.createBundle(
  "chrome://devtools/locale/aboutdebugging.properties");

class AddonsInstallError extends Component {
  static get propTypes() {
    return {
      error: PropTypes.string,
      retryInstall: PropTypes.func,
    };
  }

  render() {
    if (!this.props.error) {
      return null;
    }
    let text = `There was an error during installation: ${this.props.error}`;
    return dom.div(
      { className: "addons-install-error" },
      dom.span(
        {},
        dom.div({ className: "warning" }),
        dom.span({}, text),
      ),
      dom.button(
        { className: "addons-install-retry", onClick: this.props.retryInstall },
        Strings.GetStringFromName("retryTemporaryInstall")));
  }
}

module.exports = AddonsInstallError;
PK
!<#q3>>Qchrome/devtools/modules/devtools/client/aboutdebugging/components/addons/Panel.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 } = require("resource://gre/modules/AddonManager.jsm");
const { Management } = require("resource://gre/modules/Extension.jsm");
const { createFactory, 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 Services = require("Services");

const AddonsControls = createFactory(require("./Controls"));
const AddonTarget = createFactory(require("./Target"));
const PanelHeader = createFactory(require("../PanelHeader"));
const TargetList = createFactory(require("../TargetList"));

loader.lazyRequireGetter(this, "DebuggerClient",
  "devtools/shared/client/debugger-client", true);

const Strings = Services.strings.createBundle(
  "chrome://devtools/locale/aboutdebugging.properties");

const ExtensionIcon = "chrome://mozapps/skin/extensions/extensionGeneric.svg";
const CHROME_ENABLED_PREF = "devtools.chrome.enabled";
const REMOTE_ENABLED_PREF = "devtools.debugger.remote-enabled";
const WEB_EXT_URL = "https://developer.mozilla.org/Add-ons" +
                    "/WebExtensions/Getting_started_with_web-ext";

class AddonsPanel extends Component {
  static get propTypes() {
    return {
      client: PropTypes.instanceOf(DebuggerClient).isRequired,
      connect: PropTypes.object,
      id: PropTypes.string.isRequired
    };
  }

  constructor(props) {
    super(props);

    this.state = {
      extensions: [],
      debugDisabled: false,
    };

    this.updateDebugStatus = this.updateDebugStatus.bind(this);
    this.updateAddonsList = this.updateAddonsList.bind(this);
    this.onInstalled = this.onInstalled.bind(this);
    this.onUninstalled = this.onUninstalled.bind(this);
    this.onEnabled = this.onEnabled.bind(this);
    this.onDisabled = this.onDisabled.bind(this);
  }

  componentDidMount() {
    AddonManager.addAddonListener(this);
    // Listen to startup since that's when errors and warnings
    // get populated on the extension.
    Management.on("startup", this.updateAddonsList);

    Services.prefs.addObserver(CHROME_ENABLED_PREF,
      this.updateDebugStatus);
    Services.prefs.addObserver(REMOTE_ENABLED_PREF,
      this.updateDebugStatus);

    this.updateDebugStatus();
    this.updateAddonsList();
  }

  componentWillUnmount() {
    AddonManager.removeAddonListener(this);
    Management.off("startup", this.updateAddonsList);

    Services.prefs.removeObserver(CHROME_ENABLED_PREF,
      this.updateDebugStatus);
    Services.prefs.removeObserver(REMOTE_ENABLED_PREF,
      this.updateDebugStatus);
  }

  updateDebugStatus() {
    let debugDisabled =
      !Services.prefs.getBoolPref(CHROME_ENABLED_PREF) ||
      !Services.prefs.getBoolPref(REMOTE_ENABLED_PREF);

    this.setState({ debugDisabled });
  }

  updateAddonsList() {
    this.props.client.listAddons()
      .then(({addons}) => {
        let extensions = addons.filter(addon => addon.debuggable).map(addon => {
          return {
            addonActor: addon.actor,
            addonID: addon.id,
            // Forward the whole addon actor form for potential remote debugging.
            form: addon,
            icon: addon.iconURL || ExtensionIcon,
            manifestURL: addon.manifestURL,
            name: addon.name,
            temporarilyInstalled: addon.temporarilyInstalled,
            url: addon.url,
            warnings: addon.warnings,
          };
        });

        this.setState({ extensions });
      }, error => {
        throw new Error("Client error while listing addons: " + error);
      });
  }

  /**
   * Mandatory callback as AddonManager listener.
   */
  onInstalled() {
    this.updateAddonsList();
  }

  /**
   * Mandatory callback as AddonManager listener.
   */
  onUninstalled() {
    this.updateAddonsList();
  }

  /**
   * Mandatory callback as AddonManager listener.
   */
  onEnabled() {
    this.updateAddonsList();
  }

  /**
   * Mandatory callback as AddonManager listener.
   */
  onDisabled() {
    this.updateAddonsList();
  }

  render() {
    let { client, connect, id } = this.props;
    let { debugDisabled, extensions: targets } = this.state;
    let installedName = Strings.GetStringFromName("extensions");
    let temporaryName = Strings.GetStringFromName("temporaryExtensions");
    let targetClass = AddonTarget;

    const installedTargets = targets.filter((target) => !target.temporarilyInstalled);
    const temporaryTargets = targets.filter((target) => target.temporarilyInstalled);

    return dom.div({
      id: id + "-panel",
      className: "panel",
      role: "tabpanel",
      "aria-labelledby": id + "-header"
    },
    PanelHeader({
      id: id + "-header",
      name: Strings.GetStringFromName("addons")
    }),
    AddonsControls({ debugDisabled }),
    dom.div({ id: "temporary-addons" },
      TargetList({
        id: "temporary-extensions",
        name: temporaryName,
        targets: temporaryTargets,
        client,
        connect,
        debugDisabled,
        targetClass,
        sort: true
      }),
      dom.div({ className: "addons-tip"},
        dom.div({ className: "addons-tip-icon"}),
        dom.span({
          className: "addons-web-ext-tip",
        }, Strings.GetStringFromName("webExtTip")),
        dom.a({ href: WEB_EXT_URL, target: "_blank" },
          Strings.GetStringFromName("webExtTip.learnMore")
        )
      )
    ),
    dom.div({ id: "addons" },
      TargetList({
        id: "extensions",
        name: installedName,
        targets: installedTargets,
        client,
        connect,
        debugDisabled,
        targetClass,
        sort: true
      })
    ));
  }
}

module.exports = AddonsPanel;
PK
!<�L�Rchrome/devtools/modules/devtools/client/aboutdebugging/components/addons/Target.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 } = 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 {
  debugLocalAddon,
  debugRemoteAddon,
  isLegacyTemporaryExtension,
  isTemporaryID,
  parseFileUri,
  uninstallAddon
} = require("../../modules/addon");
const Services = require("Services");

loader.lazyImporter(this, "BrowserToolboxProcess",
  "resource://devtools/client/framework/ToolboxProcess.jsm");

loader.lazyRequireGetter(this, "DebuggerClient",
  "devtools/shared/client/debugger-client", true);

const Strings = Services.strings.createBundle(
  "chrome://devtools/locale/aboutdebugging.properties");

const TEMP_ID_URL = "https://developer.mozilla.org/Add-ons" +
                    "/WebExtensions/WebExtensions_and_the_Add-on_ID";
const LEGACY_WARNING_URL = "https://wiki.mozilla.org/Add-ons/Future_of_Bootstrap";

function filePathForTarget(target) {
  // Only show file system paths, and only for temporarily installed add-ons.
  if (!target.temporarilyInstalled || !target.url || !target.url.startsWith("file://")) {
    return [];
  }
  let path = parseFileUri(target.url);
  return [
    dom.dt(
      { className: "addon-target-info-label" },
      Strings.GetStringFromName("location")),
    // Wrap the file path in a span so we can do some RTL/LTR swapping to get
    // the ellipsis on the left.
    dom.dd(
      { className: "addon-target-info-content file-path" },
      dom.span({ className: "file-path-inner", title: path }, path),
    ),
  ];
}

function addonIDforTarget(target) {
  return [
    dom.dt(
      { className: "addon-target-info-label" },
      Strings.GetStringFromName("extensionID"),
    ),
    dom.dd(
      { className: "addon-target-info-content extension-id" },
      dom.span(
        { title: target.addonID },
        target.addonID
      )
    ),
  ];
}

function internalIDForTarget(target) {
  if (!target.manifestURL) {
    return [];
  }
  // Strip off the protocol and rest, leaving us with just the UUID.
  let uuid = /moz-extension:\/\/([^/]*)/.exec(target.manifestURL)[1];
  return [
    dom.dt(
      { className: "addon-target-info-label" },
      Strings.GetStringFromName("internalUUID"),
    ),
    dom.dd(
      { className: "addon-target-info-content internal-uuid" },
      dom.span(
        { title: uuid },
        uuid
      ),
      dom.span(
        { className: "addon-target-info-more" },
        dom.a(
          { href: target.manifestURL, target: "_blank", className: "manifest-url" },
          Strings.GetStringFromName("manifestURL"),
        ),
      )
    ),
  ];
}

function showMessages(target) {
  const messages = [
    ...warningMessages(target),
    ...infoMessages(target),
  ];
  if (messages.length > 0) {
    return dom.ul(
      { className: "addon-target-messages" },
      ...messages);
  }
  return null;
}

function infoMessages(target) {
  const messages = [];
  if (isTemporaryID(target.addonID)) {
    messages.push(dom.li(
      { className: "addon-target-info-message addon-target-message" },
      Strings.GetStringFromName("temporaryID"),
      " ",
      dom.a({ href: TEMP_ID_URL, className: "temporary-id-url", target: "_blank" },
        Strings.GetStringFromName("temporaryID.learnMore")
      )));
  }

  return messages;
}

function warningMessages(target) {
  let messages = [];

  if (isLegacyTemporaryExtension(target.form)) {
    messages.push(dom.li(
      {
        className: "addon-target-warning-message addon-target-message"
      },
      Strings.GetStringFromName("legacyExtensionWarning"),
      " ",
      dom.a(
        {
          href: LEGACY_WARNING_URL,
          target: "_blank"
        },
        Strings.GetStringFromName("legacyExtensionWarning.learnMore"))
    ));
  }

  let warnings = target.warnings || [];
  messages = messages.concat(warnings.map((warning) => {
    return dom.li(
      { className: "addon-target-warning-message addon-target-message" },
      warning);
  }));

  return messages;
}

class AddonTarget extends Component {
  static get propTypes() {
    return {
      client: PropTypes.instanceOf(DebuggerClient).isRequired,
      connect: PropTypes.object,
      debugDisabled: PropTypes.bool,
      target: PropTypes.shape({
        addonActor: PropTypes.string.isRequired,
        addonID: PropTypes.string.isRequired,
        form: PropTypes.object.isRequired,
        icon: PropTypes.string,
        name: PropTypes.string.isRequired,
        temporarilyInstalled: PropTypes.bool,
        url: PropTypes.string,
        warnings: PropTypes.array,
      }).isRequired
    };
  }

  constructor(props) {
    super(props);
    this.debug = this.debug.bind(this);
    this.uninstall = this.uninstall.bind(this);
    this.reload = this.reload.bind(this);
  }

  debug() {
    let { client, connect, target } = this.props;

    if (connect.type === "REMOTE") {
      debugRemoteAddon(target.form, client);
    } else if (connect.type === "LOCAL") {
      debugLocalAddon(target.addonID);
    }
  }

  uninstall() {
    let { target } = this.props;
    uninstallAddon(target.addonID);
  }

  async reload() {
    let { client, target } = this.props;
    let { AboutDebugging } = window;
    try {
      await client.request({
        to: target.addonActor,
        type: "reload"
      });
      AboutDebugging.emit("addon-reload");
    } catch (e) {
      throw new Error("Error reloading addon " + target.addonID + ": " + e.message);
    }
  }

  render() {
    let { target, debugDisabled } = this.props;

    return dom.li(
      { className: "addon-target-container", "data-addon-id": target.addonID },
      dom.div({ className: "target" },
        dom.img({
          className: "target-icon",
          role: "presentation",
          src: target.icon
        }),
        dom.span(
          { className: "target-name addon-target-name", title: target.name },
          target.name)
      ),
      showMessages(target),
      dom.dl(
        { className: "addon-target-info" },
        ...filePathForTarget(target),
        ...addonIDforTarget(target),
        ...internalIDForTarget(target),
      ),
      dom.div({className: "addon-target-actions"},
        dom.button({
          className: "debug-button addon-target-button",
          onClick: this.debug,
          disabled: debugDisabled,
        }, Strings.GetStringFromName("debug")),
        target.temporarilyInstalled
          ? dom.button({
            className: "reload-button addon-target-button",
            onClick: this.reload,
          }, Strings.GetStringFromName("reload"))
          : null,
        target.temporarilyInstalled
          ? dom.button({
            className: "uninstall-button addon-target-button",
            onClick: this.uninstall,
          }, Strings.GetStringFromName("remove"))
          : null,
      ),
    );
  }
}

module.exports = AddonTarget;
PK
!<�)7�G
G
Ochrome/devtools/modules/devtools/client/aboutdebugging/components/tabs/Panel.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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("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 Services = require("Services");

const PanelHeader = createFactory(require("../PanelHeader"));
const TargetList = createFactory(require("../TargetList"));
const TabTarget = createFactory(require("./Target"));

loader.lazyRequireGetter(this, "DebuggerClient",
  "devtools/shared/client/debugger-client", true);

const Strings = Services.strings.createBundle(
  "chrome://devtools/locale/aboutdebugging.properties");

class TabsPanel extends Component {
  static get propTypes() {
    return {
      client: PropTypes.instanceOf(DebuggerClient).isRequired,
      connect: PropTypes.object,
      id: PropTypes.string.isRequired
    };
  }

  constructor(props) {
    super(props);

    this.state = {
      tabs: []
    };

    this.update = this.update.bind(this);
  }

  componentDidMount() {
    let { client } = this.props;
    client.addListener("tabListChanged", this.update);
    this.update();
  }

  componentWillUnmount() {
    let { client } = this.props;
    client.removeListener("tabListChanged", this.update);
  }

  async update() {
    let { tabs } = await this.props.client.mainRoot.listTabs({ favicons: true });

    // Filter out closed tabs (represented as `null`).
    tabs = tabs.filter(tab => !!tab);

    for (let tab of tabs) {
      if (tab.favicon) {
        let base64Favicon = btoa(String.fromCharCode.apply(String, tab.favicon));
        tab.icon = "data:image/png;base64," + base64Favicon;
      } else {
        tab.icon = "chrome://devtools/skin/images/globe.svg";
      }
    }

    this.setState({ tabs });
  }

  render() {
    let { client, connect, id } = this.props;
    let { tabs } = this.state;

    return dom.div({
      id: id + "-panel",
      className: "panel",
      role: "tabpanel",
      "aria-labelledby": id + "-header"
    },
    PanelHeader({
      id: id + "-header",
      name: Strings.GetStringFromName("tabs")
    }),
    dom.div({},
      TargetList({
        client,
        connect,
        id: "tabs",
        name: Strings.GetStringFromName("tabs"),
        sort: false,
        targetClass: TabTarget,
        targets: tabs
      })
    ));
  }
}

module.exports = TabsPanel;
PK
!<�fN_��Pchrome/devtools/modules/devtools/client/aboutdebugging/components/tabs/Target.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 } = 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 Services = require("Services");

const Strings = Services.strings.createBundle(
  "chrome://devtools/locale/aboutdebugging.properties");

class TabTarget extends Component {
  static get propTypes() {
    return {
      connect: PropTypes.object,
      target: PropTypes.shape({
        icon: PropTypes.string,
        outerWindowID: PropTypes.number.isRequired,
        title: PropTypes.string,
        url: PropTypes.string.isRequired
      }).isRequired
    };
  }

  constructor(props) {
    super(props);
    this.debug = this.debug.bind(this);
  }

  debug() {
    let { target, connect } = this.props;
    let url = "about:devtools-toolbox?type=tab&id=" + target.outerWindowID;
    if (connect.type == "REMOTE") {
      let {host, port} = connect.params;
      url += `&host=${encodeURIComponent(host)}&port=${encodeURIComponent(port)}`;
    }
    window.open(url);
  }

  render() {
    let { target } = this.props;

    return dom.div({ className: "target-container" },
      dom.img({
        className: "target-icon",
        role: "presentation",
        src: target.icon
      }),
      dom.div({ className: "target" },
        // If the title is empty, display the url instead.
        dom.div({ className: "target-name", title: target.url },
          target.title || target.url)
      ),
      dom.button({
        className: "debug-button",
        onClick: this.debug,
      }, Strings.GetStringFromName("debug"))
    );
  }
}

module.exports = TabTarget;
PK
!<����kk]chrome/devtools/modules/devtools/client/aboutdebugging/components/workers/MultiE10sWarning.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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";

loader.lazyImporter(this, "PrivateBrowsingUtils",
  "resource://gre/modules/PrivateBrowsingUtils.jsm");
const { Component } = require("devtools/client/shared/vendor/react");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const Services = require("Services");
const { Ci } = require("chrome");

loader.lazyImporter(this, "PrivateBrowsingUtils",
  "resource://gre/modules/PrivateBrowsingUtils.jsm");

loader.lazyRequireGetter(this, "DebuggerClient",
  "devtools/shared/client/debugger-client", true);

const Strings = Services.strings.createBundle("chrome://devtools/locale/aboutdebugging.properties");
const MULTI_OPT_OUT_PREF = "dom.ipc.multiOptOut";

class multiE10SWarning extends Component {
  constructor(props) {
    super(props);
    this.onUpdatePreferenceClick = this.onUpdatePreferenceClick.bind(this);
  }

  onUpdatePreferenceClick() {
    let message = Strings.GetStringFromName("multiProcessWarningConfirmUpdate2");
    if (window.confirm(message)) {
      // Disable multi until at least the next experiment.
      Services.prefs.setIntPref(MULTI_OPT_OUT_PREF,
                                Services.appinfo.E10S_MULTI_EXPERIMENT);
      // Restart the browser.
      Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart);
    }
  }

  render() {
    return dom.div(
      {
        className: "service-worker-multi-process"
      },
      dom.div(
        {},
        dom.div({ className: "warning" }),
        dom.b({}, Strings.GetStringFromName("multiProcessWarningTitle"))
      ),
      dom.div(
        {},
        Strings.GetStringFromName("multiProcessWarningMessage2")
      ),
      dom.button(
        {
          className: "update-button",
          onClick: this.onUpdatePreferenceClick,
        },
        Strings.GetStringFromName("multiProcessWarningUpdateLink2")
      )
    );
  }
}

module.exports = multiE10SWarning;
PK
!<�"J5$$Rchrome/devtools/modules/devtools/client/aboutdebugging/components/workers/Panel.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 window */
"use strict";

loader.lazyImporter(this, "PrivateBrowsingUtils",
  "resource://gre/modules/PrivateBrowsingUtils.jsm");

const { Ci } = require("chrome");
const { Component, createFactory } = 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 { getWorkerForms } = require("../../modules/worker");
const Services = require("Services");

const PanelHeader = createFactory(require("../PanelHeader"));
const TargetList = createFactory(require("../TargetList"));
const WorkerTarget = createFactory(require("./Target"));
const MultiE10SWarning = createFactory(require("./MultiE10sWarning"));
const ServiceWorkerTarget = createFactory(require("./ServiceWorkerTarget"));

loader.lazyImporter(this, "PrivateBrowsingUtils",
  "resource://gre/modules/PrivateBrowsingUtils.jsm");

loader.lazyRequireGetter(this, "DebuggerClient",
  "devtools/shared/client/debugger-client", true);

const Strings = Services.strings.createBundle(
  "chrome://devtools/locale/aboutdebugging.properties");

const WorkerIcon = "chrome://devtools/skin/images/debugging-workers.svg";
const MORE_INFO_URL = "https://developer.mozilla.org/en-US/docs/Tools/about%3Adebugging" +
                      "#Service_workers_not_compatible";
const PROCESS_COUNT_PREF = "dom.ipc.processCount";
const MULTI_OPTOUT_PREF = "dom.ipc.multiOptOut";

class WorkersPanel extends Component {
  static get propTypes() {
    return {
      client: PropTypes.instanceOf(DebuggerClient).isRequired,
      id: PropTypes.string.isRequired
    };
  }

  constructor(props) {
    super(props);

    this.updateMultiE10S = this.updateMultiE10S.bind(this);
    this.updateWorkers = this.updateWorkers.bind(this);
    this.getRegistrationForWorker = this.getRegistrationForWorker.bind(this);
    this.isE10S = this.isE10S.bind(this);
    this.renderServiceWorkersError = this.renderServiceWorkersError.bind(this);

    this.state = this.initialState;
  }

  componentDidMount() {
    let client = this.props.client;
    client.addListener("workerListChanged", this.updateWorkers);
    client.addListener("serviceWorkerRegistrationListChanged", this.updateWorkers);
    client.addListener("processListChanged", this.updateWorkers);
    client.addListener("registration-changed", this.updateWorkers);

    // Some notes about these observers:
    // - nsIPrefBranch.addObserver observes prefixes. In reality, watching
    //   PROCESS_COUNT_PREF watches two separate prefs:
    //   dom.ipc.processCount *and* dom.ipc.processCount.web. Because these
    //   are the two ways that we control the number of content processes,
    //   that works perfectly fine.
    // - The user might opt in or out of multi by setting the multi opt out
    //   pref. That affects whether we need to show our warning, so we need to
    //   update our state when that pref changes.
    // - In all cases, we don't have to manually check which pref changed to
    //   what. The platform code in nsIXULRuntime.maxWebProcessCount does all
    //   of that for us.
    Services.prefs.addObserver(PROCESS_COUNT_PREF, this.updateMultiE10S);
    Services.prefs.addObserver(MULTI_OPTOUT_PREF, this.updateMultiE10S);

    this.updateMultiE10S();
    this.updateWorkers();
  }

  componentWillUnmount() {
    let client = this.props.client;
    client.removeListener("processListChanged", this.updateWorkers);
    client.removeListener("serviceWorkerRegistrationListChanged", this.updateWorkers);
    client.removeListener("workerListChanged", this.updateWorkers);
    client.removeListener("registration-changed", this.updateWorkers);

    Services.prefs.removeObserver(PROCESS_COUNT_PREF, this.updateMultiE10S);
    Services.prefs.removeObserver(MULTI_OPTOUT_PREF, this.updateMultiE10S);
  }

  get initialState() {
    return {
      workers: {
        service: [],
        shared: [],
        other: []
      },
      processCount: 1,
    };
  }

  updateMultiE10S() {
    // We watch the pref but set the state based on
    // nsIXULRuntime.maxWebProcessCount.
    let processCount = Services.appinfo.maxWebProcessCount;
    this.setState({ processCount });
  }

  updateWorkers() {
    let workers = this.initialState.workers;

    getWorkerForms(this.props.client).then(forms => {
      forms.registrations.forEach(form => {
        workers.service.push({
          icon: WorkerIcon,
          name: form.url,
          url: form.url,
          scope: form.scope,
          fetch: form.fetch,
          registrationActor: form.actor,
          active: form.active
        });
      });

      forms.workers.forEach(form => {
        let worker = {
          icon: WorkerIcon,
          name: form.url,
          url: form.url,
          workerActor: form.actor
        };
        switch (form.type) {
          case Ci.nsIWorkerDebugger.TYPE_SERVICE:
            let registration = this.getRegistrationForWorker(form, workers.service);
            if (registration) {
              // XXX: Race, sometimes a ServiceWorkerRegistrationInfo doesn't
              // have a scriptSpec, but its associated WorkerDebugger does.
              if (!registration.url) {
                registration.name = registration.url = form.url;
              }
              registration.workerActor = form.actor;
            } else {
              worker.fetch = form.fetch;

              // If a service worker registration could not be found, this means we are in
              // e10s, and registrations are not forwarded to other processes until they
              // reach the activated state. Augment the worker as a registration worker to
              // display it in aboutdebugging.
              worker.scope = form.scope;
              worker.active = false;
              workers.service.push(worker);
            }
            break;
          case Ci.nsIWorkerDebugger.TYPE_SHARED:
            workers.shared.push(worker);
            break;
          default:
            workers.other.push(worker);
        }
      });

      // XXX: Filter out the service worker registrations for which we couldn't
      // find the scriptSpec.
      workers.service = workers.service.filter(reg => !!reg.url);

      this.setState({ workers });
    });
  }

  getRegistrationForWorker(form, registrations) {
    for (let registration of registrations) {
      if (registration.scope === form.scope) {
        return registration;
      }
    }
    return null;
  }

  isE10S() {
    return Services.appinfo.browserTabsRemoteAutostart;
  }

  renderServiceWorkersError() {
    let isWindowPrivate = PrivateBrowsingUtils.isContentWindowPrivate(window);
    let isPrivateBrowsingMode = PrivateBrowsingUtils.permanentPrivateBrowsing;
    let isServiceWorkerDisabled = !Services.prefs
                                    .getBoolPref("dom.serviceWorkers.enabled");

    let isDisabled = isWindowPrivate || isPrivateBrowsingMode || isServiceWorkerDisabled;
    if (!isDisabled) {
      return "";
    }
    return dom.p(
      {
        className: "service-worker-disabled"
      },
      dom.div({ className: "warning" }),
      dom.span(
        {
          className: "service-worker-disabled-label",
        },
        Strings.GetStringFromName("configurationIsNotCompatible.label")
      ),
      dom.a(
        {
          href: MORE_INFO_URL,
          target: "_blank"
        },
        Strings.GetStringFromName("configurationIsNotCompatible.learnMore")
      ),
    );
  }

  render() {
    let { client, id } = this.props;
    let { workers, processCount } = this.state;

    let isE10S = Services.appinfo.browserTabsRemoteAutostart;
    let isMultiE10S = isE10S && processCount > 1;

    return dom.div(
      {
        id: id + "-panel",
        className: "panel",
        role: "tabpanel",
        "aria-labelledby": id + "-header"
      },
      PanelHeader({
        id: id + "-header",
        name: Strings.GetStringFromName("workers")
      }),
      isMultiE10S ? MultiE10SWarning() : "",
      dom.div(
        {
          id: "workers",
          className: "inverted-icons"
        },
        TargetList({
          client,
          debugDisabled: isMultiE10S,
          error: this.renderServiceWorkersError(),
          id: "service-workers",
          name: Strings.GetStringFromName("serviceWorkers"),
          sort: true,
          targetClass: ServiceWorkerTarget,
          targets: workers.service
        }),
        TargetList({
          client,
          id: "shared-workers",
          name: Strings.GetStringFromName("sharedWorkers"),
          sort: true,
          targetClass: WorkerTarget,
          targets: workers.shared
        }),
        TargetList({
          client,
          id: "other-workers",
          name: Strings.GetStringFromName("otherWorkers"),
          sort: true,
          targetClass: WorkerTarget,
          targets: workers.other
        })
      )
    );
  }
}

module.exports = WorkersPanel;
PK
!<�����`chrome/devtools/modules/devtools/client/aboutdebugging/components/workers/ServiceWorkerTarget.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 } = 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 { debugWorker } = require("../../modules/worker");
const Services = require("Services");

loader.lazyRequireGetter(this, "DebuggerClient",
  "devtools/shared/client/debugger-client", true);

const Strings = Services.strings.createBundle(
  "chrome://devtools/locale/aboutdebugging.properties");

class ServiceWorkerTarget extends Component {
  static get propTypes() {
    return {
      client: PropTypes.instanceOf(DebuggerClient).isRequired,
      debugDisabled: PropTypes.bool,
      target: PropTypes.shape({
        active: PropTypes.bool,
        fetch: PropTypes.bool.isRequired,
        icon: PropTypes.string,
        name: PropTypes.string.isRequired,
        url: PropTypes.string,
        scope: PropTypes.string.isRequired,
        // registrationActor can be missing in e10s.
        registrationActor: PropTypes.string,
        workerActor: PropTypes.string
      }).isRequired
    };
  }

  constructor(props) {
    super(props);

    this.state = {
      pushSubscription: null
    };

    this.debug = this.debug.bind(this);
    this.push = this.push.bind(this);
    this.start = this.start.bind(this);
    this.unregister = this.unregister.bind(this);
    this.onPushSubscriptionModified = this.onPushSubscriptionModified.bind(this);
    this.updatePushSubscription = this.updatePushSubscription.bind(this);
    this.isRunning = this.isRunning.bind(this);
    this.isActive = this.isActive.bind(this);
    this.getServiceWorkerStatus = this.getServiceWorkerStatus.bind(this);
    this.renderButtons = this.renderButtons.bind(this);
    this.renderUnregisterLink = this.renderUnregisterLink.bind(this);
  }

  componentDidMount() {
    let { client } = this.props;
    client.addListener("push-subscription-modified", this.onPushSubscriptionModified);
    this.updatePushSubscription();
  }

  componentDidUpdate(oldProps, oldState) {
    let wasActive = oldProps.target.active;
    if (!wasActive && this.isActive()) {
      // While the service worker isn't active, any calls to `updatePushSubscription`
      // won't succeed. If we just became active, make sure we didn't miss a push
      // subscription change by updating it now.
      this.updatePushSubscription();
    }
  }

  componentWillUnmount() {
    let { client } = this.props;
    client.removeListener("push-subscription-modified", this.onPushSubscriptionModified);
  }

  debug() {
    if (!this.isRunning()) {
      // If the worker is not running, we can't debug it.
      return;
    }

    let { client, target } = this.props;
    debugWorker(client, target.workerActor);
  }

  push() {
    if (!this.isActive() || !this.isRunning()) {
      // If the worker is not running, we can't push to it.
      // If the worker is not active, the registration might be unavailable and the
      // push will not succeed.
      return;
    }

    let { client, target } = this.props;
    client.request({
      to: target.workerActor,
      type: "push"
    });
  }

  start() {
    if (!this.isActive() || this.isRunning()) {
      // If the worker is not active or if it is already running, we can't start it.
      return;
    }

    let { client, target } = this.props;
    client.request({
      to: target.registrationActor,
      type: "start"
    });
  }

  unregister() {
    let { client, target } = this.props;
    client.request({
      to: target.registrationActor,
      type: "unregister"
    });
  }

  onPushSubscriptionModified(type, data) {
    let { target } = this.props;
    if (data.from === target.registrationActor) {
      this.updatePushSubscription();
    }
  }

  updatePushSubscription() {
    if (!this.props.target.registrationActor) {
      // A valid registrationActor is needed to retrieve the push subscription.
      return;
    }

    let { client, target } = this.props;
    client.request({
      to: target.registrationActor,
      type: "getPushSubscription"
    }, ({ subscription }) => {
      this.setState({ pushSubscription: subscription });
    });
  }

  isRunning() {
    // We know the target is running if it has a worker actor.
    return !!this.props.target.workerActor;
  }

  isActive() {
    return this.props.target.active;
  }

  getServiceWorkerStatus() {
    if (this.isActive() && this.isRunning()) {
      return "running";
    } else if (this.isActive()) {
      return "stopped";
    }
    // We cannot get service worker registrations unless the registration is in
    // ACTIVE state. Unable to know the actual state ("installing", "waiting"), we
    // display a custom state "registering" for now. See Bug 1153292.
    return "registering";
  }

  renderButtons() {
    let pushButton = dom.button({
      className: "push-button",
      onClick: this.push,
      disabled: this.props.debugDisabled
    }, Strings.GetStringFromName("push"));

    let debugButton = dom.button({
      className: "debug-button",
      onClick: this.debug,
      disabled: this.props.debugDisabled
    }, Strings.GetStringFromName("debug"));

    let startButton = dom.button({
      className: "start-button",
      onClick: this.start,
      disabled: this.props.debugDisabled
    }, Strings.GetStringFromName("start"));

    if (this.isRunning()) {
      if (this.isActive()) {
        return [pushButton, debugButton];
      }
      // Only debug button is available if the service worker is not active.
      return debugButton;
    }
    return startButton;
  }

  renderUnregisterLink() {
    if (!this.isActive()) {
      // If not active, there might be no registrationActor available.
      return null;
    }

    return dom.a({
      onClick: this.unregister,
      className: "unregister-link",
    }, Strings.GetStringFromName("unregister"));
  }

  render() {
    let { target } = this.props;
    let { pushSubscription } = this.state;
    let status = this.getServiceWorkerStatus();

    let fetch = target.fetch ? Strings.GetStringFromName("listeningForFetchEvents") :
      Strings.GetStringFromName("notListeningForFetchEvents");

    return dom.div({ className: "target-container" },
      dom.img({
        className: "target-icon",
        role: "presentation",
        src: target.icon
      }),
      dom.span({ className: `target-status target-status-${status}` },
        Strings.GetStringFromName(status)),
      dom.div({ className: "target" },
        dom.div({ className: "target-name", title: target.name }, target.name),
        dom.ul({ className: "target-details" },
          (pushSubscription ?
            dom.li({ className: "target-detail" },
              dom.strong(null, Strings.GetStringFromName("pushService")),
              dom.span({
                className: "service-worker-push-url",
                title: pushSubscription.endpoint
              }, pushSubscription.endpoint)) :
            null
          ),
          dom.li({ className: "target-detail" },
            dom.strong(null, Strings.GetStringFromName("fetch")),
            dom.span({
              className: "service-worker-fetch-flag",
              title: fetch
            }, fetch)),
          dom.li({ className: "target-detail" },
            dom.strong(null, Strings.GetStringFromName("scope")),
            dom.span({
              className: "service-worker-scope",
              title: target.scope
            }, target.scope),
            this.renderUnregisterLink()
          )
        )
      ),
      this.renderButtons()
    );
  }
}

module.exports = ServiceWorkerTarget;
PK
!<K��
UUSchrome/devtools/modules/devtools/client/aboutdebugging/components/workers/Target.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 } = 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 { debugWorker } = require("../../modules/worker");
const Services = require("Services");

loader.lazyRequireGetter(this, "DebuggerClient",
  "devtools/shared/client/debugger-client", true);

const Strings = Services.strings.createBundle(
  "chrome://devtools/locale/aboutdebugging.properties");

class WorkerTarget extends Component {
  static get propTypes() {
    return {
      client: PropTypes.instanceOf(DebuggerClient).isRequired,
      debugDisabled: PropTypes.bool,
      target: PropTypes.shape({
        icon: PropTypes.string,
        name: PropTypes.string.isRequired,
        workerActor: PropTypes.string
      }).isRequired
    };
  }

  constructor(props) {
    super(props);
    this.debug = this.debug.bind(this);
  }

  debug() {
    let { client, target } = this.props;
    debugWorker(client, target.workerActor);
  }

  render() {
    let { target, debugDisabled } = this.props;

    return dom.li({ className: "target-container" },
      dom.img({
        className: "target-icon",
        role: "presentation",
        src: target.icon
      }),
      dom.div({ className: "target" },
        dom.div({ className: "target-name", title: target.name }, target.name)
      ),
      dom.button({
        className: "debug-button",
        onClick: this.debug,
        disabled: debugDisabled
      }, Strings.GetStringFromName("debug"))
    );
  }
}

module.exports = WorkerTarget;
PK
!<�<�QQGchrome/devtools/modules/devtools/client/aboutdebugging/modules/addon.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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.lazyImporter(this, "BrowserToolboxProcess",
  "resource://devtools/client/framework/ToolboxProcess.jsm");
loader.lazyImporter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm");
loader.lazyImporter(this, "AddonManagerPrivate", "resource://gre/modules/AddonManager.jsm");

var {TargetFactory} = require("devtools/client/framework/target");
var {Toolbox} = require("devtools/client/framework/toolbox");

var {gDevTools} = require("devtools/client/framework/devtools");

let browserToolboxProcess = null;
let remoteAddonToolbox = null;
function closeToolbox() {
  if (browserToolboxProcess) {
    browserToolboxProcess.close();
  }

  if (remoteAddonToolbox) {
    remoteAddonToolbox.destroy();
  }
}

/**
 * Start debugging an addon in the current instance of Firefox.
 *
 * @param {String} addonID
 *        String id of the addon to debug.
 */
exports.debugLocalAddon = async function (addonID) {
  // Close previous addon debugging toolbox.
  closeToolbox();

  browserToolboxProcess = BrowserToolboxProcess.init({
    addonID,
    onClose: () => {
      browserToolboxProcess = null;
    }
  });
};

/**
 * Start debugging an addon in a remote instance of Firefox.
 *
 * @param {Object} addonForm
 *        Necessary to create an addon debugging target.
 * @param {DebuggerClient} client
 *        Required for remote debugging.
 */
exports.debugRemoteAddon = async function (addonForm, client) {
  // Close previous addon debugging toolbox.
  closeToolbox();

  let options = {
    form: addonForm,
    chrome: true,
    client,
    isTabActor: addonForm.isWebExtension
  };

  let target = await TargetFactory.forRemoteTab(options);

  let hostType = Toolbox.HostType.WINDOW;
  remoteAddonToolbox = await gDevTools.showToolbox(target, null, hostType);
  remoteAddonToolbox.once("destroy", () => {
    remoteAddonToolbox = null;
  });
};

exports.uninstallAddon = async function (addonID) {
  let addon = await AddonManager.getAddonByID(addonID);
  return addon && addon.uninstall();
};

exports.isTemporaryID = function (addonID) {
  return AddonManagerPrivate.isTemporaryInstallID(addonID);
};

exports.isLegacyTemporaryExtension = function (addonForm) {
  if (!addonForm.type) {
    // If about:debugging is connected to an older then 59 remote Firefox, and type is
    // not available on the addon/webextension actors, return false to avoid showing
    // irrelevant warning messages.
    return false;
  }
  return addonForm.type == "extension" &&
         addonForm.temporarilyInstalled &&
         !addonForm.isWebExtension &&
         !addonForm.isAPIExtension;
};

exports.parseFileUri = function (url) {
  // Strip a leading slash from Windows drive letter URIs.
  // file:///home/foo ~> /home/foo
  // file:///C:/foo ~> C:/foo
  const windowsRegex = /^file:\/\/\/([a-zA-Z]:\/.*)/;
  if (windowsRegex.test(url)) {
    return windowsRegex.exec(url)[1];
  }
  return url.slice("file://".length);
};
PK
!<�D�RRIchrome/devtools/modules/devtools/client/aboutdebugging/modules/connect.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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";

/**
 * The connect module creates a connection to a debugger server based on the current
 * context (e.g. URL parameters).
 */

const { clientFromURL } = require("devtools/client/framework/target-from-url");
const { DebuggerServer } = require("devtools/server/main");

// Supported connection types
const TYPE = {
  // Default, connected to the current instance of Firefox
  LOCAL: "LOCAL",
  // Connected to a remote instance of Firefox via host&port settings.
  REMOTE: "REMOTE",
};

/**
 * Create a plain object containing the connection information relevant to aboutdebugging
 * components.
 *
 * @returns {Object}
 *          - type: {String} from TYPE ("LOCAL", "REMOTE")
 *          - params: {Object} additional metadata depending on the type.
 *            - if type === "LOCAL", empty object
 *            - if type === "REMOTE", {host: {String}, port: {String}}
 */
function createDescriptorFromURL(url) {
  let params = url.searchParams;

  let host = params.get("host");
  let port = params.get("port");

  let descriptor;
  if (host && port) {
    descriptor = {
      type: TYPE.REMOTE,
      params: {host, port}
    };
  } else {
    descriptor = {
      type: TYPE.LOCAL,
      params: {}
    };
  }

  return descriptor;
}

/**
 * Returns a promise that resolves after creating a debugger client corresponding to the
 * provided options.
 *
 * @returns Promise that resolves an object with the following properties:
 *          - client: a DebuggerClient instance
 *          - connect: a connection descriptor, see doc for createDescriptorFromURL(url).
 */
exports.createClient = async function () {
  let href = window.location.href;
  let url = new window.URL(href.replace("about:", "http://"));

  let connect = createDescriptorFromURL(url);
  let client = await clientFromURL(url);

  DebuggerServer.allowChromeProcess = true;

  return {client, connect};
};
PK
!<��h	h	Hchrome/devtools/modules/devtools/client/aboutdebugging/modules/worker.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Task } = require("devtools/shared/task");

loader.lazyRequireGetter(this, "gDevTools",
  "devtools/client/framework/devtools", true);
loader.lazyRequireGetter(this, "TargetFactory",
  "devtools/client/framework/target", true);
loader.lazyRequireGetter(this, "Toolbox",
  "devtools/client/framework/toolbox", true);

/**
 * Open a window-hosted toolbox to debug the worker associated to the provided
 * worker actor.
 *
 * @param {DebuggerClient} client
 * @param {Object} workerActor
 *        worker actor form to debug
 */
exports.debugWorker = function (client, workerActor) {
  client.attachWorker(workerActor, (response, workerClient) => {
    let workerTarget = TargetFactory.forWorker(workerClient);
    gDevTools.showToolbox(workerTarget, "jsdebugger", Toolbox.HostType.WINDOW)
      .then(toolbox => {
        toolbox.once("destroy", () => workerClient.detach());
      });
  });
};

/**
 * Retrieve all service worker registrations as well as workers from the parent
 * and child processes.
 *
 * @param {DebuggerClient} client
 * @return {Object}
 *         - {Array} registrations
 *           Array of ServiceWorkerRegistrationActor forms
 *         - {Array} workers
 *           Array of WorkerActor forms
 */
exports.getWorkerForms = Task.async(function* (client) {
  let registrations = [];
  let workers = [];

  try {
    // List service worker registrations
    ({ registrations } =
      yield client.mainRoot.listServiceWorkerRegistrations());

    // List workers from the Parent process
    ({ workers } = yield client.mainRoot.listWorkers());

    // And then from the Child processes
    let { processes } = yield client.mainRoot.listProcesses();
    for (let process of processes) {
      // Ignore parent process
      if (process.parent) {
        continue;
      }
      let { form } = yield client.getProcess(process.id);
      let processActor = form.actor;
      let response = yield client.request({
        to: processActor,
        type: "listWorkers"
      });
      workers = workers.concat(response.workers);
    }
  } catch (e) {
    // Something went wrong, maybe our client is disconnected?
  }

  return { registrations, workers };
});
PK
!<�/LB�#�#Zchrome/devtools/modules/devtools/client/animationinspector/components/animation-details.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 {Task} = require("devtools/shared/task");
const {createNode, getCssPropertyName} =
  require("devtools/client/animationinspector/utils");
const {Keyframes} = require("devtools/client/animationinspector/components/keyframes");

const { LocalizationHelper } = require("devtools/shared/l10n");
const L10N =
  new LocalizationHelper("devtools/client/locales/animationinspector.properties");

/**
 * UI component responsible for displaying detailed information for a given
 * animation.
 * This includes information about timing, easing, keyframes, animated
 * properties.
 *
 * @param {Object} serverTraits The list of server-side capabilities.
 */
function AnimationDetails(serverTraits) {
  this.keyframeComponents = [];
  this.serverTraits = serverTraits;
}

exports.AnimationDetails = AnimationDetails;

AnimationDetails.prototype = {
  // These are part of frame objects but are not animated properties. This
  // array is used to skip them.
  NON_PROPERTIES: ["easing", "composite", "computedOffset",
                   "offset", "simulateComputeValuesFailure"],

  init: function (containerEl) {
    this.containerEl = containerEl;
  },

  destroy: function () {
    this.unrender();
    this.containerEl = null;
    this.serverTraits = null;
    this.progressIndicatorEl = null;
  },

  unrender: function () {
    for (let component of this.keyframeComponents) {
      component.destroy();
    }
    this.keyframeComponents = [];

    while (this.containerEl.firstChild) {
      this.containerEl.firstChild.remove();
    }
  },

  getPerfDataForProperty: function (animation, propertyName) {
    let warning = "";
    let className = "";
    if (animation.state.propertyState) {
      let isRunningOnCompositor;
      for (let propState of animation.state.propertyState) {
        if (propState.property == propertyName) {
          isRunningOnCompositor = propState.runningOnCompositor;
          if (typeof propState.warning != "undefined") {
            warning = propState.warning;
          }
          break;
        }
      }
      if (isRunningOnCompositor && warning == "") {
        className = "oncompositor";
      } else if (!isRunningOnCompositor && warning != "") {
        className = "warning";
      }
    }
    return {className, warning};
  },

  /**
   * Get animation types of given CSS property names.
   * @param {Array} CSS property names.
   *                e.g. ["background-color", "opacity", ...]
   * @return {Object} Animation type mapped with CSS property name.
   *                  e.g. { "background-color": "color", }
   *                         "opacity": "float", ... }
   */
  getAnimationTypes: Task.async(function* (propertyNames) {
    if (this.serverTraits.hasGetAnimationTypes) {
      return yield this.animation.getAnimationTypes(propertyNames);
    }
    // Set animation type 'none' since does not support getAnimationTypes.
    const animationTypes = {};
    propertyNames.forEach(propertyName => {
      animationTypes[propertyName] = "none";
    });
    return Promise.resolve(animationTypes);
  }),

  render: Task.async(function* (animation, tracks) {
    this.unrender();

    if (!animation) {
      return;
    }
    this.animation = animation;
    this.tracks = tracks;

    // We might have been destroyed in the meantime, or the component might
    // have been re-rendered.
    if (!this.containerEl || this.animation !== animation) {
      return;
    }

    // Get animation type for each CSS properties.
    const animationTypes = yield this.getAnimationTypes(Object.keys(this.tracks));

    // Render progress indicator.
    this.renderProgressIndicator();
    // Render animated properties header.
    this.renderAnimatedPropertiesHeader();
    // Render animated properties body.
    this.renderAnimatedPropertiesBody(animationTypes);

    // Create dummy animation to indicate the animation progress.
    const timing = Object.assign({}, animation.state, {
      iterations: animation.state.iterationCount
                  ? animation.state.iterationCount : Infinity
    });
    this.dummyAnimation =
      new this.win.Animation(new this.win.KeyframeEffect(null, null, timing), null);
  }),

  renderAnimatedPropertiesHeader: function () {
    // Add animated property header.
    const headerEl = createNode({
      parent: this.containerEl,
      attributes: { "class": "animated-properties-header" }
    });

    // Add progress tick container.
    const progressTickContainerEl = createNode({
      parent: this.containerEl,
      attributes: { "class": "progress-tick-container track-container" }
    });

    // Add label container.
    const headerLabelContainerEl = createNode({
      parent: headerEl,
      attributes: { "class": "track-container" }
    });

    // Add labels
    for (let label of [L10N.getFormatStr("detail.propertiesHeader.percentage", 0),
                       L10N.getFormatStr("detail.propertiesHeader.percentage", 50),
                       L10N.getFormatStr("detail.propertiesHeader.percentage", 100)]) {
      createNode({
        parent: progressTickContainerEl,
        nodeType: "span",
        attributes: { "class": "progress-tick" }
      });
      createNode({
        parent: headerLabelContainerEl,
        nodeType: "label",
        attributes: { "class": "header-item" },
        textContent: label
      });
    }
  },

  renderAnimatedPropertiesBody: function (animationTypes) {
    // Add animated property body.
    const bodyEl = createNode({
      parent: this.containerEl,
      attributes: { "class": "animated-properties-body" }
    });

    // Move unchanged value animation to bottom in the list.
    const propertyNames = [];
    const unchangedPropertyNames = [];
    for (let propertyName in this.tracks) {
      if (!isUnchangedProperty(this.tracks[propertyName])) {
        propertyNames.push(propertyName);
      } else {
        unchangedPropertyNames.push(propertyName);
      }
    }
    Array.prototype.push.apply(propertyNames, unchangedPropertyNames);

    for (let propertyName of propertyNames) {
      let line = createNode({
        parent: bodyEl,
        attributes: {"class": "property"}
      });
      if (unchangedPropertyNames.includes(propertyName)) {
        line.classList.add("unchanged");
      }
      let {warning, className} =
        this.getPerfDataForProperty(this.animation, propertyName);
      createNode({
        // text-overflow doesn't work in flex items, so we need a second level
        // of container to actually have an ellipsis on the name.
        // See bug 972664.
        parent: createNode({
          parent: line,
          attributes: {"class": "name"}
        }),
        textContent: getCssPropertyName(propertyName),
        attributes: {"title": warning,
                     "class": className}
      });

      // Add the keyframes diagram for this property.
      let framesWrapperEl = createNode({
        parent: line,
        attributes: {"class": "track-container"}
      });

      let framesEl = createNode({
        parent: framesWrapperEl,
        attributes: {"class": "frames"}
      });

      let keyframesComponent = new Keyframes();
      keyframesComponent.init(framesEl);
      keyframesComponent.render({
        keyframes: this.tracks[propertyName],
        propertyName: propertyName,
        animation: this.animation,
        animationType: animationTypes[propertyName]
      });
      this.keyframeComponents.push(keyframesComponent);
    }
  },

  renderProgressIndicator: function () {
    // The wrapper represents the area which the indicator is displayable.
    const progressIndicatorWrapperEl = createNode({
      parent: this.containerEl,
      attributes: {
        "class": "track-container progress-indicator-wrapper"
      }
    });
    this.progressIndicatorEl = createNode({
      parent: progressIndicatorWrapperEl,
      attributes: {
        "class": "progress-indicator"
      }
    });
    createNode({
      parent: this.progressIndicatorEl,
      attributes: {
        "class": "progress-indicator-shape"
      }
    });
  },

  indicateProgress: function (time) {
    if (!this.progressIndicatorEl) {
      // Not displayed yet.
      return;
    }
    const startTime = this.animation.state.previousStartTime || 0;
    this.dummyAnimation.currentTime =
      (time - startTime) * this.animation.state.playbackRate;
    this.progressIndicatorEl.style.left =
      `${ this.dummyAnimation.effect.getComputedTiming().progress * 100 }%`;
  },

  get win() {
    return this.containerEl.ownerDocument.defaultView;
  }
};

function isUnchangedProperty(values) {
  const firstValue = values[0].value;
  for (let i = 1; i < values.length; i++) {
    if (values[i].value !== firstValue) {
      return false;
    }
  }
  return true;
}
PK
!<a먑B
B
^chrome/devtools/modules/devtools/client/animationinspector/components/animation-target-node.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 {Task} = require("devtools/shared/task");
const EventEmitter = require("devtools/shared/event-emitter");
const {DomNodePreview} = require("devtools/client/inspector/shared/dom-node-preview");

// Map dom node fronts by animation fronts so we don't have to get them from the
// walker every time the timeline is refreshed.
var nodeFronts = new WeakMap();

/**
 * UI component responsible for displaying a preview of the target dom node of
 * a given animation.
 * Accepts the same parameters as the DomNodePreview component. See
 * devtools/client/inspector/shared/dom-node-preview.js for documentation.
 */
function AnimationTargetNode(inspector, options) {
  this.inspector = inspector;
  this.previewer = new DomNodePreview(inspector, options);
  EventEmitter.decorate(this);
}

exports.AnimationTargetNode = AnimationTargetNode;

AnimationTargetNode.prototype = {
  init: function (containerEl) {
    this.previewer.init(containerEl);
    this.isDestroyed = false;
  },

  destroy: function () {
    this.previewer.destroy();
    this.inspector = null;
    this.isDestroyed = true;
  },

  render: Task.async(function* (playerFront) {
    // Get the nodeFront from the cache if it was stored previously.
    let nodeFront = nodeFronts.get(playerFront);

    // Try and get it from the playerFront directly next.
    if (!nodeFront) {
      nodeFront = playerFront.animationTargetNodeFront;
    }

    // Finally, get it from the walkerActor if it wasn't found.
    if (!nodeFront) {
      try {
        nodeFront = yield this.inspector.walker.getNodeFromActor(
                               playerFront.actorID, ["node"]);
      } catch (e) {
        // If an error occured while getting the nodeFront and if it can't be
        // attributed to the panel having been destroyed in the meantime, this
        // error needs to be logged and render needs to stop.
        if (!this.isDestroyed) {
          console.error(e);
        }
        return;
      }

      // In all cases, if by now the panel doesn't exist anymore, we need to
      // stop rendering too.
      if (this.isDestroyed) {
        return;
      }
    }

    // Add the nodeFront to the cache.
    nodeFronts.set(playerFront, nodeFront);

    this.previewer.render(nodeFront);
    this.emit("target-retrieved");
  })
};
PK
!<��jK�d�d]chrome/devtools/modules/devtools/client/animationinspector/components/animation-time-block.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 EventEmitter = require("devtools/shared/event-emitter");
const {createNode, createSVGNode, TimeScale, getFormattedAnimationTitle} =
  require("devtools/client/animationinspector/utils");
const {SummaryGraphHelper, getPreferredKeyframesProgressThreshold,
       getPreferredProgressThreshold} =
  require("devtools/client/animationinspector/graph-helper");

const { LocalizationHelper } = require("devtools/shared/l10n");
const L10N =
      new LocalizationHelper("devtools/client/locales/animationinspector.properties");

// Show max 10 iterations for infinite animations
// to give users a clue that the animation does repeat.
const MAX_INFINITE_ANIMATIONS_ITERATIONS = 10;

// Minimum opacity for semitransparent fill color for keyframes's easing graph.
const MIN_KEYFRAMES_EASING_OPACITY = .5;

/**
 * UI component responsible for displaying a single animation timeline, which
 * basically looks like a rectangle that shows the delay and iterations.
 */
function AnimationTimeBlock() {
  EventEmitter.decorate(this);
  this.onClick = this.onClick.bind(this);
}

exports.AnimationTimeBlock = AnimationTimeBlock;

AnimationTimeBlock.prototype = {
  init: function (containerEl) {
    this.containerEl = containerEl;
    this.containerEl.addEventListener("click", this.onClick);
  },

  destroy: function () {
    this.containerEl.removeEventListener("click", this.onClick);
    this.unrender();
    this.containerEl = null;
    this.animation = null;
  },

  unrender: function () {
    while (this.containerEl.firstChild) {
      this.containerEl.firstChild.remove();
    }
  },

  render: function (animation, tracks) {
    this.unrender();

    this.animation = animation;

    // Animation summary graph element.
    const summaryEl = createSVGNode({
      parent: this.containerEl,
      nodeType: "svg",
      attributes: {
        "class": "summary",
        "preserveAspectRatio": "none"
      }
    });
    this.updateSummaryGraphViewBox(summaryEl);

    const {state} = this.animation;
    // Total displayed duration
    const totalDisplayedDuration = this.getTotalDisplayedDuration();
    // Minimum segment duration is the duration of one pixel.
    const minSegmentDuration = totalDisplayedDuration / this.containerEl.clientWidth;
    // Minimum progress threshold for effect timing.
    const minEffectProgressThreshold = getPreferredProgressThreshold(state.easing);

    // Render summary graph.
    // The summary graph is constructed from keyframes's easing and effect timing.
    const graphHelper = new SummaryGraphHelper(this.win, state, minSegmentDuration);
    renderKeyframesEasingGraph(summaryEl, state, totalDisplayedDuration,
                               minEffectProgressThreshold, tracks, graphHelper);
    if (state.easing !== "linear") {
      renderEffectEasingGraph(summaryEl, state, totalDisplayedDuration,
                              minEffectProgressThreshold, graphHelper);
    }
    graphHelper.destroy();

    // The animation name is displayed over the animation.
    const nameEl = createNode({
      parent: this.containerEl,
      attributes: {
        "class": "name",
        "title": this.getTooltipText(state)
      }
    });

    createSVGNode({
      parent: createSVGNode({
        parent: nameEl,
        nodeType: "svg",
      }),
      nodeType: "text",
      attributes: {
        "y": "50%",
        "x": "100%",
      },
      textContent: state.name
    });

    // Delay.
    if (state.delay) {
      // Negative delays need to start at 0.
      const delayEl = createNode({
        parent: this.containerEl,
        attributes: {
          "class": "delay"
                   + (state.delay < 0 ? " negative" : " positive")
                   + (state.fill === "both" ||
                      state.fill === "backwards" ? " fill" : "")
        }
      });
      this.updateDelayBounds(delayEl);
    }

    // endDelay
    if (state.iterationCount && state.endDelay) {
      const endDelayEl = createNode({
        parent: this.containerEl,
        attributes: {
          "class": "end-delay"
                   + (state.endDelay < 0 ? " negative" : " positive")
                   + (state.fill === "both" ||
                      state.fill === "forwards" ? " fill" : "")
        }
      });
      this.updateEndDelayBounds(endDelayEl);
    }
  },

  /**
   * Update animation and updating its DOM accordingly.
   * Unlike 'render' method, this method does not generate any elements, but update
   * the bounds of DOM.
   * @param {Object} animation
   */
  update: function (animation) {
    this.animation = animation;
    this.updateSummaryGraphViewBox(this.containerEl.querySelector(".summary"));
    const delayEl = this.containerEl.querySelector(".delay");
    if (delayEl) {
      this.updateDelayBounds(delayEl);
    }
    const endDelayEl = this.containerEl.querySelector(".end-delay");
    if (endDelayEl) {
      this.updateEndDelayBounds(endDelayEl);
    }
  },

  /**
   * Update viewBox and style of SVG element for summary graph to fit to latest
   * TimeScale.
   * @param {Element} summaryEl - SVG element for summary graph.
   */
  updateSummaryGraphViewBox: function (summaryEl) {
    const {x, delayW} = TimeScale.getAnimationDimensions(this.animation);
    const totalDisplayedDuration = this.getTotalDisplayedDuration();
    const strokeHeightForViewBox = 0.5 / this.containerEl.clientHeight;
    const {state} = this.animation;
    summaryEl.setAttribute("viewBox",
                           `${state.delay < 0 ? state.delay : 0} ` +
                           `-${1 + strokeHeightForViewBox} ` +
                           `${totalDisplayedDuration} ` +
                           `${1 + strokeHeightForViewBox * 2}`);
    summaryEl.setAttribute("style", `left: ${ x - (state.delay > 0 ? delayW : 0) }%`);
  },

  /**
   * Update bounds of element which represents delay to fit to latest TimeScale.
   * @param {Element} delayEl - which represents delay.
   */
  updateDelayBounds: function (delayEl) {
    const {delayX, delayW} = TimeScale.getAnimationDimensions(this.animation);
    delayEl.style.left = `${ delayX }%`;
    delayEl.style.width = `${ delayW }%`;
  },

  /**
   * Update bounds of element which represents endDelay to fit to latest TimeScale.
   * @param {Element} endDelayEl - which represents endDelay.
   */
  updateEndDelayBounds: function (endDelayEl) {
    const {endDelayX, endDelayW} = TimeScale.getAnimationDimensions(this.animation);
    endDelayEl.style.left = `${ endDelayX }%`;
    endDelayEl.style.width = `${ endDelayW }%`;
  },

  getTotalDisplayedDuration: function () {
    return this.animation.state.playbackRate * TimeScale.getDuration();
  },

  getTooltipText: function (state) {
    let getTime = time => L10N.getFormatStr("player.timeLabel",
                                            L10N.numberWithDecimals(time / 1000, 2));

    let text = "";

    // Adding the name.
    text += getFormattedAnimationTitle({state});
    text += "\n";

    // Adding the delay.
    if (state.delay) {
      text += L10N.getStr("player.animationDelayLabel") + " ";
      text += getTime(state.delay);
      text += "\n";
    }

    // Adding the duration.
    text += L10N.getStr("player.animationDurationLabel") + " ";
    text += getTime(state.duration);
    text += "\n";

    // Adding the endDelay.
    if (state.endDelay) {
      text += L10N.getStr("player.animationEndDelayLabel") + " ";
      text += getTime(state.endDelay);
      text += "\n";
    }

    // Adding the iteration count (the infinite symbol, or an integer).
    if (state.iterationCount !== 1) {
      text += L10N.getStr("player.animationIterationCountLabel") + " ";
      text += state.iterationCount ||
              L10N.getStr("player.infiniteIterationCountText");
      text += "\n";
    }

    // Adding the iteration start.
    if (state.iterationStart !== 0) {
      let iterationStartTime = state.iterationStart * state.duration / 1000;
      text += L10N.getFormatStr("player.animationIterationStartLabel",
                                state.iterationStart,
                                L10N.numberWithDecimals(iterationStartTime, 2));
      text += "\n";
    }

    // Adding the easing if it is not "linear".
    if (state.easing && state.easing !== "linear") {
      text += L10N.getStr("player.animationOverallEasingLabel") + " ";
      text += state.easing;
      text += "\n";
    }

    // Adding the fill mode.
    if (state.fill) {
      text += L10N.getStr("player.animationFillLabel") + " ";
      text += state.fill;
      text += "\n";
    }

    // Adding the direction mode if it is not "normal".
    if (state.direction && state.direction !== "normal") {
      text += L10N.getStr("player.animationDirectionLabel") + " ";
      text += state.direction;
      text += "\n";
    }

    // Adding the playback rate if it's different than 1.
    if (state.playbackRate !== 1) {
      text += L10N.getStr("player.animationRateLabel") + " ";
      text += state.playbackRate;
      text += "\n";
    }

    // Adding the animation-timing-function
    // if it is not "ease" which is default value for CSS Animations.
    if (state.animationTimingFunction && state.animationTimingFunction !== "ease") {
      text += L10N.getStr("player.animationTimingFunctionLabel") + " ";
      text += state.animationTimingFunction;
      text += "\n";
    }

    // Adding a note that the animation is running on the compositor thread if
    // needed.
    if (state.propertyState) {
      if (state.propertyState
               .every(propState => propState.runningOnCompositor)) {
        text += L10N.getStr("player.allPropertiesOnCompositorTooltip");
      } else if (state.propertyState
                      .some(propState => propState.runningOnCompositor)) {
        text += L10N.getStr("player.somePropertiesOnCompositorTooltip");
      }
    } else if (state.isRunningOnCompositor) {
      text += L10N.getStr("player.runningOnCompositorTooltip");
    }

    return text;
  },

  onClick: function (e) {
    e.stopPropagation();
    this.emit("selected", this.animation);
  },

  get win() {
    return this.containerEl.ownerDocument.defaultView;
  }
};

/**
 * Render keyframes's easing graph.
 * @param {Element} parentEl - Parent element of this appended path element.
 * @param {Object} state - State of animation.
 * @param {float} totalDisplayedDuration - Displayable total duration.
 * @param {float} minEffectProgressThreshold - Minimum progress threshold for effect.
 * @param {Object} tracks - The value of AnimationsTimeline.getTracks().
 * @param {Object} graphHelper - SummaryGraphHelper.
 */
function renderKeyframesEasingGraph(parentEl, state, totalDisplayedDuration,
                                    minEffectProgressThreshold, tracks, graphHelper) {
  const keyframesList = getOffsetAndEasingOnlyKeyframesList(tracks);
  const keyframeEasingOpacity = Math.max(1 / keyframesList.length,
                                         MIN_KEYFRAMES_EASING_OPACITY);
  for (let keyframes of keyframesList) {
    const minProgressTreshold =
      Math.min(minEffectProgressThreshold,
               getPreferredKeyframesProgressThreshold(keyframes));
    graphHelper.setMinProgressThreshold(minProgressTreshold);
    graphHelper.setKeyframes(keyframes);
    graphHelper.setClosePathNeeded(true);
    const element = renderGraph(parentEl, state, totalDisplayedDuration,
                                "keyframes-easing", graphHelper);
    element.style.opacity = keyframeEasingOpacity;
  }
}

/**
 * Render effect easing graph.
 * @param {Element} parentEl - Parent element of this appended path element.
 * @param {Object} state - State of animation.
 * @param {float} totalDisplayedDuration - Displayable total duration.
 * @param {float} minEffectProgressThreshold - Minimum progress threshold for effect.
 * @param {Object} graphHelper - SummaryGraphHelper.
 */
function renderEffectEasingGraph(parentEl, state, totalDisplayedDuration,
                                 minEffectProgressThreshold, graphHelper) {
  graphHelper.setMinProgressThreshold(minEffectProgressThreshold);
  graphHelper.setKeyframes(null);
  graphHelper.setClosePathNeeded(false);
  renderGraph(parentEl, state, totalDisplayedDuration, "effect-easing", graphHelper);
}

/**
 * Render a graph of given parameters.
 * @param {Element} parentEl - Parent element of this appended path element.
 * @param {Object} state - State of animation.
 * @param {float} totalDisplayedDuration - Displayable total duration.
 * @param {String} className - Class name for graph element.
 * @param {Object} graphHelper - SummaryGraphHelper.
 */
function renderGraph(parentEl, state, totalDisplayedDuration, className, graphHelper) {
  const graphEl = createSVGNode({
    parent: parentEl,
    nodeType: "g",
    attributes: {
      "class": className,
    }
  });

  // Starting time of main iteration.
  let mainIterationStartTime = 0;
  let iterationStart = state.iterationStart;
  let iterationCount = state.iterationCount ? state.iterationCount : Infinity;

  graphHelper.setFillMode(state.fill);
  graphHelper.setOriginalBehavior(true);

  // Append delay.
  if (state.delay > 0) {
    renderDelay(graphEl, state, graphHelper);
    mainIterationStartTime = state.delay;
  } else {
    const negativeDelayCount = -state.delay / state.duration;
    // Move to forward the starting point for negative delay.
    iterationStart += negativeDelayCount;
    // Consume iteration count by negative delay.
    if (iterationCount !== Infinity) {
      iterationCount -= negativeDelayCount;
    }
  }

  // Append 1st section of iterations,
  // This section is only useful in cases where iterationStart has decimals.
  // e.g.
  // if { iterationStart: 0.25, iterations: 3 }, firstSectionCount is 0.75.
  const firstSectionCount =
    iterationStart % 1 === 0
    ? 0 : Math.min(iterationCount, 1) - iterationStart % 1;
  if (firstSectionCount) {
    renderFirstIteration(graphEl, state, mainIterationStartTime,
                         firstSectionCount, graphHelper);
  }

  if (iterationCount === Infinity) {
    // If the animation repeats infinitely,
    // we fill the remaining area with iteration paths.
    renderInfinity(graphEl, state, mainIterationStartTime,
                   firstSectionCount, totalDisplayedDuration, graphHelper);
  } else {
    // Otherwise, we show remaining iterations, endDelay and fill.

    // Append forwards fill-mode.
    if (state.fill === "both" || state.fill === "forwards") {
      renderForwardsFill(graphEl, state, mainIterationStartTime,
                         iterationCount, totalDisplayedDuration, graphHelper);
    }

    // Append middle section of iterations.
    // e.g.
    // if { iterationStart: 0.25, iterations: 3 }, middleSectionCount is 2.
    const middleSectionCount =
      Math.floor(iterationCount - firstSectionCount);
    renderMiddleIterations(graphEl, state, mainIterationStartTime,
                           firstSectionCount, middleSectionCount, graphHelper);

    // Append last section of iterations, if there is remaining iteration.
    // e.g.
    // if { iterationStart: 0.25, iterations: 3 }, lastSectionCount is 0.25.
    const lastSectionCount =
      iterationCount - middleSectionCount - firstSectionCount;
    if (lastSectionCount) {
      renderLastIteration(graphEl, state, mainIterationStartTime,
                          firstSectionCount, middleSectionCount,
                          lastSectionCount, graphHelper);
    }

    // Append endDelay.
    if (state.endDelay > 0) {
      renderEndDelay(graphEl, state,
                     mainIterationStartTime, iterationCount, graphHelper);
    }
  }

  // Append negative delay (which overlap the animation).
  if (state.delay < 0) {
    graphHelper.setFillMode("both");
    graphHelper.setOriginalBehavior(false);
    renderNegativeDelayHiddenProgress(graphEl, state, graphHelper);
  }
  // Append negative endDelay (which overlap the animation).
  if (state.iterationCount && state.endDelay < 0) {
    graphHelper.setFillMode("both");
    graphHelper.setOriginalBehavior(false);
    renderNegativeEndDelayHiddenProgress(graphEl, state, graphHelper);
  }

  return graphEl;
}

/**
 * Render delay section.
 * @param {Element} parentEl - Parent element of this appended path element.
 * @param {Object} state - State of animation.
 * @param {Object} graphHelper - SummaryGraphHelper.
 */
function renderDelay(parentEl, state, graphHelper) {
  const startSegment = graphHelper.getSegment(0);
  const endSegment = { x: state.delay, y: startSegment.y };
  graphHelper.appendShapePath(parentEl, [startSegment, endSegment], "delay-path");
}

/**
 * Render first iteration section.
 * @param {Element} parentEl - Parent element of this appended path element.
 * @param {Object} state - State of animation.
 * @param {Number} mainIterationStartTime - Starting time of main iteration.
 * @param {Number} firstSectionCount - Iteration count of first section.
 * @param {Object} graphHelper - SummaryGraphHelper.
 */
function renderFirstIteration(parentEl, state, mainIterationStartTime,
                              firstSectionCount, graphHelper) {
  const startTime = mainIterationStartTime;
  const endTime = startTime + firstSectionCount * state.duration;
  const segments = graphHelper.createPathSegments(startTime, endTime);
  graphHelper.appendShapePath(parentEl, segments, "iteration-path");
}

/**
 * Render middle iterations section.
 * @param {Element} parentEl - Parent element of this appended path element.
 * @param {Object} state - State of animation.
 * @param {Number} mainIterationStartTime - Starting time of main iteration.
 * @param {Number} firstSectionCount - Iteration count of first section.
 * @param {Number} middleSectionCount - Iteration count of middle section.
 * @param {Object} graphHelper - SummaryGraphHelper.
 */
function renderMiddleIterations(parentEl, state, mainIterationStartTime,
                                firstSectionCount, middleSectionCount,
                                graphHelper) {
  const offset = mainIterationStartTime + firstSectionCount * state.duration;
  for (let i = 0; i < middleSectionCount; i++) {
    // Get the path segments of each iteration.
    const startTime = offset + i * state.duration;
    const endTime = startTime + state.duration;
    const segments = graphHelper.createPathSegments(startTime, endTime);
    graphHelper.appendShapePath(parentEl, segments, "iteration-path");
  }
}

/**
 * Render last iteration section.
 * @param {Element} parentEl - Parent element of this appended path element.
 * @param {Object} state - State of animation.
 * @param {Number} mainIterationStartTime - Starting time of main iteration.
 * @param {Number} firstSectionCount - Iteration count of first section.
 * @param {Number} middleSectionCount - Iteration count of middle section.
 * @param {Number} lastSectionCount - Iteration count of last section.
 * @param {Object} graphHelper - SummaryGraphHelper.
 */
function renderLastIteration(parentEl, state, mainIterationStartTime,
                             firstSectionCount, middleSectionCount,
                             lastSectionCount, graphHelper) {
  const startTime = mainIterationStartTime +
                      (firstSectionCount + middleSectionCount) * state.duration;
  const endTime = startTime + lastSectionCount * state.duration;
  const segments = graphHelper.createPathSegments(startTime, endTime);
  graphHelper.appendShapePath(parentEl, segments, "iteration-path");
}

/**
 * Render Infinity iterations.
 * @param {Element} parentEl - Parent element of this appended path element.
 * @param {Object} state - State of animation.
 * @param {Number} mainIterationStartTime - Starting time of main iteration.
 * @param {Number} firstSectionCount - Iteration count of first section.
 * @param {Number} totalDuration - Displayed max duration.
 * @param {Object} graphHelper - SummaryGraphHelper.
 */
function renderInfinity(parentEl, state, mainIterationStartTime,
                        firstSectionCount, totalDuration, graphHelper) {
  // Calculate the number of iterations to display,
  // with a maximum of MAX_INFINITE_ANIMATIONS_ITERATIONS
  let uncappedInfinityIterationCount =
    (totalDuration - firstSectionCount * state.duration) / state.duration;
  // If there is a small floating point error resulting in, e.g. 1.0000001
  // ceil will give us 2 so round first.
  uncappedInfinityIterationCount =
    parseFloat(uncappedInfinityIterationCount.toPrecision(6));
  const infinityIterationCount =
    Math.min(MAX_INFINITE_ANIMATIONS_ITERATIONS,
             Math.ceil(uncappedInfinityIterationCount));

  // Append first full iteration path.
  const firstStartTime =
    mainIterationStartTime + firstSectionCount * state.duration;
  const firstEndTime = firstStartTime + state.duration;
  const firstSegments =
    graphHelper.createPathSegments(firstStartTime, firstEndTime);
  graphHelper.appendShapePath(parentEl, firstSegments, "iteration-path infinity");

  // Append other iterations. We can copy first segments.
  const isAlternate = state.direction.match(/alternate/);
  for (let i = 1; i < infinityIterationCount; i++) {
    const startTime = firstStartTime + i * state.duration;
    let segments;
    if (isAlternate && i % 2) {
      // Copy as reverse.
      segments = firstSegments.map(segment => {
        return { x: firstEndTime - segment.x + startTime, y: segment.y };
      });
    } else {
      // Copy as is.
      segments = firstSegments.map(segment => {
        return { x: segment.x - firstStartTime + startTime, y: segment.y };
      });
    }
    graphHelper.appendShapePath(parentEl, segments, "iteration-path infinity copied");
  }
}

/**
 * Render endDelay section.
 * @param {Element} parentEl - Parent element of this appended path element.
 * @param {Object} state - State of animation.
 * @param {Number} mainIterationStartTime - Starting time of main iteration.
 * @param {Number} iterationCount - Whole iteration count.
 * @param {Object} graphHelper - SummaryGraphHelper.
 */
function renderEndDelay(parentEl, state,
                        mainIterationStartTime, iterationCount, graphHelper) {
  const startTime = mainIterationStartTime + iterationCount * state.duration;
  const startSegment = graphHelper.getSegment(startTime);
  const endSegment = { x: startTime + state.endDelay, y: startSegment.y };
  graphHelper.appendShapePath(parentEl, [startSegment, endSegment], "enddelay-path");
}

/**
 * Render forwards fill section.
 * @param {Element} parentEl - Parent element of this appended path element.
 * @param {Object} state - State of animation.
 * @param {Number} mainIterationStartTime - Starting time of main iteration.
 * @param {Number} iterationCount - Whole iteration count.
 * @param {Number} totalDuration - Displayed max duration.
 * @param {Object} graphHelper - SummaryGraphHelper.
 */
function renderForwardsFill(parentEl, state, mainIterationStartTime,
                            iterationCount, totalDuration, graphHelper) {
  const startTime = mainIterationStartTime + iterationCount * state.duration +
                      (state.endDelay > 0 ? state.endDelay : 0);
  const startSegment = graphHelper.getSegment(startTime);
  const endSegment = { x: totalDuration, y: startSegment.y };
  graphHelper.appendShapePath(parentEl, [startSegment, endSegment], "fill-forwards-path");
}

/**
 * Render hidden progress of negative delay.
 * @param {Element} parentEl - Parent element of this appended path element.
 * @param {Object} state - State of animation.
 * @param {Object} graphHelper - SummaryGraphHelper.
 */
function renderNegativeDelayHiddenProgress(parentEl, state, graphHelper) {
  const startTime = state.delay;
  const endTime = 0;
  const segments =
    graphHelper.createPathSegments(startTime, endTime);
  graphHelper.appendShapePath(parentEl, segments, "delay-path negative");
}

/**
 * Render hidden progress of negative endDelay.
 * @param {Element} parentEl - Parent element of this appended path element.
 * @param {Object} state - State of animation.
 * @param {Object} graphHelper - SummaryGraphHelper.
 */
function renderNegativeEndDelayHiddenProgress(parentEl, state, graphHelper) {
  const endTime = state.delay + state.iterationCount * state.duration;
  const startTime = endTime + state.endDelay;
  const segments = graphHelper.createPathSegments(startTime, endTime);
  graphHelper.appendShapePath(parentEl, segments, "enddelay-path negative");
}

/**
 * Create new keyframes object which has only offset and easing.
 * Also, the returned value has no duplication.
 * @param {Object} tracks - The value of AnimationsTimeline.getTracks().
 * @return {Array} keyframes list.
 */
function getOffsetAndEasingOnlyKeyframesList(tracks) {
  return Object.keys(tracks).reduce((result, name) => {
    const track = tracks[name];
    const exists = result.find(keyframes => {
      if (track.length !== keyframes.length) {
        return false;
      }
      for (let i = 0; i < track.length; i++) {
        const keyframe1 = track[i];
        const keyframe2 = keyframes[i];
        if (keyframe1.offset !== keyframe2.offset ||
            keyframe1.easing !== keyframe2.easing) {
          return false;
        }
      }
      return true;
    });
    if (!exists) {
      const keyframes = track.map(keyframe => {
        return { offset: keyframe.offset, easing: keyframe.easing };
      });
      result.push(keyframes);
    }
    return result;
  }, []);
}
PK
!<x��bfbf[chrome/devtools/modules/devtools/client/animationinspector/components/animation-timeline.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 { Task } = require("devtools/shared/task");
const EventEmitter = require("devtools/shared/event-emitter");
const {
  createNode,
  findOptimalTimeInterval,
  getFormattedAnimationTitle,
  TimeScale,
  getCssPropertyName
} = require("devtools/client/animationinspector/utils");
const { AnimationDetails } = require("devtools/client/animationinspector/components/animation-details");
const { AnimationTargetNode } = require("devtools/client/animationinspector/components/animation-target-node");
const { AnimationTimeBlock } = require("devtools/client/animationinspector/components/animation-time-block");

const { LocalizationHelper } = require("devtools/shared/l10n");
const L10N =
  new LocalizationHelper("devtools/client/locales/animationinspector.properties");

// The minimum spacing between 2 time graduation headers in the timeline (px).
const TIME_GRADUATION_MIN_SPACING = 40;
// When the container window is resized, the timeline background gets refreshed,
// but only after a timer, and the timer is reset if the window is continuously
// resized.
const TIMELINE_BACKGROUND_RESIZE_DEBOUNCE_TIMER = 50;

/**
 * UI component responsible for displaying a timeline for animations.
 * The timeline is essentially a graph with time along the x axis and animations
 * along the y axis.
 * The time is represented with a graduation header at the top and a current
 * time play head.
 * Animations are organized by lines, with a left margin containing the preview
 * of the target DOM element the animation applies to.
 * The current time play head can be moved by clicking/dragging in the header.
 * when this happens, the component emits "current-data-changed" events with the
 * new time and state of the timeline.
 *
 * @param {InspectorPanel} inspector.
 * @param {Object} serverTraits The list of server-side capabilities.
 */
function AnimationsTimeline(inspector, serverTraits) {
  this.animations = [];
  this.componentsMap = {};
  this.inspector = inspector;
  this.serverTraits = serverTraits;

  this.onAnimationStateChanged = this.onAnimationStateChanged.bind(this);
  this.onScrubberMouseDown = this.onScrubberMouseDown.bind(this);
  this.onScrubberMouseUp = this.onScrubberMouseUp.bind(this);
  this.onScrubberMouseOut = this.onScrubberMouseOut.bind(this);
  this.onScrubberMouseMove = this.onScrubberMouseMove.bind(this);
  this.onAnimationSelected = this.onAnimationSelected.bind(this);
  this.onWindowResize = this.onWindowResize.bind(this);
  this.onTimelineDataChanged = this.onTimelineDataChanged.bind(this);
  this.onDetailCloseButtonClick = this.onDetailCloseButtonClick.bind(this);

  EventEmitter.decorate(this);
}

exports.AnimationsTimeline = AnimationsTimeline;

AnimationsTimeline.prototype = {
  init: function (containerEl) {
    this.win = containerEl.ownerDocument.defaultView;
    this.rootWrapperEl = containerEl;

    this.setupSplitBox();
    this.setupAnimationTimeline();
    this.setupAnimationDetail();

    this.win.addEventListener("resize",
      this.onWindowResize);
  },

  setupSplitBox: function () {
    const browserRequire = this.win.BrowserLoader({
      window: this.win,
      useOnlyShared: true
    }).require;

    const { createFactory } = browserRequire("devtools/client/shared/vendor/react");
    const dom = require("devtools/client/shared/vendor/react-dom-factories");
    const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");

    const SplitBox = createFactory(
      browserRequire("devtools/client/shared/components/splitter/SplitBox"));

    const splitter = SplitBox({
      className: "animation-root",
      splitterSize: 1,
      initialHeight: "50%",
      endPanelControl: true,
      startPanel: dom.div({
        className: "animation-timeline"
      }),
      endPanel: dom.div({
        className: "animation-detail"
      }),
      vert: false
    });

    ReactDOM.render(splitter, this.rootWrapperEl);

    this.animationRootEl = this.rootWrapperEl.querySelector(".animation-root");
  },

  setupAnimationTimeline: function () {
    const animationTimelineEl = this.rootWrapperEl.querySelector(".animation-timeline");

    let scrubberContainer = createNode({
      parent: animationTimelineEl,
      attributes: {"class": "scrubber-wrapper"}
    });

    this.scrubberEl = createNode({
      parent: scrubberContainer,
      attributes: {
        "class": "scrubber"
      }
    });

    this.scrubberHandleEl = createNode({
      parent: this.scrubberEl,
      attributes: {
        "class": "scrubber-handle"
      }
    });
    createNode({
      parent: this.scrubberHandleEl,
      attributes: {
        "class": "scrubber-line"
      }
    });
    this.scrubberHandleEl.addEventListener("mousedown",
                                           this.onScrubberMouseDown);

    this.headerWrapper = createNode({
      parent: animationTimelineEl,
      attributes: {
        "class": "header-wrapper"
      }
    });

    this.timeHeaderEl = createNode({
      parent: this.headerWrapper,
      attributes: {
        "class": "time-header track-container"
      }
    });

    this.timeHeaderEl.addEventListener("mousedown",
                                       this.onScrubberMouseDown);

    this.timeTickEl = createNode({
      parent: animationTimelineEl,
      attributes: {
        "class": "time-body track-container"
      }
    });

    this.animationsEl = createNode({
      parent: animationTimelineEl,
      nodeType: "ul",
      attributes: {
        "class": "animations devtools-monospace"
      }
    });
  },

  setupAnimationDetail: function () {
    const animationDetailEl = this.rootWrapperEl.querySelector(".animation-detail");

    const animationDetailHeaderEl = createNode({
      parent: animationDetailEl,
      attributes: {
        "class": "animation-detail-header"
      }
    });

    const headerTitleEl = createNode({
      parent: animationDetailHeaderEl,
      attributes: {
        "class": "devtools-toolbar"
      }
    });

    createNode({
      parent: headerTitleEl,
      textContent: L10N.getStr("detail.headerTitle")
    });

    this.animationAnimationNameEl = createNode({
      parent: headerTitleEl
    });

    this.animationDetailCloseButton = createNode({
      parent: headerTitleEl,
      nodeType: "button",
      attributes: {
        "class": "devtools-button",
        title: L10N.getStr("detail.header.closeLabel"),
      }
    });
    this.animationDetailCloseButton.addEventListener("click",
                                                     this.onDetailCloseButtonClick);

    const animationDetailBodyEl = createNode({
      parent: animationDetailEl,
      attributes: {
        "class": "animation-detail-body"
      }
    });

    this.animatedPropertiesEl = createNode({
      parent: animationDetailBodyEl,
      attributes: {
        "class": "animated-properties"
      }
    });

    this.details = new AnimationDetails(this.serverTraits);
    this.details.init(this.animatedPropertiesEl);
  },

  destroy: function () {
    this.stopAnimatingScrubber();
    this.unrender();
    this.details.destroy();

    this.win.removeEventListener("resize",
      this.onWindowResize);
    this.timeHeaderEl.removeEventListener("mousedown",
      this.onScrubberMouseDown);
    this.scrubberHandleEl.removeEventListener("mousedown",
      this.onScrubberMouseDown);
    this.animationDetailCloseButton.removeEventListener("click",
      this.onDetailCloseButtonClick);

    this.rootWrapperEl.remove();
    this.animations = [];
    this.componentsMap = null;
    this.rootWrapperEl = null;
    this.timeHeaderEl = null;
    this.animationsEl = null;
    this.animatedPropertiesEl = null;
    this.scrubberEl = null;
    this.scrubberHandleEl = null;
    this.win = null;
    this.inspector = null;
    this.serverTraits = null;
    this.animationDetailEl = null;
    this.animationAnimationNameEl = null;
    this.animatedPropertiesEl = null;
    this.animationDetailCloseButton = null;
    this.animationRootEl = null;
    this.selectedAnimation = null;

    this.isDestroyed = true;
  },

  /**
   * Destroy all sub-components that have been created and stored on this instance.
   */
  destroyAllSubComponents: function () {
    for (let actorID in this.componentsMap) {
      this.destroySubComponents(actorID);
    }
  },

  /**
   * Destroy sub-components which related to given actor id.
   * @param {String} actor id
   */
  destroySubComponents: function (actorID) {
    const components = this.componentsMap[actorID];
    components.timeBlock.destroy();
    components.targetNode.destroy();
    components.animationEl.remove();
    delete components.state;
    delete components.tracks;
    delete this.componentsMap[actorID];
  },

  unrender: function () {
    for (let animation of this.animations) {
      animation.off("changed", this.onAnimationStateChanged);
    }
    this.stopAnimatingScrubber();
    TimeScale.reset();
    this.destroyAllSubComponents();
    this.animationsEl.innerHTML = "";
    this.off("timeline-data-changed", this.onTimelineDataChanged);
    this.details.unrender();
  },

  onWindowResize: function () {
    // Don't do anything if the root element has a width of 0
    if (this.rootWrapperEl.offsetWidth === 0) {
      return;
    }

    if (this.windowResizeTimer) {
      this.win.clearTimeout(this.windowResizeTimer);
    }

    this.windowResizeTimer = this.win.setTimeout(() => {
      this.drawHeaderAndBackground();
    }, TIMELINE_BACKGROUND_RESIZE_DEBOUNCE_TIMER);
  },

  onAnimationSelected: Task.async(function* (animation) {
    let index = this.animations.indexOf(animation);
    if (index === -1) {
      return;
    }

    // Unselect an animation which was selected.
    const animationEls = this.rootWrapperEl.querySelectorAll(".animation");
    for (let i = 0; i < animationEls.length; i++) {
      const animationEl = animationEls[i];
      if (!animationEl.classList.contains("selected")) {
        continue;
      }
      if (i === index) {
        if (this.animationRootEl.classList.contains("animation-detail-visible")) {
          // Already the animation is selected.
          this.emit("animation-already-selected", this.animations[i]);
          return;
        }
      } else {
        animationEl.classList.remove("selected");
        this.emit("animation-unselected", this.animations[i]);
      }
      break;
    }

    // Add class of animation type to animatedPropertiesEl to display the compositor sign.
    if (!this.animatedPropertiesEl.classList.contains(animation.state.type)) {
      this.animatedPropertiesEl.className =
        `animated-properties ${ animation.state.type }`;
    }

    // Select and render.
    const selectedAnimationEl = animationEls[index];
    selectedAnimationEl.classList.add("selected");
    this.animationRootEl.classList.add("animation-detail-visible");
    // Don't render if the detail displays same animation already.
    if (animation !== this.details.animation) {
      this.selectedAnimation = animation;
      yield this.details.render(animation, this.componentsMap[animation.actorID].tracks);
      this.animationAnimationNameEl.textContent = getFormattedAnimationTitle(animation);
    }
    this.onTimelineDataChanged({ time: this.currentTime || 0 });
    this.emit("animation-selected", animation);
  }),

  /**
   * When move the scrubber to the corresponding position
   */
  onScrubberMouseDown: function (e) {
    this.moveScrubberTo(e.pageX);
    this.win.addEventListener("mouseup", this.onScrubberMouseUp);
    this.win.addEventListener("mouseout", this.onScrubberMouseOut);
    this.win.addEventListener("mousemove", this.onScrubberMouseMove);

    // Prevent text selection while dragging.
    e.preventDefault();
  },

  onScrubberMouseUp: function () {
    this.cancelTimeHeaderDragging();
  },

  onScrubberMouseOut: function (e) {
    // Check that mouseout happened on the window itself, and if yes, cancel
    // the dragging.
    if (!this.win.document.contains(e.relatedTarget)) {
      this.cancelTimeHeaderDragging();
    }
  },

  cancelTimeHeaderDragging: function () {
    this.win.removeEventListener("mouseup", this.onScrubberMouseUp);
    this.win.removeEventListener("mouseout", this.onScrubberMouseOut);
    this.win.removeEventListener("mousemove", this.onScrubberMouseMove);
  },

  onScrubberMouseMove: function (e) {
    this.moveScrubberTo(e.pageX);
  },

  moveScrubberTo: function (pageX, noOffset) {
    this.stopAnimatingScrubber();

    // The offset needs to be in % and relative to the timeline's area (so we
    // subtract the scrubber's left offset, which is equal to the sidebar's
    // width).
    let offset = pageX;
    if (!noOffset) {
      offset -= this.timeHeaderEl.offsetLeft;
    }
    offset = offset * 100 / this.timeHeaderEl.offsetWidth;
    if (offset < 0) {
      offset = 0;
    }

    this.scrubberEl.style.left = offset + "%";

    let time = TimeScale.distanceToRelativeTime(offset);

    this.emit("timeline-data-changed", {
      isPaused: true,
      isMoving: false,
      isUserDrag: true,
      time: time
    });
  },

  getCompositorStatusClassName: function (state) {
    let className = state.isRunningOnCompositor
                    ? " fast-track"
                    : "";

    if (state.isRunningOnCompositor && state.propertyState) {
      className +=
        state.propertyState.some(propState => !propState.runningOnCompositor)
        ? " some-properties"
        : " all-properties";
    }

    return className;
  },

  render: Task.async(function* (animations, documentCurrentTime) {
    this.animations = animations;

    // Destroy components which are no longer existed in given animations.
    for (let animation of this.animations) {
      if (this.componentsMap[animation.actorID]) {
        this.componentsMap[animation.actorID].needToLeave = true;
      }
    }
    for (let actorID in this.componentsMap) {
      const components = this.componentsMap[actorID];
      if (components.needToLeave) {
        delete components.needToLeave;
      } else {
        this.destroySubComponents(actorID);
      }
    }

    if (!this.animations.length) {
      this.emit("animation-timeline-rendering-completed");
      return;
    }

    // Loop to set the time scale for all current animations.
    TimeScale.reset();
    for (let {state} of animations) {
      TimeScale.addAnimation(state);
    }

    this.drawHeaderAndBackground();

    for (let animation of this.animations) {
      animation.on("changed", this.onAnimationStateChanged);

      const tracks = yield this.getTracks(animation);
      // If we're destroyed by now, just give up.
      if (this.isDestroyed) {
        return;
      }

      if (this.componentsMap[animation.actorID]) {
        // Update animation UI using existent components.
        this.updateAnimation(animation, tracks, this.componentsMap[animation.actorID]);
      } else {
        // Render animation UI as new element.
        const animationEl = createNode({
          parent: this.animationsEl,
          nodeType: "li",
        });
        this.renderAnimation(animation, tracks, animationEl);
      }
    }

    // Use the document's current time to position the scrubber (if the server
    // doesn't provide it, hide the scrubber entirely).
    // Note that because the currentTime was sent via the protocol, some time
    // may have gone by since then, and so the scrubber might be a bit late.
    if (!documentCurrentTime) {
      this.scrubberEl.style.display = "none";
    } else {
      this.scrubberEl.style.display = "block";
      this.startAnimatingScrubber(this.wasRewound()
                                  ? TimeScale.minStartTime
                                  : documentCurrentTime);
    }

    // To indicate the animation progress in AnimationDetails.
    this.on("timeline-data-changed", this.onTimelineDataChanged);

    if (this.animations.length === 1) {
      // Display animation's detail if there is only one animation,
      // even if the detail pane is closing.
      yield this.onAnimationSelected(this.animations[0]);
    } else if (this.animationRootEl.classList.contains("animation-detail-visible") &&
               this.animations.includes(this.selectedAnimation)) {
      // animation's detail displays in case of the previously displayed animation is
      // included in timeline list and the detail pane is not closing.
      yield this.onAnimationSelected(this.selectedAnimation);
    } else {
      // Otherwise, close detail pane.
      this.onDetailCloseButtonClick();
    }
    this.emit("animation-timeline-rendering-completed");
  }),

  updateAnimation: function (animation, tracks, existentComponents) {
    // If keyframes (tracks) and effect timing (state) are not changed, we update the
    // view box only.
    // As an exception, if iterationCount reprensents Infinity, we need to re-render
    // the shape along new time scale.
    // FIXME: To avoid re-rendering even Infinity, we need to change the
    // representation for Infinity.
    if (animation.state.iterationCount &&
        areTimingEffectsEqual(existentComponents.state, animation.state) &&
        existentComponents.tracks.toString() === tracks.toString()) {
      // Update timeBlock.
      existentComponents.timeBlock.update(animation);
    } else {
      // Destroy previous components.
      existentComponents.timeBlock.destroy();
      existentComponents.targetNode.destroy();
      // Remove children to re-use.
      existentComponents.animationEl.innerHTML = "";
      // Re-render animation using existent animationEl.
      this.renderAnimation(animation, tracks, existentComponents.animationEl);
    }
  },

  renderAnimation: function (animation, tracks, animationEl) {
    animationEl.setAttribute("class",
                             "animation " + animation.state.type +
                             this.getCompositorStatusClassName(animation.state));

    // Left sidebar for the animated node.
    let animatedNodeEl = createNode({
      parent: animationEl,
      attributes: {
        "class": "target"
      }
    });

    // Draw the animated node target.
    let targetNode = new AnimationTargetNode(this.inspector, {compact: true});
    targetNode.init(animatedNodeEl);
    targetNode.render(animation);

    // Right-hand part contains the timeline itself (called time-block here).
    let timeBlockEl = createNode({
      parent: animationEl,
      attributes: {
        "class": "time-block track-container"
      }
    });

    // Draw the animation time block.
    let timeBlock = new AnimationTimeBlock();
    timeBlock.init(timeBlockEl);
    timeBlock.render(animation, tracks);
    timeBlock.on("selected", this.onAnimationSelected);

    this.componentsMap[animation.actorID] = {
      animationEl, targetNode, timeBlock, tracks, state: animation.state
    };
  },

  isAtLeastOneAnimationPlaying: function () {
    return this.animations.some(({state}) => state.playState === "running");
  },

  wasRewound: function () {
    return !this.isAtLeastOneAnimationPlaying() &&
           this.animations.every(({state}) => state.currentTime === 0);
  },

  hasInfiniteAnimations: function () {
    return this.animations.some(({state}) => !state.iterationCount);
  },

  startAnimatingScrubber: function (time) {
    let isOutOfBounds = time < TimeScale.minStartTime ||
                        time > TimeScale.maxEndTime;
    let isAllPaused = !this.isAtLeastOneAnimationPlaying();
    let hasInfinite = this.hasInfiniteAnimations();

    let x = TimeScale.startTimeToDistance(time);
    if (x > 100 && !hasInfinite) {
      x = 100;
    }
    this.scrubberEl.style.left = x + "%";

    // Only stop the scrubber if it's out of bounds or all animations have been
    // paused, but not if at least an animation is infinite.
    if (isAllPaused || (isOutOfBounds && !hasInfinite)) {
      this.stopAnimatingScrubber();
      this.emit("timeline-data-changed", {
        isPaused: !this.isAtLeastOneAnimationPlaying(),
        isMoving: false,
        isUserDrag: false,
        time: TimeScale.distanceToRelativeTime(x)
      });
      return;
    }

    this.emit("timeline-data-changed", {
      isPaused: false,
      isMoving: true,
      isUserDrag: false,
      time: TimeScale.distanceToRelativeTime(x)
    });

    let now = this.win.performance.now();
    this.rafID = this.win.requestAnimationFrame(() => {
      if (!this.rafID) {
        // In case the scrubber was stopped in the meantime.
        return;
      }
      this.startAnimatingScrubber(time + this.win.performance.now() - now);
    });
  },

  stopAnimatingScrubber: function () {
    if (this.rafID) {
      this.win.cancelAnimationFrame(this.rafID);
      this.rafID = null;
    }
  },

  onAnimationStateChanged: function () {
    // For now, simply re-render the component. The animation front's state has
    // already been updated.
    this.render(this.animations);
  },

  drawHeaderAndBackground: function () {
    let width = this.timeHeaderEl.offsetWidth;
    let animationDuration = TimeScale.maxEndTime - TimeScale.minStartTime;
    let minTimeInterval = TIME_GRADUATION_MIN_SPACING *
                          animationDuration / width;
    let intervalLength = findOptimalTimeInterval(minTimeInterval);
    let intervalWidth = intervalLength * width / animationDuration;

    // And the time graduation header.
    this.timeHeaderEl.innerHTML = "";
    this.timeTickEl.innerHTML = "";

    for (let i = 0; i <= width / intervalWidth; i++) {
      let pos = 100 * i * intervalWidth / width;

      // This element is the header of time tick for displaying animation
      // duration time.
      createNode({
        parent: this.timeHeaderEl,
        nodeType: "span",
        attributes: {
          "class": "header-item",
          "style": `left:${pos}%`
        },
        textContent: TimeScale.formatTime(TimeScale.distanceToRelativeTime(pos))
      });

      // This element is displayed as a vertical line separator corresponding
      // the header of time tick for indicating time slice for animation
      // iterations.
      createNode({
        parent: this.timeTickEl,
        nodeType: "span",
        attributes: {
          "class": "time-tick",
          "style": `left:${pos}%`
        }
      });
    }
  },

  onTimelineDataChanged: function ({ time }) {
    this.currentTime = time;
    const indicateTime =
      TimeScale.minStartTime === Infinity ? 0 : this.currentTime + TimeScale.minStartTime;
    this.details.indicateProgress(indicateTime);
  },

  onDetailCloseButtonClick: function (e) {
    if (!this.animationRootEl.classList.contains("animation-detail-visible")) {
      return;
    }
    this.animationRootEl.classList.remove("animation-detail-visible");
    this.emit("animation-detail-closed");
  },

  /**
   * Get a list of the tracks of the animation actor
   * @param {Object} animation
   * @return {Object} A list of tracks, one per animated property, each
   * with a list of keyframes
   */
  getTracks: Task.async(function* (animation) {
    let tracks = {};

    /*
     * getFrames is a AnimationPlayorActor method that returns data about the
     * keyframes of the animation.
     * In FF48, the data it returns change, and will hold only longhand
     * properties ( e.g. borderLeftWidth ), which does not match what we
     * want to display in the animation detail.
     * A new AnimationPlayerActor function, getProperties, is introduced,
     * that returns the animated css properties of the animation and their
     * keyframes values.
     * If the animation actor has the getProperties function, we use it, and if
     * not, we fall back to getFrames, which then returns values we used to
     * handle.
     */
    if (this.serverTraits.hasGetProperties) {
      let properties = [];
      try {
        properties = yield animation.getProperties();
      } catch (e) {
        // Expected if we've already been destroyed in the meantime.
        if (!this.isDestroyed) {
          throw e;
        }
      }

      for (let {name, values} of properties) {
        if (!tracks[name]) {
          tracks[name] = [];
        }

        for (let {value, offset, easing, distance} of values) {
          distance = distance ? distance : 0;
          offset = parseFloat(offset.toFixed(3));
          tracks[name].push({value, offset, easing, distance});
        }
      }
    } else {
      let frames = [];
      try {
        frames = yield animation.getFrames();
      } catch (e) {
        // Expected if we've already been destroyed in the meantime.
        if (!this.isDestroyed) {
          throw e;
        }
      }

      for (let frame of frames) {
        for (let name in frame) {
          if (this.NON_PROPERTIES.includes(name)) {
            continue;
          }

          // We have to change to CSS property name
          // since GetKeyframes returns JS property name.
          const propertyCSSName = getCssPropertyName(name);
          if (!tracks[propertyCSSName]) {
            tracks[propertyCSSName] = [];
          }

          tracks[propertyCSSName].push({
            value: frame[name],
            offset: parseFloat(frame.computedOffset.toFixed(3)),
            easing: frame.easing,
            distance: 0
          });
        }
      }
    }

    return tracks;
  })
};

/**
 * Check the equality given states as effect timing.
 * @param {Object} state of animation.
 * @param {Object} same to avobe.
 * @return {boolean} true: same effect timing
 */
function areTimingEffectsEqual(stateA, stateB) {
  for (const property of ["playbackRate", "duration", "delay", "endDelay",
                          "iterationCount", "iterationStart", "easing",
                          "fill", "direction"]) {
    if (stateA[property] !== stateB[property]) {
      return false;
    }
  }
  return true;
}
PK
!<RՕ8��Rchrome/devtools/modules/devtools/client/animationinspector/components/keyframes.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 {createNode, createSVGNode} =
  require("devtools/client/animationinspector/utils");
const { ProgressGraphHelper, } =
  require("devtools/client/animationinspector/graph-helper.js");

// Counter for linearGradient ID.
let LINEAR_GRADIENT_ID_COUNTER = 0;

/**
 * UI component responsible for displaying a list of keyframes.
 * Also, shows a graphical graph for the animation progress of one iteration.
 */
function Keyframes() {}

exports.Keyframes = Keyframes;

Keyframes.prototype = {
  init: function (containerEl) {
    this.containerEl = containerEl;

    this.keyframesEl = createNode({
      parent: this.containerEl,
      attributes: {"class": "keyframes"}
    });
  },

  destroy: function () {
    this.keyframesEl.remove();
    this.containerEl = this.keyframesEl = this.animation = null;
  },

  render: function ({keyframes, propertyName, animation, animationType}) {
    this.keyframes = keyframes;
    this.propertyName = propertyName;
    this.animation = animation;

    // Create graph element.
    const graphEl = createSVGNode({
      parent: this.keyframesEl,
      nodeType: "svg",
      attributes: {
        "preserveAspectRatio": "none"
      }
    });

    // This visual is only one iteration,
    // so we use animation.state.duration as total duration.
    const totalDuration = animation.state.duration;

    // Minimum segment duration is the duration of one pixel.
    const minSegmentDuration =
      totalDuration / this.containerEl.clientWidth;

    // Create graph helper to render the animation property graph.
    const win = this.containerEl.ownerGlobal;
    const graphHelper =
      new ProgressGraphHelper(win, propertyName, animationType, keyframes, totalDuration);

    renderPropertyGraph(graphEl, totalDuration, minSegmentDuration, graphHelper);

    // Destroy ProgressGraphHelper resources.
    graphHelper.destroy();

    // Set viewBox which includes invisible stroke width.
    // At first, calculate invisible stroke width from maximum width.
    // The reason why divide by 2 is that half of stroke width will be invisible
    // if we use 0 or 1 for y axis.
    const maxStrokeWidth =
      win.getComputedStyle(graphEl.querySelector(".keyframes svg .hint")).strokeWidth;
    const invisibleStrokeWidthInViewBox =
      maxStrokeWidth / 2 / this.containerEl.clientHeight;
    graphEl.setAttribute("viewBox",
                         `0 -${ 1 + invisibleStrokeWidthInViewBox }
                          ${ totalDuration }
                          ${ 1 + invisibleStrokeWidthInViewBox * 2 }`);

    // Append elements to display keyframe values.
    this.keyframesEl.classList.add(animation.state.type);
    for (let frame of this.keyframes) {
      createNode({
        parent: this.keyframesEl,
        attributes: {
          "class": "frame",
          "style": `left:${frame.offset * 100}%;`,
          "data-offset": frame.offset,
          "data-property": propertyName,
          "title": frame.value
        }
      });
    }
  }
};

/**
 * Render a graph representing the progress of the animation over one iteration.
 * @param {Element} parentEl - Parent element of this appended path element.
 * @param {Number} duration - Duration of one iteration.
 * @param {Number} minSegmentDuration - Minimum segment duration.
 * @param {ProgressGraphHelper} graphHelper - The object of ProgressGraphHalper.
 */
function renderPropertyGraph(parentEl, duration, minSegmentDuration, graphHelper) {
  const segments = graphHelper.createPathSegments(duration, minSegmentDuration);

  const graphType = graphHelper.getGraphType();
  if (graphType !== "color") {
    graphHelper.appendShapePath(parentEl, segments, graphType);
    renderEasingHint(parentEl, segments, graphHelper);
    return;
  }

  // Append the color to the path.
  segments.forEach(segment => {
    segment.y = 1;
  });
  const path = graphHelper.appendShapePath(parentEl, segments, graphType);
  const defEl = createSVGNode({
    parent: parentEl,
    nodeType: "def"
  });
  const id = `color-property-${ LINEAR_GRADIENT_ID_COUNTER++ }`;
  const linearGradientEl = createSVGNode({
    parent: defEl,
    nodeType: "linearGradient",
    attributes: {
      "id": id
    }
  });
  segments.forEach(segment => {
    createSVGNode({
      parent: linearGradientEl,
      nodeType: "stop",
      attributes: {
        "stop-color": segment.style,
        "offset": segment.x / duration
      }
    });
  });
  path.style.fill = `url(#${ id })`;

  renderEasingHintForColor(parentEl, graphHelper);
}

/**
 * Renders the easing hint.
 * This method renders an emphasized path over the easing path for a keyframe.
 * It appears when hovering over the easing.
 * It also renders a tooltip that appears when hovering.
 * @param {Element} parentEl - Parent element of this appended path element.
 * @param {Array} path segments - [{x: {Number} time, y: {Number} progress}, ...]
 * @param {ProgressGraphHelper} graphHelper - The object of ProgressGraphHelper.
 */
function renderEasingHint(parentEl, segments, helper) {
  const keyframes = helper.getKeyframes();
  const duration = helper.getDuration();

  // Split segments for each keyframe.
  for (let i = 0, indexOfSegments = 0; i < keyframes.length - 1; i++) {
    const startKeyframe = keyframes[i];
    const endKeyframe = keyframes[i + 1];
    const endTime = endKeyframe.offset * duration;
    const keyframeSegments = [];

    for (; indexOfSegments < segments.length; indexOfSegments++) {
      const segment = segments[indexOfSegments];
      keyframeSegments.push(segment);

      if (startKeyframe.offset === endKeyframe.offset) {
        keyframeSegments.push(segments[++indexOfSegments]);
        break;
      } else if (segment.x === endTime) {
        break;
      }
    }

    // Append easing hint as text and emphasis path.
    const gEl = createSVGNode({
      parent: parentEl,
      nodeType: "g"
    });
    createSVGNode({
      parent: gEl,
      nodeType: "title",
      textContent: startKeyframe.easing
    });
    helper.appendLinePath(gEl, keyframeSegments, `${helper.getGraphType()} hint`);
  }
}

/**
 * Render easing hint for properties that are represented by color.
 * This method render as text only.
 * @param {Element} parentEl - Parent element of this appended path element.
 * @param {ProgressGraphHelper} graphHelper - The object of ProgressGraphHalper.
 */
function renderEasingHintForColor(parentEl, helper) {
  const keyframes = helper.getKeyframes();
  const duration = helper.getDuration();

  // Split segments for each keyframe.
  for (let i = 0; i < keyframes.length - 1; i++) {
    const startKeyframe = keyframes[i];
    const startTime = startKeyframe.offset * duration;
    const endKeyframe = keyframes[i + 1];
    const endTime = endKeyframe.offset * duration;

    // Append easing hint.
    const gEl = createSVGNode({
      parent: parentEl,
      nodeType: "g"
    });
    createSVGNode({
      parent: gEl,
      nodeType: "title",
      textContent: startKeyframe.easing
    });
    createSVGNode({
      parent: gEl,
      nodeType: "rect",
      attributes: {
        x: startTime,
        y: -1,
        width: endTime - startTime,
        height: 1,
        class: "hint",
      }
    });
  }
}
PK
!<e���K
K
Vchrome/devtools/modules/devtools/client/animationinspector/components/rate-selector.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 EventEmitter = require("devtools/shared/event-emitter");
const {createNode} = require("devtools/client/animationinspector/utils");
const { LocalizationHelper } = require("devtools/shared/l10n");
const L10N =
      new LocalizationHelper("devtools/client/locales/animationinspector.properties");

// List of playback rate presets displayed in the timeline toolbar.
const PLAYBACK_RATES = [.1, .25, .5, 1, 2, 5, 10];

/**
 * UI component responsible for displaying a playback rate selector UI.
 * The rendering logic is such that a predefined list of rates is generated.
 * If *all* animations passed to render share the same rate, then that rate is
 * selected in the <select> element, otherwise, the empty value is selected.
 * If the rate that all animations share isn't part of the list of predefined
 * rates, than that rate is added to the list.
 */
function RateSelector() {
  this.onRateChanged = this.onRateChanged.bind(this);
  EventEmitter.decorate(this);
}

exports.RateSelector = RateSelector;

RateSelector.prototype = {
  init: function (containerEl) {
    this.selectEl = createNode({
      parent: containerEl,
      nodeType: "select",
      attributes: {
        "class": "devtools-button",
        "title": L10N.getStr("timeline.rateSelectorTooltip")
      }
    });

    this.selectEl.addEventListener("change", this.onRateChanged);
  },

  destroy: function () {
    this.selectEl.removeEventListener("change", this.onRateChanged);
    this.selectEl.remove();
    this.selectEl = null;
  },

  getAnimationsRates: function (animations) {
    return sortedUnique(animations.map(a => a.state.playbackRate));
  },

  getAllRates: function (animations) {
    let animationsRates = this.getAnimationsRates(animations);
    if (animationsRates.length > 1) {
      return PLAYBACK_RATES;
    }

    return sortedUnique(PLAYBACK_RATES.concat(animationsRates));
  },

  render: function (animations) {
    let allRates = this.getAnimationsRates(animations);
    let hasOneRate = allRates.length === 1;

    this.selectEl.innerHTML = "";

    if (!hasOneRate) {
      // When the animations displayed have mixed playback rates, we can't
      // select any of the predefined ones, instead, insert an empty rate.
      createNode({
        parent: this.selectEl,
        nodeType: "option",
        attributes: {value: "", selector: "true"},
        textContent: "-"
      });
    }
    for (let rate of this.getAllRates(animations)) {
      let option = createNode({
        parent: this.selectEl,
        nodeType: "option",
        attributes: {value: rate},
        textContent: L10N.getFormatStr("player.playbackRateLabel", rate)
      });

      // If there's only one rate and this is the option for it, select it.
      if (hasOneRate && rate === allRates[0]) {
        option.setAttribute("selected", "true");
      }
    }
  },

  onRateChanged: function () {
    let rate = parseFloat(this.selectEl.value);
    if (!isNaN(rate)) {
      this.emit("rate-changed", rate);
    }
  }
};

let sortedUnique = arr => [...new Set(arr)].sort((a, b) => a > b);
PK
!<�M��<v<vJchrome/devtools/modules/devtools/client/animationinspector/graph-helper.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 {createSVGNode, getJsPropertyName} =
  require("devtools/client/animationinspector/utils");
const {colorUtils} = require("devtools/shared/css/color.js");
const {parseTimingFunction} = require("devtools/client/shared/widgets/CubicBezierWidget");

// In the createPathSegments function, an animation duration is divided by
// DURATION_RESOLUTION in order to draw the way the animation progresses.
// But depending on the timing-function, we may be not able to make the graph
// smoothly progress if this resolution is not high enough.
// So, if the difference of animation progress between 2 divisions is more than
// DEFAULT_MIN_PROGRESS_THRESHOLD, then createPathSegments re-divides
// by DURATION_RESOLUTION.
// DURATION_RESOLUTION shoud be integer and more than 2.
const DURATION_RESOLUTION = 4;
// DEFAULT_MIN_PROGRESS_THRESHOLD shoud be between more than 0 to 1.
const DEFAULT_MIN_PROGRESS_THRESHOLD = 0.1;
exports.DEFAULT_MIN_PROGRESS_THRESHOLD = DEFAULT_MIN_PROGRESS_THRESHOLD;
// BOUND_EXCLUDING_TIME should be less than 1ms and is used to exclude start
// and end bounds when dividing  duration in createPathSegments.
const BOUND_EXCLUDING_TIME = 0.001;

/**
 * This helper return the segment coordinates and style for property graph,
 * also return the graph type.
 * Parameters of constructor are below.
 * @param {Window} win - window object to animate.
 * @param {String} propertyCSSName - CSS property name (e.g. background-color).
 * @param {String} animationType - Animation type of CSS property.
 * @param {Object} keyframes - AnimationInspector's keyframes object.
 * @param {float}  duration - Duration of animation.
 */
function ProgressGraphHelper(win, propertyCSSName, animationType, keyframes, duration) {
  this.win = win;
  const doc = this.win.document;
  this.targetEl = doc.createElement("div");
  doc.documentElement.appendChild(this.targetEl);

  this.propertyCSSName = propertyCSSName;
  this.propertyJSName = getJsPropertyName(this.propertyCSSName);
  this.animationType = animationType;

  // Create keyframe object to make dummy animation.
  const keyframesObject = keyframes.map(keyframe => {
    const keyframeObject = Object.assign({}, keyframe);
    keyframeObject[this.propertyJSName] = keyframe.value;
    return keyframeObject;
  });

  // Create effect timing object to make dummy animation.
  const effectTiming = {
    duration: duration,
    fill: "forwards"
  };

  this.keyframes = keyframesObject;
  this.devtoolsKeyframes = keyframes;
  this.valueHelperFunction = this.getValueHelperFunction();
  this.animation = this.targetEl.animate(this.keyframes, effectTiming);
  this.animation.pause();
}

ProgressGraphHelper.prototype = {
  /**
   * Destory this object.
   */
  destroy: function () {
    this.targetEl.remove();
    this.animation.cancel();

    this.targetEl = null;
    this.animation = null;
    this.valueHelperFunction = null;
    this.propertyCSSName = null;
    this.propertyJSName = null;
    this.animationType = null;
    this.keyframes = null;
    this.win = null;
  },

  /**
   * Return animation duration.
   * @return {Number} duration
   */
  getDuration: function () {
    return this.animation.effect.timing.duration;
  },

  /**
   * Return animation's keyframe.
   * @return {Object} keyframe
   */
  getKeyframes: function () {
    return this.keyframes;
  },

  /**
   * Return graph type.
   * @return {String} if property is 'opacity' or 'transform', return that value.
   *                  Otherwise, return given animation type in constructor.
   */
  getGraphType: function () {
    return (this.propertyJSName === "opacity" || this.propertyJSName === "transform")
           ? this.propertyJSName : this.animationType;
  },

  /**
   * Return a segment in graph by given the time.
   * @return {Object} Computed result which has follwing values.
   * - x: x value of graph (float)
   * - y: y value of graph (float between 0 - 1)
   * - style: the computed style value of the property at the time
   */
  getSegment: function (time) {
    this.animation.currentTime = time;
    const style = this.win.getComputedStyle(this.targetEl)[this.propertyJSName];
    const value = this.valueHelperFunction(style);
    return { x: time, y: value, style: style };
  },

  /**
   * Get a value helper function which calculates the value of Y axis by animation type.
   * @return {function} ValueHelperFunction returns float value of Y axis
   *                    from given progress and style (e.g. rgb(0, 0, 0))
   */
  getValueHelperFunction: function () {
    switch (this.animationType) {
      case "none": {
        return () => 1;
      }
      case "float": {
        return this.getFloatValueHelperFunction();
      }
      case "coord": {
        return this.getCoordinateValueHelperFunction();
      }
      case "color": {
        return this.getColorValueHelperFunction();
      }
      case "discrete": {
        return this.getDiscreteValueHelperFunction();
      }
    }
    return null;
  },

  /**
   * Return value helper function of animation type 'float'.
   * @param {Object} keyframes - This object shoud be same as
   *                             the parameter of getGraphHelper.
   * @return {function} ValueHelperFunction returns float value of Y axis
   *                    from given float (e.g. 1.0, 0.5 and so on)
   */
  getFloatValueHelperFunction: function () {
    let maxValue = 0;
    let minValue = Infinity;
    this.keyframes.forEach(keyframe => {
      maxValue = Math.max(maxValue, keyframe.value);
      minValue = Math.min(minValue, keyframe.value);
    });
    const distance = maxValue - minValue;
    return value => {
      return (value - minValue) / distance;
    };
  },

  /**
   * Return value helper function of animation type 'coord'.
   * @return {function} ValueHelperFunction returns float value of Y axis
   *                    from given style (e.g. 100px)
   */
  getCoordinateValueHelperFunction: function () {
    let maxValue = 0;
    let minValue = Infinity;
    for (let i = 0, n = this.keyframes.length; i < n; i++) {
      if (this.keyframes[i].value.match(/calc/)) {
        return null;
      }
      const value = parseFloat(this.keyframes[i].value);
      minValue = Math.min(minValue, value);
      maxValue = Math.max(maxValue, value);
    }
    const distance = maxValue - minValue;
    return value => {
      return (parseFloat(value) - minValue) / distance;
    };
  },

  /**
   * Return value helper function of animation type 'color'.
   * @param {Object} keyframes - This object shoud be same as
   *                             the parameter of getGraphHelper.
   * @return {function} ValueHelperFunction returns float value of Y axis
   *                    from given color (e.g. rgb(0, 0, 0))
   */
  getColorValueHelperFunction: function () {
    const maxObject = { distance: 0 };
    for (let i = 0; i < this.keyframes.length - 1; i++) {
      const value1 = getRGBA(this.keyframes[i].value);
      for (let j = i + 1; j < this.keyframes.length; j++) {
        const value2 = getRGBA(this.keyframes[j].value);
        const distance = getRGBADistance(value1, value2);
        if (maxObject.distance >= distance) {
          continue;
        }
        maxObject.distance = distance;
        maxObject.value1 = value1;
        maxObject.value2 = value2;
      }
    }
    const baseValue =
      maxObject.value1 < maxObject.value2 ? maxObject.value1 : maxObject.value2;

    return value => {
      const colorValue = getRGBA(value);
      return getRGBADistance(baseValue, colorValue) / maxObject.distance;
    };
  },

  /**
   * Return value helper function of animation type 'discrete'.
   * @return {function} ValueHelperFunction returns float value of Y axis
   *                    from given style (e.g. center)
   */
  getDiscreteValueHelperFunction: function () {
    const discreteValues = [];
    this.keyframes.forEach(keyframe => {
      // Set style once since the computed value may differ from specified keyframe value.
      this.targetEl.style[this.propertyJSName] = keyframe.value;
      const style = this.win.getComputedStyle(this.targetEl)[this.propertyJSName];
      if (!discreteValues.includes(style)) {
        discreteValues.push(style);
      }
    });
    this.targetEl.style[this.propertyJSName] = "unset";

    return value => {
      return discreteValues.indexOf(value) / (discreteValues.length - 1);
    };
  },

  /**
   * Create the path segments from given parameters.
   *
   * @param {Number} duration - Duration of animation.
   * @param {Number} minSegmentDuration - Minimum segment duration.
   * @param {Number} minProgressThreshold - Minimum progress threshold.
   * @return {Array} path segments -
   *                 [{x: {Number} time, y: {Number} progress}, ...]
   */
  createPathSegments: function (duration, minSegmentDuration, minProgressThreshold) {
    if (!this.valueHelperFunction) {
      return createKeyframesPathSegments(duration, this.devtoolsKeyframes);
    }

    const segments = [];

    for (let i = 0; i < this.devtoolsKeyframes.length - 1; i++) {
      const startKeyframe = this.devtoolsKeyframes[i];
      const endKeyframe = this.devtoolsKeyframes[i + 1];
      const startTime = startKeyframe.offset * duration;
      const endTime = endKeyframe.offset * duration;

      if (startKeyframe.offset === endKeyframe.offset) {
        const startSegment = this.getSegment(startTime - BOUND_EXCLUDING_TIME);
        startSegment.x = startTime;
        const endSegment = this.getSegment(endTime);
        segments.push(startSegment, endSegment);
      } else {
        let threshold = getPreferredProgressThreshold(startKeyframe.easing);

        if (threshold !== DEFAULT_MIN_PROGRESS_THRESHOLD) {
          // We should consider the keyframe's duration.
          threshold *= (endKeyframe.offset - startKeyframe.offset);
        }

        segments.push(...createPathSegments(startTime, endTime - BOUND_EXCLUDING_TIME,
                                            minSegmentDuration, threshold, this));
      }
    }

    const lastKeyframe = this.devtoolsKeyframes[this.devtoolsKeyframes.length - 1];
    const lastTime = lastKeyframe.offset * duration;
    segments.push(this.getSegment(lastTime));
    return segments;
  },

  /**
   * Append path element as shape. Also, this method appends two segment
   * that are {start x, 0} and {end x, 0} to make shape.
   * @param {Element} parentEl - Parent element of this appended path element.
   * @param {Array} pathSegments - Path segments. Please see createPathSegments.
   * @param {String} cls - Class name.
   * @return {Element} path element.
   */
  appendShapePath: function (parentEl, pathSegments, cls) {
    return appendShapePath(parentEl, pathSegments, cls);
  },

  /**
   * Append path element as line.
   * @param {Element} parentEl - Parent element of this appended path element.
   * @param {Array} pathSegments - Path segments. Please see createPathSegments.
   * @param {String} cls - Class name.
   * @return {Element} path element.
   */
  appendLinePath: function (parentEl, pathSegments, cls) {
    const isClosePathNeeded = false;
    return appendPathElement(parentEl, pathSegments, cls, isClosePathNeeded);
  },
};

exports.ProgressGraphHelper = ProgressGraphHelper;

/**
 * This class is used for creating the summary graph in animation-timeline.
 * The shape of the graph can be changed by using the following methods:
 * setKeyframes:
 *   If null, the shape is by computed timing progress.
 *   Otherwise, by computed style of 'opacity' to combine effect easing and
 *   keyframe's easing.
 * setFillMode:
 *   Animation fill-mode (e.g. "none", "backwards", "forwards" or "both")
 * setClosePathNeeded:
 *   If true, appendShapePath make the last segment of <path> element to
 *   "close" segment("Z").
 *   Therefore, if don't need under-line of graph, please set false.
 * setOriginalBehavior:
 *   In Animation::SetCurrentTime spec, even if current time of animation is over
 *   the endTime, the progress is changed. Likewise, in case the time is less than 0.
 *   If set true, prevent the time to make the same animation behavior as the original.
 * setMinProgressThreshold:
 *   SummaryGraphHelper searches and creates the summary graph until the progress
 *   distance is less than this minProgressThreshold.
 *   So, while setting a low threshold produces a smooth graph,
 *   it will have an effect on performance.
 * @param {Object} win - window object.
 * @param {Object} state - animation state.
 * @param {Number} minSegmentDuration - Minimum segment duration.
 */
function SummaryGraphHelper(win, state, minSegmentDuration) {
  this.win = win;
  const doc = this.win.document;
  this.targetEl = doc.createElement("div");
  doc.documentElement.appendChild(this.targetEl);

  const effectTiming = Object.assign({}, state, {
    iterations: state.iterationCount ? state.iterationCount : Infinity
  });
  this.animation = this.targetEl.animate(null, effectTiming);
  this.animation.pause();
  this.endTime = this.animation.effect.getComputedTiming().endTime;

  this.minSegmentDuration = minSegmentDuration;
  this.minProgressThreshold = DEFAULT_MIN_PROGRESS_THRESHOLD;
}

SummaryGraphHelper.prototype = {
  /**
   * Destory this object.
   */
  destroy: function () {
    this.animation.cancel();
    this.targetEl.remove();
    this.targetEl = null;
    this.animation = null;
    this.win = null;
  },

  /*
   * Set keyframes to shape graph by computed style. This method creates new keyframe
   * object using only offset and easing of given keyframes.
   * Also, allows null value. In case of null, this graph helper shapes graph using
   * computed timing progress.
   * @param {Object} keyframes - Should have offset and easing, or null.
   */
  setKeyframes: function (keyframes) {
    let frames = null;
    // We need to change the duration resolution in case of interval of keyframes offset
    // was narrow.
    let durationResolution = DURATION_RESOLUTION;

    if (keyframes) {
      let previousOffset = 0;

      // Create new keyframes for opacity as computed style.
      // The reason why we use computed value instead of computed timing progress is to
      // include the easing in keyframes as well. Although the computed timing progress
      // is not affected by the easing in keyframes at all, computed value reflects that.
      frames = keyframes.map(keyframe => {
        if (previousOffset && previousOffset != keyframe.offset) {
          const interval = keyframe.offset - previousOffset;
          durationResolution = Math.max(durationResolution, Math.ceil(1 / interval));
        }
        previousOffset = keyframe.offset;

        return {
          opacity: keyframe.offset,
          offset: keyframe.offset,
          easing: keyframe.easing
        };
      });

      // Set the underlying opacity to zero so that if we sample the animation's output
      // during the delay phase and it is not filling backwards, we get zero.
      this.targetEl.style.opacity = 0;
    }

    this.durationResolution = durationResolution;
    this.animation.effect.setKeyframes(frames);
    this.hasFrames = !!frames;
  },

  /*
   * Set animation behavior.
   * In Animation::SetCurrentTime spec, even if current time of animation is over
   * endTime, the progress is changed. Likewise, in case the time is less than 0.
   * If set true, we prevent the time to make the same animation behavior as the original.
   * @param {bool} isOriginalBehavior - true: original behavior
   *                                    false: according to spec.
   */
  setOriginalBehavior: function (isOriginalBehavior) {
    this.isOriginalBehavior = isOriginalBehavior;
  },

  /**
   * Set animation fill mode.
   * @param {String} fill - "both", "forwards", "backwards" or "both"
   */
  setFillMode: function (fill) {
    this.animation.effect.timing.fill = fill;
  },

  /**
   * Set true if need to close path in appendShapePath.
   * @param {bool} isClosePathNeeded - true: close, false: open.
   */
  setClosePathNeeded: function (isClosePathNeeded) {
    this.isClosePathNeeded = isClosePathNeeded;
  },

  /**
   * SummaryGraphHelper searches and creates the summary graph untill the progress
   * distance is less than this minProgressThreshold.
   */
  setMinProgressThreshold: function (minProgressThreshold) {
    this.minProgressThreshold = minProgressThreshold;
  },

  /**
   * Return a segment in graph by given the time.
   * @return {Object} Computed result which has follwing values.
   * - x: x value of graph (float)
   * - y: y value of graph (float between 0 - 1)
   */
  getSegment: function (time) {
    if (this.isOriginalBehavior) {
      // If the given time is less than 0, returned progress is 0.
      if (time < 0) {
        return { x: time, y: 0 };
      }
      // Avoid to apply over endTime.
      this.animation.currentTime = time < this.endTime ? time : this.endTime;
    } else {
      this.animation.currentTime = time;
    }
    const value = this.hasFrames ? this.getOpacityValue() : this.getProgressValue();
    return { x: time, y: value };
  },

  /**
   * Create the path segments from given parameters.
   * @param {Number} startTime - Starting time of animation.
   * @param {Number} endTime - Ending time of animation.
   * @return {Array} path segments -
   *                 [{x: {Number} time, y: {Number} progress}, ...]
   */
  createPathSegments: function (startTime, endTime) {
    return createPathSegments(startTime, endTime, this.minSegmentDuration,
                              this.minProgressThreshold, this, this.durationResolution);
  },

  /**
   * Append path element as shape. Also, this method appends two segment
   * that are {start x, 0} and {end x, 0} to make shape.
   * @param {Element} parentEl - Parent element of this appended path element.
   * @param {Array} pathSegments - Path segments. Please see createPathSegments.
   * @param {String} cls - Class name.
   * @return {Element} path element.
   */
  appendShapePath: function (parentEl, pathSegments, cls) {
    return appendShapePath(parentEl, pathSegments, cls, this.isClosePathNeeded);
  },

  /**
   * Return current computed timing progress of the animation.
   * @return {float} computed timing progress as float value of Y axis.
   */
  getProgressValue: function () {
    return Math.max(this.animation.effect.getComputedTiming().progress, 0);
  },

  /**
   * Return current computed 'opacity' value of the element which is animating.
   * @return {float} computed timing progress as float value of Y axis.
   */
  getOpacityValue: function () {
    return this.win.getComputedStyle(this.targetEl).opacity;
  }
};

exports.SummaryGraphHelper = SummaryGraphHelper;

/**
 * Create the path segments from given parameters.
 * @param {Number} startTime - Starting time of animation.
 * @param {Number} endTime - Ending time of animation.
 * @param {Number} minSegmentDuration - Minimum segment duration.
 * @param {Number} minProgressThreshold - Minimum progress threshold.
 * @param {Object} segmentHelper
 * @param {Number} resolution - Duration resolution for first time.
 *                              If null, use DURATION_RESOLUTION.
 * - getSegment(time): Helper function that, given a time,
 *                     will calculate the animation progress.
 * @return {Array} path segments -
 *                 [{x: {Number} time, y: {Number} progress}, ...]
 */
function createPathSegments(startTime, endTime, minSegmentDuration,
                            minProgressThreshold, segmentHelper,
                            resolution = DURATION_RESOLUTION) {
  // If the duration is too short, early return.
  if (endTime - startTime < minSegmentDuration) {
    return [segmentHelper.getSegment(startTime),
            segmentHelper.getSegment(endTime)];
  }

  // Otherwise, start creating segments.
  let pathSegments = [];

  // Append the segment for the startTime position.
  const startTimeSegment = segmentHelper.getSegment(startTime);
  pathSegments.push(startTimeSegment);
  let previousSegment = startTimeSegment;

  // Split the duration in equal intervals, and iterate over them.
  // See the definition of DURATION_RESOLUTION for more information about this.
  const interval = (endTime - startTime) / resolution;
  for (let index = 1; index <= resolution; index++) {
    // Create a segment for this interval.
    const currentSegment =
      segmentHelper.getSegment(startTime + index * interval);

    // If the distance between the Y coordinate (the animation's progress) of
    // the previous segment and the Y coordinate of the current segment is too
    // large, then recurse with a smaller duration to get more details
    // in the graph.
    if (Math.abs(currentSegment.y - previousSegment.y) > minProgressThreshold) {
      // Divide the current interval (excluding start and end bounds
      // by adding/subtracting BOUND_EXCLUDING_TIME).
      pathSegments = pathSegments.concat(
        createPathSegments(previousSegment.x + BOUND_EXCLUDING_TIME,
                           currentSegment.x - BOUND_EXCLUDING_TIME,
                           minSegmentDuration, minProgressThreshold,
                           segmentHelper));
    }

    pathSegments.push(currentSegment);
    previousSegment = currentSegment;
  }

  return pathSegments;
}

/**
 * Append path element as shape. Also, this method appends two segment
 * that are {start x, 0} and {end x, 0} to make shape.
 * But does not affect given pathSegments.
 * @param {Element} parentEl - Parent element of this appended path element.
 * @param {Array} pathSegments - Path segments. Please see createPathSegments.
 * @param {String} cls - Class name.
 * @param {bool} isClosePathNeeded - Set true if need to close the path. (default true)
 * @return {Element} path element.
 */
function appendShapePath(parentEl, pathSegments, cls, isClosePathNeeded = true) {
  const segments = [
    { x: pathSegments[0].x, y: 0 },
    ...pathSegments,
    { x: pathSegments[pathSegments.length - 1].x, y: 0 }
  ];
  return appendPathElement(parentEl, segments, cls, isClosePathNeeded);
}

/**
 * Append path element.
 * @param {Element} parentEl - Parent element of this appended path element.
 * @param {Array} pathSegments - Path segments. Please see createPathSegments.
 * @param {String} cls - Class name.
 * @param {bool} isClosePathNeeded - Set true if need to close the path.
 * @return {Element} path element.
 */
function appendPathElement(parentEl, pathSegments, cls, isClosePathNeeded) {
  // Create path string.
  let currentSegment = pathSegments[0];
  let path = `M${ currentSegment.x },${ currentSegment.y }`;
  for (let i = 1; i < pathSegments.length; i++) {
    const currentEasing = currentSegment.easing ? currentSegment.easing : "linear";
    const nextSegment = pathSegments[i];
    if (currentEasing === "linear") {
      path += createLinePathString(nextSegment);
    } else if (currentEasing.startsWith("steps")) {
      path += createStepsPathString(currentSegment, nextSegment);
    } else if (currentEasing.startsWith("frames")) {
      path += createFramesPathString(currentSegment, nextSegment);
    } else {
      path += createCubicBezierPathString(currentSegment, nextSegment);
    }
    currentSegment = nextSegment;
  }
  if (isClosePathNeeded) {
    path += " Z";
  }
  // Append and return the path element.
  return createSVGNode({
    parent: parentEl,
    nodeType: "path",
    attributes: {
      "d": path,
      "class": cls,
      "vector-effect": "non-scaling-stroke",
      "transform": "scale(1, -1)"
    }
  });
}

/**
 * Create the path segments from given keyframes.
 * @param {Number} duration - Duration of animation.
 * @param {Object} Keyframes of devtool's format.
 * @return {Array} path segments -
 *                 [{x: {Number} time, y: {Number} distance,
 *                  easing: {String} keyframe's easing,
 *                  style: {String} keyframe's value}, ...]
 */
function createKeyframesPathSegments(duration, keyframes) {
  return keyframes.map(keyframe => {
    return {
      x: keyframe.offset * duration,
      y: keyframe.distance,
      easing: keyframe.easing,
      style: keyframe.value
    };
  });
}

/**
 * Create a line path string.
 * @param {Object} segment - e.g. { x: 100, y: 1 }
 * @return {String} path string - e.g. "L100,1"
 */
function createLinePathString(segment) {
  return ` L${ segment.x },${ segment.y }`;
}

/**
 * Create a path string to represents a step function.
 * @param {Object} currentSegment - e.g. { x: 0, y: 0, easing: "steps(2)" }
 * @param {Object} nextSegment - e.g. { x: 1, y: 1 }
 * @return {String} path string - e.g. "C 0.25 0.1, 0.25 1, 1 1"
 */
function createStepsPathString(currentSegment, nextSegment) {
  const matches =
    currentSegment.easing.match(/^steps\((\d+)(,\sstart)?\)/);
  const stepNumber = parseInt(matches[1], 10);
  const oneStepX = (nextSegment.x - currentSegment.x) / stepNumber;
  const oneStepY = (nextSegment.y - currentSegment.y) / stepNumber;
  const isStepStart = matches[2];
  const stepOffsetY = isStepStart ? 1 : 0;
  let path = "";
  for (let step = 0; step < stepNumber; step++) {
    const sx = currentSegment.x + step * oneStepX;
    const ex = sx + oneStepX;
    const y = currentSegment.y + (step + stepOffsetY) * oneStepY;
    path += ` L${ sx },${ y } L${ ex },${ y }`;
  }
  if (!isStepStart) {
    path += ` L${ nextSegment.x },${ nextSegment.y }`;
  }
  return path;
}

/**
 * Create a path string to represents a frames function.
 * @param {Object} currentSegment - e.g. { x: 0, y: 0, easing: "frames(2)" }
 * @param {Object} nextSegment - e.g. { x: 1, y: 1 }
 * @return {String} path string - e.g. "C 0.25 0.1, 0.25 1, 1 1"
 */
function createFramesPathString(currentSegment, nextSegment) {
  const matches =
    currentSegment.easing.match(/^frames\((\d+)\)/);
  const framesNumber = parseInt(matches[1], 10);
  const oneFrameX = (nextSegment.x - currentSegment.x) / framesNumber;
  const oneFrameY = (nextSegment.y - currentSegment.y) / (framesNumber - 1);
  let path = "";
  for (let frame = 0; frame < framesNumber; frame++) {
    const sx = currentSegment.x + frame * oneFrameX;
    const ex = sx + oneFrameX;
    const y = currentSegment.y + frame * oneFrameY;
    path += ` L${ sx },${ y } L${ ex },${ y }`;
  }
  return path;
}

/**
 * Create a path string to represents a bezier curve.
 * @param {Object} currentSegment - e.g. { x: 0, y: 0, easing: "ease" }
 * @param {Object} nextSegment - e.g. { x: 1, y: 1 }
 * @return {String} path string - e.g. "C 0.25 0.1, 0.25 1, 1 1"
 */
function createCubicBezierPathString(currentSegment, nextSegment) {
  const controlPoints = parseTimingFunction(currentSegment.easing);
  if (!controlPoints) {
    // Just return line path string since we could not parse this easing.
    return createLinePathString(currentSegment);
  }

  const cp1x = controlPoints[0];
  const cp1y = controlPoints[1];
  const cp2x = controlPoints[2];
  const cp2y = controlPoints[3];

  const diffX = nextSegment.x - currentSegment.x;
  const diffY = nextSegment.y - currentSegment.y;
  let path =
    ` C ${ currentSegment.x + diffX * cp1x } ${ currentSegment.y + diffY * cp1y }`;
  path += `, ${ currentSegment.x + diffX * cp2x } ${ currentSegment.y + diffY * cp2y }`;
  path += `, ${ nextSegment.x } ${ nextSegment.y }`;
  return path;
}

/**
 * Parse given RGBA string.
 * @param {String} colorString - e.g. rgb(0, 0, 0) or rgba(0, 0, 0, 0.5) and so on.
 * @return {Object} RGBA {r: r, g: g, b: b, a: a}.
 */
function getRGBA(colorString) {
  const color = new colorUtils.CssColor(colorString);
  return color.getRGBATuple();
}

/**
 * Return the distance from give two RGBA.
 * @param {Object} rgba1 - RGBA (format is same to getRGBA)
 * @param {Object} rgba2 - RGBA (format is same to getRGBA)
 * @return {float} distance.
 */
function getRGBADistance(rgba1, rgba2) {
  const startA = rgba1.a;
  const startR = rgba1.r * startA;
  const startG = rgba1.g * startA;
  const startB = rgba1.b * startA;
  const endA = rgba2.a;
  const endR = rgba2.r * endA;
  const endG = rgba2.g * endA;
  const endB = rgba2.b * endA;
  const diffA = startA - endA;
  const diffR = startR - endR;
  const diffG = startG - endG;
  const diffB = startB - endB;
  return Math.sqrt(diffA * diffA + diffR * diffR + diffG * diffG + diffB * diffB);
}

/**
 * Return preferred progress threshold for given keyframes.
 * See the documentation of DURATION_RESOLUTION and DEFAULT_MIN_PROGRESS_THRESHOLD
 * for more information regarding this.
 * @param {Array} keyframes - keyframes
 * @return {float} - preferred progress threshold.
 */
function getPreferredKeyframesProgressThreshold(keyframes) {
  let minProgressTreshold = DEFAULT_MIN_PROGRESS_THRESHOLD;
  for (let i = 0; i < keyframes.length - 1; i++) {
    const keyframe = keyframes[i];
    if (!keyframe.easing) {
      continue;
    }
    let keyframeProgressThreshold = getPreferredProgressThreshold(keyframe.easing);
    if (keyframeProgressThreshold !== DEFAULT_MIN_PROGRESS_THRESHOLD) {
      // We should consider the keyframe's duration.
      keyframeProgressThreshold *=
        (keyframes[i + 1].offset - keyframe.offset);
    }
    minProgressTreshold = Math.min(keyframeProgressThreshold, minProgressTreshold);
  }
  return minProgressTreshold;
}
exports.getPreferredKeyframesProgressThreshold = getPreferredKeyframesProgressThreshold;

/**
 * Return preferred progress threshold to render summary graph.
 * @param {String} - easing e.g. steps(2), linear and so on.
 * @return {float} - preferred threshold.
 */
function getPreferredProgressThreshold(easing) {
  const stepOrFramesFunction = easing.match(/(steps|frames)\((\d+)/);
  return stepOrFramesFunction
       ? 1 / (parseInt(stepOrFramesFunction[2], 10) + 1)
       : DEFAULT_MIN_PROGRESS_THRESHOLD;
}
exports.getPreferredProgressThreshold = getPreferredProgressThreshold;
PK
!<��v_+_+Cchrome/devtools/modules/devtools/client/animationinspector/utils.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 { LocalizationHelper } = require("devtools/shared/l10n");
const L10N =
      new LocalizationHelper("devtools/client/locales/animationinspector.properties");

// How many times, maximum, can we loop before we find the optimal time
// interval in the timeline graph.
const OPTIMAL_TIME_INTERVAL_MAX_ITERS = 100;
// Time graduations should be multiple of one of these number.
const OPTIMAL_TIME_INTERVAL_MULTIPLES = [1, 2.5, 5];

const MILLIS_TIME_FORMAT_MAX_DURATION = 4000;

// SVG namespace
const SVG_NS = "http://www.w3.org/2000/svg";

/**
 * DOM node creation helper function.
 * @param {Object} Options to customize the node to be created.
 * - nodeType {String} Optional, defaults to "div",
 * - attributes {Object} Optional attributes object like
 *   {attrName1:value1, attrName2: value2, ...}
 * - parent {DOMNode} Mandatory node to append the newly created node to.
 * - textContent {String} Optional text for the node.
 * - namespace {String} Optional namespace
 * @return {DOMNode} The newly created node.
 */
function createNode(options) {
  if (!options.parent) {
    throw new Error("Missing parent DOMNode to create new node");
  }

  let type = options.nodeType || "div";
  let node =
    options.namespace
    ? options.parent.ownerDocument.createElementNS(options.namespace, type)
    : options.parent.ownerDocument.createElement(type);

  for (let name in options.attributes || {}) {
    let value = options.attributes[name];
    node.setAttribute(name, value);
  }

  if (options.textContent) {
    node.textContent = options.textContent;
  }

  options.parent.appendChild(node);
  return node;
}

exports.createNode = createNode;

/**
 * SVG DOM node creation helper function.
 * @param {Object} Options to customize the node to be created.
 * - nodeType {String} Optional, defaults to "div",
 * - attributes {Object} Optional attributes object like
 *   {attrName1:value1, attrName2: value2, ...}
 * - parent {DOMNode} Mandatory node to append the newly created node to.
 * - textContent {String} Optional text for the node.
 * @return {DOMNode} The newly created node.
 */
function createSVGNode(options) {
  options.namespace = SVG_NS;
  return createNode(options);
}
exports.createSVGNode = createSVGNode;

/**
 * Find the optimal interval between time graduations in the animation timeline
 * graph based on a minimum time interval
 * @param {Number} minTimeInterval Minimum time in ms in one interval
 * @return {Number} The optimal interval time in ms
 */
function findOptimalTimeInterval(minTimeInterval) {
  let numIters = 0;
  let multiplier = 1;

  if (!minTimeInterval) {
    return 0;
  }

  let interval;
  while (true) {
    for (let i = 0; i < OPTIMAL_TIME_INTERVAL_MULTIPLES.length; i++) {
      interval = OPTIMAL_TIME_INTERVAL_MULTIPLES[i] * multiplier;
      if (minTimeInterval <= interval) {
        return interval;
      }
    }
    if (++numIters > OPTIMAL_TIME_INTERVAL_MAX_ITERS) {
      return interval;
    }
    multiplier *= 10;
  }
}

exports.findOptimalTimeInterval = findOptimalTimeInterval;

/**
 * Format a timestamp (in ms) as a mm:ss.mmm string.
 * @param {Number} time
 * @return {String}
 */
function formatStopwatchTime(time) {
  // Format falsy values as 0
  if (!time) {
    return "00:00.000";
  }

  let milliseconds = parseInt(time % 1000, 10);
  let seconds = parseInt((time / 1000) % 60, 10);
  let minutes = parseInt((time / (1000 * 60)), 10);

  let pad = (nb, max) => {
    if (nb < max) {
      return new Array((max + "").length - (nb + "").length + 1).join("0") + nb;
    }
    return nb;
  };

  minutes = pad(minutes, 10);
  seconds = pad(seconds, 10);
  milliseconds = pad(milliseconds, 100);

  return `${minutes}:${seconds}.${milliseconds}`;
}

exports.formatStopwatchTime = formatStopwatchTime;

/**
 * The TimeScale helper object is used to know which size should something be
 * displayed with in the animation panel, depending on the animations that are
 * currently displayed.
 * If there are 5 animations displayed, and the first one starts at 10000ms and
 * the last one ends at 20000ms, then this helper can be used to convert any
 * time in this range to a distance in pixels.
 *
 * For the helper to know how to convert, it needs to know all the animations.
 * Whenever a new animation is added to the panel, addAnimation(state) should be
 * called. reset() can be called to start over.
 */
var TimeScale = {
  minStartTime: Infinity,
  maxEndTime: 0,

  /**
   * Add a new animation to time scale.
   * @param {Object} state A PlayerFront.state object.
   */
  addAnimation: function (state) {
    let {previousStartTime, delay, duration, endDelay,
         iterationCount, playbackRate} = state;

    endDelay = typeof endDelay === "undefined" ? 0 : endDelay;
    let toRate = v => v / playbackRate;
    let minZero = v => Math.max(v, 0);
    let rateRelativeDuration =
      toRate(duration * (!iterationCount ? 1 : iterationCount));
    // Negative-delayed animations have their startTimes set such that we would
    // be displaying the delay outside the time window if we didn't take it into
    // account here.
    let relevantDelay = delay < 0 ? toRate(delay) : 0;
    previousStartTime = previousStartTime || 0;

    let startTime = toRate(minZero(delay)) +
                    rateRelativeDuration +
                    endDelay;
    this.minStartTime = Math.min(
      this.minStartTime,
      previousStartTime +
      relevantDelay +
      Math.min(startTime, 0)
    );
    let length = toRate(delay) +
                 rateRelativeDuration +
                 toRate(minZero(endDelay));
    let endTime = previousStartTime + length;
    this.maxEndTime = Math.max(this.maxEndTime, endTime);
  },

  /**
   * Reset the current time scale.
   */
  reset: function () {
    this.minStartTime = Infinity;
    this.maxEndTime = 0;
  },

  /**
   * Convert a startTime to a distance in %, in the current time scale.
   * @param {Number} time
   * @return {Number}
   */
  startTimeToDistance: function (time) {
    time -= this.minStartTime;
    return this.durationToDistance(time);
  },

  /**
   * Convert a duration to a distance in %, in the current time scale.
   * @param {Number} time
   * @return {Number}
   */
  durationToDistance: function (duration) {
    return duration * 100 / this.getDuration();
  },

  /**
   * Convert a distance in % to a time, in the current time scale.
   * @param {Number} distance
   * @return {Number}
   */
  distanceToTime: function (distance) {
    return this.minStartTime + (this.getDuration() * distance / 100);
  },

  /**
   * Convert a distance in % to a time, in the current time scale.
   * The time will be relative to the current minimum start time.
   * @param {Number} distance
   * @return {Number}
   */
  distanceToRelativeTime: function (distance) {
    let time = this.distanceToTime(distance);
    return time - this.minStartTime;
  },

  /**
   * Depending on the time scale, format the given time as milliseconds or
   * seconds.
   * @param {Number} time
   * @return {String} The formatted time string.
   */
  formatTime: function (time) {
    // Format in milliseconds if the total duration is short enough.
    if (this.getDuration() <= MILLIS_TIME_FORMAT_MAX_DURATION) {
      return L10N.getFormatStr("timeline.timeGraduationLabel", time.toFixed(0));
    }

    // Otherwise format in seconds.
    return L10N.getFormatStr("player.timeLabel", (time / 1000).toFixed(1));
  },

  getDuration: function () {
    return this.maxEndTime - this.minStartTime;
  },

  /**
   * Given an animation, get the various dimensions (in %) useful to draw the
   * animation in the timeline.
   */
  getAnimationDimensions: function ({state}) {
    let start = state.previousStartTime || 0;
    let duration = state.duration;
    let rate = state.playbackRate;
    let count = state.iterationCount;
    let delay = state.delay || 0;
    let endDelay = state.endDelay || 0;

    // The start position.
    let x = this.startTimeToDistance(start + (delay / rate));
    // The width for a single iteration.
    let w = this.durationToDistance(duration / rate);
    // The width for all iterations.
    let iterationW = w * (count || 1);
    // The start position of the delay.
    let delayX = delay < 0 ? x : this.startTimeToDistance(start);
    // The width of the delay.
    let delayW = this.durationToDistance(Math.abs(delay) / rate);
    // The width of the delay if it is negative, 0 otherwise.
    let negativeDelayW = delay < 0 ? delayW : 0;
    // The width of the endDelay.
    let endDelayW = this.durationToDistance(Math.abs(endDelay) / rate);
    // The start position of the endDelay.
    let endDelayX = endDelay < 0 ? x + iterationW - endDelayW
                                 : x + iterationW;

    return {x, w, iterationW, delayX, delayW, negativeDelayW,
            endDelayX, endDelayW};
  }
};

exports.TimeScale = TimeScale;

/**
 * Convert given CSS property name to JavaScript CSS name.
 * @param {String} CSS property name (e.g. background-color).
 * @return {String} JavaScript CSS property name (e.g. backgroundColor).
 */
function getJsPropertyName(cssPropertyName) {
  if (cssPropertyName == "float") {
    return "cssFloat";
  }
  // https://drafts.csswg.org/cssom/#css-property-to-idl-attribute
  return cssPropertyName.replace(/-([a-z])/gi, (str, group) => {
    return group.toUpperCase();
  });
}
exports.getJsPropertyName = getJsPropertyName;

/**
 * Turn propertyName into property-name.
 * @param {String} jsPropertyName A camelcased CSS property name. Typically
 * something that comes out of computed styles. E.g. borderBottomColor
 * @return {String} The corresponding CSS property name: border-bottom-color
 */
function getCssPropertyName(jsPropertyName) {
  return jsPropertyName.replace(/[A-Z]/g, "-$&").toLowerCase();
}
exports.getCssPropertyName = getCssPropertyName;

/**
 * Get a formatted title for this animation. This will be either:
 * "some-name", "some-name : CSS Transition", "some-name : CSS Animation",
 * "some-name : Script Animation", or "Script Animation", depending
 * if the server provides the type, what type it is and if the animation
 * has a name
 * @param {AnimationPlayerFront} animation
 */
function getFormattedAnimationTitle({state}) {
  // Older servers don't send a type, and only know about
  // CSSAnimations and CSSTransitions, so it's safe to use
  // just the name.
  if (!state.type) {
    return state.name;
  }

  // Script-generated animations may not have a name.
  if (state.type === "scriptanimation" && !state.name) {
    return L10N.getStr("timeline.scriptanimation.unnamedLabel");
  }

  return L10N.getFormatStr(`timeline.${state.type}.nameLabel`, state.name);
}
exports.getFormattedAnimationTitle = getFormattedAnimationTitle;
PK
!<ңna��?chrome/devtools/modules/devtools/client/canvasdebugger/panel.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 { Cc, Ci, Cu, Cr } = require("chrome");
const promise = require("promise");
const EventEmitter = require("devtools/shared/event-emitter");
const { CanvasFront } = require("devtools/shared/fronts/canvas");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");

function CanvasDebuggerPanel(iframeWindow, toolbox) {
  this.panelWin = iframeWindow;
  this._toolbox = toolbox;
  this._destroyer = null;

  EventEmitter.decorate(this);
}

exports.CanvasDebuggerPanel = CanvasDebuggerPanel;

CanvasDebuggerPanel.prototype = {
  /**
   * Open is effectively an asynchronous constructor.
   *
   * @return object
   *         A promise that is resolved when the Canvas Debugger completes opening.
   */
  open: function () {
    let targetPromise;

    // Local debugging needs to make the target remote.
    if (!this.target.isRemote) {
      targetPromise = this.target.makeRemote();
    } else {
      targetPromise = promise.resolve(this.target);
    }

    return targetPromise
      .then(() => {
        this.panelWin.gToolbox = this._toolbox;
        this.panelWin.gTarget = this.target;
        this.panelWin.gFront = new CanvasFront(this.target.client, this.target.form);
        return this.panelWin.startupCanvasDebugger();
      })
      .then(() => {
        this.isReady = true;
        this.emit("ready");
        return this;
      })
      .catch(function onError(aReason) {
        DevToolsUtils.reportException("CanvasDebuggerPanel.prototype.open", aReason);
      });
  },

  // DevToolPanel API

  get target() {
    return this._toolbox.target;
  },

  destroy: function () {
    // Make sure this panel is not already destroyed.
    if (this._destroyer) {
      return this._destroyer;
    }

    return this._destroyer = this.panelWin.shutdownCanvasDebugger().then(() => {
      // Destroy front to ensure packet handler is removed from client
      this.panelWin.gFront.destroy();
      this.emit("destroyed");
    });
  }
};
PK
!<&��~��Ochrome/devtools/modules/devtools/client/debugger/content/actions/breakpoints.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 constants = require("../constants");
const promise = require("promise");
const { asPaused } = require("../utils");
const { PROMISE } = require("devtools/client/shared/redux/middleware/promise");
const {
  getSource, getBreakpoint, getBreakpoints, makeLocationId
} = require("../queries");
const { Task } = require("devtools/shared/task");

// Because breakpoints are just simple data structures, we still need
// a way to lookup the actual client instance to talk to the server.
// We keep an internal database of clients based off of actor ID.
const BREAKPOINT_CLIENT_STORE = new Map();

function setBreakpointClient(actor, client) {
  BREAKPOINT_CLIENT_STORE.set(actor, client);
}

function getBreakpointClient(actor) {
  return BREAKPOINT_CLIENT_STORE.get(actor);
}

function enableBreakpoint(location) {
  // Enabling is exactly the same as adding. It will use the existing
  // breakpoint that still stored.
  return addBreakpoint(location);
}

function _breakpointExists(state, location) {
  const currentBp = getBreakpoint(state, location);
  return currentBp && !currentBp.disabled;
}

function _getOrCreateBreakpoint(state, location, condition) {
  return getBreakpoint(state, location) || { location, condition };
}

function addBreakpoint(location, condition) {
  return (dispatch, getState) => {
    if (_breakpointExists(getState(), location)) {
      return;
    }

    const bp = _getOrCreateBreakpoint(getState(), location, condition);

    return dispatch({
      type: constants.ADD_BREAKPOINT,
      breakpoint: bp,
      condition: condition,
      [PROMISE]: Task.spawn(function* () {
        const sourceClient = gThreadClient.source(
          getSource(getState(), bp.location.actor)
        );
        const [response, bpClient] = yield sourceClient.setBreakpoint({
          line: bp.location.line,
          column: bp.location.column,
          condition: bp.condition
        });
        const { isPending, actualLocation } = response;

        // Save the client instance
        setBreakpointClient(bpClient.actor, bpClient);
        let lineOrOffset = DebuggerView.editor.isWasm ? bp.location.line :
          (actualLocation ? actualLocation.line : bp.location.line) - 1;
        return {
          text: DebuggerView.editor.getText(lineOrOffset).trim(),
          isWasm: DebuggerView.editor.isWasm,

          // If the breakpoint response has an "actualLocation" attached, then
          // the original requested placement for the breakpoint wasn't
          // accepted.
          actualLocation: isPending ? null : actualLocation,
          actor: bpClient.actor
        };
      })
    });
  };
}

function disableBreakpoint(location) {
  return _removeOrDisableBreakpoint(location, true);
}

function removeBreakpoint(location) {
  return _removeOrDisableBreakpoint(location);
}

function _removeOrDisableBreakpoint(location, isDisabled) {
  return (dispatch, getState) => {
    let bp = getBreakpoint(getState(), location);
    if (!bp) {
      throw new Error("attempt to remove breakpoint that does not exist");
    }
    if (bp.loading) {
      // TODO(jwl): make this wait until the breakpoint is saved if it
      // is still loading
      throw new Error("attempt to remove unsaved breakpoint");
    }

    const bpClient = getBreakpointClient(bp.actor);
    const action = {
      type: constants.REMOVE_BREAKPOINT,
      breakpoint: bp,
      disabled: isDisabled
    };

    // If the breakpoint is already disabled, we don't need to remove
    // it from the server. We just need to dispatch an action
    // simulating a successful server request to remove it, and it
    // will be removed completely from the state.
    if (!bp.disabled) {
      return dispatch(Object.assign({}, action, {
        [PROMISE]: bpClient.remove()
      }));
    } else {
      return dispatch(Object.assign({}, action, { status: "done" }));
    }
  };
}

function removeAllBreakpoints() {
  return (dispatch, getState) => {
    const breakpoints = getBreakpoints(getState());
    const activeBreakpoints = breakpoints.filter(bp => !bp.disabled);
    activeBreakpoints.forEach(bp => removeBreakpoint(bp.location));
  };
}

/**
 * Update the condition of a breakpoint.
 *
 * @param object aLocation
 *        @see DebuggerController.Breakpoints.addBreakpoint
 * @param string aClients
 *        The condition to set on the breakpoint
 * @return object
 *         A promise that will be resolved with the breakpoint client
 */
function setBreakpointCondition(location, condition) {
  return (dispatch, getState) => {
    const bp = getBreakpoint(getState(), location);
    if (!bp) {
      throw new Error("Breakpoint does not exist at the specified location");
    }
    if (bp.loading) {
      // TODO(jwl): when this function is called, make sure the action
      // creator waits for the breakpoint to exist
      throw new Error("breakpoint must be saved");
    }

    const bpClient = getBreakpointClient(bp.actor);
    const action = {
      type: constants.SET_BREAKPOINT_CONDITION,
      breakpoint: bp,
      condition: condition
    };

    // If it's not disabled, we need to update the condition on the
    // server. Otherwise, just dispatch a non-remote action that
    // updates the condition locally.
    if (!bp.disabled) {
      return dispatch(Object.assign({}, action, {
        [PROMISE]: Task.spawn(function* () {
          const newClient = yield bpClient.setCondition(gThreadClient, condition);

          // Remove the old instance and save the new one
          setBreakpointClient(bpClient.actor, null);
          setBreakpointClient(newClient.actor, newClient);

          return { actor: newClient.actor };
        })
      }));
    } else {
      return dispatch(action);
    }
  };
}

module.exports = {
  enableBreakpoint,
  addBreakpoint,
  disableBreakpoint,
  removeBreakpoint,
  removeAllBreakpoints,
  setBreakpointCondition
};
PK
!<6'Ɖ�Schrome/devtools/modules/devtools/client/debugger/content/actions/event-listeners.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 constants = require("../constants");
const { asPaused } = require("../utils");
const { reportException } = require("devtools/shared/DevToolsUtils");
const { setNamedTimeout } = require("devtools/client/shared/widgets/view-helpers");
const { Task } = require("devtools/shared/task");

const FETCH_EVENT_LISTENERS_DELAY = 200; // ms

function fetchEventListeners() {
  return (dispatch, getState) => {
    // Make sure we"re not sending a batch of closely repeated requests.
    // This can easily happen whenever new sources are fetched.
    setNamedTimeout("event-listeners-fetch", FETCH_EVENT_LISTENERS_DELAY, () => {
      // In case there is still a request of listeners going on (it
      // takes several RDP round trips right now), make sure we wait
      // on a currently running request
      if (getState().eventListeners.fetchingListeners) {
        dispatch({
          type: services.WAIT_UNTIL,
          predicate: action => (
            action.type === constants.FETCH_EVENT_LISTENERS &&
            action.status === "done"
          ),
          run: dispatch => dispatch(fetchEventListeners())
        });
        return;
      }

      dispatch({
        type: constants.FETCH_EVENT_LISTENERS,
        status: "begin"
      });

      asPaused(gThreadClient, _getListeners).then(listeners => {
        // Notify that event listeners were fetched and shown in the view,
        // and callback to resume the active thread if necessary.
        window.emit(EVENTS.EVENT_LISTENERS_FETCHED);

        dispatch({
          type: constants.FETCH_EVENT_LISTENERS,
          status: "done",
          listeners: listeners
        });
      });
    });
  };
}

const _getListeners = Task.async(function* () {
  const response = yield gThreadClient.eventListeners();

  // Make sure all the listeners are sorted by the event type, since
  // they"re not guaranteed to be clustered together.
  response.listeners.sort((a, b) => a.type > b.type ? 1 : -1);

  // Add all the listeners in the debugger view event linsteners container.
  let fetchedDefinitions = new Map();
  let listeners = [];
  for (let listener of response.listeners) {
    let definitionSite;
    if (fetchedDefinitions.has(listener.function.actor)) {
      definitionSite = fetchedDefinitions.get(listener.function.actor);
    } else if (listener.function.class == "Function") {
      definitionSite = yield _getDefinitionSite(listener.function);
      if (!definitionSite) {
        // We don"t know where this listener comes from so don"t show it in
        // the UI as breaking on it doesn"t work (bug 942899).
        continue;
      }

      fetchedDefinitions.set(listener.function.actor, definitionSite);
    }
    listener.function.url = definitionSite;
    listeners.push(listener);
  }
  fetchedDefinitions.clear();

  return listeners;
});

const _getDefinitionSite = Task.async(function* (aFunction) {
  const grip = gThreadClient.pauseGrip(aFunction);
  let response;

  try {
    response = yield grip.getDefinitionSite();
  }
  catch (e) {
    // Don't make this error fatal, because it would break the entire events pane.
    reportException("_getDefinitionSite", e);
    return null;
  }

  return response.source.url;
});

function updateEventBreakpoints(eventNames) {
  return dispatch => {
    setNamedTimeout("event-breakpoints-update", 0, () => {
      gThreadClient.pauseOnDOMEvents(eventNames, function () {
        // Notify that event breakpoints were added/removed on the server.
        window.emit(EVENTS.EVENT_BREAKPOINTS_UPDATED);

        dispatch({
          type: constants.UPDATE_EVENT_BREAKPOINTS,
          eventNames: eventNames
        });
      });
    });
  };
}

module.exports = { updateEventBreakpoints, fetchEventListeners };
PK
!<���Y!Y!Kchrome/devtools/modules/devtools/client/debugger/content/actions/sources.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 constants = require("../constants");
const promise = require("promise");
const Services = require("Services");
const { dumpn } = require("devtools/shared/DevToolsUtils");
const { PROMISE, HISTOGRAM_ID } = require("devtools/client/shared/redux/middleware/promise");
const { getSource, getSourceText } = require("../queries");
const { Task } = require("devtools/shared/task");

const NEW_SOURCE_IGNORED_URLS = ["debugger eval code", "XStringBundle"];
const FETCH_SOURCE_RESPONSE_DELAY = 200; // ms

function getSourceClient(source) {
  return gThreadClient.source(source);
}

/**
 * Handler for the debugger client's unsolicited newSource notification.
 */
function newSource(source) {
  return dispatch => {
    // Ignore bogus scripts, e.g. generated from 'clientEvaluate' packets.
    if (NEW_SOURCE_IGNORED_URLS.includes(source.url)) {
      return;
    }

    // Signal that a new source has been added.
    window.emit(EVENTS.NEW_SOURCE);

    return dispatch({
      type: constants.ADD_SOURCE,
      source: source
    });
  };
}

function selectSource(source, opts) {
  return (dispatch, getState) => {
    if (!gThreadClient) {
      // No connection, do nothing. This happens when the debugger is
      // shut down too fast and it tries to display a default source.
      return;
    }

    source = getSource(getState(), source.actor);

    // Make sure to start a request to load the source text.
    dispatch(loadSourceText(source));

    dispatch({
      type: constants.SELECT_SOURCE,
      source: source,
      opts: opts
    });
  };
}

function loadSources() {
  return {
    type: constants.LOAD_SOURCES,
    [PROMISE]: Task.spawn(function* () {
      const response = yield gThreadClient.getSources();

      // Top-level breakpoints may pause the entire loading process
      // because scripts are executed as they are loaded, so the
      // engine may pause in the middle of loading all the sources.
      // This is relatively harmless, as individual `newSource`
      // notifications are fired for each script and they will be
      // added to the UI through that.
      if (!response.sources) {
        dumpn(
          "Error getting sources, probably because a top-level " +
          "breakpoint was hit while executing them"
        );
        return;
      }

      // Ignore bogus scripts, e.g. generated from 'clientEvaluate' packets.
      return response.sources.filter(source => {
        return !NEW_SOURCE_IGNORED_URLS.includes(source.url);
      });
    })
  };
}

/**
 * Set the black boxed status of the given source.
 *
 * @param Object aSource
 *        The source form.
 * @param bool aBlackBoxFlag
 *        True to black box the source, false to un-black box it.
 * @returns Promise
 *          A promize that resolves to [aSource, isBlackBoxed] or rejects to
 *          [aSource, error].
 */
function blackbox(source, shouldBlackBox) {
  const client = getSourceClient(source);

  return {
    type: constants.BLACKBOX,
    source: source,
    [PROMISE]: Task.spawn(function* () {
      yield shouldBlackBox ? client.blackBox() : client.unblackBox();
      return {
        isBlackBoxed: shouldBlackBox
      };
    })
  };
}

/**
 * Toggle the pretty printing of a source's text. All subsequent calls to
 * |getText| will return the pretty-toggled text. Nothing will happen for
 * non-javascript files.
 *
 * @param Object aSource
 *        The source form from the RDP.
 * @returns Promise
 *          A promise that resolves to [aSource, prettyText] or rejects to
 *          [aSource, error].
 */
function togglePrettyPrint(source) {
  return (dispatch, getState) => {
    const sourceClient = getSourceClient(source);
    const wantPretty = !source.isPrettyPrinted;

    return dispatch({
      type: constants.TOGGLE_PRETTY_PRINT,
      source: source,
      [PROMISE]: Task.spawn(function* () {
        let response;

        // Only attempt to pretty print JavaScript sources.
        const sourceText = getSourceText(getState(), source.actor);
        const contentType = sourceText ? sourceText.contentType : null;
        if (!SourceUtils.isJavaScript(source.url, contentType)) {
          throw new Error("Can't prettify non-javascript files.");
        }

        if (wantPretty) {
          response = yield sourceClient.prettyPrint(Prefs.editorTabSize);
        }
        else {
          response = yield sourceClient.disablePrettyPrint();
        }

        // Remove the cached source AST from the Parser, to avoid getting
        // wrong locations when searching for functions.
        DebuggerController.Parser.clearSource(source.url);

        return {
          isPrettyPrinted: wantPretty,
          text: response.source,
          contentType: response.contentType
        };
      })
    });
  };
}

function loadSourceText(source) {
  return (dispatch, getState) => {
    // Fetch the source text only once.
    let textInfo = getSourceText(getState(), source.actor);
    if (textInfo) {
      // It's already loaded or is loading
      return promise.resolve(textInfo);
    }

    const sourceClient = getSourceClient(source);

    return dispatch({
      type: constants.LOAD_SOURCE_TEXT,
      source: source,
      [PROMISE]: Task.spawn(function* () {
        let transportType = gClient.localTransport ? "_LOCAL" : "_REMOTE";
        let histogramId = "DEVTOOLS_DEBUGGER_DISPLAY_SOURCE" + transportType + "_MS";
        let histogram = Services.telemetry.getHistogramById(histogramId);
        let startTime = Date.now();

        const response = yield sourceClient.source();

        histogram.add(Date.now() - startTime);

        // Automatically pretty print if enabled and the test is
        // detected to be "minified"
        if (Prefs.autoPrettyPrint &&
            !source.isPrettyPrinted &&
            SourceUtils.isMinified(source.actor, response.source)) {
          dispatch(togglePrettyPrint(source));
        }

        return { text: response.source,
                 contentType: response.contentType };
      })
    });
  };
}

/**
 * Starts fetching all the sources, silently.
 *
 * @param array aUrls
 *        The urls for the sources to fetch. If fetching a source's text
 *        takes too long, it will be discarded.
 * @return object
 *         A promise that is resolved after source texts have been fetched.
 */
function getTextForSources(actors) {
  return (dispatch, getState) => {
    let deferred = promise.defer();
    let pending = new Set(actors);
    let fetched = [];

    // Can't use promise.all, because if one fetch operation is rejected, then
    // everything is considered rejected, thus no other subsequent source will
    // be getting fetched. We don't want that. Something like Q's allSettled
    // would work like a charm here.

    // Try to fetch as many sources as possible.
    for (let actor of actors) {
      let source = getSource(getState(), actor);
      dispatch(loadSourceText(source)).then(({ text, contentType }) => {
        onFetch([source, text, contentType]);
      }, err => {
        onError(source, err);
      });
    }

    setTimeout(onTimeout, FETCH_SOURCE_RESPONSE_DELAY);

    /* Called if fetching a source takes too long. */
    function onTimeout() {
      pending = new Set();
      maybeFinish();
    }

    /* Called if fetching a source finishes successfully. */
    function onFetch([aSource, aText, aContentType]) {
      // If fetching the source has previously timed out, discard it this time.
      if (!pending.has(aSource.actor)) {
        return;
      }
      pending.delete(aSource.actor);
      fetched.push([aSource.actor, aText, aContentType]);
      maybeFinish();
    }

    /* Called if fetching a source failed because of an error. */
    function onError([aSource, aError]) {
      pending.delete(aSource.actor);
      maybeFinish();
    }

    /* Called every time something interesting happens while fetching sources. */
    function maybeFinish() {
      if (pending.size == 0) {
        // Sort the fetched sources alphabetically by their url.
        deferred.resolve(fetched.sort(([aFirst], [aSecond]) => aFirst > aSecond));
      }
    }

    return deferred.promise;
  };
}

module.exports = {
  newSource,
  selectSource,
  loadSources,
  blackbox,
  togglePrettyPrint,
  loadSourceText,
  getTextForSources
};
PK
!<���6��Echrome/devtools/modules/devtools/client/debugger/content/constants.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

exports.UPDATE_EVENT_BREAKPOINTS = "UPDATE_EVENT_BREAKPOINTS";
exports.FETCH_EVENT_LISTENERS = "FETCH_EVENT_LISTENERS";

exports.TOGGLE_PRETTY_PRINT = "TOGGLE_PRETTY_PRINT";
exports.BLACKBOX = "BLACKBOX";

exports.ADD_BREAKPOINT = "ADD_BREAKPOINT";
exports.REMOVE_BREAKPOINT = "REMOVE_BREAKPOINT";
exports.ENABLE_BREAKPOINT = "ENABLE_BREAKPOINT";
exports.DISABLE_BREAKPOINT = "DISABLE_BREAKPOINT";
exports.SET_BREAKPOINT_CONDITION = "SET_BREAKPOINT_CONDITION";

exports.ADD_SOURCE = "ADD_SOURCE";
exports.LOAD_SOURCES = "LOAD_SOURCES";
exports.LOAD_SOURCE_TEXT = "LOAD_SOURCE_TEXT";
exports.SELECT_SOURCE = "SELECT_SOURCE";
exports.UNLOAD = "UNLOAD";
exports.RELOAD = "RELOAD";
PK
!<}[B--Ichrome/devtools/modules/devtools/client/debugger/content/globalActions.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 constants = require("./constants");

// Fired when the page is being unloaded, for example when it's being
// navigated away from.
function unload() {
  return {
    type: constants.UNLOAD
  };
}

module.exports = { unload };
PK
!<�o��Cchrome/devtools/modules/devtools/client/debugger/content/queries.js
function getSource(state, actor) {
  return state.sources.sources[actor];
}

function getSources(state) {
  return state.sources.sources;
}

function getSourceCount(state) {
  return Object.keys(state.sources.sources).length;
}

function getSourceByURL(state, url) {
  for (let k in state.sources.sources) {
    const source = state.sources.sources[k];
    if (source.url === url) {
      return source;
    }
  }
}

function getSourceByActor(state, actor) {
  for (let k in state.sources.sources) {
    const source = state.sources.sources[k];
    if (source.actor === actor) {
      return source;
    }
  }
}

function getSelectedSource(state) {
  return state.sources.sources[state.sources.selectedSource];
}

function getSelectedSourceOpts(state) {
  return state.sources.selectedSourceOpts;
}

function getSourceText(state, actor) {
  return state.sources.sourcesText[actor];
}

function getBreakpoints(state) {
  return Object.keys(state.breakpoints.breakpoints).map(k => {
    return state.breakpoints.breakpoints[k];
  });
}

function getBreakpoint(state, location) {
  return state.breakpoints.breakpoints[makeLocationId(location)];
}

function makeLocationId(location) {
  return location.actor + ":" + location.line.toString();
}

module.exports = {
  getSource,
  getSources,
  getSourceCount,
  getSourceByURL,
  getSourceByActor,
  getSelectedSource,
  getSelectedSourceOpts,
  getSourceText,
  getBreakpoint,
  getBreakpoints,
  makeLocationId
};
PK
!<]Lr�''Schrome/devtools/modules/devtools/client/debugger/content/reducers/async-requests.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 constants = require("../constants");
const initialState = [];

function update(state = initialState, action, emitChange) {
  const { seqId } = action;

  if (action.type === constants.UNLOAD) {
    return initialState;
  }
  else if (seqId) {
    let newState;
    if (action.status === "start") {
      newState = [...state, seqId];
    }
    else if (action.status === "error" || action.status === "done") {
      newState = state.filter(id => id !== seqId);
    }

    emitChange("open-requests", newState);
    return newState;
  }

  return state;
}

module.exports = update;
PK
!<1�ĴPchrome/devtools/modules/devtools/client/debugger/content/reducers/breakpoints.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 constants = require("../constants");
const Immutable = require("devtools/client/shared/vendor/seamless-immutable");
const { mergeIn, setIn, deleteIn } = require("../utils");
const { makeLocationId } = require("../queries");

const initialState = Immutable({
  breakpoints: {}
});

// Return the first argument that is a string, or null if nothing is a
// string.
function firstString(...args) {
  for (var arg of args) {
    if (typeof arg === "string") {
      return arg;
    }
  }
  return null;
}

function update(state = initialState, action, emitChange) {
  switch (action.type) {
    case constants.ADD_BREAKPOINT: {
      const id = makeLocationId(action.breakpoint.location);

      if (action.status === "start") {
        const existingBp = state.breakpoints[id];
        const bp = existingBp || Immutable(action.breakpoint);

        state = setIn(state, ["breakpoints", id], bp.merge({
          disabled: false,
          loading: true,
        // We want to do an OR here, but we can't because we need
        // empty strings to be truthy, i.e. an empty string is a valid
        // condition.
          condition: firstString(action.condition, bp.condition)
        }));

        emitChange(existingBp ? "breakpoint-enabled" : "breakpoint-added",
                 state.breakpoints[id]);
        return state;
      }
      else if (action.status === "done") {
        const { actor, text, isWasm } = action.value;
        let { actualLocation } = action.value;

      // If the breakpoint moved, update the map
        if (actualLocation) {
        // XXX Bug 1227417: The `setBreakpoint` RDP request rdp
        // request returns an `actualLocation` field that doesn't
        // conform to the regular { actor, line } location shape, but
        // it has a `source` field. We should fix that.
          actualLocation = { actor: actualLocation.source.actor,
                           line: actualLocation.line };

          state = deleteIn(state, ["breakpoints", id]);

          const movedId = makeLocationId(actualLocation);
          const currentBp = state.breakpoints[movedId] || Immutable(action.breakpoint);
          const prevLocation = action.breakpoint.location;
          const newBp = currentBp.merge({ location: actualLocation });
          state = setIn(state, ["breakpoints", movedId], newBp);

          emitChange("breakpoint-moved", {
            breakpoint: newBp,
            prevLocation: prevLocation
          });
        }

        const finalLocation = (
        actualLocation ? actualLocation : action.breakpoint.location
      );
        const finalLocationId = makeLocationId(finalLocation);
        state = mergeIn(state, ["breakpoints", finalLocationId], {
          disabled: false,
          loading: false,
          actor: actor,
          isWasm: isWasm,
          text: text
        });
        emitChange("breakpoint-updated", state.breakpoints[finalLocationId]);
        return state;
      }
    else if (action.status === "error") {
      // Remove the optimistic update
      emitChange("breakpoint-removed", state.breakpoints[id]);
      return deleteIn(state, ["breakpoints", id]);
    }
      break;
    }

    case constants.REMOVE_BREAKPOINT: {
      if (action.status === "done") {
        const id = makeLocationId(action.breakpoint.location);
        const bp = state.breakpoints[id];

        if (action.disabled) {
          state = mergeIn(state, ["breakpoints", id],
                        { loading: false, disabled: true });
          emitChange("breakpoint-disabled", state.breakpoints[id]);
          return state;
        }

        state = deleteIn(state, ["breakpoints", id]);
        emitChange("breakpoint-removed", bp);
        return state;
      }
      break;
    }

    case constants.SET_BREAKPOINT_CONDITION: {
      const id = makeLocationId(action.breakpoint.location);
      const bp = state.breakpoints[id];
      emitChange("breakpoint-condition-updated", bp);

      if (!action.status) {
      // No status means that it wasn't a remote request. Just update
      // the condition locally.
        return mergeIn(state, ["breakpoints", id], {
          condition: action.condition
        });
      }
      else if (action.status === "start") {
        return mergeIn(state, ["breakpoints", id], {
          loading: true,
          condition: action.condition
        });
      }
    else if (action.status === "done") {
      return mergeIn(state, ["breakpoints", id], {
        loading: false,
        condition: action.condition,
        // Setting a condition creates a new breakpoint client as of
        // now, so we need to update the actor
        actor: action.value.actor
      });
    }
    else if (action.status === "error") {
      emitChange("breakpoint-removed", bp);
      return deleteIn(state, ["breakpoints", id]);
    }

      break;
    }}

  return state;
}

module.exports = update;
PK
!<9@T�Tchrome/devtools/modules/devtools/client/debugger/content/reducers/event-listeners.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 constants = require("../constants");

const FETCH_EVENT_LISTENERS_DELAY = 200; // ms

const initialState = {
  activeEventNames: [],
  listeners: [],
  fetchingListeners: false,
};

function update(state = initialState, action, emit) {
  switch (action.type) {
    case constants.UPDATE_EVENT_BREAKPOINTS:
      state.activeEventNames = action.eventNames;
      emit("activeEventNames", state.activeEventNames);
      break;
    case constants.FETCH_EVENT_LISTENERS:
      if (action.status === "begin") {
        state.fetchingListeners = true;
      }
      else if (action.status === "done") {
        state.fetchingListeners = false;
        state.listeners = action.listeners;
        emit("event-listeners", state.listeners);
      }
      break;
  }

  return state;
}

module.exports = update;
PK
!<T����Jchrome/devtools/modules/devtools/client/debugger/content/reducers/index.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 eventListeners = require("./event-listeners");
const sources = require("./sources");
const breakpoints = require("./breakpoints");
const asyncRequests = require("./async-requests");

module.exports = {
  eventListeners,
  sources,
  breakpoints,
  asyncRequests
};
PK
!<�RΙ((Lchrome/devtools/modules/devtools/client/debugger/content/reducers/sources.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 constants = require("../constants");
const Immutable = require("devtools/client/shared/vendor/seamless-immutable");
const { mergeIn, setIn } = require("../utils");

const initialState = Immutable({
  sources: {},
  selectedSource: null,
  selectedSourceOpts: null,
  sourcesText: {}
});

function update(state = initialState, action, emitChange) {
  switch (action.type) {
    case constants.ADD_SOURCE:
      emitChange("source", action.source);
      return mergeIn(state, ["sources", action.source.actor], action.source);

    case constants.LOAD_SOURCES:
      if (action.status === "done") {
        const sources = action.value;
        if (!sources) {
          return state;
        }
        const sourcesByActor = {};
        sources.forEach(source => {
          if (!state.sources[source.actor]) {
            emitChange("source", source);
          }
          sourcesByActor[source.actor] = source;
        });
        return mergeIn(state, ["sources"], state.sources.merge(sourcesByActor));
      }
      break;

    case constants.SELECT_SOURCE:
      emitChange("source-selected", action.source);
      return state.merge({
        selectedSource: action.source.actor,
        selectedSourceOpts: action.opts
      });

    case constants.LOAD_SOURCE_TEXT: {
      const s = _updateText(state, action);
      emitChange("source-text-loaded", s.sources[action.source.actor]);
      return s;
    }

    case constants.BLACKBOX:
      if (action.status === "done") {
        const s = mergeIn(state,
                        ["sources", action.source.actor, "isBlackBoxed"],
                        action.value.isBlackBoxed);
        emitChange("blackboxed", s.sources[action.source.actor]);
        return s;
      }
      break;

    case constants.TOGGLE_PRETTY_PRINT:
      let s = state;
      if (action.status === "error") {
        s = mergeIn(state, ["sourcesText", action.source.actor], {
          loading: false
        });

      // If it errored, just display the source as it was before, but
      // only if there is existing text already. If auto-prettifying
      // is on, the original text may still be coming in and we don't
      // have it yet. If we try to set empty text we confuse the
      // editor because it thinks it's already displaying the source's
      // text and won't load the text when it actually comes in.
        if (s.sourcesText[action.source.actor].text != null) {
          emitChange("prettyprinted", s.sources[action.source.actor]);
        }
      }
      else {
        s = _updateText(state, action);
      // Don't do this yet, the progress bar is still imperatively shown
      // from the source view. We will fix in the next iteration.
      // emitChange('source-text-loaded', s.sources[action.source.actor]);

        if (action.status === "done") {
          s = mergeIn(s,
                    ["sources", action.source.actor, "isPrettyPrinted"],
                    action.value.isPrettyPrinted);
          emitChange("prettyprinted", s.sources[action.source.actor]);
        }
      }
      return s;

    case constants.UNLOAD:
    // Reset the entire state to just the initial state, a blank state
    // if you will.
      return initialState;
  }

  return state;
}

function _updateText(state, action) {
  const { source } = action;

  if (action.status === "start") {
    // Merge this in, don't set it. That way the previous value is
    // still stored here, and we can retrieve it if whatever we're
    // doing fails.
    return mergeIn(state, ["sourcesText", source.actor], {
      loading: true
    });
  }
  else if (action.status === "error") {
    return setIn(state, ["sourcesText", source.actor], {
      error: action.error
    });
  }
  else {
    return setIn(state, ["sourcesText", source.actor], {
      text: action.value.text,
      contentType: action.value.contentType
    });
  }
}

module.exports = update;
PK
!<���BBAchrome/devtools/modules/devtools/client/debugger/content/utils.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 { reportException } = require("devtools/shared/DevToolsUtils");
const { Task } = require("devtools/shared/task");

function asPaused(client, func) {
  if (client.state != "paused") {
    return Task.spawn(function* () {
      yield client.interrupt();
      let result;

      try {
        result = yield func();
      }
      catch (e) {
        // Try to put the debugger back in a working state by resuming
        // it
        yield client.resume();
        throw e;
      }

      yield client.resume();
      return result;
    });
  } else {
    return func();
  }
}

function handleError(err) {
  reportException("promise", err.toString());
}

function onReducerEvents(controller, listeners, thisContext) {
  Object.keys(listeners).forEach(name => {
    const listener = listeners[name];
    controller.onChange(name, payload => {
      listener.call(thisContext, payload);
    });
  });
}

function _getIn(destObj, path) {
  return path.reduce(function (acc, name) {
    return acc[name];
  }, destObj);
}

function mergeIn(destObj, path, value) {
  path = [...path];
  path.reverse();
  var obj = path.reduce(function (acc, name) {
    return { [name]: acc };
  }, value);

  return destObj.merge(obj, { deep: true });
}

function setIn(destObj, path, value) {
  destObj = mergeIn(destObj, path, null);
  return mergeIn(destObj, path, value);
}

function updateIn(destObj, path, fn) {
  return setIn(destObj, path, fn(_getIn(destObj, path)));
}

function deleteIn(destObj, path) {
  const objPath = path.slice(0, -1);
  const propName = path[path.length - 1];
  const obj = _getIn(destObj, objPath);
  return setIn(destObj, objPath, obj.without(propName));
}

module.exports = {
  asPaused,
  handleError,
  onReducerEvents,
  mergeIn,
  setIn,
  updateIn,
  deleteIn
};
PK
!<�i?�~*~*Vchrome/devtools/modules/devtools/client/debugger/content/views/event-listeners-view.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

/* import-globals-from ../../debugger-controller.js */

const actions = require("../actions/event-listeners");
const { bindActionCreators } = require("devtools/client/shared/vendor/redux");
const { extend } = require("devtools/shared/extend");
const { WidgetMethods } = require("devtools/client/shared/widgets/view-helpers");
const { SideMenuWidget } = require("resource://devtools/client/shared/widgets/SideMenuWidget.jsm");

/**
 * Functions handling the event listeners UI.
 */
function EventListenersView(controller) {
  dumpn("EventListenersView was instantiated");

  this.actions = bindActionCreators(actions, controller.dispatch);
  this.getState = () => controller.getState().eventListeners;

  this._onCheck = this._onCheck.bind(this);
  this._onClick = this._onClick.bind(this);

  controller.onChange("event-listeners", this.renderListeners.bind(this));
}

EventListenersView.prototype = extend(WidgetMethods, {
  /**
   * Initialization function, called when the debugger is started.
   */
  initialize: function () {
    dumpn("Initializing the EventListenersView");

    this.widget = new SideMenuWidget(document.getElementById("event-listeners"), {
      showItemCheckboxes: true,
      showGroupCheckboxes: true
    });

    this.emptyText = L10N.getStr("noEventListenersText");
    this._eventCheckboxTooltip = L10N.getStr("eventCheckboxTooltip");
    this._onSelectorString = " " + L10N.getStr("eventOnSelector") + " ";
    this._inSourceString = " " + L10N.getStr("eventInSource") + " ";
    this._inNativeCodeString = L10N.getStr("eventNative");

    this.widget.addEventListener("check", this._onCheck);
    this.widget.addEventListener("click", this._onClick);
  },

  /**
   * Destruction function, called when the debugger is closed.
   */
  destroy: function () {
    dumpn("Destroying the EventListenersView");

    this.widget.removeEventListener("check", this._onCheck);
    this.widget.removeEventListener("click", this._onClick);
  },

  renderListeners: function (listeners) {
    listeners.forEach(listener => {
      this.addListener(listener, { staged: true });
    });

    // Flushes all the prepared events into the event listeners container.
    this.commit();
  },

  /**
   * Adds an event to this event listeners container.
   *
   * @param object aListener
   *        The listener object coming from the active thread.
   * @param object aOptions [optional]
   *        Additional options for adding the source. Supported options:
   *        - staged: true to stage the item to be appended later
   */
  addListener: function (aListener, aOptions = {}) {
    let { node: { selector }, function: { url }, type } = aListener;
    if (!type) return;

    // Some listener objects may be added from plugins, thus getting
    // translated to native code.
    if (!url) {
      url = this._inNativeCodeString;
    }

    // If an event item for this listener's url and type was already added,
    // avoid polluting the view and simply increase the "targets" count.
    let eventItem = this.getItemForPredicate(aItem =>
      aItem.attachment.url == url &&
      aItem.attachment.type == type);

    if (eventItem) {
      let { selectors, view: { targets } } = eventItem.attachment;
      if (!selectors.includes(selector)) {
        selectors.push(selector);
        targets.setAttribute("value", L10N.getFormatStr("eventNodes", selectors.length));
      }
      return;
    }

    // There's no easy way of grouping event types into higher-level groups,
    // so we need to do this by hand.
    let is = (...args) => args.includes(type);
    let has = str => type.includes(str);
    let starts = str => type.startsWith(str);
    let group;

    if (starts("animation")) {
      group = L10N.getStr("animationEvents");
    } else if (starts("audio")) {
      group = L10N.getStr("audioEvents");
    } else if (is("levelchange")) {
      group = L10N.getStr("batteryEvents");
    } else if (is("cut", "copy", "paste")) {
      group = L10N.getStr("clipboardEvents");
    } else if (starts("composition")) {
      group = L10N.getStr("compositionEvents");
    } else if (starts("device")) {
      group = L10N.getStr("deviceEvents");
    } else if (is("fullscreenchange", "fullscreenerror", "orientationchange",
      "overflow", "resize", "scroll", "underflow", "zoom")) {
      group = L10N.getStr("displayEvents");
    } else if (starts("drag") || starts("drop")) {
      group = L10N.getStr("dragAndDropEvents");
    } else if (starts("gamepad")) {
      group = L10N.getStr("gamepadEvents");
    } else if (is("canplay", "canplaythrough", "durationchange", "emptied",
      "ended", "loadeddata", "loadedmetadata", "pause", "play", "playing",
      "ratechange", "seeked", "seeking", "stalled", "suspend", "timeupdate",
      "volumechange", "waiting")) {
      group = L10N.getStr("mediaEvents");
    } else if (is("blocked", "complete", "success", "upgradeneeded", "versionchange")) {
      group = L10N.getStr("indexedDBEvents");
    } else if (is("blur", "change", "focus", "focusin", "focusout", "invalid",
      "reset", "select", "submit")) {
      group = L10N.getStr("interactionEvents");
    } else if (starts("key") || is("input")) {
      group = L10N.getStr("keyboardEvents");
    } else if (starts("mouse") || has("click") || is("contextmenu", "show", "wheel")) {
      group = L10N.getStr("mouseEvents");
    } else if (starts("DOM")) {
      group = L10N.getStr("mutationEvents");
    } else if (is("abort", "error", "hashchange", "load", "loadend", "loadstart",
      "pagehide", "pageshow", "progress", "timeout", "unload", "uploadprogress",
      "visibilitychange")) {
      group = L10N.getStr("navigationEvents");
    } else if (is("pointerlockchange", "pointerlockerror")) {
      group = L10N.getStr("pointerLockEvents");
    } else if (is("compassneedscalibration", "userproximity")) {
      group = L10N.getStr("sensorEvents");
    } else if (starts("storage")) {
      group = L10N.getStr("storageEvents");
    } else if (is("beginEvent", "endEvent", "repeatEvent")) {
      group = L10N.getStr("timeEvents");
    } else if (starts("touch")) {
      group = L10N.getStr("touchEvents");
    } else {
      group = L10N.getStr("otherEvents");
    }

    // Create the element node for the event listener item.
    const itemView = this._createItemView(type, selector, url);

    // Event breakpoints survive target navigations. Make sure the newly
    // inserted event item is correctly checked.
    const activeEventNames = this.getState().activeEventNames;
    const checkboxState = activeEventNames.includes(type);

    // Append an event listener item to this container.
    this.push([itemView.container], {
      staged: aOptions.staged, /* stage the item to be appended later? */
      attachment: {
        url: url,
        type: type,
        view: itemView,
        selectors: [selector],
        group: group,
        checkboxState: checkboxState,
        checkboxTooltip: this._eventCheckboxTooltip
      }
    });
  },

  /**
   * Gets all the event types known to this container.
   *
   * @return array
   *         List of event types, for example ["load", "click"...]
   */
  getAllEvents: function () {
    return this.attachments.map(e => e.type);
  },

  /**
   * Gets the checked event types in this container.
   *
   * @return array
   *         List of event types, for example ["load", "click"...]
   */
  getCheckedEvents: function () {
    return this.attachments.filter(e => e.checkboxState).map(e => e.type);
  },

  /**
   * Customization function for creating an item's UI.
   *
   * @param string aType
   *        The event type, for example "click".
   * @param string aSelector
   *        The target element's selector.
   * @param string url
   *        The source url in which the event listener is located.
   * @return object
   *         An object containing the event listener view nodes.
   */
  _createItemView: function (aType, aSelector, aUrl) {
    let container = document.createElement("hbox");
    container.className = "dbg-event-listener";

    let eventType = document.createElement("label");
    eventType.className = "plain dbg-event-listener-type";
    eventType.setAttribute("value", aType);
    container.appendChild(eventType);

    let typeSeparator = document.createElement("label");
    typeSeparator.className = "plain dbg-event-listener-separator";
    typeSeparator.setAttribute("value", this._onSelectorString);
    container.appendChild(typeSeparator);

    let eventTargets = document.createElement("label");
    eventTargets.className = "plain dbg-event-listener-targets";
    eventTargets.setAttribute("value", aSelector);
    container.appendChild(eventTargets);

    let selectorSeparator = document.createElement("label");
    selectorSeparator.className = "plain dbg-event-listener-separator";
    selectorSeparator.setAttribute("value", this._inSourceString);
    container.appendChild(selectorSeparator);

    let eventLocation = document.createElement("label");
    eventLocation.className = "plain dbg-event-listener-location";
    eventLocation.setAttribute("value", SourceUtils.getSourceLabel(aUrl));
    eventLocation.setAttribute("flex", "1");
    eventLocation.setAttribute("crop", "center");
    container.appendChild(eventLocation);

    return {
      container: container,
      type: eventType,
      targets: eventTargets,
      location: eventLocation
    };
  },

  /**
   * The check listener for the event listeners container.
   */
  _onCheck: function ({ detail: { description, checked }, target }) {
    if (description == "item") {
      this.getItemForElement(target).attachment.checkboxState = checked;

      this.actions.updateEventBreakpoints(this.getCheckedEvents());
      return;
    }

    // Check all the event items in this group.
    this.items
      .filter(e => e.attachment.group == description)
      .forEach(e => this.callMethod("checkItem", e.target, checked));
  },

  /**
   * The select listener for the event listeners container.
   */
  _onClick: function ({ target }) {
    // Changing the checkbox state is handled by the _onCheck event. Avoid
    // handling that again in this click event, so pass in "noSiblings"
    // when retrieving the target's item, to ignore the checkbox.
    let eventItem = this.getItemForElement(target, { noSiblings: true });
    if (eventItem) {
      let newState = eventItem.attachment.checkboxState ^= 1;
      this.callMethod("checkItem", eventItem.target, newState);
    }
  },

  _eventCheckboxTooltip: "",
  _onSelectorString: "",
  _inSourceString: "",
  _inNativeCodeString: ""
});

module.exports = EventListenersView;
PK
!<^^OγγNchrome/devtools/modules/devtools/client/debugger/content/views/sources-view.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

/* import-globals-from ../../debugger-controller.js */

const utils = require("../utils");
const {
  getSelectedSource,
  getSourceByURL,
  getBreakpoint,
  getBreakpoints,
  makeLocationId
} = require("../queries");
const actions = Object.assign(
  {},
  require("../actions/sources"),
  require("../actions/breakpoints")
);
const { bindActionCreators } = require("devtools/client/shared/vendor/redux");
const { extend } = require("devtools/shared/extend");
const {
  WidgetMethods,
  setNamedTimeout
} = require("devtools/client/shared/widgets/view-helpers");
const { Task } = require("devtools/shared/task");
const { SideMenuWidget } = require("resource://devtools/client/shared/widgets/SideMenuWidget.jsm");
const { gDevTools } = require("devtools/client/framework/devtools");
const {KeyCodes} = require("devtools/client/shared/keycodes");

const NEW_SOURCE_DISPLAY_DELAY = 200; // ms
const FUNCTION_SEARCH_POPUP_POSITION = "topcenter bottomleft";
const BREAKPOINT_LINE_TOOLTIP_MAX_LENGTH = 1000; // chars
const BREAKPOINT_CONDITIONAL_POPUP_POSITION = "before_start";
const BREAKPOINT_CONDITIONAL_POPUP_OFFSET_X = 7; // px
const BREAKPOINT_CONDITIONAL_POPUP_OFFSET_Y = -3; // px

/**
 * Functions handling the sources UI.
 */
function SourcesView(controller, DebuggerView) {
  dumpn("SourcesView was instantiated");

  utils.onReducerEvents(controller, {
    "source": this.renderSource,
    "blackboxed": this.renderBlackBoxed,
    "prettyprinted": this.updateToolbarButtonsState,
    "source-selected": this.renderSourceSelected,
    "breakpoint-updated": bp => this.renderBreakpoint(bp),
    "breakpoint-enabled": bp => this.renderBreakpoint(bp),
    "breakpoint-disabled": bp => this.renderBreakpoint(bp),
    "breakpoint-removed": bp => this.renderBreakpoint(bp, true),
  }, this);

  this.getState = controller.getState;
  this.actions = bindActionCreators(actions, controller.dispatch);
  this.DebuggerView = DebuggerView;
  this.Parser = DebuggerController.Parser;

  this.togglePrettyPrint = this.togglePrettyPrint.bind(this);
  this.toggleBlackBoxing = this.toggleBlackBoxing.bind(this);
  this.toggleBreakpoints = this.toggleBreakpoints.bind(this);

  this._onEditorCursorActivity = this._onEditorCursorActivity.bind(this);
  this._onMouseDown = this._onMouseDown.bind(this);
  this._onSourceSelect = this._onSourceSelect.bind(this);
  this._onStopBlackBoxing = this._onStopBlackBoxing.bind(this);
  this._onBreakpointRemoved = this._onBreakpointRemoved.bind(this);
  this._onBreakpointClick = this._onBreakpointClick.bind(this);
  this._onBreakpointCheckboxClick = this._onBreakpointCheckboxClick.bind(this);
  this._onConditionalPopupShowing = this._onConditionalPopupShowing.bind(this);
  this._onConditionalPopupShown = this._onConditionalPopupShown.bind(this);
  this._onConditionalPopupHiding = this._onConditionalPopupHiding.bind(this);
  this._onConditionalTextboxKeyPress = this._onConditionalTextboxKeyPress.bind(this);
  this._onEditorContextMenuOpen = this._onEditorContextMenuOpen.bind(this);
  this._onCopyUrlCommand = this._onCopyUrlCommand.bind(this);
  this._onNewTabCommand = this._onNewTabCommand.bind(this);
  this._onConditionalPopupHidden = this._onConditionalPopupHidden.bind(this);
}

SourcesView.prototype = extend(WidgetMethods, {
  /**
   * Initialization function, called when the debugger is started.
   */
  initialize: function (isWorker) {
    dumpn("Initializing the SourcesView");

    this.widget = new SideMenuWidget(document.getElementById("sources"), {
      contextMenu: document.getElementById("debuggerSourcesContextMenu"),
      showArrows: true
    });

    this._preferredSourceURL = null;
    this._unnamedSourceIndex = 0;
    this.emptyText = L10N.getStr("noSourcesText");
    this._blackBoxCheckboxTooltip = L10N.getStr("blackboxCheckboxTooltip2");

    this._commandset = document.getElementById("debuggerCommands");
    this._popupset = document.getElementById("debuggerPopupset");
    this._cmPopup = document.getElementById("sourceEditorContextMenu");
    this._cbPanel = document.getElementById("conditional-breakpoint-panel");
    this._cbTextbox = document.getElementById("conditional-breakpoint-panel-textbox");
    this._blackBoxButton = document.getElementById("black-box");
    this._stopBlackBoxButton = document.getElementById("black-boxed-message-button");
    this._prettyPrintButton = document.getElementById("pretty-print");
    this._toggleBreakpointsButton = document.getElementById("toggle-breakpoints");
    this._newTabMenuItem = document.getElementById("debugger-sources-context-newtab");
    this._copyUrlMenuItem = document.getElementById("debugger-sources-context-copyurl");

    this._noResultsFoundToolTip = new Tooltip(document);
    this._noResultsFoundToolTip.defaultPosition = FUNCTION_SEARCH_POPUP_POSITION;

    // We don't show the pretty print button if debugger a worker
    // because it simply doesn't work yet. (bug 1273730)
    if (Prefs.prettyPrintEnabled && !isWorker) {
      this._prettyPrintButton.removeAttribute("hidden");
    }

    this._editorContainer = document.getElementById("editor");
    this._editorContainer.addEventListener("mousedown", this._onMouseDown);

    this.widget.addEventListener("select", this._onSourceSelect);

    this._stopBlackBoxButton.addEventListener("click", this._onStopBlackBoxing);
    this._cbPanel.addEventListener("popupshowing", this._onConditionalPopupShowing);
    this._cbPanel.addEventListener("popupshown", this._onConditionalPopupShown);
    this._cbPanel.addEventListener("popuphiding", this._onConditionalPopupHiding);
    this._cbPanel.addEventListener("popuphidden", this._onConditionalPopupHidden);
    this._cbTextbox.addEventListener("keypress", this._onConditionalTextboxKeyPress);
    this._copyUrlMenuItem.addEventListener("command", this._onCopyUrlCommand);
    this._newTabMenuItem.addEventListener("command", this._onNewTabCommand);

    this._cbPanel.hidden = true;
    this.allowFocusOnRightClick = true;
    this.autoFocusOnSelection = false;
    this.autoFocusOnFirstItem = false;

    // Sort the contents by the displayed label.
    this.sortContents((aFirst, aSecond) => {
      return +(aFirst.attachment.label.toLowerCase() >
               aSecond.attachment.label.toLowerCase());
    });

    // Sort known source groups towards the end of the list
    this.widget.groupSortPredicate = function (a, b) {
      if ((a in KNOWN_SOURCE_GROUPS) == (b in KNOWN_SOURCE_GROUPS)) {
        return a.localeCompare(b);
      }
      return (a in KNOWN_SOURCE_GROUPS) ? 1 : -1;
    };

    this.DebuggerView.editor.on("popupOpen", this._onEditorContextMenuOpen);

    this._addCommands();
  },

  /**
   * Destruction function, called when the debugger is closed.
   */
  destroy: function () {
    dumpn("Destroying the SourcesView");

    this.widget.removeEventListener("select", this._onSourceSelect);
    this._stopBlackBoxButton.removeEventListener("click", this._onStopBlackBoxing);
    this._cbPanel.removeEventListener("popupshowing", this._onConditionalPopupShowing);
    this._cbPanel.removeEventListener("popupshown", this._onConditionalPopupShown);
    this._cbPanel.removeEventListener("popuphiding", this._onConditionalPopupHiding);
    this._cbPanel.removeEventListener("popuphidden", this._onConditionalPopupHidden);
    this._cbTextbox.removeEventListener("keypress", this._onConditionalTextboxKeyPress);
    this._copyUrlMenuItem.removeEventListener("command", this._onCopyUrlCommand);
    this._newTabMenuItem.removeEventListener("command", this._onNewTabCommand);
    this.DebuggerView.editor.off("popupOpen", this._onEditorContextMenuOpen, false);
  },

  empty: function () {
    WidgetMethods.empty.call(this);
    this._unnamedSourceIndex = 0;
    this._selectedBreakpoint = null;
  },

  /**
   * Add commands that XUL can fire.
   */
  _addCommands: function () {
    XULUtils.addCommands(this._commandset, {
      addBreakpointCommand: e => this._onCmdAddBreakpoint(e),
      addConditionalBreakpointCommand: e => this._onCmdAddConditionalBreakpoint(e),
      blackBoxCommand: () => this.toggleBlackBoxing(),
      unBlackBoxButton: () => this._onStopBlackBoxing(),
      prettyPrintCommand: () => this.togglePrettyPrint(),
      toggleBreakpointsCommand: () =>this.toggleBreakpoints(),
      nextSourceCommand: () => this.selectNextItem(),
      prevSourceCommand: () => this.selectPrevItem()
    });
  },

  /**
   * Sets the preferred location to be selected in this sources container.
   * @param string aUrl
   */
  set preferredSource(aUrl) {
    this._preferredValue = aUrl;

    // Selects the element with the specified value in this sources container,
    // if already inserted.
    if (this.containsValue(aUrl)) {
      this.selectedValue = aUrl;
    }
  },

  sourcesDidUpdate: function () {
    if (!getSelectedSource(this.getState())) {
      let url = this._preferredSourceURL;
      let source = url && getSourceByURL(this.getState(), url);
      if (source) {
        this.actions.selectSource(source);
      }
      else {
        setNamedTimeout("new-source", NEW_SOURCE_DISPLAY_DELAY, () => {
          if (!getSelectedSource(this.getState()) && this.itemCount > 0) {
            this.actions.selectSource(this.getItemAtIndex(0).attachment.source);
          }
        });
      }
    }
  },

  renderSource: function (source) {
    this.addSource(source, { staged: false });
    for (let bp of getBreakpoints(this.getState())) {
      if (bp.location.actor === source.actor) {
        this.renderBreakpoint(bp);
      }
    }
    this.sourcesDidUpdate();
  },

  /**
   * Adds a source to this sources container.
   *
   * @param object aSource
   *        The source object coming from the active thread.
   * @param object aOptions [optional]
   *        Additional options for adding the source. Supported options:
   *        - staged: true to stage the item to be appended later
   */
  addSource: function (aSource, aOptions = {}) {
    if (!aSource.url && !aOptions.force) {
      // We don't show any unnamed eval scripts yet (see bug 1124106)
      return;
    }

    let { label, group, unicodeUrl } = this._parseUrl(aSource);

    let contents = document.createElement("label");
    contents.className = "plain dbg-source-item";
    contents.setAttribute("value", label);
    contents.setAttribute("crop", "start");
    contents.setAttribute("flex", "1");
    contents.setAttribute("tooltiptext", unicodeUrl);

    if (aSource.introductionType === "wasm") {
      const wasm = document.createElement("box");
      wasm.className = "dbg-wasm-item";
      const icon = document.createElement("box");
      icon.setAttribute("tooltiptext", L10N.getStr("experimental"));
      icon.className = "icon";
      wasm.appendChild(icon);
      wasm.appendChild(contents);

      contents = wasm;
    }

    // If the source is blackboxed, apply the appropriate style.
    if (gThreadClient.source(aSource).isBlackBoxed) {
      contents.classList.add("black-boxed");
    }

    // Append a source item to this container.
    this.push([contents, aSource.actor], {
      staged: aOptions.staged, /* stage the item to be appended later? */
      attachment: {
        label: label,
        group: group,
        checkboxState: !aSource.isBlackBoxed,
        checkboxTooltip: this._blackBoxCheckboxTooltip,
        source: aSource
      }
    });
  },

  _parseUrl: function (aSource) {
    let fullUrl = aSource.url;
    let url, unicodeUrl, label, group;

    if (!fullUrl) {
      unicodeUrl = "SCRIPT" + this._unnamedSourceIndex++;
      label = unicodeUrl;
      group = L10N.getStr("anonymousSourcesLabel");
    }
    else {
      let url = fullUrl.split(" -> ").pop();
      label = aSource.addonPath ? aSource.addonPath : SourceUtils.getSourceLabel(url);
      group = aSource.addonID ? aSource.addonID : SourceUtils.getSourceGroup(url);
      unicodeUrl = NetworkHelper.convertToUnicode(unescape(fullUrl));
    }

    return {
      label: label,
      group: group,
      unicodeUrl: unicodeUrl
    };
  },

  renderBreakpoint: function (breakpoint, removed) {
    if (removed) {
      // Be defensive about the breakpoint not existing.
      if (this._getBreakpoint(breakpoint)) {
        this._removeBreakpoint(breakpoint);
      }
    }
    else {
      if (this._getBreakpoint(breakpoint)) {
        this._updateBreakpointStatus(breakpoint);
      }
      else {
        this._addBreakpoint(breakpoint);
      }
    }
  },

  /**
   * Adds a breakpoint to this sources container.
   *
   * @param object aBreakpointClient
   *               See Breakpoints.prototype._showBreakpoint
   * @param object aOptions [optional]
   *        @see DebuggerController.Breakpoints.addBreakpoint
   */
  _addBreakpoint: function (breakpoint, options = {}) {
    let disabled = breakpoint.disabled;
    let location = breakpoint.location;

    // Get the source item to which the breakpoint should be attached.
    let sourceItem = this.getItemByValue(location.actor);
    if (!sourceItem) {
      return;
    }

    // Create the element node and menu popup for the breakpoint item.
    let breakpointArgs = extend(breakpoint.asMutable(), options);
    let breakpointView = this._createBreakpointView.call(this, breakpointArgs);
    let contextMenu = this._createContextMenu.call(this, breakpointArgs);

    // Append a breakpoint child item to the corresponding source item.
    sourceItem.append(breakpointView.container, {
      attachment: extend(breakpointArgs, {
        actor: location.actor,
        line: location.line,
        view: breakpointView,
        popup: contextMenu
      }),
      attributes: [
        ["contextmenu", contextMenu.menupopupId]
      ],
      // Make sure that when the breakpoint item is removed, the corresponding
      // menupopup and commandset are also destroyed.
      finalize: this._onBreakpointRemoved
    });

    if (typeof breakpoint.condition === "string") {
      this.highlightBreakpoint(breakpoint.location, {
        openPopup: true,
        noEditorUpdate: true
      });
    }

    window.emit(EVENTS.BREAKPOINT_SHOWN_IN_PANE);
  },

  /**
   * Removes a breakpoint from this sources container.
   * It does not also remove the breakpoint from the controller. Be careful.
   *
   * @param object aLocation
   *        @see DebuggerController.Breakpoints.addBreakpoint
   */
  _removeBreakpoint: function (breakpoint) {
    // When a parent source item is removed, all the child breakpoint items are
    // also automagically removed.
    let sourceItem = this.getItemByValue(breakpoint.location.actor);
    if (!sourceItem) {
      return;
    }

    // Clear the breakpoint view.
    sourceItem.remove(this._getBreakpoint(breakpoint));

    if (this._selectedBreakpoint &&
       (queries.makeLocationId(this._selectedBreakpoint.location) ===
        queries.makeLocationId(breakpoint.location))) {
      this._selectedBreakpoint = null;
    }

    window.emit(EVENTS.BREAKPOINT_HIDDEN_IN_PANE);
  },

  _getBreakpoint: function (bp) {
    return this.getItemForPredicate(item => {
      return item.attachment.actor === bp.location.actor &&
        item.attachment.line === bp.location.line;
    });
  },

  /**
   * Updates a breakpoint.
   *
   * @param object breakpoint
   */
  _updateBreakpointStatus: function (breakpoint) {
    let location = breakpoint.location;
    let breakpointItem = this._getBreakpoint(getBreakpoint(this.getState(), location));
    if (!breakpointItem) {
      return promise.reject(new Error("No breakpoint found."));
    }

    // Breakpoint will now be enabled.
    let attachment = breakpointItem.attachment;

    // Update the corresponding menu items to reflect the enabled state.
    let prefix = "bp-cMenu-"; // "breakpoints context menu"
    let identifier = makeLocationId(location);
    let enableSelfId = prefix + "enableSelf-" + identifier + "-menuitem";
    let disableSelfId = prefix + "disableSelf-" + identifier + "-menuitem";
    let enableSelf = document.getElementById(enableSelfId);
    let disableSelf = document.getElementById(disableSelfId);

    if (breakpoint.disabled) {
      enableSelf.removeAttribute("hidden");
      disableSelf.setAttribute("hidden", true);
      attachment.view.checkbox.removeAttribute("checked");
    }
    else {
      enableSelf.setAttribute("hidden", true);
      disableSelf.removeAttribute("hidden");
      attachment.view.checkbox.setAttribute("checked", "true");

      // Update the breakpoint toggle button checked state.
      this._toggleBreakpointsButton.removeAttribute("checked");
    }

  },

  /**
   * Highlights a breakpoint in this sources container.
   *
   * @param object aLocation
   *        @see DebuggerController.Breakpoints.addBreakpoint
   * @param object aOptions [optional]
   *        An object containing some of the following boolean properties:
   *          - openPopup: tells if the expression popup should be shown.
   *          - noEditorUpdate: tells if you want to skip editor updates.
   */
  highlightBreakpoint: function (aLocation, aOptions = {}) {
    let breakpoint = getBreakpoint(this.getState(), aLocation);
    if (!breakpoint) {
      return;
    }

    // Breakpoint will now be selected.
    this._selectBreakpoint(breakpoint);

    // Update the editor location if necessary.
    if (!aOptions.noEditorUpdate) {
      this.DebuggerView.setEditorLocation(aLocation.actor, aLocation.line, { noDebug: true });
    }

    // If the breakpoint requires a new conditional expression, display
    // the panel to input the corresponding expression.
    if (aOptions.openPopup) {
      return this._openConditionalPopup();
    } else {
      return this._hideConditionalPopup();
    }
  },

  /**
   * Highlight the breakpoint on the current currently focused line/column
   * if it exists.
   */
  highlightBreakpointAtCursor: function () {
    let actor = this.selectedValue;
    let line = this.DebuggerView.editor.getCursor().line + 1;

    let location = { actor: actor, line: line };
    this.highlightBreakpoint(location, { noEditorUpdate: true });
  },

  /**
   * Unhighlights the current breakpoint in this sources container.
   */
  unhighlightBreakpoint: function () {
    this._hideConditionalPopup();
    this._unselectBreakpoint();
  },

   /**
    * Display the message thrown on breakpoint condition
    */
  showBreakpointConditionThrownMessage: function (aLocation, aMessage = "") {
    let breakpointItem = this._getBreakpoint(getBreakpoint(this.getState(), aLocation));
    if (!breakpointItem) {
      return;
    }
    let attachment = breakpointItem.attachment;
    attachment.view.container.classList.add("dbg-breakpoint-condition-thrown");
    attachment.view.message.setAttribute("value", aMessage);
  },

  /**
   * Update the checked/unchecked and enabled/disabled states of the buttons in
   * the sources toolbar based on the currently selected source's state.
   */
  updateToolbarButtonsState: function (source) {
    if (source.isBlackBoxed) {
      this._blackBoxButton.setAttribute("checked", true);
      this._prettyPrintButton.setAttribute("checked", true);
    } else {
      this._blackBoxButton.removeAttribute("checked");
      this._prettyPrintButton.removeAttribute("checked");
    }

    if (source.isPrettyPrinted) {
      this._prettyPrintButton.setAttribute("checked", true);
    } else {
      this._prettyPrintButton.removeAttribute("checked");
    }
  },

  /**
   * Toggle the pretty printing of the selected source.
   */
  togglePrettyPrint: function () {
    if (this._prettyPrintButton.hasAttribute("disabled")) {
      return;
    }

    this.DebuggerView.showProgressBar();
    const source = getSelectedSource(this.getState());
    const sourceClient = gThreadClient.source(source);
    const shouldPrettyPrint = !source.isPrettyPrinted;

    // This is only here to give immediate feedback,
    // `renderPrettyPrinted` will set the final status of the buttons
    if (shouldPrettyPrint) {
      this._prettyPrintButton.setAttribute("checked", true);
    } else {
      this._prettyPrintButton.removeAttribute("checked");
    }

    this.actions.togglePrettyPrint(source);
  },

  /**
   * Toggle the black boxed state of the selected source.
   */
  toggleBlackBoxing: Task.async(function* () {
    const source = getSelectedSource(this.getState());
    const shouldBlackBox = !source.isBlackBoxed;

    // Be optimistic that the (un-)black boxing will succeed, so
    // enable/disable the pretty print button and check/uncheck the
    // black box button immediately.
    if (shouldBlackBox) {
      this._prettyPrintButton.setAttribute("disabled", true);
      this._blackBoxButton.setAttribute("checked", true);
    } else {
      this._prettyPrintButton.removeAttribute("disabled");
      this._blackBoxButton.removeAttribute("checked");
    }

    this.actions.blackbox(source, shouldBlackBox);
  }),

  renderBlackBoxed: function (source) {
    const sourceItem = this.getItemByValue(source.actor);
    sourceItem.prebuiltNode.classList.toggle(
      "black-boxed",
      source.isBlackBoxed
    );

    if (getSelectedSource(this.getState()).actor === source.actor) {
      this.updateToolbarButtonsState(source);
    }
  },

  /**
   * Toggles all breakpoints enabled/disabled.
   */
  toggleBreakpoints: function () {
    let breakpoints = getBreakpoints(this.getState());
    let hasBreakpoints = breakpoints.length > 0;
    let hasEnabledBreakpoints = breakpoints.some(bp => !bp.disabled);

    if (hasBreakpoints && hasEnabledBreakpoints) {
      this._toggleBreakpointsButton.setAttribute("checked", true);
      this._onDisableAll();
    } else {
      this._toggleBreakpointsButton.removeAttribute("checked");
      this._onEnableAll();
    }
  },

  hidePrettyPrinting: function () {
    this._prettyPrintButton.style.display = "none";

    if (this._blackBoxButton.style.display === "none") {
      let sep = document.querySelector("#sources-toolbar .devtools-separator");
      sep.style.display = "none";
    }
  },

  hideBlackBoxing: function () {
    this._blackBoxButton.style.display = "none";

    if (this._prettyPrintButton.style.display === "none") {
      let sep = document.querySelector("#sources-toolbar .devtools-separator");
      sep.style.display = "none";
    }
  },

  getDisplayURL: function (source) {
    if (!source.url) {
      return this.getItemByValue(source.actor).attachment.label;
    }
    return NetworkHelper.convertToUnicode(unescape(source.url));
  },

  /**
   * Marks a breakpoint as selected in this sources container.
   *
   * @param object aItem
   *        The breakpoint item to select.
   */
  _selectBreakpoint: function (bp) {
    if (this._selectedBreakpoint === bp) {
      return;
    }
    this._unselectBreakpoint();
    this._selectedBreakpoint = bp;

    const item = this._getBreakpoint(bp);
    item.target.classList.add("selected");

    // Ensure the currently selected breakpoint is visible.
    this.widget.ensureElementIsVisible(item.target);
  },

  /**
   * Marks the current breakpoint as unselected in this sources container.
   */
  _unselectBreakpoint: function () {
    if (!this._selectedBreakpoint) {
      return;
    }

    const item = this._getBreakpoint(this._selectedBreakpoint);
    item.target.classList.remove("selected");

    this._selectedBreakpoint = null;
  },

  /**
   * Opens a conditional breakpoint's expression input popup.
   */
  _openConditionalPopup: function () {
    let breakpointItem = this._getBreakpoint(this._selectedBreakpoint);
    let attachment = breakpointItem.attachment;
    // Check if this is an enabled conditional breakpoint, and if so,
    // retrieve the current conditional epression.
    let bp = getBreakpoint(this.getState(), attachment);
    let expr = (bp ? (bp.condition || "") : "");
    let cbPanel = this._cbPanel;

    // Update the conditional expression textbox. If no expression was
    // previously set, revert to using an empty string by default.
    this._cbTextbox.value = expr;

    function openPopup() {
      // Show the conditional expression panel. The popup arrow should be pointing
      // at the line number node in the breakpoint item view.
      cbPanel.hidden = false;
      cbPanel.openPopup(breakpointItem.attachment.view.lineNumber,
                              BREAKPOINT_CONDITIONAL_POPUP_POSITION,
                              BREAKPOINT_CONDITIONAL_POPUP_OFFSET_X,
                              BREAKPOINT_CONDITIONAL_POPUP_OFFSET_Y);

      cbPanel.removeEventListener("popuphidden", openPopup);
    }

    // Wait until the other cb panel is closed
    if (!this._cbPanel.hidden) {
      this._cbPanel.addEventListener("popuphidden", openPopup);
    } else {
      openPopup();
    }
  },

  /**
   * Hides a conditional breakpoint's expression input popup.
   */
  _hideConditionalPopup: function () {
    // Sometimes this._cbPanel doesn't have hidePopup method which doesn't
    // break anything but simply outputs an exception to the console.
    if (this._cbPanel.hidePopup) {
      this._cbPanel.hidePopup();
    }
  },

  /**
   * Customization function for creating a breakpoint item's UI.
   *
   * @param object aOptions
   *        A couple of options or flags supported by this operation:
   *          - location: the breakpoint's source location and line number
   *          - disabled: the breakpoint's disabled state, boolean
   *          - text: the breakpoint's line text to be displayed
   *          - message: thrown string when the breakpoint condition throws
   * @return object
   *         An object containing the breakpoint container, checkbox,
   *         line number and line text nodes.
   */
  _createBreakpointView: function (aOptions) {
    let { location, disabled, text, message, isWasm } = aOptions;
    let identifier = makeLocationId(location);

    let checkbox = document.createElement("checkbox");
    if (!disabled) {
      checkbox.setAttribute("checked", true);
    }
    checkbox.className = "dbg-breakpoint-checkbox";

    let lineNumberNode = document.createElement("label");
    lineNumberNode.className = "plain dbg-breakpoint-line";
    let lineNumberStr = !isWasm ? location.line.toString() :
      location.line.toString(16).toUpperCase();
    lineNumberNode.setAttribute("value", lineNumberStr);

    let lineTextNode = document.createElement("label");
    lineTextNode.className = "plain dbg-breakpoint-text";
    lineTextNode.setAttribute("value", text);
    lineTextNode.setAttribute("crop", "end");
    lineTextNode.setAttribute("flex", "1");

    let tooltip = text ? text.substr(0, BREAKPOINT_LINE_TOOLTIP_MAX_LENGTH) : "";
    lineTextNode.setAttribute("tooltiptext", tooltip);

    let thrownNode = document.createElement("label");
    thrownNode.className = "plain dbg-breakpoint-condition-thrown-message dbg-breakpoint-text";
    thrownNode.setAttribute("value", message);
    thrownNode.setAttribute("crop", "end");
    thrownNode.setAttribute("flex", "1");

    let bpLineContainer = document.createElement("hbox");
    bpLineContainer.className = "plain dbg-breakpoint-line-container";
    bpLineContainer.setAttribute("flex", "1");

    bpLineContainer.appendChild(lineNumberNode);
    bpLineContainer.appendChild(lineTextNode);

    let bpDetailContainer = document.createElement("vbox");
    bpDetailContainer.className = "plain dbg-breakpoint-detail-container";
    bpDetailContainer.setAttribute("flex", "1");

    bpDetailContainer.appendChild(bpLineContainer);
    bpDetailContainer.appendChild(thrownNode);

    let container = document.createElement("hbox");
    container.id = "breakpoint-" + identifier;
    container.className = "dbg-breakpoint side-menu-widget-item-other";
    container.classList.add("devtools-monospace");
    container.setAttribute("align", "center");
    container.setAttribute("flex", "1");

    container.addEventListener("click", this._onBreakpointClick);
    checkbox.addEventListener("click", this._onBreakpointCheckboxClick);

    container.appendChild(checkbox);
    container.appendChild(bpDetailContainer);

    return {
      container: container,
      checkbox: checkbox,
      lineNumber: lineNumberNode,
      lineText: lineTextNode,
      message: thrownNode
    };
  },

  /**
   * Creates a context menu for a breakpoint element.
   *
   * @param object aOptions
   *        A couple of options or flags supported by this operation:
   *          - location: the breakpoint's source location and line number
   *          - disabled: the breakpoint's disabled state, boolean
   * @return object
   *         An object containing the breakpoint commandset and menu popup ids.
   */
  _createContextMenu: function (aOptions) {
    let { location, disabled } = aOptions;
    let identifier = makeLocationId(location);

    let commandset = document.createElement("commandset");
    let menupopup = document.createElement("menupopup");
    commandset.id = "bp-cSet-" + identifier;
    menupopup.id = "bp-mPop-" + identifier;

    createMenuItem.call(this, "enableSelf", !disabled);
    createMenuItem.call(this, "disableSelf", disabled);
    createMenuItem.call(this, "deleteSelf");
    createMenuSeparator();
    createMenuItem.call(this, "setConditional");
    createMenuSeparator();
    createMenuItem.call(this, "enableOthers");
    createMenuItem.call(this, "disableOthers");
    createMenuItem.call(this, "deleteOthers");
    createMenuSeparator();
    createMenuItem.call(this, "enableAll");
    createMenuItem.call(this, "disableAll");
    createMenuSeparator();
    createMenuItem.call(this, "deleteAll");

    this._popupset.appendChild(menupopup);
    this._commandset.appendChild(commandset);

    return {
      commandsetId: commandset.id,
      menupopupId: menupopup.id
    };

    /**
     * Creates a menu item specified by a name with the appropriate attributes
     * (label and handler).
     *
     * @param string aName
     *        A global identifier for the menu item.
     * @param boolean aHiddenFlag
     *        True if this menuitem should be hidden.
     */
    function createMenuItem(aName, aHiddenFlag) {
      let menuitem = document.createElement("menuitem");
      let command = document.createElement("command");

      let prefix = "bp-cMenu-"; // "breakpoints context menu"
      let commandId = prefix + aName + "-" + identifier + "-command";
      let menuitemId = prefix + aName + "-" + identifier + "-menuitem";

      let label = L10N.getStr("breakpointMenuItem." + aName);
      let func = "_on" + aName.charAt(0).toUpperCase() + aName.slice(1);

      command.id = commandId;
      command.setAttribute("label", label);
      command.addEventListener("command", () => this[func](location));

      menuitem.id = menuitemId;
      menuitem.setAttribute("command", commandId);
      aHiddenFlag && menuitem.setAttribute("hidden", "true");

      commandset.appendChild(command);
      menupopup.appendChild(menuitem);
    }

    /**
     * Creates a simple menu separator element and appends it to the current
     * menupopup hierarchy.
     */
    function createMenuSeparator() {
      let menuseparator = document.createElement("menuseparator");
      menupopup.appendChild(menuseparator);
    }
  },

  /**
   * Copy the source url from the currently selected item.
   */
  _onCopyUrlCommand: function () {
    let selected = this.selectedItem && this.selectedItem.attachment;
    if (!selected) {
      return;
    }
    clipboardHelper.copyString(selected.source.url);
  },

  /**
   * Opens selected item source in a new tab.
   */
  _onNewTabCommand: function () {
    let win = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
    let selected = this.selectedItem.attachment;
    win.openUILinkIn(selected.source.url, "tab", { relatedToCurrent: true });
  },

  /**
   * Function called each time a breakpoint item is removed.
   *
   * @param object aItem
   *        The corresponding item.
   */
  _onBreakpointRemoved: function (aItem) {
    dumpn("Finalizing breakpoint item: " + aItem.stringify());

    // Destroy the context menu for the breakpoint.
    let contextMenu = aItem.attachment.popup;
    document.getElementById(contextMenu.commandsetId).remove();
    document.getElementById(contextMenu.menupopupId).remove();
  },

  _onMouseDown: function (e) {
    this.hideNoResultsTooltip();

    if (!e.metaKey) {
      return;
    }

    let editor = this.DebuggerView.editor;
    let identifier = this._findIdentifier(e.clientX, e.clientY);

    if (!identifier) {
      return;
    }

    let foundDefinitions = this._getFunctionDefinitions(identifier);

    if (!foundDefinitions || !foundDefinitions.definitions) {
      return;
    }

    this._showFunctionDefinitionResults(identifier, foundDefinitions.definitions, editor);
  },

  /**
   * Searches for function definition of a function in a given source file
   */

  _findDefinition: function (parsedSource, aName) {
    let functionDefinitions = parsedSource.getNamedFunctionDefinitions(aName);

    let resultList = [];

    if (!functionDefinitions || !functionDefinitions.length || !functionDefinitions[0].length) {
      return {
        definitions: resultList
      };
    }

    // functionDefinitions is a list with an object full of metadata,
    // extract the data and use to construct a more useful, less
    // cluttered, contextual list
    for (let i = 0; i < functionDefinitions.length; i++) {
      let functionDefinition = {
        source: functionDefinitions[i].sourceUrl,
        startLine: functionDefinitions[i][0].functionLocation.start.line,
        startColumn: functionDefinitions[i][0].functionLocation.start.column,
        name: functionDefinitions[i][0].functionName
      };

      resultList.push(functionDefinition);
    }

    return {
      definitions: resultList
    };
  },

  /**
   * Searches for an identifier underneath the specified position in the
   * source editor.
   *
   * @param number x, y
   *        The left/top coordinates where to look for an identifier.
   */
  _findIdentifier: function (x, y) {
    let parsedSource = SourceUtils.parseSource(this.DebuggerView, this.Parser);
    let identifierInfo = SourceUtils.findIdentifier(this.DebuggerView.editor, parsedSource, x, y);

    // Not hovering over an identifier
    if (!identifierInfo) {
      return;
    }

    return identifierInfo;
  },

  /**
   * The selection listener for the source editor.
   */
  _onEditorCursorActivity: function (e) {
    let editor = this.DebuggerView.editor;
    let start = editor.getCursor("start").line + 1;
    let end = editor.getCursor().line + 1;
    let source = getSelectedSource(this.getState());

    if (source) {
      let location = { actor: source.actor, line: start };
      if (getBreakpoint(this.getState(), location) && start == end) {
        this.highlightBreakpoint(location, { noEditorUpdate: true });
      } else {
        this.unhighlightBreakpoint();
      }
    }
  },

  /*
   * Uses function definition data to perform actions in different
   * cases of how many locations were found: zero, one, or multiple definitions
   */
  _showFunctionDefinitionResults: function (aHoveredFunction, aDefinitionList, aEditor) {
    let definitions = aDefinitionList;
    let hoveredFunction = aHoveredFunction;

    // show a popup saying no results were found
    if (definitions.length == 0) {
      this._noResultsFoundToolTip.setTextContent({
        messages: [L10N.getStr("noMatchingStringsText")]
      });

      this._markedIdentifier = aEditor.markText(
        { line: hoveredFunction.location.start.line - 1, ch: hoveredFunction.location.start.column },
        { line: hoveredFunction.location.end.line - 1, ch: hoveredFunction.location.end.column });

      this._noResultsFoundToolTip.show(this._markedIdentifier.anchor);

    } else if (definitions.length == 1) {
      this.DebuggerView.setEditorLocation(definitions[0].source, definitions[0].startLine);
    } else {
      // TODO: multiple definitions found, do something else
      this.DebuggerView.setEditorLocation(definitions[0].source, definitions[0].startLine);
    }
  },

  /**
   * Hides the tooltip and clear marked text popup.
   */
  hideNoResultsTooltip: function () {
    this._noResultsFoundToolTip.hide();
    if (this._markedIdentifier) {
      this._markedIdentifier.clear();
      this._markedIdentifier = null;
    }
  },

  /*
   * Gets the definition locations from function metadata
   */
  _getFunctionDefinitions: function (aIdentifierInfo) {
    let parsedSource = SourceUtils.parseSource(this.DebuggerView, this.Parser);
    let definition_info = this._findDefinition(parsedSource, aIdentifierInfo.name);

    // Did not find any definitions for the identifier
    if (!definition_info) {
      return;
    }

    return definition_info;
  },

  /**
   * The select listener for the sources container.
   */
  _onSourceSelect: function ({ detail: sourceItem }) {
    if (!sourceItem) {
      return;
    }

    const { source } = sourceItem.attachment;
    this.actions.selectSource(source);
  },

  renderSourceSelected: function (source) {
    if (source.url) {
      this._preferredSourceURL = source.url;
    }
    this.updateToolbarButtonsState(source);
    this._selectItem(this.getItemByValue(source.actor));
  },

  /**
   * The click listener for the "stop black boxing" button.
   */
  _onStopBlackBoxing: Task.async(function* () {
    this.actions.blackbox(getSelectedSource(this.getState()), false);
  }),

  /**
   * The source editor's contextmenu handler.
   * - Toggles "Add Conditional Breakpoint" and "Edit Conditional Breakpoint" items
   */
  _onEditorContextMenuOpen: function (message, ev, popup) {
    let actor = this.selectedValue;
    let line = this.DebuggerView.editor.getCursor().line + 1;
    let location = { actor, line };

    let breakpoint = getBreakpoint(this.getState(), location);
    let addConditionalBreakpointMenuItem = popup.querySelector("#se-dbg-cMenu-addConditionalBreakpoint");
    let editConditionalBreakpointMenuItem = popup.querySelector("#se-dbg-cMenu-editConditionalBreakpoint");

    if (breakpoint && !!breakpoint.condition) {
      editConditionalBreakpointMenuItem.removeAttribute("hidden");
      addConditionalBreakpointMenuItem.setAttribute("hidden", true);
    }
    else {
      addConditionalBreakpointMenuItem.removeAttribute("hidden");
      editConditionalBreakpointMenuItem.setAttribute("hidden", true);
    }
  },

  /**
   * The click listener for a breakpoint container.
   */
  _onBreakpointClick: function (e) {
    let sourceItem = this.getItemForElement(e.target);
    let breakpointItem = this.getItemForElement.call(sourceItem, e.target);
    let attachment = breakpointItem.attachment;
    let bp = getBreakpoint(this.getState(), attachment);
    if (bp) {
      this.highlightBreakpoint(bp.location, {
        openPopup: bp.condition && e.button == 0
      });
    } else {
      this.highlightBreakpoint(bp.location);
    }
  },

  /**
   * The click listener for a breakpoint checkbox.
   */
  _onBreakpointCheckboxClick: function (e) {
    let sourceItem = this.getItemForElement(e.target);
    let breakpointItem = this.getItemForElement.call(sourceItem, e.target);
    let bp = getBreakpoint(this.getState(), breakpointItem.attachment);

    if (bp.disabled) {
      this.actions.enableBreakpoint(bp.location);
    }
    else {
      this.actions.disableBreakpoint(bp.location);
    }

    // Don't update the editor location (avoid propagating into _onBreakpointClick).
    e.preventDefault();
    e.stopPropagation();
  },

  /**
   * The popup showing listener for the breakpoints conditional expression panel.
   */
  _onConditionalPopupShowing: function () {
    this._conditionalPopupVisible = true; // Used in tests.
  },

  /**
   * The popup shown listener for the breakpoints conditional expression panel.
   */
  _onConditionalPopupShown: function () {
    this._cbTextbox.focus();
    this._cbTextbox.select();
    window.emit(EVENTS.CONDITIONAL_BREAKPOINT_POPUP_SHOWN);
  },

  /**
   * The popup hiding listener for the breakpoints conditional expression panel.
   */
  _onConditionalPopupHiding: function () {
    this._conditionalPopupVisible = false; // Used in tests.

    // Check if this is an enabled conditional breakpoint, and if so,
    // save the current conditional expression.
    let bp = this._selectedBreakpoint;
    if (bp) {
      let condition = this._cbTextbox.value;
      this.actions.setBreakpointCondition(bp.location, condition);
    }
  },

  /**
   * The popup hidden listener for the breakpoints conditional expression panel.
   */
  _onConditionalPopupHidden: function () {
    this._cbPanel.hidden = true;
    window.emit(EVENTS.CONDITIONAL_BREAKPOINT_POPUP_HIDDEN);
  },

  /**
   * The keypress listener for the breakpoints conditional expression textbox.
   */
  _onConditionalTextboxKeyPress: function (e) {
    if (e.keyCode == KeyCodes.DOM_VK_RETURN) {
      this._hideConditionalPopup();
    }
  },

  /**
   * Called when the add breakpoint key sequence was pressed.
   */
  _onCmdAddBreakpoint: function (e) {
    let actor = this.selectedValue;
    let line = (this.DebuggerView.clickedLine ?
                this.DebuggerView.clickedLine + 1 :
                this.DebuggerView.editor.getCursor().line + 1);
    let location = { actor, line };
    let bp = getBreakpoint(this.getState(), location);

    // If a breakpoint already existed, remove it now.
    if (bp) {
      this.actions.removeBreakpoint(bp.location);
    }
    // No breakpoint existed at the required location, add one now.
    else {
      this.actions.addBreakpoint(location);
    }
  },

  /**
   * Called when the add conditional breakpoint key sequence was pressed.
   */
  _onCmdAddConditionalBreakpoint: function (e) {
    let actor = this.selectedValue;
    let line = (this.DebuggerView.clickedLine ?
                this.DebuggerView.clickedLine + 1 :
                this.DebuggerView.editor.getCursor().line + 1);

    let location = { actor, line };
    let bp = getBreakpoint(this.getState(), location);

    // If a breakpoint already existed or wasn't a conditional, morph it now.
    if (bp) {
      this.highlightBreakpoint(bp.location, { openPopup: true });
    }
    // No breakpoint existed at the required location, add one now.
    else {
      this.actions.addBreakpoint(location, "");
    }
  },

  getOtherBreakpoints: function (location) {
    const bps = getBreakpoints(this.getState());
    if (location) {
      return bps.filter(bp => {
        return (bp.location.actor !== location.actor ||
                bp.location.line !== location.line);
      });
    }
    return bps;
  },

  /**
   * Function invoked on the "setConditional" menuitem command.
   *
   * @param object aLocation
   *        @see DebuggerController.Breakpoints.addBreakpoint
   */
  _onSetConditional: function (aLocation) {
    // Highlight the breakpoint and show a conditional expression popup.
    this.highlightBreakpoint(aLocation, { openPopup: true });
  },

  /**
   * Function invoked on the "enableSelf" menuitem command.
   *
   * @param object aLocation
   *        @see DebuggerController.Breakpoints.addBreakpoint
   */
  _onEnableSelf: function (aLocation) {
    // Enable the breakpoint, in this container and the controller store.
    this.actions.enableBreakpoint(aLocation);
  },

  /**
   * Function invoked on the "disableSelf" menuitem command.
   *
   * @param object aLocation
   *        @see DebuggerController.Breakpoints.addBreakpoint
   */
  _onDisableSelf: function (aLocation) {
    const bp = getBreakpoint(this.getState(), aLocation);
    if (!bp.disabled) {
      this.actions.disableBreakpoint(aLocation);
    }
  },

  /**
   * Function invoked on the "deleteSelf" menuitem command.
   *
   * @param object aLocation
   *        @see DebuggerController.Breakpoints.addBreakpoint
   */
  _onDeleteSelf: function (aLocation) {
    this.actions.removeBreakpoint(aLocation);
  },

  /**
   * Function invoked on the "enableOthers" menuitem command.
   *
   * @param object aLocation
   *        @see DebuggerController.Breakpoints.addBreakpoint
   */
  _onEnableOthers: function (aLocation) {
    let other = this.getOtherBreakpoints(aLocation);
    // TODO(jwl): batch these and interrupt the thread for all of them
    other.forEach(bp => this._onEnableSelf(bp.location));
  },

  /**
   * Function invoked on the "disableOthers" menuitem command.
   *
   * @param object aLocation
   *        @see DebuggerController.Breakpoints.addBreakpoint
   */
  _onDisableOthers: function (aLocation) {
    let other = this.getOtherBreakpoints(aLocation);
    other.forEach(bp => this._onDisableSelf(bp.location));
  },

  /**
   * Function invoked on the "deleteOthers" menuitem command.
   *
   * @param object aLocation
   *        @see DebuggerController.Breakpoints.addBreakpoint
   */
  _onDeleteOthers: function (aLocation) {
    let other = this.getOtherBreakpoints(aLocation);
    other.forEach(bp => this._onDeleteSelf(bp.location));
  },

  /**
   * Function invoked on the "enableAll" menuitem command.
   */
  _onEnableAll: function () {
    this._onEnableOthers(undefined);
  },

  /**
   * Function invoked on the "disableAll" menuitem command.
   */
  _onDisableAll: function () {
    this._onDisableOthers(undefined);
  },

  /**
   * Function invoked on the "deleteAll" menuitem command.
   */
  _onDeleteAll: function () {
    this._onDeleteOthers(undefined);
  },

  _commandset: null,
  _popupset: null,
  _cmPopup: null,
  _cbPanel: null,
  _cbTextbox: null,
  _selectedBreakpointItem: null,
  _conditionalPopupVisible: false,
  _noResultsFoundToolTip: null,
  _markedIdentifier: null,
  _selectedBreakpoint: null,
  _conditionalPopupVisible: false
});

module.exports = SourcesView;
PK
!<�����<�<Echrome/devtools/modules/devtools/client/debugger/debugger-commands.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 { Cc, Ci, Cu } = require("chrome");
const l10n = require("gcli/l10n");
loader.lazyRequireGetter(this, "gDevTools",
                         "devtools/client/framework/devtools", true);

/**
 * The commands and converters that are exported to GCLI
 */
exports.items = [];

/**
 * Utility to get access to the current breakpoint list.
 *
 * @param DebuggerPanel dbg
 *        The debugger panel.
 * @return array
 *         An array of objects, one for each breakpoint, where each breakpoint
 *         object has the following properties:
 *           - url: the URL of the source file.
 *           - label: a unique string identifier designed to be user visible.
 *           - lineNumber: the line number of the breakpoint in the source file.
 *           - lineText: the text of the line at the breakpoint.
 *           - truncatedLineText: lineText truncated to MAX_LINE_TEXT_LENGTH.
 */
function getAllBreakpoints(dbg) {
  let breakpoints = [];
  let sources = dbg._view.Sources;
  let { trimUrlLength: trim } = dbg.panelWin.SourceUtils;

  for (let source of sources) {
    for (let { attachment: breakpoint } of source) {
      breakpoints.push({
        url: source.attachment.source.url,
        label: source.attachment.label + ":" + breakpoint.line,
        lineNumber: breakpoint.line,
        lineText: breakpoint.text,
        truncatedLineText: trim(breakpoint.text, MAX_LINE_TEXT_LENGTH, "end")
      });
    }
  }

  return breakpoints;
}

function getAllSources(dbg) {
  if (!dbg) {
    return [];
  }

  let items = dbg._view.Sources.items;
  return items
    .filter(item => !!item.attachment.source.url)
    .map(item => ({
      name: item.attachment.source.url,
      value: item.attachment.source.actor
    }));
}

/**
 * 'break' command
 */
exports.items.push({
  name: "break",
  description: l10n.lookup("breakDesc"),
  manual: l10n.lookup("breakManual")
});

/**
 * 'break list' command
 */
exports.items.push({
  name: "break list",
  item: "command",
  runAt: "client",
  description: l10n.lookup("breaklistDesc"),
  returnType: "breakpoints",
  exec: function (args, context) {
    let dbg = getPanel(context, "jsdebugger", { ensureOpened: true });
    return dbg.then(getAllBreakpoints);
  }
});

exports.items.push({
  item: "converter",
  from: "breakpoints",
  to: "view",
  exec: function (breakpoints, context) {
    let dbg = getPanel(context, "jsdebugger");
    if (dbg && breakpoints.length) {
      return context.createView({
        html: breakListHtml,
        data: {
          breakpoints: breakpoints,
          onclick: context.update,
          ondblclick: context.updateExec
        }
      });
    } else {
      return context.createView({
        html: "<p>${message}</p>",
        data: { message: l10n.lookup("breaklistNone") }
      });
    }
  }
});

var breakListHtml = "" +
      "<table>" +
      " <thead>" +
      "  <th>Source</th>" +
      "  <th>Line</th>" +
      "  <th>Actions</th>" +
      " </thead>" +
      " <tbody>" +
      "  <tr foreach='breakpoint in ${breakpoints}'>" +
      "    <td class='gcli-breakpoint-label'>${breakpoint.label}</td>" +
      "    <td class='gcli-breakpoint-lineText'>" +
      "      ${breakpoint.truncatedLineText}" +
      "    </td>" +
      "    <td>" +
      "      <span class='gcli-out-shortcut'" +
      "            data-command='break del ${breakpoint.label}'" +
      "            onclick='${onclick}'" +
      "            ondblclick='${ondblclick}'>" +
      "        " + l10n.lookup("breaklistOutRemove") + "</span>" +
      "    </td>" +
      "  </tr>" +
      " </tbody>" +
      "</table>" +
      "";

var MAX_LINE_TEXT_LENGTH = 30;
var MAX_LABEL_LENGTH = 20;

/**
 * 'break add' command
 */
exports.items.push({
  name: "break add",
  description: l10n.lookup("breakaddDesc"),
  manual: l10n.lookup("breakaddManual")
});

/**
 * 'break add line' command
 */
exports.items.push({
  item: "command",
  runAt: "client",
  name: "break add line",
  description: l10n.lookup("breakaddlineDesc"),
  params: [
    {
      name: "file",
      type: {
        name: "selection",
        lookup: function (context) {
          return getAllSources(getPanel(context, "jsdebugger"));
        }
      },
      description: l10n.lookup("breakaddlineFileDesc")
    },
    {
      name: "line",
      type: { name: "number", min: 1, step: 10 },
      description: l10n.lookup("breakaddlineLineDesc")
    }
  ],
  returnType: "string",
  exec: function (args, context) {
    let dbg = getPanel(context, "jsdebugger");
    if (!dbg) {
      return l10n.lookup("debuggerStopped");
    }

    let deferred = context.defer();
    let item = dbg._view.Sources.getItemForAttachment(a => {
      return a.source && a.source.actor === args.file;
    });
    let position = { actor: item.value, line: args.line };

    dbg.addBreakpoint(position).then(() => {
      deferred.resolve(l10n.lookup("breakaddAdded"));
    }, aError => {
      deferred.resolve(l10n.lookupFormat("breakaddFailed", [aError]));
    });

    return deferred.promise;
  }
});

/**
 * 'break del' command
 */
exports.items.push({
  item: "command",
  runAt: "client",
  name: "break del",
  description: l10n.lookup("breakdelDesc"),
  params: [
    {
      name: "breakpoint",
      type: {
        name: "selection",
        lookup: function (context) {
          let dbg = getPanel(context, "jsdebugger");
          if (!dbg) {
            return [];
          }
          return getAllBreakpoints(dbg).map(breakpoint => ({
            name: breakpoint.label,
            value: breakpoint,
            description: breakpoint.truncatedLineText
          }));
        }
      },
      description: l10n.lookup("breakdelBreakidDesc")
    }
  ],
  returnType: "string",
  exec: function (args, context) {
    let dbg = getPanel(context, "jsdebugger");
    if (!dbg) {
      return l10n.lookup("debuggerStopped");
    }

    let source = dbg._view.Sources.getItemForAttachment(a => {
      return a.source && a.source.url === args.breakpoint.url;
    });

    let deferred = context.defer();
    let position = { actor: source.attachment.source.actor,
                     line: args.breakpoint.lineNumber };

    dbg.removeBreakpoint(position).then(() => {
      deferred.resolve(l10n.lookup("breakdelRemoved"));
    }, () => {
      deferred.resolve(l10n.lookup("breakNotFound"));
    });

    return deferred.promise;
  }
});

/**
 * 'dbg' command
 */
exports.items.push({
  name: "dbg",
  description: l10n.lookup("dbgDesc"),
  manual: l10n.lookup("dbgManual")
});

/**
 * 'dbg open' command
 */
exports.items.push({
  item: "command",
  runAt: "client",
  name: "dbg open",
  description: l10n.lookup("dbgOpen"),
  params: [],
  exec: function (args, context) {
    let target = context.environment.target;
    return gDevTools.showToolbox(target, "jsdebugger").then(() => null);
  }
});

/**
 * 'dbg close' command
 */
exports.items.push({
  item: "command",
  runAt: "client",
  name: "dbg close",
  description: l10n.lookup("dbgClose"),
  params: [],
  exec: function (args, context) {
    if (!getPanel(context, "jsdebugger")) {
      return;
    }
    let target = context.environment.target;
    return gDevTools.closeToolbox(target).then(() => null);
  }
});

/**
 * 'dbg interrupt' command
 */
exports.items.push({
  item: "command",
  runAt: "client",
  name: "dbg interrupt",
  description: l10n.lookup("dbgInterrupt"),
  params: [],
  exec: function (args, context) {
    let dbg = getPanel(context, "jsdebugger");
    if (!dbg) {
      return l10n.lookup("debuggerStopped");
    }

    let controller = dbg._controller;
    let thread = controller.activeThread;
    if (!thread.paused) {
      thread.interrupt();
    }
  }
});

/**
 * 'dbg continue' command
 */
exports.items.push({
  item: "command",
  runAt: "client",
  name: "dbg continue",
  description: l10n.lookup("dbgContinue"),
  params: [],
  exec: function (args, context) {
    let dbg = getPanel(context, "jsdebugger");
    if (!dbg) {
      return l10n.lookup("debuggerStopped");
    }

    let controller = dbg._controller;
    let thread = controller.activeThread;
    if (thread.paused) {
      thread.resume();
    }
  }
});

/**
 * 'dbg step' command
 */
exports.items.push({
  item: "command",
  runAt: "client",
  name: "dbg step",
  description: l10n.lookup("dbgStepDesc"),
  manual: l10n.lookup("dbgStepManual")
});

/**
 * 'dbg step over' command
 */
exports.items.push({
  item: "command",
  runAt: "client",
  name: "dbg step over",
  description: l10n.lookup("dbgStepOverDesc"),
  params: [],
  exec: function (args, context) {
    let dbg = getPanel(context, "jsdebugger");
    if (!dbg) {
      return l10n.lookup("debuggerStopped");
    }

    let controller = dbg._controller;
    let thread = controller.activeThread;
    if (thread.paused) {
      thread.stepOver();
    }
  }
});

/**
 * 'dbg step in' command
 */
exports.items.push({
  item: "command",
  runAt: "client",
  name: "dbg step in",
  description: l10n.lookup("dbgStepInDesc"),
  params: [],
  exec: function (args, context) {
    let dbg = getPanel(context, "jsdebugger");
    if (!dbg) {
      return l10n.lookup("debuggerStopped");
    }

    let controller = dbg._controller;
    let thread = controller.activeThread;
    if (thread.paused) {
      thread.stepIn();
    }
  }
});

/**
 * 'dbg step over' command
 */
exports.items.push({
  item: "command",
  runAt: "client",
  name: "dbg step out",
  description: l10n.lookup("dbgStepOutDesc"),
  params: [],
  exec: function (args, context) {
    let dbg = getPanel(context, "jsdebugger");
    if (!dbg) {
      return l10n.lookup("debuggerStopped");
    }

    let controller = dbg._controller;
    let thread = controller.activeThread;
    if (thread.paused) {
      thread.stepOut();
    }
  }
});

/**
 * 'dbg list' command
 */
exports.items.push({
  item: "command",
  runAt: "client",
  name: "dbg list",
  description: l10n.lookup("dbgListSourcesDesc"),
  params: [],
  returnType: "dom",
  exec: function (args, context) {
    let dbg = getPanel(context, "jsdebugger");
    if (!dbg) {
      return l10n.lookup("debuggerClosed");
    }

    let sources = getAllSources(dbg);
    let doc = context.environment.chromeDocument;
    let div = createXHTMLElement(doc, "div");
    let ol = createXHTMLElement(doc, "ol");

    sources.forEach(source => {
      let li = createXHTMLElement(doc, "li");
      li.textContent = source.name;
      ol.appendChild(li);
    });
    div.appendChild(ol);

    return div;
  }
});

/**
 * Define the 'dbg blackbox' and 'dbg unblackbox' commands.
 */
[
  {
    name: "blackbox",
    clientMethod: "blackBox",
    l10nPrefix: "dbgBlackBox"
  },
  {
    name: "unblackbox",
    clientMethod: "unblackBox",
    l10nPrefix: "dbgUnBlackBox"
  }
].forEach(function (cmd) {
  const lookup = function (id) {
    return l10n.lookup(cmd.l10nPrefix + id);
  };

  exports.items.push({
    item: "command",
    runAt: "client",
    name: "dbg " + cmd.name,
    description: lookup("Desc"),
    params: [
      {
        name: "source",
        type: {
          name: "selection",
          lookup: function (context) {
            return getAllSources(getPanel(context, "jsdebugger"));
          }
        },
        description: lookup("SourceDesc"),
        defaultValue: null
      },
      {
        name: "glob",
        type: "string",
        description: lookup("GlobDesc"),
        defaultValue: null
      },
      {
        name: "invert",
        type: "boolean",
        description: lookup("InvertDesc")
      }
    ],
    returnType: "dom",
    exec: function (args, context) {
      const dbg = getPanel(context, "jsdebugger");
      const doc = context.environment.chromeDocument;
      if (!dbg) {
        throw new Error(l10n.lookup("debuggerClosed"));
      }

      const { promise, resolve, reject } = context.defer();
      const { activeThread } = dbg._controller;
      const globRegExp = args.glob ? globToRegExp(args.glob) : null;

      // Filter the sources down to those that we will need to black box.

      function shouldBlackBox(source) {
        var value = globRegExp && globRegExp.test(source.url)
          || args.source && source.actor == args.source;
        return args.invert ? !value : value;
      }

      const toBlackBox = [];
      for (let {attachment: {source}} of dbg._view.Sources.items) {
        if (shouldBlackBox(source)) {
          toBlackBox.push(source);
        }
      }

      // If we aren't black boxing any sources, bail out now.

      if (toBlackBox.length === 0) {
        const empty = createXHTMLElement(doc, "div");
        empty.textContent = lookup("EmptyDesc");
        return void resolve(empty);
      }

      // Send the black box request to each source we are black boxing. As we
      // get responses, accumulate the results in `blackBoxed`.

      const blackBoxed = [];

      for (let source of toBlackBox) {
        dbg.blackbox(source, cmd.clientMethod === "blackBox").then(() => {
          blackBoxed.push(source.url);
        }, err => {
          blackBoxed.push(lookup("ErrorDesc") + " " + source.url);
        }).then(() => {
          if (toBlackBox.length === blackBoxed.length) {
            displayResults();
          }
        });
      }

      // List the results for the user.

      function displayResults() {
        const results = doc.createElement("div");
        results.textContent = lookup("NonEmptyDesc");

        const list = createXHTMLElement(doc, "ul");
        results.appendChild(list);

        for (let result of blackBoxed) {
          const item = createXHTMLElement(doc, "li");
          item.textContent = result;
          list.appendChild(item);
        }
        resolve(results);
      }

      return promise;
    }
  });
});

/**
 * A helper to create xhtml namespaced elements.
 */
function createXHTMLElement(document, tagname) {
  return document.createElementNS("http://www.w3.org/1999/xhtml", tagname);
}

/**
 * A helper to go from a command context to a debugger panel.
 */
function getPanel(context, id, options = {}) {
  if (!context) {
    return undefined;
  }

  let target = context.environment.target;

  if (options.ensureOpened) {
    return gDevTools.showToolbox(target, id).then(toolbox => {
      return toolbox.getPanel(id);
    });
  } else {
    let toolbox = gDevTools.getToolbox(target);
    if (toolbox) {
      return toolbox.getPanel(id);
    } else {
      return undefined;
    }
  }
}

/**
 * Converts a glob to a regular expression.
 */
function globToRegExp(glob) {
  const reStr = glob
  // Escape existing regular expression syntax.
    .replace(/\\/g, "\\\\")
    .replace(/\//g, "\\/")
    .replace(/\^/g, "\\^")
    .replace(/\$/g, "\\$")
    .replace(/\+/g, "\\+")
    .replace(/\?/g, "\\?")
    .replace(/\./g, "\\.")
    .replace(/\(/g, "\\(")
    .replace(/\)/g, "\\)")
    .replace(/\=/g, "\\=")
    .replace(/\!/g, "\\!")
    .replace(/\|/g, "\\|")
    .replace(/\{/g, "\\{")
    .replace(/\}/g, "\\}")
    .replace(/\,/g, "\\,")
    .replace(/\[/g, "\\[")
    .replace(/\]/g, "\\]")
    .replace(/\-/g, "\\-")
  // Turn * into the match everything wildcard.
    .replace(/\*/g, ".*");
  return new RegExp("^" + reStr + "$");
}
PK
!<p�)YRRAchrome/devtools/modules/devtools/client/debugger/new/debugger.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

menu {
  display: inline;
  padding: 0;
}

menu > menuitem::after {
  content: "\25BA";
  float: right;
  padding-left: 5px;
}

menu > menupopup {
  display: none;
}

menu > menuitem:hover + menupopup,
menu > menupopup:hover {
  display: block;
}

menupopup {
  position: fixed;
  z-index: 10000;
  background: white;
  border: 1px solid #cccccc;
  padding: 5px 0;
  background: #f2f2f2;
  border-radius: 5px;
  color: #585858;
  box-shadow: 0 0 4px 0 rgba(190, 190, 190, 0.8);
  min-width: 130px;
}

menuitem {
  display: block;
  padding: 0 20px;
  line-height: 20px;
  font-weight: 500;
  font-size: 13px;
  -moz-user-select: none;
  user-select: none;
}

menuitem:hover {
  background: #3780fb;
  color: white;
  cursor: pointer;
}

menuitem[disabled=true] {
  color: #cccccc;
}

menuitem[disabled=true]:hover {
  background-color: transparent;
  cursor: default;
}

menuitem[type=checkbox]::before {
  content: "";
  width: 10px;
  display: inline-block;
}

menuitem[type=checkbox][checked=true]::before {
  content: "\2713";
  left: -8px;
  position: relative;
}

menuseparator {
  border-bottom: 1px solid #cacdd3;
  width: 100%;
  height: 5px;
  display: block;
  margin-bottom: 5px;
}

#contextmenu-mask.show {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 999;
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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.theme-light,
:root .theme-light {
  --theme-search-overlays-semitransparent: rgba(221, 225, 228, 0.66);
}

* {
  box-sizing: border-box;
}

html,
body {
  height: 100%;
  margin: 0;
  padding: 0;
  width: 100%;
}

#mount {
  display: flex;
  height: 100%;
}

::-webkit-scrollbar {
  width: 8px;
  height: 8px;
  background: transparent;
}

::-webkit-scrollbar-track {
  border-radius: 8px;
  background: transparent;
}

::-webkit-scrollbar-thumb {
  border-radius: 8px;
  background: rgba(113, 113, 113, 0.5);
}

:root.theme-dark .CodeMirror-scrollbar-filler {
  background: transparent;
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

.landing-page {
  flex: 1;
  display: flex;
  width: 100vw;
  height: 100vh;
  flex-direction: row;
  align-items: stretch;
  /* Customs properties */
  --title-font-size: 24px;
  --ui-element-font-size: 16px;
  --primary-line-height: 22px;
  --secondary-line-height: 25px;
  --base-spacing: 20px;
  --base-transition: all 0.25s ease;
}

.landing-popup {
  min-width: 0;
}

.landing-page .panel {
  display: flex;
  flex: 1;
  flex-direction: column;
  justify-content: space-between;
}

.landing-page .panel header {
  display: flex;
  align-items: baseline;
  margin: calc(2 * var(--base-spacing)) 0 0;
  padding-bottom: var(--base-spacing);
}

.landing-page .panel header input[type="search"] {
  flex: 1;
  background-color: var(--theme-tab-toolbar-background);
  color: var(--theme-comment);
  font-size: var(--ui-element-font-size);
  border: 1px solid var(--theme-splitter-color);
  padding: calc(var(--base-spacing) / 2);
  margin: 0 var(--base-spacing);
  transition: var(--base-transition);
}

.landing-page .panel .hero {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.landing-page button {
  background-color: var(--theme-tab-toolbar-background);
  color: var(--theme-comment);
  font-size: var(--ui-element-font-size);
  border: 1px solid var(--theme-splitter-color);
  padding: calc(var(--base-spacing) / 2);
  margin: 0 var(--base-spacing);
  transition: var(--base-transition);
  cursor: pointer;
}

.landing-page .panel header h1 {
  color: var(--theme-body-color);
  font-size: var(--title-font-size);
  margin: 0;
  line-height: var(--primary-line-height);
  font-weight: normal;
  padding-left: calc(2 * var(--base-spacing));
}

.landing-page .panel h3 {
  padding-left: calc(2 * var(--base-spacing));
}

.landing-page .panel header input::placeholder {
  color: var(--theme-body-color-inactive);
}

.landing-page .panel header input:focus {
  border: 1px solid var(--theme-selection-background);
}

.landing-page .panel .center-message {
  font-size: var(--ui-element-font-size);
  line-height: var(--secondary-line-height);
  padding: calc(var(--base-spacing) / 2);
}

.landing-page .center a {
  color: var(--theme-highlight-bluegrey);
  text-decoration: none;
}

.landing-page .panel .footer-note {
  padding: var(--base-spacing) 0;
  text-align: center;
  font-size: 14px;
  color: var(--theme-comment);
}

.landing-page .panel .launch-action-container {
  text-align: center;
}

.landing-page .panel .under-construction {
  display: flex;
  width: 417px;
  color: var(--theme-comment);
  font-size: calc(var(--ui-element-font-size) / 1);
  margin: var(--base-spacing) auto;
  max-width: 350px;

  line-height: 1.4em;
}

.landing-page .panel .under-construction .github-link {
  display: block;
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

.landing-page .tab-group {
  flex: 1;
  overflow-y: auto;
}

.landing-page .tab-list {
  list-style: none;
  padding: 0;
  margin: 0;
}

.landing-page .tab {
  border-bottom: 1px solid var(--theme-splitter-color);
  padding: calc(var(--base-spacing) / 2) var(--base-spacing);
  font-family: sans-serif;
  cursor: pointer;
}

.landing-page .tab-sides {
  display: flex;
  justify-content: space-between;
  margin: 0 calc(var(--base-spacing) * 2);
}

.landing-page .tab-title {
  line-height: var(--secondary-line-height);
  font-size: var(--ui-element-font-size);
  color: var(--theme-highlight-bluegrey);
  word-break: break-all;
}

.landing-page .tab-url {
  color: var(--theme-comment);
  word-break: break-all;
}

.landing-page .tab-value {
  color: var(--theme-comment);
  line-height: var(--secondary-line-height);
  font-size: var(--ui-element-font-size);
}

.landing-page .tab:focus,
.landing-page .tab.active {
  background: var(--theme-selection-background);
  color: var(--theme-selection-color);
  transition: var(--base-transition);
}

.landing-page .tab:focus .tab-title,
.landing-page .tab.active .tab-title {
  color: inherit;
}

.landing-page .tab:focus .tab-url,
.landing-page .tab.active .tab-url {
  color: var(--theme-highlight-gray);
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

.landing-page .sidebar {
  --sidebar-width: 200px;
  display: flex;
  background-color: var(--theme-tab-toolbar-background);
  width: var(--sidebar-width);
  flex-direction: column;
  border-right: 1px solid var(--theme-splitter-color);
}

.landing-page .sidebar h1 {
  color: var(--theme-body-color);
  font-size: var(--title-font-size);
  margin: 0;
  line-height: var(--primary-line-height);
  font-weight: bold;
  padding: var(--base-spacing) var(--base-spacing);
}

.landing-page .sidebar ul {
  list-style: none;
  padding: 0;
  line-height: var(--primary-line-height);
  font-size: var(--ui-element-font-size);
}

.landing-page .sidebar li {
  padding: calc(var(--base-spacing) / 4) var(--base-spacing);
}

.landing-page .sidebar li a {
  color: var(--theme-body-color);
}

.landing-page .sidebar li.selected {
  background: var(--theme-highlight-bluegrey);
  color: var(--theme-selection-color);
  transition: var(--base-transition);
}

.landing-page .sidebar li.selected a {
  color: inherit;
}

.landing-page .sidebar li:hover,
.landing-page .sidebar li:focus {
  background: var(--theme-selection-background);
  color: var(--theme-selection-color);
  cursor: pointer;
}

.landing-page .sidebar li:hover a,
.landing-page .sidebar li:focus a {
  color: inherit;
}

.landing-page .sidebar li:last-child {
  border-top: 2px solid var(--theme-splitter-color);
  margin: 2px;
}

.landing-page .sidebar .title-wrapper .launchpad-container {
  padding-left: var(--base-spacing);
}

.landing-page
  .sidebar
  .title-wrapper
  .launchpad-container
  .launchpad-container-icon {
  display: inline-block;
}

.landing-page .sidebar .title-wrapper .launchpad-container .rocket svg {
  width: 18px;
  height: 18px;
}

.landing-page
  .sidebar
  .title-wrapper
  .launchpad-container
  .launchpad-container-title {
  display: inline;
  padding-left: 3px;
  font-weight: normal;
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

.modal-wrapper {
  position: fixed;
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
  transition: z-index 200ms;
  z-index: 100;
}

.modal {
  display: flex;
  width: 80%;
  max-height: 80vh;
  overflow-y: auto;
  background-color: var(--theme-toolbar-background);
  transition: transform 150ms cubic-bezier(0.07, 0.95, 0, 1);
  box-shadow: 1px 1px 3px 1px var(--popup-shadow-color);
}

.modal.entering,
.modal.exited {
  transform: translateY(-101%);
}

.modal.entered,
.modal.exiting {
  transform: translateY(5px);
  flex-direction: column;
}

/* This rule is active when the screen is not narrow */
@media (min-width: 580px) {
  .modal {
    width: 50%;
  }
}

@media (min-height: 340px) {
  .modal.entered,
  .modal.exiting {
    transform: translateY(30px);
  }
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

.shortcuts-content {
  padding: 15px;
  -moz-column-width: 250px;
  -webkit-column-width: 250px;
  column-width: 250px;
}

.shortcuts-content h2 {
  margin-top: 2px;
  margin-bottom: 2px;
  color: var(--theme-content-color1);
}

.shortcuts-section {
  display: inline-block;
  margin: 5px;
  margin-bottom: 15px;
  width: 250px;
}

.shortcuts-list {
  list-style: none;
  margin: 0px;
  padding: 0px;
  overflow: auto;
  width: calc(100% - 1px); /* 1px fixes the hidden right border */
}

.shortcuts-list li {
  font-size: 12px;
  color: var(--theme-body-color);
  padding-top: 5px;
  display: flex;
  justify-content: space-between;
  border: 1px solid transparent;
}

@media (max-width: 640px) {
  .shortcuts-section {
    width: 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/>. */

:root {
  --arrow-width: 10px;
  --icon-size: 13px;
}

:root.theme-light,
:root .theme-light {
  --search-overlays-semitransparent: rgba(221, 225, 228, 0.66);
  --popup-shadow-color: #d0d0d0;
}

:root.theme-dark,
:root .theme-dark {
  --search-overlays-semitransparent: rgba(42, 46, 56, 0.66);
  --popup-shadow-color: #5c667b;
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

* {
  box-sizing: border-box;
}

button {
  background: transparent;
  outline: none;
  border: none;
}

button:hover,
button:focus {
  background-color: var(--theme-toolbar-background-hover);
}

.debugger {
  display: flex;
  flex: 1;
  height: 100%;
}

.editor-pane {
  display: flex;
  position: relative;
  flex: 1;
  background-color: var(--theme-tab-toolbar-background);
  height: calc(100% - 1px);
  overflow: hidden;
}

.theme-dark .editor-pane {
  background-color: var(--theme-toolbar-background);
}

.editor-container {
  width: 100%;
}

.search-container {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: flex;
  z-index: 200;
  background-color: var(--search-overlays-semitransparent);
}

.search-container .close-button {
  width: 16px;
  margin-top: 25px;
  margin-right: 20px;
}

/* Utils */
.absolute-center {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

.d-flex {
  display: flex;
}

.align-items-center {
  align-items: center;
}

.rounded-circle {
  border-radius: 50%;
}

.text-white {
  color: white;
}

.text-center {
  text-align: center;
}

.min-width-0 {
  min-width: 0;
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

menupopup {
  position: fixed;
  z-index: 10000;
  background: white;
  border: 1px solid #cccccc;
  padding: 5px 0;
  background: #f2f2f2;
  border-radius: 5px;
  color: #585858;
  box-shadow: 0 0 4px 0 rgba(190, 190, 190, 0.8);
  min-width: 130px;
}

menuitem {
  display: block;
  padding: 0 20px;
  line-height: 20px;
  font-weight: 500;
  font-size: 13px;
  -moz-user-select: none;
  user-select: none;
}

menuitem:hover {
  background: #3780fb;
  color: white;
}

menuitem[disabled="true"] {
  color: #cccccc;
}

menuitem[disabled="true"]:hover {
  background-color: transparent;
  cursor: default;
}

menuseparator {
  border-bottom: 1px solid #cacdd3;
  width: 100%;
  height: 5px;
  display: block;
  margin-bottom: 5px;
}

#contextmenu-mask.show {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 999;
}
/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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-orange);
  --null-color: var(--theme-comment);
  --object-color: var(--theme-body-color);
  --caption-color: var(--theme-highlight-blue);
  --location-color: var(--theme-content-color1);
  --source-link-color: var(--theme-highlight-blue);
  --node-color: var(--theme-highlight-bluegrey);
  --reference-color: var(--theme-highlight-purple);
}

.theme-firebug {
  --number-color: #000088;
  --string-color: #ff0000;
  --null-color: #787878;
  --object-color: DarkGreen;
  --caption-color: #444444;
  --location-color: #555555;
  --source-link-color: blue;
  --node-color: rgb(0, 0, 136);
  --reference-color: rgb(102, 102, 255);
}

/******************************************************************************/

.objectLink:hover {
  text-decoration: underline;
}

.inline {
  display: inline;
  white-space: normal;
}

.objectBox-object {
  font-weight: bold;
  color: var(--object-color);
  white-space: pre-wrap;
}

.objectBox-string,
.objectBox-text,
.objectLink-textNode,
.objectBox-table {
  white-space: pre-wrap;
}

.objectBox-number,
.objectLink-styleRule,
.objectLink-element,
.objectLink-textNode,
.objectBox-array > .length {
  color: var(--number-color);
}

.objectBox-string {
  color: var(--string-color);
}

.objectLink-function,
.objectBox-stackTrace,
.objectLink-profile {
  color: var(--object-color);
}

.objectLink-Location {
  font-style: italic;
  color: var(--location-color);
}

.objectBox-null,
.objectBox-undefined,
.objectBox-hint,
.logRowHint {
  font-style: italic;
  color: var(--null-color);
}

.objectLink-sourceLink {
  position: absolute;
  right: 4px;
  top: 2px;
  padding-left: 8px;
  font-weight: bold;
  color: var(--source-link-color);
}

/******************************************************************************/

.objectLink-event,
.objectLink-eventLog,
.objectLink-regexp,
.objectLink-object,
.objectLink-Date {
  font-weight: bold;
  color: var(--object-color);
  white-space: pre-wrap;
}

/******************************************************************************/

.objectLink-object .nodeName,
.objectLink-NamedNodeMap .nodeName,
.objectLink-NamedNodeMap .objectEqual,
.objectLink-NamedNodeMap .arrayLeftBracket,
.objectLink-NamedNodeMap .arrayRightBracket,
.objectLink-Attr .attrEqual,
.objectLink-Attr .attrTitle {
  color: var(--node-color);
}

.objectLink-object .nodeName {
  font-weight: normal;
}

/******************************************************************************/

.objectLeftBrace,
.objectRightBrace,
.arrayLeftBracket,
.arrayRightBracket {
  font-weight: bold;
}

.objectLeftBrace,
.arrayLeftBracket {
  margin-right: 4px;
}

.objectRightBrace,
.arrayRightBracket {
  margin-left: 4px;
}

/******************************************************************************/
/* Cycle reference*/

.objectLink-Reference {
  font-weight: bold;
  color: var(--reference-color);
}

.objectBox-array > .objectTitle {
  font-weight: bold;
  color: var(--object-color);
}

.caption {
  font-weight: bold;
  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;
}
/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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: calc(var(--devtools-splitter-inline-start-width) +
    var(--devtools-splitter-inline-end-width) + 1px);

  border-left-width: var(--devtools-splitter-inline-start-width);
  border-right-width: var(--devtools-splitter-inline-end-width);

  margin-left: calc(-1 * var(--devtools-splitter-inline-start-width) - 1px);
  margin-right: calc(-1 * var(--devtools-splitter-inline-end-width));

  cursor: ew-resize;
}

.split-box.horz > .splitter {
  min-height: calc(var(--devtools-splitter-top-width) +
    var(--devtools-splitter-bottom-width) + 1px);

  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;
}

.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;
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

.arrow,
.worker,
.refresh,
.shortcut,
.add-button {
  fill: var(--theme-splitter-color);
}

.file,
.folder,
.domain {
  background-color: var(--theme-comment);
}

.worker,
.file,
.folder {
  position: relative;
  top: 2px;
}

.domain,
.worker,
.refresh,
.add-button {
  position: relative;
  top: 1px;
}

.worker svg,
.refresh svg,
.shortcut svg,
.add-button svg {
  width: 15px;
}

img.domain,
img.folder {
  width: 15px;
  height: 15px;
}

img.domain {
  mask: url("chrome://devtools/skin/images/debugger/domain.svg") no-repeat;
}

img.folder {
  mask: url("chrome://devtools/skin/images/debugger/folder.svg") no-repeat;
}

img.file {
  mask: url("chrome://devtools/skin/images/debugger/file.svg") no-repeat;
  width: 13px;
  height: 13px;
}

img.domain,
img.folder,
img.file {
  mask-size: 100%;
  margin-inline-end: 5px;
  display: inline-block;
}

.refresh svg,
.shortcut svg,
.worker svg {
  margin-inline-end: 5px;
}

img.arrow {
  mask: url("chrome://devtools/skin/images/debugger/arrow.svg");
  margin-inline-end: 5px;
  margin-top: 3px;
  width: 9px;
  height: 9px;
  padding-top: 9px;
  background: var(--disclosure-arrow);
  mask-size: 100%;
  display: inline-block;
  margin-bottom: 1px;
  transform: rotate(-90deg);
  transition: transform 0.18s ease;
}

html[dir="ltr"] img.arrow {
  transform: rotate(-90deg);
}

html[dir="rtl"] img.arrow {
  transform: rotate(90deg);
}

/* TODO (Amit): html is just for specificity. keep it like this? */
html .arrow.expanded svg {
  transform: rotate(0deg);
}

.arrow.hidden {
  visibility: hidden;
}

.webpack svg {
  width: 15px;
  height: 15px;
  margin-right: 5px;
  vertical-align: sub;
}

.function svg {
  height: 10px;
  width: 15px;
}

.function path {
  fill: var(--theme-body-color);
}

.angular svg,
.source-icon svg {
  width: 15px;
  height: 15px;
  margin-right: 5px;
  vertical-align: sub;
}

.angular,
.webpack {
  opacity: 0.8;
}

.theme-dark .angular,
.theme-dark .webpack {
  opacity: 0.5;
}

.source-icon svg {
  fill: var(--theme-comment);
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

.managed-tree .tree {
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  -o-user-select: none;
  user-select: none;

  white-space: nowrap;
  overflow: auto;
  min-width: 100%;

  display: grid;
  grid-template-columns: 1fr;
  align-content: start;
}

.managed-tree .tree button {
  display: block;
}

.managed-tree .tree .node {
  padding: 2px 3px 2px 3px;
  position: relative;
}

.managed-tree .tree .node.focused {
  color: white;
  background-color: var(--theme-selection-background);
}

html:not([dir="rtl"]) .managed-tree .tree .node > div {
  margin-left: 10px;
}

html[dir="rtl"] .managed-tree .tree .node > div {
  margin-right: 10px;
}

.managed-tree .tree .node.focused svg {
  fill: white;
}

.managed-tree .tree-node button {
  position: fixed;
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 {
  overflow: auto;
}

.tree.inline {
  display: inline-block;
}

.tree.nowrap {
  white-space: nowrap;
}

.tree.noselect {
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  -o-user-select: none;
  user-select: none;
}

.tree button {
  display: block;
}

.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: 5px;
  border-inline-start: 1px solid #A2D1FF;
  flex-shrink: 0;
}

/* Align with expandables siblings (where we have the arrow) */
.tree-node[data-expandable="false"] .tree-indent:last-of-type {
  margin-inline-end: 15px;
}

/* For non expandable root nodes, we don't have .tree-indent elements, so we declare
   the margin on the start of the node */
.tree-node[data-expandable="false"][aria-level="0"] {
  padding-inline-start: 15px
}

.tree .tree-node[data-expandable="true"] {
  cursor: default;
}

.tree .tree-node.focused {
  color: white;
  background-color: var(--theme-selection-background, #0a84ff);
}

.tree-node.focused .arrow svg {
  fill: currentColor;
}

.tree-node:hover img {
  background-color: var(--theme-content-color3);
}

.arrow svg {
  fill: var(--theme-splitter-color, #9B9B9B);
  transition: transform 0.125s ease;
  width: 10px;
  margin-inline-end: 5px;
  transform: rotate(-90deg);
}

html[dir="rtl"] .arrow svg,
.arrow svg:dir(rtl),
.arrow svg:-moz-locale-dir(rtl) {
  transform: rotate(90deg);
}

.arrow.expanded.expanded svg {
  transform: rotate(0deg);
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

.close-btn {
  width: 14px;
  height: 14px;
  border: 1px solid transparent;
  border-radius: 2px;
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
}

.close-btn .close {
  mask: url("chrome://devtools/skin/images/debugger/close.svg") no-repeat;
  mask-size: 100%;
  background-color: var(--theme-comment);
  width: 8px;
  height: 8px;
  transition: all 0.15s ease-in-out;
  padding: 0;
  margin-top: 0;
}

.close-btn:hover img.close {
  background-color: white;
}

.close-btn:hover {
  background-color: var(--theme-selection-background);
}

.close-btn.big {
  width: 16px;
  height: 16px;
}

.close-btn.big .close {
  width: 9px;
  height: 9px;
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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-field {
  width: calc(100% - 1px);
  height: 27px;
  background-color: var(--theme-toolbar-background);
  border-bottom: 1px solid var(--theme-splitter-color);
  padding-right: 10px;
  display: flex;
  flex-shrink: 0;
}

.search-field.big {
  height: 40px;
}

.search-field i {
  display: block;
  padding: 0;
  width: 16px;
}

.search-field i svg {
  width: 16px;
}

.search-field.big input {
  line-height: 40px;
}

.search-field input {
  border: none;
  line-height: 30px;
  background-color: var(--theme-toolbar-background);
  color: var(--theme-body-color-inactive);
  width: calc(100% - 38px);
  flex: 1;
}

.theme-dark .search-field input {
  color: var(--theme-body-color-inactive);
}

.search-field i.magnifying-glass,
.search-field i.sad-face {
  padding: 6px;
  width: 24px;
}

.search-field.big i.magnifying-glass,
.search-field.big i.sad-face {
  padding: 14px;
  width: 40px;
}

.search-field .magnifying-glass path,
.search-field .magnifying-glass ellipse {
  stroke: var(--theme-comment);
}

.search-field input::placeholder {
  color: var(--theme-toolbar-color);
}

.search-field input:focus {
  outline-width: 0;
}

.search-field input.empty {
  color: var(--theme-highlight-orange);
}

.search-field.big .summary {
  line-height: 40px;
}

.search-field .summary {
  line-height: 27px;
  padding-right: 10px;
  color: var(--theme-body-color-inactive);
}

.search-field .search-nav-buttons {
  display: flex;
  user-select: none;
}

.search-field .search-nav-buttons .nav-btn {
  display: flex;
  height: 100%;
  background: transparent;
  transition: all 0.25s ease-in-out;
  border: 1px solid transparent;
  justify-content: center;
  padding-top: 4px;
}

.search-field .search-nav-buttons .nav-btn:hover {
  background: var(--theme-toolbar-background-hover);
}

.search-field .search-nav-buttons .nav-btn:active path {
  fill: var(--theme-comment-alt);
}

.search-field .search-nav-buttons .nav-btn path {
  fill: var(--theme-comment);
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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-container {
  position: absolute;
  top: 30px;
  left: 0;
  width: calc(100% - 1px);
  height: calc(100% - 31px);
  display: flex;
  flex-direction: column;
  z-index: 20;
  background-color: var(--theme-body-background);
  overflow-y: hidden;
}

.project-text-search {
  flex-grow: 1;
  display: flex;
  flex-direction: column;
  overflow-y: hidden;
  height: 100%;
}

.project-text-search .result {
  display: flex;
  cursor: default;
  padding: 4px 0 4px 30px;
  line-height: 16px;
  font-size: 10px;
  width: 100%;
}

.project-text-search .matches-summary {
  margin-left: 5px;
}

.project-text-search .result {
  font-family: Menlo, monospace;
}

.project-text-search .result.focused {
  background-color: var(--search-overlays-semitransparent);
}

.project-text-search .result .query-match {
  background-color: var(--theme-selection-background);
  color: white;
  padding: 1px 4px;
  margin: 0 2px 0 2px;
  border-radius: 2px;
}

.project-text-search .result.focused .line-number {
  font-weight: bolder;
}

.project-text-search .result .line-number {
  margin-right: 1em;
  width: 2em;
  margin-left: 0.8em;
}

.project-text-search .no-result-msg {
  color: var(--theme-body-color-inactive);
  font-size: 24px;
  padding: 4px 15px;
  max-width: 100%;
  overflow-wrap: break-word;
  hyphens: auto;
}

.project-text-search .file-result {
  font-weight: bold;
  line-height: 20px;
  cursor: default;
  padding: 2px 0 2px 5px;
  font-size: 12px;
}

.project-text-search .file-result .arrow {
  margin: 2px 0 2px 0;
}

.project-text-search .file-result.focused {
  background-color: var(--search-overlays-semitransparent);
}

.project-text-search .line-match {
  display: "flex";
  grow: 1;
}

.project-text-search .search-field {
  display: flex;
  align-self: stretch;
  flex-grow: 1;
}

.project-text-search .search-field .close-btn.big {
  margin-top: 12px;
}

.project-text-search .managed-tree {
  overflow-y: auto;
  height: 100%;
}

.project-text-search .managed-tree .tree {
  height: 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/>. */

.sources-panel {
  background-color: var(--theme-sidebar-background);
  display: flex;
  flex: 1;
  flex-direction: column;
  overflow: hidden;
  position: relative;
}

.sources-panel * {
  -moz-user-select: none;
  user-select: none;
}

.sources-list {
  flex: 1;
  display: flex;
  overflow-x: hidden;
  overflow-y: auto;
}

.sources-list .tree:focus {
  outline: none;
}

.sources-list .managed-tree {
  flex: 1;
  display: flex;
  overflow-x: hidden;
  overflow-y: auto;
}

.sources-list .managed-tree .tree .node {
  padding: 0 10px 0 3px;
  width: 100%;
}

.sources-list .tree img.arrow {
  margin-right: 5px;
}

.sources-list .tree .focused img.arrow {
  background-color: white;
}

.sources-list .tree img.arrow.expanded {
  transform: rotate(0deg);
}

.theme-dark .source-list .tree .node.focused {
  background-color: var(--theme-tab-toolbar-background);
}

.sources-list .tree .focused .label {
  background-color: var(--theme-selection-background);
}

.sources-list .tree .label {
  padding: 3px 0px 3px 0px;
  display: inline-block;
}

.sources-list .tree .node .no-arrow {
  width: 10px;
  display: inline-block;
}

.no-sources-message {
  width: 100%;
  font-style: italic;
  text-align: center;
  padding: 0.5em;
  font-size: 12px;
  user-select: none;
  justify-content: center;
  align-items: center;
}

.sources-panel .outline {
  display: flex;
  flex: 1;
}

.tree-indent {
  border-inline-start: 0 none;
}

.source-outline-tabs {
  width: 100%;
  background: var(--theme-body-background);
  border-top: 1px solid var(--theme-splitter-color);
  display: flex;
  -moz-user-select: none;
  user-select: none;
  box-sizing: border-box;
  height: 30px;
}

.source-outline-tabs .tab {
  background-color: var(--theme-toolbar-background);
  border-bottom: 1px solid transparent;
  border-color: var(--theme-splitter-color);
  color: var(--theme-body-color);
  cursor: default;
  display: inline-flex;
  flex: 1;
  justify-content: center;
  margin-bottom: 0px;
  margin-top: -1px;
  overflow: hidden;
  padding: 8px 8px 7px 8px;
  position: relative;
  transition: all 0.25s ease;
}

.source-outline-tabs .tab:first-child {
  border-inline-end: 1px solid var(--theme-splitter-color);
}

.source-outline-tabs .tab:hover {
  background-color: var(--theme-toolbar-background-alt);
  border-color: var(--theme-splitter-color);
}

.source-outline-tabs .tab.active {
  background-color: var(--theme-body-background);
  border-bottom-color: transparent;
}

.source-outline-tabs .tab.active path,
.source-outline-tabs .tab:hover path {
  fill: var(--theme-body-color);
}

.sources-list .managed-tree .tree .node img.blackBox {
  mask: url("chrome://devtools/skin/images/debugger/blackBox.svg") no-repeat;
  mask-size: 100%;
  background-color: var(--theme-highlight-blue);
  width: 13px;
  height: 13px;
  display: inline-block;
  margin-inline-end: 5px;
  margin-bottom: -2px;
}

.sources-list .managed-tree .tree .node.focused img {
  background-color: white;
}

.theme-dark .sources-list .managed-tree .tree .node:not(.focused) img.blackBox {
  background-color: var(--theme-comment);
}

.theme-dark .sources-list .managed-tree .tree .node img.blackBox {
  background-color: var(--theme-body-color);
}

.theme-dark .sources-list .managed-tree .tree .node.focused img.blackBox {
  background-color: white;
}

.tree:not(.object-inspector) .tree-node[data-expandable="false"] .tree-indent:last-of-type {
  margin-inline-end: 4px;
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

.outline {
  overflow-y: auto;
}

.outline .outline-pane-info {
  width: 100%;
  font-style: italic;
  text-align: center;
  padding: 0.5em;
  user-select: none;
  font-size: 12px;
}

.outline-list {
  list-style-type: none;
  flex: 1 0 100%;
  padding: 10px 0px;
  margin: 0;
  font-family: var(--monospace-font-family);
}

.outline-list__class-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.outline-list__class-list .function-signature .function-name {
  color: var(--theme-highlight-green);
}

.outline-list .function-signature .paren {
  color: inherit;
}

.outline-list h2 {
  font-weight: normal;
  font-size: 1em;
  margin: 10px 0 10px 10px;
  color: var(--blue-55);
}

.outline-list h2:hover {
  background: var(--theme-toolbar-background-hover);
}

.theme-dark .outline-list h2 {
  color: var(--theme-highlight-blue);
}

.outline-list h2 .keyword {
  color: var(--theme-highlight-red);
}

.outline-list__element {
  padding: 3px 10px 3px 10px;
  cursor: default;
  white-space: nowrap;
}

.outline-list__element-icon {
  padding-right: 0.4rem;
  padding-left: 1rem;
}

.outline-list__element:hover {
  background: var(--theme-toolbar-background-hover);
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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-signature {
  align-self: center;
}

.function-signature .function-name {
  color: var(--theme-highlight-blue);
}

.function-signature .param {
  color: var(--theme-highlight-red);
}

.function-signature .paren {
  color: var(--object-color);
}

.function-signature .comma {
  color: var(--object-color);
}
.command-bar-button {
  appearance: none;
  background: transparent;
  border: none;
  display: inline-block;
  text-align: center;
  position: relative;
  padding: 0px 5px;
  fill: currentColor;
}

.command-bar-button:focus {
  outline: none;
}

.command-bar-button:disabled {
  opacity: 0.8;
  cursor: default;
}

.command-bar-button:not(.disabled):hover {
  background: var(--theme-toolbar-background-hover);
}

:root.theme-dark .command-bar-button {
  color: var(--theme-body-color);
}

.command-bar-button > * {
  width: 16px;
  height: 16px;
  display: inline-block;
  vertical-align: middle;
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

.toggle-button {
  transform: translate(0, 0px);
  transition: transform 0.25s ease-in-out;
  padding: 5px;
}

.toggle-button .togglePanes {
  vertical-align: -2px;
}

.toggle-button svg {
  fill: var(--theme-comment);
  vertical-align: 0;
}

:root.theme-dark .toggle-button svg {
  fill: var(--theme-comment-alt);
}

.toggle-button.end {
  margin-inline-end: 0px;
  margin-inline-start: auto;
}

.toggle-button.start {
  margin-inline-start: 0px;
}

html:not([dir="rtl"]) .toggle-button.end svg,
html[dir="rtl"] .toggle-button.start svg {
  transform: rotate(180deg);
}

html .toggle-button.end.vertical svg {
  transform: rotate(-90deg);
}

.toggle-button.start.collapsed,
.toggle-button.end.collapsed {
  transform: rotate(180deg);
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

.source-footer {
  background: var(--theme-body-background);
  border-top: 1px solid var(--theme-splitter-color);
  position: absolute;
  display: flex;
  bottom: 0;
  left: 0;
  right: 0;
  opacity: 1;
  z-index: 1;
  -moz-user-select: none;
  user-select: none;
  height: var(--editor-footer-height);
  box-sizing: border-box;
}

.source-footer .commands {
  display: flex;
  align-items: center;
}

.source-footer .commands * {
  -moz-user-select: none;
  user-select: none;
}

.source-footer > .commands > .action {
  display: flex;
  justify-content: center;
  align-items: center;
  transition: opacity 200ms;
  border: none;
  background: transparent;
  padding: 6px;
}

.source-footer > .commands > .action img {
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.source-footer > button.action:hover {
  background: var(--theme-toolbar-background-hover);
}

.source-footer > .commands > button.action:focus {
  outline: none;
}

:root.theme-dark .source-footer > .commands > .action {
  fill: var(--theme-body-color);
}

:root.theme-dark .source-footer > .commands > .action:hover {
  fill: var(--theme-selection-color);
}

.source-footer > .commands > .action > img.prettyPrint {
  mask: url("chrome://devtools/skin/images/debugger/prettyPrint.svg") no-repeat;
  height: 16px;
  width: 16px;
  background: var(--theme-body-color);
}

.source-footer > .commands > .action > img.blackBox {
  mask: url("chrome://devtools/skin/images/debugger/blackBox.svg") no-repeat;
  height: 16px;
  width: 16px;
  background: var(--theme-body-color);
}

.source-footer .commands .coverage {
  color: var(--theme-body-color);
}

.coverage-on .source-footer .commands .coverage {
  color: var(--theme-highlight-blue);
  border: 1px solid var(--theme-body-color-inactive);
  border-radius: 2px;
}

.source-footer > .commands > .blackboxed > img.blackBox {
  background: var(--theme-highlight-blue);
}

.source-footer .blackbox-summary {
  color: var(--theme-body-color);
}

.source-footer .mapped-source {
  color: var(--theme-body-color);
  padding: 2.5px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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-bar {
  display: flex;
  flex-direction: column;
}

.search-bar .search-field {
  padding-left: 7px;
  height: var(--editor-searchbar-height);
}

.search-field .close-btn {
  margin-top: 7px;
}

.search-bottom-bar * {
  -moz-user-select: none;
  user-select: none;
}

.search-bottom-bar {
  display: flex;
  flex-shrink: 0;
  justify-content: flex-end;
  width: calc(100% - 1px);
  height: var(--editor-second-searchbar-height);
  background-color: var(--theme-toolbar-background);
  border-bottom: 1px solid var(--theme-splitter-color);
  padding: 0 13px;
}

.search-bottom-bar button:focus {
  outline: none;
}

.search-bottom-bar .search-modifiers {
  display: flex;
  align-items: center;
}

.search-bottom-bar .search-modifiers button {
  padding: 0 3px;
  margin: 0 3px;
  border: none;
  background: none;
  width: 20px;
  height: 20px;
  border-radius: 3px;
}

.search-bottom-bar .search-modifiers button i {
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 0;
  width: 16px;
}

.search-bottom-bar .search-modifiers button svg {
  fill: var(--theme-comment);
  height: 16px;
  width: 16px;
}

.search-bottom-bar .search-modifiers button:hover {
  background: var(--theme-toolbar-background-hover);
}

.search-bottom-bar .search-modifiers button:active {
  outline: none;
}

.search-bottom-bar .search-modifiers button.active svg {
  fill: var(--theme-selection-background);
}

.theme-dark .search-bottom-bar .search-modifiers button.active svg {
  fill: white;
}

.search-bottom-bar .search-type-toggles {
  display: flex;
  align-items: center;
  max-width: 68%;
}

.search-bottom-bar .search-type-name {
  padding: 1px 0 0 0;
  margin: 0 0 0 6px;
  border: none;
  background: transparent;
  color: var(--theme-comment);
}

.search-bottom-bar .search-type-toggles .search-type-btn:active {
  outline: none;
}

.search-bottom-bar .search-type-toggles .search-type-btn.active {
  color: var(--theme-selection-background);
}

.theme-dark .search-bottom-bar .search-type-toggles .search-type-btn.active {
  color: white;
}

.search-bar .result-list {
  max-height: 230px;
}
/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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-highlight-blue);
  --node-color: var(--theme-highlight-purple);
  --reference-color: var(--theme-highlight-blue);
  --comment-node-color: var(--theme-comment);
}

.theme-firebug {
  --number-color: #000088;
  --string-color: #FF0000;
  --null-color: #787878;
  --object-color: DarkGreen;
  --caption-color: #444444;
  --location-color: #555555;
  --source-link-color: blue;
  --node-color: rgb(0, 0, 136);
  --reference-color: rgb(102, 102, 255);
}

/******************************************************************************/

.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;
}

.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-string a, .objectBox-string a:visited {
  color: currentColor;
  text-decoration: none;
  font-style: italic;
}

.objectBox-string a:hover {
  text-decoration: underline;
}

.objectBox-function,
.objectBox-stackTrace,
.objectBox-profile {
  color: var(--object-color);
}

.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-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);
}

.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 button */

.open-inspector svg {
  fill: var(--comment-node-color);
  height: 16px;
  width: 16px;
  margin-left: .25em;
  cursor: pointer;
  vertical-align: middle;
}

.objectBox-node:hover .open-inspector svg,
.objectBox-textNode:hover .open-inspector svg,
.open-inspector svg:hover {
  fill: var(--theme-highlight-blue);
}

/******************************************************************************/
/* Jump to definition button */

.jump-definition svg {
  stroke: var(--comment-node-color);
  height: 16px;
  width: 16px;
  margin-left: .25em;
  cursor: pointer;
  vertical-align: middle;
}

.jump-definition svg:hover {
  stroke: var(--theme-highlight-blue);
}

/******************************************************************************/
/* "more…" ellipsis */
.more-ellipsis {
  color: var(--comment-node-color);
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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);
}

.object-inspector .object-delimiter {
  color: var(--theme-comment);
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

.bracket-arrow {
  position: absolute;
}

.bracket-arrow::before,
.bracket-arrow::after {
  content: "";
  height: 0;
  width: 0;
  position: absolute;
  border: 7px solid transparent;
}

.bracket-arrow.up::before {
  border-bottom-color: var(--theme-splitter-color);
  top: -1px;
}

.theme-dark .bracket-arrow.up::before {
  border-bottom-color: var(--theme-body-color);
}

.bracket-arrow.up::after {
  border-bottom-color: var(--theme-body-background);
  top: 0px;
}

.bracket-arrow.down::before {
  border-bottom-color: transparent;
  border-top-color: var(--theme-splitter-color);
  top: 0px;
}

.theme-dark .bracket-arrow.down::before {
  border-top-color: var(--theme-body-color);
}

.bracket-arrow.down::after {
  border-bottom-color: transparent;
  border-top-color: var(--theme-body-background);
  top: -1px;
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

.popover {
  position: fixed;
  z-index: 100;
}

.popover .gap {
  height: 5px;
  padding-top: 5px;
}

.popover .preview-popup {
  margin-left: -55px;
}

.popover .add-to-expression-bar {
  margin-left: -55px;
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

.popover .preview-popup {
  background: var(--theme-body-background);
  width: 350px;
  min-height: 80px;
  border: 1px solid var(--theme-splitter-color);
  padding: 10px;
  height: auto;
  min-height: inherit;
  max-height: 200px;
  overflow: auto;
  box-shadow: 1px 2px 3px var(--popup-shadow-color);
}

.theme-dark .popover .preview-popup {
  box-shadow: 1px 2px 3px var(--popup-shadow-color);
}

.popover .preview-popup .header-container {
  width: 100%;
  line-height: 15px;
  display: flex;
  flex-direction: row;
  margin-bottom: 5px;
}

.popover .preview-popup .immutable-logo {
  width: 20px;
  margin-right: 5px;
}

.popover .preview-popup .header-container h3 {
  margin: 0;
  margin-bottom: 5px;
  font-weight: normal;
  font-size: 14px;
}

.popover .preview-popup .header .link {
  align-self: flex-end;
  color: var(--theme-highlight-blue);
  text-decoration: underline;
}

.preview-selection:hover {
  cursor: default;
}

.preview-selection,
.debug-expression.preview-selection {
  background-color: var(--theme-highlight-yellow);
}

.theme-dark .preview-selection,
.theme-dark .debug-expression.preview-selection {
  background-color: #743884;
}

.theme-dark .cm-s-mozilla .preview-selection,
.theme-dark .cm-s-mozilla .debug-expression.preview-selection {
  color: #e7ebee;
}

.popover .preview-popup .function-signature {
  padding-top: 10px;
}

.theme-dark .popover .preview-popup {
  border-color: var(--theme-body-color);
}

.theme-dark .popover .preview-popup .arrow svg {
  fill: var(--theme-comment);
}

.tooltip {
  position: fixed;
  z-index: 100;
}

.tooltip .preview-popup {
  background: var(--theme-toolbar-background);
  max-width: inherit;
  min-height: 80px;
  border: 1px solid var(--theme-splitter-color);
  box-shadow: 1px 2px 4px 1px var(--theme-toolbar-background-alt);
  padding: 5px;
  height: auto;
  min-height: inherit;
  max-height: 200px;
  overflow: auto;
}

.theme-dark .tooltip .preview-popup {
  border-color: var(--theme-body-color);
}

.tooltip .gap {
  height: 4px;
  padding-top: 4px;
}

.add-to-expression-bar {
  border: 1px solid var(--theme-splitter-color);
  border-top: none;
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  -webkit-box-align: center;
  -ms-flex-align: center;
  align-items: center;
  font-size: 14px;
  line-height: 30px;
  background: var(--theme-toolbar-background);
  color: var(--theme-comment-alt);
  padding: 0 4px;
}

.add-to-expression-bar .prompt {
  width: 1em;
}

.add-to-expression-bar .expression-to-save-label {
  width: calc(100% - 4em);
}

.add-to-expression-bar .expression-to-save-button {
  font-size: 14px;
  color: var(--theme-comment);
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

.call-site {
  background: #f0f9ff;
  position: relative;
}

.call-site::before {
  content: "";
  position: absolute;
  width: 100%;
  height: calc(100% - 2px);
  border-bottom: 2px solid #aed3ef;
}

.call-site-bp {
  position: relative;
}

.debug-expression.call-site-bp,
.call-site-bp {
  background-color: #fce7e7;
}

.call-site-bp::before {
  content: "";
  position: absolute;
  width: 100%;
  height: calc(100% - 2px);
  border-bottom: 2px solid red;
}

.theme-dark .call-site {
  background-color: #4b5462;
}

.theme-dark .call-site::before {
  border-bottom-color: #5f78a4;
}

.theme-dark .call-site-bp {
  background-color: #4b3f3f;
}

.theme-dark .call-site-bp::before {
  border-bottom-color: #dd4d4d;
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

.empty-line .CodeMirror-linenumber {
  opacity: 0.5;
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

.conditional-breakpoint-panel {
  cursor: initial;
  margin: 1em 0;
  position: relative;
  display: flex;
  align-items: center;
  background: var(--theme-toolbar-background);
  border-top: 1px solid var(--theme-splitter-color);
  border-bottom: 1px solid var(--theme-splitter-color);
}

.conditional-breakpoint-panel .prompt {
  font-size: 1.8em;
  color: var(--theme-conditional-breakpoint-color);
  padding-left: 3px;
  padding-right: 3px;
  padding-bottom: 3px;
  text-align: right;
  width: 30px;
}

.conditional-breakpoint-panel input {
  margin: 5px 10px;
  width: calc(100% - 4em);
  border: none;
  background: var(--theme-toolbar-background);
  font-size: 14px;
  color: var(--theme-conditional-breakpoint-color);
  line-height: 30px;
}

.conditional-breakpoint-panel input:focus {
  outline-width: 0;
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

.editor-wrapper {
  --debug-line-border: rgb(145, 188, 219);
  --debug-expression-background: rgba(202, 227, 255, 0.5);
  --editor-searchbar-height: 27px;
  --editor-second-searchbar-height: 27px;
  --debug-line-error-border: rgb(255, 0, 0);
  --debug-expression-error-background: rgba(231, 116, 113, 0.3);
  --editor-header-height: 30px;
}

.theme-dark .editor-wrapper {
  --debug-expression-background: #54617e;
  --debug-line-border: #7786a2;
}

.editor-wrapper .CodeMirror-linewidget {
  margin-right: -7px;
  overflow: hidden;
}

.editor-wrapper {
  min-width: 0 !important;
}

.theme-dark {
  --theme-conditional-breakpoint-color: #9fa4a9;
}

.theme-light {
  --theme-conditional-breakpoint-color: var(--theme-body-color);
}

/**
 * There's a known codemirror flex issue with chrome that this addresses.
 * BUG https://github.com/devtools-html/debugger.html/issues/63
 */
.editor-wrapper {
  position: absolute;
  height: calc(100% - var(--editor-header-height));
  width: calc(100% - 1px);
  top: var(--editor-header-height);
  left: 0px;
  --editor-footer-height: 24px;
}

html[dir="rtl"] .editor-mount {
  direction: ltr;
}

.editor-wrapper .breakpoints {
  position: absolute;
  top: 0;
  left: 0;
}

.function-search {
  max-height: 300px;
  overflow: hidden;
}

.function-search .results {
  height: auto;
}

.editor.hit-marker {
  height: 14px;
}

.editor-wrapper .highlight-lines {
  background: var(--theme-selection-background-hover);
}

.coverage-on .CodeMirror-code :not(.hit-marker) .CodeMirror-line,
.coverage-on .CodeMirror-code :not(.hit-marker) .CodeMirror-gutter-wrapper {
  opacity: 0.5;
}

.editor.new-breakpoint svg {
  fill: var(--theme-selection-background);
  width: 60px;
  height: 14px;
  position: absolute;
  top: 0px;
  right: -4px;
}

.inline-bp {
  background-color: #9ddfff;
  width: 20px;
  padding: 0px 5px;
  margin: 0px 4px;
  border-radius: 5px;
  border-color: blue;
  border: 1px solid #00b6ff;
}

.editor.new-breakpoint.folding-enabled svg {
  right: -16px;
}

.new-breakpoint.has-condition svg {
  fill: var(--theme-graphs-yellow);
}

.editor.new-breakpoint.breakpoint-disabled svg {
  opacity: 0.3;
}

.editor.column-breakpoint svg {
  fill: var(--theme-selection-background);
  vertical-align: middle;
  width: 17px;
  height: 14px;
}

.editor.column-breakpoint.breakpoint-disabled svg {
  opacity: 0.3;
}

.CodeMirror {
  width: 100%;
  height: 100%;
}

.editor-wrapper .editor-mount {
  width: 100%;
  background-color: var(--theme-body-background);
}

.CodeMirror-linenumber {
  font-size: 11px;
  line-height: 14px;
}

.folding-enabled .CodeMirror-linenumber {
  text-align: left;
  padding: 0 0 0 2px;
}

/* set the linenumber white when there is a breakpoint */
.new-breakpoint .CodeMirror-gutter-wrapper .CodeMirror-linenumber {
  color: white;
}

/* move the breakpoint below the other gutter elements */
.new-breakpoint .CodeMirror-gutter-elt:nth-child(2) {
  z-index: 0;
}

.editor-wrapper .CodeMirror-line {
  font-size: 11px;
}

.theme-dark .editor-wrapper .CodeMirror-line .cm-comment {
  color: var(--theme-comment);
}

.debug-expression {
  background-color: var(--debug-expression-background);
}

debug-expression-error {
  background-color: var(--debug-expression-error-background);
}

.new-debug-line .CodeMirror-line {
  background-color: transparent !important;
  outline: var(--debug-line-border) solid 1px;
}

/* Don't display the highlight color since the debug line
   is already highlighted */
.new-debug-line .CodeMirror-activeline-background {
  display: none;
}

.new-debug-line-error .CodeMirror-line {
  background-color: var(--debug-expression-error-background) !important;
  outline: var(--debug-line-error-border) solid 1px;
}

/* Don't display the highlight color since the debug line
  is already highlighted */
.new-debug-line-error .CodeMirror-activeline-background {
  display: none;
}

.highlight-line .CodeMirror-line {
  animation: fade-highlight-out 1.5s normal forwards;
}

@keyframes fade-highlight-out {
  0% {
    background-color: var(--theme-highlight-gray);
  }
  100% {
    background-color: transparent;
  }
}

.theme-dark .highlight-line .CodeMirror-line {
  animation: fade-highlight-out-dark 1.5s normal forwards;
}

@keyframes fade-highlight-out-dark {
  0% {
    background-color: var(--theme-comment);
  }
  100% {
    background-color: transparent;
  }
}

.CodeMirror-guttermarker-subtle {
  visibility: hidden;
}

.visible {
  visibility: visible;
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

.cm-highlight {
  position: relative;
}

.cm-highlight::before {
  position: absolute;
  border-top-style: solid;
  border-bottom-style: solid;
  border-top-color: var(--theme-comment-alt);
  border-bottom-color: var(--theme-comment-alt);
  border-top-width: 1px;
  border-bottom-width: 1px;
  top: -1px;
  bottom: 0;
  left: 0;
  right: 0;
  content: "";
  margin-bottom: -1px;
}

.cm-highlight-full::before {
  border: 1px solid var(--theme-comment-alt);
  border-radius: 2px;
  margin: 0 -1px -1px -1px;
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

.breakpoints-toggle {
  margin: 2px 3px;
}

.breakpoints-list * {
  user-select: none;
}

.breakpoints-list .breakpoint {
  font-size: 12px;
  color: var(--theme-content-color1);
  padding: 0.5em 1em 0.5em 0.5em;
  line-height: 1em;
  position: relative;
  transition: all 0.25s ease;
}

html[dir="rtl"] .breakpoints-list .breakpoint {
  border-right: 4px solid transparent;
}

html:not([dir="rtl"]) .breakpoints-list .breakpoint {
  border-left: 4px solid transparent;
}

.breakpoints-list .breakpoint:last-of-type {
  padding-bottom: 0.45em;
}

html:not([dir="rtl"]) .breakpoints-list .breakpoint.is-conditional {
  border-left-color: var(--theme-graphs-yellow);
}

html[dir="rtl"] .breakpoints-list .breakpoint.is-conditional {
  border-right-color: var(--theme-graphs-yellow);
}

html .breakpoints-list .breakpoint.paused {
  background-color: var(--theme-toolbar-background-alt);
  border-color: var(--breakpoint-active-color);
}

.breakpoints-list .breakpoint.disabled .breakpoint-label {
  color: var(--theme-comment);
  transition: color 0.5s linear;
}

.breakpoints-list .breakpoint:hover {
  background-color: var(--search-overlays-semitransparent);
}

.breakpoints-list .breakpoint.paused:hover {
  border-color: var(--breakpoint-active-color-hover);
}

.breakpoints-list .breakpoint-label {
  max-width: calc(100% - var(--breakpoint-expression-right-clear-space));
  display: inline-block;
  padding-inline-start: 2px;
  cursor: default;
}

.breakpoint-label .breakpoint-checkbox {
  margin-inline-start: 0;
  vertical-align: text-bottom;
}

.breakpoint-label .location {
  width: 100%;
  display: inline-block;
  overflow-x: hidden;
  text-overflow: ellipsis;
  padding: 1px 0;
  vertical-align: bottom;
}

.breakpoints-list .pause-indicator {
  flex: 0 1 content;
  order: 3;
}

:root.theme-light .breakpoint-snippet,
:root.theme-firebug .breakpoint-snippet {
  color: var(--theme-comment);
}

:root.theme-dark .breakpoint-snippet {
  color: var(--theme-body-color);
  opacity: 0.6;
}

.breakpoint-snippet {
  overflow: hidden;
  text-overflow: ellipsis;
  padding-inline-start: 18px;
  padding-inline-end: 18px;
}

.breakpoint .close-btn {
  position: absolute;
  offset-inline-end: 13px;
  offset-inline-start: auto;
  top: 9px;
}

.breakpoint .close {
  visibility: hidden;
}

.breakpoint:hover .close {
  visibility: visible;
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

.expression-input-form {
  width: 100%;
}

.input-expression {
  width: 100%;
  margin: 0;
  border: 1px;
  background-color: var(--theme-sidebar-background);
  font-size: 12px;
  padding: 0.5em 2.16em;
  color: var(--theme-body-color);
}

@keyframes shake {
  0%,
  100% {
    transform: translateX(0);
  }
  20%,
  60% {
    transform: translateX(-10px);
  }
  40%,
  80% {
    transform: translateX(10px);
  }
}

.input-expression.error {
  border: 1px solid red;
  animation: 150ms cubic-bezier(0.07, 0.95, 0, 1) shake;
}

.input-expression::placeholder {
  text-align: center;
  font-style: italic;
  color: var(--theme-comment);
}

.input-expression:focus {
  outline: none;
  cursor: text;
}

.expressions-list {
  /* TODO: add normalize */
  margin: 0;
  padding: 0;
}
.expression-input-container {
  display: flex;
}

.expression-container {
  border: 1px;
  padding: 0.25em 1em 0.25em 0.5em;
  width: 100%;
  color: var(--theme-body-color);
  background-color: var(--theme-body-background);
  display: block;
  position: relative;
}

.expression-container > .tree {
  width: 100%;
  overflow: hidden;
}

:root.theme-light .expression-container:hover {
  background-color: var(--theme-tab-toolbar-background);
}

:root.theme-dark .expression-container:hover {
  background-color: var(--search-overlays-semitransparent);
}

.expression-container__close-btn {
  position: absolute;
  offset-inline-end: 0px;
  top: 0px;
}

.expression-content {
  position: relative;
}

.expression-content .tree {
  overflow-x: hidden;
  max-width: calc(100% - var(--breakpoint-expression-right-clear-space));
}

.expression-content .tree-node {
  overflow-x: hidden;
}

.expression-input {
  max-width: 50%;
}

.expressions-list .tree:focus {
  outline: none;
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

.frames ul .frames-group .group,
.frames ul .frames-group .group .location {
  font-weight: 500;
  cursor: default;
}

.frames ul .frames-group.expanded .group,
.frames ul .frames-group.expanded .group .location {
  color: var(--theme-highlight-blue);
}

.frames ul .frames-group.expanded .react path {
  fill: var(--theme-highlight-blue);
}

.frames ul .frames-group .frames-list li {
  padding-left: 30px;
}

.frames ul .frames-group .frames-list {
  border-top: 1px solid var(--theme-splitter-color);
  border-bottom: 1px solid var(--theme-splitter-color);
}

.frames ul .frames-group.expanded .badge {
  color: var(--theme-highlight-blue);
}
.badge {
  --size: 17px;
  --radius: calc(var(--size) / 2);
  height: var(--size);
  min-width: var(--size);
  line-height: var(--size);
  background: var(--theme-toolbar-background-hover);
  color: var(--theme-body-color);
  border-radius: var(--radius);
  padding: 0 4px;
  font-size: 0.9em;
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

.why-paused {
  background-color: var(--theme-body-background);
  color: var(--theme-body-color);
  padding: 10px 10px 10px 20px;
  white-space: normal;
  opacity: 0.6;
  font-size: 12px;
  flex: 0 1 auto;
  text-align: center;
  font-style: italic;
  font-weight: 300;
  cursor: default;
}

.theme-dark .secondary-panes .why-paused {
  color: white;
}

.why-paused .message {
  font-size: 10px;
}

.why-paused .message.warning {
  font-size: 10px;
  color: var(--theme-graphs-red);
  font-weight: bold;
  font-style: normal;
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

.frames ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

.frames ul li {
  padding: 0 10px 0 21px;
  overflow: hidden;
  display: flex;
  justify-content: space-between;
  flex-direction: row;
  align-items: center;
  margin: 0;
}

.frames ul li * {
  -moz-user-select: none;
  user-select: none;
}

.frames .badge {
  flex-shrink: 0;
  margin-right: 4px;
}

.frames .location {
  font-weight: lighter;
  display: flex;
  justify-content: space-between;
  flex-direction: row;
  align-items: center;
  margin: 0;
  flex-shrink: 0;
}

.theme-light .frames .location,
.theme-firebug .frames .location {
  color: var(--theme-comment);
}

:root.theme-dark .frames .location {
  color: var(--theme-body-color);
  opacity: 0.6;
}

.frames .title {
  text-overflow: ellipsis;
  overflow: hidden;
  margin: 7px 0.5em 7px 0;
}

.frames ul li:hover,
.frames ul li:focus {
  background-color: var(--theme-toolbar-background-alt);
  outline: none;
}

.theme-dark .frames ul li:focus {
  background-color: var(--theme-tab-toolbar-background);
}

.frames ul li.selected {
  background-color: var(--theme-selection-background);
  color: white;
}

.frames ul li.selected i.annotation-logo svg path {
  fill: white;
}

:root.theme-light .frames ul li.selected .location,
:root.theme-firebug .frames ul li.selected .location,
:root.theme-dark .frames ul li.selected .location {
  color: white;
}

.show-more-container {
  display: flex;
}

.show-more {
  text-align: center;
  padding: 8px 0px;
  margin: 7px 10px 7px 7px;
  border: 1px solid var(--theme-splitter-color);
  background-color: var(--theme-tab-toolbar-background);
  width: 100%;
  font-size: inherit;
}

.show-more:hover {
  background-color: var(--theme-toolbar-background-hover);
}

.annotation-logo {
  width: 12px;
  margin-left: 3px;
  line-height: 8px;
}

:root.theme-dark .annotation-logo svg path {
  fill: var(--theme-highlight-blue);
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

.event-listeners {
  list-style: none;
  margin: 0;
  padding: 0;
}

.event-listeners .listener {
  padding: 7px 10px 7px 21px;
  clear: both;
  overflow: hidden;
}

.event-listeners .listener * {
  -moz-user-select: none;
  user-select: none;
}

.event-listeners .listener:nth-of-type(2n) {
  background-color: var(--theme-tab-toolbar-background);
}

.event-listeners .listener .type {
  color: var(--theme-highlight-bluegrey);
  padding-right: 5px;
}

.event-listeners .listener .selector {
  color: var(--theme-content-color2);
}

.event-listeners .listener-checkbox {
  margin-left: 0;
}

.event-listeners .listener .close-btn {
  float: right;
}

.event-listeners .listener .close {
  display: none;
}

.event-listeners .listener:hover .close {
  display: block;
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

.workers-list * {
  user-select: none;
}

.workers-list .worker {
  font-size: 1rem;
  color: var(--theme-content-color1);
  padding: 0 1em;
  line-height: 31px;
  position: relative;
  transition: all 0.25s ease;
  cursor: pointer;
}

.worker-list .worker:hover {
  background-color: var(--search-overlays-semitransparent);
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 {
  --accordion-header-background: var(--theme-toolbar-background);
  --disclosure-arrow: #b2b2b2;
}

:root.theme-dark {
  --accordion-header-background: #222225;
  --disclosure-arrow: #7f7f81;
}

.accordion {
  background-color: var(--theme-sidebar-background);
  width: 100%;
}

.accordion ._header {
  background-color: var(--accordion-header-background);
  border-bottom: 1px solid var(--theme-splitter-color);
  display: flex;
  font-size: 12px;
  padding: 4px;
  transition: all 0.25s ease;
  width: 100%;
  height: 24px;
  align-items: center;

  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  -o-user-select: none;
  user-select: none;
  cursor: default;
}

.accordion ._header:hover {
  background-color: var(--theme-toolbar-background-hover);
}

.accordion ._header button svg,
.accordion ._header:hover button svg {
  fill: currentColor;
  height: 16px;
}

.accordion ._content {
  border-bottom: 1px solid var(--theme-splitter-color);
  font-size: 12px;
}

.accordion div:last-child ._content {
  border-bottom: none;
}

.accordion ._header .header-buttons {
  display: flex;
  margin-left: auto;
  padding-right: 5px;
}

.accordion .header-buttons .add-button {
  font-size: 180%;
  text-align: center;
  line-height: 16px;
}

.accordion .header-buttons button {
  color: var(--theme-body-color);
  border: none;
  background: none;
  outline: 0;
  padding: 0;
  width: 16px;
  height: 16px;
}

.accordion .header-buttons button::-moz-focus-inner {
  border: none;
}

.accordion .arrow svg {
  fill: var(--disclosure-arrow);
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

.command-bar {
  flex: 0 0 30px;
  border-bottom: 1px solid var(--theme-splitter-color);
  display: flex;
  overflow: hidden;
  z-index: 1;
  background-color: var(--theme-toolbar-background);
}

html[dir="rtl"] .command-bar {
  border-right: 1px solid var(--theme-splitter-color);
}

.theme-dark .command-bar {
  background-color: var(--theme-toolbar-background);
}

img.pause,
img.stepOver,
img.stepIn,
img.stepOut,
img.resume,
img.rewind,
img.reverseStepOver,
img.reverseStepIn,
img.reverseStepOut,
img.replay-previous,
img.replay-next,
img.resume {
  background-color: var(--theme-body-color);
}

.command-bar img.pause {
  mask: url("chrome://devtools/skin/images/debugger/pause.svg") no-repeat;
}

.command-bar img.stepOver {
  mask: url("chrome://devtools/skin/images/debugger/stepOver.svg") no-repeat;
}

.command-bar img.stepIn {
  mask: url("chrome://devtools/skin/images/debugger/stepIn.svg") no-repeat;
}

.command-bar img.stepOut {
  mask: url("chrome://devtools/skin/images/debugger/stepOut.svg") no-repeat;
}

.command-bar img.resume {
  mask: url("chrome://devtools/skin/images/debugger/resume.svg") no-repeat;
}

.command-bar img.rewind {
  mask: url("chrome://devtools/skin/images/debugger/resume.svg") no-repeat;
  transform: scaleX(-1);
}

.command-bar img.reverseStepOver {
  mask: url("chrome://devtools/skin/images/debugger/stepOver.svg") no-repeat;
  transform: scaleX(-1);
}

.command-bar img.reverseStepIn {
  mask: url("chrome://devtools/skin/images/debugger/stepIn.svg") no-repeat;
  transform: scaleX(-1);
}

.command-bar img.reverseStepOut {
  mask: url("chrome://devtools/skin/images/debugger/stepOut.svg") no-repeat;
  transform: scaleX(-1);
}

.command-bar > .pause-exceptions.uncaught.enabled > img.pause-exceptions {
  background-color: var(--theme-highlight-purple);
}

.command-bar > .pause-exceptions.all.enabled > img.pause-exceptions {
  background-color: var(--theme-highlight-blue);
}

.command-bar > .pause-exceptions.enabled > img.pause-exceptions {
  background-color: var(--theme-body-color);
}

.command-bar img.pause-exceptions {
  mask: url("chrome://devtools/skin/images/debugger/pause-exceptions.svg") no-repeat;
  margin-inline-start: 0.2em;
}

.command-bar .filler {
  flex-grow: 1;
}

.command-bar img.replay-previous {
  mask: url("chrome://devtools/skin/images/debugger/back.svg") no-repeat;
  mask-size: 75%;
  margin-top: 5px;
}

.command-bar img.replay-next {
  mask: url("chrome://devtools/skin/images/debugger/forward.svg") no-repeat;
  mask-size: 75%;
  margin-top: 5px;
}

.command-bar .replay-inactive {
  opacity: 0.5;
}

.command-bar .step-position {
  color: var(--theme-comment-alt);
  padding-top: 8px;
  margin-inline-end: 4px;
}

.command-bar .replay-active {
  color: var(--theme-highlight-blue);
}

.command-bar .subSettings {
  float: right;
}

.bottom {
  border-bottom: none;
  background-color: var(--theme-body-background);
  border-top: 1px solid var(--theme-splitter-color);
  flex: 0 0 25px;
}

.command-bar.bottom {
  justify-content: flex-end;
}

.command-bar.bottom > button {
  color: var(--theme-comment);
  width: 26px;
}
.command-bar.bottom > button:hover {
  color: var(--theme-body-color);
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

.dropdown {
  --width: 150px;
  background: var(--theme-body-background);
  border: 1px solid var(--theme-splitter-color);
  box-shadow: 0 4px 4px 0 var(--search-overlays-semitransparent);
  max-height: 300px;
  position: absolute;
  right: 0;
  top: 23px;
  width: var(--width);
  z-index: 1000;
  overflow: auto;
}

html[dir="rtl"] .dropdown {
  right: calc((var(--width) - 11px) * (-1));
}

.dropdown-block {
  padding: 0px 2px;
  position: relative;
  align-self: center;
  height: 100%;
}

.dropdown-button {
  color: var(--theme-comment);
  background: none;
  border: none;
  padding: 0;
  font-weight: 100;
  font-size: 14px;
  height: 100%;
  width: 24px;
}

.dropdown li {
  transition: all 0.25s ease;
  padding: 2px 10px 10px 5px;
  overflow: hidden;
  height: 30px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.dropdown li:hover {
  background-color: var(--search-overlays-semitransparent);
}

.dropdown-icon {
  width: var(--icon-size);
  height: var(--icon-size);
  margin-right: 5px;
  vertical-align: middle;
  display: inline-block;
}

.dropdown-icon.prettyPrint {
  mask: url("chrome://devtools/skin/images/debugger/prettyPrint.svg") no-repeat;
  mask-size: 100%;
  background: var(--theme-highlight-blue);
}

.dropdown-icon.blackBox {
  mask: url("chrome://devtools/skin/images/debugger/blackBox.svg") no-repeat;
  mask-size: 100%;
  background: var(--theme-highlight-blue);
}

.dropdown-icon.file {
  mask: url("chrome://devtools/skin/images/debugger/file.svg") no-repeat;
  mask-size: 100%;
}

.dropdown ul {
  list-style: none;
  line-height: 2em;
  font-size: 1em;
  margin: 0;
  padding: 0;
}

.dropdown-mask {
  position: fixed;
  width: 100%;
  height: 100%;
  background: transparent;
  z-index: 999;
  left: 0;
  top: 0;
}
.dropdown span.icon-spacer {
  margin-left: 8px;
}

.dropdown li.first {
  border-bottom: 1px solid var(--theme-toolbar-background-hover);
}

img.pause-next,
img.pause-on-exceptions,
img.pause-uncaught-exceptions,
img.ignore-exceptions {
  vertical-align: middle;
  width: 16px;
  height: 20px;
  display: inline-block;
}

.dropdown div > img.pause-next {
  mask: url("chrome://devtools/skin/images/debugger/pause.svg") no-repeat;
  background-color: var(--theme-body-color);
}

.dropdown div > img.ignore-exceptions {
  mask: url("chrome://devtools/skin/images/debugger/pause-exceptions.svg") no-repeat;
  background-color: var(--theme-body-color);
}

.dropdown div > img.pause-uncaught-exceptions {
  mask: url("chrome://devtools/skin/images/debugger/pause-exceptions.svg") no-repeat;
  background-color: var(--theme-highlight-purple);
}

.dropdown div > img.pause-on-exceptions {
  mask: url("chrome://devtools/skin/images/debugger/pause-exceptions.svg") no-repeat;
  background-color: var(--theme-highlight-blue);
}

.dropdown div.pause-next.active,
.dropdown div.pause-on-exceptions.active,
.dropdown div.pause-uncaught-exceptions.active,
.dropdown div.ignore-exceptions.active > span.icon-spacer {
  color: var(--theme-highlight-blue);
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

.object-node.default-property {
  opacity: 0.6;
}

.object-label {
  color: var(--theme-highlight-blue);
}

.objectBox-object,
.objectBox-string,
.objectBox-text,
.objectBox-table,
.objectLink-textNode,
.objectLink-event,
.objectLink-eventLog,
.objectLink-regexp,
.objectLink-object,
.objectLink-Date,
.theme-dark .objectBox-object,
.theme-light .objectBox-object {
  white-space: nowrap;
}

.scopes-pane {
  overflow: auto;
}

.scopes-list {
  padding-inline-start: 4px;
}

.scopes-list .tree:focus {
  outline: none;
}

.scopes-list .function-signature {
  display: inline-block;
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

.secondary-panes {
  overflow: auto;
  display: flex;
  flex-direction: column;
  flex: 1;
  white-space: nowrap;
  --breakpoint-expression-right-clear-space: 36px;
}

/*
  We apply overflow to the container with the commandbar.
  This allows the commandbar to remain fixed when scrolling
  until the content completely ends. Not just the height of
  the wrapper.
  Ref: https://github.com/devtools-html/debugger.html/issues/3426
*/

.secondary-panes-wrapper {
  height: 100%;
  width: 100%;
  display: flex;
  flex-direction: column;
}

.secondary-panes .accordion {
  flex: 1 0 auto;
}

.pane {
  color: var(--theme-body-color);
}

.pane .pane-info {
  font-style: italic;
  text-align: center;
  padding: 0.5em;
  -moz-user-select: none;
  user-select: none;
  cursor: default;
}

.secondary-panes .breakpoints-buttons {
  display: flex;
}

.secondary-panes .accordion .plus svg {
  width: 12px;
  margin-top: 3px;
  fill: var(--theme-comment-alt);
}

.secondary-panes .accordion .plus.active svg {
  fill: var(--theme-highlight-blue);
}

.dropdown {
  width: 20em;
  overflow: auto;
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

.welcomebox {
  width: calc(100% - 1px);

  /* Offsetting it by 30px for the sources-header area */
  height: calc(100% - 30px);
  position: absolute;
  top: 30px;
  left: 0;
  padding: 50px 0 0 0;
  text-align: center;
  font-size: 1.25em;
  background-color: var(--theme-toolbar-background);
  font-weight: lighter;
  z-index: 10;
  user-select: none;
}

.theme-dark .welcomebox {
  background-color: var(--theme-body-background);
}

.welcomebox .command-bar-button {
  position: absolute;
  top: auto;
  offset-inline-end: 0;
  offset-inline-start: auto;
  bottom: 0;
}

.alignlabel {
  display: flex;
  white-space: nowrap;
}

.shortcutKeys {
  font-family: Courier;
}

.shortcutKey,
.shortcutLabel {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
}

.shortcutKey {
  text-align: right;
  padding-right: 10px;
  font-family: Courier;
}

.shortcutLabel {
  text-align: left;
  padding-left: 10px;
}

.shortcutFunction {
  flex: 1;
  color: var(--theme-comment);
}

.shortcutFunction p {
  display: flex;
}

html .welcomebox .toggle-button-end.collapsed {
  bottom: 1px;
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

.source-header {
  border-bottom: 1px solid var(--theme-splitter-color);
  width: 100%;
  height: 30px;
  display: flex;
}

.source-header * {
  -moz-user-select: none;
  user-select: none;
}

.source-header .new-tab-btn {
  padding: 4px;
  margin-top: 4px;
  margin-left: 2px;
  fill: var(--theme-comment);
  transition: 0.1s ease;
  align-self: center;
}

.source-header .new-tab-btn:hover {
  background-color: var(--theme-toolbar-background-hover);
}

.source-header .new-tab-btn svg {
  width: 12px;
  display: block;
}

.source-tabs {
  max-width: calc(100% - 80px);
  align-self: flex-start;
}

.source-tab {
  border: 1px solid transparent;
  border-top-left-radius: 2px;
  border-top-right-radius: 2px;
  display: inline-flex;
  align-items: center;
  position: relative;
  transition: all 0.15s ease;
  min-width: 40px;
  overflow: hidden;
  padding: 5px;
  margin-inline-start: 3px;
  margin-top: 3px;
  cursor: default;
}

.source-tab:first-child {
  margin-inline-start: 0;
}

.source-tab:hover {
  background-color: var(--theme-toolbar-background-alt);
  border-color: var(--theme-splitter-color);
}

.source-tab.active {
  color: var(--theme-body-color);
  background-color: var(--theme-body-background);
  border-color: var(--theme-splitter-color);
  border-bottom-color: transparent;
}

.source-tab.active path,
.source-tab:hover path {
  fill: var(--theme-body-color);
}

.source-tab img.prettyPrint {
  mask: url("chrome://devtools/skin/images/debugger/prettyPrint.svg") no-repeat;
  mask-size: 100%;
  padding-top: 12px;
  height: 12px;
  width: 12px;
  background: var(--theme-highlight-blue);
}

.source-tab .prettyPrint path {
  fill: var(--theme-textbox-box-shadow);
}

.source-tab .blackBox,
.source-tab .prettyPrint {
  align-self: center;
}

.source-tab img.blackBox {
  mask: url("chrome://devtools/skin/images/debugger/blackBox.svg") no-repeat;
  mask-size: 100%;
  padding-top: 12px;
  height: 12px;
  width: 12px;
  background: var(--theme-highlight-blue);
}

.source-tab img.react {
  mask: url("chrome://devtools/skin/images/debugger/react.svg") no-repeat;
  mask-size: 100%;
  padding-top: 12px;
  height: 14px;
  width: 14px;
  background: var(--theme-highlight-bluegrey);
}

.source-tab .blackBox path {
  fill: var(--theme-textbox-box-shadow);
}

.theme-dark .source-tab .blackBox circle {
  fill: var(--theme-body-color);
}

.source-tab .filename {
  white-space: nowrap;
  text-overflow: ellipsis;
  overflow: hidden;
  padding: 0 4px;
  align-self: flex-start;
}

.source-tab .close-btn {
  visibility: hidden;
  line-height: 0;
}

.source-tab.active .close-btn {
  visibility: visible;
}

.source-tab:hover .close-btn {
  visibility: visible;
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

.result-list {
  list-style: none;
  margin: 0px;
  padding: 0px;
  overflow: auto;
  width: calc(100% - 1px); /* 1px fixes the hidden right border */
  background: var(--theme-body-background);
}

.result-list * {
  -moz-user-select: none;
  user-select: none;
}

.result-list li {
  color: var(--theme-body-color);
  padding: 4px 13px;
  display: flex;
  justify-content: space-between;
}

.result-list.big li {
  padding: 10px;
  flex-direction: column;
  border-bottom: 1px solid var(--theme-splitter-color);
}

.result-list li:hover {
  background: var(--theme-tab-toolbar-background);
}

.result-list li.selected {
  background: var(--accordion-header-background);
}

.result-list.small li.selected {
  background-color: var(--theme-selection-background);
  color: white;
}

.theme-dark .result-list.small li.selected {
  background-color: var(--theme-body-background);
}

.theme-dark .result-list li:hover {
  background: var(--grey-70);
}

.theme-dark .result-list li.selected {
  background: var(--grey-70);
}

.result-list li .title {
  line-height: 1.5em;
  word-break: break-all;
}

.result-list li.selected .title {
  color: white;
}

.result-list.big li.selected .title {
  color: var(--theme-body-color);
}

.result-list.big li .subtitle {
  word-break: break-all;
  color: var(--theme-body-color-inactive);
}

.result-list.big li .subtitle {
  line-height: 1.5em;
}

.theme-dark .result-list.big li .subtitle {
  color: var(--theme-comment-alt);
}

.search-bar .result-list li.selected .subtitle {
  color: white;
}

.search-bar .result-list {
  border-bottom: 1px solid var(--theme-splitter-color);
}

.theme-dark .result-list {
  background-color: var(--theme-body-background);
}
.result-item .title .highlight {
  font-weight: bold;
}

.result-item .subtitle .highlight {
  color: var(--grey-90);
  font-weight: 500;
}

.theme-dark .result-item .title .highlight,
.theme-dark .result-item .subtitle .highlight {
  color: white;
}
PK
!<��>��6�6@chrome/devtools/modules/devtools/client/debugger/new/debugger.js(function webpackUniversalModuleDefinition(root, factory) {
	if(typeof exports === 'object' && typeof module === 'object')
		module.exports = factory(require("devtools/client/shared/vendor/react"), require("devtools/client/shared/vendor/lodash"), require("devtools/client/shared/vendor/react-dom"), require("Services"), require("devtools/shared/flags"), require("devtools/client/sourceeditor/editor"), require("devtools/client/shared/vendor/WasmParser"), require("devtools/client/shared/vendor/WasmDis"));
	else if(typeof define === 'function' && define.amd)
		define(["devtools/client/shared/vendor/react", "devtools/client/shared/vendor/lodash", "devtools/client/shared/vendor/react-dom", "Services", "devtools/shared/flags", "devtools/client/sourceeditor/editor", "devtools/client/shared/vendor/WasmParser", "devtools/client/shared/vendor/WasmDis"], factory);
	else {
		var a = typeof exports === 'object' ? factory(require("devtools/client/shared/vendor/react"), require("devtools/client/shared/vendor/lodash"), require("devtools/client/shared/vendor/react-dom"), require("Services"), require("devtools/shared/flags"), require("devtools/client/sourceeditor/editor"), require("devtools/client/shared/vendor/WasmParser"), require("devtools/client/shared/vendor/WasmDis")) : factory(root["devtools/client/shared/vendor/react"], root["devtools/client/shared/vendor/lodash"], root["devtools/client/shared/vendor/react-dom"], root["Services"], root["devtools/shared/flags"], root["devtools/client/sourceeditor/editor"], root["devtools/client/shared/vendor/WasmParser"], root["devtools/client/shared/vendor/WasmDis"]);
		for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
	}
})(typeof self !== 'undefined' ? self : this, function(__WEBPACK_EXTERNAL_MODULE_0__, __WEBPACK_EXTERNAL_MODULE_2__, __WEBPACK_EXTERNAL_MODULE_4__, __WEBPACK_EXTERNAL_MODULE_22__, __WEBPACK_EXTERNAL_MODULE_52__, __WEBPACK_EXTERNAL_MODULE_197__, __WEBPACK_EXTERNAL_MODULE_677__, __WEBPACK_EXTERNAL_MODULE_678__) {
return /******/ (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, {
/******/ 				configurable: false,
/******/ 				enumerable: true,
/******/ 				get: getter
/******/ 			});
/******/ 		}
/******/ 	};
/******/
/******/ 	// 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 = "/assets/build";
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(__webpack_require__.s = 46);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports) {

module.exports = __WEBPACK_EXTERNAL_MODULE_0__;

/***/ }),
/* 1 */,
/* 2 */
/***/ (function(module, exports) {

module.exports = __WEBPACK_EXTERNAL_MODULE_2__;

/***/ }),
/* 3 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__createStore__ = __webpack_require__(32);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__combineReducers__ = __webpack_require__(171);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__bindActionCreators__ = __webpack_require__(173);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__applyMiddleware__ = __webpack_require__(174);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__compose__ = __webpack_require__(36);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__utils_warning__ = __webpack_require__(35);
/* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "createStore", function() { return __WEBPACK_IMPORTED_MODULE_0__createStore__["b"]; });
/* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "combineReducers", function() { return __WEBPACK_IMPORTED_MODULE_1__combineReducers__["a"]; });
/* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "bindActionCreators", function() { return __WEBPACK_IMPORTED_MODULE_2__bindActionCreators__["a"]; });
/* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "applyMiddleware", function() { return __WEBPACK_IMPORTED_MODULE_3__applyMiddleware__["a"]; });
/* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "compose", function() { return __WEBPACK_IMPORTED_MODULE_4__compose__["a"]; });







/*
* 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 (false) {
  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 DefinePlugin for webpack (http://stackoverflow.com/questions/30030031) ' + 'to ensure you have the correct code for your production build.');
}



/***/ }),
/* 4 */
/***/ (function(module, exports) {

module.exports = __WEBPACK_EXTERNAL_MODULE_4__;

/***/ }),
/* 5 */,
/* 6 */
/***/ (function(module, exports, __webpack_require__) {

var Symbol = __webpack_require__(7),
    getRawTag = __webpack_require__(10),
    objectToString = __webpack_require__(11);

/** `Object#toString` result references. */
var nullTag = '[object Null]',
    undefinedTag = '[object Undefined]';

/** Built-in value references. */
var symToStringTag = Symbol ? Symbol.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 && symToStringTag in Object(value))
    ? getRawTag(value)
    : objectToString(value);
}

module.exports = baseGetTag;


/***/ }),
/* 7 */
/***/ (function(module, exports, __webpack_require__) {

var root = __webpack_require__(8);

/** Built-in value references. */
var Symbol = root.Symbol;

module.exports = Symbol;


/***/ }),
/* 8 */
/***/ (function(module, exports, __webpack_require__) {

var freeGlobal = __webpack_require__(9);

/** 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')();

module.exports = root;


/***/ }),
/* 9 */
/***/ (function(module, exports, __webpack_require__) {

/* WEBPACK VAR INJECTION */(function(global) {/** Detect free variable `global` from Node.js. */
var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;

module.exports = freeGlobal;

/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(792)))

/***/ }),
/* 10 */
/***/ (function(module, exports, __webpack_require__) {

var Symbol = __webpack_require__(7);

/** Used for built-in method references. */
var objectProto = Object.prototype;

/** 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 nativeObjectToString = objectProto.toString;

/** Built-in value references. */
var symToStringTag = Symbol ? Symbol.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.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;
}

module.exports = getRawTag;


/***/ }),
/* 11 */
/***/ (function(module, exports) {

/** Used for built-in method references. */
var objectProto = Object.prototype;

/**
 * Used to resolve the
 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
 * of values.
 */
var nativeObjectToString = objectProto.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.call(value);
}

module.exports = objectToString;


/***/ }),
/* 12 */,
/* 13 */,
/* 14 */
/***/ (function(module, exports) {

/**
 * 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';
}

module.exports = isObjectLike;


/***/ }),
/* 15 */,
/* 16 */,
/* 17 */,
/* 18 */,
/* 19 */,
/* 20 */
/***/ (function(module, exports, __webpack_require__) {

/**
 * 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 (false) {
  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 = require('./factoryWithTypeCheckers')(isValidElement, throwOnDirectAccess);
} else {
  // By explicitly using `prop-types` you are opting into new production behavior.
  // http://fb.me/prop-types-in-prod
  module.exports = __webpack_require__(177)();
}


/***/ }),
/* 21 */,
/* 22 */
/***/ (function(module, exports) {

module.exports = __WEBPACK_EXTERNAL_MODULE_22__;

/***/ }),
/* 23 */,
/* 24 */,
/* 25 */,
/* 26 */,
/* 27 */,
/* 28 */,
/* 29 */,
/* 30 */,
/* 31 */,
/* 32 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return ActionTypes; });
/* harmony export (immutable) */ __webpack_exports__["b"] = createStore;
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_lodash_es_isPlainObject__ = __webpack_require__(33);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_symbol_observable__ = __webpack_require__(2287);



/**
 * 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 ActionTypes = {
  INIT: '@@redux/INIT'

  /**
   * 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 === '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;

  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() {
    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 listener to be a function.');
    }

    var isSubscribed = true;

    ensureCanMutateNextListeners();
    nextListeners.push(listener);

    return function unsubscribe() {
      if (!isSubscribed) {
        return;
      }

      isSubscribed = false;

      ensureCanMutateNextListeners();
      var index = nextListeners.indexOf(listener);
      nextListeners.splice(index, 1);
    };
  }

  /**
   * 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 (!Object(__WEBPACK_IMPORTED_MODULE_0_lodash_es_isPlainObject__["a" /* default */])(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;
    dispatch({ type: ActionTypes.INIT });
  }

  /**
   * 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') {
          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[__WEBPACK_IMPORTED_MODULE_1_symbol_observable__["a" /* default */]] = 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[__WEBPACK_IMPORTED_MODULE_1_symbol_observable__["a" /* default */]] = observable, _ref2;
}

/***/ }),
/* 33 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__baseGetTag_js__ = __webpack_require__(123);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__getPrototype_js__ = __webpack_require__(129);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__isObjectLike_js__ = __webpack_require__(139);




/** `Object#toString` result references. */
var objectTag = '[object Object]';

/** Used for built-in method references. */
var funcProto = Function.prototype,
    objectProto = Object.prototype;

/** 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 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 (!Object(__WEBPACK_IMPORTED_MODULE_2__isObjectLike_js__["a" /* default */])(value) || Object(__WEBPACK_IMPORTED_MODULE_0__baseGetTag_js__["a" /* default */])(value) != objectTag) {
    return false;
  }
  var proto = Object(__WEBPACK_IMPORTED_MODULE_1__getPrototype_js__["a" /* default */])(value);
  if (proto === null) {
    return true;
  }
  var Ctor = hasOwnProperty.call(proto, 'constructor') && proto.constructor;
  return typeof Ctor == 'function' && Ctor instanceof Ctor &&
    funcToString.call(Ctor) == objectCtorString;
}

/* harmony default export */ __webpack_exports__["a"] = (isPlainObject);


/***/ }),
/* 34 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__root_js__ = __webpack_require__(124);


/** Built-in value references. */
var Symbol = __WEBPACK_IMPORTED_MODULE_0__root_js__["a" /* default */].Symbol;

/* harmony default export */ __webpack_exports__["a"] = (Symbol);


/***/ }),
/* 35 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* unused harmony export default */
/**
 * 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);
    /* eslint-disable no-empty */
  } catch (e) {}
  /* eslint-enable no-empty */
}

/***/ }),
/* 36 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony export (immutable) */ __webpack_exports__["a"] = compose;
/**
 * 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 = 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(undefined, arguments));
    };
  });
}

/***/ }),
/* 37 */,
/* 38 */,
/* 39 */,
/* 40 */,
/* 41 */,
/* 42 */,
/* 43 */,
/* 44 */,
/* 45 */,
/* 46 */
/***/ (function(module, exports, __webpack_require__) {

module.exports = __webpack_require__(1459);


/***/ }),
/* 47 */,
/* 48 */,
/* 49 */,
/* 50 */,
/* 51 */,
/* 52 */
/***/ (function(module, exports) {

module.exports = __WEBPACK_EXTERNAL_MODULE_52__;

/***/ }),
/* 53 */,
/* 54 */,
/* 55 */,
/* 56 */,
/* 57 */,
/* 58 */,
/* 59 */,
/* 60 */,
/* 61 */,
/* 62 */,
/* 63 */,
/* 64 */,
/* 65 */,
/* 66 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.decode = exports.parse = __webpack_require__(121);
exports.encode = exports.stringify = __webpack_require__(122);


/***/ }),
/* 67 */
/***/ (function(module, exports, __webpack_require__) {

var baseGet = __webpack_require__(68);

/**
 * 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;
}

module.exports = get;


/***/ }),
/* 68 */
/***/ (function(module, exports, __webpack_require__) {

var castPath = __webpack_require__(69),
    toKey = __webpack_require__(111);

/**
 * 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 = castPath(path, object);

  var index = 0,
      length = path.length;

  while (object != null && index < length) {
    object = object[toKey(path[index++])];
  }
  return (index && index == length) ? object : undefined;
}

module.exports = baseGet;


/***/ }),
/* 69 */
/***/ (function(module, exports, __webpack_require__) {

var isArray = __webpack_require__(70),
    isKey = __webpack_require__(71),
    stringToPath = __webpack_require__(73),
    toString = __webpack_require__(108);

/**
 * Casts `value` to a path array if it's not one.
 *
 * @private
 * @param {*} value The value to inspect.
 * @param {Object} [object] The object to query keys on.
 * @returns {Array} Returns the cast property path array.
 */
function castPath(value, object) {
  if (isArray(value)) {
    return value;
  }
  return isKey(value, object) ? [value] : stringToPath(toString(value));
}

module.exports = castPath;


/***/ }),
/* 70 */
/***/ (function(module, exports) {

/**
 * 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;

module.exports = isArray;


/***/ }),
/* 71 */
/***/ (function(module, exports, __webpack_require__) {

var isArray = __webpack_require__(70),
    isSymbol = __webpack_require__(72);

/** Used to match property names within property paths. */
var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,
    reIsPlainProp = /^\w*$/;

/**
 * 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));
}

module.exports = isKey;


/***/ }),
/* 72 */
/***/ (function(module, exports, __webpack_require__) {

var baseGetTag = __webpack_require__(6),
    isObjectLike = __webpack_require__(14);

/** `Object#toString` result references. */
var symbolTag = '[object Symbol]';

/**
 * 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) && baseGetTag(value) == symbolTag);
}

module.exports = isSymbol;


/***/ }),
/* 73 */
/***/ (function(module, exports, __webpack_require__) {

var memoizeCapped = __webpack_require__(74);

/** Used to match property names within property paths. */
var rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g;

/** Used to match backslashes in property paths. */
var reEscapeChar = /\\(\\)?/g;

/**
 * Converts `string` to a property path array.
 *
 * @private
 * @param {string} string The string to convert.
 * @returns {Array} Returns the property path array.
 */
var stringToPath = memoizeCapped(function(string) {
  var result = [];
  if (string.charCodeAt(0) === 46 /* . */) {
    result.push('');
  }
  string.replace(rePropName, function(match, number, quote, subString) {
    result.push(quote ? subString.replace(reEscapeChar, '$1') : (number || match));
  });
  return result;
});

module.exports = stringToPath;


/***/ }),
/* 74 */
/***/ (function(module, exports, __webpack_require__) {

var memoize = __webpack_require__(75);

/** Used as the maximum memoize cache size. */
var MAX_MEMOIZE_SIZE = 500;

/**
 * A specialized version of `_.memoize` which clears the memoized function's
 * cache when it exceeds `MAX_MEMOIZE_SIZE`.
 *
 * @private
 * @param {Function} func The function to have its output memoized.
 * @returns {Function} Returns the new memoized function.
 */
function memoizeCapped(func) {
  var result = memoize(func, function(key) {
    if (cache.size === MAX_MEMOIZE_SIZE) {
      cache.clear();
    }
    return key;
  });

  var cache = result.cache;
  return result;
}

module.exports = memoizeCapped;


/***/ }),
/* 75 */
/***/ (function(module, exports, __webpack_require__) {

var MapCache = __webpack_require__(76);

/** Error message constants. */
var FUNC_ERROR_TEXT = 'Expected a function';

/**
 * 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 `clear`, `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 != null && 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) || cache;
    return result;
  };
  memoized.cache = new (memoize.Cache || MapCache);
  return memoized;
}

// Expose `MapCache`.
memoize.Cache = MapCache;

module.exports = memoize;


/***/ }),
/* 76 */
/***/ (function(module, exports, __webpack_require__) {

var mapCacheClear = __webpack_require__(77),
    mapCacheDelete = __webpack_require__(102),
    mapCacheGet = __webpack_require__(105),
    mapCacheHas = __webpack_require__(106),
    mapCacheSet = __webpack_require__(107);

/**
 * 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 == null ? 0 : entries.length;

  this.clear();
  while (++index < length) {
    var entry = entries[index];
    this.set(entry[0], entry[1]);
  }
}

// Add methods to `MapCache`.
MapCache.prototype.clear = mapCacheClear;
MapCache.prototype['delete'] = mapCacheDelete;
MapCache.prototype.get = mapCacheGet;
MapCache.prototype.has = mapCacheHas;
MapCache.prototype.set = mapCacheSet;

module.exports = MapCache;


/***/ }),
/* 77 */
/***/ (function(module, exports, __webpack_require__) {

var Hash = __webpack_require__(78),
    ListCache = __webpack_require__(93),
    Map = __webpack_require__(101);

/**
 * Removes all key-value entries from the map.
 *
 * @private
 * @name clear
 * @memberOf MapCache
 */
function mapCacheClear() {
  this.size = 0;
  this.__data__ = {
    'hash': new Hash,
    'map': new (Map || ListCache),
    'string': new Hash
  };
}

module.exports = mapCacheClear;


/***/ }),
/* 78 */
/***/ (function(module, exports, __webpack_require__) {

var hashClear = __webpack_require__(79),
    hashDelete = __webpack_require__(89),
    hashGet = __webpack_require__(90),
    hashHas = __webpack_require__(91),
    hashSet = __webpack_require__(92);

/**
 * Creates a hash object.
 *
 * @private
 * @constructor
 * @param {Array} [entries] The key-value pairs to cache.
 */
function Hash(entries) {
  var index = -1,
      length = entries == null ? 0 : entries.length;

  this.clear();
  while (++index < length) {
    var entry = entries[index];
    this.set(entry[0], entry[1]);
  }
}

// Add methods to `Hash`.
Hash.prototype.clear = hashClear;
Hash.prototype['delete'] = hashDelete;
Hash.prototype.get = hashGet;
Hash.prototype.has = hashHas;
Hash.prototype.set = hashSet;

module.exports = Hash;


/***/ }),
/* 79 */
/***/ (function(module, exports, __webpack_require__) {

var nativeCreate = __webpack_require__(80);

/**
 * Removes all key-value entries from the hash.
 *
 * @private
 * @name clear
 * @memberOf Hash
 */
function hashClear() {
  this.__data__ = nativeCreate ? nativeCreate(null) : {};
  this.size = 0;
}

module.exports = hashClear;


/***/ }),
/* 80 */
/***/ (function(module, exports, __webpack_require__) {

var getNative = __webpack_require__(81);

/* Built-in method references that are verified to be native. */
var nativeCreate = getNative(Object, 'create');

module.exports = nativeCreate;


/***/ }),
/* 81 */
/***/ (function(module, exports, __webpack_require__) {

var baseIsNative = __webpack_require__(82),
    getValue = __webpack_require__(88);

/**
 * 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;
}

module.exports = getNative;


/***/ }),
/* 82 */
/***/ (function(module, exports, __webpack_require__) {

var isFunction = __webpack_require__(83),
    isMasked = __webpack_require__(85),
    isObject = __webpack_require__(84),
    toSource = __webpack_require__(87);

/**
 * Used to match `RegExp`
 * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).
 */
var reRegExpChar = /[\\^$.*+?()[\]{}|]/g;

/** Used to detect host constructors (Safari). */
var reIsHostCtor = /^\[object .+?Constructor\]$/;

/** Used for built-in method references. */
var funcProto = Function.prototype,
    objectProto = Object.prototype;

/** 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 detect if a method is native. */
var reIsNative = RegExp('^' +
  funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&')
  .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
);

/**
 * 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) ? reIsNative : reIsHostCtor;
  return pattern.test(toSource(value));
}

module.exports = baseIsNative;


/***/ }),
/* 83 */
/***/ (function(module, exports, __webpack_require__) {

var baseGetTag = __webpack_require__(6),
    isObject = __webpack_require__(84);

/** `Object#toString` result references. */
var asyncTag = '[object AsyncFunction]',
    funcTag = '[object Function]',
    genTag = '[object GeneratorFunction]',
    proxyTag = '[object Proxy]';

/**
 * 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) {
  if (!isObject(value)) {
    return false;
  }
  // The use of `Object#toString` avoids issues with the `typeof` operator
  // in Safari 9 which returns 'object' for typed arrays and other constructors.
  var tag = baseGetTag(value);
  return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag;
}

module.exports = isFunction;


/***/ }),
/* 84 */
/***/ (function(module, exports) {

/**
 * 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 != null && (type == 'object' || type == 'function');
}

module.exports = isObject;


/***/ }),
/* 85 */
/***/ (function(module, exports, __webpack_require__) {

var coreJsData = __webpack_require__(86);

/** 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) : '';
}());

/**
 * 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);
}

module.exports = isMasked;


/***/ }),
/* 86 */
/***/ (function(module, exports, __webpack_require__) {

var root = __webpack_require__(8);

/** Used to detect overreaching core-js shims. */
var coreJsData = root['__core-js_shared__'];

module.exports = coreJsData;


/***/ }),
/* 87 */
/***/ (function(module, exports) {

/** Used for built-in method references. */
var funcProto = Function.prototype;

/** Used to resolve the decompiled source of functions. */
var funcToString = funcProto.toString;

/**
 * Converts `func` to its source code.
 *
 * @private
 * @param {Function} func The function to convert.
 * @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 '';
}

module.exports = toSource;


/***/ }),
/* 88 */
/***/ (function(module, exports) {

/**
 * 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];
}

module.exports = getValue;


/***/ }),
/* 89 */
/***/ (function(module, exports) {

/**
 * 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) {
  var result = this.has(key) && delete this.__data__[key];
  this.size -= result ? 1 : 0;
  return result;
}

module.exports = hashDelete;


/***/ }),
/* 90 */
/***/ (function(module, exports, __webpack_require__) {

var nativeCreate = __webpack_require__(80);

/** Used to stand-in for `undefined` hash values. */
var HASH_UNDEFINED = '__lodash_hash_undefined__';

/** Used for built-in method references. */
var objectProto = Object.prototype;

/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;

/**
 * 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;
}

module.exports = hashGet;


/***/ }),
/* 91 */
/***/ (function(module, exports, __webpack_require__) {

var nativeCreate = __webpack_require__(80);

/** Used for built-in method references. */
var objectProto = Object.prototype;

/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;

/**
 * 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);
}

module.exports = hashHas;


/***/ }),
/* 92 */
/***/ (function(module, exports, __webpack_require__) {

var nativeCreate = __webpack_require__(80);

/** Used to stand-in for `undefined` hash values. */
var HASH_UNDEFINED = '__lodash_hash_undefined__';

/**
 * 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__;
  this.size += this.has(key) ? 0 : 1;
  data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value;
  return this;
}

module.exports = hashSet;


/***/ }),
/* 93 */
/***/ (function(module, exports, __webpack_require__) {

var listCacheClear = __webpack_require__(94),
    listCacheDelete = __webpack_require__(95),
    listCacheGet = __webpack_require__(98),
    listCacheHas = __webpack_require__(99),
    listCacheSet = __webpack_require__(100);

/**
 * Creates an list cache object.
 *
 * @private
 * @constructor
 * @param {Array} [entries] The key-value pairs to cache.
 */
function ListCache(entries) {
  var index = -1,
      length = entries == null ? 0 : entries.length;

  this.clear();
  while (++index < length) {
    var entry = entries[index];
    this.set(entry[0], entry[1]);
  }
}

// Add methods to `ListCache`.
ListCache.prototype.clear = listCacheClear;
ListCache.prototype['delete'] = listCacheDelete;
ListCache.prototype.get = listCacheGet;
ListCache.prototype.has = listCacheHas;
ListCache.prototype.set = listCacheSet;

module.exports = ListCache;


/***/ }),
/* 94 */
/***/ (function(module, exports) {

/**
 * Removes all key-value entries from the list cache.
 *
 * @private
 * @name clear
 * @memberOf ListCache
 */
function listCacheClear() {
  this.__data__ = [];
  this.size = 0;
}

module.exports = listCacheClear;


/***/ }),
/* 95 */
/***/ (function(module, exports, __webpack_require__) {

var assocIndexOf = __webpack_require__(96);

/** Used for built-in method references. */
var arrayProto = Array.prototype;

/** Built-in value references. */
var splice = arrayProto.splice;

/**
 * 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);
  }
  --this.size;
  return true;
}

module.exports = listCacheDelete;


/***/ }),
/* 96 */
/***/ (function(module, exports, __webpack_require__) {

var eq = __webpack_require__(97);

/**
 * 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;
}

module.exports = assocIndexOf;


/***/ }),
/* 97 */
/***/ (function(module, exports) {

/**
 * 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);
}

module.exports = eq;


/***/ }),
/* 98 */
/***/ (function(module, exports, __webpack_require__) {

var assocIndexOf = __webpack_require__(96);

/**
 * 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];
}

module.exports = listCacheGet;


/***/ }),
/* 99 */
/***/ (function(module, exports, __webpack_require__) {

var assocIndexOf = __webpack_require__(96);

/**
 * 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;
}

module.exports = listCacheHas;


/***/ }),
/* 100 */
/***/ (function(module, exports, __webpack_require__) {

var assocIndexOf = __webpack_require__(96);

/**
 * 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) {
    ++this.size;
    data.push([key, value]);
  } else {
    data[index][1] = value;
  }
  return this;
}

module.exports = listCacheSet;


/***/ }),
/* 101 */
/***/ (function(module, exports, __webpack_require__) {

var getNative = __webpack_require__(81),
    root = __webpack_require__(8);

/* Built-in method references that are verified to be native. */
var Map = getNative(root, 'Map');

module.exports = Map;


/***/ }),
/* 102 */
/***/ (function(module, exports, __webpack_require__) {

var getMapData = __webpack_require__(103);

/**
 * 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) {
  var result = getMapData(this, key)['delete'](key);
  this.size -= result ? 1 : 0;
  return result;
}

module.exports = mapCacheDelete;


/***/ }),
/* 103 */
/***/ (function(module, exports, __webpack_require__) {

var isKeyable = __webpack_require__(104);

/**
 * 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;
}

module.exports = getMapData;


/***/ }),
/* 104 */
/***/ (function(module, exports) {

/**
 * 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);
}

module.exports = isKeyable;


/***/ }),
/* 105 */
/***/ (function(module, exports, __webpack_require__) {

var getMapData = __webpack_require__(103);

/**
 * 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);
}

module.exports = mapCacheGet;


/***/ }),
/* 106 */
/***/ (function(module, exports, __webpack_require__) {

var getMapData = __webpack_require__(103);

/**
 * 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);
}

module.exports = mapCacheHas;


/***/ }),
/* 107 */
/***/ (function(module, exports, __webpack_require__) {

var getMapData = __webpack_require__(103);

/**
 * 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) {
  var data = getMapData(this, key),
      size = data.size;

  data.set(key, value);
  this.size += data.size == size ? 0 : 1;
  return this;
}

module.exports = mapCacheSet;


/***/ }),
/* 108 */
/***/ (function(module, exports, __webpack_require__) {

var baseToString = __webpack_require__(109);

/**
 * 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 convert.
 * @returns {string} Returns the converted string.
 * @example
 *
 * _.toString(null);
 * // => ''
 *
 * _.toString(-0);
 * // => '-0'
 *
 * _.toString([1, 2, 3]);
 * // => '1,2,3'
 */
function toString(value) {
  return value == null ? '' : baseToString(value);
}

module.exports = toString;


/***/ }),
/* 109 */
/***/ (function(module, exports, __webpack_require__) {

var Symbol = __webpack_require__(7),
    arrayMap = __webpack_require__(110),
    isArray = __webpack_require__(70),
    isSymbol = __webpack_require__(72);

/** Used as references for various `Number` constants. */
var INFINITY = 1 / 0;

/** Used to convert symbols to primitives and strings. */
var symbolProto = Symbol ? Symbol.prototype : undefined,
    symbolToString = symbolProto ? symbolProto.toString : undefined;

/**
 * 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 (isArray(value)) {
    // Recursively convert values (susceptible to call stack limits).
    return arrayMap(value, baseToString) + '';
  }
  if (isSymbol(value)) {
    return symbolToString ? symbolToString.call(value) : '';
  }
  var result = (value + '');
  return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
}

module.exports = baseToString;


/***/ }),
/* 110 */
/***/ (function(module, exports) {

/**
 * 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 == null ? 0 : array.length,
      result = Array(length);

  while (++index < length) {
    result[index] = iteratee(array[index], index, array);
  }
  return result;
}

module.exports = arrayMap;


/***/ }),
/* 111 */
/***/ (function(module, exports, __webpack_require__) {

var isSymbol = __webpack_require__(72);

/** Used as references for various `Number` constants. */
var INFINITY = 1 / 0;

/**
 * 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;
}

module.exports = toKey;


/***/ }),
/* 112 */
/***/ (function(module, exports, __webpack_require__) {

var baseSet = __webpack_require__(113);

/**
 * Sets the value at `path` of `object`. If a portion of `path` doesn't exist,
 * it's created. Arrays are created for missing index properties while objects
 * are created for all other missing properties. Use `_.setWith` to customize
 * `path` creation.
 *
 * **Note:** This method mutates `object`.
 *
 * @static
 * @memberOf _
 * @since 3.7.0
 * @category Object
 * @param {Object} object The object to modify.
 * @param {Array|string} path The path of the property to set.
 * @param {*} value The value to set.
 * @returns {Object} Returns `object`.
 * @example
 *
 * var object = { 'a': [{ 'b': { 'c': 3 } }] };
 *
 * _.set(object, 'a[0].b.c', 4);
 * console.log(object.a[0].b.c);
 * // => 4
 *
 * _.set(object, ['x', '0', 'y', 'z'], 5);
 * console.log(object.x[0].y.z);
 * // => 5
 */
function set(object, path, value) {
  return object == null ? object : baseSet(object, path, value);
}

module.exports = set;


/***/ }),
/* 113 */
/***/ (function(module, exports, __webpack_require__) {

var assignValue = __webpack_require__(114),
    castPath = __webpack_require__(69),
    isIndex = __webpack_require__(117),
    isObject = __webpack_require__(84),
    toKey = __webpack_require__(111);

/**
 * The base implementation of `_.set`.
 *
 * @private
 * @param {Object} object The object to modify.
 * @param {Array|string} path The path of the property to set.
 * @param {*} value The value to set.
 * @param {Function} [customizer] The function to customize path creation.
 * @returns {Object} Returns `object`.
 */
function baseSet(object, path, value, customizer) {
  if (!isObject(object)) {
    return object;
  }
  path = castPath(path, object);

  var index = -1,
      length = path.length,
      lastIndex = length - 1,
      nested = object;

  while (nested != null && ++index < length) {
    var key = toKey(path[index]),
        newValue = value;

    if (index != lastIndex) {
      var objValue = nested[key];
      newValue = customizer ? customizer(objValue, key, nested) : undefined;
      if (newValue === undefined) {
        newValue = isObject(objValue)
          ? objValue
          : (isIndex(path[index + 1]) ? [] : {});
      }
    }
    assignValue(nested, key, newValue);
    nested = nested[key];
  }
  return object;
}

module.exports = baseSet;


/***/ }),
/* 114 */
/***/ (function(module, exports, __webpack_require__) {

var baseAssignValue = __webpack_require__(115),
    eq = __webpack_require__(97);

/** Used for built-in method references. */
var objectProto = Object.prototype;

/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;

/**
 * Assigns `value` to `key` of `object` if the existing value is not equivalent
 * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
 * for equality comparisons.
 *
 * @private
 * @param {Object} object The object to modify.
 * @param {string} key The key of the property to assign.
 * @param {*} value The value to assign.
 */
function assignValue(object, key, value) {
  var objValue = object[key];
  if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) ||
      (value === undefined && !(key in object))) {
    baseAssignValue(object, key, value);
  }
}

module.exports = assignValue;


/***/ }),
/* 115 */
/***/ (function(module, exports, __webpack_require__) {

var defineProperty = __webpack_require__(116);

/**
 * The base implementation of `assignValue` and `assignMergeValue` without
 * value checks.
 *
 * @private
 * @param {Object} object The object to modify.
 * @param {string} key The key of the property to assign.
 * @param {*} value The value to assign.
 */
function baseAssignValue(object, key, value) {
  if (key == '__proto__' && defineProperty) {
    defineProperty(object, key, {
      'configurable': true,
      'enumerable': true,
      'value': value,
      'writable': true
    });
  } else {
    object[key] = value;
  }
}

module.exports = baseAssignValue;


/***/ }),
/* 116 */
/***/ (function(module, exports, __webpack_require__) {

var getNative = __webpack_require__(81);

var defineProperty = (function() {
  try {
    var func = getNative(Object, 'defineProperty');
    func({}, '', {});
    return func;
  } catch (e) {}
}());

module.exports = defineProperty;


/***/ }),
/* 117 */
/***/ (function(module, exports) {

/** Used as references for various `Number` constants. */
var MAX_SAFE_INTEGER = 9007199254740991;

/** Used to detect unsigned integer values. */
var reIsUint = /^(?:0|[1-9]\d*)$/;

/**
 * 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) {
  var type = typeof value;
  length = length == null ? MAX_SAFE_INTEGER : length;

  return !!length &&
    (type == 'number' ||
      (type != 'symbol' && reIsUint.test(value))) &&
        (value > -1 && value % 1 == 0 && value < length);
}

module.exports = isIndex;


/***/ }),
/* 118 */
/***/ (function(module, exports) {



/***/ }),
/* 119 */
/***/ (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.

// 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;
}

// Split a filename into [root, dir, basename, ext], unix version
// 'root' is just a slash, or nothing.
var splitPathRe =
    /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;
var splitPath = function(filename) {
  return splitPathRe.exec(filename).slice(1);
};

// 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) {
  var result = splitPath(path),
      root = result[0],
      dir = result[1];

  if (!root && !dir) {
    // No dirname whatsoever
    return '.';
  }

  if (dir) {
    // It has a dirname, strip trailing slash
    dir = dir.substr(0, dir.length - 1);
  }

  return root + dir;
};


exports.basename = function(path, ext) {
  var f = splitPath(path)[2];
  // TODO: make this comparison case-insensitive on windows?
  if (ext && f.substr(-1 * ext.length) === ext) {
    f = f.substr(0, f.length - ext.length);
  }
  return f;
};


exports.extname = function(path) {
  return splitPath(path)[3];
};

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(exports, __webpack_require__(120)))

/***/ }),
/* 120 */
/***/ (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; };


/***/ }),
/* 121 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
// 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.



// If obj.hasOwnProperty has been overridden, then calling
// obj.hasOwnProperty(prop) will break.
// See: https://github.com/joyent/node/issues/1707
function hasOwnProperty(obj, prop) {
  return Object.prototype.hasOwnProperty.call(obj, prop);
}

module.exports = function(qs, sep, eq, options) {
  sep = sep || '&';
  eq = eq || '=';
  var obj = {};

  if (typeof qs !== 'string' || qs.length === 0) {
    return obj;
  }

  var regexp = /\+/g;
  qs = qs.split(sep);

  var maxKeys = 1000;
  if (options && typeof options.maxKeys === 'number') {
    maxKeys = options.maxKeys;
  }

  var len = qs.length;
  // maxKeys <= 0 means that we should not limit keys count
  if (maxKeys > 0 && len > maxKeys) {
    len = maxKeys;
  }

  for (var i = 0; i < len; ++i) {
    var x = qs[i].replace(regexp, '%20'),
        idx = x.indexOf(eq),
        kstr, vstr, k, v;

    if (idx >= 0) {
      kstr = x.substr(0, idx);
      vstr = x.substr(idx + 1);
    } else {
      kstr = x;
      vstr = '';
    }

    k = decodeURIComponent(kstr);
    v = decodeURIComponent(vstr);

    if (!hasOwnProperty(obj, k)) {
      obj[k] = v;
    } else if (isArray(obj[k])) {
      obj[k].push(v);
    } else {
      obj[k] = [obj[k], v];
    }
  }

  return obj;
};

var isArray = Array.isArray || function (xs) {
  return Object.prototype.toString.call(xs) === '[object Array]';
};


/***/ }),
/* 122 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
// 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 stringifyPrimitive = function(v) {
  switch (typeof v) {
    case 'string':
      return v;

    case 'boolean':
      return v ? 'true' : 'false';

    case 'number':
      return isFinite(v) ? v : '';

    default:
      return '';
  }
};

module.exports = function(obj, sep, eq, name) {
  sep = sep || '&';
  eq = eq || '=';
  if (obj === null) {
    obj = undefined;
  }

  if (typeof obj === 'object') {
    return map(objectKeys(obj), function(k) {
      var ks = encodeURIComponent(stringifyPrimitive(k)) + eq;
      if (isArray(obj[k])) {
        return map(obj[k], function(v) {
          return ks + encodeURIComponent(stringifyPrimitive(v));
        }).join(sep);
      } else {
        return ks + encodeURIComponent(stringifyPrimitive(obj[k]));
      }
    }).join(sep);

  }

  if (!name) return '';
  return encodeURIComponent(stringifyPrimitive(name)) + eq +
         encodeURIComponent(stringifyPrimitive(obj));
};

var isArray = Array.isArray || function (xs) {
  return Object.prototype.toString.call(xs) === '[object Array]';
};

function map (xs, f) {
  if (xs.map) return xs.map(f);
  var res = [];
  for (var i = 0; i < xs.length; i++) {
    res.push(f(xs[i], i));
  }
  return res;
}

var objectKeys = Object.keys || function (obj) {
  var res = [];
  for (var key in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, key)) res.push(key);
  }
  return res;
};


/***/ }),
/* 123 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__Symbol_js__ = __webpack_require__(34);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__getRawTag_js__ = __webpack_require__(127);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__objectToString_js__ = __webpack_require__(128);




/** `Object#toString` result references. */
var nullTag = '[object Null]',
    undefinedTag = '[object Undefined]';

/** Built-in value references. */
var symToStringTag = __WEBPACK_IMPORTED_MODULE_0__Symbol_js__["a" /* default */] ? __WEBPACK_IMPORTED_MODULE_0__Symbol_js__["a" /* default */].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 && symToStringTag in Object(value))
    ? Object(__WEBPACK_IMPORTED_MODULE_1__getRawTag_js__["a" /* default */])(value)
    : Object(__WEBPACK_IMPORTED_MODULE_2__objectToString_js__["a" /* default */])(value);
}

/* harmony default export */ __webpack_exports__["a"] = (baseGetTag);


/***/ }),
/* 124 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__freeGlobal_js__ = __webpack_require__(125);


/** Detect free variable `self`. */
var freeSelf = typeof self == 'object' && self && self.Object === Object && self;

/** Used as a reference to the global object. */
var root = __WEBPACK_IMPORTED_MODULE_0__freeGlobal_js__["a" /* default */] || freeSelf || Function('return this')();

/* harmony default export */ __webpack_exports__["a"] = (root);


/***/ }),
/* 125 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* WEBPACK VAR INJECTION */(function(global) {/** Detect free variable `global` from Node.js. */
var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;

/* harmony default export */ __webpack_exports__["a"] = (freeGlobal);

/* WEBPACK VAR INJECTION */}.call(__webpack_exports__, __webpack_require__(792)))

/***/ }),
/* 126 */,
/* 127 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__Symbol_js__ = __webpack_require__(34);


/** Used for built-in method references. */
var objectProto = Object.prototype;

/** 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 nativeObjectToString = objectProto.toString;

/** Built-in value references. */
var symToStringTag = __WEBPACK_IMPORTED_MODULE_0__Symbol_js__["a" /* default */] ? __WEBPACK_IMPORTED_MODULE_0__Symbol_js__["a" /* default */].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.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;
}

/* harmony default export */ __webpack_exports__["a"] = (getRawTag);


/***/ }),
/* 128 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/** Used for built-in method references. */
var objectProto = Object.prototype;

/**
 * Used to resolve the
 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
 * of values.
 */
var nativeObjectToString = objectProto.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.call(value);
}

/* harmony default export */ __webpack_exports__["a"] = (objectToString);


/***/ }),
/* 129 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__overArg_js__ = __webpack_require__(130);


/** Built-in value references. */
var getPrototype = Object(__WEBPACK_IMPORTED_MODULE_0__overArg_js__["a" /* default */])(Object.getPrototypeOf, Object);

/* harmony default export */ __webpack_exports__["a"] = (getPrototype);


/***/ }),
/* 130 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/**
 * 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));
  };
}

/* harmony default export */ __webpack_exports__["a"] = (overArg);


/***/ }),
/* 131 */,
/* 132 */,
/* 133 */,
/* 134 */,
/* 135 */,
/* 136 */,
/* 137 */,
/* 138 */,
/* 139 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/**
 * 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';
}

/* harmony default export */ __webpack_exports__["a"] = (isObjectLike);


/***/ }),
/* 140 */,
/* 141 */,
/* 142 */,
/* 143 */,
/* 144 */,
/* 145 */,
/* 146 */
/***/ (function(module, exports, __webpack_require__) {

/**
 * 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.
 */

(function (global, factory) {
   true ? 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') {
      if (o !== o || o === Infinity) {
        return 0;
      }
      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) {
      if (end === Infinity) {
        end = originalSize;
      } else {
        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;
    },

    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);
    },

    findEntry: function(predicate, context, notSetValue) {
      var found = notSetValue;
      this.__iterate(function(v, k, c)  {
        if (predicate.call(context, v, k, c)) {
          found = [k, v];
          return false;
        }
      });
      return found;
    },

    findKey: function(predicate, context) {
      var entry = this.findEntry(predicate, context);
      return entry && entry[0];
    },

    findLast: function(predicate, context, notSetValue) {
      return this.toKeyedSeq().reverse().find(predicate, context, notSetValue);
    },

    findLastEntry: function(predicate, context, notSetValue) {
      return this.toKeyedSeq().reverse().findEntry(predicate, context, notSetValue);
    },

    findLastKey: function(predicate, context) {
      return this.toKeyedSeq().reverse().findKey(predicate, context);
    },

    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);
    },

    keyOf: function(searchValue) {
      return this.findKey(function(value ) {return is(value, searchValue)});
    },

    keySeq: function() {
      return this.toSeq().map(keyMapper).toIndexedSeq();
    },

    last: function() {
      return this.toSeq().reverse().first();
    },

    lastKeyOf: function(searchValue) {
      return this.toKeyedSeq().reverse().keyOf(searchValue);
    },

    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;

  mixin(KeyedIterable, {

    // ### More sequential methods

    flip: function() {
      return reify(this, flipFactory(this));
    },

    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.keyOf(searchValue);
      return key === undefined ? -1 : key;
    },

    lastIndexOf: function(searchValue) {
      var key = this.lastKeyOf(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 entry = this.findLastEntry(predicate, context);
      return entry ? entry[0] : -1;
    },

    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);
    },

    keySeq: function() {
      return Range(0, this.size);
    },

    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) : String(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;

}));

/***/ }),
/* 147 */,
/* 148 */,
/* 149 */,
/* 150 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * This is a straight rip-off of the React.js ReactPropTypes.js proptype validators,
 * modified to make it possible to validate Immutable.js data.
 *     ImmutableTypes.listOf is patterned after React.PropTypes.arrayOf, but for Immutable.List
 *     ImmutableTypes.shape  is based on React.PropTypes.shape, but for any Immutable.Iterable
 */


var Immutable = __webpack_require__(146);

var ANONYMOUS = "<<anonymous>>";

var ImmutablePropTypes = {
  listOf: createListOfTypeChecker,
  mapOf: createMapOfTypeChecker,
  orderedMapOf: createOrderedMapOfTypeChecker,
  setOf: createSetOfTypeChecker,
  orderedSetOf: createOrderedSetOfTypeChecker,
  stackOf: createStackOfTypeChecker,
  iterableOf: createIterableOfTypeChecker,
  recordOf: createRecordOfTypeChecker,
  shape: createShapeChecker,
  contains: createShapeChecker,
  mapContains: createMapContainsChecker,
  // Primitive Types
  list: createImmutableTypeChecker("List", Immutable.List.isList),
  map: createImmutableTypeChecker("Map", Immutable.Map.isMap),
  orderedMap: createImmutableTypeChecker("OrderedMap", Immutable.OrderedMap.isOrderedMap),
  set: createImmutableTypeChecker("Set", Immutable.Set.isSet),
  orderedSet: createImmutableTypeChecker("OrderedSet", Immutable.OrderedSet.isOrderedSet),
  stack: createImmutableTypeChecker("Stack", Immutable.Stack.isStack),
  seq: createImmutableTypeChecker("Seq", Immutable.Seq.isSeq),
  record: createImmutableTypeChecker("Record", function (isRecord) {
    return isRecord instanceof Immutable.Record;
  }),
  iterable: createImmutableTypeChecker("Iterable", Immutable.Iterable.isIterable)
};

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 (propValue instanceof Immutable.Iterable) {
    return "Immutable." + propValue.toSource().split(" ")[0];
  }
  return propType;
}

function createChainableTypeChecker(validate) {
  function checkType(isRequired, props, propName, componentName, location, propFullName) {
    for (var _len = arguments.length, rest = Array(_len > 6 ? _len - 6 : 0), _key = 6; _key < _len; _key++) {
      rest[_key - 6] = arguments[_key];
    }

    propFullName = propFullName || propName;
    componentName = componentName || ANONYMOUS;
    if (props[propName] == null) {
      var locationName = location;
      if (isRequired) {
        return new Error("Required " + locationName + " `" + propFullName + "` was not specified in " + ("`" + componentName + "`."));
      }
    } else {
      return validate.apply(undefined, [props, propName, componentName, location, propFullName].concat(rest));
    }
  }

  var chainedCheckType = checkType.bind(null, false);
  chainedCheckType.isRequired = checkType.bind(null, true);

  return chainedCheckType;
}

function createImmutableTypeChecker(immutableClassName, immutableClassTypeValidator) {
  function validate(props, propName, componentName, location, propFullName) {
    var propValue = props[propName];
    if (!immutableClassTypeValidator(propValue)) {
      var propType = getPropType(propValue);
      return new Error("Invalid " + location + " `" + propFullName + "` of type `" + propType + "` " + ("supplied to `" + componentName + "`, expected `" + immutableClassName + "`."));
    }
    return null;
  }
  return createChainableTypeChecker(validate);
}

function createIterableTypeChecker(typeChecker, immutableClassName, immutableClassTypeValidator) {

  function validate(props, propName, componentName, location, propFullName) {
    for (var _len = arguments.length, rest = Array(_len > 5 ? _len - 5 : 0), _key = 5; _key < _len; _key++) {
      rest[_key - 5] = arguments[_key];
    }

    var propValue = props[propName];
    if (!immutableClassTypeValidator(propValue)) {
      var locationName = location;
      var propType = getPropType(propValue);
      return new Error("Invalid " + locationName + " `" + propFullName + "` of type " + ("`" + propType + "` supplied to `" + componentName + "`, expected an Immutable.js " + immutableClassName + "."));
    }

    if (typeof typeChecker !== "function") {
      return new Error("Invalid typeChecker supplied to `" + componentName + "` " + ("for propType `" + propFullName + "`, expected a function."));
    }

    var propValues = propValue.toArray();
    for (var i = 0, len = propValues.length; i < len; i++) {
      var error = typeChecker.apply(undefined, [propValues, i, componentName, location, "" + propFullName + "[" + i + "]"].concat(rest));
      if (error instanceof Error) {
        return error;
      }
    }
  }
  return createChainableTypeChecker(validate);
}

function createKeysTypeChecker(typeChecker) {

  function validate(props, propName, componentName, location, propFullName) {
    for (var _len = arguments.length, rest = Array(_len > 5 ? _len - 5 : 0), _key = 5; _key < _len; _key++) {
      rest[_key - 5] = arguments[_key];
    }

    var propValue = props[propName];
    if (typeof typeChecker !== "function") {
      return new Error("Invalid keysTypeChecker (optional second argument) supplied to `" + componentName + "` " + ("for propType `" + propFullName + "`, expected a function."));
    }

    var keys = propValue.keySeq().toArray();
    for (var i = 0, len = keys.length; i < len; i++) {
      var error = typeChecker.apply(undefined, [keys, i, componentName, location, "" + propFullName + " -> key(" + keys[i] + ")"].concat(rest));
      if (error instanceof Error) {
        return error;
      }
    }
  }
  return createChainableTypeChecker(validate);
}

function createListOfTypeChecker(typeChecker) {
  return createIterableTypeChecker(typeChecker, "List", Immutable.List.isList);
}

function createMapOfTypeCheckerFactory(valuesTypeChecker, keysTypeChecker, immutableClassName, immutableClassTypeValidator) {
  function validate() {
    for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
      args[_key] = arguments[_key];
    }

    return createIterableTypeChecker(valuesTypeChecker, immutableClassName, immutableClassTypeValidator).apply(undefined, args) || keysTypeChecker && createKeysTypeChecker(keysTypeChecker).apply(undefined, args);
  }

  return createChainableTypeChecker(validate);
}

function createMapOfTypeChecker(valuesTypeChecker, keysTypeChecker) {
  return createMapOfTypeCheckerFactory(valuesTypeChecker, keysTypeChecker, "Map", Immutable.Map.isMap);
}

function createOrderedMapOfTypeChecker(valuesTypeChecker, keysTypeChecker) {
  return createMapOfTypeCheckerFactory(valuesTypeChecker, keysTypeChecker, "OrderedMap", Immutable.OrderedMap.isOrderedMap);
}

function createSetOfTypeChecker(typeChecker) {
  return createIterableTypeChecker(typeChecker, "Set", Immutable.Set.isSet);
}

function createOrderedSetOfTypeChecker(typeChecker) {
  return createIterableTypeChecker(typeChecker, "OrderedSet", Immutable.OrderedSet.isOrderedSet);
}

function createStackOfTypeChecker(typeChecker) {
  return createIterableTypeChecker(typeChecker, "Stack", Immutable.Stack.isStack);
}

function createIterableOfTypeChecker(typeChecker) {
  return createIterableTypeChecker(typeChecker, "Iterable", Immutable.Iterable.isIterable);
}

function createRecordOfTypeChecker(recordKeys) {
  function validate(props, propName, componentName, location, propFullName) {
    for (var _len = arguments.length, rest = Array(_len > 5 ? _len - 5 : 0), _key = 5; _key < _len; _key++) {
      rest[_key - 5] = arguments[_key];
    }

    var propValue = props[propName];
    if (!(propValue instanceof Immutable.Record)) {
      var propType = getPropType(propValue);
      var locationName = location;
      return new Error("Invalid " + locationName + " `" + propFullName + "` of type `" + propType + "` " + ("supplied to `" + componentName + "`, expected an Immutable.js Record."));
    }
    for (var key in recordKeys) {
      var checker = recordKeys[key];
      if (!checker) {
        continue;
      }
      var mutablePropValue = propValue.toObject();
      var error = checker.apply(undefined, [mutablePropValue, key, componentName, location, "" + propFullName + "." + key].concat(rest));
      if (error) {
        return error;
      }
    }
  }
  return createChainableTypeChecker(validate);
}

// there is some irony in the fact that shapeTypes is a standard hash and not an immutable collection
function createShapeTypeChecker(shapeTypes) {
  var immutableClassName = arguments[1] === undefined ? "Iterable" : arguments[1];
  var immutableClassTypeValidator = arguments[2] === undefined ? Immutable.Iterable.isIterable : arguments[2];

  function validate(props, propName, componentName, location, propFullName) {
    for (var _len = arguments.length, rest = Array(_len > 5 ? _len - 5 : 0), _key = 5; _key < _len; _key++) {
      rest[_key - 5] = arguments[_key];
    }

    var propValue = props[propName];
    if (!immutableClassTypeValidator(propValue)) {
      var propType = getPropType(propValue);
      var locationName = location;
      return new Error("Invalid " + locationName + " `" + propFullName + "` of type `" + propType + "` " + ("supplied to `" + componentName + "`, expected an Immutable.js " + immutableClassName + "."));
    }
    var mutablePropValue = propValue.toObject();
    for (var key in shapeTypes) {
      var checker = shapeTypes[key];
      if (!checker) {
        continue;
      }
      var error = checker.apply(undefined, [mutablePropValue, key, componentName, location, "" + propFullName + "." + key].concat(rest));
      if (error) {
        return error;
      }
    }
  }
  return createChainableTypeChecker(validate);
}

function createShapeChecker(shapeTypes) {
  return createShapeTypeChecker(shapeTypes);
}

function createMapContainsChecker(shapeTypes) {
  return createShapeTypeChecker(shapeTypes, "Map", Immutable.Map.isMap);
}

module.exports = ImmutablePropTypes;

/***/ }),
/* 151 */,
/* 152 */,
/* 153 */,
/* 154 */,
/* 155 */,
/* 156 */,
/* 157 */,
/* 158 */,
/* 159 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * 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 invariant = function(condition, format, a, b, c, d, e, f) {
  if (false) {
    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;
  }
};

module.exports = invariant;


/***/ }),
/* 160 */,
/* 161 */
/***/ (function(module, exports, __webpack_require__) {

/* WEBPACK VAR INJECTION */(function(process) {(function() {
  var Query, defaultPathSeparator, filter, matcher, parseOptions, pathScorer, preparedQueryCache, scorer;

  filter = __webpack_require__(162);

  matcher = __webpack_require__(166);

  scorer = __webpack_require__(163);

  pathScorer = __webpack_require__(164);

  Query = __webpack_require__(165);

  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);

/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(120)))

/***/ }),
/* 162 */
/***/ (function(module, exports, __webpack_require__) {

(function() {
  var Query, pathScorer, pluckCandidates, scorer, sortCandidates;

  scorer = __webpack_require__(163);

  pathScorer = __webpack_require__(164);

  Query = __webpack_require__(165);

  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);


/***/ }),
/* 163 */
/***/ (function(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 = 150;

  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);


/***/ }),
/* 164 */
/***/ (function(module, exports, __webpack_require__) {

(function() {
  var computeScore, countDir, file_coeff, getExtension, getExtensionScore, isMatch, scorePath, scoreSize, tau_depth, _ref;

  _ref = __webpack_require__(163), isMatch = _ref.isMatch, computeScore = _ref.computeScore, scoreSize = _ref.scoreSize;

  tau_depth = 20;

  file_coeff = 2.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);


/***/ }),
/* 165 */
/***/ (function(module, exports, __webpack_require__) {

(function() {
  var Query, coreChars, countDir, getCharCodes, getExtension, opt_char_re, truncatedUpperCase, _ref;

  _ref = __webpack_require__(164), 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);


/***/ }),
/* 166 */
/***/ (function(module, exports, __webpack_require__) {

(function() {
  var basenameMatch, computeMatch, isMatch, isWordStart, match, mergeMatches, scoreAcronyms, scoreCharacter, scoreConsecutives, _ref;

  _ref = __webpack_require__(163), 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);


/***/ }),
/* 167 */,
/* 168 */,
/* 169 */,
/* 170 */,
/* 171 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony export (immutable) */ __webpack_exports__["a"] = combineReducers;
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__createStore__ = __webpack_require__(32);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_lodash_es_isPlainObject__ = __webpack_require__(33);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__utils_warning__ = __webpack_require__(35);




function getUndefinedStateErrorMessage(key, action) {
  var actionType = action && action.type;
  var actionName = actionType && '"' + actionType.toString() + '"' || 'an action';

  return 'Given action ' + actionName + ', 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 === __WEBPACK_IMPORTED_MODULE_0__createStore__["a" /* 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 (!Object(__WEBPACK_IMPORTED_MODULE_1_lodash_es_isPlainObject__["a" /* default */])(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 (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: __WEBPACK_IMPORTED_MODULE_0__createStore__["a" /* 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.');
    }

    var type = '@@redux/PROBE_UNKNOWN_ACTION_' + Math.random().toString(36).substring(7).split('').join('.');
    if (typeof reducer(undefined, { type: type }) === 'undefined') {
      throw new Error('Reducer "' + key + '" returned undefined when probed with a random type. ' + ('Don\'t try to handle ' + __WEBPACK_IMPORTED_MODULE_0__createStore__["a" /* 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 (false) {
      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);

  var unexpectedKeyCache = void 0;
  if (false) {
    unexpectedKeyCache = {};
  }

  var shapeAssertionError = void 0;
  try {
    assertReducerShape(finalReducers);
  } catch (e) {
    shapeAssertionError = e;
  }

  return function combination() {
    var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
    var action = arguments[1];

    if (shapeAssertionError) {
      throw shapeAssertionError;
    }

    if (false) {
      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;
    }
    return hasChanged ? nextState : state;
  };
}

/***/ }),
/* 172 */,
/* 173 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony export (immutable) */ __webpack_exports__["a"] = bindActionCreators;
function bindActionCreator(actionCreator, dispatch) {
  return function () {
    return dispatch(actionCreator.apply(undefined, 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 a single function as the first argument,
 * and get a 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 keys = Object.keys(actionCreators);
  var boundActionCreators = {};
  for (var i = 0; i < keys.length; i++) {
    var key = keys[i];
    var actionCreator = actionCreators[key];
    if (typeof actionCreator === 'function') {
      boundActionCreators[key] = bindActionCreator(actionCreator, dispatch);
    }
  }
  return boundActionCreators;
}

/***/ }),
/* 174 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony export (immutable) */ __webpack_exports__["a"] = applyMiddleware;
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__compose__ = __webpack_require__(36);
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; };



/**
 * 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 = Array(_len), _key = 0; _key < _len; _key++) {
    middlewares[_key] = arguments[_key];
  }

  return function (createStore) {
    return function (reducer, preloadedState, enhancer) {
      var store = createStore(reducer, preloadedState, enhancer);
      var _dispatch = store.dispatch;
      var chain = [];

      var middlewareAPI = {
        getState: store.getState,
        dispatch: function dispatch(action) {
          return _dispatch(action);
        }
      };
      chain = middlewares.map(function (middleware) {
        return middleware(middlewareAPI);
      });
      _dispatch = __WEBPACK_IMPORTED_MODULE_0__compose__["a" /* default */].apply(undefined, chain)(store.dispatch);

      return _extends({}, store, {
        dispatch: _dispatch
      });
    };
  };
}

/***/ }),
/* 175 */
/***/ (function(module, exports, __webpack_require__) {

var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*!
  Copyright (c) 2016 Jed Watson.
  Licensed under the MIT License (MIT), see
  http://jedwatson.github.io/classnames
*/
/* global define */

(function () {
	'use strict';

	var hasOwn = {}.hasOwnProperty;

	function classNames () {
		var classes = [];

		for (var i = 0; i < arguments.length; i++) {
			var arg = arguments[i];
			if (!arg) continue;

			var argType = typeof arg;

			if (argType === 'string' || argType === 'number') {
				classes.push(arg);
			} else if (Array.isArray(arg)) {
				classes.push(classNames.apply(null, arg));
			} else if (argType === 'object') {
				for (var key in arg) {
					if (hasOwn.call(arg, key) && arg[key]) {
						classes.push(key);
					}
				}
			}
		}

		return classes.join(' ');
	}

	if (typeof module !== 'undefined' && module.exports) {
		module.exports = classNames;
	} else if (true) {
		// register as 'classnames', consistent with npm package name
		!(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = (function () {
			return classNames;
		}).apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
	} else {
		window.classNames = classNames;
	}
}());


/***/ }),
/* 176 */,
/* 177 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * 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 emptyFunction = __webpack_require__(178);
var invariant = __webpack_require__(180);
var ReactPropTypesSecret = __webpack_require__(187);

module.exports = function() {
  function shim(props, propName, componentName, location, propFullName, secret) {
    if (secret === ReactPropTypesSecret) {
      // It is still safe when called from React.
      return;
    }
    invariant(
      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'
    );
  };
  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,
    instanceOf: getShim,
    node: shim,
    objectOf: getShim,
    oneOf: getShim,
    oneOfType: getShim,
    shape: getShim,
    exact: getShim
  };

  ReactPropTypes.checkPropTypes = emptyFunction;
  ReactPropTypes.PropTypes = ReactPropTypes;

  return ReactPropTypes;
};


/***/ }),
/* 178 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/**
 * 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;
};

module.exports = emptyFunction;

/***/ }),
/* 179 */,
/* 180 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * 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) {};

if (false) {
  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;
  }
}

module.exports = invariant;

/***/ }),
/* 181 */,
/* 182 */,
/* 183 */,
/* 184 */,
/* 185 */,
/* 186 */,
/* 187 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * 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';

module.exports = ReactPropTypesSecret;


/***/ }),
/* 188 */,
/* 189 */,
/* 190 */,
/* 191 */,
/* 192 */,
/* 193 */,
/* 194 */,
/* 195 */,
/* 196 */,
/* 197 */
/***/ (function(module, exports) {

module.exports = __WEBPACK_EXTERNAL_MODULE_197__;

/***/ }),
/* 198 */,
/* 199 */,
/* 200 */,
/* 201 */,
/* 202 */,
/* 203 */,
/* 204 */,
/* 205 */,
/* 206 */,
/* 207 */,
/* 208 */,
/* 209 */,
/* 210 */,
/* 211 */,
/* 212 */,
/* 213 */,
/* 214 */,
/* 215 */,
/* 216 */,
/* 217 */,
/* 218 */,
/* 219 */,
/* 220 */,
/* 221 */,
/* 222 */,
/* 223 */,
/* 224 */,
/* 225 */,
/* 226 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

// @flow

const { isDevelopment } = __webpack_require__(1355);
const { Services, PrefsHelper } = __webpack_require__(1376);

const prefsSchemaVersion = "1.0.3";

const pref = Services.pref;

if (isDevelopment()) {
  pref("devtools.debugger.auto-pretty-print", true);
  pref("devtools.source-map.client-service.enabled", true);
  pref("devtools.debugger.pause-on-exceptions", false);
  pref("devtools.debugger.ignore-caught-exceptions", false);
  pref("devtools.debugger.call-stack-visible", true);
  pref("devtools.debugger.scopes-visible", true);
  pref("devtools.debugger.workers-visible", true);
  pref("devtools.debugger.expressions-visible", true);
  pref("devtools.debugger.breakpoints-visible", true);
  pref("devtools.debugger.start-panel-collapsed", false);
  pref("devtools.debugger.end-panel-collapsed", false);
  pref("devtools.debugger.tabs", "[]");
  pref("devtools.debugger.ui.framework-grouping-on", true);
  pref("devtools.debugger.pending-selected-location", "{}");
  pref("devtools.debugger.pending-breakpoints", "{}");
  pref("devtools.debugger.expressions", "[]");
  pref("devtools.debugger.file-search-case-sensitive", false);
  pref("devtools.debugger.file-search-whole-word", false);
  pref("devtools.debugger.file-search-regex-match", false);
  pref("devtools.debugger.project-directory-root", "");
  pref("devtools.debugger.prefs-schema-version", "1.0.1");
  pref("devtools.debugger.features.workers", true);
  pref("devtools.debugger.features.async-stepping", true);
  pref("devtools.debugger.features.wasm", true);
  pref("devtools.debugger.features.shortcuts", true);
  pref("devtools.debugger.features.root", true);
  pref("devtools.debugger.features.column-breakpoints", false);
  pref("devtools.debugger.features.chrome-scopes", false);
  pref("devtools.debugger.features.map-scopes", true);
  pref("devtools.debugger.features.breakpoints-dropdown", true);
  pref("devtools.debugger.features.remove-command-bar-options", true);
  pref("devtools.debugger.features.code-coverage", false);
  pref("devtools.debugger.features.event-listeners", false);
  pref("devtools.debugger.features.code-folding", false);
  pref("devtools.debugger.features.outline", true);
  pref("devtools.debugger.features.column-breakpoints", true);
  pref("devtools.debugger.features.replay", true);
}

const prefs = new PrefsHelper("devtools", {
  autoPrettyPrint: ["Bool", "debugger.auto-pretty-print"],
  clientSourceMapsEnabled: ["Bool", "source-map.client-service.enabled"],
  pauseOnExceptions: ["Bool", "debugger.pause-on-exceptions"],
  ignoreCaughtExceptions: ["Bool", "debugger.ignore-caught-exceptions"],
  callStackVisible: ["Bool", "debugger.call-stack-visible"],
  scopesVisible: ["Bool", "debugger.scopes-visible"],
  workersVisible: ["Bool", "debugger.workers-visible"],
  breakpointsVisible: ["Bool", "debugger.breakpoints-visible"],
  expressionsVisible: ["Bool", "debugger.expressions-visible"],
  startPanelCollapsed: ["Bool", "debugger.start-panel-collapsed"],
  endPanelCollapsed: ["Bool", "debugger.end-panel-collapsed"],
  frameworkGroupingOn: ["Bool", "debugger.ui.framework-grouping-on"],
  tabs: ["Json", "debugger.tabs", []],
  pendingSelectedLocation: ["Json", "debugger.pending-selected-location", {}],
  pendingBreakpoints: ["Json", "debugger.pending-breakpoints", {}],
  expressions: ["Json", "debugger.expressions", []],
  fileSearchCaseSensitive: ["Bool", "debugger.file-search-case-sensitive"],
  fileSearchWholeWord: ["Bool", "debugger.file-search-whole-word"],
  fileSearchRegexMatch: ["Bool", "debugger.file-search-regex-match"],
  debuggerPrefsSchemaVersion: ["Char", "debugger.prefs-schema-version"],
  projectDirectoryRoot: ["Char", "debugger.project-directory-root", ""]
});
/* harmony export (immutable) */ __webpack_exports__["prefs"] = prefs;


const features = new PrefsHelper("devtools.debugger.features", {
  asyncStepping: ["Bool", "async-stepping"],
  wasm: ["Bool", "wasm"],
  shortcuts: ["Bool", "shortcuts"],
  root: ["Bool", "root"],
  columnBreakpoints: ["Bool", "column-breakpoints"],
  chromeScopes: ["Bool", "chrome-scopes"],
  mapScopes: ["Bool", "map-scopes"],
  breakpointsDropdown: ["Bool", "breakpoints-dropdown"],
  removeCommandBarOptions: ["Bool", "remove-command-bar-options"],
  workers: ["Bool", "workers"],
  codeCoverage: ["Bool", "code-coverage"],
  eventListeners: ["Bool", "event-listeners"],
  outline: ["Bool", "outline"],
  codeFolding: ["Bool", "code-folding"],
  replay: ["Bool", "replay"]
});
/* harmony export (immutable) */ __webpack_exports__["features"] = features;


if (prefs.debuggerPrefsSchemaVersion !== prefsSchemaVersion) {
  // clear pending Breakpoints
  prefs.pendingBreakpoints = {};
  prefs.debuggerPrefsSchemaVersion = prefsSchemaVersion;
}


/***/ }),
/* 227 */,
/* 228 */,
/* 229 */,
/* 230 */,
/* 231 */,
/* 232 */,
/* 233 */,
/* 234 */,
/* 235 */,
/* 236 */,
/* 237 */,
/* 238 */,
/* 239 */,
/* 240 */,
/* 241 */,
/* 242 */,
/* 243 */,
/* 244 */,
/* 245 */,
/* 246 */,
/* 247 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 viewBox=\"0 0 256 272\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" preserveAspectRatio=\"xMidYMid\"><g><path d=\"M0.0996108949,45.522179 L125.908171,0.697276265 L255.103502,44.7252918 L234.185214,211.175097 L125.908171,271.140856 L19.3245136,211.971984 L0.0996108949,45.522179 Z\" fill=\"#E23237\"></path><path d=\"M255.103502,44.7252918 L125.908171,0.697276265 L125.908171,271.140856 L234.185214,211.274708 L255.103502,44.7252918 L255.103502,44.7252918 Z\" fill=\"#B52E31\"></path><path d=\"M126.107393,32.27393 L126.107393,32.27393 L47.7136187,206.692607 L76.9992218,206.194553 L92.7377432,166.848249 L126.207004,166.848249 L126.306615,166.848249 L163.063035,166.848249 L180.29572,206.692607 L208.286381,207.190661 L126.107393,32.27393 L126.107393,32.27393 Z M126.306615,88.155642 L152.803113,143.5393 L127.402335,143.5393 L126.107393,143.5393 L102.997665,143.5393 L126.306615,88.155642 L126.306615,88.155642 Z\" fill=\"#FFFFFF\"></path></g></svg>"

/***/ }),
/* 248 */
/***/ (function(module, exports, __webpack_require__) {

(function(){
  var crypt = __webpack_require__(249),
      utf8 = __webpack_require__(250).utf8,
      isBuffer = __webpack_require__(251),
      bin = __webpack_require__(250).bin,

  // The core
  md5 = function (message, options) {
    // Convert to byte array
    if (message.constructor == String)
      if (options && options.encoding === 'binary')
        message = bin.stringToBytes(message);
      else
        message = utf8.stringToBytes(message);
    else if (isBuffer(message))
      message = Array.prototype.slice.call(message, 0);
    else if (!Array.isArray(message))
      message = message.toString();
    // else, assume byte array already

    var m = crypt.bytesToWords(message),
        l = message.length * 8,
        a =  1732584193,
        b = -271733879,
        c = -1732584194,
        d =  271733878;

    // Swap endian
    for (var i = 0; i < m.length; i++) {
      m[i] = ((m[i] <<  8) | (m[i] >>> 24)) & 0x00FF00FF |
             ((m[i] << 24) | (m[i] >>>  8)) & 0xFF00FF00;
    }

    // Padding
    m[l >>> 5] |= 0x80 << (l % 32);
    m[(((l + 64) >>> 9) << 4) + 14] = l;

    // Method shortcuts
    var FF = md5._ff,
        GG = md5._gg,
        HH = md5._hh,
        II = md5._ii;

    for (var i = 0; i < m.length; i += 16) {

      var aa = a,
          bb = b,
          cc = c,
          dd = d;

      a = FF(a, b, c, d, m[i+ 0],  7, -680876936);
      d = FF(d, a, b, c, m[i+ 1], 12, -389564586);
      c = FF(c, d, a, b, m[i+ 2], 17,  606105819);
      b = FF(b, c, d, a, m[i+ 3], 22, -1044525330);
      a = FF(a, b, c, d, m[i+ 4],  7, -176418897);
      d = FF(d, a, b, c, m[i+ 5], 12,  1200080426);
      c = FF(c, d, a, b, m[i+ 6], 17, -1473231341);
      b = FF(b, c, d, a, m[i+ 7], 22, -45705983);
      a = FF(a, b, c, d, m[i+ 8],  7,  1770035416);
      d = FF(d, a, b, c, m[i+ 9], 12, -1958414417);
      c = FF(c, d, a, b, m[i+10], 17, -42063);
      b = FF(b, c, d, a, m[i+11], 22, -1990404162);
      a = FF(a, b, c, d, m[i+12],  7,  1804603682);
      d = FF(d, a, b, c, m[i+13], 12, -40341101);
      c = FF(c, d, a, b, m[i+14], 17, -1502002290);
      b = FF(b, c, d, a, m[i+15], 22,  1236535329);

      a = GG(a, b, c, d, m[i+ 1],  5, -165796510);
      d = GG(d, a, b, c, m[i+ 6],  9, -1069501632);
      c = GG(c, d, a, b, m[i+11], 14,  643717713);
      b = GG(b, c, d, a, m[i+ 0], 20, -373897302);
      a = GG(a, b, c, d, m[i+ 5],  5, -701558691);
      d = GG(d, a, b, c, m[i+10],  9,  38016083);
      c = GG(c, d, a, b, m[i+15], 14, -660478335);
      b = GG(b, c, d, a, m[i+ 4], 20, -405537848);
      a = GG(a, b, c, d, m[i+ 9],  5,  568446438);
      d = GG(d, a, b, c, m[i+14],  9, -1019803690);
      c = GG(c, d, a, b, m[i+ 3], 14, -187363961);
      b = GG(b, c, d, a, m[i+ 8], 20,  1163531501);
      a = GG(a, b, c, d, m[i+13],  5, -1444681467);
      d = GG(d, a, b, c, m[i+ 2],  9, -51403784);
      c = GG(c, d, a, b, m[i+ 7], 14,  1735328473);
      b = GG(b, c, d, a, m[i+12], 20, -1926607734);

      a = HH(a, b, c, d, m[i+ 5],  4, -378558);
      d = HH(d, a, b, c, m[i+ 8], 11, -2022574463);
      c = HH(c, d, a, b, m[i+11], 16,  1839030562);
      b = HH(b, c, d, a, m[i+14], 23, -35309556);
      a = HH(a, b, c, d, m[i+ 1],  4, -1530992060);
      d = HH(d, a, b, c, m[i+ 4], 11,  1272893353);
      c = HH(c, d, a, b, m[i+ 7], 16, -155497632);
      b = HH(b, c, d, a, m[i+10], 23, -1094730640);
      a = HH(a, b, c, d, m[i+13],  4,  681279174);
      d = HH(d, a, b, c, m[i+ 0], 11, -358537222);
      c = HH(c, d, a, b, m[i+ 3], 16, -722521979);
      b = HH(b, c, d, a, m[i+ 6], 23,  76029189);
      a = HH(a, b, c, d, m[i+ 9],  4, -640364487);
      d = HH(d, a, b, c, m[i+12], 11, -421815835);
      c = HH(c, d, a, b, m[i+15], 16,  530742520);
      b = HH(b, c, d, a, m[i+ 2], 23, -995338651);

      a = II(a, b, c, d, m[i+ 0],  6, -198630844);
      d = II(d, a, b, c, m[i+ 7], 10,  1126891415);
      c = II(c, d, a, b, m[i+14], 15, -1416354905);
      b = II(b, c, d, a, m[i+ 5], 21, -57434055);
      a = II(a, b, c, d, m[i+12],  6,  1700485571);
      d = II(d, a, b, c, m[i+ 3], 10, -1894986606);
      c = II(c, d, a, b, m[i+10], 15, -1051523);
      b = II(b, c, d, a, m[i+ 1], 21, -2054922799);
      a = II(a, b, c, d, m[i+ 8],  6,  1873313359);
      d = II(d, a, b, c, m[i+15], 10, -30611744);
      c = II(c, d, a, b, m[i+ 6], 15, -1560198380);
      b = II(b, c, d, a, m[i+13], 21,  1309151649);
      a = II(a, b, c, d, m[i+ 4],  6, -145523070);
      d = II(d, a, b, c, m[i+11], 10, -1120210379);
      c = II(c, d, a, b, m[i+ 2], 15,  718787259);
      b = II(b, c, d, a, m[i+ 9], 21, -343485551);

      a = (a + aa) >>> 0;
      b = (b + bb) >>> 0;
      c = (c + cc) >>> 0;
      d = (d + dd) >>> 0;
    }

    return crypt.endian([a, b, c, d]);
  };

  // Auxiliary functions
  md5._ff  = function (a, b, c, d, x, s, t) {
    var n = a + (b & c | ~b & d) + (x >>> 0) + t;
    return ((n << s) | (n >>> (32 - s))) + b;
  };
  md5._gg  = function (a, b, c, d, x, s, t) {
    var n = a + (b & d | c & ~d) + (x >>> 0) + t;
    return ((n << s) | (n >>> (32 - s))) + b;
  };
  md5._hh  = function (a, b, c, d, x, s, t) {
    var n = a + (b ^ c ^ d) + (x >>> 0) + t;
    return ((n << s) | (n >>> (32 - s))) + b;
  };
  md5._ii  = function (a, b, c, d, x, s, t) {
    var n = a + (c ^ (b | ~d)) + (x >>> 0) + t;
    return ((n << s) | (n >>> (32 - s))) + b;
  };

  // Package private blocksize
  md5._blocksize = 16;
  md5._digestsize = 16;

  module.exports = function (message, options) {
    if (message === undefined || message === null)
      throw new Error('Illegal argument ' + message);

    var digestbytes = crypt.wordsToBytes(md5(message, options));
    return options && options.asBytes ? digestbytes :
        options && options.asString ? bin.bytesToString(digestbytes) :
        crypt.bytesToHex(digestbytes);
  };

})();


/***/ }),
/* 249 */
/***/ (function(module, exports) {

(function() {
  var base64map
      = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',

  crypt = {
    // Bit-wise rotation left
    rotl: function(n, b) {
      return (n << b) | (n >>> (32 - b));
    },

    // Bit-wise rotation right
    rotr: function(n, b) {
      return (n << (32 - b)) | (n >>> b);
    },

    // Swap big-endian to little-endian and vice versa
    endian: function(n) {
      // If number given, swap endian
      if (n.constructor == Number) {
        return crypt.rotl(n, 8) & 0x00FF00FF | crypt.rotl(n, 24) & 0xFF00FF00;
      }

      // Else, assume array and swap all items
      for (var i = 0; i < n.length; i++)
        n[i] = crypt.endian(n[i]);
      return n;
    },

    // Generate an array of any length of random bytes
    randomBytes: function(n) {
      for (var bytes = []; n > 0; n--)
        bytes.push(Math.floor(Math.random() * 256));
      return bytes;
    },

    // Convert a byte array to big-endian 32-bit words
    bytesToWords: function(bytes) {
      for (var words = [], i = 0, b = 0; i < bytes.length; i++, b += 8)
        words[b >>> 5] |= bytes[i] << (24 - b % 32);
      return words;
    },

    // Convert big-endian 32-bit words to a byte array
    wordsToBytes: function(words) {
      for (var bytes = [], b = 0; b < words.length * 32; b += 8)
        bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF);
      return bytes;
    },

    // Convert a byte array to a hex string
    bytesToHex: function(bytes) {
      for (var hex = [], i = 0; i < bytes.length; i++) {
        hex.push((bytes[i] >>> 4).toString(16));
        hex.push((bytes[i] & 0xF).toString(16));
      }
      return hex.join('');
    },

    // Convert a hex string to a byte array
    hexToBytes: function(hex) {
      for (var bytes = [], c = 0; c < hex.length; c += 2)
        bytes.push(parseInt(hex.substr(c, 2), 16));
      return bytes;
    },

    // Convert a byte array to a base-64 string
    bytesToBase64: function(bytes) {
      for (var base64 = [], i = 0; i < bytes.length; i += 3) {
        var triplet = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
        for (var j = 0; j < 4; j++)
          if (i * 8 + j * 6 <= bytes.length * 8)
            base64.push(base64map.charAt((triplet >>> 6 * (3 - j)) & 0x3F));
          else
            base64.push('=');
      }
      return base64.join('');
    },

    // Convert a base-64 string to a byte array
    base64ToBytes: function(base64) {
      // Remove non-base-64 characters
      base64 = base64.replace(/[^A-Z0-9+\/]/ig, '');

      for (var bytes = [], i = 0, imod4 = 0; i < base64.length;
          imod4 = ++i % 4) {
        if (imod4 == 0) continue;
        bytes.push(((base64map.indexOf(base64.charAt(i - 1))
            & (Math.pow(2, -2 * imod4 + 8) - 1)) << (imod4 * 2))
            | (base64map.indexOf(base64.charAt(i)) >>> (6 - imod4 * 2)));
      }
      return bytes;
    }
  };

  module.exports = crypt;
})();


/***/ }),
/* 250 */
/***/ (function(module, exports) {

var charenc = {
  // UTF-8 encoding
  utf8: {
    // Convert a string to a byte array
    stringToBytes: function(str) {
      return charenc.bin.stringToBytes(unescape(encodeURIComponent(str)));
    },

    // Convert a byte array to a string
    bytesToString: function(bytes) {
      return decodeURIComponent(escape(charenc.bin.bytesToString(bytes)));
    }
  },

  // Binary encoding
  bin: {
    // Convert a string to a byte array
    stringToBytes: function(str) {
      for (var bytes = [], i = 0; i < str.length; i++)
        bytes.push(str.charCodeAt(i) & 0xFF);
      return bytes;
    },

    // Convert a byte array to a string
    bytesToString: function(bytes) {
      for (var str = [], i = 0; i < bytes.length; i++)
        str.push(String.fromCharCode(bytes[i]));
      return str.join('');
    }
  }
};

module.exports = charenc;


/***/ }),
/* 251 */
/***/ (function(module, exports) {

/*!
 * Determine if an object is a Buffer
 *
 * @author   Feross Aboukhadijeh <https://feross.org>
 * @license  MIT
 */

// The _isBuffer check is for Safari 5-7 support, because it's missing
// Object.prototype.constructor. Remove this eventually
module.exports = function (obj) {
  return obj != null && (isBuffer(obj) || isSlowBuffer(obj) || !!obj._isBuffer)
}

function isBuffer (obj) {
  return !!obj.constructor && typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj)
}

// For Node v0.10 support. Remove this eventually.
function isSlowBuffer (obj) {
  return typeof obj.readFloatLE === 'function' && typeof obj.slice === 'function' && isBuffer(obj.slice(0, 0))
}


/***/ }),
/* 252 */,
/* 253 */,
/* 254 */,
/* 255 */,
/* 256 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 100 100\"><g fill=\"#764ABC\"><path d=\"M65.6 65.4c2.9-.3 5.1-2.8 5-5.8-.1-3-2.6-5.4-5.6-5.4h-.2c-3.1.1-5.5 2.7-5.4 5.8.1 1.5.7 2.8 1.6 3.7-3.4 6.7-8.6 11.6-16.4 15.7-5.3 2.8-10.8 3.8-16.3 3.1-4.5-.6-8-2.6-10.2-5.9-3.2-4.9-3.5-10.2-.8-15.5 1.9-3.8 4.9-6.6 6.8-8-.4-1.3-1-3.5-1.3-5.1-14.5 10.5-13 24.7-8.6 31.4 3.3 5 10 8.1 17.4 8.1 2 0 4-.2 6-.7 12.8-2.5 22.5-10.1 28-21.4z\"></path><path d=\"M83.2 53c-7.6-8.9-18.8-13.8-31.6-13.8H50c-.9-1.8-2.8-3-4.9-3h-.2c-3.1.1-5.5 2.7-5.4 5.8.1 3 2.6 5.4 5.6 5.4h.2c2.2-.1 4.1-1.5 4.9-3.4H52c7.6 0 14.8 2.2 21.3 6.5 5 3.3 8.6 7.6 10.6 12.8 1.7 4.2 1.6 8.3-.2 11.8-2.8 5.3-7.5 8.2-13.7 8.2-4 0-7.8-1.2-9.8-2.1-1.1 1-3.1 2.6-4.5 3.6 4.3 2 8.7 3.1 12.9 3.1 9.6 0 16.7-5.3 19.4-10.6 2.9-5.8 2.7-15.8-4.8-24.3z\"></path><path d=\"M32.4 67.1c.1 3 2.6 5.4 5.6 5.4h.2c3.1-.1 5.5-2.7 5.4-5.8-.1-3-2.6-5.4-5.6-5.4h-.2c-.2 0-.5 0-.7.1-4.1-6.8-5.8-14.2-5.2-22.2.4-6 2.4-11.2 5.9-15.5 2.9-3.7 8.5-5.5 12.3-5.6 10.6-.2 15.1 13 15.4 18.3 1.3.3 3.5 1 5 1.5-1.2-16.2-11.2-24.6-20.8-24.6-9 0-17.3 6.5-20.6 16.1-4.6 12.8-1.6 25.1 4 34.8-.5.7-.8 1.8-.7 2.9z\"></path></g></svg>"

/***/ }),
/* 257 */,
/* 258 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 version=\"1.0\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 200.000000 200.000000\" preserveAspectRatio=\"xMidYMid meet\"><g transform=\"translate(0.000000,200.000000) scale(0.100000,-0.100000)\" fill=\"#000000\" stroke=\"none\"><path d=\"M247 1663 c-4 -3 -7 -235 -7 -515 l0 -508 175 0 175 0 0 515 0 515 -168 0 c-93 0 -172 -3 -175 -7z m183 -508 c0 -336 -2 -385 -15 -385 -13 0 -15 49 -15 385 0 336 2 385 15 385 13 0 15 -49 15 -385z\"></path><path d=\"M697 1664 c-4 -4 -7 -236 -7 -516 l0 -508 165 0 165 0 2 181 3 181 99 -146 c54 -80 102 -146 106 -146 3 0 51 66 106 148 l99 147 3 -182 2 -183 160 0 160 0 0 515 0 515 -132 0 -133 -1 -130 -194 c-71 -107 -132 -194 -135 -194 -3 -1 -64 86 -135 192 l-131 192 -130 3 c-71 1 -133 -1 -137 -4z m356 -374 c92 -138 172 -250 176 -250 4 0 84 112 177 250 112 166 175 250 187 250 16 0 17 -25 17 -385 0 -336 -2 -385 -15 -385 -13 0 -15 46 -15 350 0 193 -4 350 -8 350 -4 0 -82 -108 -172 -240 -90 -132 -167 -240 -171 -240 -4 0 -81 108 -171 240 -90 131 -166 239 -170 240 -5 0 -8 -157 -8 -350 0 -304 -2 -350 -15 -350 -13 0 -15 49 -15 385 0 360 1 385 18 385 11 -1 74 -86 185 -250z\"></path></g></svg>"

/***/ }),
/* 259 */
/***/ (function(module, exports, __webpack_require__) {

var toString = __webpack_require__(108);

/**
 * Used to match `RegExp`
 * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).
 */
var reRegExpChar = /[\\^$.*+?()[\]{}|]/g,
    reHasRegExpChar = RegExp(reRegExpChar.source);

/**
 * Escapes the `RegExp` special characters "^", "$", "\", ".", "*", "+",
 * "?", "(", ")", "[", "]", "{", "}", and "|" in `string`.
 *
 * @static
 * @memberOf _
 * @since 3.0.0
 * @category String
 * @param {string} [string=''] The string to escape.
 * @returns {string} Returns the escaped string.
 * @example
 *
 * _.escapeRegExp('[lodash](https://lodash.com/)');
 * // => '\[lodash\]\(https://lodash\.com/\)'
 */
function escapeRegExp(string) {
  string = toString(string);
  return (string && reHasRegExpChar.test(string))
    ? string.replace(reRegExpChar, '\\$&')
    : string;
}

module.exports = escapeRegExp;


/***/ }),
/* 260 */,
/* 261 */,
/* 262 */,
/* 263 */,
/* 264 */,
/* 265 */,
/* 266 */,
/* 267 */,
/* 268 */,
/* 269 */,
/* 270 */,
/* 271 */,
/* 272 */,
/* 273 */,
/* 274 */,
/* 275 */,
/* 276 */,
/* 277 */,
/* 278 */,
/* 279 */,
/* 280 */,
/* 281 */,
/* 282 */,
/* 283 */,
/* 284 */,
/* 285 */,
/* 286 */,
/* 287 */,
/* 288 */,
/* 289 */,
/* 290 */,
/* 291 */,
/* 292 */,
/* 293 */,
/* 294 */,
/* 295 */,
/* 296 */,
/* 297 */,
/* 298 */,
/* 299 */,
/* 300 */,
/* 301 */,
/* 302 */,
/* 303 */,
/* 304 */,
/* 305 */,
/* 306 */,
/* 307 */,
/* 308 */,
/* 309 */,
/* 310 */,
/* 311 */,
/* 312 */,
/* 313 */,
/* 314 */,
/* 315 */,
/* 316 */,
/* 317 */,
/* 318 */,
/* 319 */,
/* 320 */,
/* 321 */,
/* 322 */,
/* 323 */,
/* 324 */,
/* 325 */,
/* 326 */,
/* 327 */,
/* 328 */,
/* 329 */,
/* 330 */,
/* 331 */,
/* 332 */,
/* 333 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.EXITING = exports.ENTERED = exports.ENTERING = exports.EXITED = exports.UNMOUNTED = undefined;

var _propTypes = __webpack_require__(20);

var PropTypes = _interopRequireWildcard(_propTypes);

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _reactDom = __webpack_require__(4);

var _reactDom2 = _interopRequireDefault(_reactDom);

var _PropTypes = __webpack_require__(335);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

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(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; }

var UNMOUNTED = exports.UNMOUNTED = 'unmounted';
var EXITED = exports.EXITED = 'exited';
var ENTERING = exports.ENTERING = 'entering';
var ENTERED = exports.ENTERED = 'entered';
var EXITING = exports.EXITING = 'exiting';

/**
 * The Transition component lets you describe a transition from one component
 * state to another _over time_ with a simple declarative API. Most commonly
 * it's used to animate the mounting and unmounting of a component, but can also
 * be used to describe in-place transition states as well.
 *
 * By default the `Transition` component does not alter the behavior of the
 * component it renders, it only tracks "enter" and "exit" states for the components.
 * It's up to you to give meaning and effect to those states. For example we can
 * add styles to a component when it enters or exits:
 *
 * ```jsx
 * import Transition from 'react-transition-group/Transition';
 *
 * const duration = 300;
 *
 * const defaultStyle = {
 *   transition: `opacity ${duration}ms ease-in-out`,
 *   opacity: 0,
 * }
 *
 * const transitionStyles = {
 *   entering: { opacity: 0 },
 *   entered:  { opacity: 1 },
 * };
 *
 * const Fade = ({ in: inProp }) => (
 *   <Transition in={inProp} timeout={duration}>
 *     {(state) => (
 *       <div style={{
 *         ...defaultStyle,
 *         ...transitionStyles[state]
 *       }}>
 *         I'm A fade Transition!
 *       </div>
 *     )}
 *   </Transition>
 * );
 * ```
 *
 * As noted the `Transition` component doesn't _do_ anything by itself to its child component.
 * What it does do is track transition states over time so you can update the
 * component (such as by adding styles or classes) when it changes states.
 *
 * There are 4 main states a Transition can be in:
 *  - `ENTERING`
 *  - `ENTERED`
 *  - `EXITING`
 *  - `EXITED`
 *
 * Transition state is toggled via the `in` prop. When `true` the component begins the
 * "Enter" stage. During this stage, the component will shift from its current transition state,
 * to `'entering'` for the duration of the transition and then to the `'entered'` stage once
 * it's complete. Let's take the following example:
 *
 * ```jsx
 * state= { in: false };
 *
 * toggleEnterState = () => {
 *   this.setState({ in: true });
 * }
 *
 * render() {
 *   return (
 *     <div>
 *       <Transition in={this.state.in} timeout={500} />
 *       <button onClick={this.toggleEnterState}>Click to Enter</button>
 *     </div>
 *   );
 * }
 * ```
 *
 * When the button is clicked the component will shift to the `'entering'` state and
 * stay there for 500ms (the value of `timeout`) when finally switches to `'entered'`.
 *
 * When `in` is `false` the same thing happens except the state moves from `'exiting'` to `'exited'`.
 */

var Transition = function (_React$Component) {
  _inherits(Transition, _React$Component);

  function Transition(props, context) {
    _classCallCheck(this, Transition);

    var _this = _possibleConstructorReturn(this, _React$Component.call(this, props, context));

    var parentGroup = context.transitionGroup;
    // In the context of a TransitionGroup all enters are really appears
    var appear = parentGroup && !parentGroup.isMounting ? props.enter : props.appear;

    var initialStatus = void 0;
    _this.nextStatus = null;

    if (props.in) {
      if (appear) {
        initialStatus = EXITED;
        _this.nextStatus = ENTERING;
      } else {
        initialStatus = ENTERED;
      }
    } else {
      if (props.unmountOnExit || props.mountOnEnter) {
        initialStatus = UNMOUNTED;
      } else {
        initialStatus = EXITED;
      }
    }

    _this.state = { status: initialStatus };

    _this.nextCallback = null;
    return _this;
  }

  Transition.prototype.getChildContext = function getChildContext() {
    return { transitionGroup: null }; // allows for nested Transitions
  };

  Transition.prototype.componentDidMount = function componentDidMount() {
    this.updateStatus(true);
  };

  Transition.prototype.componentWillReceiveProps = function componentWillReceiveProps(nextProps) {
    var _ref = this.pendingState || this.state,
        status = _ref.status;

    if (nextProps.in) {
      if (status === UNMOUNTED) {
        this.setState({ status: EXITED });
      }
      if (status !== ENTERING && status !== ENTERED) {
        this.nextStatus = ENTERING;
      }
    } else {
      if (status === ENTERING || status === ENTERED) {
        this.nextStatus = EXITING;
      }
    }
  };

  Transition.prototype.componentDidUpdate = function componentDidUpdate() {
    this.updateStatus();
  };

  Transition.prototype.componentWillUnmount = function componentWillUnmount() {
    this.cancelNextCallback();
  };

  Transition.prototype.getTimeouts = function getTimeouts() {
    var timeout = this.props.timeout;

    var exit = void 0,
        enter = void 0,
        appear = void 0;

    exit = enter = appear = timeout;

    if (timeout != null && typeof timeout !== 'number') {
      exit = timeout.exit;
      enter = timeout.enter;
      appear = timeout.appear;
    }
    return { exit: exit, enter: enter, appear: appear };
  };

  Transition.prototype.updateStatus = function updateStatus() {
    var mounting = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;

    var nextStatus = this.nextStatus;

    if (nextStatus !== null) {
      this.nextStatus = null;
      // nextStatus will always be ENTERING or EXITING.
      this.cancelNextCallback();
      var node = _reactDom2.default.findDOMNode(this);

      if (nextStatus === ENTERING) {
        this.performEnter(node, mounting);
      } else {
        this.performExit(node);
      }
    } else if (this.props.unmountOnExit && this.state.status === EXITED) {
      this.setState({ status: UNMOUNTED });
    }
  };

  Transition.prototype.performEnter = function performEnter(node, mounting) {
    var _this2 = this;

    var enter = this.props.enter;

    var appearing = this.context.transitionGroup ? this.context.transitionGroup.isMounting : mounting;

    var timeouts = this.getTimeouts();

    // no enter animation skip right to ENTERED
    // if we are mounting and running this it means appear _must_ be set
    if (!mounting && !enter) {
      this.safeSetState({ status: ENTERED }, function () {
        _this2.props.onEntered(node);
      });
      return;
    }

    this.props.onEnter(node, appearing);

    this.safeSetState({ status: ENTERING }, function () {
      _this2.props.onEntering(node, appearing);

      // FIXME: appear timeout?
      _this2.onTransitionEnd(node, timeouts.enter, function () {
        _this2.safeSetState({ status: ENTERED }, function () {
          _this2.props.onEntered(node, appearing);
        });
      });
    });
  };

  Transition.prototype.performExit = function performExit(node) {
    var _this3 = this;

    var exit = this.props.exit;

    var timeouts = this.getTimeouts();

    // no exit animation skip right to EXITED
    if (!exit) {
      this.safeSetState({ status: EXITED }, function () {
        _this3.props.onExited(node);
      });
      return;
    }
    this.props.onExit(node);

    this.safeSetState({ status: EXITING }, function () {
      _this3.props.onExiting(node);

      _this3.onTransitionEnd(node, timeouts.exit, function () {
        _this3.safeSetState({ status: EXITED }, function () {
          _this3.props.onExited(node);
        });
      });
    });
  };

  Transition.prototype.cancelNextCallback = function cancelNextCallback() {
    if (this.nextCallback !== null) {
      this.nextCallback.cancel();
      this.nextCallback = null;
    }
  };

  Transition.prototype.safeSetState = function safeSetState(nextState, callback) {
    var _this4 = this;

    // We need to track pending updates for instances where a cWRP fires quickly
    // after cDM and before the state flushes, which would double trigger a
    // transition
    this.pendingState = nextState;

    // This shouldn't be necessary, but there are weird race conditions with
    // setState callbacks and unmounting in testing, so always make sure that
    // we can cancel any pending setState callbacks after we unmount.
    callback = this.setNextCallback(callback);
    this.setState(nextState, function () {
      _this4.pendingState = null;
      callback();
    });
  };

  Transition.prototype.setNextCallback = function setNextCallback(callback) {
    var _this5 = this;

    var active = true;

    this.nextCallback = function (event) {
      if (active) {
        active = false;
        _this5.nextCallback = null;

        callback(event);
      }
    };

    this.nextCallback.cancel = function () {
      active = false;
    };

    return this.nextCallback;
  };

  Transition.prototype.onTransitionEnd = function onTransitionEnd(node, timeout, handler) {
    this.setNextCallback(handler);

    if (node) {
      if (this.props.addEndListener) {
        this.props.addEndListener(node, this.nextCallback);
      }
      if (timeout != null) {
        setTimeout(this.nextCallback, timeout);
      }
    } else {
      setTimeout(this.nextCallback, 0);
    }
  };

  Transition.prototype.render = function render() {
    var status = this.state.status;
    if (status === UNMOUNTED) {
      return null;
    }

    var _props = this.props,
        children = _props.children,
        childProps = _objectWithoutProperties(_props, ['children']);
    // filter props for Transtition


    delete childProps.in;
    delete childProps.mountOnEnter;
    delete childProps.unmountOnExit;
    delete childProps.appear;
    delete childProps.enter;
    delete childProps.exit;
    delete childProps.timeout;
    delete childProps.addEndListener;
    delete childProps.onEnter;
    delete childProps.onEntering;
    delete childProps.onEntered;
    delete childProps.onExit;
    delete childProps.onExiting;
    delete childProps.onExited;

    if (typeof children === 'function') {
      return children(status, childProps);
    }

    var child = _react2.default.Children.only(children);
    return _react2.default.cloneElement(child, childProps);
  };

  return Transition;
}(_react2.default.Component);

Transition.contextTypes = {
  transitionGroup: PropTypes.object
};
Transition.childContextTypes = {
  transitionGroup: function transitionGroup() {}
};


Transition.propTypes =  false ? {
  /**
   * A `function` child can be used instead of a React element.
   * This function is called with the current transition status
   * ('entering', 'entered', 'exiting', 'exited', 'unmounted'), which can used
   * to apply context specific props to a component.
   *
   * ```jsx
   * <Transition timeout={150}>
   *   {(status) => (
   *     <MyComponent className={`fade fade-${status}`} />
   *   )}
   * </Transition>
   * ```
   */
  children: PropTypes.oneOfType([PropTypes.func.isRequired, PropTypes.element.isRequired]).isRequired,

  /**
   * Show the component; triggers the enter or exit states
   */
  in: PropTypes.bool,

  /**
   * By default the child component is mounted immediately along with
   * the parent `Transition` component. If you want to "lazy mount" the component on the
   * first `in={true}` you can set `mountOnEnter`. After the first enter transition the component will stay
   * mounted, even on "exited", unless you also specify `unmountOnExit`.
   */
  mountOnEnter: PropTypes.bool,

  /**
   * By default the child component stays mounted after it reaches the `'exited'` state.
   * Set `unmountOnExit` if you'd prefer to unmount the component after it finishes exiting.
   */
  unmountOnExit: PropTypes.bool,

  /**
   * Normally a component is not transitioned if it is shown when the `<Transition>` component mounts.
   * If you want to transition on the first mount set `appear` to `true`, and the
   * component will transition in as soon as the `<Transition>` mounts.
   *
   * > Note: there are no specific "appear" states. `appear` only adds an additional `enter` transition.
   */
  appear: PropTypes.bool,

  /**
   * Enable or disable enter transitions.
   */
  enter: PropTypes.bool,

  /**
   * Enable or disable exit transitions.
   */
  exit: PropTypes.bool,

  /**
   * The duration of the transition, in milliseconds.
   * Required unless `addEventListener` is provided
   *
   * You may specify a single timeout for all transitions like: `timeout={500}`,
   * or individually like:
   *
   * ```jsx
   * timeout={{
   *  enter: 300,
   *  exit: 500,
   * }}
   * ```
   *
   * @type {number | { enter?: number, exit?: number }}
   */
  timeout: function timeout(props) {
    for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
      args[_key - 1] = arguments[_key];
    }

    var pt = _PropTypes.timeoutsShape;
    if (!props.addEndListener) pt = pt.isRequired;
    return pt.apply(undefined, [props].concat(args));
  },

  /**
   * Add a custom transition end trigger. Called with the transitioning
   * DOM node and a `done` callback. Allows for more fine grained transition end
   * logic. **Note:** Timeouts are still used as a fallback if provided.
   *
   * ```jsx
   * addEndListener={(node, done) => {
   *   // use the css transitionend event to mark the finish of a transition
   *   node.addEventListener('transitionend', done, false);
   * }}
   * ```
   */
  addEndListener: PropTypes.func,

  /**
   * Callback fired before the "entering" status is applied. An extra parameter
   * `isAppearing` is supplied to indicate if the enter stage is occurring on the initial mount
   *
   * @type Function(node: HtmlElement, isAppearing: bool) -> void
   */
  onEnter: PropTypes.func,

  /**
   * Callback fired after the "entering" status is applied. An extra parameter
   * `isAppearing` is supplied to indicate if the enter stage is occurring on the initial mount
   *
   * @type Function(node: HtmlElement, isAppearing: bool)
   */
  onEntering: PropTypes.func,

  /**
   * Callback fired after the "entered" status is applied. An extra parameter
   * `isAppearing` is supplied to indicate if the enter stage is occurring on the initial mount
   *
   * @type Function(node: HtmlElement, isAppearing: bool) -> void
   */
  onEntered: PropTypes.func,

  /**
   * Callback fired before the "exiting" status is applied.
   *
   * @type Function(node: HtmlElement) -> void
   */
  onExit: PropTypes.func,

  /**
   * Callback fired after the "exiting" status is applied.
   *
   * @type Function(node: HtmlElement) -> void
   */
  onExiting: PropTypes.func,

  /**
   * Callback fired after the "exited" status is applied.
   *
   * @type Function(node: HtmlElement) -> void
   */
  onExited: PropTypes.func
} : {};

// Name the function so it is clearer in the documentation
function noop() {}

Transition.defaultProps = {
  in: false,
  mountOnEnter: false,
  unmountOnExit: false,
  appear: false,
  enter: true,
  exit: true,

  onEnter: noop,
  onEntering: noop,
  onEntered: noop,

  onExit: noop,
  onExiting: noop,
  onExited: noop
};

Transition.UNMOUNTED = 0;
Transition.EXITED = 1;
Transition.ENTERING = 2;
Transition.ENTERED = 3;
Transition.EXITING = 4;

exports.default = Transition;

/***/ }),
/* 334 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
// 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 punycode = __webpack_require__(916);
var util = __webpack_require__(336);

exports.parse = urlParse;
exports.resolve = urlResolve;
exports.resolveObject = urlResolveObject;
exports.format = urlFormat;

exports.Url = Url;

function Url() {
  this.protocol = null;
  this.slashes = null;
  this.auth = null;
  this.host = null;
  this.port = null;
  this.hostname = null;
  this.hash = null;
  this.search = null;
  this.query = null;
  this.pathname = null;
  this.path = null;
  this.href = null;
}

// Reference: RFC 3986, RFC 1808, RFC 2396

// define these here so at least they only have to be
// compiled once on the first module load.
var protocolPattern = /^([a-z0-9.+-]+:)/i,
    portPattern = /:[0-9]*$/,

    // Special case for a simple path URL
    simplePathPattern = /^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/,

    // RFC 2396: characters reserved for delimiting URLs.
    // We actually just auto-escape these.
    delims = ['<', '>', '"', '`', ' ', '\r', '\n', '\t'],

    // RFC 2396: characters not allowed for various reasons.
    unwise = ['{', '}', '|', '\\', '^', '`'].concat(delims),

    // Allowed by RFCs, but cause of XSS attacks.  Always escape these.
    autoEscape = ['\''].concat(unwise),
    // Characters that are never ever allowed in a hostname.
    // Note that any invalid chars are also handled, but these
    // are the ones that are *expected* to be seen, so we fast-path
    // them.
    nonHostChars = ['%', '/', '?', ';', '#'].concat(autoEscape),
    hostEndingChars = ['/', '?', '#'],
    hostnameMaxLen = 255,
    hostnamePartPattern = /^[+a-z0-9A-Z_-]{0,63}$/,
    hostnamePartStart = /^([+a-z0-9A-Z_-]{0,63})(.*)$/,
    // protocols that can allow "unsafe" and "unwise" chars.
    unsafeProtocol = {
      'javascript': true,
      'javascript:': true
    },
    // protocols that never have a hostname.
    hostlessProtocol = {
      'javascript': true,
      'javascript:': true
    },
    // protocols that always contain a // bit.
    slashedProtocol = {
      'http': true,
      'https': true,
      'ftp': true,
      'gopher': true,
      'file': true,
      'http:': true,
      'https:': true,
      'ftp:': true,
      'gopher:': true,
      'file:': true
    },
    querystring = __webpack_require__(66);

function urlParse(url, parseQueryString, slashesDenoteHost) {
  if (url && util.isObject(url) && url instanceof Url) return url;

  var u = new Url;
  u.parse(url, parseQueryString, slashesDenoteHost);
  return u;
}

Url.prototype.parse = function(url, parseQueryString, slashesDenoteHost) {
  if (!util.isString(url)) {
    throw new TypeError("Parameter 'url' must be a string, not " + typeof url);
  }

  // Copy chrome, IE, opera backslash-handling behavior.
  // Back slashes before the query string get converted to forward slashes
  // See: https://code.google.com/p/chromium/issues/detail?id=25916
  var queryIndex = url.indexOf('?'),
      splitter =
          (queryIndex !== -1 && queryIndex < url.indexOf('#')) ? '?' : '#',
      uSplit = url.split(splitter),
      slashRegex = /\\/g;
  uSplit[0] = uSplit[0].replace(slashRegex, '/');
  url = uSplit.join(splitter);

  var rest = url;

  // trim before proceeding.
  // This is to support parse stuff like "  http://foo.com  \n"
  rest = rest.trim();

  if (!slashesDenoteHost && url.split('#').length === 1) {
    // Try fast path regexp
    var simplePath = simplePathPattern.exec(rest);
    if (simplePath) {
      this.path = rest;
      this.href = rest;
      this.pathname = simplePath[1];
      if (simplePath[2]) {
        this.search = simplePath[2];
        if (parseQueryString) {
          this.query = querystring.parse(this.search.substr(1));
        } else {
          this.query = this.search.substr(1);
        }
      } else if (parseQueryString) {
        this.search = '';
        this.query = {};
      }
      return this;
    }
  }

  var proto = protocolPattern.exec(rest);
  if (proto) {
    proto = proto[0];
    var lowerProto = proto.toLowerCase();
    this.protocol = lowerProto;
    rest = rest.substr(proto.length);
  }

  // figure out if it's got a host
  // user@server is *always* interpreted as a hostname, and url
  // resolution will treat //foo/bar as host=foo,path=bar because that's
  // how the browser resolves relative URLs.
  if (slashesDenoteHost || proto || rest.match(/^\/\/[^@\/]+@[^@\/]+/)) {
    var slashes = rest.substr(0, 2) === '//';
    if (slashes && !(proto && hostlessProtocol[proto])) {
      rest = rest.substr(2);
      this.slashes = true;
    }
  }

  if (!hostlessProtocol[proto] &&
      (slashes || (proto && !slashedProtocol[proto]))) {

    // there's a hostname.
    // the first instance of /, ?, ;, or # ends the host.
    //
    // If there is an @ in the hostname, then non-host chars *are* allowed
    // to the left of the last @ sign, unless some host-ending character
    // comes *before* the @-sign.
    // URLs are obnoxious.
    //
    // ex:
    // http://a@b@c/ => user:a@b host:c
    // http://a@b?@c => user:a host:c path:/?@c

    // v0.12 TODO(isaacs): This is not quite how Chrome does things.
    // Review our test case against browsers more comprehensively.

    // find the first instance of any hostEndingChars
    var hostEnd = -1;
    for (var i = 0; i < hostEndingChars.length; i++) {
      var hec = rest.indexOf(hostEndingChars[i]);
      if (hec !== -1 && (hostEnd === -1 || hec < hostEnd))
        hostEnd = hec;
    }

    // at this point, either we have an explicit point where the
    // auth portion cannot go past, or the last @ char is the decider.
    var auth, atSign;
    if (hostEnd === -1) {
      // atSign can be anywhere.
      atSign = rest.lastIndexOf('@');
    } else {
      // atSign must be in auth portion.
      // http://a@b/c@d => host:b auth:a path:/c@d
      atSign = rest.lastIndexOf('@', hostEnd);
    }

    // Now we have a portion which is definitely the auth.
    // Pull that off.
    if (atSign !== -1) {
      auth = rest.slice(0, atSign);
      rest = rest.slice(atSign + 1);
      this.auth = decodeURIComponent(auth);
    }

    // the host is the remaining to the left of the first non-host char
    hostEnd = -1;
    for (var i = 0; i < nonHostChars.length; i++) {
      var hec = rest.indexOf(nonHostChars[i]);
      if (hec !== -1 && (hostEnd === -1 || hec < hostEnd))
        hostEnd = hec;
    }
    // if we still have not hit it, then the entire thing is a host.
    if (hostEnd === -1)
      hostEnd = rest.length;

    this.host = rest.slice(0, hostEnd);
    rest = rest.slice(hostEnd);

    // pull out port.
    this.parseHost();

    // we've indicated that there is a hostname,
    // so even if it's empty, it has to be present.
    this.hostname = this.hostname || '';

    // if hostname begins with [ and ends with ]
    // assume that it's an IPv6 address.
    var ipv6Hostname = this.hostname[0] === '[' &&
        this.hostname[this.hostname.length - 1] === ']';

    // validate a little.
    if (!ipv6Hostname) {
      var hostparts = this.hostname.split(/\./);
      for (var i = 0, l = hostparts.length; i < l; i++) {
        var part = hostparts[i];
        if (!part) continue;
        if (!part.match(hostnamePartPattern)) {
          var newpart = '';
          for (var j = 0, k = part.length; j < k; j++) {
            if (part.charCodeAt(j) > 127) {
              // we replace non-ASCII char with a temporary placeholder
              // we need this to make sure size of hostname is not
              // broken by replacing non-ASCII by nothing
              newpart += 'x';
            } else {
              newpart += part[j];
            }
          }
          // we test again with ASCII char only
          if (!newpart.match(hostnamePartPattern)) {
            var validParts = hostparts.slice(0, i);
            var notHost = hostparts.slice(i + 1);
            var bit = part.match(hostnamePartStart);
            if (bit) {
              validParts.push(bit[1]);
              notHost.unshift(bit[2]);
            }
            if (notHost.length) {
              rest = '/' + notHost.join('.') + rest;
            }
            this.hostname = validParts.join('.');
            break;
          }
        }
      }
    }

    if (this.hostname.length > hostnameMaxLen) {
      this.hostname = '';
    } else {
      // hostnames are always lower case.
      this.hostname = this.hostname.toLowerCase();
    }

    if (!ipv6Hostname) {
      // IDNA Support: Returns a punycoded representation of "domain".
      // It only converts parts of the domain name that
      // have non-ASCII characters, i.e. it doesn't matter if
      // you call it with a domain that already is ASCII-only.
      this.hostname = punycode.toASCII(this.hostname);
    }

    var p = this.port ? ':' + this.port : '';
    var h = this.hostname || '';
    this.host = h + p;
    this.href += this.host;

    // strip [ and ] from the hostname
    // the host field still retains them, though
    if (ipv6Hostname) {
      this.hostname = this.hostname.substr(1, this.hostname.length - 2);
      if (rest[0] !== '/') {
        rest = '/' + rest;
      }
    }
  }

  // now rest is set to the post-host stuff.
  // chop off any delim chars.
  if (!unsafeProtocol[lowerProto]) {

    // First, make 100% sure that any "autoEscape" chars get
    // escaped, even if encodeURIComponent doesn't think they
    // need to be.
    for (var i = 0, l = autoEscape.length; i < l; i++) {
      var ae = autoEscape[i];
      if (rest.indexOf(ae) === -1)
        continue;
      var esc = encodeURIComponent(ae);
      if (esc === ae) {
        esc = escape(ae);
      }
      rest = rest.split(ae).join(esc);
    }
  }


  // chop off from the tail first.
  var hash = rest.indexOf('#');
  if (hash !== -1) {
    // got a fragment string.
    this.hash = rest.substr(hash);
    rest = rest.slice(0, hash);
  }
  var qm = rest.indexOf('?');
  if (qm !== -1) {
    this.search = rest.substr(qm);
    this.query = rest.substr(qm + 1);
    if (parseQueryString) {
      this.query = querystring.parse(this.query);
    }
    rest = rest.slice(0, qm);
  } else if (parseQueryString) {
    // no query string, but parseQueryString still requested
    this.search = '';
    this.query = {};
  }
  if (rest) this.pathname = rest;
  if (slashedProtocol[lowerProto] &&
      this.hostname && !this.pathname) {
    this.pathname = '/';
  }

  //to support http.request
  if (this.pathname || this.search) {
    var p = this.pathname || '';
    var s = this.search || '';
    this.path = p + s;
  }

  // finally, reconstruct the href based on what has been validated.
  this.href = this.format();
  return this;
};

// format a parsed object into a url string
function urlFormat(obj) {
  // ensure it's an object, and not a string url.
  // If it's an obj, this is a no-op.
  // this way, you can call url_format() on strings
  // to clean up potentially wonky urls.
  if (util.isString(obj)) obj = urlParse(obj);
  if (!(obj instanceof Url)) return Url.prototype.format.call(obj);
  return obj.format();
}

Url.prototype.format = function() {
  var auth = this.auth || '';
  if (auth) {
    auth = encodeURIComponent(auth);
    auth = auth.replace(/%3A/i, ':');
    auth += '@';
  }

  var protocol = this.protocol || '',
      pathname = this.pathname || '',
      hash = this.hash || '',
      host = false,
      query = '';

  if (this.host) {
    host = auth + this.host;
  } else if (this.hostname) {
    host = auth + (this.hostname.indexOf(':') === -1 ?
        this.hostname :
        '[' + this.hostname + ']');
    if (this.port) {
      host += ':' + this.port;
    }
  }

  if (this.query &&
      util.isObject(this.query) &&
      Object.keys(this.query).length) {
    query = querystring.stringify(this.query);
  }

  var search = this.search || (query && ('?' + query)) || '';

  if (protocol && protocol.substr(-1) !== ':') protocol += ':';

  // only the slashedProtocols get the //.  Not mailto:, xmpp:, etc.
  // unless they had them to begin with.
  if (this.slashes ||
      (!protocol || slashedProtocol[protocol]) && host !== false) {
    host = '//' + (host || '');
    if (pathname && pathname.charAt(0) !== '/') pathname = '/' + pathname;
  } else if (!host) {
    host = '';
  }

  if (hash && hash.charAt(0) !== '#') hash = '#' + hash;
  if (search && search.charAt(0) !== '?') search = '?' + search;

  pathname = pathname.replace(/[?#]/g, function(match) {
    return encodeURIComponent(match);
  });
  search = search.replace('#', '%23');

  return protocol + host + pathname + search + hash;
};

function urlResolve(source, relative) {
  return urlParse(source, false, true).resolve(relative);
}

Url.prototype.resolve = function(relative) {
  return this.resolveObject(urlParse(relative, false, true)).format();
};

function urlResolveObject(source, relative) {
  if (!source) return relative;
  return urlParse(source, false, true).resolveObject(relative);
}

Url.prototype.resolveObject = function(relative) {
  if (util.isString(relative)) {
    var rel = new Url();
    rel.parse(relative, false, true);
    relative = rel;
  }

  var result = new Url();
  var tkeys = Object.keys(this);
  for (var tk = 0; tk < tkeys.length; tk++) {
    var tkey = tkeys[tk];
    result[tkey] = this[tkey];
  }

  // hash is always overridden, no matter what.
  // even href="" will remove it.
  result.hash = relative.hash;

  // if the relative url is empty, then there's nothing left to do here.
  if (relative.href === '') {
    result.href = result.format();
    return result;
  }

  // hrefs like //foo/bar always cut to the protocol.
  if (relative.slashes && !relative.protocol) {
    // take everything except the protocol from relative
    var rkeys = Object.keys(relative);
    for (var rk = 0; rk < rkeys.length; rk++) {
      var rkey = rkeys[rk];
      if (rkey !== 'protocol')
        result[rkey] = relative[rkey];
    }

    //urlParse appends trailing / to urls like http://www.example.com
    if (slashedProtocol[result.protocol] &&
        result.hostname && !result.pathname) {
      result.path = result.pathname = '/';
    }

    result.href = result.format();
    return result;
  }

  if (relative.protocol && relative.protocol !== result.protocol) {
    // if it's a known url protocol, then changing
    // the protocol does weird things
    // first, if it's not file:, then we MUST have a host,
    // and if there was a path
    // to begin with, then we MUST have a path.
    // if it is file:, then the host is dropped,
    // because that's known to be hostless.
    // anything else is assumed to be absolute.
    if (!slashedProtocol[relative.protocol]) {
      var keys = Object.keys(relative);
      for (var v = 0; v < keys.length; v++) {
        var k = keys[v];
        result[k] = relative[k];
      }
      result.href = result.format();
      return result;
    }

    result.protocol = relative.protocol;
    if (!relative.host && !hostlessProtocol[relative.protocol]) {
      var relPath = (relative.pathname || '').split('/');
      while (relPath.length && !(relative.host = relPath.shift()));
      if (!relative.host) relative.host = '';
      if (!relative.hostname) relative.hostname = '';
      if (relPath[0] !== '') relPath.unshift('');
      if (relPath.length < 2) relPath.unshift('');
      result.pathname = relPath.join('/');
    } else {
      result.pathname = relative.pathname;
    }
    result.search = relative.search;
    result.query = relative.query;
    result.host = relative.host || '';
    result.auth = relative.auth;
    result.hostname = relative.hostname || relative.host;
    result.port = relative.port;
    // to support http.request
    if (result.pathname || result.search) {
      var p = result.pathname || '';
      var s = result.search || '';
      result.path = p + s;
    }
    result.slashes = result.slashes || relative.slashes;
    result.href = result.format();
    return result;
  }

  var isSourceAbs = (result.pathname && result.pathname.charAt(0) === '/'),
      isRelAbs = (
          relative.host ||
          relative.pathname && relative.pathname.charAt(0) === '/'
      ),
      mustEndAbs = (isRelAbs || isSourceAbs ||
                    (result.host && relative.pathname)),
      removeAllDots = mustEndAbs,
      srcPath = result.pathname && result.pathname.split('/') || [],
      relPath = relative.pathname && relative.pathname.split('/') || [],
      psychotic = result.protocol && !slashedProtocol[result.protocol];

  // if the url is a non-slashed url, then relative
  // links like ../.. should be able
  // to crawl up to the hostname, as well.  This is strange.
  // result.protocol has already been set by now.
  // Later on, put the first path part into the host field.
  if (psychotic) {
    result.hostname = '';
    result.port = null;
    if (result.host) {
      if (srcPath[0] === '') srcPath[0] = result.host;
      else srcPath.unshift(result.host);
    }
    result.host = '';
    if (relative.protocol) {
      relative.hostname = null;
      relative.port = null;
      if (relative.host) {
        if (relPath[0] === '') relPath[0] = relative.host;
        else relPath.unshift(relative.host);
      }
      relative.host = null;
    }
    mustEndAbs = mustEndAbs && (relPath[0] === '' || srcPath[0] === '');
  }

  if (isRelAbs) {
    // it's absolute.
    result.host = (relative.host || relative.host === '') ?
                  relative.host : result.host;
    result.hostname = (relative.hostname || relative.hostname === '') ?
                      relative.hostname : result.hostname;
    result.search = relative.search;
    result.query = relative.query;
    srcPath = relPath;
    // fall through to the dot-handling below.
  } else if (relPath.length) {
    // it's relative
    // throw away the existing file, and take the new path instead.
    if (!srcPath) srcPath = [];
    srcPath.pop();
    srcPath = srcPath.concat(relPath);
    result.search = relative.search;
    result.query = relative.query;
  } else if (!util.isNullOrUndefined(relative.search)) {
    // just pull out the search.
    // like href='?foo'.
    // Put this after the other two cases because it simplifies the booleans
    if (psychotic) {
      result.hostname = result.host = srcPath.shift();
      //occationaly the auth can get stuck only in host
      //this especially happens in cases like
      //url.resolveObject('mailto:local1@domain1', 'local2@domain2')
      var authInHost = result.host && result.host.indexOf('@') > 0 ?
                       result.host.split('@') : false;
      if (authInHost) {
        result.auth = authInHost.shift();
        result.host = result.hostname = authInHost.shift();
      }
    }
    result.search = relative.search;
    result.query = relative.query;
    //to support http.request
    if (!util.isNull(result.pathname) || !util.isNull(result.search)) {
      result.path = (result.pathname ? result.pathname : '') +
                    (result.search ? result.search : '');
    }
    result.href = result.format();
    return result;
  }

  if (!srcPath.length) {
    // no path at all.  easy.
    // we've already handled the other stuff above.
    result.pathname = null;
    //to support http.request
    if (result.search) {
      result.path = '/' + result.search;
    } else {
      result.path = null;
    }
    result.href = result.format();
    return result;
  }

  // if a url ENDs in . or .., then it must get a trailing slash.
  // however, if it ends in anything else non-slashy,
  // then it must NOT get a trailing slash.
  var last = srcPath.slice(-1)[0];
  var hasTrailingSlash = (
      (result.host || relative.host || srcPath.length > 1) &&
      (last === '.' || last === '..') || last === '');

  // strip single dots, resolve double dots to parent dir
  // if the path tries to go above the root, `up` ends up > 0
  var up = 0;
  for (var i = srcPath.length; i >= 0; i--) {
    last = srcPath[i];
    if (last === '.') {
      srcPath.splice(i, 1);
    } else if (last === '..') {
      srcPath.splice(i, 1);
      up++;
    } else if (up) {
      srcPath.splice(i, 1);
      up--;
    }
  }

  // if the path is allowed to go above the root, restore leading ..s
  if (!mustEndAbs && !removeAllDots) {
    for (; up--; up) {
      srcPath.unshift('..');
    }
  }

  if (mustEndAbs && srcPath[0] !== '' &&
      (!srcPath[0] || srcPath[0].charAt(0) !== '/')) {
    srcPath.unshift('');
  }

  if (hasTrailingSlash && (srcPath.join('/').substr(-1) !== '/')) {
    srcPath.push('');
  }

  var isAbsolute = srcPath[0] === '' ||
      (srcPath[0] && srcPath[0].charAt(0) === '/');

  // put the host back
  if (psychotic) {
    result.hostname = result.host = isAbsolute ? '' :
                                    srcPath.length ? srcPath.shift() : '';
    //occationaly the auth can get stuck only in host
    //this especially happens in cases like
    //url.resolveObject('mailto:local1@domain1', 'local2@domain2')
    var authInHost = result.host && result.host.indexOf('@') > 0 ?
                     result.host.split('@') : false;
    if (authInHost) {
      result.auth = authInHost.shift();
      result.host = result.hostname = authInHost.shift();
    }
  }

  mustEndAbs = mustEndAbs || (result.host && srcPath.length);

  if (mustEndAbs && !isAbsolute) {
    srcPath.unshift('');
  }

  if (!srcPath.length) {
    result.pathname = null;
    result.path = null;
  } else {
    result.pathname = srcPath.join('/');
  }

  //to support request.http
  if (!util.isNull(result.pathname) || !util.isNull(result.search)) {
    result.path = (result.pathname ? result.pathname : '') +
                  (result.search ? result.search : '');
  }
  result.auth = relative.auth || result.auth;
  result.slashes = result.slashes || relative.slashes;
  result.href = result.format();
  return result;
};

Url.prototype.parseHost = function() {
  var host = this.host;
  var port = portPattern.exec(host);
  if (port) {
    port = port[0];
    if (port !== ':') {
      this.port = port.substr(1);
    }
    host = host.substr(0, host.length - port.length);
  }
  if (host) this.hostname = host;
};


/***/ }),
/* 335 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.classNamesShape = exports.timeoutsShape = undefined;
exports.transitionTimeout = transitionTimeout;

var _propTypes = __webpack_require__(20);

var _propTypes2 = _interopRequireDefault(_propTypes);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function transitionTimeout(transitionType) {
  var timeoutPropName = 'transition' + transitionType + 'Timeout';
  var enabledPropName = 'transition' + transitionType;

  return function (props) {
    // If the transition is enabled
    if (props[enabledPropName]) {
      // If no timeout duration is provided
      if (props[timeoutPropName] == null) {
        return new Error(timeoutPropName + ' wasn\'t supplied to CSSTransitionGroup: ' + 'this can cause unreliable animations and won\'t be supported in ' + 'a future version of React. See ' + 'https://fb.me/react-animation-transition-group-timeout for more ' + 'information.');

        // If the duration isn't a number
      } else if (typeof props[timeoutPropName] !== 'number') {
        return new Error(timeoutPropName + ' must be a number (in milliseconds)');
      }
    }

    return null;
  };
}

var timeoutsShape = exports.timeoutsShape = _propTypes2.default.oneOfType([_propTypes2.default.number, _propTypes2.default.shape({
  enter: _propTypes2.default.number,
  exit: _propTypes2.default.number
}).isRequired]);

var classNamesShape = exports.classNamesShape = _propTypes2.default.oneOfType([_propTypes2.default.string, _propTypes2.default.shape({
  enter: _propTypes2.default.string,
  exit: _propTypes2.default.string,
  active: _propTypes2.default.string
}), _propTypes2.default.shape({
  enter: _propTypes2.default.string,
  enterActive: _propTypes2.default.string,
  exit: _propTypes2.default.string,
  exitActive: _propTypes2.default.string
})]);

/***/ }),
/* 336 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


module.exports = {
  isString: function(arg) {
    return typeof(arg) === 'string';
  },
  isObject: function(arg) {
    return typeof(arg) === 'object' && arg !== null;
  },
  isNull: function(arg) {
    return arg === null;
  },
  isNullOrUndefined: function(arg) {
    return arg == null;
  }
};


/***/ }),
/* 337 */,
/* 338 */,
/* 339 */,
/* 340 */,
/* 341 */,
/* 342 */,
/* 343 */,
/* 344 */,
/* 345 */,
/* 346 */,
/* 347 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 viewBox=\"-1 73 16 11\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"><g id=\"Shape-Copy-3-+-Shape-Copy-4\" stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(0.000000, 74.000000)\"><path d=\"M0.749321284,4.16081709 L4.43130681,0.242526751 C4.66815444,-0.00952143591 5.06030999,-0.0211407611 5.30721074,0.216574262 C5.55411149,0.454289284 5.56226116,0.851320812 5.32541353,1.103369 L1.95384971,4.69131519 L5.48809879,8.09407556 C5.73499955,8.33179058 5.74314922,8.72882211 5.50630159,8.9808703 C5.26945396,9.23291849 4.87729841,9.24453781 4.63039766,9.00682279 L0.827097345,5.34502101 C0.749816996,5.31670099 0.677016974,5.27216098 0.613753508,5.21125118 C0.427367989,5.03179997 0.377040713,4.7615583 0.465458792,4.53143559 C0.492371834,4.43667624 0.541703274,4.34676528 0.613628034,4.27022448 C0.654709457,4.22650651 0.70046335,4.19002189 0.749321284,4.16081709 Z\" id=\"Shape-Copy-3\" stroke=\"#FFFFFF\" stroke-width=\"0.05\" fill=\"#DDE1E4\"></path><path d=\"M13.7119065,5.44453032 L9.77062746,9.09174784 C9.51677479,9.3266604 9.12476399,9.31089603 8.89504684,9.05653714 C8.66532968,8.80217826 8.68489539,8.40554539 8.93874806,8.17063283 L12.5546008,4.82456128 L9.26827469,1.18571135 C9.03855754,0.931352463 9.05812324,0.534719593 9.31197591,0.299807038 C9.56582858,0.0648944831 9.95783938,0.0806588502 10.1875565,0.335017737 L13.72891,4.25625178 C13.8013755,4.28980469 13.8684335,4.3382578 13.9254821,4.40142604 C14.0883019,4.58171146 14.1258883,4.83347168 14.0435812,5.04846202 C14.0126705,5.15680232 13.9526426,5.2583679 13.8641331,5.34027361 C13.8174417,5.38348136 13.7660763,5.41820853 13.7119065,5.44453032 Z\" id=\"Shape-Copy-4\" stroke=\"#FFFFFF\" stroke-width=\"0.05\" fill=\"#DDE1E4\"></path></g></svg>"

/***/ }),
/* 348 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" viewBox=\"0 0 16 16\"><path d=\"M8 13.4c-.5 0-.9-.2-1.2-.6L.4 5.2C0 4.7-.1 4.3.2 3.7S1 3 1.6 3h12.8c.6 0 1.2.1 1.4.7.3.6.2 1.1-.2 1.6l-6.4 7.6c-.3.4-.7.5-1.2.5z\"></path></svg>"

/***/ }),
/* 349 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\"><g fill-rule=\"evenodd\"><circle cx=\"8\" cy=\"8.5\" r=\"1.5\"></circle><path d=\"M15.498 8.28l-.001-.03v-.002-.004l-.002-.018-.004-.031c0-.002 0-.002 0 0l-.004-.035.006.082c-.037-.296-.133-.501-.28-.661-.4-.522-.915-1.042-1.562-1.604-1.36-1.182-2.74-1.975-4.178-2.309a6.544 6.544 0 0 0-2.755-.042c-.78.153-1.565.462-2.369.91C3.252 5.147 2.207 6 1.252 7.035c-.216.233-.36.398-.499.577-.338.437-.338 1 0 1.437.428.552.941 1.072 1.59 1.635 1.359 1.181 2.739 1.975 4.177 2.308.907.21 1.829.223 2.756.043.78-.153 1.564-.462 2.369-.91 1.097-.612 2.141-1.464 3.097-2.499.217-.235.36-.398.498-.578.12-.128.216-.334.248-.554 0 .01 0 .01-.008.04l.013-.079-.001.011.003-.031.001-.017v.005l.001-.02v.008l.002-.03.001-.05-.001-.044v-.004-.004zm-.954.045v.007l.001.004V8.33v.012l-.001.01v-.005-.005l.002-.015-.001.008c-.002.014-.002.014 0 0l-.007.084c.003-.057-.004-.041-.014-.031-.143.182-.27.327-.468.543-.89.963-1.856 1.752-2.86 2.311-.724.404-1.419.677-2.095.81a5.63 5.63 0 0 1-2.374-.036c-1.273-.295-2.523-1.014-3.774-2.101-.604-.525-1.075-1.001-1.457-1.496-.054-.07-.054-.107 0-.177.117-.152.244-.298.442-.512.89-.963 1.856-1.752 2.86-2.311.724-.404 1.419-.678 2.095-.81a5.631 5.631 0 0 1 2.374.036c1.272.295 2.523 1.014 3.774 2.101.603.524 1.074 1 1.457 1.496.035.041.043.057.046.076 0 .01 0 .01.008.043l-.009-.047.003.02-.002-.013v-.008.016c0-.004 0-.004 0 0v-.004z\"></path></g></svg>"

/***/ }),
/* 350 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" viewBox=\"0 0 60 12\"><path id=\"base-path\" d=\"M53.9,0H1C0.4,0,0,0.4,0,1v10c0,0.6,0.4,1,1,1h52.9c0.6,0,1.2-0.3,1.5-0.7L60,6l-4.4-5.3C55,0.3,54.5,0,53.9,0z\"></path></svg>"

/***/ }),
/* 351 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" viewBox=\"0 0 20 16\" stroke=\"none\" fillrule=\"evenodd\"><path d=\"M10.919,13 L9.463,13 C9.29966585,13 9.16550052,12.9591671 9.0605,12.8775 C8.95549947,12.7958329 8.8796669,12.6943339 8.833,12.573 L8.077,10.508 L3.884,10.508 L3.128,12.573 C3.09066648,12.6803339 3.01716722,12.7783329 2.9075,12.867 C2.79783279,12.9556671 2.66366746,13 2.505,13 L1.042,13 L5.018,2.878 L6.943,2.878 L10.919,13 Z M4.367,9.178 L7.594,9.178 L6.362,5.811 C6.30599972,5.66166592 6.24416701,5.48550102 6.1765,5.2825 C6.108833,5.07949898 6.04233366,4.85900119 5.977,4.621 C5.91166634,4.85900119 5.84750032,5.08066564 5.7845,5.286 C5.72149969,5.49133436 5.65966697,5.67099923 5.599,5.825 L4.367,9.178 Z M18.892,13 L18.115,13 C17.9516658,13 17.8233338,12.9755002 17.73,12.9265 C17.6366662,12.8774998 17.5666669,12.7783341 17.52,12.629 L17.366,12.118 C17.1839991,12.2813341 17.0055009,12.4248327 16.8305,12.5485 C16.6554991,12.6721673 16.4746676,12.7759996 16.288,12.86 C16.1013324,12.9440004 15.903001,13.0069998 15.693,13.049 C15.4829989,13.0910002 15.2496679,13.112 14.993,13.112 C14.6896651,13.112 14.4096679,13.0711671 14.153,12.9895 C13.896332,12.9078329 13.6758342,12.7853342 13.4915,12.622 C13.3071657,12.4586658 13.1636672,12.2556679 13.061,12.013 C12.9583328,11.7703321 12.907,11.4880016 12.907,11.166 C12.907,10.895332 12.9781659,10.628168 13.1205,10.3645 C13.262834,10.100832 13.499665,9.8628344 13.831,9.6505 C14.162335,9.43816561 14.6033306,9.2620007 15.154,9.122 C15.7046694,8.9819993 16.3883292,8.90266676 17.205,8.884 L17.205,8.464 C17.205,7.98333093 17.103501,7.62750116 16.9005,7.3965 C16.697499,7.16549885 16.4023352,7.05 16.015,7.05 C15.7349986,7.05 15.5016676,7.08266634 15.315,7.148 C15.1283324,7.21333366 14.9661673,7.28683292 14.8285,7.3685 C14.6908326,7.45016707 14.5636672,7.52366634 14.447,7.589 C14.3303327,7.65433366 14.2020007,7.687 14.062,7.687 C13.9453327,7.687 13.8450004,7.65666697 13.761,7.596 C13.6769996,7.53533303 13.6093336,7.46066711 13.558,7.372 L13.243,6.819 C14.0690041,6.06299622 15.0653275,5.685 16.232,5.685 C16.6520021,5.685 17.0264983,5.75383264 17.3555,5.8915 C17.6845016,6.02916736 17.9633322,6.22049877 18.192,6.4655 C18.4206678,6.71050122 18.5944994,7.00333163 18.7135,7.344 C18.8325006,7.68466837 18.892,8.05799797 18.892,8.464 L18.892,13 Z M15.532,11.922 C15.7093342,11.922 15.8726659,11.9056668 16.022,11.873 C16.1713341,11.8403332 16.3124993,11.7913337 16.4455,11.726 C16.5785006,11.6606663 16.7068327,11.5801671 16.8305,11.4845 C16.9541673,11.3888329 17.0789993,11.2756673 17.205,11.145 L17.205,9.934 C16.7009975,9.95733345 16.279835,10.0004997 15.9415,10.0635 C15.603165,10.1265003 15.3313343,10.2069995 15.126,10.305 C14.9206656,10.4030005 14.7748337,10.5173327 14.6885,10.648 C14.6021662,10.7786673 14.559,10.9209992 14.559,11.075 C14.559,11.3783349 14.6488324,11.5953327 14.8285,11.726 C15.0081675,11.8566673 15.2426652,11.922 15.532,11.922 L15.532,11.922 Z\"></path></svg>"

/***/ }),
/* 352 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 viewBox=\"0 0 6 6\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"><path d=\"M1.35191454,5.27895256 L5.31214367,1.35518468 C5.50830675,1.16082764 5.50977084,0.844248536 5.3154138,0.648085456 C5.12105677,0.451922377 4.80447766,0.450458288 4.60831458,0.644815324 L0.648085456,4.56858321 C0.451922377,4.76294025 0.450458288,5.07951935 0.644815324,5.27568243 C0.83917236,5.47184551 1.15575146,5.4733096 1.35191454,5.27895256 L1.35191454,5.27895256 Z\" id=\"Line\" stroke=\"none\" fill=\"#696969\" fill-rule=\"evenodd\"></path><path d=\"M5.31214367,4.56858321 L1.35191454,0.644815324 C1.15575146,0.450458288 0.83917236,0.451922377 0.644815324,0.648085456 C0.450458288,0.844248536 0.451922377,1.16082764 0.648085456,1.35518468 L4.60831458,5.27895256 C4.80447766,5.4733096 5.12105677,5.47184551 5.3154138,5.27568243 C5.50977084,5.07951935 5.50830675,4.76294025 5.31214367,4.56858321 L5.31214367,4.56858321 Z\" id=\"Line-Copy-2\" stroke=\"none\" fill=\"#696969\" fill-rule=\"evenodd\"></path></svg>"

/***/ }),
/* 353 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M9.05 4.634l-2.144.003-.116.116v1.445l.92.965.492.034.116-.116v-.617L9.13 5.7l.035-.95M12.482 10.38l-1.505-1.462H9.362l-.564.516-.034 1.108.72.768 1.323.034-.117-.116v1.2l.972 1.02.315.034.116-.116v-1.154l.422-.374.034-.927-.117.117h.26l.408-.36V10.5l-.125-.124-.575-.033\"></path><path d=\"M8.47 15.073c-3.088 0-5.6-2.513-5.6-5.602V9.4v-.003c0-.018 0-.018.002-.034l.182-.088.724.587.49.033.497.543-.034.9.317.383h.47l.114.096-.032 1.9.524.553h.105l.025-.338 1.004-.95.054-.474.53-.462v-.888l-.588-.038-1.118-1.155H4.48l-.154-.09V9.01l.155-.1h1.164v-.273l.12-.115.7.033.494-.443.034-.746-.624-.655h-.724v.28l-.11.07H4.64l-.114-.09.025-.64.48-.43v-.244h-.382c-.102 0-.152-.128-.08-.2 1.04-1.01 2.428-1.59 3.903-1.59 1.374 0 2.672.5 3.688 1.39.08.068.03.198-.075.198l-1.144-.034-.81.803.52.523v.16l-.382.388h-.158l-.176-.177v-.16l.076-.074-.252-.252-.37.362.53.53c.072.072.005.194-.096.194l-.752-.005v.844h.783L9.885 8l.16-.143h.16l.62.61v.267l.58.027.003.002V8.76l.18-.03 1.234 1.24.753-.708h.382l.116.108c0 .02.003.016.003.036v.065c0 3.09-2.515 5.603-5.605 5.603M8.47 3C4.904 3 2 5.903 2 9.47c0 3.57 2.903 6.472 6.47 6.472 3.57 0 6.472-2.903 6.472-6.47C14.942 5.9 12.04 3 8.472 3\"></path></svg>"

/***/ }),
/* 354 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M4 2v12h9V4.775L9.888 2H4zm0-1h5.888c.246 0 .483.09.666.254l3.112 2.774c.212.19.334.462.334.747V14c0 .552-.448 1-1 1H4c-.552 0-1-.448-1-1V2c0-.552.448-1 1-1z\"></path><path d=\"M9 1.5v4c0 .325.306.564.62.485l4-1c.27-.067.432-.338.365-.606-.067-.27-.338-.432-.606-.365l-4 1L10 5.5v-4c0-.276-.224-.5-.5-.5s-.5.224-.5.5z\"></path></svg>"

/***/ }),
/* 355 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M2 5.193v7.652c0 .003-.002 0 .007 0H14v-7.69c0-.003.002 0-.007 0h-7.53v-2.15c0-.002-.004-.005-.01-.005H2.01C2 3 2 3 2 3.005V5.193zm-1 0V3.005C1 2.45 1.444 2 2.01 2h4.442c.558 0 1.01.45 1.01 1.005v1.15h6.53c.557 0 1.008.44 1.008 1v7.69c0 .553-.45 1-1.007 1H2.007c-.556 0-1.007-.44-1.007-1V5.193zM6.08 4.15H2v1h4.46v-1h-.38z\" fill-rule=\"evenodd\"></path></svg>"

/***/ }),
/* 356 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 viewBox=\"14 6 13 12\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"><g id=\"world\" stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(14.000000, 6.000000)\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M6.35076107,0.354 C3.25095418,0.354 0.729,2.87582735 0.729,5.9758879 C0.729,9.07544113 3.25082735,11.5972685 6.35076107,11.5972685 C9.45044113,11.5972685 11.9723953,9.07544113 11.9723953,5.97576107 C11.9723953,2.87582735 9.45044113,0.354 6.35076107,0.354 L6.35076107,0.354 Z M6.35076107,10.8289121 C3.67445071,10.8289121 1.49722956,8.65181776 1.49722956,5.97576107 C1.49722956,5.9443064 1.49900522,5.91335907 1.49976622,5.88215806 L2.20090094,6.4213266 L2.56313696,6.4213266 L2.97268183,6.8306178 L2.97268183,7.68217686 L3.32324919,8.03287105 L3.73926255,8.03287105 L3.73926255,9.79940584 L4.27386509,10.3361645 L4.4591686,10.3361645 L4.4591686,10.000183 L5.37655417,9.08343163 L5.37655417,8.73400577 L5.85585737,8.25203907 L5.85585737,7.37206934 L5.32518666,7.37206934 L4.28439226,6.33140176 L2.82225748,6.33140176 L2.82225748,5.56938704 L3.96286973,5.56938704 L3.96286973,5.23949352 L4.65068695,5.23949352 L5.11477015,4.77667865 L5.11477015,4.03001076 L4.49087694,3.40662489 L3.75359472,3.40662489 L3.75359472,3.78725175 L2.96228149,3.78725175 L2.96228149,3.28385021 L3.42217919,2.82319151 L3.42217919,2.49786399 L2.97001833,2.49786399 C3.84466106,1.64744643 5.03714814,1.12222956 6.35063424,1.12222956 C7.57292716,1.12222956 8.69020207,1.57730759 9.54442463,2.32587797 L8.46164839,2.32587797 L7.680355,3.10666403 L8.21508437,3.64088607 L7.87238068,3.98257509 L7.7165025,3.82669692 L7.85297518,3.68946324 L7.78930484,3.62566607 L7.78943167,3.62566607 L7.56011699,3.39559038 L7.55986332,3.39571722 L7.49758815,3.33318838 L7.01904595,3.78585658 L7.55910232,4.32654712 L6.8069806,4.32198112 L6.8069806,5.25864535 L7.66716433,5.25864535 L7.6723645,4.72112565 L7.81289584,4.57996014 L8.31819988,5.08653251 L8.31819988,5.41921636 L9.00703176,5.41921636 L9.03366676,5.39321553 L9.03430093,5.39194719 L10.195587,6.55259911 L10.8637451,5.88520206 L11.2018828,5.88520206 C11.2023901,5.9153884 11.2041658,5.94532107 11.2041658,5.97563424 C11.2040389,8.65181776 9.0269446,10.8289121 6.35076107,10.8289121 L6.35076107,10.8289121 Z\" id=\"Shape\" stroke=\"#DDE1E5\" stroke-width=\"0.25\" fill=\"#DDE1E5\"></path><polygon id=\"Shape\" stroke=\"#DDE1E5\" stroke-width=\"0.25\" fill=\"#DDE1E5\" points=\"6.50676608 1.61523076 4.52892694 1.61789426 4.52892694 2.95192735 5.34560683 3.76733891 5.72496536 3.76733891 5.72496536 3.1967157 6.50676608 2.41592965\"></polygon><polygon id=\"Shape\" stroke=\"#DDE1E5\" stroke-width=\"0.25\" fill=\"#DDE1E5\" points=\"9.59959714 6.88718547 8.28623788 5.57268471 8.28623788 5.57002121 6.79607294 5.57002121 6.35101474 6.01469891 6.35101474 6.96201714 6.98429362 7.59466185 8.12909136 7.59466185 8.12909136 8.70343893 8.99434843 9.56882283 9.20971144 9.56882283 9.20971144 8.50329592 9.63029081 8.08271655 9.63029081 7.3026915 9.87025949 7.3026915 10.1711082 7.00082814 10.0558167 6.88718547\"></polygon></g></svg>"

/***/ }),
/* 357 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 16 16\"><path class=\"st0\" d=\"M9 9.3l3.6 3.6\"></path><ellipse fill=\"transparent\" cx=\"5.9\" cy=\"6.2\" rx=\"4.5\" ry=\"4.5\"></ellipse></svg>"

/***/ }),
/* 358 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\"><g fill-rule=\"evenodd\"><path d=\"M5 12.503l.052-9a.5.5 0 0 0-1-.006l-.052 9a.5.5 0 0 0 1 .006zM12 12.497l-.05-9A.488.488 0 0 0 11.474 3a.488.488 0 0 0-.473.503l.05 9a.488.488 0 0 0 .477.497.488.488 0 0 0 .473-.503z\"></path></g></svg>"

/***/ }),
/* 359 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M10.483 13.995H5.517l-3.512-3.512V5.516l3.512-3.512h4.966l3.512 3.512v4.967l-3.512 3.512zm4.37-9.042l-3.807-3.805A.503.503 0 0 0 10.691 1H5.309a.503.503 0 0 0-.356.148L1.147 4.953A.502.502 0 0 0 1 5.308v5.383c0 .134.053.262.147.356l3.806 3.806a.503.503 0 0 0 .356.147h5.382a.503.503 0 0 0 .355-.147l3.806-3.806A.502.502 0 0 0 15 10.69V5.308a.502.502 0 0 0-.147-.355z\"></path><path d=\"M10 10.5a.5.5 0 1 0 1 0v-5a.5.5 0 1 0-1 0v5zM5 10.5a.5.5 0 1 0 1 0v-5a.5.5 0 0 0-1 0v5z\"></path></svg>"

/***/ }),
/* 360 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M8.5 8.5V14a.5.5 0 1 1-1 0V8.5H2a.5.5 0 0 1 0-1h5.5V2a.5.5 0 0 1 1 0v5.5H14a.5.5 0 1 1 0 1H8.5z\" fill-rule=\"evenodd\"></path></svg>"

/***/ }),
/* 361 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M4.525 13.21h-.472c-.574 0-.987-.154-1.24-.463-.253-.31-.38-.882-.38-1.719v-.573c0-.746-.097-1.265-.292-1.557-.196-.293-.51-.44-.945-.44v-.974c.435 0 .75-.146.945-.44.195-.292.293-.811.293-1.556v-.58c0-.833.126-1.404.379-1.712.253-.31.666-.464 1.24-.464h.472v.783h-.179c-.37 0-.628.08-.774.24-.145.159-.218.54-.218 1.141v.383c0 .824-.096 1.432-.287 1.823-.191.39-.516.679-.974.866.458.191.783.482.974.873.191.39.287.998.287 1.823v.382c0 .602.073.982.218 1.142.146.16.404.239.774.239h.18v.783zm9.502-4.752c-.43 0-.744.147-.942.44-.197.292-.296.811-.296 1.557v.573c0 .837-.125 1.41-.376 1.719-.251.309-.664.463-1.237.463h-.478v-.783h.185c.37 0 .628-.08.774-.24.145-.159.218-.539.218-1.14v-.383c0-.825.096-1.433.287-1.823.191-.39.516-.682.974-.873-.458-.187-.783-.476-.974-.866-.191-.391-.287-.999-.287-1.823v-.383c0-.602-.073-.982-.218-1.142-.146-.159-.404-.239-.774-.239h-.185v-.783h.478c.573 0 .986.155 1.237.464.25.308.376.88.376 1.712v.58c0 .673.088 1.174.263 1.503.176.329.5.493.975.493v.974z\" fill-rule=\"evenodd\"></path></svg>"

/***/ }),
/* 362 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" viewBox=\"0 0 20 16\" stroke=\"none\" fillrule=\"evenodd\"><rect x=\"3\" y=\"10\" width=\"3\" height=\"3\" rx=\"1\"></rect><rect x=\"12\" y=\"3\" width=\"2\" height=\"9\" rx=\"1\"></rect><rect transform=\"translate(13.000000, 7.500000) rotate(60.000000) translate(-13.000000, -7.500000) \" x=\"12\" y=\"3\" width=\"2\" height=\"9\" rx=\"1\"></rect><rect transform=\"translate(13.000000, 7.500000) rotate(-60.000000) translate(-13.000000, -7.500000) \" x=\"12\" y=\"3\" width=\"2\" height=\"9\" rx=\"1\"></rect></svg>"

/***/ }),
/* 363 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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\" xmlns:svg=\"http://www.w3.org/2000/svg\"><path fill=\"black\" id=\"svg_1\" fill-rule=\"evenodd\" d=\"m4.55195,12.97461l7.4,-5l-7.4,-5l0,10zm-0.925,0l0,-10c0,-0.785 0.8,-1.264 1.415,-0.848l7.4,5c0.58,0.392 0.58,1.304 0,1.696l-7.4,5c-0.615,0.416 -1.415,-0.063 -1.415,-0.848z\"></path></svg>"

/***/ }),
/* 364 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" viewBox=\"0 0 33 12\"><path id=\"base-path\" d=\"M27.1,0H1C0.4,0,0,0.4,0,1v10c0,0.6,0.4,1,1,1h26.1 c0.6,0,1.2-0.3,1.5-0.7L33,6l-4.4-5.3C28.2,0.3,27.7,0,27.1,0z\"></path></svg>"

/***/ }),
/* 365 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\"><g fill-rule=\"evenodd\"><path d=\"M1.5 14.042h4.095a.5.5 0 0 0 0-1H1.5a.5.5 0 1 0 0 1zM7.983 2a.5.5 0 0 1 .517.5v7.483l3.136-3.326a.5.5 0 1 1 .728.686l-4 4.243a.499.499 0 0 1-.73-.004L3.635 7.343a.5.5 0 0 1 .728-.686L7.5 9.983V3H1.536C1.24 3 1 2.776 1 2.5s.24-.5.536-.5h6.447zM10.5 14.042h4.095a.5.5 0 0 0 0-1H10.5a.5.5 0 1 0 0 1z\"></path></g></svg>"

/***/ }),
/* 366 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\"><g fill-rule=\"evenodd\"><path d=\"M5 13.5H1a.5.5 0 1 0 0 1h4a.5.5 0 1 0 0-1zM12 13.5H8a.5.5 0 1 0 0 1h4a.5.5 0 1 0 0-1zM6.11 5.012A.427.427 0 0 1 6.21 5h7.083L9.646 1.354a.5.5 0 1 1 .708-.708l4.5 4.5a.498.498 0 0 1 0 .708l-4.5 4.5a.5.5 0 0 1-.708-.708L13.293 6H6.5v5.5a.5.5 0 1 1-1 0v-6a.5.5 0 0 1 .61-.488z\"></path></g></svg>"

/***/ }),
/* 367 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\"><g fill-rule=\"evenodd\"><path d=\"M13.297 6.912C12.595 4.39 10.167 2.5 7.398 2.5A5.898 5.898 0 0 0 1.5 8.398a.5.5 0 0 0 1 0A4.898 4.898 0 0 1 7.398 3.5c2.75 0 5.102 2.236 5.102 4.898v.004L8.669 7.029a.5.5 0 0 0-.338.942l4.462 1.598a.5.5 0 0 0 .651-.34.506.506 0 0 0 .02-.043l2-5a.5.5 0 1 0-.928-.372l-1.24 3.098z\"></path><circle cx=\"7\" cy=\"12\" r=\"1\"></circle></g></svg>"

/***/ }),
/* 368 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M12.219 7c.345 0 .635.117.869.352.234.234.351.524.351.869 0 .351-.118.652-.356.903-.238.25-.526.376-.864.376-.332 0-.615-.125-.85-.376a1.276 1.276 0 0 1-.351-.903A1.185 1.185 0 0 1 12.218 7zM8.234 7c.345 0 .635.117.87.352.234.234.351.524.351.869 0 .351-.119.652-.356.903-.238.25-.526.376-.865.376-.332 0-.613-.125-.844-.376a1.286 1.286 0 0 1-.347-.903c0-.352.114-.643.342-.874.228-.231.51-.347.85-.347zM4.201 7c.339 0 .627.117.864.352.238.234.357.524.357.869 0 .351-.119.652-.357.903-.237.25-.525.376-.864.376-.338 0-.623-.125-.854-.376A1.286 1.286 0 0 1 3 8.221 1.185 1.185 0 0 1 4.201 7z\" fill-rule=\"evenodd\"></path></svg>"

/***/ }),
/* 369 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\"><g fill-rule=\"evenodd\"><path d=\"M3.233 11.25l-.417 1H1.712C.763 12.25 0 11.574 0 10.747V6.503C0 5.675.755 5 1.712 5h4.127l-.417 1H1.597C1.257 6 1 6.225 1 6.503v4.244c0 .277.267.503.597.503h1.636zM7.405 11.27L7 12.306c.865.01 2.212-.024 2.315-.04.112-.016.112-.016.185-.035.075-.02.156-.046.251-.082.152-.056.349-.138.592-.244.415-.182.962-.435 1.612-.744l.138-.066a179.35 179.35 0 0 0 2.255-1.094c1.191-.546 1.191-2.074-.025-2.632l-.737-.34a3547.554 3547.554 0 0 0-3.854-1.78c-.029.11-.065.222-.11.336l-.232.596c.894.408 4.56 2.107 4.56 2.107.458.21.458.596 0 .806L9.197 11.27H7.405zM4.462 14.692l5-12a.5.5 0 1 0-.924-.384l-5 12a.5.5 0 1 0 .924.384z\"></path></g></svg>"

/***/ }),
/* 370 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"#0b0b0b\"><path fill-opacity=\".3\" d=\"M12,3h2v10h-2V3z M5,9.9V6.1L8,8L5,9.9z\"></path><path d=\"M14,2H2C1.4,2,1,2.4,1,3v10c0,0.6,0.4,1,1,1h12c0.6,0,1-0.4,1-1V3C15,2.4,14.6,2,14,2z M2,13L2,13V3h0h9v10 H2L2,13z M14,13C14,13,14,13,14,13h-2V3h2c0,0,0,0,0,0V13z M8.5,7.2l-3-1.9C4.6,4.7,4,5,4,6.1v3.8c0,1.1,0.6,1.4,1.5,0.8l3-1.9 C9.5,8.3,9.5,7.8,8.5,7.2z M5,9.9V6.1L8,8L5,9.9z\"></path></svg>"

/***/ }),
/* 371 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" viewBox=\"0 0 16 16\" stroke=\"none\" fillrule=\"evenodd\"><rect opacity=\"0.6\" x=\"1\" y=\"3\" width=\"2\" height=\"6\"></rect><rect opacity=\"0.6\" x=\"17\" y=\"3\" width=\"2\" height=\"6\"></rect><rect x=\"6\" y=\"3\" width=\"2\" height=\"6\"></rect><rect x=\"12\" y=\"3\" width=\"2\" height=\"6\"></rect><rect x=\"9\" y=\"3\" width=\"2\" height=\"6\"></rect><path d=\"M4.5,13 L15.5,13 L16,13 L16,12 L15.5,12 L4.5,12 L4,12 L4,13 L4.5,13 L4.5,13 Z\"></path><path d=\"M4,10.5 L4,12.5 L4,13 L5,13 L5,12.5 L5,10.5 L5,10 L4,10 L4,10.5 L4,10.5 Z\"></path><path d=\"M15,10.5 L15,12.5 L15,13 L16,13 L16,12.5 L16,10.5 L16,10 L15,10 L15,10.5 L15,10.5 Z\"></path></svg>"

/***/ }),
/* 372 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\"><path fill-rule=\"evenodd\" d=\"M8.5 8.793L5.854 6.146l-.04-.035L7.5 4.426c.2-.2.3-.4.3-.6 0-.2-.1-.4-.2-.6l-1-1c-.4-.3-.9-.3-1.2 0l-4.1 4.1c-.2.2-.3.4-.3.6 0 .2.1.4.2.6l1 1c.3.3.9.3 1.2 0l1.71-1.71.036.04L7.793 9.5l-3.647 3.646c-.195.196-.195.512 0 .708.196.195.512.195.708 0L8.5 10.207l3.646 3.647c.196.195.512.195.708 0 .195-.196.195-.512 0-.708L9.207 9.5l2.565-2.565L13.3 8.5c.1.1 2.3 1.1 2.7.7.4-.4-.3-2.7-.5-2.9l-1.1-1.1c.1-.1.2-.4.2-.6 0-.2-.1-.4-.2-.6l-.4-.4c-.3-.3-.8-.3-1.1 0l-1.5-1.4c-.2-.2-.3-.2-.5-.2s-.3.1-.5.2L9.2 3.4c-.2.1-.2.2-.2.4s.1.4.2.5l1.874 1.92L8.5 8.792z\"></path></svg>"

/***/ }),
/* 373 */,
/* 374 */,
/* 375 */,
/* 376 */,
/* 377 */,
/* 378 */,
/* 379 */,
/* 380 */,
/* 381 */,
/* 382 */,
/* 383 */,
/* 384 */,
/* 385 */,
/* 386 */,
/* 387 */,
/* 388 */,
/* 389 */,
/* 390 */,
/* 391 */,
/* 392 */,
/* 393 */,
/* 394 */,
/* 395 */,
/* 396 */,
/* 397 */,
/* 398 */,
/* 399 */,
/* 400 */,
/* 401 */,
/* 402 */,
/* 403 */,
/* 404 */,
/* 405 */,
/* 406 */,
/* 407 */,
/* 408 */,
/* 409 */,
/* 410 */,
/* 411 */,
/* 412 */,
/* 413 */,
/* 414 */,
/* 415 */,
/* 416 */,
/* 417 */,
/* 418 */,
/* 419 */,
/* 420 */,
/* 421 */,
/* 422 */,
/* 423 */,
/* 424 */,
/* 425 */,
/* 426 */,
/* 427 */,
/* 428 */,
/* 429 */,
/* 430 */,
/* 431 */,
/* 432 */,
/* 433 */,
/* 434 */,
/* 435 */,
/* 436 */,
/* 437 */,
/* 438 */,
/* 439 */,
/* 440 */,
/* 441 */,
/* 442 */,
/* 443 */,
/* 444 */,
/* 445 */,
/* 446 */,
/* 447 */,
/* 448 */,
/* 449 */,
/* 450 */,
/* 451 */,
/* 452 */,
/* 453 */,
/* 454 */,
/* 455 */,
/* 456 */,
/* 457 */,
/* 458 */,
/* 459 */,
/* 460 */,
/* 461 */,
/* 462 */,
/* 463 */,
/* 464 */,
/* 465 */,
/* 466 */,
/* 467 */,
/* 468 */,
/* 469 */,
/* 470 */,
/* 471 */,
/* 472 */,
/* 473 */,
/* 474 */,
/* 475 */,
/* 476 */,
/* 477 */,
/* 478 */,
/* 479 */,
/* 480 */,
/* 481 */,
/* 482 */,
/* 483 */,
/* 484 */,
/* 485 */,
/* 486 */,
/* 487 */,
/* 488 */,
/* 489 */,
/* 490 */,
/* 491 */,
/* 492 */,
/* 493 */,
/* 494 */,
/* 495 */,
/* 496 */,
/* 497 */,
/* 498 */,
/* 499 */,
/* 500 */,
/* 501 */,
/* 502 */,
/* 503 */,
/* 504 */,
/* 505 */,
/* 506 */,
/* 507 */,
/* 508 */,
/* 509 */,
/* 510 */,
/* 511 */,
/* 512 */,
/* 513 */,
/* 514 */,
/* 515 */,
/* 516 */,
/* 517 */,
/* 518 */,
/* 519 */,
/* 520 */,
/* 521 */,
/* 522 */,
/* 523 */,
/* 524 */,
/* 525 */,
/* 526 */,
/* 527 */,
/* 528 */,
/* 529 */,
/* 530 */,
/* 531 */,
/* 532 */,
/* 533 */,
/* 534 */,
/* 535 */,
/* 536 */,
/* 537 */,
/* 538 */,
/* 539 */,
/* 540 */,
/* 541 */,
/* 542 */,
/* 543 */,
/* 544 */,
/* 545 */,
/* 546 */,
/* 547 */,
/* 548 */,
/* 549 */,
/* 550 */,
/* 551 */,
/* 552 */,
/* 553 */,
/* 554 */,
/* 555 */,
/* 556 */,
/* 557 */,
/* 558 */,
/* 559 */,
/* 560 */,
/* 561 */,
/* 562 */,
/* 563 */,
/* 564 */,
/* 565 */,
/* 566 */,
/* 567 */,
/* 568 */,
/* 569 */,
/* 570 */,
/* 571 */,
/* 572 */,
/* 573 */,
/* 574 */,
/* 575 */,
/* 576 */,
/* 577 */,
/* 578 */,
/* 579 */,
/* 580 */,
/* 581 */,
/* 582 */,
/* 583 */,
/* 584 */,
/* 585 */,
/* 586 */,
/* 587 */,
/* 588 */,
/* 589 */,
/* 590 */,
/* 591 */,
/* 592 */,
/* 593 */,
/* 594 */,
/* 595 */,
/* 596 */,
/* 597 */,
/* 598 */,
/* 599 */,
/* 600 */,
/* 601 */,
/* 602 */,
/* 603 */,
/* 604 */,
/* 605 */,
/* 606 */,
/* 607 */,
/* 608 */,
/* 609 */,
/* 610 */,
/* 611 */,
/* 612 */,
/* 613 */,
/* 614 */,
/* 615 */,
/* 616 */,
/* 617 */,
/* 618 */,
/* 619 */,
/* 620 */,
/* 621 */,
/* 622 */,
/* 623 */,
/* 624 */,
/* 625 */,
/* 626 */,
/* 627 */,
/* 628 */,
/* 629 */,
/* 630 */,
/* 631 */,
/* 632 */,
/* 633 */,
/* 634 */,
/* 635 */,
/* 636 */,
/* 637 */,
/* 638 */,
/* 639 */,
/* 640 */,
/* 641 */,
/* 642 */,
/* 643 */,
/* 644 */,
/* 645 */,
/* 646 */,
/* 647 */,
/* 648 */,
/* 649 */,
/* 650 */,
/* 651 */,
/* 652 */,
/* 653 */,
/* 654 */,
/* 655 */,
/* 656 */,
/* 657 */,
/* 658 */,
/* 659 */,
/* 660 */,
/* 661 */,
/* 662 */,
/* 663 */,
/* 664 */,
/* 665 */,
/* 666 */,
/* 667 */,
/* 668 */,
/* 669 */,
/* 670 */,
/* 671 */,
/* 672 */,
/* 673 */,
/* 674 */,
/* 675 */,
/* 676 */,
/* 677 */
/***/ (function(module, exports) {

module.exports = __WEBPACK_EXTERNAL_MODULE_677__;

/***/ }),
/* 678 */
/***/ (function(module, exports) {

module.exports = __WEBPACK_EXTERNAL_MODULE_678__;

/***/ }),
/* 679 */,
/* 680 */,
/* 681 */,
/* 682 */,
/* 683 */,
/* 684 */,
/* 685 */,
/* 686 */,
/* 687 */,
/* 688 */,
/* 689 */,
/* 690 */,
/* 691 */,
/* 692 */,
/* 693 */,
/* 694 */,
/* 695 */,
/* 696 */,
/* 697 */,
/* 698 */,
/* 699 */,
/* 700 */,
/* 701 */,
/* 702 */,
/* 703 */,
/* 704 */,
/* 705 */,
/* 706 */,
/* 707 */,
/* 708 */,
/* 709 */,
/* 710 */,
/* 711 */,
/* 712 */,
/* 713 */,
/* 714 */,
/* 715 */,
/* 716 */,
/* 717 */,
/* 718 */,
/* 719 */,
/* 720 */,
/* 721 */,
/* 722 */,
/* 723 */,
/* 724 */,
/* 725 */,
/* 726 */,
/* 727 */,
/* 728 */,
/* 729 */,
/* 730 */,
/* 731 */,
/* 732 */,
/* 733 */,
/* 734 */,
/* 735 */,
/* 736 */,
/* 737 */,
/* 738 */,
/* 739 */,
/* 740 */,
/* 741 */,
/* 742 */,
/* 743 */,
/* 744 */,
/* 745 */,
/* 746 */,
/* 747 */,
/* 748 */,
/* 749 */,
/* 750 */,
/* 751 */,
/* 752 */,
/* 753 */,
/* 754 */,
/* 755 */,
/* 756 */,
/* 757 */,
/* 758 */,
/* 759 */,
/* 760 */,
/* 761 */,
/* 762 */,
/* 763 */,
/* 764 */,
/* 765 */,
/* 766 */,
/* 767 */,
/* 768 */,
/* 769 */,
/* 770 */,
/* 771 */,
/* 772 */,
/* 773 */,
/* 774 */,
/* 775 */,
/* 776 */,
/* 777 */,
/* 778 */,
/* 779 */,
/* 780 */,
/* 781 */,
/* 782 */,
/* 783 */,
/* 784 */,
/* 785 */,
/* 786 */,
/* 787 */,
/* 788 */,
/* 789 */,
/* 790 */,
/* 791 */,
/* 792 */
/***/ (function(module, exports) {

var g;

// This works in non-strict mode
g = (function() {
	return this;
})();

try {
	// This works if eval is allowed (see CSP)
	g = g || Function("return this")() || (1,eval)("this");
} catch(e) {
	// This works if the window reference is available
	if(typeof window === "object")
		g = window;
}

// g can still be undefined, but nothing to do about it...
// We return undefined, instead of nothing here, so it's
// easier to handle this case. if(!global) { ...}

module.exports = g;


/***/ }),
/* 793 */
/***/ (function(module, exports) {

module.exports = function(module) {
	if(!module.webpackPolyfill) {
		module.deprecate = function() {};
		module.paths = [];
		// module.parent = undefined by default
		if(!module.children) module.children = [];
		Object.defineProperty(module, "loaded", {
			enumerable: true,
			get: function() {
				return module.l;
			}
		});
		Object.defineProperty(module, "id", {
			enumerable: true,
			get: function() {
				return module.i;
			}
		});
		module.webpackPolyfill = 1;
	}
	return module;
};


/***/ }),
/* 794 */,
/* 795 */,
/* 796 */,
/* 797 */,
/* 798 */,
/* 799 */,
/* 800 */,
/* 801 */,
/* 802 */,
/* 803 */,
/* 804 */,
/* 805 */,
/* 806 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" viewBox=\"0 0 256 256\"><defs><style>.cls-1{isolation:isolate;}.cls-17,.cls-2,.cls-25{fill:none;}.cls-17,.cls-2{stroke-miterlimit:10;}.cls-2{stroke-width:0.75px;stroke:url(#linear-gradient);}.cls-3{fill:url(#linear-gradient-2);}.cls-4{fill:#f15a24;}.cls-5{fill:#ed1c24;}.cls-6{fill:#c1272d;}.cls-7{fill:url(#linear-gradient-3);}.cls-8{fill:url(#linear-gradient-4);}.cls-9{fill:url(#linear-gradient-5);}.cls-10{fill:url(#linear-gradient-6);}.cls-11{opacity:0.49;fill:url(#linear-gradient-7);}.cls-12{fill:url(#linear-gradient-8);}.cls-13{fill:#2db5f9;}.cls-13,.cls-14{mix-blend-mode:screen;}.cls-14{fill:#5fd2ff;}.cls-15{fill:#219058;}.cls-16{fill:url(#linear-gradient-9);}.cls-17{stroke:#fff;stroke-width:1.87px;}.cls-18{fill:#f7b852;}.cls-19{fill:#ff8431;}.cls-20{fill:#fffb69;}.cls-21{fill:#44c688;}.cls-22{fill:#29b36e;}.cls-23{fill:#6fd191;}.cls-24{fill:#c83ad7;}.cls-26{fill:#fba9ff;}.cls-27{fill:#ff737d;}.cls-28{fill:#fdc666;}</style><linearGradient id=\"linear-gradient\" x1=\"67.45\" y1=\"154.72\" x2=\"67.29\" y2=\"155.43\" gradientUnits=\"userSpaceOnUse\"><stop offset=\"0\" stop-color=\"#ff1d25\" stop-opacity=\"0.5\"></stop><stop offset=\"0.06\" stop-color=\"#ff1d25\" stop-opacity=\"0.54\"></stop><stop offset=\"0.37\" stop-color=\"#ff1d25\" stop-opacity=\"0.74\"></stop><stop offset=\"0.64\" stop-color=\"#ff1d25\" stop-opacity=\"0.88\"></stop><stop offset=\"0.86\" stop-color=\"#ff1d25\" stop-opacity=\"0.97\"></stop><stop offset=\"1\" stop-color=\"#ff1d25\"></stop></linearGradient><linearGradient id=\"linear-gradient-2\" x1=\"73.09\" y1=\"170.74\" x2=\"73.09\" y2=\"153.5\" gradientUnits=\"userSpaceOnUse\"><stop offset=\"0\" stop-color=\"#ffdd42\"></stop><stop offset=\"1\" stop-color=\"#fb784b\"></stop></linearGradient><linearGradient id=\"linear-gradient-3\" x1=\"201.52\" y1=\"95.13\" x2=\"207.88\" y2=\"89.89\" gradientUnits=\"userSpaceOnUse\"><stop offset=\"0\" stop-color=\"#5bcb99\"></stop><stop offset=\"1\" stop-color=\"#85a8e8\"></stop></linearGradient><linearGradient id=\"linear-gradient-4\" x1=\"81.17\" y1=\"158.3\" x2=\"279.32\" y2=\"55.49\" gradientUnits=\"userSpaceOnUse\"><stop offset=\"0\" stop-color=\"#34e28b\"></stop><stop offset=\"1\"></stop></linearGradient><linearGradient id=\"linear-gradient-5\" x1=\"117.57\" y1=\"178.22\" x2=\"133.15\" y2=\"178.22\" gradientUnits=\"userSpaceOnUse\"><stop offset=\"0\" stop-color=\"#c297ff\"></stop><stop offset=\"1\" stop-color=\"#ae31bb\"></stop></linearGradient><linearGradient id=\"linear-gradient-6\" x1=\"54.05\" y1=\"253.29\" x2=\"251.08\" y2=\"99.63\" gradientUnits=\"userSpaceOnUse\"><stop offset=\"0\"></stop><stop offset=\"1\" stop-color=\"#d23de2\"></stop></linearGradient><linearGradient id=\"linear-gradient-7\" x1=\"199.8\" y1=\"86.45\" x2=\"191.83\" y2=\"113.37\" gradientUnits=\"userSpaceOnUse\"><stop offset=\"0\"></stop><stop offset=\"1\" stop-opacity=\"0\"></stop></linearGradient><linearGradient id=\"linear-gradient-8\" x1=\"126.87\" y1=\"190.63\" x2=\"182.9\" y2=\"204.2\" gradientUnits=\"userSpaceOnUse\"><stop offset=\"0\" stop-color=\"#2db5f9\"></stop><stop offset=\"1\" stop-color=\"#092432\"></stop></linearGradient><linearGradient id=\"linear-gradient-9\" x1=\"83.08\" y1=\"49.55\" x2=\"46.06\" y2=\"151.24\" gradientUnits=\"userSpaceOnUse\"><stop offset=\"0\"></stop><stop offset=\"0.21\" stop-color=\"#48080a\"></stop><stop offset=\"0.42\" stop-color=\"#891014\"></stop><stop offset=\"0.61\" stop-color=\"#bc151b\"></stop><stop offset=\"0.78\" stop-color=\"#e01a21\"></stop><stop offset=\"0.91\" stop-color=\"#f71c24\"></stop><stop offset=\"1\" stop-color=\"#ff1d25\"></stop></linearGradient></defs><title>dojo_square</title><g class=\"cls-1\"><g id=\"Layer_1\" data-name=\"Layer 1\"><line class=\"cls-2\" x1=\"67.37\" y1=\"155.08\" x2=\"67.37\" y2=\"155.08\"></line><path class=\"cls-3\" d=\"M42.28,150.4l.52.82A58,58,0,0,0,52,161.49a45.23,45.23,0,0,0,28.74,10.25c.65,0,1.31,0,2,0a67.32,67.32,0,0,0,21.13-5.26,67.38,67.38,0,0,1-9.09.83,36.92,36.92,0,0,1-27.44-12.17A66.82,66.82,0,0,1,42.28,150.4Z\"></path><path class=\"cls-4\" d=\"M80.79,80.88a45.4,45.4,0,0,1,38.89,21.94A37,37,0,0,0,84.43,94.7c8.29,4,19.66,7.08,35.28,8.15,0,0-.77-14.87-19.8-22.64-26.57-10.84-30.33-8.69-37.06-19.9a30.09,30.09,0,0,0,6,22.15A45.45,45.45,0,0,1,80.79,80.88Z\"></path><path class=\"cls-5\" d=\"M99.92,80.21c-9.1-3.71-15.52-5.9-20.3-7.62A33.4,33.4,0,0,0,84.54,81a45.42,45.42,0,0,1,35.13,21.78,36.87,36.87,0,0,0-20.54-9.34,109,109,0,0,0,20,9.32l.59,0S118.94,88,99.92,80.21Z\"></path><path class=\"cls-6\" d=\"M119.69,102.85h.1c.29-1.62,2.07-14.46-12.4-25.7C85.78,60.37,81.56,58.82,80.53,49.94a27.8,27.8,0,0,0-.91,22.65c4.78,1.72,11.21,3.91,20.3,7.62,19,7.77,19.8,22.64,19.8,22.64Z\"></path><path class=\"cls-7\" d=\"M185.28,88.22a33.64,33.64,0,0,1,22.08,8.23,29.8,29.8,0,0,1,19,8S208.22,78,176.19,89a41.72,41.72,0,0,0-8.2,4A33.64,33.64,0,0,1,185.28,88.22Z\"></path><path class=\"cls-8\" d=\"M185.28,88.22A33.62,33.62,0,0,0,168,93l.93-.56c-25.88,15.24-57.6,62.06-101.56,62.65h0a36.92,36.92,0,0,0,27.44,12.17,67.38,67.38,0,0,0,9.09-.83c28.6-11.79,56.09-40,68.55-53.64,11.56-12.67,24.22-17,34.91-16.34A33.64,33.64,0,0,0,185.28,88.22Z\"></path><path class=\"cls-9\" d=\"M118.48,179a20.94,20.94,0,0,0-.92,5.86,22.25,22.25,0,0,0,1,6.74,16.56,16.56,0,0,1,5.5-14.74c0-.17.09-.34.14-.51a20.37,20.37,0,0,1,8.91-11.47l-.08,0a37.84,37.84,0,0,0-4,1.53A20.87,20.87,0,0,0,118.48,179Z\"></path><path class=\"cls-10\" d=\"M234.26,129.11a42.41,42.41,0,0,0-7.94-24.76,29.82,29.82,0,0,0-19-7.9A33.69,33.69,0,0,1,219.06,122c0,11.34-4.12,24.3-17.45,31.28-19.42,10.16-49.21,5.33-68.54,11.6l.08,0a20.37,20.37,0,0,0-8.91,11.47c-.05.17-.09.34-.14.51h0c5.67-5,16.84-8.54,38.63-5.73,15.56,2,27.93,2.55,38.54-.5a41.2,41.2,0,0,0,25.23-17h0A42.41,42.41,0,0,0,234.26,129.11Z\"></path><path class=\"cls-11\" d=\"M234.26,129.11a42.41,42.41,0,0,0-7.94-24.76,29.82,29.82,0,0,0-19-7.9A33.69,33.69,0,0,1,219.06,122c0,11.34-4.12,24.3-17.45,31.28-19.42,10.16-49.21,5.33-68.54,11.6l.08,0a20.37,20.37,0,0,0-8.91,11.47c-.05.17-.09.34-.14.51h0c5.67-5,16.84-8.54,38.63-5.73,15.56,2,27.93,2.55,38.54-.5a41.2,41.2,0,0,0,25.23-17h0A42.41,42.41,0,0,0,234.26,129.11Z\"></path><path class=\"cls-12\" d=\"M137.8,201.88a20.44,20.44,0,0,1-13.68-25.12h0l0,.08a16.56,16.56,0,0,0-5.5,14.73,20.94,20.94,0,0,0,33.63,9.38A20.37,20.37,0,0,1,137.8,201.88Z\"></path><path class=\"cls-13\" d=\"M144.91,200.4c2.12-6.17,9-15.7,16.33-11.34,0,0,6,3.36,7.23-6.11,0,0,4.49,22.28-17.1,21.53,0,0,4.86-3.55,4.81-7.2A24.73,24.73,0,0,1,144.91,200.4Z\"></path><path class=\"cls-14\" d=\"M153.52,186.75c2.14-1.09,4.16-2.18,8.08-.44,3,1.35,6.88-3.94,3.56-8.66,0,0,.21,4.88-3.72,4.84A8.81,8.81,0,0,0,153.52,186.75Z\"></path><path class=\"cls-15\" d=\"M175.21,115.88c-5.17,7-12.75,12.95-13.64,14.39s1.5,6.11,5,6.45.82-1.36.52-3,1.06,1,5.27.42-1.53-2.2-1.14-4.33,4.74-6.17,6.47-10.25,4.7,0,3.18,3.18c-.6,1.26,4.81-3.54,2.08-7.21S178.22,111.77,175.21,115.88Z\"></path><path class=\"cls-15\" d=\"M160.47,131.28c-.76.73-3,2.37-3,2.37s2.19,3.05,5,3c0,0,.61-.29-.27-1.18S160.27,131.82,160.47,131.28Z\"></path><path class=\"cls-16\" d=\"M84.43,94.7c-7.35-3.56-12.27-7.88-15.54-12.24A45.42,45.42,0,0,0,42.28,150.4a66.82,66.82,0,0,0,25.09,4.67h0A37,37,0,0,1,84.43,94.7Z\"></path><line class=\"cls-17\" x1=\"67.37\" y1=\"155.08\" x2=\"67.37\" y2=\"155.08\"></line><path class=\"cls-18\" d=\"M84.52,81c2,2.91,7.81,7.94,14.63,12.44a36.87,36.87,0,0,1,20.52,9.33A45.42,45.42,0,0,0,84.52,81Z\"></path><path class=\"cls-19\" d=\"M84.52,81c-1.23-.1-2.48-.16-3.74-.16a45.45,45.45,0,0,0-11.89,1.58c3.27,4.36,8.19,8.67,15.54,12.24a36.72,36.72,0,0,1,14.72-1.22C92.33,89,86.54,83.95,84.52,81Z\"></path><path class=\"cls-20\" d=\"M109.73,91.38a33.29,33.29,0,0,0-12.21-7.29C100.46,89.66,103,91.38,109.73,91.38Z\"></path><path class=\"cls-6\" d=\"M96.89,67.66a63.16,63.16,0,0,1,6.54,4.58c.52.39-.93-3.55-2.62-4.3A7.5,7.5,0,0,0,96.89,67.66Z\"></path><path class=\"cls-6\" d=\"M105,73.37a70.75,70.75,0,0,1,6.5,4.64c.53.38-.9-3.56-2.57-4.33A7.5,7.5,0,0,0,105,73.37Z\"></path><path class=\"cls-6\" d=\"M112.55,79a19.59,19.59,0,0,1,3.52,4.39c.26.38.07-2.58-.92-3.42A5.28,5.28,0,0,0,112.55,79Z\"></path><path class=\"cls-21\" d=\"M77.55,151c-.2-.65,2.35-1.48,3-1.51a11.91,11.91,0,0,1,3.57,1.19,16.34,16.34,0,0,1-4.89,1A4.46,4.46,0,0,1,77.55,151Z\"></path><path class=\"cls-21\" d=\"M86.73,148.78c-.22-.51,1.75-1.38,2.29-1.46a9.58,9.58,0,0,1,3,.65,13.15,13.15,0,0,1-3.83,1.23A3.59,3.59,0,0,1,86.73,148.78Z\"></path><path class=\"cls-21\" d=\"M94.41,146c-.22-.4,1.32-1.29,1.76-1.41a7.92,7.92,0,0,1,2.48.27,10.87,10.87,0,0,1-3,1.35A3,3,0,0,1,94.41,146Z\"></path><path class=\"cls-21\" d=\"M100.52,143.19c-.23-.35,1.11-1.27,1.5-1.41a7.25,7.25,0,0,1,2.29.07,10,10,0,0,1-2.67,1.45A2.72,2.72,0,0,1,100.52,143.19Z\"></path><path class=\"cls-21\" d=\"M106.23,140.07c-.22-.29.9-1.16,1.24-1.3a6.31,6.31,0,0,1,2,0,8.66,8.66,0,0,1-2.25,1.39A2.36,2.36,0,0,1,106.23,140.07Z\"></path><path class=\"cls-21\" d=\"M68.34,151.69c-.11-.73,2.73-1.16,3.46-1.09a12.76,12.76,0,0,1,3.57,1.86,17.51,17.51,0,0,1-5.35.26A4.78,4.78,0,0,1,68.34,151.69Z\"></path><path class=\"cls-6\" d=\"M70.08,65.82c2,2.27,6.68,3.74,6.68,3.74A19,19,0,0,1,75.83,62c0-.18-5.75-.09-6.73-4.81C68.91,56.23,68.1,63.55,70.08,65.82Z\"></path><path class=\"cls-4\" d=\"M50.54,174.68a5.49,5.49,0,0,1,2.24-3.88l.19-.11a14,14,0,0,0-1.43-.25c-6.85-1-8.59,3.06-8.95,7.64,0,.26,0,.53,0,.78a8.31,8.31,0,0,1,7.89-4.2Z\"></path><path class=\"cls-4\" d=\"M58.88,173.19c-6.81-4.13-10.63-4.43-12.15,7.14,0,0,7.17-5.67,10.55-1.92,3.68,4.07,8.68.32,6.05-2.06A18.77,18.77,0,0,0,58.88,173.19Z\"></path><path class=\"cls-4\" d=\"M56.06,174.53c-3.86,2.57-3.86,8.62-3.86,8.62s7-3.81,6.5-6.19A2.93,2.93,0,0,0,56.06,174.53Z\"></path><path class=\"cls-4\" d=\"M24,127a5.49,5.49,0,0,1,4.43-.68l.2.08a14,14,0,0,0-.7-1.27c-3.49-6-7.76-4.79-11.56-2.21-.22.15-.43.31-.63.47a8.31,8.31,0,0,1,8.21,3.53Z\"></path><path class=\"cls-4\" d=\"M31.29,134.08c-2.48-9.82-4.47-13.62-14-6.83,0,0,8.13-2.34,9.06,9.22.44,5.47,5.69,6.57,5.63,3A18.77,18.77,0,0,0,31.29,134.08Z\"></path><path class=\"cls-4\" d=\"M27.83,128.82c-4.52-1.07-8.94,3.06-8.94,3.06s6.94-.24,7.78,4C27.14,138.23,27.83,128.82,27.83,128.82Z\"></path><path class=\"cls-22\" d=\"M172.45,112.79a55.39,55.39,0,0,1,15.49-12.5c5.64-3,13.66-4.84,18.5-4.61l1.85,1.59-1-.08a37.75,37.75,0,0,0-19,3.76A55,55,0,0,0,172.45,112.79Z\"></path><path class=\"cls-23\" d=\"M207.12,95.74a34.5,34.5,0,0,1,10.45,2.68,27.42,27.42,0,0,1,8.81,6,27.05,27.05,0,0,0-9.09-5.29,43.65,43.65,0,0,0-9-1.84l-1.84-1.59Z\"></path><path class=\"cls-24\" d=\"M124.52,175.47a27.43,27.43,0,0,1,3.11-1.9,25.35,25.35,0,0,1,2.31-1c.78-.34,1.58-.58,2.38-.84a45.63,45.63,0,0,1,9.84-1.81,95.67,95.67,0,0,1,19.84.78,194.33,194.33,0,0,0,19.66,1.89,66.25,66.25,0,0,0,19.6-1.93,66.44,66.44,0,0,1-19.6,2.3,194.82,194.82,0,0,1-19.75-1.52,94.77,94.77,0,0,0-19.65-.41,44.43,44.43,0,0,0-9.55,1.94c-.76.27-1.53.52-2.27.85a24,24,0,0,0-2.18,1,21.38,21.38,0,0,0-3.93,2.7l-.53.47A18.42,18.42,0,0,1,124.52,175.47Z\"></path><line class=\"cls-25\" x1=\"124.09\" y1=\"176.84\" x2=\"124.09\" y2=\"176.84\"></line><path class=\"cls-6\" d=\"M85.66,97.64c-6.36,1.4-5.37,6.45-5.37,6.45s2-2.43,13.09-3.74A52.36,52.36,0,0,1,85.66,97.64Z\"></path><path class=\"cls-26\" d=\"M118.59,191.58a16.4,16.4,0,0,1-.08-4.24,18.64,18.64,0,0,1,.91-4.18,18.86,18.86,0,0,1,1.87-3.9,19.81,19.81,0,0,1,2.78-3.38l.46-.42s-.63,2-.7,2.51a19.27,19.27,0,0,0-1.61,1.93,17.84,17.84,0,0,0-3.33,7.5A16.22,16.22,0,0,0,118.59,191.58Z\"></path><path class=\"cls-27\" d=\"M65.89,155.08c-3.61-4-6.65-10.29-7.95-16.2a36.79,36.79,0,0,1-.82-9.49l.14-2.38.33-2.36a23,23,0,0,1,.47-2.33,19.65,19.65,0,0,1,.6-2.3,35.6,35.6,0,0,1,3.83-8.64,39.45,39.45,0,0,1,2.68-3.89l1.53-1.8c.51-.6,1.1-1.13,1.64-1.69a37.2,37.2,0,0,1,7.47-5.68,39.82,39.82,0,0,1,8.62-3.62A39.67,39.67,0,0,0,76,98.56a36.78,36.78,0,0,0-7.22,5.83c-.52.57-1.08,1.11-1.56,1.71l-1.45,1.81a38.67,38.67,0,0,0-2.51,3.89,34.62,34.62,0,0,0-3.48,8.52,18.77,18.77,0,0,0-.52,2.24,21.9,21.9,0,0,0-.38,2.27l-.25,2.28-.06,2.3a35.25,35.25,0,0,0,1.06,9.07,36.17,36.17,0,0,0,8.62,15.83l.66.71S66.44,155.12,65.89,155.08Z\"></path><path class=\"cls-28\" d=\"M103.89,166.42A47.14,47.14,0,0,1,94,167.55,37.09,37.09,0,0,1,84,165.91,46.7,46.7,0,0,1,74.71,162a34.57,34.57,0,0,1-8.12-6.17l-.69-.74s2.38,0,3-.06a33.57,33.57,0,0,0,6.72,5.5,45.26,45.26,0,0,0,8.82,4.27A36.2,36.2,0,0,0,94,166.91,47.78,47.78,0,0,0,103.89,166.42Z\"></path></g></g></svg>"

/***/ }),
/* 807 */,
/* 808 */,
/* 809 */,
/* 810 */,
/* 811 */,
/* 812 */,
/* 813 */,
/* 814 */,
/* 815 */,
/* 816 */,
/* 817 */,
/* 818 */,
/* 819 */,
/* 820 */,
/* 821 */,
/* 822 */,
/* 823 */,
/* 824 */,
/* 825 */,
/* 826 */,
/* 827 */,
/* 828 */,
/* 829 */,
/* 830 */,
/* 831 */,
/* 832 */,
/* 833 */,
/* 834 */,
/* 835 */,
/* 836 */,
/* 837 */,
/* 838 */,
/* 839 */,
/* 840 */,
/* 841 */,
/* 842 */,
/* 843 */,
/* 844 */,
/* 845 */,
/* 846 */,
/* 847 */,
/* 848 */,
/* 849 */,
/* 850 */,
/* 851 */,
/* 852 */,
/* 853 */,
/* 854 */,
/* 855 */,
/* 856 */,
/* 857 */,
/* 858 */,
/* 859 */,
/* 860 */,
/* 861 */,
/* 862 */,
/* 863 */,
/* 864 */,
/* 865 */,
/* 866 */,
/* 867 */,
/* 868 */,
/* 869 */,
/* 870 */,
/* 871 */,
/* 872 */,
/* 873 */,
/* 874 */,
/* 875 */,
/* 876 */,
/* 877 */,
/* 878 */,
/* 879 */,
/* 880 */,
/* 881 */,
/* 882 */,
/* 883 */
/***/ (function(module, exports, __webpack_require__) {

__webpack_require__(884);
var fs = __webpack_require__(118);

function Iterator(text) {
	var pos = 0, length = text.length;

	this.peek = function(num) {
		num = num || 0;
		if(pos + num >= length) { return null; }

		return text.charAt(pos + num);
	};
	this.next = function(inc) {
		inc = inc || 1;

		if(pos >= length) { return null; }

		return text.charAt((pos += inc) - inc);
	};
	this.pos = function() {
		return pos;
	};
}

var rWhitespace = /\s/;
function isWhitespace(chr) {
	return rWhitespace.test(chr);
}
function consumeWhiteSpace(iter) {
	var start = iter.pos();

	while(isWhitespace(iter.peek())) { iter.next(); }

	return { type: "whitespace", start: start, end: iter.pos() };
}

function startsComment(chr) {
	return chr === "!" || chr === "#";
}
function isEOL(chr) {
	return chr == null || chr === "\n" || chr === "\r";
}
function consumeComment(iter) {
	var start = iter.pos();

	while(!isEOL(iter.peek())) { iter.next(); }

	return { type: "comment", start: start, end: iter.pos() };
}

function startsKeyVal(chr) {
	return !isWhitespace(chr) && !startsComment(chr);
}
function startsSeparator(chr) {
	return chr === "=" || chr === ":" || isWhitespace(chr);
}
function startsEscapedVal(chr) {
	return chr === "\\";
}
function consumeEscapedVal(iter) {
	var start = iter.pos();

	iter.next(); // move past "\"
	var curChar = iter.next();
	if(curChar === "u") { // encoded unicode char
		iter.next(4); // Read in the 4 hex values
	}

	return { type: "escaped-value", start: start, end: iter.pos() };
}
function consumeKey(iter) {
	var start = iter.pos(), children = [];

	var curChar;
	while((curChar = iter.peek()) !== null) {
		if(startsSeparator(curChar)) { break; }
		if(startsEscapedVal(curChar)) { children.push(consumeEscapedVal(iter)); continue; }

		iter.next();
	}

	return { type: "key", start: start, end: iter.pos(), children: children };
}
function consumeKeyValSeparator(iter) {
	var start = iter.pos();

	var seenHardSep = false, curChar;
	while((curChar = iter.peek()) !== null) {
		if(isEOL(curChar)) { break; }

		if(isWhitespace(curChar)) { iter.next(); continue; }

		if(seenHardSep) { break; }

		seenHardSep = (curChar === ":" || curChar === "=");
		if(seenHardSep) { iter.next(); continue; }

		break; // curChar is a non-separtor char
	}

	return { type: "key-value-separator", start: start, end: iter.pos() };
}
function startsLineBreak(iter) {
	return iter.peek() === "\\" && isEOL(iter.peek(1));
}
function consumeLineBreak(iter) {
	var start = iter.pos();

	iter.next(); // consume \
	if(iter.peek() === "\r") { iter.next(); }
	iter.next(); // consume \n

	var curChar;
	while((curChar = iter.peek()) !== null) {
		if(isEOL(curChar)) { break; }
		if(!isWhitespace(curChar)) { break; }

		iter.next();
	}

	return { type: "line-break", start: start, end: iter.pos() };
}
function consumeVal(iter) {
	var start = iter.pos(), children = [];

	var curChar;
	while((curChar = iter.peek()) !== null) {
		if(startsLineBreak(iter)) { children.push(consumeLineBreak(iter)); continue; }
		if(startsEscapedVal(curChar)) { children.push(consumeEscapedVal(iter)); continue; }
		if(isEOL(curChar)) { break; }

		iter.next();
	}

	return { type: "value", start: start, end: iter.pos(), children: children };
}
function consumeKeyVal(iter) {
	return {
		type: "key-value",
		start: iter.pos(),
		children: [
			consumeKey(iter),
			consumeKeyValSeparator(iter),
			consumeVal(iter)
		],
		end: iter.pos()
	};
}

var renderChild = {
	"escaped-value": function(child, text) {
		var type = text.charAt(child.start + 1);

		if(type === "t") { return "\t"; }
		if(type === "r") { return "\r"; }
		if(type === "n") { return "\n"; }
		if(type === "f") { return "\f"; }
		if(type !== "u") { return type; }

		return String.fromCharCode(parseInt(text.substr(child.start + 2, 4), 16));
	},
	"line-break": function (child, text) {
		return "";
	}
};
function rangeToBuffer(range, text) {
	var start = range.start, buffer = [];

	for(var i = 0; i < range.children.length; i++) {
		var child = range.children[i];

		buffer.push(text.substring(start, child.start));
		buffer.push(renderChild[child.type](child, text));
		start = child.end;
	}
	buffer.push(text.substring(start, range.end));

	return buffer;
}
function rangesToObject(ranges, text) {
	var obj = Object.create(null); // Creates to a true hash map

	for(var i = 0; i < ranges.length; i++) {
		var range = ranges[i];

		if(range.type !== "key-value") { continue; }

		var key = rangeToBuffer(range.children[0], text).join("");
		var val = rangeToBuffer(range.children[2], text).join("");
		obj[key] = val;
	}

	return obj;
}

function stringToRanges(text) {
	var iter = new Iterator(text), ranges = [];

	var curChar;
	while((curChar = iter.peek()) !== null) {
		if(isWhitespace(curChar)) { ranges.push(consumeWhiteSpace(iter)); continue; }
		if(startsComment(curChar)) { ranges.push(consumeComment(iter)); continue; }
		if(startsKeyVal(curChar)) { ranges.push(consumeKeyVal(iter)); continue; }

		throw Error("Something crazy happened. text: '" + text + "'; curChar: '" + curChar + "'");
	}

	return ranges;
}

function isNewLineRange(range) {
	if(!range) { return false; }

	if(range.type === "whitespace") { return true; }

	if(range.type === "literal") {
		return isWhitespace(range.text) && range.text.indexOf("\n") > -1;
	}

	return false;
}

function escapeMaker(escapes) {
	return function escapeKey(key) {
		var zeros = [ "", "0", "00", "000" ];
		var buf = [];

		for(var i = 0; i < key.length; i++) {
			var chr = key.charAt(i);

			if(escapes[chr]) { buf.push(escapes[chr]); continue; }

			var code = chr.codePointAt(0);

			if(code <= 0x7F) { buf.push(chr); continue; }

			var hex = code.toString(16);

			buf.push("\\u");
			buf.push(zeros[4 - hex.length]);
			buf.push(hex);
		}

		return buf.join("");
	};
}

var escapeKey = escapeMaker({ " ": "\\ ", "\n": "\\n", ":": "\\:", "=": "\\=" });
var escapeVal = escapeMaker({ "\n": "\\n" });

function Editor(text, options) {
    if (typeof text === 'object') {
        options = text;
        text = null;
    }
	text = text || "";
    var path = options.path;
    var separator = options.separator || '=';

	var ranges = stringToRanges(text);
	var obj = rangesToObject(ranges, text);
	var keyRange = Object.create(null); // Creates to a true hash map

	for(var i = 0; i < ranges.length; i++) {
		var range = ranges[i];

		if(range.type !== "key-value") { continue; }

		var key = rangeToBuffer(range.children[0], text).join("");
		keyRange[key] = range;
	}

	this.addHeadComment = function(comment) {
		if(comment == null) { return; }

		ranges.unshift({ type: "literal", text: "# " + comment.replace(/\n/g, "\n# ") + "\n" });
	};

	this.get = function(key) { return obj[key]; };
	this.set = function(key, val, comment) {
		if(val == null) { this.unset(key); return; }

		obj[key] = val;
		var escapedKey = escapeKey(key);
		var escapedVal = escapeVal(val);

		var range = keyRange[key];
		if(!range) {
			keyRange[key] = range = {
				type: "literal",
				text: escapedKey + separator + escapedVal
			};

			var prevRange = ranges[ranges.length - 1];
			if(prevRange != null && !isNewLineRange(prevRange)) {
				ranges.push({ type: "literal", text: "\n" });
			}
			ranges.push(range);
		}

		// comment === null deletes comment. if comment === undefined, it's left alone
		if(comment !== undefined) {
			range.comment = comment && "# " + comment.replace(/\n/g, "\n# ") + "\n";
		}

		if(range.type === "literal") {
			range.text = escapedKey + separator + escapedVal;
			if(range.comment != null) { range.text = range.comment + range.text; }
		} else if(range.type === "key-value") {
			range.children[2] = { type: "literal", text: escapedVal };
		} else {
			throw "Unknown node type: " + range.type;
		}
	};
	this.unset = function(key) {
		if(!(key in obj)) { return; }

		var range = keyRange[key];
		var idx = ranges.indexOf(range);

		ranges.splice(idx, (isNewLineRange(ranges[idx + 1]) ? 2 : 1));

		delete keyRange[key];
		delete obj[key];
	};
	this.valueOf = this.toString = function() {
		var buffer = [], stack = [].concat(ranges);

		var node;
		while((node = stack.shift()) != null) {
			switch(node.type) {
				case "literal":
					buffer.push(node.text);
					break;
				case "key":
				case "value":
				case "comment":
				case "whitespace":
				case "key-value-separator":
				case "escaped-value":
				case "line-break":
					buffer.push(text.substring(node.start, node.end));
					break;
				case "key-value":
					Array.prototype.unshift.apply(stack, node.children);
					if(node.comment) { stack.unshift({ type: "literal", text: node.comment }); }
					break;
			}
		}

		return buffer.join("");
	};
	this.save = function(newPath, callback) {
		if(typeof newPath === 'function') {
			callback = newPath;
			newPath = path;
		}
		newPath = newPath || path;

		if(!newPath) {
            if (callback) {
                return callback("Unknown path");
            }
            throw new Error("Unknown path");
        }

        if (callback) {
            fs.writeFile(newPath, this.toString(), callback);
        } else {
            fs.writeFileSync(newPath, this.toString());
        }

	};
}
function createEditor(/*path, options, callback*/) {
    var path, options, callback;
    var args = Array.prototype.slice.call(arguments);
    for (var i = 0; i < args.length; i ++) {
        var arg = args[i];
        if (!path && typeof arg === 'string') {
            path = arg;
        } else if (!options && typeof arg === 'object') {
            options = arg;
        } else if (!callback && typeof arg === 'function') {
            callback = arg;
        }
    }
    options = options || {};
    path = path || options.path;
    callback = callback || options.callback;
    options.path = path;

	if(!path) { return new Editor(options); }

	if(!callback) { return new Editor(fs.readFileSync(path).toString(), options); }

	return fs.readFile(path, function(err, text) {
		if(err) { return callback(err, null); }

		text = text.toString();
		return callback(null, new Editor(text, options));
	});
}

function parse(text) {
	text = text.toString();
	var ranges = stringToRanges(text);
	return rangesToObject(ranges, text);
}

function read(path, callback) {
	if(!callback) { return parse(fs.readFileSync(path)); }

	return fs.readFile(path, function(err, data) {
		if(err) { return callback(err, null); }

		return callback(null, parse(data));
	});
}

module.exports = { parse: parse, read: read, createEditor: createEditor };


/***/ }),
/* 884 */
/***/ (function(module, exports) {

/*! http://mths.be/codepointat v0.2.0 by @mathias */
if (!String.prototype.codePointAt) {
	(function() {
		'use strict'; // needed to support `apply`/`call` with `undefined`/`null`
		var defineProperty = (function() {
			// IE 8 only supports `Object.defineProperty` on DOM elements
			try {
				var object = {};
				var $defineProperty = Object.defineProperty;
				var result = $defineProperty(object, object, object) && $defineProperty;
			} catch(error) {}
			return result;
		}());
		var codePointAt = function(position) {
			if (this == null) {
				throw TypeError();
			}
			var string = String(this);
			var size = string.length;
			// `ToInteger`
			var index = position ? Number(position) : 0;
			if (index != index) { // better `isNaN`
				index = 0;
			}
			// Account for out-of-bounds indices:
			if (index < 0 || index >= size) {
				return undefined;
			}
			// Get the first code unit
			var first = string.charCodeAt(index);
			var second;
			if ( // check if it’s the start of a surrogate pair
				first >= 0xD800 && first <= 0xDBFF && // high surrogate
				size > index + 1 // there is a next code unit
			) {
				second = string.charCodeAt(index + 1);
				if (second >= 0xDC00 && second <= 0xDFFF) { // low surrogate
					// http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
					return (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000;
				}
			}
			return first;
		};
		if (defineProperty) {
			defineProperty(String.prototype, 'codePointAt', {
				'value': codePointAt,
				'configurable': true,
				'writable': true
			});
		} else {
			String.prototype.codePointAt = codePointAt;
		}
	}());
}


/***/ }),
/* 885 */,
/* 886 */,
/* 887 */,
/* 888 */,
/* 889 */,
/* 890 */,
/* 891 */,
/* 892 */,
/* 893 */,
/* 894 */,
/* 895 */,
/* 896 */,
/* 897 */,
/* 898 */,
/* 899 */,
/* 900 */,
/* 901 */,
/* 902 */,
/* 903 */,
/* 904 */,
/* 905 */,
/* 906 */,
/* 907 */,
/* 908 */,
/* 909 */,
/* 910 */,
/* 911 */,
/* 912 */,
/* 913 */,
/* 914 */,
/* 915 */,
/* 916 */
/***/ (function(module, exports, __webpack_require__) {

/* WEBPACK VAR INJECTION */(function(module, global) {var __WEBPACK_AMD_DEFINE_RESULT__;/*! 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 (
		true
	) {
		!(__WEBPACK_AMD_DEFINE_RESULT__ = (function() {
			return punycode;
		}).call(exports, __webpack_require__, exports, module),
				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
	} 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));

/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(793)(module), __webpack_require__(792)))

/***/ }),
/* 917 */,
/* 918 */,
/* 919 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 1792 1792\"><path d=\"M1395 1184q0 13-10 23l-50 50q-10 10-23 10t-23-10l-393-393-393 393q-10 10-23 10t-23-10l-50-50q-10-10-10-23t10-23l466-466q10-10 23-10t23 10l466 466q10 10 10 23z\" fill=\"#696969\"></path></svg>"

/***/ }),
/* 920 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 1792 1792\"><path d=\"M1395 736q0 13-10 23l-466 466q-10 10-23 10t-23-10l-466-466q-10-10-10-23t10-23l50-50q10-10 23-10t23 10l393 393 393-393q10-10 23-10t23 10l50 50q10 10 10 23z\" fill=\"#696969\"></path></svg>"

/***/ }),
/* 921 */,
/* 922 */,
/* 923 */,
/* 924 */,
/* 925 */,
/* 926 */,
/* 927 */,
/* 928 */,
/* 929 */,
/* 930 */,
/* 931 */,
/* 932 */,
/* 933 */,
/* 934 */,
/* 935 */,
/* 936 */,
/* 937 */,
/* 938 */,
/* 939 */,
/* 940 */,
/* 941 */,
/* 942 */,
/* 943 */,
/* 944 */,
/* 945 */,
/* 946 */,
/* 947 */,
/* 948 */,
/* 949 */,
/* 950 */,
/* 951 */,
/* 952 */,
/* 953 */,
/* 954 */,
/* 955 */,
/* 956 */,
/* 957 */,
/* 958 */,
/* 959 */,
/* 960 */
/***/ (function(module, exports) {

module.exports = "# This Source Code Form is subject to the terms of the Mozilla Public\n# License, v. 2.0. If a copy of the MPL was not distributed with this\n# file, You can obtain one at http://mozilla.org/MPL/2.0/.\n\n# LOCALIZATION NOTE These strings are used inside the Debugger\n# which is available from the Web Developer sub-menu -> 'Debugger'.\n# The correct localization of this file might be to keep it in\n# English, or another language commonly spoken among web developers.\n# You want to make that choice consistent across the developer tools.\n# A good criteria is the language in which you'd find the best\n# documentation on web development on the web.\n\n# LOCALIZATION NOTE (collapsePanes): This is the tooltip for the button\n# that collapses the left and right panes in the debugger UI.\ncollapsePanes=Collapse panes\n\n# LOCALIZATION NOTE (copySource): This is the text that appears in the\n# context menu to copy the selected source of file open.\ncopySource=Copy\ncopySource.accesskey=y\n\n# LOCALIZATION NOTE (copySourceUri2): This is the text that appears in the\n# context menu to copy the source URI of file open.\ncopySourceUri2=Copy source URI\ncopySourceUri2.accesskey=u\n\n# LOCALIZATION NOTE (setDirectoryRoot.label): This is the text that appears in the\n# context menu to set a directory as root directory\nsetDirectoryRoot.label=Set directory root\nsetDirectoryRoot.accesskey=r\n\n# LOCALIZATION NOTE (removeDirectoryRoot.label): This is the text that appears in the\n# context menu to remove a directory as root directory\nremoveDirectoryRoot.label=Remove directory root\nremoveDirectoryRoot.accesskey=d\n\n# LOCALIZATION NOTE (copyFunction.label): This is the text that appears in the\n# context menu to copy the function the user selected\ncopyFunction.label=Copy function\ncopyFunction.accesskey=F\n\n# LOCALIZATION NOTE (copyStackTrace): This is the text that appears in the\n# context menu to copy the stack trace methods, file names and row number.\ncopyStackTrace=Copy stack trace\ncopyStackTrace.accesskey=c\n\n# LOCALIZATION NOTE (expandPanes): This is the tooltip for the button\n# that expands the left and right panes in the debugger UI.\nexpandPanes=Expand panes\n\n# LOCALIZATION NOTE (evaluateInConsole.label): Editor right-click menu item\n# to execute selected text in browser console.\nevaluateInConsole.label=Evaluate in console\n\n# LOCALIZATION NOTE (pauseButtonTooltip): The tooltip that is displayed for the pause\n# button when the debugger is in a running state.\npauseButtonTooltip=Pause %S\n\n# LOCALIZATION NOTE (pausePendingButtonTooltip): The tooltip that is displayed for\n# the pause button after it's been clicked but before the next JavaScript to run.\npausePendingButtonTooltip=Waiting for next execution\n\n# LOCALIZATION NOTE (resumeButtonTooltip): The label that is displayed on the pause\n# button when the debugger is in a paused state.\nresumeButtonTooltip=Resume %S\n\n# LOCALIZATION NOTE (stepOverTooltip): The label that is displayed on the\n# button that steps over a function call.\nstepOverTooltip=Step over %S\n\n# LOCALIZATION NOTE (stepInTooltip): The label that is displayed on the\n# button that steps into a function call.\nstepInTooltip=Step in %S\n\n# LOCALIZATION NOTE (stepOutTooltip): The label that is displayed on the\n# button that steps out of a function call.\nstepOutTooltip=Step out %S\n\n# LOCALIZATION NOTE (pauseButtonItem): The label that is displayed for the dropdown pause\n# list item when the debugger is in a running state.\npauseButtonItem=Pause on Next Statement\n\n# LOCALIZATION NOTE (ignoreExceptionsItem): The pause on exceptions button description\n# when the debugger will not pause on exceptions.\nignoreExceptionsItem=Ignore exceptions\n\n# LOCALIZATION NOTE (pauseOnUncaughtExceptionsItem): The pause on exceptions dropdown\n# item shown when a user is adding a new breakpoint.\npauseOnUncaughtExceptionsItem=Pause on uncaught exceptions\n\n# LOCALIZATION NOTE (pauseOnExceptionsItem): The pause on exceptions button description\n# when the debugger will pause on all exceptions.\npauseOnExceptionsItem=Pause on all exceptions\n\n# LOCALIZATION NOTE (workersHeader): The text to display in the events\n# header.\nworkersHeader=Workers\n\n# LOCALIZATION NOTE (noWorkersText): The text to display in the workers list\n# when there are no workers.\nnoWorkersText=This page has no workers.\n\n# LOCALIZATION NOTE (noSourcesText): The text to display in the sources list\n# when there are no sources.\nnoSourcesText=This page has no sources.\n\n# LOCALIZATION NOTE (noEventListenersText): The text to display in the events tab\n# when there are no events.\nnoEventListenersText=No event listeners to display.\n\n# LOCALIZATION NOTE (eventListenersHeader): The text to display in the events\n# header.\neventListenersHeader=Event listeners\n\n# LOCALIZATION NOTE (noStackFramesText): The text to display in the call stack tab\n# when there are no stack frames.\nnoStackFramesText=No stack frames to display\n\n# LOCALIZATION NOTE (eventCheckboxTooltip): The tooltip text to display when\n# the user hovers over the checkbox used to toggle an event breakpoint.\neventCheckboxTooltip=Toggle breaking on this event\n\n# LOCALIZATION NOTE (eventOnSelector): The text to display in the events tab\n# for every event item, between the event type and event selector.\neventOnSelector=on\n\n# LOCALIZATION NOTE (eventInSource): The text to display in the events tab\n# for every event item, between the event selector and listener's owner source.\neventInSource=in\n\n# LOCALIZATION NOTE (eventNodes): The text to display in the events tab when\n# an event is listened on more than one target node.\neventNodes=%S nodes\n\n# LOCALIZATION NOTE (eventNative): The text to display in the events tab when\n# a listener is added from plugins, thus getting translated to native code.\neventNative=[native code]\n\n# LOCALIZATION NOTE (*Events): The text to display in the events tab for\n# each group of sub-level event entries.\nanimationEvents=Animation\naudioEvents=Audio\nbatteryEvents=Battery\nclipboardEvents=Clipboard\ncompositionEvents=Composition\ndeviceEvents=Device\ndisplayEvents=Display\ndragAndDropEvents=Drag and Drop\ngamepadEvents=Gamepad\nindexedDBEvents=IndexedDB\ninteractionEvents=Interaction\nkeyboardEvents=Keyboard\nmediaEvents=HTML5 Media\nmouseEvents=Mouse\nmutationEvents=Mutation\nnavigationEvents=Navigation\npointerLockEvents=Pointer Lock\nsensorEvents=Sensor\nstorageEvents=Storage\ntimeEvents=Time\ntouchEvents=Touch\notherEvents=Other\n\n# LOCALIZATION NOTE (blackboxCheckboxTooltip2): The tooltip text to display when\n# the user hovers over the checkbox used to toggle blackboxing its associated\n# source.\nblackboxCheckboxTooltip2=Toggle blackboxing\n\n# LOCALIZATION NOTE (sources.search.key2): Key shortcut to open the search for\n# searching all the source files the debugger has seen.\n# Do not localize \"CmdOrCtrl+P\", or change the format of the string. These are\n# key identifiers, not messages displayed to the user.\nsources.search.key2=CmdOrCtrl+P\n\n# LOCALIZATION NOTE (sources.search.alt.key): A second key shortcut to open the\n# search for searching all the source files the debugger has seen.\n# Do not localize \"CmdOrCtrl+O\", or change the format of the string. These are\n# key identifiers, not messages displayed to the user.\nsources.search.alt.key=CmdOrCtrl+O\n\n# LOCALIZATION NOTE (projectTextSearch.key): A key shortcut to open the\n# full project text search for searching all the files the debugger has seen.\n# Do not localize \"CmdOrCtrl+Shift+F\", or change the format of the string. These are\n# key identifiers, not messages displayed to the user.\nprojectTextSearch.key=CmdOrCtrl+Shift+F\n\n# LOCALIZATION NOTE (functionSearch.key): A key shortcut to open the\n# modal for searching functions in a file.\n# Do not localize \"CmdOrCtrl+Shift+O\", or change the format of the string. These are\n# key identifiers, not messages displayed to the user.\nfunctionSearch.key=CmdOrCtrl+Shift+O\n\n# LOCALIZATION NOTE (toggleBreakpoint.key): A key shortcut to toggle\n# breakpoints.\n# Do not localize \"CmdOrCtrl+B\", or change the format of the string. These are\n# key identifiers, not messages displayed to the user.\ntoggleBreakpoint.key=CmdOrCtrl+B\n\n# LOCALIZATION NOTE (toggleCondPanel.key): A key shortcut to toggle\n# the conditional breakpoint panel.\n# Do not localize \"CmdOrCtrl+Shift+B\", or change the format of the string. These are\n# key identifiers, not messages displayed to the user.\ntoggleCondPanel.key=CmdOrCtrl+Shift+B\n\n# LOCALIZATION NOTE (stepOut.key): A key shortcut to\n# step out.\nstepOut.key=Shift+F11\n\n# LOCALIZATION NOTE (shortcuts.header.editor): Sections header in\n# the shortcuts modal for keyboard shortcuts related to editing.\nshortcuts.header.editor=Editor\n\n# LOCALIZATION NOTE (shortcuts.header.stepping): Sections header in\n# the shortcuts modal for keyboard shortcuts related to stepping.\nshortcuts.header.stepping=Stepping\n\n# LOCALIZATION NOTE (shortcuts.header.search): Sections header in\n# the shortcuts modal for keyboard shortcuts related to search.\nshortcuts.header.search=Search\n\n# LOCALIZATION NOTE (projectTextSearch.placeholder): A placeholder shown\n# when searching across all of the files in a project.\nprojectTextSearch.placeholder=Find in files…\n\n# LOCALIZATION NOTE (projectTextSearch.noResults): The center pane Text Search\n# message when the query did not match any text of all files in a project.\nprojectTextSearch.noResults=No results found\n\n# LOCALIZATION NOTE (sources.noSourcesAvailable): Text shown when the debugger\n# does not have any sources.\nsources.noSourcesAvailable=This page has no sources\n\n# LOCALIZATION NOTE (sourceSearch.search.key2): Key shortcut to open the search\n# for searching within a the currently opened files in the editor\n# Do not localize \"CmdOrCtrl+F\", or change the format of the string. These are\n# key identifiers, not messages displayed to the user.\nsourceSearch.search.key2=CmdOrCtrl+F\n\n# LOCALIZATION NOTE (sourceSearch.search.placeholder): placeholder text in\n# the source search input bar\nsourceSearch.search.placeholder=Search in file…\n\n# LOCALIZATION NOTE (sourceSearch.search.again.key2): Key shortcut to highlight\n# the next occurrence of the last search triggered from a source search\n# Do not localize \"CmdOrCtrl+G\", or change the format of the string. These are\n# key identifiers, not messages displayed to the user.\nsourceSearch.search.again.key2=CmdOrCtrl+G\n\n# LOCALIZATION NOTE (sourceSearch.search.againPrev.key2): Key shortcut to highlight\n# the previous occurrence of the last search triggered from a source search\n# Do not localize \"CmdOrCtrl+Shift+G\", or change the format of the string. These are\n# key identifiers, not messages displayed to the user.\nsourceSearch.search.againPrev.key2=CmdOrCtrl+Shift+G\n\n# LOCALIZATION NOTE (sourceSearch.resultsSummary1): Shows a summary of\n# the number of matches for autocomplete\nsourceSearch.resultsSummary1=%d results\n\n# LOCALIZATION NOTE (noMatchingStringsText): The text to display in the\n# global search results when there are no matching strings after filtering.\nnoMatchingStringsText=No matches found\n\n# LOCALIZATION NOTE (emptySearchText): This is the text that appears in the\n# filter text box when it is empty and the scripts container is selected.\nemptySearchText=Search scripts (%S)\n\n# LOCALIZATION NOTE (emptyVariablesFilterText): This is the text that\n# appears in the filter text box for the variables view container.\nemptyVariablesFilterText=Filter variables\n\n# LOCALIZATION NOTE (emptyPropertiesFilterText): This is the text that\n# appears in the filter text box for the editor's variables view bubble.\nemptyPropertiesFilterText=Filter properties\n\n# LOCALIZATION NOTE (searchPanelFilter): This is the text that appears in the\n# filter panel popup for the filter scripts operation.\nsearchPanelFilter=Filter scripts (%S)\n\n# LOCALIZATION NOTE (searchPanelGlobal): This is the text that appears in the\n# filter panel popup for the global search operation.\nsearchPanelGlobal=Search in all files (%S)\n\n# LOCALIZATION NOTE (searchPanelFunction): This is the text that appears in the\n# filter panel popup for the function search operation.\nsearchPanelFunction=Search for function definition (%S)\n\n# LOCALIZATION NOTE (searchPanelToken): This is the text that appears in the\n# filter panel popup for the token search operation.\nsearchPanelToken=Find in this file (%S)\n\n# LOCALIZATION NOTE (searchPanelGoToLine): This is the text that appears in the\n# filter panel popup for the line search operation.\nsearchPanelGoToLine=Go to line (%S)\n\n# LOCALIZATION NOTE (searchPanelVariable): This is the text that appears in the\n# filter panel popup for the variables search operation.\nsearchPanelVariable=Filter variables (%S)\n\n# LOCALIZATION NOTE (breakpointMenuItem): The text for all the elements that\n# are displayed in the breakpoints menu item popup.\nbreakpointMenuItem.setConditional=Configure conditional breakpoint\nbreakpointMenuItem.enableSelf2.label=Enable\nbreakpointMenuItem.enableSelf2.accesskey=E\nbreakpointMenuItem.disableSelf2.label=Disable\nbreakpointMenuItem.disableSelf2.accesskey=D\nbreakpointMenuItem.deleteSelf2.label=Remove\nbreakpointMenuItem.deleteSelf2.accesskey=R\nbreakpointMenuItem.enableOthers2.label=Enable others\nbreakpointMenuItem.enableOthers2.accesskey=o\nbreakpointMenuItem.disableOthers2.label=Disable others\nbreakpointMenuItem.disableOthers2.accesskey=s\nbreakpointMenuItem.deleteOthers2.label=Remove others\nbreakpointMenuItem.deleteOthers2.accesskey=h\nbreakpointMenuItem.enableAll2.label=Enable all\nbreakpointMenuItem.enableAll2.accesskey=b\nbreakpointMenuItem.disableAll2.label=Disable all\nbreakpointMenuItem.disableAll2.accesskey=k\nbreakpointMenuItem.deleteAll2.label=Remove all\nbreakpointMenuItem.deleteAll2.accesskey=a\nbreakpointMenuItem.removeCondition2.label=Remove condition\nbreakpointMenuItem.removeCondition2.accesskey=c\nbreakpointMenuItem.addCondition2.label=Add condition\nbreakpointMenuItem.addCondition2.accesskey=A\nbreakpointMenuItem.editCondition2.label=Edit condition\nbreakpointMenuItem.editCondition2.accesskey=n\nbreakpointMenuItem.enableSelf=Enable breakpoint\nbreakpointMenuItem.enableSelf.accesskey=E\nbreakpointMenuItem.disableSelf=Disable breakpoint\nbreakpointMenuItem.disableSelf.accesskey=D\nbreakpointMenuItem.deleteSelf=Remove breakpoint\nbreakpointMenuItem.deleteSelf.accesskey=R\nbreakpointMenuItem.enableOthers=Enable others\nbreakpointMenuItem.enableOthers.accesskey=o\nbreakpointMenuItem.disableOthers=Disable others\nbreakpointMenuItem.disableOthers.accesskey=s\nbreakpointMenuItem.deleteOthers=Remove others\nbreakpointMenuItem.deleteOthers.accesskey=h\nbreakpointMenuItem.enableAll=Enable all breakpoints\nbreakpointMenuItem.enableAll.accesskey=b\nbreakpointMenuItem.disableAll=Disable all breakpoints\nbreakpointMenuItem.disableAll.accesskey=k\nbreakpointMenuItem.deleteAll=Remove all breakpoints\nbreakpointMenuItem.deleteAll.accesskey=a\nbreakpointMenuItem.removeCondition.label=Remove breakpoint condition\nbreakpointMenuItem.removeCondition.accesskey=c\nbreakpointMenuItem.editCondition.label=Edit breakpoint condition\nbreakpointMenuItem.editCondition.accesskey=n\n\n# LOCALIZATION NOTE (breakpoints.header): Breakpoints right sidebar pane header.\nbreakpoints.header=Breakpoints\n\n# LOCALIZATION NOTE (breakpoints.none): The text that appears when there are\n# no breakpoints present\nbreakpoints.none=No breakpoints\n\n# LOCALIZATION NOTE (breakpoints.enable): The text that may appear as a tooltip\n# when hovering over the 'disable breakpoints' switch button in right sidebar\nbreakpoints.enable=Enable breakpoints\n\n# LOCALIZATION NOTE (breakpoints.disable): The text that may appear as a tooltip\n# when hovering over the 'disable breakpoints' switch button in right sidebar\nbreakpoints.disable=Disable breakpoints\n\n# LOCALIZATION NOTE (breakpoints.removeBreakpointTooltip): The tooltip that is displayed\n# for remove breakpoint button in right sidebar\nbreakpoints.removeBreakpointTooltip=Remove breakpoint\n\n# LOCALIZATION NOTE (callStack.header): Call Stack right sidebar pane header.\ncallStack.header=Call stack\n\n# LOCALIZATION NOTE (callStack.notPaused): Call Stack right sidebar pane\n# message when not paused.\ncallStack.notPaused=Not paused\n\n# LOCALIZATION NOTE (callStack.collapse): Call Stack right sidebar pane\n# message to hide some of the frames that are shown.\ncallStack.collapse=Collapse rows\n\n# LOCALIZATION NOTE (callStack.expand): Call Stack right sidebar pane\n# message to show more of the frames.\ncallStack.expand=Expand rows\n\n# LOCALIZATION NOTE (editor.searchResults): Editor Search bar message\n# for the summarizing the selected search result. e.g. 5 of 10 results.\neditor.searchResults=%d of %d results\n\n# LOCALIZATION NOTE (editor.singleResult): Copy shown when there is one result.\neditor.singleResult=1 result\n\n# LOCALIZATION NOTE (editor.noResults): Editor Search bar message\n# for when no results found.\neditor.noResults=No results\n\n# LOCALIZATION NOTE (editor.searchResults.nextResult): Editor Search bar\n# tooltip for traversing to the Next Result\neditor.searchResults.nextResult=Next result\n\n# LOCALIZATION NOTE (editor.searchResults.prevResult): Editor Search bar\n# tooltip for traversing to the Previous Result\neditor.searchResults.prevResult=Previous result\n\n# LOCALIZATION NOTE (editor.searchTypeToggleTitle): Search bar title for\n# toggling search type buttons(function search, variable search)\neditor.searchTypeToggleTitle=Search for:\n\n# LOCALIZATION NOTE (editor.continueToHere.label): Editor gutter context\n# menu item for jumping to a new paused location\neditor.continueToHere.label=Continue to here\neditor.continueToHere.accesskey=H\n\n# LOCALIZATION NOTE (editor.addBreakpoint): Editor gutter context menu item\n# for adding a breakpoint on a line.\neditor.addBreakpoint=Add breakpoint\n\n# LOCALIZATION NOTE (editor.disableBreakpoint): Editor gutter context menu item\n# for disabling a breakpoint on a line.\neditor.disableBreakpoint=Disable breakpoint\neditor.disableBreakpoint.accesskey=D\n\n# LOCALIZATION NOTE (editor.enableBreakpoint): Editor gutter context menu item\n# for enabling a breakpoint on a line.\neditor.enableBreakpoint=Enable breakpoint\n\n# LOCALIZATION NOTE (editor.removeBreakpoint): Editor gutter context menu item\n# for removing a breakpoint on a line.\neditor.removeBreakpoint=Remove breakpoint\n\n# LOCALIZATION NOTE (editor.editBreakpoint): Editor gutter context menu item\n# for setting a breakpoint condition on a line.\neditor.editBreakpoint=Edit breakpoint\n\n# LOCALIZATION NOTE (editor.addConditionalBreakpoint): Editor gutter context\n# menu item for adding a breakpoint condition on a line.\neditor.addConditionalBreakpoint=Add conditional breakpoint\neditor.addConditionalBreakpoint.accesskey=c\n\n# LOCALIZATION NOTE (editor.conditionalPanel.placeholder): Placeholder text for\n# input element inside ConditionalPanel component\neditor.conditionalPanel.placeholder=This breakpoint will pause when the expression is true\n\n# LOCALIZATION NOTE (editor.conditionalPanel.close): Tooltip text for\n# close button inside ConditionalPanel component\neditor.conditionalPanel.close=Cancel edit breakpoint and close\n\n# LOCALIZATION NOTE (editor.jumpToMappedLocation1): Context menu item\n# for navigating to a source mapped location\neditor.jumpToMappedLocation1=Jump to %S location\neditor.jumpToMappedLocation1.accesskey=m\n\n# LOCALIZATION NOTE (framework.disableGrouping): This is the text that appears in the\n# context menu to disable framework grouping.\nframework.disableGrouping=Disable framework grouping\nframework.disableGrouping.accesskey=u\n\n# LOCALIZATION NOTE (framework.enableGrouping): This is the text that appears in the\n# context menu to enable framework grouping.\nframework.enableGrouping=Enable framework grouping\nframework.enableGrouping.accesskey=u\n\n# LOCALIZATION NOTE (generated): Source Map term for a server source location\ngenerated=generated\n\n# LOCALIZATION NOTE (original): Source Map term for a debugger UI source location\noriginal=original\n\n# LOCALIZATION NOTE (expressions.placeholder): Placeholder text for expression\n# input element\nexpressions.placeholder=Add watch expression\n# LOCALIZATION NOTE (expressions.errorMsg): Error text for expression\n# input element\nexpressions.errorMsg=Invalid expression…\nexpressions.label=Add watch expression\nexpressions.accesskey=e\n\n# LOCALIZATION NOTE (sourceTabs.closeTab): Editor source tab context menu item\n# for closing the selected tab below the mouse.\nsourceTabs.closeTab=Close tab\nsourceTabs.closeTab.accesskey=c\n\n# LOCALIZATION NOTE (sourceTabs.closeOtherTabs): Editor source tab context menu item\n# for closing the other tabs.\nsourceTabs.closeOtherTabs=Close other tabs\nsourceTabs.closeOtherTabs.accesskey=o\n\n# LOCALIZATION NOTE (sourceTabs.closeTabsToEnd): Editor source tab context menu item\n# for closing the tabs to the end (the right for LTR languages) of the selected tab.\nsourceTabs.closeTabsToEnd=Close tabs to the right\nsourceTabs.closeTabsToEnd.accesskey=e\n\n# LOCALIZATION NOTE (sourceTabs.closeAllTabs): Editor source tab context menu item\n# for closing all tabs.\nsourceTabs.closeAllTabs=Close all tabs\nsourceTabs.closeAllTabs.accesskey=a\n\n# LOCALIZATION NOTE (sourceTabs.revealInTree): Editor source tab context menu item\n# for revealing source in tree.\nsourceTabs.revealInTree=Reveal in tree\nsourceTabs.revealInTree.accesskey=r\n\n# LOCALIZATION NOTE (sourceTabs.prettyPrint): Editor source tab context menu item\n# for pretty printing the source.\nsourceTabs.prettyPrint=Pretty print source\nsourceTabs.prettyPrint.accesskey=p\n\n# LOCALIZATION NOTE (sourceFooter.blackbox): Tooltip text associated\n# with the blackbox button\nsourceFooter.blackbox=Blackbox source\nsourceFooter.blackbox.accesskey=B\n\n# LOCALIZATION NOTE (sourceFooter.unblackbox): Tooltip text associated\n# with the blackbox button\nsourceFooter.unblackbox=Unblackbox source\nsourceFooter.unblackbox.accesskey=b\n\n# LOCALIZATION NOTE (sourceFooter.blackboxed): Text associated\n# with a blackboxed source\nsourceFooter.blackboxed=Blackboxed source\n\n# LOCALIZATION NOTE (sourceFooter.mappedSource): Text associated\n# with a mapped source. %S is replaced by the source map origin.\nsourceFooter.mappedSource=(From %S)\n\n# LOCALIZATION NOTE (sourceFooter.mappedSourceTooltip): Tooltip text associated\n# with a mapped source. %S is replaced by the source map origin.\nsourceFooter.mappedSourceTooltip=(Source mapped from %S)\n\n# LOCALIZATION NOTE (sourceFooter.codeCoverage): Text associated\n# with a code coverage button\nsourceFooter.codeCoverage=Code coverage\n\n# LOCALIZATION NOTE (sourceTabs.closeTabButtonTooltip): The tooltip that is displayed\n# for close tab button in source tabs.\nsourceTabs.closeTabButtonTooltip=Close tab\n\n# LOCALIZATION NOTE (scopes.header): Scopes right sidebar pane header.\nscopes.header=Scopes\n\n# LOCALIZATION NOTE (scopes.notAvailable): Scopes right sidebar pane message\n# for when the debugger is paused, but there isn't pause data.\nscopes.notAvailable=Scopes unavailable\n\n# LOCALIZATION NOTE (scopes.notPaused): Scopes right sidebar pane message\n# for when the debugger is not paused.\nscopes.notPaused=Not paused\n\n# LOCALIZATION NOTE (scopes.block): Refers to a block of code in\n# the scopes pane when the debugger is paused.\nscopes.block=Block\n\n# LOCALIZATION NOTE (sources.header): Sources left sidebar header\nsources.header=Sources\n\n# LOCALIZATION NOTE (outline.header): Outline left sidebar header\noutline.header=Outline\n\n# LOCALIZATION NOTE (outline.noFunctions): Outline text when there are no functions to display\noutline.noFunctions=No functions\n\n# LOCALIZATION NOTE (sources.search): Sources left sidebar prompt\n# e.g. Cmd+P to search. On a mac, we use the command unicode character.\n# On windows, it's ctrl.\nsources.search=%S to search\n\n# LOCALIZATION NOTE (watchExpressions.header): Watch Expressions right sidebar\n# pane header.\nwatchExpressions.header=Watch expressions\n\n# LOCALIZATION NOTE (watchExpressions.refreshButton): Watch Expressions header\n# button for refreshing the expressions.\nwatchExpressions.refreshButton=Refresh\n\n# LOCALIZATION NOTE (welcome.search): The center pane welcome panel's\n# search prompt. e.g. cmd+p to search for files. On windows, it's ctrl, on\n# a mac we use the unicode character.\nwelcome.search=%S to search for sources\n\n# LOCALIZATION NOTE (welcome.findInFiles): The center pane welcome panel's\n# search prompt. e.g. cmd+f to search for files. On windows, it's ctrl+shift+f, on\n# a mac we use the unicode character.\nwelcome.findInFiles=%S to find in files\n\n# LOCALIZATION NOTE (welcome.searchFunction): Label displayed in the welcome\n# panel. %S is replaced by the keyboard shortcut to search for functions.\nwelcome.searchFunction=%S to search for functions in file\n\n# LOCALIZATION NOTE (sourceSearch.search): The center pane Source Search\n# prompt for searching for files.\nsourceSearch.search=Search sources…\n\n# LOCALIZATION NOTE (sourceSearch.noResults2): The center pane Source Search\n# message when the query did not match any of the sources.\nsourceSearch.noResults2=No results found\n\n# LOCALIZATION NOTE (ignoreExceptions): The pause on exceptions button tooltip\n# when the debugger will not pause on exceptions.\nignoreExceptions=Ignore exceptions. Click to pause on uncaught exceptions\n\n# LOCALIZATION NOTE (pauseOnUncaughtExceptions): The pause on exceptions button\n# tooltip when the debugger will pause on uncaught exceptions.\npauseOnUncaughtExceptions=Pause on uncaught exceptions. Click to pause on all exceptions\n\n# LOCALIZATION NOTE (pauseOnExceptions): The pause on exceptions button tooltip\n# when the debugger will pause on all exceptions.\npauseOnExceptions=Pause on all exceptions. Click to ignore exceptions\n\n# LOCALIZATION NOTE (replayPrevious): The replay previous button tooltip\n# when the debugger will go back in stepping history.\nreplayPrevious=Go back one step in history\n\n# LOCALIZATION NOTE (replayNext): The replay next button tooltip\n# when the debugger will go forward in stepping history.\nreplayNext=Go forward one step in history\n\n# LOCALIZATION NOTE (loadingText): The text that is displayed in the script\n# editor when the loading process has started but there is no file to display\n# yet.\nloadingText=Loading\\u2026\n\n# LOCALIZATION NOTE (wasmIsNotAvailable): The text that is displayed in the\n# script editor when the WebAssembly source is not available.\nwasmIsNotAvailable=Please refresh to debug this module\n\n# LOCALIZATION NOTE (errorLoadingText3): The text that is displayed in the debugger\n# viewer when there is an error loading a file\nerrorLoadingText3=Error loading this URI: %S\n\n# LOCALIZATION NOTE (addWatchExpressionText): The text that is displayed in the\n# watch expressions list to add a new item.\naddWatchExpressionText=Add watch expression\n\n# LOCALIZATION NOTE (addWatchExpressionButton): The button that is displayed in the\n# variables view popup.\naddWatchExpressionButton=Watch\n\n# LOCALIZATION NOTE (emptyVariablesText): The text that is displayed in the\n# variables pane when there are no variables to display.\nemptyVariablesText=No variables to display\n\n# LOCALIZATION NOTE (scopeLabel): The text that is displayed in the variables\n# pane as a header for each variable scope (e.g. \"Global scope, \"With scope\",\n# etc.).\nscopeLabel=%S scope\n\n# LOCALIZATION NOTE (watchExpressionsScopeLabel): The name of the watch\n# expressions scope. This text is displayed in the variables pane as a header for\n# the watch expressions scope.\nwatchExpressionsScopeLabel=Watch expressions\n\n# LOCALIZATION NOTE (globalScopeLabel): The name of the global scope. This text\n# is added to scopeLabel and displayed in the variables pane as a header for\n# the global scope.\nglobalScopeLabel=Global\n\n# LOCALIZATION NOTE (variablesViewErrorStacktrace): This is the text that is\n# shown before the stack trace in an error.\nvariablesViewErrorStacktrace=Stack trace:\n\n# LOCALIZATION NOTE (variablesViewMoreObjects): the text that is displayed\n# when you have an object preview that does not show all of the elements. At the end of the list\n# you see \"N more...\" in the web console output.\n# This is a semi-colon list of plural forms.\n# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals\n# #1 number of remaining items in the object\n# example: 3 more…\nvariablesViewMoreObjects=#1 more…;#1 more…\n\n# LOCALIZATION NOTE (variablesEditableNameTooltip): The text that is displayed\n# in the variables list on an item with an editable name.\nvariablesEditableNameTooltip=Double click to edit\n\n# LOCALIZATION NOTE (variablesEditableValueTooltip): The text that is displayed\n# in the variables list on an item with an editable value.\nvariablesEditableValueTooltip=Click to change value\n\n# LOCALIZATION NOTE (variablesCloseButtonTooltip): The text that is displayed\n# in the variables list on an item which can be removed.\nvariablesCloseButtonTooltip=Click to remove\n\n# LOCALIZATION NOTE (variablesEditButtonTooltip): The text that is displayed\n# in the variables list on a getter or setter which can be edited.\nvariablesEditButtonTooltip=Click to set value\n\n# LOCALIZATION NOTE (variablesDomNodeValueTooltip): The text that is displayed\n# in a tooltip on the \"open in inspector\" button in the the variables list for a\n# DOMNode item.\nvariablesDomNodeValueTooltip=Click to select the node in the inspector\n\n# LOCALIZATION NOTE (configurable|...|Tooltip): The text that is displayed\n# in the variables list on certain variables or properties as tooltips.\n# Expanations of what these represent can be found at the following links:\n# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty\n# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/isExtensible\n# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/isFrozen\n# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/isSealed\n# It's probably best to keep these in English.\nconfigurableTooltip=configurable\nenumerableTooltip=enumerable\nwritableTooltip=writable\nfrozenTooltip=frozen\nsealedTooltip=sealed\nextensibleTooltip=extensible\noverriddenTooltip=overridden\nWebIDLTooltip=WebIDL\n\n# LOCALIZATION NOTE (variablesSeparatorLabel): The text that is displayed\n# in the variables list as a separator between the name and value.\nvariablesSeparatorLabel=:\n\n# LOCALIZATION NOTE (watchExpressionsSeparatorLabel2): The text that is displayed\n# in the watch expressions list as a separator between the code and evaluation.\nwatchExpressionsSeparatorLabel2=\\u0020→\n\n# LOCALIZATION NOTE (functionSearchSeparatorLabel): The text that is displayed\n# in the functions search panel as a separator between function's inferred name\n# and its real name (if available).\nfunctionSearchSeparatorLabel=←\n\n# LOCALIZATION NOTE(gotoLineModal.placeholder): The placeholder\n# text displayed when the user searches for specific lines in a file\ngotoLineModal.placeholder=Go to line…\n\n# LOCALIZATION NOTE(gotoLineModal.title): The message shown to users\n# to open the go to line modal\ngotoLineModal.title=Go to a line number in a file\n\n# LOCALIZATION NOTE(gotoLineModal.key2): The shortcut for opening the\n# go to line modal\n# Do not localize \"CmdOrCtrl+;\", or change the format of the string. These are\n# key identifiers, not messages displayed to the user.\ngotoLineModal.key2=CmdOrCtrl+;\n\n# LOCALIZATION NOTE(symbolSearch.search.functionsPlaceholder): The placeholder\n# text displayed when the user searches for functions in a file\nsymbolSearch.search.functionsPlaceholder=Search functions…\nsymbolSearch.search.functionsPlaceholder.title=Search for a function in a file\n\n# LOCALIZATION NOTE(symbolSearch.search.variablesPlaceholder): The placeholder\n# text displayed when the user searches for variables in a file\nsymbolSearch.search.variablesPlaceholder=Search variables…\nsymbolSearch.search.variablesPlaceholder.title=Search for a variable in a file\n\n# LOCALIZATION NOTE(symbolSearch.search.key2): The Key Shortcut for\n# searching for a function or variable\n# Do not localize \"CmdOrCtrl+Shift+O\", or change the format of the string. These are\n# key identifiers, not messages displayed to the user.\nsymbolSearch.search.key2=CmdOrCtrl+Shift+O\n\n# LOCALIZATION NOTE(symbolSearch.searchModifier.modifiersLabel): A label\n# preceding the group of modifiers\nsymbolSearch.searchModifier.modifiersLabel=Modifiers:\n\n# LOCALIZATION NOTE(symbolSearch.searchModifier.regex): A search option\n# when searching text in a file\nsymbolSearch.searchModifier.regex=Regex\n\n# LOCALIZATION NOTE(symbolSearch.searchModifier.caseSensitive): A search option\n# when searching text in a file\nsymbolSearch.searchModifier.caseSensitive=Case sensitive\n\n# LOCALIZATION NOTE(symbolSearch.searchModifier.wholeWord): A search option\n# when searching text in a file\nsymbolSearch.searchModifier.wholeWord=Whole word\n\n# LOCALIZATION NOTE (resumptionOrderPanelTitle): This is the text that appears\n# as a description in the notification panel popup, when multiple debuggers are\n# open in separate tabs and the user tries to resume them in the wrong order.\n# The substitution parameter is the URL of the last paused window that must be\n# resumed first.\nresumptionOrderPanelTitle=There are one or more paused debuggers. Please resume the most-recently paused debugger first at: %S\n\nvariablesViewOptimizedOut=(optimized away)\nvariablesViewUninitialized=(uninitialized)\nvariablesViewMissingArgs=(unavailable)\n\nanonymousSourcesLabel=Anonymous sources\n\nexperimental=This is an experimental feature\n\n# LOCALIZATION NOTE (whyPaused.debuggerStatement): The text that is displayed\n# in a info block explaining how the debugger is currently paused due to a `debugger`\n# statement in the code\nwhyPaused.debuggerStatement=Paused on debugger statement\n\n# LOCALIZATION NOTE (whyPaused.breakpoint): The text that is displayed\n# in a info block explaining how the debugger is currently paused on a breakpoint\nwhyPaused.breakpoint=Paused on breakpoint\n\n# LOCALIZATION NOTE (whyPaused.exception): The text that is displayed\n# in a info block explaining how the debugger is currently paused on an exception\nwhyPaused.exception=Paused on exception\n\n# LOCALIZATION NOTE (whyPaused.resumeLimit): The text that is displayed\n# in a info block explaining how the debugger is currently paused while stepping\n# in or out of the stack\nwhyPaused.resumeLimit=Paused while stepping\n\n# LOCALIZATION NOTE (whyPaused.pauseOnDOMEvents): The text that is displayed\n# in a info block explaining how the debugger is currently paused on a\n# dom event\nwhyPaused.pauseOnDOMEvents=Paused on event listener\n\n# LOCALIZATION NOTE (whyPaused.breakpointConditionThrown): The text that is displayed\n# in an info block when evaluating a conditional breakpoint throws an error\nwhyPaused.breakpointConditionThrown=Error with conditional breakpoint\n\n# LOCALIZATION NOTE (whyPaused.xhr): The text that is displayed\n# in a info block explaining how the debugger is currently paused on an\n# xml http request\nwhyPaused.xhr=Paused on XMLHttpRequest\n\n# LOCALIZATION NOTE (whyPaused.promiseRejection): The text that is displayed\n# in a info block explaining how the debugger is currently paused on a\n# promise rejection\nwhyPaused.promiseRejection=Paused on promise rejection\n\n# LOCALIZATION NOTE (whyPaused.assert): The text that is displayed\n# in a info block explaining how the debugger is currently paused on an\n# assert\nwhyPaused.assert=Paused on assertion\n\n# LOCALIZATION NOTE (whyPaused.debugCommand): The text that is displayed\n# in a info block explaining how the debugger is currently paused on a\n# debugger statement\nwhyPaused.debugCommand=Paused on debugged function\n\n# LOCALIZATION NOTE (whyPaused.other): The text that is displayed\n# in a info block explaining how the debugger is currently paused on an event\n# listener breakpoint set\nwhyPaused.other=Debugger paused\n\n# LOCALIZATION NOTE (ctrl): The text that is used for documenting\n# keyboard shortcuts that use the control key\nctrl=Ctrl\n\n# LOCALIZATION NOTE (anonymous): The text that is displayed when the\n# display name is null.\nanonymous=(anonymous)\n\n# LOCALIZATION NOTE (shortcuts.toggleBreakpoint): text describing\n# keyboard shortcut action for toggling breakpoint\nshortcuts.toggleBreakpoint=Toggle Breakpoint\nshortcuts.toggleBreakpoint.accesskey=B\n\n# LOCALIZATION NOTE (shortcuts.toggleCondPanel): text describing\n# keyboard shortcut action for toggling conditional panel keyboard\nshortcuts.toggleCondPanel=Toggle Conditional Panel\n\n# LOCALIZATION NOTE (shortcuts.pauseOrResume): text describing\n# keyboard shortcut action for pause of resume\nshortcuts.pauseOrResume=Pause/Resume\n\n# LOCALIZATION NOTE (shortcuts.stepOver): text describing\n# keyboard shortcut action for stepping over\nshortcuts.stepOver=Step Over\n\n# LOCALIZATION NOTE (shortcuts.stepIn): text describing\n# keyboard shortcut action for stepping in\nshortcuts.stepIn=Step In\n\n# LOCALIZATION NOTE (shortcuts.stepOut): text describing\n# keyboard shortcut action for stepping out\nshortcuts.stepOut=Step Out\n\n# LOCALIZATION NOTE (shortcuts.fileSearch): text describing\n# keyboard shortcut action for source file search\nshortcuts.fileSearch=Source File Search\n\n# LOCALIZATION NOTE (shortcuts.gotoLine): text describing\n# keyboard shortcut for jumping to a specific line\nshortcuts.gotoLine=Go to line\n\n# LOCALIZATION NOTE (shortcuts.searchAgain): text describing\n# keyboard shortcut action for searching again\nshortcuts.searchAgain=Search Again\n\n# LOCALIZATION NOTE (shortcuts.projectSearch): text describing\n# keyboard shortcut action for full project search\nshortcuts.projectSearch=Full Project Search\n\n# LOCALIZATION NOTE (shortcuts.functionSearch): text describing\n# keyboard shortcut action for function search\nshortcuts.functionSearch=Function Search\n\n# LOCALIZATION NOTE (shortcuts.buttonName): text describing\n# keyboard shortcut button text\nshortcuts.buttonName=Keyboard shortcuts\n"

/***/ }),
/* 961 */,
/* 962 */,
/* 963 */,
/* 964 */,
/* 965 */
/***/ (function(module, exports) {


/**
 * slice() reference.
 */

var slice = Array.prototype.slice;

/**
 * Expose `co`.
 */

module.exports = co['default'] = co.co = co;

/**
 * Wrap the given generator `fn` into a
 * function that returns a promise.
 * This is a separate function so that
 * every `co()` call doesn't create a new,
 * unnecessary closure.
 *
 * @param {GeneratorFunction} fn
 * @return {Function}
 * @api public
 */

co.wrap = function (fn) {
  createPromise.__generatorFunction__ = fn;
  return createPromise;
  function createPromise() {
    return co.call(this, fn.apply(this, arguments));
  }
};

/**
 * Execute the generator function or a generator
 * and return a promise.
 *
 * @param {Function} fn
 * @return {Promise}
 * @api public
 */

function co(gen) {
  var ctx = this;
  var args = slice.call(arguments, 1)

  // we wrap everything in a promise to avoid promise chaining,
  // which leads to memory leak errors.
  // see https://github.com/tj/co/issues/180
  return new Promise(function(resolve, reject) {
    if (typeof gen === 'function') gen = gen.apply(ctx, args);
    if (!gen || typeof gen.next !== 'function') return resolve(gen);

    onFulfilled();

    /**
     * @param {Mixed} res
     * @return {Promise}
     * @api private
     */

    function onFulfilled(res) {
      var ret;
      try {
        ret = gen.next(res);
      } catch (e) {
        return reject(e);
      }
      next(ret);
    }

    /**
     * @param {Error} err
     * @return {Promise}
     * @api private
     */

    function onRejected(err) {
      var ret;
      try {
        ret = gen.throw(err);
      } catch (e) {
        return reject(e);
      }
      next(ret);
    }

    /**
     * Get the next value in the generator,
     * return a promise.
     *
     * @param {Object} ret
     * @return {Promise}
     * @api private
     */

    function next(ret) {
      if (ret.done) return resolve(ret.value);
      var value = toPromise.call(ctx, ret.value);
      if (value && isPromise(value)) return value.then(onFulfilled, onRejected);
      return onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, '
        + 'but the following object was passed: "' + String(ret.value) + '"'));
    }
  });
}

/**
 * Convert a `yield`ed value into a promise.
 *
 * @param {Mixed} obj
 * @return {Promise}
 * @api private
 */

function toPromise(obj) {
  if (!obj) return obj;
  if (isPromise(obj)) return obj;
  if (isGeneratorFunction(obj) || isGenerator(obj)) return co.call(this, obj);
  if ('function' == typeof obj) return thunkToPromise.call(this, obj);
  if (Array.isArray(obj)) return arrayToPromise.call(this, obj);
  if (isObject(obj)) return objectToPromise.call(this, obj);
  return obj;
}

/**
 * Convert a thunk to a promise.
 *
 * @param {Function}
 * @return {Promise}
 * @api private
 */

function thunkToPromise(fn) {
  var ctx = this;
  return new Promise(function (resolve, reject) {
    fn.call(ctx, function (err, res) {
      if (err) return reject(err);
      if (arguments.length > 2) res = slice.call(arguments, 1);
      resolve(res);
    });
  });
}

/**
 * Convert an array of "yieldables" to a promise.
 * Uses `Promise.all()` internally.
 *
 * @param {Array} obj
 * @return {Promise}
 * @api private
 */

function arrayToPromise(obj) {
  return Promise.all(obj.map(toPromise, this));
}

/**
 * Convert an object of "yieldables" to a promise.
 * Uses `Promise.all()` internally.
 *
 * @param {Object} obj
 * @return {Promise}
 * @api private
 */

function objectToPromise(obj){
  var results = new obj.constructor();
  var keys = Object.keys(obj);
  var promises = [];
  for (var i = 0; i < keys.length; i++) {
    var key = keys[i];
    var promise = toPromise.call(this, obj[key]);
    if (promise && isPromise(promise)) defer(promise, key);
    else results[key] = obj[key];
  }
  return Promise.all(promises).then(function () {
    return results;
  });

  function defer(promise, key) {
    // predefine the key in the result
    results[key] = undefined;
    promises.push(promise.then(function (res) {
      results[key] = res;
    }));
  }
}

/**
 * Check if `obj` is a promise.
 *
 * @param {Object} obj
 * @return {Boolean}
 * @api private
 */

function isPromise(obj) {
  return 'function' == typeof obj.then;
}

/**
 * Check if `obj` is a generator.
 *
 * @param {Mixed} obj
 * @return {Boolean}
 * @api private
 */

function isGenerator(obj) {
  return 'function' == typeof obj.next && 'function' == typeof obj.throw;
}

/**
 * Check if `obj` is a generator function.
 *
 * @param {Mixed} obj
 * @return {Boolean}
 * @api private
 */
function isGeneratorFunction(obj) {
  var constructor = obj.constructor;
  if (!constructor) return false;
  if ('GeneratorFunction' === constructor.name || 'GeneratorFunction' === constructor.displayName) return true;
  return isGenerator(constructor.prototype);
}

/**
 * Check for plain object.
 *
 * @param {Mixed} val
 * @return {Boolean}
 * @api private
 */

function isObject(val) {
  return Object == val.constructor;
}


/***/ }),
/* 966 */,
/* 967 */,
/* 968 */,
/* 969 */,
/* 970 */,
/* 971 */,
/* 972 */,
/* 973 */,
/* 974 */,
/* 975 */,
/* 976 */,
/* 977 */,
/* 978 */,
/* 979 */,
/* 980 */,
/* 981 */,
/* 982 */,
/* 983 */,
/* 984 */,
/* 985 */,
/* 986 */,
/* 987 */,
/* 988 */,
/* 989 */,
/* 990 */,
/* 991 */,
/* 992 */,
/* 993 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.defaultMemoize = defaultMemoize;
exports.createSelectorCreator = createSelectorCreator;
exports.createStructuredSelector = createStructuredSelector;
function defaultEqualityCheck(a, b) {
  return a === b;
}

function areArgumentsShallowlyEqual(equalityCheck, 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;
}

function defaultMemoize(func) {
  var equalityCheck = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : defaultEqualityCheck;

  var lastArgs = null;
  var lastResult = null;
  // we reference arguments instead of spreading them for performance reasons
  return function () {
    if (!areArgumentsShallowlyEqual(equalityCheck, lastArgs, arguments)) {
      // apply arguments instead of spreading for performance.
      lastResult = func.apply(null, arguments);
    }

    lastArgs = arguments;
    return lastResult;
  };
}

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;
    }).join(', ');
    throw new Error('Selector creators expect all input-selectors to be functions, ' + ('instead received the following types: [' + dependencyTypes + ']'));
  }

  return dependencies;
}

function createSelectorCreator(memoize) {
  for (var _len = arguments.length, memoizeOptions = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
    memoizeOptions[_key - 1] = arguments[_key];
  }

  return function () {
    for (var _len2 = arguments.length, funcs = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
      funcs[_key2] = arguments[_key2];
    }

    var recomputations = 0;
    var resultFunc = funcs.pop();
    var dependencies = getDependencies(funcs);

    var memoizedResultFunc = memoize.apply(undefined, [function () {
      recomputations++;
      // apply arguments instead of spreading for performance.
      return resultFunc.apply(null, arguments);
    }].concat(memoizeOptions));

    // If a selector is called with the exact same arguments we don't need to traverse our dependencies again.
    var selector = defaultMemoize(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.
        params.push(dependencies[i].apply(null, arguments));
      }

      // apply arguments instead of spreading for performance.
      return memoizedResultFunc.apply(null, params);
    });

    selector.resultFunc = resultFunc;
    selector.recomputations = function () {
      return recomputations;
    };
    selector.resetRecomputations = function () {
      return recomputations = 0;
    };
    return selector;
  };
}

var createSelector = exports.createSelector = createSelectorCreator(defaultMemoize);

function createStructuredSelector(selectors) {
  var selectorCreator = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 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);
  return selectorCreator(objectKeys.map(function (key) {
    return selectors[key];
  }), function () {
    for (var _len3 = arguments.length, values = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
      values[_key3] = arguments[_key3];
    }

    return values.reduce(function (composition, value, index) {
      composition[objectKeys[index]] = value;
      return composition;
    }, {});
  });
}

/***/ }),
/* 994 */,
/* 995 */,
/* 996 */,
/* 997 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"1.1\" viewBox=\"0 0 32 32\"><script></script><path fill=\"#444444\" d=\"M16 9.875l-9.539-5.438v23.698l9.539-5.438 9.539 5.438v-23.698l-9.539 5.438zM11.248 16.286l4.752-2.709 4.752 2.709-4.752 2.709-4.752-2.709zM9.618 9.643l3.399 1.938-3.399 1.938v-3.876zM9.618 19.053l3.145 1.792-3.145 1.793v-3.585zM22.382 22.638l-3.145-1.793 3.145-1.793v3.585zM18.982 11.581l3.399-1.938v3.876l-3.399-1.938z\"></path></svg>"

/***/ }),
/* 998 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" viewBox=\"0 0 17 14\"><path id=\"base-path\" d=\"m10.9,0l-9.9,0c-0.6,0 -1,0.4 -1,1l0,10c0,0.6 0.4,1 1,1l9.9,0c0.6,0 1.2,-0.3 1.5,-0.7l4.6,-5.3l-4.4,-5.3c-0.475,-0.525 -1.1,-0.7 -1.7,-0.7z\"></path></svg>"

/***/ }),
/* 999 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" viewBox=\"0 0 32 32\"><path fill=\"#444444\" d=\"M16.232 24.047c-0.15-0.034-0.295-0.081-0.441-0.124-0.037-0.011-0.074-0.022-0.11-0.033-0.143-0.044-0.284-0.090-0.425-0.139-0.019-0.007-0.039-0.014-0.058-0.021-0.126-0.045-0.251-0.091-0.375-0.139-0.035-0.014-0.070-0.027-0.105-0.041-0.136-0.054-0.271-0.11-0.405-0.168-0.027-0.012-0.054-0.024-0.081-0.036-0.115-0.052-0.228-0.105-0.341-0.159-0.033-0.016-0.065-0.031-0.099-0.047-0.089-0.043-0.177-0.090-0.264-0.134-0.059-0.031-0.118-0.060-0.176-0.092-0.107-0.058-0.212-0.117-0.317-0.178-0.035-0.020-0.071-0.038-0.107-0.059-0.139-0.081-0.277-0.166-0.412-0.252-0.037-0.024-0.074-0.050-0.111-0.074-0.099-0.063-0.197-0.128-0.293-0.195-0.032-0.021-0.063-0.045-0.094-0.066-0.093-0.066-0.186-0.132-0.277-0.2-0.042-0.031-0.082-0.062-0.123-0.093-0.084-0.064-0.168-0.129-0.25-0.196-0.037-0.030-0.075-0.060-0.112-0.090-0.105-0.087-0.209-0.173-0.312-0.263-0.011-0.009-0.023-0.018-0.034-0.028-0.111-0.097-0.22-0.197-0.328-0.298-0.031-0.030-0.062-0.059-0.092-0.088-0.080-0.076-0.158-0.153-0.235-0.231-0.031-0.031-0.062-0.061-0.092-0.092-0.098-0.101-0.194-0.203-0.289-0.306-0.005-0.005-0.010-0.010-0.014-0.015-0.1-0.109-0.197-0.221-0.293-0.334-0.026-0.031-0.051-0.060-0.077-0.091-0.071-0.086-0.142-0.173-0.211-0.261-0.026-0.031-0.052-0.064-0.077-0.096-0.083-0.108-0.164-0.215-0.243-0.324-2.197-2.996-2.986-7.129-1.23-10.523l-1.556 1.974c-1.994 2.866-1.746 6.595-0.223 9.64 0.036 0.073 0.074 0.145 0.112 0.217 0.024 0.045 0.046 0.092 0.071 0.137 0.014 0.027 0.030 0.053 0.044 0.079 0.026 0.049 0.053 0.095 0.079 0.142 0.047 0.083 0.096 0.166 0.145 0.249 0.027 0.045 0.055 0.091 0.083 0.136 0.055 0.089 0.111 0.176 0.169 0.264 0.024 0.037 0.047 0.075 0.072 0.111 0.080 0.118 0.161 0.236 0.244 0.353 0.002 0.003 0.005 0.006 0.007 0.009 0.013 0.018 0.028 0.037 0.041 0.056 0.072 0.1 0.147 0.199 0.223 0.296 0.028 0.036 0.056 0.072 0.084 0.107 0.067 0.085 0.136 0.169 0.206 0.253 0.026 0.031 0.052 0.063 0.079 0.094 0.094 0.11 0.189 0.22 0.287 0.328 0.002 0.002 0.004 0.004 0.006 0.005 0.004 0.005 0.008 0.008 0.011 0.013 0.095 0.104 0.193 0.206 0.291 0.307 0.031 0.032 0.062 0.063 0.093 0.094 0.076 0.077 0.154 0.153 0.233 0.228 0.032 0.030 0.063 0.061 0.095 0.091 0.105 0.099 0.211 0.196 0.319 0.291 0.002 0.001 0.003 0.003 0.005 0.004 0.018 0.016 0.038 0.032 0.056 0.047 0.095 0.082 0.192 0.164 0.29 0.245 0.040 0.032 0.080 0.064 0.12 0.096 0.080 0.064 0.16 0.127 0.241 0.189 0.043 0.033 0.086 0.066 0.129 0.098 0.089 0.066 0.18 0.131 0.271 0.194 0.033 0.024 0.065 0.047 0.099 0.070 0.009 0.006 0.018 0.013 0.027 0.019 0.086 0.060 0.175 0.116 0.263 0.174 0.038 0.025 0.075 0.051 0.114 0.076 0.136 0.086 0.273 0.171 0.412 0.253 0.038 0.022 0.076 0.043 0.114 0.064 0.102 0.059 0.205 0.117 0.309 0.174 0.056 0.030 0.114 0.059 0.171 0.088 0.073 0.038 0.147 0.078 0.221 0.115 0.017 0.009 0.035 0.017 0.051 0.025 0.030 0.014 0.060 0.028 0.091 0.044 0.116 0.055 0.233 0.11 0.351 0.163 0.025 0.011 0.049 0.022 0.074 0.033 0.135 0.059 0.271 0.116 0.409 0.17 0.033 0.014 0.066 0.026 0.1 0.039 0.127 0.049 0.256 0.098 0.386 0.143 0.016 0.006 0.032 0.012 0.049 0.017 0.142 0.050 0.286 0.096 0.43 0.141 0.034 0.010 0.069 0.021 0.104 0.031 0.147 0.044 0.293 0.097 0.445 0.125 9.643 1.759 12.444-5.795 12.444-5.795-2.352 3.065-6.528 3.873-10.485 2.974zM12.758 16.231c0.216 0.31 0.456 0.678 0.742 0.927 0.104 0.114 0.213 0.226 0.324 0.336 0.028 0.029 0.057 0.056 0.085 0.084 0.108 0.105 0.217 0.207 0.33 0.307 0.005 0.003 0.009 0.008 0.014 0.012 0.001 0.001 0.002 0.002 0.003 0.003 0.125 0.11 0.255 0.216 0.386 0.319 0.029 0.022 0.058 0.046 0.088 0.069 0.132 0.101 0.266 0.2 0.404 0.295 0.004 0.003 0.008 0.006 0.012 0.009 0.061 0.042 0.123 0.081 0.184 0.122 0.030 0.019 0.058 0.040 0.088 0.058 0.098 0.063 0.198 0.125 0.299 0.183 0.014 0.009 0.028 0.016 0.042 0.024 0.087 0.051 0.176 0.1 0.265 0.148 0.031 0.018 0.063 0.033 0.094 0.049 0.061 0.032 0.123 0.064 0.185 0.096 0.009 0.004 0.019 0.009 0.028 0.012 0.127 0.063 0.255 0.123 0.386 0.18 0.028 0.012 0.057 0.023 0.085 0.035 0.105 0.045 0.21 0.088 0.316 0.129 0.045 0.017 0.091 0.033 0.135 0.050 0.097 0.036 0.193 0.069 0.291 0.101 0.044 0.014 0.087 0.028 0.131 0.042 0.139 0.043 0.276 0.098 0.42 0.122 7.445 1.233 9.164-4.499 9.164-4.499-1.549 2.232-4.55 3.296-7.752 2.465-0.142-0.038-0.282-0.078-0.422-0.122-0.043-0.013-0.084-0.027-0.127-0.041-0.099-0.032-0.197-0.066-0.295-0.102-0.045-0.017-0.089-0.033-0.133-0.050-0.107-0.041-0.213-0.084-0.317-0.128-0.029-0.013-0.058-0.024-0.086-0.036-0.131-0.057-0.261-0.117-0.389-0.18-0.066-0.032-0.13-0.066-0.195-0.099-0.037-0.019-0.075-0.038-0.112-0.058-0.083-0.045-0.165-0.092-0.246-0.139-0.019-0.011-0.040-0.022-0.059-0.033-0.101-0.059-0.2-0.12-0.299-0.182-0.030-0.019-0.060-0.040-0.090-0.060-0.065-0.042-0.13-0.085-0.193-0.128-0.137-0.095-0.271-0.194-0.402-0.294-0.030-0.024-0.061-0.047-0.091-0.071-1.401-1.107-2.512-2.619-3.041-4.334-0.554-1.778-0.434-3.775 0.525-5.395l-1.178 1.663c-1.442 2.075-1.364 4.853-0.239 7.048 0.189 0.368 0.401 0.725 0.638 1.065zM20.606 13.664c0.061 0.023 0.123 0.043 0.185 0.064 0.027 0.008 0.054 0.018 0.082 0.026 0.088 0.027 0.175 0.060 0.265 0.076 4.111 0.794 5.226-2.11 5.523-2.537-0.977 1.406-2.618 1.744-4.632 1.255-0.159-0.039-0.334-0.096-0.488-0.151-0.197-0.070-0.39-0.15-0.579-0.24-0.358-0.172-0.699-0.38-1.015-0.619-1.802-1.367-2.922-3.976-1.746-6.101l-0.637 0.877c-0.85 1.251-0.933 2.805-0.344 4.186 0.622 1.467 1.897 2.617 3.384 3.163z\"></path></svg>"

/***/ }),
/* 1000 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" viewBox=\"0 -2.5 34 32\"><path d=\"M19.314 15.987c0 1.321-1.071 2.392-2.392 2.392s-2.392-1.071-2.392-2.392c0-1.321 1.071-2.392 2.392-2.392s2.392 1.071 2.392 2.392z\"></path><path d=\"M16.922 24.783c1.878 1.826 3.729 2.906 5.221 2.906 0.489 0 0.952-0.103 1.337-0.334 1.337-0.772 1.826-2.701 1.363-5.453-0.077-0.489-0.18-0.977-0.309-1.492 0.514-0.154 0.977-0.309 1.44-0.463 2.598-1.003 4.038-2.392 4.038-3.909 0-1.543-1.44-2.932-4.038-3.909-0.463-0.18-0.926-0.334-1.44-0.463 0.129-0.514 0.232-1.003 0.309-1.492 0.437-2.803-0.051-4.758-1.389-5.53-0.386-0.231-0.849-0.334-1.337-0.334-1.466 0-3.344 1.080-5.221 2.906-1.852-1.826-3.704-2.906-5.195-2.906-0.489 0-0.952 0.103-1.337 0.334-1.337 0.772-1.826 2.701-1.363 5.453 0.077 0.489 0.18 0.977 0.309 1.492-0.514 0.154-0.977 0.309-1.44 0.463-2.598 1.003-4.038 2.392-4.038 3.909 0 1.543 1.44 2.932 4.038 3.909 0.463 0.18 0.926 0.334 1.44 0.463-0.129 0.514-0.232 1.003-0.309 1.492-0.437 2.752 0.051 4.707 1.363 5.453 0.386 0.232 0.849 0.334 1.337 0.334 1.492 0.051 3.344-1.029 5.221-2.829v0zM15.481 21.311c0.463 0.026 0.952 0.026 1.44 0.026s0.977 0 1.44-0.026c-0.463 0.617-0.952 1.183-1.44 1.723-0.489-0.54-0.977-1.106-1.44-1.723zM12.292 18.662c0.257 0.437 0.489 0.849 0.772 1.26-0.797-0.103-1.543-0.232-2.263-0.386 0.232-0.694 0.489-1.415 0.797-2.135 0.206 0.411 0.437 0.849 0.694 1.26zM10.8 12.463c0.72-0.154 1.466-0.283 2.263-0.386-0.257 0.412-0.514 0.823-0.772 1.26s-0.489 0.849-0.694 1.286c-0.334-0.746-0.592-1.466-0.797-2.161zM12.215 15.987c0.334-0.694 0.694-1.389 1.106-2.083 0.386-0.669 0.823-1.337 1.26-2.006 0.772-0.051 1.543-0.077 2.341-0.077 0.823 0 1.595 0.026 2.341 0.077 0.463 0.669 0.874 1.337 1.26 2.006 0.412 0.694 0.772 1.389 1.106 2.083-0.334 0.694-0.694 1.389-1.106 2.083-0.386 0.669-0.823 1.337-1.26 2.006-0.772 0.051-1.543 0.077-2.341 0.077-0.823 0-1.595-0.026-2.341-0.077-0.463-0.669-0.874-1.337-1.26-2.006-0.412-0.695-0.772-1.389-1.106-2.083v0zM22.272 14.598l-0.694-1.286c-0.257-0.437-0.489-0.849-0.772-1.26 0.797 0.103 1.543 0.232 2.263 0.386-0.231 0.72-0.489 1.44-0.797 2.161v0zM22.272 17.376c0.309 0.72 0.566 1.44 0.797 2.135-0.72 0.154-1.466 0.283-2.263 0.386 0.257-0.412 0.514-0.823 0.772-1.26 0.232-0.386 0.463-0.823 0.694-1.26v0zM22.863 26.301c-0.206 0.129-0.463 0.18-0.746 0.18-1.26 0-2.829-1.029-4.372-2.572 0.746-0.797 1.466-1.698 2.186-2.701 1.209-0.103 2.366-0.283 3.447-0.54 0.129 0.463 0.206 0.926 0.283 1.389 0.36 2.186 0.077 3.755-0.797 4.244zM24.201 12.746c2.881 0.823 4.604 2.083 4.604 3.241 0 1.003-1.183 2.006-3.266 2.804-0.412 0.154-0.874 0.309-1.337 0.437-0.334-1.055-0.746-2.135-1.26-3.241 0.514-1.106 0.952-2.186 1.26-3.241v0zM22.143 5.493c0.283 0 0.514 0.051 0.746 0.18 0.849 0.489 1.157 2.032 0.797 4.244-0.077 0.437-0.18 0.9-0.283 1.389-1.080-0.232-2.238-0.412-3.447-0.54-0.694-1.003-1.44-1.903-2.186-2.701 1.543-1.518 3.112-2.572 4.372-2.572zM18.362 10.663c-0.463-0.026-0.952-0.026-1.44-0.026s-0.977 0-1.44 0.026c0.463-0.617 0.952-1.183 1.44-1.723 0.489 0.54 0.977 1.132 1.44 1.723v0zM10.98 5.673c0.206-0.129 0.463-0.18 0.746-0.18 1.26 0 2.829 1.029 4.372 2.572-0.746 0.797-1.466 1.697-2.186 2.701-1.209 0.103-2.366 0.283-3.447 0.54-0.129-0.463-0.206-0.926-0.283-1.389-0.36-2.186-0.077-3.729 0.797-4.244v0zM9.643 19.228c-2.881-0.823-4.604-2.083-4.604-3.241 0-1.003 1.183-2.006 3.266-2.803 0.412-0.154 0.874-0.309 1.337-0.437 0.334 1.055 0.746 2.135 1.26 3.241-0.514 1.106-0.952 2.212-1.26 3.241zM10.183 22.057c0.077-0.437 0.18-0.9 0.283-1.389 1.080 0.232 2.238 0.412 3.447 0.54 0.694 1.003 1.44 1.903 2.186 2.701-1.543 1.517-3.112 2.572-4.372 2.572-0.283 0-0.514-0.051-0.746-0.18-0.875-0.489-1.157-2.058-0.797-4.244z\"></path></svg>"

/***/ }),
/* 1001 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 774 875.7\"><title>icon</title><path fill=\"#FFF\" d=\"M387 0l387 218.9v437.9L387 875.7 0 656.8V218.9z\"></path><path fill=\"#8ed6fb\" d=\"M704.9 641.7L399.8 814.3V679.9l190.1-104.6 115 66.4zm20.9-18.9V261.9l-111.6 64.5v232l111.6 64.4zM67.9 641.7L373 814.3V679.9L182.8 575.3 67.9 641.7zM47 622.8V261.9l111.6 64.5v232L47 622.8zm13.1-384.3L373 61.5v129.9L172.5 301.7l-1.6.9-110.8-64.1zm652.6 0l-312.9-177v129.9l200.5 110.2 1.6.9 110.8-64z\"></path><path fill=\"#1c78c0\" d=\"M373 649.3L185.4 546.1V341.8L373 450.1v199.2zm26.8 0l187.6-103.1V341.8L399.8 450.1v199.2zm-13.4-207zM198.1 318.2l188.3-103.5 188.3 103.5-188.3 108.7-188.3-108.7z\"></path></svg>"

/***/ }),
/* 1002 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 version=\"1.1\" id=\"Layer_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\" viewBox=\"0 0 128 128\" style=\"enable-background:new 0 0 128 128;\" xml:space=\"preserve\"><style type=\"text/css\"> .st0{fill:#83CD29;} </style><path class=\"st0\" d=\"M114.3,29.6L69.1,3.4c-2.8-1.6-6.6-1.6-9.4,0L14.2,29.6c-2.9,1.7-4.9,4.9-4.9,8.3v52.3c0,3.4,2,6.6,5,8.3 l12,6.8c5.8,2.8,7.3,2.8,10,2.8c8.5,0,12.9-5.2,12.9-14.1V42.3c0-0.7,0.5-1.8-0.3-1.8h-5.7c-0.7,0-2.2,1.1-2.2,1.8v51.6 c0,4-3.7,7.9-10.4,4.6l-12.4-7.2c-0.4-0.2-0.7-0.7-0.7-1.2V37.8c0-0.5,0.5-1,0.9-1.2l45.4-26.1c0.4-0.2,1-0.2,1.4,0l44.9,26.1 c0.4,0.3,0.4,0.7,0.4,1.2v52.3c0,0.5,0.1,1-0.3,1.2l-45.1,26.1c-0.4,0.2-0.9,0.2-1.3,0l-11.6-6.9c-0.3-0.2-0.8-0.3-1.1-0.1 c-3.2,1.8-3.8,2.1-6.8,3.1c-0.7,0.3-1.8,0.7,0.4,2l15.1,8.9c1.4,0.8,3.1,1.3,4.8,1.3c1.7,0,3.3-0.4,4.8-1.3l45-26.1 c2.9-1.7,4.4-4.9,4.4-8.3V37.8C118.7,34.4,117.2,31.3,114.3,29.6z M78.5,81.8c-12,0-14.6-3.3-15.5-9.3c-0.1-0.6-0.6-1.4-1.3-1.4 h-5.9c-0.7,0-1.3,0.9-1.3,1.6c0,7.6,4.2,16.9,24,16.9c14.3,0,22.6-5.6,22.6-15.4c0-9.8-6.6-12.4-20.5-14.2 c-14.1-1.9-15.5-2.8-15.5-6.1c0-2.7,1.2-6.3,11.6-6.3c9.3,0,12.7,2,14.1,8.3c0.1,0.6,0.7,1,1.3,1H98c0.4,0,0.7-0.1,1-0.4 c0.2-0.3,0.4-0.6,0.3-1c-0.9-10.8-8.1-15.8-22.6-15.8c-12.9,0-20.6,5.5-20.6,14.6c0,9.9,7.7,12.7,20.1,13.9c14.8,1.5,16,3.6,16,6.5 C92.1,79.7,88.1,81.8,78.5,81.8z\"></path></svg>"

/***/ }),
/* 1003 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 viewBox=\"0 0 512 149\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" preserveAspectRatio=\"xMidYMid\"><g><path d=\"M3.33224862,115.629027 L3.33224862,58.6475756 L74.4757566,58.6475756 L74.4757566,55.315327 L3.33224862,55.315327 L3.33224862,3.33224862 L78.9742922,3.33224862 L78.9742922,0 L-3.55271368e-15,0 L-3.55271368e-15,118.961276 L79.640742,118.961276 L79.640742,115.629027 L3.33224862,115.629027 L3.33224862,115.629027 Z M143.786528,33.3224862 L114.296128,72.1431826 L85.472177,33.3224862 L81.1402538,33.3224862 L112.296778,74.642369 L78.14123,118.961276 L82.1399284,118.961276 L114.296128,77.1415554 L146.618939,118.961276 L150.78425,118.961276 L116.462089,74.642369 L147.785226,33.3224862 L143.786528,33.3224862 L143.786528,33.3224862 Z M160.780996,148.285063 L160.780996,94.9690856 L161.114221,94.9690856 C163.11358,102.744371 167.056701,108.992275 172.943703,113.712984 C178.830705,118.433693 186.32819,120.794012 195.436381,120.794012 C201.323384,120.794012 206.543854,119.599969 211.09795,117.211845 C215.652046,114.823722 219.456324,111.574812 222.510902,107.465018 C225.565478,103.355224 227.898028,98.5790488 229.508624,93.1363488 C231.119218,87.6936488 231.924504,81.973346 231.924504,75.9752684 C231.924504,69.532889 231.09145,63.5904384 229.425318,58.1477384 C227.759184,52.7050384 225.343328,47.9844 222.177676,43.9856818 C219.012024,39.9869634 215.179976,36.8768958 210.681418,34.6553856 C206.18286,32.4338754 201.101232,31.323137 195.436381,31.323137 C191.104437,31.323137 187.07801,31.9618116 183.35698,33.23918 C179.635951,34.5165484 176.331504,36.3214982 173.443541,38.654084 C170.555577,40.9866696 168.056416,43.7357472 165.945981,46.9013992 C163.835546,50.0670512 162.224976,53.5381088 161.114221,57.3146762 L160.780996,57.3146762 L160.780996,33.3224862 L157.448747,33.3224862 L157.448747,148.285063 L160.780996,148.285063 L160.780996,148.285063 Z M195.436381,117.628376 C184.995284,117.628376 176.609208,114.046245 170.277904,106.881874 C163.9466,99.717504 160.780996,89.415405 160.780996,75.9752684 C160.780996,70.421493 161.558513,65.1454854 163.11357,60.1470876 C164.668627,55.1486896 166.917872,50.7612728 169.861373,46.9847054 C172.804874,43.2081382 176.442543,40.2091444 180.774487,37.9876342 C185.106432,35.766124 189.993681,34.6553856 195.436381,34.6553856 C200.990156,34.6553856 205.849638,35.766124 210.01497,37.9876342 C214.1803,40.2091444 217.62359,43.2359066 220.34494,47.0680118 C223.06629,50.9001168 225.121156,55.2875336 226.5096,60.2303938 C227.898044,65.173254 228.592256,70.421493 228.592256,75.9752684 C228.592256,80.9736664 227.95358,85.9442208 226.676212,90.887081 C225.398844,95.8299412 223.427284,100.272895 220.76147,104.216075 C218.095658,108.159256 214.680138,111.380398 210.514806,113.879596 C206.349474,116.378795 201.323384,117.628376 195.436381,117.628376 L195.436381,117.628376 L195.436381,117.628376 Z M250.251872,118.961276 L250.251872,70.4770582 C250.251872,65.8118868 250.918314,61.2578592 252.25122,56.814839 C253.584126,52.3718186 255.638992,48.4564656 258.41588,45.0686626 C261.192768,41.6808596 264.719362,39.0150872 268.99577,37.0712658 C273.272176,35.1274444 278.353806,34.322159 284.240808,34.6553856 L284.240808,31.323137 C279.131334,31.2120614 274.660612,31.7674308 270.828506,32.9892614 C266.996402,34.211092 263.691954,35.8771996 260.915066,37.9876342 C258.138178,40.098069 255.916702,42.569462 254.25057,45.4018874 C252.584436,48.2343128 251.362624,51.2610752 250.585096,54.4822648 L250.251872,54.4822648 L250.251872,33.3224862 L246.919622,33.3224862 L246.919622,118.961276 L250.251872,118.961276 L250.251872,118.961276 Z M288.406118,76.8083306 L360.049464,76.8083306 C360.271614,70.9213286 359.688476,65.2565626 358.300032,59.8138626 C356.911588,54.3711628 354.690112,49.5394506 351.635536,45.3185812 C348.580958,41.0977118 344.637838,37.7099596 339.806052,35.155223 C334.974268,32.6004862 329.226196,31.323137 322.561666,31.323137 C317.78542,31.323137 313.120318,32.3228016 308.566222,34.3221608 C304.012126,36.32152 300.013468,39.2372084 296.570126,43.0693134 C293.126786,46.9014184 290.34994,51.5942884 288.239506,57.1480638 C286.12907,62.7018392 285.07387,69.0330484 285.07387,76.1418808 C285.07387,82.473185 285.79585,88.387867 287.23983,93.8861048 C288.683812,99.3843424 290.90529,104.160518 293.904328,108.214774 C296.903366,112.26903 300.763182,115.406866 305.483892,117.628376 C310.204602,119.849886 315.897136,120.905088 322.561666,120.794012 C332.33631,120.794012 340.555776,118.044935 347.220306,112.546697 C353.884836,107.048459 357.827958,99.3010588 359.049788,89.304263 L355.71754,89.304263 C354.273558,98.7456812 350.580352,105.826639 344.637814,110.547348 C338.695274,115.268057 331.225558,117.628376 322.228442,117.628376 C316.119288,117.628376 310.954354,116.573175 306.733486,114.46274 C302.512616,112.352305 299.069326,109.464385 296.403514,105.798894 C293.737702,102.133402 291.766142,97.8292904 290.488774,92.8864302 C289.211404,87.94357 288.517194,82.5842572 288.406118,76.8083306 L288.406118,76.8083306 L288.406118,76.8083306 Z M356.717214,73.476082 L288.406118,73.476082 C288.739344,67.4780046 289.850082,62.0909232 291.738366,57.3146762 C293.62665,52.5384294 296.098044,48.4564656 299.15262,45.0686626 C302.207196,41.6808596 305.76156,39.0983926 309.815816,37.3211846 C313.870072,35.5439764 318.22972,34.6553856 322.89489,34.6553856 C328.448666,34.6553856 333.335916,35.6828186 337.556784,37.7377156 C341.777654,39.7926126 345.304248,42.597227 348.136674,46.1516434 C350.9691,49.7060596 353.107272,53.8435602 354.551252,58.5642694 C355.995234,63.2849786 356.717214,68.255533 356.717214,73.476082 L356.717214,73.476082 L356.717214,73.476082 Z M429.193622,58.6475756 L432.52587,58.6475756 C432.52587,49.0950818 429.749024,42.1529666 424.195248,37.8210218 C418.641474,33.489077 411.088452,31.323137 401.535958,31.323137 C396.204334,31.323137 391.705844,31.98958 388.040352,33.3224862 C384.37486,34.6553922 381.375866,36.3770368 379.04328,38.4874716 C376.710694,40.5979062 375.044586,42.930457 374.044908,45.4851936 C373.045228,48.0399304 372.545396,50.4835548 372.545396,52.8161406 C372.545396,57.481312 373.37845,61.2022858 375.044582,63.9791734 C376.710714,66.7560612 379.32095,68.9220012 382.875366,70.4770582 C385.319028,71.5878134 388.095874,72.587478 391.205988,73.476082 C394.316102,74.364686 397.926002,75.3088138 402.035796,76.3084934 C405.701288,77.1970974 409.311188,78.0856882 412.865604,78.9742922 C416.42002,79.8628962 419.557856,81.0569402 422.279206,82.5564594 C425.000556,84.0559788 427.222032,85.9720026 428.943704,88.3045884 C430.665374,90.637174 431.526196,93.6917048 431.526196,97.468272 C431.526196,101.133764 430.665374,104.243831 428.943704,106.798568 C427.222032,109.353305 425.028324,111.435939 422.362512,113.046534 C419.6967,114.657129 416.725474,115.823405 413.448748,116.545395 C410.17202,117.267386 406.978646,117.628376 403.868532,117.628376 C393.760662,117.628376 386.01326,115.379131 380.626098,110.880573 C375.238936,106.382015 372.545396,99.3010572 372.545396,89.6374878 L369.213146,89.6374878 C369.213146,100.411812 372.128836,108.298055 377.9603,113.296453 C383.791764,118.294851 392.427754,120.794012 403.868532,120.794012 C407.534024,120.794012 411.22723,120.377485 414.94826,119.544419 C418.669288,118.711353 422.001504,117.350698 424.945004,115.462415 C427.888506,113.574131 430.276594,111.130506 432.10934,108.131468 C433.942086,105.132429 434.858444,101.466992 434.858444,97.1350472 C434.858444,93.0252534 434.05316,89.693038 432.442564,87.1383014 C430.83197,84.5835646 428.721566,82.4731616 426.111292,80.807029 C423.501018,79.1408964 420.55756,77.8357786 417.280834,76.8916368 C414.004106,75.947495 410.699658,75.0311358 407.367394,74.1425318 C402.702222,72.9207012 398.620258,71.8654996 395.12138,70.9768956 C391.622502,70.0882914 388.373592,69.03309 385.374552,67.8112594 C382.48659,66.5894288 380.181808,64.8400158 378.460136,62.5629678 C376.738466,60.2859198 375.877644,57.03701 375.877644,52.8161406 C375.877644,52.038612 376.099792,50.650189 376.544094,48.6508298 C376.988396,46.6514706 378.043598,44.624373 379.70973,42.569476 C381.375862,40.5145792 383.93056,38.6818608 387.373902,37.0712658 C390.817242,35.460671 395.53788,34.6553856 401.535958,34.6553856 C405.645752,34.6553856 409.394494,35.099681 412.782298,35.988285 C416.1701,36.8768892 419.085788,38.2930806 421.52945,40.236902 C423.973112,42.1807234 425.861366,44.6521164 427.194272,47.6511552 C428.527178,50.650194 429.193622,54.3156308 429.193622,58.6475756 L429.193622,58.6475756 L429.193622,58.6475756 Z M506.335178,58.6475756 L509.667426,58.6475756 C509.667426,49.0950818 506.89058,42.1529666 501.336804,37.8210218 C495.783028,33.489077 488.230008,31.323137 478.677514,31.323137 C473.34589,31.323137 468.847398,31.98958 465.181906,33.3224862 C461.516416,34.6553922 458.517422,36.3770368 456.184836,38.4874716 C453.85225,40.5979062 452.186142,42.930457 451.186462,45.4851936 C450.186784,48.0399304 449.68695,50.4835548 449.68695,52.8161406 C449.68695,57.481312 450.520004,61.2022858 452.186138,63.9791734 C453.85227,66.7560612 456.462506,68.9220012 460.016922,70.4770582 C462.460582,71.5878134 465.237428,72.587478 468.347544,73.476082 C471.457658,74.364686 475.067558,75.3088138 479.177352,76.3084934 C482.842842,77.1970974 486.452742,78.0856882 490.00716,78.9742922 C493.561576,79.8628962 496.699412,81.0569402 499.420762,82.5564594 C502.142112,84.0559788 504.363588,85.9720026 506.085258,88.3045884 C507.80693,90.637174 508.667752,93.6917048 508.667752,97.468272 C508.667752,101.133764 507.80693,104.243831 506.085258,106.798568 C504.363588,109.353305 502.16988,111.435939 499.504068,113.046534 C496.838256,114.657129 493.86703,115.823405 490.590302,116.545395 C487.313576,117.267386 484.120202,117.628376 481.010088,117.628376 C470.902216,117.628376 463.154816,115.379131 457.767654,110.880573 C452.380492,106.382015 449.68695,99.3010572 449.68695,89.6374878 L446.354702,89.6374878 C446.354702,100.411812 449.27039,108.298055 455.101854,113.296453 C460.93332,118.294851 469.56931,120.794012 481.010088,120.794012 C484.67558,120.794012 488.368784,120.377485 492.089814,119.544419 C495.810844,118.711353 499.14306,117.350698 502.08656,115.462415 C505.030062,113.574131 507.418148,111.130506 509.250894,108.131468 C511.08364,105.132429 512,101.466992 512,97.1350472 C512,93.0252534 511.194714,89.693038 509.58412,87.1383014 C507.973524,84.5835646 505.863122,82.4731616 503.252848,80.807029 C500.642572,79.1408964 497.699116,77.8357786 494.422388,76.8916368 C491.145662,75.947495 487.841214,75.0311358 484.508948,74.1425318 C479.843778,72.9207012 475.761814,71.8654996 472.262936,70.9768956 C468.764056,70.0882914 465.515146,69.03309 462.516108,67.8112594 C459.628144,66.5894288 457.323362,64.8400158 455.601692,62.5629678 C453.880022,60.2859198 453.0192,57.03701 453.0192,52.8161406 C453.0192,52.038612 453.241348,50.650189 453.68565,48.6508298 C454.129952,46.6514706 455.185152,44.624373 456.851286,42.569476 C458.517418,40.5145792 461.072116,38.6818608 464.515458,37.0712658 C467.958798,35.460671 472.679436,34.6553856 478.677514,34.6553856 C482.787308,34.6553856 486.53605,35.099681 489.923852,35.988285 C493.311656,36.8768892 496.227344,38.2930806 498.671006,40.236902 C501.114666,42.1807234 503.002922,44.6521164 504.335828,47.6511552 C505.668734,50.650194 506.335178,54.3156308 506.335178,58.6475756 L506.335178,58.6475756 L506.335178,58.6475756 Z\" fill=\"#222222\"></path></g></svg>"

/***/ }),
/* 1004 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 version=\"1.1\" id=\"Layer_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\" viewBox=\"0 0 128 128\" style=\"enable-background:new 0 0 128 128;\" xml:space=\"preserve\"><style type=\"text/css\"> .st0{fill:#C1272D;} .st1{fill:#EFCCA3;} .st2{fill:#ED1C24;} .st3{fill:#CCAC8D;} .st4{fill:#FFFFFF;} .st5{fill:#FF931E;} .st6{fill:#FFB81E;} .st7{fill:#56332B;} .st8{fill:#442823;} .st9{fill:#7F4A41;} .st10{fill:#331712;} .st11{fill:#FFCC66;} .st12{fill:#CCCCCC;} .st13{fill:#B3B3B3;} .st14{fill:#989898;} .st15{fill:#323232;} .st16{fill:#1E1E1E;} .st17{fill:#4C4C4C;} .st18{fill:#E6E6E6;} .st19{fill:#606060;} </style><g><path class=\"st1\" d=\"M107.4,50.9c-0.2-4.4,0.4-8.3-1.6-11.6c-4.8-8.2-16.8-13-40.8-13V27c0,0-0.5,0-0.5,0c0,0,0.5,0,0.5,0v-0.7 c-24,0-36.6,4.8-41.4,13.1c-1.9,3.4-1.7,7.2-2,11.6c-0.2,3.5-1.8,7.2-1.1,11.2c0.8,5.2,1.1,10.4,1.9,15.2c0.6,3.9,6,7.2,6.5,10.9 c1.4,10.2,12,14.9,36,14.9v0.8h-0.6h0.1H65v-0.8c24,0,34.2-4.7,35.5-14.9c0.5-3.8,5.5-7,6.1-10.9c0.8-4.8,1.1-10,1.9-15.2 C109.2,58.2,107.6,54.4,107.4,50.9z\"></path><path class=\"st3\" d=\"M64.6,54.5c4.3,0.1,7.3,2.8,10.1,5.3c3.3,2.9,8.9,4.9,11.2,7.4c2.3,2.5,5.3,5,6.4,8.9 c1.1,3.9,1.4,8.9,1.4,10.2c0,1.3,0.7,1,2.7,0c4.7-2.3,9.9-8.5,9.9-8.5c-0.6,3.9-5.7,7.4-6.2,11.1C98.9,99.1,89,104,64.5,104h-0.1h0 H65\"></path><path class=\"st3\" d=\"M80.4,46.7c0.9,3.1,4.1,13.6-2.1,10.1c0,0,2.6,1.5,4.2,7.2c1.7,5.7,5.8,6.4,5.8,6.4s6.7,1.3,11.7-3 c4.2-3.6,4.9-10,3.1-14.9c-1.8-4.8-5-6.3-9.7-7.3C88.7,44.1,79.3,43.2,80.4,46.7z\"></path><g><circle cx=\"92.3\" cy=\"58.1\" r=\"8.8\"></circle><circle class=\"st4\" cx=\"90\" cy=\"54.2\" r=\"2.3\"></circle></g><path class=\"st1\" d=\"M78.9,57.7c0,0,7.9,5.4,12.2,10.7c4.3,5.3,4.2,6.3,4.2,6.3l-3.1,1.4c0,0-4.4-8.3-9.8-11.4 c-5.5-3.1-6.1-5.7-6.1-5.7L78.9,57.7z\"></path><path class=\"st3\" d=\"M64.9,54.5c-4.3,0.1-7.5,2.8-10.4,5.3c-3.3,2.9-9.1,4.9-11.4,7.4c-2.3,2.5-5.4,5-6.5,8.9 c-1.1,3.9-1.5,8.9-1.5,10.2c0,1.3,0.2,1.4-2.7,0c-4.7-2.2-9.9-8.5-9.9-8.5c0.6,3.9,5.7,7.4,6.2,11.1C30.1,99.1,40,104,64.5,104h0.1 h0H65\"></path><path class=\"st7\" d=\"M88.1,71.4C83.3,65.5,75.6,60,64.9,60h-0.1h0c-10.7,0-18.4,5.5-23.2,11.4c-5,6.1-4.6,8.5-4.6,14.3 c0,21,7.4,15,12.3,17.6c5,2.5,10.2,1.7,15.5,1.7h0h0.1c5.4,0,10.5,0.7,15.5-1.8c4.9-2.5,12.3,3.7,12.3-17.3 C92.8,80.1,93.1,77.5,88.1,71.4z\"></path><path class=\"st8\" d=\"M64.4,65.2c0,0-0.7,9.7-2.1,11.6l2.6-0.6L64.4,65.2z\"></path><path class=\"st8\" d=\"M65.1,65.2c0,0,0.7,9.7,2.1,11.6l-2.6-0.6L65.1,65.2z\"></path><path class=\"st7\" d=\"M56.7,62.9c-1-2.3,2.6-6,8.3-6.1c5.7,0,9.3,3.7,8.3,6.1c-1,2.4-4.6,3.1-8.3,3.2C61.4,66,57.7,65.3,56.7,62.9z\"></path><path d=\"M65,65.2c0-0.4,3.4-0.5,5.2-1.7c0,0-3.7,1.2-4.5,0.7c-0.8-0.4-1-1.6-1-1.6s-0.3,1.2-0.9,1.6c-0.7,0.4-4.9-0.7-4.9-0.7 s5.6,1.4,5.6,1.7c0,0.3-0.1,1.3-0.1,2c0,2.5,0,8.7,0.4,9.2c0.6,0.9,0.4-6.7,0.4-9.2C65.1,66.4,65.1,65.6,65,65.2z\"></path><path class=\"st9\" d=\"M65.2,78.6c1.7,0,4.7,1.2,7.4,3.1c-2.6-2.9-5.7-4.9-7.4-4.9c-1.8,0-5.6,2.2-8.3,5.4 C59.7,80,63.3,78.6,65.2,78.6z\"></path><path class=\"st8\" d=\"M64.5,96.3c-3.8,0-7.5-1.2-10.9-2.1c-0.7-0.2-1.4,0.3-2.1,0.1c-6.3-2-11.4-5.4-14.5-9.7c0,0.3,0,0.7,0,1 c0,21,7.4,15.1,12.3,17.6c5,2.5,10.2,1.7,15.5,1.7h0h0.1c5.4,0,10.5,0.7,15.5-1.8c4.9-2.5,12.3,3.6,12.3-17.4c0-0.8,0-1.6,0.1-2.3 c-2.9,4.7-8.2,8.4-14.8,10.6c-0.6,0.2-2-0.3-2.6-0.2C71.8,95,68.6,96.3,64.5,96.3z\"></path><path class=\"st8\" d=\"M55,85c0,0-2.5,7.5-0.8,10.8l-2.3-1C51.9,94.8,53.6,87.2,55,85z\"></path><path class=\"st8\" d=\"M74.8,85c0,0,2.5,7.5,0.8,10.8l2.3-1C77.9,94.8,76.1,87.2,74.8,85z\"></path><path class=\"st3\" d=\"M48.6,46.7c-0.9,3.1-4.1,13.6,2.1,10.1c0,0-2.6,1.5-4.2,7.2s-5.8,6.4-5.8,6.4s-6.7,1.3-11.7-3 c-4.2-3.6-4.9-10-3.1-14.9s5-6.3,9.7-7.3C40.3,44.1,49.6,43.2,48.6,46.7z\"></path><path d=\"M64.9,76.8c2.7,0,11.1,5.8,11.2,12.9c0-0.1,0-0.2,0-0.4c0-7.4-6.8-13.3-11.2-13.3c-4.4,0-11.2,6-11.2,13.3 c0,0.1,0,0.2,0,0.4C53.8,82.6,62.2,76.8,64.9,76.8z\"></path><g><ellipse transform=\"matrix(0.9683 -0.2497 0.2497 0.9683 -13.2339 18.6065)\" class=\"st10\" cx=\"66.7\" cy=\"61.5\" rx=\"0.8\" ry=\"1.5\"></ellipse><ellipse transform=\"matrix(0.9551 0.2963 -0.2963 0.9551 21.0115 -15.7209)\" class=\"st10\" cx=\"62.4\" cy=\"61.5\" rx=\"0.8\" ry=\"1.5\"></ellipse></g><g><circle cx=\"37.2\" cy=\"58.1\" r=\"8.8\"></circle><circle class=\"st4\" cx=\"39.5\" cy=\"54.2\" r=\"2.3\"></circle></g><g><path class=\"st9\" d=\"M67.5,58.2c0-0.1-2.3,1-2.9,1.1c-0.6-0.1-2.9-1.2-2.9-1.1c0,0,1.9,0,2.9,0C65.6,58.2,67.5,58.2,67.5,58.2z\"></path></g><path class=\"st1\" d=\"M50,57.7c0,0-7.9,5.4-12.2,10.7c-4.3,5.3-4.2,6.3-4.2,6.3l3.1,1.4c0,0,4.4-8.3,9.8-11.4s6.1-5.7,6.1-5.7 L50,57.7z\"></path><path class=\"st3\" d=\"M32.7,41.7c0,0-2.7,7.4-8.7,10.5C24,52.2,33.4,51.1,32.7,41.7z\"></path><path class=\"st3\" d=\"M95.8,41.7c0,0,2.7,7.4,8.7,10.5C104.5,52.2,95.1,51.1,95.8,41.7z\"></path><path class=\"st3\" d=\"M78.7,55.5c0,0-5.9-6.2-13.8-6.4l0,0c-0.1,0,0.2,0,0.1,0c-0.1,0,0.1,0,0.1,0v0c-8,0.2-13.8,6.4-13.8,6.4 c6.9-4.8,12.8-4.7,13.8-4.7v0c0,0,0,0,0,0c0,0,0,0,0,0v0C65,50.8,71.8,50.7,78.7,55.5z\"></path><path class=\"st3\" d=\"M71.8,42.5c0,0-3-4.2-7-4.3l0,0c0,0,0.1,0,0.1,0c0,0,0.1,0,0.1,0v0c-3,0.1-6.9,4.3-6.9,4.3 c3.4-3.3,6.9-3.2,6.9-3.2v0c0,0,0,0,0,0c0,0,0,0,0,0v0C65,39.3,68.3,39.2,71.8,42.5z\"></path><path class=\"st3\" d=\"M37.2,73.2c0,0-4.7,2.3-8.1,0.9l0,0c0,0-0.1,0-0.1,0c0,0,0,0,0,0v0c-3-1.7-4.5-6.8-4.5-6.8 S27.5,76.3,37.2,73.2z\"></path><path class=\"st3\" d=\"M92,73.2c0,0,4.7,2.3,8.1,0.9l0,0c0,0,0,0,0,0c0,0,0,0,0,0v0c4-1.7,4.6-6.8,4.6-6.8S101.7,76.3,92,73.2z\"></path><g><path class=\"st3\" d=\"M42.6,41.2c2.6-0.5,6.9-0.6,10.3,0.5c4.3,1.5,0.8,7,1.7,7.3c0.9,0.3,2.1-3.8,10.1-3.4c8.1,0.4,9,4,10.1,3.4 s-1.1-10,11-7.8c0,0-12.7-3.4-12.1,5.8c0,0-7.3-5.6-17.5-0.6C56.3,46.4,58.9,37.8,42.6,41.2z\"></path></g><path class=\"st3\" d=\"M86.9,41.2c0.2,0,0.3,0.1,0.4,0.1C87.4,41.3,87.2,41.2,86.9,41.2z\"></path><path class=\"st3\" d=\"M86.9,41.2C86.9,41.2,86.9,41.2,86.9,41.2C86.9,41.2,86.9,41.2,86.9,41.2z\"></path><path class=\"st3\" d=\"M39.1,28.9c0,0-10.8,13.6-12.4,18.8c-1.6,5.3-2.8,27-4.2,30.1l-5-21.4l9.2-22.3L39.1,28.9z\"></path><path class=\"st3\" d=\"M89.9,28.9c0,0,10.8,13.6,12.4,18.8c1.6,5.3,2.8,27,4.2,30.1l5-21.4l-9.2-22.3L89.9,28.9z\"></path><path class=\"st7\" d=\"M89.4,28.9c0,0,11.6,9.7,15,20.9c3.4,11.2,2,24.8,4.6,26.5c3.7,2.4,7.9-11.9,9.3-13.4c2.2-2.4,9.5-8.5,10-9.6 c0.5-1.1-14.8-17.8-21.5-21.1C98.7,28.4,88.7,28.1,89.4,28.9z\"></path><path class=\"st8\" d=\"M99.3,34.9c0,0,13.7,17.5,13.5,39.3l5.5-11.2C118.2,63,113.4,48.7,99.3,34.9z\"></path><path class=\"st7\" d=\"M39.1,28.9c0,0-11.6,9.7-15,20.9s-2,24.8-4.6,26.5c-3.7,2.4-7.9-11.9-9.3-13.4c-2.2-2.4-9.5-8.5-10-9.6 c-0.5-1.1,14.8-17.8,21.5-21.1C29.8,28.4,39.8,28.1,39.1,28.9z\"></path><path class=\"st8\" d=\"M29.2,34.9c0,0-13.7,17.5-13.5,39.3L10.3,63C10.3,63,15.1,48.7,29.2,34.9z\"></path><path class=\"st3\" d=\"M21.8,74.6c0,0,1,5.4,2.6,7.1s0.5-1.3,0.5-1.3s-1.7-0.9-1.4-7.8S21.8,74.6,21.8,74.6z\"></path><path class=\"st3\" d=\"M107.1,74.6c0,0-1,5.4-2.6,7.1s-0.5-1.3-0.5-1.3s1.7-0.9,1.4-7.8S107.1,74.6,107.1,74.6z\"></path><g><circle class=\"st8\" cx=\"54.5\" cy=\"70.5\" r=\"0.8\"></circle><circle class=\"st8\" cx=\"49.9\" cy=\"75.3\" r=\"0.8\"></circle><circle class=\"st8\" cx=\"48.4\" cy=\"70.5\" r=\"0.8\"></circle></g><g><circle class=\"st8\" cx=\"74\" cy=\"70.5\" r=\"0.8\"></circle><circle class=\"st8\" cx=\"78.6\" cy=\"75.3\" r=\"0.8\"></circle><circle class=\"st8\" cx=\"80.1\" cy=\"70.5\" r=\"0.8\"></circle></g></g></svg>"

/***/ }),
/* 1005 */,
/* 1006 */,
/* 1007 */,
/* 1008 */,
/* 1009 */,
/* 1010 */,
/* 1011 */,
/* 1012 */,
/* 1013 */,
/* 1014 */,
/* 1015 */,
/* 1016 */,
/* 1017 */,
/* 1018 */,
/* 1019 */,
/* 1020 */,
/* 1021 */,
/* 1022 */,
/* 1023 */,
/* 1024 */,
/* 1025 */,
/* 1026 */,
/* 1027 */,
/* 1028 */,
/* 1029 */,
/* 1030 */,
/* 1031 */,
/* 1032 */,
/* 1033 */,
/* 1034 */,
/* 1035 */,
/* 1036 */,
/* 1037 */,
/* 1038 */,
/* 1039 */,
/* 1040 */,
/* 1041 */,
/* 1042 */,
/* 1043 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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:svg=\"http://www.w3.org/2000/svg\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"1.1\" id=\"svg2\" viewBox=\"0 0 34 34\"><defs id=\"defs4\"><linearGradient id=\"linearGradient4275\"><stop id=\"stop4277\" offset=\"0\" style=\"stop-color:#006e00;stop-opacity:1\"></stop><stop style=\"stop-color:#00cc00;stop-opacity:1;\" offset=\"0.55349338\" id=\"stop4283\"></stop><stop id=\"stop4279\" offset=\"1\" style=\"stop-color:#eeff2a;stop-opacity:1\"></stop></linearGradient><linearGradient gradientTransform=\"matrix(0.03267513,0,0,0.03267513,5.555801,1018.6805)\" gradientUnits=\"userSpaceOnUse\" y2=\"275.13159\" x2=\"162.84953\" y1=\"823.703\" x1=\"555.89331\" id=\"linearGradient4281\" xlink:href=\"#linearGradient4275\"></linearGradient></defs><g transform=\"translate(0,-1018.3622)\" id=\"layer1\"><path id=\"path4136\" d=\"m 13.661978,1019.0545 c -9.6447445,1.1926 -10.316754,13.2244 -4.2596149,18.1959 6.0571409,4.9714 13.9697969,9.3171 10.7466029,14.4295 9.372821,-1.0092 10.165143,-10.5469 5.793842,-15.3419 -4.371301,-4.7949 -17.4582341,-10.442 -12.28083,-17.2835 z\" style=\"opacity:1;fill:url(#linearGradient4281);fill-opacity:1;stroke:#000080;stroke-width:0.29986507;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1\"></path></g></svg>"

/***/ }),
/* 1044 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 viewBox=\"0 0 7 15\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"><desc>Created with Sketch.</desc><defs></defs><g id=\"Symbols\" stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\" stroke-linecap=\"square\"><g id=\"sources\" transform=\"translate(-1.000000, 0.000000)\" stroke=\"#DDE1E5\"><g id=\"Group\"><path d=\"M1.5,0.5 L1.5,14.5\" id=\"Line\"></path><path d=\"M4.5,0.5 L4.5,14.5\" id=\"Line\"></path><path d=\"M7.5,0.5 L7.5,14.5\" id=\"Line\"></path></g></g></g></svg>"

/***/ }),
/* 1045 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 viewBox=\"0 0 14 5\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"><desc>Created with Sketch.</desc><defs></defs><g id=\"Symbols\" stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\" stroke-linecap=\"square\"><g id=\"outline\" transform=\"translate(0.000000, -2.000000)\" stroke=\"#DDE1E5\"><path d=\"M1.25,2.25 L1.25,2.75\" id=\"Line\" transform=\"translate(1.250000, 2.500000) rotate(90.000000) translate(-1.250000, -2.500000) \"></path><path d=\"M1.25,4.25 L1.25,4.75\" id=\"Line\" transform=\"translate(1.250000, 4.500000) rotate(90.000000) translate(-1.250000, -4.500000) \"></path><path d=\"M8.5,-3.5 L8.5,6.5\" id=\"Line\" transform=\"translate(8.000000, 2.000000) rotate(90.000000) translate(-8.000000, -2.000000) \"></path><path d=\"M8.5,-0.5 L8.5,9.5\" id=\"Line\" transform=\"translate(8.500000, 4.500000) rotate(90.000000) translate(-8.500000, -4.500000) \"></path><path d=\"M1.25,6.25 L1.25,6.75\" id=\"Line\" transform=\"translate(1.250000, 6.500000) rotate(90.000000) translate(-1.250000, -6.500000) \"></path><path d=\"M8.5,1.5 L8.5,11.5\" id=\"Line\" transform=\"translate(8.500000, 6.500000) rotate(90.000000) translate(-8.500000, -6.500000) \"></path></g></g></svg>"

/***/ }),
/* 1046 */,
/* 1047 */,
/* 1048 */,
/* 1049 */,
/* 1050 */,
/* 1051 */,
/* 1052 */,
/* 1053 */,
/* 1054 */,
/* 1055 */,
/* 1056 */,
/* 1057 */,
/* 1058 */,
/* 1059 */,
/* 1060 */,
/* 1061 */,
/* 1062 */,
/* 1063 */,
/* 1064 */,
/* 1065 */,
/* 1066 */,
/* 1067 */,
/* 1068 */,
/* 1069 */,
/* 1070 */,
/* 1071 */,
/* 1072 */,
/* 1073 */,
/* 1074 */,
/* 1075 */,
/* 1076 */,
/* 1077 */,
/* 1078 */,
/* 1079 */,
/* 1080 */,
/* 1081 */,
/* 1082 */,
/* 1083 */,
/* 1084 */,
/* 1085 */,
/* 1086 */,
/* 1087 */,
/* 1088 */,
/* 1089 */,
/* 1090 */,
/* 1091 */,
/* 1092 */,
/* 1093 */,
/* 1094 */,
/* 1095 */,
/* 1096 */,
/* 1097 */,
/* 1098 */,
/* 1099 */,
/* 1100 */,
/* 1101 */,
/* 1102 */,
/* 1103 */,
/* 1104 */,
/* 1105 */,
/* 1106 */,
/* 1107 */,
/* 1108 */,
/* 1109 */,
/* 1110 */,
/* 1111 */,
/* 1112 */,
/* 1113 */,
/* 1114 */,
/* 1115 */,
/* 1116 */,
/* 1117 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" viewBox=\"0 0 32 32\"><rect fill=\"#002f42\" width=\"16\" x=\"0\" y=\"28\" height=\"4\"></rect><rect fill=\"#0072b1\" width=\"16\" x=\"16\" y=\"28\" height=\"4\"></rect></svg>"

/***/ }),
/* 1118 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 2500 2500\"><path d=\"M70 188.5h220v1460h870v180H70v-1640zm1776 396c-225-3-450 140-520 357-100 250-54 564 142 755 190 158 476 170 690 58 147-78 240-233 260-396 26-170 13-356-70-510-98-164-290-270-480-263l-22-3zm20 174c165 0 313 126 340 288 50 183 20 407-130 536-140 114-365 98-480-43-140-150-140-380-68-560 50-130 183-223 323-220h18z\" fill=\"#000000\"></path><path d=\"M70 2061.5h2360v250H70v-250z\" fill=\"#3492ff\"></path></svg>"

/***/ }),
/* 1119 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 viewBox=\"0.1 31.4 256 244.5\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" preserveAspectRatio=\"xMinYMin meet\"><path fill=\"#FFF\" d=\"M57.9 219.3l.6-1.5.1-.2c.5-1.2 1-2.5 1.5-3.7.6-1.6 1.2-3.3 1.8-4.9.4-.9.7-1.8 1-2.8.8-2.2 1.9-5.1 3.2-7.9.8-1.7 1.4-2.9 2.1-4 .8-1.3 1.6-2.5 2.5-3.7l.7-1 1.1-.7c.1-.1.3-.2.5-.3.5-.3 1.1-.5 1.5-.7.7-.3 1.6-.6 2.6-.9 1.4-.4 3-.8 4.9-1.2 2.8-.6 5.8-1.2 9.6-1.8 5.7-.9 11.8-1.7 19.1-2.5 12.9-1.4 25.8-2.4 38.2-3 6.6-.3 11.6-.4 16.3-.4h3c3.1.1 6.7.2 10 .7 1.2.2 2.3.4 3.1.7.3.1.6.2.7.3l1 .4.8.7.9.9c1.1 1.1 2.3 2.4 3.5 3.9 3.8 4.8 7.2 10.6 10.2 17.2 2.9 6.4 4.7 12.1 5.8 17.7l1.5 7.9h-18.4l-1.1-5.3c-1.2-5.6-16.3-20.4-23.8-23.3-.8-.3-4-1.2-13.9-1.2-20.4 0-49.2 3.6-53.3 4.5-4.9 2.3-12.4 11.9-16.7 21.5l-1.8 3.9\"></path><path fill=\"#BA8449\" d=\"M182.9 190.1c-.9-1.2-1.9-2.3-3-3.3l-.7-.7s-.1 0-.2-.1c-.5-.2-1.2-.3-1.9-.4-2.9-.4-6-.5-9.1-.6-6.3-.1-12.6.1-18.9.4-12.6.6-25.3 1.6-37.8 3-6.3.7-12.6 1.5-18.8 2.5-3.1.5-6.2 1-9.2 1.7-1.5.3-3 .7-4.4 1.1-.7.2-1.4.4-2 .7-.3.1-.6.2-.7.4 0 0-.1 0-.1.1-.7 1-1.4 2-2.1 3.1-.7 1.1-1.2 2.3-1.8 3.5-1.1 2.4-2.1 4.9-3 7.4s-1.9 5.1-2.8 7.7c-.5 1.3-1 2.6-1.6 3.9-.2.6-.5 1.2-.8 1.8h8.5c4.6-10.3 13.3-22.1 20.5-25 2-.8 34.2-4.8 55.5-4.8 7.9 0 13.3.6 16.3 1.7 8.7 3.4 26.1 19.5 27.9 28.2h4.9c-1-5.5-3-10.9-5.3-16.1-2.6-6-5.7-11.5-9.4-16.2\"></path><path fill=\"#FFF\" d=\"M179.1 185.9z\"></path><path fill=\"#BA8449\" d=\"M179.1 185.9z\"></path><path fill=\"#FFF\" d=\"M192.1 228.6l-1-5.4c-.9-4.5-2.4-9.3-4.8-14.6-2.6-5.6-5.4-10.5-8.6-14.4-.6-.8-1.2-1.5-1.8-2.1-2.5-.4-5.4-.4-8-.5h-2.8c-4.5 0-9.3.1-15.7.4-12.2.6-24.7 1.6-37.4 3-7.1.8-13 1.6-18.4 2.4-3.5.5-6.3 1.1-8.8 1.6-1.6.4-2.8.7-3.9 1-.2 0-.3.1-.4.1-.2.4-.5.7-.7 1.1-.5.8-.9 1.7-1.6 2.9-1.1 2.3-2 4.7-2.8 6.8-.4.9-.7 1.8-1 2.7-.6 1.7-1.2 3.4-1.9 5.1-.5 1.3-1.1 2.6-1.6 4l-.1.2c-.2.6-.5 1.2-.7 1.7l-1.8 4H56.6l-1.7-.8-.2-.1c-4-2.3-4-5.7-3.9-7.1v-.3l.2-.9.3-1.1c.2-.7.4-1.5.6-2.2.4-1.5.9-2.9 1.4-4.3.9-2.6 2-5.4 3.3-8.4 1.4-3.1 2.7-5.8 4.2-8.3.7-1.3 1.5-2.6 2.5-4.1.9-1.4 1.9-2.8 2.9-4.1.9-1.1 1.7-1.8 2.3-2.2h.1c.6-.5 1.2-.8 1.5-.9.9-.5 1.6-.8 2.1-1 1.2-.5 2.3-.8 3.2-1.1 2-.6 3.9-1 5.3-1.3 2.9-.6 6-1.2 9.9-1.8 5.8-.9 11.9-1.6 19.4-2.4 13-1.3 26-2.3 38.6-2.8 6-.2 10.7-.4 15-.4h4.6c3.2.1 6.8.2 10.7.8h.1c.9.2 2.3.4 3.9.9h.1c.4.2 1 .4 1.7.7.5.3.9.5 1.2.7l.5.4.4.3.4.4 1.1 1.1c1.3 1.3 2.5 2.7 3.8 4.4 4 5.2 7.5 11.2 10.7 18.3 1.5 3.3 2.8 6.6 3.8 9.8 1.1 3.5 1.9 6.8 2.5 10l1.2 7.7h-18.2v.1z\"></path><path fill=\"#FFF\" d=\"M64.3 228.6l4.4-9.5c3.7-7.9 12.6-22.4 22.5-26.5l.2-.1c3.3-1.2 36.7-5.1 57.1-5.1 11.4 0 15.7 1.1 17.9 2 10.3 4 28.2 20.9 30.6 30.9l2 8.2H64.3z\"></path><path fill=\"#FFF\" d=\"M185.4 228.6l-1.2-5.1c-1.1-4.7-15.2-18.8-22.5-21.7-.7-.3-3.6-1.1-13.2-1.1-19.6 0-48 3.5-52.6 4.4-3.8 1.9-10.7 10-15.2 19.6l-1.8 3.8H62.3l4.2-9.3c4.9-11 14.6-24.6 24.1-28.4 3.7-1.5 37.5-5.3 58-5.3 8.9 0 14.9.7 18.6 2.1 10.3 4 29.6 21.4 32 33l1.6 8h-15.4z\"></path><path fill=\"#FFF\" d=\"M193.1 228.6l-1.9-1.8c-7.2-6.7-9.2-20.6-7-26.3 1.5-3.8 4.7-5.1 7.1-5.1.7 0 1.3.1 1.9.3 1.3-.1 4.3-2.6 5.2-4 1.4-2.6 4.1-4.2 7.4-4.2 4.9 0 12.9 3.9 15.5 7.6 1.5 2.1 1.9 4.6 1.1 6.8-.1.2-.2.6-.4 1.1-2.7 8.1-6.2 18-9 22.6l-1.9 3.1-18-.1z\"></path><path fill=\"#FFF\" d=\"M192.5 228.6l6.2-10.1c3-4.9 5.1-8.9 6.5-12-3.5 2.5-8.4 4-15.7 4.4l-.2-.2-.1.7c-4.3 0-7.8-3.5-8-7.8-.1-2.1.7-4.2 2.1-5.8 1.4-1.6 3.4-2.5 5.5-2.6 7.2-.3 8.1-2 9.4-4.5 1.8-3.4 5.7-5.4 10.8-5.4 1.3 0 2.6.1 3.8.4 6.3 1.4 10.4 6.2 10.7 12.4.3 6.3-3.2 15.5-10.2 27.3l-1.9 3.2h-18.9z\"></path><path fill=\"#FFF\" d=\"M218.8 208.9c-8.1 0-16.9-5.3-21.4-10.5-4.1-4.7-4.5-8.9-4.1-11.6.5-3.3 3.2-6.6 8.1-6.6.9 0 1.8.1 2.6.2.8-3.6 2.7-11.1 10.4-11.1h1.1c3.7.4 6.5 2.7 7.6 6.2 1.2-.6 2.6-1.1 4.2-1.1 2.4 0 4.7 1 6.4 2.8 2.7 2.9 3.3 6.9 1.6 10.7 1.8 1.1 3.2 2.8 3.8 5 .8 2.8.3 5.8-1.5 8.2-3.5 4.9-11.6 7.2-18 7.4-.4.4-.6.4-.8.4M210.8 44.2c-7.3 4.6-15.3 15.1-25.1 29.5-1.5 2.3-4 3.5-6.8 3.5-3.6 0-8.2-2.2-13.5-6.5-8.4-6.8-8.6-10.6-8.7-12.1-.2-4 2.8-7.4 7-7.8 8.1-.7 14.8-5.6 21.3-10.3 6.5-4.7 12.5-9 19.7-9 1.4 0 2.9.2 4.3.5\"></path><path fill=\"#FFF8EE\" d=\"M164.3 57.5c19.6-1.8 31.1-22.1 43-19-8 5-15.9 15-27 31.4-2.7 3.9-21.4-11.9-16-12.4\"></path><path fill=\"#FFF\" d=\"M37.3 48.2c4.9-4.5 11.5-6.8 19.6-6.8 7 0 15.3 1.7 24.6 5.1 2.5.9 5.8 1.1 8.3 1.1h3.7c2.3 0 7.2 0 9.3 4.5.7 1.7 1.6 5.2-1.1 9.3-3.3 5.1-12 10.5-20.4 10.5h-.4c-4.5-.1-8.7-2.5-13.6-5.2-6.1-3.4-13-7.3-21.2-7.3-1.1 0-2.3.1-3.4.2M188.1 96.7c-3.6 0-10.4-1.5-14.7-11.7-2.4-5.4-3.2-11.5-3.2-14.9 0-7 3.7-8.9 6-9.4 1.6-1.6 4.6-5.7 6.8-8.8 9.1-12.4 15.7-20.5 23.4-20.5 1.3 0 2.5.2 3.7.7 10.3 4.1 15.3 12.8 13.6 23.8-1.2 8.1-6.3 17.7-13.8 26.5-3.5 4.1-12.6 13.7-20.7 14.3h-1.1\"></path><path fill=\"#FFF\" d=\"M69.9 100.5c-.9 0-1.8 0-2.6-.2-12.8-2.1-28.8-15-34.8-28.3-4.1-9.1-2.3-15.3 0-18.9 3.7-5.9 9.2-9 16-9 8.2 0 16.4 4.5 23.6 8.4 2.8 1.5 6.8 3.7 8.5 4.1.6-.2 1.2-.3 1.9-.3 2.6 0 5 1.4 6.4 3.8 3.3 5.8 1.3 21.4-3.9 30.4-3.6 6.6-9 10-15.1 10z\"></path><path fill=\"#FFF\" d=\"M114.5 212.1c-30 0-59.7-14.7-69.6-25.4-10.3-11.1-14-23.7-11.2-37.5 2.4-11.3 8.7-21.4 14.2-30.4 3.8-6.2 7.4-12 8.4-16.2 12.2-53.9 42.8-60.8 68.1-60.8 1.6 0 3.3 0 5.2.1 53.9 1.6 59.3 27.6 62.5 43.2.6 2.9 1.2 5.6 1.9 7.4 18.1 44.4 21.1 71 10 89-10.9 17.7-34.8 26.4-82.4 30.3-2.3.2-4.7.3-7.1.3z\"></path><path fill=\"#FFF\" d=\"M113.4 212.2c-30.6 0-58.9-15.2-68.4-25.5-10.3-11.1-14-23.7-11.2-37.5 2.4-11.3 8.7-21.4 14.2-30.4 3.8-6.2 7.4-12 8.4-16.2 12.2-53.9 42.8-60.8 68.1-60.8 1.6 0 3.3 0 5.2.1 53.9 1.6 59.3 27.6 62.5 43.2.6 2.9 1.2 5.6 1.9 7.4 17.8 43.7 20.7 70.1 9.8 88.1-12.5 20.7-41.9 27.4-82.1 31.2-2.8.3-5.6.4-8.4.4\"></path><path fill=\"#FFF\" d=\"M191.3 174.6c.4-.9.8-1.9 1.2-2.8 4.1-9.8 7.2-18.4 2.3-23.9-5.8-6.4-30.3-18-52.1-28.2-41.7-19.7-67.5-32.4-71.2-45-.9-3.2-.5-6.5 1.2-9.3C79.9 54.1 100 42 127.5 42h.8c25.9.2 44.7 7.7 56 22.1 9.6 12.2 11.7 26.5 13.3 36.9.5 3.5 1.2 8 1.9 9.2 19.6 33.7 10.6 58.6 3.5 70.3l-11.7-5.9z\"></path><path fill=\"#FFF\" d=\"M170.8 135.8c-10.1 0-27-2.5-32.8-8.1-4.6-4.4-6.3-11.7-8.1-18.8-.7-3-1.9-7.8-3-10h-.1c-.4 1.1-.9 2.5-1.3 3.6-2.7 7.6-6.5 18.1-14.4 23.8-3.4 2.5-15.7 4.7-25.2 4.7s-16.7-2-21.5-6.1c-6.9-5.8-12.1-16.5-15.6-31.8-.2-.1-.3-.2-.5-.3-1.6-1.1-3.7-2.4-4.8-4.7-1.8-3.6-1.1-10 .8-13.4 1.4-2.5 3.8-4.1 6.4-4.3h.9c1.4 0 2.8.2 4.4.5s3.5.6 5.1.6h.9c9.5-1.4 18.4-2 26.5-2 19.7 0 30.1 4 34 5.6.3.1.6.2.8.3 1.1.3 3.2.7 5.4.7.8 0 1.6 0 2.3-.2 8.9-1.4 17.4-2 25.2-2 20.8 0 31.8 4.7 36.6 6.8.4.2.8.4 1.1.4h.4c1.3 0 3.2-.4 4.8-.7 1.2-.3 2.4-.5 3.5-.7h.9c3.1 0 5.9 1.8 7.6 4.8 1.9 3.5 3.2 10.8.5 15-1.8 2.8-4.7 4.1-7.1 5.2-.3.1-.7.3-1 .5-.8 1.4-2.2 4.2-3.4 6.5-3.5 6.8-7.4 14.6-11 18.8-3.2 3.6-9.2 5.3-18.3 5.3\"></path><defs><path id=\"a\" d=\"M42.5 75H212v59.2H42.5z\"></path></defs><clipPath id=\"b\"><use xlink:href=\"#a\" overflow=\"visible\"></use></clipPath><g opacity=\".3\" clip-path=\"url(#b)\"><path fill=\"#FFF\" d=\"M166.778 134.181c-8 0-19.3-1.2-27.4-6.9-4.6-3.3-6.7-9.9-9.6-20.5-.8-3-2-7.2-2.9-9.3-.5 1.2-1.1 2.8-1.6 4.1-3 8.1-7.1 19.1-14.6 23.7-3.9 2.4-16 4.6-25.4 4.6-10.1 0-17.4-2.3-21.6-6.7-10.7-11.2-12.8-21.9-14.1-28.2-.2-.9-.4-2.1-.6-2.8-6.3-3.3-6.7-8.2-6.6-10.3.3-3.9 3.3-6.8 7-6.8 2.5 0 4.1 1.2 5.3 2 .1.1.2.2.4.2.1 0 .4.1.5.2 1.6.5 6.3 1.8 7.3 7.9.2 1.1 1.8 7.5 4.4 14.2 4.1 10.7 7 12.5 7.3 12.6 3.4 1.2 7.4 1.8 11.9 1.8 7.4 0 13.6-1.6 16.1-2.4 1.5-2.3 4.5-8.3 6.6-12.4 6.3-12.5 7.4-14.4 10.1-15.8 1.5-.8 3.3-1.2 5.6-1.2 3 0 7.3.7 9.3 1.6 4.6 2 7 8 12.4 23.5 1 2.8 2.2 6.4 3 8.2 4 1.9 13.1 3.2 22.6 3.2 2.7 0 4.7-.1 6-.2.9-1.9 2.2-4.6 3.3-6.9 7.6-16.2 8.8-18.1 10.7-19.6 3.3-2.5 9.1-4.2 12.4-4.2 4.4 0 6.8 2.9 7.4 5.7 1.1 5.8-4.6 8-6.5 8.7-.5.2-1.2.5-1.7.7-.7 1.2-2 3.8-3 5.9-8.2 16-10.9 20.4-14 22.3-3.8 2.1-13 3.1-20 3.1\"></path></g><defs><path id=\"c\" d=\"M45 70.7h33.4v16.2H45z\"></path></defs><clipPath id=\"d\"><use xlink:href=\"#c\" overflow=\"visible\"></use></clipPath><g opacity=\".3\" clip-path=\"url(#d)\"><path fill=\"#FFF\" d=\"M61.388 86.816c-1.2 0-3.5-.2-8.1-1.7-1.2-.4-2.3-.8-3-1.1-1.5-.6-5.4-2.1-5.4-6.7v-6.7h6.6c.9 0 1.5.2 2 .3 1.4.2 5.9.5 7.4.6l.9.1c1.2 0 4.9-.5 6.5-.7 2-.3 2.6-.4 3.4-.4h5.7l.9 5.6c.4 2.8-1.1 6.3-4.8 7.4-.2.1-.9.3-1.5.5-3.5 1.3-7 2.5-10 2.6-.2.2-.4.2-.6.2\"></path></g><defs><path id=\"e\" d=\"M181 79.3h25.4v17.2H181z\"></path></defs><clipPath id=\"f\"><use xlink:href=\"#e\" overflow=\"visible\"></use></clipPath><g opacity=\".3\" clip-path=\"url(#f)\"><path fill=\"#FFF\" d=\"M193.488 96.41c-2.2 0-4.4-.9-7.4-3-2.9-2-5.1-4-5.1-7.5v-6.7h6.5c.9 0 1.8.2 2.7.6.7.3 3.4 1.2 4.6 1.5.4 0 1.2-.2 2.8-.5.8-.2 1.3-.2 2-.2h5.7l.9 5.6c.4 2.7-.8 6.1-4.5 7.4l-1.2.6c-1.8.9-3.9 1.9-6 2.3-.2-.1-.6-.1-1-.1\"></path></g><path fill=\"#FFF\" d=\"M171.7 243.3h-24.6V222h24.6v21.3z\"></path><path fill=\"#BA8449\" d=\"M182.9 190.1c-.9-1.2-1.9-2.3-3-3.3l-.7-.7s-.1 0-.2-.1c-.5-.2-1.2-.3-1.9-.4-2.9-.4-6-.5-9.1-.6-6.3-.1-12.6.1-18.9.4-12.6.6-25.3 1.6-37.8 3-6.3.7-12.6 1.5-18.8 2.5-3.1.5-6.2 1-9.2 1.7-1.5.3-3 .7-4.4 1.1-.7.2-1.4.4-2 .7-.3.1-.6.2-.7.4 0 0-.1 0-.1.1-.7 1-1.4 2-2.1 3.1-.7 1.1-1.2 2.3-1.8 3.5-1.1 2.4-2.1 4.9-3 7.4s-1.9 5.1-2.8 7.7c-.5 1.3-1 2.6-1.6 3.9-.2.6-.5 1.2-.8 1.8h8.5c4.6-10.3 13.3-22.1 20.5-25 2-.8 34.2-4.8 55.5-4.8 7.9 0 13.3.6 16.3 1.7 8.7 3.4 26.1 19.5 27.9 28.2h4.9c-1-5.5-3-10.9-5.3-16.1-2.6-6-5.7-11.5-9.4-16.2M179.1 185.9z\"></path><path fill=\"#895B2E\" d=\"M179.1 185.9zm17.8 17.9c-2.7-6-5.8-11.7-9.9-16.9-1-1.3-2.1-2.6-3.3-3.8l-.9-.9-.2-.2-.1-.1-.2-.2c-.2-.1-.4-.2-.5-.3-.3-.2-.6-.3-.9-.4-1.1-.4-1.9-.5-2.8-.7-3.4-.5-6.6-.6-9.8-.7-6.5-.1-12.8 0-19.2.3-12.8.5-25.5 1.5-38.2 2.8-6.3.7-12.7 1.4-19.1 2.4-3.2.5-6.3 1-9.5 1.7-1.6.4-3.2.7-4.8 1.2-.8.2-1.6.5-2.5.9-.4.2-.9.4-1.4.7-.2.2-.5.3-.8.5-.3.2-.6.5-1 1-.9 1.1-1.8 2.4-2.6 3.6-.8 1.2-1.6 2.5-2.3 3.8-1.4 2.5-2.7 5.1-3.9 7.7-1.1 2.6-2.2 5.2-3.1 7.9-.5 1.3-.9 2.6-1.2 4-.2.7-.4 1.3-.6 2l-.3 1-.1.5c0 .1.2.2.6.5H64.2c.3-.6.5-1.2.8-1.8.5-1.3 1.1-2.6 1.6-3.8 1-2.6 1.9-5.1 2.8-7.7.9-2.5 1.9-5.1 3-7.4.6-1.2 1.2-2.3 1.8-3.5.7-1.1 1.3-2.1 2.1-3.1 0 0 .1 0 .1-.1.2-.1.5-.2.7-.4.6-.2 1.3-.5 2-.7 1.4-.4 2.9-.8 4.4-1.1 3-.7 6.1-1.2 9.2-1.7 6.2-1 12.5-1.8 18.8-2.5 12.6-1.4 25.2-2.4 37.8-3 6.3-.3 12.6-.5 18.9-.4 3.1.1 6.3.2 9.1.6.7.1 1.4.3 1.9.4.1 0 .2 0 .2.1l.7.7c1.1 1.1 2 2.2 3 3.3 3.7 4.7 6.8 10.2 9.4 15.8 2.3 5.2 4.3 10.6 5.3 16.1h5c-.5-3.1-1.3-6.1-2.2-9-1.2-3.1-2.4-6.1-3.7-9.1\"></path><path fill=\"#FFF8EE\" d=\"M164.1 195.7c-2.6-1-7.9-1.6-15.5-1.6-21.4 0-53 4-54.7 4.7-6.5 2.6-14.6 13.6-19.1 23.2h115.9c-1.9-7.5-18-22.9-26.6-26.3\"></path><path fill=\"#E5D0AE\" d=\"M164.8 193.8c-2.9-1.1-8.4-1.7-16.3-1.7-21.3 0-53.5 4-55.5 4.8-7.3 3-15.9 14.7-20.5 25h2.2c4.5-9.5 12.6-20.5 19.1-23.2 1.7-.7 33.3-4.7 54.7-4.7 7.6 0 12.9.5 15.5 1.6 8.6 3.3 24.7 18.8 26.6 26.3h2c-1.7-8.6-19.1-24.7-27.8-28.1\"></path><path fill=\"#BA8449\" d=\"M204.4 194.7c-1.2 2.5-7.5 8.9-12.8 7.4-2.8-.8-2.3 14 4.1 19.9h11.6c3.2-5.1 8.2-20.7 8.8-22.3.7-1.9-10.5-7.5-11.7-5\"></path><path fill=\"#895B2E\" d=\"M216.7 198.4c-.2-3.2-2.1-5.5-5.5-6.3-2.7-.6-6.3-.1-7.2 1.6-1.8 3.4-4 7.5-14.9 8.1-.7 0-1.3.7-1.3 1.4s.7 1.3 1.4 1.3c11.4-.5 14.7-4.9 17.1-9.4.5-.4 3.2-.9 5.4 0 1.6.7 2.3 1.8 2.4 3.5.3 6.2-6.2 17.7-9.7 23.5h3.1c3.9-6.8 9.5-17.3 9.2-23.7\"></path><defs><path id=\"g\" d=\"M193.5 196.5H217v27.3h-23.5z\"></path></defs><clipPath id=\"h\"><use xlink:href=\"#g\" overflow=\"visible\"></use></clipPath><g opacity=\".15\" clip-path=\"url(#h)\"><path fill=\"#E3AB5E\" d=\"M194.235 222.3c7.1-1.7 13.4-8.8 13.9-18.9.1-1.9-3.8-5.5-3.8-6.5s11.9.2 12.5 3c.5 2.8-7.4 19.7-10.1 22.5-2.6 2.7-16.7.9-12.5-.1\"></path></g><path fill=\"#FCD4C3\" d=\"M199.8 187.8c-.9 6.2 11.2 14.7 19.4 14.5 6.8-.2 14.5-3.4 13.4-7.2-1.1-3.9-7.7.7-8.3-2-.5-2.6 7.7-7.5 4.4-11.1-3.1-3.3-6.7 5.2-10.3 4-3.6-1.2.7-9.5-3.8-10-5.3-.5-3.3 11.3-7.3 11.5-2.8.3-7.2-1.7-7.5.3\"></path><path fill=\"none\" stroke=\"#F8A38F\" stroke-width=\"2\" stroke-miterlimit=\"10\" d=\"M199.8 187.8c-.9 6.2 11.2 14.7 19.4 14.5 6.8-.2 14.5-3.4 13.4-7.2-1.1-3.9-7.7.7-8.3-2-.5-2.6 7.7-7.5 4.4-11.1-3.1-3.3-6.7 5.2-10.3 4-3.6-1.2.7-9.5-3.8-10-5.3-.5-3.3 11.3-7.3 11.5-2.8.3-7.2-1.7-7.5.3z\"></path><defs><path id=\"i\" d=\"M203.2 182.8h7.8v6.4h-7.8z\"></path></defs><clipPath id=\"j\"><use xlink:href=\"#i\" overflow=\"visible\"></use></clipPath><g opacity=\".5\" clip-path=\"url(#j)\"><path fill=\"#FCD4C3\" d=\"M203.238 187.742c1.3 1 6 2.3 6.7.6.8-1.7 1.4-5.1.9-5.6-.4 1.6-.9 4.6-2.6 5.3-1.3.5-4.1-.1-5-.3\"></path></g><defs><path id=\"k\" d=\"M215.9 181.2h7.7v7.2h-7.7z\"></path></defs><clipPath id=\"l\"><use xlink:href=\"#k\" overflow=\"visible\"></use></clipPath><g opacity=\".5\" clip-path=\"url(#l)\"><path fill=\"#FCD4C3\" d=\"M216.159 181.142c-.2 1.1-1.1 6.3 1.6 7 2.3.6 4-.8 5.8-3.7-1 .9-3.5 3.1-5.4 2.3-1.9-.9-2.1-2.9-2-5.6\"></path></g><defs><path id=\"m\" d=\"M204.5 193h15.8v8.6h-15.8z\"></path></defs><clipPath id=\"n\"><use xlink:href=\"#m\" overflow=\"visible\"></use></clipPath><g opacity=\".25\" clip-path=\"url(#n)\"><path fill=\"#FCD4C3\" d=\"M204.576 195.158c13.6-6.7 16.4 4.2 15.7 6.4-5.4.9-14.9-4.7-15.7-6.4\"></path></g><defs><path id=\"o\" d=\"M223 190.9h5.7v5H223z\"></path></defs><clipPath id=\"p\"><use xlink:href=\"#o\" overflow=\"visible\"></use></clipPath><g opacity=\".5\" clip-path=\"url(#p)\"><path fill=\"#FCD4C3\" d=\"M224.351 190.896c-2.3 2.6-1.2 4.7.3 5 1.9.4 4.4-1.6 4-1.6-.5 0-4.2 1.2-4.9-.6-.5-1.4.3-2.2.6-2.8\"></path></g><defs><path id=\"q\" d=\"M66.1 182.6H195v36.5H66.1z\"></path></defs><clipPath id=\"r\"><use xlink:href=\"#q\" overflow=\"visible\"></use></clipPath><g opacity=\".15\" clip-path=\"url(#r)\"><path fill=\"#E3AB5E\" d=\"M69.034 208.266c43.7 25.1 130.4-.4 125.8-8.6-6.1-10.9-8.5-17.7-21.9-17.1-13.3.7-98.9 7.5-102.4 10.1-3.4 2.7-7.2 12.3-1.5 15.6\"></path></g><path fill=\"#FFF8EE\" d=\"M164.3 57.5c19.6-1.8 31.1-22.1 43-19-8 5-15.9 15-27 31.4-2.7 3.9-21.4-11.9-16-12.4\"></path><path fill=\"none\" stroke=\"#E5D0AE\" stroke-width=\"3\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-miterlimit=\"10\" d=\"M164.3 57.5c19.6-1.8 31.1-22.1 43-19-8 5-15.9 15-27 31.4-2.7 3.9-21.4-11.9-16-12.4z\"></path><path fill=\"#BA8449\" d=\"M41.8 53.1c8.8-8.2 24.7-5 37.5-.4 7.3 2.6 16.8.5 17.4 2 1.6 3.5-7.9 10.7-15.7 10.5-7.7-.1-20.6-14.6-39.2-12.1\"></path><path fill=\"none\" stroke=\"#895B2E\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-miterlimit=\"10\" d=\"M41.8 53.1c8.8-8.2 24.7-5 37.5-.4 7.3 2.6 16.8.5 17.4 2 1.6 3.5-7.9 10.7-15.7 10.5-7.7-.1-20.6-14.6-39.2-12.1z\"></path><path fill=\"#FCD4C3\" d=\"M177.7 67.2c4.5 1.3 21.6-32.2 29.9-28.9 26.4 10.5-8 50.9-18.9 51.8-11 .8-13.5-23.7-11-22.9\"></path><path fill=\"none\" stroke=\"#F8A38F\" stroke-width=\"2\" stroke-miterlimit=\"10\" d=\"M177.7 67.2c4.5 1.3 21.6-32.2 29.9-28.9 26.4 10.5-8 50.9-18.9 51.8-11 .8-13.5-23.7-11-22.9z\"></path><path fill=\"#FCD4C3\" d=\"M82.3 63.2c-7.3 3.2-33.2-23.9-44.1-6.5C31 68.2 52.8 91.4 68.4 93.9s17.7-32.4 13.9-30.7\"></path><path fill=\"none\" stroke=\"#F8A38F\" stroke-width=\"2\" stroke-miterlimit=\"10\" d=\"M82.3 63.2c-7.3 3.2-33.2-23.9-44.1-6.5C31 68.2 52.8 91.4 68.4 93.9s17.7-32.4 13.9-30.7z\"></path><defs><path id=\"s\" d=\"M180.5 44.8h18v44.6h-18z\"></path></defs><clipPath id=\"t\"><use xlink:href=\"#s\" overflow=\"visible\"></use></clipPath><g clip-path=\"url(#t)\"><path fill=\"#FCD4C3\" d=\"M198.067 44.8c-1.4 4.6-13.2 16.5-11.6 26.7.6 3.8 4.6 15 6.3 14.6s6-3.1 5.6-2.5c-.4.6-6 4.7-8.1 5.8-2.1-1.1-11-21.8-9.7-22.9 3.9-3.5 15.8-21.5 17.5-21.7\"></path></g><defs><path id=\"u\" d=\"M38.6 51.5h44.2v31.6H38.6z\"></path></defs><clipPath id=\"v\"><use xlink:href=\"#u\" overflow=\"visible\"></use></clipPath><g clip-path=\"url(#v)\"><path fill=\"#FCD4C3\" d=\"M38.533 57.4c8-4.9 22.6-2.8 31.1 5.5 3 3 3.9 13-2.9 19.8-2.7 2.7 16-9.6 16.1-13.2 0-3.6-1.2-4.6-4-5.6-10.3-3.8-21.1-12.6-30.5-12.4-6.1.1-8.9 4.3-9.8 5.9\"></path></g><defs><path id=\"w\" d=\"M181.1 42.2h26.2v44.2h-26.2z\"></path></defs><clipPath id=\"x\"><use xlink:href=\"#w\" overflow=\"visible\"></use></clipPath><g opacity=\".25\" clip-path=\"url(#x)\"><path fill=\"#FCD4C3\" d=\"M201.3 42.175c18.2 10-11.1 25.2-8.9 44.1-3.8-.4-13.2-18.7-10.9-20.3 3.2-2.2 16.7-24.6 19.8-23.8\"></path></g><path fill=\"#E5D0AE\" d=\"M60.9 103.6c.3-1.3.5-2.7.5-4 .1-1.4.2-2.8.5-4.1.1-.7.3-1.4.6-2 .3-.6.5-1.3.7-1.9.5-1.3 1-2.6 1.6-3.8 2.3-5.1 4.9-9.9 8.1-14.4 3.2-4.5 6.7-8.7 10.7-12.3 4-3.7 8.7-6.7 13.7-8.7 5-2.1 10.4-3.4 15.8-4 5.4-.7 10.8-.8 16.2-.7 5.4.1 10.8.4 16.2 1.1 5.4.7 10.8 1.9 16 3.8 2.6 1 5.1 2.1 7.5 3.5 2.4 1.4 4.7 3.1 6.8 5.1 2.1 1.9 3.9 4.2 5.4 6.6 1.5 2.4 2.6 5 3.6 7.7.9 2.6 1.6 5.3 2.2 8s1 5.3 1.6 7.8c.3 1.2.7 2.4 1.1 3.6.5 1.2 1 2.5 1.4 3.8.9 2.5 1.9 5.1 2.8 7.6 3.5 10.2 6.6 20.6 8.5 31.2.9 5.3 1.6 10.7 1.7 16.1.1 5.4-.4 10.8-1.9 16-.4 1.3-.8 2.6-1.3 3.8-.5 1.2-1.2 2.4-1.6 3.7-.2.7-.4 1.3-.7 1.9-.3.6-.7 1.2-1.1 1.8-.7 1.1-1.6 2.3-2.5 3.3-3.5 4.2-8 7.6-12.8 10.2s-10 4.6-15.2 6.1c-5.3 1.5-10.5 2.7-15.9 3.7s-10.7 1.7-16 2.4l-8.1.9-4 .4c-1.3.1-2.7.2-4.1.3-11 .6-22-1-32.5-3.9-5.3-1.5-10.4-3.3-15.4-5.5s-9.9-4.7-14.5-7.7c-2.3-1.5-4.6-3.2-6.7-5.1-.5-.5-1.1-1-1.6-1.5s-.9-1.1-1.4-1.6c-.9-1.1-1.8-2.2-2.6-3.3-3.1-4.6-5.6-10-6.4-15.6-.9-5.7-.1-11.5 1.5-16.8 1.6-5.4 4.1-10.3 6.7-15.1 2.7-4.7 5.6-9.3 8.4-13.8 1.4-2.3 2.7-4.6 3.9-6.9 1.1-3 2.1-5.3 2.6-7.7zm4 .9c-.7 2.8-1.9 5.4-3.2 7.9s-2.7 4.8-4.1 7.1c-2.8 4.6-5.7 9.1-8.2 13.7s-4.8 9.4-6.2 14.3c-1.4 4.9-2 10-1.2 15 .8 4.9 2.9 9.6 5.9 13.7.7 1 1.6 2 2.4 3 .4.5.9 1 1.3 1.4.4.5.9.9 1.3 1.3 1.8 1.7 3.9 3.2 6.1 4.7 4.4 2.8 9 5.3 13.9 7.4 4.8 2.1 9.8 3.9 14.9 5.3s10.2 2.5 15.4 3.2c5.2.7 10.4.9 15.6.7l3.9-.3 4-.4c2.6-.2 5.3-.5 8-.8 5.3-.5 10.6-1.2 15.8-2.1 5.3-.9 10.4-1.9 15.5-3.3s10.1-3 14.9-5.1c4.7-2.2 9.3-4.9 13-8.6.9-.9 1.8-1.9 2.6-2.9.4-.5.8-1.1 1.2-1.6.4-.5.9-1 1.3-1.6.9-1.1 1.3-2.3 1.9-3.5.5-1.2.9-2.5 1.2-3.8 1.3-5.2 1.6-10.6 1.3-15.9-.4-5.3-1.3-10.6-2.6-15.8-2.5-10.4-6.2-20.4-10.1-30.3-1-2.5-2-5-3-7.4l-1.6-3.7c-.5-1.3-1-2.7-1.4-4-.7-2.7-1.2-5.3-1.8-7.9-.5-2.6-1.2-5.1-2-7.5-1.7-4.9-4.2-9.3-7.9-12.8-3.6-3.5-8.1-6.2-12.9-8.2-4.8-2-10-3.3-15.2-4.2-5.2-.9-10.5-1.4-15.8-1.7-5.3-.2-10.7-.3-16 .3-5.3.6-10.5 1.7-15.4 3.8-4.9 2-9.4 4.9-13.2 8.5-3.9 3.6-7 8-9.3 12.6-2.4 4.7-4.1 9.7-5.6 14.7-.4 1.2-.7 2.5-1 3.8-.2.6-.3 1.3-.5 1.9-.1.3-.1.7-.2 1-.1.3-.2.6-.4.9-.5 1.2-1.1 2.4-1.7 3.6-.2 1.1-.6 2.3-.9 3.6z\"></path><path fill=\"#FFF8EE\" d=\"M62.9 104.1c-4.2 18.6-40.9 48-13 78.1 9.5 10.2 40.3 25.9 71.2 23 80.4-7.6 100.7-27 66.8-110.2-5.5-13.5.7-44.7-58.4-46.5-24.8-.7-54.6 2.7-66.6 55.6\"></path><path fill=\"#E5BFA1\" d=\"M76.8 67.7c2.6-4 6-7.3 9.8-10C90.4 55 94.7 53 99 51.4c8.8-3.2 18-4.4 27.2-4.7 9.2-.2 18.5.5 27.5 2.8 4.5 1.2 8.9 2.8 13.1 5.1s8.1 5.1 11.4 8.6c3.3 3.5 6 7.4 8.1 11.7 2.1 4.3 3.6 8.8 4.7 13.3 1.1 4.6 1.8 9.1 2.4 13.6.3 2.3.6 4.5 1.1 6.6.2 1.1.5 2.1.8 2.9l.3.6.1.1.1.1.2.4.9 1.5c4.4 8.1 7.7 17 8.9 26.3.6 4.6.7 9.3.2 14-.5 4.6-1.7 9.2-3.5 13.5-.9 2.1-1.9 4.2-3.1 6.3l-.9 1.5c-.3.5-.6.9-1 1.5-.1.1-.1.2-.2.3-.3-.2.4.7-1.6 0-.5-.9-.2-.7-.3-.9 0-.3.1-.4.1-.6l.3-.9.5-1.6c1.4-4.3 2.6-8.6 3.1-13.1.2-2.2.3-4.4 0-6.7-.3-2.2-.9-4.3-2-6.1-.3-.5-.6-.9-1-1.3s-.7-.8-1.1-1.2c-.8-.8-1.6-1.5-2.5-2.2-1.8-1.4-3.7-2.5-5.7-3.5-4.1-1.8-8.6-2.5-13-2.3-4.5.2-8.9 1.1-13.2 2.3-2.2.6-4.3 1.3-6.5 2-1.1.4-2.2.7-3.3 1-.6.2-1.1.3-1.8.4-.6.1-1.1.2-1.7.2-4.6.5-9.1.8-13.8.7-4.6-.1-9.2-.6-13.8-1.6-4.5-1-9-2.5-13.2-4.4-4.2-2-8.2-4.4-11.8-7.4-3.6-3-6.8-6.4-9.6-10.1-5.6-7.5-9.3-16.2-11.1-25.3-.9-4.6-1.3-9.2-1.1-13.9.1-2.3.4-4.7.9-7 .6-1.8 1.4-4.1 2.7-6.2zm2 1.3c-1.1 1.8-1.8 3.8-2.3 6-.5 2.1-.7 4.3-.9 6.5-.2 4.4.3 8.9 1.2 13.2 1.9 8.7 5.6 17 11 24 2.7 3.5 5.8 6.7 9.2 9.5 3.4 2.8 7.2 5.2 11.2 7.1 4 1.9 8.2 3.3 12.6 4.3 4.3 1 8.8 1.6 13.2 1.7 4.5.2 8.9 0 13.4-.5l1.7-.2c.5-.1 1-.2 1.6-.3 1.1-.3 2.1-.6 3.2-.9 2.2-.6 4.4-1.3 6.6-1.8 4.4-1.1 9-1.9 13.7-1.9 4.6 0 9.3.8 13.5 2.8 2.1 1 4.1 2.2 5.9 3.6.9.7 1.8 1.5 2.6 2.3l1.2 1.2c.4.4.8.9 1.1 1.4 1.3 2 2 4.3 2.3 6.7s.3 4.7.1 7c-.4 4.6-1.4 9.1-2.7 13.5l-.5 1.6-.2.8c0 .1-.1.3 0 .3-.1-.1.2.2-.2-.7-1.9-.7-1.2.2-1.4-.1.2-.4.5-.9.8-1.4.3-.5.5-1 .8-1.5 1-2 1.9-4 2.7-6.1 3-8.3 3.6-17.4 2.1-26.1-.7-4.4-2-8.6-3.6-12.7s-3.5-8.1-5.7-12l-.8-1.4-.2-.4-.1-.2-.1-.2c-.2-.3-.3-.6-.4-.9-.5-1.2-.8-2.5-1.1-3.6-.5-2.3-.9-4.6-1.2-6.8-.7-4.5-1.3-8.9-2.4-13.2-1-4.3-2.4-8.5-4.3-12.4-1.9-3.9-4.2-7.5-7.2-10.7-5.8-6.3-13.8-10.5-22.3-13s-17.5-3.3-26.5-3.3c-8.9 0-17.9 1.1-26.3 3.9-4.2 1.4-8.2 3.3-11.9 5.8-3.8 2.4-7 5.4-9.4 9.1z\"></path><path fill=\"#895B2E\" d=\"M75.6 67.2c1.5-2.4 3.3-4.3 5.1-6.1 1.9-1.8 3.9-3.3 6-4.7 4.2-2.8 8.7-5 13.4-6.7 9.4-3.3 19.3-4.6 29.1-4.4 9.7.3 19.5 1.5 28.9 4.9 4.7 1.7 9.2 3.9 13.3 6.9 4.1 2.9 7.7 6.5 10.7 10.6 2.9 4.1 5.1 8.6 6.8 13.3 1.6 4.7 2.7 9.5 3.5 14.2.4 2.4.7 4.7 1.1 7.1.4 2.3.7 4.7 1.1 6.8.2 1.1.5 2.1.9 2.9l.1.2.1.2.2.4.4.8.9 1.6c1.1 2.1 2.2 4.3 3.2 6.5 1.9 4.4 3.5 9.1 4.6 13.9 1.1 4.7 1.6 9.6 1.6 14.5-.1 9.8-2.8 19.5-7.9 27.8-.4.6-1.2.9-1.8.5-.6-.4-.8-1.1-.6-1.6 1.4-3.6 2.9-7 4-10.5 1.2-3.5 2-7.1 2-10.6s-.9-6.9-3-9.6c-.1-.2-.3-.4-.4-.5l-.2-.2-.2-.3-.9-.9c-.6-.6-1.3-1.2-2-1.7-1.4-1.1-3-2.1-4.5-3.1-6.3-3.9-13-7.3-19.8-10.7-6.8-3.3-13.7-6.5-20.5-9.6-13.8-6.3-27.6-12.6-41.2-19.4-6.8-3.5-13.5-7-20.1-11.2-3.5-2.5-6.7-4.7-9.8-7.5-1.5-1.4-3-3-4.2-5.2-.6-1.1-1.1-2.4-1.2-4 0-.8 0-1.6.2-2.4.1-.4.2-.8.4-1.2.1-.3.4-.9.5-1h.2zm5.6 3.4c-.2.3-.1.2-.2.3 0 .1 0 .1-.1.2v.4c0 .3.2.8.4 1.4.6 1.2 1.7 2.5 2.9 3.6 2.5 2.4 5.4 4.6 8.5 6.6 6.1 4.1 12.7 7.7 19.4 11.3 13.3 7 27 13.5 40.6 20.1 6.8 3.3 13.6 6.7 20.4 10.1 6.8 3.5 13.5 7 20 11.3 1.6 1.1 3.2 2.2 4.7 3.5.8.6 1.5 1.3 2.3 2 .4.4.7.7 1.1 1.2l.2.3.3.3c.2.2.3.4.5.6 1.2 1.7 2.1 3.6 2.7 5.6s.7 4.1.7 6.1c-.1 4-1.2 7.9-2.5 11.6-1.3 3.7-2.9 7.1-4.4 10.6l-2.4-1.2c4.7-8 6.9-17.2 6.8-26.4-.2-9.2-2.6-18.3-6.5-26.7-1-2.1-2-4.2-3.2-6.2l-1.8-3.2c-.7-1.3-1-2.6-1.3-3.8-.6-2.5-1-4.8-1.4-7.2s-.8-4.7-1.2-6.9c-.9-4.6-2-9-3.6-13.3-1.6-4.2-3.7-8.3-6.3-11.8-2.6-3.6-5.9-6.7-9.5-9.2-3.7-2.5-7.7-4.5-12-5.9-8.6-2.9-17.8-3.9-27-4-4.6 0-9.1.2-13.6 1-4.5.7-8.9 1.8-13.1 3.3s-8.3 3.4-12 5.8c-1.9 1.2-3.6 2.5-5.2 4s-3.1 3-4.2 4.6z\"></path><path fill=\"#BA8449\" d=\"M77.8 68.3c-11 17 2.1 82.3 71.2 73.5 7.3-.9 31.6-13.9 48 4.3 8.9 10-3.1 35.3 0 30 3.5-5.8 16.9-28.2-3.2-62.7-6.4-11.1 4.2-64.4-65-65.1-26.5-.1-43.7 8.7-51 20\"></path><defs><path id=\"y\" d=\"M61.7 64.6H179v111.9H61.7z\"></path></defs><clipPath id=\"z\"><use xlink:href=\"#y\" overflow=\"visible\"></use></clipPath><g opacity=\".15\" clip-path=\"url(#z)\"><path fill=\"#E3AB5E\" d=\"M75.968 72.433c19.9-12.8 80.1-11.9 99.3 18.8 12.8 20.4-10.6 70.8-19.3 77.2-8.6 6.4-20.6 12-52.2 4.4-26.8-6.4 13.5-31.3-17.9-34.9-28.7-3.3-31.6-51.5-9.9-65.5\"></path></g><defs><path id=\"A\" d=\"M118.5 119.2h13.1v5.5h-13.1z\"></path></defs><clipPath id=\"B\"><use xlink:href=\"#A\" overflow=\"visible\"></use></clipPath><g opacity=\".25\" clip-path=\"url(#B)\"><path fill=\"#E3AB5E\" d=\"M131.6 121.966c0 1.5-2.9 2.8-6.5 2.8s-6.5-1.2-6.5-2.8c0-1.5 2.9-2.8 6.5-2.8 3.6.1 6.5 1.3 6.5 2.8\"></path></g><path fill=\"#F8A38F\" d=\"M120.1 113.8c1.5-1 9.5-.7 10.5.4 1 1 3.2 5.2.7 7-.9.6-.9-1.9-2.9-1.5-2.4.5-1.1 3.6-3 3.6-2.9-.1-.1-3.4-2.6-3.8-1.9-.4-2.3 1.5-3.2 1.3-2.9-.6-1.6-5.6.5-7\"></path><defs><path id=\"C\" d=\"M117.9 113.2h14.5v9.9h-14.5z\"></path></defs><clipPath id=\"D\"><use xlink:href=\"#C\" overflow=\"visible\"></use></clipPath><g clip-path=\"url(#D)\"><path fill=\"#F8A38F\" d=\"M124.42 113.205c-1.9 0-3.7.2-4.3.6-2.1 1.4-3.4 6.4-.6 6.9h.1c.7 0 1.1-1.4 2.6-1.4h.5c2.5.4-.2 3.8 2.6 3.8 1.8 0 .5-3 3-3.5h.4c1.4 0 1.6 1.6 2.2 1.6.1 0 .2 0 .3-.1 2.6-1.8.4-6-.7-7-.5-.5-3.4-.9-6.1-.9m0 .7c3 0 5.3.4 5.7.7.6.6 1.8 2.8 1.6 4.4-.1.6-.3 1.1-.7 1.4 0-.1-.1-.1-.2-.2-.4-.5-1-1.3-2.1-1.3-.2 0-.4 0-.5.1-1.7.4-2 1.7-2.3 2.6-.2.9-.3.9-.6.9-.8 0-.8-.2-.8-1.1 0-.9 0-2.3-1.8-2.6h-.6c-1.1 0-1.8.7-2.3 1.1-.1.1-.3.3-.4.3-.4-.1-.8-.3-1-1.1-.3-1.4.7-3.8 1.8-4.6.6-.4 2-.6 4.2-.6\"></path></g><defs><path id=\"E\" d=\"M74.1 124.9H187V189H74.1z\"></path></defs><clipPath id=\"F\"><use xlink:href=\"#E\" overflow=\"visible\"></use></clipPath><g opacity=\".25\" clip-path=\"url(#F)\"><path fill=\"#F7E8D0\" d=\"M186.799 140.05c3 19.2-21 43.3-53.3 48.3-32.3 4.9-56.1-23.9-59.1-43.2-3-19.2 16.8-11.9 49.1-16.8s60.3-7.5 63.3 11.7\"></path></g><defs><path id=\"G\" d=\"M80 125.9h93.6v47.5H80z\"></path></defs><clipPath id=\"H\"><use xlink:href=\"#G\" overflow=\"visible\"></use></clipPath><g opacity=\".5\" clip-path=\"url(#H)\"><path fill=\"#D8CAB2\" d=\"M102.515 169.277c-.9-1.4-1.4-3.1-1.9-4.7l-1.5-4.8c-1-3.2-2.1-6.4-3.6-9.4s-3.4-5.8-5.8-7.9c-2.4-2.1-5.3-3.5-8.4-4.1-1.1-.4-1.6-1.5-1.2-2.5.4-1 1.4-1.5 2.3-1.3 7.2 1.7 14.7 2.5 22.1 2.6s15-.3 22.5-1.2c7.5-.9 14.9-2.2 22.2-3.9 7.3-1.7 14.6-3.7 21.7-6.1 1.1-.4 2.2.2 2.5 1.2.3.8 0 1.6-.6 2.1-.2.2-.6.5-.9.8-.3.3-.6.7-.9 1-.6.7-1.2 1.5-1.8 2.3-1.2 1.6-2.3 3.2-3.3 4.9-2.2 3.3-4.2 6.7-6.1 10.2-1.9 3.5-3.8 7-5.5 10.5-.9 1.8-1.7 3.6-2.4 5.4-.4.9-.7 1.8-1.1 2.8-.2.5-.3.9-.4 1.4v.2c0 .1 0 .1-.1.2 0 .1-.1.2-.2.2-.1.1-.2.2-.4.3-.2.2-.5.3-.7.4-1.9.8-3.9 1.2-5.8 1.6-1.9.4-3.9.7-5.9.9-4 .5-7.9.9-11.9 1-4 .2-8 .2-11.9-.2-2-.2-4-.5-5.9-1-1-.3-1.9-.6-2.8-1.1-.9-.4-1.7-1-2.3-1.8m.1-.1c.5.8 1.4 1.4 2.3 1.8.9.4 1.8.7 2.8 1 1.9.5 3.9.8 5.9.9 4 .3 7.9.3 11.9.1 4-.2 7.9-.7 11.8-1.2 1.9-.3 3.9-.6 5.8-1.1s3.9-.9 5.6-1.7c.2-.1.4-.2.6-.4.2-.1.3-.2.3-.3.1-.5.2-1 .4-1.5.3-1 .6-1.9.9-2.9.7-1.9 1.4-3.7 2.2-5.6 1.6-3.7 3.2-7.3 4.9-10.9 1.7-3.6 3.5-7.2 5.4-10.7.9-1.8 1.9-3.5 3-5.3.5-.9 1.1-1.8 1.7-2.6.3-.4.6-.9.9-1.3.4-.4.7-.9 1.2-1.3l1.9 3.4c-7.3 2.5-14.6 4.5-22.1 6.2-7.5 1.7-15 3.1-22.6 4-7.6.9-15.3 1.4-23 1.2s-15.4-1.1-23-2.9l1.1-4c3.3 2 6.3 3.9 8.6 6.6 2.3 2.7 3.9 5.8 5.2 9 1.2 3.2 2.2 6.5 3 9.7.4 1.6.9 3.3 1.3 4.9.6 1.9 1.1 3.5 2 4.9\"></path></g><defs><path id=\"I\" d=\"M81 126.9h91.6v46.5H81z\"></path></defs><clipPath id=\"J\"><use xlink:href=\"#I\" overflow=\"visible\"></use></clipPath><g opacity=\".5\" clip-path=\"url(#J)\"><path fill=\"#D8CAB2\" d=\"M102.532 169.288c-.9-1.4-1.4-3-1.9-4.6-.5-1.6-.9-3.2-1.4-4.8-1-3.2-2-6.4-3.5-9.5-1.4-3-3.3-5.8-5.6-8-2.4-2.3-5.3-3.9-8.4-4.6v-.1c-.5-.2-.8-.8-.6-1.3.2-.5.7-.8 1.2-.7 7.3 1.7 14.8 2.5 22.4 2.6s15.1-.3 22.6-1.2c7.5-.9 14.9-2.3 22.3-3.9 7.4-1.7 14.7-3.7 21.8-6.1.5-.2 1.1.1 1.3.6.1.4 0 .8-.3 1.1-.6.5-1.3 1.3-1.9 2-.6.7-1.2 1.6-1.8 2.3-1.1 1.6-2.2 3.3-3.2 5-2.1 3.3-4.1 6.8-6 10.3-1.9 3.5-3.7 7-5.3 10.6l-2.4 5.4c-.4.9-.7 1.8-1 2.8-.2.5-.3.9-.4 1.4-.1.4-.4.5-.6.7-.2.2-.5.3-.7.4-1.9.8-3.9 1.2-5.8 1.6-1.9.4-3.9.7-5.9 1-3.9.5-7.9.9-11.9 1.1-4 .2-8 .2-11.9-.2-2-.2-4-.5-5.9-1-1-.3-1.9-.6-2.8-1-1-.5-1.9-1.1-2.4-1.9m0-.1c.5.8 1.4 1.4 2.3 1.9.9.4 1.8.7 2.8 1 1.9.5 3.9.8 5.9.9 4 .4 7.9.3 11.9.1 4-.2 7.9-.6 11.9-1.2 1.9-.3 3.9-.6 5.9-1.1 1.9-.4 3.9-.9 5.6-1.7.2-.1.4-.2.6-.4.2-.1.4-.3.4-.4.1-.5.2-1 .4-1.5.3-1 .6-1.9 1-2.8.7-1.9 1.5-3.7 2.3-5.5 1.6-3.7 3.3-7.2 5.1-10.8 1.8-3.6 3.6-7.1 5.6-10.6 1-1.8 2-3.5 3.1-5.1.5-.9 1.1-1.7 1.7-2.5.6-.8 1.2-1.6 2.1-2.4l1 1.8c-7.2 2.5-14.6 4.5-22 6.2s-14.9 3-22.5 4c-7.6.9-15.2 1.4-22.9 1.2-7.6-.2-15.3-1-22.8-2.7l.5-1.9c3.3 1.3 6.3 3.3 8.6 5.9 2.3 2.6 4 5.6 5.3 8.7 1.3 3.1 2.3 6.3 3.2 9.6.4 1.6.9 3.2 1.4 4.9.2 1.4.7 3 1.6 4.4\"></path></g><path fill=\"#333\" d=\"M102.6 169.2c5.1 8 46.8 2.5 47.5-.7 1.8-8.1 16.8-36.7 21.6-40.7-8.6 3-53.3 17.3-89.6 8.6 16.3 5.5 16.5 26.6 20.5 32.8\"></path><g><defs><path id=\"K\" d=\"M82 127.9h89.6v45.4H82z\"></path></defs><clipPath id=\"L\"><use xlink:href=\"#K\" overflow=\"visible\"></use></clipPath><g clip-path=\"url(#L)\"><path fill=\"#333\" d=\"M171.55 127.95c-6.7 2.3-34.9 11.4-64.1 11.4-8.6 0-17.2-.8-25.5-2.7 16.4 5.3 16.6 26.5 20.6 32.7 1.9 3 9.2 4.1 17.5 4.1 13.4 0 29.5-2.8 30-4.8 1.8-8.2 16.7-36.8 21.5-40.7m-5.6 3.9c-6.5 9.8-15.9 28.7-17.8 35.8-2.5 1.4-15.4 3.8-28.1 3.8-10.2 0-14.8-1.7-15.8-3.2-1-1.5-1.8-4.3-2.6-7.5-1.7-6.1-4.1-14.5-9.8-20.3 5 .6 10.2.9 15.7.9 23.8-.1 47.1-6.1 58.4-9.5\"></path></g></g><path fill=\"#FCD4C3\" d=\"M119.7 173.4c-8.6 0-14.2-1.2-16.4-3.6-.2-.2-.2-1.4.8-3.7 2.4-5.5 9.8-14.9 22.6-15.7.9 0 1.7-.1 2.5-.1 15 0 24 7.6 24 8.9-.2.4-.4 1-.7 1.8-.8 1.9-2 4.9-3.1 8.1-1.3 1.3-16.3 4.3-29.7 4.3\"></path><path fill=\"#F8A38F\" d=\"M129.2 149.8c-.9 0-1.7 0-2.6.1-17.5 1.1-25.6 18.4-23.8 20.5 2.6 2.8 9.3 3.8 16.9 3.8 13.5 0 29.8-3.1 30.3-4.6 1.5-4.5 3.2-8.2 3.9-10 .6-1.8-9.5-9.9-24.7-9.8m0 1.3c13.4 0 22.4 6.4 23.3 8.3-.2.4-.4.9-.6 1.5-.8 1.9-2 4.7-3.1 7.9-2.1 1.2-16.1 3.9-29.1 3.9-8.1 0-13.7-1.2-15.8-3.3-.1-.7.6-3.5 3.1-7 2.2-3.2 8.6-10.7 19.7-11.3h2.5\"></path><g><defs><path id=\"M\" d=\"M103.1 149.4h50.2v20.7h-50.2z\"></path></defs><clipPath id=\"N\"><use xlink:href=\"#M\" overflow=\"visible\"></use></clipPath><g clip-path=\"url(#N)\"><path fill=\"#FCD4C3\" d=\"M103.084 169.317c-1.2 5.4 31.9-21.1 49.7-8.4 3.7-4.9-16-13.2-27.5-11.3-9.7 1.7-19.7 7.9-22.2 19.7\"></path></g></g><path fill=\"#FFF7E3\" d=\"M109.8 140.8c1.7 3 4.8 17.6 6.1 17.9s9.5 2.6 10.6 2.1c1.2-.5 1.1-3.3 2.3-2.5 1 .7 1.5 2 2.5 1.5 1.8-.9 7.1-4.2 8-5.6.8-1.5.7-9.8 0-16.4-3.4 1.6-23.5 3.7-29.5 3\"></path><g><defs><path id=\"O\" d=\"M109.8 137.9h30.1v23.2h-30.1z\"></path></defs><clipPath id=\"P\"><use xlink:href=\"#O\" overflow=\"visible\"></use></clipPath><g clip-path=\"url(#P)\"><path fill=\"#FFDC99\" d=\"M139.35 137.85c-3 1.3-18.7 3.1-26.6 3.1-1.2 0-2.1 0-2.9-.1 1.7 3 4.8 17.6 6.1 17.9 1.2.3 8 2.2 10.1 2.2.2 0 .4 0 .5-.1 1-.4 1.1-2.6 1.9-2.6.1 0 .3 0 .4.2.9.5 1.3 1.6 2.1 1.6.1 0 .3 0 .4-.1 1.8-.9 7.1-4.2 8-5.6.9-1.5.7-9.8 0-16.5m-.5.9c.6 6.8.6 14 0 15.2-.7 1.2-5.5 4.3-7.7 5.4h-.1c-.2 0-.5-.3-.7-.6-.3-.3-.6-.6-1-.9-.3-.2-.5-.3-.8-.3-.9 0-1.2.9-1.5 1.6-.2.4-.4 1-.7 1.1h-.3c-1.7 0-6.8-1.4-8.9-1.9-.4-.1-.7-.2-.9-.2-.6-.7-1.9-5.4-2.9-8.8-.9-3.1-1.7-6-2.4-7.7h1.9c7.5 0 21.5-1.6 26-2.9\"></path></g></g><g><defs><path id=\"Q\" d=\"M109.9 137.5h29.7v10.3h-29.7z\"></path></defs><clipPath id=\"R\"><use xlink:href=\"#Q\" overflow=\"visible\"></use></clipPath><g clip-path=\"url(#R)\"><path fill=\"#EADCBE\" d=\"M112.151 147.85c-.5-5.4 24.4-7.2 27.5-6.7 0-1.3-.2-2.1-.2-3.6-4.1 2.1-26.5 4.3-29.5 3.4.3.6 1.4 3.6 2.2 6.9\"></path></g></g><g><defs><path id=\"S\" d=\"M124 143.8h5.5v14.7H124z\"></path></defs><clipPath id=\"T\"><use xlink:href=\"#S\" overflow=\"visible\"></use></clipPath><g clip-path=\"url(#T)\"><path fill=\"#FFDC99\" d=\"M124.558 143.768c.5 1.2 1 2.3 1.4 3.5l1.2 3.6 1.1 3.6 1.1 3.6-1.7.5-.8-3.7-.9-3.7-.9-3.6c-.4-1.2-.7-2.4-1.1-3.5l.6-.3z\"></path></g></g><g><defs><path id=\"U\" d=\"M39.1 51.5h39.4v35.7H39.1z\"></path></defs><clipPath id=\"V\"><use xlink:href=\"#U\" overflow=\"visible\"></use></clipPath><g opacity=\".4\" clip-path=\"url(#V)\"><path fill=\"#FCD4C3\" d=\"M40.384 59.116c13.2 3.4 11.1 26.2 18.9 28 7.9 1.8 18.2-18.8 19.2-23.3-15.2-6.5-22.3-14.1-33.2-11.9-3.6.7-8.6 6.2-4.9 7.2\"></path></g></g><g><defs><path id=\"W\" d=\"M59.9 88.7h133.7v42.5H59.9z\"></path></defs><clipPath id=\"X\"><use xlink:href=\"#W\" overflow=\"visible\"></use></clipPath><g opacity=\".5\" clip-path=\"url(#X)\"><path fill=\"#E3AB5E\" d=\"M76.967 125.833c16.6 1.3 30.3 2 35.2-3.1s6.7-27.7 14.7-27.1c6.8.5 9.1 29.9 13.5 30.7 4.4.9 35.5 9.4 43.3 1.4 7.7-8 11.3-13 9.5-17.8-1.8-4.8-47.3-20.3-56.6-21.2-9.3-.9-72.1 7-73.7 10.9-1.6 4-3.3 3.4-3 7.6.6 8.1.5 17.3 17.1 18.6\"></path></g></g><path fill=\"#333\" d=\"M123.5 92.4c1.2 0 5.9-.4 7 .5 6 4.7 5.8 24 12.1 30 5.6 5.4 36.2 9.3 41.5 3.2 5.3-6.1 13.1-24.7 14.9-26.1 1.8-1.3 5.8-2.3 7-4.2 1.2-1.8.2-10-2.6-9.6-2.8.4-8.1 2.1-11.4 1.2-3.3-1-19.4-11.1-59.9-5-3.9.6-7.8 0-10.5-.7S103.7 72.2 63 78c-4 .6-9.6-1.2-11.6-1-2 .2-2.6 6.5-1.8 8.1s5 3 5.5 5 4.2 21.6 13.8 29.7c10.2 8.6 36.2 2.6 38.5 1 10.3-7.3 12.6-28.4 16.1-28.4\"></path><g><defs><path id=\"Y\" d=\"M49.1 76.1h157.3v53.1H49.1z\"></path></defs><clipPath id=\"Z\"><use xlink:href=\"#Y\" overflow=\"visible\"></use></clipPath><g clip-path=\"url(#Z)\"><path fill=\"#1A1A1A\" d=\"M88.438 76.123c-7.1 0-15.6.5-25.6 2-.6.1-1.2.1-1.8.1-3.5 0-7.6-1.1-9.5-1.1h-.3c-2 .2-2.6 6.5-1.8 8.1.8 1.7 5 3 5.5 5s4.2 21.6 13.8 29.7c4 3.4 10.6 4.5 17.2 4.5 10 0 19.9-2.5 21.3-3.5 10.4-7.4 12.8-28.5 16.3-28.5.5 0 1.8-.1 3-.1 1.6 0 3.3.1 3.9.5 6 4.7 5.8 24 12.1 30 3.6 3.4 17.4 6.3 28.2 6.3 6.1 0 11.3-.9 13.2-3.1 5.3-6.1 13.1-24.7 14.9-26.1 1.8-1.4 5.8-2.3 7-4.2 1.1-1.8.2-9.6-2.5-9.6h-.2c-2.2.3-6 1.4-9.1 1.4-.8 0-1.6-.1-2.3-.3-2.6-.8-12.7-6.9-35.7-6.9-6.9 0-14.9.5-24.2 1.9-1.1.2-2.3.2-3.3.2-2.7 0-5.2-.4-7.1-.9-2-.2-11.2-5.4-33-5.4m0 2c18.1 0 27.4 3.6 30.8 5 .7.3 1.2.5 1.7.6 1.8.4 4.5 1 7.6 1 1.2 0 2.5-.1 3.6-.3 8.5-1.3 16.5-1.9 23.9-1.9 19 0 28.9 4.2 33.2 6.1.9.4 1.5.6 2 .8.9.3 1.8.4 2.9.4 2.2 0 4.6-.5 6.6-.9.9-.2 1.8-.4 2.5-.5.9 1 1.5 5.4 1 6.6-.4.6-2.1 1.4-3.3 1.9-1.2.5-2.3 1-3.1 1.6-1.1.8-2.2 2.9-5.4 9.3-3.1 6.1-6.9 13.7-9.8 17-1 1.2-4.6 2.4-11.8 2.4-10.5 0-23.9-2.8-26.9-5.7-2.8-2.6-4.2-8.7-5.6-14.6-1.6-6.6-3.1-12.8-6.6-15.6-.9-.7-2.3-1-5.1-1h-3.1c-2.8 0-4 3.3-6.1 9.3-2.3 6.6-5.6 15.5-11.3 19.6-1.3.8-10.7 3.2-20.2 3.2-7.3 0-12.8-1.4-15.9-4-3.8-3.2-9.1-10.7-12.9-27.8-.1-.4-.2-.7-.2-.9-.5-1.9-2.3-3-3.8-4-.6-.4-1.6-1.1-1.8-1.4-.4-.9 0-4.2.6-5.3.7 0 1.7.2 2.8.4 1.9.3 4.3.7 6.5.7.7 0 1.5 0 2.1-.2 8.9-1.1 17.4-1.8 25.1-1.8\"></path></g></g><g><defs><path id=\"aa\" d=\"M64.4 83.4h50.5v34.2H64.4z\"></path></defs><clipPath id=\"ab\"><use xlink:href=\"#aa\" overflow=\"visible\"></use></clipPath><g opacity=\".5\" clip-path=\"url(#ab)\"><path fill=\"none\" stroke=\"#333\" stroke-width=\"4\" stroke-miterlimit=\"10\" d=\"M66.2 90.45c-.9 1.2-1.5 18.9 6 23.7 4.9 3.2 25.7 2.5 29.9.2 6.5-3.7 12.9-20.5 11.1-22.9-3-4-24.6-9.3-40.8-5.1-2 .4-5.1 2.6-6.2 4.1z\"></path></g></g><path fill=\"#FFF\" d=\"M66.2 90.4c-.9 1.2-1.5 18.9 6 23.7 4.9 3.2 25.7 2.5 29.9.2 6.5-3.7 12.9-20.5 11.1-22.9-3-4-24.6-9.3-40.8-5.1-2 .5-5.1 2.6-6.2 4.1\"></path><g><defs><path id=\"ac\" d=\"M139.8 88.1h50.1v35h-50.1z\"></path></defs><clipPath id=\"ad\"><use xlink:href=\"#ac\" overflow=\"visible\"></use></clipPath><g opacity=\".5\" clip-path=\"url(#ad)\"><path fill=\"none\" stroke=\"#333\" stroke-width=\"4\" stroke-miterlimit=\"10\" d=\"M188.394 98.163c.8 1.3-1 18.9-9 22.8-5.3 2.5-25.8-.9-29.6-3.7-6-4.5-10.2-22-8.1-24.2 3.5-3.6 25.6-6 41.1.2 1.9.7 4.7 3.3 5.6 4.9z\"></path></g></g><path fill=\"#FFF\" d=\"M188.4 98.2c.8 1.3-1 18.9-9 22.8-5.3 2.5-25.8-.9-29.6-3.7-6-4.5-10.2-22-8.1-24.2 3.5-3.6 25.6-6 41.1.2 1.9.7 4.6 3.2 5.6 4.9\"></path><path d=\"M89.9 101.2c-.5 3.9-2.5 6.8-4.4 6.5-1.9-.3-2.9-3.6-2.4-7.5s2.5-6.8 4.4-6.5c1.9.3 2.9 3.6 2.4 7.5M169.6 105.6c.4 3.9-.8 7.2-2.6 7.4-1.9.2-3.7-2.8-4.1-6.7-.4-3.9.8-7.2 2.6-7.4 1.8-.1 3.6 2.9 4.1 6.7\"></path><g><defs><path id=\"ae\" d=\"M49.1 81.6h156.3v46H49.1z\"></path></defs><clipPath id=\"af\"><use xlink:href=\"#ae\" overflow=\"visible\"></use></clipPath><g opacity=\".3\" clip-path=\"url(#af)\"><path fill=\"#333\" d=\"M51.475 82.75c2.3 1.5 4.4.6 4.9 3.6s7.4 29 16 32c14.1 4.9 31.9.1 34.3-1.4 2.4-1.5 13.7-26.8 15.6-27.8 1.9-1 7.9.1 9.2.7 3.9 1.7 11.1 29.1 13 30.5 8.7 6.1 36.6 5.6 37.9 4 1.4-1.7 11.8-25.6 13.7-27 2.7-2.1 9-3.5 9.2-2.6.2.9-5.1 1.8-6.7 3.7-1.6 2-12.1 24.9-15.4 26.9-3.3 2-27.7 5.3-40.1-3.4-5.9-4.2-8.2-29.1-12.9-30.5-3-.9-5.3-.8-7.5-.2-2.9 1.9-7 23.1-15.7 28.4-3.5 2.1-30.6 7.4-38.7-1.1-15.1-15.8-10.5-29.9-15.4-31.9-3.3-1.4-4-3.2-4-4.4.3-1.3 1.4-.3 2.6.5\"></path></g></g><g><defs><path id=\"ag\" d=\"M51.6 77.2h20.2v3H51.6z\"></path></defs><clipPath id=\"ah\"><use xlink:href=\"#ag\" overflow=\"visible\"></use></clipPath><g opacity=\".3\" clip-path=\"url(#ah)\"><path fill=\"#333\" d=\"M51.75 77.45c-1.6-.6 6.8 2.9 9.9 2.7 3.1-.2 8.3-2.4 10-2.9 1.5-.5-7.8 1.2-10.1 1.1s-8.9-.6-9.8-.9\"></path></g></g><g><defs><path id=\"ai\" d=\"M116.6 82.1h21.7v5.3h-21.7z\"></path></defs><clipPath id=\"aj\"><use xlink:href=\"#ai\" overflow=\"visible\"></use></clipPath><g opacity=\".3\" clip-path=\"url(#aj)\"><path fill=\"#333\" d=\"M116.6 82.15c2.9 1.3 4.2 4.6 7 4.9s6.2.8 7.8-.1c1.6-.9 5.6-3 6.7-3.2 1.2-.2-8.7 1.3-12.4 1-3.7-.4-8-2.5-9.1-2.6\"></path></g></g><g><defs><path id=\"ak\" d=\"M187.4 86h12.3v3.9h-12.3z\"></path></defs><clipPath id=\"al\"><use xlink:href=\"#ak\" overflow=\"visible\"></use></clipPath><g opacity=\".3\" clip-path=\"url(#al)\"><path fill=\"#333\" d=\"M187.575 85.975c-1.2-.5 4.4 4 6.1 3.8 1.7-.2 4.7-2.1 6-2.6 1-.4-4 1-5.5.7-1.5-.1-5.4-1.4-6.6-1.9\"></path></g></g><g><defs><path id=\"am\" d=\"M62.3 82.8h28.9v28.6H62.3z\"></path></defs><clipPath id=\"an\"><use xlink:href=\"#am\" overflow=\"visible\"></use></clipPath><g opacity=\".3\" clip-path=\"url(#an)\"><path fill=\"#333\" d=\"M63.05 87.825c7.4-7.3 25-4.8 28.2-4.2-21.1 1.5-21.5 3.1-25.2 6.8-.5 2.9-1.2 12.5 2.1 20.9-3.7-2-7.6-21-5.1-23.5\"></path></g></g><g><defs><path id=\"ao\" d=\"M165.1 87.8h27.8v30.5h-27.8z\"></path></defs><clipPath id=\"ap\"><use xlink:href=\"#ao\" overflow=\"visible\"></use></clipPath><g opacity=\".3\" clip-path=\"url(#ap)\"><path fill=\"#333\" d=\"M192.46 95.698c-6.4-8.2-24.1-8.1-27.4-7.8 20.7 4.3 20.9 5.9 24.1 10.1.2 2.9-.4 12.5-4.7 20.4 3.8-1.6 10.1-20 8-22.7\"></path></g></g><g><defs><path id=\"aq\" d=\"M65.8 84.8h47.8v27.5H65.8z\"></path></defs><clipPath id=\"ar\"><use xlink:href=\"#aq\" overflow=\"visible\"></use></clipPath><g clip-path=\"url(#ar)\"><path fill=\"#D0DDDC\" d=\"M66.412 102.575c.7-8.6 1.5-15.6 18.6-16.1 13.7-.4 24.6 2.8 26.1 6.1 1 2.1.2 11.3-6.6 19.7 4.4-3.9 11.4-19.4 8.4-21.3-11.1-7.1-33.2-7.3-41.4-4.4-1.5.5-3.9 2.2-5.5 4-.3 2.2-.3 10.8.4 12\"></path></g></g><g><defs><path id=\"as\" d=\"M66.8 103H102v13.9H66.8z\"></path></defs><clipPath id=\"at\"><use xlink:href=\"#as\" overflow=\"visible\"></use></clipPath><g clip-path=\"url(#at)\"><path fill=\"#D0DDDC\" d=\"M66.884 103.2c2.6 16.8 33.3 11.7 35.1 11-.1.9-16.1 5.4-29.5.2-5.4-2-5.8-13.1-5.6-11.2\"></path></g></g><g><defs><path id=\"au\" d=\"M141.2 89.5h47.7V115h-47.7z\"></path></defs><clipPath id=\"av\"><use xlink:href=\"#au\" overflow=\"visible\"></use></clipPath><g clip-path=\"url(#av)\"><path fill=\"#D0DDDC\" d=\"M186.957 110.087c.4-8.7.4-15.6-16.4-18.2-13.5-2.1-24.7-.3-26.7 2.8-1.2 1.9-1.6 11.2 4.2 20.4-3.9-4.5-9-20.6-5.7-22.2 11.9-5.7 33.9-3.2 41.6.7 1.4.7 3.7 2.6 5 4.7-.1 2.1-1.1 10.7-2 11.8\"></path></g></g><g><defs><path id=\"aw\" d=\"M150.2 110.4h36.3v11.7h-36.3z\"></path></defs><clipPath id=\"ax\"><use xlink:href=\"#aw\" overflow=\"visible\"></use></clipPath><g clip-path=\"url(#ax)\"><path fill=\"#D0DDDC\" d=\"M186.383 110.613c-4.6 16.3-34.5 7.5-36.2 6.6 0 .9 15.3 7.3 29.3 3.9 5.6-1.4 7.5-12.4 6.9-10.5\"></path></g></g><g><defs><path id=\"ay\" d=\"M87.3 95.2h2v2h-2z\"></path></defs><clipPath id=\"az\"><use xlink:href=\"#ay\" overflow=\"visible\"></use></clipPath><g opacity=\".25\" clip-path=\"url(#az)\"><path fill=\"#FFF\" d=\"M89.375 96.2c0-.5-.4-1-1-1s-1 .4-1 1 .4 1 1 1 1-.4 1-1\"></path></g></g><g><defs><path id=\"aA\" d=\"M166.3 99.9h2v2h-2z\"></path></defs><clipPath id=\"aB\"><use xlink:href=\"#aA\" overflow=\"visible\"></use></clipPath><g opacity=\".25\" clip-path=\"url(#aB)\"><path fill=\"#FFF\" d=\"M168.375 100.9c0-.5-.4-1-1-1-.5 0-1 .4-1 1s.4 1 1 1c.5 0 1-.5 1-1\"></path></g></g><g><defs><path id=\"aC\" d=\"M52 79.1h64.8v9.5H52z\"></path></defs><clipPath id=\"aD\"><use xlink:href=\"#aC\" overflow=\"visible\"></use></clipPath><g opacity=\".1\" clip-path=\"url(#aD)\"><path fill=\"#D0DDDC\" d=\"M52.025 81.675c2.4 2.7 3.9 3 7.1 2.6 31.1-4 48.3-3.9 56.8 4 1.8 1.7.8-5.1-2.6-6-26.2-6.9-45.2-.7-49.2-.2-4.1.6-7-.3-12.1-.4\"></path></g></g><g><defs><path id=\"aE\" d=\"M137.7 82.3h47.9v9.2h-47.9z\"></path></defs><clipPath id=\"aF\"><use xlink:href=\"#aE\" overflow=\"visible\"></use></clipPath><g opacity=\".1\" clip-path=\"url(#aF)\"><path fill=\"#D0DDDC\" d=\"M138.025 85.675c-.7.7-.4 7.9 3 5.2 9.8-7.8 30.3-6 44.6-1.2-2.3-4.5-19-7.6-28.4-7.5-4.5.2-17.3 1.5-19.2 3.5\"></path></g></g><path fill=\"#D0DDDC\" d=\"M197.2 91.4c0 .3-1.6.5-3.5.5s-3.5-.2-3.5-.5 1.6-.5 3.5-.5c2 0 3.5.3 3.5.5M62.4 82.8c0 .3-1.6.3-3.5 0s-3.4-.8-3.3-1.1 1.6-.3 3.5 0c1.9.2 3.4.7 3.3 1.1\"></path><g><path fill=\"#FFF\" d=\"M256.04 243.036c-.6-6.1-5.1-8.8-9.2-8.8-1.9 0-3.4.5-4 .8l-.8.4-.7.5c-1 .7-2.7 1.8-4.2 2.3-.1-1.2 0-2.8.2-4.2v-.2c1.1-10.7-1.1-17-6.7-18.6-.9-.3-1.9-.4-2.9-.4-3.4 0-6.4 1.6-8.3 3-1.6-2.1-4.4-3.2-7.8-3.2-5.1 0-9.2 2.4-10.7 6.2-.2.4-.4 1-.6 1.8-3-6.6-9.1-9.1-14.8-9.1-5.4 0-10.6 2.1-12.5 3.6-.1.1-.2.1-.3.2-5.1 3.6-9.1 8.2-11.9 13.7-1.4-4.7-4.4-9.2-9.7-12.5l-.1-.1c-.2-.1-.3-.2-.4-.2 5.3-9.3 9.9-21.7 7.1-33.5-3-12.6-10.4-14.4-14.5-14.4-1.7 0-3.5.3-5.3.9h-.2c-7.8 3.1-11.2 7.7-12 8.8-.8 1.2-9.3 14.2-11.4 33.2-.2 2.1-.5 4.3-.7 6.5-1.2-1.7-3.2-3.3-6.3-4.2-1-.3-2-.4-3-.4-4.3 0-7.8 2.6-9.7 4.4-1.6-2.4-4.2-3.9-7.1-4h-.7c-4.6 0-8.2 2-10.9 4.3-1.7-3-4.9-4.9-8.6-4.9h-1.5c-5.1 0-9.1 2.4-10.7 6.4-.1.3-.3.7-.4 1.2-4-5-9.1-7.3-16-7.3h-.7c-4.3.1-14.9 1.9-21.5 16.6-1 2.3-1.8 4.6-2.4 6.8-1.4-.7-2.8-.8-3.7-.8-3.4 0-6 1.9-7.2 3.8-1 1.5-4.2 6.7-2.6 11.9.9 2.8 2.8 4.9 5.6 6 2.5 1 5.6 1.9 7.9 2.6 1.4 3.3 4 7 8.6 10.5 3.2 2.5 7.4 3.7 12.2 3.7 8.2 0 16.1-3.5 16.9-3.9l.3-.2.1-.1c1.6 1.6 3.6 2.8 6.2 3.3.8.2 1.8.3 3 .3 4.4 0 7.5-2.3 10.4-6.5 2.3 3.7 6.6 5.7 11.6 5.7.3 0 .5.2.9.2 2.6 0 6-.9 8.7-5.4 2.3 4.1 6.5 6.5 11.5 6.5 4.4 0 8.3-1.8 9-2.2l.2-.1c1.5-.8 2.7-1.5 3.7-2 3.3 1.8 8.3 3.7 14.5 3.7 1.1 0 2.1 0 3.2-.2h.6c13-2.6 21.5-5.7 26.6-8.1 3.5 4 9.2 7.8 17.2 8.4.9.1 1.8.1 2.7.1 6.6 0 11.9-1.9 15.8-3.9 1.2 1.3 2.8 2.5 4.8 3.3 1.2.5 2.5.7 3.7.7 8.1 0 11.9-9.6 12.9-12.9l1.9-5.2c1.7 1.8 3.9 3.3 6.7 4.1 1.5.4 3.2.7 4.8.7 8.9 0 17.5-6.3 18.8-7.4 2.3-2.3 4.9-6.5 4.4-12.2\"></path></g><g><path fill=\"#E24B31\" d=\"M174.35 245.713s-1.1-4 3.1-11.6 7.4-3.5 7.4-3.5 3.6 3.9-.5 9.7c-4.1 5.8-10 5.4-10 5.4m-32.1 5.5c-4.8 12.5-16.4 7.4-16.4 7.4s-1.3-4.5 2.5-17 12.6-7.6 12.6-7.6 6.1 4.7 1.3 17.2m-8.7-52.5s7.2-18.9 9-9.7c1.8 9.2-15.2 36.5-15.2 36.5.2-6.1 6.2-26.8 6.2-26.8m-103.5 47c.3-12.1 8.2-17.4 11-14.8 2.8 2.6 1.8 8.4-3.5 11.9-5.1 3.6-7.5 2.9-7.5 2.9m219.3-2c-.4-4.1-4.1-2.6-4.1-2.6s-5.9 4.6-11.1 4.1c-5.2-.5-3.5-12.1-3.5-12.1s1.1-10.7-1.9-11.6c-3.1-.9-6.8 2.8-6.8 2.8s-4.7 5.2-6.9 11.8l-.6.2s.7-11.6-.1-14.3c-.6-1.3-6.2-1.2-7.1 1.1-.9 2.3-5.4 18.6-5.7 25.5 0 0-8.8 7.4-16.4 8.7-7.7 1.2-9.5-3.6-9.5-3.6s20.8-5.8 20.1-22.4c-.7-16.6-16.8-10.5-18.6-9.1-1.8 1.3-11.1 7-13.9 22.6-.1.5-.3 2.9-.3 2.9s-8.1 5.4-12.5 6.8c0 0 12.5-21.1-2.8-30.7-6.9-4.2-12.5 4.6-12.5 4.6s20.7-23 16.1-42.5c-2.2-9.3-6.8-10.3-11.1-8.8-6.5 2.6-8.9 6.3-8.9 6.3s-8.4 12.1-10.3 30.2-4.8 39.8-4.8 39.8-4 3.9-7.6 4.1c-3.7.2-2-10.9-2-10.9s2.8-16.9 2.6-19.8-.4-4.4-3.8-5.4c-3.3-1-7 3.3-7 3.3s-9.7 14.7-10.5 16.9l-.5.9-.5-.6s6.8-20 .3-20.3c-6.5-.3-10.8 7.1-10.8 7.1s-7.4 12.5-7.7 13.9l-.5-.6s3.1-14.5 2.5-18.1c-.6-3.6-4-2.8-4-2.8s-4.3-.5-5.4 2.3c-1.1 2.8-5.2 21-5.7 26.8 0 0-10.7 7.7-17.7 7.7-7 .1-6.3-4.5-6.3-4.5s25.8-8.8 18.8-26.3c-3.2-4.5-6.8-5.9-12-5.8-5.2.1-11.3 3.3-15.5 12.6-2 4.5-2.8 8.7-2.8 11.9 0 0-4.8.9-7.3-1.1s-3.9 0-3.9 0-4.3 6.1-.1 7.7c4.2 1.6 10.6 3.2 10.6 3.2.6 2.6 2.3 7 7.4 10.8 7.6 5.8 22.3-.9 22.3-.9l6-3.6s.2 5.4 4.6 6.2c4.4.8 6.2-.1 13.9-18.6 4.5-9.5 4.8-9 4.8-9l.5-.1s-3.5 18.1-2.1 23 7.1 4.4 7.1 4.4 3.2 1.1 5.7-7.9c2.6-9 7.4-18.4 7.4-18.4h.6s-2.1 18.1 1.1 24c3.3 5.9 11.7 1.8 11.7 1.8s5.9-3.1 6.8-4c0 0 7 5.9 16.9 4.8 22.1-4.4 30-10.3 30-10.3s3.8 9.6 15.6 10.5c13.5 1 20.8-7.5 20.8-7.5s-.1 5.5 4.6 7.4c4.7 1.9 7.9-8.3 7.9-8.3l7.9-21.1h.7s.4 13.5 8.2 15.7c7.8 2.2 17.9-5.6 17.9-5.6s2.2-1.1 1.7-5.2\"></path></g></svg>"

/***/ }),
/* 1120 */,
/* 1121 */,
/* 1122 */,
/* 1123 */,
/* 1124 */,
/* 1125 */,
/* 1126 */
/***/ (function(module, exports) {

module.exports = "<svg enable-background=\"new 0 0 800 800\" id=\"GUIDE\" version=\"1.1\" viewBox=\"0 0 800 800\" xml:space=\"preserve\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:cc=\"http://creativecommons.org/ns#\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\" xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\" xmlns:svg=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"><path d=\"M580.562,219.439c-12.721-12.723-29.637-19.728-47.623-19.728c-17.987,0-34.903,7.005-47.625,19.728 c-12.72,12.72-19.725,29.634-19.725,47.621c0,17.99,7.005,34.904,19.725,47.625c12.722,12.721,29.633,19.723,47.618,19.726 c0.007,0,0.007,0,0.007,0c17.986,0,34.902-7.005,47.623-19.726c12.721-12.723,19.726-29.636,19.726-47.625 C600.286,249.073,593.281,232.16,580.562,219.439z M553.771,287.895c-5.566,5.568-12.96,8.636-20.834,8.636l0,0 c-7.872-0.002-15.271-3.068-20.834-8.636c-5.566-5.562-8.633-12.96-8.633-20.834c0-7.868,3.065-15.269,8.633-20.834 c5.563-5.565,12.967-8.63,20.834-8.63c7.868,0,15.268,3.063,20.834,8.63C565.263,257.715,565.263,276.407,553.771,287.895z\" fill=\"#041C3F\"></path><g><path d=\"M62.282,627.218c-4.847,0-9.693-1.847-13.392-5.546c-7.398-7.397-7.398-19.395,0-26.79L158.42,485.35 c7.398-7.397,19.392-7.397,26.79,0s7.398,19.395,0,26.792L75.676,621.672C71.978,625.371,67.131,627.218,62.282,627.218z\" fill=\"#041C3F\"></path></g><g><path d=\"M86.774,732.172c-4.85,0-9.696-1.85-13.395-5.549c-7.398-7.397-7.398-19.389,0-26.786L187.545,585.67 c7.398-7.398,19.392-7.398,26.787,0c7.398,7.398,7.398,19.393,0,26.79L100.168,726.623C96.47,730.322,91.62,732.172,86.774,732.172 z\" fill=\"#041C3F\"></path></g><g><path d=\"M191.725,756.661c-4.849,0-9.696-1.847-13.395-5.546c-7.398-7.397-7.398-19.393,0-26.789L287.863,614.79 c7.396-7.394,19.392-7.396,26.787,0c7.398,7.397,7.398,19.395,0,26.793L205.12,751.115 C201.421,754.813,196.574,756.661,191.725,756.661z\" fill=\"#041C3F\"></path></g><path d=\"M751.113,48.891c-4.302-4.3-10.409-6.278-16.403-5.311c-2.202,0.357-54.705,8.98-126.25,36.316 c-41.974,16.034-81.85,35.237-118.529,57.076c-45.039,26.814-85.356,57.721-119.899,91.871l-143.055,27.85 c-3.693,0.718-7.086,2.524-9.753,5.177L87.618,391.06c-5.907,5.886-7.267,14.938-3.36,22.301c3.33,6.27,9.818,10.059,16.725,10.059 c1.202,0,2.415-0.114,3.628-0.347l146.185-28.463c-9.516,18.672-18.419,38.055-26.683,58.144 c-2.904,7.072-1.279,15.194,4.125,20.603l35.811,35.811l-33.27,33.27c-7.398,7.398-7.398,19.39,0,26.787 c3.699,3.699,8.545,5.549,13.397,5.549c4.847,0,9.693-1.85,13.392-5.546l33.27-33.271l35.811,35.813 c3.625,3.619,8.469,5.548,13.4,5.548c2.423,0,4.871-0.467,7.199-1.426c20.091-8.262,39.475-17.165,58.141-26.678l-28.455,146.186 c-1.593,8.183,2.35,16.443,9.709,20.352c2.806,1.488,5.852,2.213,8.879,2.213c4.917,0,9.778-1.918,13.417-5.573l129.188-129.604 c2.656-2.663,4.459-6.061,5.181-9.753l27.845-143.055c34.148-34.547,65.06-74.859,91.876-119.901 c21.834-36.683,41.04-76.558,57.077-118.529c27.33-71.551,35.958-124.048,36.313-126.25 C757.386,59.292,755.407,53.188,751.113,48.891z M158.393,374.001l81.489-81.224l87.674-17.069 c-19.015,23.391-36.655,48.634-52.847,75.648L158.393,374.001z M507.219,560.121l-81.222,81.489l22.643-116.316 c27.021-16.192,52.259-33.83,75.648-52.848L507.219,560.121z M684.359,178.936c-23.915,62.371-68.01,152.302-142.237,226.531 c-34.171,34.168-73.96,64.54-118.89,90.838c-0.804,0.401-1.585,0.854-2.322,1.366c-24.049,13.943-49.566,26.728-76.476,38.302 l-26.806-26.809l54.11-54.106c7.395-7.397,7.395-19.392,0-26.79c-7.398-7.397-19.392-7.396-26.79,0l-54.109,54.106l-26.806-26.809 c11.578-26.913,24.361-52.433,38.308-76.488c0.508-0.732,0.951-1.5,1.35-2.295c26.298-44.938,56.672-84.732,90.849-118.909 c74.225-74.225,164.156-118.319,226.527-142.235c37.897-14.537,70.522-23.601,92.09-28.797 C707.959,108.412,698.894,141.038,684.359,178.936z\" fill=\"#041C3F\"></path></svg>"

/***/ }),
/* 1127 */,
/* 1128 */,
/* 1129 */,
/* 1130 */,
/* 1131 */,
/* 1132 */,
/* 1133 */,
/* 1134 */,
/* 1135 */,
/* 1136 */,
/* 1137 */,
/* 1138 */,
/* 1139 */,
/* 1140 */,
/* 1141 */,
/* 1142 */,
/* 1143 */,
/* 1144 */,
/* 1145 */,
/* 1146 */,
/* 1147 */,
/* 1148 */,
/* 1149 */,
/* 1150 */,
/* 1151 */,
/* 1152 */,
/* 1153 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M8,3L12,3L12,7L14,7L14,8L12,8L12,12L8,12L8,14L7,14L7,12L3,12L3,8L1,8L1,7L3,7L3,3L7,3L7,1L8,1L8,3ZM10,10L10,5L5,5L5,10L10,10Z\"></path></svg>"

/***/ }),
/* 1154 */,
/* 1155 */,
/* 1156 */,
/* 1157 */,
/* 1158 */,
/* 1159 */,
/* 1160 */,
/* 1161 */,
/* 1162 */,
/* 1163 */,
/* 1164 */,
/* 1165 */,
/* 1166 */,
/* 1167 */,
/* 1168 */,
/* 1169 */,
/* 1170 */,
/* 1171 */,
/* 1172 */,
/* 1173 */,
/* 1174 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:cc=\"http://creativecommons.org/ns#\" xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" xmlns:svg=\"http://www.w3.org/2000/svg\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 400 400\" xml:space=\"preserve\" id=\"svg2\" version=\"1.1\"><metadata id=\"metadata8\"><rdf:RDF><cc:Work rdf:about><dc:format>image/svg+xml</dc:format><dc:type rdf:resource=\"http://purl.org/dc/dcmitype/StillImage\"></dc></cc:Work></rdf:RDF></metadata><defs id=\"defs6\"></defs><g transform=\"matrix(1.3333333,0,0,-1.3333333,0,400)\" id=\"g10\"><g transform=\"translate(178.0626,235.0086)\" id=\"g12\"><path id=\"path14\" style=\"fill:#41b883;fill-opacity:1;fill-rule:nonzero;stroke:none\" d=\"M 0,0 -22.669,-39.264 -45.338,0 h -75.491 L -22.669,-170.017 75.491,0 Z\"></path></g><g transform=\"translate(178.0626,235.0086)\" id=\"g16\"><path id=\"path18\" style=\"fill:#34495e;fill-opacity:1;fill-rule:nonzero;stroke:none\" d=\"M 0,0 -22.669,-39.264 -45.338,0 H -81.565 L -22.669,-102.01 36.227,0 Z\"></path></g></g></svg>"

/***/ }),
/* 1175 */,
/* 1176 */,
/* 1177 */,
/* 1178 */,
/* 1179 */,
/* 1180 */,
/* 1181 */,
/* 1182 */,
/* 1183 */,
/* 1184 */,
/* 1185 */,
/* 1186 */,
/* 1187 */,
/* 1188 */,
/* 1189 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__components_Provider__ = __webpack_require__(1195);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__components_connectAdvanced__ = __webpack_require__(1192);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__connect_connect__ = __webpack_require__(1198);
/* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "Provider", function() { return __WEBPACK_IMPORTED_MODULE_0__components_Provider__["b"]; });
/* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "createProvider", function() { return __WEBPACK_IMPORTED_MODULE_0__components_Provider__["a"]; });
/* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "connectAdvanced", function() { return __WEBPACK_IMPORTED_MODULE_1__components_connectAdvanced__["a"]; });
/* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "connect", function() { return __WEBPACK_IMPORTED_MODULE_2__connect_connect__["a"]; });






/***/ }),
/* 1190 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony export (immutable) */ __webpack_exports__["a"] = warning;
/**
 * 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);
    /* eslint-disable no-empty */
  } catch (e) {}
  /* eslint-enable no-empty */
}

/***/ }),
/* 1191 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return subscriptionShape; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return storeShape; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_prop_types__ = __webpack_require__(20);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_prop_types___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_prop_types__);


var subscriptionShape = __WEBPACK_IMPORTED_MODULE_0_prop_types___default.a.shape({
  trySubscribe: __WEBPACK_IMPORTED_MODULE_0_prop_types___default.a.func.isRequired,
  tryUnsubscribe: __WEBPACK_IMPORTED_MODULE_0_prop_types___default.a.func.isRequired,
  notifyNestedSubs: __WEBPACK_IMPORTED_MODULE_0_prop_types___default.a.func.isRequired,
  isSubscribed: __WEBPACK_IMPORTED_MODULE_0_prop_types___default.a.func.isRequired
});

var storeShape = __WEBPACK_IMPORTED_MODULE_0_prop_types___default.a.shape({
  subscribe: __WEBPACK_IMPORTED_MODULE_0_prop_types___default.a.func.isRequired,
  dispatch: __WEBPACK_IMPORTED_MODULE_0_prop_types___default.a.func.isRequired,
  getState: __WEBPACK_IMPORTED_MODULE_0_prop_types___default.a.func.isRequired
});

/***/ }),
/* 1192 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony export (immutable) */ __webpack_exports__["a"] = connectAdvanced;
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_hoist_non_react_statics__ = __webpack_require__(1196);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_hoist_non_react_statics___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_hoist_non_react_statics__);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_invariant__ = __webpack_require__(159);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_invariant___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1_invariant__);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2_react__ = __webpack_require__(0);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2_react___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_2_react__);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__utils_Subscription__ = __webpack_require__(1197);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__utils_PropTypes__ = __webpack_require__(1191);
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; };

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; }

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; }








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] = __WEBPACK_IMPORTED_MODULE_4__utils_PropTypes__["a" /* storeShape */], _contextTypes[subscriptionKey] = __WEBPACK_IMPORTED_MODULE_4__utils_PropTypes__["b" /* subscriptionShape */], _contextTypes);
  var childContextTypes = (_childContextTypes = {}, _childContextTypes[subscriptionKey] = __WEBPACK_IMPORTED_MODULE_4__utils_PropTypes__["b" /* subscriptionShape */], _childContextTypes);

  return function wrapWithConnect(WrappedComponent) {
    __WEBPACK_IMPORTED_MODULE_1_invariant___default()(typeof WrappedComponent == 'function', 'You must pass a component to the function returned by ' + ('connect. 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);

        __WEBPACK_IMPORTED_MODULE_1_invariant___default()(_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() {
        __WEBPACK_IMPORTED_MODULE_1_invariant___default()(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 __WEBPACK_IMPORTED_MODULE_3__utils_Subscription__["a" /* default */](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 Object(__WEBPACK_IMPORTED_MODULE_2_react__["createElement"])(WrappedComponent, this.addExtraProps(selector.props));
        }
      };

      return Connect;
    }(__WEBPACK_IMPORTED_MODULE_2_react__["Component"]);

    Connect.WrappedComponent = WrappedComponent;
    Connect.displayName = displayName;
    Connect.childContextTypes = childContextTypes;
    Connect.contextTypes = contextTypes;
    Connect.propTypes = contextTypes;

    if (false) {
      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 __WEBPACK_IMPORTED_MODULE_0_hoist_non_react_statics___default()(Connect, WrappedComponent);
  };
}

/***/ }),
/* 1193 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony export (immutable) */ __webpack_exports__["a"] = wrapMapToPropsConstant;
/* unused harmony export getDependsOnOwnProps */
/* harmony export (immutable) */ __webpack_exports__["b"] = wrapMapToPropsFunc;
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__utils_verifyPlainObject__ = __webpack_require__(1194);


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);
      }

      if (false) verifyPlainObject(props, displayName, methodName);

      return props;
    };

    return proxy;
  };
}

/***/ }),
/* 1194 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* unused harmony export default */
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_lodash_es_isPlainObject__ = __webpack_require__(33);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__warning__ = __webpack_require__(1190);



function verifyPlainObject(value, displayName, methodName) {
  if (!Object(__WEBPACK_IMPORTED_MODULE_0_lodash_es_isPlainObject__["a" /* default */])(value)) {
    Object(__WEBPACK_IMPORTED_MODULE_1__warning__["a" /* default */])(methodName + '() in ' + displayName + ' must return a plain object. Instead received ' + value + '.');
  }
}

/***/ }),
/* 1195 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony export (immutable) */ __webpack_exports__["a"] = createProvider;
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_react__ = __webpack_require__(0);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_react___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_react__);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_prop_types__ = __webpack_require__(20);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_prop_types___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1_prop_types__);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__utils_PropTypes__ = __webpack_require__(1191);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__utils_warning__ = __webpack_require__(1190);
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; }






var didWarnAboutReceivingStore = false;
function warnAboutReceivingStore() {
  if (didWarnAboutReceivingStore) {
    return;
  }
  didWarnAboutReceivingStore = true;

  Object(__WEBPACK_IMPORTED_MODULE_3__utils_warning__["a" /* default */])('<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 __WEBPACK_IMPORTED_MODULE_0_react__["Children"].only(this.props.children);
    };

    return Provider;
  }(__WEBPACK_IMPORTED_MODULE_0_react__["Component"]);

  if (false) {
    Provider.prototype.componentWillReceiveProps = function (nextProps) {
      if (this[storeKey] !== nextProps.store) {
        warnAboutReceivingStore();
      }
    };
  }

  Provider.propTypes = {
    store: __WEBPACK_IMPORTED_MODULE_2__utils_PropTypes__["a" /* storeShape */].isRequired,
    children: __WEBPACK_IMPORTED_MODULE_1_prop_types___default.a.element.isRequired
  };
  Provider.childContextTypes = (_Provider$childContex = {}, _Provider$childContex[storeKey] = __WEBPACK_IMPORTED_MODULE_2__utils_PropTypes__["a" /* storeShape */].isRequired, _Provider$childContex[subscriptionKey] = __WEBPACK_IMPORTED_MODULE_2__utils_PropTypes__["b" /* subscriptionShape */], _Provider$childContex);

  return Provider;
}

/* harmony default export */ __webpack_exports__["b"] = (createProvider());

/***/ }),
/* 1196 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * Copyright 2015, Yahoo! Inc.
 * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
 */


var REACT_STATICS = {
    childContextTypes: true,
    contextTypes: true,
    defaultProps: true,
    displayName: true,
    getDefaultProps: 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);

module.exports = 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;
};


/***/ }),
/* 1197 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return Subscription; });
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

// 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() {
      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;
}();



/***/ }),
/* 1198 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* unused harmony export createConnect */
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__components_connectAdvanced__ = __webpack_require__(1192);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__utils_shallowEqual__ = __webpack_require__(1199);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__mapDispatchToProps__ = __webpack_require__(1200);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__mapStateToProps__ = __webpack_require__(1201);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__mergeProps__ = __webpack_require__(1202);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__selectorFactory__ = __webpack_require__(1203);
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; };

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; }








/*
  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 ? __WEBPACK_IMPORTED_MODULE_0__components_connectAdvanced__["a" /* default */] : _ref$connectHOC,
      _ref$mapStateToPropsF = _ref.mapStateToPropsFactories,
      mapStateToPropsFactories = _ref$mapStateToPropsF === undefined ? __WEBPACK_IMPORTED_MODULE_3__mapStateToProps__["a" /* default */] : _ref$mapStateToPropsF,
      _ref$mapDispatchToPro = _ref.mapDispatchToPropsFactories,
      mapDispatchToPropsFactories = _ref$mapDispatchToPro === undefined ? __WEBPACK_IMPORTED_MODULE_2__mapDispatchToProps__["a" /* default */] : _ref$mapDispatchToPro,
      _ref$mergePropsFactor = _ref.mergePropsFactories,
      mergePropsFactories = _ref$mergePropsFactor === undefined ? __WEBPACK_IMPORTED_MODULE_4__mergeProps__["a" /* default */] : _ref$mergePropsFactor,
      _ref$selectorFactory = _ref.selectorFactory,
      selectorFactory = _ref$selectorFactory === undefined ? __WEBPACK_IMPORTED_MODULE_5__selectorFactory__["a" /* default */] : _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 ? __WEBPACK_IMPORTED_MODULE_1__utils_shallowEqual__["a" /* default */] : _ref2$areOwnPropsEqua,
        _ref2$areStatePropsEq = _ref2.areStatePropsEqual,
        areStatePropsEqual = _ref2$areStatePropsEq === undefined ? __WEBPACK_IMPORTED_MODULE_1__utils_shallowEqual__["a" /* default */] : _ref2$areStatePropsEq,
        _ref2$areMergedPropsE = _ref2.areMergedPropsEqual,
        areMergedPropsEqual = _ref2$areMergedPropsE === undefined ? __WEBPACK_IMPORTED_MODULE_1__utils_shallowEqual__["a" /* default */] : _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));
  };
}

/* harmony default export */ __webpack_exports__["a"] = (createConnect());

/***/ }),
/* 1199 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony export (immutable) */ __webpack_exports__["a"] = shallowEqual;
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;
}

/***/ }),
/* 1200 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* unused harmony export whenMapDispatchToPropsIsFunction */
/* unused harmony export whenMapDispatchToPropsIsMissing */
/* unused harmony export whenMapDispatchToPropsIsObject */
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_redux__ = __webpack_require__(3);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__wrapMapToProps__ = __webpack_require__(1193);



function whenMapDispatchToPropsIsFunction(mapDispatchToProps) {
  return typeof mapDispatchToProps === 'function' ? Object(__WEBPACK_IMPORTED_MODULE_1__wrapMapToProps__["b" /* wrapMapToPropsFunc */])(mapDispatchToProps, 'mapDispatchToProps') : undefined;
}

function whenMapDispatchToPropsIsMissing(mapDispatchToProps) {
  return !mapDispatchToProps ? Object(__WEBPACK_IMPORTED_MODULE_1__wrapMapToProps__["a" /* wrapMapToPropsConstant */])(function (dispatch) {
    return { dispatch: dispatch };
  }) : undefined;
}

function whenMapDispatchToPropsIsObject(mapDispatchToProps) {
  return mapDispatchToProps && typeof mapDispatchToProps === 'object' ? Object(__WEBPACK_IMPORTED_MODULE_1__wrapMapToProps__["a" /* wrapMapToPropsConstant */])(function (dispatch) {
    return Object(__WEBPACK_IMPORTED_MODULE_0_redux__["bindActionCreators"])(mapDispatchToProps, dispatch);
  }) : undefined;
}

/* harmony default export */ __webpack_exports__["a"] = ([whenMapDispatchToPropsIsFunction, whenMapDispatchToPropsIsMissing, whenMapDispatchToPropsIsObject]);

/***/ }),
/* 1201 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* unused harmony export whenMapStateToPropsIsFunction */
/* unused harmony export whenMapStateToPropsIsMissing */
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__wrapMapToProps__ = __webpack_require__(1193);


function whenMapStateToPropsIsFunction(mapStateToProps) {
  return typeof mapStateToProps === 'function' ? Object(__WEBPACK_IMPORTED_MODULE_0__wrapMapToProps__["b" /* wrapMapToPropsFunc */])(mapStateToProps, 'mapStateToProps') : undefined;
}

function whenMapStateToPropsIsMissing(mapStateToProps) {
  return !mapStateToProps ? Object(__WEBPACK_IMPORTED_MODULE_0__wrapMapToProps__["a" /* wrapMapToPropsConstant */])(function () {
    return {};
  }) : undefined;
}

/* harmony default export */ __webpack_exports__["a"] = ([whenMapStateToPropsIsFunction, whenMapStateToPropsIsMissing]);

/***/ }),
/* 1202 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* unused harmony export defaultMergeProps */
/* unused harmony export wrapMergePropsFunc */
/* unused harmony export whenMergePropsIsFunction */
/* unused harmony export whenMergePropsIsOmitted */
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__utils_verifyPlainObject__ = __webpack_require__(1194);
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; };



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;

        if (false) 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;
}

/* harmony default export */ __webpack_exports__["a"] = ([whenMergePropsIsFunction, whenMergePropsIsOmitted]);

/***/ }),
/* 1203 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* unused harmony export impureFinalPropsSelectorFactory */
/* unused harmony export pureFinalPropsSelectorFactory */
/* harmony export (immutable) */ __webpack_exports__["a"] = finalPropsSelectorFactory;
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__verifySubselectors__ = __webpack_require__(1204);
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 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);

  if (false) {
    verifySubselectors(mapStateToProps, mapDispatchToProps, mergeProps, options.displayName);
  }

  var selectorFactory = options.pure ? pureFinalPropsSelectorFactory : impureFinalPropsSelectorFactory;

  return selectorFactory(mapStateToProps, mapDispatchToProps, mergeProps, dispatch, options);
}

/***/ }),
/* 1204 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* unused harmony export default */
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__utils_warning__ = __webpack_require__(1190);


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')) {
      Object(__WEBPACK_IMPORTED_MODULE_0__utils_warning__["a" /* default */])('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);
}

/***/ }),
/* 1205 */,
/* 1206 */,
/* 1207 */,
/* 1208 */,
/* 1209 */,
/* 1210 */,
/* 1211 */,
/* 1212 */,
/* 1213 */,
/* 1214 */,
/* 1215 */,
/* 1216 */,
/* 1217 */,
/* 1218 */,
/* 1219 */,
/* 1220 */,
/* 1221 */,
/* 1222 */,
/* 1223 */,
/* 1224 */,
/* 1225 */,
/* 1226 */,
/* 1227 */,
/* 1228 */,
/* 1229 */,
/* 1230 */,
/* 1231 */,
/* 1232 */,
/* 1233 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 viewBox=\"0 0 256 296\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" preserveAspectRatio=\"xMidYMid\"><g><polygon fill=\"#673AB8\" points=\"128 0 256 73.8999491 256 221.699847 128 295.599796 0 221.699847 0 73.8999491\"></polygon><path d=\"M34.8647584,220.478469 C51.8814262,242.25881 105.959701,225.662965 157.014868,185.774297 C208.070035,145.885628 237.255632,97.428608 220.238964,75.6482664 C203.222296,53.8679249 149.144022,70.4637701 98.0888543,110.352439 C47.0336869,150.241107 17.8480906,198.698127 34.8647584,220.478469 Z M42.1343351,214.798853 C36.4908625,207.575537 38.9565723,193.395881 49.7081913,175.544904 C61.0297348,156.747677 80.2490923,135.997367 103.76847,117.622015 C127.287848,99.2466634 152.071368,85.6181573 173.049166,79.1803727 C192.970945,73.066665 207.325915,74.1045667 212.969387,81.3278822 C218.61286,88.5511977 216.14715,102.730854 205.395531,120.581832 C194.073987,139.379058 174.85463,160.129368 151.335252,178.50472 C127.815874,196.880072 103.032354,210.508578 82.054556,216.946362 C62.1327769,223.06007 47.7778077,222.022168 42.1343351,214.798853 Z\" fill=\"#FFFFFF\"></path><path d=\"M220.238964,220.478469 C237.255632,198.698127 208.070035,150.241107 157.014868,110.352439 C105.959701,70.4637701 51.8814262,53.8679249 34.8647584,75.6482664 C17.8480906,97.428608 47.0336869,145.885628 98.0888543,185.774297 C149.144022,225.662965 203.222296,242.25881 220.238964,220.478469 Z M212.969387,214.798853 C207.325915,222.022168 192.970945,223.06007 173.049166,216.946362 C152.071368,210.508578 127.287848,196.880072 103.76847,178.50472 C80.2490923,160.129368 61.0297348,139.379058 49.7081913,120.581832 C38.9565723,102.730854 36.4908625,88.5511977 42.1343351,81.3278822 C47.7778077,74.1045667 62.1327769,73.066665 82.054556,79.1803727 C103.032354,85.6181573 127.815874,99.2466634 151.335252,117.622015 C174.85463,135.997367 194.073987,156.747677 205.395531,175.544904 C216.14715,193.395881 218.61286,207.575537 212.969387,214.798853 Z\" fill=\"#FFFFFF\"></path><path d=\"M127.551861,167.666971 C138.378632,167.666971 147.155465,158.890139 147.155465,148.063368 C147.155465,137.236596 138.378632,128.459764 127.551861,128.459764 C116.72509,128.459764 107.948257,137.236596 107.948257,148.063368 C107.948257,158.890139 116.72509,167.666971 127.551861,167.666971 L127.551861,167.666971 Z\" fill=\"#FFFFFF\"></path></g></svg>"

/***/ }),
/* 1234 */,
/* 1235 */,
/* 1236 */,
/* 1237 */,
/* 1238 */,
/* 1239 */,
/* 1240 */,
/* 1241 */,
/* 1242 */,
/* 1243 */,
/* 1244 */,
/* 1245 */,
/* 1246 */,
/* 1247 */,
/* 1248 */,
/* 1249 */,
/* 1250 */,
/* 1251 */,
/* 1252 */,
/* 1253 */,
/* 1254 */,
/* 1255 */,
/* 1256 */,
/* 1257 */,
/* 1258 */,
/* 1259 */,
/* 1260 */,
/* 1261 */,
/* 1262 */,
/* 1263 */,
/* 1264 */,
/* 1265 */,
/* 1266 */,
/* 1267 */,
/* 1268 */,
/* 1269 */,
/* 1270 */,
/* 1271 */,
/* 1272 */,
/* 1273 */,
/* 1274 */,
/* 1275 */,
/* 1276 */,
/* 1277 */,
/* 1278 */,
/* 1279 */,
/* 1280 */,
/* 1281 */,
/* 1282 */,
/* 1283 */,
/* 1284 */,
/* 1285 */,
/* 1286 */,
/* 1287 */,
/* 1288 */,
/* 1289 */,
/* 1290 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\" viewBox=\"0 0 512 512\" enable-background=\"new 0 0 512 512\" xml:space=\"preserve\"><g id=\"Layer_2\"><path fill=\"#132028\" d=\"M190.475,376V203.308H81.266c-27.129,0-50.139,26-58.222,62.035c-9.877,1.302-17.62,13.541-17.62,28.455 c0,14.914,7.743,27.153,17.62,28.455c7.291,32.507,26.733,56.84,50.385,61.294v68.669h395.95V376H190.475z\"></path></g><g id=\"Layer_3\"><path fill=\"#575A5B\" d=\"M490.712,427.977c0,30.941-25.082,56.023-56.023,56.023c-25.804,0-47.519-17.451-54.023-41.19 c-6.504,23.739-28.219,41.19-54.023,41.19c-30.941,0-56.023-25.082-56.023-56.023c0-30.941,25.082-56.023,56.023-56.023 c25.804,0,47.519,17.451,54.023,41.19c6.504-23.739,28.219-41.19,54.023-41.19C465.629,371.954,490.712,397.036,490.712,427.977z M161.24,203.308l29.75-113.845H94.232l29.75,113.845h-13.668c-33.865,0-61.319,40.513-61.319,90.489 c0,49.976,27.453,90.489,61.319,90.489H294.27V203.308H161.24z M162.321,420.431c-13.164,0-24.458,8.002-29.285,19.408 c-4.826-11.405-16.121-19.408-29.284-19.408c-17.554,0-31.785,14.23-31.785,31.784c0,17.554,14.23,31.785,31.785,31.785 c13.164,0,24.458-8.002,29.284-19.408C137.863,475.998,149.157,484,162.321,484c17.554,0,31.784-14.23,31.784-31.785 C194.106,434.661,179.875,420.431,162.321,420.431z\"></path></g><g id=\"Layer_4\"><path fill=\"#FFB636\" d=\"M200.78,384.287h-16.028c-20.105,0-36.403-40.513-36.403-90.489c0-49.976,16.298-90.489,36.403-90.489 h16.028c-20.105,0-36.403,40.513-36.403,90.489C164.378,343.773,180.676,384.287,200.78,384.287z M122.236,293.797 c0-49.976,16.298-90.489,36.403-90.489H142.61c-20.105,0-36.403,40.513-36.403,90.489c0,49.976,16.298,90.489,36.403,90.489h16.028 C138.534,384.287,122.236,343.773,122.236,293.797z\"></path></g><g id=\"Layer_5\"><path fill=\"#FF473E\" d=\"M489.353,384.287H294.27V85.761h195.083V384.287z M74.21,384.103h-7.99c-2.436,0-4.707,1.48-6.035,3.934 l-43.633,80.594C13.436,474.387,16.874,482,22.587,482h52.826c4.027,0,7.271-3.968,7.199-8.806l-1.203-80.594 C81.339,387.883,78.136,384.103,74.21,384.103z\"></path></g><g id=\"Layer_6\"><path fill=\"#EF2020\" d=\"M497.28,66.397H286.342c-5.92,0-10.72,4.8-10.72,10.72v1.626c0,5.92,4.8,10.72,10.72,10.72H497.28 c5.921,0,10.72-4.8,10.72-10.72v-1.626C508,71.197,503.201,66.397,497.28,66.397z\"></path></g><g id=\"Layer_7\"><path fill=\"#76DFFF\" d=\"M371.466,257.523h-40.952c-3.976,0-7.2-3.224-7.2-7.2V124.018c0-3.976,3.224-7.2,7.2-7.2h40.952 c3.976,0,7.2,3.224,7.2,7.2v126.305C378.666,254.3,375.442,257.523,371.466,257.523z M460.474,250.323V124.018 c0-3.976-3.224-7.2-7.2-7.2h-40.952c-3.976,0-7.2,3.224-7.2,7.2v126.305c0,3.976,3.224,7.2,7.2,7.2h40.952 C457.251,257.523,460.474,254.3,460.474,250.323z\"></path></g><g id=\"Layer_8\"><path fill=\"#132028\" d=\"M489.353,339.586H294.27v-36.582h195.083V339.586z M111.17,52L94.206,89.463h96.758L174,52H111.17z M326.643,414.954c-7.192,0-13.023,5.831-13.023,13.023S319.451,441,326.643,441c7.192,0,13.023-5.831,13.023-13.023 S333.835,414.954,326.643,414.954z M434.689,414.954c-7.192,0-13.023,5.831-13.023,13.023S427.496,441,434.689,441 s13.023-5.831,13.023-13.023S441.881,414.954,434.689,414.954z M103.752,444.827c-4.081,0-7.389,3.308-7.389,7.389 s3.308,7.389,7.389,7.389c4.081,0,7.389-3.308,7.389-7.389S107.833,444.827,103.752,444.827z M162.321,444.827 c-4.081,0-7.389,3.308-7.389,7.389s3.308,7.389,7.389,7.389s7.389-3.308,7.389-7.389S166.402,444.827,162.321,444.827z\"></path></g><g id=\"Layer_9\"><path fill=\"#FFB636\" d=\"M196.709,444.208h-92.957c-4.91,0-8.891-3.98-8.891-8.891s3.98-8.891,8.891-8.891h90.449l36.759-22.53 c1.398-0.857,3.006-1.311,4.646-1.311H459.54c4.91,0,8.891,3.98,8.891,8.891s-3.98,8.891-8.891,8.891H238.114l-36.759,22.53 C199.957,443.755,198.349,444.208,196.709,444.208z\"></path></g><g id=\"Layer_10\"><path fill=\"#ADB7BC\" d=\"M69.849,393.079c-5.787,0-10.485-4.685-10.5-10.475c-0.014-5.799,4.676-10.512,10.475-10.525l413.924-1 c0.009,0,0.018,0,0.026,0c5.787,0,10.485,4.685,10.499,10.475c0.014,5.799-4.676,10.512-10.475,10.525l-413.924,1 C69.867,393.079,69.857,393.079,69.849,393.079z\"></path></g></svg>"

/***/ }),
/* 1291 */,
/* 1292 */,
/* 1293 */,
/* 1294 */,
/* 1295 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1296 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1297 */,
/* 1298 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1299 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1300 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1301 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1302 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1303 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1304 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1305 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1306 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1307 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1308 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1309 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1310 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1311 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1312 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1313 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1314 */,
/* 1315 */,
/* 1316 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1317 */,
/* 1318 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1319 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1320 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1321 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1322 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1323 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1324 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1325 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1326 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1327 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1328 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1329 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1330 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1331 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1332 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1333 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1334 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1335 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1336 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1337 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1338 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1339 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1340 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1341 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1342 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1343 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1344 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1345 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1346 */,
/* 1347 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 16 16\" fill=\"#D92215\"><path d=\"M8 14.5c-3.6 0-6.5-2.9-6.5-6.5S4.4 1.5 8 1.5s6.5 2.9 6.5 6.5-2.9 6.5-6.5 6.5zm0-12C5 2.5 2.5 5 2.5 8S5 13.5 8 13.5 13.5 11 13.5 8 11 2.5 8 2.5z\"></path><circle cx=\"5\" cy=\"6\" r=\"1\" transform=\"translate(1 1)\"></circle><circle cx=\"9\" cy=\"6\" r=\"1\" transform=\"translate(1 1)\"></circle><path d=\"M5.5 11c-.1 0-.2 0-.3-.1-.2-.1-.3-.4-.1-.7C6 9 7 8.5 8.1 8.5c1.7.1 2.8 1.7 2.8 1.8.2.2.1.5-.1.7-.2.1-.6 0-.7-.2 0 0-.9-1.3-2-1.3-.7 0-1.4.4-2.1 1.3-.2.2-.4.2-.5.2z\"></path></svg>"

/***/ }),
/* 1348 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"context-fill\"><path d=\"M13.917 7C13.44 4.162 10.973 2 8 2 4.686 2 2 4.686 2 8s2.686 6 6 6c2.22 0 4.16-1.207 5.197-3H12c-.912 1.214-2.364 2-4 2-2.76 0-5-2.24-5-5s2.24-5 5-5c2.42 0 4.437 1.718 4.9 4h1.017z\"></path><path d=\"M14 1L8 7h6V1zm-1 1L9 6h4V2z\" fill-rule=\"evenodd\"></path></svg>"

/***/ }),
/* 1349 */,
/* 1350 */,
/* 1351 */,
/* 1352 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _expressions = __webpack_require__(1417);

Object.keys(_expressions).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  Object.defineProperty(exports, key, {
    enumerable: true,
    get: function () {
      return _expressions[key];
    }
  });
});

var _sources = __webpack_require__(1369);

Object.keys(_sources).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  Object.defineProperty(exports, key, {
    enumerable: true,
    get: function () {
      return _sources[key];
    }
  });
});

var _pause = __webpack_require__(1394);

Object.keys(_pause).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  Object.defineProperty(exports, key, {
    enumerable: true,
    get: function () {
      return _pause[key];
    }
  });
});

var _debuggee = __webpack_require__(1418);

Object.keys(_debuggee).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  Object.defineProperty(exports, key, {
    enumerable: true,
    get: function () {
      return _debuggee[key];
    }
  });
});

var _breakpoints = __webpack_require__(1378);

Object.keys(_breakpoints).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  Object.defineProperty(exports, key, {
    enumerable: true,
    get: function () {
      return _breakpoints[key];
    }
  });
});

var _pendingBreakpoints = __webpack_require__(1419);

Object.keys(_pendingBreakpoints).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  Object.defineProperty(exports, key, {
    enumerable: true,
    get: function () {
      return _pendingBreakpoints[key];
    }
  });
});

var _ui = __webpack_require__(1421);

Object.keys(_ui).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  Object.defineProperty(exports, key, {
    enumerable: true,
    get: function () {
      return _ui[key];
    }
  });
});

var _fileSearch = __webpack_require__(1422);

Object.keys(_fileSearch).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  Object.defineProperty(exports, key, {
    enumerable: true,
    get: function () {
      return _fileSearch[key];
    }
  });
});

var _ast = __webpack_require__(1383);

Object.keys(_ast).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  Object.defineProperty(exports, key, {
    enumerable: true,
    get: function () {
      return _ast[key];
    }
  });
});

var _coverage = __webpack_require__(1423);

Object.keys(_coverage).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  Object.defineProperty(exports, key, {
    enumerable: true,
    get: function () {
      return _coverage[key];
    }
  });
});

var _projectTextSearch = __webpack_require__(1424);

Object.keys(_projectTextSearch).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  Object.defineProperty(exports, key, {
    enumerable: true,
    get: function () {
      return _projectTextSearch[key];
    }
  });
});

var _replay = __webpack_require__(2342);

Object.keys(_replay).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  Object.defineProperty(exports, key, {
    enumerable: true,
    get: function () {
      return _replay[key];
    }
  });
});

var _sourceTree = __webpack_require__(1426);

Object.keys(_sourceTree).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  Object.defineProperty(exports, key, {
    enumerable: true,
    get: function () {
      return _sourceTree[key];
    }
  });
});

var _eventListeners = __webpack_require__(1420);

Object.defineProperty(exports, "getEventListeners", {
  enumerable: true,
  get: function () {
    return _eventListeners.getEventListeners;
  }
});

var _quickOpen = __webpack_require__(1635);

Object.defineProperty(exports, "getQuickOpenEnabled", {
  enumerable: true,
  get: function () {
    return _quickOpen.getQuickOpenEnabled;
  }
});
Object.defineProperty(exports, "getQuickOpenQuery", {
  enumerable: true,
  get: function () {
    return _quickOpen.getQuickOpenQuery;
  }
});
Object.defineProperty(exports, "getQuickOpenType", {
  enumerable: true,
  get: function () {
    return _quickOpen.getQuickOpenType;
  }
});

var _breakpointAtLocation = __webpack_require__(1503);

Object.defineProperty(exports, "getBreakpointAtLocation", {
  enumerable: true,
  get: function () {
    return _breakpointAtLocation.getBreakpointAtLocation;
  }
});

var _visibleBreakpoints = __webpack_require__(1427);

Object.defineProperty(exports, "getVisibleBreakpoints", {
  enumerable: true,
  get: function () {
    return _visibleBreakpoints.getVisibleBreakpoints;
  }
});

var _isSelectedFrameVisible = __webpack_require__(1505);

Object.defineProperty(exports, "isSelectedFrameVisible", {
  enumerable: true,
  get: function () {
    return _isSelectedFrameVisible.isSelectedFrameVisible;
  }
});

var _getCallStackFrames = __webpack_require__(1779);

Object.defineProperty(exports, "getCallStackFrames", {
  enumerable: true,
  get: function () {
    return _getCallStackFrames.getCallStackFrames;
  }
});

var _visibleSelectedFrame = __webpack_require__(1780);

Object.defineProperty(exports, "getVisibleSelectedFrame", {
  enumerable: true,
  get: function () {
    return _visibleSelectedFrame.getVisibleSelectedFrame;
  }
});

/***/ }),
/* 1353 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Dependencies
const validProtocols = /^(http|https|ftp|javascript|resource|chrome):/i;
const tokenSplitRegex = /(\s|\'|\"|\\)+/;
const ELLIPSIS = "\u2026";
const dom = __webpack_require__(1758);
const { span } = dom;

/**
 * Returns true if the given object is a grip (see RDP protocol)
 */
function isGrip(object) {
  return object && object.actor;
}

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: "\\\\"
};

// Regexp that matches any character we might possibly want to escape.
// 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 escapeRegexp = new RegExp("[" +
// Quote and 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" + "]", "g");

/**
 * Escape a string so that the result is viewable and valid JS.
 * Control characters, other invisibles, invalid characters,
 * backslash, and double quotes are escaped.  The resulting string is
 * surrounded by double quotes.
 *
 * @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) {
  return "\"" + str.replace(escapeRegexp, (match, offset) => {
    let 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;
      }
      let 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);
  }) + "\"";
}

/**
 * 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;
  }

  let 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
  let 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) {
  let params = [];

  // In case the text is empty just return the empty parameters
  if (text == "") {
    return params;
  }

  let searchParams = new URLSearchParams(text);
  let entries = [...searchParams.entries()];
  return entries.map(entry => {
    return {
      name: entry[0],
      value: entry[1]
    };
  });
}

function getFileName(url) {
  let 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}([^\/]*)(.*?)([^\/]*?)($|\?.*)/;
  let 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 [];
  }

  // Promise resolved value Grip
  if (grip.promiseState && grip.promiseState.value) {
    return [grip.promiseState.value];
  }

  // 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) {
    let keys = Object.keys(grip.preview.properties);
    let 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);

    let 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) {
  if (typeof grip !== "string") {
    return false;
  }

  let tokens = grip.split(tokenSplitRegex);
  return tokens.some(isURL);
}

/**
 * Determines whether a string token is a valid URL.
 *
 * @param string token
 *        The token.
 * @return boolean
 *         Whenther the token is a URL.
 */
function isURL(token) {
  try {
    if (!validProtocols.test(token)) {
      return false;
    }
    new URL(token);
    return true;
  } catch (e) {
    return false;
  }
}

const ellipsisElement = span({
  key: "more",
  className: "more-ellipsis",
  title: `more${ELLIPSIS}`
}, ELLIPSIS);

module.exports = {
  isGrip,
  isURL,
  cropString,
  containsURL,
  rawCropString,
  sanitizeString,
  escapeString,
  wrapRender,
  cropMultipleLines,
  parseURLParams,
  parseURLEncodedText,
  getFileName,
  getURLDisplayString,
  maybeEscapePropertyName,
  getGripPreviewItems,
  getGripType,
  tokenSplitRegex,
  ellipsisElement,
  ELLIPSIS
};

/***/ }),
/* 1354 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

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; }; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                                                                                                                                                                                   * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                                                                                                                                                                                                                                   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

var _breakpoints = __webpack_require__(1396);

var breakpoints = _interopRequireWildcard(_breakpoints);

var _expressions = __webpack_require__(1398);

var expressions = _interopRequireWildcard(_expressions);

var _eventListeners = __webpack_require__(1527);

var eventListeners = _interopRequireWildcard(_eventListeners);

var _pause = __webpack_require__(1639);

var pause = _interopRequireWildcard(_pause);

var _navigation = __webpack_require__(1529);

var navigation = _interopRequireWildcard(_navigation);

var _ui = __webpack_require__(1385);

var ui = _interopRequireWildcard(_ui);

var _fileSearch = __webpack_require__(1530);

var fileSearch = _interopRequireWildcard(_fileSearch);

var _ast = __webpack_require__(1399);

var ast = _interopRequireWildcard(_ast);

var _coverage = __webpack_require__(1531);

var coverage = _interopRequireWildcard(_coverage);

var _projectTextSearch = __webpack_require__(1433);

var projectTextSearch = _interopRequireWildcard(_projectTextSearch);

var _replay = __webpack_require__(2343);

var replay = _interopRequireWildcard(_replay);

var _quickOpen = __webpack_require__(1647);

var quickOpen = _interopRequireWildcard(_quickOpen);

var _sourceTree = __webpack_require__(1532);

var sourceTree = _interopRequireWildcard(_sourceTree);

var _sources = __webpack_require__(1797);

var sources = _interopRequireWildcard(_sources);

var _debuggee = __webpack_require__(1533);

var debuggee = _interopRequireWildcard(_debuggee);

var _toolbox = __webpack_require__(1534);

var toolbox = _interopRequireWildcard(_toolbox);

var _preview = __webpack_require__(1786);

var preview = _interopRequireWildcard(_preview);

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

exports.default = _extends({}, navigation, breakpoints, expressions, eventListeners, sources, pause, ui, fileSearch, ast, coverage, projectTextSearch, replay, quickOpen, sourceTree, debuggee, toolbox, preview);

/***/ }),
/* 1355 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


const feature = __webpack_require__(1461);

module.exports = feature;

/***/ }),
/* 1356 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.isMinified = undefined;

var _isMinified = __webpack_require__(1778);

Object.defineProperty(exports, "isMinified", {
  enumerable: true,
  get: function () {
    return _isMinified.isMinified;
  }
});
exports.shouldPrettyPrint = shouldPrettyPrint;
exports.isJavaScript = isJavaScript;
exports.isPretty = isPretty;
exports.isPrettyURL = isPrettyURL;
exports.isThirdParty = isThirdParty;
exports.getPrettySourceURL = getPrettySourceURL;
exports.getRawSourceURL = getRawSourceURL;
exports.getFilenameFromURL = getFilenameFromURL;
exports.getFormattedSourceId = getFormattedSourceId;
exports.getFilename = getFilename;
exports.getFileURL = getFileURL;
exports.getSourcePath = getSourcePath;
exports.getSourceLineCount = getSourceLineCount;
exports.getMode = getMode;
exports.isLoaded = isLoaded;
exports.isLoading = isLoading;

var _devtoolsSourceMap = __webpack_require__(1360);

var _utils = __webpack_require__(1366);

var _path = __webpack_require__(1393);

var _url = __webpack_require__(334);

/**
 * Trims the query part or reference identifier of a url string, if necessary.
 *
 * @memberof utils/source
 * @static
 */
function trimUrlQuery(url) {
  const length = url.length;
  const q1 = url.indexOf("?");
  const q2 = url.indexOf("&");
  const q3 = url.indexOf("#");
  const q = Math.min(q1 != -1 ? q1 : length, q2 != -1 ? q2 : length, q3 != -1 ? q3 : length);

  return url.slice(0, q);
}

function shouldPrettyPrint(source) {
  if (!source) {
    return false;
  }
  const _isPretty = isPretty(source);
  const _isJavaScript = isJavaScript(source);
  const isOriginal = (0, _devtoolsSourceMap.isOriginalId)(source.get("id"));
  const hasSourceMap = source.get("sourceMapURL");

  if (_isPretty || isOriginal || hasSourceMap || !_isJavaScript) {
    return false;
  }

  return true;
}

/**
 * Returns true if the specified url and/or content type are specific to
 * javascript files.
 *
 * @return boolean
 *         True if the source is likely javascript.
 *
 * @memberof utils/source
 * @static
 */
function isJavaScript(source) {
  const url = source.get("url");
  const contentType = source.get("contentType");
  return url && /\.(jsm|js)?$/.test(trimUrlQuery(url)) || !!(contentType && contentType.includes("javascript"));
}

/**
 * @memberof utils/source
 * @static
 */
function isPretty(source) {
  const url = source.get("url");
  return isPrettyURL(url);
}

function isPrettyURL(url) {
  return url ? /formatted$/.test(url) : false;
}

function isThirdParty(source) {
  const url = source.get("url");
  if (!source || !url) {
    return false;
  }

  return !!url.match(/(node_modules|bower_components)/);
}

/**
 * @memberof utils/source
 * @static
 */
function getPrettySourceURL(url) {
  if (!url) {
    url = "";
  }
  return `${url}:formatted`;
}

/**
 * @memberof utils/source
 * @static
 */
function getRawSourceURL(url) {
  return url ? url.replace(/:formatted$/, "") : url;
}

function resolveFileURL(url, transformUrl = initialUrl => initialUrl) {
  url = getRawSourceURL(url || "");
  const name = transformUrl(url);
  return (0, _utils.endTruncateStr)(name, 50);
}

function getFilenameFromURL(url) {
  return resolveFileURL(url, initialUrl => (0, _path.basename)(initialUrl) || "(index)");
}

function getFormattedSourceId(id) {
  const sourceId = id.split("/")[1];
  return `SOURCE${sourceId}`;
}

/**
 * Show a source url's filename.
 * If the source does not have a url, use the source id.
 *
 * @memberof utils/source
 * @static
 */
function getFilename(source) {
  const { url, id } = source;
  if (!url) {
    return getFormattedSourceId(id);
  }

  let filename = getFilenameFromURL(url);
  const qMarkIdx = filename.indexOf("?");
  if (qMarkIdx > 0) {
    filename = filename.slice(0, qMarkIdx);
  }
  return filename;
}

/**
 * Show a source url.
 * If the source does not have a url, use the source id.
 *
 * @memberof utils/source
 * @static
 */
function getFileURL(source) {
  const { url, id } = source;
  if (!url) {
    return getFormattedSourceId(id);
  }

  return resolveFileURL(url);
}

const contentTypeModeMap = {
  "text/javascript": { name: "javascript" },
  "text/typescript": { name: "javascript", typescript: true },
  "text/coffeescript": { name: "coffeescript" },
  "text/typescript-jsx": {
    name: "jsx",
    base: { name: "javascript", typescript: true }
  },
  "text/jsx": { name: "jsx" },
  "text/x-elm": { name: "elm" },
  "text/x-clojure": { name: "clojure" },
  "text/wasm": { name: "text" },
  "text/html": { name: "htmlmixed" }
};

function getSourcePath(url) {
  if (!url) {
    return "";
  }

  const { path, href } = (0, _url.parse)(url);
  // for URLs like "about:home" the path is null so we pass the full href
  return path || href;
}

/**
 * Returns amount of lines in the source. If source is a WebAssembly binary,
 * the function returns amount of bytes.
 */
function getSourceLineCount(source) {
  if (source.isWasm && !source.error) {
    const { binary } = source.text;
    return binary.length;
  }
  return source.text != undefined ? source.text.split("\n").length : 0;
}

/**
 *
 * Checks if a source is minified based on some heuristics
 * @param key
 * @param text
 * @return boolean
 * @memberof utils/source
 * @static
 */

/**
 *
 * Returns Code Mirror mode for source content type
 * @param contentType
 * @return String
 * @memberof utils/source
 * @static
 */

function getMode(source, symbols) {
  const { contentType, text, isWasm, url } = source;

  if (!text || isWasm) {
    return { name: "text" };
  }

  if (url && url.match(/\.jsx$/i) || symbols && symbols.hasJsx) {
    if (symbols && symbols.hasTypes) {
      return { name: "text/typescript-jsx" };
    }
    return { name: "jsx" };
  }

  if (symbols && symbols.hasTypes) {
    if (symbols.hasJsx) {
      return { name: "text/typescript-jsx" };
    }

    return { name: "text/typescript" };
  }

  const languageMimeMap = [{ ext: ".c", mode: "text/x-csrc" }, { ext: ".kt", mode: "text/x-kotlin" }, { ext: ".cpp", mode: "text/x-c++src" }, { ext: ".m", mode: "text/x-objectivec" }, { ext: ".rs", mode: "text/x-rustsrc" }];

  // check for C and other non JS languages
  if (url) {
    const result = languageMimeMap.find(({ ext }) => url.endsWith(ext));

    if (result !== undefined) {
      return { name: result.mode };
    }
  }

  // if the url ends with .marko we set the name to Javascript so
  // syntax highlighting works for marko too
  if (url && url.match(/\.marko$/i)) {
    return { name: "javascript" };
  }

  // Use HTML mode for files in which the first non whitespace
  // character is `<` regardless of extension.
  const isHTMLLike = text.match(/^\s*</);
  if (!contentType) {
    if (isHTMLLike) {
      return { name: "htmlmixed" };
    }
    return { name: "text" };
  }

  // //  or /*  */
  if (text.match(/^\s*(\/\/ @flow|\/\* @flow \*\/)/)) {
    return contentTypeModeMap["text/typescript"];
  }

  if (/script|elm|jsx|clojure|wasm|html/.test(contentType)) {
    if (contentType in contentTypeModeMap) {
      return contentTypeModeMap[contentType];
    }

    return contentTypeModeMap["text/javascript"];
  }

  if (isHTMLLike) {
    return { name: "htmlmixed" };
  }

  return { name: "text" };
}

function isLoaded(source) {
  return source.get("loadedState") === "loaded";
}

function isLoading(source) {
  return source.get("loadedState") === "loading";
}

/***/ }),
/* 1357 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

module.exports = {
  MODE: {
    TINY: Symbol("TINY"),
    SHORT: Symbol("SHORT"),
    LONG: Symbol("LONG")
  }
};

/***/ }),
/* 1358 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

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; }; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                                                                                                                                                                                   * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                                                                                                                                                                                                                                   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

var _sourceDocuments = __webpack_require__(1436);

Object.keys(_sourceDocuments).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  Object.defineProperty(exports, key, {
    enumerable: true,
    get: function () {
      return _sourceDocuments[key];
    }
  });
});

var _getTokenLocation = __webpack_require__(2244);

Object.keys(_getTokenLocation).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  Object.defineProperty(exports, key, {
    enumerable: true,
    get: function () {
      return _getTokenLocation[key];
    }
  });
});

var _sourceSearch = __webpack_require__(1526);

Object.keys(_sourceSearch).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  Object.defineProperty(exports, key, {
    enumerable: true,
    get: function () {
      return _sourceSearch[key];
    }
  });
});

var _ui = __webpack_require__(1439);

Object.keys(_ui).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  Object.defineProperty(exports, key, {
    enumerable: true,
    get: function () {
      return _ui[key];
    }
  });
});

var _createEditor = __webpack_require__(1806);

Object.keys(_createEditor).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  Object.defineProperty(exports, key, {
    enumerable: true,
    get: function () {
      return _createEditor[key];
    }
  });
});
exports.shouldShowPrettyPrint = shouldShowPrettyPrint;
exports.shouldShowFooter = shouldShowFooter;
exports.traverseResults = traverseResults;
exports.toEditorLine = toEditorLine;
exports.toEditorPosition = toEditorPosition;
exports.toEditorRange = toEditorRange;
exports.toSourceLine = toSourceLine;
exports.scrollToColumn = scrollToColumn;
exports.toSourceLocation = toSourceLocation;
exports.markText = markText;
exports.lineAtHeight = lineAtHeight;
exports.getSourceLocationFromMouseEvent = getSourceLocationFromMouseEvent;
exports.forEachLine = forEachLine;
exports.removeLineClass = removeLineClass;
exports.clearLineClass = clearLineClass;
exports.getTextForLine = getTextForLine;
exports.getCursorLine = getCursorLine;

var _source = __webpack_require__(1356);

var _wasm = __webpack_require__(1401);

var _devtoolsSourceMap = __webpack_require__(1360);

function shouldShowPrettyPrint(selectedSource) {
  if (!selectedSource) {
    return false;
  }

  return (0, _source.shouldPrettyPrint)(selectedSource);
}

function shouldShowFooter(selectedSource, horizontal) {
  if (!horizontal) {
    return true;
  }
  if (!selectedSource) {
    return false;
  }
  return shouldShowPrettyPrint(selectedSource) || (0, _devtoolsSourceMap.isOriginalId)(selectedSource.get("id"));
}

function traverseResults(e, ctx, query, dir, modifiers) {
  e.stopPropagation();
  e.preventDefault();

  if (dir == "prev") {
    (0, _sourceSearch.findPrev)(ctx, query, true, modifiers);
  } else if (dir == "next") {
    (0, _sourceSearch.findNext)(ctx, query, true, modifiers);
  }
}

function toEditorLine(sourceId, lineOrOffset) {
  if ((0, _wasm.isWasm)(sourceId)) {
    // TODO ensure offset is always "mappable" to edit line.
    return (0, _wasm.wasmOffsetToLine)(sourceId, lineOrOffset) || 0;
  }

  return lineOrOffset ? lineOrOffset - 1 : 1;
}

function toEditorPosition(location) {
  return {
    line: toEditorLine(location.sourceId, location.line),
    column: (0, _wasm.isWasm)(location.sourceId) || !location.column ? 0 : location.column
  };
}

function toEditorRange(sourceId, location) {
  const { start, end } = location;
  return {
    start: toEditorPosition(_extends({}, start, { sourceId })),
    end: toEditorPosition(_extends({}, end, { sourceId }))
  };
}

function toSourceLine(sourceId, line) {
  return (0, _wasm.isWasm)(sourceId) ? (0, _wasm.lineToWasmOffset)(sourceId, line) : line + 1;
}

function scrollToColumn(codeMirror, line, column) {
  const { top, left } = codeMirror.charCoords({ line: line, ch: column }, "local");

  if (!isVisible(codeMirror, top, left)) {
    const scroller = codeMirror.getScrollerElement();
    const centeredX = Math.max(left - scroller.offsetWidth / 2, 0);
    const centeredY = Math.max(top - scroller.offsetHeight / 2, 0);

    codeMirror.scrollTo(centeredX, centeredY);
  }
}

function isVisible(codeMirror, top, left) {
  function withinBounds(x, min, max) {
    return x >= min && x <= max;
  }

  const scrollArea = codeMirror.getScrollInfo();

  const charWidth = codeMirror.defaultCharWidth();
  const inXView = withinBounds(left, scrollArea.left, scrollArea.left + (scrollArea.clientWidth - 30) - charWidth);

  const fontHeight = codeMirror.defaultTextHeight();
  const inYView = withinBounds(top, scrollArea.top, scrollArea.top + scrollArea.clientHeight - fontHeight);

  return inXView && inYView;
}

function toSourceLocation(sourceId, location) {
  return {
    line: toSourceLine(sourceId, location.line),
    column: (0, _wasm.isWasm)(sourceId) ? undefined : location.column
  };
}

function markText(editor, className, { start, end }) {
  return editor.codeMirror.markText({ ch: start.column, line: start.line }, { ch: end.column, line: end.line }, { className });
}

function lineAtHeight(editor, sourceId, event) {
  const editorLine = editor.codeMirror.lineAtHeight(event.clientY);
  return toSourceLine(sourceId, editorLine);
}

function getSourceLocationFromMouseEvent(editor, selectedLocation, e) {
  const { line, ch } = editor.codeMirror.coordsChar({
    left: e.clientX,
    top: e.clientY
  });

  return {
    sourceId: selectedLocation.sourceId,
    line: line + 1,
    column: ch + 1
  };
}

function forEachLine(codeMirror, iter) {
  codeMirror.operation(() => {
    codeMirror.doc.iter(0, codeMirror.lineCount(), iter);
  });
}

function removeLineClass(codeMirror, line, className) {
  codeMirror.removeLineClass(line, "line", className);
}

function clearLineClass(codeMirror, className) {
  forEachLine(codeMirror, line => {
    removeLineClass(codeMirror, line, className);
  });
}

function getTextForLine(codeMirror, line) {
  return codeMirror.getLine(line - 1).trim();
}

function getCursorLine(codeMirror) {
  return codeMirror.getCursor().line;
}

/***/ }),
/* 1359 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _Svg = __webpack_require__(1540);

var _Svg2 = _interopRequireDefault(_Svg);

__webpack_require__(1310);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 file maps the SVG React Components in the assets/images directory.
 */

exports.default = _Svg2.default;

/***/ }),
/* 1360 */
/***/ (function(module, exports, __webpack_require__) {

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 {
  originalToGeneratedId,
  generatedToOriginalId,
  isGeneratedId,
  isOriginalId
} = __webpack_require__(1389);

const { workerUtils: { WorkerDispatcher } } = __webpack_require__(1363);

const dispatcher = new WorkerDispatcher();

const getOriginalURLs = dispatcher.task("getOriginalURLs");
const getGeneratedLocation = dispatcher.task("getGeneratedLocation");
const getOriginalLocation = dispatcher.task("getOriginalLocation");
const getLocationScopes = dispatcher.task("getLocationScopes");
const getOriginalSourceText = dispatcher.task("getOriginalSourceText");
const applySourceMap = dispatcher.task("applySourceMap");
const clearSourceMaps = dispatcher.task("clearSourceMaps");
const hasMappedSource = dispatcher.task("hasMappedSource");

module.exports = {
  originalToGeneratedId,
  generatedToOriginalId,
  isGeneratedId,
  isOriginalId,
  hasMappedSource,
  getOriginalURLs,
  getGeneratedLocation,
  getOriginalLocation,
  getLocationScopes,
  getOriginalSourceText,
  applySourceMap,
  clearSourceMaps,
  startSourceMapWorker: dispatcher.start.bind(dispatcher),
  stopSourceMapWorker: dispatcher.stop.bind(dispatcher)
};

/***/ }),
/* 1361 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _immutable = __webpack_require__(146);

var I = _interopRequireWildcard(_immutable);

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

/**
 * Make an immutable record type
 *
 * @param spec - the keys and their default values
 * @return a state record factory function
 * @memberof utils/makeRecord
 * @static
 */


/**
 * @memberof utils/makeRecord
 * @static
 */
function makeRecord(spec) {
  return I.Record(spec);
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/**
 * When Flow 0.29 is released (very soon), we can use this Record type
 * instead of the builtin immutable.js Record type. This is better
 * because all the fields are actually typed, unlike the builtin one.
 * This depends on a performance fix that will go out in 0.29 though;
 * @module utils/makeRecord
 */

exports.default = makeRecord;

/***/ }),
/* 1362 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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, document, DebuggerConfig */

const { bindActionCreators, combineReducers } = __webpack_require__(3);
const { Provider } = __webpack_require__(1189);

const { defer } = __webpack_require__(1412);
const { debugGlobal } = __webpack_require__(1460);
const { setConfig, getValue, isDevelopment } = __webpack_require__(1355);
const L10N = __webpack_require__(1462);
const { showMenu, buildMenu } = __webpack_require__(1413);

setConfig({"environment":"firefox-panel","logging":false,"clientLogging":false,"firefox":{"mcPath":"./firefox"},"workers":{"parserURL":"resource://devtools/client/debugger/new/parser-worker.js","prettyPrintURL":"resource://devtools/client/debugger/new/pretty-print-worker.js","searchURL":"resource://devtools/client/debugger/new/search-worker.js"},"features":{}});

// Set various flags before requiring app code.
if (getValue("logging.client")) {
  // DevToolsUtils.dumpn.wantLogging = true;
}

const { firefox, chrome, startDebugging } = __webpack_require__(1470);
const Root = __webpack_require__(1476);

// Using this static variable allows webpack to know at compile-time
// to avoid this require and not include it at all in the output.
if (false) {
  require("devtools-mc-assets/assets/devtools/client/themes/light-theme.css");
  require("devtools-mc-assets/assets/devtools/client/themes/dark-theme.css");
  require("devtools-mc-assets/assets/devtools/client/themes/firebug-theme.css");
}

function updateTheme(className) {
  if (false) {
    const theme = getValue("theme");
    const root = document.body.parentNode;
    const appRoot = document.querySelector(".launchpad-root");

    root.className = "";
    appRoot.className = className;

    root.classList.add(`theme-${theme}`);
    appRoot.classList.add(`theme-${theme}`);
  }
}

function updatePlatform(className) {
  if (false) {
    const root = document.body.parentNode;
    const appRoot = document.querySelector(".launchpad-root");

    const agent = navigator.userAgent.toLowerCase();
    const win = agent.indexOf("windows") > -1 ? "win" : "linux";
    const platform = agent.indexOf("mac os") > -1 ? "mac" : win;

    root.classList.add("html");
    appRoot.setAttribute("platform", platform);
  }
}

function updateDir() {
  const dir = getValue("dir");
  const root = document.body.parentNode;
  root.dir = dir;
}

async function updateConfig() {
  const response = await fetch("/getconfig", {
    method: "get"
  });

  const config = await response.json();
  setConfig(config);
  return config;
}

async function initApp() {
  const configureStore = __webpack_require__(1477);
  const reducers = __webpack_require__(1486);
  const LaunchpadApp = __webpack_require__(1489);

  const createStore = configureStore({
    log: getValue("logging.actions"),
    makeThunkArgs: (args, state) => {
      return Object.assign({}, args, {});
    }
  });

  const store = createStore(combineReducers(reducers));
  const actions = bindActionCreators(__webpack_require__(1415), store.dispatch);

  debugGlobal("launchpadStore", store);

  if (isDevelopment()) {
    const config = await updateConfig();
    actions.setConfig(config);
    // AppConstants.DEBUG_JS_MODULES = true;
  }

  return { store, actions, LaunchpadApp };
}

function renderRoot(_React, _ReactDOM, component, _store, props) {
  const { createElement } = _React;
  const mount = document.querySelector("#mount");

  // bail in test environments that do not have a mount
  if (!mount) {
    return;
  }

  const className = "launchpad-root theme-body";
  const root = Root(className);
  mount.appendChild(root);

  if (isDevelopment()) {
    updateConfig();
    updateTheme(className);
    updatePlatform();
  }

  if (component.props || component.propTypes) {
    _ReactDOM.render(createElement(Provider, { store: _store }, createElement(component, props)), root);
  } else {
    root.appendChild(component);
  }
}

function unmountRoot(_ReactDOM) {
  const mount = document.querySelector("#mount .launchpad-root");
  _ReactDOM.unmountComponentAtNode(mount);
}

function getTargetFromQuery() {
  const href = window.location.href;
  const nodeMatch = href.match(/node-tab=([^&#]*)/);
  const firefoxMatch = href.match(/firefox-tab=([^&#]*)/);
  const chromeMatch = href.match(/chrome-tab=([^&#]*)/);

  if (nodeMatch) {
    return { type: "node", param: nodeMatch[1] };
  } else if (firefoxMatch) {
    return { type: "firefox", param: firefoxMatch[1] };
  } else if (chromeMatch) {
    return { type: "chrome", param: chromeMatch[1] };
  }

  return null;
}

async function connectClients(actions) {
  const firefoxTabs = await firefox.connectClient();
  actions.newTabs(firefoxTabs);

  chrome.connectClient().then(actions.newTabs);

  chrome.connectNodeClient().then(actions.newTabs);
}

async function getTabs(actions) {
  const firefoxTabs = await firefox.connectClient();
  const chromeTabs = await chrome.connectClient();
  const nodeTabs = await chrome.connectNodeClient();

  actions.clearTabs();

  actions.newTabs(firefoxTabs);
  actions.newTabs(chromeTabs);
  actions.newTabs(nodeTabs);
}

async function bootstrap(React, ReactDOM) {
  const connTarget = getTargetFromQuery();
  if (connTarget) {
    const { tab, tabConnection } = await startDebugging(connTarget);

    await updateConfig();
    return { tab, connTarget, tabConnection };
  }

  const { store, actions, LaunchpadApp } = await initApp();
  renderRoot(React, ReactDOM, LaunchpadApp, store);
  await connectClients(actions);
  setInterval(async () => await getTabs(actions), 3000);

  return undefined;
}

module.exports = {
  bootstrap,
  buildMenu,
  debugGlobal,
  defer,
  renderRoot,
  L10N,
  showMenu,
  unmountRoot,
  updateTheme,
  updateDir
};

/***/ }),
/* 1363 */
/***/ (function(module, exports, __webpack_require__) {

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 networkRequest = __webpack_require__(1367);
const workerUtils = __webpack_require__(1368);

module.exports = {
  networkRequest,
  workerUtils
};

/***/ }),
/* 1364 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.findScopeByName = exports.getASTLocation = undefined;

var _astBreakpointLocation = __webpack_require__(1416);

Object.defineProperty(exports, "getASTLocation", {
  enumerable: true,
  get: function () {
    return _astBreakpointLocation.getASTLocation;
  }
});
Object.defineProperty(exports, "findScopeByName", {
  enumerable: true,
  get: function () {
    return _astBreakpointLocation.findScopeByName;
  }
});
exports.firstString = firstString;
exports.locationMoved = locationMoved;
exports.makeLocationId = makeLocationId;
exports.makePendingLocationId = makePendingLocationId;
exports.assertBreakpoint = assertBreakpoint;
exports.assertPendingBreakpoint = assertPendingBreakpoint;
exports.assertLocation = assertLocation;
exports.assertPendingLocation = assertPendingLocation;
exports.breakpointAtLocation = breakpointAtLocation;
exports.breakpointExists = breakpointExists;
exports.createBreakpoint = createBreakpoint;
exports.createPendingBreakpoint = createPendingBreakpoint;

var _selectors = __webpack_require__(1352);

var _assert = __webpack_require__(1384);

var _assert2 = _interopRequireDefault(_assert);

var _prefs = __webpack_require__(226);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

// Return the first argument that is a string, or null if nothing is a
// string.
function firstString(...args) {
  for (const arg of args) {
    if (typeof arg === "string") {
      return arg;
    }
  }
  return null;
}

function locationMoved(location, newLocation) {
  return location.line !== newLocation.line || location.column !== newLocation.column;
}

function makeLocationId(location) {
  const { sourceId, line, column } = location;
  const columnString = column || "";
  return `${sourceId}:${line}:${columnString}`;
}

function makePendingLocationId(location) {
  assertPendingLocation(location);
  const { sourceUrl, line, column } = location;
  const sourceUrlString = sourceUrl || "";
  const columnString = column || "";

  return `${sourceUrlString}:${line}:${columnString}`;
}

function assertBreakpoint(breakpoint) {
  assertLocation(breakpoint.location);
  assertLocation(breakpoint.generatedLocation);
}

function assertPendingBreakpoint(pendingBreakpoint) {
  assertPendingLocation(pendingBreakpoint.location);
  assertPendingLocation(pendingBreakpoint.generatedLocation);
}

function assertLocation(location) {
  assertPendingLocation(location);
  const { sourceId } = location;
  (0, _assert2.default)(!!sourceId, "location must have a source id");
}

function assertPendingLocation(location) {
  (0, _assert2.default)(!!location, "location must exist");

  const { sourceUrl } = location;

  // sourceUrl is null when the source does not have a url
  (0, _assert2.default)(sourceUrl !== undefined, "location must have a source url");
  (0, _assert2.default)(location.hasOwnProperty("line"), "location must have a line");
  (0, _assert2.default)(location.hasOwnProperty("column") != null, "location must have a column");
}

// syncing
function breakpointAtLocation(breakpoints, { line, column }) {
  return breakpoints.find(breakpoint => {
    const sameLine = breakpoint.location.line === line;
    if (!sameLine) {
      return false;
    }

    // NOTE: when column breakpoints are disabled we want to find
    // the first breakpoint
    if (!_prefs.features.columnBreakpoints) {
      return true;
    }

    return breakpoint.location.column === column;
  });
}

function breakpointExists(state, location) {
  const currentBp = (0, _selectors.getBreakpoint)(state, location);
  return currentBp && !currentBp.disabled;
}

function createBreakpoint(location, overrides = {}) {
  const {
    condition,
    disabled,
    hidden,
    generatedLocation,
    astLocation,
    id
  } = overrides;

  const defaultASTLocation = { name: undefined, offset: location };
  const properties = {
    id,
    condition: condition || null,
    disabled: disabled || false,
    hidden: hidden || false,
    loading: false,
    text: "",
    astLocation: astLocation || defaultASTLocation,
    generatedLocation: generatedLocation || location,
    location
  };

  return properties;
}

function createPendingLocation(location) {
  const { sourceUrl, line, column } = location;
  return { sourceUrl, line, column };
}

function createPendingBreakpoint(bp) {
  const pendingLocation = createPendingLocation(bp.location);
  const pendingGeneratedLocation = createPendingLocation(bp.generatedLocation);

  assertPendingLocation(pendingLocation);

  return {
    condition: bp.condition,
    disabled: bp.disabled,
    location: pendingLocation,
    astLocation: bp.astLocation,
    generatedLocation: pendingGeneratedLocation
  };
}

/***/ }),
/* 1365 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.replaceOriginalVariableName = exports.getFramework = exports.hasSyntaxError = exports.clearSources = exports.setSource = exports.hasSource = exports.getEmptyLines = exports.isInvalidPauseLocation = exports.getNextStep = exports.clearASTs = exports.clearScopes = exports.clearSymbols = exports.findOutOfScopeLocations = exports.getScopes = exports.getSymbols = exports.getClosestExpression = exports.stopParserWorker = exports.startParserWorker = undefined;

var _devtoolsUtils = __webpack_require__(1363);

const { WorkerDispatcher } = _devtoolsUtils.workerUtils; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                          * License, v. 2.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 dispatcher = new WorkerDispatcher();
const startParserWorker = exports.startParserWorker = dispatcher.start.bind(dispatcher);
const stopParserWorker = exports.stopParserWorker = dispatcher.stop.bind(dispatcher);

const getClosestExpression = exports.getClosestExpression = dispatcher.task("getClosestExpression");
const getSymbols = exports.getSymbols = dispatcher.task("getSymbols");
const getScopes = exports.getScopes = dispatcher.task("getScopes");
const findOutOfScopeLocations = exports.findOutOfScopeLocations = dispatcher.task("findOutOfScopeLocations");
const clearSymbols = exports.clearSymbols = dispatcher.task("clearSymbols");
const clearScopes = exports.clearScopes = dispatcher.task("clearScopes");
const clearASTs = exports.clearASTs = dispatcher.task("clearASTs");
const getNextStep = exports.getNextStep = dispatcher.task("getNextStep");
const isInvalidPauseLocation = exports.isInvalidPauseLocation = dispatcher.task("isInvalidPauseLocation");
const getEmptyLines = exports.getEmptyLines = dispatcher.task("getEmptyLines");
const hasSource = exports.hasSource = dispatcher.task("hasSource");
const setSource = exports.setSource = dispatcher.task("setSource");
const clearSources = exports.clearSources = dispatcher.task("clearSources");
const hasSyntaxError = exports.hasSyntaxError = dispatcher.task("hasSyntaxError");
const getFramework = exports.getFramework = dispatcher.task("getFramework");
const replaceOriginalVariableName = exports.replaceOriginalVariableName = dispatcher.task("replaceOriginalVariableName");

/***/ }),
/* 1366 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/**
 * Utils for utils, by utils
 * @module utils/utils
 */

/**
 * @memberof utils/utils
 * @static
 */
function handleError(err) {
  console.log("ERROR: ", err);
}

/**
 * @memberof utils/utils
 * @static
 */
function promisify(context, method, ...args) {
  return new Promise((resolve, reject) => {
    args.push(response => {
      if (response.error) {
        reject(response);
      } else {
        resolve(response);
      }
    });
    method.apply(context, args);
  });
}

/**
 * @memberof utils/utils
 * @static
 */
function endTruncateStr(str, size) {
  if (str.length > size) {
    return `...${str.slice(str.length - size)}`;
  }
  return str;
}

function waitForMs(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

exports.handleError = handleError;
exports.promisify = promisify;
exports.endTruncateStr = endTruncateStr;
exports.waitForMs = waitForMs;

/***/ }),
/* 1367 */
/***/ (function(module, exports) {

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 networkRequest(url, opts) {
  return fetch(url, {
    cache: opts.loadFromCache ? "default" : "no-cache"
  }).then(res => {
    if (res.status >= 200 && res.status < 300) {
      return res.text().then(text => ({ content: text }));
    }
    return Promise.reject(`request failed with status ${res.status}`);
  });
}

module.exports = networkRequest;

/***/ }),
/* 1368 */
/***/ (function(module, exports) {

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"); }); }; }

function WorkerDispatcher() {
  this.msgId = 1;
  this.worker = null;
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

WorkerDispatcher.prototype = {
  start(url) {
    this.worker = new Worker(url);
    this.worker.onerror = () => {
      console.error(`Error in worker ${url}`);
    };
  },

  stop() {
    if (!this.worker) {
      return;
    }

    this.worker.terminate();
    this.worker = null;
  },

  task(method) {
    return (...args) => {
      return new Promise((resolve, reject) => {
        const id = this.msgId++;
        this.worker.postMessage({ id, method, args });

        const listener = ({ data: result }) => {
          if (result.id !== id) {
            return;
          }

          if (!this.worker) {
            return;
          }

          this.worker.removeEventListener("message", listener);
          if (result.error) {
            reject(result.error);
          } else {
            resolve(result.response);
          }
        };

        this.worker.addEventListener("message", listener);
      });
    };
  }
};

function workerHandler(publicInterface) {
  return function (msg) {
    const { id, method, args } = msg.data;
    try {
      const response = publicInterface[method].apply(undefined, args);
      if (response instanceof Promise) {
        response.then(val => self.postMessage({ id, response: val }),
        // Error can't be sent via postMessage, so be sure to
        // convert to string.
        err => self.postMessage({ id, error: err.toString() }));
      } else {
        self.postMessage({ id, response });
      }
    } catch (error) {
      // Error can't be sent via postMessage, so be sure to convert to
      // string.
      self.postMessage({ id, error: error.toString() });
    }
  };
}

function streamingWorkerHandler(publicInterface, { timeout = 100 } = {}, worker = self) {
  let streamingWorker = (() => {
    var _ref = _asyncToGenerator(function* (id, tasks) {
      let isWorking = true;

      const intervalId = setTimeout(function () {
        isWorking = false;
      }, timeout);

      const results = [];
      while (tasks.length !== 0 && isWorking) {
        const { callback, context, args } = tasks.shift();
        const result = yield callback.call(context, args);
        results.push(result);
      }
      worker.postMessage({ id, status: "pending", data: results });
      clearInterval(intervalId);

      if (tasks.length !== 0) {
        yield streamingWorker(id, tasks);
      }
    });

    return function streamingWorker(_x, _x2) {
      return _ref.apply(this, arguments);
    };
  })();

  return (() => {
    var _ref2 = _asyncToGenerator(function* (msg) {
      const { id, method, args } = msg.data;
      const workerMethod = publicInterface[method];
      if (!workerMethod) {
        console.error(`Could not find ${method} defined in worker.`);
      }
      worker.postMessage({ id, status: "start" });

      try {
        const tasks = workerMethod(args);
        yield streamingWorker(id, tasks);
        worker.postMessage({ id, status: "done" });
      } catch (error) {
        worker.postMessage({ id, status: "error", error });
      }
    });

    return function (_x3) {
      return _ref2.apply(this, arguments);
    };
  })();
}

module.exports = {
  WorkerDispatcher,
  workerHandler,
  streamingWorkerHandler
};

/***/ }),
/* 1369 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getSelectedSourceText = exports.getSelectedSource = exports.getSelectedLocation = exports.getSourcesForTabs = exports.getSourceTabs = exports.getTabs = exports.getSources = undefined;

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; }; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                                                                                                                                                                                   * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                                                                                                                                                                                                                                   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/**
 * Sources reducer
 * @module reducers/sources
 */

exports.initialSourcesState = initialSourcesState;
exports.removeSourceFromTabList = removeSourceFromTabList;
exports.removeSourcesFromTabList = removeSourcesFromTabList;
exports.getNewSelectedSourceId = getNewSelectedSourceId;
exports.getSource = getSource;
exports.getSourceByURL = getSourceByURL;
exports.getGeneratedSource = getGeneratedSource;
exports.getPendingSelectedLocation = getPendingSelectedLocation;
exports.getPrettySource = getPrettySource;
exports.hasPrettySource = hasPrettySource;
exports.getSourceInSources = getSourceInSources;

var _immutable = __webpack_require__(146);

var I = _interopRequireWildcard(_immutable);

var _reselect = __webpack_require__(993);

var _makeRecord = __webpack_require__(1361);

var _makeRecord2 = _interopRequireDefault(_makeRecord);

var _source = __webpack_require__(1356);

var _devtoolsSourceMap = __webpack_require__(1360);

var _prefs = __webpack_require__(226);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

function initialSourcesState() {
  return (0, _makeRecord2.default)({
    sources: I.Map(),
    selectedLocation: undefined,
    pendingSelectedLocation: _prefs.prefs.pendingSelectedLocation,
    sourcesText: I.Map(),
    tabs: I.List(restoreTabs())
  })();
}

function update(state = initialSourcesState(), action) {
  let location = null;

  switch (action.type) {
    case "UPDATE_SOURCE":
      {
        const source = action.source;
        return updateSource(state, source);
      }

    case "ADD_SOURCE":
      {
        return updateSource(state, action.source);
      }

    case "ADD_SOURCES":
      {
        return action.sources.reduce((newState, source) => updateSource(newState, source), state);
      }

    case "SELECT_SOURCE":
      location = _extends({}, action.location, {
        url: action.source.url
      });

      _prefs.prefs.pendingSelectedLocation = location;
      return state.set("selectedLocation", _extends({
        sourceId: action.source.id
      }, action.location)).set("pendingSelectedLocation", location);

    case "CLEAR_SELECTED_SOURCE":
      location = { url: "" };
      _prefs.prefs.pendingSelectedLocation = location;

      return state.set("selectedLocation", { sourceId: "" }).set("pendingSelectedLocation", location);

    case "SELECT_SOURCE_URL":
      location = {
        url: action.url,
        line: action.line
      };

      _prefs.prefs.pendingSelectedLocation = location;
      return state.set("pendingSelectedLocation", location);

    case "ADD_TAB":
      return state.merge({
        tabs: updateTabList({ sources: state }, action.source.url)
      });

    case "MOVE_TAB":
      return state.merge({
        tabs: updateTabList({ sources: state }, action.url, action.tabIndex)
      });

    case "CLOSE_TAB":
      _prefs.prefs.tabs = action.tabs;
      return state.merge({ tabs: action.tabs });

    case "CLOSE_TABS":
      _prefs.prefs.tabs = action.tabs;
      return state.merge({ tabs: action.tabs });

    case "LOAD_SOURCE_TEXT":
      return setSourceTextProps(state, action);

    case "BLACKBOX":
      if (action.status === "done") {
        return state.setIn(["sources", action.source.id, "isBlackBoxed"], action.value.isBlackBoxed);
      }
      break;

    case "NAVIGATE":
      const source = getSelectedSource({ sources: state });
      const url = source && source.get("url");

      if (!url) {
        return initialSourcesState();
      }

      return initialSourcesState().set("pendingSelectedLocation", { url });
  }

  return state;
}

function getTextPropsFromAction(action) {
  const { value, sourceId } = action;

  if (action.status === "start") {
    return { id: sourceId, loadedState: "loading" };
  } else if (action.status === "error") {
    return { id: sourceId, error: action.error, loadedState: "loaded" };
  }
  return {
    text: value.text,
    id: sourceId,
    contentType: value.contentType,
    loadedState: "loaded"
  };
}

// TODO: Action is coerced to `any` unfortunately because how we type
// asynchronous actions is wrong. The `value` may be null for the
// "start" and "error" states but we don't type it like that. We need
// to rethink how we type async actions.
function setSourceTextProps(state, action) {
  const text = getTextPropsFromAction(action);
  return updateSource(state, text);
}

function updateSource(state, source) {
  if (!source.id) {
    return state;
  }

  return state.mergeIn(["sources", source.id], source);
}

function removeSourceFromTabList(tabs, url) {
  return tabs.filter(tab => tab != url);
}

function removeSourcesFromTabList(tabs, urls) {
  return urls.reduce((t, url) => removeSourceFromTabList(t, url), tabs);
}

function restoreTabs() {
  const prefsTabs = _prefs.prefs.tabs || [];
  if (prefsTabs.length == 0) {
    return;
  }

  return prefsTabs;
}

/**
 * Adds the new source to the tab list if it is not already there
 * @memberof reducers/sources
 * @static
 */
function updateTabList(state, url, tabIndex) {
  let tabs = state.sources.get("tabs");

  const urlIndex = tabs.indexOf(url);
  const includesUrl = !!tabs.find(tab => tab == url);

  if (includesUrl) {
    if (tabIndex != undefined) {
      tabs = tabs.delete(urlIndex).insert(tabIndex, url);
    }
  } else {
    tabs = tabs.insert(0, url);
  }

  _prefs.prefs.tabs = tabs.toJS();
  return tabs;
}

/**
 * Gets the next tab to select when a tab closes. Heuristics:
 * 1. if the selected tab is available, it remains selected
 * 2. if it is gone, the next available tab to the left should be active
 * 3. if the first tab is active and closed, select the second tab
 *
 * @memberof reducers/sources
 * @static
 */
function getNewSelectedSourceId(state, availableTabs) {
  const selectedLocation = state.sources.selectedLocation;
  if (!selectedLocation) {
    return "";
  }

  const selectedTab = state.sources.sources.get(selectedLocation.sourceId);

  const selectedTabUrl = selectedTab ? selectedTab.get("url") : "";

  if (availableTabs.includes(selectedTabUrl)) {
    const sources = state.sources.sources;
    if (!sources) {
      return "";
    }

    const selectedSource = sources.find(source => source.get("url") == selectedTabUrl);

    if (selectedSource) {
      return selectedSource.get("id");
    }

    return "";
  }

  const tabUrls = state.sources.tabs.toJS();
  const leftNeighborIndex = Math.max(tabUrls.indexOf(selectedTabUrl) - 1, 0);
  const lastAvailbleTabIndex = availableTabs.size - 1;
  const newSelectedTabIndex = Math.min(leftNeighborIndex, lastAvailbleTabIndex);
  const availableTab = availableTabs.toJS()[newSelectedTabIndex];
  const tabSource = getSourceByUrlInSources(state.sources.sources, availableTab);

  if (tabSource) {
    return tabSource.get("id");
  }

  return "";
}

// Selectors

// Unfortunately, it's really hard to make these functions accept just
// the state that we care about and still type it with Flow. The
// problem is that we want to re-export all selectors from a single
// module for the UI, and all of those selectors should take the
// top-level app state, so we'd have to "wrap" them to automatically
// pick off the piece of state we're interested in. It's impossible
// (right now) to type those wrapped functions.


const getSourcesState = state => state.sources;

function getSource(state, id) {
  return getSourceInSources(getSources(state), id);
}

function getSourceByURL(state, url) {
  return getSourceByUrlInSources(state.sources.sources, url);
}

function getGeneratedSource(state, source) {
  if (!source || !(0, _devtoolsSourceMap.isOriginalId)(source.id)) {
    return null;
  }
  return getSource(state, (0, _devtoolsSourceMap.originalToGeneratedId)(source.id));
}

function getPendingSelectedLocation(state) {
  return state.sources.pendingSelectedLocation;
}

function getPrettySource(state, id) {
  const source = getSource(state, id);
  if (!source) {
    return;
  }

  return getSourceByURL(state, (0, _source.getPrettySourceURL)(source.get("url")));
}

function hasPrettySource(state, id) {
  return !!getPrettySource(state, id);
}

function getSourceByUrlInSources(sources, url) {
  if (!url) {
    return null;
  }

  return sources.find(source => source.get("url") === url);
}

function getSourceInSources(sources, id) {
  return sources.get(id);
}

const getSources = exports.getSources = (0, _reselect.createSelector)(getSourcesState, sources => sources.sources);

const getTabs = exports.getTabs = (0, _reselect.createSelector)(getSourcesState, sources => sources.tabs);

const getSourceTabs = exports.getSourceTabs = (0, _reselect.createSelector)(getTabs, getSources, (tabs, sources) => tabs.filter(tab => getSourceByUrlInSources(sources, tab)));

const getSourcesForTabs = exports.getSourcesForTabs = (0, _reselect.createSelector)(getSourceTabs, getSources, (tabs, sources) => {
  return tabs.map(tab => getSourceByUrlInSources(sources, tab)).filter(source => source);
});

const getSelectedLocation = exports.getSelectedLocation = (0, _reselect.createSelector)(getSourcesState, sources => sources.selectedLocation);

const getSelectedSource = exports.getSelectedSource = (0, _reselect.createSelector)(getSelectedLocation, getSources, (selectedLocation, sources) => {
  if (!selectedLocation) {
    return;
  }

  return sources.get(selectedLocation.sourceId);
});

const getSelectedSourceText = exports.getSelectedSourceText = (0, _reselect.createSelector)(getSelectedSource, getSourcesState, (selectedSource, sources) => {
  const id = selectedSource.get("id");
  return id ? sources.sourcesText.get(id) : null;
});

exports.default = update;

/***/ }),
/* 1370 */,
/* 1371 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.nodeHasChildren = nodeHasChildren;
exports.isExactUrlMatch = isExactUrlMatch;
exports.isDirectory = isDirectory;
exports.getExtension = getExtension;
exports.isNotJavaScript = isNotJavaScript;
exports.isInvalidUrl = isInvalidUrl;
exports.partIsFile = partIsFile;
exports.createNode = createNode;
exports.createParentMap = createParentMap;
exports.getRelativePath = getRelativePath;

var _url = __webpack_require__(334);

var _source = __webpack_require__(1356);

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 IGNORED_URLS = ["debugger eval code", "XStringBundle"];

function nodeHasChildren(item) {
  return Array.isArray(item.contents);
}

function isExactUrlMatch(pathPart, debuggeeUrl) {
  // compare to hostname with an optional 'www.' prefix
  const { host } = (0, _url.parse)(debuggeeUrl);
  if (!host) {
    return false;
  }
  return host.replace(/^www\./, "") === pathPart.replace(/^www\./, "");
}

function isDirectory(url) {
  const parts = url.path.split("/").filter(p => p !== "");

  // Assume that all urls point to files except when they end with '/'
  // Or directory node has children
  return (parts.length === 0 || url.path.slice(-1) === "/" || nodeHasChildren(url)) && url.name != "(index)";
}

function getExtension(source) {
  const parsedUrl = (0, _url.parse)(source.get("url")).pathname;
  if (!parsedUrl) {
    return "";
  }
  return parsedUrl.split(".").pop();
}

function isNotJavaScript(source) {
  return ["css", "svg", "png"].includes(getExtension(source));
}

function isInvalidUrl(url, source) {
  return IGNORED_URLS.indexOf(url) != -1 || !source.get("url") || !url.group || (0, _source.isPretty)(source) || isNotJavaScript(source);
}

function partIsFile(index, parts, url) {
  const isLastPart = index === parts.length - 1;
  return !isDirectory(url) && isLastPart;
}

function createNode(name, path, contents) {
  return {
    name,
    path,
    contents
  };
}

function createParentMap(tree) {
  const map = new WeakMap();

  function _traverse(subtree) {
    if (nodeHasChildren(subtree)) {
      for (const child of subtree.contents) {
        map.set(child, subtree);
        _traverse(child);
      }
    }
  }

  // Don't link each top-level path to the "root" node because the
  // user never sees the root
  tree.contents.forEach(_traverse);
  return map;
}

function getRelativePath(url) {
  const { pathname } = (0, _url.parse)(url);
  if (!pathname) {
    return url;
  }
  const path = pathname.split("/");
  path.shift();
  return path.join("/");
}

/***/ }),
/* 1372 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

__webpack_require__(1324);

// Load all existing rep templates
const Undefined = __webpack_require__(1560);
const Null = __webpack_require__(1561);
const StringRep = __webpack_require__(1447);
const Number = __webpack_require__(1563);
const ArrayRep = __webpack_require__(1448);
const Obj = __webpack_require__(1564);
const SymbolRep = __webpack_require__(1565);
const InfinityRep = __webpack_require__(1566);
const NaNRep = __webpack_require__(1567);
const Accessor = __webpack_require__(1568);

// DOM types (grips)
const Attribute = __webpack_require__(1569);
const DateTime = __webpack_require__(1570);
const Document = __webpack_require__(1571);
const Event = __webpack_require__(1572);
const Func = __webpack_require__(1573);
const PromiseRep = __webpack_require__(1574);
const RegExp = __webpack_require__(1575);
const StyleSheet = __webpack_require__(1576);
const CommentNode = __webpack_require__(1577);
const ElementNode = __webpack_require__(1578);
const TextNode = __webpack_require__(1579);
const ErrorRep = __webpack_require__(1580);
const Window = __webpack_require__(1581);
const ObjectWithText = __webpack_require__(1582);
const ObjectWithURL = __webpack_require__(1583);
const GripArray = __webpack_require__(1450);
const GripMap = __webpack_require__(1584);
const GripMapEntry = __webpack_require__(1451);
const Grip = __webpack_require__(1409);

// List of all registered template.
// XXX there should be a way for extensions to register a new
// or modify an existing rep.
let reps = [RegExp, StyleSheet, Event, DateTime, CommentNode, ElementNode, TextNode, Attribute, Func, PromiseRep, ArrayRep, Document, Window, ObjectWithText, ObjectWithURL, ErrorRep, GripArray, GripMap, GripMapEntry, Grip, Undefined, Null, StringRep, Number, SymbolRep, InfinityRep, NaNRep, Accessor];

/**
 * Generic rep that is using 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 is as 'object'
 * property.
 */
const Rep = function (props) {
  let {
    object,
    defaultRep
  } = props;
  let rep = getRep(object, defaultRep, props.noGrip);
  return rep(props);
};

// 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 defaultObject {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.
 */
function getRep(object, defaultRep = Obj, noGrip = false) {
  for (let i = 0; i < reps.length; i++) {
    let rep = reps[i];
    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: {
    Accessor,
    ArrayRep,
    Attribute,
    CommentNode,
    DateTime,
    Document,
    ElementNode,
    ErrorRep,
    Event,
    Func,
    Grip,
    GripArray,
    GripMap,
    GripMapEntry,
    InfinityRep,
    NaNRep,
    Null,
    Number,
    Obj,
    ObjectWithText,
    ObjectWithURL,
    PromiseRep,
    RegExp,
    Rep,
    StringRep,
    StyleSheet,
    SymbolRep,
    TextNode,
    Undefined,
    Window
  },
  // Exporting for tests
  getRep
};

/***/ }),
/* 1373 */,
/* 1374 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

__webpack_require__(1312);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 CloseButton({ handleClick, buttonClass, tooltip }) {
  return _react2.default.createElement(
    "div",
    {
      className: buttonClass ? `close-btn ${buttonClass}` : "close-btn",
      onClick: handleClick,
      title: tooltip
    },
    _react2.default.createElement("img", { className: "close" })
  );
}

exports.default = CloseButton;

/***/ }),
/* 1375 */,
/* 1376 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 Menu = __webpack_require__(1464);
const MenuItem = __webpack_require__(1466);
const { PrefsHelper } = __webpack_require__(1467);
const Services = __webpack_require__(22);
const KeyShortcuts = __webpack_require__(1468);
const { ZoomKeys } = __webpack_require__(1469);
const EventEmitter = __webpack_require__(1382);

module.exports = {
  KeyShortcuts,
  Menu,
  MenuItem,
  PrefsHelper,
  Services,
  ZoomKeys,
  EventEmitter
};

/***/ }),
/* 1377 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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/. */
const sidePanelItems = {
  Firefox: {
    name: "Firefox",
    clientType: "firefox",
    paramName: "firefox-tab",
    docsUrlPart: "firefox"
  },
  Chrome: {
    name: "Chrome",
    clientType: "chrome",
    paramName: "chrome-tab",
    docsUrlPart: "chrome",
    isUnderConstruction: true
  },
  Node: {
    name: "Node",
    clientType: "node",
    paramName: "node-tab",
    docsUrlPart: "node",
    isUnderConstruction: true
  },
  Settings: {
    name: "Settings",
    clientType: "settings",
    paramName: "settings-tab",
    docsUrlPart: "settings"
  }
};

module.exports = {
  CLEAR_TABS: "CLEAR_TABS",
  ADD_TABS: "ADD_TABS",
  SELECT_TAB: "SELECT_TAB",
  FILTER_TABS: "FILTER_TABS",
  SET_VALUE: "SET_VALUE",
  SET_CONFIG: "SET_CONFIG",
  sidePanelItems
};

/***/ }),
/* 1378 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

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; }; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                                                                                                                                                                                   * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                                                                                                                                                                                                                                   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/**
 * Breakpoints reducer
 * @module reducers/breakpoints
 */

exports.initialBreakpointsState = initialBreakpointsState;
exports.getBreakpoints = getBreakpoints;
exports.getBreakpoint = getBreakpoint;
exports.getBreakpointsDisabled = getBreakpointsDisabled;
exports.getBreakpointsLoading = getBreakpointsLoading;
exports.getBreakpointsForSource = getBreakpointsForSource;
exports.getBreakpointForLine = getBreakpointForLine;
exports.getHiddenBreakpoint = getHiddenBreakpoint;
exports.getHiddenBreakpointLocation = getHiddenBreakpointLocation;

var _immutable = __webpack_require__(146);

var I = _interopRequireWildcard(_immutable);

var _makeRecord = __webpack_require__(1361);

var _makeRecord2 = _interopRequireDefault(_makeRecord);

var _devtoolsSourceMap = __webpack_require__(1360);

var _breakpoint = __webpack_require__(1364);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

function initialBreakpointsState() {
  return (0, _makeRecord2.default)({
    breakpoints: I.Map(),
    breakpointsDisabled: false
  })();
}

function update(state = initialBreakpointsState(), action) {
  switch (action.type) {
    case "ADD_BREAKPOINT":
      {
        return addBreakpoint(state, action);
      }

    case "SYNC_BREAKPOINT":
      {
        return syncBreakpoint(state, action);
      }

    case "ENABLE_BREAKPOINT":
      {
        return addBreakpoint(state, action);
      }

    case "DISABLE_BREAKPOINT":
      {
        return updateBreakpoint(state, action);
      }

    case "SET_BREAKPOINT_CONDITION":
      {
        return updateBreakpoint(state, action);
      }

    case "REMOVE_BREAKPOINT":
      {
        return removeBreakpoint(state, action);
      }

    case "REMAP_BREAKPOINTS":
      {
        return remapBreakpoints(state, action);
      }

    case "NAVIGATE":
      {
        return initialBreakpointsState();
      }
  }

  return state;
}

function addBreakpoint(state, action) {
  if (action.status === "start" && action.breakpoint) {
    const { breakpoint } = action;
    const locationId = (0, _breakpoint.makeLocationId)(breakpoint.location);
    return state.setIn(["breakpoints", locationId], breakpoint);
  }

  // when the action completes, we can commit the breakpoint
  if (action.status === "done") {
    return syncBreakpoint(state, action.value);
  }

  // Remove the optimistic update
  if (action.status === "error" && action.breakpoint) {
    const locationId = (0, _breakpoint.makeLocationId)(action.breakpoint.location);
    return state.deleteIn(["breakpoints", locationId]);
  }

  return state;
}

function syncBreakpoint(state, data) {
  const { breakpoint, previousLocation } = data;

  if (previousLocation) {
    state = state.deleteIn(["breakpoints", (0, _breakpoint.makeLocationId)(previousLocation)]);
  }

  if (!breakpoint) {
    return state;
  }

  const locationId = (0, _breakpoint.makeLocationId)(breakpoint.location);
  return state.setIn(["breakpoints", locationId], breakpoint);
}

function updateBreakpoint(state, action) {
  const { breakpoint } = action;
  const locationId = (0, _breakpoint.makeLocationId)(breakpoint.location);
  return state.setIn(["breakpoints", locationId], breakpoint);
}

function remapBreakpoints(state, action) {
  const breakpoints = action.breakpoints.reduce((updatedBreakpoints, breakpoint) => {
    const locationId = (0, _breakpoint.makeLocationId)(breakpoint.location);
    return _extends({}, updatedBreakpoints, { [locationId]: breakpoint });
  }, {});

  return state.set("breakpoints", I.Map(breakpoints));
}

function removeBreakpoint(state, action) {
  const { breakpoint } = action;
  const id = (0, _breakpoint.makeLocationId)(breakpoint.location);
  return state.deleteIn(["breakpoints", id]);
}

// Selectors
// TODO: these functions should be moved out of the reducer

function getBreakpoints(state) {
  return state.breakpoints.breakpoints;
}

function getBreakpoint(state, location) {
  const breakpoints = getBreakpoints(state);
  return breakpoints.get((0, _breakpoint.makeLocationId)(location));
}

function getBreakpointsDisabled(state) {
  return state.breakpoints.breakpoints.every(x => x.disabled);
}

function getBreakpointsLoading(state) {
  const breakpoints = getBreakpoints(state);
  const isLoading = !!breakpoints.valueSeq().filter(bp => bp.loading).first();

  return breakpoints.size > 0 && isLoading;
}

function getBreakpointsForSource(state, sourceId) {
  if (!sourceId) {
    return I.Map();
  }

  const isGeneratedSource = (0, _devtoolsSourceMap.isGeneratedId)(sourceId);
  const breakpoints = getBreakpoints(state);

  return breakpoints.filter(bp => {
    const location = isGeneratedSource ? bp.generatedLocation || bp.location : bp.location;
    return location.sourceId === sourceId;
  });
}

function getBreakpointForLine(state, sourceId, line) {
  if (!sourceId) {
    return I.Map();
  }
  const breakpoints = getBreakpointsForSource(state, sourceId);
  return breakpoints.find(breakpoint => breakpoint.location.line === line);
}

function getHiddenBreakpoint(state) {
  return getBreakpoints(state).valueSeq().filter(breakpoint => breakpoint.hidden).first();
}

function getHiddenBreakpointLocation(state) {
  const hiddenBreakpoint = getHiddenBreakpoint(state);
  if (!hiddenBreakpoint) {
    return null;
  }
  return hiddenBreakpoint.location;
}

exports.default = update;

/***/ }),
/* 1379 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _Svg = __webpack_require__(1359);

var _Svg2 = _interopRequireDefault(_Svg);

var _classnames = __webpack_require__(175);

var _classnames2 = _interopRequireDefault(_classnames);

var _Close = __webpack_require__(1374);

var _Close2 = _interopRequireDefault(_Close);

__webpack_require__(1313);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

const arrowBtn = (onClick, type, className, tooltip) => {
  const props = {
    className,
    key: type,
    onClick,
    title: tooltip,
    type
  };

  return _react2.default.createElement(
    "button",
    props,
    _react2.default.createElement(_Svg2.default, { name: type })
  );
}; /* This Source Code Form is subject to the terms of the Mozilla Public
    * License, v. 2.0. If a copy of the MPL was not distributed with this
    * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

class SearchInput extends _react.Component {

  componentDidMount() {
    this.setFocus();
  }

  componentDidUpdate(prevProps) {
    if (this.props.shouldFocus && !prevProps.shouldFocus) {
      this.setFocus();
    }
  }

  setFocus() {
    if (this.$input) {
      const input = this.$input;
      input.focus();

      if (!input.value) {
        return;
      }

      // omit prefix @:# from being selected
      const selectStartPos = this.props.hasPrefix ? 1 : 0;
      input.setSelectionRange(selectStartPos, input.value.length + 1);
    }
  }

  renderSvg() {
    const svgName = this.props.showErrorEmoji ? "sad-face" : "magnifying-glass";
    return _react2.default.createElement(_Svg2.default, { name: svgName });
  }

  renderArrowButtons() {
    const { handleNext, handlePrev } = this.props;

    return [arrowBtn(handleNext, "arrow-down", (0, _classnames2.default)("nav-btn", "next"), L10N.getFormatStr("editor.searchResults.nextResult")), arrowBtn(handlePrev, "arrow-up", (0, _classnames2.default)("nav-btn", "prev"), L10N.getFormatStr("editor.searchResults.prevResult"))];
  }

  renderNav() {
    const { count, handleNext, handlePrev } = this.props;
    if (!handleNext && !handlePrev || !count || count == 1) {
      return;
    }

    return _react2.default.createElement(
      "div",
      { className: "search-nav-buttons" },
      this.renderArrowButtons()
    );
  }

  render() {
    const {
      expanded,
      handleClose,
      onBlur,
      onChange,
      onFocus,
      onKeyDown,
      onKeyUp,
      placeholder,
      query,
      selectedItemId,
      showErrorEmoji,
      size,
      summaryMsg
    } = this.props;

    const inputProps = {
      className: (0, _classnames2.default)({
        empty: showErrorEmoji
      }),
      onChange,
      onKeyDown,
      onKeyUp,
      onFocus,
      onBlur,
      "aria-autocomplete": "list",
      "aria-controls": "result-list",
      "aria-activedescendant": expanded && selectedItemId ? `${selectedItemId}-title` : "",
      placeholder,
      value: query,
      spellCheck: false,
      ref: c => this.$input = c
    };

    return _react2.default.createElement(
      "div",
      {
        className: (0, _classnames2.default)("search-field", size),
        role: "combobox",
        "aria-haspopup": "listbox",
        "aria-owns": "result-list",
        "aria-expanded": expanded
      },
      this.renderSvg(),
      _react2.default.createElement("input", inputProps),
      summaryMsg && _react2.default.createElement(
        "div",
        { className: "summary" },
        summaryMsg
      ),
      this.renderNav(),
      _react2.default.createElement(_Close2.default, { handleClick: handleClose, buttonClass: size })
    );
  }
}

SearchInput.defaultProps = {
  expanded: false,
  hasPrefix: false,
  selectedItemId: "",
  size: ""
};
exports.default = SearchInput;

/***/ }),
/* 1380 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

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; }; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                                                                                                                                                                                   * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                                                                                                                                                                                                                                   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

exports.getLibraryFromUrl = getLibraryFromUrl;
exports.annotateFrame = annotateFrame;
exports.simplifyDisplayName = simplifyDisplayName;
exports.formatDisplayName = formatDisplayName;
exports.formatCopyName = formatCopyName;
exports.collapseFrames = collapseFrames;

var _utils = __webpack_require__(1366);

var _source = __webpack_require__(1356);

var _lodash = __webpack_require__(2);

function getFrameUrl(frame) {
  return (0, _lodash.get)(frame, "source.url", "") || "";
}

const libraryMap = [{
  label: "Backbone",
  pattern: /backbone/i
}, {
  label: "jQuery",
  pattern: /jquery/i
}, {
  label: "Preact",
  pattern: /preact/i
}, {
  label: "React",
  pattern: /react/i
}, {
  label: "Immutable",
  pattern: /immutable/i
}, {
  label: "Webpack",
  pattern: /webpack\/bootstrap/i
}, {
  label: "Node",
  pattern: /(^internal\/|^[^.\/]+\.js)/
}, {
  label: "Express",
  pattern: /node_modules\/express/
}, {
  label: "Pug",
  pattern: /node_modules\/pug/
}, {
  label: "ExtJS",
  pattern: /\/ext-all[\.\-]/
}, {
  label: "MobX",
  pattern: /mobx/i
}, {
  label: "Underscore",
  pattern: /underscore/i
}, {
  label: "Lodash",
  pattern: /lodash/i
}, {
  label: "Ember",
  pattern: /ember/i
}, {
  label: "Choo",
  pattern: /choo/i
}, {
  label: "VueJS",
  pattern: /vue\.js/i
}, {
  label: "RxJS",
  pattern: /rxjs/i
}, {
  label: "Angular",
  pattern: /angular/i
}, {
  label: "Redux",
  pattern: /redux/i
}, {
  label: "Dojo",
  pattern: /dojo/i
}, {
  label: "Marko",
  pattern: /marko/i
}, {
  label: "NuxtJS",
  pattern: /[\._]nuxt/i
}, {
  label: "Aframe",
  pattern: /aframe/i
}, {
  label: "NextJS",
  pattern: /[\._]next/i
}];

function getLibraryFromUrl(frame) {
  // @TODO each of these fns calls getFrameUrl, just call it once
  // (assuming there's not more complex logic to identify a lib)
  const frameUrl = getFrameUrl(frame);
  const match = (0, _lodash.find)(libraryMap, o => frameUrl.match(o.pattern));
  return match && match.label;
}

const displayNameMap = {
  Backbone: {
    "extend/child": "Create Class",
    ".create": "Create Model"
  },
  jQuery: {
    "jQuery.event.dispatch": "Dispatch Event"
  },
  React: {
    // eslint-disable-next-line max-len
    "ReactCompositeComponent._renderValidatedComponentWithoutOwnerOrContext/renderedElement<": "Render",
    _renderValidatedComponentWithoutOwnerOrContext: "Render"
  },
  VueJS: {
    "renderMixin/Vue.prototype._render": "Render"
  },
  Webpack: {
    // eslint-disable-next-line camelcase
    __webpack_require__: "Bootstrap"
  }
};

function mapDisplayNames(frame, library) {
  const map = displayNameMap[library];
  const { displayName } = frame;
  return map && map[displayName] || displayName;
}

function annotateFrame(frame) {
  const library = getLibraryFromUrl(frame);
  if (library) {
    return _extends({}, frame, { library });
  }

  return frame;
}

// Decodes an anonymous naming scheme that
// spider monkey implements based on "Naming Anonymous JavaScript Functions"
// http://johnjbarton.github.io/nonymous/index.html
const objectProperty = /([\w\d]+)$/;
const arrayProperty = /\[(.*?)\]$/;
const functionProperty = /([\w\d]+)[\/\.<]*?$/;
const annonymousProperty = /([\w\d]+)\(\^\)$/;

function simplifyDisplayName(displayName) {
  // if the display name has a space it has already been mapped
  if (/\s/.exec(displayName)) {
    return displayName;
  }

  const scenarios = [objectProperty, arrayProperty, functionProperty, annonymousProperty];

  for (const reg of scenarios) {
    const match = reg.exec(displayName);
    if (match) {
      return match[1];
    }
  }

  return displayName;
}

function formatDisplayName(frame, { shouldMapDisplayName = true } = {}) {
  let { displayName, library } = frame;
  if (library && shouldMapDisplayName) {
    displayName = mapDisplayNames(frame, library);
  }

  displayName = simplifyDisplayName(displayName);
  return (0, _utils.endTruncateStr)(displayName, 25);
}

function formatCopyName(frame) {
  const displayName = formatDisplayName(frame);
  const fileName = (0, _source.getFilename)(frame.source);
  const frameLocation = frame.location.line;

  return `${displayName} (${fileName}#${frameLocation})`;
}

function collapseFrames(frames) {
  // We collapse groups of one so that user frames
  // are not in a group of one
  function addGroupToList(group, list) {
    if (!group) {
      return list;
    }

    if (group.length > 1) {
      list.push(group);
    } else {
      list = list.concat(group);
    }

    return list;
  }
  const { newFrames, lastGroup } = collapseLastFrames(frames);
  frames = newFrames;
  let items = [];
  let currentGroup = null;
  let prevItem = null;
  for (const frame of frames) {
    const prevLibrary = (0, _lodash.get)(prevItem, "library");

    if (!currentGroup) {
      currentGroup = [frame];
    } else if (prevLibrary && prevLibrary == frame.library) {
      currentGroup.push(frame);
    } else {
      items = addGroupToList(currentGroup, items);
      currentGroup = [frame];
    }

    prevItem = frame;
  }

  items = addGroupToList(currentGroup, items);
  items = addGroupToList(lastGroup, items);
  return items;
}

function collapseLastFrames(frames) {
  const index = (0, _lodash.findIndex)(frames, frame => getFrameUrl(frame).match(/webpack\/bootstrap/i));

  if (index == -1) {
    return { newFrames: frames, lastGroup: [] };
  }

  const newFrames = frames.slice(0, index);
  const lastGroup = frames.slice(index);
  return { newFrames, lastGroup };
}

/***/ }),
/* 1381 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Dependencies
const PropTypes = __webpack_require__(20);
const {
  maybeEscapePropertyName,
  wrapRender
} = __webpack_require__(1353);
const { MODE } = __webpack_require__(1357);

const dom = __webpack_require__(1758);
const { span } = dom;

/**
 * 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 = {
  // Property name.
  name: PropTypes.oneOfType([PropTypes.string, PropTypes.object]).isRequired,
  // Equal character rendered between property name and value.
  equal: PropTypes.string,
  // @TODO Change this to Object.values once it's supported in Node's version of V8
  mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
  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
};

/**
 * 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 = __webpack_require__(1409);
  const { Rep } = __webpack_require__(1372);

  let {
    name,
    mode,
    equal,
    suppressQuotes
  } = props;

  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": "nodeName" }, name);
  } else {
    key = Rep(Object.assign({}, props, {
      className: "nodeName",
      object: name,
      mode: mode || MODE.TINY,
      defaultRep: Grip
    }));
  }

  return [key, span({
    "className": "objectEqual"
  }, equal), Rep(Object.assign({}, props))];
}

// Exports from this module
module.exports = wrapRender(PropRep);

/***/ }),
/* 1382 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

var EventEmitter = function EventEmitter() {};
module.exports = EventEmitter;

const promise = __webpack_require__(1465);

/**
 * Decorate an object with event emitter functionality.
 *
 * @param Object aObjectToDecorate
 *        Bind all public methods of EventEmitter to
 *        the aObjectToDecorate object.
 */
EventEmitter.decorate = function EventEmitter_decorate(aObjectToDecorate) {
  let emitter = new EventEmitter();
  aObjectToDecorate.on = emitter.on.bind(emitter);
  aObjectToDecorate.off = emitter.off.bind(emitter);
  aObjectToDecorate.once = emitter.once.bind(emitter);
  aObjectToDecorate.emit = emitter.emit.bind(emitter);
};

EventEmitter.prototype = {
  /**
   * Connect a listener.
   *
   * @param string aEvent
   *        The event name to which we're connecting.
   * @param function aListener
   *        Called when the event is fired.
   */
  on: function EventEmitter_on(aEvent, aListener) {
    if (!this._eventEmitterListeners) this._eventEmitterListeners = new Map();
    if (!this._eventEmitterListeners.has(aEvent)) {
      this._eventEmitterListeners.set(aEvent, []);
    }
    this._eventEmitterListeners.get(aEvent).push(aListener);
  },

  /**
   * Listen for the next time an event is fired.
   *
   * @param string aEvent
   *        The event name to which we're connecting.
   * @param function aListener
   *        (Optional) Called when the event is fired. Will be called at most
   *        one time.
   * @return promise
   *        A promise which is resolved when the event next happens. The
   *        resolution value of the promise is the first event argument. If
   *        you need access to second or subsequent event arguments (it's rare
   *        that this is needed) then use aListener
   */
  once: function EventEmitter_once(aEvent, aListener) {
    let deferred = promise.defer();

    let handler = (aEvent, aFirstArg, ...aRest) => {
      this.off(aEvent, handler);
      if (aListener) {
        aListener.apply(null, [aEvent, aFirstArg, ...aRest]);
      }
      deferred.resolve(aFirstArg);
    };

    handler._originalListener = aListener;
    this.on(aEvent, handler);

    return deferred.promise;
  },

  /**
   * Remove a previously-registered event listener.  Works for events
   * registered with either on or once.
   *
   * @param string aEvent
   *        The event name whose listener we're disconnecting.
   * @param function aListener
   *        The listener to remove.
   */
  off: function EventEmitter_off(aEvent, aListener) {
    if (!this._eventEmitterListeners) return;
    let listeners = this._eventEmitterListeners.get(aEvent);
    if (listeners) {
      this._eventEmitterListeners.set(aEvent, listeners.filter(l => {
        return l !== aListener && l._originalListener !== aListener;
      }));
    }
  },

  /**
   * Emit an event.  All arguments to this method will
   * be sent to listener functions.
   */
  emit: function EventEmitter_emit(aEvent) {
    if (!this._eventEmitterListeners || !this._eventEmitterListeners.has(aEvent)) {
      return;
    }

    let originalListeners = this._eventEmitterListeners.get(aEvent);
    for (let listener of this._eventEmitterListeners.get(aEvent)) {
      // If the object was destroyed during event emission, stop
      // emitting.
      if (!this._eventEmitterListeners) {
        break;
      }

      // If listeners were removed during emission, make sure the
      // event handler we're going to fire wasn't removed.
      if (originalListeners === this._eventEmitterListeners.get(aEvent) || this._eventEmitterListeners.get(aEvent).some(l => l === listener)) {
        try {
          listener.apply(null, arguments);
        } catch (ex) {
          // Prevent a bad listener from interfering with the others.
          let msg = ex + ": " + ex.stack;
          //console.error(msg);
          console.log(msg);
        }
      }
    }
  }
};

/***/ }),
/* 1383 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

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; }; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                                                                                                                                                                                   * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                                                                                                                                                                                                                                   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/**
 * Ast reducer
 * @module reducers/ast
 */

exports.initialASTState = initialASTState;
exports.getSymbols = getSymbols;
exports.hasSymbols = hasSymbols;
exports.isEmptyLineInSource = isEmptyLineInSource;
exports.getEmptyLines = getEmptyLines;
exports.getOutOfScopeLocations = getOutOfScopeLocations;
exports.getPreview = getPreview;
exports.getSourceMetaData = getSourceMetaData;
exports.getInScopeLines = getInScopeLines;
exports.isLineInScope = isLineInScope;

var _immutable = __webpack_require__(146);

var I = _interopRequireWildcard(_immutable);

var _makeRecord = __webpack_require__(1361);

var _makeRecord2 = _interopRequireDefault(_makeRecord);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

function initialASTState() {
  return (0, _makeRecord2.default)({
    symbols: I.Map(),
    emptyLines: I.Map(),
    outOfScopeLocations: null,
    inScopeLines: null,
    preview: null,
    sourceMetaData: I.Map()
  })();
}

function update(state = initialASTState(), action) {
  switch (action.type) {
    case "SET_SYMBOLS":
      {
        const { source, symbols } = action;
        return state.setIn(["symbols", source.id], symbols);
      }

    case "SET_EMPTY_LINES":
      {
        const { source, emptyLines } = action;
        return state.setIn(["emptyLines", source.id], emptyLines);
      }

    case "OUT_OF_SCOPE_LOCATIONS":
      {
        return state.set("outOfScopeLocations", action.locations);
      }

    case "IN_SCOPE_LINES":
      {
        return state.set("inScopeLines", action.lines);
      }

    case "CLEAR_SELECTION":
      {
        return state.set("preview", null);
      }

    case "SET_PREVIEW":
      {
        if (action.status == "start") {
          return state.set("preview", { updating: true });
        }

        if (!action.value) {
          return state.set("preview", null);
        }

        return state.set("preview", _extends({}, action.value, {
          updating: false
        }));
      }

    case "RESUME":
      {
        return state.set("outOfScopeLocations", null);
      }

    case "NAVIGATE":
      {
        return initialASTState();
      }

    case "SET_SOURCE_METADATA":
      {
        return state.setIn(["sourceMetaData", action.sourceId], action.sourceMetaData);
      }

    default:
      {
        return state;
      }
  }
}

// NOTE: we'd like to have the app state fully typed
// https://github.com/devtools-html/debugger.html/blob/master/src/reducers/sources.js#L179-L185


const emptySymbols = { variables: [], functions: [] };
function getSymbols(state, source) {
  if (!source) {
    return emptySymbols;
  }

  const symbols = state.ast.getIn(["symbols", source.id]);
  return symbols || emptySymbols;
}

function hasSymbols(state, source) {
  if (!source) {
    return false;
  }

  return !!state.ast.getIn(["symbols", source.id]);
}

function isEmptyLineInSource(state, line, selectedSource) {
  const emptyLines = getEmptyLines(state, selectedSource);
  return emptyLines.includes(line);
}

function getEmptyLines(state, source) {
  if (!source) {
    return [];
  }

  return state.ast.getIn(["emptyLines", source.id]) || [];
}

function getOutOfScopeLocations(state) {
  return state.ast.get("outOfScopeLocations");
}

function getPreview(state) {
  return state.ast.get("preview");
}

const emptySourceMetaData = {};
function getSourceMetaData(state, sourceId) {
  return state.ast.getIn(["sourceMetaData", sourceId]) || emptySourceMetaData;
}

function getInScopeLines(state) {
  return state.ast.get("inScopeLines");
}

function isLineInScope(state, line) {
  const linesInScope = state.ast.get("inScopeLines");
  return linesInScope && linesInScope.includes(line);
}

exports.default = update;

/***/ }),
/* 1384 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = assert;

var _devtoolsConfig = __webpack_require__(1355);

function assert(condition, message) {
  if ((0, _devtoolsConfig.isDevelopment)() && !condition) {
    throw new Error(`Assertion failure: ${message}`);
  }
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/***/ }),
/* 1385 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.setContextMenu = setContextMenu;
exports.setPrimaryPaneTab = setPrimaryPaneTab;
exports.closeActiveSearch = closeActiveSearch;
exports.setActiveSearch = setActiveSearch;
exports.toggleFrameworkGrouping = toggleFrameworkGrouping;
exports.showSource = showSource;
exports.togglePaneCollapse = togglePaneCollapse;
exports.highlightLineRange = highlightLineRange;
exports.flashLineRange = flashLineRange;
exports.clearHighlightLineRange = clearHighlightLineRange;
exports.openConditionalPanel = openConditionalPanel;
exports.closeConditionalPanel = closeConditionalPanel;
exports.clearProjectDirectoryRoot = clearProjectDirectoryRoot;
exports.setProjectDirectoryRoot = setProjectDirectoryRoot;
exports.setOrientation = setOrientation;

var _selectors = __webpack_require__(1352);

var _ui = __webpack_require__(1421);

var _source = __webpack_require__(1356);

function setContextMenu(type, event) {
  return ({ dispatch }) => {
    dispatch({ type: "SET_CONTEXT_MENU", contextMenu: { type, event } });
  };
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.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 setPrimaryPaneTab(tabName) {
  return { type: "SET_PRIMARY_PANE_TAB", tabName };
}

function closeActiveSearch() {
  return {
    type: "TOGGLE_ACTIVE_SEARCH",
    value: null
  };
}

function setActiveSearch(activeSearch) {
  return ({ dispatch, getState }) => {
    const activeSearchState = (0, _selectors.getActiveSearch)(getState());
    if (activeSearchState === activeSearch) {
      return;
    }

    if ((0, _selectors.getQuickOpenEnabled)(getState())) {
      dispatch({ type: "CLOSE_QUICK_OPEN" });
    }

    dispatch({
      type: "TOGGLE_ACTIVE_SEARCH",
      value: activeSearch
    });
  };
}

function toggleFrameworkGrouping(toggleValue) {
  return ({ dispatch, getState }) => {
    dispatch({
      type: "TOGGLE_FRAMEWORK_GROUPING",
      value: toggleValue
    });
  };
}

function showSource(sourceId) {
  return ({ dispatch, getState }) => {
    const source = (0, _selectors.getSource)(getState(), sourceId);

    dispatch(setPrimaryPaneTab("sources"));
    dispatch({
      type: "SHOW_SOURCE",
      sourceUrl: ""
    });

    dispatch({
      type: "SHOW_SOURCE",
      sourceUrl: (0, _source.getRawSourceURL)(source.get("url"))
    });
  };
}

function togglePaneCollapse(position, paneCollapsed) {
  return ({ dispatch, getState }) => {
    const prevPaneCollapse = (0, _selectors.getPaneCollapse)(getState(), position);
    if (prevPaneCollapse === paneCollapsed) {
      return;
    }

    dispatch({
      type: "TOGGLE_PANE",
      position,
      paneCollapsed
    });
  };
}

/**
 * @memberof actions/sources
 * @static
 */
function highlightLineRange(location) {
  return {
    type: "HIGHLIGHT_LINES",
    location
  };
}

function flashLineRange(location) {
  return ({ dispatch }) => {
    dispatch(highlightLineRange(location));
    setTimeout(() => dispatch(clearHighlightLineRange()), 200);
  };
}

/**
 * @memberof actions/sources
 * @static
 */
function clearHighlightLineRange() {
  return {
    type: "CLEAR_HIGHLIGHT_LINES"
  };
}

function openConditionalPanel(line) {
  if (!line) {
    return;
  }

  return {
    type: "OPEN_CONDITIONAL_PANEL",
    line
  };
}

function closeConditionalPanel() {
  return {
    type: "CLOSE_CONDITIONAL_PANEL"
  };
}

function clearProjectDirectoryRoot() {
  return {
    type: "SET_PROJECT_DIRECTORY_ROOT",
    url: ""
  };
}

function setProjectDirectoryRoot(newRoot) {
  return ({ dispatch, getState }) => {
    const curRoot = (0, _ui.getProjectDirectoryRoot)(getState());
    if (newRoot && curRoot) {
      const temp = newRoot.split("/");
      temp.splice(0, 2);
      newRoot = `${curRoot}/${temp.join("/")}`;
    }

    dispatch({
      type: "SET_PROJECT_DIRECTORY_ROOT",
      url: newRoot
    });
  };
}

function setOrientation(orientation) {
  return { type: "SET_ORIENTATION", orientation };
}

/***/ }),
/* 1386 */,
/* 1387 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.formatKeyShortcut = undefined;

var _devtoolsModules = __webpack_require__(1376);

const { appinfo } = _devtoolsModules.Services; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/**
 * Utils for keyboard command strings
 * @module utils/text
 */


const isMacOS = appinfo.OS === "Darwin";

/**
 * Formats key for use in tooltips
 * For macOS we use the following unicode
 *
 * cmd ⌘ = \u2318
 * shift ⇧ – \u21E7
 * option (alt) ⌥ \u2325
 *
 * For Win/Lin this replaces CommandOrControl or CmdOrCtrl with Ctrl
 *
 * @memberof utils/text
 * @static
 */
function formatKeyShortcut(shortcut) {
  if (isMacOS) {
    return shortcut.replace(/Shift\+/g, "\u21E7 ").replace(/Command\+|Cmd\+/g, "\u2318 ").replace(/CommandOrControl\+|CmdOrCtrl\+/g, "\u2318 ").replace(/Alt\+/g, "\u2325 ");
  }
  return shortcut.replace(/CommandOrControl\+|CmdOrCtrl\+/g, `${L10N.getStr("ctrl")} `).replace(/Shift\+/g, "Shift ");
}

exports.formatKeyShortcut = formatKeyShortcut;

/***/ }),
/* 1388 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.copyToTheClipboard = copyToTheClipboard;
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/**
 * Clipboard function taken from
 * https://dxr.mozilla.org/mozilla-central/source/devtools/shared/platform/content/clipboard.js
 */
function copyToTheClipboard(string) {
  const doCopy = function (e) {
    e.clipboardData.setData("text/plain", string);
    e.preventDefault();
  };

  document.addEventListener("copy", doCopy);
  document.execCommand("copy", false, null);
  document.removeEventListener("copy", doCopy);
}

/***/ }),
/* 1389 */
/***/ (function(module, exports, __webpack_require__) {

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 md5 = __webpack_require__(248);

function originalToGeneratedId(originalId) {
  const match = originalId.match(/(.*)\/originalSource/);
  return match ? match[1] : "";
}

function generatedToOriginalId(generatedId, url) {
  return `${generatedId}/originalSource-${md5(url)}`;
}

function isOriginalId(id) {
  return !!id.match(/\/originalSource/);
}

function isGeneratedId(id) {
  return !isOriginalId(id);
}

/**
 * Trims the query part or reference identifier of a URL string, if necessary.
 */
function trimUrlQuery(url) {
  let length = url.length;
  let q1 = url.indexOf("?");
  let q2 = url.indexOf("&");
  let q3 = url.indexOf("#");
  let q = Math.min(q1 != -1 ? q1 : length, q2 != -1 ? q2 : length, q3 != -1 ? q3 : length);

  return url.slice(0, q);
}

// Map suffix to content type.
const contentMap = {
  "js": "text/javascript",
  "jsm": "text/javascript",
  "ts": "text/typescript",
  "tsx": "text/typescript-jsx",
  "jsx": "text/jsx",
  "coffee": "text/coffeescript",
  "elm": "text/elm",
  "cljs": "text/x-clojure"
};

/**
 * 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);
  let dot = url.lastIndexOf(".");
  if (dot >= 0) {
    let name = url.substring(dot + 1);
    if (name in contentMap) {
      return contentMap[name];
    }
  }
  return "text/plain";
}

module.exports = {
  originalToGeneratedId,
  generatedToOriginalId,
  isOriginalId,
  isGeneratedId,
  getContentType,
  contentMapForTesting: contentMap
};

/***/ }),
/* 1390 */,
/* 1391 */,
/* 1392 */,
/* 1393 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 basename(path) {
  return path.split("/").pop();
}

function dirname(path) {
  const idx = path.lastIndexOf("/");
  return path.slice(0, idx);
}

function isURL(str) {
  return str.indexOf("://") !== -1;
}

function isAbsolute(str) {
  return str[0] === "/";
}

function join(base, dir) {
  return `${base}/${dir}`;
}

exports.basename = basename;
exports.dirname = dirname;
exports.isURL = isURL;
exports.isAbsolute = isAbsolute;
exports.join = join;

/***/ }),
/* 1394 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getSelectedFrame = exports.getAllPopupObjectProperties = exports.createPauseState = undefined;

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; }; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                                                                                                                                                                                   * License, v. 2.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 complexity: ["error", 30]*/

/**
 * Pause reducer
 * @module reducers/pause
 */

exports.getPauseReason = getPauseReason;
exports.isStepping = isStepping;
exports.isPaused = isPaused;
exports.getPreviousPauseFrameLocation = getPreviousPauseFrameLocation;
exports.isEvaluatingExpression = isEvaluatingExpression;
exports.getPopupObjectProperties = getPopupObjectProperties;
exports.getIsWaitingOnBreak = getIsWaitingOnBreak;
exports.getShouldPauseOnExceptions = getShouldPauseOnExceptions;
exports.getShouldIgnoreCaughtExceptions = getShouldIgnoreCaughtExceptions;
exports.getCanRewind = getCanRewind;
exports.getFrames = getFrames;
exports.getGeneratedFrameScope = getGeneratedFrameScope;
exports.getFrameScopes = getFrameScopes;
exports.getFrameScope = getFrameScope;
exports.getSelectedScope = getSelectedScope;
exports.getSelectedFrameId = getSelectedFrameId;
exports.getTopFrame = getTopFrame;
exports.getDebuggeeUrl = getDebuggeeUrl;
exports.getChromeScopes = getChromeScopes;

var _reselect = __webpack_require__(993);

var _devtoolsSourceMap = __webpack_require__(1360);

var _prefs = __webpack_require__(226);

var _sources = __webpack_require__(1369);

const createPauseState = exports.createPauseState = () => ({
  why: null,
  isWaitingOnBreak: false,
  frames: undefined,
  selectedFrameId: undefined,
  frameScopes: {
    generated: {},
    original: {}
  },
  loadedObjects: {},
  shouldPauseOnExceptions: _prefs.prefs.pauseOnExceptions,
  shouldIgnoreCaughtExceptions: _prefs.prefs.ignoreCaughtExceptions,
  canRewind: false,
  debuggeeUrl: "",
  command: "",
  previousLocation: null
});

const emptyPauseState = {
  pause: null,
  frames: null,
  frameScopes: {
    generated: {},
    original: {}
  },
  selectedFrameId: null,
  loadedObjects: {},
  previousLocation: null
};

function update(state = createPauseState(), action) {
  switch (action.type) {
    case "PAUSED":
      {
        const { selectedFrameId, frames, loadedObjects, why } = action;

        // turn this into an object keyed by object id
        const objectMap = {};
        loadedObjects.forEach(obj => {
          objectMap[obj.value.objectId] = obj;
        });

        return _extends({}, state, {
          isWaitingOnBreak: false,
          selectedFrameId,
          frames,
          frameScopes: _extends({}, emptyPauseState.frameScopes),
          loadedObjects: objectMap,
          why
        });
      }

    case "MAP_FRAMES":
      {
        return _extends({}, state, { frames: action.frames });
      }

    case "ADD_SCOPES":
      {
        const { frame, status, value } = action;
        const selectedFrameId = frame.id;

        const generated = _extends({}, state.frameScopes.generated, {
          [selectedFrameId]: {
            pending: status !== "done",
            scope: value
          }
        });
        return _extends({}, state, {
          frameScopes: _extends({}, state.frameScopes, {
            generated
          })
        });
      }

    case "TRAVEL_TO":
      return _extends({}, state, action.data.paused);

    case "MAP_SCOPES":
      {
        const { frame, status, value } = action;
        const selectedFrameId = frame.id;

        const original = _extends({}, state.frameScopes.original, {
          [selectedFrameId]: {
            pending: status !== "done",
            scope: value
          }
        });
        return _extends({}, state, {
          frameScopes: _extends({}, state.frameScopes, {
            original
          })
        });
      }

    case "BREAK_ON_NEXT":
      return _extends({}, state, { isWaitingOnBreak: true });

    case "SELECT_FRAME":
      return _extends({}, state, {
        selectedFrameId: action.frame.id
      });

    case "SET_POPUP_OBJECT_PROPERTIES":
      if (!action.properties) {
        return _extends({}, state);
      }

      return _extends({}, state, {
        loadedObjects: _extends({}, state.loadedObjects, {
          [action.objectId]: action.properties
        })
      });

    case "CONNECT":
      return _extends({}, createPauseState(), {
        debuggeeUrl: action.url,
        canRewind: action.canRewind
      });

    case "PAUSE_ON_EXCEPTIONS":
      const { shouldPauseOnExceptions, shouldIgnoreCaughtExceptions } = action;

      _prefs.prefs.pauseOnExceptions = shouldPauseOnExceptions;
      _prefs.prefs.ignoreCaughtExceptions = shouldIgnoreCaughtExceptions;

      return _extends({}, state, {
        shouldPauseOnExceptions,
        shouldIgnoreCaughtExceptions
      });

    case "COMMAND":
      {
        return action.status === "start" ? _extends({}, state, emptyPauseState, {
          command: action.command,
          previousLocation: buildPreviousLocation(state, action)
        }) : _extends({}, state, { command: "" });
      }

    case "RESUME":
      // We clear why on resume because we need it to decide if
      // we shoul re-evaluate watch expressions.
      return _extends({}, state, { why: null });

    case "EVALUATE_EXPRESSION":
      return _extends({}, state, {
        command: action.status === "start" ? "expression" : ""
      });

    case "NAVIGATE":
      return _extends({}, state, emptyPauseState, { debuggeeUrl: action.url });
  }

  return state;
}

function buildPreviousLocation(state, action) {
  const { frames, previousLocation } = state;

  if (action.command !== "stepOver") {
    return null;
  }

  const frame = frames && frames.length > 0 ? frames[0] : null;
  if (!frame) {
    return previousLocation;
  }

  return {
    location: frame.location,
    generatedLocation: frame.generatedLocation
  };
}

// Selectors

// Unfortunately, it's really hard to make these functions accept just
// the state that we care about and still type it with Flow. The
// problem is that we want to re-export all selectors from a single
// module for the UI, and all of those selectors should take the
// top-level app state, so we'd have to "wrap" them to automatically
// pick off the piece of state we're interested in. It's impossible
// (right now) to type those wrapped functions.


const getPauseState = state => state.pause;

const getAllPopupObjectProperties = exports.getAllPopupObjectProperties = (0, _reselect.createSelector)(getPauseState, pauseWrapper => pauseWrapper.loadedObjects);

function getPauseReason(state) {
  return state.pause.why;
}

function isStepping(state) {
  return ["stepIn", "stepOver", "stepOut"].includes(state.pause.command);
}

function isPaused(state) {
  return !!getFrames(state);
}

function getPreviousPauseFrameLocation(state) {
  return state.pause.previousLocation;
}

function isEvaluatingExpression(state) {
  return state.pause.command === "expression";
}

function getPopupObjectProperties(state, objectId) {
  return getAllPopupObjectProperties(state)[objectId];
}

function getIsWaitingOnBreak(state) {
  return state.pause.isWaitingOnBreak;
}

function getShouldPauseOnExceptions(state) {
  return state.pause.shouldPauseOnExceptions;
}

function getShouldIgnoreCaughtExceptions(state) {
  return state.pause.shouldIgnoreCaughtExceptions;
}

function getCanRewind(state) {
  return state.pause.canRewind;
}

function getFrames(state) {
  return state.pause.frames;
}

function getGeneratedFrameScope(state, frameId) {
  if (!frameId) {
    return null;
  }

  return getFrameScopes(state).generated[frameId];
}

function getFrameScopes(state) {
  return state.pause.frameScopes;
}

function getFrameScope(state, sourceId, frameId) {
  if (!frameId || !sourceId) {
    return null;
  }

  const isGenerated = (0, _devtoolsSourceMap.isGeneratedId)(sourceId);
  const original = getFrameScopes(state).original[frameId];

  if (!isGenerated && original && (original.pending || original.scope)) {
    return original;
  }

  return getFrameScopes(state).generated[frameId];
}

function getSelectedScope(state) {
  const sourceRecord = (0, _sources.getSelectedSource)(state);
  const frameId = getSelectedFrameId(state);
  const { scope } = getFrameScope(state, sourceRecord && sourceRecord.get("id"), frameId) || {};
  return scope || null;
}

function getSelectedFrameId(state) {
  return state.pause.selectedFrameId;
}

function getTopFrame(state) {
  const frames = getFrames(state);
  return frames && frames[0];
}

const getSelectedFrame = exports.getSelectedFrame = (0, _reselect.createSelector)(getSelectedFrameId, getFrames, (selectedFrameId, frames) => {
  if (!frames) {
    return null;
  }
  return frames.find(frame => frame.id == selectedFrameId);
});

function getDebuggeeUrl(state) {
  return state.pause.debuggeeUrl;
}

// NOTE: currently only used for chrome
function getChromeScopes(state) {
  const frame = getSelectedFrame(state);
  return frame ? frame.scopeChain : undefined;
}

exports.default = update;

/***/ }),
/* 1395 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.findSourceMatches = exports.searchSources = exports.getMatches = exports.stopSearchWorker = exports.startSearchWorker = undefined;

var _devtoolsUtils = __webpack_require__(1363);

const { WorkerDispatcher } = _devtoolsUtils.workerUtils; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                          * License, v. 2.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 dispatcher = new WorkerDispatcher();
const startSearchWorker = exports.startSearchWorker = dispatcher.start.bind(dispatcher);
const stopSearchWorker = exports.stopSearchWorker = dispatcher.stop.bind(dispatcher);

const getMatches = exports.getMatches = dispatcher.task("getMatches");
const searchSources = exports.searchSources = dispatcher.task("searchSources");
const findSourceMatches = exports.findSourceMatches = dispatcher.task("findSourceMatches");

/***/ }),
/* 1396 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

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; }; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                                                                                                                                                                                   * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                                                                                                                                                                                                                                   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/**
 * Redux actions for breakpoints
 * @module actions/breakpoints
 */

// this will need to be changed so that addCLientBreakpoint is removed


exports.syncBreakpoint = syncBreakpoint;
exports.addBreakpoint = addBreakpoint;
exports.addHiddenBreakpoint = addHiddenBreakpoint;
exports.removeBreakpoint = removeBreakpoint;
exports.enableBreakpoint = enableBreakpoint;
exports.disableBreakpoint = disableBreakpoint;
exports.toggleAllBreakpoints = toggleAllBreakpoints;
exports.toggleBreakpoints = toggleBreakpoints;
exports.removeAllBreakpoints = removeAllBreakpoints;
exports.removeBreakpoints = removeBreakpoints;
exports.remapBreakpoints = remapBreakpoints;
exports.setBreakpointCondition = setBreakpointCondition;
exports.toggleBreakpoint = toggleBreakpoint;
exports.addOrToggleDisabledBreakpoint = addOrToggleDisabledBreakpoint;
exports.toggleDisabledBreakpoint = toggleDisabledBreakpoint;

var _promise = __webpack_require__(1653);

var _selectors = __webpack_require__(1352);

var _breakpoint = __webpack_require__(1364);

var _addBreakpoint = __webpack_require__(1519);

var _addBreakpoint2 = _interopRequireDefault(_addBreakpoint);

var _remapLocations = __webpack_require__(1520);

var _remapLocations2 = _interopRequireDefault(_remapLocations);

var _ast = __webpack_require__(1383);

var _syncBreakpoint = __webpack_require__(1521);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/**
 * Syncing a breakpoint add breakpoint information that is stored, and
 * contact the server for more data.
 *
 * @memberof actions/breakpoints
 * @static
 * @param {String} $1.sourceId String  value
 * @param {PendingBreakpoint} $1.location PendingBreakpoint  value
 */
function syncBreakpoint(sourceId, pendingBreakpoint) {
  return async ({ dispatch, getState, client, sourceMaps }) => {
    const { breakpoint, previousLocation } = await (0, _syncBreakpoint.syncClientBreakpoint)(getState, client, sourceMaps, sourceId, pendingBreakpoint);

    return dispatch({
      type: "SYNC_BREAKPOINT",
      breakpoint,
      previousLocation
    });
  };
}

/**
 * Add a new breakpoint
 *
 * @memberof actions/breakpoints
 * @static
 * @param {String} $1.condition Conditional breakpoint condition value
 * @param {Boolean} $1.disabled Disable value for breakpoint value
 */

function addBreakpoint(location, { condition, hidden } = {}) {
  const breakpoint = (0, _breakpoint.createBreakpoint)(location, { condition, hidden });
  return ({ dispatch, getState, sourceMaps, client }) => {
    const action = { type: "ADD_BREAKPOINT", breakpoint };
    const promise = (0, _addBreakpoint2.default)(getState, client, sourceMaps, action);
    return dispatch(_extends({}, action, { [_promise.PROMISE]: promise }));
  };
}

/**
 * Add a new hidden breakpoint
 *
 * @memberOf actions/breakpoints
 * @param location
 * @return {function(ThunkArgs)}
 */
function addHiddenBreakpoint(location) {
  return ({ dispatch }) => {
    return dispatch(addBreakpoint(location, { hidden: true }));
  };
}

/**
 * Remove a single breakpoint
 *
 * @memberof actions/breakpoints
 * @static
 */
function removeBreakpoint(location) {
  return ({ dispatch, getState, client }) => {
    const bp = (0, _selectors.getBreakpoint)(getState(), location);
    if (!bp || bp.loading) {
      return;
    }

    // If the breakpoint is already disabled, we don't need to communicate
    // with the server. We just need to dispatch an action
    // simulating a successful server request
    if (bp.disabled) {
      return dispatch({
        type: "REMOVE_BREAKPOINT",
        breakpoint: bp,
        status: "done"
      });
    }

    return dispatch({
      type: "REMOVE_BREAKPOINT",
      breakpoint: bp,
      [_promise.PROMISE]: client.removeBreakpoint(bp.generatedLocation)
    });
  };
}

/**
 * Enabling a breakpoint
 * will reuse the existing breakpoint information that is stored.
 *
 * @memberof actions/breakpoints
 * @static
 * @param {Location} $1.location Location  value
 */
function enableBreakpoint(location) {
  return async ({ dispatch, getState, client, sourceMaps }) => {
    const breakpoint = (0, _selectors.getBreakpoint)(getState(), location);
    if (!breakpoint || breakpoint.loading) {
      return;
    }

    const action = { type: "ENABLE_BREAKPOINT", breakpoint };
    const promise = (0, _addBreakpoint2.default)(getState, client, sourceMaps, action);
    return dispatch({
      type: "ENABLE_BREAKPOINT",
      breakpoint,
      [_promise.PROMISE]: promise
    });
  };
}

/**
 * Disable a single breakpoint
 *
 * @memberof actions/breakpoints
 * @static
 */
function disableBreakpoint(location) {
  return async ({ dispatch, getState, client }) => {
    const bp = (0, _selectors.getBreakpoint)(getState(), location);

    if (!bp || bp.loading) {
      return;
    }

    await client.removeBreakpoint(bp.generatedLocation);
    const newBreakpoint = _extends({}, bp, { disabled: true });

    return dispatch({
      type: "DISABLE_BREAKPOINT",
      breakpoint: newBreakpoint
    });
  };
}

/**
 * Toggle All Breakpoints
 *
 * @memberof actions/breakpoints
 * @static
 */
function toggleAllBreakpoints(shouldDisableBreakpoints) {
  return async ({ dispatch, getState }) => {
    const breakpoints = (0, _selectors.getBreakpoints)(getState());
    for (const [, breakpoint] of breakpoints) {
      if (shouldDisableBreakpoints) {
        await dispatch(disableBreakpoint(breakpoint.location));
      } else {
        await dispatch(enableBreakpoint(breakpoint.location));
      }
    }
  };
}

/**
 * Toggle Breakpoints
 *
 * @memberof actions/breakpoints
 * @static
 */
function toggleBreakpoints(shouldDisableBreakpoints, breakpoints) {
  return async ({ dispatch }) => {
    for (const [, breakpoint] of breakpoints) {
      if (shouldDisableBreakpoints) {
        await dispatch(disableBreakpoint(breakpoint.location));
      } else {
        await dispatch(enableBreakpoint(breakpoint.location));
      }
    }
  };
}

/**
 * Removes all breakpoints
 *
 * @memberof actions/breakpoints
 * @static
 */
function removeAllBreakpoints() {
  return async ({ dispatch, getState }) => {
    const breakpoints = (0, _selectors.getBreakpoints)(getState());
    for (const [, breakpoint] of breakpoints) {
      await dispatch(removeBreakpoint(breakpoint.location));
    }
  };
}

/**
 * Removes breakpoints
 *
 * @memberof actions/breakpoints
 * @static
 */
function removeBreakpoints(breakpoints) {
  return async ({ dispatch }) => {
    for (const [, breakpoint] of breakpoints) {
      await dispatch(removeBreakpoint(breakpoint.location));
    }
  };
}

function remapBreakpoints(sourceId) {
  return async ({ dispatch, getState, sourceMaps }) => {
    const breakpoints = (0, _selectors.getBreakpoints)(getState());
    const newBreakpoints = await (0, _remapLocations2.default)(breakpoints, sourceId, sourceMaps);

    return dispatch({
      type: "REMAP_BREAKPOINTS",
      breakpoints: newBreakpoints
    });
  };
}

/**
 * Update the condition of a breakpoint.
 *
 * @throws {Error} "not implemented"
 * @memberof actions/breakpoints
 * @static
 * @param {Location} location
 *        @see DebuggerController.Breakpoints.addBreakpoint
 * @param {string} condition
 *        The condition to set on the breakpoint
 * @param {Boolean} $1.disabled Disable value for breakpoint value
 */
function setBreakpointCondition(location, { condition } = {}) {
  return async ({ dispatch, getState, client, sourceMaps }) => {
    const bp = (0, _selectors.getBreakpoint)(getState(), location);
    if (!bp) {
      return dispatch(addBreakpoint(location, { condition }));
    }

    if (bp.loading) {
      return;
    }

    if (bp.disabled) {
      await dispatch(enableBreakpoint(location));
      bp.disabled = !bp.disabled;
    }

    await client.setBreakpointCondition(bp.id, location, condition, sourceMaps.isOriginalId(bp.location.sourceId));

    const newBreakpoint = _extends({}, bp, { condition });

    (0, _breakpoint.assertBreakpoint)(newBreakpoint);

    return dispatch({
      type: "SET_BREAKPOINT_CONDITION",
      breakpoint: newBreakpoint
    });
  };
}

function toggleBreakpoint(line, column) {
  return ({ dispatch, getState, client, sourceMaps }) => {
    if (!line) {
      return;
    }

    const state = getState();
    const selectedSource = (0, _selectors.getSelectedSource)(state);
    const bp = (0, _selectors.getBreakpointAtLocation)(state, { line, column });
    const isEmptyLine = (0, _ast.isEmptyLineInSource)(state, line, selectedSource.toJS());

    if (!bp && isEmptyLine || bp && bp.loading) {
      return;
    }

    if (bp) {
      // NOTE: it's possible the breakpoint has slid to a column
      return dispatch(removeBreakpoint({
        sourceId: bp.location.sourceId,
        sourceUrl: bp.location.sourceUrl,
        line: bp.location.line,
        column: column || bp.location.column
      }));
    }
    return dispatch(addBreakpoint({
      sourceId: selectedSource.get("id"),
      sourceUrl: selectedSource.get("url"),
      line: line,
      column: column
    }));
  };
}

function addOrToggleDisabledBreakpoint(line, column) {
  return ({ dispatch, getState, client, sourceMaps }) => {
    if (!line) {
      return;
    }

    const selectedSource = (0, _selectors.getSelectedSource)(getState());
    const bp = (0, _selectors.getBreakpointAtLocation)(getState(), { line, column });

    if (bp && bp.loading) {
      return;
    }

    if (bp) {
      // NOTE: it's possible the breakpoint has slid to a column
      return dispatch(toggleDisabledBreakpoint(line, column || bp.location.column));
    }

    return dispatch(addBreakpoint({
      sourceId: selectedSource.get("id"),
      sourceUrl: selectedSource.get("url"),
      line: line,
      column: column
    }));
  };
}

function toggleDisabledBreakpoint(line, column) {
  return ({ dispatch, getState, client, sourceMaps }) => {
    const bp = (0, _selectors.getBreakpointAtLocation)(getState(), { line, column });
    if (!bp || bp.loading) {
      return;
    }

    if (!bp.disabled) {
      return dispatch(disableBreakpoint(bp.location));
    }
    return dispatch(enableBreakpoint(bp.location));
  };
}

/***/ }),
/* 1397 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getGeneratedLocation = getGeneratedLocation;

var _selectors = __webpack_require__(1352);

async function getGeneratedLocation(state, source, location, sourceMaps) {
  if (!sourceMaps.isOriginalId(location.sourceId)) {
    return location;
  }

  const { line, sourceId, column } = await sourceMaps.getGeneratedLocation(location, source);

  const generatedSource = (0, _selectors.getSource)(state, sourceId);
  const sourceUrl = generatedSource.get("url");
  return {
    line,
    sourceId,
    column: column === 0 ? undefined : column,
    sourceUrl
  };
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/***/ }),
/* 1398 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.addExpression = addExpression;
exports.clearExpressionError = clearExpressionError;
exports.updateExpression = updateExpression;
exports.deleteExpression = deleteExpression;
exports.evaluateExpressions = evaluateExpressions;
exports.getMappedExpression = getMappedExpression;

var _selectors = __webpack_require__(1352);

var _promise = __webpack_require__(1653);

var _devtoolsSourceMap = __webpack_require__(1360);

var _expressions = __webpack_require__(1437);

var _parser = __webpack_require__(1365);

var parser = _interopRequireWildcard(_parser);

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

/**
 * Add expression for debugger to watch
 *
 * @param {object} expression
 * @param {number} expression.id
 * @memberof actions/pause
 * @static
 */
function addExpression(input) {
  return async ({ dispatch, getState }) => {
    if (!input) {
      return;
    }

    const expression = (0, _selectors.getExpression)(getState(), input);
    if (expression) {
      return dispatch(evaluateExpression(expression));
    }

    const expressionError = await parser.hasSyntaxError(input);
    dispatch({ type: "ADD_EXPRESSION", input, expressionError });

    const newExpression = (0, _selectors.getExpression)(getState(), input);
    if (newExpression) {
      return dispatch(evaluateExpression(newExpression));
    }
  };
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.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 clearExpressionError() {
  return { type: "CLEAR_EXPRESSION_ERROR" };
}

function updateExpression(input, expression) {
  return async ({ dispatch, getState }) => {
    if (!input || input == expression.input) {
      return;
    }

    const expressionError = await parser.hasSyntaxError(input);
    dispatch({
      type: "UPDATE_EXPRESSION",
      expression,
      input: expressionError ? expression.input : input,
      expressionError
    });

    dispatch(evaluateExpressions());
  };
}

/**
 *
 * @param {object} expression
 * @param {number} expression.id
 * @memberof actions/pause
 * @static
 */
function deleteExpression(expression) {
  return ({ dispatch }) => {
    dispatch({
      type: "DELETE_EXPRESSION",
      input: expression.input
    });
  };
}

/**
 *
 * @memberof actions/pause
 * @param {number} selectedFrameId
 * @static
 */
function evaluateExpressions() {
  return async function ({ dispatch, getState, client }) {
    const expressions = (0, _selectors.getExpressions)(getState()).toJS();
    for (const expression of expressions) {
      await dispatch(evaluateExpression(expression));
    }
  };
}

function evaluateExpression(expression) {
  return async function ({ dispatch, getState, client, sourceMaps }) {
    if (!expression.input) {
      console.warn("Expressions should not be empty");
      return;
    }

    let input = expression.input;
    const frame = (0, _selectors.getSelectedFrame)(getState());

    if (frame) {
      const { location, generatedLocation } = frame;
      const source = (0, _selectors.getSource)(getState(), location.sourceId);
      const sourceId = source.get("id");

      if (!(0, _devtoolsSourceMap.isGeneratedId)(sourceId)) {
        input = await getMappedExpression({ sourceMaps }, generatedLocation, input);
      }
    }

    const frameId = (0, _selectors.getSelectedFrameId)(getState());

    return dispatch({
      type: "EVALUATE_EXPRESSION",
      input: expression.input,
      [_promise.PROMISE]: client.evaluate((0, _expressions.wrapExpression)(input), { frameId })
    });
  };
}

/**
 * Gets information about original variable names from the source map
 * and replaces all posible generated names.
 */
async function getMappedExpression({ sourceMaps }, generatedLocation, expression) {
  return expression;
}

/***/ }),
/* 1399 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.setSourceMetaData = setSourceMetaData;
exports.setSymbols = setSymbols;
exports.setEmptyLines = setEmptyLines;
exports.setOutOfScopeLocations = setOutOfScopeLocations;

var _selectors = __webpack_require__(1352);

var _setInScopeLines = __webpack_require__(1781);

var _parser = __webpack_require__(1365);

function setSourceMetaData(sourceId) {
  return async ({ dispatch, getState }) => {
    const sourceRecord = (0, _selectors.getSource)(getState(), sourceId);
    if (!sourceRecord) {
      return;
    }

    const source = sourceRecord.toJS();
    if (!source.text || source.isWasm) {
      return;
    }

    const framework = await (0, _parser.getFramework)(source.id);
    dispatch({
      type: "SET_SOURCE_METADATA",
      sourceId: source.id,
      sourceMetaData: {
        framework
      }
    });
  };
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.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 setSymbols(sourceId) {
  return async ({ dispatch, getState }) => {
    const sourceRecord = (0, _selectors.getSource)(getState(), sourceId);
    if (!sourceRecord) {
      return;
    }

    const source = sourceRecord.toJS();
    if (!source.text || source.isWasm || (0, _selectors.hasSymbols)(getState(), source)) {
      return;
    }

    const symbols = await (0, _parser.getSymbols)(source.id);
    dispatch({ type: "SET_SYMBOLS", source, symbols });
    dispatch(setEmptyLines(sourceId));
    dispatch(setSourceMetaData(sourceId));
  };
}

function setEmptyLines(sourceId) {
  return async ({ dispatch, getState }) => {
    const sourceRecord = (0, _selectors.getSource)(getState(), sourceId);
    if (!sourceRecord) {
      return;
    }

    const source = sourceRecord.toJS();
    if (!source.text || source.isWasm) {
      return;
    }

    const emptyLines = await (0, _parser.getEmptyLines)(source.id);

    dispatch({
      type: "SET_EMPTY_LINES",
      source,
      emptyLines
    });
  };
}

function setOutOfScopeLocations() {
  return async ({ dispatch, getState }) => {
    const location = (0, _selectors.getSelectedLocation)(getState());
    if (!location) {
      return;
    }

    const source = (0, _selectors.getSource)(getState(), location.sourceId);

    let locations = null;
    if (location.line && source && (0, _selectors.isPaused)(getState())) {
      locations = await (0, _parser.findOutOfScopeLocations)(source.get("id"), location);
    }

    dispatch({
      type: "OUT_OF_SCOPE_LOCATIONS",
      locations
    });

    dispatch((0, _setInScopeLines.setInScopeLines)());
  };
}

/***/ }),
/* 1400 */,
/* 1401 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.renderWasmText = exports.clearWasmStates = exports.wasmOffsetToLine = exports.lineToWasmOffset = exports.isWasm = exports.getWasmLineNumberFormatter = exports.getWasmText = undefined;

var _WasmParser = __webpack_require__(677);

var _WasmDis = __webpack_require__(678);

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

var wasmStates = Object.create(null);

/**
 * @memberof utils/wasm
 * @static
 */
function getWasmText(sourceId, 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;
  const lines = [];
  for (let i = 0; i < offsets.length; i++) {
    lines[offsets[i]] = i;
  }

  wasmStates[sourceId] = { offsets, lines };

  return { lines: result.lines, done: result.done };
}

/**
 * @memberof utils/wasm
 * @static
 */
function getWasmLineNumberFormatter(sourceId) {
  const codeOf0 = 48,
        codeOfA = 65;
  const buffer = [codeOf0, codeOf0, codeOf0, codeOf0, codeOf0, codeOf0, codeOf0, codeOf0];
  let last0 = 7;
  return function (number) {
    const offset = lineToWasmOffset(sourceId, number - 1);
    if (offset == undefined) {
      return "";
    }
    let i = 7;
    for (let n = offset; 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);
  };
}

/**
 * @memberof utils/wasm
 * @static
 */
function isWasm(sourceId) {
  return sourceId in wasmStates;
}

/**
 * @memberof utils/wasm
 * @static
 */
function lineToWasmOffset(sourceId, number) {
  const wasmState = wasmStates[sourceId];
  if (!wasmState) {
    return undefined;
  }
  let offset = wasmState.offsets[number];
  while (offset === undefined && number > 0) {
    offset = wasmState.offsets[--number];
  }
  return offset;
}

/**
 * @memberof utils/wasm
 * @static
 */
function wasmOffsetToLine(sourceId, offset) {
  const wasmState = wasmStates[sourceId];
  if (!wasmState) {
    return undefined;
  }
  return wasmState.lines[offset];
}

/**
 * @memberof utils/wasm
 * @static
 */
function clearWasmStates() {
  wasmStates = Object.create(null);
}

function renderWasmText(sourceId, { binary }) {
  // binary does not survive as Uint8Array, converting from string
  const data = new Uint8Array(binary.length);
  for (let i = 0; i < data.length; i++) {
    data[i] = binary.charCodeAt(i);
  }
  const { lines } = getWasmText(sourceId, data);
  const MAX_LINES = 1000000;
  if (lines.length > MAX_LINES) {
    lines.splice(MAX_LINES, lines.length - MAX_LINES);
    lines.push(";; .... text is truncated due to the size");
  }
  return lines;
}

exports.getWasmText = getWasmText;
exports.getWasmLineNumberFormatter = getWasmLineNumberFormatter;
exports.isWasm = isWasm;
exports.lineToWasmOffset = lineToWasmOffset;
exports.wasmOffsetToLine = wasmOffsetToLine;
exports.clearWasmStates = clearWasmStates;
exports.renderWasmText = renderWasmText;

/***/ }),
/* 1402 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = buildQuery;

var _escapeRegExp = __webpack_require__(259);

var _escapeRegExp2 = _interopRequireDefault(_escapeRegExp);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/**
 * Ignore doing outline matches for less than 3 whitespaces
 *
 * @memberof utils/source-search
 * @static
 */
function ignoreWhiteSpace(str) {
  return (/^\s{0,2}$/.test(str) ? "(?!\\s*.*)" : str
  );
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.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 wholeMatch(query, wholeWord) {
  if (query === "" || !wholeWord) {
    return query;
  }

  return `\\b${query}\\b`;
}

function buildFlags(caseSensitive, isGlobal) {
  if (caseSensitive && isGlobal) {
    return "g";
  }

  if (!caseSensitive && isGlobal) {
    return "gi";
  }

  if (!caseSensitive && !isGlobal) {
    return "i";
  }

  return;
}

function buildQuery(originalQuery, modifiers, { isGlobal = false, ignoreSpaces = false }) {
  const { caseSensitive, regexMatch, wholeWord } = modifiers;

  if (originalQuery === "") {
    return new RegExp(originalQuery);
  }

  let query = originalQuery;
  if (ignoreSpaces) {
    query = ignoreWhiteSpace(query);
  }

  if (!regexMatch) {
    query = (0, _escapeRegExp2.default)(query);
  }

  query = wholeMatch(query, wholeWord);
  const flags = buildFlags(caseSensitive, isGlobal);

  if (flags) {
    return new RegExp(query, flags);
  }

  return new RegExp(query);
}

/***/ }),
/* 1403 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.Modal = exports.transitionTimeout = undefined;
exports.default = Slide;

var _propTypes = __webpack_require__(20);

var _propTypes2 = _interopRequireDefault(_propTypes);

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _classnames = __webpack_require__(175);

var _classnames2 = _interopRequireDefault(_classnames);

var _Transition = __webpack_require__(333);

var _Transition2 = _interopRequireDefault(_Transition);

__webpack_require__(1303);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

const transitionTimeout = exports.transitionTimeout = 175; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                            * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                            * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

class Modal extends _react2.default.Component {
  constructor(...args) {
    var _temp;

    return _temp = super(...args), this.onClick = e => {
      e.stopPropagation();
    }, _temp;
  }

  render() {
    const { status } = this.props;

    return _react2.default.createElement(
      "div",
      { className: "modal-wrapper", onClick: this.props.handleClose },
      _react2.default.createElement(
        "div",
        {
          className: (0, _classnames2.default)("modal", this.props.additionalClass, status),
          onClick: this.onClick
        },
        this.props.children
      )
    );
  }
}

exports.Modal = Modal;
Modal.contextTypes = {
  shortcuts: _propTypes2.default.object
};

function Slide({
  in: inProp,
  children,
  additionalClass,
  handleClose
}) {
  return _react2.default.createElement(
    _Transition2.default,
    { "in": inProp, timeout: transitionTimeout, appear: true },
    status => _react2.default.createElement(
      Modal,
      {
        status: status,
        additionalClass: additionalClass,
        handleClose: handleClose
      },
      children
    )
  );
}

/***/ }),
/* 1404 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

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; }; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                                                                                                                                                                                   * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                                                                                                                                                                                                                                   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

__webpack_require__(1311);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

const { Tree } = __webpack_require__(1441);

class ManagedTree extends _react.Component {
  constructor(props) {
    super(props);

    this.setExpanded = (item, isExpanded, shouldIncludeChildren) => {
      const expandItem = i => {
        const path = this.props.getPath(i);
        if (isExpanded) {
          expanded.add(path);
        } else {
          expanded.delete(path);
        }
      };
      const { expanded } = this.state;
      expandItem(item);

      if (shouldIncludeChildren) {
        let parents = [item];
        while (parents.length) {
          const children = [];
          for (const parent of parents) {
            if (parent.contents && parent.contents.length) {
              for (const child of parent.contents) {
                expandItem(child);
                children.push(child);
              }
            }
          }
          parents = children;
        }
      }
      this.setState({ expanded });

      if (isExpanded && this.props.onExpand) {
        this.props.onExpand(item, expanded);
      } else if (!isExpanded && this.props.onCollapse) {
        this.props.onCollapse(item, expanded);
      }
    };

    this.focusItem = item => {
      if (!this.props.disabledFocus && this.state.focusedItem !== item) {
        this.setState({ focusedItem: item });

        if (this.props.onFocus) {
          this.props.onFocus(item);
        }
      }
    };

    this.state = {
      expanded: props.expanded || new Set(),
      focusedItem: null
    };
  }

  componentWillReceiveProps(nextProps) {
    const { listItems, highlightItems, focused } = this.props;
    if (nextProps.listItems && nextProps.listItems != listItems && nextProps.listItems.length) {
      this.expandListItems(nextProps.listItems);
    }

    if (nextProps.highlightItems && nextProps.highlightItems != highlightItems && nextProps.highlightItems.length) {
      this.highlightItem(nextProps.highlightItems);
    }

    if (nextProps.focused && nextProps.focused !== focused) {
      this.focusItem(nextProps.focused);
    }
  }

  expandListItems(listItems) {
    const { expanded } = this.state;
    listItems.forEach(item => expanded.add(this.props.getPath(item)));
    this.focusItem(listItems[0]);
    this.setState({ expanded });
  }

  highlightItem(highlightItems) {
    const { expanded } = this.state;
    // This file is visible, so we highlight it.
    if (expanded.has(this.props.getPath(highlightItems[0]))) {
      this.focusItem(highlightItems[0]);
    } else {
      // Look at folders starting from the top-level until finds a
      // closed folder and highlights this folder
      const index = highlightItems.reverse().findIndex(item => !expanded.has(this.props.getPath(item)));
      this.focusItem(highlightItems[index]);
    }
  }

  render() {
    const { expanded, focusedItem } = this.state;
    return _react2.default.createElement(
      "div",
      { className: "managed-tree" },
      _react2.default.createElement(Tree, _extends({}, this.props, {
        isExpanded: item => expanded.has(this.props.getPath(item)),
        focused: focusedItem,
        getKey: this.props.getPath,
        onExpand: item => this.setExpanded(item, true, false),
        onCollapse: item => this.setExpanded(item, false, false),
        onFocus: this.focusItem,
        renderItem: (...args) => this.props.renderItem(...args, {
          setExpanded: this.setExpanded
        })
      }))
    );
  }
}

exports.default = ManagedTree;

/***/ }),
/* 1405 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getFilenameFromPath = getFilenameFromPath;
exports.getURL = getURL;

var _url = __webpack_require__(334);

var _lodash = __webpack_require__(2);

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

function getFilenameFromPath(pathname) {
  let filename = "";
  if (pathname) {
    filename = pathname.substring(pathname.lastIndexOf("/") + 1);
    // This file does not have a name. Default should be (index).
    if (filename == "" || !filename.includes(".")) {
      filename = "(index)";
    }
  }
  return filename;
}

const NoDomain = "(no domain)";
function getURL(sourceUrl, debuggeeUrl = "") {
  const url = sourceUrl;
  const def = { path: "", group: "", filename: "" };
  if (!url) {
    return def;
  }

  const { pathname, protocol, host, path } = (0, _url.parse)(url);
  const defaultDomain = (0, _url.parse)(debuggeeUrl).host;
  const filename = getFilenameFromPath(pathname);

  switch (protocol) {
    case "javascript:":
      // Ignore `javascript:` URLs for now
      return def;

    case "webpack:":
      // A Webpack source is a special case
      return (0, _lodash.merge)(def, {
        path: path,
        group: "Webpack",
        filename: filename
      });

    case "ng:":
      // An Angular source is a special case
      return (0, _lodash.merge)(def, {
        path: path,
        group: "Angular",
        filename: filename
      });

    case "about:":
      // An about page is a special case
      return (0, _lodash.merge)(def, {
        path: "/",
        group: url,
        filename: filename
      });

    case "data:":
      return (0, _lodash.merge)(def, {
        path: "/",
        group: NoDomain,
        filename: url
      });

    case null:
      if (pathname && pathname.startsWith("/")) {
        // If it's just a URL like "/foo/bar.js", resolve it to the file
        // protocol
        return (0, _lodash.merge)(def, {
          path: path,
          group: "file://",
          filename: filename
        });
      } else if (host === null) {
        // We don't know what group to put this under, and it's a script
        // with a weird URL. Just group them all under an anonymous group.
        return (0, _lodash.merge)(def, {
          path: url,
          group: defaultDomain,
          filename: filename
        });
      }
      break;

    case "http:":
    case "https:":
      return (0, _lodash.merge)(def, {
        path: pathname,
        group: host,
        filename: filename
      });
  }

  return (0, _lodash.merge)(def, {
    path: path,
    group: protocol ? `${protocol}//` : "",
    filename: filename
  });
}

/***/ }),
/* 1406 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.scrollList = undefined;

var _devtoolsConfig = __webpack_require__(1355);

var _Modal = __webpack_require__(1403);

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 scrollList(resultList, index, delayed = false) {
  if (!resultList.hasOwnProperty(index)) {
    return;
  }

  const resultEl = resultList[index];

  const scroll = () => {
    if ((0, _devtoolsConfig.isFirefox)()) {
      resultEl.scrollIntoView({ block: "center", behavior: "smooth" });
    } else {
      chromeScrollList(resultEl, index);
    }
  };

  if (delayed) {
    // Wait for Modal Transition timeout before scrolling to resultEl.
    setTimeout(scroll, _Modal.transitionTimeout + 10);
    return;
  }

  scroll();
}

function chromeScrollList(elem, index) {
  const resultsEl = elem.parentNode;
  if (!resultsEl || resultsEl.children.length === 0) {
    return;
  }

  const resultsHeight = resultsEl.clientHeight;
  const itemHeight = resultsEl.children[0].clientHeight;
  const numVisible = resultsHeight / itemHeight;
  const positionsToScroll = index - numVisible + 1;
  const itemOffset = resultsHeight % itemHeight;
  const scroll = positionsToScroll * (itemHeight + 2) + itemOffset;

  resultsEl.scrollTop = Math.max(0, scroll);
}

exports.scrollList = scrollList;

/***/ }),
/* 1407 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _classnames = __webpack_require__(175);

var _classnames2 = _interopRequireDefault(_classnames);

var _Svg = __webpack_require__(1359);

var _Svg2 = _interopRequireDefault(_Svg);

var _CommandBarButton = __webpack_require__(1764);

var _CommandBarButton2 = _interopRequireDefault(_CommandBarButton);

__webpack_require__(1321);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

class PaneToggleButton extends _react.PureComponent {

  render() {
    const { position, collapsed, horizontal, handleClick } = this.props;
    const title = !collapsed ? L10N.getStr("expandPanes") : L10N.getStr("collapsePanes");

    return _react2.default.createElement(
      _CommandBarButton2.default,
      {
        className: (0, _classnames2.default)("toggle-button", position, {
          collapsed,
          vertical: !horizontal
        }),
        onClick: () => handleClick(position, collapsed),
        title: title
      },
      _react2.default.createElement(_Svg2.default, { name: "togglePanes" })
    );
  }
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

PaneToggleButton.defaultProps = {
  horizontal: false
};
exports.default = PaneToggleButton;

/***/ }),
/* 1408 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 } = __webpack_require__(1357);
const { REPS, getRep } = __webpack_require__(1372);
const ObjectInspector = __webpack_require__(1585);
const ObjectInspectorUtils = __webpack_require__(1938);

const {
  parseURLEncodedText,
  parseURLParams,
  maybeEscapePropertyName,
  getGripPreviewItems
} = __webpack_require__(1353);

module.exports = {
  REPS,
  getRep,
  MODE,
  maybeEscapePropertyName,
  parseURLEncodedText,
  parseURLParams,
  getGripPreviewItems,
  ObjectInspector,
  ObjectInspectorUtils
};

/***/ }),
/* 1409 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// ReactJS
const PropTypes = __webpack_require__(20);

// Dependencies
const {
  isGrip,
  wrapRender
} = __webpack_require__(1353);
const PropRep = __webpack_require__(1381);
const { MODE } = __webpack_require__(1357);

const dom = __webpack_require__(1758);
const { span } = dom;

/**
 * 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,
  // @TODO Change this to Object.values once it's supported in Node's version of V8
  mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
  isInterestingProp: PropTypes.func,
  title: PropTypes.string,
  onDOMNodeMouseOver: PropTypes.func,
  onDOMNodeMouseOut: PropTypes.func,
  onInspectIconClick: PropTypes.func,
  noGrip: PropTypes.bool
};

const DEFAULT_TITLE = "Object";

function GripRep(props) {
  let {
    mode = MODE.SHORT,
    object
  } = props;

  const config = {
    "data-link-actor-id": object.actor,
    className: "objectBox objectBox-object"
  };

  if (mode === MODE.TINY) {
    let 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",
        title: "more…"
      }, "…") : null, span({
        className: "objectRightBrace"
      }, "}"));
    }

    return span(config, ...tinyModeItems);
  }

  let propsArray = safePropIterator(props, object, maxLengthMap.get(mode));

  return span(config, getTitleElement(props, object), span({
    className: "objectLeftBrace"
  }, " { "), ...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;
  }

  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 } = __webpack_require__(1372);

    return [Rep({
      object: object.preview.wrappedValue,
      mode: props.mode || MODE.TINY,
      defaultRep: Grip
    })];
  }

  // Property filter. Show only interesting properties to the user.
  let isInterestingProp = props.isInterestingProp || ((type, value) => {
    return type == "boolean" || type == "number" || type == "string" && value.length != 0;
  });

  let properties = object.preview ? object.preview.ownProperties : {};
  let propertiesLength = getPropertiesLength(object);

  if (object.preview && object.preview.safeGetterValues) {
    properties = Object.assign({}, 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.
  const suppressQuotes = object.class === "Proxy";
  let propsArray = getProps(props, properties, indexes, suppressQuotes);

  // 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 => {
      return PropRep(Object.assign({}, props, {
        mode: MODE.TINY,
        name: symbolItem,
        object: symbolItem.descriptor.value,
        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",
      title: "more…"
    }, "…"));
  }

  return unfoldProps(propsArray);
}

function unfoldProps(items) {
  return items.reduce((res, item, index) => {
    if (Array.isArray(item)) {
      res = res.concat(item);
    } else {
      res.push(item);
    }

    // Interleave commas between elements
    if (index !== items.length - 1) {
      res.push(", ");
    }
    return res;
  }, []);
}

/**
 * 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 => {
    let name = propertiesKeys[i];
    let value = getPropValue(properties[name]);

    return PropRep(Object.assign({}, 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) {
  let indexes = [];

  try {
    let i = 0;
    for (let name in properties) {
      if (indexes.length >= max) {
        return indexes;
      }

      // Type is specified in grip's "class" field and for primitive
      // values use typeof.
      let 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") {
    let 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 (noGrip === true || !isGrip(object)) {
    return 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.
let Grip = {
  rep: wrapRender(GripRep),
  supportsObject,
  maxLengthMap
};

// Exports from this module
module.exports = Grip;

/***/ }),
/* 1410 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var _svgInlineReact = __webpack_require__(1763);

var _svgInlineReact2 = _interopRequireDefault(_svgInlineReact);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 React = __webpack_require__(0);
const PropTypes = __webpack_require__(20);


const svg = {
  "open-inspector": __webpack_require__(1153),
  "jump-definition": __webpack_require__(2012)
};

Svg.propTypes = {
  className: PropTypes.string
};

function Svg(name, props) {
  if (!svg[name]) {
    throw new Error("Unknown SVG: " + name);
  }
  let className = name;
  if (props && props.className) {
    className = `${name} ${props.className}`;
  }
  if (name === "subSettings") {
    className = "";
  }
  props = Object.assign({}, props, { className, src: svg[name] });
  return React.createElement(_svgInlineReact2.default, props);
}

module.exports = Svg;

/***/ }),
/* 1411 */,
/* 1412 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
 * Returns a deferred object, with a resolve and reject property.
 * https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/Promise.jsm/Deferred
 */
module.exports = function defer() {
  let resolve, reject;
  let promise = new Promise(function () {
    resolve = arguments[0];
    reject = arguments[1];
  });
  return {
    resolve: resolve,
    reject: reject,
    promise: promise
  };
};

/***/ }),
/* 1413 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 { Menu, MenuItem } = __webpack_require__(1767);

function inToolbox() {
  return window.parent.document.documentURI == "about:devtools-toolbox";
}

if (!inToolbox()) {
  __webpack_require__(1301);
}

function createPopup(doc) {
  let popup = doc.createElement("menupopup");
  popup.className = "landing-popup";
  if (popup.openPopupAtScreen) {
    return popup;
  }

  function preventDefault(e) {
    e.preventDefault();
    e.returnValue = false;
  }

  let mask = document.querySelector("#contextmenu-mask");
  if (!mask) {
    mask = doc.createElement("div");
    mask.id = "contextmenu-mask";
    document.body.appendChild(mask);
  }

  mask.onclick = () => popup.hidePopup();

  popup.openPopupAtScreen = function (clientX, clientY) {
    this.style.setProperty("left", `${clientX}px`);
    this.style.setProperty("top", `${clientY}px`);
    mask = document.querySelector("#contextmenu-mask");
    window.onwheel = preventDefault;
    mask.classList.add("show");
    this.dispatchEvent(new Event("popupshown"));
    this.popupshown;
  };

  popup.hidePopup = function () {
    this.remove();
    mask = document.querySelector("#contextmenu-mask");
    mask.classList.remove("show");
    window.onwheel = null;
  };

  return popup;
}

if (!inToolbox()) {
  Menu.prototype.createPopup = createPopup;
}

function onShown(menu, popup) {
  popup.childNodes.forEach((menuItemNode, i) => {
    let item = menu.items[i];

    if (!item.disabled && item.visible) {
      menuItemNode.onclick = () => {
        item.click();
        popup.hidePopup();
      };

      showSubMenu(item.submenu, menuItemNode, popup);
    }
  });
}

function showMenu(evt, items) {
  if (items.length === 0) {
    return;
  }

  let menu = new Menu();
  items.filter(item => item.visible === undefined || item.visible === true).forEach(item => {
    let menuItem = new MenuItem(item);
    menuItem.submenu = createSubMenu(item.submenu);
    menu.append(menuItem);
  });

  if (inToolbox()) {
    menu.popup(evt.screenX, evt.screenY, { doc: window.parent.document });
    return;
  }

  menu.on("open", (_, popup) => onShown(menu, popup));
  menu.popup(evt.clientX, evt.clientY, { doc: document });
}

function createSubMenu(subItems) {
  if (subItems) {
    let subMenu = new Menu();
    subItems.forEach(subItem => {
      subMenu.append(new MenuItem(subItem));
    });
    return subMenu;
  }
  return null;
}

function showSubMenu(subMenu, menuItemNode, popup) {
  if (subMenu) {
    let subMenuNode = menuItemNode.querySelector("menupopup");
    let { top } = menuItemNode.getBoundingClientRect();
    let { left, width } = popup.getBoundingClientRect();
    subMenuNode.style.setProperty("left", `${left + width - 1}px`);
    subMenuNode.style.setProperty("top", `${top}px`);

    let subMenuItemNodes = menuItemNode.querySelector("menupopup:not(.landing-popup)").childNodes;
    subMenuItemNodes.forEach((subMenuItemNode, j) => {
      let subMenuItem = subMenu.items.filter(item => item.visible === undefined || item.visible === true)[j];
      if (!subMenuItem.disabled && subMenuItem.visible) {
        subMenuItemNode.onclick = () => {
          subMenuItem.click();
          popup.hidePopup();
        };
      }
    });
  }
}

function buildMenu(items) {
  return items.map(itm => {
    const hide = typeof itm.hidden === "function" ? itm.hidden() : itm.hidden;
    return hide ? null : itm.item;
  }).filter(itm => itm !== null);
}

module.exports = {
  showMenu,
  buildMenu
};

/***/ }),
/* 1414 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 Immutable = __webpack_require__(146);

// When our app state is fully types, we should be able to get rid of
// this function. This is only temporarily necessary to support
// converting typed objects to immutable.js, which usually happens in
// reducers.
function fromJS(value) {
  if (Array.isArray(value)) {
    return Immutable.Seq(value).map(fromJS).toList();
  }
  if (value && value.constructor.meta) {
    // This adds support for tcomb objects which are native JS objects
    // but are not "plain", so the above checks fail. Since they
    // behave the same we can use the same constructors, but we need
    // special checks for them.
    const kind = value.constructor.meta.kind;
    if (kind === "struct") {
      return Immutable.Seq(value).map(fromJS).toMap();
    } else if (kind === "list") {
      return Immutable.Seq(value).map(fromJS).toList();
    }
  }

  // If it's a primitive type, just return the value. Note `==` check
  // for null, which is intentionally used to match either `null` or
  // `undefined`.
  if (value == null || typeof value !== "object") {
    return value;
  }

  // Otherwise, treat it like an object. We can't reliably detect if
  // it's a plain object because we might be objects from other JS
  // contexts so `Object !== Object`.
  return Immutable.Seq(value).map(fromJS).toMap();
}

module.exports = fromJS;

/***/ }),
/* 1415 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 tabs = __webpack_require__(1497);
const config = __webpack_require__(1498);

module.exports = Object.assign({}, tabs, config);

/***/ }),
/* 1416 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.containsPosition = containsPosition;
exports.findClosestScope = findClosestScope;
exports.getASTLocation = getASTLocation;
exports.findScopeByName = findScopeByName;

var _parser = __webpack_require__(1365);

function containsPosition(a, b) {
  const startsBefore = a.start.line < b.line || a.start.line === b.line && a.start.column <= b.column;
  const endsAfter = a.end.line > b.line || a.end.line === b.line && a.end.column >= b.column;

  return startsBefore && endsAfter;
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.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 findClosestScope(functions, location) {
  return functions.reduce((found, currNode) => {
    if (currNode.name === "anonymous" || !containsPosition(currNode.location, location)) {
      return found;
    }

    if (!found) {
      return currNode;
    }

    if (found.location.start.line > currNode.location.start.line) {
      return found;
    }
    if (found.location.start.line === currNode.location.start.line && found.location.start.column > currNode.location.start.column) {
      return found;
    }

    return currNode;
  }, null);
}

function getASTLocation(source, symbols, location) {
  if (source.isWasm) {
    return { name: undefined, offset: location };
  }

  const functions = [...symbols.functions];

  const scope = findClosestScope(functions, location);
  if (scope) {
    // we only record the line, but at some point we may
    // also do column offsets
    const line = location.line - scope.location.start.line;
    return {
      name: scope.name,
      offset: { line, column: undefined }
    };
  }
  return { name: undefined, offset: location };
}

async function findScopeByName(source, name) {
  const symbols = await (0, _parser.getSymbols)(source.id);
  const functions = symbols.functions;

  return functions.find(node => node.name === name);
}

/***/ }),
/* 1417 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getExpressionError = exports.getExpressions = exports.createExpressionState = undefined;
exports.getExpression = getExpression;

var _makeRecord = __webpack_require__(1361);

var _makeRecord2 = _interopRequireDefault(_makeRecord);

var _immutable = __webpack_require__(146);

var _lodash = __webpack_require__(2);

var _reselect = __webpack_require__(993);

var _prefs = __webpack_require__(226);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

const createExpressionState = exports.createExpressionState = (0, _makeRecord2.default)({
  expressions: (0, _immutable.List)(restoreExpressions()),
  expressionError: false
}); /* This Source Code Form is subject to the terms of the Mozilla Public
     * License, v. 2.0. If a copy of the MPL was not distributed with this
     * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/**
 * Expressions reducer
 * @module reducers/expressions
 */

function update(state = createExpressionState(), action) {
  switch (action.type) {
    case "ADD_EXPRESSION":
      if (action.expressionError) {
        return state.set("expressionError", !!action.expressionError);
      }
      return appendToList(state, ["expressions"], {
        input: action.input,
        value: null,
        updating: true
      });
    case "UPDATE_EXPRESSION":
      const key = action.expression.input;
      return updateItemInList(state, ["expressions"], key, {
        input: action.input,
        value: null,
        updating: true
      }).set("expressionError", !!action.expressionError);
    case "EVALUATE_EXPRESSION":
      return updateItemInList(state, ["expressions"], action.input, {
        input: action.input,
        value: action.value,
        updating: false
      });
    case "DELETE_EXPRESSION":
      return deleteExpression(state, action.input);
    case "CLEAR_EXPRESSION_ERROR":
      return state.set("expressionError", false);

    // respond to time travel
    case "TRAVEL_TO":
      {
        return travelTo(state, action);
      }
  }

  return state;
}

function travelTo(state, action) {
  const { expressions } = action.data;
  if (!expressions) {
    return state;
  }
  return expressions.reduce((finalState, previousState) => updateItemInList(finalState, ["expressions"], previousState.input, {
    input: previousState.input,
    value: previousState.value,
    updating: false
  }), state);
}

function restoreExpressions() {
  const exprs = _prefs.prefs.expressions;
  if (exprs.length == 0) {
    return;
  }
  return exprs;
}

function storeExpressions(state) {
  const expressions = state.getIn(["expressions"]).map(expression => (0, _lodash.omit)(expression, "value")).toJS();

  _prefs.prefs.expressions = expressions;
}

function appendToList(state, path, value) {
  const newState = state.updateIn(path, () => {
    return state.getIn(path).push(value);
  });
  storeExpressions(newState);
  return newState;
}

function updateItemInList(state, path, key, value) {
  const newState = state.updateIn(path, () => {
    const list = state.getIn(path);
    const index = list.findIndex(e => e.input == key);
    return list.update(index, () => value);
  });
  storeExpressions(newState);
  return newState;
}

function deleteExpression(state, input) {
  const index = getExpressions({ expressions: state }).findKey(e => e.input == input);
  const newState = state.deleteIn(["expressions", index]);
  storeExpressions(newState);
  return newState;
}

const getExpressionsWrapper = state => state.expressions;

const getExpressions = exports.getExpressions = (0, _reselect.createSelector)(getExpressionsWrapper, expressions => expressions.get("expressions"));

function getExpression(state, input) {
  return getExpressions(state).find(exp => exp.input == input);
}

const getExpressionError = exports.getExpressionError = (0, _reselect.createSelector)(getExpressionsWrapper, expressions => expressions.get("expressionError"));

exports.default = update;

/***/ }),
/* 1418 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getWorkers = exports.createDebuggeeState = undefined;
exports.default = debuggee;
exports.getWorker = getWorker;

var _reselect = __webpack_require__(993);

var _immutable = __webpack_require__(146);

var _makeRecord = __webpack_require__(1361);

var _makeRecord2 = _interopRequireDefault(_makeRecord);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

const createDebuggeeState = exports.createDebuggeeState = (0, _makeRecord2.default)({
  workers: (0, _immutable.List)()
}); /* This Source Code Form is subject to the terms of the Mozilla Public
     * License, v. 2.0. If a copy of the MPL was not distributed with this
     * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/**
 * Debuggee reducer
 * @module reducers/debuggee
 */

function debuggee(state = createDebuggeeState(), action) {
  switch (action.type) {
    case "SET_WORKERS":
      return state.set("workers", (0, _immutable.List)(action.workers));
    default:
      return state;
  }
}

const getDebuggeeWrapper = state => state.debuggee;

const getWorkers = exports.getWorkers = (0, _reselect.createSelector)(getDebuggeeWrapper, debuggeeState => debuggeeState.get("workers"));

function getWorker(state, url) {
  return getWorkers(state).find(value => url);
}

/***/ }),
/* 1419 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.initialPendingBreakpointsState = initialPendingBreakpointsState;
exports.getPendingBreakpoints = getPendingBreakpoints;
exports.getPendingBreakpointsForSource = getPendingBreakpointsForSource;

var _immutable = __webpack_require__(146);

var I = _interopRequireWildcard(_immutable);

var _makeRecord = __webpack_require__(1361);

var _makeRecord2 = _interopRequireDefault(_makeRecord);

var _breakpoint = __webpack_require__(1364);

var _prefs = __webpack_require__(226);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/**
 * Pending breakpoints reducer
 * @module reducers/pending-breakpoints
 */

function initialPendingBreakpointsState() {
  return (0, _makeRecord2.default)({
    pendingBreakpoints: restorePendingBreakpoints()
  })();
}

function update(state = initialPendingBreakpointsState(), action) {
  switch (action.type) {
    case "ADD_BREAKPOINT":
      {
        if (action.breakpoint.hidden) {
          return state;
        }
        return addBreakpoint(state, action);
      }

    case "SYNC_BREAKPOINT":
      {
        return syncBreakpoint(state, action);
      }

    case "ENABLE_BREAKPOINT":
      {
        return addBreakpoint(state, action);
      }

    case "DISABLE_BREAKPOINT":
      {
        return updateBreakpoint(state, action);
      }

    case "SET_BREAKPOINT_CONDITION":
      {
        return updateBreakpoint(state, action);
      }

    case "REMOVE_BREAKPOINT":
      {
        if (action.breakpoint.hidden) {
          return state;
        }
        return removeBreakpoint(state, action);
      }
  }

  return state;
}

function addBreakpoint(state, action) {
  if (action.status !== "done") {
    return state;
  }
  // when the action completes, we can commit the breakpoint
  const { value: { breakpoint } } = action;
  const locationId = (0, _breakpoint.makePendingLocationId)(breakpoint.location);
  const pendingBreakpoint = (0, _breakpoint.createPendingBreakpoint)(breakpoint);

  return state.setIn(["pendingBreakpoints", locationId], pendingBreakpoint);
}

function syncBreakpoint(state, action) {
  const { breakpoint, previousLocation } = action;

  if (previousLocation) {
    state = state.deleteIn(["pendingBreakpoints", (0, _breakpoint.makePendingLocationId)(previousLocation)]);
  }

  if (!breakpoint) {
    return state;
  }

  const locationId = (0, _breakpoint.makePendingLocationId)(breakpoint.location);
  const pendingBreakpoint = (0, _breakpoint.createPendingBreakpoint)(breakpoint);

  return state.setIn(["pendingBreakpoints", locationId], pendingBreakpoint);
}

function updateBreakpoint(state, action) {
  const { breakpoint } = action;
  const locationId = (0, _breakpoint.makePendingLocationId)(breakpoint.location);
  const pendingBreakpoint = (0, _breakpoint.createPendingBreakpoint)(breakpoint);

  return state.setIn(["pendingBreakpoints", locationId], pendingBreakpoint);
}

function removeBreakpoint(state, action) {
  const { breakpoint } = action;

  const locationId = (0, _breakpoint.makePendingLocationId)(breakpoint.location);
  const pendingBp = state.getIn(["pendingBreakpoints", locationId]);

  if (!pendingBp && action.status == "start") {
    return state.set("pendingBreakpoints", I.Map());
  }

  return state.deleteIn(["pendingBreakpoints", locationId]);
}

// Selectors
// TODO: these functions should be moved out of the reducer

function getPendingBreakpoints(state) {
  return state.pendingBreakpoints.pendingBreakpoints;
}

function getPendingBreakpointsForSource(state, sourceUrl) {
  const pendingBreakpoints = state.pendingBreakpoints.pendingBreakpoints || I.Map();
  return pendingBreakpoints.filter(pendingBreakpoint => pendingBreakpoint.location.sourceUrl === sourceUrl);
}

function restorePendingBreakpoints() {
  return I.Map(_prefs.prefs.pendingBreakpoints);
}

exports.default = update;

/***/ }),
/* 1420 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getEventListeners = getEventListeners;
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
 * Event listeners reducer
 * @module reducers/event-listeners
 */

const initialEventListenersState = {
  activeEventNames: [],
  listeners: [],
  fetchingListeners: false
};

function update(state = initialEventListenersState, action, emit) {
  switch (action.type) {
    case "UPDATE_EVENT_BREAKPOINTS":
      state.activeEventNames = action.eventNames;
      // emit("activeEventNames", state.activeEventNames);
      break;
    case "FETCH_EVENT_LISTENERS":
      if (action.status === "begin") {
        state.fetchingListeners = true;
      } else if (action.status === "done") {
        state.fetchingListeners = false;
        state.listeners = action.listeners;
      }
      break;
    case "NAVIGATE":
      return initialEventListenersState;
  }

  return state;
}

function getEventListeners(state) {
  return state.eventListeners.listeners;
}

exports.default = update;

/***/ }),
/* 1421 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.createUIState = undefined;
exports.getSelectedPrimaryPaneTab = getSelectedPrimaryPaneTab;
exports.getActiveSearch = getActiveSearch;
exports.getContextMenu = getContextMenu;
exports.getFrameworkGroupingState = getFrameworkGroupingState;
exports.getShownSource = getShownSource;
exports.getPaneCollapse = getPaneCollapse;
exports.getHighlightedLineRange = getHighlightedLineRange;
exports.getConditionalPanelLine = getConditionalPanelLine;
exports.getProjectDirectoryRoot = getProjectDirectoryRoot;
exports.getOrientation = getOrientation;

var _makeRecord = __webpack_require__(1361);

var _makeRecord2 = _interopRequireDefault(_makeRecord);

var _prefs = __webpack_require__(226);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/**
 * UI reducer
 * @module reducers/ui
 */

const createUIState = exports.createUIState = (0, _makeRecord2.default)({
  selectedPrimaryPaneTab: "sources",
  activeSearch: null,
  contextMenu: {},
  shownSource: "",
  projectDirectoryRoot: _prefs.prefs.projectDirectoryRoot,
  startPanelCollapsed: _prefs.prefs.startPanelCollapsed,
  endPanelCollapsed: _prefs.prefs.endPanelCollapsed,
  frameworkGroupingOn: _prefs.prefs.frameworkGroupingOn,
  highlightedLineRange: undefined,
  conditionalPanelLine: null,
  orientation: "horizontal"
});

function update(state = createUIState(), action) {
  switch (action.type) {
    case "TOGGLE_ACTIVE_SEARCH":
      {
        return state.set("activeSearch", action.value);
      }

    case "TOGGLE_FRAMEWORK_GROUPING":
      {
        _prefs.prefs.frameworkGroupingOn = action.value;
        return state.set("frameworkGroupingOn", action.value);
      }

    case "SET_CONTEXT_MENU":
      {
        return state.set("contextMenu", action.contextMenu);
      }

    case "SET_ORIENTATION":
      {
        return state.set("orientation", action.orientation);
      }

    case "SHOW_SOURCE":
      {
        return state.set("shownSource", action.sourceUrl);
      }

    case "TOGGLE_PANE":
      {
        if (action.position == "start") {
          _prefs.prefs.startPanelCollapsed = action.paneCollapsed;
          return state.set("startPanelCollapsed", action.paneCollapsed);
        }

        _prefs.prefs.endPanelCollapsed = action.paneCollapsed;
        return state.set("endPanelCollapsed", action.paneCollapsed);
      }

    case "HIGHLIGHT_LINES":
      const { start, end, sourceId } = action.location;
      let lineRange = {};

      if (start && end && sourceId) {
        lineRange = { start, end, sourceId };
      }

      return state.set("highlightedLineRange", lineRange);

    case "CLOSE_QUICK_OPEN":
    case "CLEAR_HIGHLIGHT_LINES":
      return state.set("highlightedLineRange", {});

    case "OPEN_CONDITIONAL_PANEL":
      return state.set("conditionalPanelLine", action.line);

    case "CLOSE_CONDITIONAL_PANEL":
      return state.set("conditionalPanelLine", null);

    case "SET_PROJECT_DIRECTORY_ROOT":
      _prefs.prefs.projectDirectoryRoot = action.url;
      return state.set("projectDirectoryRoot", action.url);

    case "SET_PRIMARY_PANE_TAB":
      return state.set("selectedPrimaryPaneTab", action.tabName);

    case "CLOSE_PROJECT_SEARCH":
      {
        if (state.get("activeSearch") === "project") {
          return state.set("activeSearch", null);
        }
        return state;
      }

    default:
      {
        return state;
      }
  }
}

// NOTE: we'd like to have the app state fully typed
// https://github.com/devtools-html/debugger.html/blob/master/src/reducers/sources.js#L179-L185
function getSelectedPrimaryPaneTab(state) {
  return state.ui.get("selectedPrimaryPaneTab");
}

function getActiveSearch(state) {
  return state.ui.get("activeSearch");
}

function getContextMenu(state) {
  return state.ui.get("contextMenu");
}

function getFrameworkGroupingState(state) {
  return state.ui.get("frameworkGroupingOn");
}

function getShownSource(state) {
  return state.ui.get("shownSource");
}

function getPaneCollapse(state, position) {
  if (position == "start") {
    return state.ui.get("startPanelCollapsed");
  }

  return state.ui.get("endPanelCollapsed");
}

function getHighlightedLineRange(state) {
  return state.ui.get("highlightedLineRange");
}

function getConditionalPanelLine(state) {
  return state.ui.get("conditionalPanelLine");
}

function getProjectDirectoryRoot(state) {
  return state.ui.get("projectDirectoryRoot");
}

function getOrientation(state) {
  return state.ui.get("orientation");
}

exports.default = update;

/***/ }),
/* 1422 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.createFileSearchState = undefined;
exports.getFileSearchQuery = getFileSearchQuery;
exports.getFileSearchModifiers = getFileSearchModifiers;
exports.getFileSearchResults = getFileSearchResults;

var _makeRecord = __webpack_require__(1361);

var _makeRecord2 = _interopRequireDefault(_makeRecord);

var _prefs = __webpack_require__(226);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/**
 * File Search reducer
 * @module reducers/fileSearch
 */

const createFileSearchState = exports.createFileSearchState = (0, _makeRecord2.default)({
  query: "",
  searchResults: {
    matches: [],
    matchIndex: -1,
    index: -1,
    count: 0
  },
  modifiers: (0, _makeRecord2.default)({
    caseSensitive: _prefs.prefs.fileSearchCaseSensitive,
    wholeWord: _prefs.prefs.fileSearchWholeWord,
    regexMatch: _prefs.prefs.fileSearchRegexMatch
  })()
});

function update(state = createFileSearchState(), action) {
  switch (action.type) {
    case "UPDATE_FILE_SEARCH_QUERY":
      {
        return state.set("query", action.query);
      }

    case "UPDATE_SEARCH_RESULTS":
      {
        return state.set("searchResults", action.results);
      }

    case "TOGGLE_FILE_SEARCH_MODIFIER":
      {
        const actionVal = !state.getIn(["modifiers", action.modifier]);

        if (action.modifier == "caseSensitive") {
          _prefs.prefs.fileSearchCaseSensitive = actionVal;
        }

        if (action.modifier == "wholeWord") {
          _prefs.prefs.fileSearchWholeWord = actionVal;
        }

        if (action.modifier == "regexMatch") {
          _prefs.prefs.fileSearchRegexMatch = actionVal;
        }

        return state.setIn(["modifiers", action.modifier], actionVal);
      }

    default:
      {
        return state;
      }
  }
}

// NOTE: we'd like to have the app state fully typed
// https://github.com/devtools-html/debugger.html/blob/master/src/reducers/sources.js#L179-L185
function getFileSearchQuery(state) {
  return state.fileSearch.get("query");
}

function getFileSearchModifiers(state) {
  return state.fileSearch.get("modifiers");
}

function getFileSearchResults(state) {
  return state.fileSearch.get("searchResults");
}

exports.default = update;

/***/ }),
/* 1423 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.createCoverageState = undefined;
exports.getHitCountForSource = getHitCountForSource;
exports.getCoverageEnabled = getCoverageEnabled;

var _makeRecord = __webpack_require__(1361);

var _makeRecord2 = _interopRequireDefault(_makeRecord);

var _immutable = __webpack_require__(146);

var I = _interopRequireWildcard(_immutable);

var _fromJS = __webpack_require__(1502);

var _fromJS2 = _interopRequireDefault(_fromJS);

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

const createCoverageState = exports.createCoverageState = (0, _makeRecord2.default)({
  coverageOn: false,
  hitCount: I.Map()
}); /* This Source Code Form is subject to the terms of the Mozilla Public
     * License, v. 2.0. If a copy of the MPL was not distributed with this
     * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/**
 * Code coverage reducer
 * @module reducers/coverage
 */

function update(state = createCoverageState(), action) {
  switch (action.type) {
    case "RECORD_COVERAGE":
      return state.mergeIn(["hitCount"], (0, _fromJS2.default)(action.value.coverage)).setIn(["coverageOn"], true);

    default:
      {
        return state;
      }
  }
}

// NOTE: we'd like to have the app state fully typed
// https://github.com/devtools-html/debugger.html/blob/master/src/reducers/sources.js#L179-L185
function getHitCountForSource(state, sourceId) {
  const hitCount = state.coverage.get("hitCount");
  return hitCount.get(sourceId);
}

function getCoverageEnabled(state) {
  return state.coverage.get("coverageOn");
}

exports.default = update;

/***/ }),
/* 1424 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.statusType = undefined;
exports.initialProjectTextSearchState = initialProjectTextSearchState;
exports.getTextSearchResults = getTextSearchResults;
exports.getTextSearchStatus = getTextSearchStatus;
exports.getTextSearchQuery = getTextSearchQuery;

var _immutable = __webpack_require__(146);

var I = _interopRequireWildcard(_immutable);

var _makeRecord = __webpack_require__(1361);

var _makeRecord2 = _interopRequireDefault(_makeRecord);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

// @format

/**
 * Project text search reducer
 * @module reducers/project-text-search
 */

const statusType = exports.statusType = {
  initial: "INITIAL",
  fetching: "FETCHING",
  done: "DONE",
  error: "ERROR"
};

function initialProjectTextSearchState() {
  return (0, _makeRecord2.default)({
    query: "",
    results: I.List(),
    status: statusType.initial
  })();
}

function update(state = initialProjectTextSearchState(), action) {
  switch (action.type) {
    case "ADD_QUERY":
      const actionCopy = action;
      return state.update("query", value => actionCopy.query);

    case "CLEAR_QUERY":
      return state.merge({
        query: "",
        status: statusType.initial
      });

    case "ADD_SEARCH_RESULT":
      const results = state.get("results");
      return state.merge({ results: results.push(action.result) });

    case "UPDATE_STATUS":
      return state.merge({ status: action.status });

    case "CLEAR_SEARCH_RESULTS":
      return state.merge({
        results: state.get("results").clear()
      });

    case "CLEAR_SEARCH":
    case "CLOSE_PROJECT_SEARCH":
      return state.merge({
        query: "",
        results: state.get("results").clear(),
        status: statusType.initial
      });
  }
  return state;
}

function getTextSearchResults(state) {
  return state.projectTextSearch.get("results");
}

function getTextSearchStatus(state) {
  return state.projectTextSearch.get("status");
}

function getTextSearchQuery(state) {
  return state.projectTextSearch.get("query");
}

exports.default = update;

/***/ }),
/* 1425 */,
/* 1426 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.InitialState = InitialState;
exports.default = update;
exports.getExpandedState = getExpandedState;

var _makeRecord = __webpack_require__(1361);

var _makeRecord2 = _interopRequireDefault(_makeRecord);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function InitialState() {
  return (0, _makeRecord2.default)({
    expanded: null
  })();
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/**
 * Source tree reducer
 * @module reducers/source-tree
 */

function update(state = InitialState(), action) {
  switch (action.type) {
    case "SET_EXPANDED_STATE":
      return state.set("expanded", action.expanded);
  }
  return state;
}

function getExpandedState(state) {
  return state.sourceTree.get("expanded");
}

/***/ }),
/* 1427 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getVisibleBreakpoints = getVisibleBreakpoints;

var _breakpoints = __webpack_require__(1378);

var _sources = __webpack_require__(1369);

var _devtoolsSourceMap = __webpack_require__(1360);

function getLocation(breakpoint, isGeneratedSource) {
  return isGeneratedSource ? breakpoint.generatedLocation || breakpoint.location : breakpoint.location;
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.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 formatBreakpoint(breakpoint, selectedSource) {
  const { condition, loading, disabled, hidden } = breakpoint;
  const sourceId = selectedSource.get("id");
  const isGeneratedSource = (0, _devtoolsSourceMap.isGeneratedId)(sourceId);

  return {
    location: getLocation(breakpoint, isGeneratedSource),
    condition,
    loading,
    disabled,
    hidden
  };
}

function isVisible(breakpoint, selectedSource) {
  const sourceId = selectedSource.get("id");
  const isGeneratedSource = (0, _devtoolsSourceMap.isGeneratedId)(sourceId);

  const location = getLocation(breakpoint, isGeneratedSource);
  return location.sourceId === sourceId;
}
/*
 * Finds the breakpoints, which appear in the selected source.
 *
 * This
 */
function getVisibleBreakpoints(state) {
  const selectedSource = (0, _sources.getSelectedSource)(state);
  if (!selectedSource) {
    return null;
  }

  return (0, _breakpoints.getBreakpoints)(state).filter(bp => isVisible(bp, selectedSource)).map(bp => formatBreakpoint(bp, selectedSource));
}

/***/ }),
/* 1428 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

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; };

exports.createFrame = createFrame;
exports.createSource = createSource;
exports.createPause = createPause;
exports.createBreakpointLocation = createBreakpointLocation;
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 module converts Firefox specific types to the generic types

function createFrame(frame) {
  if (!frame) {
    return null;
  }
  let title;
  if (frame.type == "call") {
    const c = frame.callee;
    title = c.name || c.userDisplayName || c.displayName || L10N.getStr("anonymous");
  } else {
    title = `(${frame.type})`;
  }
  const location = {
    sourceId: frame.where.source.actor,
    line: frame.where.line,
    column: frame.where.column
  };

  return {
    id: frame.actor,
    displayName: title,
    location,
    generatedLocation: location,
    this: frame.this,
    scope: frame.environment
  };
}

function createSource(source, { supportsWasm }) {
  return {
    id: source.actor,
    url: source.url,
    isPrettyPrinted: false,
    isWasm: supportsWasm && source.introductionType === "wasm",
    sourceMapURL: source.sourceMapURL,
    isBlackBoxed: false,
    loadedState: "unloaded"
  };
}

function createPause(packet, response) {
  // NOTE: useful when the debugger is already paused
  const frame = packet.frame || response.frames[0];

  return _extends({}, packet, {
    frame: createFrame(frame),
    frames: response.frames.map(createFrame)
  });
}

// Firefox only returns `actualLocation` if it actually changed,
// but we want it always to exist. Format `actualLocation` if it
// exists, otherwise use `location`.

function createBreakpointLocation(location, actualLocation) {
  if (!actualLocation) {
    return location;
  }

  return {
    sourceId: actualLocation.source.actor,
    sourceUrl: actualLocation.source.url,
    line: actualLocation.line,
    column: actualLocation.column
  };
}

/***/ }),
/* 1429 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.fromServerLocation = fromServerLocation;
exports.toServerLocation = toServerLocation;
exports.createFrame = createFrame;
exports.createLoadedObject = createLoadedObject;
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 fromServerLocation(serverLocation) {
  if (serverLocation) {
    return {
      sourceId: serverLocation.scriptId,
      line: serverLocation.lineNumber + 1,
      column: serverLocation.columnNumber,
      sourceUrl: ""
    };
  }
}

function toServerLocation(location) {
  return {
    scriptId: location.sourceId,
    lineNumber: location.line - 1
  };
}

function createFrame(frame) {
  return {
    id: frame.callFrameId,
    displayName: frame.functionName,
    scopeChain: frame.scopeChain,
    generatedLocation: frame.location,
    location: fromServerLocation(frame.location)
  };
}

function createLoadedObject(serverObject, parentId) {
  const { value, name } = serverObject;

  return {
    objectId: value.objectId,
    parentId,
    name,
    value
  };
}

/***/ }),
/* 1430 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

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; }; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                                                                                                                                                                                   * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                                                                                                                                                                                                                                   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

exports.bootstrapStore = bootstrapStore;
exports.bootstrapApp = bootstrapApp;
exports.bootstrapWorkers = bootstrapWorkers;
exports.teardownWorkers = teardownWorkers;

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _redux = __webpack_require__(3);

var _reactDom = __webpack_require__(4);

var _reactDom2 = _interopRequireDefault(_reactDom);

var _devtoolsConfig = __webpack_require__(1355);

var _devtoolsLaunchpad = __webpack_require__(1362);

var _devtoolsSourceMap = __webpack_require__(1360);

var _search = __webpack_require__(1395);

var _prettyPrint = __webpack_require__(1431);

var _parser = __webpack_require__(1365);

var _createStore = __webpack_require__(1658);

var _createStore2 = _interopRequireDefault(_createStore);

var _reducers = __webpack_require__(1516);

var _reducers2 = _interopRequireDefault(_reducers);

var _selectors = __webpack_require__(1352);

var selectors = _interopRequireWildcard(_selectors);

var _App = __webpack_require__(1518);

var _App2 = _interopRequireDefault(_App);

var _prefs = __webpack_require__(226);

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function bootstrapStore(client, { services, toolboxActions }) {
  const createStore = (0, _createStore2.default)({
    log: (0, _devtoolsConfig.isTesting)() || (0, _devtoolsConfig.getValue)("logging.actions"),
    timing: (0, _devtoolsConfig.isDevelopment)(),
    makeThunkArgs: (args, state) => {
      return _extends({}, args, { client }, services, toolboxActions);
    }
  });

  const store = createStore((0, _redux.combineReducers)(_reducers2.default));
  store.subscribe(() => updatePrefs(store.getState()));

  const actions = (0, _redux.bindActionCreators)(__webpack_require__(1354).default, store.dispatch);

  return { store, actions, selectors };
}

function bootstrapApp(connection, { store, actions }) {
  window.appStore = store;

  // Expose the bound actions so external things can do things like
  // selecting a source.
  window.actions = {
    selectLocation: actions.selectLocation,
    selectSourceURL: actions.selectSourceURL
  };

  (0, _devtoolsLaunchpad.renderRoot)(_react2.default, _reactDom2.default, _App2.default, store);
}

function bootstrapWorkers() {
  if (!(0, _devtoolsConfig.isFirefoxPanel)()) {
    // When used in Firefox, the toolbox manages the source map worker.
    (0, _devtoolsSourceMap.startSourceMapWorker)((0, _devtoolsConfig.getValue)("workers.sourceMapURL"));
  }
  (0, _prettyPrint.startPrettyPrintWorker)((0, _devtoolsConfig.getValue)("workers.prettyPrintURL"));
  (0, _parser.startParserWorker)((0, _devtoolsConfig.getValue)("workers.parserURL"));
  (0, _search.startSearchWorker)((0, _devtoolsConfig.getValue)("workers.searchURL"));
}

function teardownWorkers() {
  if (!(0, _devtoolsConfig.isFirefoxPanel)()) {
    // When used in Firefox, the toolbox manages the source map worker.
    (0, _devtoolsSourceMap.stopSourceMapWorker)();
  }
  (0, _prettyPrint.stopPrettyPrintWorker)();
  (0, _parser.stopParserWorker)();
  (0, _search.stopSearchWorker)();
}

function updatePrefs(state) {
  const pendingBreakpoints = selectors.getPendingBreakpoints(state);

  if (_prefs.prefs.pendingBreakpoints !== pendingBreakpoints) {
    _prefs.prefs.pendingBreakpoints = pendingBreakpoints;
  }
}

/***/ }),
/* 1431 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.stopPrettyPrintWorker = exports.startPrettyPrintWorker = undefined;
exports.prettyPrint = prettyPrint;

var _devtoolsUtils = __webpack_require__(1363);

var _source = __webpack_require__(1356);

var _assert = __webpack_require__(1384);

var _assert2 = _interopRequireDefault(_assert);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

const { WorkerDispatcher } = _devtoolsUtils.workerUtils; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                          * License, v. 2.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 dispatcher = new WorkerDispatcher();
const startPrettyPrintWorker = exports.startPrettyPrintWorker = dispatcher.start.bind(dispatcher);
const stopPrettyPrintWorker = exports.stopPrettyPrintWorker = dispatcher.stop.bind(dispatcher);
const _prettyPrint = dispatcher.task("prettyPrint");

async function prettyPrint({ source, url }) {
  const indent = 2;

  (0, _assert2.default)((0, _source.isJavaScript)(source), "Can't prettify non-javascript files.");

  return await _prettyPrint({
    url,
    indent,
    sourceText: source.get("text")
  });
}

/***/ }),
/* 1432 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.reportException = reportException;
exports.executeSoon = executeSoon;

var _assert = __webpack_require__(1384);

var _assert2 = _interopRequireDefault(_assert);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function reportException(who, exception) {
  const msg = `${who} threw an exception: `;
  console.error(msg, exception);
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.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 executeSoon(fn) {
  setTimeout(fn, 0);
}

exports.default = _assert2.default;

/***/ }),
/* 1433 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.addSearchQuery = addSearchQuery;
exports.clearSearchQuery = clearSearchQuery;
exports.clearSearchResults = clearSearchResults;
exports.clearSearch = clearSearch;
exports.updateSearchStatus = updateSearchStatus;
exports.closeProjectSearch = closeProjectSearch;
exports.searchSources = searchSources;
exports.searchSource = searchSource;

var _search = __webpack_require__(1395);

var _selectors = __webpack_require__(1352);

var _source = __webpack_require__(1356);

var _sources = __webpack_require__(1797);

var _projectTextSearch = __webpack_require__(1424);

function addSearchQuery(query) {
  return { type: "ADD_QUERY", query };
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/**
 * Redux actions for the search state
 * @module actions/search
 */

function clearSearchQuery() {
  return { type: "CLEAR_QUERY" };
}

function clearSearchResults() {
  return { type: "CLEAR_SEARCH_RESULTS" };
}

function clearSearch() {
  return { type: "CLEAR_SEARCH" };
}

function updateSearchStatus(status) {
  return { type: "UPDATE_STATUS", status };
}

function closeProjectSearch() {
  return { type: "CLOSE_PROJECT_SEARCH" };
}

function searchSources(query) {
  return async ({ dispatch, getState }) => {
    await dispatch(clearSearchResults());
    await dispatch(addSearchQuery(query));
    dispatch(updateSearchStatus(_projectTextSearch.statusType.fetching));
    const sources = (0, _selectors.getSources)(getState());
    const validSources = sources.valueSeq().filter(source => !(0, _selectors.hasPrettySource)(getState(), source.get("id")) && !(0, _source.isThirdParty)(source));
    for (const source of validSources) {
      await dispatch((0, _sources.loadSourceText)(source));
      await dispatch(searchSource(source.get("id"), query));
    }
    dispatch(updateSearchStatus(_projectTextSearch.statusType.done));
  };
}

function searchSource(sourceId, query) {
  return async ({ dispatch, getState }) => {
    const sourceRecord = (0, _selectors.getSource)(getState(), sourceId);
    if (!sourceRecord) {
      return;
    }

    const matches = await (0, _search.findSourceMatches)(sourceRecord.toJS(), query);
    if (!matches.length) {
      return;
    }
    dispatch({
      type: "ADD_SEARCH_RESULT",
      result: {
        sourceId: sourceRecord.get("id"),
        filepath: sourceRecord.get("url"),
        matches
      }
    });
  };
}

/***/ }),
/* 1434 */,
/* 1435 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.loadSourceText = loadSourceText;

var _devtoolsSourceMap = __webpack_require__(1360);

var _promise = __webpack_require__(1653);

var _selectors = __webpack_require__(1352);

var _parser = __webpack_require__(1365);

var parser = _interopRequireWildcard(_parser);

var _source = __webpack_require__(1356);

var _defer = __webpack_require__(1800);

var _defer2 = _interopRequireDefault(_defer);

var _devtoolsModules = __webpack_require__(1376);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 requests = new Map();

const loadSourceHistogram = _devtoolsModules.Services.telemetry.getHistogramById("DEVTOOLS_DEBUGGER_LOAD_SOURCE_MS");

async function loadSource(source, { sourceMaps, client }) {
  const id = source.get("id");
  if ((0, _devtoolsSourceMap.isOriginalId)(id)) {
    return await sourceMaps.getOriginalSourceText(source.toJS());
  }

  const response = await client.sourceContents(id);

  return {
    id,
    text: response.source,
    contentType: response.contentType || "text/javascript"
  };
}

/**
 * @memberof actions/sources
 * @static
 */
function loadSourceText(source) {
  return async ({ dispatch, getState, client, sourceMaps }) => {
    const id = source.get("id");

    // Fetch the source text only once.
    if (requests.has(id)) {
      return requests.get(id);
    }

    if ((0, _source.isLoaded)(source)) {
      return Promise.resolve();
    }

    const telemetryStart = performance.now();
    const deferred = (0, _defer2.default)();
    requests.set(id, deferred.promise);

    try {
      await dispatch({
        type: "LOAD_SOURCE_TEXT",
        sourceId: id,
        [_promise.PROMISE]: loadSource(source, { sourceMaps, client })
      });
    } catch (e) {
      deferred.resolve();
      requests.delete(id);
      return;
    }

    const newSource = (0, _selectors.getSource)(getState(), source.get("id")).toJS();

    if ((0, _devtoolsSourceMap.isOriginalId)(newSource.id) && !newSource.isWasm) {
      const generatedSource = (0, _selectors.getGeneratedSource)(getState(), source.toJS());
      await dispatch(loadSourceText(generatedSource));
    }

    if (!newSource.isWasm) {
      await parser.setSource(newSource);
    }

    // signal that the action is finished
    deferred.resolve();
    requests.delete(id);

    const telemetryEnd = performance.now();
    const duration = telemetryEnd - telemetryStart;
    loadSourceHistogram.add(duration);
  };
}

/***/ }),
/* 1436 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.showLoading = exports.showErrorMessage = exports.showSourceText = exports.clearEditor = exports.updateDocument = exports.updateLineNumberFormat = exports.clearDocuments = exports.removeDocument = exports.hasDocument = exports.setDocument = exports.getDocument = undefined;

var _source = __webpack_require__(1356);

var _wasm = __webpack_require__(1401);

var _ui = __webpack_require__(1439);

var _sourceEditor = __webpack_require__(197);

var _sourceEditor2 = _interopRequireDefault(_sourceEditor);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

let sourceDocs = {};

function getDocument(key) {
  return sourceDocs[key];
}

function hasDocument(key) {
  return !!getDocument(key);
}

function setDocument(key, doc) {
  sourceDocs[key] = doc;
}

function removeDocument(key) {
  delete sourceDocs[key];
}

function clearDocuments() {
  sourceDocs = {};
}

function resetLineNumberFormat(editor) {
  const cm = editor.codeMirror;
  cm.setOption("lineNumberFormatter", number => number);
  (0, _ui.resizeBreakpointGutter)(cm);
  (0, _ui.resizeToggleButton)(cm);
}

function updateLineNumberFormat(editor, sourceId) {
  if (!(0, _wasm.isWasm)(sourceId)) {
    return resetLineNumberFormat(editor);
  }
  const cm = editor.codeMirror;
  const lineNumberFormatter = (0, _wasm.getWasmLineNumberFormatter)(sourceId);
  cm.setOption("lineNumberFormatter", lineNumberFormatter);
  (0, _ui.resizeBreakpointGutter)(cm);
  (0, _ui.resizeToggleButton)(cm);
}

function updateDocument(editor, source) {
  if (!source) {
    return;
  }

  const sourceId = source.get("id");
  const doc = getDocument(sourceId) || editor.createDocument();
  editor.replaceDocument(doc);

  updateLineNumberFormat(editor, sourceId);
}

function clearEditor(editor) {
  const doc = editor.createDocument();
  editor.replaceDocument(doc);
  editor.setText("");
  editor.setMode({ name: "text" });
  resetLineNumberFormat(editor);
}

function showLoading(editor) {
  if (hasDocument("loading")) {
    return;
  }

  const doc = editor.createDocument();
  setDocument("loading", doc);
  editor.replaceDocument(doc);
  editor.setText(L10N.getStr("loadingText"));
  editor.setMode({ name: "text" });
}

function showErrorMessage(editor, msg) {
  let error;
  if (msg.includes("WebAssembly binary source is not available")) {
    error = L10N.getStr("wasmIsNotAvailable");
  } else {
    error = L10N.getFormatStr("errorLoadingText3", msg);
  }
  const doc = editor.createDocument();
  editor.replaceDocument(doc);
  editor.setText(error);
  editor.setMode({ name: "text" });
  resetLineNumberFormat(editor);
}

function setEditorText(editor, source) {
  const { text, id: sourceId } = source;
  if (source.isWasm) {
    const wasmLines = (0, _wasm.renderWasmText)(sourceId, text);
    // cm will try to split into lines anyway, saving memory
    const wasmText = { split: () => wasmLines, match: () => false };
    editor.setText(wasmText);
  } else {
    editor.setText(text);
  }
}

/**
 * Handle getting the source document or creating a new
 * document with the correct mode and text.
 */
function showSourceText(editor, source, symbols) {
  if (!source) {
    return;
  }

  if (hasDocument(source.id)) {
    const doc = getDocument(source.id);
    if (editor.codeMirror.doc === doc) {
      const mode = (0, _source.getMode)(source, symbols);

      if (doc.mode.name !== mode.name) {
        editor.setMode(mode);
      }

      return;
    }

    editor.replaceDocument(doc);
    updateLineNumberFormat(editor, source.id);
    editor.setMode((0, _source.getMode)(source, symbols));
    return doc;
  }

  const doc = editor.createDocument();
  setDocument(source.id, doc);
  editor.replaceDocument(doc);

  setEditorText(editor, source);
  editor.setMode((0, _source.getMode)(source, symbols));
  updateLineNumberFormat(editor, source.id);
}

exports.getDocument = getDocument;
exports.setDocument = setDocument;
exports.hasDocument = hasDocument;
exports.removeDocument = removeDocument;
exports.clearDocuments = clearDocuments;
exports.updateLineNumberFormat = updateLineNumberFormat;
exports.updateDocument = updateDocument;
exports.clearEditor = clearEditor;
exports.showSourceText = showSourceText;
exports.showErrorMessage = showErrorMessage;
exports.showLoading = showLoading;

/***/ }),
/* 1437 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.sanitizeInput = sanitizeInput;
exports.wrapExpression = wrapExpression;
exports.getValue = getValue;

var _indentation = __webpack_require__(1438);

// replace quotes that could interfere with the evaluation.
function sanitizeInput(input) {
  return input.replace(/"/g, '"');
}

/*
 * wrap the expression input in a try/catch so that it can be safely
 * evaluated.
 *
 * NOTE: we add line after the expression to protect against comments.
*/
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 wrapExpression(input) {
  return (0, _indentation.correctIndentation)(`
    try {
      ${sanitizeInput(input)}
    } catch (e) {
      e
    }
  `);
}

function getValue(expression) {
  const value = expression.value;
  if (!value) {
    return {
      path: expression.from,
      value: { unavailable: true }
    };
  }

  if (value.exception) {
    return {
      path: value.from,
      value: value.exception
    };
  }

  if (value.error) {
    return {
      path: value.from,
      value: value.error
    };
  }

  if (value.result && value.result.class == "Error") {
    const { name, message } = value.result.preview;
    const newValue = name === "ReferenceError" ? { unavailable: true } : `${name}: ${message}`;

    return { path: value.input, value: newValue };
  }

  if (typeof value.result == "object") {
    return {
      path: value.result.actor,
      value: value.result
    };
  }

  return {
    path: value.input,
    value: value.result
  };
}

/***/ }),
/* 1438 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getIndentation = getIndentation;
exports.correctIndentation = correctIndentation;
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 getIndentation(line) {
  if (!line) {
    return 0;
  }
  return line.match(/^\s*/)[0].length;
}

function getMaxIndentation(lines) {
  const firstLine = lines[0];
  const secondLine = lines[1];
  const lastLine = lines[lines.length - 1];

  const indentations = [getIndentation(firstLine), getIndentation(secondLine), getIndentation(lastLine)];

  return Math.max(...indentations);
}

function correctIndentation(text) {
  const lines = text.trim().split("\n");
  const indentation = getMaxIndentation(lines);
  const formattedLines = lines.map(_line => _line.replace(new RegExp(`^\\s{0,${indentation - 1}}`), ""));

  return formattedLines.join("\n");
}

/***/ }),
/* 1439 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.isVisible = isVisible;
exports.getLineNumberWidth = getLineNumberWidth;
exports.resizeBreakpointGutter = resizeBreakpointGutter;
exports.resizeToggleButton = resizeToggleButton;
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/* Checks to see if the root element is available and
 * if the element is visible. We check the width of the element
 * because it is more reliable than either checking a focus state or
 * the visibleState or hidden property.
 */
function isVisible() {
  const el = document.querySelector("#mount");
  return el && el.getBoundingClientRect().width;
}

/* Gets the line numbers width in the code editor
 */
function getLineNumberWidth(editor) {
  const gutters = editor.display.gutters;
  const lineNumbers = gutters.querySelector(".CodeMirror-linenumbers");
  return lineNumbers && lineNumbers.clientWidth;
}

/**
 * Forces the breakpoint gutter to be the same size as the line
 * numbers gutter. Editor CSS will absolutely position the gutter
 * beneath the line numbers. This makes it easy to be flexible with
 * how we overlay breakpoints.
 */
function resizeBreakpointGutter(editor) {
  const gutters = editor.display.gutters;
  const breakpoints = gutters.querySelector(".breakpoints");
  if (breakpoints) {
    breakpoints.style.width = `${getLineNumberWidth(editor)}px`;
  }
}

/**
 * Forces the left toggle button in source header to be the same size
 * as the line numbers gutter.
 */
function resizeToggleButton(editor) {
  const toggleButton = document.querySelector(".source-header .toggle-button-start");
  if (toggleButton) {
    toggleButton.style.width = `${getLineNumberWidth(editor)}px`;
  }
}

/***/ }),
/* 1440 */
/***/ (function(module, exports, __webpack_require__) {

const SplitBox = __webpack_require__(1536);

module.exports = SplitBox;

/***/ }),
/* 1441 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var _tree = __webpack_require__(1541);

var _tree2 = _interopRequireDefault(_tree);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

module.exports = {
  Tree: _tree2.default
}; /* This Source Code Form is subject to the terms of the Mozilla Public
    * License, v. 2.0. If a copy of the MPL was not distributed with this
    * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/***/ }),
/* 1442 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _addToTree = __webpack_require__(1443);

Object.defineProperty(exports, "addToTree", {
  enumerable: true,
  get: function () {
    return _addToTree.addToTree;
  }
});

var _collapseTree = __webpack_require__(1444);

Object.defineProperty(exports, "collapseTree", {
  enumerable: true,
  get: function () {
    return _collapseTree.collapseTree;
  }
});

var _createTree = __webpack_require__(1546);

Object.defineProperty(exports, "createTree", {
  enumerable: true,
  get: function () {
    return _createTree.createTree;
  }
});

var _formatTree = __webpack_require__(1542);

Object.defineProperty(exports, "formatTree", {
  enumerable: true,
  get: function () {
    return _formatTree.formatTree;
  }
});

var _getDirectories = __webpack_require__(1545);

Object.defineProperty(exports, "getDirectories", {
  enumerable: true,
  get: function () {
    return _getDirectories.getDirectories;
  }
});

var _getURL = __webpack_require__(1405);

Object.defineProperty(exports, "getFilenameFromPath", {
  enumerable: true,
  get: function () {
    return _getURL.getFilenameFromPath;
  }
});
Object.defineProperty(exports, "getURL", {
  enumerable: true,
  get: function () {
    return _getURL.getURL;
  }
});

var _sortTree = __webpack_require__(1544);

Object.defineProperty(exports, "sortEntireTree", {
  enumerable: true,
  get: function () {
    return _sortTree.sortEntireTree;
  }
});
Object.defineProperty(exports, "sortTree", {
  enumerable: true,
  get: function () {
    return _sortTree.sortTree;
  }
});

var _updateTree = __webpack_require__(2245);

Object.defineProperty(exports, "updateTree", {
  enumerable: true,
  get: function () {
    return _updateTree.updateTree;
  }
});

var _utils = __webpack_require__(1371);

Object.defineProperty(exports, "createNode", {
  enumerable: true,
  get: function () {
    return _utils.createNode;
  }
});
Object.defineProperty(exports, "createParentMap", {
  enumerable: true,
  get: function () {
    return _utils.createParentMap;
  }
});
Object.defineProperty(exports, "getRelativePath", {
  enumerable: true,
  get: function () {
    return _utils.getRelativePath;
  }
});
Object.defineProperty(exports, "isDirectory", {
  enumerable: true,
  get: function () {
    return _utils.isDirectory;
  }
});
Object.defineProperty(exports, "isExactUrlMatch", {
  enumerable: true,
  get: function () {
    return _utils.isExactUrlMatch;
  }
});
Object.defineProperty(exports, "isNotJavaScript", {
  enumerable: true,
  get: function () {
    return _utils.isNotJavaScript;
  }
});
Object.defineProperty(exports, "nodeHasChildren", {
  enumerable: true,
  get: function () {
    return _utils.nodeHasChildren;
  }
});
Object.defineProperty(exports, "getExtension", {
  enumerable: true,
  get: function () {
    return _utils.getExtension;
  }
});

/***/ }),
/* 1443 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.addToTree = addToTree;

var _utils = __webpack_require__(1371);

var _treeOrder = __webpack_require__(1543);

var _getURL = __webpack_require__(1405);

function isUnderRoot(url, projectRoot) {
  if (!projectRoot) {
    return true;
  }

  return `/${url.group}${url.path}`.startsWith(projectRoot);
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.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 removeProjectRoot(parts, projectRoot) {
  const rootParts = projectRoot.replace("://", "").split("/");
  return parts.splice(0, rootParts.length - 2);
}

function createNodeInTree(part, path, tree, index) {
  const node = (0, _utils.createNode)(part, path, []);

  // we are modifying the tree
  const contents = tree.contents.slice(0);
  contents.splice(index, 0, node);
  tree.contents = contents;

  return node;
}

/*
 * Look for the child directory
 * 1. if it exists return it
 * 2. if it does not exist create it
 * 3. if it is a file, replace it with a directory
 */
function findOrCreateNode(parts, subTree, path, part, index, url, debuggeeHost) {
  const addedPartIsFile = (0, _utils.partIsFile)(index, parts, url);
  const { found: childFound, index: childIndex } = (0, _treeOrder.findNodeInContents)(subTree, (0, _treeOrder.createTreeNodeMatcher)(part, !addedPartIsFile, debuggeeHost));

  // we create and enter the new node
  if (!childFound) {
    return createNodeInTree(part, path, subTree, childIndex);
  }

  // we found a path with the same name as the part. We need to determine
  // if this is the correct child, or if we have a naming conflict
  const child = subTree.contents[childIndex];
  const childIsFile = !(0, _utils.nodeHasChildren)(child);

  // if we have a naming conflict, we'll create a new node
  if (childIsFile && !addedPartIsFile || !childIsFile && addedPartIsFile) {
    return createNodeInTree(part, path, subTree, childIndex);
  }

  // if there is no naming conflict, we can traverse into the child
  return child;
}

/*
 * walk the source tree to the final node for a given url,
 * adding new nodes along the way
 */
function traverseTree(url, tree, debuggeeHost, projectRoot) {
  url.path = decodeURIComponent(url.path);

  const parts = url.path.split("/").filter(p => p !== "");
  parts.unshift(url.group);

  if (projectRoot) {
    removeProjectRoot(parts, projectRoot);
  }

  let path = "";
  return parts.reduce((subTree, part, index) => {
    path = `${path}/${part}`;
    const debuggeeHostIfRoot = index === 0 ? debuggeeHost : null;
    return findOrCreateNode(parts, subTree, path, part, index, url, debuggeeHostIfRoot);
  }, tree);
}

/*
 * Add a source file to a directory node in the tree
 */
function addSourceToNode(node, url, source) {
  const isFile = !(0, _utils.isDirectory)(url);

  // if we have a file, and the subtree has no elements, overwrite the
  // subtree contents with the source
  if (isFile) {
    return source;
  }

  const { filename } = url;
  const { found: childFound, index: childIndex } = (0, _treeOrder.findNodeInContents)(node, (0, _treeOrder.createTreeNodeMatcher)(filename, false, null));

  // if we are readding an existing file in the node, overwrite the existing
  // file and return the node's contents
  if (childFound) {
    const existingNode = node.contents[childIndex];
    existingNode.contents = source;
    return node.contents;
  }

  // if this is a new file, add the new file;
  const newNode = (0, _utils.createNode)(filename, source.get("url"), source);
  const contents = node.contents.slice(0);
  contents.splice(childIndex, 0, newNode);
  return contents;
}

/**
 * @memberof utils/sources-tree
 * @static
 */
function addToTree(tree, source, debuggeeUrl, projectRoot) {
  const url = (0, _getURL.getURL)(source.get("url"), debuggeeUrl);
  const debuggeeHost = (0, _treeOrder.getDomain)(debuggeeUrl);

  if ((0, _utils.isInvalidUrl)(url, source) || !isUnderRoot(url, projectRoot)) {
    return;
  }

  const finalNode = traverseTree(url, tree, debuggeeHost, projectRoot);
  finalNode.contents = addSourceToNode(finalNode, url, source);
}

/***/ }),
/* 1444 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.collapseTree = collapseTree;

var _utils = __webpack_require__(1371);

/**
 * Take an existing source tree, and return a new one with collapsed nodes.
 */
function collapseTree(node, depth = 0) {
  // Node is a folder.
  if (Array.isArray(node.contents)) {
    // Node is not a root/domain node, and only contains 1 item.
    if (depth > 1 && node.contents.length === 1) {
      const next = node.contents[0];
      // Do not collapse if the next node is a leaf node.
      if ((0, _utils.nodeHasChildren)(next)) {
        return collapseTree((0, _utils.createNode)(`${node.name}/${next.name}`, next.path, next.contents), depth + 1);
      }
    }
    // Map the contents.
    return (0, _utils.createNode)(node.name, node.path, node.contents.map(next => collapseTree(next, depth + 1)));
  }
  // Node is a leaf, not a folder, do not modify it.
  return node;
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/***/ }),
/* 1445 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _classnames = __webpack_require__(175);

var _classnames2 = _interopRequireDefault(_classnames);

__webpack_require__(1316);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

class ResultList extends _react.Component {

  constructor(props) {
    super(props);
    this.renderListItem = this.renderListItem.bind(this);
  }

  renderListItem(item, index) {
    const { selectItem, selected } = this.props;
    const props = {
      onClick: event => selectItem(event, item, index),
      key: `${item.id}${item.value}${index}`,
      ref: String(index),
      title: item.value,
      "aria-labelledby": `${item.id}-title`,
      "aria-describedby": `${item.id}-subtitle`,
      role: "option",
      className: (0, _classnames2.default)("result-item", {
        selected: index === selected
      })
    };

    return _react2.default.createElement(
      "li",
      props,
      _react2.default.createElement(
        "div",
        { id: `${item.id}-title`, className: "title" },
        item.title
      ),
      _react2.default.createElement(
        "div",
        { id: `${item.id}-subtitle`, className: "subtitle" },
        item.subtitle
      )
    );
  }

  render() {
    const { size, items, role } = this.props;

    return _react2.default.createElement(
      "ul",
      {
        className: (0, _classnames2.default)("result-list", size),
        id: "result-list",
        role: role,
        "aria-live": "polite"
      },
      items.map(this.renderListItem)
    );
  }
}
exports.default = ResultList; /* This Source Code Form is subject to the terms of the Mozilla Public
                               * License, v. 2.0. If a copy of the MPL was not distributed with this
                               * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

ResultList.defaultProps = {
  size: "small",
  role: "listbox"
};

/***/ }),
/* 1446 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _lodash = __webpack_require__(2);

var _frame = __webpack_require__(1380);

__webpack_require__(1320);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 getFunctionName(func) {
  const name = func.userDisplayName || func.displayName || func.name;
  return (0, _frame.simplifyDisplayName)(name);
}

class PreviewFunction extends _react.Component {
  renderFunctionName(func) {
    const name = getFunctionName(func);
    return _react2.default.createElement(
      "span",
      { className: "function-name" },
      name
    );
  }

  renderParams(func) {
    const { parameterNames = [] } = func;
    const params = parameterNames.filter(i => i).map(param => _react2.default.createElement(
      "span",
      { className: "param", key: param },
      param
    ));

    const commas = (0, _lodash.times)(params.length - 1).map((_, i) => _react2.default.createElement(
      "span",
      { className: "delimiter", key: i },
      ", "
    ));

    return (0, _lodash.flatten)((0, _lodash.zip)(params, commas));
  }

  render() {
    return _react2.default.createElement(
      "span",
      { className: "function-signature" },
      this.renderFunctionName(this.props.func),
      _react2.default.createElement(
        "span",
        { className: "paren" },
        "("
      ),
      this.renderParams(this.props.func),
      _react2.default.createElement(
        "span",
        { className: "paren" },
        ")"
      )
    );
  }
}
exports.default = PreviewFunction;

/***/ }),
/* 1447 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Dependencies
const PropTypes = __webpack_require__(20);

const {
  containsURL,
  isURL,
  escapeString,
  getGripType,
  rawCropString,
  sanitizeString,
  wrapRender,
  isGrip,
  tokenSplitRegex,
  ELLIPSIS
} = __webpack_require__(1353);

const dom = __webpack_require__(1758);
const { a, span } = dom;

/**
 * Renders a string. String value is enclosed within quotes.
 */
StringRep.propTypes = {
  useQuotes: PropTypes.bool,
  escapeWhitespace: PropTypes.bool,
  style: PropTypes.object,
  cropLimit: PropTypes.number.isRequired,
  member: PropTypes.string,
  object: PropTypes.object.isRequired,
  openLink: PropTypes.func,
  className: PropTypes.string,
  omitLinkHref: PropTypes.bool
};

function StringRep(props) {
  let {
    className,
    style,
    cropLimit,
    object,
    useQuotes = true,
    escapeWhitespace = true,
    member,
    openLink,
    omitLinkHref = true
  } = props;

  let text = object;

  const isLong = isLongString(object);
  const shouldCrop = (!member || !member.open) && cropLimit && text.length > cropLimit;

  if (isLong) {
    text = maybeCropLongString({
      shouldCrop,
      cropLimit
    }, text);
  }

  text = formatText({
    useQuotes,
    escapeWhitespace
  }, text);

  const config = getElementConfig({
    className,
    style,
    actor: object.actor
  });

  if (!isLong) {
    if (containsURL(text)) {
      return span(config, ...getLinkifiedElements(text, shouldCrop && cropLimit, omitLinkHref, openLink));
    }

    // Cropping of longString has been handled before formatting.
    text = maybeCropString({
      isLong,
      shouldCrop,
      cropLimit
    }, text);
  }

  return span(config, text);
}

function maybeCropLongString(opts, text) {
  const {
    shouldCrop,
    cropLimit
  } = opts;

  const {
    fullText,
    initial,
    length
  } = text;

  text = shouldCrop ? initial.substring(0, cropLimit) : fullText || initial;

  if (text.length < length) {
    text += ELLIPSIS;
  }

  return text;
}

function formatText(opts, text) {
  let {
    useQuotes,
    escapeWhitespace
  } = opts;

  return useQuotes ? escapeString(text, escapeWhitespace) : sanitizeString(text);
}

function getElementConfig(opts) {
  const {
    className,
    style,
    actor
  } = opts;

  const config = {};

  if (actor) {
    config["data-link-actor-id"] = actor;
  }

  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 {String} text: The actual string to linkify.
 * @param {Integer | null} cropLimit
 * @param {Boolean} omitLinkHref: Do not create an href attribute if true.
 * @param {Function} openLink: Function handling the link opening.
 * @returns {Array<String|ReactElement>}
 */
function getLinkifiedElements(text, cropLimit, omitLinkHref, openLink) {
  const halfLimit = Math.ceil((cropLimit - ELLIPSIS.length) / 2);
  const startCropIndex = cropLimit ? halfLimit : null;
  const endCropIndex = cropLimit ? text.length - halfLimit : null;

  // As we walk through the tokens of the source string, we make sure to preserve
  // the original whitespace that separated the tokens.
  let currentIndex = 0;
  const items = [];
  for (let token of text.split(tokenSplitRegex)) {
    if (isURL(token)) {
      // Let's grab all the non-url strings before the link.
      const tokenStart = text.indexOf(token, currentIndex);
      let nonUrlText = text.slice(currentIndex, tokenStart);
      nonUrlText = getCroppedString(nonUrlText, currentIndex, startCropIndex, endCropIndex);
      if (nonUrlText) {
        items.push(nonUrlText);
      }

      // Update the index to match the beginning of the token.
      currentIndex = tokenStart;

      let linkText = getCroppedString(token, currentIndex, startCropIndex, endCropIndex);
      if (linkText) {
        items.push(a({
          className: "url",
          title: token,
          href: omitLinkHref === true ? null : token,
          draggable: false,
          onClick: openLink ? e => {
            e.preventDefault();
            openLink(token);
          } : null
        }, linkText));
      }

      currentIndex = tokenStart + token.length;
    }
  }

  // Clean up any non-URL text at the end of the source string,
  // i.e. not handled in the loop.
  if (currentIndex !== text.length) {
    let nonUrlText = text.slice(currentIndex, text.length);
    if (currentIndex < endCropIndex) {
      const cutIndex = endCropIndex - currentIndex;
      nonUrlText = nonUrlText.substring(cutIndex);
    }
    items.push(nonUrlText);
  }

  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) {
  return object && object.type === "longString";
}

function supportsObject(object, noGrip = false) {
  if (noGrip === false && isGrip(object)) {
    return isLongString(object);
  }

  return getGripType(object, noGrip) == "string";
}

// Exports from this module

module.exports = {
  rep: wrapRender(StringRep),
  supportsObject
};

/***/ }),
/* 1448 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Dependencies
const dom = __webpack_require__(1758);
const PropTypes = __webpack_require__(20);
const {
  wrapRender
} = __webpack_require__(1353);
const { MODE } = __webpack_require__(1357);
const { span } = dom;

const ModePropType = PropTypes.oneOf(
// @TODO Change this to Object.values once it's supported in Node's version of V8
Object.keys(MODE).map(key => MODE[key]));

/**
 * 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
};

function ArrayRep(props) {
  let {
    object,
    mode = MODE.SHORT
  } = props;

  let items;
  let brackets;
  let needSpace = function (space) {
    return space ? { left: "[ ", right: " ]" } : { left: "[", right: "]" };
  };

  if (mode === MODE.TINY) {
    let isEmpty = object.length === 0;
    if (isEmpty) {
      items = [];
    } else {
      items = [span({
        className: "more-ellipsis",
        title: "more…"
      }, "…")];
    }
    brackets = needSpace(false);
  } else {
    items = arrayIterator(props, object, maxLengthMap.get(mode));
    brackets = needSpace(items.length > 0);
  }

  return span({
    className: "objectBox objectBox-array" }, span({
    className: "arrayLeftBracket"
  }, brackets.left), ...items, span({
    className: "arrayRightBracket"
  }, brackets.right), span({
    className: "arrayProperties",
    role: "group" }));
}

function arrayIterator(props, array, max) {
  let items = [];

  for (let i = 0; i < array.length && i < max; i++) {
    let config = {
      mode: MODE.TINY,
      delim: i == array.length - 1 ? "" : ", "
    };
    let item;

    try {
      item = ItemRep(Object.assign({}, props, config, {
        object: array[i]
      }));
    } catch (exc) {
      item = ItemRep(Object.assign({}, props, config, {
        object: exc
      }));
    }
    items.push(item);
  }

  if (array.length > max) {
    items.push(span({
      className: "more-ellipsis",
      title: "more…"
    }, "…"));
  }

  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 } = __webpack_require__(1372);

  let {
    object,
    delim,
    mode
  } = props;
  return span({}, Rep(Object.assign({}, props, {
    object: object,
    mode: mode
  })), delim);
}

function getLength(object) {
  return object.length;
}

function supportsObject(object) {
  return 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,
  ModePropType
};

/***/ }),
/* 1449 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

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
};

/***/ }),
/* 1450 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Dependencies
const PropTypes = __webpack_require__(20);

const { lengthBubble } = __webpack_require__(2249);
const {
  getGripType,
  isGrip,
  wrapRender,
  ellipsisElement
} = __webpack_require__(1353);
const { MODE } = __webpack_require__(1357);

const dom = __webpack_require__(1758);
const { span } = dom;
const { ModePropType } = __webpack_require__(1448);

/**
 * 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,
  // @TODO Change this to Object.values once it's supported in Node's version of V8
  mode: ModePropType,
  provider: PropTypes.object,
  onDOMNodeMouseOver: PropTypes.func,
  onDOMNodeMouseOut: PropTypes.func,
  onInspectIconClick: PropTypes.func
};

function GripArray(props) {
  let {
    object,
    mode = MODE.SHORT
  } = props;

  let items;
  let brackets;
  let needSpace = function (space) {
    return space ? { left: "[ ", right: " ]" } : { left: "[", right: "]" };
  };

  const config = {
    "data-link-actor-id": object.actor,
    className: "objectBox objectBox-array"
  };

  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));
  }

  let max = maxLengthMap.get(mode);
  items = arrayIterator(props, object, max);
  brackets = needSpace(items.length > 0);

  return span({
    "data-link-actor-id": object.actor,
    className: "objectBox objectBox-array" }, title, span({
    className: "arrayLeftBracket"
  }, brackets.left), ...interleaveCommas(items), span({
    className: "arrayRightBracket"
  }, brackets.right), span({
    className: "arrayProperties",
    role: "group" }));
}

function interleaveCommas(items) {
  return items.reduce((res, item, index) => {
    if (index !== items.length - 1) {
      return res.concat(item, ", ");
    }
    return res.concat(item);
  }, []);
}

function getLength(grip) {
  if (!grip.preview) {
    return 0;
  }

  return grip.preview.length || grip.preview.childNodesLength || 0;
}

function getTitle(props, object) {
  let objectLength = getLength(object);
  let isEmpty = objectLength === 0;

  let title = props.title || object.class || "Array";

  const length = lengthBubble({
    object,
    mode: props.mode,
    maxLengthMap,
    getLength
  });

  if (props.mode === MODE.TINY) {
    if (isEmpty) {
      return object.class === "Array" ? "" : span({
        className: "objectTitle" }, title, " ");
    }

    let trailingSpace;
    if (object.class === "Array") {
      title = "";
      trailingSpace = " ";
    }

    return span({
      className: "objectTitle" }, title, length, trailingSpace);
  }

  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) {
  let { Rep } = __webpack_require__(1372);

  let items = [];
  const gripLength = getLength(grip);

  if (!gripLength) {
    return items;
  }

  const previewItems = getPreviewItems(grip);
  let 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(Object.assign({}, 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/devtools-html/reps/issues/141
  return `<${number} empty slot${number > 1 ? "s" : ""}>`;
}

function supportsObject(grip, noGrip = false) {
  if (noGrip === true || !isGrip(grip)) {
    return 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
};

/***/ }),
/* 1451 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Dependencies
const PropTypes = __webpack_require__(20);
// Shortcuts
const dom = __webpack_require__(1758);
const { span } = dom;
const {
  wrapRender
} = __webpack_require__(1353);
const PropRep = __webpack_require__(1381);
const { MODE } = __webpack_require__(1357);
/**
 * Renders an map entry. A map entry is represented by its key, a column and its value.
 */
GripMapEntry.propTypes = {
  object: PropTypes.object,
  // @TODO Change this to Object.values once it's supported in Node's version of V8
  mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
  onDOMNodeMouseOver: PropTypes.func,
  onDOMNodeMouseOut: PropTypes.func,
  onInspectIconClick: PropTypes.func
};

function GripMapEntry(props) {
  const {
    object
  } = props;

  const {
    key,
    value
  } = object.preview;

  return span({
    className: "objectBox objectBox-map-entry"
  }, ...PropRep(Object.assign({}, 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 === "mapEntry" && grip.preview;
}

function createGripMapEntry(key, value) {
  return {
    type: "mapEntry",
    preview: {
      key,
      value
    }
  };
}

// Exports from this module
module.exports = {
  rep: wrapRender(GripMapEntry),
  createGripMapEntry,
  supportsObject
};

/***/ }),
/* 1452 */,
/* 1453 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _classnames = __webpack_require__(175);

var _classnames2 = _interopRequireDefault(_classnames);

var _Svg = __webpack_require__(1359);

var _Svg2 = _interopRequireDefault(_Svg);

var _frame = __webpack_require__(1380);

var _source = __webpack_require__(1356);

var _FrameMenu = __webpack_require__(1454);

var _FrameMenu2 = _interopRequireDefault(_FrameMenu);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 FrameTitle({ frame, options }) {
  const displayName = (0, _frame.formatDisplayName)(frame, options);
  return _react2.default.createElement(
    "div",
    { className: "title" },
    displayName
  );
}

function FrameLocation({ frame }) {
  if (!frame.source) {
    return;
  }

  if (frame.library) {
    return _react2.default.createElement(
      "div",
      { className: "location" },
      frame.library,
      _react2.default.createElement(_Svg2.default, { name: frame.library.toLowerCase(), className: "annotation-logo" })
    );
  }

  const filename = (0, _source.getFilename)(frame.source);
  return _react2.default.createElement(
    "div",
    { className: "location" },
    `${filename}: ${frame.location.line}`
  );
}

FrameLocation.displayName = "FrameLocation";

class FrameComponent extends _react.Component {

  onContextMenu(event) {
    const {
      frame,
      copyStackTrace,
      toggleFrameworkGrouping,
      toggleBlackBox,
      frameworkGroupingOn
    } = this.props;
    (0, _FrameMenu2.default)(frame, frameworkGroupingOn, { copyStackTrace, toggleFrameworkGrouping, toggleBlackBox }, event);
  }

  onMouseDown(e, frame, selectedFrame) {
    if (e.nativeEvent.which == 3) {
      return;
    }
    this.props.selectFrame(frame);
  }

  onKeyUp(event, frame, selectedFrame) {
    if (event.key != "Enter") {
      return;
    }
    this.props.selectFrame(frame);
  }

  render() {
    const {
      frame,
      selectedFrame,
      hideLocation,
      shouldMapDisplayName
    } = this.props;

    const className = (0, _classnames2.default)("frame", {
      selected: selectedFrame && selectedFrame.id === frame.id
    });
    return _react2.default.createElement(
      "li",
      {
        key: frame.id,
        className: className,
        onMouseDown: e => this.onMouseDown(e, frame, selectedFrame),
        onKeyUp: e => this.onKeyUp(e, frame, selectedFrame),
        onContextMenu: e => this.onContextMenu(e),
        tabIndex: 0
      },
      _react2.default.createElement(FrameTitle, { frame: frame, options: { shouldMapDisplayName } }),
      !hideLocation && _react2.default.createElement(FrameLocation, { frame: frame })
    );
  }
}

exports.default = FrameComponent;
FrameComponent.defaultProps = {
  hideLocation: false,
  shouldMapDisplayName: true
};
FrameComponent.displayName = "Frame";

/***/ }),
/* 1454 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = FrameMenu;

var _devtoolsContextmenu = __webpack_require__(1413);

var _clipboard = __webpack_require__(1388);

var _lodash = __webpack_require__(2);

const blackboxString = "sourceFooter.blackbox"; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                 * License, v. 2.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 unblackboxString = "sourceFooter.unblackbox";

function formatMenuElement(labelString, click, disabled = false) {
  const label = L10N.getStr(labelString);
  const accesskey = L10N.getStr(`${labelString}.accesskey`);
  const id = `node-menu-${(0, _lodash.kebabCase)(label)}`;
  return {
    id,
    label,
    accesskey,
    disabled,
    click
  };
}

function copySourceElement(url) {
  return formatMenuElement("copySourceUri2", () => (0, _clipboard.copyToTheClipboard)(url));
}

function copyStackTraceElement(copyStackTrace) {
  return formatMenuElement("copyStackTrace", () => copyStackTrace());
}

function toggleFrameworkGroupingElement(toggleFrameworkGrouping, frameworkGroupingOn) {
  const actionType = frameworkGroupingOn ? "framework.disableGrouping" : "framework.enableGrouping";

  return formatMenuElement(actionType, () => toggleFrameworkGrouping());
}

function blackBoxSource(source, toggleBlackBox) {
  const toggleBlackBoxString = source.isBlackBoxed ? unblackboxString : blackboxString;

  return formatMenuElement(toggleBlackBoxString, () => toggleBlackBox(source));
}

function FrameMenu(frame, frameworkGroupingOn, callbacks, event) {
  event.stopPropagation();
  event.preventDefault();

  const menuOptions = [];

  const source = frame.source;

  const toggleFrameworkElement = toggleFrameworkGroupingElement(callbacks.toggleFrameworkGrouping, frameworkGroupingOn);
  menuOptions.push(toggleFrameworkElement);

  if (source) {
    const copySourceUri2 = copySourceElement(source.url);
    menuOptions.push(copySourceUri2);
    menuOptions.push(blackBoxSource(source, callbacks.toggleBlackBox));
  }

  const copyStackTraceItem = copyStackTraceElement(callbacks.copyStackTrace);

  menuOptions.push(copyStackTraceItem);

  (0, _devtoolsContextmenu.showMenu)(event, menuOptions);
}

/***/ }),
/* 1455 */,
/* 1456 */,
/* 1457 */,
/* 1458 */,
/* 1459 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _reactDom = __webpack_require__(4);

var _reactDom2 = _interopRequireDefault(_reactDom);

var _devtoolsLaunchpad = __webpack_require__(1362);

var _devtoolsConfig = __webpack_require__(1355);

var _client = __webpack_require__(1499);

var _bootstrap = __webpack_require__(1430);

var _sourceQueue = __webpack_require__(1795);

var _sourceQueue2 = _interopRequireDefault(_sourceQueue);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

if (false) {
  window.Perf = require("react-addons-perf");
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

if ((0, _devtoolsConfig.isFirefoxPanel)()) {
  module.exports = {
    bootstrap: ({
      threadClient,
      tabTarget,
      debuggerClient,
      sourceMaps,
      toolboxActions
    }) => {
      return (0, _client.onConnect)({
        tab: { clientType: "firefox" },
        tabConnection: {
          tabTarget,
          threadClient,
          debuggerClient
        }
      }, {
        services: { sourceMaps },
        toolboxActions
      });
    },
    destroy: () => {
      (0, _devtoolsLaunchpad.unmountRoot)(_reactDom2.default);
      _sourceQueue2.default.clear();
      (0, _bootstrap.teardownWorkers)();
    }
  };
} else {
  window.L10N = _devtoolsLaunchpad.L10N;
  // $FlowIgnore:
  window.L10N.setBundle(__webpack_require__(960));

  (0, _devtoolsLaunchpad.bootstrap)(_react2.default, _reactDom2.default).then(connection => {
    (0, _client.onConnect)(connection, {
      services: { sourceMaps: __webpack_require__(1360) },
      toolboxActions: {}
    });
  });
}

/***/ }),
/* 1460 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 { isDevelopment, isTesting } = __webpack_require__(1355);

function debugGlobal(field, value) {
  if (isDevelopment() || isTesting()) {
    window[field] = value;
  }
}

module.exports = {
  debugGlobal
};

/***/ }),
/* 1461 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


const pick = __webpack_require__(67);
const put = __webpack_require__(112);
const fs = __webpack_require__(118);
const path = __webpack_require__(119);

let config;

const flag = __webpack_require__(52);

function isBrowser() {
  return typeof window == "object" && typeof module == "undefined";
}

/**
 * Gets a config value for a given key
 * e.g "chrome.webSocketPort"
 */
function getValue(key) {
  return pick(config, key);
}

function setValue(key, value) {
  return put(config, key, value);
}

function isEnabled(key) {
  return config.features && typeof config.features[key] == "object" ? config.features[key].enabled : config.features[key];
}

function isDevelopment() {
  if (isBrowser()) {
    if (true) {
      return false;
    }
    const href = window.location ? window.location.href : "";
    return href.match(/^file:/) || href.match(/localhost:/);
  }

  if (isFirefoxPanel()) {
    // Default to production if compiling for the Firefox panel
    return "production" === "development";
  }
  return "production" !== "production";
}

function isTesting() {
  return flag.testing;
}

function isFirefoxPanel() {
  return "firefox-panel" == "firefox-panel";
}

function isApplication() {
  return "firefox-panel" == "application";
}

function isFirefox() {
  return (/firefox/i.test(navigator.userAgent)
  );
}

function setConfig(value) {
  config = value;
}

function getConfig() {
  return config;
}

function updateLocalConfig(relativePath) {
  const localConfigPath = path.resolve(relativePath, "../configs/local.json");
  const output = JSON.stringify(config, null, 2);
  fs.writeFileSync(localConfigPath, output, { flag: "w" });
  return output;
}

module.exports = {
  isEnabled,
  getValue,
  setValue,
  isDevelopment,
  isTesting,
  isFirefoxPanel,
  isApplication,
  isFirefox,
  getConfig,
  setConfig,
  updateLocalConfig
};

/***/ }),
/* 1462 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 { sprintf } = __webpack_require__(1463);
const { parse } = __webpack_require__(883);

let strings = {};

function setBundle(bundle) {
  bundle = parse(bundle);
  strings = Object.assign(strings, bundle);
}

function getStr(key) {
  if (!strings[key]) {
    throw new Error(`L10N key ${key} cannot be found.`);
  }
  return strings[key];
}

function getFormatStr(name, ...args) {
  return sprintf(getStr(name), ...args);
}

function numberWithDecimals(number, decimals = 0) {
  // If this is an integer, don't do anything special.
  if (number === (number | 0)) {
    return number;
  }
  // If this isn't a number (and yes, `isNaN(null)` is false), return zero.
  if (isNaN(number) || number === null) {
    return "0";
  }

  let localized = number.toLocaleString();

  // If no grouping or decimal separators are available, bail out, because
  // padding with zeros at the end of the string won't make sense anymore.
  if (!localized.match(/[^\d]/)) {
    return localized;
  }

  return number.toLocaleString(undefined, {
    maximumFractionDigits: decimals,
    minimumFractionDigits: decimals
  });
}

module.exports = {
  setBundle,
  getStr,
  getFormatStr,
  numberWithDecimals
};

/***/ }),
/* 1463 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
var __WEBPACK_AMD_DEFINE_RESULT__;

/* globals window, exports, define */

(function (window) {
    'use strict';

    var re = {
        not_string: /[^s]/,
        not_bool: /[^t]/,
        not_type: /[^T]/,
        not_primitive: /[^v]/,
        number: /[diefg]/,
        numeric_arg: /bcdiefguxX/,
        json: /[j]/,
        not_json: /[^j]/,
        text: /^[^\x25]+/,
        modulo: /^\x25{2}/,
        placeholder: /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-gijosStTuvxX])/,
        key: /^([a-z_][a-z_\d]*)/i,
        key_access: /^\.([a-z_][a-z_\d]*)/i,
        index_access: /^\[(\d+)\]/,
        sign: /^[\+\-]/
    };

    function sprintf() {
        var key = arguments[0],
            cache = sprintf.cache;
        if (!(cache[key] && cache.hasOwnProperty(key))) {
            cache[key] = sprintf.parse(key);
        }
        return sprintf.format.call(null, cache[key], arguments);
    }

    sprintf.format = function (parse_tree, argv) {
        var cursor = 1,
            tree_length = parse_tree.length,
            node_type = '',
            arg,
            output = [],
            i,
            k,
            match,
            pad,
            pad_character,
            pad_length,
            is_positive = true,
            sign = '';
        for (i = 0; i < tree_length; i++) {
            node_type = get_type(parse_tree[i]);
            if (node_type === 'string') {
                output[output.length] = parse_tree[i];
            } else if (node_type === 'array') {
                match = parse_tree[i]; // convenience purposes only
                if (match[2]) {
                    // keyword argument
                    arg = argv[cursor];
                    for (k = 0; k < match[2].length; k++) {
                        if (!arg.hasOwnProperty(match[2][k])) {
                            throw new Error(sprintf('[sprintf] property "%s" does not exist', match[2][k]));
                        }
                        arg = arg[match[2][k]];
                    }
                } else if (match[1]) {
                    // positional argument (explicit)
                    arg = argv[match[1]];
                } else {
                    // positional argument (implicit)
                    arg = argv[cursor++];
                }

                if (re.not_type.test(match[8]) && re.not_primitive.test(match[8]) && get_type(arg) == 'function') {
                    arg = arg();
                }

                if (re.numeric_arg.test(match[8]) && get_type(arg) != 'number' && isNaN(arg)) {
                    throw new TypeError(sprintf("[sprintf] expecting number but found %s", get_type(arg)));
                }

                if (re.number.test(match[8])) {
                    is_positive = arg >= 0;
                }

                switch (match[8]) {
                    case 'b':
                        arg = parseInt(arg, 10).toString(2);
                        break;
                    case 'c':
                        arg = String.fromCharCode(parseInt(arg, 10));
                        break;
                    case 'd':
                    case 'i':
                        arg = parseInt(arg, 10);
                        break;
                    case 'j':
                        arg = JSON.stringify(arg, null, match[6] ? parseInt(match[6]) : 0);
                        break;
                    case 'e':
                        arg = match[7] ? parseFloat(arg).toExponential(match[7]) : parseFloat(arg).toExponential();
                        break;
                    case 'f':
                        arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg);
                        break;
                    case 'g':
                        arg = match[7] ? parseFloat(arg).toPrecision(match[7]) : parseFloat(arg);
                        break;
                    case 'o':
                        arg = arg.toString(8);
                        break;
                    case 's':
                    case 'S':
                        arg = String(arg);
                        arg = match[7] ? arg.substring(0, match[7]) : arg;
                        break;
                    case 't':
                        arg = String(!!arg);
                        arg = match[7] ? arg.substring(0, match[7]) : arg;
                        break;
                    case 'T':
                        arg = get_type(arg);
                        arg = match[7] ? arg.substring(0, match[7]) : arg;
                        break;
                    case 'u':
                        arg = parseInt(arg, 10) >>> 0;
                        break;
                    case 'v':
                        arg = arg.valueOf();
                        arg = match[7] ? arg.substring(0, match[7]) : arg;
                        break;
                    case 'x':
                        arg = parseInt(arg, 10).toString(16);
                        break;
                    case 'X':
                        arg = parseInt(arg, 10).toString(16).toUpperCase();
                        break;
                }
                if (re.json.test(match[8])) {
                    output[output.length] = arg;
                } else {
                    if (re.number.test(match[8]) && (!is_positive || match[3])) {
                        sign = is_positive ? '+' : '-';
                        arg = arg.toString().replace(re.sign, '');
                    } else {
                        sign = '';
                    }
                    pad_character = match[4] ? match[4] === '0' ? '0' : match[4].charAt(1) : ' ';
                    pad_length = match[6] - (sign + arg).length;
                    pad = match[6] ? pad_length > 0 ? str_repeat(pad_character, pad_length) : '' : '';
                    output[output.length] = match[5] ? sign + arg + pad : pad_character === '0' ? sign + pad + arg : pad + sign + arg;
                }
            }
        }
        return output.join('');
    };

    sprintf.cache = {};

    sprintf.parse = function (fmt) {
        var _fmt = fmt,
            match = [],
            parse_tree = [],
            arg_names = 0;
        while (_fmt) {
            if ((match = re.text.exec(_fmt)) !== null) {
                parse_tree[parse_tree.length] = match[0];
            } else if ((match = re.modulo.exec(_fmt)) !== null) {
                parse_tree[parse_tree.length] = '%';
            } else if ((match = re.placeholder.exec(_fmt)) !== null) {
                if (match[2]) {
                    arg_names |= 1;
                    var field_list = [],
                        replacement_field = match[2],
                        field_match = [];
                    if ((field_match = re.key.exec(replacement_field)) !== null) {
                        field_list[field_list.length] = field_match[1];
                        while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
                            if ((field_match = re.key_access.exec(replacement_field)) !== null) {
                                field_list[field_list.length] = field_match[1];
                            } else if ((field_match = re.index_access.exec(replacement_field)) !== null) {
                                field_list[field_list.length] = field_match[1];
                            } else {
                                throw new SyntaxError("[sprintf] failed to parse named argument key");
                            }
                        }
                    } else {
                        throw new SyntaxError("[sprintf] failed to parse named argument key");
                    }
                    match[2] = field_list;
                } else {
                    arg_names |= 2;
                }
                if (arg_names === 3) {
                    throw new Error("[sprintf] mixing positional and named placeholders is not (yet) supported");
                }
                parse_tree[parse_tree.length] = match;
            } else {
                throw new SyntaxError("[sprintf] unexpected placeholder");
            }
            _fmt = _fmt.substring(match[0].length);
        }
        return parse_tree;
    };

    var vsprintf = function (fmt, argv, _argv) {
        _argv = (argv || []).slice(0);
        _argv.splice(0, 0, fmt);
        return sprintf.apply(null, _argv);
    };

    /**
     * helpers
     */
    function get_type(variable) {
        if (typeof variable === 'number') {
            return 'number';
        } else if (typeof variable === 'string') {
            return 'string';
        } else {
            return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase();
        }
    }

    var preformattedPadding = {
        '0': ['', '0', '00', '000', '0000', '00000', '000000', '0000000'],
        ' ': ['', ' ', '  ', '   ', '    ', '     ', '      ', '       '],
        '_': ['', '_', '__', '___', '____', '_____', '______', '_______']
    };
    function str_repeat(input, multiplier) {
        if (multiplier >= 0 && multiplier <= 7 && preformattedPadding[input]) {
            return preformattedPadding[input][multiplier];
        }
        return Array(multiplier + 1).join(input);
    }

    /**
     * export to either browser or node.js
     */
    if (true) {
        exports.sprintf = sprintf;
        exports.vsprintf = vsprintf;
    }
    if (typeof window !== 'undefined') {
        window.sprintf = sprintf;
        window.vsprintf = vsprintf;

        if (true) {
            !(__WEBPACK_AMD_DEFINE_RESULT__ = (function () {
                return {
                    sprintf: sprintf,
                    vsprintf: vsprintf
                };
            }).call(exports, __webpack_require__, exports, module),
				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
        }
    }
})(typeof window === 'undefined' ? undefined : window);

/***/ }),
/* 1464 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 EventEmitter = __webpack_require__(1382);

function inToolbox() {
  return window.parent.document.documentURI == "about:devtools-toolbox";
}

/**
 * A partial implementation of the Menu API provided by electron:
 * https://github.com/electron/electron/blob/master/docs/api/menu.md.
 *
 * Extra features:
 *  - Emits an 'open' and 'close' event when the menu is opened/closed

 * @param String id (non standard)
 *        Needed so tests can confirm the XUL implementation is working
 */
function Menu({ id = null } = {}) {
  this.menuitems = [];
  this.id = id;

  Object.defineProperty(this, "items", {
    get() {
      return this.menuitems;
    }
  });

  EventEmitter.decorate(this);
}

/**
 * Add an item to the end of the Menu
 *
 * @param {MenuItem} menuItem
 */
Menu.prototype.append = function (menuItem) {
  this.menuitems.push(menuItem);
};

/**
 * Add an item to a specified position in the menu
 *
 * @param {int} pos
 * @param {MenuItem} menuItem
 */
Menu.prototype.insert = function (pos, menuItem) {
  throw Error("Not implemented");
};

/**
 * Show the Menu at a specified location on the screen
 *
 * Missing features:
 *   - browserWindow - BrowserWindow (optional) - Default is null.
 *   - positioningItem Number - (optional) OS X
 *
 * @param {int} screenX
 * @param {int} screenY
 * @param Toolbox toolbox (non standard)
 *        Needed so we in which window to inject XUL
 */
Menu.prototype.popup = function (screenX, screenY, toolbox) {
  let doc = toolbox.doc;
  let popupset = doc.querySelector("popupset");
  // See bug 1285229, on Windows, opening the same popup multiple times in a
  // row ends up duplicating the popup. The newly inserted popup doesn't
  // dismiss the old one. So remove any previously displayed popup before
  // opening a new one.
  let popup = popupset.querySelector("menupopup[menu-api=\"true\"]");
  if (popup) {
    popup.hidePopup();
  }

  popup = this.createPopup(doc);
  popup.setAttribute("menu-api", "true");

  if (this.id) {
    popup.id = this.id;
  }
  this._createMenuItems(popup);

  // Remove the menu from the DOM once it's hidden.
  popup.addEventListener("popuphidden", e => {
    if (e.target === popup) {
      popup.remove();
      this.emit("close", popup);
    }
  });

  popup.addEventListener("popupshown", e => {
    if (e.target === popup) {
      this.emit("open", popup);
    }
  });

  popupset.appendChild(popup);
  popup.openPopupAtScreen(screenX, screenY, true);
};

Menu.prototype.createPopup = function (doc) {
  return doc.createElement("menupopup");
};

Menu.prototype._createMenuItems = function (parent) {
  let doc = parent.ownerDocument;
  this.menuitems.forEach(item => {
    if (!item.visible) {
      return;
    }

    if (item.submenu) {
      let menupopup = doc.createElement("menupopup");
      item.submenu._createMenuItems(menupopup);

      let menuitem = doc.createElement("menuitem");
      menuitem.setAttribute("label", item.label);
      if (!inToolbox()) {
        menuitem.textContent = item.label;
      }

      let menu = doc.createElement("menu");
      menu.appendChild(menuitem);
      menu.appendChild(menupopup);
      if (item.disabled) {
        menu.setAttribute("disabled", "true");
      }
      if (item.accesskey) {
        menu.setAttribute("accesskey", item.accesskey);
      }
      if (item.id) {
        menu.id = item.id;
      }
      parent.appendChild(menu);
    } else if (item.type === "separator") {
      let menusep = doc.createElement("menuseparator");
      parent.appendChild(menusep);
    } else {
      let menuitem = doc.createElement("menuitem");
      menuitem.setAttribute("label", item.label);

      if (!inToolbox()) {
        menuitem.textContent = item.label;
      }

      menuitem.addEventListener("command", () => item.click());

      if (item.type === "checkbox") {
        menuitem.setAttribute("type", "checkbox");
      }
      if (item.type === "radio") {
        menuitem.setAttribute("type", "radio");
      }
      if (item.disabled) {
        menuitem.setAttribute("disabled", "true");
      }
      if (item.checked) {
        menuitem.setAttribute("checked", "true");
      }
      if (item.accesskey) {
        menuitem.setAttribute("accesskey", item.accesskey);
      }
      if (item.id) {
        menuitem.id = item.id;
      }

      parent.appendChild(menuitem);
    }
  });
};

Menu.setApplicationMenu = () => {
  throw Error("Not implemented");
};

Menu.sendActionToFirstResponder = () => {
  throw Error("Not implemented");
};

Menu.buildFromTemplate = () => {
  throw Error("Not implemented");
};

module.exports = Menu;

/***/ }),
/* 1465 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 sham for https://dxr.mozilla.org/mozilla-central/source/toolkit/modules/Promise.jsm
 */

/**
 * Promise.jsm is mostly the Promise web API with a `defer` method. Just drop this in here,
 * and use the native web API (although building with webpack/babel, it may replace this
 * with it's own version if we want to target environments that do not have `Promise`.
 */

let p = typeof window != "undefined" ? window.Promise : Promise;
p.defer = function defer() {
  var resolve, reject;
  var promise = new Promise(function () {
    resolve = arguments[0];
    reject = arguments[1];
  });
  return {
    resolve: resolve,
    reject: reject,
    promise: promise
  };
};

module.exports = p;

/***/ }),
/* 1466 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 partial implementation of the MenuItem API provided by electron:
 * https://github.com/electron/electron/blob/master/docs/api/menu-item.md.
 *
 * Missing features:
 *   - id String - Unique within a single menu. If defined then it can be used
 *                 as a reference to this item by the position attribute.
 *   - role String - Define the action of the menu item; when specified the
 *                   click property will be ignored
 *   - sublabel String
 *   - accelerator Accelerator
 *   - icon NativeImage
 *   - position String - This field allows fine-grained definition of the
 *                       specific location within a given menu.
 *
 * Implemented features:
 *  @param Object options
 *    Function click
 *      Will be called with click(menuItem, browserWindow) when the menu item
 *       is clicked
 *    String type
 *      Can be normal, separator, submenu, checkbox or radio
 *    String label
 *    Boolean enabled
 *      If false, the menu item will be greyed out and unclickable.
 *    Boolean checked
 *      Should only be specified for checkbox or radio type menu items.
 *    Menu submenu
 *      Should be specified for submenu type menu items. If submenu is specified,
 *      the type: 'submenu' can be omitted. If the value is not a Menu then it
 *      will be automatically converted to one using Menu.buildFromTemplate.
 *    Boolean visible
 *      If false, the menu item will be entirely hidden.
 */
function MenuItem({
  accesskey = null,
  checked = false,
  click = () => {},
  disabled = false,
  label = "",
  id = null,
  submenu = null,
  type = "normal",
  visible = true
} = {}) {
  this.accesskey = accesskey;
  this.checked = checked;
  this.click = click;
  this.disabled = disabled;
  this.id = id;
  this.label = label;
  this.submenu = submenu;
  this.type = type;
  this.visible = visible;
}

module.exports = MenuItem;

/***/ }),
/* 1467 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 Services = __webpack_require__(22);
const EventEmitter = __webpack_require__(1382);

/**
 * 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", (prefName, prefValue) => {
 *     ...
 *   });
 *
 * @param string prefsRoot
 *        The root path to the required preferences branch.
 * @param object prefsBlueprint
 *        An object containing { accessorName: [prefType, prefName, prefDefault] } keys.
 */
function PrefsHelper(prefsRoot = "", prefsBlueprint = {}) {
  EventEmitter.decorate(this);

  let cache = new Map();

  for (let accessorName in prefsBlueprint) {
    let [prefType, prefName, prefDefault] = prefsBlueprint[accessorName];
    map(this, cache, accessorName, prefType, prefsRoot, prefName, prefDefault);
  }

  let 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
 * @return any
 */
function get(cache, prefType, prefsRoot, prefName) {
  let cachedPref = cache.get(prefName);
  if (cachedPref !== undefined) {
    return cachedPref;
  }
  let value = Services.prefs["get" + prefType + "Pref"]([prefsRoot, prefName].join("."));
  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 prefDefault
 * @param array serializer [optional]
 */
function map(self, cache, accessorName, prefType, prefsRoot, prefName, prefDefault, 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, prefDefault, {
      in: JSON.parse,
      out: JSON.stringify
    });
    return;
  }
  if (prefType == "Float") {
    map(self, cache, accessorName, "Char", prefsRoot, prefName, prefDefault, {
      in: Number.parseFloat,
      out: n => n + ""
    });
    return;
  }

  Object.defineProperty(self, accessorName, {
    get: () => {
      try {
        return serializer.in(get(cache, prefType, prefsRoot, prefName));
      } catch (e) {
        if (typeof prefDefault !== 'undefined') {
          return prefDefault;
        }
        throw e;
      }
    },
    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 (let accessorName in prefsBlueprint) {
    let [, 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: function () {
      this._branch = Services.prefs.getBranch(prefsRoot + ".");
      this._branch.addObserver("", this);
    },
    unregister: function () {
      this._branch.removeObserver("", this);
    },
    observe: function (subject, topic, prefName) {
      // If this particular pref isn't handled by the blueprint object,
      // even though it's in the specified branch, ignore it.
      let accessorName = accessorNameForPref(prefName, prefsBlueprint);
      if (!(accessorName in self)) {
        return;
      }
      cache.delete(prefName);
      self.emit("pref-changed", accessorName, self[accessorName]);
    }
  };
}

exports.PrefsHelper = PrefsHelper;

/***/ }),
/* 1468 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 { appinfo } = __webpack_require__(22);
const EventEmitter = __webpack_require__(1382);
const isOSX = appinfo.OS === "Darwin";

// 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 decribed 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) {
  let modifiers = str.split("+");
  let key = modifiers.pop();

  let shortcut = {
    ctrl: false,
    meta: false,
    alt: false,
    shift: false,
    // Set for character keys
    key: undefined,
    // Set for non-character keys
    keyCode: undefined
  };
  for (let 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) {
    // 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 = window.KeyboardEvent[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) {
  let 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("+");
};

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;
    }
    // Shift is a special modifier, it may implicitely be required if the
    // expected key is a special character accessible via shift.
    if (shortcut.shift != event.shiftKey && event.key && event.key.match(/[a-zA-Z]/)) {
      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.
    let 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 (let [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)) {
      let 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;

/***/ }),
/* 1469 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */



/**
 * Empty shim for "devtools/client/shared/zoom-keys" module
 *
 * Based on nsIMarkupDocumentViewer.fullZoom API
 * https://developer.mozilla.org/en-US/Firefox/Releases/3/Full_page_zoom
 */

exports.register = function (window) {};

/***/ }),
/* 1470 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 firefox = __webpack_require__(1471);
const chrome = __webpack_require__(1472);

function startDebugging(connTarget) {
  if (connTarget.type === "node") {
    return startDebuggingNode(connTarget.param);
  }

  return startDebuggingTab(connTarget);
}

async function startDebuggingNode(tabId) {
  const clientType = "node";
  const tabs = await chrome.connectNodeClient();
  if (!tabs) {
    return {};
  }

  const tab = tabs.find(t => t.id.indexOf(tabId) !== -1);

  if (!tab) {
    return {};
  }

  const tabConnection = await chrome.connectNode(tab.tab);
  chrome.initPage({ clientType, tabConnection });

  return { tabs, tab, clientType, client: chrome, tabConnection };
}

async function startDebuggingTab(connTarget) {
  let clientType = connTarget.type;
  const client = clientType === "chrome" ? chrome : firefox;

  const tabs = await client.connectClient();

  if (!tabs) {
    return undefined;
  }

  const tab = tabs.find(t => t.id.indexOf(connTarget.param) !== -1);
  if (!tab) {
    return undefined;
  }

  const tabConnection = await client.connectTab(tab.tab);

  client.initPage({ tab, clientType, tabConnection });

  return { tab, tabConnection };
}

module.exports = {
  startDebugging,
  firefox,
  chrome
};

/***/ }),
/* 1471 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 {
  DebuggerClient,
  DebuggerTransport,
  TargetFactory,
  WebsocketTransport
} = __webpack_require__(52);

const { getValue } = __webpack_require__(1355);


let debuggerClient = null;

function lookupTabTarget(tab) {
  const options = { client: debuggerClient, form: tab, chrome: false };
  return TargetFactory.forRemoteTab(options);
}

function createTabs(tabs) {
  return tabs.map(tab => {
    return {
      title: tab.title,
      url: tab.url,
      id: tab.actor,
      tab,
      clientType: "firefox"
    };
  });
}

async function connectClient() {
  const useWebSocket = getValue("firefox.webSocketConnection");
  const firefoxHost = useWebSocket ? getValue("firefox.host") : "localhost";
  const firefoxPort = getValue("firefox.webSocketPort");

  const socket = new WebSocket(`ws://${firefoxHost}:${firefoxPort}`);
  const transport = useWebSocket ? new WebsocketTransport(socket) : new DebuggerTransport(socket);

  debuggerClient = new DebuggerClient(transport);
  if (!debuggerClient) {
    return [];
  }

  try {
    await debuggerClient.connect();
    return await getTabs();
  } catch (err) {
    console.log(err);
    return [];
  }
}

async function connectTab(tab) {
  window.addEventListener("beforeunload", () => {
    if (tabTarget !== null) {
      tabTarget.destroy();
    }
  });

  const tabTarget = await lookupTabTarget(tab);

  const [, threadClient] = await tabTarget.activeTab.attachThread({
    ignoreFrameEnvironment: true
  });

  threadClient.resume();
  return { debuggerClient, threadClient, tabTarget };
}

async function getTabs() {
  if (!debuggerClient || !debuggerClient.mainRoot) {
    return undefined;
  }

  const response = await debuggerClient.listTabs();
  return createTabs(response.tabs);
}

function initPage(options) {}

module.exports = {
  connectClient,
  connectTab,
  initPage,
  getTabs
};

/***/ }),
/* 1472 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 CDP = __webpack_require__(52);
const { getValue } = __webpack_require__(1355);
const { networkRequest } = __webpack_require__(1473);

let connection;

function createTabs(tabs, { type, clientType } = {}) {
  return tabs.filter(tab => {
    return tab.type == type;
  }).map(tab => {
    return {
      title: tab.title,
      url: tab.url,
      id: tab.id,
      tab,
      clientType
    };
  });
}

window.criRequest = function (options, callback) {
  const { host, port, path } = options;
  const url = `http://${host}:${port}${path}`;

  networkRequest(url).then(res => callback(null, res.content)).catch(err => callback(err));
};

async function connectClient() {
  if (!getValue("chrome.debug")) {
    return createTabs([]);
  }

  try {
    const tabs = await CDP.List({
      port: getValue("chrome.port"),
      host: getValue("chrome.host")
    });

    return createTabs(tabs, {
      clientType: "chrome",
      type: "page"
    });
  } catch (e) {
    return [];
  }
}

async function connectNodeClient() {
  if (!getValue("node.debug")) {
    return createTabs([]);
  }

  let tabs;
  try {
    tabs = await CDP.List({
      port: getValue("node.port"),
      host: getValue("node.host")
    });
  } catch (e) {
    return undefined;
  }

  return createTabs(tabs, {
    clientType: "node",
    type: "node"
  });
}

async function connectTab(tab) {
  const tabConnection = await CDP({ tab: tab.webSocketDebuggerUrl });
  return tabConnection;
}

async function connectNode(tab) {
  const tabConnection = await CDP({ tab: tab.webSocketDebuggerUrl });

  window.addEventListener("beforeunload", () => {
    tabConnection.onclose = function disable() {};
    tabConnection.close();
  });

  return tabConnection;
}

function initPage({ tab, clientType, tabConnection }) {
  const { Runtime, Page } = tabConnection;

  Runtime.enable();

  if (clientType == "node") {
    Runtime.runIfWaitingForDebugger();
  }

  if (clientType == "chrome") {
    Page.enable();
  }

  return connection;
}

module.exports = {
  connectClient,
  connectNodeClient,
  connectNode,
  connectTab,
  initPage
};

/***/ }),
/* 1473 */
/***/ (function(module, exports, __webpack_require__) {

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 networkRequest = __webpack_require__(1474);
const workerUtils = __webpack_require__(1475);

module.exports = {
  networkRequest,
  workerUtils
};

/***/ }),
/* 1474 */
/***/ (function(module, exports) {

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 networkRequest(url, opts) {
  return fetch(url, {
    cache: opts.loadFromCache ? "default" : "no-cache"
  }).then(res => {
    if (res.status >= 200 && res.status < 300) {
      return res.text().then(text => ({ content: text }));
    }
    return Promise.reject(`request failed with status ${res.status}`);
  });
}

module.exports = networkRequest;

/***/ }),
/* 1475 */
/***/ (function(module, exports) {

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"); }); }; }

function WorkerDispatcher() {
  this.msgId = 1;
  this.worker = null;
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

WorkerDispatcher.prototype = {
  start(url) {
    this.worker = new Worker(url);
    this.worker.onerror = () => {
      console.error(`Error in worker ${url}`);
    };
  },

  stop() {
    if (!this.worker) {
      return;
    }

    this.worker.terminate();
    this.worker = null;
  },

  task(method) {
    return (...args) => {
      return new Promise((resolve, reject) => {
        const id = this.msgId++;
        this.worker.postMessage({ id, method, args });

        const listener = ({ data: result }) => {
          if (result.id !== id) {
            return;
          }

          if (!this.worker) {
            return;
          }

          this.worker.removeEventListener("message", listener);
          if (result.error) {
            reject(result.error);
          } else {
            resolve(result.response);
          }
        };

        this.worker.addEventListener("message", listener);
      });
    };
  }
};

function workerHandler(publicInterface) {
  return function (msg) {
    const { id, method, args } = msg.data;
    try {
      const response = publicInterface[method].apply(undefined, args);
      if (response instanceof Promise) {
        response.then(val => self.postMessage({ id, response: val }),
        // Error can't be sent via postMessage, so be sure to
        // convert to string.
        err => self.postMessage({ id, error: err.toString() }));
      } else {
        self.postMessage({ id, response });
      }
    } catch (error) {
      // Error can't be sent via postMessage, so be sure to convert to
      // string.
      self.postMessage({ id, error: error.toString() });
    }
  };
}

function streamingWorkerHandler(publicInterface, { timeout = 100 } = {}, worker = self) {
  let streamingWorker = (() => {
    var _ref = _asyncToGenerator(function* (id, tasks) {
      let isWorking = true;

      const intervalId = setTimeout(function () {
        isWorking = false;
      }, timeout);

      const results = [];
      while (tasks.length !== 0 && isWorking) {
        const { callback, context, args } = tasks.shift();
        const result = yield callback.call(context, args);
        results.push(result);
      }
      worker.postMessage({ id, status: "pending", data: results });
      clearInterval(intervalId);

      if (tasks.length !== 0) {
        yield streamingWorker(id, tasks);
      }
    });

    return function streamingWorker(_x, _x2) {
      return _ref.apply(this, arguments);
    };
  })();

  return (() => {
    var _ref2 = _asyncToGenerator(function* (msg) {
      const { id, method, args } = msg.data;
      const workerMethod = publicInterface[method];
      if (!workerMethod) {
        console.error(`Could not find ${method} defined in worker.`);
      }
      worker.postMessage({ id, status: "start" });

      try {
        const tasks = workerMethod(args);
        yield streamingWorker(id, tasks);
        worker.postMessage({ id, status: "done" });
      } catch (error) {
        worker.postMessage({ id, status: "error", error });
      }
    });

    return function (_x3) {
      return _ref2.apply(this, arguments);
    };
  })();
}

module.exports = {
  WorkerDispatcher,
  workerHandler,
  streamingWorkerHandler
};

/***/ }),
/* 1476 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 classnames = __webpack_require__(175);
__webpack_require__(1302);

module.exports = function (className) {
  const root = document.createElement("div");
  root.className = classnames(className);
  root.style.setProperty("flex", 1);
  return root;
};

/***/ }),
/* 1477 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 */

const { createStore, applyMiddleware } = __webpack_require__(3);
const { waitUntilService } = __webpack_require__(1478);
const { log } = __webpack_require__(1479);
const { history } = __webpack_require__(1480);
const { promise } = __webpack_require__(1481);
const { thunk } = __webpack_require__(1485);

/**
 * 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:
 *        - log: log all dispatched actions to console
 *        - history: an array to store every action in. Should only be
 *                   used in tests.
 *        - middleware: array of middleware to be included in the redux store
 */
const configureStore = (opts = {}) => {
  const middleware = [thunk(opts.makeThunkArgs), 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.history) {
    middleware.push(history(opts.history));
  }

  if (opts.middleware) {
    opts.middleware.forEach(fn => middleware.push(fn));
  }

  if (opts.log) {
    middleware.push(log);
  }

  // Hook in the redux devtools browser extension if it exists
  const devtoolsExt = typeof window === "object" && window.devToolsExtension ? window.devToolsExtension() : f => f;

  return applyMiddleware(...middleware)(devtoolsExt(createStore));
};

module.exports = configureStore;

/***/ }),
/* 1478 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 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) {
    let readyRequests = [];
    let 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 (let request of pending) {
      if (request.predicate(action)) {
        readyRequests.push(request);
      } else {
        stillPending.push(request);
      }
    }

    pending = stillPending;
    for (let request of readyRequests) {
      request.run(dispatch, getState, action);
    }
  }

  return next => action => {
    if (action.type === NAME) {
      pending.push(action);
      return null;
    }
    let result = next(action);
    checkPending(action);
    return result;
  };
}
exports.waitUntilService = waitUntilService;

/***/ }),
/* 1479 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 middleware that logs all actions coming through the system
 * to the console.
 */
function log({ dispatch, getState }) {
  return next => action => {
    const actionText = JSON.stringify(action, null, 2);
    const truncatedActionText = `${actionText.slice(0, 1000)}...`;
    console.log(`[DISPATCH ${action.type}]`, action, truncatedActionText);
    next(action);
  };
}

exports.log = log;

/***/ }),
/* 1480 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 { isDevelopment } = __webpack_require__(1355);

/**
 * A middleware that stores every action coming through the store in the passed
 * in logging object. Should only be used for tests, as it collects all
 * action information, which will cause memory bloat.
 */
exports.history = (log = []) => ({ dispatch, getState }) => {
  if (isDevelopment()) {
    console.warn("Using history middleware stores all actions in state for " + "testing and devtools is not currently running in test " + "mode. Be sure this is intentional.");
  }
  return next => action => {
    log.push(action);
    next(action);
  };
};

/***/ }),
/* 1481 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 { defer } = __webpack_require__(1412);
const { entries, toObject } = __webpack_require__(1482);
const { executeSoon } = __webpack_require__(1483);

const PROMISE = exports.PROMISE = "@@dispatch/promise";
let seqIdVal = 1;

function seqIdGen() {
  return seqIdVal++;
}

function promiseMiddleware({ dispatch, getState }) {
  return next => action => {
    if (!(PROMISE in action)) {
      return next(action);
    }

    const promiseInst = action[PROMISE];
    const seqId = seqIdGen().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" }));

    // Return the promise so action creators can still compose if they
    // want to.
    const deferred = defer();
    promiseInst.then(value => {
      executeSoon(() => {
        dispatch(Object.assign({}, action, {
          status: "done",
          value: value
        }));
        deferred.resolve(value);
      });
    }, error => {
      executeSoon(() => {
        dispatch(Object.assign({}, action, {
          status: "error",
          error: error.message || error
        }));
        deferred.reject(error);
      });
    });
    return deferred.promise;
  };
}

exports.promise = promiseMiddleware;

/***/ }),
/* 1482 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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/. */

const co = __webpack_require__(965);

function asPaused(client, func) {
  if (client.state != "paused") {
    return co(function* () {
      yield client.interrupt();
      let result;

      try {
        result = yield func();
      } catch (e) {
        // Try to put the debugger back in a working state by resuming
        // it
        yield client.resume();
        throw e;
      }

      yield client.resume();
      return result;
    });
  }
  return func();
}

function handleError(err) {
  console.log("ERROR: ", err);
}

function promisify(context, method, ...args) {
  return new Promise((resolve, reject) => {
    args.push(response => {
      if (response.error) {
        reject(response);
      } else {
        resolve(response);
      }
    });
    method.apply(context, args);
  });
}

function truncateStr(str, size) {
  if (str.length > size) {
    return `${str.slice(0, size)}...`;
  }
  return str;
}

function endTruncateStr(str, size) {
  if (str.length > size) {
    return `...${str.slice(str.length - size)}`;
  }
  return str;
}

let msgId = 1;
function workerTask(worker, method) {
  return function (...args) {
    return new Promise((resolve, reject) => {
      const id = msgId++;
      worker.postMessage({ id, method, args });

      const listener = ({ data: result }) => {
        if (result.id !== id) {
          return;
        }

        worker.removeEventListener("message", listener);
        if (result.error) {
          reject(result.error);
        } else {
          resolve(result.response);
        }
      };

      worker.addEventListener("message", listener);
    });
  };
}

/**
 * Interleaves two arrays element by element, returning the combined array, like
 * a zip. In the case of arrays with different sizes, undefined values will be
 * interleaved at the end along with the extra values of the larger array.
 *
 * @param Array a
 * @param Array b
 * @returns Array
 *          The combined array, in the form [a1, b1, a2, b2, ...]
 */
function zip(a, b) {
  if (!b) {
    return a;
  }
  if (!a) {
    return b;
  }
  const pairs = [];
  for (let i = 0, aLength = a.length, bLength = b.length; i < aLength || i < bLength; i++) {
    pairs.push([a[i], b[i]]);
  }
  return pairs;
}

/**
 * Converts an object into an array with 2-element arrays as key/value
 * pairs of the object. `{ foo: 1, bar: 2}` would become
 * `[[foo, 1], [bar 2]]` (order not guaranteed);
 *
 * @param object obj
 * @returns array
 */
function entries(obj) {
  return Object.keys(obj).map(k => [k, obj[k]]);
}

function mapObject(obj, iteratee) {
  return toObject(entries(obj).map(([key, value]) => {
    return [key, iteratee(key, value)];
  }));
}

/**
 * Takes an array of 2-element arrays as key/values pairs and
 * constructs an object using them.
 */
function toObject(arr) {
  const obj = {};
  for (let pair of arr) {
    obj[pair[0]] = pair[1];
  }
  return obj;
}

/**
 * Composes the given functions into a single function, which will
 * apply the results of each function right-to-left, starting with
 * applying the given arguments to the right-most function.
 * `compose(foo, bar, baz)` === `args => foo(bar(baz(args)`
 *
 * @param ...function funcs
 * @returns function
 */
function compose(...funcs) {
  return (...args) => {
    const initialValue = funcs[funcs.length - 1].apply(null, args);
    const leftFuncs = funcs.slice(0, -1);
    return leftFuncs.reduceRight((composed, f) => f(composed), initialValue);
  };
}

function updateObj(obj, fields) {
  return Object.assign({}, obj, fields);
}

function throttle(func, ms) {
  let timeout, _this;
  return function (...args) {
    _this = this;
    if (!timeout) {
      timeout = setTimeout(() => {
        func.apply(_this, ...args);
        timeout = null;
      }, ms);
    }
  };
}

module.exports = {
  asPaused,
  handleError,
  promisify,
  truncateStr,
  endTruncateStr,
  workerTask,
  zip,
  entries,
  toObject,
  mapObject,
  compose,
  updateObj,
  throttle
};

/***/ }),
/* 1483 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 assert = __webpack_require__(1484);

function reportException(who, exception) {
  let msg = `${who} threw an exception: `;
  console.error(msg, exception);
}

function executeSoon(fn) {
  setTimeout(fn, 0);
}

module.exports = {
  reportException,
  executeSoon,
  assert
};

/***/ }),
/* 1484 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 assert(condition, message) {
  if (!condition) {
    throw new Error(`Assertion failure: ${message}`);
  }
}

module.exports = assert;

/***/ }),
/* 1485 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 middleware that allows thunks (functions) to be dispatched. If
 * it's a thunk, it is called with an argument that contains
 * `dispatch`, `getState`, and any additional args passed in via the
 * middleware constructure. This allows the action to create multiple
 * actions (most likely asynchronously).
 */
function thunk(makeArgs) {
  return ({ dispatch, getState }) => {
    const args = { dispatch, getState };

    return next => action => {
      return typeof action === "function" ? action(makeArgs ? makeArgs(args, getState()) : args) : next(action);
    };
  };
}
exports.thunk = thunk;

/***/ }),
/* 1486 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 tabs = __webpack_require__(1487);
const config = __webpack_require__(1488);

module.exports = {
  tabs,
  config
};

/***/ }),
/* 1487 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 constants = __webpack_require__(1377);
const Immutable = __webpack_require__(146);
const fromJS = __webpack_require__(1414);

const initialState = fromJS({
  tabs: {},
  selectedTab: null,
  filterString: ""
});

function update(state = initialState, action) {
  switch (action.type) {
    case constants.CLEAR_TABS:
      return state.setIn(["tabs"], Immutable.Map()).setIn(["selectedTab"], null);

    case constants.ADD_TABS:
      const tabs = action.value;
      if (!tabs) {
        return state;
      }

      return state.mergeIn(["tabs"], Immutable.Map(tabs.map(tab => {
        tab = Object.assign({}, tab, { id: getTabId(tab) });
        return [tab.id, Immutable.Map(tab)];
      })));

    case constants.SELECT_TAB:
      const tabToSelect = state.getIn(["tabs", action.id]);
      return state.setIn(["selectedTab"], tabToSelect);

    case constants.FILTER_TABS:
      return state.setIn(["filterString"], action.value);
  }

  return state;
}

function getTabId(tab) {
  let id = tab.id;
  const isFirefox = tab.clientType == "firefox";

  // NOTE: we're getting the last part of the actor because
  // we want to ignore the connection id
  if (isFirefox) {
    id = tab.id.split(".").pop();
  }

  return id;
}

module.exports = update;

/***/ }),
/* 1488 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 constants = __webpack_require__(1377);
const fromJS = __webpack_require__(1414);
const I = __webpack_require__(146);

const initialState = fromJS({
  config: I.Map()
});

function update(state = initialState, action) {
  switch (action.type) {
    case constants.SET_VALUE:
      return state.setIn(["config", ...action.path.split(".")], action.value);

    case constants.SET_CONFIG:
      return state.setIn(["config"], fromJS(action.config));
  }

  return state;
}

module.exports = update;

/***/ }),
/* 1489 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 React = __webpack_require__(0);
const { Component } = React;
const PropTypes = __webpack_require__(20);
const ImPropTypes = __webpack_require__(150);
const { connect } = __webpack_require__(1189);
const { bindActionCreators } = __webpack_require__(3);
const { getTabs, getFilterString, getConfig } = __webpack_require__(1490);
const { getValue } = __webpack_require__(1355);
const LandingPage = React.createFactory(__webpack_require__(1491));

class LaunchpadApp extends Component {
  static get propTypes() {
    return {
      tabs: ImPropTypes.map.isRequired,
      filterString: PropTypes.string,
      actions: PropTypes.object,
      config: PropTypes.object
    };
  }

  render() {
    const {
      filterString,
      actions: { setValue, filterTabs }, config } = this.props;

    return LandingPage({
      tabs: this.props.tabs,
      supportsFirefox: !!getValue("firefox"),
      supportsChrome: !!getValue("chrome"),
      title: getValue("title"),
      filterString,
      onFilterChange: filterTabs,
      onTabClick: url => {
        window.location = url;
      },
      config,
      setValue
    });
  }
}

function mapStateToProps(state) {
  return {
    tabs: getTabs(state),
    filterString: getFilterString(state),
    config: getConfig(state)
  };
}

function mapDispatchToProps(dispatch) {
  return {
    actions: bindActionCreators(__webpack_require__(1415), dispatch)
  };
}

module.exports = connect(mapStateToProps, mapDispatchToProps)(LaunchpadApp);

/***/ }),
/* 1490 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 { score } = __webpack_require__(1774);

function getTabs(state) {
  let tabs = state.tabs.get("tabs");
  let filterString = getFilterString(state);

  if (filterString === "") {
    return tabs;
  }

  return tabs.map(tab => {
    const _overallScore = score(tab.get("title"), filterString) + score(tab.get("url"), filterString);
    return tab.set("filteredOut", _overallScore === 0);
  });
}

function getSelectedTab(state) {
  return state.tabs.get("selectedTab");
}

function getFilterString(state) {
  return state.tabs.get("filterString");
}

function getConfig(state) {
  return state.config.get("config").toJS();
}

module.exports = {
  getTabs,
  getSelectedTab,
  getFilterString,
  getConfig
};

/***/ }),
/* 1491 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 React = __webpack_require__(0);

__webpack_require__(1298);
const { Component } = React;
const PropTypes = __webpack_require__(20);
const dom = __webpack_require__(1758);
const ImPropTypes = __webpack_require__(150);
const configMap = __webpack_require__(1377).sidePanelItems;
const Tabs = React.createFactory(__webpack_require__(1492));
const Sidebar = React.createFactory(__webpack_require__(1493));
const Settings = React.createFactory(__webpack_require__(1496));

const githubUrl = "https://github.com/devtools-html/debugger.html/blob/master";

function getTabsByClientType(tabs, clientType) {
  return tabs.valueSeq().filter(tab => tab.get("clientType") == clientType);
}

function firstTimeMessage(title, urlPart) {
  return dom.div({ className: "footer-note" }, `First time connecting to ${title}? Checkout out the `, dom.a({
    href: `${githubUrl}/docs/getting-setup.md#starting-${urlPart}`,
    target: "_blank"
  }, "docs"), ".");
}

class LandingPage extends Component {
  static get propTypes() {
    return {
      tabs: ImPropTypes.map.isRequired,
      supportsFirefox: PropTypes.bool.isRequired,
      supportsChrome: PropTypes.bool.isRequired,
      title: PropTypes.string.isRequired,
      filterString: PropTypes.string,
      onFilterChange: PropTypes.func.isRequired,
      onTabClick: PropTypes.func.isRequired,
      config: PropTypes.object.isRequired,
      setValue: PropTypes.func.isRequired
    };
  }

  constructor(props) {
    super(props);

    this.state = {
      selectedPane: configMap.Firefox.name,
      firefoxConnected: false,
      chromeConnected: false
    };

    this.onFilterChange = this.onFilterChange.bind(this);
    this.onSideBarItemClick = this.onSideBarItemClick.bind(this);
    this.renderLaunchOptions = this.renderLaunchOptions.bind(this);
    this.renderLaunchButton = this.renderLaunchButton.bind(this);
    this.renderExperimentalMessage = this.renderExperimentalMessage.bind(this);
    this.launchBrowser = this.launchBrowser.bind(this);
    this.renderEmptyPanel = this.renderEmptyPanel.bind(this);
    this.renderSettings = this.renderSettings.bind(this);
    this.renderFilter = this.renderFilter.bind(this);
    this.renderPanel = this.renderPanel.bind(this);
  }

  componentDidUpdate() {
    if (this.refs.filterInput) {
      this.refs.filterInput.focus();
    }
  }

  onFilterChange(newFilterString) {
    this.props.onFilterChange(newFilterString);
  }

  onSideBarItemClick(itemTitle) {
    if (itemTitle !== this.state.selectedPane) {
      this.setState({ selectedPane: itemTitle });
    }
  }

  renderLaunchOptions() {
    const { selectedPane } = this.state;
    const { name, isUnderConstruction } = configMap[selectedPane];

    const isConnected = name === configMap.Firefox.name ? this.state.firefoxConnected : this.state.chromeConnected;
    const isNodeSelected = name === configMap.Node.name;

    if (isNodeSelected) {
      return dom.div({ className: "launch-action-container" }, dom.h3({}, "Run a node script in the terminal with `--inspect`"), isUnderConstruction ? this.renderExperimentalMessage(name) : null);
    }

    const connectedStateText = isNodeSelected ? null : `Please open a tab in ${name}`;

    return isConnected ? connectedStateText : this.renderLaunchButton(name, isUnderConstruction);
  }

  renderLaunchButton(browserName, isUnderConstruction) {
    return dom.div({ className: "launch-action-container" }, dom.button({ onClick: () => this.launchBrowser(browserName) }, `Launch ${browserName}`), isUnderConstruction ? this.renderExperimentalMessage(browserName) : null);
  }

  renderExperimentalMessage(browserName) {
    const underConstructionMessage = "Debugging is experimental and certain features won't work (i.e, seeing variables, attaching breakpoints)"; // eslint-disable-line max-len

    return dom.div({ className: "under-construction" }, dom.div({ className: "under-construction-message" }, dom.p({}, underConstructionMessage), dom.img({ src: "/assets/under_construction.png" }), dom.a({
      className: "github-link",
      target: "_blank"
    }, "Help us make it happen")));
  }

  launchBrowser(browser) {
    fetch("/launch", {
      body: JSON.stringify({ browser }),
      headers: {
        "Content-Type": "application/json"
      },
      method: "post"
    }).then(resp => {
      if (browser === configMap.Firefox.name) {
        this.setState({ firefoxConnected: true });
      } else {
        this.setState({ chromeConnected: true });
      }
    }).catch(err => {
      alert(`Error launching ${browser}. ${err.message}`);
    });
  }

  renderEmptyPanel() {
    return dom.div({ className: "hero" }, this.renderLaunchOptions());
  }

  renderSettings() {
    const { config, setValue } = this.props;

    return dom.div({}, dom.header({}, dom.h1({}, configMap.Settings.name)), Settings({ config, setValue }));
  }

  renderFilter() {
    const { selectedPane } = this.state;

    const { tabs, filterString = "" } = this.props;

    const { clientType, paramName } = configMap[selectedPane];

    const targets = getTabsByClientType(tabs, clientType);

    return dom.header({}, dom.input({
      ref: "filterInput",
      placeholder: "Filter tabs",
      value: filterString,
      autoFocus: true,
      type: "search",
      onChange: e => this.onFilterChange(e.target.value),
      onKeyDown: e => {
        if (targets.size === 1 && e.keyCode === 13) {
          this.onTabClick(targets.first(), paramName);
        }
      }
    }));
  }

  renderPanel() {
    const { onTabClick, tabs } = this.props;
    const { selectedPane } = this.state;

    const { name, clientType, paramName } = configMap[selectedPane];

    const clientTargets = getTabsByClientType(tabs, clientType);
    const tabsDetected = clientTargets && clientTargets.count() > 0;
    const targets = clientTargets.filter(t => !t.get("filteredOut"));

    const isSettingsPaneSelected = name === configMap.Settings.name;

    if (isSettingsPaneSelected) {
      return this.renderSettings();
    }

    if (!tabsDetected) {
      return this.renderEmptyPanel();
    }

    return dom.div({}, this.renderFilter(), Tabs({ targets, paramName, onTabClick }));
  }

  render() {
    const { supportsFirefox, supportsChrome, title } = this.props;
    const { selectedPane } = this.state;
    const { onSideBarItemClick } = this;

    const { name, docsUrlPart } = configMap[selectedPane];

    return dom.div({
      className: "landing-page"
    }, Sidebar({
      supportsFirefox,
      supportsChrome,
      title,
      selectedPane,
      onSideBarItemClick
    }), dom.main({ className: "panel" }, this.renderPanel(), firstTimeMessage(name, docsUrlPart)));
  }
}

module.exports = LandingPage;

/***/ }),
/* 1492 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 React = __webpack_require__(0);

__webpack_require__(1299);
const { Component } = React;
const dom = __webpack_require__(1758);
const PropTypes = __webpack_require__(20);
const classnames = __webpack_require__(175);

function getTabURL(tab, paramName) {
  const tabID = tab.get("id");
  return `/?react_perf&${paramName}=${tabID}`;
}

class Tabs extends Component {
  static get propTypes() {
    return {
      targets: PropTypes.object.isRequired,
      paramName: PropTypes.string.isRequired,
      onTabClick: PropTypes.func.isRequired
    };
  }

  constructor(props) {
    super(props);
    this.onTabClick = this.onTabClick.bind(this);
  }

  onTabClick(tab, paramName) {
    this.props.onTabClick(getTabURL(tab, paramName));
  }

  render() {
    const { targets, paramName } = this.props;

    if (!targets || targets.count() == 0) {
      return dom.div({}, "");
    }

    let tabClassNames = ["tab"];
    if (targets.size === 1) {
      tabClassNames.push("active");
    }

    return dom.div({ className: "tab-group" }, dom.ul({ className: "tab-list" }, targets.valueSeq().map(tab => dom.li({
      className: classnames("tab", {
        active: targets.size === 1
      }),
      key: tab.get("id"),
      tabIndex: 0,
      role: "link",
      onClick: () => this.onTabClick(tab, paramName),
      onKeyDown: e => {
        if (e.keyCode === 13) {
          this.onTabClick(tab, paramName);
        }
      }
    }, dom.div({ className: "tab-title" }, tab.get("title")), dom.div({ className: "tab-url" }, tab.get("url"))))));
  }
}

module.exports = Tabs;

/***/ }),
/* 1493 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 React = __webpack_require__(0);
__webpack_require__(1300);
const { Component } = React;
const dom = __webpack_require__(1758);
const PropTypes = __webpack_require__(20);
const classnames = __webpack_require__(175);
const Svg = __webpack_require__(1494);

class Sidebar extends Component {
  static get propTypes() {
    return {
      supportsFirefox: PropTypes.bool.isRequired,
      supportsChrome: PropTypes.bool.isRequired,
      title: PropTypes.string.isRequired,
      selectedPane: PropTypes.string.isRequired,
      onSideBarItemClick: PropTypes.func.isRequired
    };
  }

  constructor(props) {
    super(props);
    this.renderTitle = this.renderTitle.bind(this);
    this.renderItem = this.renderItem.bind(this);
  }

  renderTitle(title) {
    return dom.div({ className: "title-wrapper" }, dom.h1({}, title), dom.div({ className: "launchpad-container" }, Svg({ name: "rocket" }), dom.h2({ className: "launchpad-container-title" }, "Launchpad")));
  }

  renderItem(title) {
    return dom.li({
      className: classnames({
        selected: title == this.props.selectedPane
      }),
      key: title,
      tabIndex: 0,
      role: "button",
      onClick: () => this.props.onSideBarItemClick(title),
      onKeyDown: e => {
        if (e.keyCode === 13) {
          this.props.onSideBarItemClick(title);
        }
      }
    }, dom.a({}, title));
  }

  render() {
    let connections = [];

    if (this.props.supportsFirefox) {
      connections.push("Firefox");
    }

    if (this.props.supportsChrome) {
      connections.push("Chrome", "Node");
    }

    return dom.aside({
      className: "sidebar"
    }, this.renderTitle(this.props.title), dom.ul({}, connections.map(title => this.renderItem(title)), this.renderItem("Settings")));
  }
}

module.exports = Sidebar;

/***/ }),
/* 1494 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


const React = __webpack_require__(0);
const { default: InlineSVG } = __webpack_require__(1763);
const { isDevelopment } = __webpack_require__(1355);

const svg = {
  rocket: __webpack_require__(1126)
};

function Svg({ name, className, onClick, "aria-label": ariaLabel }) {
  className = `${name} ${className || ""}`;

  const props = {
    className,
    onClick,
    ["aria-label"]: ariaLabel,
    src: svg[name]
  };

  return React.createElement(InlineSVG, props);
}

Svg.displayName = "Svg";

module.exports = Svg;

/***/ }),
/* 1495 */,
/* 1496 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 React = __webpack_require__(0);
const { Component } = React;
const dom = __webpack_require__(1758);
const PropTypes = __webpack_require__(20);
const { showMenu, buildMenu } = __webpack_require__(1413);

class Settings extends Component {
  static get propTypes() {
    return {
      config: PropTypes.object.isRequired,
      setValue: PropTypes.func.isRequired
    };
  }

  constructor(props) {
    super(props);
    this.onConfigContextMenu = this.onConfigContextMenu.bind(this);
    this.onInputHandler = this.onInputHandler.bind(this);
    this.renderConfig = this.renderConfig.bind(this);
    this.renderFeatures = this.renderFeatures.bind(this);
  }

  onConfigContextMenu(event, key) {
    event.preventDefault();

    const { setValue, config } = this.props;

    const setConfig = (path, value) => {
      setValue(path, value);
    };

    const ltrMenuItem = {
      id: "node-menu-ltr",
      label: "ltr",
      disabled: config[key] === "ltr",
      click: () => setConfig(key, "ltr")
    };

    const rtlMenuItem = {
      id: "node-menu-rtl",
      label: "rtl",
      disabled: config[key] === "rtl",
      click: () => setConfig(key, "rtl")
    };

    const lightMenuItem = {
      id: "node-menu-light",
      label: "light",
      disabled: config[key] === "light",
      click: () => setConfig(key, "light")
    };

    const darkMenuItem = {
      id: "node-menu-dark",
      label: "dark",
      disabled: config[key] === "dark",
      click: () => setConfig(key, "dark")
    };

    const firebugMenuItem = {
      id: "node-menu-firebug",
      label: "firebug",
      disabled: config[key] === "firebug",
      click: () => setConfig(key, "firebug")
    };

    const items = {
      "dir": [{ item: ltrMenuItem }, { item: rtlMenuItem }],
      "theme": [{ item: lightMenuItem }, { item: darkMenuItem }, { item: firebugMenuItem }]
    };
    showMenu(event, buildMenu(items[key]));
  }

  onInputHandler(e, path) {
    const { setValue } = this.props;
    setValue(path, e.target.checked);
  }

  renderConfig(config) {
    const configs = [{ name: "dir", label: "direction" }, { name: "theme", label: "theme"
      // Hiding hotReloading option for now. See Issue #242
      // { name: "hotReloading", label: "hot reloading", bool: true }
    }];

    return dom.ul({ className: "tab-list" }, configs.map(c => {
      return dom.li({ key: c.name, className: "tab tab-sides" }, dom.div({ className: "tab-title" }, c.label), c.bool ? dom.input({
        type: "checkbox",
        defaultChecked: config[c.name],
        onChange: e => this.onInputHandler(e, c.name)
      }, null) : dom.div({
        className: "tab-value",
        onClick: e => this.onConfigContextMenu(e, c.name)
      }, config[c.name]));
    }));
  }

  renderFeatures(features) {
    return dom.ul({ className: "tab-list" }, Object.keys(features).map(key => dom.li({
      className: "tab tab-sides",
      key
    }, dom.div({ className: "tab-title" }, typeof features[key] == "object" ? features[key].label : key), dom.div({ className: "tab-value" }, dom.input({
      type: "checkbox",
      defaultChecked: features[key].enabled,
      onChange: e => this.onInputHandler(e, `features.${key}.enabled`)
    })))));
  }

  render() {
    const { config } = this.props;

    return dom.div({ className: "tab-group" }, dom.h3({}, "Configurations"), this.renderConfig(config), config.features ? (dom.h3({}, "Features"), this.renderFeatures(config.features)) : null);
  }
}

module.exports = Settings;

/***/ }),
/* 1497 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 */

/**
 * Redux actions for the pause state
 * @module actions/tabs
 */

const constants = __webpack_require__(1377);

/**
 * @typedef {Object} TabAction
 * @memberof actions/tabs
 * @static
 * @property {number} type The type of Action
 * @property {number} value The payload of the Action
 */

/**
 * @memberof actions/tabs
 * @static
 * @returns {TabAction} with type constants.CLEAR_TABS and tabs as value
 */
function clearTabs() {
  return {
    type: constants.CLEAR_TABS
  };
}

/**
 * @memberof actions/tabs
 * @static
 * @param {Array} tabs
 * @returns {TabAction} with type constants.ADD_TABS and tabs as value
 */
function newTabs(tabs) {
  return ({ getState, dispatch }) => {
    return dispatch({
      type: constants.ADD_TABS,
      value: tabs
    });
  };
}

/**
 * @memberof actions/tabs
 * @static
 * @param {String} $0.id Unique ID of the tab to select
 * @returns {TabAction}
 */
function selectTab({ id }) {
  return {
    type: constants.SELECT_TAB,
    id: id
  };
}

/**
 * @memberof actions/tabs
 * @static
 * @param {String} value String which should be used to filter tabs
 * @returns {TabAction} with type constants.FILTER_TABS
 *          and filter string as value
 */
function filterTabs(value) {
  return {
    type: constants.FILTER_TABS,
    value
  };
}

module.exports = {
  newTabs,
  selectTab,
  filterTabs,
  clearTabs
};

/***/ }),
/* 1498 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 { setConfig: _setConfig } = __webpack_require__(1355);
const { updateTheme, updateDir } = __webpack_require__(1362);

/**
 * Redux actions for the pause state
 * @module actions/config
 */

const constants = __webpack_require__(1377);


/**
 * @typedef {Object} ConfigAction
 * @memberof actions/config
 * @static
 * @property {number} type The type of Action
 * @property {number} value The payload of the Action
 */

/**
 * @memberof actions/config
 * @static
 * @param {string} path
 * @param {string} value
 * @returns {ConfigAction} with type constants.SET_VALUE and value
 */
function setValue(path, value) {
  return async function ({ dispatch }) {
    const response = await fetch("/setconfig", {
      method: "post",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ path, value })
    });

    const config = await response.json();
    _setConfig(config);
    updateTheme();
    updateDir();

    dispatch({
      type: constants.SET_VALUE,
      path,
      value
    });
  };
}

/**
 * @memberof actions/config
 * @static
 * @param {string} config
 * @returns {ConfigAction} with type constants.SET_CONFIG and config
 */
function setConfig(config) {
  return {
    type: constants.SET_CONFIG,
    config
  };
}

module.exports = {
  setValue,
  setConfig
};

/***/ }),
/* 1499 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.onConnect = undefined;

var _firefox = __webpack_require__(1500);

var firefox = _interopRequireWildcard(_firefox);

var _chrome = __webpack_require__(1507);

var chrome = _interopRequireWildcard(_chrome);

var _prefs = __webpack_require__(226);

var _dbg = __webpack_require__(2246);

var _bootstrap = __webpack_require__(1430);

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

function loadFromPrefs(actions) {
  const { pauseOnExceptions, ignoreCaughtExceptions } = _prefs.prefs;
  if (pauseOnExceptions || ignoreCaughtExceptions) {
    return actions.pauseOnExceptions(pauseOnExceptions, ignoreCaughtExceptions);
  }
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.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 getClient(connection) {
  const { tab: { clientType } } = connection;
  return clientType == "firefox" ? firefox : chrome;
}

async function onConnect(connection, { services, toolboxActions }) {
  // NOTE: the landing page does not connect to a JS process
  if (!connection) {
    return;
  }

  const client = getClient(connection);
  const commands = client.clientCommands;
  const { store, actions, selectors } = (0, _bootstrap.bootstrapStore)(commands, {
    services,
    toolboxActions
  });

  (0, _bootstrap.bootstrapWorkers)();
  await client.onConnect(connection, actions);
  await loadFromPrefs(actions);

  (0, _dbg.setupHelper)({
    store,
    actions,
    selectors,
    client: client.clientCommands
  });

  (0, _bootstrap.bootstrapApp)(connection, { store, actions });

  return { store, actions, selectors, client: commands };
}

exports.onConnect = onConnect;

/***/ }),
/* 1500 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.clientEvents = exports.clientCommands = exports.createObjectClient = undefined;
exports.onConnect = onConnect;

var _commands = __webpack_require__(1501);

var _events = __webpack_require__(1506);

var _prefs = __webpack_require__(226);

let DebuggerClient; /* This Source Code Form is subject to the terms of the Mozilla Public
                     * License, v. 2.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 createObjectClient(grip) {
  return DebuggerClient.createObjectClient(grip);
}

async function onConnect(connection, actions) {
  const {
    tabConnection: { tabTarget, threadClient, debuggerClient }
  } = connection;

  DebuggerClient = debuggerClient;

  if (!tabTarget || !threadClient || !debuggerClient) {
    return { bpClients: {} };
  }

  const supportsWasm = _prefs.features.wasm && !!debuggerClient.mainRoot.traits.wasmBinarySource;

  const { bpClients } = (0, _commands.setupCommands)({
    threadClient,
    tabTarget,
    debuggerClient,
    supportsWasm
  });

  if (actions) {
    (0, _events.setupEvents)({ threadClient, actions, supportsWasm });
  }

  tabTarget.on("will-navigate", actions.willNavigate);
  tabTarget.on("navigate", actions.navigated);

  await threadClient.reconfigure({
    observeAsmJS: true,
    wasmBinarySource: supportsWasm
  });

  // In Firefox, we need to initially request all of the sources. This
  // usually fires off individual `newSource` notifications as the
  // debugger finds them, but there may be existing sources already in
  // the debugger (if it's paused already, or if loading the page from
  // bfcache) so explicity fire `newSource` events for all returned
  // sources.
  const sources = await _commands.clientCommands.fetchSources();
  const traits = tabTarget.activeTab ? tabTarget.activeTab.traits : null;
  await actions.connect(tabTarget.url, traits && traits.canRewind);
  await actions.newSources(sources);

  // If the threadClient is already paused, make sure to show a
  // paused state.
  const pausedPacket = threadClient.getLastPausePacket();
  if (pausedPacket) {
    _events.clientEvents.paused("paused", pausedPacket);
  }

  return { bpClients };
}

exports.createObjectClient = createObjectClient;
exports.clientCommands = _commands.clientCommands;
exports.clientEvents = _events.clientEvents;

/***/ }),
/* 1501 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.clientCommands = exports.setupCommands = undefined;

var _breakpoint = __webpack_require__(1364);

var _create = __webpack_require__(1428);

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

let bpClients;
let threadClient;
let tabTarget;
let debuggerClient;
let supportsWasm;

function setupCommands(dependencies) {
  threadClient = dependencies.threadClient;
  tabTarget = dependencies.tabTarget;
  debuggerClient = dependencies.debuggerClient;
  supportsWasm = dependencies.supportsWasm;
  bpClients = {};

  return { bpClients };
}

function resume() {
  return new Promise(resolve => {
    threadClient.resume(resolve);
  });
}

function stepIn() {
  return new Promise(resolve => {
    threadClient.stepIn(resolve);
  });
}

function stepOver() {
  return new Promise(resolve => {
    threadClient.stepOver(resolve);
  });
}

function stepOut() {
  return new Promise(resolve => {
    threadClient.stepOut(resolve);
  });
}

function rewind() {
  return new Promise(resolve => {
    threadClient.rewind(resolve);
  });
}

function reverseStepIn() {
  return new Promise(resolve => {
    threadClient.reverseStepIn(resolve);
  });
}

function reverseStepOver() {
  return new Promise(resolve => {
    threadClient.reverseStepOver(resolve);
  });
}

function reverseStepOut() {
  return new Promise(resolve => {
    threadClient.reverseStepOut(resolve);
  });
}

function breakOnNext() {
  return threadClient.breakOnNext();
}

function sourceContents(sourceId) {
  const sourceClient = threadClient.source({ actor: sourceId });
  return sourceClient.source();
}

function getBreakpointByLocation(location) {
  const id = (0, _breakpoint.makePendingLocationId)(location);
  const bpClient = bpClients[id];

  if (bpClient) {
    const { actor, url, line, column, condition } = bpClient.location;
    return {
      id: bpClient.actor,
      condition,
      actualLocation: {
        line,
        column,
        sourceId: actor,
        sourceUrl: url
      }
    };
  }
  return null;
}

function setBreakpoint(location, condition, noSliding) {
  const sourceClient = threadClient.source({ actor: location.sourceId });

  return sourceClient.setBreakpoint({
    line: location.line,
    column: location.column,
    condition,
    noSliding
  }).then(([{ actualLocation }, bpClient]) => {
    actualLocation = (0, _create.createBreakpointLocation)(location, actualLocation);
    const id = (0, _breakpoint.makePendingLocationId)(actualLocation);
    bpClients[id] = bpClient;
    bpClient.location.line = actualLocation.line;
    bpClient.location.column = actualLocation.column;
    bpClient.location.url = actualLocation.sourceUrl || "";

    return { id, actualLocation };
  });
}

function removeBreakpoint(generatedLocation) {
  try {
    const id = (0, _breakpoint.makePendingLocationId)(generatedLocation);
    const bpClient = bpClients[id];
    if (!bpClient) {
      console.warn("No breakpoint to delete on server");
      return Promise.resolve();
    }
    delete bpClients[id];
    return bpClient.remove();
  } catch (_error) {
    console.warn("No breakpoint to delete on server");
  }
}

function setBreakpointCondition(breakpointId, location, condition, noSliding) {
  const bpClient = bpClients[breakpointId];
  delete bpClients[breakpointId];

  return bpClient.setCondition(threadClient, condition, noSliding).then(_bpClient => {
    bpClients[breakpointId] = _bpClient;
    return { id: breakpointId };
  });
}

function evaluateInFrame(frameId, script) {
  return evaluate(script, { frameId });
}

function evaluate(script, { frameId } = {}) {
  const params = frameId ? { frameActor: frameId } : {};
  if (!tabTarget || !tabTarget.activeConsole || !script) {
    return Promise.resolve();
  }

  return new Promise(resolve => {
    tabTarget.activeConsole.evaluateJS(script, result => resolve(result), params);
  });
}

function debuggeeCommand(script) {
  tabTarget.activeConsole.evaluateJS(script, () => {}, {});

  if (!debuggerClient) {
    return;
  }

  const consoleActor = tabTarget.form.consoleActor;
  const request = debuggerClient._activeRequests.get(consoleActor);
  request.emit("json-reply", {});
  debuggerClient._activeRequests.delete(consoleActor);

  return Promise.resolve();
}

function navigate(url) {
  return tabTarget.activeTab.navigateTo(url);
}

function reload() {
  return tabTarget.activeTab.reload();
}

function getProperties(grip) {
  const objClient = threadClient.pauseGrip(grip);

  return objClient.getPrototypeAndProperties().then(resp => {
    const { ownProperties, safeGetterValues } = resp;
    for (const name in safeGetterValues) {
      const { enumerable, writable, getterValue } = safeGetterValues[name];
      ownProperties[name] = { enumerable, writable, value: getterValue };
    }
    return resp;
  });
}

async function getFrameScopes(frame) {
  if (frame.scope) {
    return frame.scope;
  }

  return threadClient.getEnvironment(frame.id);
}

function pauseOnExceptions(shouldPauseOnExceptions, shouldIgnoreCaughtExceptions) {
  return threadClient.pauseOnExceptions(shouldPauseOnExceptions, shouldIgnoreCaughtExceptions);
}

function prettyPrint(sourceId, indentSize) {
  const sourceClient = threadClient.source({ actor: sourceId });
  return sourceClient.prettyPrint(indentSize);
}

async function blackBox(sourceId, isBlackBoxed) {
  const sourceClient = threadClient.source({ actor: sourceId });
  if (isBlackBoxed) {
    await sourceClient.unblackBox();
  } else {
    await sourceClient.blackBox();
  }

  return { isBlackBoxed: !isBlackBoxed };
}

function disablePrettyPrint(sourceId) {
  const sourceClient = threadClient.source({ actor: sourceId });
  return sourceClient.disablePrettyPrint();
}

function interrupt() {
  return threadClient.interrupt();
}

function eventListeners() {
  return threadClient.eventListeners();
}

function pauseGrip(func) {
  return threadClient.pauseGrip(func);
}

async function fetchSources() {
  const { sources } = await threadClient.getSources();
  return sources.map(source => (0, _create.createSource)(source, { supportsWasm }));
}

async function fetchWorkers() {
  // NOTE: The Worker and Browser Content toolboxes do not have a parent
  // with a listWorkers function
  // TODO: there is a listWorkers property, but it is not a function on the
  // parent. Investigate what it is
  if (!threadClient._parent || typeof threadClient._parent.listWorkers != "function") {
    return Promise.resolve({ workers: [] });
  }

  return threadClient._parent.listWorkers();
}

const clientCommands = {
  blackBox,
  interrupt,
  eventListeners,
  pauseGrip,
  resume,
  stepIn,
  stepOut,
  stepOver,
  rewind,
  reverseStepIn,
  reverseStepOut,
  reverseStepOver,
  breakOnNext,
  sourceContents,
  getBreakpointByLocation,
  setBreakpoint,
  removeBreakpoint,
  setBreakpointCondition,
  evaluate,
  evaluateInFrame,
  debuggeeCommand,
  navigate,
  reload,
  getProperties,
  getFrameScopes,
  pauseOnExceptions,
  prettyPrint,
  disablePrettyPrint,
  fetchSources,
  fetchWorkers
};

exports.setupCommands = setupCommands;
exports.clientCommands = clientCommands;

/***/ }),
/* 1502 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var _immutable = __webpack_require__(146);

var I = _interopRequireWildcard(_immutable);

var _lodash = __webpack_require__(2);

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

// hasOwnProperty is defensive because it is possible that the
// object that we're creating a map for has a `hasOwnProperty` field
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/**
 * Immutable JS conversion utils
 * @deprecated
 * @module utils/fromJS
 */

function hasOwnProperty(value, key) {
  if (value.hasOwnProperty && (0, _lodash.isFunction)(value.hasOwnProperty)) {
    return value.hasOwnProperty(key);
  }

  if (value.prototype && value.prototype.hasOwnProperty) {
    return value.prototype.hasOwnProperty(key);
  }

  return false;
}

/*
  creates an immutable map, where each of the value's
  items are transformed into their own map.

  NOTE: we guard against `length` being a property because
  length confuses Immutable's internal algorithm.
*/
function createMap(value) {
  const hasLength = hasOwnProperty(value, "length");
  const length = value.length;

  if (hasLength) {
    value.length = `${value.length}`;
  }

  let map = I.Seq(value).map(fromJS).toMap();

  if (hasLength) {
    map = map.set("length", length);
    value.length = length;
  }

  return map;
}

function createList(value) {
  return I.Seq(value).map(fromJS).toList();
}

/**
 * When our app state is fully typed, we should be able to get rid of
 * this function. This is only temporarily necessary to support
 * converting typed objects to immutable.js, which usually happens in
 * reducers.
 *
 * @memberof utils/fromJS
 * @static
 */
function fromJS(value) {
  if (Array.isArray(value)) {
    return createList(value);
  }
  if (value && value.constructor && value.constructor.meta) {
    // This adds support for tcomb objects which are native JS objects
    // but are not "plain", so the above checks fail. Since they
    // behave the same we can use the same constructors, but we need
    // special checks for them.
    const kind = value.constructor.meta.kind;
    if (kind === "struct") {
      return createMap(value);
    } else if (kind === "list") {
      return createList(value);
    }
  }

  // If it's a primitive type, just return the value. Note `==` check
  // for null, which is intentionally used to match either `null` or
  // `undefined`.
  if (value == null || typeof value !== "object") {
    return value;
  }

  // Otherwise, treat it like an object. We can't reliably detect if
  // it's a plain object because we might be objects from other JS
  // contexts so `Object !== Object`.

  return createMap(value);
}

module.exports = fromJS;

/***/ }),
/* 1503 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getBreakpointAtLocation = getBreakpointAtLocation;

var _sources = __webpack_require__(1369);

var _breakpoints = __webpack_require__(1378);

var _devtoolsSourceMap = __webpack_require__(1360);

function isGenerated(selectedSource) {
  const sourceId = selectedSource.get("id");
  return (0, _devtoolsSourceMap.isGeneratedId)(sourceId);
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.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 getColumn(column, selectedSource) {
  if (column) {
    return column;
  }

  return isGenerated(selectedSource) ? undefined : 0;
}

function getLocation(bp, selectedSource) {
  return isGenerated(selectedSource) ? bp.generatedLocation || bp.location : bp.location;
}

function getBreakpointsForSource(state, selectedSource) {
  const breakpoints = (0, _breakpoints.getBreakpoints)(state);

  return breakpoints.filter(bp => {
    const location = getLocation(bp, selectedSource);
    return location.sourceId === selectedSource.get("id");
  });
}

function findBreakpointAtLocation(breakpoints, selectedSource, { line, column }) {
  return breakpoints.find(breakpoint => {
    const location = getLocation(breakpoint, selectedSource);
    const sameLine = location.line === line;
    if (!sameLine) {
      return false;
    }

    if (column === undefined) {
      return true;
    }

    return location.column === getColumn(column, selectedSource);
  });
}

/*
 * Finds a breakpoint at a location (line, column) of the
 * selected source.
 *
 * This is useful for finding a breakpoint when the
 * user clicks in the gutter or on a token.
 */
function getBreakpointAtLocation(state, location) {
  const selectedSource = (0, _sources.getSelectedSource)(state);
  const breakpoints = getBreakpointsForSource(state, selectedSource);

  return findBreakpointAtLocation(breakpoints, selectedSource, location);
}

/***/ }),
/* 1504 */,
/* 1505 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.isSelectedFrameVisible = isSelectedFrameVisible;

var _pause = __webpack_require__(1394);

var _sources = __webpack_require__(1369);

/*
 * Checks to if the selected frame's source is currently
 * selected.
 */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 isSelectedFrameVisible(state) {
  const selectedLocation = (0, _sources.getSelectedLocation)(state);
  const selectedFrame = (0, _pause.getSelectedFrame)(state);

  return selectedFrame && selectedLocation && selectedFrame.location.sourceId == selectedLocation.sourceId;
}

/***/ }),
/* 1506 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.clientEvents = exports.setupEvents = undefined;

var _create = __webpack_require__(1428);

var _sourceQueue = __webpack_require__(1795);

var _sourceQueue2 = _interopRequireDefault(_sourceQueue);

var _prefs = __webpack_require__(226);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 CALL_STACK_PAGE_SIZE = 1000;

let threadClient;
let actions;
let supportsWasm;
let isInterrupted;

function setupEvents(dependencies) {
  threadClient = dependencies.threadClient;
  actions = dependencies.actions;
  supportsWasm = dependencies.supportsWasm;
  _sourceQueue2.default.initialize({ actions, supportsWasm, createSource: _create.createSource });

  if (threadClient) {
    Object.keys(clientEvents).forEach(eventName => {
      threadClient.addListener(eventName, clientEvents[eventName]);
    });

    if (threadClient._parent) {
      threadClient._parent.addListener("workerListChanged", workerListChanged);
    }
  }
}

async function paused(_, packet) {
  // If paused by an explicit interrupt, which are generated by the
  // slow script dialog and internal events such as setting
  // breakpoints, ignore the event.
  const { why } = packet;
  if (why.type === "interrupted" && !packet.why.onNext) {
    isInterrupted = true;
    return;
  }

  // Eagerly fetch the frames
  const response = await threadClient.getFrames(0, CALL_STACK_PAGE_SIZE);

  if (why.type != "alreadyPaused") {
    const pause = (0, _create.createPause)(packet, response);
    await _sourceQueue2.default.flush();
    actions.paused(pause);
  }
}

function resumed(_, packet) {
  // NOTE: the client suppresses resumed events while interrupted
  // to prevent unintentional behavior.
  // see [client docs](../README.md#interrupted) for more information.
  if (isInterrupted) {
    isInterrupted = false;
    return;
  }

  actions.resumed(packet);
}

function newSource(_, { source }) {
  _sourceQueue2.default.queue(source);

  if (_prefs.features.eventListeners) {
    actions.fetchEventListeners();
  }
}

function workerListChanged() {
  actions.updateWorkers();
}

const clientEvents = {
  paused,
  resumed,
  newSource
};

exports.setupEvents = setupEvents;
exports.clientEvents = clientEvents;

/***/ }),
/* 1507 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.clientEvents = exports.clientCommands = undefined;
exports.onConnect = onConnect;

var _commands = __webpack_require__(1508);

var _events = __webpack_require__(1509);

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

async function onConnect(connection, actions) {
  const { tabConnection, connTarget: { type } } = connection;
  const { Debugger, Runtime, Page } = tabConnection;

  Debugger.enable();
  Debugger.setPauseOnExceptions({ state: "none" });
  Debugger.setAsyncCallStackDepth({ maxDepth: 0 });

  if (type == "chrome") {
    Page.frameNavigated(_events.pageEvents.frameNavigated);
    Page.frameStartedLoading(_events.pageEvents.frameStartedLoading);
    Page.frameStoppedLoading(_events.pageEvents.frameStoppedLoading);
  }

  Debugger.scriptParsed(_events.clientEvents.scriptParsed);
  Debugger.scriptFailedToParse(_events.clientEvents.scriptFailedToParse);
  Debugger.paused(_events.clientEvents.paused);
  Debugger.resumed(_events.clientEvents.resumed);

  (0, _commands.setupCommands)({ Debugger, Runtime, Page });
  (0, _events.setupEvents)({ actions, Page, type, Runtime });
  return {};
}

exports.clientCommands = _commands.clientCommands;
exports.clientEvents = _events.clientEvents;

/***/ }),
/* 1508 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.clientCommands = exports.setupCommands = undefined;

var _create = __webpack_require__(1429);

let debuggerAgent; /* This Source Code Form is subject to the terms of the Mozilla Public
                    * License, v. 2.0. If a copy of the MPL was not distributed with this
                    * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

let runtimeAgent;
let pageAgent;

function setupCommands({ Debugger, Runtime, Page }) {
  debuggerAgent = Debugger;
  runtimeAgent = Runtime;
  pageAgent = Page;
}

function resume() {
  return debuggerAgent.resume();
}

function stepIn() {
  return debuggerAgent.stepInto();
}

function stepOver() {
  return debuggerAgent.stepOver();
}

function stepOut() {
  return debuggerAgent.stepOut();
}

function pauseOnExceptions(shouldPauseOnExceptions, shouldIgnoreCaughtExceptions) {
  if (!shouldPauseOnExceptions) {
    return debuggerAgent.setPauseOnExceptions({ state: "none" });
  }
  const state = shouldIgnoreCaughtExceptions ? "uncaught" : "all";
  return debuggerAgent.setPauseOnExceptions({ state });
}

function breakOnNext() {
  return debuggerAgent.pause();
}

function sourceContents(sourceId) {
  return debuggerAgent.getScriptSource({ scriptId: sourceId }).then(({ scriptSource }) => ({
    source: scriptSource,
    contentType: null
  }));
}

async function setBreakpoint(location, condition) {
  const {
    breakpointId,
    serverLocation
  } = await debuggerAgent.setBreakpoint({
    location: (0, _create.toServerLocation)(location),
    columnNumber: location.column
  });

  const actualLocation = (0, _create.fromServerLocation)(serverLocation) || location;

  return {
    id: breakpointId,
    actualLocation: actualLocation
  };
}

function removeBreakpoint(breakpointId) {
  return debuggerAgent.removeBreakpoint({ breakpointId });
}

async function getProperties(object) {
  const { result } = await runtimeAgent.getProperties({
    objectId: object.objectId
  });

  const loadedObjects = result.map(_create.createLoadedObject);

  return { loadedObjects };
}

function evaluate(script) {
  return runtimeAgent.evaluate({ expression: script });
}

function debuggeeCommand(script) {
  evaluate(script);
  return Promise.resolve();
}

function navigate(url) {
  return pageAgent.navigate({ url });
}

const clientCommands = {
  resume,
  stepIn,
  stepOut,
  stepOver,
  pauseOnExceptions,
  breakOnNext,
  sourceContents,
  setBreakpoint,
  removeBreakpoint,
  evaluate,
  debuggeeCommand,
  navigate,
  getProperties
};

exports.setupCommands = setupCommands;
exports.clientCommands = clientCommands;

/***/ }),
/* 1509 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.clientEvents = exports.pageEvents = exports.setupEvents = undefined;

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; }; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                                                                                                                                                                                   * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                                                                                                                                                                                                                                   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

var _create = __webpack_require__(1429);

let actions;
let pageAgent;
let clientType;
let runtimeAgent;

function setupEvents(dependencies) {
  actions = dependencies.actions;
  pageAgent = dependencies.Page;
  clientType = dependencies.clientType;
  runtimeAgent = dependencies.Runtime;
}

// Debugger Events
function scriptParsed({
  scriptId,
  url,
  startLine,
  startColumn,
  endLine,
  endColumn,
  executionContextId,
  hash,
  isContentScript,
  isInternalScript,
  isLiveEdit,
  sourceMapURL,
  hasSourceURL,
  deprecatedCommentWasUsed
}) {
  if (isContentScript) {
    return;
  }

  if (clientType == "node") {
    sourceMapURL = undefined;
  }

  actions.newSource({
    id: scriptId,
    url,
    sourceMapURL,
    isPrettyPrinted: false
  });
}

function scriptFailedToParse() {}

async function paused({
  callFrames,
  reason,
  data,
  hitBreakpoints,
  asyncStackTrace
}) {
  const frames = callFrames.map(_create.createFrame);
  const frame = frames[0];
  const why = _extends({ type: reason }, data);

  const objectId = frame.scopeChain[0].object.objectId;
  const { result } = await runtimeAgent.getProperties({
    objectId
  });

  const loadedObjects = result.map(_create.createLoadedObject);

  if (clientType == "chrome") {
    pageAgent.configureOverlay({ message: "Paused in debugger.html" });
  }

  await actions.paused({ frame, why, frames, loadedObjects });
}

function resumed() {
  if (clientType == "chrome") {
    pageAgent.configureOverlay({ suspended: false });
  }

  actions.resumed();
}

function globalObjectCleared() {}

// Page Events
function frameNavigated(frame) {
  actions.navigated();
}

function frameStartedLoading() {
  actions.willNavigate();
}

function domContentEventFired() {}

function loadEventFired() {}

function frameStoppedLoading() {}

const clientEvents = {
  scriptParsed,
  scriptFailedToParse,
  paused,
  resumed,
  globalObjectCleared
};

const pageEvents = {
  frameNavigated,
  frameStartedLoading,
  domContentEventFired,
  loadEventFired,
  frameStoppedLoading
};

exports.setupEvents = setupEvents;
exports.pageEvents = pageEvents;
exports.clientEvents = clientEvents;

/***/ }),
/* 1510 */,
/* 1511 */,
/* 1512 */,
/* 1513 */,
/* 1514 */,
/* 1515 */,
/* 1516 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _expressions = __webpack_require__(1417);

var _expressions2 = _interopRequireDefault(_expressions);

var _eventListeners = __webpack_require__(1420);

var _eventListeners2 = _interopRequireDefault(_eventListeners);

var _sources = __webpack_require__(1369);

var _sources2 = _interopRequireDefault(_sources);

var _breakpoints = __webpack_require__(1378);

var _breakpoints2 = _interopRequireDefault(_breakpoints);

var _pendingBreakpoints = __webpack_require__(1419);

var _pendingBreakpoints2 = _interopRequireDefault(_pendingBreakpoints);

var _asyncRequests = __webpack_require__(1517);

var _asyncRequests2 = _interopRequireDefault(_asyncRequests);

var _pause = __webpack_require__(1394);

var _pause2 = _interopRequireDefault(_pause);

var _ui = __webpack_require__(1421);

var _ui2 = _interopRequireDefault(_ui);

var _fileSearch = __webpack_require__(1422);

var _fileSearch2 = _interopRequireDefault(_fileSearch);

var _ast = __webpack_require__(1383);

var _ast2 = _interopRequireDefault(_ast);

var _coverage = __webpack_require__(1423);

var _coverage2 = _interopRequireDefault(_coverage);

var _projectTextSearch = __webpack_require__(1424);

var _projectTextSearch2 = _interopRequireDefault(_projectTextSearch);

var _replay = __webpack_require__(2342);

var _replay2 = _interopRequireDefault(_replay);

var _quickOpen = __webpack_require__(1635);

var _quickOpen2 = _interopRequireDefault(_quickOpen);

var _sourceTree = __webpack_require__(1426);

var _sourceTree2 = _interopRequireDefault(_sourceTree);

var _debuggee = __webpack_require__(1418);

var _debuggee2 = _interopRequireDefault(_debuggee);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
 * Reducer index
 * @module reducers/index
 */

exports.default = {
  expressions: _expressions2.default,
  eventListeners: _eventListeners2.default,
  sources: _sources2.default,
  breakpoints: _breakpoints2.default,
  pendingBreakpoints: _pendingBreakpoints2.default,
  asyncRequests: _asyncRequests2.default,
  pause: _pause2.default,
  ui: _ui2.default,
  fileSearch: _fileSearch2.default,
  ast: _ast2.default,
  coverage: _coverage2.default,
  projectTextSearch: _projectTextSearch2.default,
  replay: _replay2.default,
  quickOpen: _quickOpen2.default,
  sourceTree: _sourceTree2.default,
  debuggee: _debuggee2.default
};

/***/ }),
/* 1517 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
 * Async request reducer
 * @module reducers/async-request
 */

const initialAsyncRequestState = [];

function update(state = initialAsyncRequestState, action) {
  const { seqId } = action;

  if (action.type === "NAVIGATE") {
    return initialAsyncRequestState;
  } else if (seqId) {
    let newState;
    if (action.status === "start") {
      newState = [...state, seqId];
    } else if (action.status === "error" || action.status === "done") {
      newState = state.filter(id => id !== seqId);
    }

    return newState;
  }

  return state;
}

exports.default = update;

/***/ }),
/* 1518 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _propTypes = __webpack_require__(20);

var _propTypes2 = _interopRequireDefault(_propTypes);

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _reactRedux = __webpack_require__(1189);

var _redux = __webpack_require__(3);

var _prefs = __webpack_require__(226);

var _actions = __webpack_require__(1354);

var _actions2 = _interopRequireDefault(_actions);

var _ShortcutsModal = __webpack_require__(1535);

var _selectors = __webpack_require__(1352);

var _devtoolsModules = __webpack_require__(1376);

__webpack_require__(1305);

__webpack_require__(1306);

__webpack_require__(1307);

__webpack_require__(1308);

var _devtoolsSplitter = __webpack_require__(1440);

var _devtoolsSplitter2 = _interopRequireDefault(_devtoolsSplitter);

var _ProjectSearch = __webpack_require__(2009);

var _ProjectSearch2 = _interopRequireDefault(_ProjectSearch);

var _PrimaryPanes = __webpack_require__(1551);

var _PrimaryPanes2 = _interopRequireDefault(_PrimaryPanes);

var _Editor = __webpack_require__(1554);

var _Editor2 = _interopRequireDefault(_Editor);

var _SecondaryPanes = __webpack_require__(1599);

var _SecondaryPanes2 = _interopRequireDefault(_SecondaryPanes);

var _WelcomeBox = __webpack_require__(1613);

var _WelcomeBox2 = _interopRequireDefault(_WelcomeBox);

var _Tabs = __webpack_require__(1614);

var _Tabs2 = _interopRequireDefault(_Tabs);

var _QuickOpenModal = __webpack_require__(1652);

var _QuickOpenModal2 = _interopRequireDefault(_QuickOpenModal);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

const shortcuts = new _devtoolsModules.KeyShortcuts({ window }); /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                  * License, v. 2.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 { appinfo } = _devtoolsModules.Services;

const isMacOS = appinfo.OS === "Darwin";

const horizontalLayoutBreakpoint = window.matchMedia("(min-width: 800px)");
const verticalLayoutBreakpoint = window.matchMedia("(min-width: 10px) and (max-width: 800px)");

class App extends _react.Component {

  constructor(props) {
    super(props);
    this.state = {
      shortcutsModalEnabled: false,
      startPanelSize: 0,
      endPanelSize: 0
    };

    this.getChildContext = this.getChildContext.bind(this);
    this.onLayoutChange = this.onLayoutChange.bind(this);
    this.toggleQuickOpenModal = this.toggleQuickOpenModal.bind(this);
    this.renderEditorPane = this.renderEditorPane.bind(this);
    this.renderLayout = this.renderLayout.bind(this);
    this.onEscape = this.onEscape.bind(this);
    this.onCommandSlash = this.onCommandSlash.bind(this);
  }

  getChildContext() {
    return { shortcuts };
  }

  componentDidMount() {
    horizontalLayoutBreakpoint.addListener(this.onLayoutChange);
    verticalLayoutBreakpoint.addListener(this.onLayoutChange);
    this.setOrientation();

    shortcuts.on(L10N.getStr("symbolSearch.search.key2"), (_, e) => this.toggleQuickOpenModal(_, e, "@"));

    const searchKeys = [L10N.getStr("sources.search.key2"), L10N.getStr("sources.search.alt.key")];
    searchKeys.forEach(key => shortcuts.on(key, this.toggleQuickOpenModal));

    shortcuts.on(L10N.getStr("gotoLineModal.key2"), (_, e) => this.toggleQuickOpenModal(_, e, ":"));

    shortcuts.on("Escape", this.onEscape);
    shortcuts.on("Cmd+/", this.onCommandSlash);
  }

  componentWillUnmount() {
    horizontalLayoutBreakpoint.removeListener(this.onLayoutChange);
    verticalLayoutBreakpoint.removeListener(this.onLayoutChange);
    shortcuts.off(L10N.getStr("symbolSearch.search.key2"), this.toggleQuickOpenModal);

    const searchKeys = [L10N.getStr("sources.search.key2"), L10N.getStr("sources.search.alt.key")];
    searchKeys.forEach(key => shortcuts.off(key, this.toggleQuickOpenModal));

    shortcuts.off(L10N.getStr("gotoLineModal.key2"), this.toggleQuickOpenModal);

    shortcuts.off("Escape", this.onEscape);
  }

  onEscape(_, e) {
    const {
      activeSearch,
      quickOpenEnabled,
      closeActiveSearch,
      closeQuickOpen
    } = this.props;

    if (activeSearch) {
      e.preventDefault();
      closeActiveSearch();
    }

    if (quickOpenEnabled === true) {
      closeQuickOpen();
    }
  }

  onCommandSlash() {
    this.toggleShortcutsModal();
  }

  isHorizontal() {
    return this.props.orientation === "horizontal";
  }

  toggleQuickOpenModal(_, e, query) {
    const { quickOpenEnabled, openQuickOpen, closeQuickOpen } = this.props;

    e.preventDefault();
    e.stopPropagation();

    if (quickOpenEnabled === true) {
      closeQuickOpen();
      return;
    }

    if (query != null) {
      openQuickOpen(query);
      return;
    }
    openQuickOpen();
    return;
  }

  onLayoutChange() {
    this.setOrientation();
  }

  setOrientation() {
    // If the orientation does not match (if it is not visible) it will
    // not setOrientation, or if it is the same as before, calling
    // setOrientation will not cause a rerender.
    if (horizontalLayoutBreakpoint.matches) {
      this.props.setOrientation("horizontal");
    } else if (verticalLayoutBreakpoint.matches) {
      this.props.setOrientation("vertical");
    }
  }

  renderEditorPane() {
    const { startPanelCollapsed, endPanelCollapsed } = this.props;
    const { endPanelSize, startPanelSize } = this.state;
    const horizontal = this.isHorizontal();

    return _react2.default.createElement(
      "div",
      { className: "editor-pane" },
      _react2.default.createElement(
        "div",
        { className: "editor-container" },
        _react2.default.createElement(_Tabs2.default, {
          startPanelCollapsed: startPanelCollapsed,
          endPanelCollapsed: endPanelCollapsed,
          horizontal: horizontal,
          startPanelSize: startPanelSize,
          endPanelSize: endPanelSize
        }),
        _react2.default.createElement(_Editor2.default, {
          horizontal: horizontal,
          startPanelSize: startPanelSize,
          endPanelSize: endPanelSize
        }),
        !this.props.selectedSource ? _react2.default.createElement(_WelcomeBox2.default, { horizontal: horizontal }) : null,
        _react2.default.createElement(_ProjectSearch2.default, null)
      )
    );
  }

  toggleShortcutsModal() {
    this.setState({
      shortcutsModalEnabled: !this.state.shortcutsModalEnabled
    });
  }

  renderLayout() {
    const { startPanelCollapsed, endPanelCollapsed } = this.props;
    const horizontal = this.isHorizontal();

    const maxSize = horizontal ? "70%" : "95%";
    const primaryInitialSize = horizontal ? "250px" : "150px";

    return _react2.default.createElement(_devtoolsSplitter2.default, {
      style: { width: "100vw" },
      initialHeight: 400,
      initialWidth: 300,
      minSize: 30,
      maxSize: maxSize,
      splitterSize: 1,
      vert: horizontal,
      startPanel: _react2.default.createElement(_devtoolsSplitter2.default, {
        style: { width: "100vw" },
        initialSize: primaryInitialSize,
        minSize: 30,
        maxSize: "85%",
        splitterSize: 1,
        startPanelCollapsed: startPanelCollapsed,
        startPanel: _react2.default.createElement(_PrimaryPanes2.default, { horizontal: horizontal }),
        endPanel: this.renderEditorPane()
      }),
      endPanelControl: true,
      endPanel: _react2.default.createElement(_SecondaryPanes2.default, {
        horizontal: horizontal,
        toggleShortcutsModal: () => this.toggleShortcutsModal()
      }),
      endPanelCollapsed: endPanelCollapsed
    });
  }

  renderShortcutsModal() {
    const additionalClass = isMacOS ? "mac" : "";

    if (!_prefs.features.shortcuts) {
      return;
    }

    return _react2.default.createElement(_ShortcutsModal.ShortcutsModal, {
      additionalClass: additionalClass,
      enabled: this.state.shortcutsModalEnabled,
      handleClose: () => this.toggleShortcutsModal()
    });
  }

  render() {
    const { quickOpenEnabled } = this.props;
    return _react2.default.createElement(
      "div",
      { className: "debugger" },
      this.renderLayout(),
      quickOpenEnabled === true && _react2.default.createElement(_QuickOpenModal2.default, {
        shortcutsModalEnabled: this.state.shortcutsModalEnabled,
        toggleShortcutsModal: () => this.toggleShortcutsModal()
      }),
      this.renderShortcutsModal()
    );
  }
}

App.childContextTypes = { shortcuts: _propTypes2.default.object };

function mapStateToProps(state) {
  return {
    selectedSource: (0, _selectors.getSelectedSource)(state),
    startPanelCollapsed: (0, _selectors.getPaneCollapse)(state, "start"),
    endPanelCollapsed: (0, _selectors.getPaneCollapse)(state, "end"),
    activeSearch: (0, _selectors.getActiveSearch)(state),
    quickOpenEnabled: (0, _selectors.getQuickOpenEnabled)(state),
    orientation: (0, _selectors.getOrientation)(state)
  };
}

exports.default = (0, _reactRedux.connect)(mapStateToProps, dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(App);

/***/ }),
/* 1519 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

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; }; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                                                                                                                                                                                   * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                                                                                                                                                                                                                                   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

var _breakpoint = __webpack_require__(1364);

var _selectors = __webpack_require__(1352);

var _sourceMaps = __webpack_require__(1397);

exports.default = async function addBreakpoint(getState, client, sourceMaps, { breakpoint }) {
  const state = getState();

  const source = (0, _selectors.getSource)(state, breakpoint.location.sourceId);
  const sourceRecord = source.toJS();
  const location = _extends({}, breakpoint.location, { sourceUrl: source.get("url") });
  const generatedLocation = await (0, _sourceMaps.getGeneratedLocation)(state, sourceRecord, location, sourceMaps);

  (0, _breakpoint.assertLocation)(location);
  (0, _breakpoint.assertLocation)(generatedLocation);

  if ((0, _breakpoint.breakpointExists)(state, location)) {
    const newBreakpoint = _extends({}, breakpoint, { location, generatedLocation });
    (0, _breakpoint.assertBreakpoint)(newBreakpoint);
    return { breakpoint: newBreakpoint };
  }

  const { id, hitCount, actualLocation } = await client.setBreakpoint(generatedLocation, breakpoint.condition, sourceMaps.isOriginalId(location.sourceId));

  const newGeneratedLocation = actualLocation || generatedLocation;
  const newLocation = await sourceMaps.getOriginalLocation(newGeneratedLocation);

  const symbols = (0, _selectors.getSymbols)(getState(), sourceRecord);
  const astLocation = await (0, _breakpoint.getASTLocation)(sourceRecord, symbols, newLocation);

  const newBreakpoint = {
    id,
    disabled: false,
    hidden: breakpoint.hidden,
    loading: false,
    condition: breakpoint.condition,
    location: newLocation,
    astLocation,
    hitCount,
    generatedLocation: newGeneratedLocation
  };

  (0, _breakpoint.assertBreakpoint)(newBreakpoint);

  const previousLocation = (0, _breakpoint.locationMoved)(location, newLocation) ? location : null;

  return {
    breakpoint: newBreakpoint,
    previousLocation
  };
};

/***/ }),
/* 1520 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

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; };

exports.default = remapLocations;
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 remapLocations(breakpoints, sourceId, sourceMaps) {
  const sourceBreakpoints = breakpoints.map(async breakpoint => {
    if (breakpoint.location.sourceId !== sourceId) {
      return breakpoint;
    }
    const location = await sourceMaps.getOriginalLocation(breakpoint.location);
    return _extends({}, breakpoint, { location });
  });

  return Promise.all(sourceBreakpoints.valueSeq());
}

/***/ }),
/* 1521 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

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; }; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                                                                                                                                                                                   * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                                                                                                                                                                                                                                   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

exports.syncClientBreakpoint = syncClientBreakpoint;

var _breakpoint = __webpack_require__(1364);

var _sourceMaps = __webpack_require__(1397);

var _devtoolsSourceMap = __webpack_require__(1360);

var _selectors = __webpack_require__(1352);

async function makeScopedLocation({ name, offset }, location, source) {
  const scope = await (0, _breakpoint.findScopeByName)(source, name);
  // fallback onto the location line, if the scope is not found
  // note: we may at some point want to delete the breakpoint if the scope
  // disappears
  const line = scope ? scope.location.start.line + offset.line : location.line;
  return {
    line,
    column: location.column,
    sourceUrl: source.url,
    sourceId: source.id
  };
}

function createSyncData(id, pendingBreakpoint, location, generatedLocation, previousLocation) {
  const overrides = _extends({}, pendingBreakpoint, { generatedLocation, id });
  const breakpoint = (0, _breakpoint.createBreakpoint)(location, overrides);

  (0, _breakpoint.assertBreakpoint)(breakpoint);
  return { breakpoint, previousLocation };
}

// we have three forms of syncing: disabled syncing, existing server syncing
// and adding a new breakpoint
async function syncClientBreakpoint(getState, client, sourceMaps, sourceId, pendingBreakpoint) {
  (0, _breakpoint.assertPendingBreakpoint)(pendingBreakpoint);

  const source = (0, _selectors.getSource)(getState(), sourceId).toJS();
  const generatedSourceId = sourceMaps.isOriginalId(sourceId) ? (0, _devtoolsSourceMap.originalToGeneratedId)(sourceId) : sourceId;

  const { location, astLocation } = pendingBreakpoint;
  const previousLocation = _extends({}, location, { sourceId });

  const scopedLocation = await makeScopedLocation(astLocation, previousLocation, source);

  const scopedGeneratedLocation = await (0, _sourceMaps.getGeneratedLocation)(getState(), source, scopedLocation, sourceMaps);

  // this is the generatedLocation of the pending breakpoint, with
  // the source id updated to reflect the new connection
  const generatedLocation = _extends({}, pendingBreakpoint.generatedLocation, {
    sourceId: generatedSourceId
  });

  const isSameLocation = !(0, _breakpoint.locationMoved)(generatedLocation, scopedGeneratedLocation);

  const existingClient = client.getBreakpointByLocation(generatedLocation);

  /** ******* CASE 1: No server change ***********/
  // early return if breakpoint is disabled or we are in the sameLocation
  // send update only to redux
  if (pendingBreakpoint.disabled || existingClient && isSameLocation) {
    const id = pendingBreakpoint.disabled ? "" : existingClient.id;
    return createSyncData(id, pendingBreakpoint, scopedLocation, scopedGeneratedLocation, previousLocation);
  }

  // clear server breakpoints if they exist and we have moved
  if (existingClient) {
    await client.removeBreakpoint(generatedLocation);
  }

  /** ******* Case 2: Add New Breakpoint ***********/
  // If we are not disabled, set the breakpoint on the server and get
  // that info so we can set it on our breakpoints.

  if (!scopedGeneratedLocation.line) {
    return { previousLocation, breakpoint: null };
  }

  const { id, actualLocation } = await client.setBreakpoint(scopedGeneratedLocation, pendingBreakpoint.condition, sourceMaps.isOriginalId(sourceId));

  // the breakpoint might have slid server side, so we want to get the location
  // based on the server's return value
  const newGeneratedLocation = actualLocation;
  const newLocation = await sourceMaps.getOriginalLocation(newGeneratedLocation);

  return createSyncData(id, pendingBreakpoint, newLocation, newGeneratedLocation, previousLocation);
}

/***/ }),
/* 1522 */,
/* 1523 */,
/* 1524 */,
/* 1525 */,
/* 1526 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.buildQuery = undefined;
exports.getMatchIndex = getMatchIndex;
exports.removeOverlay = removeOverlay;
exports.find = find;
exports.findNext = findNext;
exports.findPrev = findPrev;

var _buildQuery = __webpack_require__(1402);

var _buildQuery2 = _interopRequireDefault(_buildQuery);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/**
 * @memberof utils/source-search
 * @static
 */
function getSearchCursor(cm, query, pos, modifiers) {
  const regexQuery = (0, _buildQuery2.default)(query, modifiers, { isGlobal: true });
  return cm.getSearchCursor(regexQuery, pos);
}

/**
 * @memberof utils/source-search
 * @static
 */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 SearchState() {
  this.posFrom = this.posTo = this.query = null;
  this.overlay = null;
  this.results = [];
}

/**
 * @memberof utils/source-search
 * @static
 */
function getSearchState(cm, query, modifiers) {
  const state = cm.state.search || (cm.state.search = new SearchState());
  return state;
}

function isWhitespace(query) {
  return !query.match(/\S/);
}

/**
 * This returns a mode object used by CoeMirror's addOverlay function
 * to parse and style tokens in the file.
 * The mode object contains a tokenizer function (token) which takes
 * a character stream as input, advances it a character at a time,
 * and returns style(s) for that token. For more details see
 * https://codemirror.net/doc/manual.html#modeapi
 *
 * Also the token function code is mainly based of work done
 * by the chrome devtools team. Thanks guys! :)
 *
 * @memberof utils/source-search
 * @static
 */
function searchOverlay(query, modifiers) {
  const regexQuery = (0, _buildQuery2.default)(query, modifiers, {
    ignoreSpaces: true,
    // regex must be global for the overlay
    isGlobal: true
  });

  return {
    token: function (stream, state) {
      // set the last index to be the current stream position
      // this acts as an offset
      regexQuery.lastIndex = stream.pos;
      const match = regexQuery.exec(stream.string);
      if (match && match.index === stream.pos) {
        // if we have a match at the current stream position
        // set the class for a match
        stream.pos += match[0].length || 1;
        return "highlight highlight-full";
      } else if (match) {
        // if we have a match somewhere in the line, go to that point in the
        // stream
        stream.pos = match.index;
      } else {
        // if we have no matches in this line, skip to the end of the line
        stream.skipToEnd();
      }
    }
  };
}

/**
 * @memberof utils/source-search
 * @static
 */
function updateOverlay(cm, state, query, modifiers) {
  cm.removeOverlay(state.overlay);
  state.overlay = searchOverlay(query, modifiers);
  cm.addOverlay(state.overlay, { opaque: false });
}

function updateCursor(cm, state, keepSelection) {
  state.posTo = cm.getCursor("anchor");
  state.posFrom = cm.getCursor("head");

  if (!keepSelection) {
    state.posTo = { line: 0, ch: 0 };
    state.posFrom = { line: 0, ch: 0 };
  }
}

function getMatchIndex(count, currentIndex, rev) {
  if (!rev) {
    if (currentIndex == count - 1) {
      return 0;
    }

    return currentIndex + 1;
  }

  if (currentIndex == 0) {
    return count - 1;
  }

  return currentIndex - 1;
}

/**
 * If there's a saved search, selects the next results.
 * Otherwise, creates a new search and selects the first
 * result.
 *
 * @memberof utils/source-search
 * @static
 */
function doSearch(ctx, rev, query, keepSelection, modifiers) {
  const { cm } = ctx;
  if (!cm) {
    return;
  }
  const defaultIndex = { line: -1, ch: -1 };

  return cm.operation(function () {
    if (!query || isWhitespace(query)) {
      clearSearch(cm, query, modifiers);
      return;
    }

    const state = getSearchState(cm, query, modifiers);
    const isNewQuery = state.query !== query;
    state.query = query;

    updateOverlay(cm, state, query, modifiers);
    updateCursor(cm, state, keepSelection);
    const searchLocation = searchNext(ctx, rev, query, isNewQuery, modifiers);

    return searchLocation ? searchLocation.from : defaultIndex;
  });
}

function getCursorPos(newQuery, rev, state) {
  if (newQuery) {
    return rev ? state.posFrom : state.posTo;
  }

  return rev ? state.posTo : state.posFrom;
}

/**
 * Selects the next result of a saved search.
 *
 * @memberof utils/source-search
 * @static
 */
function searchNext(ctx, rev, query, newQuery, modifiers) {
  const { cm, ed } = ctx;
  let nextMatch;
  cm.operation(function () {
    const state = getSearchState(cm, query, modifiers);
    const pos = getCursorPos(newQuery, rev, state);

    if (!state.query) {
      return;
    }

    let cursor = getSearchCursor(cm, state.query, pos, modifiers);

    const location = rev ? { line: cm.lastLine(), ch: null } : { line: cm.firstLine(), ch: 0 };

    if (!cursor.find(rev) && state.query) {
      cursor = getSearchCursor(cm, state.query, location, modifiers);
      if (!cursor.find(rev)) {
        return;
      }
    }

    // We don't want to jump the editor
    // when we're selecting text
    if (!cm.state.selectingText) {
      ed.alignLine(cursor.from().line, "center");
      cm.setSelection(cursor.from(), cursor.to());
    }

    nextMatch = { from: cursor.from(), to: cursor.to() };
  });

  return nextMatch;
}

/**
 * Remove overlay.
 *
 * @memberof utils/source-search
 * @static
 */
function removeOverlay(ctx, query, modifiers) {
  const state = getSearchState(ctx.cm, query, modifiers);
  ctx.cm.removeOverlay(state.overlay);
  const { line, ch } = ctx.cm.getCursor();
  ctx.cm.doc.setSelection({ line, ch }, { line, ch }, { scroll: false });
}

/**
 * Clears the currently saved search.
 *
 * @memberof utils/source-search
 * @static
 */
function clearSearch(cm, query, modifiers) {
  const state = getSearchState(cm, query, modifiers);

  state.results = [];

  if (!state.query) {
    return;
  }
  cm.removeOverlay(state.overlay);
  state.query = null;
}

/**
 * Starts a new search.
 *
 * @memberof utils/source-search
 * @static
 */
function find(ctx, query, keepSelection, modifiers) {
  clearSearch(ctx.cm, query, modifiers);
  return doSearch(ctx, false, query, keepSelection, modifiers);
}

/**
 * Finds the next item based on the currently saved search.
 *
 * @memberof utils/source-search
 * @static
 */
function findNext(ctx, query, keepSelection, modifiers) {
  return doSearch(ctx, false, query, keepSelection, modifiers);
}

/**
 * Finds the previous item based on the currently saved search.
 *
 * @memberof utils/source-search
 * @static
 */
function findPrev(ctx, query, keepSelection, modifiers) {
  return doSearch(ctx, true, query, keepSelection, modifiers);
}

exports.buildQuery = _buildQuery2.default;

/***/ }),
/* 1527 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.fetchEventListeners = fetchEventListeners;
exports.updateEventBreakpoints = updateEventBreakpoints;

var _DevToolsUtils = __webpack_require__(1432);

var _selectors = __webpack_require__(1352);

var _waitService = __webpack_require__(1659);

// delay is in ms
const FETCH_EVENT_LISTENERS_DELAY = 200; /* This Source Code Form is subject to the terms of the Mozilla Public
                                          * License, v. 2.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 gThreadClient setNamedTimeout EVENTS */
/* eslint no-shadow: 0  */

/**
 * Redux actions for the event listeners state
 * @module actions/event-listeners
 */

let fetchListenersTimerID;

/**
 * @memberof utils/utils
 * @static
 */
async function asPaused(state, client, func) {
  if (!(0, _selectors.isPaused)(state)) {
    await client.interrupt();
    let result;

    try {
      result = await func(client);
    } catch (e) {
      // Try to put the debugger back in a working state by resuming
      // it
      await client.resume();
      throw e;
    }

    await client.resume();
    return result;
  }

  return func(client);
}

/**
 * @memberof actions/event-listeners
 * @static
 */
function fetchEventListeners() {
  return ({ dispatch, getState, client }) => {
    // Make sure we"re not sending a batch of closely repeated requests.
    // This can easily happen whenever new sources are fetched.
    if (fetchListenersTimerID) {
      clearTimeout(fetchListenersTimerID);
    }

    fetchListenersTimerID = setTimeout(() => {
      // In case there is still a request of listeners going on (it
      // takes several RDP round trips right now), make sure we wait
      // on a currently running request
      if (getState().eventListeners.fetchingListeners) {
        dispatch({
          type: _waitService.NAME,
          predicate: action => action.type === "FETCH_EVENT_LISTENERS" && action.status === "done",
          run: dispatch => dispatch(fetchEventListeners())
        });
        return;
      }

      dispatch({
        type: "FETCH_EVENT_LISTENERS",
        status: "begin"
      });

      asPaused(getState(), client, _getEventListeners).then(listeners => {
        dispatch({
          type: "FETCH_EVENT_LISTENERS",
          status: "done",
          listeners: formatListeners(getState(), listeners)
        });
      });
    }, FETCH_EVENT_LISTENERS_DELAY);
  };
}

function formatListeners(state, listeners) {
  return listeners.map(l => {
    return {
      selector: l.node.selector,
      type: l.type,
      sourceId: (0, _selectors.getSourceByURL)(state, l.function.location.url).get("id"),
      line: l.function.location.line
    };
  });
}

async function _getEventListeners(threadClient) {
  const response = await threadClient.eventListeners();

  // Make sure all the listeners are sorted by the event type, since
  // they"re not guaranteed to be clustered together.
  response.listeners.sort((a, b) => a.type > b.type ? 1 : -1);

  // Add all the listeners in the debugger view event linsteners container.
  const fetchedDefinitions = new Map();
  const listeners = [];
  for (const listener of response.listeners) {
    let definitionSite;
    if (fetchedDefinitions.has(listener.function.actor)) {
      definitionSite = fetchedDefinitions.get(listener.function.actor);
    } else if (listener.function.class == "Function") {
      definitionSite = await _getDefinitionSite(threadClient, listener.function);
      if (!definitionSite) {
        // We don"t know where this listener comes from so don"t show it in
        // the UI as breaking on it doesn"t work (bug 942899).
        continue;
      }

      fetchedDefinitions.set(listener.function.actor, definitionSite);
    }
    listener.function.url = definitionSite;
    listeners.push(listener);
  }
  fetchedDefinitions.clear();

  return listeners;
}

async function _getDefinitionSite(threadClient, func) {
  const grip = threadClient.pauseGrip(func);
  let response;

  try {
    response = await grip.getDefinitionSite();
  } catch (e) {
    // Don't make this error fatal, it would break the entire events pane.
    (0, _DevToolsUtils.reportException)("_getDefinitionSite", e);
    return null;
  }

  return response.source.url;
}

/**
 * @memberof actions/event-listeners
 * @static
 * @param {string} eventNames
 */
function updateEventBreakpoints(eventNames) {
  return dispatch => {
    setNamedTimeout("event-breakpoints-update", 0, () => {
      gThreadClient.pauseOnDOMEvents(eventNames, () => {
        // Notify that event breakpoints were added/removed on the server.
        window.emit(EVENTS.EVENT_BREAKPOINTS_UPDATED);

        dispatch({
          type: "UPDATE_EVENT_BREAKPOINTS",
          eventNames: eventNames
        });
      });
    });
  };
}

/***/ }),
/* 1528 */,
/* 1529 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.willNavigate = willNavigate;
exports.navigate = navigate;
exports.connect = connect;
exports.navigated = navigated;

var _editor = __webpack_require__(1358);

var _sourceQueue = __webpack_require__(1795);

var _sourceQueue2 = _interopRequireDefault(_sourceQueue);

var _sources = __webpack_require__(1369);

var _utils = __webpack_require__(1366);

var _sources2 = __webpack_require__(1797);

var _debuggee = __webpack_require__(1533);

var _parser = __webpack_require__(1365);

var _wasm = __webpack_require__(1401);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/**
 * Redux actions for the navigation state
 * @module actions/navigation
 */

/**
 * @memberof actions/navigation
 * @static
 */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 willNavigate(_, event) {
  return async function ({ dispatch, getState, client, sourceMaps }) {
    await sourceMaps.clearSourceMaps();
    (0, _wasm.clearWasmStates)();
    (0, _editor.clearDocuments)();
    (0, _parser.clearSymbols)();
    (0, _parser.clearASTs)();
    (0, _parser.clearScopes)();
    (0, _parser.clearSources)();
    dispatch(navigate(event.url));
  };
}

function navigate(url) {
  _sourceQueue2.default.clear();

  return {
    type: "NAVIGATE",
    url
  };
}

function connect(url, canRewind) {
  return async function ({ dispatch }) {
    await dispatch((0, _debuggee.updateWorkers)());
    dispatch({ type: "CONNECT", url, canRewind });
  };
}

/**
 * @memberof actions/navigation
 * @static
 */
function navigated() {
  return async function ({ dispatch, getState, client }) {
    await (0, _utils.waitForMs)(100);
    if ((0, _sources.getSources)(getState()).size == 0) {
      const sources = await client.fetchSources();
      dispatch((0, _sources2.newSources)(sources));
    }
  };
}

/***/ }),
/* 1530 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.doSearch = doSearch;
exports.setFileSearchQuery = setFileSearchQuery;
exports.toggleFileSearchModifier = toggleFileSearchModifier;
exports.updateSearchResults = updateSearchResults;
exports.searchContents = searchContents;
exports.traverseResults = traverseResults;
exports.closeFileSearch = closeFileSearch;

var _editor = __webpack_require__(1358);

var _search = __webpack_require__(1395);

var _selectors = __webpack_require__(1352);

var _ui = __webpack_require__(1385);

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 doSearch(query, editor) {
  return ({ getState, dispatch }) => {
    const selectedSource = (0, _selectors.getSelectedSource)(getState());
    if (!selectedSource || !selectedSource.get("text")) {
      return;
    }

    dispatch(setFileSearchQuery(query));
    dispatch(searchContents(query, editor));
  };
}

function setFileSearchQuery(query) {
  return {
    type: "UPDATE_FILE_SEARCH_QUERY",
    query
  };
}

function toggleFileSearchModifier(modifier) {
  return { type: "TOGGLE_FILE_SEARCH_MODIFIER", modifier };
}

function updateSearchResults(characterIndex, line, matches) {
  const matchIndex = matches.findIndex(elm => elm.line === line && elm.ch === characterIndex);

  return {
    type: "UPDATE_SEARCH_RESULTS",
    results: {
      matches,
      matchIndex,
      count: matches.length,
      index: characterIndex
    }
  };
}

function searchContents(query, editor) {
  return async ({ getState, dispatch }) => {
    const modifiers = (0, _selectors.getFileSearchModifiers)(getState());
    const selectedSource = (0, _selectors.getSelectedSource)(getState());

    if (!query || !editor || !selectedSource || !selectedSource.get("text") || !modifiers) {
      return;
    }

    const ctx = { ed: editor, cm: editor.codeMirror };
    const _modifiers = modifiers.toJS();
    const matches = await (0, _search.getMatches)(query, selectedSource.get("text"), _modifiers);

    const res = (0, _editor.find)(ctx, query, true, _modifiers);
    if (!res) {
      return;
    }

    const { ch, line } = res;

    dispatch(updateSearchResults(ch, line, matches));
  };
}

function traverseResults(rev, editor) {
  return async ({ getState, dispatch }) => {
    if (!editor) {
      return;
    }

    const ctx = { ed: editor, cm: editor.codeMirror };

    const query = (0, _selectors.getFileSearchQuery)(getState());
    const modifiers = (0, _selectors.getFileSearchModifiers)(getState());
    const { matches } = (0, _selectors.getFileSearchResults)(getState());

    if (query === "") {
      dispatch((0, _ui.setActiveSearch)("file"));
    }

    if (modifiers) {
      const matchedLocations = matches || [];
      const results = rev ? (0, _editor.findPrev)(ctx, query, true, modifiers.toJS()) : (0, _editor.findNext)(ctx, query, true, modifiers.toJS());

      if (!results) {
        return;
      }
      const { ch, line } = results;
      dispatch(updateSearchResults(ch, line, matchedLocations));
    }
  };
}

function closeFileSearch(editor) {
  return ({ getState, dispatch }) => {
    const modifiers = (0, _selectors.getFileSearchModifiers)(getState());
    const query = (0, _selectors.getFileSearchQuery)(getState());

    if (editor && modifiers) {
      const ctx = { ed: editor, cm: editor.codeMirror };
      (0, _editor.removeOverlay)(ctx, query, modifiers.toJS());
    }

    dispatch(setFileSearchQuery(""));
    dispatch((0, _ui.closeActiveSearch)());
    dispatch((0, _ui.clearHighlightLineRange)());
  };
}

/***/ }),
/* 1531 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.recordCoverage = recordCoverage;
function recordCoverage() {
  return async function ({ dispatch, getState, client }) {
    const { coverage } = await client.recordCoverage();

    return dispatch({
      type: "RECORD_COVERAGE",
      value: { coverage }
    });
  };
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/***/ }),
/* 1532 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.setExpandedState = setExpandedState;
function setExpandedState(expanded) {
  return ({ dispatch, getState }) => {
    dispatch({
      type: "SET_EXPANDED_STATE",
      expanded
    });
  };
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/***/ }),
/* 1533 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.updateWorkers = updateWorkers;
function updateWorkers() {
  return async function ({ dispatch, client }) {
    const { workers } = await client.fetchWorkers();
    dispatch({ type: "SET_WORKERS", workers });
  };
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/***/ }),
/* 1534 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.openLink = openLink;
exports.openWorkerToolbox = openWorkerToolbox;
exports.evaluateInConsole = evaluateInConsole;
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 { isDevelopment } = __webpack_require__(1355);
const { getSelectedFrameId } = __webpack_require__(1352);

/**
 * @memberof actions/toolbox
 * @static
 */
function openLink(url) {
  return async function ({ openLink: openLinkCommand }) {
    if (isDevelopment()) {
      const win = window.open(url, "_blank");
      win.focus();
    } else {
      openLinkCommand(url);
    }
  };
}

function openWorkerToolbox(worker) {
  return async function ({
    getState,
    openWorkerToolbox: openWorkerToolboxCommand
  }) {
    if (isDevelopment()) {
      alert(worker.url);
    } else {
      openWorkerToolboxCommand(worker);
    }
  };
}

function evaluateInConsole(inputString) {
  return async ({ client, getState }) => {
    const frameId = getSelectedFrameId(getState());
    client.evaluate(`console.log("${inputString}"); console.log(${inputString})`, { frameId });
  };
}

/***/ }),
/* 1535 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.ShortcutsModal = undefined;

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _Modal = __webpack_require__(1403);

var _Modal2 = _interopRequireDefault(_Modal);

var _classnames = __webpack_require__(175);

var _classnames2 = _interopRequireDefault(_classnames);

var _text = __webpack_require__(1387);

__webpack_require__(1304);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

class ShortcutsModal extends _react.Component {
  renderPrettyCombos(combo) {
    return combo.split(" ").map(c => _react2.default.createElement(
      "span",
      { key: c, className: "keystroke" },
      c
    )).reduce((prev, curr) => [prev, " + ", curr]);
  }

  renderShorcutItem(title, combo) {
    return _react2.default.createElement(
      "li",
      null,
      _react2.default.createElement(
        "span",
        null,
        title
      ),
      _react2.default.createElement(
        "span",
        null,
        this.renderPrettyCombos(combo)
      )
    );
  }

  renderEditorShortcuts() {
    return _react2.default.createElement(
      "ul",
      { className: "shortcuts-list" },
      this.renderShorcutItem(L10N.getStr("shortcuts.toggleBreakpoint"), (0, _text.formatKeyShortcut)(L10N.getStr("toggleBreakpoint.key"))),
      this.renderShorcutItem(L10N.getStr("shortcuts.toggleCondPanel"), (0, _text.formatKeyShortcut)(L10N.getStr("toggleCondPanel.key")))
    );
  }

  renderSteppingShortcuts() {
    return _react2.default.createElement(
      "ul",
      { className: "shortcuts-list" },
      this.renderShorcutItem(L10N.getStr("shortcuts.pauseOrResume"), "F8"),
      this.renderShorcutItem(L10N.getStr("shortcuts.stepOver"), "F10"),
      this.renderShorcutItem(L10N.getStr("shortcuts.stepIn"), "F11"),
      this.renderShorcutItem(L10N.getStr("shortcuts.stepOut"), (0, _text.formatKeyShortcut)(L10N.getStr("stepOut.key")))
    );
  }

  renderSearchShortcuts() {
    return _react2.default.createElement(
      "ul",
      { className: "shortcuts-list" },
      this.renderShorcutItem(L10N.getStr("shortcuts.fileSearch"), (0, _text.formatKeyShortcut)(L10N.getStr("sources.search.key2"))),
      this.renderShorcutItem(L10N.getStr("shortcuts.searchAgain"), (0, _text.formatKeyShortcut)(L10N.getStr("sourceSearch.search.again.key2"))),
      this.renderShorcutItem(L10N.getStr("shortcuts.projectSearch"), (0, _text.formatKeyShortcut)(L10N.getStr("projectTextSearch.key"))),
      this.renderShorcutItem(L10N.getStr("shortcuts.functionSearch"), (0, _text.formatKeyShortcut)(L10N.getStr("functionSearch.key"))),
      this.renderShorcutItem(L10N.getStr("shortcuts.gotoLine"), (0, _text.formatKeyShortcut)(L10N.getStr("gotoLineModal.key2")))
    );
  }

  renderShortcutsContent() {
    return _react2.default.createElement(
      "div",
      {
        className: (0, _classnames2.default)("shortcuts-content", this.props.additionalClass)
      },
      _react2.default.createElement(
        "div",
        { className: "shortcuts-section" },
        _react2.default.createElement(
          "h2",
          null,
          L10N.getStr("shortcuts.header.editor")
        ),
        this.renderEditorShortcuts()
      ),
      _react2.default.createElement(
        "div",
        { className: "shortcuts-section" },
        _react2.default.createElement(
          "h2",
          null,
          L10N.getStr("shortcuts.header.stepping")
        ),
        this.renderSteppingShortcuts()
      ),
      _react2.default.createElement(
        "div",
        { className: "shortcuts-section" },
        _react2.default.createElement(
          "h2",
          null,
          L10N.getStr("shortcuts.header.search")
        ),
        this.renderSearchShortcuts()
      )
    );
  }

  render() {
    const { enabled } = this.props;

    if (!enabled) {
      return null;
    }

    return _react2.default.createElement(
      _Modal2.default,
      {
        "in": enabled,
        additionalClass: "shortcuts-modal",
        handleClose: this.props.handleClose
      },
      this.renderShortcutsContent()
    );
  }
}
exports.ShortcutsModal = ShortcutsModal; /* This Source Code Form is subject to the terms of the Mozilla Public
                                          * License, v. 2.0. If a copy of the MPL was not distributed with this
                                          * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/***/ }),
/* 1536 */
/***/ (function(module, exports, __webpack_require__) {

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 React = __webpack_require__(0);
const ReactDOM = __webpack_require__(4);
const Draggable = React.createFactory(__webpack_require__(1537));
const { Component } = React;
const PropTypes = __webpack_require__(20);
const dom = __webpack_require__(1758);

__webpack_require__(1309);

/**
 * 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.any,
      // Optional initial width of controlled panel.
      initialWidth: PropTypes.number,
      // Optional initial height of controlled panel.
      initialHeight: PropTypes.number,
      // Left/top panel
      startPanel: PropTypes.any,
      // Left/top panel collapse state.
      startPanelCollapsed: PropTypes.bool,
      // Min panel size.
      minSize: PropTypes.any,
      // Max panel size.
      maxSize: PropTypes.any,
      // 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,
      // Optional style properties passed into the splitbox
      style: PropTypes.object,
      // Optional callback when splitbox resize stops
      onResizeEnd: PropTypes.func
    };
  }

  static get defaultProps() {
    return {
      splitterSize: 5,
      vert: true,
      endPanelControl: false,
      endPanelCollapsed: false,
      startPanelCollapsed: false
    };
  }

  constructor(props) {
    super(props);

    this.state = {
      vert: props.vert,
      // We use integers for these properties
      width: parseInt(props.initialWidth || props.initialSize, 10),
      height: parseInt(props.initialHeight || props.initialSize, 10)
    };

    this.onStartMove = this.onStartMove.bind(this);
    this.onStopMove = this.onStopMove.bind(this);
    this.onMove = this.onMove.bind(this);
    this.preparePanelStyles = this.preparePanelStyles.bind(this);
  }

  componentWillReceiveProps(nextProps) {
    if (this.props.vert !== nextProps.vert) {
      this.setState({ vert: nextProps.vert });
    }
  }

  // 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 splitBox = ReactDOM.findDOMNode(this);
    const doc = splitBox.ownerDocument;
    let defaultCursor = doc.documentElement.style.cursor;
    doc.documentElement.style.cursor = this.state.vert ? "ew-resize" : "ns-resize";

    splitBox.classList.add("dragging");

    this.setState({
      defaultCursor: defaultCursor
    });
  }

  onStopMove() {
    const splitBox = ReactDOM.findDOMNode(this);
    const doc = splitBox.ownerDocument;
    doc.documentElement.style.cursor = this.state.defaultCursor;

    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({ movementX, movementY }) {
    const node = ReactDOM.findDOMNode(this);
    const doc = node.ownerDocument;

    if (this.props.endPanelControl) {
      // For the end panel we need to increase the width/height when the
      // movement is towards the left/top.
      movementX = -movementX;
      movementY = -movementY;
    }

    if (this.state.vert) {
      const isRtl = doc.dir === "rtl";
      if (isRtl) {
        // In RTL we need to reverse the movement again -- but only for vertical
        // splitters
        movementX = -movementX;
      }

      this.setState((state, props) => ({
        width: state.width + movementX
      }));
    } else {
      this.setState((state, props) => ({
        height: state.height + movementY
      }));
    }
  }

  // Rendering
  preparePanelStyles() {
    const vert = this.state.vert;
    const {
      minSize,
      maxSize,
      startPanelCollapsed,
      endPanelControl,
      endPanelCollapsed
    } = this.props;
    let leftPanelStyle, rightPanelStyle;

    // Set proper size for panels depending on the current state.
    if (vert) {
      let startWidth = endPanelControl ? null : this.state.width,
          endWidth = endPanelControl ? this.state.width : null;

      leftPanelStyle = {
        maxWidth: endPanelControl ? null : maxSize,
        minWidth: endPanelControl ? null : minSize,
        width: startPanelCollapsed ? 0 : startWidth
      };
      rightPanelStyle = {
        maxWidth: endPanelControl ? maxSize : null,
        minWidth: endPanelControl ? minSize : null,
        width: endPanelCollapsed ? 0 : endWidth
      };
    } else {
      let startHeight = endPanelControl ? null : this.state.height,
          endHeight = endPanelControl ? this.state.height : null;

      leftPanelStyle = {
        maxHeight: endPanelControl ? null : maxSize,
        minHeight: endPanelControl ? null : minSize,
        height: endPanelCollapsed ? maxSize : startHeight
      };
      rightPanelStyle = {
        maxHeight: endPanelControl ? maxSize : null,
        minHeight: endPanelControl ? minSize : null,
        height: startPanelCollapsed ? maxSize : endHeight
      };
    }

    return { leftPanelStyle, rightPanelStyle };
  }

  render() {
    const vert = this.state.vert;
    const {
      startPanelCollapsed,
      startPanel,
      endPanel,
      endPanelControl,
      splitterSize,
      endPanelCollapsed
    } = this.props;

    let style = Object.assign({}, 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(" "));
    }

    const { leftPanelStyle, rightPanelStyle } = this.preparePanelStyles();

    // Calculate splitter size
    let splitterStyle = {
      flex: `0 0 ${splitterSize}px`
    };

    return dom.div({
      className: classNames.join(" "),
      style: style
    }, !startPanelCollapsed ? dom.div({
      className: endPanelControl ? "uncontrolled" : "controlled",
      style: leftPanelStyle
    }, startPanel) : null, Draggable({
      className: "splitter",
      style: splitterStyle,
      onStart: this.onStartMove,
      onStop: this.onStopMove,
      onMove: this.onMove
    }), !endPanelCollapsed ? dom.div({
      className: endPanelControl ? "controlled" : "uncontrolled",
      style: rightPanelStyle
    }, endPanel) : null);
  }
}

module.exports = SplitBox;

/***/ }),
/* 1537 */
/***/ (function(module, exports, __webpack_require__) {

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 React = __webpack_require__(0);
const ReactDOM = __webpack_require__(4);
const { Component } = React;
const PropTypes = __webpack_require__(20);
const dom = __webpack_require__(1758);

class Draggable extends Component {
  static get propTypes() {
    return {
      onMove: PropTypes.func.isRequired,
      onStart: PropTypes.func,
      onStop: PropTypes.func,
      style: PropTypes.object,
      className: PropTypes.string
    };
  }

  constructor(props) {
    super(props);
    this.startDragging = this.startDragging.bind(this);
    this.onMove = this.onMove.bind(this);
    this.onUp = this.onUp.bind(this);
  }

  startDragging(ev) {
    ev.preventDefault();
    const doc = ReactDOM.findDOMNode(this).ownerDocument;
    doc.addEventListener("mousemove", this.onMove);
    doc.addEventListener("mouseup", this.onUp);
    this.props.onStart && this.props.onStart();
  }

  onMove(ev) {
    ev.preventDefault();
    // We pass the whole event because we don't know which properties
    // the callee needs.
    this.props.onMove(ev);
  }

  onUp(ev) {
    ev.preventDefault();
    const doc = ReactDOM.findDOMNode(this).ownerDocument;
    doc.removeEventListener("mousemove", this.onMove);
    doc.removeEventListener("mouseup", this.onUp);
    this.props.onStop && this.props.onStop();
  }

  render() {
    return dom.div({
      style: this.props.style,
      className: this.props.className,
      onMouseDown: this.startDragging
    });
  }
}

module.exports = Draggable;

/***/ }),
/* 1538 */,
/* 1539 */,
/* 1540 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var _svgInlineReact = __webpack_require__(1763);

var _svgInlineReact2 = _interopRequireDefault(_svgInlineReact);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

const React = __webpack_require__(0);

const { isDevelopment } = __webpack_require__(1355);

const svg = {
  "angle-brackets": __webpack_require__(347),
  angular: __webpack_require__(247),
  arrow: __webpack_require__(348),
  backbone: __webpack_require__(997),
  blackBox: __webpack_require__(349),
  breakpoint: __webpack_require__(350),
  "column-breakpoint": __webpack_require__(998),
  "case-match": __webpack_require__(351),
  choo: __webpack_require__(1290),
  close: __webpack_require__(352),
  coffeescript: __webpack_require__(2250),
  dojo: __webpack_require__(806),
  domain: __webpack_require__(353),
  file: __webpack_require__(354),
  folder: __webpack_require__(355),
  globe: __webpack_require__(356),
  javascript: __webpack_require__(2251),
  jquery: __webpack_require__(999),
  underscore: __webpack_require__(1117),
  lodash: __webpack_require__(1118),
  ember: __webpack_require__(1119),
  vuejs: __webpack_require__(1174),
  "magnifying-glass": __webpack_require__(357),
  "arrow-up": __webpack_require__(919),
  "arrow-down": __webpack_require__(920),
  pause: __webpack_require__(358),
  "pause-exceptions": __webpack_require__(359),
  plus: __webpack_require__(360),
  preact: __webpack_require__(1233),
  aframe: __webpack_require__(1648),
  prettyPrint: __webpack_require__(361),
  react: __webpack_require__(1000),
  "regex-match": __webpack_require__(362),
  redux: __webpack_require__(256),
  immutable: __webpack_require__(258),
  resume: __webpack_require__(363),
  settings: __webpack_require__(364),
  stepIn: __webpack_require__(365),
  stepOut: __webpack_require__(366),
  stepOver: __webpack_require__(367),
  subSettings: __webpack_require__(368),
  toggleBreakpoints: __webpack_require__(369),
  togglePanes: __webpack_require__(370),
  typescript: __webpack_require__(2252),
  "whole-word-match": __webpack_require__(371),
  worker: __webpack_require__(372),
  "sad-face": __webpack_require__(1347),
  refresh: __webpack_require__(1348),
  webpack: __webpack_require__(1001),
  node: __webpack_require__(1002),
  express: __webpack_require__(1003),
  pug: __webpack_require__(1004),
  extjs: __webpack_require__(1043),
  mobx: __webpack_require__(1733),
  marko: __webpack_require__(1649),
  nextjs: __webpack_require__(1650),
  showSources: __webpack_require__(1044),
  showOutline: __webpack_require__(1045),
  nuxtjs: __webpack_require__(1651),
  rxjs: __webpack_require__(1808)
};

function Svg({ name, className, onClick, "aria-label": ariaLabel }) {
  if (!svg[name]) {
    const error = `Unknown SVG: ${name}`;
    if (isDevelopment()) {
      throw new Error(error);
    }

    console.warn(error);
    return null;
  }

  className = `${name} ${className || ""}`;
  if (name === "subSettings") {
    className = "";
  }

  const props = {
    className,
    onClick,
    ["aria-label"]: ariaLabel,
    src: svg[name]
  };

  return React.createElement(_svgInlineReact2.default, props);
}

Svg.displayName = "Svg";

module.exports = Svg;

/***/ }),
/* 1541 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _reactDomFactories = __webpack_require__(1758);

var _reactDomFactories2 = _interopRequireDefault(_reactDomFactories);

var _propTypes = __webpack_require__(20);

var _propTypes2 = _interopRequireDefault(_propTypes);

var _svgInlineReact = __webpack_require__(1763);

var _svgInlineReact2 = _interopRequireDefault(_svgInlineReact);

var _arrow = __webpack_require__(2247);

var _arrow2 = _interopRequireDefault(_arrow);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

const { Component, createFactory, createElement } = _react2.default; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                      * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                                      * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

__webpack_require__(2248);

const AUTO_EXPAND_DEPTH = 0; // depth

/**
 * 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: _propTypes2.default.bool
    };
  }

  shouldComponentUpdate(nextProps, nextState) {
    return this.props.expanded !== nextProps.expanded;
  }

  render() {
    const {
      expanded
    } = this.props;

    const classNames = ["arrow"];
    if (expanded) {
      classNames.push("expanded");
    }
    return createElement(_svgInlineReact2.default, {
      className: classNames.join(" "),
      src: _arrow2.default
    });
  }
}

const treeIndent = _reactDomFactories2.default.span({ className: "tree-indent" }, "\u200B");

class TreeNode extends Component {
  static get propTypes() {
    return {
      id: _propTypes2.default.any.isRequired,
      index: _propTypes2.default.number.isRequired,
      depth: _propTypes2.default.number.isRequired,
      focused: _propTypes2.default.bool.isRequired,
      expanded: _propTypes2.default.bool.isRequired,
      item: _propTypes2.default.any.isRequired,
      isExpandable: _propTypes2.default.bool.isRequired,
      onClick: _propTypes2.default.func,
      renderItem: _propTypes2.default.func.isRequired
    };
  }

  shouldComponentUpdate(nextProps) {
    return this.props.item !== nextProps.item || this.props.focused !== nextProps.focused || this.props.expanded !== nextProps.expanded;
  }

  render() {
    const {
      depth,
      id,
      item,
      focused,
      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 }).fill(treeIndent);
    let items = indents.concat(renderItem(item, depth, focused, arrow, expanded));

    return _reactDomFactories2.default.div({
      id,
      className: "tree-node" + (focused ? " focused" : ""),
      onClick: this.props.onClick,
      role: "treeitem",
      "aria-level": depth,
      "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
 * @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;
    });
  };
}

/**
 * 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 performance and memory panels 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({
 *           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,
 *             },
 *               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: _propTypes2.default.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: _propTypes2.default.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: _propTypes2.default.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: _propTypes2.default.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: _propTypes2.default.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: _propTypes2.default.func.isRequired,

      // Optional props

      // The currently focused item, if any such item exists.
      focused: _propTypes2.default.any,

      // Handle when a new item is focused.
      onFocus: _propTypes2.default.func,

      // The depth to which we should automatically expand new items.
      autoExpandDepth: _propTypes2.default.number,
      // Should auto expand all new items or just the new items under the first
      // root item.
      autoExpandAll: _propTypes2.default.bool,

      // 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: _propTypes2.default.string,
      // Accessibility label for a tree widget.
      label: _propTypes2.default.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: _propTypes2.default.func,
      onCollapse: _propTypes2.default.func,
      isExpandable: _propTypes2.default.func,
      // Additional classes to add to the root element.
      className: _propTypes2.default.string,
      // style object to be applied to the root element.
      style: _propTypes2.default.object
    };
  }

  static get defaultProps() {
    return {
      autoExpandDepth: AUTO_EXPAND_DEPTH,
      autoExpandAll: true
    };
  }

  constructor(props) {
    super(props);

    this.state = {
      seen: new Set()
    };

    this._onExpand = oncePerAnimationFrame(this._onExpand).bind(this);
    this._onCollapse = oncePerAnimationFrame(this._onCollapse).bind(this);
    this._focusPrevNode = oncePerAnimationFrame(this._focusPrevNode).bind(this);
    this._focusNextNode = oncePerAnimationFrame(this._focusNextNode).bind(this);
    this._focusParentNode = oncePerAnimationFrame(this._focusParentNode).bind(this);

    this._autoExpand = this._autoExpand.bind(this);
    this._preventArrowKeyScrolling = this._preventArrowKeyScrolling.bind(this);
    this._dfs = this._dfs.bind(this);
    this._dfsFromRoots = this._dfsFromRoots.bind(this);
    this._focus = this._focus.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);
    }
  }

  componentWillReceiveProps(nextProps) {
    this._autoExpand();
  }

  componentDidUpdate(prevProps, prevState) {
    if (prevProps.focused !== this.props.focused) {
      this._scrollNodeIntoView(this.props.focused);
    }
  }

  _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;
    if (this.props.autoExpandAll) {
      for (let i = 0; i < length; i++) {
        autoExpand(roots[i], 0);
      }
    } else if (length != 0) {
      autoExpand(roots[0], 0);
    }
  }

  _preventArrowKeyScrolling(e) {
    switch (e.key) {
      case "ArrowUp":
      case "ArrowDown":
      case "ArrowLeft":
      case "ArrowRight":
        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) {
    this._scrollNodeIntoView(item, options);
    if (this.props.onFocus) {
      this.props.onFocus(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;
      const element = document.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 isVisible = !scrolledParent || top >= 0 && bottom <= scrolledParent.clientHeight;

        if (!isVisible) {
          let scrollToTop = !options.alignTo && top < 0 || options.alignTo === "top";
          element.scrollIntoView(scrollToTop);
        }
      }
    }
  }

  /**
   * Sets the state to have no focused item.
   */
  _onBlur() {
    this._focus(undefined);
  }

  /**
   * Handles key down events in the tree's container.
   *
   * @param {Event} e
   */
  _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();
        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();
        }
    }
  }

  /**
   * 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;
    }

    const traversal = this._dfsFromRoots();
    const length = traversal.length;
    let parentIndex = 0;
    for (; parentIndex < length; parentIndex++) {
      if (traversal[parentIndex].item === parent) {
        break;
      }
    }

    this._focus(parent, { alignTo: "top" });
  }

  _nodeIsExpandable(item) {
    return this.props.isExpandable ? this.props.isExpandable(item) : !!this.props.getChildren(item).length;
  }

  render() {
    const traversal = this._dfsFromRoots();
    const {
      focused
    } = this.props;

    const nodes = traversal.map((v, i) => {
      const { item, depth } = traversal[i];
      const key = this.props.getKey(item, i);
      return TreeNodeFactory({
        key,
        id: key,
        index: i,
        item,
        depth,
        renderItem: this.props.renderItem,
        focused: focused === item,
        expanded: this.props.isExpanded(item),
        isExpandable: this._nodeIsExpandable(item),
        onExpand: this._onExpand,
        onCollapse: this._onCollapse,
        onClick: e => {
          this._focus(item);
          if (this.props.isExpanded(item)) {
            this.props.onCollapse(item);
          } else {
            this.props.onExpand(item, e.altKey);
          }
        }
      });
    });

    const style = Object.assign({}, this.props.style || {}, {
      padding: 0,
      margin: 0
    });

    return _reactDomFactories2.default.div({
      className: `tree ${this.props.className ? this.props.className : ""}`,
      ref: el => {
        this.treeRef = el;
      },
      role: "tree",
      tabIndex: "0",
      onKeyDown: this._onKeyDown,
      onKeyPress: this._preventArrowKeyScrolling,
      onKeyUp: this._preventArrowKeyScrolling,
      onFocus: ({ nativeEvent }) => {
        if (focused || !nativeEvent || !this.treeRef) {
          return;
        }

        let { 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 && !this.treeRef.contains(explicitOriginalTarget)) {
          this._focus(traversal[0].item);
        }
      },
      onBlur: this._onBlur,
      onClick: () => {
        // Focus should always remain on the tree container itself.
        this.treeRef.focus();
      },
      "aria-label": this.props.label,
      "aria-labelledby": this.props.labelledby,
      "aria-activedescendant": focused && this.props.getKey(focused),
      style
    }, nodes);
  }
}

exports.default = Tree;

/***/ }),
/* 1542 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.formatTree = formatTree;
function formatTree(tree, depth = 0, str = "") {
  const whitespace = new Array(depth * 2).join(" ");

  if (Array.isArray(tree.contents)) {
    str += `${whitespace} - ${tree.name} path=${tree.path} \n`;
    tree.contents.forEach(t => {
      str = formatTree(t, depth + 1, str);
    });
  } else {
    const id = tree.contents.get("id");
    str += `${whitespace} - ${tree.name} path=${tree.path} source_id=${id} \n`;
  }

  return str;
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/***/ }),
/* 1543 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getDomain = getDomain;
exports.findNodeInContents = findNodeInContents;
exports.createTreeNodeMatcher = createTreeNodeMatcher;

var _url = __webpack_require__(334);

var _utils = __webpack_require__(1371);

/*
 * Gets domain from url (without www prefix)
 */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 getDomain(url) {
  // TODO: define how files should be ordered on the browser debugger
  if (!url) {
    return null;
  }
  const { host } = (0, _url.parse)(url);
  if (!host) {
    return null;
  }
  return host.startsWith("www.") ? host.substr("www.".length) : host;
}

/*
 * Checks if node name matches debugger host/domain.
 */
function isExactDomainMatch(part, debuggeeHost) {
  return part.startsWith("www.") ? part.substr("www.".length) === debuggeeHost : part === debuggeeHost;
}

/*
 * Function to assist with node search for a defined sorted order, see e.g.
 * `createTreeNodeMatcher`. Returns negative number if the node
 * stands earlier in sorting order, positive number if the node stands later
 * in sorting order, or zero if the node is found.
 */


/*
 * Performs a binary search to insert a node into contents. Returns positive
 * number, index of the found child, or negative number, which can be used
 * to calculate a position where a new node can be inserted (`-index - 1`).
 * The matcher is a function that returns result of comparision of a node with
 * lookup value.
 */
function findNodeInContents(tree, matcher) {
  const { contents } = tree;
  if (contents.length === 0) {
    return { found: false, index: 0 };
  }
  let left = 0;
  let right = contents.length - 1;
  while (left < right) {
    const middle = Math.floor((left + right) / 2);
    if (matcher(contents[middle]) < 0) {
      left = middle + 1;
    } else {
      right = middle;
    }
  }
  const result = matcher(contents[left]);
  if (result === 0) {
    return { found: true, index: left };
  }
  return { found: false, index: result > 0 ? left : left + 1 };
}

const IndexName = "(index)";

function createTreeNodeMatcherWithIndex() {
  return node => node.name === IndexName ? 0 : 1;
}

function createTreeNodeMatcherWithDebuggeeHost(debuggeeHost) {
  return node => {
    if (node.name === IndexName) {
      return -1;
    }
    return isExactDomainMatch(node.name, debuggeeHost) ? 0 : 1;
  };
}

function createTreeNodeMatcherWithNameAndOther(part, isDir, debuggeeHost) {
  return node => {
    if (node.name === IndexName) {
      return -1;
    }
    if (debuggeeHost && isExactDomainMatch(node.name, debuggeeHost)) {
      return -1;
    }
    const nodeIsDir = (0, _utils.nodeHasChildren)(node);
    if (nodeIsDir && !isDir) {
      return -1;
    } else if (!nodeIsDir && isDir) {
      return 1;
    }
    return node.name.localeCompare(part);
  };
}

/*
 * Creates a matcher for findNodeInContents.
 * The sorting order of nodes during comparison is:
 * - "(index)" node
 * - root node with the debuggee host/domain
 * - hosts/directories (not files) sorted by name
 * - files sorted by name
 */
function createTreeNodeMatcher(part, isDir, debuggeeHost) {
  if (part === IndexName) {
    // Specialied matcher, when we are looking for "(index)" position.
    return createTreeNodeMatcherWithIndex();
  }
  if (debuggeeHost && isExactDomainMatch(part, debuggeeHost)) {
    // Specialied matcher, when we are looking for domain position.
    return createTreeNodeMatcherWithDebuggeeHost(debuggeeHost);
  }
  // Rest of the cases, without mentioned above.
  return createTreeNodeMatcherWithNameAndOther(part, isDir, debuggeeHost);
}

/***/ }),
/* 1544 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

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; }; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                                                                                                                                                                                   * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                                                                                                                                                                                                                                   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

exports.sortEntireTree = sortEntireTree;
exports.sortTree = sortTree;

var _utils = __webpack_require__(1371);

/**
 * Look at the nodes in the source tree, and determine the index of where to
 * insert a new node. The ordering is index -> folder -> file.
 * @memberof utils/sources-tree
 * @static
 */
function sortEntireTree(tree, debuggeeUrl = "") {
  if ((0, _utils.nodeHasChildren)(tree)) {
    const contents = sortTree(tree, debuggeeUrl).map(subtree => sortEntireTree(subtree));
    return _extends({}, tree, { contents });
  }
  return tree;
}

/**
 * Look at the nodes in the source tree, and determine the index of where to
 * insert a new node. The ordering is index -> folder -> file.
 * @memberof utils/sources-tree
 * @static
 */
function sortTree(tree, debuggeeUrl = "") {
  return tree.contents.sort((previousNode, currentNode) => {
    const currentNodeIsDir = (0, _utils.nodeHasChildren)(currentNode);
    const previousNodeIsDir = (0, _utils.nodeHasChildren)(previousNode);
    if (currentNode.name === "(index)") {
      return 1;
    } else if (previousNode.name === "(index)") {
      return -1;
    } else if ((0, _utils.isExactUrlMatch)(currentNode.name, debuggeeUrl)) {
      return 1;
    } else if ((0, _utils.isExactUrlMatch)(previousNode.name, debuggeeUrl)) {
      return -1;
      // If neither is the case, continue to compare alphabetically
    } else if (previousNodeIsDir && !currentNodeIsDir) {
      return -1;
    } else if (!previousNodeIsDir && currentNodeIsDir) {
      return 1;
    }
    return previousNode.name.localeCompare(currentNode.name);
  });
}

/***/ }),
/* 1545 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getDirectories = getDirectories;

var _utils = __webpack_require__(1371);

var _getURL = __webpack_require__(1405);

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 findSource(sourceTree, sourceUrl) {
  let returnTarget = null;
  function _traverse(subtree) {
    if ((0, _utils.nodeHasChildren)(subtree)) {
      for (const child of subtree.contents) {
        _traverse(child);
      }
    } else if (!returnTarget) {
      if (subtree.path.replace(/http(s)?:\//, "") == sourceUrl) {
        returnTarget = subtree;
        return;
      }
    }
  }

  sourceTree.contents.forEach(_traverse);

  if (!returnTarget) {
    return sourceTree;
  }

  return returnTarget;
}

function getDirectories(sourceUrl, sourceTree) {
  const url = (0, _getURL.getURL)(sourceUrl);
  const fullUrl = `/${url.group}${url.path}`;
  const parentMap = (0, _utils.createParentMap)(sourceTree);
  const source = findSource(sourceTree, fullUrl);
  if (!source) {
    return [];
  }

  let node = source;
  const directories = [];
  directories.push(source);
  while (true) {
    node = parentMap.get(node);
    if (!node) {
      return directories;
    }
    directories.push(node);
  }
}

/***/ }),
/* 1546 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.createTree = createTree;

var _addToTree = __webpack_require__(1443);

var _collapseTree = __webpack_require__(1444);

var _utils = __webpack_require__(1371);

function createTree({ sources, debuggeeUrl, projectRoot }) {
  const uncollapsedTree = (0, _utils.createNode)("root", "", []);
  for (const source of sources.valueSeq()) {
    (0, _addToTree.addToTree)(uncollapsedTree, source, debuggeeUrl, projectRoot);
  }

  const sourceTree = (0, _collapseTree.collapseTree)(uncollapsedTree);

  return {
    uncollapsedTree,
    sourceTree,
    parentMap: (0, _utils.createParentMap)(sourceTree),
    focusedItem: null
  };
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/***/ }),
/* 1547 */,
/* 1548 */,
/* 1549 */,
/* 1550 */,
/* 1551 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _redux = __webpack_require__(3);

var _reactRedux = __webpack_require__(1189);

var _text = __webpack_require__(1387);

var _actions = __webpack_require__(1354);

var _actions2 = _interopRequireDefault(_actions);

var _selectors = __webpack_require__(1352);

var _prefs = __webpack_require__(226);

__webpack_require__(1318);

var _classnames = __webpack_require__(175);

var _classnames2 = _interopRequireDefault(_classnames);

var _Outline = __webpack_require__(1552);

var _Outline2 = _interopRequireDefault(_Outline);

var _SourcesTree = __webpack_require__(1553);

var _SourcesTree2 = _interopRequireDefault(_SourcesTree);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

class PrimaryPanes extends _react.Component {

  constructor(props) {
    super(props);

    this.renderShortcut = this.renderShortcut.bind(this);
    this.showPane = this.showPane.bind(this);
    this.renderTabs = this.renderTabs.bind(this);
  }

  showPane(selectedPane) {
    this.props.setPrimaryPaneTab(selectedPane);
  }

  renderOutlineTabs() {
    if (!_prefs.features.outline) {
      return;
    }

    const sources = (0, _text.formatKeyShortcut)(L10N.getStr("sources.header"));

    const outline = (0, _text.formatKeyShortcut)(L10N.getStr("outline.header"));

    return [_react2.default.createElement(
      "div",
      {
        className: (0, _classnames2.default)("tab sources-tab", {
          active: this.props.selectedTab === "sources"
        }),
        onClick: () => this.showPane("sources"),
        key: "sources-tab"
      },
      sources
    ), _react2.default.createElement(
      "div",
      {
        className: (0, _classnames2.default)("tab outline-tab", {
          active: this.props.selectedTab === "outline"
        }),
        onClick: () => this.showPane("outline"),
        key: "outline-tab"
      },
      outline
    )];
  }

  renderTabs() {
    return _react2.default.createElement(
      "div",
      { className: "source-outline-tabs" },
      this.renderOutlineTabs()
    );
  }

  renderShortcut() {
    if (this.props.horizontal) {
      const onClick = () => {
        if (this.props.sourceSearchOn) {
          return this.props.closeActiveSearch();
        }
        this.props.setActiveSearch("source");
      };
      return _react2.default.createElement(
        "span",
        { className: "sources-header-info", dir: "ltr", onClick: onClick },
        L10N.getFormatStr("sources.search", (0, _text.formatKeyShortcut)(L10N.getStr("sources.search.key2")))
      );
    }
  }

  render() {
    const { selectedTab } = this.props;

    return _react2.default.createElement(
      "div",
      { className: "sources-panel" },
      this.renderTabs(),
      selectedTab === "sources" ? _react2.default.createElement(_SourcesTree2.default, null) : _react2.default.createElement(_Outline2.default, null)
    );
  }
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

exports.default = (0, _reactRedux.connect)(state => ({
  selectedTab: (0, _selectors.getSelectedPrimaryPaneTab)(state),
  sources: (0, _selectors.getSources)(state),
  sourceSearchOn: (0, _selectors.getActiveSearch)(state) === "source"
}), dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(PrimaryPanes);

/***/ }),
/* 1552 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.Outline = undefined;

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _redux = __webpack_require__(3);

var _reactRedux = __webpack_require__(1189);

var _actions = __webpack_require__(1354);

var _actions2 = _interopRequireDefault(_actions);

var _selectors = __webpack_require__(1352);

__webpack_require__(1319);

var _PreviewFunction = __webpack_require__(1446);

var _PreviewFunction2 = _interopRequireDefault(_PreviewFunction);

var _lodash = __webpack_require__(2);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

class Outline extends _react.Component {
  selectItem(location) {
    const { selectedSource, selectLocation } = this.props;
    if (!selectedSource) {
      return;
    }
    const selectedSourceId = selectedSource.get("id");
    const startLine = location.start.line;
    selectLocation({ sourceId: selectedSourceId, line: startLine });
  }

  renderPlaceholder() {
    return _react2.default.createElement(
      "div",
      { className: "outline-pane-info" },
      L10N.getStr("outline.noFunctions")
    );
  }

  renderFunction(func) {
    const { name, location, parameterNames } = func;

    return _react2.default.createElement(
      "li",
      {
        key: `${name}:${location.start.line}:${location.start.column}`,
        className: "outline-list__element",
        onClick: () => this.selectItem(location)
      },
      _react2.default.createElement(
        "span",
        { className: "outline-list__element-icon" },
        "\u03BB"
      ),
      _react2.default.createElement(_PreviewFunction2.default, { func: { name, parameterNames } })
    );
  }

  renderClassFunctions(klass, functions) {
    if (klass == null || functions.length == 0) {
      return null;
    }

    const classFunc = functions.find(func => func.name === klass);
    const classFunctions = functions.filter(func => func.klass === klass);
    const classInfo = this.props.symbols.classes.find(c => c.name === klass);

    const heading = classFunc ? _react2.default.createElement(
      "h2",
      null,
      this.renderFunction(classFunc)
    ) : _react2.default.createElement(
      "h2",
      {
        onClick: classInfo ? () => this.selectItem(classInfo.location) : null
      },
      _react2.default.createElement(
        "span",
        { className: "keyword" },
        "class"
      ),
      " ",
      klass
    );

    return _react2.default.createElement(
      "div",
      { className: "outline-list__class", key: klass },
      heading,
      _react2.default.createElement(
        "ul",
        { className: "outline-list__class-list" },
        classFunctions.map(func => this.renderFunction(func))
      )
    );
  }

  renderFunctions(functions) {
    const classes = (0, _lodash.uniq)(functions.map(func => func.klass));
    const namedFunctions = functions.filter(func => func.name != "anonymous" && !func.klass && !classes.includes(func.name));

    const classFunctions = functions.filter(func => func.name != "anonymous" && !!func.klass);

    return _react2.default.createElement(
      "ul",
      { className: "outline-list" },
      namedFunctions.map(func => this.renderFunction(func)),
      classes.map(klass => this.renderClassFunctions(klass, classFunctions))
    );
  }

  render() {
    const { symbols } = this.props;

    const symbolsToDisplay = symbols.functions.filter(func => func.name != "anonymous");

    return _react2.default.createElement(
      "div",
      { className: "outline" },
      symbolsToDisplay.length > 0 ? this.renderFunctions(symbols.functions) : this.renderPlaceholder()
    );
  }
}

exports.Outline = Outline;
exports.default = (0, _reactRedux.connect)(state => {
  const selectedSource = (0, _selectors.getSelectedSource)(state);
  return {
    symbols: (0, _selectors.getSymbols)(state, selectedSource && selectedSource.toJS()),
    selectedSource
  };
}, dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(Outline);

/***/ }),
/* 1553 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _classnames = __webpack_require__(175);

var _classnames2 = _interopRequireDefault(_classnames);

var _devtoolsContextmenu = __webpack_require__(1413);

var _reactRedux = __webpack_require__(1189);

var _selectors = __webpack_require__(1352);

var _sourceTree = __webpack_require__(1532);

var _sources = __webpack_require__(1797);

var _ui = __webpack_require__(1385);

var _ManagedTree = __webpack_require__(1404);

var _ManagedTree2 = _interopRequireDefault(_ManagedTree);

var _Svg = __webpack_require__(1359);

var _Svg2 = _interopRequireDefault(_Svg);

var _sourcesTree = __webpack_require__(1442);

var _source = __webpack_require__(1356);

var _clipboard = __webpack_require__(1388);

var _prefs = __webpack_require__(226);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

// Utils


// Components


// Selectors
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

// Dependencies
class SourcesTree extends _react.Component {

  constructor(props) {
    super(props);

    _initialiseProps.call(this);

    const { debuggeeUrl, sources, projectRoot } = this.props;

    this.state = (0, _sourcesTree.createTree)({
      projectRoot,
      debuggeeUrl,
      sources
    });
  }

  componentWillReceiveProps(nextProps) {
    const {
      projectRoot,
      debuggeeUrl,
      sources,
      shownSource,
      selectedSource
    } = this.props;

    const { uncollapsedTree, sourceTree } = this.state;

    if (projectRoot != nextProps.projectRoot || debuggeeUrl != nextProps.debuggeeUrl || nextProps.sources.size === 0) {
      // early recreate tree because of changes
      // to project root, debugee url or lack of sources
      return this.setState((0, _sourcesTree.createTree)({
        sources: nextProps.sources,
        debuggeeUrl: nextProps.debuggeeUrl,
        projectRoot: nextProps.projectRoot
      }));
    }
    if (nextProps.shownSource && nextProps.shownSource != shownSource) {
      const listItems = (0, _sourcesTree.getDirectories)(nextProps.shownSource, sourceTree);

      if (listItems && listItems[0]) {
        this.selectItem(listItems[0]);
      }

      return this.setState({ listItems });
    }

    if (nextProps.selectedSource && nextProps.selectedSource != selectedSource) {
      const highlightItems = (0, _sourcesTree.getDirectories)((0, _source.getRawSourceURL)(nextProps.selectedSource.get("url")), sourceTree);

      return this.setState({ highlightItems });
    }

    // NOTE: do not run this every time a source is clicked,
    // only when a new source is added
    if (nextProps.sources != this.props.sources) {
      this.setState((0, _sourcesTree.updateTree)({
        newSources: nextProps.sources,
        prevSources: sources,
        debuggeeUrl,
        projectRoot,
        uncollapsedTree,
        sourceTree
      }));
    }
  }

  render() {
    const expanded = this.props.expanded;
    const {
      focusedItem,
      highlightItems,
      listItems,
      parentMap,
      sourceTree
    } = this.state;

    const onExpand = (item, expandedState) => {
      this.props.setExpandedState(expandedState);
    };

    const onCollapse = (item, expandedState) => {
      this.props.setExpandedState(expandedState);
    };

    const isEmpty = sourceTree.contents.length === 0;
    const treeProps = {
      autoExpandAll: false,
      autoExpandDepth: expanded ? 0 : 1,
      expanded,
      getChildren: item => (0, _sourcesTree.nodeHasChildren)(item) ? item.contents : [],
      getParent: item => parentMap.get(item),
      getPath: this.getPath,
      getRoots: () => sourceTree.contents,
      highlightItems,
      itemHeight: 21,
      key: isEmpty ? "empty" : "full",
      listItems,
      onCollapse,
      onExpand,
      onFocus: this.focusItem,
      renderItem: this.renderItem
    };

    const tree = _react2.default.createElement(_ManagedTree2.default, treeProps);

    if (isEmpty) {
      return _react2.default.createElement(
        "div",
        { className: "no-sources-message" },
        L10N.getStr("sources.noSourcesAvailable")
      );
    }

    const onKeyDown = e => {
      if (e.keyCode === 13 && focusedItem) {
        this.selectItem(focusedItem);
      }
    };

    return _react2.default.createElement(
      "div",
      { className: "sources-list", onKeyDown: onKeyDown },
      tree
    );
  }
}

// Actions

var _initialiseProps = function () {
  this.focusItem = item => {
    this.setState({ focusedItem: item });
  };

  this.selectItem = item => {
    if (!(0, _sourcesTree.nodeHasChildren)(item)) {
      this.props.selectLocation({ sourceId: item.contents.get("id") });
    }
  };

  this.getPath = item => {
    const { sources } = this.props;
    const obj = item.contents.get && item.contents.get("id");

    let blackBoxedPart = "";

    if (typeof obj !== "undefined" && sources.has(obj) && sources.get(obj).get("isBlackBoxed")) {
      blackBoxedPart = "update";
    }

    return `${item.path}/${item.name}/${blackBoxedPart}`;
  };

  this.getIcon = (sources, item, depth) => {
    const { debuggeeUrl } = this.props;

    if (item.path === "/Webpack") {
      return _react2.default.createElement(_Svg2.default, { name: "webpack" });
    }
    if (item.path === "/Angular") {
      return _react2.default.createElement(_Svg2.default, { name: "angular" });
    }

    if (depth === 0) {
      return _react2.default.createElement("img", {
        className: (0, _classnames2.default)("domain", {
          debuggee: debuggeeUrl && debuggeeUrl.includes(item.name)
        })
      });
    }

    if (!(0, _sourcesTree.nodeHasChildren)(item)) {
      const obj = item.contents.get("id");
      const source = sources.get(obj);
      if (source && source.get("isBlackBoxed")) {
        return _react2.default.createElement("img", { className: "blackBox" });
      }
      const sourceType = {
        coffee: "coffeescript",
        js: "javascript",
        jsx: "react",
        ts: "typescript"
      }[(0, _sourcesTree.getExtension)(source)];
      return sourceType ? _react2.default.createElement(_Svg2.default, { className: "source-icon", name: sourceType }) : _react2.default.createElement("img", { className: "file" });
    }

    return _react2.default.createElement("img", { className: "folder" });
  };

  this.onContextMenu = (event, item) => {
    const copySourceUri2Label = L10N.getStr("copySourceUri2");
    const copySourceUri2Key = L10N.getStr("copySourceUri2.accesskey");
    const setDirectoryRootLabel = L10N.getStr("setDirectoryRoot.label");
    const setDirectoryRootKey = L10N.getStr("setDirectoryRoot.accesskey");
    const removeDirectoryRootLabel = L10N.getStr("removeDirectoryRoot.label");

    event.stopPropagation();
    event.preventDefault();

    const menuOptions = [];

    if (!(0, _sourcesTree.isDirectory)(item)) {
      const source = item.contents.get("url");
      const copySourceUri2 = {
        id: "node-menu-copy-source",
        label: copySourceUri2Label,
        accesskey: copySourceUri2Key,
        disabled: false,
        click: () => (0, _clipboard.copyToTheClipboard)(source)
      };

      menuOptions.push(copySourceUri2);
    }

    if ((0, _sourcesTree.isDirectory)(item) && _prefs.features.root) {
      const { path } = item;
      const { projectRoot } = this.props;

      if (projectRoot.endsWith(path)) {
        menuOptions.push({
          id: "node-remove-directory-root",
          label: removeDirectoryRootLabel,
          disabled: false,
          click: () => this.props.clearProjectDirectoryRoot()
        });
      } else {
        menuOptions.push({
          id: "node-set-directory-root",
          label: setDirectoryRootLabel,
          accesskey: setDirectoryRootKey,
          disabled: false,
          click: () => this.props.setProjectDirectoryRoot(path)
        });
      }
    }

    (0, _devtoolsContextmenu.showMenu)(event, menuOptions);
  };

  this.renderItem = (item, depth, focused, _, expanded, { setExpanded }) => {
    const arrow = (0, _sourcesTree.nodeHasChildren)(item) ? _react2.default.createElement("img", {
      className: (0, _classnames2.default)("arrow", {
        expanded: expanded
      })
    }) : _react2.default.createElement("i", { className: "no-arrow" });
    const { sources } = this.props;
    const icon = this.getIcon(sources, item, depth);

    return _react2.default.createElement(
      "div",
      {
        className: (0, _classnames2.default)("node", { focused }),
        key: item.path,
        onClick: e => {
          this.focusItem(item);

          if ((0, _sourcesTree.isDirectory)(item)) {
            setExpanded(item, !!expanded, e.altKey);
          } else {
            this.selectItem(item);
          }
        },
        onContextMenu: e => this.onContextMenu(e, item)
      },
      arrow,
      icon,
      _react2.default.createElement(
        "span",
        { className: "label" },
        " ",
        item.name,
        " "
      )
    );
  };
};

const mapStateToProps = state => {
  return {
    shownSource: (0, _selectors.getShownSource)(state),
    selectedSource: (0, _selectors.getSelectedSource)(state),
    debuggeeUrl: (0, _selectors.getDebuggeeUrl)(state),
    expanded: (0, _selectors.getExpandedState)(state),
    projectRoot: (0, _selectors.getProjectDirectoryRoot)(state),
    sources: (0, _selectors.getSources)(state)
  };
};

const actionCreators = {
  setExpandedState: _sourceTree.setExpandedState,
  selectLocation: _sources.selectLocation,
  setProjectDirectoryRoot: _ui.setProjectDirectoryRoot,
  clearProjectDirectoryRoot: _ui.clearProjectDirectoryRoot
};

exports.default = (0, _reactRedux.connect)(mapStateToProps, actionCreators)(SourcesTree);

/***/ }),
/* 1554 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _propTypes = __webpack_require__(20);

var _propTypes2 = _interopRequireDefault(_propTypes);

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _reactDom = __webpack_require__(4);

var _reactDom2 = _interopRequireDefault(_reactDom);

var _reactRedux = __webpack_require__(1189);

var _classnames = __webpack_require__(175);

var _classnames2 = _interopRequireDefault(_classnames);

var _devtoolsLaunchpad = __webpack_require__(1362);

var _source = __webpack_require__(1356);

var _devtoolsConfig = __webpack_require__(1355);

var _prefs = __webpack_require__(226);

var _indentation = __webpack_require__(1438);

var _selectors = __webpack_require__(1352);

var _redux = __webpack_require__(3);

var _actions = __webpack_require__(1354);

var _actions2 = _interopRequireDefault(_actions);

var _Footer = __webpack_require__(1555);

var _Footer2 = _interopRequireDefault(_Footer);

var _SearchBar = __webpack_require__(1556);

var _SearchBar2 = _interopRequireDefault(_SearchBar);

var _HighlightLines = __webpack_require__(1557);

var _HighlightLines2 = _interopRequireDefault(_HighlightLines);

var _Preview = __webpack_require__(1558);

var _Preview2 = _interopRequireDefault(_Preview);

var _Breakpoints = __webpack_require__(1588);

var _Breakpoints2 = _interopRequireDefault(_Breakpoints);

var _HitMarker = __webpack_require__(1590);

var _HitMarker2 = _interopRequireDefault(_HitMarker);

var _CallSites = __webpack_require__(1591);

var _CallSites2 = _interopRequireDefault(_CallSites);

var _DebugLine = __webpack_require__(1593);

var _DebugLine2 = _interopRequireDefault(_DebugLine);

var _HighlightLine = __webpack_require__(1796);

var _HighlightLine2 = _interopRequireDefault(_HighlightLine);

var _EmptyLines = __webpack_require__(1594);

var _EmptyLines2 = _interopRequireDefault(_EmptyLines);

var _GutterMenu = __webpack_require__(1595);

var _GutterMenu2 = _interopRequireDefault(_GutterMenu);

var _EditorMenu = __webpack_require__(1596);

var _EditorMenu2 = _interopRequireDefault(_EditorMenu);

var _ConditionalPanel = __webpack_require__(1598);

var _ConditionalPanel2 = _interopRequireDefault(_ConditionalPanel);

var _editor = __webpack_require__(1358);

var _ui = __webpack_require__(1439);

__webpack_require__(1332);

__webpack_require__(1333);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 cssVars = {
  searchbarHeight: "var(--editor-searchbar-height)",
  secondSearchbarHeight: "var(--editor-second-searchbar-height)",
  footerHeight: "var(--editor-footer-height)"
};

// Redux actions


class Editor extends _react.PureComponent {
  constructor(props) {
    super(props);

    this.onToggleBreakpoint = (key, e) => {
      e.preventDefault();
      e.stopPropagation();
      const { selectedSource, conditionalPanelLine } = this.props;

      if (!selectedSource) {
        return;
      }

      const line = this.getCurrentLine();

      if (e.shiftKey) {
        this.toggleConditionalPanel(line);
      } else if (!conditionalPanelLine) {
        this.props.toggleBreakpoint(line);
      } else {
        this.toggleConditionalPanel(line);
        this.props.toggleBreakpoint(line);
      }
    };

    this.onToggleConditionalPanel = (key, e) => {
      e.stopPropagation();
      e.preventDefault();
      const line = this.getCurrentLine();
      this.toggleConditionalPanel(line);
    };

    this.onEscape = (key, e) => {
      if (!this.state.editor) {
        return;
      }

      const { codeMirror } = this.state.editor;
      if (codeMirror.listSelections().length > 1) {
        codeMirror.execCommand("singleSelection");
        e.preventDefault();
      }
    };

    this.onSearchAgain = (_, e) => {
      this.props.traverseResults(e.shiftKey, this.state.editor);
    };

    this.onGutterClick = (cm, line, gutter, ev) => {
      const {
        selectedSource,
        conditionalPanelLine,
        closeConditionalPanel,
        addOrToggleDisabledBreakpoint,
        toggleBreakpoint,
        continueToHere
      } = this.props;

      // ignore right clicks in the gutter
      if (ev.ctrlKey && ev.button === 0 || ev.which === 3 || selectedSource && selectedSource.get("isBlackBoxed") || !selectedSource) {
        return;
      }

      if (conditionalPanelLine) {
        return closeConditionalPanel();
      }

      if (gutter === "CodeMirror-foldgutter") {
        return;
      }

      const sourceLine = (0, _editor.toSourceLine)(selectedSource.get("id"), line);

      if (ev.altKey) {
        return continueToHere(sourceLine);
      }

      if (ev.shiftKey) {
        return addOrToggleDisabledBreakpoint(sourceLine);
      }

      return toggleBreakpoint(sourceLine);
    };

    this.onGutterContextMenu = event => {
      event.stopPropagation();
      event.preventDefault();
      return this.props.setContextMenu("Gutter", event);
    };

    this.toggleConditionalPanel = line => {
      const {
        conditionalPanelLine,
        closeConditionalPanel,
        openConditionalPanel
      } = this.props;

      if (conditionalPanelLine) {
        return closeConditionalPanel();
      }

      return openConditionalPanel(line);
    };

    this.closeConditionalPanel = () => {
      return this.props.closeConditionalPanel();
    };

    this.state = {
      highlightedLineRange: null,
      editor: null
    };
  }

  componentWillReceiveProps(nextProps) {
    if (!this.state.editor) {
      return;
    }

    (0, _ui.resizeBreakpointGutter)(this.state.editor.codeMirror);
    (0, _ui.resizeToggleButton)(this.state.editor.codeMirror);
  }

  componentWillUpdate(nextProps) {
    this.setText(nextProps);
    this.setSize(nextProps);
    this.scrollToLocation(nextProps);
  }

  setupEditor() {
    const editor = (0, _editor.createEditor)();

    // disables the default search shortcuts
    // $FlowIgnore
    editor._initShortcuts = () => {};

    const node = _reactDom2.default.findDOMNode(this);
    if (node instanceof HTMLElement) {
      editor.appendToLocalElement(node.querySelector(".editor-mount"));
    }

    const { codeMirror } = editor;
    const codeMirrorWrapper = codeMirror.getWrapperElement();

    (0, _ui.resizeBreakpointGutter)(codeMirror);
    (0, _ui.resizeToggleButton)(codeMirror);

    (0, _devtoolsLaunchpad.debugGlobal)("cm", codeMirror);

    codeMirror.on("gutterClick", this.onGutterClick);

    // Set code editor wrapper to be focusable
    codeMirrorWrapper.tabIndex = 0;
    codeMirrorWrapper.addEventListener("keydown", e => this.onKeyDown(e));
    codeMirrorWrapper.addEventListener("click", e => this.onClick(e));

    const toggleFoldMarkerVisibility = e => {
      if (node instanceof HTMLElement) {
        node.querySelectorAll(".CodeMirror-guttermarker-subtle").forEach(elem => {
          elem.classList.toggle("visible");
        });
      }
    };

    const codeMirrorGutter = codeMirror.getGutterElement();
    codeMirrorGutter.addEventListener("mouseleave", toggleFoldMarkerVisibility);
    codeMirrorGutter.addEventListener("mouseenter", toggleFoldMarkerVisibility);

    if (!(0, _devtoolsConfig.isFirefox)()) {
      codeMirror.on("gutterContextMenu", (cm, line, eventName, event) => this.onGutterContextMenu(event));
      codeMirror.on("contextmenu", (cm, event) => this.openMenu(event));
    } else {
      codeMirrorWrapper.addEventListener("contextmenu", event => this.openMenu(event));
    }

    this.setState({ editor });
    return editor;
  }

  componentDidMount() {
    const editor = this.setupEditor();

    const { selectedSource } = this.props;
    const { shortcuts } = this.context;

    const searchAgainKey = L10N.getStr("sourceSearch.search.again.key2");
    const searchAgainPrevKey = L10N.getStr("sourceSearch.search.againPrev.key2");

    shortcuts.on(L10N.getStr("toggleBreakpoint.key"), this.onToggleBreakpoint);
    shortcuts.on(L10N.getStr("toggleCondPanel.key"), this.onToggleConditionalPanel);
    shortcuts.on("Esc", this.onEscape);
    shortcuts.on(searchAgainPrevKey, this.onSearchAgain);
    shortcuts.on(searchAgainKey, this.onSearchAgain);

    (0, _editor.updateDocument)(editor, selectedSource);
  }

  componentWillUnmount() {
    if (this.state.editor) {
      this.state.editor.destroy();
      this.setState({ editor: null });
    }

    const searchAgainKey = L10N.getStr("sourceSearch.search.again.key2");
    const searchAgainPrevKey = L10N.getStr("sourceSearch.search.againPrev.key2");
    const shortcuts = this.context.shortcuts;
    shortcuts.off(L10N.getStr("toggleBreakpoint.key"));
    shortcuts.off(L10N.getStr("toggleCondPanel.key"));
    shortcuts.off(searchAgainPrevKey);
    shortcuts.off(searchAgainKey);
  }

  componentDidUpdate(prevProps, prevState) {
    // NOTE: when devtools are opened, the editor is not set when
    // the source loads so we need to wait until the editor is
    // set to update the text and size.
    if (!prevState.editor && this.state.editor) {
      this.setText(this.props);
      this.setSize(this.props);
    }
  }

  getCurrentLine() {
    const { codeMirror } = this.state.editor;
    const { selectedSource } = this.props;
    const line = (0, _editor.getCursorLine)(codeMirror);

    return (0, _editor.toSourceLine)(selectedSource.get("id"), line);
  }

  onKeyDown(e) {
    const { codeMirror } = this.state.editor;
    const { key, target } = e;
    const codeWrapper = codeMirror.getWrapperElement();
    const textArea = codeWrapper.querySelector("textArea");

    if (key === "Escape" && target == textArea) {
      e.stopPropagation();
      e.preventDefault();
      codeWrapper.focus();
    } else if (key === "Enter" && target == codeWrapper) {
      e.preventDefault();
      // Focus into editor's text area
      textArea.focus();
    }
  }

  /*
   * The default Esc command is overridden in the CodeMirror keymap to allow
   * the Esc keypress event to be catched by the toolbox and trigger the
   * split console. Restore it here, but preventDefault if and only if there
   * is a multiselection.
   */


  openMenu(event) {
    event.stopPropagation();
    event.preventDefault();

    const { setContextMenu } = this.props;
    if (event.target.classList.contains("CodeMirror-linenumber")) {
      return setContextMenu("Gutter", event);
    }

    return setContextMenu("Editor", event);
  }

  onClick(e) {
    const { selectedLocation, jumpToMappedLocation } = this.props;

    if (e.metaKey && e.altKey) {
      const sourceLocation = (0, _editor.getSourceLocationFromMouseEvent)(this.state.editor, selectedLocation, e);
      jumpToMappedLocation(sourceLocation);
    }
  }

  shouldScrollToLocation(nextProps) {
    const { selectedLocation, selectedSource } = this.props;
    const { editor } = this.state;

    if (!nextProps.selectedSource || !editor || !nextProps.selectedLocation) {
      return false;
    }

    if (!(0, _source.isLoaded)(nextProps.selectedSource)) {
      return false;
    }

    if (!nextProps.selectedLocation.line) {
      return false;
    }

    const isFirstLoad = (!selectedSource || !(0, _source.isLoaded)(selectedSource)) && (0, _source.isLoaded)(nextProps.selectedSource);

    const locationChanged = selectedLocation !== nextProps.selectedLocation;
    return isFirstLoad || locationChanged;
  }

  scrollToLocation(nextProps) {
    const { editor } = this.state;

    if (this.shouldScrollToLocation(nextProps)) {
      let { line, column } = (0, _editor.toEditorPosition)(nextProps.selectedLocation);

      if ((0, _editor.hasDocument)(nextProps.selectedSource.get("id"))) {
        const doc = (0, _editor.getDocument)(nextProps.selectedSource.get("id"));
        const lineText = doc.getLine(line);
        column = Math.max(column, (0, _indentation.getIndentation)(lineText));
      }
      (0, _editor.scrollToColumn)(editor.codeMirror, line, column);
    }
  }

  setSize(nextProps) {
    if (!this.state.editor) {
      return;
    }

    if (nextProps.startPanelSize !== this.props.startPanelSize || nextProps.endPanelSize !== this.props.endPanelSize) {
      this.state.editor.codeMirror.setSize();
    }
  }

  setText(props) {
    const { selectedSource, symbols } = props;

    if (!this.state.editor) {
      return;
    }

    if (!selectedSource) {
      return this.clearEditor();
    }

    if (!(0, _source.isLoaded)(selectedSource)) {
      return (0, _editor.showLoading)(this.state.editor);
    }

    if (selectedSource.get("error")) {
      return this.showErrorMessage(selectedSource.get("error"));
    }
    if (selectedSource) {
      return (0, _editor.showSourceText)(this.state.editor, selectedSource.toJS(), symbols);
    }
  }

  clearEditor() {
    const { editor } = this.state;
    if (!editor) {
      return;
    }

    (0, _editor.clearEditor)(editor);
  }

  showErrorMessage(msg) {
    const { editor } = this.state;
    if (!editor) {
      return;
    }

    (0, _editor.showErrorMessage)(editor, msg);
  }

  getInlineEditorStyles() {
    const { selectedSource, horizontal, searchOn } = this.props;

    const subtractions = [];

    if ((0, _editor.shouldShowFooter)(selectedSource, horizontal)) {
      subtractions.push(cssVars.footerHeight);
    }

    if (searchOn) {
      subtractions.push(cssVars.searchbarHeight);
      subtractions.push(cssVars.secondSearchbarHeight);
    }

    return {
      height: subtractions.length === 0 ? "100%" : `calc(100% - ${subtractions.join(" - ")})`
    };
  }

  renderHitCounts() {
    const { hitCount, selectedSource } = this.props;

    if (!selectedSource || !(0, _source.isLoaded)(selectedSource) || !hitCount || !this.state.editor) {
      return;
    }

    return hitCount.filter(marker => marker.get("count") > 0).map(marker => _react2.default.createElement(_HitMarker2.default, {
      key: marker.get("line"),
      hitData: marker.toJS(),
      editor: this.state.editor.codeMirror
    }));
  }

  renderItems() {
    const { horizontal, selectedSource } = this.props;
    const { editor } = this.state;

    if (!editor || !selectedSource) {
      return null;
    }

    return _react2.default.createElement(
      "div",
      null,
      _react2.default.createElement(_DebugLine2.default, null),
      _react2.default.createElement(_HighlightLine2.default, null),
      _react2.default.createElement(_EmptyLines2.default, { editor: editor }),
      _react2.default.createElement(_Breakpoints2.default, { editor: editor }),
      _react2.default.createElement(_Preview2.default, { editor: editor, editorRef: this.$editorWrapper }),
      ";",
      _react2.default.createElement(_Footer2.default, { editor: editor, horizontal: horizontal }),
      _react2.default.createElement(_HighlightLines2.default, { editor: editor }),
      _react2.default.createElement(_EditorMenu2.default, { editor: editor }),
      _react2.default.createElement(_GutterMenu2.default, { editor: editor }),
      _react2.default.createElement(_ConditionalPanel2.default, { editor: editor }),
      _prefs.features.columnBreakpoints ? _react2.default.createElement(_CallSites2.default, { editor: editor }) : null,
      this.renderHitCounts()
    );
  }

  renderSearchBar() {
    const { editor } = this.state;

    if (!editor) {
      return null;
    }

    return _react2.default.createElement(_SearchBar2.default, { editor: editor });
  }

  render() {
    const { coverageOn } = this.props;

    return _react2.default.createElement(
      "div",
      {
        className: (0, _classnames2.default)("editor-wrapper", {
          "coverage-on": coverageOn
        }),
        ref: c => this.$editorWrapper = c
      },
      this.renderSearchBar(),
      _react2.default.createElement("div", {
        className: "editor-mount devtools-monospace",
        style: this.getInlineEditorStyles()
      }),
      this.renderItems()
    );
  }
}

Editor.contextTypes = {
  shortcuts: _propTypes2.default.object
};

const mapStateToProps = state => {
  const selectedSource = (0, _selectors.getSelectedSource)(state);
  const sourceId = selectedSource ? selectedSource.get("id") : "";
  return {
    selectedLocation: (0, _selectors.getSelectedLocation)(state),
    selectedSource,
    searchOn: (0, _selectors.getActiveSearch)(state) === "file",
    hitCount: (0, _selectors.getHitCountForSource)(state, sourceId),
    coverageOn: (0, _selectors.getCoverageEnabled)(state),
    conditionalPanelLine: (0, _selectors.getConditionalPanelLine)(state),
    symbols: (0, _selectors.getSymbols)(state, selectedSource && selectedSource.toJS())
  };
};

exports.default = (0, _reactRedux.connect)(mapStateToProps, dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(Editor);

/***/ }),
/* 1555 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _reactRedux = __webpack_require__(1189);

var _redux = __webpack_require__(3);

var _actions = __webpack_require__(1354);

var _actions2 = _interopRequireDefault(_actions);

var _selectors = __webpack_require__(1352);

var _classnames = __webpack_require__(175);

var _classnames2 = _interopRequireDefault(_classnames);

var _prefs = __webpack_require__(226);

var _source = __webpack_require__(1356);

var _sources = __webpack_require__(1369);

var _editor = __webpack_require__(1358);

var _PaneToggle = __webpack_require__(1407);

var _PaneToggle2 = _interopRequireDefault(_PaneToggle);

__webpack_require__(1322);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

class SourceFooter extends _react.PureComponent {
  prettyPrintButton() {
    const { selectedSource, togglePrettyPrint } = this.props;
    const sourceLoaded = selectedSource && (0, _source.isLoaded)(selectedSource);

    if (!(0, _editor.shouldShowPrettyPrint)(selectedSource)) {
      return;
    }

    const tooltip = L10N.getStr("sourceTabs.prettyPrint");

    const type = "prettyPrint";
    return _react2.default.createElement(
      "button",
      {
        onClick: () => togglePrettyPrint(selectedSource.get("id")),
        className: (0, _classnames2.default)("action", type, {
          active: sourceLoaded,
          pretty: (0, _source.isPretty)(selectedSource)
        }),
        key: type,
        title: tooltip,
        "aria-label": tooltip
      },
      _react2.default.createElement("img", { className: type })
    );
  }

  blackBoxButton() {
    const { selectedSource, toggleBlackBox } = this.props;
    const sourceLoaded = selectedSource && (0, _source.isLoaded)(selectedSource);

    if (!sourceLoaded) {
      return;
    }

    const blackboxed = selectedSource.get("isBlackBoxed");

    const tooltip = L10N.getStr("sourceFooter.blackbox");
    const type = "black-box";

    return _react2.default.createElement(
      "button",
      {
        onClick: () => toggleBlackBox(selectedSource.toJS()),
        className: (0, _classnames2.default)("action", type, {
          active: sourceLoaded,
          blackboxed: blackboxed
        }),
        key: type,
        title: tooltip,
        "aria-label": tooltip
      },
      _react2.default.createElement("img", { className: "blackBox" })
    );
  }

  blackBoxSummary() {
    const { selectedSource } = this.props;

    if (!selectedSource || !selectedSource.get("isBlackBoxed")) {
      return;
    }

    return _react2.default.createElement(
      "span",
      { className: "blackbox-summary" },
      L10N.getStr("sourceFooter.blackboxed")
    );
  }

  coverageButton() {
    const { recordCoverage } = this.props;

    if (!_prefs.features.codeCoverage) {
      return;
    }

    return _react2.default.createElement(
      "button",
      {
        className: "coverage action",
        title: L10N.getStr("sourceFooter.codeCoverage"),
        onClick: () => recordCoverage(),
        "aria-label": L10N.getStr("sourceFooter.codeCoverage")
      },
      "C"
    );
  }

  renderToggleButton() {
    if (this.props.horizontal) {
      return;
    }

    return _react2.default.createElement(_PaneToggle2.default, {
      position: "end",
      collapsed: !this.props.endPanelCollapsed,
      horizontal: this.props.horizontal,
      handleClick: this.props.togglePaneCollapse
    });
  }

  renderCommands() {
    return _react2.default.createElement(
      "div",
      { className: "commands" },
      this.prettyPrintButton(),
      this.blackBoxButton(),
      this.blackBoxSummary(),
      this.coverageButton()
    );
  }

  renderSourceSummary() {
    const { mappedSource, jumpToMappedLocation, selectedSource } = this.props;
    if (mappedSource) {
      const bundleSource = mappedSource.toJS();
      const filename = (0, _source.getFilename)(bundleSource);
      const tooltip = L10N.getFormatStr("sourceFooter.mappedSourceTooltip", filename);
      const title = L10N.getFormatStr("sourceFooter.mappedSource", filename);
      const mappedSourceLocation = {
        sourceId: selectedSource.get("id"),
        line: 1,
        column: 1
      };
      return _react2.default.createElement(
        "button",
        {
          className: "mapped-source",
          onClick: () => jumpToMappedLocation(mappedSourceLocation),
          title: tooltip
        },
        _react2.default.createElement(
          "span",
          null,
          title
        )
      );
    }
    return null;
  }

  render() {
    const { selectedSource, horizontal } = this.props;

    if (!(0, _editor.shouldShowFooter)(selectedSource, horizontal)) {
      return null;
    }

    return _react2.default.createElement(
      "div",
      { className: "source-footer" },
      this.renderCommands(),
      this.renderSourceSummary(),
      this.renderToggleButton()
    );
  }
}

exports.default = (0, _reactRedux.connect)(state => {
  const selectedSource = (0, _selectors.getSelectedSource)(state);
  const selectedId = selectedSource.get("id");
  const source = selectedSource.toJS();
  return {
    selectedSource,
    mappedSource: (0, _sources.getGeneratedSource)(state, source),
    prettySource: (0, _selectors.getPrettySource)(state, selectedId),
    endPanelCollapsed: (0, _selectors.getPaneCollapse)(state, "end")
  };
}, dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(SourceFooter);

/***/ }),
/* 1556 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _propTypes = __webpack_require__(20);

var _propTypes2 = _interopRequireDefault(_propTypes);

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _reactRedux = __webpack_require__(1189);

var _redux = __webpack_require__(3);

var _Svg = __webpack_require__(1359);

var _Svg2 = _interopRequireDefault(_Svg);

var _actions = __webpack_require__(1354);

var _actions2 = _interopRequireDefault(_actions);

var _selectors = __webpack_require__(1352);

var _editor = __webpack_require__(1358);

var _resultList = __webpack_require__(1406);

var _classnames = __webpack_require__(175);

var _classnames2 = _interopRequireDefault(_classnames);

var _SearchInput = __webpack_require__(1379);

var _SearchInput2 = _interopRequireDefault(_SearchInput);

var _lodash = __webpack_require__(2);

__webpack_require__(1323);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function getShortcuts() {
  const searchAgainKey = L10N.getStr("sourceSearch.search.again.key2");
  const searchAgainPrevKey = L10N.getStr("sourceSearch.search.againPrev.key2");
  const searchKey = L10N.getStr("sourceSearch.search.key2");

  return {
    shiftSearchAgainShortcut: searchAgainPrevKey,
    searchAgainShortcut: searchAgainKey,
    searchShortcut: searchKey
  };
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

class SearchBar extends _react.Component {
  constructor(props) {
    super(props);

    this.onEscape = e => {
      this.closeSearch(e);
    };

    this.clearSearch = () => {
      const { editor: ed, query, modifiers } = this.props;
      if (ed && modifiers) {
        const ctx = { ed, cm: ed.codeMirror };
        (0, _editor.removeOverlay)(ctx, query, modifiers.toJS());
      }
    };

    this.closeSearch = e => {
      const { closeFileSearch, editor, searchOn } = this.props;
      if (editor && searchOn) {
        this.clearSearch();
        closeFileSearch(editor);
        e.stopPropagation();
        e.preventDefault();
      }
      this.setState({ query: "", inputFocused: false });
    };

    this.toggleSearch = e => {
      e.stopPropagation();
      e.preventDefault();
      const { editor, searchOn, setActiveSearch } = this.props;

      if (!searchOn) {
        setActiveSearch("file");
      }

      if (searchOn && editor) {
        const query = editor.codeMirror.getSelection() || this.state.query;

        if (query !== "") {
          this.setState({ query, inputFocused: true });
          this.doSearch(query);
        } else {
          this.setState({ query: "", inputFocused: true });
        }
      }
    };

    this.doSearch = query => {
      const { selectedSource } = this.props;
      if (!selectedSource || !selectedSource.get("text")) {
        return;
      }

      this.props.doSearch(query, this.props.editor);
    };

    this.updateSearchResults = (characterIndex, line, matches) => {
      const matchIndex = matches.findIndex(elm => elm.line === line && elm.ch === characterIndex);
      this.props.updateSearchResults({
        matches,
        matchIndex,
        count: matches.length,
        index: characterIndex
      });
    };

    this.traverseResults = (e, rev) => {
      e.stopPropagation();
      e.preventDefault();
      const editor = this.props.editor;

      if (!editor) {
        return;
      }
      this.props.traverseResults(rev, editor);
    };

    this.onChange = e => {
      this.setState({ query: e.target.value });

      return this.doSearch(e.target.value);
    };

    this.onBlur = e => {
      this.setState({ inputFocused: false });
    };

    this.onKeyDown = e => {
      if (e.key !== "Enter" && e.key !== "F3") {
        return;
      }

      this.traverseResults(e, e.shiftKey);
      e.preventDefault();
    };

    this.renderSearchModifiers = () => {
      const { modifiers, toggleFileSearchModifier, query } = this.props;
      const { doSearch } = this;

      function SearchModBtn({ modVal, className, svgName, tooltip }) {
        const preppedClass = (0, _classnames2.default)(className, {
          active: modifiers && modifiers.get(modVal)
        });
        return _react2.default.createElement(
          "button",
          {
            className: preppedClass,
            onClick: () => {
              toggleFileSearchModifier(modVal);
              doSearch(query);
            },
            title: tooltip
          },
          _react2.default.createElement(_Svg2.default, { name: svgName })
        );
      }

      return _react2.default.createElement(
        "div",
        { className: "search-modifiers" },
        _react2.default.createElement(
          "span",
          { className: "search-type-name" },
          L10N.getStr("symbolSearch.searchModifier.modifiersLabel")
        ),
        _react2.default.createElement(SearchModBtn, {
          modVal: "regexMatch",
          className: "regex-match-btn",
          svgName: "regex-match",
          tooltip: L10N.getStr("symbolSearch.searchModifier.regex")
        }),
        _react2.default.createElement(SearchModBtn, {
          modVal: "caseSensitive",
          className: "case-sensitive-btn",
          svgName: "case-match",
          tooltip: L10N.getStr("symbolSearch.searchModifier.caseSensitive")
        }),
        _react2.default.createElement(SearchModBtn, {
          modVal: "wholeWord",
          className: "whole-word-btn",
          svgName: "whole-word-match",
          tooltip: L10N.getStr("symbolSearch.searchModifier.wholeWord")
        })
      );
    };

    this.state = {
      query: props.query,
      selectedResultIndex: 0,
      count: 0,
      index: -1,
      inputFocused: false
    };
  }

  componentWillUnmount() {
    const shortcuts = this.context.shortcuts;
    const {
      searchShortcut,
      searchAgainShortcut,
      shiftSearchAgainShortcut
    } = getShortcuts();

    shortcuts.off(searchShortcut);
    shortcuts.off("Escape");
    shortcuts.off(searchAgainShortcut);
    shortcuts.off(shiftSearchAgainShortcut);
  }

  componentDidMount() {
    // overwrite this.doSearch with debounced version to
    // reduce frequency of queries
    this.doSearch = (0, _lodash.debounce)(this.doSearch, 100);
    const shortcuts = this.context.shortcuts;
    const {
      searchShortcut,
      searchAgainShortcut,
      shiftSearchAgainShortcut
    } = getShortcuts();

    shortcuts.on(searchShortcut, (_, e) => this.toggleSearch(e));
    shortcuts.on("Escape", (_, e) => this.onEscape(e));

    shortcuts.on(shiftSearchAgainShortcut, (_, e) => this.traverseResults(e, true));

    shortcuts.on(searchAgainShortcut, (_, e) => this.traverseResults(e, false));
  }

  componentDidUpdate(prevProps, prevState) {
    if (this.refs.resultList && this.refs.resultList.refs) {
      (0, _resultList.scrollList)(this.refs.resultList.refs, this.state.selectedResultIndex);
    }
  }

  // Handlers

  // Renderers
  buildSummaryMsg() {
    const { searchResults: { matchIndex, count, index }, query } = this.props;

    if (query.trim() == "") {
      return "";
    }

    if (count == 0) {
      return L10N.getStr("editor.noResults");
    }

    if (index == -1) {
      return L10N.getFormatStr("sourceSearch.resultsSummary1", count);
    }

    return L10N.getFormatStr("editor.searchResults", matchIndex + 1, count);
  }

  shouldShowErrorEmoji() {
    const { query, searchResults: { count } } = this.props;
    return !!query && !count;
  }

  render() {
    const { searchResults: { count }, searchOn } = this.props;

    if (!searchOn) {
      return _react2.default.createElement("div", null);
    }
    return _react2.default.createElement(
      "div",
      { className: "search-bar" },
      _react2.default.createElement(_SearchInput2.default, {
        query: this.state.query,
        count: count,
        placeholder: L10N.getStr("sourceSearch.search.placeholder"),
        summaryMsg: this.buildSummaryMsg(),
        onChange: this.onChange,
        onBlur: this.onBlur,
        showErrorEmoji: this.shouldShowErrorEmoji(),
        onKeyDown: this.onKeyDown,
        handleNext: e => this.traverseResults(e, false),
        handlePrev: e => this.traverseResults(e, true),
        handleClose: this.closeSearch,
        shouldFocus: this.state.inputFocused
      }),
      _react2.default.createElement(
        "div",
        { className: "search-bottom-bar" },
        this.renderSearchModifiers()
      )
    );
  }
}

SearchBar.contextTypes = {
  shortcuts: _propTypes2.default.object
};

exports.default = (0, _reactRedux.connect)(state => {
  return {
    searchOn: (0, _selectors.getActiveSearch)(state) === "file",
    selectedSource: (0, _selectors.getSelectedSource)(state),
    selectedLocation: (0, _selectors.getSelectedLocation)(state),
    query: (0, _selectors.getFileSearchQuery)(state),
    modifiers: (0, _selectors.getFileSearchModifiers)(state),
    highlightedLineRange: (0, _selectors.getHighlightedLineRange)(state),
    searchResults: (0, _selectors.getFileSearchResults)(state)
  };
}, dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(SearchBar);

/***/ }),
/* 1557 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _react = __webpack_require__(0);

var _lodash = __webpack_require__(2);

var _reactRedux = __webpack_require__(1189);

var _selectors = __webpack_require__(1352);

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

class HighlightLines extends _react.Component {

  constructor() {
    super();
    this.highlightLineRange = this.highlightLineRange.bind(this);
  }

  componentDidMount() {
    this.highlightLineRange();
  }

  componentWillUpdate() {
    this.clearHighlightRange();
  }

  componentDidUpdate() {
    this.highlightLineRange();
  }

  componentWillUnmount() {
    this.clearHighlightRange();
  }

  clearHighlightRange() {
    const { highlightedLineRange, editor } = this.props;

    const { codeMirror } = editor;

    if ((0, _lodash.isEmpty)(highlightedLineRange) || !codeMirror) {
      return;
    }

    const { start, end } = highlightedLineRange;
    codeMirror.operation(() => {
      (0, _lodash.range)(start - 1, end).forEach(line => {
        codeMirror.removeLineClass(line, "line", "highlight-lines");
      });
    });
  }

  highlightLineRange() {
    const { highlightedLineRange, editor } = this.props;

    const { codeMirror } = editor;

    if ((0, _lodash.isEmpty)(highlightedLineRange) || !codeMirror) {
      return;
    }

    const { start, end } = highlightedLineRange;

    codeMirror.operation(() => {
      editor.alignLine(start);

      (0, _lodash.range)(start - 1, end).forEach(line => {
        codeMirror.addLineClass(line, "line", "highlight-lines");
      });
    });
  }

  render() {
    return null;
  }
}

exports.default = (0, _reactRedux.connect)(state => ({
  highlightedLineRange: (0, _selectors.getHighlightedLineRange)(state)
}))(HighlightLines);

/***/ }),
/* 1558 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _reactRedux = __webpack_require__(1189);

var _lodash = __webpack_require__(2);

var _Popup = __webpack_require__(1559);

var _Popup2 = _interopRequireDefault(_Popup);

var _selectors = __webpack_require__(1352);

var _actions = __webpack_require__(1354);

var _actions2 = _interopRequireDefault(_actions);

var _editor = __webpack_require__(1358);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

class Preview extends _react.PureComponent {
  constructor(props) {
    super(props);

    this.onMouseOver = e => {
      const { target } = e;
      if (this.props.selectedFrameVisible) {
        this.props.updatePreview(target, this.props.editor);
      }
    };

    this.onMouseUp = () => {
      this.setState({ selecting: false });
      return true;
    };

    this.onMouseDown = () => {
      this.setState({ selecting: true });
      return true;
    };

    this.onScroll = () => {
      this.props.clearPreview();
    };

    this.onClose = e => {
      this.props.clearPreview();
    };

    this.state = { selecting: false };
    self.onMouseOver = (0, _lodash.debounce)(this.onMouseOver, 40);
  }

  componentDidMount() {
    const { codeMirror } = this.props.editor;
    const codeMirrorWrapper = codeMirror.getWrapperElement();

    codeMirror.on("scroll", this.onScroll);
    codeMirrorWrapper.addEventListener("mouseover", this.onMouseOver);
    codeMirrorWrapper.addEventListener("mouseup", this.onMouseUp);
    codeMirrorWrapper.addEventListener("mousedown", this.onMouseDown);
  }

  componentWillUnmount() {
    const codeMirror = this.props.editor.codeMirror;
    const codeMirrorWrapper = codeMirror.getWrapperElement();
    codeMirrorWrapper.removeEventListener("mouseover", this.onMouseOver);
    codeMirrorWrapper.removeEventListener("mouseup", this.onMouseUp);
    codeMirrorWrapper.removeEventListener("mousedown", this.onMouseDown);
    codeMirror.off("scroll", this.onScroll);
  }

  render() {
    const { selectedSource, preview } = this.props;

    if (!this.props.editor || !selectedSource || this.state.selecting) {
      return null;
    }

    if (!preview || preview.updating) {
      return null;
    }

    const { result, expression, location, cursorPos, extra } = preview;
    const value = result;
    if (typeof value == "undefined" || value.optimizedOut) {
      return null;
    }

    const editorRange = (0, _editor.toEditorRange)(selectedSource.get("id"), location);

    return _react2.default.createElement(_Popup2.default, {
      value: value,
      editor: this.props.editor,
      editorRef: this.props.editorRef,
      range: editorRange,
      expression: expression,
      popoverPos: cursorPos,
      extra: extra,
      onClose: e => this.onClose(e)
    });
  }
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.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 {
  addExpression,
  setPopupObjectProperties,
  updatePreview,
  clearPreview
} = _actions2.default;

exports.default = (0, _reactRedux.connect)(state => ({
  preview: (0, _selectors.getPreview)(state),
  selectedSource: (0, _selectors.getSelectedSource)(state),
  selectedFrameVisible: (0, _selectors.isSelectedFrameVisible)(state)
}), {
  addExpression,
  setPopupObjectProperties,
  updatePreview,
  clearPreview
})(Preview);

/***/ }),
/* 1559 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.Popup = undefined;

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _reactRedux = __webpack_require__(1189);

var _devtoolsReps = __webpack_require__(1408);

var _devtoolsReps2 = _interopRequireDefault(_devtoolsReps);

var _actions = __webpack_require__(1354);

var _actions2 = _interopRequireDefault(_actions);

var _selectors = __webpack_require__(1352);

var _Popover = __webpack_require__(1586);

var _Popover2 = _interopRequireDefault(_Popover);

var _PreviewFunction = __webpack_require__(1446);

var _PreviewFunction2 = _interopRequireDefault(_PreviewFunction);

var _editor = __webpack_require__(1358);

var _preview = __webpack_require__(1807);

var _Svg = __webpack_require__(1359);

var _Svg2 = _interopRequireDefault(_Svg);

var _firefox = __webpack_require__(1500);

__webpack_require__(1328);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

const { REPS: { Rep }, MODE, ObjectInspector, ObjectInspectorUtils } = _devtoolsReps2.default; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                * License, v. 2.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,
  getChildren,
  getValue,
  nodeIsPrimitive
} = ObjectInspectorUtils.node;
const { loadItemProperties } = ObjectInspectorUtils.loadProperties;

class Popup extends _react.Component {

  async componentWillMount() {
    const {
      value,
      expression,
      setPopupObjectProperties,
      popupObjectProperties
    } = this.props;
    const root = createNode(null, expression, expression, { value });

    if (!nodeIsPrimitive(root) && value && value.actor && !popupObjectProperties[value.actor]) {
      const onLoadItemProperties = loadItemProperties(root, _firefox.createObjectClient);
      if (onLoadItemProperties !== null) {
        const properties = await onLoadItemProperties;
        setPopupObjectProperties(value, properties);
      }
    }
  }

  componentDidMount() {
    const { value, editor, range } = this.props;

    if (!value || !value.type == "object") {
      return;
    }

    this.marker = (0, _editor.markText)(editor, "preview-selection", range);
  }

  componentWillUnmount() {
    if (this.marker) {
      this.marker.clear();
    }
  }

  getRoot() {
    const { expression, value } = this.props;

    return {
      name: expression,
      path: expression,
      contents: { value }
    };
  }

  getChildren() {
    const { popupObjectProperties } = this.props;

    const root = this.getRoot();
    const value = getValue(root);
    const actor = value ? value.actor : null;
    const loadedRootProperties = popupObjectProperties[actor];
    if (!loadedRootProperties) {
      return null;
    }

    const children = getChildren({
      item: root,
      loadedProperties: new Map([[root.path, loadedRootProperties]])
    });

    if (children.length > 0) {
      return children;
    }

    return null;
  }

  renderFunctionPreview() {
    const { selectSourceURL, value } = this.props;

    if (!value) {
      return null;
    }

    const { location } = value;
    return _react2.default.createElement(
      "div",
      {
        className: "preview-popup",
        onClick: () => selectSourceURL(location.url, { line: location.line })
      },
      _react2.default.createElement(_PreviewFunction2.default, { func: value })
    );
  }

  renderReact(react, roots) {
    const reactHeader = react.displayName || "React Component";

    const header = _react2.default.createElement(
      "div",
      { className: "header-container" },
      _react2.default.createElement(
        "h3",
        null,
        reactHeader
      )
    );

    roots = roots.filter(r => ["state", "props"].includes(r.name));
    return _react2.default.createElement(
      "div",
      { className: "preview-popup" },
      header,
      this.renderObjectInspector(roots)
    );
  }

  renderImmutable(immutable) {
    const immutableHeader = immutable.type || "Immutable";

    const header = _react2.default.createElement(
      "div",
      { className: "header-container" },
      _react2.default.createElement(_Svg2.default, { name: "immutable", className: "immutable-logo" }),
      _react2.default.createElement(
        "h3",
        null,
        immutableHeader
      )
    );

    const roots = [createNode(null, "entries", "entries", { value: immutable.entries })];

    return _react2.default.createElement(
      "div",
      { className: "preview-popup" },
      header,
      this.renderObjectInspector(roots)
    );
  }

  renderObjectPreview() {
    const root = this.getRoot();

    if (nodeIsPrimitive(root)) {
      return null;
    }

    const roots = this.getChildren();
    if (!Array.isArray(roots) || roots.length === 0) {
      return null;
    }

    const { extra: { react, immutable } } = this.props;
    const grip = getValue(root);

    if ((0, _preview.isReactComponent)(grip)) {
      return this.renderReact(react, roots);
    }

    if ((0, _preview.isImmutable)(grip)) {
      return this.renderImmutable(immutable);
    }

    return _react2.default.createElement(
      "div",
      { className: "preview-popup" },
      this.renderObjectInspector(roots)
    );
  }

  renderSimplePreview(value) {
    const { openLink } = this.props;
    return _react2.default.createElement(
      "div",
      { className: "preview-popup" },
      Rep({
        object: value,
        mode: MODE.LONG,
        openLink
      })
    );
  }

  renderObjectInspector(roots) {
    const { openLink } = this.props;

    return _react2.default.createElement(ObjectInspector, {
      roots: roots,
      autoExpandDepth: 0,
      disableWrap: true,
      disabledFocus: true,
      openLink: openLink,
      createObjectClient: grip => (0, _firefox.createObjectClient)(grip)
    });
  }

  renderPreview() {
    const { value } = this.props;
    if (!value) {
      return null;
    }

    if (value.class === "Function") {
      return this.renderFunctionPreview();
    }

    if (value.type === "object") {
      return _react2.default.createElement(
        "div",
        null,
        this.renderObjectPreview()
      );
    }

    return this.renderSimplePreview(value);
  }

  getPreviewType(value) {
    if (typeof value == "number" || typeof value == "boolean" || typeof value == "string" && value.length < 10 || typeof value == "number" && value.toString().length < 10 || value.type == "null" || value.type == "undefined" || value.class === "Function") {
      return "tooltip";
    }

    return "popover";
  }

  render() {
    const { popoverPos, onClose, value, editorRef } = this.props;
    const type = this.getPreviewType(value);

    if (value && value.type === "object" && !this.getChildren()) {
      return null;
    }

    return _react2.default.createElement(
      _Popover2.default,
      {
        targetPosition: popoverPos,
        onMouseLeave: onClose,
        type: type,
        editorRef: editorRef
      },
      this.renderPreview()
    );
  }
}

exports.Popup = Popup;
const {
  addExpression,
  selectSourceURL,
  selectLocation,
  setPopupObjectProperties,
  openLink
} = _actions2.default;

exports.default = (0, _reactRedux.connect)(state => ({
  popupObjectProperties: (0, _selectors.getAllPopupObjectProperties)(state)
}), {
  addExpression,
  selectSourceURL,
  selectLocation,
  setPopupObjectProperties,
  openLink
})(Popup);

/***/ }),
/* 1560 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Dependencies
const {
  getGripType,
  wrapRender
} = __webpack_require__(1353);

const dom = __webpack_require__(1758);
const { span } = dom;

/**
 * Renders undefined value
 */
const Undefined = function () {
  return span({ className: "objectBox objectBox-undefined" }, "undefined");
};

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
};

/***/ }),
/* 1561 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Dependencies
const { wrapRender } = __webpack_require__(1353);
const dom = __webpack_require__(1758);
const { span } = dom;

/**
 * Renders null value
 */
function Null(props) {
  return span({ className: "objectBox objectBox-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
};

/***/ }),
/* 1562 */,
/* 1563 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Dependencies
const PropTypes = __webpack_require__(20);

const {
  getGripType,
  wrapRender
} = __webpack_require__(1353);

const dom = __webpack_require__(1758);
const { span } = dom;

/**
 * Renders a number
 */
Number.propTypes = {
  object: PropTypes.oneOfType([PropTypes.object, PropTypes.number, PropTypes.bool]).isRequired
};

function Number(props) {
  let value = props.object;

  return span({ className: "objectBox objectBox-number" }, stringify(value));
}

function stringify(object) {
  let isNegativeZero = Object.is(object, -0) || object.type && object.type == "-0";

  return isNegativeZero ? "-0" : String(object);
}

function supportsObject(object, noGrip = false) {
  return ["boolean", "number", "-0"].includes(getGripType(object, noGrip));
}

// Exports from this module

module.exports = {
  rep: wrapRender(Number),
  supportsObject
};

/***/ }),
/* 1564 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Dependencies
const PropTypes = __webpack_require__(20);
const {
  wrapRender,
  ellipsisElement
} = __webpack_require__(1353);
const PropRep = __webpack_require__(1381);
const { MODE } = __webpack_require__(1357);

const dom = __webpack_require__(1758);
const { span } = dom;

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,
  // @TODO Change this to Object.values once it's supported in Node's version of V8
  mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
  title: PropTypes.string
};

function ObjectRep(props) {
  let object = props.object;
  let propsArray = safePropIterator(props, object);

  if (props.mode === MODE.TINY) {
    const tinyModeItems = [];
    if (getTitle(props, object) !== DEFAULT_TITLE) {
      tinyModeItems.push(getTitleElement(props, object));
    } else {
      tinyModeItems.push(span({
        className: "objectLeftBrace"
      }, "{"), propsArray.length > 0 ? ellipsisElement : null, span({
        className: "objectRightBrace"
      }, "}"));
    }

    return span({ className: "objectBox objectBox-object" }, ...tinyModeItems);
  }

  return span({ className: "objectBox objectBox-object" }, getTitleElement(props, object), span({
    className: "objectLeftBrace"
  }, " { "), ...propsArray, span({
    className: "objectRightBrace"
  }, " }"));
}

function getTitleElement(props, object) {
  return span({ className: "objectTitle" }, getTitle(props, object));
}

function getTitle(props, object) {
  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(Object.assign({}, props, {
      key: name,
      mode: MODE.TINY,
      name,
      object: value,
      equal: ": "
    })));
    propertiesNumber++;

    if (propertiesNumber < propertiesNames.length) {
      elements.push(", ");
    }
  };

  try {
    for (let 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 (let 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) {
  return true;
}

// Exports from this module
module.exports = {
  rep: wrapRender(ObjectRep),
  supportsObject
};

/***/ }),
/* 1565 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Dependencies
const PropTypes = __webpack_require__(20);

const {
  getGripType,
  wrapRender
} = __webpack_require__(1353);

const dom = __webpack_require__(1758);
const { span } = dom;

/**
 * Renders a symbol.
 */
SymbolRep.propTypes = {
  object: PropTypes.object.isRequired
};

function SymbolRep(props) {
  let {
    className = "objectBox objectBox-symbol",
    object
  } = props;
  let { name } = object;

  return span({
    className,
    "data-link-actor-id": object.actor
  }, `Symbol(${name || ""})`);
}

function supportsObject(object, noGrip = false) {
  return getGripType(object, noGrip) == "symbol";
}

// Exports from this module
module.exports = {
  rep: wrapRender(SymbolRep),
  supportsObject
};

/***/ }),
/* 1566 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Dependencies
const PropTypes = __webpack_require__(20);

const {
  getGripType,
  wrapRender
} = __webpack_require__(1353);

const dom = __webpack_require__(1758);
const { span } = dom;

/**
 * Renders a Infinity object
 */
InfinityRep.propTypes = {
  object: PropTypes.object.isRequired
};

function InfinityRep(props) {
  const {
    object
  } = props;

  return span({ className: "objectBox objectBox-number" }, object.type);
}

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
};

/***/ }),
/* 1567 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Dependencies
const {
  getGripType,
  wrapRender
} = __webpack_require__(1353);

const dom = __webpack_require__(1758);
const { span } = dom;

/**
 * Renders a NaN object
 */
function NaNRep(props) {
  return span({ className: "objectBox objectBox-nan" }, "NaN");
}

function supportsObject(object, noGrip = false) {
  return getGripType(object, noGrip) == "NaN";
}

// Exports from this module
module.exports = {
  rep: wrapRender(NaNRep),
  supportsObject
};

/***/ }),
/* 1568 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Dependencies
const dom = __webpack_require__(1758);
const PropTypes = __webpack_require__(20);
const {
  wrapRender
} = __webpack_require__(1353);
const { MODE } = __webpack_require__(1357);
const { span } = dom;

/**
 * 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))
};

function Accessor(props) {
  const {
    object
  } = props;

  const accessors = [];
  if (hasGetter(object)) {
    accessors.push("Getter");
  }
  if (hasSetter(object)) {
    accessors.push("Setter");
  }
  const title = accessors.join(" & ");

  return span({ className: "objectBox objectBox-accessor" }, span({
    className: "objectTitle"
  }, title));
}

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, noGrip = false) {
  if (noGrip !== true && (hasGetter(object) || hasSetter(object))) {
    return true;
  }

  return false;
}

// Exports from this module
module.exports = {
  rep: wrapRender(Accessor),
  supportsObject
};

/***/ }),
/* 1569 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// ReactJS
const PropTypes = __webpack_require__(20);
const dom = __webpack_require__(1758);
const { span } = dom;

// Reps
const {
  getGripType,
  isGrip,
  wrapRender
} = __webpack_require__(1353);
const { rep: StringRep } = __webpack_require__(1447);

/**
 * Renders DOM attribute
 */
Attribute.propTypes = {
  object: PropTypes.object.isRequired
};

function Attribute(props) {
  let {
    object
  } = props;
  let value = object.preview.value;

  return span({
    "data-link-actor-id": object.actor,
    className: "objectBox-Attr"
  }, span({ className: "attrName" }, getTitle(object)), span({ className: "attrEqual" }, "="), StringRep({ className: "attrValue", object: value }));
}

function getTitle(grip) {
  return grip.preview.nodeName;
}

// Registration
function supportsObject(grip, noGrip = false) {
  if (noGrip === true || !isGrip(grip)) {
    return false;
  }

  return getGripType(grip, noGrip) == "Attr" && grip.preview;
}

module.exports = {
  rep: wrapRender(Attribute),
  supportsObject
};

/***/ }),
/* 1570 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// ReactJS
const PropTypes = __webpack_require__(20);

// Reps
const {
  getGripType,
  isGrip,
  wrapRender
} = __webpack_require__(1353);

const dom = __webpack_require__(1758);
const { span } = dom;

/**
 * Used to render JS built-in Date() object.
 */
DateTime.propTypes = {
  object: PropTypes.object.isRequired
};

function DateTime(props) {
  let grip = props.object;
  let date;
  try {
    date = span({
      "data-link-actor-id": grip.actor,
      className: "objectBox"
    }, getTitle(grip), span({ className: "Date" }, new Date(grip.preview.timestamp).toISOString()));
  } catch (e) {
    date = span({ className: "objectBox" }, "Invalid Date");
  }

  return date;
}

function getTitle(grip) {
  return span({
    className: "objectTitle"
  }, grip.class + " ");
}

// Registration
function supportsObject(grip, noGrip = false) {
  if (noGrip === true || !isGrip(grip)) {
    return false;
  }

  return getGripType(grip, noGrip) == "Date" && grip.preview;
}

// Exports from this module
module.exports = {
  rep: wrapRender(DateTime),
  supportsObject
};

/***/ }),
/* 1571 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// ReactJS
const PropTypes = __webpack_require__(20);

// Reps
const {
  getGripType,
  isGrip,
  getURLDisplayString,
  wrapRender
} = __webpack_require__(1353);

const dom = __webpack_require__(1758);
const { span } = dom;

/**
 * Renders DOM document object.
 */
Document.propTypes = {
  object: PropTypes.object.isRequired
};

function Document(props) {
  let grip = props.object;
  const location = getLocation(grip);
  return span({
    "data-link-actor-id": grip.actor,
    className: "objectBox objectBox-document"
  }, getTitle(grip), location ? " " : null, location ? span({ className: "location" }, location) : null);
}

function getLocation(grip) {
  let location = grip.preview.location;
  return location ? getURLDisplayString(location) : null;
}

function getTitle(grip) {
  return span({
    className: "objectTitle"
  }, grip.class);
}

// Registration
function supportsObject(object, noGrip = false) {
  if (noGrip === true || !isGrip(object)) {
    return false;
  }

  const type = getGripType(object, noGrip);
  return object.preview && (type === "HTMLDocument" || type === "XULDocument");
}

// Exports from this module
module.exports = {
  rep: wrapRender(Document),
  supportsObject
};

/***/ }),
/* 1572 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// ReactJS
const PropTypes = __webpack_require__(20);

// Reps
const {
  isGrip,
  wrapRender
} = __webpack_require__(1353);

const { MODE } = __webpack_require__(1357);
const { rep } = __webpack_require__(1409);

/**
 * Renders DOM event objects.
 */
Event.propTypes = {
  object: PropTypes.object.isRequired,
  // @TODO Change this to Object.values once it's supported in Node's version of V8
  mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
  onDOMNodeMouseOver: PropTypes.func,
  onDOMNodeMouseOut: PropTypes.func,
  onInspectIconClick: PropTypes.func
};

function Event(props) {
  // Use `Object.assign` to keep `props` without changes because:
  // 1. JSON.stringify/JSON.parse is slow.
  // 2. Immutable.js is planned for the future.
  let gripProps = Object.assign({}, props, {
    title: getTitle(props)
  });
  gripProps.object = Object.assign({}, props.object);
  gripProps.object.preview = Object.assign({}, props.object.preview);

  gripProps.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) {
  let 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, noGrip = false) {
  if (noGrip === true || !isGrip(grip)) {
    return false;
  }

  return grip.preview && grip.preview.kind == "DOMEvent";
}

// Exports from this module
module.exports = {
  rep: wrapRender(Event),
  supportsObject
};

/***/ }),
/* 1573 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// ReactJS
const PropTypes = __webpack_require__(20);

// Reps
const {
  getGripType,
  isGrip,
  cropString,
  wrapRender
} = __webpack_require__(1353);
const { MODE } = __webpack_require__(1357);
const Svg = __webpack_require__(1410);

const dom = __webpack_require__(1758);
const { span } = dom;

const IGNORED_SOURCE_URLS = ["debugger eval code"];

/**
 * This component represents a template for Function objects.
 */
FunctionRep.propTypes = {
  object: PropTypes.object.isRequired,
  parameterNames: PropTypes.array,
  onViewSourceInDebugger: PropTypes.func
};

function FunctionRep(props) {
  let {
    object: grip,
    onViewSourceInDebugger
  } = props;

  let jumpToDefinitionButton;
  if (onViewSourceInDebugger && grip.location && grip.location.url && !IGNORED_SOURCE_URLS.includes(grip.location.url)) {
    jumpToDefinitionButton = Svg("jump-definition", {
      element: "a",
      draggable: false,
      title: "Jump to definition",
      onClick: e => {
        // Stop the event propagation so we don't trigger ObjectInspector expand/collapse.
        e.stopPropagation();
        onViewSourceInDebugger(grip.location);
      }
    });
  }

  return span({
    "data-link-actor-id": grip.actor,
    className: "objectBox objectBox-function",
    // Set dir="ltr" to prevent function parentheses from
    // appearing in the wrong direction
    dir: "ltr"
  }, getTitle(grip, props), getFunctionName(grip, props), "(", ...renderParams(props), ")", jumpToDefinitionButton);
}

function getTitle(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);
}

// Decodes an anonymous naming scheme that
// spider monkey implements based on "Naming Anonymous JavaScript Functions"
// http://johnjbarton.github.io/nonymous/index.html
const objectProperty = /([\w\d]+)$/;
const arrayProperty = /\[(.*?)\]$/;
const functionProperty = /([\w\d]+)[\/\.<]*?$/;
const annonymousProperty = /([\w\d]+)\(\^\)$/;

function getFunctionName(grip, props = {}) {
  let { functionName } = props;
  let name;

  if (functionName) {
    let 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 = grip.userDisplayName || grip.displayName || grip.name || props.functionName || "";

    const scenarios = [objectProperty, arrayProperty, functionProperty, annonymousProperty];

    scenarios.some(reg => {
      const match = reg.exec(name);
      if (match) {
        name = match[1];
        return true;
      }
      return false;
    });
  }

  return cropString(name, 100);
}

function renderParams(props) {
  const {
    parameterNames = []
  } = props;

  return parameterNames.filter(param => param).reduce((res, param, index, arr) => {
    res.push(span({ className: "param" }, param));
    if (index < arr.length - 1) {
      res.push(span({ className: "delimiter" }, ", "));
    }
    return res;
  }, []);
}

// Registration
function supportsObject(grip, noGrip = false) {
  const type = getGripType(grip, noGrip);
  if (noGrip === true || !isGrip(grip)) {
    return type == "function";
  }

  return type == "Function";
}

// Exports from this module

module.exports = {
  rep: wrapRender(FunctionRep),
  supportsObject,
  // exported for testing purpose.
  getFunctionName
};

/***/ }),
/* 1574 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// ReactJS
const PropTypes = __webpack_require__(20);
// Dependencies
const {
  getGripType,
  isGrip,
  wrapRender
} = __webpack_require__(1353);

const PropRep = __webpack_require__(1381);
const { MODE } = __webpack_require__(1357);

const dom = __webpack_require__(1758);
const { span } = dom;

/**
 * Renders a DOM Promise object.
 */
PromiseRep.propTypes = {
  object: PropTypes.object.isRequired,
  // @TODO Change this to Object.values once it's supported in Node's version of V8
  mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
  onDOMNodeMouseOver: PropTypes.func,
  onDOMNodeMouseOut: PropTypes.func,
  onInspectIconClick: PropTypes.func
};

function PromiseRep(props) {
  const object = props.object;
  const { promiseState } = object;

  const config = {
    "data-link-actor-id": object.actor,
    className: "objectBox objectBox-object"
  };

  if (props.mode === MODE.TINY) {
    let { Rep } = __webpack_require__(1372);

    return span(config, getTitle(object), span({
      className: "objectLeftBrace"
    }, " { "), Rep({ object: promiseState.state }), span({
      className: "objectRightBrace"
    }, " }"));
  }

  const propsArray = getProps(props, promiseState);
  return span(config, getTitle(object), span({
    className: "objectLeftBrace"
  }, " { "), ...propsArray, span({
    className: "objectRightBrace"
  }, " }"));
}

function getTitle(object) {
  return span({
    className: "objectTitle"
  }, object.class);
}

function getProps(props, promiseState) {
  const keys = ["state"];
  if (Object.keys(promiseState).includes("value")) {
    keys.push("value");
  }

  return keys.reduce((res, key, i) => {
    let object = promiseState[key];
    res = res.concat(PropRep(Object.assign({}, props, {
      mode: MODE.TINY,
      name: `<${key}>`,
      object,
      equal: ": ",
      suppressQuotes: true
    })));

    // Interleave commas between elements
    if (i !== keys.length - 1) {
      res.push(", ");
    }

    return res;
  }, []);
}

// Registration
function supportsObject(object, noGrip = false) {
  if (noGrip === true || !isGrip(object)) {
    return false;
  }
  return getGripType(object, noGrip) == "Promise";
}

// Exports from this module
module.exports = {
  rep: wrapRender(PromiseRep),
  supportsObject
};

/***/ }),
/* 1575 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// ReactJS
const PropTypes = __webpack_require__(20);

// Reps
const {
  getGripType,
  isGrip,
  wrapRender
} = __webpack_require__(1353);

const dom = __webpack_require__(1758);
const { span } = dom;

/**
 * Renders a grip object with regular expression.
 */
RegExp.propTypes = {
  object: PropTypes.object.isRequired
};

function RegExp(props) {
  let { object } = props;

  return span({
    "data-link-actor-id": object.actor,
    className: "objectBox objectBox-regexp regexpSource"
  }, getSource(object));
}

function getSource(grip) {
  return grip.displayString;
}

// Registration
function supportsObject(object, noGrip = false) {
  if (noGrip === true || !isGrip(object)) {
    return false;
  }

  return getGripType(object, noGrip) == "RegExp";
}

// Exports from this module
module.exports = {
  rep: wrapRender(RegExp),
  supportsObject
};

/***/ }),
/* 1576 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// ReactJS
const PropTypes = __webpack_require__(20);

// Reps
const {
  getGripType,
  isGrip,
  getURLDisplayString,
  wrapRender
} = __webpack_require__(1353);

const dom = __webpack_require__(1758);
const { span } = dom;

/**
 * Renders a grip representing CSSStyleSheet
 */
StyleSheet.propTypes = {
  object: PropTypes.object.isRequired
};

function StyleSheet(props) {
  let grip = props.object;

  return span({
    "data-link-actor-id": grip.actor,
    className: "objectBox objectBox-object"
  }, getTitle(grip), span({ className: "objectPropValue" }, getLocation(grip)));
}

function getTitle(grip) {
  let title = "StyleSheet ";
  return span({ className: "objectBoxTitle" }, title);
}

function getLocation(grip) {
  // Embedded stylesheets don't have URL and so, no preview.
  let url = grip.preview ? grip.preview.url : "";
  return url ? getURLDisplayString(url) : "";
}

// Registration
function supportsObject(object, noGrip = false) {
  if (noGrip === true || !isGrip(object)) {
    return false;
  }

  return getGripType(object, noGrip) == "CSSStyleSheet";
}

// Exports from this module

module.exports = {
  rep: wrapRender(StyleSheet),
  supportsObject
};

/***/ }),
/* 1577 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Dependencies
const PropTypes = __webpack_require__(20);
const {
  isGrip,
  cropString,
  cropMultipleLines,
  wrapRender
} = __webpack_require__(1353);
const { MODE } = __webpack_require__(1357);
const nodeConstants = __webpack_require__(1449);
const dom = __webpack_require__(1758);
const { span } = dom;

/**
 * Renders DOM comment node.
 */
CommentNode.propTypes = {
  object: PropTypes.object.isRequired,
  // @TODO Change this to Object.values once it's supported in Node's version of V8
  mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key]))
};

function CommentNode(props) {
  let {
    object,
    mode = MODE.SHORT
  } = props;

  let { textContent } = object.preview;
  if (mode === MODE.TINY) {
    textContent = cropMultipleLines(textContent, 30);
  } else if (mode === MODE.SHORT) {
    textContent = cropString(textContent, 50);
  }

  return span({
    className: "objectBox theme-comment",
    "data-link-actor-id": object.actor
  }, `<!-- ${textContent} -->`);
}

// Registration
function supportsObject(object, noGrip = false) {
  if (noGrip === true || !isGrip(object)) {
    return false;
  }
  return object.preview && object.preview.nodeType === nodeConstants.COMMENT_NODE;
}

// Exports from this module
module.exports = {
  rep: wrapRender(CommentNode),
  supportsObject
};

/***/ }),
/* 1578 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// ReactJS
const PropTypes = __webpack_require__(20);

// Utils
const {
  isGrip,
  wrapRender
} = __webpack_require__(1353);
const { rep: StringRep } = __webpack_require__(1447);
const { MODE } = __webpack_require__(1357);
const nodeConstants = __webpack_require__(1449);
const Svg = __webpack_require__(1410);

const dom = __webpack_require__(1758);
const { span } = dom;

/**
 * Renders DOM element node.
 */
ElementNode.propTypes = {
  object: PropTypes.object.isRequired,
  // @TODO Change this to Object.values once it's supported in Node's version of V8
  mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
  onDOMNodeMouseOver: PropTypes.func,
  onDOMNodeMouseOut: PropTypes.func,
  onInspectIconClick: PropTypes.func
};

function ElementNode(props) {
  let {
    object,
    mode,
    onDOMNodeMouseOver,
    onDOMNodeMouseOut,
    onInspectIconClick
  } = props;
  let elements = getElements(object, mode);

  let isInTree = object.preview && object.preview.isConnected === true;

  let baseConfig = {
    "data-link-actor-id": object.actor,
    className: "objectBox objectBox-node"
  };
  let inspectIcon;
  if (isInTree) {
    if (onDOMNodeMouseOver) {
      Object.assign(baseConfig, {
        onMouseOver: _ => onDOMNodeMouseOver(object)
      });
    }

    if (onDOMNodeMouseOut) {
      Object.assign(baseConfig, {
        onMouseOut: onDOMNodeMouseOut
      });
    }

    if (onInspectIconClick) {
      inspectIcon = Svg("open-inspector", {
        element: "a",
        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)
      });
    }
  }

  return span(baseConfig, ...elements, inspectIcon);
}

function getElements(grip, mode) {
  let { attributes, nodeName } = grip.preview;
  const nodeNameElement = span({
    className: "tag-name"
  }, nodeName);

  if (mode === MODE.TINY) {
    let elements = [nodeNameElement];
    if (attributes.id) {
      elements.push(span({ className: "attrName" }, `#${attributes.id}`));
    }
    if (attributes.class) {
      elements.push(span({ className: "attrName" }, attributes.class.trim().split(/\s+/).map(cls => `.${cls}`).join("")));
    }
    return elements;
  }
  let 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) => {
    let value = attributes[name];
    let attribute = span({}, span({ className: "attrName" }, name), span({ className: "attrEqual" }, "="), StringRep({ className: "attrValue", object: value }));

    return arr.concat([" ", attribute]);
  }, []);

  return [span({ className: "angleBracket" }, "<"), nodeNameElement, ...attributeElements, span({ className: "angleBracket" }, ">")];
}

// Registration
function supportsObject(object, noGrip = false) {
  if (noGrip === true || !isGrip(object)) {
    return false;
  }
  return object.preview && object.preview.nodeType === nodeConstants.ELEMENT_NODE;
}

// Exports from this module
module.exports = {
  rep: wrapRender(ElementNode),
  supportsObject
};

/***/ }),
/* 1579 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// ReactJS
const PropTypes = __webpack_require__(20);

// Reps
const {
  isGrip,
  cropString,
  wrapRender
} = __webpack_require__(1353);
const { MODE } = __webpack_require__(1357);
const Svg = __webpack_require__(1410);

const dom = __webpack_require__(1758);
const { span } = dom;

/**
 * Renders DOM #text node.
 */
TextNode.propTypes = {
  object: PropTypes.object.isRequired,
  // @TODO Change this to Object.values once it's supported in Node's version of V8
  mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
  onDOMNodeMouseOver: PropTypes.func,
  onDOMNodeMouseOut: PropTypes.func,
  onInspectIconClick: PropTypes.func
};

function TextNode(props) {
  let {
    object: grip,
    mode = MODE.SHORT,
    onDOMNodeMouseOver,
    onDOMNodeMouseOut,
    onInspectIconClick
  } = props;

  let baseConfig = {
    "data-link-actor-id": grip.actor,
    className: "objectBox objectBox-textNode"
  };
  let inspectIcon;
  let isInTree = grip.preview && grip.preview.isConnected === true;

  if (isInTree) {
    if (onDOMNodeMouseOver) {
      Object.assign(baseConfig, {
        onMouseOver: _ => onDOMNodeMouseOver(grip)
      });
    }

    if (onDOMNodeMouseOut) {
      Object.assign(baseConfig, {
        onMouseOut: onDOMNodeMouseOut
      });
    }

    if (onInspectIconClick) {
      inspectIcon = Svg("open-inspector", {
        element: "a",
        draggable: false,
        // TODO: Localize this with "openNodeInInspector" when Bug 1317038 lands
        title: "Click to select the node in the inspector",
        onClick: e => onInspectIconClick(grip, e)
      });
    }
  }

  if (mode === MODE.TINY) {
    return span(baseConfig, getTitle(grip), inspectIcon);
  }

  return span(baseConfig, getTitle(grip), span({ className: "nodeValue" }, " ", `"${getTextContent(grip)}"`), inspectIcon);
}

function getTextContent(grip) {
  return cropString(grip.preview.textContent);
}

function getTitle(grip) {
  const title = "#text";
  return span({}, title);
}

// Registration
function supportsObject(grip, noGrip = false) {
  if (noGrip === true || !isGrip(grip)) {
    return false;
  }

  return grip.preview && grip.class == "Text";
}

// Exports from this module
module.exports = {
  rep: wrapRender(TextNode),
  supportsObject
};

/***/ }),
/* 1580 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// ReactJS
const PropTypes = __webpack_require__(20);
// Utils
const {
  getGripType,
  isGrip,
  wrapRender
} = __webpack_require__(1353);
const { MODE } = __webpack_require__(1357);

const dom = __webpack_require__(1758);
const { span } = dom;

/**
 * Renders Error objects.
 */
ErrorRep.propTypes = {
  object: PropTypes.object.isRequired,
  // @TODO Change this to Object.values once it's supported in Node's version of V8
  mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key]))
};

function ErrorRep(props) {
  let object = props.object;
  let preview = object.preview;

  let name;
  if (preview && preview.name && 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";
  }

  let content = props.mode === MODE.TINY ? name : `${name}: ${preview.message}`;

  if (preview.stack && props.mode !== MODE.TINY) {
    /*
      * Since Reps are used in the JSON Viewer, we can't localize
      * the "Stack trace" label (defined in debugger.properties as
      * "variablesViewErrorStacktrace" property), until Bug 1317038 lands.
      */
    content = `${content}\nStack trace:\n${preview.stack}`;
  }

  return span({
    "data-link-actor-id": object.actor,
    className: "objectBox-stackTrace"
  }, content);
}

// Registration
function supportsObject(object, noGrip = false) {
  if (noGrip === true || !isGrip(object)) {
    return false;
  }
  return object.preview && getGripType(object, noGrip) === "Error" || object.class === "DOMException";
}

// Exports from this module
module.exports = {
  rep: wrapRender(ErrorRep),
  supportsObject
};

/***/ }),
/* 1581 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// ReactJS
const PropTypes = __webpack_require__(20);

// Reps
const {
  getGripType,
  isGrip,
  getURLDisplayString,
  wrapRender
} = __webpack_require__(1353);

const { MODE } = __webpack_require__(1357);

const dom = __webpack_require__(1758);
const { span } = dom;

/**
 * Renders a grip representing a window.
 */
WindowRep.propTypes = {
  // @TODO Change this to Object.values once it's supported in Node's version of V8
  mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
  object: PropTypes.object.isRequired
};

function WindowRep(props) {
  let {
    mode,
    object
  } = props;

  const config = {
    "data-link-actor-id": object.actor,
    className: "objectBox objectBox-Window"
  };

  if (mode === MODE.TINY) {
    return span(config, getTitle(object));
  }

  return span(config, getTitle(object), " ", span({ className: "location" }, getLocation(object)));
}

function getTitle(object) {
  let title = object.displayClass || object.class || "Window";
  return span({ className: "objectTitle" }, title);
}

function getLocation(object) {
  return getURLDisplayString(object.preview.url);
}

// Registration
function supportsObject(object, noGrip = false) {
  if (noGrip === true || !isGrip(object)) {
    return false;
  }

  return object.preview && getGripType(object, noGrip) == "Window";
}

// Exports from this module
module.exports = {
  rep: wrapRender(WindowRep),
  supportsObject
};

/***/ }),
/* 1582 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// ReactJS
const PropTypes = __webpack_require__(20);

// Reps
const {
  isGrip,
  wrapRender
} = __webpack_require__(1353);

const String = __webpack_require__(1447).rep;

const dom = __webpack_require__(1758);
const { span } = dom;

/**
 * Renders a grip object with textual data.
 */
ObjectWithText.propTypes = {
  object: PropTypes.object.isRequired
};

function ObjectWithText(props) {
  let grip = props.object;
  return span({
    "data-link-actor-id": grip.actor,
    className: "objectTitle objectBox objectBox-" + getType(grip)
  }, getType(grip), " ", getDescription(grip));
}

function getType(grip) {
  return grip.class;
}

function getDescription(grip) {
  return String({
    object: grip.preview.text
  });
}

// Registration
function supportsObject(grip, noGrip = false) {
  if (noGrip === true || !isGrip(grip)) {
    return false;
  }

  return grip.preview && grip.preview.kind == "ObjectWithText";
}

// Exports from this module
module.exports = {
  rep: wrapRender(ObjectWithText),
  supportsObject
};

/***/ }),
/* 1583 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// ReactJS
const PropTypes = __webpack_require__(20);

// Reps
const {
  isGrip,
  getURLDisplayString,
  wrapRender
} = __webpack_require__(1353);

const dom = __webpack_require__(1758);
const { span } = dom;

/**
 * Renders a grip object with URL data.
 */
ObjectWithURL.propTypes = {
  object: PropTypes.object.isRequired
};

function ObjectWithURL(props) {
  let grip = props.object;
  return span({
    "data-link-actor-id": grip.actor,
    className: "objectBox objectBox-" + getType(grip)
  }, getTitle(grip), span({ className: "objectPropValue" }, getDescription(grip)));
}

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, noGrip = false) {
  if (noGrip === true || !isGrip(grip)) {
    return false;
  }

  return grip.preview && grip.preview.kind == "ObjectWithURL";
}

// Exports from this module
module.exports = {
  rep: wrapRender(ObjectWithURL),
  supportsObject
};

/***/ }),
/* 1584 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Dependencies

const { lengthBubble } = __webpack_require__(2249);
const PropTypes = __webpack_require__(20);
const {
  isGrip,
  wrapRender,
  ellipsisElement
} = __webpack_require__(1353);
const PropRep = __webpack_require__(1381);
const { MODE } = __webpack_require__(1357);
const { ModePropType } = __webpack_require__(1448);

const dom = __webpack_require__(1758);
const { span } = dom;

/**
 * Renders an map. A map is represented by a list of its
 * entries enclosed in curly brackets.
 */
GripMap.propTypes = {
  object: PropTypes.object,
  // @TODO Change this to Object.values once it's supported in Node's version of V8
  mode: ModePropType,
  isInterestingEntry: PropTypes.func,
  onDOMNodeMouseOver: PropTypes.func,
  onDOMNodeMouseOut: PropTypes.func,
  onInspectIconClick: PropTypes.func,
  title: PropTypes.string
};

function GripMap(props) {
  let {
    mode,
    object
  } = props;

  const config = {
    "data-link-actor-id": object.actor,
    className: "objectBox objectBox-object"
  };

  const title = getTitle(props, object);
  const isEmpty = getLength(object) === 0;

  if (isEmpty || mode === MODE.TINY) {
    return span(config, title);
  }

  const propsArray = safeEntriesIterator(props, object, maxLengthMap.get(mode));

  return span(config, title, span({
    className: "objectLeftBrace"
  }, " { "), ...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 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.
  let isInterestingEntry = props.isInterestingEntry || ((type, value) => {
    return type == "boolean" || type == "number" || type == "string" && value.length != 0;
  });

  let 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);
    }));
  }

  let entries = getEntries(props, mapEntries, indexes);
  if (entries.length < getLength(object)) {
    // There are some undisplayed entries. Then display "…".
    entries.push(ellipsisElement);
  }

  return unfoldEntries(entries);
}

function unfoldEntries(items) {
  return items.reduce((res, item, index) => {
    if (Array.isArray(item)) {
      res = res.concat(item);
    } else {
      res.push(item);
    }

    // Interleave commas between elements
    if (index !== items.length - 1) {
      res.push(", ");
    }
    return res;
  }, []);
}

/**
 * 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) {
  let {
    onDOMNodeMouseOver,
    onDOMNodeMouseOut,
    onInspectIconClick
  } = props;

  // Make indexes ordered by ascending.
  indexes.sort(function (a, b) {
    return a - b;
  });

  return indexes.map((index, i) => {
    let [key, entryValue] = entries[index];
    let value = entryValue.value !== undefined ? entryValue.value : entryValue;

    return PropRep({
      name: key,
      equal: " \u2192 ",
      object: 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) {
      let value = entry && entry.value !== undefined ? entry.value : entry;
      // Type is specified in grip's "class" field and for primitive
      // values use typeof.
      let 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, noGrip = false) {
  if (noGrip === true || !isGrip(grip)) {
    return false;
  }
  return grip.preview && 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
};

/***/ }),
/* 1585 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var _devtoolsComponents = __webpack_require__(1441);

var _devtoolsComponents2 = _interopRequireDefault(_devtoolsComponents);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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, createFactory } = __webpack_require__(0);
const PropTypes = __webpack_require__(20);
const dom = __webpack_require__(1758);

const Tree = createFactory(_devtoolsComponents2.default.Tree);
__webpack_require__(1325);

const classnames = __webpack_require__(175);

const {
  REPS: {
    Rep,
    Grip
  }
} = __webpack_require__(1372);
const {
  MODE
} = __webpack_require__(1357);

const Utils = __webpack_require__(1938);

const {
  getChildren,
  getClosestGripNode,
  getParent,
  getValue,
  nodeHasAccessors,
  nodeHasProperties,
  nodeIsDefaultProperties,
  nodeIsFunction,
  nodeIsGetter,
  nodeIsMapEntry,
  nodeIsMissingArguments,
  nodeIsOptimizedOut,
  nodeIsPrimitive,
  nodeIsPrototype,
  nodeIsSetter,
  nodeIsUninitializedBinding,
  nodeIsUnmappedBinding,
  nodeIsUnscopedBinding,
  nodeIsWindow
} = Utils.node;

const {
  loadItemProperties
} = Utils.loadProperties;

// 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 child
// 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 {
  constructor(props) {
    super();
    this.cachedNodes = new Map();

    this.state = {
      actors: new Set(),
      expandedPaths: new Set(),
      focusedItem: null,
      loadedProperties: props.loadedProperties || new Map(),
      loading: new Map()
    };

    const self = this;

    self.getChildren = this.getChildren.bind(this);
    self.renderTreeItem = this.renderTreeItem.bind(this);
    self.setExpanded = this.setExpanded.bind(this);
    self.focusItem = this.focusItem.bind(this);
    self.getRoots = this.getRoots.bind(this);
  }

  shouldComponentUpdate(nextProps, nextState) {
    const {
      expandedPaths,
      loadedProperties
    } = this.state;

    if (this.props.roots !== nextProps.roots) {
      // Since the roots changed, we assume the properties did as well. Thus we can clear
      // the cachedNodes to avoid bugs and memory leaks.
      this.cachedNodes.clear();
      return true;
    }

    return expandedPaths.size !== nextState.expandedPaths.size || loadedProperties.size !== nextState.loadedProperties.size || [...expandedPaths].some(key => !nextState.expandedPaths.has(key));
  }

  componentWillUnmount() {
    const { releaseActor } = this.props;
    if (typeof releaseActor !== "function") {
      return;
    }

    const { actors } = this.state;
    for (let actor of actors) {
      releaseActor(actor);
    }
  }

  getChildren(item) {
    const {
      loadedProperties
    } = this.state;
    const { cachedNodes } = this;

    return getChildren({
      loadedProperties,
      cachedNodes,
      item
    });
  }

  getRoots() {
    return this.props.roots;
  }

  getKey(item) {
    return item.path;
  }

  /**
   * This function is responsible for expanding/collapsing a given node,
   * which also means that it will check if we need to fetch properties,
   * entries, prototype and symbols for the said node. If we do, it will call
   * the appropriate ObjectClient functions, and change the state of the component
   * with the results it gets from those functions.
   */
  async setExpanded(item, expand) {
    if (nodeIsPrimitive(item)) {
      return;
    }

    const {
      loadedProperties
    } = this.state;

    const key = this.getKey(item);

    this.setState((prevState, props) => {
      const newPaths = new Set(prevState.expandedPaths);
      if (expand === true) {
        newPaths.add(key);
      } else {
        newPaths.delete(key);
      }
      return {
        expandedPaths: newPaths
      };
    });

    if (expand === true) {
      const gripItem = getClosestGripNode(item);
      const value = getValue(gripItem);
      const path = item.path;

      const onItemPropertiesLoaded = loadItemProperties(item, this.props.createObjectClient, loadedProperties);
      if (onItemPropertiesLoaded !== null) {
        this.setState((prevState, props) => {
          const nextLoading = new Map(prevState.loading);
          nextLoading.set(path, onItemPropertiesLoaded);
          return {
            loading: nextLoading
          };
        });

        const properties = await onItemPropertiesLoaded;

        this.setState((prevState, props) => {
          const nextLoading = new Map(prevState.loading);
          nextLoading.delete(path);

          const isRoot = this.props.roots.some(root => {
            const rootValue = getValue(root);
            return rootValue && rootValue.actor === value.actor;
          });

          return {
            actors: isRoot ? prevState.actors : new Set(prevState.actors).add(value.actor),
            loadedProperties: new Map(prevState.loadedProperties).set(path, properties),
            loading: nextLoading
          };
        });
      }
    }
  }

  focusItem(item) {
    if (!this.props.disabledFocus && this.state.focusedItem !== item) {
      this.setState({
        focusedItem: item
      });

      if (this.props.onFocus) {
        this.props.onFocus(item);
      }
    }
  }

  renderTreeItem(item, depth, focused, arrow, expanded) {
    let objectValue;
    let label = item.name;
    let itemValue = getValue(item);

    const isPrimitive = nodeIsPrimitive(item);

    const unavailable = isPrimitive && itemValue && itemValue.hasOwnProperty && itemValue.hasOwnProperty("unavailable");

    if (nodeIsUninitializedBinding(item)) {
      objectValue = dom.span({ className: "unavailable" }, "(uninitialized)");
    } else if (nodeIsUnmappedBinding(item)) {
      objectValue = dom.span({ className: "unavailable" }, "(unmapped)");
    } else if (nodeIsUnscopedBinding(item)) {
      objectValue = dom.span({ className: "unavailable" }, "(unscoped)");
    } else if (nodeIsOptimizedOut(item)) {
      objectValue = dom.span({ className: "unavailable" }, "(optimized away)");
    } else if (nodeIsMissingArguments(item) || unavailable) {
      objectValue = dom.span({ className: "unavailable" }, "(unavailable)");
    } else if (nodeIsFunction(item) && !nodeIsGetter(item) && !nodeIsSetter(item) && (this.props.mode === MODE.TINY || !this.props.mode)) {
      objectValue = undefined;
      label = this.renderGrip(item, Object.assign({}, this.props, {
        functionName: label
      }));
    } else if (nodeHasProperties(item) || nodeHasAccessors(item) || nodeIsMapEntry(item) || isPrimitive) {
      let repsProp = Object.assign({}, this.props);
      if (depth > 0) {
        repsProp.mode = this.props.mode === MODE.LONG ? MODE.SHORT : MODE.TINY;
      }
      if (expanded) {
        repsProp.mode = MODE.TINY;
      }

      objectValue = this.renderGrip(item, repsProp);
    }

    const hasLabel = label !== null && typeof label !== "undefined";
    const hasValue = typeof objectValue !== "undefined";

    const {
      onDoubleClick,
      onLabelClick,
      dimTopLevelWindow
    } = this.props;

    return dom.div({
      className: classnames("node object-node", {
        focused,
        lessen: !expanded && (nodeIsDefaultProperties(item) || nodeIsPrototype(item) || dimTopLevelWindow === true && nodeIsWindow(item) && depth === 0)
      }),
      onClick: e => {
        e.stopPropagation();
        if (isPrimitive === false) {
          this.setExpanded(item, !expanded);
        }
      },
      onDoubleClick: onDoubleClick ? e => {
        e.stopPropagation();
        onDoubleClick(item, {
          depth,
          focused,
          expanded
        });
      } : null
    }, arrow, hasLabel ? dom.span({
      className: "object-label",
      onClick: onLabelClick ? event => {
        event.stopPropagation();
        onLabelClick(item, {
          depth,
          focused,
          expanded,
          setExpanded: this.setExpanded
        });
      } : null
    }, label) : null, hasLabel && hasValue ? dom.span({ className: "object-delimiter" }, ": ") : null, hasValue ? objectValue : null);
  }

  renderGrip(item, props) {
    const object = getValue(item);
    return Rep(Object.assign({}, props, {
      object,
      mode: props.mode || MODE.TINY,
      defaultRep: Grip
    }));
  }

  render() {
    const {
      autoExpandDepth = 1,
      autoExpandAll = true,
      disabledFocus,
      inline,
      itemHeight = 20,
      disableWrap = false
    } = this.props;

    const {
      expandedPaths,
      focusedItem
    } = this.state;

    let roots = this.getRoots();
    if (roots.length === 1) {
      const root = roots[0];
      const name = root && root.name;
      if (nodeIsPrimitive(root) && (name === null || typeof name === "undefined")) {
        return this.renderGrip(root, this.props);
      }
    }

    return Tree({
      className: classnames({
        inline,
        nowrap: disableWrap,
        "object-inspector": true
      }),
      autoExpandAll,
      autoExpandDepth,
      disabledFocus,
      itemHeight,

      isExpanded: item => expandedPaths.has(this.getKey(item)),
      isExpandable: item => nodeIsPrimitive(item) === false,
      focused: focusedItem,

      getRoots: this.getRoots,
      getParent,
      getChildren: this.getChildren,
      getKey: this.getKey,

      onExpand: item => this.setExpanded(item, true),
      onCollapse: item => this.setExpanded(item, false),
      onFocus: this.focusItem,

      renderItem: this.renderTreeItem
    });
  }
}

ObjectInspector.displayName = "ObjectInspector";

ObjectInspector.propTypes = {
  autoExpandAll: PropTypes.bool,
  autoExpandDepth: PropTypes.number,
  disabledFocus: PropTypes.bool,
  disableWrap: PropTypes.bool,
  inline: PropTypes.bool,
  roots: PropTypes.array,
  itemHeight: PropTypes.number,
  mode: PropTypes.oneOf(Object.values(MODE)),
  createObjectClient: PropTypes.func.isRequired,
  onFocus: PropTypes.func,
  onDoubleClick: PropTypes.func,
  onLabelClick: PropTypes.func
};

module.exports = ObjectInspector;

/***/ }),
/* 1586 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _classnames = __webpack_require__(175);

var _classnames2 = _interopRequireDefault(_classnames);

var _BracketArrow = __webpack_require__(1587);

var _BracketArrow2 = _interopRequireDefault(_BracketArrow);

__webpack_require__(1327);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

class Popover extends _react.Component {
  constructor(props) {
    super(props);

    this.onMouseLeave = e => {
      const { onMouseLeave } = this.props;
      if (/^(bracket-arrow|gap)$/.test(e.currentTarget.className)) {
        return;
      }

      onMouseLeave();
    };

    this.state = {
      left: 0,
      top: 0,
      targetMid: 0,
      orientation: "up"
    };
  }

  componentDidMount() {
    const { type } = this.props;
    const { left, top, orientation, targetMid } = type == "popover" ? this.getPopoverCoords() : this.getTooltipCoords();

    // eslint-disable-next-line react/no-did-mount-set-state
    this.setState({ left, top, orientation, targetMid });
  }

  calculateLeft(target, editor, popover) {
    const estimatedLeft = target.left;
    const estimatedRight = estimatedLeft + popover.width;
    const isOverflowingRight = estimatedRight > editor.right;
    if (isOverflowingRight) {
      const adjustedLeft = editor.right - popover.width - 8;
      return adjustedLeft;
    }
    return estimatedLeft;
  }

  calculateVerticalOrientation(target, editor, popover) {
    const estimatedBottom = target.bottom + popover.height;

    return estimatedBottom > editor.bottom ? "up" : "down";
  }

  getPopoverCoords() {
    if (this.$popover && this.props.editorRef) {
      const popover = this.$popover;
      const editor = this.props.editorRef;
      const popoverRect = popover.getBoundingClientRect();
      const editorRect = editor.getBoundingClientRect();
      const targetRect = this.props.targetPosition;
      const popoverLeft = this.calculateLeft(targetRect, editorRect, popoverRect);
      const orientation = this.calculateVerticalOrientation(targetRect, editorRect, popoverRect);
      const top = orientation == "down" ? targetRect.bottom : targetRect.top - popoverRect.height;

      const targetMid = targetRect.left - popoverLeft + targetRect.width / 2 - 8;

      return { left: popoverLeft, top, orientation, targetMid };
    }
    return { left: 0, top: 0, orientation: "down", targetMid: 0 };
  }

  getTooltipCoords() {
    if (this.$tooltip && this.props.editorRef) {
      const tooltip = this.$tooltip;
      const editor = this.props.editorRef;
      const tooltipRect = tooltip.getBoundingClientRect();
      const editorRect = editor.getBoundingClientRect();
      const targetRect = this.props.targetPosition;
      const left = this.calculateLeft(targetRect, editorRect, tooltipRect);
      const top = targetRect.top - tooltipRect.height;

      return { left, top, orientation: "up", targetMid: 0 };
    }
    return { left: 0, top: 0, orientation: "up", targetMid: 0 };
  }

  getChildren() {
    const { children } = this.props;
    const { orientation } = this.state;
    const gap = _react2.default.createElement("div", { className: "gap", key: "gap" });
    return orientation === "up" ? [children, gap] : [gap, children];
  }

  getPopoverArrow(orientation, left) {
    const arrowOrientation = orientation === "up" ? "down" : "up";

    const arrowProp = arrowOrientation === "up" ? "top" : "bottom";
    const arrowPropValue = arrowOrientation === "up" ? -7 : 5;

    const arrowProps = {
      orientation: arrowOrientation,
      left,
      [arrowProp]: arrowPropValue
    };

    return _react2.default.createElement(_BracketArrow2.default, arrowProps);
  }

  renderPopover() {
    const { top, left, orientation, targetMid } = this.state;
    const arrow = this.getPopoverArrow(orientation, targetMid);

    return _react2.default.createElement(
      "div",
      {
        className: (0, _classnames2.default)("popover", { up: orientation === "up" }),
        onMouseLeave: this.onMouseLeave,
        style: { top, left },
        ref: c => this.$popover = c
      },
      arrow,
      this.getChildren()
    );
  }

  renderTooltip() {
    const { onMouseLeave } = this.props;
    const { top, left } = this.state;

    return _react2.default.createElement(
      "div",
      {
        className: "tooltip",
        onMouseLeave: onMouseLeave,
        style: { top, left },
        ref: c => this.$tooltip = c
      },
      this.getChildren()
    );
  }

  render() {
    const { type } = this.props;

    if (type === "tooltip") {
      return this.renderTooltip();
    }

    return this.renderPopover();
  }
}

Popover.defaultProps = {
  onMouseLeave: () => {},
  type: "popover"
};
exports.default = Popover;

/***/ }),
/* 1587 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _classnames = __webpack_require__(175);

var _classnames2 = _interopRequireDefault(_classnames);

__webpack_require__(1326);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

const BracketArrow = ({
  orientation,
  left,
  top,
  bottom
}) => {
  return _react2.default.createElement("div", {
    className: (0, _classnames2.default)("bracket-arrow", orientation || "up"),
    style: { left, top, bottom }
  });
}; /* This Source Code Form is subject to the terms of the Mozilla Public
    * License, v. 2.0. If a copy of the MPL was not distributed with this
    * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

exports.default = BracketArrow;

/***/ }),
/* 1588 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _reactRedux = __webpack_require__(1189);

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _Breakpoint = __webpack_require__(1589);

var _Breakpoint2 = _interopRequireDefault(_Breakpoint);

var _selectors = __webpack_require__(1352);

var _breakpoint = __webpack_require__(1364);

var _source = __webpack_require__(1356);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

class Breakpoints extends _react.Component {
  shouldComponentUpdate(nextProps) {
    if (nextProps.selectedSource && !(0, _source.isLoaded)(nextProps.selectedSource)) {
      return false;
    }

    return true;
  }

  render() {
    const { breakpoints, selectedSource, editor } = this.props;

    if (!selectedSource || !breakpoints || selectedSource.get("isBlackBoxed")) {
      return null;
    }

    return _react2.default.createElement(
      "div",
      null,
      breakpoints.valueSeq().map(bp => {
        return _react2.default.createElement(_Breakpoint2.default, {
          key: (0, _breakpoint.makeLocationId)(bp.location),
          breakpoint: bp,
          selectedSource: selectedSource,
          editor: editor
        });
      })
    );
  }
}

exports.default = (0, _reactRedux.connect)(state => ({
  breakpoints: (0, _selectors.getVisibleBreakpoints)(state),
  selectedSource: (0, _selectors.getSelectedSource)(state)
}))(Breakpoints);

/***/ }),
/* 1589 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _reactDom = __webpack_require__(4);

var _reactDom2 = _interopRequireDefault(_reactDom);

var _classnames = __webpack_require__(175);

var _classnames2 = _interopRequireDefault(_classnames);

var _Svg = __webpack_require__(1359);

var _Svg2 = _interopRequireDefault(_Svg);

var _editor = __webpack_require__(1358);

var _prefs = __webpack_require__(226);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 breakpointSvg = document.createElement("div");
_reactDom2.default.render(_react2.default.createElement(_Svg2.default, { name: "breakpoint" }), breakpointSvg);

function makeMarker(isDisabled) {
  const bp = breakpointSvg.cloneNode(true);
  bp.className = (0, _classnames2.default)("editor new-breakpoint", {
    "breakpoint-disabled": isDisabled,
    "folding-enabled": _prefs.features.codeFolding
  });

  return bp;
}

class Breakpoint extends _react.Component {

  constructor() {
    super();
    this.addBreakpoint = this.addBreakpoint.bind(this);
  }

  addBreakpoint() {
    const { breakpoint, editor, selectedSource } = this.props;

    // Hidden Breakpoints are never rendered on the client
    if (breakpoint.hidden) {
      return;
    }

    // NOTE: we need to wait for the breakpoint to be loaded
    // to get the generated location
    if (!selectedSource || breakpoint.loading) {
      return;
    }

    const sourceId = selectedSource.get("id");
    const line = (0, _editor.toEditorLine)(sourceId, breakpoint.location.line);

    editor.codeMirror.setGutterMarker(line, "breakpoints", makeMarker(breakpoint.disabled));

    editor.codeMirror.addLineClass(line, "line", "new-breakpoint");
    if (breakpoint.condition) {
      editor.codeMirror.addLineClass(line, "line", "has-condition");
    } else {
      editor.codeMirror.removeLineClass(line, "line", "has-condition");
    }
  }

  shouldComponentUpdate(nextProps) {
    const { editor, breakpoint, selectedSource } = this.props;
    return editor !== nextProps.editor || breakpoint.disabled !== nextProps.breakpoint.disabled || breakpoint.hidden !== nextProps.breakpoint.hidden || breakpoint.condition !== nextProps.breakpoint.condition || breakpoint.loading !== nextProps.breakpoint.loading || selectedSource !== nextProps.selectedSource;
  }

  componentDidMount() {
    this.addBreakpoint();
  }

  componentDidUpdate() {
    this.addBreakpoint();
  }

  componentWillUnmount() {
    const { editor, breakpoint, selectedSource } = this.props;

    if (!selectedSource) {
      return;
    }

    if (breakpoint.loading) {
      return;
    }

    const sourceId = selectedSource.get("id");
    const doc = (0, _editor.getDocument)(sourceId);
    if (!doc) {
      return;
    }

    const line = (0, _editor.toEditorLine)(sourceId, breakpoint.location.line);

    // NOTE: when we upgrade codemirror we can use `doc.setGutterMarker`
    if (doc.setGutterMarker) {
      doc.setGutterMarker(line, "breakpoints", null);
    } else {
      editor.codeMirror.setGutterMarker(line, "breakpoints", null);
    }

    doc.removeLineClass(line, "line", "new-breakpoint");
    doc.removeLineClass(line, "line", "has-condition");
  }

  render() {
    return null;
  }
}

exports.default = Breakpoint;

/***/ }),
/* 1590 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _react = __webpack_require__(0);

const markerEl = document.createElement("div"); /* This Source Code Form is subject to the terms of the Mozilla Public
                                                 * License, v. 2.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 makeMarker() {
  const marker = markerEl.cloneNode(true);
  marker.className = "editor hit-marker";
  return marker;
}

class HitMarker extends _react.Component {
  addMarker() {
    const hitData = this.props.hitData;
    const line = hitData.line - 1;

    this.props.editor.setGutterMarker(line, "hit-markers", makeMarker());

    this.props.editor.addLineClass(line, "line", "hit-marker");
  }

  shouldComponentUpdate(nextProps) {
    return this.props.editor !== nextProps.editor || this.props.hitData !== nextProps.hitData;
  }

  componentDidMount() {
    this.addMarker();
  }

  componentDidUpdate() {
    this.addMarker();
  }

  componentWillUnmount() {
    const hitData = this.props.hitData;
    const line = hitData.line - 1;

    this.props.editor.setGutterMarker(line, "hit-markers", null);
    this.props.editor.removeLineClass(line, "line", "hit-marker");
  }

  render() {
    return null;
  }
}

exports.default = HitMarker;

/***/ }),
/* 1591 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

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; }; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                                                                                                                                                                                   * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                                                                                                                                                                                                                                   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _reactRedux = __webpack_require__(1189);

var _lodash = __webpack_require__(2);

var _CallSite = __webpack_require__(1592);

var _CallSite2 = _interopRequireDefault(_CallSite);

var _selectors = __webpack_require__(1352);

var _editor = __webpack_require__(1358);

var _wasm = __webpack_require__(1401);

var _actions = __webpack_require__(1354);

var _actions2 = _interopRequireDefault(_actions);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function getCallSiteAtLocation(callSites, location) {
  return callSites.find(callSite => (0, _lodash.isEqualWith)(callSite.location, location, (cloc, loc) => {
    return loc.line === cloc.start.line && loc.column >= cloc.start.column && loc.column <= cloc.end.column;
  }));
}

class CallSites extends _react.Component {

  constructor(props) {
    super(props);
    this.onKeyDown = this.onKeyDown.bind(this);
    this.onKeyUp = this.onKeyUp.bind(this);

    this.state = {
      showCallSites: false
    };
  }

  componentDidMount() {
    const { editor } = this.props;
    const codeMirrorWrapper = editor.codeMirror.getWrapperElement();

    codeMirrorWrapper.addEventListener("click", e => this.onTokenClick(e));
    document.body.addEventListener("keydown", this.onKeyDown);
    document.body.addEventListener("keyup", this.onKeyUp);
  }

  componentWillUnmount() {
    const { editor } = this.props;
    const codeMirrorWrapper = editor.codeMirror.getWrapperElement();

    codeMirrorWrapper.removeEventListener("click", e => this.onTokenClick(e));
    document.body.removeEventListener("keydown", this.onKeyDown);
    document.body.removeEventListener("keyup", this.onKeyUp);
  }

  onKeyUp(e) {
    if (e.key === "Alt") {
      e.preventDefault();
      this.setState({ showCallSites: false });
    }
  }

  onKeyDown(e) {
    if (e.key === "Alt") {
      e.preventDefault();
      this.setState({ showCallSites: true });
    }
  }

  onTokenClick(e) {
    const { target } = e;
    const { editor, selectedLocation } = this.props;

    if (!e.altKey && !target.classList.contains("call-site-bp") || !target.classList.contains("call-site") && !target.classList.contains("call-site-bp")) {
      return;
    }

    const { sourceId } = selectedLocation;
    const { line, column } = (0, _editor.getTokenLocation)(editor.codeMirror, target);

    this.toggleBreakpoint(line, (0, _wasm.isWasm)(sourceId) ? undefined : column);
  }

  toggleBreakpoint(line, column = undefined) {
    const {
      selectedSource,
      selectedLocation,
      addBreakpoint,
      removeBreakpoint,
      callSites
    } = this.props;

    const callSite = getCallSiteAtLocation(callSites, { line, column });

    if (!callSite) {
      return;
    }

    const bp = callSite.breakpoint;

    if (bp && bp.loading || !selectedLocation || !selectedSource) {
      return;
    }

    const { sourceId } = selectedLocation;

    if (bp) {
      // NOTE: it's possible the breakpoint has slid to a column
      column = column || bp.location.column;
      removeBreakpoint({
        sourceId: sourceId,
        line: line,
        column
      });
    } else {
      addBreakpoint({
        sourceId: sourceId,
        sourceUrl: selectedSource.get("url"),
        line: line,
        column: column
      });
    }
  }

  render() {
    const { editor, callSites, selectedSource } = this.props;
    const { showCallSites } = this.state;
    let sites;
    if (!callSites) {
      return null;
    }

    editor.codeMirror.operation(() => {
      const childCallSites = callSites.map((callSite, index) => {
        const props = {
          key: index,
          callSite,
          editor,
          source: selectedSource,
          breakpoint: callSite.breakpoint,
          showCallSite: showCallSites
        };
        return _react2.default.createElement(_CallSite2.default, props);
      });
      sites = _react2.default.createElement(
        "div",
        null,
        childCallSites
      );
    });
    return sites;
  }
}

function getCallSites(symbols, breakpoints) {
  if (!symbols || !symbols.callExpressions) {
    return;
  }

  const callSites = symbols.callExpressions;

  // NOTE: we create a breakpoint map keyed on location
  // to speed up the lookups. Hopefully we'll fix the
  // inconsistency with column offsets so that we can expect
  // a breakpoint to be added at the beginning of a call expression.
  const bpLocationMap = (0, _lodash.keyBy)(breakpoints.valueSeq().toJS(), ({ location }) => locationKey(location));

  function locationKey({ line, column }) {
    return `${line}/${column}`;
  }

  function findBreakpoint(callSite) {
    const { location: { start, end } } = callSite;

    const breakpointId = (0, _lodash.range)(start.column - 1, end.column).map(column => locationKey({ line: start.line, column })).find(key => bpLocationMap[key]);

    if (breakpointId) {
      return bpLocationMap[breakpointId];
    }
  }

  return callSites.filter(({ location }) => location.start.line === location.end.line).map(callSite => _extends({}, callSite, { breakpoint: findBreakpoint(callSite) }));
}

const { addBreakpoint, removeBreakpoint } = _actions2.default;

exports.default = (0, _reactRedux.connect)(state => {
  const selectedLocation = (0, _selectors.getSelectedLocation)(state);
  const selectedSource = (0, _selectors.getSelectedSource)(state);
  const sourceId = selectedLocation && selectedLocation.sourceId;
  const source = selectedSource && selectedSource.toJS();

  const symbols = (0, _selectors.getSymbols)(state, source);
  const breakpoints = (0, _selectors.getBreakpointsForSource)(state, sourceId);

  return {
    selectedLocation,
    selectedSource,
    callSites: getCallSites(symbols, breakpoints),
    breakpoints: breakpoints
  };
}, {
  addBreakpoint,
  removeBreakpoint
})(CallSites);

/***/ }),
/* 1592 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _react = __webpack_require__(0);

var _editor = __webpack_require__(1358);

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

__webpack_require__(1329);

class CallSite extends _react.Component {

  constructor() {
    super();

    this.addCallSite = nextProps => {
      const { editor, callSite, breakpoint, source } = nextProps || this.props;
      const className = !breakpoint ? "call-site" : "call-site-bp";
      const sourceId = source.get("id");
      const editorRange = (0, _editor.toEditorRange)(sourceId, callSite.location);
      this.marker = (0, _editor.markText)(editor, className, editorRange);
    };

    this.clearCallSite = () => {
      if (this.marker) {
        this.marker.clear();
        this.marker = null;
      }
    };

    this.marker = undefined;
  }

  shouldComponentUpdate(nextProps) {
    return this.props.editor !== nextProps.editor;
  }

  componentDidMount() {
    const { breakpoint, showCallSite } = this.props;

    if (!breakpoint && !showCallSite) {
      return;
    }

    this.addCallSite();
  }

  componentWillReceiveProps(nextProps) {
    const { breakpoint, showCallSite } = this.props;

    if (nextProps.breakpoint !== breakpoint) {
      if (this.marker) {
        this.clearCallSite();
      }
      if (nextProps.showCallSite) {
        this.addCallSite(nextProps);
      }
    }

    if (nextProps.showCallSite !== showCallSite) {
      if (nextProps.showCallSite) {
        if (!this.marker) {
          this.addCallSite();
        }
      } else if (!nextProps.breakpoint) {
        this.clearCallSite();
      }
    }
  }

  componentWillUnmount() {
    if (!this.marker) {
      return;
    }
    this.marker.clear();
  }

  render() {
    return null;
  }
}
exports.default = CallSite;

/***/ }),
/* 1593 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.DebugLine = undefined;

var _react = __webpack_require__(0);

var _editor = __webpack_require__(1358);

var _source = __webpack_require__(1356);

var _pause = __webpack_require__(2419);

var _indentation = __webpack_require__(1438);

var _reactRedux = __webpack_require__(1189);

var _selectors = __webpack_require__(1352);

function isDocumentReady(selectedSource, selectedFrame) {
  return selectedFrame && (0, _source.isLoaded)(selectedSource) && (0, _editor.hasDocument)(selectedFrame.location.sourceId);
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

class DebugLine extends _react.Component {

  componentDidUpdate(prevProps) {
    const { why, selectedFrame, selectedSource } = this.props;
    this.setDebugLine(why, selectedFrame, selectedSource);
  }

  componentWillUpdate() {
    const { why, selectedFrame, selectedSource } = this.props;
    this.clearDebugLine(selectedFrame, selectedSource, why);
  }

  componentDidMount() {
    const { why, selectedFrame, selectedSource } = this.props;
    this.setDebugLine(why, selectedFrame, selectedSource);
  }

  setDebugLine(why, selectedFrame, selectedSource) {
    if (!isDocumentReady(selectedSource, selectedFrame)) {
      return;
    }
    const sourceId = selectedFrame.location.sourceId;
    const doc = (0, _editor.getDocument)(sourceId);

    let { line, column } = (0, _editor.toEditorPosition)(selectedFrame.location);
    const { markTextClass, lineClass } = this.getTextClasses(why);
    doc.addLineClass(line, "line", lineClass);

    const lineText = doc.getLine(line);
    column = Math.max(column, (0, _indentation.getIndentation)(lineText));

    this.debugExpression = doc.markText({ ch: column, line }, { ch: null, line }, { className: markTextClass });
  }

  clearDebugLine(selectedFrame, selectedSource, why) {
    if (!isDocumentReady(selectedSource, selectedFrame)) {
      return;
    }

    if (this.debugExpression) {
      this.debugExpression.clear();
    }

    const sourceId = selectedFrame.location.sourceId;
    const { line } = (0, _editor.toEditorPosition)(selectedFrame.location);
    const doc = (0, _editor.getDocument)(sourceId);
    const { lineClass } = this.getTextClasses(why);
    doc.removeLineClass(line, "line", lineClass);
  }

  getTextClasses(why) {
    if ((0, _pause.isException)(why)) {
      return {
        markTextClass: "debug-expression-error",
        lineClass: "new-debug-line-error"
      };
    }

    return { markTextClass: "debug-expression", lineClass: "new-debug-line" };
  }

  render() {
    return null;
  }
}

exports.DebugLine = DebugLine;
exports.default = (0, _reactRedux.connect)(state => {
  return {
    selectedFrame: (0, _selectors.getVisibleSelectedFrame)(state),
    selectedSource: (0, _selectors.getSelectedSource)(state),
    why: (0, _selectors.getPauseReason)(state)
  };
})(DebugLine);

/***/ }),
/* 1594 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _reactRedux = __webpack_require__(1189);

var _react = __webpack_require__(0);

var _selectors = __webpack_require__(1352);

var _editor = __webpack_require__(1358);

__webpack_require__(1330);

class EmptyLines extends _react.Component {

  componentDidMount() {
    this.disableEmptyLines();
  }

  componentDidUpdate() {
    this.disableEmptyLines();
  }

  componentWillUnmount() {
    const { emptyLines, selectedSource, editor } = this.props;

    if (!emptyLines) {
      return;
    }
    editor.codeMirror.operation(() => {
      emptyLines.forEach(emptyLine => {
        const line = (0, _editor.toEditorLine)(selectedSource.get("id"), emptyLine);
        editor.codeMirror.removeLineClass(line, "line", "empty-line");
      });
    });
  }

  disableEmptyLines() {
    const { emptyLines, selectedSource, editor } = this.props;

    if (!emptyLines) {
      return;
    }
    editor.codeMirror.operation(() => {
      emptyLines.forEach(emptyLine => {
        const line = (0, _editor.toEditorLine)(selectedSource.get("id"), emptyLine);
        editor.codeMirror.addLineClass(line, "line", "empty-line");
      });
    });
  }

  render() {
    return null;
  }
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

exports.default = (0, _reactRedux.connect)(state => {
  const selectedSource = (0, _selectors.getSelectedSource)(state);
  return {
    selectedSource,
    emptyLines: selectedSource ? (0, _selectors.getEmptyLines)(state, selectedSource.toJS()) : []
  };
})(EmptyLines);

/***/ }),
/* 1595 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

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; }; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                                                                                                                                                                                   * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                                                                                                                                                                                                                                   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

exports.gutterMenu = gutterMenu;

var _react = __webpack_require__(0);

var _devtoolsContextmenu = __webpack_require__(1413);

var _redux = __webpack_require__(3);

var _reactRedux = __webpack_require__(1189);

var _editor = __webpack_require__(1358);

var _selectors = __webpack_require__(1352);

var _actions = __webpack_require__(1354);

var _actions2 = _interopRequireDefault(_actions);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

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 gutterMenu({
  breakpoint,
  line,
  event,
  isPaused,
  toggleBreakpoint,
  openConditionalPanel,
  toggleDisabledBreakpoint,
  isCbPanelOpen,
  closeConditionalPanel,
  continueToHere
}) {
  event.stopPropagation();
  event.preventDefault();

  const gutterItems = {
    addBreakpoint: {
      id: "node-menu-add-breakpoint",
      label: L10N.getStr("editor.addBreakpoint")
    },
    addConditional: {
      id: "node-menu-add-conditional-breakpoint",
      label: L10N.getStr("editor.addConditionalBreakpoint")
    },
    removeBreakpoint: {
      id: "node-menu-remove-breakpoint",
      label: L10N.getStr("editor.removeBreakpoint")
    },
    editConditional: {
      id: "node-menu-edit-conditional-breakpoint",
      label: L10N.getStr("editor.editBreakpoint")
    },
    enableBreakpoint: {
      id: "node-menu-enable-breakpoint",
      label: L10N.getStr("editor.enableBreakpoint")
    },
    disableBreakpoint: {
      id: "node-menu-disable-breakpoint",
      label: L10N.getStr("editor.disableBreakpoint")
    },
    continueToHere: {
      id: "node-menu-continue-to-here",
      label: L10N.getStr("editor.continueToHere.label")
    }
  };

  const toggleBreakpointItem = _extends({
    accesskey: L10N.getStr("shortcuts.toggleBreakpoint.accesskey"),
    disabled: false,
    click: () => {
      toggleBreakpoint(line);
      if (isCbPanelOpen) {
        closeConditionalPanel();
      }
    }
  }, breakpoint ? gutterItems.removeBreakpoint : gutterItems.addBreakpoint);

  const conditionalBreakpoint = _extends({
    accesskey: L10N.getStr("editor.addConditionalBreakpoint.accesskey"),
    disabled: false,
    click: () => openConditionalPanel(line)
  }, breakpoint && breakpoint.condition ? gutterItems.editConditional : gutterItems.addConditional);

  const items = [toggleBreakpointItem, conditionalBreakpoint];

  if (isPaused) {
    const continueToHereItem = _extends({
      accesskey: L10N.getStr("editor.continueToHere.accesskey"),
      disabled: false,
      click: () => continueToHere(line)
    }, gutterItems.continueToHere);
    items.push(continueToHereItem);
  }

  if (breakpoint) {
    const disableBreakpoint = _extends({
      accesskey: L10N.getStr("editor.disableBreakpoint.accesskey"),
      disabled: false,
      click: () => toggleDisabledBreakpoint(line)
    }, breakpoint.disabled ? gutterItems.enableBreakpoint : gutterItems.disableBreakpoint);
    items.push(disableBreakpoint);
  }

  (0, _devtoolsContextmenu.showMenu)(event, items);
}

class GutterContextMenuComponent extends _react.PureComponent {

  constructor() {
    super();
  }

  shouldComponentUpdate(nextProps) {
    return nextProps.contextMenu.type === "Gutter";
  }

  componentWillUpdate(nextProps) {
    // clear the context menu since it is open
    this.props.setContextMenu("", null);
    return this.showMenu(nextProps);
  }

  showMenu(nextProps) {
    const { contextMenu } = nextProps,
          props = _objectWithoutProperties(nextProps, ["contextMenu"]);
    const { event } = contextMenu;
    const sourceId = props.selectedSource ? props.selectedSource.get("id") : "";
    const line = (0, _editor.lineAtHeight)(props.editor, sourceId, event);
    const breakpoint = nextProps.breakpoints.find(bp => bp.location.line === line);

    if (props.emptyLines.includes(line)) {
      return;
    }

    gutterMenu(_extends({ event, sourceId, line, breakpoint }, props));
  }

  render() {
    return null;
  }
}

exports.default = (0, _reactRedux.connect)(state => {
  const selectedSource = (0, _selectors.getSelectedSource)(state);
  return {
    selectedLocation: (0, _selectors.getSelectedLocation)(state),
    selectedSource: selectedSource,
    breakpoints: (0, _selectors.getVisibleBreakpoints)(state),
    isPaused: (0, _selectors.isPaused)(state),
    contextMenu: (0, _selectors.getContextMenu)(state),
    emptyLines: selectedSource ? (0, _selectors.getEmptyLines)(state, selectedSource.toJS()) : []
  };
}, dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(GutterContextMenuComponent);

/***/ }),
/* 1596 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _react = __webpack_require__(0);

var _reactRedux = __webpack_require__(1189);

var _devtoolsLaunchpad = __webpack_require__(1362);

var _devtoolsSourceMap = __webpack_require__(1360);

var _clipboard = __webpack_require__(1388);

var _function = __webpack_require__(1597);

var _astBreakpointLocation = __webpack_require__(1416);

var _editor = __webpack_require__(1358);

var _source = __webpack_require__(1356);

var _selectors = __webpack_require__(1352);

var _actions = __webpack_require__(1354);

var _actions2 = _interopRequireDefault(_actions);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

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; } /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                                                                                                                                              * License, v. 2.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 getMenuItems(event, {
  addExpression,
  editor,
  evaluateInConsole,
  flashLineRange,
  getFunctionLocation,
  getFunctionText,
  hasPrettyPrint,
  jumpToMappedLocation,
  onGutterContextMenu,
  selectedLocation,
  selectedSource,
  showSource,
  toggleBlackBox
}) {
  // variables
  const hasSourceMap = !!selectedSource.get("sourceMapURL");
  const isOriginal = (0, _devtoolsSourceMap.isOriginalId)(selectedLocation.sourceId);
  const isPrettyPrinted = (0, _source.isPretty)(selectedSource);
  const isPrettified = isPrettyPrinted || hasPrettyPrint;
  const isMapped = isOriginal || hasSourceMap;
  const { line } = editor.codeMirror.coordsChar({
    left: event.clientX,
    top: event.clientY
  });
  const selectionText = editor.codeMirror.getSelection().trim();
  const sourceLocation = (0, _editor.getSourceLocationFromMouseEvent)(editor, selectedLocation, event);
  const isTextSelected = editor.codeMirror.somethingSelected();

  // localizations
  const blackboxKey = L10N.getStr("sourceFooter.blackbox.accesskey");
  const blackboxLabel = L10N.getStr("sourceFooter.blackbox");
  const unblackboxLabel = L10N.getStr("sourceFooter.unblackbox");
  const toggleBlackBoxLabel = selectedSource.get("isBlackBoxed") ? unblackboxLabel : blackboxLabel;
  const copyFunctionKey = L10N.getStr("copyFunction.accesskey");
  const copyFunctionLabel = L10N.getStr("copyFunction.label");
  const copySourceKey = L10N.getStr("copySource.accesskey");
  const copySourceLabel = L10N.getStr("copySource");
  const copySourceUri2Key = L10N.getStr("copySourceUri2.accesskey");
  const copySourceUri2Label = L10N.getStr("copySourceUri2");
  const evaluateInConsoleLabel = L10N.getStr("evaluateInConsole.label");
  const jumpToMappedLocKey = L10N.getStr("editor.jumpToMappedLocation1.accesskey");
  const jumpToMappedLocLabel = L10N.getFormatStr("editor.jumpToMappedLocation1", isOriginal ? L10N.getStr("generated") : L10N.getStr("original"));
  const revealInTreeKey = L10N.getStr("sourceTabs.revealInTree.accesskey");
  const revealInTreeLabel = L10N.getStr("sourceTabs.revealInTree");
  const watchExpressionKey = L10N.getStr("expressions.accesskey");
  const watchExpressionLabel = L10N.getStr("expressions.label");

  // menu items
  const copySourceItem = {
    id: "node-menu-copy-source",
    label: copySourceLabel,
    accesskey: copySourceKey,
    disabled: selectionText.length === 0,
    click: () => (0, _clipboard.copyToTheClipboard)(selectionText)
  };

  const copySourceUri2Item = {
    id: "node-menu-copy-source-url",
    label: copySourceUri2Label,
    accesskey: copySourceUri2Key,
    disabled: false,
    click: () => (0, _clipboard.copyToTheClipboard)((0, _source.getRawSourceURL)(selectedSource.get("url")))
  };

  const sourceId = selectedSource.get("id");
  const sourceLine = (0, _editor.toSourceLine)(sourceId, line);

  const functionText = getFunctionText(sourceLine);
  const copyFunctionItem = {
    id: "node-menu-copy-function",
    label: copyFunctionLabel,
    accesskey: copyFunctionKey,
    disabled: !functionText,
    click: () => {
      const { location: { start, end } } = getFunctionLocation(sourceLine);
      flashLineRange({
        start: start.line,
        end: end.line,
        sourceId: selectedLocation.sourceId
      });
      return (0, _clipboard.copyToTheClipboard)(functionText);
    }
  };

  const jumpToMappedLocationItem = {
    id: "node-menu-jump",
    label: jumpToMappedLocLabel,
    accesskey: jumpToMappedLocKey,
    disabled: !isMapped && !isPrettified,
    click: () => jumpToMappedLocation(sourceLocation)
  };

  const showSourceMenuItem = {
    id: "node-menu-show-source",
    label: revealInTreeLabel,
    accesskey: revealInTreeKey,
    disabled: false,
    click: () => showSource(sourceId)
  };

  const blackBoxMenuItem = {
    id: "node-menu-blackbox",
    label: toggleBlackBoxLabel,
    accesskey: blackboxKey,
    disabled: isOriginal || isPrettyPrinted || hasSourceMap,
    click: () => toggleBlackBox(selectedSource.toJS())
  };

  const watchExpressionItem = {
    id: "node-menu-add-watch-expression",
    label: watchExpressionLabel,
    accesskey: watchExpressionKey,
    click: () => addExpression(editor.codeMirror.getSelection())
  };

  const evaluateInConsoleItem = {
    id: "node-menu-evaluate-in-console",
    label: evaluateInConsoleLabel,
    click: () => evaluateInConsole(selectionText)
  };

  // construct menu
  const menuItems = [copySourceItem, copySourceUri2Item, copyFunctionItem, { type: "separator" }, jumpToMappedLocationItem, showSourceMenuItem, blackBoxMenuItem];

  // conditionally added items
  // TODO: Find a new way to only add this for mapped sources?
  if (isTextSelected) {
    menuItems.push(watchExpressionItem, evaluateInConsoleItem);
  }

  return menuItems;
}

class EditorMenu extends _react.PureComponent {

  constructor() {
    super();
  }

  shouldComponentUpdate(nextProps) {
    return nextProps.contextMenu.type === "Editor";
  }

  componentWillUpdate(nextProps) {
    // clear the context menu since it is open
    this.props.setContextMenu("", null);
    return this.showMenu(nextProps);
  }

  showMenu(nextProps) {
    const { contextMenu } = nextProps,
          options = _objectWithoutProperties(nextProps, ["contextMenu"]);
    const { event } = contextMenu;
    (0, _devtoolsLaunchpad.showMenu)(event, getMenuItems(event, options));
  }

  render() {
    return null;
  }
}

const {
  addExpression,
  evaluateInConsole,
  flashLineRange,
  jumpToMappedLocation,
  setContextMenu,
  showSource,
  toggleBlackBox
} = _actions2.default;

exports.default = (0, _reactRedux.connect)(state => {
  const selectedSource = (0, _selectors.getSelectedSource)(state);
  return {
    selectedLocation: (0, _selectors.getSelectedLocation)(state),
    selectedSource,
    hasPrettyPrint: !!(0, _selectors.getPrettySource)(state, selectedSource.get("id")),
    contextMenu: (0, _selectors.getContextMenu)(state),
    getFunctionText: line => (0, _function.findFunctionText)(line, selectedSource.toJS(), (0, _selectors.getSymbols)(state, selectedSource.toJS())),
    getFunctionLocation: line => (0, _astBreakpointLocation.findClosestScope)((0, _selectors.getSymbols)(state, selectedSource.toJS()).functions, {
      line,
      column: Infinity
    })
  };
}, {
  addExpression,
  evaluateInConsole,
  flashLineRange,
  jumpToMappedLocation,
  setContextMenu,
  showSource,
  toggleBlackBox
})(EditorMenu);

/***/ }),
/* 1597 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.findFunctionText = findFunctionText;

var _astBreakpointLocation = __webpack_require__(1416);

var _indentation = __webpack_require__(1438);

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 findFunctionText(line, source, symbols) {
  const func = (0, _astBreakpointLocation.findClosestScope)(symbols.functions, { line, column: Infinity });
  if (!func) {
    return null;
  }

  const { location: { start, end } } = func;
  const lines = source.text.split("\n");
  const firstLine = lines[start.line - 1].slice(start.column);
  const lastLine = lines[end.line - 1].slice(0, end.column);
  const middle = lines.slice(start.line, end.line - 1);
  const functionText = [firstLine, ...middle, lastLine].join("\n");
  const indentedFunctionText = (0, _indentation.correctIndentation)(functionText);

  return indentedFunctionText;
}

/***/ }),
/* 1598 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.ConditionalPanel = undefined;

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _reactDom = __webpack_require__(4);

var _reactDom2 = _interopRequireDefault(_reactDom);

var _reactRedux = __webpack_require__(1189);

__webpack_require__(1331);

var _editor = __webpack_require__(1358);

var _actions = __webpack_require__(1354);

var _actions2 = _interopRequireDefault(_actions);

var _selectors = __webpack_require__(1352);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

class ConditionalPanel extends _react.PureComponent {

  constructor() {
    super();

    this.saveAndClose = () => {
      if (this.input) {
        this.setBreakpoint(this.input.value);
      }

      this.props.closeConditionalPanel();
    };

    this.onKey = e => {
      if (e.key === "Enter") {
        this.saveAndClose();
      } else if (e.key === "Escape") {
        this.props.closeConditionalPanel();
      }
    };

    this.repositionOnScroll = () => {
      if (this.panelNode && this.scrollParent) {
        const { scrollLeft } = this.scrollParent;
        this.panelNode.style.transform = `translateX(${scrollLeft}px)`;
      }
    };

    this.cbPanel = null;
  }

  keepFocusOnInput() {
    if (this.input) {
      this.input.focus();
    }
  }

  setBreakpoint(condition) {
    const { selectedLocation, line } = this.props;
    const sourceId = selectedLocation ? selectedLocation.sourceId : "";
    const location = { sourceId, line };
    return this.props.setBreakpointCondition(location, { condition });
  }

  clearConditionalPanel() {
    if (this.cbPanel) {
      this.cbPanel.clear();
      this.cbPanel = null;
    }
    if (this.scrollParent) {
      this.scrollParent.removeEventListener("scroll", this.repositionOnScroll);
    }
  }

  componentWillMount() {
    if (this.props.line) {
      return this.renderToWidget(this.props);
    }
  }

  componentWillUpdate(nextProps) {
    if (nextProps.line) {
      return this.renderToWidget(nextProps);
    }
    return this.clearConditionalPanel();
  }

  componentWillUnmount() {
    // This is called if CodeMirror is re-initializing itself before the
    // user closes the conditional panel. Clear the widget, and re-render it
    // as soon as this component gets remounted
    return this.clearConditionalPanel();
  }

  renderToWidget(props) {
    if (this.cbPanel) {
      if (this.props.line && this.props.line == props.line) {
        return props.closeConditionalPanel();
      }
      this.clearConditionalPanel();
    }

    const { selectedLocation, line, editor } = props;
    const sourceId = selectedLocation ? selectedLocation.sourceId : "";

    const editorLine = (0, _editor.toEditorLine)(sourceId, line);
    this.cbPanel = editor.codeMirror.addLineWidget(editorLine, this.renderConditionalPanel(props), {
      coverGutter: true,
      noHScroll: false
    });
    if (this.input) {
      let parent = this.input.parentNode;
      while (parent) {
        if (parent instanceof HTMLElement && parent.classList.contains("CodeMirror-scroll")) {
          this.scrollParent = parent;
          break;
        }
        parent = parent.parentNode;
      }

      if (this.scrollParent) {
        this.scrollParent.addEventListener("scroll", this.repositionOnScroll);
        this.repositionOnScroll();
      }

      this.input.focus();
    }
  }

  renderConditionalPanel(props) {
    const { breakpoint } = props;
    const condition = breakpoint ? breakpoint.condition : "";
    const panel = document.createElement("div");
    _reactDom2.default.render(_react2.default.createElement(
      "div",
      {
        className: "conditional-breakpoint-panel",
        onClick: () => this.keepFocusOnInput(),
        onBlur: this.props.closeConditionalPanel,
        ref: node => this.panelNode = node
      },
      _react2.default.createElement(
        "div",
        { className: "prompt" },
        "\xBB"
      ),
      _react2.default.createElement("input", {
        defaultValue: condition,
        placeholder: L10N.getStr("editor.conditionalPanel.placeholder"),
        onKeyDown: this.onKey,
        ref: input => this.input = input
      })
    ), panel);
    return panel;
  }

  render() {
    return null;
  }
}

exports.ConditionalPanel = ConditionalPanel; /* This Source Code Form is subject to the terms of the Mozilla Public
                                              * License, v. 2.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 {
  setBreakpointCondition,
  openConditionalPanel,
  closeConditionalPanel
} = _actions2.default;

exports.default = (0, _reactRedux.connect)(state => {
  const line = (0, _selectors.getConditionalPanelLine)(state);
  const selectedLocation = (0, _selectors.getSelectedLocation)(state);
  return {
    selectedLocation,
    breakpoint: (0, _selectors.getBreakpointForLine)(state, selectedLocation.sourceId, line),
    line
  };
}, {
  setBreakpointCondition,
  openConditionalPanel,
  closeConditionalPanel
})(ConditionalPanel);

/***/ }),
/* 1599 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _propTypes = __webpack_require__(20);

var _propTypes2 = _interopRequireDefault(_propTypes);

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _reactRedux = __webpack_require__(1189);

var _redux = __webpack_require__(3);

var _actions = __webpack_require__(1354);

var _actions2 = _interopRequireDefault(_actions);

var _selectors = __webpack_require__(1352);

var _Svg = __webpack_require__(1359);

var _Svg2 = _interopRequireDefault(_Svg);

var _prefs = __webpack_require__(226);

var _Breakpoints = __webpack_require__(1600);

var _Breakpoints2 = _interopRequireDefault(_Breakpoints);

var _Expressions = __webpack_require__(1601);

var _Expressions2 = _interopRequireDefault(_Expressions);

var _devtoolsSplitter = __webpack_require__(1440);

var _devtoolsSplitter2 = _interopRequireDefault(_devtoolsSplitter);

var _Frames = __webpack_require__(1602);

var _Frames2 = _interopRequireDefault(_Frames);

var _EventListeners = __webpack_require__(1605);

var _EventListeners2 = _interopRequireDefault(_EventListeners);

var _Workers = __webpack_require__(1606);

var _Workers2 = _interopRequireDefault(_Workers);

var _Accordion = __webpack_require__(1607);

var _Accordion2 = _interopRequireDefault(_Accordion);

var _CommandBar = __webpack_require__(1608);

var _CommandBar2 = _interopRequireDefault(_CommandBar);

var _UtilsBar = __webpack_require__(1609);

var _UtilsBar2 = _interopRequireDefault(_UtilsBar);

var _BreakpointsDropdown = __webpack_require__(1790);

var _BreakpointsDropdown2 = _interopRequireDefault(_BreakpointsDropdown);

var _ChromeScopes = __webpack_require__(1610);

var _ChromeScopes2 = _interopRequireDefault(_ChromeScopes);

var _Scopes2 = __webpack_require__(1611);

var _Scopes3 = _interopRequireDefault(_Scopes2);

__webpack_require__(1342);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 Scopes = _prefs.features.chromeScopes ? _ChromeScopes2.default : _Scopes3.default;

function debugBtn(onClick, type, className, tooltip) {
  return _react2.default.createElement(
    "button",
    {
      onClick: onClick,
      className: `${type} ${className}`,
      key: type,
      title: tooltip
    },
    _react2.default.createElement(_Svg2.default, { name: type, title: tooltip, "aria-label": tooltip })
  );
}

class SecondaryPanes extends _react.Component {
  renderBreakpointsToggle() {
    const {
      toggleAllBreakpoints,
      breakpoints,
      breakpointsDisabled,
      breakpointsLoading
    } = this.props;
    const isIndeterminate = !breakpointsDisabled && breakpoints.some(x => x.disabled);

    if (breakpoints.size == 0) {
      return null;
    }

    const inputProps = {
      type: "checkbox",
      "aria-label": breakpointsDisabled ? L10N.getStr("breakpoints.enable") : L10N.getStr("breakpoints.disable"),
      className: "breakpoints-toggle",
      disabled: breakpointsLoading,
      key: "breakpoints-toggle",
      onChange: e => {
        e.stopPropagation();
        toggleAllBreakpoints(!breakpointsDisabled);
      },
      onClick: e => e.stopPropagation(),
      checked: !breakpointsDisabled && !isIndeterminate,
      ref: input => {
        if (input) {
          input.indeterminate = isIndeterminate;
        }
      },
      title: breakpointsDisabled ? L10N.getStr("breakpoints.enable") : L10N.getStr("breakpoints.disable")
    };

    return _react2.default.createElement("input", inputProps);
  }

  watchExpressionHeaderButtons() {
    return [debugBtn(evt => {
      evt.stopPropagation();
      this.props.evaluateExpressions();
    }, "refresh", "refresh", L10N.getStr("watchExpressions.refreshButton"))];
  }

  getScopeItem() {
    return {
      header: L10N.getStr("scopes.header"),
      className: "scopes-pane",
      component: _react2.default.createElement(Scopes, null),
      opened: _prefs.prefs.scopesVisible,
      onToggle: opened => {
        _prefs.prefs.scopesVisible = opened;
      }
    };
  }

  getWatchItem() {
    return {
      header: L10N.getStr("watchExpressions.header"),
      className: "watch-expressions-pane",
      buttons: this.watchExpressionHeaderButtons(),
      component: _react2.default.createElement(_Expressions2.default, null),
      opened: _prefs.prefs.expressionsVisible,
      onToggle: opened => {
        _prefs.prefs.expressionsVisible = opened;
      }
    };
  }

  getCallStackItem() {
    return {
      header: L10N.getStr("callStack.header"),
      className: "call-stack-pane",
      component: _react2.default.createElement(_Frames2.default, null),
      opened: _prefs.prefs.callStackVisible,
      onToggle: opened => {
        _prefs.prefs.callStackVisible = opened;
      }
    };
  }

  getWorkersItem() {
    return {
      header: L10N.getStr("workersHeader"),
      className: "workers-pane",
      component: _react2.default.createElement(_Workers2.default, null),
      opened: _prefs.prefs.workersVisible,
      onToggle: opened => {
        _prefs.prefs.workersVisible = opened;
      }
    };
  }

  getBreakpointsItem() {
    return {
      header: L10N.getStr("breakpoints.header"),
      className: "breakpoints-pane",
      buttons: [this.breakpointDropdown(), this.renderBreakpointsToggle()],
      component: _react2.default.createElement(_Breakpoints2.default, null),
      opened: _prefs.prefs.breakpointsVisible,
      onToggle: opened => {
        _prefs.prefs.breakpointsVisible = opened;
      }
    };
  }

  breakpointDropdown() {
    if (!_prefs.features.breakpointsDropdown) {
      return;
    }

    const {
      breakOnNext,
      pauseOnExceptions,
      shouldPauseOnExceptions,
      shouldIgnoreCaughtExceptions,
      isWaitingOnBreak
    } = this.props;

    return (0, _BreakpointsDropdown2.default)(breakOnNext, pauseOnExceptions, shouldPauseOnExceptions, shouldIgnoreCaughtExceptions, isWaitingOnBreak);
  }

  getStartItems() {
    const { workers } = this.props;

    const items = [];
    if (this.props.horizontal) {
      if (_prefs.features.workers && workers.size > 0) {
        items.push(this.getWorkersItem());
      }

      items.push(this.getWatchItem());
    }

    items.push(this.getBreakpointsItem());

    if (this.props.isPaused) {
      items.push(this.getCallStackItem());
      if (this.props.horizontal) {
        items.push(this.getScopeItem());
      }
    }

    if (_prefs.features.eventListeners) {
      items.push({
        header: L10N.getStr("eventListenersHeader"),
        className: "event-listeners-pane",
        component: _react2.default.createElement(_EventListeners2.default, null)
      });
    }

    return items.filter(item => item);
  }

  renderHorizontalLayout() {
    return _react2.default.createElement(_Accordion2.default, { items: this.getItems() });
  }

  getEndItems() {
    const { workers } = this.props;

    let items = [];

    if (this.props.horizontal) {
      return [];
    }

    if (_prefs.features.workers && workers.size > 0) {
      items.push(this.getWorkersItem());
    }

    items.push(this.getWatchItem());

    if (this.props.isPaused) {
      items = [...items, this.getScopeItem()];
    }

    return items;
  }

  getItems() {
    return [...this.getStartItems(), ...this.getEndItems()];
  }

  renderVerticalLayout() {
    return _react2.default.createElement(_devtoolsSplitter2.default, {
      initialSize: "300px",
      minSize: 10,
      maxSize: "50%",
      splitterSize: 1,
      startPanel: _react2.default.createElement(_Accordion2.default, { items: this.getStartItems() }),
      endPanel: _react2.default.createElement(_Accordion2.default, { items: this.getEndItems() })
    });
  }

  renderUtilsBar() {
    if (!_prefs.features.shortcuts) {
      return;
    }

    return _react2.default.createElement(_UtilsBar2.default, {
      horizontal: this.props.horizontal,
      toggleShortcutsModal: this.props.toggleShortcutsModal
    });
  }

  render() {
    return _react2.default.createElement(
      "div",
      { className: "secondary-panes-wrapper" },
      _react2.default.createElement(_CommandBar2.default, { horizontal: this.props.horizontal }),
      _react2.default.createElement(
        "div",
        { className: "secondary-panes" },
        this.props.horizontal ? this.renderHorizontalLayout() : this.renderVerticalLayout()
      ),
      this.renderUtilsBar()
    );
  }
}

SecondaryPanes.contextTypes = {
  shortcuts: _propTypes2.default.object
};

exports.default = (0, _reactRedux.connect)(state => ({
  isPaused: (0, _selectors.isPaused)(state),
  breakpoints: (0, _selectors.getBreakpoints)(state),
  breakpointsDisabled: (0, _selectors.getBreakpointsDisabled)(state),
  breakpointsLoading: (0, _selectors.getBreakpointsLoading)(state),
  isWaitingOnBreak: (0, _selectors.getIsWaitingOnBreak)(state),
  shouldPauseOnExceptions: (0, _selectors.getShouldPauseOnExceptions)(state),
  shouldIgnoreCaughtExceptions: (0, _selectors.getShouldIgnoreCaughtExceptions)(state),
  workers: (0, _selectors.getWorkers)(state)
}), dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(SecondaryPanes);

/***/ }),
/* 1600 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

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; }; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                                                                                                                                                                                   * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                                                                                                                                                                                                                                   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _redux = __webpack_require__(3);

var _reactRedux = __webpack_require__(1189);

var _immutable = __webpack_require__(146);

var I = _interopRequireWildcard(_immutable);

var _classnames = __webpack_require__(175);

var _classnames2 = _interopRequireDefault(_classnames);

var _reselect = __webpack_require__(993);

var _lodash = __webpack_require__(2);

var _actions = __webpack_require__(1354);

var _actions2 = _interopRequireDefault(_actions);

var _Close = __webpack_require__(1374);

var _Close2 = _interopRequireDefault(_Close);

var _utils = __webpack_require__(1366);

var _prefs = __webpack_require__(226);

var _source = __webpack_require__(1356);

var _selectors = __webpack_require__(1352);

var _pause = __webpack_require__(2419);

var _breakpoint = __webpack_require__(1364);

var _BreakpointsContextMenu = __webpack_require__(1805);

var _BreakpointsContextMenu2 = _interopRequireDefault(_BreakpointsContextMenu);

__webpack_require__(1334);

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function isCurrentlyPausedAtBreakpoint(frame, why, breakpoint) {
  if (!frame || !(0, _pause.isInterrupted)(why)) {
    return false;
  }

  const bpId = (0, _breakpoint.makeLocationId)(breakpoint.location);
  const pausedId = (0, _breakpoint.makeLocationId)(frame.location);
  return bpId === pausedId;
}

function getBreakpointFilename(source) {
  return source && source.toJS ? (0, _source.getFilename)(source.toJS()) : "";
}

function renderSourceLocation(source, line, column) {
  const filename = getBreakpointFilename(source);
  const isWasm = source && source.get("isWasm");
  const columnVal = _prefs.features.columnBreakpoints && column ? `:${column}` : "";
  const bpLocation = isWasm ? `0x${line.toString(16).toUpperCase()}` : `${line}${columnVal}`;

  if (!filename) {
    return null;
  }

  return _react2.default.createElement(
    "div",
    { className: "location" },
    `${(0, _utils.endTruncateStr)(filename, 30)}: ${bpLocation}`
  );
}

class Breakpoints extends _react.PureComponent {
  shouldComponentUpdate(nextProps, nextState) {
    const { breakpoints } = this.props;
    return breakpoints !== nextProps.breakpoints;
  }

  handleCheckbox(breakpoint) {
    if (breakpoint.loading) {
      return;
    }

    if (breakpoint.disabled) {
      this.props.enableBreakpoint(breakpoint.location);
    } else {
      this.props.disableBreakpoint(breakpoint.location);
    }
  }

  selectBreakpoint(breakpoint) {
    this.props.selectLocation(breakpoint.location);
  }

  removeBreakpoint(event, breakpoint) {
    event.stopPropagation();
    this.props.removeBreakpoint(breakpoint.location);
  }

  renderBreakpoint(breakpoint) {
    const snippet = breakpoint.text || "";
    const locationId = breakpoint.locationId;
    const line = breakpoint.location.line;
    const column = breakpoint.location.column;
    const isCurrentlyPaused = breakpoint.isCurrentlyPaused;
    const isDisabled = breakpoint.disabled;
    const isConditional = !!breakpoint.condition;
    const isHidden = breakpoint.hidden;

    if (isHidden) {
      return;
    }

    return _react2.default.createElement(
      "div",
      {
        className: (0, _classnames2.default)({
          breakpoint,
          paused: isCurrentlyPaused,
          disabled: isDisabled,
          "is-conditional": isConditional
        }),
        key: locationId,
        onClick: () => this.selectBreakpoint(breakpoint),
        onContextMenu: e => (0, _BreakpointsContextMenu2.default)(_extends({}, this.props, { breakpoint, contextMenuEvent: e }))
      },
      _react2.default.createElement("input", {
        type: "checkbox",
        className: "breakpoint-checkbox",
        checked: !isDisabled,
        onChange: () => this.handleCheckbox(breakpoint),
        onClick: ev => ev.stopPropagation()
      }),
      _react2.default.createElement(
        "label",
        { className: "breakpoint-label", title: breakpoint.text },
        renderSourceLocation(breakpoint.location.source, line, column)
      ),
      _react2.default.createElement(
        "div",
        { className: "breakpoint-snippet" },
        snippet
      ),
      _react2.default.createElement(_Close2.default, {
        handleClick: ev => this.removeBreakpoint(ev, breakpoint),
        tooltip: L10N.getStr("breakpoints.removeBreakpointTooltip")
      })
    );
  }

  render() {
    const { breakpoints } = this.props;
    const children = breakpoints.size === 0 ? _react2.default.createElement(
      "div",
      { className: "pane-info" },
      L10N.getStr("breakpoints.none")
    ) : (0, _lodash.sortBy)([...breakpoints.valueSeq()], [bp => getBreakpointFilename(bp.location.source), bp => bp.location.line]).map(bp => this.renderBreakpoint(bp));

    return _react2.default.createElement(
      "div",
      { className: "pane breakpoints-list" },
      children
    );
  }
}

function updateLocation(sources, frame, why, bp) {
  const source = (0, _selectors.getSourceInSources)(sources, bp.location.sourceId);
  const isCurrentlyPaused = isCurrentlyPausedAtBreakpoint(frame, why, bp);
  const locationId = (0, _breakpoint.makeLocationId)(bp.location);

  const location = _extends({}, bp.location, { source });
  const localBP = _extends({}, bp, { location, locationId, isCurrentlyPaused });

  return localBP;
}

const _getBreakpoints = (0, _reselect.createSelector)(_selectors.getBreakpoints, _selectors.getSources, _selectors.getTopFrame, _selectors.getPauseReason, (breakpoints, sources, frame, why) => breakpoints.map(bp => updateLocation(sources, frame, why, bp)).filter(bp => bp.location.source && !bp.location.source.get("isBlackBoxed")));

exports.default = (0, _reactRedux.connect)((state, props) => ({ breakpoints: _getBreakpoints(state) }), dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(Breakpoints);

/***/ }),
/* 1601 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _reactRedux = __webpack_require__(1189);

var _classnames = __webpack_require__(175);

var _classnames2 = _interopRequireDefault(_classnames);

var _devtoolsReps = __webpack_require__(1408);

var _actions = __webpack_require__(1354);

var _actions2 = _interopRequireDefault(_actions);

var _selectors = __webpack_require__(1352);

var _expressions = __webpack_require__(1437);

var _firefox = __webpack_require__(1500);

var _Close = __webpack_require__(1374);

var _Close2 = _interopRequireDefault(_Close);

__webpack_require__(1335);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

class Expressions extends _react.PureComponent {

  constructor(props) {
    super(props);

    this.clear = () => {
      this.setState(() => {
        this.props.clearExpressionError();
        return { editing: false, editIndex: -1, inputValue: "" };
      });
    };

    this.handleChange = e => {
      this.setState({ inputValue: e.target.value });
    };

    this.handleKeyDown = e => {
      if (e.key === "Escape") {
        this.clear();
      }
    };

    this.handleExistingSubmit = async (e, expression) => {
      e.preventDefault();
      e.stopPropagation();
      this.props.updateExpression(this.state.inputValue, expression);
    };

    this.handleNewSubmit = async e => {
      e.preventDefault();
      e.stopPropagation();
      this.props.addExpression(this.state.inputValue);
      this.setState({ editing: false, editIndex: -1, inputValue: "" });
    };

    this.state = { editing: false, editIndex: -1, inputValue: "" };
    this.renderExpression = this.renderExpression.bind(this);
  }

  componentDidMount() {
    const { expressions, evaluateExpressions } = this.props;
    if (expressions.size > 0) {
      evaluateExpressions();
    }
  }

  componentWillReceiveProps(nextProps) {
    if (this.state.editing && !nextProps.expressionError) {
      this.clear();
    }
  }

  shouldComponentUpdate(nextProps, nextState) {
    const { editing, inputValue } = this.state;
    const { expressions, expressionError } = this.props;
    return expressions !== nextProps.expressions || expressionError !== nextProps.expressionError || editing !== nextState.editing || inputValue !== nextState.inputValue;
  }

  componentDidUpdate(prevProps, prevState) {
    if (this._input && !prevState.editing) {
      const input = this._input;
      input.setSelectionRange(0, input.value.length);
      input.focus();
    }
  }

  editExpression(expression, index, { depth }) {
    if (depth > 0) {
      return;
    }
    this.setState({
      inputValue: expression.input,
      editing: true,
      editIndex: index
    });
  }

  deleteExpression(e, expression) {
    e.stopPropagation();
    const { deleteExpression } = this.props;
    deleteExpression(expression);
  }

  renderExpression(expression, index) {
    const { expressionError, openLink } = this.props;
    const { editing, editIndex } = this.state;
    const { input, updating } = expression;
    const isEditingExpr = editing && editIndex === index;
    if (isEditingExpr || isEditingExpr && expressionError) {
      return this.renderExpressionEditInput(expression);
    }

    if (updating) {
      return;
    }

    const { value } = (0, _expressions.getValue)(expression);

    const root = {
      name: expression.input,
      path: input,
      contents: { value }
    };

    return _react2.default.createElement(
      "li",
      { className: "expression-container", key: input },
      _react2.default.createElement(
        "div",
        { className: "expression-content" },
        _react2.default.createElement(_devtoolsReps.ObjectInspector, {
          roots: [root],
          autoExpandDepth: 0,
          disableWrap: true,
          disabledFocus: true,
          onDoubleClick: (items, options) => this.editExpression(expression, index, options),
          openLink: openLink,
          createObjectClient: grip => (0, _firefox.createObjectClient)(grip)
        }),
        _react2.default.createElement(
          "div",
          { className: "expression-container__close-btn" },
          _react2.default.createElement(_Close2.default, {
            handleClick: e => this.deleteExpression(e, expression)
          })
        )
      )
    );
  }

  renderNewExpressionInput() {
    const { expressionError } = this.props;
    const { editing, inputValue } = this.state;
    const error = editing === false && expressionError === true;
    const placeholder = error ? L10N.getStr("expressions.errorMsg") : L10N.getStr("expressions.placeholder");
    return _react2.default.createElement(
      "li",
      { className: "expression-input-container" },
      _react2.default.createElement(
        "form",
        { className: "expression-input-form", onSubmit: this.handleNewSubmit },
        _react2.default.createElement("input", {
          className: (0, _classnames2.default)("input-expression", { error }),
          type: "text",
          placeholder: placeholder,
          onChange: this.handleChange,
          onBlur: this.clear,
          onKeyDown: this.handleKeyDown,
          value: !editing ? inputValue : ""
        }),
        _react2.default.createElement("input", { type: "submit", style: { display: "none" } })
      )
    );
  }

  renderExpressionEditInput(expression) {
    const { expressionError } = this.props;
    const { inputValue, editing } = this.state;
    const error = editing === true && expressionError === true;
    return _react2.default.createElement(
      "span",
      { className: "expression-input-container", key: expression.input },
      _react2.default.createElement(
        "form",
        {
          className: "expression-input-form",
          onSubmit: e => this.handleExistingSubmit(e, expression)
        },
        _react2.default.createElement("input", {
          className: (0, _classnames2.default)("input-expression", { error }),
          type: "text",
          onChange: this.handleChange,
          onBlur: this.clear,
          onKeyDown: this.handleKeyDown,
          value: editing ? inputValue : expression.input,
          ref: c => this._input = c
        }),
        _react2.default.createElement("input", { type: "submit", style: { display: "none" } })
      )
    );
  }

  render() {
    const { expressions } = this.props;
    return _react2.default.createElement(
      "ul",
      { className: "pane expressions-list" },
      expressions.map(this.renderExpression),
      this.renderNewExpressionInput()
    );
  }
}

exports.default = (0, _reactRedux.connect)(state => ({
  expressions: (0, _selectors.getExpressions)(state),
  expressionError: (0, _selectors.getExpressionError)(state)
}), _actions2.default)(Expressions);

/***/ }),
/* 1602 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _redux = __webpack_require__(3);

var _reactRedux = __webpack_require__(1189);

var _Frame = __webpack_require__(1453);

var _Frame2 = _interopRequireDefault(_Frame);

var _Group = __webpack_require__(1603);

var _Group2 = _interopRequireDefault(_Group);

var _WhyPaused = __webpack_require__(1604);

var _WhyPaused2 = _interopRequireDefault(_WhyPaused);

var _actions = __webpack_require__(1354);

var _actions2 = _interopRequireDefault(_actions);

var _frame = __webpack_require__(1380);

var _clipboard = __webpack_require__(1388);

var _selectors = __webpack_require__(1352);

__webpack_require__(1338);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

const NUM_FRAMES_SHOWN = 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/>. */

class Frames extends _react.Component {

  constructor(props) {
    super(props);

    this.state = {
      showAllFrames: false
    };

    this.toggleFramesDisplay = this.toggleFramesDisplay.bind(this);
    this.copyStackTrace = this.copyStackTrace.bind(this);
    this.toggleFrameworkGrouping = this.toggleFrameworkGrouping.bind(this);
  }

  shouldComponentUpdate(nextProps, nextState) {
    const { frames, selectedFrame, frameworkGroupingOn } = this.props;
    const { showAllFrames } = this.state;
    return frames !== nextProps.frames || selectedFrame !== nextProps.selectedFrame || showAllFrames !== nextState.showAllFrames || frameworkGroupingOn !== nextProps.frameworkGroupingOn;
  }

  toggleFramesDisplay() {
    this.setState({
      showAllFrames: !this.state.showAllFrames
    });
  }

  collapseFrames(frames) {
    const { frameworkGroupingOn } = this.props;
    if (!frameworkGroupingOn) {
      return frames;
    }

    return (0, _frame.collapseFrames)(frames);
  }

  truncateFrames(frames) {
    const numFramesToShow = this.state.showAllFrames ? frames.length : NUM_FRAMES_SHOWN;

    return frames.slice(0, numFramesToShow);
  }

  copyStackTrace() {
    const { frames } = this.props;
    const framesToCopy = frames.map(f => (0, _frame.formatCopyName)(f)).join("\n");
    (0, _clipboard.copyToTheClipboard)(framesToCopy);
  }

  toggleFrameworkGrouping() {
    const { toggleFrameworkGrouping, frameworkGroupingOn } = this.props;
    toggleFrameworkGrouping(!frameworkGroupingOn);
  }

  renderFrames(frames) {
    const {
      selectFrame,
      selectedFrame,
      toggleBlackBox,
      frameworkGroupingOn
    } = this.props;

    const framesOrGroups = this.truncateFrames(this.collapseFrames(frames));


    return _react2.default.createElement(
      "ul",
      null,
      framesOrGroups.map(frameOrGroup => frameOrGroup.id ? _react2.default.createElement(_Frame2.default, {
        frame: frameOrGroup,
        toggleFrameworkGrouping: this.toggleFrameworkGrouping,
        copyStackTrace: this.copyStackTrace,
        frameworkGroupingOn: frameworkGroupingOn,
        selectFrame: selectFrame,
        selectedFrame: selectedFrame,
        toggleBlackBox: toggleBlackBox,
        key: String(frameOrGroup.id)
      }) : _react2.default.createElement(_Group2.default, {
        group: frameOrGroup,
        toggleFrameworkGrouping: this.toggleFrameworkGrouping,
        copyStackTrace: this.copyStackTrace,
        frameworkGroupingOn: frameworkGroupingOn,
        selectFrame: selectFrame,
        selectedFrame: selectedFrame,
        toggleBlackBox: toggleBlackBox,
        key: frameOrGroup[0].id
      }))
    );
  }

  renderToggleButton(frames) {
    const buttonMessage = this.state.showAllFrames ? L10N.getStr("callStack.collapse") : L10N.getStr("callStack.expand");

    frames = this.collapseFrames(frames);
    if (frames.length <= NUM_FRAMES_SHOWN) {
      return null;
    }

    return _react2.default.createElement(
      "div",
      { className: "show-more-container" },
      _react2.default.createElement(
        "button",
        { className: "show-more", onClick: this.toggleFramesDisplay },
        buttonMessage
      )
    );
  }

  render() {
    const { frames, why } = this.props;

    if (!frames) {
      return _react2.default.createElement(
        "div",
        { className: "pane frames" },
        _react2.default.createElement(
          "div",
          { className: "pane-info empty" },
          L10N.getStr("callStack.notPaused")
        )
      );
    }

    return _react2.default.createElement(
      "div",
      { className: "pane frames" },
      this.renderFrames(frames),
      (0, _WhyPaused2.default)(why),
      this.renderToggleButton(frames)
    );
  }
}

exports.default = (0, _reactRedux.connect)(state => ({
  frames: (0, _selectors.getCallStackFrames)(state),
  why: (0, _selectors.getPauseReason)(state),
  frameworkGroupingOn: (0, _selectors.getFrameworkGroupingState)(state),
  selectedFrame: (0, _selectors.getSelectedFrame)(state),
  pause: (0, _selectors.isPaused)(state)
}), dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(Frames);

/***/ }),
/* 1603 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _classnames = __webpack_require__(175);

var _classnames2 = _interopRequireDefault(_classnames);

var _Svg = __webpack_require__(1359);

var _Svg2 = _interopRequireDefault(_Svg);

var _frame = __webpack_require__(1380);

var _FrameMenu = __webpack_require__(1454);

var _FrameMenu2 = _interopRequireDefault(_FrameMenu);

__webpack_require__(1336);

var _Frame = __webpack_require__(1453);

var _Frame2 = _interopRequireDefault(_Frame);

var _Badge = __webpack_require__(1704);

var _Badge2 = _interopRequireDefault(_Badge);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 FrameLocation({ frame }) {
  const library = (0, _frame.getLibraryFromUrl)(frame);
  if (!library) {
    return null;
  }

  return _react2.default.createElement(
    "div",
    { className: "location" },
    library,
    _react2.default.createElement(_Svg2.default, { name: library.toLowerCase(), className: "annotation-logo" })
  );
}

FrameLocation.displayName = "FrameLocation";

class Group extends _react.Component {

  constructor(...args) {
    super(...args);

    this.toggleFrames = () => {
      this.setState({ expanded: !this.state.expanded });
    };

    this.state = { expanded: false };
  }

  onContextMenu(event) {
    const {
      group,
      copyStackTrace,
      toggleFrameworkGrouping,
      toggleBlackBox,
      frameworkGroupingOn
    } = this.props;
    const frame = group[0];
    (0, _FrameMenu2.default)(frame, frameworkGroupingOn, { copyStackTrace, toggleFrameworkGrouping, toggleBlackBox }, event);
  }

  renderFrames() {
    const {
      group,
      selectFrame,
      selectedFrame,
      toggleFrameworkGrouping,
      frameworkGroupingOn,
      toggleBlackBox,
      copyStackTrace
    } = this.props;

    const { expanded } = this.state;
    if (!expanded) {
      return null;
    }

    return _react2.default.createElement(
      "div",
      { className: "frames-list" },
      group.map(frame => _react2.default.createElement(_Frame2.default, {
        copyStackTrace: copyStackTrace,
        frame: frame,
        frameworkGroupingOn: frameworkGroupingOn,
        hideLocation: true,
        key: frame.id,
        selectedFrame: selectedFrame,
        selectFrame: selectFrame,
        shouldMapDisplayName: false,
        toggleBlackBox: toggleBlackBox,
        toggleFrameworkGrouping: toggleFrameworkGrouping
      }))
    );
  }

  renderDescription() {
    const frame = this.props.group[0];
    const displayName = (0, _frame.formatDisplayName)(frame);
    return _react2.default.createElement(
      "li",
      {
        key: frame.id,
        className: (0, _classnames2.default)("group"),
        onClick: this.toggleFrames,
        tabIndex: 0
      },
      _react2.default.createElement(
        "div",
        { className: "d-flex align-items-center min-width-0" },
        _react2.default.createElement(
          "div",
          { className: "title" },
          displayName
        ),
        _react2.default.createElement(
          _Badge2.default,
          null,
          this.props.group.length
        )
      ),
      _react2.default.createElement(FrameLocation, { frame: frame })
    );
  }

  render() {
    const { expanded } = this.state;
    return _react2.default.createElement(
      "div",
      {
        className: (0, _classnames2.default)("frames-group", { expanded }),
        onContextMenu: e => this.onContextMenu(e)
      },
      this.renderDescription(),
      this.renderFrames()
    );
  }
}

exports.default = Group;
Group.displayName = "Group";

/***/ }),
/* 1604 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = renderWhyPaused;

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _pause = __webpack_require__(2419);

__webpack_require__(1337);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function renderExceptionSummary(exception) {
  if (typeof exception === "string") {
    return exception;
  }

  const preview = exception.preview;
  if (!preview) {
    return;
  }

  return `${preview.name}: ${preview.message}`;
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.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 renderMessage(why) {
  if (why.type == "exception" && why.exception) {
    return _react2.default.createElement(
      "div",
      { className: "message warning" },
      renderExceptionSummary(why.exception)
    );
  }

  if (typeof why.message == "string") {
    return _react2.default.createElement(
      "div",
      { className: "message" },
      why.message
    );
  }

  return null;
}

function renderWhyPaused(why) {
  const reason = (0, _pause.getPauseReason)(why);

  if (!reason) {
    return null;
  }

  return _react2.default.createElement(
    "div",
    { className: "pane why-paused" },
    _react2.default.createElement(
      "div",
      null,
      L10N.getStr(reason)
    ),
    renderMessage(why)
  );
}
renderWhyPaused.displayName = "whyPaused";

/***/ }),
/* 1605 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

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; }; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                                                                                                                                                                                   * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                                                                                                                                                                                                                                   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _redux = __webpack_require__(3);

var _reactRedux = __webpack_require__(1189);

var _actions = __webpack_require__(1354);

var _actions2 = _interopRequireDefault(_actions);

var _selectors = __webpack_require__(1352);

var _Close = __webpack_require__(1374);

var _Close2 = _interopRequireDefault(_Close);

__webpack_require__(1339);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

class EventListeners extends _react.Component {

  constructor(...args) {
    super(...args);

    this.renderListener = this.renderListener.bind(this);
  }

  renderListener({ type, selector, line, sourceId, breakpoint }) {
    const checked = breakpoint && !breakpoint.disabled;
    const location = { sourceId, line };

    return _react2.default.createElement(
      "div",
      {
        className: "listener",
        onClick: () => this.props.selectLocation({ sourceId, line }),
        key: `${type}.${selector}.${sourceId}.${line}`
      },
      _react2.default.createElement("input", {
        type: "checkbox",
        className: "listener-checkbox",
        checked: checked,
        onChange: () => this.handleCheckbox(breakpoint, location)
      }),
      _react2.default.createElement(
        "span",
        { className: "type" },
        type
      ),
      _react2.default.createElement(
        "span",
        { className: "selector" },
        selector
      ),
      breakpoint ? _react2.default.createElement(_Close2.default, {
        handleClick: ev => this.removeBreakpoint(ev, breakpoint)
      }) : ""
    );
  }

  handleCheckbox(breakpoint, location) {
    if (!breakpoint) {
      return this.props.addBreakpoint(location);
    }

    if (breakpoint.loading) {
      return;
    }

    if (breakpoint.disabled) {
      this.props.enableBreakpoint(breakpoint.location);
    } else {
      this.props.disableBreakpoint(breakpoint.location);
    }
  }

  removeBreakpoint(event, breakpoint) {
    event.stopPropagation();
    this.props.removeBreakpoint(breakpoint.location);
  }

  render() {
    const { listeners } = this.props;
    return _react2.default.createElement(
      "div",
      { className: "pane event-listeners" },
      listeners.map(this.renderListener)
    );
  }
}

exports.default = (0, _reactRedux.connect)(state => {
  const listeners = (0, _selectors.getEventListeners)(state).map(l => {
    return _extends({}, l, {
      breakpoint: (0, _selectors.getBreakpoint)(state, {
        sourceId: l.sourceId,
        line: l.line,
        column: null
      })
    });
  });

  return { listeners };
}, dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(EventListeners);

/***/ }),
/* 1606 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.Workers = undefined;

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _redux = __webpack_require__(3);

var _reactRedux = __webpack_require__(1189);

__webpack_require__(1340);

var _actions = __webpack_require__(1354);

var _actions2 = _interopRequireDefault(_actions);

var _selectors = __webpack_require__(1352);

var _path = __webpack_require__(1393);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

class Workers extends _react.PureComponent {

  renderWorkers(workers) {
    const { openWorkerToolbox } = this.props;
    return workers.map(worker => _react2.default.createElement(
      "div",
      {
        className: "worker",
        key: worker.actor,
        onClick: () => openWorkerToolbox(worker)
      },
      _react2.default.createElement("img", { className: "domain" }),
      (0, _path.basename)(worker.url)
    ));
  }

  renderNoWorkersPlaceholder() {
    return _react2.default.createElement(
      "div",
      { className: "pane-info" },
      L10N.getStr("noWorkersText")
    );
  }

  render() {
    const { workers } = this.props;
    return _react2.default.createElement(
      "div",
      { className: "pane workers-list" },
      workers && workers.size > 0 ? this.renderWorkers(workers) : this.renderNoWorkersPlaceholder()
    );
  }
}

exports.Workers = Workers; /* This Source Code Form is subject to the terms of the Mozilla Public
                            * License, v. 2.0. If a copy of the MPL was not distributed with this
                            * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

exports.default = (0, _reactRedux.connect)(state => {
  return { workers: (0, _selectors.getWorkers)(state) };
}, dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(Workers);

/***/ }),
/* 1607 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _Svg = __webpack_require__(1359);

var _Svg2 = _interopRequireDefault(_Svg);

__webpack_require__(1341);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

class Accordion extends _react.Component {
  constructor(props) {
    super(props);

    this.renderContainer = (item, i) => {
      const { opened } = item;

      return _react2.default.createElement(
        "div",
        { className: item.className, key: i },
        _react2.default.createElement(
          "div",
          { className: "_header", onClick: () => this.handleHeaderClick(i) },
          _react2.default.createElement(_Svg2.default, { name: "arrow", className: opened ? "expanded" : "" }),
          item.header,
          item.buttons ? _react2.default.createElement(
            "div",
            { className: "header-buttons" },
            item.buttons
          ) : null
        ),
        opened && _react2.default.createElement(
          "div",
          { className: "_content" },
          (0, _react.cloneElement)(item.component, item.componentProps || {})
        )
      );
    };

    this.state = {
      opened: props.items.map(item => item.opened),
      created: []
    };
  }

  handleHeaderClick(i) {
    const item = this.props.items[i];
    const opened = !item.opened;
    item.opened = opened;

    if (item.onToggle) {
      item.onToggle(opened);
    }

    // We force an update because otherwise the accordion
    // would not re-render
    this.forceUpdate();
  }

  render() {
    return _react2.default.createElement(
      "div",
      { className: "accordion" },
      this.props.items.map(this.renderContainer)
    );
  }
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

exports.default = Accordion;

/***/ }),
/* 1608 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _propTypes = __webpack_require__(20);

var _propTypes2 = _interopRequireDefault(_propTypes);

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _reactRedux = __webpack_require__(1189);

var _redux = __webpack_require__(3);

var _classnames = __webpack_require__(175);

var _classnames2 = _interopRequireDefault(_classnames);

var _prefs = __webpack_require__(226);

var _selectors = __webpack_require__(1352);

var _text = __webpack_require__(1387);

var _actions = __webpack_require__(1354);

var _actions2 = _interopRequireDefault(_actions);

var _CommandBarButton = __webpack_require__(1764);

var _CommandBarButton2 = _interopRequireDefault(_CommandBarButton);

__webpack_require__(1295);

var _devtoolsModules = __webpack_require__(1376);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/* -*- indent-tabs-mode: nil; js-indent-level: 2; js-indent-level: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

const { appinfo } = _devtoolsModules.Services;

const isMacOS = appinfo.OS === "Darwin";

const COMMANDS = ["resume", "stepOver", "stepIn", "stepOut"];

const KEYS = {
  WINNT: {
    resume: "F8",
    pause: "F8",
    stepOver: "F10",
    stepIn: "F11",
    stepOut: "Shift+F11"
  },
  Darwin: {
    resume: "Cmd+\\",
    pause: "Cmd+\\",
    stepOver: "Cmd+'",
    stepIn: "Cmd+;",
    stepOut: "Cmd+Shift+:",
    stepOutDisplay: "Cmd+Shift+;"
  },
  Linux: {
    resume: "F8",
    pause: "F8",
    stepOver: "F10",
    stepIn: "Ctrl+F11",
    stepOut: "Ctrl+Shift+F11"
  }
};

function getKey(action) {
  return getKeyForOS(appinfo.OS, action);
}

function getKeyForOS(os, action) {
  const osActions = KEYS[os] || KEYS.Linux;
  return osActions[action];
}

function formatKey(action) {
  const key = getKey(`${action}Display`) || getKey(action);
  if (isMacOS) {
    const winKey = getKeyForOS("WINNT", `${action}Display`) || getKeyForOS("WINNT", action);
    // display both Windows type and Mac specific keys
    return (0, _text.formatKeyShortcut)([key, winKey].join(" "));
  }
  return (0, _text.formatKeyShortcut)(key);
}

function debugBtn(onClick, type, className, tooltip, disabled = false, ariaPressed = false) {
  return _react2.default.createElement(
    _CommandBarButton2.default,
    {
      className: (0, _classnames2.default)(type, className),
      disabled: disabled,
      key: type,
      onClick: onClick,
      pressed: ariaPressed,
      title: tooltip
    },
    _react2.default.createElement("img", { className: type })
  );
}

class CommandBar extends _react.Component {
  componentWillUnmount() {
    const shortcuts = this.context.shortcuts;
    COMMANDS.forEach(action => shortcuts.off(getKey(action)));
    if (isMacOS) {
      COMMANDS.forEach(action => shortcuts.off(getKeyForOS("WINNT", action)));
    }
  }

  componentDidMount() {
    const shortcuts = this.context.shortcuts;

    COMMANDS.forEach(action => shortcuts.on(getKey(action), (_, e) => this.handleEvent(e, action)));

    if (isMacOS) {
      // The Mac supports both the Windows Function keys
      // as well as the Mac non-Function keys
      COMMANDS.forEach(action => shortcuts.on(getKeyForOS("WINNT", action), (_, e) => this.handleEvent(e, action)));
    }
  }

  handleEvent(e, action) {
    e.preventDefault();
    e.stopPropagation();

    this.props[action]();
  }

  setHistory(offset) {
    this.props.timeTravelTo(this.props.historyPosition + offset);
  }

  renderStepButtons() {
    const { isPaused } = this.props;
    const className = isPaused ? "active" : "disabled";
    const isDisabled = !isPaused;

    if (!isPaused && _prefs.features.removeCommandBarOptions) {
      return;
    }

    return [debugBtn(this.props.stepOver, "stepOver", className, L10N.getFormatStr("stepOverTooltip", formatKey("stepOver")), isDisabled), debugBtn(this.props.stepIn, "stepIn", className, L10N.getFormatStr("stepInTooltip", formatKey("stepIn")), isDisabled), debugBtn(this.props.stepOut, "stepOut", className, L10N.getFormatStr("stepOutTooltip", formatKey("stepOut")), isDisabled)];
  }

  resume() {
    this.props.resume();
    this.props.clearHistory();
  }

  renderPauseButton() {
    const { isPaused, breakOnNext, isWaitingOnBreak } = this.props;

    if (isPaused) {
      return debugBtn(() => this.resume(), "resume", "active", L10N.getFormatStr("resumeButtonTooltip", formatKey("resume")));
    }

    if (_prefs.features.removeCommandBarOptions && !this.props.canRewind) {
      return;
    }

    if (isWaitingOnBreak) {
      return debugBtn(null, "pause", "disabled", L10N.getStr("pausePendingButtonTooltip"), true);
    }

    return debugBtn(breakOnNext, "pause", "active", L10N.getFormatStr("pauseButtonTooltip", formatKey("pause")));
  }

  /*
   * The pause on exception button has three states in this order:
   *  1. don't pause on exceptions      [false, false]
   *  2. pause on uncaught exceptions   [true, true]
   *  3. pause on all exceptions        [true, false]
  */
  renderPauseOnExceptions() {
    if (_prefs.features.breakpointsDropdown) {
      return;
    }

    const {
      shouldPauseOnExceptions,
      shouldIgnoreCaughtExceptions,
      pauseOnExceptions
    } = this.props;

    if (!shouldPauseOnExceptions && !shouldIgnoreCaughtExceptions) {
      return debugBtn(() => pauseOnExceptions(true, true), "pause-exceptions", "enabled", L10N.getStr("ignoreExceptions"), false, false);
    }

    if (shouldPauseOnExceptions && shouldIgnoreCaughtExceptions) {
      return debugBtn(() => pauseOnExceptions(true, false), "pause-exceptions", "uncaught enabled", L10N.getStr("pauseOnUncaughtExceptions"), false, true);
    }

    return debugBtn(() => pauseOnExceptions(false, false), "pause-exceptions", "all enabled", L10N.getStr("pauseOnExceptions"), false, true);
  }

  renderRewindButton() {
    if (!this.props.canRewind || !this.props.isPaused) {
      return;
    }

    return debugBtn(this.props.rewind, "rewind", "active", "Rewind Execution");
  }

  renderReverseStepButtons() {
    if (!this.props.canRewind || !this.props.isPaused) {
      return;
    }

    return [debugBtn(this.props.reverseStepOver, "reverseStepOver", "active", "Reverse step over"), debugBtn(this.props.reverseStepIn, "reverseStepIn", "active", "Reverse step in"), debugBtn(this.props.reverseStepOut, "reverseStepOut", "active", "Reverse step out")];
  }

  replayPreviousButton() {
    const historyLength = this.props.history.length;
    if (!historyLength || !_prefs.features.replay) {
      return null;
    }
    const enabled = this.props.historyPosition === 0;
    const activeClass = enabled ? "replay-inactive" : "";
    return debugBtn(() => this.setHistory(-1), `replay-previous ${activeClass}`, "active", L10N.getStr("replayPrevious"), enabled);
  }

  replayNextButton() {
    const historyLength = this.props.history.length;
    if (!historyLength || !_prefs.features.replay) {
      return null;
    }
    const enabled = this.props.historyPosition + 1 === historyLength;
    const activeClass = enabled ? "replay-inactive" : "";
    return debugBtn(() => this.setHistory(1), `replay-next ${activeClass}`, "active", L10N.getStr("replayNext"), enabled);
  }

  renderStepPosition() {
    if (!this.props.history.length || !_prefs.features.replay) {
      return null;
    }
    const position = this.props.historyPosition + 1;
    const total = this.props.history.length;
    const activePrev = position > 1 ? "replay-active" : "replay-inactive";
    const activeNext = position < total ? "replay-active" : "replay-inactive";
    return _react2.default.createElement(
      "div",
      { className: "step-position" },
      _react2.default.createElement(
        "span",
        { className: activePrev },
        position
      ),
      _react2.default.createElement(
        "span",
        null,
        " | "
      ),
      _react2.default.createElement(
        "span",
        { className: activeNext },
        total
      )
    );
  }

  render() {
    return _react2.default.createElement(
      "div",
      {
        className: (0, _classnames2.default)("command-bar", {
          vertical: !this.props.horizontal
        })
      },
      this.renderPauseButton(),
      this.renderRewindButton(),
      this.renderStepButtons(),
      this.renderPauseOnExceptions(),
      this.renderReverseStepButtons(),
      _react2.default.createElement("div", { className: "filler" }),
      this.replayPreviousButton(),
      this.renderStepPosition(),
      this.replayNextButton()
    );
  }
}

CommandBar.contextTypes = {
  shortcuts: _propTypes2.default.object
};

exports.default = (0, _reactRedux.connect)(state => {
  return {
    isPaused: (0, _selectors.isPaused)(state),
    history: (0, _selectors.getHistory)(state),
    historyPosition: (0, _selectors.getHistoryPosition)(state),
    isWaitingOnBreak: (0, _selectors.getIsWaitingOnBreak)(state),
    shouldPauseOnExceptions: (0, _selectors.getShouldPauseOnExceptions)(state),
    shouldIgnoreCaughtExceptions: (0, _selectors.getShouldIgnoreCaughtExceptions)(state),
    canRewind: (0, _selectors.getCanRewind)(state)
  };
}, dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(CommandBar);

/***/ }),
/* 1609 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

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; }; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                                                                                                                                                                                   * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                                                                                                                                                                                                                                   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _classnames = __webpack_require__(175);

var _classnames2 = _interopRequireDefault(_classnames);

__webpack_require__(1295);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function debugBtn(onClick, type, className, tooltip, disabled = false) {
  const props = {
    onClick,
    key: type,
    "aria-label": tooltip,
    title: tooltip,
    disabled
  };

  return _react2.default.createElement(
    "button",
    _extends({ className: (0, _classnames2.default)(type, className) }, props),
    "?"
  );
}

class UtilsBar extends _react.Component {
  renderUtilButtons() {
    return [debugBtn(this.props.toggleShortcutsModal, "shortcut", "active", L10N.getStr("shortcuts.buttonName"), false)];
  }

  render() {
    return _react2.default.createElement(
      "div",
      {
        className: (0, _classnames2.default)("command-bar bottom", {
          vertical: !this.props.horizontal
        })
      },
      this.renderUtilButtons()
    );
  }
}

exports.default = UtilsBar;

/***/ }),
/* 1610 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _redux = __webpack_require__(3);

var _reactRedux = __webpack_require__(1189);

var _classnames = __webpack_require__(175);

var _classnames2 = _interopRequireDefault(_classnames);

var _actions = __webpack_require__(1354);

var _actions2 = _interopRequireDefault(_actions);

var _selectors = __webpack_require__(1352);

var _Svg = __webpack_require__(1359);

var _Svg2 = _interopRequireDefault(_Svg);

var _ManagedTree = __webpack_require__(1404);

var _ManagedTree2 = _interopRequireDefault(_ManagedTree);

__webpack_require__(1296);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

// check to see if its an object with propertie
function nodeHasProperties(item) {
  return !nodeHasChildren(item) && item.contents.value.type === "object";
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.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 nodeIsPrimitive(item) {}

function nodeHasChildren(item) {
  return Array.isArray(item.contents);
}

function createNode(name, path, contents) {
  // 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,
  // i.e. `{ foo: { bar: { baz: 5 }}}` will have a path of `foo/bar/baz`
  // for the inner object.
  return { name, path, contents };
}

class Scopes extends _react.Component {

  constructor(...args) {
    super(...args);

    // Cache of dynamically built nodes. We shouldn't need to clear
    // this out ever, since we don't ever "switch out" the object
    // being inspected.
    this.objectCache = {};

    this.getChildren = this.getChildren.bind(this);
    this.onExpand = this.onExpand.bind(this);
    this.renderItem = this.renderItem.bind(this);
  }

  makeNodesForProperties(objProps, parentPath) {
    const { ownProperties, prototype } = objProps;

    const nodes = Object.keys(ownProperties).sort()
    // Ignore non-concrete values like getters and setters
    // for now by making sure we have a value.
    .filter(name => "value" in ownProperties[name]).map(name => createNode(name, `${parentPath}/${name}`, ownProperties[name]));

    // Add the prototype if it exists and is not null
    if (prototype && prototype.type !== "null") {
      nodes.push(createNode("__proto__", `${parentPath}/__proto__`, {
        value: prototype
      }));
    }

    return nodes;
  }

  renderItem(item, depth, focused, _, expanded, { setExpanded }) {
    const notEnumberable = false;
    const objectValue = "";

    return _react2.default.createElement(
      "div",
      {
        className: (0, _classnames2.default)("node object-node", {
          focused: false,
          "not-enumerable": notEnumberable
        }),
        style: { marginLeft: depth * 15 },
        key: item.path,
        onClick: e => {
          e.stopPropagation();
          setExpanded(item, !expanded);
        }
      },
      _react2.default.createElement(_Svg2.default, {
        name: "arrow",
        className: (0, _classnames2.default)({
          expanded,
          hidden: nodeIsPrimitive(item)
        })
      }),
      _react2.default.createElement(
        "span",
        { className: "object-label" },
        item.name
      ),
      _react2.default.createElement(
        "span",
        { className: "object-delimiter" },
        objectValue ? ": " : ""
      ),
      _react2.default.createElement(
        "span",
        { className: "object-value" },
        objectValue || ""
      )
    );
  }

  getObjectProperties(item) {
    this.props.loadedObjects[item.contents.value.objectId];
  }

  getChildren(item) {
    const obj = item.contents;

    // Nodes can either have children already, or be an object with
    // properties that we need to go and fetch.
    if (nodeHasChildren(item)) {
      return item.contents;
    } else if (nodeHasProperties(item)) {
      const objectId = obj.value.objectId;

      // Because we are dynamically creating the tree as the user
      // expands it (not precalcuated 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.
      const key = item.path;
      if (this.objectCache[key]) {
        return this.objectCache[key];
      }

      const loadedProps = this.getObjectProperties(item);
      if (loadedProps) {
        const children = this.makeNodesForProperties(loadedProps, item.path);
        this.objectCache[objectId] = children;
        return children;
      }
      return [];
    }
    return [];
  }

  onExpand(item) {
    const { loadObjectProperties } = this.props;

    if (nodeHasProperties(item)) {
      loadObjectProperties(item.contents.value);
    }
  }

  getRoots() {
    return this.props.scopes.map(scope => {
      const name = scope.name || (scope.type == "global" ? "Window" : "");

      return {
        name: name,
        path: name,
        contents: { value: scope.object }
      };
    });
  }

  render() {
    const { isPaused } = this.props;

    if (!isPaused) {
      return _react2.default.createElement(
        "div",
        { className: (0, _classnames2.default)("pane", "scopes-list") },
        _react2.default.createElement(
          "div",
          { className: "pane-info" },
          L10N.getStr("scopes.notPaused")
        )
      );
    }

    const roots = this.getRoots();

    return _react2.default.createElement(
      "div",
      { className: (0, _classnames2.default)("pane", "scopes-list") },
      _react2.default.createElement(_ManagedTree2.default, {
        itemHeight: 20,
        getParent: item => null,
        getChildren: this.getChildren,
        getRoots: () => roots,
        getPath: item => item.path,
        autoExpand: 0,
        autoExpandDepth: 1,
        autoExpandAll: false,
        disabledFocus: true,
        onExpand: this.onExpand,
        renderItem: this.renderItem
      })
    );
  }
}

exports.default = (0, _reactRedux.connect)(state => ({
  isPaused: (0, _selectors.isPaused)(state),
  loadedObjects: () => {
    throw new Error("This is not implemented.");
  },
  scopes: (0, _selectors.getChromeScopes)(state)
}), dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(Scopes);

/***/ }),
/* 1611 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _redux = __webpack_require__(3);

var _reactRedux = __webpack_require__(1189);

var _actions = __webpack_require__(1354);

var _actions2 = _interopRequireDefault(_actions);

var _firefox = __webpack_require__(1500);

var _selectors = __webpack_require__(1352);

var _scopes = __webpack_require__(1792);

var _devtoolsReps = __webpack_require__(1408);

__webpack_require__(1296);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

class Scopes extends _react.PureComponent {
  constructor(props, ...args) {
    const { why, selectedFrame, frameScopes } = props;

    super(props, ...args);

    this.state = {
      scopes: (0, _scopes.getScopes)(why, selectedFrame, frameScopes)
    };
  }

  componentWillReceiveProps(nextProps) {
    const { isPaused, selectedFrame, frameScopes } = this.props;
    const isPausedChanged = isPaused !== nextProps.isPaused;
    const selectedFrameChanged = selectedFrame !== nextProps.selectedFrame;
    const frameScopesChanged = frameScopes !== nextProps.frameScopes;

    if (isPausedChanged || selectedFrameChanged || frameScopesChanged) {
      this.setState({
        scopes: (0, _scopes.getScopes)(nextProps.why, nextProps.selectedFrame, nextProps.frameScopes)
      });
    }
  }

  render() {
    const { isPaused, isLoading } = this.props;
    const { scopes } = this.state;

    if (scopes) {
      return _react2.default.createElement(
        "div",
        { className: "pane scopes-list" },
        _react2.default.createElement(_devtoolsReps.ObjectInspector, {
          roots: scopes,
          autoExpandAll: false,
          autoExpandDepth: 1,
          disableWrap: true,
          disabledFocus: true,
          dimTopLevelWindow: true,
          createObjectClient: grip => (0, _firefox.createObjectClient)(grip)
        })
      );
    }

    let stateText = L10N.getStr("scopes.notPaused");
    if (isPaused) {
      if (isLoading) {
        stateText = L10N.getStr("loadingText");
      } else {
        stateText = L10N.getStr("scopes.notAvailable");
      }
    }

    return _react2.default.createElement(
      "div",
      { className: "pane scopes-list" },
      _react2.default.createElement(
        "div",
        { className: "pane-info" },
        stateText
      )
    );
  }
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

exports.default = (0, _reactRedux.connect)(state => {
  const selectedFrame = (0, _selectors.getSelectedFrame)(state);
  const selectedSource = (0, _selectors.getSelectedSource)(state);

  const { scope: frameScopes, pending } = (0, _selectors.getFrameScope)(state, selectedSource && selectedSource.get("id"), selectedFrame && selectedFrame.id) || { scope: null, pending: false };

  return {
    selectedFrame,
    isPaused: (0, _selectors.isPaused)(state),
    isLoading: pending,
    why: (0, _selectors.getPauseReason)(state),
    frameScopes: frameScopes
  };
}, dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(Scopes);

/***/ }),
/* 1612 */,
/* 1613 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _reactRedux = __webpack_require__(1189);

var _redux = __webpack_require__(3);

var _actions = __webpack_require__(1354);

var _actions2 = _interopRequireDefault(_actions);

var _selectors = __webpack_require__(1352);

var _text = __webpack_require__(1387);

var _PaneToggle = __webpack_require__(1407);

var _PaneToggle2 = _interopRequireDefault(_PaneToggle);

__webpack_require__(1343);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

class WelcomeBox extends _react.Component {
  renderToggleButton() {
    const { horizontal, endPanelCollapsed, togglePaneCollapse } = this.props;
    if (horizontal) {
      return;
    }

    return _react2.default.createElement(_PaneToggle2.default, {
      position: "end",
      collapsed: !endPanelCollapsed,
      horizontal: horizontal,
      handleClick: togglePaneCollapse
    });
  }

  render() {
    const searchSourcesShortcut = (0, _text.formatKeyShortcut)(L10N.getStr("sources.search.key2"));

    const searchProjectShortcut = (0, _text.formatKeyShortcut)(L10N.getStr("projectTextSearch.key"));

    const searchSourcesLabel = L10N.getStr("welcome.search").substring(2);
    const searchProjectLabel = L10N.getStr("welcome.findInFiles").substring(2);
    const { setActiveSearch, openQuickOpen } = this.props;

    return _react2.default.createElement(
      "div",
      { className: "welcomebox" },
      _react2.default.createElement(
        "div",
        { className: "alignlabel" },
        _react2.default.createElement(
          "div",
          { className: "shortcutFunction" },
          _react2.default.createElement(
            "p",
            { onClick: () => openQuickOpen() },
            _react2.default.createElement(
              "span",
              { className: "shortcutKey" },
              searchSourcesShortcut
            ),
            _react2.default.createElement(
              "span",
              { className: "shortcutLabel" },
              searchSourcesLabel
            )
          ),
          _react2.default.createElement(
            "p",
            { onClick: setActiveSearch.bind(null, "project") },
            _react2.default.createElement(
              "span",
              { className: "shortcutKey" },
              searchProjectShortcut
            ),
            _react2.default.createElement(
              "span",
              { className: "shortcutLabel" },
              searchProjectLabel
            )
          )
        ),
        this.renderToggleButton()
      )
    );
  }
}

exports.default = (0, _reactRedux.connect)(state => ({
  endPanelCollapsed: (0, _selectors.getPaneCollapse)(state, "end")
}), dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(WelcomeBox);

/***/ }),
/* 1614 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _reactRedux = __webpack_require__(1189);

var _redux = __webpack_require__(3);

var _immutable = __webpack_require__(146);

var I = _interopRequireWildcard(_immutable);

var _selectors = __webpack_require__(1352);

var _ui = __webpack_require__(1439);

var _tabs = __webpack_require__(1941);

var _source = __webpack_require__(1356);

var _actions = __webpack_require__(1354);

var _actions2 = _interopRequireDefault(_actions);

var _lodash = __webpack_require__(2);

__webpack_require__(1344);

var _Tab = __webpack_require__(2018);

var _Tab2 = _interopRequireDefault(_Tab);

var _PaneToggle = __webpack_require__(1407);

var _PaneToggle2 = _interopRequireDefault(_PaneToggle);

var _Dropdown = __webpack_require__(1615);

var _Dropdown2 = _interopRequireDefault(_Dropdown);

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

class Tabs extends _react.PureComponent {

  constructor(props) {
    super(props);

    this.renderDropdownSource = source => {
      const { selectSource } = this.props;
      const filename = (0, _source.getFilename)(source.toJS());

      const onClick = () => selectSource(source.get("id"));
      return _react2.default.createElement(
        "li",
        { key: source.get("id"), onClick: onClick },
        _react2.default.createElement("img", { className: `dropdown-icon ${this.getIconClass(source)}` }),
        filename
      );
    };

    this.state = {
      dropdownShown: false,
      hiddenTabs: I.List()
    };

    this.onResize = (0, _lodash.debounce)(() => {
      this.updateHiddenTabs();
    });
  }

  componentDidUpdate(prevProps) {
    if (!(prevProps === this.props)) {
      this.updateHiddenTabs();
    }
  }

  componentDidMount() {
    this.updateHiddenTabs();
    window.addEventListener("resize", this.onResize);
  }

  componentWillUnmount() {
    window.removeEventListener("resize", this.onResize);
  }

  /*
   * Updates the hiddenSourceTabs state, by
   * finding the source tabs which are wrapped and are not on the top row.
   */
  updateHiddenTabs() {
    if (!this.refs.sourceTabs) {
      return;
    }
    const { selectedSource, tabSources, moveTab } = this.props;
    const sourceTabEls = this.refs.sourceTabs.children;
    const hiddenTabs = (0, _tabs.getHiddenTabs)(tabSources, sourceTabEls);

    if ((0, _ui.isVisible)() && hiddenTabs.indexOf(selectedSource) !== -1) {
      return moveTab(selectedSource.get("url"), 0);
    }

    this.setState({ hiddenTabs });
  }

  toggleSourcesDropdown(e) {
    this.setState({
      dropdownShown: !this.state.dropdownShown
    });
  }

  getIconClass(source) {
    if ((0, _source.isPretty)(source)) {
      return "prettyPrint";
    }
    if (source.get("isBlackBoxed")) {
      return "blackBox";
    }
    return "file";
  }

  renderTabs() {
    const { tabSources } = this.props;
    if (!tabSources) {
      return;
    }

    return _react2.default.createElement(
      "div",
      { className: "source-tabs", ref: "sourceTabs" },
      tabSources.map((source, index) => _react2.default.createElement(_Tab2.default, { key: index, source: source }))
    );
  }

  renderDropdown() {
    const hiddenTabs = this.state.hiddenTabs;
    if (!hiddenTabs || hiddenTabs.size == 0) {
      return null;
    }

    const Panel = _react2.default.createElement(
      "ul",
      null,
      hiddenTabs.map(this.renderDropdownSource)
    );

    return _react2.default.createElement(_Dropdown2.default, { panel: Panel, icon: "»" });
  }

  renderStartPanelToggleButton() {
    return _react2.default.createElement(_PaneToggle2.default, {
      position: "start",
      collapsed: !this.props.startPanelCollapsed,
      handleClick: this.props.togglePaneCollapse
    });
  }

  renderEndPanelToggleButton() {
    const { horizontal, endPanelCollapsed, togglePaneCollapse } = this.props;
    if (!horizontal) {
      return;
    }

    return _react2.default.createElement(_PaneToggle2.default, {
      position: "end",
      collapsed: !endPanelCollapsed,
      handleClick: togglePaneCollapse,
      horizontal: horizontal
    });
  }

  render() {
    return _react2.default.createElement(
      "div",
      { className: "source-header" },
      this.renderStartPanelToggleButton(),
      this.renderTabs(),
      this.renderDropdown(),
      this.renderEndPanelToggleButton()
    );
  }
}

exports.default = (0, _reactRedux.connect)(state => {
  return {
    selectedSource: (0, _selectors.getSelectedSource)(state),
    tabSources: (0, _selectors.getSourcesForTabs)(state)
  };
}, dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(Tabs);

/***/ }),
/* 1615 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.Dropdown = undefined;

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

__webpack_require__(1345);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

class Dropdown extends _react.Component {
  constructor(props) {
    super(props);

    this.toggleDropdown = e => {
      this.setState({
        dropdownShown: !this.state.dropdownShown
      });
    };

    this.state = {
      dropdownShown: false
    };

    this.toggleDropdown = this.toggleDropdown.bind(this);
  }

  renderPanel() {
    return _react2.default.createElement(
      "div",
      {
        className: "dropdown",
        onClick: this.toggleDropdown,
        style: { display: this.state.dropdownShown ? "block" : "none" }
      },
      this.props.panel
    );
  }

  renderButton() {
    return _react2.default.createElement(
      "button",
      { className: "dropdown-button", onClick: this.toggleDropdown },
      this.props.icon
    );
  }

  renderMask() {
    return _react2.default.createElement("div", {
      className: "dropdown-mask",
      onClick: this.toggleDropdown,
      style: { display: this.state.dropdownShown ? "block" : "none" }
    });
  }

  render() {
    return _react2.default.createElement(
      "div",
      { className: "dropdown-block" },
      this.renderPanel(),
      this.renderButton(),
      this.renderMask()
    );
  }
}

exports.Dropdown = Dropdown;
exports.default = Dropdown;

/***/ }),
/* 1616 */,
/* 1617 */,
/* 1618 */,
/* 1619 */,
/* 1620 */,
/* 1621 */,
/* 1622 */,
/* 1623 */,
/* 1624 */,
/* 1625 */,
/* 1626 */,
/* 1627 */,
/* 1628 */,
/* 1629 */,
/* 1630 */,
/* 1631 */,
/* 1632 */,
/* 1633 */,
/* 1634 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

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; }; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                                                                                                                                                                                   * License, v. 2.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-next-line max-len


exports.mapScopes = mapScopes;

var _selectors = __webpack_require__(1352);

var _loadSourceText = __webpack_require__(1435);

var _parser = __webpack_require__(1365);

var _promise = __webpack_require__(1653);

var _locColumn = __webpack_require__(2349);

var _findGeneratedBindingFromPosition = __webpack_require__(2358);

var _prefs = __webpack_require__(226);

var _log = __webpack_require__(2359);

var _devtoolsSourceMap = __webpack_require__(1360);

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 mapScopes(scopes, frame) {
  return async function ({ dispatch, getState, client, sourceMaps }) {
    const generatedSourceRecord = (0, _selectors.getSource)(getState(), frame.generatedLocation.sourceId);

    const sourceRecord = (0, _selectors.getSource)(getState(), frame.location.sourceId);

    const shouldMapScopes = _prefs.features.mapScopes && !generatedSourceRecord.get("isWasm") && !sourceRecord.get("isPrettyPrinted") && !(0, _devtoolsSourceMap.isGeneratedId)(frame.location.sourceId);

    dispatch({
      type: "MAP_SCOPES",
      frame,
      [_promise.PROMISE]: async function () {
        if (!shouldMapScopes) {
          return null;
        }

        await dispatch((0, _loadSourceText.loadSourceText)(sourceRecord));

        try {
          return await buildMappedScopes(sourceRecord.toJS(), frame, (await scopes), sourceMaps, client);
        } catch (e) {
          (0, _log.log)(e);
          return null;
        }
      }()
    });
  };
}

async function buildMappedScopes(source, frame, scopes, sourceMaps, client) {
  const originalAstScopes = await (0, _parser.getScopes)(frame.location);
  const generatedAstScopes = await (0, _parser.getScopes)(frame.generatedLocation);

  if (!originalAstScopes || !generatedAstScopes) {
    return null;
  }

  const generatedAstBindings = buildGeneratedBindingList(scopes, generatedAstScopes, frame.this);

  const mappedOriginalScopes = await Promise.all(Array.from(originalAstScopes, async item => {
    const generatedBindings = {};

    await Promise.all(Object.keys(item.bindings).map(async name => {
      const binding = item.bindings[name];

      const result = await findGeneratedBinding(sourceMaps, client, source, name, binding, generatedAstBindings);

      if (result) {
        generatedBindings[name] = result;
      }
    }));

    return _extends({}, item, {
      generatedBindings
    });
  }));

  return generateClientScope(scopes, mappedOriginalScopes);
}

function generateClientScope(scopes, originalScopes) {
  // Pull the root object scope and root lexical scope to reuse them in
  // our mapped scopes. This assumes that file file being processed is
  // a CommonJS or ES6 module, which might not be ideal. Potentially
  let globalLexicalScope = null;
  for (let s = scopes; s.parent; s = s.parent) {
    // $FlowIgnore - Flow doesn't like casting 'parent'.
    globalLexicalScope = s;
  }
  if (!globalLexicalScope) {
    throw new Error("Assertion failure - there should always be a scope");
  }

  // Build a structure similar to the client's linked scope object using
  // the original AST scopes, but pulling in the generated bindings
  // linked to each scope.
  const result = originalScopes.slice(0, -2).reverse().reduce((acc, orig, i) => {
    const _orig$generatedBindin = orig.generatedBindings,
          {
      // The 'this' binding data we have is handled independently, so
      // the binding data is not included here.
      // eslint-disable-next-line no-unused-vars
      this: _this
    } = _orig$generatedBindin,
          variables = _objectWithoutProperties(_orig$generatedBindin, ["this"]);

    return _extends({
      // Flow doesn't like casting 'parent'.
      parent: acc,
      actor: `originalActor${i}`,
      type: orig.type,
      bindings: {
        arguments: [],
        variables
      }
    }, orig.type === "function" ? {
      function: {
        displayName: orig.displayName
      }
    } : null, orig.type === "block" ? {
      block: {
        displayName: orig.displayName
      }
    } : null);
  }, globalLexicalScope);

  // The rendering logic in getScope 'this' bindings only runs on the current
  // selected frame scope, so we pluck out the 'this' binding that was mapped,
  // and put it in a special location
  const thisScope = originalScopes.find(scope => scope.bindings.this);
  if (thisScope) {
    result.bindings.this = thisScope.generatedBindings.this || null;
  }

  return result;
}

async function findGeneratedBinding(sourceMaps, client, source, name, originalBinding, generatedAstBindings) {
  // If there are no references to the implicits, then we have no way to
  // even attempt to map it back to the original since there is no location
  // data to use. Bail out instead of just showing it as unmapped.
  if (originalBinding.type === "implicit" && originalBinding.refs.length === 0) {
    return null;
  }

  const { declarations, refs } = originalBinding;

  const genContent = await declarations.concat(refs).reduce(async (acc, pos) => {
    const result = await acc;
    if (result) {
      return result;
    }

    return await (0, _findGeneratedBindingFromPosition.findGeneratedBindingFromPosition)(sourceMaps, client, source, pos, name, originalBinding.type, generatedAstBindings);
  }, null);

  if (genContent && genContent.desc) {
    return genContent.desc;
  } else if (genContent) {
    // If there is no descriptor for 'this', then this is not the top-level
    // 'this' that the server gave us a binding for, and we can just ignore it.
    if (name === "this") {
      return null;
    }

    // If the location is found but the descriptor is not, then it
    // means that the server scope information didn't match the scope
    // information from the DevTools parsed scopes.
    return {
      configurable: false,
      enumerable: true,
      writable: false,
      value: {
        type: "unscoped",
        unscoped: true,

        // HACK: Until support for "unscoped" lands in devtools-reps,
        // this will make these show as (unavailable).
        missingArguments: true
      }
    };
  }

  // If no location mapping is found, then the map is bad, or
  // the map is okay but it original location is inside
  // of some scope, but the generated location is outside, leading
  // us to search for bindings that don't technically exist.
  return {
    configurable: false,
    enumerable: true,
    writable: false,
    value: {
      type: "unmapped",
      unmapped: true,

      // HACK: Until support for "unmapped" lands in devtools-reps,
      // this will make these show as (unavailable).
      missingArguments: true
    }
  };
}

function buildGeneratedBindingList(scopes, generatedAstScopes, thisBinding) {
  const clientScopes = [];
  for (let s = scopes; s; s = s.parent) {
    clientScopes.push(s);
  }

  // The server's binding data doesn't include general 'this' binding
  // information, so we manually inject the one 'this' binding we have into
  // the normal binding data we are working with.
  const frameThisOwner = generatedAstScopes.find(generated => "this" in generated.bindings);

  const generatedBindings = clientScopes.reverse().map((s, i) => {
    const generated = generatedAstScopes[generatedAstScopes.length - 1 - i];

    const bindings = s.bindings ? Object.assign({}, ...s.bindings.arguments, s.bindings.variables) : {};

    if (generated === frameThisOwner && thisBinding) {
      bindings.this = {
        value: thisBinding
      };
    }

    return {
      generated,
      client: _extends({}, s, {
        bindings
      })
    };
  }).slice(2).reduce((acc, { client: { bindings }, generated }) => {
    // If the parser worker's result didn't match the client scopes,
    // there might not be a generated scope that matches.
    if (generated) {
      for (const name of Object.keys(generated.bindings)) {
        const { declarations, refs } = generated.bindings[name];
        for (const loc of declarations.concat(refs)) {
          acc.push({
            name,
            loc,
            desc: bindings[name] || null
          });
        }
      }
    }
    return acc;
  }, [])
  // Sort so we can binary-search.
  .sort((a, b) => {
    const aStart = a.loc.start;
    const bStart = a.loc.start;

    if (aStart.line === bStart.line) {
      return (0, _locColumn.locColumn)(aStart) - (0, _locColumn.locColumn)(bStart);
    }
    return aStart.line - bStart.line;
  });

  return generatedBindings;
}

/***/ }),
/* 1635 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.createQuickOpenState = undefined;
exports.default = update;
exports.getQuickOpenEnabled = getQuickOpenEnabled;
exports.getQuickOpenQuery = getQuickOpenQuery;
exports.getQuickOpenType = getQuickOpenType;

var _makeRecord = __webpack_require__(1361);

var _makeRecord2 = _interopRequireDefault(_makeRecord);

var _quickOpen = __webpack_require__(1636);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
 * Quick Open reducer
 * @module reducers/quick-open
 */

const createQuickOpenState = exports.createQuickOpenState = (0, _makeRecord2.default)({
  enabled: false,
  query: "",
  searchType: "sources"
});

function update(state = createQuickOpenState(), action) {
  switch (action.type) {
    case "OPEN_QUICK_OPEN":
      if (action.query != null) {
        return state.merge({
          enabled: true,
          query: action.query,
          searchType: (0, _quickOpen.parseQuickOpenQuery)(action.query)
        });
      }
      return state.set("enabled", true);
    case "CLOSE_QUICK_OPEN":
      return createQuickOpenState();
    case "SET_QUICK_OPEN_QUERY":
      return state.merge({
        query: action.query,
        searchType: (0, _quickOpen.parseQuickOpenQuery)(action.query)
      });
    default:
      return state;
  }
}

function getQuickOpenEnabled(state) {
  return state.quickOpen.get("enabled");
}

function getQuickOpenQuery(state) {
  return state.quickOpen.get("query");
}

function getQuickOpenType(state) {
  return state.quickOpen.get("searchType");
}

/***/ }),
/* 1636 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.MODIFIERS = undefined;

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; }; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                                                                                                                                                                                   * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                                                                                                                                                                                                                                   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

exports.parseQuickOpenQuery = parseQuickOpenQuery;
exports.parseLineColumn = parseLineColumn;
exports.formatSymbol = formatSymbol;
exports.formatSymbols = formatSymbols;
exports.formatShortcutResults = formatShortcutResults;
exports.formatSources = formatSources;

var _utils = __webpack_require__(1366);

var _source = __webpack_require__(1356);

const MODIFIERS = exports.MODIFIERS = {
  "@": "functions",
  "#": "variables",
  ":": "goto",
  "?": "shortcuts"
};

function parseQuickOpenQuery(query) {
  const modifierPattern = /^@|#|:|\?$/;
  const gotoSourcePattern = /^(\w+)\:/;
  const startsWithModifier = modifierPattern.test(query[0]);
  const isGotoSource = gotoSourcePattern.test(query);

  if (startsWithModifier) {
    const modifier = query[0];
    return MODIFIERS[modifier];
  }

  if (isGotoSource) {
    return "gotoSource";
  }

  return "sources";
}

function parseLineColumn(query) {
  const [, line, column] = query.split(":");
  const lineNumber = parseInt(line, 10);
  const columnNumber = parseInt(column, 10);
  if (!isNaN(lineNumber)) {
    return _extends({
      line: lineNumber
    }, !isNaN(columnNumber) ? { column: columnNumber } : null);
  }
}

function formatSymbol(symbol) {
  return {
    id: `${symbol.name}:${symbol.location.start.line}`,
    title: symbol.name,
    subtitle: `${symbol.location.start.line}`,
    value: symbol.name,
    location: symbol.location
  };
}

function formatSymbols(symbols) {
  if (!symbols) {
    return { variables: [], functions: [] };
  }

  const { variables, functions } = symbols;

  return {
    variables: variables.map(formatSymbol),
    functions: functions.map(formatSymbol)
  };
}

function formatShortcutResults() {
  return [{
    value: L10N.getStr("symbolSearch.search.functionsPlaceholder.title"),
    title: `@ ${L10N.getStr("symbolSearch.search.functionsPlaceholder")}`,
    id: "@"
  }, {
    value: L10N.getStr("symbolSearch.search.variablesPlaceholder.title"),
    title: `# ${L10N.getStr("symbolSearch.search.variablesPlaceholder")}`,
    id: "#"
  }, {
    value: L10N.getStr("gotoLineModal.title"),
    title: `: ${L10N.getStr("gotoLineModal.placeholder")}`,
    id: ":"
  }];
}

function formatSources(sources) {
  return sources.valueSeq().filter(source => !(0, _source.isPretty)(source)).map(source => {
    const sourcePath = (0, _source.getSourcePath)(source.get("url"));
    return {
      value: sourcePath,
      title: sourcePath.split("/").pop(),
      subtitle: (0, _utils.endTruncateStr)(sourcePath, 100),
      id: source.get("id"),
      url: source.get("url")
    };
  }).filter(({ value }) => value != "").toJS();
}

/***/ }),
/* 1637 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.command = command;
exports.stepIn = stepIn;
exports.stepOver = stepOver;
exports.stepOut = stepOut;
exports.resume = resume;
exports.rewind = rewind;
exports.reverseStepIn = reverseStepIn;
exports.reverseStepOver = reverseStepOver;
exports.reverseStepOut = reverseStepOut;
exports.astCommand = astCommand;

var _selectors = __webpack_require__(1352);

var _promise = __webpack_require__(1653);

var _parser = __webpack_require__(1365);

var _breakpoints = __webpack_require__(1396);

var _prefs = __webpack_require__(226);

/**
 * Debugger commands like stepOver, stepIn, stepUp
 *
 * @param string $0.type
 * @memberof actions/pause
 * @static
 */
function command(type) {
  return async ({ dispatch, client }) => {
    return dispatch({
      type: "COMMAND",
      command: type,
      [_promise.PROMISE]: client[type]()
    });
  };
}

/**
 * StepIn
 * @memberof actions/pause
 * @static
 * @returns {Function} {@link command}
 */
/* -*- indent-tabs-mode: nil; js-indent-level: 2; js-indent-level: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

function stepIn() {
  return ({ dispatch, getState }) => {
    if ((0, _selectors.isPaused)(getState())) {
      return dispatch(command("stepIn"));
    }
  };
}

/**
 * stepOver
 * @memberof actions/pause
 * @static
 * @returns {Function} {@link command}
 */
function stepOver() {
  return ({ dispatch, getState }) => {
    if ((0, _selectors.isPaused)(getState())) {
      return dispatch(astCommand("stepOver"));
    }
  };
}

/**
 * stepOut
 * @memberof actions/pause
 * @static
 * @returns {Function} {@link command}
 */
function stepOut() {
  return ({ dispatch, getState }) => {
    if ((0, _selectors.isPaused)(getState())) {
      return dispatch(command("stepOut"));
    }
  };
}

/**
 * resume
 * @memberof actions/pause
 * @static
 * @returns {Function} {@link command}
 */
function resume() {
  return ({ dispatch, getState }) => {
    if ((0, _selectors.isPaused)(getState())) {
      return dispatch(command("resume"));
    }
  };
}

/**
 * rewind
 * @memberof actions/pause
 * @static
 * @returns {Function} {@link command}
 */
function rewind() {
  return ({ dispatch, getState }) => {
    if ((0, _selectors.isPaused)(getState())) {
      return dispatch(command("rewind"));
    }
  };
}

/**
 * reverseStepIn
 * @memberof actions/pause
 * @static
 * @returns {Function} {@link command}
 */
function reverseStepIn() {
  return ({ dispatch, getState }) => {
    if ((0, _selectors.isPaused)(getState())) {
      return dispatch(command("reverseStepIn"));
    }
  };
}

/**
 * reverseStepOver
 * @memberof actions/pause
 * @static
 * @returns {Function} {@link command}
 */
function reverseStepOver() {
  return ({ dispatch, getState }) => {
    if ((0, _selectors.isPaused)(getState())) {
      return dispatch(astCommand("reverseStepOver"));
    }
  };
}

/**
 * reverseStepOut
 * @memberof actions/pause
 * @static
 * @returns {Function} {@link command}
 */
function reverseStepOut() {
  return ({ dispatch, getState }) => {
    if ((0, _selectors.isPaused)(getState())) {
      return dispatch(command("reverseStepOut"));
    }
  };
}

/*
 * Checks for await or yield calls on the paused line
 * This avoids potentially expensive parser calls when we are likely
 * not at an async expression.
 */
function hasAwait(source, pauseLocation) {
  const { line, column } = pauseLocation;
  if (!source.text) {
    return false;
  }

  const lineText = source.text.split("\n")[line - 1];

  if (!lineText) {
    return false;
  }

  const snippet = lineText.slice(column - 50, column + 50);

  return !!snippet.match(/(yield|await)/);
}

/**
 * @memberOf actions/pause
 * @static
 * @param stepType
 * @returns {function(ThunkArgs)}
 */
function astCommand(stepType) {
  return async ({ dispatch, getState, sourceMaps }) => {
    if (!_prefs.features.asyncStepping) {
      return dispatch(command(stepType));
    }

    if (stepType == "stepOver") {
      // This type definition is ambiguous:
      const frame = (0, _selectors.getTopFrame)(getState());
      const selectedSource = (0, _selectors.getSelectedSource)(getState());
      const source = selectedSource ? selectedSource.toJS() : null;

      if (source && hasAwait(source, frame.location)) {
        const nextLocation = await (0, _parser.getNextStep)(source.id, frame.location);
        if (nextLocation) {
          await dispatch((0, _breakpoints.addHiddenBreakpoint)(nextLocation));
          return dispatch(command("resume"));
        }
      }
    }

    return dispatch(command(stepType));
  };
}

/***/ }),
/* 1638 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.findBestMatchExpression = findBestMatchExpression;
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 findBestMatchExpression(symbols, tokenPos, token) {
  const { memberExpressions, identifiers } = symbols;
  const { line, column } = tokenPos;
  return identifiers.concat(memberExpressions).reduce((found, expression) => {
    const overlaps = expression.location.start.line == line && expression.location.start.column <= column && expression.location.end.column >= column && !expression.computed;

    if (overlaps) {
      return expression;
    }

    return found;
  }, {});
}

/***/ }),
/* 1639 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _commands = __webpack_require__(1637);

Object.defineProperty(exports, "stepIn", {
  enumerable: true,
  get: function () {
    return _commands.stepIn;
  }
});
Object.defineProperty(exports, "stepOver", {
  enumerable: true,
  get: function () {
    return _commands.stepOver;
  }
});
Object.defineProperty(exports, "stepOut", {
  enumerable: true,
  get: function () {
    return _commands.stepOut;
  }
});
Object.defineProperty(exports, "resume", {
  enumerable: true,
  get: function () {
    return _commands.resume;
  }
});
Object.defineProperty(exports, "rewind", {
  enumerable: true,
  get: function () {
    return _commands.rewind;
  }
});
Object.defineProperty(exports, "reverseStepIn", {
  enumerable: true,
  get: function () {
    return _commands.reverseStepIn;
  }
});
Object.defineProperty(exports, "reverseStepOver", {
  enumerable: true,
  get: function () {
    return _commands.reverseStepOver;
  }
});
Object.defineProperty(exports, "reverseStepOut", {
  enumerable: true,
  get: function () {
    return _commands.reverseStepOut;
  }
});

var _fetchScopes = __webpack_require__(1655);

Object.defineProperty(exports, "fetchScopes", {
  enumerable: true,
  get: function () {
    return _fetchScopes.fetchScopes;
  }
});

var _paused = __webpack_require__(1640);

Object.defineProperty(exports, "paused", {
  enumerable: true,
  get: function () {
    return _paused.paused;
  }
});

var _resumed = __webpack_require__(1641);

Object.defineProperty(exports, "resumed", {
  enumerable: true,
  get: function () {
    return _resumed.resumed;
  }
});

var _continueToHere = __webpack_require__(1642);

Object.defineProperty(exports, "continueToHere", {
  enumerable: true,
  get: function () {
    return _continueToHere.continueToHere;
  }
});

var _breakOnNext = __webpack_require__(1643);

Object.defineProperty(exports, "breakOnNext", {
  enumerable: true,
  get: function () {
    return _breakOnNext.breakOnNext;
  }
});

var _mapFrames = __webpack_require__(1804);

Object.defineProperty(exports, "mapFrames", {
  enumerable: true,
  get: function () {
    return _mapFrames.mapFrames;
  }
});

var _setPopupObjectProperties = __webpack_require__(2008);

Object.defineProperty(exports, "setPopupObjectProperties", {
  enumerable: true,
  get: function () {
    return _setPopupObjectProperties.setPopupObjectProperties;
  }
});

var _pauseOnExceptions = __webpack_require__(1645);

Object.defineProperty(exports, "pauseOnExceptions", {
  enumerable: true,
  get: function () {
    return _pauseOnExceptions.pauseOnExceptions;
  }
});

var _selectFrame = __webpack_require__(1646);

Object.defineProperty(exports, "selectFrame", {
  enumerable: true,
  get: function () {
    return _selectFrame.selectFrame;
  }
});

/***/ }),
/* 1640 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.paused = paused;

var _devtoolsSourceMap = __webpack_require__(1360);

var _selectors = __webpack_require__(1352);

var _ = __webpack_require__(1639);

var _breakpoints = __webpack_require__(1396);

var _expressions = __webpack_require__(1398);

var _sources = __webpack_require__(1797);

var _ui = __webpack_require__(1385);

var _commands = __webpack_require__(1637);

var _pause = __webpack_require__(2419);

var _mapFrames = __webpack_require__(1804);

var _fetchScopes = __webpack_require__(1655);

async function getOriginalSourceForFrame(state, frame) {
  return (0, _selectors.getSources)(state).get(frame.location.sourceId);
}
/**
 * Debugger has just paused
 *
 * @param {object} pauseInfo
 * @memberof actions/pause
 * @static
 */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 paused(pauseInfo) {
  return async function ({ dispatch, getState, client, sourceMaps }) {
    const { frames, why, loadedObjects } = pauseInfo;
    const rootFrame = frames.length > 0 ? frames[0] : null;

    if (rootFrame) {
      const mappedFrame = await (0, _mapFrames.updateFrameLocation)(rootFrame, sourceMaps);
      const source = await getOriginalSourceForFrame(getState(), mappedFrame);

      // Ensure that the original file has loaded if there is one.
      await dispatch((0, _sources.loadSourceText)(source));

      if (await (0, _pause.shouldStep)(mappedFrame, getState(), sourceMaps)) {
        dispatch((0, _commands.command)("stepOver"));
        return;
      }
    }

    dispatch({
      type: "PAUSED",
      why,
      frames,
      selectedFrameId: rootFrame ? rootFrame.id : undefined,
      loadedObjects: loadedObjects || []
    });

    const hiddenBreakpointLocation = (0, _selectors.getHiddenBreakpointLocation)(getState());
    if (hiddenBreakpointLocation) {
      dispatch((0, _breakpoints.removeBreakpoint)(hiddenBreakpointLocation));
    }

    if (!(0, _selectors.isEvaluatingExpression)(getState())) {
      dispatch((0, _expressions.evaluateExpressions)());
    }

    await dispatch((0, _.mapFrames)());
    const selectedFrame = (0, _selectors.getSelectedFrame)(getState());

    if (selectedFrame) {
      const visibleFrame = (0, _selectors.getVisibleSelectedFrame)(getState());
      const location = (0, _devtoolsSourceMap.isGeneratedId)(visibleFrame.location.sourceId) ? selectedFrame.generatedLocation : selectedFrame.location;
      await dispatch((0, _sources.selectLocation)(location));
    }

    dispatch((0, _ui.togglePaneCollapse)("end", false));
    dispatch((0, _fetchScopes.fetchScopes)());
  };
}

/***/ }),
/* 1641 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.resumed = resumed;

var _selectors = __webpack_require__(1352);

var _expressions = __webpack_require__(1398);

var _pause = __webpack_require__(2419);

/**
 * Debugger has just resumed
 *
 * @memberof actions/pause
 * @static
 */
function resumed() {
  return async ({ dispatch, client, getState }) => {
    const why = (0, _selectors.getPauseReason)(getState());
    const wasPausedInEval = (0, _pause.inDebuggerEval)(why);

    if (!(0, _selectors.isStepping)(getState()) && !wasPausedInEval) {
      await dispatch((0, _expressions.evaluateExpressions)());
    }

    dispatch({
      type: "RESUME"
    });
  };
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/***/ }),
/* 1642 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.continueToHere = continueToHere;

var _selectors = __webpack_require__(1352);

var _breakpoints = __webpack_require__(1396);

var _commands = __webpack_require__(1637);

function continueToHere(line) {
  return async function ({ dispatch, getState }) {
    const source = (0, _selectors.getSelectedSource)(getState()).toJS();

    await dispatch((0, _breakpoints.addHiddenBreakpoint)({
      line,
      column: undefined,
      sourceId: source.id
    }));

    dispatch((0, _commands.resume)());
  };
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/***/ }),
/* 1643 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.breakOnNext = breakOnNext;


/**
 * Debugger breakOnNext command.
 * It's different from the comand action because we also want to
 * highlight the pause icon.
 *
 * @memberof actions/pause
 * @static
 */
function breakOnNext() {
  return ({ dispatch, client }) => {
    client.breakOnNext();

    return dispatch({
      type: "BREAK_ON_NEXT",
      value: true
    });
  };
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/***/ }),
/* 1644 */,
/* 1645 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.pauseOnExceptions = pauseOnExceptions;

var _promise = __webpack_require__(1653);

/**
 *
 * @memberof actions/pause
 * @static
 */
function pauseOnExceptions(shouldPauseOnExceptions, shouldIgnoreCaughtExceptions) {
  return ({ dispatch, client }) => {
    dispatch({
      type: "PAUSE_ON_EXCEPTIONS",
      shouldPauseOnExceptions,
      shouldIgnoreCaughtExceptions,
      [_promise.PROMISE]: client.pauseOnExceptions(shouldPauseOnExceptions, shouldIgnoreCaughtExceptions)
    });
  };
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/***/ }),
/* 1646 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.selectFrame = selectFrame;

var _sources = __webpack_require__(1797);

var _expressions = __webpack_require__(1398);

var _fetchScopes = __webpack_require__(1655);

/**
 * @memberof actions/pause
 * @static
 */
function selectFrame(frame) {
  return async ({ dispatch, client, getState, sourceMaps }) => {
    dispatch({
      type: "SELECT_FRAME",
      frame
    });

    dispatch((0, _sources.selectLocation)(frame.location));

    dispatch((0, _expressions.evaluateExpressions)());
    dispatch((0, _fetchScopes.fetchScopes)());
  };
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/***/ }),
/* 1647 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.setQuickOpenQuery = setQuickOpenQuery;
exports.openQuickOpen = openQuickOpen;
exports.closeQuickOpen = closeQuickOpen;
function setQuickOpenQuery(query) {
  return {
    type: "SET_QUICK_OPEN_QUERY",
    query
  };
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.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 openQuickOpen(query) {
  if (query != null) {
    return { type: "OPEN_QUICK_OPEN", query };
  }
  return { type: "OPEN_QUICK_OPEN" };
}

function closeQuickOpen() {
  return { type: "CLOSE_QUICK_OPEN" };
}

/***/ }),
/* 1648 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 id=\"svg2\" xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" xmlns:cc=\"http://creativecommons.org/ns#\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\" viewBox=\"0 0 354.33071 354.33071\"><metadata id=\"metadata7\"><rdf:RDF><cc:Work rdf:about><dc:format>image/svg+xml</dc:format><dc:type rdf:resource=\"http://purl.org/dc/dcmitype/StillImage\"></dc><dc:title></dc></cc:Work></rdf:RDF></metadata><g id=\"layer1\" fill=\"#EF2D5E\" transform=\"translate(0 -698.03)\"><path id=\"text3336\" d=\"m102.28 749.3-88.657 293.97h72.114l14.848-63.206h86.112l14.845 63.206h73.81l-34.36-113.69-128.53 0.00011 30.967-129.8v-50.48z\"></path><rect id=\"rect3356\" transform=\"rotate(237.21)\" height=\"79.557\" width=\"79.557\" y=\"-311.53\" x=\"-785.82\"></rect><rect id=\"rect3362\" transform=\"matrix(-.78208 .62317 -.62317 -.78208 0 0)\" height=\"95.238\" width=\"95.238\" y=\"-885.85\" x=\"318.72\"></rect><rect id=\"rect3364\" transform=\"rotate(12.582)\" height=\"95.238\" width=\"95.238\" y=\"633.02\" x=\"396.31\"></rect></g></svg>"

/***/ }),
/* 1649 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 viewBox=\"0 0 256 140\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" preserveAspectRatio=\"xMidYMid\"><defs><linearGradient x1=\"49.9109718%\" y1=\"99.9084071%\" x2=\"49.9109718%\" y2=\"-0.0902654867%\" id=\"linearGradient-1\"><stop stop-color=\"#8AC23E\" offset=\"0%\"></stop><stop stop-color=\"#8AC23E\" stop-opacity=\"0\" offset=\"100%\"></stop></linearGradient><linearGradient x1=\"49.9835165%\" y1=\"0.0637125664%\" x2=\"49.9835165%\" y2=\"100.062389%\" id=\"linearGradient-2\"><stop stop-color=\"#698932\" offset=\"0%\"></stop><stop stop-color=\"#698932\" stop-opacity=\"0\" offset=\"100%\"></stop></linearGradient><linearGradient x1=\"50.0595611%\" y1=\"0.0637125664%\" x2=\"50.0595611%\" y2=\"100.062389%\" id=\"linearGradient-3\"><stop stop-color=\"#FFED01\" offset=\"0%\"></stop><stop stop-color=\"#FFED01\" stop-opacity=\"0\" offset=\"100%\"></stop></linearGradient><linearGradient x1=\"50.0170846%\" y1=\"0.0637125664%\" x2=\"50.0170846%\" y2=\"100.092699%\" id=\"linearGradient-4\"><stop stop-color=\"#E02A89\" offset=\"0%\"></stop><stop stop-color=\"#E02A89\" stop-opacity=\"0\" offset=\"100%\"></stop></linearGradient><linearGradient x1=\"49.9631661%\" y1=\"-0.0915929204%\" x2=\"49.9631661%\" y2=\"99.9070796%\" id=\"linearGradient-5\"><stop stop-color=\"#7F1E4F\" offset=\"0%\"></stop><stop stop-color=\"#7F1E4F\" stop-opacity=\"0\" offset=\"100%\"></stop></linearGradient><linearGradient x1=\"49.9652038%\" y1=\"-0.0915929204%\" x2=\"49.9652038%\" y2=\"99.9070796%\" id=\"linearGradient-6\"><stop stop-color=\"#E95506\" offset=\"0%\"></stop><stop stop-color=\"#E95506\" stop-opacity=\"0\" offset=\"100%\"></stop></linearGradient><linearGradient x1=\"49.9766458%\" y1=\"99.9084071%\" x2=\"49.9766458%\" y2=\"-0.0265486726%\" id=\"linearGradient-7\"><stop stop-color=\"#88D0F1\" offset=\"0%\"></stop><stop stop-color=\"#88D0F1\" stop-opacity=\"0\" offset=\"100%\"></stop></linearGradient><linearGradient x1=\"50.0205329%\" y1=\"0.0637125664%\" x2=\"50.0205329%\" y2=\"99.9986726%\" id=\"linearGradient-8\"><stop stop-color=\"#00828B\" offset=\"0%\"></stop><stop stop-color=\"#00828B\" stop-opacity=\"0\" offset=\"83.25%\"></stop></linearGradient><linearGradient x1=\"50.078022%\" y1=\"99.9752212%\" x2=\"50.078022%\" y2=\"0.0705752212%\" id=\"linearGradient-9\"><stop stop-color=\"#2073BA\" offset=\"0%\"></stop><stop stop-color=\"#2073BA\" stop-opacity=\"0\" offset=\"100%\"></stop></linearGradient><linearGradient x1=\"50.0177116%\" y1=\"0.0637125664%\" x2=\"50.0177116%\" y2=\"99.9986726%\" id=\"linearGradient-10\"><stop stop-color=\"#8ED0E1\" offset=\"0%\"></stop><stop stop-color=\"#88D0F1\" stop-opacity=\"0\" offset=\"100%\"></stop></linearGradient></defs><g><path d=\"M128.1,0 L164.2,0 C150.9,21.8 136.2,45.8 121.5,69.7 L85.4,69.7 C100.1,45.8 114.8,21.8 128.1,0 Z\" fill=\"#8DC220\"></path><path d=\"M42.7,0 L78.8,0 C71.7,11.6 64.5,23.3 57.4,34.9 C50.3,46.5 43.2,58.1 36.1,69.7 C43.2,81.3 50.3,93 57.4,104.6 C64.5,116.2 71.6,127.8 78.8,139.4 L42.7,139.4 C35.6,127.8 28.4,116.2 21.3,104.6 C14.2,93.1 7.1,81.4 0,69.8 C7.1,58.2 14.2,46.6 21.3,35 C28.4,23.3 35.6,11.7 42.7,0 Z\" fill=\"#44BFEF\"></path><path d=\"M42.7,0 L42.7,0 C57.4,24 72.1,48 85.4,69.7 L121.5,69.7 C108.2,48 93.5,24 78.8,0 L78.8,0 L42.7,0 Z\" fill=\"#00AC71\"></path><path d=\"M164.2,0 L128.1,0 C135.2,11.6 142.3,23.3 149.5,34.9 C156.6,46.5 163.7,58.1 170.9,69.7 C163.8,81.3 156.6,93 149.5,104.6 C142.4,116.2 135.3,127.8 128.1,139.4 L164.2,139.4 C171.3,127.8 178.4,116.2 185.6,104.6 C192.7,93 199.8,81.4 207,69.7 C199.8,58.1 192.7,46.5 185.6,34.9 C178.5,23.3 171.3,11.7 164.2,0 Z\" fill=\"#F9BC00\"></path><path d=\"M149.3,24.6 L113.2,24.6 C108.6,32.1 104,39.7 99.4,47.2 C94.8,54.7 90.2,62.3 85.5,69.8 L121.6,69.8 C126.2,62.3 130.9,54.7 135.5,47.2 C140,39.6 144.6,32.1 149.3,24.6 Z\" fill=\"url(#linearGradient-1)\"></path><path d=\"M100.5,45.2 L136.6,45.2 C141.2,37.7 145.8,30.1 150.4,22.6 C155,15.1 159.6,7.5 164.2,0 L128.1,0 C123.5,7.5 118.9,15.1 114.3,22.6 C109.7,30.2 105.1,37.7 100.5,45.2 Z\" fill=\"url(#linearGradient-2)\"></path><path d=\"M191.9,45.2 L155.8,45.2 C151.2,37.7 146.5,30.1 141.9,22.6 C137.3,15.1 132.7,7.5 128.1,0 L164.2,0 C168.8,7.5 173.4,15.1 178,22.6 C182.7,30.2 187.3,37.7 191.9,45.2 Z\" fill=\"url(#linearGradient-3)\"></path><path d=\"M213.2,0 L177.1,0 C184.2,11.7 191.3,23.3 198.4,34.9 C205.5,46.5 212.7,58.1 219.8,69.7 C212.7,81.3 205.5,93 198.4,104.6 C191.3,116.2 184.2,127.8 177,139.4 L213.1,139.4 C220.2,127.8 227.3,116.2 234.5,104.6 C241.6,93 248.7,81.4 255.9,69.7 C248.8,58.1 241.7,46.5 234.5,34.9 C227.5,23.3 220.3,11.7 213.2,0 Z\" fill=\"#DF1B1C\"></path><path d=\"M240.9,45.2 L204.8,45.2 C200.1,37.7 195.5,30.1 190.9,22.6 C186.3,15.1 181.7,7.5 177.1,0 L213.2,0 C217.8,7.5 222.4,15.1 227,22.6 C231.6,30.2 236.3,37.7 240.9,45.2 Z\" fill=\"url(#linearGradient-4)\"></path><path d=\"M192.2,115 L228.3,115 C232.9,107.5 237.5,100 242.1,92.4 C246.7,84.9 251.3,77.3 256,69.8 L219.8,69.8 C215.2,77.3 210.6,84.9 206,92.4 C201.4,99.9 196.8,107.4 192.2,115 Z\" fill=\"url(#linearGradient-5)\"></path><path d=\"M143.2,115 L179.3,115 C183.9,107.5 188.5,99.9 193.1,92.4 C197.7,84.9 202.3,77.3 207,69.8 L170.9,69.8 C166.3,77.3 161.7,84.9 157.1,92.4 C152.4,99.9 147.8,107.4 143.2,115 Z\" fill=\"url(#linearGradient-6)\"></path><path d=\"M63.8,24.6 L27.7,24.6 C23.1,32.1 18.5,39.7 13.9,47.2 C9.2,54.7 4.6,62.3 0,69.8 L36.1,69.8 C40.7,62.3 45.3,54.8 49.9,47.2 C54.5,39.7 59.1,32.1 63.8,24.6 Z\" fill=\"url(#linearGradient-7)\"></path><path d=\"M15,45.2 L51.1,45.2 C55.7,37.7 60.3,30.1 64.9,22.6 C69.6,15.1 74.2,7.5 78.8,0 L42.7,0 C38.1,7.5 33.5,15 28.9,22.6 C24.3,30.1 19.7,37.7 15,45.2 Z\" fill=\"url(#linearGradient-8)\"></path><path d=\"M15,94.3 L51.1,94.3 C55.7,101.8 60.3,109.4 64.9,116.9 C69.5,124.4 74.1,132 78.7,139.5 L42.6,139.5 C38,132 33.3,124.5 28.7,116.9 C24.2,109.4 19.6,101.9 15,94.3 Z\" fill=\"url(#linearGradient-9)\"></path><path d=\"M106.5,45.2 L70.4,45.2 C65.8,37.7 61.2,30.1 56.6,22.6 C51.9,15.1 47.3,7.5 42.7,0 L78.8,0 C83.4,7.5 88,15 92.6,22.6 C97.3,30.1 101.9,37.7 106.5,45.2 Z\" fill=\"url(#linearGradient-10)\"></path></g></svg>"

/***/ }),
/* 1650 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 viewBox=\"0 0 512 512\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" style=\"background: #FFFFFF;\"><title>Zeit - Black on white logo</title><defs><linearGradient x1=\"114.720775%\" y1=\"181.283245%\" x2=\"39.5399306%\" y2=\"100%\" id=\"linearGradient-1\"><stop stop-color=\"#FFFFFF\" offset=\"0%\"></stop><stop stop-color=\"#000000\" offset=\"100%\"></stop></linearGradient></defs><g id=\"Page-1\" stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\"><g id=\"Black-on-white\" fill=\"url(#linearGradient-1)\"><polygon id=\"Triangle-3-Copy\" points=\"254 156 367 356 141 356\"></polygon></g></g></svg>"

/***/ }),
/* 1651 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" viewBox=\"0 0 1000 1000\"><style>.st0{display:none}.st1{display:inline}.st2{fill:#222}.st3{fill:#41b883}.st4{fill:#328170}.st5{fill:#35495e}</style><g id=\"express\" class=\"st0\"><g class=\"st1\"><path class=\"st2\" d=\"M24.5 870.5v-376H494v-22H24.5v-343h499.1v-22H2.5v785H528v-22H24.5z\"></path><path class=\"st2\" d=\"M951.3 327.4L756.7 583.6 566.5 327.4h-28.6l205.6 272.7-225.4 292.4h26.4l212.2-276 213.3 276h27.5L771 600l206.7-272.6h-26.4z\"></path></g></g><g id=\"nuxt\"><path class=\"st3\" d=\"M317.9 852H3.7l408.1-704 408.1 704H507.7\"></path><path class=\"st4\" d=\"M779.8 852h216.5l-354-608.5-351 608.5h216.5\"></path><path class=\"st5\" d=\"M651.2 852h159.5L549.9 403.8 291.3 852h159.5\"></path></g></svg>"

/***/ }),
/* 1652 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.QuickOpenModal = undefined;

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; }; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                                                                                                                                                                                   * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                                                                                                                                                                                                                                   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _reactRedux = __webpack_require__(1189);

var _fuzzaldrinPlus = __webpack_require__(161);

var _fuzzaldrinPlus2 = _interopRequireDefault(_fuzzaldrinPlus);

var _path = __webpack_require__(1393);

var _actions = __webpack_require__(1354);

var _actions2 = _interopRequireDefault(_actions);

var _selectors = __webpack_require__(1352);

var _resultList = __webpack_require__(1406);

var _quickOpen = __webpack_require__(1636);

var _Modal = __webpack_require__(1403);

var _Modal2 = _interopRequireDefault(_Modal);

var _SearchInput = __webpack_require__(1379);

var _SearchInput2 = _interopRequireDefault(_SearchInput);

var _ResultList = __webpack_require__(1445);

var _ResultList2 = _interopRequireDefault(_ResultList);

__webpack_require__(2253);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function filter(values, query) {
  return _fuzzaldrinPlus2.default.filter(values, query, {
    key: "value",
    maxResults: 1000
  });
}

class QuickOpenModal extends _react.Component {
  constructor(props) {
    super(props);

    this.closeModal = () => {
      this.props.closeQuickOpen();
    };

    this.dropGoto = query => {
      return query.split(":")[0];
    };

    this.searchSources = query => {
      const { sources } = this.props;
      const results = query == "" ? sources : filter(sources, this.dropGoto(query));
      return this.setState({ results });
    };

    this.searchSymbols = query => {
      const { symbols: { functions, variables } } = this.props;

      let results = functions;
      if (this.isVariableQuery()) {
        results = variables;
      }
      if (query === "@" || query === "#") {
        return this.setState({ results });
      }

      this.setState({ results: filter(results, query.slice(1)) });
    };

    this.searchShortcuts = query => {
      const results = (0, _quickOpen.formatShortcutResults)();
      if (query == "?") {
        this.setState({ results });
      } else {
        this.setState({ results: filter(results, query.slice(1)) });
      }
    };

    this.showTopSources = () => {
      const { tabs, sources } = this.props;
      if (tabs.length > 0) {
        this.setState({
          results: sources.filter(source => tabs.includes(source.url))
        });
      } else {
        this.setState({ results: sources.slice(0, 100) });
      }
    };

    this.updateResults = query => {
      if (this.isGotoQuery()) {
        return;
      }

      if (query == "" && !this.isShortcutQuery()) {
        return this.showTopSources();
      }

      if (this.isSymbolSearch()) {
        return this.searchSymbols(query);
      }

      if (this.isShortcutQuery()) {
        return this.searchShortcuts(query);
      }
      return this.searchSources(query);
    };

    this.setModifier = item => {
      if (["@", "#", ":"].includes(item.id)) {
        this.props.setQuickOpenQuery(item.id);
      }
    };

    this.selectResultItem = (e, item) => {
      if (item == null) {
        return;
      }

      if (this.isShortcutQuery()) {
        return this.setModifier(item);
      }

      if (this.isGotoSourceQuery()) {
        const location = (0, _quickOpen.parseLineColumn)(this.props.query);
        return this.gotoLocation(_extends({}, location, { sourceId: item.id }));
      }

      if (this.isSymbolSearch()) {
        return this.gotoLocation({
          line: item.location && item.location.start ? item.location.start.line : 0
        });
      }

      this.gotoLocation({ sourceId: item.id, line: 0 });
    };

    this.onSelectResultItem = item => {
      const { selectLocation, selectedSource, highlightLineRange } = this.props;
      if (!this.isSymbolSearch() || selectedSource == null) {
        return;
      }

      if (this.isVariableQuery()) {
        const line = item.location && item.location.start ? item.location.start.line : 0;
        return selectLocation({
          sourceId: selectedSource.get("id"),
          line,
          column: null
        });
      }

      if (this.isFunctionQuery()) {
        return highlightLineRange(_extends({}, item.location != null ? { start: item.location.start.line, end: item.location.end.line } : {}, {
          sourceId: selectedSource.get("id")
        }));
      }
    };

    this.traverseResults = e => {
      const direction = e.key === "ArrowUp" ? -1 : 1;
      const { selectedIndex, results } = this.state;
      const resultCount = this.getResultCount();
      const index = selectedIndex + direction;
      const nextIndex = (index + resultCount) % resultCount;

      this.setState({ selectedIndex: nextIndex });

      if (results != null) {
        this.onSelectResultItem(results[nextIndex]);
      }
    };

    this.gotoLocation = location => {
      const { selectLocation, selectedSource } = this.props;
      const selectedSourceId = selectedSource ? selectedSource.get("id") : "";
      if (location != null) {
        const sourceId = location.sourceId ? location.sourceId : selectedSourceId;
        selectLocation({
          sourceId,
          line: location.line,
          column: location.column || null
        });
        this.closeModal();
      }
    };

    this.onChange = e => {
      const { selectedSource, setQuickOpenQuery } = this.props;
      setQuickOpenQuery(e.target.value);
      const noSource = !selectedSource || !selectedSource.get("text");
      if (this.isSymbolSearch() && noSource || this.isGotoQuery()) {
        return;
      }
      this.updateResults(e.target.value);
    };

    this.onKeyDown = e => {
      const { enabled, query } = this.props;
      const { results, selectedIndex } = this.state;

      if (!this.isGotoQuery() && (!enabled || !results)) {
        return;
      }

      if (e.key === "Enter") {
        if (this.isGotoQuery()) {
          const location = (0, _quickOpen.parseLineColumn)(query);
          return this.gotoLocation(location);
        }

        if (results) {
          if (this.isShortcutQuery()) {
            return this.setModifier(results[selectedIndex]);
          }

          return this.selectResultItem(e, results[selectedIndex]);
        }
      }

      if (e.key === "Tab") {
        return this.closeModal();
      }

      if (["ArrowUp", "ArrowDown"].includes(e.key)) {
        return this.traverseResults(e);
      }
    };

    this.getResultCount = () => {
      const results = this.state.results;
      return results && results.length ? results.length : 0;
    };

    this.isFunctionQuery = () => this.props.searchType === "functions";

    this.isVariableQuery = () => this.props.searchType === "variables";

    this.isSymbolSearch = () => this.isFunctionQuery() || this.isVariableQuery();

    this.isGotoQuery = () => this.props.searchType === "goto";

    this.isGotoSourceQuery = () => this.props.searchType === "gotoSource";

    this.isShortcutQuery = () => this.props.searchType === "shortcuts";

    this.isSourcesQuery = () => this.props.searchType === "sources";

    this.isSourceSearch = () => this.isSourcesQuery() || this.isGotoSourceQuery();

    this.renderHighlight = (candidateString, query, name) => {
      const html = _fuzzaldrinPlus2.default.wrap(candidateString, query);

      return _react2.default.createElement("div", { dangerouslySetInnerHTML: { __html: html } });
    };

    this.highlightMatching = (query, results) => {
      let newQuery = query;
      if (newQuery === "") {
        return results;
      }
      newQuery = query.replace(/[@:#?]/gi, " ");

      return results.map(result => {
        return _extends({}, result, {
          title: this.renderHighlight(result.title, (0, _path.basename)(newQuery), "title")
        }, result.subtitle != null && !this.isSymbolSearch() ? {
          subtitle: this.renderHighlight(result.subtitle, newQuery, "subtitle")
        } : null);
      });
    };

    this.hasPrefix = () => /^[:#@]/.test(this.props.query);

    this.state = { results: null, selectedIndex: 0 };
  }

  componentDidMount() {
    const { query, shortcutsModalEnabled, toggleShortcutsModal } = this.props;

    this.updateResults(query);

    if (shortcutsModalEnabled) {
      toggleShortcutsModal();
    }
  }

  componentDidUpdate(prevProps) {
    const nowEnabled = !prevProps.enabled && this.props.enabled;
    const queryChanged = prevProps.query !== this.props.query;

    if (this.refs.resultList && this.refs.resultList.refs) {
      (0, _resultList.scrollList)(this.refs.resultList.refs, this.state.selectedIndex, nowEnabled || !queryChanged);
    }

    if (nowEnabled || queryChanged) {
      this.updateResults(this.props.query);
    }
  }

  // Query helpers


  /* eslint-disable react/no-danger */


  shouldShowErrorEmoji() {
    const { query } = this.props;
    if (this.isGotoQuery()) {
      return !/^:\d*$/.test(query);
    }
    return !this.getResultCount() && !!query;
  }

  render() {
    const { enabled, query } = this.props;
    const { selectedIndex, results } = this.state;

    if (!enabled) {
      return null;
    }
    const newResults = results && results.slice(0, 100);
    const items = this.highlightMatching(query, newResults || []);
    const expanded = !!items && items.length > 0;
    return _react2.default.createElement(
      _Modal2.default,
      { "in": enabled, handleClose: this.closeModal },
      _react2.default.createElement(_SearchInput2.default, {
        query: query,
        count: this.getResultCount(),
        placeholder: L10N.getStr("sourceSearch.search"),
        summaryMsg: "",
        showErrorEmoji: this.shouldShowErrorEmoji(),
        onChange: this.onChange,
        onKeyDown: this.onKeyDown,
        handleClose: this.closeModal,
        hasPrefix: this.hasPrefix(),
        expanded: expanded,
        selectedItemId: expanded && items[selectedIndex] ? items[selectedIndex].id : ""
      }),
      newResults && _react2.default.createElement(_ResultList2.default, _extends({
        key: "results",
        items: items,
        selected: selectedIndex,
        selectItem: this.selectResultItem,
        ref: "resultList",
        expanded: expanded
      }, this.isSourceSearch() ? { size: "big" } : {}))
    );
  }
}

exports.QuickOpenModal = QuickOpenModal; /* istanbul ignore next: ignoring testing of redux connection stuff */

function mapStateToProps(state) {
  const selectedSource = (0, _selectors.getSelectedSource)(state);
  let symbols = null;
  if (selectedSource != null) {
    symbols = (0, _selectors.getSymbols)(state, selectedSource.toJS());
  }

  return {
    enabled: (0, _selectors.getQuickOpenEnabled)(state),
    sources: (0, _quickOpen.formatSources)((0, _selectors.getSources)(state)),
    selectedSource,
    symbols: (0, _quickOpen.formatSymbols)(symbols),
    query: (0, _selectors.getQuickOpenQuery)(state),
    searchType: (0, _selectors.getQuickOpenType)(state),
    tabs: (0, _selectors.getTabs)(state).toArray()
  };
}

/* istanbul ignore next: ignoring testing of redux connection stuff */
exports.default = (0, _reactRedux.connect)(mapStateToProps, _actions2.default)(QuickOpenModal);

/***/ }),
/* 1653 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.promise = exports.PROMISE = undefined;

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; }; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                                                                                                                                                                                   * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                                                                                                                                                                                                                                   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

var _lodash = __webpack_require__(2);

var _DevToolsUtils = __webpack_require__(1432);

let seqIdVal = 1;

function seqIdGen() {
  return seqIdVal++;
}

function filterAction(action) {
  return (0, _lodash.fromPairs)((0, _lodash.toPairs)(action).filter(pair => pair[0] !== PROMISE));
}

function promiseMiddleware({
  dispatch,
  getState
}) {
  return next => action => {
    if (!(PROMISE in action)) {
      return next(action);
    }

    const promiseInst = action[PROMISE];
    const seqId = seqIdGen().toString();

    // Create a new action that doesn't have the promise field and has
    // the `seqId` field that represents the sequence id
    action = _extends({}, filterAction(action), { seqId });

    dispatch(_extends({}, action, { status: "start" }));

    // Return the promise so action creators can still compose if they
    // want to.
    return new Promise((resolve, reject) => {
      promiseInst.then(value => {
        (0, _DevToolsUtils.executeSoon)(() => {
          dispatch(_extends({}, action, { status: "done", value: value }));
          resolve(value);
        });
      }, error => {
        (0, _DevToolsUtils.executeSoon)(() => {
          dispatch(_extends({}, action, {
            status: "error",
            error: error.message || error
          }));
          reject(error);
        });
      });
    });
  };
}

const PROMISE = exports.PROMISE = "@@dispatch/promise";
exports.promise = promiseMiddleware;

/***/ }),
/* 1654 */,
/* 1655 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.fetchScopes = fetchScopes;

var _selectors = __webpack_require__(1352);

var _mapScopes = __webpack_require__(1634);

var _promise = __webpack_require__(1653);

function fetchScopes() {
  return async function ({ dispatch, getState, client, sourceMaps }) {
    const frame = (0, _selectors.getSelectedFrame)(getState());
    if (!frame || (0, _selectors.getGeneratedFrameScope)(getState(), frame.id)) {
      return;
    }

    const scopes = dispatch({
      type: "ADD_SCOPES",
      frame,
      [_promise.PROMISE]: client.getFrameScopes(frame)
    });

    dispatch((0, _mapScopes.mapScopes)(scopes, frame));
  };
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/***/ }),
/* 1656 */,
/* 1657 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getAsyncTimes = getAsyncTimes;
exports.steppingTimings = steppingTimings;

var _lodash = __webpack_require__(2);

function getAsyncTimes(name) {
  return (0, _lodash.zip)(window.performance.getEntriesByName(`${name}_start`), window.performance.getEntriesByName(`${name}_end`)).map(([start, end]) => +(end.startTime - start.startTime).toPrecision(2));
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

function getTimes(name) {
  return window.performance.getEntriesByName(name).map(time => +time.duration.toPrecision(2));
}

function getStats(times) {
  if (times.length == 0) {
    return { times: [], avg: null, median: null };
  }
  const avg = times.reduce((sum, time) => time + sum, 0) / times.length;
  const sortedtimings = [...times].sort((a, b) => a - b);
  const median = sortedtimings[times.length / 2];
  return {
    times,
    avg: +avg.toPrecision(2),
    median: +median.toPrecision(2)
  };
}

function steppingTimings() {
  const commandTimings = getAsyncTimes("COMMAND");
  const pausedTimings = getTimes("PAUSED");

  return {
    commands: getStats(commandTimings),
    paused: getStats(pausedTimings)
  };
}

// console.log("..", asyncTimes("COMMAND"));

/***/ }),
/* 1658 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _redux = __webpack_require__(3);

var _waitService = __webpack_require__(1659);

var _log = __webpack_require__(1660);

var _history = __webpack_require__(1661);

var _promise = __webpack_require__(1653);

var _thunk = __webpack_require__(1662);

var _timing = __webpack_require__(1663);

/**
 * 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:
 *        - log: log all dispatched actions to console
 *        - history: an array to store every action in. Should only be
 *                   used in tests.
 *        - middleware: array of middleware to be included in the redux store
 * @memberof utils/create-store
 * @static
 */


/**
 * @memberof utils/create-store
 * @static
 */
const configureStore = (opts = {}) => {
  const middleware = [(0, _thunk.thunk)(opts.makeThunkArgs), _promise.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.
  _waitService.waitUntilService];

  if (opts.history) {
    middleware.push((0, _history.history)(opts.history));
  }

  if (opts.middleware) {
    opts.middleware.forEach(fn => middleware.push(fn));
  }

  if (opts.log) {
    middleware.push(_log.log);
  }

  if (opts.timing) {
    middleware.push(_timing.timing);
  }

  // Hook in the redux devtools browser extension if it exists
  const devtoolsExt = typeof window === "object" && window.devToolsExtension ? window.devToolsExtension() : f => f;

  return (0, _redux.applyMiddleware)(...middleware)(devtoolsExt(_redux.createStore));
}; /* This Source Code Form is subject to the terms of the Mozilla Public
    * License, v. 2.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 */

/**
 * Redux store utils
 * @module utils/create-store
 */

exports.default = configureStore;

/***/ }),
/* 1659 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.waitUntilService = waitUntilService;
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 */

/**
 * 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 === "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;
  };
}

/***/ }),
/* 1660 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

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; }; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                                                                                                                                                                                   * License, v. 2.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 */

exports.log = log;

var _devtoolsConfig = __webpack_require__(1355);

const blacklist = ["SET_POPUP_OBJECT_PROPERTIES", "SET_SYMBOLS", "OUT_OF_SCOPE_LOCATIONS"];

function cloneAction(action) {
  action = action || {};
  action = _extends({}, action);

  // ADD_TAB, ...
  if (action.source && action.source.text) {
    const source = _extends({}, action.source, { text: "" });
    action.source = source;
  }

  if (action.sources) {
    const sources = action.sources.slice(0, 30).map(source => {
      const url = !source.url || source.url.includes("data:") ? "" : source.url;
      return _extends({}, source, { url });
    });
    action.sources = sources;
  }

  // LOAD_SOURCE_TEXT
  if (action.text) {
    action.text = "";
  }

  if (action.value && action.value.text) {
    const value = _extends({}, action.value, { text: "" });
    action.value = value;
  }

  return action;
}

function formatFrame(frame) {
  const { id, location, displayName } = frame;
  return { id, location, displayName };
}

function formatPause(pause) {
  return _extends({}, pause, {
    pauseInfo: { why: pause.why },
    scopes: [],
    frames: pause.frames.map(formatFrame),
    loadedObjects: []
  });
}

function serializeAction(action) {
  try {
    action = cloneAction(action);
    if (blacklist.includes(action.type)) {
      action = {};
    }

    if (action.type === "PAUSED") {
      action = formatPause(action);
    }

    // dump(`> ${action.type}...\n ${JSON.stringify(action)}\n`);
    return JSON.stringify(action);
  } catch (e) {
    console.error(e);
  }
}

/**
 * A middleware that logs all actions coming through the system
 * to the console.
 */
function log({ dispatch, getState }) {
  return next => action => {
    const asyncMsg = !action.status ? "" : `[${action.status}]`;

    if ((0, _devtoolsConfig.isTesting)()) {
      dump(`[ACTION] ${action.type} ${asyncMsg} - ${serializeAction(action)}\n`);
    } else {
      console.log(action, asyncMsg);
    }

    next(action);
  };
}

/***/ }),
/* 1661 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.history = undefined;

var _devtoolsConfig = __webpack_require__(1355);

/**
 * A middleware that stores every action coming through the store in the passed
 * in logging object. Should only be used for tests, as it collects all
 * action information, which will cause memory bloat.
 */
const history = exports.history = (log = []) => ({
  dispatch,
  getState
}) => {
  return next => action => {
    if ((0, _devtoolsConfig.isDevelopment)()) {
      log.push(action);
    }

    return next(action);
  };
}; /* This Source Code Form is subject to the terms of the Mozilla Public
    * License, v. 2.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 */

/***/ }),
/* 1662 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.thunk = thunk;


/**
 * A middleware that allows thunks (functions) to be dispatched. If
 * it's a thunk, it is called with an argument that contains
 * `dispatch`, `getState`, and any additional args passed in via the
 * middleware constructure. This allows the action to create multiple
 * actions (most likely asynchronously).
 */
function thunk(makeArgs) {
  return ({ dispatch, getState }) => {
    const args = { dispatch, getState };

    return next => action => {
      return typeof action === "function" ? action(makeArgs ? makeArgs(args, getState()) : args) : next(action);
    };
  };
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.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 */

/***/ }),
/* 1663 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.timing = timing;
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 */

/**
 * Redux middleware that sets performance markers for all actions such that they
 * will appear in performance tooling under the User Timing API
 */

const mark = window.performance && window.performance.mark ? window.performance.mark.bind(window.performance) : () => {};

const measure = window.performance && window.performance.measure ? window.performance.measure.bind(window.performance) : () => {};

function timing(store) {
  return next => action => {
    mark(`${action.type}_start`);
    const result = next(action);
    mark(`${action.type}_end`);
    measure(`${action.type}`, `${action.type}_start`, `${action.type}_end`);
    return result;
  };
}

/***/ }),
/* 1664 */,
/* 1665 */,
/* 1666 */,
/* 1667 */,
/* 1668 */,
/* 1669 */,
/* 1670 */,
/* 1671 */,
/* 1672 */,
/* 1673 */,
/* 1674 */,
/* 1675 */,
/* 1676 */,
/* 1677 */,
/* 1678 */,
/* 1679 */,
/* 1680 */,
/* 1681 */,
/* 1682 */,
/* 1683 */,
/* 1684 */,
/* 1685 */,
/* 1686 */,
/* 1687 */,
/* 1688 */,
/* 1689 */,
/* 1690 */,
/* 1691 */,
/* 1692 */,
/* 1693 */,
/* 1694 */,
/* 1695 */,
/* 1696 */,
/* 1697 */,
/* 1698 */,
/* 1699 */,
/* 1700 */,
/* 1701 */,
/* 1702 */,
/* 1703 */,
/* 1704 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

__webpack_require__(1705);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

const Badge = ({ children }) => _react2.default.createElement(
  "div",
  { className: "badge text-white text-center" },
  children
);

exports.default = Badge;

/***/ }),
/* 1705 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1706 */,
/* 1707 */,
/* 1708 */,
/* 1709 */,
/* 1710 */,
/* 1711 */,
/* 1712 */,
/* 1713 */,
/* 1714 */,
/* 1715 */,
/* 1716 */,
/* 1717 */,
/* 1718 */,
/* 1719 */,
/* 1720 */,
/* 1721 */,
/* 1722 */,
/* 1723 */,
/* 1724 */,
/* 1725 */,
/* 1726 */,
/* 1727 */,
/* 1728 */,
/* 1729 */,
/* 1730 */,
/* 1731 */,
/* 1732 */,
/* 1733 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 version=\"1.1\" id=\"Layer_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\" viewBox=\"0 0 1200 1200\" style=\"enable-background:new 0 0 1200 1200;\" xml:space=\"preserve\"><style type=\"text/css\"> .st0{clip-path:url(#SVGID_2_);} .st1{fill:url(#SVGID_3_);} .st2{fill:url(#SVGID_4_);} .st3{opacity:0.28;fill:#C24411;} .st4{fill:#FFFFFF;} </style><g><defs><path id=\"SVGID_1_\" d=\"M1088.7,1165.5H111.3c-42.4,0-76.7-34.4-76.7-76.7V111.3c0-42.4,34.4-76.7,76.7-76.7h977.4 c42.4,0,76.7,34.4,76.7,76.7v977.4C1165.5,1131.1,1131.1,1165.5,1088.7,1165.5z\"></path></defs><clipPath id=\"SVGID_2_\"><use xlink:href=\"#SVGID_1_\" style=\"overflow:visible;\"></use></clipPath><g class=\"st0\"><linearGradient id=\"SVGID_3_\" gradientUnits=\"userSpaceOnUse\" x1=\"426.6738\" y1=\"482.4993\" x2=\"1284.413\" y2=\"1239.9835\"><stop offset=\"0\" style=\"stop-color:#F77122\"></stop><stop offset=\"1\" style=\"stop-color:#D6560A\"></stop></linearGradient><polygon class=\"st1\" points=\"573.6,1896.7 -87.9,1286.6 1204.5,-115 1866,495 \"></polygon><linearGradient id=\"SVGID_4_\" gradientUnits=\"userSpaceOnUse\" x1=\"1128.9907\" y1=\"-135.8044\" x2=\"-496.2911\" y2=\"788.1552\"><stop offset=\"0\" style=\"stop-color:#D6560A\"></stop><stop offset=\"1\" style=\"stop-color:#F77122\"></stop></linearGradient><polygon class=\"st2\" points=\"-66.9,1307.6 -644.1,775.5 648.4,-626.1 1225.5,-94 \"></polygon><rect x=\"-143.7\" y=\"-143.6\" transform=\"matrix(0.9658 -0.2591 0.2591 0.9658 -201.3159 92.1307)\" class=\"st3\" width=\"785\" height=\"1906.5\"></rect></g></g><g><path class=\"st4\" d=\"M384.9,900.1H245.4v-45.3h85.6V345.2h-85.6v-45.3h139.5V900.1z\"></path><path class=\"st4\" d=\"M745.7,463c-16.1,60.4-35,118.2-56.7,173.6c-21.7,55.4-42.7,105-62.9,148.8H574 c-20.3-43.8-41.2-93.4-62.9-148.8c-21.7-55.4-40.6-113.2-56.7-173.6h62c5,20.3,10.8,41.9,17.7,64.8s14,45.7,21.7,68.2 c7.6,22.5,15.4,44.3,23.2,65.4c7.8,21.1,15.3,39.9,22.3,56.4c7-16.5,14.5-35.3,22.3-56.4c7.8-21.1,15.6-42.9,23.2-65.4 c7.6-22.5,14.9-45.3,21.7-68.2s12.7-44.5,17.7-64.8H745.7z\"></path><path class=\"st4\" d=\"M815.1,299.9h139.5v45.3h-85.6v509.6h85.6v45.3H815.1V299.9z\"></path></g></svg>"

/***/ }),
/* 1734 */,
/* 1735 */,
/* 1736 */,
/* 1737 */,
/* 1738 */,
/* 1739 */,
/* 1740 */,
/* 1741 */,
/* 1742 */,
/* 1743 */,
/* 1744 */,
/* 1745 */,
/* 1746 */,
/* 1747 */,
/* 1748 */,
/* 1749 */,
/* 1750 */,
/* 1751 */,
/* 1752 */,
/* 1753 */,
/* 1754 */,
/* 1755 */,
/* 1756 */,
/* 1757 */,
/* 1758 */
/***/ (function(module, exports, __webpack_require__) {

"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 (true) {
    module.exports = f(__webpack_require__(0));
    /* global define */
  } else if (typeof define === 'function' && define.amd) {
    define(['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;
});



/***/ }),
/* 1759 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

var EventEmitter = function EventEmitter() {};
module.exports = EventEmitter;

const promise = __webpack_require__(1769);

/**
 * Decorate an object with event emitter functionality.
 *
 * @param Object aObjectToDecorate
 *        Bind all public methods of EventEmitter to
 *        the aObjectToDecorate object.
 */
EventEmitter.decorate = function EventEmitter_decorate(aObjectToDecorate) {
  let emitter = new EventEmitter();
  aObjectToDecorate.on = emitter.on.bind(emitter);
  aObjectToDecorate.off = emitter.off.bind(emitter);
  aObjectToDecorate.once = emitter.once.bind(emitter);
  aObjectToDecorate.emit = emitter.emit.bind(emitter);
};

EventEmitter.prototype = {
  /**
   * Connect a listener.
   *
   * @param string aEvent
   *        The event name to which we're connecting.
   * @param function aListener
   *        Called when the event is fired.
   */
  on: function EventEmitter_on(aEvent, aListener) {
    if (!this._eventEmitterListeners) this._eventEmitterListeners = new Map();
    if (!this._eventEmitterListeners.has(aEvent)) {
      this._eventEmitterListeners.set(aEvent, []);
    }
    this._eventEmitterListeners.get(aEvent).push(aListener);
  },

  /**
   * Listen for the next time an event is fired.
   *
   * @param string aEvent
   *        The event name to which we're connecting.
   * @param function aListener
   *        (Optional) Called when the event is fired. Will be called at most
   *        one time.
   * @return promise
   *        A promise which is resolved when the event next happens. The
   *        resolution value of the promise is the first event argument. If
   *        you need access to second or subsequent event arguments (it's rare
   *        that this is needed) then use aListener
   */
  once: function EventEmitter_once(aEvent, aListener) {
    let deferred = promise.defer();

    let handler = (aEvent, aFirstArg, ...aRest) => {
      this.off(aEvent, handler);
      if (aListener) {
        aListener.apply(null, [aEvent, aFirstArg, ...aRest]);
      }
      deferred.resolve(aFirstArg);
    };

    handler._originalListener = aListener;
    this.on(aEvent, handler);

    return deferred.promise;
  },

  /**
   * Remove a previously-registered event listener.  Works for events
   * registered with either on or once.
   *
   * @param string aEvent
   *        The event name whose listener we're disconnecting.
   * @param function aListener
   *        The listener to remove.
   */
  off: function EventEmitter_off(aEvent, aListener) {
    if (!this._eventEmitterListeners) return;
    let listeners = this._eventEmitterListeners.get(aEvent);
    if (listeners) {
      this._eventEmitterListeners.set(aEvent, listeners.filter(l => {
        return l !== aListener && l._originalListener !== aListener;
      }));
    }
  },

  /**
   * Emit an event.  All arguments to this method will
   * be sent to listener functions.
   */
  emit: function EventEmitter_emit(aEvent) {
    if (!this._eventEmitterListeners || !this._eventEmitterListeners.has(aEvent)) {
      return;
    }

    let originalListeners = this._eventEmitterListeners.get(aEvent);
    for (let listener of this._eventEmitterListeners.get(aEvent)) {
      // If the object was destroyed during event emission, stop
      // emitting.
      if (!this._eventEmitterListeners) {
        break;
      }

      // If listeners were removed during emission, make sure the
      // event handler we're going to fire wasn't removed.
      if (originalListeners === this._eventEmitterListeners.get(aEvent) || this._eventEmitterListeners.get(aEvent).some(l => l === listener)) {
        try {
          listener.apply(null, arguments);
        } catch (ex) {
          // Prevent a bad listener from interfering with the others.
          let msg = ex + ": " + ex.stack;
          //console.error(msg);
          console.log(msg);
        }
      }
    }
  }
};

/***/ }),
/* 1760 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


(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_invalid, csc_row, csc_score, i, j, m, miss_budget, miss_left, mm, 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;
    j = -1;
    while (++j < n) {
      score_row[j] = 0;
      csc_row[j] = 0;
    }
    i = subject_lw.indexOf(query_lw[0]);
    if (i > -1) {
      i--;
    }
    mm = subject_lw.lastIndexOf(query_lw[n - 1], m);
    if (mm > i) {
      m = mm + 1;
    }
    csc_invalid = true;
    while (++i < m) {
      si_lw = subject_lw[i];
      if (preparedQuery.charCodes[si_lw.charCodeAt(0)] == null) {
        if (csc_invalid !== true) {
          j = -1;
          while (++j < n) {
            csc_row[j] = 0;
          }
          csc_invalid = true;
        }
        continue;
      }
      score = 0;
      score_diag = 0;
      csc_diag = 0;
      record_miss = true;
      csc_invalid = false;
      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 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 === 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(undefined);

/***/ }),
/* 1761 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


(function () {
  var computeScore, countDir, file_coeff, getExtension, getExtensionScore, isMatch, scorePath, scoreSize, tau_depth, _ref;

  _ref = __webpack_require__(1760), isMatch = _ref.isMatch, computeScore = _ref.computeScore, scoreSize = _ref.scoreSize;

  tau_depth = 13;

  file_coeff = 1.2;

  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(undefined);

/***/ }),
/* 1762 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


(function () {
  var Query, coreChars, countDir, getCharCodes, getExtension, opt_char_re, truncatedUpperCase, _ref;

  _ref = __webpack_require__(1761), 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(undefined);

/***/ }),
/* 1763 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
    value: true
});

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 _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; }; }();

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _propTypes = __webpack_require__(20);

var _util = __webpack_require__(1777);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

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(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; }

var process = process || { env: {} };

var InlineSVG = function (_React$Component) {
    _inherits(InlineSVG, _React$Component);

    function InlineSVG() {
        _classCallCheck(this, InlineSVG);

        return _possibleConstructorReturn(this, (InlineSVG.__proto__ || Object.getPrototypeOf(InlineSVG)).apply(this, arguments));
    }

    _createClass(InlineSVG, [{
        key: 'componentWillReceiveProps',
        value: function componentWillReceiveProps(_ref) {
            var children = _ref.children;

            if ("production" !== process.env.NODE_ENV && children != null) {
                console.info('<InlineSVG />: `children` prop will be ignored.');
            }
        }
    }, {
        key: 'render',
        value: function render() {
            var Element = void 0,
                __html = void 0,
                svgProps = void 0;

            var _props = this.props,
                element = _props.element,
                raw = _props.raw,
                src = _props.src,
                otherProps = _objectWithoutProperties(_props, ['element', 'raw', 'src']);

            if (raw === true) {
                Element = 'svg';
                svgProps = (0, _util.extractSVGProps)(src);
                __html = (0, _util.getSVGFromSource)(src).innerHTML;
            }
            __html = __html || src;
            Element = Element || element;
            svgProps = svgProps || {};

            return _react2.default.createElement(Element, _extends({}, svgProps, otherProps, { src: null, children: null,
                dangerouslySetInnerHTML: { __html: __html } }));
        }
    }]);

    return InlineSVG;
}(_react2.default.Component);

exports.default = InlineSVG;


InlineSVG.defaultProps = {
    element: 'i',
    raw: false,
    src: ''
};

InlineSVG.propTypes = {
    src: _propTypes.string.isRequired,
    element: _propTypes.string,
    raw: _propTypes.bool
};

/***/ }),
/* 1764 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

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 _classnames = __webpack_require__(175);

var _classnames2 = _interopRequireDefault(_classnames);

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

__webpack_require__(1788);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

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; } /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                                                                                                                                              * License, v. 2.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 CommandBarButton = props => {
  const { children, className, pressed = false } = props,
        rest = _objectWithoutProperties(props, ["children", "className", "pressed"]);

  return _react2.default.createElement(
    "button",
    _extends({
      "aria-pressed": pressed,
      className: (0, _classnames2.default)("command-bar-button", className)
    }, rest),
    children
  );
};

exports.default = CommandBarButton;

/***/ }),
/* 1765 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getBindingVariables = getBindingVariables;

var _lodash = __webpack_require__(2);

// Create the tree nodes representing all the variables and arguments
// for the bindings from a scope.


// VarAndBindingsPair actually is [name: string, contents: BindingContents]


// Scope's bindings field which holds variables and arguments
function getBindingVariables(bindings, parentName) {
  const args = bindings.arguments.map(arg => (0, _lodash.toPairs)(arg)[0]);
  const variables = (0, _lodash.toPairs)(bindings.variables);

  return args.concat(variables).map(binding => {
    const name = binding[0];
    const contents = binding[1];
    return {
      name,
      path: `${parentName}/${name}`,
      contents
    };
  });
} /* eslint max-nested-callbacks: ["error", 4] */

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/***/ }),
/* 1766 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getFramePopVariables = getFramePopVariables;
exports.getThisVariable = getThisVariable;
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 getFramePopVariables(why, path) {
  const vars = [];

  if (why && why.frameFinished) {
    const frameFinished = why.frameFinished;

    // Always display a `throw` property if present, even if it is falsy.
    if (Object.prototype.hasOwnProperty.call(frameFinished, "throw")) {
      vars.push({
        name: "<exception>",
        path: `${path}/<exception>`,
        contents: { value: frameFinished.throw }
      });
    }

    if (Object.prototype.hasOwnProperty.call(frameFinished, "return")) {
      const returned = frameFinished.return;

      // Do not display undefined. Do display falsy values like 0 and false. The
      // protocol grip for undefined is a JSON object: { type: "undefined" }.
      if (typeof returned !== "object" || returned.type !== "undefined") {
        vars.push({
          name: "<return>",
          path: `${path}/<return>`,
          contents: { value: returned }
        });
      }
    }
  }

  return vars;
}

function getThisVariable(this_, path) {
  if (!this_) {
    return null;
  }

  return {
    name: "<this>",
    path: `${path}/<this>`,
    contents: { value: this_ }
  };
}

/***/ }),
/* 1767 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 Menu = __webpack_require__(1768);
const MenuItem = __webpack_require__(1770);
const { PrefsHelper } = __webpack_require__(1771);
const Services = __webpack_require__(22);
const KeyShortcuts = __webpack_require__(1772);
const { ZoomKeys } = __webpack_require__(1773);
const EventEmitter = __webpack_require__(1759);

module.exports = {
  KeyShortcuts,
  Menu,
  MenuItem,
  PrefsHelper,
  Services,
  ZoomKeys,
  EventEmitter
};

/***/ }),
/* 1768 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 EventEmitter = __webpack_require__(1759);

function inToolbox() {
  return window.parent.document.documentURI == "about:devtools-toolbox";
}

/**
 * A partial implementation of the Menu API provided by electron:
 * https://github.com/electron/electron/blob/master/docs/api/menu.md.
 *
 * Extra features:
 *  - Emits an 'open' and 'close' event when the menu is opened/closed

 * @param String id (non standard)
 *        Needed so tests can confirm the XUL implementation is working
 */
function Menu({ id = null } = {}) {
  this.menuitems = [];
  this.id = id;

  Object.defineProperty(this, "items", {
    get() {
      return this.menuitems;
    }
  });

  EventEmitter.decorate(this);
}

/**
 * Add an item to the end of the Menu
 *
 * @param {MenuItem} menuItem
 */
Menu.prototype.append = function (menuItem) {
  this.menuitems.push(menuItem);
};

/**
 * Add an item to a specified position in the menu
 *
 * @param {int} pos
 * @param {MenuItem} menuItem
 */
Menu.prototype.insert = function (pos, menuItem) {
  throw Error("Not implemented");
};

/**
 * Show the Menu at a specified location on the screen
 *
 * Missing features:
 *   - browserWindow - BrowserWindow (optional) - Default is null.
 *   - positioningItem Number - (optional) OS X
 *
 * @param {int} screenX
 * @param {int} screenY
 * @param Toolbox toolbox (non standard)
 *        Needed so we in which window to inject XUL
 */
Menu.prototype.popup = function (screenX, screenY, toolbox) {
  let doc = toolbox.doc;
  let popupset = doc.querySelector("popupset");
  // See bug 1285229, on Windows, opening the same popup multiple times in a
  // row ends up duplicating the popup. The newly inserted popup doesn't
  // dismiss the old one. So remove any previously displayed popup before
  // opening a new one.
  let popup = popupset.querySelector("menupopup[menu-api=\"true\"]");
  if (popup) {
    popup.hidePopup();
  }

  popup = this.createPopup(doc);
  popup.setAttribute("menu-api", "true");

  if (this.id) {
    popup.id = this.id;
  }
  this._createMenuItems(popup);

  // Remove the menu from the DOM once it's hidden.
  popup.addEventListener("popuphidden", e => {
    if (e.target === popup) {
      popup.remove();
      this.emit("close", popup);
    }
  });

  popup.addEventListener("popupshown", e => {
    if (e.target === popup) {
      this.emit("open", popup);
    }
  });

  popupset.appendChild(popup);
  popup.openPopupAtScreen(screenX, screenY, true);
};

Menu.prototype.createPopup = function (doc) {
  return doc.createElement("menupopup");
};

Menu.prototype._createMenuItems = function (parent) {
  let doc = parent.ownerDocument;
  this.menuitems.forEach(item => {
    if (!item.visible) {
      return;
    }

    if (item.submenu) {
      let menupopup = doc.createElement("menupopup");
      item.submenu._createMenuItems(menupopup);

      let menuitem = doc.createElement("menuitem");
      menuitem.setAttribute("label", item.label);
      if (!inToolbox()) {
        menuitem.textContent = item.label;
      }

      let menu = doc.createElement("menu");
      menu.appendChild(menuitem);
      menu.appendChild(menupopup);
      if (item.disabled) {
        menu.setAttribute("disabled", "true");
      }
      if (item.accesskey) {
        menu.setAttribute("accesskey", item.accesskey);
      }
      if (item.id) {
        menu.id = item.id;
      }
      parent.appendChild(menu);
    } else if (item.type === "separator") {
      let menusep = doc.createElement("menuseparator");
      parent.appendChild(menusep);
    } else {
      let menuitem = doc.createElement("menuitem");
      menuitem.setAttribute("label", item.label);

      if (!inToolbox()) {
        menuitem.textContent = item.label;
      }

      menuitem.addEventListener("command", () => item.click());

      if (item.type === "checkbox") {
        menuitem.setAttribute("type", "checkbox");
      }
      if (item.type === "radio") {
        menuitem.setAttribute("type", "radio");
      }
      if (item.disabled) {
        menuitem.setAttribute("disabled", "true");
      }
      if (item.checked) {
        menuitem.setAttribute("checked", "true");
      }
      if (item.accesskey) {
        menuitem.setAttribute("accesskey", item.accesskey);
      }
      if (item.id) {
        menuitem.id = item.id;
      }

      parent.appendChild(menuitem);
    }
  });
};

Menu.setApplicationMenu = () => {
  throw Error("Not implemented");
};

Menu.sendActionToFirstResponder = () => {
  throw Error("Not implemented");
};

Menu.buildFromTemplate = () => {
  throw Error("Not implemented");
};

module.exports = Menu;

/***/ }),
/* 1769 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 sham for https://dxr.mozilla.org/mozilla-central/source/toolkit/modules/Promise.jsm
 */

/**
 * Promise.jsm is mostly the Promise web API with a `defer` method. Just drop this in here,
 * and use the native web API (although building with webpack/babel, it may replace this
 * with it's own version if we want to target environments that do not have `Promise`.
 */

let p = typeof window != "undefined" ? window.Promise : Promise;
p.defer = function defer() {
  var resolve, reject;
  var promise = new Promise(function () {
    resolve = arguments[0];
    reject = arguments[1];
  });
  return {
    resolve: resolve,
    reject: reject,
    promise: promise
  };
};

module.exports = p;

/***/ }),
/* 1770 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 partial implementation of the MenuItem API provided by electron:
 * https://github.com/electron/electron/blob/master/docs/api/menu-item.md.
 *
 * Missing features:
 *   - id String - Unique within a single menu. If defined then it can be used
 *                 as a reference to this item by the position attribute.
 *   - role String - Define the action of the menu item; when specified the
 *                   click property will be ignored
 *   - sublabel String
 *   - accelerator Accelerator
 *   - icon NativeImage
 *   - position String - This field allows fine-grained definition of the
 *                       specific location within a given menu.
 *
 * Implemented features:
 *  @param Object options
 *    Function click
 *      Will be called with click(menuItem, browserWindow) when the menu item
 *       is clicked
 *    String type
 *      Can be normal, separator, submenu, checkbox or radio
 *    String label
 *    Boolean enabled
 *      If false, the menu item will be greyed out and unclickable.
 *    Boolean checked
 *      Should only be specified for checkbox or radio type menu items.
 *    Menu submenu
 *      Should be specified for submenu type menu items. If submenu is specified,
 *      the type: 'submenu' can be omitted. If the value is not a Menu then it
 *      will be automatically converted to one using Menu.buildFromTemplate.
 *    Boolean visible
 *      If false, the menu item will be entirely hidden.
 */
function MenuItem({
  accesskey = null,
  checked = false,
  click = () => {},
  disabled = false,
  label = "",
  id = null,
  submenu = null,
  type = "normal",
  visible = true
} = {}) {
  this.accesskey = accesskey;
  this.checked = checked;
  this.click = click;
  this.disabled = disabled;
  this.id = id;
  this.label = label;
  this.submenu = submenu;
  this.type = type;
  this.visible = visible;
}

module.exports = MenuItem;

/***/ }),
/* 1771 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 Services = __webpack_require__(22);
const EventEmitter = __webpack_require__(1759);

/**
 * 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", (prefName, prefValue) => {
 *     ...
 *   });
 *
 * @param string prefsRoot
 *        The root path to the required preferences branch.
 * @param object prefsBlueprint
 *        An object containing { accessorName: [prefType, prefName, prefDefault] } keys.
 */
function PrefsHelper(prefsRoot = "", prefsBlueprint = {}) {
  EventEmitter.decorate(this);

  let cache = new Map();

  for (let accessorName in prefsBlueprint) {
    let [prefType, prefName, prefDefault] = prefsBlueprint[accessorName];
    map(this, cache, accessorName, prefType, prefsRoot, prefName, prefDefault);
  }

  let 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
 * @return any
 */
function get(cache, prefType, prefsRoot, prefName) {
  let cachedPref = cache.get(prefName);
  if (cachedPref !== undefined) {
    return cachedPref;
  }
  let value = Services.prefs["get" + prefType + "Pref"]([prefsRoot, prefName].join("."));
  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 prefDefault
 * @param array serializer [optional]
 */
function map(self, cache, accessorName, prefType, prefsRoot, prefName, prefDefault, 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, prefDefault, {
      in: JSON.parse,
      out: JSON.stringify
    });
    return;
  }
  if (prefType == "Float") {
    map(self, cache, accessorName, "Char", prefsRoot, prefName, prefDefault, {
      in: Number.parseFloat,
      out: n => n + ""
    });
    return;
  }

  Object.defineProperty(self, accessorName, {
    get: () => {
      try {
        return serializer.in(get(cache, prefType, prefsRoot, prefName));
      } catch (e) {
        if (typeof prefDefault !== 'undefined') {
          return prefDefault;
        }
        throw e;
      }
    },
    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 (let accessorName in prefsBlueprint) {
    let [, 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: function () {
      this._branch = Services.prefs.getBranch(prefsRoot + ".");
      this._branch.addObserver("", this);
    },
    unregister: function () {
      this._branch.removeObserver("", this);
    },
    observe: function (subject, topic, prefName) {
      // If this particular pref isn't handled by the blueprint object,
      // even though it's in the specified branch, ignore it.
      let accessorName = accessorNameForPref(prefName, prefsBlueprint);
      if (!(accessorName in self)) {
        return;
      }
      cache.delete(prefName);
      self.emit("pref-changed", accessorName, self[accessorName]);
    }
  };
}

exports.PrefsHelper = PrefsHelper;

/***/ }),
/* 1772 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 { appinfo } = __webpack_require__(22);
const EventEmitter = __webpack_require__(1759);
const isOSX = appinfo.OS === "Darwin";

// 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 decribed 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) {
  let modifiers = str.split("+");
  let key = modifiers.pop();

  let shortcut = {
    ctrl: false,
    meta: false,
    alt: false,
    shift: false,
    // Set for character keys
    key: undefined,
    // Set for non-character keys
    keyCode: undefined
  };
  for (let 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) {
    // 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 = window.KeyboardEvent[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) {
  let 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("+");
};

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;
    }
    // Shift is a special modifier, it may implicitely be required if the
    // expected key is a special character accessible via shift.
    if (shortcut.shift != event.shiftKey && event.key && event.key.match(/[a-zA-Z]/)) {
      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.
    let 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 (let [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)) {
      let 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;

/***/ }),
/* 1773 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */



/**
 * Empty shim for "devtools/client/shared/zoom-keys" module
 *
 * Based on nsIMarkupDocumentViewer.fullZoom API
 * https://developer.mozilla.org/en-US/Firefox/Releases/3/Full_page_zoom
 */

exports.register = function (window) {};

/***/ }),
/* 1774 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/* WEBPACK VAR INJECTION */(function(process) {

(function () {
  var Query, defaultPathSeparator, filter, matcher, parseOptions, pathScorer, preparedQueryCache, scorer;

  filter = __webpack_require__(1775);

  matcher = __webpack_require__(1776);

  scorer = __webpack_require__(1760);

  pathScorer = __webpack_require__(1761);

  Query = __webpack_require__(1762);

  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(undefined);
/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(120)))

/***/ }),
/* 1775 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


(function () {
  var Query, pathScorer, pluckCandidates, scorer, sortCandidates;

  scorer = __webpack_require__(1760);

  pathScorer = __webpack_require__(1761);

  Query = __webpack_require__(1762);

  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(undefined);

/***/ }),
/* 1776 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


(function () {
  var basenameMatch, computeMatch, isMatch, isWordStart, match, mergeMatches, scoreAcronyms, scoreCharacter, scoreConsecutives, _ref;

  _ref = __webpack_require__(1760), 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(undefined);

/***/ }),
/* 1777 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
    value: true
});
exports.convertReactSVGDOMProperty = convertReactSVGDOMProperty;
exports.startsWith = startsWith;
exports.serializeAttrs = serializeAttrs;
exports.getSVGFromSource = getSVGFromSource;
exports.extractSVGProps = extractSVGProps;
// Transform DOM prop/attr names applicable to `<svg>` element but react-limited

function convertReactSVGDOMProperty(str) {
    return str.replace(/[-|:]([a-z])/g, function (g) {
        return g[1].toUpperCase();
    });
}

function startsWith(str, substring) {
    return str.indexOf(substring) === 0;
}

var DataPropPrefix = 'data-';
// Serialize `Attr` objects in `NamedNodeMap`
function serializeAttrs(map) {
    var ret = {};
    for (var prop, i = 0; i < map.length; i++) {
        var key = map[i].name;
        if (!startsWith(key, DataPropPrefix)) {
            prop = convertReactSVGDOMProperty(key);
        }
        ret[prop] = map[i].value;
    }
    return ret;
}

function getSVGFromSource(src) {
    var svgContainer = document.createElement('div');
    svgContainer.innerHTML = src;
    var svg = svgContainer.firstElementChild;
    svg.remove(); // deref from parent element
    return svg;
}

// get <svg /> element props
function extractSVGProps(src) {
    var map = getSVGFromSource(src).attributes;
    return map.length > 0 ? serializeAttrs(map) : null;
}

/***/ }),
/* 1778 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.isMinified = isMinified;


// Used to detect minification for automatic pretty printing
const SAMPLE_SIZE = 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/>. */

const INDENT_COUNT_THRESHOLD = 5;
const CHARACTER_LIMIT = 250;
const _minifiedCache = new Map();

function isMinified(source) {
  if (_minifiedCache.has(source.get("id"))) {
    return _minifiedCache.get(source.get("id"));
  }

  let text = source.get("text");
  if (!text) {
    return false;
  }

  let lineEndIndex = 0;
  let lineStartIndex = 0;
  let lines = 0;
  let indentCount = 0;
  let overCharLimit = false;

  // Strip comments.
  text = text.replace(/\/\*[\S\s]*?\*\/|\/\/(.+|\n)/g, "");

  while (lines++ < SAMPLE_SIZE) {
    lineEndIndex = text.indexOf("\n", lineStartIndex);
    if (lineEndIndex == -1) {
      break;
    }
    if (/^\s+/.test(text.slice(lineStartIndex, lineEndIndex))) {
      indentCount++;
    }
    // For files with no indents but are not minified.
    if (lineEndIndex - lineStartIndex > CHARACTER_LIMIT) {
      overCharLimit = true;
      break;
    }
    lineStartIndex = lineEndIndex + 1;
  }

  const minified = indentCount / lines * 100 < INDENT_COUNT_THRESHOLD || overCharLimit;

  _minifiedCache.set(source.id, minified);
  return minified;
}

/***/ }),
/* 1779 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getCallStackFrames = undefined;

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; }; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                                                                                                                                                                                   * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                                                                                                                                                                                                                                   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

exports.formatCallStackFrames = formatCallStackFrames;

var _sources = __webpack_require__(1369);

var _pause = __webpack_require__(1394);

var _frame = __webpack_require__(1380);

var _devtoolsSourceMap = __webpack_require__(1360);

var _lodash = __webpack_require__(2);

var _reselect = __webpack_require__(993);

function getLocation(frame, isGeneratedSource) {
  return isGeneratedSource ? frame.generatedLocation || frame.location : frame.location;
}

function getSourceForFrame(sources, frame, isGeneratedSource) {
  const sourceId = getLocation(frame, isGeneratedSource).sourceId;
  return (0, _sources.getSourceInSources)(sources, sourceId);
}

function appendSource(sources, frame, selectedSource) {
  const isGeneratedSource = selectedSource && !(0, _devtoolsSourceMap.isOriginalId)(selectedSource.get("id"));
  return _extends({}, frame, {
    location: getLocation(frame, isGeneratedSource),
    source: getSourceForFrame(sources, frame, isGeneratedSource).toJS()
  });
}

function formatCallStackFrames(frames, sources, selectedSource) {
  if (!frames) {
    return null;
  }

  return frames.filter(frame => getSourceForFrame(sources, frame)).map(frame => appendSource(sources, frame, selectedSource)).filter(frame => !(0, _lodash.get)(frame, "source.isBlackBoxed")).map(_frame.annotateFrame);
}

const getCallStackFrames = exports.getCallStackFrames = (0, _reselect.createSelector)(_sources.getSelectedSource, _sources.getSources, _pause.getFrames, (selectedSource, sources, frames) => formatCallStackFrames(frames, sources, selectedSource));

/***/ }),
/* 1780 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getVisibleSelectedFrame = undefined;

var _sources = __webpack_require__(1369);

var _pause = __webpack_require__(1394);

var _devtoolsSourceMap = __webpack_require__(1360);

var _reselect = __webpack_require__(993);

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 getLocation(frame, location) {
  if (!location) {
    return frame.location;
  }

  return !(0, _devtoolsSourceMap.isOriginalId)(location.sourceId) ? frame.generatedLocation || frame.location : frame.location;
}

const getVisibleSelectedFrame = exports.getVisibleSelectedFrame = (0, _reselect.createSelector)(_sources.getSelectedLocation, _pause.getSelectedFrame, (selectedLocation, selectedFrame) => {
  if (!selectedFrame) {
    return null;
  }

  const { id } = selectedFrame;

  return {
    id,
    location: getLocation(selectedFrame, selectedLocation)
  };
});

/***/ }),
/* 1781 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.setInScopeLines = setInScopeLines;

var _selectors = __webpack_require__(1352);

var _source = __webpack_require__(1356);

var _lodash = __webpack_require__(2);

function getOutOfScopeLines(outOfScopeLocations) {
  if (!outOfScopeLocations) {
    return null;
  }

  return (0, _lodash.uniq)((0, _lodash.flatMap)(outOfScopeLocations, location => (0, _lodash.range)(location.start.line, location.end.line)));
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.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 setInScopeLines() {
  return ({ dispatch, getState }) => {
    const source = (0, _selectors.getSelectedSource)(getState());
    const outOfScopeLocations = (0, _selectors.getOutOfScopeLocations)(getState());

    if (!source || !source.get("text")) {
      return;
    }

    const linesOutOfScope = getOutOfScopeLines(outOfScopeLocations, source.toJS());

    const sourceNumLines = (0, _source.getSourceLineCount)(source.toJS());
    const sourceLines = (0, _lodash.range)(1, sourceNumLines + 1);

    const inScopeLines = !linesOutOfScope ? sourceLines : (0, _lodash.without)(sourceLines, ...linesOutOfScope);

    dispatch({
      type: "IN_SCOPE_LINES",
      lines: inScopeLines
    });
  };
}

/***/ }),
/* 1782 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.createLocation = createLocation;
function createLocation({
  sourceId,
  line,
  column,
  sourceUrl
}) {
  return {
    sourceId,
    line,
    column,
    sourceUrl: sourceUrl || null
  };
}

/***/ }),
/* 1783 */,
/* 1784 */,
/* 1785 */,
/* 1786 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

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; }; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                                                                                                                                                                                   * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                                                                                                                                                                                                                                   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

exports.updatePreview = updatePreview;
exports.setPreview = setPreview;
exports.clearPreview = clearPreview;

var _ast = __webpack_require__(1638);

var _editor = __webpack_require__(1358);

var _preview = __webpack_require__(1807);

var _devtoolsSourceMap = __webpack_require__(1360);

var _promise = __webpack_require__(1653);

var _getExpression = __webpack_require__(2360);

var _selectors = __webpack_require__(1352);

var _expressions = __webpack_require__(1398);

var _lodash = __webpack_require__(2);

async function getReactProps(evaluate) {
  const reactDisplayName = await evaluate("this._reactInternalInstance.getName()");

  return {
    displayName: reactDisplayName.result
  };
}

async function getImmutableProps(expression, evaluate) {
  const immutableEntries = await evaluate((exp => `${exp}.toJS()`)(expression));

  const immutableType = await evaluate((exp => `${exp}.constructor.name`)(expression));

  return {
    type: immutableType.result,
    entries: immutableEntries.result
  };
}

async function getExtraProps(expression, result, evaluate) {
  const props = {};
  if ((0, _preview.isReactComponent)(result)) {
    props.react = await getReactProps(evaluate);
  }

  if ((0, _preview.isImmutable)(result)) {
    props.immutable = await getImmutableProps(expression, evaluate);
  }

  return props;
}

function isInvalidTarget(target) {
  if (!target || !target.innerText) {
    return true;
  }

  const tokenText = target.innerText.trim();
  const cursorPos = target.getBoundingClientRect();

  // exclude literal tokens where it does not make sense to show a preview
  const invaildType = ["cm-string", "cm-number", "cm-atom"].includes(target.className);

  // exclude syntax where the expression would be a syntax error
  const invalidToken = tokenText === "" || tokenText.match(/[(){}\|&%,.;=<>\+-/\*\s]/);

  // exclude codemirror elements that are not tokens
  const invalidTarget = target.parentElement && !target.parentElement.closest(".CodeMirror-line") || cursorPos.top == 0;

  return invalidTarget || invalidToken || invaildType;
}

function updatePreview(target, editor) {
  return ({ dispatch, getState, client, sourceMaps }) => {
    const tokenText = target.innerText ? target.innerText.trim() : "";
    const tokenPos = (0, _editor.getTokenLocation)(editor.codeMirror, target);
    const cursorPos = target.getBoundingClientRect();
    const preview = (0, _selectors.getPreview)(getState());

    if (preview) {
      // Return early if we are currently showing another preview or
      // if we are mousing over the same token as before
      if (preview.updating || (0, _lodash.isEqual)(preview.tokenPos, tokenPos)) {
        return;
      }

      // We are mousing over a new token that is not in the preview
      if (!target.classList.contains("debug-expression")) {
        dispatch(clearPreview());
      }
    }

    if (isInvalidTarget(target)) {
      return;
    }

    if (!(0, _selectors.isLineInScope)(getState(), tokenPos.line)) {
      return;
    }

    const source = (0, _selectors.getSelectedSource)(getState());
    const symbols = (0, _selectors.getSymbols)(getState(), source.toJS());

    let match;
    if (!symbols || symbols.identifiers) {
      match = (0, _ast.findBestMatchExpression)(symbols, tokenPos, tokenText);
    } else {
      match = (0, _getExpression.getExpressionFromCoords)(editor.codeMirror, tokenPos);
    }

    if (!match || !match.expression) {
      return;
    }

    const { expression, location } = match;
    dispatch(setPreview(expression, location, tokenPos, cursorPos));
  };
}

function setPreview(expression, location, tokenPos, cursorPos) {
  return async ({ dispatch, getState, client, sourceMaps }) => {
    await dispatch({
      type: "SET_PREVIEW",
      [_promise.PROMISE]: async function () {
        const source = (0, _selectors.getSelectedSource)(getState());

        const sourceId = source.get("id");
        if (location && !(0, _devtoolsSourceMap.isGeneratedId)(sourceId)) {
          const generatedLocation = await sourceMaps.getGeneratedLocation(_extends({}, location.start, { sourceId }), source.toJS());

          expression = await (0, _expressions.getMappedExpression)({ sourceMaps }, generatedLocation, expression);
        }

        const selectedFrame = (0, _selectors.getSelectedFrame)(getState());
        if (!selectedFrame) {
          return;
        }

        const { result } = await client.evaluateInFrame(selectedFrame.id, expression);

        if (result === undefined) {
          return;
        }

        const extra = await getExtraProps(expression, result, expr => client.evaluateInFrame(selectedFrame.id, expr));

        return {
          expression,
          result,
          location,
          tokenPos,
          cursorPos,
          extra
        };
      }()
    });
  };
}

function clearPreview() {
  return ({ dispatch, getState, client }) => {
    const currentSelection = (0, _selectors.getPreview)(getState());
    if (!currentSelection) {
      return;
    }

    return dispatch({
      type: "CLEAR_SELECTION"
    });
  };
}

/***/ }),
/* 1787 */,
/* 1788 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1789 */,
/* 1790 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = renderBreakpointsDropdown;

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _Svg = __webpack_require__(1359);

var _Svg2 = _interopRequireDefault(_Svg);

var _Dropdown = __webpack_require__(1615);

var _Dropdown2 = _interopRequireDefault(_Dropdown);

var _classnames = __webpack_require__(175);

var _classnames2 = _interopRequireDefault(_classnames);

__webpack_require__(1791);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function renderPause(isWaitingOnBreak) {
  const active = isWaitingOnBreak;
  return _react2.default.createElement(
    "div",
    {
      className: (0, _classnames2.default)("pause-next", {
        active: active,
        inactive: !active
      })
    },
    _react2.default.createElement("img", { className: "pause-next" }),
    _react2.default.createElement(
      "span",
      { className: "icon-spacer" },
      L10N.getStr("pauseButtonItem")
    )
  );
}

function renderPauseOnExceptions(shouldPauseOnExceptions, shouldIgnoreCaughtExceptions) {
  const active = (shouldPauseOnExceptions || shouldIgnoreCaughtExceptions) && (!shouldPauseOnExceptions || !shouldIgnoreCaughtExceptions);
  return _react2.default.createElement(
    "div",
    {
      className: (0, _classnames2.default)("pause-on-exceptions", {
        active: active,
        inactive: !active
      })
    },
    _react2.default.createElement("img", { className: "pause-on-exceptions" }),
    _react2.default.createElement(
      "span",
      { className: "icon-spacer" },
      L10N.getStr("pauseOnExceptionsItem")
    )
  );
}

function renderPauseOnUncaughtExceptions(shouldPauseOnExceptions, shouldIgnoreCaughtExceptions) {
  const active = shouldPauseOnExceptions && shouldIgnoreCaughtExceptions;
  return _react2.default.createElement(
    "div",
    {
      className: (0, _classnames2.default)("pause-uncaught-exceptions", {
        active: active,
        inactive: !active
      })
    },
    _react2.default.createElement("img", { className: "pause-uncaught-exceptions" }),
    _react2.default.createElement(
      "span",
      { className: "icon-spacer" },
      L10N.getStr("pauseOnUncaughtExceptionsItem")
    )
  );
}

function renderIgnoreExceptions(shouldPauseOnExceptions, shouldIgnoreCaughtExceptions) {
  const active = !shouldPauseOnExceptions && !shouldIgnoreCaughtExceptions;
  return _react2.default.createElement(
    "div",
    {
      className: (0, _classnames2.default)("ignore-exceptions", {
        active: active,
        inactive: !active
      })
    },
    _react2.default.createElement("img", { className: "ignore-exceptions" }),
    _react2.default.createElement(
      "span",
      { className: "icon-spacer" },
      L10N.getStr("ignoreExceptionsItem")
    )
  );
}

function handleClick(e) {
  e.stopPropagation();
}

function renderBreakpointsDropdown(breakOnNext, pauseOnExceptions, shouldPauseOnExceptions, shouldIgnoreCaughtExceptions, isWaitingOnBreak) {
  const Panel = _react2.default.createElement(
    "ul",
    null,
    _react2.default.createElement(
      "li",
      { onClick: () => breakOnNext(), className: "first" },
      renderPause(isWaitingOnBreak)
    ),
    _react2.default.createElement(
      "li",
      { onClick: () => pauseOnExceptions(false, false) },
      renderIgnoreExceptions(shouldPauseOnExceptions, shouldIgnoreCaughtExceptions)
    ),
    _react2.default.createElement(
      "li",
      { onClick: () => pauseOnExceptions(true, true) },
      renderPauseOnUncaughtExceptions(shouldPauseOnExceptions, shouldIgnoreCaughtExceptions)
    ),
    _react2.default.createElement(
      "li",
      { onClick: () => pauseOnExceptions(true, false) },
      renderPauseOnExceptions(shouldPauseOnExceptions, shouldIgnoreCaughtExceptions)
    )
  );

  const active = shouldPauseOnExceptions || shouldIgnoreCaughtExceptions || isWaitingOnBreak;

  return _react2.default.createElement(
    "div",
    {
      className: "breakpoints-dropdown",
      onClick: e => handleClick(e),
      key: "breakpoints-dropdown"
    },
    _react2.default.createElement(_Dropdown2.default, {
      className: "dropdown",
      panel: Panel,
      icon: _react2.default.createElement(_Svg2.default, {
        name: "plus",
        className: (0, _classnames2.default)("plus", {
          active: active,
          inactive: !active
        })
      })
    })
  );
}

/***/ }),
/* 1791 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 1792 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getScopes = getScopes;

var _getScope = __webpack_require__(1794);

function getScopes(why, selectedFrame, frameScopes) {
  if (!why || !selectedFrame) {
    return null;
  }

  if (!frameScopes) {
    return null;
  }

  const scopes = [];

  let scope = frameScopes;
  let scopeIndex = 1;

  while (scope) {
    const scopeItem = (0, _getScope.getScope)(scope, selectedFrame, frameScopes, why, scopeIndex);

    if (scopeItem) {
      scopes.push(scopeItem);
    }
    scopeIndex++;
    scope = scope.parent;
  }

  return scopes;
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/***/ }),
/* 1793 */,
/* 1794 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

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; }; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                                                                                                                                                                                   * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                                                                                                                                                                                                                                   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

exports.getScope = getScope;

var _getVariables = __webpack_require__(1765);

var _utils = __webpack_require__(1766);

var _frame = __webpack_require__(1380);

function getScopeTitle(type, scope) {
  if (type === "block" && scope.block && scope.block.displayName) {
    return scope.block.displayName;
  }

  if (type === "function" && scope.function) {
    return scope.function.displayName ? (0, _frame.simplifyDisplayName)(scope.function.displayName) : L10N.getStr("anonymous");
  }
  return L10N.getStr("scopes.block");
}

function getScope(scope, selectedFrame, frameScopes, why, scopeIndex) {
  const { type, actor } = scope;

  const isLocalScope = scope.actor === frameScopes.actor;

  const key = `${actor}-${scopeIndex}`;
  if (type === "function" || type === "block") {
    const bindings = scope.bindings;

    let vars = (0, _getVariables.getBindingVariables)(bindings, key);

    // show exception, return, and this variables in innermost scope
    if (isLocalScope) {
      vars = vars.concat((0, _utils.getFramePopVariables)(why, key));

      let thisDesc_ = selectedFrame.this;

      if ("this" in bindings) {
        // The presence of "this" means we're rendering a "this" binding
        // generated from mapScopes and this can override the binding
        // provided by the current frame.
        thisDesc_ = bindings.this ? bindings.this.value : null;
      }

      const this_ = (0, _utils.getThisVariable)(thisDesc_, key);

      if (this_) {
        vars.push(this_);
      }
    }

    if (vars && vars.length) {
      const title = getScopeTitle(type, scope);
      vars.sort((a, b) => a.name.localeCompare(b.name));
      return {
        name: title,
        path: key,
        contents: vars
      };
    }
  } else if (type === "object" && scope.object) {
    let value = scope.object;
    // If this is the global window scope, mark it as such so that it will
    // preview Window: Global instead of Window: Window
    if (value.class === "Window") {
      value = _extends({}, scope.object, { displayClass: "Global" });
    }
    return {
      name: scope.object.class,
      path: key,
      contents: { value }
    };
  }

  return null;
}

/***/ }),
/* 1795 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _lodash = __webpack_require__(2);

let newSources;
let createSource;
let supportsWasm = false;
let queuedSources;

async function dispatchNewSources() {
  const sources = queuedSources;
  queuedSources = [];

  await newSources(sources.map(source => createSource(source, { supportsWasm })));
}

const queue = (0, _lodash.throttle)(dispatchNewSources, 100);

exports.default = {
  initialize: options => {
    newSources = options.actions.newSources;
    createSource = options.createSource;
    supportsWasm = options.supportsWasm;
    queuedSources = [];
  },
  queue: source => {
    queuedSources.push(source);
    queue();
  },
  flush: () => queue.flush(),
  clear: () => queue.cancel()
};

/***/ }),
/* 1796 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.HighlightLine = undefined;

var _react = __webpack_require__(0);

var _editor = __webpack_require__(1358);

var _sourceDocuments = __webpack_require__(1436);

var _source = __webpack_require__(1356);

var _reactRedux = __webpack_require__(1189);

var _selectors = __webpack_require__(1352);

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 isDebugLine(selectedFrame, selectedLocation) {
  if (!selectedFrame) {
    return;
  }

  return selectedFrame.location.sourceId == selectedLocation.sourceId && selectedFrame.location.line == selectedLocation.line;
}

function isDocumentReady(selectedSource, selectedLocation) {
  return selectedLocation && (0, _source.isLoaded)(selectedSource) && (0, _sourceDocuments.hasDocument)(selectedLocation.sourceId);
}

class HighlightLine extends _react.PureComponent {
  componentDidUpdate(prevProps) {
    const { selectedLocation, selectedFrame, selectedSource } = this.props;

    this.clearHighlightLine(prevProps.selectedLocation, prevProps.selectedSource);

    this.setHighlightLine(selectedLocation, selectedFrame, selectedSource);
  }

  setHighlightLine(selectedLocation, selectedFrame, selectedSource) {
    if (!isDocumentReady(selectedSource, selectedLocation)) {
      return;
    }

    const { sourceId, line } = selectedLocation;

    if (!line || isDebugLine(selectedFrame, selectedLocation)) {
      return;
    }

    const editorLine = (0, _editor.toEditorLine)(sourceId, line);
    const doc = (0, _sourceDocuments.getDocument)(sourceId);
    doc.addLineClass(editorLine, "line", "highlight-line");
  }

  clearHighlightLine(selectedLocation, selectedSource) {
    if (!isDocumentReady(selectedSource, selectedLocation)) {
      return;
    }

    const { line, sourceId } = selectedLocation;
    const editorLine = (0, _editor.toEditorLine)(sourceId, line);
    const doc = (0, _sourceDocuments.getDocument)(sourceId);
    doc.removeLineClass(editorLine, "line", "highlight-line");
  }

  render() {
    return null;
  }
}

exports.HighlightLine = HighlightLine;
exports.default = (0, _reactRedux.connect)(state => ({
  selectedFrame: (0, _selectors.getVisibleSelectedFrame)(state),
  selectedLocation: (0, _selectors.getSelectedLocation)(state),
  selectedSource: (0, _selectors.getSelectedSource)(state)
}))(HighlightLine);

/***/ }),
/* 1797 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _loadSourceText = __webpack_require__(1435);

Object.keys(_loadSourceText).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  Object.defineProperty(exports, key, {
    enumerable: true,
    get: function () {
      return _loadSourceText[key];
    }
  });
});

var _prettyPrint = __webpack_require__(1798);

Object.keys(_prettyPrint).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  Object.defineProperty(exports, key, {
    enumerable: true,
    get: function () {
      return _prettyPrint[key];
    }
  });
});

var _newSources = __webpack_require__(1801);

Object.keys(_newSources).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  Object.defineProperty(exports, key, {
    enumerable: true,
    get: function () {
      return _newSources[key];
    }
  });
});

var _blackbox = __webpack_require__(1802);

Object.keys(_blackbox).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  Object.defineProperty(exports, key, {
    enumerable: true,
    get: function () {
      return _blackbox[key];
    }
  });
});

var _select = __webpack_require__(1803);

Object.keys(_select).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  Object.defineProperty(exports, key, {
    enumerable: true,
    get: function () {
      return _select[key];
    }
  });
});

var _tabs = __webpack_require__(1799);

Object.keys(_tabs).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  Object.defineProperty(exports, key, {
    enumerable: true,
    get: function () {
      return _tabs[key];
    }
  });
});

/***/ }),
/* 1798 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

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; }; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                                                                                                                                                                                   * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                                                                                                                                                                                                                                   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

exports.createPrettySource = createPrettySource;
exports.togglePrettyPrint = togglePrettyPrint;

var _assert = __webpack_require__(1384);

var _assert2 = _interopRequireDefault(_assert);

var _breakpoints = __webpack_require__(1396);

var _ast = __webpack_require__(1399);

var _prettyPrint = __webpack_require__(1431);

var _parser = __webpack_require__(1365);

var _source = __webpack_require__(1356);

var _loadSourceText = __webpack_require__(1435);

var _sources = __webpack_require__(1797);

var _pause = __webpack_require__(1639);

var _selectors = __webpack_require__(1352);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function createPrettySource(sourceId) {
  return async ({ dispatch, getState, sourceMaps }) => {
    const source = (0, _selectors.getSource)(getState(), sourceId);
    const url = (0, _source.getPrettySourceURL)(source.get("url"));
    const id = await sourceMaps.generatedToOriginalId(sourceId, url);

    const prettySource = {
      url,
      id,
      isPrettyPrinted: true,
      contentType: "text/javascript",
      loadedState: "loading"
    };
    dispatch({ type: "ADD_SOURCE", source: prettySource });

    const { code, mappings } = await (0, _prettyPrint.prettyPrint)({ source, url });
    await sourceMaps.applySourceMap(source.get("id"), url, code, mappings);

    const loadedPrettySource = _extends({}, prettySource, {
      text: code,
      loadedState: "loaded"
    });

    (0, _parser.setSource)(loadedPrettySource);

    dispatch({ type: "UPDATE_SOURCE", source: loadedPrettySource });

    return prettySource;
  };
}

/**
 * Toggle the pretty printing of a source's text. All subsequent calls to
 * |getText| will return the pretty-toggled text. Nothing will happen for
 * non-javascript files.
 *
 * @memberof actions/sources
 * @static
 * @param string id The source form from the RDP.
 * @returns Promise
 *          A promise that resolves to [aSource, prettyText] or rejects to
 *          [aSource, error].
 */
function togglePrettyPrint(sourceId) {
  return async ({ dispatch, getState, client, sourceMaps }) => {
    const source = (0, _selectors.getSource)(getState(), sourceId);
    if (!source) {
      return {};
    }

    if (!(0, _source.isLoaded)(source)) {
      await dispatch((0, _loadSourceText.loadSourceText)(source));
    }

    (0, _assert2.default)(sourceMaps.isGeneratedId(sourceId), "Pretty-printing only allowed on generated sources");

    const selectedLocation = (0, _selectors.getSelectedLocation)(getState());
    const url = (0, _source.getPrettySourceURL)(source.get("url"));
    const prettySource = (0, _selectors.getSourceByURL)(getState(), url);

    const options = {};
    if (selectedLocation) {
      options.location = await sourceMaps.getOriginalLocation(selectedLocation);
    }

    if (prettySource) {
      const _sourceId = prettySource.get("id");
      return dispatch((0, _sources.selectLocation)(_extends({}, options.location, { sourceId: _sourceId })));
    }

    const newPrettySource = await dispatch(createPrettySource(sourceId));

    await dispatch((0, _breakpoints.remapBreakpoints)(sourceId));
    await dispatch((0, _pause.mapFrames)());
    await dispatch((0, _ast.setEmptyLines)(newPrettySource.id));
    await dispatch((0, _ast.setSymbols)(newPrettySource.id));

    return dispatch((0, _sources.selectLocation)(_extends({}, options.location, { sourceId: newPrettySource.id })));
  };
}

/***/ }),
/* 1799 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.addTab = addTab;
exports.moveTab = moveTab;
exports.closeTab = closeTab;
exports.closeTabs = closeTabs;

var _editor = __webpack_require__(1358);

var _sources = __webpack_require__(1797);

var _selectors = __webpack_require__(1352);

function addTab(source, tabIndex) {
  return {
    type: "ADD_TAB",
    source,
    tabIndex
  };
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/**
 * Redux actions for the sources state
 * @module actions/sources
 */

function moveTab(url, tabIndex) {
  return {
    type: "MOVE_TAB",
    url,
    tabIndex
  };
}

/**
 * @memberof actions/sources
 * @static
 */
function closeTab(url) {
  return ({ dispatch, getState, client }) => {
    (0, _editor.removeDocument)(url);
    const tabs = (0, _selectors.removeSourceFromTabList)((0, _selectors.getSourceTabs)(getState()), url);
    const sourceId = (0, _selectors.getNewSelectedSourceId)(getState(), tabs);
    dispatch({ type: "CLOSE_TAB", url, tabs });
    dispatch((0, _sources.selectSource)(sourceId));
  };
}

/**
 * @memberof actions/sources
 * @static
 */
function closeTabs(urls) {
  return ({ dispatch, getState, client }) => {
    urls.forEach(url => {
      const source = (0, _selectors.getSourceByURL)(getState(), url);
      if (source) {
        (0, _editor.removeDocument)(source.get("id"));
      }
    });

    const tabs = (0, _selectors.removeSourcesFromTabList)((0, _selectors.getSourceTabs)(getState()), urls);
    dispatch({ type: "CLOSE_TABS", urls, tabs });

    const sourceId = (0, _selectors.getNewSelectedSourceId)(getState(), tabs);
    dispatch((0, _sources.selectSource)(sourceId));
  };
}

/***/ }),
/* 1800 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = defer;
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 defer() {
  let resolve = () => {};
  let reject = () => {};
  const promise = new Promise((_res, _rej) => {
    resolve = _res;
    reject = _rej;
  });

  return { resolve, reject, promise };
}

/***/ }),
/* 1801 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

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; }; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                                                                                                                                                                                   * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                                                                                                                                                                                                                                   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/**
 * Redux actions for the sources state
 * @module actions/sources
 */

exports.loadSourceMap = loadSourceMap;
exports.newSource = newSource;
exports.newSources = newSources;

var _devtoolsSourceMap = __webpack_require__(1360);

var _lodash = __webpack_require__(2);

var _breakpoints = __webpack_require__(1396);

var _loadSourceText = __webpack_require__(1435);

var _prettyPrint = __webpack_require__(1798);

var _sources = __webpack_require__(1797);

var _source = __webpack_require__(1356);

var _selectors = __webpack_require__(1352);

function createOriginalSource(originalUrl, generatedSource, sourceMaps) {
  return {
    url: originalUrl,
    id: sourceMaps.generatedToOriginalId(generatedSource.id, originalUrl),
    isPrettyPrinted: false,
    isWasm: false,
    isBlackBoxed: false,
    loadedState: "unloaded"
  };
}

function loadSourceMaps(sources) {
  return async function ({ dispatch, getState, sourceMaps }) {
    const originalSources = await Promise.all(sources.map(source => dispatch(loadSourceMap(source.id))));

    await dispatch(newSources((0, _lodash.flatten)(originalSources)));
  };
}

/**
 * @memberof actions/sources
 * @static
 */
function loadSourceMap(sourceId) {
  return async function ({ dispatch, getState, sourceMaps }) {
    const source = (0, _selectors.getSource)(getState(), sourceId).toJS();
    if (!(0, _devtoolsSourceMap.isGeneratedId)(source.id) || !source.sourceMapURL) {
      return;
    }

    let urls;

    try {
      urls = await sourceMaps.getOriginalURLs(source);
    } catch (e) {
      console.error(e);
      urls = null;
    }

    if (!urls) {
      // If this source doesn't have a sourcemap, enable it for pretty printing
      dispatch({
        type: "UPDATE_SOURCE",
        source: _extends({}, source, { sourceMapURL: "" })
      });
      return;
    }

    const originalSources = urls.map(url => createOriginalSource(url, source, sourceMaps));

    dispatch(newSources(originalSources));
  };
}

// If a request has been made to show this source, go ahead and
// select it.
function checkSelectedSource(sourceId) {
  return async ({ dispatch, getState }) => {
    const source = (0, _selectors.getSource)(getState(), sourceId).toJS();

    const pendingLocation = (0, _selectors.getPendingSelectedLocation)(getState());

    if (!pendingLocation || !pendingLocation.url || !source.url) {
      return;
    }

    const pendingUrl = pendingLocation.url;
    const rawPendingUrl = (0, _source.getRawSourceURL)(pendingUrl);

    if (rawPendingUrl === source.url) {
      if ((0, _source.isPrettyURL)(pendingUrl)) {
        return await dispatch((0, _prettyPrint.togglePrettyPrint)(source.id));
      }

      await dispatch((0, _sources.selectLocation)(_extends({}, pendingLocation, { sourceId: source.id })));
    }
  };
}

function checkPendingBreakpoints(sourceId) {
  return async ({ dispatch, getState }) => {
    // source may have been modified by selectLocation
    const source = (0, _selectors.getSource)(getState(), sourceId);

    const pendingBreakpoints = (0, _selectors.getPendingBreakpointsForSource)(getState(), source.get("url"));

    if (!pendingBreakpoints.size) {
      return;
    }

    // load the source text if there is a pending breakpoint for it
    await dispatch((0, _loadSourceText.loadSourceText)(source));

    const pendingBreakpointsArray = pendingBreakpoints.valueSeq().toJS();
    for (const pendingBreakpoint of pendingBreakpointsArray) {
      await dispatch((0, _breakpoints.syncBreakpoint)(sourceId, pendingBreakpoint));
    }
  };
}

/**
 * Handler for the debugger client's unsolicited newSource notification.
 * @memberof actions/sources
 * @static
 */
function newSource(source) {
  return async ({ dispatch }) => {
    await dispatch(newSources([source]));
  };
}

function newSources(sources) {
  return async ({ dispatch, getState }) => {
    const filteredSources = sources.filter(source => source && !(0, _selectors.getSource)(getState(), source.id));

    if (filteredSources.length == 0) {
      return;
    }

    dispatch({
      type: "ADD_SOURCES",
      sources: filteredSources
    });

    for (const source of filteredSources) {
      dispatch(checkSelectedSource(source.id));
      dispatch(checkPendingBreakpoints(source.id));
    }

    await dispatch(loadSourceMaps(filteredSources));
  };
}

/***/ }),
/* 1802 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.toggleBlackBox = toggleBlackBox;

var _promise = __webpack_require__(1653);

function toggleBlackBox(source) {
  return async ({ dispatch, getState, client, sourceMaps }) => {
    const { isBlackBoxed, id } = source;

    return dispatch({
      type: "BLACKBOX",
      source,
      [_promise.PROMISE]: client.blackBox(id, isBlackBoxed)
    });
  };
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/**
 * Redux actions for the sources state
 * @module actions/sources
 */

/***/ }),
/* 1803 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

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; }; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                                                                                                                                                                                   * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                                                                                                                                                                                                                                   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/**
 * Redux actions for the sources state
 * @module actions/sources
 */

exports.selectSourceURL = selectSourceURL;
exports.selectSource = selectSource;
exports.selectLocation = selectLocation;
exports.jumpToMappedLocation = jumpToMappedLocation;
exports.jumpToMappedSelectedLocation = jumpToMappedSelectedLocation;

var _devtoolsSourceMap = __webpack_require__(1360);

var _ast = __webpack_require__(1399);

var _ui = __webpack_require__(1385);

var _prettyPrint = __webpack_require__(1798);

var _tabs = __webpack_require__(1799);

var _loadSourceText = __webpack_require__(1435);

var _prefs = __webpack_require__(226);

var _source = __webpack_require__(1356);

var _location = __webpack_require__(1782);

var _sourceMaps = __webpack_require__(1397);

var _selectors = __webpack_require__(1352);

/**
 * Deterministically select a source that has a given URL. This will
 * work regardless of the connection status or if the source exists
 * yet. This exists mostly for external things to interact with the
 * debugger.
 *
 * @memberof actions/sources
 * @static
 */
function selectSourceURL(url, options = {}) {
  return async ({ dispatch, getState }) => {
    const source = (0, _selectors.getSourceByURL)(getState(), url);
    if (source) {
      const sourceId = source.get("id");
      const location = (0, _location.createLocation)(_extends({}, options.location, { sourceId }));
      // flow is unable to comprehend that if an options.location object
      // exists, that we have a valid Location object, and if it doesnt,
      // we have a valid { sourceId: string } object. So we are overriding
      // the error
      // $FlowIgnore
      await dispatch(selectLocation(location, options.tabIndex));
    } else {
      dispatch({
        type: "SELECT_SOURCE_URL",
        url: url,
        tabIndex: options.tabIndex,
        location: options.location
      });
    }
  };
}

/**
 * @memberof actions/sources
 * @static
 */
function selectSource(sourceId, tabIndex = "") {
  return async ({ dispatch }) => {
    const location = (0, _location.createLocation)({ sourceId });
    return await dispatch(selectLocation(location, tabIndex));
  };
}

/**
 * @memberof actions/sources
 * @static
 */
function selectLocation(location, tabIndex = "") {
  return async ({ dispatch, getState, client }) => {
    if (!client) {
      // No connection, do nothing. This happens when the debugger is
      // shut down too fast and it tries to display a default source.
      return;
    }

    const source = (0, _selectors.getSource)(getState(), location.sourceId);
    if (!source) {
      // If there is no source we deselect the current selected source
      return dispatch({ type: "CLEAR_SELECTED_SOURCE" });
    }

    const activeSearch = (0, _selectors.getActiveSearch)(getState());
    if (activeSearch !== "file") {
      dispatch((0, _ui.closeActiveSearch)());
    }

    dispatch((0, _tabs.addTab)(source.toJS(), 0));

    dispatch({
      type: "SELECT_SOURCE",
      source: source.toJS(),
      tabIndex,
      location
    });

    await dispatch((0, _loadSourceText.loadSourceText)(source));
    const selectedSource = (0, _selectors.getSelectedSource)(getState());
    if (!selectedSource) {
      return;
    }

    const sourceId = selectedSource.get("id");
    if (_prefs.prefs.autoPrettyPrint && !(0, _selectors.getPrettySource)(getState(), sourceId) && (0, _source.shouldPrettyPrint)(selectedSource) && (0, _source.isMinified)(selectedSource)) {
      await dispatch((0, _prettyPrint.togglePrettyPrint)(sourceId));
      dispatch((0, _tabs.closeTab)(source.get("url")));
    }

    dispatch((0, _ast.setSymbols)(sourceId));
    dispatch((0, _ast.setOutOfScopeLocations)());
  };
}

/**
 * @memberof actions/sources
 * @static
 */
function jumpToMappedLocation(location) {
  return async function ({ dispatch, getState, client, sourceMaps }) {
    if (!client) {
      return;
    }

    const source = (0, _selectors.getSource)(getState(), location.sourceId);
    let pairedLocation;
    if ((0, _devtoolsSourceMap.isOriginalId)(location.sourceId)) {
      pairedLocation = await (0, _sourceMaps.getGeneratedLocation)(getState(), source.toJS(), location, sourceMaps);
    } else {
      pairedLocation = await sourceMaps.getOriginalLocation(location, source.toJS());
    }

    return dispatch(selectLocation(_extends({}, pairedLocation)));
  };
}

function jumpToMappedSelectedLocation() {
  return async function ({ dispatch, getState }) {
    const location = (0, _selectors.getSelectedLocation)(getState());
    await dispatch(jumpToMappedLocation(location));
  };
}

/***/ }),
/* 1804 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

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; };

exports.updateFrameLocation = updateFrameLocation;
exports.mapFrames = mapFrames;

var _selectors = __webpack_require__(1352);

function updateFrameLocation(frame, sourceMaps) {
  return sourceMaps.getOriginalLocation(frame.location).then(loc => _extends({}, frame, {
    location: loc,
    generatedLocation: frame.generatedLocation || frame.location
  }));
}

function updateFrameLocations(frames, sourceMaps) {
  if (!frames || frames.length == 0) {
    return Promise.resolve(frames);
  }

  return Promise.all(frames.map(frame => updateFrameLocation(frame, sourceMaps)));
}

/**
 * Map call stack frame locations to original locations.
 * e.g.
 * 1. When the debuggee pauses
 * 2. When a source is pretty printed
 *
 * @memberof actions/pause
 * @static
 */
function mapFrames() {
  return async function ({ dispatch, getState, sourceMaps }) {
    const frames = (0, _selectors.getFrames)(getState());
    if (!frames) {
      return;
    }

    const mappedFrames = await updateFrameLocations(frames, sourceMaps);

    dispatch({
      type: "MAP_FRAMES",
      frames: mappedFrames
    });
  };
}

/***/ }),
/* 1805 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = showContextMenu;

var _devtoolsContextmenu = __webpack_require__(1413);

function showContextMenu(props) {
  const {
    removeBreakpoint,
    removeBreakpoints,
    removeAllBreakpoints,
    toggleBreakpoints,
    toggleAllBreakpoints,
    toggleDisabledBreakpoint,
    selectLocation,
    setBreakpointCondition,
    openConditionalPanel,
    breakpoints,
    breakpoint,
    contextMenuEvent
  } = props;

  contextMenuEvent.preventDefault();

  const deleteSelfLabel = L10N.getStr("breakpointMenuItem.deleteSelf2.label");
  const deleteAllLabel = L10N.getStr("breakpointMenuItem.deleteAll2.label");
  const deleteOthersLabel = L10N.getStr("breakpointMenuItem.deleteOthers2.label");
  const enableSelfLabel = L10N.getStr("breakpointMenuItem.enableSelf2.label");
  const enableAllLabel = L10N.getStr("breakpointMenuItem.enableAll2.label");
  const enableOthersLabel = L10N.getStr("breakpointMenuItem.enableOthers2.label");
  const disableSelfLabel = L10N.getStr("breakpointMenuItem.disableSelf2.label");
  const disableAllLabel = L10N.getStr("breakpointMenuItem.disableAll2.label");
  const disableOthersLabel = L10N.getStr("breakpointMenuItem.disableOthers2.label");
  const removeConditionLabel = L10N.getStr("breakpointMenuItem.removeCondition2.label");
  const addConditionLabel = L10N.getStr("breakpointMenuItem.addCondition2.label");
  const editConditionLabel = L10N.getStr("breakpointMenuItem.editCondition2.label");

  const deleteSelfKey = L10N.getStr("breakpointMenuItem.deleteSelf2.accesskey");
  const deleteAllKey = L10N.getStr("breakpointMenuItem.deleteAll2.accesskey");
  const deleteOthersKey = L10N.getStr("breakpointMenuItem.deleteOthers2.accesskey");
  const enableSelfKey = L10N.getStr("breakpointMenuItem.enableSelf2.accesskey");
  const enableAllKey = L10N.getStr("breakpointMenuItem.enableAll2.accesskey");
  const enableOthersKey = L10N.getStr("breakpointMenuItem.enableOthers2.accesskey");
  const disableSelfKey = L10N.getStr("breakpointMenuItem.disableSelf2.accesskey");
  const disableAllKey = L10N.getStr("breakpointMenuItem.disableAll2.accesskey");
  const disableOthersKey = L10N.getStr("breakpointMenuItem.disableOthers2.accesskey");
  const removeConditionKey = L10N.getStr("breakpointMenuItem.removeCondition2.accesskey");
  const editConditionKey = L10N.getStr("breakpointMenuItem.editCondition2.accesskey");
  const addConditionKey = L10N.getStr("breakpointMenuItem.addCondition2.accesskey");

  const otherBreakpoints = breakpoints.filter(b => b !== breakpoint);
  const enabledBreakpoints = breakpoints.filter(b => !b.disabled);
  const disabledBreakpoints = breakpoints.filter(b => b.disabled);
  const otherEnabledBreakpoints = breakpoints.filter(b => !b.disabled && b !== breakpoint);
  const otherDisabledBreakpoints = breakpoints.filter(b => b.disabled && b !== breakpoint);

  const deleteSelfItem = {
    id: "node-menu-delete-self",
    label: deleteSelfLabel,
    accesskey: deleteSelfKey,
    disabled: false,
    click: () => removeBreakpoint(breakpoint.location)
  };

  const deleteAllItem = {
    id: "node-menu-delete-all",
    label: deleteAllLabel,
    accesskey: deleteAllKey,
    disabled: false,
    click: () => removeAllBreakpoints()
  };

  const deleteOthersItem = {
    id: "node-menu-delete-other",
    label: deleteOthersLabel,
    accesskey: deleteOthersKey,
    disabled: false,
    click: () => removeBreakpoints(otherBreakpoints)
  };

  const enableSelfItem = {
    id: "node-menu-enable-self",
    label: enableSelfLabel,
    accesskey: enableSelfKey,
    disabled: false,
    click: () => toggleDisabledBreakpoint(breakpoint.location.line)
  };

  const enableAllItem = {
    id: "node-menu-enable-all",
    label: enableAllLabel,
    accesskey: enableAllKey,
    disabled: false,
    click: () => toggleAllBreakpoints(false)
  };

  const enableOthersItem = {
    id: "node-menu-enable-others",
    label: enableOthersLabel,
    accesskey: enableOthersKey,
    disabled: false,
    click: () => toggleBreakpoints(false, otherDisabledBreakpoints)
  };

  const disableSelfItem = {
    id: "node-menu-disable-self",
    label: disableSelfLabel,
    accesskey: disableSelfKey,
    disabled: false,
    click: () => toggleDisabledBreakpoint(breakpoint.location.line)
  };

  const disableAllItem = {
    id: "node-menu-disable-all",
    label: disableAllLabel,
    accesskey: disableAllKey,
    disabled: false,
    click: () => toggleAllBreakpoints(true)
  };

  const disableOthersItem = {
    id: "node-menu-disable-others",
    label: disableOthersLabel,
    accesskey: disableOthersKey,
    click: () => toggleBreakpoints(true, otherEnabledBreakpoints)
  };

  const removeConditionItem = {
    id: "node-menu-remove-condition",
    label: removeConditionLabel,
    accesskey: removeConditionKey,
    disabled: false,
    click: () => setBreakpointCondition(breakpoint.location)
  };

  const addConditionItem = {
    id: "node-menu-add-condition",
    label: addConditionLabel,
    accesskey: addConditionKey,
    click: () => {
      selectLocation(breakpoint.location);
      openConditionalPanel(breakpoint.location.line);
    }
  };

  const editConditionItem = {
    id: "node-menu-edit-condition",
    label: editConditionLabel,
    accesskey: editConditionKey,
    click: () => {
      selectLocation(breakpoint.location);
      openConditionalPanel(breakpoint.location.line);
    }
  };

  const hideEnableSelfItem = !breakpoint.disabled;
  const hideEnableAllItem = disabledBreakpoints.size === 0;
  const hideEnableOthersItem = otherDisabledBreakpoints.size === 0;
  const hideDisableAllItem = enabledBreakpoints.size === 0;
  const hideDisableOthersItem = otherEnabledBreakpoints.size === 0;
  const hideDisableSelfItem = breakpoint.disabled;

  const items = [{ item: enableSelfItem, hidden: () => hideEnableSelfItem }, { item: enableAllItem, hidden: () => hideEnableAllItem }, { item: enableOthersItem, hidden: () => hideEnableOthersItem }, {
    item: { type: "separator" },
    hidden: () => hideEnableSelfItem && hideEnableAllItem && hideEnableOthersItem
  }, { item: deleteSelfItem }, { item: deleteAllItem }, { item: deleteOthersItem, hidden: () => breakpoints.size === 1 }, {
    item: { type: "separator" },
    hidden: () => hideDisableSelfItem && hideDisableAllItem && hideDisableOthersItem
  }, { item: disableSelfItem, hidden: () => hideDisableSelfItem }, { item: disableAllItem, hidden: () => hideDisableAllItem }, { item: disableOthersItem, hidden: () => hideDisableOthersItem }, {
    item: { type: "separator" }
  }, {
    item: addConditionItem,
    hidden: () => breakpoint.condition
  }, {
    item: editConditionItem,
    hidden: () => !breakpoint.condition
  }, {
    item: removeConditionItem,
    hidden: () => !breakpoint.condition
  }];

  (0, _devtoolsContextmenu.showMenu)(contextMenuEvent, (0, _devtoolsContextmenu.buildMenu)(items));
}

/***/ }),
/* 1806 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.createEditor = createEditor;

var _sourceEditor = __webpack_require__(197);

var _sourceEditor2 = _interopRequireDefault(_sourceEditor);

var _prefs = __webpack_require__(226);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 createEditor() {
  const gutters = ["breakpoints", "hit-markers", "CodeMirror-linenumbers"];

  if (_prefs.features.codeFolding) {
    gutters.push("CodeMirror-foldgutter");
  }

  return new _sourceEditor2.default({
    mode: "javascript",
    foldGutter: _prefs.features.codeFolding,
    enableCodeFolding: _prefs.features.codeFolding,
    readOnly: true,
    lineNumbers: true,
    theme: "mozilla",
    styleActiveLine: false,
    lineWrapping: false,
    matchBrackets: true,
    showAnnotationRuler: true,
    gutters,
    value: " ",
    extraKeys: {
      // Override code mirror keymap to avoid conflicts with split console.
      Esc: false,
      "Cmd-F": false,
      "Ctrl-F": false,
      "Cmd-G": false,
      "Ctrl-G": false
    }
  });
}

/***/ }),
/* 1807 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.isImmutable = isImmutable;
exports.isReactComponent = isReactComponent;


const IMMUTABLE_FIELDS = ["_root", "__ownerID", "__altered", "__hash"]; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                         * License, v. 2.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 isImmutable(result) {
  if (!result || !result.preview) {
    return;
  }

  const ownProperties = result.preview.ownProperties;
  if (!ownProperties) {
    return;
  }

  return IMMUTABLE_FIELDS.every(field => Object.keys(ownProperties).includes(field));
}

function isReactComponent(result) {
  if (!result || !result.preview) {
    return;
  }

  const ownProperties = result.preview.ownProperties;
  if (!ownProperties) {
    return;
  }

  return Object.keys(ownProperties).includes("_reactInternalInstance");
}

/***/ }),
/* 1808 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 viewBox=\"0 0 256 247\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" preserveAspectRatio=\"xMidYMid\"><defs><radialGradient cx=\"78.7636112%\" cy=\"37.8476394%\" fx=\"78.7636112%\" fy=\"37.8476394%\" r=\"89.8725577%\" id=\"radialGradient-1\"><stop stop-color=\"#F80090\" offset=\"0%\"></stop><stop stop-color=\"#4D008E\" offset=\"100%\"></stop></radialGradient><radialGradient cx=\"68.7389016%\" cy=\"4.39833672%\" fx=\"68.7389016%\" fy=\"4.39833672%\" r=\"81.7284786%\" id=\"radialGradient-2\"><stop stop-color=\"#57008E\" offset=\"0%\"></stop><stop stop-color=\"#5C008E\" offset=\"29.1746283%\"></stop><stop stop-color=\"#F80090\" offset=\"100%\"></stop></radialGradient><linearGradient x1=\"18.2386532%\" y1=\"0%\" x2=\"81.1591125%\" y2=\"84.3374763%\" id=\"linearGradient-3\"><stop stop-color=\"#F70090\" offset=\"0%\"></stop><stop stop-color=\"#E50090\" offset=\"66.9712865%\"></stop><stop stop-color=\"#D6008F\" stop-opacity=\"0.2\" offset=\"82.7147533%\"></stop><stop stop-color=\"#C10090\" stop-opacity=\"0\" offset=\"100%\"></stop></linearGradient><linearGradient x1=\"64.9060589%\" y1=\"71.5585538%\" x2=\"44.2897699%\" y2=\"50%\" id=\"linearGradient-4\"><stop stop-color=\"#B2008F\" stop-opacity=\"0.151340138\" offset=\"0%\"></stop><stop stop-color=\"#F70090\" stop-opacity=\"0.4\" offset=\"40.0350765%\"></stop><stop stop-color=\"#F60090\" stop-opacity=\"0.891668\" offset=\"64.8995536%\"></stop><stop stop-color=\"#FF0090\" offset=\"100%\"></stop></linearGradient></defs><g><path d=\"M16.6852208,157.125328 C3.56690702,87.3798324 38.2363025,20.1145078 117.808706,11.1662199 C106.835616,-0.558801732 91.8452087,-0.646905628 84.9481697,0.779380087 C72.770288,4.66044372 73.1525932,12.540855 59.3390152,22.7199675 C45.6064437,30.5634307 38.7094156,24.5568182 28.7057455,32.6879515 C18.7234849,40.7583874 25.6888528,59.2851732 21.5022823,62.8870857 C17.3464381,70.0905489 4.45500952,76.5077264 2.10834286,85.6062545 C0.168948918,97.2420641 7.37241212,105.553752 7.09535584,115.527778 C7.92652468,123.839467 -1.17920693,128.539449 0.129052814,135.275796 C4.0477368,146.281025 11.600845,152.904887 15.1615723,155.958047 C15.9781085,156.533531 16.8404881,157.95083 16.6852208,157.125328 L16.6852208,157.125328 Z\" fill=\"#FF0090\"></path><path d=\"M158.275491,60.578542 C155.368486,60.578542 153.011422,58.2214776 153.011422,55.3144727 C153.011422,52.4074679 155.368486,50.0504035 158.275491,50.0504035 C161.182496,50.0504035 163.53956,52.4074679 163.53956,55.3144727 C163.53956,58.2214776 161.182496,60.578542 158.275491,60.578542 L158.275491,60.578542 Z M19.7566405,164.732808 C7.1500258,104.116773 46.1602355,53.4676156 121.704062,78.4026805 C166.031404,104.334594 221.793282,102.646102 224.307422,85.8832 C230.514061,65.7878769 196.047681,24.3767065 144.515214,13.5715117 C42.2814476,-6.37654026 -12.8335943,104.116774 19.7566405,164.732808 L19.7566405,164.732808 Z\" fill=\"url(#radialGradient-1)\"></path><path d=\"M187.458604,171.493257 C202.639072,173.137863 217.048769,169.494573 230.402327,158.61014 C210.228197,181.112651 185.002777,192.426521 156.059262,195.505171 C169.878829,207.254019 183.20579,212.546348 195.955366,210.281136 C160.528734,220.05679 130.847947,209.296529 94.7424273,173.340673 C92.8517347,183.020022 103.074741,198.100667 113.611745,207.727264 C52.4742909,181.221845 47.1143627,98.6544556 121.66531,78.3442237 C44.3844415,41.214641 0.686373501,113.357693 22.1558444,172.485931 C43.1623368,218.026693 99.1402667,253.085223 160.492163,245.3753 C190.292928,241.7251 234.79401,221.178935 252.973664,172.485931 C240.160919,183.983766 217.257941,193.997836 207.037617,194.765984 C241.628648,177.478781 260.301586,148.103896 255.060336,107.955387 C247.895106,125.013742 238.441392,138.114625 226.616076,147.112305 C251.735653,107.955387 247.425219,87.716426 228.832526,65.4732398 C242.131228,102.044668 224.928249,142.633967 187.458604,171.493257 L187.458604,171.493257 Z\" fill=\"url(#radialGradient-2)\"></path><path d=\"M169.707072,213.625541 C167.082407,213.13513 175.656929,217.098842 159.079366,212.710316 C142.501804,208.32179 125.622502,204.092744 94.7424273,173.340673 C92.8517347,183.020022 103.074741,198.100667 113.611745,207.727264 C142.056275,227.564927 122.711866,218.286797 166.051946,233.269481 C169.52976,226.346862 169.707072,220.195346 169.707072,213.625541 L169.707072,213.625541 Z\" fill=\"url(#linearGradient-3)\"></path><path d=\"M114.601372,57.8510108 C114.601372,57.8510108 118.369452,52.2893628 119.836219,49.7810251 C121.633641,46.7072319 124.393939,41.104618 124.393939,41.104618 C124.393939,41.104618 95.389611,31.6417749 88.2716448,30.4871665 C66.1450215,36.2308801 66.0645022,45.5009559 78.435065,59.690116 C79.8114806,61.2693368 114.601372,57.8510108 114.601372,57.8510108 L114.601372,57.8510108 Z\" fill=\"url(#linearGradient-4)\"></path></g></svg>"

/***/ }),
/* 1809 */,
/* 1810 */,
/* 1811 */,
/* 1812 */,
/* 1813 */,
/* 1814 */,
/* 1815 */,
/* 1816 */,
/* 1817 */,
/* 1818 */,
/* 1819 */,
/* 1820 */,
/* 1821 */,
/* 1822 */,
/* 1823 */,
/* 1824 */,
/* 1825 */,
/* 1826 */,
/* 1827 */,
/* 1828 */,
/* 1829 */,
/* 1830 */,
/* 1831 */,
/* 1832 */,
/* 1833 */,
/* 1834 */,
/* 1835 */,
/* 1836 */,
/* 1837 */,
/* 1838 */,
/* 1839 */,
/* 1840 */,
/* 1841 */,
/* 1842 */,
/* 1843 */,
/* 1844 */,
/* 1845 */,
/* 1846 */,
/* 1847 */,
/* 1848 */,
/* 1849 */,
/* 1850 */,
/* 1851 */,
/* 1852 */,
/* 1853 */,
/* 1854 */,
/* 1855 */,
/* 1856 */,
/* 1857 */,
/* 1858 */,
/* 1859 */,
/* 1860 */,
/* 1861 */,
/* 1862 */,
/* 1863 */,
/* 1864 */,
/* 1865 */,
/* 1866 */,
/* 1867 */,
/* 1868 */,
/* 1869 */,
/* 1870 */,
/* 1871 */,
/* 1872 */,
/* 1873 */,
/* 1874 */,
/* 1875 */,
/* 1876 */,
/* 1877 */,
/* 1878 */,
/* 1879 */,
/* 1880 */,
/* 1881 */,
/* 1882 */,
/* 1883 */,
/* 1884 */,
/* 1885 */,
/* 1886 */,
/* 1887 */,
/* 1888 */,
/* 1889 */,
/* 1890 */,
/* 1891 */,
/* 1892 */,
/* 1893 */,
/* 1894 */,
/* 1895 */,
/* 1896 */,
/* 1897 */,
/* 1898 */,
/* 1899 */,
/* 1900 */,
/* 1901 */,
/* 1902 */,
/* 1903 */,
/* 1904 */,
/* 1905 */,
/* 1906 */,
/* 1907 */,
/* 1908 */,
/* 1909 */,
/* 1910 */,
/* 1911 */,
/* 1912 */,
/* 1913 */,
/* 1914 */,
/* 1915 */,
/* 1916 */,
/* 1917 */,
/* 1918 */,
/* 1919 */,
/* 1920 */,
/* 1921 */,
/* 1922 */,
/* 1923 */,
/* 1924 */,
/* 1925 */,
/* 1926 */,
/* 1927 */,
/* 1928 */,
/* 1929 */,
/* 1930 */,
/* 1931 */,
/* 1932 */,
/* 1933 */,
/* 1934 */,
/* 1935 */,
/* 1936 */,
/* 1937 */,
/* 1938 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 = __webpack_require__(1939);
const loadProperties = __webpack_require__(2017);
const node = __webpack_require__(1940);

module.exports = {
  client,
  loadProperties,
  node
};

/***/ }),
/* 1939 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


async function enumIndexedProperties(objectClient, start, end) {
  try {
    const { iterator } = await objectClient.enumProperties({ ignoreNonIndexedProperties: true });
    const response = await iteratorSlice(iterator, start, end);
    return response;
  } catch (e) {
    console.error("Error in enumIndexedProperties", e);
    return {};
  }
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

async function enumNonIndexedProperties(objectClient, start, end) {
  try {
    const { iterator } = await objectClient.enumProperties({ ignoreIndexedProperties: true });
    const response = await iteratorSlice(iterator, start, end);
    return response;
  } catch (e) {
    console.error("Error in enumNonIndexedProperties", e);
    return {};
  }
}

async function enumEntries(objectClient, start, end) {
  try {
    const { iterator } = await objectClient.enumEntries();
    const response = await iteratorSlice(iterator, start, end);
    return response;
  } catch (e) {
    console.error("Error in enumEntries", e);
    return {};
  }
}

async function enumSymbols(objectClient, start, end) {
  try {
    const { iterator } = await objectClient.enumSymbols();
    const response = await iteratorSlice(iterator, start, end);
    return response;
  } catch (e) {
    console.error("Error in enumSymbols", e);
    return {};
  }
}

async function getPrototype(objectClient) {
  if (typeof objectClient.getPrototype !== "function") {
    console.error("objectClient.getPrototype is not a function");
    return Promise.resolve({});
  }
  return objectClient.getPrototype();
}

function iteratorSlice(iterator, start, end) {
  start = start || 0;
  const count = end ? end - start + 1 : iterator.count;
  return iterator.slice(start, count);
}

module.exports = {
  enumEntries,
  enumIndexedProperties,
  enumNonIndexedProperties,
  enumSymbols,
  getPrototype
};

/***/ }),
/* 1940 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 { get, has } = __webpack_require__(2);
const { maybeEscapePropertyName } = __webpack_require__(1353);
const ArrayRep = __webpack_require__(1448);
const GripArrayRep = __webpack_require__(1450);
const GripMap = __webpack_require__(1584);
const GripMapEntryRep = __webpack_require__(1451);

const MAX_NUMERICAL_PROPERTIES = 100;

const NODE_TYPES = {
  BUCKET: Symbol("[n…n]"),
  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("__proto__")
};

let WINDOW_PROPERTIES = {};

if (typeof window === "object") {
  WINDOW_PROPERTIES = Object.getOwnPropertyNames(window);
}

const SAFE_PATH_PREFIX = "##-";

function getType(item) {
  return item.type;
}

function getValue(item) {
  if (has(item, "contents.value")) {
    return get(item, "contents.value");
  }

  if (has(item, "contents.getterValue")) {
    return get(item, "contents.getterValue", undefined);
  }

  if (nodeHasAccessors(item)) {
    return item.contents;
  }

  return undefined;
}

function nodeIsBucket(item) {
  return getType(item) === NODE_TYPES.BUCKET;
}

function nodeIsEntries(item) {
  return getType(item) === NODE_TYPES.ENTRIES;
}

function nodeIsMapEntry(item) {
  return GripMapEntryRep.supportsObject(getValue(item));
}

function nodeHasChildren(item) {
  return Array.isArray(item.contents);
}

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
// debugger 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 !nodeHasChildren(item) && !nodeHasProperties(item) && !nodeIsEntries(item) && !nodeIsMapEntry(item) && !nodeHasAccessors(item) && !nodeIsBucket(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 nodeHasAccessors(item) {
  return !!getNodeGetter(item) || !!getNodeSetter(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;
  }

  return value.class === "Map" || value.class === "Set" || value.class === "WeakMap" || value.class === "WeakSet";
}

function nodeHasAllEntriesInPreview(item) {
  const { preview } = getValue(item) || {};
  if (!preview) {
    return false;
  }

  const {
    entries,
    items,
    length,
    size
  } = preview;

  if (!entries && !items) {
    return false;
  }

  return entries ? entries.length === size : items.length === length;
}

function nodeNeedsNumericalBuckets(item) {
  return nodeSupportsNumericalBucketing(item) && getNumericalPropertiesCount(item) > MAX_NUMERICAL_PROPERTIES;
}

function makeNodesForPromiseProperties(item) {
  const { promiseState: { reason, value, state } } = getValue(item);

  const properties = [];

  if (state) {
    properties.push(createNode(item, "<state>", `${item.path}/${SAFE_PATH_PREFIX}state`, { value: state }, NODE_TYPES.PROMISE_STATE));
  }

  if (reason) {
    properties.push(createNode(item, "<reason>", `${item.path}/${SAFE_PATH_PREFIX}reason`, { value: reason }, NODE_TYPES.PROMISE_REASON));
  }

  if (value) {
    properties.push(createNode(item, "<value>", `${item.path}/${SAFE_PATH_PREFIX}value`, { value: value }, NODE_TYPES.PROMISE_VALUE));
  }

  return properties;
}

function makeNodesForProxyProperties(item) {
  const {
    proxyHandler,
    proxyTarget
  } = getValue(item);

  return [createNode(item, "<target>", `${item.path}/${SAFE_PATH_PREFIX}target`, { value: proxyTarget }, NODE_TYPES.PROXY_TARGET), createNode(item, "<handler>", `${item.path}/${SAFE_PATH_PREFIX}handler`, { value: proxyHandler }, NODE_TYPES.PROXY_HANDLER)];
}

function makeNodesForEntries(item) {
  const { path } = item;
  const nodeName = "<entries>";
  const entriesPath = `${path}/${SAFE_PATH_PREFIX}entries`;

  if (nodeHasAllEntriesInPreview(item)) {
    let entriesNodes = [];
    const { preview } = getValue(item);
    if (preview.entries) {
      entriesNodes = preview.entries.map(([key, value], index) => {
        return createNode(item, index, `${entriesPath}/${index}`, {
          value: GripMapEntryRep.createGripMapEntry(key, value)
        });
      });
    } else if (preview.items) {
      entriesNodes = preview.items.map((value, index) => {
        return createNode(item, index, `${entriesPath}/${index}`, { value });
      });
    }
    return createNode(item, nodeName, entriesPath, entriesNodes, NODE_TYPES.ENTRIES);
  }
  return createNode(item, nodeName, entriesPath, null, NODE_TYPES.ENTRIES);
}

function makeNodesForMapEntry(item) {
  const nodeValue = getValue(item);
  if (!nodeValue || !nodeValue.preview) {
    return [];
  }

  const { key, value } = nodeValue.preview;
  const path = item.path;

  return [createNode(item, "<key>", `${path}/##key`, { value: key }, NODE_TYPES.MAP_ENTRY_KEY), createNode(item, "<value>", `${path}/##value`, { value }, NODE_TYPES.MAP_ENTRY_VALUE)];
}

function getNodeGetter(item) {
  return get(item, "contents.get", undefined);
}

function getNodeSetter(item) {
  return get(item, "contents.set", undefined);
}

function makeNodesForAccessors(item) {
  const accessors = [];

  const getter = getNodeGetter(item);
  if (getter && getter.type !== "undefined") {
    accessors.push(createNode(item, "<get>", `${item.path}/${SAFE_PATH_PREFIX}get`, { value: getter }, NODE_TYPES.GET));
  }

  const setter = getNodeSetter(item);
  if (setter && setter.type !== "undefined") {
    accessors.push(createNode(item, "<set>", `${item.path}/${SAFE_PATH_PREFIX}set`, { value: setter }, NODE_TYPES.SET));
  }

  return accessors;
}

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 parentPath = parent.path;
  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);

  let 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 bucketKey = `${SAFE_PATH_PREFIX}bucket_${minIndex}-${maxIndex}`;
    const bucketName = `[${minIndex}…${maxIndex}]`;

    buckets.push(createNode(parent, bucketName, `${parentPath}/${bucketKey}`, null, NODE_TYPES.BUCKET, {
      startIndex: minIndex,
      endIndex: maxIndex
    }));
  }
  return buckets;
}

function makeDefaultPropsBucket(propertiesNames, parent, ownProperties) {
  const parentPath = parent.path;

  const userPropertiesNames = [];
  const defaultProperties = [];

  propertiesNames.forEach(name => {
    if (isDefaultWindowProperty(name)) {
      defaultProperties.push(name);
    } else {
      userPropertiesNames.push(name);
    }
  });

  let nodes = makeNodesForOwnProps(userPropertiesNames, parent, ownProperties);

  if (defaultProperties.length > 0) {
    const defaultPropertiesNode = createNode(parent, "[default properties]", `${parentPath}/${SAFE_PATH_PREFIX}default`, null, NODE_TYPES.DEFAULT_PROPERTIES);

    const defaultNodes = defaultProperties.map((name, index) => createNode(defaultPropertiesNode, maybeEscapePropertyName(name), `${parentPath}/${SAFE_PATH_PREFIX}bucket${index}/${name}`, ownProperties[name]));
    nodes.push(setNodeChildren(defaultPropertiesNode, defaultNodes));
  }
  return nodes;
}

function makeNodesForOwnProps(propertiesNames, parent, ownProperties) {
  const parentPath = parent.path;
  return propertiesNames.map(name => createNode(parent, maybeEscapePropertyName(name), `${parentPath}/${name}`, ownProperties[name]));
}

function makeNodesForProperties(objProps, parent) {
  const {
    ownProperties = {},
    ownSymbols,
    prototype,
    safeGetterValues
  } = objProps;

  const parentPath = parent.path;
  const parentValue = getValue(parent);

  let allProperties = Object.assign({}, 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));
  });

  let nodes = [];
  if (parentValue && parentValue.class == "Window") {
    nodes = makeDefaultPropsBucket(propertiesNames, parent, allProperties);
  } else {
    nodes = makeNodesForOwnProps(propertiesNames, parent, allProperties);
  }

  if (Array.isArray(ownSymbols)) {
    ownSymbols.forEach((ownSymbol, index) => {
      nodes.push(createNode(parent, ownSymbol.name, `${parentPath}/${SAFE_PATH_PREFIX}symbol-${index}`, ownSymbol.descriptor || null));
    }, this);
  }

  if (nodeIsPromise(parent)) {
    nodes.push(...makeNodesForPromiseProperties(parent));
  }

  if (nodeHasEntries(parent)) {
    nodes.push(makeNodesForEntries(parent));
  }

  // Add the prototype if it exists and is not null
  if (prototype && prototype.type !== "null") {
    nodes.push(makeNodeForPrototype(objProps, parent));
  }

  return nodes;
}

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, "__proto__", `${parent.path}/__proto__`, { value: prototype }, NODE_TYPES.PROTOTYPE);
  }

  return null;
}

function createNode(parent, name, path, contents, type = NODE_TYPES.GRIP, meta) {
  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,
  // i.e. `{ foo: { bar: { baz: 5 }}}` will have a path of `foo/bar/baz`
  // for the inner object.
  return {
    parent,
    name,
    path,
    contents,
    type,
    meta
  };
}

function setNodeChildren(node, children) {
  node.contents = children;
  return node;
}

function getChildren(options) {
  const {
    cachedNodes,
    loadedProperties = new Map(),
    item
  } = options;

  const key = item.path;
  if (cachedNodes && cachedNodes.has(key)) {
    return cachedNodes.get(key);
  }

  const loadedProps = loadedProperties.get(key);
  const {
    ownProperties,
    ownSymbols,
    safeGetterValues,
    prototype
  } = loadedProps || {};
  const hasLoadedProps = ownProperties || ownSymbols || safeGetterValues || prototype;

  // 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 (nodeHasAccessors(item)) {
    return addToCache(makeNodesForAccessors(item));
  }

  if (nodeIsMapEntry(item)) {
    return addToCache(makeNodesForMapEntry(item));
  }

  if (nodeIsProxy(item)) {
    const nodes = makeNodesForProxyProperties(item);
    const protoNode = makeNodeForPrototype(loadedProps, item);
    if (protoNode) {
      return addToCache(nodes.concat(protoNode));
    }
    return nodes;
  }

  if (nodeNeedsNumericalBuckets(item)) {
    const bucketNodes = makeNumericalBuckets(item);
    // Even if we have numerical buckets, we might have loaded non indexed properties,
    // like length for example.
    if (hasLoadedProps) {
      return addToCache(bucketNodes.concat(makeNodesForProperties(loadedProps, item)));
    }

    // We don't cache the result here so we can have the prototype, properties and symbols
    // when they are loaded.
    return bucketNodes;
  }

  if (!nodeIsEntries(item) && !nodeIsBucket(item) && !nodeHasProperties(item)) {
    return [];
  }

  if (!hasLoadedProps) {
    return [];
  }

  return addToCache(makeNodesForProperties(loadedProps, item));
}

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);
}

module.exports = {
  createNode,
  getChildren,
  getClosestGripNode,
  getClosestNonBucketNode,
  getParent,
  getNumericalPropertiesCount,
  getValue,
  makeNodesForEntries,
  makeNodesForPromiseProperties,
  makeNodesForProperties,
  makeNumericalBuckets,
  nodeHasAccessors,
  nodeHasAllEntriesInPreview,
  nodeHasChildren,
  nodeHasEntries,
  nodeHasProperties,
  nodeIsBucket,
  nodeIsDefaultProperties,
  nodeIsEntries,
  nodeIsFunction,
  nodeIsGetter,
  nodeIsMapEntry,
  nodeIsMissingArguments,
  nodeIsObject,
  nodeIsOptimizedOut,
  nodeIsPrimitive,
  nodeIsPromise,
  nodeIsPrototype,
  nodeIsProxy,
  nodeIsSetter,
  nodeIsUninitializedBinding,
  nodeIsUnmappedBinding,
  nodeIsUnscopedBinding,
  nodeIsWindow,
  nodeNeedsNumericalBuckets,
  nodeSupportsNumericalBucketing,
  setNodeChildren,
  sortProperties,
  NODE_TYPES,
  // Export for testing purpose.
  SAFE_PATH_PREFIX
};

/***/ }),
/* 1941 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getHiddenTabs = getHiddenTabs;
exports.getSourceAnnotation = getSourceAnnotation;
exports.getTabMenuItems = getTabMenuItems;

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _source = __webpack_require__(1356);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/*
 * Finds the hidden tabs by comparing the tabs' top offset.
 * hidden tabs will have a great top offset.
 *
 * @param sourceTabs Immutable.list
 * @param sourceTabEls HTMLCollection
 *
 * @returns Immutable.list
 */

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 getHiddenTabs(sourceTabs, sourceTabEls) {
  sourceTabEls = [].slice.call(sourceTabEls);
  function getTopOffset() {
    const topOffsets = sourceTabEls.map(t => t.getBoundingClientRect().top);
    return Math.min(...topOffsets);
  }

  function hasTopOffset(el) {
    // adding 10px helps account for cases where the tab might be offset by
    // styling such as selected tabs which don't have a border.
    const tabTopOffset = getTopOffset();
    return el.getBoundingClientRect().top > tabTopOffset + 10;
  }

  return sourceTabs.filter((tab, index) => {
    const element = sourceTabEls[index];
    return element && hasTopOffset(element);
  });
}

function getSourceAnnotation(source, sourceMetaData) {
  const framework = sourceMetaData && sourceMetaData.framework ? sourceMetaData.framework : false;

  if (framework) {
    return _react2.default.createElement("img", { className: framework.toLowerCase() });
  }

  if ((0, _source.isPretty)(source)) {
    return _react2.default.createElement("img", { className: "prettyPrint" });
  }
  if (source.get("isBlackBoxed")) {
    return _react2.default.createElement("img", { className: "blackBox" });
  }
}

function getTabMenuItems() {
  return {
    closeTab: {
      id: "node-menu-close-tab",
      label: L10N.getStr("sourceTabs.closeTab"),
      accesskey: L10N.getStr("sourceTabs.closeTab.accesskey"),
      disabled: false
    },
    closeOtherTabs: {
      id: "node-menu-close-other-tabs",
      label: L10N.getStr("sourceTabs.closeOtherTabs"),
      accesskey: L10N.getStr("sourceTabs.closeOtherTabs.accesskey"),
      disabled: false
    },
    closeTabsToEnd: {
      id: "node-menu-close-tabs-to-end",
      label: L10N.getStr("sourceTabs.closeTabsToEnd"),
      accesskey: L10N.getStr("sourceTabs.closeTabsToEnd.accesskey"),
      disabled: false
    },
    closeAllTabs: {
      id: "node-menu-close-all-tabs",
      label: L10N.getStr("sourceTabs.closeAllTabs"),
      accesskey: L10N.getStr("sourceTabs.closeAllTabs.accesskey"),
      disabled: false
    },
    showSource: {
      id: "node-menu-show-source",
      label: L10N.getStr("sourceTabs.revealInTree"),
      accesskey: L10N.getStr("sourceTabs.revealInTree.accesskey"),
      disabled: false
    },
    copySourceUri2: {
      id: "node-menu-copy-source-url",
      label: L10N.getStr("copySourceUri2"),
      accesskey: L10N.getStr("copySourceUri2.accesskey"),
      disabled: false
    },
    prettyPrint: {
      id: "node-menu-pretty-print",
      label: L10N.getStr("sourceTabs.prettyPrint"),
      accesskey: L10N.getStr("sourceTabs.prettyPrint.accesskey"),
      disabled: false
    }
  };
}

/***/ }),
/* 1942 */,
/* 1943 */,
/* 1944 */,
/* 1945 */,
/* 1946 */,
/* 1947 */,
/* 1948 */,
/* 1949 */,
/* 1950 */,
/* 1951 */,
/* 1952 */,
/* 1953 */,
/* 1954 */,
/* 1955 */,
/* 1956 */,
/* 1957 */,
/* 1958 */,
/* 1959 */,
/* 1960 */,
/* 1961 */,
/* 1962 */,
/* 1963 */,
/* 1964 */,
/* 1965 */,
/* 1966 */,
/* 1967 */,
/* 1968 */,
/* 1969 */,
/* 1970 */,
/* 1971 */,
/* 1972 */,
/* 1973 */,
/* 1974 */,
/* 1975 */,
/* 1976 */,
/* 1977 */,
/* 1978 */,
/* 1979 */,
/* 1980 */,
/* 1981 */,
/* 1982 */,
/* 1983 */,
/* 1984 */,
/* 1985 */,
/* 1986 */,
/* 1987 */,
/* 1988 */,
/* 1989 */,
/* 1990 */,
/* 1991 */,
/* 1992 */,
/* 1993 */,
/* 1994 */,
/* 1995 */,
/* 1996 */,
/* 1997 */,
/* 1998 */,
/* 1999 */,
/* 2000 */,
/* 2001 */,
/* 2002 */,
/* 2003 */,
/* 2004 */,
/* 2005 */,
/* 2006 */,
/* 2007 */,
/* 2008 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.setPopupObjectProperties = setPopupObjectProperties;

var _selectors = __webpack_require__(1352);

/**
 * @memberof actions/pause
 * @static
 */
function setPopupObjectProperties(object, properties) {
  return ({ dispatch, client, getState }) => {
    const objectId = object.actor || object.objectId;

    if ((0, _selectors.getPopupObjectProperties)(getState(), object.actor)) {
      return;
    }

    dispatch({
      type: "SET_POPUP_OBJECT_PROPERTIES",
      objectId,
      properties
    });
  };
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/***/ }),
/* 2009 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.ProjectSearch = undefined;

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; }; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                                                                                                                                                                                   * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                                                                                                                                                                                                                                   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

var _propTypes = __webpack_require__(20);

var _propTypes2 = _interopRequireDefault(_propTypes);

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _reactRedux = __webpack_require__(1189);

var _classnames = __webpack_require__(175);

var _classnames2 = _interopRequireDefault(_classnames);

var _redux = __webpack_require__(3);

var _actions = __webpack_require__(1354);

var _actions2 = _interopRequireDefault(_actions);

var _projectSearch = __webpack_require__(2010);

var _projectTextSearch = __webpack_require__(1424);

var _sourcesTree = __webpack_require__(1442);

var _selectors = __webpack_require__(1352);

var _Svg = __webpack_require__(1359);

var _Svg2 = _interopRequireDefault(_Svg);

var _ManagedTree = __webpack_require__(1404);

var _ManagedTree2 = _interopRequireDefault(_ManagedTree);

var _SearchInput = __webpack_require__(1379);

var _SearchInput2 = _interopRequireDefault(_SearchInput);

__webpack_require__(2011);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function getFilePath(item, index) {
  return item.type === "RESULT" ? `${item.sourceId}-${index || "$"}` : `${item.sourceId}-${item.line}-${item.column}-${index || "$"}`;
}

function sanitizeQuery(query) {
  // no '\' at end of query
  return query.replace(/\\$/, "");
}

class ProjectSearch extends _react.Component {
  constructor(props) {
    super(props);

    this.toggleProjectTextSearch = (key, e) => {
      const { closeProjectSearch, setActiveSearch } = this.props;
      if (e) {
        e.preventDefault();
      }

      if (this.isProjectSearchEnabled()) {
        return closeProjectSearch();
      }

      return setActiveSearch("project");
    };

    this.isProjectSearchEnabled = () => this.props.activeSearch === "project";

    this.selectMatchItem = matchItem => {
      this.props.selectLocation(_extends({}, matchItem));
    };

    this.getResults = () => {
      const { results } = this.props;
      return results.toJS().map(result => _extends({
        type: "RESULT"
      }, result, {
        matches: result.matches.map(m => _extends({ type: "MATCH" }, m))
      })).filter(result => result.filepath && result.matches.length > 0);
    };

    this.getResultCount = () => this.getResults().reduce((count, file) => count + file.matches.length, 0);

    this.onKeyDown = e => {
      if (e.key === "Escape") {
        return;
      }

      e.stopPropagation();

      if (e.key !== "Enter") {
        return;
      }
      this.focusedItem = null;
      const query = sanitizeQuery(this.state.inputValue);
      if (query) {
        this.props.searchSources(query);
      }
    };

    this.onEnterPress = () => {
      if (this.focusedItem && !this.state.inputFocused) {
        const { setExpanded, file, expanded, match } = this.focusedItem;
        if (setExpanded) {
          setExpanded(file, !expanded);
        } else if (match) {
          this.selectMatchItem(match);
        }
      }
    };

    this.inputOnChange = e => {
      const inputValue = e.target.value;
      const { clearSearch } = this.props;
      this.setState({ inputValue });
      if (inputValue === "") {
        clearSearch();
      }
    };

    this.renderFile = (file, focused, expanded, setExpanded) => {
      if (focused) {
        this.focusedItem = { setExpanded, file, expanded };
      }

      const matchesLength = file.matches.length;
      const matches = ` (${matchesLength} match${matchesLength > 1 ? "es" : ""})`;

      return _react2.default.createElement(
        "div",
        {
          className: (0, _classnames2.default)("file-result", { focused }),
          key: file.sourceId,
          onClick: e => setExpanded(file, !expanded)
        },
        _react2.default.createElement(_Svg2.default, { name: "arrow", className: (0, _classnames2.default)({ expanded }) }),
        _react2.default.createElement("img", { className: "file" }),
        _react2.default.createElement(
          "span",
          { className: "file-path" },
          (0, _sourcesTree.getRelativePath)(file.filepath)
        ),
        _react2.default.createElement(
          "span",
          { className: "matches-summary" },
          matches
        )
      );
    };

    this.renderMatch = (match, focused) => {
      if (focused) {
        this.focusedItem = { match };
      }
      return _react2.default.createElement(
        "div",
        {
          className: (0, _classnames2.default)("result", { focused }),
          onClick: () => setTimeout(() => this.selectMatchItem(match), 50)
        },
        _react2.default.createElement(
          "span",
          { className: "line-number", key: match.line },
          match.line
        ),
        (0, _projectSearch.highlightMatches)(match)
      );
    };

    this.renderItem = (item, depth, focused, _, expanded, { setExpanded }) => {
      if (item.type === "RESULT") {
        return this.renderFile(item, focused, expanded, setExpanded);
      }
      return this.renderMatch(item, focused);
    };

    this.renderResults = () => {
      const results = this.getResults();
      const { status } = this.props;
      if (!this.props.query) {
        return;
      }
      if (results.length && status === _projectTextSearch.statusType.done) {
        return _react2.default.createElement(_ManagedTree2.default, {
          getRoots: () => results,
          getChildren: file => file.matches || [],
          itemHeight: 24,
          autoExpandAll: true,
          autoExpandDepth: 1,
          getParent: item => null,
          getPath: getFilePath,
          renderItem: this.renderItem
        });
      }
      const msg = status === _projectTextSearch.statusType.fetching ? L10N.getStr("loadingText") : L10N.getStr("projectTextSearch.noResults");
      return _react2.default.createElement(
        "div",
        { className: "no-result-msg absolute-center" },
        msg
      );
    };

    this.renderSummary = () => {
      return this.props.query !== "" ? L10N.getFormatStr("sourceSearch.resultsSummary1", this.getResultCount()) : "";
    };

    this.state = {
      inputValue: this.props.query || "",
      inputFocused: false
    };
  }

  componentDidMount() {
    const { shortcuts } = this.context;

    shortcuts.on(L10N.getStr("projectTextSearch.key"), this.toggleProjectTextSearch);
    shortcuts.on("Enter", this.onEnterPress);
  }

  componentWillUnmount() {
    const { shortcuts } = this.context;
    shortcuts.off(L10N.getStr("projectTextSearch.key"), this.toggleProjectTextSearch);
    shortcuts.off("Enter", this.onEnterPress);
  }

  shouldShowErrorEmoji() {
    return !this.getResultCount() && this.props.status === _projectTextSearch.statusType.done;
  }

  renderInput() {
    return _react2.default.createElement(_SearchInput2.default, {
      query: this.state.inputValue,
      count: this.getResultCount(),
      placeholder: L10N.getStr("projectTextSearch.placeholder"),
      size: "big",
      showErrorEmoji: this.shouldShowErrorEmoji(),
      summaryMsg: this.renderSummary(),
      onChange: this.inputOnChange,
      onFocus: () => this.setState({ inputFocused: true }),
      onBlur: () => this.setState({ inputFocused: false }),
      onKeyDown: this.onKeyDown,
      handleClose: this.props.closeProjectSearch,
      ref: "searchInput"
    });
  }

  render() {
    if (!this.isProjectSearchEnabled()) {
      return null;
    }

    return _react2.default.createElement(
      "div",
      { className: "search-container" },
      _react2.default.createElement(
        "div",
        { className: "project-text-search" },
        _react2.default.createElement(
          "div",
          { className: "header" },
          this.renderInput()
        ),
        this.renderResults()
      )
    );
  }
}
exports.ProjectSearch = ProjectSearch;
ProjectSearch.contextTypes = {
  shortcuts: _propTypes2.default.object
};

exports.default = (0, _reactRedux.connect)(state => ({
  sources: (0, _selectors.getSources)(state),
  activeSearch: (0, _selectors.getActiveSearch)(state),
  results: (0, _selectors.getTextSearchResults)(state),
  query: (0, _selectors.getTextSearchQuery)(state),
  status: (0, _selectors.getTextSearchStatus)(state)
}), dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(ProjectSearch);

/***/ }),
/* 2010 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.highlightMatches = highlightMatches;

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function highlightMatches(lineMatch) {
  const { value, column, match } = lineMatch;
  const len = match.length;

  return _react2.default.createElement(
    "span",
    { className: "line-value" },
    _react2.default.createElement(
      "span",
      { className: "line-match", key: 0 },
      value.slice(0, column)
    ),
    _react2.default.createElement(
      "span",
      { className: "query-match", key: 1 },
      value.substr(column, len)
    ),
    _react2.default.createElement(
      "span",
      { className: "line-match", key: 2 },
      value.slice(column + len, value.length)
    )
  );
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

// Maybe reuse file search's functions?

/***/ }),
/* 2011 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 2012 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\"><g stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\" stroke-linecap=\"round\"><g id=\"arrow\" transform=\"translate(1.000000, 3.000000)\"><path d=\"M4.5,0.5 L6.5,2.5\"></path><path d=\"M4.5,2.5 L6.5,4.5\" transform=\"translate(5.500000, 3.500000) scale(1, -1) translate(-5.500000, -3.500000) \"></path><path d=\"M6.00090144,2.5 C4.67806937,2.5 3.67938478,2.5 3.00484766,2.5 C1.99304199,2.5 1.01049805,3.5168457 0.993840144,4.52403846 C0.988750751,4.54723808 0.988750751,5.87097168 0.993840144,8.49523926\" id=\"Path-2\" stroke-linejoin=\"round\"></path></g><g id=\"content-lines\" transform=\"translate(9.000000, 2.000000)\"><path d=\"M1.5,3.5 L5.5,3.5\"></path><path d=\"M0.5,1.5 L5.5,1.5\"></path><path d=\"M0.5,5.5 L5.5,5.5\"></path></g></g></svg>"

/***/ }),
/* 2013 */,
/* 2014 */,
/* 2015 */,
/* 2016 */,
/* 2017 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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,
  getPrototype,
  enumSymbols
} = __webpack_require__(1939);

const {
  getClosestGripNode,
  getClosestNonBucketNode,
  getValue,
  nodeHasAccessors,
  nodeHasAllEntriesInPreview,
  nodeHasProperties,
  nodeIsBucket,
  nodeIsDefaultProperties,
  nodeIsEntries,
  nodeIsMapEntry,
  nodeIsPrimitive,
  nodeIsProxy,
  nodeNeedsNumericalBuckets
} = __webpack_require__(1940);

function loadItemProperties(item, createObjectClient, loadedProperties) {
  const [start, end] = item.meta ? [item.meta.startIndex, item.meta.endIndex] : [];

  let objectClient;
  const getObjectClient = () => {
    if (objectClient) {
      return objectClient;
    }

    const gripItem = getClosestGripNode(item);
    const value = getValue(gripItem);
    return createObjectClient(value);
  };

  let loadingPromises = [];
  if (shouldLoadItemIndexedProperties(item, loadedProperties)) {
    loadingPromises.push(enumIndexedProperties(getObjectClient(), start, end));
  }

  if (shouldLoadItemNonIndexedProperties(item, loadedProperties)) {
    loadingPromises.push(enumNonIndexedProperties(getObjectClient(), start, end));
  }

  if (shouldLoadItemEntries(item, loadedProperties)) {
    loadingPromises.push(enumEntries(getObjectClient(), start, end));
  }

  if (shouldLoadItemPrototype(item, loadedProperties)) {
    loadingPromises.push(getPrototype(getObjectClient()));
  }

  if (shouldLoadItemSymbols(item, loadedProperties)) {
    loadingPromises.push(enumSymbols(getObjectClient(), start, end));
  }

  if (loadingPromises.length === 0) {
    return null;
  }

  return Promise.all(loadingPromises).then(responses => responses.reduce((accumulator, res) => {
    // Let's loop through the responses to build a single response object.
    Object.entries(res).forEach(([k, v]) => {
      if (accumulator.hasOwnProperty(k)) {
        if (Array.isArray(accumulator[k])) {
          accumulator[k].push(...v);
        } else if (typeof accumulator[k] === "object") {
          accumulator[k] = Object.assign({}, accumulator[k], v);
        }
      } else {
        accumulator[k] = v;
      }
    });
    return accumulator;
  }, {}));
}

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)) && !nodeHasAllEntriesInPreview(gripItem) && !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);
}

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) && !nodeIsProxy(item);
}

module.exports = {
  loadItemProperties,
  shouldLoadItemEntries,
  shouldLoadItemIndexedProperties,
  shouldLoadItemNonIndexedProperties,
  shouldLoadItemPrototype,
  shouldLoadItemSymbols
};

/***/ }),
/* 2018 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

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; }; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                                                                                                                                                                                   * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                                                                                                                                                                                                                                   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

var _react = __webpack_require__(0);

var _react2 = _interopRequireDefault(_react);

var _reactRedux = __webpack_require__(1189);

var _redux = __webpack_require__(3);

var _devtoolsContextmenu = __webpack_require__(1413);

var _Close = __webpack_require__(1374);

var _Close2 = _interopRequireDefault(_Close);

var _actions = __webpack_require__(1354);

var _actions2 = _interopRequireDefault(_actions);

var _source = __webpack_require__(1356);

var _clipboard = __webpack_require__(1388);

var _tabs = __webpack_require__(1941);

var _selectors = __webpack_require__(1352);

var _classnames = __webpack_require__(175);

var _classnames2 = _interopRequireDefault(_classnames);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

class Tab extends _react.PureComponent {
  constructor(...args) {
    var _temp;

    return _temp = super(...args), this.onTabContextMenu = (event, tab) => {
      event.preventDefault();
      this.showContextMenu(event, tab);
    }, _temp;
  }

  showContextMenu(e, tab) {
    const {
      closeTab,
      closeTabs,
      tabSources,
      showSource,
      togglePrettyPrint
    } = this.props;

    const otherTabs = tabSources.filter(t => t.get("id") !== tab);
    const sourceTab = tabSources.find(t => t.get("id") == tab);
    const tabURLs = tabSources.map(t => t.get("url"));
    const otherTabURLs = otherTabs.map(t => t.get("url"));

    if (!sourceTab) {
      return;
    }

    const isPrettySource = (0, _source.isPretty)(sourceTab);
    const tabMenuItems = (0, _tabs.getTabMenuItems)();
    const items = [{
      item: _extends({}, tabMenuItems.closeTab, {
        click: () => closeTab(sourceTab.get("url"))
      })
    }, {
      item: _extends({}, tabMenuItems.closeOtherTabs, {
        click: () => closeTabs(otherTabURLs)
      }),
      hidden: () => tabSources.size === 1
    }, {
      item: _extends({}, tabMenuItems.closeTabsToEnd, {
        click: () => {
          const tabIndex = tabSources.findIndex(t => t.get("id") == tab);
          closeTabs(tabURLs.filter((t, i) => i > tabIndex));
        }
      }),
      hidden: () => tabSources.size === 1 || tabSources.some((t, i) => t === tab && tabSources.size - 1 === i)
    }, {
      item: _extends({}, tabMenuItems.closeAllTabs, { click: () => closeTabs(tabURLs) })
    }, { item: { type: "separator" } }, {
      item: _extends({}, tabMenuItems.copySourceUri2, {
        click: () => (0, _clipboard.copyToTheClipboard)((0, _source.getRawSourceURL)(sourceTab.get("url")))
      })
    }];

    items.push({
      item: _extends({}, tabMenuItems.showSource, { click: () => showSource(tab) })
    });

    if (!isPrettySource) {
      items.push({
        item: _extends({}, tabMenuItems.prettyPrint, {
          click: () => togglePrettyPrint(tab)
        })
      });
    }

    (0, _devtoolsContextmenu.showMenu)(e, (0, _devtoolsContextmenu.buildMenu)(items));
  }

  isProjectSearchEnabled() {
    return this.props.activeSearch === "project";
  }

  isSourceSearchEnabled() {
    return this.props.activeSearch === "source";
  }

  render() {
    const {
      selectedSource,
      selectSource,
      closeTab,
      source,
      sourceMetaData
    } = this.props;
    const src = source.toJS();
    const filename = (0, _source.getFilename)(src);
    const sourceId = source.get("id");
    const active = selectedSource && sourceId == selectedSource.get("id") && !this.isProjectSearchEnabled() && !this.isSourceSearchEnabled();
    const isPrettyCode = (0, _source.isPretty)(source);
    const sourceAnnotation = (0, _tabs.getSourceAnnotation)(source, sourceMetaData);

    function onClickClose(e) {
      e.stopPropagation();
      closeTab(source.get("url"));
    }

    function handleTabClick(e) {
      e.preventDefault();
      e.stopPropagation();

      // Accommodate middle click to close tab
      if (e.button === 1) {
        return closeTab(source.get("url"));
      }

      return selectSource(sourceId);
    }

    const className = (0, _classnames2.default)("source-tab", {
      active,
      pretty: isPrettyCode
    });

    return _react2.default.createElement(
      "div",
      {
        className: className,
        key: sourceId,
        onMouseUp: handleTabClick,
        onContextMenu: e => this.onTabContextMenu(e, sourceId),
        title: (0, _source.getFileURL)(src)
      },
      sourceAnnotation,
      _react2.default.createElement(
        "div",
        { className: "filename" },
        filename
      ),
      _react2.default.createElement(_Close2.default, {
        handleClick: onClickClose,
        tooltip: L10N.getStr("sourceTabs.closeTabButtonTooltip")
      })
    );
  }
}
exports.default = (0, _reactRedux.connect)((state, props) => {
  const selectedSource = (0, _selectors.getSelectedSource)(state);
  const { source } = props;
  return {
    tabSources: (0, _selectors.getSourcesForTabs)(state),
    selectedSource: selectedSource,
    sourceMetaData: (0, _selectors.getSourceMetaData)(state, source.get("id")),
    activeSearch: (0, _selectors.getActiveSearch)(state)
  };
}, dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(Tab);

/***/ }),
/* 2019 */,
/* 2020 */,
/* 2021 */,
/* 2022 */,
/* 2023 */,
/* 2024 */,
/* 2025 */,
/* 2026 */,
/* 2027 */,
/* 2028 */,
/* 2029 */,
/* 2030 */,
/* 2031 */,
/* 2032 */,
/* 2033 */,
/* 2034 */,
/* 2035 */,
/* 2036 */,
/* 2037 */,
/* 2038 */,
/* 2039 */,
/* 2040 */,
/* 2041 */,
/* 2042 */,
/* 2043 */,
/* 2044 */,
/* 2045 */,
/* 2046 */,
/* 2047 */,
/* 2048 */,
/* 2049 */,
/* 2050 */,
/* 2051 */,
/* 2052 */,
/* 2053 */,
/* 2054 */,
/* 2055 */,
/* 2056 */,
/* 2057 */,
/* 2058 */,
/* 2059 */,
/* 2060 */,
/* 2061 */,
/* 2062 */,
/* 2063 */,
/* 2064 */,
/* 2065 */,
/* 2066 */,
/* 2067 */,
/* 2068 */,
/* 2069 */,
/* 2070 */,
/* 2071 */,
/* 2072 */,
/* 2073 */,
/* 2074 */,
/* 2075 */,
/* 2076 */,
/* 2077 */,
/* 2078 */,
/* 2079 */,
/* 2080 */,
/* 2081 */,
/* 2082 */,
/* 2083 */,
/* 2084 */,
/* 2085 */,
/* 2086 */,
/* 2087 */,
/* 2088 */,
/* 2089 */,
/* 2090 */,
/* 2091 */,
/* 2092 */,
/* 2093 */,
/* 2094 */,
/* 2095 */,
/* 2096 */,
/* 2097 */,
/* 2098 */,
/* 2099 */,
/* 2100 */,
/* 2101 */,
/* 2102 */,
/* 2103 */,
/* 2104 */,
/* 2105 */,
/* 2106 */,
/* 2107 */,
/* 2108 */,
/* 2109 */,
/* 2110 */,
/* 2111 */,
/* 2112 */,
/* 2113 */,
/* 2114 */,
/* 2115 */,
/* 2116 */,
/* 2117 */,
/* 2118 */,
/* 2119 */,
/* 2120 */,
/* 2121 */,
/* 2122 */,
/* 2123 */,
/* 2124 */,
/* 2125 */,
/* 2126 */,
/* 2127 */,
/* 2128 */,
/* 2129 */,
/* 2130 */,
/* 2131 */,
/* 2132 */,
/* 2133 */,
/* 2134 */,
/* 2135 */,
/* 2136 */,
/* 2137 */,
/* 2138 */,
/* 2139 */,
/* 2140 */,
/* 2141 */,
/* 2142 */,
/* 2143 */,
/* 2144 */,
/* 2145 */,
/* 2146 */,
/* 2147 */,
/* 2148 */,
/* 2149 */,
/* 2150 */,
/* 2151 */,
/* 2152 */,
/* 2153 */,
/* 2154 */,
/* 2155 */,
/* 2156 */,
/* 2157 */,
/* 2158 */,
/* 2159 */,
/* 2160 */,
/* 2161 */,
/* 2162 */,
/* 2163 */,
/* 2164 */,
/* 2165 */,
/* 2166 */,
/* 2167 */,
/* 2168 */,
/* 2169 */,
/* 2170 */,
/* 2171 */,
/* 2172 */,
/* 2173 */,
/* 2174 */,
/* 2175 */,
/* 2176 */,
/* 2177 */,
/* 2178 */,
/* 2179 */,
/* 2180 */,
/* 2181 */,
/* 2182 */,
/* 2183 */,
/* 2184 */,
/* 2185 */,
/* 2186 */,
/* 2187 */,
/* 2188 */,
/* 2189 */,
/* 2190 */,
/* 2191 */,
/* 2192 */,
/* 2193 */,
/* 2194 */,
/* 2195 */,
/* 2196 */,
/* 2197 */,
/* 2198 */,
/* 2199 */,
/* 2200 */,
/* 2201 */,
/* 2202 */,
/* 2203 */,
/* 2204 */,
/* 2205 */,
/* 2206 */,
/* 2207 */,
/* 2208 */,
/* 2209 */,
/* 2210 */,
/* 2211 */,
/* 2212 */,
/* 2213 */,
/* 2214 */,
/* 2215 */,
/* 2216 */,
/* 2217 */,
/* 2218 */,
/* 2219 */,
/* 2220 */,
/* 2221 */,
/* 2222 */,
/* 2223 */,
/* 2224 */,
/* 2225 */,
/* 2226 */,
/* 2227 */,
/* 2228 */,
/* 2229 */,
/* 2230 */,
/* 2231 */,
/* 2232 */,
/* 2233 */,
/* 2234 */,
/* 2235 */,
/* 2236 */,
/* 2237 */,
/* 2238 */,
/* 2239 */,
/* 2240 */,
/* 2241 */,
/* 2242 */,
/* 2243 */,
/* 2244 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getTokenLocation = getTokenLocation;
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 getTokenLocation(codeMirror, tokenEl) {
  const { left, top, width, height } = tokenEl.getBoundingClientRect();
  const { line, ch } = codeMirror.coordsChar({
    left: left + width / 2,
    top: top + height / 2
  });

  return {
    line: line + 1,
    column: ch
  };
}

/***/ }),
/* 2245 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.updateTree = updateTree;

var _addToTree = __webpack_require__(1443);

var _collapseTree = __webpack_require__(1444);

var _utils = __webpack_require__(1371);

function newSourcesSet(newSources, prevSources) {
  const next = newSources.toSet();
  const prev = prevSources.toSet();
  return next.subtract(prev);
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.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 updateTree({
  newSources,
  prevSources,
  debuggeeUrl,
  projectRoot,
  uncollapsedTree,
  sourceTree
}) {
  const newSet = newSourcesSet(newSources, prevSources);

  for (const source of newSet) {
    (0, _addToTree.addToTree)(uncollapsedTree, source, debuggeeUrl, projectRoot);
  }

  const newSourceTree = (0, _collapseTree.collapseTree)(uncollapsedTree);

  return {
    uncollapsedTree,
    sourceTree: newSourceTree,
    parentMap: (0, _utils.createParentMap)(sourceTree),
    focusedItem: null
  };
}

/***/ }),
/* 2246 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

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; };

exports.setupHelper = setupHelper;

var _redux = __webpack_require__(3);

var _timings = __webpack_require__(1657);

var timings = _interopRequireWildcard(_timings);

var _prefs = __webpack_require__(226);

var _devtoolsConfig = __webpack_require__(1355);

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

function findSource(dbg, url) {
  const sources = dbg.selectors.getSources();
  const source = sources.find(s => (s.get("url") || "").includes(url));

  if (!source) {
    return;
  }

  return source.toJS();
}

function sendPacket(dbg, packet, callback) {
  dbg.connection.tabConnection.debuggerClient.request(packet).then(callback || console.log);
}

function evaluate(dbg, expression, callback) {
  dbg.client.evaluate(expression).then(callback || console.log);
}

function bindSelectors(obj) {
  return Object.keys(obj.selectors).reduce((bound, selector) => {
    bound[selector] = (a, b, c) => obj.selectors[selector](obj.store.getState(), a, b, c);
    return bound;
  }, {});
}

function getCM() {
  const cm = document.querySelector(".CodeMirror");
  return cm && cm.CodeMirror;
}

function setupHelper(obj) {
  const selectors = bindSelectors(obj);
  const actions = (0, _redux.bindActionCreators)(obj.actions, obj.store.dispatch);
  const dbg = _extends({}, obj, {
    selectors,
    actions,
    prefs: _prefs.prefs,
    features: _prefs.features,
    timings,
    getCM,
    helpers: {
      findSource: url => findSource(dbg, url),
      evaluate: (expression, cbk) => evaluate(dbg, expression, cbk),
      sendPacket: (packet, cbk) => sendPacket(dbg, packet, cbk)
    }
  });

  window.dbg = dbg;

  if ((0, _devtoolsConfig.isDevelopment)()) {
    console.group("Development Notes");
    const baseUrl = "https://devtools-html.github.io/debugger.html";
    const localDevelopmentUrl = `${baseUrl}/docs/dbg.html`;
    console.log("Debugging Tips", localDevelopmentUrl);
    console.log("dbg", window.dbg);
    console.groupEnd();
  }
}

/***/ }),
/* 2247 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M8 13.4c-.5 0-.9-.2-1.2-.6L.4 5.2C0 4.7-.1 4.3.2 3.7S1 3 1.6 3h12.8c.6 0 1.2.1 1.4.7.3.6.2 1.1-.2 1.6l-6.4 7.6c-.3.4-.7.5-1.2.5z\"></path></svg>"

/***/ }),
/* 2248 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 2249 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


const PropTypes = __webpack_require__(20);

const { wrapRender } = __webpack_require__(1353);
const { MODE } = __webpack_require__(1357);
const { ModePropType } = __webpack_require__(1448);

const dom = __webpack_require__(1758);
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)
};

/***/ }),
/* 2250 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 viewBox=\"0 0 128 128\"><path d=\"M50.3 29.6c11.7-1 15-8.5 28.7-9.8 6.7-.6 11 .8 11.4 3.1.4 2.2-2.9 3.7-7 4-5.6.6-8-1.5-8.4-3.4-4.1.4-4.8 2.2-4.6 3.5.4 2.4 5.5 4.7 14.1 3.9 9.8-.8 13-4.6 12.2-8.5-1-5-8.5-9.2-22-8-17.3 1.6-17.2 9.5-28.9 10.5-4.8.4-7.5-.7-8-2.6-.3-1.9 2-2.8 4.8-3 2.6-.2 5.7.2 7.2 1 1.1-.6 1.5-1.1 1.3-1.8-.4-1.8-4-2.6-8.5-2.2-8.7.8-8.7 4.7-8.4 6.4 1.1 4.7 7.8 7.7 16.1 6.9zM108.9 49.4c-10.8 2.5-24.6 4.1-41.2 4.1-16.9 0-30.7-1.8-41.5-4.1-9.6-2.5-14.8-5.2-16.6-8 .9 6.3 2.5 12.4 4.6 18.2-2.4 1.5-4.7 3.5-6.7 6-3.8 4.8-5.5 10.4-5.2 15.9.3 5.5 3 10 7.3 13.5 4.5 3.5 9.3 4.5 14.8 3.5 2.1-.3 4.5-1.5 6.6-2.1-4.5 0-8.3-1.5-12.1-4.5-4.1-3-7-7.3-7.6-12.4-1-4.8 0-9.3 2.7-13.2.6-.8 1.2-1.4 1.9-2 1.5 3.8 3.3 7.4 5.2 10.9 4.1 6.3 8.3 11.8 12.4 17.7 1.8 3.5 3 7 3.8 10.4 2.7 3.8 6.6 6.5 11.4 7.9 5.9 2.1 12.1 2.9 18.4 2.9h.7c6.3 0 12.9-1 19-3 4.5-1.5 8.3-4 11.1-8h.3c.7-3 1.8-6.8 3.5-10.3 4.1-5.9 8.3-11.4 12.4-17.7 5.5-10 9.3-21.4 11.4-33.6-2.1 3-7.3 5.8-16.6 7.9zM26.2 40.7c10.8 2.7 24.6 4.1 41.2 4.1 16.9 0 30.4-1.5 41.2-4.1 11.4-2.7 16.9-6.3 16.9-9.6 0-2.5-2.5-4.8-7-6.6 1 .7 1.8 1.8 1.8 3 0 3.5-5.2 6.3-15.6 8.6-9.6 2.1-22 3.6-37 3.6-14.5 0-27.4-1.5-36.7-3.5-10-2.5-15.3-5.2-15.3-8.6 0-1.5.7-2.7 2.7-4.1-6.3 2.5-9.6 4.5-9.6 7.6.3 3.5 5.9 7 17.4 9.6z\"></path></svg>"

/***/ }),
/* 2251 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" viewBox=\"0 0 28 28\"><path d=\"M9.633 7.968h3.751v10.514c0 4.738-2.271 6.392-5.899 6.392-0.888 0-2.024-0.148-2.764-0.395l0.42-3.036c0.518 0.173 1.185 0.296 1.925 0.296 1.58 0 2.567-0.716 2.567-3.282v-10.489zM16.641 20.753c0.987 0.518 2.567 1.037 4.171 1.037 1.728 0 2.641-0.716 2.641-1.826 0-1.012-0.79-1.629-2.789-2.32-2.764-0.987-4.59-2.517-4.59-4.961 0-2.838 2.394-4.985 6.293-4.985 1.9 0 3.258 0.37 4.245 0.839l-0.839 3.011c-0.642-0.321-1.851-0.79-3.455-0.79-1.629 0-2.419 0.765-2.419 1.604 0 1.061 0.913 1.53 3.085 2.369 2.937 1.086 4.294 2.616 4.294 4.985 0 2.789-2.122 5.158-6.688 5.158-1.9 0-3.776-0.518-4.714-1.037l0.765-3.085z\"></path></svg>"

/***/ }),
/* 2252 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 viewBox=\"0 0 128 128\"><path xmlns=\"http://www.w3.org/2000/svg\" class=\"cls-2\" id=\"original-2\" d=\"M 1.5 63.91 v 62.5 h 125 V 1.41 H 1.5 Z m 100.73 -5 a 15.56 15.56 0 0 1 7.82 4.5 a 20.58 20.58 0 0 1 3 4 c 0 0.16 -5.4 3.81 -8.69 5.85 c -0.12 0.08 -0.6 -0.44 -1.13 -1.23 a 7.09 7.09 0 0 0 -5.87 -3.53 c -3.79 -0.26 -6.23 1.73 -6.21 5 a 4.58 4.58 0 0 0 0.54 2.34 c 0.83 1.73 2.38 2.76 7.24 4.86 c 8.95 3.85 12.78 6.39 15.16 10 c 2.66 4 3.25 10.46 1.45 15.24 c -2 5.2 -6.9 8.73 -13.83 9.9 a 38.32 38.32 0 0 1 -9.52 -0.1 a 23 23 0 0 1 -12.72 -6.63 c -1.15 -1.27 -3.39 -4.58 -3.25 -4.82 a 9.34 9.34 0 0 1 1.15 -0.73 L 82 101 l 3.59 -2.08 l 0.75 1.11 a 16.78 16.78 0 0 0 4.74 4.54 c 4 2.1 9.46 1.81 12.16 -0.62 a 5.43 5.43 0 0 0 0.69 -6.92 c -1 -1.39 -3 -2.56 -8.59 -5 c -6.45 -2.78 -9.23 -4.5 -11.77 -7.24 a 16.48 16.48 0 0 1 -3.43 -6.25 a 25 25 0 0 1 -0.22 -8 c 1.33 -6.23 6 -10.58 12.82 -11.87 A 31.66 31.66 0 0 1 102.23 58.93 Z M 72.89 64.15 l 0 5.12 H 56.66 V 115.5 H 45.15 V 69.26 H 28.88 v -5 A 49.19 49.19 0 0 1 29 59.09 C 29.08 59 39 59 51 59 L 72.83 59 Z\" data-name=\"original\"></path></svg>"

/***/ }),
/* 2253 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 2254 */,
/* 2255 */,
/* 2256 */,
/* 2257 */,
/* 2258 */,
/* 2259 */,
/* 2260 */,
/* 2261 */,
/* 2262 */,
/* 2263 */,
/* 2264 */,
/* 2265 */,
/* 2266 */,
/* 2267 */,
/* 2268 */,
/* 2269 */,
/* 2270 */,
/* 2271 */,
/* 2272 */,
/* 2273 */,
/* 2274 */,
/* 2275 */,
/* 2276 */,
/* 2277 */,
/* 2278 */,
/* 2279 */,
/* 2280 */,
/* 2281 */,
/* 2282 */,
/* 2283 */,
/* 2284 */,
/* 2285 */,
/* 2286 */,
/* 2287 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* WEBPACK VAR INJECTION */(function(global, module) {/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__ponyfill_js__ = __webpack_require__(2289);
/* 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 (true) {
  root = module;
} else {
  root = Function('return this')();
}

var result = Object(__WEBPACK_IMPORTED_MODULE_0__ponyfill_js__["a" /* default */])(root);
/* harmony default export */ __webpack_exports__["a"] = (result);

/* WEBPACK VAR INJECTION */}.call(__webpack_exports__, __webpack_require__(792), __webpack_require__(2288)(module)))

/***/ }),
/* 2288 */
/***/ (function(module, exports) {

module.exports = function(originalModule) {
	if(!originalModule.webpackPolyfill) {
		var module = Object.create(originalModule);
		// module.parent = undefined by default
		if(!module.children) module.children = [];
		Object.defineProperty(module, "loaded", {
			enumerable: true,
			get: function() {
				return module.l;
			}
		});
		Object.defineProperty(module, "id", {
			enumerable: true,
			get: function() {
				return module.i;
			}
		});
		Object.defineProperty(module, "exports", {
			enumerable: true,
		});
		module.webpackPolyfill = 1;
	}
	return module;
};


/***/ }),
/* 2289 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony export (immutable) */ __webpack_exports__["a"] = symbolObservablePonyfill;
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;
};


/***/ }),
/* 2290 */,
/* 2291 */,
/* 2292 */,
/* 2293 */,
/* 2294 */,
/* 2295 */,
/* 2296 */,
/* 2297 */,
/* 2298 */,
/* 2299 */,
/* 2300 */,
/* 2301 */,
/* 2302 */,
/* 2303 */,
/* 2304 */,
/* 2305 */,
/* 2306 */,
/* 2307 */,
/* 2308 */,
/* 2309 */,
/* 2310 */,
/* 2311 */,
/* 2312 */,
/* 2313 */,
/* 2314 */,
/* 2315 */,
/* 2316 */,
/* 2317 */,
/* 2318 */,
/* 2319 */,
/* 2320 */,
/* 2321 */,
/* 2322 */,
/* 2323 */,
/* 2324 */,
/* 2325 */,
/* 2326 */,
/* 2327 */,
/* 2328 */,
/* 2329 */,
/* 2330 */,
/* 2331 */,
/* 2332 */,
/* 2333 */,
/* 2334 */,
/* 2335 */,
/* 2336 */,
/* 2337 */,
/* 2338 */,
/* 2339 */,
/* 2340 */,
/* 2341 */,
/* 2342 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

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; }; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                                                                                                                                                                                   * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                                                                                                                                                                                                                                   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

exports.initialState = initialState;
exports.getHistory = getHistory;
exports.getHistoryFrame = getHistoryFrame;
exports.getHistoryPosition = getHistoryPosition;

var _prefs = __webpack_require__(226);

/**
 * Breakpoints reducer
 * @module reducers/replay
 */
function initialState() {
  return {
    history: [],
    position: -1
  };
}

const defaultFrameScopes = {
  original: {},
  generated: {}
};

function update(state = initialState(), action) {
  if (!_prefs.features.replay) {
    return state;
  }

  switch (action.type) {
    case "TRAVEL_TO":
      {
        return _extends({}, state, { position: action.position });
      }

    case "ADD_SCOPES":
      {
        return addScopes(state, action);
      }

    case "MAP_SCOPES":
      {
        return mapScopes(state, action);
      }

    case "CLEAR_HISTORY":
      {
        return { history: [], position: -1 };
      }

    case "PAUSED":
      {
        return paused(state, action);
      }

    case "EVALUATE_EXPRESSION":
      {
        return evaluateExpression(state, action);
      }
  }

  return state;
}

function addScopes(state, action) {
  const { frame, status, value } = action;
  const selectedFrameId = frame.id;
  const instance = state.history[state.position];

  if (!instance) {
    return state;
  }

  const pausedInst = instance.paused;

  const generated = _extends({}, pausedInst.frameScopes.generated, {
    [selectedFrameId]: {
      pending: status !== "done",
      scope: value
    }
  });

  const newPaused = _extends({}, pausedInst, {
    frameScopes: _extends({}, pausedInst.frameScopes, {
      generated
    })
  });

  const history = [...state.history];
  history[state.position] = _extends({}, instance, { paused: newPaused });
  return _extends({}, state, { history });
}

function mapScopes(state, action) {
  const { frame, status, value } = action;
  const selectedFrameId = frame.id;
  const instance = state.history[state.position];

  if (!instance) {
    return state;
  }

  const pausedInst = instance.paused;

  const original = _extends({}, pausedInst.frameScopes.original, {
    [selectedFrameId]: {
      pending: status !== "done",
      scope: value
    }
  });

  const newPaused = _extends({}, pausedInst, {
    frameScopes: _extends({}, pausedInst.frameScopes, {
      original
    })
  });

  const history = [...state.history];
  history[state.position] = _extends({}, instance, { paused: newPaused });
  return _extends({}, state, { history });
}

function evaluateExpression(state, action) {
  const { input, value } = action;
  const instance = state.history[state.position];
  if (!instance) {
    return state;
  }

  const prevExpressions = instance.expressions || [];
  const expression = { input, value };
  const expressions = [...prevExpressions, expression];

  const history = [...state.history];
  history[state.position] = _extends({}, instance, { expressions });
  return _extends({}, state, { history });
}

function paused(state, action) {
  const { selectedFrameId, frames, loadedObjects, why } = action;

  // turn this into an object keyed by object id
  const objectMap = {};
  loadedObjects.forEach(obj => {
    objectMap[obj.value.objectId] = obj;
  });

  const pausedInfo = {
    isWaitingOnBreak: false,
    selectedFrameId,
    frames,
    frameScopes: defaultFrameScopes,
    loadedObjects: objectMap,
    why
  };

  const history = [...state.history, { paused: pausedInfo }];
  const position = state.position + 1;
  return _extends({}, state, { history, position });
}

function getHistory(state) {
  return state.replay.history;
}

function getHistoryFrame(state, position) {
  return state.replay.history[position];
}

function getHistoryPosition(state) {
  return state.replay.position;
}

exports.default = update;

/***/ }),
/* 2343 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.timeTravelTo = timeTravelTo;
exports.clearHistory = clearHistory;

var _selectors = __webpack_require__(1352);

var _sources = __webpack_require__(1797);

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/**
 * Redux actions for replay
 * @module actions/replay
 */

function timeTravelTo(position) {
  return ({ dispatch, getState }) => {
    const data = (0, _selectors.getHistoryFrame)(getState(), position);
    dispatch({
      type: "TRAVEL_TO",
      data,
      position
    });
    dispatch((0, _sources.selectLocation)(data.paused.frames[0].location));
  };
}

function clearHistory() {
  return ({ dispatch, getState }) => {
    dispatch({
      type: "CLEAR_HISTORY"
    });
  };
}

/***/ }),
/* 2344 */,
/* 2345 */,
/* 2346 */,
/* 2347 */,
/* 2348 */,
/* 2349 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.locColumn = locColumn;
function locColumn(loc) {
  if (typeof loc.column !== "number") {
    // This shouldn't really happen with locations from the AST, but
    // the datatype we are using allows null/undefined column.
    return 0;
  }

  return loc.column;
}

/***/ }),
/* 2350 */,
/* 2351 */,
/* 2352 */,
/* 2353 */,
/* 2354 */,
/* 2355 */,
/* 2356 */,
/* 2357 */,
/* 2358 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.findGeneratedBindingFromPosition = findGeneratedBindingFromPosition;

__webpack_require__(1365);

var _locColumn = __webpack_require__(2349);

var _firefox = __webpack_require__(1500);

async function findGeneratedBindingFromPosition(sourceMaps, client, source, pos, name, type, generatedAstBindings) {
  const gen = await sourceMaps.getGeneratedLocation(pos.start, source);
  const genEnd = await sourceMaps.getGeneratedLocation(pos.end, source);

  // Since the map takes the closest location, sometimes mapping a
  // binding's location can point at the start of a binding listed after
  // it, so we need to make sure it maps to a location that actually has
  // a size in order to avoid picking up the wrong descriptor.
  if (gen.line === genEnd.line && gen.column === genEnd.column) {
    return null;
  }

  return generatedAstBindings.reduce(async (acc, val) => {
    const accVal = await acc;
    if (accVal) {
      return accVal;
    }

    if (type === "import") {
      const desc = await mapImportBindingToDescriptor(val, {
        start: gen,
        end: genEnd
      });

      if (desc) {
        return {
          name: val.name,
          desc
        };
      }
      return null;
    }

    // Allow the mapping to point anywhere within the generated binding
    // location to allow for less than perfect sourcemaps. Since you also
    // need at least one character between identifiers, we also give one
    // characters of space at the front the generated binding in order
    // to increase the probability of finding the right mapping.
    if (gen.line === val.loc.start.line && (0, _locColumn.locColumn)(gen) >= (0, _locColumn.locColumn)(val.loc.start) - 1 && (0, _locColumn.locColumn)(gen) <= (0, _locColumn.locColumn)(val.loc.end)) {
      return {
        name: val.name,
        desc: val.desc
      };
    }

    return null;
  }, null);
}

/**
 * Given an generated binding, and a range over the generated code, statically
 * evaluate accessed properties within the mapped range to resolve the actual
 * imported value.
 */

// eslint-disable-next-line max-len


async function mapImportBindingToDescriptor(binding, mapped) {
  // Expression matches require broader searching because sourcemaps usage
  // varies in how they map certain things. For instance given
  //
  //   import { bar } from "mod";
  //   bar();
  //
  // The "bar()" expression is generally expanded into one of two possibly
  // forms, both of which map the "bar" identifier in different ways. See
  // the "^^" markers below for the ranges.
  //
  //   (0, foo.bar)()    // Babel
  //       ^^^^^^^       // mapping
  //       ^^^           // binding
  // vs
  //
  //   Object(foo.bar)() // Webpack
  //   ^^^^^^^^^^^^^^^   // mapping
  //          ^^^        // binding
  //
  // Unfortunately, Webpack also has a tendancy to over-map past the call
  // expression to the start of the next line, at least when there isn't
  // anything else on that line that is mapped, e.g.
  //
  //   Object(foo.bar)()
  //   ^^^^^^^^^^^^^^^^^
  //   ^                 // wrapped to column 0 of next line

  if (!mappingContains(mapped, binding.loc)) {
    return null;
  }

  const { meta } = binding.loc;

  let desc = binding.desc;

  // Limit to 2 simple property or inherits operartions, since it would
  // just be more work to search more and it is very unlikely that
  // bindings would be mapped to more than a single member + inherits
  // wrapper.
  for (let op = meta, index = 0; op && mappingContains(mapped, op) && desc && index < 2; index++, op = op && op.parent) {
    // Calling could potentially trigger side-effects, which would not
    // be ideal for this case.
    if (op.type === "call") {
      return null;
    }

    if (op.type === "inherit") {
      continue;
    }

    const objectClient = (0, _firefox.createObjectClient)(desc.value);
    desc = (await objectClient.getProperty(op.property)).descriptor;
  }

  return desc;
}

function mappingContains(mapped, item) {
  return (item.start.line > mapped.start.line || item.start.line === mapped.start.line && (0, _locColumn.locColumn)(item.start) >= (0, _locColumn.locColumn)(mapped.start)) && (item.end.line < mapped.end.line || item.end.line === mapped.end.line && (0, _locColumn.locColumn)(item.end) <= (0, _locColumn.locColumn)(mapped.end));
}

/***/ }),
/* 2359 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.log = log;

var _devtoolsConfig = __webpack_require__(1355);

/**
 * Produces a formatted console log line by imploding args, prefixed by [log]
 *
 * function input: log(["hello", "world"])
 * console output: [log] hello world
 *
 * @memberof utils/log
 * @static
 */
function log(...args) {
  if (!(0, _devtoolsConfig.isDevelopment)()) {
    return;
  }

  console.log.apply(console, ["[log]", ...args]);
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/**
 *
 * Utils for logging to the console
 * Suppresses logging in non-development environment
 *
 * @module utils/log
 */

/***/ }),
/* 2360 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.tokenAtTextPosition = tokenAtTextPosition;
exports.getExpressionFromCoords = getExpressionFromCoords;
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 tokenAtTextPosition(cm, { line, column }) {
  if (line < 0 || line >= cm.lineCount()) {
    return null;
  }

  const token = cm.getTokenAt({ line: line - 1, ch: column });
  if (!token) {
    return null;
  }

  return { startColumn: token.start, endColumn: token.end, type: token.type };
}

// The strategy of querying codeMirror tokens was borrowed
// from Chrome's inital implementation in JavaScriptSourceFrame.js#L414
function getExpressionFromCoords(cm, coord) {
  const token = tokenAtTextPosition(cm, coord);
  if (!token) {
    return null;
  }

  let startHighlight = token.startColumn;
  const endHighlight = token.endColumn;
  const lineNumber = coord.line;
  const line = cm.doc.getLine(coord.line - 1);
  while (startHighlight > 1 && line.charAt(startHighlight - 1) === ".") {
    const tokenBefore = tokenAtTextPosition(cm, {
      line: coord.line,
      column: startHighlight - 2
    });

    if (!tokenBefore || !tokenBefore.type) {
      return null;
    }

    startHighlight = tokenBefore.startColumn;
  }
  const expression = line.substring(startHighlight, endHighlight);

  if (!expression) {
    return null;
  }

  const location = {
    start: { line: lineNumber, column: startHighlight },
    end: { line: lineNumber, column: endHighlight }
  };
  return { expression, location };
}

/***/ }),
/* 2361 */,
/* 2362 */,
/* 2363 */,
/* 2364 */,
/* 2365 */,
/* 2366 */,
/* 2367 */,
/* 2368 */,
/* 2369 */,
/* 2370 */,
/* 2371 */,
/* 2372 */,
/* 2373 */,
/* 2374 */,
/* 2375 */,
/* 2376 */,
/* 2377 */,
/* 2378 */,
/* 2379 */,
/* 2380 */,
/* 2381 */,
/* 2382 */,
/* 2383 */,
/* 2384 */,
/* 2385 */,
/* 2386 */,
/* 2387 */,
/* 2388 */,
/* 2389 */,
/* 2390 */,
/* 2391 */,
/* 2392 */,
/* 2393 */,
/* 2394 */,
/* 2395 */,
/* 2396 */,
/* 2397 */,
/* 2398 */,
/* 2399 */,
/* 2400 */,
/* 2401 */,
/* 2402 */,
/* 2403 */,
/* 2404 */,
/* 2405 */,
/* 2406 */,
/* 2407 */,
/* 2408 */,
/* 2409 */,
/* 2410 */,
/* 2411 */,
/* 2412 */,
/* 2413 */,
/* 2414 */,
/* 2415 */,
/* 2416 */,
/* 2417 */,
/* 2418 */,
/* 2419 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _why = __webpack_require__(2420);

Object.keys(_why).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  Object.defineProperty(exports, key, {
    enumerable: true,
    get: function () {
      return _why[key];
    }
  });
});

var _stepping = __webpack_require__(2421);

Object.keys(_stepping).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  Object.defineProperty(exports, key, {
    enumerable: true,
    get: function () {
      return _stepping[key];
    }
  });
});

/***/ }),
/* 2420 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getPauseReason = getPauseReason;
exports.isException = isException;
exports.isInterrupted = isInterrupted;
exports.inDebuggerEval = inDebuggerEval;


// Map protocol pause "why" reason to a valid L10N key
// These are the known unhandled reasons:
// "breakpointConditionThrown", "clientEvaluated"
// "interrupted", "attached"
const reasons = {
  debuggerStatement: "whyPaused.debuggerStatement",
  breakpoint: "whyPaused.breakpoint",
  exception: "whyPaused.exception",
  resumeLimit: "whyPaused.resumeLimit",
  pauseOnDOMEvents: "whyPaused.pauseOnDOMEvents",
  breakpointConditionThrown: "whyPaused.breakpointConditionThrown",

  // V8
  DOM: "whyPaused.breakpoint",
  EventListener: "whyPaused.pauseOnDOMEvents",
  XHR: "whyPaused.xhr",
  promiseRejection: "whyPaused.promiseRejection",
  assert: "whyPaused.assert",
  debugCommand: "whyPaused.debugCommand",
  other: "whyPaused.other"
}; /* This Source Code Form is subject to the terms of the Mozilla Public
    * License, v. 2.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 getPauseReason(why) {
  if (!why) {
    return null;
  }

  const reasonType = why.type;
  if (!reasons[reasonType]) {
    console.log("Please file an issue: reasonType=", reasonType);
  }
  return reasons[reasonType];
}

function isException(why) {
  return why && why.type && why.type === "exception";
}

function isInterrupted(why) {
  return why && why.type && why.type === "interrupted";
}

function inDebuggerEval(why) {
  if (why && why.type === "exception" && why.exception && why.exception.preview && why.exception.preview.fileName) {
    return why.exception.preview.fileName === "debugger eval code";
  }

  return false;
}

/***/ }),
/* 2421 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.shouldStep = shouldStep;

var _lodash = __webpack_require__(2);

var _devtoolsSourceMap = __webpack_require__(1360);

var _selectors = __webpack_require__(1352);

var _parser = __webpack_require__(1365);

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

async function shouldStep(rootFrame, state, sourceMaps) {
  if (!rootFrame) {
    return false;
  }

  const selectedSource = (0, _selectors.getSelectedSource)(state);
  const previousFrameInfo = (0, _selectors.getPreviousPauseFrameLocation)(state);

  let previousFrameLoc;
  let currentFrameLoc;

  if (selectedSource && (0, _devtoolsSourceMap.isOriginalId)(selectedSource.get("id"))) {
    currentFrameLoc = rootFrame.location;
    previousFrameLoc = previousFrameInfo && previousFrameInfo.location;
  } else {
    currentFrameLoc = rootFrame.generatedLocation;
    previousFrameLoc = previousFrameInfo && previousFrameInfo.generatedLocation;
  }

  return (0, _devtoolsSourceMap.isOriginalId)(currentFrameLoc.sourceId) && (previousFrameLoc && (0, _lodash.isEqual)(previousFrameLoc, currentFrameLoc) || (await (0, _parser.isInvalidPauseLocation)(currentFrameLoc)));
}

/***/ })
/******/ ]);
});PK
!<���
�
=chrome/devtools/modules/devtools/client/debugger/new/panel.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Task } = require("devtools/shared/task");
const { LocalizationHelper } = require("devtools/shared/l10n");
const { gDevTools } = require("devtools/client/framework/devtools");
const { TargetFactory } = require("devtools/client/framework/target");
const { Toolbox } = require("devtools/client/framework/toolbox");

const DBG_STRINGS_URI = "devtools/client/locales/debugger.properties";
const L10N = new LocalizationHelper(DBG_STRINGS_URI);

function DebuggerPanel(iframeWindow, toolbox) {
  this.panelWin = iframeWindow;
  this.panelWin.L10N = L10N;
  this.toolbox = toolbox;
}

DebuggerPanel.prototype = {
  open: async function() {
    if (!this.toolbox.target.isRemote) {
      await this.toolbox.target.makeRemote();
    }

    const {
      actions,
      store,
      selectors,
      client
    } = await this.panelWin.Debugger.bootstrap({
      threadClient: this.toolbox.threadClient,
      tabTarget: this.toolbox.target,
      debuggerClient: this.toolbox.target.client,
      sourceMaps: this.toolbox.sourceMapService,
      toolboxActions: {
        // Open a link in a new browser tab.
        openLink: this.openLink.bind(this),
        openWorkerToolbox: this.openWorkerToolbox.bind(this)
      }
    });

    this._actions = actions;
    this._store = store;
    this._selectors = selectors;
    this._client = client;
    this.isReady = true;
    return this;
  },

  getVarsForTests() {
    return {
      store: this._store,
      selectors: this._selectors,
      actions: this._actions,
      client: this._client
    };
  },

  _getState: function() {
    return this._store.getState();
  },

  openLink: function(url) {
    const parentDoc = this.toolbox.doc;
    if (!parentDoc) {
      return;
    }

    const win = parentDoc.querySelector("window");
    if (!win) {
      return;
    }

    const top = win.ownerDocument.defaultView.top;
    if (!top || typeof top.openUILinkIn !== "function") {
      return;
    }

    top.openUILinkIn(url, "tab");
  },

  openWorkerToolbox: function(worker) {
    this.toolbox.target.client.attachWorker(
      worker.actor,
      (response, workerClient) => {
        const workerTarget = TargetFactory.forWorker(workerClient);
        gDevTools
          .showToolbox(workerTarget, "jsdebugger", Toolbox.HostType.WINDOW)
          .then(toolbox => {
            toolbox.once("destroy", () => workerClient.detach());
          });
      }
    );
  },

  getFrames: function() {
    let frames = this._selectors.getFrames(this._getState());

    // Frames is null when the debugger is not paused.
    if (!frames) {
      return {
        frames: [],
        selected: -1
      };
    }

    const selectedFrame = this._selectors.getSelectedFrame(this._getState());
    const selected = frames.findIndex(frame => frame.id == selectedFrame.id);

    frames.forEach(frame => {
      frame.actor = frame.id;
    });

    return { frames, selected };
  },

  isPaused() {
    return this._selectors.isPaused(this._getState());
  },

  selectSource(url, line) {
    this._actions.selectSourceURL(url, { location: { line } });
  },

  getSource(sourceURL) {
    return this._selectors.getSourceByURL(this._getState(), sourceURL);
  },

  destroy: function() {
    this.panelWin.Debugger.destroy();
    this.emit("destroyed");
  }
};

exports.DebuggerPanel = DebuggerPanel;
PK
!<B��绝��Echrome/devtools/modules/devtools/client/debugger/new/parser-worker.js(function webpackUniversalModuleDefinition(root, factory) {
	if(typeof exports === 'object' && typeof module === 'object')
		module.exports = factory();
	else if(typeof define === 'function' && define.amd)
		define([], factory);
	else {
		var a = factory();
		for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
	}
})(typeof self !== 'undefined' ? self : this, function() {
return /******/ (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, {
/******/ 				configurable: false,
/******/ 				enumerable: true,
/******/ 				get: getter
/******/ 			});
/******/ 		}
/******/ 	};
/******/
/******/ 	// 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 = "/assets/build";
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(__webpack_require__.s = 1272);
/******/ })
/************************************************************************/
/******/ ({

/***/ 10:
/***/ (function(module, exports, __webpack_require__) {

var Symbol = __webpack_require__(7);

/** Used for built-in method references. */
var objectProto = Object.prototype;

/** 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 nativeObjectToString = objectProto.toString;

/** Built-in value references. */
var symToStringTag = Symbol ? Symbol.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.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;
}

module.exports = getRawTag;


/***/ }),

/***/ 100:
/***/ (function(module, exports, __webpack_require__) {

var assocIndexOf = __webpack_require__(96);

/**
 * 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) {
    ++this.size;
    data.push([key, value]);
  } else {
    data[index][1] = value;
  }
  return this;
}

module.exports = listCacheSet;


/***/ }),

/***/ 101:
/***/ (function(module, exports, __webpack_require__) {

var getNative = __webpack_require__(81),
    root = __webpack_require__(8);

/* Built-in method references that are verified to be native. */
var Map = getNative(root, 'Map');

module.exports = Map;


/***/ }),

/***/ 102:
/***/ (function(module, exports, __webpack_require__) {

var getMapData = __webpack_require__(103);

/**
 * 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) {
  var result = getMapData(this, key)['delete'](key);
  this.size -= result ? 1 : 0;
  return result;
}

module.exports = mapCacheDelete;


/***/ }),

/***/ 1023:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.parseScriptTags = exports.parseScripts = exports.parseScript = exports.getCandidateScriptLocations = exports.generateWhitespace = exports.extractScriptTags = undefined;

var _types = __webpack_require__(2268);

var types = _interopRequireWildcard(_types);

var _babylon = __webpack_require__(435);

var babylon = _interopRequireWildcard(_babylon);

var _customParse = __webpack_require__(2337);

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

function parseScript(_ref) {
  var source = _ref.source,
      line = _ref.line;

  // remove empty or only whitespace scripts
  if (source.length === 0 || /^\s+$/.test(source)) {
    return null;
  }

  try {
    return babylon.parse(source, {
      sourceType: "script",
      startLine: line
    });
  } catch (e) {
    return null;
  }
}

function parseScripts(locations) {
  var parser = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : parseScript;

  return (0, _customParse.parseScripts)(locations, parser);
}

function extractScriptTags(source) {
  return parseScripts((0, _customParse.getCandidateScriptLocations)(source), function (loc) {
    var ast = parseScript(loc);

    if (ast) {
      return loc;
    }

    return null;
  }).filter(types.isFile);
}

function parseScriptTags(source) {
  var parser = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : parseScript;

  return (0, _customParse.parseScriptTags)(source, parser);
}

exports.default = parseScriptTags;
exports.extractScriptTags = extractScriptTags;
exports.generateWhitespace = _customParse.generateWhitespace;
exports.getCandidateScriptLocations = _customParse.getCandidateScriptLocations;
exports.parseScript = parseScript;
exports.parseScripts = parseScripts;
exports.parseScriptTags = parseScriptTags;

/***/ }),

/***/ 103:
/***/ (function(module, exports, __webpack_require__) {

var isKeyable = __webpack_require__(104);

/**
 * 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;
}

module.exports = getMapData;


/***/ }),

/***/ 104:
/***/ (function(module, exports) {

/**
 * 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);
}

module.exports = isKeyable;


/***/ }),

/***/ 105:
/***/ (function(module, exports, __webpack_require__) {

var getMapData = __webpack_require__(103);

/**
 * 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);
}

module.exports = mapCacheGet;


/***/ }),

/***/ 106:
/***/ (function(module, exports, __webpack_require__) {

var getMapData = __webpack_require__(103);

/**
 * 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);
}

module.exports = mapCacheHas;


/***/ }),

/***/ 107:
/***/ (function(module, exports, __webpack_require__) {

var getMapData = __webpack_require__(103);

/**
 * 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) {
  var data = getMapData(this, key),
      size = data.size;

  data.set(key, value);
  this.size += data.size == size ? 0 : 1;
  return this;
}

module.exports = mapCacheSet;


/***/ }),

/***/ 108:
/***/ (function(module, exports, __webpack_require__) {

var baseToString = __webpack_require__(109);

/**
 * 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 convert.
 * @returns {string} Returns the converted string.
 * @example
 *
 * _.toString(null);
 * // => ''
 *
 * _.toString(-0);
 * // => '-0'
 *
 * _.toString([1, 2, 3]);
 * // => '1,2,3'
 */
function toString(value) {
  return value == null ? '' : baseToString(value);
}

module.exports = toString;


/***/ }),

/***/ 109:
/***/ (function(module, exports, __webpack_require__) {

var Symbol = __webpack_require__(7),
    arrayMap = __webpack_require__(110),
    isArray = __webpack_require__(70),
    isSymbol = __webpack_require__(72);

/** Used as references for various `Number` constants. */
var INFINITY = 1 / 0;

/** Used to convert symbols to primitives and strings. */
var symbolProto = Symbol ? Symbol.prototype : undefined,
    symbolToString = symbolProto ? symbolProto.toString : undefined;

/**
 * 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 (isArray(value)) {
    // Recursively convert values (susceptible to call stack limits).
    return arrayMap(value, baseToString) + '';
  }
  if (isSymbol(value)) {
    return symbolToString ? symbolToString.call(value) : '';
  }
  var result = (value + '');
  return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
}

module.exports = baseToString;


/***/ }),

/***/ 11:
/***/ (function(module, exports) {

/** Used for built-in method references. */
var objectProto = Object.prototype;

/**
 * Used to resolve the
 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
 * of values.
 */
var nativeObjectToString = objectProto.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.call(value);
}

module.exports = objectToString;


/***/ }),

/***/ 110:
/***/ (function(module, exports) {

/**
 * 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 == null ? 0 : array.length,
      result = Array(length);

  while (++index < length) {
    result[index] = iteratee(array[index], index, array);
  }
  return result;
}

module.exports = arrayMap;


/***/ }),

/***/ 111:
/***/ (function(module, exports, __webpack_require__) {

var isSymbol = __webpack_require__(72);

/** Used as references for various `Number` constants. */
var INFINITY = 1 / 0;

/**
 * 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;
}

module.exports = toKey;


/***/ }),

/***/ 1129:
/***/ (function(module, exports, __webpack_require__) {

var baseDifference = __webpack_require__(1131),
    baseFlatten = __webpack_require__(707),
    baseRest = __webpack_require__(411),
    isArrayLikeObject = __webpack_require__(1155);

/**
 * Creates an array of `array` values not included in the other given arrays
 * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
 * for equality comparisons. The order and references of result values are
 * determined by the first array.
 *
 * **Note:** Unlike `_.pullAll`, this method returns a new array.
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Array
 * @param {Array} array The array to inspect.
 * @param {...Array} [values] The values to exclude.
 * @returns {Array} Returns the new array of filtered values.
 * @see _.without, _.xor
 * @example
 *
 * _.difference([2, 1], [2, 3]);
 * // => [1]
 */
var difference = baseRest(function(array, values) {
  return isArrayLikeObject(array)
    ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true))
    : [];
});

module.exports = difference;


/***/ }),

/***/ 1131:
/***/ (function(module, exports, __webpack_require__) {

var SetCache = __webpack_require__(276),
    arrayIncludes = __webpack_require__(563),
    arrayIncludesWith = __webpack_require__(567),
    arrayMap = __webpack_require__(110),
    baseUnary = __webpack_require__(215),
    cacheHas = __webpack_require__(280);

/** Used as the size to enable large array optimizations. */
var LARGE_ARRAY_SIZE = 200;

/**
 * The base implementation of methods like `_.difference` without support
 * for excluding multiple arrays or iteratee shorthands.
 *
 * @private
 * @param {Array} array The array to inspect.
 * @param {Array} values The values to exclude.
 * @param {Function} [iteratee] The iteratee invoked per element.
 * @param {Function} [comparator] The comparator invoked per element.
 * @returns {Array} Returns the new array of filtered values.
 */
function baseDifference(array, values, iteratee, comparator) {
  var index = -1,
      includes = arrayIncludes,
      isCommon = true,
      length = array.length,
      result = [],
      valuesLength = values.length;

  if (!length) {
    return result;
  }
  if (iteratee) {
    values = arrayMap(values, baseUnary(iteratee));
  }
  if (comparator) {
    includes = arrayIncludesWith;
    isCommon = false;
  }
  else if (values.length >= LARGE_ARRAY_SIZE) {
    includes = cacheHas;
    isCommon = false;
    values = new SetCache(values);
  }
  outer:
  while (++index < length) {
    var value = array[index],
        computed = iteratee == null ? value : iteratee(value);

    value = (comparator || value !== 0) ? value : 0;
    if (isCommon && computed === computed) {
      var valuesIndex = valuesLength;
      while (valuesIndex--) {
        if (values[valuesIndex] === computed) {
          continue outer;
        }
      }
      result.push(value);
    }
    else if (!includes(values, computed, comparator)) {
      result.push(value);
    }
  }
  return result;
}

module.exports = baseDifference;


/***/ }),

/***/ 114:
/***/ (function(module, exports, __webpack_require__) {

var baseAssignValue = __webpack_require__(115),
    eq = __webpack_require__(97);

/** Used for built-in method references. */
var objectProto = Object.prototype;

/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;

/**
 * Assigns `value` to `key` of `object` if the existing value is not equivalent
 * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
 * for equality comparisons.
 *
 * @private
 * @param {Object} object The object to modify.
 * @param {string} key The key of the property to assign.
 * @param {*} value The value to assign.
 */
function assignValue(object, key, value) {
  var objValue = object[key];
  if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) ||
      (value === undefined && !(key in object))) {
    baseAssignValue(object, key, value);
  }
}

module.exports = assignValue;


/***/ }),

/***/ 115:
/***/ (function(module, exports, __webpack_require__) {

var defineProperty = __webpack_require__(116);

/**
 * The base implementation of `assignValue` and `assignMergeValue` without
 * value checks.
 *
 * @private
 * @param {Object} object The object to modify.
 * @param {string} key The key of the property to assign.
 * @param {*} value The value to assign.
 */
function baseAssignValue(object, key, value) {
  if (key == '__proto__' && defineProperty) {
    defineProperty(object, key, {
      'configurable': true,
      'enumerable': true,
      'value': value,
      'writable': true
    });
  } else {
    object[key] = value;
  }
}

module.exports = baseAssignValue;


/***/ }),

/***/ 1155:
/***/ (function(module, exports, __webpack_require__) {

var isArrayLike = __webpack_require__(220),
    isObjectLike = __webpack_require__(14);

/**
 * 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);
}

module.exports = isArrayLikeObject;


/***/ }),

/***/ 116:
/***/ (function(module, exports, __webpack_require__) {

var getNative = __webpack_require__(81);

var defineProperty = (function() {
  try {
    var func = getNative(Object, 'defineProperty');
    func({}, '', {});
    return func;
  } catch (e) {}
}());

module.exports = defineProperty;


/***/ }),

/***/ 117:
/***/ (function(module, exports) {

/** Used as references for various `Number` constants. */
var MAX_SAFE_INTEGER = 9007199254740991;

/** Used to detect unsigned integer values. */
var reIsUint = /^(?:0|[1-9]\d*)$/;

/**
 * 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) {
  var type = typeof value;
  length = length == null ? MAX_SAFE_INTEGER : length;

  return !!length &&
    (type == 'number' ||
      (type != 'symbol' && reIsUint.test(value))) &&
        (value > -1 && value % 1 == 0 && value < length);
}

module.exports = isIndex;


/***/ }),

/***/ 12:
/***/ (function(module, exports, __webpack_require__) {

var overArg = __webpack_require__(13);

/** Built-in value references. */
var getPrototype = overArg(Object.getPrototypeOf, Object);

module.exports = getPrototype;


/***/ }),

/***/ 1272:
/***/ (function(module, exports, __webpack_require__) {

module.exports = __webpack_require__(1618);


/***/ }),

/***/ 13:
/***/ (function(module, exports) {

/**
 * 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));
  };
}

module.exports = overArg;


/***/ }),

/***/ 1360:
/***/ (function(module, exports, __webpack_require__) {

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 {
  originalToGeneratedId,
  generatedToOriginalId,
  isGeneratedId,
  isOriginalId
} = __webpack_require__(1389);

const { workerUtils: { WorkerDispatcher } } = __webpack_require__(1363);

const dispatcher = new WorkerDispatcher();

const getOriginalURLs = dispatcher.task("getOriginalURLs");
const getGeneratedLocation = dispatcher.task("getGeneratedLocation");
const getOriginalLocation = dispatcher.task("getOriginalLocation");
const getLocationScopes = dispatcher.task("getLocationScopes");
const getOriginalSourceText = dispatcher.task("getOriginalSourceText");
const applySourceMap = dispatcher.task("applySourceMap");
const clearSourceMaps = dispatcher.task("clearSourceMaps");
const hasMappedSource = dispatcher.task("hasMappedSource");

module.exports = {
  originalToGeneratedId,
  generatedToOriginalId,
  isGeneratedId,
  isOriginalId,
  hasMappedSource,
  getOriginalURLs,
  getGeneratedLocation,
  getOriginalLocation,
  getLocationScopes,
  getOriginalSourceText,
  applySourceMap,
  clearSourceMaps,
  startSourceMapWorker: dispatcher.start.bind(dispatcher),
  stopSourceMapWorker: dispatcher.stop.bind(dispatcher)
};

/***/ }),

/***/ 1363:
/***/ (function(module, exports, __webpack_require__) {

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 networkRequest = __webpack_require__(1367);
const workerUtils = __webpack_require__(1368);

module.exports = {
  networkRequest,
  workerUtils
};

/***/ }),

/***/ 1367:
/***/ (function(module, exports) {

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 networkRequest(url, opts) {
  return fetch(url, {
    cache: opts.loadFromCache ? "default" : "no-cache"
  }).then(res => {
    if (res.status >= 200 && res.status < 300) {
      return res.text().then(text => ({ content: text }));
    }
    return Promise.reject(`request failed with status ${res.status}`);
  });
}

module.exports = networkRequest;

/***/ }),

/***/ 1368:
/***/ (function(module, exports) {

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"); }); }; }

function WorkerDispatcher() {
  this.msgId = 1;
  this.worker = null;
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

WorkerDispatcher.prototype = {
  start(url) {
    this.worker = new Worker(url);
    this.worker.onerror = () => {
      console.error(`Error in worker ${url}`);
    };
  },

  stop() {
    if (!this.worker) {
      return;
    }

    this.worker.terminate();
    this.worker = null;
  },

  task(method) {
    return (...args) => {
      return new Promise((resolve, reject) => {
        const id = this.msgId++;
        this.worker.postMessage({ id, method, args });

        const listener = ({ data: result }) => {
          if (result.id !== id) {
            return;
          }

          if (!this.worker) {
            return;
          }

          this.worker.removeEventListener("message", listener);
          if (result.error) {
            reject(result.error);
          } else {
            resolve(result.response);
          }
        };

        this.worker.addEventListener("message", listener);
      });
    };
  }
};

function workerHandler(publicInterface) {
  return function (msg) {
    const { id, method, args } = msg.data;
    try {
      const response = publicInterface[method].apply(undefined, args);
      if (response instanceof Promise) {
        response.then(val => self.postMessage({ id, response: val }),
        // Error can't be sent via postMessage, so be sure to
        // convert to string.
        err => self.postMessage({ id, error: err.toString() }));
      } else {
        self.postMessage({ id, response });
      }
    } catch (error) {
      // Error can't be sent via postMessage, so be sure to convert to
      // string.
      self.postMessage({ id, error: error.toString() });
    }
  };
}

function streamingWorkerHandler(publicInterface, { timeout = 100 } = {}, worker = self) {
  let streamingWorker = (() => {
    var _ref = _asyncToGenerator(function* (id, tasks) {
      let isWorking = true;

      const intervalId = setTimeout(function () {
        isWorking = false;
      }, timeout);

      const results = [];
      while (tasks.length !== 0 && isWorking) {
        const { callback, context, args } = tasks.shift();
        const result = yield callback.call(context, args);
        results.push(result);
      }
      worker.postMessage({ id, status: "pending", data: results });
      clearInterval(intervalId);

      if (tasks.length !== 0) {
        yield streamingWorker(id, tasks);
      }
    });

    return function streamingWorker(_x, _x2) {
      return _ref.apply(this, arguments);
    };
  })();

  return (() => {
    var _ref2 = _asyncToGenerator(function* (msg) {
      const { id, method, args } = msg.data;
      const workerMethod = publicInterface[method];
      if (!workerMethod) {
        console.error(`Could not find ${method} defined in worker.`);
      }
      worker.postMessage({ id, status: "start" });

      try {
        const tasks = workerMethod(args);
        yield streamingWorker(id, tasks);
        worker.postMessage({ id, status: "done" });
      } catch (error) {
        worker.postMessage({ id, status: "error", error });
      }
    });

    return function (_x3) {
      return _ref2.apply(this, arguments);
    };
  })();
}

module.exports = {
  WorkerDispatcher,
  workerHandler,
  streamingWorkerHandler
};

/***/ }),

/***/ 1375:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

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; }; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                                                                                                                                                                                   * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                                                                                                                                                                                                                                   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

exports.parseScript = parseScript;
exports.getAst = getAst;
exports.clearASTs = clearASTs;
exports.traverseAst = traverseAst;

var _parseScriptTags = __webpack_require__(1023);

var _parseScriptTags2 = _interopRequireDefault(_parseScriptTags);

var _babylon = __webpack_require__(435);

var babylon = _interopRequireWildcard(_babylon);

var _types = __webpack_require__(2268);

var t = _interopRequireWildcard(_types);

var _isEmpty = __webpack_require__(963);

var _isEmpty2 = _interopRequireDefault(_isEmpty);

var _sources = __webpack_require__(1458);

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

let ASTs = new Map();

function _parse(code, opts) {
  return babylon.parse(code, _extends({}, opts, {
    tokens: true
  }));
}

const sourceOptions = {
  generated: {
    tokens: true
  },
  original: {
    sourceType: "unambiguous",
    tokens: true,
    plugins: ["jsx", "flow", "doExpressions", "objectRestSpread", "classProperties", "exportDefaultFrom", "exportNamespaceFrom", "asyncGenerators", "functionBind", "functionSent", "dynamicImport"]
  }
};

function parse(text, opts) {
  let ast;
  if (!text) {
    return;
  }

  try {
    ast = _parse(text, opts);
  } catch (error) {
    ast = {};
  }

  return ast;
}

// Custom parser for parse-script-tags that adapts its input structure to
// our parser's signature
function htmlParser({ source, line }) {
  return parse(source, { startLine: line });
}

function parseScript(text, opts) {
  return _parse(text, opts);
}

function getAst(sourceId) {
  if (ASTs.has(sourceId)) {
    return ASTs.get(sourceId);
  }

  const source = (0, _sources.getSource)(sourceId);

  let ast = {};
  const { contentType } = source;
  if (contentType == "text/html") {
    ast = (0, _parseScriptTags2.default)(source.text, htmlParser) || {};
  } else if (contentType && contentType.match(/(javascript|jsx)/)) {
    const type = source.id.includes("original") ? "original" : "generated";
    const options = sourceOptions[type];
    ast = parse(source.text, options);
  }

  ASTs.set(source.id, ast);
  return ast;
}

function clearASTs() {
  ASTs = new Map();
}

function traverseAst(sourceId, visitor, state) {
  const ast = getAst(sourceId);
  if ((0, _isEmpty2.default)(ast)) {
    return null;
  }

  t.traverse(ast, visitor, state);
  // t.fastTraverse(ast, visitor);
  return ast;
}

/***/ }),

/***/ 1389:
/***/ (function(module, exports, __webpack_require__) {

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 md5 = __webpack_require__(248);

function originalToGeneratedId(originalId) {
  const match = originalId.match(/(.*)\/originalSource/);
  return match ? match[1] : "";
}

function generatedToOriginalId(generatedId, url) {
  return `${generatedId}/originalSource-${md5(url)}`;
}

function isOriginalId(id) {
  return !!id.match(/\/originalSource/);
}

function isGeneratedId(id) {
  return !isOriginalId(id);
}

/**
 * Trims the query part or reference identifier of a URL string, if necessary.
 */
function trimUrlQuery(url) {
  let length = url.length;
  let q1 = url.indexOf("?");
  let q2 = url.indexOf("&");
  let q3 = url.indexOf("#");
  let q = Math.min(q1 != -1 ? q1 : length, q2 != -1 ? q2 : length, q3 != -1 ? q3 : length);

  return url.slice(0, q);
}

// Map suffix to content type.
const contentMap = {
  "js": "text/javascript",
  "jsm": "text/javascript",
  "ts": "text/typescript",
  "tsx": "text/typescript-jsx",
  "jsx": "text/jsx",
  "coffee": "text/coffeescript",
  "elm": "text/elm",
  "cljs": "text/x-clojure"
};

/**
 * 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);
  let dot = url.lastIndexOf(".");
  if (dot >= 0) {
    let name = url.substring(dot + 1);
    if (name in contentMap) {
      return contentMap[name];
    }
  }
  return "text/plain";
}

module.exports = {
  originalToGeneratedId,
  generatedToOriginalId,
  isOriginalId,
  isGeneratedId,
  getContentType,
  contentMapForTesting: contentMap
};

/***/ }),

/***/ 14:
/***/ (function(module, exports) {

/**
 * 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';
}

module.exports = isObjectLike;


/***/ }),

/***/ 1411:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.isFunction = isFunction;
exports.isAwaitExpression = isAwaitExpression;
exports.isYieldExpression = isYieldExpression;
exports.isVariable = isVariable;
exports.isComputedExpression = isComputedExpression;
exports.getMemberExpression = getMemberExpression;
exports.getVariables = getVariables;

var _types = __webpack_require__(2268);

var t = _interopRequireWildcard(_types);

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

function isFunction(node) {
  return t.isFunction(node) || t.isArrowFunctionExpression(node) || t.isObjectMethod(node) || t.isClassMethod(node);
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.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 isAwaitExpression(path) {
  const { node, parent } = path;
  return t.isAwaitExpression(node) || t.isAwaitExpression(parent.init) || t.isAwaitExpression(parent);
}

function isYieldExpression(path) {
  const { node, parent } = path;
  return t.isYieldExpression(node) || t.isYieldExpression(parent.init) || t.isYieldExpression(parent);
}

function isVariable(path) {
  const node = path.node;
  return t.isVariableDeclaration(node) || isFunction(path) && path.node.params != null && path.node.params.length || t.isObjectProperty(node) && !isFunction(path.node.value);
}

function isComputedExpression(expression) {
  return (/^\[/m.test(expression)
  );
}

function getMemberExpression(root) {
  function _getMemberExpression(node, expr) {
    if (t.isMemberExpression(node)) {
      expr = [node.property.name].concat(expr);
      return _getMemberExpression(node.object, expr);
    }

    if (t.isCallExpression(node)) {
      return [];
    }

    if (t.isThisExpression(node)) {
      return ["this"].concat(expr);
    }

    return [node.name].concat(expr);
  }

  const expr = _getMemberExpression(root, []);
  return expr.join(".");
}

function getVariables(dec) {
  if (!dec.id) {
    return [];
  }

  if (t.isArrayPattern(dec.id)) {
    if (!dec.id.elements) {
      return [];
    }

    // NOTE: it's possible that an element is empty
    // e.g. const [, a] = arr
    return dec.id.elements.filter(element => element).map(element => {
      return {
        name: t.isAssignmentPattern(element) ? element.left.name : element.name || element.argument.name,
        location: element.loc
      };
    });
  }

  return [{
    name: dec.id.name,
    location: dec.loc
  }];
}

/***/ }),

/***/ 1455:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getClosestExpression = getClosestExpression;
exports.getClosestPath = getClosestPath;

var _types = __webpack_require__(2268);

var t = _interopRequireWildcard(_types);

var _simplePath = __webpack_require__(3590);

var _simplePath2 = _interopRequireDefault(_simplePath);

var _ast = __webpack_require__(1375);

var _helpers = __webpack_require__(1411);

var _contains = __webpack_require__(1456);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

function getNodeValue(node) {
  if (t.isThisExpression(node)) {
    return "this";
  }

  return node.name;
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.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 getClosestMemberExpression(sourceId, token, location) {
  const closest = getClosestPath(sourceId, location).find(path => t.isMemberExpression(path.node) && path.node.property.name === token);

  if (closest) {
    const memberExpression = (0, _helpers.getMemberExpression)(closest.node);
    return {
      expression: memberExpression,
      location: closest.node.loc
    };
  }
  return null;
}

function getClosestExpression(sourceId, token, location) {
  const memberExpression = getClosestMemberExpression(sourceId, token, location);
  if (memberExpression) {
    return memberExpression;
  }

  const path = getClosestPath(sourceId, location);
  if (!path || !path.node) {
    return;
  }

  const { node } = path;
  return { expression: getNodeValue(node), location: node.loc };
}

function getClosestPath(sourceId, location) {
  let closestPath = null;

  (0, _ast.traverseAst)(sourceId, {
    enter(node, ancestors) {
      if ((0, _contains.nodeContainsPosition)(node, location)) {
        const path = (0, _simplePath2.default)(ancestors);

        if (path && (!closestPath || path.depth > closestPath.depth)) {
          closestPath = path;
        }
      }
    }
  });

  if (!closestPath) {
    throw new Error("Assertion failure - This should always fine a path");
  }

  return closestPath;
}

/***/ }),

/***/ 1456:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.containsPosition = containsPosition;
exports.containsLocation = containsLocation;
exports.nodeContainsPosition = nodeContainsPosition;
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 startsBefore(a, b) {
  let before = a.start.line < b.line;
  if (a.start.line === b.line) {
    before = a.start.column >= 0 && b.column >= 0 ? a.start.column <= b.column : true;
  }
  return before;
}

function endsAfter(a, b) {
  let after = a.end.line > b.line;
  if (a.end.line === b.line) {
    after = a.end.column >= 0 && b.column >= 0 ? a.end.column >= b.column : true;
  }
  return after;
}

function containsPosition(a, b) {
  return startsBefore(a, b) && endsAfter(a, b);
}

function containsLocation(a, b) {
  return containsPosition(a, b.start) && containsPosition(a, b.end);
}

function nodeContainsPosition(node, position) {
  return containsPosition(node.loc, position);
}

/***/ }),

/***/ 1457:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

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; }; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                                                                                                                                                                                   * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                                                                                                                                                                                                                                   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

exports.clearSymbols = clearSymbols;
exports.default = getSymbols;

var _flatten = __webpack_require__(706);

var _flatten2 = _interopRequireDefault(_flatten);

var _types = __webpack_require__(2268);

var t = _interopRequireWildcard(_types);

var _simplePath = __webpack_require__(3590);

var _simplePath2 = _interopRequireDefault(_simplePath);

var _ast = __webpack_require__(1375);

var _helpers = __webpack_require__(1411);

var _inferClassName = __webpack_require__(1620);

var _getFunctionName = __webpack_require__(1621);

var _getFunctionName2 = _interopRequireDefault(_getFunctionName);

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

let symbolDeclarations = new Map();

function getFunctionParameterNames(path) {
  if (path.node.params != null) {
    return path.node.params.map(param => {
      if (param.type !== "AssignmentPattern") {
        return param.name;
      }

      // Parameter with default value
      if (param.left.type === "Identifier" && param.right.type === "Identifier") {
        return `${param.left.name} = ${param.right.name}`;
      } else if (param.left.type === "Identifier" && param.right.type === "StringLiteral") {
        return `${param.left.name} = ${param.right.value}`;
      } else if (param.left.type === "Identifier" && param.right.type === "ObjectExpression") {
        return `${param.left.name} = {}`;
      } else if (param.left.type === "Identifier" && param.right.type === "ArrayExpression") {
        return `${param.left.name} = []`;
      } else if (param.left.type === "Identifier" && param.right.type === "NullLiteral") {
        return `${param.left.name} = null`;
      }
    });
  }
  return [];
}

function getVariableNames(path) {
  if (t.isObjectProperty(path.node) && !(0, _helpers.isFunction)(path.node.value)) {
    if (path.node.key.type === "StringLiteral") {
      return [{
        name: path.node.key.value,
        location: path.node.loc
      }];
    } else if (path.node.value.type === "Identifier") {
      return [{ name: path.node.value.name, location: path.node.loc }];
    } else if (path.node.value.type === "AssignmentPattern") {
      return [{ name: path.node.value.left.name, location: path.node.loc }];
    }

    return [{
      name: path.node.key.name,
      location: path.node.loc
    }];
  }

  if (!path.node.declarations) {
    return path.node.params.map(dec => ({
      name: dec.name,
      location: dec.loc
    }));
  }

  const declarations = path.node.declarations.filter(dec => dec.id.type !== "ObjectPattern").map(_helpers.getVariables);

  return (0, _flatten2.default)(declarations);
}

function getComments(ast) {
  if (!ast || !ast.comments) {
    return [];
  }
  return ast.comments.map(comment => ({
    name: comment.location,
    location: comment.loc
  }));
}

function getSpecifiers(specifiers) {
  if (!specifiers) {
    return null;
  }

  return specifiers.map(specifier => specifier.local && specifier.local.name);
}

function extractSymbol(path, symbols) {
  if ((0, _helpers.isVariable)(path)) {
    symbols.variables.push(...getVariableNames(path));
  }

  if ((0, _helpers.isFunction)(path)) {
    symbols.functions.push({
      name: (0, _getFunctionName2.default)(path.node, path.parent),
      klass: (0, _inferClassName.inferClassName)(path),
      location: path.node.loc,
      parameterNames: getFunctionParameterNames(path),
      identifier: path.node.id
    });
  }

  if (t.isJSXElement(path)) {
    symbols.hasJsx = true;
  }

  if (t.isGenericTypeAnnotation(path)) {
    symbols.hasTypes = true;
  }

  if (t.isClassDeclaration(path)) {
    symbols.classes.push({
      name: path.node.id.name,
      parent: path.node.superClass,
      location: path.node.loc
    });
  }

  if (t.isImportDeclaration(path)) {
    symbols.imports.push({
      source: path.node.source.value,
      location: path.node.loc,
      specifiers: getSpecifiers(path.node.specifiers)
    });
  }

  if (t.isObjectProperty(path)) {
    const { start, end, identifierName } = path.node.key.loc;
    symbols.objectProperties.push({
      name: identifierName,
      location: { start, end },
      expression: getSnippet(path)
    });
  }

  if (t.isMemberExpression(path)) {
    const { start, end } = path.node.property.loc;
    symbols.memberExpressions.push({
      name: path.node.property.name,
      location: { start, end },
      expressionLocation: path.node.loc,
      expression: getSnippet(path),
      computed: path.node.computed
    });
  }

  if (t.isCallExpression(path)) {
    const callee = path.node.callee;
    const args = path.node.arguments;
    if (!t.isMemberExpression(callee)) {
      const { start, end, identifierName } = callee.loc;
      symbols.callExpressions.push({
        name: identifierName,
        values: args.filter(arg => arg.value).map(arg => arg.value),
        location: { start, end }
      });
    }
  }

  if (t.isIdentifier(path)) {
    let { start, end } = path.node.loc;

    if (path.node.typeAnnotation) {
      const column = path.node.typeAnnotation.loc.start.column;
      end = _extends({}, end, { column });
    }

    symbols.identifiers.push({
      name: path.node.name,
      expression: path.node.name,
      location: { start, end }
    });
  }

  if (t.isThisExpression(path.node)) {
    const { start, end } = path.node.loc;
    symbols.identifiers.push({
      name: "this",
      location: { start, end },
      expressionLocation: path.node.loc,
      expression: "this"
    });
  }

  if (t.isVariableDeclarator(path)) {
    const node = path.node.id;
    const { start, end } = path.node.loc;
    if (t.isArrayPattern(node)) {
      return;
    }

    symbols.identifiers.push({
      name: node.name,
      expression: node.name,
      location: { start, end }
    });
  }
}

function extractSymbols(sourceId) {
  const symbols = {
    functions: [],
    variables: [],
    callExpressions: [],
    memberExpressions: [],
    objectProperties: [],
    comments: [],
    identifiers: [],
    classes: [],
    imports: [],
    hasJsx: false,
    hasTypes: false
  };

  const ast = (0, _ast.traverseAst)(sourceId, {
    enter(node, ancestors) {
      try {
        const path = (0, _simplePath2.default)(ancestors);
        if (path) {
          extractSymbol(path, symbols);
        }
      } catch (e) {
        console.error(e);
      }
    }
  });

  // comments are extracted separately from the AST
  symbols.comments = getComments(ast);

  return symbols;
}

function extendSnippet(name, expression, path = null, prevPath = null) {
  const computed = path && path.node.computed;
  const prevComputed = prevPath && prevPath.node.computed;
  const prevArray = t.isArrayExpression(prevPath);
  const array = t.isArrayExpression(path);
  const value = path && path.node.property && path.node.property.extra && path.node.property.extra.raw || "";

  if (expression === "") {
    if (computed) {
      return name === undefined ? `[${value}]` : `[${name}]`;
    }
    return name;
  }

  if (computed || array) {
    if (prevComputed || prevArray) {
      return `[${name}]${expression}`;
    }
    return `[${name === undefined ? value : name}].${expression}`;
  }

  if (prevComputed || prevArray) {
    return `${name}${expression}`;
  }

  if ((0, _helpers.isComputedExpression)(expression) && name !== undefined) {
    return `${name}${expression}`;
  }

  return `${name}.${expression}`;
}

function getMemberSnippet(node, expression = "") {
  if (t.isMemberExpression(node)) {
    const name = node.property.name;
    const snippet = getMemberSnippet(node.object, extendSnippet(name, expression, { node }));
    return snippet;
  }

  if (t.isCallExpression(node)) {
    return "";
  }

  if (t.isThisExpression(node)) {
    return `this.${expression}`;
  }

  if (t.isIdentifier(node)) {
    if ((0, _helpers.isComputedExpression)(expression)) {
      return `${node.name}${expression}`;
    }
    return `${node.name}.${expression}`;
  }

  return expression;
}

function getObjectSnippet(path, prevPath, expression = "") {
  if (!path) {
    return expression;
  }

  const name = path.node.key.name;

  const extendedExpression = extendSnippet(name, expression, path, prevPath);

  const nextPrevPath = path;
  const nextPath = path.parentPath && path.parentPath.parentPath;

  return getSnippet(nextPath, nextPrevPath, extendedExpression);
}

function getArraySnippet(path, prevPath, expression) {
  if (!prevPath.parentPath) {
    throw new Error("Assertion failure - path should exist");
  }

  const index = `${prevPath.parentPath.containerIndex}`;
  const extendedExpression = extendSnippet(index, expression, path, prevPath);

  const nextPrevPath = path;
  const nextPath = path.parentPath && path.parentPath.parentPath;

  return getSnippet(nextPath, nextPrevPath, extendedExpression);
}

function getSnippet(path, prevPath = null, expression = "") {
  if (!path) {
    return expression;
  }

  if (t.isVariableDeclaration(path)) {
    const node = path.node.declarations[0];
    const name = node.id.name;
    return extendSnippet(name, expression, path, prevPath);
  }

  if (t.isVariableDeclarator(path)) {
    const node = path.node.id;
    if (t.isObjectPattern(node)) {
      return expression;
    }

    const name = node.name;
    const prop = extendSnippet(name, expression, path, prevPath);
    return prop;
  }

  if (t.isAssignmentExpression(path)) {
    const node = path.node.left;
    const name = t.isMemberExpression(node) ? getMemberSnippet(node) : node.name;

    const prop = extendSnippet(name, expression, path, prevPath);
    return prop;
  }

  if ((0, _helpers.isFunction)(path)) {
    return expression;
  }

  if (t.isIdentifier(path)) {
    const node = path.node;
    return `${node.name}.${expression}`;
  }

  if (t.isObjectProperty(path)) {
    return getObjectSnippet(path, prevPath, expression);
  }

  if (t.isObjectExpression(path)) {
    const parentPath = prevPath && prevPath.parentPath;
    return getObjectSnippet(parentPath, prevPath, expression);
  }

  if (t.isMemberExpression(path)) {
    return getMemberSnippet(path.node, expression);
  }

  if (t.isArrayExpression(path)) {
    if (!prevPath) {
      throw new Error("Assertion failure - path should exist");
    }

    return getArraySnippet(path, prevPath, expression);
  }
}

function clearSymbols() {
  symbolDeclarations = new Map();
}

function getSymbols(sourceId) {
  if (symbolDeclarations.has(sourceId)) {
    const symbols = symbolDeclarations.get(sourceId);
    if (symbols) {
      return symbols;
    }
  }

  const symbols = extractSymbols(sourceId);

  symbolDeclarations.set(sourceId, symbols);
  return symbols;
}

/***/ }),

/***/ 1458:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.hasSource = hasSource;
exports.setSource = setSource;
exports.getSource = getSource;
exports.clearSources = clearSources;


let cachedSources = new Map(); /* This Source Code Form is subject to the terms of the Mozilla Public
                                * License, v. 2.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 hasSource(sourceId) {
  return cachedSources.has(sourceId);
}

function setSource(source) {
  cachedSources.set(source.id, source);
}

function getSource(sourceId) {
  if (!cachedSources.has(sourceId)) {
    throw new Error(`Parser: source ${sourceId} was not provided.`);
  }

  return cachedSources.get(sourceId);
}

function clearSources() {
  cachedSources = new Map();
}

/***/ }),

/***/ 1618:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var _closest = __webpack_require__(1455);

var _getSymbols = __webpack_require__(1457);

var _getSymbols2 = _interopRequireDefault(_getSymbols);

var _ast = __webpack_require__(1375);

var _getScopes = __webpack_require__(2413);

var _getScopes2 = _interopRequireDefault(_getScopes);

var _sources = __webpack_require__(1458);

var _findOutOfScopeLocations = __webpack_require__(1624);

var _findOutOfScopeLocations2 = _interopRequireDefault(_findOutOfScopeLocations);

var _steps = __webpack_require__(1625);

var _getEmptyLines = __webpack_require__(1628);

var _getEmptyLines2 = _interopRequireDefault(_getEmptyLines);

var _validate = __webpack_require__(1629);

var _frameworks = __webpack_require__(1703);

var _pauseLocation = __webpack_require__(2422);

var _devtoolsUtils = __webpack_require__(1363);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 { workerHandler } = _devtoolsUtils.workerUtils;

self.onmessage = workerHandler({
  getClosestExpression: _closest.getClosestExpression,
  findOutOfScopeLocations: _findOutOfScopeLocations2.default,
  getSymbols: _getSymbols2.default,
  getScopes: _getScopes2.default,
  clearSymbols: _getSymbols.clearSymbols,
  clearScopes: _getScopes.clearScopes,
  clearASTs: _ast.clearASTs,
  hasSource: _sources.hasSource,
  setSource: _sources.setSource,
  clearSources: _sources.clearSources,
  isInvalidPauseLocation: _pauseLocation.isInvalidPauseLocation,
  getNextStep: _steps.getNextStep,
  getEmptyLines: _getEmptyLines2.default,
  hasSyntaxError: _validate.hasSyntaxError,
  getFramework: _frameworks.getFramework
});

/***/ }),

/***/ 1620:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.inferClassName = inferClassName;

var _types = __webpack_require__(2268);

var t = _interopRequireWildcard(_types);

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

// the function class is inferred from a call like
// createClass or extend
function fromCallExpression(callExpression) {
  const whitelist = ["extend", "createClass"];
  const callee = callExpression.node.callee;
  if (!callee) {
    return null;
  }

  const name = t.isMemberExpression(callee) ? callee.property.name : callee.name;

  if (!whitelist.includes(name)) {
    return null;
  }

  const variable = callExpression.findParent(p => t.isVariableDeclarator(p.node));
  if (variable) {
    return variable.node.id.name;
  }

  const assignment = callExpression.findParent(p => t.isAssignmentExpression(p.node));

  if (!assignment) {
    return null;
  }

  const left = assignment.node.left;

  if (left.name) {
    return name;
  }

  if (t.isMemberExpression(left)) {
    return left.property.name;
  }

  return null;
}

// the function class is inferred from a prototype assignment
// e.g. TodoClass.prototype.render = function() {}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 fromPrototype(assignment) {
  const left = assignment.node.left;
  if (!left) {
    return null;
  }

  if (t.isMemberExpression(left) && left.object && t.isMemberExpression(left.object) && left.object.property.identifier === "prototype") {
    return left.object.object.name;
  }

  return null;
}

// infer class finds an appropriate class for functions
// that are defined inside of a class like thing.
// e.g. `class Foo`, `TodoClass.prototype.foo`,
//      `Todo = createClass({ foo: () => {}})`
function inferClassName(path) {
  const classDeclaration = path.findParent(p => t.isClassDeclaration(p.node));
  if (classDeclaration) {
    return classDeclaration.node.id.name;
  }

  const callExpression = path.findParent(p => t.isCallExpression(p.node));
  if (callExpression) {
    return fromCallExpression(callExpression);
  }

  const assignment = path.findParent(p => t.isAssignmentExpression(p.node));
  if (assignment) {
    return fromPrototype(assignment);
  }

  return null;
}

/***/ }),

/***/ 1621:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = getFunctionName;

var _types = __webpack_require__(2268);

var t = _interopRequireWildcard(_types);

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

// Perform ES6's anonymous function name inference for all
// locations where static analysis is possible.
// eslint-disable-next-line complexity
function getFunctionName(node, parent) {
  if (t.isIdentifier(node.id)) {
    return node.id.name;
  }

  if (t.isObjectMethod(node, { computed: false }) || t.isClassMethod(node, { computed: false })) {
    const key = node.key;

    if (t.isIdentifier(key)) {
      return key.name;
    }
    if (t.isStringLiteral(key)) {
      return key.value;
    }
    if (t.isNumericLiteral(key)) {
      return `${key.value}`;
    }
  }

  if (t.isObjectProperty(parent, { computed: false, value: node }) ||
  // TODO: Babylon 6 doesn't support computed class props. It is included
  // here so that it is most flexible. Once Babylon 7 is used, this
  // can change to use computed: false like ObjectProperty.
  t.isClassProperty(parent, { value: node }) && !parent.computed) {
    const key = parent.key;

    if (t.isIdentifier(key)) {
      return key.name;
    }
    if (t.isStringLiteral(key)) {
      return key.value;
    }
    if (t.isNumericLiteral(key)) {
      return `${key.value}`;
    }
  }

  if (t.isAssignmentExpression(parent, { operator: "=", right: node })) {
    if (t.isIdentifier(parent.left)) {
      return parent.left.name;
    }

    // This case is not supported in standard ES6 name inference, but it
    // is included here since it is still a helpful case during debugging.
    if (t.isMemberExpression(parent.left, { computed: false })) {
      return parent.left.property.name;
    }
  }

  if (t.isAssignmentPattern(parent, { right: node }) && t.isIdentifier(parent.left)) {
    return parent.left.name;
  }

  if (t.isVariableDeclarator(parent, { init: node }) && t.isIdentifier(parent.id)) {
    return parent.id.name;
  }

  if (t.isExportDefaultDeclaration(parent, { declaration: node }) && t.isFunctionDeclaration(node)) {
    return "default";
  }

  return "anonymous";
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/***/ }),

/***/ 1624:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

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; }; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                                                                                                                                                                                   * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                                                                                                                                                                                                                                   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

var _get = __webpack_require__(67);

var _get2 = _interopRequireDefault(_get);

var _findIndex = __webpack_require__(262);

var _findIndex2 = _interopRequireDefault(_findIndex);

var _findLastIndex = __webpack_require__(1686);

var _findLastIndex2 = _interopRequireDefault(_findLastIndex);

var _contains = __webpack_require__(1456);

var _getSymbols = __webpack_require__(1457);

var _getSymbols2 = _interopRequireDefault(_getSymbols);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function findSymbols(source) {
  const { functions, comments } = (0, _getSymbols2.default)(source);
  return { functions, comments };
}

/**
 * Returns the location for a given function path. If the path represents a
 * function declaration, the location will begin after the function identifier
 * but before the function parameters.
 */

function getLocation(func) {
  const location = _extends({}, func.location);

  // if the function has an identifier, start the block after it so the
  // identifier is included in the "scope" of its parent
  const identifierEnd = (0, _get2.default)(func, "identifier.loc.end");
  if (identifierEnd) {
    location.start = identifierEnd;
  }

  return location;
}

/**
 * Find the nearest location containing the input position and
 * return new locations without inner locations under that nearest location
 *
 * @param locations Notice! The locations MUST be sorted by `sortByStart`
 *                  so that we can do linear time complexity operation.
 */
function removeInnerLocations(locations, position) {
  // First, let's find the nearest position-enclosing function location,
  // which is to find the last location enclosing the position.
  const newLocs = locations.slice();
  const parentIndex = (0, _findLastIndex2.default)(newLocs, loc => (0, _contains.containsPosition)(loc, position));
  if (parentIndex < 0) {
    return newLocs;
  }

  // Second, from the nearest location, loop locations again, stop looping
  // once seeing the 1st location not enclosed by the nearest location
  // to find the last inner locations inside the nearest location.
  const innerStartIndex = parentIndex + 1;
  const parentLoc = newLocs[parentIndex];
  const outerBoundaryIndex = (0, _findIndex2.default)(newLocs, loc => !(0, _contains.containsLocation)(parentLoc, loc), innerStartIndex);
  const innerBoundaryIndex = outerBoundaryIndex < 0 ? newLocs.length - 1 : outerBoundaryIndex - 1;

  // Third, remove those inner functions
  newLocs.splice(innerStartIndex, innerBoundaryIndex - parentIndex);
  return newLocs;
}

/**
 * Return an new locations array which excludes
 * items that are completely enclosed by another location in the input locations
 *
 * @param locations Notice! The locations MUST be sorted by `sortByStart`
 *                  so that we can do linear time complexity operation.
 */
function removeOverlaps(locations) {
  if (locations.length == 0) {
    return [];
  }
  const firstParent = locations[0];
  return locations.reduce(deduplicateNode, [firstParent]);
}

function deduplicateNode(nodes, location) {
  const parent = nodes[nodes.length - 1];
  if (!(0, _contains.containsLocation)(parent, location)) {
    nodes.push(location);
  }
  return nodes;
}

/**
 * Sorts an array of locations by start position
 */
function sortByStart(a, b) {
  if (a.start.line < b.start.line) {
    return -1;
  } else if (a.start.line === b.start.line) {
    return a.start.column - b.start.column;
  }

  return 1;
}

/**
 * Returns an array of locations that are considered out of scope for the given
 * location.
 */
function findOutOfScopeLocations(sourceId, position) {
  const { functions, comments } = findSymbols(sourceId);
  const commentLocations = comments.map(c => c.location);
  let locations = functions.map(getLocation).concat(commentLocations).sort(sortByStart);
  // Must remove inner locations then filter, otherwise,
  // we will mis-judge in-scope inner locations as out of scope.
  locations = removeInnerLocations(locations, position).filter(loc => !(0, _contains.containsPosition)(loc, position));
  return removeOverlaps(locations);
}

exports.default = findOutOfScopeLocations;

/***/ }),

/***/ 1625:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

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; }; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                                                                                                                                                                                   * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                                                                                                                                                                                                                                   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

exports.getNextStep = getNextStep;

var _types = __webpack_require__(2268);

var t = _interopRequireWildcard(_types);

var _types2 = __webpack_require__(1627);

var _closest = __webpack_require__(1455);

var _helpers = __webpack_require__(1411);

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

function getNextStep(sourceId, pausedPosition) {
  const currentExpression = getSteppableExpression(sourceId, pausedPosition);
  if (!currentExpression) {
    return null;
  }

  const currentStatement = currentExpression.find(p => {
    return p.inList && t.isStatement(p.node);
  });

  if (!currentStatement) {
    throw new Error("Assertion failure - this should always find at least Program");
  }

  return _getNextStep(currentStatement, pausedPosition);
}

function getSteppableExpression(sourceId, pausedPosition) {
  const closestPath = (0, _closest.getClosestPath)(sourceId, pausedPosition);

  if (!closestPath) {
    return null;
  }

  if ((0, _helpers.isAwaitExpression)(closestPath) || (0, _helpers.isYieldExpression)(closestPath)) {
    return closestPath;
  }

  return closestPath.find(p => t.isAwaitExpression(p.node) || t.isYieldExpression(p.node));
}

function _getNextStep(statement, position) {
  const nextStatement = statement.getSibling(1);
  if (nextStatement) {
    return _extends({}, nextStatement.node.loc.start, {
      sourceId: position.sourceId
    });
  }

  return null;
}

/***/ }),

/***/ 1627:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/***/ }),

/***/ 1628:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = getEmptyLines;

var _uniq = __webpack_require__(561);

var _uniq2 = _interopRequireDefault(_uniq);

var _difference = __webpack_require__(1129);

var _difference2 = _interopRequireDefault(_difference);

var _ast = __webpack_require__(1375);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

const commentTokens = ["CommentBlock", "CommentLine"]; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                        * License, v. 2.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 fillRange(start, end) {
  return Array(end - start + 1).fill().map((item, index) => start + index);
}

// Populates a pre-filled array of every line number,
// then removes lines which were found to be executable
function getLines(ast) {
  return fillRange(1, ast.tokens[ast.tokens.length - 1].loc.end.line);
}

// The following sequence stores lines which have executable code
// (contents other than comments or EOF, regardless of line position)
function getExecutableLines(ast) {
  const lines = ast.tokens.filter(token => !commentTokens.includes(token.type) && (!token.type || token.type.label && token.type.label != "eof")).map(token => token.loc.start.line);

  return (0, _uniq2.default)(lines);
}

function getEmptyLines(sourceId) {
  if (!sourceId) {
    return null;
  }

  const ast = (0, _ast.getAst)(sourceId);
  if (!ast || !ast.comments) {
    return [];
  }

  const executableLines = getExecutableLines(ast);
  const lines = getLines(ast);
  return (0, _difference2.default)(lines, executableLines);
}

/***/ }),

/***/ 1629:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.hasSyntaxError = hasSyntaxError;

var _ast = __webpack_require__(1375);

function hasSyntaxError(input) {
  try {
    (0, _ast.parseScript)(input);
    return false;
  } catch (e) {
    return `${e.name} : ${e.message}`;
  }
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/***/ }),

/***/ 1686:
/***/ (function(module, exports, __webpack_require__) {

var baseFindIndex = __webpack_require__(263),
    baseIteratee = __webpack_require__(814),
    toInteger = __webpack_require__(302);

/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeMax = Math.max,
    nativeMin = Math.min;

/**
 * This method is like `_.findIndex` except that it iterates over elements
 * of `collection` from right to left.
 *
 * @static
 * @memberOf _
 * @since 2.0.0
 * @category Array
 * @param {Array} array The array to inspect.
 * @param {Function} [predicate=_.identity] The function invoked per iteration.
 * @param {number} [fromIndex=array.length-1] The index to search from.
 * @returns {number} Returns the index of the found element, else `-1`.
 * @example
 *
 * var users = [
 *   { 'user': 'barney',  'active': true },
 *   { 'user': 'fred',    'active': false },
 *   { 'user': 'pebbles', 'active': false }
 * ];
 *
 * _.findLastIndex(users, function(o) { return o.user == 'pebbles'; });
 * // => 2
 *
 * // The `_.matches` iteratee shorthand.
 * _.findLastIndex(users, { 'user': 'barney', 'active': true });
 * // => 0
 *
 * // The `_.matchesProperty` iteratee shorthand.
 * _.findLastIndex(users, ['active', false]);
 * // => 2
 *
 * // The `_.property` iteratee shorthand.
 * _.findLastIndex(users, 'active');
 * // => 0
 */
function findLastIndex(array, predicate, fromIndex) {
  var length = array == null ? 0 : array.length;
  if (!length) {
    return -1;
  }
  var index = length - 1;
  if (fromIndex !== undefined) {
    index = toInteger(fromIndex);
    index = fromIndex < 0
      ? nativeMax(length + index, 0)
      : nativeMin(index, length - 1);
  }
  return baseFindIndex(array, baseIteratee(predicate, 3), index, true);
}

module.exports = findLastIndex;


/***/ }),

/***/ 1703:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getFramework = getFramework;

var _getSymbols = __webpack_require__(1457);

var _getSymbols2 = _interopRequireDefault(_getSymbols);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function getFramework(sourceId) {
  if (isReactComponent(sourceId)) {
    return "React";
  }
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.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 isReactComponent(sourceId) {
  const { imports, classes, callExpressions } = (0, _getSymbols2.default)(sourceId);
  return (importsReact(imports) || requiresReact(callExpressions)) && extendsComponent(classes);
}

function importsReact(imports) {
  return imports.some(importObj => importObj.source === "react" && importObj.specifiers.some(specifier => specifier === "React"));
}

function requiresReact(callExpressions) {
  return callExpressions.some(callExpression => callExpression.name === "require" && callExpression.values.some(value => value === "react"));
}

function extendsComponent(classes) {
  let result = false;
  classes.some(classObj => {
    if (classObj.parent.name === "Component" || classObj.parent.name === "PureComponent" || classObj.parent.property.name === "Component") {
      result = true;
    }
  });

  return result;
}

/***/ }),

/***/ 198:
/***/ (function(module, exports, __webpack_require__) {

var DataView = __webpack_require__(199),
    Map = __webpack_require__(101),
    Promise = __webpack_require__(200),
    Set = __webpack_require__(201),
    WeakMap = __webpack_require__(202),
    baseGetTag = __webpack_require__(6),
    toSource = __webpack_require__(87);

/** `Object#toString` result references. */
var mapTag = '[object Map]',
    objectTag = '[object Object]',
    promiseTag = '[object Promise]',
    setTag = '[object Set]',
    weakMapTag = '[object WeakMap]';

var dataViewTag = '[object DataView]';

/** Used to detect maps, sets, and weakmaps. */
var dataViewCtorString = toSource(DataView),
    mapCtorString = toSource(Map),
    promiseCtorString = toSource(Promise),
    setCtorString = toSource(Set),
    weakMapCtorString = toSource(WeakMap);

/**
 * 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 and promises in Node.js < 6.
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 = baseGetTag(value),
        Ctor = result == objectTag ? value.constructor : undefined,
        ctorString = Ctor ? toSource(Ctor) : '';

    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;
  };
}

module.exports = getTag;


/***/ }),

/***/ 199:
/***/ (function(module, exports, __webpack_require__) {

var getNative = __webpack_require__(81),
    root = __webpack_require__(8);

/* Built-in method references that are verified to be native. */
var DataView = getNative(root, 'DataView');

module.exports = DataView;


/***/ }),

/***/ 200:
/***/ (function(module, exports, __webpack_require__) {

var getNative = __webpack_require__(81),
    root = __webpack_require__(8);

/* Built-in method references that are verified to be native. */
var Promise = getNative(root, 'Promise');

module.exports = Promise;


/***/ }),

/***/ 201:
/***/ (function(module, exports, __webpack_require__) {

var getNative = __webpack_require__(81),
    root = __webpack_require__(8);

/* Built-in method references that are verified to be native. */
var Set = getNative(root, 'Set');

module.exports = Set;


/***/ }),

/***/ 202:
/***/ (function(module, exports, __webpack_require__) {

var getNative = __webpack_require__(81),
    root = __webpack_require__(8);

/* Built-in method references that are verified to be native. */
var WeakMap = getNative(root, 'WeakMap');

module.exports = WeakMap;


/***/ }),

/***/ 203:
/***/ (function(module, exports) {

/**
 * 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;
}

module.exports = mapToArray;


/***/ }),

/***/ 205:
/***/ (function(module, exports, __webpack_require__) {

var arrayLikeKeys = __webpack_require__(206),
    baseKeys = __webpack_require__(217),
    isArrayLike = __webpack_require__(220);

/**
 * 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);
}

module.exports = keys;


/***/ }),

/***/ 206:
/***/ (function(module, exports, __webpack_require__) {

var baseTimes = __webpack_require__(207),
    isArguments = __webpack_require__(208),
    isArray = __webpack_require__(70),
    isBuffer = __webpack_require__(210),
    isIndex = __webpack_require__(117),
    isTypedArray = __webpack_require__(212);

/** Used for built-in method references. */
var objectProto = Object.prototype;

/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;

/**
 * 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) {
  var isArr = isArray(value),
      isArg = !isArr && isArguments(value),
      isBuff = !isArr && !isArg && isBuffer(value),
      isType = !isArr && !isArg && !isBuff && isTypedArray(value),
      skipIndexes = isArr || isArg || isBuff || isType,
      result = skipIndexes ? baseTimes(value.length, String) : [],
      length = result.length;

  for (var key in value) {
    if ((inherited || hasOwnProperty.call(value, key)) &&
        !(skipIndexes && (
           // Safari 9 has enumerable `arguments.length` in strict mode.
           key == 'length' ||
           // Node.js 0.10 has enumerable non-index properties on buffers.
           (isBuff && (key == 'offset' || key == 'parent')) ||
           // PhantomJS 2 has enumerable non-index properties on typed arrays.
           (isType && (key == 'buffer' || key == 'byteLength' || key == 'byteOffset')) ||
           // Skip index properties.
           isIndex(key, length)
        ))) {
      result.push(key);
    }
  }
  return result;
}

module.exports = arrayLikeKeys;


/***/ }),

/***/ 207:
/***/ (function(module, exports) {

/**
 * 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;
}

module.exports = baseTimes;


/***/ }),

/***/ 208:
/***/ (function(module, exports, __webpack_require__) {

var baseIsArguments = __webpack_require__(209),
    isObjectLike = __webpack_require__(14);

/** Used for built-in method references. */
var objectProto = Object.prototype;

/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;

/** Built-in value references. */
var propertyIsEnumerable = objectProto.propertyIsEnumerable;

/**
 * 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
 */
var isArguments = baseIsArguments(function() { return arguments; }()) ? baseIsArguments : function(value) {
  return isObjectLike(value) && hasOwnProperty.call(value, 'callee') &&
    !propertyIsEnumerable.call(value, 'callee');
};

module.exports = isArguments;


/***/ }),

/***/ 209:
/***/ (function(module, exports, __webpack_require__) {

var baseGetTag = __webpack_require__(6),
    isObjectLike = __webpack_require__(14);

/** `Object#toString` result references. */
var argsTag = '[object Arguments]';

/**
 * The base implementation of `_.isArguments`.
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is an `arguments` object,
 */
function baseIsArguments(value) {
  return isObjectLike(value) && baseGetTag(value) == argsTag;
}

module.exports = baseIsArguments;


/***/ }),

/***/ 210:
/***/ (function(module, exports, __webpack_require__) {

/* WEBPACK VAR INJECTION */(function(module) {var root = __webpack_require__(8),
    stubFalse = __webpack_require__(211);

/** 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;

/** Built-in value references. */
var Buffer = moduleExports ? root.Buffer : undefined;

/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined;

/**
 * Checks if `value` is a buffer.
 *
 * @static
 * @memberOf _
 * @since 4.3.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a buffer, else `false`.
 * @example
 *
 * _.isBuffer(new Buffer(2));
 * // => true
 *
 * _.isBuffer(new Uint8Array(2));
 * // => false
 */
var isBuffer = nativeIsBuffer || stubFalse;

module.exports = isBuffer;

/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(793)(module)))

/***/ }),

/***/ 211:
/***/ (function(module, exports) {

/**
 * This method returns `false`.
 *
 * @static
 * @memberOf _
 * @since 4.13.0
 * @category Util
 * @returns {boolean} Returns `false`.
 * @example
 *
 * _.times(2, _.stubFalse);
 * // => [false, false]
 */
function stubFalse() {
  return false;
}

module.exports = stubFalse;


/***/ }),

/***/ 212:
/***/ (function(module, exports, __webpack_require__) {

var baseIsTypedArray = __webpack_require__(213),
    baseUnary = __webpack_require__(215),
    nodeUtil = __webpack_require__(216);

/* Node.js helper references. */
var nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray;

/**
 * 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;

module.exports = isTypedArray;


/***/ }),

/***/ 213:
/***/ (function(module, exports, __webpack_require__) {

var baseGetTag = __webpack_require__(6),
    isLength = __webpack_require__(214),
    isObjectLike = __webpack_require__(14);

/** `Object#toString` result references. */
var argsTag = '[object Arguments]',
    arrayTag = '[object Array]',
    boolTag = '[object Boolean]',
    dateTag = '[object Date]',
    errorTag = '[object Error]',
    funcTag = '[object Function]',
    mapTag = '[object Map]',
    numberTag = '[object Number]',
    objectTag = '[object Object]',
    regexpTag = '[object RegExp]',
    setTag = '[object Set]',
    stringTag = '[object String]',
    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 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;

/**
 * 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[baseGetTag(value)];
}

module.exports = baseIsTypedArray;


/***/ }),

/***/ 214:
/***/ (function(module, exports) {

/** Used as references for various `Number` constants. */
var MAX_SAFE_INTEGER = 9007199254740991;

/**
 * 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;
}

module.exports = isLength;


/***/ }),

/***/ 215:
/***/ (function(module, exports) {

/**
 * 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);
  };
}

module.exports = baseUnary;


/***/ }),

/***/ 216:
/***/ (function(module, exports, __webpack_require__) {

/* WEBPACK VAR INJECTION */(function(module) {var freeGlobal = __webpack_require__(9);

/** 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 && freeProcess.binding('util');
  } catch (e) {}
}());

module.exports = nodeUtil;

/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(793)(module)))

/***/ }),

/***/ 217:
/***/ (function(module, exports, __webpack_require__) {

var isPrototype = __webpack_require__(218),
    nativeKeys = __webpack_require__(219);

/** Used for built-in method references. */
var objectProto = Object.prototype;

/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;

/**
 * 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;
}

module.exports = baseKeys;


/***/ }),

/***/ 218:
/***/ (function(module, exports) {

/** Used for built-in method references. */
var objectProto = Object.prototype;

/**
 * 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;
}

module.exports = isPrototype;


/***/ }),

/***/ 219:
/***/ (function(module, exports, __webpack_require__) {

var overArg = __webpack_require__(13);

/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeKeys = overArg(Object.keys, Object);

module.exports = nativeKeys;


/***/ }),

/***/ 220:
/***/ (function(module, exports, __webpack_require__) {

var isFunction = __webpack_require__(83),
    isLength = __webpack_require__(214);

/**
 * 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);
}

module.exports = isArrayLike;


/***/ }),

/***/ 2255:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.isArrayExpression = isArrayExpression;
exports.isAssignmentExpression = isAssignmentExpression;
exports.isBinaryExpression = isBinaryExpression;
exports.isDirective = isDirective;
exports.isDirectiveLiteral = isDirectiveLiteral;
exports.isBlockStatement = isBlockStatement;
exports.isBreakStatement = isBreakStatement;
exports.isCallExpression = isCallExpression;
exports.isCatchClause = isCatchClause;
exports.isConditionalExpression = isConditionalExpression;
exports.isContinueStatement = isContinueStatement;
exports.isDebuggerStatement = isDebuggerStatement;
exports.isDoWhileStatement = isDoWhileStatement;
exports.isEmptyStatement = isEmptyStatement;
exports.isExpressionStatement = isExpressionStatement;
exports.isFile = isFile;
exports.isForInStatement = isForInStatement;
exports.isForStatement = isForStatement;
exports.isFunctionDeclaration = isFunctionDeclaration;
exports.isFunctionExpression = isFunctionExpression;
exports.isIdentifier = isIdentifier;
exports.isIfStatement = isIfStatement;
exports.isLabeledStatement = isLabeledStatement;
exports.isStringLiteral = isStringLiteral;
exports.isNumericLiteral = isNumericLiteral;
exports.isNullLiteral = isNullLiteral;
exports.isBooleanLiteral = isBooleanLiteral;
exports.isRegExpLiteral = isRegExpLiteral;
exports.isLogicalExpression = isLogicalExpression;
exports.isMemberExpression = isMemberExpression;
exports.isNewExpression = isNewExpression;
exports.isProgram = isProgram;
exports.isObjectExpression = isObjectExpression;
exports.isObjectMethod = isObjectMethod;
exports.isObjectProperty = isObjectProperty;
exports.isRestElement = isRestElement;
exports.isReturnStatement = isReturnStatement;
exports.isSequenceExpression = isSequenceExpression;
exports.isSwitchCase = isSwitchCase;
exports.isSwitchStatement = isSwitchStatement;
exports.isThisExpression = isThisExpression;
exports.isThrowStatement = isThrowStatement;
exports.isTryStatement = isTryStatement;
exports.isUnaryExpression = isUnaryExpression;
exports.isUpdateExpression = isUpdateExpression;
exports.isVariableDeclaration = isVariableDeclaration;
exports.isVariableDeclarator = isVariableDeclarator;
exports.isWhileStatement = isWhileStatement;
exports.isWithStatement = isWithStatement;
exports.isAssignmentPattern = isAssignmentPattern;
exports.isArrayPattern = isArrayPattern;
exports.isArrowFunctionExpression = isArrowFunctionExpression;
exports.isClassBody = isClassBody;
exports.isClassDeclaration = isClassDeclaration;
exports.isClassExpression = isClassExpression;
exports.isExportAllDeclaration = isExportAllDeclaration;
exports.isExportDefaultDeclaration = isExportDefaultDeclaration;
exports.isExportNamedDeclaration = isExportNamedDeclaration;
exports.isExportSpecifier = isExportSpecifier;
exports.isForOfStatement = isForOfStatement;
exports.isImportDeclaration = isImportDeclaration;
exports.isImportDefaultSpecifier = isImportDefaultSpecifier;
exports.isImportNamespaceSpecifier = isImportNamespaceSpecifier;
exports.isImportSpecifier = isImportSpecifier;
exports.isMetaProperty = isMetaProperty;
exports.isClassMethod = isClassMethod;
exports.isObjectPattern = isObjectPattern;
exports.isSpreadElement = isSpreadElement;
exports.isSuper = isSuper;
exports.isTaggedTemplateExpression = isTaggedTemplateExpression;
exports.isTemplateElement = isTemplateElement;
exports.isTemplateLiteral = isTemplateLiteral;
exports.isYieldExpression = isYieldExpression;
exports.isAnyTypeAnnotation = isAnyTypeAnnotation;
exports.isArrayTypeAnnotation = isArrayTypeAnnotation;
exports.isBooleanTypeAnnotation = isBooleanTypeAnnotation;
exports.isBooleanLiteralTypeAnnotation = isBooleanLiteralTypeAnnotation;
exports.isNullLiteralTypeAnnotation = isNullLiteralTypeAnnotation;
exports.isClassImplements = isClassImplements;
exports.isDeclareClass = isDeclareClass;
exports.isDeclareFunction = isDeclareFunction;
exports.isDeclareInterface = isDeclareInterface;
exports.isDeclareModule = isDeclareModule;
exports.isDeclareModuleExports = isDeclareModuleExports;
exports.isDeclareTypeAlias = isDeclareTypeAlias;
exports.isDeclareOpaqueType = isDeclareOpaqueType;
exports.isDeclareVariable = isDeclareVariable;
exports.isDeclareExportDeclaration = isDeclareExportDeclaration;
exports.isDeclareExportAllDeclaration = isDeclareExportAllDeclaration;
exports.isDeclaredPredicate = isDeclaredPredicate;
exports.isExistsTypeAnnotation = isExistsTypeAnnotation;
exports.isFunctionTypeAnnotation = isFunctionTypeAnnotation;
exports.isFunctionTypeParam = isFunctionTypeParam;
exports.isGenericTypeAnnotation = isGenericTypeAnnotation;
exports.isInferredPredicate = isInferredPredicate;
exports.isInterfaceExtends = isInterfaceExtends;
exports.isInterfaceDeclaration = isInterfaceDeclaration;
exports.isIntersectionTypeAnnotation = isIntersectionTypeAnnotation;
exports.isMixedTypeAnnotation = isMixedTypeAnnotation;
exports.isEmptyTypeAnnotation = isEmptyTypeAnnotation;
exports.isNullableTypeAnnotation = isNullableTypeAnnotation;
exports.isNumberLiteralTypeAnnotation = isNumberLiteralTypeAnnotation;
exports.isNumberTypeAnnotation = isNumberTypeAnnotation;
exports.isObjectTypeAnnotation = isObjectTypeAnnotation;
exports.isObjectTypeCallProperty = isObjectTypeCallProperty;
exports.isObjectTypeIndexer = isObjectTypeIndexer;
exports.isObjectTypeProperty = isObjectTypeProperty;
exports.isObjectTypeSpreadProperty = isObjectTypeSpreadProperty;
exports.isOpaqueType = isOpaqueType;
exports.isQualifiedTypeIdentifier = isQualifiedTypeIdentifier;
exports.isStringLiteralTypeAnnotation = isStringLiteralTypeAnnotation;
exports.isStringTypeAnnotation = isStringTypeAnnotation;
exports.isThisTypeAnnotation = isThisTypeAnnotation;
exports.isTupleTypeAnnotation = isTupleTypeAnnotation;
exports.isTypeofTypeAnnotation = isTypeofTypeAnnotation;
exports.isTypeAlias = isTypeAlias;
exports.isTypeAnnotation = isTypeAnnotation;
exports.isTypeCastExpression = isTypeCastExpression;
exports.isTypeParameter = isTypeParameter;
exports.isTypeParameterDeclaration = isTypeParameterDeclaration;
exports.isTypeParameterInstantiation = isTypeParameterInstantiation;
exports.isUnionTypeAnnotation = isUnionTypeAnnotation;
exports.isVariance = isVariance;
exports.isVoidTypeAnnotation = isVoidTypeAnnotation;
exports.isJSXAttribute = isJSXAttribute;
exports.isJSXClosingElement = isJSXClosingElement;
exports.isJSXElement = isJSXElement;
exports.isJSXEmptyExpression = isJSXEmptyExpression;
exports.isJSXExpressionContainer = isJSXExpressionContainer;
exports.isJSXSpreadChild = isJSXSpreadChild;
exports.isJSXIdentifier = isJSXIdentifier;
exports.isJSXMemberExpression = isJSXMemberExpression;
exports.isJSXNamespacedName = isJSXNamespacedName;
exports.isJSXOpeningElement = isJSXOpeningElement;
exports.isJSXSpreadAttribute = isJSXSpreadAttribute;
exports.isJSXText = isJSXText;
exports.isJSXFragment = isJSXFragment;
exports.isJSXOpeningFragment = isJSXOpeningFragment;
exports.isJSXClosingFragment = isJSXClosingFragment;
exports.isNoop = isNoop;
exports.isParenthesizedExpression = isParenthesizedExpression;
exports.isAwaitExpression = isAwaitExpression;
exports.isBindExpression = isBindExpression;
exports.isClassProperty = isClassProperty;
exports.isImport = isImport;
exports.isDecorator = isDecorator;
exports.isDoExpression = isDoExpression;
exports.isExportDefaultSpecifier = isExportDefaultSpecifier;
exports.isExportNamespaceSpecifier = isExportNamespaceSpecifier;
exports.isTSParameterProperty = isTSParameterProperty;
exports.isTSDeclareFunction = isTSDeclareFunction;
exports.isTSDeclareMethod = isTSDeclareMethod;
exports.isTSQualifiedName = isTSQualifiedName;
exports.isTSCallSignatureDeclaration = isTSCallSignatureDeclaration;
exports.isTSConstructSignatureDeclaration = isTSConstructSignatureDeclaration;
exports.isTSPropertySignature = isTSPropertySignature;
exports.isTSMethodSignature = isTSMethodSignature;
exports.isTSIndexSignature = isTSIndexSignature;
exports.isTSAnyKeyword = isTSAnyKeyword;
exports.isTSNumberKeyword = isTSNumberKeyword;
exports.isTSObjectKeyword = isTSObjectKeyword;
exports.isTSBooleanKeyword = isTSBooleanKeyword;
exports.isTSStringKeyword = isTSStringKeyword;
exports.isTSSymbolKeyword = isTSSymbolKeyword;
exports.isTSVoidKeyword = isTSVoidKeyword;
exports.isTSUndefinedKeyword = isTSUndefinedKeyword;
exports.isTSNullKeyword = isTSNullKeyword;
exports.isTSNeverKeyword = isTSNeverKeyword;
exports.isTSThisType = isTSThisType;
exports.isTSFunctionType = isTSFunctionType;
exports.isTSConstructorType = isTSConstructorType;
exports.isTSTypeReference = isTSTypeReference;
exports.isTSTypePredicate = isTSTypePredicate;
exports.isTSTypeQuery = isTSTypeQuery;
exports.isTSTypeLiteral = isTSTypeLiteral;
exports.isTSArrayType = isTSArrayType;
exports.isTSTupleType = isTSTupleType;
exports.isTSUnionType = isTSUnionType;
exports.isTSIntersectionType = isTSIntersectionType;
exports.isTSParenthesizedType = isTSParenthesizedType;
exports.isTSTypeOperator = isTSTypeOperator;
exports.isTSIndexedAccessType = isTSIndexedAccessType;
exports.isTSMappedType = isTSMappedType;
exports.isTSLiteralType = isTSLiteralType;
exports.isTSExpressionWithTypeArguments = isTSExpressionWithTypeArguments;
exports.isTSInterfaceDeclaration = isTSInterfaceDeclaration;
exports.isTSInterfaceBody = isTSInterfaceBody;
exports.isTSTypeAliasDeclaration = isTSTypeAliasDeclaration;
exports.isTSAsExpression = isTSAsExpression;
exports.isTSTypeAssertion = isTSTypeAssertion;
exports.isTSEnumDeclaration = isTSEnumDeclaration;
exports.isTSEnumMember = isTSEnumMember;
exports.isTSModuleDeclaration = isTSModuleDeclaration;
exports.isTSModuleBlock = isTSModuleBlock;
exports.isTSImportEqualsDeclaration = isTSImportEqualsDeclaration;
exports.isTSExternalModuleReference = isTSExternalModuleReference;
exports.isTSNonNullExpression = isTSNonNullExpression;
exports.isTSExportAssignment = isTSExportAssignment;
exports.isTSNamespaceExportDeclaration = isTSNamespaceExportDeclaration;
exports.isTSTypeAnnotation = isTSTypeAnnotation;
exports.isTSTypeParameterInstantiation = isTSTypeParameterInstantiation;
exports.isTSTypeParameterDeclaration = isTSTypeParameterDeclaration;
exports.isTSTypeParameter = isTSTypeParameter;
exports.isExpression = isExpression;
exports.isBinary = isBinary;
exports.isScopable = isScopable;
exports.isBlockParent = isBlockParent;
exports.isBlock = isBlock;
exports.isStatement = isStatement;
exports.isTerminatorless = isTerminatorless;
exports.isCompletionStatement = isCompletionStatement;
exports.isConditional = isConditional;
exports.isLoop = isLoop;
exports.isWhile = isWhile;
exports.isExpressionWrapper = isExpressionWrapper;
exports.isFor = isFor;
exports.isForXStatement = isForXStatement;
exports.isFunction = isFunction;
exports.isFunctionParent = isFunctionParent;
exports.isPureish = isPureish;
exports.isDeclaration = isDeclaration;
exports.isPatternLike = isPatternLike;
exports.isLVal = isLVal;
exports.isTSEntityName = isTSEntityName;
exports.isLiteral = isLiteral;
exports.isImmutable = isImmutable;
exports.isUserWhitespacable = isUserWhitespacable;
exports.isMethod = isMethod;
exports.isObjectMember = isObjectMember;
exports.isProperty = isProperty;
exports.isUnaryLike = isUnaryLike;
exports.isPattern = isPattern;
exports.isClass = isClass;
exports.isModuleDeclaration = isModuleDeclaration;
exports.isExportDeclaration = isExportDeclaration;
exports.isModuleSpecifier = isModuleSpecifier;
exports.isFlow = isFlow;
exports.isFlowType = isFlowType;
exports.isFlowBaseAnnotation = isFlowBaseAnnotation;
exports.isFlowDeclaration = isFlowDeclaration;
exports.isFlowPredicate = isFlowPredicate;
exports.isJSX = isJSX;
exports.isTSTypeElement = isTSTypeElement;
exports.isTSType = isTSType;
exports.isNumberLiteral = isNumberLiteral;
exports.isRegexLiteral = isRegexLiteral;
exports.isRestProperty = isRestProperty;
exports.isSpreadProperty = isSpreadProperty;

var _is = _interopRequireDefault(__webpack_require__(2262));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function isArrayExpression(node, opts) {
  return (0, _is.default)("ArrayExpression", node, opts);
}

function isAssignmentExpression(node, opts) {
  return (0, _is.default)("AssignmentExpression", node, opts);
}

function isBinaryExpression(node, opts) {
  return (0, _is.default)("BinaryExpression", node, opts);
}

function isDirective(node, opts) {
  return (0, _is.default)("Directive", node, opts);
}

function isDirectiveLiteral(node, opts) {
  return (0, _is.default)("DirectiveLiteral", node, opts);
}

function isBlockStatement(node, opts) {
  return (0, _is.default)("BlockStatement", node, opts);
}

function isBreakStatement(node, opts) {
  return (0, _is.default)("BreakStatement", node, opts);
}

function isCallExpression(node, opts) {
  return (0, _is.default)("CallExpression", node, opts);
}

function isCatchClause(node, opts) {
  return (0, _is.default)("CatchClause", node, opts);
}

function isConditionalExpression(node, opts) {
  return (0, _is.default)("ConditionalExpression", node, opts);
}

function isContinueStatement(node, opts) {
  return (0, _is.default)("ContinueStatement", node, opts);
}

function isDebuggerStatement(node, opts) {
  return (0, _is.default)("DebuggerStatement", node, opts);
}

function isDoWhileStatement(node, opts) {
  return (0, _is.default)("DoWhileStatement", node, opts);
}

function isEmptyStatement(node, opts) {
  return (0, _is.default)("EmptyStatement", node, opts);
}

function isExpressionStatement(node, opts) {
  return (0, _is.default)("ExpressionStatement", node, opts);
}

function isFile(node, opts) {
  return (0, _is.default)("File", node, opts);
}

function isForInStatement(node, opts) {
  return (0, _is.default)("ForInStatement", node, opts);
}

function isForStatement(node, opts) {
  return (0, _is.default)("ForStatement", node, opts);
}

function isFunctionDeclaration(node, opts) {
  return (0, _is.default)("FunctionDeclaration", node, opts);
}

function isFunctionExpression(node, opts) {
  return (0, _is.default)("FunctionExpression", node, opts);
}

function isIdentifier(node, opts) {
  return (0, _is.default)("Identifier", node, opts);
}

function isIfStatement(node, opts) {
  return (0, _is.default)("IfStatement", node, opts);
}

function isLabeledStatement(node, opts) {
  return (0, _is.default)("LabeledStatement", node, opts);
}

function isStringLiteral(node, opts) {
  return (0, _is.default)("StringLiteral", node, opts);
}

function isNumericLiteral(node, opts) {
  return (0, _is.default)("NumericLiteral", node, opts);
}

function isNullLiteral(node, opts) {
  return (0, _is.default)("NullLiteral", node, opts);
}

function isBooleanLiteral(node, opts) {
  return (0, _is.default)("BooleanLiteral", node, opts);
}

function isRegExpLiteral(node, opts) {
  return (0, _is.default)("RegExpLiteral", node, opts);
}

function isLogicalExpression(node, opts) {
  return (0, _is.default)("LogicalExpression", node, opts);
}

function isMemberExpression(node, opts) {
  return (0, _is.default)("MemberExpression", node, opts);
}

function isNewExpression(node, opts) {
  return (0, _is.default)("NewExpression", node, opts);
}

function isProgram(node, opts) {
  return (0, _is.default)("Program", node, opts);
}

function isObjectExpression(node, opts) {
  return (0, _is.default)("ObjectExpression", node, opts);
}

function isObjectMethod(node, opts) {
  return (0, _is.default)("ObjectMethod", node, opts);
}

function isObjectProperty(node, opts) {
  return (0, _is.default)("ObjectProperty", node, opts);
}

function isRestElement(node, opts) {
  return (0, _is.default)("RestElement", node, opts);
}

function isReturnStatement(node, opts) {
  return (0, _is.default)("ReturnStatement", node, opts);
}

function isSequenceExpression(node, opts) {
  return (0, _is.default)("SequenceExpression", node, opts);
}

function isSwitchCase(node, opts) {
  return (0, _is.default)("SwitchCase", node, opts);
}

function isSwitchStatement(node, opts) {
  return (0, _is.default)("SwitchStatement", node, opts);
}

function isThisExpression(node, opts) {
  return (0, _is.default)("ThisExpression", node, opts);
}

function isThrowStatement(node, opts) {
  return (0, _is.default)("ThrowStatement", node, opts);
}

function isTryStatement(node, opts) {
  return (0, _is.default)("TryStatement", node, opts);
}

function isUnaryExpression(node, opts) {
  return (0, _is.default)("UnaryExpression", node, opts);
}

function isUpdateExpression(node, opts) {
  return (0, _is.default)("UpdateExpression", node, opts);
}

function isVariableDeclaration(node, opts) {
  return (0, _is.default)("VariableDeclaration", node, opts);
}

function isVariableDeclarator(node, opts) {
  return (0, _is.default)("VariableDeclarator", node, opts);
}

function isWhileStatement(node, opts) {
  return (0, _is.default)("WhileStatement", node, opts);
}

function isWithStatement(node, opts) {
  return (0, _is.default)("WithStatement", node, opts);
}

function isAssignmentPattern(node, opts) {
  return (0, _is.default)("AssignmentPattern", node, opts);
}

function isArrayPattern(node, opts) {
  return (0, _is.default)("ArrayPattern", node, opts);
}

function isArrowFunctionExpression(node, opts) {
  return (0, _is.default)("ArrowFunctionExpression", node, opts);
}

function isClassBody(node, opts) {
  return (0, _is.default)("ClassBody", node, opts);
}

function isClassDeclaration(node, opts) {
  return (0, _is.default)("ClassDeclaration", node, opts);
}

function isClassExpression(node, opts) {
  return (0, _is.default)("ClassExpression", node, opts);
}

function isExportAllDeclaration(node, opts) {
  return (0, _is.default)("ExportAllDeclaration", node, opts);
}

function isExportDefaultDeclaration(node, opts) {
  return (0, _is.default)("ExportDefaultDeclaration", node, opts);
}

function isExportNamedDeclaration(node, opts) {
  return (0, _is.default)("ExportNamedDeclaration", node, opts);
}

function isExportSpecifier(node, opts) {
  return (0, _is.default)("ExportSpecifier", node, opts);
}

function isForOfStatement(node, opts) {
  return (0, _is.default)("ForOfStatement", node, opts);
}

function isImportDeclaration(node, opts) {
  return (0, _is.default)("ImportDeclaration", node, opts);
}

function isImportDefaultSpecifier(node, opts) {
  return (0, _is.default)("ImportDefaultSpecifier", node, opts);
}

function isImportNamespaceSpecifier(node, opts) {
  return (0, _is.default)("ImportNamespaceSpecifier", node, opts);
}

function isImportSpecifier(node, opts) {
  return (0, _is.default)("ImportSpecifier", node, opts);
}

function isMetaProperty(node, opts) {
  return (0, _is.default)("MetaProperty", node, opts);
}

function isClassMethod(node, opts) {
  return (0, _is.default)("ClassMethod", node, opts);
}

function isObjectPattern(node, opts) {
  return (0, _is.default)("ObjectPattern", node, opts);
}

function isSpreadElement(node, opts) {
  return (0, _is.default)("SpreadElement", node, opts);
}

function isSuper(node, opts) {
  return (0, _is.default)("Super", node, opts);
}

function isTaggedTemplateExpression(node, opts) {
  return (0, _is.default)("TaggedTemplateExpression", node, opts);
}

function isTemplateElement(node, opts) {
  return (0, _is.default)("TemplateElement", node, opts);
}

function isTemplateLiteral(node, opts) {
  return (0, _is.default)("TemplateLiteral", node, opts);
}

function isYieldExpression(node, opts) {
  return (0, _is.default)("YieldExpression", node, opts);
}

function isAnyTypeAnnotation(node, opts) {
  return (0, _is.default)("AnyTypeAnnotation", node, opts);
}

function isArrayTypeAnnotation(node, opts) {
  return (0, _is.default)("ArrayTypeAnnotation", node, opts);
}

function isBooleanTypeAnnotation(node, opts) {
  return (0, _is.default)("BooleanTypeAnnotation", node, opts);
}

function isBooleanLiteralTypeAnnotation(node, opts) {
  return (0, _is.default)("BooleanLiteralTypeAnnotation", node, opts);
}

function isNullLiteralTypeAnnotation(node, opts) {
  return (0, _is.default)("NullLiteralTypeAnnotation", node, opts);
}

function isClassImplements(node, opts) {
  return (0, _is.default)("ClassImplements", node, opts);
}

function isDeclareClass(node, opts) {
  return (0, _is.default)("DeclareClass", node, opts);
}

function isDeclareFunction(node, opts) {
  return (0, _is.default)("DeclareFunction", node, opts);
}

function isDeclareInterface(node, opts) {
  return (0, _is.default)("DeclareInterface", node, opts);
}

function isDeclareModule(node, opts) {
  return (0, _is.default)("DeclareModule", node, opts);
}

function isDeclareModuleExports(node, opts) {
  return (0, _is.default)("DeclareModuleExports", node, opts);
}

function isDeclareTypeAlias(node, opts) {
  return (0, _is.default)("DeclareTypeAlias", node, opts);
}

function isDeclareOpaqueType(node, opts) {
  return (0, _is.default)("DeclareOpaqueType", node, opts);
}

function isDeclareVariable(node, opts) {
  return (0, _is.default)("DeclareVariable", node, opts);
}

function isDeclareExportDeclaration(node, opts) {
  return (0, _is.default)("DeclareExportDeclaration", node, opts);
}

function isDeclareExportAllDeclaration(node, opts) {
  return (0, _is.default)("DeclareExportAllDeclaration", node, opts);
}

function isDeclaredPredicate(node, opts) {
  return (0, _is.default)("DeclaredPredicate", node, opts);
}

function isExistsTypeAnnotation(node, opts) {
  return (0, _is.default)("ExistsTypeAnnotation", node, opts);
}

function isFunctionTypeAnnotation(node, opts) {
  return (0, _is.default)("FunctionTypeAnnotation", node, opts);
}

function isFunctionTypeParam(node, opts) {
  return (0, _is.default)("FunctionTypeParam", node, opts);
}

function isGenericTypeAnnotation(node, opts) {
  return (0, _is.default)("GenericTypeAnnotation", node, opts);
}

function isInferredPredicate(node, opts) {
  return (0, _is.default)("InferredPredicate", node, opts);
}

function isInterfaceExtends(node, opts) {
  return (0, _is.default)("InterfaceExtends", node, opts);
}

function isInterfaceDeclaration(node, opts) {
  return (0, _is.default)("InterfaceDeclaration", node, opts);
}

function isIntersectionTypeAnnotation(node, opts) {
  return (0, _is.default)("IntersectionTypeAnnotation", node, opts);
}

function isMixedTypeAnnotation(node, opts) {
  return (0, _is.default)("MixedTypeAnnotation", node, opts);
}

function isEmptyTypeAnnotation(node, opts) {
  return (0, _is.default)("EmptyTypeAnnotation", node, opts);
}

function isNullableTypeAnnotation(node, opts) {
  return (0, _is.default)("NullableTypeAnnotation", node, opts);
}

function isNumberLiteralTypeAnnotation(node, opts) {
  return (0, _is.default)("NumberLiteralTypeAnnotation", node, opts);
}

function isNumberTypeAnnotation(node, opts) {
  return (0, _is.default)("NumberTypeAnnotation", node, opts);
}

function isObjectTypeAnnotation(node, opts) {
  return (0, _is.default)("ObjectTypeAnnotation", node, opts);
}

function isObjectTypeCallProperty(node, opts) {
  return (0, _is.default)("ObjectTypeCallProperty", node, opts);
}

function isObjectTypeIndexer(node, opts) {
  return (0, _is.default)("ObjectTypeIndexer", node, opts);
}

function isObjectTypeProperty(node, opts) {
  return (0, _is.default)("ObjectTypeProperty", node, opts);
}

function isObjectTypeSpreadProperty(node, opts) {
  return (0, _is.default)("ObjectTypeSpreadProperty", node, opts);
}

function isOpaqueType(node, opts) {
  return (0, _is.default)("OpaqueType", node, opts);
}

function isQualifiedTypeIdentifier(node, opts) {
  return (0, _is.default)("QualifiedTypeIdentifier", node, opts);
}

function isStringLiteralTypeAnnotation(node, opts) {
  return (0, _is.default)("StringLiteralTypeAnnotation", node, opts);
}

function isStringTypeAnnotation(node, opts) {
  return (0, _is.default)("StringTypeAnnotation", node, opts);
}

function isThisTypeAnnotation(node, opts) {
  return (0, _is.default)("ThisTypeAnnotation", node, opts);
}

function isTupleTypeAnnotation(node, opts) {
  return (0, _is.default)("TupleTypeAnnotation", node, opts);
}

function isTypeofTypeAnnotation(node, opts) {
  return (0, _is.default)("TypeofTypeAnnotation", node, opts);
}

function isTypeAlias(node, opts) {
  return (0, _is.default)("TypeAlias", node, opts);
}

function isTypeAnnotation(node, opts) {
  return (0, _is.default)("TypeAnnotation", node, opts);
}

function isTypeCastExpression(node, opts) {
  return (0, _is.default)("TypeCastExpression", node, opts);
}

function isTypeParameter(node, opts) {
  return (0, _is.default)("TypeParameter", node, opts);
}

function isTypeParameterDeclaration(node, opts) {
  return (0, _is.default)("TypeParameterDeclaration", node, opts);
}

function isTypeParameterInstantiation(node, opts) {
  return (0, _is.default)("TypeParameterInstantiation", node, opts);
}

function isUnionTypeAnnotation(node, opts) {
  return (0, _is.default)("UnionTypeAnnotation", node, opts);
}

function isVariance(node, opts) {
  return (0, _is.default)("Variance", node, opts);
}

function isVoidTypeAnnotation(node, opts) {
  return (0, _is.default)("VoidTypeAnnotation", node, opts);
}

function isJSXAttribute(node, opts) {
  return (0, _is.default)("JSXAttribute", node, opts);
}

function isJSXClosingElement(node, opts) {
  return (0, _is.default)("JSXClosingElement", node, opts);
}

function isJSXElement(node, opts) {
  return (0, _is.default)("JSXElement", node, opts);
}

function isJSXEmptyExpression(node, opts) {
  return (0, _is.default)("JSXEmptyExpression", node, opts);
}

function isJSXExpressionContainer(node, opts) {
  return (0, _is.default)("JSXExpressionContainer", node, opts);
}

function isJSXSpreadChild(node, opts) {
  return (0, _is.default)("JSXSpreadChild", node, opts);
}

function isJSXIdentifier(node, opts) {
  return (0, _is.default)("JSXIdentifier", node, opts);
}

function isJSXMemberExpression(node, opts) {
  return (0, _is.default)("JSXMemberExpression", node, opts);
}

function isJSXNamespacedName(node, opts) {
  return (0, _is.default)("JSXNamespacedName", node, opts);
}

function isJSXOpeningElement(node, opts) {
  return (0, _is.default)("JSXOpeningElement", node, opts);
}

function isJSXSpreadAttribute(node, opts) {
  return (0, _is.default)("JSXSpreadAttribute", node, opts);
}

function isJSXText(node, opts) {
  return (0, _is.default)("JSXText", node, opts);
}

function isJSXFragment(node, opts) {
  return (0, _is.default)("JSXFragment", node, opts);
}

function isJSXOpeningFragment(node, opts) {
  return (0, _is.default)("JSXOpeningFragment", node, opts);
}

function isJSXClosingFragment(node, opts) {
  return (0, _is.default)("JSXClosingFragment", node, opts);
}

function isNoop(node, opts) {
  return (0, _is.default)("Noop", node, opts);
}

function isParenthesizedExpression(node, opts) {
  return (0, _is.default)("ParenthesizedExpression", node, opts);
}

function isAwaitExpression(node, opts) {
  return (0, _is.default)("AwaitExpression", node, opts);
}

function isBindExpression(node, opts) {
  return (0, _is.default)("BindExpression", node, opts);
}

function isClassProperty(node, opts) {
  return (0, _is.default)("ClassProperty", node, opts);
}

function isImport(node, opts) {
  return (0, _is.default)("Import", node, opts);
}

function isDecorator(node, opts) {
  return (0, _is.default)("Decorator", node, opts);
}

function isDoExpression(node, opts) {
  return (0, _is.default)("DoExpression", node, opts);
}

function isExportDefaultSpecifier(node, opts) {
  return (0, _is.default)("ExportDefaultSpecifier", node, opts);
}

function isExportNamespaceSpecifier(node, opts) {
  return (0, _is.default)("ExportNamespaceSpecifier", node, opts);
}

function isTSParameterProperty(node, opts) {
  return (0, _is.default)("TSParameterProperty", node, opts);
}

function isTSDeclareFunction(node, opts) {
  return (0, _is.default)("TSDeclareFunction", node, opts);
}

function isTSDeclareMethod(node, opts) {
  return (0, _is.default)("TSDeclareMethod", node, opts);
}

function isTSQualifiedName(node, opts) {
  return (0, _is.default)("TSQualifiedName", node, opts);
}

function isTSCallSignatureDeclaration(node, opts) {
  return (0, _is.default)("TSCallSignatureDeclaration", node, opts);
}

function isTSConstructSignatureDeclaration(node, opts) {
  return (0, _is.default)("TSConstructSignatureDeclaration", node, opts);
}

function isTSPropertySignature(node, opts) {
  return (0, _is.default)("TSPropertySignature", node, opts);
}

function isTSMethodSignature(node, opts) {
  return (0, _is.default)("TSMethodSignature", node, opts);
}

function isTSIndexSignature(node, opts) {
  return (0, _is.default)("TSIndexSignature", node, opts);
}

function isTSAnyKeyword(node, opts) {
  return (0, _is.default)("TSAnyKeyword", node, opts);
}

function isTSNumberKeyword(node, opts) {
  return (0, _is.default)("TSNumberKeyword", node, opts);
}

function isTSObjectKeyword(node, opts) {
  return (0, _is.default)("TSObjectKeyword", node, opts);
}

function isTSBooleanKeyword(node, opts) {
  return (0, _is.default)("TSBooleanKeyword", node, opts);
}

function isTSStringKeyword(node, opts) {
  return (0, _is.default)("TSStringKeyword", node, opts);
}

function isTSSymbolKeyword(node, opts) {
  return (0, _is.default)("TSSymbolKeyword", node, opts);
}

function isTSVoidKeyword(node, opts) {
  return (0, _is.default)("TSVoidKeyword", node, opts);
}

function isTSUndefinedKeyword(node, opts) {
  return (0, _is.default)("TSUndefinedKeyword", node, opts);
}

function isTSNullKeyword(node, opts) {
  return (0, _is.default)("TSNullKeyword", node, opts);
}

function isTSNeverKeyword(node, opts) {
  return (0, _is.default)("TSNeverKeyword", node, opts);
}

function isTSThisType(node, opts) {
  return (0, _is.default)("TSThisType", node, opts);
}

function isTSFunctionType(node, opts) {
  return (0, _is.default)("TSFunctionType", node, opts);
}

function isTSConstructorType(node, opts) {
  return (0, _is.default)("TSConstructorType", node, opts);
}

function isTSTypeReference(node, opts) {
  return (0, _is.default)("TSTypeReference", node, opts);
}

function isTSTypePredicate(node, opts) {
  return (0, _is.default)("TSTypePredicate", node, opts);
}

function isTSTypeQuery(node, opts) {
  return (0, _is.default)("TSTypeQuery", node, opts);
}

function isTSTypeLiteral(node, opts) {
  return (0, _is.default)("TSTypeLiteral", node, opts);
}

function isTSArrayType(node, opts) {
  return (0, _is.default)("TSArrayType", node, opts);
}

function isTSTupleType(node, opts) {
  return (0, _is.default)("TSTupleType", node, opts);
}

function isTSUnionType(node, opts) {
  return (0, _is.default)("TSUnionType", node, opts);
}

function isTSIntersectionType(node, opts) {
  return (0, _is.default)("TSIntersectionType", node, opts);
}

function isTSParenthesizedType(node, opts) {
  return (0, _is.default)("TSParenthesizedType", node, opts);
}

function isTSTypeOperator(node, opts) {
  return (0, _is.default)("TSTypeOperator", node, opts);
}

function isTSIndexedAccessType(node, opts) {
  return (0, _is.default)("TSIndexedAccessType", node, opts);
}

function isTSMappedType(node, opts) {
  return (0, _is.default)("TSMappedType", node, opts);
}

function isTSLiteralType(node, opts) {
  return (0, _is.default)("TSLiteralType", node, opts);
}

function isTSExpressionWithTypeArguments(node, opts) {
  return (0, _is.default)("TSExpressionWithTypeArguments", node, opts);
}

function isTSInterfaceDeclaration(node, opts) {
  return (0, _is.default)("TSInterfaceDeclaration", node, opts);
}

function isTSInterfaceBody(node, opts) {
  return (0, _is.default)("TSInterfaceBody", node, opts);
}

function isTSTypeAliasDeclaration(node, opts) {
  return (0, _is.default)("TSTypeAliasDeclaration", node, opts);
}

function isTSAsExpression(node, opts) {
  return (0, _is.default)("TSAsExpression", node, opts);
}

function isTSTypeAssertion(node, opts) {
  return (0, _is.default)("TSTypeAssertion", node, opts);
}

function isTSEnumDeclaration(node, opts) {
  return (0, _is.default)("TSEnumDeclaration", node, opts);
}

function isTSEnumMember(node, opts) {
  return (0, _is.default)("TSEnumMember", node, opts);
}

function isTSModuleDeclaration(node, opts) {
  return (0, _is.default)("TSModuleDeclaration", node, opts);
}

function isTSModuleBlock(node, opts) {
  return (0, _is.default)("TSModuleBlock", node, opts);
}

function isTSImportEqualsDeclaration(node, opts) {
  return (0, _is.default)("TSImportEqualsDeclaration", node, opts);
}

function isTSExternalModuleReference(node, opts) {
  return (0, _is.default)("TSExternalModuleReference", node, opts);
}

function isTSNonNullExpression(node, opts) {
  return (0, _is.default)("TSNonNullExpression", node, opts);
}

function isTSExportAssignment(node, opts) {
  return (0, _is.default)("TSExportAssignment", node, opts);
}

function isTSNamespaceExportDeclaration(node, opts) {
  return (0, _is.default)("TSNamespaceExportDeclaration", node, opts);
}

function isTSTypeAnnotation(node, opts) {
  return (0, _is.default)("TSTypeAnnotation", node, opts);
}

function isTSTypeParameterInstantiation(node, opts) {
  return (0, _is.default)("TSTypeParameterInstantiation", node, opts);
}

function isTSTypeParameterDeclaration(node, opts) {
  return (0, _is.default)("TSTypeParameterDeclaration", node, opts);
}

function isTSTypeParameter(node, opts) {
  return (0, _is.default)("TSTypeParameter", node, opts);
}

function isExpression(node, opts) {
  return (0, _is.default)("Expression", node, opts);
}

function isBinary(node, opts) {
  return (0, _is.default)("Binary", node, opts);
}

function isScopable(node, opts) {
  return (0, _is.default)("Scopable", node, opts);
}

function isBlockParent(node, opts) {
  return (0, _is.default)("BlockParent", node, opts);
}

function isBlock(node, opts) {
  return (0, _is.default)("Block", node, opts);
}

function isStatement(node, opts) {
  return (0, _is.default)("Statement", node, opts);
}

function isTerminatorless(node, opts) {
  return (0, _is.default)("Terminatorless", node, opts);
}

function isCompletionStatement(node, opts) {
  return (0, _is.default)("CompletionStatement", node, opts);
}

function isConditional(node, opts) {
  return (0, _is.default)("Conditional", node, opts);
}

function isLoop(node, opts) {
  return (0, _is.default)("Loop", node, opts);
}

function isWhile(node, opts) {
  return (0, _is.default)("While", node, opts);
}

function isExpressionWrapper(node, opts) {
  return (0, _is.default)("ExpressionWrapper", node, opts);
}

function isFor(node, opts) {
  return (0, _is.default)("For", node, opts);
}

function isForXStatement(node, opts) {
  return (0, _is.default)("ForXStatement", node, opts);
}

function isFunction(node, opts) {
  return (0, _is.default)("Function", node, opts);
}

function isFunctionParent(node, opts) {
  return (0, _is.default)("FunctionParent", node, opts);
}

function isPureish(node, opts) {
  return (0, _is.default)("Pureish", node, opts);
}

function isDeclaration(node, opts) {
  return (0, _is.default)("Declaration", node, opts);
}

function isPatternLike(node, opts) {
  return (0, _is.default)("PatternLike", node, opts);
}

function isLVal(node, opts) {
  return (0, _is.default)("LVal", node, opts);
}

function isTSEntityName(node, opts) {
  return (0, _is.default)("TSEntityName", node, opts);
}

function isLiteral(node, opts) {
  return (0, _is.default)("Literal", node, opts);
}

function isImmutable(node, opts) {
  return (0, _is.default)("Immutable", node, opts);
}

function isUserWhitespacable(node, opts) {
  return (0, _is.default)("UserWhitespacable", node, opts);
}

function isMethod(node, opts) {
  return (0, _is.default)("Method", node, opts);
}

function isObjectMember(node, opts) {
  return (0, _is.default)("ObjectMember", node, opts);
}

function isProperty(node, opts) {
  return (0, _is.default)("Property", node, opts);
}

function isUnaryLike(node, opts) {
  return (0, _is.default)("UnaryLike", node, opts);
}

function isPattern(node, opts) {
  return (0, _is.default)("Pattern", node, opts);
}

function isClass(node, opts) {
  return (0, _is.default)("Class", node, opts);
}

function isModuleDeclaration(node, opts) {
  return (0, _is.default)("ModuleDeclaration", node, opts);
}

function isExportDeclaration(node, opts) {
  return (0, _is.default)("ExportDeclaration", node, opts);
}

function isModuleSpecifier(node, opts) {
  return (0, _is.default)("ModuleSpecifier", node, opts);
}

function isFlow(node, opts) {
  return (0, _is.default)("Flow", node, opts);
}

function isFlowType(node, opts) {
  return (0, _is.default)("FlowType", node, opts);
}

function isFlowBaseAnnotation(node, opts) {
  return (0, _is.default)("FlowBaseAnnotation", node, opts);
}

function isFlowDeclaration(node, opts) {
  return (0, _is.default)("FlowDeclaration", node, opts);
}

function isFlowPredicate(node, opts) {
  return (0, _is.default)("FlowPredicate", node, opts);
}

function isJSX(node, opts) {
  return (0, _is.default)("JSX", node, opts);
}

function isTSTypeElement(node, opts) {
  return (0, _is.default)("TSTypeElement", node, opts);
}

function isTSType(node, opts) {
  return (0, _is.default)("TSType", node, opts);
}

function isNumberLiteral(node, opts) {
  console.trace("The node type NumberLiteral has been renamed to NumericLiteral");
  return (0, _is.default)("NumberLiteral", node, opts);
}

function isRegexLiteral(node, opts) {
  console.trace("The node type RegexLiteral has been renamed to RegExpLiteral");
  return (0, _is.default)("RegexLiteral", node, opts);
}

function isRestProperty(node, opts) {
  console.trace("The node type RestProperty has been renamed to RestElement");
  return (0, _is.default)("RestProperty", node, opts);
}

function isSpreadProperty(node, opts) {
  console.trace("The node type SpreadProperty has been renamed to SpreadElement");
  return (0, _is.default)("SpreadProperty", node, opts);
}

/***/ }),

/***/ 2256:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.arrayExpression = exports.ArrayExpression = ArrayExpression;
exports.assignmentExpression = exports.AssignmentExpression = AssignmentExpression;
exports.binaryExpression = exports.BinaryExpression = BinaryExpression;
exports.directive = exports.Directive = Directive;
exports.directiveLiteral = exports.DirectiveLiteral = DirectiveLiteral;
exports.blockStatement = exports.BlockStatement = BlockStatement;
exports.breakStatement = exports.BreakStatement = BreakStatement;
exports.callExpression = exports.CallExpression = CallExpression;
exports.catchClause = exports.CatchClause = CatchClause;
exports.conditionalExpression = exports.ConditionalExpression = ConditionalExpression;
exports.continueStatement = exports.ContinueStatement = ContinueStatement;
exports.debuggerStatement = exports.DebuggerStatement = DebuggerStatement;
exports.doWhileStatement = exports.DoWhileStatement = DoWhileStatement;
exports.emptyStatement = exports.EmptyStatement = EmptyStatement;
exports.expressionStatement = exports.ExpressionStatement = ExpressionStatement;
exports.file = exports.File = File;
exports.forInStatement = exports.ForInStatement = ForInStatement;
exports.forStatement = exports.ForStatement = ForStatement;
exports.functionDeclaration = exports.FunctionDeclaration = FunctionDeclaration;
exports.functionExpression = exports.FunctionExpression = FunctionExpression;
exports.identifier = exports.Identifier = Identifier;
exports.ifStatement = exports.IfStatement = IfStatement;
exports.labeledStatement = exports.LabeledStatement = LabeledStatement;
exports.stringLiteral = exports.StringLiteral = StringLiteral;
exports.numericLiteral = exports.NumericLiteral = NumericLiteral;
exports.nullLiteral = exports.NullLiteral = NullLiteral;
exports.booleanLiteral = exports.BooleanLiteral = BooleanLiteral;
exports.regExpLiteral = exports.RegExpLiteral = RegExpLiteral;
exports.logicalExpression = exports.LogicalExpression = LogicalExpression;
exports.memberExpression = exports.MemberExpression = MemberExpression;
exports.newExpression = exports.NewExpression = NewExpression;
exports.program = exports.Program = Program;
exports.objectExpression = exports.ObjectExpression = ObjectExpression;
exports.objectMethod = exports.ObjectMethod = ObjectMethod;
exports.objectProperty = exports.ObjectProperty = ObjectProperty;
exports.restElement = exports.RestElement = RestElement;
exports.returnStatement = exports.ReturnStatement = ReturnStatement;
exports.sequenceExpression = exports.SequenceExpression = SequenceExpression;
exports.switchCase = exports.SwitchCase = SwitchCase;
exports.switchStatement = exports.SwitchStatement = SwitchStatement;
exports.thisExpression = exports.ThisExpression = ThisExpression;
exports.throwStatement = exports.ThrowStatement = ThrowStatement;
exports.tryStatement = exports.TryStatement = TryStatement;
exports.unaryExpression = exports.UnaryExpression = UnaryExpression;
exports.updateExpression = exports.UpdateExpression = UpdateExpression;
exports.variableDeclaration = exports.VariableDeclaration = VariableDeclaration;
exports.variableDeclarator = exports.VariableDeclarator = VariableDeclarator;
exports.whileStatement = exports.WhileStatement = WhileStatement;
exports.withStatement = exports.WithStatement = WithStatement;
exports.assignmentPattern = exports.AssignmentPattern = AssignmentPattern;
exports.arrayPattern = exports.ArrayPattern = ArrayPattern;
exports.arrowFunctionExpression = exports.ArrowFunctionExpression = ArrowFunctionExpression;
exports.classBody = exports.ClassBody = ClassBody;
exports.classDeclaration = exports.ClassDeclaration = ClassDeclaration;
exports.classExpression = exports.ClassExpression = ClassExpression;
exports.exportAllDeclaration = exports.ExportAllDeclaration = ExportAllDeclaration;
exports.exportDefaultDeclaration = exports.ExportDefaultDeclaration = ExportDefaultDeclaration;
exports.exportNamedDeclaration = exports.ExportNamedDeclaration = ExportNamedDeclaration;
exports.exportSpecifier = exports.ExportSpecifier = ExportSpecifier;
exports.forOfStatement = exports.ForOfStatement = ForOfStatement;
exports.importDeclaration = exports.ImportDeclaration = ImportDeclaration;
exports.importDefaultSpecifier = exports.ImportDefaultSpecifier = ImportDefaultSpecifier;
exports.importNamespaceSpecifier = exports.ImportNamespaceSpecifier = ImportNamespaceSpecifier;
exports.importSpecifier = exports.ImportSpecifier = ImportSpecifier;
exports.metaProperty = exports.MetaProperty = MetaProperty;
exports.classMethod = exports.ClassMethod = ClassMethod;
exports.objectPattern = exports.ObjectPattern = ObjectPattern;
exports.spreadElement = exports.SpreadElement = SpreadElement;
exports.super = exports.Super = Super;
exports.taggedTemplateExpression = exports.TaggedTemplateExpression = TaggedTemplateExpression;
exports.templateElement = exports.TemplateElement = TemplateElement;
exports.templateLiteral = exports.TemplateLiteral = TemplateLiteral;
exports.yieldExpression = exports.YieldExpression = YieldExpression;
exports.anyTypeAnnotation = exports.AnyTypeAnnotation = AnyTypeAnnotation;
exports.arrayTypeAnnotation = exports.ArrayTypeAnnotation = ArrayTypeAnnotation;
exports.booleanTypeAnnotation = exports.BooleanTypeAnnotation = BooleanTypeAnnotation;
exports.booleanLiteralTypeAnnotation = exports.BooleanLiteralTypeAnnotation = BooleanLiteralTypeAnnotation;
exports.nullLiteralTypeAnnotation = exports.NullLiteralTypeAnnotation = NullLiteralTypeAnnotation;
exports.classImplements = exports.ClassImplements = ClassImplements;
exports.declareClass = exports.DeclareClass = DeclareClass;
exports.declareFunction = exports.DeclareFunction = DeclareFunction;
exports.declareInterface = exports.DeclareInterface = DeclareInterface;
exports.declareModule = exports.DeclareModule = DeclareModule;
exports.declareModuleExports = exports.DeclareModuleExports = DeclareModuleExports;
exports.declareTypeAlias = exports.DeclareTypeAlias = DeclareTypeAlias;
exports.declareOpaqueType = exports.DeclareOpaqueType = DeclareOpaqueType;
exports.declareVariable = exports.DeclareVariable = DeclareVariable;
exports.declareExportDeclaration = exports.DeclareExportDeclaration = DeclareExportDeclaration;
exports.declareExportAllDeclaration = exports.DeclareExportAllDeclaration = DeclareExportAllDeclaration;
exports.declaredPredicate = exports.DeclaredPredicate = DeclaredPredicate;
exports.existsTypeAnnotation = exports.ExistsTypeAnnotation = ExistsTypeAnnotation;
exports.functionTypeAnnotation = exports.FunctionTypeAnnotation = FunctionTypeAnnotation;
exports.functionTypeParam = exports.FunctionTypeParam = FunctionTypeParam;
exports.genericTypeAnnotation = exports.GenericTypeAnnotation = GenericTypeAnnotation;
exports.inferredPredicate = exports.InferredPredicate = InferredPredicate;
exports.interfaceExtends = exports.InterfaceExtends = InterfaceExtends;
exports.interfaceDeclaration = exports.InterfaceDeclaration = InterfaceDeclaration;
exports.intersectionTypeAnnotation = exports.IntersectionTypeAnnotation = IntersectionTypeAnnotation;
exports.mixedTypeAnnotation = exports.MixedTypeAnnotation = MixedTypeAnnotation;
exports.emptyTypeAnnotation = exports.EmptyTypeAnnotation = EmptyTypeAnnotation;
exports.nullableTypeAnnotation = exports.NullableTypeAnnotation = NullableTypeAnnotation;
exports.numberLiteralTypeAnnotation = exports.NumberLiteralTypeAnnotation = NumberLiteralTypeAnnotation;
exports.numberTypeAnnotation = exports.NumberTypeAnnotation = NumberTypeAnnotation;
exports.objectTypeAnnotation = exports.ObjectTypeAnnotation = ObjectTypeAnnotation;
exports.objectTypeCallProperty = exports.ObjectTypeCallProperty = ObjectTypeCallProperty;
exports.objectTypeIndexer = exports.ObjectTypeIndexer = ObjectTypeIndexer;
exports.objectTypeProperty = exports.ObjectTypeProperty = ObjectTypeProperty;
exports.objectTypeSpreadProperty = exports.ObjectTypeSpreadProperty = ObjectTypeSpreadProperty;
exports.opaqueType = exports.OpaqueType = OpaqueType;
exports.qualifiedTypeIdentifier = exports.QualifiedTypeIdentifier = QualifiedTypeIdentifier;
exports.stringLiteralTypeAnnotation = exports.StringLiteralTypeAnnotation = StringLiteralTypeAnnotation;
exports.stringTypeAnnotation = exports.StringTypeAnnotation = StringTypeAnnotation;
exports.thisTypeAnnotation = exports.ThisTypeAnnotation = ThisTypeAnnotation;
exports.tupleTypeAnnotation = exports.TupleTypeAnnotation = TupleTypeAnnotation;
exports.typeofTypeAnnotation = exports.TypeofTypeAnnotation = TypeofTypeAnnotation;
exports.typeAlias = exports.TypeAlias = TypeAlias;
exports.typeAnnotation = exports.TypeAnnotation = TypeAnnotation;
exports.typeCastExpression = exports.TypeCastExpression = TypeCastExpression;
exports.typeParameter = exports.TypeParameter = TypeParameter;
exports.typeParameterDeclaration = exports.TypeParameterDeclaration = TypeParameterDeclaration;
exports.typeParameterInstantiation = exports.TypeParameterInstantiation = TypeParameterInstantiation;
exports.unionTypeAnnotation = exports.UnionTypeAnnotation = UnionTypeAnnotation;
exports.variance = exports.Variance = Variance;
exports.voidTypeAnnotation = exports.VoidTypeAnnotation = VoidTypeAnnotation;
exports.jSXAttribute = exports.jsxAttribute = exports.JSXAttribute = JSXAttribute;
exports.jSXClosingElement = exports.jsxClosingElement = exports.JSXClosingElement = JSXClosingElement;
exports.jSXElement = exports.jsxElement = exports.JSXElement = JSXElement;
exports.jSXEmptyExpression = exports.jsxEmptyExpression = exports.JSXEmptyExpression = JSXEmptyExpression;
exports.jSXExpressionContainer = exports.jsxExpressionContainer = exports.JSXExpressionContainer = JSXExpressionContainer;
exports.jSXSpreadChild = exports.jsxSpreadChild = exports.JSXSpreadChild = JSXSpreadChild;
exports.jSXIdentifier = exports.jsxIdentifier = exports.JSXIdentifier = JSXIdentifier;
exports.jSXMemberExpression = exports.jsxMemberExpression = exports.JSXMemberExpression = JSXMemberExpression;
exports.jSXNamespacedName = exports.jsxNamespacedName = exports.JSXNamespacedName = JSXNamespacedName;
exports.jSXOpeningElement = exports.jsxOpeningElement = exports.JSXOpeningElement = JSXOpeningElement;
exports.jSXSpreadAttribute = exports.jsxSpreadAttribute = exports.JSXSpreadAttribute = JSXSpreadAttribute;
exports.jSXText = exports.jsxText = exports.JSXText = JSXText;
exports.jSXFragment = exports.jsxFragment = exports.JSXFragment = JSXFragment;
exports.jSXOpeningFragment = exports.jsxOpeningFragment = exports.JSXOpeningFragment = JSXOpeningFragment;
exports.jSXClosingFragment = exports.jsxClosingFragment = exports.JSXClosingFragment = JSXClosingFragment;
exports.noop = exports.Noop = Noop;
exports.parenthesizedExpression = exports.ParenthesizedExpression = ParenthesizedExpression;
exports.awaitExpression = exports.AwaitExpression = AwaitExpression;
exports.bindExpression = exports.BindExpression = BindExpression;
exports.classProperty = exports.ClassProperty = ClassProperty;
exports.import = exports.Import = Import;
exports.decorator = exports.Decorator = Decorator;
exports.doExpression = exports.DoExpression = DoExpression;
exports.exportDefaultSpecifier = exports.ExportDefaultSpecifier = ExportDefaultSpecifier;
exports.exportNamespaceSpecifier = exports.ExportNamespaceSpecifier = ExportNamespaceSpecifier;
exports.tSParameterProperty = exports.tsParameterProperty = exports.TSParameterProperty = TSParameterProperty;
exports.tSDeclareFunction = exports.tsDeclareFunction = exports.TSDeclareFunction = TSDeclareFunction;
exports.tSDeclareMethod = exports.tsDeclareMethod = exports.TSDeclareMethod = TSDeclareMethod;
exports.tSQualifiedName = exports.tsQualifiedName = exports.TSQualifiedName = TSQualifiedName;
exports.tSCallSignatureDeclaration = exports.tsCallSignatureDeclaration = exports.TSCallSignatureDeclaration = TSCallSignatureDeclaration;
exports.tSConstructSignatureDeclaration = exports.tsConstructSignatureDeclaration = exports.TSConstructSignatureDeclaration = TSConstructSignatureDeclaration;
exports.tSPropertySignature = exports.tsPropertySignature = exports.TSPropertySignature = TSPropertySignature;
exports.tSMethodSignature = exports.tsMethodSignature = exports.TSMethodSignature = TSMethodSignature;
exports.tSIndexSignature = exports.tsIndexSignature = exports.TSIndexSignature = TSIndexSignature;
exports.tSAnyKeyword = exports.tsAnyKeyword = exports.TSAnyKeyword = TSAnyKeyword;
exports.tSNumberKeyword = exports.tsNumberKeyword = exports.TSNumberKeyword = TSNumberKeyword;
exports.tSObjectKeyword = exports.tsObjectKeyword = exports.TSObjectKeyword = TSObjectKeyword;
exports.tSBooleanKeyword = exports.tsBooleanKeyword = exports.TSBooleanKeyword = TSBooleanKeyword;
exports.tSStringKeyword = exports.tsStringKeyword = exports.TSStringKeyword = TSStringKeyword;
exports.tSSymbolKeyword = exports.tsSymbolKeyword = exports.TSSymbolKeyword = TSSymbolKeyword;
exports.tSVoidKeyword = exports.tsVoidKeyword = exports.TSVoidKeyword = TSVoidKeyword;
exports.tSUndefinedKeyword = exports.tsUndefinedKeyword = exports.TSUndefinedKeyword = TSUndefinedKeyword;
exports.tSNullKeyword = exports.tsNullKeyword = exports.TSNullKeyword = TSNullKeyword;
exports.tSNeverKeyword = exports.tsNeverKeyword = exports.TSNeverKeyword = TSNeverKeyword;
exports.tSThisType = exports.tsThisType = exports.TSThisType = TSThisType;
exports.tSFunctionType = exports.tsFunctionType = exports.TSFunctionType = TSFunctionType;
exports.tSConstructorType = exports.tsConstructorType = exports.TSConstructorType = TSConstructorType;
exports.tSTypeReference = exports.tsTypeReference = exports.TSTypeReference = TSTypeReference;
exports.tSTypePredicate = exports.tsTypePredicate = exports.TSTypePredicate = TSTypePredicate;
exports.tSTypeQuery = exports.tsTypeQuery = exports.TSTypeQuery = TSTypeQuery;
exports.tSTypeLiteral = exports.tsTypeLiteral = exports.TSTypeLiteral = TSTypeLiteral;
exports.tSArrayType = exports.tsArrayType = exports.TSArrayType = TSArrayType;
exports.tSTupleType = exports.tsTupleType = exports.TSTupleType = TSTupleType;
exports.tSUnionType = exports.tsUnionType = exports.TSUnionType = TSUnionType;
exports.tSIntersectionType = exports.tsIntersectionType = exports.TSIntersectionType = TSIntersectionType;
exports.tSParenthesizedType = exports.tsParenthesizedType = exports.TSParenthesizedType = TSParenthesizedType;
exports.tSTypeOperator = exports.tsTypeOperator = exports.TSTypeOperator = TSTypeOperator;
exports.tSIndexedAccessType = exports.tsIndexedAccessType = exports.TSIndexedAccessType = TSIndexedAccessType;
exports.tSMappedType = exports.tsMappedType = exports.TSMappedType = TSMappedType;
exports.tSLiteralType = exports.tsLiteralType = exports.TSLiteralType = TSLiteralType;
exports.tSExpressionWithTypeArguments = exports.tsExpressionWithTypeArguments = exports.TSExpressionWithTypeArguments = TSExpressionWithTypeArguments;
exports.tSInterfaceDeclaration = exports.tsInterfaceDeclaration = exports.TSInterfaceDeclaration = TSInterfaceDeclaration;
exports.tSInterfaceBody = exports.tsInterfaceBody = exports.TSInterfaceBody = TSInterfaceBody;
exports.tSTypeAliasDeclaration = exports.tsTypeAliasDeclaration = exports.TSTypeAliasDeclaration = TSTypeAliasDeclaration;
exports.tSAsExpression = exports.tsAsExpression = exports.TSAsExpression = TSAsExpression;
exports.tSTypeAssertion = exports.tsTypeAssertion = exports.TSTypeAssertion = TSTypeAssertion;
exports.tSEnumDeclaration = exports.tsEnumDeclaration = exports.TSEnumDeclaration = TSEnumDeclaration;
exports.tSEnumMember = exports.tsEnumMember = exports.TSEnumMember = TSEnumMember;
exports.tSModuleDeclaration = exports.tsModuleDeclaration = exports.TSModuleDeclaration = TSModuleDeclaration;
exports.tSModuleBlock = exports.tsModuleBlock = exports.TSModuleBlock = TSModuleBlock;
exports.tSImportEqualsDeclaration = exports.tsImportEqualsDeclaration = exports.TSImportEqualsDeclaration = TSImportEqualsDeclaration;
exports.tSExternalModuleReference = exports.tsExternalModuleReference = exports.TSExternalModuleReference = TSExternalModuleReference;
exports.tSNonNullExpression = exports.tsNonNullExpression = exports.TSNonNullExpression = TSNonNullExpression;
exports.tSExportAssignment = exports.tsExportAssignment = exports.TSExportAssignment = TSExportAssignment;
exports.tSNamespaceExportDeclaration = exports.tsNamespaceExportDeclaration = exports.TSNamespaceExportDeclaration = TSNamespaceExportDeclaration;
exports.tSTypeAnnotation = exports.tsTypeAnnotation = exports.TSTypeAnnotation = TSTypeAnnotation;
exports.tSTypeParameterInstantiation = exports.tsTypeParameterInstantiation = exports.TSTypeParameterInstantiation = TSTypeParameterInstantiation;
exports.tSTypeParameterDeclaration = exports.tsTypeParameterDeclaration = exports.TSTypeParameterDeclaration = TSTypeParameterDeclaration;
exports.tSTypeParameter = exports.tsTypeParameter = exports.TSTypeParameter = TSTypeParameter;
exports.numberLiteral = exports.NumberLiteral = NumberLiteral;
exports.regexLiteral = exports.RegexLiteral = RegexLiteral;
exports.restProperty = exports.RestProperty = RestProperty;
exports.spreadProperty = exports.SpreadProperty = SpreadProperty;

var _builder = _interopRequireDefault(__webpack_require__(2304));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function ArrayExpression() {
  for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
    args[_key] = arguments[_key];
  }

  return _builder.default.apply(void 0, ["ArrayExpression"].concat(args));
}

function AssignmentExpression() {
  for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
    args[_key2] = arguments[_key2];
  }

  return _builder.default.apply(void 0, ["AssignmentExpression"].concat(args));
}

function BinaryExpression() {
  for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
    args[_key3] = arguments[_key3];
  }

  return _builder.default.apply(void 0, ["BinaryExpression"].concat(args));
}

function Directive() {
  for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
    args[_key4] = arguments[_key4];
  }

  return _builder.default.apply(void 0, ["Directive"].concat(args));
}

function DirectiveLiteral() {
  for (var _len5 = arguments.length, args = new Array(_len5), _key5 = 0; _key5 < _len5; _key5++) {
    args[_key5] = arguments[_key5];
  }

  return _builder.default.apply(void 0, ["DirectiveLiteral"].concat(args));
}

function BlockStatement() {
  for (var _len6 = arguments.length, args = new Array(_len6), _key6 = 0; _key6 < _len6; _key6++) {
    args[_key6] = arguments[_key6];
  }

  return _builder.default.apply(void 0, ["BlockStatement"].concat(args));
}

function BreakStatement() {
  for (var _len7 = arguments.length, args = new Array(_len7), _key7 = 0; _key7 < _len7; _key7++) {
    args[_key7] = arguments[_key7];
  }

  return _builder.default.apply(void 0, ["BreakStatement"].concat(args));
}

function CallExpression() {
  for (var _len8 = arguments.length, args = new Array(_len8), _key8 = 0; _key8 < _len8; _key8++) {
    args[_key8] = arguments[_key8];
  }

  return _builder.default.apply(void 0, ["CallExpression"].concat(args));
}

function CatchClause() {
  for (var _len9 = arguments.length, args = new Array(_len9), _key9 = 0; _key9 < _len9; _key9++) {
    args[_key9] = arguments[_key9];
  }

  return _builder.default.apply(void 0, ["CatchClause"].concat(args));
}

function ConditionalExpression() {
  for (var _len10 = arguments.length, args = new Array(_len10), _key10 = 0; _key10 < _len10; _key10++) {
    args[_key10] = arguments[_key10];
  }

  return _builder.default.apply(void 0, ["ConditionalExpression"].concat(args));
}

function ContinueStatement() {
  for (var _len11 = arguments.length, args = new Array(_len11), _key11 = 0; _key11 < _len11; _key11++) {
    args[_key11] = arguments[_key11];
  }

  return _builder.default.apply(void 0, ["ContinueStatement"].concat(args));
}

function DebuggerStatement() {
  for (var _len12 = arguments.length, args = new Array(_len12), _key12 = 0; _key12 < _len12; _key12++) {
    args[_key12] = arguments[_key12];
  }

  return _builder.default.apply(void 0, ["DebuggerStatement"].concat(args));
}

function DoWhileStatement() {
  for (var _len13 = arguments.length, args = new Array(_len13), _key13 = 0; _key13 < _len13; _key13++) {
    args[_key13] = arguments[_key13];
  }

  return _builder.default.apply(void 0, ["DoWhileStatement"].concat(args));
}

function EmptyStatement() {
  for (var _len14 = arguments.length, args = new Array(_len14), _key14 = 0; _key14 < _len14; _key14++) {
    args[_key14] = arguments[_key14];
  }

  return _builder.default.apply(void 0, ["EmptyStatement"].concat(args));
}

function ExpressionStatement() {
  for (var _len15 = arguments.length, args = new Array(_len15), _key15 = 0; _key15 < _len15; _key15++) {
    args[_key15] = arguments[_key15];
  }

  return _builder.default.apply(void 0, ["ExpressionStatement"].concat(args));
}

function File() {
  for (var _len16 = arguments.length, args = new Array(_len16), _key16 = 0; _key16 < _len16; _key16++) {
    args[_key16] = arguments[_key16];
  }

  return _builder.default.apply(void 0, ["File"].concat(args));
}

function ForInStatement() {
  for (var _len17 = arguments.length, args = new Array(_len17), _key17 = 0; _key17 < _len17; _key17++) {
    args[_key17] = arguments[_key17];
  }

  return _builder.default.apply(void 0, ["ForInStatement"].concat(args));
}

function ForStatement() {
  for (var _len18 = arguments.length, args = new Array(_len18), _key18 = 0; _key18 < _len18; _key18++) {
    args[_key18] = arguments[_key18];
  }

  return _builder.default.apply(void 0, ["ForStatement"].concat(args));
}

function FunctionDeclaration() {
  for (var _len19 = arguments.length, args = new Array(_len19), _key19 = 0; _key19 < _len19; _key19++) {
    args[_key19] = arguments[_key19];
  }

  return _builder.default.apply(void 0, ["FunctionDeclaration"].concat(args));
}

function FunctionExpression() {
  for (var _len20 = arguments.length, args = new Array(_len20), _key20 = 0; _key20 < _len20; _key20++) {
    args[_key20] = arguments[_key20];
  }

  return _builder.default.apply(void 0, ["FunctionExpression"].concat(args));
}

function Identifier() {
  for (var _len21 = arguments.length, args = new Array(_len21), _key21 = 0; _key21 < _len21; _key21++) {
    args[_key21] = arguments[_key21];
  }

  return _builder.default.apply(void 0, ["Identifier"].concat(args));
}

function IfStatement() {
  for (var _len22 = arguments.length, args = new Array(_len22), _key22 = 0; _key22 < _len22; _key22++) {
    args[_key22] = arguments[_key22];
  }

  return _builder.default.apply(void 0, ["IfStatement"].concat(args));
}

function LabeledStatement() {
  for (var _len23 = arguments.length, args = new Array(_len23), _key23 = 0; _key23 < _len23; _key23++) {
    args[_key23] = arguments[_key23];
  }

  return _builder.default.apply(void 0, ["LabeledStatement"].concat(args));
}

function StringLiteral() {
  for (var _len24 = arguments.length, args = new Array(_len24), _key24 = 0; _key24 < _len24; _key24++) {
    args[_key24] = arguments[_key24];
  }

  return _builder.default.apply(void 0, ["StringLiteral"].concat(args));
}

function NumericLiteral() {
  for (var _len25 = arguments.length, args = new Array(_len25), _key25 = 0; _key25 < _len25; _key25++) {
    args[_key25] = arguments[_key25];
  }

  return _builder.default.apply(void 0, ["NumericLiteral"].concat(args));
}

function NullLiteral() {
  for (var _len26 = arguments.length, args = new Array(_len26), _key26 = 0; _key26 < _len26; _key26++) {
    args[_key26] = arguments[_key26];
  }

  return _builder.default.apply(void 0, ["NullLiteral"].concat(args));
}

function BooleanLiteral() {
  for (var _len27 = arguments.length, args = new Array(_len27), _key27 = 0; _key27 < _len27; _key27++) {
    args[_key27] = arguments[_key27];
  }

  return _builder.default.apply(void 0, ["BooleanLiteral"].concat(args));
}

function RegExpLiteral() {
  for (var _len28 = arguments.length, args = new Array(_len28), _key28 = 0; _key28 < _len28; _key28++) {
    args[_key28] = arguments[_key28];
  }

  return _builder.default.apply(void 0, ["RegExpLiteral"].concat(args));
}

function LogicalExpression() {
  for (var _len29 = arguments.length, args = new Array(_len29), _key29 = 0; _key29 < _len29; _key29++) {
    args[_key29] = arguments[_key29];
  }

  return _builder.default.apply(void 0, ["LogicalExpression"].concat(args));
}

function MemberExpression() {
  for (var _len30 = arguments.length, args = new Array(_len30), _key30 = 0; _key30 < _len30; _key30++) {
    args[_key30] = arguments[_key30];
  }

  return _builder.default.apply(void 0, ["MemberExpression"].concat(args));
}

function NewExpression() {
  for (var _len31 = arguments.length, args = new Array(_len31), _key31 = 0; _key31 < _len31; _key31++) {
    args[_key31] = arguments[_key31];
  }

  return _builder.default.apply(void 0, ["NewExpression"].concat(args));
}

function Program() {
  for (var _len32 = arguments.length, args = new Array(_len32), _key32 = 0; _key32 < _len32; _key32++) {
    args[_key32] = arguments[_key32];
  }

  return _builder.default.apply(void 0, ["Program"].concat(args));
}

function ObjectExpression() {
  for (var _len33 = arguments.length, args = new Array(_len33), _key33 = 0; _key33 < _len33; _key33++) {
    args[_key33] = arguments[_key33];
  }

  return _builder.default.apply(void 0, ["ObjectExpression"].concat(args));
}

function ObjectMethod() {
  for (var _len34 = arguments.length, args = new Array(_len34), _key34 = 0; _key34 < _len34; _key34++) {
    args[_key34] = arguments[_key34];
  }

  return _builder.default.apply(void 0, ["ObjectMethod"].concat(args));
}

function ObjectProperty() {
  for (var _len35 = arguments.length, args = new Array(_len35), _key35 = 0; _key35 < _len35; _key35++) {
    args[_key35] = arguments[_key35];
  }

  return _builder.default.apply(void 0, ["ObjectProperty"].concat(args));
}

function RestElement() {
  for (var _len36 = arguments.length, args = new Array(_len36), _key36 = 0; _key36 < _len36; _key36++) {
    args[_key36] = arguments[_key36];
  }

  return _builder.default.apply(void 0, ["RestElement"].concat(args));
}

function ReturnStatement() {
  for (var _len37 = arguments.length, args = new Array(_len37), _key37 = 0; _key37 < _len37; _key37++) {
    args[_key37] = arguments[_key37];
  }

  return _builder.default.apply(void 0, ["ReturnStatement"].concat(args));
}

function SequenceExpression() {
  for (var _len38 = arguments.length, args = new Array(_len38), _key38 = 0; _key38 < _len38; _key38++) {
    args[_key38] = arguments[_key38];
  }

  return _builder.default.apply(void 0, ["SequenceExpression"].concat(args));
}

function SwitchCase() {
  for (var _len39 = arguments.length, args = new Array(_len39), _key39 = 0; _key39 < _len39; _key39++) {
    args[_key39] = arguments[_key39];
  }

  return _builder.default.apply(void 0, ["SwitchCase"].concat(args));
}

function SwitchStatement() {
  for (var _len40 = arguments.length, args = new Array(_len40), _key40 = 0; _key40 < _len40; _key40++) {
    args[_key40] = arguments[_key40];
  }

  return _builder.default.apply(void 0, ["SwitchStatement"].concat(args));
}

function ThisExpression() {
  for (var _len41 = arguments.length, args = new Array(_len41), _key41 = 0; _key41 < _len41; _key41++) {
    args[_key41] = arguments[_key41];
  }

  return _builder.default.apply(void 0, ["ThisExpression"].concat(args));
}

function ThrowStatement() {
  for (var _len42 = arguments.length, args = new Array(_len42), _key42 = 0; _key42 < _len42; _key42++) {
    args[_key42] = arguments[_key42];
  }

  return _builder.default.apply(void 0, ["ThrowStatement"].concat(args));
}

function TryStatement() {
  for (var _len43 = arguments.length, args = new Array(_len43), _key43 = 0; _key43 < _len43; _key43++) {
    args[_key43] = arguments[_key43];
  }

  return _builder.default.apply(void 0, ["TryStatement"].concat(args));
}

function UnaryExpression() {
  for (var _len44 = arguments.length, args = new Array(_len44), _key44 = 0; _key44 < _len44; _key44++) {
    args[_key44] = arguments[_key44];
  }

  return _builder.default.apply(void 0, ["UnaryExpression"].concat(args));
}

function UpdateExpression() {
  for (var _len45 = arguments.length, args = new Array(_len45), _key45 = 0; _key45 < _len45; _key45++) {
    args[_key45] = arguments[_key45];
  }

  return _builder.default.apply(void 0, ["UpdateExpression"].concat(args));
}

function VariableDeclaration() {
  for (var _len46 = arguments.length, args = new Array(_len46), _key46 = 0; _key46 < _len46; _key46++) {
    args[_key46] = arguments[_key46];
  }

  return _builder.default.apply(void 0, ["VariableDeclaration"].concat(args));
}

function VariableDeclarator() {
  for (var _len47 = arguments.length, args = new Array(_len47), _key47 = 0; _key47 < _len47; _key47++) {
    args[_key47] = arguments[_key47];
  }

  return _builder.default.apply(void 0, ["VariableDeclarator"].concat(args));
}

function WhileStatement() {
  for (var _len48 = arguments.length, args = new Array(_len48), _key48 = 0; _key48 < _len48; _key48++) {
    args[_key48] = arguments[_key48];
  }

  return _builder.default.apply(void 0, ["WhileStatement"].concat(args));
}

function WithStatement() {
  for (var _len49 = arguments.length, args = new Array(_len49), _key49 = 0; _key49 < _len49; _key49++) {
    args[_key49] = arguments[_key49];
  }

  return _builder.default.apply(void 0, ["WithStatement"].concat(args));
}

function AssignmentPattern() {
  for (var _len50 = arguments.length, args = new Array(_len50), _key50 = 0; _key50 < _len50; _key50++) {
    args[_key50] = arguments[_key50];
  }

  return _builder.default.apply(void 0, ["AssignmentPattern"].concat(args));
}

function ArrayPattern() {
  for (var _len51 = arguments.length, args = new Array(_len51), _key51 = 0; _key51 < _len51; _key51++) {
    args[_key51] = arguments[_key51];
  }

  return _builder.default.apply(void 0, ["ArrayPattern"].concat(args));
}

function ArrowFunctionExpression() {
  for (var _len52 = arguments.length, args = new Array(_len52), _key52 = 0; _key52 < _len52; _key52++) {
    args[_key52] = arguments[_key52];
  }

  return _builder.default.apply(void 0, ["ArrowFunctionExpression"].concat(args));
}

function ClassBody() {
  for (var _len53 = arguments.length, args = new Array(_len53), _key53 = 0; _key53 < _len53; _key53++) {
    args[_key53] = arguments[_key53];
  }

  return _builder.default.apply(void 0, ["ClassBody"].concat(args));
}

function ClassDeclaration() {
  for (var _len54 = arguments.length, args = new Array(_len54), _key54 = 0; _key54 < _len54; _key54++) {
    args[_key54] = arguments[_key54];
  }

  return _builder.default.apply(void 0, ["ClassDeclaration"].concat(args));
}

function ClassExpression() {
  for (var _len55 = arguments.length, args = new Array(_len55), _key55 = 0; _key55 < _len55; _key55++) {
    args[_key55] = arguments[_key55];
  }

  return _builder.default.apply(void 0, ["ClassExpression"].concat(args));
}

function ExportAllDeclaration() {
  for (var _len56 = arguments.length, args = new Array(_len56), _key56 = 0; _key56 < _len56; _key56++) {
    args[_key56] = arguments[_key56];
  }

  return _builder.default.apply(void 0, ["ExportAllDeclaration"].concat(args));
}

function ExportDefaultDeclaration() {
  for (var _len57 = arguments.length, args = new Array(_len57), _key57 = 0; _key57 < _len57; _key57++) {
    args[_key57] = arguments[_key57];
  }

  return _builder.default.apply(void 0, ["ExportDefaultDeclaration"].concat(args));
}

function ExportNamedDeclaration() {
  for (var _len58 = arguments.length, args = new Array(_len58), _key58 = 0; _key58 < _len58; _key58++) {
    args[_key58] = arguments[_key58];
  }

  return _builder.default.apply(void 0, ["ExportNamedDeclaration"].concat(args));
}

function ExportSpecifier() {
  for (var _len59 = arguments.length, args = new Array(_len59), _key59 = 0; _key59 < _len59; _key59++) {
    args[_key59] = arguments[_key59];
  }

  return _builder.default.apply(void 0, ["ExportSpecifier"].concat(args));
}

function ForOfStatement() {
  for (var _len60 = arguments.length, args = new Array(_len60), _key60 = 0; _key60 < _len60; _key60++) {
    args[_key60] = arguments[_key60];
  }

  return _builder.default.apply(void 0, ["ForOfStatement"].concat(args));
}

function ImportDeclaration() {
  for (var _len61 = arguments.length, args = new Array(_len61), _key61 = 0; _key61 < _len61; _key61++) {
    args[_key61] = arguments[_key61];
  }

  return _builder.default.apply(void 0, ["ImportDeclaration"].concat(args));
}

function ImportDefaultSpecifier() {
  for (var _len62 = arguments.length, args = new Array(_len62), _key62 = 0; _key62 < _len62; _key62++) {
    args[_key62] = arguments[_key62];
  }

  return _builder.default.apply(void 0, ["ImportDefaultSpecifier"].concat(args));
}

function ImportNamespaceSpecifier() {
  for (var _len63 = arguments.length, args = new Array(_len63), _key63 = 0; _key63 < _len63; _key63++) {
    args[_key63] = arguments[_key63];
  }

  return _builder.default.apply(void 0, ["ImportNamespaceSpecifier"].concat(args));
}

function ImportSpecifier() {
  for (var _len64 = arguments.length, args = new Array(_len64), _key64 = 0; _key64 < _len64; _key64++) {
    args[_key64] = arguments[_key64];
  }

  return _builder.default.apply(void 0, ["ImportSpecifier"].concat(args));
}

function MetaProperty() {
  for (var _len65 = arguments.length, args = new Array(_len65), _key65 = 0; _key65 < _len65; _key65++) {
    args[_key65] = arguments[_key65];
  }

  return _builder.default.apply(void 0, ["MetaProperty"].concat(args));
}

function ClassMethod() {
  for (var _len66 = arguments.length, args = new Array(_len66), _key66 = 0; _key66 < _len66; _key66++) {
    args[_key66] = arguments[_key66];
  }

  return _builder.default.apply(void 0, ["ClassMethod"].concat(args));
}

function ObjectPattern() {
  for (var _len67 = arguments.length, args = new Array(_len67), _key67 = 0; _key67 < _len67; _key67++) {
    args[_key67] = arguments[_key67];
  }

  return _builder.default.apply(void 0, ["ObjectPattern"].concat(args));
}

function SpreadElement() {
  for (var _len68 = arguments.length, args = new Array(_len68), _key68 = 0; _key68 < _len68; _key68++) {
    args[_key68] = arguments[_key68];
  }

  return _builder.default.apply(void 0, ["SpreadElement"].concat(args));
}

function Super() {
  for (var _len69 = arguments.length, args = new Array(_len69), _key69 = 0; _key69 < _len69; _key69++) {
    args[_key69] = arguments[_key69];
  }

  return _builder.default.apply(void 0, ["Super"].concat(args));
}

function TaggedTemplateExpression() {
  for (var _len70 = arguments.length, args = new Array(_len70), _key70 = 0; _key70 < _len70; _key70++) {
    args[_key70] = arguments[_key70];
  }

  return _builder.default.apply(void 0, ["TaggedTemplateExpression"].concat(args));
}

function TemplateElement() {
  for (var _len71 = arguments.length, args = new Array(_len71), _key71 = 0; _key71 < _len71; _key71++) {
    args[_key71] = arguments[_key71];
  }

  return _builder.default.apply(void 0, ["TemplateElement"].concat(args));
}

function TemplateLiteral() {
  for (var _len72 = arguments.length, args = new Array(_len72), _key72 = 0; _key72 < _len72; _key72++) {
    args[_key72] = arguments[_key72];
  }

  return _builder.default.apply(void 0, ["TemplateLiteral"].concat(args));
}

function YieldExpression() {
  for (var _len73 = arguments.length, args = new Array(_len73), _key73 = 0; _key73 < _len73; _key73++) {
    args[_key73] = arguments[_key73];
  }

  return _builder.default.apply(void 0, ["YieldExpression"].concat(args));
}

function AnyTypeAnnotation() {
  for (var _len74 = arguments.length, args = new Array(_len74), _key74 = 0; _key74 < _len74; _key74++) {
    args[_key74] = arguments[_key74];
  }

  return _builder.default.apply(void 0, ["AnyTypeAnnotation"].concat(args));
}

function ArrayTypeAnnotation() {
  for (var _len75 = arguments.length, args = new Array(_len75), _key75 = 0; _key75 < _len75; _key75++) {
    args[_key75] = arguments[_key75];
  }

  return _builder.default.apply(void 0, ["ArrayTypeAnnotation"].concat(args));
}

function BooleanTypeAnnotation() {
  for (var _len76 = arguments.length, args = new Array(_len76), _key76 = 0; _key76 < _len76; _key76++) {
    args[_key76] = arguments[_key76];
  }

  return _builder.default.apply(void 0, ["BooleanTypeAnnotation"].concat(args));
}

function BooleanLiteralTypeAnnotation() {
  for (var _len77 = arguments.length, args = new Array(_len77), _key77 = 0; _key77 < _len77; _key77++) {
    args[_key77] = arguments[_key77];
  }

  return _builder.default.apply(void 0, ["BooleanLiteralTypeAnnotation"].concat(args));
}

function NullLiteralTypeAnnotation() {
  for (var _len78 = arguments.length, args = new Array(_len78), _key78 = 0; _key78 < _len78; _key78++) {
    args[_key78] = arguments[_key78];
  }

  return _builder.default.apply(void 0, ["NullLiteralTypeAnnotation"].concat(args));
}

function ClassImplements() {
  for (var _len79 = arguments.length, args = new Array(_len79), _key79 = 0; _key79 < _len79; _key79++) {
    args[_key79] = arguments[_key79];
  }

  return _builder.default.apply(void 0, ["ClassImplements"].concat(args));
}

function DeclareClass() {
  for (var _len80 = arguments.length, args = new Array(_len80), _key80 = 0; _key80 < _len80; _key80++) {
    args[_key80] = arguments[_key80];
  }

  return _builder.default.apply(void 0, ["DeclareClass"].concat(args));
}

function DeclareFunction() {
  for (var _len81 = arguments.length, args = new Array(_len81), _key81 = 0; _key81 < _len81; _key81++) {
    args[_key81] = arguments[_key81];
  }

  return _builder.default.apply(void 0, ["DeclareFunction"].concat(args));
}

function DeclareInterface() {
  for (var _len82 = arguments.length, args = new Array(_len82), _key82 = 0; _key82 < _len82; _key82++) {
    args[_key82] = arguments[_key82];
  }

  return _builder.default.apply(void 0, ["DeclareInterface"].concat(args));
}

function DeclareModule() {
  for (var _len83 = arguments.length, args = new Array(_len83), _key83 = 0; _key83 < _len83; _key83++) {
    args[_key83] = arguments[_key83];
  }

  return _builder.default.apply(void 0, ["DeclareModule"].concat(args));
}

function DeclareModuleExports() {
  for (var _len84 = arguments.length, args = new Array(_len84), _key84 = 0; _key84 < _len84; _key84++) {
    args[_key84] = arguments[_key84];
  }

  return _builder.default.apply(void 0, ["DeclareModuleExports"].concat(args));
}

function DeclareTypeAlias() {
  for (var _len85 = arguments.length, args = new Array(_len85), _key85 = 0; _key85 < _len85; _key85++) {
    args[_key85] = arguments[_key85];
  }

  return _builder.default.apply(void 0, ["DeclareTypeAlias"].concat(args));
}

function DeclareOpaqueType() {
  for (var _len86 = arguments.length, args = new Array(_len86), _key86 = 0; _key86 < _len86; _key86++) {
    args[_key86] = arguments[_key86];
  }

  return _builder.default.apply(void 0, ["DeclareOpaqueType"].concat(args));
}

function DeclareVariable() {
  for (var _len87 = arguments.length, args = new Array(_len87), _key87 = 0; _key87 < _len87; _key87++) {
    args[_key87] = arguments[_key87];
  }

  return _builder.default.apply(void 0, ["DeclareVariable"].concat(args));
}

function DeclareExportDeclaration() {
  for (var _len88 = arguments.length, args = new Array(_len88), _key88 = 0; _key88 < _len88; _key88++) {
    args[_key88] = arguments[_key88];
  }

  return _builder.default.apply(void 0, ["DeclareExportDeclaration"].concat(args));
}

function DeclareExportAllDeclaration() {
  for (var _len89 = arguments.length, args = new Array(_len89), _key89 = 0; _key89 < _len89; _key89++) {
    args[_key89] = arguments[_key89];
  }

  return _builder.default.apply(void 0, ["DeclareExportAllDeclaration"].concat(args));
}

function DeclaredPredicate() {
  for (var _len90 = arguments.length, args = new Array(_len90), _key90 = 0; _key90 < _len90; _key90++) {
    args[_key90] = arguments[_key90];
  }

  return _builder.default.apply(void 0, ["DeclaredPredicate"].concat(args));
}

function ExistsTypeAnnotation() {
  for (var _len91 = arguments.length, args = new Array(_len91), _key91 = 0; _key91 < _len91; _key91++) {
    args[_key91] = arguments[_key91];
  }

  return _builder.default.apply(void 0, ["ExistsTypeAnnotation"].concat(args));
}

function FunctionTypeAnnotation() {
  for (var _len92 = arguments.length, args = new Array(_len92), _key92 = 0; _key92 < _len92; _key92++) {
    args[_key92] = arguments[_key92];
  }

  return _builder.default.apply(void 0, ["FunctionTypeAnnotation"].concat(args));
}

function FunctionTypeParam() {
  for (var _len93 = arguments.length, args = new Array(_len93), _key93 = 0; _key93 < _len93; _key93++) {
    args[_key93] = arguments[_key93];
  }

  return _builder.default.apply(void 0, ["FunctionTypeParam"].concat(args));
}

function GenericTypeAnnotation() {
  for (var _len94 = arguments.length, args = new Array(_len94), _key94 = 0; _key94 < _len94; _key94++) {
    args[_key94] = arguments[_key94];
  }

  return _builder.default.apply(void 0, ["GenericTypeAnnotation"].concat(args));
}

function InferredPredicate() {
  for (var _len95 = arguments.length, args = new Array(_len95), _key95 = 0; _key95 < _len95; _key95++) {
    args[_key95] = arguments[_key95];
  }

  return _builder.default.apply(void 0, ["InferredPredicate"].concat(args));
}

function InterfaceExtends() {
  for (var _len96 = arguments.length, args = new Array(_len96), _key96 = 0; _key96 < _len96; _key96++) {
    args[_key96] = arguments[_key96];
  }

  return _builder.default.apply(void 0, ["InterfaceExtends"].concat(args));
}

function InterfaceDeclaration() {
  for (var _len97 = arguments.length, args = new Array(_len97), _key97 = 0; _key97 < _len97; _key97++) {
    args[_key97] = arguments[_key97];
  }

  return _builder.default.apply(void 0, ["InterfaceDeclaration"].concat(args));
}

function IntersectionTypeAnnotation() {
  for (var _len98 = arguments.length, args = new Array(_len98), _key98 = 0; _key98 < _len98; _key98++) {
    args[_key98] = arguments[_key98];
  }

  return _builder.default.apply(void 0, ["IntersectionTypeAnnotation"].concat(args));
}

function MixedTypeAnnotation() {
  for (var _len99 = arguments.length, args = new Array(_len99), _key99 = 0; _key99 < _len99; _key99++) {
    args[_key99] = arguments[_key99];
  }

  return _builder.default.apply(void 0, ["MixedTypeAnnotation"].concat(args));
}

function EmptyTypeAnnotation() {
  for (var _len100 = arguments.length, args = new Array(_len100), _key100 = 0; _key100 < _len100; _key100++) {
    args[_key100] = arguments[_key100];
  }

  return _builder.default.apply(void 0, ["EmptyTypeAnnotation"].concat(args));
}

function NullableTypeAnnotation() {
  for (var _len101 = arguments.length, args = new Array(_len101), _key101 = 0; _key101 < _len101; _key101++) {
    args[_key101] = arguments[_key101];
  }

  return _builder.default.apply(void 0, ["NullableTypeAnnotation"].concat(args));
}

function NumberLiteralTypeAnnotation() {
  for (var _len102 = arguments.length, args = new Array(_len102), _key102 = 0; _key102 < _len102; _key102++) {
    args[_key102] = arguments[_key102];
  }

  return _builder.default.apply(void 0, ["NumberLiteralTypeAnnotation"].concat(args));
}

function NumberTypeAnnotation() {
  for (var _len103 = arguments.length, args = new Array(_len103), _key103 = 0; _key103 < _len103; _key103++) {
    args[_key103] = arguments[_key103];
  }

  return _builder.default.apply(void 0, ["NumberTypeAnnotation"].concat(args));
}

function ObjectTypeAnnotation() {
  for (var _len104 = arguments.length, args = new Array(_len104), _key104 = 0; _key104 < _len104; _key104++) {
    args[_key104] = arguments[_key104];
  }

  return _builder.default.apply(void 0, ["ObjectTypeAnnotation"].concat(args));
}

function ObjectTypeCallProperty() {
  for (var _len105 = arguments.length, args = new Array(_len105), _key105 = 0; _key105 < _len105; _key105++) {
    args[_key105] = arguments[_key105];
  }

  return _builder.default.apply(void 0, ["ObjectTypeCallProperty"].concat(args));
}

function ObjectTypeIndexer() {
  for (var _len106 = arguments.length, args = new Array(_len106), _key106 = 0; _key106 < _len106; _key106++) {
    args[_key106] = arguments[_key106];
  }

  return _builder.default.apply(void 0, ["ObjectTypeIndexer"].concat(args));
}

function ObjectTypeProperty() {
  for (var _len107 = arguments.length, args = new Array(_len107), _key107 = 0; _key107 < _len107; _key107++) {
    args[_key107] = arguments[_key107];
  }

  return _builder.default.apply(void 0, ["ObjectTypeProperty"].concat(args));
}

function ObjectTypeSpreadProperty() {
  for (var _len108 = arguments.length, args = new Array(_len108), _key108 = 0; _key108 < _len108; _key108++) {
    args[_key108] = arguments[_key108];
  }

  return _builder.default.apply(void 0, ["ObjectTypeSpreadProperty"].concat(args));
}

function OpaqueType() {
  for (var _len109 = arguments.length, args = new Array(_len109), _key109 = 0; _key109 < _len109; _key109++) {
    args[_key109] = arguments[_key109];
  }

  return _builder.default.apply(void 0, ["OpaqueType"].concat(args));
}

function QualifiedTypeIdentifier() {
  for (var _len110 = arguments.length, args = new Array(_len110), _key110 = 0; _key110 < _len110; _key110++) {
    args[_key110] = arguments[_key110];
  }

  return _builder.default.apply(void 0, ["QualifiedTypeIdentifier"].concat(args));
}

function StringLiteralTypeAnnotation() {
  for (var _len111 = arguments.length, args = new Array(_len111), _key111 = 0; _key111 < _len111; _key111++) {
    args[_key111] = arguments[_key111];
  }

  return _builder.default.apply(void 0, ["StringLiteralTypeAnnotation"].concat(args));
}

function StringTypeAnnotation() {
  for (var _len112 = arguments.length, args = new Array(_len112), _key112 = 0; _key112 < _len112; _key112++) {
    args[_key112] = arguments[_key112];
  }

  return _builder.default.apply(void 0, ["StringTypeAnnotation"].concat(args));
}

function ThisTypeAnnotation() {
  for (var _len113 = arguments.length, args = new Array(_len113), _key113 = 0; _key113 < _len113; _key113++) {
    args[_key113] = arguments[_key113];
  }

  return _builder.default.apply(void 0, ["ThisTypeAnnotation"].concat(args));
}

function TupleTypeAnnotation() {
  for (var _len114 = arguments.length, args = new Array(_len114), _key114 = 0; _key114 < _len114; _key114++) {
    args[_key114] = arguments[_key114];
  }

  return _builder.default.apply(void 0, ["TupleTypeAnnotation"].concat(args));
}

function TypeofTypeAnnotation() {
  for (var _len115 = arguments.length, args = new Array(_len115), _key115 = 0; _key115 < _len115; _key115++) {
    args[_key115] = arguments[_key115];
  }

  return _builder.default.apply(void 0, ["TypeofTypeAnnotation"].concat(args));
}

function TypeAlias() {
  for (var _len116 = arguments.length, args = new Array(_len116), _key116 = 0; _key116 < _len116; _key116++) {
    args[_key116] = arguments[_key116];
  }

  return _builder.default.apply(void 0, ["TypeAlias"].concat(args));
}

function TypeAnnotation() {
  for (var _len117 = arguments.length, args = new Array(_len117), _key117 = 0; _key117 < _len117; _key117++) {
    args[_key117] = arguments[_key117];
  }

  return _builder.default.apply(void 0, ["TypeAnnotation"].concat(args));
}

function TypeCastExpression() {
  for (var _len118 = arguments.length, args = new Array(_len118), _key118 = 0; _key118 < _len118; _key118++) {
    args[_key118] = arguments[_key118];
  }

  return _builder.default.apply(void 0, ["TypeCastExpression"].concat(args));
}

function TypeParameter() {
  for (var _len119 = arguments.length, args = new Array(_len119), _key119 = 0; _key119 < _len119; _key119++) {
    args[_key119] = arguments[_key119];
  }

  return _builder.default.apply(void 0, ["TypeParameter"].concat(args));
}

function TypeParameterDeclaration() {
  for (var _len120 = arguments.length, args = new Array(_len120), _key120 = 0; _key120 < _len120; _key120++) {
    args[_key120] = arguments[_key120];
  }

  return _builder.default.apply(void 0, ["TypeParameterDeclaration"].concat(args));
}

function TypeParameterInstantiation() {
  for (var _len121 = arguments.length, args = new Array(_len121), _key121 = 0; _key121 < _len121; _key121++) {
    args[_key121] = arguments[_key121];
  }

  return _builder.default.apply(void 0, ["TypeParameterInstantiation"].concat(args));
}

function UnionTypeAnnotation() {
  for (var _len122 = arguments.length, args = new Array(_len122), _key122 = 0; _key122 < _len122; _key122++) {
    args[_key122] = arguments[_key122];
  }

  return _builder.default.apply(void 0, ["UnionTypeAnnotation"].concat(args));
}

function Variance() {
  for (var _len123 = arguments.length, args = new Array(_len123), _key123 = 0; _key123 < _len123; _key123++) {
    args[_key123] = arguments[_key123];
  }

  return _builder.default.apply(void 0, ["Variance"].concat(args));
}

function VoidTypeAnnotation() {
  for (var _len124 = arguments.length, args = new Array(_len124), _key124 = 0; _key124 < _len124; _key124++) {
    args[_key124] = arguments[_key124];
  }

  return _builder.default.apply(void 0, ["VoidTypeAnnotation"].concat(args));
}

function JSXAttribute() {
  for (var _len125 = arguments.length, args = new Array(_len125), _key125 = 0; _key125 < _len125; _key125++) {
    args[_key125] = arguments[_key125];
  }

  return _builder.default.apply(void 0, ["JSXAttribute"].concat(args));
}

function JSXClosingElement() {
  for (var _len126 = arguments.length, args = new Array(_len126), _key126 = 0; _key126 < _len126; _key126++) {
    args[_key126] = arguments[_key126];
  }

  return _builder.default.apply(void 0, ["JSXClosingElement"].concat(args));
}

function JSXElement() {
  for (var _len127 = arguments.length, args = new Array(_len127), _key127 = 0; _key127 < _len127; _key127++) {
    args[_key127] = arguments[_key127];
  }

  return _builder.default.apply(void 0, ["JSXElement"].concat(args));
}

function JSXEmptyExpression() {
  for (var _len128 = arguments.length, args = new Array(_len128), _key128 = 0; _key128 < _len128; _key128++) {
    args[_key128] = arguments[_key128];
  }

  return _builder.default.apply(void 0, ["JSXEmptyExpression"].concat(args));
}

function JSXExpressionContainer() {
  for (var _len129 = arguments.length, args = new Array(_len129), _key129 = 0; _key129 < _len129; _key129++) {
    args[_key129] = arguments[_key129];
  }

  return _builder.default.apply(void 0, ["JSXExpressionContainer"].concat(args));
}

function JSXSpreadChild() {
  for (var _len130 = arguments.length, args = new Array(_len130), _key130 = 0; _key130 < _len130; _key130++) {
    args[_key130] = arguments[_key130];
  }

  return _builder.default.apply(void 0, ["JSXSpreadChild"].concat(args));
}

function JSXIdentifier() {
  for (var _len131 = arguments.length, args = new Array(_len131), _key131 = 0; _key131 < _len131; _key131++) {
    args[_key131] = arguments[_key131];
  }

  return _builder.default.apply(void 0, ["JSXIdentifier"].concat(args));
}

function JSXMemberExpression() {
  for (var _len132 = arguments.length, args = new Array(_len132), _key132 = 0; _key132 < _len132; _key132++) {
    args[_key132] = arguments[_key132];
  }

  return _builder.default.apply(void 0, ["JSXMemberExpression"].concat(args));
}

function JSXNamespacedName() {
  for (var _len133 = arguments.length, args = new Array(_len133), _key133 = 0; _key133 < _len133; _key133++) {
    args[_key133] = arguments[_key133];
  }

  return _builder.default.apply(void 0, ["JSXNamespacedName"].concat(args));
}

function JSXOpeningElement() {
  for (var _len134 = arguments.length, args = new Array(_len134), _key134 = 0; _key134 < _len134; _key134++) {
    args[_key134] = arguments[_key134];
  }

  return _builder.default.apply(void 0, ["JSXOpeningElement"].concat(args));
}

function JSXSpreadAttribute() {
  for (var _len135 = arguments.length, args = new Array(_len135), _key135 = 0; _key135 < _len135; _key135++) {
    args[_key135] = arguments[_key135];
  }

  return _builder.default.apply(void 0, ["JSXSpreadAttribute"].concat(args));
}

function JSXText() {
  for (var _len136 = arguments.length, args = new Array(_len136), _key136 = 0; _key136 < _len136; _key136++) {
    args[_key136] = arguments[_key136];
  }

  return _builder.default.apply(void 0, ["JSXText"].concat(args));
}

function JSXFragment() {
  for (var _len137 = arguments.length, args = new Array(_len137), _key137 = 0; _key137 < _len137; _key137++) {
    args[_key137] = arguments[_key137];
  }

  return _builder.default.apply(void 0, ["JSXFragment"].concat(args));
}

function JSXOpeningFragment() {
  for (var _len138 = arguments.length, args = new Array(_len138), _key138 = 0; _key138 < _len138; _key138++) {
    args[_key138] = arguments[_key138];
  }

  return _builder.default.apply(void 0, ["JSXOpeningFragment"].concat(args));
}

function JSXClosingFragment() {
  for (var _len139 = arguments.length, args = new Array(_len139), _key139 = 0; _key139 < _len139; _key139++) {
    args[_key139] = arguments[_key139];
  }

  return _builder.default.apply(void 0, ["JSXClosingFragment"].concat(args));
}

function Noop() {
  for (var _len140 = arguments.length, args = new Array(_len140), _key140 = 0; _key140 < _len140; _key140++) {
    args[_key140] = arguments[_key140];
  }

  return _builder.default.apply(void 0, ["Noop"].concat(args));
}

function ParenthesizedExpression() {
  for (var _len141 = arguments.length, args = new Array(_len141), _key141 = 0; _key141 < _len141; _key141++) {
    args[_key141] = arguments[_key141];
  }

  return _builder.default.apply(void 0, ["ParenthesizedExpression"].concat(args));
}

function AwaitExpression() {
  for (var _len142 = arguments.length, args = new Array(_len142), _key142 = 0; _key142 < _len142; _key142++) {
    args[_key142] = arguments[_key142];
  }

  return _builder.default.apply(void 0, ["AwaitExpression"].concat(args));
}

function BindExpression() {
  for (var _len143 = arguments.length, args = new Array(_len143), _key143 = 0; _key143 < _len143; _key143++) {
    args[_key143] = arguments[_key143];
  }

  return _builder.default.apply(void 0, ["BindExpression"].concat(args));
}

function ClassProperty() {
  for (var _len144 = arguments.length, args = new Array(_len144), _key144 = 0; _key144 < _len144; _key144++) {
    args[_key144] = arguments[_key144];
  }

  return _builder.default.apply(void 0, ["ClassProperty"].concat(args));
}

function Import() {
  for (var _len145 = arguments.length, args = new Array(_len145), _key145 = 0; _key145 < _len145; _key145++) {
    args[_key145] = arguments[_key145];
  }

  return _builder.default.apply(void 0, ["Import"].concat(args));
}

function Decorator() {
  for (var _len146 = arguments.length, args = new Array(_len146), _key146 = 0; _key146 < _len146; _key146++) {
    args[_key146] = arguments[_key146];
  }

  return _builder.default.apply(void 0, ["Decorator"].concat(args));
}

function DoExpression() {
  for (var _len147 = arguments.length, args = new Array(_len147), _key147 = 0; _key147 < _len147; _key147++) {
    args[_key147] = arguments[_key147];
  }

  return _builder.default.apply(void 0, ["DoExpression"].concat(args));
}

function ExportDefaultSpecifier() {
  for (var _len148 = arguments.length, args = new Array(_len148), _key148 = 0; _key148 < _len148; _key148++) {
    args[_key148] = arguments[_key148];
  }

  return _builder.default.apply(void 0, ["ExportDefaultSpecifier"].concat(args));
}

function ExportNamespaceSpecifier() {
  for (var _len149 = arguments.length, args = new Array(_len149), _key149 = 0; _key149 < _len149; _key149++) {
    args[_key149] = arguments[_key149];
  }

  return _builder.default.apply(void 0, ["ExportNamespaceSpecifier"].concat(args));
}

function TSParameterProperty() {
  for (var _len150 = arguments.length, args = new Array(_len150), _key150 = 0; _key150 < _len150; _key150++) {
    args[_key150] = arguments[_key150];
  }

  return _builder.default.apply(void 0, ["TSParameterProperty"].concat(args));
}

function TSDeclareFunction() {
  for (var _len151 = arguments.length, args = new Array(_len151), _key151 = 0; _key151 < _len151; _key151++) {
    args[_key151] = arguments[_key151];
  }

  return _builder.default.apply(void 0, ["TSDeclareFunction"].concat(args));
}

function TSDeclareMethod() {
  for (var _len152 = arguments.length, args = new Array(_len152), _key152 = 0; _key152 < _len152; _key152++) {
    args[_key152] = arguments[_key152];
  }

  return _builder.default.apply(void 0, ["TSDeclareMethod"].concat(args));
}

function TSQualifiedName() {
  for (var _len153 = arguments.length, args = new Array(_len153), _key153 = 0; _key153 < _len153; _key153++) {
    args[_key153] = arguments[_key153];
  }

  return _builder.default.apply(void 0, ["TSQualifiedName"].concat(args));
}

function TSCallSignatureDeclaration() {
  for (var _len154 = arguments.length, args = new Array(_len154), _key154 = 0; _key154 < _len154; _key154++) {
    args[_key154] = arguments[_key154];
  }

  return _builder.default.apply(void 0, ["TSCallSignatureDeclaration"].concat(args));
}

function TSConstructSignatureDeclaration() {
  for (var _len155 = arguments.length, args = new Array(_len155), _key155 = 0; _key155 < _len155; _key155++) {
    args[_key155] = arguments[_key155];
  }

  return _builder.default.apply(void 0, ["TSConstructSignatureDeclaration"].concat(args));
}

function TSPropertySignature() {
  for (var _len156 = arguments.length, args = new Array(_len156), _key156 = 0; _key156 < _len156; _key156++) {
    args[_key156] = arguments[_key156];
  }

  return _builder.default.apply(void 0, ["TSPropertySignature"].concat(args));
}

function TSMethodSignature() {
  for (var _len157 = arguments.length, args = new Array(_len157), _key157 = 0; _key157 < _len157; _key157++) {
    args[_key157] = arguments[_key157];
  }

  return _builder.default.apply(void 0, ["TSMethodSignature"].concat(args));
}

function TSIndexSignature() {
  for (var _len158 = arguments.length, args = new Array(_len158), _key158 = 0; _key158 < _len158; _key158++) {
    args[_key158] = arguments[_key158];
  }

  return _builder.default.apply(void 0, ["TSIndexSignature"].concat(args));
}

function TSAnyKeyword() {
  for (var _len159 = arguments.length, args = new Array(_len159), _key159 = 0; _key159 < _len159; _key159++) {
    args[_key159] = arguments[_key159];
  }

  return _builder.default.apply(void 0, ["TSAnyKeyword"].concat(args));
}

function TSNumberKeyword() {
  for (var _len160 = arguments.length, args = new Array(_len160), _key160 = 0; _key160 < _len160; _key160++) {
    args[_key160] = arguments[_key160];
  }

  return _builder.default.apply(void 0, ["TSNumberKeyword"].concat(args));
}

function TSObjectKeyword() {
  for (var _len161 = arguments.length, args = new Array(_len161), _key161 = 0; _key161 < _len161; _key161++) {
    args[_key161] = arguments[_key161];
  }

  return _builder.default.apply(void 0, ["TSObjectKeyword"].concat(args));
}

function TSBooleanKeyword() {
  for (var _len162 = arguments.length, args = new Array(_len162), _key162 = 0; _key162 < _len162; _key162++) {
    args[_key162] = arguments[_key162];
  }

  return _builder.default.apply(void 0, ["TSBooleanKeyword"].concat(args));
}

function TSStringKeyword() {
  for (var _len163 = arguments.length, args = new Array(_len163), _key163 = 0; _key163 < _len163; _key163++) {
    args[_key163] = arguments[_key163];
  }

  return _builder.default.apply(void 0, ["TSStringKeyword"].concat(args));
}

function TSSymbolKeyword() {
  for (var _len164 = arguments.length, args = new Array(_len164), _key164 = 0; _key164 < _len164; _key164++) {
    args[_key164] = arguments[_key164];
  }

  return _builder.default.apply(void 0, ["TSSymbolKeyword"].concat(args));
}

function TSVoidKeyword() {
  for (var _len165 = arguments.length, args = new Array(_len165), _key165 = 0; _key165 < _len165; _key165++) {
    args[_key165] = arguments[_key165];
  }

  return _builder.default.apply(void 0, ["TSVoidKeyword"].concat(args));
}

function TSUndefinedKeyword() {
  for (var _len166 = arguments.length, args = new Array(_len166), _key166 = 0; _key166 < _len166; _key166++) {
    args[_key166] = arguments[_key166];
  }

  return _builder.default.apply(void 0, ["TSUndefinedKeyword"].concat(args));
}

function TSNullKeyword() {
  for (var _len167 = arguments.length, args = new Array(_len167), _key167 = 0; _key167 < _len167; _key167++) {
    args[_key167] = arguments[_key167];
  }

  return _builder.default.apply(void 0, ["TSNullKeyword"].concat(args));
}

function TSNeverKeyword() {
  for (var _len168 = arguments.length, args = new Array(_len168), _key168 = 0; _key168 < _len168; _key168++) {
    args[_key168] = arguments[_key168];
  }

  return _builder.default.apply(void 0, ["TSNeverKeyword"].concat(args));
}

function TSThisType() {
  for (var _len169 = arguments.length, args = new Array(_len169), _key169 = 0; _key169 < _len169; _key169++) {
    args[_key169] = arguments[_key169];
  }

  return _builder.default.apply(void 0, ["TSThisType"].concat(args));
}

function TSFunctionType() {
  for (var _len170 = arguments.length, args = new Array(_len170), _key170 = 0; _key170 < _len170; _key170++) {
    args[_key170] = arguments[_key170];
  }

  return _builder.default.apply(void 0, ["TSFunctionType"].concat(args));
}

function TSConstructorType() {
  for (var _len171 = arguments.length, args = new Array(_len171), _key171 = 0; _key171 < _len171; _key171++) {
    args[_key171] = arguments[_key171];
  }

  return _builder.default.apply(void 0, ["TSConstructorType"].concat(args));
}

function TSTypeReference() {
  for (var _len172 = arguments.length, args = new Array(_len172), _key172 = 0; _key172 < _len172; _key172++) {
    args[_key172] = arguments[_key172];
  }

  return _builder.default.apply(void 0, ["TSTypeReference"].concat(args));
}

function TSTypePredicate() {
  for (var _len173 = arguments.length, args = new Array(_len173), _key173 = 0; _key173 < _len173; _key173++) {
    args[_key173] = arguments[_key173];
  }

  return _builder.default.apply(void 0, ["TSTypePredicate"].concat(args));
}

function TSTypeQuery() {
  for (var _len174 = arguments.length, args = new Array(_len174), _key174 = 0; _key174 < _len174; _key174++) {
    args[_key174] = arguments[_key174];
  }

  return _builder.default.apply(void 0, ["TSTypeQuery"].concat(args));
}

function TSTypeLiteral() {
  for (var _len175 = arguments.length, args = new Array(_len175), _key175 = 0; _key175 < _len175; _key175++) {
    args[_key175] = arguments[_key175];
  }

  return _builder.default.apply(void 0, ["TSTypeLiteral"].concat(args));
}

function TSArrayType() {
  for (var _len176 = arguments.length, args = new Array(_len176), _key176 = 0; _key176 < _len176; _key176++) {
    args[_key176] = arguments[_key176];
  }

  return _builder.default.apply(void 0, ["TSArrayType"].concat(args));
}

function TSTupleType() {
  for (var _len177 = arguments.length, args = new Array(_len177), _key177 = 0; _key177 < _len177; _key177++) {
    args[_key177] = arguments[_key177];
  }

  return _builder.default.apply(void 0, ["TSTupleType"].concat(args));
}

function TSUnionType() {
  for (var _len178 = arguments.length, args = new Array(_len178), _key178 = 0; _key178 < _len178; _key178++) {
    args[_key178] = arguments[_key178];
  }

  return _builder.default.apply(void 0, ["TSUnionType"].concat(args));
}

function TSIntersectionType() {
  for (var _len179 = arguments.length, args = new Array(_len179), _key179 = 0; _key179 < _len179; _key179++) {
    args[_key179] = arguments[_key179];
  }

  return _builder.default.apply(void 0, ["TSIntersectionType"].concat(args));
}

function TSParenthesizedType() {
  for (var _len180 = arguments.length, args = new Array(_len180), _key180 = 0; _key180 < _len180; _key180++) {
    args[_key180] = arguments[_key180];
  }

  return _builder.default.apply(void 0, ["TSParenthesizedType"].concat(args));
}

function TSTypeOperator() {
  for (var _len181 = arguments.length, args = new Array(_len181), _key181 = 0; _key181 < _len181; _key181++) {
    args[_key181] = arguments[_key181];
  }

  return _builder.default.apply(void 0, ["TSTypeOperator"].concat(args));
}

function TSIndexedAccessType() {
  for (var _len182 = arguments.length, args = new Array(_len182), _key182 = 0; _key182 < _len182; _key182++) {
    args[_key182] = arguments[_key182];
  }

  return _builder.default.apply(void 0, ["TSIndexedAccessType"].concat(args));
}

function TSMappedType() {
  for (var _len183 = arguments.length, args = new Array(_len183), _key183 = 0; _key183 < _len183; _key183++) {
    args[_key183] = arguments[_key183];
  }

  return _builder.default.apply(void 0, ["TSMappedType"].concat(args));
}

function TSLiteralType() {
  for (var _len184 = arguments.length, args = new Array(_len184), _key184 = 0; _key184 < _len184; _key184++) {
    args[_key184] = arguments[_key184];
  }

  return _builder.default.apply(void 0, ["TSLiteralType"].concat(args));
}

function TSExpressionWithTypeArguments() {
  for (var _len185 = arguments.length, args = new Array(_len185), _key185 = 0; _key185 < _len185; _key185++) {
    args[_key185] = arguments[_key185];
  }

  return _builder.default.apply(void 0, ["TSExpressionWithTypeArguments"].concat(args));
}

function TSInterfaceDeclaration() {
  for (var _len186 = arguments.length, args = new Array(_len186), _key186 = 0; _key186 < _len186; _key186++) {
    args[_key186] = arguments[_key186];
  }

  return _builder.default.apply(void 0, ["TSInterfaceDeclaration"].concat(args));
}

function TSInterfaceBody() {
  for (var _len187 = arguments.length, args = new Array(_len187), _key187 = 0; _key187 < _len187; _key187++) {
    args[_key187] = arguments[_key187];
  }

  return _builder.default.apply(void 0, ["TSInterfaceBody"].concat(args));
}

function TSTypeAliasDeclaration() {
  for (var _len188 = arguments.length, args = new Array(_len188), _key188 = 0; _key188 < _len188; _key188++) {
    args[_key188] = arguments[_key188];
  }

  return _builder.default.apply(void 0, ["TSTypeAliasDeclaration"].concat(args));
}

function TSAsExpression() {
  for (var _len189 = arguments.length, args = new Array(_len189), _key189 = 0; _key189 < _len189; _key189++) {
    args[_key189] = arguments[_key189];
  }

  return _builder.default.apply(void 0, ["TSAsExpression"].concat(args));
}

function TSTypeAssertion() {
  for (var _len190 = arguments.length, args = new Array(_len190), _key190 = 0; _key190 < _len190; _key190++) {
    args[_key190] = arguments[_key190];
  }

  return _builder.default.apply(void 0, ["TSTypeAssertion"].concat(args));
}

function TSEnumDeclaration() {
  for (var _len191 = arguments.length, args = new Array(_len191), _key191 = 0; _key191 < _len191; _key191++) {
    args[_key191] = arguments[_key191];
  }

  return _builder.default.apply(void 0, ["TSEnumDeclaration"].concat(args));
}

function TSEnumMember() {
  for (var _len192 = arguments.length, args = new Array(_len192), _key192 = 0; _key192 < _len192; _key192++) {
    args[_key192] = arguments[_key192];
  }

  return _builder.default.apply(void 0, ["TSEnumMember"].concat(args));
}

function TSModuleDeclaration() {
  for (var _len193 = arguments.length, args = new Array(_len193), _key193 = 0; _key193 < _len193; _key193++) {
    args[_key193] = arguments[_key193];
  }

  return _builder.default.apply(void 0, ["TSModuleDeclaration"].concat(args));
}

function TSModuleBlock() {
  for (var _len194 = arguments.length, args = new Array(_len194), _key194 = 0; _key194 < _len194; _key194++) {
    args[_key194] = arguments[_key194];
  }

  return _builder.default.apply(void 0, ["TSModuleBlock"].concat(args));
}

function TSImportEqualsDeclaration() {
  for (var _len195 = arguments.length, args = new Array(_len195), _key195 = 0; _key195 < _len195; _key195++) {
    args[_key195] = arguments[_key195];
  }

  return _builder.default.apply(void 0, ["TSImportEqualsDeclaration"].concat(args));
}

function TSExternalModuleReference() {
  for (var _len196 = arguments.length, args = new Array(_len196), _key196 = 0; _key196 < _len196; _key196++) {
    args[_key196] = arguments[_key196];
  }

  return _builder.default.apply(void 0, ["TSExternalModuleReference"].concat(args));
}

function TSNonNullExpression() {
  for (var _len197 = arguments.length, args = new Array(_len197), _key197 = 0; _key197 < _len197; _key197++) {
    args[_key197] = arguments[_key197];
  }

  return _builder.default.apply(void 0, ["TSNonNullExpression"].concat(args));
}

function TSExportAssignment() {
  for (var _len198 = arguments.length, args = new Array(_len198), _key198 = 0; _key198 < _len198; _key198++) {
    args[_key198] = arguments[_key198];
  }

  return _builder.default.apply(void 0, ["TSExportAssignment"].concat(args));
}

function TSNamespaceExportDeclaration() {
  for (var _len199 = arguments.length, args = new Array(_len199), _key199 = 0; _key199 < _len199; _key199++) {
    args[_key199] = arguments[_key199];
  }

  return _builder.default.apply(void 0, ["TSNamespaceExportDeclaration"].concat(args));
}

function TSTypeAnnotation() {
  for (var _len200 = arguments.length, args = new Array(_len200), _key200 = 0; _key200 < _len200; _key200++) {
    args[_key200] = arguments[_key200];
  }

  return _builder.default.apply(void 0, ["TSTypeAnnotation"].concat(args));
}

function TSTypeParameterInstantiation() {
  for (var _len201 = arguments.length, args = new Array(_len201), _key201 = 0; _key201 < _len201; _key201++) {
    args[_key201] = arguments[_key201];
  }

  return _builder.default.apply(void 0, ["TSTypeParameterInstantiation"].concat(args));
}

function TSTypeParameterDeclaration() {
  for (var _len202 = arguments.length, args = new Array(_len202), _key202 = 0; _key202 < _len202; _key202++) {
    args[_key202] = arguments[_key202];
  }

  return _builder.default.apply(void 0, ["TSTypeParameterDeclaration"].concat(args));
}

function TSTypeParameter() {
  for (var _len203 = arguments.length, args = new Array(_len203), _key203 = 0; _key203 < _len203; _key203++) {
    args[_key203] = arguments[_key203];
  }

  return _builder.default.apply(void 0, ["TSTypeParameter"].concat(args));
}

function NumberLiteral() {
  console.trace("The node type NumberLiteral has been renamed to NumericLiteral");

  for (var _len204 = arguments.length, args = new Array(_len204), _key204 = 0; _key204 < _len204; _key204++) {
    args[_key204] = arguments[_key204];
  }

  return NumberLiteral.apply(void 0, ["NumberLiteral"].concat(args));
}

function RegexLiteral() {
  console.trace("The node type RegexLiteral has been renamed to RegExpLiteral");

  for (var _len205 = arguments.length, args = new Array(_len205), _key205 = 0; _key205 < _len205; _key205++) {
    args[_key205] = arguments[_key205];
  }

  return RegexLiteral.apply(void 0, ["RegexLiteral"].concat(args));
}

function RestProperty() {
  console.trace("The node type RestProperty has been renamed to RestElement");

  for (var _len206 = arguments.length, args = new Array(_len206), _key206 = 0; _key206 < _len206; _key206++) {
    args[_key206] = arguments[_key206];
  }

  return RestProperty.apply(void 0, ["RestProperty"].concat(args));
}

function SpreadProperty() {
  console.trace("The node type SpreadProperty has been renamed to SpreadElement");

  for (var _len207 = arguments.length, args = new Array(_len207), _key207 = 0; _key207 < _len207; _key207++) {
    args[_key207] = arguments[_key207];
  }

  return SpreadProperty.apply(void 0, ["SpreadProperty"].concat(args));
}

/***/ }),

/***/ 2257:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.TYPES = void 0;

var _toFastProperties = _interopRequireDefault(__webpack_require__(541));

__webpack_require__(2265);

__webpack_require__(2266);

__webpack_require__(2296);

__webpack_require__(2297);

__webpack_require__(2298);

__webpack_require__(2299);

__webpack_require__(2300);

var _utils = __webpack_require__(2258);

exports.VISITOR_KEYS = _utils.VISITOR_KEYS;
exports.ALIAS_KEYS = _utils.ALIAS_KEYS;
exports.FLIPPED_ALIAS_KEYS = _utils.FLIPPED_ALIAS_KEYS;
exports.NODE_FIELDS = _utils.NODE_FIELDS;
exports.BUILDER_KEYS = _utils.BUILDER_KEYS;
exports.DEPRECATED_KEYS = _utils.DEPRECATED_KEYS;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

(0, _toFastProperties.default)(_utils.VISITOR_KEYS);
(0, _toFastProperties.default)(_utils.ALIAS_KEYS);
(0, _toFastProperties.default)(_utils.FLIPPED_ALIAS_KEYS);
(0, _toFastProperties.default)(_utils.NODE_FIELDS);
(0, _toFastProperties.default)(_utils.BUILDER_KEYS);
(0, _toFastProperties.default)(_utils.DEPRECATED_KEYS);
var TYPES = Object.keys(_utils.VISITOR_KEYS).concat(Object.keys(_utils.FLIPPED_ALIAS_KEYS)).concat(Object.keys(_utils.DEPRECATED_KEYS));
exports.TYPES = TYPES;

/***/ }),

/***/ 2258:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.validate = validate;
exports.typeIs = typeIs;
exports.validateType = validateType;
exports.validateOptional = validateOptional;
exports.validateOptionalType = validateOptionalType;
exports.arrayOf = arrayOf;
exports.arrayOfType = arrayOfType;
exports.validateArrayOfType = validateArrayOfType;
exports.assertEach = assertEach;
exports.assertOneOf = assertOneOf;
exports.assertNodeType = assertNodeType;
exports.assertNodeOrValueType = assertNodeOrValueType;
exports.assertValueType = assertValueType;
exports.chain = chain;
exports.default = defineType;
exports.DEPRECATED_KEYS = exports.BUILDER_KEYS = exports.NODE_FIELDS = exports.FLIPPED_ALIAS_KEYS = exports.ALIAS_KEYS = exports.VISITOR_KEYS = void 0;

var _is = _interopRequireDefault(__webpack_require__(2262));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

var VISITOR_KEYS = {};
exports.VISITOR_KEYS = VISITOR_KEYS;
var ALIAS_KEYS = {};
exports.ALIAS_KEYS = ALIAS_KEYS;
var FLIPPED_ALIAS_KEYS = {};
exports.FLIPPED_ALIAS_KEYS = FLIPPED_ALIAS_KEYS;
var NODE_FIELDS = {};
exports.NODE_FIELDS = NODE_FIELDS;
var BUILDER_KEYS = {};
exports.BUILDER_KEYS = BUILDER_KEYS;
var DEPRECATED_KEYS = {};
exports.DEPRECATED_KEYS = DEPRECATED_KEYS;

function getType(val) {
  if (Array.isArray(val)) {
    return "array";
  } else if (val === null) {
    return "null";
  } else if (val === undefined) {
    return "undefined";
  } else {
    return typeof val;
  }
}

function validate(validate) {
  return {
    validate: validate
  };
}

function typeIs(typeName) {
  return typeof typeName === "string" ? assertNodeType(typeName) : assertNodeType.apply(void 0, typeName);
}

function validateType(typeName) {
  return validate(typeIs(typeName));
}

function validateOptional(validate) {
  return {
    validate: validate,
    optional: true
  };
}

function validateOptionalType(typeName) {
  return {
    validate: typeIs(typeName),
    optional: true
  };
}

function arrayOf(elementType) {
  return chain(assertValueType("array"), assertEach(elementType));
}

function arrayOfType(typeName) {
  return arrayOf(typeIs(typeName));
}

function validateArrayOfType(typeName) {
  return validate(arrayOfType(typeName));
}

function assertEach(callback) {
  function validator(node, key, val) {
    if (!Array.isArray(val)) return;

    for (var i = 0; i < val.length; i++) {
      callback(node, key + "[" + i + "]", val[i]);
    }
  }

  validator.each = callback;
  return validator;
}

function assertOneOf() {
  for (var _len = arguments.length, values = new Array(_len), _key = 0; _key < _len; _key++) {
    values[_key] = arguments[_key];
  }

  function validate(node, key, val) {
    if (values.indexOf(val) < 0) {
      throw new TypeError("Property " + key + " expected value to be one of " + JSON.stringify(values) + " but got " + JSON.stringify(val));
    }
  }

  validate.oneOf = values;
  return validate;
}

function assertNodeType() {
  for (var _len2 = arguments.length, types = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
    types[_key2] = arguments[_key2];
  }

  function validate(node, key, val) {
    var valid = false;

    for (var _i = 0; _i < types.length; _i++) {
      var type = types[_i];

      if ((0, _is.default)(type, val)) {
        valid = true;
        break;
      }
    }

    if (!valid) {
      throw new TypeError("Property " + key + " of " + node.type + " expected node to be of a type " + JSON.stringify(types) + " " + ("but instead got " + JSON.stringify(val && val.type)));
    }
  }

  validate.oneOfNodeTypes = types;
  return validate;
}

function assertNodeOrValueType() {
  for (var _len3 = arguments.length, types = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
    types[_key3] = arguments[_key3];
  }

  function validate(node, key, val) {
    var valid = false;

    for (var _i2 = 0; _i2 < types.length; _i2++) {
      var type = types[_i2];

      if (getType(val) === type || (0, _is.default)(type, val)) {
        valid = true;
        break;
      }
    }

    if (!valid) {
      throw new TypeError("Property " + key + " of " + node.type + " expected node to be of a type " + JSON.stringify(types) + " " + ("but instead got " + JSON.stringify(val && val.type)));
    }
  }

  validate.oneOfNodeOrValueTypes = types;
  return validate;
}

function assertValueType(type) {
  function validate(node, key, val) {
    var valid = getType(val) === type;

    if (!valid) {
      throw new TypeError("Property " + key + " expected type of " + type + " but got " + getType(val));
    }
  }

  validate.type = type;
  return validate;
}

function chain() {
  for (var _len4 = arguments.length, fns = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
    fns[_key4] = arguments[_key4];
  }

  function validate() {
    for (var _i3 = 0; _i3 < fns.length; _i3++) {
      var fn = fns[_i3];
      fn.apply(void 0, arguments);
    }
  }

  validate.chainOf = fns;
  return validate;
}

function defineType(type, opts) {
  if (opts === void 0) {
    opts = {};
  }

  var inherits = opts.inherits && store[opts.inherits] || {};
  var fields = opts.fields || inherits.fields || {};
  var visitor = opts.visitor || inherits.visitor || [];
  var aliases = opts.aliases || inherits.aliases || [];
  var builder = opts.builder || inherits.builder || opts.visitor || [];

  if (opts.deprecatedAlias) {
    DEPRECATED_KEYS[opts.deprecatedAlias] = type;
  }

  var _arr = visitor.concat(builder);

  for (var _i4 = 0; _i4 < _arr.length; _i4++) {
    var key = _arr[_i4];
    fields[key] = fields[key] || {};
  }

  for (var _key5 in fields) {
    var field = fields[_key5];

    if (builder.indexOf(_key5) === -1) {
      field.optional = true;
    }

    if (field.default === undefined) {
      field.default = null;
    } else if (!field.validate) {
      field.validate = assertValueType(getType(field.default));
    }
  }

  VISITOR_KEYS[type] = opts.visitor = visitor;
  BUILDER_KEYS[type] = opts.builder = builder;
  NODE_FIELDS[type] = opts.fields = fields;
  ALIAS_KEYS[type] = opts.aliases = aliases;
  aliases.forEach(function (alias) {
    FLIPPED_ALIAS_KEYS[alias] = FLIPPED_ALIAS_KEYS[alias] || [];
    FLIPPED_ALIAS_KEYS[alias].push(type);
  });
  store[type] = opts;
}

var store = {};

/***/ }),

/***/ 2259:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.NOT_LOCAL_BINDING = exports.BLOCK_SCOPED_SYMBOL = exports.INHERIT_KEYS = exports.UNARY_OPERATORS = exports.STRING_UNARY_OPERATORS = exports.NUMBER_UNARY_OPERATORS = exports.BOOLEAN_UNARY_OPERATORS = exports.BINARY_OPERATORS = exports.NUMBER_BINARY_OPERATORS = exports.BOOLEAN_BINARY_OPERATORS = exports.COMPARISON_BINARY_OPERATORS = exports.EQUALITY_BINARY_OPERATORS = exports.BOOLEAN_NUMBER_BINARY_OPERATORS = exports.UPDATE_OPERATORS = exports.LOGICAL_OPERATORS = exports.COMMENT_KEYS = exports.FOR_INIT_KEYS = exports.FLATTENABLE_KEYS = exports.STATEMENT_OR_BLOCK_KEYS = void 0;
var STATEMENT_OR_BLOCK_KEYS = ["consequent", "body", "alternate"];
exports.STATEMENT_OR_BLOCK_KEYS = STATEMENT_OR_BLOCK_KEYS;
var FLATTENABLE_KEYS = ["body", "expressions"];
exports.FLATTENABLE_KEYS = FLATTENABLE_KEYS;
var FOR_INIT_KEYS = ["left", "init"];
exports.FOR_INIT_KEYS = FOR_INIT_KEYS;
var COMMENT_KEYS = ["leadingComments", "trailingComments", "innerComments"];
exports.COMMENT_KEYS = COMMENT_KEYS;
var LOGICAL_OPERATORS = ["||", "&&", "??"];
exports.LOGICAL_OPERATORS = LOGICAL_OPERATORS;
var UPDATE_OPERATORS = ["++", "--"];
exports.UPDATE_OPERATORS = UPDATE_OPERATORS;
var BOOLEAN_NUMBER_BINARY_OPERATORS = [">", "<", ">=", "<="];
exports.BOOLEAN_NUMBER_BINARY_OPERATORS = BOOLEAN_NUMBER_BINARY_OPERATORS;
var EQUALITY_BINARY_OPERATORS = ["==", "===", "!=", "!=="];
exports.EQUALITY_BINARY_OPERATORS = EQUALITY_BINARY_OPERATORS;
var COMPARISON_BINARY_OPERATORS = EQUALITY_BINARY_OPERATORS.concat(["in", "instanceof"]);
exports.COMPARISON_BINARY_OPERATORS = COMPARISON_BINARY_OPERATORS;
var BOOLEAN_BINARY_OPERATORS = COMPARISON_BINARY_OPERATORS.concat(BOOLEAN_NUMBER_BINARY_OPERATORS);
exports.BOOLEAN_BINARY_OPERATORS = BOOLEAN_BINARY_OPERATORS;
var NUMBER_BINARY_OPERATORS = ["-", "/", "%", "*", "**", "&", "|", ">>", ">>>", "<<", "^"];
exports.NUMBER_BINARY_OPERATORS = NUMBER_BINARY_OPERATORS;
var BINARY_OPERATORS = ["+"].concat(NUMBER_BINARY_OPERATORS, BOOLEAN_BINARY_OPERATORS);
exports.BINARY_OPERATORS = BINARY_OPERATORS;
var BOOLEAN_UNARY_OPERATORS = ["delete", "!"];
exports.BOOLEAN_UNARY_OPERATORS = BOOLEAN_UNARY_OPERATORS;
var NUMBER_UNARY_OPERATORS = ["+", "-", "~"];
exports.NUMBER_UNARY_OPERATORS = NUMBER_UNARY_OPERATORS;
var STRING_UNARY_OPERATORS = ["typeof"];
exports.STRING_UNARY_OPERATORS = STRING_UNARY_OPERATORS;
var UNARY_OPERATORS = ["void", "throw"].concat(BOOLEAN_UNARY_OPERATORS, NUMBER_UNARY_OPERATORS, STRING_UNARY_OPERATORS);
exports.UNARY_OPERATORS = UNARY_OPERATORS;
var INHERIT_KEYS = {
  optional: ["typeAnnotation", "typeParameters", "returnType"],
  force: ["start", "loc", "end"]
};
exports.INHERIT_KEYS = INHERIT_KEYS;
var BLOCK_SCOPED_SYMBOL = Symbol.for("var used to be block scoped");
exports.BLOCK_SCOPED_SYMBOL = BLOCK_SCOPED_SYMBOL;
var NOT_LOCAL_BINDING = Symbol.for("should not be considered a local binding");
exports.NOT_LOCAL_BINDING = NOT_LOCAL_BINDING;

/***/ }),

/***/ 2260:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = isValidIdentifier;

var _esutils = _interopRequireDefault(__webpack_require__(530));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function isValidIdentifier(name) {
  if (typeof name !== "string" || _esutils.default.keyword.isReservedWordES6(name, true)) {
    return false;
  } else if (name === "await") {
    return false;
  } else {
    return _esutils.default.keyword.isIdentifierNameES6(name);
  }
}

/***/ }),

/***/ 2261:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = cloneNode;

var _definitions = __webpack_require__(2257);

var has = Function.call.bind(Object.prototype.hasOwnProperty);

function cloneIfNode(obj, deep) {
  if (obj && typeof obj.type === "string" && obj.type !== "CommentLine" && obj.type !== "CommentBlock") {
    return cloneNode(obj, deep);
  }

  return obj;
}

function cloneIfNodeOrArray(obj, deep) {
  if (Array.isArray(obj)) {
    return obj.map(function (node) {
      return cloneIfNode(node, deep);
    });
  }

  return cloneIfNode(obj, deep);
}

function cloneNode(node, deep) {
  if (deep === void 0) {
    deep = true;
  }

  if (!node) return node;
  var type = node.type;
  var newNode = {
    type: type
  };

  if (type === "Identifier") {
    newNode.name = node.name;
  } else if (!has(_definitions.NODE_FIELDS, type)) {
    throw new Error("Unknown node type: \"" + type + "\"");
  } else {
    var _arr = Object.keys(_definitions.NODE_FIELDS[type]);

    for (var _i = 0; _i < _arr.length; _i++) {
      var field = _arr[_i];

      if (has(node, field)) {
        newNode[field] = deep ? cloneIfNodeOrArray(node[field], true) : node[field];
      }
    }
  }

  if (has(node, "loc")) {
    newNode.loc = node.loc;
  }

  if (has(node, "leadingComments")) {
    newNode.leadingComments = node.leadingComments;
  }

  if (has(node, "innerComments")) {
    newNode.innerComments = node.innerCmments;
  }

  if (has(node, "trailingComments")) {
    newNode.trailingComments = node.trailingComments;
  }

  if (has(node, "extra")) {
    newNode.extra = Object.assign({}, node.extra);
  }

  return newNode;
}

/***/ }),

/***/ 2262:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = is;

var _shallowEqual = _interopRequireDefault(__webpack_require__(2271));

var _isType = _interopRequireDefault(__webpack_require__(2264));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function is(type, node, opts) {
  if (!node) return false;
  var matches = (0, _isType.default)(node.type, type);
  if (!matches) return false;

  if (typeof opts === "undefined") {
    return true;
  } else {
    return (0, _shallowEqual.default)(node, opts);
  }
}

/***/ }),

/***/ 2263:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = getBindingIdentifiers;

var _generated = __webpack_require__(2255);

function getBindingIdentifiers(node, duplicates, outerOnly) {
  var search = [].concat(node);
  var ids = Object.create(null);

  while (search.length) {
    var id = search.shift();
    if (!id) continue;
    var keys = getBindingIdentifiers.keys[id.type];

    if ((0, _generated.isIdentifier)(id)) {
      if (duplicates) {
        var _ids = ids[id.name] = ids[id.name] || [];

        _ids.push(id);
      } else {
        ids[id.name] = id;
      }

      continue;
    }

    if ((0, _generated.isExportDeclaration)(id)) {
      if ((0, _generated.isDeclaration)(id.declaration)) {
        search.push(id.declaration);
      }

      continue;
    }

    if (outerOnly) {
      if ((0, _generated.isFunctionDeclaration)(id)) {
        search.push(id.id);
        continue;
      }

      if ((0, _generated.isFunctionExpression)(id)) {
        continue;
      }
    }

    if (keys) {
      for (var i = 0; i < keys.length; i++) {
        var key = keys[i];

        if (id[key]) {
          search = search.concat(id[key]);
        }
      }
    }
  }

  return ids;
}

getBindingIdentifiers.keys = {
  DeclareClass: ["id"],
  DeclareFunction: ["id"],
  DeclareModule: ["id"],
  DeclareVariable: ["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"],
  ForInStatement: ["left"],
  ForOfStatement: ["left"],
  ClassDeclaration: ["id"],
  ClassExpression: ["id"],
  RestElement: ["argument"],
  UpdateExpression: ["argument"],
  ObjectProperty: ["value"],
  AssignmentPattern: ["left"],
  ArrayPattern: ["elements"],
  ObjectPattern: ["properties"],
  VariableDeclaration: ["declarations"],
  VariableDeclarator: ["id"]
};

/***/ }),

/***/ 2264:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = isType;

var _definitions = __webpack_require__(2257);

function isType(nodeType, targetType) {
  if (nodeType === targetType) return true;
  if (_definitions.ALIAS_KEYS[targetType]) return false;
  var aliases = _definitions.FLIPPED_ALIAS_KEYS[targetType];

  if (aliases) {
    if (aliases[0] === nodeType) return true;

    for (var _iterator = aliases, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {
      var _ref;

      if (_isArray) {
        if (_i >= _iterator.length) break;
        _ref = _iterator[_i++];
      } else {
        _i = _iterator.next();
        if (_i.done) break;
        _ref = _i.value;
      }

      var _alias = _ref;
      if (nodeType === _alias) return true;
    }
  }

  return false;
}

/***/ }),

/***/ 2265:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.patternLikeCommon = exports.functionDeclarationCommon = exports.functionTypeAnnotationCommon = exports.functionCommon = void 0;

var _isValidIdentifier = _interopRequireDefault(__webpack_require__(2260));

var _constants = __webpack_require__(2259);

var _utils = _interopRequireWildcard(__webpack_require__(2258));

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

(0, _utils.default)("ArrayExpression", {
  fields: {
    elements: {
      validate: (0, _utils.chain)((0, _utils.assertValueType)("array"), (0, _utils.assertEach)((0, _utils.assertNodeOrValueType)("null", "Expression", "SpreadElement"))),
      default: []
    }
  },
  visitor: ["elements"],
  aliases: ["Expression"]
});
(0, _utils.default)("AssignmentExpression", {
  fields: {
    operator: {
      validate: (0, _utils.assertValueType)("string")
    },
    left: {
      validate: (0, _utils.assertNodeType)("LVal")
    },
    right: {
      validate: (0, _utils.assertNodeType)("Expression")
    }
  },
  builder: ["operator", "left", "right"],
  visitor: ["left", "right"],
  aliases: ["Expression"]
});
(0, _utils.default)("BinaryExpression", {
  builder: ["operator", "left", "right"],
  fields: {
    operator: {
      validate: _utils.assertOneOf.apply(void 0, _constants.BINARY_OPERATORS)
    },
    left: {
      validate: (0, _utils.assertNodeType)("Expression")
    },
    right: {
      validate: (0, _utils.assertNodeType)("Expression")
    }
  },
  visitor: ["left", "right"],
  aliases: ["Binary", "Expression"]
});
(0, _utils.default)("Directive", {
  visitor: ["value"],
  fields: {
    value: {
      validate: (0, _utils.assertNodeType)("DirectiveLiteral")
    }
  }
});
(0, _utils.default)("DirectiveLiteral", {
  builder: ["value"],
  fields: {
    value: {
      validate: (0, _utils.assertValueType)("string")
    }
  }
});
(0, _utils.default)("BlockStatement", {
  builder: ["body", "directives"],
  visitor: ["directives", "body"],
  fields: {
    directives: {
      validate: (0, _utils.chain)((0, _utils.assertValueType)("array"), (0, _utils.assertEach)((0, _utils.assertNodeType)("Directive"))),
      default: []
    },
    body: {
      validate: (0, _utils.chain)((0, _utils.assertValueType)("array"), (0, _utils.assertEach)((0, _utils.assertNodeType)("Statement")))
    }
  },
  aliases: ["Scopable", "BlockParent", "Block", "Statement"]
});
(0, _utils.default)("BreakStatement", {
  visitor: ["label"],
  fields: {
    label: {
      validate: (0, _utils.assertNodeType)("Identifier"),
      optional: true
    }
  },
  aliases: ["Statement", "Terminatorless", "CompletionStatement"]
});
(0, _utils.default)("CallExpression", {
  visitor: ["callee", "arguments", "typeParameters"],
  builder: ["callee", "arguments"],
  aliases: ["Expression"],
  fields: {
    callee: {
      validate: (0, _utils.assertNodeType)("Expression")
    },
    arguments: {
      validate: (0, _utils.chain)((0, _utils.assertValueType)("array"), (0, _utils.assertEach)((0, _utils.assertNodeType)("Expression", "SpreadElement", "JSXNamespacedName")))
    },
    optional: {
      validate: (0, _utils.assertOneOf)(true, false),
      optional: true
    },
    typeParameters: {
      validate: (0, _utils.assertNodeType)("TypeParameterInstantiation", "TSTypeParameterInstantiation"),
      optional: true
    }
  }
});
(0, _utils.default)("CatchClause", {
  visitor: ["param", "body"],
  fields: {
    param: {
      validate: (0, _utils.assertNodeType)("Identifier"),
      optional: true
    },
    body: {
      validate: (0, _utils.assertNodeType)("BlockStatement")
    }
  },
  aliases: ["Scopable", "BlockParent"]
});
(0, _utils.default)("ConditionalExpression", {
  visitor: ["test", "consequent", "alternate"],
  fields: {
    test: {
      validate: (0, _utils.assertNodeType)("Expression")
    },
    consequent: {
      validate: (0, _utils.assertNodeType)("Expression")
    },
    alternate: {
      validate: (0, _utils.assertNodeType)("Expression")
    }
  },
  aliases: ["Expression", "Conditional"]
});
(0, _utils.default)("ContinueStatement", {
  visitor: ["label"],
  fields: {
    label: {
      validate: (0, _utils.assertNodeType)("Identifier"),
      optional: true
    }
  },
  aliases: ["Statement", "Terminatorless", "CompletionStatement"]
});
(0, _utils.default)("DebuggerStatement", {
  aliases: ["Statement"]
});
(0, _utils.default)("DoWhileStatement", {
  visitor: ["test", "body"],
  fields: {
    test: {
      validate: (0, _utils.assertNodeType)("Expression")
    },
    body: {
      validate: (0, _utils.assertNodeType)("Statement")
    }
  },
  aliases: ["Statement", "BlockParent", "Loop", "While", "Scopable"]
});
(0, _utils.default)("EmptyStatement", {
  aliases: ["Statement"]
});
(0, _utils.default)("ExpressionStatement", {
  visitor: ["expression"],
  fields: {
    expression: {
      validate: (0, _utils.assertNodeType)("Expression")
    }
  },
  aliases: ["Statement", "ExpressionWrapper"]
});
(0, _utils.default)("File", {
  builder: ["program", "comments", "tokens"],
  visitor: ["program"],
  fields: {
    program: {
      validate: (0, _utils.assertNodeType)("Program")
    }
  }
});
(0, _utils.default)("ForInStatement", {
  visitor: ["left", "right", "body"],
  aliases: ["Scopable", "Statement", "For", "BlockParent", "Loop", "ForXStatement"],
  fields: {
    left: {
      validate: (0, _utils.assertNodeType)("VariableDeclaration", "LVal")
    },
    right: {
      validate: (0, _utils.assertNodeType)("Expression")
    },
    body: {
      validate: (0, _utils.assertNodeType)("Statement")
    }
  }
});
(0, _utils.default)("ForStatement", {
  visitor: ["init", "test", "update", "body"],
  aliases: ["Scopable", "Statement", "For", "BlockParent", "Loop"],
  fields: {
    init: {
      validate: (0, _utils.assertNodeType)("VariableDeclaration", "Expression"),
      optional: true
    },
    test: {
      validate: (0, _utils.assertNodeType)("Expression"),
      optional: true
    },
    update: {
      validate: (0, _utils.assertNodeType)("Expression"),
      optional: true
    },
    body: {
      validate: (0, _utils.assertNodeType)("Statement")
    }
  }
});
var functionCommon = {
  params: {
    validate: (0, _utils.chain)((0, _utils.assertValueType)("array"), (0, _utils.assertEach)((0, _utils.assertNodeType)("LVal")))
  },
  generator: {
    default: false,
    validate: (0, _utils.assertValueType)("boolean")
  },
  async: {
    validate: (0, _utils.assertValueType)("boolean"),
    default: false
  }
};
exports.functionCommon = functionCommon;
var functionTypeAnnotationCommon = {
  returnType: {
    validate: (0, _utils.assertNodeType)("TypeAnnotation", "TSTypeAnnotation", "Noop"),
    optional: true
  },
  typeParameters: {
    validate: (0, _utils.assertNodeType)("TypeParameterDeclaration", "TSTypeParameterDeclaration", "Noop"),
    optional: true
  }
};
exports.functionTypeAnnotationCommon = functionTypeAnnotationCommon;
var functionDeclarationCommon = Object.assign({}, functionCommon, {
  declare: {
    validate: (0, _utils.assertValueType)("boolean"),
    optional: true
  },
  id: {
    validate: (0, _utils.assertNodeType)("Identifier"),
    optional: true
  }
});
exports.functionDeclarationCommon = functionDeclarationCommon;
(0, _utils.default)("FunctionDeclaration", {
  builder: ["id", "params", "body", "generator", "async"],
  visitor: ["id", "params", "body", "returnType", "typeParameters"],
  fields: Object.assign({}, functionDeclarationCommon, functionTypeAnnotationCommon, {
    body: {
      validate: (0, _utils.assertNodeType)("BlockStatement")
    }
  }),
  aliases: ["Scopable", "Function", "BlockParent", "FunctionParent", "Statement", "Pureish", "Declaration"]
});
(0, _utils.default)("FunctionExpression", {
  inherits: "FunctionDeclaration",
  aliases: ["Scopable", "Function", "BlockParent", "FunctionParent", "Expression", "Pureish"],
  fields: Object.assign({}, functionCommon, functionTypeAnnotationCommon, {
    id: {
      validate: (0, _utils.assertNodeType)("Identifier"),
      optional: true
    },
    body: {
      validate: (0, _utils.assertNodeType)("BlockStatement")
    }
  })
});
var patternLikeCommon = {
  typeAnnotation: {
    validate: (0, _utils.assertNodeType)("TypeAnnotation", "TSTypeAnnotation", "Noop"),
    optional: true
  },
  decorators: {
    validate: (0, _utils.chain)((0, _utils.assertValueType)("array"), (0, _utils.assertEach)((0, _utils.assertNodeType)("Decorator")))
  }
};
exports.patternLikeCommon = patternLikeCommon;
(0, _utils.default)("Identifier", {
  builder: ["name"],
  visitor: ["typeAnnotation"],
  aliases: ["Expression", "PatternLike", "LVal", "TSEntityName"],
  fields: Object.assign({}, patternLikeCommon, {
    name: {
      validate: (0, _utils.chain)(function (node, key, val) {
        if (!(0, _isValidIdentifier.default)(val)) {}
      }, (0, _utils.assertValueType)("string"))
    },
    optional: {
      validate: (0, _utils.assertValueType)("boolean"),
      optional: true
    }
  })
});
(0, _utils.default)("IfStatement", {
  visitor: ["test", "consequent", "alternate"],
  aliases: ["Statement", "Conditional"],
  fields: {
    test: {
      validate: (0, _utils.assertNodeType)("Expression")
    },
    consequent: {
      validate: (0, _utils.assertNodeType)("Statement")
    },
    alternate: {
      optional: true,
      validate: (0, _utils.assertNodeType)("Statement")
    }
  }
});
(0, _utils.default)("LabeledStatement", {
  visitor: ["label", "body"],
  aliases: ["Statement"],
  fields: {
    label: {
      validate: (0, _utils.assertNodeType)("Identifier")
    },
    body: {
      validate: (0, _utils.assertNodeType)("Statement")
    }
  }
});
(0, _utils.default)("StringLiteral", {
  builder: ["value"],
  fields: {
    value: {
      validate: (0, _utils.assertValueType)("string")
    }
  },
  aliases: ["Expression", "Pureish", "Literal", "Immutable"]
});
(0, _utils.default)("NumericLiteral", {
  builder: ["value"],
  deprecatedAlias: "NumberLiteral",
  fields: {
    value: {
      validate: (0, _utils.assertValueType)("number")
    }
  },
  aliases: ["Expression", "Pureish", "Literal", "Immutable"]
});
(0, _utils.default)("NullLiteral", {
  aliases: ["Expression", "Pureish", "Literal", "Immutable"]
});
(0, _utils.default)("BooleanLiteral", {
  builder: ["value"],
  fields: {
    value: {
      validate: (0, _utils.assertValueType)("boolean")
    }
  },
  aliases: ["Expression", "Pureish", "Literal", "Immutable"]
});
(0, _utils.default)("RegExpLiteral", {
  builder: ["pattern", "flags"],
  deprecatedAlias: "RegexLiteral",
  aliases: ["Expression", "Literal"],
  fields: {
    pattern: {
      validate: (0, _utils.assertValueType)("string")
    },
    flags: {
      validate: (0, _utils.assertValueType)("string"),
      default: ""
    }
  }
});
(0, _utils.default)("LogicalExpression", {
  builder: ["operator", "left", "right"],
  visitor: ["left", "right"],
  aliases: ["Binary", "Expression"],
  fields: {
    operator: {
      validate: _utils.assertOneOf.apply(void 0, _constants.LOGICAL_OPERATORS)
    },
    left: {
      validate: (0, _utils.assertNodeType)("Expression")
    },
    right: {
      validate: (0, _utils.assertNodeType)("Expression")
    }
  }
});
(0, _utils.default)("MemberExpression", {
  builder: ["object", "property", "computed", "optional"],
  visitor: ["object", "property"],
  aliases: ["Expression", "LVal"],
  fields: {
    object: {
      validate: (0, _utils.assertNodeType)("Expression")
    },
    property: {
      validate: function () {
        var normal = (0, _utils.assertNodeType)("Identifier");
        var computed = (0, _utils.assertNodeType)("Expression");
        return function (node, key, val) {
          var validator = node.computed ? computed : normal;
          validator(node, key, val);
        };
      }()
    },
    computed: {
      default: false
    },
    optional: {
      validate: (0, _utils.assertOneOf)(true, false),
      optional: true
    }
  }
});
(0, _utils.default)("NewExpression", {
  inherits: "CallExpression"
});
(0, _utils.default)("Program", {
  visitor: ["directives", "body"],
  builder: ["body", "directives", "sourceType"],
  fields: {
    sourceFile: {
      validate: (0, _utils.assertValueType)("string")
    },
    sourceType: {
      validate: (0, _utils.assertOneOf)("script", "module"),
      default: "script"
    },
    directives: {
      validate: (0, _utils.chain)((0, _utils.assertValueType)("array"), (0, _utils.assertEach)((0, _utils.assertNodeType)("Directive"))),
      default: []
    },
    body: {
      validate: (0, _utils.chain)((0, _utils.assertValueType)("array"), (0, _utils.assertEach)((0, _utils.assertNodeType)("Statement")))
    }
  },
  aliases: ["Scopable", "BlockParent", "Block"]
});
(0, _utils.default)("ObjectExpression", {
  visitor: ["properties"],
  aliases: ["Expression"],
  fields: {
    properties: {
      validate: (0, _utils.chain)((0, _utils.assertValueType)("array"), (0, _utils.assertEach)((0, _utils.assertNodeType)("ObjectMethod", "ObjectProperty", "SpreadElement")))
    }
  }
});
(0, _utils.default)("ObjectMethod", {
  builder: ["kind", "key", "params", "body", "computed"],
  fields: Object.assign({}, functionCommon, functionTypeAnnotationCommon, {
    kind: {
      validate: (0, _utils.chain)((0, _utils.assertValueType)("string"), (0, _utils.assertOneOf)("method", "get", "set")),
      default: "method"
    },
    computed: {
      validate: (0, _utils.assertValueType)("boolean"),
      default: false
    },
    key: {
      validate: function () {
        var normal = (0, _utils.assertNodeType)("Identifier", "StringLiteral", "NumericLiteral");
        var computed = (0, _utils.assertNodeType)("Expression");
        return function (node, key, val) {
          var validator = node.computed ? computed : normal;
          validator(node, key, val);
        };
      }()
    },
    decorators: {
      validate: (0, _utils.chain)((0, _utils.assertValueType)("array"), (0, _utils.assertEach)((0, _utils.assertNodeType)("Decorator")))
    },
    body: {
      validate: (0, _utils.assertNodeType)("BlockStatement")
    }
  }),
  visitor: ["key", "params", "body", "decorators", "returnType", "typeParameters"],
  aliases: ["UserWhitespacable", "Function", "Scopable", "BlockParent", "FunctionParent", "Method", "ObjectMember"]
});
(0, _utils.default)("ObjectProperty", {
  builder: ["key", "value", "computed", "shorthand", "decorators"],
  fields: {
    computed: {
      validate: (0, _utils.assertValueType)("boolean"),
      default: false
    },
    key: {
      validate: function () {
        var normal = (0, _utils.assertNodeType)("Identifier", "StringLiteral", "NumericLiteral");
        var computed = (0, _utils.assertNodeType)("Expression");
        return function (node, key, val) {
          var validator = node.computed ? computed : normal;
          validator(node, key, val);
        };
      }()
    },
    value: {
      validate: (0, _utils.assertNodeType)("Expression", "PatternLike")
    },
    shorthand: {
      validate: (0, _utils.assertValueType)("boolean"),
      default: false
    },
    decorators: {
      validate: (0, _utils.chain)((0, _utils.assertValueType)("array"), (0, _utils.assertEach)((0, _utils.assertNodeType)("Decorator"))),
      optional: true
    }
  },
  visitor: ["key", "value", "decorators"],
  aliases: ["UserWhitespacable", "Property", "ObjectMember"]
});
(0, _utils.default)("RestElement", {
  visitor: ["argument", "typeAnnotation"],
  builder: ["argument"],
  aliases: ["LVal", "PatternLike"],
  deprecatedAlias: "RestProperty",
  fields: Object.assign({}, patternLikeCommon, {
    argument: {
      validate: (0, _utils.assertNodeType)("LVal")
    }
  })
});
(0, _utils.default)("ReturnStatement", {
  visitor: ["argument"],
  aliases: ["Statement", "Terminatorless", "CompletionStatement"],
  fields: {
    argument: {
      validate: (0, _utils.assertNodeType)("Expression"),
      optional: true
    }
  }
});
(0, _utils.default)("SequenceExpression", {
  visitor: ["expressions"],
  fields: {
    expressions: {
      validate: (0, _utils.chain)((0, _utils.assertValueType)("array"), (0, _utils.assertEach)((0, _utils.assertNodeType)("Expression")))
    }
  },
  aliases: ["Expression"]
});
(0, _utils.default)("SwitchCase", {
  visitor: ["test", "consequent"],
  fields: {
    test: {
      validate: (0, _utils.assertNodeType)("Expression"),
      optional: true
    },
    consequent: {
      validate: (0, _utils.chain)((0, _utils.assertValueType)("array"), (0, _utils.assertEach)((0, _utils.assertNodeType)("Statement")))
    }
  }
});
(0, _utils.default)("SwitchStatement", {
  visitor: ["discriminant", "cases"],
  aliases: ["Statement", "BlockParent", "Scopable"],
  fields: {
    discriminant: {
      validate: (0, _utils.assertNodeType)("Expression")
    },
    cases: {
      validate: (0, _utils.chain)((0, _utils.assertValueType)("array"), (0, _utils.assertEach)((0, _utils.assertNodeType)("SwitchCase")))
    }
  }
});
(0, _utils.default)("ThisExpression", {
  aliases: ["Expression"]
});
(0, _utils.default)("ThrowStatement", {
  visitor: ["argument"],
  aliases: ["Statement", "Terminatorless", "CompletionStatement"],
  fields: {
    argument: {
      validate: (0, _utils.assertNodeType)("Expression")
    }
  }
});
(0, _utils.default)("TryStatement", {
  visitor: ["block", "handler", "finalizer"],
  aliases: ["Statement"],
  fields: {
    block: {
      validate: (0, _utils.assertNodeType)("BlockStatement")
    },
    handler: {
      optional: true,
      validate: (0, _utils.assertNodeType)("CatchClause")
    },
    finalizer: {
      optional: true,
      validate: (0, _utils.assertNodeType)("BlockStatement")
    }
  }
});
(0, _utils.default)("UnaryExpression", {
  builder: ["operator", "argument", "prefix"],
  fields: {
    prefix: {
      default: true
    },
    argument: {
      validate: (0, _utils.assertNodeType)("Expression")
    },
    operator: {
      validate: _utils.assertOneOf.apply(void 0, _constants.UNARY_OPERATORS)
    }
  },
  visitor: ["argument"],
  aliases: ["UnaryLike", "Expression"]
});
(0, _utils.default)("UpdateExpression", {
  builder: ["operator", "argument", "prefix"],
  fields: {
    prefix: {
      default: false
    },
    argument: {
      validate: (0, _utils.assertNodeType)("Expression")
    },
    operator: {
      validate: _utils.assertOneOf.apply(void 0, _constants.UPDATE_OPERATORS)
    }
  },
  visitor: ["argument"],
  aliases: ["Expression"]
});
(0, _utils.default)("VariableDeclaration", {
  builder: ["kind", "declarations"],
  visitor: ["declarations"],
  aliases: ["Statement", "Declaration"],
  fields: {
    declare: {
      validate: (0, _utils.assertValueType)("boolean"),
      optional: true
    },
    kind: {
      validate: (0, _utils.chain)((0, _utils.assertValueType)("string"), (0, _utils.assertOneOf)("var", "let", "const"))
    },
    declarations: {
      validate: (0, _utils.chain)((0, _utils.assertValueType)("array"), (0, _utils.assertEach)((0, _utils.assertNodeType)("VariableDeclarator")))
    }
  }
});
(0, _utils.default)("VariableDeclarator", {
  visitor: ["id", "init"],
  fields: {
    id: {
      validate: (0, _utils.assertNodeType)("LVal")
    },
    init: {
      optional: true,
      validate: (0, _utils.assertNodeType)("Expression")
    }
  }
});
(0, _utils.default)("WhileStatement", {
  visitor: ["test", "body"],
  aliases: ["Statement", "BlockParent", "Loop", "While", "Scopable"],
  fields: {
    test: {
      validate: (0, _utils.assertNodeType)("Expression")
    },
    body: {
      validate: (0, _utils.assertNodeType)("BlockStatement", "Statement")
    }
  }
});
(0, _utils.default)("WithStatement", {
  visitor: ["object", "body"],
  aliases: ["Statement"],
  fields: {
    object: {
      validate: (0, _utils.assertNodeType)("Expression")
    },
    body: {
      validate: (0, _utils.assertNodeType)("BlockStatement", "Statement")
    }
  }
});

/***/ }),

/***/ 2266:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.classMethodOrDeclareMethodCommon = exports.classMethodOrPropertyCommon = void 0;

var _utils = _interopRequireWildcard(__webpack_require__(2258));

var _core = __webpack_require__(2265);

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }

(0, _utils.default)("AssignmentPattern", {
  visitor: ["left", "right"],
  builder: ["left", "right"],
  aliases: ["Pattern", "PatternLike", "LVal"],
  fields: Object.assign({}, _core.patternLikeCommon, {
    left: {
      validate: (0, _utils.assertNodeType)("Identifier", "ObjectPattern", "ArrayPattern")
    },
    right: {
      validate: (0, _utils.assertNodeType)("Expression")
    },
    decorators: {
      validate: (0, _utils.chain)((0, _utils.assertValueType)("array"), (0, _utils.assertEach)((0, _utils.assertNodeType)("Decorator")))
    }
  })
});
(0, _utils.default)("ArrayPattern", {
  visitor: ["elements", "typeAnnotation"],
  builder: ["elements"],
  aliases: ["Pattern", "PatternLike", "LVal"],
  fields: Object.assign({}, _core.patternLikeCommon, {
    elements: {
      validate: (0, _utils.chain)((0, _utils.assertValueType)("array"), (0, _utils.assertEach)((0, _utils.assertNodeType)("PatternLike")))
    },
    decorators: {
      validate: (0, _utils.chain)((0, _utils.assertValueType)("array"), (0, _utils.assertEach)((0, _utils.assertNodeType)("Decorator")))
    }
  })
});
(0, _utils.default)("ArrowFunctionExpression", {
  builder: ["params", "body", "async"],
  visitor: ["params", "body", "returnType", "typeParameters"],
  aliases: ["Scopable", "Function", "BlockParent", "FunctionParent", "Expression", "Pureish"],
  fields: Object.assign({}, _core.functionCommon, _core.functionTypeAnnotationCommon, {
    expression: {
      validate: (0, _utils.assertValueType)("boolean")
    },
    body: {
      validate: (0, _utils.assertNodeType)("BlockStatement", "Expression")
    }
  })
});
(0, _utils.default)("ClassBody", {
  visitor: ["body"],
  fields: {
    body: {
      validate: (0, _utils.chain)((0, _utils.assertValueType)("array"), (0, _utils.assertEach)((0, _utils.assertNodeType)("ClassMethod", "ClassProperty", "TSDeclareMethod", "TSIndexSignature")))
    }
  }
});
var classCommon = {
  typeParameters: {
    validate: (0, _utils.assertNodeType)("TypeParameterDeclaration", "TSTypeParameterDeclaration", "Noop"),
    optional: true
  },
  body: {
    validate: (0, _utils.assertNodeType)("ClassBody")
  },
  superClass: {
    optional: true,
    validate: (0, _utils.assertNodeType)("Expression")
  },
  superTypeParameters: {
    validate: (0, _utils.assertNodeType)("TypeParameterInstantiation", "TSTypeParameterInstantiation"),
    optional: true
  },
  implements: {
    validate: (0, _utils.chain)((0, _utils.assertValueType)("array"), (0, _utils.assertEach)((0, _utils.assertNodeType)("TSExpressionWithTypeArguments", "ClassImplements"))),
    optional: true
  }
};
(0, _utils.default)("ClassDeclaration", {
  builder: ["id", "superClass", "body", "decorators"],
  visitor: ["id", "body", "superClass", "mixins", "typeParameters", "superTypeParameters", "implements", "decorators"],
  aliases: ["Scopable", "Class", "Statement", "Declaration", "Pureish"],
  fields: Object.assign({}, classCommon, {
    declare: {
      validate: (0, _utils.assertValueType)("boolean"),
      optional: true
    },
    abstract: {
      validate: (0, _utils.assertValueType)("boolean"),
      optional: true
    },
    id: {
      validate: (0, _utils.assertNodeType)("Identifier"),
      optional: true
    },
    decorators: {
      validate: (0, _utils.chain)((0, _utils.assertValueType)("array"), (0, _utils.assertEach)((0, _utils.assertNodeType)("Decorator"))),
      optional: true
    }
  })
});
(0, _utils.default)("ClassExpression", {
  inherits: "ClassDeclaration",
  aliases: ["Scopable", "Class", "Expression", "Pureish"],
  fields: Object.assign({}, classCommon, {
    id: {
      optional: true,
      validate: (0, _utils.assertNodeType)("Identifier")
    },
    body: {
      validate: (0, _utils.assertNodeType)("ClassBody")
    },
    superClass: {
      optional: true,
      validate: (0, _utils.assertNodeType)("Expression")
    },
    decorators: {
      validate: (0, _utils.chain)((0, _utils.assertValueType)("array"), (0, _utils.assertEach)((0, _utils.assertNodeType)("Decorator"))),
      optional: true
    }
  })
});
(0, _utils.default)("ExportAllDeclaration", {
  visitor: ["source"],
  aliases: ["Statement", "Declaration", "ModuleDeclaration", "ExportDeclaration"],
  fields: {
    source: {
      validate: (0, _utils.assertNodeType)("StringLiteral")
    }
  }
});
(0, _utils.default)("ExportDefaultDeclaration", {
  visitor: ["declaration"],
  aliases: ["Statement", "Declaration", "ModuleDeclaration", "ExportDeclaration"],
  fields: {
    declaration: {
      validate: (0, _utils.assertNodeType)("FunctionDeclaration", "TSDeclareFunction", "ClassDeclaration", "Expression")
    }
  }
});
(0, _utils.default)("ExportNamedDeclaration", {
  visitor: ["declaration", "specifiers", "source"],
  aliases: ["Statement", "Declaration", "ModuleDeclaration", "ExportDeclaration"],
  fields: {
    declaration: {
      validate: (0, _utils.assertNodeType)("Declaration"),
      optional: true
    },
    specifiers: {
      validate: (0, _utils.chain)((0, _utils.assertValueType)("array"), (0, _utils.assertEach)((0, _utils.assertNodeType)("ExportSpecifier", "ExportDefaultSpecifier", "ExportNamespaceSpecifier")))
    },
    source: {
      validate: (0, _utils.assertNodeType)("StringLiteral"),
      optional: true
    }
  }
});
(0, _utils.default)("ExportSpecifier", {
  visitor: ["local", "exported"],
  aliases: ["ModuleSpecifier"],
  fields: {
    local: {
      validate: (0, _utils.assertNodeType)("Identifier")
    },
    exported: {
      validate: (0, _utils.assertNodeType)("Identifier")
    }
  }
});
(0, _utils.default)("ForOfStatement", {
  visitor: ["left", "right", "body"],
  aliases: ["Scopable", "Statement", "For", "BlockParent", "Loop", "ForXStatement"],
  fields: {
    left: {
      validate: (0, _utils.assertNodeType)("VariableDeclaration", "LVal")
    },
    right: {
      validate: (0, _utils.assertNodeType)("Expression")
    },
    body: {
      validate: (0, _utils.assertNodeType)("Statement")
    },
    await: {
      default: false,
      validate: (0, _utils.assertValueType)("boolean")
    }
  }
});
(0, _utils.default)("ImportDeclaration", {
  visitor: ["specifiers", "source"],
  aliases: ["Statement", "Declaration", "ModuleDeclaration"],
  fields: {
    specifiers: {
      validate: (0, _utils.chain)((0, _utils.assertValueType)("array"), (0, _utils.assertEach)((0, _utils.assertNodeType)("ImportSpecifier", "ImportDefaultSpecifier", "ImportNamespaceSpecifier")))
    },
    source: {
      validate: (0, _utils.assertNodeType)("StringLiteral")
    }
  }
});
(0, _utils.default)("ImportDefaultSpecifier", {
  visitor: ["local"],
  aliases: ["ModuleSpecifier"],
  fields: {
    local: {
      validate: (0, _utils.assertNodeType)("Identifier")
    }
  }
});
(0, _utils.default)("ImportNamespaceSpecifier", {
  visitor: ["local"],
  aliases: ["ModuleSpecifier"],
  fields: {
    local: {
      validate: (0, _utils.assertNodeType)("Identifier")
    }
  }
});
(0, _utils.default)("ImportSpecifier", {
  visitor: ["local", "imported"],
  aliases: ["ModuleSpecifier"],
  fields: {
    local: {
      validate: (0, _utils.assertNodeType)("Identifier")
    },
    imported: {
      validate: (0, _utils.assertNodeType)("Identifier")
    },
    importKind: {
      validate: (0, _utils.assertOneOf)(null, "type", "typeof")
    }
  }
});
(0, _utils.default)("MetaProperty", {
  visitor: ["meta", "property"],
  aliases: ["Expression"],
  fields: {
    meta: {
      validate: (0, _utils.assertNodeType)("Identifier")
    },
    property: {
      validate: (0, _utils.assertNodeType)("Identifier")
    }
  }
});
var classMethodOrPropertyCommon = {
  abstract: {
    validate: (0, _utils.assertValueType)("boolean"),
    optional: true
  },
  accessibility: {
    validate: (0, _utils.chain)((0, _utils.assertValueType)("string"), (0, _utils.assertOneOf)("public", "private", "protected")),
    optional: true
  },
  static: {
    validate: (0, _utils.assertValueType)("boolean"),
    optional: true
  },
  computed: {
    default: false,
    validate: (0, _utils.assertValueType)("boolean")
  },
  optional: {
    validate: (0, _utils.assertValueType)("boolean"),
    optional: true
  },
  key: {
    validate: (0, _utils.chain)(function () {
      var normal = (0, _utils.assertNodeType)("Identifier", "StringLiteral", "NumericLiteral");
      var computed = (0, _utils.assertNodeType)("Expression");
      return function (node, key, val) {
        var validator = node.computed ? computed : normal;
        validator(node, key, val);
      };
    }(), (0, _utils.assertNodeType)("Identifier", "StringLiteral", "NumericLiteral", "Expression"))
  }
};
exports.classMethodOrPropertyCommon = classMethodOrPropertyCommon;
var classMethodOrDeclareMethodCommon = Object.assign({}, _core.functionCommon, classMethodOrPropertyCommon, {
  kind: {
    validate: (0, _utils.chain)((0, _utils.assertValueType)("string"), (0, _utils.assertOneOf)("get", "set", "method", "constructor")),
    default: "method"
  },
  access: {
    validate: (0, _utils.chain)((0, _utils.assertValueType)("string"), (0, _utils.assertOneOf)("public", "private", "protected")),
    optional: true
  },
  decorators: {
    validate: (0, _utils.chain)((0, _utils.assertValueType)("array"), (0, _utils.assertEach)((0, _utils.assertNodeType)("Decorator"))),
    optional: true
  }
});
exports.classMethodOrDeclareMethodCommon = classMethodOrDeclareMethodCommon;
(0, _utils.default)("ClassMethod", {
  aliases: ["Function", "Scopable", "BlockParent", "FunctionParent", "Method"],
  builder: ["kind", "key", "params", "body", "computed", "static"],
  visitor: ["key", "params", "body", "decorators", "returnType", "typeParameters"],
  fields: Object.assign({}, classMethodOrDeclareMethodCommon, _core.functionTypeAnnotationCommon, {
    body: {
      validate: (0, _utils.assertNodeType)("BlockStatement")
    }
  })
});
(0, _utils.default)("ObjectPattern", {
  visitor: ["properties", "typeAnnotation"],
  builder: ["properties"],
  aliases: ["Pattern", "PatternLike", "LVal"],
  fields: Object.assign({}, _core.patternLikeCommon, {
    properties: {
      validate: (0, _utils.chain)((0, _utils.assertValueType)("array"), (0, _utils.assertEach)((0, _utils.assertNodeType)("RestElement", "ObjectProperty")))
    }
  })
});
(0, _utils.default)("SpreadElement", {
  visitor: ["argument"],
  aliases: ["UnaryLike"],
  deprecatedAlias: "SpreadProperty",
  fields: {
    argument: {
      validate: (0, _utils.assertNodeType)("Expression")
    }
  }
});
(0, _utils.default)("Super", {
  aliases: ["Expression"]
});
(0, _utils.default)("TaggedTemplateExpression", {
  visitor: ["tag", "quasi"],
  aliases: ["Expression"],
  fields: {
    tag: {
      validate: (0, _utils.assertNodeType)("Expression")
    },
    quasi: {
      validate: (0, _utils.assertNodeType)("TemplateLiteral")
    }
  }
});
(0, _utils.default)("TemplateElement", {
  builder: ["value", "tail"],
  fields: {
    value: {},
    tail: {
      validate: (0, _utils.assertValueType)("boolean"),
      default: false
    }
  }
});
(0, _utils.default)("TemplateLiteral", {
  visitor: ["quasis", "expressions"],
  aliases: ["Expression", "Literal"],
  fields: {
    quasis: {
      validate: (0, _utils.chain)((0, _utils.assertValueType)("array"), (0, _utils.assertEach)((0, _utils.assertNodeType)("TemplateElement")))
    },
    expressions: {
      validate: (0, _utils.chain)((0, _utils.assertValueType)("array"), (0, _utils.assertEach)((0, _utils.assertNodeType)("Expression")))
    }
  }
});
(0, _utils.default)("YieldExpression", {
  builder: ["argument", "delegate"],
  visitor: ["argument"],
  aliases: ["Expression", "Terminatorless"],
  fields: {
    delegate: {
      validate: (0, _utils.assertValueType)("boolean"),
      default: false
    },
    argument: {
      optional: true,
      validate: (0, _utils.assertNodeType)("Expression")
    }
  }
});

/***/ }),

/***/ 2267:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = inherit;

var _uniq = _interopRequireDefault(__webpack_require__(561));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function inherit(key, child, parent) {
  if (child && parent) {
    child[key] = (0, _uniq.default)([].concat(child[key], parent[key]).filter(Boolean));
  }
}

/***/ }),

/***/ 2268:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
var _exportNames = {
  assertNode: true,
  createTypeAnnotationBasedOnTypeof: true,
  createUnionTypeAnnotation: true,
  cloneNode: true,
  clone: true,
  cloneDeep: true,
  cloneWithoutLoc: true,
  addComment: true,
  addComments: true,
  inheritInnerComments: true,
  inheritLeadingComments: true,
  inheritsComments: true,
  inheritTrailingComments: true,
  removeComments: true,
  ensureBlock: true,
  toBindingIdentifierName: true,
  toBlock: true,
  toComputedKey: true,
  toExpression: true,
  toIdentifier: true,
  toKeyAlias: true,
  toSequenceExpression: true,
  toStatement: true,
  valueToNode: true,
  appendToMemberExpression: true,
  inherits: true,
  prependToMemberExpression: true,
  removeProperties: true,
  removePropertiesDeep: true,
  removeTypeDuplicates: true,
  getBindingIdentifiers: true,
  getOuterBindingIdentifiers: true,
  traverse: true,
  traverseFast: true,
  shallowEqual: true,
  is: true,
  isBinding: true,
  isBlockScoped: true,
  isImmutable: true,
  isLet: true,
  isNode: true,
  isNodesEquivalent: true,
  isReferenced: true,
  isScope: true,
  isSpecifierDefault: true,
  isType: true,
  isValidES3Identifier: true,
  isValidIdentifier: true,
  isVar: true,
  matchesPattern: true,
  validate: true,
  buildMatchMemberExpression: true,
  react: true
};
exports.react = exports.buildMatchMemberExpression = exports.validate = exports.matchesPattern = exports.isVar = exports.isValidIdentifier = exports.isValidES3Identifier = exports.isType = exports.isSpecifierDefault = exports.isScope = exports.isReferenced = exports.isNodesEquivalent = exports.isNode = exports.isLet = exports.isImmutable = exports.isBlockScoped = exports.isBinding = exports.is = exports.shallowEqual = exports.traverseFast = exports.traverse = exports.getOuterBindingIdentifiers = exports.getBindingIdentifiers = exports.removeTypeDuplicates = exports.removePropertiesDeep = exports.removeProperties = exports.prependToMemberExpression = exports.inherits = exports.appendToMemberExpression = exports.valueToNode = exports.toStatement = exports.toSequenceExpression = exports.toKeyAlias = exports.toIdentifier = exports.toExpression = exports.toComputedKey = exports.toBlock = exports.toBindingIdentifierName = exports.ensureBlock = exports.removeComments = exports.inheritTrailingComments = exports.inheritsComments = exports.inheritLeadingComments = exports.inheritInnerComments = exports.addComments = exports.addComment = exports.cloneWithoutLoc = exports.cloneDeep = exports.clone = exports.cloneNode = exports.createUnionTypeAnnotation = exports.createTypeAnnotationBasedOnTypeof = exports.assertNode = void 0;

var _isReactComponent = _interopRequireDefault(__webpack_require__(2295));

var _isCompatTag = _interopRequireDefault(__webpack_require__(2301));

var _buildChildren = _interopRequireDefault(__webpack_require__(2302));

var _assertNode = _interopRequireDefault(__webpack_require__(2305));

exports.assertNode = _assertNode.default;

var _generated = __webpack_require__(2306);

Object.keys(_generated).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
  exports[key] = _generated[key];
});

var _createTypeAnnotationBasedOnTypeof = _interopRequireDefault(__webpack_require__(2307));

exports.createTypeAnnotationBasedOnTypeof = _createTypeAnnotationBasedOnTypeof.default;

var _createUnionTypeAnnotation = _interopRequireDefault(__webpack_require__(2308));

exports.createUnionTypeAnnotation = _createUnionTypeAnnotation.default;

var _generated2 = __webpack_require__(2256);

Object.keys(_generated2).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
  exports[key] = _generated2[key];
});

var _cloneNode = _interopRequireDefault(__webpack_require__(2261));

exports.cloneNode = _cloneNode.default;

var _clone = _interopRequireDefault(__webpack_require__(2275));

exports.clone = _clone.default;

var _cloneDeep = _interopRequireDefault(__webpack_require__(2309));

exports.cloneDeep = _cloneDeep.default;

var _cloneWithoutLoc = _interopRequireDefault(__webpack_require__(2310));

exports.cloneWithoutLoc = _cloneWithoutLoc.default;

var _addComment = _interopRequireDefault(__webpack_require__(2311));

exports.addComment = _addComment.default;

var _addComments = _interopRequireDefault(__webpack_require__(2276));

exports.addComments = _addComments.default;

var _inheritInnerComments = _interopRequireDefault(__webpack_require__(2277));

exports.inheritInnerComments = _inheritInnerComments.default;

var _inheritLeadingComments = _interopRequireDefault(__webpack_require__(2278));

exports.inheritLeadingComments = _inheritLeadingComments.default;

var _inheritsComments = _interopRequireDefault(__webpack_require__(2279));

exports.inheritsComments = _inheritsComments.default;

var _inheritTrailingComments = _interopRequireDefault(__webpack_require__(2280));

exports.inheritTrailingComments = _inheritTrailingComments.default;

var _removeComments = _interopRequireDefault(__webpack_require__(2312));

exports.removeComments = _removeComments.default;

var _generated3 = __webpack_require__(2313);

Object.keys(_generated3).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
  exports[key] = _generated3[key];
});

var _constants = __webpack_require__(2259);

Object.keys(_constants).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
  exports[key] = _constants[key];
});

var _ensureBlock = _interopRequireDefault(__webpack_require__(2314));

exports.ensureBlock = _ensureBlock.default;

var _toBindingIdentifierName = _interopRequireDefault(__webpack_require__(2315));

exports.toBindingIdentifierName = _toBindingIdentifierName.default;

var _toBlock = _interopRequireDefault(__webpack_require__(2281));

exports.toBlock = _toBlock.default;

var _toComputedKey = _interopRequireDefault(__webpack_require__(2316));

exports.toComputedKey = _toComputedKey.default;

var _toExpression = _interopRequireDefault(__webpack_require__(2317));

exports.toExpression = _toExpression.default;

var _toIdentifier = _interopRequireDefault(__webpack_require__(2282));

exports.toIdentifier = _toIdentifier.default;

var _toKeyAlias = _interopRequireDefault(__webpack_require__(2318));

exports.toKeyAlias = _toKeyAlias.default;

var _toSequenceExpression = _interopRequireDefault(__webpack_require__(2319));

exports.toSequenceExpression = _toSequenceExpression.default;

var _toStatement = _interopRequireDefault(__webpack_require__(2321));

exports.toStatement = _toStatement.default;

var _valueToNode = _interopRequireDefault(__webpack_require__(2322));

exports.valueToNode = _valueToNode.default;

var _definitions = __webpack_require__(2257);

Object.keys(_definitions).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
  exports[key] = _definitions[key];
});

var _appendToMemberExpression = _interopRequireDefault(__webpack_require__(2323));

exports.appendToMemberExpression = _appendToMemberExpression.default;

var _inherits = _interopRequireDefault(__webpack_require__(2324));

exports.inherits = _inherits.default;

var _prependToMemberExpression = _interopRequireDefault(__webpack_require__(2325));

exports.prependToMemberExpression = _prependToMemberExpression.default;

var _removeProperties = _interopRequireDefault(__webpack_require__(2285));

exports.removeProperties = _removeProperties.default;

var _removePropertiesDeep = _interopRequireDefault(__webpack_require__(2283));

exports.removePropertiesDeep = _removePropertiesDeep.default;

var _removeTypeDuplicates = _interopRequireDefault(__webpack_require__(2274));

exports.removeTypeDuplicates = _removeTypeDuplicates.default;

var _getBindingIdentifiers = _interopRequireDefault(__webpack_require__(2263));

exports.getBindingIdentifiers = _getBindingIdentifiers.default;

var _getOuterBindingIdentifiers = _interopRequireDefault(__webpack_require__(2326));

exports.getOuterBindingIdentifiers = _getOuterBindingIdentifiers.default;

var _traverse = _interopRequireDefault(__webpack_require__(2327));

exports.traverse = _traverse.default;

var _traverseFast = _interopRequireDefault(__webpack_require__(2284));

exports.traverseFast = _traverseFast.default;

var _shallowEqual = _interopRequireDefault(__webpack_require__(2271));

exports.shallowEqual = _shallowEqual.default;

var _is = _interopRequireDefault(__webpack_require__(2262));

exports.is = _is.default;

var _isBinding = _interopRequireDefault(__webpack_require__(2328));

exports.isBinding = _isBinding.default;

var _isBlockScoped = _interopRequireDefault(__webpack_require__(2329));

exports.isBlockScoped = _isBlockScoped.default;

var _isImmutable = _interopRequireDefault(__webpack_require__(2330));

exports.isImmutable = _isImmutable.default;

var _isLet = _interopRequireDefault(__webpack_require__(2286));

exports.isLet = _isLet.default;

var _isNode = _interopRequireDefault(__webpack_require__(2273));

exports.isNode = _isNode.default;

var _isNodesEquivalent = _interopRequireDefault(__webpack_require__(2331));

exports.isNodesEquivalent = _isNodesEquivalent.default;

var _isReferenced = _interopRequireDefault(__webpack_require__(2332));

exports.isReferenced = _isReferenced.default;

var _isScope = _interopRequireDefault(__webpack_require__(2333));

exports.isScope = _isScope.default;

var _isSpecifierDefault = _interopRequireDefault(__webpack_require__(2334));

exports.isSpecifierDefault = _isSpecifierDefault.default;

var _isType = _interopRequireDefault(__webpack_require__(2264));

exports.isType = _isType.default;

var _isValidES3Identifier = _interopRequireDefault(__webpack_require__(2335));

exports.isValidES3Identifier = _isValidES3Identifier.default;

var _isValidIdentifier = _interopRequireDefault(__webpack_require__(2260));

exports.isValidIdentifier = _isValidIdentifier.default;

var _isVar = _interopRequireDefault(__webpack_require__(2336));

exports.isVar = _isVar.default;

var _matchesPattern = _interopRequireDefault(__webpack_require__(2270));

exports.matchesPattern = _matchesPattern.default;

var _validate = _interopRequireDefault(__webpack_require__(2272));

exports.validate = _validate.default;

var _buildMatchMemberExpression = _interopRequireDefault(__webpack_require__(2269));

exports.buildMatchMemberExpression = _buildMatchMemberExpression.default;

var _generated4 = __webpack_require__(2255);

Object.keys(_generated4).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
  exports[key] = _generated4[key];
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

var react = {
  isReactComponent: _isReactComponent.default,
  isCompatTag: _isCompatTag.default,
  buildChildren: _buildChildren.default
};
exports.react = react;

/***/ }),

/***/ 2269:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = buildMatchMemberExpression;

var _matchesPattern = _interopRequireDefault(__webpack_require__(2270));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function buildMatchMemberExpression(match, allowPartial) {
  var parts = match.split(".");
  return function (member) {
    return (0, _matchesPattern.default)(member, parts, allowPartial);
  };
}

/***/ }),

/***/ 2270:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = matchesPattern;

var _generated = __webpack_require__(2255);

function matchesPattern(member, match, allowPartial) {
  if (!(0, _generated.isMemberExpression)(member)) return false;
  var parts = Array.isArray(match) ? match : match.split(".");
  var nodes = [];
  var node;

  for (node = member; (0, _generated.isMemberExpression)(node); node = node.object) {
    nodes.push(node.property);
  }

  nodes.push(node);
  if (nodes.length < parts.length) return false;
  if (!allowPartial && nodes.length > parts.length) return false;

  for (var i = 0, j = nodes.length - 1; i < parts.length; i++, j--) {
    var _node = nodes[j];
    var value = void 0;

    if ((0, _generated.isIdentifier)(_node)) {
      value = _node.name;
    } else if ((0, _generated.isStringLiteral)(_node)) {
      value = _node.value;
    } else {
      return false;
    }

    if (parts[i] !== value) return false;
  }

  return true;
}

/***/ }),

/***/ 2271:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = shallowEqual;

function shallowEqual(actual, expected) {
  var keys = Object.keys(expected);
  var _arr = keys;

  for (var _i = 0; _i < _arr.length; _i++) {
    var key = _arr[_i];

    if (actual[key] !== expected[key]) {
      return false;
    }
  }

  return true;
}

/***/ }),

/***/ 2272:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = validate;

var _definitions = __webpack_require__(2257);

function validate(node, key, val) {
  if (!node) return;
  var fields = _definitions.NODE_FIELDS[node.type];
  if (!fields) return;
  var field = fields[key];
  if (!field || !field.validate) return;
  if (field.optional && val == null) return;
  field.validate(node, key, val);
}

/***/ }),

/***/ 2273:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = isNode;

var _definitions = __webpack_require__(2257);

function isNode(node) {
  return !!(node && _definitions.VISITOR_KEYS[node.type]);
}

/***/ }),

/***/ 2274:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = removeTypeDuplicates;

var _generated = __webpack_require__(2255);

function removeTypeDuplicates(nodes) {
  var generics = {};
  var bases = {};
  var typeGroups = [];
  var types = [];

  for (var i = 0; i < nodes.length; i++) {
    var node = nodes[i];
    if (!node) continue;

    if (types.indexOf(node) >= 0) {
      continue;
    }

    if ((0, _generated.isAnyTypeAnnotation)(node)) {
      return [node];
    }

    if ((0, _generated.isFlowBaseAnnotation)(node)) {
      bases[node.type] = node;
      continue;
    }

    if ((0, _generated.isUnionTypeAnnotation)(node)) {
      if (typeGroups.indexOf(node.types) < 0) {
        nodes = nodes.concat(node.types);
        typeGroups.push(node.types);
      }

      continue;
    }

    if ((0, _generated.isGenericTypeAnnotation)(node)) {
      var name = node.id.name;

      if (generics[name]) {
        var existing = generics[name];

        if (existing.typeParameters) {
          if (node.typeParameters) {
            existing.typeParameters.params = removeTypeDuplicates(existing.typeParameters.params.concat(node.typeParameters.params));
          }
        } else {
          existing = node.typeParameters;
        }
      } else {
        generics[name] = node;
      }

      continue;
    }

    types.push(node);
  }

  for (var type in bases) {
    types.push(bases[type]);
  }

  for (var _name in generics) {
    types.push(generics[_name]);
  }

  return types;
}

/***/ }),

/***/ 2275:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = clone;

var _cloneNode = _interopRequireDefault(__webpack_require__(2261));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function clone(node) {
  return (0, _cloneNode.default)(node, false);
}

/***/ }),

/***/ 2276:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = addComments;

function addComments(node, type, comments) {
  if (!comments || !node) return node;
  var key = type + "Comments";

  if (node[key]) {
    if (type === "leading") {
      node[key] = comments.concat(node[key]);
    } else {
      node[key] = node[key].concat(comments);
    }
  } else {
    node[key] = comments;
  }

  return node;
}

/***/ }),

/***/ 2277:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = inheritInnerComments;

var _inherit = _interopRequireDefault(__webpack_require__(2267));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function inheritInnerComments(child, parent) {
  (0, _inherit.default)("innerComments", child, parent);
}

/***/ }),

/***/ 2278:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = inheritLeadingComments;

var _inherit = _interopRequireDefault(__webpack_require__(2267));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function inheritLeadingComments(child, parent) {
  (0, _inherit.default)("leadingComments", child, parent);
}

/***/ }),

/***/ 2279:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = inheritsComments;

var _inheritTrailingComments = _interopRequireDefault(__webpack_require__(2280));

var _inheritLeadingComments = _interopRequireDefault(__webpack_require__(2278));

var _inheritInnerComments = _interopRequireDefault(__webpack_require__(2277));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function inheritsComments(child, parent) {
  (0, _inheritTrailingComments.default)(child, parent);
  (0, _inheritLeadingComments.default)(child, parent);
  (0, _inheritInnerComments.default)(child, parent);
  return child;
}

/***/ }),

/***/ 2280:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = inheritTrailingComments;

var _inherit = _interopRequireDefault(__webpack_require__(2267));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function inheritTrailingComments(child, parent) {
  (0, _inherit.default)("trailingComments", child, parent);
}

/***/ }),

/***/ 2281:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = toBlock;

var _generated = __webpack_require__(2255);

var _generated2 = __webpack_require__(2256);

function toBlock(node, parent) {
  if ((0, _generated.isBlockStatement)(node)) {
    return node;
  }

  var blockNodes = [];

  if ((0, _generated.isEmptyStatement)(node)) {
    blockNodes = [];
  } else {
    if (!(0, _generated.isStatement)(node)) {
      if ((0, _generated.isFunction)(parent)) {
        node = (0, _generated2.returnStatement)(node);
      } else {
        node = (0, _generated2.expressionStatement)(node);
      }
    }

    blockNodes = [node];
  }

  return (0, _generated2.blockStatement)(blockNodes);
}

/***/ }),

/***/ 2282:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = toIdentifier;

var _isValidIdentifier = _interopRequireDefault(__webpack_require__(2260));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function toIdentifier(name) {
  name = name + "";
  name = name.replace(/[^a-zA-Z0-9$_]/g, "-");
  name = name.replace(/^[-0-9]+/, "");
  name = name.replace(/[-\s]+(.)?/g, function (match, c) {
    return c ? c.toUpperCase() : "";
  });

  if (!(0, _isValidIdentifier.default)(name)) {
    name = "_" + name;
  }

  return name || "_";
}

/***/ }),

/***/ 2283:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = removePropertiesDeep;

var _traverseFast = _interopRequireDefault(__webpack_require__(2284));

var _removeProperties = _interopRequireDefault(__webpack_require__(2285));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function removePropertiesDeep(tree, opts) {
  (0, _traverseFast.default)(tree, _removeProperties.default, opts);
  return tree;
}

/***/ }),

/***/ 2284:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = traverseFast;

var _definitions = __webpack_require__(2257);

function traverseFast(node, enter, opts) {
  if (!node) return;
  var keys = _definitions.VISITOR_KEYS[node.type];
  if (!keys) return;
  opts = opts || {};
  enter(node, opts);

  for (var _iterator = keys, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {
    var _ref;

    if (_isArray) {
      if (_i >= _iterator.length) break;
      _ref = _iterator[_i++];
    } else {
      _i = _iterator.next();
      if (_i.done) break;
      _ref = _i.value;
    }

    var _key = _ref;
    var subNode = node[_key];

    if (Array.isArray(subNode)) {
      for (var _iterator2 = subNode, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) {
        var _ref2;

        if (_isArray2) {
          if (_i2 >= _iterator2.length) break;
          _ref2 = _iterator2[_i2++];
        } else {
          _i2 = _iterator2.next();
          if (_i2.done) break;
          _ref2 = _i2.value;
        }

        var _node2 = _ref2;
        traverseFast(_node2, enter, opts);
      }
    } else {
      traverseFast(subNode, enter, opts);
    }
  }
}

/***/ }),

/***/ 2285:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = removeProperties;

var _constants = __webpack_require__(2259);

var CLEAR_KEYS = ["tokens", "start", "end", "loc", "raw", "rawValue"];

var CLEAR_KEYS_PLUS_COMMENTS = _constants.COMMENT_KEYS.concat(["comments"]).concat(CLEAR_KEYS);

function removeProperties(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  var map = opts.preserveComments ? CLEAR_KEYS : CLEAR_KEYS_PLUS_COMMENTS;

  for (var _iterator = map, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {
    var _ref;

    if (_isArray) {
      if (_i >= _iterator.length) break;
      _ref = _iterator[_i++];
    } else {
      _i = _iterator.next();
      if (_i.done) break;
      _ref = _i.value;
    }

    var _key2 = _ref;
    if (node[_key2] != null) node[_key2] = undefined;
  }

  for (var _key in node) {
    if (_key[0] === "_" && node[_key] != null) node[_key] = undefined;
  }

  var symbols = Object.getOwnPropertySymbols(node);

  for (var _iterator2 = symbols, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) {
    var _ref2;

    if (_isArray2) {
      if (_i2 >= _iterator2.length) break;
      _ref2 = _iterator2[_i2++];
    } else {
      _i2 = _iterator2.next();
      if (_i2.done) break;
      _ref2 = _i2.value;
    }

    var _sym = _ref2;
    node[_sym] = null;
  }
}

/***/ }),

/***/ 2286:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = isLet;

var _generated = __webpack_require__(2255);

var _constants = __webpack_require__(2259);

function isLet(node) {
  return (0, _generated.isVariableDeclaration)(node) && (node.kind !== "var" || node[_constants.BLOCK_SCOPED_SYMBOL]);
}

/***/ }),

/***/ 2291:
/***/ (function(module, exports, __webpack_require__) {

var baseIsMap = __webpack_require__(2292),
    baseUnary = __webpack_require__(215),
    nodeUtil = __webpack_require__(216);

/* Node.js helper references. */
var nodeIsMap = nodeUtil && nodeUtil.isMap;

/**
 * Checks if `value` is classified as a `Map` object.
 *
 * @static
 * @memberOf _
 * @since 4.3.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a map, else `false`.
 * @example
 *
 * _.isMap(new Map);
 * // => true
 *
 * _.isMap(new WeakMap);
 * // => false
 */
var isMap = nodeIsMap ? baseUnary(nodeIsMap) : baseIsMap;

module.exports = isMap;


/***/ }),

/***/ 2292:
/***/ (function(module, exports, __webpack_require__) {

var getTag = __webpack_require__(198),
    isObjectLike = __webpack_require__(14);

/** `Object#toString` result references. */
var mapTag = '[object Map]';

/**
 * The base implementation of `_.isMap` without Node.js optimizations.
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a map, else `false`.
 */
function baseIsMap(value) {
  return isObjectLike(value) && getTag(value) == mapTag;
}

module.exports = baseIsMap;


/***/ }),

/***/ 2293:
/***/ (function(module, exports, __webpack_require__) {

var baseIsSet = __webpack_require__(2294),
    baseUnary = __webpack_require__(215),
    nodeUtil = __webpack_require__(216);

/* Node.js helper references. */
var nodeIsSet = nodeUtil && nodeUtil.isSet;

/**
 * Checks if `value` is classified as a `Set` object.
 *
 * @static
 * @memberOf _
 * @since 4.3.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a set, else `false`.
 * @example
 *
 * _.isSet(new Set);
 * // => true
 *
 * _.isSet(new WeakSet);
 * // => false
 */
var isSet = nodeIsSet ? baseUnary(nodeIsSet) : baseIsSet;

module.exports = isSet;


/***/ }),

/***/ 2294:
/***/ (function(module, exports, __webpack_require__) {

var getTag = __webpack_require__(198),
    isObjectLike = __webpack_require__(14);

/** `Object#toString` result references. */
var setTag = '[object Set]';

/**
 * The base implementation of `_.isSet` without Node.js optimizations.
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a set, else `false`.
 */
function baseIsSet(value) {
  return isObjectLike(value) && getTag(value) == setTag;
}

module.exports = baseIsSet;


/***/ }),

/***/ 2295:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = void 0;

var _buildMatchMemberExpression = _interopRequireDefault(__webpack_require__(2269));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

var isReactComponent = (0, _buildMatchMemberExpression.default)("React.Component");
var _default = isReactComponent;
exports.default = _default;

/***/ }),

/***/ 2296:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var _utils = _interopRequireWildcard(__webpack_require__(2258));

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }

(0, _utils.default)("AnyTypeAnnotation", {
  aliases: ["Flow", "FlowType", "FlowBaseAnnotation"]
});
(0, _utils.default)("ArrayTypeAnnotation", {
  visitor: ["elementType"],
  aliases: ["Flow", "FlowType"],
  fields: {
    elementType: (0, _utils.validateType)("FlowType")
  }
});
(0, _utils.default)("BooleanTypeAnnotation", {
  aliases: ["Flow", "FlowType", "FlowBaseAnnotation"]
});
(0, _utils.default)("BooleanLiteralTypeAnnotation", {
  aliases: ["Flow", "FlowType"],
  fields: {
    value: (0, _utils.validate)((0, _utils.assertValueType)("boolean"))
  }
});
(0, _utils.default)("NullLiteralTypeAnnotation", {
  aliases: ["Flow", "FlowType", "FlowBaseAnnotation"]
});
(0, _utils.default)("ClassImplements", {
  visitor: ["id", "typeParameters"],
  aliases: ["Flow"],
  fields: {
    id: (0, _utils.validateType)("Identifier"),
    typeParameters: (0, _utils.validateOptionalType)("TypeParameterInstantiation")
  }
});
(0, _utils.default)("DeclareClass", {
  visitor: ["id", "typeParameters", "extends", "body"],
  aliases: ["Flow", "FlowDeclaration", "Statement", "Declaration"],
  fields: {
    id: (0, _utils.validateType)("Identifier"),
    typeParameters: (0, _utils.validateOptionalType)("TypeParameterInstantiation"),
    extends: (0, _utils.validateOptional)((0, _utils.arrayOfType)("InterfaceExtends")),
    mixins: (0, _utils.validateOptional)((0, _utils.arrayOfType)("InterfaceExtends")),
    body: (0, _utils.validateType)("ObjectTypeAnnotation")
  }
});
(0, _utils.default)("DeclareFunction", {
  visitor: ["id"],
  aliases: ["Flow", "FlowDeclaration", "Statement", "Declaration"],
  fields: {
    id: (0, _utils.validateType)("Identifier"),
    predicate: (0, _utils.validateOptionalType)("DeclaredPredicate")
  }
});
(0, _utils.default)("DeclareInterface", {
  visitor: ["id", "typeParameters", "extends", "body"],
  aliases: ["Flow", "FlowDeclaration", "Statement", "Declaration"],
  fields: {
    id: (0, _utils.validateType)("Identifier"),
    typeParameters: (0, _utils.validateOptionalType)("TypeParameterDeclaration"),
    extends: (0, _utils.validateOptionalType)("InterfaceExtends"),
    mixins: (0, _utils.validateOptional)((0, _utils.arrayOfType)("Flow")),
    body: (0, _utils.validateType)("ObjectTypeAnnotation")
  }
});
(0, _utils.default)("DeclareModule", {
  builder: ["id", "body", "kind"],
  visitor: ["id", "body"],
  aliases: ["Flow", "FlowDeclaration", "Statement", "Declaration"],
  fields: {
    id: (0, _utils.validateType)(["Identifier", "StringLiteral"]),
    body: (0, _utils.validateType)("BlockStatement"),
    kind: (0, _utils.validateOptional)((0, _utils.assertOneOf)("CommonJS", "ES"))
  }
});
(0, _utils.default)("DeclareModuleExports", {
  visitor: ["typeAnnotation"],
  aliases: ["Flow", "FlowDeclaration", "Statement", "Declaration"],
  fields: {
    typeAnnotation: (0, _utils.validateType)("TypeAnnotation")
  }
});
(0, _utils.default)("DeclareTypeAlias", {
  visitor: ["id", "typeParameters", "right"],
  aliases: ["Flow", "FlowDeclaration", "Statement", "Declaration"],
  fields: {
    id: (0, _utils.validateType)("Identifier"),
    typeParameters: (0, _utils.validateOptionalType)("TypeParameterDeclaration"),
    right: (0, _utils.validateType)("FlowType")
  }
});
(0, _utils.default)("DeclareOpaqueType", {
  visitor: ["id", "typeParameters", "supertype"],
  aliases: ["Flow", "FlowDeclaration", "Statement", "Declaration"],
  fields: {
    id: (0, _utils.validateType)("Identifier"),
    typeParameters: (0, _utils.validateOptionalType)("TypeParameterDeclaration"),
    supertype: (0, _utils.validateOptionalType)("FlowType")
  }
});
(0, _utils.default)("DeclareVariable", {
  visitor: ["id"],
  aliases: ["Flow", "FlowDeclaration", "Statement", "Declaration"],
  fields: {
    id: (0, _utils.validateType)("Identifier")
  }
});
(0, _utils.default)("DeclareExportDeclaration", {
  visitor: ["declaration", "specifiers", "source"],
  aliases: ["Flow", "FlowDeclaration", "Statement", "Declaration"],
  fields: {
    declaration: (0, _utils.validateOptionalType)("Flow"),
    specifiers: (0, _utils.validateOptional)((0, _utils.arrayOfType)(["ExportSpecifier", "ExportNamespaceSpecifier"])),
    source: (0, _utils.validateOptionalType)("StringLiteral"),
    default: (0, _utils.validateOptional)((0, _utils.assertValueType)("boolean"))
  }
});
(0, _utils.default)("DeclareExportAllDeclaration", {
  visitor: ["source"],
  aliases: ["Flow", "FlowDeclaration", "Statement", "Declaration"],
  fields: {
    source: (0, _utils.validateType)("StringLiteral"),
    exportKind: (0, _utils.validateOptional)((0, _utils.assertOneOf)(["type", "value"]))
  }
});
(0, _utils.default)("DeclaredPredicate", {
  visitor: ["value"],
  aliases: ["Flow", "FlowPredicate"],
  fields: {
    value: (0, _utils.validateType)("Flow")
  }
});
(0, _utils.default)("ExistsTypeAnnotation", {
  aliases: ["Flow", "FlowType"]
});
(0, _utils.default)("FunctionTypeAnnotation", {
  visitor: ["typeParameters", "params", "rest", "returnType"],
  aliases: ["Flow", "FlowType"],
  fields: {
    typeParameters: (0, _utils.validateOptionalType)("TypeParameterDeclaration"),
    params: (0, _utils.validate)((0, _utils.arrayOfType)("FunctionTypeParam")),
    rest: (0, _utils.validateOptionalType)("FunctionTypeParam"),
    returnType: (0, _utils.validateType)("FlowType")
  }
});
(0, _utils.default)("FunctionTypeParam", {
  visitor: ["name", "typeAnnotation"],
  aliases: ["Flow"],
  fields: {
    name: (0, _utils.validateOptionalType)("Identifier"),
    typeAnnotation: (0, _utils.validateType)("FlowType"),
    optional: (0, _utils.validateOptional)((0, _utils.assertValueType)("boolean"))
  }
});
(0, _utils.default)("GenericTypeAnnotation", {
  visitor: ["id", "typeParameters"],
  aliases: ["Flow", "FlowType"],
  fields: {
    id: (0, _utils.validateType)("Identifier"),
    typeParameters: (0, _utils.validateOptionalType)("TypeParameterInstantiation")
  }
});
(0, _utils.default)("InferredPredicate", {
  aliases: ["Flow", "FlowPredicate"]
});
(0, _utils.default)("InterfaceExtends", {
  visitor: ["id", "typeParameters"],
  aliases: ["Flow"],
  fields: {
    id: (0, _utils.validateType)("Identifier"),
    typeParameters: (0, _utils.validateOptionalType)("TypeParameterInstantiation")
  }
});
(0, _utils.default)("InterfaceDeclaration", {
  visitor: ["id", "typeParameters", "extends", "body"],
  aliases: ["Flow", "FlowDeclaration", "Statement", "Declaration"],
  fields: {
    id: (0, _utils.validateType)("Identifier"),
    typeParameters: (0, _utils.validateOptionalType)("TypeParameterDeclaration"),
    extends: (0, _utils.validate)((0, _utils.arrayOfType)("InterfaceExtends")),
    mixins: (0, _utils.validate)((0, _utils.arrayOfType)("InterfaceExtends")),
    body: (0, _utils.validateType)("ObjectTypeAnnotation")
  }
});
(0, _utils.default)("IntersectionTypeAnnotation", {
  visitor: ["types"],
  aliases: ["Flow", "FlowType"],
  fields: {
    types: (0, _utils.validate)((0, _utils.arrayOfType)("FlowType"))
  }
});
(0, _utils.default)("MixedTypeAnnotation", {
  aliases: ["Flow", "FlowType", "FlowBaseAnnotation"]
});
(0, _utils.default)("EmptyTypeAnnotation", {
  aliases: ["Flow", "FlowType", "FlowBaseAnnotation"]
});
(0, _utils.default)("NullableTypeAnnotation", {
  visitor: ["typeAnnotation"],
  aliases: ["Flow", "FlowType"],
  fields: {
    typeAnnotation: (0, _utils.validateType)("FlowType")
  }
});
(0, _utils.default)("NumberLiteralTypeAnnotation", {
  aliases: ["Flow", "FlowType"],
  fields: {
    value: (0, _utils.validate)((0, _utils.assertValueType)("number"))
  }
});
(0, _utils.default)("NumberTypeAnnotation", {
  aliases: ["Flow", "FlowType", "FlowBaseAnnotation"]
});
(0, _utils.default)("ObjectTypeAnnotation", {
  visitor: ["properties", "indexers", "callProperties"],
  aliases: ["Flow", "FlowType"],
  fields: {
    properties: (0, _utils.validate)((0, _utils.arrayOfType)(["ObjectTypeProperty", "ObjectTypeSpreadProperty"])),
    indexers: (0, _utils.validateOptional)((0, _utils.arrayOfType)("ObjectTypeIndexer")),
    callProperties: (0, _utils.validateOptional)((0, _utils.arrayOfType)("ObjectTypeCallProperty")),
    exact: (0, _utils.validate)((0, _utils.assertValueType)("boolean"))
  }
});
(0, _utils.default)("ObjectTypeCallProperty", {
  visitor: ["value"],
  aliases: ["Flow", "UserWhitespacable"],
  fields: {
    value: (0, _utils.validateType)("FlowType"),
    static: (0, _utils.validate)((0, _utils.assertValueType)("boolean"))
  }
});
(0, _utils.default)("ObjectTypeIndexer", {
  visitor: ["id", "key", "value", "variance"],
  aliases: ["Flow", "UserWhitespacable"],
  fields: {
    id: (0, _utils.validateOptionalType)("Identifier"),
    key: (0, _utils.validateType)("FlowType"),
    value: (0, _utils.validateType)("FlowType"),
    static: (0, _utils.validate)((0, _utils.assertValueType)("boolean")),
    variance: (0, _utils.validateOptionalType)("Variance")
  }
});
(0, _utils.default)("ObjectTypeProperty", {
  visitor: ["key", "value", "variance"],
  aliases: ["Flow", "UserWhitespacable"],
  fields: {
    key: (0, _utils.validateType)("Identifier"),
    value: (0, _utils.validateType)("FlowType"),
    kind: (0, _utils.validate)((0, _utils.assertOneOf)("init", "get", "set")),
    static: (0, _utils.validate)((0, _utils.assertValueType)("boolean")),
    optional: (0, _utils.validate)((0, _utils.assertValueType)("boolean")),
    variance: (0, _utils.validateOptionalType)("Variance")
  }
});
(0, _utils.default)("ObjectTypeSpreadProperty", {
  visitor: ["argument"],
  aliases: ["Flow", "UserWhitespacable"],
  fields: {
    argument: (0, _utils.validateType)("FlowType")
  }
});
(0, _utils.default)("OpaqueType", {
  visitor: ["id", "typeParameters", "supertype", "impltype"],
  aliases: ["Flow", "FlowDeclaration", "Statement", "Declaration"],
  fields: {
    id: (0, _utils.validateType)("Identifier"),
    typeParameters: (0, _utils.validateOptionalType)("TypeParameterDeclaration"),
    supertype: (0, _utils.validateOptionalType)("FlowType"),
    impltype: (0, _utils.validateType)("FlowType")
  }
});
(0, _utils.default)("QualifiedTypeIdentifier", {
  visitor: ["id", "qualification"],
  aliases: ["Flow"],
  fields: {
    id: (0, _utils.validateType)("Identifier"),
    qualification: (0, _utils.validateType)(["Identifier", "QualifiedTypeIdentifier"])
  }
});
(0, _utils.default)("StringLiteralTypeAnnotation", {
  aliases: ["Flow", "FlowType"],
  fields: {
    value: (0, _utils.validate)((0, _utils.assertValueType)("string"))
  }
});
(0, _utils.default)("StringTypeAnnotation", {
  aliases: ["Flow", "FlowType", "FlowBaseAnnotation"]
});
(0, _utils.default)("ThisTypeAnnotation", {
  aliases: ["Flow", "FlowType", "FlowBaseAnnotation"]
});
(0, _utils.default)("TupleTypeAnnotation", {
  visitor: ["types"],
  aliases: ["Flow", "FlowType"],
  fields: {
    types: (0, _utils.validate)((0, _utils.arrayOfType)("FlowType"))
  }
});
(0, _utils.default)("TypeofTypeAnnotation", {
  visitor: ["argument"],
  aliases: ["Flow", "FlowType"],
  fields: {
    argument: (0, _utils.validateType)("FlowType")
  }
});
(0, _utils.default)("TypeAlias", {
  visitor: ["id", "typeParameters", "right"],
  aliases: ["Flow", "FlowDeclaration", "Statement", "Declaration"],
  fields: {
    id: (0, _utils.validateType)("Identifier"),
    typeParameters: (0, _utils.validateOptionalType)("TypeParameterDeclaration"),
    right: (0, _utils.validateType)("FlowType")
  }
});
(0, _utils.default)("TypeAnnotation", {
  aliases: ["Flow"],
  visitor: ["typeAnnotation"],
  fields: {
    typeAnnotation: (0, _utils.validateType)("FlowType")
  }
});
(0, _utils.default)("TypeCastExpression", {
  visitor: ["expression", "typeAnnotation"],
  aliases: ["Flow", "ExpressionWrapper", "Expression"],
  fields: {
    expression: (0, _utils.validateType)("Expression"),
    typeAnnotation: (0, _utils.validateType)("TypeAnnotation")
  }
});
(0, _utils.default)("TypeParameter", {
  aliases: ["Flow"],
  visitor: ["bound", "default", "variance"],
  fields: {
    name: (0, _utils.validate)((0, _utils.assertValueType)("string")),
    bound: (0, _utils.validateOptionalType)("TypeAnnotation"),
    default: (0, _utils.validateOptionalType)("FlowType"),
    variance: (0, _utils.validateOptionalType)("Variance")
  }
});
(0, _utils.default)("TypeParameterDeclaration", {
  aliases: ["Flow"],
  visitor: ["params"],
  fields: {
    params: (0, _utils.validate)((0, _utils.arrayOfType)("TypeParameter"))
  }
});
(0, _utils.default)("TypeParameterInstantiation", {
  aliases: ["Flow"],
  visitor: ["params"],
  fields: {
    params: (0, _utils.validate)((0, _utils.arrayOfType)("FlowType"))
  }
});
(0, _utils.default)("UnionTypeAnnotation", {
  visitor: ["types"],
  aliases: ["Flow", "FlowType"],
  fields: {
    types: (0, _utils.validate)((0, _utils.arrayOfType)("FlowType"))
  }
});
(0, _utils.default)("Variance", {
  aliases: ["Flow"],
  builder: ["kind"],
  fields: {
    kind: (0, _utils.validate)((0, _utils.assertOneOf)("minus", "plus"))
  }
});
(0, _utils.default)("VoidTypeAnnotation", {
  aliases: ["Flow", "FlowType", "FlowBaseAnnotation"]
});

/***/ }),

/***/ 2297:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var _utils = _interopRequireWildcard(__webpack_require__(2258));

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }

(0, _utils.default)("JSXAttribute", {
  visitor: ["name", "value"],
  aliases: ["JSX", "Immutable"],
  fields: {
    name: {
      validate: (0, _utils.assertNodeType)("JSXIdentifier", "JSXNamespacedName")
    },
    value: {
      optional: true,
      validate: (0, _utils.assertNodeType)("JSXElement", "JSXFragment", "StringLiteral", "JSXExpressionContainer")
    }
  }
});
(0, _utils.default)("JSXClosingElement", {
  visitor: ["name"],
  aliases: ["JSX", "Immutable"],
  fields: {
    name: {
      validate: (0, _utils.assertNodeType)("JSXIdentifier", "JSXMemberExpression")
    }
  }
});
(0, _utils.default)("JSXElement", {
  builder: ["openingElement", "closingElement", "children", "selfClosing"],
  visitor: ["openingElement", "children", "closingElement"],
  aliases: ["JSX", "Immutable", "Expression"],
  fields: {
    openingElement: {
      validate: (0, _utils.assertNodeType)("JSXOpeningElement")
    },
    closingElement: {
      optional: true,
      validate: (0, _utils.assertNodeType)("JSXClosingElement")
    },
    children: {
      validate: (0, _utils.chain)((0, _utils.assertValueType)("array"), (0, _utils.assertEach)((0, _utils.assertNodeType)("JSXText", "JSXExpressionContainer", "JSXSpreadChild", "JSXElement", "JSXFragment")))
    }
  }
});
(0, _utils.default)("JSXEmptyExpression", {
  aliases: ["JSX"]
});
(0, _utils.default)("JSXExpressionContainer", {
  visitor: ["expression"],
  aliases: ["JSX", "Immutable"],
  fields: {
    expression: {
      validate: (0, _utils.assertNodeType)("Expression")
    }
  }
});
(0, _utils.default)("JSXSpreadChild", {
  visitor: ["expression"],
  aliases: ["JSX", "Immutable"],
  fields: {
    expression: {
      validate: (0, _utils.assertNodeType)("Expression")
    }
  }
});
(0, _utils.default)("JSXIdentifier", {
  builder: ["name"],
  aliases: ["JSX"],
  fields: {
    name: {
      validate: (0, _utils.assertValueType)("string")
    }
  }
});
(0, _utils.default)("JSXMemberExpression", {
  visitor: ["object", "property"],
  aliases: ["JSX"],
  fields: {
    object: {
      validate: (0, _utils.assertNodeType)("JSXMemberExpression", "JSXIdentifier")
    },
    property: {
      validate: (0, _utils.assertNodeType)("JSXIdentifier")
    }
  }
});
(0, _utils.default)("JSXNamespacedName", {
  visitor: ["namespace", "name"],
  aliases: ["JSX"],
  fields: {
    namespace: {
      validate: (0, _utils.assertNodeType)("JSXIdentifier")
    },
    name: {
      validate: (0, _utils.assertNodeType)("JSXIdentifier")
    }
  }
});
(0, _utils.default)("JSXOpeningElement", {
  builder: ["name", "attributes", "selfClosing"],
  visitor: ["name", "attributes"],
  aliases: ["JSX", "Immutable"],
  fields: {
    name: {
      validate: (0, _utils.assertNodeType)("JSXIdentifier", "JSXMemberExpression")
    },
    selfClosing: {
      default: false,
      validate: (0, _utils.assertValueType)("boolean")
    },
    attributes: {
      validate: (0, _utils.chain)((0, _utils.assertValueType)("array"), (0, _utils.assertEach)((0, _utils.assertNodeType)("JSXAttribute", "JSXSpreadAttribute")))
    }
  }
});
(0, _utils.default)("JSXSpreadAttribute", {
  visitor: ["argument"],
  aliases: ["JSX"],
  fields: {
    argument: {
      validate: (0, _utils.assertNodeType)("Expression")
    }
  }
});
(0, _utils.default)("JSXText", {
  aliases: ["JSX", "Immutable"],
  builder: ["value"],
  fields: {
    value: {
      validate: (0, _utils.assertValueType)("string")
    }
  }
});
(0, _utils.default)("JSXFragment", {
  builder: ["openingFragment", "closingFragment", "children"],
  visitor: ["openingFragment", "children", "closingFragment"],
  aliases: ["JSX", "Immutable", "Expression"],
  fields: {
    openingFragment: {
      validate: (0, _utils.assertNodeType)("JSXOpeningFragment")
    },
    closingFragment: {
      validate: (0, _utils.assertNodeType)("JSXClosingFragment")
    },
    children: {
      validate: (0, _utils.chain)((0, _utils.assertValueType)("array"), (0, _utils.assertEach)((0, _utils.assertNodeType)("JSXText", "JSXExpressionContainer", "JSXSpreadChild", "JSXElement", "JSXFragment")))
    }
  }
});
(0, _utils.default)("JSXOpeningFragment", {
  aliases: ["JSX", "Immutable"]
});
(0, _utils.default)("JSXClosingFragment", {
  aliases: ["JSX", "Immutable"]
});

/***/ }),

/***/ 2298:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var _utils = _interopRequireWildcard(__webpack_require__(2258));

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }

(0, _utils.default)("Noop", {
  visitor: []
});
(0, _utils.default)("ParenthesizedExpression", {
  visitor: ["expression"],
  aliases: ["Expression", "ExpressionWrapper"],
  fields: {
    expression: {
      validate: (0, _utils.assertNodeType)("Expression")
    }
  }
});

/***/ }),

/***/ 2299:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var _utils = _interopRequireWildcard(__webpack_require__(2258));

var _es = __webpack_require__(2266);

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }

(0, _utils.default)("AwaitExpression", {
  builder: ["argument"],
  visitor: ["argument"],
  aliases: ["Expression", "Terminatorless"],
  fields: {
    argument: {
      validate: (0, _utils.assertNodeType)("Expression")
    }
  }
});
(0, _utils.default)("BindExpression", {
  visitor: ["object", "callee"],
  aliases: ["Expression"],
  fields: {}
});
(0, _utils.default)("ClassProperty", {
  visitor: ["key", "value", "typeAnnotation", "decorators"],
  builder: ["key", "value", "typeAnnotation", "decorators", "computed"],
  aliases: ["Property"],
  fields: Object.assign({}, _es.classMethodOrPropertyCommon, {
    value: {
      validate: (0, _utils.assertNodeType)("Expression"),
      optional: true
    },
    typeAnnotation: {
      validate: (0, _utils.assertNodeType)("TypeAnnotation", "TSTypeAnnotation", "Noop"),
      optional: true
    },
    decorators: {
      validate: (0, _utils.chain)((0, _utils.assertValueType)("array"), (0, _utils.assertEach)((0, _utils.assertNodeType)("Decorator"))),
      optional: true
    },
    readonly: {
      validate: (0, _utils.assertValueType)("boolean"),
      optional: true
    }
  })
});
(0, _utils.default)("Import", {
  aliases: ["Expression"]
});
(0, _utils.default)("Decorator", {
  visitor: ["expression"],
  fields: {
    expression: {
      validate: (0, _utils.assertNodeType)("Expression")
    }
  }
});
(0, _utils.default)("DoExpression", {
  visitor: ["body"],
  aliases: ["Expression"],
  fields: {
    body: {
      validate: (0, _utils.assertNodeType)("BlockStatement")
    }
  }
});
(0, _utils.default)("ExportDefaultSpecifier", {
  visitor: ["exported"],
  aliases: ["ModuleSpecifier"],
  fields: {
    exported: {
      validate: (0, _utils.assertNodeType)("Identifier")
    }
  }
});
(0, _utils.default)("ExportNamespaceSpecifier", {
  visitor: ["exported"],
  aliases: ["ModuleSpecifier"],
  fields: {
    exported: {
      validate: (0, _utils.assertNodeType)("Identifier")
    }
  }
});

/***/ }),

/***/ 2300:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var _utils = _interopRequireWildcard(__webpack_require__(2258));

var _core = __webpack_require__(2265);

var _es = __webpack_require__(2266);

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }

var bool = (0, _utils.assertValueType)("boolean");
var tSFunctionTypeAnnotationCommon = {
  returnType: {
    validate: (0, _utils.assertNodeType)("TSTypeAnnotation", "Noop"),
    optional: true
  },
  typeParameters: {
    validate: (0, _utils.assertNodeType)("TSTypeParameterDeclaration", "Noop"),
    optional: true
  }
};
(0, _utils.default)("TSParameterProperty", {
  aliases: ["LVal"],
  visitor: ["parameter"],
  fields: {
    accessibility: {
      validate: (0, _utils.assertOneOf)("public", "private", "protected"),
      optional: true
    },
    readonly: {
      validate: (0, _utils.assertValueType)("boolean"),
      optional: true
    },
    parameter: {
      validate: (0, _utils.assertNodeType)("Identifier", "AssignmentPattern")
    }
  }
});
(0, _utils.default)("TSDeclareFunction", {
  aliases: ["Statement", "Declaration"],
  visitor: ["id", "typeParameters", "params", "returnType"],
  fields: Object.assign({}, _core.functionDeclarationCommon, tSFunctionTypeAnnotationCommon)
});
(0, _utils.default)("TSDeclareMethod", {
  visitor: ["decorators", "key", "typeParameters", "params", "returnType"],
  fields: Object.assign({}, _es.classMethodOrDeclareMethodCommon, tSFunctionTypeAnnotationCommon)
});
(0, _utils.default)("TSQualifiedName", {
  aliases: ["TSEntityName"],
  visitor: ["left", "right"],
  fields: {
    left: (0, _utils.validateType)("TSEntityName"),
    right: (0, _utils.validateType)("Identifier")
  }
});
var signatureDeclarationCommon = {
  typeParameters: (0, _utils.validateOptionalType)("TSTypeParameterDeclaration"),
  parameters: (0, _utils.validateArrayOfType)(["Identifier", "RestElement"]),
  typeAnnotation: (0, _utils.validateOptionalType)("TSTypeAnnotation")
};
var callConstructSignatureDeclaration = {
  aliases: ["TSTypeElement"],
  visitor: ["typeParameters", "parameters", "typeAnnotation"],
  fields: signatureDeclarationCommon
};
(0, _utils.default)("TSCallSignatureDeclaration", callConstructSignatureDeclaration);
(0, _utils.default)("TSConstructSignatureDeclaration", callConstructSignatureDeclaration);
var namedTypeElementCommon = {
  key: (0, _utils.validateType)("Expression"),
  computed: (0, _utils.validate)(bool),
  optional: (0, _utils.validateOptional)(bool)
};
(0, _utils.default)("TSPropertySignature", {
  aliases: ["TSTypeElement"],
  visitor: ["key", "typeAnnotation", "initializer"],
  fields: Object.assign({}, namedTypeElementCommon, {
    readonly: (0, _utils.validateOptional)(bool),
    typeAnnotation: (0, _utils.validateOptionalType)("TSTypeAnnotation"),
    initializer: (0, _utils.validateOptionalType)("Expression")
  })
});
(0, _utils.default)("TSMethodSignature", {
  aliases: ["TSTypeElement"],
  visitor: ["key", "typeParameters", "parameters", "typeAnnotation"],
  fields: Object.assign({}, signatureDeclarationCommon, namedTypeElementCommon)
});
(0, _utils.default)("TSIndexSignature", {
  aliases: ["TSTypeElement"],
  visitor: ["parameters", "typeAnnotation"],
  fields: {
    readonly: (0, _utils.validateOptional)(bool),
    parameters: (0, _utils.validateArrayOfType)("Identifier"),
    typeAnnotation: (0, _utils.validateOptionalType)("TSTypeAnnotation")
  }
});
var tsKeywordTypes = ["TSAnyKeyword", "TSNumberKeyword", "TSObjectKeyword", "TSBooleanKeyword", "TSStringKeyword", "TSSymbolKeyword", "TSVoidKeyword", "TSUndefinedKeyword", "TSNullKeyword", "TSNeverKeyword"];

for (var _i = 0; _i < tsKeywordTypes.length; _i++) {
  var type = tsKeywordTypes[_i];
  (0, _utils.default)(type, {
    aliases: ["TSType"],
    visitor: [],
    fields: {}
  });
}

(0, _utils.default)("TSThisType", {
  aliases: ["TSType"],
  visitor: [],
  fields: {}
});
var fnOrCtr = {
  aliases: ["TSType"],
  visitor: ["typeParameters", "typeAnnotation"],
  fields: signatureDeclarationCommon
};
(0, _utils.default)("TSFunctionType", fnOrCtr);
(0, _utils.default)("TSConstructorType", fnOrCtr);
(0, _utils.default)("TSTypeReference", {
  aliases: ["TSType"],
  visitor: ["typeName", "typeParameters"],
  fields: {
    typeName: (0, _utils.validateType)("TSEntityName"),
    typeParameters: (0, _utils.validateOptionalType)("TSTypeParameterInstantiation")
  }
});
(0, _utils.default)("TSTypePredicate", {
  aliases: ["TSType"],
  visitor: ["parameterName", "typeAnnotation"],
  fields: {
    parameterName: (0, _utils.validateType)(["Identifier", "TSThisType"]),
    typeAnnotation: (0, _utils.validateType)("TSTypeAnnotation")
  }
});
(0, _utils.default)("TSTypeQuery", {
  aliases: ["TSType"],
  visitor: ["exprName"],
  fields: {
    exprName: (0, _utils.validateType)("TSEntityName")
  }
});
(0, _utils.default)("TSTypeLiteral", {
  aliases: ["TSType"],
  visitor: ["members"],
  fields: {
    members: (0, _utils.validateArrayOfType)("TSTypeElement")
  }
});
(0, _utils.default)("TSArrayType", {
  aliases: ["TSType"],
  visitor: ["elementType"],
  fields: {
    elementType: (0, _utils.validateType)("TSType")
  }
});
(0, _utils.default)("TSTupleType", {
  aliases: ["TSType"],
  visitor: ["elementTypes"],
  fields: {
    elementTypes: (0, _utils.validateArrayOfType)("TSType")
  }
});
var unionOrIntersection = {
  aliases: ["TSType"],
  visitor: ["types"],
  fields: {
    types: (0, _utils.validateArrayOfType)("TSType")
  }
};
(0, _utils.default)("TSUnionType", unionOrIntersection);
(0, _utils.default)("TSIntersectionType", unionOrIntersection);
(0, _utils.default)("TSParenthesizedType", {
  aliases: ["TSType"],
  visitor: ["typeAnnotation"],
  fields: {
    typeAnnotation: (0, _utils.validateType)("TSType")
  }
});
(0, _utils.default)("TSTypeOperator", {
  aliases: ["TSType"],
  visitor: ["typeAnnotation"],
  fields: {
    operator: (0, _utils.validate)((0, _utils.assertValueType)("string")),
    typeAnnotation: (0, _utils.validateType)("TSType")
  }
});
(0, _utils.default)("TSIndexedAccessType", {
  aliases: ["TSType"],
  visitor: ["objectType", "indexType"],
  fields: {
    objectType: (0, _utils.validateType)("TSType"),
    indexType: (0, _utils.validateType)("TSType")
  }
});
(0, _utils.default)("TSMappedType", {
  aliases: ["TSType"],
  visitor: ["typeParameter", "typeAnnotation"],
  fields: {
    readonly: (0, _utils.validateOptional)(bool),
    typeParameter: (0, _utils.validateType)("TSTypeParameter"),
    optional: (0, _utils.validateOptional)(bool),
    typeAnnotation: (0, _utils.validateOptionalType)("TSType")
  }
});
(0, _utils.default)("TSLiteralType", {
  aliases: ["TSType"],
  visitor: ["literal"],
  fields: {
    literal: (0, _utils.validateType)(["NumericLiteral", "StringLiteral", "BooleanLiteral"])
  }
});
(0, _utils.default)("TSExpressionWithTypeArguments", {
  aliases: ["TSType"],
  visitor: ["expression", "typeParameters"],
  fields: {
    expression: (0, _utils.validateType)("TSEntityName"),
    typeParameters: (0, _utils.validateOptionalType)("TSTypeParameterInstantiation")
  }
});
(0, _utils.default)("TSInterfaceDeclaration", {
  aliases: ["Statement", "Declaration"],
  visitor: ["id", "typeParameters", "extends", "body"],
  fields: {
    declare: (0, _utils.validateOptional)(bool),
    id: (0, _utils.validateType)("Identifier"),
    typeParameters: (0, _utils.validateOptionalType)("TSTypeParameterDeclaration"),
    extends: (0, _utils.validateOptional)((0, _utils.arrayOfType)("TSExpressionWithTypeArguments")),
    body: (0, _utils.validateType)("TSInterfaceBody")
  }
});
(0, _utils.default)("TSInterfaceBody", {
  visitor: ["body"],
  fields: {
    body: (0, _utils.validateArrayOfType)("TSTypeElement")
  }
});
(0, _utils.default)("TSTypeAliasDeclaration", {
  aliases: ["Statement", "Declaration"],
  visitor: ["id", "typeParameters", "typeAnnotation"],
  fields: {
    declare: (0, _utils.validateOptional)(bool),
    id: (0, _utils.validateType)("Identifier"),
    typeParameters: (0, _utils.validateOptionalType)("TSTypeParameterDeclaration"),
    typeAnnotation: (0, _utils.validateType)("TSType")
  }
});
(0, _utils.default)("TSAsExpression", {
  aliases: ["Expression"],
  visitor: ["expression", "typeAnnotation"],
  fields: {
    expression: (0, _utils.validateType)("Expression"),
    typeAnnotation: (0, _utils.validateType)("TSType")
  }
});
(0, _utils.default)("TSTypeAssertion", {
  aliases: ["Expression"],
  visitor: ["typeAnnotation", "expression"],
  fields: {
    typeAnnotation: (0, _utils.validateType)("TSType"),
    expression: (0, _utils.validateType)("Expression")
  }
});
(0, _utils.default)("TSEnumDeclaration", {
  aliases: ["Statement", "Declaration"],
  visitor: ["id", "members"],
  fields: {
    declare: (0, _utils.validateOptional)(bool),
    const: (0, _utils.validateOptional)(bool),
    id: (0, _utils.validateType)("Identifier"),
    members: (0, _utils.validateArrayOfType)("TSEnumMember"),
    initializer: (0, _utils.validateOptionalType)("Expression")
  }
});
(0, _utils.default)("TSEnumMember", {
  visitor: ["id", "initializer"],
  fields: {
    id: (0, _utils.validateType)(["Identifier", "StringLiteral"]),
    initializer: (0, _utils.validateOptionalType)("Expression")
  }
});
(0, _utils.default)("TSModuleDeclaration", {
  aliases: ["Statement", "Declaration"],
  visitor: ["id", "body"],
  fields: {
    declare: (0, _utils.validateOptional)(bool),
    global: (0, _utils.validateOptional)(bool),
    id: (0, _utils.validateType)(["Identifier", "StringLiteral"]),
    body: (0, _utils.validateType)(["TSModuleBlock", "TSModuleDeclaration"])
  }
});
(0, _utils.default)("TSModuleBlock", {
  visitor: ["body"],
  fields: {
    body: (0, _utils.validateArrayOfType)("Statement")
  }
});
(0, _utils.default)("TSImportEqualsDeclaration", {
  aliases: ["Statement"],
  visitor: ["id", "moduleReference"],
  fields: {
    isExport: (0, _utils.validate)(bool),
    id: (0, _utils.validateType)("Identifier"),
    moduleReference: (0, _utils.validateType)(["TSEntityName", "TSExternalModuleReference"])
  }
});
(0, _utils.default)("TSExternalModuleReference", {
  visitor: ["expression"],
  fields: {
    expression: (0, _utils.validateType)("StringLiteral")
  }
});
(0, _utils.default)("TSNonNullExpression", {
  aliases: ["Expression"],
  visitor: ["expression"],
  fields: {
    expression: (0, _utils.validateType)("Expression")
  }
});
(0, _utils.default)("TSExportAssignment", {
  aliases: ["Statement"],
  visitor: ["expression"],
  fields: {
    expression: (0, _utils.validateType)("Expression")
  }
});
(0, _utils.default)("TSNamespaceExportDeclaration", {
  aliases: ["Statement"],
  visitor: ["id"],
  fields: {
    id: (0, _utils.validateType)("Identifier")
  }
});
(0, _utils.default)("TSTypeAnnotation", {
  visitor: ["typeAnnotation"],
  fields: {
    typeAnnotation: {
      validate: (0, _utils.assertNodeType)("TSType")
    }
  }
});
(0, _utils.default)("TSTypeParameterInstantiation", {
  visitor: ["params"],
  fields: {
    params: {
      validate: (0, _utils.chain)((0, _utils.assertValueType)("array"), (0, _utils.assertEach)((0, _utils.assertNodeType)("TSType")))
    }
  }
});
(0, _utils.default)("TSTypeParameterDeclaration", {
  visitor: ["params"],
  fields: {
    params: {
      validate: (0, _utils.chain)((0, _utils.assertValueType)("array"), (0, _utils.assertEach)((0, _utils.assertNodeType)("TSTypeParameter")))
    }
  }
});
(0, _utils.default)("TSTypeParameter", {
  visitor: ["constraint", "default"],
  fields: {
    name: {
      validate: (0, _utils.assertValueType)("string")
    },
    constraint: {
      validate: (0, _utils.assertNodeType)("TSType"),
      optional: true
    },
    default: {
      validate: (0, _utils.assertNodeType)("TSType"),
      optional: true
    }
  }
});

/***/ }),

/***/ 2301:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = isCompatTag;

function isCompatTag(tagName) {
  return !!tagName && /^[a-z]/.test(tagName);
}

/***/ }),

/***/ 2302:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = buildChildren;

var _generated = __webpack_require__(2255);

var _cleanJSXElementLiteralChild = _interopRequireDefault(__webpack_require__(2303));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function buildChildren(node) {
  var elements = [];

  for (var i = 0; i < node.children.length; i++) {
    var child = node.children[i];

    if ((0, _generated.isJSXText)(child)) {
      (0, _cleanJSXElementLiteralChild.default)(child, elements);
      continue;
    }

    if ((0, _generated.isJSXExpressionContainer)(child)) child = child.expression;
    if ((0, _generated.isJSXEmptyExpression)(child)) continue;
    elements.push(child);
  }

  return elements;
}

/***/ }),

/***/ 2303:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = cleanJSXElementLiteralChild;

var _generated = __webpack_require__(2256);

function cleanJSXElementLiteralChild(child, args) {
  var lines = child.value.split(/\r\n|\n|\r/);
  var lastNonEmptyLine = 0;

  for (var i = 0; i < lines.length; i++) {
    if (lines[i].match(/[^ \t]/)) {
      lastNonEmptyLine = i;
    }
  }

  var str = "";

  for (var _i = 0; _i < lines.length; _i++) {
    var line = lines[_i];
    var isFirstLine = _i === 0;
    var isLastLine = _i === lines.length - 1;
    var isLastNonEmptyLine = _i === lastNonEmptyLine;
    var trimmedLine = line.replace(/\t/g, " ");

    if (!isFirstLine) {
      trimmedLine = trimmedLine.replace(/^[ ]+/, "");
    }

    if (!isLastLine) {
      trimmedLine = trimmedLine.replace(/[ ]+$/, "");
    }

    if (trimmedLine) {
      if (!isLastNonEmptyLine) {
        trimmedLine += " ";
      }

      str += trimmedLine;
    }
  }

  if (str) args.push((0, _generated.stringLiteral)(str));
}

/***/ }),

/***/ 2304:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = builder;

var _clone = _interopRequireDefault(__webpack_require__(542));

var _definitions = __webpack_require__(2257);

var _validate = _interopRequireDefault(__webpack_require__(2272));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function builder(type) {
  for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
    args[_key - 1] = arguments[_key];
  }

  var keys = _definitions.BUILDER_KEYS[type];
  var countArgs = args.length;

  if (countArgs > keys.length) {
    throw new Error(type + ": Too many arguments passed. Received " + countArgs + " but can receive no more than " + keys.length);
  }

  var node = {
    type: type
  };
  var i = 0;
  keys.forEach(function (key) {
    var field = _definitions.NODE_FIELDS[type][key];
    var arg;
    if (i < countArgs) arg = args[i];
    if (arg === undefined) arg = (0, _clone.default)(field.default);
    node[key] = arg;
    i++;
  });

  for (var key in node) {
    (0, _validate.default)(node, key, node[key]);
  }

  return node;
}

/***/ }),

/***/ 2305:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = assertNode;

var _isNode = _interopRequireDefault(__webpack_require__(2273));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function assertNode(node) {
  if (!(0, _isNode.default)(node)) {
    var type = node && node.type || JSON.stringify(node);
    throw new TypeError("Not a valid node of type \"" + type + "\"");
  }
}

/***/ }),

/***/ 2306:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.assertArrayExpression = assertArrayExpression;
exports.assertAssignmentExpression = assertAssignmentExpression;
exports.assertBinaryExpression = assertBinaryExpression;
exports.assertDirective = assertDirective;
exports.assertDirectiveLiteral = assertDirectiveLiteral;
exports.assertBlockStatement = assertBlockStatement;
exports.assertBreakStatement = assertBreakStatement;
exports.assertCallExpression = assertCallExpression;
exports.assertCatchClause = assertCatchClause;
exports.assertConditionalExpression = assertConditionalExpression;
exports.assertContinueStatement = assertContinueStatement;
exports.assertDebuggerStatement = assertDebuggerStatement;
exports.assertDoWhileStatement = assertDoWhileStatement;
exports.assertEmptyStatement = assertEmptyStatement;
exports.assertExpressionStatement = assertExpressionStatement;
exports.assertFile = assertFile;
exports.assertForInStatement = assertForInStatement;
exports.assertForStatement = assertForStatement;
exports.assertFunctionDeclaration = assertFunctionDeclaration;
exports.assertFunctionExpression = assertFunctionExpression;
exports.assertIdentifier = assertIdentifier;
exports.assertIfStatement = assertIfStatement;
exports.assertLabeledStatement = assertLabeledStatement;
exports.assertStringLiteral = assertStringLiteral;
exports.assertNumericLiteral = assertNumericLiteral;
exports.assertNullLiteral = assertNullLiteral;
exports.assertBooleanLiteral = assertBooleanLiteral;
exports.assertRegExpLiteral = assertRegExpLiteral;
exports.assertLogicalExpression = assertLogicalExpression;
exports.assertMemberExpression = assertMemberExpression;
exports.assertNewExpression = assertNewExpression;
exports.assertProgram = assertProgram;
exports.assertObjectExpression = assertObjectExpression;
exports.assertObjectMethod = assertObjectMethod;
exports.assertObjectProperty = assertObjectProperty;
exports.assertRestElement = assertRestElement;
exports.assertReturnStatement = assertReturnStatement;
exports.assertSequenceExpression = assertSequenceExpression;
exports.assertSwitchCase = assertSwitchCase;
exports.assertSwitchStatement = assertSwitchStatement;
exports.assertThisExpression = assertThisExpression;
exports.assertThrowStatement = assertThrowStatement;
exports.assertTryStatement = assertTryStatement;
exports.assertUnaryExpression = assertUnaryExpression;
exports.assertUpdateExpression = assertUpdateExpression;
exports.assertVariableDeclaration = assertVariableDeclaration;
exports.assertVariableDeclarator = assertVariableDeclarator;
exports.assertWhileStatement = assertWhileStatement;
exports.assertWithStatement = assertWithStatement;
exports.assertAssignmentPattern = assertAssignmentPattern;
exports.assertArrayPattern = assertArrayPattern;
exports.assertArrowFunctionExpression = assertArrowFunctionExpression;
exports.assertClassBody = assertClassBody;
exports.assertClassDeclaration = assertClassDeclaration;
exports.assertClassExpression = assertClassExpression;
exports.assertExportAllDeclaration = assertExportAllDeclaration;
exports.assertExportDefaultDeclaration = assertExportDefaultDeclaration;
exports.assertExportNamedDeclaration = assertExportNamedDeclaration;
exports.assertExportSpecifier = assertExportSpecifier;
exports.assertForOfStatement = assertForOfStatement;
exports.assertImportDeclaration = assertImportDeclaration;
exports.assertImportDefaultSpecifier = assertImportDefaultSpecifier;
exports.assertImportNamespaceSpecifier = assertImportNamespaceSpecifier;
exports.assertImportSpecifier = assertImportSpecifier;
exports.assertMetaProperty = assertMetaProperty;
exports.assertClassMethod = assertClassMethod;
exports.assertObjectPattern = assertObjectPattern;
exports.assertSpreadElement = assertSpreadElement;
exports.assertSuper = assertSuper;
exports.assertTaggedTemplateExpression = assertTaggedTemplateExpression;
exports.assertTemplateElement = assertTemplateElement;
exports.assertTemplateLiteral = assertTemplateLiteral;
exports.assertYieldExpression = assertYieldExpression;
exports.assertAnyTypeAnnotation = assertAnyTypeAnnotation;
exports.assertArrayTypeAnnotation = assertArrayTypeAnnotation;
exports.assertBooleanTypeAnnotation = assertBooleanTypeAnnotation;
exports.assertBooleanLiteralTypeAnnotation = assertBooleanLiteralTypeAnnotation;
exports.assertNullLiteralTypeAnnotation = assertNullLiteralTypeAnnotation;
exports.assertClassImplements = assertClassImplements;
exports.assertDeclareClass = assertDeclareClass;
exports.assertDeclareFunction = assertDeclareFunction;
exports.assertDeclareInterface = assertDeclareInterface;
exports.assertDeclareModule = assertDeclareModule;
exports.assertDeclareModuleExports = assertDeclareModuleExports;
exports.assertDeclareTypeAlias = assertDeclareTypeAlias;
exports.assertDeclareOpaqueType = assertDeclareOpaqueType;
exports.assertDeclareVariable = assertDeclareVariable;
exports.assertDeclareExportDeclaration = assertDeclareExportDeclaration;
exports.assertDeclareExportAllDeclaration = assertDeclareExportAllDeclaration;
exports.assertDeclaredPredicate = assertDeclaredPredicate;
exports.assertExistsTypeAnnotation = assertExistsTypeAnnotation;
exports.assertFunctionTypeAnnotation = assertFunctionTypeAnnotation;
exports.assertFunctionTypeParam = assertFunctionTypeParam;
exports.assertGenericTypeAnnotation = assertGenericTypeAnnotation;
exports.assertInferredPredicate = assertInferredPredicate;
exports.assertInterfaceExtends = assertInterfaceExtends;
exports.assertInterfaceDeclaration = assertInterfaceDeclaration;
exports.assertIntersectionTypeAnnotation = assertIntersectionTypeAnnotation;
exports.assertMixedTypeAnnotation = assertMixedTypeAnnotation;
exports.assertEmptyTypeAnnotation = assertEmptyTypeAnnotation;
exports.assertNullableTypeAnnotation = assertNullableTypeAnnotation;
exports.assertNumberLiteralTypeAnnotation = assertNumberLiteralTypeAnnotation;
exports.assertNumberTypeAnnotation = assertNumberTypeAnnotation;
exports.assertObjectTypeAnnotation = assertObjectTypeAnnotation;
exports.assertObjectTypeCallProperty = assertObjectTypeCallProperty;
exports.assertObjectTypeIndexer = assertObjectTypeIndexer;
exports.assertObjectTypeProperty = assertObjectTypeProperty;
exports.assertObjectTypeSpreadProperty = assertObjectTypeSpreadProperty;
exports.assertOpaqueType = assertOpaqueType;
exports.assertQualifiedTypeIdentifier = assertQualifiedTypeIdentifier;
exports.assertStringLiteralTypeAnnotation = assertStringLiteralTypeAnnotation;
exports.assertStringTypeAnnotation = assertStringTypeAnnotation;
exports.assertThisTypeAnnotation = assertThisTypeAnnotation;
exports.assertTupleTypeAnnotation = assertTupleTypeAnnotation;
exports.assertTypeofTypeAnnotation = assertTypeofTypeAnnotation;
exports.assertTypeAlias = assertTypeAlias;
exports.assertTypeAnnotation = assertTypeAnnotation;
exports.assertTypeCastExpression = assertTypeCastExpression;
exports.assertTypeParameter = assertTypeParameter;
exports.assertTypeParameterDeclaration = assertTypeParameterDeclaration;
exports.assertTypeParameterInstantiation = assertTypeParameterInstantiation;
exports.assertUnionTypeAnnotation = assertUnionTypeAnnotation;
exports.assertVariance = assertVariance;
exports.assertVoidTypeAnnotation = assertVoidTypeAnnotation;
exports.assertJSXAttribute = assertJSXAttribute;
exports.assertJSXClosingElement = assertJSXClosingElement;
exports.assertJSXElement = assertJSXElement;
exports.assertJSXEmptyExpression = assertJSXEmptyExpression;
exports.assertJSXExpressionContainer = assertJSXExpressionContainer;
exports.assertJSXSpreadChild = assertJSXSpreadChild;
exports.assertJSXIdentifier = assertJSXIdentifier;
exports.assertJSXMemberExpression = assertJSXMemberExpression;
exports.assertJSXNamespacedName = assertJSXNamespacedName;
exports.assertJSXOpeningElement = assertJSXOpeningElement;
exports.assertJSXSpreadAttribute = assertJSXSpreadAttribute;
exports.assertJSXText = assertJSXText;
exports.assertJSXFragment = assertJSXFragment;
exports.assertJSXOpeningFragment = assertJSXOpeningFragment;
exports.assertJSXClosingFragment = assertJSXClosingFragment;
exports.assertNoop = assertNoop;
exports.assertParenthesizedExpression = assertParenthesizedExpression;
exports.assertAwaitExpression = assertAwaitExpression;
exports.assertBindExpression = assertBindExpression;
exports.assertClassProperty = assertClassProperty;
exports.assertImport = assertImport;
exports.assertDecorator = assertDecorator;
exports.assertDoExpression = assertDoExpression;
exports.assertExportDefaultSpecifier = assertExportDefaultSpecifier;
exports.assertExportNamespaceSpecifier = assertExportNamespaceSpecifier;
exports.assertTSParameterProperty = assertTSParameterProperty;
exports.assertTSDeclareFunction = assertTSDeclareFunction;
exports.assertTSDeclareMethod = assertTSDeclareMethod;
exports.assertTSQualifiedName = assertTSQualifiedName;
exports.assertTSCallSignatureDeclaration = assertTSCallSignatureDeclaration;
exports.assertTSConstructSignatureDeclaration = assertTSConstructSignatureDeclaration;
exports.assertTSPropertySignature = assertTSPropertySignature;
exports.assertTSMethodSignature = assertTSMethodSignature;
exports.assertTSIndexSignature = assertTSIndexSignature;
exports.assertTSAnyKeyword = assertTSAnyKeyword;
exports.assertTSNumberKeyword = assertTSNumberKeyword;
exports.assertTSObjectKeyword = assertTSObjectKeyword;
exports.assertTSBooleanKeyword = assertTSBooleanKeyword;
exports.assertTSStringKeyword = assertTSStringKeyword;
exports.assertTSSymbolKeyword = assertTSSymbolKeyword;
exports.assertTSVoidKeyword = assertTSVoidKeyword;
exports.assertTSUndefinedKeyword = assertTSUndefinedKeyword;
exports.assertTSNullKeyword = assertTSNullKeyword;
exports.assertTSNeverKeyword = assertTSNeverKeyword;
exports.assertTSThisType = assertTSThisType;
exports.assertTSFunctionType = assertTSFunctionType;
exports.assertTSConstructorType = assertTSConstructorType;
exports.assertTSTypeReference = assertTSTypeReference;
exports.assertTSTypePredicate = assertTSTypePredicate;
exports.assertTSTypeQuery = assertTSTypeQuery;
exports.assertTSTypeLiteral = assertTSTypeLiteral;
exports.assertTSArrayType = assertTSArrayType;
exports.assertTSTupleType = assertTSTupleType;
exports.assertTSUnionType = assertTSUnionType;
exports.assertTSIntersectionType = assertTSIntersectionType;
exports.assertTSParenthesizedType = assertTSParenthesizedType;
exports.assertTSTypeOperator = assertTSTypeOperator;
exports.assertTSIndexedAccessType = assertTSIndexedAccessType;
exports.assertTSMappedType = assertTSMappedType;
exports.assertTSLiteralType = assertTSLiteralType;
exports.assertTSExpressionWithTypeArguments = assertTSExpressionWithTypeArguments;
exports.assertTSInterfaceDeclaration = assertTSInterfaceDeclaration;
exports.assertTSInterfaceBody = assertTSInterfaceBody;
exports.assertTSTypeAliasDeclaration = assertTSTypeAliasDeclaration;
exports.assertTSAsExpression = assertTSAsExpression;
exports.assertTSTypeAssertion = assertTSTypeAssertion;
exports.assertTSEnumDeclaration = assertTSEnumDeclaration;
exports.assertTSEnumMember = assertTSEnumMember;
exports.assertTSModuleDeclaration = assertTSModuleDeclaration;
exports.assertTSModuleBlock = assertTSModuleBlock;
exports.assertTSImportEqualsDeclaration = assertTSImportEqualsDeclaration;
exports.assertTSExternalModuleReference = assertTSExternalModuleReference;
exports.assertTSNonNullExpression = assertTSNonNullExpression;
exports.assertTSExportAssignment = assertTSExportAssignment;
exports.assertTSNamespaceExportDeclaration = assertTSNamespaceExportDeclaration;
exports.assertTSTypeAnnotation = assertTSTypeAnnotation;
exports.assertTSTypeParameterInstantiation = assertTSTypeParameterInstantiation;
exports.assertTSTypeParameterDeclaration = assertTSTypeParameterDeclaration;
exports.assertTSTypeParameter = assertTSTypeParameter;
exports.assertExpression = assertExpression;
exports.assertBinary = assertBinary;
exports.assertScopable = assertScopable;
exports.assertBlockParent = assertBlockParent;
exports.assertBlock = assertBlock;
exports.assertStatement = assertStatement;
exports.assertTerminatorless = assertTerminatorless;
exports.assertCompletionStatement = assertCompletionStatement;
exports.assertConditional = assertConditional;
exports.assertLoop = assertLoop;
exports.assertWhile = assertWhile;
exports.assertExpressionWrapper = assertExpressionWrapper;
exports.assertFor = assertFor;
exports.assertForXStatement = assertForXStatement;
exports.assertFunction = assertFunction;
exports.assertFunctionParent = assertFunctionParent;
exports.assertPureish = assertPureish;
exports.assertDeclaration = assertDeclaration;
exports.assertPatternLike = assertPatternLike;
exports.assertLVal = assertLVal;
exports.assertTSEntityName = assertTSEntityName;
exports.assertLiteral = assertLiteral;
exports.assertImmutable = assertImmutable;
exports.assertUserWhitespacable = assertUserWhitespacable;
exports.assertMethod = assertMethod;
exports.assertObjectMember = assertObjectMember;
exports.assertProperty = assertProperty;
exports.assertUnaryLike = assertUnaryLike;
exports.assertPattern = assertPattern;
exports.assertClass = assertClass;
exports.assertModuleDeclaration = assertModuleDeclaration;
exports.assertExportDeclaration = assertExportDeclaration;
exports.assertModuleSpecifier = assertModuleSpecifier;
exports.assertFlow = assertFlow;
exports.assertFlowType = assertFlowType;
exports.assertFlowBaseAnnotation = assertFlowBaseAnnotation;
exports.assertFlowDeclaration = assertFlowDeclaration;
exports.assertFlowPredicate = assertFlowPredicate;
exports.assertJSX = assertJSX;
exports.assertTSTypeElement = assertTSTypeElement;
exports.assertTSType = assertTSType;
exports.assertNumberLiteral = assertNumberLiteral;
exports.assertRegexLiteral = assertRegexLiteral;
exports.assertRestProperty = assertRestProperty;
exports.assertSpreadProperty = assertSpreadProperty;

var _is = _interopRequireDefault(__webpack_require__(2262));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function assert(type, node, opts) {
  if (!(0, _is.default)(type, node, opts)) {
    throw new Error("Expected type \"" + type + "\" with option " + JSON.stringify(opts) + ", but instead got \"" + node.type + "\".");
  }
}

function assertArrayExpression(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ArrayExpression", node, opts);
}

function assertAssignmentExpression(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("AssignmentExpression", node, opts);
}

function assertBinaryExpression(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("BinaryExpression", node, opts);
}

function assertDirective(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("Directive", node, opts);
}

function assertDirectiveLiteral(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("DirectiveLiteral", node, opts);
}

function assertBlockStatement(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("BlockStatement", node, opts);
}

function assertBreakStatement(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("BreakStatement", node, opts);
}

function assertCallExpression(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("CallExpression", node, opts);
}

function assertCatchClause(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("CatchClause", node, opts);
}

function assertConditionalExpression(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ConditionalExpression", node, opts);
}

function assertContinueStatement(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ContinueStatement", node, opts);
}

function assertDebuggerStatement(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("DebuggerStatement", node, opts);
}

function assertDoWhileStatement(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("DoWhileStatement", node, opts);
}

function assertEmptyStatement(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("EmptyStatement", node, opts);
}

function assertExpressionStatement(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ExpressionStatement", node, opts);
}

function assertFile(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("File", node, opts);
}

function assertForInStatement(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ForInStatement", node, opts);
}

function assertForStatement(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ForStatement", node, opts);
}

function assertFunctionDeclaration(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("FunctionDeclaration", node, opts);
}

function assertFunctionExpression(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("FunctionExpression", node, opts);
}

function assertIdentifier(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("Identifier", node, opts);
}

function assertIfStatement(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("IfStatement", node, opts);
}

function assertLabeledStatement(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("LabeledStatement", node, opts);
}

function assertStringLiteral(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("StringLiteral", node, opts);
}

function assertNumericLiteral(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("NumericLiteral", node, opts);
}

function assertNullLiteral(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("NullLiteral", node, opts);
}

function assertBooleanLiteral(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("BooleanLiteral", node, opts);
}

function assertRegExpLiteral(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("RegExpLiteral", node, opts);
}

function assertLogicalExpression(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("LogicalExpression", node, opts);
}

function assertMemberExpression(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("MemberExpression", node, opts);
}

function assertNewExpression(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("NewExpression", node, opts);
}

function assertProgram(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("Program", node, opts);
}

function assertObjectExpression(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ObjectExpression", node, opts);
}

function assertObjectMethod(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ObjectMethod", node, opts);
}

function assertObjectProperty(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ObjectProperty", node, opts);
}

function assertRestElement(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("RestElement", node, opts);
}

function assertReturnStatement(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ReturnStatement", node, opts);
}

function assertSequenceExpression(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("SequenceExpression", node, opts);
}

function assertSwitchCase(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("SwitchCase", node, opts);
}

function assertSwitchStatement(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("SwitchStatement", node, opts);
}

function assertThisExpression(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ThisExpression", node, opts);
}

function assertThrowStatement(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ThrowStatement", node, opts);
}

function assertTryStatement(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TryStatement", node, opts);
}

function assertUnaryExpression(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("UnaryExpression", node, opts);
}

function assertUpdateExpression(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("UpdateExpression", node, opts);
}

function assertVariableDeclaration(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("VariableDeclaration", node, opts);
}

function assertVariableDeclarator(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("VariableDeclarator", node, opts);
}

function assertWhileStatement(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("WhileStatement", node, opts);
}

function assertWithStatement(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("WithStatement", node, opts);
}

function assertAssignmentPattern(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("AssignmentPattern", node, opts);
}

function assertArrayPattern(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ArrayPattern", node, opts);
}

function assertArrowFunctionExpression(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ArrowFunctionExpression", node, opts);
}

function assertClassBody(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ClassBody", node, opts);
}

function assertClassDeclaration(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ClassDeclaration", node, opts);
}

function assertClassExpression(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ClassExpression", node, opts);
}

function assertExportAllDeclaration(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ExportAllDeclaration", node, opts);
}

function assertExportDefaultDeclaration(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ExportDefaultDeclaration", node, opts);
}

function assertExportNamedDeclaration(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ExportNamedDeclaration", node, opts);
}

function assertExportSpecifier(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ExportSpecifier", node, opts);
}

function assertForOfStatement(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ForOfStatement", node, opts);
}

function assertImportDeclaration(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ImportDeclaration", node, opts);
}

function assertImportDefaultSpecifier(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ImportDefaultSpecifier", node, opts);
}

function assertImportNamespaceSpecifier(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ImportNamespaceSpecifier", node, opts);
}

function assertImportSpecifier(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ImportSpecifier", node, opts);
}

function assertMetaProperty(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("MetaProperty", node, opts);
}

function assertClassMethod(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ClassMethod", node, opts);
}

function assertObjectPattern(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ObjectPattern", node, opts);
}

function assertSpreadElement(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("SpreadElement", node, opts);
}

function assertSuper(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("Super", node, opts);
}

function assertTaggedTemplateExpression(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TaggedTemplateExpression", node, opts);
}

function assertTemplateElement(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TemplateElement", node, opts);
}

function assertTemplateLiteral(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TemplateLiteral", node, opts);
}

function assertYieldExpression(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("YieldExpression", node, opts);
}

function assertAnyTypeAnnotation(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("AnyTypeAnnotation", node, opts);
}

function assertArrayTypeAnnotation(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ArrayTypeAnnotation", node, opts);
}

function assertBooleanTypeAnnotation(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("BooleanTypeAnnotation", node, opts);
}

function assertBooleanLiteralTypeAnnotation(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("BooleanLiteralTypeAnnotation", node, opts);
}

function assertNullLiteralTypeAnnotation(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("NullLiteralTypeAnnotation", node, opts);
}

function assertClassImplements(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ClassImplements", node, opts);
}

function assertDeclareClass(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("DeclareClass", node, opts);
}

function assertDeclareFunction(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("DeclareFunction", node, opts);
}

function assertDeclareInterface(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("DeclareInterface", node, opts);
}

function assertDeclareModule(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("DeclareModule", node, opts);
}

function assertDeclareModuleExports(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("DeclareModuleExports", node, opts);
}

function assertDeclareTypeAlias(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("DeclareTypeAlias", node, opts);
}

function assertDeclareOpaqueType(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("DeclareOpaqueType", node, opts);
}

function assertDeclareVariable(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("DeclareVariable", node, opts);
}

function assertDeclareExportDeclaration(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("DeclareExportDeclaration", node, opts);
}

function assertDeclareExportAllDeclaration(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("DeclareExportAllDeclaration", node, opts);
}

function assertDeclaredPredicate(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("DeclaredPredicate", node, opts);
}

function assertExistsTypeAnnotation(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ExistsTypeAnnotation", node, opts);
}

function assertFunctionTypeAnnotation(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("FunctionTypeAnnotation", node, opts);
}

function assertFunctionTypeParam(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("FunctionTypeParam", node, opts);
}

function assertGenericTypeAnnotation(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("GenericTypeAnnotation", node, opts);
}

function assertInferredPredicate(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("InferredPredicate", node, opts);
}

function assertInterfaceExtends(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("InterfaceExtends", node, opts);
}

function assertInterfaceDeclaration(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("InterfaceDeclaration", node, opts);
}

function assertIntersectionTypeAnnotation(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("IntersectionTypeAnnotation", node, opts);
}

function assertMixedTypeAnnotation(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("MixedTypeAnnotation", node, opts);
}

function assertEmptyTypeAnnotation(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("EmptyTypeAnnotation", node, opts);
}

function assertNullableTypeAnnotation(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("NullableTypeAnnotation", node, opts);
}

function assertNumberLiteralTypeAnnotation(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("NumberLiteralTypeAnnotation", node, opts);
}

function assertNumberTypeAnnotation(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("NumberTypeAnnotation", node, opts);
}

function assertObjectTypeAnnotation(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ObjectTypeAnnotation", node, opts);
}

function assertObjectTypeCallProperty(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ObjectTypeCallProperty", node, opts);
}

function assertObjectTypeIndexer(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ObjectTypeIndexer", node, opts);
}

function assertObjectTypeProperty(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ObjectTypeProperty", node, opts);
}

function assertObjectTypeSpreadProperty(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ObjectTypeSpreadProperty", node, opts);
}

function assertOpaqueType(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("OpaqueType", node, opts);
}

function assertQualifiedTypeIdentifier(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("QualifiedTypeIdentifier", node, opts);
}

function assertStringLiteralTypeAnnotation(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("StringLiteralTypeAnnotation", node, opts);
}

function assertStringTypeAnnotation(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("StringTypeAnnotation", node, opts);
}

function assertThisTypeAnnotation(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ThisTypeAnnotation", node, opts);
}

function assertTupleTypeAnnotation(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TupleTypeAnnotation", node, opts);
}

function assertTypeofTypeAnnotation(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TypeofTypeAnnotation", node, opts);
}

function assertTypeAlias(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TypeAlias", node, opts);
}

function assertTypeAnnotation(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TypeAnnotation", node, opts);
}

function assertTypeCastExpression(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TypeCastExpression", node, opts);
}

function assertTypeParameter(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TypeParameter", node, opts);
}

function assertTypeParameterDeclaration(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TypeParameterDeclaration", node, opts);
}

function assertTypeParameterInstantiation(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TypeParameterInstantiation", node, opts);
}

function assertUnionTypeAnnotation(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("UnionTypeAnnotation", node, opts);
}

function assertVariance(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("Variance", node, opts);
}

function assertVoidTypeAnnotation(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("VoidTypeAnnotation", node, opts);
}

function assertJSXAttribute(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("JSXAttribute", node, opts);
}

function assertJSXClosingElement(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("JSXClosingElement", node, opts);
}

function assertJSXElement(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("JSXElement", node, opts);
}

function assertJSXEmptyExpression(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("JSXEmptyExpression", node, opts);
}

function assertJSXExpressionContainer(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("JSXExpressionContainer", node, opts);
}

function assertJSXSpreadChild(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("JSXSpreadChild", node, opts);
}

function assertJSXIdentifier(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("JSXIdentifier", node, opts);
}

function assertJSXMemberExpression(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("JSXMemberExpression", node, opts);
}

function assertJSXNamespacedName(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("JSXNamespacedName", node, opts);
}

function assertJSXOpeningElement(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("JSXOpeningElement", node, opts);
}

function assertJSXSpreadAttribute(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("JSXSpreadAttribute", node, opts);
}

function assertJSXText(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("JSXText", node, opts);
}

function assertJSXFragment(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("JSXFragment", node, opts);
}

function assertJSXOpeningFragment(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("JSXOpeningFragment", node, opts);
}

function assertJSXClosingFragment(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("JSXClosingFragment", node, opts);
}

function assertNoop(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("Noop", node, opts);
}

function assertParenthesizedExpression(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ParenthesizedExpression", node, opts);
}

function assertAwaitExpression(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("AwaitExpression", node, opts);
}

function assertBindExpression(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("BindExpression", node, opts);
}

function assertClassProperty(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ClassProperty", node, opts);
}

function assertImport(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("Import", node, opts);
}

function assertDecorator(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("Decorator", node, opts);
}

function assertDoExpression(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("DoExpression", node, opts);
}

function assertExportDefaultSpecifier(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ExportDefaultSpecifier", node, opts);
}

function assertExportNamespaceSpecifier(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ExportNamespaceSpecifier", node, opts);
}

function assertTSParameterProperty(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSParameterProperty", node, opts);
}

function assertTSDeclareFunction(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSDeclareFunction", node, opts);
}

function assertTSDeclareMethod(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSDeclareMethod", node, opts);
}

function assertTSQualifiedName(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSQualifiedName", node, opts);
}

function assertTSCallSignatureDeclaration(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSCallSignatureDeclaration", node, opts);
}

function assertTSConstructSignatureDeclaration(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSConstructSignatureDeclaration", node, opts);
}

function assertTSPropertySignature(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSPropertySignature", node, opts);
}

function assertTSMethodSignature(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSMethodSignature", node, opts);
}

function assertTSIndexSignature(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSIndexSignature", node, opts);
}

function assertTSAnyKeyword(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSAnyKeyword", node, opts);
}

function assertTSNumberKeyword(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSNumberKeyword", node, opts);
}

function assertTSObjectKeyword(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSObjectKeyword", node, opts);
}

function assertTSBooleanKeyword(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSBooleanKeyword", node, opts);
}

function assertTSStringKeyword(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSStringKeyword", node, opts);
}

function assertTSSymbolKeyword(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSSymbolKeyword", node, opts);
}

function assertTSVoidKeyword(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSVoidKeyword", node, opts);
}

function assertTSUndefinedKeyword(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSUndefinedKeyword", node, opts);
}

function assertTSNullKeyword(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSNullKeyword", node, opts);
}

function assertTSNeverKeyword(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSNeverKeyword", node, opts);
}

function assertTSThisType(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSThisType", node, opts);
}

function assertTSFunctionType(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSFunctionType", node, opts);
}

function assertTSConstructorType(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSConstructorType", node, opts);
}

function assertTSTypeReference(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSTypeReference", node, opts);
}

function assertTSTypePredicate(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSTypePredicate", node, opts);
}

function assertTSTypeQuery(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSTypeQuery", node, opts);
}

function assertTSTypeLiteral(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSTypeLiteral", node, opts);
}

function assertTSArrayType(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSArrayType", node, opts);
}

function assertTSTupleType(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSTupleType", node, opts);
}

function assertTSUnionType(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSUnionType", node, opts);
}

function assertTSIntersectionType(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSIntersectionType", node, opts);
}

function assertTSParenthesizedType(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSParenthesizedType", node, opts);
}

function assertTSTypeOperator(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSTypeOperator", node, opts);
}

function assertTSIndexedAccessType(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSIndexedAccessType", node, opts);
}

function assertTSMappedType(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSMappedType", node, opts);
}

function assertTSLiteralType(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSLiteralType", node, opts);
}

function assertTSExpressionWithTypeArguments(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSExpressionWithTypeArguments", node, opts);
}

function assertTSInterfaceDeclaration(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSInterfaceDeclaration", node, opts);
}

function assertTSInterfaceBody(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSInterfaceBody", node, opts);
}

function assertTSTypeAliasDeclaration(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSTypeAliasDeclaration", node, opts);
}

function assertTSAsExpression(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSAsExpression", node, opts);
}

function assertTSTypeAssertion(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSTypeAssertion", node, opts);
}

function assertTSEnumDeclaration(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSEnumDeclaration", node, opts);
}

function assertTSEnumMember(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSEnumMember", node, opts);
}

function assertTSModuleDeclaration(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSModuleDeclaration", node, opts);
}

function assertTSModuleBlock(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSModuleBlock", node, opts);
}

function assertTSImportEqualsDeclaration(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSImportEqualsDeclaration", node, opts);
}

function assertTSExternalModuleReference(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSExternalModuleReference", node, opts);
}

function assertTSNonNullExpression(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSNonNullExpression", node, opts);
}

function assertTSExportAssignment(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSExportAssignment", node, opts);
}

function assertTSNamespaceExportDeclaration(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSNamespaceExportDeclaration", node, opts);
}

function assertTSTypeAnnotation(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSTypeAnnotation", node, opts);
}

function assertTSTypeParameterInstantiation(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSTypeParameterInstantiation", node, opts);
}

function assertTSTypeParameterDeclaration(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSTypeParameterDeclaration", node, opts);
}

function assertTSTypeParameter(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSTypeParameter", node, opts);
}

function assertExpression(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("Expression", node, opts);
}

function assertBinary(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("Binary", node, opts);
}

function assertScopable(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("Scopable", node, opts);
}

function assertBlockParent(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("BlockParent", node, opts);
}

function assertBlock(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("Block", node, opts);
}

function assertStatement(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("Statement", node, opts);
}

function assertTerminatorless(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("Terminatorless", node, opts);
}

function assertCompletionStatement(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("CompletionStatement", node, opts);
}

function assertConditional(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("Conditional", node, opts);
}

function assertLoop(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("Loop", node, opts);
}

function assertWhile(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("While", node, opts);
}

function assertExpressionWrapper(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ExpressionWrapper", node, opts);
}

function assertFor(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("For", node, opts);
}

function assertForXStatement(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ForXStatement", node, opts);
}

function assertFunction(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("Function", node, opts);
}

function assertFunctionParent(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("FunctionParent", node, opts);
}

function assertPureish(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("Pureish", node, opts);
}

function assertDeclaration(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("Declaration", node, opts);
}

function assertPatternLike(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("PatternLike", node, opts);
}

function assertLVal(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("LVal", node, opts);
}

function assertTSEntityName(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSEntityName", node, opts);
}

function assertLiteral(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("Literal", node, opts);
}

function assertImmutable(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("Immutable", node, opts);
}

function assertUserWhitespacable(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("UserWhitespacable", node, opts);
}

function assertMethod(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("Method", node, opts);
}

function assertObjectMember(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ObjectMember", node, opts);
}

function assertProperty(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("Property", node, opts);
}

function assertUnaryLike(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("UnaryLike", node, opts);
}

function assertPattern(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("Pattern", node, opts);
}

function assertClass(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("Class", node, opts);
}

function assertModuleDeclaration(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ModuleDeclaration", node, opts);
}

function assertExportDeclaration(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ExportDeclaration", node, opts);
}

function assertModuleSpecifier(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("ModuleSpecifier", node, opts);
}

function assertFlow(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("Flow", node, opts);
}

function assertFlowType(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("FlowType", node, opts);
}

function assertFlowBaseAnnotation(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("FlowBaseAnnotation", node, opts);
}

function assertFlowDeclaration(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("FlowDeclaration", node, opts);
}

function assertFlowPredicate(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("FlowPredicate", node, opts);
}

function assertJSX(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("JSX", node, opts);
}

function assertTSTypeElement(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSTypeElement", node, opts);
}

function assertTSType(node, opts) {
  if (opts === void 0) {
    opts = {};
  }

  assert("TSType", node, opts);
}

function assertNumberLiteral(node, opts) {
  console.trace("The node type NumberLiteral has been renamed to NumericLiteral");
  assert("NumberLiteral", node, opts);
}

function assertRegexLiteral(node, opts) {
  console.trace("The node type RegexLiteral has been renamed to RegExpLiteral");
  assert("RegexLiteral", node, opts);
}

function assertRestProperty(node, opts) {
  console.trace("The node type RestProperty has been renamed to RestElement");
  assert("RestProperty", node, opts);
}

function assertSpreadProperty(node, opts) {
  console.trace("The node type SpreadProperty has been renamed to SpreadElement");
  assert("SpreadProperty", node, opts);
}

/***/ }),

/***/ 2307:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = createTypeAnnotationBasedOnTypeof;

var _generated = __webpack_require__(2256);

function createTypeAnnotationBasedOnTypeof(type) {
  if (type === "string") {
    return (0, _generated.stringTypeAnnotation)();
  } else if (type === "number") {
    return (0, _generated.numberTypeAnnotation)();
  } else if (type === "undefined") {
    return (0, _generated.voidTypeAnnotation)();
  } else if (type === "boolean") {
    return (0, _generated.booleanTypeAnnotation)();
  } else if (type === "function") {
    return (0, _generated.genericTypeAnnotation)((0, _generated.identifier)("Function"));
  } else if (type === "object") {
    return (0, _generated.genericTypeAnnotation)((0, _generated.identifier)("Object"));
  } else if (type === "symbol") {
    return (0, _generated.genericTypeAnnotation)((0, _generated.identifier)("Symbol"));
  } else {
    throw new Error("Invalid typeof value");
  }
}

/***/ }),

/***/ 2308:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = createUnionTypeAnnotation;

var _generated = __webpack_require__(2256);

var _removeTypeDuplicates = _interopRequireDefault(__webpack_require__(2274));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function createUnionTypeAnnotation(types) {
  var flattened = (0, _removeTypeDuplicates.default)(types);

  if (flattened.length === 1) {
    return flattened[0];
  } else {
    return (0, _generated.unionTypeAnnotation)(flattened);
  }
}

/***/ }),

/***/ 2309:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = cloneDeep;

var _cloneNode = _interopRequireDefault(__webpack_require__(2261));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function cloneDeep(node) {
  return (0, _cloneNode.default)(node);
}

/***/ }),

/***/ 2310:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = cloneWithoutLoc;

var _clone = _interopRequireDefault(__webpack_require__(2275));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function cloneWithoutLoc(node) {
  var newNode = (0, _clone.default)(node);
  newNode.loc = null;
  return newNode;
}

/***/ }),

/***/ 2311:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = addComment;

var _addComments = _interopRequireDefault(__webpack_require__(2276));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function addComment(node, type, content, line) {
  return (0, _addComments.default)(node, type, [{
    type: line ? "CommentLine" : "CommentBlock",
    value: content
  }]);
}

/***/ }),

/***/ 2312:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = removeComments;

var _constants = __webpack_require__(2259);

function removeComments(node) {
  _constants.COMMENT_KEYS.forEach(function (key) {
    node[key] = null;
  });

  return node;
}

/***/ }),

/***/ 2313:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.TSTYPE_TYPES = exports.TSTYPEELEMENT_TYPES = exports.JSX_TYPES = exports.FLOWPREDICATE_TYPES = exports.FLOWDECLARATION_TYPES = exports.FLOWBASEANNOTATION_TYPES = exports.FLOWTYPE_TYPES = exports.FLOW_TYPES = exports.MODULESPECIFIER_TYPES = exports.EXPORTDECLARATION_TYPES = exports.MODULEDECLARATION_TYPES = exports.CLASS_TYPES = exports.PATTERN_TYPES = exports.UNARYLIKE_TYPES = exports.PROPERTY_TYPES = exports.OBJECTMEMBER_TYPES = exports.METHOD_TYPES = exports.USERWHITESPACABLE_TYPES = exports.IMMUTABLE_TYPES = exports.LITERAL_TYPES = exports.TSENTITYNAME_TYPES = exports.LVAL_TYPES = exports.PATTERNLIKE_TYPES = exports.DECLARATION_TYPES = exports.PUREISH_TYPES = exports.FUNCTIONPARENT_TYPES = exports.FUNCTION_TYPES = exports.FORXSTATEMENT_TYPES = exports.FOR_TYPES = exports.EXPRESSIONWRAPPER_TYPES = exports.WHILE_TYPES = exports.LOOP_TYPES = exports.CONDITIONAL_TYPES = exports.COMPLETIONSTATEMENT_TYPES = exports.TERMINATORLESS_TYPES = exports.STATEMENT_TYPES = exports.BLOCK_TYPES = exports.BLOCKPARENT_TYPES = exports.SCOPABLE_TYPES = exports.BINARY_TYPES = exports.EXPRESSION_TYPES = void 0;

var _definitions = __webpack_require__(2257);

var EXPRESSION_TYPES = _definitions.FLIPPED_ALIAS_KEYS["Expression"];
exports.EXPRESSION_TYPES = EXPRESSION_TYPES;
var BINARY_TYPES = _definitions.FLIPPED_ALIAS_KEYS["Binary"];
exports.BINARY_TYPES = BINARY_TYPES;
var SCOPABLE_TYPES = _definitions.FLIPPED_ALIAS_KEYS["Scopable"];
exports.SCOPABLE_TYPES = SCOPABLE_TYPES;
var BLOCKPARENT_TYPES = _definitions.FLIPPED_ALIAS_KEYS["BlockParent"];
exports.BLOCKPARENT_TYPES = BLOCKPARENT_TYPES;
var BLOCK_TYPES = _definitions.FLIPPED_ALIAS_KEYS["Block"];
exports.BLOCK_TYPES = BLOCK_TYPES;
var STATEMENT_TYPES = _definitions.FLIPPED_ALIAS_KEYS["Statement"];
exports.STATEMENT_TYPES = STATEMENT_TYPES;
var TERMINATORLESS_TYPES = _definitions.FLIPPED_ALIAS_KEYS["Terminatorless"];
exports.TERMINATORLESS_TYPES = TERMINATORLESS_TYPES;
var COMPLETIONSTATEMENT_TYPES = _definitions.FLIPPED_ALIAS_KEYS["CompletionStatement"];
exports.COMPLETIONSTATEMENT_TYPES = COMPLETIONSTATEMENT_TYPES;
var CONDITIONAL_TYPES = _definitions.FLIPPED_ALIAS_KEYS["Conditional"];
exports.CONDITIONAL_TYPES = CONDITIONAL_TYPES;
var LOOP_TYPES = _definitions.FLIPPED_ALIAS_KEYS["Loop"];
exports.LOOP_TYPES = LOOP_TYPES;
var WHILE_TYPES = _definitions.FLIPPED_ALIAS_KEYS["While"];
exports.WHILE_TYPES = WHILE_TYPES;
var EXPRESSIONWRAPPER_TYPES = _definitions.FLIPPED_ALIAS_KEYS["ExpressionWrapper"];
exports.EXPRESSIONWRAPPER_TYPES = EXPRESSIONWRAPPER_TYPES;
var FOR_TYPES = _definitions.FLIPPED_ALIAS_KEYS["For"];
exports.FOR_TYPES = FOR_TYPES;
var FORXSTATEMENT_TYPES = _definitions.FLIPPED_ALIAS_KEYS["ForXStatement"];
exports.FORXSTATEMENT_TYPES = FORXSTATEMENT_TYPES;
var FUNCTION_TYPES = _definitions.FLIPPED_ALIAS_KEYS["Function"];
exports.FUNCTION_TYPES = FUNCTION_TYPES;
var FUNCTIONPARENT_TYPES = _definitions.FLIPPED_ALIAS_KEYS["FunctionParent"];
exports.FUNCTIONPARENT_TYPES = FUNCTIONPARENT_TYPES;
var PUREISH_TYPES = _definitions.FLIPPED_ALIAS_KEYS["Pureish"];
exports.PUREISH_TYPES = PUREISH_TYPES;
var DECLARATION_TYPES = _definitions.FLIPPED_ALIAS_KEYS["Declaration"];
exports.DECLARATION_TYPES = DECLARATION_TYPES;
var PATTERNLIKE_TYPES = _definitions.FLIPPED_ALIAS_KEYS["PatternLike"];
exports.PATTERNLIKE_TYPES = PATTERNLIKE_TYPES;
var LVAL_TYPES = _definitions.FLIPPED_ALIAS_KEYS["LVal"];
exports.LVAL_TYPES = LVAL_TYPES;
var TSENTITYNAME_TYPES = _definitions.FLIPPED_ALIAS_KEYS["TSEntityName"];
exports.TSENTITYNAME_TYPES = TSENTITYNAME_TYPES;
var LITERAL_TYPES = _definitions.FLIPPED_ALIAS_KEYS["Literal"];
exports.LITERAL_TYPES = LITERAL_TYPES;
var IMMUTABLE_TYPES = _definitions.FLIPPED_ALIAS_KEYS["Immutable"];
exports.IMMUTABLE_TYPES = IMMUTABLE_TYPES;
var USERWHITESPACABLE_TYPES = _definitions.FLIPPED_ALIAS_KEYS["UserWhitespacable"];
exports.USERWHITESPACABLE_TYPES = USERWHITESPACABLE_TYPES;
var METHOD_TYPES = _definitions.FLIPPED_ALIAS_KEYS["Method"];
exports.METHOD_TYPES = METHOD_TYPES;
var OBJECTMEMBER_TYPES = _definitions.FLIPPED_ALIAS_KEYS["ObjectMember"];
exports.OBJECTMEMBER_TYPES = OBJECTMEMBER_TYPES;
var PROPERTY_TYPES = _definitions.FLIPPED_ALIAS_KEYS["Property"];
exports.PROPERTY_TYPES = PROPERTY_TYPES;
var UNARYLIKE_TYPES = _definitions.FLIPPED_ALIAS_KEYS["UnaryLike"];
exports.UNARYLIKE_TYPES = UNARYLIKE_TYPES;
var PATTERN_TYPES = _definitions.FLIPPED_ALIAS_KEYS["Pattern"];
exports.PATTERN_TYPES = PATTERN_TYPES;
var CLASS_TYPES = _definitions.FLIPPED_ALIAS_KEYS["Class"];
exports.CLASS_TYPES = CLASS_TYPES;
var MODULEDECLARATION_TYPES = _definitions.FLIPPED_ALIAS_KEYS["ModuleDeclaration"];
exports.MODULEDECLARATION_TYPES = MODULEDECLARATION_TYPES;
var EXPORTDECLARATION_TYPES = _definitions.FLIPPED_ALIAS_KEYS["ExportDeclaration"];
exports.EXPORTDECLARATION_TYPES = EXPORTDECLARATION_TYPES;
var MODULESPECIFIER_TYPES = _definitions.FLIPPED_ALIAS_KEYS["ModuleSpecifier"];
exports.MODULESPECIFIER_TYPES = MODULESPECIFIER_TYPES;
var FLOW_TYPES = _definitions.FLIPPED_ALIAS_KEYS["Flow"];
exports.FLOW_TYPES = FLOW_TYPES;
var FLOWTYPE_TYPES = _definitions.FLIPPED_ALIAS_KEYS["FlowType"];
exports.FLOWTYPE_TYPES = FLOWTYPE_TYPES;
var FLOWBASEANNOTATION_TYPES = _definitions.FLIPPED_ALIAS_KEYS["FlowBaseAnnotation"];
exports.FLOWBASEANNOTATION_TYPES = FLOWBASEANNOTATION_TYPES;
var FLOWDECLARATION_TYPES = _definitions.FLIPPED_ALIAS_KEYS["FlowDeclaration"];
exports.FLOWDECLARATION_TYPES = FLOWDECLARATION_TYPES;
var FLOWPREDICATE_TYPES = _definitions.FLIPPED_ALIAS_KEYS["FlowPredicate"];
exports.FLOWPREDICATE_TYPES = FLOWPREDICATE_TYPES;
var JSX_TYPES = _definitions.FLIPPED_ALIAS_KEYS["JSX"];
exports.JSX_TYPES = JSX_TYPES;
var TSTYPEELEMENT_TYPES = _definitions.FLIPPED_ALIAS_KEYS["TSTypeElement"];
exports.TSTYPEELEMENT_TYPES = TSTYPEELEMENT_TYPES;
var TSTYPE_TYPES = _definitions.FLIPPED_ALIAS_KEYS["TSType"];
exports.TSTYPE_TYPES = TSTYPE_TYPES;

/***/ }),

/***/ 2314:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = ensureBlock;

var _toBlock = _interopRequireDefault(__webpack_require__(2281));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function ensureBlock(node, key) {
  if (key === void 0) {
    key = "body";
  }

  return node[key] = (0, _toBlock.default)(node[key], node);
}

/***/ }),

/***/ 2315:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = toBindingIdentifierName;

var _toIdentifier = _interopRequireDefault(__webpack_require__(2282));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function toBindingIdentifierName(name) {
  name = (0, _toIdentifier.default)(name);
  if (name === "eval" || name === "arguments") name = "_" + name;
  return name;
}

/***/ }),

/***/ 2316:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = toComputedKey;

var _generated = __webpack_require__(2255);

var _generated2 = __webpack_require__(2256);

function toComputedKey(node, key) {
  if (key === void 0) {
    key = node.key || node.property;
  }

  if (!node.computed && (0, _generated.isIdentifier)(key)) key = (0, _generated2.stringLiteral)(key.name);
  return key;
}

/***/ }),

/***/ 2317:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = toExpression;

var _generated = __webpack_require__(2255);

function toExpression(node) {
  if ((0, _generated.isExpressionStatement)(node)) {
    node = node.expression;
  }

  if ((0, _generated.isExpression)(node)) {
    return node;
  }

  if ((0, _generated.isClass)(node)) {
    node.type = "ClassExpression";
  } else if ((0, _generated.isFunction)(node)) {
    node.type = "FunctionExpression";
  }

  if (!(0, _generated.isExpression)(node)) {
    throw new Error("cannot turn " + node.type + " to an expression");
  }

  return node;
}

/***/ }),

/***/ 2318:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = toKeyAlias;

var _generated = __webpack_require__(2255);

var _cloneNode = _interopRequireDefault(__webpack_require__(2261));

var _removePropertiesDeep = _interopRequireDefault(__webpack_require__(2283));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function toKeyAlias(node, key) {
  if (key === void 0) {
    key = node.key;
  }

  var alias;

  if (node.kind === "method") {
    return toKeyAlias.increment() + "";
  } else if ((0, _generated.isIdentifier)(key)) {
    alias = key.name;
  } else if ((0, _generated.isStringLiteral)(key)) {
    alias = JSON.stringify(key.value);
  } else {
    alias = JSON.stringify((0, _removePropertiesDeep.default)((0, _cloneNode.default)(key)));
  }

  if (node.computed) {
    alias = "[" + alias + "]";
  }

  if (node.static) {
    alias = "static:" + alias;
  }

  return alias;
}

toKeyAlias.uid = 0;

toKeyAlias.increment = function () {
  if (toKeyAlias.uid >= Number.MAX_SAFE_INTEGER) {
    return toKeyAlias.uid = 0;
  } else {
    return toKeyAlias.uid++;
  }
};

/***/ }),

/***/ 2319:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = toSequenceExpression;

var _gatherSequenceExpressions = _interopRequireDefault(__webpack_require__(2320));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function toSequenceExpression(nodes, scope) {
  if (!nodes || !nodes.length) return;
  var declars = [];
  var result = (0, _gatherSequenceExpressions.default)(nodes, scope, declars);
  if (!result) return;

  for (var _i = 0; _i < declars.length; _i++) {
    var declar = declars[_i];
    scope.push(declar);
  }

  return result;
}

/***/ }),

/***/ 2320:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = gatherSequenceExpressions;

var _getBindingIdentifiers = _interopRequireDefault(__webpack_require__(2263));

var _generated = __webpack_require__(2255);

var _generated2 = __webpack_require__(2256);

var _cloneNode = _interopRequireDefault(__webpack_require__(2261));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function gatherSequenceExpressions(nodes, scope, declars) {
  var exprs = [];
  var ensureLastUndefined = true;

  for (var _iterator = nodes, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {
    var _ref;

    if (_isArray) {
      if (_i >= _iterator.length) break;
      _ref = _iterator[_i++];
    } else {
      _i = _iterator.next();
      if (_i.done) break;
      _ref = _i.value;
    }

    var _node = _ref;
    ensureLastUndefined = false;

    if ((0, _generated.isExpression)(_node)) {
      exprs.push(_node);
    } else if ((0, _generated.isExpressionStatement)(_node)) {
      exprs.push(_node.expression);
    } else if ((0, _generated.isVariableDeclaration)(_node)) {
      if (_node.kind !== "var") return;
      var _arr = _node.declarations;

      for (var _i2 = 0; _i2 < _arr.length; _i2++) {
        var declar = _arr[_i2];
        var bindings = (0, _getBindingIdentifiers.default)(declar);

        for (var key in bindings) {
          declars.push({
            kind: _node.kind,
            id: (0, _cloneNode.default)(bindings[key])
          });
        }

        if (declar.init) {
          exprs.push((0, _generated2.assignmentExpression)("=", declar.id, declar.init));
        }
      }

      ensureLastUndefined = true;
    } else if ((0, _generated.isIfStatement)(_node)) {
      var consequent = _node.consequent ? gatherSequenceExpressions([_node.consequent], scope, declars) : scope.buildUndefinedNode();
      var alternate = _node.alternate ? gatherSequenceExpressions([_node.alternate], scope, declars) : scope.buildUndefinedNode();
      if (!consequent || !alternate) return;
      exprs.push((0, _generated2.conditionalExpression)(_node.test, consequent, alternate));
    } else if ((0, _generated.isBlockStatement)(_node)) {
      var body = gatherSequenceExpressions(_node.body, scope, declars);
      if (!body) return;
      exprs.push(body);
    } else if ((0, _generated.isEmptyStatement)(_node)) {
      ensureLastUndefined = true;
    } else {
      return;
    }
  }

  if (ensureLastUndefined) {
    exprs.push(scope.buildUndefinedNode());
  }

  if (exprs.length === 1) {
    return exprs[0];
  } else {
    return (0, _generated2.sequenceExpression)(exprs);
  }
}

/***/ }),

/***/ 2321:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = toStatement;

var _generated = __webpack_require__(2255);

var _generated2 = __webpack_require__(2256);

function toStatement(node, ignore) {
  if ((0, _generated.isStatement)(node)) {
    return node;
  }

  var mustHaveId = false;
  var newType;

  if ((0, _generated.isClass)(node)) {
    mustHaveId = true;
    newType = "ClassDeclaration";
  } else if ((0, _generated.isFunction)(node)) {
    mustHaveId = true;
    newType = "FunctionDeclaration";
  } else if ((0, _generated.isAssignmentExpression)(node)) {
    return (0, _generated2.expressionStatement)(node);
  }

  if (mustHaveId && !node.id) {
    newType = false;
  }

  if (!newType) {
    if (ignore) {
      return false;
    } else {
      throw new Error("cannot turn " + node.type + " to a statement");
    }
  }

  node.type = newType;
  return node;
}

/***/ }),

/***/ 2322:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = valueToNode;

var _isPlainObject = _interopRequireDefault(__webpack_require__(5));

var _isRegExp = _interopRequireDefault(__webpack_require__(538));

var _isValidIdentifier = _interopRequireDefault(__webpack_require__(2260));

var _generated = __webpack_require__(2256);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function valueToNode(value) {
  if (value === undefined) {
    return (0, _generated.identifier)("undefined");
  }

  if (value === true || value === false) {
    return (0, _generated.booleanLiteral)(value);
  }

  if (value === null) {
    return (0, _generated.nullLiteral)();
  }

  if (typeof value === "string") {
    return (0, _generated.stringLiteral)(value);
  }

  if (typeof value === "number") {
    return (0, _generated.numericLiteral)(value);
  }

  if ((0, _isRegExp.default)(value)) {
    var pattern = value.source;
    var flags = value.toString().match(/\/([a-z]+|)$/)[1];
    return (0, _generated.regExpLiteral)(pattern, flags);
  }

  if (Array.isArray(value)) {
    return (0, _generated.arrayExpression)(value.map(valueToNode));
  }

  if ((0, _isPlainObject.default)(value)) {
    var props = [];

    for (var key in value) {
      var nodeKey = void 0;

      if ((0, _isValidIdentifier.default)(key)) {
        nodeKey = (0, _generated.identifier)(key);
      } else {
        nodeKey = (0, _generated.stringLiteral)(key);
      }

      props.push((0, _generated.objectProperty)(nodeKey, valueToNode(value[key])));
    }

    return (0, _generated.objectExpression)(props);
  }

  throw new Error("don't know how to turn this value into a node");
}

/***/ }),

/***/ 2323:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = appendToMemberExpression;

var _generated = __webpack_require__(2256);

function appendToMemberExpression(member, append, computed) {
  if (computed === void 0) {
    computed = false;
  }

  member.object = (0, _generated.memberExpression)(member.object, member.property, member.computed);
  member.property = append;
  member.computed = !!computed;
  return member;
}

/***/ }),

/***/ 2324:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = inherits;

var _constants = __webpack_require__(2259);

var _inheritsComments = _interopRequireDefault(__webpack_require__(2279));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function inherits(child, parent) {
  if (!child || !parent) return child;
  var _arr = _constants.INHERIT_KEYS.optional;

  for (var _i = 0; _i < _arr.length; _i++) {
    var key = _arr[_i];

    if (child[key] == null) {
      child[key] = parent[key];
    }
  }

  for (var _key in parent) {
    if (_key[0] === "_" && _key !== "__clone") child[_key] = parent[_key];
  }

  var _arr2 = _constants.INHERIT_KEYS.force;

  for (var _i2 = 0; _i2 < _arr2.length; _i2++) {
    var _key2 = _arr2[_i2];
    child[_key2] = parent[_key2];
  }

  (0, _inheritsComments.default)(child, parent);
  return child;
}

/***/ }),

/***/ 2325:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = prependToMemberExpression;

var _generated = __webpack_require__(2256);

function prependToMemberExpression(member, prepend) {
  member.object = (0, _generated.memberExpression)(prepend, member.object);
  return member;
}

/***/ }),

/***/ 2326:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = getOuterBindingIdentifiers;

var _getBindingIdentifiers = _interopRequireDefault(__webpack_require__(2263));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function getOuterBindingIdentifiers(node, duplicates) {
  return (0, _getBindingIdentifiers.default)(node, duplicates, true);
}

/***/ }),

/***/ 2327:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = traverse;

var _definitions = __webpack_require__(2257);

function traverse(node, handlers, state) {
  if (typeof handlers === "function") {
    handlers = {
      enter: handlers
    };
  }

  var _ref = handlers,
      enter = _ref.enter,
      exit = _ref.exit;
  traverseSimpleImpl(node, enter, exit, state, []);
}

function traverseSimpleImpl(node, enter, exit, state, ancestors) {
  var keys = _definitions.VISITOR_KEYS[node.type];
  if (!keys) return;
  if (enter) enter(node, ancestors, state);

  for (var _iterator = keys, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {
    var _ref2;

    if (_isArray) {
      if (_i >= _iterator.length) break;
      _ref2 = _iterator[_i++];
    } else {
      _i = _iterator.next();
      if (_i.done) break;
      _ref2 = _i.value;
    }

    var _key2 = _ref2;
    var subNode = node[_key2];

    if (Array.isArray(subNode)) {
      for (var i = 0; i < subNode.length; i++) {
        var child = subNode[i];
        if (!child) continue;
        ancestors.push({
          node: node,
          key: _key2,
          index: i
        });
        traverseSimpleImpl(child, enter, exit, state, ancestors);
        ancestors.pop();
      }
    } else if (subNode) {
      ancestors.push({
        node: node,
        key: _key2
      });
      traverseSimpleImpl(subNode, enter, exit, state, ancestors);
      ancestors.pop();
    }
  }

  if (exit) exit(node, ancestors, state);
}

/***/ }),

/***/ 2328:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = isBinding;

var _getBindingIdentifiers = _interopRequireDefault(__webpack_require__(2263));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function isBinding(node, parent) {
  var keys = _getBindingIdentifiers.default.keys[parent.type];

  if (keys) {
    for (var i = 0; i < keys.length; i++) {
      var key = keys[i];
      var val = parent[key];

      if (Array.isArray(val)) {
        if (val.indexOf(node) >= 0) return true;
      } else {
        if (val === node) return true;
      }
    }
  }

  return false;
}

/***/ }),

/***/ 2329:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = isBlockScoped;

var _generated = __webpack_require__(2255);

var _isLet = _interopRequireDefault(__webpack_require__(2286));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function isBlockScoped(node) {
  return (0, _generated.isFunctionDeclaration)(node) || (0, _generated.isClassDeclaration)(node) || (0, _isLet.default)(node);
}

/***/ }),

/***/ 2330:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = isImmutable;

var _isType = _interopRequireDefault(__webpack_require__(2264));

var _generated = __webpack_require__(2255);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function isImmutable(node) {
  if ((0, _isType.default)(node.type, "Immutable")) return true;

  if ((0, _generated.isIdentifier)(node)) {
    if (node.name === "undefined") {
      return true;
    } else {
      return false;
    }
  }

  return false;
}

/***/ }),

/***/ 2331:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = isNodesEquivalent;

var _definitions = __webpack_require__(2257);

function isNodesEquivalent(a, b) {
  if (typeof a !== "object" || typeof b !== "object" || a == null || b == null) {
    return a === b;
  }

  if (a.type !== b.type) {
    return false;
  }

  var fields = Object.keys(_definitions.NODE_FIELDS[a.type] || a.type);

  for (var _i = 0; _i < fields.length; _i++) {
    var field = fields[_i];

    if (typeof a[field] !== typeof b[field]) {
      return false;
    }

    if (Array.isArray(a[field])) {
      if (!Array.isArray(b[field])) {
        return false;
      }

      if (a[field].length !== b[field].length) {
        return false;
      }

      for (var i = 0; i < a[field].length; i++) {
        if (!isNodesEquivalent(a[field][i], b[field][i])) {
          return false;
        }
      }

      continue;
    }

    if (!isNodesEquivalent(a[field], b[field])) {
      return false;
    }
  }

  return true;
}

/***/ }),

/***/ 2332:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = isReferenced;

function isReferenced(node, parent) {
  switch (parent.type) {
    case "BindExpression":
      return parent.object === node || parent.callee === node;

    case "MemberExpression":
    case "JSXMemberExpression":
      if (parent.property === node && parent.computed) {
        return true;
      } else if (parent.object === node) {
        return true;
      } else {
        return false;
      }

    case "MetaProperty":
      return false;

    case "ObjectProperty":
      if (parent.key === node) {
        return parent.computed;
      }

    case "VariableDeclarator":
      return parent.id !== node;

    case "ArrowFunctionExpression":
    case "FunctionDeclaration":
    case "FunctionExpression":
      var _arr = parent.params;

      for (var _i = 0; _i < _arr.length; _i++) {
        var param = _arr[_i];
        if (param === node) return false;
      }

      return parent.id !== node;

    case "ExportSpecifier":
      if (parent.source) {
        return false;
      } else {
        return parent.local === node;
      }

    case "ExportNamespaceSpecifier":
    case "ExportDefaultSpecifier":
      return false;

    case "JSXAttribute":
      return parent.name !== node;

    case "ClassProperty":
      if (parent.key === node) {
        return parent.computed;
      } else {
        return parent.value === node;
      }

    case "ImportDefaultSpecifier":
    case "ImportNamespaceSpecifier":
    case "ImportSpecifier":
      return false;

    case "ClassDeclaration":
    case "ClassExpression":
      return parent.id !== node;

    case "ClassMethod":
    case "ObjectMethod":
      return parent.key === node && parent.computed;

    case "LabeledStatement":
      return false;

    case "CatchClause":
      return parent.param !== node;

    case "RestElement":
      return false;

    case "AssignmentExpression":
      return parent.right === node;

    case "AssignmentPattern":
      return parent.right === node;

    case "ObjectPattern":
    case "ArrayPattern":
      return false;
  }

  return true;
}

/***/ }),

/***/ 2333:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = isScope;

var _generated = __webpack_require__(2255);

function isScope(node, parent) {
  if ((0, _generated.isBlockStatement)(node) && (0, _generated.isFunction)(parent, {
    body: node
  })) {
    return false;
  }

  if ((0, _generated.isBlockStatement)(node) && (0, _generated.isCatchClause)(parent, {
    body: node
  })) {
    return false;
  }

  return (0, _generated.isScopable)(node);
}

/***/ }),

/***/ 2334:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = isSpecifierDefault;

var _generated = __webpack_require__(2255);

function isSpecifierDefault(specifier) {
  return (0, _generated.isImportDefaultSpecifier)(specifier) || (0, _generated.isIdentifier)(specifier.imported || specifier.exported, {
    name: "default"
  });
}

/***/ }),

/***/ 2335:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = isValidES3Identifier;

var _isValidIdentifier = _interopRequireDefault(__webpack_require__(2260));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

var RESERVED_WORDS_ES3_ONLY = 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 isValidES3Identifier(name) {
  return (0, _isValidIdentifier.default)(name) && !RESERVED_WORDS_ES3_ONLY.has(name);
}

/***/ }),

/***/ 2336:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.default = isVar;

var _generated = __webpack_require__(2255);

var _constants = __webpack_require__(2259);

function isVar(node) {
  return (0, _generated.isVariableDeclaration)(node, {
    kind: "var"
  }) && !node[_constants.BLOCK_SCOPED_SYMBOL];
}

/***/ }),

/***/ 2337:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.parseScriptTags = exports.parseScripts = exports.getCandidateScriptLocations = exports.generateWhitespace = undefined;

var _types = __webpack_require__(2268);

var types = _interopRequireWildcard(_types);

var _parseScriptFragment = __webpack_require__(2338);

var _parseScriptFragment2 = _interopRequireDefault(_parseScriptFragment);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }

var startScript = /<script[^>]*>/im;
var endScript = /<\/script\s*>/im;
// https://stackoverflow.com/questions/5034781/js-regex-to-split-by-line#comment5633979_5035005
var newLines = /\r\n|[\n\v\f\r\x85\u2028\u2029]/;

function getType(tag) {
  var fragment = (0, _parseScriptFragment2.default)(tag);

  if (fragment) {
    var type = fragment.attributes.type;

    return type ? type.toLowerCase() : null;
  }

  return null;
}

function getCandidateScriptLocations(source, index) {
  var i = index || 0;
  var str = source.substring(i);

  var startMatch = startScript.exec(str);
  if (startMatch) {
    var startsAt = startMatch.index + startMatch[0].length;
    var afterStart = str.substring(startsAt);
    var endMatch = endScript.exec(afterStart);
    if (endMatch) {
      var locLength = endMatch.index;
      var locIndex = i + startsAt;
      var endIndex = locIndex + locLength + endMatch[0].length;

      // extract the complete tag (incl start and end tags and content). if the
      // type is invalid (= not JS), skip this tag and continue
      var tag = source.substring(i + startMatch.index, endIndex);
      var type = getType(tag);
      if (type && type !== "javascript" && type !== "text/javascript") {
        return getCandidateScriptLocations(source, endIndex);
      }

      return [adjustForLineAndColumn(source, {
        index: locIndex,
        length: locLength,
        source: source.substring(locIndex, locIndex + locLength)
      })].concat(_toConsumableArray(getCandidateScriptLocations(source, endIndex)));
    }
  }

  return [];
}

function parseScripts(locations, parser) {
  return locations.map(parser);
}

function generateWhitespace(length) {
  return Array.from(new Array(length + 1)).join(" ");
}

function calcLineAndColumn(source, index) {
  var lines = source.substring(0, index).split(newLines);
  var line = lines.length;
  var column = lines.pop().length + 1;

  return {
    column: column,
    line: line
  };
}

function adjustForLineAndColumn(fullSource, location) {
  var _calcLineAndColumn = calcLineAndColumn(fullSource, location.index),
      column = _calcLineAndColumn.column,
      line = _calcLineAndColumn.line;

  return Object.assign({}, location, {
    line: line,
    column: column,
    // prepend whitespace for scripts that do not start on the first column
    source: generateWhitespace(column) + location.source
  });
}

function parseScriptTags(source, parser) {
  var scripts = parseScripts(getCandidateScriptLocations(source), parser).filter(types.isFile).reduce(function (main, script) {
    return {
      statements: main.statements.concat(script.program.body),
      comments: main.comments.concat(script.comments),
      tokens: main.tokens.concat(script.tokens)
    };
  }, {
    statements: [],
    comments: [],
    tokens: []
  });

  var program = types.program(scripts.statements);
  var file = types.file(program, scripts.comments, scripts.tokens);

  var end = calcLineAndColumn(source, source.length);
  file.start = program.start = 0;
  file.end = program.end = source.length;
  file.loc = program.loc = {
    start: {
      line: 1,
      column: 0
    },
    end: end
  };

  return file;
}

exports.default = parseScriptTags;
exports.generateWhitespace = generateWhitespace;
exports.getCandidateScriptLocations = getCandidateScriptLocations;
exports.parseScripts = parseScripts;
exports.parseScriptTags = parseScriptTags;

/***/ }),

/***/ 2338:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var alphanum = /[a-z0-9\-]/i;

function parseToken(str, start) {
  var i = start;
  while (i < str.length && alphanum.test(str.charAt(i++))) {
    continue;
  }

  if (i !== start) {
    return {
      token: str.substring(start, i - 1),
      index: i
    };
  }

  return null;
}

function parseAttributes(str, start) {
  var i = start;
  var attributes = {};
  var attribute = null;

  while (i < str.length) {
    var c = str.charAt(i);

    if (attribute === null && c == ">") {
      break;
    } else if (attribute === null && alphanum.test(c)) {
      attribute = {
        name: null,
        value: true,
        bool: true,
        terminator: null
      };

      var attributeNameNode = parseToken(str, i);
      if (attributeNameNode) {
        attribute.name = attributeNameNode.token;
        i = attributeNameNode.index - 2;
      }
    } else if (attribute !== null) {
      if (c === "=") {
        // once we've started an attribute, look for = to indicate
        // it's a non-boolean attribute
        attribute.bool = false;
        if (attribute.value === true) {
          attribute.value = "";
        }
      } else if (!attribute.bool && attribute.terminator === null && (c === '"' || c === "'")) {
        // once we've determined it's non-boolean, look for a
        // value terminator (", ')
        attribute.terminator = c;
      } else if (attribute.terminator) {
        if (c === attribute.terminator) {
          // if we had a terminator and found another, we've
          // reach the end of the attribute
          attributes[attribute.name] = attribute.value;
          attribute = null;
        } else {
          // otherwise, append the character to the attribute value
          attribute.value += c;

          // check for an escaped terminator and push it as well
          // to avoid terminating prematurely
          if (c === "\\") {
            var next = str.charAt(i + 1);
            if (next === attribute.terminator) {
              attribute.value += next;
              i += 1;
            }
          }
        }
      } else if (!/\s/.test(c)) {
        // if we've hit a non-space character and aren't processing a value,
        // we're starting a new attribute so push the attribute and clear the
        // local variable
        attributes[attribute.name] = attribute.value;
        attribute = null;

        // move the cursor back to re-find the start of the attribute
        i -= 1;
      }
    }

    i++;
  }

  if (i !== start) {
    return {
      attributes: attributes,
      index: i
    };
  }

  return null;
}

function parseFragment(str) {
  var start = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;

  var tag = null;
  var open = false;
  var attributes = {};

  var i = start;
  while (i < str.length) {
    var c = str.charAt(i++);

    if (!open && !tag && c === "<") {
      // Open Start Tag
      open = true;

      var tagNode = parseToken(str, i);
      if (!tagNode) {
        return null;
      }

      i = tagNode.index - 1;
      tag = tagNode.token;
    } else if (open && c === ">") {
      // Close Start Tag
      break;
    } else if (open) {
      // Attributes
      var attributeNode = parseAttributes(str, i - 1);

      if (attributeNode) {
        i = attributeNode.index;
        attributes = attributeNode.attributes || attributes;
      }
    }
  }

  if (tag) {
    return {
      tag: tag,
      attributes: attributes
    };
  }

  return null;
}

exports.default = parseFragment;
exports.parseFragment = parseFragment;

/***/ }),

/***/ 2413:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = getScopes;
exports.clearScopes = clearScopes;

var _visitor = __webpack_require__(2414);

let parsedScopesCache = new Map(); /* This Source Code Form is subject to the terms of the Mozilla Public
                                    * License, v. 2.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 getScopes(location) {
  const { sourceId } = location;
  let parsedScopes = parsedScopesCache.get(sourceId);
  if (!parsedScopes) {
    parsedScopes = (0, _visitor.parseSourceScopes)(sourceId);
    parsedScopesCache.set(sourceId, parsedScopes);
  }
  return parsedScopes ? findScopes(parsedScopes, location) : [];
}

function clearScopes() {
  parsedScopesCache = new Map();
}

/**
 * Searches all scopes and their bindings at the specific location.
 */
function findScopes(scopes, location) {
  // Find inner most in the tree structure.
  let searchInScopes = scopes;
  const found = [];
  while (searchInScopes) {
    const foundOne = searchInScopes.some(s => {
      if (compareLocations(s.start, location) <= 0 && compareLocations(location, s.end) < 0) {
        // Found the next scope, trying to search recusevly in its children.
        found.unshift(s);
        searchInScopes = s.children;
        return true;
      }
      return false;
    });
    if (!foundOne) {
      break;
    }
  }
  return found.map(i => {
    return {
      type: i.type,
      displayName: i.displayName,
      start: i.start,
      end: i.end,
      bindings: i.bindings
    };
  });
}

function compareLocations(a, b) {
  // According to type of Location.column can be undefined, if will not be the
  // case here, ignoring flow error.
  // $FlowIgnore
  return a.line == b.line ? a.column - b.column : a.line - b.line;
}

/***/ }),

/***/ 2414:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

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; }; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                                                                                                                                                                                                                   * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                                                                                                                                                                                                                                   * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

exports.parseSourceScopes = parseSourceScopes;

var _isEmpty = __webpack_require__(963);

var _isEmpty2 = _interopRequireDefault(_isEmpty);

var _types = __webpack_require__(2268);

var t = _interopRequireWildcard(_types);

var _devtoolsSourceMap = __webpack_require__(1360);

var _getFunctionName = __webpack_require__(1621);

var _getFunctionName2 = _interopRequireDefault(_getFunctionName);

var _ast = __webpack_require__(1375);

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/**
 * "implicit"
 * Variables added automaticly like "this" and "arguments"
 *
 * "var"
 * Variables declared with "var" or non-block function declarations
 *
 * "let"
 * Variables declared with "let".
 *
 * "const"
 * Variables declared with "const", or added as const
 * bindings like inner function expressions and inner class names.
 *
 * "import"
 * Imported binding names exposed from other modules.
 */


// Location information about the expression immediartely surrounding a
// given binding reference.
function parseSourceScopes(sourceId) {
  const ast = (0, _ast.getAst)(sourceId);
  if ((0, _isEmpty2.default)(ast)) {
    return null;
  }

  const { global, lexical } = createGlobalScope(ast);

  const state = {
    sourceId,
    scope: lexical,
    scopeStack: []
  };
  t.traverse(ast, scopeCollectionVisitor, state);

  // TODO: This should probably check for ".mjs" extension on the
  // original file, and should also be skipped if the the generated
  // code is an ES6 module rather than a script.
  if ((0, _devtoolsSourceMap.isGeneratedId)(sourceId) || ast.program.sourceType === "script" && !looksLikeCommonJS(global)) {
    stripModuleScope(global);
  }

  return toParsedScopes([global], sourceId) || [];
}

function createTempScope(type, displayName, parent, loc) {
  const result = {
    type,
    displayName,
    parent,
    children: [],
    loc,
    names: Object.create(null)
  };
  if (parent) {
    parent.children.push(result);
  }
  return result;
}
function pushTempScope(state, type, displayName, loc) {
  const scope = createTempScope(type, displayName, state.scope, loc);

  state.scope = scope;
  return scope;
}

function isNode(node, type) {
  return node ? node.type === type : false;
}

function getVarScope(scope) {
  let s = scope;
  while (s.type !== "function" && s.type !== "module") {
    if (!s.parent) {
      return s;
    }
    s = s.parent;
  }
  return s;
}

function fromBabelLocation(location, sourceId) {
  return {
    sourceId,
    line: location.line,
    column: location.column
  };
}

function parseDeclarator(declaratorId, targetScope, type) {
  if (isNode(declaratorId, "Identifier")) {
    let existing = targetScope.names[declaratorId.name];
    if (!existing) {
      existing = {
        type,
        declarations: [],
        refs: []
      };
      targetScope.names[declaratorId.name] = existing;
    }
    existing.declarations.push(declaratorId.loc);
  } else if (isNode(declaratorId, "ObjectPattern")) {
    declaratorId.properties.forEach(prop => {
      parseDeclarator(prop.value, targetScope, type);
    });
  } else if (isNode(declaratorId, "ArrayPattern")) {
    declaratorId.elements.forEach(item => {
      parseDeclarator(item, targetScope, type);
    });
  } else if (isNode(declaratorId, "AssignmentPattern")) {
    parseDeclarator(declaratorId.left, targetScope, type);
  } else if (isNode(declaratorId, "RestElement")) {
    parseDeclarator(declaratorId.argument, targetScope, type);
  }
}

function isLetOrConst(node) {
  return node.kind === "let" || node.kind === "const";
}

function hasLexicalDeclaration(node, parent) {
  const isFunctionBody = t.isFunction(parent, { body: node });

  return node.body.some(child => isLexicalVariable(child) || !isFunctionBody && child.type === "FunctionDeclaration" || child.type === "ClassDeclaration");
}
function isLexicalVariable(node) {
  return isNode(node, "VariableDeclaration") && isLetOrConst(node);
}

function findIdentifierInScopes(scope, name) {
  // Find nearest outer scope with the specifed name and add reference.
  for (let s = scope; s; s = s.parent) {
    if (name in s.names) {
      return s;
    }
  }
  return null;
}

function toParsedScopes(children, sourceId) {
  if (!children || children.length === 0) {
    return undefined;
  }
  return children.map(scope => {
    // Removing unneed information from TempScope such as parent reference.
    // We also need to convert BabelLocation to the Location type.
    const bindings = Object.keys(scope.names).reduce((_bindings, n) => {
      const nameRefs = scope.names[n];

      _bindings[n] = {
        type: nameRefs.type,
        declarations: nameRefs.declarations.map(({ start, end }) => ({
          start: fromBabelLocation(start, sourceId),
          end: fromBabelLocation(end, sourceId)
        })),
        refs: nameRefs.refs.map(({ start, end, meta }) => ({
          start: fromBabelLocation(start, sourceId),
          end: fromBabelLocation(end, sourceId),
          // eslint-disable-next-line max-nested-callbacks
          meta: mapMeta(meta || null, item => {
            // $FlowIgnore - Flow doesn't like merging here.
            return _extends({}, item, {
              start: fromBabelLocation(item.start, sourceId),
              end: fromBabelLocation(item.end, sourceId)
            });
          })
        }))
      };
      return _bindings;
    }, Object.create(null));
    return {
      start: fromBabelLocation(scope.loc.start, sourceId),
      end: fromBabelLocation(scope.loc.end, sourceId),
      type: scope.type === "module" ? "block" : scope.type,
      displayName: scope.displayName,
      bindings: bindings,
      children: toParsedScopes(scope.children, sourceId)
    };
  });
}

function mapMeta(item, callback) {
  if (!item) {
    return null;
  }

  const result = callback(item);

  // $FlowIgnore - Flow doesn't like merging here.
  return _extends({}, result, {
    parent: mapMeta(item.parent, callback)
  });
}

function createGlobalScope(ast) {
  const global = createTempScope("object", "Global", null, ast.loc);

  // Include fake bindings to collect references to CommonJS
  Object.assign(global.names, {
    module: {
      type: "var",
      declarations: [],
      refs: []
    },
    exports: {
      type: "var",
      declarations: [],
      refs: []
    },
    __dirname: {
      type: "var",
      declarations: [],
      refs: []
    },
    __filename: {
      type: "var",
      declarations: [],
      refs: []
    },
    require: {
      type: "var",
      declarations: [],
      refs: []
    }
  });

  const lexical = createTempScope("block", "Lexical Global", global, ast.loc);

  return {
    global,
    lexical
  };
}

const scopeCollectionVisitor = {
  // eslint-disable-next-line complexity
  enter(node, ancestors, state) {
    state.scopeStack.push(state.scope);

    const parentNode = ancestors.length === 0 ? null : ancestors[ancestors.length - 1].node;

    if (t.isProgram(node)) {
      const scope = pushTempScope(state, "module", "Module", node.loc);
      scope.names.this = {
        type: "implicit",
        declarations: [],
        refs: []
      };
    } else if (t.isFunction(node)) {
      let scope = state.scope;
      if (t.isFunctionExpression(node) && isNode(node.id, "Identifier")) {
        scope = pushTempScope(state, "block", "Function Expression", node.loc);
        scope.names[node.id.name] = {
          type: "const",
          declarations: [node.id.loc],
          refs: []
        };
      }

      if (t.isFunctionDeclaration(node) && isNode(node.id, "Identifier")) {
        // This ignores Annex B function declaration hoisting, which
        // is probably a fine assumption.
        const fnScope = getVarScope(scope);
        scope.names[node.id.name] = {
          type: fnScope === scope ? "var" : "let",
          declarations: [node.id.loc],
          refs: []
        };
      }

      scope = pushTempScope(state, "function", (0, _getFunctionName2.default)(node, parentNode), {
        // Being at the start of a function doesn't count as
        // being inside of it.
        start: node.params[0] ? node.params[0].loc.start : node.loc.start,
        end: node.loc.end
      });

      node.params.forEach(param => parseDeclarator(param, scope, "var"));

      if (!t.isArrowFunctionExpression(node)) {
        scope.names.this = {
          type: "implicit",
          declarations: [],
          refs: []
        };
        scope.names.arguments = {
          type: "implicit",
          declarations: [],
          refs: []
        };
      }
    } else if (t.isClass(node)) {
      if (t.isClassDeclaration(node) && t.isIdentifier(node.id)) {
        state.scope.names[node.id.name] = {
          type: "let",
          declarations: [node.id.loc],
          refs: []
        };
      }

      if (t.isIdentifier(node.id)) {
        const scope = pushTempScope(state, "block", "Class", node.loc);

        scope.names[node.id.name] = {
          type: "const",
          declarations: [node.id.loc],
          refs: []
        };
      }
    } else if (t.isForXStatement(node) || t.isForStatement(node)) {
      const init = node.init || node.left;
      if (isNode(init, "VariableDeclaration") && isLetOrConst(init)) {
        // Debugger will create new lexical environment for the for.
        pushTempScope(state, "block", "For", {
          // Being at the start of a for loop doesn't count as
          // being inside it.
          start: init.loc.start,
          end: node.loc.end
        });
      }
    } else if (t.isCatchClause(node)) {
      const scope = pushTempScope(state, "block", "Catch", node.loc);
      parseDeclarator(node.param, scope, "var");
    } else if (t.isBlockStatement(node) && hasLexicalDeclaration(node, parentNode)) {
      // Debugger will create new lexical environment for the block.
      pushTempScope(state, "block", "Block", node.loc);
    } else if (t.isVariableDeclaration(node) && (node.kind === "var" ||
    // Lexical declarations in for statements are handled above.
    !t.isForStatement(parentNode, { init: node }) || !t.isForXStatement(parentNode, { left: node }))) {
      // Finds right lexical environment
      const hoistAt = !isLetOrConst(node) ? getVarScope(state.scope) : state.scope;
      node.declarations.forEach(declarator => {
        parseDeclarator(declarator.id, hoistAt, node.kind);
      });
    } else if (t.isImportDeclaration(node)) {
      node.specifiers.forEach(spec => {
        state.scope.names[spec.local.name] = {
          // Imported namespaces aren't live import bindings, they are
          // just normal const bindings.
          type: t.isImportNamespaceSpecifier(spec) ? "const" : "import",
          declarations: [spec.local.loc],
          refs: []
        };
      });
    } else if (t.isIdentifier(node) && t.isReferenced(node, parentNode)) {
      const identScope = findIdentifierInScopes(state.scope, node.name);
      if (identScope) {
        identScope.names[node.name].refs.push({
          start: node.loc.start,
          end: node.loc.end,
          meta: buildMetaBindings(node, ancestors)
        });
      }
    } else if (t.isThisExpression(node)) {
      const identScope = findIdentifierInScopes(state.scope, "this");
      if (identScope) {
        identScope.names.this.refs.push({
          start: node.loc.start,
          end: node.loc.end,
          meta: buildMetaBindings(node, ancestors)
        });
      }
    } else if (t.isClassProperty(parentNode, { value: node })) {
      const scope = pushTempScope(state, "function", "Class Field", node.loc);
      scope.names.this = {
        type: "implicit",
        declarations: [],
        refs: []
      };
      scope.names.arguments = {
        type: "implicit",
        declarations: [],
        refs: []
      };
    } else if (t.isSwitchStatement(node) && node.cases.some(caseNode => caseNode.consequent.some(child => isLexicalVariable(child)))) {
      pushTempScope(state, "block", "Switch", node.loc);
    }
  },
  exit(node, ancestors, state) {
    const scope = state.scopeStack.pop();
    if (!scope) {
      throw new Error("Assertion failure - unsynchronized pop");
    }

    state.scope = scope;
  }
};

function buildMetaBindings(node, ancestors, parentIndex = ancestors.length - 1) {
  if (parentIndex <= 1) {
    return null;
  }
  const parent = ancestors[parentIndex].node;
  const grandparent = ancestors[parentIndex - 1].node;

  // Consider "0, foo" to be equivalent to "foo".
  if (t.isSequenceExpression(parent) && parent.expressions.length === 2 && t.isNumericLiteral(parent.expressions[0]) && parent.expressions[1] === node) {
    let start = parent.loc.start;
    let end = parent.loc.end;

    if (t.isCallExpression(grandparent, { callee: parent })) {
      // Attempt to expand the range around parentheses, e.g.
      // (0, foo.bar)()
      start = grandparent.loc.start;
      end = Object.assign({}, end);
      end.column += 1;
    }

    return {
      type: "inherit",
      start,
      end,
      parent: buildMetaBindings(parent, ancestors, parentIndex - 1)
    };
  }

  // Consider "Object(foo)" to be equivalent to "foo"
  if (t.isCallExpression(parent) && t.isIdentifier(parent.callee, { name: "Object" }) && parent.arguments.length === 1 && parent.arguments[0] === node) {
    return {
      type: "inherit",
      start: parent.loc.start,
      end: parent.loc.end,
      parent: buildMetaBindings(parent, ancestors, parentIndex - 1)
    };
  }

  if (t.isMemberExpression(parent, { object: node })) {
    if (parent.computed) {
      if (t.isStringLiteral(parent.property)) {
        return {
          type: "member",
          start: parent.loc.start,
          end: parent.loc.end,
          property: parent.property.value,
          parent: buildMetaBindings(parent, ancestors, parentIndex - 1)
        };
      }
    } else {
      return {
        type: "member",
        start: parent.loc.start,
        end: parent.loc.end,
        property: parent.property.name,
        parent: buildMetaBindings(parent, ancestors, parentIndex - 1)
      };
    }
  }
  if (t.isCallExpression(parent, { callee: node }) && parent.arguments.length == 0) {
    return {
      type: "call",
      start: parent.loc.start,
      end: parent.loc.end,
      parent: buildMetaBindings(parent, ancestors, parentIndex - 1)
    };
  }

  return null;
}

function looksLikeCommonJS(rootScope) {
  return rootScope.names.__dirname.refs.length > 0 || rootScope.names.__filename.refs.length > 0 || rootScope.names.require.refs.length > 0 || rootScope.names.exports.refs.length > 0 || rootScope.names.module.refs.length > 0;
}

function stripModuleScope(rootScope) {
  const rootLexicalScope = rootScope.children[0];
  const moduleScope = rootLexicalScope.children[0];
  if (moduleScope.type !== "module") {
    throw new Error("Assertion failure - should be module");
  }

  Object.keys(moduleScope.names).forEach(name => {
    const binding = moduleScope.names[name];
    if (binding.type === "let" || binding.type === "const") {
      rootLexicalScope.names[name] = binding;
    } else {
      rootScope.names[name] = binding;
    }
  });
  rootLexicalScope.children = moduleScope.children;
  rootLexicalScope.children.forEach(child => {
    child.parent = rootLexicalScope;
  });
}

/***/ }),

/***/ 2422:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.isInvalidPauseLocation = isInvalidPauseLocation;

var _types = __webpack_require__(2268);

var t = _interopRequireWildcard(_types);

var _ast = __webpack_require__(1375);

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 STOP = {};

function isInvalidPauseLocation(location) {
  const state = {
    invalid: false,
    location
  };

  try {
    (0, _ast.traverseAst)(location.sourceId, { enter: invalidLocationVisitor }, state);
  } catch (e) {
    if (e !== STOP) {
      throw e;
    }
  }

  return state.invalid;
}

function invalidLocationVisitor(node, ancestors, state) {
  const { location } = state;

  if (node.loc.end.line < location.line) {
    return;
  }
  if (node.loc.start.line > location.line) {
    throw STOP;
  }

  if (location.line === node.loc.start.line && location.column >= node.loc.start.column && t.isFunction(node) && !t.isArrowFunctionExpression(node) && (location.line < node.body.loc.start.line || location.line === node.body.loc.start.line && location.column <= node.body.loc.start.column)) {
    // Disallow pausing _inside_ in function arguments to avoid pausing inside
    // of destructuring and other logic.
    state.invalid = true;
    throw STOP;
  }

  if (location.line === node.loc.start.line && location.column === node.loc.start.column && t.isBlockStatement(node)) {
    // Disallow pausing directly before the opening curly of a block statement.
    // Babel occasionally maps statements with unknown original positions to
    // this location.
    state.invalid = true;
    throw STOP;
  }
}

/***/ }),

/***/ 248:
/***/ (function(module, exports, __webpack_require__) {

(function(){
  var crypt = __webpack_require__(249),
      utf8 = __webpack_require__(250).utf8,
      isBuffer = __webpack_require__(251),
      bin = __webpack_require__(250).bin,

  // The core
  md5 = function (message, options) {
    // Convert to byte array
    if (message.constructor == String)
      if (options && options.encoding === 'binary')
        message = bin.stringToBytes(message);
      else
        message = utf8.stringToBytes(message);
    else if (isBuffer(message))
      message = Array.prototype.slice.call(message, 0);
    else if (!Array.isArray(message))
      message = message.toString();
    // else, assume byte array already

    var m = crypt.bytesToWords(message),
        l = message.length * 8,
        a =  1732584193,
        b = -271733879,
        c = -1732584194,
        d =  271733878;

    // Swap endian
    for (var i = 0; i < m.length; i++) {
      m[i] = ((m[i] <<  8) | (m[i] >>> 24)) & 0x00FF00FF |
             ((m[i] << 24) | (m[i] >>>  8)) & 0xFF00FF00;
    }

    // Padding
    m[l >>> 5] |= 0x80 << (l % 32);
    m[(((l + 64) >>> 9) << 4) + 14] = l;

    // Method shortcuts
    var FF = md5._ff,
        GG = md5._gg,
        HH = md5._hh,
        II = md5._ii;

    for (var i = 0; i < m.length; i += 16) {

      var aa = a,
          bb = b,
          cc = c,
          dd = d;

      a = FF(a, b, c, d, m[i+ 0],  7, -680876936);
      d = FF(d, a, b, c, m[i+ 1], 12, -389564586);
      c = FF(c, d, a, b, m[i+ 2], 17,  606105819);
      b = FF(b, c, d, a, m[i+ 3], 22, -1044525330);
      a = FF(a, b, c, d, m[i+ 4],  7, -176418897);
      d = FF(d, a, b, c, m[i+ 5], 12,  1200080426);
      c = FF(c, d, a, b, m[i+ 6], 17, -1473231341);
      b = FF(b, c, d, a, m[i+ 7], 22, -45705983);
      a = FF(a, b, c, d, m[i+ 8],  7,  1770035416);
      d = FF(d, a, b, c, m[i+ 9], 12, -1958414417);
      c = FF(c, d, a, b, m[i+10], 17, -42063);
      b = FF(b, c, d, a, m[i+11], 22, -1990404162);
      a = FF(a, b, c, d, m[i+12],  7,  1804603682);
      d = FF(d, a, b, c, m[i+13], 12, -40341101);
      c = FF(c, d, a, b, m[i+14], 17, -1502002290);
      b = FF(b, c, d, a, m[i+15], 22,  1236535329);

      a = GG(a, b, c, d, m[i+ 1],  5, -165796510);
      d = GG(d, a, b, c, m[i+ 6],  9, -1069501632);
      c = GG(c, d, a, b, m[i+11], 14,  643717713);
      b = GG(b, c, d, a, m[i+ 0], 20, -373897302);
      a = GG(a, b, c, d, m[i+ 5],  5, -701558691);
      d = GG(d, a, b, c, m[i+10],  9,  38016083);
      c = GG(c, d, a, b, m[i+15], 14, -660478335);
      b = GG(b, c, d, a, m[i+ 4], 20, -405537848);
      a = GG(a, b, c, d, m[i+ 9],  5,  568446438);
      d = GG(d, a, b, c, m[i+14],  9, -1019803690);
      c = GG(c, d, a, b, m[i+ 3], 14, -187363961);
      b = GG(b, c, d, a, m[i+ 8], 20,  1163531501);
      a = GG(a, b, c, d, m[i+13],  5, -1444681467);
      d = GG(d, a, b, c, m[i+ 2],  9, -51403784);
      c = GG(c, d, a, b, m[i+ 7], 14,  1735328473);
      b = GG(b, c, d, a, m[i+12], 20, -1926607734);

      a = HH(a, b, c, d, m[i+ 5],  4, -378558);
      d = HH(d, a, b, c, m[i+ 8], 11, -2022574463);
      c = HH(c, d, a, b, m[i+11], 16,  1839030562);
      b = HH(b, c, d, a, m[i+14], 23, -35309556);
      a = HH(a, b, c, d, m[i+ 1],  4, -1530992060);
      d = HH(d, a, b, c, m[i+ 4], 11,  1272893353);
      c = HH(c, d, a, b, m[i+ 7], 16, -155497632);
      b = HH(b, c, d, a, m[i+10], 23, -1094730640);
      a = HH(a, b, c, d, m[i+13],  4,  681279174);
      d = HH(d, a, b, c, m[i+ 0], 11, -358537222);
      c = HH(c, d, a, b, m[i+ 3], 16, -722521979);
      b = HH(b, c, d, a, m[i+ 6], 23,  76029189);
      a = HH(a, b, c, d, m[i+ 9],  4, -640364487);
      d = HH(d, a, b, c, m[i+12], 11, -421815835);
      c = HH(c, d, a, b, m[i+15], 16,  530742520);
      b = HH(b, c, d, a, m[i+ 2], 23, -995338651);

      a = II(a, b, c, d, m[i+ 0],  6, -198630844);
      d = II(d, a, b, c, m[i+ 7], 10,  1126891415);
      c = II(c, d, a, b, m[i+14], 15, -1416354905);
      b = II(b, c, d, a, m[i+ 5], 21, -57434055);
      a = II(a, b, c, d, m[i+12],  6,  1700485571);
      d = II(d, a, b, c, m[i+ 3], 10, -1894986606);
      c = II(c, d, a, b, m[i+10], 15, -1051523);
      b = II(b, c, d, a, m[i+ 1], 21, -2054922799);
      a = II(a, b, c, d, m[i+ 8],  6,  1873313359);
      d = II(d, a, b, c, m[i+15], 10, -30611744);
      c = II(c, d, a, b, m[i+ 6], 15, -1560198380);
      b = II(b, c, d, a, m[i+13], 21,  1309151649);
      a = II(a, b, c, d, m[i+ 4],  6, -145523070);
      d = II(d, a, b, c, m[i+11], 10, -1120210379);
      c = II(c, d, a, b, m[i+ 2], 15,  718787259);
      b = II(b, c, d, a, m[i+ 9], 21, -343485551);

      a = (a + aa) >>> 0;
      b = (b + bb) >>> 0;
      c = (c + cc) >>> 0;
      d = (d + dd) >>> 0;
    }

    return crypt.endian([a, b, c, d]);
  };

  // Auxiliary functions
  md5._ff  = function (a, b, c, d, x, s, t) {
    var n = a + (b & c | ~b & d) + (x >>> 0) + t;
    return ((n << s) | (n >>> (32 - s))) + b;
  };
  md5._gg  = function (a, b, c, d, x, s, t) {
    var n = a + (b & d | c & ~d) + (x >>> 0) + t;
    return ((n << s) | (n >>> (32 - s))) + b;
  };
  md5._hh  = function (a, b, c, d, x, s, t) {
    var n = a + (b ^ c ^ d) + (x >>> 0) + t;
    return ((n << s) | (n >>> (32 - s))) + b;
  };
  md5._ii  = function (a, b, c, d, x, s, t) {
    var n = a + (c ^ (b | ~d)) + (x >>> 0) + t;
    return ((n << s) | (n >>> (32 - s))) + b;
  };

  // Package private blocksize
  md5._blocksize = 16;
  md5._digestsize = 16;

  module.exports = function (message, options) {
    if (message === undefined || message === null)
      throw new Error('Illegal argument ' + message);

    var digestbytes = crypt.wordsToBytes(md5(message, options));
    return options && options.asBytes ? digestbytes :
        options && options.asString ? bin.bytesToString(digestbytes) :
        crypt.bytesToHex(digestbytes);
  };

})();


/***/ }),

/***/ 249:
/***/ (function(module, exports) {

(function() {
  var base64map
      = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',

  crypt = {
    // Bit-wise rotation left
    rotl: function(n, b) {
      return (n << b) | (n >>> (32 - b));
    },

    // Bit-wise rotation right
    rotr: function(n, b) {
      return (n << (32 - b)) | (n >>> b);
    },

    // Swap big-endian to little-endian and vice versa
    endian: function(n) {
      // If number given, swap endian
      if (n.constructor == Number) {
        return crypt.rotl(n, 8) & 0x00FF00FF | crypt.rotl(n, 24) & 0xFF00FF00;
      }

      // Else, assume array and swap all items
      for (var i = 0; i < n.length; i++)
        n[i] = crypt.endian(n[i]);
      return n;
    },

    // Generate an array of any length of random bytes
    randomBytes: function(n) {
      for (var bytes = []; n > 0; n--)
        bytes.push(Math.floor(Math.random() * 256));
      return bytes;
    },

    // Convert a byte array to big-endian 32-bit words
    bytesToWords: function(bytes) {
      for (var words = [], i = 0, b = 0; i < bytes.length; i++, b += 8)
        words[b >>> 5] |= bytes[i] << (24 - b % 32);
      return words;
    },

    // Convert big-endian 32-bit words to a byte array
    wordsToBytes: function(words) {
      for (var bytes = [], b = 0; b < words.length * 32; b += 8)
        bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF);
      return bytes;
    },

    // Convert a byte array to a hex string
    bytesToHex: function(bytes) {
      for (var hex = [], i = 0; i < bytes.length; i++) {
        hex.push((bytes[i] >>> 4).toString(16));
        hex.push((bytes[i] & 0xF).toString(16));
      }
      return hex.join('');
    },

    // Convert a hex string to a byte array
    hexToBytes: function(hex) {
      for (var bytes = [], c = 0; c < hex.length; c += 2)
        bytes.push(parseInt(hex.substr(c, 2), 16));
      return bytes;
    },

    // Convert a byte array to a base-64 string
    bytesToBase64: function(bytes) {
      for (var base64 = [], i = 0; i < bytes.length; i += 3) {
        var triplet = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
        for (var j = 0; j < 4; j++)
          if (i * 8 + j * 6 <= bytes.length * 8)
            base64.push(base64map.charAt((triplet >>> 6 * (3 - j)) & 0x3F));
          else
            base64.push('=');
      }
      return base64.join('');
    },

    // Convert a base-64 string to a byte array
    base64ToBytes: function(base64) {
      // Remove non-base-64 characters
      base64 = base64.replace(/[^A-Z0-9+\/]/ig, '');

      for (var bytes = [], i = 0, imod4 = 0; i < base64.length;
          imod4 = ++i % 4) {
        if (imod4 == 0) continue;
        bytes.push(((base64map.indexOf(base64.charAt(i - 1))
            & (Math.pow(2, -2 * imod4 + 8) - 1)) << (imod4 * 2))
            | (base64map.indexOf(base64.charAt(i)) >>> (6 - imod4 * 2)));
      }
      return bytes;
    }
  };

  module.exports = crypt;
})();


/***/ }),

/***/ 250:
/***/ (function(module, exports) {

var charenc = {
  // UTF-8 encoding
  utf8: {
    // Convert a string to a byte array
    stringToBytes: function(str) {
      return charenc.bin.stringToBytes(unescape(encodeURIComponent(str)));
    },

    // Convert a byte array to a string
    bytesToString: function(bytes) {
      return decodeURIComponent(escape(charenc.bin.bytesToString(bytes)));
    }
  },

  // Binary encoding
  bin: {
    // Convert a string to a byte array
    stringToBytes: function(str) {
      for (var bytes = [], i = 0; i < str.length; i++)
        bytes.push(str.charCodeAt(i) & 0xFF);
      return bytes;
    },

    // Convert a byte array to a string
    bytesToString: function(bytes) {
      for (var str = [], i = 0; i < bytes.length; i++)
        str.push(String.fromCharCode(bytes[i]));
      return str.join('');
    }
  }
};

module.exports = charenc;


/***/ }),

/***/ 251:
/***/ (function(module, exports) {

/*!
 * Determine if an object is a Buffer
 *
 * @author   Feross Aboukhadijeh <https://feross.org>
 * @license  MIT
 */

// The _isBuffer check is for Safari 5-7 support, because it's missing
// Object.prototype.constructor. Remove this eventually
module.exports = function (obj) {
  return obj != null && (isBuffer(obj) || isSlowBuffer(obj) || !!obj._isBuffer)
}

function isBuffer (obj) {
  return !!obj.constructor && typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj)
}

// For Node v0.10 support. Remove this eventually.
function isSlowBuffer (obj) {
  return typeof obj.readFloatLE === 'function' && typeof obj.slice === 'function' && isBuffer(obj.slice(0, 0))
}


/***/ }),

/***/ 262:
/***/ (function(module, exports, __webpack_require__) {

var baseFindIndex = __webpack_require__(263),
    baseIteratee = __webpack_require__(814),
    toInteger = __webpack_require__(302);

/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeMax = Math.max;

/**
 * This method is like `_.find` except that it returns the index of the first
 * element `predicate` returns truthy for instead of the element itself.
 *
 * @static
 * @memberOf _
 * @since 1.1.0
 * @category Array
 * @param {Array} array The array to inspect.
 * @param {Function} [predicate=_.identity] The function invoked per iteration.
 * @param {number} [fromIndex=0] The index to search from.
 * @returns {number} Returns the index of the found element, else `-1`.
 * @example
 *
 * var users = [
 *   { 'user': 'barney',  'active': false },
 *   { 'user': 'fred',    'active': false },
 *   { 'user': 'pebbles', 'active': true }
 * ];
 *
 * _.findIndex(users, function(o) { return o.user == 'barney'; });
 * // => 0
 *
 * // The `_.matches` iteratee shorthand.
 * _.findIndex(users, { 'user': 'fred', 'active': false });
 * // => 1
 *
 * // The `_.matchesProperty` iteratee shorthand.
 * _.findIndex(users, ['active', false]);
 * // => 0
 *
 * // The `_.property` iteratee shorthand.
 * _.findIndex(users, 'active');
 * // => 2
 */
function findIndex(array, predicate, fromIndex) {
  var length = array == null ? 0 : array.length;
  if (!length) {
    return -1;
  }
  var index = fromIndex == null ? 0 : toInteger(fromIndex);
  if (index < 0) {
    index = nativeMax(length + index, 0);
  }
  return baseFindIndex(array, baseIteratee(predicate, 3), index);
}

module.exports = findIndex;


/***/ }),

/***/ 263:
/***/ (function(module, exports) {

/**
 * The base implementation of `_.findIndex` and `_.findLastIndex` without
 * support for iteratee shorthands.
 *
 * @private
 * @param {Array} array The array to inspect.
 * @param {Function} predicate The function invoked per iteration.
 * @param {number} fromIndex The index to search from.
 * @param {boolean} [fromRight] Specify iterating from right to left.
 * @returns {number} Returns the index of the matched value, else `-1`.
 */
function baseFindIndex(array, predicate, fromIndex, fromRight) {
  var length = array.length,
      index = fromIndex + (fromRight ? 1 : -1);

  while ((fromRight ? index-- : ++index < length)) {
    if (predicate(array[index], index, array)) {
      return index;
    }
  }
  return -1;
}

module.exports = baseFindIndex;


/***/ }),

/***/ 267:
/***/ (function(module, exports, __webpack_require__) {

var ListCache = __webpack_require__(93),
    stackClear = __webpack_require__(268),
    stackDelete = __webpack_require__(269),
    stackGet = __webpack_require__(270),
    stackHas = __webpack_require__(271),
    stackSet = __webpack_require__(272);

/**
 * Creates a stack cache object to store key-value pairs.
 *
 * @private
 * @constructor
 * @param {Array} [entries] The key-value pairs to cache.
 */
function Stack(entries) {
  var data = this.__data__ = new ListCache(entries);
  this.size = data.size;
}

// Add methods to `Stack`.
Stack.prototype.clear = stackClear;
Stack.prototype['delete'] = stackDelete;
Stack.prototype.get = stackGet;
Stack.prototype.has = stackHas;
Stack.prototype.set = stackSet;

module.exports = Stack;


/***/ }),

/***/ 268:
/***/ (function(module, exports, __webpack_require__) {

var ListCache = __webpack_require__(93);

/**
 * Removes all key-value entries from the stack.
 *
 * @private
 * @name clear
 * @memberOf Stack
 */
function stackClear() {
  this.__data__ = new ListCache;
  this.size = 0;
}

module.exports = stackClear;


/***/ }),

/***/ 269:
/***/ (function(module, exports) {

/**
 * 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) {
  var data = this.__data__,
      result = data['delete'](key);

  this.size = data.size;
  return result;
}

module.exports = stackDelete;


/***/ }),

/***/ 270:
/***/ (function(module, exports) {

/**
 * 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);
}

module.exports = stackGet;


/***/ }),

/***/ 271:
/***/ (function(module, exports) {

/**
 * 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);
}

module.exports = stackHas;


/***/ }),

/***/ 272:
/***/ (function(module, exports, __webpack_require__) {

var ListCache = __webpack_require__(93),
    Map = __webpack_require__(101),
    MapCache = __webpack_require__(76);

/** Used as the size to enable large array optimizations. */
var LARGE_ARRAY_SIZE = 200;

/**
 * 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 data = this.__data__;
  if (data instanceof ListCache) {
    var pairs = data.__data__;
    if (!Map || (pairs.length < LARGE_ARRAY_SIZE - 1)) {
      pairs.push([key, value]);
      this.size = ++data.size;
      return this;
    }
    data = this.__data__ = new MapCache(pairs);
  }
  data.set(key, value);
  this.size = data.size;
  return this;
}

module.exports = stackSet;


/***/ }),

/***/ 276:
/***/ (function(module, exports, __webpack_require__) {

var MapCache = __webpack_require__(76),
    setCacheAdd = __webpack_require__(277),
    setCacheHas = __webpack_require__(278);

/**
 *
 * 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 == null ? 0 : values.length;

  this.__data__ = new MapCache;
  while (++index < length) {
    this.add(values[index]);
  }
}

// Add methods to `SetCache`.
SetCache.prototype.add = SetCache.prototype.push = setCacheAdd;
SetCache.prototype.has = setCacheHas;

module.exports = SetCache;


/***/ }),

/***/ 277:
/***/ (function(module, exports) {

/** Used to stand-in for `undefined` hash values. */
var HASH_UNDEFINED = '__lodash_hash_undefined__';

/**
 * 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;
}

module.exports = setCacheAdd;


/***/ }),

/***/ 278:
/***/ (function(module, exports) {

/**
 * 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);
}

module.exports = setCacheHas;


/***/ }),

/***/ 280:
/***/ (function(module, exports) {

/**
 * Checks if a `cache` value for `key` exists.
 *
 * @private
 * @param {Object} cache The cache to query.
 * @param {string} key The key of the entry to check.
 * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
 */
function cacheHas(cache, key) {
  return cache.has(key);
}

module.exports = cacheHas;


/***/ }),

/***/ 282:
/***/ (function(module, exports, __webpack_require__) {

var root = __webpack_require__(8);

/** Built-in value references. */
var Uint8Array = root.Uint8Array;

module.exports = Uint8Array;


/***/ }),

/***/ 283:
/***/ (function(module, exports) {

/**
 * 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;
}

module.exports = setToArray;


/***/ }),

/***/ 285:
/***/ (function(module, exports, __webpack_require__) {

var baseGetAllKeys = __webpack_require__(286),
    getSymbols = __webpack_require__(288),
    keys = __webpack_require__(205);

/**
 * Creates an array of own enumerable property names and symbols of `object`.
 *
 * @private
 * @param {Object} object The object to query.
 * @returns {Array} Returns the array of property names and symbols.
 */
function getAllKeys(object) {
  return baseGetAllKeys(object, keys, getSymbols);
}

module.exports = getAllKeys;


/***/ }),

/***/ 286:
/***/ (function(module, exports, __webpack_require__) {

var arrayPush = __webpack_require__(287),
    isArray = __webpack_require__(70);

/**
 * The base implementation of `getAllKeys` and `getAllKeysIn` which uses
 * `keysFunc` and `symbolsFunc` to get the enumerable property names and
 * symbols of `object`.
 *
 * @private
 * @param {Object} object The object to query.
 * @param {Function} keysFunc The function to get the keys of `object`.
 * @param {Function} symbolsFunc The function to get the symbols of `object`.
 * @returns {Array} Returns the array of property names and symbols.
 */
function baseGetAllKeys(object, keysFunc, symbolsFunc) {
  var result = keysFunc(object);
  return isArray(object) ? result : arrayPush(result, symbolsFunc(object));
}

module.exports = baseGetAllKeys;


/***/ }),

/***/ 287:
/***/ (function(module, exports) {

/**
 * 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;
}

module.exports = arrayPush;


/***/ }),

/***/ 288:
/***/ (function(module, exports, __webpack_require__) {

var arrayFilter = __webpack_require__(289),
    stubArray = __webpack_require__(290);

/** Used for built-in method references. */
var objectProto = Object.prototype;

/** Built-in value references. */
var propertyIsEnumerable = objectProto.propertyIsEnumerable;

/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeGetSymbols = Object.getOwnPropertySymbols;

/**
 * Creates an array of the own enumerable symbols of `object`.
 *
 * @private
 * @param {Object} object The object to query.
 * @returns {Array} Returns the array of symbols.
 */
var getSymbols = !nativeGetSymbols ? stubArray : function(object) {
  if (object == null) {
    return [];
  }
  object = Object(object);
  return arrayFilter(nativeGetSymbols(object), function(symbol) {
    return propertyIsEnumerable.call(object, symbol);
  });
};

module.exports = getSymbols;


/***/ }),

/***/ 289:
/***/ (function(module, exports) {

/**
 * A specialized version of `_.filter` 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 {Array} Returns the new filtered array.
 */
function arrayFilter(array, predicate) {
  var index = -1,
      length = array == null ? 0 : array.length,
      resIndex = 0,
      result = [];

  while (++index < length) {
    var value = array[index];
    if (predicate(value, index, array)) {
      result[resIndex++] = value;
    }
  }
  return result;
}

module.exports = arrayFilter;


/***/ }),

/***/ 290:
/***/ (function(module, exports) {

/**
 * This method returns a new empty array.
 *
 * @static
 * @memberOf _
 * @since 4.13.0
 * @category Util
 * @returns {Array} Returns the new empty array.
 * @example
 *
 * var arrays = _.times(2, _.stubArray);
 *
 * console.log(arrays);
 * // => [[], []]
 *
 * console.log(arrays[0] === arrays[1]);
 * // => false
 */
function stubArray() {
  return [];
}

module.exports = stubArray;


/***/ }),

/***/ 294:
/***/ (function(module, exports, __webpack_require__) {

var baseIsEqual = __webpack_require__(799),
    get = __webpack_require__(67),
    hasIn = __webpack_require__(837),
    isKey = __webpack_require__(71),
    isStrictComparable = __webpack_require__(801),
    matchesStrictComparable = __webpack_require__(803),
    toKey = __webpack_require__(111);

/** Used to compose bitmasks for value comparisons. */
var COMPARE_PARTIAL_FLAG = 1,
    COMPARE_UNORDERED_FLAG = 2;

/**
 * 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, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG);
  };
}

module.exports = baseMatchesProperty;


/***/ }),

/***/ 297:
/***/ (function(module, exports, __webpack_require__) {

var castPath = __webpack_require__(69),
    isArguments = __webpack_require__(208),
    isArray = __webpack_require__(70),
    isIndex = __webpack_require__(117),
    isLength = __webpack_require__(214),
    toKey = __webpack_require__(111);

/**
 * 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 = castPath(path, object);

  var index = -1,
      length = path.length,
      result = false;

  while (++index < length) {
    var key = toKey(path[index]);
    if (!(result = object != null && hasFunc(object, key))) {
      break;
    }
    object = object[key];
  }
  if (result || ++index != length) {
    return result;
  }
  length = object == null ? 0 : object.length;
  return !!length && isLength(length) && isIndex(key, length) &&
    (isArray(object) || isArguments(object));
}

module.exports = hasPath;


/***/ }),

/***/ 298:
/***/ (function(module, exports) {

/**
 * 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;
}

module.exports = identity;


/***/ }),

/***/ 302:
/***/ (function(module, exports, __webpack_require__) {

var toFinite = __webpack_require__(303);

/**
 * Converts `value` to an integer.
 *
 * **Note:** This method is loosely based on
 * [`ToInteger`](http://www.ecma-international.org/ecma-262/7.0/#sec-tointeger).
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to convert.
 * @returns {number} Returns the converted integer.
 * @example
 *
 * _.toInteger(3.2);
 * // => 3
 *
 * _.toInteger(Number.MIN_VALUE);
 * // => 0
 *
 * _.toInteger(Infinity);
 * // => 1.7976931348623157e+308
 *
 * _.toInteger('3.2');
 * // => 3
 */
function toInteger(value) {
  var result = toFinite(value),
      remainder = result % 1;

  return result === result ? (remainder ? result - remainder : result) : 0;
}

module.exports = toInteger;


/***/ }),

/***/ 303:
/***/ (function(module, exports, __webpack_require__) {

var toNumber = __webpack_require__(304);

/** Used as references for various `Number` constants. */
var INFINITY = 1 / 0,
    MAX_INTEGER = 1.7976931348623157e+308;

/**
 * Converts `value` to a finite number.
 *
 * @static
 * @memberOf _
 * @since 4.12.0
 * @category Lang
 * @param {*} value The value to convert.
 * @returns {number} Returns the converted number.
 * @example
 *
 * _.toFinite(3.2);
 * // => 3.2
 *
 * _.toFinite(Number.MIN_VALUE);
 * // => 5e-324
 *
 * _.toFinite(Infinity);
 * // => 1.7976931348623157e+308
 *
 * _.toFinite('3.2');
 * // => 3.2
 */
function toFinite(value) {
  if (!value) {
    return value === 0 ? value : 0;
  }
  value = toNumber(value);
  if (value === INFINITY || value === -INFINITY) {
    var sign = (value < 0 ? -1 : 1);
    return sign * MAX_INTEGER;
  }
  return value === value ? value : 0;
}

module.exports = toFinite;


/***/ }),

/***/ 304:
/***/ (function(module, exports, __webpack_require__) {

var isObject = __webpack_require__(84),
    isSymbol = __webpack_require__(72);

/** Used as references for various `Number` constants. */
var NAN = 0 / 0;

/** Used to match leading and trailing whitespace. */
var reTrim = /^\s+|\s+$/g;

/** Used to detect bad signed hexadecimal string values. */
var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;

/** Used to detect binary string values. */
var reIsBinary = /^0b[01]+$/i;

/** Used to detect octal string values. */
var reIsOctal = /^0o[0-7]+$/i;

/** Built-in method references without a dependency on `root`. */
var freeParseInt = parseInt;

/**
 * Converts `value` to a number.
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to process.
 * @returns {number} Returns the number.
 * @example
 *
 * _.toNumber(3.2);
 * // => 3.2
 *
 * _.toNumber(Number.MIN_VALUE);
 * // => 5e-324
 *
 * _.toNumber(Infinity);
 * // => Infinity
 *
 * _.toNumber('3.2');
 * // => 3.2
 */
function toNumber(value) {
  if (typeof value == 'number') {
    return value;
  }
  if (isSymbol(value)) {
    return NAN;
  }
  if (isObject(value)) {
    var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
    value = isObject(other) ? (other + '') : other;
  }
  if (typeof value != 'string') {
    return value === 0 ? value : +value;
  }
  value = value.replace(reTrim, '');
  var isBinary = reIsBinary.test(value);
  return (isBinary || reIsOctal.test(value))
    ? freeParseInt(value.slice(2), isBinary ? 2 : 8)
    : (reIsBadHex.test(value) ? NAN : +value);
}

module.exports = toNumber;


/***/ }),

/***/ 3590:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = createSimplePath;
function createSimplePath(ancestors) {
  if (ancestors.length === 0) {
    return null;
  }

  // Slice the array because babel-types traverse may continue mutating
  // the ancestors array in later traversal logic.
  return new SimplePath(ancestors.slice());
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/**
 * Mimics @babel/traverse's NodePath API in a simpler fashion that isn't as
 * heavy, but still allows the ease of passing paths around to process nested
 * AST structures.
 */
class SimplePath {

  constructor(ancestors, index = ancestors.length - 1) {
    if (index < 0 || index >= ancestors.length) {
      console.error(ancestors);
      throw new Error("Created invalid path");
    }

    this._ancestors = ancestors;
    this._ancestor = ancestors[index];
    this._index = index;
  }

  get parentPath() {
    let path = this._parentPath;
    if (path === undefined) {
      if (this._index === 0) {
        path = null;
      } else {
        path = new SimplePath(this._ancestors, this._index - 1);
      }
      this._parentPath = path;
    }

    return path;
  }

  get parent() {
    return this._ancestor.node;
  }

  get node() {
    const { node, key, index } = this._ancestor;

    if (typeof index === "number") {
      return node[key][index];
    }

    return node[key];
  }

  set node(replacement) {
    if (this.type !== "Identifier") {
      throw new Error("Replacing anything other than leaf nodes is undefined behavior " + "in t.traverse()");
    }

    const { node, key, index } = this._ancestor;
    if (typeof index === "number") {
      node[key][index] = replacement;
    } else {
      node[key] = replacement;
    }
  }

  get type() {
    return this.node.type;
  }

  get inList() {
    return typeof this._ancestor.index === "number";
  }

  get containerIndex() {
    const { index } = this._ancestor;

    if (typeof index !== "number") {
      throw new Error("Cannot get index of non-array node");
    }

    return index;
  }

  get depth() {
    return this._index;
  }

  replace(node) {
    this.node = node;
  }

  find(predicate) {
    for (let path = this; path; path = path.parentPath) {
      if (predicate(path)) {
        return path;
      }
    }
    return null;
  }
  findParent(predicate) {
    if (!this.parentPath) {
      throw new Error("Cannot use findParent on root path");
    }

    return this.parentPath.find(predicate);
  }

  getSibling(offset) {
    const { node, key, index } = this._ancestor;

    if (typeof index !== "number") {
      throw new Error("Non-array nodes do not have siblings");
    }

    const container = node[key];

    const siblingIndex = index + offset;
    if (siblingIndex < 0 || siblingIndex >= container.length) {
      return null;
    }

    return new SimplePath(this._ancestors.slice(0, -1).concat([{ node, key, index: siblingIndex }]));
  }
}

/***/ }),

/***/ 398:
/***/ (function(module, exports, __webpack_require__) {

/* WEBPACK VAR INJECTION */(function(module) {var root = __webpack_require__(8);

/** 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;

/** Built-in value references. */
var Buffer = moduleExports ? root.Buffer : undefined,
    allocUnsafe = Buffer ? Buffer.allocUnsafe : undefined;

/**
 * Creates a clone of  `buffer`.
 *
 * @private
 * @param {Buffer} buffer The buffer to clone.
 * @param {boolean} [isDeep] Specify a deep clone.
 * @returns {Buffer} Returns the cloned buffer.
 */
function cloneBuffer(buffer, isDeep) {
  if (isDeep) {
    return buffer.slice();
  }
  var length = buffer.length,
      result = allocUnsafe ? allocUnsafe(length) : new buffer.constructor(length);

  buffer.copy(result);
  return result;
}

module.exports = cloneBuffer;

/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(793)(module)))

/***/ }),

/***/ 399:
/***/ (function(module, exports, __webpack_require__) {

var cloneArrayBuffer = __webpack_require__(400);

/**
 * Creates a clone of `typedArray`.
 *
 * @private
 * @param {Object} typedArray The typed array to clone.
 * @param {boolean} [isDeep] Specify a deep clone.
 * @returns {Object} Returns the cloned typed array.
 */
function cloneTypedArray(typedArray, isDeep) {
  var buffer = isDeep ? cloneArrayBuffer(typedArray.buffer) : typedArray.buffer;
  return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length);
}

module.exports = cloneTypedArray;


/***/ }),

/***/ 400:
/***/ (function(module, exports, __webpack_require__) {

var Uint8Array = __webpack_require__(282);

/**
 * Creates a clone of `arrayBuffer`.
 *
 * @private
 * @param {ArrayBuffer} arrayBuffer The array buffer to clone.
 * @returns {ArrayBuffer} Returns the cloned array buffer.
 */
function cloneArrayBuffer(arrayBuffer) {
  var result = new arrayBuffer.constructor(arrayBuffer.byteLength);
  new Uint8Array(result).set(new Uint8Array(arrayBuffer));
  return result;
}

module.exports = cloneArrayBuffer;


/***/ }),

/***/ 401:
/***/ (function(module, exports) {

/**
 * Copies the values of `source` to `array`.
 *
 * @private
 * @param {Array} source The array to copy values from.
 * @param {Array} [array=[]] The array to copy values to.
 * @returns {Array} Returns `array`.
 */
function copyArray(source, array) {
  var index = -1,
      length = source.length;

  array || (array = Array(length));
  while (++index < length) {
    array[index] = source[index];
  }
  return array;
}

module.exports = copyArray;


/***/ }),

/***/ 402:
/***/ (function(module, exports, __webpack_require__) {

var baseCreate = __webpack_require__(403),
    getPrototype = __webpack_require__(12),
    isPrototype = __webpack_require__(218);

/**
 * Initializes an object clone.
 *
 * @private
 * @param {Object} object The object to clone.
 * @returns {Object} Returns the initialized clone.
 */
function initCloneObject(object) {
  return (typeof object.constructor == 'function' && !isPrototype(object))
    ? baseCreate(getPrototype(object))
    : {};
}

module.exports = initCloneObject;


/***/ }),

/***/ 403:
/***/ (function(module, exports, __webpack_require__) {

var isObject = __webpack_require__(84);

/** Built-in value references. */
var objectCreate = Object.create;

/**
 * The base implementation of `_.create` without support for assigning
 * properties to the created object.
 *
 * @private
 * @param {Object} proto The object to inherit from.
 * @returns {Object} Returns the new object.
 */
var baseCreate = (function() {
  function object() {}
  return function(proto) {
    if (!isObject(proto)) {
      return {};
    }
    if (objectCreate) {
      return objectCreate(proto);
    }
    object.prototype = proto;
    var result = new object;
    object.prototype = undefined;
    return result;
  };
}());

module.exports = baseCreate;


/***/ }),

/***/ 406:
/***/ (function(module, exports, __webpack_require__) {

var assignValue = __webpack_require__(114),
    baseAssignValue = __webpack_require__(115);

/**
 * Copies properties of `source` to `object`.
 *
 * @private
 * @param {Object} source The object to copy properties from.
 * @param {Array} props The property identifiers to copy.
 * @param {Object} [object={}] The object to copy properties to.
 * @param {Function} [customizer] The function to customize copied values.
 * @returns {Object} Returns `object`.
 */
function copyObject(source, props, object, customizer) {
  var isNew = !object;
  object || (object = {});

  var index = -1,
      length = props.length;

  while (++index < length) {
    var key = props[index];

    var newValue = customizer
      ? customizer(object[key], source[key], key, object, source)
      : undefined;

    if (newValue === undefined) {
      newValue = source[key];
    }
    if (isNew) {
      baseAssignValue(object, key, newValue);
    } else {
      assignValue(object, key, newValue);
    }
  }
  return object;
}

module.exports = copyObject;


/***/ }),

/***/ 407:
/***/ (function(module, exports, __webpack_require__) {

var arrayLikeKeys = __webpack_require__(206),
    baseKeysIn = __webpack_require__(408),
    isArrayLike = __webpack_require__(220);

/**
 * Creates an array of the own and inherited enumerable property names of `object`.
 *
 * **Note:** Non-object values are coerced to objects.
 *
 * @static
 * @memberOf _
 * @since 3.0.0
 * @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;
 *
 * _.keysIn(new Foo);
 * // => ['a', 'b', 'c'] (iteration order is not guaranteed)
 */
function keysIn(object) {
  return isArrayLike(object) ? arrayLikeKeys(object, true) : baseKeysIn(object);
}

module.exports = keysIn;


/***/ }),

/***/ 408:
/***/ (function(module, exports, __webpack_require__) {

var isObject = __webpack_require__(84),
    isPrototype = __webpack_require__(218),
    nativeKeysIn = __webpack_require__(409);

/** Used for built-in method references. */
var objectProto = Object.prototype;

/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;

/**
 * The base implementation of `_.keysIn` 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 baseKeysIn(object) {
  if (!isObject(object)) {
    return nativeKeysIn(object);
  }
  var isProto = isPrototype(object),
      result = [];

  for (var key in object) {
    if (!(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) {
      result.push(key);
    }
  }
  return result;
}

module.exports = baseKeysIn;


/***/ }),

/***/ 409:
/***/ (function(module, exports) {

/**
 * This function is like
 * [`Object.keys`](http://ecma-international.org/ecma-262/7.0/#sec-object.keys)
 * except that it includes inherited enumerable properties.
 *
 * @private
 * @param {Object} object The object to query.
 * @returns {Array} Returns the array of property names.
 */
function nativeKeysIn(object) {
  var result = [];
  if (object != null) {
    for (var key in Object(object)) {
      result.push(key);
    }
  }
  return result;
}

module.exports = nativeKeysIn;


/***/ }),

/***/ 411:
/***/ (function(module, exports, __webpack_require__) {

var identity = __webpack_require__(298),
    overRest = __webpack_require__(412),
    setToString = __webpack_require__(414);

/**
 * 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) {
  return setToString(overRest(func, start, identity), func + '');
}

module.exports = baseRest;


/***/ }),

/***/ 412:
/***/ (function(module, exports, __webpack_require__) {

var apply = __webpack_require__(413);

/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeMax = Math.max;

/**
 * A specialized version of `baseRest` which transforms the rest array.
 *
 * @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.
 * @param {Function} transform The rest array transform.
 * @returns {Function} Returns the new function.
 */
function overRest(func, start, transform) {
  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] = transform(array);
    return apply(func, this, otherArgs);
  };
}

module.exports = overRest;


/***/ }),

/***/ 413:
/***/ (function(module, exports) {

/**
 * 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);
}

module.exports = apply;


/***/ }),

/***/ 414:
/***/ (function(module, exports, __webpack_require__) {

var baseSetToString = __webpack_require__(415),
    shortOut = __webpack_require__(417);

/**
 * Sets the `toString` method of `func` to return `string`.
 *
 * @private
 * @param {Function} func The function to modify.
 * @param {Function} string The `toString` result.
 * @returns {Function} Returns `func`.
 */
var setToString = shortOut(baseSetToString);

module.exports = setToString;


/***/ }),

/***/ 415:
/***/ (function(module, exports, __webpack_require__) {

var constant = __webpack_require__(416),
    defineProperty = __webpack_require__(116),
    identity = __webpack_require__(298);

/**
 * The base implementation of `setToString` without support for hot loop shorting.
 *
 * @private
 * @param {Function} func The function to modify.
 * @param {Function} string The `toString` result.
 * @returns {Function} Returns `func`.
 */
var baseSetToString = !defineProperty ? identity : function(func, string) {
  return defineProperty(func, 'toString', {
    'configurable': true,
    'enumerable': false,
    'value': constant(string),
    'writable': true
  });
};

module.exports = baseSetToString;


/***/ }),

/***/ 416:
/***/ (function(module, exports) {

/**
 * Creates a function that returns `value`.
 *
 * @static
 * @memberOf _
 * @since 2.4.0
 * @category Util
 * @param {*} value The value to return from the new function.
 * @returns {Function} Returns the new constant function.
 * @example
 *
 * var objects = _.times(2, _.constant({ 'a': 1 }));
 *
 * console.log(objects);
 * // => [{ 'a': 1 }, { 'a': 1 }]
 *
 * console.log(objects[0] === objects[1]);
 * // => true
 */
function constant(value) {
  return function() {
    return value;
  };
}

module.exports = constant;


/***/ }),

/***/ 417:
/***/ (function(module, exports) {

/** Used to detect hot functions by number of calls within a span of milliseconds. */
var HOT_COUNT = 800,
    HOT_SPAN = 16;

/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeNow = Date.now;

/**
 * Creates a function that'll short out and invoke `identity` instead
 * of `func` when it's called `HOT_COUNT` or more times in `HOT_SPAN`
 * milliseconds.
 *
 * @private
 * @param {Function} func The function to restrict.
 * @returns {Function} Returns the new shortable function.
 */
function shortOut(func) {
  var count = 0,
      lastCalled = 0;

  return function() {
    var stamp = nativeNow(),
        remaining = HOT_SPAN - (stamp - lastCalled);

    lastCalled = stamp;
    if (remaining > 0) {
      if (++count >= HOT_COUNT) {
        return arguments[0];
      }
    } else {
      count = 0;
    }
    return func.apply(undefined, arguments);
  };
}

module.exports = shortOut;


/***/ }),

/***/ 435:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, '__esModule', { value: true });

function _inheritsLoose(subClass, superClass) {
  subClass.prototype = Object.create(superClass.prototype);
  subClass.prototype.constructor = subClass;
  subClass.__proto__ = superClass;
}

// A second optional argument can be given to further configure
// the parser process. These options are recognized:
var defaultOptions = {
  // Source type ("script" or "module") for different semantics
  sourceType: "script",
  // Source filename.
  sourceFilename: undefined,
  // Line from which to start counting source. Useful for
  // integration with other tools.
  startLine: 1,
  // When enabled, a return at the top level is not considered an
  // error.
  allowReturnOutsideFunction: false,
  // When enabled, import/export statements are not constrained to
  // appearing at the top of the program.
  allowImportExportEverywhere: false,
  // TODO
  allowSuperOutsideMethod: false,
  // An array of plugins to enable
  plugins: [],
  // TODO
  strictMode: null,
  // Nodes have their start and end characters offsets recorded in
  // `start` and `end` properties (directly on the node, rather than
  // the `loc` object, which holds line/column data. To also add a
  // [semi-standardized][range] `range` property holding a `[start,
  // end]` array with the same numbers, set the `ranges` option to
  // `true`.
  //
  // [range]: https://bugzilla.mozilla.org/show_bug.cgi?id=745678
  ranges: false,
  // Adds all parsed tokens to a `tokens` property on the `File` node
  tokens: false
}; // Interpret and default an options object

function getOptions(opts) {
  var options = {};

  for (var key in defaultOptions) {
    options[key] = opts && opts[key] != null ? opts[key] : defaultOptions[key];
  }

  return options;
}

// ## Token types
// The assignment of fine-grained, information-carrying type objects
// allows the tokenizer to store the information it has about a
// token in a way that is very cheap for the parser to look up.
// All token type variables start with an underscore, to make them
// easy to recognize.
// The `beforeExpr` property is used to disambiguate between regular
// expressions and divisions. It is set on all token types that can
// be followed by an expression (thus, a slash after them would be a
// regular expression).
//
// `isLoop` marks a keyword as starting a loop, which is important
// to know when parsing a label, in order to allow or disallow
// continue jumps to that label.
var beforeExpr = true;
var startsExpr = true;
var isLoop = true;
var isAssign = true;
var prefix = true;
var postfix = true;
var TokenType = function TokenType(label, conf) {
  if (conf === void 0) {
    conf = {};
  }

  this.label = label;
  this.keyword = conf.keyword;
  this.beforeExpr = !!conf.beforeExpr;
  this.startsExpr = !!conf.startsExpr;
  this.rightAssociative = !!conf.rightAssociative;
  this.isLoop = !!conf.isLoop;
  this.isAssign = !!conf.isAssign;
  this.prefix = !!conf.prefix;
  this.postfix = !!conf.postfix;
  this.binop = conf.binop === 0 ? 0 : conf.binop || null;
  this.updateContext = null;
};

var KeywordTokenType =
/*#__PURE__*/
function (_TokenType) {
  _inheritsLoose(KeywordTokenType, _TokenType);

  function KeywordTokenType(name, options) {
    if (options === void 0) {
      options = {};
    }

    options.keyword = name;
    return _TokenType.call(this, name, options) || this;
  }

  return KeywordTokenType;
}(TokenType);

var BinopTokenType =
/*#__PURE__*/
function (_TokenType2) {
  _inheritsLoose(BinopTokenType, _TokenType2);

  function BinopTokenType(name, prec) {
    return _TokenType2.call(this, name, {
      beforeExpr,
      binop: prec
    }) || this;
  }

  return BinopTokenType;
}(TokenType);
var types = {
  num: new TokenType("num", {
    startsExpr
  }),
  bigint: new TokenType("bigint", {
    startsExpr
  }),
  regexp: new TokenType("regexp", {
    startsExpr
  }),
  string: new TokenType("string", {
    startsExpr
  }),
  name: new TokenType("name", {
    startsExpr
  }),
  eof: new TokenType("eof"),
  // Punctuation token types.
  bracketL: new TokenType("[", {
    beforeExpr,
    startsExpr
  }),
  bracketR: new TokenType("]"),
  braceL: new TokenType("{", {
    beforeExpr,
    startsExpr
  }),
  braceBarL: new TokenType("{|", {
    beforeExpr,
    startsExpr
  }),
  braceR: new TokenType("}"),
  braceBarR: new TokenType("|}"),
  parenL: new TokenType("(", {
    beforeExpr,
    startsExpr
  }),
  parenR: new TokenType(")"),
  comma: new TokenType(",", {
    beforeExpr
  }),
  semi: new TokenType(";", {
    beforeExpr
  }),
  colon: new TokenType(":", {
    beforeExpr
  }),
  doubleColon: new TokenType("::", {
    beforeExpr
  }),
  dot: new TokenType("."),
  question: new TokenType("?", {
    beforeExpr
  }),
  questionDot: new TokenType("?."),
  arrow: new TokenType("=>", {
    beforeExpr
  }),
  template: new TokenType("template"),
  ellipsis: new TokenType("...", {
    beforeExpr
  }),
  backQuote: new TokenType("`", {
    startsExpr
  }),
  dollarBraceL: new TokenType("${", {
    beforeExpr,
    startsExpr
  }),
  at: new TokenType("@"),
  hash: new TokenType("#"),
  // Operators. These carry several kinds of properties to help the
  // parser use them properly (the presence of these properties is
  // what categorizes them as operators).
  //
  // `binop`, when present, specifies that this operator is a binary
  // operator, and will refer to its precedence.
  //
  // `prefix` and `postfix` mark the operator as a prefix or postfix
  // unary operator.
  //
  // `isAssign` marks all of `=`, `+=`, `-=` etcetera, which act as
  // binary operators with a very low precedence, that should result
  // in AssignmentExpression nodes.
  eq: new TokenType("=", {
    beforeExpr,
    isAssign
  }),
  assign: new TokenType("_=", {
    beforeExpr,
    isAssign
  }),
  incDec: new TokenType("++/--", {
    prefix,
    postfix,
    startsExpr
  }),
  bang: new TokenType("!", {
    beforeExpr,
    prefix,
    startsExpr
  }),
  tilde: new TokenType("~", {
    beforeExpr,
    prefix,
    startsExpr
  }),
  pipeline: new BinopTokenType("|>", 0),
  nullishCoalescing: new BinopTokenType("??", 1),
  logicalOR: new BinopTokenType("||", 1),
  logicalAND: new BinopTokenType("&&", 2),
  bitwiseOR: new BinopTokenType("|", 3),
  bitwiseXOR: new BinopTokenType("^", 4),
  bitwiseAND: new BinopTokenType("&", 5),
  equality: new BinopTokenType("==/!=", 6),
  relational: new BinopTokenType("</>", 7),
  bitShift: new BinopTokenType("<</>>", 8),
  plusMin: new TokenType("+/-", {
    beforeExpr,
    binop: 9,
    prefix,
    startsExpr
  }),
  modulo: new BinopTokenType("%", 10),
  star: new BinopTokenType("*", 10),
  slash: new BinopTokenType("/", 10),
  exponent: new TokenType("**", {
    beforeExpr,
    binop: 11,
    rightAssociative: true
  })
};
var keywords = {
  break: new KeywordTokenType("break"),
  case: new KeywordTokenType("case", {
    beforeExpr
  }),
  catch: new KeywordTokenType("catch"),
  continue: new KeywordTokenType("continue"),
  debugger: new KeywordTokenType("debugger"),
  default: new KeywordTokenType("default", {
    beforeExpr
  }),
  do: new KeywordTokenType("do", {
    isLoop,
    beforeExpr
  }),
  else: new KeywordTokenType("else", {
    beforeExpr
  }),
  finally: new KeywordTokenType("finally"),
  for: new KeywordTokenType("for", {
    isLoop
  }),
  function: new KeywordTokenType("function", {
    startsExpr
  }),
  if: new KeywordTokenType("if"),
  return: new KeywordTokenType("return", {
    beforeExpr
  }),
  switch: new KeywordTokenType("switch"),
  throw: new KeywordTokenType("throw", {
    beforeExpr,
    prefix,
    startsExpr
  }),
  try: new KeywordTokenType("try"),
  var: new KeywordTokenType("var"),
  let: new KeywordTokenType("let"),
  const: new KeywordTokenType("const"),
  while: new KeywordTokenType("while", {
    isLoop
  }),
  with: new KeywordTokenType("with"),
  new: new KeywordTokenType("new", {
    beforeExpr,
    startsExpr
  }),
  this: new KeywordTokenType("this", {
    startsExpr
  }),
  super: new KeywordTokenType("super", {
    startsExpr
  }),
  class: new KeywordTokenType("class"),
  extends: new KeywordTokenType("extends", {
    beforeExpr
  }),
  export: new KeywordTokenType("export"),
  import: new KeywordTokenType("import", {
    startsExpr
  }),
  yield: new KeywordTokenType("yield", {
    beforeExpr,
    startsExpr
  }),
  null: new KeywordTokenType("null", {
    startsExpr
  }),
  true: new KeywordTokenType("true", {
    startsExpr
  }),
  false: new KeywordTokenType("false", {
    startsExpr
  }),
  in: new KeywordTokenType("in", {
    beforeExpr,
    binop: 7
  }),
  instanceof: new KeywordTokenType("instanceof", {
    beforeExpr,
    binop: 7
  }),
  typeof: new KeywordTokenType("typeof", {
    beforeExpr,
    prefix,
    startsExpr
  }),
  void: new KeywordTokenType("void", {
    beforeExpr,
    prefix,
    startsExpr
  }),
  delete: new KeywordTokenType("delete", {
    beforeExpr,
    prefix,
    startsExpr
  })
}; // Map keyword names to token types.

Object.keys(keywords).forEach(function (name) {
  types["_" + name] = keywords[name];
});

/* eslint max-len: 0 */
function makePredicate(words) {
  var wordsArr = words.split(" ");
  return function (str) {
    return wordsArr.indexOf(str) >= 0;
  };
} // Reserved word lists for various dialects of the language


var reservedWords = {
  "6": makePredicate("enum await"),
  strict: makePredicate("implements interface let package private protected public static yield"),
  strictBind: makePredicate("eval arguments")
}; // And the keywords

var isKeyword = makePredicate("break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this let const class extends export import yield super"); // ## Character categories
// Big ugly regular expressions that match characters in the
// whitespace, identifier, and identifier-start categories. These
// are only applied when a character is found to actually have a
// code point above 128.
// Generated by `bin/generate-identifier-regex.js`.

/* prettier-ignore */

var nonASCIIidentifierStartChars = "\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\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-\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-\u312e\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fea\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";
/* prettier-ignore */

var nonASCIIidentifierChars = "\u200c\u200d\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\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u08d4-\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\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-\u0c03\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\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\ua900-\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";
var nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]");
var nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]");
nonASCIIidentifierStartChars = nonASCIIidentifierChars = null; // These are a run-length and offset encoded representation of the
// >0xffff code points that are a valid part of identifiers. The
// offset starts at 0x10000, and each pair of numbers represents an
// offset to the next range, and then a size of the range. They were
// generated by `bin/generate-identifier-regex.js`.

/* prettier-ignore */

var astralIdentifierStartCodes = [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, 26, 45, 28, 4, 28, 36, 7, 2, 27, 28, 53, 11, 21, 11, 18, 14, 17, 111, 72, 56, 50, 14, 50, 785, 52, 76, 44, 33, 24, 27, 35, 42, 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, 54, 47, 21, 1, 2, 0, 185, 46, 42, 3, 37, 47, 21, 0, 60, 42, 86, 25, 391, 63, 32, 0, 257, 0, 11, 39, 8, 0, 22, 0, 12, 39, 3, 3, 55, 56, 264, 8, 2, 36, 18, 0, 50, 29, 113, 6, 2, 1, 2, 37, 22, 0, 698, 921, 103, 110, 18, 195, 2749, 1070, 4050, 582, 8634, 568, 8, 30, 114, 29, 19, 47, 17, 3, 32, 20, 6, 18, 881, 68, 12, 0, 67, 12, 65, 1, 31, 6124, 20, 754, 9486, 286, 82, 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, 4149, 196, 60, 67, 1213, 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];
/* prettier-ignore */

var astralIdentifierCodes = [509, 0, 227, 0, 150, 4, 294, 9, 1368, 2, 2, 1, 6, 3, 41, 2, 5, 0, 166, 1, 1306, 2, 54, 14, 32, 9, 16, 3, 46, 10, 54, 9, 7, 2, 37, 13, 2, 9, 52, 0, 13, 2, 49, 13, 10, 2, 4, 9, 83, 11, 7, 0, 161, 11, 6, 9, 7, 3, 57, 0, 2, 6, 3, 1, 3, 2, 10, 0, 11, 1, 3, 6, 4, 4, 193, 17, 10, 9, 87, 19, 13, 9, 214, 6, 3, 8, 28, 1, 83, 16, 16, 9, 82, 12, 9, 9, 84, 14, 5, 9, 423, 9, 280, 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, 19719, 9, 135, 4, 60, 6, 26, 9, 1016, 45, 17, 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, 2214, 6, 110, 6, 6, 9, 792487, 239]; // This has a complexity linear to the value of the code. The
// assumption is that looking up astral identifier characters is
// rare.

function isInAstralSet(code, set) {
  var pos = 0x10000;

  for (var i = 0; i < set.length; i += 2) {
    pos += set[i];
    if (pos > code) return false;
    pos += set[i + 1];
    if (pos >= code) return true;
  }

  return false;
} // Test whether a given character code starts an identifier.


function isIdentifierStart(code) {
  if (code < 65) return code === 36;
  if (code < 91) return true;
  if (code < 97) return code === 95;
  if (code < 123) return true;

  if (code <= 0xffff) {
    return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code));
  }

  return isInAstralSet(code, astralIdentifierStartCodes);
} // Test whether a current state character code and next character code  is @

function isIteratorStart(current, next) {
  return current === 64 && next === 64;
} // Test whether a given character is part of an identifier.

function isIdentifierChar(code) {
  if (code < 48) return code === 36;
  if (code < 58) return true;
  if (code < 65) return false;
  if (code < 91) return true;
  if (code < 97) return code === 95;
  if (code < 123) return true;

  if (code <= 0xffff) {
    return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code));
  }

  return isInAstralSet(code, astralIdentifierStartCodes) || isInAstralSet(code, astralIdentifierCodes);
}

// Matches a whole line break (where CRLF is considered a single
// line break). Used to count lines.
var lineBreak = /\r\n?|\n|\u2028|\u2029/;
var lineBreakG = new RegExp(lineBreak.source, "g");
function isNewLine(code) {
  return code === 10 || code === 13 || code === 0x2028 || code === 0x2029;
}
var nonASCIIwhitespace = /[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/;

// The algorithm used to determine whether a regexp can appear at a
// given point in the program is loosely based on sweet.js' approach.
// See https://github.com/mozilla/sweet.js/wiki/design
var TokContext = function TokContext(token, isExpr, preserveSpace, override) // Takes a Tokenizer as a this-parameter, and returns void.
{
  this.token = token;
  this.isExpr = !!isExpr;
  this.preserveSpace = !!preserveSpace;
  this.override = override;
};
var types$1 = {
  braceStatement: new TokContext("{", false),
  braceExpression: new TokContext("{", true),
  templateQuasi: new TokContext("${", true),
  parenStatement: new TokContext("(", false),
  parenExpression: new TokContext("(", true),
  template: new TokContext("`", true, true, function (p) {
    return p.readTmplToken();
  }),
  functionExpression: new TokContext("function", true)
}; // Token-specific context update code

types.parenR.updateContext = types.braceR.updateContext = function () {
  if (this.state.context.length === 1) {
    this.state.exprAllowed = true;
    return;
  }

  var out = this.state.context.pop();

  if (out === types$1.braceStatement && this.curContext() === types$1.functionExpression) {
    this.state.context.pop();
    this.state.exprAllowed = false;
  } else if (out === types$1.templateQuasi) {
    this.state.exprAllowed = true;
  } else {
    this.state.exprAllowed = !out.isExpr;
  }
};

types.name.updateContext = function (prevType) {
  if (this.state.value === "of" && this.curContext() === types$1.parenStatement) {
    this.state.exprAllowed = !prevType.beforeExpr;
    return;
  }

  this.state.exprAllowed = false;

  if (prevType === types._let || prevType === types._const || prevType === types._var) {
    if (lineBreak.test(this.input.slice(this.state.end))) {
      this.state.exprAllowed = true;
    }
  }

  if (this.state.isIterator) {
    this.state.isIterator = false;
  }
};

types.braceL.updateContext = function (prevType) {
  this.state.context.push(this.braceIsBlock(prevType) ? types$1.braceStatement : types$1.braceExpression);
  this.state.exprAllowed = true;
};

types.dollarBraceL.updateContext = function () {
  this.state.context.push(types$1.templateQuasi);
  this.state.exprAllowed = true;
};

types.parenL.updateContext = function (prevType) {
  var statementParens = prevType === types._if || prevType === types._for || prevType === types._with || prevType === types._while;
  this.state.context.push(statementParens ? types$1.parenStatement : types$1.parenExpression);
  this.state.exprAllowed = true;
};

types.incDec.updateContext = function () {// tokExprAllowed stays unchanged
};

types._function.updateContext = function (prevType) {
  if (this.state.exprAllowed && !this.braceIsBlock(prevType)) {
    this.state.context.push(types$1.functionExpression);
  }

  this.state.exprAllowed = false;
};

types.backQuote.updateContext = function () {
  if (this.curContext() === types$1.template) {
    this.state.context.pop();
  } else {
    this.state.context.push(types$1.template);
  }

  this.state.exprAllowed = false;
};

// These are used when `options.locations` is on, for the
// `startLoc` and `endLoc` properties.
var Position = function Position(line, col) {
  this.line = line;
  this.column = col;
};
var SourceLocation = function SourceLocation(start, end) {
  this.start = start; // $FlowIgnore (may start as null, but initialized later)

  this.end = end;
}; // The `getLineInfo` function is mostly useful when the
// `locations` option is off (for performance reasons) and you
// want to find the line/column position for a given character
// offset. `input` should be the code string that the offset refers
// into.

function getLineInfo(input, offset) {
  for (var line = 1, cur = 0;;) {
    lineBreakG.lastIndex = cur;
    var match = lineBreakG.exec(input);

    if (match && match.index < offset) {
      ++line;
      cur = match.index + match[0].length;
    } else {
      return new Position(line, offset - cur);
    }
  } // istanbul ignore next


  throw new Error("Unreachable");
}

var BaseParser =
/*#__PURE__*/
function () {
  function BaseParser() {}

  var _proto = BaseParser.prototype;

  // Properties set by constructor in index.js
  // Initialized by Tokenizer
  _proto.isReservedWord = function isReservedWord(word) {
    if (word === "await") {
      return this.inModule;
    } else {
      return reservedWords[6](word);
    }
  };

  _proto.hasPlugin = function hasPlugin(name) {
    return !!this.plugins[name];
  };

  return BaseParser;
}();

/* eslint max-len: 0 */

/**
 * Based on the comment attachment algorithm used in espree and estraverse.
 *
 * 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.
 *
 * 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 <COPYRIGHT HOLDER> 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 last(stack) {
  return stack[stack.length - 1];
}

var CommentsParser =
/*#__PURE__*/
function (_BaseParser) {
  _inheritsLoose(CommentsParser, _BaseParser);

  function CommentsParser() {
    return _BaseParser.apply(this, arguments) || this;
  }

  var _proto = CommentsParser.prototype;

  _proto.addComment = function addComment(comment) {
    if (this.filename) comment.loc.filename = this.filename;
    this.state.trailingComments.push(comment);
    this.state.leadingComments.push(comment);
  };

  _proto.processComment = function processComment(node) {
    if (node.type === "Program" && node.body.length > 0) return;
    var stack = this.state.commentStack;
    var firstChild, lastChild, trailingComments, i, j;

    if (this.state.trailingComments.length > 0) {
      // If the first comment in trailingComments comes after the
      // current node, then we're good - all comments in the array will
      // come after the node and so it's safe to add them as official
      // trailingComments.
      if (this.state.trailingComments[0].start >= node.end) {
        trailingComments = this.state.trailingComments;
        this.state.trailingComments = [];
      } else {
        // Otherwise, if the first comment doesn't come after the
        // current node, that means we have a mix of leading and trailing
        // comments in the array and that leadingComments contains the
        // same items as trailingComments. Reset trailingComments to
        // zero items and we'll handle this by evaluating leadingComments
        // later.
        this.state.trailingComments.length = 0;
      }
    } else if (stack.length > 0) {
      var lastInStack = last(stack);

      if (lastInStack.trailingComments && lastInStack.trailingComments[0].start >= node.end) {
        trailingComments = lastInStack.trailingComments;
        delete lastInStack.trailingComments;
      }
    } // Eating the stack.


    if (stack.length > 0 && last(stack).start >= node.start) {
      firstChild = stack.pop();
    }

    while (stack.length > 0 && last(stack).start >= node.start) {
      lastChild = stack.pop();
    }

    if (!lastChild && firstChild) lastChild = firstChild; // Attach comments that follow a trailing comma on the last
    // property in an object literal or a trailing comma in function arguments
    // as trailing comments

    if (firstChild && this.state.leadingComments.length > 0) {
      var lastComment = last(this.state.leadingComments);

      if (firstChild.type === "ObjectProperty") {
        if (lastComment.start >= node.start) {
          if (this.state.commentPreviousNode) {
            for (j = 0; j < this.state.leadingComments.length; j++) {
              if (this.state.leadingComments[j].end < this.state.commentPreviousNode.end) {
                this.state.leadingComments.splice(j, 1);
                j--;
              }
            }

            if (this.state.leadingComments.length > 0) {
              firstChild.trailingComments = this.state.leadingComments;
              this.state.leadingComments = [];
            }
          }
        }
      } else if (node.type === "CallExpression" && node.arguments && node.arguments.length) {
        var lastArg = last(node.arguments);

        if (lastArg && lastComment.start >= lastArg.start && lastComment.end <= node.end) {
          if (this.state.commentPreviousNode) {
            if (this.state.leadingComments.length > 0) {
              lastArg.trailingComments = this.state.leadingComments;
              this.state.leadingComments = [];
            }
          }
        }
      }
    }

    if (lastChild) {
      if (lastChild.leadingComments) {
        if (lastChild !== node && lastChild.leadingComments.length > 0 && last(lastChild.leadingComments).end <= node.start) {
          node.leadingComments = lastChild.leadingComments;
          delete lastChild.leadingComments;
        } else {
          // A leading comment for an anonymous class had been stolen by its first ClassMethod,
          // so this takes back the leading comment.
          // See also: https://github.com/eslint/espree/issues/158
          for (i = lastChild.leadingComments.length - 2; i >= 0; --i) {
            if (lastChild.leadingComments[i].end <= node.start) {
              node.leadingComments = lastChild.leadingComments.splice(0, i + 1);
              break;
            }
          }
        }
      }
    } else if (this.state.leadingComments.length > 0) {
      if (last(this.state.leadingComments).end <= node.start) {
        if (this.state.commentPreviousNode) {
          for (j = 0; j < this.state.leadingComments.length; j++) {
            if (this.state.leadingComments[j].end < this.state.commentPreviousNode.end) {
              this.state.leadingComments.splice(j, 1);
              j--;
            }
          }
        }

        if (this.state.leadingComments.length > 0) {
          node.leadingComments = this.state.leadingComments;
          this.state.leadingComments = [];
        }
      } else {
        // https://github.com/eslint/espree/issues/2
        //
        // In special cases, such as return (without a value) and
        // debugger, all comments will end up as leadingComments and
        // will otherwise be eliminated. This step runs when the
        // commentStack is empty and there are comments left
        // in leadingComments.
        //
        // This loop figures out the stopping point between the actual
        // leading and trailing comments by finding the location of the
        // first comment that comes after the given node.
        for (i = 0; i < this.state.leadingComments.length; i++) {
          if (this.state.leadingComments[i].end > node.start) {
            break;
          }
        } // Split the array based on the location of the first comment
        // that comes after the node. Keep in mind that this could
        // result in an empty array, and if so, the array must be
        // deleted.


        var leadingComments = this.state.leadingComments.slice(0, i);

        if (leadingComments.length) {
          node.leadingComments = leadingComments;
        } // Similarly, trailing comments are attached later. The variable
        // must be reset to null if there are no trailing comments.


        trailingComments = this.state.leadingComments.slice(i);

        if (trailingComments.length === 0) {
          trailingComments = null;
        }
      }
    }

    this.state.commentPreviousNode = node;

    if (trailingComments) {
      if (trailingComments.length && trailingComments[0].start >= node.start && last(trailingComments).end <= node.end) {
        node.innerComments = trailingComments;
      } else {
        node.trailingComments = trailingComments;
      }
    }

    stack.push(node);
  };

  return CommentsParser;
}(BaseParser);

// takes an offset integer (into the current `input`) to indicate
// the location of the error, attaches the position to the end
// of the error message, and then raises a `SyntaxError` with that
// message.

var LocationParser =
/*#__PURE__*/
function (_CommentsParser) {
  _inheritsLoose(LocationParser, _CommentsParser);

  function LocationParser() {
    return _CommentsParser.apply(this, arguments) || this;
  }

  var _proto = LocationParser.prototype;

  _proto.raise = function raise(pos, message, missingPluginNames) {
    var loc = getLineInfo(this.input, pos);
    message += ` (${loc.line}:${loc.column})`; // $FlowIgnore

    var err = new SyntaxError(message);
    err.pos = pos;
    err.loc = loc;

    if (missingPluginNames) {
      err.missingPlugin = missingPluginNames;
    }

    throw err;
  };

  return LocationParser;
}(CommentsParser);

var State =
/*#__PURE__*/
function () {
  function State() {}

  var _proto = State.prototype;

  _proto.init = function init(options, input) {
    this.strict = options.strictMode === false ? false : options.sourceType === "module";
    this.input = input;
    this.potentialArrowAt = -1;
    this.noArrowAt = [];
    this.noArrowParamsConversionAt = []; // eslint-disable-next-line max-len

    this.inMethod = this.inFunction = this.inParameters = this.maybeInArrowParameters = this.inGenerator = this.inAsync = this.inPropertyName = this.inType = this.inClassProperty = this.noAnonFunctionType = false;
    this.classLevel = 0;
    this.labels = [];
    this.decoratorStack = [[]];
    this.yieldInPossibleArrowParameters = null;
    this.tokens = [];
    this.comments = [];
    this.trailingComments = [];
    this.leadingComments = [];
    this.commentStack = []; // $FlowIgnore

    this.commentPreviousNode = null;
    this.pos = this.lineStart = 0;
    this.curLine = options.startLine;
    this.type = types.eof;
    this.value = null;
    this.start = this.end = this.pos;
    this.startLoc = this.endLoc = this.curPosition(); // $FlowIgnore

    this.lastTokEndLoc = this.lastTokStartLoc = null;
    this.lastTokStart = this.lastTokEnd = this.pos;
    this.context = [types$1.braceStatement];
    this.exprAllowed = true;
    this.containsEsc = this.containsOctal = false;
    this.octalPosition = null;
    this.invalidTemplateEscapePosition = null;
    this.exportedIdentifiers = [];
  }; // TODO


  _proto.curPosition = function curPosition() {
    return new Position(this.curLine, this.pos - this.lineStart);
  };

  _proto.clone = function clone(skipArrays) {
    var _this = this;

    var state = new State();
    Object.keys(this).forEach(function (key) {
      // $FlowIgnore
      var val = _this[key];

      if ((!skipArrays || key === "context") && Array.isArray(val)) {
        val = val.slice();
      } // $FlowIgnore


      state[key] = val;
    });
    return state;
  };

  return State;
}();

var _isDigit = function isDigit(code) {
  return code >= 48 && code <= 57;
};

/* eslint max-len: 0 */
// an immediate sibling of NumericLiteralSeparator _

var forbiddenNumericSeparatorSiblings = {
  decBinOct: [46, 66, 69, 79, 95, // multiple separators are not allowed
  98, 101, 111],
  hex: [46, 88, 95, // multiple separators are not allowed
  120]
};
var allowedNumericSeparatorSiblings = {};
allowedNumericSeparatorSiblings.bin = [// 0 - 1
48, 49];
allowedNumericSeparatorSiblings.oct = allowedNumericSeparatorSiblings.bin.concat([50, 51, 52, 53, 54, 55]);
allowedNumericSeparatorSiblings.dec = allowedNumericSeparatorSiblings.oct.concat([56, 57]);
allowedNumericSeparatorSiblings.hex = allowedNumericSeparatorSiblings.dec.concat([65, 66, 67, 68, 69, 70, 97, 98, 99, 100, 101, 102]); // Object type used to represent tokens. Note that normally, tokens
// simply exist as properties on the parser object. This is only
// used for the onToken callback and the external tokenizer.

var Token = function Token(state) {
  this.type = state.type;
  this.value = state.value;
  this.start = state.start;
  this.end = state.end;
  this.loc = new SourceLocation(state.startLoc, state.endLoc);
}; // ## Tokenizer

function codePointToString(code) {
  // UTF-16 Decoding
  if (code <= 0xffff) {
    return String.fromCharCode(code);
  } else {
    return String.fromCharCode((code - 0x10000 >> 10) + 0xd800, (code - 0x10000 & 1023) + 0xdc00);
  }
}

var Tokenizer =
/*#__PURE__*/
function (_LocationParser) {
  _inheritsLoose(Tokenizer, _LocationParser);

  // Forward-declarations
  // parser/util.js
  function Tokenizer(options, input) {
    var _this;

    _this = _LocationParser.call(this) || this;
    _this.state = new State();

    _this.state.init(options, input);

    _this.isLookahead = false;
    return _this;
  } // Move to the next token


  var _proto = Tokenizer.prototype;

  _proto.next = function next() {
    if (this.options.tokens && !this.isLookahead) {
      this.state.tokens.push(new Token(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();
  }; // TODO


  _proto.eat = function eat(type) {
    if (this.match(type)) {
      this.next();
      return true;
    } else {
      return false;
    }
  }; // TODO


  _proto.match = function match(type) {
    return this.state.type === type;
  }; // TODO


  _proto.isKeyword = function isKeyword$$1(word) {
    return isKeyword(word);
  }; // TODO


  _proto.lookahead = function lookahead() {
    var old = this.state;
    this.state = old.clone(true);
    this.isLookahead = true;
    this.next();
    this.isLookahead = false;
    var curr = this.state;
    this.state = old;
    return curr;
  }; // Toggle strict mode. Re-reads the next number or string to please
  // pedantic tests (`"use strict"; 010;` should fail).


  _proto.setStrict = function setStrict(strict) {
    this.state.strict = strict;
    if (!this.match(types.num) && !this.match(types.string)) return;
    this.state.pos = this.state.start;

    while (this.state.pos < this.state.lineStart) {
      this.state.lineStart = this.input.lastIndexOf("\n", this.state.lineStart - 2) + 1;
      --this.state.curLine;
    }

    this.nextToken();
  };

  _proto.curContext = function curContext() {
    return this.state.context[this.state.context.length - 1];
  }; // Read a single token, updating the parser object's token-related
  // properties.


  _proto.nextToken = function nextToken() {
    var curContext = this.curContext();
    if (!curContext || !curContext.preserveSpace) this.skipSpace();
    this.state.containsOctal = false;
    this.state.octalPosition = null;
    this.state.start = this.state.pos;
    this.state.startLoc = this.state.curPosition();

    if (this.state.pos >= this.input.length) {
      this.finishToken(types.eof);
      return;
    }

    if (curContext.override) {
      curContext.override(this);
    } else {
      this.readToken(this.fullCharCodeAtPos());
    }
  };

  _proto.readToken = function readToken(code) {
    // Identifier or keyword. '\uXXXX' sequences are allowed in
    // identifiers, so '\' also dispatches to that.
    if (isIdentifierStart(code) || code === 92) {
      this.readWord();
    } else {
      this.getTokenFromCode(code);
    }
  };

  _proto.fullCharCodeAtPos = function fullCharCodeAtPos() {
    var code = this.input.charCodeAt(this.state.pos);
    if (code <= 0xd7ff || code >= 0xe000) return code;
    var next = this.input.charCodeAt(this.state.pos + 1);
    return (code << 10) + next - 0x35fdc00;
  };

  _proto.pushComment = function pushComment(block, text, start, end, startLoc, endLoc) {
    var comment = {
      type: block ? "CommentBlock" : "CommentLine",
      value: text,
      start: start,
      end: end,
      loc: new SourceLocation(startLoc, endLoc)
    };

    if (!this.isLookahead) {
      if (this.options.tokens) this.state.tokens.push(comment);
      this.state.comments.push(comment);
      this.addComment(comment);
    }
  };

  _proto.skipBlockComment = function skipBlockComment() {
    var startLoc = this.state.curPosition();
    var start = this.state.pos;
    var end = this.input.indexOf("*/", this.state.pos += 2);
    if (end === -1) this.raise(this.state.pos - 2, "Unterminated comment");
    this.state.pos = end + 2;
    lineBreakG.lastIndex = start;
    var match;

    while ((match = lineBreakG.exec(this.input)) && match.index < this.state.pos) {
      ++this.state.curLine;
      this.state.lineStart = match.index + match[0].length;
    }

    this.pushComment(true, this.input.slice(start + 2, end), start, this.state.pos, startLoc, this.state.curPosition());
  };

  _proto.skipLineComment = function skipLineComment(startSkip) {
    var start = this.state.pos;
    var startLoc = this.state.curPosition();
    var ch = this.input.charCodeAt(this.state.pos += startSkip);

    if (this.state.pos < this.input.length) {
      while (ch !== 10 && ch !== 13 && ch !== 8232 && ch !== 8233 && ++this.state.pos < this.input.length) {
        ch = this.input.charCodeAt(this.state.pos);
      }
    }

    this.pushComment(false, this.input.slice(start + startSkip, this.state.pos), start, this.state.pos, startLoc, this.state.curPosition());
  }; // Called at the start of the parse and after every token. Skips
  // whitespace and comments, and.


  _proto.skipSpace = function skipSpace() {
    loop: while (this.state.pos < this.input.length) {
      var ch = this.input.charCodeAt(this.state.pos);

      switch (ch) {
        case 32:
        case 160:
          ++this.state.pos;
          break;

        case 13:
          if (this.input.charCodeAt(this.state.pos + 1) === 10) {
            ++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 loop;
          }

          break;

        default:
          if (ch > 8 && ch < 14 || ch >= 5760 && nonASCIIwhitespace.test(String.fromCharCode(ch))) {
            ++this.state.pos;
          } else {
            break loop;
          }

      }
    }
  }; // Called at the end of every token. Sets `end`, `val`, and
  // maintains `context` and `exprAllowed`, and skips the space after
  // the token, so that the next one's `start` will point at the
  // right position.


  _proto.finishToken = function finishToken(type, val) {
    this.state.end = this.state.pos;
    this.state.endLoc = this.state.curPosition();
    var prevType = this.state.type;
    this.state.type = type;
    this.state.value = val;
    this.updateContext(prevType);
  }; // ### Token reading
  // This is the function that is called to fetch the next token. It
  // is somewhat obscure, because it works in character codes rather
  // than characters, and because operator parsing has been inlined
  // into it.
  //
  // All in the name of speed.
  //


  _proto.readToken_dot = function readToken_dot() {
    var next = this.input.charCodeAt(this.state.pos + 1);

    if (next >= 48 && next <= 57) {
      this.readNumber(true);
      return;
    }

    var next2 = this.input.charCodeAt(this.state.pos + 2);

    if (next === 46 && next2 === 46) {
      this.state.pos += 3;
      this.finishToken(types.ellipsis);
    } else {
      ++this.state.pos;
      this.finishToken(types.dot);
    }
  };

  _proto.readToken_slash = function readToken_slash() {
    // '/'
    if (this.state.exprAllowed) {
      ++this.state.pos;
      this.readRegexp();
      return;
    }

    var next = this.input.charCodeAt(this.state.pos + 1);

    if (next === 61) {
      this.finishOp(types.assign, 2);
    } else {
      this.finishOp(types.slash, 1);
    }
  };

  _proto.readToken_mult_modulo = function readToken_mult_modulo(code) {
    // '%*'
    var type = code === 42 ? types.star : types.modulo;
    var width = 1;
    var next = this.input.charCodeAt(this.state.pos + 1);
    var exprAllowed = this.state.exprAllowed; // Exponentiation operator **

    if (code === 42 && next === 42) {
      width++;
      next = this.input.charCodeAt(this.state.pos + 2);
      type = types.exponent;
    }

    if (next === 61 && !exprAllowed) {
      width++;
      type = types.assign;
    }

    this.finishOp(type, width);
  };

  _proto.readToken_pipe_amp = function readToken_pipe_amp(code) {
    // '|&'
    var next = this.input.charCodeAt(this.state.pos + 1);

    if (next === code) {
      this.finishOp(code === 124 ? types.logicalOR : types.logicalAND, 2);
      return;
    }

    if (code === 124) {
      // '|>'
      if (next === 62) {
        this.finishOp(types.pipeline, 2);
        return;
      } else if (next === 125 && this.hasPlugin("flow")) {
        // '|}'
        this.finishOp(types.braceBarR, 2);
        return;
      }
    }

    if (next === 61) {
      this.finishOp(types.assign, 2);
      return;
    }

    this.finishOp(code === 124 ? types.bitwiseOR : types.bitwiseAND, 1);
  };

  _proto.readToken_caret = function readToken_caret() {
    // '^'
    var next = this.input.charCodeAt(this.state.pos + 1);

    if (next === 61) {
      this.finishOp(types.assign, 2);
    } else {
      this.finishOp(types.bitwiseXOR, 1);
    }
  };

  _proto.readToken_plus_min = function readToken_plus_min(code) {
    // '+-'
    var next = this.input.charCodeAt(this.state.pos + 1);

    if (next === code) {
      if (next === 45 && !this.inModule && this.input.charCodeAt(this.state.pos + 2) === 62 && lineBreak.test(this.input.slice(this.state.lastTokEnd, this.state.pos))) {
        // A `-->` line comment
        this.skipLineComment(3);
        this.skipSpace();
        this.nextToken();
        return;
      }

      this.finishOp(types.incDec, 2);
      return;
    }

    if (next === 61) {
      this.finishOp(types.assign, 2);
    } else {
      this.finishOp(types.plusMin, 1);
    }
  };

  _proto.readToken_lt_gt = function readToken_lt_gt(code) {
    // '<>'
    var next = this.input.charCodeAt(this.state.pos + 1);
    var size = 1;

    if (next === code) {
      size = code === 62 && this.input.charCodeAt(this.state.pos + 2) === 62 ? 3 : 2;

      if (this.input.charCodeAt(this.state.pos + size) === 61) {
        this.finishOp(types.assign, size + 1);
        return;
      }

      this.finishOp(types.bitShift, size);
      return;
    }

    if (next === 33 && code === 60 && !this.inModule && this.input.charCodeAt(this.state.pos + 2) === 45 && this.input.charCodeAt(this.state.pos + 3) === 45) {
      // `<!--`, an XML-style comment that should be interpreted as a line comment
      this.skipLineComment(4);
      this.skipSpace();
      this.nextToken();
      return;
    }

    if (next === 61) {
      // <= | >=
      size = 2;
    }

    this.finishOp(types.relational, size);
  };

  _proto.readToken_eq_excl = function readToken_eq_excl(code) {
    // '=!'
    var next = this.input.charCodeAt(this.state.pos + 1);

    if (next === 61) {
      this.finishOp(types.equality, this.input.charCodeAt(this.state.pos + 2) === 61 ? 3 : 2);
      return;
    }

    if (code === 61 && next === 62) {
      // '=>'
      this.state.pos += 2;
      this.finishToken(types.arrow);
      return;
    }

    this.finishOp(code === 61 ? types.eq : types.bang, 1);
  };

  _proto.readToken_question = function readToken_question() {
    // '?'
    var next = this.input.charCodeAt(this.state.pos + 1);
    var next2 = this.input.charCodeAt(this.state.pos + 2);

    if (next === 63) {
      // '??'
      this.finishOp(types.nullishCoalescing, 2);
    } else if (next === 46 && !(next2 >= 48 && next2 <= 57)) {
      // '.' not followed by a number
      this.state.pos += 2;
      this.finishToken(types.questionDot);
    } else {
      ++this.state.pos;
      this.finishToken(types.question);
    }
  };

  _proto.getTokenFromCode = function getTokenFromCode(code) {
    switch (code) {
      case 35:
        if ((this.hasPlugin("classPrivateProperties") || this.hasPlugin("classPrivateMethods")) && this.state.classLevel > 0) {
          ++this.state.pos;
          this.finishToken(types.hash);
          return;
        } else {
          this.raise(this.state.pos, `Unexpected character '${codePointToString(code)}'`);
        }

      // The interpretation of a dot depends on whether it is followed
      // by a digit or another two dots.

      case 46:
        this.readToken_dot();
        return;
      // Punctuation tokens.

      case 40:
        ++this.state.pos;
        this.finishToken(types.parenL);
        return;

      case 41:
        ++this.state.pos;
        this.finishToken(types.parenR);
        return;

      case 59:
        ++this.state.pos;
        this.finishToken(types.semi);
        return;

      case 44:
        ++this.state.pos;
        this.finishToken(types.comma);
        return;

      case 91:
        ++this.state.pos;
        this.finishToken(types.bracketL);
        return;

      case 93:
        ++this.state.pos;
        this.finishToken(types.bracketR);
        return;

      case 123:
        if (this.hasPlugin("flow") && this.input.charCodeAt(this.state.pos + 1) === 124) {
          this.finishOp(types.braceBarL, 2);
        } else {
          ++this.state.pos;
          this.finishToken(types.braceL);
        }

        return;

      case 125:
        ++this.state.pos;
        this.finishToken(types.braceR);
        return;

      case 58:
        if (this.hasPlugin("functionBind") && this.input.charCodeAt(this.state.pos + 1) === 58) {
          this.finishOp(types.doubleColon, 2);
        } else {
          ++this.state.pos;
          this.finishToken(types.colon);
        }

        return;

      case 63:
        this.readToken_question();
        return;

      case 64:
        ++this.state.pos;
        this.finishToken(types.at);
        return;

      case 96:
        ++this.state.pos;
        this.finishToken(types.backQuote);
        return;

      case 48:
        {
          var next = this.input.charCodeAt(this.state.pos + 1); // '0x', '0X' - hex number

          if (next === 120 || next === 88) {
            this.readRadixNumber(16);
            return;
          } // '0o', '0O' - octal number


          if (next === 111 || next === 79) {
            this.readRadixNumber(8);
            return;
          } // '0b', '0B' - binary number


          if (next === 98 || next === 66) {
            this.readRadixNumber(2);
            return;
          }
        }
      // Anything else beginning with a digit is an integer, octal
      // number, or float.

      case 49:
      case 50:
      case 51:
      case 52:
      case 53:
      case 54:
      case 55:
      case 56:
      case 57:
        this.readNumber(false);
        return;
      // Quotes produce strings.

      case 34:
      case 39:
        this.readString(code);
        return;
      // Operators are parsed inline in tiny state machines. '=' (charCodes.equalsTo) is
      // often referred to. `finishOp` simply skips the amount of
      // characters it is given as second argument, and returns a token
      // of the type given by its first argument.

      case 47:
        this.readToken_slash();
        return;

      case 37:
      case 42:
        this.readToken_mult_modulo(code);
        return;

      case 124:
      case 38:
        this.readToken_pipe_amp(code);
        return;

      case 94:
        this.readToken_caret();
        return;

      case 43:
      case 45:
        this.readToken_plus_min(code);
        return;

      case 60:
      case 62:
        this.readToken_lt_gt(code);
        return;

      case 61:
      case 33:
        this.readToken_eq_excl(code);
        return;

      case 126:
        this.finishOp(types.tilde, 1);
        return;
    }

    this.raise(this.state.pos, `Unexpected character '${codePointToString(code)}'`);
  };

  _proto.finishOp = function finishOp(type, size) {
    var str = this.input.slice(this.state.pos, this.state.pos + size);
    this.state.pos += size;
    this.finishToken(type, str);
  };

  _proto.readRegexp = function readRegexp() {
    var start = this.state.pos;
    var escaped, inClass;

    for (;;) {
      if (this.state.pos >= this.input.length) {
        this.raise(start, "Unterminated regular expression");
      }

      var ch = this.input.charAt(this.state.pos);

      if (lineBreak.test(ch)) {
        this.raise(start, "Unterminated regular expression");
      }

      if (escaped) {
        escaped = false;
      } else {
        if (ch === "[") {
          inClass = true;
        } else if (ch === "]" && inClass) {
          inClass = false;
        } else if (ch === "/" && !inClass) {
          break;
        }

        escaped = ch === "\\";
      }

      ++this.state.pos;
    }

    var content = this.input.slice(start, this.state.pos);
    ++this.state.pos;
    var validFlags = /^[gmsiyu]$/;
    var mods = "";

    while (this.state.pos < this.input.length) {
      var char = this.input[this.state.pos];
      var charCode = this.fullCharCodeAtPos();

      if (validFlags.test(char)) {
        ++this.state.pos;
        mods += char;
      } else if (isIdentifierChar(charCode) || charCode === 92) {
        this.raise(this.state.pos, "Invalid regular expression flag");
      } else {
        break;
      }
    }

    this.finishToken(types.regexp, {
      pattern: content,
      flags: mods
    });
  }; // Read an integer in the given radix. Return null if zero digits
  // were read, the integer value otherwise. When `len` is given, this
  // will return `null` unless the integer has exactly `len` digits.


  _proto.readInt = function readInt(radix, len) {
    var start = this.state.pos;
    var forbiddenSiblings = radix === 16 ? forbiddenNumericSeparatorSiblings.hex : forbiddenNumericSeparatorSiblings.decBinOct;
    var allowedSiblings = radix === 16 ? allowedNumericSeparatorSiblings.hex : radix === 10 ? allowedNumericSeparatorSiblings.dec : radix === 8 ? allowedNumericSeparatorSiblings.oct : allowedNumericSeparatorSiblings.bin;
    var total = 0;

    for (var i = 0, e = len == null ? Infinity : len; i < e; ++i) {
      var code = this.input.charCodeAt(this.state.pos);
      var val = void 0;

      if (this.hasPlugin("numericSeparator")) {
        var prev = this.input.charCodeAt(this.state.pos - 1);
        var next = this.input.charCodeAt(this.state.pos + 1);

        if (code === 95) {
          if (allowedSiblings.indexOf(next) === -1) {
            this.raise(this.state.pos, "Invalid or unexpected token");
          }

          if (forbiddenSiblings.indexOf(prev) > -1 || forbiddenSiblings.indexOf(next) > -1 || Number.isNaN(next)) {
            this.raise(this.state.pos, "Invalid or unexpected token");
          } // Ignore this _ character


          ++this.state.pos;
          continue;
        }
      }

      if (code >= 97) {
        val = code - 97 + 10;
      } else if (code >= 65) {
        val = code - 65 + 10;
      } else if (_isDigit(code)) {
        val = code - 48; // 0-9
      } else {
        val = Infinity;
      }

      if (val >= radix) break;
      ++this.state.pos;
      total = total * radix + val;
    }

    if (this.state.pos === start || len != null && this.state.pos - start !== len) {
      return null;
    }

    return total;
  };

  _proto.readRadixNumber = function readRadixNumber(radix) {
    var start = this.state.pos;
    var isBigInt = false;
    this.state.pos += 2; // 0x

    var val = this.readInt(radix);

    if (val == null) {
      this.raise(this.state.start + 2, "Expected number in radix " + radix);
    }

    if (this.hasPlugin("bigInt")) {
      if (this.input.charCodeAt(this.state.pos) === 110) {
        ++this.state.pos;
        isBigInt = true;
      }
    }

    if (isIdentifierStart(this.fullCharCodeAtPos())) {
      this.raise(this.state.pos, "Identifier directly after number");
    }

    if (isBigInt) {
      var str = this.input.slice(start, this.state.pos).replace(/[_n]/g, "");
      this.finishToken(types.bigint, str);
      return;
    }

    this.finishToken(types.num, val);
  }; // Read an integer, octal integer, or floating-point number.


  _proto.readNumber = function readNumber(startsWithDot) {
    var start = this.state.pos;
    var octal = this.input.charCodeAt(start) === 48;
    var isFloat = false;
    var isBigInt = false;

    if (!startsWithDot && this.readInt(10) === null) {
      this.raise(start, "Invalid number");
    }

    if (octal && this.state.pos == start + 1) octal = false; // number === 0

    var next = this.input.charCodeAt(this.state.pos);

    if (next === 46 && !octal) {
      ++this.state.pos;
      this.readInt(10);
      isFloat = true;
      next = this.input.charCodeAt(this.state.pos);
    }

    if ((next === 69 || next === 101) && !octal) {
      next = this.input.charCodeAt(++this.state.pos);

      if (next === 43 || next === 45) {
        ++this.state.pos;
      }

      if (this.readInt(10) === null) this.raise(start, "Invalid number");
      isFloat = true;
      next = this.input.charCodeAt(this.state.pos);
    }

    if (this.hasPlugin("bigInt")) {
      if (next === 110) {
        // disallow floats and legacy octal syntax, new style octal ("0o") is handled in this.readRadixNumber
        if (isFloat || octal) this.raise(start, "Invalid BigIntLiteral");
        ++this.state.pos;
        isBigInt = true;
      }
    }

    if (isIdentifierStart(this.fullCharCodeAtPos())) {
      this.raise(this.state.pos, "Identifier directly after number");
    } // remove "_" for numeric literal separator, and "n" for BigInts


    var str = this.input.slice(start, this.state.pos).replace(/[_n]/g, "");

    if (isBigInt) {
      this.finishToken(types.bigint, str);
      return;
    }

    var val;

    if (isFloat) {
      val = parseFloat(str);
    } else if (!octal || str.length === 1) {
      val = parseInt(str, 10);
    } else if (this.state.strict) {
      this.raise(start, "Invalid number");
    } else if (/[89]/.test(str)) {
      val = parseInt(str, 10);
    } else {
      val = parseInt(str, 8);
    }

    this.finishToken(types.num, val);
  }; // Read a string value, interpreting backslash-escapes.


  _proto.readCodePoint = function readCodePoint(throwOnInvalid) {
    var ch = this.input.charCodeAt(this.state.pos);
    var code;

    if (ch === 123) {
      var codePos = ++this.state.pos;
      code = this.readHexChar(this.input.indexOf("}", this.state.pos) - this.state.pos, throwOnInvalid);
      ++this.state.pos;

      if (code === null) {
        // $FlowFixMe (is this always non-null?)
        --this.state.invalidTemplateEscapePosition; // to point to the '\'' instead of the 'u'
      } else if (code > 0x10ffff) {
        if (throwOnInvalid) {
          this.raise(codePos, "Code point out of bounds");
        } else {
          this.state.invalidTemplateEscapePosition = codePos - 2;
          return null;
        }
      }
    } else {
      code = this.readHexChar(4, throwOnInvalid);
    }

    return code;
  };

  _proto.readString = function readString(quote) {
    var out = "",
        chunkStart = ++this.state.pos;

    for (;;) {
      if (this.state.pos >= this.input.length) {
        this.raise(this.state.start, "Unterminated string constant");
      }

      var ch = this.input.charCodeAt(this.state.pos);
      if (ch === quote) break;

      if (ch === 92) {
        out += this.input.slice(chunkStart, this.state.pos); // $FlowFixMe

        out += this.readEscapedChar(false);
        chunkStart = this.state.pos;
      } else {
        if (isNewLine(ch)) {
          this.raise(this.state.start, "Unterminated string constant");
        }

        ++this.state.pos;
      }
    }

    out += this.input.slice(chunkStart, this.state.pos++);
    this.finishToken(types.string, out);
  }; // Reads template string tokens.


  _proto.readTmplToken = function readTmplToken() {
    var out = "",
        chunkStart = this.state.pos,
        containsInvalid = false;

    for (;;) {
      if (this.state.pos >= this.input.length) {
        this.raise(this.state.start, "Unterminated template");
      }

      var ch = this.input.charCodeAt(this.state.pos);

      if (ch === 96 || ch === 36 && this.input.charCodeAt(this.state.pos + 1) === 123) {
        if (this.state.pos === this.state.start && this.match(types.template)) {
          if (ch === 36) {
            this.state.pos += 2;
            this.finishToken(types.dollarBraceL);
            return;
          } else {
            ++this.state.pos;
            this.finishToken(types.backQuote);
            return;
          }
        }

        out += this.input.slice(chunkStart, this.state.pos);
        this.finishToken(types.template, containsInvalid ? null : out);
        return;
      }

      if (ch === 92) {
        out += this.input.slice(chunkStart, this.state.pos);
        var escaped = this.readEscapedChar(true);

        if (escaped === null) {
          containsInvalid = true;
        } else {
          out += escaped;
        }

        chunkStart = this.state.pos;
      } else if (isNewLine(ch)) {
        out += this.input.slice(chunkStart, this.state.pos);
        ++this.state.pos;

        switch (ch) {
          case 13:
            if (this.input.charCodeAt(this.state.pos) === 10) {
              ++this.state.pos;
            }

          case 10:
            out += "\n";
            break;

          default:
            out += String.fromCharCode(ch);
            break;
        }

        ++this.state.curLine;
        this.state.lineStart = this.state.pos;
        chunkStart = this.state.pos;
      } else {
        ++this.state.pos;
      }
    }
  }; // Used to read escaped characters


  _proto.readEscapedChar = function readEscapedChar(inTemplate) {
    var throwOnInvalid = !inTemplate;
    var ch = this.input.charCodeAt(++this.state.pos);
    ++this.state.pos;

    switch (ch) {
      case 110:
        return "\n";

      case 114:
        return "\r";

      case 120:
        {
          var code = this.readHexChar(2, throwOnInvalid);
          return code === null ? null : String.fromCharCode(code);
        }

      case 117:
        {
          var _code = this.readCodePoint(throwOnInvalid);

          return _code === null ? null : codePointToString(_code);
        }

      case 116:
        return "\t";

      case 98:
        return "\b";

      case 118:
        return "\u000b";

      case 102:
        return "\f";

      case 13:
        if (this.input.charCodeAt(this.state.pos) === 10) {
          ++this.state.pos;
        }

      case 10:
        this.state.lineStart = this.state.pos;
        ++this.state.curLine;
        return "";

      default:
        if (ch >= 48 && ch <= 55) {
          var codePos = this.state.pos - 1; // $FlowFixMe

          var octalStr = this.input.substr(this.state.pos - 1, 3).match(/^[0-7]+/)[0];
          var octal = parseInt(octalStr, 8);

          if (octal > 255) {
            octalStr = octalStr.slice(0, -1);
            octal = parseInt(octalStr, 8);
          }

          if (octal > 0) {
            if (inTemplate) {
              this.state.invalidTemplateEscapePosition = codePos;
              return null;
            } else if (this.state.strict) {
              this.raise(codePos, "Octal literal in strict mode");
            } else if (!this.state.containsOctal) {
              // These properties are only used to throw an error for an octal which occurs
              // in a directive which occurs prior to a "use strict" directive.
              this.state.containsOctal = true;
              this.state.octalPosition = codePos;
            }
          }

          this.state.pos += octalStr.length - 1;
          return String.fromCharCode(octal);
        }

        return String.fromCharCode(ch);
    }
  }; // Used to read character escape sequences ('\x', '\u').


  _proto.readHexChar = function readHexChar(len, throwOnInvalid) {
    var codePos = this.state.pos;
    var n = this.readInt(16, len);

    if (n === null) {
      if (throwOnInvalid) {
        this.raise(codePos, "Bad character escape sequence");
      } else {
        this.state.pos = codePos - 1;
        this.state.invalidTemplateEscapePosition = codePos - 1;
      }
    }

    return n;
  }; // Read an identifier, and return it as a string. Sets `this.state.containsEsc`
  // to whether the word contained a '\u' escape.
  //
  // Incrementally adds only escaped chars, adding other chunks as-is
  // as a micro-optimization.


  _proto.readWord1 = function readWord1() {
    this.state.containsEsc = false;
    var word = "",
        first = true,
        chunkStart = this.state.pos;

    while (this.state.pos < this.input.length) {
      var ch = this.fullCharCodeAtPos();

      if (isIdentifierChar(ch)) {
        this.state.pos += ch <= 0xffff ? 1 : 2;
      } else if (this.state.isIterator && ch === 64) {
        this.state.pos += 1;
      } else if (ch === 92) {
        this.state.containsEsc = true;
        word += this.input.slice(chunkStart, this.state.pos);
        var escStart = this.state.pos;

        if (this.input.charCodeAt(++this.state.pos) !== 117) {
          this.raise(this.state.pos, "Expecting Unicode escape sequence \\uXXXX");
        }

        ++this.state.pos;
        var esc = this.readCodePoint(true); // $FlowFixMe (thinks esc may be null, but throwOnInvalid is true)

        if (!(first ? isIdentifierStart : isIdentifierChar)(esc, true)) {
          this.raise(escStart, "Invalid Unicode escape");
        } // $FlowFixMe


        word += codePointToString(esc);
        chunkStart = this.state.pos;
      } else {
        break;
      }

      first = false;
    }

    return word + this.input.slice(chunkStart, this.state.pos);
  };

  _proto.isIterator = function isIterator(word) {
    return word === "@@iterator" || word === "@@asyncIterator";
  }; // Read an identifier or keyword token. Will check for reserved
  // words when necessary.


  _proto.readWord = function readWord() {
    var word = this.readWord1();
    var type = types.name;

    if (this.isKeyword(word)) {
      if (this.state.containsEsc) {
        this.raise(this.state.pos, `Escape sequence in keyword ${word}`);
      }

      type = keywords[word];
    } // Allow @@iterator and @@asyncIterator as a identifier only inside type


    if (this.state.isIterator && (!this.isIterator(word) || !this.state.inType)) {
      this.raise(this.state.pos, `Invalid identifier ${word}`);
    }

    this.finishToken(type, word);
  };

  _proto.braceIsBlock = function braceIsBlock(prevType) {
    if (prevType === types.colon) {
      var parent = this.curContext();

      if (parent === types$1.braceStatement || parent === types$1.braceExpression) {
        return !parent.isExpr;
      }
    }

    if (prevType === types._return) {
      return lineBreak.test(this.input.slice(this.state.lastTokEnd, this.state.start));
    }

    if (prevType === types._else || prevType === types.semi || prevType === types.eof || prevType === types.parenR) {
      return true;
    }

    if (prevType === types.braceL) {
      return this.curContext() === types$1.braceStatement;
    }

    if (prevType === types.relational) {
      // `class C<T> { ... }`
      return true;
    }

    return !this.state.exprAllowed;
  };

  _proto.updateContext = function updateContext(prevType) {
    var type = this.state.type;
    var update;

    if (type.keyword && (prevType === types.dot || prevType === types.questionDot)) {
      this.state.exprAllowed = false;
    } else if (update = type.updateContext) {
      update.call(this, prevType);
    } else {
      this.state.exprAllowed = type.beforeExpr;
    }
  };

  return Tokenizer;
}(LocationParser);

var UtilParser =
/*#__PURE__*/
function (_Tokenizer) {
  _inheritsLoose(UtilParser, _Tokenizer);

  function UtilParser() {
    return _Tokenizer.apply(this, arguments) || this;
  }

  var _proto = UtilParser.prototype;

  // TODO
  _proto.addExtra = function addExtra(node, key, val) {
    if (!node) return;
    var extra = node.extra = node.extra || {};
    extra[key] = val;
  }; // TODO


  _proto.isRelational = function isRelational(op) {
    return this.match(types.relational) && this.state.value === op;
  }; // TODO


  _proto.expectRelational = function expectRelational(op) {
    if (this.isRelational(op)) {
      this.next();
    } else {
      this.unexpected(null, types.relational);
    }
  }; // eat() for relational operators.


  _proto.eatRelational = function eatRelational(op) {
    if (this.isRelational(op)) {
      this.next();
      return true;
    }

    return false;
  }; // Tests whether parsed token is a contextual keyword.


  _proto.isContextual = function isContextual(name) {
    return this.match(types.name) && this.state.value === name;
  };

  _proto.isLookaheadContextual = function isLookaheadContextual(name) {
    var l = this.lookahead();
    return l.type === types.name && l.value === name;
  }; // Consumes contextual keyword if possible.


  _proto.eatContextual = function eatContextual(name) {
    return this.state.value === name && this.eat(types.name);
  }; // Asserts that following token is given contextual keyword.


  _proto.expectContextual = function expectContextual(name, message) {
    if (!this.eatContextual(name)) this.unexpected(null, message);
  }; // Test whether a semicolon can be inserted at the current position.


  _proto.canInsertSemicolon = function canInsertSemicolon() {
    return this.match(types.eof) || this.match(types.braceR) || this.hasPrecedingLineBreak();
  };

  _proto.hasPrecedingLineBreak = function hasPrecedingLineBreak() {
    return lineBreak.test(this.input.slice(this.state.lastTokEnd, this.state.start));
  }; // TODO


  _proto.isLineTerminator = function isLineTerminator() {
    return this.eat(types.semi) || this.canInsertSemicolon();
  }; // Consume a semicolon, or, failing that, see if we are allowed to
  // pretend that there is a semicolon at this position.


  _proto.semicolon = function semicolon() {
    if (!this.isLineTerminator()) this.unexpected(null, types.semi);
  }; // Expect a token of a given type. If found, consume it, otherwise,
  // raise an unexpected token error at given pos.


  _proto.expect = function expect(type, pos) {
    this.eat(type) || this.unexpected(pos, type);
  }; // Raise an unexpected token error. Can take the expected token type
  // instead of a message string.


  _proto.unexpected = function unexpected(pos, messageOrType) {
    if (messageOrType === void 0) {
      messageOrType = "Unexpected token";
    }

    if (typeof messageOrType !== "string") {
      messageOrType = `Unexpected token, expected "${messageOrType.label}"`;
    }

    throw this.raise(pos != null ? pos : this.state.start, messageOrType);
  };

  _proto.expectPlugin = function expectPlugin(name, pos) {
    if (!this.hasPlugin(name)) {
      throw this.raise(pos != null ? pos : this.state.start, `This experimental syntax requires enabling the parser plugin: '${name}'`, [name]);
    }

    return true;
  };

  _proto.expectOnePlugin = function expectOnePlugin(names, pos) {
    var _this = this;

    if (!names.some(function (n) {
      return _this.hasPlugin(n);
    })) {
      throw this.raise(pos != null ? pos : this.state.start, `This experimental syntax requires enabling one of the following parser plugin(s): '${names.join(", ")}'`, names);
    }
  };

  return UtilParser;
}(Tokenizer);

// Start an AST node, attaching a start offset.
var commentKeys = ["leadingComments", "trailingComments", "innerComments"];

var Node =
/*#__PURE__*/
function () {
  function Node(parser, pos, loc) {
    this.type = "";
    this.start = pos;
    this.end = 0;
    this.loc = new SourceLocation(loc);
    if (parser && parser.options.ranges) this.range = [pos, 0];
    if (parser && parser.filename) this.loc.filename = parser.filename;
  }

  var _proto = Node.prototype;

  _proto.__clone = function __clone() {
    var _this = this;

    // $FlowIgnore
    var node2 = new Node();
    Object.keys(this).forEach(function (key) {
      // Do not clone comments that are already attached to the node
      if (commentKeys.indexOf(key) < 0) {
        // $FlowIgnore
        node2[key] = _this[key];
      }
    });
    return node2;
  };

  return Node;
}();

var NodeUtils =
/*#__PURE__*/
function (_UtilParser) {
  _inheritsLoose(NodeUtils, _UtilParser);

  function NodeUtils() {
    return _UtilParser.apply(this, arguments) || this;
  }

  var _proto2 = NodeUtils.prototype;

  _proto2.startNode = function startNode() {
    // $FlowIgnore
    return new Node(this, this.state.start, this.state.startLoc);
  };

  _proto2.startNodeAt = function startNodeAt(pos, loc) {
    // $FlowIgnore
    return new Node(this, pos, loc);
  };
  /** Start a new node with a previous node's location. */


  _proto2.startNodeAtNode = function startNodeAtNode(type) {
    return this.startNodeAt(type.start, type.loc.start);
  }; // Finish an AST node, adding `type` and `end` properties.


  _proto2.finishNode = function finishNode(node, type) {
    return this.finishNodeAt(node, type, this.state.lastTokEnd, this.state.lastTokEndLoc);
  }; // Finish node at given position


  _proto2.finishNodeAt = function finishNodeAt(node, type, pos, loc) {
    node.type = type;
    node.end = pos;
    node.loc.end = loc;
    if (this.options.ranges) node.range[1] = pos;
    this.processComment(node);
    return node;
  };
  /**
   * Reset the start location of node to the start location of locationNode
   */


  _proto2.resetStartLocationFromNode = function resetStartLocationFromNode(node, locationNode) {
    node.start = locationNode.start;
    node.loc.start = locationNode.loc.start;
    if (this.options.ranges) node.range[0] = locationNode.range[0];
  };

  return NodeUtils;
}(UtilParser);

var LValParser =
/*#__PURE__*/
function (_NodeUtils) {
  _inheritsLoose(LValParser, _NodeUtils);

  function LValParser() {
    return _NodeUtils.apply(this, arguments) || this;
  }

  var _proto = LValParser.prototype;

  // Forward-declaration: defined in expression.js
  // Forward-declaration: defined in statement.js
  // Convert existing expression atom to assignable pattern
  // if possible.
  _proto.toAssignable = function toAssignable(node, isBinding, contextDescription) {
    if (node) {
      switch (node.type) {
        case "Identifier":
        case "ObjectPattern":
        case "ArrayPattern":
        case "AssignmentPattern":
          break;

        case "ObjectExpression":
          node.type = "ObjectPattern";

          for (var index = 0; index < node.properties.length; index++) {
            var prop = node.properties[index];
            var isLast = index === node.properties.length - 1;
            this.toAssignableObjectExpressionProp(prop, isBinding, isLast);
          }

          break;

        case "ObjectProperty":
          this.toAssignable(node.value, isBinding, contextDescription);
          break;

        case "SpreadElement":
          {
            this.checkToRestConversion(node);
            node.type = "RestElement";
            var arg = node.argument;
            this.toAssignable(arg, isBinding, contextDescription);
            break;
          }

        case "ArrayExpression":
          node.type = "ArrayPattern";
          this.toAssignableList(node.elements, isBinding, contextDescription);
          break;

        case "AssignmentExpression":
          if (node.operator === "=") {
            node.type = "AssignmentPattern";
            delete node.operator;
          } else {
            this.raise(node.left.end, "Only '=' operator can be used for specifying default value.");
          }

          break;

        case "MemberExpression":
          if (!isBinding) break;

        default:
          {
            var message = "Invalid left-hand side" + (contextDescription ? " in " + contextDescription :
            /* istanbul ignore next */
            "expression");
            this.raise(node.start, message);
          }
      }
    }

    return node;
  };

  _proto.toAssignableObjectExpressionProp = function toAssignableObjectExpressionProp(prop, isBinding, isLast) {
    if (prop.type === "ObjectMethod") {
      var error = prop.kind === "get" || prop.kind === "set" ? "Object pattern can't contain getter or setter" : "Object pattern can't contain methods";
      this.raise(prop.key.start, error);
    } else if (prop.type === "SpreadElement" && !isLast) {
      this.raise(prop.start, "The rest element has to be the last element when destructuring");
    } else {
      this.toAssignable(prop, isBinding, "object destructuring pattern");
    }
  }; // Convert list of expression atoms to binding list.


  _proto.toAssignableList = function toAssignableList(exprList, isBinding, contextDescription) {
    var end = exprList.length;

    if (end) {
      var last = exprList[end - 1];

      if (last && last.type === "RestElement") {
        --end;
      } else if (last && last.type === "SpreadElement") {
        last.type = "RestElement";
        var arg = last.argument;
        this.toAssignable(arg, isBinding, contextDescription);

        if (["Identifier", "MemberExpression", "ArrayPattern", "ObjectPattern"].indexOf(arg.type) === -1) {
          this.unexpected(arg.start);
        }

        --end;
      }
    }

    for (var i = 0; i < end; i++) {
      var elt = exprList[i];

      if (elt && elt.type === "SpreadElement") {
        this.raise(elt.start, "The rest element has to be the last element when destructuring");
      }

      if (elt) this.toAssignable(elt, isBinding, contextDescription);
    }

    return exprList;
  }; // Convert list of expression atoms to a list of


  _proto.toReferencedList = function toReferencedList(exprList) {
    return exprList;
  }; // Parses spread element.


  _proto.parseSpread = function parseSpread(refShorthandDefaultPos) {
    var node = this.startNode();
    this.next();
    node.argument = this.parseMaybeAssign(false, refShorthandDefaultPos);
    return this.finishNode(node, "SpreadElement");
  };

  _proto.parseRest = function parseRest() {
    var node = this.startNode();
    this.next();
    node.argument = this.parseBindingAtom();
    return this.finishNode(node, "RestElement");
  };

  _proto.shouldAllowYieldIdentifier = function shouldAllowYieldIdentifier() {
    return this.match(types._yield) && !this.state.strict && !this.state.inGenerator;
  };

  _proto.parseBindingIdentifier = function parseBindingIdentifier() {
    return this.parseIdentifier(this.shouldAllowYieldIdentifier());
  }; // Parses lvalue (assignable) atom.


  _proto.parseBindingAtom = function parseBindingAtom() {
    switch (this.state.type) {
      case types._yield:
      case types.name:
        return this.parseBindingIdentifier();

      case types.bracketL:
        {
          var node = this.startNode();
          this.next();
          node.elements = this.parseBindingList(types.bracketR, true);
          return this.finishNode(node, "ArrayPattern");
        }

      case types.braceL:
        return this.parseObj(true);

      default:
        throw this.unexpected();
    }
  };

  _proto.parseBindingList = function parseBindingList(close, allowEmpty, allowModifiers) {
    var elts = [];
    var first = true;

    while (!this.eat(close)) {
      if (first) {
        first = false;
      } else {
        this.expect(types.comma);
      }

      if (allowEmpty && this.match(types.comma)) {
        // $FlowFixMe This method returns `$ReadOnlyArray<?Pattern>` if `allowEmpty` is set.
        elts.push(null);
      } else if (this.eat(close)) {
        break;
      } else if (this.match(types.ellipsis)) {
        elts.push(this.parseAssignableListItemTypes(this.parseRest()));
        this.expect(close);
        break;
      } else {
        var decorators = [];

        if (this.match(types.at) && this.hasPlugin("decorators2")) {
          this.raise(this.state.start, "Stage 2 decorators cannot be used to decorate parameters");
        }

        while (this.match(types.at)) {
          decorators.push(this.parseDecorator());
        }

        elts.push(this.parseAssignableListItem(allowModifiers, decorators));
      }
    }

    return elts;
  };

  _proto.parseAssignableListItem = function parseAssignableListItem(allowModifiers, decorators) {
    var left = this.parseMaybeDefault();
    this.parseAssignableListItemTypes(left);
    var elt = this.parseMaybeDefault(left.start, left.loc.start, left);

    if (decorators.length) {
      left.decorators = decorators;
    }

    return elt;
  };

  _proto.parseAssignableListItemTypes = function parseAssignableListItemTypes(param) {
    return param;
  }; // Parses assignment pattern around given atom if possible.


  _proto.parseMaybeDefault = function parseMaybeDefault(startPos, startLoc, left) {
    startLoc = startLoc || this.state.startLoc;
    startPos = startPos || this.state.start;
    left = left || this.parseBindingAtom();
    if (!this.eat(types.eq)) return left;
    var node = this.startNodeAt(startPos, startLoc);
    node.left = left;
    node.right = this.parseMaybeAssign();
    return this.finishNode(node, "AssignmentPattern");
  }; // Verify that a node is an lval — something that can be assigned
  // to.


  _proto.checkLVal = function checkLVal(expr, isBinding, checkClashes, contextDescription) {
    switch (expr.type) {
      case "Identifier":
        this.checkReservedWord(expr.name, expr.start, false, true);

        if (checkClashes) {
          // we need to prefix this with an underscore for the cases where we have a key of
          // `__proto__`. there's a bug in old V8 where the following wouldn't work:
          //
          //   > var obj = Object.create(null);
          //   undefined
          //   > obj.__proto__
          //   null
          //   > obj.__proto__ = true;
          //   true
          //   > obj.__proto__
          //   null
          var _key = `_${expr.name}`;

          if (checkClashes[_key]) {
            this.raise(expr.start, "Argument name clash in strict mode");
          } else {
            checkClashes[_key] = true;
          }
        }

        break;

      case "MemberExpression":
        if (isBinding) this.raise(expr.start, "Binding member expression");
        break;

      case "ObjectPattern":
        for (var _i2 = 0, _expr$properties2 = expr.properties; _i2 < _expr$properties2.length; _i2++) {
          var prop = _expr$properties2[_i2];
          if (prop.type === "ObjectProperty") prop = prop.value;
          this.checkLVal(prop, isBinding, checkClashes, "object destructuring pattern");
        }

        break;

      case "ArrayPattern":
        for (var _i4 = 0, _expr$elements2 = expr.elements; _i4 < _expr$elements2.length; _i4++) {
          var elem = _expr$elements2[_i4];

          if (elem) {
            this.checkLVal(elem, isBinding, checkClashes, "array destructuring pattern");
          }
        }

        break;

      case "AssignmentPattern":
        this.checkLVal(expr.left, isBinding, checkClashes, "assignment pattern");
        break;

      case "RestElement":
        this.checkLVal(expr.argument, isBinding, checkClashes, "rest element");
        break;

      default:
        {
          var message = (isBinding ?
          /* istanbul ignore next */
          "Binding invalid" : "Invalid") + " left-hand side" + (contextDescription ? " in " + contextDescription :
          /* istanbul ignore next */
          "expression");
          this.raise(expr.start, message);
        }
    }
  };

  _proto.checkToRestConversion = function checkToRestConversion(node) {
    var validArgumentTypes = ["Identifier", "MemberExpression"];

    if (validArgumentTypes.indexOf(node.argument.type) !== -1) {
      return;
    }

    this.raise(node.argument.start, "Invalid rest operator's argument");
  };

  return LValParser;
}(NodeUtils);

/* eslint max-len: 0 */
// A recursive descent parser operates by defining functions for all
// syntactic elements, and recursively calling those, each function
// advancing the input stream and returning an AST node. Precedence
// of constructs (for example, the fact that `!x[1]` means `!(x[1])`
// instead of `(!x)[1]` is handled by the fact that the parser
// function that parses unary prefix operators is called first, and
// in turn calls the function that parses `[]` subscripts — that
// way, it'll receive the node for `x[1]` already parsed, and wraps
// *that* in the unary operator node.
//
// Acorn uses an [operator precedence parser][opp] to handle binary
// operator precedence, because it is much more compact than using
// the technique outlined above, which uses different, nesting
// functions to specify precedence, for all of the ten binary
// precedence levels that JavaScript defines.
//
// [opp]: http://en.wikipedia.org/wiki/Operator-precedence_parser
var ExpressionParser =
/*#__PURE__*/
function (_LValParser) {
  _inheritsLoose(ExpressionParser, _LValParser);

  function ExpressionParser() {
    return _LValParser.apply(this, arguments) || this;
  }

  var _proto = ExpressionParser.prototype;

  // Forward-declaration: defined in statement.js
  // Check if property name clashes with already added.
  // Object/class getters and setters are not allowed to clash —
  // either with each other or with an init property — and in
  // strict mode, init properties are also not allowed to be repeated.
  _proto.checkPropClash = function checkPropClash(prop, propHash) {
    if (prop.computed || prop.kind) return;
    var key = prop.key; // It is either an Identifier or a String/NumericLiteral

    var name = key.type === "Identifier" ? key.name : String(key.value);

    if (name === "__proto__") {
      if (propHash.proto) {
        this.raise(key.start, "Redefinition of __proto__ property");
      }

      propHash.proto = true;
    }
  }; // Convenience method to parse an Expression only


  _proto.getExpression = function getExpression() {
    this.nextToken();
    var expr = this.parseExpression();

    if (!this.match(types.eof)) {
      this.unexpected();
    }

    expr.comments = this.state.comments;
    return expr;
  }; // ### Expression parsing
  // These nest, from the most general expression type at the top to
  // 'atomic', nondivisible expression types at the bottom. Most of
  // the functions will simply let the function (s) below them parse,
  // and, *if* the syntactic construct they handle is present, wrap
  // the AST node that the inner parser gave them in another node.
  // Parse a full expression. The optional arguments are used to
  // forbid the `in` operator (in for loops initialization expressions)
  // and provide reference for storing '=' operator inside shorthand
  // property assignment in contexts where both object expression
  // and object pattern might appear (so it's possible to raise
  // delayed syntax error at correct position).


  _proto.parseExpression = function parseExpression(noIn, refShorthandDefaultPos) {
    var startPos = this.state.start;
    var startLoc = this.state.startLoc;
    var expr = this.parseMaybeAssign(noIn, refShorthandDefaultPos);

    if (this.match(types.comma)) {
      var _node = this.startNodeAt(startPos, startLoc);

      _node.expressions = [expr];

      while (this.eat(types.comma)) {
        _node.expressions.push(this.parseMaybeAssign(noIn, refShorthandDefaultPos));
      }

      this.toReferencedList(_node.expressions);
      return this.finishNode(_node, "SequenceExpression");
    }

    return expr;
  }; // Parse an assignment expression. This includes applications of
  // operators like `+=`.


  _proto.parseMaybeAssign = function parseMaybeAssign(noIn, refShorthandDefaultPos, afterLeftParse, refNeedsArrowPos) {
    var startPos = this.state.start;
    var startLoc = this.state.startLoc;

    if (this.match(types._yield) && this.state.inGenerator) {
      var _left = this.parseYield();

      if (afterLeftParse) {
        _left = afterLeftParse.call(this, _left, startPos, startLoc);
      }

      return _left;
    }

    var failOnShorthandAssign;

    if (refShorthandDefaultPos) {
      failOnShorthandAssign = false;
    } else {
      refShorthandDefaultPos = {
        start: 0
      };
      failOnShorthandAssign = true;
    }

    if (this.match(types.parenL) || this.match(types.name) || this.match(types._yield)) {
      this.state.potentialArrowAt = this.state.start;
    }

    var left = this.parseMaybeConditional(noIn, refShorthandDefaultPos, refNeedsArrowPos);

    if (afterLeftParse) {
      left = afterLeftParse.call(this, left, startPos, startLoc);
    }

    if (this.state.type.isAssign) {
      var _node2 = this.startNodeAt(startPos, startLoc);

      _node2.operator = this.state.value;
      _node2.left = this.match(types.eq) ? this.toAssignable(left, undefined, "assignment expression") : left;
      refShorthandDefaultPos.start = 0; // reset because shorthand default was used correctly

      this.checkLVal(left, undefined, undefined, "assignment expression");

      if (left.extra && left.extra.parenthesized) {
        var errorMsg;

        if (left.type === "ObjectPattern") {
          errorMsg = "`({a}) = 0` use `({a} = 0)`";
        } else if (left.type === "ArrayPattern") {
          errorMsg = "`([a]) = 0` use `([a] = 0)`";
        }

        if (errorMsg) {
          this.raise(left.start, `You're trying to assign to a parenthesized expression, eg. instead of ${errorMsg}`);
        }
      }

      this.next();
      _node2.right = this.parseMaybeAssign(noIn);
      return this.finishNode(_node2, "AssignmentExpression");
    } else if (failOnShorthandAssign && refShorthandDefaultPos.start) {
      this.unexpected(refShorthandDefaultPos.start);
    }

    return left;
  }; // Parse a ternary conditional (`?:`) operator.


  _proto.parseMaybeConditional = function parseMaybeConditional(noIn, refShorthandDefaultPos, refNeedsArrowPos) {
    var startPos = this.state.start;
    var startLoc = this.state.startLoc;
    var potentialArrowAt = this.state.potentialArrowAt;
    var expr = this.parseExprOps(noIn, refShorthandDefaultPos);

    if (expr.type === "ArrowFunctionExpression" && expr.start === potentialArrowAt) {
      return expr;
    }

    if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr;
    return this.parseConditional(expr, noIn, startPos, startLoc, refNeedsArrowPos);
  };

  _proto.parseConditional = function parseConditional(expr, noIn, startPos, startLoc, // FIXME: Disabling this for now since can't seem to get it to play nicely
  refNeedsArrowPos) {
    if (this.eat(types.question)) {
      var _node3 = this.startNodeAt(startPos, startLoc);

      _node3.test = expr;
      _node3.consequent = this.parseMaybeAssign();
      this.expect(types.colon);
      _node3.alternate = this.parseMaybeAssign(noIn);
      return this.finishNode(_node3, "ConditionalExpression");
    }

    return expr;
  }; // Start the precedence parser.


  _proto.parseExprOps = function parseExprOps(noIn, refShorthandDefaultPos) {
    var startPos = this.state.start;
    var startLoc = this.state.startLoc;
    var potentialArrowAt = this.state.potentialArrowAt;
    var expr = this.parseMaybeUnary(refShorthandDefaultPos);

    if (expr.type === "ArrowFunctionExpression" && expr.start === potentialArrowAt) {
      return expr;
    }

    if (refShorthandDefaultPos && refShorthandDefaultPos.start) {
      return expr;
    }

    return this.parseExprOp(expr, startPos, startLoc, -1, noIn);
  }; // Parse binary operators with the operator precedence parsing
  // algorithm. `left` is the left-hand side of the operator.
  // `minPrec` provides context that allows the function to stop and
  // defer further parser to one of its callers when it encounters an
  // operator that has a lower precedence than the set it is parsing.


  _proto.parseExprOp = function parseExprOp(left, leftStartPos, leftStartLoc, minPrec, noIn) {
    var prec = this.state.type.binop;

    if (prec != null && (!noIn || !this.match(types._in))) {
      if (prec > minPrec) {
        var _node4 = this.startNodeAt(leftStartPos, leftStartLoc);

        _node4.left = left;
        _node4.operator = this.state.value;

        if (_node4.operator === "**" && left.type === "UnaryExpression" && left.extra && !left.extra.parenthesizedArgument && !left.extra.parenthesized) {
          this.raise(left.argument.start, "Illegal expression. Wrap left hand side or entire exponentiation in parentheses.");
        }

        var op = this.state.type;
        this.next();
        var startPos = this.state.start;
        var startLoc = this.state.startLoc;

        if (_node4.operator === "|>") {
          this.expectPlugin("pipelineOperator"); // Support syntax such as 10 |> x => x + 1

          this.state.potentialArrowAt = startPos;
        }

        if (_node4.operator === "??") {
          this.expectPlugin("nullishCoalescingOperator");
        }

        _node4.right = this.parseExprOp(this.parseMaybeUnary(), startPos, startLoc, op.rightAssociative ? prec - 1 : prec, noIn);
        this.finishNode(_node4, op === types.logicalOR || op === types.logicalAND || op === types.nullishCoalescing ? "LogicalExpression" : "BinaryExpression");
        return this.parseExprOp(_node4, leftStartPos, leftStartLoc, minPrec, noIn);
      }
    }

    return left;
  }; // Parse unary operators, both prefix and postfix.


  _proto.parseMaybeUnary = function parseMaybeUnary(refShorthandDefaultPos) {
    if (this.state.type.prefix) {
      var _node5 = this.startNode();

      var update = this.match(types.incDec);
      _node5.operator = this.state.value;
      _node5.prefix = true;

      if (_node5.operator === "throw") {
        this.expectPlugin("throwExpressions");
      }

      this.next();
      var argType = this.state.type;
      _node5.argument = this.parseMaybeUnary();
      this.addExtra(_node5, "parenthesizedArgument", argType === types.parenL && (!_node5.argument.extra || !_node5.argument.extra.parenthesized));

      if (refShorthandDefaultPos && refShorthandDefaultPos.start) {
        this.unexpected(refShorthandDefaultPos.start);
      }

      if (update) {
        this.checkLVal(_node5.argument, undefined, undefined, "prefix operation");
      } else if (this.state.strict && _node5.operator === "delete") {
        var arg = _node5.argument;

        if (arg.type === "Identifier") {
          this.raise(_node5.start, "Deleting local variable in strict mode");
        } else if (arg.type === "MemberExpression" && arg.property.type === "PrivateName") {
          this.raise(_node5.start, "Deleting a private field is not allowed");
        }
      }

      return this.finishNode(_node5, update ? "UpdateExpression" : "UnaryExpression");
    }

    var startPos = this.state.start;
    var startLoc = this.state.startLoc;
    var expr = this.parseExprSubscripts(refShorthandDefaultPos);
    if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr;

    while (this.state.type.postfix && !this.canInsertSemicolon()) {
      var _node6 = this.startNodeAt(startPos, startLoc);

      _node6.operator = this.state.value;
      _node6.prefix = false;
      _node6.argument = expr;
      this.checkLVal(expr, undefined, undefined, "postfix operation");
      this.next();
      expr = this.finishNode(_node6, "UpdateExpression");
    }

    return expr;
  }; // Parse call, dot, and `[]`-subscript expressions.


  _proto.parseExprSubscripts = function parseExprSubscripts(refShorthandDefaultPos) {
    var startPos = this.state.start;
    var startLoc = this.state.startLoc;
    var potentialArrowAt = this.state.potentialArrowAt;
    var expr = this.parseExprAtom(refShorthandDefaultPos);

    if (expr.type === "ArrowFunctionExpression" && expr.start === potentialArrowAt) {
      return expr;
    }

    if (refShorthandDefaultPos && refShorthandDefaultPos.start) {
      return expr;
    }

    return this.parseSubscripts(expr, startPos, startLoc);
  };

  _proto.parseSubscripts = function parseSubscripts(base, startPos, startLoc, noCalls) {
    var state = {
      stop: false
    };

    do {
      base = this.parseSubscript(base, startPos, startLoc, noCalls, state);
    } while (!state.stop);

    return base;
  };
  /** @param state Set 'state.stop = true' to indicate that we should stop parsing subscripts. */


  _proto.parseSubscript = function parseSubscript(base, startPos, startLoc, noCalls, state) {
    if (!noCalls && this.eat(types.doubleColon)) {
      var _node7 = this.startNodeAt(startPos, startLoc);

      _node7.object = base;
      _node7.callee = this.parseNoCallExpr();
      state.stop = true;
      return this.parseSubscripts(this.finishNode(_node7, "BindExpression"), startPos, startLoc, noCalls);
    } else if (this.match(types.questionDot)) {
      this.expectPlugin("optionalChaining");

      if (noCalls && this.lookahead().type == types.parenL) {
        state.stop = true;
        return base;
      }

      this.next();

      var _node8 = this.startNodeAt(startPos, startLoc);

      if (this.eat(types.bracketL)) {
        _node8.object = base;
        _node8.property = this.parseExpression();
        _node8.computed = true;
        _node8.optional = true;
        this.expect(types.bracketR);
        return this.finishNode(_node8, "MemberExpression");
      } else if (this.eat(types.parenL)) {
        var possibleAsync = this.atPossibleAsync(base);
        _node8.callee = base;
        _node8.arguments = this.parseCallExpressionArguments(types.parenR, possibleAsync);
        _node8.optional = true;
        return this.finishNode(_node8, "CallExpression");
      } else {
        _node8.object = base;
        _node8.property = this.parseIdentifier(true);
        _node8.computed = false;
        _node8.optional = true;
        return this.finishNode(_node8, "MemberExpression");
      }
    } else if (this.eat(types.dot)) {
      var _node9 = this.startNodeAt(startPos, startLoc);

      _node9.object = base;
      _node9.property = this.parseMaybePrivateName();
      _node9.computed = false;
      return this.finishNode(_node9, "MemberExpression");
    } else if (this.eat(types.bracketL)) {
      var _node10 = this.startNodeAt(startPos, startLoc);

      _node10.object = base;
      _node10.property = this.parseExpression();
      _node10.computed = true;
      this.expect(types.bracketR);
      return this.finishNode(_node10, "MemberExpression");
    } else if (!noCalls && this.match(types.parenL)) {
      var _possibleAsync = this.atPossibleAsync(base);

      this.next();

      var _node11 = this.startNodeAt(startPos, startLoc);

      _node11.callee = base; // TODO: Clean up/merge this into `this.state` or a class like acorn's
      // `DestructuringErrors` alongside refShorthandDefaultPos and
      // refNeedsArrowPos.

      var refTrailingCommaPos = {
        start: -1
      };
      _node11.arguments = this.parseCallExpressionArguments(types.parenR, _possibleAsync, refTrailingCommaPos);
      this.finishCallExpression(_node11);

      if (_possibleAsync && this.shouldParseAsyncArrow()) {
        state.stop = true;

        if (refTrailingCommaPos.start > -1) {
          this.raise(refTrailingCommaPos.start, "A trailing comma is not permitted after the rest element");
        }

        return this.parseAsyncArrowFromCallExpression(this.startNodeAt(startPos, startLoc), _node11);
      } else {
        this.toReferencedList(_node11.arguments);
      }

      return _node11;
    } else if (this.match(types.backQuote)) {
      var _node12 = this.startNodeAt(startPos, startLoc);

      _node12.tag = base;
      _node12.quasi = this.parseTemplate(true);
      return this.finishNode(_node12, "TaggedTemplateExpression");
    } else {
      state.stop = true;
      return base;
    }
  };

  _proto.atPossibleAsync = function atPossibleAsync(base) {
    return this.state.potentialArrowAt === base.start && base.type === "Identifier" && base.name === "async" && !this.canInsertSemicolon();
  };

  _proto.finishCallExpression = function finishCallExpression(node) {
    if (node.callee.type === "Import") {
      if (node.arguments.length !== 1) {
        this.raise(node.start, "import() requires exactly one argument");
      }

      var importArg = node.arguments[0];

      if (importArg && importArg.type === "SpreadElement") {
        this.raise(importArg.start, "... is not allowed in import()");
      }
    }

    return this.finishNode(node, "CallExpression");
  };

  _proto.parseCallExpressionArguments = function parseCallExpressionArguments(close, possibleAsyncArrow, refTrailingCommaPos) {
    var elts = [];
    var innerParenStart;
    var first = true;

    while (!this.eat(close)) {
      if (first) {
        first = false;
      } else {
        this.expect(types.comma);
        if (this.eat(close)) break;
      } // we need to make sure that if this is an async arrow functions, that we don't allow inner parens inside the params


      if (this.match(types.parenL) && !innerParenStart) {
        innerParenStart = this.state.start;
      }

      elts.push(this.parseExprListItem(false, possibleAsyncArrow ? {
        start: 0
      } : undefined, possibleAsyncArrow ? {
        start: 0
      } : undefined, possibleAsyncArrow ? refTrailingCommaPos : undefined));
    } // we found an async arrow function so let's not allow any inner parens


    if (possibleAsyncArrow && innerParenStart && this.shouldParseAsyncArrow()) {
      this.unexpected();
    }

    return elts;
  };

  _proto.shouldParseAsyncArrow = function shouldParseAsyncArrow() {
    return this.match(types.arrow);
  };

  _proto.parseAsyncArrowFromCallExpression = function parseAsyncArrowFromCallExpression(node, call) {
    var oldYield = this.state.yieldInPossibleArrowParameters;
    this.state.yieldInPossibleArrowParameters = null;
    this.expect(types.arrow);
    this.parseArrowExpression(node, call.arguments, true);
    this.state.yieldInPossibleArrowParameters = oldYield;
    return node;
  }; // Parse a no-call expression (like argument of `new` or `::` operators).


  _proto.parseNoCallExpr = function parseNoCallExpr() {
    var startPos = this.state.start;
    var startLoc = this.state.startLoc;
    return this.parseSubscripts(this.parseExprAtom(), startPos, startLoc, true);
  }; // Parse an atomic expression — either a single token that is an
  // expression, an expression started by a keyword like `function` or
  // `new`, or an expression wrapped in punctuation like `()`, `[]`,
  // or `{}`.


  _proto.parseExprAtom = function parseExprAtom(refShorthandDefaultPos) {
    var canBeArrow = this.state.potentialArrowAt === this.state.start;
    var node;

    switch (this.state.type) {
      case types._super:
        if (!this.state.inMethod && !this.state.inClassProperty && !this.options.allowSuperOutsideMethod) {
          this.raise(this.state.start, "super is only allowed in object methods and classes");
        }

        node = this.startNode();
        this.next();

        if (!this.match(types.parenL) && !this.match(types.bracketL) && !this.match(types.dot)) {
          this.unexpected();
        }

        if (this.match(types.parenL) && this.state.inMethod !== "constructor" && !this.options.allowSuperOutsideMethod) {
          this.raise(node.start, "super() is only valid inside a class constructor. Make sure the method name is spelled exactly as 'constructor'.");
        }

        return this.finishNode(node, "Super");

      case types._import:
        if (this.lookahead().type === types.dot) {
          return this.parseImportMetaProperty();
        }

        this.expectPlugin("dynamicImport");
        node = this.startNode();
        this.next();

        if (!this.match(types.parenL)) {
          this.unexpected(null, types.parenL);
        }

        return this.finishNode(node, "Import");

      case types._this:
        node = this.startNode();
        this.next();
        return this.finishNode(node, "ThisExpression");

      case types._yield:
        if (this.state.inGenerator) this.unexpected();

      case types.name:
        {
          node = this.startNode();
          var allowAwait = this.state.value === "await" && this.state.inAsync;
          var allowYield = this.shouldAllowYieldIdentifier();
          var id = this.parseIdentifier(allowAwait || allowYield);

          if (id.name === "await") {
            if (this.state.inAsync || this.inModule) {
              return this.parseAwait(node);
            }
          } else if (id.name === "async" && this.match(types._function) && !this.canInsertSemicolon()) {
            this.next();
            return this.parseFunction(node, false, false, true);
          } else if (canBeArrow && id.name === "async" && this.match(types.name)) {
            var oldYield = this.state.yieldInPossibleArrowParameters;
            this.state.yieldInPossibleArrowParameters = null;
            var params = [this.parseIdentifier()];
            this.expect(types.arrow); // let foo = bar => {};

            this.parseArrowExpression(node, params, true);
            this.state.yieldInPossibleArrowParameters = oldYield;
            return node;
          }

          if (canBeArrow && !this.canInsertSemicolon() && this.eat(types.arrow)) {
            var _oldYield = this.state.yieldInPossibleArrowParameters;
            this.state.yieldInPossibleArrowParameters = null;
            this.parseArrowExpression(node, [id]);
            this.state.yieldInPossibleArrowParameters = _oldYield;
            return node;
          }

          return id;
        }

      case types._do:
        {
          this.expectPlugin("doExpressions");

          var _node13 = this.startNode();

          this.next();
          var oldInFunction = this.state.inFunction;
          var oldLabels = this.state.labels;
          this.state.labels = [];
          this.state.inFunction = false;
          _node13.body = this.parseBlock(false);
          this.state.inFunction = oldInFunction;
          this.state.labels = oldLabels;
          return this.finishNode(_node13, "DoExpression");
        }

      case types.regexp:
        {
          var value = this.state.value;
          node = this.parseLiteral(value.value, "RegExpLiteral");
          node.pattern = value.pattern;
          node.flags = value.flags;
          return node;
        }

      case types.num:
        return this.parseLiteral(this.state.value, "NumericLiteral");

      case types.bigint:
        return this.parseLiteral(this.state.value, "BigIntLiteral");

      case types.string:
        return this.parseLiteral(this.state.value, "StringLiteral");

      case types._null:
        node = this.startNode();
        this.next();
        return this.finishNode(node, "NullLiteral");

      case types._true:
      case types._false:
        return this.parseBooleanLiteral();

      case types.parenL:
        return this.parseParenAndDistinguishExpression(canBeArrow);

      case types.bracketL:
        node = this.startNode();
        this.next();
        node.elements = this.parseExprList(types.bracketR, true, refShorthandDefaultPos);
        this.toReferencedList(node.elements);
        return this.finishNode(node, "ArrayExpression");

      case types.braceL:
        return this.parseObj(false, refShorthandDefaultPos);

      case types._function:
        return this.parseFunctionExpression();

      case types.at:
        this.parseDecorators();

      case types._class:
        node = this.startNode();
        this.takeDecorators(node);
        return this.parseClass(node, false);

      case types._new:
        return this.parseNew();

      case types.backQuote:
        return this.parseTemplate(false);

      case types.doubleColon:
        {
          node = this.startNode();
          this.next();
          node.object = null;
          var callee = node.callee = this.parseNoCallExpr();

          if (callee.type === "MemberExpression") {
            return this.finishNode(node, "BindExpression");
          } else {
            throw this.raise(callee.start, "Binding should be performed on object property.");
          }
        }

      default:
        throw this.unexpected();
    }
  };

  _proto.parseBooleanLiteral = function parseBooleanLiteral() {
    var node = this.startNode();
    node.value = this.match(types._true);
    this.next();
    return this.finishNode(node, "BooleanLiteral");
  };

  _proto.parseMaybePrivateName = function parseMaybePrivateName() {
    var isPrivate = this.match(types.hash);

    if (isPrivate) {
      this.expectOnePlugin(["classPrivateProperties", "classPrivateMethods"]);

      var _node14 = this.startNode();

      this.next();
      _node14.id = this.parseIdentifier(true);
      return this.finishNode(_node14, "PrivateName");
    } else {
      return this.parseIdentifier(true);
    }
  };

  _proto.parseFunctionExpression = function parseFunctionExpression() {
    var node = this.startNode();
    var meta = this.parseIdentifier(true);

    if (this.state.inGenerator && this.eat(types.dot)) {
      return this.parseMetaProperty(node, meta, "sent");
    }

    return this.parseFunction(node, false);
  };

  _proto.parseMetaProperty = function parseMetaProperty(node, meta, propertyName) {
    node.meta = meta;

    if (meta.name === "function" && propertyName === "sent") {
      if (this.isContextual(propertyName)) {
        this.expectPlugin("functionSent");
      } else if (!this.hasPlugin("functionSent")) {
        // They didn't actually say `function.sent`, just `function.`, so a simple error would be less confusing.
        this.unexpected();
      }
    }

    node.property = this.parseIdentifier(true);

    if (node.property.name !== propertyName) {
      this.raise(node.property.start, `The only valid meta property for ${meta.name} is ${meta.name}.${propertyName}`);
    }

    return this.finishNode(node, "MetaProperty");
  };

  _proto.parseImportMetaProperty = function parseImportMetaProperty() {
    var node = this.startNode();
    var id = this.parseIdentifier(true);
    this.expect(types.dot);

    if (id.name === "import") {
      if (this.isContextual("meta")) {
        this.expectPlugin("importMeta");
      } else if (!this.hasPlugin("importMeta")) {
        this.raise(id.start, `Dynamic imports require a parameter: import('a.js').then`);
      }
    }

    if (!this.inModule) {
      this.raise(id.start, `import.meta may appear only with 'sourceType: "module"'`);
    }

    return this.parseMetaProperty(node, id, "meta");
  };

  _proto.parseLiteral = function parseLiteral(value, type, startPos, startLoc) {
    startPos = startPos || this.state.start;
    startLoc = startLoc || this.state.startLoc;
    var node = this.startNodeAt(startPos, startLoc);
    this.addExtra(node, "rawValue", value);
    this.addExtra(node, "raw", this.input.slice(startPos, this.state.end));
    node.value = value;
    this.next();
    return this.finishNode(node, type);
  };

  _proto.parseParenExpression = function parseParenExpression() {
    this.expect(types.parenL);
    var val = this.parseExpression();
    this.expect(types.parenR);
    return val;
  };

  _proto.parseParenAndDistinguishExpression = function parseParenAndDistinguishExpression(canBeArrow) {
    var startPos = this.state.start;
    var startLoc = this.state.startLoc;
    var val;
    this.expect(types.parenL);
    var oldMaybeInArrowParameters = this.state.maybeInArrowParameters;
    var oldYield = this.state.yieldInPossibleArrowParameters;
    this.state.maybeInArrowParameters = true;
    this.state.yieldInPossibleArrowParameters = null;
    var innerStartPos = this.state.start;
    var innerStartLoc = this.state.startLoc;
    var exprList = [];
    var refShorthandDefaultPos = {
      start: 0
    };
    var refNeedsArrowPos = {
      start: 0
    };
    var first = true;
    var spreadStart;
    var optionalCommaStart;

    while (!this.match(types.parenR)) {
      if (first) {
        first = false;
      } else {
        this.expect(types.comma, refNeedsArrowPos.start || null);

        if (this.match(types.parenR)) {
          optionalCommaStart = this.state.start;
          break;
        }
      }

      if (this.match(types.ellipsis)) {
        var spreadNodeStartPos = this.state.start;
        var spreadNodeStartLoc = this.state.startLoc;
        spreadStart = this.state.start;
        exprList.push(this.parseParenItem(this.parseRest(), spreadNodeStartPos, spreadNodeStartLoc));

        if (this.match(types.comma) && this.lookahead().type === types.parenR) {
          this.raise(this.state.start, "A trailing comma is not permitted after the rest element");
        }

        break;
      } else {
        exprList.push(this.parseMaybeAssign(false, refShorthandDefaultPos, this.parseParenItem, refNeedsArrowPos));
      }
    }

    var innerEndPos = this.state.start;
    var innerEndLoc = this.state.startLoc;
    this.expect(types.parenR);
    this.state.maybeInArrowParameters = oldMaybeInArrowParameters;
    var arrowNode = this.startNodeAt(startPos, startLoc);

    if (canBeArrow && this.shouldParseArrow() && (arrowNode = this.parseArrow(arrowNode))) {
      for (var _i2 = 0; _i2 < exprList.length; _i2++) {
        var param = exprList[_i2];

        if (param.extra && param.extra.parenthesized) {
          this.unexpected(param.extra.parenStart);
        }
      }

      this.parseArrowExpression(arrowNode, exprList);
      this.state.yieldInPossibleArrowParameters = oldYield;
      return arrowNode;
    }

    this.state.yieldInPossibleArrowParameters = oldYield;

    if (!exprList.length) {
      this.unexpected(this.state.lastTokStart);
    }

    if (optionalCommaStart) this.unexpected(optionalCommaStart);
    if (spreadStart) this.unexpected(spreadStart);

    if (refShorthandDefaultPos.start) {
      this.unexpected(refShorthandDefaultPos.start);
    }

    if (refNeedsArrowPos.start) this.unexpected(refNeedsArrowPos.start);

    if (exprList.length > 1) {
      val = this.startNodeAt(innerStartPos, innerStartLoc);
      val.expressions = exprList;
      this.toReferencedList(val.expressions);
      this.finishNodeAt(val, "SequenceExpression", innerEndPos, innerEndLoc);
    } else {
      val = exprList[0];
    }

    this.addExtra(val, "parenthesized", true);
    this.addExtra(val, "parenStart", startPos);
    return val;
  };

  _proto.shouldParseArrow = function shouldParseArrow() {
    return !this.canInsertSemicolon();
  };

  _proto.parseArrow = function parseArrow(node) {
    if (this.eat(types.arrow)) {
      return node;
    }
  };

  _proto.parseParenItem = function parseParenItem(node, startPos, // eslint-disable-next-line no-unused-vars
  startLoc) {
    return node;
  }; // New's precedence is slightly tricky. It must allow its argument to
  // be a `[]` or dot subscript expression, but not a call — at least,
  // not without wrapping it in parentheses. Thus, it uses the noCalls
  // argument to parseSubscripts to prevent it from consuming the
  // argument list.


  _proto.parseNew = function parseNew() {
    var node = this.startNode();
    var meta = this.parseIdentifier(true);

    if (this.eat(types.dot)) {
      var metaProp = this.parseMetaProperty(node, meta, "target");

      if (!this.state.inFunction && !this.state.inClassProperty) {
        var error = "new.target can only be used in functions";

        if (this.hasPlugin("classProperties")) {
          error += " or class properties";
        }

        this.raise(metaProp.start, error);
      }

      return metaProp;
    }

    node.callee = this.parseNoCallExpr();
    if (this.eat(types.questionDot)) node.optional = true;
    this.parseNewArguments(node);
    return this.finishNode(node, "NewExpression");
  };

  _proto.parseNewArguments = function parseNewArguments(node) {
    if (this.eat(types.parenL)) {
      var args = this.parseExprList(types.parenR);
      this.toReferencedList(args); // $FlowFixMe (parseExprList should be all non-null in this case)

      node.arguments = args;
    } else {
      node.arguments = [];
    }
  }; // Parse template expression.


  _proto.parseTemplateElement = function parseTemplateElement(isTagged) {
    var elem = this.startNode();

    if (this.state.value === null) {
      if (!isTagged) {
        // TODO: fix this
        this.raise(this.state.invalidTemplateEscapePosition || 0, "Invalid escape sequence in template");
      } else {
        this.state.invalidTemplateEscapePosition = null;
      }
    }

    elem.value = {
      raw: this.input.slice(this.state.start, this.state.end).replace(/\r\n?/g, "\n"),
      cooked: this.state.value
    };
    this.next();
    elem.tail = this.match(types.backQuote);
    return this.finishNode(elem, "TemplateElement");
  };

  _proto.parseTemplate = function parseTemplate(isTagged) {
    var node = this.startNode();
    this.next();
    node.expressions = [];
    var curElt = this.parseTemplateElement(isTagged);
    node.quasis = [curElt];

    while (!curElt.tail) {
      this.expect(types.dollarBraceL);
      node.expressions.push(this.parseExpression());
      this.expect(types.braceR);
      node.quasis.push(curElt = this.parseTemplateElement(isTagged));
    }

    this.next();
    return this.finishNode(node, "TemplateLiteral");
  }; // Parse an object literal or binding pattern.


  _proto.parseObj = function parseObj(isPattern, refShorthandDefaultPos) {
    var decorators = [];
    var propHash = Object.create(null);
    var first = true;
    var node = this.startNode();
    node.properties = [];
    this.next();
    var firstRestLocation = null;

    while (!this.eat(types.braceR)) {
      if (first) {
        first = false;
      } else {
        this.expect(types.comma);
        if (this.eat(types.braceR)) break;
      }

      if (this.match(types.at)) {
        if (this.hasPlugin("decorators2")) {
          this.raise(this.state.start, "Stage 2 decorators disallow object literal property decorators");
        } else {
          // we needn't check if decorators (stage 0) plugin is enabled since it's checked by
          // the call to this.parseDecorator
          while (this.match(types.at)) {
            decorators.push(this.parseDecorator());
          }
        }
      }

      var prop = this.startNode(),
          isGenerator = false,
          _isAsync = false,
          startPos = void 0,
          startLoc = void 0;

      if (decorators.length) {
        prop.decorators = decorators;
        decorators = [];
      }

      if (this.match(types.ellipsis)) {
        this.expectPlugin("objectRestSpread");
        prop = this.parseSpread(isPattern ? {
          start: 0
        } : undefined);

        if (isPattern) {
          this.toAssignable(prop, true, "object pattern");
        }

        node.properties.push(prop);

        if (isPattern) {
          var position = this.state.start;

          if (firstRestLocation !== null) {
            this.unexpected(firstRestLocation, "Cannot have multiple rest elements when destructuring");
          } else if (this.eat(types.braceR)) {
            break;
          } else if (this.match(types.comma) && this.lookahead().type === types.braceR) {
            this.unexpected(position, "A trailing comma is not permitted after the rest element");
          } else {
            firstRestLocation = position;
            continue;
          }
        } else {
          continue;
        }
      }

      prop.method = false;

      if (isPattern || refShorthandDefaultPos) {
        startPos = this.state.start;
        startLoc = this.state.startLoc;
      }

      if (!isPattern) {
        isGenerator = this.eat(types.star);
      }

      if (!isPattern && this.isContextual("async")) {
        if (isGenerator) this.unexpected();
        var asyncId = this.parseIdentifier();

        if (this.match(types.colon) || this.match(types.parenL) || this.match(types.braceR) || this.match(types.eq) || this.match(types.comma)) {
          prop.key = asyncId;
          prop.computed = false;
        } else {
          _isAsync = true;

          if (this.match(types.star)) {
            this.expectPlugin("asyncGenerators");
            this.next();
            isGenerator = true;
          }

          this.parsePropertyName(prop);
        }
      } else {
        this.parsePropertyName(prop);
      }

      this.parseObjPropValue(prop, startPos, startLoc, isGenerator, _isAsync, isPattern, refShorthandDefaultPos);
      this.checkPropClash(prop, propHash);

      if (prop.shorthand) {
        this.addExtra(prop, "shorthand", true);
      }

      node.properties.push(prop);
    }

    if (firstRestLocation !== null) {
      this.unexpected(firstRestLocation, "The rest element has to be the last element when destructuring");
    }

    if (decorators.length) {
      this.raise(this.state.start, "You have trailing decorators with no property");
    }

    return this.finishNode(node, isPattern ? "ObjectPattern" : "ObjectExpression");
  };

  _proto.isGetterOrSetterMethod = function isGetterOrSetterMethod(prop, isPattern) {
    return !isPattern && !prop.computed && prop.key.type === "Identifier" && (prop.key.name === "get" || prop.key.name === "set") && (this.match(types.string) || // get "string"() {}
    this.match(types.num) || // get 1() {}
    this.match(types.bracketL) || // get ["string"]() {}
    this.match(types.name) || // get foo() {}
    !!this.state.type.keyword) // get debugger() {}
    ;
  }; // get methods aren't allowed to have any parameters
  // set methods must have exactly 1 parameter


  _proto.checkGetterSetterParamCount = function checkGetterSetterParamCount(method) {
    var paramCount = method.kind === "get" ? 0 : 1;

    if (method.params.length !== paramCount) {
      var start = method.start;

      if (method.kind === "get") {
        this.raise(start, "getter should have no params");
      } else {
        this.raise(start, "setter should have exactly one param");
      }
    }
  };

  _proto.parseObjectMethod = function parseObjectMethod(prop, isGenerator, isAsync, isPattern) {
    if (isAsync || isGenerator || this.match(types.parenL)) {
      if (isPattern) this.unexpected();
      prop.kind = "method";
      prop.method = true;
      return this.parseMethod(prop, isGenerator, isAsync,
      /* isConstructor */
      false, "ObjectMethod");
    }

    if (this.isGetterOrSetterMethod(prop, isPattern)) {
      if (isGenerator || isAsync) this.unexpected();
      prop.kind = prop.key.name;
      this.parsePropertyName(prop);
      this.parseMethod(prop,
      /* isGenerator */
      false,
      /* isAsync */
      false,
      /* isConstructor */
      false, "ObjectMethod");
      this.checkGetterSetterParamCount(prop);
      return prop;
    }
  };

  _proto.parseObjectProperty = function parseObjectProperty(prop, startPos, startLoc, isPattern, refShorthandDefaultPos) {
    prop.shorthand = false;

    if (this.eat(types.colon)) {
      prop.value = isPattern ? this.parseMaybeDefault(this.state.start, this.state.startLoc) : this.parseMaybeAssign(false, refShorthandDefaultPos);
      return this.finishNode(prop, "ObjectProperty");
    }

    if (!prop.computed && prop.key.type === "Identifier") {
      this.checkReservedWord(prop.key.name, prop.key.start, true, true);

      if (isPattern) {
        prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key.__clone());
      } else if (this.match(types.eq) && refShorthandDefaultPos) {
        if (!refShorthandDefaultPos.start) {
          refShorthandDefaultPos.start = this.state.start;
        }

        prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key.__clone());
      } else {
        prop.value = prop.key.__clone();
      }

      prop.shorthand = true;
      return this.finishNode(prop, "ObjectProperty");
    }
  };

  _proto.parseObjPropValue = function parseObjPropValue(prop, startPos, startLoc, isGenerator, isAsync, isPattern, refShorthandDefaultPos) {
    var node = this.parseObjectMethod(prop, isGenerator, isAsync, isPattern) || this.parseObjectProperty(prop, startPos, startLoc, isPattern, refShorthandDefaultPos);
    if (!node) this.unexpected(); // $FlowFixMe

    return node;
  };

  _proto.parsePropertyName = function parsePropertyName(prop) {
    if (this.eat(types.bracketL)) {
      prop.computed = true;
      prop.key = this.parseMaybeAssign();
      this.expect(types.bracketR);
    } else {
      var oldInPropertyName = this.state.inPropertyName;
      this.state.inPropertyName = true; // We check if it's valid for it to be a private name when we push it.

      prop.key = this.match(types.num) || this.match(types.string) ? this.parseExprAtom() : this.parseMaybePrivateName();

      if (prop.key.type !== "PrivateName") {
        // ClassPrivateProperty is never computed, so we don't assign in that case.
        prop.computed = false;
      }

      this.state.inPropertyName = oldInPropertyName;
    }

    return prop.key;
  }; // Initialize empty function node.


  _proto.initFunction = function initFunction(node, isAsync) {
    node.id = null;
    node.generator = false;
    node.async = !!isAsync;
  }; // Parse object or class method.


  _proto.parseMethod = function parseMethod(node, isGenerator, isAsync, isConstructor, type) {
    var oldInFunc = this.state.inFunction;
    var oldInMethod = this.state.inMethod;
    var oldInGenerator = this.state.inGenerator;
    this.state.inFunction = true;
    this.state.inMethod = node.kind || true;
    this.state.inGenerator = isGenerator;
    this.initFunction(node, isAsync);
    node.generator = !!isGenerator;
    var allowModifiers = isConstructor; // For TypeScript parameter properties

    this.parseFunctionParams(node, allowModifiers);
    this.parseFunctionBodyAndFinish(node, type);
    this.state.inFunction = oldInFunc;
    this.state.inMethod = oldInMethod;
    this.state.inGenerator = oldInGenerator;
    return node;
  }; // Parse arrow function expression.
  // If the parameters are provided, they will be converted to an
  // assignable list.


  _proto.parseArrowExpression = function parseArrowExpression(node, params, isAsync) {
    // if we got there, it's no more "yield in possible arrow parameters";
    // it's just "yield in arrow parameters"
    if (this.state.yieldInPossibleArrowParameters) {
      this.raise(this.state.yieldInPossibleArrowParameters.start, "yield is not allowed in the parameters of an arrow function" + " inside a generator");
    }

    var oldInFunc = this.state.inFunction;
    this.state.inFunction = true;
    this.initFunction(node, isAsync);
    if (params) this.setArrowFunctionParameters(node, params);
    var oldInGenerator = this.state.inGenerator;
    var oldMaybeInArrowParameters = this.state.maybeInArrowParameters;
    this.state.inGenerator = false;
    this.state.maybeInArrowParameters = false;
    this.parseFunctionBody(node, true);
    this.state.inGenerator = oldInGenerator;
    this.state.inFunction = oldInFunc;
    this.state.maybeInArrowParameters = oldMaybeInArrowParameters;
    return this.finishNode(node, "ArrowFunctionExpression");
  };

  _proto.setArrowFunctionParameters = function setArrowFunctionParameters(node, params) {
    node.params = this.toAssignableList(params, true, "arrow function parameters");
  };

  _proto.isStrictBody = function isStrictBody(node) {
    var isBlockStatement = node.body.type === "BlockStatement";

    if (isBlockStatement && node.body.directives.length) {
      for (var _i4 = 0, _node$body$directives2 = node.body.directives; _i4 < _node$body$directives2.length; _i4++) {
        var directive = _node$body$directives2[_i4];

        if (directive.value.value === "use strict") {
          return true;
        }
      }
    }

    return false;
  };

  _proto.parseFunctionBodyAndFinish = function parseFunctionBodyAndFinish(node, type, allowExpressionBody) {
    // $FlowIgnore (node is not bodiless if we get here)
    this.parseFunctionBody(node, allowExpressionBody);
    this.finishNode(node, type);
  }; // Parse function body and check parameters.


  _proto.parseFunctionBody = function parseFunctionBody(node, allowExpression) {
    var isExpression = allowExpression && !this.match(types.braceL);
    var oldInParameters = this.state.inParameters;
    var oldInAsync = this.state.inAsync;
    this.state.inParameters = false;
    this.state.inAsync = node.async;

    if (isExpression) {
      node.body = this.parseMaybeAssign();
    } else {
      // Start a new scope with regard to labels and the `inGenerator`
      // flag (restore them to their old value afterwards).
      var oldInGen = this.state.inGenerator;
      var oldInFunc = this.state.inFunction;
      var oldLabels = this.state.labels;
      this.state.inGenerator = node.generator;
      this.state.inFunction = true;
      this.state.labels = [];
      node.body = this.parseBlock(true);
      this.state.inFunction = oldInFunc;
      this.state.inGenerator = oldInGen;
      this.state.labels = oldLabels;
    }

    this.state.inAsync = oldInAsync;
    this.checkFunctionNameAndParams(node, allowExpression);
    this.state.inParameters = oldInParameters;
  };

  _proto.checkFunctionNameAndParams = function checkFunctionNameAndParams(node, isArrowFunction) {
    // If this is a strict mode function, verify that argument names
    // are not repeated, and it does not try to bind the words `eval`
    // or `arguments`.
    var isStrict = this.isStrictBody(node); // Also check for arrow functions

    var checkLVal = this.state.strict || isStrict || isArrowFunction;
    var oldStrict = this.state.strict;
    if (isStrict) this.state.strict = isStrict;

    if (node.id) {
      this.checkReservedWord(node.id, node.start, true, true);
    }

    if (checkLVal) {
      var nameHash = Object.create(null);

      if (node.id) {
        this.checkLVal(node.id, true, undefined, "function name");
      }

      for (var _i6 = 0, _node$params2 = node.params; _i6 < _node$params2.length; _i6++) {
        var param = _node$params2[_i6];

        if (isStrict && param.type !== "Identifier") {
          this.raise(param.start, "Non-simple parameter in strict mode");
        }

        this.checkLVal(param, true, nameHash, "function parameter list");
      }
    }

    this.state.strict = oldStrict;
  }; // Parses a comma-separated list of expressions, and returns them as
  // an array. `close` is the token type that ends the list, and
  // `allowEmpty` can be turned on to allow subsequent commas with
  // nothing in between them to be parsed as `null` (which is needed
  // for array literals).


  _proto.parseExprList = function parseExprList(close, allowEmpty, refShorthandDefaultPos) {
    var elts = [];
    var first = true;

    while (!this.eat(close)) {
      if (first) {
        first = false;
      } else {
        this.expect(types.comma);
        if (this.eat(close)) break;
      }

      elts.push(this.parseExprListItem(allowEmpty, refShorthandDefaultPos));
    }

    return elts;
  };

  _proto.parseExprListItem = function parseExprListItem(allowEmpty, refShorthandDefaultPos, refNeedsArrowPos, refTrailingCommaPos) {
    var elt;

    if (allowEmpty && this.match(types.comma)) {
      elt = null;
    } else if (this.match(types.ellipsis)) {
      elt = this.parseSpread(refShorthandDefaultPos);

      if (refTrailingCommaPos && this.match(types.comma)) {
        refTrailingCommaPos.start = this.state.start;
      }
    } else {
      elt = this.parseMaybeAssign(false, refShorthandDefaultPos, this.parseParenItem, refNeedsArrowPos);
    }

    return elt;
  }; // Parse the next token as an identifier. If `liberal` is true (used
  // when parsing properties), it will also convert keywords into
  // identifiers.


  _proto.parseIdentifier = function parseIdentifier(liberal) {
    var node = this.startNode();
    var name = this.parseIdentifierName(node.start, liberal);
    node.name = name;
    node.loc.identifierName = name;
    return this.finishNode(node, "Identifier");
  };

  _proto.parseIdentifierName = function parseIdentifierName(pos, liberal) {
    if (!liberal) {
      this.checkReservedWord(this.state.value, this.state.start, !!this.state.type.keyword, false);
    }

    var name;

    if (this.match(types.name)) {
      name = this.state.value;
    } else if (this.state.type.keyword) {
      name = this.state.type.keyword;
    } else {
      throw this.unexpected();
    }

    if (!liberal && name === "await" && this.state.inAsync) {
      this.raise(pos, "invalid use of await inside of an async function");
    }

    this.next();
    return name;
  };

  _proto.checkReservedWord = function checkReservedWord(word, startLoc, checkKeywords, isBinding) {
    if (this.state.strict && (reservedWords.strict(word) || isBinding && reservedWords.strictBind(word))) {
      this.raise(startLoc, word + " is a reserved word in strict mode");
    }

    if (this.state.inGenerator && word === "yield") {
      this.raise(startLoc, "yield is a reserved word inside generator functions");
    }

    if (this.isReservedWord(word) || checkKeywords && this.isKeyword(word)) {
      this.raise(startLoc, word + " is a reserved word");
    }
  }; // Parses await expression inside async function.


  _proto.parseAwait = function parseAwait(node) {
    // istanbul ignore next: this condition is checked at the call site so won't be hit here
    if (!this.state.inAsync) {
      this.unexpected();
    }

    if (this.match(types.star)) {
      this.raise(node.start, "await* has been removed from the async functions proposal. Use Promise.all() instead.");
    }

    node.argument = this.parseMaybeUnary();
    return this.finishNode(node, "AwaitExpression");
  }; // Parses yield expression inside generator.


  _proto.parseYield = function parseYield() {
    var node = this.startNode();

    if (this.state.inParameters) {
      this.raise(node.start, "yield is not allowed in generator parameters");
    }

    if (this.state.maybeInArrowParameters && // We only set yieldInPossibleArrowParameters if we haven't already
    // found a possible invalid YieldExpression.
    !this.state.yieldInPossibleArrowParameters) {
      this.state.yieldInPossibleArrowParameters = node;
    }

    this.next();

    if (this.match(types.semi) || this.canInsertSemicolon() || !this.match(types.star) && !this.state.type.startsExpr) {
      node.delegate = false;
      node.argument = null;
    } else {
      node.delegate = this.eat(types.star);
      node.argument = this.parseMaybeAssign();
    }

    return this.finishNode(node, "YieldExpression");
  };

  return ExpressionParser;
}(LValParser);

/* eslint max-len: 0 */
var empty = [];
var loopLabel = {
  kind: "loop"
};
var switchLabel = {
  kind: "switch"
};

var StatementParser =
/*#__PURE__*/
function (_ExpressionParser) {
  _inheritsLoose(StatementParser, _ExpressionParser);

  function StatementParser() {
    return _ExpressionParser.apply(this, arguments) || this;
  }

  var _proto = StatementParser.prototype;

  // ### Statement parsing
  // Parse a program. Initializes the parser, reads any number of
  // statements, and wraps them in a Program node.  Optionally takes a
  // `program` argument.  If present, the statements will be appended
  // to its body instead of creating a new node.
  _proto.parseTopLevel = function parseTopLevel(file, program) {
    program.sourceType = this.options.sourceType;
    this.parseBlockBody(program, true, true, types.eof);
    file.program = this.finishNode(program, "Program");
    file.comments = this.state.comments;
    if (this.options.tokens) file.tokens = this.state.tokens;
    return this.finishNode(file, "File");
  }; // TODO


  _proto.stmtToDirective = function stmtToDirective(stmt) {
    var expr = stmt.expression;
    var directiveLiteral = this.startNodeAt(expr.start, expr.loc.start);
    var directive = this.startNodeAt(stmt.start, stmt.loc.start);
    var raw = this.input.slice(expr.start, expr.end);
    var val = directiveLiteral.value = raw.slice(1, -1); // remove quotes

    this.addExtra(directiveLiteral, "raw", raw);
    this.addExtra(directiveLiteral, "rawValue", val);
    directive.value = this.finishNodeAt(directiveLiteral, "DirectiveLiteral", expr.end, expr.loc.end);
    return this.finishNodeAt(directive, "Directive", stmt.end, stmt.loc.end);
  }; // Parse a single statement.
  //
  // If expecting a statement and finding a slash operator, parse a
  // regular expression literal. This is to handle cases like
  // `if (foo) /blah/.exec(foo)`, where looking at the previous token
  // does not help.


  _proto.parseStatement = function parseStatement(declaration, topLevel) {
    if (this.match(types.at)) {
      this.parseDecorators(true);
    }

    return this.parseStatementContent(declaration, topLevel);
  };

  _proto.parseStatementContent = function parseStatementContent(declaration, topLevel) {
    var starttype = this.state.type;
    var node = this.startNode(); // Most types of statements are recognized by the keyword they
    // start with. Many are trivial to parse, some require a bit of
    // complexity.

    switch (starttype) {
      case types._break:
      case types._continue:
        // $FlowFixMe
        return this.parseBreakContinueStatement(node, starttype.keyword);

      case types._debugger:
        return this.parseDebuggerStatement(node);

      case types._do:
        return this.parseDoStatement(node);

      case types._for:
        return this.parseForStatement(node);

      case types._function:
        if (this.lookahead().type === types.dot) break;
        if (!declaration) this.unexpected();
        return this.parseFunctionStatement(node);

      case types._class:
        if (!declaration) this.unexpected();
        return this.parseClass(node, true);

      case types._if:
        return this.parseIfStatement(node);

      case types._return:
        return this.parseReturnStatement(node);

      case types._switch:
        return this.parseSwitchStatement(node);

      case types._throw:
        return this.parseThrowStatement(node);

      case types._try:
        return this.parseTryStatement(node);

      case types._let:
      case types._const:
        if (!declaration) this.unexpected();
      // NOTE: falls through to _var

      case types._var:
        return this.parseVarStatement(node, starttype);

      case types._while:
        return this.parseWhileStatement(node);

      case types._with:
        return this.parseWithStatement(node);

      case types.braceL:
        return this.parseBlock();

      case types.semi:
        return this.parseEmptyStatement(node);

      case types._export:
      case types._import:
        {
          var nextToken = this.lookahead();

          if (nextToken.type === types.parenL || nextToken.type === types.dot) {
            break;
          }

          if (!this.options.allowImportExportEverywhere && !topLevel) {
            this.raise(this.state.start, "'import' and 'export' may only appear at the top level");
          }

          this.next();
          var result;

          if (starttype == types._import) {
            result = this.parseImport(node);
          } else {
            result = this.parseExport(node);
          }

          this.assertModuleNodeAllowed(node);
          return result;
        }

      case types.name:
        if (this.state.value === "async") {
          // peek ahead and see if next token is a function
          var state = this.state.clone();
          this.next();

          if (this.match(types._function) && !this.canInsertSemicolon()) {
            this.expect(types._function);
            return this.parseFunction(node, true, false, true);
          } else {
            this.state = state;
          }
        }

    } // If the statement does not start with a statement keyword or a
    // brace, it's an ExpressionStatement or LabeledStatement. We
    // simply start parsing an expression, and afterwards, if the
    // next token is a colon and the expression was a simple
    // Identifier node, we switch to interpreting it as a label.


    var maybeName = this.state.value;
    var expr = this.parseExpression();

    if (starttype === types.name && expr.type === "Identifier" && this.eat(types.colon)) {
      return this.parseLabeledStatement(node, maybeName, expr);
    } else {
      return this.parseExpressionStatement(node, expr);
    }
  };

  _proto.assertModuleNodeAllowed = function assertModuleNodeAllowed(node) {
    if (!this.options.allowImportExportEverywhere && !this.inModule) {
      this.raise(node.start, `'import' and 'export' may appear only with 'sourceType: "module"'`);
    }
  };

  _proto.takeDecorators = function takeDecorators(node) {
    var decorators = this.state.decoratorStack[this.state.decoratorStack.length - 1];

    if (decorators.length) {
      node.decorators = decorators;
      this.resetStartLocationFromNode(node, decorators[0]);
      this.state.decoratorStack[this.state.decoratorStack.length - 1] = [];
    }
  };

  _proto.parseDecorators = function parseDecorators(allowExport) {
    if (this.hasPlugin("decorators2")) {
      allowExport = false;
    }

    var currentContextDecorators = this.state.decoratorStack[this.state.decoratorStack.length - 1];

    while (this.match(types.at)) {
      var decorator = this.parseDecorator();
      currentContextDecorators.push(decorator);
    }

    if (this.match(types._export)) {
      if (allowExport) {
        return;
      } else {
        this.raise(this.state.start, "Using the export keyword between a decorator and a class is not allowed. Please use `export @dec class` instead");
      }
    }

    if (!this.match(types._class)) {
      this.raise(this.state.start, "Leading decorators must be attached to a class declaration");
    }
  };

  _proto.parseDecorator = function parseDecorator() {
    this.expectOnePlugin(["decorators", "decorators2"]);
    var node = this.startNode();
    this.next();

    if (this.hasPlugin("decorators2")) {
      var startPos = this.state.start;
      var startLoc = this.state.startLoc;
      var expr = this.parseIdentifier(false);

      while (this.eat(types.dot)) {
        var _node = this.startNodeAt(startPos, startLoc);

        _node.object = expr;
        _node.property = this.parseIdentifier(true);
        _node.computed = false;
        expr = this.finishNode(_node, "MemberExpression");
      }

      if (this.eat(types.parenL)) {
        var _node2 = this.startNodeAt(startPos, startLoc);

        _node2.callee = expr; // Every time a decorator class expression is evaluated, a new empty array is pushed onto the stack
        // So that the decorators of any nested class expressions will be dealt with separately

        this.state.decoratorStack.push([]);
        _node2.arguments = this.parseCallExpressionArguments(types.parenR, false);
        this.state.decoratorStack.pop();
        expr = this.finishNode(_node2, "CallExpression");
        this.toReferencedList(expr.arguments);
      }

      node.expression = expr;
    } else {
      node.expression = this.parseMaybeAssign();
    }

    return this.finishNode(node, "Decorator");
  };

  _proto.parseBreakContinueStatement = function parseBreakContinueStatement(node, keyword) {
    var isBreak = keyword === "break";
    this.next();

    if (this.isLineTerminator()) {
      node.label = null;
    } else if (!this.match(types.name)) {
      this.unexpected();
    } else {
      node.label = this.parseIdentifier();
      this.semicolon();
    } // Verify that there is an actual destination to break or
    // continue to.


    var i;

    for (i = 0; i < this.state.labels.length; ++i) {
      var lab = this.state.labels[i];

      if (node.label == null || lab.name === node.label.name) {
        if (lab.kind != null && (isBreak || lab.kind === "loop")) break;
        if (node.label && isBreak) break;
      }
    }

    if (i === this.state.labels.length) {
      this.raise(node.start, "Unsyntactic " + keyword);
    }

    return this.finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement");
  };

  _proto.parseDebuggerStatement = function parseDebuggerStatement(node) {
    this.next();
    this.semicolon();
    return this.finishNode(node, "DebuggerStatement");
  };

  _proto.parseDoStatement = function parseDoStatement(node) {
    this.next();
    this.state.labels.push(loopLabel);
    node.body = this.parseStatement(false);
    this.state.labels.pop();
    this.expect(types._while);
    node.test = this.parseParenExpression();
    this.eat(types.semi);
    return this.finishNode(node, "DoWhileStatement");
  }; // Disambiguating between a `for` and a `for`/`in` or `for`/`of`
  // loop is non-trivial. Basically, we have to parse the init `var`
  // statement or expression, disallowing the `in` operator (see
  // the second parameter to `parseExpression`), and then check
  // whether the next token is `in` or `of`. When there is no init
  // part (semicolon immediately after the opening parenthesis), it
  // is a regular `for` loop.


  _proto.parseForStatement = function parseForStatement(node) {
    this.next();
    this.state.labels.push(loopLabel);
    var forAwait = false;

    if (this.state.inAsync && this.isContextual("await")) {
      this.expectPlugin("asyncGenerators");
      forAwait = true;
      this.next();
    }

    this.expect(types.parenL);

    if (this.match(types.semi)) {
      if (forAwait) {
        this.unexpected();
      }

      return this.parseFor(node, null);
    }

    if (this.match(types._var) || this.match(types._let) || this.match(types._const)) {
      var _init = this.startNode();

      var varKind = this.state.type;
      this.next();
      this.parseVar(_init, true, varKind);
      this.finishNode(_init, "VariableDeclaration");

      if (this.match(types._in) || this.isContextual("of")) {
        if (_init.declarations.length === 1 && !_init.declarations[0].init) {
          return this.parseForIn(node, _init, forAwait);
        }
      }

      if (forAwait) {
        this.unexpected();
      }

      return this.parseFor(node, _init);
    }

    var refShorthandDefaultPos = {
      start: 0
    };
    var init = this.parseExpression(true, refShorthandDefaultPos);

    if (this.match(types._in) || this.isContextual("of")) {
      var description = this.isContextual("of") ? "for-of statement" : "for-in statement";
      this.toAssignable(init, undefined, description);
      this.checkLVal(init, undefined, undefined, description);
      return this.parseForIn(node, init, forAwait);
    } else if (refShorthandDefaultPos.start) {
      this.unexpected(refShorthandDefaultPos.start);
    }

    if (forAwait) {
      this.unexpected();
    }

    return this.parseFor(node, init);
  };

  _proto.parseFunctionStatement = function parseFunctionStatement(node) {
    this.next();
    return this.parseFunction(node, true);
  };

  _proto.parseIfStatement = function parseIfStatement(node) {
    this.next();
    node.test = this.parseParenExpression();
    node.consequent = this.parseStatement(false);
    node.alternate = this.eat(types._else) ? this.parseStatement(false) : null;
    return this.finishNode(node, "IfStatement");
  };

  _proto.parseReturnStatement = function parseReturnStatement(node) {
    if (!this.state.inFunction && !this.options.allowReturnOutsideFunction) {
      this.raise(this.state.start, "'return' outside of function");
    }

    this.next(); // In `return` (and `break`/`continue`), the keywords with
    // optional arguments, we eagerly look for a semicolon or the
    // possibility to insert one.

    if (this.isLineTerminator()) {
      node.argument = null;
    } else {
      node.argument = this.parseExpression();
      this.semicolon();
    }

    return this.finishNode(node, "ReturnStatement");
  };

  _proto.parseSwitchStatement = function parseSwitchStatement(node) {
    this.next();
    node.discriminant = this.parseParenExpression();
    var cases = node.cases = [];
    this.expect(types.braceL);
    this.state.labels.push(switchLabel); // Statements under must be grouped (by label) in SwitchCase
    // nodes. `cur` is used to keep the node that we are currently
    // adding statements to.

    var cur;

    for (var sawDefault; !this.match(types.braceR);) {
      if (this.match(types._case) || this.match(types._default)) {
        var isCase = this.match(types._case);
        if (cur) this.finishNode(cur, "SwitchCase");
        cases.push(cur = this.startNode());
        cur.consequent = [];
        this.next();

        if (isCase) {
          cur.test = this.parseExpression();
        } else {
          if (sawDefault) {
            this.raise(this.state.lastTokStart, "Multiple default clauses");
          }

          sawDefault = true;
          cur.test = null;
        }

        this.expect(types.colon);
      } else {
        if (cur) {
          cur.consequent.push(this.parseStatement(true));
        } else {
          this.unexpected();
        }
      }
    }

    if (cur) this.finishNode(cur, "SwitchCase");
    this.next(); // Closing brace

    this.state.labels.pop();
    return this.finishNode(node, "SwitchStatement");
  };

  _proto.parseThrowStatement = function parseThrowStatement(node) {
    this.next();

    if (lineBreak.test(this.input.slice(this.state.lastTokEnd, this.state.start))) {
      this.raise(this.state.lastTokEnd, "Illegal newline after throw");
    }

    node.argument = this.parseExpression();
    this.semicolon();
    return this.finishNode(node, "ThrowStatement");
  };

  _proto.parseTryStatement = function parseTryStatement(node) {
    this.next();
    node.block = this.parseBlock();
    node.handler = null;

    if (this.match(types._catch)) {
      var clause = this.startNode();
      this.next();

      if (this.match(types.parenL)) {
        this.expect(types.parenL);
        clause.param = this.parseBindingAtom();
        var clashes = Object.create(null);
        this.checkLVal(clause.param, true, clashes, "catch clause");
        this.expect(types.parenR);
      } else {
        this.expectPlugin("optionalCatchBinding");
        clause.param = null;
      }

      clause.body = this.parseBlock();
      node.handler = this.finishNode(clause, "CatchClause");
    }

    node.guardedHandlers = empty;
    node.finalizer = this.eat(types._finally) ? this.parseBlock() : null;

    if (!node.handler && !node.finalizer) {
      this.raise(node.start, "Missing catch or finally clause");
    }

    return this.finishNode(node, "TryStatement");
  };

  _proto.parseVarStatement = function parseVarStatement(node, kind) {
    this.next();
    this.parseVar(node, false, kind);
    this.semicolon();
    return this.finishNode(node, "VariableDeclaration");
  };

  _proto.parseWhileStatement = function parseWhileStatement(node) {
    this.next();
    node.test = this.parseParenExpression();
    this.state.labels.push(loopLabel);
    node.body = this.parseStatement(false);
    this.state.labels.pop();
    return this.finishNode(node, "WhileStatement");
  };

  _proto.parseWithStatement = function parseWithStatement(node) {
    if (this.state.strict) {
      this.raise(this.state.start, "'with' in strict mode");
    }

    this.next();
    node.object = this.parseParenExpression();
    node.body = this.parseStatement(false);
    return this.finishNode(node, "WithStatement");
  };

  _proto.parseEmptyStatement = function parseEmptyStatement(node) {
    this.next();
    return this.finishNode(node, "EmptyStatement");
  };

  _proto.parseLabeledStatement = function parseLabeledStatement(node, maybeName, expr) {
    for (var _i2 = 0, _state$labels2 = this.state.labels; _i2 < _state$labels2.length; _i2++) {
      var label = _state$labels2[_i2];

      if (label.name === maybeName) {
        this.raise(expr.start, `Label '${maybeName}' is already declared`);
      }
    }

    var kind = this.state.type.isLoop ? "loop" : this.match(types._switch) ? "switch" : null;

    for (var i = this.state.labels.length - 1; i >= 0; i--) {
      var _label = this.state.labels[i];

      if (_label.statementStart === node.start) {
        _label.statementStart = this.state.start;
        _label.kind = kind;
      } else {
        break;
      }
    }

    this.state.labels.push({
      name: maybeName,
      kind: kind,
      statementStart: this.state.start
    });
    node.body = this.parseStatement(true);

    if (node.body.type == "ClassDeclaration" || node.body.type == "VariableDeclaration" && node.body.kind !== "var" || node.body.type == "FunctionDeclaration" && (this.state.strict || node.body.generator || node.body.async)) {
      this.raise(node.body.start, "Invalid labeled declaration");
    }

    this.state.labels.pop();
    node.label = expr;
    return this.finishNode(node, "LabeledStatement");
  };

  _proto.parseExpressionStatement = function parseExpressionStatement(node, expr) {
    node.expression = expr;
    this.semicolon();
    return this.finishNode(node, "ExpressionStatement");
  }; // Parse a semicolon-enclosed block of statements, handling `"use
  // strict"` declarations when `allowStrict` is true (used for
  // function bodies).


  _proto.parseBlock = function parseBlock(allowDirectives) {
    var node = this.startNode();
    this.expect(types.braceL);
    this.parseBlockBody(node, allowDirectives, false, types.braceR);
    return this.finishNode(node, "BlockStatement");
  };

  _proto.isValidDirective = function isValidDirective(stmt) {
    return stmt.type === "ExpressionStatement" && stmt.expression.type === "StringLiteral" && !stmt.expression.extra.parenthesized;
  };

  _proto.parseBlockBody = function parseBlockBody(node, allowDirectives, topLevel, end) {
    var body = node.body = [];
    var directives = node.directives = [];
    this.parseBlockOrModuleBlockBody(body, allowDirectives ? directives : undefined, topLevel, end);
  }; // Undefined directives means that directives are not allowed.


  _proto.parseBlockOrModuleBlockBody = function parseBlockOrModuleBlockBody(body, directives, topLevel, end) {
    var parsedNonDirective = false;
    var oldStrict;
    var octalPosition;

    while (!this.eat(end)) {
      if (!parsedNonDirective && this.state.containsOctal && !octalPosition) {
        octalPosition = this.state.octalPosition;
      }

      var stmt = this.parseStatement(true, topLevel);

      if (directives && !parsedNonDirective && this.isValidDirective(stmt)) {
        var directive = this.stmtToDirective(stmt);
        directives.push(directive);

        if (oldStrict === undefined && directive.value.value === "use strict") {
          oldStrict = this.state.strict;
          this.setStrict(true);

          if (octalPosition) {
            this.raise(octalPosition, "Octal literal in strict mode");
          }
        }

        continue;
      }

      parsedNonDirective = true;
      body.push(stmt);
    }

    if (oldStrict === false) {
      this.setStrict(false);
    }
  }; // Parse a regular `for` loop. The disambiguation code in
  // `parseStatement` will already have parsed the init statement or
  // expression.


  _proto.parseFor = function parseFor(node, init) {
    node.init = init;
    this.expect(types.semi);
    node.test = this.match(types.semi) ? null : this.parseExpression();
    this.expect(types.semi);
    node.update = this.match(types.parenR) ? null : this.parseExpression();
    this.expect(types.parenR);
    node.body = this.parseStatement(false);
    this.state.labels.pop();
    return this.finishNode(node, "ForStatement");
  }; // Parse a `for`/`in` and `for`/`of` loop, which are almost
  // same from parser's perspective.


  _proto.parseForIn = function parseForIn(node, init, forAwait) {
    var type = this.match(types._in) ? "ForInStatement" : "ForOfStatement";

    if (forAwait) {
      this.eatContextual("of");
    } else {
      this.next();
    }

    if (type === "ForOfStatement") {
      node.await = !!forAwait;
    }

    node.left = init;
    node.right = this.parseExpression();
    this.expect(types.parenR);
    node.body = this.parseStatement(false);
    this.state.labels.pop();
    return this.finishNode(node, type);
  }; // Parse a list of variable declarations.


  _proto.parseVar = function parseVar(node, isFor, kind) {
    var declarations = node.declarations = []; // $FlowFixMe

    node.kind = kind.keyword;

    for (;;) {
      var decl = this.startNode();
      this.parseVarHead(decl);

      if (this.eat(types.eq)) {
        decl.init = this.parseMaybeAssign(isFor);
      } else {
        if (kind === types._const && !(this.match(types._in) || this.isContextual("of"))) {
          // `const` with no initializer is allowed in TypeScript. It could be a declaration `const x: number;`.
          if (!this.hasPlugin("typescript")) {
            this.unexpected();
          }
        } else if (decl.id.type !== "Identifier" && !(isFor && (this.match(types._in) || this.isContextual("of")))) {
          this.raise(this.state.lastTokEnd, "Complex binding patterns require an initialization value");
        }

        decl.init = null;
      }

      declarations.push(this.finishNode(decl, "VariableDeclarator"));
      if (!this.eat(types.comma)) break;
    }

    return node;
  };

  _proto.parseVarHead = function parseVarHead(decl) {
    decl.id = this.parseBindingAtom();
    this.checkLVal(decl.id, true, undefined, "variable declaration");
  }; // Parse a function declaration or literal (depending on the
  // `isStatement` parameter).


  _proto.parseFunction = function parseFunction(node, isStatement, allowExpressionBody, isAsync, optionalId) {
    var oldInFunc = this.state.inFunction;
    var oldInMethod = this.state.inMethod;
    var oldInGenerator = this.state.inGenerator;
    this.state.inFunction = true;
    this.state.inMethod = false;
    this.initFunction(node, isAsync);

    if (this.match(types.star)) {
      if (node.async) {
        this.expectPlugin("asyncGenerators");
      }

      node.generator = true;
      this.next();
    }

    if (isStatement && !optionalId && !this.match(types.name) && !this.match(types._yield)) {
      this.unexpected();
    } // When parsing function expression, the binding identifier is parsed
    // according to the rules inside the function.
    // e.g. (function* yield() {}) is invalid because "yield" is disallowed in
    // generators.
    // This isn't the case with function declarations: function* yield() {} is
    // valid because yield is parsed as if it was outside the generator.
    // Therefore, this.state.inGenerator is set before or after parsing the
    // function id according to the "isStatement" parameter.


    if (!isStatement) this.state.inGenerator = node.generator;

    if (this.match(types.name) || this.match(types._yield)) {
      node.id = this.parseBindingIdentifier();
    }

    if (isStatement) this.state.inGenerator = node.generator;
    this.parseFunctionParams(node);
    this.parseFunctionBodyAndFinish(node, isStatement ? "FunctionDeclaration" : "FunctionExpression", allowExpressionBody);
    this.state.inFunction = oldInFunc;
    this.state.inMethod = oldInMethod;
    this.state.inGenerator = oldInGenerator;
    return node;
  };

  _proto.parseFunctionParams = function parseFunctionParams(node, allowModifiers) {
    var oldInParameters = this.state.inParameters;
    this.state.inParameters = true;
    this.expect(types.parenL);
    node.params = this.parseBindingList(types.parenR,
    /* allowEmpty */
    false, allowModifiers);
    this.state.inParameters = oldInParameters;
  }; // Parse a class declaration or literal (depending on the
  // `isStatement` parameter).


  _proto.parseClass = function parseClass(node, isStatement, optionalId) {
    this.next();
    this.takeDecorators(node);
    this.parseClassId(node, isStatement, optionalId);
    this.parseClassSuper(node);
    this.parseClassBody(node);
    return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression");
  };

  _proto.isClassProperty = function isClassProperty() {
    return this.match(types.eq) || this.match(types.semi) || this.match(types.braceR);
  };

  _proto.isClassMethod = function isClassMethod() {
    return this.match(types.parenL);
  };

  _proto.isNonstaticConstructor = function isNonstaticConstructor(method) {
    return !method.computed && !method.static && (method.key.name === "constructor" || // Identifier
    method.key.value === "constructor") // String literal
    ;
  };

  _proto.parseClassBody = function parseClassBody(node) {
    // class bodies are implicitly strict
    var oldStrict = this.state.strict;
    this.state.strict = true;
    this.state.classLevel++;
    var state = {
      hadConstructor: false
    };
    var decorators = [];
    var classBody = this.startNode();
    classBody.body = [];
    this.expect(types.braceL);

    while (!this.eat(types.braceR)) {
      if (this.eat(types.semi)) {
        if (decorators.length > 0) {
          this.raise(this.state.lastTokEnd, "Decorators must not be followed by a semicolon");
        }

        continue;
      }

      if (this.match(types.at)) {
        decorators.push(this.parseDecorator());
        continue;
      }

      var member = this.startNode(); // steal the decorators if there are any

      if (decorators.length) {
        member.decorators = decorators;
        this.resetStartLocationFromNode(member, decorators[0]);
        decorators = [];
      }

      this.parseClassMember(classBody, member, state);

      if (this.hasPlugin("decorators2") && ["method", "get", "set"].indexOf(member.kind) === -1 && member.decorators && member.decorators.length > 0) {
        this.raise(member.start, "Stage 2 decorators may only be used with a class or a class method");
      }
    }

    if (decorators.length) {
      this.raise(this.state.start, "You have trailing decorators with no method");
    }

    node.body = this.finishNode(classBody, "ClassBody");
    this.state.classLevel--;
    this.state.strict = oldStrict;
  };

  _proto.parseClassMember = function parseClassMember(classBody, member, state) {
    var isStatic = false;

    if (this.match(types.name) && this.state.value === "static") {
      var key = this.parseIdentifier(true); // eats 'static'

      if (this.isClassMethod()) {
        var method = member; // a method named 'static'

        method.kind = "method";
        method.computed = false;
        method.key = key;
        method.static = false;
        this.pushClassMethod(classBody, method, false, false,
        /* isConstructor */
        false);
        return;
      } else if (this.isClassProperty()) {
        var prop = member; // a property named 'static'

        prop.computed = false;
        prop.key = key;
        prop.static = false;
        classBody.body.push(this.parseClassProperty(prop));
        return;
      } // otherwise something static


      isStatic = true;
    }

    this.parseClassMemberWithIsStatic(classBody, member, state, isStatic);
  };

  _proto.parseClassMemberWithIsStatic = function parseClassMemberWithIsStatic(classBody, member, state, isStatic) {
    var publicMethod = member;
    var privateMethod = member;
    var publicProp = member;
    var privateProp = member;
    var method = publicMethod;
    var publicMember = publicMethod;
    member.static = isStatic;

    if (this.eat(types.star)) {
      // a generator
      method.kind = "method";
      this.parseClassPropertyName(method);

      if (method.key.type === "PrivateName") {
        // Private generator method
        this.pushClassPrivateMethod(classBody, privateMethod, true, false);
        return;
      }

      if (this.isNonstaticConstructor(publicMethod)) {
        this.raise(publicMethod.key.start, "Constructor can't be a generator");
      }

      this.pushClassMethod(classBody, publicMethod, true, false,
      /* isConstructor */
      false);
      return;
    }

    var key = this.parseClassPropertyName(member);
    var isPrivate = key.type === "PrivateName"; // Check the key is not a computed expression or string literal.

    var isSimple = key.type === "Identifier";
    this.parsePostMemberNameModifiers(publicMember);

    if (this.isClassMethod()) {
      method.kind = "method";

      if (isPrivate) {
        this.pushClassPrivateMethod(classBody, privateMethod, false, false);
        return;
      } // a normal method


      var isConstructor = this.isNonstaticConstructor(publicMethod);

      if (isConstructor) {
        publicMethod.kind = "constructor";

        if (publicMethod.decorators) {
          this.raise(publicMethod.start, "You can't attach decorators to a class constructor");
        } // TypeScript allows multiple overloaded constructor declarations.


        if (state.hadConstructor && !this.hasPlugin("typescript")) {
          this.raise(key.start, "Duplicate constructor in the same class");
        }

        state.hadConstructor = true;
      }

      this.pushClassMethod(classBody, publicMethod, false, false, isConstructor);
    } else if (this.isClassProperty()) {
      if (isPrivate) {
        this.pushClassPrivateProperty(classBody, privateProp);
      } else {
        this.pushClassProperty(classBody, publicProp);
      }
    } else if (isSimple && key.name === "async" && !this.isLineTerminator()) {
      // an async method
      var isGenerator = this.match(types.star);

      if (isGenerator) {
        this.expectPlugin("asyncGenerators");
        this.next();
      }

      method.kind = "method"; // The so-called parsed name would have been "async": get the real name.

      this.parseClassPropertyName(method);

      if (method.key.type === "PrivateName") {
        // private async method
        this.pushClassPrivateMethod(classBody, privateMethod, isGenerator, true);
      } else {
        if (this.isNonstaticConstructor(publicMethod)) {
          this.raise(publicMethod.key.start, "Constructor can't be an async function");
        }

        this.pushClassMethod(classBody, publicMethod, isGenerator, true,
        /* isConstructor */
        false);
      }
    } else if (isSimple && (key.name === "get" || key.name === "set") && !(this.isLineTerminator() && this.match(types.star))) {
      // `get\n*` is an uninitialized property named 'get' followed by a generator.
      // a getter or setter
      method.kind = key.name; // The so-called parsed name would have been "get/set": get the real name.

      this.parseClassPropertyName(publicMethod);

      if (method.key.type === "PrivateName") {
        // private getter/setter
        this.pushClassPrivateMethod(classBody, privateMethod, false, false);
      } else {
        if (this.isNonstaticConstructor(publicMethod)) {
          this.raise(publicMethod.key.start, "Constructor can't have get/set modifier");
        }

        this.pushClassMethod(classBody, publicMethod, false, false,
        /* isConstructor */
        false);
      }

      this.checkGetterSetterParamCount(publicMethod);
    } else if (this.isLineTerminator()) {
      // an uninitialized class property (due to ASI, since we don't otherwise recognize the next token)
      if (isPrivate) {
        this.pushClassPrivateProperty(classBody, privateProp);
      } else {
        this.pushClassProperty(classBody, publicProp);
      }
    } else {
      this.unexpected();
    }
  };

  _proto.parseClassPropertyName = function parseClassPropertyName(member) {
    var key = this.parsePropertyName(member);

    if (!member.computed && member.static && (key.name === "prototype" || key.value === "prototype")) {
      this.raise(key.start, "Classes may not have static property named prototype");
    }

    if (key.type === "PrivateName" && key.id.name === "constructor") {
      this.raise(key.start, "Classes may not have a private field named '#constructor'");
    }

    return key;
  };

  _proto.pushClassProperty = function pushClassProperty(classBody, prop) {
    // This only affects properties, not methods.
    if (this.isNonstaticConstructor(prop)) {
      this.raise(prop.key.start, "Classes may not have a non-static field named 'constructor'");
    }

    classBody.body.push(this.parseClassProperty(prop));
  };

  _proto.pushClassPrivateProperty = function pushClassPrivateProperty(classBody, prop) {
    this.expectPlugin("classPrivateProperties", prop.key.start);
    classBody.body.push(this.parseClassPrivateProperty(prop));
  };

  _proto.pushClassMethod = function pushClassMethod(classBody, method, isGenerator, isAsync, isConstructor) {
    classBody.body.push(this.parseMethod(method, isGenerator, isAsync, isConstructor, "ClassMethod"));
  };

  _proto.pushClassPrivateMethod = function pushClassPrivateMethod(classBody, method, isGenerator, isAsync) {
    this.expectPlugin("classPrivateMethods", method.key.start);
    classBody.body.push(this.parseMethod(method, isGenerator, isAsync,
    /* isConstructor */
    false, "ClassPrivateMethod"));
  }; // Overridden in typescript.js


  _proto.parsePostMemberNameModifiers = function parsePostMemberNameModifiers( // eslint-disable-next-line no-unused-vars
  methodOrProp) {}; // Overridden in typescript.js


  _proto.parseAccessModifier = function parseAccessModifier() {
    return undefined;
  };

  _proto.parseClassPrivateProperty = function parseClassPrivateProperty(node) {
    this.state.inClassProperty = true;
    node.value = this.eat(types.eq) ? this.parseMaybeAssign() : null;
    this.semicolon();
    this.state.inClassProperty = false;
    return this.finishNode(node, "ClassPrivateProperty");
  };

  _proto.parseClassProperty = function parseClassProperty(node) {
    if (!node.typeAnnotation) {
      this.expectPlugin("classProperties");
    }

    this.state.inClassProperty = true;

    if (this.match(types.eq)) {
      this.expectPlugin("classProperties");
      this.next();
      node.value = this.parseMaybeAssign();
    } else {
      node.value = null;
    }

    this.semicolon();
    this.state.inClassProperty = false;
    return this.finishNode(node, "ClassProperty");
  };

  _proto.parseClassId = function parseClassId(node, isStatement, optionalId) {
    if (this.match(types.name)) {
      node.id = this.parseIdentifier();
    } else {
      if (optionalId || !isStatement) {
        node.id = null;
      } else {
        this.unexpected(null, "A class name is required");
      }
    }
  };

  _proto.parseClassSuper = function parseClassSuper(node) {
    node.superClass = this.eat(types._extends) ? this.parseExprSubscripts() : null;
  }; // Parses module export declaration.
  // TODO: better type. Node is an N.AnyExport.


  _proto.parseExport = function parseExport(node) {
    // export * from '...'
    if (this.shouldParseExportStar()) {
      this.parseExportStar(node);
      if (node.type === "ExportAllDeclaration") return node;
    } else if (this.isExportDefaultSpecifier()) {
      this.expectPlugin("exportDefaultFrom");
      var specifier = this.startNode();
      specifier.exported = this.parseIdentifier(true);
      var specifiers = [this.finishNode(specifier, "ExportDefaultSpecifier")];
      node.specifiers = specifiers;

      if (this.match(types.comma) && this.lookahead().type === types.star) {
        this.expect(types.comma);

        var _specifier = this.startNode();

        this.expect(types.star);
        this.expectContextual("as");
        _specifier.exported = this.parseIdentifier();
        specifiers.push(this.finishNode(_specifier, "ExportNamespaceSpecifier"));
      } else {
        this.parseExportSpecifiersMaybe(node);
      }

      this.parseExportFrom(node, true);
    } else if (this.eat(types._default)) {
      // export default ...
      node.declaration = this.parseExportDefaultExpression();
      this.checkExport(node, true, true);
      return this.finishNode(node, "ExportDefaultDeclaration");
    } else if (this.shouldParseExportDeclaration()) {
      if (this.isContextual("async")) {
        var next = this.lookahead(); // export async;

        if (next.type !== types._function) {
          this.unexpected(next.start, `Unexpected token, expected "function"`);
        }
      }

      node.specifiers = [];
      node.source = null;
      node.declaration = this.parseExportDeclaration(node);
    } else {
      // export { x, y as z } [from '...']
      node.declaration = null;
      node.specifiers = this.parseExportSpecifiers();
      this.parseExportFrom(node);
    }

    this.checkExport(node, true);
    return this.finishNode(node, "ExportNamedDeclaration");
  };

  _proto.parseExportDefaultExpression = function parseExportDefaultExpression() {
    var expr = this.startNode();

    if (this.eat(types._function)) {
      return this.parseFunction(expr, true, false, false, true);
    } else if (this.isContextual("async") && this.lookahead().type === types._function) {
      // async function declaration
      this.eatContextual("async");
      this.eat(types._function);
      return this.parseFunction(expr, true, false, true, true);
    } else if (this.match(types._class)) {
      return this.parseClass(expr, true, true);
    } else if (this.match(types.at)) {
      this.parseDecorators(false);
      return this.parseClass(expr, true, true);
    } else {
      var res = this.parseMaybeAssign();
      this.semicolon();
      return res;
    }
  }; // eslint-disable-next-line no-unused-vars


  _proto.parseExportDeclaration = function parseExportDeclaration(node) {
    return this.parseStatement(true);
  };

  _proto.isExportDefaultSpecifier = function isExportDefaultSpecifier() {
    if (this.match(types.name)) {
      return this.state.value !== "async";
    }

    if (!this.match(types._default)) {
      return false;
    }

    var lookahead = this.lookahead();
    return lookahead.type === types.comma || lookahead.type === types.name && lookahead.value === "from";
  };

  _proto.parseExportSpecifiersMaybe = function parseExportSpecifiersMaybe(node) {
    if (this.eat(types.comma)) {
      node.specifiers = node.specifiers.concat(this.parseExportSpecifiers());
    }
  };

  _proto.parseExportFrom = function parseExportFrom(node, expect) {
    if (this.eatContextual("from")) {
      node.source = this.match(types.string) ? this.parseExprAtom() : this.unexpected();
      this.checkExport(node);
    } else {
      if (expect) {
        this.unexpected();
      } else {
        node.source = null;
      }
    }

    this.semicolon();
  };

  _proto.shouldParseExportStar = function shouldParseExportStar() {
    return this.match(types.star);
  };

  _proto.parseExportStar = function parseExportStar(node) {
    this.expect(types.star);

    if (this.isContextual("as")) {
      this.parseExportNamespace(node);
    } else {
      this.parseExportFrom(node, true);
      this.finishNode(node, "ExportAllDeclaration");
    }
  };

  _proto.parseExportNamespace = function parseExportNamespace(node) {
    this.expectPlugin("exportNamespaceFrom");
    var specifier = this.startNodeAt(this.state.lastTokStart, this.state.lastTokStartLoc);
    this.next();
    specifier.exported = this.parseIdentifier(true);
    node.specifiers = [this.finishNode(specifier, "ExportNamespaceSpecifier")];
    this.parseExportSpecifiersMaybe(node);
    this.parseExportFrom(node, true);
  };

  _proto.shouldParseExportDeclaration = function shouldParseExportDeclaration() {
    return this.state.type.keyword === "var" || this.state.type.keyword === "const" || this.state.type.keyword === "let" || this.state.type.keyword === "function" || this.state.type.keyword === "class" || this.isContextual("async") || this.match(types.at) && this.expectPlugin("decorators2");
  };

  _proto.checkExport = function checkExport(node, checkNames, isDefault) {
    if (checkNames) {
      // Check for duplicate exports
      if (isDefault) {
        // Default exports
        this.checkDuplicateExports(node, "default");
      } else if (node.specifiers && node.specifiers.length) {
        // Named exports
        for (var _i4 = 0, _node$specifiers2 = node.specifiers; _i4 < _node$specifiers2.length; _i4++) {
          var specifier = _node$specifiers2[_i4];
          this.checkDuplicateExports(specifier, specifier.exported.name);
        }
      } else if (node.declaration) {
        // Exported declarations
        if (node.declaration.type === "FunctionDeclaration" || node.declaration.type === "ClassDeclaration") {
          this.checkDuplicateExports(node, node.declaration.id.name);
        } else if (node.declaration.type === "VariableDeclaration") {
          for (var _i6 = 0, _node$declaration$dec2 = node.declaration.declarations; _i6 < _node$declaration$dec2.length; _i6++) {
            var declaration = _node$declaration$dec2[_i6];
            this.checkDeclaration(declaration.id);
          }
        }
      }
    }

    var currentContextDecorators = this.state.decoratorStack[this.state.decoratorStack.length - 1];

    if (currentContextDecorators.length) {
      var isClass = node.declaration && (node.declaration.type === "ClassDeclaration" || node.declaration.type === "ClassExpression");

      if (!node.declaration || !isClass) {
        throw this.raise(node.start, "You can only use decorators on an export when exporting a class");
      }

      this.takeDecorators(node.declaration);
    }
  };

  _proto.checkDeclaration = function checkDeclaration(node) {
    if (node.type === "ObjectPattern") {
      for (var _i8 = 0, _node$properties2 = node.properties; _i8 < _node$properties2.length; _i8++) {
        var prop = _node$properties2[_i8];
        this.checkDeclaration(prop);
      }
    } else if (node.type === "ArrayPattern") {
      for (var _i10 = 0, _node$elements2 = node.elements; _i10 < _node$elements2.length; _i10++) {
        var elem = _node$elements2[_i10];

        if (elem) {
          this.checkDeclaration(elem);
        }
      }
    } else if (node.type === "ObjectProperty") {
      this.checkDeclaration(node.value);
    } else if (node.type === "RestElement") {
      this.checkDeclaration(node.argument);
    } else if (node.type === "Identifier") {
      this.checkDuplicateExports(node, node.name);
    }
  };

  _proto.checkDuplicateExports = function checkDuplicateExports(node, name) {
    if (this.state.exportedIdentifiers.indexOf(name) > -1) {
      this.raiseDuplicateExportError(node, name);
    }

    this.state.exportedIdentifiers.push(name);
  };

  _proto.raiseDuplicateExportError = function raiseDuplicateExportError(node, name) {
    throw this.raise(node.start, name === "default" ? "Only one default export allowed per module." : `\`${name}\` has already been exported. Exported identifiers must be unique.`);
  }; // Parses a comma-separated list of module exports.


  _proto.parseExportSpecifiers = function parseExportSpecifiers() {
    var nodes = [];
    var first = true;
    var needsFrom; // export { x, y as z } [from '...']

    this.expect(types.braceL);

    while (!this.eat(types.braceR)) {
      if (first) {
        first = false;
      } else {
        this.expect(types.comma);
        if (this.eat(types.braceR)) break;
      }

      var isDefault = this.match(types._default);
      if (isDefault && !needsFrom) needsFrom = true;
      var node = this.startNode();
      node.local = this.parseIdentifier(isDefault);
      node.exported = this.eatContextual("as") ? this.parseIdentifier(true) : node.local.__clone();
      nodes.push(this.finishNode(node, "ExportSpecifier"));
    } // https://github.com/ember-cli/ember-cli/pull/3739


    if (needsFrom && !this.isContextual("from")) {
      this.unexpected();
    }

    return nodes;
  }; // Parses import declaration.


  _proto.parseImport = function parseImport(node) {
    // import '...'
    if (this.match(types.string)) {
      node.specifiers = [];
      node.source = this.parseExprAtom();
    } else {
      node.specifiers = [];
      this.parseImportSpecifiers(node);
      this.expectContextual("from");
      node.source = this.match(types.string) ? this.parseExprAtom() : this.unexpected();
    }

    this.semicolon();
    return this.finishNode(node, "ImportDeclaration");
  }; // eslint-disable-next-line no-unused-vars


  _proto.shouldParseDefaultImport = function shouldParseDefaultImport(node) {
    return this.match(types.name);
  };

  _proto.parseImportSpecifierLocal = function parseImportSpecifierLocal(node, specifier, type, contextDescription) {
    specifier.local = this.parseIdentifier();
    this.checkLVal(specifier.local, true, undefined, contextDescription);
    node.specifiers.push(this.finishNode(specifier, type));
  }; // Parses a comma-separated list of module imports.


  _proto.parseImportSpecifiers = function parseImportSpecifiers(node) {
    var first = true;

    if (this.shouldParseDefaultImport(node)) {
      // import defaultObj, { x, y as z } from '...'
      this.parseImportSpecifierLocal(node, this.startNode(), "ImportDefaultSpecifier", "default import specifier");
      if (!this.eat(types.comma)) return;
    }

    if (this.match(types.star)) {
      var specifier = this.startNode();
      this.next();
      this.expectContextual("as");
      this.parseImportSpecifierLocal(node, specifier, "ImportNamespaceSpecifier", "import namespace specifier");
      return;
    }

    this.expect(types.braceL);

    while (!this.eat(types.braceR)) {
      if (first) {
        first = false;
      } else {
        // Detect an attempt to deep destructure
        if (this.eat(types.colon)) {
          this.unexpected(null, "ES2015 named imports do not destructure. Use another statement for destructuring after the import.");
        }

        this.expect(types.comma);
        if (this.eat(types.braceR)) break;
      }

      this.parseImportSpecifier(node);
    }
  };

  _proto.parseImportSpecifier = function parseImportSpecifier(node) {
    var specifier = this.startNode();
    specifier.imported = this.parseIdentifier(true);

    if (this.eatContextual("as")) {
      specifier.local = this.parseIdentifier();
    } else {
      this.checkReservedWord(specifier.imported.name, specifier.start, true, true);
      specifier.local = specifier.imported.__clone();
    }

    this.checkLVal(specifier.local, true, undefined, "import specifier");
    node.specifiers.push(this.finishNode(specifier, "ImportSpecifier"));
  };

  return StatementParser;
}(ExpressionParser);

var plugins = {};

var Parser =
/*#__PURE__*/
function (_StatementParser) {
  _inheritsLoose(Parser, _StatementParser);

  function Parser(options, input) {
    var _this;

    options = getOptions(options);
    _this = _StatementParser.call(this, options, input) || this;
    _this.options = options;
    _this.inModule = _this.options.sourceType === "module";
    _this.input = input;
    _this.plugins = pluginsMap(_this.options.plugins);
    _this.filename = options.sourceFilename; // If enabled, skip leading hashbang line.

    if (_this.state.pos === 0 && _this.input[0] === "#" && _this.input[1] === "!") {
      _this.skipLineComment(2);
    }

    return _this;
  }

  var _proto = Parser.prototype;

  _proto.parse = function parse() {
    var file = this.startNode();
    var program = this.startNode();
    this.nextToken();
    return this.parseTopLevel(file, program);
  };

  return Parser;
}(StatementParser);

function pluginsMap(pluginList) {
  var pluginMap = {};

  for (var _i2 = 0; _i2 < pluginList.length; _i2++) {
    var _name = pluginList[_i2];
    pluginMap[_name] = true;
  }

  return pluginMap;
}

function isSimpleProperty(node) {
  return node != null && node.type === "Property" && node.kind === "init" && node.method === false;
}

var estreePlugin = (function (superClass) {
  return (
    /*#__PURE__*/
    function (_superClass) {
      _inheritsLoose(_class, _superClass);

      function _class() {
        return _superClass.apply(this, arguments) || this;
      }

      var _proto = _class.prototype;

      _proto.estreeParseRegExpLiteral = function estreeParseRegExpLiteral(_ref) {
        var pattern = _ref.pattern,
            flags = _ref.flags;
        var regex = null;

        try {
          regex = new RegExp(pattern, flags);
        } catch (e) {// In environments that don't support these flags value will
          // be null as the regex can't be represented natively.
        }

        var node = this.estreeParseLiteral(regex);
        node.regex = {
          pattern,
          flags
        };
        return node;
      };

      _proto.estreeParseLiteral = function estreeParseLiteral(value) {
        return this.parseLiteral(value, "Literal");
      };

      _proto.directiveToStmt = function directiveToStmt(directive) {
        var directiveLiteral = directive.value;
        var stmt = this.startNodeAt(directive.start, directive.loc.start);
        var expression = this.startNodeAt(directiveLiteral.start, directiveLiteral.loc.start);
        expression.value = directiveLiteral.value;
        expression.raw = directiveLiteral.extra.raw;
        stmt.expression = this.finishNodeAt(expression, "Literal", directiveLiteral.end, directiveLiteral.loc.end);
        stmt.directive = directiveLiteral.extra.raw.slice(1, -1);
        return this.finishNodeAt(stmt, "ExpressionStatement", directive.end, directive.loc.end);
      }; // ==================================
      // Overrides
      // ==================================


      _proto.initFunction = function initFunction(node, isAsync) {
        _superClass.prototype.initFunction.call(this, node, isAsync);

        node.expression = false;
      };

      _proto.checkDeclaration = function checkDeclaration(node) {
        if (isSimpleProperty(node)) {
          this.checkDeclaration(node.value);
        } else {
          _superClass.prototype.checkDeclaration.call(this, node);
        }
      };

      _proto.checkGetterSetterParamCount = function checkGetterSetterParamCount(prop) {
        var paramCount = prop.kind === "get" ? 0 : 1; // $FlowFixMe (prop.value present for ObjectMethod, but for ClassMethod should use prop.params?)

        if (prop.value.params.length !== paramCount) {
          var start = prop.start;

          if (prop.kind === "get") {
            this.raise(start, "getter should have no params");
          } else {
            this.raise(start, "setter should have exactly one param");
          }
        }
      };

      _proto.checkLVal = function checkLVal(expr, isBinding, checkClashes, contextDescription) {
        var _this = this;

        switch (expr.type) {
          case "ObjectPattern":
            expr.properties.forEach(function (prop) {
              _this.checkLVal(prop.type === "Property" ? prop.value : prop, isBinding, checkClashes, "object destructuring pattern");
            });
            break;

          default:
            _superClass.prototype.checkLVal.call(this, expr, isBinding, checkClashes, contextDescription);

        }
      };

      _proto.checkPropClash = function checkPropClash(prop, propHash) {
        if (prop.computed || !isSimpleProperty(prop)) return;
        var key = prop.key; // It is either an Identifier or a String/NumericLiteral

        var name = key.type === "Identifier" ? key.name : String(key.value);

        if (name === "__proto__") {
          if (propHash.proto) {
            this.raise(key.start, "Redefinition of __proto__ property");
          }

          propHash.proto = true;
        }
      };

      _proto.isStrictBody = function isStrictBody(node) {
        var isBlockStatement = node.body.type === "BlockStatement";

        if (isBlockStatement && node.body.body.length > 0) {
          for (var _i2 = 0, _node$body$body2 = node.body.body; _i2 < _node$body$body2.length; _i2++) {
            var directive = _node$body$body2[_i2];

            if (directive.type === "ExpressionStatement" && directive.expression.type === "Literal") {
              if (directive.expression.value === "use strict") return true;
            } else {
              // Break for the first non literal expression
              break;
            }
          }
        }

        return false;
      };

      _proto.isValidDirective = function isValidDirective(stmt) {
        return stmt.type === "ExpressionStatement" && stmt.expression.type === "Literal" && typeof stmt.expression.value === "string" && (!stmt.expression.extra || !stmt.expression.extra.parenthesized);
      };

      _proto.stmtToDirective = function stmtToDirective(stmt) {
        var directive = _superClass.prototype.stmtToDirective.call(this, stmt);

        var value = stmt.expression.value; // Reset value to the actual value as in estree mode we want
        // the stmt to have the real value and not the raw value

        directive.value.value = value;
        return directive;
      };

      _proto.parseBlockBody = function parseBlockBody(node, allowDirectives, topLevel, end) {
        var _this2 = this;

        _superClass.prototype.parseBlockBody.call(this, node, allowDirectives, topLevel, end);

        var directiveStatements = node.directives.map(function (d) {
          return _this2.directiveToStmt(d);
        });
        node.body = directiveStatements.concat(node.body);
        delete node.directives;
      };

      _proto.pushClassMethod = function pushClassMethod(classBody, method, isGenerator, isAsync, isConstructor) {
        this.parseMethod(method, isGenerator, isAsync, isConstructor, "MethodDefinition");

        if (method.typeParameters) {
          // $FlowIgnore
          method.value.typeParameters = method.typeParameters;
          delete method.typeParameters;
        }

        classBody.body.push(method);
      };

      _proto.parseExprAtom = function parseExprAtom(refShorthandDefaultPos) {
        switch (this.state.type) {
          case types.regexp:
            return this.estreeParseRegExpLiteral(this.state.value);

          case types.num:
          case types.string:
            return this.estreeParseLiteral(this.state.value);

          case types._null:
            return this.estreeParseLiteral(null);

          case types._true:
            return this.estreeParseLiteral(true);

          case types._false:
            return this.estreeParseLiteral(false);

          default:
            return _superClass.prototype.parseExprAtom.call(this, refShorthandDefaultPos);
        }
      };

      _proto.parseLiteral = function parseLiteral(value, type, startPos, startLoc) {
        var node = _superClass.prototype.parseLiteral.call(this, value, type, startPos, startLoc);

        node.raw = node.extra.raw;
        delete node.extra;
        return node;
      };

      _proto.parseFunctionBody = function parseFunctionBody(node, allowExpression) {
        _superClass.prototype.parseFunctionBody.call(this, node, allowExpression);

        node.expression = node.body.type !== "BlockStatement";
      };

      _proto.parseMethod = function parseMethod(node, isGenerator, isAsync, isConstructor, type) {
        var funcNode = this.startNode();
        funcNode.kind = node.kind; // provide kind, so super method correctly sets state

        funcNode = _superClass.prototype.parseMethod.call(this, funcNode, isGenerator, isAsync, isConstructor, "FunctionExpression");
        delete funcNode.kind; // $FlowIgnore

        node.value = funcNode;
        return this.finishNode(node, type);
      };

      _proto.parseObjectMethod = function parseObjectMethod(prop, isGenerator, isAsync, isPattern) {
        var node = _superClass.prototype.parseObjectMethod.call(this, prop, isGenerator, isAsync, isPattern);

        if (node) {
          node.type = "Property";
          if (node.kind === "method") node.kind = "init";
          node.shorthand = false;
        }

        return node;
      };

      _proto.parseObjectProperty = function parseObjectProperty(prop, startPos, startLoc, isPattern, refShorthandDefaultPos) {
        var node = _superClass.prototype.parseObjectProperty.call(this, prop, startPos, startLoc, isPattern, refShorthandDefaultPos);

        if (node) {
          node.kind = "init";
          node.type = "Property";
        }

        return node;
      };

      _proto.toAssignable = function toAssignable(node, isBinding, contextDescription) {
        if (isSimpleProperty(node)) {
          this.toAssignable(node.value, isBinding, contextDescription);
          return node;
        }

        return _superClass.prototype.toAssignable.call(this, node, isBinding, contextDescription);
      };

      _proto.toAssignableObjectExpressionProp = function toAssignableObjectExpressionProp(prop, isBinding, isLast) {
        if (prop.kind === "get" || prop.kind === "set") {
          this.raise(prop.key.start, "Object pattern can't contain getter or setter");
        } else if (prop.method) {
          this.raise(prop.key.start, "Object pattern can't contain methods");
        } else {
          _superClass.prototype.toAssignableObjectExpressionProp.call(this, prop, isBinding, isLast);
        }
      };

      return _class;
    }(superClass)
  );
});

/* eslint max-len: 0 */
var primitiveTypes = ["any", "bool", "boolean", "empty", "false", "mixed", "null", "number", "static", "string", "true", "typeof", "void"];

function isEsModuleType(bodyElement) {
  return bodyElement.type === "DeclareExportAllDeclaration" || bodyElement.type === "DeclareExportDeclaration" && (!bodyElement.declaration || bodyElement.declaration.type !== "TypeAlias" && bodyElement.declaration.type !== "InterfaceDeclaration");
}

function hasTypeImportKind(node) {
  return node.importKind === "type" || node.importKind === "typeof";
}

function isMaybeDefaultImport(state) {
  return (state.type === types.name || !!state.type.keyword) && state.value !== "from";
}

var exportSuggestions = {
  const: "declare export var",
  let: "declare export var",
  type: "export type",
  interface: "export interface"
}; // Like Array#filter, but returns a tuple [ acceptedElements, discardedElements ]

function partition(list, test) {
  var list1 = [];
  var list2 = [];

  for (var i = 0; i < list.length; i++) {
    (test(list[i], i, list) ? list1 : list2).push(list[i]);
  }

  return [list1, list2];
}

var flowPlugin = (function (superClass) {
  return (
    /*#__PURE__*/
    function (_superClass) {
      _inheritsLoose(_class, _superClass);

      function _class() {
        return _superClass.apply(this, arguments) || this;
      }

      var _proto = _class.prototype;

      _proto.flowParseTypeInitialiser = function flowParseTypeInitialiser(tok) {
        var oldInType = this.state.inType;
        this.state.inType = true;
        this.expect(tok || types.colon);
        var type = this.flowParseType();
        this.state.inType = oldInType;
        return type;
      };

      _proto.flowParsePredicate = function flowParsePredicate() {
        var node = this.startNode();
        var moduloLoc = this.state.startLoc;
        var moduloPos = this.state.start;
        this.expect(types.modulo);
        var checksLoc = this.state.startLoc;
        this.expectContextual("checks"); // Force '%' and 'checks' to be adjacent

        if (moduloLoc.line !== checksLoc.line || moduloLoc.column !== checksLoc.column - 1) {
          this.raise(moduloPos, "Spaces between ´%´ and ´checks´ are not allowed here.");
        }

        if (this.eat(types.parenL)) {
          node.value = this.parseExpression();
          this.expect(types.parenR);
          return this.finishNode(node, "DeclaredPredicate");
        } else {
          return this.finishNode(node, "InferredPredicate");
        }
      };

      _proto.flowParseTypeAndPredicateInitialiser = function flowParseTypeAndPredicateInitialiser() {
        var oldInType = this.state.inType;
        this.state.inType = true;
        this.expect(types.colon);
        var type = null;
        var predicate = null;

        if (this.match(types.modulo)) {
          this.state.inType = oldInType;
          predicate = this.flowParsePredicate();
        } else {
          type = this.flowParseType();
          this.state.inType = oldInType;

          if (this.match(types.modulo)) {
            predicate = this.flowParsePredicate();
          }
        }

        return [type, predicate];
      };

      _proto.flowParseDeclareClass = function flowParseDeclareClass(node) {
        this.next();
        this.flowParseInterfaceish(node,
        /*isClass*/
        true);
        return this.finishNode(node, "DeclareClass");
      };

      _proto.flowParseDeclareFunction = function flowParseDeclareFunction(node) {
        this.next();
        var id = node.id = this.parseIdentifier();
        var typeNode = this.startNode();
        var typeContainer = this.startNode();

        if (this.isRelational("<")) {
          typeNode.typeParameters = this.flowParseTypeParameterDeclaration();
        } else {
          typeNode.typeParameters = null;
        }

        this.expect(types.parenL);
        var tmp = this.flowParseFunctionTypeParams();
        typeNode.params = tmp.params;
        typeNode.rest = tmp.rest;
        this.expect(types.parenR);

        var _flowParseTypeAndPred = this.flowParseTypeAndPredicateInitialiser();

        // $FlowFixMe (destructuring not supported yet)
        typeNode.returnType = _flowParseTypeAndPred[0];
        // $FlowFixMe (destructuring not supported yet)
        node.predicate = _flowParseTypeAndPred[1];
        typeContainer.typeAnnotation = this.finishNode(typeNode, "FunctionTypeAnnotation");
        id.typeAnnotation = this.finishNode(typeContainer, "TypeAnnotation");
        this.finishNode(id, id.type);
        this.semicolon();
        return this.finishNode(node, "DeclareFunction");
      };

      _proto.flowParseDeclare = function flowParseDeclare(node, insideModule) {
        if (this.match(types._class)) {
          return this.flowParseDeclareClass(node);
        } else if (this.match(types._function)) {
          return this.flowParseDeclareFunction(node);
        } else if (this.match(types._var)) {
          return this.flowParseDeclareVariable(node);
        } else if (this.isContextual("module")) {
          if (this.lookahead().type === types.dot) {
            return this.flowParseDeclareModuleExports(node);
          } else {
            if (insideModule) {
              this.unexpected(null, "`declare module` cannot be used inside another `declare module`");
            }

            return this.flowParseDeclareModule(node);
          }
        } else if (this.isContextual("type")) {
          return this.flowParseDeclareTypeAlias(node);
        } else if (this.isContextual("opaque")) {
          return this.flowParseDeclareOpaqueType(node);
        } else if (this.isContextual("interface")) {
          return this.flowParseDeclareInterface(node);
        } else if (this.match(types._export)) {
          return this.flowParseDeclareExportDeclaration(node, insideModule);
        } else {
          throw this.unexpected();
        }
      };

      _proto.flowParseDeclareVariable = function flowParseDeclareVariable(node) {
        this.next();
        node.id = this.flowParseTypeAnnotatableIdentifier(
        /*allowPrimitiveOverride*/
        true);
        this.semicolon();
        return this.finishNode(node, "DeclareVariable");
      };

      _proto.flowParseDeclareModule = function flowParseDeclareModule(node) {
        var _this = this;

        this.next();

        if (this.match(types.string)) {
          node.id = this.parseExprAtom();
        } else {
          node.id = this.parseIdentifier();
        }

        var bodyNode = node.body = this.startNode();
        var body = bodyNode.body = [];
        this.expect(types.braceL);

        while (!this.match(types.braceR)) {
          var _bodyNode = this.startNode();

          if (this.match(types._import)) {
            var lookahead = this.lookahead();

            if (lookahead.value !== "type" && lookahead.value !== "typeof") {
              this.unexpected(null, "Imports within a `declare module` body must always be `import type` or `import typeof`");
            }

            this.next();
            this.parseImport(_bodyNode);
          } else {
            this.expectContextual("declare", "Only declares and type imports are allowed inside declare module");
            _bodyNode = this.flowParseDeclare(_bodyNode, true);
          }

          body.push(_bodyNode);
        }

        this.expect(types.braceR);
        this.finishNode(bodyNode, "BlockStatement");
        var kind = null;
        var hasModuleExport = false;
        var errorMessage = "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";
        body.forEach(function (bodyElement) {
          if (isEsModuleType(bodyElement)) {
            if (kind === "CommonJS") {
              _this.unexpected(bodyElement.start, errorMessage);
            }

            kind = "ES";
          } else if (bodyElement.type === "DeclareModuleExports") {
            if (hasModuleExport) {
              _this.unexpected(bodyElement.start, "Duplicate `declare module.exports` statement");
            }

            if (kind === "ES") _this.unexpected(bodyElement.start, errorMessage);
            kind = "CommonJS";
            hasModuleExport = true;
          }
        });
        node.kind = kind || "CommonJS";
        return this.finishNode(node, "DeclareModule");
      };

      _proto.flowParseDeclareExportDeclaration = function flowParseDeclareExportDeclaration(node, insideModule) {
        this.expect(types._export);

        if (this.eat(types._default)) {
          if (this.match(types._function) || this.match(types._class)) {
            // declare export default class ...
            // declare export default function ...
            node.declaration = this.flowParseDeclare(this.startNode());
          } else {
            // declare export default [type];
            node.declaration = this.flowParseType();
            this.semicolon();
          }

          node.default = true;
          return this.finishNode(node, "DeclareExportDeclaration");
        } else {
          if (this.match(types._const) || this.match(types._let) || (this.isContextual("type") || this.isContextual("interface")) && !insideModule) {
            var label = this.state.value;
            var suggestion = exportSuggestions[label];
            this.unexpected(this.state.start, `\`declare export ${label}\` is not supported. Use \`${suggestion}\` instead`);
          }

          if (this.match(types._var) || // declare export var ...
          this.match(types._function) || // declare export function ...
          this.match(types._class) || // declare export class ...
          this.isContextual("opaque") // declare export opaque ..
          ) {
              node.declaration = this.flowParseDeclare(this.startNode());
              node.default = false;
              return this.finishNode(node, "DeclareExportDeclaration");
            } else if (this.match(types.star) || // declare export * from ''
          this.match(types.braceL) || // declare export {} ...
          this.isContextual("interface") || // declare export interface ...
          this.isContextual("type") || // declare export type ...
          this.isContextual("opaque") // declare export opaque type ...
          ) {
              node = this.parseExport(node);

              if (node.type === "ExportNamedDeclaration") {
                // flow does not support the ExportNamedDeclaration
                // $FlowIgnore
                node.type = "ExportDeclaration"; // $FlowFixMe

                node.default = false;
                delete node.exportKind;
              } // $FlowIgnore


              node.type = "Declare" + node.type;
              return node;
            }
        }

        throw this.unexpected();
      };

      _proto.flowParseDeclareModuleExports = function flowParseDeclareModuleExports(node) {
        this.expectContextual("module");
        this.expect(types.dot);
        this.expectContextual("exports");
        node.typeAnnotation = this.flowParseTypeAnnotation();
        this.semicolon();
        return this.finishNode(node, "DeclareModuleExports");
      };

      _proto.flowParseDeclareTypeAlias = function flowParseDeclareTypeAlias(node) {
        this.next();
        this.flowParseTypeAlias(node);
        return this.finishNode(node, "DeclareTypeAlias");
      };

      _proto.flowParseDeclareOpaqueType = function flowParseDeclareOpaqueType(node) {
        this.next();
        this.flowParseOpaqueType(node, true);
        return this.finishNode(node, "DeclareOpaqueType");
      };

      _proto.flowParseDeclareInterface = function flowParseDeclareInterface(node) {
        this.next();
        this.flowParseInterfaceish(node);
        return this.finishNode(node, "DeclareInterface");
      }; // Interfaces


      _proto.flowParseInterfaceish = function flowParseInterfaceish(node, isClass) {
        node.id = this.flowParseRestrictedIdentifier(
        /*liberal*/
        !isClass);

        if (this.isRelational("<")) {
          node.typeParameters = this.flowParseTypeParameterDeclaration();
        } else {
          node.typeParameters = null;
        }

        node.extends = [];
        node.mixins = [];

        if (this.eat(types._extends)) {
          do {
            node.extends.push(this.flowParseInterfaceExtends());
          } while (!isClass && this.eat(types.comma));
        }

        if (this.isContextual("mixins")) {
          this.next();

          do {
            node.mixins.push(this.flowParseInterfaceExtends());
          } while (this.eat(types.comma));
        }

        node.body = this.flowParseObjectType(true, false, false);
      };

      _proto.flowParseInterfaceExtends = function flowParseInterfaceExtends() {
        var node = this.startNode();
        node.id = this.flowParseQualifiedTypeIdentifier();

        if (this.isRelational("<")) {
          node.typeParameters = this.flowParseTypeParameterInstantiation();
        } else {
          node.typeParameters = null;
        }

        return this.finishNode(node, "InterfaceExtends");
      };

      _proto.flowParseInterface = function flowParseInterface(node) {
        this.flowParseInterfaceish(node);
        return this.finishNode(node, "InterfaceDeclaration");
      };

      _proto.checkReservedType = function checkReservedType(word, startLoc) {
        if (primitiveTypes.indexOf(word) > -1) {
          this.raise(startLoc, `Cannot overwrite primitive type ${word}`);
        }
      };

      _proto.flowParseRestrictedIdentifier = function flowParseRestrictedIdentifier(liberal) {
        this.checkReservedType(this.state.value, this.state.start);
        return this.parseIdentifier(liberal);
      }; // Type aliases


      _proto.flowParseTypeAlias = function flowParseTypeAlias(node) {
        node.id = this.flowParseRestrictedIdentifier();

        if (this.isRelational("<")) {
          node.typeParameters = this.flowParseTypeParameterDeclaration();
        } else {
          node.typeParameters = null;
        }

        node.right = this.flowParseTypeInitialiser(types.eq);
        this.semicolon();
        return this.finishNode(node, "TypeAlias");
      };

      _proto.flowParseOpaqueType = function flowParseOpaqueType(node, declare) {
        this.expectContextual("type");
        node.id = this.flowParseRestrictedIdentifier(
        /*liberal*/
        true);

        if (this.isRelational("<")) {
          node.typeParameters = this.flowParseTypeParameterDeclaration();
        } else {
          node.typeParameters = null;
        } // Parse the supertype


        node.supertype = null;

        if (this.match(types.colon)) {
          node.supertype = this.flowParseTypeInitialiser(types.colon);
        }

        node.impltype = null;

        if (!declare) {
          node.impltype = this.flowParseTypeInitialiser(types.eq);
        }

        this.semicolon();
        return this.finishNode(node, "OpaqueType");
      }; // Type annotations


      _proto.flowParseTypeParameter = function flowParseTypeParameter() {
        var node = this.startNode();
        var variance = this.flowParseVariance();
        var ident = this.flowParseTypeAnnotatableIdentifier();
        node.name = ident.name;
        node.variance = variance;
        node.bound = ident.typeAnnotation;

        if (this.match(types.eq)) {
          this.eat(types.eq);
          node.default = this.flowParseType();
        }

        return this.finishNode(node, "TypeParameter");
      };

      _proto.flowParseTypeParameterDeclaration = function flowParseTypeParameterDeclaration() {
        var oldInType = this.state.inType;
        var node = this.startNode();
        node.params = [];
        this.state.inType = true; // istanbul ignore else: this condition is already checked at all call sites

        if (this.isRelational("<") || this.match(types.jsxTagStart)) {
          this.next();
        } else {
          this.unexpected();
        }

        do {
          node.params.push(this.flowParseTypeParameter());

          if (!this.isRelational(">")) {
            this.expect(types.comma);
          }
        } while (!this.isRelational(">"));

        this.expectRelational(">");
        this.state.inType = oldInType;
        return this.finishNode(node, "TypeParameterDeclaration");
      };

      _proto.flowParseTypeParameterInstantiation = function flowParseTypeParameterInstantiation() {
        var node = this.startNode();
        var oldInType = this.state.inType;
        node.params = [];
        this.state.inType = true;
        this.expectRelational("<");

        while (!this.isRelational(">")) {
          node.params.push(this.flowParseType());

          if (!this.isRelational(">")) {
            this.expect(types.comma);
          }
        }

        this.expectRelational(">");
        this.state.inType = oldInType;
        return this.finishNode(node, "TypeParameterInstantiation");
      };

      _proto.flowParseObjectPropertyKey = function flowParseObjectPropertyKey() {
        return this.match(types.num) || this.match(types.string) ? this.parseExprAtom() : this.parseIdentifier(true);
      };

      _proto.flowParseObjectTypeIndexer = function flowParseObjectTypeIndexer(node, isStatic, variance) {
        node.static = isStatic;
        this.expect(types.bracketL);

        if (this.lookahead().type === types.colon) {
          node.id = this.flowParseObjectPropertyKey();
          node.key = this.flowParseTypeInitialiser();
        } else {
          node.id = null;
          node.key = this.flowParseType();
        }

        this.expect(types.bracketR);
        node.value = this.flowParseTypeInitialiser();
        node.variance = variance;
        return this.finishNode(node, "ObjectTypeIndexer");
      };

      _proto.flowParseObjectTypeMethodish = function flowParseObjectTypeMethodish(node) {
        node.params = [];
        node.rest = null;
        node.typeParameters = null;

        if (this.isRelational("<")) {
          node.typeParameters = this.flowParseTypeParameterDeclaration();
        }

        this.expect(types.parenL);

        while (!this.match(types.parenR) && !this.match(types.ellipsis)) {
          node.params.push(this.flowParseFunctionTypeParam());

          if (!this.match(types.parenR)) {
            this.expect(types.comma);
          }
        }

        if (this.eat(types.ellipsis)) {
          node.rest = this.flowParseFunctionTypeParam();
        }

        this.expect(types.parenR);
        node.returnType = this.flowParseTypeInitialiser();
        return this.finishNode(node, "FunctionTypeAnnotation");
      };

      _proto.flowParseObjectTypeCallProperty = function flowParseObjectTypeCallProperty(node, isStatic) {
        var valueNode = this.startNode();
        node.static = isStatic;
        node.value = this.flowParseObjectTypeMethodish(valueNode);
        return this.finishNode(node, "ObjectTypeCallProperty");
      };

      _proto.flowParseObjectType = function flowParseObjectType(allowStatic, allowExact, allowSpread) {
        var oldInType = this.state.inType;
        this.state.inType = true;
        var nodeStart = this.startNode();
        nodeStart.callProperties = [];
        nodeStart.properties = [];
        nodeStart.indexers = [];
        var endDelim;
        var exact;

        if (allowExact && this.match(types.braceBarL)) {
          this.expect(types.braceBarL);
          endDelim = types.braceBarR;
          exact = true;
        } else {
          this.expect(types.braceL);
          endDelim = types.braceR;
          exact = false;
        }

        nodeStart.exact = exact;

        while (!this.match(endDelim)) {
          var isStatic = false;
          var node = this.startNode();

          if (allowStatic && this.isContextual("static") && this.lookahead().type !== types.colon) {
            this.next();
            isStatic = true;
          }

          var variance = this.flowParseVariance();

          if (this.match(types.bracketL)) {
            nodeStart.indexers.push(this.flowParseObjectTypeIndexer(node, isStatic, variance));
          } else if (this.match(types.parenL) || this.isRelational("<")) {
            if (variance) {
              this.unexpected(variance.start);
            }

            nodeStart.callProperties.push(this.flowParseObjectTypeCallProperty(node, isStatic));
          } else {
            var kind = "init";

            if (this.isContextual("get") || this.isContextual("set")) {
              var lookahead = this.lookahead();

              if (lookahead.type === types.name || lookahead.type === types.string || lookahead.type === types.num) {
                kind = this.state.value;
                this.next();
              }
            }

            nodeStart.properties.push(this.flowParseObjectTypeProperty(node, isStatic, variance, kind, allowSpread));
          }

          this.flowObjectTypeSemicolon();
        }

        this.expect(endDelim);
        var out = this.finishNode(nodeStart, "ObjectTypeAnnotation");
        this.state.inType = oldInType;
        return out;
      };

      _proto.flowParseObjectTypeProperty = function flowParseObjectTypeProperty(node, isStatic, variance, kind, allowSpread) {
        if (this.match(types.ellipsis)) {
          if (!allowSpread) {
            this.unexpected(null, "Spread operator cannot appear in class or interface definitions");
          }

          if (variance) {
            this.unexpected(variance.start, "Spread properties cannot have variance");
          }

          this.expect(types.ellipsis);
          node.argument = this.flowParseType();
          return this.finishNode(node, "ObjectTypeSpreadProperty");
        } else {
          node.key = this.flowParseObjectPropertyKey();
          node.static = isStatic;
          node.kind = kind;
          var optional = false;

          if (this.isRelational("<") || this.match(types.parenL)) {
            // This is a method property
            node.method = true;

            if (variance) {
              this.unexpected(variance.start);
            }

            node.value = this.flowParseObjectTypeMethodish(this.startNodeAt(node.start, node.loc.start));

            if (kind === "get" || kind === "set") {
              this.flowCheckGetterSetterParamCount(node);
            }
          } else {
            if (kind !== "init") this.unexpected();
            node.method = false;

            if (this.eat(types.question)) {
              optional = true;
            }

            node.value = this.flowParseTypeInitialiser();
            node.variance = variance;
          }

          node.optional = optional;
          return this.finishNode(node, "ObjectTypeProperty");
        }
      }; // This is similar to checkGetterSetterParamCount, but as
      // babylon uses non estree properties we cannot reuse it here


      _proto.flowCheckGetterSetterParamCount = function flowCheckGetterSetterParamCount(property) {
        var paramCount = property.kind === "get" ? 0 : 1;

        if (property.value.params.length !== paramCount) {
          var start = property.start;

          if (property.kind === "get") {
            this.raise(start, "getter should have no params");
          } else {
            this.raise(start, "setter should have exactly one param");
          }
        }
      };

      _proto.flowObjectTypeSemicolon = function flowObjectTypeSemicolon() {
        if (!this.eat(types.semi) && !this.eat(types.comma) && !this.match(types.braceR) && !this.match(types.braceBarR)) {
          this.unexpected();
        }
      };

      _proto.flowParseQualifiedTypeIdentifier = function flowParseQualifiedTypeIdentifier(startPos, startLoc, id) {
        startPos = startPos || this.state.start;
        startLoc = startLoc || this.state.startLoc;
        var node = id || this.parseIdentifier();

        while (this.eat(types.dot)) {
          var node2 = this.startNodeAt(startPos, startLoc);
          node2.qualification = node;
          node2.id = this.parseIdentifier();
          node = this.finishNode(node2, "QualifiedTypeIdentifier");
        }

        return node;
      };

      _proto.flowParseGenericType = function flowParseGenericType(startPos, startLoc, id) {
        var node = this.startNodeAt(startPos, startLoc);
        node.typeParameters = null;
        node.id = this.flowParseQualifiedTypeIdentifier(startPos, startLoc, id);

        if (this.isRelational("<")) {
          node.typeParameters = this.flowParseTypeParameterInstantiation();
        }

        return this.finishNode(node, "GenericTypeAnnotation");
      };

      _proto.flowParseTypeofType = function flowParseTypeofType() {
        var node = this.startNode();
        this.expect(types._typeof);
        node.argument = this.flowParsePrimaryType();
        return this.finishNode(node, "TypeofTypeAnnotation");
      };

      _proto.flowParseTupleType = function flowParseTupleType() {
        var node = this.startNode();
        node.types = [];
        this.expect(types.bracketL); // We allow trailing commas

        while (this.state.pos < this.input.length && !this.match(types.bracketR)) {
          node.types.push(this.flowParseType());
          if (this.match(types.bracketR)) break;
          this.expect(types.comma);
        }

        this.expect(types.bracketR);
        return this.finishNode(node, "TupleTypeAnnotation");
      };

      _proto.flowParseFunctionTypeParam = function flowParseFunctionTypeParam() {
        var name = null;
        var optional = false;
        var typeAnnotation = null;
        var node = this.startNode();
        var lh = this.lookahead();

        if (lh.type === types.colon || lh.type === types.question) {
          name = this.parseIdentifier();

          if (this.eat(types.question)) {
            optional = true;
          }

          typeAnnotation = this.flowParseTypeInitialiser();
        } else {
          typeAnnotation = this.flowParseType();
        }

        node.name = name;
        node.optional = optional;
        node.typeAnnotation = typeAnnotation;
        return this.finishNode(node, "FunctionTypeParam");
      };

      _proto.reinterpretTypeAsFunctionTypeParam = function reinterpretTypeAsFunctionTypeParam(type) {
        var node = this.startNodeAt(type.start, type.loc.start);
        node.name = null;
        node.optional = false;
        node.typeAnnotation = type;
        return this.finishNode(node, "FunctionTypeParam");
      };

      _proto.flowParseFunctionTypeParams = function flowParseFunctionTypeParams(params) {
        if (params === void 0) {
          params = [];
        }

        var rest = null;

        while (!this.match(types.parenR) && !this.match(types.ellipsis)) {
          params.push(this.flowParseFunctionTypeParam());

          if (!this.match(types.parenR)) {
            this.expect(types.comma);
          }
        }

        if (this.eat(types.ellipsis)) {
          rest = this.flowParseFunctionTypeParam();
        }

        return {
          params,
          rest
        };
      };

      _proto.flowIdentToTypeAnnotation = function flowIdentToTypeAnnotation(startPos, startLoc, node, id) {
        switch (id.name) {
          case "any":
            return this.finishNode(node, "AnyTypeAnnotation");

          case "void":
            return this.finishNode(node, "VoidTypeAnnotation");

          case "bool":
          case "boolean":
            return this.finishNode(node, "BooleanTypeAnnotation");

          case "mixed":
            return this.finishNode(node, "MixedTypeAnnotation");

          case "empty":
            return this.finishNode(node, "EmptyTypeAnnotation");

          case "number":
            return this.finishNode(node, "NumberTypeAnnotation");

          case "string":
            return this.finishNode(node, "StringTypeAnnotation");

          default:
            return this.flowParseGenericType(startPos, startLoc, id);
        }
      }; // The parsing of types roughly parallels the parsing of expressions, and
      // primary types are kind of like primary expressions...they're the
      // primitives with which other types are constructed.


      _proto.flowParsePrimaryType = function flowParsePrimaryType() {
        var startPos = this.state.start;
        var startLoc = this.state.startLoc;
        var node = this.startNode();
        var tmp;
        var type;
        var isGroupedType = false;
        var oldNoAnonFunctionType = this.state.noAnonFunctionType;

        switch (this.state.type) {
          case types.name:
            return this.flowIdentToTypeAnnotation(startPos, startLoc, node, this.parseIdentifier());

          case types.braceL:
            return this.flowParseObjectType(false, false, true);

          case types.braceBarL:
            return this.flowParseObjectType(false, true, true);

          case types.bracketL:
            return this.flowParseTupleType();

          case types.relational:
            if (this.state.value === "<") {
              node.typeParameters = this.flowParseTypeParameterDeclaration();
              this.expect(types.parenL);
              tmp = this.flowParseFunctionTypeParams();
              node.params = tmp.params;
              node.rest = tmp.rest;
              this.expect(types.parenR);
              this.expect(types.arrow);
              node.returnType = this.flowParseType();
              return this.finishNode(node, "FunctionTypeAnnotation");
            }

            break;

          case types.parenL:
            this.next(); // Check to see if this is actually a grouped type

            if (!this.match(types.parenR) && !this.match(types.ellipsis)) {
              if (this.match(types.name)) {
                var token = this.lookahead().type;
                isGroupedType = token !== types.question && token !== types.colon;
              } else {
                isGroupedType = true;
              }
            }

            if (isGroupedType) {
              this.state.noAnonFunctionType = false;
              type = this.flowParseType();
              this.state.noAnonFunctionType = oldNoAnonFunctionType; // A `,` or a `) =>` means this is an anonymous function type

              if (this.state.noAnonFunctionType || !(this.match(types.comma) || this.match(types.parenR) && this.lookahead().type === types.arrow)) {
                this.expect(types.parenR);
                return type;
              } else {
                // Eat a comma if there is one
                this.eat(types.comma);
              }
            }

            if (type) {
              tmp = this.flowParseFunctionTypeParams([this.reinterpretTypeAsFunctionTypeParam(type)]);
            } else {
              tmp = this.flowParseFunctionTypeParams();
            }

            node.params = tmp.params;
            node.rest = tmp.rest;
            this.expect(types.parenR);
            this.expect(types.arrow);
            node.returnType = this.flowParseType();
            node.typeParameters = null;
            return this.finishNode(node, "FunctionTypeAnnotation");

          case types.string:
            return this.parseLiteral(this.state.value, "StringLiteralTypeAnnotation");

          case types._true:
          case types._false:
            node.value = this.match(types._true);
            this.next();
            return this.finishNode(node, "BooleanLiteralTypeAnnotation");

          case types.plusMin:
            if (this.state.value === "-") {
              this.next();

              if (!this.match(types.num)) {
                this.unexpected(null, `Unexpected token, expected "number"`);
              }

              return this.parseLiteral(-this.state.value, "NumberLiteralTypeAnnotation", node.start, node.loc.start);
            }

            this.unexpected();

          case types.num:
            return this.parseLiteral(this.state.value, "NumberLiteralTypeAnnotation");

          case types._null:
            this.next();
            return this.finishNode(node, "NullLiteralTypeAnnotation");

          case types._this:
            this.next();
            return this.finishNode(node, "ThisTypeAnnotation");

          case types.star:
            this.next();
            return this.finishNode(node, "ExistsTypeAnnotation");

          default:
            if (this.state.type.keyword === "typeof") {
              return this.flowParseTypeofType();
            }

        }

        throw this.unexpected();
      };

      _proto.flowParsePostfixType = function flowParsePostfixType() {
        var startPos = this.state.start,
            startLoc = this.state.startLoc;
        var type = this.flowParsePrimaryType();

        while (!this.canInsertSemicolon() && this.match(types.bracketL)) {
          var node = this.startNodeAt(startPos, startLoc);
          node.elementType = type;
          this.expect(types.bracketL);
          this.expect(types.bracketR);
          type = this.finishNode(node, "ArrayTypeAnnotation");
        }

        return type;
      };

      _proto.flowParsePrefixType = function flowParsePrefixType() {
        var node = this.startNode();

        if (this.eat(types.question)) {
          node.typeAnnotation = this.flowParsePrefixType();
          return this.finishNode(node, "NullableTypeAnnotation");
        } else {
          return this.flowParsePostfixType();
        }
      };

      _proto.flowParseAnonFunctionWithoutParens = function flowParseAnonFunctionWithoutParens() {
        var param = this.flowParsePrefixType();

        if (!this.state.noAnonFunctionType && this.eat(types.arrow)) {
          // TODO: This should be a type error. Passing in a SourceLocation, and it expects a Position.
          var node = this.startNodeAt(param.start, param.loc.start);
          node.params = [this.reinterpretTypeAsFunctionTypeParam(param)];
          node.rest = null;
          node.returnType = this.flowParseType();
          node.typeParameters = null;
          return this.finishNode(node, "FunctionTypeAnnotation");
        }

        return param;
      };

      _proto.flowParseIntersectionType = function flowParseIntersectionType() {
        var node = this.startNode();
        this.eat(types.bitwiseAND);
        var type = this.flowParseAnonFunctionWithoutParens();
        node.types = [type];

        while (this.eat(types.bitwiseAND)) {
          node.types.push(this.flowParseAnonFunctionWithoutParens());
        }

        return node.types.length === 1 ? type : this.finishNode(node, "IntersectionTypeAnnotation");
      };

      _proto.flowParseUnionType = function flowParseUnionType() {
        var node = this.startNode();
        this.eat(types.bitwiseOR);
        var type = this.flowParseIntersectionType();
        node.types = [type];

        while (this.eat(types.bitwiseOR)) {
          node.types.push(this.flowParseIntersectionType());
        }

        return node.types.length === 1 ? type : this.finishNode(node, "UnionTypeAnnotation");
      };

      _proto.flowParseType = function flowParseType() {
        var oldInType = this.state.inType;
        this.state.inType = true;
        var type = this.flowParseUnionType();
        this.state.inType = oldInType; // Ensure that a brace after a function generic type annotation is a
        // statement, except in arrow functions (noAnonFunctionType)

        this.state.exprAllowed = this.state.exprAllowed || this.state.noAnonFunctionType;
        return type;
      };

      _proto.flowParseTypeAnnotation = function flowParseTypeAnnotation() {
        var node = this.startNode();
        node.typeAnnotation = this.flowParseTypeInitialiser();
        return this.finishNode(node, "TypeAnnotation");
      };

      _proto.flowParseTypeAnnotatableIdentifier = function flowParseTypeAnnotatableIdentifier(allowPrimitiveOverride) {
        var ident = allowPrimitiveOverride ? this.parseIdentifier() : this.flowParseRestrictedIdentifier();

        if (this.match(types.colon)) {
          ident.typeAnnotation = this.flowParseTypeAnnotation();
          this.finishNode(ident, ident.type);
        }

        return ident;
      };

      _proto.typeCastToParameter = function typeCastToParameter(node) {
        node.expression.typeAnnotation = node.typeAnnotation;
        return this.finishNodeAt(node.expression, node.expression.type, node.typeAnnotation.end, node.typeAnnotation.loc.end);
      };

      _proto.flowParseVariance = function flowParseVariance() {
        var variance = null;

        if (this.match(types.plusMin)) {
          variance = this.startNode();

          if (this.state.value === "+") {
            variance.kind = "plus";
          } else {
            variance.kind = "minus";
          }

          this.next();
          this.finishNode(variance, "Variance");
        }

        return variance;
      }; // ==================================
      // Overrides
      // ==================================


      _proto.parseFunctionBody = function parseFunctionBody(node, allowExpressionBody) {
        var _this2 = this;

        if (allowExpressionBody) {
          return this.forwardNoArrowParamsConversionAt(node, function () {
            return _superClass.prototype.parseFunctionBody.call(_this2, node, true);
          });
        }

        return _superClass.prototype.parseFunctionBody.call(this, node, false);
      };

      _proto.parseFunctionBodyAndFinish = function parseFunctionBodyAndFinish(node, type, allowExpressionBody) {
        // For arrow functions, `parseArrow` handles the return type itself.
        if (!allowExpressionBody && this.match(types.colon)) {
          var typeNode = this.startNode();

          var _flowParseTypeAndPred2 = this.flowParseTypeAndPredicateInitialiser();

          // $FlowFixMe (destructuring not supported yet)
          typeNode.typeAnnotation = _flowParseTypeAndPred2[0];
          // $FlowFixMe (destructuring not supported yet)
          node.predicate = _flowParseTypeAndPred2[1];
          node.returnType = typeNode.typeAnnotation ? this.finishNode(typeNode, "TypeAnnotation") : null;
        }

        _superClass.prototype.parseFunctionBodyAndFinish.call(this, node, type, allowExpressionBody);
      }; // interfaces


      _proto.parseStatement = function parseStatement(declaration, topLevel) {
        // strict mode handling of `interface` since it's a reserved word
        if (this.state.strict && this.match(types.name) && this.state.value === "interface") {
          var node = this.startNode();
          this.next();
          return this.flowParseInterface(node);
        } else {
          return _superClass.prototype.parseStatement.call(this, declaration, topLevel);
        }
      }; // declares, interfaces and type aliases


      _proto.parseExpressionStatement = function parseExpressionStatement(node, expr) {
        if (expr.type === "Identifier") {
          if (expr.name === "declare") {
            if (this.match(types._class) || this.match(types.name) || this.match(types._function) || this.match(types._var) || this.match(types._export)) {
              return this.flowParseDeclare(node);
            }
          } else if (this.match(types.name)) {
            if (expr.name === "interface") {
              return this.flowParseInterface(node);
            } else if (expr.name === "type") {
              return this.flowParseTypeAlias(node);
            } else if (expr.name === "opaque") {
              return this.flowParseOpaqueType(node, false);
            }
          }
        }

        return _superClass.prototype.parseExpressionStatement.call(this, node, expr);
      }; // export type


      _proto.shouldParseExportDeclaration = function shouldParseExportDeclaration() {
        return this.isContextual("type") || this.isContextual("interface") || this.isContextual("opaque") || _superClass.prototype.shouldParseExportDeclaration.call(this);
      };

      _proto.isExportDefaultSpecifier = function isExportDefaultSpecifier() {
        if (this.match(types.name) && (this.state.value === "type" || this.state.value === "interface" || this.state.value == "opaque")) {
          return false;
        }

        return _superClass.prototype.isExportDefaultSpecifier.call(this);
      };

      _proto.parseConditional = function parseConditional(expr, noIn, startPos, startLoc, refNeedsArrowPos) {
        var _this3 = this;

        if (!this.match(types.question)) return expr; // only do the expensive clone if there is a question mark
        // and if we come from inside parens

        if (refNeedsArrowPos) {
          var _state = this.state.clone();

          try {
            return _superClass.prototype.parseConditional.call(this, expr, noIn, startPos, startLoc);
          } catch (err) {
            if (err instanceof SyntaxError) {
              this.state = _state;
              refNeedsArrowPos.start = err.pos || this.state.start;
              return expr;
            } else {
              // istanbul ignore next: no such error is expected
              throw err;
            }
          }
        }

        this.expect(types.question);
        var state = this.state.clone();
        var originalNoArrowAt = this.state.noArrowAt;
        var node = this.startNodeAt(startPos, startLoc);

        var _tryParseConditionalC = this.tryParseConditionalConsequent(),
            consequent = _tryParseConditionalC.consequent,
            failed = _tryParseConditionalC.failed;

        var _getArrowLikeExpressi = this.getArrowLikeExpressions(consequent),
            valid = _getArrowLikeExpressi[0],
            invalid = _getArrowLikeExpressi[1];

        if (failed || invalid.length > 0) {
          var noArrowAt = originalNoArrowAt.concat();

          if (invalid.length > 0) {
            this.state = state;
            this.state.noArrowAt = noArrowAt;

            for (var i = 0; i < invalid.length; i++) {
              noArrowAt.push(invalid[i].start);
            }

            var _tryParseConditionalC2 = this.tryParseConditionalConsequent();

            consequent = _tryParseConditionalC2.consequent;
            failed = _tryParseConditionalC2.failed;

            var _getArrowLikeExpressi2 = this.getArrowLikeExpressions(consequent);

            valid = _getArrowLikeExpressi2[0];
            invalid = _getArrowLikeExpressi2[1];
          }

          if (failed && valid.length > 1) {
            // if there are two or more possible correct ways of parsing, throw an
            // error.
            // e.g.   Source: a ? (b): c => (d): e => f
            //      Result 1: a ? b : (c => ((d): e => f))
            //      Result 2: a ? ((b): c => d) : (e => f)
            this.raise(state.start, "Ambiguous expression: wrap the arrow functions in parentheses to disambiguate.");
          }

          if (failed && valid.length === 1) {
            this.state = state;
            this.state.noArrowAt = noArrowAt.concat(valid[0].start);

            var _tryParseConditionalC3 = this.tryParseConditionalConsequent();

            consequent = _tryParseConditionalC3.consequent;
            failed = _tryParseConditionalC3.failed;
          }

          this.getArrowLikeExpressions(consequent, true);
        }

        this.state.noArrowAt = originalNoArrowAt;
        this.expect(types.colon);
        node.test = expr;
        node.consequent = consequent;
        node.alternate = this.forwardNoArrowParamsConversionAt(node, function () {
          return _this3.parseMaybeAssign(noIn, undefined, undefined, undefined);
        });
        return this.finishNode(node, "ConditionalExpression");
      };

      _proto.tryParseConditionalConsequent = function tryParseConditionalConsequent() {
        this.state.noArrowParamsConversionAt.push(this.state.start);
        var consequent = this.parseMaybeAssign();
        var failed = !this.match(types.colon);
        this.state.noArrowParamsConversionAt.pop();
        return {
          consequent,
          failed
        };
      }; // Given an expression, walks throught its arrow functions whose body is
      // an expression and throught conditional expressions. It returns every
      // function which has been parsed with a return type but could have been
      // parenthesized expressions.
      // These functions are separated into two arrays: one containing the ones
      // whose parameters can be converted to assignable lists, one containing the
      // others.


      _proto.getArrowLikeExpressions = function getArrowLikeExpressions(node, disallowInvalid) {
        var _this4 = this;

        var stack = [node];
        var arrows = [];

        while (stack.length !== 0) {
          var _node = stack.pop();

          if (_node.type === "ArrowFunctionExpression") {
            if (_node.typeParameters || !_node.returnType) {
              // This is an arrow expression without ambiguity, so check its parameters
              this.toAssignableList( // node.params is Expression[] instead of $ReadOnlyArray<Pattern> because it
              // has not been converted yet.
              _node.params, true, "arrow function parameters"); // Use super's method to force the parameters to be checked

              _superClass.prototype.checkFunctionNameAndParams.call(this, _node, true);
            } else {
              arrows.push(_node);
            }

            stack.push(_node.body);
          } else if (_node.type === "ConditionalExpression") {
            stack.push(_node.consequent);
            stack.push(_node.alternate);
          }
        }

        if (disallowInvalid) {
          for (var i = 0; i < arrows.length; i++) {
            this.toAssignableList(node.params, true, "arrow function parameters");
          }

          return [arrows, []];
        }

        return partition(arrows, function (node) {
          try {
            _this4.toAssignableList(node.params, true, "arrow function parameters");

            return true;
          } catch (err) {
            return false;
          }
        });
      };

      _proto.forwardNoArrowParamsConversionAt = function forwardNoArrowParamsConversionAt(node, parse) {
        var result;

        if (this.state.noArrowParamsConversionAt.indexOf(node.start) !== -1) {
          this.state.noArrowParamsConversionAt.push(this.state.start);
          result = parse();
          this.state.noArrowParamsConversionAt.pop();
        } else {
          result = parse();
        }

        return result;
      };

      _proto.parseParenItem = function parseParenItem(node, startPos, startLoc) {
        node = _superClass.prototype.parseParenItem.call(this, node, startPos, startLoc);

        if (this.eat(types.question)) {
          node.optional = true;
        }

        if (this.match(types.colon)) {
          var typeCastNode = this.startNodeAt(startPos, startLoc);
          typeCastNode.expression = node;
          typeCastNode.typeAnnotation = this.flowParseTypeAnnotation();
          return this.finishNode(typeCastNode, "TypeCastExpression");
        }

        return node;
      };

      _proto.assertModuleNodeAllowed = function assertModuleNodeAllowed(node) {
        if (node.type === "ImportDeclaration" && (node.importKind === "type" || node.importKind === "typeof") || node.type === "ExportNamedDeclaration" && node.exportKind === "type" || node.type === "ExportAllDeclaration" && node.exportKind === "type") {
          // Allow Flowtype imports and exports in all conditions because
          // Flow itself does not care about 'sourceType'.
          return;
        }

        _superClass.prototype.assertModuleNodeAllowed.call(this, node);
      };

      _proto.parseExport = function parseExport(node) {
        node = _superClass.prototype.parseExport.call(this, node);

        if (node.type === "ExportNamedDeclaration" || node.type === "ExportAllDeclaration") {
          node.exportKind = node.exportKind || "value";
        }

        return node;
      };

      _proto.parseExportDeclaration = function parseExportDeclaration(node) {
        if (this.isContextual("type")) {
          node.exportKind = "type";
          var declarationNode = this.startNode();
          this.next();

          if (this.match(types.braceL)) {
            // export type { foo, bar };
            node.specifiers = this.parseExportSpecifiers();
            this.parseExportFrom(node);
            return null;
          } else {
            // export type Foo = Bar;
            return this.flowParseTypeAlias(declarationNode);
          }
        } else if (this.isContextual("opaque")) {
          node.exportKind = "type";

          var _declarationNode = this.startNode();

          this.next(); // export opaque type Foo = Bar;

          return this.flowParseOpaqueType(_declarationNode, false);
        } else if (this.isContextual("interface")) {
          node.exportKind = "type";

          var _declarationNode2 = this.startNode();

          this.next();
          return this.flowParseInterface(_declarationNode2);
        } else {
          return _superClass.prototype.parseExportDeclaration.call(this, node);
        }
      };

      _proto.shouldParseExportStar = function shouldParseExportStar() {
        return _superClass.prototype.shouldParseExportStar.call(this) || this.isContextual("type") && this.lookahead().type === types.star;
      };

      _proto.parseExportStar = function parseExportStar(node) {
        if (this.eatContextual("type")) {
          node.exportKind = "type";
        }

        return _superClass.prototype.parseExportStar.call(this, node);
      };

      _proto.parseExportNamespace = function parseExportNamespace(node) {
        if (node.exportKind === "type") {
          this.unexpected();
        }

        return _superClass.prototype.parseExportNamespace.call(this, node);
      };

      _proto.parseClassId = function parseClassId(node, isStatement, optionalId) {
        _superClass.prototype.parseClassId.call(this, node, isStatement, optionalId);

        if (this.isRelational("<")) {
          node.typeParameters = this.flowParseTypeParameterDeclaration();
        }
      }; // don't consider `void` to be a keyword as then it'll use the void token type
      // and set startExpr


      _proto.isKeyword = function isKeyword$$1(name) {
        if (this.state.inType && name === "void") {
          return false;
        } else {
          return _superClass.prototype.isKeyword.call(this, name);
        }
      }; // ensure that inside flow types, we bypass the jsx parser plugin


      _proto.readToken = function readToken(code) {
        var next = this.input.charCodeAt(this.state.pos + 1);

        if (this.state.inType && (code === 62 || code === 60)) {
          return this.finishOp(types.relational, 1);
        } else if (isIteratorStart(code, next)) {
          this.state.isIterator = true;
          return _superClass.prototype.readWord.call(this, code);
        } else {
          return _superClass.prototype.readToken.call(this, code);
        }
      };

      _proto.toAssignable = function toAssignable(node, isBinding, contextDescription) {
        if (node.type === "TypeCastExpression") {
          return _superClass.prototype.toAssignable.call(this, this.typeCastToParameter(node), isBinding, contextDescription);
        } else {
          return _superClass.prototype.toAssignable.call(this, node, isBinding, contextDescription);
        }
      }; // turn type casts that we found in function parameter head into type annotated params


      _proto.toAssignableList = function toAssignableList(exprList, isBinding, contextDescription) {
        for (var i = 0; i < exprList.length; i++) {
          var expr = exprList[i];

          if (expr && expr.type === "TypeCastExpression") {
            exprList[i] = this.typeCastToParameter(expr);
          }
        }

        return _superClass.prototype.toAssignableList.call(this, exprList, isBinding, contextDescription);
      }; // this is a list of nodes, from something like a call expression, we need to filter the
      // type casts that we've found that are illegal in this context


      _proto.toReferencedList = function toReferencedList(exprList) {
        for (var i = 0; i < exprList.length; i++) {
          var expr = exprList[i];

          if (expr && expr._exprListItem && expr.type === "TypeCastExpression") {
            this.raise(expr.start, "Unexpected type cast");
          }
        }

        return exprList;
      }; // parse an item inside a expression list eg. `(NODE, NODE)` where NODE represents
      // the position where this function is called


      _proto.parseExprListItem = function parseExprListItem(allowEmpty, refShorthandDefaultPos, refNeedsArrowPos) {
        var container = this.startNode();

        var node = _superClass.prototype.parseExprListItem.call(this, allowEmpty, refShorthandDefaultPos, refNeedsArrowPos);

        if (this.match(types.colon)) {
          container._exprListItem = true;
          container.expression = node;
          container.typeAnnotation = this.flowParseTypeAnnotation();
          return this.finishNode(container, "TypeCastExpression");
        } else {
          return node;
        }
      };

      _proto.checkLVal = function checkLVal(expr, isBinding, checkClashes, contextDescription) {
        if (expr.type !== "TypeCastExpression") {
          return _superClass.prototype.checkLVal.call(this, expr, isBinding, checkClashes, contextDescription);
        }
      }; // parse class property type annotations


      _proto.parseClassProperty = function parseClassProperty(node) {
        if (this.match(types.colon)) {
          node.typeAnnotation = this.flowParseTypeAnnotation();
        }

        return _superClass.prototype.parseClassProperty.call(this, node);
      }; // determine whether or not we're currently in the position where a class method would appear


      _proto.isClassMethod = function isClassMethod() {
        return this.isRelational("<") || _superClass.prototype.isClassMethod.call(this);
      }; // determine whether or not we're currently in the position where a class property would appear


      _proto.isClassProperty = function isClassProperty() {
        return this.match(types.colon) || _superClass.prototype.isClassProperty.call(this);
      };

      _proto.isNonstaticConstructor = function isNonstaticConstructor(method) {
        return !this.match(types.colon) && _superClass.prototype.isNonstaticConstructor.call(this, method);
      }; // parse type parameters for class methods


      _proto.pushClassMethod = function pushClassMethod(classBody, method, isGenerator, isAsync, isConstructor) {
        if (method.variance) {
          this.unexpected(method.variance.start);
        }

        delete method.variance;

        if (this.isRelational("<")) {
          method.typeParameters = this.flowParseTypeParameterDeclaration();
        }

        _superClass.prototype.pushClassMethod.call(this, classBody, method, isGenerator, isAsync, isConstructor);
      };

      _proto.pushClassPrivateMethod = function pushClassPrivateMethod(classBody, method, isGenerator, isAsync) {
        if (method.variance) {
          this.unexpected(method.variance.start);
        }

        delete method.variance;

        if (this.isRelational("<")) {
          method.typeParameters = this.flowParseTypeParameterDeclaration();
        }

        _superClass.prototype.pushClassPrivateMethod.call(this, classBody, method, isGenerator, isAsync);
      }; // parse a the super class type parameters and implements


      _proto.parseClassSuper = function parseClassSuper(node) {
        _superClass.prototype.parseClassSuper.call(this, node);

        if (node.superClass && this.isRelational("<")) {
          node.superTypeParameters = this.flowParseTypeParameterInstantiation();
        }

        if (this.isContextual("implements")) {
          this.next();
          var implemented = node.implements = [];

          do {
            var _node2 = this.startNode();

            _node2.id = this.flowParseRestrictedIdentifier(
            /*liberal*/
            true);

            if (this.isRelational("<")) {
              _node2.typeParameters = this.flowParseTypeParameterInstantiation();
            } else {
              _node2.typeParameters = null;
            }

            implemented.push(this.finishNode(_node2, "ClassImplements"));
          } while (this.eat(types.comma));
        }
      };

      _proto.parsePropertyName = function parsePropertyName(node) {
        var variance = this.flowParseVariance();

        var key = _superClass.prototype.parsePropertyName.call(this, node); // $FlowIgnore ("variance" not defined on TsNamedTypeElementBase)


        node.variance = variance;
        return key;
      }; // parse type parameters for object method shorthand


      _proto.parseObjPropValue = function parseObjPropValue(prop, startPos, startLoc, isGenerator, isAsync, isPattern, refShorthandDefaultPos) {
        if (prop.variance) {
          this.unexpected(prop.variance.start);
        }

        delete prop.variance;
        var typeParameters; // method shorthand

        if (this.isRelational("<")) {
          typeParameters = this.flowParseTypeParameterDeclaration();
          if (!this.match(types.parenL)) this.unexpected();
        }

        _superClass.prototype.parseObjPropValue.call(this, prop, startPos, startLoc, isGenerator, isAsync, isPattern, refShorthandDefaultPos); // add typeParameters if we found them


        if (typeParameters) {
          // $FlowFixMe (trying to set '.typeParameters' on an expression)
          (prop.value || prop).typeParameters = typeParameters;
        }
      };

      _proto.parseAssignableListItemTypes = function parseAssignableListItemTypes(param) {
        if (this.eat(types.question)) {
          if (param.type !== "Identifier") {
            throw this.raise(param.start, "A binding pattern parameter cannot be optional in an implementation signature.");
          }

          param.optional = true;
        }

        if (this.match(types.colon)) {
          param.typeAnnotation = this.flowParseTypeAnnotation();
        }

        this.finishNode(param, param.type);
        return param;
      };

      _proto.parseMaybeDefault = function parseMaybeDefault(startPos, startLoc, left) {
        var node = _superClass.prototype.parseMaybeDefault.call(this, startPos, startLoc, left);

        if (node.type === "AssignmentPattern" && node.typeAnnotation && node.right.start < node.typeAnnotation.start) {
          this.raise(node.typeAnnotation.start, "Type annotations must come before default assignments, e.g. instead of `age = 25: number` use `age: number = 25`");
        }

        return node;
      };

      _proto.shouldParseDefaultImport = function shouldParseDefaultImport(node) {
        if (!hasTypeImportKind(node)) {
          return _superClass.prototype.shouldParseDefaultImport.call(this, node);
        }

        return isMaybeDefaultImport(this.state);
      };

      _proto.parseImportSpecifierLocal = function parseImportSpecifierLocal(node, specifier, type, contextDescription) {
        specifier.local = hasTypeImportKind(node) ? this.flowParseRestrictedIdentifier(true) : this.parseIdentifier();
        this.checkLVal(specifier.local, true, undefined, contextDescription);
        node.specifiers.push(this.finishNode(specifier, type));
      }; // parse typeof and type imports


      _proto.parseImportSpecifiers = function parseImportSpecifiers(node) {
        node.importKind = "value";
        var kind = null;

        if (this.match(types._typeof)) {
          kind = "typeof";
        } else if (this.isContextual("type")) {
          kind = "type";
        }

        if (kind) {
          var lh = this.lookahead(); // import type * is not allowed

          if (kind === "type" && lh.type === types.star) {
            this.unexpected(lh.start);
          }

          if (isMaybeDefaultImport(lh) || lh.type === types.braceL || lh.type === types.star) {
            this.next();
            node.importKind = kind;
          }
        }

        _superClass.prototype.parseImportSpecifiers.call(this, node);
      }; // parse import-type/typeof shorthand


      _proto.parseImportSpecifier = function parseImportSpecifier(node) {
        var specifier = this.startNode();
        var firstIdentLoc = this.state.start;
        var firstIdent = this.parseIdentifier(true);
        var specifierTypeKind = null;

        if (firstIdent.name === "type") {
          specifierTypeKind = "type";
        } else if (firstIdent.name === "typeof") {
          specifierTypeKind = "typeof";
        }

        var isBinding = false;

        if (this.isContextual("as") && !this.isLookaheadContextual("as")) {
          var as_ident = this.parseIdentifier(true);

          if (specifierTypeKind !== null && !this.match(types.name) && !this.state.type.keyword) {
            // `import {type as ,` or `import {type as }`
            specifier.imported = as_ident;
            specifier.importKind = specifierTypeKind;
            specifier.local = as_ident.__clone();
          } else {
            // `import {type as foo`
            specifier.imported = firstIdent;
            specifier.importKind = null;
            specifier.local = this.parseIdentifier();
          }
        } else if (specifierTypeKind !== null && (this.match(types.name) || this.state.type.keyword)) {
          // `import {type foo`
          specifier.imported = this.parseIdentifier(true);
          specifier.importKind = specifierTypeKind;

          if (this.eatContextual("as")) {
            specifier.local = this.parseIdentifier();
          } else {
            isBinding = true;
            specifier.local = specifier.imported.__clone();
          }
        } else {
          isBinding = true;
          specifier.imported = firstIdent;
          specifier.importKind = null;
          specifier.local = specifier.imported.__clone();
        }

        var nodeIsTypeImport = hasTypeImportKind(node);
        var specifierIsTypeImport = hasTypeImportKind(specifier);

        if (nodeIsTypeImport && specifierIsTypeImport) {
          this.raise(firstIdentLoc, "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");
        }

        if (nodeIsTypeImport || specifierIsTypeImport) {
          this.checkReservedType(specifier.local.name, specifier.local.start);
        }

        if (isBinding && !nodeIsTypeImport && !specifierIsTypeImport) {
          this.checkReservedWord(specifier.local.name, specifier.start, true, true);
        }

        this.checkLVal(specifier.local, true, undefined, "import specifier");
        node.specifiers.push(this.finishNode(specifier, "ImportSpecifier"));
      }; // parse function type parameters - function foo<T>() {}


      _proto.parseFunctionParams = function parseFunctionParams(node) {
        // $FlowFixMe
        var kind = node.kind;

        if (kind !== "get" && kind !== "set" && this.isRelational("<")) {
          node.typeParameters = this.flowParseTypeParameterDeclaration();
        }

        _superClass.prototype.parseFunctionParams.call(this, node);
      }; // parse flow type annotations on variable declarator heads - let foo: string = bar


      _proto.parseVarHead = function parseVarHead(decl) {
        _superClass.prototype.parseVarHead.call(this, decl);

        if (this.match(types.colon)) {
          decl.id.typeAnnotation = this.flowParseTypeAnnotation();
          this.finishNode(decl.id, decl.id.type);
        }
      }; // parse the return type of an async arrow function - let foo = (async (): number => {});


      _proto.parseAsyncArrowFromCallExpression = function parseAsyncArrowFromCallExpression(node, call) {
        if (this.match(types.colon)) {
          var oldNoAnonFunctionType = this.state.noAnonFunctionType;
          this.state.noAnonFunctionType = true;
          node.returnType = this.flowParseTypeAnnotation();
          this.state.noAnonFunctionType = oldNoAnonFunctionType;
        }

        return _superClass.prototype.parseAsyncArrowFromCallExpression.call(this, node, call);
      }; // todo description


      _proto.shouldParseAsyncArrow = function shouldParseAsyncArrow() {
        return this.match(types.colon) || _superClass.prototype.shouldParseAsyncArrow.call(this);
      }; // We need to support type parameter declarations for arrow functions. This
      // is tricky. There are three situations we need to handle
      //
      // 1. This is either JSX or an arrow function. We'll try JSX first. If that
      //    fails, we'll try an arrow function. If that fails, we'll throw the JSX
      //    error.
      // 2. This is an arrow function. We'll parse the type parameter declaration,
      //    parse the rest, make sure the rest is an arrow function, and go from
      //    there
      // 3. This is neither. Just call the super method


      _proto.parseMaybeAssign = function parseMaybeAssign(noIn, refShorthandDefaultPos, afterLeftParse, refNeedsArrowPos) {
        var _this5 = this;

        var jsxError = null;

        if (types.jsxTagStart && this.match(types.jsxTagStart)) {
          var state = this.state.clone();

          try {
            return _superClass.prototype.parseMaybeAssign.call(this, noIn, refShorthandDefaultPos, afterLeftParse, refNeedsArrowPos);
          } catch (err) {
            if (err instanceof SyntaxError) {
              this.state = state; // Remove `tc.j_expr` and `tc.j_oTag` from context added
              // by parsing `jsxTagStart` to stop the JSX plugin from
              // messing with the tokens

              this.state.context.length -= 2;
              jsxError = err;
            } else {
              // istanbul ignore next: no such error is expected
              throw err;
            }
          }
        }

        if (jsxError != null || this.isRelational("<")) {
          var arrowExpression;
          var typeParameters;

          try {
            typeParameters = this.flowParseTypeParameterDeclaration();
            arrowExpression = this.forwardNoArrowParamsConversionAt(typeParameters, function () {
              return _superClass.prototype.parseMaybeAssign.call(_this5, noIn, refShorthandDefaultPos, afterLeftParse, refNeedsArrowPos);
            });
            arrowExpression.typeParameters = typeParameters;
            this.resetStartLocationFromNode(arrowExpression, typeParameters);
          } catch (err) {
            throw jsxError || err;
          }

          if (arrowExpression.type === "ArrowFunctionExpression") {
            return arrowExpression;
          } else if (jsxError != null) {
            throw jsxError;
          } else {
            this.raise(typeParameters.start, "Expected an arrow function after this type parameter declaration");
          }
        }

        return _superClass.prototype.parseMaybeAssign.call(this, noIn, refShorthandDefaultPos, afterLeftParse, refNeedsArrowPos);
      }; // handle return types for arrow functions


      _proto.parseArrow = function parseArrow(node) {
        if (this.match(types.colon)) {
          var state = this.state.clone();

          try {
            var oldNoAnonFunctionType = this.state.noAnonFunctionType;
            this.state.noAnonFunctionType = true;
            var typeNode = this.startNode();

            var _flowParseTypeAndPred3 = this.flowParseTypeAndPredicateInitialiser();

            // $FlowFixMe (destructuring not supported yet)
            typeNode.typeAnnotation = _flowParseTypeAndPred3[0];
            // $FlowFixMe (destructuring not supported yet)
            node.predicate = _flowParseTypeAndPred3[1];
            this.state.noAnonFunctionType = oldNoAnonFunctionType;
            if (this.canInsertSemicolon()) this.unexpected();
            if (!this.match(types.arrow)) this.unexpected(); // assign after it is clear it is an arrow

            node.returnType = typeNode.typeAnnotation ? this.finishNode(typeNode, "TypeAnnotation") : null;
          } catch (err) {
            if (err instanceof SyntaxError) {
              this.state = state;
            } else {
              // istanbul ignore next: no such error is expected
              throw err;
            }
          }
        }

        return _superClass.prototype.parseArrow.call(this, node);
      };

      _proto.shouldParseArrow = function shouldParseArrow() {
        return this.match(types.colon) || _superClass.prototype.shouldParseArrow.call(this);
      };

      _proto.setArrowFunctionParameters = function setArrowFunctionParameters(node, params) {
        if (this.state.noArrowParamsConversionAt.indexOf(node.start) !== -1) {
          node.params = params;
        } else {
          _superClass.prototype.setArrowFunctionParameters.call(this, node, params);
        }
      };

      _proto.checkFunctionNameAndParams = function checkFunctionNameAndParams(node, isArrowFunction) {
        if (isArrowFunction && this.state.noArrowParamsConversionAt.indexOf(node.start) !== -1) {
          return;
        }

        return _superClass.prototype.checkFunctionNameAndParams.call(this, node, isArrowFunction);
      };

      _proto.parseParenAndDistinguishExpression = function parseParenAndDistinguishExpression(canBeArrow) {
        return _superClass.prototype.parseParenAndDistinguishExpression.call(this, canBeArrow && this.state.noArrowAt.indexOf(this.state.start) === -1);
      };

      _proto.parseSubscripts = function parseSubscripts(base, startPos, startLoc, noCalls) {
        if (base.type === "Identifier" && base.name === "async" && this.state.noArrowAt.indexOf(startPos) !== -1) {
          this.next();
          var node = this.startNodeAt(startPos, startLoc);
          node.callee = base;
          node.arguments = this.parseCallExpressionArguments(types.parenR, false);
          base = this.finishNode(node, "CallExpression");
        } else if (base.type === "Identifier" && base.name === "async" && this.isRelational("<")) {
          var state = this.state.clone();
          var error;

          try {
            var _node3 = this.parseAsyncArrowWithTypeParameters(startPos, startLoc);

            if (_node3) return _node3;
          } catch (e) {
            error = e;
          }

          this.state = state;

          try {
            return _superClass.prototype.parseSubscripts.call(this, base, startPos, startLoc, noCalls);
          } catch (e) {
            throw error || e;
          }
        }

        return _superClass.prototype.parseSubscripts.call(this, base, startPos, startLoc, noCalls);
      };

      _proto.parseAsyncArrowWithTypeParameters = function parseAsyncArrowWithTypeParameters(startPos, startLoc) {
        var node = this.startNodeAt(startPos, startLoc);
        this.parseFunctionParams(node);
        if (!this.parseArrow(node)) return;
        return this.parseArrowExpression(node,
        /* params */
        undefined,
        /* isAsync */
        true);
      };

      _proto.readToken_mult_modulo = function readToken_mult_modulo(code) {
        var next = this.input.charCodeAt(this.state.pos + 1);

        if (code === 42 && next === 47 && this.state.hasFlowComment) {
          this.state.hasFlowComment = false;
          this.state.pos += 2;
          this.nextToken();
          return;
        }

        _superClass.prototype.readToken_mult_modulo.call(this, code);
      };

      _proto.skipBlockComment = function skipBlockComment() {
        if (this.hasPlugin("flow") && this.hasPlugin("flowComments") && this.skipFlowComment()) {
          this.hasFlowCommentCompletion();
          this.state.pos += this.skipFlowComment();
          this.state.hasFlowComment = true;
          return;
        }

        var end;

        if (this.hasPlugin("flow") && this.state.hasFlowComment) {
          end = this.input.indexOf("*-/", this.state.pos += 2);
          if (end === -1) this.raise(this.state.pos - 2, "Unterminated comment");
          this.state.pos = end + 3;
          return;
        }

        _superClass.prototype.skipBlockComment.call(this);
      };

      _proto.skipFlowComment = function skipFlowComment() {
        var ch2 = this.input.charCodeAt(this.state.pos + 2);
        var ch3 = this.input.charCodeAt(this.state.pos + 3);

        if (ch2 === 58 && ch3 === 58) {
          return 4; // check for /*::
        }

        if (this.input.slice(this.state.pos + 2, 14) === "flow-include") {
          return 14; // check for /*flow-include
        }

        if (ch2 === 58 && ch3 !== 58 && 2) {
          return 2; // check for /*:, advance only 2 steps
        }

        return false;
      };

      _proto.hasFlowCommentCompletion = function hasFlowCommentCompletion() {
        var end = this.input.indexOf("*/", this.state.pos);

        if (end === -1) {
          this.raise(this.state.pos, "Unterminated comment");
        }
      };

      return _class;
    }(superClass)
  );
});

var entities = {
  quot: "\u0022",
  amp: "&",
  apos: "\u0027",
  lt: "<",
  gt: ">",
  nbsp: "\u00A0",
  iexcl: "\u00A1",
  cent: "\u00A2",
  pound: "\u00A3",
  curren: "\u00A4",
  yen: "\u00A5",
  brvbar: "\u00A6",
  sect: "\u00A7",
  uml: "\u00A8",
  copy: "\u00A9",
  ordf: "\u00AA",
  laquo: "\u00AB",
  not: "\u00AC",
  shy: "\u00AD",
  reg: "\u00AE",
  macr: "\u00AF",
  deg: "\u00B0",
  plusmn: "\u00B1",
  sup2: "\u00B2",
  sup3: "\u00B3",
  acute: "\u00B4",
  micro: "\u00B5",
  para: "\u00B6",
  middot: "\u00B7",
  cedil: "\u00B8",
  sup1: "\u00B9",
  ordm: "\u00BA",
  raquo: "\u00BB",
  frac14: "\u00BC",
  frac12: "\u00BD",
  frac34: "\u00BE",
  iquest: "\u00BF",
  Agrave: "\u00C0",
  Aacute: "\u00C1",
  Acirc: "\u00C2",
  Atilde: "\u00C3",
  Auml: "\u00C4",
  Aring: "\u00C5",
  AElig: "\u00C6",
  Ccedil: "\u00C7",
  Egrave: "\u00C8",
  Eacute: "\u00C9",
  Ecirc: "\u00CA",
  Euml: "\u00CB",
  Igrave: "\u00CC",
  Iacute: "\u00CD",
  Icirc: "\u00CE",
  Iuml: "\u00CF",
  ETH: "\u00D0",
  Ntilde: "\u00D1",
  Ograve: "\u00D2",
  Oacute: "\u00D3",
  Ocirc: "\u00D4",
  Otilde: "\u00D5",
  Ouml: "\u00D6",
  times: "\u00D7",
  Oslash: "\u00D8",
  Ugrave: "\u00D9",
  Uacute: "\u00DA",
  Ucirc: "\u00DB",
  Uuml: "\u00DC",
  Yacute: "\u00DD",
  THORN: "\u00DE",
  szlig: "\u00DF",
  agrave: "\u00E0",
  aacute: "\u00E1",
  acirc: "\u00E2",
  atilde: "\u00E3",
  auml: "\u00E4",
  aring: "\u00E5",
  aelig: "\u00E6",
  ccedil: "\u00E7",
  egrave: "\u00E8",
  eacute: "\u00E9",
  ecirc: "\u00EA",
  euml: "\u00EB",
  igrave: "\u00EC",
  iacute: "\u00ED",
  icirc: "\u00EE",
  iuml: "\u00EF",
  eth: "\u00F0",
  ntilde: "\u00F1",
  ograve: "\u00F2",
  oacute: "\u00F3",
  ocirc: "\u00F4",
  otilde: "\u00F5",
  ouml: "\u00F6",
  divide: "\u00F7",
  oslash: "\u00F8",
  ugrave: "\u00F9",
  uacute: "\u00FA",
  ucirc: "\u00FB",
  uuml: "\u00FC",
  yacute: "\u00FD",
  thorn: "\u00FE",
  yuml: "\u00FF",
  OElig: "\u0152",
  oelig: "\u0153",
  Scaron: "\u0160",
  scaron: "\u0161",
  Yuml: "\u0178",
  fnof: "\u0192",
  circ: "\u02C6",
  tilde: "\u02DC",
  Alpha: "\u0391",
  Beta: "\u0392",
  Gamma: "\u0393",
  Delta: "\u0394",
  Epsilon: "\u0395",
  Zeta: "\u0396",
  Eta: "\u0397",
  Theta: "\u0398",
  Iota: "\u0399",
  Kappa: "\u039A",
  Lambda: "\u039B",
  Mu: "\u039C",
  Nu: "\u039D",
  Xi: "\u039E",
  Omicron: "\u039F",
  Pi: "\u03A0",
  Rho: "\u03A1",
  Sigma: "\u03A3",
  Tau: "\u03A4",
  Upsilon: "\u03A5",
  Phi: "\u03A6",
  Chi: "\u03A7",
  Psi: "\u03A8",
  Omega: "\u03A9",
  alpha: "\u03B1",
  beta: "\u03B2",
  gamma: "\u03B3",
  delta: "\u03B4",
  epsilon: "\u03B5",
  zeta: "\u03B6",
  eta: "\u03B7",
  theta: "\u03B8",
  iota: "\u03B9",
  kappa: "\u03BA",
  lambda: "\u03BB",
  mu: "\u03BC",
  nu: "\u03BD",
  xi: "\u03BE",
  omicron: "\u03BF",
  pi: "\u03C0",
  rho: "\u03C1",
  sigmaf: "\u03C2",
  sigma: "\u03C3",
  tau: "\u03C4",
  upsilon: "\u03C5",
  phi: "\u03C6",
  chi: "\u03C7",
  psi: "\u03C8",
  omega: "\u03C9",
  thetasym: "\u03D1",
  upsih: "\u03D2",
  piv: "\u03D6",
  ensp: "\u2002",
  emsp: "\u2003",
  thinsp: "\u2009",
  zwnj: "\u200C",
  zwj: "\u200D",
  lrm: "\u200E",
  rlm: "\u200F",
  ndash: "\u2013",
  mdash: "\u2014",
  lsquo: "\u2018",
  rsquo: "\u2019",
  sbquo: "\u201A",
  ldquo: "\u201C",
  rdquo: "\u201D",
  bdquo: "\u201E",
  dagger: "\u2020",
  Dagger: "\u2021",
  bull: "\u2022",
  hellip: "\u2026",
  permil: "\u2030",
  prime: "\u2032",
  Prime: "\u2033",
  lsaquo: "\u2039",
  rsaquo: "\u203A",
  oline: "\u203E",
  frasl: "\u2044",
  euro: "\u20AC",
  image: "\u2111",
  weierp: "\u2118",
  real: "\u211C",
  trade: "\u2122",
  alefsym: "\u2135",
  larr: "\u2190",
  uarr: "\u2191",
  rarr: "\u2192",
  darr: "\u2193",
  harr: "\u2194",
  crarr: "\u21B5",
  lArr: "\u21D0",
  uArr: "\u21D1",
  rArr: "\u21D2",
  dArr: "\u21D3",
  hArr: "\u21D4",
  forall: "\u2200",
  part: "\u2202",
  exist: "\u2203",
  empty: "\u2205",
  nabla: "\u2207",
  isin: "\u2208",
  notin: "\u2209",
  ni: "\u220B",
  prod: "\u220F",
  sum: "\u2211",
  minus: "\u2212",
  lowast: "\u2217",
  radic: "\u221A",
  prop: "\u221D",
  infin: "\u221E",
  ang: "\u2220",
  and: "\u2227",
  or: "\u2228",
  cap: "\u2229",
  cup: "\u222A",
  int: "\u222B",
  there4: "\u2234",
  sim: "\u223C",
  cong: "\u2245",
  asymp: "\u2248",
  ne: "\u2260",
  equiv: "\u2261",
  le: "\u2264",
  ge: "\u2265",
  sub: "\u2282",
  sup: "\u2283",
  nsub: "\u2284",
  sube: "\u2286",
  supe: "\u2287",
  oplus: "\u2295",
  otimes: "\u2297",
  perp: "\u22A5",
  sdot: "\u22C5",
  lceil: "\u2308",
  rceil: "\u2309",
  lfloor: "\u230A",
  rfloor: "\u230B",
  lang: "\u2329",
  rang: "\u232A",
  loz: "\u25CA",
  spades: "\u2660",
  clubs: "\u2663",
  hearts: "\u2665",
  diams: "\u2666"
};

var HEX_NUMBER = /^[\da-fA-F]+$/;
var DECIMAL_NUMBER = /^\d+$/;
types$1.j_oTag = new TokContext("<tag", false);
types$1.j_cTag = new TokContext("</tag", false);
types$1.j_expr = new TokContext("<tag>...</tag>", true, true);
types.jsxName = new TokenType("jsxName");
types.jsxText = new TokenType("jsxText", {
  beforeExpr: true
});
types.jsxTagStart = new TokenType("jsxTagStart", {
  startsExpr: true
});
types.jsxTagEnd = new TokenType("jsxTagEnd");

types.jsxTagStart.updateContext = function () {
  this.state.context.push(types$1.j_expr); // treat as beginning of JSX expression

  this.state.context.push(types$1.j_oTag); // start opening tag context

  this.state.exprAllowed = false;
};

types.jsxTagEnd.updateContext = function (prevType) {
  var out = this.state.context.pop();

  if (out === types$1.j_oTag && prevType === types.slash || out === types$1.j_cTag) {
    this.state.context.pop();
    this.state.exprAllowed = this.curContext() === types$1.j_expr;
  } else {
    this.state.exprAllowed = true;
  }
};

function isFragment(object) {
  return object ? object.type === "JSXOpeningFragment" || object.type === "JSXClosingFragment" : false;
} // Transforms JSX element name to string.


function getQualifiedJSXName(object) {
  if (object.type === "JSXIdentifier") {
    return object.name;
  }

  if (object.type === "JSXNamespacedName") {
    return object.namespace.name + ":" + object.name.name;
  }

  if (object.type === "JSXMemberExpression") {
    return getQualifiedJSXName(object.object) + "." + getQualifiedJSXName(object.property);
  } // istanbul ignore next


  throw new Error("Node had unexpected type: " + object.type);
}

var jsxPlugin = (function (superClass) {
  return (
    /*#__PURE__*/
    function (_superClass) {
      _inheritsLoose(_class, _superClass);

      function _class() {
        return _superClass.apply(this, arguments) || this;
      }

      var _proto = _class.prototype;

      // Reads inline JSX contents token.
      _proto.jsxReadToken = function jsxReadToken() {
        var out = "";
        var chunkStart = this.state.pos;

        for (;;) {
          if (this.state.pos >= this.input.length) {
            this.raise(this.state.start, "Unterminated JSX contents");
          }

          var ch = this.input.charCodeAt(this.state.pos);

          switch (ch) {
            case 60:
            case 123:
              if (this.state.pos === this.state.start) {
                if (ch === 60 && this.state.exprAllowed) {
                  ++this.state.pos;
                  return this.finishToken(types.jsxTagStart);
                }

                return this.getTokenFromCode(ch);
              }

              out += this.input.slice(chunkStart, this.state.pos);
              return this.finishToken(types.jsxText, out);

            case 38:
              out += this.input.slice(chunkStart, this.state.pos);
              out += this.jsxReadEntity();
              chunkStart = this.state.pos;
              break;

            default:
              if (isNewLine(ch)) {
                out += this.input.slice(chunkStart, this.state.pos);
                out += this.jsxReadNewLine(true);
                chunkStart = this.state.pos;
              } else {
                ++this.state.pos;
              }

          }
        }
      };

      _proto.jsxReadNewLine = function jsxReadNewLine(normalizeCRLF) {
        var ch = this.input.charCodeAt(this.state.pos);
        var out;
        ++this.state.pos;

        if (ch === 13 && this.input.charCodeAt(this.state.pos) === 10) {
          ++this.state.pos;
          out = normalizeCRLF ? "\n" : "\r\n";
        } else {
          out = String.fromCharCode(ch);
        }

        ++this.state.curLine;
        this.state.lineStart = this.state.pos;
        return out;
      };

      _proto.jsxReadString = function jsxReadString(quote) {
        var out = "";
        var chunkStart = ++this.state.pos;

        for (;;) {
          if (this.state.pos >= this.input.length) {
            this.raise(this.state.start, "Unterminated string constant");
          }

          var ch = this.input.charCodeAt(this.state.pos);
          if (ch === quote) break;

          if (ch === 38) {
            out += this.input.slice(chunkStart, this.state.pos);
            out += this.jsxReadEntity();
            chunkStart = this.state.pos;
          } else if (isNewLine(ch)) {
            out += this.input.slice(chunkStart, this.state.pos);
            out += this.jsxReadNewLine(false);
            chunkStart = this.state.pos;
          } else {
            ++this.state.pos;
          }
        }

        out += this.input.slice(chunkStart, this.state.pos++);
        return this.finishToken(types.string, out);
      };

      _proto.jsxReadEntity = function jsxReadEntity() {
        var str = "";
        var count = 0;
        var entity;
        var ch = this.input[this.state.pos];
        var startPos = ++this.state.pos;

        while (this.state.pos < this.input.length && count++ < 10) {
          ch = this.input[this.state.pos++];

          if (ch === ";") {
            if (str[0] === "#") {
              if (str[1] === "x") {
                str = str.substr(2);

                if (HEX_NUMBER.test(str)) {
                  entity = String.fromCodePoint(parseInt(str, 16));
                }
              } else {
                str = str.substr(1);

                if (DECIMAL_NUMBER.test(str)) {
                  entity = String.fromCodePoint(parseInt(str, 10));
                }
              }
            } else {
              entity = entities[str];
            }

            break;
          }

          str += ch;
        }

        if (!entity) {
          this.state.pos = startPos;
          return "&";
        }

        return entity;
      }; // Read a JSX identifier (valid tag or attribute name).
      //
      // Optimized version since JSX identifiers can"t contain
      // escape characters and so can be read as single slice.
      // Also assumes that first character was already checked
      // by isIdentifierStart in readToken.


      _proto.jsxReadWord = function jsxReadWord() {
        var ch;
        var start = this.state.pos;

        do {
          ch = this.input.charCodeAt(++this.state.pos);
        } while (isIdentifierChar(ch) || ch === 45);

        return this.finishToken(types.jsxName, this.input.slice(start, this.state.pos));
      }; // Parse next token as JSX identifier


      _proto.jsxParseIdentifier = function jsxParseIdentifier() {
        var node = this.startNode();

        if (this.match(types.jsxName)) {
          node.name = this.state.value;
        } else if (this.state.type.keyword) {
          node.name = this.state.type.keyword;
        } else {
          this.unexpected();
        }

        this.next();
        return this.finishNode(node, "JSXIdentifier");
      }; // Parse namespaced identifier.


      _proto.jsxParseNamespacedName = function jsxParseNamespacedName() {
        var startPos = this.state.start;
        var startLoc = this.state.startLoc;
        var name = this.jsxParseIdentifier();
        if (!this.eat(types.colon)) return name;
        var node = this.startNodeAt(startPos, startLoc);
        node.namespace = name;
        node.name = this.jsxParseIdentifier();
        return this.finishNode(node, "JSXNamespacedName");
      }; // Parses element name in any form - namespaced, member
      // or single identifier.


      _proto.jsxParseElementName = function jsxParseElementName() {
        var startPos = this.state.start;
        var startLoc = this.state.startLoc;
        var node = this.jsxParseNamespacedName();

        while (this.eat(types.dot)) {
          var newNode = this.startNodeAt(startPos, startLoc);
          newNode.object = node;
          newNode.property = this.jsxParseIdentifier();
          node = this.finishNode(newNode, "JSXMemberExpression");
        }

        return node;
      }; // Parses any type of JSX attribute value.


      _proto.jsxParseAttributeValue = function jsxParseAttributeValue() {
        var node;

        switch (this.state.type) {
          case types.braceL:
            node = this.jsxParseExpressionContainer();

            if (node.expression.type === "JSXEmptyExpression") {
              throw this.raise(node.start, "JSX attributes must only be assigned a non-empty expression");
            } else {
              return node;
            }

          case types.jsxTagStart:
          case types.string:
            return this.parseExprAtom();

          default:
            throw this.raise(this.state.start, "JSX value should be either an expression or a quoted JSX text");
        }
      }; // JSXEmptyExpression is unique type since it doesn't actually parse anything,
      // and so it should start at the end of last read token (left brace) and finish
      // at the beginning of the next one (right brace).


      _proto.jsxParseEmptyExpression = function jsxParseEmptyExpression() {
        var node = this.startNodeAt(this.state.lastTokEnd, this.state.lastTokEndLoc);
        return this.finishNodeAt(node, "JSXEmptyExpression", this.state.start, this.state.startLoc);
      }; // Parse JSX spread child


      _proto.jsxParseSpreadChild = function jsxParseSpreadChild() {
        var node = this.startNode();
        this.expect(types.braceL);
        this.expect(types.ellipsis);
        node.expression = this.parseExpression();
        this.expect(types.braceR);
        return this.finishNode(node, "JSXSpreadChild");
      }; // Parses JSX expression enclosed into curly brackets.


      _proto.jsxParseExpressionContainer = function jsxParseExpressionContainer() {
        var node = this.startNode();
        this.next();

        if (this.match(types.braceR)) {
          node.expression = this.jsxParseEmptyExpression();
        } else {
          node.expression = this.parseExpression();
        }

        this.expect(types.braceR);
        return this.finishNode(node, "JSXExpressionContainer");
      }; // Parses following JSX attribute name-value pair.


      _proto.jsxParseAttribute = function jsxParseAttribute() {
        var node = this.startNode();

        if (this.eat(types.braceL)) {
          this.expect(types.ellipsis);
          node.argument = this.parseMaybeAssign();
          this.expect(types.braceR);
          return this.finishNode(node, "JSXSpreadAttribute");
        }

        node.name = this.jsxParseNamespacedName();
        node.value = this.eat(types.eq) ? this.jsxParseAttributeValue() : null;
        return this.finishNode(node, "JSXAttribute");
      }; // Parses JSX opening tag starting after "<".


      _proto.jsxParseOpeningElementAt = function jsxParseOpeningElementAt(startPos, startLoc) {
        var node = this.startNodeAt(startPos, startLoc);

        if (this.match(types.jsxTagEnd)) {
          this.expect(types.jsxTagEnd);
          return this.finishNode(node, "JSXOpeningFragment");
        }

        node.attributes = [];
        node.name = this.jsxParseElementName();

        while (!this.match(types.slash) && !this.match(types.jsxTagEnd)) {
          node.attributes.push(this.jsxParseAttribute());
        }

        node.selfClosing = this.eat(types.slash);
        this.expect(types.jsxTagEnd);
        return this.finishNode(node, "JSXOpeningElement");
      }; // Parses JSX closing tag starting after "</".


      _proto.jsxParseClosingElementAt = function jsxParseClosingElementAt(startPos, startLoc) {
        var node = this.startNodeAt(startPos, startLoc);

        if (this.match(types.jsxTagEnd)) {
          this.expect(types.jsxTagEnd);
          return this.finishNode(node, "JSXClosingFragment");
        }

        node.name = this.jsxParseElementName();
        this.expect(types.jsxTagEnd);
        return this.finishNode(node, "JSXClosingElement");
      }; // Parses entire JSX element, including it"s opening tag
      // (starting after "<"), attributes, contents and closing tag.


      _proto.jsxParseElementAt = function jsxParseElementAt(startPos, startLoc) {
        var node = this.startNodeAt(startPos, startLoc);
        var children = [];
        var openingElement = this.jsxParseOpeningElementAt(startPos, startLoc);
        var closingElement = null;

        if (!openingElement.selfClosing) {
          contents: for (;;) {
            switch (this.state.type) {
              case types.jsxTagStart:
                startPos = this.state.start;
                startLoc = this.state.startLoc;
                this.next();

                if (this.eat(types.slash)) {
                  closingElement = this.jsxParseClosingElementAt(startPos, startLoc);
                  break contents;
                }

                children.push(this.jsxParseElementAt(startPos, startLoc));
                break;

              case types.jsxText:
                children.push(this.parseExprAtom());
                break;

              case types.braceL:
                if (this.lookahead().type === types.ellipsis) {
                  children.push(this.jsxParseSpreadChild());
                } else {
                  children.push(this.jsxParseExpressionContainer());
                }

                break;
              // istanbul ignore next - should never happen

              default:
                throw this.unexpected();
            }
          }

          if (isFragment(openingElement) && !isFragment(closingElement)) {
            this.raise( // $FlowIgnore
            closingElement.start, "Expected corresponding JSX closing tag for <>");
          } else if (!isFragment(openingElement) && isFragment(closingElement)) {
            this.raise( // $FlowIgnore
            closingElement.start, "Expected corresponding JSX closing tag for <" + getQualifiedJSXName(openingElement.name) + ">");
          } else if (!isFragment(openingElement) && !isFragment(closingElement)) {
            if ( // $FlowIgnore
            getQualifiedJSXName(closingElement.name) !== getQualifiedJSXName(openingElement.name)) {
              this.raise( // $FlowIgnore
              closingElement.start, "Expected corresponding JSX closing tag for <" + getQualifiedJSXName(openingElement.name) + ">");
            }
          }
        }

        if (isFragment(openingElement)) {
          node.openingFragment = openingElement;
          node.closingFragment = closingElement;
        } else {
          node.openingElement = openingElement;
          node.closingElement = closingElement;
        }

        node.children = children;

        if (this.match(types.relational) && this.state.value === "<") {
          this.raise(this.state.start, "Adjacent JSX elements must be wrapped in an enclosing tag. " + "Did you want a JSX fragment <>...</>?");
        }

        return isFragment(openingElement) ? this.finishNode(node, "JSXFragment") : this.finishNode(node, "JSXElement");
      }; // Parses entire JSX element from current position.


      _proto.jsxParseElement = function jsxParseElement() {
        var startPos = this.state.start;
        var startLoc = this.state.startLoc;
        this.next();
        return this.jsxParseElementAt(startPos, startLoc);
      }; // ==================================
      // Overrides
      // ==================================


      _proto.parseExprAtom = function parseExprAtom(refShortHandDefaultPos) {
        if (this.match(types.jsxText)) {
          return this.parseLiteral(this.state.value, "JSXText");
        } else if (this.match(types.jsxTagStart)) {
          return this.jsxParseElement();
        } else {
          return _superClass.prototype.parseExprAtom.call(this, refShortHandDefaultPos);
        }
      };

      _proto.readToken = function readToken(code) {
        if (this.state.inPropertyName) return _superClass.prototype.readToken.call(this, code);
        var context = this.curContext();

        if (context === types$1.j_expr) {
          return this.jsxReadToken();
        }

        if (context === types$1.j_oTag || context === types$1.j_cTag) {
          if (isIdentifierStart(code)) {
            return this.jsxReadWord();
          }

          if (code === 62) {
            ++this.state.pos;
            return this.finishToken(types.jsxTagEnd);
          }

          if ((code === 34 || code === 39) && context === types$1.j_oTag) {
            return this.jsxReadString(code);
          }
        }

        if (code === 60 && this.state.exprAllowed) {
          ++this.state.pos;
          return this.finishToken(types.jsxTagStart);
        }

        return _superClass.prototype.readToken.call(this, code);
      };

      _proto.updateContext = function updateContext(prevType) {
        if (this.match(types.braceL)) {
          var curContext = this.curContext();

          if (curContext === types$1.j_oTag) {
            this.state.context.push(types$1.braceExpression);
          } else if (curContext === types$1.j_expr) {
            this.state.context.push(types$1.templateQuasi);
          } else {
            _superClass.prototype.updateContext.call(this, prevType);
          }

          this.state.exprAllowed = true;
        } else if (this.match(types.slash) && prevType === types.jsxTagStart) {
          this.state.context.length -= 2; // do not consider JSX expr -> JSX open tag -> ... anymore

          this.state.context.push(types$1.j_cTag); // reconsider as closing tag context

          this.state.exprAllowed = false;
        } else {
          return _superClass.prototype.updateContext.call(this, prevType);
        }
      };

      return _class;
    }(superClass)
  );
});

function nonNull(x) {
  if (x == null) {
    // $FlowIgnore
    throw new Error(`Unexpected ${x} value.`);
  }

  return x;
}

function assert(x) {
  if (!x) {
    throw new Error("Assert fail");
  }
}

// Doesn't handle "void" or "null" because those are keywords, not identifiers.
function keywordTypeFromName(value) {
  switch (value) {
    case "any":
      return "TSAnyKeyword";

    case "boolean":
      return "TSBooleanKeyword";

    case "never":
      return "TSNeverKeyword";

    case "number":
      return "TSNumberKeyword";

    case "object":
      return "TSObjectKeyword";

    case "string":
      return "TSStringKeyword";

    case "symbol":
      return "TSSymbolKeyword";

    case "undefined":
      return "TSUndefinedKeyword";

    default:
      return undefined;
  }
}

var typescriptPlugin = (function (superClass) {
  return (
    /*#__PURE__*/
    function (_superClass) {
      _inheritsLoose(_class, _superClass);

      function _class() {
        return _superClass.apply(this, arguments) || this;
      }

      var _proto = _class.prototype;

      _proto.tsIsIdentifier = function tsIsIdentifier() {
        // TODO: actually a bit more complex in TypeScript, but shouldn't matter.
        // See https://github.com/Microsoft/TypeScript/issues/15008
        return this.match(types.name);
      };

      _proto.tsNextTokenCanFollowModifier = function tsNextTokenCanFollowModifier() {
        // Note: TypeScript's implementation is much more complicated because
        // more things are considered modifiers there.
        // This implementation only handles modifiers not handled by babylon itself. And "static".
        // TODO: Would be nice to avoid lookahead. Want a hasLineBreakUpNext() method...
        this.next();
        return !this.hasPrecedingLineBreak() && !this.match(types.parenL) && !this.match(types.colon) && !this.match(types.eq) && !this.match(types.question);
      };
      /** Parses a modifier matching one the given modifier names. */


      _proto.tsParseModifier = function tsParseModifier(allowedModifiers) {
        if (!this.match(types.name)) {
          return undefined;
        }

        var modifier = this.state.value;

        if (allowedModifiers.indexOf(modifier) !== -1 && this.tsTryParse(this.tsNextTokenCanFollowModifier.bind(this))) {
          return modifier;
        }

        return undefined;
      };

      _proto.tsIsListTerminator = function tsIsListTerminator(kind) {
        switch (kind) {
          case "EnumMembers":
          case "TypeMembers":
            return this.match(types.braceR);

          case "HeritageClauseElement":
            return this.match(types.braceL);

          case "TupleElementTypes":
            return this.match(types.bracketR);

          case "TypeParametersOrArguments":
            return this.isRelational(">");
        }

        throw new Error("Unreachable");
      };

      _proto.tsParseList = function tsParseList(kind, parseElement) {
        var result = [];

        while (!this.tsIsListTerminator(kind)) {
          // Skipping "parseListElement" from the TS source since that's just for error handling.
          result.push(parseElement());
        }

        return result;
      };

      _proto.tsParseDelimitedList = function tsParseDelimitedList(kind, parseElement) {
        return nonNull(this.tsParseDelimitedListWorker(kind, parseElement,
        /* expectSuccess */
        true));
      };

      _proto.tsTryParseDelimitedList = function tsTryParseDelimitedList(kind, parseElement) {
        return this.tsParseDelimitedListWorker(kind, parseElement,
        /* expectSuccess */
        false);
      };
      /**
       * If !expectSuccess, returns undefined instead of failing to parse.
       * If expectSuccess, parseElement should always return a defined value.
       */


      _proto.tsParseDelimitedListWorker = function tsParseDelimitedListWorker(kind, parseElement, expectSuccess) {
        var result = [];

        while (true) {
          if (this.tsIsListTerminator(kind)) {
            break;
          }

          var element = parseElement();

          if (element == null) {
            return undefined;
          }

          result.push(element);

          if (this.eat(types.comma)) {
            continue;
          }

          if (this.tsIsListTerminator(kind)) {
            break;
          }

          if (expectSuccess) {
            // This will fail with an error about a missing comma
            this.expect(types.comma);
          }

          return undefined;
        }

        return result;
      };

      _proto.tsParseBracketedList = function tsParseBracketedList(kind, parseElement, bracket, skipFirstToken) {
        if (!skipFirstToken) {
          if (bracket) {
            this.expect(types.bracketL);
          } else {
            this.expectRelational("<");
          }
        }

        var result = this.tsParseDelimitedList(kind, parseElement);

        if (bracket) {
          this.expect(types.bracketR);
        } else {
          this.expectRelational(">");
        }

        return result;
      };

      _proto.tsParseEntityName = function tsParseEntityName(allowReservedWords) {
        var entity = this.parseIdentifier();

        while (this.eat(types.dot)) {
          var node = this.startNodeAtNode(entity);
          node.left = entity;
          node.right = this.parseIdentifier(allowReservedWords);
          entity = this.finishNode(node, "TSQualifiedName");
        }

        return entity;
      };

      _proto.tsParseTypeReference = function tsParseTypeReference() {
        var node = this.startNode();
        node.typeName = this.tsParseEntityName(
        /* allowReservedWords */
        false);

        if (!this.hasPrecedingLineBreak() && this.isRelational("<")) {
          node.typeParameters = this.tsParseTypeArguments();
        }

        return this.finishNode(node, "TSTypeReference");
      };

      _proto.tsParseThisTypePredicate = function tsParseThisTypePredicate(lhs) {
        this.next();
        var node = this.startNode();
        node.parameterName = lhs;
        node.typeAnnotation = this.tsParseTypeAnnotation(
        /* eatColon */
        false);
        return this.finishNode(node, "TSTypePredicate");
      };

      _proto.tsParseThisTypeNode = function tsParseThisTypeNode() {
        var node = this.startNode();
        this.next();
        return this.finishNode(node, "TSThisType");
      };

      _proto.tsParseTypeQuery = function tsParseTypeQuery() {
        var node = this.startNode();
        this.expect(types._typeof);
        node.exprName = this.tsParseEntityName(
        /* allowReservedWords */
        true);
        return this.finishNode(node, "TSTypeQuery");
      };

      _proto.tsParseTypeParameter = function tsParseTypeParameter() {
        var node = this.startNode();
        node.name = this.parseIdentifierName(node.start);
        node.constraint = this.tsEatThenParseType(types._extends);
        node.default = this.tsEatThenParseType(types.eq);
        return this.finishNode(node, "TSTypeParameter");
      };

      _proto.tsTryParseTypeParameters = function tsTryParseTypeParameters() {
        if (this.isRelational("<")) {
          return this.tsParseTypeParameters();
        }
      };

      _proto.tsParseTypeParameters = function tsParseTypeParameters() {
        var node = this.startNode();

        if (this.isRelational("<") || this.match(types.jsxTagStart)) {
          this.next();
        } else {
          this.unexpected();
        }

        node.params = this.tsParseBracketedList("TypeParametersOrArguments", this.tsParseTypeParameter.bind(this),
        /* bracket */
        false,
        /* skipFirstToken */
        true);
        return this.finishNode(node, "TSTypeParameterDeclaration");
      }; // Note: In TypeScript implementation we must provide `yieldContext` and `awaitContext`,
      // but here it's always false, because this is only used for types.


      _proto.tsFillSignature = function tsFillSignature(returnToken, signature) {
        // Arrow fns *must* have return token (`=>`). Normal functions can omit it.
        var returnTokenRequired = returnToken === types.arrow;
        signature.typeParameters = this.tsTryParseTypeParameters();
        this.expect(types.parenL);
        signature.parameters = this.tsParseBindingListForSignature();

        if (returnTokenRequired) {
          signature.typeAnnotation = this.tsParseTypeOrTypePredicateAnnotation(returnToken);
        } else if (this.match(returnToken)) {
          signature.typeAnnotation = this.tsParseTypeOrTypePredicateAnnotation(returnToken);
        }
      };

      _proto.tsParseBindingListForSignature = function tsParseBindingListForSignature() {
        var _this = this;

        return this.parseBindingList(types.parenR).map(function (pattern) {
          if (pattern.type !== "Identifier" && pattern.type !== "RestElement") {
            throw _this.unexpected(pattern.start, "Name in a signature must be an Identifier.");
          }

          return pattern;
        });
      };

      _proto.tsParseTypeMemberSemicolon = function tsParseTypeMemberSemicolon() {
        if (!this.eat(types.comma)) {
          this.semicolon();
        }
      };

      _proto.tsParseSignatureMember = function tsParseSignatureMember(kind) {
        var node = this.startNode();

        if (kind === "TSConstructSignatureDeclaration") {
          this.expect(types._new);
        }

        this.tsFillSignature(types.colon, node);
        this.tsParseTypeMemberSemicolon();
        return this.finishNode(node, kind);
      };

      _proto.tsIsUnambiguouslyIndexSignature = function tsIsUnambiguouslyIndexSignature() {
        this.next(); // Skip '{'

        return this.eat(types.name) && this.match(types.colon);
      };

      _proto.tsTryParseIndexSignature = function tsTryParseIndexSignature(node) {
        if (!(this.match(types.bracketL) && this.tsLookAhead(this.tsIsUnambiguouslyIndexSignature.bind(this)))) {
          return undefined;
        }

        this.expect(types.bracketL);
        var id = this.parseIdentifier();
        this.expect(types.colon);
        id.typeAnnotation = this.tsParseTypeAnnotation(
        /* eatColon */
        false);
        this.expect(types.bracketR);
        node.parameters = [id];
        var type = this.tsTryParseTypeAnnotation();
        if (type) node.typeAnnotation = type;
        this.tsParseTypeMemberSemicolon();
        return this.finishNode(node, "TSIndexSignature");
      };

      _proto.tsParsePropertyOrMethodSignature = function tsParsePropertyOrMethodSignature(node, readonly) {
        this.parsePropertyName(node);
        if (this.eat(types.question)) node.optional = true;
        var nodeAny = node;

        if (!readonly && (this.match(types.parenL) || this.isRelational("<"))) {
          var method = nodeAny;
          this.tsFillSignature(types.colon, method);
          this.tsParseTypeMemberSemicolon();
          return this.finishNode(method, "TSMethodSignature");
        } else {
          var property = nodeAny;
          if (readonly) property.readonly = true;
          var type = this.tsTryParseTypeAnnotation();
          if (type) property.typeAnnotation = type;
          this.tsParseTypeMemberSemicolon();
          return this.finishNode(property, "TSPropertySignature");
        }
      };

      _proto.tsParseTypeMember = function tsParseTypeMember() {
        if (this.match(types.parenL) || this.isRelational("<")) {
          return this.tsParseSignatureMember("TSCallSignatureDeclaration");
        }

        if (this.match(types._new) && this.tsLookAhead(this.tsIsStartOfConstructSignature.bind(this))) {
          return this.tsParseSignatureMember("TSConstructSignatureDeclaration");
        } // Instead of fullStart, we create a node here.


        var node = this.startNode();
        var readonly = !!this.tsParseModifier(["readonly"]);
        var idx = this.tsTryParseIndexSignature(node);

        if (idx) {
          if (readonly) node.readonly = true;
          return idx;
        }

        return this.tsParsePropertyOrMethodSignature(node, readonly);
      };

      _proto.tsIsStartOfConstructSignature = function tsIsStartOfConstructSignature() {
        this.next();
        return this.match(types.parenL) || this.isRelational("<");
      };

      _proto.tsParseTypeLiteral = function tsParseTypeLiteral() {
        var node = this.startNode();
        node.members = this.tsParseObjectTypeMembers();
        return this.finishNode(node, "TSTypeLiteral");
      };

      _proto.tsParseObjectTypeMembers = function tsParseObjectTypeMembers() {
        this.expect(types.braceL);
        var members = this.tsParseList("TypeMembers", this.tsParseTypeMember.bind(this));
        this.expect(types.braceR);
        return members;
      };

      _proto.tsIsStartOfMappedType = function tsIsStartOfMappedType() {
        this.next();

        if (this.isContextual("readonly")) {
          this.next();
        }

        if (!this.match(types.bracketL)) {
          return false;
        }

        this.next();

        if (!this.tsIsIdentifier()) {
          return false;
        }

        this.next();
        return this.match(types._in);
      };

      _proto.tsParseMappedTypeParameter = function tsParseMappedTypeParameter() {
        var node = this.startNode();
        node.name = this.parseIdentifierName(node.start);
        node.constraint = this.tsExpectThenParseType(types._in);
        return this.finishNode(node, "TSTypeParameter");
      };

      _proto.tsParseMappedType = function tsParseMappedType() {
        var node = this.startNode();
        this.expect(types.braceL);

        if (this.eatContextual("readonly")) {
          node.readonly = true;
        }

        this.expect(types.bracketL);
        node.typeParameter = this.tsParseMappedTypeParameter();
        this.expect(types.bracketR);

        if (this.eat(types.question)) {
          node.optional = true;
        }

        node.typeAnnotation = this.tsTryParseType();
        this.semicolon();
        this.expect(types.braceR);
        return this.finishNode(node, "TSMappedType");
      };

      _proto.tsParseTupleType = function tsParseTupleType() {
        var node = this.startNode();
        node.elementTypes = this.tsParseBracketedList("TupleElementTypes", this.tsParseType.bind(this),
        /* bracket */
        true,
        /* skipFirstToken */
        false);
        return this.finishNode(node, "TSTupleType");
      };

      _proto.tsParseParenthesizedType = function tsParseParenthesizedType() {
        var node = this.startNode();
        this.expect(types.parenL);
        node.typeAnnotation = this.tsParseType();
        this.expect(types.parenR);
        return this.finishNode(node, "TSParenthesizedType");
      };

      _proto.tsParseFunctionOrConstructorType = function tsParseFunctionOrConstructorType(type) {
        var node = this.startNode();

        if (type === "TSConstructorType") {
          this.expect(types._new);
        }

        this.tsFillSignature(types.arrow, node);
        return this.finishNode(node, type);
      };

      _proto.tsParseLiteralTypeNode = function tsParseLiteralTypeNode() {
        var _this2 = this;

        var node = this.startNode();

        node.literal = function () {
          switch (_this2.state.type) {
            case types.num:
              return _this2.parseLiteral(_this2.state.value, "NumericLiteral");

            case types.string:
              return _this2.parseLiteral(_this2.state.value, "StringLiteral");

            case types._true:
            case types._false:
              return _this2.parseBooleanLiteral();

            default:
              throw _this2.unexpected();
          }
        }();

        return this.finishNode(node, "TSLiteralType");
      };

      _proto.tsParseNonArrayType = function tsParseNonArrayType() {
        switch (this.state.type) {
          case types.name:
          case types._void:
          case types._null:
            {
              var type = this.match(types._void) ? "TSVoidKeyword" : this.match(types._null) ? "TSNullKeyword" : keywordTypeFromName(this.state.value);

              if (type !== undefined && this.lookahead().type !== types.dot) {
                var node = this.startNode();
                this.next();
                return this.finishNode(node, type);
              }

              return this.tsParseTypeReference();
            }

          case types.string:
          case types.num:
          case types._true:
          case types._false:
            return this.tsParseLiteralTypeNode();

          case types.plusMin:
            if (this.state.value === "-") {
              var _node = this.startNode();

              this.next();

              if (!this.match(types.num)) {
                throw this.unexpected();
              }

              _node.literal = this.parseLiteral(-this.state.value, "NumericLiteral", _node.start, _node.loc.start);
              return this.finishNode(_node, "TSLiteralType");
            }

            break;

          case types._this:
            {
              var thisKeyword = this.tsParseThisTypeNode();

              if (this.isContextual("is") && !this.hasPrecedingLineBreak()) {
                return this.tsParseThisTypePredicate(thisKeyword);
              } else {
                return thisKeyword;
              }
            }

          case types._typeof:
            return this.tsParseTypeQuery();

          case types.braceL:
            return this.tsLookAhead(this.tsIsStartOfMappedType.bind(this)) ? this.tsParseMappedType() : this.tsParseTypeLiteral();

          case types.bracketL:
            return this.tsParseTupleType();

          case types.parenL:
            return this.tsParseParenthesizedType();
        }

        throw this.unexpected();
      };

      _proto.tsParseArrayTypeOrHigher = function tsParseArrayTypeOrHigher() {
        var type = this.tsParseNonArrayType();

        while (!this.hasPrecedingLineBreak() && this.eat(types.bracketL)) {
          if (this.match(types.bracketR)) {
            var node = this.startNodeAtNode(type);
            node.elementType = type;
            this.expect(types.bracketR);
            type = this.finishNode(node, "TSArrayType");
          } else {
            var _node2 = this.startNodeAtNode(type);

            _node2.objectType = type;
            _node2.indexType = this.tsParseType();
            this.expect(types.bracketR);
            type = this.finishNode(_node2, "TSIndexedAccessType");
          }
        }

        return type;
      };

      _proto.tsParseTypeOperator = function tsParseTypeOperator(operator) {
        var node = this.startNode();
        this.expectContextual(operator);
        node.operator = operator;
        node.typeAnnotation = this.tsParseTypeOperatorOrHigher();
        return this.finishNode(node, "TSTypeOperator");
      };

      _proto.tsParseTypeOperatorOrHigher = function tsParseTypeOperatorOrHigher() {
        var _this3 = this;

        var operator = ["keyof", "unique"].find(function (kw) {
          return _this3.isContextual(kw);
        });
        return operator ? this.tsParseTypeOperator(operator) : this.tsParseArrayTypeOrHigher();
      };

      _proto.tsParseUnionOrIntersectionType = function tsParseUnionOrIntersectionType(kind, parseConstituentType, operator) {
        this.eat(operator);
        var type = parseConstituentType();

        if (this.match(operator)) {
          var types$$1 = [type];

          while (this.eat(operator)) {
            types$$1.push(parseConstituentType());
          }

          var node = this.startNodeAtNode(type);
          node.types = types$$1;
          type = this.finishNode(node, kind);
        }

        return type;
      };

      _proto.tsParseIntersectionTypeOrHigher = function tsParseIntersectionTypeOrHigher() {
        return this.tsParseUnionOrIntersectionType("TSIntersectionType", this.tsParseTypeOperatorOrHigher.bind(this), types.bitwiseAND);
      };

      _proto.tsParseUnionTypeOrHigher = function tsParseUnionTypeOrHigher() {
        return this.tsParseUnionOrIntersectionType("TSUnionType", this.tsParseIntersectionTypeOrHigher.bind(this), types.bitwiseOR);
      };

      _proto.tsIsStartOfFunctionType = function tsIsStartOfFunctionType() {
        if (this.isRelational("<")) {
          return true;
        }

        return this.match(types.parenL) && this.tsLookAhead(this.tsIsUnambiguouslyStartOfFunctionType.bind(this));
      };

      _proto.tsSkipParameterStart = function tsSkipParameterStart() {
        if (this.match(types.name) || this.match(types._this)) {
          this.next();
          return true;
        }

        return false;
      };

      _proto.tsIsUnambiguouslyStartOfFunctionType = function tsIsUnambiguouslyStartOfFunctionType() {
        this.next();

        if (this.match(types.parenR) || this.match(types.ellipsis)) {
          // ( )
          // ( ...
          return true;
        }

        if (this.tsSkipParameterStart()) {
          if (this.match(types.colon) || this.match(types.comma) || this.match(types.question) || this.match(types.eq)) {
            // ( xxx :
            // ( xxx ,
            // ( xxx ?
            // ( xxx =
            return true;
          }

          if (this.match(types.parenR)) {
            this.next();

            if (this.match(types.arrow)) {
              // ( xxx ) =>
              return true;
            }
          }
        }

        return false;
      };

      _proto.tsParseTypeOrTypePredicateAnnotation = function tsParseTypeOrTypePredicateAnnotation(returnToken) {
        var _this4 = this;

        return this.tsInType(function () {
          var t = _this4.startNode();

          _this4.expect(returnToken);

          var typePredicateVariable = _this4.tsIsIdentifier() && _this4.tsTryParse(_this4.tsParseTypePredicatePrefix.bind(_this4));

          if (!typePredicateVariable) {
            return _this4.tsParseTypeAnnotation(
            /* eatColon */
            false, t);
          }

          var type = _this4.tsParseTypeAnnotation(
          /* eatColon */
          false);

          var node = _this4.startNodeAtNode(typePredicateVariable);

          node.parameterName = typePredicateVariable;
          node.typeAnnotation = type;
          t.typeAnnotation = _this4.finishNode(node, "TSTypePredicate");
          return _this4.finishNode(t, "TSTypeAnnotation");
        });
      };

      _proto.tsTryParseTypeOrTypePredicateAnnotation = function tsTryParseTypeOrTypePredicateAnnotation() {
        return this.match(types.colon) ? this.tsParseTypeOrTypePredicateAnnotation(types.colon) : undefined;
      };

      _proto.tsTryParseTypeAnnotation = function tsTryParseTypeAnnotation() {
        return this.match(types.colon) ? this.tsParseTypeAnnotation() : undefined;
      };

      _proto.tsTryParseType = function tsTryParseType() {
        return this.tsEatThenParseType(types.colon);
      };

      _proto.tsParseTypePredicatePrefix = function tsParseTypePredicatePrefix() {
        var id = this.parseIdentifier();

        if (this.isContextual("is") && !this.hasPrecedingLineBreak()) {
          this.next();
          return id;
        }
      };

      _proto.tsParseTypeAnnotation = function tsParseTypeAnnotation(eatColon, t) {
        var _this5 = this;

        if (eatColon === void 0) {
          eatColon = true;
        }

        if (t === void 0) {
          t = this.startNode();
        }

        this.tsInType(function () {
          if (eatColon) _this5.expect(types.colon);
          t.typeAnnotation = _this5.tsParseType();
        });
        return this.finishNode(t, "TSTypeAnnotation");
      };
      /** Be sure to be in a type context before calling this, using `tsInType`. */


      _proto.tsParseType = function tsParseType() {
        // Need to set `state.inType` so that we don't parse JSX in a type context.
        assert(this.state.inType);

        if (this.tsIsStartOfFunctionType()) {
          return this.tsParseFunctionOrConstructorType("TSFunctionType");
        }

        if (this.match(types._new)) {
          // As in `new () => Date`
          return this.tsParseFunctionOrConstructorType("TSConstructorType");
        }

        return this.tsParseUnionTypeOrHigher();
      };

      _proto.tsParseTypeAssertion = function tsParseTypeAssertion() {
        var _this6 = this;

        var node = this.startNode(); // Not actually necessary to set state.inType because we never reach here if JSX plugin is enabled,
        // but need `tsInType` to satisfy the assertion in `tsParseType`.

        node.typeAnnotation = this.tsInType(function () {
          return _this6.tsParseType();
        });
        this.expectRelational(">");
        node.expression = this.parseMaybeUnary();
        return this.finishNode(node, "TSTypeAssertion");
      };

      _proto.tsTryParseTypeArgumentsInExpression = function tsTryParseTypeArgumentsInExpression() {
        var _this7 = this;

        return this.tsTryParseAndCatch(function () {
          var res = _this7.tsParseTypeArguments();

          _this7.expect(types.parenL);

          return res;
        });
      };

      _proto.tsParseHeritageClause = function tsParseHeritageClause() {
        return this.tsParseDelimitedList("HeritageClauseElement", this.tsParseExpressionWithTypeArguments.bind(this));
      };

      _proto.tsParseExpressionWithTypeArguments = function tsParseExpressionWithTypeArguments() {
        var node = this.startNode(); // Note: TS uses parseLeftHandSideExpressionOrHigher,
        // then has grammar errors later if it's not an EntityName.

        node.expression = this.tsParseEntityName(
        /* allowReservedWords */
        false);

        if (this.isRelational("<")) {
          node.typeParameters = this.tsParseTypeArguments();
        }

        return this.finishNode(node, "TSExpressionWithTypeArguments");
      };

      _proto.tsParseInterfaceDeclaration = function tsParseInterfaceDeclaration(node) {
        node.id = this.parseIdentifier();
        node.typeParameters = this.tsTryParseTypeParameters();

        if (this.eat(types._extends)) {
          node.extends = this.tsParseHeritageClause();
        }

        var body = this.startNode();
        body.body = this.tsParseObjectTypeMembers();
        node.body = this.finishNode(body, "TSInterfaceBody");
        return this.finishNode(node, "TSInterfaceDeclaration");
      };

      _proto.tsParseTypeAliasDeclaration = function tsParseTypeAliasDeclaration(node) {
        node.id = this.parseIdentifier();
        node.typeParameters = this.tsTryParseTypeParameters();
        node.typeAnnotation = this.tsExpectThenParseType(types.eq);
        this.semicolon();
        return this.finishNode(node, "TSTypeAliasDeclaration");
      };
      /**
       * Runs `cb` in a type context.
       * This should be called one token *before* the first type token,
       * so that the call to `next()` is run in type context.
       */


      _proto.tsInType = function tsInType(cb) {
        var oldInType = this.state.inType;
        this.state.inType = true;

        try {
          return cb();
        } finally {
          this.state.inType = oldInType;
        }
      };

      _proto.tsEatThenParseType = function tsEatThenParseType(token) {
        return !this.match(token) ? undefined : this.tsNextThenParseType();
      };

      _proto.tsExpectThenParseType = function tsExpectThenParseType(token) {
        var _this8 = this;

        return this.tsDoThenParseType(function () {
          return _this8.expect(token);
        });
      };

      _proto.tsNextThenParseType = function tsNextThenParseType() {
        var _this9 = this;

        return this.tsDoThenParseType(function () {
          return _this9.next();
        });
      };

      _proto.tsDoThenParseType = function tsDoThenParseType(cb) {
        var _this10 = this;

        return this.tsInType(function () {
          cb();
          return _this10.tsParseType();
        });
      };

      _proto.tsParseEnumMember = function tsParseEnumMember() {
        var node = this.startNode(); // Computed property names are grammar errors in an enum, so accept just string literal or identifier.

        node.id = this.match(types.string) ? this.parseLiteral(this.state.value, "StringLiteral") : this.parseIdentifier(
        /* liberal */
        true);

        if (this.eat(types.eq)) {
          node.initializer = this.parseMaybeAssign();
        }

        return this.finishNode(node, "TSEnumMember");
      };

      _proto.tsParseEnumDeclaration = function tsParseEnumDeclaration(node, isConst) {
        if (isConst) node.const = true;
        node.id = this.parseIdentifier();
        this.expect(types.braceL);
        node.members = this.tsParseDelimitedList("EnumMembers", this.tsParseEnumMember.bind(this));
        this.expect(types.braceR);
        return this.finishNode(node, "TSEnumDeclaration");
      };

      _proto.tsParseModuleBlock = function tsParseModuleBlock() {
        var node = this.startNode();
        this.expect(types.braceL); // Inside of a module block is considered "top-level", meaning it can have imports and exports.

        this.parseBlockOrModuleBlockBody(node.body = [],
        /* directives */
        undefined,
        /* topLevel */
        true,
        /* end */
        types.braceR);
        return this.finishNode(node, "TSModuleBlock");
      };

      _proto.tsParseModuleOrNamespaceDeclaration = function tsParseModuleOrNamespaceDeclaration(node) {
        node.id = this.parseIdentifier();

        if (this.eat(types.dot)) {
          var inner = this.startNode();
          this.tsParseModuleOrNamespaceDeclaration(inner);
          node.body = inner;
        } else {
          node.body = this.tsParseModuleBlock();
        }

        return this.finishNode(node, "TSModuleDeclaration");
      };

      _proto.tsParseAmbientExternalModuleDeclaration = function tsParseAmbientExternalModuleDeclaration(node) {
        if (this.isContextual("global")) {
          node.global = true;
          node.id = this.parseIdentifier();
        } else if (this.match(types.string)) {
          node.id = this.parseExprAtom();
        } else {
          this.unexpected();
        }

        if (this.match(types.braceL)) {
          node.body = this.tsParseModuleBlock();
        } else {
          this.semicolon();
        }

        return this.finishNode(node, "TSModuleDeclaration");
      };

      _proto.tsParseImportEqualsDeclaration = function tsParseImportEqualsDeclaration(node, isExport) {
        node.isExport = isExport || false;
        node.id = this.parseIdentifier();
        this.expect(types.eq);
        node.moduleReference = this.tsParseModuleReference();
        this.semicolon();
        return this.finishNode(node, "TSImportEqualsDeclaration");
      };

      _proto.tsIsExternalModuleReference = function tsIsExternalModuleReference() {
        return this.isContextual("require") && this.lookahead().type === types.parenL;
      };

      _proto.tsParseModuleReference = function tsParseModuleReference() {
        return this.tsIsExternalModuleReference() ? this.tsParseExternalModuleReference() : this.tsParseEntityName(
        /* allowReservedWords */
        false);
      };

      _proto.tsParseExternalModuleReference = function tsParseExternalModuleReference() {
        var node = this.startNode();
        this.expectContextual("require");
        this.expect(types.parenL);

        if (!this.match(types.string)) {
          throw this.unexpected();
        }

        node.expression = this.parseLiteral(this.state.value, "StringLiteral");
        this.expect(types.parenR);
        return this.finishNode(node, "TSExternalModuleReference");
      }; // Utilities


      _proto.tsLookAhead = function tsLookAhead(f) {
        var state = this.state.clone();
        var res = f();
        this.state = state;
        return res;
      };

      _proto.tsTryParseAndCatch = function tsTryParseAndCatch(f) {
        var state = this.state.clone();

        try {
          return f();
        } catch (e) {
          if (e instanceof SyntaxError) {
            this.state = state;
            return undefined;
          }

          throw e;
        }
      };

      _proto.tsTryParse = function tsTryParse(f) {
        var state = this.state.clone();
        var result = f();

        if (result !== undefined && result !== false) {
          return result;
        } else {
          this.state = state;
          return undefined;
        }
      };

      _proto.nodeWithSamePosition = function nodeWithSamePosition(original, type) {
        var node = this.startNodeAtNode(original);
        node.type = type;
        node.end = original.end;
        node.loc.end = original.loc.end;

        if (original.leadingComments) {
          node.leadingComments = original.leadingComments;
        }

        if (original.trailingComments) {
          node.trailingComments = original.trailingComments;
        }

        if (original.innerComments) node.innerComments = original.innerComments;
        return node;
      };

      _proto.tsTryParseDeclare = function tsTryParseDeclare(nany) {
        switch (this.state.type) {
          case types._function:
            this.next();
            return this.parseFunction(nany,
            /* isStatement */
            true);

          case types._class:
            return this.parseClass(nany,
            /* isStatement */
            true,
            /* optionalId */
            false);

          case types._const:
            if (this.match(types._const) && this.isLookaheadContextual("enum")) {
              // `const enum = 0;` not allowed because "enum" is a strict mode reserved word.
              this.expect(types._const);
              this.expectContextual("enum");
              return this.tsParseEnumDeclaration(nany,
              /* isConst */
              true);
            }

          // falls through

          case types._var:
          case types._let:
            return this.parseVarStatement(nany, this.state.type);

          case types.name:
            {
              var value = this.state.value;

              if (value === "global") {
                return this.tsParseAmbientExternalModuleDeclaration(nany);
              } else {
                return this.tsParseDeclaration(nany, value,
                /* next */
                true);
              }
            }
        }
      }; // Note: this won't be called unless the keyword is allowed in `shouldParseExportDeclaration`.


      _proto.tsTryParseExportDeclaration = function tsTryParseExportDeclaration() {
        return this.tsParseDeclaration(this.startNode(), this.state.value,
        /* next */
        true);
      };

      _proto.tsParseExpressionStatement = function tsParseExpressionStatement(node, expr) {
        switch (expr.name) {
          case "declare":
            {
              var declaration = this.tsTryParseDeclare(node);

              if (declaration) {
                declaration.declare = true;
                return declaration;
              }

              break;
            }

          case "global":
            // `global { }` (with no `declare`) may appear inside an ambient module declaration.
            // Would like to use tsParseAmbientExternalModuleDeclaration here, but already ran past "global".
            if (this.match(types.braceL)) {
              var mod = node;
              mod.global = true;
              mod.id = expr;
              mod.body = this.tsParseModuleBlock();
              return this.finishNode(mod, "TSModuleDeclaration");
            }

            break;

          default:
            return this.tsParseDeclaration(node, expr.name,
            /* next */
            false);
        }
      }; // Common to tsTryParseDeclare, tsTryParseExportDeclaration, and tsParseExpressionStatement.


      _proto.tsParseDeclaration = function tsParseDeclaration(node, value, next) {
        switch (value) {
          case "abstract":
            if (next || this.match(types._class)) {
              var cls = node;
              cls.abstract = true;
              if (next) this.next();
              return this.parseClass(cls,
              /* isStatement */
              true,
              /* optionalId */
              false);
            }

            break;

          case "enum":
            if (next || this.match(types.name)) {
              if (next) this.next();
              return this.tsParseEnumDeclaration(node,
              /* isConst */
              false);
            }

            break;

          case "interface":
            if (next || this.match(types.name)) {
              if (next) this.next();
              return this.tsParseInterfaceDeclaration(node);
            }

            break;

          case "module":
            if (next) this.next();

            if (this.match(types.string)) {
              return this.tsParseAmbientExternalModuleDeclaration(node);
            } else if (next || this.match(types.name)) {
              return this.tsParseModuleOrNamespaceDeclaration(node);
            }

            break;

          case "namespace":
            if (next || this.match(types.name)) {
              if (next) this.next();
              return this.tsParseModuleOrNamespaceDeclaration(node);
            }

            break;

          case "type":
            if (next || this.match(types.name)) {
              if (next) this.next();
              return this.tsParseTypeAliasDeclaration(node);
            }

            break;
        }
      };

      _proto.tsTryParseGenericAsyncArrowFunction = function tsTryParseGenericAsyncArrowFunction(startPos, startLoc) {
        var _this11 = this;

        var res = this.tsTryParseAndCatch(function () {
          var node = _this11.startNodeAt(startPos, startLoc);

          node.typeParameters = _this11.tsParseTypeParameters(); // Don't use overloaded parseFunctionParams which would look for "<" again.

          _superClass.prototype.parseFunctionParams.call(_this11, node);

          node.returnType = _this11.tsTryParseTypeOrTypePredicateAnnotation();

          _this11.expect(types.arrow);

          return node;
        });

        if (!res) {
          return undefined;
        }

        res.id = null;
        res.generator = false;
        res.expression = true; // May be set again by parseFunctionBody.

        res.async = true;
        this.parseFunctionBody(res, true);
        return this.finishNode(res, "ArrowFunctionExpression");
      };

      _proto.tsParseTypeArguments = function tsParseTypeArguments() {
        var _this12 = this;

        var node = this.startNode();
        node.params = this.tsInType(function () {
          _this12.expectRelational("<");

          return _this12.tsParseDelimitedList("TypeParametersOrArguments", _this12.tsParseType.bind(_this12));
        });
        this.expectRelational(">");
        return this.finishNode(node, "TSTypeParameterInstantiation");
      };

      _proto.tsIsDeclarationStart = function tsIsDeclarationStart() {
        if (this.match(types.name)) {
          switch (this.state.value) {
            case "abstract":
            case "declare":
            case "enum":
            case "interface":
            case "module":
            case "namespace":
            case "type":
              return true;
          }
        }

        return false;
      }; // ======================================================
      // OVERRIDES
      // ======================================================


      _proto.isExportDefaultSpecifier = function isExportDefaultSpecifier() {
        if (this.tsIsDeclarationStart()) return false;
        return _superClass.prototype.isExportDefaultSpecifier.call(this);
      };

      _proto.parseAssignableListItem = function parseAssignableListItem(allowModifiers, decorators) {
        var accessibility;
        var readonly = false;

        if (allowModifiers) {
          accessibility = this.parseAccessModifier();
          readonly = !!this.tsParseModifier(["readonly"]);
        }

        var left = this.parseMaybeDefault();
        this.parseAssignableListItemTypes(left);
        var elt = this.parseMaybeDefault(left.start, left.loc.start, left);

        if (accessibility || readonly) {
          var pp = this.startNodeAtNode(elt);

          if (decorators.length) {
            pp.decorators = decorators;
          }

          if (accessibility) pp.accessibility = accessibility;
          if (readonly) pp.readonly = readonly;

          if (elt.type !== "Identifier" && elt.type !== "AssignmentPattern") {
            throw this.raise(pp.start, "A parameter property may not be declared using a binding pattern.");
          }

          pp.parameter = elt;
          return this.finishNode(pp, "TSParameterProperty");
        } else {
          if (decorators.length) {
            left.decorators = decorators;
          }

          return elt;
        }
      };

      _proto.parseFunctionBodyAndFinish = function parseFunctionBodyAndFinish(node, type, allowExpressionBody) {
        // For arrow functions, `parseArrow` handles the return type itself.
        if (!allowExpressionBody && this.match(types.colon)) {
          node.returnType = this.tsParseTypeOrTypePredicateAnnotation(types.colon);
        }

        var bodilessType = type === "FunctionDeclaration" ? "TSDeclareFunction" : type === "ClassMethod" ? "TSDeclareMethod" : undefined;

        if (bodilessType && !this.match(types.braceL) && this.isLineTerminator()) {
          this.finishNode(node, bodilessType);
          return;
        }

        _superClass.prototype.parseFunctionBodyAndFinish.call(this, node, type, allowExpressionBody);
      };

      _proto.parseSubscript = function parseSubscript(base, startPos, startLoc, noCalls, state) {
        if (!this.hasPrecedingLineBreak() && this.eat(types.bang)) {
          var nonNullExpression = this.startNodeAt(startPos, startLoc);
          nonNullExpression.expression = base;
          return this.finishNode(nonNullExpression, "TSNonNullExpression");
        }

        if (!noCalls && this.isRelational("<")) {
          if (this.atPossibleAsync(base)) {
            // Almost certainly this is a generic async function `async <T>() => ...
            // But it might be a call with a type argument `async<T>();`
            var asyncArrowFn = this.tsTryParseGenericAsyncArrowFunction(startPos, startLoc);

            if (asyncArrowFn) {
              return asyncArrowFn;
            }
          }

          var node = this.startNodeAt(startPos, startLoc);
          node.callee = base; // May be passing type arguments. But may just be the `<` operator.

          var typeArguments = this.tsTryParseTypeArgumentsInExpression(); // Also eats the "("

          if (typeArguments) {
            // possibleAsync always false here, because we would have handled it above.
            // $FlowIgnore (won't be any undefined arguments)
            node.arguments = this.parseCallExpressionArguments(types.parenR,
            /* possibleAsync */
            false);
            node.typeParameters = typeArguments;
            return this.finishCallExpression(node);
          }
        }

        return _superClass.prototype.parseSubscript.call(this, base, startPos, startLoc, noCalls, state);
      };

      _proto.parseNewArguments = function parseNewArguments(node) {
        var _this13 = this;

        if (this.isRelational("<")) {
          // tsTryParseAndCatch is expensive, so avoid if not necessary.
          // 99% certain this is `new C<T>();`. But may be `new C < T;`, which is also legal.
          var typeParameters = this.tsTryParseAndCatch(function () {
            var args = _this13.tsParseTypeArguments();

            if (!_this13.match(types.parenL)) _this13.unexpected();
            return args;
          });

          if (typeParameters) {
            node.typeParameters = typeParameters;
          }
        }

        _superClass.prototype.parseNewArguments.call(this, node);
      };

      _proto.parseExprOp = function parseExprOp(left, leftStartPos, leftStartLoc, minPrec, noIn) {
        if (nonNull(types._in.binop) > minPrec && !this.hasPrecedingLineBreak() && this.isContextual("as")) {
          var node = this.startNodeAt(leftStartPos, leftStartLoc);
          node.expression = left;
          node.typeAnnotation = this.tsNextThenParseType();
          this.finishNode(node, "TSAsExpression");
          return this.parseExprOp(node, leftStartPos, leftStartLoc, minPrec, noIn);
        }

        return _superClass.prototype.parseExprOp.call(this, left, leftStartPos, leftStartLoc, minPrec, noIn);
      };

      _proto.checkReservedWord = function checkReservedWord(word, startLoc, checkKeywords, // eslint-disable-next-line no-unused-vars
      isBinding) {} // Don't bother checking for TypeScript code.
      // Strict mode words may be allowed as in `declare namespace N { const static: number; }`.
      // And we have a type checker anyway, so don't bother having the parser do it.

      /*
      Don't bother doing this check in TypeScript code because:
      1. We may have a nested export statement with the same name:
        export const x = 0;
        export namespace N {
          export const x = 1;
        }
      2. We have a type checker to warn us about this sort of thing.
      */
      ;

      _proto.checkDuplicateExports = function checkDuplicateExports() {};

      _proto.parseImport = function parseImport(node) {
        if (this.match(types.name) && this.lookahead().type === types.eq) {
          return this.tsParseImportEqualsDeclaration(node);
        }

        return _superClass.prototype.parseImport.call(this, node);
      };

      _proto.parseExport = function parseExport(node) {
        if (this.match(types._import)) {
          // `export import A = B;`
          this.expect(types._import);
          return this.tsParseImportEqualsDeclaration(node,
          /* isExport */
          true);
        } else if (this.eat(types.eq)) {
          // `export = x;`
          var assign = node;
          assign.expression = this.parseExpression();
          this.semicolon();
          return this.finishNode(assign, "TSExportAssignment");
        } else if (this.eatContextual("as")) {
          // `export as namespace A;`
          var decl = node; // See `parseNamespaceExportDeclaration` in TypeScript's own parser

          this.expectContextual("namespace");
          decl.id = this.parseIdentifier();
          this.semicolon();
          return this.finishNode(decl, "TSNamespaceExportDeclaration");
        } else {
          return _superClass.prototype.parseExport.call(this, node);
        }
      };

      _proto.parseExportDefaultExpression = function parseExportDefaultExpression() {
        if (this.isContextual("abstract") && this.lookahead().type === types._class) {
          var cls = this.startNode();
          this.next(); // Skip "abstract"

          this.parseClass(cls, true, true);
          cls.abstract = true;
          return cls;
        }

        return _superClass.prototype.parseExportDefaultExpression.call(this);
      };

      _proto.parseStatementContent = function parseStatementContent(declaration, topLevel) {
        if (this.state.type === types._const) {
          var ahead = this.lookahead();

          if (ahead.type === types.name && ahead.value === "enum") {
            var node = this.startNode();
            this.expect(types._const);
            this.expectContextual("enum");
            return this.tsParseEnumDeclaration(node,
            /* isConst */
            true);
          }
        }

        return _superClass.prototype.parseStatementContent.call(this, declaration, topLevel);
      };

      _proto.parseAccessModifier = function parseAccessModifier() {
        return this.tsParseModifier(["public", "protected", "private"]);
      };

      _proto.parseClassMember = function parseClassMember(classBody, member, state) {
        var accessibility = this.parseAccessModifier();
        if (accessibility) member.accessibility = accessibility;

        _superClass.prototype.parseClassMember.call(this, classBody, member, state);
      };

      _proto.parseClassMemberWithIsStatic = function parseClassMemberWithIsStatic(classBody, member, state, isStatic) {
        var methodOrProp = member;
        var prop = member;
        var propOrIdx = member;
        var abstract = false,
            readonly = false;
        var mod = this.tsParseModifier(["abstract", "readonly"]);

        switch (mod) {
          case "readonly":
            readonly = true;
            abstract = !!this.tsParseModifier(["abstract"]);
            break;

          case "abstract":
            abstract = true;
            readonly = !!this.tsParseModifier(["readonly"]);
            break;
        }

        if (abstract) methodOrProp.abstract = true;
        if (readonly) propOrIdx.readonly = true;

        if (!abstract && !isStatic && !methodOrProp.accessibility) {
          var idx = this.tsTryParseIndexSignature(member);

          if (idx) {
            classBody.body.push(idx);
            return;
          }
        }

        if (readonly) {
          // Must be a property (if not an index signature).
          methodOrProp.static = isStatic;
          this.parseClassPropertyName(prop);
          this.parsePostMemberNameModifiers(methodOrProp);
          this.pushClassProperty(classBody, prop);
          return;
        }

        _superClass.prototype.parseClassMemberWithIsStatic.call(this, classBody, member, state, isStatic);
      };

      _proto.parsePostMemberNameModifiers = function parsePostMemberNameModifiers(methodOrProp) {
        var optional = this.eat(types.question);
        if (optional) methodOrProp.optional = true;
      }; // Note: The reason we do this in `parseExpressionStatement` and not `parseStatement`
      // is that e.g. `type()` is valid JS, so we must try parsing that first.
      // If it's really a type, we will parse `type` as the statement, and can correct it here
      // by parsing the rest.


      _proto.parseExpressionStatement = function parseExpressionStatement(node, expr) {
        var decl = expr.type === "Identifier" ? this.tsParseExpressionStatement(node, expr) : undefined;
        return decl || _superClass.prototype.parseExpressionStatement.call(this, node, expr);
      }; // export type
      // Should be true for anything parsed by `tsTryParseExportDeclaration`.


      _proto.shouldParseExportDeclaration = function shouldParseExportDeclaration() {
        if (this.tsIsDeclarationStart()) return true;
        return _superClass.prototype.shouldParseExportDeclaration.call(this);
      }; // An apparent conditional expression could actually be an optional parameter in an arrow function.


      _proto.parseConditional = function parseConditional(expr, noIn, startPos, startLoc, refNeedsArrowPos) {
        // only do the expensive clone if there is a question mark
        // and if we come from inside parens
        if (!refNeedsArrowPos || !this.match(types.question)) {
          return _superClass.prototype.parseConditional.call(this, expr, noIn, startPos, startLoc, refNeedsArrowPos);
        }

        var state = this.state.clone();

        try {
          return _superClass.prototype.parseConditional.call(this, expr, noIn, startPos, startLoc);
        } catch (err) {
          if (!(err instanceof SyntaxError)) {
            // istanbul ignore next: no such error is expected
            throw err;
          }

          this.state = state;
          refNeedsArrowPos.start = err.pos || this.state.start;
          return expr;
        }
      }; // Note: These "type casts" are *not* valid TS expressions.
      // But we parse them here and change them when completing the arrow function.


      _proto.parseParenItem = function parseParenItem(node, startPos, startLoc) {
        node = _superClass.prototype.parseParenItem.call(this, node, startPos, startLoc);

        if (this.eat(types.question)) {
          node.optional = true;
        }

        if (this.match(types.colon)) {
          var typeCastNode = this.startNodeAt(startPos, startLoc);
          typeCastNode.expression = node;
          typeCastNode.typeAnnotation = this.tsParseTypeAnnotation();
          return this.finishNode(typeCastNode, "TSTypeCastExpression");
        }

        return node;
      };

      _proto.parseExportDeclaration = function parseExportDeclaration(node) {
        // "export declare" is equivalent to just "export".
        var isDeclare = this.eatContextual("declare");
        var declaration;

        if (this.match(types.name)) {
          declaration = this.tsTryParseExportDeclaration();
        }

        if (!declaration) {
          declaration = _superClass.prototype.parseExportDeclaration.call(this, node);
        }

        if (declaration && isDeclare) {
          declaration.declare = true;
        }

        return declaration;
      };

      _proto.parseClassId = function parseClassId(node, isStatement, optionalId) {
        var _superClass$prototype;

        if ((!isStatement || optionalId) && this.isContextual("implements")) {
          return;
        }

        (_superClass$prototype = _superClass.prototype.parseClassId).call.apply(_superClass$prototype, [this].concat(Array.prototype.slice.call(arguments)));

        var typeParameters = this.tsTryParseTypeParameters();
        if (typeParameters) node.typeParameters = typeParameters;
      };

      _proto.parseClassProperty = function parseClassProperty(node) {
        var type = this.tsTryParseTypeAnnotation();
        if (type) node.typeAnnotation = type;
        return _superClass.prototype.parseClassProperty.call(this, node);
      };

      _proto.pushClassMethod = function pushClassMethod(classBody, method, isGenerator, isAsync, isConstructor) {
        var typeParameters = this.tsTryParseTypeParameters();
        if (typeParameters) method.typeParameters = typeParameters;

        _superClass.prototype.pushClassMethod.call(this, classBody, method, isGenerator, isAsync, isConstructor);
      };

      _proto.pushClassPrivateMethod = function pushClassPrivateMethod(classBody, method, isGenerator, isAsync) {
        var typeParameters = this.tsTryParseTypeParameters();
        if (typeParameters) method.typeParameters = typeParameters;

        _superClass.prototype.pushClassPrivateMethod.call(this, classBody, method, isGenerator, isAsync);
      };

      _proto.parseClassSuper = function parseClassSuper(node) {
        _superClass.prototype.parseClassSuper.call(this, node);

        if (node.superClass && this.isRelational("<")) {
          node.superTypeParameters = this.tsParseTypeArguments();
        }

        if (this.eatContextual("implements")) {
          node.implements = this.tsParseHeritageClause();
        }
      };

      _proto.parseObjPropValue = function parseObjPropValue(prop) {
        var _superClass$prototype2;

        if (this.isRelational("<")) {
          throw new Error("TODO");
        }

        for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
          args[_key - 1] = arguments[_key];
        }

        (_superClass$prototype2 = _superClass.prototype.parseObjPropValue).call.apply(_superClass$prototype2, [this, prop].concat(args));
      };

      _proto.parseFunctionParams = function parseFunctionParams(node, allowModifiers) {
        var typeParameters = this.tsTryParseTypeParameters();
        if (typeParameters) node.typeParameters = typeParameters;

        _superClass.prototype.parseFunctionParams.call(this, node, allowModifiers);
      }; // `let x: number;`


      _proto.parseVarHead = function parseVarHead(decl) {
        _superClass.prototype.parseVarHead.call(this, decl);

        var type = this.tsTryParseTypeAnnotation();

        if (type) {
          decl.id.typeAnnotation = type;
          this.finishNode(decl.id, decl.id.type); // set end position to end of type
        }
      }; // parse the return type of an async arrow function - let foo = (async (): number => {});


      _proto.parseAsyncArrowFromCallExpression = function parseAsyncArrowFromCallExpression(node, call) {
        if (this.match(types.colon)) {
          node.returnType = this.tsParseTypeAnnotation();
        }

        return _superClass.prototype.parseAsyncArrowFromCallExpression.call(this, node, call);
      };

      _proto.parseMaybeAssign = function parseMaybeAssign() {
        // Note: When the JSX plugin is on, type assertions (`<T> x`) aren't valid syntax.
        var jsxError;

        for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
          args[_key2] = arguments[_key2];
        }

        if (this.match(types.jsxTagStart)) {
          var context = this.curContext();
          assert(context === types$1.j_oTag); // Only time j_oTag is pushed is right after j_expr.

          assert(this.state.context[this.state.context.length - 2] === types$1.j_expr); // Prefer to parse JSX if possible. But may be an arrow fn.

          var _state = this.state.clone();

          try {
            var _superClass$prototype3;

            return (_superClass$prototype3 = _superClass.prototype.parseMaybeAssign).call.apply(_superClass$prototype3, [this].concat(args));
          } catch (err) {
            if (!(err instanceof SyntaxError)) {
              // istanbul ignore next: no such error is expected
              throw err;
            }

            this.state = _state; // Pop the context added by the jsxTagStart.

            assert(this.curContext() === types$1.j_oTag);
            this.state.context.pop();
            assert(this.curContext() === types$1.j_expr);
            this.state.context.pop();
            jsxError = err;
          }
        }

        if (jsxError === undefined && !this.isRelational("<")) {
          var _superClass$prototype4;

          return (_superClass$prototype4 = _superClass.prototype.parseMaybeAssign).call.apply(_superClass$prototype4, [this].concat(args));
        } // Either way, we're looking at a '<': tt.jsxTagStart or relational.


        var arrowExpression;
        var typeParameters;
        var state = this.state.clone();

        try {
          var _superClass$prototype5;

          // This is similar to TypeScript's `tryParseParenthesizedArrowFunctionExpression`.
          typeParameters = this.tsParseTypeParameters();
          arrowExpression = (_superClass$prototype5 = _superClass.prototype.parseMaybeAssign).call.apply(_superClass$prototype5, [this].concat(args));

          if (arrowExpression.type !== "ArrowFunctionExpression") {
            this.unexpected(); // Go to the catch block (needs a SyntaxError).
          }
        } catch (err) {
          var _superClass$prototype6;

          if (!(err instanceof SyntaxError)) {
            // istanbul ignore next: no such error is expected
            throw err;
          }

          if (jsxError) {
            throw jsxError;
          } // Try parsing a type cast instead of an arrow function.
          // This will never happen outside of JSX.
          // (Because in JSX the '<' should be a jsxTagStart and not a relational.


          assert(!this.hasPlugin("jsx")); // Parsing an arrow function failed, so try a type cast.

          this.state = state; // This will start with a type assertion (via parseMaybeUnary).
          // But don't directly call `this.tsParseTypeAssertion` because we want to handle any binary after it.

          return (_superClass$prototype6 = _superClass.prototype.parseMaybeAssign).call.apply(_superClass$prototype6, [this].concat(args));
        } // Correct TypeScript code should have at least 1 type parameter, but don't crash on bad code.


        if (typeParameters && typeParameters.params.length !== 0) {
          this.resetStartLocationFromNode(arrowExpression, typeParameters.params[0]);
        }

        arrowExpression.typeParameters = typeParameters;
        return arrowExpression;
      }; // Handle type assertions


      _proto.parseMaybeUnary = function parseMaybeUnary(refShorthandDefaultPos) {
        if (!this.hasPlugin("jsx") && this.eatRelational("<")) {
          return this.tsParseTypeAssertion();
        } else {
          return _superClass.prototype.parseMaybeUnary.call(this, refShorthandDefaultPos);
        }
      };

      _proto.parseArrow = function parseArrow(node) {
        if (this.match(types.colon)) {
          // This is different from how the TS parser does it.
          // TS uses lookahead. Babylon parses it as a parenthesized expression and converts.
          var state = this.state.clone();

          try {
            var returnType = this.tsParseTypeOrTypePredicateAnnotation(types.colon);
            if (this.canInsertSemicolon()) this.unexpected();
            if (!this.match(types.arrow)) this.unexpected();
            node.returnType = returnType;
          } catch (err) {
            if (err instanceof SyntaxError) {
              this.state = state;
            } else {
              // istanbul ignore next: no such error is expected
              throw err;
            }
          }
        }

        return _superClass.prototype.parseArrow.call(this, node);
      }; // Allow type annotations inside of a parameter list.


      _proto.parseAssignableListItemTypes = function parseAssignableListItemTypes(param) {
        if (this.eat(types.question)) {
          if (param.type !== "Identifier") {
            throw this.raise(param.start, "A binding pattern parameter cannot be optional in an implementation signature.");
          }

          param.optional = true;
        }

        var type = this.tsTryParseTypeAnnotation();
        if (type) param.typeAnnotation = type;
        return this.finishNode(param, param.type);
      };

      _proto.toAssignable = function toAssignable(node, isBinding, contextDescription) {
        switch (node.type) {
          case "TSTypeCastExpression":
            return _superClass.prototype.toAssignable.call(this, this.typeCastToParameter(node), isBinding, contextDescription);

          case "TSParameterProperty":
            return _superClass.prototype.toAssignable.call(this, node, isBinding, contextDescription);

          case "TSAsExpression":
            node.expression = this.toAssignable(node.expression, isBinding, contextDescription);
            return node;

          default:
            return _superClass.prototype.toAssignable.call(this, node, isBinding, contextDescription);
        }
      };

      _proto.checkLVal = function checkLVal(expr, isBinding, checkClashes, contextDescription) {
        switch (expr.type) {
          case "TSTypeCastExpression":
            // Allow "typecasts" to appear on the left of assignment expressions,
            // because it may be in an arrow function.
            // e.g. `const f = (foo: number = 0) => foo;`
            return;

          case "TSParameterProperty":
            this.checkLVal(expr.parameter, isBinding, checkClashes, "parameter property");
            return;

          case "TSAsExpression":
            this.checkLVal(expr.expression, isBinding, checkClashes, contextDescription);
            return;

          default:
            _superClass.prototype.checkLVal.call(this, expr, isBinding, checkClashes, contextDescription);

            return;
        }
      };

      _proto.parseBindingAtom = function parseBindingAtom() {
        switch (this.state.type) {
          case types._this:
            // "this" may be the name of a parameter, so allow it.
            return this.parseIdentifier(
            /* liberal */
            true);

          default:
            return _superClass.prototype.parseBindingAtom.call(this);
        }
      }; // === === === === === === === === === === === === === === === ===
      // Note: All below methods are duplicates of something in flow.js.
      // Not sure what the best way to combine these is.
      // === === === === === === === === === === === === === === === ===


      _proto.isClassMethod = function isClassMethod() {
        return this.isRelational("<") || _superClass.prototype.isClassMethod.call(this);
      };

      _proto.isClassProperty = function isClassProperty() {
        return this.match(types.colon) || _superClass.prototype.isClassProperty.call(this);
      };

      _proto.parseMaybeDefault = function parseMaybeDefault() {
        var _superClass$prototype7;

        for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
          args[_key3] = arguments[_key3];
        }

        var node = (_superClass$prototype7 = _superClass.prototype.parseMaybeDefault).call.apply(_superClass$prototype7, [this].concat(args));

        if (node.type === "AssignmentPattern" && node.typeAnnotation && node.right.start < node.typeAnnotation.start) {
          this.raise(node.typeAnnotation.start, "Type annotations must come before default assignments, " + "e.g. instead of `age = 25: number` use `age: number = 25`");
        }

        return node;
      }; // ensure that inside types, we bypass the jsx parser plugin


      _proto.readToken = function readToken(code) {
        if (this.state.inType && (code === 62 || code === 60)) {
          return this.finishOp(types.relational, 1);
        } else {
          return _superClass.prototype.readToken.call(this, code);
        }
      };

      _proto.toAssignableList = function toAssignableList(exprList, isBinding, contextDescription) {
        for (var i = 0; i < exprList.length; i++) {
          var expr = exprList[i];

          if (expr && expr.type === "TSTypeCastExpression") {
            exprList[i] = this.typeCastToParameter(expr);
          }
        }

        return _superClass.prototype.toAssignableList.call(this, exprList, isBinding, contextDescription);
      };

      _proto.typeCastToParameter = function typeCastToParameter(node) {
        node.expression.typeAnnotation = node.typeAnnotation;
        return this.finishNodeAt(node.expression, node.expression.type, node.typeAnnotation.end, node.typeAnnotation.loc.end);
      };

      _proto.toReferencedList = function toReferencedList(exprList) {
        for (var i = 0; i < exprList.length; i++) {
          var expr = exprList[i];

          if (expr && expr._exprListItem && expr.type === "TsTypeCastExpression") {
            this.raise(expr.start, "Did not expect a type annotation here.");
          }
        }

        return exprList;
      };

      _proto.shouldParseArrow = function shouldParseArrow() {
        return this.match(types.colon) || _superClass.prototype.shouldParseArrow.call(this);
      };

      _proto.shouldParseAsyncArrow = function shouldParseAsyncArrow() {
        return this.match(types.colon) || _superClass.prototype.shouldParseAsyncArrow.call(this);
      };

      return _class;
    }(superClass)
  );
});

plugins.estree = estreePlugin;
plugins.flow = flowPlugin;
plugins.jsx = jsxPlugin;
plugins.typescript = typescriptPlugin;
function parse(input, options) {
  if (options && options.sourceType === "unambiguous") {
    options = Object.assign({}, options);

    try {
      options.sourceType = "module";
      var ast = getParser(options, input).parse(); // Rather than try to parse as a script first, we opt to parse as a module and convert back
      // to a script where possible to avoid having to do a full re-parse of the input content.

      if (!hasModuleSyntax(ast)) ast.program.sourceType = "script";
      return ast;
    } catch (moduleError) {
      try {
        options.sourceType = "script";
        return getParser(options, input).parse();
      } catch (scriptError) {}

      throw moduleError;
    }
  } else {
    return getParser(options, input).parse();
  }
}
function parseExpression(input, options) {
  var parser = getParser(options, input);

  if (parser.options.strictMode) {
    parser.state.strict = true;
  }

  return parser.getExpression();
}
function getParser(options, input) {
  var cls = options && options.plugins ? getParserClass(options.plugins) : Parser;
  return new cls(options, input);
}

var parserClassCache = {};
/** Get a Parser class with plugins applied. */

function getParserClass(pluginsFromOptions) {
  if (pluginsFromOptions.indexOf("decorators") >= 0 && pluginsFromOptions.indexOf("decorators2") >= 0) {
    throw new Error("Cannot use decorators and decorators2 plugin together");
  } // Filter out just the plugins that have an actual mixin associated with them.


  var pluginList = pluginsFromOptions.filter(function (p) {
    return p === "estree" || p === "flow" || p === "jsx" || p === "typescript";
  });

  if (pluginList.indexOf("flow") >= 0) {
    // ensure flow plugin loads last
    pluginList = pluginList.filter(function (plugin) {
      return plugin !== "flow";
    });
    pluginList.push("flow");
  }

  if (pluginList.indexOf("flow") >= 0 && pluginList.indexOf("typescript") >= 0) {
    throw new Error("Cannot combine flow and typescript plugins.");
  }

  if (pluginList.indexOf("typescript") >= 0) {
    // ensure typescript plugin loads last
    pluginList = pluginList.filter(function (plugin) {
      return plugin !== "typescript";
    });
    pluginList.push("typescript");
  }

  if (pluginList.indexOf("estree") >= 0) {
    // ensure estree plugin loads first
    pluginList = pluginList.filter(function (plugin) {
      return plugin !== "estree";
    });
    pluginList.unshift("estree");
  }

  var key = pluginList.join("/");
  var cls = parserClassCache[key];

  if (!cls) {
    cls = Parser;

    for (var _i2 = 0, _pluginList2 = pluginList; _i2 < _pluginList2.length; _i2++) {
      var plugin = _pluginList2[_i2];
      cls = plugins[plugin](cls);
    }

    parserClassCache[key] = cls;
  }

  return cls;
}

function hasModuleSyntax(ast) {
  return ast.program.body.some(function (child) {
    return child.type === "ImportDeclaration" && (!child.importKind || child.importKind === "value") || child.type === "ExportNamedDeclaration" && (!child.exportKind || child.exportKind === "value") || child.type === "ExportAllDeclaration" && (!child.exportKind || child.exportKind === "value") || child.type === "ExportDefaultDeclaration";
  });
}

exports.parse = parse;
exports.parseExpression = parseExpression;
exports.tokTypes = types;


/***/ }),

/***/ 5:
/***/ (function(module, exports, __webpack_require__) {

var baseGetTag = __webpack_require__(6),
    getPrototype = __webpack_require__(12),
    isObjectLike = __webpack_require__(14);

/** `Object#toString` result references. */
var objectTag = '[object Object]';

/** Used for built-in method references. */
var funcProto = Function.prototype,
    objectProto = Object.prototype;

/** 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 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.call(proto, 'constructor') && proto.constructor;
  return typeof Ctor == 'function' && Ctor instanceof Ctor &&
    funcToString.call(Ctor) == objectCtorString;
}

module.exports = isPlainObject;


/***/ }),

/***/ 530:
/***/ (function(module, exports, __webpack_require__) {

/*
  Copyright (C) 2013 Yusuke Suzuki <[email protected]>

  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.

  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 <COPYRIGHT HOLDER> 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 () {
    'use strict';

    exports.ast = __webpack_require__(531);
    exports.code = __webpack_require__(532);
    exports.keyword = __webpack_require__(533);
}());
/* vim: set sw=4 ts=4 et tw=80 : */


/***/ }),

/***/ 531:
/***/ (function(module, exports) {

/*
  Copyright (C) 2013 Yusuke Suzuki <[email protected]>

  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.

  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 <COPYRIGHT HOLDER> 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 () {
    'use strict';

    function isExpression(node) {
        if (node == null) { return false; }
        switch (node.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 true;
        }
        return false;
    }

    function isIterationStatement(node) {
        if (node == null) { return false; }
        switch (node.type) {
            case 'DoWhileStatement':
            case 'ForInStatement':
            case 'ForStatement':
            case 'WhileStatement':
                return true;
        }
        return false;
    }

    function isStatement(node) {
        if (node == null) { return false; }
        switch (node.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 true;
        }
        return false;
    }

    function isSourceElement(node) {
      return isStatement(node) || node != null && node.type === 'FunctionDeclaration';
    }

    function trailingStatement(node) {
        switch (node.type) {
        case 'IfStatement':
            if (node.alternate != null) {
                return node.alternate;
            }
            return node.consequent;

        case 'LabeledStatement':
        case 'ForStatement':
        case 'ForInStatement':
        case 'WhileStatement':
        case 'WithStatement':
            return node.body;
        }
        return null;
    }

    function isProblematicIfStatement(node) {
        var current;

        if (node.type !== 'IfStatement') {
            return false;
        }
        if (node.alternate == null) {
            return false;
        }
        current = node.consequent;
        do {
            if (current.type === 'IfStatement') {
                if (current.alternate == null)  {
                    return true;
                }
            }
            current = trailingStatement(current);
        } while (current);

        return false;
    }

    module.exports = {
        isExpression: isExpression,
        isStatement: isStatement,
        isIterationStatement: isIterationStatement,
        isSourceElement: isSourceElement,
        isProblematicIfStatement: isProblematicIfStatement,

        trailingStatement: trailingStatement
    };
}());
/* vim: set sw=4 ts=4 et tw=80 : */


/***/ }),

/***/ 532:
/***/ (function(module, exports) {

/*
  Copyright (C) 2013-2014 Yusuke Suzuki <[email protected]>
  Copyright (C) 2014 Ivan Nikulin <[email protected]>

  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.

  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 <COPYRIGHT HOLDER> 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 () {
    'use strict';

    var ES6Regex, ES5Regex, NON_ASCII_WHITESPACES, IDENTIFIER_START, IDENTIFIER_PART, ch;

    // See `tools/generate-identifier-regex.js`.
    ES5Regex = {
        // ECMAScript 5.1/Unicode v7.0.0 NonAsciiIdentifierStart:
        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-\u08B2\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\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\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\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-\u13F4\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\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\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-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA7AD\uA7B0\uA7B1\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\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-\uAB5F\uAB64\uAB65\uABC0-\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]/,
        // ECMAScript 5.1/Unicode v7.0.0 NonAsciiIdentifierPart:
        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-\u08B2\u08E4-\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\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\u0C59\u0C60-\u0C63\u0C66-\u0C6F\u0C81-\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\u0D57\u0D60-\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-\u13F4\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\u1CD0-\u1CD2\u1CD4-\u1CF6\u1CF8\u1CF9\u1D00-\u1DF5\u1DFC-\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-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66F\uA674-\uA67D\uA67F-\uA69D\uA69F-\uA6F1\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA7AD\uA7B0\uA7B1\uA7F7-\uA827\uA840-\uA873\uA880-\uA8C4\uA8D0-\uA8D9\uA8E0-\uA8F7\uA8FB\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-\uAB5F\uAB64\uAB65\uABC0-\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-\uFE2D\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]/
    };

    ES6Regex = {
        // ECMAScript 6/Unicode v7.0.0 NonAsciiIdentifierStart:
        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-\u08B2\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\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\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\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-\u13F4\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\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\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-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA7AD\uA7B0\uA7B1\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\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-\uAB5F\uAB64\uAB65\uABC0-\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\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\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]|\uD804[\uDC03-\uDC37\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDE00-\uDE11\uDE13-\uDE2B\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF5D-\uDF61]|\uD805[\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDE00-\uDE2F\uDE44\uDE80-\uDEAA]|\uD806[\uDCA0-\uDCDF\uDCFF\uDEC0-\uDEF8]|\uD808[\uDC00-\uDF98]|\uD809[\uDC00-\uDC6E]|[\uD80C\uD840-\uD868\uD86A-\uD86C][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50\uDF93-\uDF9F]|\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]|\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]|\uD87E[\uDC00-\uDE1D]/,
        // ECMAScript 6/Unicode v7.0.0 NonAsciiIdentifierPart:
        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-\u08B2\u08E4-\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\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\u0C59\u0C60-\u0C63\u0C66-\u0C6F\u0C81-\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\u0D57\u0D60-\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-\u13F4\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\u1CD0-\u1CD2\u1CD4-\u1CF6\u1CF8\u1CF9\u1D00-\u1DF5\u1DFC-\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-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66F\uA674-\uA67D\uA67F-\uA69D\uA69F-\uA6F1\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA7AD\uA7B0\uA7B1\uA7F7-\uA827\uA840-\uA873\uA880-\uA8C4\uA8D0-\uA8D9\uA8E0-\uA8F7\uA8FB\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-\uAB5F\uAB64\uAB65\uABC0-\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-\uFE2D\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\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\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]|\uD804[\uDC00-\uDC46\uDC66-\uDC6F\uDC7F-\uDCBA\uDCD0-\uDCE8\uDCF0-\uDCF9\uDD00-\uDD34\uDD36-\uDD3F\uDD50-\uDD73\uDD76\uDD80-\uDDC4\uDDD0-\uDDDA\uDE00-\uDE11\uDE13-\uDE37\uDEB0-\uDEEA\uDEF0-\uDEF9\uDF01-\uDF03\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3C-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF57\uDF5D-\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD805[\uDC80-\uDCC5\uDCC7\uDCD0-\uDCD9\uDD80-\uDDB5\uDDB8-\uDDC0\uDE00-\uDE40\uDE44\uDE50-\uDE59\uDE80-\uDEB7\uDEC0-\uDEC9]|\uD806[\uDCA0-\uDCE9\uDCFF\uDEC0-\uDEF8]|\uD808[\uDC00-\uDF98]|\uD809[\uDC00-\uDC6E]|[\uD80C\uD840-\uD868\uD86A-\uD86C][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\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]|\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]|\uD83A[\uDC00-\uDCC4\uDCD0-\uDCD6]|\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]|\uD87E[\uDC00-\uDE1D]|\uDB40[\uDD00-\uDDEF]/
    };

    function isDecimalDigit(ch) {
        return 0x30 <= ch && ch <= 0x39;  // 0..9
    }

    function isHexDigit(ch) {
        return 0x30 <= ch && ch <= 0x39 ||  // 0..9
            0x61 <= ch && ch <= 0x66 ||     // a..f
            0x41 <= ch && ch <= 0x46;       // A..F
    }

    function isOctalDigit(ch) {
        return ch >= 0x30 && ch <= 0x37;  // 0..7
    }

    // 7.2 White Space

    NON_ASCII_WHITESPACES = [
        0x1680, 0x180E,
        0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A,
        0x202F, 0x205F,
        0x3000,
        0xFEFF
    ];

    function isWhiteSpace(ch) {
        return ch === 0x20 || ch === 0x09 || ch === 0x0B || ch === 0x0C || ch === 0xA0 ||
            ch >= 0x1680 && NON_ASCII_WHITESPACES.indexOf(ch) >= 0;
    }

    // 7.3 Line Terminators

    function isLineTerminator(ch) {
        return ch === 0x0A || ch === 0x0D || ch === 0x2028 || ch === 0x2029;
    }

    // 7.6 Identifier Names and Identifiers

    function fromCodePoint(cp) {
        if (cp <= 0xFFFF) { return String.fromCharCode(cp); }
        var cu1 = String.fromCharCode(Math.floor((cp - 0x10000) / 0x400) + 0xD800);
        var cu2 = String.fromCharCode(((cp - 0x10000) % 0x400) + 0xDC00);
        return cu1 + cu2;
    }

    IDENTIFIER_START = new Array(0x80);
    for(ch = 0; ch < 0x80; ++ch) {
        IDENTIFIER_START[ch] =
            ch >= 0x61 && ch <= 0x7A ||  // a..z
            ch >= 0x41 && ch <= 0x5A ||  // A..Z
            ch === 0x24 || ch === 0x5F;  // $ (dollar) and _ (underscore)
    }

    IDENTIFIER_PART = new Array(0x80);
    for(ch = 0; ch < 0x80; ++ch) {
        IDENTIFIER_PART[ch] =
            ch >= 0x61 && ch <= 0x7A ||  // a..z
            ch >= 0x41 && ch <= 0x5A ||  // A..Z
            ch >= 0x30 && ch <= 0x39 ||  // 0..9
            ch === 0x24 || ch === 0x5F;  // $ (dollar) and _ (underscore)
    }

    function isIdentifierStartES5(ch) {
        return ch < 0x80 ? IDENTIFIER_START[ch] : ES5Regex.NonAsciiIdentifierStart.test(fromCodePoint(ch));
    }

    function isIdentifierPartES5(ch) {
        return ch < 0x80 ? IDENTIFIER_PART[ch] : ES5Regex.NonAsciiIdentifierPart.test(fromCodePoint(ch));
    }

    function isIdentifierStartES6(ch) {
        return ch < 0x80 ? IDENTIFIER_START[ch] : ES6Regex.NonAsciiIdentifierStart.test(fromCodePoint(ch));
    }

    function isIdentifierPartES6(ch) {
        return ch < 0x80 ? IDENTIFIER_PART[ch] : ES6Regex.NonAsciiIdentifierPart.test(fromCodePoint(ch));
    }

    module.exports = {
        isDecimalDigit: isDecimalDigit,
        isHexDigit: isHexDigit,
        isOctalDigit: isOctalDigit,
        isWhiteSpace: isWhiteSpace,
        isLineTerminator: isLineTerminator,
        isIdentifierStartES5: isIdentifierStartES5,
        isIdentifierPartES5: isIdentifierPartES5,
        isIdentifierStartES6: isIdentifierStartES6,
        isIdentifierPartES6: isIdentifierPartES6
    };
}());
/* vim: set sw=4 ts=4 et tw=80 : */


/***/ }),

/***/ 533:
/***/ (function(module, exports, __webpack_require__) {

/*
  Copyright (C) 2013 Yusuke Suzuki <[email protected]>

  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.

  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 <COPYRIGHT HOLDER> 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 () {
    'use strict';

    var code = __webpack_require__(532);

    function isStrictModeReservedWordES6(id) {
        switch (id) {
        case 'implements':
        case 'interface':
        case 'package':
        case 'private':
        case 'protected':
        case 'public':
        case 'static':
        case 'let':
            return true;
        default:
            return false;
        }
    }

    function isKeywordES5(id, strict) {
        // yield should not be treated as keyword under non-strict mode.
        if (!strict && id === 'yield') {
            return false;
        }
        return isKeywordES6(id, strict);
    }

    function isKeywordES6(id, strict) {
        if (strict && isStrictModeReservedWordES6(id)) {
            return true;
        }

        switch (id.length) {
        case 2:
            return (id === 'if') || (id === 'in') || (id === 'do');
        case 3:
            return (id === 'var') || (id === 'for') || (id === 'new') || (id === 'try');
        case 4:
            return (id === 'this') || (id === 'else') || (id === 'case') ||
                (id === 'void') || (id === 'with') || (id === 'enum');
        case 5:
            return (id === 'while') || (id === 'break') || (id === 'catch') ||
                (id === 'throw') || (id === 'const') || (id === 'yield') ||
                (id === 'class') || (id === 'super');
        case 6:
            return (id === 'return') || (id === 'typeof') || (id === 'delete') ||
                (id === 'switch') || (id === 'export') || (id === 'import');
        case 7:
            return (id === 'default') || (id === 'finally') || (id === 'extends');
        case 8:
            return (id === 'function') || (id === 'continue') || (id === 'debugger');
        case 10:
            return (id === 'instanceof');
        default:
            return false;
        }
    }

    function isReservedWordES5(id, strict) {
        return id === 'null' || id === 'true' || id === 'false' || isKeywordES5(id, strict);
    }

    function isReservedWordES6(id, strict) {
        return id === 'null' || id === 'true' || id === 'false' || isKeywordES6(id, strict);
    }

    function isRestrictedWord(id) {
        return id === 'eval' || id === 'arguments';
    }

    function isIdentifierNameES5(id) {
        var i, iz, ch;

        if (id.length === 0) { return false; }

        ch = id.charCodeAt(0);
        if (!code.isIdentifierStartES5(ch)) {
            return false;
        }

        for (i = 1, iz = id.length; i < iz; ++i) {
            ch = id.charCodeAt(i);
            if (!code.isIdentifierPartES5(ch)) {
                return false;
            }
        }
        return true;
    }

    function decodeUtf16(lead, trail) {
        return (lead - 0xD800) * 0x400 + (trail - 0xDC00) + 0x10000;
    }

    function isIdentifierNameES6(id) {
        var i, iz, ch, lowCh, check;

        if (id.length === 0) { return false; }

        check = code.isIdentifierStartES6;
        for (i = 0, iz = id.length; i < iz; ++i) {
            ch = id.charCodeAt(i);
            if (0xD800 <= ch && ch <= 0xDBFF) {
                ++i;
                if (i >= iz) { return false; }
                lowCh = id.charCodeAt(i);
                if (!(0xDC00 <= lowCh && lowCh <= 0xDFFF)) {
                    return false;
                }
                ch = decodeUtf16(ch, lowCh);
            }
            if (!check(ch)) {
                return false;
            }
            check = code.isIdentifierPartES6;
        }
        return true;
    }

    function isIdentifierES5(id, strict) {
        return isIdentifierNameES5(id) && !isReservedWordES5(id, strict);
    }

    function isIdentifierES6(id, strict) {
        return isIdentifierNameES6(id) && !isReservedWordES6(id, strict);
    }

    module.exports = {
        isKeywordES5: isKeywordES5,
        isKeywordES6: isKeywordES6,
        isReservedWordES5: isReservedWordES5,
        isReservedWordES6: isReservedWordES6,
        isRestrictedWord: isRestrictedWord,
        isIdentifierNameES5: isIdentifierNameES5,
        isIdentifierNameES6: isIdentifierNameES6,
        isIdentifierES5: isIdentifierES5,
        isIdentifierES6: isIdentifierES6
    };
}());
/* vim: set sw=4 ts=4 et tw=80 : */


/***/ }),

/***/ 538:
/***/ (function(module, exports, __webpack_require__) {

var baseIsRegExp = __webpack_require__(539),
    baseUnary = __webpack_require__(215),
    nodeUtil = __webpack_require__(216);

/* Node.js helper references. */
var nodeIsRegExp = nodeUtil && nodeUtil.isRegExp;

/**
 * Checks if `value` is classified as a `RegExp` object.
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a regexp, else `false`.
 * @example
 *
 * _.isRegExp(/abc/);
 * // => true
 *
 * _.isRegExp('/abc/');
 * // => false
 */
var isRegExp = nodeIsRegExp ? baseUnary(nodeIsRegExp) : baseIsRegExp;

module.exports = isRegExp;


/***/ }),

/***/ 539:
/***/ (function(module, exports, __webpack_require__) {

var baseGetTag = __webpack_require__(6),
    isObjectLike = __webpack_require__(14);

/** `Object#toString` result references. */
var regexpTag = '[object RegExp]';

/**
 * The base implementation of `_.isRegExp` without Node.js optimizations.
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a regexp, else `false`.
 */
function baseIsRegExp(value) {
  return isObjectLike(value) && baseGetTag(value) == regexpTag;
}

module.exports = baseIsRegExp;


/***/ }),

/***/ 541:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


let fastProto = null;

// Creates an object with permanently fast properties in V8. See Toon Verwaest's
// post https://medium.com/@tverwaes/setting-up-prototypes-in-v8-ec9c9491dfe2#5f62
// for more details. Use %HasFastProperties(object) and the Node.js flag
// --allow-natives-syntax to check whether an object has fast properties.
function FastObject(o) {
	// A prototype object will have "fast properties" enabled once it is checked
	// against the inline property cache of a function, e.g. fastProto.property:
	// https://github.com/v8/v8/blob/6.0.122/test/mjsunit/fast-prototype.js#L48-L63
	if (fastProto !== null && typeof fastProto.property) {
		const result = fastProto;
		fastProto = FastObject.prototype = null;
		return result;
	}
	fastProto = FastObject.prototype = o == null ? Object.create(null) : o;
	return new FastObject;
}

// Initialize the inline property cache of FastObject
FastObject();

module.exports = function toFastproperties(o) {
	return FastObject(o);
};


/***/ }),

/***/ 542:
/***/ (function(module, exports, __webpack_require__) {

var baseClone = __webpack_require__(543);

/** Used to compose bitmasks for cloning. */
var CLONE_SYMBOLS_FLAG = 4;

/**
 * Creates a shallow clone of `value`.
 *
 * **Note:** This method is loosely based on the
 * [structured clone algorithm](https://mdn.io/Structured_clone_algorithm)
 * and supports cloning arrays, array buffers, booleans, date objects, maps,
 * numbers, `Object` objects, regexes, sets, strings, symbols, and typed
 * arrays. The own enumerable properties of `arguments` objects are cloned
 * as plain objects. An empty object is returned for uncloneable values such
 * as error objects, functions, DOM nodes, and WeakMaps.
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Lang
 * @param {*} value The value to clone.
 * @returns {*} Returns the cloned value.
 * @see _.cloneDeep
 * @example
 *
 * var objects = [{ 'a': 1 }, { 'b': 2 }];
 *
 * var shallow = _.clone(objects);
 * console.log(shallow[0] === objects[0]);
 * // => true
 */
function clone(value) {
  return baseClone(value, CLONE_SYMBOLS_FLAG);
}

module.exports = clone;


/***/ }),

/***/ 543:
/***/ (function(module, exports, __webpack_require__) {

var Stack = __webpack_require__(267),
    arrayEach = __webpack_require__(544),
    assignValue = __webpack_require__(114),
    baseAssign = __webpack_require__(545),
    baseAssignIn = __webpack_require__(546),
    cloneBuffer = __webpack_require__(398),
    copyArray = __webpack_require__(401),
    copySymbols = __webpack_require__(547),
    copySymbolsIn = __webpack_require__(548),
    getAllKeys = __webpack_require__(285),
    getAllKeysIn = __webpack_require__(550),
    getTag = __webpack_require__(198),
    initCloneArray = __webpack_require__(551),
    initCloneByTag = __webpack_require__(552),
    initCloneObject = __webpack_require__(402),
    isArray = __webpack_require__(70),
    isBuffer = __webpack_require__(210),
    isMap = __webpack_require__(2291),
    isObject = __webpack_require__(84),
    isSet = __webpack_require__(2293),
    keys = __webpack_require__(205);

/** Used to compose bitmasks for cloning. */
var CLONE_DEEP_FLAG = 1,
    CLONE_FLAT_FLAG = 2,
    CLONE_SYMBOLS_FLAG = 4;

/** `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]',
    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 identify `toStringTag` values supported by `_.clone`. */
var cloneableTags = {};
cloneableTags[argsTag] = cloneableTags[arrayTag] =
cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] =
cloneableTags[boolTag] = cloneableTags[dateTag] =
cloneableTags[float32Tag] = cloneableTags[float64Tag] =
cloneableTags[int8Tag] = cloneableTags[int16Tag] =
cloneableTags[int32Tag] = cloneableTags[mapTag] =
cloneableTags[numberTag] = cloneableTags[objectTag] =
cloneableTags[regexpTag] = cloneableTags[setTag] =
cloneableTags[stringTag] = cloneableTags[symbolTag] =
cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] =
cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;
cloneableTags[errorTag] = cloneableTags[funcTag] =
cloneableTags[weakMapTag] = false;

/**
 * The base implementation of `_.clone` and `_.cloneDeep` which tracks
 * traversed objects.
 *
 * @private
 * @param {*} value The value to clone.
 * @param {boolean} bitmask The bitmask flags.
 *  1 - Deep clone
 *  2 - Flatten inherited properties
 *  4 - Clone symbols
 * @param {Function} [customizer] The function to customize cloning.
 * @param {string} [key] The key of `value`.
 * @param {Object} [object] The parent object of `value`.
 * @param {Object} [stack] Tracks traversed objects and their clone counterparts.
 * @returns {*} Returns the cloned value.
 */
function baseClone(value, bitmask, customizer, key, object, stack) {
  var result,
      isDeep = bitmask & CLONE_DEEP_FLAG,
      isFlat = bitmask & CLONE_FLAT_FLAG,
      isFull = bitmask & CLONE_SYMBOLS_FLAG;

  if (customizer) {
    result = object ? customizer(value, key, object, stack) : customizer(value);
  }
  if (result !== undefined) {
    return result;
  }
  if (!isObject(value)) {
    return value;
  }
  var isArr = isArray(value);
  if (isArr) {
    result = initCloneArray(value);
    if (!isDeep) {
      return copyArray(value, result);
    }
  } else {
    var tag = getTag(value),
        isFunc = tag == funcTag || tag == genTag;

    if (isBuffer(value)) {
      return cloneBuffer(value, isDeep);
    }
    if (tag == objectTag || tag == argsTag || (isFunc && !object)) {
      result = (isFlat || isFunc) ? {} : initCloneObject(value);
      if (!isDeep) {
        return isFlat
          ? copySymbolsIn(value, baseAssignIn(result, value))
          : copySymbols(value, baseAssign(result, value));
      }
    } else {
      if (!cloneableTags[tag]) {
        return object ? value : {};
      }
      result = initCloneByTag(value, tag, isDeep);
    }
  }
  // Check for circular references and return its corresponding clone.
  stack || (stack = new Stack);
  var stacked = stack.get(value);
  if (stacked) {
    return stacked;
  }
  stack.set(value, result);

  if (isSet(value)) {
    value.forEach(function(subValue) {
      result.add(baseClone(subValue, bitmask, customizer, subValue, value, stack));
    });

    return result;
  }

  if (isMap(value)) {
    value.forEach(function(subValue, key) {
      result.set(key, baseClone(subValue, bitmask, customizer, key, value, stack));
    });

    return result;
  }

  var keysFunc = isFull
    ? (isFlat ? getAllKeysIn : getAllKeys)
    : (isFlat ? keysIn : keys);

  var props = isArr ? undefined : keysFunc(value);
  arrayEach(props || value, function(subValue, key) {
    if (props) {
      key = subValue;
      subValue = value[key];
    }
    // Recursively populate clone (susceptible to call stack limits).
    assignValue(result, key, baseClone(subValue, bitmask, customizer, key, value, stack));
  });
  return result;
}

module.exports = baseClone;


/***/ }),

/***/ 544:
/***/ (function(module, exports) {

/**
 * A specialized version of `_.forEach` 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 `array`.
 */
function arrayEach(array, iteratee) {
  var index = -1,
      length = array == null ? 0 : array.length;

  while (++index < length) {
    if (iteratee(array[index], index, array) === false) {
      break;
    }
  }
  return array;
}

module.exports = arrayEach;


/***/ }),

/***/ 545:
/***/ (function(module, exports, __webpack_require__) {

var copyObject = __webpack_require__(406),
    keys = __webpack_require__(205);

/**
 * The base implementation of `_.assign` without support for multiple sources
 * or `customizer` functions.
 *
 * @private
 * @param {Object} object The destination object.
 * @param {Object} source The source object.
 * @returns {Object} Returns `object`.
 */
function baseAssign(object, source) {
  return object && copyObject(source, keys(source), object);
}

module.exports = baseAssign;


/***/ }),

/***/ 546:
/***/ (function(module, exports, __webpack_require__) {

var copyObject = __webpack_require__(406),
    keysIn = __webpack_require__(407);

/**
 * The base implementation of `_.assignIn` without support for multiple sources
 * or `customizer` functions.
 *
 * @private
 * @param {Object} object The destination object.
 * @param {Object} source The source object.
 * @returns {Object} Returns `object`.
 */
function baseAssignIn(object, source) {
  return object && copyObject(source, keysIn(source), object);
}

module.exports = baseAssignIn;


/***/ }),

/***/ 547:
/***/ (function(module, exports, __webpack_require__) {

var copyObject = __webpack_require__(406),
    getSymbols = __webpack_require__(288);

/**
 * Copies own symbols of `source` to `object`.
 *
 * @private
 * @param {Object} source The object to copy symbols from.
 * @param {Object} [object={}] The object to copy symbols to.
 * @returns {Object} Returns `object`.
 */
function copySymbols(source, object) {
  return copyObject(source, getSymbols(source), object);
}

module.exports = copySymbols;


/***/ }),

/***/ 548:
/***/ (function(module, exports, __webpack_require__) {

var copyObject = __webpack_require__(406),
    getSymbolsIn = __webpack_require__(549);

/**
 * Copies own and inherited symbols of `source` to `object`.
 *
 * @private
 * @param {Object} source The object to copy symbols from.
 * @param {Object} [object={}] The object to copy symbols to.
 * @returns {Object} Returns `object`.
 */
function copySymbolsIn(source, object) {
  return copyObject(source, getSymbolsIn(source), object);
}

module.exports = copySymbolsIn;


/***/ }),

/***/ 549:
/***/ (function(module, exports, __webpack_require__) {

var arrayPush = __webpack_require__(287),
    getPrototype = __webpack_require__(12),
    getSymbols = __webpack_require__(288),
    stubArray = __webpack_require__(290);

/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeGetSymbols = Object.getOwnPropertySymbols;

/**
 * Creates an array of the own and inherited enumerable symbols of `object`.
 *
 * @private
 * @param {Object} object The object to query.
 * @returns {Array} Returns the array of symbols.
 */
var getSymbolsIn = !nativeGetSymbols ? stubArray : function(object) {
  var result = [];
  while (object) {
    arrayPush(result, getSymbols(object));
    object = getPrototype(object);
  }
  return result;
};

module.exports = getSymbolsIn;


/***/ }),

/***/ 550:
/***/ (function(module, exports, __webpack_require__) {

var baseGetAllKeys = __webpack_require__(286),
    getSymbolsIn = __webpack_require__(549),
    keysIn = __webpack_require__(407);

/**
 * Creates an array of own and inherited enumerable property names and
 * symbols of `object`.
 *
 * @private
 * @param {Object} object The object to query.
 * @returns {Array} Returns the array of property names and symbols.
 */
function getAllKeysIn(object) {
  return baseGetAllKeys(object, keysIn, getSymbolsIn);
}

module.exports = getAllKeysIn;


/***/ }),

/***/ 551:
/***/ (function(module, exports) {

/** Used for built-in method references. */
var objectProto = Object.prototype;

/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;

/**
 * Initializes an array clone.
 *
 * @private
 * @param {Array} array The array to clone.
 * @returns {Array} Returns the initialized clone.
 */
function initCloneArray(array) {
  var length = array.length,
      result = new array.constructor(length);

  // Add properties assigned by `RegExp#exec`.
  if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {
    result.index = array.index;
    result.input = array.input;
  }
  return result;
}

module.exports = initCloneArray;


/***/ }),

/***/ 552:
/***/ (function(module, exports, __webpack_require__) {

var cloneArrayBuffer = __webpack_require__(400),
    cloneDataView = __webpack_require__(553),
    cloneRegExp = __webpack_require__(557),
    cloneSymbol = __webpack_require__(560),
    cloneTypedArray = __webpack_require__(399);

/** `Object#toString` result references. */
var boolTag = '[object Boolean]',
    dateTag = '[object Date]',
    mapTag = '[object Map]',
    numberTag = '[object Number]',
    regexpTag = '[object RegExp]',
    setTag = '[object Set]',
    stringTag = '[object String]',
    symbolTag = '[object Symbol]';

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]';

/**
 * Initializes an object clone based on its `toStringTag`.
 *
 * **Note:** This function only supports cloning values with tags of
 * `Boolean`, `Date`, `Error`, `Map`, `Number`, `RegExp`, `Set`, or `String`.
 *
 * @private
 * @param {Object} object The object to clone.
 * @param {string} tag The `toStringTag` of the object to clone.
 * @param {boolean} [isDeep] Specify a deep clone.
 * @returns {Object} Returns the initialized clone.
 */
function initCloneByTag(object, tag, isDeep) {
  var Ctor = object.constructor;
  switch (tag) {
    case arrayBufferTag:
      return cloneArrayBuffer(object);

    case boolTag:
    case dateTag:
      return new Ctor(+object);

    case dataViewTag:
      return cloneDataView(object, isDeep);

    case float32Tag: case float64Tag:
    case int8Tag: case int16Tag: case int32Tag:
    case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag:
      return cloneTypedArray(object, isDeep);

    case mapTag:
      return new Ctor;

    case numberTag:
    case stringTag:
      return new Ctor(object);

    case regexpTag:
      return cloneRegExp(object);

    case setTag:
      return new Ctor;

    case symbolTag:
      return cloneSymbol(object);
  }
}

module.exports = initCloneByTag;


/***/ }),

/***/ 553:
/***/ (function(module, exports, __webpack_require__) {

var cloneArrayBuffer = __webpack_require__(400);

/**
 * Creates a clone of `dataView`.
 *
 * @private
 * @param {Object} dataView The data view to clone.
 * @param {boolean} [isDeep] Specify a deep clone.
 * @returns {Object} Returns the cloned data view.
 */
function cloneDataView(dataView, isDeep) {
  var buffer = isDeep ? cloneArrayBuffer(dataView.buffer) : dataView.buffer;
  return new dataView.constructor(buffer, dataView.byteOffset, dataView.byteLength);
}

module.exports = cloneDataView;


/***/ }),

/***/ 557:
/***/ (function(module, exports) {

/** Used to match `RegExp` flags from their coerced string values. */
var reFlags = /\w*$/;

/**
 * Creates a clone of `regexp`.
 *
 * @private
 * @param {Object} regexp The regexp to clone.
 * @returns {Object} Returns the cloned regexp.
 */
function cloneRegExp(regexp) {
  var result = new regexp.constructor(regexp.source, reFlags.exec(regexp));
  result.lastIndex = regexp.lastIndex;
  return result;
}

module.exports = cloneRegExp;


/***/ }),

/***/ 560:
/***/ (function(module, exports, __webpack_require__) {

var Symbol = __webpack_require__(7);

/** Used to convert symbols to primitives and strings. */
var symbolProto = Symbol ? Symbol.prototype : undefined,
    symbolValueOf = symbolProto ? symbolProto.valueOf : undefined;

/**
 * Creates a clone of the `symbol` object.
 *
 * @private
 * @param {Object} symbol The symbol object to clone.
 * @returns {Object} Returns the cloned symbol object.
 */
function cloneSymbol(symbol) {
  return symbolValueOf ? Object(symbolValueOf.call(symbol)) : {};
}

module.exports = cloneSymbol;


/***/ }),

/***/ 561:
/***/ (function(module, exports, __webpack_require__) {

var baseUniq = __webpack_require__(562);

/**
 * Creates a duplicate-free version of an array, using
 * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
 * for equality comparisons, in which only the first occurrence of each element
 * is kept. The order of result values is determined by the order they occur
 * in the array.
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Array
 * @param {Array} array The array to inspect.
 * @returns {Array} Returns the new duplicate free array.
 * @example
 *
 * _.uniq([2, 1, 2]);
 * // => [2, 1]
 */
function uniq(array) {
  return (array && array.length) ? baseUniq(array) : [];
}

module.exports = uniq;


/***/ }),

/***/ 562:
/***/ (function(module, exports, __webpack_require__) {

var SetCache = __webpack_require__(276),
    arrayIncludes = __webpack_require__(563),
    arrayIncludesWith = __webpack_require__(567),
    cacheHas = __webpack_require__(280),
    createSet = __webpack_require__(568),
    setToArray = __webpack_require__(283);

/** Used as the size to enable large array optimizations. */
var LARGE_ARRAY_SIZE = 200;

/**
 * The base implementation of `_.uniqBy` without support for iteratee shorthands.
 *
 * @private
 * @param {Array} array The array to inspect.
 * @param {Function} [iteratee] The iteratee invoked per element.
 * @param {Function} [comparator] The comparator invoked per element.
 * @returns {Array} Returns the new duplicate free array.
 */
function baseUniq(array, iteratee, comparator) {
  var index = -1,
      includes = arrayIncludes,
      length = array.length,
      isCommon = true,
      result = [],
      seen = result;

  if (comparator) {
    isCommon = false;
    includes = arrayIncludesWith;
  }
  else if (length >= LARGE_ARRAY_SIZE) {
    var set = iteratee ? null : createSet(array);
    if (set) {
      return setToArray(set);
    }
    isCommon = false;
    includes = cacheHas;
    seen = new SetCache;
  }
  else {
    seen = iteratee ? [] : result;
  }
  outer:
  while (++index < length) {
    var value = array[index],
        computed = iteratee ? iteratee(value) : value;

    value = (comparator || value !== 0) ? value : 0;
    if (isCommon && computed === computed) {
      var seenIndex = seen.length;
      while (seenIndex--) {
        if (seen[seenIndex] === computed) {
          continue outer;
        }
      }
      if (iteratee) {
        seen.push(computed);
      }
      result.push(value);
    }
    else if (!includes(seen, computed, comparator)) {
      if (seen !== result) {
        seen.push(computed);
      }
      result.push(value);
    }
  }
  return result;
}

module.exports = baseUniq;


/***/ }),

/***/ 563:
/***/ (function(module, exports, __webpack_require__) {

var baseIndexOf = __webpack_require__(564);

/**
 * A specialized version of `_.includes` for arrays without support for
 * specifying an index to search from.
 *
 * @private
 * @param {Array} [array] The array to inspect.
 * @param {*} target The value to search for.
 * @returns {boolean} Returns `true` if `target` is found, else `false`.
 */
function arrayIncludes(array, value) {
  var length = array == null ? 0 : array.length;
  return !!length && baseIndexOf(array, value, 0) > -1;
}

module.exports = arrayIncludes;


/***/ }),

/***/ 564:
/***/ (function(module, exports, __webpack_require__) {

var baseFindIndex = __webpack_require__(263),
    baseIsNaN = __webpack_require__(565),
    strictIndexOf = __webpack_require__(566);

/**
 * The base implementation of `_.indexOf` without `fromIndex` bounds checks.
 *
 * @private
 * @param {Array} array The array to inspect.
 * @param {*} value The value to search for.
 * @param {number} fromIndex The index to search from.
 * @returns {number} Returns the index of the matched value, else `-1`.
 */
function baseIndexOf(array, value, fromIndex) {
  return value === value
    ? strictIndexOf(array, value, fromIndex)
    : baseFindIndex(array, baseIsNaN, fromIndex);
}

module.exports = baseIndexOf;


/***/ }),

/***/ 565:
/***/ (function(module, exports) {

/**
 * The base implementation of `_.isNaN` without support for number objects.
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`.
 */
function baseIsNaN(value) {
  return value !== value;
}

module.exports = baseIsNaN;


/***/ }),

/***/ 566:
/***/ (function(module, exports) {

/**
 * A specialized version of `_.indexOf` which performs strict equality
 * comparisons of values, i.e. `===`.
 *
 * @private
 * @param {Array} array The array to inspect.
 * @param {*} value The value to search for.
 * @param {number} fromIndex The index to search from.
 * @returns {number} Returns the index of the matched value, else `-1`.
 */
function strictIndexOf(array, value, fromIndex) {
  var index = fromIndex - 1,
      length = array.length;

  while (++index < length) {
    if (array[index] === value) {
      return index;
    }
  }
  return -1;
}

module.exports = strictIndexOf;


/***/ }),

/***/ 567:
/***/ (function(module, exports) {

/**
 * This function is like `arrayIncludes` except that it accepts a comparator.
 *
 * @private
 * @param {Array} [array] The array to inspect.
 * @param {*} target The value to search for.
 * @param {Function} comparator The comparator invoked per element.
 * @returns {boolean} Returns `true` if `target` is found, else `false`.
 */
function arrayIncludesWith(array, value, comparator) {
  var index = -1,
      length = array == null ? 0 : array.length;

  while (++index < length) {
    if (comparator(value, array[index])) {
      return true;
    }
  }
  return false;
}

module.exports = arrayIncludesWith;


/***/ }),

/***/ 568:
/***/ (function(module, exports, __webpack_require__) {

var Set = __webpack_require__(201),
    noop = __webpack_require__(569),
    setToArray = __webpack_require__(283);

/** Used as references for various `Number` constants. */
var INFINITY = 1 / 0;

/**
 * Creates a set object of `values`.
 *
 * @private
 * @param {Array} values The values to add to the set.
 * @returns {Object} Returns the new set.
 */
var createSet = !(Set && (1 / setToArray(new Set([,-0]))[1]) == INFINITY) ? noop : function(values) {
  return new Set(values);
};

module.exports = createSet;


/***/ }),

/***/ 569:
/***/ (function(module, exports) {

/**
 * This method returns `undefined`.
 *
 * @static
 * @memberOf _
 * @since 2.3.0
 * @category Util
 * @example
 *
 * _.times(2, _.noop);
 * // => [undefined, undefined]
 */
function noop() {
  // No operation performed.
}

module.exports = noop;


/***/ }),

/***/ 6:
/***/ (function(module, exports, __webpack_require__) {

var Symbol = __webpack_require__(7),
    getRawTag = __webpack_require__(10),
    objectToString = __webpack_require__(11);

/** `Object#toString` result references. */
var nullTag = '[object Null]',
    undefinedTag = '[object Undefined]';

/** Built-in value references. */
var symToStringTag = Symbol ? Symbol.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 && symToStringTag in Object(value))
    ? getRawTag(value)
    : objectToString(value);
}

module.exports = baseGetTag;


/***/ }),

/***/ 67:
/***/ (function(module, exports, __webpack_require__) {

var baseGet = __webpack_require__(68);

/**
 * 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;
}

module.exports = get;


/***/ }),

/***/ 68:
/***/ (function(module, exports, __webpack_require__) {

var castPath = __webpack_require__(69),
    toKey = __webpack_require__(111);

/**
 * 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 = castPath(path, object);

  var index = 0,
      length = path.length;

  while (object != null && index < length) {
    object = object[toKey(path[index++])];
  }
  return (index && index == length) ? object : undefined;
}

module.exports = baseGet;


/***/ }),

/***/ 69:
/***/ (function(module, exports, __webpack_require__) {

var isArray = __webpack_require__(70),
    isKey = __webpack_require__(71),
    stringToPath = __webpack_require__(73),
    toString = __webpack_require__(108);

/**
 * Casts `value` to a path array if it's not one.
 *
 * @private
 * @param {*} value The value to inspect.
 * @param {Object} [object] The object to query keys on.
 * @returns {Array} Returns the cast property path array.
 */
function castPath(value, object) {
  if (isArray(value)) {
    return value;
  }
  return isKey(value, object) ? [value] : stringToPath(toString(value));
}

module.exports = castPath;


/***/ }),

/***/ 7:
/***/ (function(module, exports, __webpack_require__) {

var root = __webpack_require__(8);

/** Built-in value references. */
var Symbol = root.Symbol;

module.exports = Symbol;


/***/ }),

/***/ 70:
/***/ (function(module, exports) {

/**
 * 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;

module.exports = isArray;


/***/ }),

/***/ 706:
/***/ (function(module, exports, __webpack_require__) {

var baseFlatten = __webpack_require__(707);

/**
 * Flattens `array` a single level deep.
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Array
 * @param {Array} array The array to flatten.
 * @returns {Array} Returns the new flattened array.
 * @example
 *
 * _.flatten([1, [2, [3, [4]], 5]]);
 * // => [1, 2, [3, [4]], 5]
 */
function flatten(array) {
  var length = array == null ? 0 : array.length;
  return length ? baseFlatten(array, 1) : [];
}

module.exports = flatten;


/***/ }),

/***/ 707:
/***/ (function(module, exports, __webpack_require__) {

var arrayPush = __webpack_require__(287),
    isFlattenable = __webpack_require__(708);

/**
 * 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;
}

module.exports = baseFlatten;


/***/ }),

/***/ 708:
/***/ (function(module, exports, __webpack_require__) {

var Symbol = __webpack_require__(7),
    isArguments = __webpack_require__(208),
    isArray = __webpack_require__(70);

/** Built-in value references. */
var spreadableSymbol = Symbol ? Symbol.isConcatSpreadable : undefined;

/**
 * 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]);
}

module.exports = isFlattenable;


/***/ }),

/***/ 71:
/***/ (function(module, exports, __webpack_require__) {

var isArray = __webpack_require__(70),
    isSymbol = __webpack_require__(72);

/** Used to match property names within property paths. */
var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,
    reIsPlainProp = /^\w*$/;

/**
 * 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));
}

module.exports = isKey;


/***/ }),

/***/ 72:
/***/ (function(module, exports, __webpack_require__) {

var baseGetTag = __webpack_require__(6),
    isObjectLike = __webpack_require__(14);

/** `Object#toString` result references. */
var symbolTag = '[object Symbol]';

/**
 * 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) && baseGetTag(value) == symbolTag);
}

module.exports = isSymbol;


/***/ }),

/***/ 73:
/***/ (function(module, exports, __webpack_require__) {

var memoizeCapped = __webpack_require__(74);

/** Used to match property names within property paths. */
var rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g;

/** Used to match backslashes in property paths. */
var reEscapeChar = /\\(\\)?/g;

/**
 * Converts `string` to a property path array.
 *
 * @private
 * @param {string} string The string to convert.
 * @returns {Array} Returns the property path array.
 */
var stringToPath = memoizeCapped(function(string) {
  var result = [];
  if (string.charCodeAt(0) === 46 /* . */) {
    result.push('');
  }
  string.replace(rePropName, function(match, number, quote, subString) {
    result.push(quote ? subString.replace(reEscapeChar, '$1') : (number || match));
  });
  return result;
});

module.exports = stringToPath;


/***/ }),

/***/ 74:
/***/ (function(module, exports, __webpack_require__) {

var memoize = __webpack_require__(75);

/** Used as the maximum memoize cache size. */
var MAX_MEMOIZE_SIZE = 500;

/**
 * A specialized version of `_.memoize` which clears the memoized function's
 * cache when it exceeds `MAX_MEMOIZE_SIZE`.
 *
 * @private
 * @param {Function} func The function to have its output memoized.
 * @returns {Function} Returns the new memoized function.
 */
function memoizeCapped(func) {
  var result = memoize(func, function(key) {
    if (cache.size === MAX_MEMOIZE_SIZE) {
      cache.clear();
    }
    return key;
  });

  var cache = result.cache;
  return result;
}

module.exports = memoizeCapped;


/***/ }),

/***/ 75:
/***/ (function(module, exports, __webpack_require__) {

var MapCache = __webpack_require__(76);

/** Error message constants. */
var FUNC_ERROR_TEXT = 'Expected a function';

/**
 * 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 `clear`, `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 != null && 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) || cache;
    return result;
  };
  memoized.cache = new (memoize.Cache || MapCache);
  return memoized;
}

// Expose `MapCache`.
memoize.Cache = MapCache;

module.exports = memoize;


/***/ }),

/***/ 76:
/***/ (function(module, exports, __webpack_require__) {

var mapCacheClear = __webpack_require__(77),
    mapCacheDelete = __webpack_require__(102),
    mapCacheGet = __webpack_require__(105),
    mapCacheHas = __webpack_require__(106),
    mapCacheSet = __webpack_require__(107);

/**
 * 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 == null ? 0 : entries.length;

  this.clear();
  while (++index < length) {
    var entry = entries[index];
    this.set(entry[0], entry[1]);
  }
}

// Add methods to `MapCache`.
MapCache.prototype.clear = mapCacheClear;
MapCache.prototype['delete'] = mapCacheDelete;
MapCache.prototype.get = mapCacheGet;
MapCache.prototype.has = mapCacheHas;
MapCache.prototype.set = mapCacheSet;

module.exports = MapCache;


/***/ }),

/***/ 77:
/***/ (function(module, exports, __webpack_require__) {

var Hash = __webpack_require__(78),
    ListCache = __webpack_require__(93),
    Map = __webpack_require__(101);

/**
 * Removes all key-value entries from the map.
 *
 * @private
 * @name clear
 * @memberOf MapCache
 */
function mapCacheClear() {
  this.size = 0;
  this.__data__ = {
    'hash': new Hash,
    'map': new (Map || ListCache),
    'string': new Hash
  };
}

module.exports = mapCacheClear;


/***/ }),

/***/ 78:
/***/ (function(module, exports, __webpack_require__) {

var hashClear = __webpack_require__(79),
    hashDelete = __webpack_require__(89),
    hashGet = __webpack_require__(90),
    hashHas = __webpack_require__(91),
    hashSet = __webpack_require__(92);

/**
 * Creates a hash object.
 *
 * @private
 * @constructor
 * @param {Array} [entries] The key-value pairs to cache.
 */
function Hash(entries) {
  var index = -1,
      length = entries == null ? 0 : entries.length;

  this.clear();
  while (++index < length) {
    var entry = entries[index];
    this.set(entry[0], entry[1]);
  }
}

// Add methods to `Hash`.
Hash.prototype.clear = hashClear;
Hash.prototype['delete'] = hashDelete;
Hash.prototype.get = hashGet;
Hash.prototype.has = hashHas;
Hash.prototype.set = hashSet;

module.exports = Hash;


/***/ }),

/***/ 79:
/***/ (function(module, exports, __webpack_require__) {

var nativeCreate = __webpack_require__(80);

/**
 * Removes all key-value entries from the hash.
 *
 * @private
 * @name clear
 * @memberOf Hash
 */
function hashClear() {
  this.__data__ = nativeCreate ? nativeCreate(null) : {};
  this.size = 0;
}

module.exports = hashClear;


/***/ }),

/***/ 792:
/***/ (function(module, exports) {

var g;

// This works in non-strict mode
g = (function() {
	return this;
})();

try {
	// This works if eval is allowed (see CSP)
	g = g || Function("return this")() || (1,eval)("this");
} catch(e) {
	// This works if the window reference is available
	if(typeof window === "object")
		g = window;
}

// g can still be undefined, but nothing to do about it...
// We return undefined, instead of nothing here, so it's
// easier to handle this case. if(!global) { ...}

module.exports = g;


/***/ }),

/***/ 793:
/***/ (function(module, exports) {

module.exports = function(module) {
	if(!module.webpackPolyfill) {
		module.deprecate = function() {};
		module.paths = [];
		// module.parent = undefined by default
		if(!module.children) module.children = [];
		Object.defineProperty(module, "loaded", {
			enumerable: true,
			get: function() {
				return module.l;
			}
		});
		Object.defineProperty(module, "id", {
			enumerable: true,
			get: function() {
				return module.i;
			}
		});
		module.webpackPolyfill = 1;
	}
	return module;
};


/***/ }),

/***/ 799:
/***/ (function(module, exports, __webpack_require__) {

var baseIsEqualDeep = __webpack_require__(832),
    isObjectLike = __webpack_require__(14);

/**
 * 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 {boolean} bitmask The bitmask flags.
 *  1 - Unordered comparison
 *  2 - Partial comparison
 * @param {Function} [customizer] The function to customize comparisons.
 * @param {Object} [stack] Tracks traversed `value` and `other` objects.
 * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
 */
function baseIsEqual(value, other, bitmask, customizer, stack) {
  if (value === other) {
    return true;
  }
  if (value == null || other == null || (!isObjectLike(value) && !isObjectLike(other))) {
    return value !== value && other !== other;
  }
  return baseIsEqualDeep(value, other, bitmask, customizer, baseIsEqual, stack);
}

module.exports = baseIsEqual;


/***/ }),

/***/ 8:
/***/ (function(module, exports, __webpack_require__) {

var freeGlobal = __webpack_require__(9);

/** 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')();

module.exports = root;


/***/ }),

/***/ 80:
/***/ (function(module, exports, __webpack_require__) {

var getNative = __webpack_require__(81);

/* Built-in method references that are verified to be native. */
var nativeCreate = getNative(Object, 'create');

module.exports = nativeCreate;


/***/ }),

/***/ 800:
/***/ (function(module, exports, __webpack_require__) {

var SetCache = __webpack_require__(276),
    arraySome = __webpack_require__(833),
    cacheHas = __webpack_require__(280);

/** Used to compose bitmasks for value comparisons. */
var COMPARE_PARTIAL_FLAG = 1,
    COMPARE_UNORDERED_FLAG = 2;

/**
 * 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 {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
 * @param {Function} customizer The function to customize comparisons.
 * @param {Function} equalFunc The function to determine equivalents of values.
 * @param {Object} stack Tracks traversed `array` and `other` objects.
 * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.
 */
function equalArrays(array, other, bitmask, customizer, equalFunc, stack) {
  var isPartial = bitmask & COMPARE_PARTIAL_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 & COMPARE_UNORDERED_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 (!cacheHas(seen, othIndex) &&
                (arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) {
              return seen.push(othIndex);
            }
          })) {
        result = false;
        break;
      }
    } else if (!(
          arrValue === othValue ||
            equalFunc(arrValue, othValue, bitmask, customizer, stack)
        )) {
      result = false;
      break;
    }
  }
  stack['delete'](array);
  stack['delete'](other);
  return result;
}

module.exports = equalArrays;


/***/ }),

/***/ 801:
/***/ (function(module, exports, __webpack_require__) {

var isObject = __webpack_require__(84);

/**
 * 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);
}

module.exports = isStrictComparable;


/***/ }),

/***/ 803:
/***/ (function(module, exports) {

/**
 * 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)));
  };
}

module.exports = matchesStrictComparable;


/***/ }),

/***/ 81:
/***/ (function(module, exports, __webpack_require__) {

var baseIsNative = __webpack_require__(82),
    getValue = __webpack_require__(88);

/**
 * 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;
}

module.exports = getNative;


/***/ }),

/***/ 814:
/***/ (function(module, exports, __webpack_require__) {

var baseMatches = __webpack_require__(826),
    baseMatchesProperty = __webpack_require__(294),
    identity = __webpack_require__(298),
    isArray = __webpack_require__(70),
    property = __webpack_require__(839);

/**
 * 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);
}

module.exports = baseIteratee;


/***/ }),

/***/ 82:
/***/ (function(module, exports, __webpack_require__) {

var isFunction = __webpack_require__(83),
    isMasked = __webpack_require__(85),
    isObject = __webpack_require__(84),
    toSource = __webpack_require__(87);

/**
 * Used to match `RegExp`
 * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).
 */
var reRegExpChar = /[\\^$.*+?()[\]{}|]/g;

/** Used to detect host constructors (Safari). */
var reIsHostCtor = /^\[object .+?Constructor\]$/;

/** Used for built-in method references. */
var funcProto = Function.prototype,
    objectProto = Object.prototype;

/** 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 detect if a method is native. */
var reIsNative = RegExp('^' +
  funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&')
  .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
);

/**
 * 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) ? reIsNative : reIsHostCtor;
  return pattern.test(toSource(value));
}

module.exports = baseIsNative;


/***/ }),

/***/ 826:
/***/ (function(module, exports, __webpack_require__) {

var baseIsMatch = __webpack_require__(831),
    getMatchData = __webpack_require__(836),
    matchesStrictComparable = __webpack_require__(803);

/**
 * 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);
  };
}

module.exports = baseMatches;


/***/ }),

/***/ 83:
/***/ (function(module, exports, __webpack_require__) {

var baseGetTag = __webpack_require__(6),
    isObject = __webpack_require__(84);

/** `Object#toString` result references. */
var asyncTag = '[object AsyncFunction]',
    funcTag = '[object Function]',
    genTag = '[object GeneratorFunction]',
    proxyTag = '[object Proxy]';

/**
 * 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) {
  if (!isObject(value)) {
    return false;
  }
  // The use of `Object#toString` avoids issues with the `typeof` operator
  // in Safari 9 which returns 'object' for typed arrays and other constructors.
  var tag = baseGetTag(value);
  return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag;
}

module.exports = isFunction;


/***/ }),

/***/ 831:
/***/ (function(module, exports, __webpack_require__) {

var Stack = __webpack_require__(267),
    baseIsEqual = __webpack_require__(799);

/** Used to compose bitmasks for value comparisons. */
var COMPARE_PARTIAL_FLAG = 1,
    COMPARE_UNORDERED_FLAG = 2;

/**
 * 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, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG, customizer, stack)
            : result
          )) {
        return false;
      }
    }
  }
  return true;
}

module.exports = baseIsMatch;


/***/ }),

/***/ 832:
/***/ (function(module, exports, __webpack_require__) {

var Stack = __webpack_require__(267),
    equalArrays = __webpack_require__(800),
    equalByTag = __webpack_require__(834),
    equalObjects = __webpack_require__(835),
    getTag = __webpack_require__(198),
    isArray = __webpack_require__(70),
    isBuffer = __webpack_require__(210),
    isTypedArray = __webpack_require__(212);

/** Used to compose bitmasks for value comparisons. */
var COMPARE_PARTIAL_FLAG = 1;

/** `Object#toString` result references. */
var argsTag = '[object Arguments]',
    arrayTag = '[object Array]',
    objectTag = '[object Object]';

/** Used for built-in method references. */
var objectProto = Object.prototype;

/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;

/**
 * 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 {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
 * @param {Function} customizer The function to customize comparisons.
 * @param {Function} equalFunc The function to determine equivalents of values.
 * @param {Object} [stack] Tracks traversed `object` and `other` objects.
 * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
 */
function baseIsEqualDeep(object, other, bitmask, customizer, equalFunc, stack) {
  var objIsArr = isArray(object),
      othIsArr = isArray(other),
      objTag = objIsArr ? arrayTag : getTag(object),
      othTag = othIsArr ? arrayTag : getTag(other);

  objTag = objTag == argsTag ? objectTag : objTag;
  othTag = othTag == argsTag ? objectTag : othTag;

  var objIsObj = objTag == objectTag,
      othIsObj = othTag == objectTag,
      isSameTag = objTag == othTag;

  if (isSameTag && isBuffer(object)) {
    if (!isBuffer(other)) {
      return false;
    }
    objIsArr = true;
    objIsObj = false;
  }
  if (isSameTag && !objIsObj) {
    stack || (stack = new Stack);
    return (objIsArr || isTypedArray(object))
      ? equalArrays(object, other, bitmask, customizer, equalFunc, stack)
      : equalByTag(object, other, objTag, bitmask, customizer, equalFunc, stack);
  }
  if (!(bitmask & COMPARE_PARTIAL_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, bitmask, customizer, stack);
    }
  }
  if (!isSameTag) {
    return false;
  }
  stack || (stack = new Stack);
  return equalObjects(object, other, bitmask, customizer, equalFunc, stack);
}

module.exports = baseIsEqualDeep;


/***/ }),

/***/ 833:
/***/ (function(module, exports) {

/**
 * 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 == null ? 0 : array.length;

  while (++index < length) {
    if (predicate(array[index], index, array)) {
      return true;
    }
  }
  return false;
}

module.exports = arraySome;


/***/ }),

/***/ 834:
/***/ (function(module, exports, __webpack_require__) {

var Symbol = __webpack_require__(7),
    Uint8Array = __webpack_require__(282),
    eq = __webpack_require__(97),
    equalArrays = __webpack_require__(800),
    mapToArray = __webpack_require__(203),
    setToArray = __webpack_require__(283);

/** Used to compose bitmasks for value comparisons. */
var COMPARE_PARTIAL_FLAG = 1,
    COMPARE_UNORDERED_FLAG = 2;

/** `Object#toString` result references. */
var boolTag = '[object Boolean]',
    dateTag = '[object Date]',
    errorTag = '[object Error]',
    mapTag = '[object Map]',
    numberTag = '[object Number]',
    regexpTag = '[object RegExp]',
    setTag = '[object Set]',
    stringTag = '[object String]',
    symbolTag = '[object Symbol]';

var arrayBufferTag = '[object ArrayBuffer]',
    dataViewTag = '[object DataView]';

/** Used to convert symbols to primitives and strings. */
var symbolProto = Symbol ? Symbol.prototype : undefined,
    symbolValueOf = symbolProto ? symbolProto.valueOf : undefined;

/**
 * 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 {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
 * @param {Function} customizer The function to customize comparisons.
 * @param {Function} equalFunc The function to determine equivalents of values.
 * @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, bitmask, customizer, equalFunc, 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 & COMPARE_PARTIAL_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 |= COMPARE_UNORDERED_FLAG;

      // Recursively compare objects (susceptible to call stack limits).
      stack.set(object, other);
      var result = equalArrays(convert(object), convert(other), bitmask, customizer, equalFunc, stack);
      stack['delete'](object);
      return result;

    case symbolTag:
      if (symbolValueOf) {
        return symbolValueOf.call(object) == symbolValueOf.call(other);
      }
  }
  return false;
}

module.exports = equalByTag;


/***/ }),

/***/ 835:
/***/ (function(module, exports, __webpack_require__) {

var getAllKeys = __webpack_require__(285);

/** Used to compose bitmasks for value comparisons. */
var COMPARE_PARTIAL_FLAG = 1;

/** Used for built-in method references. */
var objectProto = Object.prototype;

/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;

/**
 * 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 {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
 * @param {Function} customizer The function to customize comparisons.
 * @param {Function} equalFunc The function to determine equivalents of values.
 * @param {Object} stack Tracks traversed `object` and `other` objects.
 * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
 */
function equalObjects(object, other, bitmask, customizer, equalFunc, stack) {
  var isPartial = bitmask & COMPARE_PARTIAL_FLAG,
      objProps = getAllKeys(object),
      objLength = objProps.length,
      othProps = getAllKeys(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, bitmask, customizer, 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;
}

module.exports = equalObjects;


/***/ }),

/***/ 836:
/***/ (function(module, exports, __webpack_require__) {

var isStrictComparable = __webpack_require__(801),
    keys = __webpack_require__(205);

/**
 * 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;
}

module.exports = getMatchData;


/***/ }),

/***/ 837:
/***/ (function(module, exports, __webpack_require__) {

var baseHasIn = __webpack_require__(838),
    hasPath = __webpack_require__(297);

/**
 * 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);
}

module.exports = hasIn;


/***/ }),

/***/ 838:
/***/ (function(module, exports) {

/**
 * 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);
}

module.exports = baseHasIn;


/***/ }),

/***/ 839:
/***/ (function(module, exports, __webpack_require__) {

var baseProperty = __webpack_require__(840),
    basePropertyDeep = __webpack_require__(841),
    isKey = __webpack_require__(71),
    toKey = __webpack_require__(111);

/**
 * 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 = property;


/***/ }),

/***/ 84:
/***/ (function(module, exports) {

/**
 * 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 != null && (type == 'object' || type == 'function');
}

module.exports = isObject;


/***/ }),

/***/ 840:
/***/ (function(module, exports) {

/**
 * 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];
  };
}

module.exports = baseProperty;


/***/ }),

/***/ 841:
/***/ (function(module, exports, __webpack_require__) {

var baseGet = __webpack_require__(68);

/**
 * 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);
  };
}

module.exports = basePropertyDeep;


/***/ }),

/***/ 85:
/***/ (function(module, exports, __webpack_require__) {

var coreJsData = __webpack_require__(86);

/** 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) : '';
}());

/**
 * 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);
}

module.exports = isMasked;


/***/ }),

/***/ 86:
/***/ (function(module, exports, __webpack_require__) {

var root = __webpack_require__(8);

/** Used to detect overreaching core-js shims. */
var coreJsData = root['__core-js_shared__'];

module.exports = coreJsData;


/***/ }),

/***/ 87:
/***/ (function(module, exports) {

/** Used for built-in method references. */
var funcProto = Function.prototype;

/** Used to resolve the decompiled source of functions. */
var funcToString = funcProto.toString;

/**
 * Converts `func` to its source code.
 *
 * @private
 * @param {Function} func The function to convert.
 * @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 '';
}

module.exports = toSource;


/***/ }),

/***/ 88:
/***/ (function(module, exports) {

/**
 * 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];
}

module.exports = getValue;


/***/ }),

/***/ 89:
/***/ (function(module, exports) {

/**
 * 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) {
  var result = this.has(key) && delete this.__data__[key];
  this.size -= result ? 1 : 0;
  return result;
}

module.exports = hashDelete;


/***/ }),

/***/ 9:
/***/ (function(module, exports, __webpack_require__) {

/* WEBPACK VAR INJECTION */(function(global) {/** Detect free variable `global` from Node.js. */
var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;

module.exports = freeGlobal;

/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(792)))

/***/ }),

/***/ 90:
/***/ (function(module, exports, __webpack_require__) {

var nativeCreate = __webpack_require__(80);

/** Used to stand-in for `undefined` hash values. */
var HASH_UNDEFINED = '__lodash_hash_undefined__';

/** Used for built-in method references. */
var objectProto = Object.prototype;

/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;

/**
 * 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;
}

module.exports = hashGet;


/***/ }),

/***/ 91:
/***/ (function(module, exports, __webpack_require__) {

var nativeCreate = __webpack_require__(80);

/** Used for built-in method references. */
var objectProto = Object.prototype;

/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;

/**
 * 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);
}

module.exports = hashHas;


/***/ }),

/***/ 92:
/***/ (function(module, exports, __webpack_require__) {

var nativeCreate = __webpack_require__(80);

/** Used to stand-in for `undefined` hash values. */
var HASH_UNDEFINED = '__lodash_hash_undefined__';

/**
 * 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__;
  this.size += this.has(key) ? 0 : 1;
  data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value;
  return this;
}

module.exports = hashSet;


/***/ }),

/***/ 93:
/***/ (function(module, exports, __webpack_require__) {

var listCacheClear = __webpack_require__(94),
    listCacheDelete = __webpack_require__(95),
    listCacheGet = __webpack_require__(98),
    listCacheHas = __webpack_require__(99),
    listCacheSet = __webpack_require__(100);

/**
 * Creates an list cache object.
 *
 * @private
 * @constructor
 * @param {Array} [entries] The key-value pairs to cache.
 */
function ListCache(entries) {
  var index = -1,
      length = entries == null ? 0 : entries.length;

  this.clear();
  while (++index < length) {
    var entry = entries[index];
    this.set(entry[0], entry[1]);
  }
}

// Add methods to `ListCache`.
ListCache.prototype.clear = listCacheClear;
ListCache.prototype['delete'] = listCacheDelete;
ListCache.prototype.get = listCacheGet;
ListCache.prototype.has = listCacheHas;
ListCache.prototype.set = listCacheSet;

module.exports = ListCache;


/***/ }),

/***/ 94:
/***/ (function(module, exports) {

/**
 * Removes all key-value entries from the list cache.
 *
 * @private
 * @name clear
 * @memberOf ListCache
 */
function listCacheClear() {
  this.__data__ = [];
  this.size = 0;
}

module.exports = listCacheClear;


/***/ }),

/***/ 95:
/***/ (function(module, exports, __webpack_require__) {

var assocIndexOf = __webpack_require__(96);

/** Used for built-in method references. */
var arrayProto = Array.prototype;

/** Built-in value references. */
var splice = arrayProto.splice;

/**
 * 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);
  }
  --this.size;
  return true;
}

module.exports = listCacheDelete;


/***/ }),

/***/ 96:
/***/ (function(module, exports, __webpack_require__) {

var eq = __webpack_require__(97);

/**
 * 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;
}

module.exports = assocIndexOf;


/***/ }),

/***/ 963:
/***/ (function(module, exports, __webpack_require__) {

var baseKeys = __webpack_require__(217),
    getTag = __webpack_require__(198),
    isArguments = __webpack_require__(208),
    isArray = __webpack_require__(70),
    isArrayLike = __webpack_require__(220),
    isBuffer = __webpack_require__(210),
    isPrototype = __webpack_require__(218),
    isTypedArray = __webpack_require__(212);

/** `Object#toString` result references. */
var mapTag = '[object Map]',
    setTag = '[object Set]';

/** Used for built-in method references. */
var objectProto = Object.prototype;

/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;

/**
 * Checks if `value` is an empty object, collection, map, or set.
 *
 * Objects are considered empty if they have no own enumerable string keyed
 * properties.
 *
 * Array-like values such as `arguments` objects, arrays, buffers, strings, or
 * jQuery-like collections are considered empty if they have a `length` of `0`.
 * Similarly, maps and sets are considered empty if they have a `size` of `0`.
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is empty, else `false`.
 * @example
 *
 * _.isEmpty(null);
 * // => true
 *
 * _.isEmpty(true);
 * // => true
 *
 * _.isEmpty(1);
 * // => true
 *
 * _.isEmpty([1, 2, 3]);
 * // => false
 *
 * _.isEmpty({ 'a': 1 });
 * // => false
 */
function isEmpty(value) {
  if (value == null) {
    return true;
  }
  if (isArrayLike(value) &&
      (isArray(value) || typeof value == 'string' || typeof value.splice == 'function' ||
        isBuffer(value) || isTypedArray(value) || isArguments(value))) {
    return !value.length;
  }
  var tag = getTag(value);
  if (tag == mapTag || tag == setTag) {
    return !value.size;
  }
  if (isPrototype(value)) {
    return !baseKeys(value).length;
  }
  for (var key in value) {
    if (hasOwnProperty.call(value, key)) {
      return false;
    }
  }
  return true;
}

module.exports = isEmpty;


/***/ }),

/***/ 97:
/***/ (function(module, exports) {

/**
 * 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);
}

module.exports = eq;


/***/ }),

/***/ 98:
/***/ (function(module, exports, __webpack_require__) {

var assocIndexOf = __webpack_require__(96);

/**
 * 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];
}

module.exports = listCacheGet;


/***/ }),

/***/ 99:
/***/ (function(module, exports, __webpack_require__) {

var assocIndexOf = __webpack_require__(96);

/**
 * 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;
}

module.exports = listCacheHas;


/***/ })

/******/ });
});PK
!<_��h!h!Kchrome/devtools/modules/devtools/client/debugger/new/pretty-print-worker.js(function webpackUniversalModuleDefinition(root, factory) {
	if(typeof exports === 'object' && typeof module === 'object')
		module.exports = factory();
	else if(typeof define === 'function' && define.amd)
		define([], factory);
	else {
		var a = factory();
		for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
	}
})(typeof self !== 'undefined' ? self : this, function() {
return /******/ (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, {
/******/ 				configurable: false,
/******/ 				enumerable: true,
/******/ 				get: getter
/******/ 			});
/******/ 		}
/******/ 	};
/******/
/******/ 	// 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 = "/assets/build";
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(__webpack_require__.s = 1282);
/******/ })
/************************************************************************/
/******/ ({

/***/ 1282:
/***/ (function(module, exports, __webpack_require__) {

module.exports = __webpack_require__(1630);


/***/ }),

/***/ 1363:
/***/ (function(module, exports, __webpack_require__) {

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 networkRequest = __webpack_require__(1367);
const workerUtils = __webpack_require__(1368);

module.exports = {
  networkRequest,
  workerUtils
};

/***/ }),

/***/ 1367:
/***/ (function(module, exports) {

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 networkRequest(url, opts) {
  return fetch(url, {
    cache: opts.loadFromCache ? "default" : "no-cache"
  }).then(res => {
    if (res.status >= 200 && res.status < 300) {
      return res.text().then(text => ({ content: text }));
    }
    return Promise.reject(`request failed with status ${res.status}`);
  });
}

module.exports = networkRequest;

/***/ }),

/***/ 1368:
/***/ (function(module, exports) {

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"); }); }; }

function WorkerDispatcher() {
  this.msgId = 1;
  this.worker = null;
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

WorkerDispatcher.prototype = {
  start(url) {
    this.worker = new Worker(url);
    this.worker.onerror = () => {
      console.error(`Error in worker ${url}`);
    };
  },

  stop() {
    if (!this.worker) {
      return;
    }

    this.worker.terminate();
    this.worker = null;
  },

  task(method) {
    return (...args) => {
      return new Promise((resolve, reject) => {
        const id = this.msgId++;
        this.worker.postMessage({ id, method, args });

        const listener = ({ data: result }) => {
          if (result.id !== id) {
            return;
          }

          if (!this.worker) {
            return;
          }

          this.worker.removeEventListener("message", listener);
          if (result.error) {
            reject(result.error);
          } else {
            resolve(result.response);
          }
        };

        this.worker.addEventListener("message", listener);
      });
    };
  }
};

function workerHandler(publicInterface) {
  return function (msg) {
    const { id, method, args } = msg.data;
    try {
      const response = publicInterface[method].apply(undefined, args);
      if (response instanceof Promise) {
        response.then(val => self.postMessage({ id, response: val }),
        // Error can't be sent via postMessage, so be sure to
        // convert to string.
        err => self.postMessage({ id, error: err.toString() }));
      } else {
        self.postMessage({ id, response });
      }
    } catch (error) {
      // Error can't be sent via postMessage, so be sure to convert to
      // string.
      self.postMessage({ id, error: error.toString() });
    }
  };
}

function streamingWorkerHandler(publicInterface, { timeout = 100 } = {}, worker = self) {
  let streamingWorker = (() => {
    var _ref = _asyncToGenerator(function* (id, tasks) {
      let isWorking = true;

      const intervalId = setTimeout(function () {
        isWorking = false;
      }, timeout);

      const results = [];
      while (tasks.length !== 0 && isWorking) {
        const { callback, context, args } = tasks.shift();
        const result = yield callback.call(context, args);
        results.push(result);
      }
      worker.postMessage({ id, status: "pending", data: results });
      clearInterval(intervalId);

      if (tasks.length !== 0) {
        yield streamingWorker(id, tasks);
      }
    });

    return function streamingWorker(_x, _x2) {
      return _ref.apply(this, arguments);
    };
  })();

  return (() => {
    var _ref2 = _asyncToGenerator(function* (msg) {
      const { id, method, args } = msg.data;
      const workerMethod = publicInterface[method];
      if (!workerMethod) {
        console.error(`Could not find ${method} defined in worker.`);
      }
      worker.postMessage({ id, status: "start" });

      try {
        const tasks = workerMethod(args);
        yield streamingWorker(id, tasks);
        worker.postMessage({ id, status: "done" });
      } catch (error) {
        worker.postMessage({ id, status: "error", error });
      }
    });

    return function (_x3) {
      return _ref2.apply(this, arguments);
    };
  })();
}

module.exports = {
  WorkerDispatcher,
  workerHandler,
  streamingWorkerHandler
};

/***/ }),

/***/ 1630:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var _prettyFast = __webpack_require__(802);

var _prettyFast2 = _interopRequireDefault(_prettyFast);

var _devtoolsUtils = __webpack_require__(1363);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 { workerHandler } = _devtoolsUtils.workerUtils;

function prettyPrint({ url, indent, sourceText }) {
  const prettified = (0, _prettyFast2.default)(sourceText, {
    url: url,
    indent: " ".repeat(indent)
  });

  return {
    code: prettified.code,
    mappings: invertMappings(prettified.map._mappings)
  };
}

function invertMappings(mappings) {
  return mappings._array.map(m => {
    const mapping = {
      generated: {
        line: m.originalLine,
        column: m.originalColumn
      }
    };
    if (m.source) {
      mapping.source = m.source;
      mapping.original = {
        line: m.generatedLine,
        column: m.generatedColumn
      };
      mapping.name = m.name;
    }
    return mapping;
  });
}

self.onmessage = workerHandler({ prettyPrint });

/***/ }),

/***/ 381:
/***/ (function(module, exports, __webpack_require__) {

(function (global, factory) {
	 true ? factory(exports) :
	typeof define === 'function' && define.amd ? define(['exports'], factory) :
	(factory((global.acorn = global.acorn || {})));
}(this, (function (exports) { 'use strict';

// Reserved word lists for various dialects of the language

var reservedWords = {
  3: "abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized throws transient volatile",
  5: "class enum extends super const export import",
  6: "enum",
  strict: "implements interface let package private protected public static yield",
  strictBind: "eval arguments"
};

// And the keywords

var ecma5AndLessKeywords = "break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this";

var keywords = {
  5: ecma5AndLessKeywords,
  6: ecma5AndLessKeywords + " const class extends export import super"
};

// ## Character categories

// Big ugly regular expressions that match characters in the
// whitespace, identifier, and identifier-start categories. These
// are only applied when a character is found to actually have a
// code point above 128.
// Generated by `bin/generate-identifier-regex.js`.

var nonASCIIidentifierStartChars = "\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";
var nonASCIIidentifierChars = "\u200c\u200d\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\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u08d4-\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\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\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-\u0c03\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\u0d01-\u0d03\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\u1cf8\u1cf9\u1dc0-\u1df5\u1dfb-\u1dff\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\ua900-\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";

var nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]");
var nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]");

nonASCIIidentifierStartChars = nonASCIIidentifierChars = null;

// These are a run-length and offset encoded representation of the
// >0xffff code points that are a valid part of identifiers. The
// offset starts at 0x10000, and each pair of numbers represents an
// offset to the next range, and then a size of the range. They were
// generated by bin/generate-identifier-regex.js

// eslint-disable-next-line comma-spacing
var astralIdentifierStartCodes = [0,11,2,25,2,18,2,1,2,14,3,13,35,122,70,52,268,28,4,48,48,31,17,26,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,26,45,28,4,28,36,7,2,27,28,53,11,21,11,18,14,17,111,72,56,50,14,50,785,52,76,44,33,24,27,35,42,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,54,47,21,1,2,0,185,46,42,3,37,47,21,0,60,42,86,25,391,63,32,0,449,56,264,8,2,36,18,0,50,29,881,921,103,110,18,195,2749,1070,4050,582,8634,568,8,30,114,29,19,47,17,3,32,20,6,18,881,68,12,0,67,12,65,0,32,6124,20,754,9486,1,3071,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,4149,196,60,67,1213,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,10591,541];

// eslint-disable-next-line comma-spacing
var astralIdentifierCodes = [509,0,227,0,150,4,294,9,1368,2,2,1,6,3,41,2,5,0,166,1,1306,2,54,14,32,9,16,3,46,10,54,9,7,2,37,13,2,9,52,0,13,2,49,13,10,2,4,9,83,11,7,0,161,11,6,9,7,3,57,0,2,6,3,1,3,2,10,0,11,1,3,6,4,4,193,17,10,9,87,19,13,9,214,6,3,8,28,1,83,16,16,9,82,12,9,9,84,14,5,9,423,9,838,7,2,7,17,9,57,21,2,13,19882,9,135,4,60,6,26,9,1016,45,17,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,2214,6,110,6,6,9,792487,239];

// This has a complexity linear to the value of the code. The
// assumption is that looking up astral identifier characters is
// rare.
function isInAstralSet(code, set) {
  var pos = 0x10000;
  for (var i = 0; i < set.length; i += 2) {
    pos += set[i];
    if (pos > code) { return false }
    pos += set[i + 1];
    if (pos >= code) { return true }
  }
}

// Test whether a given character code starts an identifier.

function isIdentifierStart(code, astral) {
  if (code < 65) { return code === 36 }
  if (code < 91) { return true }
  if (code < 97) { return code === 95 }
  if (code < 123) { return true }
  if (code <= 0xffff) { return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code)) }
  if (astral === false) { return false }
  return isInAstralSet(code, astralIdentifierStartCodes)
}

// Test whether a given character is part of an identifier.

function isIdentifierChar(code, astral) {
  if (code < 48) { return code === 36 }
  if (code < 58) { return true }
  if (code < 65) { return false }
  if (code < 91) { return true }
  if (code < 97) { return code === 95 }
  if (code < 123) { return true }
  if (code <= 0xffff) { return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code)) }
  if (astral === false) { return false }
  return isInAstralSet(code, astralIdentifierStartCodes) || isInAstralSet(code, astralIdentifierCodes)
}

// ## Token types

// The assignment of fine-grained, information-carrying type objects
// allows the tokenizer to store the information it has about a
// token in a way that is very cheap for the parser to look up.

// All token type variables start with an underscore, to make them
// easy to recognize.

// The `beforeExpr` property is used to disambiguate between regular
// expressions and divisions. It is set on all token types that can
// be followed by an expression (thus, a slash after them would be a
// regular expression).
//
// The `startsExpr` property is used to check if the token ends a
// `yield` expression. It is set on all token types that either can
// directly start an expression (like a quotation mark) or can
// continue an expression (like the body of a string).
//
// `isLoop` marks a keyword as starting a loop, which is important
// to know when parsing a label, in order to allow or disallow
// continue jumps to that label.

var TokenType = function TokenType(label, conf) {
  if ( conf === void 0 ) conf = {};

  this.label = label;
  this.keyword = conf.keyword;
  this.beforeExpr = !!conf.beforeExpr;
  this.startsExpr = !!conf.startsExpr;
  this.isLoop = !!conf.isLoop;
  this.isAssign = !!conf.isAssign;
  this.prefix = !!conf.prefix;
  this.postfix = !!conf.postfix;
  this.binop = conf.binop || null;
  this.updateContext = null;
};

function binop(name, prec) {
  return new TokenType(name, {beforeExpr: true, binop: prec})
}
var beforeExpr = {beforeExpr: true};
var startsExpr = {startsExpr: true};

// Map keyword names to token types.

var keywords$1 = {};

// Succinct definitions of keyword token types
function kw(name, options) {
  if ( options === void 0 ) options = {};

  options.keyword = name;
  return keywords$1[name] = new TokenType(name, options)
}

var types = {
  num: new TokenType("num", startsExpr),
  regexp: new TokenType("regexp", startsExpr),
  string: new TokenType("string", startsExpr),
  name: new TokenType("name", startsExpr),
  eof: new TokenType("eof"),

  // Punctuation token types.
  bracketL: new TokenType("[", {beforeExpr: true, startsExpr: true}),
  bracketR: new TokenType("]"),
  braceL: new TokenType("{", {beforeExpr: true, startsExpr: true}),
  braceR: new TokenType("}"),
  parenL: new TokenType("(", {beforeExpr: true, startsExpr: true}),
  parenR: new TokenType(")"),
  comma: new TokenType(",", beforeExpr),
  semi: new TokenType(";", beforeExpr),
  colon: new TokenType(":", beforeExpr),
  dot: new TokenType("."),
  question: new TokenType("?", beforeExpr),
  arrow: new TokenType("=>", beforeExpr),
  template: new TokenType("template"),
  invalidTemplate: new TokenType("invalidTemplate"),
  ellipsis: new TokenType("...", beforeExpr),
  backQuote: new TokenType("`", startsExpr),
  dollarBraceL: new TokenType("${", {beforeExpr: true, startsExpr: true}),

  // Operators. These carry several kinds of properties to help the
  // parser use them properly (the presence of these properties is
  // what categorizes them as operators).
  //
  // `binop`, when present, specifies that this operator is a binary
  // operator, and will refer to its precedence.
  //
  // `prefix` and `postfix` mark the operator as a prefix or postfix
  // unary operator.
  //
  // `isAssign` marks all of `=`, `+=`, `-=` etcetera, which act as
  // binary operators with a very low precedence, that should result
  // in AssignmentExpression nodes.

  eq: new TokenType("=", {beforeExpr: true, isAssign: true}),
  assign: new TokenType("_=", {beforeExpr: true, isAssign: true}),
  incDec: new TokenType("++/--", {prefix: true, postfix: true, startsExpr: true}),
  prefix: new TokenType("!/~", {beforeExpr: true, prefix: true, startsExpr: true}),
  logicalOR: binop("||", 1),
  logicalAND: binop("&&", 2),
  bitwiseOR: binop("|", 3),
  bitwiseXOR: binop("^", 4),
  bitwiseAND: binop("&", 5),
  equality: binop("==/!=/===/!==", 6),
  relational: binop("</>/<=/>=", 7),
  bitShift: binop("<</>>/>>>", 8),
  plusMin: new TokenType("+/-", {beforeExpr: true, binop: 9, prefix: true, startsExpr: true}),
  modulo: binop("%", 10),
  star: binop("*", 10),
  slash: binop("/", 10),
  starstar: new TokenType("**", {beforeExpr: true}),

  // Keyword token types.
  _break: kw("break"),
  _case: kw("case", beforeExpr),
  _catch: kw("catch"),
  _continue: kw("continue"),
  _debugger: kw("debugger"),
  _default: kw("default", beforeExpr),
  _do: kw("do", {isLoop: true, beforeExpr: true}),
  _else: kw("else", beforeExpr),
  _finally: kw("finally"),
  _for: kw("for", {isLoop: true}),
  _function: kw("function", startsExpr),
  _if: kw("if"),
  _return: kw("return", beforeExpr),
  _switch: kw("switch"),
  _throw: kw("throw", beforeExpr),
  _try: kw("try"),
  _var: kw("var"),
  _const: kw("const"),
  _while: kw("while", {isLoop: true}),
  _with: kw("with"),
  _new: kw("new", {beforeExpr: true, startsExpr: true}),
  _this: kw("this", startsExpr),
  _super: kw("super", startsExpr),
  _class: kw("class", startsExpr),
  _extends: kw("extends", beforeExpr),
  _export: kw("export"),
  _import: kw("import"),
  _null: kw("null", startsExpr),
  _true: kw("true", startsExpr),
  _false: kw("false", startsExpr),
  _in: kw("in", {beforeExpr: true, binop: 7}),
  _instanceof: kw("instanceof", {beforeExpr: true, binop: 7}),
  _typeof: kw("typeof", {beforeExpr: true, prefix: true, startsExpr: true}),
  _void: kw("void", {beforeExpr: true, prefix: true, startsExpr: true}),
  _delete: kw("delete", {beforeExpr: true, prefix: true, startsExpr: true})
};

// Matches a whole line break (where CRLF is considered a single
// line break). Used to count lines.

var lineBreak = /\r\n?|\n|\u2028|\u2029/;
var lineBreakG = new RegExp(lineBreak.source, "g");

function isNewLine(code) {
  return code === 10 || code === 13 || code === 0x2028 || code === 0x2029
}

var nonASCIIwhitespace = /[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/;

var skipWhiteSpace = /(?:\s|\/\/.*|\/\*[^]*?\*\/)*/g;

var ref = Object.prototype;
var hasOwnProperty = ref.hasOwnProperty;
var toString = ref.toString;

// Checks if an object has a property.

function has(obj, propName) {
  return hasOwnProperty.call(obj, propName)
}

var isArray = Array.isArray || (function (obj) { return (
  toString.call(obj) === "[object Array]"
); });

// These are used when `options.locations` is on, for the
// `startLoc` and `endLoc` properties.

var Position = function Position(line, col) {
  this.line = line;
  this.column = col;
};

Position.prototype.offset = function offset (n) {
  return new Position(this.line, this.column + n)
};

var SourceLocation = function SourceLocation(p, start, end) {
  this.start = start;
  this.end = end;
  if (p.sourceFile !== null) { this.source = p.sourceFile; }
};

// The `getLineInfo` function is mostly useful when the
// `locations` option is off (for performance reasons) and you
// want to find the line/column position for a given character
// offset. `input` should be the code string that the offset refers
// into.

function getLineInfo(input, offset) {
  for (var line = 1, cur = 0;;) {
    lineBreakG.lastIndex = cur;
    var match = lineBreakG.exec(input);
    if (match && match.index < offset) {
      ++line;
      cur = match.index + match[0].length;
    } else {
      return new Position(line, offset - cur)
    }
  }
}

// A second optional argument can be given to further configure
// the parser process. These options are recognized:

var defaultOptions = {
  // `ecmaVersion` indicates the ECMAScript version to parse. Must
  // be either 3, 5, 6 (2015), 7 (2016), or 8 (2017). This influences support
  // for strict mode, the set of reserved words, and support for
  // new syntax features. The default is 7.
  ecmaVersion: 7,
  // `sourceType` indicates the mode the code should be parsed in.
  // Can be either `"script"` or `"module"`. This influences global
  // strict mode and parsing of `import` and `export` declarations.
  sourceType: "script",
  // `onInsertedSemicolon` can be a callback that will be called
  // when a semicolon is automatically inserted. It will be passed
  // th position of the comma as an offset, and if `locations` is
  // enabled, it is given the location as a `{line, column}` object
  // as second argument.
  onInsertedSemicolon: null,
  // `onTrailingComma` is similar to `onInsertedSemicolon`, but for
  // trailing commas.
  onTrailingComma: null,
  // By default, reserved words are only enforced if ecmaVersion >= 5.
  // Set `allowReserved` to a boolean value to explicitly turn this on
  // an off. When this option has the value "never", reserved words
  // and keywords can also not be used as property names.
  allowReserved: null,
  // When enabled, a return at the top level is not considered an
  // error.
  allowReturnOutsideFunction: false,
  // When enabled, import/export statements are not constrained to
  // appearing at the top of the program.
  allowImportExportEverywhere: false,
  // When enabled, hashbang directive in the beginning of file
  // is allowed and treated as a line comment.
  allowHashBang: false,
  // When `locations` is on, `loc` properties holding objects with
  // `start` and `end` properties in `{line, column}` form (with
  // line being 1-based and column 0-based) will be attached to the
  // nodes.
  locations: false,
  // A function can be passed as `onToken` option, which will
  // cause Acorn to call that function with object in the same
  // format as tokens returned from `tokenizer().getToken()`. Note
  // that you are not allowed to call the parser from the
  // callback—that will corrupt its internal state.
  onToken: null,
  // A function can be passed as `onComment` option, which will
  // cause Acorn to call that function with `(block, text, start,
  // end)` parameters whenever a comment is skipped. `block` is a
  // boolean indicating whether this is a block (`/* */`) comment,
  // `text` is the content of the comment, and `start` and `end` are
  // character offsets that denote the start and end of the comment.
  // When the `locations` option is on, two more parameters are
  // passed, the full `{line, column}` locations of the start and
  // end of the comments. Note that you are not allowed to call the
  // parser from the callback—that will corrupt its internal state.
  onComment: null,
  // Nodes have their start and end characters offsets recorded in
  // `start` and `end` properties (directly on the node, rather than
  // the `loc` object, which holds line/column data. To also add a
  // [semi-standardized][range] `range` property holding a `[start,
  // end]` array with the same numbers, set the `ranges` option to
  // `true`.
  //
  // [range]: https://bugzilla.mozilla.org/show_bug.cgi?id=745678
  ranges: false,
  // It is possible to parse multiple files into a single AST by
  // passing the tree produced by parsing the first file as
  // `program` option in subsequent parses. This will add the
  // toplevel forms of the parsed file to the `Program` (top) node
  // of an existing parse tree.
  program: null,
  // When `locations` is on, you can pass this to record the source
  // file in every node's `loc` object.
  sourceFile: null,
  // This value, if given, is stored in every node, whether
  // `locations` is on or off.
  directSourceFile: null,
  // When enabled, parenthesized expressions are represented by
  // (non-standard) ParenthesizedExpression nodes
  preserveParens: false,
  plugins: {}
};

// Interpret and default an options object

function getOptions(opts) {
  var options = {};

  for (var opt in defaultOptions)
    { options[opt] = opts && has(opts, opt) ? opts[opt] : defaultOptions[opt]; }

  if (options.ecmaVersion >= 2015)
    { options.ecmaVersion -= 2009; }

  if (options.allowReserved == null)
    { options.allowReserved = options.ecmaVersion < 5; }

  if (isArray(options.onToken)) {
    var tokens = options.onToken;
    options.onToken = function (token) { return tokens.push(token); };
  }
  if (isArray(options.onComment))
    { options.onComment = pushComment(options, options.onComment); }

  return options
}

function pushComment(options, array) {
  return function(block, text, start, end, startLoc, endLoc) {
    var comment = {
      type: block ? "Block" : "Line",
      value: text,
      start: start,
      end: end
    };
    if (options.locations)
      { comment.loc = new SourceLocation(this, startLoc, endLoc); }
    if (options.ranges)
      { comment.range = [start, end]; }
    array.push(comment);
  }
}

// Registered plugins
var plugins = {};

function keywordRegexp(words) {
  return new RegExp("^(?:" + words.replace(/ /g, "|") + ")$")
}

var Parser = function Parser(options, input, startPos) {
  this.options = options = getOptions(options);
  this.sourceFile = options.sourceFile;
  this.keywords = keywordRegexp(keywords[options.ecmaVersion >= 6 ? 6 : 5]);
  var reserved = "";
  if (!options.allowReserved) {
    for (var v = options.ecmaVersion;; v--)
      { if (reserved = reservedWords[v]) { break } }
    if (options.sourceType == "module") { reserved += " await"; }
  }
  this.reservedWords = keywordRegexp(reserved);
  var reservedStrict = (reserved ? reserved + " " : "") + reservedWords.strict;
  this.reservedWordsStrict = keywordRegexp(reservedStrict);
  this.reservedWordsStrictBind = keywordRegexp(reservedStrict + " " + reservedWords.strictBind);
  this.input = String(input);

  // Used to signal to callers of `readWord1` whether the word
  // contained any escape sequences. This is needed because words with
  // escape sequences must not be interpreted as keywords.
  this.containsEsc = false;

  // Load plugins
  this.loadPlugins(options.plugins);

  // Set up token state

  // The current position of the tokenizer in the input.
  if (startPos) {
    this.pos = startPos;
    this.lineStart = this.input.lastIndexOf("\n", startPos - 1) + 1;
    this.curLine = this.input.slice(0, this.lineStart).split(lineBreak).length;
  } else {
    this.pos = this.lineStart = 0;
    this.curLine = 1;
  }

  // Properties of the current token:
  // Its type
  this.type = types.eof;
  // For tokens that include more information than their type, the value
  this.value = null;
  // Its start and end offset
  this.start = this.end = this.pos;
  // And, if locations are used, the {line, column} object
  // corresponding to those offsets
  this.startLoc = this.endLoc = this.curPosition();

  // Position information for the previous token
  this.lastTokEndLoc = this.lastTokStartLoc = null;
  this.lastTokStart = this.lastTokEnd = this.pos;

  // The context stack is used to superficially track syntactic
  // context to predict whether a regular expression is allowed in a
  // given position.
  this.context = this.initialContext();
  this.exprAllowed = true;

  // Figure out if it's a module code.
  this.inModule = options.sourceType === "module";
  this.strict = this.inModule || this.strictDirective(this.pos);

  // Used to signify the start of a potential arrow function
  this.potentialArrowAt = -1;

  // Flags to track whether we are in a function, a generator, an async function.
  this.inFunction = this.inGenerator = this.inAsync = false;
  // Positions to delayed-check that yield/await does not exist in default parameters.
  this.yieldPos = this.awaitPos = 0;
  // Labels in scope.
  this.labels = [];

  // If enabled, skip leading hashbang line.
  if (this.pos === 0 && options.allowHashBang && this.input.slice(0, 2) === "#!")
    { this.skipLineComment(2); }

  // Scope tracking for duplicate variable names (see scope.js)
  this.scopeStack = [];
  this.enterFunctionScope();
};

// DEPRECATED Kept for backwards compatibility until 3.0 in case a plugin uses them
Parser.prototype.isKeyword = function isKeyword (word) { return this.keywords.test(word) };
Parser.prototype.isReservedWord = function isReservedWord (word) { return this.reservedWords.test(word) };

Parser.prototype.extend = function extend (name, f) {
  this[name] = f(this[name]);
};

Parser.prototype.loadPlugins = function loadPlugins (pluginConfigs) {
    var this$1 = this;

  for (var name in pluginConfigs) {
    var plugin = plugins[name];
    if (!plugin) { throw new Error("Plugin '" + name + "' not found") }
    plugin(this$1, pluginConfigs[name]);
  }
};

Parser.prototype.parse = function parse () {
  var node = this.options.program || this.startNode();
  this.nextToken();
  return this.parseTopLevel(node)
};

var pp = Parser.prototype;

// ## Parser utilities

var literal = /^(?:'((?:\\.|[^'])*?)'|"((?:\\.|[^"])*?)"|;)/;
pp.strictDirective = function(start) {
  var this$1 = this;

  for (;;) {
    skipWhiteSpace.lastIndex = start;
    start += skipWhiteSpace.exec(this$1.input)[0].length;
    var match = literal.exec(this$1.input.slice(start));
    if (!match) { return false }
    if ((match[1] || match[2]) == "use strict") { return true }
    start += match[0].length;
  }
};

// Predicate that tests whether the next token is of the given
// type, and if yes, consumes it as a side effect.

pp.eat = function(type) {
  if (this.type === type) {
    this.next();
    return true
  } else {
    return false
  }
};

// Tests whether parsed token is a contextual keyword.

pp.isContextual = function(name) {
  return this.type === types.name && this.value === name
};

// Consumes contextual keyword if possible.

pp.eatContextual = function(name) {
  return this.value === name && this.eat(types.name)
};

// Asserts that following token is given contextual keyword.

pp.expectContextual = function(name) {
  if (!this.eatContextual(name)) { this.unexpected(); }
};

// Test whether a semicolon can be inserted at the current position.

pp.canInsertSemicolon = function() {
  return this.type === types.eof ||
    this.type === types.braceR ||
    lineBreak.test(this.input.slice(this.lastTokEnd, this.start))
};

pp.insertSemicolon = function() {
  if (this.canInsertSemicolon()) {
    if (this.options.onInsertedSemicolon)
      { this.options.onInsertedSemicolon(this.lastTokEnd, this.lastTokEndLoc); }
    return true
  }
};

// Consume a semicolon, or, failing that, see if we are allowed to
// pretend that there is a semicolon at this position.

pp.semicolon = function() {
  if (!this.eat(types.semi) && !this.insertSemicolon()) { this.unexpected(); }
};

pp.afterTrailingComma = function(tokType, notNext) {
  if (this.type == tokType) {
    if (this.options.onTrailingComma)
      { this.options.onTrailingComma(this.lastTokStart, this.lastTokStartLoc); }
    if (!notNext)
      { this.next(); }
    return true
  }
};

// Expect a token of a given type. If found, consume it, otherwise,
// raise an unexpected token error.

pp.expect = function(type) {
  this.eat(type) || this.unexpected();
};

// Raise an unexpected token error.

pp.unexpected = function(pos) {
  this.raise(pos != null ? pos : this.start, "Unexpected token");
};

function DestructuringErrors() {
  this.shorthandAssign =
  this.trailingComma =
  this.parenthesizedAssign =
  this.parenthesizedBind =
    -1;
}

pp.checkPatternErrors = function(refDestructuringErrors, isAssign) {
  if (!refDestructuringErrors) { return }
  if (refDestructuringErrors.trailingComma > -1)
    { this.raiseRecoverable(refDestructuringErrors.trailingComma, "Comma is not permitted after the rest element"); }
  var parens = isAssign ? refDestructuringErrors.parenthesizedAssign : refDestructuringErrors.parenthesizedBind;
  if (parens > -1) { this.raiseRecoverable(parens, "Parenthesized pattern"); }
};

pp.checkExpressionErrors = function(refDestructuringErrors, andThrow) {
  var pos = refDestructuringErrors ? refDestructuringErrors.shorthandAssign : -1;
  if (!andThrow) { return pos >= 0 }
  if (pos > -1) { this.raise(pos, "Shorthand property assignments are valid only in destructuring patterns"); }
};

pp.checkYieldAwaitInDefaultParams = function() {
  if (this.yieldPos && (!this.awaitPos || this.yieldPos < this.awaitPos))
    { this.raise(this.yieldPos, "Yield expression cannot be a default value"); }
  if (this.awaitPos)
    { this.raise(this.awaitPos, "Await expression cannot be a default value"); }
};

pp.isSimpleAssignTarget = function(expr) {
  if (expr.type === "ParenthesizedExpression")
    { return this.isSimpleAssignTarget(expr.expression) }
  return expr.type === "Identifier" || expr.type === "MemberExpression"
};

var pp$1 = Parser.prototype;

// ### Statement parsing

// Parse a program. Initializes the parser, reads any number of
// statements, and wraps them in a Program node.  Optionally takes a
// `program` argument.  If present, the statements will be appended
// to its body instead of creating a new node.

pp$1.parseTopLevel = function(node) {
  var this$1 = this;

  var exports = {};
  if (!node.body) { node.body = []; }
  while (this.type !== types.eof) {
    var stmt = this$1.parseStatement(true, true, exports);
    node.body.push(stmt);
  }
  this.adaptDirectivePrologue(node.body);
  this.next();
  if (this.options.ecmaVersion >= 6) {
    node.sourceType = this.options.sourceType;
  }
  return this.finishNode(node, "Program")
};

var loopLabel = {kind: "loop"};
var switchLabel = {kind: "switch"};

pp$1.isLet = function() {
  if (this.type !== types.name || this.options.ecmaVersion < 6 || this.value != "let") { return false }
  skipWhiteSpace.lastIndex = this.pos;
  var skip = skipWhiteSpace.exec(this.input);
  var next = this.pos + skip[0].length, nextCh = this.input.charCodeAt(next);
  if (nextCh === 91 || nextCh == 123) { return true } // '{' and '['
  if (isIdentifierStart(nextCh, true)) {
    var pos = next + 1;
    while (isIdentifierChar(this.input.charCodeAt(pos), true)) { ++pos; }
    var ident = this.input.slice(next, pos);
    if (!this.isKeyword(ident)) { return true }
  }
  return false
};

// check 'async [no LineTerminator here] function'
// - 'async /*foo*/ function' is OK.
// - 'async /*\n*/ function' is invalid.
pp$1.isAsyncFunction = function() {
  if (this.type !== types.name || this.options.ecmaVersion < 8 || this.value != "async")
    { return false }

  skipWhiteSpace.lastIndex = this.pos;
  var skip = skipWhiteSpace.exec(this.input);
  var next = this.pos + skip[0].length;
  return !lineBreak.test(this.input.slice(this.pos, next)) &&
    this.input.slice(next, next + 8) === "function" &&
    (next + 8 == this.input.length || !isIdentifierChar(this.input.charAt(next + 8)))
};

// Parse a single statement.
//
// If expecting a statement and finding a slash operator, parse a
// regular expression literal. This is to handle cases like
// `if (foo) /blah/.exec(foo)`, where looking at the previous token
// does not help.

pp$1.parseStatement = function(declaration, topLevel, exports) {
  var starttype = this.type, node = this.startNode(), kind;

  if (this.isLet()) {
    starttype = types._var;
    kind = "let";
  }

  // Most types of statements are recognized by the keyword they
  // start with. Many are trivial to parse, some require a bit of
  // complexity.

  switch (starttype) {
  case types._break: case types._continue: return this.parseBreakContinueStatement(node, starttype.keyword)
  case types._debugger: return this.parseDebuggerStatement(node)
  case types._do: return this.parseDoStatement(node)
  case types._for: return this.parseForStatement(node)
  case types._function:
    if (!declaration && this.options.ecmaVersion >= 6) { this.unexpected(); }
    return this.parseFunctionStatement(node, false)
  case types._class:
    if (!declaration) { this.unexpected(); }
    return this.parseClass(node, true)
  case types._if: return this.parseIfStatement(node)
  case types._return: return this.parseReturnStatement(node)
  case types._switch: return this.parseSwitchStatement(node)
  case types._throw: return this.parseThrowStatement(node)
  case types._try: return this.parseTryStatement(node)
  case types._const: case types._var:
    kind = kind || this.value;
    if (!declaration && kind != "var") { this.unexpected(); }
    return this.parseVarStatement(node, kind)
  case types._while: return this.parseWhileStatement(node)
  case types._with: return this.parseWithStatement(node)
  case types.braceL: return this.parseBlock()
  case types.semi: return this.parseEmptyStatement(node)
  case types._export:
  case types._import:
    if (!this.options.allowImportExportEverywhere) {
      if (!topLevel)
        { this.raise(this.start, "'import' and 'export' may only appear at the top level"); }
      if (!this.inModule)
        { this.raise(this.start, "'import' and 'export' may appear only with 'sourceType: module'"); }
    }
    return starttype === types._import ? this.parseImport(node) : this.parseExport(node, exports)

    // If the statement does not start with a statement keyword or a
    // brace, it's an ExpressionStatement or LabeledStatement. We
    // simply start parsing an expression, and afterwards, if the
    // next token is a colon and the expression was a simple
    // Identifier node, we switch to interpreting it as a label.
  default:
    if (this.isAsyncFunction() && declaration) {
      this.next();
      return this.parseFunctionStatement(node, true)
    }

    var maybeName = this.value, expr = this.parseExpression();
    if (starttype === types.name && expr.type === "Identifier" && this.eat(types.colon))
      { return this.parseLabeledStatement(node, maybeName, expr) }
    else { return this.parseExpressionStatement(node, expr) }
  }
};

pp$1.parseBreakContinueStatement = function(node, keyword) {
  var this$1 = this;

  var isBreak = keyword == "break";
  this.next();
  if (this.eat(types.semi) || this.insertSemicolon()) { node.label = null; }
  else if (this.type !== types.name) { this.unexpected(); }
  else {
    node.label = this.parseIdent();
    this.semicolon();
  }

  // Verify that there is an actual destination to break or
  // continue to.
  var i = 0;
  for (; i < this.labels.length; ++i) {
    var lab = this$1.labels[i];
    if (node.label == null || lab.name === node.label.name) {
      if (lab.kind != null && (isBreak || lab.kind === "loop")) { break }
      if (node.label && isBreak) { break }
    }
  }
  if (i === this.labels.length) { this.raise(node.start, "Unsyntactic " + keyword); }
  return this.finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement")
};

pp$1.parseDebuggerStatement = function(node) {
  this.next();
  this.semicolon();
  return this.finishNode(node, "DebuggerStatement")
};

pp$1.parseDoStatement = function(node) {
  this.next();
  this.labels.push(loopLabel);
  node.body = this.parseStatement(false);
  this.labels.pop();
  this.expect(types._while);
  node.test = this.parseParenExpression();
  if (this.options.ecmaVersion >= 6)
    { this.eat(types.semi); }
  else
    { this.semicolon(); }
  return this.finishNode(node, "DoWhileStatement")
};

// Disambiguating between a `for` and a `for`/`in` or `for`/`of`
// loop is non-trivial. Basically, we have to parse the init `var`
// statement or expression, disallowing the `in` operator (see
// the second parameter to `parseExpression`), and then check
// whether the next token is `in` or `of`. When there is no init
// part (semicolon immediately after the opening parenthesis), it
// is a regular `for` loop.

pp$1.parseForStatement = function(node) {
  this.next();
  this.labels.push(loopLabel);
  this.enterLexicalScope();
  this.expect(types.parenL);
  if (this.type === types.semi) { return this.parseFor(node, null) }
  var isLet = this.isLet();
  if (this.type === types._var || this.type === types._const || isLet) {
    var init$1 = this.startNode(), kind = isLet ? "let" : this.value;
    this.next();
    this.parseVar(init$1, true, kind);
    this.finishNode(init$1, "VariableDeclaration");
    if ((this.type === types._in || (this.options.ecmaVersion >= 6 && this.isContextual("of"))) && init$1.declarations.length === 1 &&
        !(kind !== "var" && init$1.declarations[0].init))
      { return this.parseForIn(node, init$1) }
    return this.parseFor(node, init$1)
  }
  var refDestructuringErrors = new DestructuringErrors;
  var init = this.parseExpression(true, refDestructuringErrors);
  if (this.type === types._in || (this.options.ecmaVersion >= 6 && this.isContextual("of"))) {
    this.toAssignable(init);
    this.checkLVal(init);
    this.checkPatternErrors(refDestructuringErrors, true);
    return this.parseForIn(node, init)
  } else {
    this.checkExpressionErrors(refDestructuringErrors, true);
  }
  return this.parseFor(node, init)
};

pp$1.parseFunctionStatement = function(node, isAsync) {
  this.next();
  return this.parseFunction(node, true, false, isAsync)
};

pp$1.isFunction = function() {
  return this.type === types._function || this.isAsyncFunction()
};

pp$1.parseIfStatement = function(node) {
  this.next();
  node.test = this.parseParenExpression();
  // allow function declarations in branches, but only in non-strict mode
  node.consequent = this.parseStatement(!this.strict && this.isFunction());
  node.alternate = this.eat(types._else) ? this.parseStatement(!this.strict && this.isFunction()) : null;
  return this.finishNode(node, "IfStatement")
};

pp$1.parseReturnStatement = function(node) {
  if (!this.inFunction && !this.options.allowReturnOutsideFunction)
    { this.raise(this.start, "'return' outside of function"); }
  this.next();

  // In `return` (and `break`/`continue`), the keywords with
  // optional arguments, we eagerly look for a semicolon or the
  // possibility to insert one.

  if (this.eat(types.semi) || this.insertSemicolon()) { node.argument = null; }
  else { node.argument = this.parseExpression(); this.semicolon(); }
  return this.finishNode(node, "ReturnStatement")
};

pp$1.parseSwitchStatement = function(node) {
  var this$1 = this;

  this.next();
  node.discriminant = this.parseParenExpression();
  node.cases = [];
  this.expect(types.braceL);
  this.labels.push(switchLabel);
  this.enterLexicalScope();

  // Statements under must be grouped (by label) in SwitchCase
  // nodes. `cur` is used to keep the node that we are currently
  // adding statements to.

  var cur;
  for (var sawDefault = false; this.type != types.braceR;) {
    if (this$1.type === types._case || this$1.type === types._default) {
      var isCase = this$1.type === types._case;
      if (cur) { this$1.finishNode(cur, "SwitchCase"); }
      node.cases.push(cur = this$1.startNode());
      cur.consequent = [];
      this$1.next();
      if (isCase) {
        cur.test = this$1.parseExpression();
      } else {
        if (sawDefault) { this$1.raiseRecoverable(this$1.lastTokStart, "Multiple default clauses"); }
        sawDefault = true;
        cur.test = null;
      }
      this$1.expect(types.colon);
    } else {
      if (!cur) { this$1.unexpected(); }
      cur.consequent.push(this$1.parseStatement(true));
    }
  }
  this.exitLexicalScope();
  if (cur) { this.finishNode(cur, "SwitchCase"); }
  this.next(); // Closing brace
  this.labels.pop();
  return this.finishNode(node, "SwitchStatement")
};

pp$1.parseThrowStatement = function(node) {
  this.next();
  if (lineBreak.test(this.input.slice(this.lastTokEnd, this.start)))
    { this.raise(this.lastTokEnd, "Illegal newline after throw"); }
  node.argument = this.parseExpression();
  this.semicolon();
  return this.finishNode(node, "ThrowStatement")
};

// Reused empty array added for node fields that are always empty.

var empty = [];

pp$1.parseTryStatement = function(node) {
  this.next();
  node.block = this.parseBlock();
  node.handler = null;
  if (this.type === types._catch) {
    var clause = this.startNode();
    this.next();
    this.expect(types.parenL);
    clause.param = this.parseBindingAtom();
    this.enterLexicalScope();
    this.checkLVal(clause.param, "let");
    this.expect(types.parenR);
    clause.body = this.parseBlock(false);
    this.exitLexicalScope();
    node.handler = this.finishNode(clause, "CatchClause");
  }
  node.finalizer = this.eat(types._finally) ? this.parseBlock() : null;
  if (!node.handler && !node.finalizer)
    { this.raise(node.start, "Missing catch or finally clause"); }
  return this.finishNode(node, "TryStatement")
};

pp$1.parseVarStatement = function(node, kind) {
  this.next();
  this.parseVar(node, false, kind);
  this.semicolon();
  return this.finishNode(node, "VariableDeclaration")
};

pp$1.parseWhileStatement = function(node) {
  this.next();
  node.test = this.parseParenExpression();
  this.labels.push(loopLabel);
  node.body = this.parseStatement(false);
  this.labels.pop();
  return this.finishNode(node, "WhileStatement")
};

pp$1.parseWithStatement = function(node) {
  if (this.strict) { this.raise(this.start, "'with' in strict mode"); }
  this.next();
  node.object = this.parseParenExpression();
  node.body = this.parseStatement(false);
  return this.finishNode(node, "WithStatement")
};

pp$1.parseEmptyStatement = function(node) {
  this.next();
  return this.finishNode(node, "EmptyStatement")
};

pp$1.parseLabeledStatement = function(node, maybeName, expr) {
  var this$1 = this;

  for (var i$1 = 0, list = this$1.labels; i$1 < list.length; i$1 += 1)
    {
    var label = list[i$1];

    if (label.name === maybeName)
      { this$1.raise(expr.start, "Label '" + maybeName + "' is already declared");
  } }
  var kind = this.type.isLoop ? "loop" : this.type === types._switch ? "switch" : null;
  for (var i = this.labels.length - 1; i >= 0; i--) {
    var label$1 = this$1.labels[i];
    if (label$1.statementStart == node.start) {
      label$1.statementStart = this$1.start;
      label$1.kind = kind;
    } else { break }
  }
  this.labels.push({name: maybeName, kind: kind, statementStart: this.start});
  node.body = this.parseStatement(true);
  if (node.body.type == "ClassDeclaration" ||
      node.body.type == "VariableDeclaration" && node.body.kind != "var" ||
      node.body.type == "FunctionDeclaration" && (this.strict || node.body.generator))
    { this.raiseRecoverable(node.body.start, "Invalid labeled declaration"); }
  this.labels.pop();
  node.label = expr;
  return this.finishNode(node, "LabeledStatement")
};

pp$1.parseExpressionStatement = function(node, expr) {
  node.expression = expr;
  this.semicolon();
  return this.finishNode(node, "ExpressionStatement")
};

// Parse a semicolon-enclosed block of statements, handling `"use
// strict"` declarations when `allowStrict` is true (used for
// function bodies).

pp$1.parseBlock = function(createNewLexicalScope) {
  var this$1 = this;
  if ( createNewLexicalScope === void 0 ) createNewLexicalScope = true;

  var node = this.startNode();
  node.body = [];
  this.expect(types.braceL);
  if (createNewLexicalScope) {
    this.enterLexicalScope();
  }
  while (!this.eat(types.braceR)) {
    var stmt = this$1.parseStatement(true);
    node.body.push(stmt);
  }
  if (createNewLexicalScope) {
    this.exitLexicalScope();
  }
  return this.finishNode(node, "BlockStatement")
};

// Parse a regular `for` loop. The disambiguation code in
// `parseStatement` will already have parsed the init statement or
// expression.

pp$1.parseFor = function(node, init) {
  node.init = init;
  this.expect(types.semi);
  node.test = this.type === types.semi ? null : this.parseExpression();
  this.expect(types.semi);
  node.update = this.type === types.parenR ? null : this.parseExpression();
  this.expect(types.parenR);
  this.exitLexicalScope();
  node.body = this.parseStatement(false);
  this.labels.pop();
  return this.finishNode(node, "ForStatement")
};

// Parse a `for`/`in` and `for`/`of` loop, which are almost
// same from parser's perspective.

pp$1.parseForIn = function(node, init) {
  var type = this.type === types._in ? "ForInStatement" : "ForOfStatement";
  this.next();
  node.left = init;
  node.right = this.parseExpression();
  this.expect(types.parenR);
  this.exitLexicalScope();
  node.body = this.parseStatement(false);
  this.labels.pop();
  return this.finishNode(node, type)
};

// Parse a list of variable declarations.

pp$1.parseVar = function(node, isFor, kind) {
  var this$1 = this;

  node.declarations = [];
  node.kind = kind;
  for (;;) {
    var decl = this$1.startNode();
    this$1.parseVarId(decl, kind);
    if (this$1.eat(types.eq)) {
      decl.init = this$1.parseMaybeAssign(isFor);
    } else if (kind === "const" && !(this$1.type === types._in || (this$1.options.ecmaVersion >= 6 && this$1.isContextual("of")))) {
      this$1.unexpected();
    } else if (decl.id.type != "Identifier" && !(isFor && (this$1.type === types._in || this$1.isContextual("of")))) {
      this$1.raise(this$1.lastTokEnd, "Complex binding patterns require an initialization value");
    } else {
      decl.init = null;
    }
    node.declarations.push(this$1.finishNode(decl, "VariableDeclarator"));
    if (!this$1.eat(types.comma)) { break }
  }
  return node
};

pp$1.parseVarId = function(decl, kind) {
  decl.id = this.parseBindingAtom(kind);
  this.checkLVal(decl.id, kind, false);
};

// Parse a function declaration or literal (depending on the
// `isStatement` parameter).

pp$1.parseFunction = function(node, isStatement, allowExpressionBody, isAsync) {
  this.initFunction(node);
  if (this.options.ecmaVersion >= 6 && !isAsync)
    { node.generator = this.eat(types.star); }
  if (this.options.ecmaVersion >= 8)
    { node.async = !!isAsync; }

  if (isStatement) {
    node.id = isStatement === "nullableID" && this.type != types.name ? null : this.parseIdent();
    if (node.id) {
      this.checkLVal(node.id, "var");
    }
  }

  var oldInGen = this.inGenerator, oldInAsync = this.inAsync,
      oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, oldInFunc = this.inFunction;
  this.inGenerator = node.generator;
  this.inAsync = node.async;
  this.yieldPos = 0;
  this.awaitPos = 0;
  this.inFunction = true;
  this.enterFunctionScope();

  if (!isStatement)
    { node.id = this.type == types.name ? this.parseIdent() : null; }

  this.parseFunctionParams(node);
  this.parseFunctionBody(node, allowExpressionBody);

  this.inGenerator = oldInGen;
  this.inAsync = oldInAsync;
  this.yieldPos = oldYieldPos;
  this.awaitPos = oldAwaitPos;
  this.inFunction = oldInFunc;
  return this.finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression")
};

pp$1.parseFunctionParams = function(node) {
  this.expect(types.parenL);
  node.params = this.parseBindingList(types.parenR, false, this.options.ecmaVersion >= 8);
  this.checkYieldAwaitInDefaultParams();
};

// Parse a class declaration or literal (depending on the
// `isStatement` parameter).

pp$1.parseClass = function(node, isStatement) {
  var this$1 = this;

  this.next();

  this.parseClassId(node, isStatement);
  this.parseClassSuper(node);
  var classBody = this.startNode();
  var hadConstructor = false;
  classBody.body = [];
  this.expect(types.braceL);
  while (!this.eat(types.braceR)) {
    if (this$1.eat(types.semi)) { continue }
    var method = this$1.startNode();
    var isGenerator = this$1.eat(types.star);
    var isAsync = false;
    var isMaybeStatic = this$1.type === types.name && this$1.value === "static";
    this$1.parsePropertyName(method);
    method.static = isMaybeStatic && this$1.type !== types.parenL;
    if (method.static) {
      if (isGenerator) { this$1.unexpected(); }
      isGenerator = this$1.eat(types.star);
      this$1.parsePropertyName(method);
    }
    if (this$1.options.ecmaVersion >= 8 && !isGenerator && !method.computed &&
        method.key.type === "Identifier" && method.key.name === "async" && this$1.type !== types.parenL &&
        !this$1.canInsertSemicolon()) {
      isAsync = true;
      this$1.parsePropertyName(method);
    }
    method.kind = "method";
    var isGetSet = false;
    if (!method.computed) {
      var key = method.key;
      if (!isGenerator && !isAsync && key.type === "Identifier" && this$1.type !== types.parenL && (key.name === "get" || key.name === "set")) {
        isGetSet = true;
        method.kind = key.name;
        key = this$1.parsePropertyName(method);
      }
      if (!method.static && (key.type === "Identifier" && key.name === "constructor" ||
          key.type === "Literal" && key.value === "constructor")) {
        if (hadConstructor) { this$1.raise(key.start, "Duplicate constructor in the same class"); }
        if (isGetSet) { this$1.raise(key.start, "Constructor can't have get/set modifier"); }
        if (isGenerator) { this$1.raise(key.start, "Constructor can't be a generator"); }
        if (isAsync) { this$1.raise(key.start, "Constructor can't be an async method"); }
        method.kind = "constructor";
        hadConstructor = true;
      }
    }
    this$1.parseClassMethod(classBody, method, isGenerator, isAsync);
    if (isGetSet) {
      var paramCount = method.kind === "get" ? 0 : 1;
      if (method.value.params.length !== paramCount) {
        var start = method.value.start;
        if (method.kind === "get")
          { this$1.raiseRecoverable(start, "getter should have no params"); }
        else
          { this$1.raiseRecoverable(start, "setter should have exactly one param"); }
      } else {
        if (method.kind === "set" && method.value.params[0].type === "RestElement")
          { this$1.raiseRecoverable(method.value.params[0].start, "Setter cannot use rest params"); }
      }
    }
  }
  node.body = this.finishNode(classBody, "ClassBody");
  return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression")
};

pp$1.parseClassMethod = function(classBody, method, isGenerator, isAsync) {
  method.value = this.parseMethod(isGenerator, isAsync);
  classBody.body.push(this.finishNode(method, "MethodDefinition"));
};

pp$1.parseClassId = function(node, isStatement) {
  node.id = this.type === types.name ? this.parseIdent() : isStatement === true ? this.unexpected() : null;
};

pp$1.parseClassSuper = function(node) {
  node.superClass = this.eat(types._extends) ? this.parseExprSubscripts() : null;
};

// Parses module export declaration.

pp$1.parseExport = function(node, exports) {
  var this$1 = this;

  this.next();
  // export * from '...'
  if (this.eat(types.star)) {
    this.expectContextual("from");
    node.source = this.type === types.string ? this.parseExprAtom() : this.unexpected();
    this.semicolon();
    return this.finishNode(node, "ExportAllDeclaration")
  }
  if (this.eat(types._default)) { // export default ...
    this.checkExport(exports, "default", this.lastTokStart);
    var isAsync;
    if (this.type === types._function || (isAsync = this.isAsyncFunction())) {
      var fNode = this.startNode();
      this.next();
      if (isAsync) { this.next(); }
      node.declaration = this.parseFunction(fNode, "nullableID", false, isAsync);
    } else if (this.type === types._class) {
      var cNode = this.startNode();
      node.declaration = this.parseClass(cNode, "nullableID");
    } else {
      node.declaration = this.parseMaybeAssign();
      this.semicolon();
    }
    return this.finishNode(node, "ExportDefaultDeclaration")
  }
  // export var|const|let|function|class ...
  if (this.shouldParseExportStatement()) {
    node.declaration = this.parseStatement(true);
    if (node.declaration.type === "VariableDeclaration")
      { this.checkVariableExport(exports, node.declaration.declarations); }
    else
      { this.checkExport(exports, node.declaration.id.name, node.declaration.id.start); }
    node.specifiers = [];
    node.source = null;
  } else { // export { x, y as z } [from '...']
    node.declaration = null;
    node.specifiers = this.parseExportSpecifiers(exports);
    if (this.eatContextual("from")) {
      node.source = this.type === types.string ? this.parseExprAtom() : this.unexpected();
    } else {
      // check for keywords used as local names
      for (var i = 0, list = node.specifiers; i < list.length; i += 1) {
        var spec = list[i];

        this$1.checkUnreserved(spec.local);
      }

      node.source = null;
    }
    this.semicolon();
  }
  return this.finishNode(node, "ExportNamedDeclaration")
};

pp$1.checkExport = function(exports, name, pos) {
  if (!exports) { return }
  if (has(exports, name))
    { this.raiseRecoverable(pos, "Duplicate export '" + name + "'"); }
  exports[name] = true;
};

pp$1.checkPatternExport = function(exports, pat) {
  var this$1 = this;

  var type = pat.type;
  if (type == "Identifier")
    { this.checkExport(exports, pat.name, pat.start); }
  else if (type == "ObjectPattern")
    { for (var i = 0, list = pat.properties; i < list.length; i += 1)
      {
        var prop = list[i];

        this$1.checkPatternExport(exports, prop.value);
      } }
  else if (type == "ArrayPattern")
    { for (var i$1 = 0, list$1 = pat.elements; i$1 < list$1.length; i$1 += 1) {
      var elt = list$1[i$1];

        if (elt) { this$1.checkPatternExport(exports, elt); }
    } }
  else if (type == "AssignmentPattern")
    { this.checkPatternExport(exports, pat.left); }
  else if (type == "ParenthesizedExpression")
    { this.checkPatternExport(exports, pat.expression); }
};

pp$1.checkVariableExport = function(exports, decls) {
  var this$1 = this;

  if (!exports) { return }
  for (var i = 0, list = decls; i < list.length; i += 1)
    {
    var decl = list[i];

    this$1.checkPatternExport(exports, decl.id);
  }
};

pp$1.shouldParseExportStatement = function() {
  return this.type.keyword === "var" ||
    this.type.keyword === "const" ||
    this.type.keyword === "class" ||
    this.type.keyword === "function" ||
    this.isLet() ||
    this.isAsyncFunction()
};

// Parses a comma-separated list of module exports.

pp$1.parseExportSpecifiers = function(exports) {
  var this$1 = this;

  var nodes = [], first = true;
  // export { x, y as z } [from '...']
  this.expect(types.braceL);
  while (!this.eat(types.braceR)) {
    if (!first) {
      this$1.expect(types.comma);
      if (this$1.afterTrailingComma(types.braceR)) { break }
    } else { first = false; }

    var node = this$1.startNode();
    node.local = this$1.parseIdent(true);
    node.exported = this$1.eatContextual("as") ? this$1.parseIdent(true) : node.local;
    this$1.checkExport(exports, node.exported.name, node.exported.start);
    nodes.push(this$1.finishNode(node, "ExportSpecifier"));
  }
  return nodes
};

// Parses import declaration.

pp$1.parseImport = function(node) {
  this.next();
  // import '...'
  if (this.type === types.string) {
    node.specifiers = empty;
    node.source = this.parseExprAtom();
  } else {
    node.specifiers = this.parseImportSpecifiers();
    this.expectContextual("from");
    node.source = this.type === types.string ? this.parseExprAtom() : this.unexpected();
  }
  this.semicolon();
  return this.finishNode(node, "ImportDeclaration")
};

// Parses a comma-separated list of module imports.

pp$1.parseImportSpecifiers = function() {
  var this$1 = this;

  var nodes = [], first = true;
  if (this.type === types.name) {
    // import defaultObj, { x, y as z } from '...'
    var node = this.startNode();
    node.local = this.parseIdent();
    this.checkLVal(node.local, "let");
    nodes.push(this.finishNode(node, "ImportDefaultSpecifier"));
    if (!this.eat(types.comma)) { return nodes }
  }
  if (this.type === types.star) {
    var node$1 = this.startNode();
    this.next();
    this.expectContextual("as");
    node$1.local = this.parseIdent();
    this.checkLVal(node$1.local, "let");
    nodes.push(this.finishNode(node$1, "ImportNamespaceSpecifier"));
    return nodes
  }
  this.expect(types.braceL);
  while (!this.eat(types.braceR)) {
    if (!first) {
      this$1.expect(types.comma);
      if (this$1.afterTrailingComma(types.braceR)) { break }
    } else { first = false; }

    var node$2 = this$1.startNode();
    node$2.imported = this$1.parseIdent(true);
    if (this$1.eatContextual("as")) {
      node$2.local = this$1.parseIdent();
    } else {
      this$1.checkUnreserved(node$2.imported);
      node$2.local = node$2.imported;
    }
    this$1.checkLVal(node$2.local, "let");
    nodes.push(this$1.finishNode(node$2, "ImportSpecifier"));
  }
  return nodes
};

// Set `ExpressionStatement#directive` property for directive prologues.
pp$1.adaptDirectivePrologue = function(statements) {
  for (var i = 0; i < statements.length && this.isDirectiveCandidate(statements[i]); ++i) {
    statements[i].directive = statements[i].expression.raw.slice(1, -1);
  }
};
pp$1.isDirectiveCandidate = function(statement) {
  return (
    statement.type === "ExpressionStatement" &&
    statement.expression.type === "Literal" &&
    typeof statement.expression.value === "string" &&
    // Reject parenthesized strings.
    (this.input[statement.start] === "\"" || this.input[statement.start] === "'")
  )
};

var pp$2 = Parser.prototype;

// Convert existing expression atom to assignable pattern
// if possible.

pp$2.toAssignable = function(node, isBinding) {
  var this$1 = this;

  if (this.options.ecmaVersion >= 6 && node) {
    switch (node.type) {
    case "Identifier":
      if (this.inAsync && node.name === "await")
        { this.raise(node.start, "Can not use 'await' as identifier inside an async function"); }
      break

    case "ObjectPattern":
    case "ArrayPattern":
      break

    case "ObjectExpression":
      node.type = "ObjectPattern";
      for (var i = 0, list = node.properties; i < list.length; i += 1) {
        var prop = list[i];

      if (prop.kind !== "init") { this$1.raise(prop.key.start, "Object pattern can't contain getter or setter"); }
        this$1.toAssignable(prop.value, isBinding);
      }
      break

    case "ArrayExpression":
      node.type = "ArrayPattern";
      this.toAssignableList(node.elements, isBinding);
      break

    case "AssignmentExpression":
      if (node.operator === "=") {
        node.type = "AssignmentPattern";
        delete node.operator;
        this.toAssignable(node.left, isBinding);
        // falls through to AssignmentPattern
      } else {
        this.raise(node.left.end, "Only '=' operator can be used for specifying default value.");
        break
      }

    case "AssignmentPattern":
      break

    case "ParenthesizedExpression":
      this.toAssignable(node.expression, isBinding);
      break

    case "MemberExpression":
      if (!isBinding) { break }

    default:
      this.raise(node.start, "Assigning to rvalue");
    }
  }
  return node
};

// Convert list of expression atoms to binding list.

pp$2.toAssignableList = function(exprList, isBinding) {
  var this$1 = this;

  var end = exprList.length;
  if (end) {
    var last = exprList[end - 1];
    if (last && last.type == "RestElement") {
      --end;
    } else if (last && last.type == "SpreadElement") {
      last.type = "RestElement";
      var arg = last.argument;
      this.toAssignable(arg, isBinding);
      --end;
    }

    if (this.options.ecmaVersion === 6 && isBinding && last && last.type === "RestElement" && last.argument.type !== "Identifier")
      { this.unexpected(last.argument.start); }
  }
  for (var i = 0; i < end; i++) {
    var elt = exprList[i];
    if (elt) { this$1.toAssignable(elt, isBinding); }
  }
  return exprList
};

// Parses spread element.

pp$2.parseSpread = function(refDestructuringErrors) {
  var node = this.startNode();
  this.next();
  node.argument = this.parseMaybeAssign(false, refDestructuringErrors);
  return this.finishNode(node, "SpreadElement")
};

pp$2.parseRestBinding = function() {
  var node = this.startNode();
  this.next();

  // RestElement inside of a function parameter must be an identifier
  if (this.options.ecmaVersion === 6 && this.type !== types.name)
    { this.unexpected(); }

  node.argument = this.parseBindingAtom();

  return this.finishNode(node, "RestElement")
};

// Parses lvalue (assignable) atom.

pp$2.parseBindingAtom = function() {
  if (this.options.ecmaVersion >= 6) {
    switch (this.type) {
    case types.bracketL:
      var node = this.startNode();
      this.next();
      node.elements = this.parseBindingList(types.bracketR, true, true);
      return this.finishNode(node, "ArrayPattern")

    case types.braceL:
      return this.parseObj(true)
    }
  }
  return this.parseIdent()
};

pp$2.parseBindingList = function(close, allowEmpty, allowTrailingComma) {
  var this$1 = this;

  var elts = [], first = true;
  while (!this.eat(close)) {
    if (first) { first = false; }
    else { this$1.expect(types.comma); }
    if (allowEmpty && this$1.type === types.comma) {
      elts.push(null);
    } else if (allowTrailingComma && this$1.afterTrailingComma(close)) {
      break
    } else if (this$1.type === types.ellipsis) {
      var rest = this$1.parseRestBinding();
      this$1.parseBindingListItem(rest);
      elts.push(rest);
      if (this$1.type === types.comma) { this$1.raise(this$1.start, "Comma is not permitted after the rest element"); }
      this$1.expect(close);
      break
    } else {
      var elem = this$1.parseMaybeDefault(this$1.start, this$1.startLoc);
      this$1.parseBindingListItem(elem);
      elts.push(elem);
    }
  }
  return elts
};

pp$2.parseBindingListItem = function(param) {
  return param
};

// Parses assignment pattern around given atom if possible.

pp$2.parseMaybeDefault = function(startPos, startLoc, left) {
  left = left || this.parseBindingAtom();
  if (this.options.ecmaVersion < 6 || !this.eat(types.eq)) { return left }
  var node = this.startNodeAt(startPos, startLoc);
  node.left = left;
  node.right = this.parseMaybeAssign();
  return this.finishNode(node, "AssignmentPattern")
};

// Verify that a node is an lval — something that can be assigned
// to.
// bindingType can be either:
// 'var' indicating that the lval creates a 'var' binding
// 'let' indicating that the lval creates a lexical ('let' or 'const') binding
// 'none' indicating that the binding should be checked for illegal identifiers, but not for duplicate references

pp$2.checkLVal = function(expr, bindingType, checkClashes) {
  var this$1 = this;

  switch (expr.type) {
  case "Identifier":
    if (this.strict && this.reservedWordsStrictBind.test(expr.name))
      { this.raiseRecoverable(expr.start, (bindingType ? "Binding " : "Assigning to ") + expr.name + " in strict mode"); }
    if (checkClashes) {
      if (has(checkClashes, expr.name))
        { this.raiseRecoverable(expr.start, "Argument name clash"); }
      checkClashes[expr.name] = true;
    }
    if (bindingType && bindingType !== "none") {
      if (
        bindingType === "var" && !this.canDeclareVarName(expr.name) ||
        bindingType !== "var" && !this.canDeclareLexicalName(expr.name)
      ) {
        this.raiseRecoverable(expr.start, ("Identifier '" + (expr.name) + "' has already been declared"));
      }
      if (bindingType === "var") {
        this.declareVarName(expr.name);
      } else {
        this.declareLexicalName(expr.name);
      }
    }
    break

  case "MemberExpression":
    if (bindingType) { this.raiseRecoverable(expr.start, (bindingType ? "Binding" : "Assigning to") + " member expression"); }
    break

  case "ObjectPattern":
    for (var i = 0, list = expr.properties; i < list.length; i += 1)
      {
    var prop = list[i];

    this$1.checkLVal(prop.value, bindingType, checkClashes);
  }
    break

  case "ArrayPattern":
    for (var i$1 = 0, list$1 = expr.elements; i$1 < list$1.length; i$1 += 1) {
      var elem = list$1[i$1];

    if (elem) { this$1.checkLVal(elem, bindingType, checkClashes); }
    }
    break

  case "AssignmentPattern":
    this.checkLVal(expr.left, bindingType, checkClashes);
    break

  case "RestElement":
    this.checkLVal(expr.argument, bindingType, checkClashes);
    break

  case "ParenthesizedExpression":
    this.checkLVal(expr.expression, bindingType, checkClashes);
    break

  default:
    this.raise(expr.start, (bindingType ? "Binding" : "Assigning to") + " rvalue");
  }
};

// A recursive descent parser operates by defining functions for all
// syntactic elements, and recursively calling those, each function
// advancing the input stream and returning an AST node. Precedence
// of constructs (for example, the fact that `!x[1]` means `!(x[1])`
// instead of `(!x)[1]` is handled by the fact that the parser
// function that parses unary prefix operators is called first, and
// in turn calls the function that parses `[]` subscripts — that
// way, it'll receive the node for `x[1]` already parsed, and wraps
// *that* in the unary operator node.
//
// Acorn uses an [operator precedence parser][opp] to handle binary
// operator precedence, because it is much more compact than using
// the technique outlined above, which uses different, nesting
// functions to specify precedence, for all of the ten binary
// precedence levels that JavaScript defines.
//
// [opp]: http://en.wikipedia.org/wiki/Operator-precedence_parser

var pp$3 = Parser.prototype;

// Check if property name clashes with already added.
// Object/class getters and setters are not allowed to clash —
// either with each other or with an init property — and in
// strict mode, init properties are also not allowed to be repeated.

pp$3.checkPropClash = function(prop, propHash) {
  if (this.options.ecmaVersion >= 6 && (prop.computed || prop.method || prop.shorthand))
    { return }
  var key = prop.key;
  var name;
  switch (key.type) {
  case "Identifier": name = key.name; break
  case "Literal": name = String(key.value); break
  default: return
  }
  var kind = prop.kind;
  if (this.options.ecmaVersion >= 6) {
    if (name === "__proto__" && kind === "init") {
      if (propHash.proto) { this.raiseRecoverable(key.start, "Redefinition of __proto__ property"); }
      propHash.proto = true;
    }
    return
  }
  name = "$" + name;
  var other = propHash[name];
  if (other) {
    var redefinition;
    if (kind === "init") {
      redefinition = this.strict && other.init || other.get || other.set;
    } else {
      redefinition = other.init || other[kind];
    }
    if (redefinition)
      { this.raiseRecoverable(key.start, "Redefinition of property"); }
  } else {
    other = propHash[name] = {
      init: false,
      get: false,
      set: false
    };
  }
  other[kind] = true;
};

// ### Expression parsing

// These nest, from the most general expression type at the top to
// 'atomic', nondivisible expression types at the bottom. Most of
// the functions will simply let the function(s) below them parse,
// and, *if* the syntactic construct they handle is present, wrap
// the AST node that the inner parser gave them in another node.

// Parse a full expression. The optional arguments are used to
// forbid the `in` operator (in for loops initalization expressions)
// and provide reference for storing '=' operator inside shorthand
// property assignment in contexts where both object expression
// and object pattern might appear (so it's possible to raise
// delayed syntax error at correct position).

pp$3.parseExpression = function(noIn, refDestructuringErrors) {
  var this$1 = this;

  var startPos = this.start, startLoc = this.startLoc;
  var expr = this.parseMaybeAssign(noIn, refDestructuringErrors);
  if (this.type === types.comma) {
    var node = this.startNodeAt(startPos, startLoc);
    node.expressions = [expr];
    while (this.eat(types.comma)) { node.expressions.push(this$1.parseMaybeAssign(noIn, refDestructuringErrors)); }
    return this.finishNode(node, "SequenceExpression")
  }
  return expr
};

// Parse an assignment expression. This includes applications of
// operators like `+=`.

pp$3.parseMaybeAssign = function(noIn, refDestructuringErrors, afterLeftParse) {
  if (this.inGenerator && this.isContextual("yield")) { return this.parseYield() }

  var ownDestructuringErrors = false, oldParenAssign = -1, oldTrailingComma = -1;
  if (refDestructuringErrors) {
    oldParenAssign = refDestructuringErrors.parenthesizedAssign;
    oldTrailingComma = refDestructuringErrors.trailingComma;
    refDestructuringErrors.parenthesizedAssign = refDestructuringErrors.trailingComma = -1;
  } else {
    refDestructuringErrors = new DestructuringErrors;
    ownDestructuringErrors = true;
  }

  var startPos = this.start, startLoc = this.startLoc;
  if (this.type == types.parenL || this.type == types.name)
    { this.potentialArrowAt = this.start; }
  var left = this.parseMaybeConditional(noIn, refDestructuringErrors);
  if (afterLeftParse) { left = afterLeftParse.call(this, left, startPos, startLoc); }
  if (this.type.isAssign) {
    this.checkPatternErrors(refDestructuringErrors, true);
    if (!ownDestructuringErrors) { DestructuringErrors.call(refDestructuringErrors); }
    var node = this.startNodeAt(startPos, startLoc);
    node.operator = this.value;
    node.left = this.type === types.eq ? this.toAssignable(left) : left;
    refDestructuringErrors.shorthandAssign = -1; // reset because shorthand default was used correctly
    this.checkLVal(left);
    this.next();
    node.right = this.parseMaybeAssign(noIn);
    return this.finishNode(node, "AssignmentExpression")
  } else {
    if (ownDestructuringErrors) { this.checkExpressionErrors(refDestructuringErrors, true); }
  }
  if (oldParenAssign > -1) { refDestructuringErrors.parenthesizedAssign = oldParenAssign; }
  if (oldTrailingComma > -1) { refDestructuringErrors.trailingComma = oldTrailingComma; }
  return left
};

// Parse a ternary conditional (`?:`) operator.

pp$3.parseMaybeConditional = function(noIn, refDestructuringErrors) {
  var startPos = this.start, startLoc = this.startLoc;
  var expr = this.parseExprOps(noIn, refDestructuringErrors);
  if (this.checkExpressionErrors(refDestructuringErrors)) { return expr }
  if (this.eat(types.question)) {
    var node = this.startNodeAt(startPos, startLoc);
    node.test = expr;
    node.consequent = this.parseMaybeAssign();
    this.expect(types.colon);
    node.alternate = this.parseMaybeAssign(noIn);
    return this.finishNode(node, "ConditionalExpression")
  }
  return expr
};

// Start the precedence parser.

pp$3.parseExprOps = function(noIn, refDestructuringErrors) {
  var startPos = this.start, startLoc = this.startLoc;
  var expr = this.parseMaybeUnary(refDestructuringErrors, false);
  if (this.checkExpressionErrors(refDestructuringErrors)) { return expr }
  return expr.start == startPos && expr.type === "ArrowFunctionExpression" ? expr : this.parseExprOp(expr, startPos, startLoc, -1, noIn)
};

// Parse binary operators with the operator precedence parsing
// algorithm. `left` is the left-hand side of the operator.
// `minPrec` provides context that allows the function to stop and
// defer further parser to one of its callers when it encounters an
// operator that has a lower precedence than the set it is parsing.

pp$3.parseExprOp = function(left, leftStartPos, leftStartLoc, minPrec, noIn) {
  var prec = this.type.binop;
  if (prec != null && (!noIn || this.type !== types._in)) {
    if (prec > minPrec) {
      var logical = this.type === types.logicalOR || this.type === types.logicalAND;
      var op = this.value;
      this.next();
      var startPos = this.start, startLoc = this.startLoc;
      var right = this.parseExprOp(this.parseMaybeUnary(null, false), startPos, startLoc, prec, noIn);
      var node = this.buildBinary(leftStartPos, leftStartLoc, left, right, op, logical);
      return this.parseExprOp(node, leftStartPos, leftStartLoc, minPrec, noIn)
    }
  }
  return left
};

pp$3.buildBinary = function(startPos, startLoc, left, right, op, logical) {
  var node = this.startNodeAt(startPos, startLoc);
  node.left = left;
  node.operator = op;
  node.right = right;
  return this.finishNode(node, logical ? "LogicalExpression" : "BinaryExpression")
};

// Parse unary operators, both prefix and postfix.

pp$3.parseMaybeUnary = function(refDestructuringErrors, sawUnary) {
  var this$1 = this;

  var startPos = this.start, startLoc = this.startLoc, expr;
  if (this.inAsync && this.isContextual("await")) {
    expr = this.parseAwait();
    sawUnary = true;
  } else if (this.type.prefix) {
    var node = this.startNode(), update = this.type === types.incDec;
    node.operator = this.value;
    node.prefix = true;
    this.next();
    node.argument = this.parseMaybeUnary(null, true);
    this.checkExpressionErrors(refDestructuringErrors, true);
    if (update) { this.checkLVal(node.argument); }
    else if (this.strict && node.operator === "delete" &&
             node.argument.type === "Identifier")
      { this.raiseRecoverable(node.start, "Deleting local variable in strict mode"); }
    else { sawUnary = true; }
    expr = this.finishNode(node, update ? "UpdateExpression" : "UnaryExpression");
  } else {
    expr = this.parseExprSubscripts(refDestructuringErrors);
    if (this.checkExpressionErrors(refDestructuringErrors)) { return expr }
    while (this.type.postfix && !this.canInsertSemicolon()) {
      var node$1 = this$1.startNodeAt(startPos, startLoc);
      node$1.operator = this$1.value;
      node$1.prefix = false;
      node$1.argument = expr;
      this$1.checkLVal(expr);
      this$1.next();
      expr = this$1.finishNode(node$1, "UpdateExpression");
    }
  }

  if (!sawUnary && this.eat(types.starstar))
    { return this.buildBinary(startPos, startLoc, expr, this.parseMaybeUnary(null, false), "**", false) }
  else
    { return expr }
};

// Parse call, dot, and `[]`-subscript expressions.

pp$3.parseExprSubscripts = function(refDestructuringErrors) {
  var startPos = this.start, startLoc = this.startLoc;
  var expr = this.parseExprAtom(refDestructuringErrors);
  var skipArrowSubscripts = expr.type === "ArrowFunctionExpression" && this.input.slice(this.lastTokStart, this.lastTokEnd) !== ")";
  if (this.checkExpressionErrors(refDestructuringErrors) || skipArrowSubscripts) { return expr }
  var result = this.parseSubscripts(expr, startPos, startLoc);
  if (refDestructuringErrors && result.type === "MemberExpression") {
    if (refDestructuringErrors.parenthesizedAssign >= result.start) { refDestructuringErrors.parenthesizedAssign = -1; }
    if (refDestructuringErrors.parenthesizedBind >= result.start) { refDestructuringErrors.parenthesizedBind = -1; }
  }
  return result
};

pp$3.parseSubscripts = function(base, startPos, startLoc, noCalls) {
  var this$1 = this;

  var maybeAsyncArrow = this.options.ecmaVersion >= 8 && base.type === "Identifier" && base.name === "async" &&
      this.lastTokEnd == base.end && !this.canInsertSemicolon();
  for (var computed = (void 0);;) {
    if ((computed = this$1.eat(types.bracketL)) || this$1.eat(types.dot)) {
      var node = this$1.startNodeAt(startPos, startLoc);
      node.object = base;
      node.property = computed ? this$1.parseExpression() : this$1.parseIdent(true);
      node.computed = !!computed;
      if (computed) { this$1.expect(types.bracketR); }
      base = this$1.finishNode(node, "MemberExpression");
    } else if (!noCalls && this$1.eat(types.parenL)) {
      var refDestructuringErrors = new DestructuringErrors, oldYieldPos = this$1.yieldPos, oldAwaitPos = this$1.awaitPos;
      this$1.yieldPos = 0;
      this$1.awaitPos = 0;
      var exprList = this$1.parseExprList(types.parenR, this$1.options.ecmaVersion >= 8, false, refDestructuringErrors);
      if (maybeAsyncArrow && !this$1.canInsertSemicolon() && this$1.eat(types.arrow)) {
        this$1.checkPatternErrors(refDestructuringErrors, false);
        this$1.checkYieldAwaitInDefaultParams();
        this$1.yieldPos = oldYieldPos;
        this$1.awaitPos = oldAwaitPos;
        return this$1.parseArrowExpression(this$1.startNodeAt(startPos, startLoc), exprList, true)
      }
      this$1.checkExpressionErrors(refDestructuringErrors, true);
      this$1.yieldPos = oldYieldPos || this$1.yieldPos;
      this$1.awaitPos = oldAwaitPos || this$1.awaitPos;
      var node$1 = this$1.startNodeAt(startPos, startLoc);
      node$1.callee = base;
      node$1.arguments = exprList;
      base = this$1.finishNode(node$1, "CallExpression");
    } else if (this$1.type === types.backQuote) {
      var node$2 = this$1.startNodeAt(startPos, startLoc);
      node$2.tag = base;
      node$2.quasi = this$1.parseTemplate({isTagged: true});
      base = this$1.finishNode(node$2, "TaggedTemplateExpression");
    } else {
      return base
    }
  }
};

// Parse an atomic expression — either a single token that is an
// expression, an expression started by a keyword like `function` or
// `new`, or an expression wrapped in punctuation like `()`, `[]`,
// or `{}`.

pp$3.parseExprAtom = function(refDestructuringErrors) {
  var node, canBeArrow = this.potentialArrowAt == this.start;
  switch (this.type) {
  case types._super:
    if (!this.inFunction)
      { this.raise(this.start, "'super' outside of function or class"); }
    node = this.startNode();
    this.next();
    // The `super` keyword can appear at below:
    // SuperProperty:
    //     super [ Expression ]
    //     super . IdentifierName
    // SuperCall:
    //     super Arguments
    if (this.type !== types.dot && this.type !== types.bracketL && this.type !== types.parenL)
      { this.unexpected(); }
    return this.finishNode(node, "Super")

  case types._this:
    node = this.startNode();
    this.next();
    return this.finishNode(node, "ThisExpression")

  case types.name:
    var startPos = this.start, startLoc = this.startLoc;
    var id = this.parseIdent(this.type !== types.name);
    if (this.options.ecmaVersion >= 8 && id.name === "async" && !this.canInsertSemicolon() && this.eat(types._function))
      { return this.parseFunction(this.startNodeAt(startPos, startLoc), false, false, true) }
    if (canBeArrow && !this.canInsertSemicolon()) {
      if (this.eat(types.arrow))
        { return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), [id], false) }
      if (this.options.ecmaVersion >= 8 && id.name === "async" && this.type === types.name) {
        id = this.parseIdent();
        if (this.canInsertSemicolon() || !this.eat(types.arrow))
          { this.unexpected(); }
        return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), [id], true)
      }
    }
    return id

  case types.regexp:
    var value = this.value;
    node = this.parseLiteral(value.value);
    node.regex = {pattern: value.pattern, flags: value.flags};
    return node

  case types.num: case types.string:
    return this.parseLiteral(this.value)

  case types._null: case types._true: case types._false:
    node = this.startNode();
    node.value = this.type === types._null ? null : this.type === types._true;
    node.raw = this.type.keyword;
    this.next();
    return this.finishNode(node, "Literal")

  case types.parenL:
    var start = this.start, expr = this.parseParenAndDistinguishExpression(canBeArrow);
    if (refDestructuringErrors) {
      if (refDestructuringErrors.parenthesizedAssign < 0 && !this.isSimpleAssignTarget(expr))
        { refDestructuringErrors.parenthesizedAssign = start; }
      if (refDestructuringErrors.parenthesizedBind < 0)
        { refDestructuringErrors.parenthesizedBind = start; }
    }
    return expr

  case types.bracketL:
    node = this.startNode();
    this.next();
    node.elements = this.parseExprList(types.bracketR, true, true, refDestructuringErrors);
    return this.finishNode(node, "ArrayExpression")

  case types.braceL:
    return this.parseObj(false, refDestructuringErrors)

  case types._function:
    node = this.startNode();
    this.next();
    return this.parseFunction(node, false)

  case types._class:
    return this.parseClass(this.startNode(), false)

  case types._new:
    return this.parseNew()

  case types.backQuote:
    return this.parseTemplate()

  default:
    this.unexpected();
  }
};

pp$3.parseLiteral = function(value) {
  var node = this.startNode();
  node.value = value;
  node.raw = this.input.slice(this.start, this.end);
  this.next();
  return this.finishNode(node, "Literal")
};

pp$3.parseParenExpression = function() {
  this.expect(types.parenL);
  var val = this.parseExpression();
  this.expect(types.parenR);
  return val
};

pp$3.parseParenAndDistinguishExpression = function(canBeArrow) {
  var this$1 = this;

  var startPos = this.start, startLoc = this.startLoc, val, allowTrailingComma = this.options.ecmaVersion >= 8;
  if (this.options.ecmaVersion >= 6) {
    this.next();

    var innerStartPos = this.start, innerStartLoc = this.startLoc;
    var exprList = [], first = true, lastIsComma = false;
    var refDestructuringErrors = new DestructuringErrors, oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, spreadStart, innerParenStart;
    this.yieldPos = 0;
    this.awaitPos = 0;
    while (this.type !== types.parenR) {
      first ? first = false : this$1.expect(types.comma);
      if (allowTrailingComma && this$1.afterTrailingComma(types.parenR, true)) {
        lastIsComma = true;
        break
      } else if (this$1.type === types.ellipsis) {
        spreadStart = this$1.start;
        exprList.push(this$1.parseParenItem(this$1.parseRestBinding()));
        if (this$1.type === types.comma) { this$1.raise(this$1.start, "Comma is not permitted after the rest element"); }
        break
      } else {
        if (this$1.type === types.parenL && !innerParenStart) {
          innerParenStart = this$1.start;
        }
        exprList.push(this$1.parseMaybeAssign(false, refDestructuringErrors, this$1.parseParenItem));
      }
    }
    var innerEndPos = this.start, innerEndLoc = this.startLoc;
    this.expect(types.parenR);

    if (canBeArrow && !this.canInsertSemicolon() && this.eat(types.arrow)) {
      this.checkPatternErrors(refDestructuringErrors, false);
      this.checkYieldAwaitInDefaultParams();
      if (innerParenStart) { this.unexpected(innerParenStart); }
      this.yieldPos = oldYieldPos;
      this.awaitPos = oldAwaitPos;
      return this.parseParenArrowList(startPos, startLoc, exprList)
    }

    if (!exprList.length || lastIsComma) { this.unexpected(this.lastTokStart); }
    if (spreadStart) { this.unexpected(spreadStart); }
    this.checkExpressionErrors(refDestructuringErrors, true);
    this.yieldPos = oldYieldPos || this.yieldPos;
    this.awaitPos = oldAwaitPos || this.awaitPos;

    if (exprList.length > 1) {
      val = this.startNodeAt(innerStartPos, innerStartLoc);
      val.expressions = exprList;
      this.finishNodeAt(val, "SequenceExpression", innerEndPos, innerEndLoc);
    } else {
      val = exprList[0];
    }
  } else {
    val = this.parseParenExpression();
  }

  if (this.options.preserveParens) {
    var par = this.startNodeAt(startPos, startLoc);
    par.expression = val;
    return this.finishNode(par, "ParenthesizedExpression")
  } else {
    return val
  }
};

pp$3.parseParenItem = function(item) {
  return item
};

pp$3.parseParenArrowList = function(startPos, startLoc, exprList) {
  return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), exprList)
};

// New's precedence is slightly tricky. It must allow its argument to
// be a `[]` or dot subscript expression, but not a call — at least,
// not without wrapping it in parentheses. Thus, it uses the noCalls
// argument to parseSubscripts to prevent it from consuming the
// argument list.

var empty$1 = [];

pp$3.parseNew = function() {
  var node = this.startNode();
  var meta = this.parseIdent(true);
  if (this.options.ecmaVersion >= 6 && this.eat(types.dot)) {
    node.meta = meta;
    node.property = this.parseIdent(true);
    if (node.property.name !== "target")
      { this.raiseRecoverable(node.property.start, "The only valid meta property for new is new.target"); }
    if (!this.inFunction)
      { this.raiseRecoverable(node.start, "new.target can only be used in functions"); }
    return this.finishNode(node, "MetaProperty")
  }
  var startPos = this.start, startLoc = this.startLoc;
  node.callee = this.parseSubscripts(this.parseExprAtom(), startPos, startLoc, true);
  if (this.eat(types.parenL)) { node.arguments = this.parseExprList(types.parenR, this.options.ecmaVersion >= 8, false); }
  else { node.arguments = empty$1; }
  return this.finishNode(node, "NewExpression")
};

// Parse template expression.

pp$3.parseTemplateElement = function(ref) {
  var isTagged = ref.isTagged;

  var elem = this.startNode();
  if (this.type === types.invalidTemplate) {
    if (!isTagged) {
      this.raiseRecoverable(this.start, "Bad escape sequence in untagged template literal");
    }
    elem.value = {
      raw: this.value,
      cooked: null
    };
  } else {
    elem.value = {
      raw: this.input.slice(this.start, this.end).replace(/\r\n?/g, "\n"),
      cooked: this.value
    };
  }
  this.next();
  elem.tail = this.type === types.backQuote;
  return this.finishNode(elem, "TemplateElement")
};

pp$3.parseTemplate = function(ref) {
  var this$1 = this;
  if ( ref === void 0 ) ref = {};
  var isTagged = ref.isTagged; if ( isTagged === void 0 ) isTagged = false;

  var node = this.startNode();
  this.next();
  node.expressions = [];
  var curElt = this.parseTemplateElement({isTagged: isTagged});
  node.quasis = [curElt];
  while (!curElt.tail) {
    this$1.expect(types.dollarBraceL);
    node.expressions.push(this$1.parseExpression());
    this$1.expect(types.braceR);
    node.quasis.push(curElt = this$1.parseTemplateElement({isTagged: isTagged}));
  }
  this.next();
  return this.finishNode(node, "TemplateLiteral")
};

pp$3.isAsyncProp = function(prop) {
  return !prop.computed && prop.key.type === "Identifier" && prop.key.name === "async" &&
    (this.type === types.name || this.type === types.num || this.type === types.string || this.type === types.bracketL || this.type.keyword) &&
    !lineBreak.test(this.input.slice(this.lastTokEnd, this.start))
};

// Parse an object literal or binding pattern.

pp$3.parseObj = function(isPattern, refDestructuringErrors) {
  var this$1 = this;

  var node = this.startNode(), first = true, propHash = {};
  node.properties = [];
  this.next();
  while (!this.eat(types.braceR)) {
    if (!first) {
      this$1.expect(types.comma);
      if (this$1.afterTrailingComma(types.braceR)) { break }
    } else { first = false; }

    var prop = this$1.parseProperty(isPattern, refDestructuringErrors);
    this$1.checkPropClash(prop, propHash);
    node.properties.push(prop);
  }
  return this.finishNode(node, isPattern ? "ObjectPattern" : "ObjectExpression")
};

pp$3.parseProperty = function(isPattern, refDestructuringErrors) {
  var prop = this.startNode(), isGenerator, isAsync, startPos, startLoc;
  if (this.options.ecmaVersion >= 6) {
    prop.method = false;
    prop.shorthand = false;
    if (isPattern || refDestructuringErrors) {
      startPos = this.start;
      startLoc = this.startLoc;
    }
    if (!isPattern)
      { isGenerator = this.eat(types.star); }
  }
  this.parsePropertyName(prop);
  if (!isPattern && this.options.ecmaVersion >= 8 && !isGenerator && this.isAsyncProp(prop)) {
    isAsync = true;
    this.parsePropertyName(prop, refDestructuringErrors);
  } else {
    isAsync = false;
  }
  this.parsePropertyValue(prop, isPattern, isGenerator, isAsync, startPos, startLoc, refDestructuringErrors);
  return this.finishNode(prop, "Property")
};

pp$3.parsePropertyValue = function(prop, isPattern, isGenerator, isAsync, startPos, startLoc, refDestructuringErrors) {
  if ((isGenerator || isAsync) && this.type === types.colon)
    { this.unexpected(); }

  if (this.eat(types.colon)) {
    prop.value = isPattern ? this.parseMaybeDefault(this.start, this.startLoc) : this.parseMaybeAssign(false, refDestructuringErrors);
    prop.kind = "init";
  } else if (this.options.ecmaVersion >= 6 && this.type === types.parenL) {
    if (isPattern) { this.unexpected(); }
    prop.kind = "init";
    prop.method = true;
    prop.value = this.parseMethod(isGenerator, isAsync);
  } else if (!isPattern &&
             this.options.ecmaVersion >= 5 && !prop.computed && prop.key.type === "Identifier" &&
             (prop.key.name === "get" || prop.key.name === "set") &&
             (this.type != types.comma && this.type != types.braceR)) {
    if (isGenerator || isAsync) { this.unexpected(); }
    prop.kind = prop.key.name;
    this.parsePropertyName(prop);
    prop.value = this.parseMethod(false);
    var paramCount = prop.kind === "get" ? 0 : 1;
    if (prop.value.params.length !== paramCount) {
      var start = prop.value.start;
      if (prop.kind === "get")
        { this.raiseRecoverable(start, "getter should have no params"); }
      else
        { this.raiseRecoverable(start, "setter should have exactly one param"); }
    } else {
      if (prop.kind === "set" && prop.value.params[0].type === "RestElement")
        { this.raiseRecoverable(prop.value.params[0].start, "Setter cannot use rest params"); }
    }
  } else if (this.options.ecmaVersion >= 6 && !prop.computed && prop.key.type === "Identifier") {
    this.checkUnreserved(prop.key);
    prop.kind = "init";
    if (isPattern) {
      prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key);
    } else if (this.type === types.eq && refDestructuringErrors) {
      if (refDestructuringErrors.shorthandAssign < 0)
        { refDestructuringErrors.shorthandAssign = this.start; }
      prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key);
    } else {
      prop.value = prop.key;
    }
    prop.shorthand = true;
  } else { this.unexpected(); }
};

pp$3.parsePropertyName = function(prop) {
  if (this.options.ecmaVersion >= 6) {
    if (this.eat(types.bracketL)) {
      prop.computed = true;
      prop.key = this.parseMaybeAssign();
      this.expect(types.bracketR);
      return prop.key
    } else {
      prop.computed = false;
    }
  }
  return prop.key = this.type === types.num || this.type === types.string ? this.parseExprAtom() : this.parseIdent(true)
};

// Initialize empty function node.

pp$3.initFunction = function(node) {
  node.id = null;
  if (this.options.ecmaVersion >= 6) {
    node.generator = false;
    node.expression = false;
  }
  if (this.options.ecmaVersion >= 8)
    { node.async = false; }
};

// Parse object or class method.

pp$3.parseMethod = function(isGenerator, isAsync) {
  var node = this.startNode(), oldInGen = this.inGenerator, oldInAsync = this.inAsync,
      oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, oldInFunc = this.inFunction;

  this.initFunction(node);
  if (this.options.ecmaVersion >= 6)
    { node.generator = isGenerator; }
  if (this.options.ecmaVersion >= 8)
    { node.async = !!isAsync; }

  this.inGenerator = node.generator;
  this.inAsync = node.async;
  this.yieldPos = 0;
  this.awaitPos = 0;
  this.inFunction = true;
  this.enterFunctionScope();

  this.expect(types.parenL);
  node.params = this.parseBindingList(types.parenR, false, this.options.ecmaVersion >= 8);
  this.checkYieldAwaitInDefaultParams();
  this.parseFunctionBody(node, false);

  this.inGenerator = oldInGen;
  this.inAsync = oldInAsync;
  this.yieldPos = oldYieldPos;
  this.awaitPos = oldAwaitPos;
  this.inFunction = oldInFunc;
  return this.finishNode(node, "FunctionExpression")
};

// Parse arrow function expression with given parameters.

pp$3.parseArrowExpression = function(node, params, isAsync) {
  var oldInGen = this.inGenerator, oldInAsync = this.inAsync,
      oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, oldInFunc = this.inFunction;

  this.enterFunctionScope();
  this.initFunction(node);
  if (this.options.ecmaVersion >= 8)
    { node.async = !!isAsync; }

  this.inGenerator = false;
  this.inAsync = node.async;
  this.yieldPos = 0;
  this.awaitPos = 0;
  this.inFunction = true;

  node.params = this.toAssignableList(params, true);
  this.parseFunctionBody(node, true);

  this.inGenerator = oldInGen;
  this.inAsync = oldInAsync;
  this.yieldPos = oldYieldPos;
  this.awaitPos = oldAwaitPos;
  this.inFunction = oldInFunc;
  return this.finishNode(node, "ArrowFunctionExpression")
};

// Parse function body and check parameters.

pp$3.parseFunctionBody = function(node, isArrowFunction) {
  var isExpression = isArrowFunction && this.type !== types.braceL;
  var oldStrict = this.strict, useStrict = false;

  if (isExpression) {
    node.body = this.parseMaybeAssign();
    node.expression = true;
    this.checkParams(node, false);
  } else {
    var nonSimple = this.options.ecmaVersion >= 7 && !this.isSimpleParamList(node.params);
    if (!oldStrict || nonSimple) {
      useStrict = this.strictDirective(this.end);
      // If this is a strict mode function, verify that argument names
      // are not repeated, and it does not try to bind the words `eval`
      // or `arguments`.
      if (useStrict && nonSimple)
        { this.raiseRecoverable(node.start, "Illegal 'use strict' directive in function with non-simple parameter list"); }
    }
    // Start a new scope with regard to labels and the `inFunction`
    // flag (restore them to their old value afterwards).
    var oldLabels = this.labels;
    this.labels = [];
    if (useStrict) { this.strict = true; }

    // Add the params to varDeclaredNames to ensure that an error is thrown
    // if a let/const declaration in the function clashes with one of the params.
    this.checkParams(node, !oldStrict && !useStrict && !isArrowFunction && this.isSimpleParamList(node.params));
    node.body = this.parseBlock(false);
    node.expression = false;
    this.adaptDirectivePrologue(node.body.body);
    this.labels = oldLabels;
  }
  this.exitFunctionScope();

  if (this.strict && node.id) {
    // Ensure the function name isn't a forbidden identifier in strict mode, e.g. 'eval'
    this.checkLVal(node.id, "none");
  }
  this.strict = oldStrict;
};

pp$3.isSimpleParamList = function(params) {
  for (var i = 0, list = params; i < list.length; i += 1)
    {
    var param = list[i];

    if (param.type !== "Identifier") { return false
  } }
  return true
};

// Checks function params for various disallowed patterns such as using "eval"
// or "arguments" and duplicate parameters.

pp$3.checkParams = function(node, allowDuplicates) {
  var this$1 = this;

  var nameHash = {};
  for (var i = 0, list = node.params; i < list.length; i += 1)
    {
    var param = list[i];

    this$1.checkLVal(param, "var", allowDuplicates ? null : nameHash);
  }
};

// Parses a comma-separated list of expressions, and returns them as
// an array. `close` is the token type that ends the list, and
// `allowEmpty` can be turned on to allow subsequent commas with
// nothing in between them to be parsed as `null` (which is needed
// for array literals).

pp$3.parseExprList = function(close, allowTrailingComma, allowEmpty, refDestructuringErrors) {
  var this$1 = this;

  var elts = [], first = true;
  while (!this.eat(close)) {
    if (!first) {
      this$1.expect(types.comma);
      if (allowTrailingComma && this$1.afterTrailingComma(close)) { break }
    } else { first = false; }

    var elt = (void 0);
    if (allowEmpty && this$1.type === types.comma)
      { elt = null; }
    else if (this$1.type === types.ellipsis) {
      elt = this$1.parseSpread(refDestructuringErrors);
      if (refDestructuringErrors && this$1.type === types.comma && refDestructuringErrors.trailingComma < 0)
        { refDestructuringErrors.trailingComma = this$1.start; }
    } else {
      elt = this$1.parseMaybeAssign(false, refDestructuringErrors);
    }
    elts.push(elt);
  }
  return elts
};

pp$3.checkUnreserved = function(ref) {
  var start = ref.start;
  var end = ref.end;
  var name = ref.name;

  if (this.inGenerator && name === "yield")
    { this.raiseRecoverable(start, "Can not use 'yield' as identifier inside a generator"); }
  if (this.inAsync && name === "await")
    { this.raiseRecoverable(start, "Can not use 'await' as identifier inside an async function"); }
  if (this.isKeyword(name))
    { this.raise(start, ("Unexpected keyword '" + name + "'")); }
  if (this.options.ecmaVersion < 6 &&
    this.input.slice(start, end).indexOf("\\") != -1) { return }
  var re = this.strict ? this.reservedWordsStrict : this.reservedWords;
  if (re.test(name))
    { this.raiseRecoverable(start, ("The keyword '" + name + "' is reserved")); }
};

// Parse the next token as an identifier. If `liberal` is true (used
// when parsing properties), it will also convert keywords into
// identifiers.

pp$3.parseIdent = function(liberal, isBinding) {
  var node = this.startNode();
  if (liberal && this.options.allowReserved == "never") { liberal = false; }
  if (this.type === types.name) {
    node.name = this.value;
  } else if (this.type.keyword) {
    node.name = this.type.keyword;

    // To fix https://github.com/ternjs/acorn/issues/575
    // `class` and `function` keywords push new context into this.context.
    // But there is no chance to pop the context if the keyword is consumed as an identifier such as a property name.
    // If the previous token is a dot, this does not apply because the context-managing code already ignored the keyword
    if ((node.name === "class" || node.name === "function") &&
        (this.lastTokEnd !== this.lastTokStart + 1 || this.input.charCodeAt(this.lastTokStart) !== 46)) {
      this.context.pop();
    }
  } else {
    this.unexpected();
  }
  this.next();
  this.finishNode(node, "Identifier");
  if (!liberal) { this.checkUnreserved(node); }
  return node
};

// Parses yield expression inside generator.

pp$3.parseYield = function() {
  if (!this.yieldPos) { this.yieldPos = this.start; }

  var node = this.startNode();
  this.next();
  if (this.type == types.semi || this.canInsertSemicolon() || (this.type != types.star && !this.type.startsExpr)) {
    node.delegate = false;
    node.argument = null;
  } else {
    node.delegate = this.eat(types.star);
    node.argument = this.parseMaybeAssign();
  }
  return this.finishNode(node, "YieldExpression")
};

pp$3.parseAwait = function() {
  if (!this.awaitPos) { this.awaitPos = this.start; }

  var node = this.startNode();
  this.next();
  node.argument = this.parseMaybeUnary(null, true);
  return this.finishNode(node, "AwaitExpression")
};

var pp$4 = Parser.prototype;

// This function is used to raise exceptions on parse errors. It
// takes an offset integer (into the current `input`) to indicate
// the location of the error, attaches the position to the end
// of the error message, and then raises a `SyntaxError` with that
// message.

pp$4.raise = function(pos, message) {
  var loc = getLineInfo(this.input, pos);
  message += " (" + loc.line + ":" + loc.column + ")";
  var err = new SyntaxError(message);
  err.pos = pos; err.loc = loc; err.raisedAt = this.pos;
  throw err
};

pp$4.raiseRecoverable = pp$4.raise;

pp$4.curPosition = function() {
  if (this.options.locations) {
    return new Position(this.curLine, this.pos - this.lineStart)
  }
};

var pp$5 = Parser.prototype;

// Object.assign polyfill
var assign = Object.assign || function(target) {
  var sources = [], len = arguments.length - 1;
  while ( len-- > 0 ) sources[ len ] = arguments[ len + 1 ];

  for (var i = 0, list = sources; i < list.length; i += 1) {
    var source = list[i];

    for (var key in source) {
      if (has(source, key)) {
        target[key] = source[key];
      }
    }
  }
  return target
};

// The functions in this module keep track of declared variables in the current scope in order to detect duplicate variable names.

pp$5.enterFunctionScope = function() {
  // var: a hash of var-declared names in the current lexical scope
  // lexical: a hash of lexically-declared names in the current lexical scope
  // childVar: a hash of var-declared names in all child lexical scopes of the current lexical scope (within the current function scope)
  // parentLexical: a hash of lexically-declared names in all parent lexical scopes of the current lexical scope (within the current function scope)
  this.scopeStack.push({var: {}, lexical: {}, childVar: {}, parentLexical: {}});
};

pp$5.exitFunctionScope = function() {
  this.scopeStack.pop();
};

pp$5.enterLexicalScope = function() {
  var parentScope = this.scopeStack[this.scopeStack.length - 1];
  var childScope = {var: {}, lexical: {}, childVar: {}, parentLexical: {}};

  this.scopeStack.push(childScope);
  assign(childScope.parentLexical, parentScope.lexical, parentScope.parentLexical);
};

pp$5.exitLexicalScope = function() {
  var childScope = this.scopeStack.pop();
  var parentScope = this.scopeStack[this.scopeStack.length - 1];

  assign(parentScope.childVar, childScope.var, childScope.childVar);
};

/**
 * A name can be declared with `var` if there are no variables with the same name declared with `let`/`const`
 * in the current lexical scope or any of the parent lexical scopes in this function.
 */
pp$5.canDeclareVarName = function(name) {
  var currentScope = this.scopeStack[this.scopeStack.length - 1];

  return !has(currentScope.lexical, name) && !has(currentScope.parentLexical, name)
};

/**
 * A name can be declared with `let`/`const` if there are no variables with the same name declared with `let`/`const`
 * in the current scope, and there are no variables with the same name declared with `var` in the current scope or in
 * any child lexical scopes in this function.
 */
pp$5.canDeclareLexicalName = function(name) {
  var currentScope = this.scopeStack[this.scopeStack.length - 1];

  return !has(currentScope.lexical, name) && !has(currentScope.var, name) && !has(currentScope.childVar, name)
};

pp$5.declareVarName = function(name) {
  this.scopeStack[this.scopeStack.length - 1].var[name] = true;
};

pp$5.declareLexicalName = function(name) {
  this.scopeStack[this.scopeStack.length - 1].lexical[name] = true;
};

var Node = function Node(parser, pos, loc) {
  this.type = "";
  this.start = pos;
  this.end = 0;
  if (parser.options.locations)
    { this.loc = new SourceLocation(parser, loc); }
  if (parser.options.directSourceFile)
    { this.sourceFile = parser.options.directSourceFile; }
  if (parser.options.ranges)
    { this.range = [pos, 0]; }
};

// Start an AST node, attaching a start offset.

var pp$6 = Parser.prototype;

pp$6.startNode = function() {
  return new Node(this, this.start, this.startLoc)
};

pp$6.startNodeAt = function(pos, loc) {
  return new Node(this, pos, loc)
};

// Finish an AST node, adding `type` and `end` properties.

function finishNodeAt(node, type, pos, loc) {
  node.type = type;
  node.end = pos;
  if (this.options.locations)
    { node.loc.end = loc; }
  if (this.options.ranges)
    { node.range[1] = pos; }
  return node
}

pp$6.finishNode = function(node, type) {
  return finishNodeAt.call(this, node, type, this.lastTokEnd, this.lastTokEndLoc)
};

// Finish node at given position

pp$6.finishNodeAt = function(node, type, pos, loc) {
  return finishNodeAt.call(this, node, type, pos, loc)
};

// The algorithm used to determine whether a regexp can appear at a
// given point in the program is loosely based on sweet.js' approach.
// See https://github.com/mozilla/sweet.js/wiki/design

var TokContext = function TokContext(token, isExpr, preserveSpace, override, generator) {
  this.token = token;
  this.isExpr = !!isExpr;
  this.preserveSpace = !!preserveSpace;
  this.override = override;
  this.generator = !!generator;
};

var types$1 = {
  b_stat: new TokContext("{", false),
  b_expr: new TokContext("{", true),
  b_tmpl: new TokContext("${", false),
  p_stat: new TokContext("(", false),
  p_expr: new TokContext("(", true),
  q_tmpl: new TokContext("`", true, true, function (p) { return p.tryReadTemplateToken(); }),
  f_stat: new TokContext("function", false),
  f_expr: new TokContext("function", true),
  f_expr_gen: new TokContext("function", true, false, null, true),
  f_gen: new TokContext("function", false, false, null, true)
};

var pp$7 = Parser.prototype;

pp$7.initialContext = function() {
  return [types$1.b_stat]
};

pp$7.braceIsBlock = function(prevType) {
  var parent = this.curContext();
  if (parent === types$1.f_expr || parent === types$1.f_stat)
    { return true }
  if (prevType === types.colon && (parent === types$1.b_stat || parent === types$1.b_expr))
    { return !parent.isExpr }

  // The check for `tt.name && exprAllowed` detects whether we are
  // after a `yield` or `of` construct. See the `updateContext` for
  // `tt.name`.
  if (prevType === types._return || prevType == types.name && this.exprAllowed)
    { return lineBreak.test(this.input.slice(this.lastTokEnd, this.start)) }
  if (prevType === types._else || prevType === types.semi || prevType === types.eof || prevType === types.parenR || prevType == types.arrow)
    { return true }
  if (prevType == types.braceL)
    { return parent === types$1.b_stat }
  if (prevType == types._var || prevType == types.name)
    { return false }
  return !this.exprAllowed
};

pp$7.inGeneratorContext = function() {
  var this$1 = this;

  for (var i = this.context.length - 1; i >= 1; i--) {
    var context = this$1.context[i];
    if (context.token === "function")
      { return context.generator }
  }
  return false
};

pp$7.updateContext = function(prevType) {
  var update, type = this.type;
  if (type.keyword && prevType == types.dot)
    { this.exprAllowed = false; }
  else if (update = type.updateContext)
    { update.call(this, prevType); }
  else
    { this.exprAllowed = type.beforeExpr; }
};

// Token-specific context update code

types.parenR.updateContext = types.braceR.updateContext = function() {
  if (this.context.length == 1) {
    this.exprAllowed = true;
    return
  }
  var out = this.context.pop();
  if (out === types$1.b_stat && this.curContext().token === "function") {
    out = this.context.pop();
  }
  this.exprAllowed = !out.isExpr;
};

types.braceL.updateContext = function(prevType) {
  this.context.push(this.braceIsBlock(prevType) ? types$1.b_stat : types$1.b_expr);
  this.exprAllowed = true;
};

types.dollarBraceL.updateContext = function() {
  this.context.push(types$1.b_tmpl);
  this.exprAllowed = true;
};

types.parenL.updateContext = function(prevType) {
  var statementParens = prevType === types._if || prevType === types._for || prevType === types._with || prevType === types._while;
  this.context.push(statementParens ? types$1.p_stat : types$1.p_expr);
  this.exprAllowed = true;
};

types.incDec.updateContext = function() {
  // tokExprAllowed stays unchanged
};

types._function.updateContext = types._class.updateContext = function(prevType) {
  if (prevType.beforeExpr && prevType !== types.semi && prevType !== types._else &&
      !((prevType === types.colon || prevType === types.braceL) && this.curContext() === types$1.b_stat))
    { this.context.push(types$1.f_expr); }
  else
    { this.context.push(types$1.f_stat); }
  this.exprAllowed = false;
};

types.backQuote.updateContext = function() {
  if (this.curContext() === types$1.q_tmpl)
    { this.context.pop(); }
  else
    { this.context.push(types$1.q_tmpl); }
  this.exprAllowed = false;
};

types.star.updateContext = function(prevType) {
  if (prevType == types._function) {
    var index = this.context.length - 1;
    if (this.context[index] === types$1.f_expr)
      { this.context[index] = types$1.f_expr_gen; }
    else
      { this.context[index] = types$1.f_gen; }
  }
  this.exprAllowed = true;
};

types.name.updateContext = function(prevType) {
  var allowed = false;
  if (this.options.ecmaVersion >= 6) {
    if (this.value == "of" && !this.exprAllowed ||
        this.value == "yield" && this.inGeneratorContext())
      { allowed = true; }
  }
  this.exprAllowed = allowed;
};

// Object type used to represent tokens. Note that normally, tokens
// simply exist as properties on the parser object. This is only
// used for the onToken callback and the external tokenizer.

var Token = function Token(p) {
  this.type = p.type;
  this.value = p.value;
  this.start = p.start;
  this.end = p.end;
  if (p.options.locations)
    { this.loc = new SourceLocation(p, p.startLoc, p.endLoc); }
  if (p.options.ranges)
    { this.range = [p.start, p.end]; }
};

// ## Tokenizer

var pp$8 = Parser.prototype;

// Are we running under Rhino?
var isRhino = typeof Packages == "object" && Object.prototype.toString.call(Packages) == "[object JavaPackage]";

// Move to the next token

pp$8.next = function() {
  if (this.options.onToken)
    { this.options.onToken(new Token(this)); }

  this.lastTokEnd = this.end;
  this.lastTokStart = this.start;
  this.lastTokEndLoc = this.endLoc;
  this.lastTokStartLoc = this.startLoc;
  this.nextToken();
};

pp$8.getToken = function() {
  this.next();
  return new Token(this)
};

// If we're in an ES6 environment, make parsers iterable
if (typeof Symbol !== "undefined")
  { pp$8[Symbol.iterator] = function() {
    var this$1 = this;

    return {
      next: function () {
        var token = this$1.getToken();
        return {
          done: token.type === types.eof,
          value: token
        }
      }
    }
  }; }

// Toggle strict mode. Re-reads the next number or string to please
// pedantic tests (`"use strict"; 010;` should fail).

pp$8.curContext = function() {
  return this.context[this.context.length - 1]
};

// Read a single token, updating the parser object's token-related
// properties.

pp$8.nextToken = function() {
  var curContext = this.curContext();
  if (!curContext || !curContext.preserveSpace) { this.skipSpace(); }

  this.start = this.pos;
  if (this.options.locations) { this.startLoc = this.curPosition(); }
  if (this.pos >= this.input.length) { return this.finishToken(types.eof) }

  if (curContext.override) { return curContext.override(this) }
  else { this.readToken(this.fullCharCodeAtPos()); }
};

pp$8.readToken = function(code) {
  // Identifier or keyword. '\uXXXX' sequences are allowed in
  // identifiers, so '\' also dispatches to that.
  if (isIdentifierStart(code, this.options.ecmaVersion >= 6) || code === 92 /* '\' */)
    { return this.readWord() }

  return this.getTokenFromCode(code)
};

pp$8.fullCharCodeAtPos = function() {
  var code = this.input.charCodeAt(this.pos);
  if (code <= 0xd7ff || code >= 0xe000) { return code }
  var next = this.input.charCodeAt(this.pos + 1);
  return (code << 10) + next - 0x35fdc00
};

pp$8.skipBlockComment = function() {
  var this$1 = this;

  var startLoc = this.options.onComment && this.curPosition();
  var start = this.pos, end = this.input.indexOf("*/", this.pos += 2);
  if (end === -1) { this.raise(this.pos - 2, "Unterminated comment"); }
  this.pos = end + 2;
  if (this.options.locations) {
    lineBreakG.lastIndex = start;
    var match;
    while ((match = lineBreakG.exec(this.input)) && match.index < this.pos) {
      ++this$1.curLine;
      this$1.lineStart = match.index + match[0].length;
    }
  }
  if (this.options.onComment)
    { this.options.onComment(true, this.input.slice(start + 2, end), start, this.pos,
                           startLoc, this.curPosition()); }
};

pp$8.skipLineComment = function(startSkip) {
  var this$1 = this;

  var start = this.pos;
  var startLoc = this.options.onComment && this.curPosition();
  var ch = this.input.charCodeAt(this.pos += startSkip);
  while (this.pos < this.input.length && !isNewLine(ch)) {
    ch = this$1.input.charCodeAt(++this$1.pos);
  }
  if (this.options.onComment)
    { this.options.onComment(false, this.input.slice(start + startSkip, this.pos), start, this.pos,
                           startLoc, this.curPosition()); }
};

// Called at the start of the parse and after every token. Skips
// whitespace and comments, and.

pp$8.skipSpace = function() {
  var this$1 = this;

  loop: while (this.pos < this.input.length) {
    var ch = this$1.input.charCodeAt(this$1.pos);
    switch (ch) {
    case 32: case 160: // ' '
      ++this$1.pos;
      break
    case 13:
      if (this$1.input.charCodeAt(this$1.pos + 1) === 10) {
        ++this$1.pos;
      }
    case 10: case 8232: case 8233:
      ++this$1.pos;
      if (this$1.options.locations) {
        ++this$1.curLine;
        this$1.lineStart = this$1.pos;
      }
      break
    case 47: // '/'
      switch (this$1.input.charCodeAt(this$1.pos + 1)) {
      case 42: // '*'
        this$1.skipBlockComment();
        break
      case 47:
        this$1.skipLineComment(2);
        break
      default:
        break loop
      }
      break
    default:
      if (ch > 8 && ch < 14 || ch >= 5760 && nonASCIIwhitespace.test(String.fromCharCode(ch))) {
        ++this$1.pos;
      } else {
        break loop
      }
    }
  }
};

// Called at the end of every token. Sets `end`, `val`, and
// maintains `context` and `exprAllowed`, and skips the space after
// the token, so that the next one's `start` will point at the
// right position.

pp$8.finishToken = function(type, val) {
  this.end = this.pos;
  if (this.options.locations) { this.endLoc = this.curPosition(); }
  var prevType = this.type;
  this.type = type;
  this.value = val;

  this.updateContext(prevType);
};

// ### Token reading

// This is the function that is called to fetch the next token. It
// is somewhat obscure, because it works in character codes rather
// than characters, and because operator parsing has been inlined
// into it.
//
// All in the name of speed.
//
pp$8.readToken_dot = function() {
  var next = this.input.charCodeAt(this.pos + 1);
  if (next >= 48 && next <= 57) { return this.readNumber(true) }
  var next2 = this.input.charCodeAt(this.pos + 2);
  if (this.options.ecmaVersion >= 6 && next === 46 && next2 === 46) { // 46 = dot '.'
    this.pos += 3;
    return this.finishToken(types.ellipsis)
  } else {
    ++this.pos;
    return this.finishToken(types.dot)
  }
};

pp$8.readToken_slash = function() { // '/'
  var next = this.input.charCodeAt(this.pos + 1);
  if (this.exprAllowed) { ++this.pos; return this.readRegexp() }
  if (next === 61) { return this.finishOp(types.assign, 2) }
  return this.finishOp(types.slash, 1)
};

pp$8.readToken_mult_modulo_exp = function(code) { // '%*'
  var next = this.input.charCodeAt(this.pos + 1);
  var size = 1;
  var tokentype = code === 42 ? types.star : types.modulo;

  // exponentiation operator ** and **=
  if (this.options.ecmaVersion >= 7 && code == 42 && next === 42) {
    ++size;
    tokentype = types.starstar;
    next = this.input.charCodeAt(this.pos + 2);
  }

  if (next === 61) { return this.finishOp(types.assign, size + 1) }
  return this.finishOp(tokentype, size)
};

pp$8.readToken_pipe_amp = function(code) { // '|&'
  var next = this.input.charCodeAt(this.pos + 1);
  if (next === code) { return this.finishOp(code === 124 ? types.logicalOR : types.logicalAND, 2) }
  if (next === 61) { return this.finishOp(types.assign, 2) }
  return this.finishOp(code === 124 ? types.bitwiseOR : types.bitwiseAND, 1)
};

pp$8.readToken_caret = function() { // '^'
  var next = this.input.charCodeAt(this.pos + 1);
  if (next === 61) { return this.finishOp(types.assign, 2) }
  return this.finishOp(types.bitwiseXOR, 1)
};

pp$8.readToken_plus_min = function(code) { // '+-'
  var next = this.input.charCodeAt(this.pos + 1);
  if (next === code) {
    if (next == 45 && !this.inModule && this.input.charCodeAt(this.pos + 2) == 62 &&
        (this.lastTokEnd === 0 || lineBreak.test(this.input.slice(this.lastTokEnd, this.pos)))) {
      // A `-->` line comment
      this.skipLineComment(3);
      this.skipSpace();
      return this.nextToken()
    }
    return this.finishOp(types.incDec, 2)
  }
  if (next === 61) { return this.finishOp(types.assign, 2) }
  return this.finishOp(types.plusMin, 1)
};

pp$8.readToken_lt_gt = function(code) { // '<>'
  var next = this.input.charCodeAt(this.pos + 1);
  var size = 1;
  if (next === code) {
    size = code === 62 && this.input.charCodeAt(this.pos + 2) === 62 ? 3 : 2;
    if (this.input.charCodeAt(this.pos + size) === 61) { return this.finishOp(types.assign, size + 1) }
    return this.finishOp(types.bitShift, size)
  }
  if (next == 33 && code == 60 && !this.inModule && this.input.charCodeAt(this.pos + 2) == 45 &&
      this.input.charCodeAt(this.pos + 3) == 45) {
    // `<!--`, an XML-style comment that should be interpreted as a line comment
    this.skipLineComment(4);
    this.skipSpace();
    return this.nextToken()
  }
  if (next === 61) { size = 2; }
  return this.finishOp(types.relational, size)
};

pp$8.readToken_eq_excl = function(code) { // '=!'
  var next = this.input.charCodeAt(this.pos + 1);
  if (next === 61) { return this.finishOp(types.equality, this.input.charCodeAt(this.pos + 2) === 61 ? 3 : 2) }
  if (code === 61 && next === 62 && this.options.ecmaVersion >= 6) { // '=>'
    this.pos += 2;
    return this.finishToken(types.arrow)
  }
  return this.finishOp(code === 61 ? types.eq : types.prefix, 1)
};

pp$8.getTokenFromCode = function(code) {
  switch (code) {
    // The interpretation of a dot depends on whether it is followed
    // by a digit or another two dots.
  case 46: // '.'
    return this.readToken_dot()

    // Punctuation tokens.
  case 40: ++this.pos; return this.finishToken(types.parenL)
  case 41: ++this.pos; return this.finishToken(types.parenR)
  case 59: ++this.pos; return this.finishToken(types.semi)
  case 44: ++this.pos; return this.finishToken(types.comma)
  case 91: ++this.pos; return this.finishToken(types.bracketL)
  case 93: ++this.pos; return this.finishToken(types.bracketR)
  case 123: ++this.pos; return this.finishToken(types.braceL)
  case 125: ++this.pos; return this.finishToken(types.braceR)
  case 58: ++this.pos; return this.finishToken(types.colon)
  case 63: ++this.pos; return this.finishToken(types.question)

  case 96: // '`'
    if (this.options.ecmaVersion < 6) { break }
    ++this.pos;
    return this.finishToken(types.backQuote)

  case 48: // '0'
    var next = this.input.charCodeAt(this.pos + 1);
    if (next === 120 || next === 88) { return this.readRadixNumber(16) } // '0x', '0X' - hex number
    if (this.options.ecmaVersion >= 6) {
      if (next === 111 || next === 79) { return this.readRadixNumber(8) } // '0o', '0O' - octal number
      if (next === 98 || next === 66) { return this.readRadixNumber(2) } // '0b', '0B' - binary number
    }
    // Anything else beginning with a digit is an integer, octal
    // number, or float.
  case 49: case 50: case 51: case 52: case 53: case 54: case 55: case 56: case 57: // 1-9
    return this.readNumber(false)

    // Quotes produce strings.
  case 34: case 39: // '"', "'"
    return this.readString(code)

    // Operators are parsed inline in tiny state machines. '=' (61) is
    // often referred to. `finishOp` simply skips the amount of
    // characters it is given as second argument, and returns a token
    // of the type given by its first argument.

  case 47: // '/'
    return this.readToken_slash()

  case 37: case 42: // '%*'
    return this.readToken_mult_modulo_exp(code)

  case 124: case 38: // '|&'
    return this.readToken_pipe_amp(code)

  case 94: // '^'
    return this.readToken_caret()

  case 43: case 45: // '+-'
    return this.readToken_plus_min(code)

  case 60: case 62: // '<>'
    return this.readToken_lt_gt(code)

  case 61: case 33: // '=!'
    return this.readToken_eq_excl(code)

  case 126: // '~'
    return this.finishOp(types.prefix, 1)
  }

  this.raise(this.pos, "Unexpected character '" + codePointToString(code) + "'");
};

pp$8.finishOp = function(type, size) {
  var str = this.input.slice(this.pos, this.pos + size);
  this.pos += size;
  return this.finishToken(type, str)
};

// Parse a regular expression. Some context-awareness is necessary,
// since a '/' inside a '[]' set does not end the expression.

function tryCreateRegexp(src, flags, throwErrorAt, parser) {
  try {
    return new RegExp(src, flags)
  } catch (e) {
    if (throwErrorAt !== undefined) {
      if (e instanceof SyntaxError) { parser.raise(throwErrorAt, "Error parsing regular expression: " + e.message); }
      throw e
    }
  }
}

var regexpUnicodeSupport = !!tryCreateRegexp("\uffff", "u");

pp$8.readRegexp = function() {
  var this$1 = this;

  var escaped, inClass, start = this.pos;
  for (;;) {
    if (this$1.pos >= this$1.input.length) { this$1.raise(start, "Unterminated regular expression"); }
    var ch = this$1.input.charAt(this$1.pos);
    if (lineBreak.test(ch)) { this$1.raise(start, "Unterminated regular expression"); }
    if (!escaped) {
      if (ch === "[") { inClass = true; }
      else if (ch === "]" && inClass) { inClass = false; }
      else if (ch === "/" && !inClass) { break }
      escaped = ch === "\\";
    } else { escaped = false; }
    ++this$1.pos;
  }
  var content = this.input.slice(start, this.pos);
  ++this.pos;
  // Need to use `readWord1` because '\uXXXX' sequences are allowed
  // here (don't ask).
  var mods = this.readWord1();
  var tmp = content, tmpFlags = "";
  if (mods) {
    var validFlags = /^[gim]*$/;
    if (this.options.ecmaVersion >= 6) { validFlags = /^[gimuy]*$/; }
    if (!validFlags.test(mods)) { this.raise(start, "Invalid regular expression flag"); }
    if (mods.indexOf("u") >= 0) {
      if (regexpUnicodeSupport) {
        tmpFlags = "u";
      } else {
        // Replace each astral symbol and every Unicode escape sequence that
        // possibly represents an astral symbol or a paired surrogate with a
        // single ASCII symbol to avoid throwing on regular expressions that
        // are only valid in combination with the `/u` flag.
        // Note: replacing with the ASCII symbol `x` might cause false
        // negatives in unlikely scenarios. For example, `[\u{61}-b]` is a
        // perfectly valid pattern that is equivalent to `[a-b]`, but it would
        // be replaced by `[x-b]` which throws an error.
        tmp = tmp.replace(/\\u\{([0-9a-fA-F]+)\}/g, function (_match, code, offset) {
          code = Number("0x" + code);
          if (code > 0x10FFFF) { this$1.raise(start + offset + 3, "Code point out of bounds"); }
          return "x"
        });
        tmp = tmp.replace(/\\u([a-fA-F0-9]{4})|[\uD800-\uDBFF][\uDC00-\uDFFF]/g, "x");
        tmpFlags = tmpFlags.replace("u", "");
      }
    }
  }
  // Detect invalid regular expressions.
  var value = null;
  // Rhino's regular expression parser is flaky and throws uncatchable exceptions,
  // so don't do detection if we are running under Rhino
  if (!isRhino) {
    tryCreateRegexp(tmp, tmpFlags, start, this);
    // Get a regular expression object for this pattern-flag pair, or `null` in
    // case the current environment doesn't support the flags it uses.
    value = tryCreateRegexp(content, mods);
  }
  return this.finishToken(types.regexp, {pattern: content, flags: mods, value: value})
};

// Read an integer in the given radix. Return null if zero digits
// were read, the integer value otherwise. When `len` is given, this
// will return `null` unless the integer has exactly `len` digits.

pp$8.readInt = function(radix, len) {
  var this$1 = this;

  var start = this.pos, total = 0;
  for (var i = 0, e = len == null ? Infinity : len; i < e; ++i) {
    var code = this$1.input.charCodeAt(this$1.pos), val = (void 0);
    if (code >= 97) { val = code - 97 + 10; } // a
    else if (code >= 65) { val = code - 65 + 10; } // A
    else if (code >= 48 && code <= 57) { val = code - 48; } // 0-9
    else { val = Infinity; }
    if (val >= radix) { break }
    ++this$1.pos;
    total = total * radix + val;
  }
  if (this.pos === start || len != null && this.pos - start !== len) { return null }

  return total
};

pp$8.readRadixNumber = function(radix) {
  this.pos += 2; // 0x
  var val = this.readInt(radix);
  if (val == null) { this.raise(this.start + 2, "Expected number in radix " + radix); }
  if (isIdentifierStart(this.fullCharCodeAtPos())) { this.raise(this.pos, "Identifier directly after number"); }
  return this.finishToken(types.num, val)
};

// Read an integer, octal integer, or floating-point number.

pp$8.readNumber = function(startsWithDot) {
  var start = this.pos, isFloat = false, octal = this.input.charCodeAt(this.pos) === 48;
  if (!startsWithDot && this.readInt(10) === null) { this.raise(start, "Invalid number"); }
  if (octal && this.pos == start + 1) { octal = false; }
  var next = this.input.charCodeAt(this.pos);
  if (next === 46 && !octal) { // '.'
    ++this.pos;
    this.readInt(10);
    isFloat = true;
    next = this.input.charCodeAt(this.pos);
  }
  if ((next === 69 || next === 101) && !octal) { // 'eE'
    next = this.input.charCodeAt(++this.pos);
    if (next === 43 || next === 45) { ++this.pos; } // '+-'
    if (this.readInt(10) === null) { this.raise(start, "Invalid number"); }
    isFloat = true;
  }
  if (isIdentifierStart(this.fullCharCodeAtPos())) { this.raise(this.pos, "Identifier directly after number"); }

  var str = this.input.slice(start, this.pos), val;
  if (isFloat) { val = parseFloat(str); }
  else if (!octal || str.length === 1) { val = parseInt(str, 10); }
  else if (this.strict) { this.raise(start, "Invalid number"); }
  else if (/[89]/.test(str)) { val = parseInt(str, 10); }
  else { val = parseInt(str, 8); }
  return this.finishToken(types.num, val)
};

// Read a string value, interpreting backslash-escapes.

pp$8.readCodePoint = function() {
  var ch = this.input.charCodeAt(this.pos), code;

  if (ch === 123) { // '{'
    if (this.options.ecmaVersion < 6) { this.unexpected(); }
    var codePos = ++this.pos;
    code = this.readHexChar(this.input.indexOf("}", this.pos) - this.pos);
    ++this.pos;
    if (code > 0x10FFFF) { this.invalidStringToken(codePos, "Code point out of bounds"); }
  } else {
    code = this.readHexChar(4);
  }
  return code
};

function codePointToString(code) {
  // UTF-16 Decoding
  if (code <= 0xFFFF) { return String.fromCharCode(code) }
  code -= 0x10000;
  return String.fromCharCode((code >> 10) + 0xD800, (code & 1023) + 0xDC00)
}

pp$8.readString = function(quote) {
  var this$1 = this;

  var out = "", chunkStart = ++this.pos;
  for (;;) {
    if (this$1.pos >= this$1.input.length) { this$1.raise(this$1.start, "Unterminated string constant"); }
    var ch = this$1.input.charCodeAt(this$1.pos);
    if (ch === quote) { break }
    if (ch === 92) { // '\'
      out += this$1.input.slice(chunkStart, this$1.pos);
      out += this$1.readEscapedChar(false);
      chunkStart = this$1.pos;
    } else {
      if (isNewLine(ch)) { this$1.raise(this$1.start, "Unterminated string constant"); }
      ++this$1.pos;
    }
  }
  out += this.input.slice(chunkStart, this.pos++);
  return this.finishToken(types.string, out)
};

// Reads template string tokens.

var INVALID_TEMPLATE_ESCAPE_ERROR = {};

pp$8.tryReadTemplateToken = function() {
  this.inTemplateElement = true;
  try {
    this.readTmplToken();
  } catch (err) {
    if (err === INVALID_TEMPLATE_ESCAPE_ERROR) {
      this.readInvalidTemplateToken();
    } else {
      throw err
    }
  }

  this.inTemplateElement = false;
};

pp$8.invalidStringToken = function(position, message) {
  if (this.inTemplateElement && this.options.ecmaVersion >= 9) {
    throw INVALID_TEMPLATE_ESCAPE_ERROR
  } else {
    this.raise(position, message);
  }
};

pp$8.readTmplToken = function() {
  var this$1 = this;

  var out = "", chunkStart = this.pos;
  for (;;) {
    if (this$1.pos >= this$1.input.length) { this$1.raise(this$1.start, "Unterminated template"); }
    var ch = this$1.input.charCodeAt(this$1.pos);
    if (ch === 96 || ch === 36 && this$1.input.charCodeAt(this$1.pos + 1) === 123) { // '`', '${'
      if (this$1.pos === this$1.start && (this$1.type === types.template || this$1.type === types.invalidTemplate)) {
        if (ch === 36) {
          this$1.pos += 2;
          return this$1.finishToken(types.dollarBraceL)
        } else {
          ++this$1.pos;
          return this$1.finishToken(types.backQuote)
        }
      }
      out += this$1.input.slice(chunkStart, this$1.pos);
      return this$1.finishToken(types.template, out)
    }
    if (ch === 92) { // '\'
      out += this$1.input.slice(chunkStart, this$1.pos);
      out += this$1.readEscapedChar(true);
      chunkStart = this$1.pos;
    } else if (isNewLine(ch)) {
      out += this$1.input.slice(chunkStart, this$1.pos);
      ++this$1.pos;
      switch (ch) {
      case 13:
        if (this$1.input.charCodeAt(this$1.pos) === 10) { ++this$1.pos; }
      case 10:
        out += "\n";
        break
      default:
        out += String.fromCharCode(ch);
        break
      }
      if (this$1.options.locations) {
        ++this$1.curLine;
        this$1.lineStart = this$1.pos;
      }
      chunkStart = this$1.pos;
    } else {
      ++this$1.pos;
    }
  }
};

// Reads a template token to search for the end, without validating any escape sequences
pp$8.readInvalidTemplateToken = function() {
  var this$1 = this;

  for (; this.pos < this.input.length; this.pos++) {
    switch (this$1.input[this$1.pos]) {
    case "\\":
      ++this$1.pos;
      break

    case "$":
      if (this$1.input[this$1.pos + 1] !== "{") {
        break
      }
    // falls through

    case "`":
      return this$1.finishToken(types.invalidTemplate, this$1.input.slice(this$1.start, this$1.pos))

    // no default
    }
  }
  this.raise(this.start, "Unterminated template");
};

// Used to read escaped characters

pp$8.readEscapedChar = function(inTemplate) {
  var ch = this.input.charCodeAt(++this.pos);
  ++this.pos;
  switch (ch) {
  case 110: return "\n" // 'n' -> '\n'
  case 114: return "\r" // 'r' -> '\r'
  case 120: return String.fromCharCode(this.readHexChar(2)) // 'x'
  case 117: return codePointToString(this.readCodePoint()) // 'u'
  case 116: return "\t" // 't' -> '\t'
  case 98: return "\b" // 'b' -> '\b'
  case 118: return "\u000b" // 'v' -> '\u000b'
  case 102: return "\f" // 'f' -> '\f'
  case 13: if (this.input.charCodeAt(this.pos) === 10) { ++this.pos; } // '\r\n'
  case 10: // ' \n'
    if (this.options.locations) { this.lineStart = this.pos; ++this.curLine; }
    return ""
  default:
    if (ch >= 48 && ch <= 55) {
      var octalStr = this.input.substr(this.pos - 1, 3).match(/^[0-7]+/)[0];
      var octal = parseInt(octalStr, 8);
      if (octal > 255) {
        octalStr = octalStr.slice(0, -1);
        octal = parseInt(octalStr, 8);
      }
      if (octalStr !== "0" && (this.strict || inTemplate)) {
        this.invalidStringToken(this.pos - 2, "Octal literal in strict mode");
      }
      this.pos += octalStr.length - 1;
      return String.fromCharCode(octal)
    }
    return String.fromCharCode(ch)
  }
};

// Used to read character escape sequences ('\x', '\u', '\U').

pp$8.readHexChar = function(len) {
  var codePos = this.pos;
  var n = this.readInt(16, len);
  if (n === null) { this.invalidStringToken(codePos, "Bad character escape sequence"); }
  return n
};

// Read an identifier, and return it as a string. Sets `this.containsEsc`
// to whether the word contained a '\u' escape.
//
// Incrementally adds only escaped chars, adding other chunks as-is
// as a micro-optimization.

pp$8.readWord1 = function() {
  var this$1 = this;

  this.containsEsc = false;
  var word = "", first = true, chunkStart = this.pos;
  var astral = this.options.ecmaVersion >= 6;
  while (this.pos < this.input.length) {
    var ch = this$1.fullCharCodeAtPos();
    if (isIdentifierChar(ch, astral)) {
      this$1.pos += ch <= 0xffff ? 1 : 2;
    } else if (ch === 92) { // "\"
      this$1.containsEsc = true;
      word += this$1.input.slice(chunkStart, this$1.pos);
      var escStart = this$1.pos;
      if (this$1.input.charCodeAt(++this$1.pos) != 117) // "u"
        { this$1.invalidStringToken(this$1.pos, "Expecting Unicode escape sequence \\uXXXX"); }
      ++this$1.pos;
      var esc = this$1.readCodePoint();
      if (!(first ? isIdentifierStart : isIdentifierChar)(esc, astral))
        { this$1.invalidStringToken(escStart, "Invalid Unicode escape"); }
      word += codePointToString(esc);
      chunkStart = this$1.pos;
    } else {
      break
    }
    first = false;
  }
  return word + this.input.slice(chunkStart, this.pos)
};

// Read an identifier or keyword token. Will check for reserved
// words when necessary.

pp$8.readWord = function() {
  var word = this.readWord1();
  var type = types.name;
  if (this.keywords.test(word)) {
    if (this.containsEsc) { this.raiseRecoverable(this.start, "Escape sequence in keyword " + word); }
    type = keywords$1[word];
  }
  return this.finishToken(type, word)
};

// Acorn is a tiny, fast JavaScript parser written in JavaScript.
//
// Acorn was written by Marijn Haverbeke, Ingvar Stepanyan, and
// various contributors and released under an MIT license.
//
// Git repositories for Acorn are available at
//
//     http://marijnhaverbeke.nl/git/acorn
//     https://github.com/ternjs/acorn.git
//
// Please use the [github bug tracker][ghbt] to report issues.
//
// [ghbt]: https://github.com/ternjs/acorn/issues
//
// This file defines the main parser interface. The library also comes
// with a [error-tolerant parser][dammit] and an
// [abstract syntax tree walker][walk], defined in other files.
//
// [dammit]: acorn_loose.js
// [walk]: util/walk.js

var version = "5.2.1";

// The main exported interface (under `self.acorn` when in the
// browser) is a `parse` function that takes a code string and
// returns an abstract syntax tree as specified by [Mozilla parser
// API][api].
//
// [api]: https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API

function parse(input, options) {
  return new Parser(options, input).parse()
}

// This function tries to parse a single expression at a given
// offset in a string. Useful for parsing mixed-language formats
// that embed JavaScript expressions.

function parseExpressionAt(input, pos, options) {
  var p = new Parser(options, input, pos);
  p.nextToken();
  return p.parseExpression()
}

// Acorn is organized as a tokenizer and a recursive-descent parser.
// The `tokenizer` export provides an interface to the tokenizer.

function tokenizer(input, options) {
  return new Parser(options, input)
}

// This is a terrible kludge to support the existing, pre-ES6
// interface where the loose parser module retroactively adds exports
// to this module.
 // eslint-disable-line camelcase
function addLooseExports(parse, Parser$$1, plugins$$1) {
  exports.parse_dammit = parse; // eslint-disable-line camelcase
  exports.LooseParser = Parser$$1;
  exports.pluginsLoose = plugins$$1;
}

exports.version = version;
exports.parse = parse;
exports.parseExpressionAt = parseExpressionAt;
exports.tokenizer = tokenizer;
exports.addLooseExports = addLooseExports;
exports.Parser = Parser;
exports.plugins = plugins;
exports.defaultOptions = defaultOptions;
exports.Position = Position;
exports.SourceLocation = SourceLocation;
exports.getLineInfo = getLineInfo;
exports.Node = Node;
exports.TokenType = TokenType;
exports.tokTypes = types;
exports.keywordTypes = keywords$1;
exports.TokContext = TokContext;
exports.tokContexts = types$1;
exports.isIdentifierChar = isIdentifierChar;
exports.isIdentifierStart = isIdentifierStart;
exports.Token = Token;
exports.isNewLine = isNewLine;
exports.lineBreak = lineBreak;
exports.lineBreakG = lineBreakG;
exports.nonASCIIwhitespace = nonASCIIwhitespace;

Object.defineProperty(exports, '__esModule', { value: true });

})));


/***/ }),

/***/ 802:
/***/ (function(module, exports, __webpack_require__) {

var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_RESULT__;/* -*- indent-tabs-mode: nil; js-indent-level: 2; fill-column: 80 -*- */
/*
 * Copyright 2013 Mozilla Foundation and contributors
 * Licensed under the New BSD license. See LICENSE.md or:
 * http://opensource.org/licenses/BSD-2-Clause
 */
(function (root, factory) {
  "use strict";

  if (true) {
    !(__WEBPACK_AMD_DEFINE_FACTORY__ = (factory),
				__WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ?
				(__WEBPACK_AMD_DEFINE_FACTORY__.call(exports, __webpack_require__, exports, module)) :
				__WEBPACK_AMD_DEFINE_FACTORY__),
				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
  } else if (typeof exports === "object") {
    module.exports = factory();
  } else {
    root.prettyFast = factory();
  }
}(this, function () {
  "use strict";

  var acorn = this.acorn || __webpack_require__(381);
  var sourceMap = this.sourceMap || __webpack_require__(815);
  var SourceNode = sourceMap.SourceNode;

  // If any of these tokens are seen before a "[" token, we know that "[" token
  // is the start of an array literal, rather than a property access.
  //
  // The only exception is "}", which would need to be disambiguated by
  // parsing. The majority of the time, an open bracket following a closing
  // curly is going to be an array literal, so we brush the complication under
  // the rug, and handle the ambiguity by always assuming that it will be an
  // array literal.
  var PRE_ARRAY_LITERAL_TOKENS = {
    "typeof": true,
    "void": true,
    "delete": true,
    "case": true,
    "do": true,
    "=": true,
    "in": true,
    "{": true,
    "*": true,
    "/": true,
    "%": true,
    "else": true,
    ";": true,
    "++": true,
    "--": true,
    "+": true,
    "-": true,
    "~": true,
    "!": true,
    ":": true,
    "?": true,
    ">>": true,
    ">>>": true,
    "<<": true,
    "||": true,
    "&&": true,
    "<": true,
    ">": true,
    "<=": true,
    ">=": true,
    "instanceof": true,
    "&": true,
    "^": true,
    "|": true,
    "==": true,
    "!=": true,
    "===": true,
    "!==": true,
    ",": true,

    "}": true
  };

  /**
   * Determines if we think that the given token starts an array literal.
   *
   * @param Object token
   *        The token we want to determine if it is an array literal.
   * @param Object lastToken
   *        The last token we added to the pretty printed results.
   *
   * @returns Boolean
   *          True if we believe it is an array literal, false otherwise.
   */
  function isArrayLiteral(token, lastToken) {
    if (token.type.label != "[") {
      return false;
    }
    if (!lastToken) {
      return true;
    }
    if (lastToken.type.isAssign) {
      return true;
    }
    return !!PRE_ARRAY_LITERAL_TOKENS[
      lastToken.type.keyword || lastToken.type.label
    ];
  }

  // If any of these tokens are followed by a token on a new line, we know that
  // ASI cannot happen.
  var PREVENT_ASI_AFTER_TOKENS = {
    // Binary operators
    "*": true,
    "/": true,
    "%": true,
    "+": true,
    "-": true,
    "<<": true,
    ">>": true,
    ">>>": true,
    "<": true,
    ">": true,
    "<=": true,
    ">=": true,
    "instanceof": true,
    "in": true,
    "==": true,
    "!=": true,
    "===": true,
    "!==": true,
    "&": true,
    "^": true,
    "|": true,
    "&&": true,
    "||": true,
    ",": true,
    ".": true,
    "=": true,
    "*=": true,
    "/=": true,
    "%=": true,
    "+=": true,
    "-=": true,
    "<<=": true,
    ">>=": true,
    ">>>=": true,
    "&=": true,
    "^=": true,
    "|=": true,
    // Unary operators
    "delete": true,
    "void": true,
    "typeof": true,
    "~": true,
    "!": true,
    "new": true,
    // Function calls and grouped expressions
    "(": true
  };

  // If any of these tokens are on a line after the token before it, we know
  // that ASI cannot happen.
  var PREVENT_ASI_BEFORE_TOKENS = {
    // Binary operators
    "*": true,
    "/": true,
    "%": true,
    "<<": true,
    ">>": true,
    ">>>": true,
    "<": true,
    ">": true,
    "<=": true,
    ">=": true,
    "instanceof": true,
    "in": true,
    "==": true,
    "!=": true,
    "===": true,
    "!==": true,
    "&": true,
    "^": true,
    "|": true,
    "&&": true,
    "||": true,
    ",": true,
    ".": true,
    "=": true,
    "*=": true,
    "/=": true,
    "%=": true,
    "+=": true,
    "-=": true,
    "<<=": true,
    ">>=": true,
    ">>>=": true,
    "&=": true,
    "^=": true,
    "|=": true,
    // Function calls
    "(": true
  };

  /**
   * Determines if Automatic Semicolon Insertion (ASI) occurs between these
   * tokens.
   *
   * @param Object token
   *        The current token.
   * @param Object lastToken
   *        The last token we added to the pretty printed results.
   *
   * @returns Boolean
   *          True if we believe ASI occurs.
   */
  function isASI(token, lastToken) {
    if (!lastToken) {
      return false;
    }
    if (token.loc.start.line === lastToken.loc.start.line) {
      return false;
    }
    if (PREVENT_ASI_AFTER_TOKENS[
      lastToken.type.label || lastToken.type.keyword
    ]) {
      return false;
    }
    if (PREVENT_ASI_BEFORE_TOKENS[token.type.label || token.type.keyword]) {
      return false;
    }
    return true;
  }

  /**
   * Determine if we have encountered a getter or setter.
   *
   * @param Object token
   *        The current token. If this is a getter or setter, it would be the
   *        property name.
   * @param Object lastToken
   *        The last token we added to the pretty printed results. If this is a
   *        getter or setter, it would be the `get` or `set` keyword
   *        respectively.
   * @param Array stack
   *        The stack of open parens/curlies/brackets/etc.
   *
   * @returns Boolean
   *          True if this is a getter or setter.
   */
  function isGetterOrSetter(token, lastToken, stack) {
    return stack[stack.length - 1] == "{"
      && lastToken
      && lastToken.type.label == "name"
      && (lastToken.value == "get" || lastToken.value == "set")
      && token.type.label == "name";
  }

  /**
   * Determine if we should add a newline after the given token.
   *
   * @param Object token
   *        The token we are looking at.
   * @param Array stack
   *        The stack of open parens/curlies/brackets/etc.
   *
   * @returns Boolean
   *          True if we should add a newline.
   */
  function isLineDelimiter(token, stack) {
    if (token.isArrayLiteral) {
      return true;
    }
    var ttl = token.type.label;
    var top = stack[stack.length - 1];
    return ttl == ";" && top != "("
      || ttl == "{"
      || ttl == "," && top != "("
      || ttl == ":" && (top == "case" || top == "default");
  }

  /**
   * Append the necessary whitespace to the result after we have added the given
   * token.
   *
   * @param Object token
   *        The token that was just added to the result.
   * @param Function write
   *        The function to write to the pretty printed results.
   * @param Array stack
   *        The stack of open parens/curlies/brackets/etc.
   *
   * @returns Boolean
   *          Returns true if we added a newline to result, false in all other
   *          cases.
   */
  function appendNewline(token, write, stack) {
    if (isLineDelimiter(token, stack)) {
      write("\n", token.loc.start.line, token.loc.start.column);
      return true;
    }
    return false;
  }

  /**
   * Determines if we need to add a space between the last token we added and
   * the token we are about to add.
   *
   * @param Object token
   *        The token we are about to add to the pretty printed code.
   * @param Object lastToken
   *        The last token added to the pretty printed code.
   */
  function needsSpaceAfter(token, lastToken) {
    if (lastToken) {
      if (lastToken.type.isLoop) {
        return true;
      }
      if (lastToken.type.isAssign) {
        return true;
      }
      if (lastToken.type.binop != null) {
        return true;
      }

      var ltt = lastToken.type.label;
      if (ltt == "?") {
        return true;
      }
      if (ltt == ":") {
        return true;
      }
      if (ltt == ",") {
        return true;
      }
      if (ltt == ";") {
        return true;
      }

      var ltk = lastToken.type.keyword;
      if (ltk != null) {
        if (ltk == "break" || ltk == "continue" || ltk == "return") {
          return token.type.label != ";";
        }
        if (ltk != "debugger"
            && ltk != "null"
            && ltk != "true"
            && ltk != "false"
            && ltk != "this"
            && ltk != "default") {
          return true;
        }
      }

      if (ltt == ")" && (token.type.label != ")"
                         && token.type.label != "]"
                         && token.type.label != ";"
                         && token.type.label != ","
                         && token.type.label != ".")) {
        return true;
      }

      if (lastToken.value == "let") {
        return true;
      }

      if (lastToken.value == "const") {
        return true;
      }
    }

    if (token.type.isAssign) {
      return true;
    }
    if (token.type.binop != null) {
      return true;
    }
    if (token.type.label == "?") {
      return true;
    }

    return false;
  }

  /**
   * Add the required whitespace before this token, whether that is a single
   * space, newline, and/or the indent on fresh lines.
   *
   * @param Object token
   *        The token we are about to add to the pretty printed code.
   * @param Object lastToken
   *        The last token we added to the pretty printed code.
   * @param Boolean addedNewline
   *        Whether we added a newline after adding the last token to the pretty
   *        printed code.
   * @param Function write
   *        The function to write pretty printed code to the result SourceNode.
   * @param Object options
   *        The options object.
   * @param Number indentLevel
   *        The number of indents deep we are.
   * @param Array stack
   *        The stack of open curlies, brackets, etc.
   */
  function prependWhiteSpace(token, lastToken, addedNewline, write, options,
                             indentLevel, stack) {
    var ttk = token.type.keyword;
    var ttl = token.type.label;
    var newlineAdded = addedNewline;
    var ltt = lastToken ? lastToken.type.label : null;

    // Handle whitespace and newlines after "}" here instead of in
    // `isLineDelimiter` because it is only a line delimiter some of the
    // time. For example, we don't want to put "else if" on a new line after
    // the first if's block.
    if (lastToken && ltt == "}") {
      if (ttk == "while" && stack[stack.length - 1] == "do") {
        write(" ",
              lastToken.loc.start.line,
              lastToken.loc.start.column);
      } else if (ttk == "else" ||
                 ttk == "catch" ||
                 ttk == "finally") {
        write(" ",
              lastToken.loc.start.line,
              lastToken.loc.start.column);
      } else if (ttl != "(" &&
                 ttl != ";" &&
                 ttl != "," &&
                 ttl != ")" &&
                 ttl != ".") {
        write("\n",
              lastToken.loc.start.line,
              lastToken.loc.start.column);
        newlineAdded = true;
      }
    }

    if (isGetterOrSetter(token, lastToken, stack)) {
      write(" ",
            lastToken.loc.start.line,
            lastToken.loc.start.column);
    }

    if (ttl == ":" && stack[stack.length - 1] == "?") {
      write(" ",
            lastToken.loc.start.line,
            lastToken.loc.start.column);
    }

    if (lastToken && ltt != "}" && ttk == "else") {
      write(" ",
            lastToken.loc.start.line,
            lastToken.loc.start.column);
    }

    function ensureNewline() {
      if (!newlineAdded) {
        write("\n",
              lastToken.loc.start.line,
              lastToken.loc.start.column);
        newlineAdded = true;
      }
    }

    if (isASI(token, lastToken)) {
      ensureNewline();
    }

    if (decrementsIndent(ttl, stack)) {
      ensureNewline();
    }

    if (newlineAdded) {
      if (ttk == "case" || ttk == "default") {
        write(repeat(options.indent, indentLevel - 1),
              token.loc.start.line,
              token.loc.start.column);
      } else {
        write(repeat(options.indent, indentLevel),
              token.loc.start.line,
              token.loc.start.column);
      }
    } else if (needsSpaceAfter(token, lastToken)) {
      write(" ",
            lastToken.loc.start.line,
            lastToken.loc.start.column);
    }
  }

  /**
   * Repeat the `str` string `n` times.
   *
   * @param String str
   *        The string to be repeated.
   * @param Number n
   *        The number of times to repeat the string.
   *
   * @returns String
   *          The repeated string.
   */
  function repeat(str, n) {
    var result = "";
    while (n > 0) {
      if (n & 1) {
        result += str;
      }
      n >>= 1;
      str += str;
    }
    return result;
  }

  /**
   * Make sure that we output the escaped character combination inside string
   * literals instead of various problematic characters.
   */
  var sanitize = (function () {
    var escapeCharacters = {
      // Backslash
      "\\": "\\\\",
      // Newlines
      "\n": "\\n",
      // Carriage return
      "\r": "\\r",
      // Tab
      "\t": "\\t",
      // Vertical tab
      "\v": "\\v",
      // Form feed
      "\f": "\\f",
      // Null character
      "\0": "\\0",
      // Single quotes
      "'": "\\'"
    };

    var regExpString = "("
      + Object.keys(escapeCharacters)
              .map(function (c) { return escapeCharacters[c]; })
              .join("|")
      + ")";
    var escapeCharactersRegExp = new RegExp(regExpString, "g");

    return function (str) {
      return str.replace(escapeCharactersRegExp, function (_, c) {
        return escapeCharacters[c];
      });
    };
  }());
  /**
   * Add the given token to the pretty printed results.
   *
   * @param Object token
   *        The token to add.
   * @param Function write
   *        The function to write pretty printed code to the result SourceNode.
   */
  function addToken(token, write) {
    if (token.type.label == "string") {
      write("'" + sanitize(token.value) + "'",
            token.loc.start.line,
            token.loc.start.column);
    } else if (token.type.label == "regexp") {
      write(String(token.value.value),
            token.loc.start.line,
            token.loc.start.column);
    } else {
      write(String(token.value != null ? token.value : token.type.label),
            token.loc.start.line,
            token.loc.start.column);
    }
  }

  /**
   * Returns true if the given token type belongs on the stack.
   */
  function belongsOnStack(token) {
    var ttl = token.type.label;
    var ttk = token.type.keyword;
    return ttl == "{"
      || ttl == "("
      || ttl == "["
      || ttl == "?"
      || ttk == "do"
      || ttk == "switch"
      || ttk == "case"
      || ttk == "default";
  }

  /**
   * Returns true if the given token should cause us to pop the stack.
   */
  function shouldStackPop(token, stack) {
    var ttl = token.type.label;
    var ttk = token.type.keyword;
    var top = stack[stack.length - 1];
    return ttl == "]"
      || ttl == ")"
      || ttl == "}"
      || (ttl == ":" && (top == "case" || top == "default" || top == "?"))
      || (ttk == "while" && top == "do");
  }

  /**
   * Returns true if the given token type should cause us to decrement the
   * indent level.
   */
  function decrementsIndent(tokenType, stack) {
    return tokenType == "}"
      || (tokenType == "]" && stack[stack.length - 1] == "[\n");
  }

  /**
   * Returns true if the given token should cause us to increment the indent
   * level.
   */
  function incrementsIndent(token) {
    return token.type.label == "{"
      || token.isArrayLiteral
      || token.type.keyword == "switch";
  }

  /**
   * Add a comment to the pretty printed code.
   *
   * @param Function write
   *        The function to write pretty printed code to the result SourceNode.
   * @param Number indentLevel
   *        The number of indents deep we are.
   * @param Object options
   *        The options object.
   * @param Boolean block
   *        True if the comment is a multiline block style comment.
   * @param String text
   *        The text of the comment.
   * @param Number line
   *        The line number to comment appeared on.
   * @param Number column
   *        The column number the comment appeared on.
   */
  function addComment(write, indentLevel, options, block, text, line, column) {
    var indentString = repeat(options.indent, indentLevel);

    write(indentString, line, column);
    if (block) {
      write("/*");
      write(text
            .split(new RegExp("/\n" + indentString + "/", "g"))
            .join("\n" + indentString));
      write("*/");
    } else {
      write("//");
      write(text);
    }
    write("\n");
  }

  /**
   * The main function.
   *
   * @param String input
   *        The ugly JS code we want to pretty print.
   * @param Object options
   *        The options object. Provides configurability of the pretty
   *        printing. Properties:
   *          - url: The URL string of the ugly JS code.
   *          - indent: The string to indent code by.
   *
   * @returns Object
   *          An object with the following properties:
   *            - code: The pretty printed code string.
   *            - map: A SourceMapGenerator instance.
   */
  return function prettyFast(input, options) {
    // The level of indents deep we are.
    var indentLevel = 0;

    // We will accumulate the pretty printed code in this SourceNode.
    var result = new SourceNode();

    /**
     * Write a pretty printed string to the result SourceNode.
     *
     * We buffer our writes so that we only create one mapping for each line in
     * the source map. This enhances performance by avoiding extraneous mapping
     * serialization, and flattening the tree that
     * `SourceNode#toStringWithSourceMap` will have to recursively walk. When
     * timing how long it takes to pretty print jQuery, this optimization
     * brought the time down from ~390 ms to ~190ms!
     *
     * @param String str
     *        The string to be added to the result.
     * @param Number line
     *        The line number the string came from in the ugly source.
     * @param Number column
     *        The column number the string came from in the ugly source.
     */
    var write = (function () {
      var buffer = [];
      var bufferLine = -1;
      var bufferColumn = -1;
      return function write(str, line, column) {
        if (line != null && bufferLine === -1) {
          bufferLine = line;
        }
        if (column != null && bufferColumn === -1) {
          bufferColumn = column;
        }
        buffer.push(str);

        if (str == "\n") {
          var lineStr = "";
          for (var i = 0, len = buffer.length; i < len; i++) {
            lineStr += buffer[i];
          }
          result.add(new SourceNode(bufferLine, bufferColumn, options.url,
                                    lineStr));
          buffer.splice(0, buffer.length);
          bufferLine = -1;
          bufferColumn = -1;
        }
      };
    }());

    // Whether or not we added a newline on after we added the last token.
    var addedNewline = false;

    // The current token we will be adding to the pretty printed code.
    var token;

    // Shorthand for token.type.label, so we don't have to repeatedly access
    // properties.
    var ttl;

    // Shorthand for token.type.keyword, so we don't have to repeatedly access
    // properties.
    var ttk;

    // The last token we added to the pretty printed code.
    var lastToken;

    // Stack of token types/keywords that can affect whether we want to add a
    // newline or a space. We can make that decision based on what token type is
    // on the top of the stack. For example, a comma in a parameter list should
    // be followed by a space, while a comma in an object literal should be
    // followed by a newline.
    //
    // Strings that go on the stack:
    //
    //   - "{"
    //   - "("
    //   - "["
    //   - "[\n"
    //   - "do"
    //   - "?"
    //   - "switch"
    //   - "case"
    //   - "default"
    //
    // The difference between "[" and "[\n" is that "[\n" is used when we are
    // treating "[" and "]" tokens as line delimiters and should increment and
    // decrement the indent level when we find them.
    var stack = [];

    // Pass through acorn's tokenizer and append tokens and comments into a
    // single queue to process.  For example, the source file:
    //
    //     foo
    //     // a
    //     // b
    //     bar
    //
    // After this process, tokenQueue has the following token stream:
    //
    //     [ foo, '// a', '// b', bar]
    var tokenQueue = [];

    var tokens = acorn.tokenizer(input, {
      locations: true,
      sourceFile: options.url,
      onComment: function (block, text, start, end, startLoc, endLoc) {
        tokenQueue.push({
          type: {},
          comment: true,
          block: block,
          text: text,
          loc: { start: startLoc, end: endLoc }
        });
      }
    });

    for (;;) {
      token = tokens.getToken();
      tokenQueue.push(token);
      if (token.type.label == "eof") {
        break;
      }
    }

    for (var i = 0; i < tokenQueue.length; i++) {
      token = tokenQueue[i];

      if (token.comment) {
        var commentIndentLevel = indentLevel;
        if (lastToken && (lastToken.loc.end.line == token.loc.start.line)) {
          commentIndentLevel = 0;
          write(" ");
        }
        addComment(write, commentIndentLevel, options, token.block, token.text,
                   token.loc.start.line, token.loc.start.column);
        addedNewline = true;
        continue;
      }

      ttk = token.type.keyword;
      ttl = token.type.label;

      if (ttl == "eof") {
        if (!addedNewline) {
          write("\n");
        }
        break;
      }

      token.isArrayLiteral = isArrayLiteral(token, lastToken);

      if (belongsOnStack(token)) {
        if (token.isArrayLiteral) {
          stack.push("[\n");
        } else {
          stack.push(ttl || ttk);
        }
      }

      if (decrementsIndent(ttl, stack)) {
        indentLevel--;
        if (ttl == "}"
            && stack.length > 1
            && stack[stack.length - 2] == "switch") {
          indentLevel--;
        }
      }

      prependWhiteSpace(token, lastToken, addedNewline, write, options,
                        indentLevel, stack);
      addToken(token, write);

      // If the next token is going to be a comment starting on the same line,
      // then no need to add one here
      var nextToken = tokenQueue[i + 1];
      if (!nextToken || !nextToken.comment || token.loc.end.line != nextToken.loc.start.line) {
        addedNewline = appendNewline(token, write, stack);
      }

      if (shouldStackPop(token, stack)) {
        stack.pop();
        if (token == "}" && stack.length
            && stack[stack.length - 1] == "switch") {
          stack.pop();
        }
      }

      if (incrementsIndent(token)) {
        indentLevel++;
      }

      // Acorn's tokenizer re-uses tokens, so we have to copy the last token on
      // every iteration. We follow acorn's lead here, and reuse the lastToken
      // object the same way that acorn reuses the token object. This allows us
      // to avoid allocations and minimize GC pauses.
      if (!lastToken) {
        lastToken = { loc: { start: {}, end: {} } };
      }
      lastToken.start = token.start;
      lastToken.end = token.end;
      lastToken.loc.start.line = token.loc.start.line;
      lastToken.loc.start.column = token.loc.start.column;
      lastToken.loc.end.line = token.loc.end.line;
      lastToken.loc.end.column = token.loc.end.column;
      lastToken.type = token.type;
      lastToken.value = token.value;
      lastToken.isArrayLiteral = token.isArrayLiteral;
    }

    return result.toStringWithSourceMap({ file: options.url });
  };

}.bind(this)));


/***/ }),

/***/ 815:
/***/ (function(module, exports, __webpack_require__) {

/*
 * 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 = __webpack_require__(816).SourceMapGenerator;
exports.SourceMapConsumer = __webpack_require__(822).SourceMapConsumer;
exports.SourceNode = __webpack_require__(825).SourceNode;


/***/ }),

/***/ 816:
/***/ (function(module, exports, __webpack_require__) {

/* -*- 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
 */

var base64VLQ = __webpack_require__(817);
var util = __webpack_require__(819);
var ArraySet = __webpack_require__(820).ArraySet;
var MappingList = __webpack_require__(821).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.
 */
function SourceMapGenerator(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;
}

SourceMapGenerator.prototype._version = 3;

/**
 * Creates a new SourceMapGenerator based on a SourceMapConsumer
 *
 * @param aSourceMapConsumer The SourceMap.
 */
SourceMapGenerator.fromSourceMap =
  function SourceMapGenerator_fromSourceMap(aSourceMapConsumer) {
    var sourceRoot = aSourceMapConsumer.sourceRoot;
    var generator = new SourceMapGenerator({
      file: aSourceMapConsumer.file,
      sourceRoot: sourceRoot
    });
    aSourceMapConsumer.eachMapping(function (mapping) {
      var 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) {
      var 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.
 */
SourceMapGenerator.prototype.addMapping =
  function SourceMapGenerator_addMapping(aArgs) {
    var generated = util.getArg(aArgs, 'generated');
    var original = util.getArg(aArgs, 'original', null);
    var source = util.getArg(aArgs, 'source', null);
    var 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 != null && original.line,
      originalColumn: original != null && original.column,
      source: source,
      name: name
    });
  };

/**
 * Set the source content for a source file.
 */
SourceMapGenerator.prototype.setSourceContent =
  function SourceMapGenerator_setSourceContent(aSourceFile, aSourceContent) {
    var 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.
 */
SourceMapGenerator.prototype.applySourceMap =
  function SourceMapGenerator_applySourceMap(aSourceMapConsumer, aSourceFile, aSourceMapPath) {
    var 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;
    }
    var 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.
    var newSources = new ArraySet();
    var 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.
        var 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;
          }
        }
      }

      var source = mapping.source;
      if (source != null && !newSources.has(source)) {
        newSources.add(source);
      }

      var 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 (sourceFile) {
      var content = aSourceMapConsumer.sourceContentFor(sourceFile);
      if (content != null) {
        if (aSourceMapPath != null) {
          sourceFile = util.join(aSourceMapPath, sourceFile);
        }
        if (sourceRoot != null) {
          sourceFile = util.relative(sourceRoot, sourceFile);
        }
        this.setSourceContent(sourceFile, 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.
 */
SourceMapGenerator.prototype._validateMapping =
  function SourceMapGenerator_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.
      return;
    }
    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.
      return;
    }
    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.
 */
SourceMapGenerator.prototype._serializeMappings =
  function SourceMapGenerator_serializeMappings() {
    var previousGeneratedColumn = 0;
    var previousGeneratedLine = 1;
    var previousOriginalColumn = 0;
    var previousOriginalLine = 0;
    var previousName = 0;
    var previousSource = 0;
    var result = '';
    var next;
    var mapping;
    var nameIdx;
    var sourceIdx;

    var mappings = this._mappings.toArray();
    for (var 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;
  };

SourceMapGenerator.prototype._generateSourcesContent =
  function SourceMapGenerator_generateSourcesContent(aSources, aSourceRoot) {
    return aSources.map(function (source) {
      if (!this._sourcesContents) {
        return null;
      }
      if (aSourceRoot != null) {
        source = util.relative(aSourceRoot, source);
      }
      var key = util.toSetString(source);
      return Object.prototype.hasOwnProperty.call(this._sourcesContents, key)
        ? this._sourcesContents[key]
        : null;
    }, this);
  };

/**
 * Externalize the source map.
 */
SourceMapGenerator.prototype.toJSON =
  function SourceMapGenerator_toJSON() {
    var 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.
 */
SourceMapGenerator.prototype.toString =
  function SourceMapGenerator_toString() {
    return JSON.stringify(this.toJSON());
  };

exports.SourceMapGenerator = SourceMapGenerator;


/***/ }),

/***/ 817:
/***/ (function(module, exports, __webpack_require__) {

/* -*- 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.
 */

var base64 = __webpack_require__(818);

// 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

var VLQ_BASE_SHIFT = 5;

// binary: 100000
var VLQ_BASE = 1 << VLQ_BASE_SHIFT;

// binary: 011111
var VLQ_BASE_MASK = VLQ_BASE - 1;

// binary: 100000
var 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;
}

/**
 * Converts to a two-complement value from a value where the sign bit is
 * placed in the least significant bit.  For example, as decimals:
 *   2 (10 binary) becomes 1, 3 (11 binary) becomes -1
 *   4 (100 binary) becomes 2, 5 (101 binary) becomes -2
 */
function fromVLQSigned(aValue) {
  var isNegative = (aValue & 1) === 1;
  var shifted = aValue >> 1;
  return isNegative
    ? -shifted
    : shifted;
}

/**
 * Returns the base 64 VLQ encoded value.
 */
exports.encode = function base64VLQ_encode(aValue) {
  var encoded = "";
  var digit;

  var 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;
};

/**
 * Decodes the next base 64 VLQ value from the given string and returns the
 * value and the rest of the string via the out parameter.
 */
exports.decode = function base64VLQ_decode(aStr, aIndex, aOutParam) {
  var strLen = aStr.length;
  var result = 0;
  var shift = 0;
  var continuation, digit;

  do {
    if (aIndex >= strLen) {
      throw new Error("Expected more digits in base 64 VLQ value.");
    }

    digit = base64.decode(aStr.charCodeAt(aIndex++));
    if (digit === -1) {
      throw new Error("Invalid base64 digit: " + aStr.charAt(aIndex - 1));
    }

    continuation = !!(digit & VLQ_CONTINUATION_BIT);
    digit &= VLQ_BASE_MASK;
    result = result + (digit << shift);
    shift += VLQ_BASE_SHIFT;
  } while (continuation);

  aOutParam.value = fromVLQSigned(result);
  aOutParam.rest = aIndex;
};


/***/ }),

/***/ 818:
/***/ (function(module, exports) {

/* -*- 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
 */

var 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);
};

/**
 * Decode a single base 64 character code digit to an integer. Returns -1 on
 * failure.
 */
exports.decode = function (charCode) {
  var bigA = 65;     // 'A'
  var bigZ = 90;     // 'Z'

  var littleA = 97;  // 'a'
  var littleZ = 122; // 'z'

  var zero = 48;     // '0'
  var nine = 57;     // '9'

  var plus = 43;     // '+'
  var slash = 47;    // '/'

  var littleOffset = 26;
  var numberOffset = 52;

  // 0 - 25: ABCDEFGHIJKLMNOPQRSTUVWXYZ
  if (bigA <= charCode && charCode <= bigZ) {
    return (charCode - bigA);
  }

  // 26 - 51: abcdefghijklmnopqrstuvwxyz
  if (littleA <= charCode && charCode <= littleZ) {
    return (charCode - littleA + littleOffset);
  }

  // 52 - 61: 0123456789
  if (zero <= charCode && charCode <= nine) {
    return (charCode - zero + numberOffset);
  }

  // 62: +
  if (charCode == plus) {
    return 62;
  }

  // 63: /
  if (charCode == slash) {
    return 63;
  }

  // Invalid base64 digit.
  return -1;
};


/***/ }),

/***/ 819:
/***/ (function(module, exports) {

/* -*- 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
 */

/**
 * 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;
  } else {
    throw new Error('"' + aName + '" is a required argument.');
  }
}
exports.getArg = getArg;

var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.]*)(?::(\d+))?(\S*)$/;
var dataUrlRegexp = /^data:.+\,.+$/;

function urlParse(aUrl) {
  var match = aUrl.match(urlRegexp);
  if (!match) {
    return null;
  }
  return {
    scheme: match[1],
    auth: match[2],
    host: match[3],
    port: match[4],
    path: match[5]
  };
}
exports.urlParse = urlParse;

function urlGenerate(aParsedUrl) {
  var url = '';
  if (aParsedUrl.scheme) {
    url += aParsedUrl.scheme + ':';
  }
  url += '//';
  if (aParsedUrl.auth) {
    url += aParsedUrl.auth + '@';
  }
  if (aParsedUrl.host) {
    url += aParsedUrl.host;
  }
  if (aParsedUrl.port) {
    url += ":" + aParsedUrl.port
  }
  if (aParsedUrl.path) {
    url += aParsedUrl.path;
  }
  return url;
}
exports.urlGenerate = urlGenerate;

/**
 * Normalizes a path, or the path portion of a URL:
 *
 * - Replaces consecutive slashes with one slash.
 * - Removes unnecessary '.' parts.
 * - Removes unnecessary '<dir>/..' parts.
 *
 * Based on code in the Node.js 'path' core module.
 *
 * @param aPath The path or url to normalize.
 */
function normalize(aPath) {
  var path = aPath;
  var url = urlParse(aPath);
  if (url) {
    if (!url.path) {
      return aPath;
    }
    path = url.path;
  }
  var isAbsolute = exports.isAbsolute(path);

  var parts = path.split(/\/+/);
  for (var part, up = 0, i = parts.length - 1; i >= 0; i--) {
    part = parts[i];
    if (part === '.') {
      parts.splice(i, 1);
    } else if (part === '..') {
      up++;
    } else if (up > 0) {
      if (part === '') {
        // The first part is blank if the path is absolute. Trying to go
        // above the root is a no-op. Therefore we can remove all '..' parts
        // directly after the root.
        parts.splice(i + 1, up);
        up = 0;
      } else {
        parts.splice(i, 2);
        up--;
      }
    }
  }
  path = parts.join('/');

  if (path === '') {
    path = isAbsolute ? '/' : '.';
  }

  if (url) {
    url.path = path;
    return urlGenerate(url);
  }
  return path;
}
exports.normalize = normalize;

/**
 * Joins two paths/URLs.
 *
 * @param aRoot The root path or URL.
 * @param aPath The path or URL to be joined with the root.
 *
 * - If aPath is a URL or a data URI, aPath is returned, unless aPath is a
 *   scheme-relative URL: Then the scheme of aRoot, if any, is prepended
 *   first.
 * - Otherwise aPath is a path. If aRoot is a URL, then its path portion
 *   is updated with the result and aRoot is returned. Otherwise the result
 *   is returned.
 *   - If aPath is absolute, the result is aPath.
 *   - Otherwise the two paths are joined with a slash.
 * - Joining for example 'http://' and 'www.example.com' is also supported.
 */
function join(aRoot, aPath) {
  if (aRoot === "") {
    aRoot = ".";
  }
  if (aPath === "") {
    aPath = ".";
  }
  var aPathUrl = urlParse(aPath);
  var aRootUrl = urlParse(aRoot);
  if (aRootUrl) {
    aRoot = aRootUrl.path || '/';
  }

  // `join(foo, '//www.example.org')`
  if (aPathUrl && !aPathUrl.scheme) {
    if (aRootUrl) {
      aPathUrl.scheme = aRootUrl.scheme;
    }
    return urlGenerate(aPathUrl);
  }

  if (aPathUrl || aPath.match(dataUrlRegexp)) {
    return aPath;
  }

  // `join('http://', 'www.example.com')`
  if (aRootUrl && !aRootUrl.host && !aRootUrl.path) {
    aRootUrl.host = aPath;
    return urlGenerate(aRootUrl);
  }

  var joined = aPath.charAt(0) === '/'
    ? aPath
    : normalize(aRoot.replace(/\/+$/, '') + '/' + aPath);

  if (aRootUrl) {
    aRootUrl.path = joined;
    return urlGenerate(aRootUrl);
  }
  return joined;
}
exports.join = join;

exports.isAbsolute = function (aPath) {
  return aPath.charAt(0) === '/' || !!aPath.match(urlRegexp);
};

/**
 * Make a path relative to a URL or another path.
 *
 * @param aRoot The root path or URL.
 * @param aPath The path or URL to be made relative to aRoot.
 */
function relative(aRoot, aPath) {
  if (aRoot === "") {
    aRoot = ".";
  }

  aRoot = aRoot.replace(/\/$/, '');

  // It is possible for the path to be above the root. In this case, simply
  // checking whether the root is a prefix of the path won't work. Instead, we
  // need to remove components from the root one by one, until either we find
  // a prefix that fits, or we run out of components to remove.
  var level = 0;
  while (aPath.indexOf(aRoot + '/') !== 0) {
    var index = aRoot.lastIndexOf("/");
    if (index < 0) {
      return aPath;
    }

    // If the only part of the root that is left is the scheme (i.e. http://,
    // file:///, etc.), one or more slashes (/), or simply nothing at all, we
    // have exhausted all components, so the path is not relative to the root.
    aRoot = aRoot.slice(0, index);
    if (aRoot.match(/^([^\/]+:\/)?\/*$/)) {
      return aPath;
    }

    ++level;
  }

  // Make sure we add a "../" for each component we removed from the root.
  return Array(level + 1).join("../") + aPath.substr(aRoot.length + 1);
}
exports.relative = relative;

var supportsNullProto = (function () {
  var 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;
  }

  var length = s.length;

  if (length < 9 /* "__proto__".length */) {
    return false;
  }

  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;
  }

  for (var i = length - 10; i >= 0; i--) {
    if (s.charCodeAt(i) !== 36 /* '$' */) {
      return false;
    }
  }

  return true;
}

/**
 * Comparator between two mappings where the original positions are compared.
 *
 * Optionally pass in `true` as `onlyCompareGenerated` to consider two
 * mappings with the same original source/line/column, but different generated
 * line and column the same. Useful when searching for a mapping with a
 * stubbed out mapping.
 */
function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) {
  var cmp = 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 || onlyCompareOriginal) {
    return cmp;
  }

  cmp = mappingA.generatedColumn - mappingB.generatedColumn;
  if (cmp !== 0) {
    return cmp;
  }

  cmp = mappingA.generatedLine - mappingB.generatedLine;
  if (cmp !== 0) {
    return cmp;
  }

  return mappingA.name - mappingB.name;
}
exports.compareByOriginalPositions = compareByOriginalPositions;

/**
 * Comparator between two mappings with deflated source and name indices where
 * the generated positions are compared.
 *
 * Optionally pass in `true` as `onlyCompareGenerated` to consider two
 * mappings with the same generated line and column, but different
 * source/name/original line and column the same. Useful when searching for a
 * mapping with a stubbed out mapping.
 */
function compareByGeneratedPositionsDeflated(mappingA, mappingB, onlyCompareGenerated) {
  var cmp = mappingA.generatedLine - mappingB.generatedLine;
  if (cmp !== 0) {
    return cmp;
  }

  cmp = mappingA.generatedColumn - mappingB.generatedColumn;
  if (cmp !== 0 || onlyCompareGenerated) {
    return cmp;
  }

  cmp = 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 mappingA.name - mappingB.name;
}
exports.compareByGeneratedPositionsDeflated = compareByGeneratedPositionsDeflated;

function strcmp(aStr1, aStr2) {
  if (aStr1 === aStr2) {
    return 0;
  }

  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) {
  var 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;


/***/ }),

/***/ 820:
/***/ (function(module, exports, __webpack_require__) {

/* -*- 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
 */

var util = __webpack_require__(819);
var has = Object.prototype.hasOwnProperty;
var hasNativeMap = typeof Map !== "undefined";

/**
 * 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.
 */
function ArraySet() {
  this._array = [];
  this._set = hasNativeMap ? new Map() : Object.create(null);
}

/**
 * Static method for creating ArraySet instances from an existing array.
 */
ArraySet.fromArray = function ArraySet_fromArray(aArray, aAllowDuplicates) {
  var set = new ArraySet();
  for (var 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
 */
ArraySet.prototype.size = function ArraySet_size() {
  return hasNativeMap ? this._set.size : Object.getOwnPropertyNames(this._set).length;
};

/**
 * Add the given string to this set.
 *
 * @param String aStr
 */
ArraySet.prototype.add = function ArraySet_add(aStr, aAllowDuplicates) {
  var sStr = hasNativeMap ? aStr : util.toSetString(aStr);
  var isDuplicate = hasNativeMap ? this.has(aStr) : has.call(this._set, sStr);
  var idx = this._array.length;
  if (!isDuplicate || aAllowDuplicates) {
    this._array.push(aStr);
  }
  if (!isDuplicate) {
    if (hasNativeMap) {
      this._set.set(aStr, idx);
    } else {
      this._set[sStr] = idx;
    }
  }
};

/**
 * Is the given string a member of this set?
 *
 * @param String aStr
 */
ArraySet.prototype.has = function ArraySet_has(aStr) {
  if (hasNativeMap) {
    return this._set.has(aStr);
  } else {
    var sStr = util.toSetString(aStr);
    return has.call(this._set, sStr);
  }
};

/**
 * What is the index of the given string in the array?
 *
 * @param String aStr
 */
ArraySet.prototype.indexOf = function ArraySet_indexOf(aStr) {
  if (hasNativeMap) {
    var idx = this._set.get(aStr);
    if (idx >= 0) {
        return idx;
    }
  } else {
    var sStr = util.toSetString(aStr);
    if (has.call(this._set, sStr)) {
      return this._set[sStr];
    }
  }

  throw new Error('"' + aStr + '" is not in the set.');
};

/**
 * What is the element at the given index?
 *
 * @param Number aIdx
 */
ArraySet.prototype.at = function ArraySet_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.
 */
ArraySet.prototype.toArray = function ArraySet_toArray() {
  return this._array.slice();
};

exports.ArraySet = ArraySet;


/***/ }),

/***/ 821:
/***/ (function(module, exports, __webpack_require__) {

/* -*- 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
 */

var util = __webpack_require__(819);

/**
 * Determine whether mappingB is after mappingA with respect to generated
 * position.
 */
function generatedPositionAfter(mappingA, mappingB) {
  // Optimized for most common case
  var lineA = mappingA.generatedLine;
  var lineB = mappingB.generatedLine;
  var columnA = mappingA.generatedColumn;
  var 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 neglibable overhead in general
 * case for a large speedup in case of mappings being added in order.
 */
function MappingList() {
  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.
 */
MappingList.prototype.unsortedForEach =
  function MappingList_forEach(aCallback, aThisArg) {
    this._array.forEach(aCallback, aThisArg);
  };

/**
 * Add the given source mapping.
 *
 * @param Object aMapping
 */
MappingList.prototype.add = function MappingList_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.
 */
MappingList.prototype.toArray = function MappingList_toArray() {
  if (!this._sorted) {
    this._array.sort(util.compareByGeneratedPositionsInflated);
    this._sorted = true;
  }
  return this._array;
};

exports.MappingList = MappingList;


/***/ }),

/***/ 822:
/***/ (function(module, exports, __webpack_require__) {

/* -*- 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
 */

var util = __webpack_require__(819);
var binarySearch = __webpack_require__(823);
var ArraySet = __webpack_require__(820).ArraySet;
var base64VLQ = __webpack_require__(817);
var quickSort = __webpack_require__(824).quickSort;

function SourceMapConsumer(aSourceMap) {
  var sourceMap = aSourceMap;
  if (typeof aSourceMap === 'string') {
    sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
  }

  return sourceMap.sections != null
    ? new IndexedSourceMapConsumer(sourceMap)
    : new BasicSourceMapConsumer(sourceMap);
}

SourceMapConsumer.fromSourceMap = function(aSourceMap) {
  return BasicSourceMapConsumer.fromSourceMap(aSourceMap);
}

/**
 * The version of the source mapping spec that we are consuming.
 */
SourceMapConsumer.prototype._version = 3;

// `__generatedMappings` and `__originalMappings` are arrays that hold the
// parsed mapping coordinates from the source map's "mappings" attribute. They
// are lazily instantiated, accessed via the `_generatedMappings` and
// `_originalMappings` getters respectively, and we only parse the mappings
// and create these arrays once queried for a source location. We jump through
// these hoops because there can be many thousands of mappings, and parsing
// them is expensive, so we only want to do it if we must.
//
// Each object in the arrays is of the form:
//
//     {
//       generatedLine: The line number in the generated code,
//       generatedColumn: The column number in the generated code,
//       source: The path to the original source file that generated this
//               chunk of code,
//       originalLine: The line number in the original source that
//                     corresponds to this chunk of generated code,
//       originalColumn: The column number in the original source that
//                       corresponds to this chunk of generated code,
//       name: The name of the original symbol which generated this chunk of
//             code.
//     }
//
// All properties except for `generatedLine` and `generatedColumn` can be
// `null`.
//
// `_generatedMappings` is ordered by the generated positions.
//
// `_originalMappings` is ordered by the original positions.

SourceMapConsumer.prototype.__generatedMappings = null;
Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', {
  get: function () {
    if (!this.__generatedMappings) {
      this._parseMappings(this._mappings, this.sourceRoot);
    }

    return this.__generatedMappings;
  }
});

SourceMapConsumer.prototype.__originalMappings = null;
Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', {
  get: function () {
    if (!this.__originalMappings) {
      this._parseMappings(this._mappings, this.sourceRoot);
    }

    return this.__originalMappings;
  }
});

SourceMapConsumer.prototype._charIsMappingSeparator =
  function SourceMapConsumer_charIsMappingSeparator(aStr, index) {
    var c = aStr.charAt(index);
    return c === ";" || c === ",";
  };

/**
 * 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).
 */
SourceMapConsumer.prototype._parseMappings =
  function SourceMapConsumer_parseMappings(aStr, aSourceRoot) {
    throw new Error("Subclasses must implement _parseMappings");
  };

SourceMapConsumer.GENERATED_ORDER = 1;
SourceMapConsumer.ORIGINAL_ORDER = 2;

SourceMapConsumer.GREATEST_LOWER_BOUND = 1;
SourceMapConsumer.LEAST_UPPER_BOUND = 2;

/**
 * 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`.
 */
SourceMapConsumer.prototype.eachMapping =
  function SourceMapConsumer_eachMapping(aCallback, aContext, aOrder) {
    var context = aContext || null;
    var order = aOrder || SourceMapConsumer.GENERATED_ORDER;

    var mappings;
    switch (order) {
    case SourceMapConsumer.GENERATED_ORDER:
      mappings = this._generatedMappings;
      break;
    case SourceMapConsumer.ORIGINAL_ORDER:
      mappings = this._originalMappings;
      break;
    default:
      throw new Error("Unknown order of iteration.");
    }

    var sourceRoot = this.sourceRoot;
    mappings.map(function (mapping) {
      var source = mapping.source === null ? null : this._sources.at(mapping.source);
      if (source != null && sourceRoot != null) {
        source = util.join(sourceRoot, source);
      }
      return {
        source: source,
        generatedLine: mapping.generatedLine,
        generatedColumn: mapping.generatedColumn,
        originalLine: mapping.originalLine,
        originalColumn: mapping.originalColumn,
        name: mapping.name === null ? null : this._names.at(mapping.name)
      };
    }, this).forEach(aCallback, context);
  };

/**
 * 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.
 *   - column: Optional. the column number in the original source.
 *
 * and an array of objects is returned, each with the following properties:
 *
 *   - line: The line number in the generated source, or null.
 *   - column: The column number in the generated source, or null.
 */
SourceMapConsumer.prototype.allGeneratedPositionsFor =
  function SourceMapConsumer_allGeneratedPositionsFor(aArgs) {
    var line = util.getArg(aArgs, 'line');

    // When there is no exact match, BasicSourceMapConsumer.prototype._findMapping
    // returns the index of the closest mapping less than the needle. By
    // setting needle.originalColumn to 0, we thus find the last mapping for
    // the given line, provided such a mapping exists.
    var needle = {
      source: util.getArg(aArgs, 'source'),
      originalLine: line,
      originalColumn: util.getArg(aArgs, 'column', 0)
    };

    if (this.sourceRoot != null) {
      needle.source = util.relative(this.sourceRoot, needle.source);
    }
    if (!this._sources.has(needle.source)) {
      return [];
    }
    needle.source = this._sources.indexOf(needle.source);

    var mappings = [];

    var index = this._findMapping(needle,
                                  this._originalMappings,
                                  "originalLine",
                                  "originalColumn",
                                  util.compareByOriginalPositions,
                                  binarySearch.LEAST_UPPER_BOUND);
    if (index >= 0) {
      var mapping = this._originalMappings[index];

      if (aArgs.column === undefined) {
        var originalLine = mapping.originalLine;

        // Iterate until either we run out of mappings, or we run into
        // a mapping for a different line than the one we found. Since
        // mappings are sorted, this is guaranteed to find all mappings for
        // the line we found.
        while (mapping && mapping.originalLine === originalLine) {
          mappings.push({
            line: util.getArg(mapping, 'generatedLine', null),
            column: util.getArg(mapping, 'generatedColumn', null),
            lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null)
          });

          mapping = this._originalMappings[++index];
        }
      } else {
        var originalColumn = mapping.originalColumn;

        // Iterate until either we run out of mappings, or we run into
        // a mapping for a different line than the one we were searching for.
        // Since mappings are sorted, this is guaranteed to find all mappings for
        // the line we are searching for.
        while (mapping &&
               mapping.originalLine === line &&
               mapping.originalColumn == originalColumn) {
          mappings.push({
            line: util.getArg(mapping, 'generatedLine', null),
            column: util.getArg(mapping, 'generatedColumn', null),
            lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null)
          });

          mapping = this._originalMappings[++index];
        }
      }
    }

    return mappings;
  };

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 only 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 referrenced 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;"
 *     }
 *
 * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1#
 */
function BasicSourceMapConsumer(aSourceMap) {
  var sourceMap = aSourceMap;
  if (typeof aSourceMap === 'string') {
    sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
  }

  var version = util.getArg(sourceMap, 'version');
  var sources = util.getArg(sourceMap, 'sources');
  // Sass 3.3 leaves out the 'names' array, so we deviate from the spec (which
  // requires the array) to play nice here.
  var names = util.getArg(sourceMap, 'names', []);
  var sourceRoot = util.getArg(sourceMap, 'sourceRoot', null);
  var sourcesContent = util.getArg(sourceMap, 'sourcesContent', null);
  var mappings = util.getArg(sourceMap, 'mappings');
  var file = util.getArg(sourceMap, 'file', 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 != this._version) {
    throw new Error('Unsupported version: ' + version);
  }

  sources = sources
    .map(String)
    // Some source maps produce relative source paths like "./foo.js" instead of
    // "foo.js".  Normalize these first so that future comparisons will succeed.
    // See bugzil.la/1090768.
    .map(util.normalize)
    // Always ensure that absolute sources are internally stored relative to
    // the source root, if the source root is absolute. Not doing this would
    // be particularly problematic when the source root is a prefix of the
    // source (valid, but why??). See github issue #199 and bugzil.la/1188982.
    .map(function (source) {
      return sourceRoot && util.isAbsolute(sourceRoot) && util.isAbsolute(source)
        ? util.relative(sourceRoot, source)
        : source;
    });

  // 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.
  this._names = ArraySet.fromArray(names.map(String), true);
  this._sources = ArraySet.fromArray(sources, true);

  this.sourceRoot = sourceRoot;
  this.sourcesContent = sourcesContent;
  this._mappings = mappings;
  this.file = file;
}

BasicSourceMapConsumer.prototype = Object.create(SourceMapConsumer.prototype);
BasicSourceMapConsumer.prototype.consumer = SourceMapConsumer;

/**
 * Create a BasicSourceMapConsumer from a SourceMapGenerator.
 *
 * @param SourceMapGenerator aSourceMap
 *        The source map that will be consumed.
 * @returns BasicSourceMapConsumer
 */
BasicSourceMapConsumer.fromSourceMap =
  function SourceMapConsumer_fromSourceMap(aSourceMap) {
    var smc = Object.create(BasicSourceMapConsumer.prototype);

    var names = smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true);
    var sources = smc._sources = ArraySet.fromArray(aSourceMap._sources.toArray(), true);
    smc.sourceRoot = aSourceMap._sourceRoot;
    smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(),
                                                            smc.sourceRoot);
    smc.file = aSourceMap._file;

    // Because we are modifying the entries (by converting string sources and
    // names to indices into the sources and names ArraySets), we have to make
    // a copy of the entry or else bad things happen. Shared mutable state
    // strikes again! See github issue #191.

    var generatedMappings = aSourceMap._mappings.toArray().slice();
    var destGeneratedMappings = smc.__generatedMappings = [];
    var destOriginalMappings = smc.__originalMappings = [];

    for (var i = 0, length = generatedMappings.length; i < length; i++) {
      var srcMapping = generatedMappings[i];
      var destMapping = new Mapping;
      destMapping.generatedLine = srcMapping.generatedLine;
      destMapping.generatedColumn = srcMapping.generatedColumn;

      if (srcMapping.source) {
        destMapping.source = sources.indexOf(srcMapping.source);
        destMapping.originalLine = srcMapping.originalLine;
        destMapping.originalColumn = srcMapping.originalColumn;

        if (srcMapping.name) {
          destMapping.name = names.indexOf(srcMapping.name);
        }

        destOriginalMappings.push(destMapping);
      }

      destGeneratedMappings.push(destMapping);
    }

    quickSort(smc.__originalMappings, util.compareByOriginalPositions);

    return smc;
  };

/**
 * The version of the source mapping spec that we are consuming.
 */
BasicSourceMapConsumer.prototype._version = 3;

/**
 * The list of original sources.
 */
Object.defineProperty(BasicSourceMapConsumer.prototype, 'sources', {
  get: function () {
    return this._sources.toArray().map(function (s) {
      return this.sourceRoot != null ? util.join(this.sourceRoot, s) : s;
    }, this);
  }
});

/**
 * Provide the JIT with a nice shape / hidden class.
 */
function Mapping() {
  this.generatedLine = 0;
  this.generatedColumn = 0;
  this.source = null;
  this.originalLine = null;
  this.originalColumn = null;
  this.name = null;
}

/**
 * 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).
 */
BasicSourceMapConsumer.prototype._parseMappings =
  function SourceMapConsumer_parseMappings(aStr, aSourceRoot) {
    var generatedLine = 1;
    var previousGeneratedColumn = 0;
    var previousOriginalLine = 0;
    var previousOriginalColumn = 0;
    var previousSource = 0;
    var previousName = 0;
    var length = aStr.length;
    var index = 0;
    var cachedSegments = {};
    var temp = {};
    var originalMappings = [];
    var generatedMappings = [];
    var mapping, str, segment, end, value;

    while (index < length) {
      if (aStr.charAt(index) === ';') {
        generatedLine++;
        index++;
        previousGeneratedColumn = 0;
      }
      else if (aStr.charAt(index) === ',') {
        index++;
      }
      else {
        mapping = new Mapping();
        mapping.generatedLine = generatedLine;

        // Because each offset is encoded relative to the previous one,
        // many segments often have the same encoding. We can exploit this
        // fact by caching the parsed variable length fields of each segment,
        // allowing us to avoid a second parse if we encounter the same
        // segment again.
        for (end = index; end < length; end++) {
          if (this._charIsMappingSeparator(aStr, end)) {
            break;
          }
        }
        str = aStr.slice(index, end);

        segment = cachedSegments[str];
        if (segment) {
          index += str.length;
        } else {
          segment = [];
          while (index < end) {
            base64VLQ.decode(aStr, index, temp);
            value = temp.value;
            index = temp.rest;
            segment.push(value);
          }

          if (segment.length === 2) {
            throw new Error('Found a source, but no line and column');
          }

          if (segment.length === 3) {
            throw new Error('Found a source and line, but no column');
          }

          cachedSegments[str] = segment;
        }

        // Generated column.
        mapping.generatedColumn = previousGeneratedColumn + segment[0];
        previousGeneratedColumn = mapping.generatedColumn;

        if (segment.length > 1) {
          // Original source.
          mapping.source = previousSource + segment[1];
          previousSource += segment[1];

          // Original line.
          mapping.originalLine = previousOriginalLine + segment[2];
          previousOriginalLine = mapping.originalLine;
          // Lines are stored 0-based
          mapping.originalLine += 1;

          // Original column.
          mapping.originalColumn = previousOriginalColumn + segment[3];
          previousOriginalColumn = mapping.originalColumn;

          if (segment.length > 4) {
            // Original name.
            mapping.name = previousName + segment[4];
            previousName += segment[4];
          }
        }

        generatedMappings.push(mapping);
        if (typeof mapping.originalLine === 'number') {
          originalMappings.push(mapping);
        }
      }
    }

    quickSort(generatedMappings, util.compareByGeneratedPositionsDeflated);
    this.__generatedMappings = generatedMappings;

    quickSort(originalMappings, util.compareByOriginalPositions);
    this.__originalMappings = originalMappings;
  };

/**
 * Find the mapping that best matches the hypothetical "needle" mapping that
 * we are searching for in the given "haystack" of mappings.
 */
BasicSourceMapConsumer.prototype._findMapping =
  function SourceMapConsumer_findMapping(aNeedle, aMappings, aLineName,
                                         aColumnName, aComparator, aBias) {
    // To return the position we are searching for, we must first find the
    // mapping for the given position and then return the opposite position it
    // points to. Because the mappings are sorted, we can use binary search to
    // find the best mapping.

    if (aNeedle[aLineName] <= 0) {
      throw new TypeError('Line must be greater than or equal to 1, got '
                          + aNeedle[aLineName]);
    }
    if (aNeedle[aColumnName] < 0) {
      throw new TypeError('Column must be greater than or equal to 0, got '
                          + aNeedle[aColumnName]);
    }

    return binarySearch.search(aNeedle, aMappings, aComparator, aBias);
  };

/**
 * Compute the last column for each generated mapping. The last column is
 * inclusive.
 */
BasicSourceMapConsumer.prototype.computeColumnSpans =
  function SourceMapConsumer_computeColumnSpans() {
    for (var index = 0; index < this._generatedMappings.length; ++index) {
      var mapping = this._generatedMappings[index];

      // Mappings do not contain a field for the last generated columnt. We
      // can come up with an optimistic estimate, however, by assuming that
      // mappings are contiguous (i.e. given two consecutive mappings, the
      // first mapping ends where the second one starts).
      if (index + 1 < this._generatedMappings.length) {
        var nextMapping = this._generatedMappings[index + 1];

        if (mapping.generatedLine === nextMapping.generatedLine) {
          mapping.lastGeneratedColumn = nextMapping.generatedColumn - 1;
          continue;
        }
      }

      // The last mapping for each line spans the entire line.
      mapping.lastGeneratedColumn = Infinity;
    }
  };

/**
 * 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.
 *   - column: The column number in the generated source.
 *   - 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.
 *   - column: The column number in the original source, or null.
 *   - name: The original identifier, or null.
 */
BasicSourceMapConsumer.prototype.originalPositionFor =
  function SourceMapConsumer_originalPositionFor(aArgs) {
    var needle = {
      generatedLine: util.getArg(aArgs, 'line'),
      generatedColumn: util.getArg(aArgs, 'column')
    };

    var index = this._findMapping(
      needle,
      this._generatedMappings,
      "generatedLine",
      "generatedColumn",
      util.compareByGeneratedPositionsDeflated,
      util.getArg(aArgs, 'bias', SourceMapConsumer.GREATEST_LOWER_BOUND)
    );

    if (index >= 0) {
      var mapping = this._generatedMappings[index];

      if (mapping.generatedLine === needle.generatedLine) {
        var source = util.getArg(mapping, 'source', null);
        if (source !== null) {
          source = this._sources.at(source);
          if (this.sourceRoot != null) {
            source = util.join(this.sourceRoot, source);
          }
        }
        var name = util.getArg(mapping, 'name', null);
        if (name !== null) {
          name = this._names.at(name);
        }
        return {
          source: source,
          line: util.getArg(mapping, 'originalLine', null),
          column: util.getArg(mapping, 'originalColumn', null),
          name: 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.
 */
BasicSourceMapConsumer.prototype.hasContentsOfAllSources =
  function BasicSourceMapConsumer_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.
 */
BasicSourceMapConsumer.prototype.sourceContentFor =
  function SourceMapConsumer_sourceContentFor(aSource, nullOnMissing) {
    if (!this.sourcesContent) {
      return null;
    }

    if (this.sourceRoot != null) {
      aSource = util.relative(this.sourceRoot, aSource);
    }

    if (this._sources.has(aSource)) {
      return this.sourcesContent[this._sources.indexOf(aSource)];
    }

    var url;
    if (this.sourceRoot != null
        && (url = util.urlParse(this.sourceRoot))) {
      // XXX: file:// URIs and absolute paths lead to unexpected behavior for
      // many users. We can help them out when they expect file:// URIs to
      // behave like it would if they were running a local HTTP server. See
      // https://bugzilla.mozilla.org/show_bug.cgi?id=885597.
      var fileUriAbsPath = aSource.replace(/^file:\/\//, "");
      if (url.scheme == "file"
          && this._sources.has(fileUriAbsPath)) {
        return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)]
      }

      if ((!url.path || url.path == "/")
          && this._sources.has("/" + aSource)) {
        return this.sourcesContent[this._sources.indexOf("/" + aSource)];
      }
    }

    // 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;
    }
    else {
      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.
 *   - column: The column number in the original source.
 *   - 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.
 *   - column: The column number in the generated source, or null.
 */
BasicSourceMapConsumer.prototype.generatedPositionFor =
  function SourceMapConsumer_generatedPositionFor(aArgs) {
    var source = util.getArg(aArgs, 'source');
    if (this.sourceRoot != null) {
      source = util.relative(this.sourceRoot, source);
    }
    if (!this._sources.has(source)) {
      return {
        line: null,
        column: null,
        lastColumn: null
      };
    }
    source = this._sources.indexOf(source);

    var needle = {
      source: source,
      originalLine: util.getArg(aArgs, 'line'),
      originalColumn: util.getArg(aArgs, 'column')
    };

    var index = this._findMapping(
      needle,
      this._originalMappings,
      "originalLine",
      "originalColumn",
      util.compareByOriginalPositions,
      util.getArg(aArgs, 'bias', SourceMapConsumer.GREATEST_LOWER_BOUND)
    );

    if (index >= 0) {
      var mapping = this._originalMappings[index];

      if (mapping.source === needle.source) {
        return {
          line: util.getArg(mapping, 'generatedLine', null),
          column: util.getArg(mapping, 'generatedColumn', null),
          lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null)
        };
      }
    }

    return {
      line: null,
      column: null,
      lastColumn: null
    };
  };

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 only 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;"
 *      }
 *    }],
 *  }
 *
 * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#heading=h.535es3xeprgt
 */
function IndexedSourceMapConsumer(aSourceMap) {
  var sourceMap = aSourceMap;
  if (typeof aSourceMap === 'string') {
    sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
  }

  var version = util.getArg(sourceMap, 'version');
  var sections = util.getArg(sourceMap, 'sections');

  if (version != this._version) {
    throw new Error('Unsupported version: ' + version);
  }

  this._sources = new ArraySet();
  this._names = new ArraySet();

  var lastOffset = {
    line: -1,
    column: 0
  };
  this._sections = sections.map(function (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.');
    }
    var offset = util.getArg(s, 'offset');
    var offsetLine = util.getArg(offset, 'line');
    var 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;

    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: new SourceMapConsumer(util.getArg(s, 'map'))
    }
  });
}

IndexedSourceMapConsumer.prototype = Object.create(SourceMapConsumer.prototype);
IndexedSourceMapConsumer.prototype.constructor = SourceMapConsumer;

/**
 * The version of the source mapping spec that we are consuming.
 */
IndexedSourceMapConsumer.prototype._version = 3;

/**
 * The list of original sources.
 */
Object.defineProperty(IndexedSourceMapConsumer.prototype, 'sources', {
  get: function () {
    var sources = [];
    for (var i = 0; i < this._sections.length; i++) {
      for (var 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.
 *   - column: The column number in the generated source.
 *
 * 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.
 *   - column: The column number in the original source, or null.
 *   - name: The original identifier, or null.
 */
IndexedSourceMapConsumer.prototype.originalPositionFor =
  function IndexedSourceMapConsumer_originalPositionFor(aArgs) {
    var 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.
    var sectionIndex = binarySearch.search(needle, this._sections,
      function(needle, section) {
        var cmp = needle.generatedLine - section.generatedOffset.generatedLine;
        if (cmp) {
          return cmp;
        }

        return (needle.generatedColumn -
                section.generatedOffset.generatedColumn);
      });
    var 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.
 */
IndexedSourceMapConsumer.prototype.hasContentsOfAllSources =
  function IndexedSourceMapConsumer_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.
 */
IndexedSourceMapConsumer.prototype.sourceContentFor =
  function IndexedSourceMapConsumer_sourceContentFor(aSource, nullOnMissing) {
    for (var i = 0; i < this._sections.length; i++) {
      var section = this._sections[i];

      var content = section.consumer.sourceContentFor(aSource, true);
      if (content) {
        return content;
      }
    }
    if (nullOnMissing) {
      return null;
    }
    else {
      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.
 *   - column: The column number in the original source.
 *
 * and an object is returned with the following properties:
 *
 *   - line: The line number in the generated source, or null.
 *   - column: The column number in the generated source, or null.
 */
IndexedSourceMapConsumer.prototype.generatedPositionFor =
  function IndexedSourceMapConsumer_generatedPositionFor(aArgs) {
    for (var i = 0; i < this._sections.length; i++) {
      var section = this._sections[i];

      // Only consider this section if the requested source is in the list of
      // sources of the consumer.
      if (section.consumer.sources.indexOf(util.getArg(aArgs, 'source')) === -1) {
        continue;
      }
      var generatedPosition = section.consumer.generatedPositionFor(aArgs);
      if (generatedPosition) {
        var ret = {
          line: generatedPosition.line +
            (section.generatedOffset.generatedLine - 1),
          column: generatedPosition.column +
            (section.generatedOffset.generatedLine === generatedPosition.line
             ? section.generatedOffset.generatedColumn - 1
             : 0)
        };
        return ret;
      }
    }

    return {
      line: null,
      column: null
    };
  };

/**
 * 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).
 */
IndexedSourceMapConsumer.prototype._parseMappings =
  function IndexedSourceMapConsumer_parseMappings(aStr, aSourceRoot) {
    this.__generatedMappings = [];
    this.__originalMappings = [];
    for (var i = 0; i < this._sections.length; i++) {
      var section = this._sections[i];
      var sectionMappings = section.consumer._generatedMappings;
      for (var j = 0; j < sectionMappings.length; j++) {
        var mapping = sectionMappings[j];

        var source = section.consumer._sources.at(mapping.source);
        if (section.consumer.sourceRoot !== null) {
          source = util.join(section.consumer.sourceRoot, source);
        }
        this._sources.add(source);
        source = this._sources.indexOf(source);

        var name = section.consumer._names.at(mapping.name);
        this._names.add(name);
        name = this._names.indexOf(name);

        // The mappings coming from the consumer for the section have
        // generated positions relative to the start of the section, so we
        // need to offset them to be relative to the start of the concatenated
        // generated file.
        var adjustedMapping = {
          source: source,
          generatedLine: mapping.generatedLine +
            (section.generatedOffset.generatedLine - 1),
          generatedColumn: mapping.generatedColumn +
            (section.generatedOffset.generatedLine === mapping.generatedLine
            ? section.generatedOffset.generatedColumn - 1
            : 0),
          originalLine: mapping.originalLine,
          originalColumn: mapping.originalColumn,
          name: name
        };

        this.__generatedMappings.push(adjustedMapping);
        if (typeof adjustedMapping.originalLine === 'number') {
          this.__originalMappings.push(adjustedMapping);
        }
      }
    }

    quickSort(this.__generatedMappings, util.compareByGeneratedPositionsDeflated);
    quickSort(this.__originalMappings, util.compareByOriginalPositions);
  };

exports.IndexedSourceMapConsumer = IndexedSourceMapConsumer;


/***/ }),

/***/ 823:
/***/ (function(module, exports) {

/* -*- 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.
  var mid = Math.floor((aHigh - aLow) / 2) + aLow;
  var 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;
    } else {
      return mid;
    }
  }
  else {
    // 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;
    } else {
      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;
  }

  var 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 than
  // 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;
};


/***/ }),

/***/ 824:
/***/ (function(module, exports) {

/* -*- 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
 */

// It turns out that some (most?) JavaScript engines don't self-host
// `Array.prototype.sort`. This makes sense because C++ will likely remain
// faster than JS when doing raw CPU-intensive sorting. However, when using a
// custom comparator function, calling back and forth between the VM's C++ and
// JIT'd JS is rather slow *and* loses JIT type information, resulting in
// worse generated code for the comparator function than would be optimal. In
// fact, when sorting with a comparator, these costs outweigh the benefits of
// sorting in C++. By using our own JS-implemented Quick Sort (below), we get
// a ~3500ms mean speed-up in `bench/bench.html`.

/**
 * Swap the elements indexed by `x` and `y` in the array `ary`.
 *
 * @param {Array} ary
 *        The array.
 * @param {Number} x
 *        The index of the first item.
 * @param {Number} y
 *        The index of the second item.
 */
function swap(ary, x, y) {
  var temp = ary[x];
  ary[x] = ary[y];
  ary[y] = temp;
}

/**
 * Returns a random integer within the range `low .. high` inclusive.
 *
 * @param {Number} low
 *        The lower bound on the range.
 * @param {Number} high
 *        The upper bound on the range.
 */
function randomIntInRange(low, high) {
  return Math.round(low + (Math.random() * (high - low)));
}

/**
 * The Quick Sort algorithm.
 *
 * @param {Array} ary
 *        An array to sort.
 * @param {function} comparator
 *        Function to use to compare two items.
 * @param {Number} p
 *        Start index of the array
 * @param {Number} r
 *        End index of the array
 */
function doQuickSort(ary, comparator, p, r) {
  // If our lower bound is less than our upper bound, we (1) partition the
  // array into two pieces and (2) recurse on each half. If it is not, this is
  // the empty array and our base case.

  if (p < r) {
    // (1) Partitioning.
    //
    // The partitioning chooses a pivot between `p` and `r` and moves all
    // elements that are less than or equal to the pivot to the before it, and
    // all the elements that are greater than it after it. The effect is that
    // once partition is done, the pivot is in the exact place it will be when
    // the array is put in sorted order, and it will not need to be moved
    // again. This runs in O(n) time.

    // Always choose a random pivot so that an input array which is reverse
    // sorted does not cause O(n^2) running time.
    var pivotIndex = randomIntInRange(p, r);
    var i = p - 1;

    swap(ary, pivotIndex, r);
    var pivot = ary[r];

    // Immediately after `j` is incremented in this loop, the following hold
    // true:
    //
    //   * Every element in `ary[p .. i]` is less than or equal to the pivot.
    //
    //   * Every element in `ary[i+1 .. j-1]` is greater than the pivot.
    for (var j = p; j < r; j++) {
      if (comparator(ary[j], pivot) <= 0) {
        i += 1;
        swap(ary, i, j);
      }
    }

    swap(ary, i + 1, j);
    var q = i + 1;

    // (2) Recurse on each half.

    doQuickSort(ary, comparator, p, q - 1);
    doQuickSort(ary, comparator, q + 1, r);
  }
}

/**
 * Sort the given array in-place with the given comparator function.
 *
 * @param {Array} ary
 *        An array to sort.
 * @param {function} comparator
 *        Function to use to compare two items.
 */
exports.quickSort = function (ary, comparator) {
  doQuickSort(ary, comparator, 0, ary.length - 1);
};


/***/ }),

/***/ 825:
/***/ (function(module, exports, __webpack_require__) {

/* -*- 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
 */

var SourceMapGenerator = __webpack_require__(816).SourceMapGenerator;
var util = __webpack_require__(819);

// Matches a Windows-style `\r\n` newline or a `\n` newline used by all other
// operating systems these days (capturing the result).
var REGEX_NEWLINE = /(\r?\n)/;

// Newline character code for charCodeAt() comparisons
var 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!
var 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.
 */
function SourceNode(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.
 */
SourceNode.fromStringWithSourceMap =
  function SourceNode_fromStringWithSourceMap(aGeneratedCode, aSourceMapConsumer, aRelativePath) {
    // The SourceNode we want to fill with the generated code
    // and the SourceMap
    var 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`.
    var remainingLines = aGeneratedCode.split(REGEX_NEWLINE);
    var remainingLinesIndex = 0;
    var shiftNextLine = function() {
      var lineContents = getNextLine();
      // The last line of a file might not have a newline.
      var newLine = getNextLine() || "";
      return lineContents + newLine;

      function getNextLine() {
        return remainingLinesIndex < remainingLines.length ?
            remainingLines[remainingLinesIndex++] : undefined;
      }
    };

    // We need to remember the position of "remainingLines"
    var 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.
    var lastMapping = null;

    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"
          var nextLine = remainingLines[remainingLinesIndex];
          var 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) {
        var 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) {
      var 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 {
        var 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.
 */
SourceNode.prototype.add = function SourceNode_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.
 */
SourceNode.prototype.prepend = function SourceNode_prepend(aChunk) {
  if (Array.isArray(aChunk)) {
    for (var 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.
 */
SourceNode.prototype.walk = function SourceNode_walk(aFn) {
  var chunk;
  for (var 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.
 */
SourceNode.prototype.join = function SourceNode_join(aSep) {
  var newChildren;
  var i;
  var 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.
 */
SourceNode.prototype.replaceRight = function SourceNode_replaceRight(aPattern, aReplacement) {
  var 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
 */
SourceNode.prototype.setSourceContent =
  function SourceNode_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.
 */
SourceNode.prototype.walkSourceContents =
  function SourceNode_walkSourceContents(aFn) {
    for (var i = 0, len = this.children.length; i < len; i++) {
      if (this.children[i][isSourceNode]) {
        this.children[i].walkSourceContents(aFn);
      }
    }

    var sources = Object.keys(this.sourceContents);
    for (var 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.
 */
SourceNode.prototype.toString = function SourceNode_toString() {
  var str = "";
  this.walk(function (chunk) {
    str += chunk;
  });
  return str;
};

/**
 * Returns the string representation of this source node along with a source
 * map.
 */
SourceNode.prototype.toStringWithSourceMap = function SourceNode_toStringWithSourceMap(aArgs) {
  var generated = {
    code: "",
    line: 1,
    column: 0
  };
  var map = new SourceMapGenerator(aArgs);
  var sourceMappingActive = false;
  var lastOriginalSource = null;
  var lastOriginalLine = null;
  var lastOriginalColumn = null;
  var 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 (var 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: map };
};

exports.SourceNode = SourceNode;


/***/ })

/******/ });
});PK
!<��Qw�X�XEchrome/devtools/modules/devtools/client/debugger/new/search-worker.js(function webpackUniversalModuleDefinition(root, factory) {
	if(typeof exports === 'object' && typeof module === 'object')
		module.exports = factory();
	else if(typeof define === 'function' && define.amd)
		define([], factory);
	else {
		var a = factory();
		for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
	}
})(typeof self !== 'undefined' ? self : this, function() {
return /******/ (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, {
/******/ 				configurable: false,
/******/ 				enumerable: true,
/******/ 				get: getter
/******/ 			});
/******/ 		}
/******/ 	};
/******/
/******/ 	// 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 = "/assets/build";
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(__webpack_require__.s = 1284);
/******/ })
/************************************************************************/
/******/ ({

/***/ 10:
/***/ (function(module, exports, __webpack_require__) {

var Symbol = __webpack_require__(7);

/** Used for built-in method references. */
var objectProto = Object.prototype;

/** 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 nativeObjectToString = objectProto.toString;

/** Built-in value references. */
var symToStringTag = Symbol ? Symbol.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.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;
}

module.exports = getRawTag;


/***/ }),

/***/ 108:
/***/ (function(module, exports, __webpack_require__) {

var baseToString = __webpack_require__(109);

/**
 * 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 convert.
 * @returns {string} Returns the converted string.
 * @example
 *
 * _.toString(null);
 * // => ''
 *
 * _.toString(-0);
 * // => '-0'
 *
 * _.toString([1, 2, 3]);
 * // => '1,2,3'
 */
function toString(value) {
  return value == null ? '' : baseToString(value);
}

module.exports = toString;


/***/ }),

/***/ 109:
/***/ (function(module, exports, __webpack_require__) {

var Symbol = __webpack_require__(7),
    arrayMap = __webpack_require__(110),
    isArray = __webpack_require__(70),
    isSymbol = __webpack_require__(72);

/** Used as references for various `Number` constants. */
var INFINITY = 1 / 0;

/** Used to convert symbols to primitives and strings. */
var symbolProto = Symbol ? Symbol.prototype : undefined,
    symbolToString = symbolProto ? symbolProto.toString : undefined;

/**
 * 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 (isArray(value)) {
    // Recursively convert values (susceptible to call stack limits).
    return arrayMap(value, baseToString) + '';
  }
  if (isSymbol(value)) {
    return symbolToString ? symbolToString.call(value) : '';
  }
  var result = (value + '');
  return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
}

module.exports = baseToString;


/***/ }),

/***/ 11:
/***/ (function(module, exports) {

/** Used for built-in method references. */
var objectProto = Object.prototype;

/**
 * Used to resolve the
 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
 * of values.
 */
var nativeObjectToString = objectProto.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.call(value);
}

module.exports = objectToString;


/***/ }),

/***/ 110:
/***/ (function(module, exports) {

/**
 * 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 == null ? 0 : array.length,
      result = Array(length);

  while (++index < length) {
    result[index] = iteratee(array[index], index, array);
  }
  return result;
}

module.exports = arrayMap;


/***/ }),

/***/ 1284:
/***/ (function(module, exports, __webpack_require__) {

module.exports = __webpack_require__(1631);


/***/ }),

/***/ 1363:
/***/ (function(module, exports, __webpack_require__) {

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 networkRequest = __webpack_require__(1367);
const workerUtils = __webpack_require__(1368);

module.exports = {
  networkRequest,
  workerUtils
};

/***/ }),

/***/ 1367:
/***/ (function(module, exports) {

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 networkRequest(url, opts) {
  return fetch(url, {
    cache: opts.loadFromCache ? "default" : "no-cache"
  }).then(res => {
    if (res.status >= 200 && res.status < 300) {
      return res.text().then(text => ({ content: text }));
    }
    return Promise.reject(`request failed with status ${res.status}`);
  });
}

module.exports = networkRequest;

/***/ }),

/***/ 1368:
/***/ (function(module, exports) {

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"); }); }; }

function WorkerDispatcher() {
  this.msgId = 1;
  this.worker = null;
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

WorkerDispatcher.prototype = {
  start(url) {
    this.worker = new Worker(url);
    this.worker.onerror = () => {
      console.error(`Error in worker ${url}`);
    };
  },

  stop() {
    if (!this.worker) {
      return;
    }

    this.worker.terminate();
    this.worker = null;
  },

  task(method) {
    return (...args) => {
      return new Promise((resolve, reject) => {
        const id = this.msgId++;
        this.worker.postMessage({ id, method, args });

        const listener = ({ data: result }) => {
          if (result.id !== id) {
            return;
          }

          if (!this.worker) {
            return;
          }

          this.worker.removeEventListener("message", listener);
          if (result.error) {
            reject(result.error);
          } else {
            resolve(result.response);
          }
        };

        this.worker.addEventListener("message", listener);
      });
    };
  }
};

function workerHandler(publicInterface) {
  return function (msg) {
    const { id, method, args } = msg.data;
    try {
      const response = publicInterface[method].apply(undefined, args);
      if (response instanceof Promise) {
        response.then(val => self.postMessage({ id, response: val }),
        // Error can't be sent via postMessage, so be sure to
        // convert to string.
        err => self.postMessage({ id, error: err.toString() }));
      } else {
        self.postMessage({ id, response });
      }
    } catch (error) {
      // Error can't be sent via postMessage, so be sure to convert to
      // string.
      self.postMessage({ id, error: error.toString() });
    }
  };
}

function streamingWorkerHandler(publicInterface, { timeout = 100 } = {}, worker = self) {
  let streamingWorker = (() => {
    var _ref = _asyncToGenerator(function* (id, tasks) {
      let isWorking = true;

      const intervalId = setTimeout(function () {
        isWorking = false;
      }, timeout);

      const results = [];
      while (tasks.length !== 0 && isWorking) {
        const { callback, context, args } = tasks.shift();
        const result = yield callback.call(context, args);
        results.push(result);
      }
      worker.postMessage({ id, status: "pending", data: results });
      clearInterval(intervalId);

      if (tasks.length !== 0) {
        yield streamingWorker(id, tasks);
      }
    });

    return function streamingWorker(_x, _x2) {
      return _ref.apply(this, arguments);
    };
  })();

  return (() => {
    var _ref2 = _asyncToGenerator(function* (msg) {
      const { id, method, args } = msg.data;
      const workerMethod = publicInterface[method];
      if (!workerMethod) {
        console.error(`Could not find ${method} defined in worker.`);
      }
      worker.postMessage({ id, status: "start" });

      try {
        const tasks = workerMethod(args);
        yield streamingWorker(id, tasks);
        worker.postMessage({ id, status: "done" });
      } catch (error) {
        worker.postMessage({ id, status: "error", error });
      }
    });

    return function (_x3) {
      return _ref2.apply(this, arguments);
    };
  })();
}

module.exports = {
  WorkerDispatcher,
  workerHandler,
  streamingWorkerHandler
};

/***/ }),

/***/ 14:
/***/ (function(module, exports) {

/**
 * 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';
}

module.exports = isObjectLike;


/***/ }),

/***/ 1402:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = buildQuery;

var _escapeRegExp = __webpack_require__(259);

var _escapeRegExp2 = _interopRequireDefault(_escapeRegExp);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/**
 * Ignore doing outline matches for less than 3 whitespaces
 *
 * @memberof utils/source-search
 * @static
 */
function ignoreWhiteSpace(str) {
  return (/^\s{0,2}$/.test(str) ? "(?!\\s*.*)" : str
  );
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.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 wholeMatch(query, wholeWord) {
  if (query === "" || !wholeWord) {
    return query;
  }

  return `\\b${query}\\b`;
}

function buildFlags(caseSensitive, isGlobal) {
  if (caseSensitive && isGlobal) {
    return "g";
  }

  if (!caseSensitive && isGlobal) {
    return "gi";
  }

  if (!caseSensitive && !isGlobal) {
    return "i";
  }

  return;
}

function buildQuery(originalQuery, modifiers, { isGlobal = false, ignoreSpaces = false }) {
  const { caseSensitive, regexMatch, wholeWord } = modifiers;

  if (originalQuery === "") {
    return new RegExp(originalQuery);
  }

  let query = originalQuery;
  if (ignoreSpaces) {
    query = ignoreWhiteSpace(query);
  }

  if (!regexMatch) {
    query = (0, _escapeRegExp2.default)(query);
  }

  query = wholeMatch(query, wholeWord);
  const flags = buildFlags(caseSensitive, isGlobal);

  if (flags) {
    return new RegExp(query, flags);
  }

  return new RegExp(query);
}

/***/ }),

/***/ 1631:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var _getMatches = __webpack_require__(1632);

var _getMatches2 = _interopRequireDefault(_getMatches);

var _projectSearch = __webpack_require__(1633);

var _devtoolsUtils = __webpack_require__(1363);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

const { workerHandler } = _devtoolsUtils.workerUtils; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                       * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                       * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

self.onmessage = workerHandler({ getMatches: _getMatches2.default, findSourceMatches: _projectSearch.findSourceMatches });

/***/ }),

/***/ 1632:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = getMatches;

var _buildQuery = __webpack_require__(1402);

var _buildQuery2 = _interopRequireDefault(_buildQuery);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function getMatches(query, text, modifiers) {
  if (!query || !text || !modifiers) {
    return [];
  }
  const regexQuery = (0, _buildQuery2.default)(query, modifiers, {
    isGlobal: true
  });
  const matchedLocations = [];
  const lines = text.split("\n");
  for (let i = 0; i < lines.length; i++) {
    let singleMatch;
    const line = lines[i];
    while ((singleMatch = regexQuery.exec(line)) !== null) {
      matchedLocations.push({ line: i, ch: singleMatch.index });
    }
  }
  return matchedLocations;
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/***/ }),

/***/ 1633:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.findSourceMatches = findSourceMatches;
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

// Maybe reuse file search's functions?

function findSourceMatches(source, queryText) {
  const text = source.text;
  if (source.loadedState !== "loaded" || !text || queryText == "") {
    return [];
  }

  const lines = text.split("\n");
  let result = undefined;
  const query = new RegExp(queryText, "g");

  let matches = lines.map((_text, line) => {
    const indices = [];

    while (result = query.exec(_text)) {
      indices.push({
        sourceId: source.id,
        line: line + 1,
        column: result.index,
        match: result[0],
        value: _text,
        text: result.input
      });
    }
    return indices;
  }).filter(_matches => _matches.length > 0);

  matches = [].concat(...matches);
  return matches;
}

/***/ }),

/***/ 259:
/***/ (function(module, exports, __webpack_require__) {

var toString = __webpack_require__(108);

/**
 * Used to match `RegExp`
 * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).
 */
var reRegExpChar = /[\\^$.*+?()[\]{}|]/g,
    reHasRegExpChar = RegExp(reRegExpChar.source);

/**
 * Escapes the `RegExp` special characters "^", "$", "\", ".", "*", "+",
 * "?", "(", ")", "[", "]", "{", "}", and "|" in `string`.
 *
 * @static
 * @memberOf _
 * @since 3.0.0
 * @category String
 * @param {string} [string=''] The string to escape.
 * @returns {string} Returns the escaped string.
 * @example
 *
 * _.escapeRegExp('[lodash](https://lodash.com/)');
 * // => '\[lodash\]\(https://lodash\.com/\)'
 */
function escapeRegExp(string) {
  string = toString(string);
  return (string && reHasRegExpChar.test(string))
    ? string.replace(reRegExpChar, '\\$&')
    : string;
}

module.exports = escapeRegExp;


/***/ }),

/***/ 6:
/***/ (function(module, exports, __webpack_require__) {

var Symbol = __webpack_require__(7),
    getRawTag = __webpack_require__(10),
    objectToString = __webpack_require__(11);

/** `Object#toString` result references. */
var nullTag = '[object Null]',
    undefinedTag = '[object Undefined]';

/** Built-in value references. */
var symToStringTag = Symbol ? Symbol.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 && symToStringTag in Object(value))
    ? getRawTag(value)
    : objectToString(value);
}

module.exports = baseGetTag;


/***/ }),

/***/ 7:
/***/ (function(module, exports, __webpack_require__) {

var root = __webpack_require__(8);

/** Built-in value references. */
var Symbol = root.Symbol;

module.exports = Symbol;


/***/ }),

/***/ 70:
/***/ (function(module, exports) {

/**
 * 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;

module.exports = isArray;


/***/ }),

/***/ 72:
/***/ (function(module, exports, __webpack_require__) {

var baseGetTag = __webpack_require__(6),
    isObjectLike = __webpack_require__(14);

/** `Object#toString` result references. */
var symbolTag = '[object Symbol]';

/**
 * 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) && baseGetTag(value) == symbolTag);
}

module.exports = isSymbol;


/***/ }),

/***/ 792:
/***/ (function(module, exports) {

var g;

// This works in non-strict mode
g = (function() {
	return this;
})();

try {
	// This works if eval is allowed (see CSP)
	g = g || Function("return this")() || (1,eval)("this");
} catch(e) {
	// This works if the window reference is available
	if(typeof window === "object")
		g = window;
}

// g can still be undefined, but nothing to do about it...
// We return undefined, instead of nothing here, so it's
// easier to handle this case. if(!global) { ...}

module.exports = g;


/***/ }),

/***/ 8:
/***/ (function(module, exports, __webpack_require__) {

var freeGlobal = __webpack_require__(9);

/** 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')();

module.exports = root;


/***/ }),

/***/ 9:
/***/ (function(module, exports, __webpack_require__) {

/* WEBPACK VAR INJECTION */(function(global) {/** Detect free variable `global` from Node.js. */
var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;

module.exports = freeGlobal;

/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(792)))

/***/ })

/******/ });
});PK
!<ݟx�rr9chrome/devtools/modules/devtools/client/debugger/panel.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 { Cc, Ci, Cu, Cr } = require("chrome");
const promise = require("promise");
const EventEmitter = require("devtools/shared/old-event-emitter");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");

function DebuggerPanel(iframeWindow, toolbox) {
  this.panelWin = iframeWindow;
  this._toolbox = toolbox;
  this._destroyer = null;

  this._view = this.panelWin.DebuggerView;
  this._controller = this.panelWin.DebuggerController;
  this._view._hostType = this._toolbox.hostType;
  this._controller._target = this.target;
  this._controller._toolbox = this._toolbox;

  this.handleHostChanged = this.handleHostChanged.bind(this);
  EventEmitter.decorate(this);
}

exports.DebuggerPanel = DebuggerPanel;

DebuggerPanel.prototype = {
  /**
   * Open is effectively an asynchronous constructor.
   *
   * @return object
   *         A promise that is resolved when the Debugger completes opening.
   */
  open: function () {
    let targetPromise;

    // Local debugging needs to make the target remote.
    if (!this.target.isRemote) {
      targetPromise = this.target.makeRemote();
      // Listen for tab switching events to manage focus when the content window
      // is paused and events suppressed.
      this.target.tab.addEventListener("TabSelect", this);
    } else {
      targetPromise = promise.resolve(this.target);
    }

    return targetPromise
      .then(() => this._controller.startupDebugger())
      .then(() => this._controller.connect())
      .then(() => {
        this._toolbox.on("host-changed", this.handleHostChanged);
        // Add keys from this document's keyset to the toolbox, so they
        // can work when the split console is focused.
        let keysToClone = ["resumeKey", "stepOverKey", "stepInKey", "stepOutKey"];
        for (let key of keysToClone) {
          let elm = this.panelWin.document.getElementById(key);
          let keycode = elm.getAttribute("keycode");
          let modifiers = elm.getAttribute("modifiers");
          let command = elm.getAttribute("command");
          let handler = this._view.Toolbar.getCommandHandler(command);

          let keyShortcut = this.translateToKeyShortcut(keycode, modifiers);
          this._toolbox.useKeyWithSplitConsole(keyShortcut, handler, "jsdebugger");
        }
        this.isReady = true;
        this.emit("ready");
        return this;
      })
      .catch(function onError(aReason) {
        DevToolsUtils.reportException("DebuggerPanel.prototype.open", aReason);
      });
  },

  /**
   * Translate a VK_ keycode, with modifiers, to a key shortcut that can be used with
   * shared/key-shortcut.
   *
   * @param {String} keycode
   *        The VK_* keycode to translate
   * @param {String} modifiers
   *        The list (blank-space separated) of modifiers applying to this keycode.
   * @return {String} a key shortcut ready to be used with shared/key-shortcut.js
   */
  translateToKeyShortcut: function (keycode, modifiers) {
    // Remove the VK_ prefix.
    keycode = keycode.replace("VK_", "");

    // Translate modifiers
    if (modifiers.includes("shift")) {
      keycode = "Shift+" + keycode;
    }
    if (modifiers.includes("alt")) {
      keycode = "Alt+" + keycode;
    }
    if (modifiers.includes("control")) {
      keycode = "Ctrl+" + keycode;
    }
    if (modifiers.includes("meta")) {
      keycode = "Cmd+" + keycode;
    }
    if (modifiers.includes("accel")) {
      keycode = "CmdOrCtrl+" + keycode;
    }

    return keycode;
  },

  // DevToolPanel API

  get target() {
    return this._toolbox.target;
  },

  destroy: function () {
    // Make sure this panel is not already destroyed.
    if (this._destroyer) {
      return this._destroyer;
    }

    if (!this.target.isRemote) {
      this.target.tab.removeEventListener("TabSelect", this);
    }

    return this._destroyer = this._controller.shutdownDebugger().then(() => {
      this.emit("destroyed");
    });
  },

  // DebuggerPanel API

  isPaused() {
    let framesController = this.panelWin.DebuggerController.StackFrames;
    let thread = framesController.activeThread;
    return thread && thread.paused;
  },

  getFrames() {
    let framesController = this.panelWin.DebuggerController.StackFrames;
    let thread = framesController.activeThread;
    if (this.isPaused()) {
      return {
        frames: thread.cachedFrames,
        selected: framesController.currentFrameDepth,
      };
    }

    return null;
  },

  addBreakpoint: function (location) {
    const { actions } = this.panelWin;
    const { dispatch } = this._controller;

    return dispatch(actions.addBreakpoint(location));
  },

  removeBreakpoint: function (location) {
    const { actions } = this.panelWin;
    const { dispatch } = this._controller;

    return dispatch(actions.removeBreakpoint(location));
  },

  blackbox: function (source, flag) {
    const { actions } = this.panelWin;
    const { dispatch } = this._controller;
    return dispatch(actions.blackbox(source, flag));
  },

  handleHostChanged: function () {
    this._view.handleHostChanged(this._toolbox.hostType);
  },

  // nsIDOMEventListener API

  handleEvent: function (aEvent) {
    if (aEvent.target == this.target.tab &&
        this._controller.activeThread.state == "paused") {
      // Wait a tick for the content focus event to be delivered.
      DevToolsUtils.executeSoon(() => this._toolbox.focusTool("jsdebugger"));
    }
  }
};
PK
!<�تN�N6chrome/devtools/modules/devtools/client/definitions.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Services = require("Services");
const osString = Services.appinfo.OS;

// Panels
loader.lazyGetter(this, "OptionsPanel", () => require("devtools/client/framework/toolbox-options").OptionsPanel);
loader.lazyGetter(this, "InspectorPanel", () => require("devtools/client/inspector/panel").InspectorPanel);
loader.lazyGetter(this, "WebConsolePanel", () => require("devtools/client/webconsole/panel").WebConsolePanel);
loader.lazyGetter(this, "DebuggerPanel", () => require("devtools/client/debugger/panel").DebuggerPanel);
loader.lazyGetter(this, "NewDebuggerPanel", () => require("devtools/client/debugger/new/panel").DebuggerPanel);
loader.lazyGetter(this, "StyleEditorPanel", () => require("devtools/client/styleeditor/styleeditor-panel").StyleEditorPanel);
loader.lazyGetter(this, "ShaderEditorPanel", () => require("devtools/client/shadereditor/panel").ShaderEditorPanel);
loader.lazyGetter(this, "CanvasDebuggerPanel", () => require("devtools/client/canvasdebugger/panel").CanvasDebuggerPanel);
loader.lazyGetter(this, "WebAudioEditorPanel", () => require("devtools/client/webaudioeditor/panel").WebAudioEditorPanel);
loader.lazyGetter(this, "MemoryPanel", () => require("devtools/client/memory/panel").MemoryPanel);
loader.lazyGetter(this, "PerformancePanel", () => require("devtools/client/performance/panel").PerformancePanel);
loader.lazyGetter(this, "NewPerformancePanel", () => require("devtools/client/performance-new/panel").PerformancePanel);
loader.lazyGetter(this, "NetMonitorPanel", () => require("devtools/client/netmonitor/panel").NetMonitorPanel);
loader.lazyGetter(this, "StoragePanel", () => require("devtools/client/storage/panel").StoragePanel);
loader.lazyGetter(this, "ScratchpadPanel", () => require("devtools/client/scratchpad/scratchpad-panel").ScratchpadPanel);
loader.lazyGetter(this, "DomPanel", () => require("devtools/client/dom/dom-panel").DomPanel);

// Other dependencies
loader.lazyRequireGetter(this, "CommandUtils", "devtools/client/shared/developer-toolbar", true);
loader.lazyRequireGetter(this, "CommandState", "devtools/shared/gcli/command-state", true);
loader.lazyRequireGetter(this, "ResponsiveUIManager", "devtools/client/responsive.html/manager", true);
loader.lazyImporter(this, "ScratchpadManager", "resource://devtools/client/scratchpad/scratchpad-manager.jsm");

const {MultiLocalizationHelper} = require("devtools/shared/l10n");
const L10N = new MultiLocalizationHelper(
  "devtools/client/locales/startup.properties",
  "devtools/shim/locales/key-shortcuts.properties"
);

var Tools = {};
exports.Tools = Tools;

// Definitions
Tools.options = {
  id: "options",
  ordinal: 0,
  url: "chrome://devtools/content/framework/toolbox-options.xhtml",
  icon: "chrome://devtools/skin/images/tool-options.svg",
  bgTheme: "theme-body",
  label: l10n("options.label"),
  iconOnly: true,
  panelLabel: l10n("options.panelLabel"),
  tooltip: l10n("optionsButton.tooltip"),
  inMenu: false,

  isTargetSupported: function () {
    return true;
  },

  build: function (iframeWindow, toolbox) {
    return new OptionsPanel(iframeWindow, toolbox);
  }
};

Tools.inspector = {
  id: "inspector",
  accesskey: l10n("inspector.accesskey"),
  ordinal: 1,
  icon: "chrome://devtools/skin/images/tool-inspector.svg",
  url: "chrome://devtools/content/inspector/inspector.xhtml",
  label: l10n("inspector.label"),
  panelLabel: l10n("inspector.panelLabel"),
  get tooltip() {
    return l10n("inspector.tooltip2",
    (osString == "Darwin" ? "Cmd+Opt+" : "Ctrl+Shift+") +
    l10n("inspector.commandkey"));
  },
  inMenu: true,
  commands: [
    "devtools/client/responsive.html/commands",
    "devtools/client/inspector/inspector-commands"
  ],

  preventClosingOnKey: true,
  onkey: function (panel, toolbox) {
    toolbox.highlighterUtils.togglePicker();
  },

  isTargetSupported: function (target) {
    return target.hasActor("inspector");
  },

  build: function (iframeWindow, toolbox) {
    return new InspectorPanel(iframeWindow, toolbox);
  }
};
Tools.webConsole = {
  id: "webconsole",
  accesskey: l10n("webConsoleCmd.accesskey"),
  ordinal: 2,
  oldWebConsoleURL: "chrome://devtools/content/webconsole/webconsole.xul",
  newWebConsoleURL: "chrome://devtools/content/webconsole/webconsole.html",
  get browserConsoleURL() {
    if (Services.prefs.getBoolPref("devtools.browserconsole.new-frontend-enabled")) {
      return "chrome://devtools/content/webconsole/browserconsole.xul";
    }
    return Tools.webConsole.oldWebConsoleURL;
  },
  icon: "chrome://devtools/skin/images/tool-webconsole.svg",
  label: l10n("ToolboxTabWebconsole.label"),
  menuLabel: l10n("MenuWebconsole.label"),
  panelLabel: l10n("ToolboxWebConsole.panelLabel"),
  get tooltip() {
    return l10n("ToolboxWebconsole.tooltip2",
    (osString == "Darwin" ? "Cmd+Opt+" : "Ctrl+Shift+") +
    l10n("webconsole.commandkey"));
  },
  inMenu: true,
  commands: "devtools/client/webconsole/console-commands",

  preventClosingOnKey: true,
  onkey: function (panel, toolbox) {
    if (toolbox.splitConsole) {
      return toolbox.focusConsoleInput();
    }

    panel.focusInput();
    return undefined;
  },

  isTargetSupported: function () {
    return true;
  },
  build: function (iframeWindow, toolbox) {
    return new WebConsolePanel(iframeWindow, toolbox);
  }
};
function switchWebconsole() {
  if (Services.prefs.getBoolPref("devtools.webconsole.new-frontend-enabled")) {
    Tools.webConsole.url = Tools.webConsole.newWebConsoleURL;
  } else {
    Tools.webConsole.url = Tools.webConsole.oldWebConsoleURL;
  }
}
switchWebconsole();

Services.prefs.addObserver(
  "devtools.webconsole.new-frontend-enabled",
  { observe: switchWebconsole }
);

Tools.jsdebugger = {
  id: "jsdebugger",
  accesskey: l10n("debuggerMenu.accesskey"),
  ordinal: 3,
  icon: "chrome://devtools/skin/images/tool-debugger.svg",
  url: "chrome://devtools/content/debugger/debugger.xul",
  label: l10n("ToolboxDebugger.label"),
  panelLabel: l10n("ToolboxDebugger.panelLabel"),
  get tooltip() {
    return l10n("ToolboxDebugger.tooltip2",
    (osString == "Darwin" ? "Cmd+Opt+" : "Ctrl+Shift+") +
    l10n("debugger.commandkey"));
  },
  inMenu: true,
  commands: "devtools/client/debugger/debugger-commands",

  isTargetSupported: function () {
    return true;
  },

  build: function (iframeWindow, toolbox) {
    return new DebuggerPanel(iframeWindow, toolbox);
  }
};

function switchDebugger() {
  if (Services.prefs.getBoolPref("devtools.debugger.new-debugger-frontend")) {
    Tools.jsdebugger.url = "chrome://devtools/content/debugger/new/index.html";
    Tools.jsdebugger.build = function (iframeWindow, toolbox) {
      return new NewDebuggerPanel(iframeWindow, toolbox);
    };
  } else {
    Tools.jsdebugger.url = "chrome://devtools/content/debugger/debugger.xul";
    Tools.jsdebugger.build = function (iframeWindow, toolbox) {
      return new DebuggerPanel(iframeWindow, toolbox);
    };
  }
}
switchDebugger();

Services.prefs.addObserver(
  "devtools.debugger.new-debugger-frontend",
  { observe: switchDebugger }
);

Tools.styleEditor = {
  id: "styleeditor",
  ordinal: 4,
  visibilityswitch: "devtools.styleeditor.enabled",
  accesskey: l10n("open.accesskey"),
  icon: "chrome://devtools/skin/images/tool-styleeditor.svg",
  url: "chrome://devtools/content/styleeditor/styleeditor.xul",
  label: l10n("ToolboxStyleEditor.label"),
  panelLabel: l10n("ToolboxStyleEditor.panelLabel"),
  get tooltip() {
    return l10n("ToolboxStyleEditor.tooltip3",
    "Shift+" + functionkey(l10n("styleeditor.commandkey")));
  },
  inMenu: true,
  commands: "devtools/client/styleeditor/styleeditor-commands",

  isTargetSupported: function (target) {
    return target.hasActor("styleSheets");
  },

  build: function (iframeWindow, toolbox) {
    return new StyleEditorPanel(iframeWindow, toolbox);
  }
};

Tools.shaderEditor = {
  id: "shadereditor",
  ordinal: 5,
  visibilityswitch: "devtools.shadereditor.enabled",
  icon: "chrome://devtools/skin/images/tool-shadereditor.svg",
  url: "chrome://devtools/content/shadereditor/shadereditor.xul",
  label: l10n("ToolboxShaderEditor.label"),
  panelLabel: l10n("ToolboxShaderEditor.panelLabel"),
  tooltip: l10n("ToolboxShaderEditor.tooltip"),

  isTargetSupported: function (target) {
    return target.hasActor("webgl") && !target.chrome;
  },

  build: function (iframeWindow, toolbox) {
    return new ShaderEditorPanel(iframeWindow, toolbox);
  }
};

Tools.canvasDebugger = {
  id: "canvasdebugger",
  ordinal: 6,
  visibilityswitch: "devtools.canvasdebugger.enabled",
  icon: "chrome://devtools/skin/images/tool-canvas.svg",
  url: "chrome://devtools/content/canvasdebugger/canvasdebugger.xul",
  label: l10n("ToolboxCanvasDebugger.label"),
  panelLabel: l10n("ToolboxCanvasDebugger.panelLabel"),
  tooltip: l10n("ToolboxCanvasDebugger.tooltip"),

  // Hide the Canvas Debugger in the Add-on Debugger and Browser Toolbox
  // (bug 1047520).
  isTargetSupported: function (target) {
    return target.hasActor("canvas") && !target.chrome;
  },

  build: function (iframeWindow, toolbox) {
    return new CanvasDebuggerPanel(iframeWindow, toolbox);
  }
};

Tools.performance = {
 id: "performance",
 ordinal: 7,
 icon: "chrome://devtools/skin/images/tool-profiler.svg",
 visibilityswitch: "devtools.performance.enabled",
 label: l10n("performance.label"),
 panelLabel: l10n("performance.panelLabel"),
 get tooltip() {
   return l10n("performance.tooltip", "Shift+" +
   functionkey(l10n("performance.commandkey")));
 },
 accesskey: l10n("performance.accesskey"),
 inMenu: true,
};

function switchPerformancePanel() {
  if (Services.prefs.getBoolPref("devtools.performance.new-panel-enabled", false)) {
    Tools.performance.url = "chrome://devtools/content/performance-new/perf.xhtml";
    Tools.performance.build = function (frame, target) {
      return new NewPerformancePanel(frame, target);
    };
    Tools.performance.isTargetSupported = function (target) {
     // Root actors are lazily initialized, so we can't check if the target has
     // the perf actor yet. Also this function is not async, so we can't initialize
     // the actor yet.
      return true;
    };
  } else {
    Tools.performance.url = "chrome://devtools/content/performance/performance.xul";
    Tools.performance.build = function (frame, target) {
      return new PerformancePanel(frame, target);
    };
    Tools.performance.isTargetSupported = function (target) {
      return target.hasActor("performance");
    };
  }
}
switchPerformancePanel();

Services.prefs.addObserver(
 "devtools.performance.new-panel-enabled",
 { observe: switchPerformancePanel }
);

Tools.memory = {
  id: "memory",
  ordinal: 8,
  icon: "chrome://devtools/skin/images/tool-memory.svg",
  url: "chrome://devtools/content/memory/memory.xhtml",
  visibilityswitch: "devtools.memory.enabled",
  label: l10n("memory.label"),
  panelLabel: l10n("memory.panelLabel"),
  tooltip: l10n("memory.tooltip"),

  isTargetSupported: function (target) {
    return target.getTrait("heapSnapshots") && !target.isAddon;
  },

  build: function (frame, target) {
    return new MemoryPanel(frame, target);
  }
};

Tools.netMonitor = {
  id: "netmonitor",
  accesskey: l10n("netmonitor.accesskey"),
  ordinal: 9,
  visibilityswitch: "devtools.netmonitor.enabled",
  icon: "chrome://devtools/skin/images/tool-network.svg",
  url: "chrome://devtools/content/netmonitor/index.html",
  label: l10n("netmonitor.label"),
  panelLabel: l10n("netmonitor.panelLabel"),
  get tooltip() {
    return l10n("netmonitor.tooltip2",
    (osString == "Darwin" ? "Cmd+Opt+" : "Ctrl+Shift+") +
    l10n("netmonitor.commandkey"));
  },
  inMenu: true,

  isTargetSupported: function (target) {
    return target.getTrait("networkMonitor");
  },

  build: function (iframeWindow, toolbox) {
    return new NetMonitorPanel(iframeWindow, toolbox);
  }
};

Tools.storage = {
  id: "storage",
  ordinal: 10,
  accesskey: l10n("storage.accesskey"),
  visibilityswitch: "devtools.storage.enabled",
  icon: "chrome://devtools/skin/images/tool-storage.svg",
  url: "chrome://devtools/content/storage/storage.xul",
  label: l10n("storage.label"),
  menuLabel: l10n("storage.menuLabel"),
  panelLabel: l10n("storage.panelLabel"),
  get tooltip() {
    return l10n("storage.tooltip3", "Shift+" +
    functionkey(l10n("storage.commandkey")));
  },
  inMenu: true,

  isTargetSupported: function (target) {
    return target.isLocalTab ||
           (target.hasActor("storage") && target.getTrait("storageInspector"));
  },

  build: function (iframeWindow, toolbox) {
    return new StoragePanel(iframeWindow, toolbox);
  }
};

Tools.webAudioEditor = {
  id: "webaudioeditor",
  ordinal: 11,
  visibilityswitch: "devtools.webaudioeditor.enabled",
  icon: "chrome://devtools/skin/images/tool-webaudio.svg",
  url: "chrome://devtools/content/webaudioeditor/webaudioeditor.xul",
  label: l10n("ToolboxWebAudioEditor1.label"),
  panelLabel: l10n("ToolboxWebAudioEditor1.panelLabel"),
  tooltip: l10n("ToolboxWebAudioEditor1.tooltip"),

  isTargetSupported: function (target) {
    return !target.chrome && target.hasActor("webaudio");
  },

  build: function (iframeWindow, toolbox) {
    return new WebAudioEditorPanel(iframeWindow, toolbox);
  }
};

Tools.scratchpad = {
  id: "scratchpad",
  ordinal: 12,
  visibilityswitch: "devtools.scratchpad.enabled",
  icon: "chrome://devtools/skin/images/tool-scratchpad.svg",
  url: "chrome://devtools/content/scratchpad/scratchpad.xul",
  label: l10n("scratchpad.label"),
  panelLabel: l10n("scratchpad.panelLabel"),
  tooltip: l10n("scratchpad.tooltip"),
  inMenu: false,
  commands: "devtools/client/scratchpad/scratchpad-commands",

  isTargetSupported: function (target) {
    return target.hasActor("console");
  },

  build: function (iframeWindow, toolbox) {
    return new ScratchpadPanel(iframeWindow, toolbox);
  }
};

Tools.dom = {
  id: "dom",
  accesskey: l10n("dom.accesskey"),
  ordinal: 13,
  visibilityswitch: "devtools.dom.enabled",
  icon: "chrome://devtools/skin/images/tool-dom.svg",
  url: "chrome://devtools/content/dom/dom.html",
  label: l10n("dom.label"),
  panelLabel: l10n("dom.panelLabel"),
  get tooltip() {
    return l10n("dom.tooltip",
      (osString == "Darwin" ? "Cmd+Opt+" : "Ctrl+Shift+") +
      l10n("dom.commandkey"));
  },
  inMenu: true,

  isTargetSupported: function (target) {
    return target.getTrait("webConsoleCommands");
  },

  build: function (iframeWindow, toolbox) {
    return new DomPanel(iframeWindow, toolbox);
  }
};

var defaultTools = [
  Tools.options,
  Tools.webConsole,
  Tools.inspector,
  Tools.jsdebugger,
  Tools.styleEditor,
  Tools.shaderEditor,
  Tools.canvasDebugger,
  Tools.webAudioEditor,
  Tools.performance,
  Tools.netMonitor,
  Tools.storage,
  Tools.scratchpad,
  Tools.memory,
  Tools.dom,
];

exports.defaultTools = defaultTools;

Tools.darkTheme = {
  id: "dark",
  label: l10n("options.darkTheme.label2"),
  ordinal: 1,
  stylesheets: ["chrome://devtools/skin/dark-theme.css"],
  classList: ["theme-dark"],
};

Tools.lightTheme = {
  id: "light",
  label: l10n("options.lightTheme.label2"),
  ordinal: 2,
  stylesheets: ["chrome://devtools/skin/light-theme.css"],
  classList: ["theme-light"],
};

Tools.firebugTheme = {
  id: "firebug",
  label: l10n("options.firebugTheme.label2"),
  ordinal: 3,
  stylesheets: ["chrome://devtools/skin/firebug-theme.css"],
  classList: ["theme-light", "theme-firebug"],
};

exports.defaultThemes = [
  Tools.darkTheme,
  Tools.lightTheme,
  Tools.firebugTheme,
];

// White-list buttons that can be toggled to prevent adding prefs for
// addons that have manually inserted toolbarbuttons into DOM.
// (By default, supported target is only local tab)
exports.ToolboxButtons = [
  { id: "command-button-splitconsole",
    description: l10n("toolbox.buttons.splitconsole", "Esc"),
    isTargetSupported: target => !target.isAddon,
    onClick(event, toolbox) {
      toolbox.toggleSplitConsole();
    },
    isChecked(toolbox) {
      return toolbox.splitConsole;
    },
    setup(toolbox, onChange) {
      toolbox.on("split-console", onChange);
    },
    teardown(toolbox, onChange) {
      toolbox.off("split-console", onChange);
    }
  },
  { id: "command-button-paintflashing",
    description: l10n("toolbox.buttons.paintflashing"),
    isTargetSupported: target => target.isLocalTab,
    onClick(event, toolbox) {
      CommandUtils.executeOnTarget(toolbox.target, "paintflashing toggle");
    },
    isChecked(toolbox) {
      return CommandState.isEnabledForTarget(toolbox.target, "paintflashing");
    },
    setup(toolbox, onChange) {
      CommandState.on("changed", onChange);
    },
    teardown(toolbox, onChange) {
      CommandState.off("changed", onChange);
    }
  },
  { id: "command-button-scratchpad",
    description: l10n("toolbox.buttons.scratchpad"),
    isTargetSupported: target => target.isLocalTab,
    onClick(event, toolbox) {
      ScratchpadManager.openScratchpad();
    }
  },
  { id: "command-button-responsive",
    description: l10n("toolbox.buttons.responsive",
                      osString == "Darwin" ? "Cmd+Opt+M" : "Ctrl+Shift+M"),
    isTargetSupported: target => target.isLocalTab,
    onClick(event, toolbox) {
      let tab = toolbox.target.tab;
      let browserWindow = tab.ownerDocument.defaultView;
      ResponsiveUIManager.handleGcliCommand(browserWindow, tab,
        "resize toggle", null);
    },
    isChecked(toolbox) {
      if (!toolbox.target.tab) {
        return false;
      }
      return ResponsiveUIManager.isActiveForTab(toolbox.target.tab);
    },
    setup(toolbox, onChange) {
      ResponsiveUIManager.on("on", onChange);
      ResponsiveUIManager.on("off", onChange);
    },
    teardown(toolbox, onChange) {
      ResponsiveUIManager.off("on", onChange);
      ResponsiveUIManager.off("off", onChange);
    }
  },
  { id: "command-button-screenshot",
    description: l10n("toolbox.buttons.screenshot"),
    isTargetSupported: target => target.isLocalTab,
    onClick(event, toolbox) {
      // Special case for screenshot button to check for clipboard preference
      const clipboardEnabled = Services.prefs
        .getBoolPref("devtools.screenshot.clipboard.enabled");
      let args = "--fullpage --file";
      if (clipboardEnabled) {
        args += " --clipboard";
      }
      CommandUtils.executeOnTarget(toolbox.target, "screenshot " + args);
    }
  },
  { id: "command-button-rulers",
    description: l10n("toolbox.buttons.rulers"),
    isTargetSupported: target => target.isLocalTab,
    onClick(event, toolbox) {
      CommandUtils.executeOnTarget(toolbox.target, "rulers");
    },
    isChecked(toolbox) {
      return CommandState.isEnabledForTarget(toolbox.target, "rulers");
    },
    setup(toolbox, onChange) {
      CommandState.on("changed", onChange);
    },
    teardown(toolbox, onChange) {
      CommandState.off("changed", onChange);
    }
  },
  { id: "command-button-measure",
    description: l10n("toolbox.buttons.measure"),
    isTargetSupported: target => target.isLocalTab,
    onClick(event, toolbox) {
      CommandUtils.executeOnTarget(toolbox.target, "measure");
    },
    isChecked(toolbox) {
      return CommandState.isEnabledForTarget(toolbox.target, "measure");
    },
    setup(toolbox, onChange) {
      CommandState.on("changed", onChange);
    },
    teardown(toolbox, onChange) {
      CommandState.off("changed", onChange);
    }
  },
];

/**
 * Lookup l10n string from a string bundle.
 *
 * @param {string} name
 *        The key to lookup.
 * @param {string} arg
 *        Optional format argument.
 * @returns A localized version of the given key.
 */
function l10n(name, arg) {
  try {
    return arg ? L10N.getFormatStr(name, arg) : L10N.getStr(name);
  } catch (ex) {
    console.log("Error reading '" + name + "'");
    throw new Error("l10n error with " + name);
  }
}

function functionkey(shortkey) {
  return shortkey.split("_")[1];
}
PK
!<�QL�ffEchrome/devtools/modules/devtools/client/dom/content/actions/filter.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 constants = require("../constants");

/**
 * Used to filter DOM panel content.
 */
function setVisibilityFilter(filter) {
  return {
    filter: filter,
    type: constants.SET_VISIBILITY_FILTER,
  };
}

// Exports from this module
exports.setVisibilityFilter = setVisibilityFilter;
PK
!<F�����Dchrome/devtools/modules/devtools/client/dom/content/actions/grips.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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/. */
 /* globals DomProvider */
"use strict";

const constants = require("../constants");

/**
 * Used to fetch grip prototype and properties from the backend.
 */
function requestProperties(grip) {
  return {
    grip: grip,
    type: constants.FETCH_PROPERTIES,
    status: "start",
    error: false
  };
}

/**
 * Executed when grip properties are received from the backend.
 */
function receiveProperties(grip, response, error) {
  return {
    grip: grip,
    type: constants.FETCH_PROPERTIES,
    status: "end",
    response: response,
    error: error
  };
}

/**
 * Used to get properties from the backend and fire an action
 * when they are received.
 */
function fetchProperties(grip) {
  return dispatch => {
    // dispatch(requestProperties(grip));

    // Use 'DomProvider' object exposed from the chrome scope.
    return DomProvider.getPrototypeAndProperties(grip).then(response => {
      dispatch(receiveProperties(grip, response));
    });
  };
}

// Exports from this module
exports.requestProperties = requestProperties;
exports.receiveProperties = receiveProperties;
exports.fetchProperties = fetchProperties;
PK
!<i�

Jchrome/devtools/modules/devtools/client/dom/content/components/dom-tree.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

// React & Redux
const { Component, createFactory } = require("devtools/client/shared/vendor/react");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");

const { connect } = require("devtools/client/shared/vendor/react-redux");

const TreeView = createFactory(require("devtools/client/shared/components/tree/TreeView"));
// Reps
const { REPS, MODE } = require("devtools/client/shared/components/reps/reps");
const { Rep } = REPS;

const Grip = REPS.Grip;
// DOM Panel
const { GripProvider } = require("../grip-provider");

const { DomDecorator } = require("../dom-decorator");

/**
 * Renders DOM panel tree.
 */
class DomTree extends Component {
  static get propTypes() {
    return {
      dispatch: PropTypes.func.isRequired,
      filter: PropTypes.string,
      grips: PropTypes.object,
      object: PropTypes.any,
      openLink: PropTypes.func,
    };
  }

  constructor(props) {
    super(props);
    this.onFilter = this.onFilter.bind(this);
  }

  /**
   * Filter DOM properties. Return true if the object
   * should be visible in the tree.
   */
  onFilter(object) {
    if (!this.props.filter) {
      return true;
    }

    return (object.name && object.name.indexOf(this.props.filter) > -1);
  }

  /**
   * Render DOM panel content
   */
  render() {
    let {
      dispatch,
      grips,
      object,
      openLink,
    } = this.props;

    let columns = [{
      "id": "value"
    }];

    // This is the integration point with Reps. The DomTree is using
    // Reps to render all values. The code also specifies default rep
    // used for data types that don't have its own specific template.
    let renderValue = props => {
      return Rep(Object.assign({}, props, {
        defaultRep: Grip,
        cropLimit: 50,
      }));
    };

    return (
      TreeView({
        columns,
        decorator: new DomDecorator(),
        mode: MODE.SHORT,
        object,
        onFilter: this.onFilter,
        openLink,
        provider: new GripProvider(grips, dispatch),
        renderValue,
      })
    );
  }
}

const mapStateToProps = (state) => {
  return {
    grips: state.grips,
    filter: state.filter
  };
};

// Exports from this module
module.exports = connect(mapStateToProps)(DomTree);
PK
!<��1�ooLchrome/devtools/modules/devtools/client/dom/content/components/main-frame.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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/. */
/* globals DomProvider */

"use strict";

// React & Redux
const { Component, createFactory } = 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");

const { connect } = require("devtools/client/shared/vendor/react-redux");
// DOM Panel
const DomTree = createFactory(require("./dom-tree"));

const MainToolbar = createFactory(require("./main-toolbar"));
// Shortcuts
const { div } = dom;

/**
 * Renders basic layout of the DOM panel. The DOM panel content consists
 * from two main parts: toolbar and tree.
 */
class MainFrame extends Component {
  static get propTypes() {
    return {
      dispatch: PropTypes.func.isRequired,
      filter: PropTypes.string,
      object: PropTypes.any,
    };
  }

  /**
   * Render DOM panel content
   */
  render() {
    let {
      filter,
      object,
    } = this.props;

    return (
      div({className: "mainFrame"},
        MainToolbar({
          dispatch: this.props.dispatch,
          object: this.props.object
        }),
        div({className: "treeTableBox"},
          DomTree({
            filter,
            object,
            openLink: url => DomProvider.openLink(url),
          })
        )
      )
    );
  }
}

// Transform state into props
// Note: use https://github.com/faassen/reselect for better performance.
const mapStateToProps = (state) => {
  return {
    filter: state.filter
  };
};

// Exports from this module
module.exports = connect(mapStateToProps)(MainFrame);
PK
!<ˬ�,  Nchrome/devtools/modules/devtools/client/dom/content/components/main-toolbar.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

// React
const { Component, createFactory } = require("devtools/client/shared/vendor/react");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");

const { l10n } = require("../utils");
// Reps
const { createFactories } = require("devtools/client/shared/react-utils");

const { Toolbar, ToolbarButton } = createFactories(require("devtools/client/jsonview/components/reps/Toolbar"));

// DOM Panel
const SearchBox = createFactory(require("devtools/client/shared/components/SearchBox"));
// Actions
const { fetchProperties } = require("../actions/grips");

const { setVisibilityFilter } = require("../actions/filter");

/**
 * This template is responsible for rendering a toolbar
 * within the 'Headers' panel.
 */
class MainToolbar extends Component {
  static get propTypes() {
    return {
      object: PropTypes.any.isRequired,
      dispatch: PropTypes.func.isRequired,
    };
  }

  constructor(props) {
    super(props);
    this.onRefresh = this.onRefresh.bind(this);
    this.onSearch = this.onSearch.bind(this);
  }

  onRefresh() {
    this.props.dispatch(fetchProperties(this.props.object));
  }

  onSearch(value) {
    this.props.dispatch(setVisibilityFilter(value));
  }

  render() {
    return (
      Toolbar({},
        ToolbarButton({
          key: "refresh",
          className: "refresh devtools-button",
          id: "dom-refresh-button",
          title: l10n.getStr("dom.refresh"),
          onClick: this.onRefresh
        }),
        SearchBox({
          key: "filter",
          delay: 250,
          onChange: this.onSearch,
          placeholder: l10n.getStr("dom.filterDOMPanel"),
          type: "filter"
        })
      )
    );
  }
}

// Exports from this module
module.exports = MainToolbar;
PK
!<i�梭�@chrome/devtools/modules/devtools/client/dom/content/constants.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

exports.FETCH_PROPERTIES = "FETCH_PROPERTIES";
exports.SET_VISIBILITY_FILTER = "SET_VISIBILITY_FILTER";
PK
!<�����Dchrome/devtools/modules/devtools/client/dom/content/dom-decorator.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 { Property } = require("./reducers/grips");

// Implementation

function DomDecorator() {
}

/**
 * Decorator for DOM panel tree component. It's responsible for
 * appending an icon to read only properties.
 */
DomDecorator.prototype = {
  getRowClass: function (object) {
    if (object instanceof Property) {
      let value = object.value;
      let names = [];

      if (value.enumerable) {
        names.push("enumerable");
      }
      if (value.writable) {
        names.push("writable");
      }
      if (value.configurable) {
        names.push("configurable");
      }

      return names;
    }

    return null;
  },

  /**
   * Return custom React template for specified object. The template
   * might depend on specified column.
   */
  getValueRep: function (value, colId) {
  }
};

// Exports from this module
exports.DomDecorator = DomDecorator;
PK
!<�F�5

@chrome/devtools/modules/devtools/client/dom/content/dom-view.css/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/******************************************************************************/
/* General */

body {
  padding: 0;
  margin: 0;
  overflow: hidden;
}

.mainFrame {
  display: flex;
  flex-direction: column;
  height: 100vh;
}

.mainFrame > .treeTableBox {
  flex: 1 1 auto;
  overflow: auto;
}

/******************************************************************************/
/* TreeView Customization */

.treeTable {
  width: 100%;
}

/* Space for read only properties icon */
.treeTable td.treeValueCell {
  padding-inline-start: 16px;
}

.treeTable .treeLabel,
.treeTable td.treeValueCell .objectBox {
  direction: ltr; /* Don't change the direction of english labels */
}

/* Read only properties have a padlock icon */
.treeTable tr:not(.writable) td.treeValueCell {
  background: url("chrome://devtools/skin/images/firebug/read-only.svg") no-repeat;
  background-position: 1px 5px;
  background-size: 10px 10px;
}

.treeTable tr:not(.writable) td.treeValueCell:dir(rtl) {
  background-position-x: right 1px;
}

/* Non-enumerable properties are grayed out */
.treeTable tr:not(.enumerable) td.treeValueCell {
  opacity: 0.7;
}

.treeTable > tbody > tr > td {
  border-bottom: 1px solid #EFEFEF;
}

/* Label Types */
.treeTable .userLabel,
.treeTable .userClassLabel,
.treeTable .userFunctionLabel {
  font-weight: bold;
}

.treeTable .userLabel {
  color: #000000;
}

.treeTable .userClassLabel {
  color: #E90000;
}

.treeTable .userFunctionLabel {
  color: #025E2A;
}

.treeTable .domLabel {
  color: #000000;
}

.treeTable .domClassLabel {
  color: #E90000;
}

.treeTable .domFunctionLabel {
  color: #025E2A;
}

.treeTable .ordinalLabel {
  color: SlateBlue;
  font-weight: bold;
}

/******************************************************************************/
/* Refresh button */
#dom-refresh-button::before {
  background-image: url("chrome://devtools/skin/images/reload.svg");
}

/******************************************************************************/
/* Search box */
.devtools-searchbox {
  margin-inline-start: auto; /* Align to the right */
  flex: none; /* Don't flex */
}

/******************************************************************************/
/* Theme Dark */

.theme-dark .treeTable > tbody > tr > td {
  border-bottom: none;
}

.theme-dark body {
  background-color: var(--theme-body-background);
}
PK
!<�x��?chrome/devtools/modules/devtools/client/dom/content/dom-view.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

// React & Redux
const React = require("devtools/client/shared/vendor/react");
const ReactDOM = require("devtools/client/shared/vendor/react-dom");
const { Provider } = require("devtools/client/shared/vendor/react-redux");
const { combineReducers } = require("devtools/client/shared/vendor/redux");

// DOM Panel
const MainFrame = React.createFactory(require("./components/main-frame"));

// Store
const createStore = require("devtools/client/shared/redux/create-store")({
  log: false
});

const { reducers } = require("./reducers/index");
const store = createStore(combineReducers(reducers));

/**
 * This object represents view of the DOM panel and is responsible
 * for rendering the content. It renders the top level ReactJS
 * component: the MainFrame.
 */
function DomView(localStore) {
  addEventListener("devtools/chrome/message",
    this.onMessage.bind(this), true);

  // Make it local so, tests can access it.
  this.store = localStore;
}

DomView.prototype = {
  initialize: function (rootGrip) {
    let content = document.querySelector("#content");
    let mainFrame = MainFrame({
      object: rootGrip,
    });

    // Render top level component
    let provider = React.createElement(Provider, {
      store: this.store
    }, mainFrame);

    this.mainFrame = ReactDOM.render(provider, content);
  },

  onMessage: function (event) {
    let data = event.data;
    let method = data.type;

    if (typeof this[method] == "function") {
      this[method](data.args);
    }
  },
};

// Construct DOM panel view object and expose it to tests.
// Tests can access it through: |panel.panelWin.view|
window.view = new DomView(store);
PK
!<̋B

Dchrome/devtools/modules/devtools/client/dom/content/grip-provider.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 { fetchProperties } = require("./actions/grips");
const { Property } = require("./reducers/grips");

// Implementation
function GripProvider(grips, dispatch) {
  this.grips = grips;
  this.dispatch = dispatch;
}

/**
 * This object provides data for the tree displayed in the tooltip
 * content.
 */
GripProvider.prototype = {
  /**
   * Fetches properties from the backend. These properties might be
   * displayed as child objects in e.g. a tree UI widget.
   */
  getChildren: function (object) {
    let grip = object;
    if (object instanceof Property) {
      grip = this.getValue(object);
    }

    if (!grip || !grip.actor) {
      return [];
    }

    let props = this.grips.get(grip.actor);
    if (!props) {
      // Fetch missing data from the backend. Returning a promise
      // from data provider causes the tree to show a spinner.
      return this.dispatch(fetchProperties(grip));
    }

    return props;
  },

  hasChildren: function (object) {
    if (object instanceof Property) {
      let value = this.getValue(object);
      if (!value) {
        return false;
      }

      let hasChildren = value.ownPropertyLength > 0;

      if (value.preview) {
        hasChildren = hasChildren || value.preview.ownPropertiesLength > 0;
      }

      if (value.preview) {
        let preview = value.preview;
        let k = preview.kind;
        let objectsWithProps = ["DOMNode", "ObjectWithURL"];
        hasChildren = hasChildren || (objectsWithProps.includes(k));
        hasChildren = hasChildren || (k == "ArrayLike" && preview.length > 0);
      }

      return (value.type == "object" && hasChildren);
    }

    return null;
  },

  getValue: function (object) {
    if (object instanceof Property) {
      let value = object.value;
      return (typeof value.value != "undefined") ? value.value :
        value.getterValue;
    }

    return object;
  },

  getLabel: function (object) {
    return (object instanceof Property) ? object.name : null;
  },

  getKey: function (object) {
    return (object instanceof Property) ? object.key : null;
  },

  getType: function (object) {
    return object.class ? object.class : "";
  },
};

// Exports from this module
exports.GripProvider = GripProvider;
PK
!<�ײ���Fchrome/devtools/modules/devtools/client/dom/content/reducers/filter.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 constants = require("../constants");

/**
 * Initial state definition
 */
function getInitialState() {
  return "";
}

/**
 * Filter displayed object properties.
 */
function filter(state = getInitialState(), action) {
  if (action.type == constants.SET_VISIBILITY_FILTER) {
    return action.filter;
  }

  return state;
}

// Exports from this module
exports.filter = filter;
PK
!<1\6ӳ�Echrome/devtools/modules/devtools/client/dom/content/reducers/grips.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 constants = require("../constants");

/**
 * Initial state definition
 */
function getInitialState() {
  return new Map();
}

/**
 * Maintain a cache of received grip responses from the backend.
 */
function grips(state = getInitialState(), action) {
  // This reducer supports only one action, fetching actor properties
  // from the backend so, bail out if we are dealing with any other
  // action.
  if (action.type != constants.FETCH_PROPERTIES) {
    return state;
  }

  switch (action.status) {
    case "start":
      return onRequestProperties(state, action);
    case "end":
      return onReceiveProperties(state, action);
  }

  return state;
}

/**
 * Handle requestProperties action
 */
function onRequestProperties(state, action) {
  return state;
}

/**
 * Handle receiveProperties action
 */
function onReceiveProperties(cache, action) {
  let response = action.response;
  let from = response.from;
  let className = action.grip.class;

  // Properly deal with getters.
  mergeProperties(response);

  // Compute list of requested children.
  let previewProps = response.preview ? response.preview.ownProperties : null;
  let ownProps = response.ownProperties || previewProps || [];

  let props = Object.keys(ownProps).map(key => {
    // Array indexes as a special case. We convert any keys that are string
    // representations of integers to integers.
    if (className === "Array" && isInteger(key)) {
      key = parseInt(key, 10);
    }
    return new Property(key, ownProps[key], key);
  });

  props.sort(sortName);

  // Return new state/map.
  let newCache = new Map(cache);
  newCache.set(from, props);

  return newCache;
}

// Helpers

function mergeProperties(response) {
  let { ownProperties } = response;

  // 'safeGetterValues' is new and isn't necessary defined on old grips.
  let safeGetterValues = response.safeGetterValues || {};

  // Merge the safe getter values into one object such that we can use it
  // in variablesView.
  for (let name of Object.keys(safeGetterValues)) {
    if (name in ownProperties) {
      let { getterValue, getterPrototypeLevel } = safeGetterValues[name];
      ownProperties[name].getterValue = getterValue;
      ownProperties[name].getterPrototypeLevel = getterPrototypeLevel;
    } else {
      ownProperties[name] = safeGetterValues[name];
    }
  }
}

function sortName(a, b) {
  // Display non-enumerable properties at the end.
  if (!a.value.enumerable && b.value.enumerable) {
    return 1;
  }
  if (a.value.enumerable && !b.value.enumerable) {
    return -1;
  }
  return a.name > b.name ? 1 : -1;
}

function isInteger(n) {
  // We use parseInt(n, 10) == n to disregard scientific notation e.g. "3e24"
  return isFinite(n) && parseInt(n, 10) == n;
}

function Property(name, value, key) {
  this.name = name;
  this.value = value;
  this.key = key;
}

// Exports from this module
exports.grips = grips;
exports.Property = Property;
PK
!<�Fo���Echrome/devtools/modules/devtools/client/dom/content/reducers/index.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 { grips } = require("./grips");
const { filter } = require("./filter");

exports.reducers = {
  grips,
  filter,
};
PK
!<i�"��<chrome/devtools/modules/devtools/client/dom/content/utils.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

/**
 * The default localization just returns the last part of the key
 * (all after the last dot).
 */
const DefaultL10N = {
  getStr: function (key) {
    let index = key.lastIndexOf(".");
    return key.substr(index + 1);
  }
};

/**
 * The 'l10n' object is set by main.js in case the DOM panel content
 * runs within a scope with chrome privileges.
 *
 * Note that DOM panel content can also run within a scope with no chrome
 * privileges, e.g. in an iframe with type 'content' or in a browser tab,
 * which allows using our own tools for development.
 */
exports.l10n = window.l10n || DefaultL10N;
PK
!<���'��8chrome/devtools/modules/devtools/client/dom/dom-panel.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 { Cu } = require("chrome");
const ObjectClient = require("devtools/shared/client/object-client");

const defer = require("devtools/shared/defer");
const EventEmitter = require("devtools/shared/old-event-emitter");
const { Task } = require("devtools/shared/task");

/**
 * This object represents DOM panel. It's responsibility is to
 * render Document Object Model of the current debugger target.
 */
function DomPanel(iframeWindow, toolbox) {
  this.panelWin = iframeWindow;
  this._toolbox = toolbox;

  this.onTabNavigated = this.onTabNavigated.bind(this);
  this.onContentMessage = this.onContentMessage.bind(this);
  this.onPanelVisibilityChange = this.onPanelVisibilityChange.bind(this);

  this.pendingRequests = new Map();

  EventEmitter.decorate(this);
}

DomPanel.prototype = {
  /**
   * Open is effectively an asynchronous constructor.
   *
   * @return object
   *         A promise that is resolved when the DOM panel completes opening.
   */
  open: Task.async(function* () {
    if (this._opening) {
      return this._opening;
    }

    let deferred = defer();
    this._opening = deferred.promise;

    // Local monitoring needs to make the target remote.
    if (!this.target.isRemote) {
      yield this.target.makeRemote();
    }

    this.initialize();

    this.isReady = true;
    this.emit("ready");
    deferred.resolve(this);

    return this._opening;
  }),

  // Initialization

  initialize: function () {
    this.panelWin.addEventListener("devtools/content/message",
      this.onContentMessage, true);

    this.target.on("navigate", this.onTabNavigated);
    this._toolbox.on("select", this.onPanelVisibilityChange);

    // Export provider object with useful API for DOM panel.
    let provider = {
      getPrototypeAndProperties: this.getPrototypeAndProperties.bind(this),
      openLink: this.openLink.bind(this),
    };

    exportIntoContentScope(this.panelWin, provider, "DomProvider");

    this.shouldRefresh = true;
  },

  destroy: Task.async(function* () {
    if (this._destroying) {
      return this._destroying;
    }

    let deferred = defer();
    this._destroying = deferred.promise;

    this.target.off("navigate", this.onTabNavigated);
    this._toolbox.off("select", this.onPanelVisibilityChange);

    this.emit("destroyed");

    deferred.resolve();
    return this._destroying;
  }),

  // Events

  refresh: function () {
    // Do not refresh if the panel isn't visible.
    if (!this.isPanelVisible()) {
      return;
    }

    // Do not refresh if it isn't necessary.
    if (!this.shouldRefresh) {
      return;
    }

    // Alright reset the flag we are about to refresh the panel.
    this.shouldRefresh = false;

    this.getRootGrip().then(rootGrip => {
      this.postContentMessage("initialize", rootGrip);
    });
  },

  /**
   * Make sure the panel is refreshed when the page is reloaded.
   * The panel is refreshed immediately if it's currently selected
   * or lazily  when the user actually selects it.
   */
  onTabNavigated: function () {
    this.shouldRefresh = true;
    this.refresh();
  },

  /**
   * Make sure the panel is refreshed (if needed) when it's selected.
   */
  onPanelVisibilityChange: function () {
    this.refresh();
  },

  // Helpers

  /**
   * Return true if the DOM panel is currently selected.
   */
  isPanelVisible: function () {
    return this._toolbox.currentToolId === "dom";
  },

  getPrototypeAndProperties: function (grip) {
    let deferred = defer();

    if (!grip.actor) {
      console.error("No actor!", grip);
      deferred.reject(new Error("Failed to get actor from grip."));
      return deferred.promise;
    }

    // Bail out if target doesn't exist (toolbox maybe closed already).
    if (!this.target) {
      return deferred.promise;
    }

    // If a request for the grips is already in progress
    // use the same promise.
    let request = this.pendingRequests.get(grip.actor);
    if (request) {
      return request;
    }

    let client = new ObjectClient(this.target.client, grip);
    client.getPrototypeAndProperties(response => {
      this.pendingRequests.delete(grip.actor, deferred.promise);
      deferred.resolve(response);

      // Fire an event about not having any pending requests.
      if (!this.pendingRequests.size) {
        this.emit("no-pending-requests");
      }
    });

    this.pendingRequests.set(grip.actor, deferred.promise);

    return deferred.promise;
  },

  openLink: function (url) {
    let parentDoc = this._toolbox.doc;
    let iframe = parentDoc.getElementById("this._toolbox");
    let top = iframe.ownerDocument.defaultView.top;
    top.openUILinkIn(url, "tab");
  },

  getRootGrip: function () {
    let deferred = defer();

    // Attach Console. It might involve RDP communication, so wait
    // asynchronously for the result
    this.target.activeConsole.evaluateJSAsync("window", res => {
      deferred.resolve(res.result);
    });

    return deferred.promise;
  },

  postContentMessage: function (type, args) {
    let data = {
      type: type,
      args: args,
    };

    let event = new this.panelWin.MessageEvent("devtools/chrome/message", {
      bubbles: true,
      cancelable: true,
      data: data,
    });

    this.panelWin.dispatchEvent(event);
  },

  onContentMessage: function (event) {
    let data = event.data;
    let method = data.type;
    if (typeof this[method] == "function") {
      this[method](data.args);
    }
  },

  get target() {
    return this._toolbox.target;
  },
};

// Helpers

function exportIntoContentScope(win, obj, defineAs) {
  let clone = Cu.createObjectIn(win, {
    defineAs: defineAs
  });

  let props = Object.getOwnPropertyNames(obj);
  for (let i = 0; i < props.length; i++) {
    let propName = props[i];
    let propValue = obj[propName];
    if (typeof propValue == "function") {
      Cu.exportFunction(propValue, clone, {
        defineAs: propName
      });
    }
  }
}

// Exports from this module
exports.DomPanel = DomPanel;
PK
!<����-4-4Dchrome/devtools/modules/devtools/client/framework/ToolboxProcess.jsm/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 DBG_XUL = "chrome://devtools/content/framework/toolbox-process-window.xul";
const CHROME_DEBUGGER_PROFILE_NAME = "chrome_debugger_profile";

const { require, DevToolsLoader } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");

ChromeUtils.defineModuleGetter(this, "Subprocess", "resource://gre/modules/Subprocess.jsm");
XPCOMUtils.defineLazyGetter(this, "Telemetry", function () {
  return require("devtools/client/shared/telemetry");
});
XPCOMUtils.defineLazyGetter(this, "EventEmitter", function () {
  return require("devtools/shared/old-event-emitter");
});
XPCOMUtils.defineLazyGetter(this, "system", function () {
  return require("devtools/shared/system");
});
const promise = require("promise");
const Services = require("Services");

this.EXPORTED_SYMBOLS = ["BrowserToolboxProcess"];

var processes = new Set();

/**
 * Constructor for creating a process that will hold a chrome toolbox.
 *
 * @param function onClose [optional]
 *        A function called when the process stops running.
 * @param function onRun [optional]
 *        A function called when the process starts running.
 * @param object options [optional]
 *        An object with properties for configuring BrowserToolboxProcess.
 */
this.BrowserToolboxProcess = function BrowserToolboxProcess(onClose, onRun, options) {
  let emitter = new EventEmitter();
  this.on = emitter.on.bind(emitter);
  this.off = emitter.off.bind(emitter);
  this.once = emitter.once.bind(emitter);
  // Forward any events to the shared emitter.
  this.emit = function (...args) {
    emitter.emit(...args);
    BrowserToolboxProcess.emit(...args);
  };

  // If first argument is an object, use those properties instead of
  // all three arguments
  if (typeof onClose === "object") {
    if (onClose.onClose) {
      this.once("close", onClose.onClose);
    }
    if (onClose.onRun) {
      this.once("run", onClose.onRun);
    }
    this._options = onClose;
  } else {
    if (onClose) {
      this.once("close", onClose);
    }
    if (onRun) {
      this.once("run", onRun);
    }
    this._options = options || {};
  }

  this._telemetry = new Telemetry();

  this._onConnectionChange = this._onConnectionChange.bind(this);

  this.close = this.close.bind(this);
  Services.obs.addObserver(this.close, "quit-application");
  this._initServer();
  this._initProfile();
  this._create();

  processes.add(this);
};

EventEmitter.decorate(BrowserToolboxProcess);

/**
 * Initializes and starts a chrome toolbox process.
 * @return object
 */
BrowserToolboxProcess.init = function (onClose, onRun, options) {
  if (!Services.prefs.getBoolPref("devtools.chrome.enabled") ||
      !Services.prefs.getBoolPref("devtools.debugger.remote-enabled")) {
    console.error("Could not start Browser Toolbox, you need to enable it.");
    return null;
  }
  return new BrowserToolboxProcess(onClose, onRun, options);
};

/**
 * Figure out if there are any open Browser Toolboxes that'll need to be restored.
 * @return bool
 */
BrowserToolboxProcess.getBrowserToolboxSessionState = function () {
  for (let process of processes.values()) {
    // Don't worry about addon toolboxes, we only want to restore the Browser Toolbox.
    if (!process._options || !process._options.addonID) {
      return true;
    }
  }
  return false;
};

/**
 * Passes a set of options to the BrowserAddonActors for the given ID.
 *
 * @param id string
 *        The ID of the add-on to pass the options to
 * @param options object
 *        The options.
 * @return a promise that will be resolved when complete.
 */
BrowserToolboxProcess.setAddonOptions = function (id, options) {
  let promises = [];

  for (let process of processes.values()) {
    promises.push(process.debuggerServer.setAddonOptions(id, options));
  }

  return promise.all(promises);
};

BrowserToolboxProcess.prototype = {
  /**
   * Initializes the debugger server.
   */
  _initServer: function () {
    if (this.debuggerServer) {
      dumpn("The chrome toolbox server is already running.");
      return;
    }

    dumpn("Initializing the chrome toolbox server.");

    // Create a separate loader instance, so that we can be sure to receive a
    // separate instance of the DebuggingServer from the rest of the devtools.
    // This allows us to safely use the tools against even the actors and
    // DebuggingServer itself, especially since we can mark this loader as
    // invisible to the debugger (unlike the usual loader settings).
    this.loader = new DevToolsLoader();
    this.loader.invisibleToDebugger = true;
    let { DebuggerServer } = this.loader.require("devtools/server/main");
    this.debuggerServer = DebuggerServer;
    dumpn("Created a separate loader instance for the DebuggerServer.");

    // Forward interesting events.
    this.debuggerServer.on("connectionchange", this._onConnectionChange);

    this.debuggerServer.init();
    // We mainly need a root actor and tab actors for opening a toolbox, even
    // against chrome/content/addon. But the "no auto hide" button uses the
    // preference actor, so also register the browser actors.
    this.debuggerServer.registerAllActors();
    this.debuggerServer.allowChromeProcess = true;
    dumpn("initialized and added the browser actors for the DebuggerServer.");

    let chromeDebuggingWebSocket =
      Services.prefs.getBoolPref("devtools.debugger.chrome-debugging-websocket");
    let listener = this.debuggerServer.createListener();
    listener.portOrPath = -1;
    listener.webSocket = chromeDebuggingWebSocket;
    listener.open();
    this.port = listener.port;

    if (!this.port) {
      throw new Error("No debugger server port");
    }

    dumpn("Finished initializing the chrome toolbox server.");
    dump(`Debugger Server for Browser Toolbox listening on port: ${this.port}\n`);
  },

  /**
   * Initializes a profile for the remote debugger process.
   */
  _initProfile: function () {
    dumpn("Initializing the chrome toolbox user profile.");

    // We used to use `ProfLD` instead of `ProfD`, so migrate old profiles if they exist.
    this._migrateProfileDir();

    let debuggingProfileDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
    debuggingProfileDir.append(CHROME_DEBUGGER_PROFILE_NAME);
    try {
      debuggingProfileDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
    } catch (ex) {
      // Don't re-copy over the prefs again if this profile already exists
      if (ex.result === Cr.NS_ERROR_FILE_ALREADY_EXISTS) {
        this._dbgProfilePath = debuggingProfileDir.path;
      } else {
        dumpn("Error trying to create a profile directory, failing.");
        dumpn("Error: " + (ex.message || ex));
      }
      return;
    }

    this._dbgProfilePath = debuggingProfileDir.path;

    // We would like to copy prefs into this new profile...
    let prefsFile = debuggingProfileDir.clone();
    prefsFile.append("prefs.js");
    // ... but unfortunately, when we run tests, it seems the starting profile
    // clears out the prefs file before re-writing it, and in practice the
    // file is empty when we get here. So just copying doesn't work in that
    // case.
    // We could force a sync pref flush and then copy it... but if we're doing
    // that, we might as well just flush directly to the new profile, which
    // always works:
    Services.prefs.savePrefFile(prefsFile);

    dumpn("Finished creating the chrome toolbox user profile at: " +
          this._dbgProfilePath);
  },

  /**
   * Originally, the profile was placed in `ProfLD` instead of `ProfD`.  On some systems,
   * such as macOS, `ProfLD` is in the user's Caches directory, which is not an
   * appropriate place to store supposedly persistent profile data.
   */
  _migrateProfileDir() {
    let oldDebuggingProfileDir = Services.dirsvc.get("ProfLD", Ci.nsIFile);
    let newDebuggingProfileDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
    if (oldDebuggingProfileDir.path == newDebuggingProfileDir.path) {
      // It's possible for these locations to be the same, such as running from
      // a custom profile directory specified via CLI.
      return;
    }
    oldDebuggingProfileDir.append(CHROME_DEBUGGER_PROFILE_NAME);
    if (!oldDebuggingProfileDir.exists()) {
      return;
    }
    dumpn(`Old debugging profile exists: ${oldDebuggingProfileDir.path}`);
    try {
      // Remove the directory from the target location, if it exists
      newDebuggingProfileDir.append(CHROME_DEBUGGER_PROFILE_NAME);
      if (newDebuggingProfileDir.exists()) {
        dumpn(`Removing folder at destination: ${newDebuggingProfileDir.path}`);
        newDebuggingProfileDir.remove(true);
      }
      // Move profile from old to new location
      let newDebuggingProfileParent = Services.dirsvc.get("ProfD", Ci.nsIFile);
      oldDebuggingProfileDir.moveTo(newDebuggingProfileParent, null);
      dumpn("Debugging profile migrated successfully");
    } catch (e) {
      dumpn(`Debugging profile migration failed: ${e}`);
    }
  },

  /**
   * Creates and initializes the profile & process for the remote debugger.
   */
  _create: function () {
    dumpn("Initializing chrome debugging process.");

    let command = Services.dirsvc.get("XREExeF", Ci.nsIFile).path;

    dumpn("Running chrome debugging process.");
    let args = [
      "-no-remote",
      "-foreground",
      "-profile", this._dbgProfilePath,
      "-chrome", DBG_XUL
    ];
    let environment = {
      // Disable safe mode for the new process in case this was opened via the
      // keyboard shortcut.
      MOZ_DISABLE_SAFE_MODE_KEY: "1",
      MOZ_BROWSER_TOOLBOX_PORT: String(this.port),
    };
    if (this._options.addonID) {
      environment.MOZ_BROWSER_TOOLBOX_ADDONID = String(this._options.addonID);
    }

    // During local development, incremental builds can trigger the main process
    // to clear its startup cache with the "flag file" .purgecaches, but this
    // file is removed during app startup time, so we aren't able to know if it
    // was present in order to also clear the child profile's startup cache as
    // well.
    //
    // As an approximation of "isLocalBuild", check for an unofficial build.
    if (!system.constants.MOZILLA_OFFICIAL) {
      args.push("-purgecaches");
    }

    this._dbgProcessPromise = Subprocess.call({
      command,
      arguments: args,
      environmentAppend: true,
      stderr: "stdout",
      environment,
    }).then(proc => {
      this._dbgProcess = proc;

      this._telemetry.toolOpened("jsbrowserdebugger");

      dumpn("Chrome toolbox is now running...");
      this.emit("run", this);

      proc.stdin.close();
      let dumpPipe = async pipe => {
        let data = await pipe.readString();
        while (data) {
          dump(data);
          data = await pipe.readString();
        }
      };
      dumpPipe(proc.stdout);

      proc.wait().then(() => this.close());

      return proc;
    }, err => {
      console.log(`Error loading Browser Toolbox: ${command} ${args.join(" ")}`, err);
    });
  },

  /**
   * Called upon receiving the connectionchange event from a debuggerServer.
   *
   * @param {String} what
   *        Type of connection change (can be either 'opened' or 'closed').
   * @param {DebuggerServerConnection} connection
   *        The connection that was opened or closed.
   */
  _onConnectionChange: function (what, connection) {
    let wrappedJSObject = { what, connection };
    Services.obs.notifyObservers({ wrappedJSObject }, "toolbox-connection-change");
  },

  /**
   * Closes the remote debugging server and kills the toolbox process.
   */
  close: async function () {
    if (this.closed) {
      return;
    }

    this.closed = true;

    dumpn("Cleaning up the chrome debugging process.");

    Services.obs.removeObserver(this.close, "quit-application");

    this._dbgProcess.stdout.close();
    await this._dbgProcess.kill();

    this._telemetry.toolClosed("jsbrowserdebugger");
    if (this.debuggerServer) {
      this.debuggerServer.off("connectionchange", this._onConnectionChange);
      this.debuggerServer.destroy();
      this.debuggerServer = null;
    }

    dumpn("Chrome toolbox is now closed...");
    this.emit("close", this);
    processes.delete(this);

    this._dbgProcess = null;
    this._options = null;
    if (this.loader) {
      this.loader.destroy();
    }
    this.loader = null;
    this._telemetry = null;
  }
};

/**
 * Helper method for debugging.
 * @param string
 */
function dumpn(str) {
  if (wantLogging) {
    dump("DBG-FRONTEND: " + str + "\n");
  }
}

var wantLogging = Services.prefs.getBoolPref("devtools.debugger.log");

Services.prefs.addObserver("devtools.debugger.log", {
  observe: (...args) => {
    wantLogging = Services.prefs.getBoolPref(args.pop());
  }
});

Services.prefs.addObserver("toolbox-update-addon-options", {
  observe: (subject) => {
    let {id, options} = subject.wrappedJSObject;
    BrowserToolboxProcess.setAddonOptions(id, options);
  }
});
PK
!<�����Bchrome/devtools/modules/devtools/client/framework/attach-thread.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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/. */

const {Cc, Ci, Cu} = require("chrome");
const Services = require("Services");
const defer = require("devtools/shared/defer");

const {LocalizationHelper} = require("devtools/shared/l10n");
const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");

function handleThreadState(toolbox, event, packet) {
  // Suppress interrupted events by default because the thread is
  // paused/resumed a lot for various actions.
  if (event !== "paused" || packet.why.type !== "interrupted") {
    // TODO: Bug 1225492, we continue emitting events on the target
    // like we used to, but we should emit these only on the
    // threadClient now.
    toolbox.target.emit("thread-" + event);
  }

  if (event === "paused") {
    toolbox.highlightTool("jsdebugger");

    if (packet.why.type === "debuggerStatement" ||
       packet.why.type === "breakpoint" ||
       packet.why.type === "exception") {
      toolbox.raise();
      toolbox.selectTool("jsdebugger");
    }
  } else if (event === "resumed") {
    toolbox.unhighlightTool("jsdebugger");
  }
}

function attachThread(toolbox) {
  let deferred = defer();

  let target = toolbox.target;
  let { form: { chromeDebugger, actor } } = target;

  // Sourcemaps are always turned off when using the new debugger
  // frontend. This is because it does sourcemapping on the
  // client-side, so the server should not do it.
  let useSourceMaps = false;
  let autoBlackBox = false;
  let ignoreFrameEnvironment = false;
  const newDebuggerEnabled = Services.prefs.getBoolPref("devtools.debugger.new-debugger-frontend");
  if(!newDebuggerEnabled) {
    useSourceMaps = Services.prefs.getBoolPref("devtools.debugger.source-maps-enabled");
    autoBlackBox = Services.prefs.getBoolPref("devtools.debugger.auto-black-box");
  } else {
    ignoreFrameEnvironment = true;
  }

  let threadOptions = { useSourceMaps, autoBlackBox, ignoreFrameEnvironment };

  let handleResponse = (res, threadClient) => {
    if (res.error) {
      deferred.reject(new Error("Couldn't attach to thread: " + res.error));
      return;
    }
    threadClient.addListener("paused", handleThreadState.bind(null, toolbox));
    threadClient.addListener("resumed", handleThreadState.bind(null, toolbox));

    if (!threadClient.paused) {
      deferred.reject(
        new Error("Thread in wrong state when starting up, should be paused")
      );
    }

    // These flags need to be set here because the client sends them
    // with the `resume` request. We make sure to do this before
    // resuming to avoid another interrupt. We can't pass it in with
    // `threadOptions` because the resume request will override them.
    threadClient.pauseOnExceptions(
      Services.prefs.getBoolPref("devtools.debugger.pause-on-exceptions"),
      Services.prefs.getBoolPref("devtools.debugger.ignore-caught-exceptions")
    );

    threadClient.resume(res => {
      if (res.error === "wrongOrder") {
        const box = toolbox.getNotificationBox();
        box.appendNotification(
          L10N.getStr("toolbox.resumeOrderWarning"),
          "wrong-resume-order",
          "",
          box.PRIORITY_WARNING_HIGH
        );
      }

      deferred.resolve(threadClient);
    });
  };

  if (target.isTabActor) {
    // Attaching a tab, a browser process, or a WebExtensions add-on.
    target.activeTab.attachThread(threadOptions, handleResponse);
  } else if (target.isAddon) {
    // Attaching a legacy addon.
    target.client.attachAddon(actor, res => {
      target.client.attachThread(res.threadActor, handleResponse);
    });
  }  else {
    // Attaching an old browser debugger or a content process.
    target.client.attachThread(chromeDebugger, handleResponse);
  }

  return deferred.promise;
}

function detachThread(threadClient) {
  threadClient.removeListener("paused");
  threadClient.removeListener("resumed");
}

module.exports = { attachThread, detachThread };
PK
!<��<D��Bchrome/devtools/modules/devtools/client/framework/browser-menus.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 inject dynamically menu items into browser UI.
 *
 * Menu definitions are fetched from:
 * - devtools/client/menus for top level entires
 * - devtools/client/definitions for tool-specifics entries
 */

const {LocalizationHelper} = require("devtools/shared/l10n");
const MENUS_L10N = new LocalizationHelper("devtools/client/locales/menus.properties");

loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
loader.lazyRequireGetter(this, "gDevToolsBrowser", "devtools/client/framework/devtools-browser", true);

// Keep list of inserted DOM Elements in order to remove them on unload
// Maps browser xul document => list of DOM Elements
const FragmentsCache = new Map();

function l10n(key) {
  return MENUS_L10N.getStr(key);
}

/**
 * Create a xul:menuitem element
 *
 * @param {XULDocument} doc
 *        The document to which menus are to be added.
 * @param {String} id
 *        Element id.
 * @param {String} label
 *        Menu label.
 * @param {String} accesskey (optional)
 *        Access key of the menuitem, used as shortcut while opening the menu.
 * @param {Boolean} isCheckbox (optional)
 *        If true, the menuitem will act as a checkbox and have an optional
 *        tick on its left.
 *
 * @return XULMenuItemElement
 */
function createMenuItem({ doc, id, label, accesskey, isCheckbox }) {
  let menuitem = doc.createElement("menuitem");
  menuitem.id = id;
  menuitem.setAttribute("label", label);
  if (accesskey) {
    menuitem.setAttribute("accesskey", accesskey);
  }
  if (isCheckbox) {
    menuitem.setAttribute("type", "checkbox");
    menuitem.setAttribute("autocheck", "false");
  }
  return menuitem;
}

/**
 * Add a menu entry for a tool definition
 *
 * @param {Object} toolDefinition
 *        Tool definition of the tool to add a menu entry.
 * @param {XULDocument} doc
 *        The document to which the tool menu item is to be added.
 */
function createToolMenuElements(toolDefinition, doc) {
  let id = toolDefinition.id;
  let menuId = "menuitem_" + id;

  // Prevent multiple entries for the same tool.
  if (doc.getElementById(menuId)) {
    return;
  }

  let oncommand = function (id, event) {
    let window = event.target.ownerDocument.defaultView;
    gDevToolsBrowser.selectToolCommand(window.gBrowser, id, window.performance.now());
  }.bind(null, id);

  let menuitem = createMenuItem({
    doc,
    id: "menuitem_" + id,
    label: toolDefinition.menuLabel || toolDefinition.label,
    accesskey: toolDefinition.accesskey
  });
  // Refer to the key in order to display the key shortcut at menu ends
  // This <key> element is being created by devtools/client/devtools-startup.js
  menuitem.setAttribute("key", "key_" + id);
  menuitem.addEventListener("command", oncommand);

  return {
    menuitem
  };
}

/**
 * Create xul menuitem, key elements for a given tool.
 * And then insert them into browser DOM.
 *
 * @param {XULDocument} doc
 *        The document to which the tool is to be registered.
 * @param {Object} toolDefinition
 *        Tool definition of the tool to register.
 * @param {Object} prevDef
 *        The tool definition after which the tool menu item is to be added.
 */
function insertToolMenuElements(doc, toolDefinition, prevDef) {
  let { menuitem } = createToolMenuElements(toolDefinition, doc);

  let ref;
  if (prevDef) {
    let menuitem = doc.getElementById("menuitem_" + prevDef.id);
    ref = menuitem && menuitem.nextSibling ? menuitem.nextSibling : null;
  } else {
    ref = doc.getElementById("menu_devtools_separator");
  }

  if (ref) {
    ref.parentNode.insertBefore(menuitem, ref);
  }
}
exports.insertToolMenuElements = insertToolMenuElements;

/**
 * Remove a tool's menuitem from a window
 *
 * @param {string} toolId
 *        Id of the tool to add a menu entry for
 * @param {XULDocument} doc
 *        The document to which the tool menu item is to be removed from
 */
function removeToolFromMenu(toolId, doc) {
  let key = doc.getElementById("key_" + toolId);
  if (key) {
    key.remove();
  }

  let menuitem = doc.getElementById("menuitem_" + toolId);
  if (menuitem) {
    menuitem.remove();
  }
}
exports.removeToolFromMenu = removeToolFromMenu;

/**
 * Add all tools to the developer tools menu of a window.
 *
 * @param {XULDocument} doc
 *        The document to which the tool items are to be added.
 */
function addAllToolsToMenu(doc) {
  let fragKeys = doc.createDocumentFragment();
  let fragMenuItems = doc.createDocumentFragment();

  for (let toolDefinition of gDevTools.getToolDefinitionArray()) {
    if (!toolDefinition.inMenu) {
      continue;
    }

    let elements = createToolMenuElements(toolDefinition, doc);

    if (!elements) {
      continue;
    }

    if (elements.key) {
      fragKeys.appendChild(elements.key);
    }
    fragMenuItems.appendChild(elements.menuitem);
  }

  let mps = doc.getElementById("menu_devtools_separator");
  if (mps) {
    mps.parentNode.insertBefore(fragMenuItems, mps);
  }
}

/**
 * Add global menus that are not panel specific.
 *
 * @param {XULDocument} doc
 *        The document to which menus are to be added.
 */
function addTopLevelItems(doc) {
  let menuItems = doc.createDocumentFragment();

  let { menuitems } = require("../menus");
  for (let item of menuitems) {
    if (item.separator) {
      let separator = doc.createElement("menuseparator");
      separator.id = item.id;
      menuItems.appendChild(separator);
    } else {
      let { id, l10nKey } = item;

      // Create a <menuitem>
      let menuitem = createMenuItem({
        doc,
        id,
        label: l10n(l10nKey + ".label"),
        accesskey: l10n(l10nKey + ".accesskey"),
        isCheckbox: item.checkbox
      });
      menuitem.addEventListener("command", item.oncommand);
      menuItems.appendChild(menuitem);

      if (item.keyId) {
        menuitem.setAttribute("key", "key_" + item.keyId);
      }
    }
  }

  // Cache all nodes before insertion to be able to remove them on unload
  let nodes = [];
  for (let node of menuItems.children) {
    nodes.push(node);
  }
  FragmentsCache.set(doc, nodes);

  let menu = doc.getElementById("menuWebDeveloperPopup");
  menu.appendChild(menuItems);

  // There is still "Page Source" menuitem hardcoded into browser.xul. Instead
  // of manually inserting everything around it, move it to the expected
  // position.
  let pageSource = doc.getElementById("menu_pageSource");
  let endSeparator = doc.getElementById("devToolsEndSeparator");
  menu.insertBefore(pageSource, endSeparator);
}

/**
 * Remove global menus that are not panel specific.
 *
 * @param {XULDocument} doc
 *        The document to which menus are to be added.
 */
function removeTopLevelItems(doc) {
  let nodes = FragmentsCache.get(doc);
  if (!nodes) {
    return;
  }
  FragmentsCache.delete(doc);
  for (let node of nodes) {
    node.remove();
  }
}

/**
 * Add menus to a browser document
 *
 * @param {XULDocument} doc
 *        The document to which menus are to be added.
 */
exports.addMenus = function (doc) {
  addTopLevelItems(doc);

  addAllToolsToMenu(doc);
};

/**
 * Remove menus from a browser document
 *
 * @param {XULDocument} doc
 *        The document to which menus are to be removed.
 */
exports.removeMenus = function (doc) {
  // We only remove top level entries. Per-tool entries are removed while
  // unregistering each tool.
  removeTopLevelItems(doc);
};
PK
!<E3i�xxRchrome/devtools/modules/devtools/client/framework/components/toolbox-controller.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/vendor/react");
const ToolboxToolbar = createFactory(require("devtools/client/framework/components/toolbox-toolbar"));
const ELEMENT_PICKER_ID = "command-button-pick";

/**
 * This component serves as a state controller for the toolbox React component. It's a
 * thin layer for translating events and state of the outside world into the React update
 * cycle. This solution was used to keep the amount of code changes to a minimimum while
 * adapting the existing codebase to start using React.
 */
class ToolboxController extends Component {
  constructor(props, context) {
    super(props, context);

    // See the ToolboxToolbar propTypes for documentation on each of these items in
    // state, and for the definitions of the props that are expected to be passed in.
    this.state = {
      focusedButton: ELEMENT_PICKER_ID,
      currentToolId: null,
      canRender: false,
      highlightedTools: new Set(),
      areDockButtonsEnabled: true,
      panelDefinitions: [],
      hostTypes: [],
      canCloseToolbox: true,
      toolboxButtons: [],
      buttonIds: [],
      checkedButtonsUpdated: () => {
        this.forceUpdate();
      }
    };

    this.updateButtonIds = this.updateButtonIds.bind(this);
    this.updateFocusedButton = this.updateFocusedButton.bind(this);
    this.setFocusedButton = this.setFocusedButton.bind(this);
    this.setCurrentToolId = this.setCurrentToolId.bind(this);
    this.setCanRender = this.setCanRender.bind(this);
    this.setOptionsPanel = this.setOptionsPanel.bind(this);
    this.highlightTool = this.highlightTool.bind(this);
    this.unhighlightTool = this.unhighlightTool.bind(this);
    this.setDockButtonsEnabled = this.setDockButtonsEnabled.bind(this);
    this.setHostTypes = this.setHostTypes.bind(this);
    this.setCanCloseToolbox = this.setCanCloseToolbox.bind(this);
    this.setPanelDefinitions = this.setPanelDefinitions.bind(this);
    this.setToolboxButtons = this.setToolboxButtons.bind(this);
    this.setCanMinimize = this.setCanMinimize.bind(this);
  }

  shouldComponentUpdate() {
    return this.state.canRender;
  }

  componentWillUnmount() {
    this.state.toolboxButtons.forEach(button => {
      button.off("updatechecked", this.state.checkedButtonsUpdated);
    });
  }

  /**
   * The button and tab ids must be known in order to be able to focus left and right
   * using the arrow keys.
   */
  updateButtonIds() {
    const {panelDefinitions, toolboxButtons, optionsPanel, hostTypes,
           canCloseToolbox} = this.state;

    // This is a little gnarly, but go through all of the state and extract the IDs.
    this.setState({
      buttonIds: [
        ...toolboxButtons.filter(btn => btn.isInStartContainer).map(({id}) => id),
        ...panelDefinitions.map(({id}) => id),
        ...toolboxButtons.filter(btn => !btn.isInStartContainer).map(({id}) => id),
        optionsPanel ? optionsPanel.id : null,
        ...hostTypes.map(({position}) => "toolbox-dock-" + position),
        canCloseToolbox ? "toolbox-close" : null
      ].filter(id => id)
    });

    this.updateFocusedButton();
  }

  updateFocusedButton() {
    this.setFocusedButton(this.state.focusedButton);
  }

  setFocusedButton(focusedButton) {
    const {buttonIds} = this.state;

    focusedButton = focusedButton && buttonIds.includes(focusedButton)
        ? focusedButton
        : buttonIds[0];
    if (this.state.focusedButton !== focusedButton) {
      this.setState({
        focusedButton
      });
    }
  }

  setCurrentToolId(currentToolId) {
    this.setState({currentToolId}, () => {
      // Also set the currently focused button to this tool.
      this.setFocusedButton(currentToolId);
    });
  }

  setCanRender() {
    this.setState({ canRender: true }, this.updateButtonIds);
  }

  setOptionsPanel(optionsPanel) {
    this.setState({ optionsPanel }, this.updateButtonIds);
  }

  highlightTool(highlightedTool) {
    let { highlightedTools } = this.state;
    highlightedTools.add(highlightedTool);
    this.setState({ highlightedTools });
  }

  unhighlightTool(id) {
    let { highlightedTools } = this.state;
    if (highlightedTools.has(id)) {
      highlightedTools.delete(id);
      this.setState({ highlightedTools });
    }
  }

  setDockButtonsEnabled(areDockButtonsEnabled) {
    this.setState({ areDockButtonsEnabled }, this.updateButtonIds);
  }

  setHostTypes(hostTypes) {
    this.setState({ hostTypes }, this.updateButtonIds);
  }

  setCanCloseToolbox(canCloseToolbox) {
    this.setState({ canCloseToolbox }, this.updateButtonIds);
  }

  setPanelDefinitions(panelDefinitions) {
    this.setState({ panelDefinitions }, this.updateButtonIds);
  }

  get panelDefinitions() {
    return this.state.panelDefinitions;
  }

  setToolboxButtons(toolboxButtons) {
    // Listen for updates of the checked attribute.
    this.state.toolboxButtons.forEach(button => {
      button.off("updatechecked", this.state.checkedButtonsUpdated);
    });
    toolboxButtons.forEach(button => {
      button.on("updatechecked", this.state.checkedButtonsUpdated);
    });

    this.setState({ toolboxButtons }, this.updateButtonIds);
  }

  setCanMinimize(canMinimize) {
    /* Bug 1177463 - The minimize button is currently hidden until we agree on
       the UI for it, and until bug 1173849 is fixed too. */

    // this.setState({ canMinimize });
  }

  render() {
    return ToolboxToolbar(Object.assign({}, this.props, this.state));
  }
}

module.exports = ToolboxController;
PK
!<z�m���Kchrome/devtools/modules/devtools/client/framework/components/toolbox-tab.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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 {img, button, span} = dom;

class ToolboxTab extends Component {
  // See toolbox-toolbar propTypes for details on the props used here.
  static get propTypes() {
    return {
      currentToolId: PropTypes.string,
      focusButton: PropTypes.func,
      focusedButton: PropTypes.string,
      highlightedTools: PropTypes.object.isRequired,
      panelDefinition: PropTypes.object,
      selectTool: PropTypes.func,
    };
  }

  constructor(props) {
    super(props);
    this.renderIcon = this.renderIcon.bind(this);
  }

  renderIcon(definition, isHighlighted) {
    const {icon} = definition;
    if (!icon) {
      return [];
    }
    return [
      img({
        src: icon
      }),
    ];
  }

  render() {
    const {panelDefinition, currentToolId, highlightedTools, selectTool,
           focusedButton, focusButton} = this.props;
    const {id, tooltip, label, iconOnly} = panelDefinition;
    const isHighlighted = id === currentToolId;

    const className = [
      "devtools-tab",
      currentToolId === id ? "selected" : "",
      highlightedTools.has(id) ? "highlighted" : "",
      iconOnly ? "devtools-tab-icon-only" : ""
    ].join(" ");

    return button(
      {
        className,
        id: `toolbox-tab-${id}`,
        "data-id": id,
        title: tooltip,
        type: "button",
        "aria-pressed": currentToolId === id ? "true" : "false",
        tabIndex: focusedButton === id ? "0" : "-1",
        onFocus: () => focusButton(id),
        onMouseDown: () => selectTool(id),
      },
      span(
        {
          className: "devtools-tab-line"
        }
      ),
      ...this.renderIcon(panelDefinition, isHighlighted),
      iconOnly ?
        null :
        span(
          {
            className: "devtools-tab-label"
          },
          label
        )
    );
  }
}

module.exports = ToolboxTab;
PK
!<�,U��Lchrome/devtools/modules/devtools/client/framework/components/toolbox-tabs.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const {findDOMNode} = require("devtools/client/shared/vendor/react-dom");
const {button, div} = dom;

const Menu = require("devtools/client/framework/menu");
const MenuItem = require("devtools/client/framework/menu-item");
const ToolboxTab = createFactory(require("devtools/client/framework/components/toolbox-tab"));

class ToolboxTabs extends Component {
  // See toolbox-toolbar propTypes for details on the props used here.
  static get propTypes() {
    return {
      currentToolId: PropTypes.string,
      focusButton: PropTypes.func,
      focusedButton: PropTypes.string,
      highlightedTools: PropTypes.object,
      panelDefinitions: PropTypes.array,
      selectTool: PropTypes.func,
      toolbox: PropTypes.object,
      L10N: PropTypes.object,
    };
  }

  constructor(props) {
    super(props);

    this.state = {
      overflow: false,
    };

    this.addFlowEvents = this.addFlowEvents.bind(this);
    this.removeFlowEvents = this.removeFlowEvents.bind(this);
    this.onOverflow = this.onOverflow.bind(this);
    this.onUnderflow = this.onUnderflow.bind(this);
  }

  componentDidUpdate() {
    this.addFlowEvents();
  }

  componentWillUnmount() {
    this.removeFlowEvents();
  }

  addFlowEvents() {
    this.removeFlowEvents();
    let node = findDOMNode(this);
    if (node) {
      node.addEventListener("overflow", this.onOverflow);
      node.addEventListener("underflow", this.onUnderflow);
    }
  }

  removeFlowEvents() {
    let node = findDOMNode(this);
    if (node) {
      node.removeEventListener("overflow", this.onOverflow);
      node.removeEventListener("underflow", this.onUnderflow);
    }
  }

  onOverflow() {
    this.setState({
      overflow: true
    });
  }

  onUnderflow() {
    this.setState({
      overflow: false
    });
  }

  /**
   * Render all of the tabs, based on the panel definitions and builds out
   * a toolbox tab for each of them. Will render an all-tabs button if the
   * container has an overflow.
   */
  render() {
    let {
      currentToolId,
      focusButton,
      focusedButton,
      highlightedTools,
      panelDefinitions,
      selectTool,
    } = this.props;

    let tabs = panelDefinitions.map(panelDefinition => ToolboxTab({
      key: panelDefinition.id,
      currentToolId,
      focusButton,
      focusedButton,
      highlightedTools,
      panelDefinition,
      selectTool,
    }));

    // A wrapper is needed to get flex sizing correct in XUL.
    return div(
      {
        className: "toolbox-tabs-wrapper"
      },
      div(
        {
          className: "toolbox-tabs"
        },
        tabs
      ),
      this.state.overflow ? renderAllToolsButton(this.props) : null
    );
  }
}

module.exports = ToolboxTabs;

/**
 * Render a button to access all tools, displayed only when the toolbar presents an
 * overflow.
 */
function renderAllToolsButton(props) {
  let {
    currentToolId,
    panelDefinitions,
    selectTool,
    toolbox,
    L10N,
  } = props;

  return button({
    className: "all-tools-menu all-tabs-menu",
    tabIndex: -1,
    title: L10N.getStr("toolbox.allToolsButton.tooltip"),
    onClick: ({ target }) => {
      let menu = new Menu({
        id: "all-tools-menupopup"
      });
      panelDefinitions.forEach(({id, label}) => {
        menu.append(new MenuItem({
          checked: currentToolId === id,
          click: () => {
            selectTool(id);
          },
          id: "all-tools-menupopup-" + id,
          label,
          type: "checkbox",
        }));
      });

      let rect = target.getBoundingClientRect();
      let screenX = target.ownerDocument.defaultView.mozInnerScreenX;
      let screenY = target.ownerDocument.defaultView.mozInnerScreenY;

      // Display the popup below the button.
      menu.popup(rect.left + screenX, rect.bottom + screenY, toolbox);
      return menu;
    },
  });
}
PK
!<_ߢ� � Ochrome/devtools/modules/devtools/client/framework/components/toolbox-toolbar.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const {div, button} = dom;

const ToolboxTab = createFactory(require("devtools/client/framework/components/toolbox-tab"));
const ToolboxTabs = createFactory(require("devtools/client/framework/components/toolbox-tabs"));

/**
 * This is the overall component for the toolbox toolbar. It is designed to not know how
 * the state is being managed, and attempts to be as pure as possible. The
 * ToolboxController component controls the changing state, and passes in everything as
 * props.
 */
class ToolboxToolbar extends Component {
  static get propTypes() {
    return {
      // The currently focused item (for arrow keyboard navigation)
      // This ID determines the tabindex being 0 or -1.
      focusedButton: PropTypes.string,
      // List of command button definitions.
      toolboxButtons: PropTypes.array,
      // The id of the currently selected tool, e.g. "inspector"
      currentToolId: PropTypes.string,
      // An optionally highlighted tools, e.g. "inspector".
      // Note: highlightedTools must be an instance of Set.
      highlightedTools: PropTypes.object,
      // List of tool panel definitions.
      panelDefinitions: PropTypes.array,
      // Function to select a tool based on its id.
      selectTool: PropTypes.func,
      // Keep a record of what button is focused.
      focusButton: PropTypes.func,
      // The options button definition.
      optionsPanel: PropTypes.object,
      // Hold off displaying the toolbar until enough information is ready for
      // it to render nicely.
      canRender: PropTypes.bool,
      // Localization interface.
      L10N: PropTypes.object,
      // The devtools toolbox
      toolbox: PropTypes.object,
    };
  }

  /**
   * The render function is kept fairly short for maintainability. See the individual
   * render functions for how each of the sections is rendered.
   */
  render() {
    const containerProps = {className: "devtools-tabbar"};
    return this.props.canRender
      ? (
        div(
          containerProps,
          renderToolboxButtonsStart(this.props),
          ToolboxTabs(this.props),
          renderToolboxButtonsEnd(this.props),
          renderOptions(this.props),
          renderSeparator(),
          renderDockButtons(this.props)
        )
      )
      : div(containerProps);
  }
}

module.exports = ToolboxToolbar;

/**
 * A little helper function to call renderToolboxButtons for buttons at the start
 * of the toolbox.
 */
function renderToolboxButtonsStart(props) {
  return renderToolboxButtons(props, true);
}

/**
* A little helper function to call renderToolboxButtons for buttons at the end
* of the toolbox.
 */
function renderToolboxButtonsEnd(props) {
  return renderToolboxButtons(props, false);
}

/**
 * Render all of the tabs, this takes in a list of toolbox button states. These are plain
 * objects that have all of the relevant information needed to render the button.
 * See Toolbox.prototype._createButtonState in devtools/client/framework/toolbox.js for
 * documentation on this object.
 *
 * @param {Array} toolboxButtons - Array of objects that define the command buttons.
 * @param {String} focusedButton - The id of the focused button.
 * @param {Function} focusButton - Keep a record of the currently focused button.
 * @param {boolean} isStart - Render either the starting buttons, or ending buttons.
 */
function renderToolboxButtons({toolboxButtons, focusedButton, focusButton}, isStart) {
  const visibleButtons = toolboxButtons.filter(command => {
    const {isVisible, isInStartContainer} = command;
    return isVisible && (isStart ? isInStartContainer : !isInStartContainer);
  });

  if (visibleButtons.length === 0) {
    return null;
  }

  return div({id: `toolbox-buttons-${isStart ? "start" : "end"}`},
    ...visibleButtons.map(command => {
      const {
        id,
        description,
        disabled,
        onClick,
        isChecked,
        className: buttonClass,
        onKeyDown
      } = command;
      return button({
        id,
        title: description,
        disabled,
        className: (
          "command-button devtools-button "
          + buttonClass + (isChecked ? " checked" : "")
        ),
        onClick: (event) => {
          onClick(event);
          focusButton(id);
        },
        onFocus: () => focusButton(id),
        tabIndex: id === focusedButton ? "0" : "-1",
        onKeyDown: (event) => {
          onKeyDown(event);
        }
      });
    }),
    isStart ? div({className: "devtools-separator"}) : null
  );
}

/**
 * The options button is a ToolboxTab just like in the ToolboxTabs component. However
 * it is separate from the normal tabs, so deal with it separately here.
 *
 * @param {Object}   optionsPanel - A single panel definition for the options panel.
 * @param {String}   currentToolId - The currently selected tool's id; e.g. "inspector".
 * @param {Function} selectTool - Function to select a tool in the toolbox.
 * @param {String}   focusedButton - The id of the focused button.
 * @param {Function} focusButton - Keep a record of the currently focused button.
 * @param {Object}   highlightedTools - Optionally highlighted tools, e.g. "inspector".
 */
function renderOptions({optionsPanel, currentToolId, selectTool, focusedButton,
                        focusButton, highlightedTools}) {
  return div({id: "toolbox-option-container"}, ToolboxTab({
    panelDefinition: optionsPanel,
    currentToolId,
    selectTool,
    highlightedTools,
    focusedButton,
    focusButton,
  }));
}

/**
 * Render a separator.
 */
function renderSeparator() {
  return div({className: "devtools-separator"});
}

/**
 * Render the dock buttons, and handle all the cases for what type of host the toolbox
 * is attached to. The following props are expected.
 *
 * @property {String} focusedButton - The id of the focused button.
 * @property {Function} closeToolbox - Completely close the toolbox.
 * @property {Array} hostTypes - Array of host type objects, containing:
 *                   @property {String} position - Position name
 *                   @property {Function} switchHost - Function to switch the host.
 * @property {Function} focusButton - Keep a record of the currently focused button.
 * @property {Object} L10N - Localization interface.
 * @property {Boolean} areDockButtonsEnabled - They are not enabled in certain situations
 *                                             like when they are in the WebIDE.
 * @property {Boolean} canCloseToolbox - Are the tools in a context where they can be
 *                                       closed? This is not always the case, e.g. in the
 *                                       WebIDE.
 */
function renderDockButtons(props) {
  const {
    focusedButton,
    closeToolbox,
    hostTypes,
    focusButton,
    L10N,
    areDockButtonsEnabled,
    canCloseToolbox,
  } = props;

  let buttons = [];

  if (areDockButtonsEnabled) {
    hostTypes.forEach(hostType => {
      const id = "toolbox-dock-" + hostType.position;
      buttons.push(button({
        id,
        onFocus: () => focusButton(id),
        className: "toolbox-dock-button devtools-button",
        title: L10N.getStr(`toolboxDockButtons.${hostType.position}.tooltip`),
        onClick: e => {
          hostType.switchHost();
          focusButton(id);
        },
        tabIndex: focusedButton === id ? "0" : "-1",
      }));
    });
  }

  const closeButtonId = "toolbox-close";

  const closeButton = canCloseToolbox
    ? button({
      id: closeButtonId,
      onFocus: () => focusButton(closeButtonId),
      className: "devtools-button",
      title: L10N.getStr("toolbox.closebutton.tooltip"),
      onClick: () => {
        closeToolbox();
      },
      tabIndex: focusedButton === "toolbox-close" ? "0" : "-1",
    })
    : null;

  return div({id: "toolbox-controls"},
    div({id: "toolbox-dock-buttons"}, ...buttons),
    closeButton
  );
}
PK
!<�1�&�f�fEchrome/devtools/modules/devtools/client/framework/devtools-browser.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 the main module loaded in Firefox desktop that handles browser
 * windows and coordinates devtools around each window.
 *
 * This module is loaded lazily by devtools-clhandler.js, once the first
 * browser window is ready (i.e. fired browser-delayed-startup-finished event)
 **/

const {Cc, Ci} = require("chrome");
const Services = require("Services");
const defer = require("devtools/shared/defer");
const {gDevTools} = require("./devtools");

// Load target and toolbox lazily as they need gDevTools to be fully initialized
loader.lazyRequireGetter(this, "TargetFactory", "devtools/client/framework/target", true);
loader.lazyRequireGetter(this, "Toolbox", "devtools/client/framework/toolbox", true);
loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true);
loader.lazyRequireGetter(this, "DebuggerClient", "devtools/shared/client/debugger-client", true);
loader.lazyRequireGetter(this, "BrowserMenus", "devtools/client/framework/browser-menus");
loader.lazyRequireGetter(this, "appendStyleSheet", "devtools/client/shared/stylesheet-utils", true);
loader.lazyRequireGetter(this, "DeveloperToolbar", "devtools/client/shared/developer-toolbar", true);
loader.lazyRequireGetter(this, "ResponsiveUIManager", "devtools/client/responsive.html/manager", true);
loader.lazyImporter(this, "BrowserToolboxProcess", "resource://devtools/client/framework/ToolboxProcess.jsm");
loader.lazyImporter(this, "ScratchpadManager", "resource://devtools/client/scratchpad/scratchpad-manager.jsm");

loader.lazyImporter(this, "CustomizableUI", "resource:///modules/CustomizableUI.jsm");
loader.lazyImporter(this, "CustomizableWidgets", "resource:///modules/CustomizableWidgets.jsm");
loader.lazyImporter(this, "AppConstants", "resource://gre/modules/AppConstants.jsm");

const {LocalizationHelper} = require("devtools/shared/l10n");
const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");

const BROWSER_STYLESHEET_URL = "chrome://devtools/skin/devtools-browser.css";

/**
 * gDevToolsBrowser exposes functions to connect the gDevTools instance with a
 * Firefox instance.
 */
var gDevToolsBrowser = exports.gDevToolsBrowser = {
  /**
   * A record of the windows whose menus we altered, so we can undo the changes
   * as the window is closed
   */
  _trackedBrowserWindows: new Set(),

  /**
   * WeakMap keeping track of the devtools-browser stylesheets loaded in the various
   * tracked windows.
   */
  _browserStyleSheets: new WeakMap(),

  /**
   * WeakMap keeping track of DeveloperToolbar instances for each firefox window.
   */
  _toolbars: new WeakMap(),

  /**
   * This function is for the benefit of Tools:DevToolbox in
   * browser/base/content/browser-sets.inc and should not be used outside
   * of there
   */
  // used by browser-sets.inc, command
  toggleToolboxCommand(gBrowser, startTime) {
    let target = TargetFactory.forTab(gBrowser.selectedTab);
    let toolbox = gDevTools.getToolbox(target);

    // If a toolbox exists, using toggle from the Main window :
    // - should close a docked toolbox
    // - should focus a windowed toolbox
    let isDocked = toolbox && toolbox.hostType != Toolbox.HostType.WINDOW;
    if (isDocked) {
      gDevTools.closeToolbox(target);
    } else {
      gDevTools.showToolbox(target, null, null, null, startTime);
    }
  },

  /**
   * This function ensures the right commands are enabled in a window,
   * depending on their relevant prefs. It gets run when a window is registered,
   * or when any of the devtools prefs change.
   */
  updateCommandAvailability(win) {
    let doc = win.document;

    function toggleMenuItem(id, isEnabled) {
      let cmd = doc.getElementById(id);
      if (isEnabled) {
        cmd.removeAttribute("disabled");
        cmd.removeAttribute("hidden");
      } else {
        cmd.setAttribute("disabled", "true");
        cmd.setAttribute("hidden", "true");
      }
    }

    // Enable developer toolbar?
    let devToolbarEnabled = Services.prefs.getBoolPref("devtools.toolbar.enabled");
    toggleMenuItem("menu_devToolbar", devToolbarEnabled);
    let focusEl = doc.getElementById("menu_devToolbar");
    if (devToolbarEnabled) {
      focusEl.removeAttribute("disabled");
    } else {
      focusEl.setAttribute("disabled", "true");
    }
    if (devToolbarEnabled && Services.prefs.getBoolPref("devtools.toolbar.visible")) {
      this.getDeveloperToolbar(win).show(false).catch(console.error);
    }

    // Enable WebIDE?
    let webIDEEnabled = Services.prefs.getBoolPref("devtools.webide.enabled");
    toggleMenuItem("menu_webide", webIDEEnabled);

    if (webIDEEnabled) {
      gDevToolsBrowser.installWebIDEWidget();
    } else {
      gDevToolsBrowser.uninstallWebIDEWidget();
    }

    // Enable Browser Toolbox?
    let chromeEnabled = Services.prefs.getBoolPref("devtools.chrome.enabled");
    let devtoolsRemoteEnabled = Services.prefs.getBoolPref(
      "devtools.debugger.remote-enabled");
    let remoteEnabled = chromeEnabled && devtoolsRemoteEnabled;
    toggleMenuItem("menu_browserToolbox", remoteEnabled);
    toggleMenuItem("menu_browserContentToolbox",
      remoteEnabled && win.gMultiProcessBrowser);

    // Enable DevTools connection screen, if the preference allows this.
    toggleMenuItem("menu_devtools_connect", devtoolsRemoteEnabled);
  },

  /**
   * This function makes sure that the "devtoolstheme" attribute is set on the browser
   * window to make it possible to change colors on elements in the browser (like gcli,
   * or the splitter between the toolbox and web content).
   */
  updateDevtoolsThemeAttribute(win) {
    // Set an attribute on root element of each window to make it possible
    // to change colors based on the selected devtools theme.
    let devtoolsTheme = Services.prefs.getCharPref("devtools.theme");
    if (devtoolsTheme != "dark") {
      devtoolsTheme = "light";
    }

    // Style gcli and the splitter between the toolbox and page content.  This used to
    // set the attribute on the browser's root node but that regressed tpaint:
    // bug 1331449.
    win.document.getElementById("browser-bottombox")
       .setAttribute("devtoolstheme", devtoolsTheme);
    win.document.getElementById("appcontent")
       .setAttribute("devtoolstheme", devtoolsTheme);
  },

  observe(subject, topic, prefName) {
    switch (topic) {
      case "browser-delayed-startup-finished":
        this._registerBrowserWindow(subject);
        break;
      case "nsPref:changed":
        if (prefName.endsWith("enabled")) {
          for (let win of this._trackedBrowserWindows) {
            this.updateCommandAvailability(win);
          }
        }
        if (prefName === "devtools.theme") {
          for (let win of this._trackedBrowserWindows) {
            this.updateDevtoolsThemeAttribute(win);
          }
        }
        break;
      case "quit-application":
        gDevToolsBrowser.destroy({ shuttingDown: true });
        break;
      case "sdk:loader:destroy":
        // This event is fired when the devtools loader unloads, which happens
        // only when the add-on workflow ask devtools to be reloaded.
        if (subject.wrappedJSObject == require("@loader/unload")) {
          gDevToolsBrowser.destroy({ shuttingDown: false });
        }
        break;
    }
  },

  _prefObserverRegistered: false,

  ensurePrefObserver() {
    if (!this._prefObserverRegistered) {
      this._prefObserverRegistered = true;
      Services.prefs.addObserver("devtools.", this);
    }
  },

  /**
   * This function is for the benefit of Tools:{toolId} commands,
   * triggered from the WebDeveloper menu and keyboard shortcuts.
   *
   * selectToolCommand's behavior:
   * - if the toolbox is closed,
   *   we open the toolbox and select the tool
   * - if the toolbox is open, and the targeted tool is not selected,
   *   we select it
   * - if the toolbox is open, and the targeted tool is selected,
   *   and the host is NOT a window, we close the toolbox
   * - if the toolbox is open, and the targeted tool is selected,
   *   and the host is a window, we raise the toolbox window
   */
  // Used when: - registering a new tool
  //            - new xul window, to add menu items
  selectToolCommand(gBrowser, toolId, startTime) {
    let target = TargetFactory.forTab(gBrowser.selectedTab);
    let toolbox = gDevTools.getToolbox(target);
    let toolDefinition = gDevTools.getToolDefinition(toolId);

    if (toolbox &&
        (toolbox.currentToolId == toolId ||
          (toolId == "webconsole" && toolbox.splitConsole))) {
      toolbox.fireCustomKey(toolId);

      if (toolDefinition.preventClosingOnKey ||
          toolbox.hostType == Toolbox.HostType.WINDOW) {
        toolbox.raise();
      } else {
        gDevTools.closeToolbox(target);
      }
      gDevTools.emit("select-tool-command", toolId);
    } else {
      gDevTools.showToolbox(target, toolId, null, null, startTime).then(newToolbox => {
        newToolbox.fireCustomKey(toolId);
        gDevTools.emit("select-tool-command", toolId);
      });
    }
  },

  /**
   * Called by devtools/client/devtools-startup.js when a key shortcut is pressed
   *
   * @param  {Window} window
   *         The top level browser window from which the key shortcut is pressed.
   * @param  {Object} key
   *         Key object describing the key shortcut being pressed. It comes
   *         from devtools-startup.js's KeyShortcuts array. The useful fields here
   *         are:
   *         - `toolId` used to identify a toolbox's panel like inspector or webconsole,
   *         - `id` used to identify any other key shortcuts like scratchpad or
   *         about:debugging
   * @param {Number} startTime
   *        Optional, indicates the time at which the key event fired. This is a
   *        `performance.now()` timing.
   */
  onKeyShortcut(window, key, startTime) {
    // If this is a toolbox's panel key shortcut, delegate to selectToolCommand
    if (key.toolId) {
      gDevToolsBrowser.selectToolCommand(window.gBrowser, key.toolId, startTime);
      return;
    }
    // Otherwise implement all other key shortcuts individually here
    switch (key.id) {
      case "toggleToolbox":
      case "toggleToolboxF12":
        gDevToolsBrowser.toggleToolboxCommand(window.gBrowser, startTime);
        break;
      case "toggleToolbar":
        gDevToolsBrowser.getDeveloperToolbar(window).focusToggle();
        break;
      case "webide":
        gDevToolsBrowser.openWebIDE();
        break;
      case "browserToolbox":
        BrowserToolboxProcess.init();
        break;
      case "browserConsole":
        let {HUDService} = require("devtools/client/webconsole/hudservice");
        HUDService.openBrowserConsoleOrFocus();
        break;
      case "responsiveDesignMode":
        ResponsiveUIManager.toggle(window, window.gBrowser.selectedTab);
        break;
      case "scratchpad":
        ScratchpadManager.openScratchpad();
        break;
    }
  },

  /**
   * Open a tab on "about:debugging", optionally pre-select a given tab.
   */
   // Used by browser-sets.inc, command
  openAboutDebugging(gBrowser, hash) {
    let url = "about:debugging" + (hash ? "#" + hash : "");
    gBrowser.selectedTab = gBrowser.addTab(url);
  },

  /**
   * Open a tab to allow connects to a remote browser
   */
   // Used by browser-sets.inc, command
  openConnectScreen(gBrowser) {
    gBrowser.selectedTab = gBrowser.addTab("chrome://devtools/content/framework/connect/connect.xhtml");
  },

  /**
   * Open WebIDE
   */
   // Used by browser-sets.inc, command
   //         itself, webide widget
  openWebIDE() {
    let win = Services.wm.getMostRecentWindow("devtools:webide");
    if (win) {
      win.focus();
    } else {
      Services.ww.openWindow(null, "chrome://webide/content/", "webide", "chrome,centerscreen,resizable", null);
    }
  },

  _getContentProcessTarget(processId) {
    // Create a DebuggerServer in order to connect locally to it
    DebuggerServer.init();
    DebuggerServer.registerAllActors();
    DebuggerServer.allowChromeProcess = true;

    let transport = DebuggerServer.connectPipe();
    let client = new DebuggerClient(transport);

    let deferred = defer();
    client.connect().then(() => {
      client.getProcess(processId)
            .then(response => {
              let options = {
                form: response.form,
                client: client,
                chrome: true,
                isTabActor: false
              };
              return TargetFactory.forRemoteTab(options);
            })
            .then(target => {
              // Ensure closing the connection in order to cleanup
              // the debugger client and also the server created in the
              // content process
              target.on("close", () => {
                client.close();
              });
              deferred.resolve(target);
            });
    });

    return deferred.promise;
  },

  /**
   * Open the Browser Content Toolbox for the provided gBrowser instance.
   * Returns a promise that resolves with a toolbox instance. If no content process is
   * available, the promise will be rejected and a message will be displayed to the user.
   *
   * Used by menus.js
  */
  openContentProcessToolbox(gBrowser) {
    let { childCount } = Services.ppmm;
    // Get the process message manager for the current tab
    let mm = gBrowser.selectedBrowser.messageManager.processMessageManager;
    let processId = null;
    for (let i = 1; i < childCount; i++) {
      let child = Services.ppmm.getChildAt(i);
      if (child == mm) {
        processId = i;
        break;
      }
    }
    if (processId) {
      return this._getContentProcessTarget(processId)
          .then(target => {
            // Display a new toolbox in a new window
            return gDevTools.showToolbox(target, null, Toolbox.HostType.WINDOW);
          });
    }

    let msg = L10N.getStr("toolbox.noContentProcessForTab.message");
    Services.prompt.alert(null, "", msg);
    return Promise.reject(msg);
  },

  /**
   * Install WebIDE widget
   */
  // Used by itself
  installWebIDEWidget() {
    if (this.isWebIDEWidgetInstalled()) {
      return;
    }

    CustomizableUI.createWidget({
      id: "webide-button",
      shortcutId: "key_webide",
      label: "devtools-webide-button2.label",
      tooltiptext: "devtools-webide-button2.tooltiptext",
      onCommand(event) {
        gDevToolsBrowser.openWebIDE();
      }
    });
  },

  isWebIDEWidgetInstalled() {
    let widgetWrapper = CustomizableUI.getWidget("webide-button");
    return !!(widgetWrapper && widgetWrapper.provider == CustomizableUI.PROVIDER_API);
  },

  /**
   * Add the devtools-browser stylesheet to browser window's document. Returns a promise.
   *
   * @param  {Window} win
   *         The window on which the stylesheet should be added.
   * @return {Promise} promise that resolves when the stylesheet is loaded (or rejects
   *         if it fails to load).
   */
  loadBrowserStyleSheet: function (win) {
    if (this._browserStyleSheets.has(win)) {
      return Promise.resolve();
    }

    let doc = win.document;
    let {styleSheet, loadPromise} = appendStyleSheet(doc, BROWSER_STYLESHEET_URL);
    this._browserStyleSheets.set(win, styleSheet);
    return loadPromise;
  },

  /**
   * The deferred promise will be resolved by WebIDE's UI.init()
   */
  isWebIDEInitialized: defer(),

  /**
   * Uninstall WebIDE widget
   */
  uninstallWebIDEWidget() {
    if (this.isWebIDEWidgetInstalled()) {
      CustomizableUI.removeWidgetFromArea("webide-button");
    }
    CustomizableUI.destroyWidget("webide-button");
  },

  /**
   * Add this DevTools's presence to a browser window's document
   *
   * @param {XULDocument} doc
   *        The document to which devtools should be hooked to.
   */
  _registerBrowserWindow(win) {
    if (gDevToolsBrowser._trackedBrowserWindows.has(win)) {
      return;
    }
    gDevToolsBrowser._trackedBrowserWindows.add(win);

    BrowserMenus.addMenus(win.document);

    this.updateCommandAvailability(win);
    this.updateDevtoolsThemeAttribute(win);
    this.ensurePrefObserver();
    win.addEventListener("unload", this);

    let tabContainer = win.gBrowser.tabContainer;
    tabContainer.addEventListener("TabSelect", this);
  },

  /**
   * Create singleton instance of the developer toolbar for a given top level window.
   *
   * @param {Window} win
   *        The window to which the toolbar should be created.
   */
  getDeveloperToolbar(win) {
    let toolbar = this._toolbars.get(win);
    if (toolbar) {
      return toolbar;
    }
    toolbar = new DeveloperToolbar(win);
    this._toolbars.set(win, toolbar);
    return toolbar;
  },

  /**
   * Hook the JS debugger tool to the "Debug Script" button of the slow script
   * dialog.
   */
  setSlowScriptDebugHandler() {
    let debugService = Cc["@mozilla.org/dom/slow-script-debug;1"]
                         .getService(Ci.nsISlowScriptDebug);

    function slowScriptDebugHandler(tab, callback) {
      let target = TargetFactory.forTab(tab);

      gDevTools.showToolbox(target, "jsdebugger").then(toolbox => {
        let threadClient = toolbox.threadClient;

        // Break in place, which means resuming the debuggee thread and pausing
        // right before the next step happens.
        switch (threadClient.state) {
          case "paused":
            // When the debugger is already paused.
            threadClient.resumeThenPause();
            callback();
            break;
          case "attached":
            // When the debugger is already open.
            threadClient.interrupt(() => {
              threadClient.resumeThenPause();
              callback();
            });
            break;
          case "resuming":
            // The debugger is newly opened.
            threadClient.addOneTimeListener("resumed", () => {
              threadClient.interrupt(() => {
                threadClient.resumeThenPause();
                callback();
              });
            });
            break;
          default:
            throw Error("invalid thread client state in slow script debug handler: " +
                        threadClient.state);
        }
      });
    }

    debugService.activationHandler = function (window) {
      let chromeWindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
                                .getInterface(Ci.nsIWebNavigation)
                                .QueryInterface(Ci.nsIDocShellTreeItem)
                                .rootTreeItem
                                .QueryInterface(Ci.nsIInterfaceRequestor)
                                .getInterface(Ci.nsIDOMWindow)
                                .QueryInterface(Ci.nsIDOMChromeWindow);

      let setupFinished = false;
      slowScriptDebugHandler(chromeWindow.gBrowser.selectedTab,
        () => {
          setupFinished = true;
        });

      // Don't return from the interrupt handler until the debugger is brought
      // up; no reason to continue executing the slow script.
      let utils = window.QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsIDOMWindowUtils);
      utils.enterModalState();
      Services.tm.spinEventLoopUntil(() => {
        return setupFinished;
      });
      utils.leaveModalState();
    };

    debugService.remoteActivationHandler = function (browser, callback) {
      let chromeWindow = browser.ownerDocument.defaultView;
      let tab = chromeWindow.gBrowser.getTabForBrowser(browser);
      chromeWindow.gBrowser.selected = tab;

      slowScriptDebugHandler(tab, function () {
        callback.finishDebuggerStartup();
      });
    };
  },

  /**
   * Unset the slow script debug handler.
   */
  unsetSlowScriptDebugHandler() {
    let debugService = Cc["@mozilla.org/dom/slow-script-debug;1"]
                         .getService(Ci.nsISlowScriptDebug);
    debugService.activationHandler = undefined;
  },

  /**
   * Add the menuitem for a tool to all open browser windows.
   *
   * @param {object} toolDefinition
   *        properties of the tool to add
   */
  _addToolToWindows(toolDefinition) {
    // No menu item or global shortcut is required for options panel.
    if (!toolDefinition.inMenu) {
      return;
    }

    // Skip if the tool is disabled.
    try {
      if (toolDefinition.visibilityswitch &&
         !Services.prefs.getBoolPref(toolDefinition.visibilityswitch)) {
        return;
      }
    } catch (e) {
      // Prevent breaking everything if the pref doesn't exists.
    }

    // We need to insert the new tool in the right place, which means knowing
    // the tool that comes before the tool that we're trying to add
    let allDefs = gDevTools.getToolDefinitionArray();
    let prevDef;
    for (let def of allDefs) {
      if (!def.inMenu) {
        continue;
      }
      if (def === toolDefinition) {
        break;
      }
      prevDef = def;
    }

    for (let win of gDevToolsBrowser._trackedBrowserWindows) {
      BrowserMenus.insertToolMenuElements(win.document, toolDefinition, prevDef);
    }

    if (toolDefinition.id === "jsdebugger") {
      gDevToolsBrowser.setSlowScriptDebugHandler();
    }
  },

  hasToolboxOpened(win) {
    let tab = win.gBrowser.selectedTab;
    for (let [target, ] of gDevTools._toolboxes) {
      if (target.tab == tab) {
        return true;
      }
    }
    return false;
  },

  /**
   * Update the "Toggle Tools" checkbox in the developer tools menu. This is
   * called when a toolbox is created or destroyed.
   */
  _updateMenuCheckbox() {
    for (let win of gDevToolsBrowser._trackedBrowserWindows) {
      let hasToolbox = gDevToolsBrowser.hasToolboxOpened(win);

      let menu = win.document.getElementById("menu_devToolbox");
      if (hasToolbox) {
        menu.setAttribute("checked", "true");
      } else {
        menu.removeAttribute("checked");
      }
    }
  },

  /**
   * Remove the menuitem for a tool to all open browser windows.
   *
   * @param {string} toolId
   *        id of the tool to remove
   */
  _removeToolFromWindows(toolId) {
    for (let win of gDevToolsBrowser._trackedBrowserWindows) {
      BrowserMenus.removeToolFromMenu(toolId, win.document);
    }

    if (toolId === "jsdebugger") {
      gDevToolsBrowser.unsetSlowScriptDebugHandler();
    }
  },

  /**
   * Called on browser unload to remove menu entries, toolboxes and event
   * listeners from the closed browser window.
   *
   * @param  {XULWindow} win
   *         The window containing the menu entry
   */
  _forgetBrowserWindow(win) {
    if (!gDevToolsBrowser._trackedBrowserWindows.has(win)) {
      return;
    }
    gDevToolsBrowser._trackedBrowserWindows.delete(win);
    win.removeEventListener("unload", this);

    BrowserMenus.removeMenus(win.document);

    // Destroy toolboxes for closed window
    for (let [target, toolbox] of gDevTools._toolboxes) {
      if (target.tab && target.tab.ownerDocument.defaultView == win) {
        toolbox.destroy();
      }
    }

    let styleSheet = this._browserStyleSheets.get(win);
    if (styleSheet) {
      styleSheet.remove();
      this._browserStyleSheets.delete(win);
    }

    this._toolbars.delete(win);

    let tabContainer = win.gBrowser.tabContainer;
    tabContainer.removeEventListener("TabSelect", this);
  },

  handleEvent(event) {
    switch (event.type) {
      case "TabSelect":
        gDevToolsBrowser._updateMenuCheckbox();
        break;
      case "unload":
        // top-level browser window unload
        gDevToolsBrowser._forgetBrowserWindow(event.target.defaultView);
        break;
    }
  },

  /**
   * Either the SDK Loader has been destroyed by the add-on contribution
   * workflow, or firefox is shutting down.

   * @param {boolean} shuttingDown
   *        True if firefox is currently shutting down. We may prevent doing
   *        some cleanups to speed it up. Otherwise everything need to be
   *        cleaned up in order to be able to load devtools again.
   */
  destroy({ shuttingDown }) {
    Services.prefs.removeObserver("devtools.", gDevToolsBrowser);
    Services.obs.removeObserver(gDevToolsBrowser, "browser-delayed-startup-finished");
    Services.obs.removeObserver(gDevToolsBrowser, "quit-application");
    Services.obs.removeObserver(gDevToolsBrowser, "sdk:loader:destroy");

    for (let win of gDevToolsBrowser._trackedBrowserWindows) {
      gDevToolsBrowser._forgetBrowserWindow(win);
    }

    // Remove scripts loaded in content process to support the Browser Content Toolbox.
    DebuggerServer.removeContentServerScript();

    gDevTools.destroy({ shuttingDown });
  },
};

// Handle all already registered tools,
gDevTools.getToolDefinitionArray()
         .forEach(def => gDevToolsBrowser._addToolToWindows(def));
// and the new ones.
gDevTools.on("tool-registered", function (ev, toolId) {
  let toolDefinition = gDevTools._tools.get(toolId);
  // If the tool has been registered globally, add to all the
  // available windows.
  if (toolDefinition) {
    gDevToolsBrowser._addToolToWindows(toolDefinition);
  }
});

gDevTools.on("tool-unregistered", function (ev, toolId) {
  gDevToolsBrowser._removeToolFromWindows(toolId);
});

gDevTools.on("toolbox-ready", gDevToolsBrowser._updateMenuCheckbox);
gDevTools.on("toolbox-destroyed", gDevToolsBrowser._updateMenuCheckbox);

Services.obs.addObserver(gDevToolsBrowser, "quit-application");
Services.obs.addObserver(gDevToolsBrowser, "browser-delayed-startup-finished");
// Watch for module loader unload. Fires when the tools are reloaded.
Services.obs.addObserver(gDevToolsBrowser, "sdk:loader:destroy");

// Fake end of browser window load event for all already opened windows
// that is already fully loaded.
let enumerator = Services.wm.getEnumerator(gDevTools.chromeWindowType);
while (enumerator.hasMoreElements()) {
  let win = enumerator.getNext();
  if (win.gBrowserInit && win.gBrowserInit.delayedStartupFinished) {
    gDevToolsBrowser._registerBrowserWindow(win);
  }
}
PK
!<�G��&^&^=chrome/devtools/modules/devtools/client/framework/devtools.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {Cu} = require("chrome");
const Services = require("Services");

const {DevToolsShim} = require("chrome://devtools-shim/content/DevToolsShim.jsm");

// Load gDevToolsBrowser toolbox lazily as they need gDevTools to be fully initialized
loader.lazyRequireGetter(this, "TargetFactory", "devtools/client/framework/target", true);
loader.lazyRequireGetter(this, "TabTarget", "devtools/client/framework/target", true);
loader.lazyRequireGetter(this, "Toolbox", "devtools/client/framework/toolbox", true);
loader.lazyRequireGetter(this, "ToolboxHostManager", "devtools/client/framework/toolbox-host-manager", true);
loader.lazyRequireGetter(this, "gDevToolsBrowser", "devtools/client/framework/devtools-browser", true);
loader.lazyRequireGetter(this, "HUDService", "devtools/client/webconsole/hudservice", true);
loader.lazyImporter(this, "ScratchpadManager", "resource://devtools/client/scratchpad/scratchpad-manager.jsm");
loader.lazyImporter(this, "BrowserToolboxProcess", "resource://devtools/client/framework/ToolboxProcess.jsm");

loader.lazyRequireGetter(this, "WebExtensionInspectedWindowFront",
      "devtools/shared/fronts/webextension-inspected-window", true);

const {defaultTools: DefaultTools, defaultThemes: DefaultThemes} =
  require("devtools/client/definitions");
const EventEmitter = require("devtools/shared/old-event-emitter");
const {Task} = require("devtools/shared/task");
const {getTheme, setTheme, addThemeObserver, removeThemeObserver} =
  require("devtools/client/shared/theme");

const FORBIDDEN_IDS = new Set(["toolbox", ""]);
const MAX_ORDINAL = 99;

/**
 * DevTools is a class that represents a set of developer tools, it holds a
 * set of tools and keeps track of open toolboxes in the browser.
 */
function DevTools() {
  this._tools = new Map(); // Map<toolId, tool>
  this._themes = new Map(); // Map<themeId, theme>
  this._toolboxes = new Map(); // Map<target, toolbox>
  // List of toolboxes that are still in process of creation
  this._creatingToolboxes = new Map(); // Map<target, toolbox Promise>

  EventEmitter.decorate(this);

  // Listen for changes to the theme pref.
  this._onThemeChanged = this._onThemeChanged.bind(this);
  addThemeObserver(this._onThemeChanged);

  // This is important step in initialization codepath where we are going to
  // start registering all default tools and themes: create menuitems, keys, emit
  // related events.
  this.registerDefaults();

  // Register this new DevTools instance to Firefox. DevToolsShim is part of Firefox,
  // integrating with all Firefox codebase and making the glue between code from
  // mozilla-central and DevTools add-on on github
  DevToolsShim.register(this);
}

DevTools.prototype = {
  // The windowtype of the main window, used in various tools. This may be set
  // to something different by other gecko apps.
  chromeWindowType: "navigator:browser",

  registerDefaults() {
    // Ensure registering items in the sorted order (getDefault* functions
    // return sorted lists)
    this.getDefaultTools().forEach(definition => this.registerTool(definition));
    this.getDefaultThemes().forEach(definition => this.registerTheme(definition));
  },

  unregisterDefaults() {
    for (let definition of this.getToolDefinitionArray()) {
      this.unregisterTool(definition.id);
    }
    for (let definition of this.getThemeDefinitionArray()) {
      this.unregisterTheme(definition.id);
    }
  },

  /**
   * Register a new developer tool.
   *
   * A definition is a light object that holds different information about a
   * developer tool. This object is not supposed to have any operational code.
   * See it as a "manifest".
   * The only actual code lives in the build() function, which will be used to
   * start an instance of this tool.
   *
   * Each toolDefinition has the following properties:
   * - id: Unique identifier for this tool (string|required)
   * - visibilityswitch: Property name to allow us to hide this tool from the
   *                     DevTools Toolbox.
   *                     A falsy value indicates that it cannot be hidden.
   * - icon: URL pointing to a graphic which will be used as the src for an
   *         16x16 img tag (string|required)
   * - url: URL pointing to a XUL/XHTML document containing the user interface
   *        (string|required)
   * - label: Localized name for the tool to be displayed to the user
   *          (string|required)
   * - hideInOptions: Boolean indicating whether or not this tool should be
                      shown in toolbox options or not. Defaults to false.
   *                  (boolean)
   * - build: Function that takes an iframe, which has been populated with the
   *          markup from |url|, and also the toolbox containing the panel.
   *          And returns an instance of ToolPanel (function|required)
   */
  registerTool(toolDefinition) {
    let toolId = toolDefinition.id;

    if (!toolId || FORBIDDEN_IDS.has(toolId)) {
      throw new Error("Invalid definition.id");
    }

    // Make sure that additional tools will always be able to be hidden.
    // When being called from main.js, defaultTools has not yet been exported.
    // But, we can assume that in this case, it is a default tool.
    if (!DefaultTools.includes(toolDefinition)) {
      toolDefinition.visibilityswitch = "devtools." + toolId + ".enabled";
    }

    this._tools.set(toolId, toolDefinition);

    this.emit("tool-registered", toolId);
  },

  /**
   * Removes all tools that match the given |toolId|
   * Needed so that add-ons can remove themselves when they are deactivated
   *
   * @param {string|object} tool
   *        Definition or the id of the tool to unregister. Passing the
   *        tool id should be avoided as it is a temporary measure.
   * @param {boolean} isQuitApplication
   *        true to indicate that the call is due to app quit, so we should not
   *        cause a cascade of costly events
   */
  unregisterTool(tool, isQuitApplication) {
    let toolId = null;
    if (typeof tool == "string") {
      toolId = tool;
      tool = this._tools.get(tool);
    } else {
      let {Deprecated} = require("resource://gre/modules/Deprecated.jsm");
      Deprecated.warning("Deprecation WARNING: gDevTools.unregisterTool(tool) is " +
        "deprecated. You should unregister a tool using its toolId: " +
        "gDevTools.unregisterTool(toolId).");
      toolId = tool.id;
    }
    this._tools.delete(toolId);

    if (!isQuitApplication) {
      this.emit("tool-unregistered", toolId);
    }
  },

  /**
   * Sorting function used for sorting tools based on their ordinals.
   */
  ordinalSort(d1, d2) {
    let o1 = (typeof d1.ordinal == "number") ? d1.ordinal : MAX_ORDINAL;
    let o2 = (typeof d2.ordinal == "number") ? d2.ordinal : MAX_ORDINAL;
    return o1 - o2;
  },

  getDefaultTools() {
    return DefaultTools.sort(this.ordinalSort);
  },

  getAdditionalTools() {
    let tools = [];
    for (let [, value] of this._tools) {
      if (!DefaultTools.includes(value)) {
        tools.push(value);
      }
    }
    return tools.sort(this.ordinalSort);
  },

  getDefaultThemes() {
    return DefaultThemes.sort(this.ordinalSort);
  },

  /**
   * Get a tool definition if it exists and is enabled.
   *
   * @param {string} toolId
   *        The id of the tool to show
   *
   * @return {ToolDefinition|null} tool
   *         The ToolDefinition for the id or null.
   */
  getToolDefinition(toolId) {
    let tool = this._tools.get(toolId);
    if (!tool) {
      return null;
    } else if (!tool.visibilityswitch) {
      return tool;
    }

    let enabled = Services.prefs.getBoolPref(tool.visibilityswitch, true);

    return enabled ? tool : null;
  },

  /**
   * Allow ToolBoxes to get at the list of tools that they should populate
   * themselves with.
   *
   * @return {Map} tools
   *         A map of the the tool definitions registered in this instance
   */
  getToolDefinitionMap() {
    let tools = new Map();

    for (let [id, definition] of this._tools) {
      if (this.getToolDefinition(id)) {
        tools.set(id, definition);
      }
    }

    return tools;
  },

  /**
   * Tools have an inherent ordering that can't be represented in a Map so
   * getToolDefinitionArray provides an alternative representation of the
   * definitions sorted by ordinal value.
   *
   * @return {Array} tools
   *         A sorted array of the tool definitions registered in this instance
   */
  getToolDefinitionArray() {
    let definitions = [];

    for (let [id, definition] of this._tools) {
      if (this.getToolDefinition(id)) {
        definitions.push(definition);
      }
    }

    return definitions.sort(this.ordinalSort);
  },

  /**
   * Returns the name of the current theme for devtools.
   *
   * @return {string} theme
   *         The name of the current devtools theme.
   */
  getTheme() {
    return getTheme();
  },

  /**
   * Called when the developer tools theme changes.
   */
  _onThemeChanged() {
    this.emit("theme-changed", getTheme());
  },

  /**
   * Register a new theme for developer tools toolbox.
   *
   * A definition is a light object that holds various information about a
   * theme.
   *
   * Each themeDefinition has the following properties:
   * - id: Unique identifier for this theme (string|required)
   * - label: Localized name for the theme to be displayed to the user
   *          (string|required)
   * - stylesheets: Array of URLs pointing to a CSS document(s) containing
   *                the theme style rules (array|required)
   * - classList: Array of class names identifying the theme within a document.
   *              These names are set to document element when applying
   *              the theme (array|required)
   * - onApply: Function that is executed by the framework when the theme
   *            is applied. The function takes the current iframe window
   *            and the previous theme id as arguments (function)
   * - onUnapply: Function that is executed by the framework when the theme
   *            is unapplied. The function takes the current iframe window
   *            and the new theme id as arguments (function)
   */
  registerTheme(themeDefinition) {
    let themeId = themeDefinition.id;

    if (!themeId) {
      throw new Error("Invalid theme id");
    }

    if (this._themes.get(themeId)) {
      throw new Error("Theme with the same id is already registered");
    }

    this._themes.set(themeId, themeDefinition);

    this.emit("theme-registered", themeId);
  },

  /**
   * Removes an existing theme from the list of registered themes.
   * Needed so that add-ons can remove themselves when they are deactivated
   *
   * @param {string|object} theme
   *        Definition or the id of the theme to unregister.
   */
  unregisterTheme(theme) {
    let themeId = null;
    if (typeof theme == "string") {
      themeId = theme;
      theme = this._themes.get(theme);
    } else {
      themeId = theme.id;
    }

    let currTheme = getTheme();

    // Note that we can't check if `theme` is an item
    // of `DefaultThemes` as we end up reloading definitions
    // module and end up with different theme objects
    let isCoreTheme = DefaultThemes.some(t => t.id === themeId);

    // Reset the theme if an extension theme that's currently applied
    // is being removed.
    // Ignore shutdown since addons get disabled during that time.
    if (!Services.startup.shuttingDown &&
        !isCoreTheme &&
        theme.id == currTheme) {
      setTheme("light");

      this.emit("theme-unregistered", theme);
    }

    this._themes.delete(themeId);
  },

  /**
   * Get a theme definition if it exists.
   *
   * @param {string} themeId
   *        The id of the theme
   *
   * @return {ThemeDefinition|null} theme
   *         The ThemeDefinition for the id or null.
   */
  getThemeDefinition(themeId) {
    let theme = this._themes.get(themeId);
    if (!theme) {
      return null;
    }
    return theme;
  },

  /**
   * Get map of registered themes.
   *
   * @return {Map} themes
   *         A map of the the theme definitions registered in this instance
   */
  getThemeDefinitionMap() {
    let themes = new Map();

    for (let [id, definition] of this._themes) {
      if (this.getThemeDefinition(id)) {
        themes.set(id, definition);
      }
    }

    return themes;
  },

  /**
   * Get registered themes definitions sorted by ordinal value.
   *
   * @return {Array} themes
   *         A sorted array of the theme definitions registered in this instance
   */
  getThemeDefinitionArray() {
    let definitions = [];

    for (let [id, definition] of this._themes) {
      if (this.getThemeDefinition(id)) {
        definitions.push(definition);
      }
    }

    return definitions.sort(this.ordinalSort);
  },

  /**
   * Called from SessionStore.jsm in mozilla-central when saving the current state.
   *
   * @param {Object} state
   *                 A SessionStore state object that gets modified by reference
   */
  saveDevToolsSession: function (state) {
    state.browserConsole = HUDService.getBrowserConsoleSessionState();
    state.browserToolbox = BrowserToolboxProcess.getBrowserToolboxSessionState();

    // Check if the module is loaded to avoid loading ScratchpadManager for no reason.
    state.scratchpads = [];
    if (Cu.isModuleLoaded("resource://devtools/client/scratchpad/scratchpad-manager.jsm")) {
      state.scratchpads = ScratchpadManager.getSessionState();
    }
  },

  /**
   * Restore the devtools session state as provided by SessionStore.
   */
  restoreDevToolsSession: function ({scratchpads, browserConsole, browserToolbox}) {
    if (scratchpads) {
      ScratchpadManager.restoreSession(scratchpads);
    }

    if (browserToolbox) {
      BrowserToolboxProcess.init();
    }

    if (browserConsole && !HUDService.getBrowserConsole()) {
      HUDService.toggleBrowserConsole();
    }
  },

  /**
   * Boolean, true, if we never opened a toolbox.
   * Used to implement the telemetry tracking toolbox opening.
   */
  _firstShowToolbox: true,

  /**
   * Show a Toolbox for a target (either by creating a new one, or if a toolbox
   * already exists for the target, by bring to the front the existing one)
   * If |toolId| is specified then the displayed toolbox will have the
   * specified tool selected.
   * If |hostType| is specified then the toolbox will be displayed using the
   * specified HostType.
   *
   * @param {Target} target
   *         The target the toolbox will debug
   * @param {string} toolId
   *        The id of the tool to show
   * @param {Toolbox.HostType} hostType
   *        The type of host (bottom, window, side)
   * @param {object} hostOptions
   *        Options for host specifically
   * @param {Number} startTime
   *        Optional, indicates the time at which the user event related to this toolbox
   *        opening started. This is a `performance.now()` timing.
   *
   * @return {Toolbox} toolbox
   *        The toolbox that was opened
   */
  showToolbox: Task.async(function* (target, toolId, hostType, hostOptions, startTime) {
    let toolbox = this._toolboxes.get(target);
    if (toolbox) {
      if (hostType != null && toolbox.hostType != hostType) {
        yield toolbox.switchHost(hostType);
      }

      if (toolId != null && toolbox.currentToolId != toolId) {
        yield toolbox.selectTool(toolId);
      }

      toolbox.raise();
    } else {
      // As toolbox object creation is async, we have to be careful about races
      // Check for possible already in process of loading toolboxes before
      // actually trying to create a new one.
      let promise = this._creatingToolboxes.get(target);
      if (promise) {
        return yield promise;
      }
      let toolboxPromise = this.createToolbox(target, toolId, hostType, hostOptions);
      this._creatingToolboxes.set(target, toolboxPromise);
      toolbox = yield toolboxPromise;
      this._creatingToolboxes.delete(target);

      if (startTime) {
        this.logToolboxOpenTime(toolbox.currentToolId, startTime);
      }
      this._firstShowToolbox = false;
    }
    return toolbox;
  }),

  /**
   * Log telemetry related to toolbox opening.
   * Two distinct probes are logged. One for cold startup, when we open the very first
   * toolbox. This one includes devtools framework loading. And a second one for all
   * subsequent toolbox opening, which should all be faster.
   * These two probes are indexed by Tool ID.
   *
   * @param {String} toolId
   *        The id of the opened tool.
   * @param {Number} startTime
   *        Indicates the time at which the user event related to the toolbox
   *        opening started. This is a `performance.now()` timing.
   */
  logToolboxOpenTime(toolId, startTime) {
    let { performance } = Services.appShell.hiddenDOMWindow;
    let delay = performance.now() - startTime;
    let telemetryKey = this._firstShowToolbox ?
      "DEVTOOLS_COLD_TOOLBOX_OPEN_DELAY_MS" : "DEVTOOLS_WARM_TOOLBOX_OPEN_DELAY_MS";
    let histogram = Services.telemetry.getKeyedHistogramById(telemetryKey);
    histogram.add(toolId, delay);
  },

  createToolbox: Task.async(function* (target, toolId, hostType, hostOptions) {
    let manager = new ToolboxHostManager(target, hostType, hostOptions);

    let toolbox = yield manager.create(toolId);

    this._toolboxes.set(target, toolbox);

    this.emit("toolbox-created", toolbox);

    toolbox.once("destroy", () => {
      this.emit("toolbox-destroy", target);
    });

    toolbox.once("destroyed", () => {
      this._toolboxes.delete(target);
      this.emit("toolbox-destroyed", target);
    });

    yield toolbox.open();
    this.emit("toolbox-ready", toolbox);

    return toolbox;
  }),

  /**
   * Return the toolbox for a given target.
   *
   * @param  {object} target
   *         Target value e.g. the target that owns this toolbox
   *
   * @return {Toolbox} toolbox
   *         The toolbox that is debugging the given target
   */
  getToolbox(target) {
    return this._toolboxes.get(target);
  },

  /**
   * Close the toolbox for a given target
   *
   * @return promise
   *         This promise will resolve to false if no toolbox was found
   *         associated to the target. true, if the toolbox was successfully
   *         closed.
   */
  closeToolbox: Task.async(function* (target) {
    let toolbox = yield this._creatingToolboxes.get(target);
    if (!toolbox) {
      toolbox = this._toolboxes.get(target);
    }
    if (!toolbox) {
      return false;
    }
    yield toolbox.destroy();
    return true;
  }),

  /**
   * Wrapper on TargetFactory.forTab, constructs a Target for the provided tab.
   *
   * @param  {XULTab} tab
   *         The tab to use in creating a new target.
   *
   * @return {TabTarget} A target object
   */
  getTargetForTab: function (tab) {
    return TargetFactory.forTab(tab);
  },

  /**
   * Compatibility layer for web-extensions. Used by DevToolsShim for
   * browser/components/extensions/ext-devtools.js
   *
   * web-extensions need to use dedicated instances of TabTarget and cannot reuse the
   * cached instances managed by DevTools target factory.
   */
  createTargetForTab: function (tab) {
    return new TabTarget(tab);
  },

  /**
   * Compatibility layer for web-extensions. Used by DevToolsShim for
   * browser/components/extensions/ext-devtools-inspectedWindow.js
   */
  createWebExtensionInspectedWindowFront: function (tabTarget) {
    return new WebExtensionInspectedWindowFront(tabTarget.client, tabTarget.form);
  },

  /**
   * Compatibility layer for web-extensions. Used by DevToolsShim for
   * toolkit/components/extensions/ext-c-toolkit.js
   */
  openBrowserConsole: function () {
    let {HUDService} = require("devtools/client/webconsole/hudservice");
    HUDService.openBrowserConsoleOrFocus();
  },

  /**
   * Called from the DevToolsShim, used by nsContextMenu.js.
   *
   * @param {XULTab} tab
   *        The browser tab on which inspect node was used.
   * @param {Array} selectors
   *        An array of CSS selectors to find the target node. Several selectors can be
   *        needed if the element is nested in frames and not directly in the root
   *        document. The selectors are ordered starting with the root document and
   *        ending with the deepest nested frame.
   * @param {Number} startTime
   *        Optional, indicates the time at which the user event related to this node
   *        inspection started. This is a `performance.now()` timing.
   * @return {Promise} a promise that resolves when the node is selected in the inspector
   *         markup view.
   */
  async inspectNode(tab, nodeSelectors, startTime) {
    let target = TargetFactory.forTab(tab);

    let toolbox = await gDevTools.showToolbox(target, "inspector", null, null, startTime);
    let inspector = toolbox.getCurrentPanel();

    // If the toolbox has been switched into a nested frame, we should first remove
    // selectors according to the frame depth.
    nodeSelectors.splice(0, toolbox.selectedFrameDepth);

    // new-node-front tells us when the node has been selected, whether the
    // browser is remote or not.
    let onNewNode = inspector.selection.once("new-node-front");

    // Evaluate the cross iframes query selectors
    async function querySelectors(nodeFront) {
      let selector = nodeSelectors.shift();
      if (!selector) {
        return nodeFront;
      }
      nodeFront = await inspector.walker.querySelector(nodeFront, selector);
      if (nodeSelectors.length > 0) {
        let { nodes } = await inspector.walker.children(nodeFront);
        // This is the NodeFront for the document node inside the iframe
        nodeFront = nodes[0];
      }
      return querySelectors(nodeFront);
    }
    let nodeFront = await inspector.walker.getRootNode();
    nodeFront = await querySelectors(nodeFront);
    // Select the final node
    inspector.selection.setNodeFront(nodeFront, "browser-context-menu");

    await onNewNode;
    // Now that the node has been selected, wait until the inspector is
    // fully updated.
    await inspector.once("inspector-updated");
  },

  /**
   * Either the SDK Loader has been destroyed by the add-on contribution
   * workflow, or firefox is shutting down.

   * @param {boolean} shuttingDown
   *        True if firefox is currently shutting down. We may prevent doing
   *        some cleanups to speed it up. Otherwise everything need to be
   *        cleaned up in order to be able to load devtools again.
   */
  destroy({ shuttingDown }) {
    // Do not cleanup everything during firefox shutdown, but only when
    // devtools are reloaded via the add-on contribution workflow.
    if (!shuttingDown) {
      for (let [, toolbox] of this._toolboxes) {
        toolbox.destroy();
      }
    }

    for (let [key, ] of this.getToolDefinitionMap()) {
      this.unregisterTool(key, true);
    }

    gDevTools.unregisterDefaults();

    removeThemeObserver(this._onThemeChanged);

    // Do not unregister devtools from the DevToolsShim if the destroy is caused by an
    // application shutdown. For instance SessionStore needs to save the Scratchpad
    // manager state on shutdown.
    if (!shuttingDown) {
      // Notify the DevToolsShim that DevTools are no longer available, particularly if
      // the destroy was caused by disabling/removing the DevTools add-on.
      DevToolsShim.unregister();
    }

    // Cleaning down the toolboxes: i.e.
    //   for (let [target, toolbox] of this._toolboxes) toolbox.destroy();
    // Is taken care of by the gDevToolsBrowser.forgetBrowserWindow
  },

  /**
   * Iterator that yields each of the toolboxes.
   */
  * [Symbol.iterator ]() {
    for (let toolbox of this._toolboxes) {
      yield toolbox;
    }
  }
};

const gDevTools = exports.gDevTools = new DevTools();
PK
!<���::?chrome/devtools/modules/devtools/client/framework/gDevTools.jsm/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

/**
 * This JSM is here to keep some compatibility with existing add-ons.
 * Please now use the modules:
 * - devtools/client/framework/devtools for gDevTools
 * - devtools/client/framework/devtools-browser for gDevToolsBrowser
 */

this.EXPORTED_SYMBOLS = [ "gDevTools", "gDevToolsBrowser" ];

const { loader } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});

/**
 * Do not directly map to the commonjs modules so that callsites of
 * gDevTools.jsm do not have to do anything to access to the very last version
 * of the module. The `devtools` and `browser` getter are always going to
 * retrieve the very last version of the modules.
 */
Object.defineProperty(this, "require", {
  get() {
    let { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
    return require;
  }
});
Object.defineProperty(this, "devtools", {
  get() {
    return require("devtools/client/framework/devtools").gDevTools;
  }
});
Object.defineProperty(this, "browser", {
  get() {
    return require("devtools/client/framework/devtools-browser").gDevToolsBrowser;
  }
});

/**
 * gDevTools is a singleton that controls the Firefox Developer Tools.
 *
 * It is an instance of a DevTools class that holds a set of tools. It has the
 * same lifetime as the browser.
 */
let gDevToolsMethods = [
  // Used by: - b2g desktop.js
  //          - nsContextMenu
  //          - /devtools code
  "showToolbox",

  // Used by Addon SDK and /devtools
  "closeToolbox",
  "getToolbox",

  // Used by Addon SDK, main.js and tests:
  "registerTool",
  "registerTheme",
  "unregisterTool",
  "unregisterTheme",

  // Used by main.js and test
  "getToolDefinitionArray",
  "getThemeDefinitionArray",

  // Used by WebExtensions devtools API
  "getTheme",

  // Used by theme-switching.js
  "getThemeDefinition",
  "emit",

  // Used by /devtools
  "on",
  "off",
  "once",

  // Used by tests
  "getToolDefinitionMap",
  "getThemeDefinitionMap",
  "getDefaultTools",
  "getAdditionalTools",
  "getToolDefinition",
];
this.gDevTools = {
  // Used by tests
  get _toolboxes() {
    return devtools._toolboxes;
  },
  get _tools() {
    return devtools._tools;
  },
  *[Symbol.iterator ]() {
    for (let toolbox of this._toolboxes) {
      yield toolbox;
    }
  }
};
gDevToolsMethods.forEach(name => {
  this.gDevTools[name] = (...args) => {
    return devtools[name].apply(devtools, args);
  };
});


/**
 * gDevToolsBrowser exposes functions to connect the gDevTools instance with a
 * Firefox instance.
 */
let gDevToolsBrowserMethods = [
  // used by browser-sets.inc, command
  "toggleToolboxCommand",

  // Used by browser.js itself, by setting a oncommand string...
  "selectToolCommand",

  // Used by browser-sets.inc, command
  "openAboutDebugging",

  // Used by browser-sets.inc, command
  "openConnectScreen",

  // Used by browser-sets.inc, command
  //         itself, webide widget
  "openWebIDE",

  // Used by browser-sets.inc, command
  "openContentProcessToolbox",

  // Used by browser.js
  "registerBrowserWindow",

  // Used by devtools-browser.js for the Toggle Toolbox status
  "hasToolboxOpened",

  // Used by browser.js
  "forgetBrowserWindow"
];
this.gDevToolsBrowser = {
  // Used by webide.js
  get isWebIDEInitialized() {
    return browser.isWebIDEInitialized;
  },
  // Used by a test (should be removed)
  get _trackedBrowserWindows() {
    return browser._trackedBrowserWindows;
  }
};
gDevToolsBrowserMethods.forEach(name => {
  this.gDevToolsBrowser[name] = (...args) => {
    return browser[name].apply(browser, args);
  };
});
PK
!<�����>chrome/devtools/modules/devtools/client/framework/menu-item.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

/**
 * A partial implementation of the MenuItem API provided by electron:
 * https://github.com/electron/electron/blob/master/docs/api/menu-item.md.
 *
 * Missing features:
 *   - id String - Unique within a single menu. If defined then it can be used
 *                 as a reference to this item by the position attribute.
 *   - role String - Define the action of the menu item; when specified the
 *                   click property will be ignored
 *   - sublabel String
 *   - accelerator Accelerator
 *   - icon NativeImage
 *   - position String - This field allows fine-grained definition of the
 *                       specific location within a given menu.
 *
 * Implemented features:
 *  @param Object options
 *    Function click
 *      Will be called with click(menuItem, browserWindow) when the menu item
 *       is clicked
 *    String type
 *      Can be normal, separator, submenu, checkbox or radio
 *    String label
 *    Boolean enabled
 *      If false, the menu item will be greyed out and unclickable.
 *    Boolean checked
 *      Should only be specified for checkbox or radio type menu items.
 *    Menu submenu
 *      Should be specified for submenu type menu items. If submenu is specified,
 *      the type: 'submenu' can be omitted. If the value is not a Menu then it
 *      will be automatically converted to one using Menu.buildFromTemplate.
 *    Boolean visible
 *      If false, the menu item will be entirely hidden.
 */
function MenuItem({
    accesskey = null,
    checked = false,
    click = () => {},
    disabled = false,
    hover = () => {},
    id = null,
    label = "",
    submenu = null,
    type = "normal",
    visible = true,
} = { }) {
  this.accesskey = accesskey;
  this.checked = checked;
  this.click = click;
  this.disabled = disabled;
  this.hover = hover;
  this.id = id;
  this.label = label;
  this.submenu = submenu;
  this.type = type;
  this.visible = visible;
}

module.exports = MenuItem;
PK
!<�K��9chrome/devtools/modules/devtools/client/framework/menu.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 EventEmitter = require("devtools/shared/old-event-emitter");

/**
 * A partial implementation of the Menu API provided by electron:
 * https://github.com/electron/electron/blob/master/docs/api/menu.md.
 *
 * Extra features:
 *  - Emits an 'open' and 'close' event when the menu is opened/closed

 * @param String id (non standard)
 *        Needed so tests can confirm the XUL implementation is working
 */
function Menu({ id = null } = {}) {
  this.menuitems = [];
  this.id = id;

  Object.defineProperty(this, "items", {
    get() {
      return this.menuitems;
    }
  });

  EventEmitter.decorate(this);
}

/**
 * Add an item to the end of the Menu
 *
 * @param {MenuItem} menuItem
 */
Menu.prototype.append = function (menuItem) {
  this.menuitems.push(menuItem);
};

/**
 * Add an item to a specified position in the menu
 *
 * @param {int} pos
 * @param {MenuItem} menuItem
 */
Menu.prototype.insert = function (pos, menuItem) {
  throw Error("Not implemented");
};

/**
 * Show the Menu at a specified location on the screen
 *
 * Missing features:
 *   - browserWindow - BrowserWindow (optional) - Default is null.
 *   - positioningItem Number - (optional) OS X
 *
 * @param {int} screenX
 * @param {int} screenY
 * @param Toolbox toolbox (non standard)
 *        Needed so we in which window to inject XUL
 */
Menu.prototype.popup = function (screenX, screenY, toolbox) {
  let doc = toolbox.doc;
  let popupset = doc.querySelector("popupset");
  // See bug 1285229, on Windows, opening the same popup multiple times in a
  // row ends up duplicating the popup. The newly inserted popup doesn't
  // dismiss the old one. So remove any previously displayed popup before
  // opening a new one.
  let popup = popupset.querySelector("menupopup[menu-api=\"true\"]");
  if (popup) {
    popup.hidePopup();
  }

  popup = doc.createElement("menupopup");
  popup.setAttribute("menu-api", "true");

  if (this.id) {
    popup.id = this.id;
  }
  this._createMenuItems(popup);

  // Remove the menu from the DOM once it's hidden.
  popup.addEventListener("popuphidden", (e) => {
    if (e.target === popup) {
      popup.remove();
      this.emit("close");
    }
  });

  popup.addEventListener("popupshown", (e) => {
    if (e.target === popup) {
      this.emit("open");
    }
  });

  popupset.appendChild(popup);
  popup.openPopupAtScreen(screenX, screenY, true);
};

Menu.prototype._createMenuItems = function (parent) {
  let doc = parent.ownerDocument;
  this.menuitems.forEach(item => {
    if (!item.visible) {
      return;
    }

    if (item.submenu) {
      let menupopup = doc.createElement("menupopup");
      item.submenu._createMenuItems(menupopup);

      let menu = doc.createElement("menu");
      menu.appendChild(menupopup);
      menu.setAttribute("label", item.label);
      if (item.disabled) {
        menu.setAttribute("disabled", "true");
      }
      if (item.accesskey) {
        menu.setAttribute("accesskey", item.accesskey);
      }
      if (item.id) {
        menu.id = item.id;
      }
      parent.appendChild(menu);
    } else if (item.type === "separator") {
      let menusep = doc.createElement("menuseparator");
      parent.appendChild(menusep);
    } else {
      let menuitem = doc.createElement("menuitem");
      menuitem.setAttribute("label", item.label);
      menuitem.addEventListener("command", () => {
        item.click();
      });
      menuitem.addEventListener("DOMMenuItemActive", () => {
        item.hover();
      });

      if (item.type === "checkbox") {
        menuitem.setAttribute("type", "checkbox");
      }
      if (item.type === "radio") {
        menuitem.setAttribute("type", "radio");
      }
      if (item.disabled) {
        menuitem.setAttribute("disabled", "true");
      }
      if (item.checked) {
        menuitem.setAttribute("checked", "true");
      }
      if (item.accesskey) {
        menuitem.setAttribute("accesskey", item.accesskey);
      }
      if (item.id) {
        menuitem.id = item.id;
      }

      parent.appendChild(menuitem);
    }
  });
};

Menu.setApplicationMenu = () => {
  throw Error("Not implemented");
};

Menu.sendActionToFirstResponder = () => {
  throw Error("Not implemented");
};

Menu.buildFromTemplate = () => {
  throw Error("Not implemented");
};

module.exports = Menu;
PK
!<�zl BB>chrome/devtools/modules/devtools/client/framework/selection.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 nodeConstants = require("devtools/shared/dom-node-constants");
var EventEmitter = require("devtools/shared/old-event-emitter");

/**
 * API
 *
 *   new Selection(walker=null)
 *   destroy()
 *   node (readonly)
 *   setNode(node, origin="unknown")
 *
 * Helpers:
 *
 *   window
 *   document
 *   isRoot()
 *   isNode()
 *   isHTMLNode()
 *
 * Check the nature of the node:
 *
 *   isElementNode()
 *   isAttributeNode()
 *   isTextNode()
 *   isCDATANode()
 *   isEntityRefNode()
 *   isEntityNode()
 *   isProcessingInstructionNode()
 *   isCommentNode()
 *   isDocumentNode()
 *   isDocumentTypeNode()
 *   isDocumentFragmentNode()
 *   isNotationNode()
 *
 * Events:
 *   "new-node-front" when the inner node changed
 *   "attribute-changed" when an attribute is changed
 *   "detached-front" when the node (or one of its parents) is removed from
 *   the document
 *   "reparented" when the node (or one of its parents) is moved under
 *   a different node
 */

/**
 * A Selection object. Hold a reference to a node.
 * Includes some helpers, fire some helpful events.
 */
function Selection(walker) {
  EventEmitter.decorate(this);

  this._onMutations = this._onMutations.bind(this);
  this.setNodeFront = this.setNodeFront.bind(this);
  this.setWalker(walker);
}

exports.Selection = Selection;

Selection.prototype = {
  _walker: null,

  _onMutations: function (mutations) {
    let attributeChange = false;
    let pseudoChange = false;
    let detached = false;
    let parentNode = null;

    for (let m of mutations) {
      if (!attributeChange && m.type == "attributes") {
        attributeChange = true;
      }
      if (m.type == "childList") {
        if (!detached && !this.isConnected()) {
          if (this.isNode()) {
            parentNode = m.target;
          }
          detached = true;
        }
      }
      if (m.type == "pseudoClassLock") {
        pseudoChange = true;
      }
    }

    // Fire our events depending on what changed in the mutations array
    if (attributeChange) {
      this.emit("attribute-changed");
    }
    if (pseudoChange) {
      this.emit("pseudoclass");
    }
    if (detached) {
      this.emit("detached-front", parentNode);
    }
  },

  destroy: function () {
    this.setWalker(null);
  },

  setWalker: function (walker) {
    if (this._walker) {
      this._walker.off("mutations", this._onMutations);
    }
    this._walker = walker;
    if (this._walker) {
      this._walker.on("mutations", this._onMutations);
    }
  },

  setNodeFront: function (value, reason = "unknown") {
    this.reason = reason;

    // If an inlineTextChild text node is being set, then set it's parent instead.
    let parentNode = value && value.parentNode();
    if (value && parentNode && parentNode.inlineTextChild === value) {
      value = parentNode;
    }

    this._nodeFront = value;
    this.emit("new-node-front", value, this.reason);
  },

  get documentFront() {
    return this._walker.document(this._nodeFront);
  },

  get nodeFront() {
    return this._nodeFront;
  },

  isRoot: function () {
    return this.isNode() &&
           this.isConnected() &&
           this._nodeFront.isDocumentElement;
  },

  isNode: function () {
    return !!this._nodeFront;
  },

  isConnected: function () {
    let node = this._nodeFront;
    if (!node || !node.actorID) {
      return false;
    }

    while (node) {
      if (node === this._walker.rootNode) {
        return true;
      }
      node = node.parentNode();
    }
    return false;
  },

  isHTMLNode: function () {
    let xhtmlNs = "http://www.w3.org/1999/xhtml";
    return this.isNode() && this.nodeFront.namespaceURI == xhtmlNs;
  },

  // Node type

  isElementNode: function () {
    return this.isNode() && this.nodeFront.nodeType == nodeConstants.ELEMENT_NODE;
  },

  isPseudoElementNode: function () {
    return this.isNode() && this.nodeFront.isPseudoElement;
  },

  isAnonymousNode: function () {
    return this.isNode() && this.nodeFront.isAnonymous;
  },

  isAttributeNode: function () {
    return this.isNode() && this.nodeFront.nodeType == nodeConstants.ATTRIBUTE_NODE;
  },

  isTextNode: function () {
    return this.isNode() && this.nodeFront.nodeType == nodeConstants.TEXT_NODE;
  },

  isCDATANode: function () {
    return this.isNode() && this.nodeFront.nodeType == nodeConstants.CDATA_SECTION_NODE;
  },

  isEntityRefNode: function () {
    return this.isNode() &&
      this.nodeFront.nodeType == nodeConstants.ENTITY_REFERENCE_NODE;
  },

  isEntityNode: function () {
    return this.isNode() && this.nodeFront.nodeType == nodeConstants.ENTITY_NODE;
  },

  isProcessingInstructionNode: function () {
    return this.isNode() &&
      this.nodeFront.nodeType == nodeConstants.PROCESSING_INSTRUCTION_NODE;
  },

  isCommentNode: function () {
    return this.isNode() &&
      this.nodeFront.nodeType == nodeConstants.PROCESSING_INSTRUCTION_NODE;
  },

  isDocumentNode: function () {
    return this.isNode() && this.nodeFront.nodeType == nodeConstants.DOCUMENT_NODE;
  },

  /**
   * @returns true if the selection is the <body> HTML element.
   */
  isBodyNode: function () {
    return this.isHTMLNode() &&
           this.isConnected() &&
           this.nodeFront.nodeName === "BODY";
  },

  /**
   * @returns true if the selection is the <head> HTML element.
   */
  isHeadNode: function () {
    return this.isHTMLNode() &&
           this.isConnected() &&
           this.nodeFront.nodeName === "HEAD";
  },

  isDocumentTypeNode: function () {
    return this.isNode() && this.nodeFront.nodeType == nodeConstants.DOCUMENT_TYPE_NODE;
  },

  isDocumentFragmentNode: function () {
    return this.isNode() &&
      this.nodeFront.nodeType == nodeConstants.DOCUMENT_FRAGMENT_NODE;
  },

  isNotationNode: function () {
    return this.isNode() && this.nodeFront.nodeType == nodeConstants.NOTATION_NODE;
  },
};
PK
!<h;j�D�D<chrome/devtools/modules/devtools/client/framework/sidebar.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var Services = require("Services");
var {Task} = require("devtools/shared/task");
var EventEmitter = require("devtools/shared/event-emitter");
var Telemetry = require("devtools/client/shared/telemetry");

const {LocalizationHelper} = require("devtools/shared/l10n");
const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");

const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";

/**
 * ToolSidebar provides methods to register tabs in the sidebar.
 * It's assumed that the sidebar contains a xul:tabbox.
 * Typically, you'll want the tabbox parameter to be a XUL tabbox like this:
 *
 * <tabbox id="inspector-sidebar" handleCtrlTab="false" class="devtools-sidebar-tabs">
 *   <tabs/>
 *   <tabpanels flex="1"/>
 * </tabbox>
 *
 * The ToolSidebar API has a method to add new tabs, so the tabs and tabpanels
 * nodes can be empty. But they can also already contain items before the
 * ToolSidebar is created.
 *
 * Tabs added through the addTab method are only identified by an ID and a URL
 * which is used as the href of an iframe node that is inserted in the newly
 * created tabpanel.
 * Tabs already present before the ToolSidebar is created may contain anything.
 * However, these tabs must have ID attributes if it is required for the various
 * methods that accept an ID as argument to work here.
 *
 * @param {Node} tabbox
 *  <tabbox> node;
 * @param {ToolPanel} panel
 *  Related ToolPanel instance;
 * @param {String} uid
 *  Unique ID
 * @param {Object} options
 *  - hideTabstripe: Should the tabs be hidden. Defaults to false
 *  - showAllTabsMenu: Should a drop-down menu be displayed in case tabs
 *    become hidden. Defaults to false.
 *  - disableTelemetry: By default, switching tabs on and off in the sidebar
 *    will record tool usage in telemetry, pass this option to true to avoid it.
 *
 * Events raised:
 * - new-tab-registered : After a tab has been added via addTab. The tab ID
 *   is passed with the event. This however, is raised before the tab iframe
 *   is fully loaded.
 * - <tabid>-ready : After the tab iframe has been loaded
 * - <tabid>-selected : After tab <tabid> was selected
 * - select : Same as above, but for any tab, the ID is passed with the event
 * - <tabid>-unselected : After tab <tabid> is unselected
 */
function ToolSidebar(tabbox, panel, uid, options = {}) {
  EventEmitter.decorate(this);

  this._tabbox = tabbox;
  this._uid = uid;
  this._panelDoc = this._tabbox.ownerDocument;
  this._toolPanel = panel;
  this._options = options;

  this._onTabBoxOverflow = this._onTabBoxOverflow.bind(this);
  this._onTabBoxUnderflow = this._onTabBoxUnderflow.bind(this);

  try {
    this._width = Services.prefs.getIntPref("devtools.toolsidebar-width." + this._uid);
  } catch (e) {}

  if (!options.disableTelemetry) {
    this._telemetry = new Telemetry();
  }

  this._tabbox.tabpanels.addEventListener("select", this, true);

  this._tabs = new Map();

  // Check for existing tabs in the DOM and add them.
  this.addExistingTabs();

  if (this._options.hideTabstripe) {
    this._tabbox.setAttribute("hidetabs", "true");
  }

  if (this._options.showAllTabsMenu) {
    this.addAllTabsMenu();
  }

  this._toolPanel.emit("sidebar-created", this);
}

exports.ToolSidebar = ToolSidebar;

ToolSidebar.prototype = {
  TAB_ID_PREFIX: "sidebar-tab-",

  TABPANEL_ID_PREFIX: "sidebar-panel-",

  /**
   * Add a "…" button at the end of the tabstripe that toggles a dropdown menu
   * containing the list of all tabs if any become hidden due to lack of room.
   *
   * If the ToolSidebar was created with the "showAllTabsMenu" option set to
   * true, this is already done automatically. If not, you may call this
   * function at any time to add the menu.
   */
  addAllTabsMenu: function () {
    if (this._allTabsBtn) {
      return;
    }

    let tabs = this._tabbox.tabs;

    // Create a container and insert it first in the tabbox
    let allTabsContainer = this._panelDoc.createElementNS(XULNS, "stack");
    this._tabbox.insertBefore(allTabsContainer, tabs);

    // Move the tabs inside and make them flex
    allTabsContainer.appendChild(tabs);
    tabs.setAttribute("flex", "1");

    // Create the dropdown menu next to the tabs
    this._allTabsBtn = this._panelDoc.createElementNS(XULNS, "toolbarbutton");
    this._allTabsBtn.setAttribute("class", "devtools-sidebar-alltabs");
    this._allTabsBtn.setAttribute("end", "0");
    this._allTabsBtn.setAttribute("top", "0");
    this._allTabsBtn.setAttribute("width", "15");
    this._allTabsBtn.setAttribute("type", "menu");
    this._allTabsBtn.setAttribute("tooltiptext",
      L10N.getStr("sidebar.showAllTabs.tooltip"));
    this._allTabsBtn.setAttribute("hidden", "true");
    allTabsContainer.appendChild(this._allTabsBtn);

    let menuPopup = this._panelDoc.createElementNS(XULNS, "menupopup");
    this._allTabsBtn.appendChild(menuPopup);

    // Listening to tabs overflow event to toggle the alltabs button
    tabs.addEventListener("overflow", this._onTabBoxOverflow);
    tabs.addEventListener("underflow", this._onTabBoxUnderflow);

    // Add menuitems to the alltabs menu if there are already tabs in the
    // sidebar
    for (let [id, tab] of this._tabs) {
      let item = this._addItemToAllTabsMenu(id, tab, {
        selected: tab.hasAttribute("selected")
      });
      if (tab.hidden) {
        item.hidden = true;
      }
    }
  },

  removeAllTabsMenu: function () {
    if (!this._allTabsBtn) {
      return;
    }

    let tabs = this._tabbox.tabs;

    tabs.removeEventListener("overflow", this._onTabBoxOverflow);
    tabs.removeEventListener("underflow", this._onTabBoxUnderflow);

    // Moving back the tabs as a first child of the tabbox
    this._tabbox.insertBefore(tabs, this._tabbox.tabpanels);
    this._tabbox.querySelector("stack").remove();

    this._allTabsBtn = null;
  },

  _onTabBoxOverflow: function () {
    this._allTabsBtn.removeAttribute("hidden");
  },

  _onTabBoxUnderflow: function () {
    this._allTabsBtn.setAttribute("hidden", "true");
  },

  /**
   * Add an item in the allTabs menu for a given tab.
   */
  _addItemToAllTabsMenu: function (id, tab, options) {
    if (!this._allTabsBtn) {
      return;
    }

    let item = this._panelDoc.createElementNS(XULNS, "menuitem");
    let idPrefix = "sidebar-alltabs-item-";
    item.setAttribute("id", idPrefix + id);
    item.setAttribute("label", tab.getAttribute("label"));
    item.setAttribute("type", "checkbox");
    if (options.selected) {
      item.setAttribute("checked", true);
    }
    // The auto-checking of menuitems in this menu doesn't work, so let's do
    // it manually
    item.setAttribute("autocheck", false);

    let menu = this._allTabsBtn.querySelector("menupopup");
    if (options.insertBefore) {
      let referenceItem = menu.querySelector(`#${idPrefix}${options.insertBefore}`);
      menu.insertBefore(item, referenceItem);
    } else {
      menu.appendChild(item);
    }

    item.addEventListener("click", () => {
      this._tabbox.selectedTab = tab;
    });

    tab.allTabsMenuItem = item;

    return item;
  },

  /**
   * Register a tab. A tab is a document.
   * The document must have a title, which will be used as the name of the tab.
   *
   * @param {string} id The unique id for this tab.
   * @param {string} url The URL of the document to load in this new tab.
   * @param {Object} options A set of options for this new tab:
   * - {Boolean} selected Set to true to make this new tab selected by default.
   * - {String} insertBefore By default, the new tab is appended at the end of the
   * tabbox, pass the ID of an existing tab to insert it before that tab instead.
   */
  addTab: function (id, url, options = {}) {
    let iframe = this._panelDoc.createElementNS(XULNS, "iframe");
    iframe.className = "iframe-" + id;
    iframe.setAttribute("flex", "1");
    iframe.setAttribute("src", url);
    iframe.tooltip = "aHTMLTooltip";

    // Creating the tab and adding it to the tabbox
    let tab = this._panelDoc.createElementNS(XULNS, "tab");

    tab.setAttribute("id", this.TAB_ID_PREFIX + id);
    tab.setAttribute("crop", "end");
    // Avoid showing "undefined" while the tab is loading
    tab.setAttribute("label", "");

    if (options.insertBefore) {
      let referenceTab = this.getTab(options.insertBefore);
      this._tabbox.tabs.insertBefore(tab, referenceTab);
    } else {
      this._tabbox.tabs.appendChild(tab);
    }

    // Add the tab to the allTabs menu if exists
    let allTabsItem = this._addItemToAllTabsMenu(id, tab, options);

    let onIFrameLoaded = (event) => {
      let doc = event.target;
      let win = doc.defaultView;
      tab.setAttribute("label", doc.title);

      if (allTabsItem) {
        allTabsItem.setAttribute("label", doc.title);
      }

      iframe.removeEventListener("load", onIFrameLoaded, true);
      if ("setPanel" in win) {
        win.setPanel(this._toolPanel, iframe);
      }
      this.emit(id + "-ready");
    };

    iframe.addEventListener("load", onIFrameLoaded, true);

    let tabpanel = this._panelDoc.createElementNS(XULNS, "tabpanel");
    tabpanel.setAttribute("id", this.TABPANEL_ID_PREFIX + id);
    tabpanel.appendChild(iframe);

    if (options.insertBefore) {
      let referenceTabpanel = this.getTabPanel(options.insertBefore);
      this._tabbox.tabpanels.insertBefore(tabpanel, referenceTabpanel);
    } else {
      this._tabbox.tabpanels.appendChild(tabpanel);
    }

    this._tooltip = this._panelDoc.createElementNS(XULNS, "tooltip");
    this._tooltip.id = "aHTMLTooltip";
    tabpanel.appendChild(this._tooltip);
    this._tooltip.page = true;

    tab.linkedPanel = this.TABPANEL_ID_PREFIX + id;

    // We store the index of this tab.
    this._tabs.set(id, tab);

    if (options.selected) {
      this._selectTabSoon(id);
    }

    this.emit("new-tab-registered", id);
  },

  untitledTabsIndex: 0,

  /**
   * Search for existing tabs in the markup that aren't know yet and add them.
   */
  addExistingTabs: function () {
    let knownTabs = [...this._tabs.values()];

    for (let tab of this._tabbox.tabs.querySelectorAll("tab")) {
      if (knownTabs.includes(tab)) {
        continue;
      }

      // Find an ID for this unknown tab
      let id = tab.getAttribute("id") || "untitled-tab-" + (this.untitledTabsIndex++);

      // If the existing tab contains the tab ID prefix, extract the ID of the
      // tab
      if (id.startsWith(this.TAB_ID_PREFIX)) {
        id = id.split(this.TAB_ID_PREFIX).pop();
      }

      // Register the tab
      this._tabs.set(id, tab);
      this.emit("new-tab-registered", id);
    }
  },

  /**
   * Remove an existing tab.
   * @param {String} tabId The ID of the tab that was used to register it, or
   * the tab id attribute value if the tab existed before the sidebar got created.
   * @param {String} tabPanelId Optional. If provided, this ID will be used
   * instead of the tabId to retrieve and remove the corresponding <tabpanel>
   */
  removeTab: Task.async(function* (tabId, tabPanelId) {
    // Remove the tab if it can be found
    let tab = this.getTab(tabId);
    if (!tab) {
      return;
    }

    let win = this.getWindowForTab(tabId);
    if (win && ("destroy" in win)) {
      yield win.destroy();
    }

    tab.remove();

    // Also remove the tabpanel
    let panel = this.getTabPanel(tabPanelId || tabId);
    if (panel) {
      panel.remove();
    }

    this._tabs.delete(tabId);
    this.emit("tab-unregistered", tabId);
  }),

  /**
   * Show or hide a specific tab.
   * @param {Boolean} isVisible True to show the tab/tabpanel, False to hide it.
   * @param {String} id The ID of the tab to be hidden.
   */
  toggleTab: function (isVisible, id) {
    // Toggle the tab.
    let tab = this.getTab(id);
    if (!tab) {
      return;
    }
    tab.hidden = !isVisible;

    // Toggle the item in the allTabs menu.
    if (this._allTabsBtn) {
      this._allTabsBtn.querySelector("#sidebar-alltabs-item-" + id).hidden = !isVisible;
    }
  },

  /**
   * Select a specific tab.
   */
  select: function (id) {
    let tab = this.getTab(id);
    if (tab) {
      this._tabbox.selectedTab = tab;
    }
  },

  /**
   * Hack required to select a tab right after it was created.
   *
   * @param  {String} id
   *         The sidebar tab id to select.
   */
  _selectTabSoon: function (id) {
    this._panelDoc.defaultView.setTimeout(() => {
      this.select(id);
    }, 0);
  },

  /**
   * Return the id of the selected tab.
   */
  getCurrentTabID: function () {
    let currentID = null;
    for (let [id, tab] of this._tabs) {
      if (this._tabbox.tabs.selectedItem == tab) {
        currentID = id;
        break;
      }
    }
    return currentID;
  },

  /**
   * Returns the requested tab panel based on the id.
   * @param {String} id
   * @return {DOMNode}
   */
  getTabPanel: function (id) {
    // Search with and without the ID prefix as there might have been existing
    // tabpanels by the time the sidebar got created
    return this._tabbox.tabpanels.querySelector("#" + this.TABPANEL_ID_PREFIX + id + ", #" + id);
  },

  /**
   * Return the tab based on the provided id, if one was registered with this id.
   * @param {String} id
   * @return {DOMNode}
   */
  getTab: function (id) {
    // FIXME: A workaround for broken browser_net_raw_headers.js failure only in non-e10s mode
    return this._tabs && this._tabs.get(id);
  },

  /**
   * Event handler.
   */
  handleEvent: function (event) {
    if (event.type !== "select" || this._destroyed) {
      return;
    }

    if (this._currentTool == this.getCurrentTabID()) {
      // Tool hasn't changed.
      return;
    }

    let previousTool = this._currentTool;
    this._currentTool = this.getCurrentTabID();
    if (previousTool) {
      if (this._telemetry) {
        this._telemetry.toolClosed(previousTool);
      }
      this.emit(previousTool + "-unselected");
    }

    if (this._telemetry) {
      this._telemetry.toolOpened(this._currentTool);
    }

    this.emit(this._currentTool + "-selected");
    this.emit("select", this._currentTool);

    // Handlers for "select"/"...-selected"/"...-unselected" events might have
    // destroyed the sidebar in the meantime.
    if (this._destroyed) {
      return;
    }

    // Handle menuitem selection if the allTabsMenu is there by unchecking all
    // items except the selected one.
    let tab = this._tabbox.selectedTab;
    if (tab.allTabsMenuItem) {
      for (let otherItem of this._allTabsBtn.querySelectorAll("menuitem")) {
        otherItem.removeAttribute("checked");
      }
      tab.allTabsMenuItem.setAttribute("checked", true);
    }
  },

  /**
   * Toggle sidebar's visibility state.
   */
  toggle: function () {
    if (this._tabbox.hasAttribute("hidden")) {
      this.show();
    } else {
      this.hide();
    }
  },

  /**
   * Show the sidebar.
   *
   * @param  {String} id
   *         The sidebar tab id to select.
   */
  show: function (id) {
    if (this._width) {
      this._tabbox.width = this._width;
    }
    this._tabbox.removeAttribute("hidden");

    // If an id is given, select the corresponding sidebar tab and record the
    // tool opened.
    if (id) {
      this._currentTool = id;

      if (this._telemetry) {
        this._telemetry.toolOpened(this._currentTool);
      }

      this._selectTabSoon(id);
    }

    this.emit("show");
  },

  /**
   * Show the sidebar.
   */
  hide: function () {
    Services.prefs.setIntPref("devtools.toolsidebar-width." + this._uid, this._tabbox.width);
    this._tabbox.setAttribute("hidden", "true");
    this._panelDoc.activeElement.blur();

    this.emit("hide");
  },

  /**
   * Return the window containing the tab content.
   */
  getWindowForTab: function (id) {
    if (!this._tabs.has(id)) {
      return null;
    }

    // Get the tabpanel and make sure it contains an iframe
    let panel = this.getTabPanel(id);
    if (!panel || !panel.firstChild || !panel.firstChild.contentWindow) {
      return;
    }
    return panel.firstChild.contentWindow;
  },

  /**
   * Clean-up.
   */
  destroy: Task.async(function* () {
    if (this._destroyed) {
      return;
    }
    this._destroyed = true;

    Services.prefs.setIntPref("devtools.toolsidebar-width." + this._uid, this._tabbox.width);

    if (this._allTabsBtn) {
      this.removeAllTabsMenu();
    }

    this._tabbox.tabpanels.removeEventListener("select", this, true);

    // Note that we check for the existence of this._tabbox.tabpanels at each
    // step as the container window may have been closed by the time one of the
    // panel's destroy promise resolves.
    while (this._tabbox.tabpanels && this._tabbox.tabpanels.hasChildNodes()) {
      let panel = this._tabbox.tabpanels.firstChild;
      let win = panel.firstChild.contentWindow;
      if (win && ("destroy" in win)) {
        yield win.destroy();
      }
      panel.remove();
    }

    while (this._tabbox.tabs && this._tabbox.tabs.hasChildNodes()) {
      this._tabbox.tabs.firstChild.remove();
    }

    if (this._currentTool && this._telemetry) {
      this._telemetry.toolClosed(this._currentTool);
    }

    this._toolPanel.emit("sidebar-destroyed", this);

    this._tabs = null;
    this._tabbox = null;
    this._panelDoc = null;
    this._toolPanel = null;
  })
};
PK
!<wSG/G/Kchrome/devtools/modules/devtools/client/framework/source-map-url-service.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Services = require("Services");
const SOURCE_MAP_PREF = "devtools.source-map.client-service.enabled";

/**
 * A simple service to track source actors and keep a mapping between
 * original URLs and objects holding the source or style actor's ID
 * (which is used as a cookie by the devtools-source-map service) and
 * the source map URL.
 *
 * @param {object} toolbox
 *        The toolbox.
 * @param {SourceMapService} sourceMapService
 *        The devtools-source-map functions
 */
function SourceMapURLService(toolbox, sourceMapService) {
  this._toolbox = toolbox;
  this._target = toolbox.target;
  this._sourceMapService = sourceMapService;
  // Map from content URLs to descriptors.  Descriptors are later
  // passed to the source map worker.
  this._urls = new Map();
  // Map from (stringified) locations to callbacks that are called
  // when the service decides a location should change (say, a source
  // map is available or the user changes the pref).
  this._subscriptions = new Map();
  // A backward map from actor IDs to the original URL.  This is used
  // to support pretty-printing.
  this._idMap = new Map();

  this._onSourceUpdated = this._onSourceUpdated.bind(this);
  this.reset = this.reset.bind(this);
  this._prefValue = Services.prefs.getBoolPref(SOURCE_MAP_PREF);
  this._onPrefChanged = this._onPrefChanged.bind(this);
  this._onNewStyleSheet = this._onNewStyleSheet.bind(this);

  this._target.on("source-updated", this._onSourceUpdated);
  this._target.on("will-navigate", this.reset);

  Services.prefs.addObserver(SOURCE_MAP_PREF, this._onPrefChanged);

  this._stylesheetsFront = null;
  this._loadingPromise = null;
}

/**
 * Lazy initialization.  Returns a promise that will resolve when all
 * the relevant URLs have been registered.
 */
SourceMapURLService.prototype._getLoadingPromise = function () {
  if (!this._loadingPromise) {
    let styleSheetsLoadingPromise = null;
    this._stylesheetsFront = this._toolbox.initStyleSheetsFront();
    if (this._stylesheetsFront) {
      this._stylesheetsFront.on("stylesheet-added", this._onNewStyleSheet);
      styleSheetsLoadingPromise =
          this._stylesheetsFront.getStyleSheets().then(sheets => {
            sheets.forEach(this._onNewStyleSheet);
          }, () => {
            // Ignore any protocol-based errors.
          });
    }

    // Start fetching the sources now.
    let loadingPromise = this._toolbox.threadClient.getSources().then(({sources}) => {
      // Ignore errors.  Register the sources we got; we can't rely on
      // an event to arrive if the source actor already existed.
      for (let source of sources) {
        this._onSourceUpdated(null, {source});
      }
    }, e => {
      // Also ignore any protocol-based errors.
    });

    this._loadingPromise = Promise.all([styleSheetsLoadingPromise, loadingPromise]);
  }
  return this._loadingPromise;
};

/**
 * Reset the service.  This flushes the internal cache.
 */
SourceMapURLService.prototype.reset = function () {
  this._sourceMapService.clearSourceMaps();
  this._urls.clear();
  this._subscriptions.clear();
  this._idMap.clear();
};

/**
 * Shut down the service, unregistering its event listeners and
 * flushing the cache.  After this call the service will no longer
 * function.
 */
SourceMapURLService.prototype.destroy = function () {
  this.reset();
  this._target.off("source-updated", this._onSourceUpdated);
  this._target.off("will-navigate", this.reset);
  if (this._stylesheetsFront) {
    this._stylesheetsFront.off("stylesheet-added", this._onNewStyleSheet);
  }
  Services.prefs.removeObserver(SOURCE_MAP_PREF, this._onPrefChanged);
  this._target = this._urls = this._subscriptions = this._idMap = null;
};

/**
 * A helper function that is called when a new source is available.
 */
SourceMapURLService.prototype._onSourceUpdated = function (_, sourceEvent) {
  // Maybe we were shut down while waiting.
  if (!this._urls) {
    return;
  }

  let { source } = sourceEvent;
  let { generatedUrl, url, actor: id, sourceMapURL } = source;

  // |generatedUrl| comes from the actor and is extracted from the
  // source code by SpiderMonkey.
  let seenUrl = generatedUrl || url;
  this._urls.set(seenUrl, { id, url: seenUrl, sourceMapURL });
  this._idMap.set(id, seenUrl);
};

/**
 * A helper function that is called when a new style sheet is
 * available.
 * @param {StyleSheetActor} sheet
 *        The new style sheet's actor.
 */
SourceMapURLService.prototype._onNewStyleSheet = function (sheet) {
  // Maybe we were shut down while waiting.
  if (!this._urls) {
    return;
  }

  let {href, nodeHref, sourceMapURL, actorID: id} = sheet;
  let url = href || nodeHref;
  this._urls.set(url, { id, url, sourceMapURL});
  this._idMap.set(id, url);
};

/**
 * A callback that is called from the lower-level source map service
 * proxy (see toolbox.js) when some tool has installed a new source
 * map.  This happens when pretty-printing a source.
 *
 * @param {String} id
 *        The actor ID (used as a cookie here as elsewhere in this file)
 * @param {String} newUrl
 *        The URL of the pretty-printed source
 */
SourceMapURLService.prototype.sourceMapChanged = function (id, newUrl) {
  if (!this._urls) {
    return;
  }

  let urlKey = this._idMap.get(id);
  if (urlKey) {
    // The source map URL here doesn't actually matter.
    this._urls.set(urlKey, { id, url: newUrl, sourceMapURL: "" });

    // Walk over all the location subscribers, looking for any that
    // are subscribed to a location coming from |urlKey|.  Then,
    // re-notify any such subscriber by clearing the stored promise
    // and forcing a re-evaluation.
    for (let [, subscriptionEntry] of this._subscriptions) {
      if (subscriptionEntry.url === urlKey) {
        // Force an update.
        subscriptionEntry.promise = null;
        for (let callback of subscriptionEntry.callbacks) {
          this._callOneCallback(subscriptionEntry, callback);
        }
      }
    }
  }
};

/**
 * Look up the original position for a given location.  This returns a
 * promise resolving to either the original location, or null if the
 * given location is not source-mapped.  If a location is returned, it
 * is of the same form as devtools-source-map's |getOriginalLocation|.
 *
 * @param {String} url
 *        The URL to map.
 * @param {number} line
 *        The line number to map.
 * @param {number} column
 *        The column number to map.
 * @return Promise
 *        A promise resolving either to the original location, or null.
 */
SourceMapURLService.prototype.originalPositionFor = async function (url, line, column) {
  // Ensure the sources are loaded before replying.
  await this._getLoadingPromise();

  // Maybe we were shut down while waiting.
  if (!this._urls) {
    return null;
  }

  const urlInfo = this._urls.get(url);
  if (!urlInfo) {
    return null;
  }
  // Call getOriginalURLs to make sure the source map has been
  // fetched.  We don't actually need the result of this though.
  await this._sourceMapService.getOriginalURLs(urlInfo);
  const location = { sourceId: urlInfo.id, line, column, sourceUrl: url };
  let resolvedLocation = await this._sourceMapService.getOriginalLocation(location);
  if (!resolvedLocation ||
      (resolvedLocation.line === location.line &&
       resolvedLocation.column === location.column &&
       resolvedLocation.sourceUrl === location.sourceUrl)) {
    return null;
  }
  return resolvedLocation;
};

/**
 * Helper function to call a single callback for a given subscription
 * entry.
 * @param {Object} subscriptionEntry
 *                 An entry in the _subscriptions map.
 * @param {Function} callback
 *                 The callback to call; @see subscribe
 */
SourceMapURLService.prototype._callOneCallback = async function (subscriptionEntry,
                                                                 callback) {
  // If source maps are disabled, immediately call with just "false".
  if (!this._prefValue) {
    callback(false);
    return;
  }

  if (!subscriptionEntry.promise) {
    const {url, line, column} = subscriptionEntry;
    subscriptionEntry.promise = this.originalPositionFor(url, line, column);
  }

  let resolvedLocation = await subscriptionEntry.promise;
  if (resolvedLocation) {
    const {line, column, sourceUrl} = resolvedLocation;
    // In case we're racing a pref change, pass the current value
    // here, not plain "true".
    callback(this._prefValue, sourceUrl, line, column);
  }
};

/**
 * Subscribe to changes to a given location.  This will arrange to
 * call a callback when an original location is determined (if source
 * maps are enabled), or when the source map pref changes.
 *
 * @param {String} url
 *                 The URL of the generated location.
 * @param {Number} line
 *                 The line number of the generated location.
 * @param {Number} column
 *                 The column number of the generated location (can be undefined).
 * @param {Function} callback
 *                 The callback to call.  This may be called zero or
 *                 more times -- it may not be called if the location
 *                 is not source mapped; and it may be called multiple
 *                 times if the source map pref changes.  It is called
 *                 as callback(enabled, url, line, column).  |enabled|
 *                 is a boolean.  If true then source maps are enabled
 *                 and the remaining arguments are the original
 *                 location.  If false, then source maps are disabled
 *                 and the generated location should be used; in this
 *                 case the remaining arguments should be ignored.
 */
SourceMapURLService.prototype.subscribe = function (url, line, column, callback) {
  if (!this._subscriptions) {
    return;
  }

  let key = JSON.stringify([url, line, column]);
  let subscriptionEntry = this._subscriptions.get(key);
  if (!subscriptionEntry) {
    subscriptionEntry = {
      url,
      line,
      column,
      promise: null,
      callbacks: [],
    };
    this._subscriptions.set(key, subscriptionEntry);
  }
  subscriptionEntry.callbacks.push(callback);

  // Only notify upon subscription if source maps are actually in use.
  if (this._prefValue) {
    this._callOneCallback(subscriptionEntry, callback);
  }
};

/**
 * Unsubscribe from changes to a given location.
 *
 * @param {String} url
 *                 The URL of the generated location.
 * @param {Number} line
 *                 The line number of the generated location.
 * @param {Number} column
 *                 The column number of the generated location (can be undefined).
 * @param {Function} callback
 *                 The callback.
 */
SourceMapURLService.prototype.unsubscribe = function (url, line, column, callback) {
  if (!this._subscriptions) {
    return;
  }
  let key = JSON.stringify([url, line, column]);
  let subscriptionEntry = this._subscriptions.get(key);
  if (subscriptionEntry) {
    let index = subscriptionEntry.callbacks.indexOf(callback);
    if (index !== -1) {
      subscriptionEntry.callbacks.splice(index, 1);
      // Remove the whole entry when the last subscriber is removed.
      if (subscriptionEntry.callbacks.length === 0) {
        this._subscriptions.delete(key);
      }
    }
  }
};

/**
 * A helper function that is called when the source map pref changes.
 * This function notifies all subscribers of the state change.
 */
SourceMapURLService.prototype._onPrefChanged = function () {
  if (!this._subscriptions) {
    return;
  }

  this._prefValue = Services.prefs.getBoolPref(SOURCE_MAP_PREF);
  for (let [, subscriptionEntry] of this._subscriptions) {
    for (let callback of subscriptionEntry.callbacks) {
      this._callOneCallback(subscriptionEntry, callback);
    }
  }
};

exports.SourceMapURLService = SourceMapURLService;
PK
!<� qDchrome/devtools/modules/devtools/client/framework/target-from-url.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { TargetFactory } = require("devtools/client/framework/target");
const { DebuggerServer } = require("devtools/server/main");
const { DebuggerClient } = require("devtools/shared/client/debugger-client");

/**
 * Construct a Target for a given URL object having various query parameters:
 *
 * - host, port & ws: See the documentation for clientFromURL
 *
 * - type: tab, process, window
 *      {String} The type of target to connect to.
 *
 * If type == "tab":
 * - id:
 *      {Number} the tab outerWindowID
 * - chrome: Optional
 *      {Boolean} Force the creation of a chrome target. Gives more privileges to
 *      the tab actor. Allows chrome execution in the webconsole and see chrome
 *      files in the debugger. (handy when contributing to firefox)
 *
 * If type == "process":
 * - id:
 *      {Number} the process id to debug. Default to 0, which is the parent process.
 *
 * If type == "window":
 * - id:
 *      {Number} the window outerWindowID
 *
 * @param {URL} url
 *        The url to fetch query params from.
 *
 * @return A target object
 */
exports.targetFromURL = async function targetFromURL(url) {
  let client = await clientFromURL(url);
  await client.connect();

  let params = url.searchParams;
  let type = params.get("type");
  if (!type) {
    throw new Error("targetFromURL, missing type parameter");
  }
  let id = params.get("id");
  // Allows to spawn a chrome enabled target for any context
  // (handy to debug chrome stuff in a child process)
  let chrome = params.has("chrome");

  let form, isTabActor;
  if (type === "tab") {
    // Fetch target for a remote tab
    id = parseInt(id, 10);
    if (isNaN(id)) {
      throw new Error(`targetFromURL, wrong tab id '${id}', should be a number`);
    }
    try {
      let response = await client.getTab({ outerWindowID: id });
      form = response.tab;
    } catch (ex) {
      if (ex.error == "noTab") {
        throw new Error(`targetFromURL, tab with outerWindowID '${id}' doesn't exist`);
      }
      throw ex;
    }
  } else if (type == "process") {
    // Fetch target for a remote chrome actor
    DebuggerServer.allowChromeProcess = true;
    try {
      id = parseInt(id, 10);
      if (isNaN(id)) {
        id = 0;
      }
      let response = await client.getProcess(id);
      form = response.form;
      chrome = true;
      if (id != 0) {
        // Child process are not exposing tab actors and only support debugger+console
        isTabActor = false;
      }
    } catch (ex) {
      if (ex.error == "noProcess") {
        throw new Error(`targetFromURL, process with id '${id}' doesn't exist`);
      }
      throw ex;
    }
  } else if (type == "window") {
    // Fetch target for a remote window actor
    DebuggerServer.allowChromeProcess = true;
    try {
      id = parseInt(id, 10);
      if (isNaN(id)) {
        throw new Error("targetFromURL, window requires id parameter");
      }
      let response = await client.mainRoot.getWindow({
        outerWindowID: id,
      });
      form = response.window;
      chrome = true;
    } catch (ex) {
      if (ex.error == "notFound") {
        throw new Error(`targetFromURL, window with id '${id}' doesn't exist`);
      }
      throw ex;
    }
  } else {
    throw new Error(`targetFromURL, unsupported type '${type}' parameter`);
  }

  return TargetFactory.forRemoteTab({ client, form, chrome, isTabActor });
};

/**
 * Create a DebuggerClient for a given URL object having various query parameters:
 *
 * host:
 *    {String} The hostname or IP address to connect to.
 * port:
 *    {Number} The TCP port to connect to, to use with `host` argument.
 * ws:
 *    {Boolean} If true, connect via websocket instead of regular TCP connection.
 *
 * @param {URL} url
 *        The url to fetch query params from.
 * @return a promise that resolves a DebuggerClient object
 */
async function clientFromURL(url) {
  let params = url.searchParams;
  let host = params.get("host");
  let port = params.get("port");
  let webSocket = !!params.get("ws");

  let transport;
  if (port) {
    transport = await DebuggerClient.socketConnect({ host, port, webSocket });
  } else {
    // Setup a server if we don't have one already running
    DebuggerServer.init();
    DebuggerServer.registerAllActors();
    transport = DebuggerServer.connectPipe();
  }
  return new DebuggerClient(transport);
}

exports.clientFromURL = clientFromURL;
PK
!<��	l	l;chrome/devtools/modules/devtools/client/framework/target.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Ci } = require("chrome");
const defer = require("devtools/shared/defer");
const EventEmitter = require("devtools/shared/old-event-emitter");
const Services = require("Services");
const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");

loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true);
loader.lazyRequireGetter(this, "DebuggerClient",
  "devtools/shared/client/debugger-client", true);
loader.lazyRequireGetter(this, "gDevTools",
  "devtools/client/framework/devtools", true);

const targets = new WeakMap();
const promiseTargets = new WeakMap();

/**
 * Functions for creating Targets
 */
const TargetFactory = exports.TargetFactory = {
  /**
   * Construct a Target
   * @param {XULTab} tab
   *        The tab to use in creating a new target.
   *
   * @return A target object
   */
  forTab: function (tab) {
    let target = targets.get(tab);
    if (target == null) {
      target = new TabTarget(tab);
      targets.set(tab, target);
    }
    return target;
  },

  /**
   * Return a promise of a Target for a remote tab.
   * @param {Object} options
   *        The options object has the following properties:
   *        {
   *          form: the remote protocol form of a tab,
   *          client: a DebuggerClient instance
   *                  (caller owns this and is responsible for closing),
   *          chrome: true if the remote target is the whole process
   *        }
   *
   * @return A promise of a target object
   */
  forRemoteTab: function (options) {
    let targetPromise = promiseTargets.get(options);
    if (targetPromise == null) {
      let target = new TabTarget(options);
      targetPromise = target.makeRemote().then(() => target);
      promiseTargets.set(options, targetPromise);
    }
    return targetPromise;
  },

  forWorker: function (workerClient) {
    let target = targets.get(workerClient);
    if (target == null) {
      target = new WorkerTarget(workerClient);
      targets.set(workerClient, target);
    }
    return target;
  },

  /**
   * Creating a target for a tab that is being closed is a problem because it
   * allows a leak as a result of coming after the close event which normally
   * clears things up. This function allows us to ask if there is a known
   * target for a tab without creating a target
   * @return true/false
   */
  isKnownTab: function (tab) {
    return targets.has(tab);
  },
};

/**
 * A Target represents something that we can debug. Targets are generally
 * read-only. Any changes that you wish to make to a target should be done via
 * a Tool that attaches to the target. i.e. a Target is just a pointer saying
 * "the thing to debug is over there".
 *
 * Providing a generalized abstraction of a web-page or web-browser (available
 * either locally or remotely) is beyond the scope of this class (and maybe
 * also beyond the scope of this universe) However Target does attempt to
 * abstract some common events and read-only properties common to many Tools.
 *
 * Supported read-only properties:
 * - name, isRemote, url
 *
 * Target extends EventEmitter and provides support for the following events:
 * - close: The target window has been closed. All tools attached to this
 *          target should close. This event is not currently cancelable.
 * - navigate: The target window has navigated to a different URL
 *
 * Optional events:
 * - will-navigate: The target window will navigate to a different URL
 * - hidden: The target is not visible anymore (for TargetTab, another tab is
 *           selected)
 * - visible: The target is visible (for TargetTab, tab is selected)
 *
 * Comparing Targets: 2 instances of a Target object can point at the same
 * thing, so t1 !== t2 and t1 != t2 even when they represent the same object.
 * To compare to targets use 't1.equals(t2)'.
 */

/**
 * A TabTarget represents a page living in a browser tab. Generally these will
 * be web pages served over http(s), but they don't have to be.
 */
function TabTarget(tab) {
  EventEmitter.decorate(this);
  this.destroy = this.destroy.bind(this);
  this.activeTab = this.activeConsole = null;
  // Only real tabs need initialization here. Placeholder objects for remote
  // targets will be initialized after a makeRemote method call.
  if (tab && !["client", "form", "chrome"].every(tab.hasOwnProperty, tab)) {
    this._tab = tab;
    this._setupListeners();
  } else {
    this._form = tab.form;
    this._url = this._form.url;
    this._title = this._form.title;

    this._client = tab.client;
    this._chrome = tab.chrome;
  }
  // Default isTabActor to true if not explicitly specified
  if (typeof tab.isTabActor == "boolean") {
    this._isTabActor = tab.isTabActor;
  } else {
    this._isTabActor = true;
  }
}

exports.TabTarget = TabTarget;

TabTarget.prototype = {
  _webProgressListener: null,

  /**
   * Returns a promise for the protocol description from the root actor. Used
   * internally with `target.actorHasMethod`. Takes advantage of caching if
   * definition was fetched previously with the corresponding actor information.
   * Actors are lazily loaded, so not only must the tool using a specific actor
   * be in use, the actors are only registered after invoking a method (for
   * performance reasons, added in bug 988237), so to use these actor detection
   * methods, one must already be communicating with a specific actor of that
   * type.
   *
   * Must be a remote target.
   *
   * @return {Promise}
   * {
   *   "category": "actor",
   *   "typeName": "longstractor",
   *   "methods": [{
   *     "name": "substring",
   *     "request": {
   *       "type": "substring",
   *       "start": {
   *         "_arg": 0,
   *         "type": "primitive"
   *       },
   *       "end": {
   *         "_arg": 1,
   *         "type": "primitive"
   *       }
   *     },
   *     "response": {
   *       "substring": {
   *         "_retval": "primitive"
   *       }
   *     }
   *   }],
   *  "events": {}
   * }
   */
  getActorDescription: function (actorName) {
    if (!this.client) {
      throw new Error("TabTarget#getActorDescription() can only be called on " +
                      "remote tabs.");
    }

    let deferred = defer();

    if (this._protocolDescription &&
        this._protocolDescription.types[actorName]) {
      deferred.resolve(this._protocolDescription.types[actorName]);
    } else {
      this.client.mainRoot.protocolDescription(description => {
        this._protocolDescription = description;
        deferred.resolve(description.types[actorName]);
      });
    }

    return deferred.promise;
  },

  /**
   * Returns a boolean indicating whether or not the specific actor
   * type exists. Must be a remote target.
   *
   * @param {String} actorName
   * @return {Boolean}
   */
  hasActor: function (actorName) {
    if (!this.client) {
      throw new Error("TabTarget#hasActor() can only be called on remote " +
                      "tabs.");
    }
    if (this.form) {
      return !!this.form[actorName + "Actor"];
    }
    return false;
  },

  /**
   * Queries the protocol description to see if an actor has
   * an available method. The actor must already be lazily-loaded (read
   * the restrictions in the `getActorDescription` comments),
   * so this is for use inside of tool. Returns a promise that
   * resolves to a boolean. Must be a remote target.
   *
   * @param {String} actorName
   * @param {String} methodName
   * @return {Promise}
   */
  actorHasMethod: function (actorName, methodName) {
    if (!this.client) {
      throw new Error("TabTarget#actorHasMethod() can only be called on " +
                      "remote tabs.");
    }
    return this.getActorDescription(actorName).then(desc => {
      if (desc && desc.methods) {
        return !!desc.methods.find(method => method.name === methodName);
      }
      return false;
    });
  },

  /**
   * Returns a trait from the root actor.
   *
   * @param {String} traitName
   * @return {Mixed}
   */
  getTrait: function (traitName) {
    if (!this.client) {
      throw new Error("TabTarget#getTrait() can only be called on remote " +
                      "tabs.");
    }

    // If the targeted actor exposes traits and has a defined value for this
    // traits, override the root actor traits
    if (this.form.traits && traitName in this.form.traits) {
      return this.form.traits[traitName];
    }

    return this.client.traits[traitName];
  },

  get tab() {
    return this._tab;
  },

  get form() {
    return this._form;
  },

  // Get a promise of the root form returned by a getRoot request. This promise
  // is cached.
  get root() {
    if (!this._root) {
      this._root = this._getRoot();
    }
    return this._root;
  },

  _getRoot: function () {
    return new Promise((resolve, reject) => {
      this.client.mainRoot.getRoot(response => {
        if (response.error) {
          reject(new Error(response.error + ": " + response.message));
          return;
        }

        resolve(response);
      });
    });
  },

  get client() {
    return this._client;
  },

  // Tells us if we are debugging content document
  // or if we are debugging chrome stuff.
  // Allows to controls which features are available against
  // a chrome or a content document.
  get chrome() {
    return this._chrome;
  },

  // Tells us if the related actor implements TabActor interface
  // and requires to call `attach` request before being used
  // and `detach` during cleanup
  get isTabActor() {
    return this._isTabActor;
  },

  get window() {
    // XXX - this is a footgun for e10s - there .contentWindow will be null,
    // and even though .contentWindowAsCPOW *might* work, it will not work
    // in all contexts.  Consumers of .window need to be refactored to not
    // rely on this.
    if (Services.appinfo.processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT) {
      console.error("The .window getter on devtools' |target| object isn't " +
                    "e10s friendly!\n" + Error().stack);
    }
    // Be extra careful here, since this may be called by HS_getHudByWindow
    // during shutdown.
    if (this._tab && this._tab.linkedBrowser) {
      return this._tab.linkedBrowser.contentWindow;
    }
    return null;
  },

  get name() {
    if (this.isAddon) {
      return this._form.name;
    }
    return this._title;
  },

  get url() {
    return this._url;
  },

  get isRemote() {
    return !this.isLocalTab;
  },

  get isAddon() {
    return !!(this._form && this._form.actor &&
              this._form.actor.match(/conn\d+\.addon\d+/)) || this.isWebExtension;
  },

  get isWebExtension() {
    return !!(this._form && this._form.actor && (
      this._form.actor.match(/conn\d+\.webExtension\d+/) ||
      this._form.actor.match(/child\d+\/webExtension\d+/)
    ));
  },

  get isLocalTab() {
    return !!this._tab;
  },

  get isMultiProcess() {
    return !this.window;
  },

  getExtensionPathName(url) {
    // Return the url if the target is not a webextension.
    if (!this.isWebExtension) {
      throw new Error("Target is not a WebExtension");
    }

    try {
      const parsedURL = new URL(url);
      // Only moz-extension URL should be shortened into the URL pathname.
      if (parsedURL.protocol !== "moz-extension:") {
        return url;
      }
      return parsedURL.pathname;
    } catch (e) {
      // Return the url if unable to resolve the pathname.
      return url;
    }
  },

  /**
   * Adds remote protocol capabilities to the target, so that it can be used
   * for tools that support the Remote Debugging Protocol even for local
   * connections.
   */
  makeRemote: async function () {
    if (this._remote) {
      return this._remote.promise;
    }

    this._remote = defer();

    if (this.isLocalTab) {
      // Since a remote protocol connection will be made, let's start the
      // DebuggerServer here, once and for all tools.
      DebuggerServer.init();

      // When connecting to a local tab, we only need the root actor.
      // Then we are going to call DebuggerServer.connectToChild and talk
      // directly with actors living in the child process.
      // We also need browser actors for actor registry which enabled addons
      // to register custom actors.
      // TODO: the comment and implementation are out of sync here. See Bug 1420134.
      DebuggerServer.registerAllActors();

      this._client = new DebuggerClient(DebuggerServer.connectPipe());
      // A local TabTarget will never perform chrome debugging.
      this._chrome = false;
    } else if (this._form.isWebExtension &&
          this.client.mainRoot.traits.webExtensionAddonConnect) {
      // The addonActor form is related to a WebExtensionParentActor instance,
      // which isn't a tab actor on its own, it is an actor living in the parent process
      // with access to the addon metadata, it can control the addon (e.g. reloading it)
      // and listen to the AddonManager events related to the lifecycle of the addon
      // (e.g. when the addon is disabled or uninstalled ).
      // To retrieve the TabActor instance, we call its "connect" method,
      // (which fetches the TabActor form from a WebExtensionChildActor instance).
      let {form} = await this._client.request({
        to: this._form.actor, type: "connect",
      });

      this._form = form;
      this._url = form.url;
      this._title = form.title;
    }

    this._setupRemoteListeners();

    let attachTab = () => {
      this._client.attachTab(this._form.actor, (response, tabClient) => {
        if (!tabClient) {
          this._remote.reject("Unable to attach to the tab");
          return;
        }
        this.activeTab = tabClient;
        this.threadActor = response.threadActor;

        attachConsole();
      });
    };

    let onConsoleAttached = (response, consoleClient) => {
      if (!consoleClient) {
        this._remote.reject("Unable to attach to the console");
        return;
      }
      this.activeConsole = consoleClient;

      this._onInspectObject = (event, packet) => this.emit("inspect-object", packet);
      this.activeConsole.on("inspectObject", this._onInspectObject);

      this._remote.resolve(null);
    };

    let attachConsole = () => {
      this._client.attachConsole(this._form.consoleActor, [], onConsoleAttached);
    };

    if (this.isLocalTab) {
      this._client.connect()
        .then(() => this._client.getTab({ tab: this.tab }))
        .then(response => {
          this._form = response.tab;
          this._url = this._form.url;
          this._title = this._form.title;

          attachTab();
        }, e => this._remote.reject(e));
    } else if (this.isTabActor) {
      // In the remote debugging case, the protocol connection will have been
      // already initialized in the connection screen code.
      attachTab();
    } else {
      // AddonActor and chrome debugging on RootActor doesn't inherits from
      // TabActor and doesn't need to be attached.
      attachConsole();
    }

    return this._remote.promise;
  },

  /**
   * Listen to the different events.
   */
  _setupListeners: function () {
    this._webProgressListener = new TabWebProgressListener(this);
    this.tab.linkedBrowser.addProgressListener(this._webProgressListener);
    this.tab.addEventListener("TabClose", this);
    this.tab.parentNode.addEventListener("TabSelect", this);
    this.tab.ownerDocument.defaultView.addEventListener("unload", this);
    this.tab.addEventListener("TabRemotenessChange", this);
  },

  /**
   * Teardown event listeners.
   */
  _teardownListeners: function () {
    if (this._webProgressListener) {
      this._webProgressListener.destroy();
    }

    this._tab.ownerDocument.defaultView.removeEventListener("unload", this);
    this._tab.removeEventListener("TabClose", this);
    this._tab.parentNode.removeEventListener("TabSelect", this);
    this._tab.removeEventListener("TabRemotenessChange", this);
  },

  /**
   * Setup listeners for remote debugging, updating existing ones as necessary.
   */
  _setupRemoteListeners: function () {
    this.client.addListener("closed", this.destroy);

    this._onTabDetached = (type, packet) => {
      // We have to filter message to ensure that this detach is for this tab
      if (packet.from == this._form.actor) {
        this.destroy();
      }
    };
    this.client.addListener("tabDetached", this._onTabDetached);

    this._onTabNavigated = (type, packet) => {
      let event = Object.create(null);
      event.url = packet.url;
      event.title = packet.title;
      event.nativeConsoleAPI = packet.nativeConsoleAPI;
      event.isFrameSwitching = packet.isFrameSwitching;

      // Keep the title unmodified when a developer toolbox switches frame
      // for a tab (Bug 1261687), but always update the title when the target
      // is a WebExtension (where the addon name is always included in the title
      // and the url is supposed to be updated every time the selected frame changes).
      if (!packet.isFrameSwitching || this.isWebExtension) {
        this._url = packet.url;
        this._title = packet.title;
      }

      // Send any stored event payload (DOMWindow or nsIRequest) for backwards
      // compatibility with non-remotable tools.
      if (packet.state == "start") {
        event._navPayload = this._navRequest;
        this.emit("will-navigate", event);
        this._navRequest = null;
      } else {
        event._navPayload = this._navWindow;
        this.emit("navigate", event);
        this._navWindow = null;
      }
    };
    this.client.addListener("tabNavigated", this._onTabNavigated);

    this._onFrameUpdate = (type, packet) => {
      this.emit("frame-update", packet);
    };
    this.client.addListener("frameUpdate", this._onFrameUpdate);

    this._onSourceUpdated = (event, packet) => this.emit("source-updated", packet);
    this.client.addListener("newSource", this._onSourceUpdated);
    this.client.addListener("updatedSource", this._onSourceUpdated);
  },

  /**
   * Teardown listeners for remote debugging.
   */
  _teardownRemoteListeners: function () {
    this.client.removeListener("closed", this.destroy);
    this.client.removeListener("tabNavigated", this._onTabNavigated);
    this.client.removeListener("tabDetached", this._onTabDetached);
    this.client.removeListener("frameUpdate", this._onFrameUpdate);
    this.client.removeListener("newSource", this._onSourceUpdated);
    this.client.removeListener("updatedSource", this._onSourceUpdated);
    if (this.activeConsole && this._onInspectObject) {
      this.activeConsole.off("inspectObject", this._onInspectObject);
    }
  },

  /**
   * Handle tabs events.
   */
  handleEvent: function (event) {
    switch (event.type) {
      case "TabClose":
      case "unload":
        this.destroy();
        break;
      case "TabSelect":
        if (this.tab.selected) {
          this.emit("visible", event);
        } else {
          this.emit("hidden", event);
        }
        break;
      case "TabRemotenessChange":
        this.onRemotenessChange();
        break;
    }
  },

  /**
   * Automatically respawn the toolbox when the tab changes between being
   * loaded within the parent process and loaded from a content process.
   * Process change can go in both ways.
   */
  onRemotenessChange: function () {
    // Responsive design do a crazy dance around tabs and triggers
    // remotenesschange events. But we should ignore them as at the end
    // the content doesn't change its remoteness.
    if (this._tab.isResponsiveDesignMode) {
      return;
    }

    // Save a reference to the tab as it will be nullified on destroy
    let tab = this._tab;
    let onToolboxDestroyed = (event, target) => {
      if (target != this) {
        return;
      }
      gDevTools.off("toolbox-destroyed", target);

      // Recreate a fresh target instance as the current one is now destroyed
      let newTarget = TargetFactory.forTab(tab);
      gDevTools.showToolbox(newTarget);
    };
    gDevTools.on("toolbox-destroyed", onToolboxDestroyed);
  },

  /**
   * Target is not alive anymore.
   */
  destroy: function () {
    // If several things call destroy then we give them all the same
    // destruction promise so we're sure to destroy only once
    if (this._destroyer) {
      return this._destroyer.promise;
    }

    this._destroyer = defer();

    // Before taking any action, notify listeners that destruction is imminent.
    this.emit("close");

    if (this._tab) {
      this._teardownListeners();
    }

    let cleanupAndResolve = () => {
      this._cleanup();
      this._destroyer.resolve(null);
    };
    // If this target was not remoted, the promise will be resolved before the
    // function returns.
    if (this._tab && !this._client) {
      cleanupAndResolve();
    } else if (this._client) {
      // If, on the other hand, this target was remoted, the promise will be
      // resolved after the remote connection is closed.
      this._teardownRemoteListeners();

      if (this.isLocalTab) {
        // We started with a local tab and created the client ourselves, so we
        // should close it.
        this._client.close().then(cleanupAndResolve);
      } else if (this.activeTab) {
        // The client was handed to us, so we are not responsible for closing
        // it. We just need to detach from the tab, if already attached.
        // |detach| may fail if the connection is already dead, so proceed with
        // cleanup directly after this.
        this.activeTab.detach();
        cleanupAndResolve();
      } else {
        cleanupAndResolve();
      }
    }

    return this._destroyer.promise;
  },

  /**
   * Clean up references to what this target points to.
   */
  _cleanup: function () {
    if (this._tab) {
      targets.delete(this._tab);
    } else {
      promiseTargets.delete(this._form);
    }

    this.activeTab = null;
    this.activeConsole = null;
    this._client = null;
    this._tab = null;
    this._form = null;
    this._remote = null;
    this._root = null;
    this._title = null;
    this._url = null;
    this.threadActor = null;
  },

  toString: function () {
    let id = this._tab ? this._tab : (this._form && this._form.actor);
    return `TabTarget:${id}`;
  },

  /**
   * Log an error of some kind to the tab's console.
   *
   * @param {String} text
   *                 The text to log.
   * @param {String} category
   *                 The category of the message.  @see nsIScriptError.
   */
  logErrorInPage: function (text, category) {
    if (this.activeTab && this.activeTab.traits.logInPage) {
      const errorFlag = 0;
      let packet = {
        to: this.form.actor,
        type: "logInPage",
        flags: errorFlag,
        text,
        category,
      };
      this.client.request(packet);
    }
  },

  /**
   * Log a warning of some kind to the tab's console.
   *
   * @param {String} text
   *                 The text to log.
   * @param {String} category
   *                 The category of the message.  @see nsIScriptError.
   */
  logWarningInPage: function (text, category) {
    if (this.activeTab && this.activeTab.traits.logInPage) {
      const warningFlag = 1;
      let packet = {
        to: this.form.actor,
        type: "logInPage",
        flags: warningFlag,
        text,
        category,
      };
      this.client.request(packet);
    }
  },
};

/**
 * WebProgressListener for TabTarget.
 *
 * @param object target
 *        The TabTarget instance to work with.
 */
function TabWebProgressListener(target) {
  this.target = target;
}

TabWebProgressListener.prototype = {
  target: null,

  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
                                         Ci.nsISupportsWeakReference]),

  onStateChange: function (progress, request, flag) {
    let isStart = flag & Ci.nsIWebProgressListener.STATE_START;
    let isDocument = flag & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT;
    let isNetwork = flag & Ci.nsIWebProgressListener.STATE_IS_NETWORK;
    let isRequest = flag & Ci.nsIWebProgressListener.STATE_IS_REQUEST;

    // Skip non-interesting states.
    if (!isStart || !isDocument || !isRequest || !isNetwork) {
      return;
    }

    // emit event if the top frame is navigating
    if (progress.isTopLevel) {
      // Emit the event if the target is not remoted or store the payload for
      // later emission otherwise.
      if (this.target._client) {
        this.target._navRequest = request;
      } else {
        this.target.emit("will-navigate", request);
      }
    }
  },

  onProgressChange: function () {},
  onSecurityChange: function () {},
  onStatusChange: function () {},

  onLocationChange: function (webProgress, request, URI, flags) {
    if (this.target &&
        !(flags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT)) {
      let window = webProgress.DOMWindow;
      // Emit the event if the target is not remoted or store the payload for
      // later emission otherwise.
      if (this.target._client) {
        this.target._navWindow = window;
      } else {
        this.target.emit("navigate", window);
      }
    }
  },

  /**
   * Destroy the progress listener instance.
   */
  destroy: function () {
    if (this.target.tab) {
      try {
        this.target.tab.linkedBrowser.removeProgressListener(this);
      } catch (ex) {
        // This can throw when a tab crashes in e10s.
      }
    }
    this.target._webProgressListener = null;
    this.target._navRequest = null;
    this.target._navWindow = null;
    this.target = null;
  }
};

function WorkerTarget(workerClient) {
  EventEmitter.decorate(this);
  this._workerClient = workerClient;
}

/**
 * A WorkerTarget represents a worker. Unlike TabTarget, which can represent
 * either a local or remote tab, WorkerTarget always represents a remote worker.
 * Moreover, unlike TabTarget, which is constructed with a placeholder object
 * for remote tabs (from which a TabClient can then be lazily obtained),
 * WorkerTarget is constructed with a WorkerClient directly.
 *
 * WorkerClient is designed to mimic the interface of TabClient as closely as
 * possible. This allows us to debug workers as if they were ordinary tabs,
 * requiring only minimal changes to the rest of the frontend.
 */
WorkerTarget.prototype = {
  get isRemote() {
    return true;
  },

  get isTabActor() {
    return true;
  },

  get name() {
    return "Worker";
  },

  get url() {
    return this._workerClient.url;
  },

  get isWorkerTarget() {
    return true;
  },

  get form() {
    return {
      consoleActor: this._workerClient.consoleActor
    };
  },

  get activeTab() {
    return this._workerClient;
  },

  get activeConsole() {
    return this.client._clients.get(this.form.consoleActor);
  },

  get client() {
    return this._workerClient.client;
  },

  destroy: function () {
    this._workerClient.detach();
  },

  hasActor: function (name) {
    // console is the only one actor implemented by WorkerActor
    if (name == "console") {
      return true;
    }
    return false;
  },

  getTrait: function () {
    return undefined;
  },

  makeRemote: function () {
    return Promise.resolve();
  },

  logErrorInPage: function () {
    // No-op.  See bug 1368680.
  },

  logWarningInPage: function () {
    // No-op.  See bug 1368680.
  },
};
PK
!<��
oT,T,Nchrome/devtools/modules/devtools/client/framework/toolbox-highlighter-utils.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 promise = require("promise");
const {Task} = require("devtools/shared/task");
const flags = require("devtools/shared/flags");

/**
 * Client-side highlighter shared module.
 * To be used by toolbox panels that need to highlight DOM elements.
 *
 * Highlighting and selecting elements is common enough that it needs to be at
 * toolbox level, accessible by any panel that needs it.
 * That's why the toolbox is the one that initializes the inspector and
 * highlighter. It's also why the API returned by this module needs a reference
 * to the toolbox which should be set once only.
 */

/**
 * Get the highighterUtils instance for a given toolbox.
 * This should be done once only by the toolbox itself and stored there so that
 * panels can get it from there. That's because the API returned has a stateful
 * scope that would be different for another instance returned by this function.
 *
 * @param {Toolbox} toolbox
 * @return {Object} the highlighterUtils public API
 */
exports.getHighlighterUtils = function (toolbox) {
  if (!toolbox || !toolbox.target) {
    throw new Error("Missing or invalid toolbox passed to getHighlighterUtils");
  }

  // Exported API properties will go here
  let exported = {};

  // The current toolbox target
  let target = toolbox.target;

  // Is the highlighter currently in pick mode
  let isPicking = false;

  // Is the box model already displayed, used to prevent dispatching
  // unnecessary requests, especially during toolbox shutdown
  let isNodeFrontHighlighted = false;

  /**
   * Release this utils, nullifying the references to the toolbox
   */
  exported.release = function () {
    toolbox = target = null;
  };

  /**
   * Does the target have the highlighter actor.
   * The devtools must be backwards compatible with at least B2G 1.3 (28),
   * which doesn't have the highlighter actor. This can be removed as soon as
   * the minimal supported version becomes 1.4 (29)
   */
  let isRemoteHighlightable = exported.isRemoteHighlightable = function () {
    return target.client.traits.highlightable;
  };

  /**
   * Does the target support custom highlighters.
   */
  let supportsCustomHighlighters = exported.supportsCustomHighlighters = () => {
    return !!target.client.traits.customHighlighters;
  };

  /**
   * Make a function that initializes the inspector before it runs.
   * Since the init of the inspector is asynchronous, the return value will be
   * produced by Task.async and the argument should be a generator
   * @param {Function*} generator A generator function
   * @return {Function} A function
   */
  let isInspectorInitialized = false;
  let requireInspector = generator => {
    return Task.async(function* (...args) {
      if (!isInspectorInitialized) {
        yield toolbox.initInspector();
        isInspectorInitialized = true;
      }
      return yield generator.apply(null, args);
    });
  };

  /**
   * Start/stop the element picker on the debuggee target.
   * @param {Boolean} doFocus - Optionally focus the content area once the picker is
   *                            activated.
   * @return A promise that resolves when done
   */
  exported.togglePicker = function (doFocus) {
    if (isPicking) {
      return cancelPicker();
    }
    return startPicker(doFocus);
  };

  /**
   * Start the element picker on the debuggee target.
   * This will request the inspector actor to start listening for mouse events
   * on the target page to highlight the hovered/picked element.
   * Depending on the server-side capabilities, this may fire events when nodes
   * are hovered.
   * @param {Boolean} doFocus - Optionally focus the content area once the picker is
   *                            activated.
   * @return A promise that resolves when the picker has started or immediately
   * if it is already started
   */
  let startPicker = exported.startPicker = requireInspector(function* (doFocus = false) {
    if (isPicking) {
      return;
    }
    isPicking = true;

    toolbox.pickerButton.isChecked = true;
    yield toolbox.selectTool("inspector");
    toolbox.on("select", cancelPicker);

    if (isRemoteHighlightable()) {
      toolbox.walker.on("picker-node-hovered", onPickerNodeHovered);
      toolbox.walker.on("picker-node-picked", onPickerNodePicked);
      toolbox.walker.on("picker-node-previewed", onPickerNodePreviewed);
      toolbox.walker.on("picker-node-canceled", onPickerNodeCanceled);

      yield toolbox.highlighter.pick(doFocus);
      toolbox.emit("picker-started");
    } else {
      // If the target doesn't have the highlighter actor, we can use the
      // walker's pick method instead, knowing that it only responds when a node
      // is picked (instead of emitting events)
      toolbox.emit("picker-started");
      let node = yield toolbox.walker.pick();
      onPickerNodePicked({node: node});
    }
  });

  /**
   * Stop the element picker. Note that the picker is automatically stopped when
   * an element is picked
   * @return A promise that resolves when the picker has stopped or immediately
   * if it is already stopped
   */
  let stopPicker = exported.stopPicker = requireInspector(function* () {
    if (!isPicking) {
      return;
    }
    isPicking = false;

    toolbox.pickerButton.isChecked = false;

    if (isRemoteHighlightable()) {
      yield toolbox.highlighter.cancelPick();
      toolbox.walker.off("picker-node-hovered", onPickerNodeHovered);
      toolbox.walker.off("picker-node-picked", onPickerNodePicked);
      toolbox.walker.off("picker-node-previewed", onPickerNodePreviewed);
      toolbox.walker.off("picker-node-canceled", onPickerNodeCanceled);
    } else {
      // If the target doesn't have the highlighter actor, use the walker's
      // cancelPick method instead
      yield toolbox.walker.cancelPick();
    }

    toolbox.off("select", cancelPicker);
    toolbox.emit("picker-stopped");
  });

  /**
   * Stop the picker, but also emit an event that the picker was canceled.
   */
  let cancelPicker = exported.cancelPicker = Task.async(function* () {
    yield stopPicker();
    toolbox.emit("picker-canceled");
  });

  /**
   * When a node is hovered by the mouse when the highlighter is in picker mode
   * @param {Object} data Information about the node being hovered
   */
  function onPickerNodeHovered(data) {
    toolbox.emit("picker-node-hovered", data.node);
  }

  /**
   * When a node has been picked while the highlighter is in picker mode
   * @param {Object} data Information about the picked node
   */
  function onPickerNodePicked(data) {
    toolbox.selection.setNodeFront(data.node, "picker-node-picked");
    stopPicker();
  }

  /**
   * When a node has been shift-clicked (previewed) while the highlighter is in
   * picker mode
   * @param {Object} data Information about the picked node
   */
  function onPickerNodePreviewed(data) {
    toolbox.selection.setNodeFront(data.node, "picker-node-previewed");
  }

  /**
   * When the picker is canceled, stop the picker, and make sure the toolbox
   * gets the focus.
   */
  function onPickerNodeCanceled() {
    cancelPicker();
    toolbox.win.focus();
  }

  /**
   * Show the box model highlighter on a node in the content page.
   * The node needs to be a NodeFront, as defined by the inspector actor
   * @see devtools/server/actors/inspector/inspector.js
   * @param {NodeFront} nodeFront The node to highlight
   * @param {Object} options
   * @return A promise that resolves when the node has been highlighted
   */
  let highlightNodeFront = exported.highlightNodeFront = requireInspector(
  function* (nodeFront, options = {}) {
    if (!nodeFront) {
      return;
    }

    isNodeFrontHighlighted = true;
    if (isRemoteHighlightable()) {
      yield toolbox.highlighter.showBoxModel(nodeFront, options);
    } else {
      // If the target doesn't have the highlighter actor, revert to the
      // walker's highlight method, which draws a simple outline
      yield toolbox.walker.highlight(nodeFront);
    }

    toolbox.emit("node-highlight", nodeFront);
  });

  /**
   * This is a convenience method in case you don't have a nodeFront but a
   * valueGrip. This is often the case with VariablesView properties.
   * This method will simply translate the grip into a nodeFront and call
   * highlightNodeFront, so it has the same signature.
   * @see highlightNodeFront
   */
  exported.highlightDomValueGrip = requireInspector(function* (valueGrip, options = {}) {
    let nodeFront = yield gripToNodeFront(valueGrip);
    if (nodeFront) {
      yield highlightNodeFront(nodeFront, options);
    } else {
      throw new Error("The ValueGrip passed could not be translated to a NodeFront");
    }
  });

  /**
   * Translate a debugger value grip into a node front usable by the inspector
   * @param {ValueGrip}
   * @return a promise that resolves to the node front when done
   */
  let gripToNodeFront = exported.gripToNodeFront = requireInspector(
  function* (grip) {
    return yield toolbox.walker.getNodeActorFromObjectActor(grip.actor);
  });

  /**
   * Hide the highlighter.
   * @param {Boolean} forceHide Only really matters in test mode (when
   * flags.testing is true). In test mode, hovering over several nodes
   * in the markup view doesn't hide/show the highlighter to ease testing. The
   * highlighter stays visible at all times, except when the mouse leaves the
   * markup view, which is when this param is passed to true
   * @return a promise that resolves when the highlighter is hidden
   */
  exported.unhighlight = Task.async(function* (forceHide = false) {
    forceHide = forceHide || !flags.testing;

    // Note that if isRemoteHighlightable is true, there's no need to hide the
    // highlighter as the walker uses setTimeout to hide it after some time
    if (isNodeFrontHighlighted && forceHide && toolbox.highlighter &&
        isRemoteHighlightable()) {
      isNodeFrontHighlighted = false;
      yield toolbox.highlighter.hideBoxModel();
    }

    // unhighlight is called when destroying the toolbox, which means that by
    // now, the toolbox reference might have been nullified already.
    if (toolbox) {
      toolbox.emit("node-unhighlight");
    }
  });

  /**
   * If the main, box-model, highlighter isn't enough, or if multiple
   * highlighters are needed in parallel, this method can be used to return a
   * new instance of a highlighter actor, given a type.
   * The type of the highlighter passed must be known by the server.
   * The highlighter actor returned will have the show(nodeFront) and hide()
   * methods and needs to be released by the consumer when not needed anymore.
   * @return a promise that resolves to the highlighter
   */
  exported.getHighlighterByType = requireInspector(function* (typeName) {
    let highlighter = null;

    if (supportsCustomHighlighters()) {
      highlighter = yield toolbox.inspector.getHighlighterByType(typeName);
    }

    return highlighter || promise.reject("The target doesn't support " +
        `creating highlighters by types or ${typeName} is unknown`);
  });

  // Return the public API
  return exported;
};
PK
!<���y{ { Ichrome/devtools/modules/devtools/client/framework/toolbox-host-manager.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Services = require("Services");
const {LocalizationHelper} = require("devtools/shared/l10n");
const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const {Task} = require("devtools/shared/task");

loader.lazyRequireGetter(this, "Toolbox", "devtools/client/framework/toolbox", true);
loader.lazyRequireGetter(this, "Hosts", "devtools/client/framework/toolbox-hosts", true);

/**
 * Implement a wrapper on the chrome side to setup a Toolbox within Firefox UI.
 *
 * This component handles iframe creation within Firefox, in which we are loading
 * the toolbox document. Then both the chrome and the toolbox document communicate
 * via "message" events.
 *
 * Messages sent by the toolbox to the chrome:
 * - switch-host:
 *   Order to display the toolbox in another host (side, bottom, window, or the
 *   previously used one)
 * - toggle-minimize-mode:
 *   When using the bottom host, the toolbox can be miximized to only display
 *   the tool titles
 * - maximize-host:
 *   When using the bottom host in minimized mode, revert back to regular mode
 *   in order to see tool titles and the tools
 * - raise-host:
 *   Focus the tools
 * - set-host-title:
 *   When using the window host, update the window title
 *
 * Messages sent by the chrome to the toolbox:
 * - host-minimized:
 *   The bottom host is done minimizing (after animation end)
 * - host-maximized:
 *   The bottom host is done switching back to regular mode (after animation
 *   end)
 * - switched-host:
 *   The `switch-host` command sent by the toolbox is done
 */

const LAST_HOST = "devtools.toolbox.host";
const PREVIOUS_HOST = "devtools.toolbox.previousHost";
let ID_COUNTER = 1;

function ToolboxHostManager(target, hostType, hostOptions) {
  this.target = target;

  this.frameId = ID_COUNTER++;

  if (!hostType) {
    hostType = Services.prefs.getCharPref(LAST_HOST);
  }
  this.onHostMinimized = this.onHostMinimized.bind(this);
  this.onHostMaximized = this.onHostMaximized.bind(this);
  this.host = this.createHost(hostType, hostOptions);
  this.hostType = hostType;
}

ToolboxHostManager.prototype = {
  create: Task.async(function* (toolId) {
    yield this.host.create();

    this.host.frame.setAttribute("aria-label", L10N.getStr("toolbox.label"));
    this.host.frame.ownerDocument.defaultView.addEventListener("message", this);
    // We have to listen on capture as no event fires on bubble
    this.host.frame.addEventListener("unload", this, true);

    let toolbox = new Toolbox(this.target, toolId, this.host.type,
                              this.host.frame.contentWindow, this.frameId);

    // Prevent reloading the toolbox when loading the tools in a tab
    // (e.g. from about:debugging)
    let location = this.host.frame.contentWindow.location;
    if (!location.href.startsWith("about:devtools-toolbox")) {
      this.host.frame.setAttribute("src", "about:devtools-toolbox");
    }

    return toolbox;
  }),

  handleEvent(event) {
    switch (event.type) {
      case "message":
        this.onMessage(event);
        break;
      case "unload":
        // On unload, host iframe already lost its contentWindow attribute, so
        // we can only compare against locations. Here we filter two very
        // different cases: preliminary about:blank document as well as iframes
        // like tool iframes.
        if (!event.target.location.href.startsWith("about:devtools-toolbox")) {
          break;
        }
        // Don't destroy the host during unload event (esp., don't remove the
        // iframe from DOM!). Otherwise the unload event for the toolbox
        // document doesn't fire within the toolbox *document*! This is
        // the unload event that fires on the toolbox *iframe*.
        DevToolsUtils.executeSoon(() => {
          this.destroy();
        });
        break;
    }
  },

  onMessage(event) {
    if (!event.data) {
      return;
    }
    // Toolbox document is still chrome and disallow identifying message
    // origin via event.source as it is null. So use a custom id.
    if (event.data.frameId != this.frameId) {
      return;
    }
    switch (event.data.name) {
      case "switch-host":
        this.switchHost(event.data.hostType);
        break;
      case "maximize-host":
        this.host.maximize();
        break;
      case "raise-host":
        this.host.raise();
        break;
      case "toggle-minimize-mode":
        this.host.toggleMinimizeMode(event.data.toolbarHeight);
        break;
      case "set-host-title":
        this.host.setTitle(event.data.title);
        break;
    }
  },

  postMessage(data) {
    let window = this.host.frame.contentWindow;
    window.postMessage(data, "*");
  },

  destroy() {
    this.destroyHost();
    this.host = null;
    this.hostType = null;
    this.target = null;
  },

  /**
   * Create a host object based on the given host type.
   *
   * Warning: bottom and sidebar hosts require that the toolbox target provides
   * a reference to the attached tab. Not all Targets have a tab property -
   * make sure you correctly mix and match hosts and targets.
   *
   * @param {string} hostType
   *        The host type of the new host object
   *
   * @return {Host} host
   *        The created host object
   */
  createHost(hostType, options) {
    if (!Hosts[hostType]) {
      throw new Error("Unknown hostType: " + hostType);
    }

    let newHost = new Hosts[hostType](this.target.tab, options);
    // Update the label and icon when the state changes.
    newHost.on("minimized", this.onHostMinimized);
    newHost.on("maximized", this.onHostMaximized);
    return newHost;
  },

  onHostMinimized() {
    this.postMessage({
      name: "host-minimized"
    });
  },

  onHostMaximized() {
    this.postMessage({
      name: "host-maximized"
    });
  },

  switchHost: Task.async(function* (hostType) {
    if (hostType == "previous") {
      // Switch to the last used host for the toolbox UI.
      // This is determined by the devtools.toolbox.previousHost pref.
      hostType = Services.prefs.getCharPref(PREVIOUS_HOST);

      // Handle the case where the previous host happens to match the current
      // host. If so, switch to bottom if it's not already used, and side if not.
      if (hostType === this.hostType) {
        if (hostType === Toolbox.HostType.BOTTOM) {
          hostType = Toolbox.HostType.SIDE;
        } else {
          hostType = Toolbox.HostType.BOTTOM;
        }
      }
    }
    let iframe = this.host.frame;
    let newHost = this.createHost(hostType);
    let newIframe = yield newHost.create();
    // change toolbox document's parent to the new host
    newIframe.swapFrameLoaders(iframe);

    this.destroyHost();

    if (this.hostType != Toolbox.HostType.CUSTOM) {
      Services.prefs.setCharPref(PREVIOUS_HOST, this.hostType);
    }

    this.host = newHost;
    this.hostType = hostType;
    this.host.setTitle(this.host.frame.contentWindow.document.title);
    this.host.frame.ownerDocument.defaultView.addEventListener("message", this);
    this.host.frame.addEventListener("unload", this, true);

    if (hostType != Toolbox.HostType.CUSTOM) {
      Services.prefs.setCharPref(LAST_HOST, hostType);
    }

    // Tell the toolbox the host changed
    this.postMessage({
      name: "switched-host",
      hostType
    });
  }),

  /**
   * Destroy the current host, and remove event listeners from its frame.
   *
   * @return {promise} to be resolved when the host is destroyed.
   */
  destroyHost() {
    // When Firefox toplevel is closed, the frame may already be detached and
    // the top level document gone
    if (this.host.frame.ownerDocument.defaultView) {
      this.host.frame.ownerDocument.defaultView.removeEventListener("message", this);
    }
    this.host.frame.removeEventListener("unload", this, true);

    this.host.off("minimized", this.onHostMinimized);
    this.host.off("maximized", this.onHostMaximized);
    return this.host.destroy();
  }
};
exports.ToolboxHostManager = ToolboxHostManager;
PK
!<Zi��*�*Bchrome/devtools/modules/devtools/client/framework/toolbox-hosts.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 EventEmitter = require("devtools/shared/old-event-emitter");
const promise = require("promise");
const defer = require("devtools/shared/defer");
const Services = require("Services");
const {DOMHelpers} = require("resource://devtools/client/shared/DOMHelpers.jsm");

loader.lazyRequireGetter(this, "system", "devtools/shared/system");
loader.lazyRequireGetter(this, "gDevToolsBrowser", "devtools/client/framework/devtools-browser", true);

/* A host should always allow this much space for the page to be displayed.
 * There is also a min-height on the browser, but we still don't want to set
 * frame.height to be larger than that, since it can cause problems with
 * resizing the toolbox and panel layout. */
const MIN_PAGE_SIZE = 25;

/**
 * A toolbox host represents an object that contains a toolbox (e.g. the
 * sidebar or a separate window). Any host object should implement the
 * following functions:
 *
 * create() - create the UI and emit a 'ready' event when the UI is ready to use
 * destroy() - destroy the host's UI
 */

exports.Hosts = {
  "bottom": BottomHost,
  "side": SidebarHost,
  "window": WindowHost,
  "custom": CustomHost
};

/**
 * Host object for the dock on the bottom of the browser
 */
function BottomHost(hostTab) {
  this.hostTab = hostTab;

  EventEmitter.decorate(this);
}

BottomHost.prototype = {
  type: "bottom",

  heightPref: "devtools.toolbox.footer.height",

  /**
   * Create a box at the bottom of the host tab.
   */
  create: async function () {
    await gDevToolsBrowser.loadBrowserStyleSheet(this.hostTab.ownerGlobal);

    let gBrowser = this.hostTab.ownerDocument.defaultView.gBrowser;
    let ownerDocument = gBrowser.ownerDocument;
    this._nbox = gBrowser.getNotificationBox(this.hostTab.linkedBrowser);

    this._splitter = ownerDocument.createElement("splitter");
    this._splitter.setAttribute("class", "devtools-horizontal-splitter");
    // Avoid resizing notification containers
    this._splitter.setAttribute("resizebefore", "flex");

    this.frame = ownerDocument.createElement("iframe");
    this.frame.className = "devtools-toolbox-bottom-iframe";
    this.frame.height = Math.min(
      Services.prefs.getIntPref(this.heightPref),
      this._nbox.clientHeight - MIN_PAGE_SIZE
    );

    this._nbox.appendChild(this._splitter);
    this._nbox.appendChild(this.frame);

    this.frame.tooltip = "aHTMLTooltip";

    // we have to load something so we can switch documents if we have to
    this.frame.setAttribute("src", "about:blank");

    let frame = await new Promise(resolve => {
      let domHelper = new DOMHelpers(this.frame.contentWindow);
      let frameLoad = () => {
        this.emit("ready", this.frame);
        resolve(this.frame);
      };
      domHelper.onceDOMReady(frameLoad);
      focusTab(this.hostTab);
    });

    return frame;
  },

  /**
   * Raise the host.
   */
  raise: function () {
    focusTab(this.hostTab);
  },

  /**
   * Minimize this host so that only the toolbox tabbar remains visible.
   * @param {Number} height The height to minimize to. Defaults to 0, which
   * means that the toolbox won't be visible at all once minimized.
   */
  minimize: function (height = 0) {
    if (this.isMinimized) {
      return;
    }
    this.isMinimized = true;

    let onTransitionEnd = event => {
      if (event.propertyName !== "margin-bottom") {
        // Ignore transitionend on unrelated properties.
        return;
      }

      this.frame.removeEventListener("transitionend", onTransitionEnd);
      this.emit("minimized");
    };
    this.frame.addEventListener("transitionend", onTransitionEnd);
    this.frame.style.marginBottom = -this.frame.height + height + "px";
    this._splitter.classList.add("disabled");
  },

  /**
   * If the host was minimized before, maximize it again (the host will be
   * maximized to the height it previously had).
   */
  maximize: function () {
    if (!this.isMinimized) {
      return;
    }
    this.isMinimized = false;

    let onTransitionEnd = event => {
      if (event.propertyName !== "margin-bottom") {
        // Ignore transitionend on unrelated properties.
        return;
      }

      this.frame.removeEventListener("transitionend", onTransitionEnd);
      this.emit("maximized");
    };
    this.frame.addEventListener("transitionend", onTransitionEnd);
    this.frame.style.marginBottom = "0";
    this._splitter.classList.remove("disabled");
  },

  /**
   * Toggle the minimize mode.
   * @param {Number} minHeight The height to minimize to.
   */
  toggleMinimizeMode: function (minHeight) {
    this.isMinimized ? this.maximize() : this.minimize(minHeight);
  },

  /**
   * Set the toolbox title.
   * Nothing to do for this host type.
   */
  setTitle: function () {},

  /**
   * Destroy the bottom dock.
   */
  destroy: function () {
    if (!this._destroyed) {
      this._destroyed = true;

      Services.prefs.setIntPref(this.heightPref, this.frame.height);
      this._nbox.removeChild(this._splitter);
      this._nbox.removeChild(this.frame);
      this.frame = null;
      this._nbox = null;
      this._splitter = null;
    }

    return promise.resolve(null);
  }
};

/**
 * Host object for the in-browser sidebar
 */
function SidebarHost(hostTab) {
  this.hostTab = hostTab;

  EventEmitter.decorate(this);
}

SidebarHost.prototype = {
  type: "side",

  widthPref: "devtools.toolbox.sidebar.width",

  /**
   * Create a box in the sidebar of the host tab.
   */
  create: async function () {
    await gDevToolsBrowser.loadBrowserStyleSheet(this.hostTab.ownerGlobal);

    let gBrowser = this.hostTab.ownerDocument.defaultView.gBrowser;
    let ownerDocument = gBrowser.ownerDocument;
    this._sidebar = gBrowser.getSidebarContainer(this.hostTab.linkedBrowser);

    this._splitter = ownerDocument.createElement("splitter");
    this._splitter.setAttribute("class", "devtools-side-splitter");

    this.frame = ownerDocument.createElement("iframe");
    this.frame.className = "devtools-toolbox-side-iframe";

    this.frame.width = Math.min(
      Services.prefs.getIntPref(this.widthPref),
      this._sidebar.clientWidth - MIN_PAGE_SIZE
    );

    this._sidebar.appendChild(this._splitter);
    this._sidebar.appendChild(this.frame);

    this.frame.tooltip = "aHTMLTooltip";
    this.frame.setAttribute("src", "about:blank");

    let frame = await new Promise(resolve => {
      let domHelper = new DOMHelpers(this.frame.contentWindow);
      let frameLoad = () => {
        this.emit("ready", this.frame);
        resolve(this.frame);
      };
      domHelper.onceDOMReady(frameLoad);
      focusTab(this.hostTab);
    });

    return frame;
  },

  /**
   * Raise the host.
   */
  raise: function () {
    focusTab(this.hostTab);
  },

  /**
   * Set the toolbox title.
   * Nothing to do for this host type.
   */
  setTitle: function () {},

  /**
   * Destroy the sidebar.
   */
  destroy: function () {
    if (!this._destroyed) {
      this._destroyed = true;

      Services.prefs.setIntPref(this.widthPref, this.frame.width);
      this._sidebar.removeChild(this._splitter);
      this._sidebar.removeChild(this.frame);
    }

    return promise.resolve(null);
  }
};

/**
 * Host object for the toolbox in a separate window
 */
function WindowHost() {
  this._boundUnload = this._boundUnload.bind(this);

  EventEmitter.decorate(this);
}

WindowHost.prototype = {
  type: "window",

  WINDOW_URL: "chrome://devtools/content/framework/toolbox-window.xul",

  /**
   * Create a new xul window to contain the toolbox.
   */
  create: function () {
    let deferred = defer();

    let flags = "chrome,centerscreen,resizable,dialog=no";
    let win = Services.ww.openWindow(null, this.WINDOW_URL, "_blank",
                                     flags, null);

    let frameLoad = () => {
      win.removeEventListener("load", frameLoad, true);
      win.focus();

      let key;
      if (system.constants.platform === "macosx") {
        key = win.document.getElementById("toolbox-key-toggle-osx");
      } else {
        key = win.document.getElementById("toolbox-key-toggle");
      }
      key.removeAttribute("disabled");

      this.frame = win.document.getElementById("toolbox-iframe");
      this.emit("ready", this.frame);

      deferred.resolve(this.frame);
    };

    win.addEventListener("load", frameLoad, true);
    win.addEventListener("unload", this._boundUnload);

    this._window = win;

    return deferred.promise;
  },

  /**
   * Catch the user closing the window.
   */
  _boundUnload: function (event) {
    if (event.target.location != this.WINDOW_URL) {
      return;
    }
    this._window.removeEventListener("unload", this._boundUnload);

    this.emit("window-closed");
  },

  /**
   * Raise the host.
   */
  raise: function () {
    this._window.focus();
  },

  /**
   * Set the toolbox title.
   */
  setTitle: function (title) {
    this._window.document.title = title;
  },

  /**
   * Destroy the window.
   */
  destroy: function () {
    if (!this._destroyed) {
      this._destroyed = true;

      this._window.removeEventListener("unload", this._boundUnload);
      this._window.close();
    }

    return promise.resolve(null);
  }
};

/**
 * Host object for the toolbox in its own tab
 */
function CustomHost(hostTab, options) {
  this.frame = options.customIframe;
  this.uid = options.uid;
  EventEmitter.decorate(this);
}

CustomHost.prototype = {
  type: "custom",

  _sendMessageToTopWindow: function (msg, data) {
    // It's up to the custom frame owner (parent window) to honor
    // "close" or "raise" instructions.
    let topWindow = this.frame.ownerDocument.defaultView;
    if (!topWindow) {
      return;
    }
    let json = {name: "toolbox-" + msg, uid: this.uid};
    if (data) {
      json.data = data;
    }
    topWindow.postMessage(JSON.stringify(json), "*");
  },

  /**
   * Create a new xul window to contain the toolbox.
   */
  create: function () {
    return promise.resolve(this.frame);
  },

  /**
   * Raise the host.
   */
  raise: function () {
    this._sendMessageToTopWindow("raise");
  },

  /**
   * Set the toolbox title.
   */
  setTitle: function (title) {
    this._sendMessageToTopWindow("title", { value: title });
  },

  /**
   * Destroy the window.
   */
  destroy: function () {
    if (!this._destroyed) {
      this._destroyed = true;
      this._sendMessageToTopWindow("close");
    }
    return promise.resolve(null);
  }
};

/**
 *  Switch to the given tab in a browser and focus the browser window
 */
function focusTab(tab) {
  let browserWindow = tab.ownerDocument.defaultView;
  browserWindow.focus();
  browserWindow.gBrowser.selectedTab = tab;
}
PK
!<P�KBBDchrome/devtools/modules/devtools/client/framework/toolbox-options.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Services = require("Services");
const defer = require("devtools/shared/defer");
const {Task} = require("devtools/shared/task");
const {gDevTools} = require("devtools/client/framework/devtools");

const {LocalizationHelper} = require("devtools/shared/l10n");
const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");

loader.lazyRequireGetter(this, "system", "devtools/shared/system");

exports.OptionsPanel = OptionsPanel;

function GetPref(name) {
  let type = Services.prefs.getPrefType(name);
  switch (type) {
    case Services.prefs.PREF_STRING:
      return Services.prefs.getCharPref(name);
    case Services.prefs.PREF_INT:
      return Services.prefs.getIntPref(name);
    case Services.prefs.PREF_BOOL:
      return Services.prefs.getBoolPref(name);
    default:
      throw new Error("Unknown type");
  }
}

function SetPref(name, value) {
  let type = Services.prefs.getPrefType(name);
  switch (type) {
    case Services.prefs.PREF_STRING:
      return Services.prefs.setCharPref(name, value);
    case Services.prefs.PREF_INT:
      return Services.prefs.setIntPref(name, value);
    case Services.prefs.PREF_BOOL:
      return Services.prefs.setBoolPref(name, value);
    default:
      throw new Error("Unknown type");
  }
}

function InfallibleGetBoolPref(key) {
  try {
    return Services.prefs.getBoolPref(key);
  } catch (ex) {
    return true;
  }
}

/**
 * Represents the Options Panel in the Toolbox.
 */
function OptionsPanel(iframeWindow, toolbox) {
  this.panelDoc = iframeWindow.document;
  this.panelWin = iframeWindow;

  this.toolbox = toolbox;
  this.isReady = false;

  this._prefChanged = this._prefChanged.bind(this);
  this._themeRegistered = this._themeRegistered.bind(this);
  this._themeUnregistered = this._themeUnregistered.bind(this);
  this._disableJSClicked = this._disableJSClicked.bind(this);

  this.disableJSNode = this.panelDoc.getElementById("devtools-disable-javascript");

  this._addListeners();

  const EventEmitter = require("devtools/shared/old-event-emitter");
  EventEmitter.decorate(this);
}

OptionsPanel.prototype = {

  get target() {
    return this.toolbox.target;
  },

  open: Task.async(function* () {
    // For local debugging we need to make the target remote.
    if (!this.target.isRemote) {
      yield this.target.makeRemote();
    }

    this.setupToolsList();
    this.setupToolbarButtonsList();
    this.setupThemeList();
    this.setupNightlyOptions();
    yield this.populatePreferences();
    this.isReady = true;
    this.emit("ready");
    return this;
  }),

  _addListeners: function () {
    Services.prefs.addObserver("devtools.cache.disabled", this._prefChanged);
    Services.prefs.addObserver("devtools.theme", this._prefChanged);
    Services.prefs.addObserver("devtools.source-map.client-service.enabled",
                               this._prefChanged);
    gDevTools.on("theme-registered", this._themeRegistered);
    gDevTools.on("theme-unregistered", this._themeUnregistered);
  },

  _removeListeners: function () {
    Services.prefs.removeObserver("devtools.cache.disabled", this._prefChanged);
    Services.prefs.removeObserver("devtools.theme", this._prefChanged);
    Services.prefs.removeObserver("devtools.source-map.client-service.enabled",
                                  this._prefChanged);
    gDevTools.off("theme-registered", this._themeRegistered);
    gDevTools.off("theme-unregistered", this._themeUnregistered);
  },

  _prefChanged: function (subject, topic, prefName) {
    if (prefName === "devtools.cache.disabled") {
      let cacheDisabled = GetPref(prefName);
      let cbx = this.panelDoc.getElementById("devtools-disable-cache");
      cbx.checked = cacheDisabled;
    } else if (prefName === "devtools.theme") {
      this.updateCurrentTheme();
    } else if (prefName === "devtools.source-map.client-service.enabled") {
      this.updateSourceMapPref();
    }
  },

  _themeRegistered: function (event, themeId) {
    this.setupThemeList();
  },

  _themeUnregistered: function (event, theme) {
    let themeBox = this.panelDoc.getElementById("devtools-theme-box");
    let themeInput = themeBox.querySelector(`[value=${theme.id}]`);

    if (themeInput) {
      themeInput.parentNode.remove();
    }
  },

  setupToolbarButtonsList: Task.async(function* () {
    // Ensure the toolbox is open, and the buttons are all set up.
    yield this.toolbox.isOpen;

    let enabledToolbarButtonsBox = this.panelDoc.getElementById(
      "enabled-toolbox-buttons-box");

    let toolbarButtons = this.toolbox.toolbarButtons;

    if (!toolbarButtons) {
      console.warn("The command buttons weren't initiated yet.");
      return;
    }

    let onCheckboxClick = (checkbox) => {
      let commandButton = toolbarButtons.filter(
        toggleableButton => toggleableButton.id === checkbox.id)[0];
      Services.prefs.setBoolPref(
        commandButton.visibilityswitch, checkbox.checked);
      this.toolbox.updateToolboxButtonsVisibility();
    };

    let createCommandCheckbox = button => {
      let checkboxLabel = this.panelDoc.createElement("label");
      let checkboxSpanLabel = this.panelDoc.createElement("span");
      checkboxSpanLabel.textContent = button.description;
      let checkboxInput = this.panelDoc.createElement("input");
      checkboxInput.setAttribute("type", "checkbox");
      checkboxInput.setAttribute("id", button.id);
      if (button.isVisible) {
        checkboxInput.setAttribute("checked", true);
      }
      checkboxInput.addEventListener("change",
        onCheckboxClick.bind(this, checkboxInput));

      checkboxLabel.appendChild(checkboxInput);
      checkboxLabel.appendChild(checkboxSpanLabel);
      return checkboxLabel;
    };

    for (let button of toolbarButtons) {
      if (!button.isTargetSupported(this.toolbox.target)) {
        continue;
      }

      enabledToolbarButtonsBox.appendChild(createCommandCheckbox(button));
    }
  }),

  setupToolsList: function () {
    let defaultToolsBox = this.panelDoc.getElementById("default-tools-box");
    let additionalToolsBox = this.panelDoc.getElementById(
      "additional-tools-box");
    let toolsNotSupportedLabel = this.panelDoc.getElementById(
      "tools-not-supported-label");
    let atleastOneToolNotSupported = false;

    const toolbox = this.toolbox;

    // Signal tool registering/unregistering globally (for the tools registered
    // globally) and per toolbox (for the tools registered to a single toolbox).
    let onCheckboxClick = function (id) {
      let toolDefinition = gDevTools._tools.get(id) || toolbox.getToolDefinition(id);
      // Set the kill switch pref boolean to true
      Services.prefs.setBoolPref(toolDefinition.visibilityswitch, this.checked);
      gDevTools.emit(this.checked ? "tool-registered" : "tool-unregistered", id);
    };

    let createToolCheckbox = tool => {
      let checkboxLabel = this.panelDoc.createElement("label");
      let checkboxInput = this.panelDoc.createElement("input");
      checkboxInput.setAttribute("type", "checkbox");
      checkboxInput.setAttribute("id", tool.id);
      checkboxInput.setAttribute("title", tool.tooltip || "");

      let checkboxSpanLabel = this.panelDoc.createElement("span");
      if (tool.isTargetSupported(this.target)) {
        checkboxSpanLabel.textContent = tool.label;
      } else {
        atleastOneToolNotSupported = true;
        checkboxSpanLabel.textContent =
          L10N.getFormatStr("options.toolNotSupportedMarker", tool.label);
        checkboxInput.setAttribute("data-unsupported", "true");
        checkboxInput.setAttribute("disabled", "true");
      }

      if (InfallibleGetBoolPref(tool.visibilityswitch)) {
        checkboxInput.setAttribute("checked", "true");
      }

      checkboxInput.addEventListener("change",
        onCheckboxClick.bind(checkboxInput, tool.id));

      checkboxLabel.appendChild(checkboxInput);
      checkboxLabel.appendChild(checkboxSpanLabel);
      return checkboxLabel;
    };

    // Populating the default tools lists
    let toggleableTools = gDevTools.getDefaultTools().filter(tool => {
      return tool.visibilityswitch && !tool.hiddenInOptions;
    });

    for (let tool of toggleableTools) {
      defaultToolsBox.appendChild(createToolCheckbox(tool));
    }

    // Populating the additional tools list that came from add-ons.
    let atleastOneAddon = false;
    for (let tool of gDevTools.getAdditionalTools()) {
      atleastOneAddon = true;
      additionalToolsBox.appendChild(createToolCheckbox(tool));
    }

    // Populating the additional toolbox-specific tools list that came
    // from WebExtension add-ons.
    for (let tool of this.toolbox.getAdditionalTools()) {
      atleastOneAddon = true;
      additionalToolsBox.appendChild(createToolCheckbox(tool));
    }

    if (!atleastOneAddon) {
      additionalToolsBox.style.display = "none";
    }

    if (!atleastOneToolNotSupported) {
      toolsNotSupportedLabel.style.display = "none";
    }

    this.panelWin.focus();
  },

  setupThemeList: function () {
    let themeBox = this.panelDoc.getElementById("devtools-theme-box");
    let themeLabels = themeBox.querySelectorAll("label");
    for (let label of themeLabels) {
      label.remove();
    }

    let createThemeOption = theme => {
      let inputLabel = this.panelDoc.createElement("label");
      let inputRadio = this.panelDoc.createElement("input");
      inputRadio.setAttribute("type", "radio");
      inputRadio.setAttribute("value", theme.id);
      inputRadio.setAttribute("name", "devtools-theme-item");
      inputRadio.addEventListener("change", function (e) {
        SetPref(themeBox.getAttribute("data-pref"),
          e.target.value);
      });

      let inputSpanLabel = this.panelDoc.createElement("span");
      inputSpanLabel.textContent = theme.label;
      inputLabel.appendChild(inputRadio);
      inputLabel.appendChild(inputSpanLabel);

      return inputLabel;
    };

    // Populating the default theme list
    let themes = gDevTools.getThemeDefinitionArray();
    for (let theme of themes) {
      themeBox.appendChild(createThemeOption(theme));
    }

    this.updateCurrentTheme();
  },

  /**
   * Add common preferences enabled only on Nightly.
   */
  setupNightlyOptions: function () {
    let isNightly = system.constants.NIGHTLY_BUILD;
    if (!isNightly) {
      return;
    }

    // Labels for these new buttons are nightly only and mostly intended for working on
    // devtools.
    let prefDefinitions = [{
      pref: "devtools.webconsole.new-frontend-enabled",
      label: L10N.getStr("toolbox.options.enableNewConsole.label"),
      id: "devtools-new-webconsole",
      parentId: "webconsole-options"
    }, {
      pref: "devtools.debugger.new-debugger-frontend",
      label: L10N.getStr("toolbox.options.enableNewDebugger.label"),
      id: "devtools-new-debugger",
      parentId: "debugger-options"
    }, {
      pref: "devtools.performance.new-panel-enabled",
      label: "Enable new performance recorder (then re-open DevTools)",
      id: "devtools-new-performance",
      parentId: "context-options"
    }];

    let createPreferenceOption = ({pref, label, id}) => {
      let inputLabel = this.panelDoc.createElement("label");
      let checkbox = this.panelDoc.createElement("input");
      checkbox.setAttribute("type", "checkbox");
      if (GetPref(pref)) {
        checkbox.setAttribute("checked", "checked");
      }
      checkbox.setAttribute("id", id);
      checkbox.addEventListener("change", e => {
        SetPref(pref, e.target.checked);
      });

      let inputSpanLabel = this.panelDoc.createElement("span");
      inputSpanLabel.textContent = label;
      inputLabel.appendChild(checkbox);
      inputLabel.appendChild(inputSpanLabel);

      return inputLabel;
    };

    for (let prefDefinition of prefDefinitions) {
      let parent = this.panelDoc.getElementById(prefDefinition.parentId);
      parent.appendChild(createPreferenceOption(prefDefinition));
      parent.removeAttribute("hidden");
    }
  },

  populatePreferences: Task.async(function* () {
    let prefCheckboxes = this.panelDoc.querySelectorAll(
      "input[type=checkbox][data-pref]");
    for (let prefCheckbox of prefCheckboxes) {
      if (GetPref(prefCheckbox.getAttribute("data-pref"))) {
        prefCheckbox.setAttribute("checked", true);
      }
      prefCheckbox.addEventListener("change", function (e) {
        let checkbox = e.target;
        SetPref(checkbox.getAttribute("data-pref"), checkbox.checked);
      });
    }
    // Themes radio inputs are handled in setupThemeList
    let prefRadiogroups = this.panelDoc.querySelectorAll(
      ".radiogroup[data-pref]:not(#devtools-theme-box)");
    for (let radioGroup of prefRadiogroups) {
      let selectedValue = GetPref(radioGroup.getAttribute("data-pref"));

      for (let radioInput of radioGroup.querySelectorAll("input[type=radio]")) {
        if (radioInput.getAttribute("value") == selectedValue) {
          radioInput.setAttribute("checked", true);
        }

        radioInput.addEventListener("change", function (e) {
          SetPref(radioGroup.getAttribute("data-pref"),
            e.target.value);
        });
      }
    }
    let prefSelects = this.panelDoc.querySelectorAll("select[data-pref]");
    for (let prefSelect of prefSelects) {
      let pref = GetPref(prefSelect.getAttribute("data-pref"));
      let options = [...prefSelect.options];
      options.some(function (option) {
        let value = option.value;
        // non strict check to allow int values.
        if (value == pref) {
          prefSelect.selectedIndex = options.indexOf(option);
          return true;
        }
        return false;
      });

      prefSelect.addEventListener("change", function (e) {
        let select = e.target;
        SetPref(select.getAttribute("data-pref"),
          select.options[select.selectedIndex].value);
      });
    }

    if (this.target.activeTab) {
      let [ response ] = yield this.target.client.attachTab(this.target.activeTab._actor);
      this._origJavascriptEnabled = !response.javascriptEnabled;
      this.disableJSNode.checked = this._origJavascriptEnabled;
      this.disableJSNode.addEventListener("click", this._disableJSClicked);
    } else {
      // Hide the checkbox and label
      this.disableJSNode.parentNode.style.display = "none";
    }
  }),

  updateCurrentTheme: function () {
    let currentTheme = GetPref("devtools.theme");
    let themeBox = this.panelDoc.getElementById("devtools-theme-box");
    let themeRadioInput = themeBox.querySelector(`[value=${currentTheme}]`);

    if (themeRadioInput) {
      themeRadioInput.checked = true;
    } else {
      // If the current theme does not exist anymore, switch to light theme
      let lightThemeInputRadio = themeBox.querySelector("[value=light]");
      lightThemeInputRadio.checked = true;
    }
  },

  updateSourceMapPref: function () {
    const prefName = "devtools.source-map.client-service.enabled";
    let enabled = GetPref(prefName);
    let box = this.panelDoc.querySelector(`[data-pref="${prefName}"]`);
    box.checked = enabled;
  },

  /**
   * Disables JavaScript for the currently loaded tab. We force a page refresh
   * here because setting docShell.allowJavascript to true fails to block JS
   * execution from event listeners added using addEventListener(), AJAX calls
   * and timers. The page refresh prevents these things from being added in the
   * first place.
   *
   * @param {Event} event
   *        The event sent by checking / unchecking the disable JS checkbox.
   */
  _disableJSClicked: function (event) {
    let checked = event.target.checked;

    let options = {
      "javascriptEnabled": !checked
    };

    this.target.activeTab.reconfigure(options);
  },

  destroy: function () {
    if (this.destroyPromise) {
      return this.destroyPromise;
    }

    let deferred = defer();
    this.destroyPromise = deferred.promise;

    this._removeListeners();

    if (this.target.activeTab) {
      this.disableJSNode.removeEventListener("click", this._disableJSClicked);
      // FF41+ automatically cleans up state in actor on disconnect
      if (!this.target.activeTab.traits.noTabReconfigureOnClose) {
        let options = {
          "javascriptEnabled": this._origJavascriptEnabled,
          "performReload": false
        };
        this.target.activeTab.reconfigure(options, deferred.resolve);
      } else {
        deferred.resolve();
      }
    } else {
      deferred.resolve();
    }

    this.panelWin = this.panelDoc = this.disableJSNode = this.toolbox = null;

    return this.destroyPromise;
  }
};
PK
!<-���b�b�<chrome/devtools/modules/devtools/client/framework/toolbox.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 SOURCE_MAP_WORKER = "resource://devtools/client/shared/source-map/worker.js";

const MAX_ORDINAL = 99;
const SPLITCONSOLE_ENABLED_PREF = "devtools.toolbox.splitconsoleEnabled";
const SPLITCONSOLE_HEIGHT_PREF = "devtools.toolbox.splitconsoleHeight";
const DISABLE_AUTOHIDE_PREF = "ui.popup.disable_autohide";
const HOST_HISTOGRAM = "DEVTOOLS_TOOLBOX_HOST";
const SCREENSIZE_HISTOGRAM = "DEVTOOLS_SCREEN_RESOLUTION_ENUMERATED_PER_USER";
const CURRENT_THEME_SCALAR = "devtools.current_theme";
const HTML_NS = "http://www.w3.org/1999/xhtml";

var {Ci, Cc} = require("chrome");
var promise = require("promise");
var defer = require("devtools/shared/defer");
var Services = require("Services");
var ChromeUtils = require("ChromeUtils");
var {Task} = require("devtools/shared/task");
var {gDevTools} = require("devtools/client/framework/devtools");
var EventEmitter = require("devtools/shared/old-event-emitter");
var Telemetry = require("devtools/client/shared/telemetry");
var { attachThread, detachThread } = require("./attach-thread");
var Menu = require("devtools/client/framework/menu");
var MenuItem = require("devtools/client/framework/menu-item");
var { DOMHelpers } = require("resource://devtools/client/shared/DOMHelpers.jsm");
const { KeyCodes } = require("devtools/client/shared/keycodes");
var Startup = Cc["@mozilla.org/devtools/startup-clh;1"].getService(Ci.nsISupports)
  .wrappedJSObject;

const { BrowserLoader } =
  ChromeUtils.import("resource://devtools/client/shared/browser-loader.js", {});

const {LocalizationHelper} = require("devtools/shared/l10n");
const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");

loader.lazyRequireGetter(this, "getHighlighterUtils",
  "devtools/client/framework/toolbox-highlighter-utils", true);
loader.lazyRequireGetter(this, "Selection",
  "devtools/client/framework/selection", true);
loader.lazyRequireGetter(this, "InspectorFront",
  "devtools/shared/fronts/inspector", true);
loader.lazyRequireGetter(this, "flags",
  "devtools/shared/flags");
loader.lazyRequireGetter(this, "showDoorhanger",
  "devtools/client/shared/doorhanger", true);
loader.lazyRequireGetter(this, "createPerformanceFront",
  "devtools/shared/fronts/performance", true);
loader.lazyRequireGetter(this, "system",
  "devtools/shared/system");
loader.lazyRequireGetter(this, "getPreferenceFront",
  "devtools/shared/fronts/preference", true);
loader.lazyRequireGetter(this, "KeyShortcuts",
  "devtools/client/shared/key-shortcuts");
loader.lazyRequireGetter(this, "ZoomKeys",
  "devtools/client/shared/zoom-keys");
loader.lazyRequireGetter(this, "settleAll",
  "devtools/shared/ThreadSafeDevToolsUtils", true);
loader.lazyRequireGetter(this, "ToolboxButtons",
  "devtools/client/definitions", true);
loader.lazyRequireGetter(this, "SourceMapURLService",
  "devtools/client/framework/source-map-url-service", true);
loader.lazyRequireGetter(this, "HUDService",
  "devtools/client/webconsole/hudservice", true);
loader.lazyRequireGetter(this, "viewSource",
  "devtools/client/shared/view-source");
loader.lazyRequireGetter(this, "StyleSheetsFront",
  "devtools/shared/fronts/stylesheets", true);
loader.lazyRequireGetter(this, "buildHarLog",
  "devtools/client/netmonitor/src/har/har-builder-utils", true);
loader.lazyRequireGetter(this, "getKnownDeviceFront",
  "devtools/shared/fronts/device", true);

loader.lazyGetter(this, "domNodeConstants", () => {
  return require("devtools/shared/dom-node-constants");
});

loader.lazyGetter(this, "registerHarOverlay", () => {
  return require("devtools/client/netmonitor/src/har/toolbox-overlay").register;
});

/**
 * A "Toolbox" is the component that holds all the tools for one specific
 * target. Visually, it's a document that includes the tools tabs and all
 * the iframes where the tool panels will be living in.
 *
 * @param {object} target
 *        The object the toolbox is debugging.
 * @param {string} selectedTool
 *        Tool to select initially
 * @param {Toolbox.HostType} hostType
 *        Type of host that will host the toolbox (e.g. sidebar, window)
 * @param {DOMWindow} contentWindow
 *        The window object of the toolbox document
 * @param {string} frameId
 *        A unique identifier to differentiate toolbox documents from the
 *        chrome codebase when passing DOM messages
 */
function Toolbox(target, selectedTool, hostType, contentWindow, frameId) {
  this._target = target;
  this._win = contentWindow;
  this.frameId = frameId;

  this._toolPanels = new Map();
  this._inspectorExtensionSidebars = new Map();
  this._telemetry = new Telemetry();

  this._initInspector = null;
  this._inspector = null;
  this._styleSheets = null;

  // Map of frames (id => frame-info) and currently selected frame id.
  this.frameMap = new Map();
  this.selectedFrameId = null;

  // List of listeners for `devtools.network.onRequestFinished` WebExt API
  this._requestFinishedListeners = new Set();

  this._toolRegistered = this._toolRegistered.bind(this);
  this._toolUnregistered = this._toolUnregistered.bind(this);
  this._onWillNavigate = this._onWillNavigate.bind(this);
  this._refreshHostTitle = this._refreshHostTitle.bind(this);
  this._toggleNoAutohide = this._toggleNoAutohide.bind(this);
  this.showFramesMenu = this.showFramesMenu.bind(this);
  this.handleKeyDownOnFramesButton = this.handleKeyDownOnFramesButton.bind(this);
  this.showFramesMenuOnKeyDown = this.showFramesMenuOnKeyDown.bind(this);
  this._updateFrames = this._updateFrames.bind(this);
  this._splitConsoleOnKeypress = this._splitConsoleOnKeypress.bind(this);
  this.destroy = this.destroy.bind(this);
  this.highlighterUtils = getHighlighterUtils(this);
  this._highlighterReady = this._highlighterReady.bind(this);
  this._highlighterHidden = this._highlighterHidden.bind(this);
  this._applyCacheSettings = this._applyCacheSettings.bind(this);
  this._applyServiceWorkersTestingSettings =
    this._applyServiceWorkersTestingSettings.bind(this);
  this._saveSplitConsoleHeight = this._saveSplitConsoleHeight.bind(this);
  this._onFocus = this._onFocus.bind(this);
  this._onBrowserMessage = this._onBrowserMessage.bind(this);
  this._showDevEditionPromo = this._showDevEditionPromo.bind(this);
  this._updateTextBoxMenuItems = this._updateTextBoxMenuItems.bind(this);
  this._onBottomHostMinimized = this._onBottomHostMinimized.bind(this);
  this._onBottomHostMaximized = this._onBottomHostMaximized.bind(this);
  this._onToolSelectWhileMinimized = this._onToolSelectWhileMinimized.bind(this);
  this._onPerformanceFrontEvent = this._onPerformanceFrontEvent.bind(this);
  this._onBottomHostWillChange = this._onBottomHostWillChange.bind(this);
  this._toggleMinimizeMode = this._toggleMinimizeMode.bind(this);
  this._onToolbarFocus = this._onToolbarFocus.bind(this);
  this._onToolbarArrowKeypress = this._onToolbarArrowKeypress.bind(this);
  this._onPickerClick = this._onPickerClick.bind(this);
  this._onPickerKeypress = this._onPickerKeypress.bind(this);
  this._onPickerStarted = this._onPickerStarted.bind(this);
  this._onPickerStopped = this._onPickerStopped.bind(this);
  this._onInspectObject = this._onInspectObject.bind(this);
  this._onNewSelectedNodeFront = this._onNewSelectedNodeFront.bind(this);
  this._updatePickerButton = this._updatePickerButton.bind(this);
  this.selectTool = this.selectTool.bind(this);

  this._target.on("close", this.destroy);

  if (!selectedTool) {
    selectedTool = Services.prefs.getCharPref(this._prefs.LAST_TOOL);
  }
  this._defaultToolId = selectedTool;

  this._hostType = hostType;

  this._isOpenDeferred = defer();
  this.isOpen = this._isOpenDeferred.promise;

  EventEmitter.decorate(this);

  this._target.on("will-navigate", this._onWillNavigate);
  this._target.on("navigate", this._refreshHostTitle);
  this._target.on("frame-update", this._updateFrames);
  this._target.on("inspect-object", this._onInspectObject);

  this.on("host-changed", this._refreshHostTitle);
  this.on("select", this._refreshHostTitle);
  this.on("select", this._updatePickerButton);

  this.on("ready", this._showDevEditionPromo);

  gDevTools.on("tool-registered", this._toolRegistered);
  gDevTools.on("tool-unregistered", this._toolUnregistered);

  this.on("picker-started", this._onPickerStarted);
  this.on("picker-stopped", this._onPickerStopped);

  /**
   * Get text direction for the current locale direction.
   *
   * `getComputedStyle` forces a synchronous reflow, so use a lazy getter in order to
   * call it only once.
   */
  loader.lazyGetter(this, "direction", () => {
    // Get the direction from browser.xul document
    let top = this.win.top;
    let topDocEl = top.document.documentElement;
    let isRtl = top.getComputedStyle(topDocEl).direction === "rtl";
    return isRtl ? "rtl" : "ltr";
  });
}
exports.Toolbox = Toolbox;

/**
 * The toolbox can be 'hosted' either embedded in a browser window
 * or in a separate window.
 */
Toolbox.HostType = {
  BOTTOM: "bottom",
  SIDE: "side",
  WINDOW: "window",
  CUSTOM: "custom"
};

Toolbox.prototype = {
  _URL: "about:devtools-toolbox",

  _prefs: {
    LAST_TOOL: "devtools.toolbox.selectedTool",
    SIDE_ENABLED: "devtools.toolbox.sideEnabled",
  },

  get currentToolId() {
    return this._currentToolId;
  },

  set currentToolId(id) {
    this._currentToolId = id;
    this.component.setCurrentToolId(id);
  },

  get panelDefinitions() {
    return this._panelDefinitions;
  },

  set panelDefinitions(definitions) {
    this._panelDefinitions = definitions;
    this._combineAndSortPanelDefinitions();
  },

  get visibleAdditionalTools() {
    if (!this._visibleAdditionalTools) {
      this._visibleAdditionalTools = [];
    }

    return this._visibleAdditionalTools;
  },

  set visibleAdditionalTools(tools) {
    this._visibleAdditionalTools = tools;
    if (this.isReady) {
      this._combineAndSortPanelDefinitions();
    }
  },

  /**
   * Combines the built-in panel definitions and the additional tool definitions that
   * can be set by add-ons.
   */
  _combineAndSortPanelDefinitions() {
    const definitions = [...this._panelDefinitions, ...this.getVisibleAdditionalTools()];
    definitions.sort(definition => {
      return -1 * (definition.ordinal == undefined || definition.ordinal < 0
        ? MAX_ORDINAL
        : definition.ordinal
      );
    });
    this.component.setPanelDefinitions(definitions);
  },

  lastUsedToolId: null,

  /**
   * Returns a *copy* of the _toolPanels collection.
   *
   * @return {Map} panels
   *         All the running panels in the toolbox
   */
  getToolPanels: function () {
    return new Map(this._toolPanels);
  },

  /**
   * Access the panel for a given tool
   */
  getPanel: function (id) {
    return this._toolPanels.get(id);
  },

  /**
   * Get the panel instance for a given tool once it is ready.
   * If the tool is already opened, the promise will resolve immediately,
   * otherwise it will wait until the tool has been opened before resolving.
   *
   * Note that this does not open the tool, use selectTool if you'd
   * like to select the tool right away.
   *
   * @param  {String} id
   *         The id of the panel, for example "jsdebugger".
   * @returns Promise
   *          A promise that resolves once the panel is ready.
   */
  getPanelWhenReady: function (id) {
    let deferred = defer();
    let panel = this.getPanel(id);
    if (panel) {
      deferred.resolve(panel);
    } else {
      this.on(id + "-ready", (e, initializedPanel) => {
        deferred.resolve(initializedPanel);
      });
    }

    return deferred.promise;
  },

  /**
   * This is a shortcut for getPanel(currentToolId) because it is much more
   * likely that we're going to want to get the panel that we've just made
   * visible
   */
  getCurrentPanel: function () {
    return this._toolPanels.get(this.currentToolId);
  },

  /**
   * Get/alter the target of a Toolbox so we're debugging something different.
   * See Target.jsm for more details.
   * TODO: Do we allow |toolbox.target = null;| ?
   */
  get target() {
    return this._target;
  },

  get threadClient() {
    return this._threadClient;
  },

  /**
   * Get/alter the host of a Toolbox, i.e. is it in browser or in a separate
   * tab. See HostType for more details.
   */
  get hostType() {
    return this._hostType;
  },

  /**
   * Shortcut to the window containing the toolbox UI
   */
  get win() {
    return this._win;
  },

  /**
   * Shortcut to the document containing the toolbox UI
   */
  get doc() {
    return this.win.document;
  },

  /**
   * Get the toolbox highlighter front. Note that it may not always have been
   * initialized first. Use `initInspector()` if needed.
   * Consider using highlighterUtils instead, it exposes the highlighter API in
   * a useful way for the toolbox panels
   */
  get highlighter() {
    return this._highlighter;
  },

  /**
   * Get the toolbox's performance front. Note that it may not always have been
   * initialized first. Use `initPerformance()` if needed.
   */
  get performance() {
    return this._performance;
  },

  /**
   * Get the toolbox's inspector front. Note that it may not always have been
   * initialized first. Use `initInspector()` if needed.
   */
  get inspector() {
    return this._inspector;
  },

  /**
   * Get the toolbox's walker front. Note that it may not always have been
   * initialized first. Use `initInspector()` if needed.
   */
  get walker() {
    return this._walker;
  },

  /**
   * Get the toolbox's node selection. Note that it may not always have been
   * initialized first. Use `initInspector()` if needed.
   */
  get selection() {
    return this._selection;
  },

  /**
   * Get the toggled state of the split console
   */
  get splitConsole() {
    return this._splitConsole;
  },

  /**
   * Get the focused state of the split console
   */
  isSplitConsoleFocused: function () {
    if (!this._splitConsole) {
      return false;
    }
    let focusedWin = Services.focus.focusedWindow;
    return focusedWin && focusedWin ===
      this.doc.querySelector("#toolbox-panel-iframe-webconsole").contentWindow;
  },

  /**
   * Open the toolbox
   */
  open: function () {
    return Task.spawn(function* () {
      this.browserRequire = BrowserLoader({
        window: this.doc.defaultView,
        useOnlyShared: true
      }).require;

      if (this.win.location.href.startsWith(this._URL)) {
        // Update the URL so that onceDOMReady watch for the right url.
        this._URL = this.win.location.href;
      }

      let domReady = defer();
      let domHelper = new DOMHelpers(this.win);
      domHelper.onceDOMReady(() => {
        domReady.resolve();
      }, this._URL);

      // Optimization: fire up a few other things before waiting on
      // the iframe being ready (makes startup faster)

      // Load the toolbox-level actor fronts and utilities now
      yield this._target.makeRemote();

      // Start tracking network activity on toolbox open for targets such as tabs.
      // (Workers and potentially others don't manage the console client in the target.)
      if (this._target.activeConsole) {
        yield this._target.activeConsole.startListeners([
          "NetworkActivity",
        ]);
      }

      // Attach the thread
      this._threadClient = yield attachThread(this);
      yield domReady.promise;

      this.isReady = true;

      let framesPromise = this._listFrames();

      Services.prefs.addObserver("devtools.cache.disabled", this._applyCacheSettings);
      Services.prefs.addObserver("devtools.serviceWorkers.testing.enabled",
                                 this._applyServiceWorkersTestingSettings);

      this.textBoxContextMenuPopup =
        this.doc.getElementById("toolbox-textbox-context-popup");
      this.textBoxContextMenuPopup.addEventListener("popupshowing",
        this._updateTextBoxMenuItems, true);
      this.doc.addEventListener("contextmenu", (e) => {
        if (e.originalTarget.closest("input[type=text]") ||
            e.originalTarget.closest("input[type=search]") ||
            e.originalTarget.closest("input:not([type])") ||
            e.originalTarget.closest("textarea")) {
          e.stopPropagation();
          e.preventDefault();
          this.openTextBoxContextMenu(e.screenX, e.screenY);
        }
      });

      this.shortcuts = new KeyShortcuts({
        window: this.doc.defaultView
      });
      // Get the DOM element to mount the ToolboxController to.
      this._componentMount = this.doc.getElementById("toolbox-toolbar-mount");

      this._mountReactComponent();
      this._buildDockButtons();
      this._buildOptions();
      this._buildTabs();
      this._applyCacheSettings();
      this._applyServiceWorkersTestingSettings();
      this._addKeysToWindow();
      this._addReloadKeys();
      this._addHostListeners();
      this._registerOverlays();
      if (!this._hostOptions || this._hostOptions.zoom === true) {
        ZoomKeys.register(this.win);
      }

      this._componentMount.addEventListener("keypress", this._onToolbarArrowKeypress);
      this._componentMount.setAttribute("aria-label", L10N.getStr("toolbox.label"));

      this.webconsolePanel = this.doc.querySelector("#toolbox-panel-webconsole");
      this.webconsolePanel.height = Services.prefs.getIntPref(SPLITCONSOLE_HEIGHT_PREF);
      this.webconsolePanel.addEventListener("resize", this._saveSplitConsoleHeight);

      let buttonsPromise = this._buildButtons();

      this._pingTelemetry();

      // The isTargetSupported check needs to happen after the target is
      // remoted, otherwise we could have done it in the toolbox constructor
      // (bug 1072764).
      let toolDef = gDevTools.getToolDefinition(this._defaultToolId);
      if (!toolDef || !toolDef.isTargetSupported(this._target)) {
        this._defaultToolId = "webconsole";
      }

      // Start rendering the toolbox toolbar before selecting the tool, as the tools
      // can take a few hundred milliseconds seconds to start up.
      // But wait for toolbar buttons to be set before updating this react component.
      buttonsPromise.then(() => {
        // Delay React rendering as Toolbox.open and buttonsPromise are synchronous.
        // Even if this involve promises, this is synchronous. Toolbox.open already loads
        // react modules and freeze the event loop for a significant time.
        // requestIdleCallback allows releasing it to allow user events to be processed.
        // Use 16ms maximum delay to allow one frame to be rendered at 60FPS
        // (1000ms/60FPS=16ms)
        this.win.requestIdleCallback(() => {
          this.component.setCanRender();
        }, {timeout: 16});
      });

      yield this.selectTool(this._defaultToolId);

      // Wait until the original tool is selected so that the split
      // console input will receive focus.
      let splitConsolePromise = promise.resolve();
      if (Services.prefs.getBoolPref(SPLITCONSOLE_ENABLED_PREF)) {
        splitConsolePromise = this.openSplitConsole();
      }

      yield promise.all([
        splitConsolePromise,
        buttonsPromise,
        framesPromise
      ]);

      // Lazily connect to the profiler here and don't wait for it to complete,
      // used to intercept console.profile calls before the performance tools are open.
      let performanceFrontConnection = this.initPerformance();

      // If in testing environment, wait for performance connection to finish,
      // so we don't have to explicitly wait for this in tests; ideally, all tests
      // will handle this on their own, but each have their own tear down function.
      if (flags.testing) {
        yield performanceFrontConnection;
      }

      this.emit("ready");
      this._isOpenDeferred.resolve();
    }.bind(this)).catch(console.error.bind(console));
  },

  /**
   * loading React modules when needed (to avoid performance penalties
   * during Firefox start up time).
   */
  get React() {
    return this.browserRequire("devtools/client/shared/vendor/react");
  },

  get ReactDOM() {
    return this.browserRequire("devtools/client/shared/vendor/react-dom");
  },

  get ReactRedux() {
    return this.browserRequire("devtools/client/shared/vendor/react-redux");
  },

  get ToolboxController() {
    return this.browserRequire("devtools/client/framework/components/toolbox-controller");
  },

  /**
   * Unconditionally create and get the source map service.
   */
  _createSourceMapService: function () {
    if (this._sourceMapService) {
      return this._sourceMapService;
    }
    // Uses browser loader to access the `Worker` global.
    let service = this.browserRequire("devtools/client/shared/source-map/index");

    // Provide a wrapper for the service that reports errors more nicely.
    this._sourceMapService = new Proxy(service, {
      get: (target, name) => {
        switch (name) {
          case "getOriginalURLs":
            return (urlInfo) => {
              return target.getOriginalURLs(urlInfo)
                .catch(text => {
                  let message = L10N.getFormatStr("toolbox.sourceMapFailure",
                                                  text, urlInfo.url,
                                                  urlInfo.sourceMapURL);
                  this.target.logWarningInPage(message, "source map");
                  // It's ok to swallow errors here, because a null
                  // result just means that no source map was found.
                  return null;
                });
            };

          case "getOriginalSourceText":
            return (originalSource) => {
              return target.getOriginalSourceText(originalSource)
                .catch(text => {
                  let message = L10N.getFormatStr("toolbox.sourceMapSourceFailure",
                                                  text, originalSource.url);
                  this.target.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",
                  };
                });
            };

          case "applySourceMap":
            return (generatedId, url, code, mappings) => {
              return target.applySourceMap(generatedId, url, code, mappings)
                .then(result => {
                  // If a tool has changed or introduced a source map
                  // (e.g, by pretty-printing a source), tell the
                  // source map URL service about the change, so that
                  // subscribers to that service can be updated as
                  // well.
                  if (this._sourceMapURLService) {
                    this._sourceMapURLService.sourceMapChanged(generatedId, url);
                  }
                  return result;
                });
            };

          default:
            return target[name];
        }
      },
    });

    this._sourceMapService.startSourceMapWorker(SOURCE_MAP_WORKER);
    return this._sourceMapService;
  },

  /**
   * A common access point for the client-side mapping service for source maps that
   * any panel can use.  This is a "low-level" API that connects to
   * the source map worker.
   */
  get sourceMapService() {
    if (!Services.prefs.getBoolPref("devtools.source-map.client-service.enabled")) {
      return null;
    }
    return this._createSourceMapService();
  },

  /**
   * Clients wishing to use source maps but that want the toolbox to
   * track the source and style sheet actor mapping can use this
   * source map service.  This is a higher-level service than the one
   * returned by |sourceMapService|, in that it automatically tracks
   * source and style sheet actor IDs.
   */
  get sourceMapURLService() {
    if (this._sourceMapURLService) {
      return this._sourceMapURLService;
    }
    let sourceMaps = this._createSourceMapService();
    this._sourceMapURLService = new SourceMapURLService(this, sourceMaps);
    return this._sourceMapURLService;
  },

  // Return HostType id for telemetry
  _getTelemetryHostId: function () {
    switch (this.hostType) {
      case Toolbox.HostType.BOTTOM: return 0;
      case Toolbox.HostType.SIDE: return 1;
      case Toolbox.HostType.WINDOW: return 2;
      case Toolbox.HostType.CUSTOM: return 3;
      default: return 9;
    }
  },

  _pingTelemetry: function () {
    this._telemetry.toolOpened("toolbox");

    this._telemetry.logOncePerBrowserVersion(SCREENSIZE_HISTOGRAM,
                                             system.getScreenDimensions());
    this._telemetry.log(HOST_HISTOGRAM, this._getTelemetryHostId());

    // Log current theme. The question we want to answer is:
    // "What proportion of users use which themes?"
    let currentTheme = Services.prefs.getCharPref("devtools.theme");
    this._telemetry.logKeyedScalar(CURRENT_THEME_SCALAR, currentTheme, 1);
  },

  /**
   * Create a simple object to store the state of a toolbox button. The checked state of
   * a button can be updated arbitrarily outside of the scope of the toolbar and its
   * controllers. In order to simplify this interaction this object emits an
   * "updatechecked" event any time the isChecked value is updated, allowing any consuming
   * components to listen and respond to updates.
   *
   * @param {Object} options:
   *
   * @property {String} id - The id of the button or command.
   * @property {String} className - An optional additional className for the button.
   * @property {String} description - The value that will display as a tooltip and in
   *                    the options panel for enabling/disabling.
   * @property {Boolean} disabled - An optional disabled state for the button.
   * @property {Function} onClick - The function to run when the button is activated by
   *                      click or keyboard shortcut. First argument will be the 'click'
   *                      event, and second argument is the toolbox instance.
   * @property {Boolean} isInStartContainer - Buttons can either be placed at the start
   *                     of the toolbar, or at the end.
   * @property {Function} setup - Function run immediately to listen for events changing
   *                      whenever the button is checked or unchecked. The toolbox object
   *                      is passed as first argument and a callback is passed as second
   *                       argument, to be called whenever the checked state changes.
   * @property {Function} teardown - Function run on toolbox close to let a chance to
   *                      unregister listeners set when `setup` was called and avoid
   *                      memory leaks. The same arguments than `setup` function are
   *                      passed to `teardown`.
   * @property {Function} isTargetSupported - Function to automatically enable/disable
   *                      the button based on the target. If the target don't support
   *                      the button feature, this method should return false.
   * @property {Function} isChecked - Optional function called to known if the button
   *                      is toggled or not. The function should return true when
   *                      the button should be displayed as toggled on.
   */
  _createButtonState: function (options) {
    let isCheckedValue = false;
    const {
      id,
      className,
      description,
      disabled,
      onClick,
      isInStartContainer,
      setup,
      teardown,
      isTargetSupported,
      isChecked,
      onKeyDown
    } = options;
    const toolbox = this;
    const button = {
      id,
      className,
      description,
      disabled,
      onClick(event) {
        if (typeof onClick == "function") {
          onClick(event, toolbox);
        }
      },
      onKeyDown(event) {
        if (typeof onKeyDown == "function") {
          onKeyDown(event, toolbox);
        }
      },
      isTargetSupported,
      get isChecked() {
        if (typeof isChecked == "function") {
          return isChecked(toolbox);
        }
        return isCheckedValue;
      },
      set isChecked(value) {
        // Note that if options.isChecked is given, this is ignored
        isCheckedValue = value;
        this.emit("updatechecked");
      },
      // The preference for having this button visible.
      visibilityswitch: `devtools.${id}.enabled`,
      // The toolbar has a container at the start and end of the toolbar for
      // holding buttons. By default the buttons are placed in the end container.
      isInStartContainer: !!isInStartContainer
    };
    if (typeof setup == "function") {
      let onChange = () => {
        button.emit("updatechecked");
      };
      setup(this, onChange);
      // Save a reference to the cleanup method that will unregister the onChange
      // callback. Immediately bind the function argument so that we don't have to
      // also save a reference to them.
      button.teardown = teardown.bind(options, this, onChange);
    }
    button.isVisible = this._commandIsVisible(button);

    EventEmitter.decorate(button);

    return button;
  },

  _buildOptions: function () {
    let selectOptions = event => {
      // Flip back to the last used panel if we are already
      // on the options panel.
      if (this.currentToolId === "options" &&
          gDevTools.getToolDefinition(this.lastUsedToolId)) {
        this.selectTool(this.lastUsedToolId);
      } else {
        this.selectTool("options");
      }
      // Prevent the opening of bookmarks window on toolbox.options.key
      event.preventDefault();
    };
    this.shortcuts.on(L10N.getStr("toolbox.options.key"), selectOptions);
    this.shortcuts.on(L10N.getStr("toolbox.help.key"), selectOptions);
  },

  _splitConsoleOnKeypress: function (e) {
    if (e.keyCode === KeyCodes.DOM_VK_ESCAPE) {
      this.toggleSplitConsole();
      // If the debugger is paused, don't let the ESC key stop any pending
      // navigation.
      if (this._threadClient.state == "paused") {
        e.preventDefault();
      }
    }
  },

  /**
   * Add a shortcut key that should work when a split console
   * has focus to the toolbox.
   *
   * @param {String} key
   *        The electron key shortcut.
   * @param {Function} handler
   *        The callback that should be called when the provided key shortcut is pressed.
   * @param {String} whichTool
   *        The tool the key belongs to. The corresponding handler will only be triggered
   *        if this tool is active.
   */
  useKeyWithSplitConsole: function (key, handler, whichTool) {
    this.shortcuts.on(key, event => {
      if (this.currentToolId === whichTool && this.isSplitConsoleFocused()) {
        handler();
        event.preventDefault();
      }
    });
  },

  _addReloadKeys: function () {
    [
      ["reload", false],
      ["reload2", false],
      ["forceReload", true],
      ["forceReload2", true]
    ].forEach(([id, force]) => {
      let key = L10N.getStr("toolbox." + id + ".key");
      this.shortcuts.on(key, event => {
        this.reloadTarget(force);

        // Prevent Firefox shortcuts from reloading the page
        event.preventDefault();
      });
    });
  },

  _addHostListeners: function () {
    this.shortcuts.on(L10N.getStr("toolbox.nextTool.key"),
                 event => {
                   this.selectNextTool();
                   event.preventDefault();
                 });
    this.shortcuts.on(L10N.getStr("toolbox.previousTool.key"),
                 event => {
                   this.selectPreviousTool();
                   event.preventDefault();
                 });
    this.shortcuts.on(L10N.getStr("toolbox.minimize.key"),
                 event => {
                   this._toggleMinimizeMode();
                   event.preventDefault();
                 });
    this.shortcuts.on(L10N.getStr("toolbox.toggleHost.key"),
                 event => {
                   this.switchToPreviousHost();
                   event.preventDefault();
                 });

    this.doc.addEventListener("keypress", this._splitConsoleOnKeypress);
    this.doc.addEventListener("focus", this._onFocus, true);
    this.win.addEventListener("unload", this.destroy);
    this.win.addEventListener("message", this._onBrowserMessage, true);
  },

  _removeHostListeners: function () {
    // The host iframe's contentDocument may already be gone.
    if (this.doc) {
      this.doc.removeEventListener("keypress", this._splitConsoleOnKeypress);
      this.doc.removeEventListener("focus", this._onFocus, true);
      this.win.removeEventListener("unload", this.destroy);
      this.win.removeEventListener("message", this._onBrowserMessage, true);
    }
  },

  // Called whenever the chrome send a message
  _onBrowserMessage: function (event) {
    if (!event.data) {
      return;
    }
    switch (event.data.name) {
      case "switched-host":
        this._onSwitchedHost(event.data);
        break;
      case "host-minimized":
        if (this.hostType == Toolbox.HostType.BOTTOM) {
          this._onBottomHostMinimized();
        }
        break;
      case "host-maximized":
        if (this.hostType == Toolbox.HostType.BOTTOM) {
          this._onBottomHostMaximized();
        }
        break;
    }
  },

  _registerOverlays: function () {
    registerHarOverlay(this);
  },

  _saveSplitConsoleHeight: function () {
    Services.prefs.setIntPref(SPLITCONSOLE_HEIGHT_PREF,
      this.webconsolePanel.height);
  },

  /**
   * Make sure that the console is showing up properly based on all the
   * possible conditions.
   *   1) If the console tab is selected, then regardless of split state
   *      it should take up the full height of the deck, and we should
   *      hide the deck and splitter.
   *   2) If the console tab is not selected and it is split, then we should
   *      show the splitter, deck, and console.
   *   3) If the console tab is not selected and it is *not* split,
   *      then we should hide the console and splitter, and show the deck
   *      at full height.
   */
  _refreshConsoleDisplay: function () {
    let deck = this.doc.getElementById("toolbox-deck");
    let webconsolePanel = this.webconsolePanel;
    let splitter = this.doc.getElementById("toolbox-console-splitter");
    let openedConsolePanel = this.currentToolId === "webconsole";

    if (openedConsolePanel) {
      deck.setAttribute("collapsed", "true");
      splitter.setAttribute("hidden", "true");
      webconsolePanel.removeAttribute("collapsed");
    } else {
      deck.removeAttribute("collapsed");
      if (this.splitConsole) {
        webconsolePanel.removeAttribute("collapsed");
        splitter.removeAttribute("hidden");
      } else {
        webconsolePanel.setAttribute("collapsed", "true");
        splitter.setAttribute("hidden", "true");
      }
    }
  },

  /**
   * Adds the keys and commands to the Toolbox Window in window mode.
   */
  _addKeysToWindow: function () {
    if (this.hostType != Toolbox.HostType.WINDOW) {
      return;
    }

    let doc = this.win.parent.document;

    for (let item of Startup.KeyShortcuts) {
      // KeyShortcuts contain tool-specific and global key shortcuts,
      // here we only need to copy shortcut specific to each tool.
      if (!item.toolId) {
        continue;
      }
      let { toolId, shortcut, modifiers } = item;

      let key = doc.createElement("key");

      key.id = "key_" + toolId;

      if (shortcut.startsWith("VK_")) {
        key.setAttribute("keycode", shortcut);
      } else {
        key.setAttribute("key", shortcut);
      }

      key.setAttribute("modifiers", modifiers);
      // needed. See bug 371900
      key.setAttribute("oncommand", "void(0);");
      key.addEventListener("command", () => {
        this.selectTool(toolId).then(() => this.fireCustomKey(toolId));
      }, true);
      doc.getElementById("toolbox-keyset").appendChild(key);
    }

    // Add key for toggling the browser console from the detached window
    if (!doc.getElementById("key_browserconsole")) {
      let key = doc.createElement("key");
      key.id = "key_browserconsole";

      key.setAttribute("key", L10N.getStr("browserConsoleCmd.commandkey"));
      key.setAttribute("modifiers", "accel,shift");
      // needed. See bug 371900
      key.setAttribute("oncommand", "void(0)");
      key.addEventListener("command", () => {
        HUDService.toggleBrowserConsole();
      }, true);
      doc.getElementById("toolbox-keyset").appendChild(key);
    }
  },

  /**
   * Handle any custom key events.  Returns true if there was a custom key
   * binding run.
   * @param {string} toolId Which tool to run the command on (skip if not
   * current)
   */
  fireCustomKey: function (toolId) {
    let toolDefinition = gDevTools.getToolDefinition(toolId);

    if (toolDefinition.onkey &&
        ((this.currentToolId === toolId) ||
          (toolId == "webconsole" && this.splitConsole))) {
      toolDefinition.onkey(this.getCurrentPanel(), this);
    }
  },

  /**
   * Build the notification box as soon as needed.
   */
  get notificationBox() {
    if (!this._notificationBox) {
      let { NotificationBox, PriorityLevels } =
        this.browserRequire(
          "devtools/client/shared/components/NotificationBox");

      NotificationBox = this.React.createFactory(NotificationBox);

      // Render NotificationBox and assign priority levels to it.
      let box = this.doc.getElementById("toolbox-notificationbox");
      this._notificationBox = Object.assign(
        this.ReactDOM.render(NotificationBox({}), box),
        PriorityLevels);
    }
    return this._notificationBox;
  },

  /**
   * Build the buttons for changing hosts. Called every time
   * the host changes.
   */
  _buildDockButtons: function () {
    if (!this._target.isLocalTab) {
      this.component.setDockButtonsEnabled(false);
      return;
    }

    // Bottom-type host can be minimized, add a button for this.
    if (this.hostType == Toolbox.HostType.BOTTOM) {
      this.component.setCanMinimize(true);

      // Maximize again when a tool gets selected.
      this.on("before-select", this._onToolSelectWhileMinimized);
      // Maximize and stop listening before the host type changes.
      this.once("host-will-change", this._onBottomHostWillChange);
    }

    this.component.setDockButtonsEnabled(true);
    this.component.setCanCloseToolbox(this.hostType !== Toolbox.HostType.WINDOW);

    let sideEnabled = Services.prefs.getBoolPref(this._prefs.SIDE_ENABLED);

    let hostTypes = [];
    for (let type in Toolbox.HostType) {
      let position = Toolbox.HostType[type];
      if (position == this.hostType ||
          position == Toolbox.HostType.CUSTOM ||
          (!sideEnabled && position == Toolbox.HostType.SIDE)) {
        continue;
      }

      hostTypes.push({
        position,
        switchHost: this.switchHost.bind(this, position)
      });
    }

    this.component.setHostTypes(hostTypes);
  },

  _onBottomHostMinimized: function () {
    this.component.setMinimizeState("minimized");
  },

  _onBottomHostMaximized: function () {
    this.component.setMinimizeState("maximized");
  },

  _onToolSelectWhileMinimized: function () {
    this.postMessage({
      name: "maximize-host"
    });
  },

  postMessage: function (msg) {
    // We sometime try to send messages in middle of destroy(), where the
    // toolbox iframe may already be detached and no longer have a parent.
    if (this.win.parent) {
      // Toolbox document is still chrome and disallow identifying message
      // origin via event.source as it is null. So use a custom id.
      msg.frameId = this.frameId;
      this.win.parent.postMessage(msg, "*");
    }
  },

  _onBottomHostWillChange: function () {
    this.postMessage({
      name: "maximize-host"
    });

    this.off("before-select", this._onToolSelectWhileMinimized);
  },

  _toggleMinimizeMode: function () {
    if (this.hostType !== Toolbox.HostType.BOTTOM) {
      return;
    }

    // Calculate the height to which the host should be minimized so the
    // tabbar is still visible.
    let toolbarHeight = this._componentMount.getBoxQuads({box: "content"})[0].bounds
                                                                             .height;
    this.postMessage({
      name: "toggle-minimize-mode",
      toolbarHeight
    });
  },

  /**
   * Initiate ToolboxTabs React component and all it's properties. Do the initial render.
   */
  _buildTabs: function () {
    // Get the initial list of tab definitions. This list can be amended at a later time
    // by tools registering themselves.
    const definitions = gDevTools.getToolDefinitionArray();
    definitions.forEach(definition => this._buildPanelForTool(definition));

    // Get the definitions that will only affect the main tab area.
    this.panelDefinitions = definitions.filter(definition =>
      definition.isTargetSupported(this._target) && definition.id !== "options");

    this.optionsDefinition = definitions.find(({id}) => id === "options");
    // The options tool is treated slightly differently, and is in a different area.
    this.component.setOptionsPanel(definitions.find(({id}) => id === "options"));
  },

  _mountReactComponent: function () {
    // Ensure the toolbar doesn't try to render until the tool is ready.
    const element = this.React.createElement(this.ToolboxController, {
      L10N,
      currentToolId: this.currentToolId,
      selectTool: this.selectTool,
      closeToolbox: this.destroy,
      focusButton: this._onToolbarFocus,
      toggleMinimizeMode: this._toggleMinimizeMode,
      toolbox: this
    });

    this.component = this.ReactDOM.render(element, this._componentMount);
  },

  /**
   * Reset tabindex attributes across all focusable elements inside the toolbar.
   * Only have one element with tabindex=0 at a time to make sure that tabbing
   * results in navigating away from the toolbar container.
   * @param  {FocusEvent} event
   */
  _onToolbarFocus: function (id) {
    this.component.setFocusedButton(id);
  },

  /**
   * On left/right arrow press, attempt to move the focus inside the toolbar to
   * the previous/next focusable element. This is not in the React component
   * as it is difficult to coordinate between different component elements.
   * The components are responsible for setting the correct tabindex value
   * for if they are the focused element.
   * @param  {KeyboardEvent} event
   */
  _onToolbarArrowKeypress: function (event) {
    let { key, target, ctrlKey, shiftKey, altKey, metaKey } = event;

    // If any of the modifier keys are pressed do not attempt navigation as it
    // might conflict with global shortcuts (Bug 1327972).
    if (ctrlKey || shiftKey || altKey || metaKey) {
      return;
    }

    let buttons = [...this._componentMount.querySelectorAll("button")];
    let curIndex = buttons.indexOf(target);

    if (curIndex === -1) {
      console.warn(target + " is not found among Developer Tools tab bar " +
        "focusable elements.");
      return;
    }

    let newTarget;

    if (key === "ArrowLeft") {
      // Do nothing if already at the beginning.
      if (curIndex === 0) {
        return;
      }
      newTarget = buttons[curIndex - 1];
    } else if (key === "ArrowRight") {
      // Do nothing if already at the end.
      if (curIndex === buttons.length - 1) {
        return;
      }
      newTarget = buttons[curIndex + 1];
    } else {
      return;
    }

    newTarget.focus();

    event.preventDefault();
    event.stopPropagation();
  },

  /**
   * Add buttons to the UI as specified in devtools/client/definitions.js
   */
  _buildButtons: Task.async(function* () {
    // Beyond the normal preference filtering
    this.toolbarButtons = [
      this._buildPickerButton(),
      this._buildFrameButton(),
      yield this._buildNoAutoHideButton()
    ];

    ToolboxButtons.forEach(definition => {
      let button = this._createButtonState(definition);
      this.toolbarButtons.push(button);
    });

    this.component.setToolboxButtons(this.toolbarButtons);
  }),

  /**
   * Button to select a frame for the inspector to target.
   */
  _buildFrameButton() {
    this.frameButton = this._createButtonState({
      id: "command-button-frames",
      description: L10N.getStr("toolbox.frames.tooltip"),
      onClick: this.showFramesMenu,
      isTargetSupported: target => {
        return target.activeTab && target.activeTab.traits.frames;
      },
      onKeyDown: this.handleKeyDownOnFramesButton
    });

    return this.frameButton;
  },

  /**
   * Button that disables/enables auto-hiding XUL pop-ups. When enabled, XUL
   * pop-ups will not automatically close when they lose focus.
   */
  _buildNoAutoHideButton: Task.async(function* () {
    this.autohideButton = this._createButtonState({
      id: "command-button-noautohide",
      description: L10N.getStr("toolbox.noautohide.tooltip"),
      onClick: this._toggleNoAutohide,
      isTargetSupported: target => target.chrome
    });

    this._isDisableAutohideEnabled().then(enabled => {
      this.autohideButton.isChecked = enabled;
    });

    return this.autohideButton;
  }),

  /**
   * Toggle the picker, but also decide whether or not the highlighter should
   * focus the window. This is only desirable when the toolbox is mounted to the
   * window. When devtools is free floating, then the target window should not
   * pop in front of the viewer when the picker is clicked.
   *
   * Note: Toggle picker can be overwritten by panel other than the inspector to
   * allow for custom picker behaviour.
   */
  _onPickerClick: function () {
    let focus = this.hostType === Toolbox.HostType.BOTTOM ||
                this.hostType === Toolbox.HostType.SIDE;
    let currentPanel = this.getCurrentPanel();
    if (currentPanel.togglePicker) {
      currentPanel.togglePicker(focus);
    } else {
      this.highlighterUtils.togglePicker(focus);
    }
  },

  /**
   * If the picker is activated, then allow the Escape key to deactivate the
   * functionality instead of the default behavior of toggling the console.
   */
  _onPickerKeypress: function (event) {
    if (event.keyCode === KeyCodes.DOM_VK_ESCAPE) {
      let currentPanel = this.getCurrentPanel();
      if (currentPanel.cancelPicker) {
        currentPanel.cancelPicker();
      } else {
        this.highlighterUtils.cancelPicker();
      }
      // Stop the console from toggling.
      event.stopImmediatePropagation();
    }
  },

  _onPickerStarted: function () {
    this.doc.addEventListener("keypress", this._onPickerKeypress, true);
  },

  _onPickerStopped: function () {
    this.doc.removeEventListener("keypress", this._onPickerKeypress, true);
  },

  /**
   * The element picker button enables the ability to select a DOM node by clicking
   * it on the page.
   */
  _buildPickerButton() {
    this.pickerButton = this._createButtonState({
      id: "command-button-pick",
      description: L10N.getStr("pickButton.tooltip"),
      onClick: this._onPickerClick,
      isInStartContainer: true,
      isTargetSupported: target => {
        return target.activeTab && target.activeTab.traits.frames;
      }
    });

    return this.pickerButton;
  },

  /**
   * Apply the current cache setting from devtools.cache.disabled to this
   * toolbox's tab.
   */
  _applyCacheSettings: function () {
    let pref = "devtools.cache.disabled";
    let cacheDisabled = Services.prefs.getBoolPref(pref);

    if (this.target.activeTab) {
      this.target.activeTab.reconfigure({"cacheDisabled": cacheDisabled});
    }
  },

  /**
   * Apply the current service workers testing setting from
   * devtools.serviceWorkers.testing.enabled to this toolbox's tab.
   */
  _applyServiceWorkersTestingSettings: function () {
    let pref = "devtools.serviceWorkers.testing.enabled";
    let serviceWorkersTestingEnabled =
      Services.prefs.getBoolPref(pref) || false;

    if (this.target.activeTab) {
      this.target.activeTab.reconfigure({
        "serviceWorkersTestingEnabled": serviceWorkersTestingEnabled
      });
    }
  },

 /**
  * Return all toolbox buttons (command buttons, plus any others that were
  * added manually).

  /**
   * Update the visibility of the buttons.
   */
  updateToolboxButtonsVisibility() {
    this.toolbarButtons.forEach(button => {
      button.isVisible = this._commandIsVisible(button);
    });
    this.component.setToolboxButtons(this.toolbarButtons);
  },

  /**
   * Visually update picker button.
   * This function is called on every "select" event. Newly selected panel can
   * update the visual state of the picker button such as disabled state,
   * additional CSS classes (className), and tooltip (description).
   */
  _updatePickerButton() {
    const button = this.pickerButton;
    let currentPanel = this.getCurrentPanel();

    if (currentPanel && currentPanel.updatePickerButton) {
      currentPanel.updatePickerButton();
    } else {
      // If the current panel doesn't define a custom updatePickerButton,
      // revert the button to its default state
      button.description = L10N.getStr("pickButton.tooltip");
      button.className = null;
      button.disabled = null;
    }

    this.component.setToolboxButtons(this.toolbarButtons);
  },

  /**
   * Ensure the visibility of each toolbox button matches the preference value.
   */
  _commandIsVisible: function (button) {
    const {
      isTargetSupported,
      visibilityswitch
    } = button;

    let visible = Services.prefs.getBoolPref(visibilityswitch, true);

    if (isTargetSupported) {
      return visible && isTargetSupported(this.target);
    }

    return visible;
  },

  /**
   * Build a panel for a tool definition.
   *
   * @param {string} toolDefinition
   *        Tool definition of the tool to build a tab for.
   */
  _buildPanelForTool: function (toolDefinition) {
    if (!toolDefinition.isTargetSupported(this._target)) {
      return;
    }

    let deck = this.doc.getElementById("toolbox-deck");
    let id = toolDefinition.id;

    if (toolDefinition.ordinal == undefined || toolDefinition.ordinal < 0) {
      toolDefinition.ordinal = MAX_ORDINAL;
    }

    if (!toolDefinition.bgTheme) {
      toolDefinition.bgTheme = "theme-toolbar";
    }
    let panel = this.doc.createElement("vbox");
    panel.className = "toolbox-panel " + toolDefinition.bgTheme;

    // There is already a container for the webconsole frame.
    if (!this.doc.getElementById("toolbox-panel-" + id)) {
      panel.id = "toolbox-panel-" + id;
    }

    deck.appendChild(panel);

    this._addKeysToWindow();
  },

  /**
   * Lazily created map of the additional tools registered to this toolbox.
   *
   * @returns {Map<string, object>}
   *          a map of the tools definitions registered to this
   *          particular toolbox (the key is the toolId string, the value
   *          is the tool definition plain javascript object).
   */
  get additionalToolDefinitions() {
    if (!this._additionalToolDefinitions) {
      this._additionalToolDefinitions = new Map();
    }

    return this._additionalToolDefinitions;
  },

  /**
   * Retrieve the array of the additional tools registered to this toolbox.
   *
   * @return {Array<object>}
   *         the array of additional tool definitions registered on this toolbox.
   */
  getAdditionalTools() {
    if (this._additionalToolDefinitions) {
      return Array.from(this.additionalToolDefinitions.values());
    }
    return [];
  },

  /**
   * Get the additional tools that have been registered and are visible.
   *
   * @return {Array<object>}
   *         the array of additional tool definitions registered on this toolbox.
   */
  getVisibleAdditionalTools() {
    return this.visibleAdditionalTools
               .map(toolId => this.additionalToolDefinitions.get(toolId));
  },

  /**
   * Test the existence of a additional tools registered to this toolbox by tool id.
   *
   * @param {string} toolId
   *        the id of the tool to test for existence.
   *
   * @return {boolean}
   *
   */
  hasAdditionalTool(toolId) {
    return this.additionalToolDefinitions.has(toolId);
  },

  /**
   * Register and load an additional tool on this particular toolbox.
   *
   * @param {object} definition
   *        the additional tool definition to register and add to this toolbox.
   */
  addAdditionalTool(definition) {
    if (!definition.id) {
      throw new Error("Tool definition id is missing");
    }

    if (this.isToolRegistered(definition.id)) {
      throw new Error("Tool definition already registered: " +
                      definition.id);
    }

    this.additionalToolDefinitions.set(definition.id, definition);
    this.visibleAdditionalTools = [...this.visibleAdditionalTools, definition.id];

    const buildPanel = () => this._buildPanelForTool(definition);

    if (this.isReady) {
      buildPanel();
    } else {
      this.once("ready", buildPanel);
    }
  },

  /**
   * Retrieve the registered inspector extension sidebars
   * (used by the inspector panel during its deferred initialization).
   */
  get inspectorExtensionSidebars() {
    return this._inspectorExtensionSidebars;
  },

  /**
   * Register an extension sidebar for the inspector panel.
   *
   * @param {String} id
   *        An unique sidebar id
   * @param {Object} options
   * @param {String} options.title
   *        A title for the sidebar
   */
  async registerInspectorExtensionSidebar(id, options) {
    this._inspectorExtensionSidebars.set(id, options);

    // Defer the extension sidebar creation if the inspector
    // has not been created yet (and do not create the inspector
    // only to register an extension sidebar).
    if (!this._inspector) {
      return;
    }

    const inspector = this.getPanel("inspector");
    inspector.addExtensionSidebar(id, options);
  },

  /**
   * Unregister an extension sidebar for the inspector panel.
   *
   * @param {String} id
   *        An unique sidebar id
   */
  unregisterInspectorExtensionSidebar(id) {
    const sidebarDef = this._inspectorExtensionSidebars.get(id);
    if (!sidebarDef) {
      return;
    }

    this._inspectorExtensionSidebars.delete(id);

    // Remove the created sidebar instance if the inspector panel
    // has been already created.
    if (!this._inspector) {
      return;
    }

    const inspector = this.getPanel("inspector");
    inspector.removeExtensionSidebar(id);
  },

  /**
   * Unregister and unload an additional tool from this particular toolbox.
   *
   * @param {string} toolId
   *        the id of the additional tool to unregister and remove.
   */
  removeAdditionalTool(toolId) {
    // Early exit if the toolbox is already destroying itself.
    if (this._destroyer) {
      return;
    }

    if (!this.hasAdditionalTool(toolId)) {
      throw new Error("Tool definition not registered to this toolbox: " +
                      toolId);
    }

    this.additionalToolDefinitions.delete(toolId);
    this.visibleAdditionalTools = this.visibleAdditionalTools
                                      .filter(id => id !== toolId);
    this.unloadTool(toolId);
  },

  /**
   * Ensure the tool with the given id is loaded.
   *
   * @param {string} id
   *        The id of the tool to load.
   */
  loadTool: function (id) {
    if (id === "inspector" && !this._inspector) {
      return this.initInspector().then(() => this.loadTool(id));
    }

    let deferred = defer();
    let iframe = this.doc.getElementById("toolbox-panel-iframe-" + id);

    if (iframe) {
      let panel = this._toolPanels.get(id);
      if (panel) {
        deferred.resolve(panel);
      } else {
        this.once(id + "-ready", initializedPanel => {
          deferred.resolve(initializedPanel);
        });
      }
      return deferred.promise;
    }

    // Retrieve the tool definition (from the global or the per-toolbox tool maps)
    let definition = this.getToolDefinition(id);

    if (!definition) {
      deferred.reject(new Error("no such tool id " + id));
      return deferred.promise;
    }

    iframe = this.doc.createElement("iframe");
    iframe.className = "toolbox-panel-iframe";
    iframe.id = "toolbox-panel-iframe-" + id;
    iframe.setAttribute("flex", 1);
    iframe.setAttribute("forceOwnRefreshDriver", "");
    iframe.tooltip = "aHTMLTooltip";
    iframe.style.visibility = "hidden";

    gDevTools.emit(id + "-init", this, iframe);
    this.emit(id + "-init", iframe);

    // If no parent yet, append the frame into default location.
    if (!iframe.parentNode) {
      let vbox = this.doc.getElementById("toolbox-panel-" + id);
      vbox.appendChild(iframe);
      vbox.visibility = "visible";
    }

    let onLoad = () => {
      // Prevent flicker while loading by waiting to make visible until now.
      iframe.style.visibility = "visible";

      // Try to set the dir attribute as early as possible.
      this.setIframeDocumentDir(iframe);

      // The build method should return a panel instance, so events can
      // be fired with the panel as an argument. However, in order to keep
      // backward compatibility with existing extensions do a check
      // for a promise return value.
      let built = definition.build(iframe.contentWindow, this);

      if (!(typeof built.then == "function")) {
        let panel = built;
        iframe.panel = panel;

        // The panel instance is expected to fire (and listen to) various
        // framework events, so make sure it's properly decorated with
        // appropriate API (on, off, once, emit).
        // In this case we decorate panel instances directly returned by
        // the tool definition 'build' method.
        if (typeof panel.emit == "undefined") {
          EventEmitter.decorate(panel);
        }

        gDevTools.emit(id + "-build", this, panel);
        this.emit(id + "-build", panel);

        // The panel can implement an 'open' method for asynchronous
        // initialization sequence.
        if (typeof panel.open == "function") {
          built = panel.open();
        } else {
          let buildDeferred = defer();
          buildDeferred.resolve(panel);
          built = buildDeferred.promise;
        }
      }

      // Wait till the panel is fully ready and fire 'ready' events.
      promise.resolve(built).then((panel) => {
        this._toolPanels.set(id, panel);

        // Make sure to decorate panel object with event API also in case
        // where the tool definition 'build' method returns only a promise
        // and the actual panel instance is available as soon as the
        // promise is resolved.
        if (typeof panel.emit == "undefined") {
          EventEmitter.decorate(panel);
        }

        gDevTools.emit(id + "-ready", this, panel);
        this.emit(id + "-ready", panel);

        deferred.resolve(panel);
      }, console.error);
    };

    iframe.setAttribute("src", definition.url);
    if (definition.panelLabel) {
      iframe.setAttribute("aria-label", definition.panelLabel);
    }

    // Depending on the host, iframe.contentWindow is not always
    // defined at this moment. If it is not defined, we use an
    // event listener on the iframe DOM node. If it's defined,
    // we use the chromeEventHandler. We can't use a listener
    // on the DOM node every time because this won't work
    // if the (xul chrome) iframe is loaded in a content docshell.
    if (iframe.contentWindow) {
      let domHelper = new DOMHelpers(iframe.contentWindow);
      domHelper.onceDOMReady(onLoad);
    } else {
      let callback = () => {
        iframe.removeEventListener("DOMContentLoaded", callback);
        onLoad();
      };

      iframe.addEventListener("DOMContentLoaded", callback);
    }

    return deferred.promise;
  },

  /**
   * Set the dir attribute on the content document element of the provided iframe.
   *
   * @param {IFrameElement} iframe
   */
  setIframeDocumentDir: function (iframe) {
    let docEl = iframe.contentWindow && iframe.contentWindow.document.documentElement;
    if (!docEl || docEl.namespaceURI !== HTML_NS) {
      // Bail out if the content window or document is not ready or if the document is not
      // HTML.
      return;
    }

    if (docEl.hasAttribute("dir")) {
      // Set the dir attribute value only if dir is already present on the document.
      docEl.setAttribute("dir", this.direction);
    }
  },

  /**
   * Mark all in collection as unselected; and id as selected
   * @param {string} collection
   *        DOM collection of items
   * @param {string} id
   *        The Id of the item within the collection to select
   */
  selectSingleNode: function (collection, id) {
    [...collection].forEach(node => {
      if (node.id === id) {
        node.setAttribute("selected", "true");
        node.setAttribute("aria-selected", "true");
      } else {
        node.removeAttribute("selected");
        node.removeAttribute("aria-selected");
      }
      // The webconsole panel is in a special location due to split console
      if (!node.id) {
        node = this.webconsolePanel;
      }

      let iframe = node.querySelector(".toolbox-panel-iframe");
      if (iframe) {
        let visible = node.id == id;
        // Prevents hiding the split-console if it is currently enabled
        if (node == this.webconsolePanel && this.splitConsole) {
          visible = true;
        }
        this.setIframeVisible(iframe, visible);
      }
    });
  },

  /**
   * Make a privileged iframe visible/hidden.
   *
   * For now, XUL Iframes loading chrome documents (i.e. <iframe type!="content" />)
   * can't be hidden at platform level. And so don't support 'visibilitychange' event.
   *
   * This helper workarounds that by at least being able to send these kind of events.
   * It will help panel react differently depending on them being displayed or in
   * background.
   */
  setIframeVisible: function (iframe, visible) {
    let state = visible ? "visible" : "hidden";
    let win = iframe.contentWindow;
    let doc = win.document;
    if (doc.visibilityState != state) {
      // 1) Overload document's `visibilityState` attribute
      // Use defineProperty, as by default `document.visbilityState` is read only.
      Object.defineProperty(doc, "visibilityState", { value: state, configurable: true });

      // 2) Fake the 'visibilitychange' event
      win.dispatchEvent(new win.Event("visibilitychange"));
    }
  },

  /**
   * Switch to the tool with the given id
   *
   * @param {string} id
   *        The id of the tool to switch to
   */
  selectTool: function (id) {
    this.emit("before-select", id);

    if (this.currentToolId == id) {
      let panel = this._toolPanels.get(id);
      if (panel) {
        // We have a panel instance, so the tool is already fully loaded.

        // re-focus tool to get key events again
        this.focusTool(id);

        // Return the existing panel in order to have a consistent return value.
        return promise.resolve(panel);
      }
      // Otherwise, if there is no panel instance, it is still loading,
      // so we are racing another call to selectTool with the same id.
      return this.once("select").then(() => promise.resolve(this._toolPanels.get(id)));
    }

    if (!this.isReady) {
      throw new Error("Can't select tool, wait for toolbox 'ready' event");
    }

    // Check if the tool exists.
    if (this.panelDefinitions.find((definition) => definition.id === id) ||
        id === "options" ||
        this.additionalToolDefinitions.get(id)) {
      if (this.currentToolId) {
        this._telemetry.toolClosed(this.currentToolId);
      }
      this._telemetry.toolOpened(id);
    } else {
      throw new Error("No tool found");
    }

    // and select the right iframe
    let toolboxPanels = this.doc.querySelectorAll(".toolbox-panel");
    this.selectSingleNode(toolboxPanels, "toolbox-panel-" + id);

    this.lastUsedToolId = this.currentToolId;
    this.currentToolId = id;
    this._refreshConsoleDisplay();
    if (id != "options") {
      Services.prefs.setCharPref(this._prefs.LAST_TOOL, id);
    }

    return this.loadTool(id).then(panel => {
      // focus the tool's frame to start receiving key events
      this.focusTool(id);

      this.emit("select", id);
      this.emit(id + "-selected", panel);
      return panel;
    });
  },

  /**
   * Focus a tool's panel by id
   * @param  {string} id
   *         The id of tool to focus
   */
  focusTool: function (id, state = true) {
    let iframe = this.doc.getElementById("toolbox-panel-iframe-" + id);

    if (state) {
      iframe.focus();
    } else {
      iframe.blur();
    }
  },

  /**
   * Focus split console's input line
   */
  focusConsoleInput: function () {
    let consolePanel = this.getPanel("webconsole");
    if (consolePanel) {
      consolePanel.focusInput();
    }
  },

  /**
   * If the console is split and we are focusing an element outside
   * of the console, then store the newly focused element, so that
   * it can be restored once the split console closes.
   */
  _onFocus: function ({originalTarget}) {
    // Ignore any non element nodes, or any elements contained
    // within the webconsole frame.
    let webconsoleURL = gDevTools.getToolDefinition("webconsole").url;
    if (originalTarget.nodeType !== 1 ||
        originalTarget.baseURI === webconsoleURL) {
      return;
    }

    this._lastFocusedElement = originalTarget;
  },

  /**
   * Opens the split console.
   *
   * @returns {Promise} a promise that resolves once the tool has been
   *          loaded and focused.
   */
  openSplitConsole: function () {
    this._splitConsole = true;
    Services.prefs.setBoolPref(SPLITCONSOLE_ENABLED_PREF, true);
    this._refreshConsoleDisplay();

    // Ensure split console is visible if console was already loaded in background
    let iframe = this.webconsolePanel.querySelector(".toolbox-panel-iframe");
    if (iframe) {
      this.setIframeVisible(iframe, true);
    }

    return this.loadTool("webconsole").then(() => {
      this.emit("split-console");
      this.focusConsoleInput();
    });
  },

  /**
   * Closes the split console.
   *
   * @returns {Promise} a promise that resolves once the tool has been
   *          closed.
   */
  closeSplitConsole: function () {
    this._splitConsole = false;
    Services.prefs.setBoolPref(SPLITCONSOLE_ENABLED_PREF, false);
    this._refreshConsoleDisplay();
    this.emit("split-console");

    if (this._lastFocusedElement) {
      this._lastFocusedElement.focus();
    }
    return promise.resolve();
  },

  /**
   * Toggles the split state of the webconsole.  If the webconsole panel
   * is already selected then this command is ignored.
   *
   * @returns {Promise} a promise that resolves once the tool has been
   *          opened or closed.
   */
  toggleSplitConsole: function () {
    if (this.currentToolId !== "webconsole") {
      return this.splitConsole ?
             this.closeSplitConsole() :
             this.openSplitConsole();
    }

    return promise.resolve();
  },

  /**
   * Tells the target tab to reload.
   */
  reloadTarget: function (force) {
    this.target.activeTab.reload({ force: force });
  },

  /**
   * Loads the tool next to the currently selected tool.
   */
  selectNextTool: function () {
    let definitions = this.component.panelDefinitions;
    const index = definitions.findIndex(({id}) => id === this.currentToolId);
    let definition = definitions[index + 1];
    if (!definition) {
      definition = index === -1 ? definitions[0] : this.optionsDefinition;
    }
    return this.selectTool(definition.id);
  },

  /**
   * Loads the tool just left to the currently selected tool.
   */
  selectPreviousTool: function () {
    let definitions = this.component.panelDefinitions;
    const index = definitions.findIndex(({id}) => id === this.currentToolId);
    let definition = definitions[index - 1];
    if (!definition) {
      definition = index === -1
        ? definitions[definitions.length - 1]
        : this.optionsDefinition;
    }
    return this.selectTool(definition.id);
  },

  /**
   * Highlights the tool's tab if it is not the currently selected tool.
   *
   * @param {string} id
   *        The id of the tool to highlight
   */
  highlightTool: Task.async(function* (id) {
    if (!this.component) {
      yield this.isOpen;
    }
    this.component.highlightTool(id);
  }),

  /**
   * De-highlights the tool's tab.
   *
   * @param {string} id
   *        The id of the tool to unhighlight
   */
  unhighlightTool: Task.async(function* (id) {
    if (!this.component) {
      yield this.isOpen;
    }
    this.component.unhighlightTool(id);
  }),

  /**
   * Raise the toolbox host.
   */
  raise: function () {
    this.postMessage({
      name: "raise-host"
    });
  },

  /**
   * Fired when user just started navigating away to another web page.
   */
  async _onWillNavigate() {
    let toolId = this.currentToolId;
    // For now, only inspector, webconsole and netmonitor fire "reloaded" event
    if (toolId != "inspector" && toolId != "webconsole" && toolId != "netmonitor") {
      return;
    }

    let start = this.win.performance.now();
    let panel = this.getPanel(toolId);
    // Ignore the timing if the panel is still loading
    if (!panel) {
      return;
    }
    await panel.once("reloaded");
    let delay = this.win.performance.now() - start;

    let telemetryKey = "DEVTOOLS_TOOLBOX_PAGE_RELOAD_DELAY_MS";
    let histogram = Services.telemetry.getKeyedHistogramById(telemetryKey);
    histogram.add(toolId, delay);
  },

  /**
   * Refresh the host's title.
   */
  _refreshHostTitle: function () {
    let title;
    if (this.target.name && this.target.name != this.target.url) {
      const url = this.target.isWebExtension ?
                  this.target.getExtensionPathName(this.target.url) : this.target.url;
      title = L10N.getFormatStr("toolbox.titleTemplate2", this.target.name,
                                                          url);
    } else {
      title = L10N.getFormatStr("toolbox.titleTemplate1", this.target.url);
    }
    this.postMessage({
      name: "set-host-title",
      title
    });
  },

  // Returns an instance of the preference actor
  get preferenceFront() {
    if (this._preferenceFront) {
      return Promise.resolve(this._preferenceFront);
    }
    return this.isOpen.then(() => {
      return this.target.root.then(rootForm => {
        let front = getPreferenceFront(this.target.client, rootForm);
        this._preferenceFront = front;
        return front;
      });
    });
  },

  _toggleNoAutohide: Task.async(function* () {
    let front = yield this.preferenceFront;
    let toggledValue = !(yield this._isDisableAutohideEnabled());

    front.setBoolPref(DISABLE_AUTOHIDE_PREF, toggledValue);

    this.autohideButton.isChecked = toggledValue;
    this._autohideHasBeenToggled = true;
  }),

  _isDisableAutohideEnabled: Task.async(function* () {
    // Ensure that the tools are open, and the button is visible.
    yield this.isOpen;
    if (!this.autohideButton.isVisible) {
      return false;
    }

    let prefFront = yield this.preferenceFront;
    return yield prefFront.getBoolPref(DISABLE_AUTOHIDE_PREF);
  }),

  _listFrames: function (event) {
    if (!this._target.activeTab || !this._target.activeTab.traits.frames) {
      // We are not targetting a regular TabActor
      // it can be either an addon or browser toolbox actor
      return promise.resolve();
    }
    let packet = {
      to: this._target.form.actor,
      type: "listFrames"
    };
    return this._target.client.request(packet, resp => {
      this._updateFrames(null, { frames: resp.frames });
    });
  },

  /**
   * Show a drop down menu that allows the user to switch frames.
   */
  showFramesMenu: async function (event) {
    let menu = new Menu();
    let target = event.target;

    // Need to initInspector to check presence of getNodeActorFromWindowID
    // and use the highlighter later
    await this.initInspector();
    if (!("_supportsFrameHighlight" in this)) {
    // Only works with FF58+ targets
      this._supportsFrameHighlight =
        await this.target.actorHasMethod("domwalker", "getNodeActorFromWindowID");
    }

    // Generate list of menu items from the list of frames.
    this.frameMap.forEach(frame => {
      // A frame is checked if it's the selected one.
      let checked = frame.id == this.selectedFrameId;

      let label = frame.url;

      if (this.target.isWebExtension) {
        // Show a shorter url for extensions page.
        label = this.target.getExtensionPathName(frame.url);
      }

      // Create menu item.
      menu.append(new MenuItem({
        label,
        type: "radio",
        checked,
        click: () => {
          this.onSelectFrame(frame.id);
        },
        hover: () => {
          this.onHightlightFrame(frame.id);
        }
      }));
    });

    menu.once("open").then(() => {
      this.frameButton.isChecked = true;
    });

    menu.once("close").then(() => {
      this.frameButton.isChecked = false;
      this.highlighterUtils.unhighlight();
    });

    // Show a drop down menu with frames.
    // XXX Missing menu API for specifying target (anchor)
    // and relative position to it. See also:
    // https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL/Method/openPopup
    // https://bugzilla.mozilla.org/show_bug.cgi?id=1274551
    let rect = target.getBoundingClientRect();
    let screenX = target.ownerDocument.defaultView.mozInnerScreenX;
    let screenY = target.ownerDocument.defaultView.mozInnerScreenY;
    menu.popup(rect.left + screenX, rect.bottom + screenY, this);

    return menu;
  },

  /**
   * Handle keyDown event on 'frames' button to show available frames
   */
  handleKeyDownOnFramesButton: function (event) {
    this.shortcuts.on(L10N.getStr("toolbox.showFrames.key"),
      this.showFramesMenuOnKeyDown);
  },

  /**
   * Show 'frames' menu on key down
   */
  showFramesMenuOnKeyDown: function (event) {
    if (event.target.id == "command-button-frames") {
      this.showFramesMenu(event);
    }
  },

  /**
   * Select a frame by sending 'switchToFrame' packet to the backend.
   */
  onSelectFrame: function (frameId) {
    // Send packet to the backend to select specified frame and
    // wait for 'frameUpdate' event packet to update the UI.
    let packet = {
      to: this._target.form.actor,
      type: "switchToFrame",
      windowId: frameId
    };
    this._target.client.request(packet);
  },

  /**
   * Highlight a frame in the page
   */
  onHightlightFrame: async function (frameId) {
    // Only enable frame highlighting when the top level document is targeted
    if (this._supportsFrameHighlight && this.rootFrameSelected) {
      let frameActor = await this.walker.getNodeActorFromWindowID(frameId);
      this.highlighterUtils.highlightNodeFront(frameActor);
    }
  },

  /**
   * A handler for 'frameUpdate' packets received from the backend.
   * Following properties might be set on the packet:
   *
   * destroyAll {Boolean}: All frames have been destroyed.
   * selected {Number}: A frame has been selected
   * frames {Array}: list of frames. Every frame can have:
   *                 id {Number}: frame ID
   *                 url {String}: frame URL
   *                 title {String}: frame title
   *                 destroy {Boolean}: Set to true if destroyed
   *                 parentID {Number}: ID of the parent frame (not set
   *                                    for top level window)
   */
  _updateFrames: function (event, data) {
    if (!Services.prefs.getBoolPref("devtools.command-button-frames.enabled")) {
      return;
    }

    // We may receive this event before the toolbox is ready.
    if (!this.isReady) {
      return;
    }

    // Store (synchronize) data about all existing frames on the backend
    if (data.destroyAll) {
      this.frameMap.clear();
      this.selectedFrameId = null;
    } else if (data.selected) {
      this.selectedFrameId = data.selected;
    } else if (data.frames) {
      data.frames.forEach(frame => {
        if (frame.destroy) {
          this.frameMap.delete(frame.id);

          // Reset the currently selected frame if it's destroyed.
          if (this.selectedFrameId == frame.id) {
            this.selectedFrameId = null;
          }
        } else {
          this.frameMap.set(frame.id, frame);
        }
      });
    }

    // If there is no selected frame select the first top level
    // frame by default. Note that there might be more top level
    // frames in case of the BrowserToolbox.
    if (!this.selectedFrameId) {
      let frames = [...this.frameMap.values()];
      let topFrames = frames.filter(frame => !frame.parentID);
      this.selectedFrameId = topFrames.length ? topFrames[0].id : null;
    }

    // Check out whether top frame is currently selected.
    // Note that only child frame has parentID.
    let frame = this.frameMap.get(this.selectedFrameId);
    let topFrameSelected = frame ? !frame.parentID : false;
    this._framesButtonChecked = false;

    // If non-top level frame is selected the toolbar button is
    // marked as 'checked' indicating that a child frame is active.
    if (!topFrameSelected && this.selectedFrameId) {
      this._framesButtonChecked = false;
    }
  },

  /**
   * Returns a 0-based selected frame depth.
   *
   * For example, if the root frame is selected, the returned value is 0.  For a sub-frame
   * of the root document, the returned value is 1, and so on.
   */
  get selectedFrameDepth() {
    // If the frame switcher is disabled, we won't have a selected frame ID.
    // In this case, we're always showing the root frame.
    if (!this.selectedFrameId) {
      return 0;
    }
    let depth = 0;
    let frame = this.frameMap.get(this.selectedFrameId);
    while (frame) {
      depth++;
      frame = this.frameMap.get(frame.parentID);
    }
    return depth - 1;
  },

  /**
   * Returns whether a root frame (with no parent frame) is selected.
   */
  get rootFrameSelected() {
    return this.selectedFrameDepth == 0;
  },

  /**
   * Switch to the last used host for the toolbox UI.
   */
  switchToPreviousHost: function () {
    return this.switchHost("previous");
  },

  /**
   * Switch to a new host for the toolbox UI. E.g. bottom, sidebar, window,
   * and focus the window when done.
   *
   * @param {string} hostType
   *        The host type of the new host object
   */
  switchHost: function (hostType) {
    if (hostType == this.hostType || !this._target.isLocalTab) {
      return null;
    }

    this.emit("host-will-change", hostType);

    // ToolboxHostManager is going to call swapFrameLoaders which mess up with
    // focus. We have to blur before calling it in order to be able to restore
    // the focus after, in _onSwitchedHost.
    this.focusTool(this.currentToolId, false);

    // Host code on the chrome side will send back a message once the host
    // switched
    this.postMessage({
      name: "switch-host",
      hostType
    });

    return this.once("host-changed");
  },

  _onSwitchedHost: function ({ hostType }) {
    this._hostType = hostType;

    this._buildDockButtons();
    this._addKeysToWindow();

    // We blurred the tools at start of switchHost, but also when clicking on
    // host switching button. We now have to restore the focus.
    this.focusTool(this.currentToolId, true);

    this.emit("host-changed");
    this._telemetry.log(HOST_HISTOGRAM, this._getTelemetryHostId());
  },

  /**
   * Test the availability of a tool (both globally registered tools and
   * additional tools registered to this toolbox) by tool id.
   *
   * @param  {string} toolId
   *         Id of the tool definition to search in the per-toolbox or globally
   *         registered tools.
   *
   * @returns {bool}
   *         Returns true if the tool is registered globally or on this toolbox.
   */
  isToolRegistered: function (toolId) {
    return !!this.getToolDefinition(toolId);
  },

  /**
   * Return the tool definition registered globally or additional tools registered
   * to this toolbox.
   *
   * @param  {string} toolId
   *         Id of the tool definition to retrieve for the per-toolbox and globally
   *         registered tools.
   *
   * @returns {object}
   *         The plain javascript object that represents the requested tool definition.
   */
  getToolDefinition: function (toolId) {
    return gDevTools.getToolDefinition(toolId) ||
      this.additionalToolDefinitions.get(toolId);
  },

  /**
   * Internal helper that removes a loaded tool from the toolbox,
   * it removes a loaded tool panel and tab from the toolbox without removing
   * its definition, so that it can still be listed in options and re-added later.
   *
   * @param  {string} toolId
   *         Id of the tool to be removed.
   */
  unloadTool: function (toolId) {
    if (typeof toolId != "string") {
      throw new Error("Unexpected non-string toolId received.");
    }

    if (this._toolPanels.has(toolId)) {
      let instance = this._toolPanels.get(toolId);
      instance.destroy();
      this._toolPanels.delete(toolId);
    }

    let panel = this.doc.getElementById("toolbox-panel-" + toolId);

    // Select another tool.
    if (this.currentToolId == toolId) {
      let index = this.panelDefinitions.findIndex(({id}) => id === toolId);
      let nextTool = this.panelDefinitions[index + 1];
      let previousTool = this.panelDefinitions[index - 1];
      let toolNameToSelect;

      if (nextTool) {
        toolNameToSelect = nextTool.id;
      }
      if (previousTool) {
        toolNameToSelect = previousTool.id;
      }
      if (toolNameToSelect) {
        this.selectTool(toolNameToSelect);
      }
    }

    // Remove this tool from the current panel definitions.
    this.panelDefinitions = this.panelDefinitions.filter(({id}) => id !== toolId);
    this.visibleAdditionalTools = this.visibleAdditionalTools
                                      .filter(id => id !== toolId);
    this._combineAndSortPanelDefinitions();

    if (panel) {
      panel.remove();
    }

    if (this.hostType == Toolbox.HostType.WINDOW) {
      let doc = this.win.parent.document;
      let key = doc.getElementById("key_" + toolId);
      if (key) {
        key.remove();
      }
    }
  },

  /**
   * Handler for the tool-registered event.
   * @param  {string} event
   *         Name of the event ("tool-registered")
   * @param  {string} toolId
   *         Id of the tool that was registered
   */
  _toolRegistered: function (event, toolId) {
    // Tools can either be in the global devtools, or added to this specific toolbox
    // as an additional tool.
    let definition = gDevTools.getToolDefinition(toolId);
    let isAdditionalTool = false;
    if (!definition) {
      definition = this.additionalToolDefinitions.get(toolId);
      isAdditionalTool = true;
    }

    if (definition.isTargetSupported(this._target)) {
      if (isAdditionalTool) {
        this.visibleAdditionalTools = [...this.visibleAdditionalTools, toolId];
        this._combineAndSortPanelDefinitions();
      } else {
        this.panelDefinitions = this.panelDefinitions.concat(definition);
      }
      this._buildPanelForTool(definition);

      // Emit the event so tools can listen to it from the toolbox level
      // instead of gDevTools.
      this.emit("tool-registered", toolId);
    }
  },

  /**
   * Handler for the tool-unregistered event.
   * @param  {string} event
   *         Name of the event ("tool-unregistered")
   * @param  {string} toolId
   *         id of the tool that was unregistered
   */
  _toolUnregistered: function (event, toolId) {
    this.unloadTool(toolId);
    // Emit the event so tools can listen to it from the toolbox level
    // instead of gDevTools
    this.emit("tool-unregistered", toolId);
  },

  /**
   * Initialize the inspector/walker/selection/highlighter fronts.
   * Returns a promise that resolves when the fronts are initialized
   */
  initInspector: function () {
    if (!this._initInspector) {
      this._initInspector = Task.spawn(function* () {
        this._inspector = InspectorFront(this._target.client, this._target.form);
        let pref = "devtools.inspector.showAllAnonymousContent";
        let showAllAnonymousContent = Services.prefs.getBoolPref(pref);
        this._walker = yield this._inspector.getWalker({ showAllAnonymousContent });
        this._selection = new Selection(this._walker);
        this._selection.on("new-node-front", this._onNewSelectedNodeFront);

        if (this.highlighterUtils.isRemoteHighlightable()) {
          this.walker.on("highlighter-ready", this._highlighterReady);
          this.walker.on("highlighter-hide", this._highlighterHidden);

          let autohide = !flags.testing;
          this._highlighter = yield this._inspector.getHighlighter(autohide);
        }
      }.bind(this));
    }
    return this._initInspector;
  },

  _onNewSelectedNodeFront: function (evt) {
    // Emit a "selection-changed" event when the toolbox.selection has been set
    // to a new node (or cleared). Currently used in the WebExtensions APIs (to
    // provide the `devtools.panels.elements.onSelectionChanged` event).
    this.emit("selection-changed");
  },

  _onInspectObject: function (evt, packet) {
    this.inspectObjectActor(packet.objectActor, packet.inspectFromAnnotation);
  },

  inspectObjectActor: async function (objectActor, inspectFromAnnotation) {
    if (objectActor.preview &&
        objectActor.preview.nodeType === domNodeConstants.ELEMENT_NODE) {
      // Open the inspector and select the DOM Element.
      await this.loadTool("inspector");
      const inspector = this.getPanel("inspector");
      const nodeFound = await inspector.inspectNodeActor(objectActor.actor,
                                                         inspectFromAnnotation);
      if (nodeFound) {
        await this.selectTool("inspector");
      }
    } else if (objectActor.type !== "null" &&
               objectActor.type !== "undefined") {
      // Open then split console and inspect the object in the variables view,
      // when the objectActor doesn't represent an undefined or null value.
      await this.openSplitConsole();
      const panel = this.getPanel("webconsole");
      const jsterm = panel.hud.jsterm;

      jsterm.inspectObjectActor(objectActor);
    }
  },

  /**
   * Destroy the inspector/walker/selection fronts
   * Returns a promise that resolves when the fronts are destroyed
   */
  destroyInspector: function () {
    if (this._destroyingInspector) {
      return this._destroyingInspector;
    }

    this._destroyingInspector = Task.spawn(function* () {
      if (!this._inspector) {
        return;
      }

      // Ensure that the inspector isn't still being initiated, otherwise race conditions
      // in the initialization process can throw errors.
      yield this._initInspector;

      let currentPanel = this.getCurrentPanel();
      if (currentPanel.stopPicker) {
        yield currentPanel.stopPicker();
      } else {
        yield this.highlighterUtils.stopPicker();
      }

      yield this._inspector.destroy();
      if (this._highlighter) {
        // Note that if the toolbox is closed, this will work fine, but will fail
        // in case the browser is closed and will trigger a noSuchActor message.
        // We ignore the promise that |_hideBoxModel| returns, since we should still
        // proceed with the rest of destruction if it fails.
        // FF42+ now does the cleanup from the actor.
        if (!this.highlighter.traits.autoHideOnDestroy) {
          this.highlighterUtils.unhighlight();
        }
        yield this._highlighter.destroy();
      }
      if (this._selection) {
        this._selection.off("new-node-front", this._onNewSelectedNodeFront);
        this._selection.destroy();
      }

      if (this.walker) {
        this.walker.off("highlighter-ready", this._highlighterReady);
        this.walker.off("highlighter-hide", this._highlighterHidden);
      }

      this._inspector = null;
      this._highlighter = null;
      this._selection = null;
      this._walker = null;
    }.bind(this));
    return this._destroyingInspector;
  },

  /**
   * Get the toolbox's notification component
   *
   * @return The notification box component.
   */
  getNotificationBox: function () {
    return this.notificationBox;
  },

  /**
   * Remove all UI elements, detach from target and clear up
   */
  destroy: function () {
    // If several things call destroy then we give them all the same
    // destruction promise so we're sure to destroy only once
    if (this._destroyer) {
      return this._destroyer;
    }
    let deferred = defer();
    this._destroyer = deferred.promise;

    this.emit("destroy");

    this._target.off("inspect-object", this._onInspectObject);
    this._target.off("will-navigate", this._onWillNavigate);
    this._target.off("navigate", this._refreshHostTitle);
    this._target.off("frame-update", this._updateFrames);
    this.off("select", this._refreshHostTitle);
    this.off("select", this._updatePickerButton);
    this.off("host-changed", this._refreshHostTitle);
    this.off("ready", this._showDevEditionPromo);

    gDevTools.off("tool-registered", this._toolRegistered);
    gDevTools.off("tool-unregistered", this._toolUnregistered);

    Services.prefs.removeObserver("devtools.cache.disabled", this._applyCacheSettings);
    Services.prefs.removeObserver("devtools.serviceWorkers.testing.enabled",
                                  this._applyServiceWorkersTestingSettings);

    this._lastFocusedElement = null;

    if (this._sourceMapURLService) {
      this._sourceMapURLService.destroy();
      this._sourceMapURLService = null;
    }

    if (this._sourceMapService) {
      this._sourceMapService.stopSourceMapWorker();
      this._sourceMapService = null;
    }

    if (this.webconsolePanel) {
      this._saveSplitConsoleHeight();
      this.webconsolePanel.removeEventListener("resize",
        this._saveSplitConsoleHeight);
      this.webconsolePanel = null;
    }
    if (this.textBoxContextMenuPopup) {
      this.textBoxContextMenuPopup.removeEventListener("popupshowing",
        this._updateTextBoxMenuItems, true);
      this.textBoxContextMenuPopup = null;
    }
    if (this._componentMount) {
      this._componentMount.removeEventListener("keypress", this._onToolbarArrowKeypress);
      this.ReactDOM.unmountComponentAtNode(this._componentMount);
      this._componentMount = null;
    }

    let outstanding = [];
    for (let [id, panel] of this._toolPanels) {
      try {
        gDevTools.emit(id + "-destroy", this, panel);
        this.emit(id + "-destroy", panel);

        outstanding.push(panel.destroy());
      } catch (e) {
        // We don't want to stop here if any panel fail to close.
        console.error("Panel " + id + ":", e);
      }
    }

    this.browserRequire = null;

    // Now that we are closing the toolbox we can re-enable the cache settings
    // and disable the service workers testing settings for the current tab.
    // FF41+ automatically cleans up state in actor on disconnect.
    if (this.target.activeTab && !this.target.activeTab.traits.noTabReconfigureOnClose) {
      this.target.activeTab.reconfigure({
        "cacheDisabled": false,
        "serviceWorkersTestingEnabled": false
      });
    }

    // Destroying the walker and inspector fronts
    outstanding.push(this.destroyInspector());

    // Destroy the profiler connection
    outstanding.push(this.destroyPerformance());

    // Destroy the preference front
    outstanding.push(this.destroyPreference());

    // Destroy the style sheet front.
    if (this._styleSheets) {
      this._styleSheets.destroy();
      this._styleSheets = null;
    }

    // Destroy the device front for the current client if any.
    // A given DeviceFront instance can cached and shared between different panels, so
    // destroying it is the responsibility of the toolbox.
    let deviceFront = getKnownDeviceFront(this.target.client);
    if (deviceFront) {
      deviceFront.destroy();
    }

    // Detach the thread
    detachThread(this._threadClient);
    this._threadClient = null;

    // Unregister buttons listeners
    this.toolbarButtons.forEach(button => {
      if (typeof button.teardown == "function") {
        // teardown arguments have already been bound in _createButtonState
        button.teardown();
      }
    });

    // We need to grab a reference to win before this._host is destroyed.
    let win = this.win;

    this._telemetry.toolClosed("toolbox");
    this._telemetry.destroy();

    // Finish all outstanding tasks (which means finish destroying panels and
    // then destroying the host, successfully or not) before destroying the
    // target.
    deferred.resolve(settleAll(outstanding)
        .catch(console.error)
        .then(() => {
          this._removeHostListeners();

          // `location` may already be 'invalid' if the toolbox document is
          // already in process of destruction. Otherwise if it is still
          // around, ensure releasing toolbox document and triggering cleanup
          // thanks to unload event. We do that precisely here, before
          // nullifying the target as various cleanup code depends on the
          // target attribute to be still
          // defined.
          try {
            win.location.replace("about:blank");
          } catch (e) {
            // Do nothing;
          }

          // Targets need to be notified that the toolbox is being torn down.
          // This is done after other destruction tasks since it may tear down
          // fronts and the debugger transport which earlier destroy methods may
          // require to complete.
          if (!this._target) {
            return null;
          }
          let target = this._target;
          this._target = null;
          this.highlighterUtils.release();
          target.off("close", this.destroy);
          return target.destroy();
        }, console.error).then(() => {
          this.emit("destroyed");

          // Free _host after the call to destroyed in order to let a chance
          // to destroyed listeners to still query toolbox attributes
          this._host = null;
          this._win = null;
          this._toolPanels.clear();

          // Force GC to prevent long GC pauses when running tests and to free up
          // memory in general when the toolbox is closed.
          if (flags.testing) {
            win.QueryInterface(Ci.nsIInterfaceRequestor)
              .getInterface(Ci.nsIDOMWindowUtils)
              .garbageCollect();
          }
        }).catch(console.error));

    let leakCheckObserver = ({wrappedJSObject: barrier}) => {
      // Make the leak detector wait until this toolbox is properly destroyed.
      barrier.client.addBlocker("DevTools: Wait until toolbox is destroyed",
                                this._destroyer);
    };

    let topic = "shutdown-leaks-before-check";
    Services.obs.addObserver(leakCheckObserver, topic);
    this._destroyer.then(() => {
      Services.obs.removeObserver(leakCheckObserver, topic);
    });

    return this._destroyer;
  },

  _highlighterReady: function () {
    this.emit("highlighter-ready");
  },

  _highlighterHidden: function () {
    this.emit("highlighter-hide");
  },

  /**
   * For displaying the promotional Doorhanger on first opening of
   * the developer tools, promoting the Developer Edition.
   */
  _showDevEditionPromo: function () {
    // Do not display in browser toolbox
    if (this.target.chrome) {
      return;
    }
    showDoorhanger({ window: this.win, type: "deveditionpromo" });
  },

  /**
   * Enable / disable necessary textbox menu items using globalOverlay.js.
   */
  _updateTextBoxMenuItems: function () {
    let window = this.win;
    ["cmd_undo", "cmd_delete", "cmd_cut",
     "cmd_copy", "cmd_paste", "cmd_selectAll"].forEach(window.goUpdateCommand);
  },

  /**
   * Open the textbox context menu at given coordinates.
   * Panels in the toolbox can call this on contextmenu events with event.screenX/Y
   * instead of having to implement their own copy/paste/selectAll menu.
   * @param {Number} x
   * @param {Number} y
   */
  openTextBoxContextMenu: function (x, y) {
    this.textBoxContextMenuPopup.openPopupAtScreen(x, y, true);
  },

  /**
   * Connects to the Gecko Profiler when the developer tools are open. This is
   * necessary because of the WebConsole's `profile` and `profileEnd` methods.
   */
  initPerformance: Task.async(function* () {
    // If target does not have performance actor (addons), do not
    // even register the shared performance connection.
    if (!this.target.hasActor("performance")) {
      return promise.resolve();
    }

    if (this._performanceFrontConnection) {
      return this._performanceFrontConnection.promise;
    }

    this._performanceFrontConnection = defer();
    this._performance = createPerformanceFront(this._target);
    yield this.performance.connect();

    // Emit an event when connected, but don't wait on startup for this.
    this.emit("profiler-connected");

    this.performance.on("*", this._onPerformanceFrontEvent);
    this._performanceFrontConnection.resolve(this.performance);
    return this._performanceFrontConnection.promise;
  }),

  /**
   * Disconnects the underlying Performance actor. If the connection
   * has not finished initializing, as opening a toolbox does not wait,
   * the performance connection destroy method will wait for it on its own.
   */
  destroyPerformance: Task.async(function* () {
    if (!this.performance) {
      return;
    }
    // If still connecting to performance actor, allow the
    // actor to resolve its connection before attempting to destroy.
    if (this._performanceFrontConnection) {
      yield this._performanceFrontConnection.promise;
    }
    this.performance.off("*", this._onPerformanceFrontEvent);
    yield this.performance.destroy();
    this._performance = null;
  }),

  /**
   * Return the style sheets front, creating it if necessary.  If the
   * style sheets front is not supported by the target, returns null.
   */
  initStyleSheetsFront: function () {
    if (!this._styleSheets && this.target.hasActor("styleSheets")) {
      this._styleSheets = StyleSheetsFront(this.target.client, this.target.form);
    }
    return this._styleSheets;
  },

  /**
   * Destroy the preferences actor when the toolbox is unloaded.
   */
  destroyPreference: Task.async(function* () {
    if (!this._preferenceFront) {
      return;
    }

    // Only reset the autohide pref in the Browser Toolbox if it's been toggled
    // in the UI (don't reset the pref if it was already set before opening)
    if (this._autohideHasBeenToggled) {
      yield this._preferenceFront.clearUserPref(DISABLE_AUTOHIDE_PREF);
    }

    this._preferenceFront.destroy();
    this._preferenceFront = null;
  }),

  /**
   * Called when any event comes from the PerformanceFront. If the performance tool is
   * already loaded when the first event comes in, immediately unbind this handler, as
   * this is only used to queue up observed recordings before the performance tool can
   * handle them, which will only occur when `console.profile()` recordings are started
   * before the tool loads.
   */
  _onPerformanceFrontEvent: Task.async(function* (eventName, recording) {
    if (this.getPanel("performance")) {
      this.performance.off("*", this._onPerformanceFrontEvent);
      return;
    }

    this._performanceQueuedRecordings = this._performanceQueuedRecordings || [];
    let recordings = this._performanceQueuedRecordings;

    // Before any console recordings, we'll get a `console-profile-start` event
    // warning us that a recording will come later (via `recording-started`), so
    // start to boot up the tool and populate the tool with any other recordings
    // observed during that time.
    if (eventName === "console-profile-start" && !this._performanceToolOpenedViaConsole) {
      this._performanceToolOpenedViaConsole = this.loadTool("performance");
      let panel = yield this._performanceToolOpenedViaConsole;
      yield panel.open();

      panel.panelWin.PerformanceController.populateWithRecordings(recordings);
      this.performance.off("*", this._onPerformanceFrontEvent);
    }

    // Otherwise, if it's a recording-started event, we've already started loading
    // the tool, so just store this recording in our array to be later populated
    // once the tool loads.
    if (eventName === "recording-started") {
      recordings.push(recording);
    }
  }),

  /**
   * Returns gViewSourceUtils for viewing source.
   */
  get gViewSourceUtils() {
    return this.win.gViewSourceUtils;
  },

  /**
   * Opens source in style editor. Falls back to plain "view-source:".
   * @see devtools/client/shared/source-utils.js
   */
  viewSourceInStyleEditor: function (sourceURL, sourceLine) {
    return viewSource.viewSourceInStyleEditor(this, sourceURL, sourceLine);
  },

  /**
   * Opens source in debugger. Falls back to plain "view-source:".
   * @see devtools/client/shared/source-utils.js
   */
  viewSourceInDebugger: function (sourceURL, sourceLine) {
    return viewSource.viewSourceInDebugger(this, sourceURL, sourceLine);
  },

  /**
   * Opens source in scratchpad. Falls back to plain "view-source:".
   * TODO The `sourceURL` for scratchpad instances are like `Scratchpad/1`.
   * If instances are scoped one-per-browser-window, then we should be able
   * to infer the URL from this toolbox, or use the built in scratchpad IN
   * the toolbox.
   *
   * @see devtools/client/shared/source-utils.js
   */
  viewSourceInScratchpad: function (sourceURL, sourceLine) {
    return viewSource.viewSourceInScratchpad(sourceURL, sourceLine);
  },

  /**
   * Opens source in plain "view-source:".
   * @see devtools/client/shared/source-utils.js
   */
  viewSource: function (sourceURL, sourceLine) {
    return viewSource.viewSource(this, sourceURL, sourceLine);
  },

  // Support for WebExtensions API (`devtools.network.*`)

  /**
   * Returns data (HAR) collected by the Network panel.
   */
  getHARFromNetMonitor: async function () {
    let netPanel = this.getPanel("netmonitor");

    // The panel doesn't have to exist (it must be selected
    // by the user at least once to be created).
    // Return default empty HAR log in such case.
    if (!netPanel) {
      let har = await buildHarLog(Services.appinfo);
      return har.log;
    }

    // Use Netmonitor object to get the current HAR log.
    let har = await netPanel.panelWin.Netmonitor.getHar();

    // Return the log directly to be compatible with
    // Chrome WebExtension API.
    return har.log;
  },

  /**
   * Add listener for `onRequestFinished` events.
   *
   * @param {Object} listener
   *        The listener to be called it's expected to be
   *        a function that takes ({harEntry, requestId})
   *        as first argument.
   */
  addRequestFinishedListener: function (listener) {
    // Log console message informing the extension developer
    // that the Network panel needs to be selected at least
    // once in order to receive `onRequestFinished` events.
    let message = "The Network panel needs to be selected at least" +
      " once in order to receive 'onRequestFinished' events.";
    this.target.logWarningInPage(message, "har");

    // Add the listener into internal list.
    this._requestFinishedListeners.add(listener);
  },

  removeRequestFinishedListener: function (listener) {
    this._requestFinishedListeners.delete(listener);
  },

  getRequestFinishedListeners: function () {
    return this._requestFinishedListeners;
  },

  /**
   * Used to lazily fetch HTTP response content within
   * `onRequestFinished` event listener.
   *
   * @param {String} requestId
   *        Id of the request for which the response content
   *        should be fetched.
   */
  fetchResponseContent: function (requestId) {
    let netPanel = this.getPanel("netmonitor");

    // The panel doesn't have to exist (it must be selected
    // by the user at least once to be created).
    // Return undefined content in such case.
    if (!netPanel) {
      return Promise.resolve({content: {}});
    }

    return netPanel.panelWin.Netmonitor.fetchResponseContent(requestId);
  }
};
PK
!<,noe77Qchrome/devtools/modules/devtools/client/inspector/animation/actions/animations.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  UPDATE_ANIMATIONS,
  UPDATE_DETAIL_VISIBILITY,
  UPDATE_ELEMENT_PICKER_ENABLED,
  UPDATE_SELECTED_ANIMATION,
  UPDATE_SIDEBAR_SIZE
} = require("./index");

module.exports = {
  /**
   * Update the list of animation in the animation inspector.
   */
  updateAnimations(animations) {
    return {
      type: UPDATE_ANIMATIONS,
      animations,
    };
  },

  /**
   * Update visibility of detail pane.
   */
  updateDetailVisibility(detailVisibility) {
    return {
      type: UPDATE_DETAIL_VISIBILITY,
      detailVisibility,
    };
  },

  /**
   * Update the state of element picker in animation inspector.
   */
  updateElementPickerEnabled(elementPickerEnabled) {
    return {
      type: UPDATE_ELEMENT_PICKER_ENABLED,
      elementPickerEnabled,
    };
  },

  /**
   * Update selected animation.
   */
  updateSelectedAnimation(selectedAnimation) {
    return {
      type: UPDATE_SELECTED_ANIMATION,
      selectedAnimation,
    };
  },

  /**
   * Update the sidebar size.
   */
  updateSidebarSize(sidebarSize) {
    return {
      type: UPDATE_SIDEBAR_SIZE,
      sidebarSize,
    };
  }
};
PK
!<KVn�Lchrome/devtools/modules/devtools/client/inspector/animation/actions/index.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { createEnum } = require("devtools/client/shared/enum");

createEnum([

  // Update the list of animation.
  "UPDATE_ANIMATIONS",

  // Update visibility of detail pane.
  "UPDATE_DETAIL_VISIBILITY",

  // Update state of the picker enabled.
  "UPDATE_ELEMENT_PICKER_ENABLED",

  // Update selected animation.
  "UPDATE_SELECTED_ANIMATION",

  // Update sidebar size.
  "UPDATE_SIDEBAR_SIZE",

], module.exports);
PK
!<#���$�$Hchrome/devtools/modules/devtools/client/inspector/animation/animation.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { AnimationsFront } = require("devtools/shared/fronts/animation");
const { createElement, createFactory } = require("devtools/client/shared/vendor/react");
const { Provider } = require("devtools/client/shared/vendor/react-redux");

const EventEmitter = require("devtools/shared/event-emitter");

const App = createFactory(require("./components/App"));

const {
  updateAnimations,
  updateDetailVisibility,
  updateElementPickerEnabled,
  updateSelectedAnimation,
  updateSidebarSize
} = require("./actions/animations");
const { isAllAnimationEqual } = require("./utils/utils");

class AnimationInspector {
  constructor(inspector, win) {
    this.inspector = inspector;
    this.win = win;

    this.getAnimatedPropertyMap = this.getAnimatedPropertyMap.bind(this);
    this.getComputedStyle = this.getComputedStyle.bind(this);
    this.getNodeFromActor = this.getNodeFromActor.bind(this);
    this.selectAnimation = this.selectAnimation.bind(this);
    this.setDetailVisibility = this.setDetailVisibility.bind(this);
    this.simulateAnimation = this.simulateAnimation.bind(this);
    this.toggleElementPicker = this.toggleElementPicker.bind(this);
    this.update = this.update.bind(this);
    this.onElementPickerStarted = this.onElementPickerStarted.bind(this);
    this.onElementPickerStopped = this.onElementPickerStopped.bind(this);
    this.onSidebarResized = this.onSidebarResized.bind(this);
    this.onSidebarSelect = this.onSidebarSelect.bind(this);

    EventEmitter.decorate(this);
    this.emit = this.emit.bind(this);

    this.init();
  }

  init() {
    const {
      setSelectedNode,
      onShowBoxModelHighlighterForNode,
    } = this.inspector.getCommonComponentProps();

    const {
      onHideBoxModelHighlighter,
    } = this.inspector.getPanel("boxmodel").getComponentProps();

    const {
      emit: emitEventForTest,
      getAnimatedPropertyMap,
      getComputedStyle,
      getNodeFromActor,
      selectAnimation,
      setDetailVisibility,
      simulateAnimation,
      toggleElementPicker,
    } = this;

    const target = this.inspector.target;
    this.animationsFront = new AnimationsFront(target.client, target.form);

    const provider = createElement(Provider,
      {
        id: "newanimationinspector",
        key: "newanimationinspector",
        store: this.inspector.store
      },
      App(
        {
          emitEventForTest,
          getAnimatedPropertyMap,
          getComputedStyle,
          getNodeFromActor,
          onHideBoxModelHighlighter,
          onShowBoxModelHighlighterForNode,
          selectAnimation,
          setDetailVisibility,
          setSelectedNode,
          simulateAnimation,
          toggleElementPicker,
        }
      )
    );
    this.provider = provider;

    this.inspector.selection.on("new-node-front", this.update);
    this.inspector.sidebar.on("newanimationinspector-selected", this.onSidebarSelect);
    this.inspector.toolbox.on("inspector-sidebar-resized", this.onSidebarResized);
    this.inspector.toolbox.on("picker-started", this.onElementPickerStarted);
    this.inspector.toolbox.on("picker-stopped", this.onElementPickerStopped);
  }

  destroy() {
    this.inspector.selection.off("new-node-front", this.update);
    this.inspector.sidebar.off("newanimationinspector-selected", this.onSidebarSelect);
    this.inspector.toolbox.off("inspector-sidebar-resized", this.onSidebarResized);
    this.inspector.toolbox.off("picker-started", this.onElementPickerStarted);
    this.inspector.toolbox.off("picker-stopped", this.onElementPickerStopped);

    if (this.simulatedAnimation) {
      this.simulatedAnimation.cancel();
      this.simulatedAnimation = null;
    }

    if (this.simulatedElement) {
      this.simulatedElement.remove();
      this.simulatedElement = null;
    }

    this.inspector = null;
    this.win = null;
  }

  /**
   * Return a map of animated property from given animation actor.
   *
   * @param {Object} animation
   * @return {Map} A map of animated property
   *         key: {String} Animated property name
   *         value: {Array} Array of keyframe object
   *         Also, the keyframe object is consisted as following.
   *         {
   *           value: {String} style,
   *           offset: {Number} offset of keyframe,
   *           easing: {String} easing from this keyframe to next keyframe,
   *           distance: {Number} use as y coordinate in graph,
   *         }
   */
  async getAnimatedPropertyMap(animation) {
    let properties = [];

    try {
      properties = await animation.getProperties();
    } catch (e) {
      // Expected if we've already been destroyed in the meantime.
      console.error(e);
    }

    const animatedPropertyMap = new Map();

    for (const { name, values } of properties) {
      const keyframes = values.map(({ value, offset, easing, distance = 0 }) => {
        offset = parseFloat(offset.toFixed(3));
        return { value, offset, easing, distance };
      });

      animatedPropertyMap.set(name, keyframes);
    }

    return animatedPropertyMap;
  }

  /**
   * Return the computed style of the specified property after setting the given styles
   * to the simulated element.
   *
   * @param {String} property
   *        CSS property name (e.g. text-align).
   * @param {Object} styles
   *        Map of CSS property name and value.
   * @return {String}
   *         Computed style of property.
   */
  getComputedStyle(property, styles) {
    this.simulatedElement.style.cssText = "";

    for (let propertyName in styles) {
      this.simulatedElement.style.setProperty(propertyName, styles[propertyName]);
    }

    return this.win.getComputedStyle(this.simulatedElement).getPropertyValue(property);
  }

  getNodeFromActor(actorID) {
    return this.inspector.walker.getNodeFromActor(actorID, ["node"]);
  }

  isPanelVisible() {
    return this.inspector && this.inspector.toolbox && this.inspector.sidebar &&
           this.inspector.toolbox.currentToolId === "inspector" &&
           this.inspector.sidebar.getCurrentTabID() === "newanimationinspector";
  }

  /**
   * Returns simulatable animation by given parameters.
   * The returned animation is implementing Animation interface of Web Animation API.
   * https://drafts.csswg.org/web-animations/#the-animation-interface
   *
   * @param {Array} keyframes
   *        e.g. [{ opacity: 0 }, { opacity: 1 }]
   * @param {Object} effectTiming
   *        e.g. { duration: 1000, fill: "both" }
   * @param {Boolean} isElementNeeded
   *        true:  create animation with an element.
   *               If want to know computed value of the element, turn on.
   *        false: create animation without an element,
   *               If need to know only timing progress.
   * @return {Animation}
   *         https://drafts.csswg.org/web-animations/#the-animation-interface
   */
  simulateAnimation(keyframes, effectTiming, isElementNeeded) {
    let targetEl = null;

    if (isElementNeeded) {
      if (!this.simulatedElement) {
        this.simulatedElement = this.win.document.createElement("div");
        this.win.document.documentElement.appendChild(this.simulatedElement);
      } else {
        // Reset styles.
        this.simulatedElement.style.cssText = "";
      }

      targetEl = this.simulatedElement;
    }

    if (!this.simulatedAnimation) {
      this.simulatedAnimation = new this.win.Animation();
    }

    this.simulatedAnimation.effect =
      new this.win.KeyframeEffect(targetEl, keyframes, effectTiming);

    return this.simulatedAnimation;
  }

  toggleElementPicker() {
    this.inspector.toolbox.highlighterUtils.togglePicker();
  }

  async update() {
    if (!this.inspector || !this.isPanelVisible()) {
      // AnimationInspector was destroyed already or the panel is hidden.
      return;
    }

    const done = this.inspector.updating("newanimationinspector");

    const selection = this.inspector.selection;
    const animations =
      selection.isConnected() && selection.isElementNode()
      ? await this.animationsFront.getAnimationPlayersForNode(selection.nodeFront)
      : [];

    if (!this.animations || !isAllAnimationEqual(animations, this.animations)) {
      this.inspector.store.dispatch(updateAnimations(animations));
      this.animations = animations;
      // If number of displayed animations is one, we select the animation automatically.
      this.selectAnimation(animations.length === 1 ? animations[0] : null);
    }

    done();
  }

  selectAnimation(animation) {
    this.inspector.store.dispatch(updateSelectedAnimation(animation));
  }

  setDetailVisibility(isVisible) {
    this.inspector.store.dispatch(updateDetailVisibility(isVisible));
  }

  onElementPickerStarted() {
    this.inspector.store.dispatch(updateElementPickerEnabled(true));
  }

  onElementPickerStopped() {
    this.inspector.store.dispatch(updateElementPickerEnabled(false));
  }

  onSidebarSelect() {
    this.update();
    this.onSidebarResized(null, this.inspector.getSidebarSize());
  }

  onSidebarResized(type, size) {
    if (!this.isPanelVisible()) {
      return;
    }

    this.inspector.store.dispatch(updateSidebarSize(size));
  }
}

module.exports = AnimationInspector;
PK
!<�ɠJ^chrome/devtools/modules/devtools/client/inspector/animation/components/AnimatedPropertyItem.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");

const AnimatedPropertyName = createFactory(require("./AnimatedPropertyName"));
const KeyframesGraph = createFactory(require("./keyframes-graph/KeyframesGraph"));

class AnimatedPropertyItem extends PureComponent {
  static get propTypes() {
    return {
      getComputedStyle: PropTypes.func.isRequired,
      property: PropTypes.string.isRequired,
      simulateAnimation: PropTypes.func.isRequired,
      state: PropTypes.object.isRequired,
      type: PropTypes.string.isRequired,
      values: PropTypes.array.isRequired,
    };
  }

  render() {
    const {
      getComputedStyle,
      property,
      simulateAnimation,
      state,
      type,
      values,
    } = this.props;

    return dom.li(
      {
        className: "animated-property-item"
      },
      AnimatedPropertyName(
        {
          property,
          state,
        }
      ),
      KeyframesGraph(
        {
          getComputedStyle,
          property,
          simulateAnimation,
          type,
          values,
        }
      )
    );
  }
}

module.exports = AnimatedPropertyItem;
PK
!<�e��$
$
^chrome/devtools/modules/devtools/client/inspector/animation/components/AnimatedPropertyList.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");

const AnimatedPropertyItem = createFactory(require("./AnimatedPropertyItem"));

class AnimatedPropertyList extends PureComponent {
  static get propTypes() {
    return {
      animation: PropTypes.object.isRequired,
      emitEventForTest: PropTypes.func.isRequired,
      getAnimatedPropertyMap: PropTypes.func.isRequired,
      getComputedStyle: PropTypes.func.isRequired,
      simulateAnimation: PropTypes.func.isRequired,
    };
  }

  constructor(props) {
    super(props);

    this.state = {
      animatedPropertyMap: null
    };
  }

  componentDidMount() {
    this.updateKeyframesList(this.props.animation);
  }

  componentWillReceiveProps(nextProps) {
    this.updateKeyframesList(nextProps.animation);
  }

  getPropertyState(property) {
    const { animation } = this.props;

    for (const propState of animation.state.propertyState) {
      if (propState.property === property) {
        return propState;
      }
    }

    return null;
  }

  async updateKeyframesList(animation) {
    const {
      getAnimatedPropertyMap,
      emitEventForTest,
    } = this.props;
    const animatedPropertyMap = await getAnimatedPropertyMap(animation);
    const animationTypes = await animation.getAnimationTypes(animatedPropertyMap.keys());

    this.setState({ animatedPropertyMap, animationTypes });

    emitEventForTest("animation-keyframes-rendered");
  }

  render() {
    const {
      getComputedStyle,
      simulateAnimation,
    } = this.props;
    const {
      animatedPropertyMap,
      animationTypes,
    } = this.state;

    if (!animatedPropertyMap) {
      return null;
    }

    return dom.ul(
      {
        className: "animated-property-list"
      },
      [...animatedPropertyMap.entries()].map(([property, values]) => {
        const state = this.getPropertyState(property);
        const type = animationTypes[property];
        return AnimatedPropertyItem(
          {
            getComputedStyle,
            property,
            simulateAnimation,
            state,
            type,
            values,
          }
        );
      })
    );
  }
}

module.exports = AnimatedPropertyList;
PK
!<A��++gchrome/devtools/modules/devtools/client/inspector/animation/components/AnimatedPropertyListContainer.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");

const AnimatedPropertyList = createFactory(require("./AnimatedPropertyList"));
const AnimatedPropertyListHeader = createFactory(require("./AnimatedPropertyListHeader"));

class AnimatedPropertyListContainer extends PureComponent {
  static get propTypes() {
    return {
      animation: PropTypes.object.isRequired,
      emitEventForTest: PropTypes.func.isRequired,
      getAnimatedPropertyMap: PropTypes.func.isRequired,
      getComputedStyle: PropTypes.func.isRequired,
      simulateAnimation: PropTypes.func.isRequired,
    };
  }

  render() {
    const {
      animation,
      emitEventForTest,
      getAnimatedPropertyMap,
      getComputedStyle,
      simulateAnimation,
    } = this.props;

    return dom.div(
      {
        className: `animated-property-list-container ${ animation.state.type }`
      },
      AnimatedPropertyListHeader(),
      AnimatedPropertyList(
        {
          animation,
          emitEventForTest,
          getAnimatedPropertyMap,
          getComputedStyle,
          simulateAnimation,
        }
      )
    );
  }
}

module.exports = AnimatedPropertyListContainer;
PK
!<Ջ�;��dchrome/devtools/modules/devtools/client/inspector/animation/components/AnimatedPropertyListHeader.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/vendor/react");
const dom = require("devtools/client/shared/vendor/react-dom-factories");

const KeyframesProgressTickList = createFactory(require("./KeyframesProgressTickList"));

class AnimatedPropertyListHeader extends PureComponent {
  render() {
    return dom.div(
      {
        className: "animated-property-list-header devtools-toolbar"
      },
      KeyframesProgressTickList()
    );
  }
}

module.exports = AnimatedPropertyListHeader;
PK
!<��'Q[[^chrome/devtools/modules/devtools/client/inspector/animation/components/AnimatedPropertyName.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");

class AnimatedPropertyName extends PureComponent {
  static get propTypes() {
    return {
      property: PropTypes.string.isRequired,
      state: PropTypes.oneOfType([null, PropTypes.object]).isRequired,
    };
  }

  render() {
    const {
      property,
      state,
    } = this.props;

    return dom.div(
      {
        className: "animated-property-name" +
                   (state && state.runningOnCompositor ? " compositor" : "") +
                   (state && state.warning ? " warning" : ""),
        title: state ? state.warning : "",
      },
      dom.span(
        {},
        property
      )
    );
  }
}

module.exports = AnimatedPropertyName;
PK
!<�j���bchrome/devtools/modules/devtools/client/inspector/animation/components/AnimationDetailContainer.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { connect } = require("devtools/client/shared/vendor/react-redux");
const { createFactory, PureComponent } = 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");

const AnimationDetailHeader = createFactory(require("./AnimationDetailHeader"));
const AnimatedPropertyListContainer =
  createFactory(require("./AnimatedPropertyListContainer"));

class AnimationDetailContainer extends PureComponent {
  static get propTypes() {
    return {
      animation: PropTypes.object.isRequired,
      emitEventForTest: PropTypes.func.isRequired,
      getAnimatedPropertyMap: PropTypes.func.isRequired,
      getComputedStyle: PropTypes.func.isRequired,
      setDetailVisibility: PropTypes.func.isRequired,
      simulateAnimation: PropTypes.func.isRequired,
    };
  }

  render() {
    const {
      animation,
      emitEventForTest,
      getAnimatedPropertyMap,
      getComputedStyle,
      setDetailVisibility,
      simulateAnimation,
    } = this.props;

    return dom.div(
      {
        className: "animation-detail-container"
      },
      animation ?
        AnimationDetailHeader(
          {
            animation,
            setDetailVisibility,
          }
        )
      :
        null,
      animation ?
        AnimatedPropertyListContainer(
          {
            animation,
            emitEventForTest,
            getAnimatedPropertyMap,
            getComputedStyle,
            simulateAnimation,
          }
        )
      :
        null
    );
  }
}

const mapStateToProps = state => {
  return {
    animation: state.animations.selectedAnimation,
  };
};

module.exports = connect(mapStateToProps)(AnimationDetailContainer);
PK
!<�6�-_chrome/devtools/modules/devtools/client/inspector/animation/components/AnimationDetailHeader.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");

const { getFormattedTitle } = require("../utils/l10n");

class AnimationDetailHeader extends PureComponent {
  static get propTypes() {
    return {
      animation: PropTypes.object.isRequired,
      setDetailVisibility: PropTypes.func.isRequired,
    };
  }

  onClick() {
    const { setDetailVisibility } = this.props;
    setDetailVisibility(false);
  }

  render() {
    const { animation } = this.props;

    return dom.div(
      {
        className: "animation-detail-header devtools-toolbar",
      },
      dom.div(
        {
          className: "animation-detail-title",
        },
        getFormattedTitle(animation.state)
      ),
      dom.button(
        {
          className: "animation-detail-close-button devtools-button",
          onClick: this.onClick.bind(this),
        }
      )
    );
  }
}

module.exports = AnimationDetailHeader;
PK
!<+����Wchrome/devtools/modules/devtools/client/inspector/animation/components/AnimationItem.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { connect } = require("devtools/client/shared/vendor/react-redux");
const { createFactory, PureComponent } = 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");

const AnimationTarget = createFactory(require("./AnimationTarget"));
const SummaryGraph = createFactory(require("./graph/SummaryGraph"));

class AnimationItem extends PureComponent {
  static get propTypes() {
    return {
      animation: PropTypes.object.isRequired,
      emitEventForTest: PropTypes.func.isRequired,
      getAnimatedPropertyMap: PropTypes.func.isRequired,
      getNodeFromActor: PropTypes.func.isRequired,
      onHideBoxModelHighlighter: PropTypes.func.isRequired,
      onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
      selectAnimation: PropTypes.func.isRequired,
      selectedAnimation: PropTypes.object.isRequired,
      setSelectedNode: PropTypes.func.isRequired,
      simulateAnimation: PropTypes.func.isRequired,
      timeScale: PropTypes.object.isRequired,
    };
  }

  constructor(props) {
    super(props);

    this.state = {
      isSelected: false,
    };
  }

  componentWillReceiveProps(nextProps) {
    const { animation } = this.props;

    this.setState({
      isSelected: nextProps.selectedAnimation &&
                  animation.actorID === nextProps.selectedAnimation.actorID
    });
  }

  shouldComponentUpdate(nextProps, nextState) {
    return this.state.isSelected !== nextState.isSelected ||
           this.props.animation !== nextProps.animation ||
           this.props.timeScale !== nextProps.timeScale;
  }

  render() {
    const {
      animation,
      emitEventForTest,
      getAnimatedPropertyMap,
      getNodeFromActor,
      onHideBoxModelHighlighter,
      onShowBoxModelHighlighterForNode,
      selectAnimation,
      setSelectedNode,
      simulateAnimation,
      timeScale,
    } = this.props;
    const {
      isSelected,
    } = this.state;

    return dom.li(
      {
        className: `animation-item ${ animation.state.type } ` +
                   (isSelected ? "selected" : ""),
      },
      AnimationTarget(
        {
          animation,
          emitEventForTest,
          getNodeFromActor,
          onHideBoxModelHighlighter,
          onShowBoxModelHighlighterForNode,
          setSelectedNode,
        }
      ),
      SummaryGraph(
        {
          animation,
          emitEventForTest,
          getAnimatedPropertyMap,
          selectAnimation,
          simulateAnimation,
          timeScale,
        }
      )
    );
  }
}

const mapStateToProps = state => {
  return {
    selectedAnimation: state.animations.selectedAnimation,
  };
};

module.exports = connect(mapStateToProps)(AnimationItem);
PK
!<������Wchrome/devtools/modules/devtools/client/inspector/animation/components/AnimationList.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");

const AnimationItem = createFactory(require("./AnimationItem"));

class AnimationList extends PureComponent {
  static get propTypes() {
    return {
      animations: PropTypes.arrayOf(PropTypes.object).isRequired,
      emitEventForTest: PropTypes.func.isRequired,
      getAnimatedPropertyMap: PropTypes.func.isRequired,
      getNodeFromActor: PropTypes.func.isRequired,
      onHideBoxModelHighlighter: PropTypes.func.isRequired,
      onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
      selectAnimation: PropTypes.func.isRequired,
      setSelectedNode: PropTypes.func.isRequired,
      simulateAnimation: PropTypes.func.isRequired,
      timeScale: PropTypes.object.isRequired,
    };
  }

  render() {
    const {
      animations,
      emitEventForTest,
      getAnimatedPropertyMap,
      getNodeFromActor,
      onHideBoxModelHighlighter,
      onShowBoxModelHighlighterForNode,
      selectAnimation,
      setSelectedNode,
      simulateAnimation,
      timeScale,
    } = this.props;

    return dom.ul(
      {
        className: "animation-list"
      },
      animations.map(animation =>
        AnimationItem(
          {
            animation,
            emitEventForTest,
            getAnimatedPropertyMap,
            getNodeFromActor,
            onHideBoxModelHighlighter,
            onShowBoxModelHighlighterForNode,
            selectAnimation,
            setSelectedNode,
            simulateAnimation,
            timeScale,
          }
        )
      )
    );
  }
}

module.exports = AnimationList;
PK
!<>LKrr`chrome/devtools/modules/devtools/client/inspector/animation/components/AnimationListContainer.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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 AnimationList = createFactory(require("./AnimationList"));
const AnimationListHeader = createFactory(require("./AnimationListHeader"));

const TimeScale = require("../utils/timescale");

class AnimationListContainer extends PureComponent {
  static get propTypes() {
    return {
      animations: PropTypes.arrayOf(PropTypes.object).isRequired,
      emitEventForTest: PropTypes.func.isRequired,
      getAnimatedPropertyMap: PropTypes.func.isRequired,
      getNodeFromActor: PropTypes.func.isRequired,
      onHideBoxModelHighlighter: PropTypes.func.isRequired,
      onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
      selectAnimation: PropTypes.func.isRequired,
      setSelectedNode: PropTypes.func.isRequired,
      simulateAnimation: PropTypes.func.isRequired,
    };
  }

  render() {
    const {
      animations,
      emitEventForTest,
      getAnimatedPropertyMap,
      getNodeFromActor,
      onHideBoxModelHighlighter,
      onShowBoxModelHighlighterForNode,
      selectAnimation,
      setSelectedNode,
      simulateAnimation,
    } = this.props;
    const timeScale = new TimeScale(animations);

    return dom.div(
      {
        className: "animation-list-container"
      },
      AnimationListHeader(
        {
          timeScale,
        }
      ),
      AnimationList(
        {
          animations,
          emitEventForTest,
          getAnimatedPropertyMap,
          getNodeFromActor,
          onHideBoxModelHighlighter,
          onShowBoxModelHighlighterForNode,
          selectAnimation,
          setSelectedNode,
          simulateAnimation,
          timeScale,
        }
      )
    );
  }
}

module.exports = AnimationListContainer;
PK
!<���g��]chrome/devtools/modules/devtools/client/inspector/animation/components/AnimationListHeader.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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 AnimationTimelineTickList = createFactory(require("./AnimationTimelineTickList"));

class AnimationListHeader extends PureComponent {
  static get propTypes() {
    return {
      timeScale: PropTypes.object.isRequired,
    };
  }

  render() {
    const { timeScale } = this.props;

    return dom.div(
      {
        className: "animation-list-header devtools-toolbar"
      },
      AnimationTimelineTickList(
        {
          timeScale
        }
      )
    );
  }
}

module.exports = AnimationListHeader;
PK
!<I3�1..Ychrome/devtools/modules/devtools/client/inspector/animation/components/AnimationTarget.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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 { translateNodeFrontToGrip } = require("devtools/client/inspector/shared/utils");

const { REPS, MODE } = require("devtools/client/shared/components/reps/reps");
const { Rep } = REPS;
const ElementNode = REPS.ElementNode;

class AnimationTarget extends PureComponent {
  static get propTypes() {
    return {
      animation: PropTypes.object.isRequired,
      emitEventForTest: PropTypes.func.isRequired,
      getNodeFromActor: PropTypes.func.isRequired,
      onHideBoxModelHighlighter: PropTypes.func.isRequired,
      onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
      setSelectedNode: PropTypes.func.isRequired,
    };
  }

  constructor(props) {
    super(props);

    this.state = {
      nodeFront: null,
    };
  }

  componentWillMount() {
    this.updateNodeFront(this.props.animation);
  }

  componentWillReceiveProps(nextProps) {
    if (this.props.animation.actorID !== nextProps.animation.actorID) {
      this.updateNodeFront(nextProps.animation);
    }
  }

  shouldComponentUpdate(nextProps, nextState) {
    return this.state.nodeFront !== nextState.nodeFront;
  }

  async updateNodeFront(animation) {
    const { emitEventForTest, getNodeFromActor } = this.props;

    // Try and get it from the playerFront directly.
    let nodeFront = animation.animationTargetNodeFront;

    // Next, get it from the walkerActor if it wasn't found.
    if (!nodeFront) {
      try {
        nodeFront = await getNodeFromActor(animation.actorID);
      } catch (e) {
        // If an error occured while getting the nodeFront and if it can't be
        // attributed to the panel having been destroyed in the meantime, this
        // error needs to be logged and render needs to stop.
        console.error(e);
        return;
      }
    }

    this.setState({ nodeFront });
    emitEventForTest("animation-target-rendered");
  }

  render() {
    const {
      onHideBoxModelHighlighter,
      onShowBoxModelHighlighterForNode,
      setSelectedNode,
    } = this.props;

    const { nodeFront } = this.state;

    if (!nodeFront) {
      return dom.div(
        {
          className: "animation-target"
        }
      );
    }

    return dom.div(
      {
        className: "animation-target"
      },
      Rep(
        {
          defaultRep: ElementNode,
          mode: MODE.TINY,
          object: translateNodeFrontToGrip(nodeFront),
          onDOMNodeMouseOut: () => onHideBoxModelHighlighter(),
          onDOMNodeMouseOver: () => onShowBoxModelHighlighterForNode(nodeFront),
          onInspectIconClick: () => setSelectedNode(nodeFront, "animation-panel"),
        }
      )
    );
  }
}

module.exports = AnimationTarget;
PK
!<C�뢔�cchrome/devtools/modules/devtools/client/inspector/animation/components/AnimationTimelineTickItem.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");

class AnimationTimeTickItem extends PureComponent {
  static get propTypes() {
    return {
      position: PropTypes.number.isRequired,
      timeTickLabel: PropTypes.string.isRequired,
    };
  }

  render() {
    const { position, timeTickLabel } = this.props;

    return dom.div(
      {
        className: "animation-timeline-tick-item",
        style: { left: `${ position }%` }
      },
      timeTickLabel
    );
  }
}

module.exports = AnimationTimeTickItem;
PK
!<�$���cchrome/devtools/modules/devtools/client/inspector/animation/components/AnimationTimelineTickList.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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 { connect } = require("devtools/client/shared/vendor/react-redux");
const ReactDOM = require("devtools/client/shared/vendor/react-dom");

const AnimationTimelineTickItem = createFactory(require("./AnimationTimelineTickItem"));

const { findOptimalTimeInterval } = require("../utils/utils");

// The minimum spacing between 2 time graduation headers in the timeline (px).
const TIME_GRADUATION_MIN_SPACING = 40;

class AnimationTimelineTickList extends PureComponent {
  static get propTypes() {
    return {
      sidebarWidth: PropTypes.number.isRequired,
      timeScale: PropTypes.object.isRequired,
    };
  }

  constructor(props) {
    super(props);

    this.state = {
      tickList: [],
    };
  }

  componentDidMount() {
    this.updateTickList();
  }

  componentWillReceiveProps(nextProps) {
    this.updateTickList();
  }

  shouldComponentUpdate(nextProps, nextState) {
    if (this.state.tickList.length !== nextState.tickList.length) {
      return true;
    }

    for (let i = 0; i < this.state.tickList.length; i++) {
      const currentTickItem = this.state.tickList[i];
      const nextTickItem = nextState.tickList[i];

      if (currentTickItem.text !== nextTickItem.text) {
        return true;
      }
    }

    return false;
  }

  updateTickList() {
    const { timeScale } = this.props;
    const tickListEl = ReactDOM.findDOMNode(this);
    const width = tickListEl.offsetWidth;
    const animationDuration = timeScale.getDuration();
    const minTimeInterval = TIME_GRADUATION_MIN_SPACING * animationDuration / width;
    const intervalLength = findOptimalTimeInterval(minTimeInterval);
    const intervalWidth = intervalLength * width / animationDuration;
    const tickCount = width / intervalWidth;
    const intervalPositionPercentage = 100 * intervalWidth / width;

    const tickList = [];
    for (let i = 0; i <= tickCount; i++) {
      const position = i * intervalPositionPercentage;
      const timeTickLabel =
        timeScale.formatTime(timeScale.distanceToRelativeTime(position));
      tickList.push({ position, timeTickLabel });
    }

    this.setState({ tickList });
  }

  render() {
    const { tickList } = this.state;

    return dom.div(
      {
        className: "animation-timeline-tick-list"
      },
      tickList.map(tickItem => AnimationTimelineTickItem(tickItem))
    );
  }
}

const mapStateToProps = state => {
  return {
    sidebarWidth: state.animations.sidebarSize.width
  };
};

module.exports = connect(mapStateToProps)(AnimationTimelineTickList);
PK
!<�����
�
Mchrome/devtools/modules/devtools/client/inspector/animation/components/App.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const { connect } = require("devtools/client/shared/vendor/react-redux");

const AnimationDetailContainer = createFactory(require("./AnimationDetailContainer"));
const AnimationListContainer = createFactory(require("./AnimationListContainer"));
const NoAnimationPanel = createFactory(require("./NoAnimationPanel"));
const SplitBox = createFactory(require("devtools/client/shared/components/splitter/SplitBox"));

class App extends PureComponent {
  static get propTypes() {
    return {
      animations: PropTypes.arrayOf(PropTypes.object).isRequired,
      detailVisibility: PropTypes.bool.isRequired,
      emitEventForTest: PropTypes.func.isRequired,
      getAnimatedPropertyMap: PropTypes.func.isRequired,
      getComputedStyle: PropTypes.func.isRequired,
      getNodeFromActor: PropTypes.func.isRequired,
      onHideBoxModelHighlighter: PropTypes.func.isRequired,
      onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
      selectAnimation: PropTypes.func.isRequired,
      setDetailVisibility: PropTypes.func.isRequired,
      setSelectedNode: PropTypes.func.isRequired,
      simulateAnimation: PropTypes.func.isRequired,
      toggleElementPicker: PropTypes.func.isRequired,
    };
  }

  shouldComponentUpdate(nextProps, nextState) {
    return this.props.animations.length !== 0 || nextProps.animations.length !== 0;
  }

  render() {
    const {
      animations,
      detailVisibility,
      emitEventForTest,
      getAnimatedPropertyMap,
      getComputedStyle,
      getNodeFromActor,
      onHideBoxModelHighlighter,
      onShowBoxModelHighlighterForNode,
      selectAnimation,
      setDetailVisibility,
      setSelectedNode,
      simulateAnimation,
      toggleElementPicker,
    } = this.props;

    return dom.div(
      {
        id: "animation-container",
        className: detailVisibility ? "animation-detail-visible" : "",
      },
      animations.length ?
      SplitBox({
        className: "animation-container-splitter",
        endPanel: AnimationDetailContainer(
          {
            emitEventForTest,
            getAnimatedPropertyMap,
            getComputedStyle,
            setDetailVisibility,
            simulateAnimation,
          }
        ),
        endPanelControl: true,
        initialHeight: "50%",
        splitterSize: 1,
        startPanel: AnimationListContainer(
          {
            animations,
            emitEventForTest,
            getAnimatedPropertyMap,
            getNodeFromActor,
            onHideBoxModelHighlighter,
            onShowBoxModelHighlighterForNode,
            selectAnimation,
            setSelectedNode,
            simulateAnimation,
          }
        ),
        vert: false,
      })
      :
      NoAnimationPanel(
        {
          toggleElementPicker
        }
      )
    );
  }
}

const mapStateToProps = state => {
  return {
    animations: state.animations.animations,
    detailVisibility: state.animations.detailVisibility,
  };
};

module.exports = connect(mapStateToProps)(App);
PK
!<<��.cchrome/devtools/modules/devtools/client/inspector/animation/components/KeyframesProgressTickItem.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");

class KeyframesProgressTickItem extends PureComponent {
  static get propTypes() {
    return {
      direction: PropTypes.string.isRequired,
      position: PropTypes.number.isRequired,
      progressTickLabel: PropTypes.string.isRequired,
    };
  }

  render() {
    const {
      direction,
      position,
      progressTickLabel,
    } = this.props;

    return dom.div(
      {
        className: `keyframes-progress-tick-item ${ direction }`,
        style: { [direction]: `${ position }%` }
      },
      progressTickLabel
    );
  }
}

module.exports = KeyframesProgressTickItem;
PK
!<�%6���cchrome/devtools/modules/devtools/client/inspector/animation/components/KeyframesProgressTickList.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/vendor/react");
const dom = require("devtools/client/shared/vendor/react-dom-factories");

const KeyframesProgressTickItem = createFactory(require("./KeyframesProgressTickItem"));
const { getFormatStr } = require("../utils/l10n");

class KeyframesProgressTickList extends PureComponent {
  render() {
    return dom.div(
      {
        className: "keyframes-progress-tick-list"
      },
      [0, 50, 100].map(progress => {
        const direction = progress === 100 ? "right" : "left";
        const position = progress === 100 ? 0 : progress;
        const progressTickLabel =
          getFormatStr("detail.propertiesHeader.percentage", progress);

        return KeyframesProgressTickItem(
          {
            direction,
            position,
            progressTickLabel,
          }
        );
      })
    );
  }
}

module.exports = KeyframesProgressTickList;
PK
!<�v���Zchrome/devtools/modules/devtools/client/inspector/animation/components/NoAnimationPanel.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const { connect } = require("devtools/client/shared/vendor/react-redux");
const { LocalizationHelper } = require("devtools/shared/l10n");

const L10N =
  new LocalizationHelper("devtools/client/locales/animationinspector.properties");

class NoAnimationPanel extends PureComponent {
  static get propTypes() {
    return {
      elementPickerEnabled: PropTypes.bool.isRequired,
      toggleElementPicker: PropTypes.func.isRequired,
    };
  }

  shouldComponentUpdate(nextProps, nextState) {
    return this.props.elementPickerEnabled != nextProps.elementPickerEnabled;
  }

  render() {
    const { elementPickerEnabled, toggleElementPicker } = this.props;

    return dom.div(
      {
        className: "animation-error-message devtools-sidepanel-no-result"
      },
      dom.p(
        null,
        L10N.getStr("panel.noAnimation")
      ),
      dom.button(
        {
          className: "animation-element-picker devtools-button" +
                     (elementPickerEnabled ? " checked" : ""),
          "data-standalone": true,
          onClick: toggleElementPicker
        }
      )
    );
  }
}

const mapStateToProps = state => {
  return {
    elementPickerEnabled: state.animations.elementPickerEnabled
  };
};

module.exports = connect(mapStateToProps)(NoAnimationPanel);
PK
!<����vv]chrome/devtools/modules/devtools/client/inspector/animation/components/graph/AnimationName.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");

class AnimationName extends PureComponent {
  static get propTypes() {
    return {
      animation: PropTypes.object.isRequired,
    };
  }

  render() {
    const {
      animation,
    } = this.props;

    return dom.svg(
      {
        className: "animation-name",
      },
      dom.text(
        {
          y: "50%",
          x: "100%"
        },
        animation.state.name
      )
    );
  }
}

module.exports = AnimationName;
PK
!<��J5��bchrome/devtools/modules/devtools/client/inspector/animation/components/graph/ComputedTimingPath.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/vendor/react-prop-types");
const dom = require("devtools/client/shared/vendor/react-dom-factories");

const { SummaryGraphHelper, toPathString } = require("../../utils/graph-helper");
const TimingPath = require("./TimingPath");

class ComputedTimingPath extends TimingPath {
  static get propTypes() {
    return {
      animation: PropTypes.object.isRequired,
      durationPerPixel: PropTypes.number.isRequired,
      keyframes: PropTypes.object.isRequired,
      opacity: PropTypes.number.isRequired,
      simulateAnimation: PropTypes.func.isRequired,
      totalDuration: PropTypes.number.isRequired,
    };
  }

  render() {
    const {
      animation,
      durationPerPixel,
      keyframes,
      opacity,
      simulateAnimation,
      totalDuration,
    } = this.props;

    const { state } = animation;
    const effectTiming = Object.assign({}, state, {
      iterations: state.iterationCount ? state.iterationCount : Infinity
    });

    // Create new keyframes for opacity as computed style.
    // The reason why we use computed value instead of computed timing progress is to
    // include the easing in keyframes as well. Although the computed timing progress
    // is not affected by the easing in keyframes at all, computed value reflects that.
    const frames = keyframes.map(keyframe => {
      return {
        opacity: keyframe.offset,
        offset: keyframe.offset,
        easing: keyframe.easing
      };
    });
    const simulatedAnimation = simulateAnimation(frames, effectTiming, true);
    const simulatedElement = simulatedAnimation.effect.target;
    const win = simulatedElement.ownerGlobal;
    const endTime = simulatedAnimation.effect.getComputedTiming().endTime;

    // Set the underlying opacity to zero so that if we sample the animation's output
    // during the delay phase and it is not filling backwards, we get zero.
    simulatedElement.style.opacity = 0;

    const getValueFunc = time => {
      if (time < 0) {
        return { x: time, y: 0 };
      }

      simulatedAnimation.currentTime = time < endTime ? time : endTime;
      return win.getComputedStyle(simulatedElement).opacity;
    };

    const toPathStringFunc = segments => {
      const firstSegment = segments[0];
      let pathString = `M${ firstSegment.x },0 `;
      pathString += toPathString(segments);
      const lastSegment = segments[segments.length - 1];
      pathString += `L${ lastSegment.x },0 Z`;
      return pathString;
    };

    const helper = new SummaryGraphHelper(state, keyframes,
                                          totalDuration, durationPerPixel,
                                          getValueFunc, toPathStringFunc);
    const offset = state.previousStartTime ? state.previousStartTime : 0;

    return dom.g(
      {
        className: "animation-computed-timing-path",
        style: { opacity },
        transform: `translate(${ offset })`
      },
      super.renderGraph(state, helper)
    );
  }
}

module.exports = ComputedTimingPath;
PK
!<SY���Ychrome/devtools/modules/devtools/client/inspector/animation/components/graph/DelaySign.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");

class DelaySign extends PureComponent {
  static get propTypes() {
    return {
      animation: PropTypes.object.isRequired,
      timeScale: PropTypes.object.isRequired,
    };
  }

  render() {
    const {
      animation,
      timeScale,
    } = this.props;

    const { state } = animation;
    const startTime = (state.previousStartTime || 0) - timeScale.minStartTime
                      + (state.delay < 0 ? state.delay : 0);
    const offset = startTime / timeScale.getDuration() * 100;
    const width = Math.abs(state.delay) / timeScale.getDuration() * 100;

    const delayClass = state.delay < 0 ? "negative" : "";
    const fillClass = state.fill === "both" || state.fill === "backwards" ? "fill" : "";

    return dom.div(
      {
        className: `animation-delay-sign ${ delayClass } ${ fillClass }`,
        style: {
          width: `${ width }%`,
          left: `${ offset }%`,
        },
      }
    );
  }
}

module.exports = DelaySign;
PK
!<�(ד		`chrome/devtools/modules/devtools/client/inspector/animation/components/graph/EffectTimingPath.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/vendor/react-prop-types");
const dom = require("devtools/client/shared/vendor/react-dom-factories");

const { SummaryGraphHelper, toPathString } = require("../../utils/graph-helper");
const TimingPath = require("./TimingPath");

class EffectTimingPath extends TimingPath {
  static get propTypes() {
    return {
      animation: PropTypes.object.isRequired,
      durationPerPixel: PropTypes.number.isRequired,
      simulateAnimation: PropTypes.func.isRequired,
      totalDuration: PropTypes.number.isRequired,
    };
  }

  render() {
    const {
      animation,
      durationPerPixel,
      simulateAnimation,
      totalDuration,
    } = this.props;

    const { state } = animation;
    const effectTiming = Object.assign({}, state, {
      iterations: state.iterationCount ? state.iterationCount : Infinity
    });

    const simulatedAnimation = simulateAnimation(null, effectTiming, false);
    const endTime = simulatedAnimation.effect.getComputedTiming().endTime;

    const getValueFunc = time => {
      if (time < 0) {
        return { x: time, y: 0 };
      }

      simulatedAnimation.currentTime = time < endTime ? time : endTime;
      return Math.max(simulatedAnimation.effect.getComputedTiming().progress, 0);
    };

    const toPathStringFunc = segments => {
      const firstSegment = segments[0];
      let pathString = `M${ firstSegment.x },0 `;
      pathString += toPathString(segments);
      const lastSegment = segments[segments.length - 1];
      pathString += `L${ lastSegment.x },0`;
      return pathString;
    };

    const helper = new SummaryGraphHelper(state, null,
                                          totalDuration, durationPerPixel,
                                          getValueFunc, toPathStringFunc);
    const offset = state.previousStartTime ? state.previousStartTime : 0;

    return dom.g(
      {
        className: "animation-effect-timing-path",
        transform: `translate(${ offset })`
      },
      super.renderGraph(state, helper)
    );
  }
}

module.exports = EffectTimingPath;
PK
!<��!p��\chrome/devtools/modules/devtools/client/inspector/animation/components/graph/EndDelaySign.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");

class EndDelaySign extends PureComponent {
  static get propTypes() {
    return {
      animation: PropTypes.object.isRequired,
      timeScale: PropTypes.object.isRequired,
    };
  }

  render() {
    const {
      animation,
      timeScale,
    } = this.props;

    const { state } = animation;
    const startTime = (state.previousStartTime || 0) - timeScale.minStartTime;
    const endTime = state.duration * state.iterationCount + state.delay;
    const endDelay = state.endDelay < 0 ? state.endDelay : 0;
    const offset = (startTime + endTime + endDelay) / timeScale.getDuration() * 100;
    const width = Math.abs(state.endDelay) / timeScale.getDuration() * 100;

    const endDelayClass = state.endDelay < 0 ? "negative" : "";
    const fillClass = state.fill === "both" || state.fill === "forwards" ? "fill" : "";

    return dom.div(
      {
        className: `animation-end-delay-sign ${ endDelayClass } ${ fillClass }`,
        style: {
          width: `${ width }%`,
          left: `${ offset }%`,
        },
      }
    );
  }
}

module.exports = EndDelaySign;
PK
!<�룄�achrome/devtools/modules/devtools/client/inspector/animation/components/graph/NegativeDelayPath.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/vendor/react-prop-types");
const dom = require("devtools/client/shared/vendor/react-dom-factories");

const NegativePath = require("./NegativePath");

class NegativeDelayPath extends NegativePath {
  static get propTypes() {
    return {
      animation: PropTypes.object.isRequired,
      durationPerPixel: PropTypes.number.isRequired,
      keyframes: PropTypes.object.isRequired,
      simulateAnimation: PropTypes.func.isRequired,
      totalDuration: PropTypes.number.isRequired,
    };
  }

  constructor(props) {
    props.className = "animation-negative-delay-path";
    super(props);
  }

  renderGraph(state, helper) {
    const startTime = state.delay;
    const endTime = 0;
    const segments = helper.createPathSegments(startTime, endTime);

    return dom.path(
      {
        d: helper.toPathString(segments),
      }
    );
  }
}

module.exports = NegativeDelayPath;
PK
!<�)���dchrome/devtools/modules/devtools/client/inspector/animation/components/graph/NegativeEndDelayPath.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/vendor/react-prop-types");
const dom = require("devtools/client/shared/vendor/react-dom-factories");

const NegativePath = require("./NegativePath");

class NegativeEndDelayPath extends NegativePath {
  static get propTypes() {
    return {
      animation: PropTypes.object.isRequired,
      durationPerPixel: PropTypes.number.isRequired,
      keyframes: PropTypes.object.isRequired,
      simulateAnimation: PropTypes.func.isRequired,
      totalDuration: PropTypes.number.isRequired,
    };
  }

  constructor(props) {
    props.className = "animation-negative-end-delay-path";
    super(props);
  }

  renderGraph(state, helper) {
    const endTime = state.delay + state.iterationCount * state.duration;
    const startTime = endTime + state.endDelay;
    const segments = helper.createPathSegments(startTime, endTime);

    return dom.path(
      {
        d: helper.toPathString(segments),
      }
    );
  }
}

module.exports = NegativeEndDelayPath;
PK
!<8}�\chrome/devtools/modules/devtools/client/inspector/animation/components/graph/NegativePath.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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 { SummaryGraphHelper, toPathString } = require("../../utils/graph-helper");

class NegativePath extends PureComponent {
  static get propTypes() {
    return {
      animation: PropTypes.object.isRequired,
      className: PropTypes.string.isRequired,
      durationPerPixel: PropTypes.number.isRequired,
      keyframes: PropTypes.object.isRequired,
      simulateAnimation: PropTypes.func.isRequired,
      totalDuration: PropTypes.number.isRequired,
    };
  }

  render() {
    const {
      animation,
      className,
      durationPerPixel,
      keyframes,
      simulateAnimation,
      totalDuration,
    } = this.props;

    const { state } = animation;
    const effectTiming = Object.assign({}, state, {
      fill: "both",
      iterations: state.iterationCount ? state.iterationCount : Infinity
    });

    // Create new keyframes for opacity as computed style.
    // The reason why we use computed value instead of computed timing progress is to
    // include the easing in keyframes as well. Although the computed timing progress
    // is not affected by the easing in keyframes at all, computed value reflects that.
    const frames = keyframes.map(keyframe => {
      return {
        opacity: keyframe.offset,
        offset: keyframe.offset,
        easing: keyframe.easing
      };
    });

    const simulatedAnimation = simulateAnimation(frames, effectTiming, true);
    const simulatedElement = simulatedAnimation.effect.target;
    const win = simulatedElement.ownerGlobal;

    // Set the underlying opacity to zero so that if we sample the animation's output
    // during the delay phase and it is not filling backwards, we get zero.
    simulatedElement.style.opacity = 0;

    const getValueFunc = time => {
      simulatedAnimation.currentTime = time;
      return win.getComputedStyle(simulatedElement).opacity;
    };

    const toPathStringFunc = segments => {
      const firstSegment = segments[0];
      let pathString = `M${ firstSegment.x },0 `;
      pathString += toPathString(segments);
      const lastSegment = segments[segments.length - 1];
      pathString += `L${ lastSegment.x },0 Z`;
      return pathString;
    };

    const helper = new SummaryGraphHelper(state, keyframes,
                                          totalDuration, durationPerPixel,
                                          getValueFunc, toPathStringFunc);
    const offset = state.previousStartTime ? state.previousStartTime : 0;

    return dom.g(
      {
        className,
        transform: `translate(${ offset })`
      },
      this.renderGraph(state, helper)
    );
  }
}

module.exports = NegativePath;
PK
!<7�H���\chrome/devtools/modules/devtools/client/inspector/animation/components/graph/SummaryGraph.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");

const AnimationName = createFactory(require("./AnimationName"));
const DelaySign = createFactory(require("./DelaySign"));
const EndDelaySign = createFactory(require("./EndDelaySign"));
const SummaryGraphPath = createFactory(require("./SummaryGraphPath"));

const { getFormattedTitle, getFormatStr, getStr, numberWithDecimals } = require("../../utils/l10n");

class SummaryGraph extends PureComponent {
  static get propTypes() {
    return {
      animation: PropTypes.object.isRequired,
      emitEventForTest: PropTypes.func.isRequired,
      getAnimatedPropertyMap: PropTypes.func.isRequired,
      selectAnimation: PropTypes.func.isRequired,
      simulateAnimation: PropTypes.func.isRequired,
      timeScale: PropTypes.object.isRequired,
    };
  }

  constructor(props) {
    super(props);

    this.onClick = this.onClick.bind(this);
  }

  onClick() {
    this.props.selectAnimation(this.props.animation);
  }

  getTitleText(state) {
    const getTime =
      time => getFormatStr("player.timeLabel", numberWithDecimals(time / 1000, 2));

    let text = "";

    // Adding the name.
    text += getFormattedTitle(state);
    text += "\n";

    // Adding the delay.
    if (state.delay) {
      text += getStr("player.animationDelayLabel") + " ";
      text += getTime(state.delay);
      text += "\n";
    }

    // Adding the duration.
    text += getStr("player.animationDurationLabel") + " ";
    text += getTime(state.duration);
    text += "\n";

    // Adding the endDelay.
    if (state.endDelay) {
      text += getStr("player.animationEndDelayLabel") + " ";
      text += getTime(state.endDelay);
      text += "\n";
    }

    // Adding the iteration count (the infinite symbol, or an integer).
    if (state.iterationCount !== 1) {
      text += getStr("player.animationIterationCountLabel") + " ";
      text += state.iterationCount || getStr("player.infiniteIterationCountText");
      text += "\n";
    }

    // Adding the iteration start.
    if (state.iterationStart !== 0) {
      const iterationStartTime = state.iterationStart * state.duration / 1000;
      text += getFormatStr("player.animationIterationStartLabel",
                           state.iterationStart,
                           numberWithDecimals(iterationStartTime, 2));
      text += "\n";
    }

    // Adding the easing if it is not "linear".
    if (state.easing && state.easing !== "linear") {
      text += getStr("player.animationOverallEasingLabel") + " ";
      text += state.easing;
      text += "\n";
    }

    // Adding the fill mode.
    if (state.fill && state.fill !== "none") {
      text += getStr("player.animationFillLabel") + " ";
      text += state.fill;
      text += "\n";
    }

    // Adding the direction mode if it is not "normal".
    if (state.direction && state.direction !== "normal") {
      text += getStr("player.animationDirectionLabel") + " ";
      text += state.direction;
      text += "\n";
    }

    // Adding the playback rate if it's different than 1.
    if (state.playbackRate !== 1) {
      text += getStr("player.animationRateLabel") + " ";
      text += state.playbackRate;
      text += "\n";
    }

    // Adding the animation-timing-function
    // if it is not "ease" which is default value for CSS Animations.
    if (state.animationTimingFunction && state.animationTimingFunction !== "ease") {
      text += getStr("player.animationTimingFunctionLabel") + " ";
      text += state.animationTimingFunction;
      text += "\n";
    }

    // Adding a note that the animation is running on the compositor thread if
    // needed.
    if (state.propertyState) {
      if (state.propertyState.every(propState => propState.runningOnCompositor)) {
        text += getStr("player.allPropertiesOnCompositorTooltip");
      } else if (state.propertyState.some(propState => propState.runningOnCompositor)) {
        text += getStr("player.somePropertiesOnCompositorTooltip");
      }
    } else if (state.isRunningOnCompositor) {
      text += getStr("player.runningOnCompositorTooltip");
    }

    return text;
  }

  render() {
    const {
      animation,
      emitEventForTest,
      getAnimatedPropertyMap,
      simulateAnimation,
      timeScale,
    } = this.props;

    return dom.div(
      {
        className: "animation-summary-graph" +
                   (animation.state.isRunningOnCompositor ? " compositor" : ""),
        onClick: this.onClick,
        title: this.getTitleText(animation.state),
      },
      SummaryGraphPath(
        {
          animation,
          emitEventForTest,
          getAnimatedPropertyMap,
          simulateAnimation,
          timeScale,
        }
      ),
      animation.state.delay ?
        DelaySign(
          {
            animation,
            timeScale,
          }
        )
      :
      null,
      animation.state.iterationCount && animation.state.endDelay ?
        EndDelaySign(
          {
            animation,
            timeScale,
          }
        )
      :
      null,
      animation.state.name ?
        AnimationName(
          {
            animation
          }
        )
      :
      null
    );
  }
}

module.exports = SummaryGraph;
PK
!<Q=<Q��`chrome/devtools/modules/devtools/client/inspector/animation/components/graph/SummaryGraphPath.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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 ReactDOM = require("devtools/client/shared/vendor/react-dom");

const ComputedTimingPath = createFactory(require("./ComputedTimingPath"));
const EffectTimingPath = createFactory(require("./EffectTimingPath"));
const NegativeDelayPath = createFactory(require("./NegativeDelayPath"));
const NegativeEndDelayPath = createFactory(require("./NegativeEndDelayPath"));
const { DEFAULT_GRAPH_HEIGHT } = require("../../utils/graph-helper");

// Minimum opacity for semitransparent fill color for keyframes's easing graph.
const MIN_KEYFRAMES_EASING_OPACITY = 0.5;

class SummaryGraphPath extends PureComponent {
  static get propTypes() {
    return {
      animation: PropTypes.object.isRequired,
      emitEventForTest: PropTypes.func.isRequired,
      getAnimatedPropertyMap: PropTypes.object.isRequired,
      simulateAnimation: PropTypes.func.isRequired,
      timeScale: PropTypes.object.isRequired,
    };
  }

  constructor(props) {
    super(props);

    this.state = {
      // Duration which can display in one pixel.
      durationPerPixel: 0,
      // List of keyframe which consists by only offset and easing.
      keyframesList: [],
    };
  }

  componentDidMount() {
    this.updateState(this.props.animation);
  }

  componentWillReceiveProps(nextProps) {
    this.updateState(nextProps.animation);
  }

  /**
   * Return animatable keyframes list which has only offset and easing.
   * Also, this method remove duplicate keyframes.
   * For example, if the given animatedPropertyMap is,
   * [
   *   {
   *     key: "color",
   *     values: [
   *       {
   *         offset: 0,
   *         easing: "ease",
   *         value: "rgb(255, 0, 0)",
   *       },
   *       {
   *         offset: 1,
   *         value: "rgb(0, 255, 0)",
   *       },
   *     ],
   *   },
   *   {
   *     key: "opacity",
   *     values: [
   *       {
   *         offset: 0,
   *         easing: "ease",
   *         value: 0,
   *       },
   *       {
   *         offset: 1,
   *         value: 1,
   *       },
   *     ],
   *   },
   * ]
   *
   * then this method returns,
   * [
   *   [
   *     {
   *       offset: 0,
   *       easing: "ease",
   *     },
   *     {
   *       offset: 1,
   *     },
   *   ],
   * ]
   *
   * @param {Map} animated property map
   *        which can get form getAnimatedPropertyMap in animation.js
   * @return {Array} list of keyframes which has only easing and offset.
   */
  getOffsetAndEasingOnlyKeyframes(animatedPropertyMap) {
    return [...animatedPropertyMap.values()].filter((keyframes1, i, self) => {
      return i !== self.findIndex((keyframes2, j) => {
        return this.isOffsetAndEasingKeyframesEqual(keyframes1, keyframes2) ? j : -1;
      });
    }).map(keyframes => {
      return keyframes.map(keyframe => {
        return { easing: keyframe.easing, offset: keyframe.offset };
      });
    });
  }

  getTotalDuration(animation, timeScale) {
    return animation.state.playbackRate * timeScale.getDuration();
  }

  /**
   * Return true if given keyframes have same length, offset and easing.
   *
   * @param {Array} keyframes1
   * @param {Array} keyframes2
   * @return {Boolean} true: equals
   */
  isOffsetAndEasingKeyframesEqual(keyframes1, keyframes2) {
    if (keyframes1.length !== keyframes2.length) {
      return false;
    }

    for (let i = 0; i < keyframes1.length; i++) {
      const keyframe1 = keyframes1[i];
      const keyframe2 = keyframes2[i];

      if (keyframe1.offset !== keyframe2.offset ||
          keyframe1.easing !== keyframe2.easing) {
        return false;
      }
    }

    return true;
  }

  async updateState(animation) {
    const {
      emitEventForTest,
      getAnimatedPropertyMap,
      timeScale,
    } = this.props;

    const animatedPropertyMap = await getAnimatedPropertyMap(animation);
    const keyframesList = this.getOffsetAndEasingOnlyKeyframes(animatedPropertyMap);

    const thisEl = ReactDOM.findDOMNode(this);
    const totalDuration = this.getTotalDuration(animation, timeScale);
    const durationPerPixel = totalDuration / thisEl.parentNode.clientWidth;

    this.setState({ durationPerPixel, keyframesList });

    emitEventForTest("animation-summary-graph-rendered");
  }

  render() {
    const { durationPerPixel, keyframesList } = this.state;

    if (!durationPerPixel) {
      return dom.svg();
    }

    const {
      animation,
      simulateAnimation,
      timeScale,
    } = this.props;

    const totalDuration = this.getTotalDuration(animation, timeScale);
    const startTime = timeScale.minStartTime;
    const opacity = Math.max(1 / keyframesList.length, MIN_KEYFRAMES_EASING_OPACITY);

    return dom.svg(
      {
        className: "animation-summary-graph-path",
        preserveAspectRatio: "none",
        viewBox: `${ startTime } -${ DEFAULT_GRAPH_HEIGHT } `
                 + `${ totalDuration } ${ DEFAULT_GRAPH_HEIGHT }`,
      },
      keyframesList.map(keyframes =>
        ComputedTimingPath(
          {
            animation,
            durationPerPixel,
            keyframes,
            opacity,
            simulateAnimation,
            totalDuration,
          }
        )
      ),
      animation.state.easing !== "linear" ?
        EffectTimingPath(
          {
            animation,
            durationPerPixel,
            simulateAnimation,
            totalDuration,
          }
        )
      :
      null,
      animation.state.delay < 0 ?
        keyframesList.map(keyframes => {
          return NegativeDelayPath(
            {
              animation,
              durationPerPixel,
              keyframes,
              simulateAnimation,
              totalDuration,
            }
          );
        })
      :
      null,
      animation.state.iterationCount && animation.state.endDelay < 0 ?
        keyframesList.map(keyframes => {
          return NegativeEndDelayPath(
            {
              animation,
              durationPerPixel,
              keyframes,
              simulateAnimation,
              totalDuration,
            }
          );
        })
      :
      null
    );
  }
}

module.exports = SummaryGraphPath;
PK
!<r�VP�1�1Zchrome/devtools/modules/devtools/client/inspector/animation/components/graph/TimingPath.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/vendor/react");
const dom = require("devtools/client/shared/vendor/react-dom-factories");

// Show max 10 iterations for infinite animations
// to give users a clue that the animation does repeat.
const MAX_INFINITE_ANIMATIONS_ITERATIONS = 10;

class TimingPath extends PureComponent {
  /**
   * Render a graph of given parameters and return as <path> element list.
   *
   * @param {Object} state
   *        State of animation.
   * @param {SummaryGraphHelper} helper
   *        Instance of SummaryGraphHelper.
   * @return {Array}
   *         list of <path> element.
   */
  renderGraph(state, helper) {
    // Starting time of main iteration.
    let mainIterationStartTime = 0;
    let iterationStart = state.iterationStart;
    let iterationCount = state.iterationCount ? state.iterationCount : Infinity;

    const pathList = [];

    // Append delay.
    if (state.delay > 0) {
      this.renderDelay(pathList, state, helper);
      mainIterationStartTime = state.delay;
    } else {
      const negativeDelayCount = -state.delay / state.duration;
      // Move to forward the starting point for negative delay.
      iterationStart += negativeDelayCount;
      // Consume iteration count by negative delay.
      if (iterationCount !== Infinity) {
        iterationCount -= negativeDelayCount;
      }
    }

    // Append 1st section of iterations,
    // This section is only useful in cases where iterationStart has decimals.
    // e.g.
    // if { iterationStart: 0.25, iterations: 3 }, firstSectionCount is 0.75.
    const firstSectionCount = iterationStart % 1 === 0
                            ? 0
                            : Math.min(iterationCount, 1) - iterationStart % 1;
    if (firstSectionCount) {
      this.renderFirstIteration(pathList, state,
                                mainIterationStartTime, firstSectionCount, helper);
    }

    if (iterationCount === Infinity) {
      // If the animation repeats infinitely,
      // we fill the remaining area with iteration paths.
      this.renderInfinity(pathList, state,
                          mainIterationStartTime, firstSectionCount, helper);
    } else {
      // Otherwise, we show remaining iterations, endDelay and fill.

      // Append forwards fill-mode.
      if (state.fill === "both" || state.fill === "forwards") {
        this.renderForwardsFill(pathList, state,
                                mainIterationStartTime, iterationCount, helper);
      }

      // Append middle section of iterations.
      // e.g.
      // if { iterationStart: 0.25, iterations: 3 }, middleSectionCount is 2.
      const middleSectionCount = Math.floor(iterationCount - firstSectionCount);
      this.renderMiddleIterations(pathList, state, mainIterationStartTime,
                                  firstSectionCount, middleSectionCount, helper);

      // Append last section of iterations, if there is remaining iteration.
      // e.g.
      // if { iterationStart: 0.25, iterations: 3 }, lastSectionCount is 0.25.
      const lastSectionCount = iterationCount - middleSectionCount - firstSectionCount;
      if (lastSectionCount) {
        this.renderLastIteration(pathList, state, mainIterationStartTime,
                                 firstSectionCount, middleSectionCount,
                                 lastSectionCount, helper);
      }

      // Append endDelay.
      if (state.endDelay > 0) {
        this.renderEndDelay(pathList, state,
                            mainIterationStartTime, iterationCount, helper);
      }
    }
    return pathList;
  }

  /**
   * Render 'delay' part in animation and add a <path> element to given pathList.
   *
   * @param {Array} pathList
   *        Add rendered <path> element to this array.
   * @param {Object} state
   *        State of animation.
   * @param {SummaryGraphHelper} helper
   *        Instance of SummaryGraphHelper.
   */
  renderDelay(pathList, state, helper) {
    const startSegment = helper.getSegment(0);
    const endSegment = { x: state.delay, y: startSegment.y };
    const segments = [startSegment, endSegment];
    pathList.push(
      dom.path(
        {
          className: "animation-delay-path",
          d: helper.toPathString(segments),
        }
      )
    );
  }

  /**
   * Render 1st section of iterations and add a <path> element to given pathList.
   * This section is only useful in cases where iterationStart has decimals.
   *
   * @param {Array} pathList
   *        Add rendered <path> element to this array.
   * @param {Object} state
   *        State of animation.
   * @param {Number} mainIterationStartTime
   *        Start time of main iteration.
   * @param {Number} firstSectionCount
   *        Iteration count of first section.
   * @param {SummaryGraphHelper} helper
   *        Instance of SummaryGraphHelper.
   */
  renderFirstIteration(pathList, state, mainIterationStartTime,
                       firstSectionCount, helper) {
    const startTime = mainIterationStartTime;
    const endTime = startTime + firstSectionCount * state.duration;
    const segments = helper.createPathSegments(startTime, endTime);
    pathList.push(
      dom.path(
        {
          className: "animation-iteration-path",
          d: helper.toPathString(segments),
        }
      )
    );
  }

  /**
   * Render middle iterations and add <path> elements to given pathList.
   *
   * @param {Array} pathList
   *        Add rendered <path> elements to this array.
   * @param {Object} state
   *        State of animation.
   * @param {Number} mainIterationStartTime
   *        Starting time of main iteration.
   * @param {Number} firstSectionCount
   *        Iteration count of first section.
   * @param {Number} middleSectionCount
   *        Iteration count of middle section.
   * @param {SummaryGraphHelper} helper
   *        Instance of SummaryGraphHelper.
   */
  renderMiddleIterations(pathList, state, mainIterationStartTime,
                         firstSectionCount, middleSectionCount, helper) {
    const offset = mainIterationStartTime + firstSectionCount * state.duration;
    for (let i = 0; i < middleSectionCount; i++) {
      // Get the path segments of each iteration.
      const startTime = offset + i * state.duration;
      const endTime = startTime + state.duration;
      const segments = helper.createPathSegments(startTime, endTime);
      pathList.push(
        dom.path(
          {
            className: "animation-iteration-path",
            d: helper.toPathString(segments),
          }
        )
      );
    }
  }

  /**
   * Render last section of iterations and add a <path> element to given pathList.
   * This section is only useful in cases where iterationStart has decimals.
   *
   * @param {Array} pathList
   *        Add rendered <path> elements to this array.
   * @param {Object} state
   *        State of animation.
   * @param {Number} mainIterationStartTime
   *        Starting time of main iteration.
   * @param {Number} firstSectionCount
   *        Iteration count of first section.
   * @param {Number} middleSectionCount
   *        Iteration count of middle section.
   * @param {Number} lastSectionCount
   *        Iteration count of last section.
   * @param {SummaryGraphHelper} helper
   *        Instance of SummaryGraphHelper.
   */
  renderLastIteration(pathList, state, mainIterationStartTime, firstSectionCount,
                      middleSectionCount, lastSectionCount, helper) {
    const startTime = mainIterationStartTime
                    + (firstSectionCount + middleSectionCount) * state.duration;
    const endTime = startTime + lastSectionCount * state.duration;
    const segments = helper.createPathSegments(startTime, endTime);
    pathList.push(
      dom.path(
        {
          className: "animation-iteration-path",
          d: helper.toPathString(segments),
        }
      )
    );
  }

  /**
   * Render infinity iterations and add <path> elements to given pathList.
   *
   * @param {Array} pathList
   *        Add rendered <path> elements to this array.
   * @param {Object} state
   *        State of animation.
   * @param {Number} mainIterationStartTime
   *        Starting time of main iteration.
   * @param {Number} firstSectionCount
   *        Iteration count of first section.
   * @param {SummaryGraphHelper} helper
   *        Instance of SummaryGraphHelper.
   */
  renderInfinity(pathList, state, mainIterationStartTime, firstSectionCount, helper) {
    // Calculate the number of iterations to display,
    // with a maximum of MAX_INFINITE_ANIMATIONS_ITERATIONS
    let uncappedInfinityIterationCount =
      (helper.totalDuration - firstSectionCount * state.duration) / state.duration;
    // If there is a small floating point error resulting in, e.g. 1.0000001
    // ceil will give us 2 so round first.
    uncappedInfinityIterationCount =
      parseFloat(uncappedInfinityIterationCount.toPrecision(6));
    const infinityIterationCount = Math.min(MAX_INFINITE_ANIMATIONS_ITERATIONS,
                                            Math.ceil(uncappedInfinityIterationCount));

    // Append first full iteration path.
    const firstStartTime =
      mainIterationStartTime + firstSectionCount * state.duration;
    const firstEndTime = firstStartTime + state.duration;
    const firstSegments = helper.createPathSegments(firstStartTime, firstEndTime);
    pathList.push(
      dom.path(
        {
          className: "animation-iteration-path",
          d: helper.toPathString(firstSegments),
        }
      )
    );

    // Append other iterations. We can copy first segments.
    const isAlternate = state.direction.match(/alternate/);
    for (let i = 1; i < infinityIterationCount; i++) {
      const startTime = firstStartTime + i * state.duration;
      let segments;
      if (isAlternate && i % 2) {
        // Copy as reverse.
        segments = firstSegments.map(segment => {
          return { x: firstEndTime - segment.x + startTime, y: segment.y };
        });
      } else {
        // Copy as is.
        segments = firstSegments.map(segment => {
          return { x: segment.x - firstStartTime + startTime, y: segment.y };
        });
      }
      pathList.push(
        dom.path(
          {
            className: "animation-iteration-path infinity",
            d: helper.toPathString(segments),
          }
        )
      );
    }
  }

  /**
   * Render 'endDelay' part in animation and add a <path> element to given pathList.
   *
   * @param {Array} pathList
   *        Add rendered <path> element to this array.
   * @param {Object} state
   *        State of animation.
   * @param {Number} mainIterationStartTime
   *        Starting time of main iteration.
   * @param {Number} iterationCount
   *        Iteration count of whole animation.
   * @param {SummaryGraphHelper} helper
   *        Instance of SummaryGraphHelper.
   */
  renderEndDelay(pathList, state, mainIterationStartTime, iterationCount, helper) {
    const startTime = mainIterationStartTime + iterationCount * state.duration;
    const startSegment = helper.getSegment(startTime);
    const endSegment = { x: startTime + state.endDelay, y: startSegment.y };
    pathList.push(
      dom.path(
        {
          className: "animation-enddelay-path",
          d: helper.toPathString([startSegment, endSegment]),
        }
      )
    );
  }

  /**
   * Render 'fill' for forwards part in animation and
   * add a <path> element to given pathList.
   *
   * @param {Array} pathList
   *        Add rendered <path> element to this array.
   * @param {Object} state
   *        State of animation.
   * @param {Number} mainIterationStartTime
   *        Starting time of main iteration.
   * @param {Number} iterationCount
   *        Iteration count of whole animation.
   * @param {SummaryGraphHelper} helper
   *        Instance of SummaryGraphHelper.
   */
  renderForwardsFill(pathList, state, mainIterationStartTime, iterationCount, helper) {
    const startTime = mainIterationStartTime + iterationCount * state.duration
                    + (state.endDelay > 0 ? state.endDelay : 0);
    const startSegment = helper.getSegment(startTime);
    const endSegment = { x: helper.totalDuration, y: startSegment.y };
    pathList.push(
      dom.path(
        {
          className: "animation-fill-forwards-path",
          d: helper.toPathString([startSegment, endSegment]),
        }
      )
    );
  }
}

module.exports = TimingPath;
PK
!<'��cchrome/devtools/modules/devtools/client/inspector/animation/components/keyframes-graph/ColorPath.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 dom = require("devtools/client/shared/vendor/react-dom-factories");

const {colorUtils} = require("devtools/shared/css/color.js");

const ComputedStylePath = require("./ComputedStylePath");

/* Count for linearGradient ID */
let LINEAR_GRADIENT_ID_COUNT = 0;

class ColorPath extends ComputedStylePath {
  constructor(props) {
    super(props);

    this.state = this.propToState(props);
  }

  componentWillReceiveProps(nextProps) {
    this.setState(this.propToState(nextProps));
  }

  getPropertyName() {
    return "color";
  }

  getPropertyValue(keyframe) {
    return keyframe.value;
  }

  propToState({ values }) {
    const maxObject = { distance: 0 };

    for (let i = 0; i < values.length - 1; i++) {
      const value1 = getRGBA(values[i].value);
      for (let j = i + 1; j < values.length; j++) {
        const value2 = getRGBA(values[j].value);
        const distance = getRGBADistance(value1, value2);

        if (maxObject.distance >= distance) {
          continue;
        }

        maxObject.distance = distance;
        maxObject.value1 = value1;
        maxObject.value2 = value2;
      }
    }

    const maxDistance = maxObject.distance;
    const baseValue =
      maxObject.value1 < maxObject.value2 ? maxObject.value1 : maxObject.value2;

    return { baseValue, maxDistance };
  }

  toSegmentValue(computedStyle) {
    const { baseValue, maxDistance } = this.state;
    const value = getRGBA(computedStyle);
    return getRGBADistance(baseValue, value) / maxDistance;
  }

  /**
   * Overide parent's method.
   */
  renderEasingHint() {
    const {
      easingHintStrokeWidth,
      graphHeight,
      totalDuration,
      values,
    } = this.props;

    const hints = [];

    for (let i = 0; i < values.length - 1; i++) {
      const startKeyframe = values[i];
      const endKeyframe = values[i + 1];
      const startTime = startKeyframe.offset * totalDuration;
      const endTime = endKeyframe.offset * totalDuration;

      const g = dom.g(
        {
          className: "hint"
        },
        dom.title({}, startKeyframe.easing),
        dom.rect(
          {
            x: startTime,
            y: -graphHeight,
            height: graphHeight,
            width: endTime - startTime,
            style: {
              "stroke-width": easingHintStrokeWidth,
            },
          }
        )
      );
      hints.push(g);
    }

    return hints;
  }

  /**
   * Overide parent's method.
   */
  renderPathSegments(segments) {
    for (const segment of segments) {
      segment.y = 1;
    }

    const lastSegment = segments[segments.length - 1];
    const id = `color-property-${ LINEAR_GRADIENT_ID_COUNT++ }`;
    const path = super.renderPathSegments(segments, { fill: `url(#${ id })` });
    const linearGradient = dom.linearGradient(
      { id },
      segments.map(segment => {
        return dom.stop(
          {
            "stopColor": segment.computedStyle,
            "offset": segment.x / lastSegment.x,
          }
        );
      })
    );

    return [path, linearGradient];
  }

  render() {
    return dom.g(
      {
        className: "color-path",
      },
      super.renderGraph()
    );
  }
}

/**
 * Parse given RGBA string.
 *
 * @param {String} colorString
 *        e.g. rgb(0, 0, 0) or rgba(0, 0, 0, 0.5) and so on.
 * @return {Object}
 *         RGBA {r: r, g: g, b: b, a: a}.
 */
function getRGBA(colorString) {
  const color = new colorUtils.CssColor(colorString);
  return color.getRGBATuple();
}

/**
 * Return the distance from give two RGBA.
 *
 * @param {Object} rgba1
 *        RGBA (format is same to getRGBA)
 * @param {Object} rgba2
 *        RGBA (format is same to getRGBA)
 * @return {Number}
 *         The range is 0 - 1.0.
 */
function getRGBADistance(rgba1, rgba2) {
  const startA = rgba1.a;
  const startR = rgba1.r * startA;
  const startG = rgba1.g * startA;
  const startB = rgba1.b * startA;
  const endA = rgba2.a;
  const endR = rgba2.r * endA;
  const endG = rgba2.g * endA;
  const endB = rgba2.b * endA;
  const diffA = startA - endA;
  const diffR = startR - endR;
  const diffG = startG - endG;
  const diffB = startB - endB;
  return Math.sqrt(diffA * diffA + diffR * diffR + diffG * diffG + diffB * diffB);
}

module.exports = ColorPath;
PK
!<��kchrome/devtools/modules/devtools/client/inspector/animation/components/keyframes-graph/ComputedStylePath.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");

const {
  createPathSegments,
  DEFAULT_DURATION_RESOLUTION,
  getPreferredProgressThresholdByKeyframes,
  toPathString,
} = require("../../utils/graph-helper");

/*
 * This class is an abstraction for computed style path of keyframes.
 * Subclass of this should implement the following methods:
 *
 * getPropertyName()
 *   Returns property name which will be animated.
 *   @return {String}
 *           e.g. opacity
 *
 * getPropertyValue(keyframe)
 *   Returns value which uses as animated keyframe value from given parameter.
 *   @param {Object} keyframe
 *   @return {String||Number}
 *           e.g. 0
 *
 * toSegmentValue(computedStyle)
 *   Convert computed style to segment value of graph.
 *   @param {String||Number}
 *          e.g. 0
 *   @return {Number}
 *          e.g. 0 (should be 0 - 1.0)
 */
class ComputedStylePath extends PureComponent {
  static get propTypes() {
    return {
      componentWidth: PropTypes.number.isRequired,
      easingHintStrokeWidth: PropTypes.number.isRequired,
      graphHeight: PropTypes.number.isRequired,
      simulateAnimation: PropTypes.func.isRequired,
      totalDuration: PropTypes.number.isRequired,
      values: PropTypes.array.isRequired,
    };
  }

  /**
   * Return an array containing the path segments between the given start and
   * end keyframe values.
   *
   * @param {Object} startValue
   *        Starting keyframe.
   * @param {Object} startValue
   *        Ending keyframe.
   * @return {Array}
   *         Array of path segment.
   *         [{x: {Number} time, y: {Number} segment value}, ...]
   */
  getPathSegments(startValue, endValue) {
    const {
      componentWidth,
      simulateAnimation,
      totalDuration,
    } = this.props;

    const propertyName = this.getPropertyName();
    const offsetDistance = endValue.offset - startValue.offset;
    const duration = offsetDistance * totalDuration;

    const keyframes = [startValue, endValue].map((keyframe, index) => {
      return {
        offset: index,
        easing: keyframe.easing,
        [getJsPropertyName(propertyName)]: this.getPropertyValue(keyframe),
      };
    });
    const effect = {
      duration,
      fill: "forwards",
    };
    const simulatedAnimation = simulateAnimation(keyframes, effect, true);
    const simulatedElement = simulatedAnimation.effect.target;
    const win = simulatedElement.ownerGlobal;
    const threshold = getPreferredProgressThresholdByKeyframes(keyframes);

    const getSegment = time => {
      simulatedAnimation.currentTime = time;
      const computedStyle =
        win.getComputedStyle(simulatedElement).getPropertyValue(propertyName);

      return {
        computedStyle,
        x: time,
        y: this.toSegmentValue(computedStyle),
      };
    };

    const segments = createPathSegments(0, duration, duration / componentWidth, threshold,
                                        DEFAULT_DURATION_RESOLUTION, getSegment);
    const offset = startValue.offset * totalDuration;

    for (const segment of segments) {
      segment.x += offset;
    }

    return segments;
  }

  /**
   * Render easing hint from given path segments.
   *
   * @param {Array} segments
   *        Path segments.
   * @return {Element}
   *         Element which represents easing hint.
   */
  renderEasingHint(segments) {
    const {
      easingHintStrokeWidth,
      totalDuration,
      values,
    } = this.props;

    const hints = [];

    for (let i = 0, indexOfSegments = 0; i < values.length - 1; i++) {
      const startKeyframe = values[i];
      const endKeyframe = values[i + 1];
      const endTime = endKeyframe.offset * totalDuration;
      const hintSegments = [];

      for (; indexOfSegments < segments.length; indexOfSegments++) {
        const segment = segments[indexOfSegments];
        hintSegments.push(segment);

        if (startKeyframe.offset === endKeyframe.offset) {
          hintSegments.push(segments[++indexOfSegments]);
          break;
        } else if (segment.x === endTime) {
          break;
        }
      }

      const g = dom.g(
        {
          className: "hint"
        },
        dom.title({}, startKeyframe.easing),
        dom.path(
          {
            d: `M${ hintSegments[0].x },${ hintSegments[0].y } ` +
               toPathString(hintSegments),
            style: {
              "stroke-width": easingHintStrokeWidth,
            }
          }
        )
      );

      hints.push(g);
    }

    return hints;
  }

  /**
   * Render graph. This method returns React dom.
   *
   * @return {Element}
   */
  renderGraph() {
    const { values } = this.props;

    const segments = [];

    for (let i = 0; i < values.length - 1; i++) {
      const startValue = values[i];
      const endValue = values[i + 1];
      segments.push(...this.getPathSegments(startValue, endValue));
    }

    return [
      this.renderPathSegments(segments),
      this.renderEasingHint(segments)
    ];
  }

  /**
   * Return react dom fron given path segments.
   *
   * @param {Array} segments
   * @param {Object} style
   * @return {Element}
   */
  renderPathSegments(segments, style) {
    const { graphHeight } = this.props;

    for (const segment of segments) {
      segment.y *= graphHeight;
    }

    let d = `M${ segments[0].x },0 `;
    d += toPathString(segments);
    d += `L${ segments[segments.length - 1].x },0 Z`;

    return dom.path({ d, style });
  }
}

/**
 * Convert given CSS property name to JavaScript CSS name.
 *
 * @param {String} cssPropertyName
 *        CSS property name (e.g. background-color).
 * @return {String}
 *         JavaScript CSS property name (e.g. backgroundColor).
 */
function getJsPropertyName(cssPropertyName) {
  if (cssPropertyName == "float") {
    return "cssFloat";
  }
  // https://drafts.csswg.org/cssom/#css-property-to-idl-attribute
  return cssPropertyName.replace(/-([a-z])/gi, (str, group) => {
    return group.toUpperCase();
  });
}

module.exports = ComputedStylePath;
PK
!<8�fchrome/devtools/modules/devtools/client/inspector/animation/components/keyframes-graph/DiscretePath.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 dom = require("devtools/client/shared/vendor/react-dom-factories");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");

const ComputedStylePath = require("./ComputedStylePath");

class DiscretePath extends ComputedStylePath {
  static get propTypes() {
    return {
      property: PropTypes.string.isRequired,
    };
  }

  constructor(props) {
    super(props);

    this.state = this.propToState(props);
  }

  componentWillReceiveProps(nextProps) {
    this.setState(this.propToState(nextProps));
  }

  getPropertyName() {
    return this.props.property;
  }

  getPropertyValue(keyframe) {
    return keyframe.value;
  }

  propToState({ property, getComputedStyle, values }) {
    const discreteValues = [];

    for (const keyframe of values) {
      const style = getComputedStyle(property, { [property]: keyframe.value });

      if (!discreteValues.includes(style)) {
        discreteValues.push(style);
      }
    }

    return { discreteValues };
  }

  toSegmentValue(computedStyle) {
    const { discreteValues } = this.state;
    return discreteValues.indexOf(computedStyle) / (discreteValues.length - 1);
  }

  render() {
    return dom.g(
      {
        className: "discrete-path",
      },
      super.renderGraph()
    );
  }
}

module.exports = DiscretePath;
PK
!<�W���fchrome/devtools/modules/devtools/client/inspector/animation/components/keyframes-graph/DistancePath.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 dom = require("devtools/client/shared/vendor/react-dom-factories");

const ComputedStylePath = require("./ComputedStylePath");

class DistancePath extends ComputedStylePath {
  getPropertyName() {
    return "opacity";
  }

  getPropertyValue(keyframe) {
    return keyframe.distance;
  }

  toSegmentValue(computedStyle) {
    return computedStyle;
  }

  render() {
    return dom.g(
      {
        className: "distance-path",
      },
      super.renderGraph()
    );
  }
}

module.exports = DistancePath;
PK
!<K��oolchrome/devtools/modules/devtools/client/inspector/animation/components/keyframes-graph/KeyframeMarkerItem.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");

class KeyframeMarkerItem extends PureComponent {
  static get propTypes() {
    return {
      keyframe: PropTypes.object.isRequired,
    };
  }

  render() {
    const { keyframe } = this.props;

    return dom.li(
      {
        className: "keyframe-marker-item",
        title: keyframe.value,
        style: {
          left: `${ keyframe.offset * 100 }%`,
        },
      }
    );
  }
}

module.exports = KeyframeMarkerItem;
PK
!<�[}!��lchrome/devtools/modules/devtools/client/inspector/animation/components/keyframes-graph/KeyframeMarkerList.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");

const KeyframeMarkerItem = createFactory(require("./KeyframeMarkerItem"));

class KeyframeMarkerList extends PureComponent {
  static get propTypes() {
    return {
      values: PropTypes.array.isRequired,
    };
  }

  render() {
    const { values } = this.props;

    return dom.ul(
      {
        className: "keyframe-marker-list"
      },
      values.map(value => KeyframeMarkerItem({ keyframe: value }))
    );
  }
}

module.exports = KeyframeMarkerList;
PK
!<�Q����hchrome/devtools/modules/devtools/client/inspector/animation/components/keyframes-graph/KeyframesGraph.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");

const KeyframeMarkerList = createFactory(require("./KeyframeMarkerList"));
const KeyframesGraphPath = createFactory(require("./KeyframesGraphPath"));

class KeyframesGraph extends PureComponent {
  static get propTypes() {
    return {
      getComputedStyle: PropTypes.func.isRequired,
      property: PropTypes.string.isRequired,
      simulateAnimation: PropTypes.func.isRequired,
      type: PropTypes.string.isRequired,
      values: PropTypes.array.isRequired,
    };
  }

  render() {
    const {
      getComputedStyle,
      property,
      simulateAnimation,
      type,
      values,
    } = this.props;

    return dom.div(
      {
        className: `keyframes-graph ${ property }`
      },
      KeyframesGraphPath(
        {
          getComputedStyle,
          property,
          simulateAnimation,
          type,
          values,
        }
      ),
      KeyframeMarkerList({ values })
    );
  }
}

module.exports = KeyframesGraph;
PK
!<-n��}}lchrome/devtools/modules/devtools/client/inspector/animation/components/keyframes-graph/KeyframesGraphPath.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const ReactDOM = require("devtools/client/shared/vendor/react-dom");

const ColorPath = createFactory(require("./ColorPath"));
const DiscretePath = createFactory(require("./DiscretePath"));
const DistancePath = createFactory(require("./DistancePath"));

const {
  DEFAULT_EASING_HINT_STROKE_WIDTH,
  DEFAULT_GRAPH_HEIGHT,
  DEFAULT_KEYFRAMES_GRAPH_DURATION,
} = require("../../utils/graph-helper");

class KeyframesGraphPath extends PureComponent {
  static get propTypes() {
    return {
      getComputedStyle: PropTypes.func.isRequired,
      property: PropTypes.string.isRequired,
      simulateAnimation: PropTypes.func.isRequired,
      type: PropTypes.string.isRequired,
      values: PropTypes.array.isRequired,
    };
  }

  constructor(props) {
    super(props);

    this.state = {
      componentHeight: 0,
      componentWidth: 0,
    };
  }

  componentDidMount() {
    this.updateState();
  }

  getPathComponent(type) {
    switch (type) {
      case "color" :
        return ColorPath;
      case "discrete" :
        return DiscretePath;
      default :
        return DistancePath;
    }
  }

  updateState() {
    const thisEl = ReactDOM.findDOMNode(this);
    this.setState({
      componentHeight: thisEl.parentNode.clientHeight,
      componentWidth: thisEl.parentNode.clientWidth,
    });
  }

  render() {
    const {
      getComputedStyle,
      property,
      simulateAnimation,
      type,
      values,
    } = this.props;
    const {
      componentHeight,
      componentWidth,
    } = this.state;

    if (!componentWidth) {
      return dom.svg();
    }

    const pathComponent = this.getPathComponent(type);
    const strokeWidthInViewBox =
      DEFAULT_EASING_HINT_STROKE_WIDTH / 2 / componentHeight * DEFAULT_GRAPH_HEIGHT;

    return dom.svg(
      {
        className: "keyframes-graph-path",
        preserveAspectRatio: "none",
        viewBox: `0 -${ DEFAULT_GRAPH_HEIGHT + strokeWidthInViewBox } ` +
                 `${ DEFAULT_KEYFRAMES_GRAPH_DURATION } ` +
                 `${ DEFAULT_GRAPH_HEIGHT + strokeWidthInViewBox * 2 }`,
      },
      pathComponent(
        {
          componentWidth,
          easingHintStrokeWidth: DEFAULT_EASING_HINT_STROKE_WIDTH,
          getComputedStyle,
          graphHeight: DEFAULT_GRAPH_HEIGHT,
          property,
          simulateAnimation,
          totalDuration: DEFAULT_KEYFRAMES_GRAPH_DURATION,
          values,
        }
      )
    );
  }
}

module.exports = KeyframesGraphPath;
PK
!<2��%��Rchrome/devtools/modules/devtools/client/inspector/animation/reducers/animations.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  UPDATE_ANIMATIONS,
  UPDATE_DETAIL_VISIBILITY,
  UPDATE_ELEMENT_PICKER_ENABLED,
  UPDATE_SELECTED_ANIMATION,
  UPDATE_SIDEBAR_SIZE,
} = require("../actions/index");

const INITIAL_STATE = {
  animations: [],
  detailVisibility: false,
  elementPickerEnabled: false,
  selectedAnimation: null,
  sidebarSize: {
    height: 0,
    width: 0,
  },
};

const reducers = {
  [UPDATE_ANIMATIONS](state, { animations }) {
    return Object.assign({}, state, {
      animations,
    });
  },

  [UPDATE_DETAIL_VISIBILITY](state, { detailVisibility }) {
    return Object.assign({}, state, {
      detailVisibility
    });
  },

  [UPDATE_ELEMENT_PICKER_ENABLED](state, { elementPickerEnabled }) {
    return Object.assign({}, state, {
      elementPickerEnabled
    });
  },

  [UPDATE_SELECTED_ANIMATION](state, { selectedAnimation }) {
    const detailVisibility = !!selectedAnimation;

    return Object.assign({}, state, {
      detailVisibility,
      selectedAnimation
    });
  },

  [UPDATE_SIDEBAR_SIZE](state, { sidebarSize }) {
    return Object.assign({}, state, {
      sidebarSize
    });
  },
};

module.exports = function (state = INITIAL_STATE, action) {
  const reducer = reducers[action.type];
  return reducer ? reducer(state, action) : state;
};
PK
!<�"�o$o$Qchrome/devtools/modules/devtools/client/inspector/animation/utils/graph-helper.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

// BOUND_EXCLUDING_TIME should be less than 1ms and is used to exclude start
// and end bounds when dividing duration in createPathSegments.
const BOUND_EXCLUDING_TIME = 0.001;
// We define default graph height since if the height of viewport in SVG is
// too small (e.g. 1), vector-effect may not be able to calculate correctly.
const DEFAULT_GRAPH_HEIGHT = 100;
// Default animation duration for keyframes graph.
const DEFAULT_KEYFRAMES_GRAPH_DURATION = 1000;
// DEFAULT_MIN_PROGRESS_THRESHOLD shoud be between more than 0 to 1.
const DEFAULT_MIN_PROGRESS_THRESHOLD = 0.1;
// In the createPathSegments function, an animation duration is divided by
// DEFAULT_DURATION_RESOLUTION in order to draw the way the animation progresses.
// But depending on the timing-function, we may be not able to make the graph
// smoothly progress if this resolution is not high enough.
// So, if the difference of animation progress between 2 divisions is more than
// DEFAULT_MIN_PROGRESS_THRESHOLD * DEFAULT_GRAPH_HEIGHT, then createPathSegments
// re-divides by DEFAULT_DURATION_RESOLUTION.
// DEFAULT_DURATION_RESOLUTION shoud be integer and more than 2.
const DEFAULT_DURATION_RESOLUTION = 4;
// Stroke width for easing hint.
const DEFAULT_EASING_HINT_STROKE_WIDTH = 5;

/**
 * The helper class for creating summary graph.
 */
class SummaryGraphHelper {
  /**
   * Constructor.
   *
   * @param {Object} state
   *        State of animation.
   * @param {Array} keyframes
   *        Array of keyframe.
   * @param {Number} totalDuration
   *        Total displayable duration.
   * @param {Number} minSegmentDuration
   *        Minimum segment duration.
   * @param {Function} getValueFunc
   *        Which returns graph value of given time.
   *        The function should return a number value between 0 - 1.
   *        e.g. time => { return 1.0 };
   * @param {Function} toPathStringFunc
   *        Which returns a path string for 'd' attribute for <path> from given segments.
   */
  constructor(state, keyframes, totalDuration, minSegmentDuration,
              getValueFunc, toPathStringFunc) {
    this.totalDuration = totalDuration;
    this.minSegmentDuration = minSegmentDuration;
    this.minProgressThreshold =
      getPreferredProgressThreshold(state, keyframes) * DEFAULT_GRAPH_HEIGHT;
    this.durationResolution = getPreferredDurationResolution(keyframes);
    this.getValue = getValueFunc;
    this.toPathString = toPathStringFunc;

    this.getSegment = this.getSegment.bind(this);
  }

  /**
   * Create the path segments from given parameters.
   *
   * @param {Number} startTime
   *        Starting time of animation.
   * @param {Number} endTime
   *        Ending time of animation.
   * @return {Array}
   *         Array of path segment.
   *         e.g.[{x: {Number} time, y: {Number} progress}, ...]
   */
  createPathSegments(startTime, endTime) {
    return createPathSegments(startTime, endTime,
                              this.minSegmentDuration, this.minProgressThreshold,
                              this.durationResolution, this.getSegment);
  }

  /**
   * Return a coordinate as a graph segment at given time.
   *
   * @param {Number} time
   * @return {Object}
   *         { x: Number, y: Number }
   */
  getSegment(time) {
    const value = this.getValue(time);
    return { x: time, y: value * DEFAULT_GRAPH_HEIGHT };
  }
}

/**
 * Create the path segments from given parameters.
 *
 * @param {Number} startTime
 *        Starting time of animation.
 * @param {Number} endTime
 *        Ending time of animation.
 * @param {Number} minSegmentDuration
 *        Minimum segment duration.
 * @param {Number} minProgressThreshold
 *        Minimum progress threshold.
 * @param {Number} resolution
 *        Duration resolution for first time.
 * @param {Function} getSegment
 *        A function that calculate the graph segment.
 * @return {Array}
 *         Array of path segment.
 *         e.g.[{x: {Number} time, y: {Number} progress}, ...]
 */
function createPathSegments(startTime, endTime, minSegmentDuration,
                            minProgressThreshold, resolution, getSegment) {
  // If the duration is too short, early return.
  if (endTime - startTime < minSegmentDuration) {
    return [getSegment(startTime), getSegment(endTime)];
  }

  // Otherwise, start creating segments.
  let pathSegments = [];

  // Append the segment for the startTime position.
  const startTimeSegment = getSegment(startTime);
  pathSegments.push(startTimeSegment);
  let previousSegment = startTimeSegment;

  // Split the duration in equal intervals, and iterate over them.
  // See the definition of DEFAULT_DURATION_RESOLUTION for more information about this.
  const interval = (endTime - startTime) / resolution;
  for (let index = 1; index <= resolution; index++) {
    // Create a segment for this interval.
    const currentSegment = getSegment(startTime + index * interval);

    // If the distance between the Y coordinate (the animation's progress) of
    // the previous segment and the Y coordinate of the current segment is too
    // large, then recurse with a smaller duration to get more details
    // in the graph.
    if (Math.abs(currentSegment.y - previousSegment.y) > minProgressThreshold) {
      // Divide the current interval (excluding start and end bounds
      // by adding/subtracting BOUND_EXCLUDING_TIME).
      const nextStartTime = previousSegment.x + BOUND_EXCLUDING_TIME;
      const nextEndTime = currentSegment.x - BOUND_EXCLUDING_TIME;
      const segments =
        createPathSegments(nextStartTime, nextEndTime, minSegmentDuration,
                           minProgressThreshold, DEFAULT_DURATION_RESOLUTION, getSegment);
      pathSegments = pathSegments.concat(segments);
    }

    pathSegments.push(currentSegment);
    previousSegment = currentSegment;
  }

  return pathSegments;
}

/**
 * Return preferred duration resolution.
 * This corresponds to narrow interval keyframe offset.
 *
 * @param {Array} keyframes
 *        Array of keyframe.
 * @return {Number}
 *         Preferred duration resolution.
 */
function getPreferredDurationResolution(keyframes) {
  if (!keyframes) {
    return DEFAULT_DURATION_RESOLUTION;
  }

  let durationResolution = DEFAULT_DURATION_RESOLUTION;
  let previousOffset = 0;
  for (let keyframe of keyframes) {
    if (previousOffset && previousOffset != keyframe.offset) {
      const interval = keyframe.offset - previousOffset;
      durationResolution = Math.max(durationResolution, Math.ceil(1 / interval));
    }
    previousOffset = keyframe.offset;
  }

  return durationResolution;
}

/**
 * Return preferred progress threshold to render summary graph.
 *
 * @param {Object} state
 *        State of animation.
 * @param {Array} keyframes
 *        Array of keyframe.
 * @return {float}
 *         Preferred threshold.
 */
function getPreferredProgressThreshold(state, keyframes) {
  let threshold = DEFAULT_MIN_PROGRESS_THRESHOLD;
  let stepsOrFrames;

  if ((stepsOrFrames = getStepsOrFramesCount(state.easing))) {
    threshold = Math.min(threshold, (1 / (stepsOrFrames + 1)));
  }

  if (!keyframes) {
    return threshold;
  }

  threshold = Math.min(threshold, getPreferredProgressThresholdByKeyframes(keyframes));

  return threshold;
}

/**
 * Return preferred progress threshold by keyframes.
 *
 * @param {Array} keyframes
 *        Array of keyframe.
 * @return {float}
 *         Preferred threshold.
 */
function getPreferredProgressThresholdByKeyframes(keyframes) {
  let threshold = DEFAULT_MIN_PROGRESS_THRESHOLD;
  let stepsOrFrames;

  for (let i = 0; i < keyframes.length - 1; i++) {
    const keyframe = keyframes[i];

    if (!keyframe.easing) {
      continue;
    }

    if ((stepsOrFrames = getStepsOrFramesCount(keyframe.easing))) {
      const nextKeyframe = keyframes[i + 1];
      threshold =
        Math.min(threshold,
                 1 / (stepsOrFrames + 1) * (nextKeyframe.offset - keyframe.offset));
    }
  }

  return threshold;
}

function getStepsOrFramesCount(easing) {
  const stepsOrFramesFunction = easing.match(/(steps|frames)\((\d+)/);
  return stepsOrFramesFunction ? parseInt(stepsOrFramesFunction[2], 10) : 0;
}

/**
 * Return path string for 'd' attribute for <path> from given segments.
 *
 * @param {Array} segments
 *        e.g. [{ x: 100, y: 0 }, { x: 200, y: 1 }]
 * @return {String}
 *         Path string.
 *         e.g. "L100,0 L200,1"
 */
function toPathString(segments) {
  let pathString = "";
  segments.forEach(segment => {
    pathString += `L${ segment.x },${ segment.y } `;
  });
  return pathString;
}

exports.createPathSegments = createPathSegments;
exports.DEFAULT_DURATION_RESOLUTION = DEFAULT_DURATION_RESOLUTION;
exports.DEFAULT_EASING_HINT_STROKE_WIDTH = DEFAULT_EASING_HINT_STROKE_WIDTH;
exports.DEFAULT_GRAPH_HEIGHT = DEFAULT_GRAPH_HEIGHT;
exports.DEFAULT_KEYFRAMES_GRAPH_DURATION = DEFAULT_KEYFRAMES_GRAPH_DURATION;
exports.getPreferredProgressThresholdByKeyframes =
  getPreferredProgressThresholdByKeyframes;
exports.SummaryGraphHelper = SummaryGraphHelper;
exports.toPathString = toPathString;
PK
!<u	C�ggIchrome/devtools/modules/devtools/client/inspector/animation/utils/l10n.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/shared/l10n");
const L10N =
  new LocalizationHelper("devtools/client/locales/animationinspector.properties");

/**
 * Get a formatted title for this animation. This will be either:
 * "%S", "%S : CSS Transition", "%S : CSS Animation",
 * "%S : Script Animation", or "Script Animation", depending
 * if the server provides the type, what type it is and if the animation
 * has a name.
 *
 * @param {Object} state
 */
function getFormattedTitle(state) {
  // Older servers don't send a type, and only know about
  // CSSAnimations and CSSTransitions, so it's safe to use
  // just the name.
  if (!state.type) {
    return state.name;
  }

  // Script-generated animations may not have a name.
  if (state.type === "scriptanimation" && !state.name) {
    return L10N.getStr("timeline.scriptanimation.unnamedLabel");
  }

  return L10N.getFormatStr(`timeline.${state.type}.nameLabel`, state.name);
}

module.exports = {
  getFormatStr: (...args) => L10N.getFormatStr(...args),
  getFormattedTitle,
  getStr: (...args) => L10N.getStr(...args),
  numberWithDecimals: (...args) => L10N.numberWithDecimals(...args),
};
PK
!<���Nchrome/devtools/modules/devtools/client/inspector/animation/utils/timescale.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { getFormatStr } = require("./l10n");

// If total duration for all animations is eqaul to or less than
// TIME_FORMAT_MAX_DURATION_IN_MS, the text which expresses time is in milliseconds,
// and seconds otherwise. Use in formatTime function.
const TIME_FORMAT_MAX_DURATION_IN_MS = 4000;

/**
 * TimeScale object holds the total duration, start time and end time information for all
 * animations which should be displayed, and is used to calculate the displayed area for
 * each animation.
 *
 * For the helper to know how to convert, it needs to know all the animations.
 * Whenever a new animation is added to the panel, addAnimation(state) should be
 * called.
 */
class TimeScale {
  constructor(animations) {
    this.minStartTime = Infinity;
    this.maxEndTime = 0;
    for (const animation of animations) {
      this.addAnimation(animation.state);
    }
  }

  /**
   * Add a new animation to time scale.
   *
   * @param {Object} state
   *                 A PlayerFront.state object.
   */
  addAnimation(state) {
    let {
      delay,
      duration,
      endDelay = 0,
      iterationCount,
      playbackRate,
      previousStartTime,
    } = state;

    const toRate = v => v / playbackRate;
    const minZero = v => Math.max(v, 0);
    const rateRelativeDuration =
      toRate(duration * (!iterationCount ? 1 : iterationCount));
    // Negative-delayed animations have their startTimes set such that we would
    // be displaying the delay outside the time window if we didn't take it into
    // account here.
    const relevantDelay = delay < 0 ? toRate(delay) : 0;
    previousStartTime = previousStartTime || 0;

    const startTime = toRate(minZero(delay)) +
                      rateRelativeDuration +
                      endDelay;
    this.minStartTime = Math.min(
      this.minStartTime,
      previousStartTime +
      relevantDelay +
      Math.min(startTime, 0)
    );
    const length = toRate(delay) + rateRelativeDuration + toRate(minZero(endDelay));
    const endTime = previousStartTime + length;
    this.maxEndTime = Math.max(this.maxEndTime, endTime);
  }

  /**
   * Convert a distance in % to a time, in the current time scale.
   *
   * @param {Number} distance
   * @return {Number}
   */
  distanceToTime(distance) {
    return this.minStartTime + (this.getDuration() * distance / 100);
  }

  /**
   * Convert a distance in % to a time, in the current time scale.
   * The time will be relative to the current minimum start time.
   *
   * @param {Number} distance
   * @return {Number}
   */
  distanceToRelativeTime(distance) {
    const time = this.distanceToTime(distance);
    return time - this.minStartTime;
  }

  /**
   * Depending on the time scale, format the given time as milliseconds or
   * seconds.
   *
   * @param {Number} time
   * @return {String} The formatted time string.
   */
  formatTime(time) {
    // Format in milliseconds if the total duration is short enough.
    if (this.getDuration() <= TIME_FORMAT_MAX_DURATION_IN_MS) {
      return getFormatStr("timeline.timeGraduationLabel", time.toFixed(0));
    }

    // Otherwise format in seconds.
    return getFormatStr("player.timeLabel", (time / 1000).toFixed(1));
  }

  /**
   * Return entire animations duration.
   *
   * @return {Number} duration
   */
  getDuration() {
    return this.maxEndTime - this.minStartTime;
  }
}

module.exports = TimeScale;
PK
!<�埑d
d
Jchrome/devtools/modules/devtools/client/inspector/animation/utils/utils.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 maximum number of times we can loop before we find the optimal time interval in the
// timeline graph.
const OPTIMAL_TIME_INTERVAL_MAX_ITERS = 100;
// Time graduations should be multiple of one of these number.
const OPTIMAL_TIME_INTERVAL_MULTIPLES = [1, 2.5, 5];

/**
 * Find the optimal interval between time graduations in the animation timeline
 * graph based on a minimum time interval.
 *
 * @param {Number} minTimeInterval
 *                 Minimum time in ms in one interval
 * @return {Number} The optimal interval time in ms
 */
function findOptimalTimeInterval(minTimeInterval) {
  if (!minTimeInterval) {
    return 0;
  }

  let numIters = 0;
  let multiplier = 1;
  let interval;

  while (true) {
    for (let i = 0; i < OPTIMAL_TIME_INTERVAL_MULTIPLES.length; i++) {
      interval = OPTIMAL_TIME_INTERVAL_MULTIPLES[i] * multiplier;

      if (minTimeInterval <= interval) {
        return interval;
      }
    }

    if (++numIters > OPTIMAL_TIME_INTERVAL_MAX_ITERS) {
      return interval;
    }

    multiplier *= 10;
  }
}

/**
 * Check the equality of the given animations.
 *
 * @param {Array} animations.
 * @param {Array} same to above.
 * @return {Boolean} true: same animations
 */
function isAllAnimationEqual(animationsA, animationsB) {
  if (animationsA.length !== animationsB.length) {
    return false;
  }

  for (let i = 0; i < animationsA.length; i++) {
    const animationA = animationsA[i];
    const animationB = animationsB[i];

    if (animationA.actorID !== animationB.actorID ||
        !isTimingEffectEqual(animationsA[i].state, animationsB[i].state)) {
      return false;
    }
  }

  return true;
}

/**
 * Check the equality given states as effect timing.
 *
 * @param {Object} state of animation.
 * @param {Object} same to avobe.
 * @return {Boolean} true: same effect timing
 */
function isTimingEffectEqual(stateA, stateB) {
  return stateA.delay === stateB.delay &&
         stateA.direction === stateB.direction &&
         stateA.duration === stateB.duration &&
         stateA.easing === stateB.easing &&
         stateA.endDelay === stateB.endDelay &&
         stateA.fill === stateB.fill &&
         stateA.iterationCount === stateB.iterationCount &&
         stateA.iterationStart === stateB.iterationStart;
}

exports.findOptimalTimeInterval = findOptimalTimeInterval;
exports.isAllAnimationEqual = isAllAnimationEqual;
exports.isTimingEffectEqual = isTimingEffectEqual;
PK
!<������Ochrome/devtools/modules/devtools/client/inspector/boxmodel/actions/box-model.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  UPDATE_GEOMETRY_EDITOR_ENABLED,
  UPDATE_LAYOUT,
  UPDATE_OFFSET_PARENT,
} = require("./index");

module.exports = {

  /**
   * Update the geometry editor's enabled state.
   *
   * @param  {Boolean} enabled
   *         Whether or not the geometry editor is enabled or not.
   */
  updateGeometryEditorEnabled(enabled) {
    return {
      type: UPDATE_GEOMETRY_EDITOR_ENABLED,
      enabled,
    };
  },

  /**
   * Update the layout state with the new layout properties.
   */
  updateLayout(layout) {
    return {
      type: UPDATE_LAYOUT,
      layout,
    };
  },

  /**
   * Update the offset parent state with the new DOM node.
   */
  updateOffsetParent(offsetParent) {
    return {
      type: UPDATE_OFFSET_PARENT,
      offsetParent,
    };
  }

};
PK
!<�Tl??Kchrome/devtools/modules/devtools/client/inspector/boxmodel/actions/index.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { createEnum } = require("devtools/client/shared/enum");

createEnum([

  // Update the geometry editor's enabled state.
  "UPDATE_GEOMETRY_EDITOR_ENABLED",

  // Update the layout state with the latest layout properties.
  "UPDATE_LAYOUT",

  // Update the offset parent state with the new DOM node.
  "UPDATE_OFFSET_PARENT",

], module.exports);
PK
!<�L��.�.Gchrome/devtools/modules/devtools/client/inspector/boxmodel/box-model.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Task } = require("devtools/shared/task");
const { getCssProperties } = require("devtools/shared/fronts/css-properties");

const { InplaceEditor } = require("devtools/client/shared/inplace-editor");

const {
  updateGeometryEditorEnabled,
  updateLayout,
  updateOffsetParent,
} = require("./actions/box-model");

const EditingSession = require("./utils/editing-session");

const NUMERIC = /^-?[\d\.]+$/;

/**
 * A singleton instance of the box model controllers.
 *
 * @param  {Inspector} inspector
 *         An instance of the Inspector currently loaded in the toolbox.
 * @param  {Window} window
 *         The document window of the toolbox.
 */
function BoxModel(inspector, window) {
  this.document = window.document;
  this.highlighters = inspector.highlighters;
  this.inspector = inspector;
  this.store = inspector.store;

  this.updateBoxModel = this.updateBoxModel.bind(this);

  this.onHideBoxModelHighlighter = this.onHideBoxModelHighlighter.bind(this);
  this.onHideGeometryEditor = this.onHideGeometryEditor.bind(this);
  this.onMarkupViewLeave = this.onMarkupViewLeave.bind(this);
  this.onMarkupViewNodeHover = this.onMarkupViewNodeHover.bind(this);
  this.onNewSelection = this.onNewSelection.bind(this);
  this.onShowBoxModelEditor = this.onShowBoxModelEditor.bind(this);
  this.onShowBoxModelHighlighter = this.onShowBoxModelHighlighter.bind(this);
  this.onSidebarSelect = this.onSidebarSelect.bind(this);
  this.onToggleGeometryEditor = this.onToggleGeometryEditor.bind(this);

  this.inspector.selection.on("new-node-front", this.onNewSelection);
  this.inspector.sidebar.on("select", this.onSidebarSelect);
}

BoxModel.prototype = {

  /**
   * Destruction function called when the inspector is destroyed. Removes event listeners
   * and cleans up references.
   */
  destroy() {
    this.inspector.selection.off("new-node-front", this.onNewSelection);
    this.inspector.sidebar.off("select", this.onSidebarSelect);

    this.untrackReflows();

    this.document = null;
    this.highlighters = null;
    this.inspector = null;
    this.walker = null;
  },

  /**
   * Returns an object containing the box model's handler functions used in the box
   * model's React component props.
   */
  getComponentProps() {
    return {
      onHideBoxModelHighlighter: this.onHideBoxModelHighlighter,
      onShowBoxModelEditor: this.onShowBoxModelEditor,
      onShowBoxModelHighlighter: this.onShowBoxModelHighlighter,
      onToggleGeometryEditor: this.onToggleGeometryEditor,
    };
  },

  /**
   * Returns true if the computed or layout panel is visible, and false otherwise.
   */
  isPanelVisible() {
    return this.inspector.toolbox && this.inspector.sidebar &&
           this.inspector.toolbox.currentToolId === "inspector" &&
           (this.inspector.sidebar.getCurrentTabID() === "layoutview" ||
            this.inspector.sidebar.getCurrentTabID() === "computedview");
  },

  /**
   * Returns true if the layout panel is visible and the current element is valid to
   * be displayed in the view.
   */
  isPanelVisibleAndNodeValid() {
    return this.isPanelVisible() &&
           this.inspector.selection.isConnected() &&
           this.inspector.selection.isElementNode();
  },

  /**
   * Starts listening to reflows in the current tab.
   */
  trackReflows() {
    this.inspector.reflowTracker.trackReflows(this, this.updateBoxModel);
  },

  /**
   * Stops listening to reflows in the current tab.
   */
  untrackReflows() {
    this.inspector.reflowTracker.untrackReflows(this, this.updateBoxModel);
  },

  /**
   * Updates the box model panel by dispatching the new layout data.
   *
   * @param  {String} reason
   *         Optional string describing the reason why the boxmodel is updated.
   */
  updateBoxModel(reason) {
    this._updateReasons = this._updateReasons || [];
    if (reason) {
      this._updateReasons.push(reason);
    }

    let lastRequest = Task.spawn((function* () {
      if (!this.inspector ||
          !this.isPanelVisible() ||
          !this.inspector.selection.isConnected() ||
          !this.inspector.selection.isElementNode()) {
        return null;
      }

      let node = this.inspector.selection.nodeFront;

      let layout = yield this.inspector.pageStyle.getLayout(node, {
        autoMargins: true,
      });

      let styleEntries = yield this.inspector.pageStyle.getApplied(node, {
        // We don't need styles applied to pseudo elements of the current node.
        skipPseudo: true
      });
      this.elementRules = styleEntries.map(e => e.rule);

      // Update the layout properties with whether or not the element's position is
      // editable with the geometry editor.
      let isPositionEditable = yield this.inspector.pageStyle.isPositionEditable(node);

      layout = Object.assign({}, layout, {
        isPositionEditable,
      });

      const actorCanGetOffSetParent =
        yield this.inspector.target.actorHasMethod("domwalker", "getOffsetParent");

      if (actorCanGetOffSetParent) {
        // Update the redux store with the latest offset parent DOM node
        let offsetParent = yield this.inspector.walker.getOffsetParent(node);
        this.store.dispatch(updateOffsetParent(offsetParent));
      }

      // Update the redux store with the latest layout properties and update the box
      // model view.
      this.store.dispatch(updateLayout(layout));

      // If a subsequent request has been made, wait for that one instead.
      if (this._lastRequest != lastRequest) {
        return this._lastRequest;
      }

      this.inspector.emit("boxmodel-view-updated", this._updateReasons);

      this._lastRequest = null;
      this._updateReasons = [];

      return null;
    }).bind(this)).catch(console.error);

    this._lastRequest = lastRequest;
  },

  /**
   * Hides the box-model highlighter on the currently selected element.
   */
  onHideBoxModelHighlighter() {
    if (!this.inspector) {
      return;
    }

    let toolbox = this.inspector.toolbox;
    toolbox.highlighterUtils.unhighlight();
  },

  /**
   * Hides the geometry editor and updates the box moodel store with the new
   * geometry editor enabled state.
   */
  onHideGeometryEditor() {
    let { markup, selection, toolbox } = this.inspector;

    this.highlighters.hideGeometryEditor();
    this.store.dispatch(updateGeometryEditorEnabled(false));

    toolbox.off("picker-started", this.onHideGeometryEditor);
    selection.off("new-node-front", this.onHideGeometryEditor);
    markup.off("leave", this.onMarkupViewLeave);
    markup.off("node-hover", this.onMarkupViewNodeHover);
  },

  /**
   * Handler function that re-shows the geometry editor for an element that already
   * had the geometry editor enabled. This handler function is called on a "leave" event
   * on the markup view.
   */
  onMarkupViewLeave() {
    let state = this.store.getState();
    let enabled = state.boxModel.geometryEditorEnabled;

    if (!enabled) {
      return;
    }

    let nodeFront = this.inspector.selection.nodeFront;
    this.highlighters.showGeometryEditor(nodeFront);
  },

  /**
   * Handler function that temporarily hides the geomery editor when the
   * markup view has a "node-hover" event.
   */
  onMarkupViewNodeHover() {
    this.highlighters.hideGeometryEditor();
  },

  /**
   * Selection 'new-node-front' event handler.
   */
  onNewSelection() {
    if (!this.isPanelVisibleAndNodeValid()) {
      return;
    }

    if (this.inspector.selection.isConnected() &&
        this.inspector.selection.isElementNode()) {
      this.trackReflows();
    }

    this.updateBoxModel("new-selection");
  },

  /**
   * Shows the inplace editor when a box model editable value is clicked on the
   * box model panel.
   *
   * @param  {DOMNode} element
   *         The element that was clicked.
   * @param  {Event} event
   *         The event object.
   * @param  {String} property
   *         The name of the property.
   */
  onShowBoxModelEditor(element, event, property) {
    let session = new EditingSession({
      inspector: this.inspector,
      doc: this.document,
      elementRules: this.elementRules,
    });
    let initialValue = session.getProperty(property);

    let editor = new InplaceEditor({
      element: element,
      initial: initialValue,
      contentType: InplaceEditor.CONTENT_TYPES.CSS_VALUE,
      property: {
        name: property
      },
      start: self => {
        self.elt.parentNode.classList.add("boxmodel-editing");
      },
      change: value => {
        if (NUMERIC.test(value)) {
          value += "px";
        }

        let properties = [
          { name: property, value: value }
        ];

        if (property.substring(0, 7) == "border-") {
          let bprop = property.substring(0, property.length - 5) + "style";
          let style = session.getProperty(bprop);
          if (!style || style == "none" || style == "hidden") {
            properties.push({ name: bprop, value: "solid" });
          }
        }

        if (property.substring(0, 9) == "position-") {
          properties[0].name = property.substring(9);
        }

        session.setProperties(properties).catch(console.error);
      },
      done: (value, commit) => {
        editor.elt.parentNode.classList.remove("boxmodel-editing");
        if (!commit) {
          session.revert().then(() => {
            session.destroy();
          }, console.error);
          return;
        }

        this.updateBoxModel("editable-value-change");
      },
      cssProperties: getCssProperties(this.inspector.toolbox)
    }, event);
  },

  /**
   * Shows the box-model highlighter on the currently selected element.
   *
   * @param  {Object} options
   *         Options passed to the highlighter actor.
   */
  onShowBoxModelHighlighter(options = {}) {
    if (!this.inspector) {
      return;
    }

    let toolbox = this.inspector.toolbox;
    let nodeFront = this.inspector.selection.nodeFront;

    toolbox.highlighterUtils.highlightNodeFront(nodeFront, options);
  },

  /**
   * Handler for the inspector sidebar select event. Starts tracking reflows if the
   * layout panel is visible. Otherwise, stop tracking reflows. Finally, refresh the box
   * model view if it is visible.
   */
  onSidebarSelect() {
    if (!this.isPanelVisible()) {
      this.untrackReflows();
      return;
    }

    if (this.inspector.selection.isConnected() &&
        this.inspector.selection.isElementNode()) {
      this.trackReflows();
    }

    this.updateBoxModel();
  },

  /**
   * Toggles on/off the geometry editor for the current element when the geometry editor
   * toggle button is clicked.
   */
  onToggleGeometryEditor() {
    let { markup, selection, toolbox } = this.inspector;
    let nodeFront = this.inspector.selection.nodeFront;
    let state = this.store.getState();
    let enabled = !state.boxModel.geometryEditorEnabled;

    this.highlighters.toggleGeometryHighlighter(nodeFront);
    this.store.dispatch(updateGeometryEditorEnabled(enabled));

    if (enabled) {
      // Hide completely the geometry editor if the picker is clicked or a new node front
      toolbox.on("picker-started", this.onHideGeometryEditor);
      selection.on("new-node-front", this.onHideGeometryEditor);
      // Temporary hide the geometry editor
      markup.on("leave", this.onMarkupViewLeave);
      markup.on("node-hover", this.onMarkupViewNodeHover);
    } else {
      toolbox.off("picker-started", this.onHideGeometryEditor);
      selection.off("new-node-front", this.onHideGeometryEditor);
      markup.off("leave", this.onMarkupViewLeave);
      markup.off("node-hover", this.onMarkupViewNodeHover);
    }
  },

};

module.exports = BoxModel;
PK
!<���4
4
Qchrome/devtools/modules/devtools/client/inspector/boxmodel/components/BoxModel.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");

const BoxModelInfo = createFactory(require("./BoxModelInfo"));
const BoxModelMain = createFactory(require("./BoxModelMain"));
const BoxModelProperties = createFactory(require("./BoxModelProperties"));

const Types = require("../types");

class BoxModel extends PureComponent {
  static get propTypes() {
    return {
      boxModel: PropTypes.shape(Types.boxModel).isRequired,
      setSelectedNode: PropTypes.func.isRequired,
      showBoxModelProperties: PropTypes.bool.isRequired,
      onHideBoxModelHighlighter: PropTypes.func.isRequired,
      onShowBoxModelEditor: PropTypes.func.isRequired,
      onShowBoxModelHighlighter: PropTypes.func.isRequired,
      onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
      onToggleGeometryEditor: PropTypes.func.isRequired,
    };
  }

  constructor(props) {
    super(props);
    this.onKeyDown = this.onKeyDown.bind(this);
  }

  onKeyDown(event) {
    let { target } = event;

    if (target == this.boxModelContainer) {
      this.boxModelMain.onKeyDown(event);
    }
  }

  render() {
    let {
      boxModel,
      setSelectedNode,
      showBoxModelProperties,
      onHideBoxModelHighlighter,
      onShowBoxModelEditor,
      onShowBoxModelHighlighter,
      onShowBoxModelHighlighterForNode,
      onToggleGeometryEditor,
    } = this.props;

    return dom.div(
      {
        className: "boxmodel-container",
        tabIndex: 0,
        ref: div => {
          this.boxModelContainer = div;
        },
        onKeyDown: this.onKeyDown,
      },
      BoxModelMain({
        boxModel,
        boxModelContainer: this.boxModelContainer,
        ref: boxModelMain => {
          this.boxModelMain = boxModelMain;
        },
        onHideBoxModelHighlighter,
        onShowBoxModelEditor,
        onShowBoxModelHighlighter,
      }),
      BoxModelInfo({
        boxModel,
        onToggleGeometryEditor,
      }),
      showBoxModelProperties ?
        BoxModelProperties({
          boxModel,
          setSelectedNode,
          onHideBoxModelHighlighter,
          onShowBoxModelHighlighterForNode,
        })
        :
        null
    );
  }
}

module.exports = BoxModel;
PK
!<���2Tchrome/devtools/modules/devtools/client/inspector/boxmodel/components/BoxModelApp.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Services = require("Services");
const { createFactory, PureComponent } = require("devtools/client/shared/vendor/react");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const { connect } = require("devtools/client/shared/vendor/react-redux");
const { LocalizationHelper } = require("devtools/shared/l10n");

const Accordion =
  createFactory(require("devtools/client/inspector/layout/components/Accordion"));
const BoxModel = createFactory(require("./BoxModel"));

const Types = require("../types");

const BOXMODEL_STRINGS_URI = "devtools/client/locales/boxmodel.properties";
const BOXMODEL_L10N = new LocalizationHelper(BOXMODEL_STRINGS_URI);

const BOXMODEL_OPENED_PREF = "devtools.computed.boxmodel.opened";

class BoxModelApp extends PureComponent {
  static get propTypes() {
    return {
      boxModel: PropTypes.shape(Types.boxModel).isRequired,
      setSelectedNode: PropTypes.func.isRequired,
      showBoxModelProperties: PropTypes.bool.isRequired,
      onHideBoxModelHighlighter: PropTypes.func.isRequired,
      onShowBoxModelEditor: PropTypes.func.isRequired,
      onShowBoxModelHighlighter: PropTypes.func.isRequired,
      onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
      onToggleGeometryEditor: PropTypes.func.isRequired,
    };
  }

  render() {
    return Accordion({
      items: [
        {
          header: BOXMODEL_L10N.getStr("boxmodel.title"),
          component: BoxModel,
          componentProps: this.props,
          opened: Services.prefs.getBoolPref(BOXMODEL_OPENED_PREF),
          onToggled: () => {
            let opened = Services.prefs.getBoolPref(BOXMODEL_OPENED_PREF);
            Services.prefs.setBoolPref(BOXMODEL_OPENED_PREF, !opened);
          }
        }
      ]
    });
  }
}

module.exports = connect(state => state)(BoxModelApp);
PK
!<�4
�YYYchrome/devtools/modules/devtools/client/inspector/boxmodel/components/BoxModelEditable.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const { editableItem } = require("devtools/client/shared/inplace-editor");

const LONG_TEXT_ROTATE_LIMIT = 3;

class BoxModelEditable extends PureComponent {
  static get propTypes() {
    return {
      box: PropTypes.string.isRequired,
      direction: PropTypes.string,
      focusable: PropTypes.bool.isRequired,
      level: PropTypes.string,
      property: PropTypes.string.isRequired,
      textContent: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
      onShowBoxModelEditor: PropTypes.func.isRequired,
    };
  }

  componentDidMount() {
    let { property, onShowBoxModelEditor } = this.props;

    editableItem({
      element: this.boxModelEditable,
    }, (element, event) => {
      onShowBoxModelEditor(element, event, property);
    });
  }

  render() {
    let {
      box,
      direction,
      focusable,
      level,
      property,
      textContent,
    } = this.props;

    let rotate = direction &&
                 (direction == "left" || direction == "right") &&
                 box !== "position" &&
                 textContent.toString().length > LONG_TEXT_ROTATE_LIMIT;

    return dom.p(
      {
        className: `boxmodel-${box}
                    ${direction ? " boxmodel-" + direction : "boxmodel-" + property}
                    ${rotate ? " boxmodel-rotate" : ""}`,
      },
      dom.span(
        {
          className: "boxmodel-editable",
          "data-box": box,
          tabIndex: box === level && focusable ? 0 : -1,
          title: property,
          ref: span => {
            this.boxModelEditable = span;
          },
        },
        textContent
      )
    );
  }
}

module.exports = BoxModelEditable;
PK
!<ǦK�		Uchrome/devtools/modules/devtools/client/inspector/boxmodel/components/BoxModelInfo.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const { LocalizationHelper } = require("devtools/shared/l10n");

const Types = require("../types");

const BOXMODEL_STRINGS_URI = "devtools/client/locales/boxmodel.properties";
const BOXMODEL_L10N = new LocalizationHelper(BOXMODEL_STRINGS_URI);

const SHARED_STRINGS_URI = "devtools/client/locales/shared.properties";
const SHARED_L10N = new LocalizationHelper(SHARED_STRINGS_URI);

class BoxModelInfo extends PureComponent {
  static get propTypes() {
    return {
      boxModel: PropTypes.shape(Types.boxModel).isRequired,
      onToggleGeometryEditor: PropTypes.func.isRequired,
    };
  }

  constructor(props) {
    super(props);
    this.onToggleGeometryEditor = this.onToggleGeometryEditor.bind(this);
  }

  onToggleGeometryEditor(e) {
    this.props.onToggleGeometryEditor();
  }

  render() {
    let { boxModel } = this.props;
    let { geometryEditorEnabled, layout } = boxModel;
    let {
      height = "-",
      isPositionEditable,
      position,
      width = "-",
    } = layout;

    let buttonClass = "layout-geometry-editor devtools-button";
    if (geometryEditorEnabled) {
      buttonClass += " checked";
    }

    return dom.div(
      {
        className: "boxmodel-info",
      },
      dom.span(
        {
          className: "boxmodel-element-size",
        },
        SHARED_L10N.getFormatStr("dimensions", width, height)
      ),
      dom.section(
        {
          className: "boxmodel-position-group",
        },
        isPositionEditable ?
          dom.button({
            className: buttonClass,
            title: BOXMODEL_L10N.getStr("boxmodel.geometryButton.tooltip"),
            onClick: this.onToggleGeometryEditor,
          })
          :
          null,
        dom.span(
          {
            className: "boxmodel-element-position",
          },
          position
        )
      )
    );
  }
}

module.exports = BoxModelInfo;
PK
!<�ڈJ�O�OUchrome/devtools/modules/devtools/client/inspector/boxmodel/components/BoxModelMain.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const { findDOMNode } = require("devtools/client/shared/vendor/react-dom");
const { KeyCodes } = require("devtools/client/shared/keycodes");
const { LocalizationHelper } = require("devtools/shared/l10n");

const BoxModelEditable = createFactory(require("./BoxModelEditable"));

const Types = require("../types");

const BOXMODEL_STRINGS_URI = "devtools/client/locales/boxmodel.properties";
const BOXMODEL_L10N = new LocalizationHelper(BOXMODEL_STRINGS_URI);

const SHARED_STRINGS_URI = "devtools/client/locales/shared.properties";
const SHARED_L10N = new LocalizationHelper(SHARED_STRINGS_URI);

class BoxModelMain extends PureComponent {
  static get propTypes() {
    return {
      boxModel: PropTypes.shape(Types.boxModel).isRequired,
      boxModelContainer: PropTypes.object,
      onHideBoxModelHighlighter: PropTypes.func.isRequired,
      onShowBoxModelEditor: PropTypes.func.isRequired,
      onShowBoxModelHighlighter: PropTypes.func.isRequired,
    };
  }

  constructor(props) {
    super(props);

    this.state = {
      activeDescendant: null,
      focusable: false,
    };

    this.getAriaActiveDescendant = this.getAriaActiveDescendant.bind(this);
    this.getBorderOrPaddingValue = this.getBorderOrPaddingValue.bind(this);
    this.getContextBox = this.getContextBox.bind(this);
    this.getDisplayPosition = this.getDisplayPosition.bind(this);
    this.getHeightValue = this.getHeightValue.bind(this);
    this.getMarginValue = this.getMarginValue.bind(this);
    this.getPositionValue = this.getPositionValue.bind(this);
    this.getWidthValue = this.getWidthValue.bind(this);
    this.moveFocus = this.moveFocus.bind(this);
    this.setAriaActive = this.setAriaActive.bind(this);
    this.onHighlightMouseOver = this.onHighlightMouseOver.bind(this);
    this.onKeyDown = this.onKeyDown.bind(this);
    this.onLevelClick = this.onLevelClick.bind(this);
  }

  componentDidUpdate() {
    let displayPosition = this.getDisplayPosition();
    let isContentBox = this.getContextBox();

    this.layouts = {
      "position": new Map([
        [KeyCodes.DOM_VK_ESCAPE, this.positionLayout],
        [KeyCodes.DOM_VK_DOWN, this.marginLayout],
        [KeyCodes.DOM_VK_RETURN, this.positionEditable],
        [KeyCodes.DOM_VK_UP, null],
        ["click", this.positionLayout]
      ]),
      "margin": new Map([
        [KeyCodes.DOM_VK_ESCAPE, this.marginLayout],
        [KeyCodes.DOM_VK_DOWN, this.borderLayout],
        [KeyCodes.DOM_VK_RETURN, this.marginEditable],
        [KeyCodes.DOM_VK_UP, displayPosition ? this.positionLayout : null],
        ["click", this.marginLayout]
      ]),
      "border": new Map([
        [KeyCodes.DOM_VK_ESCAPE, this.borderLayout],
        [KeyCodes.DOM_VK_DOWN, this.paddingLayout],
        [KeyCodes.DOM_VK_RETURN, this.borderEditable],
        [KeyCodes.DOM_VK_UP, this.marginLayout],
        ["click", this.borderLayout]
      ]),
      "padding": new Map([
        [KeyCodes.DOM_VK_ESCAPE, this.paddingLayout],
        [KeyCodes.DOM_VK_DOWN, isContentBox ? this.contentLayout : null],
        [KeyCodes.DOM_VK_RETURN, this.paddingEditable],
        [KeyCodes.DOM_VK_UP, this.borderLayout],
        ["click", this.paddingLayout]
      ]),
      "content": new Map([
        [KeyCodes.DOM_VK_ESCAPE, this.contentLayout],
        [KeyCodes.DOM_VK_DOWN, null],
        [KeyCodes.DOM_VK_RETURN, this.contentEditable],
        [KeyCodes.DOM_VK_UP, this.paddingLayout],
        ["click", this.contentLayout]
      ])
    };
  }

  getAriaActiveDescendant() {
    let { activeDescendant } = this.state;

    if (!activeDescendant) {
      let displayPosition = this.getDisplayPosition();
      let nextLayout = displayPosition ? this.positionLayout : this.marginLayout;
      activeDescendant = nextLayout.getAttribute("data-box");
      this.setAriaActive(nextLayout);
    }

    return activeDescendant;
  }

  getBorderOrPaddingValue(property) {
    let { layout } = this.props.boxModel;
    return layout[property] ? parseFloat(layout[property]) : "-";
  }

  /**
   * Returns true if the layout box sizing is context box and false otherwise.
   */
  getContextBox() {
    let { layout } = this.props.boxModel;
    return layout["box-sizing"] == "content-box";
  }

  /**
   * Returns true if the position is displayed and false otherwise.
   */
  getDisplayPosition() {
    let { layout } = this.props.boxModel;
    return layout.position && layout.position != "static";
  }

  getHeightValue(property) {
    if (property == undefined) {
      return "-";
    }

    let { layout } = this.props.boxModel;

    property -= parseFloat(layout["border-top-width"]) +
                parseFloat(layout["border-bottom-width"]) +
                parseFloat(layout["padding-top"]) +
                parseFloat(layout["padding-bottom"]);
    property = parseFloat(property.toPrecision(6));

    return property;
  }

  getMarginValue(property, direction) {
    let { layout } = this.props.boxModel;
    let autoMargins = layout.autoMargins || {};
    let value = "-";

    if (direction in autoMargins) {
      value = autoMargins[direction];
    } else if (layout[property]) {
      let parsedValue = parseFloat(layout[property]);

      if (Number.isNaN(parsedValue)) {
        // Not a number. We use the raw string.
        // Useful for pseudo-elements with auto margins since they
        // don't appear in autoMargins.
        value = layout[property];
      } else {
        value = parsedValue;
      }
    }

    return value;
  }

  getPositionValue(property) {
    let { layout } = this.props.boxModel;
    let value = "-";

    if (!layout[property]) {
      return value;
    }

    let parsedValue = parseFloat(layout[property]);

    if (Number.isNaN(parsedValue)) {
      // Not a number. We use the raw string.
      value = layout[property];
    } else {
      value = parsedValue;
    }

    return value;
  }

  getWidthValue(property) {
    if (property == undefined) {
      return "-";
    }

    let { layout } = this.props.boxModel;

    property -= parseFloat(layout["border-left-width"]) +
                parseFloat(layout["border-right-width"]) +
                parseFloat(layout["padding-left"]) +
                parseFloat(layout["padding-right"]);
    property = parseFloat(property.toPrecision(6));

    return property;
  }

  /**
   * Move the focus to the next/previous editable element of the current layout.
   *
   * @param  {Element} target
   *         Node to be observed
   * @param  {Boolean} shiftKey
   *         Determines if shiftKey was pressed
   * @param  {String} level
   *         Current active layout
   */
  moveFocus({ target, shiftKey }, level) {
    let editBoxes = [
      ...findDOMNode(this).querySelectorAll(`[data-box="${level}"].boxmodel-editable`)
    ];
    let editingMode = target.tagName === "input";
    // target.nextSibling is input field
    let position = editingMode ? editBoxes.indexOf(target.nextSibling)
                               : editBoxes.indexOf(target);

    if (position === editBoxes.length - 1 && !shiftKey) {
      position = 0;
    } else if (position === 0 && shiftKey) {
      position = editBoxes.length - 1;
    } else {
      shiftKey ? position-- : position++;
    }

    let editBox = editBoxes[position];
    editBox.focus();

    if (editingMode) {
      editBox.click();
    }
  }

  /**
   * Active aria-level set to current layout.
   *
   * @param  {Element} nextLayout
   *         Element of next layout that user has navigated to
   */
  setAriaActive(nextLayout) {
    let { boxModelContainer } = this.props;

    // We set this attribute for testing purposes.
    if (boxModelContainer) {
      boxModelContainer.setAttribute("activedescendant", nextLayout.className);
    }

    this.setState({
      activeDescendant: nextLayout.getAttribute("data-box"),
    });
  }

  onHighlightMouseOver(event) {
    let region = event.target.getAttribute("data-box");

    if (!region) {
      let el = event.target;

      do {
        el = el.parentNode;

        if (el && el.getAttribute("data-box")) {
          region = el.getAttribute("data-box");
          break;
        }
      } while (el.parentNode);

      this.props.onHideBoxModelHighlighter();
    }

    this.props.onShowBoxModelHighlighter({
      region,
      showOnly: region,
      onlyRegionArea: true,
    });
  }

  /**
   * Handle keyboard navigation and focus for box model layouts.
   *
   * Updates active layout on arrow key navigation
   * Focuses next layout's editboxes on enter key
   * Unfocuses current layout's editboxes when active layout changes
   * Controls tabbing between editBoxes
   *
   * @param  {Event} event
   *         The event triggered by a keypress on the box model
   */
  onKeyDown(event) {
    let { target, keyCode } = event;
    let isEditable = target._editable || target.editor;

    let level = this.getAriaActiveDescendant();
    let editingMode = target.tagName === "input";

    switch (keyCode) {
      case KeyCodes.DOM_VK_RETURN:
        if (!isEditable) {
          this.setState({ focusable: true }, () => {
            let editableBox = this.layouts[level].get(keyCode);
            if (editableBox) {
              editableBox.boxModelEditable.focus();
            }
          });
        }
        break;
      case KeyCodes.DOM_VK_DOWN:
      case KeyCodes.DOM_VK_UP:
        if (!editingMode) {
          event.preventDefault();
          event.stopPropagation();
          this.setState({ focusable: false }, () => {
            let nextLayout = this.layouts[level].get(keyCode);

            if (!nextLayout) {
              return;
            }

            this.setAriaActive(nextLayout);

            if (target && target._editable) {
              target.blur();
            }

            this.props.boxModelContainer.focus();
          });
        }
        break;
      case KeyCodes.DOM_VK_TAB:
        if (isEditable) {
          event.preventDefault();
          this.moveFocus(event, level);
        }
        break;
      case KeyCodes.DOM_VK_ESCAPE:
        if (target._editable) {
          event.preventDefault();
          event.stopPropagation();
          this.setState({ focusable: false }, () => {
            this.props.boxModelContainer.focus();
          });
        }
        break;
      default:
        break;
    }
  }

  /**
   * Update aria-active on mouse click.
   *
   * @param  {Event} event
   *         The event triggered by a mouse click on the box model
   */
  onLevelClick(event) {
    let { target } = event;
    let displayPosition = this.getDisplayPosition();
    let isContentBox = this.getContextBox();

    // Avoid switching the aria active descendant to the position or content layout
    // if those are not editable.
    if ((!displayPosition && target == this.positionLayout) ||
        (!isContentBox && target == this.contentLayout)) {
      return;
    }

    let nextLayout = this.layouts[target.getAttribute("data-box")].get("click");
    this.setAriaActive(nextLayout);

    if (target && target._editable) {
      target.blur();
    }
  }

  render() {
    let {
      boxModel,
      onShowBoxModelEditor,
    } = this.props;
    let { layout } = boxModel;
    let { height, width } = layout;
    let { activeDescendant: level, focusable } = this.state;

    let borderTop = this.getBorderOrPaddingValue("border-top-width");
    let borderRight = this.getBorderOrPaddingValue("border-right-width");
    let borderBottom = this.getBorderOrPaddingValue("border-bottom-width");
    let borderLeft = this.getBorderOrPaddingValue("border-left-width");

    let paddingTop = this.getBorderOrPaddingValue("padding-top");
    let paddingRight = this.getBorderOrPaddingValue("padding-right");
    let paddingBottom = this.getBorderOrPaddingValue("padding-bottom");
    let paddingLeft = this.getBorderOrPaddingValue("padding-left");

    let displayPosition = this.getDisplayPosition();
    let positionTop = this.getPositionValue("top");
    let positionRight = this.getPositionValue("right");
    let positionBottom = this.getPositionValue("bottom");
    let positionLeft = this.getPositionValue("left");

    let marginTop = this.getMarginValue("margin-top", "top");
    let marginRight = this.getMarginValue("margin-right", "right");
    let marginBottom = this.getMarginValue("margin-bottom", "bottom");
    let marginLeft = this.getMarginValue("margin-left", "left");

    height = this.getHeightValue(height);
    width = this.getWidthValue(width);

    let contentBox = layout["box-sizing"] == "content-box" ?
      dom.div(
        {
          className: "boxmodel-size",
        },
        BoxModelEditable({
          box: "content",
          focusable,
          level,
          property: "width",
          ref: editable => {
            this.contentEditable = editable;
          },
          textContent: width,
          onShowBoxModelEditor
        }),
        dom.span(
          {},
          "\u00D7"
        ),
        BoxModelEditable({
          box: "content",
          focusable,
          level,
          property: "height",
          textContent: height,
          onShowBoxModelEditor
        })
      )
      :
      dom.p(
        {
          className: "boxmodel-size",
        },
        dom.span(
          {
            title: BOXMODEL_L10N.getStr("boxmodel.content"),
          },
          SHARED_L10N.getFormatStr("dimensions", width, height)
        )
      );

    return dom.div(
      {
        className: "boxmodel-main devtools-monospace",
        "data-box": "position",
        ref: div => {
          this.positionLayout = div;
        },
        onClick: this.onLevelClick,
        onKeyDown: this.onKeyDown,
        onMouseOver: this.onHighlightMouseOver,
        onMouseOut: this.props.onHideBoxModelHighlighter,
      },
      dom.div(
        {
          className: "boxmodel-box"
        },
        dom.span(
          {
            className: "boxmodel-legend",
            "data-box": "margin",
            title: BOXMODEL_L10N.getStr("boxmodel.margin"),
          },
          BOXMODEL_L10N.getStr("boxmodel.margin")
        ),
        dom.div(
          {
            className: "boxmodel-margins",
            "data-box": "margin",
            title: BOXMODEL_L10N.getStr("boxmodel.margin"),
            ref: div => {
              this.marginLayout = div;
            },
          },
          dom.span(
            {
              className: "boxmodel-legend",
              "data-box": "border",
              title: BOXMODEL_L10N.getStr("boxmodel.border"),
            },
            BOXMODEL_L10N.getStr("boxmodel.border")
          ),
          dom.div(
            {
              className: "boxmodel-borders",
              "data-box": "border",
              title: BOXMODEL_L10N.getStr("boxmodel.border"),
              ref: div => {
                this.borderLayout = div;
              },
            },
            dom.span(
              {
                className: "boxmodel-legend",
                "data-box": "padding",
                title: BOXMODEL_L10N.getStr("boxmodel.padding"),
              },
              BOXMODEL_L10N.getStr("boxmodel.padding")
            ),
            dom.div(
              {
                className: "boxmodel-paddings",
                "data-box": "padding",
                title: BOXMODEL_L10N.getStr("boxmodel.padding"),
                ref: div => {
                  this.paddingLayout = div;
                },
              },
              dom.div({
                className: "boxmodel-contents",
                "data-box": "content",
                title: BOXMODEL_L10N.getStr("boxmodel.content"),
                ref: div => {
                  this.contentLayout = div;
                },
              })
            )
          )
        )
      ),
      displayPosition ?
        BoxModelEditable({
          box: "position",
          direction: "top",
          focusable,
          level,
          property: "position-top",
          ref: editable => {
            this.positionEditable = editable;
          },
          textContent: positionTop,
          onShowBoxModelEditor,
        })
        :
        null,
      displayPosition ?
        BoxModelEditable({
          box: "position",
          direction: "right",
          focusable,
          level,
          property: "position-right",
          textContent: positionRight,
          onShowBoxModelEditor,
        })
        :
        null,
      displayPosition ?
        BoxModelEditable({
          box: "position",
          direction: "bottom",
          focusable,
          level,
          property: "position-bottom",
          textContent: positionBottom,
          onShowBoxModelEditor,
        })
        :
        null,
      displayPosition ?
        BoxModelEditable({
          box: "position",
          direction: "left",
          focusable,
          level,
          property: "position-left",
          textContent: positionLeft,
          onShowBoxModelEditor,
        })
        :
        null,
      BoxModelEditable({
        box: "margin",
        direction: "top",
        focusable,
        level,
        property: "margin-top",
        ref: editable => {
          this.marginEditable = editable;
        },
        textContent: marginTop,
        onShowBoxModelEditor,
      }),
      BoxModelEditable({
        box: "margin",
        direction: "right",
        focusable,
        level,
        property: "margin-right",
        textContent: marginRight,
        onShowBoxModelEditor,
      }),
      BoxModelEditable({
        box: "margin",
        direction: "bottom",
        focusable,
        level,
        property: "margin-bottom",
        textContent: marginBottom,
        onShowBoxModelEditor,
      }),
      BoxModelEditable({
        box: "margin",
        direction: "left",
        focusable,
        level,
        property: "margin-left",
        textContent: marginLeft,
        onShowBoxModelEditor,
      }),
      BoxModelEditable({
        box: "border",
        direction: "top",
        focusable,
        level,
        property: "border-top-width",
        ref: editable => {
          this.borderEditable = editable;
        },
        textContent: borderTop,
        onShowBoxModelEditor,
      }),
      BoxModelEditable({
        box: "border",
        direction: "right",
        focusable,
        level,
        property: "border-right-width",
        textContent: borderRight,
        onShowBoxModelEditor,
      }),
      BoxModelEditable({
        box: "border",
        direction: "bottom",
        focusable,
        level,
        property: "border-bottom-width",
        textContent: borderBottom,
        onShowBoxModelEditor,
      }),
      BoxModelEditable({
        box: "border",
        direction: "left",
        focusable,
        level,
        property: "border-left-width",
        textContent: borderLeft,
        onShowBoxModelEditor,
      }),
      BoxModelEditable({
        box: "padding",
        direction: "top",
        focusable,
        level,
        property: "padding-top",
        ref: editable => {
          this.paddingEditable = editable;
        },
        textContent: paddingTop,
        onShowBoxModelEditor,
      }),
      BoxModelEditable({
        box: "padding",
        direction: "right",
        focusable,
        level,
        property: "padding-right",
        textContent: paddingRight,
        onShowBoxModelEditor,
      }),
      BoxModelEditable({
        box: "padding",
        direction: "bottom",
        focusable,
        level,
        property: "padding-bottom",
        textContent: paddingBottom,
        onShowBoxModelEditor,
      }),
      BoxModelEditable({
        box: "padding",
        direction: "left",
        focusable,
        level,
        property: "padding-left",
        textContent: paddingLeft,
        onShowBoxModelEditor,
      }),
      contentBox
    );
  }
}

module.exports = BoxModelMain;
PK
!<H�����[chrome/devtools/modules/devtools/client/inspector/boxmodel/components/BoxModelProperties.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const { LocalizationHelper } = require("devtools/shared/l10n");

const ComputedProperty = createFactory(require("./ComputedProperty"));

const Types = require("../types");

const BOXMODEL_STRINGS_URI = "devtools/client/locales/boxmodel.properties";
const BOXMODEL_L10N = new LocalizationHelper(BOXMODEL_STRINGS_URI);

class BoxModelProperties extends PureComponent {
  static get propTypes() {
    return {
      boxModel: PropTypes.shape(Types.boxModel).isRequired,
      setSelectedNode: PropTypes.func.isRequired,
      onHideBoxModelHighlighter: PropTypes.func.isRequired,
      onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
    };
  }

  constructor(props) {
    super(props);

    this.state = {
      isOpen: true,
    };

    this.getReferenceElement = this.getReferenceElement.bind(this);
    this.onToggleExpander = this.onToggleExpander.bind(this);
  }

  /**
   * Various properties can display a reference element. E.g. position displays an offset
   * parent if its value is other than fixed or static. Or z-index displays a stacking
   * context, etc.
   * This returns the right element if there needs to be one, and one was passed in the
   * props.
   *
   * @return {Object} An object with 2 properties:
   * - referenceElement {NodeFront}
   * - referenceElementType {String}
   */
  getReferenceElement(propertyName) {
    let value = this.props.boxModel.layout[propertyName];

    if (propertyName === "position" &&
        value !== "static" && value !== "fixed" &&
        this.props.boxModel.offsetParent) {
      return {
        referenceElement: this.props.boxModel.offsetParent,
        referenceElementType: BOXMODEL_L10N.getStr("boxmodel.offsetParent")
      };
    }

    return {};
  }

  onToggleExpander(event) {
    this.setState({
      isOpen: !this.state.isOpen,
    });
    event.stopPropagation();
  }

  render() {
    let {
      boxModel,
      setSelectedNode,
      onHideBoxModelHighlighter,
      onShowBoxModelHighlighterForNode,
    } = this.props;
    let { layout } = boxModel;

    let layoutInfo = ["box-sizing", "display", "float",
                      "line-height", "position", "z-index"];

    const properties = layoutInfo.map(info => {
      let { referenceElement, referenceElementType } = this.getReferenceElement(info);

      return ComputedProperty({
        name: info,
        key: info,
        value: layout[info],
        referenceElement,
        referenceElementType,
        setSelectedNode,
        onHideBoxModelHighlighter,
        onShowBoxModelHighlighterForNode,
      });
    });

    return dom.div(
      {
        className: "boxmodel-properties",
      },
      dom.div(
        {
          className: "boxmodel-properties-header",
          onDoubleClick: this.onToggleExpander,
        },
        dom.span(
          {
            className: "boxmodel-properties-expander theme-twisty",
            open: this.state.isOpen,
            onClick: this.onToggleExpander,
          }
        ),
        BOXMODEL_L10N.getStr("boxmodel.propertiesLabel")
      ),
      dom.div(
        {
          className: "boxmodel-properties-wrapper devtools-monospace",
          hidden: !this.state.isOpen,
          tabIndex: 0,
        },
        properties
      )
    );
  }
}

module.exports = BoxModelProperties;
PK
!<��&,,Ychrome/devtools/modules/devtools/client/inspector/boxmodel/components/ComputedProperty.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const { translateNodeFrontToGrip } = require("devtools/client/inspector/shared/utils");

const { REPS, MODE } = require("devtools/client/shared/components/reps/reps");
const { Rep } = REPS;

class ComputedProperty extends PureComponent {
  static get propTypes() {
    return {
      name: PropTypes.string.isRequired,
      referenceElement: PropTypes.object,
      referenceElementType: PropTypes.string,
      setSelectedNode: PropTypes.func.isRequired,
      value: PropTypes.string,
      onHideBoxModelHighlighter: PropTypes.func.isRequired,
      onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
    };
  }

  constructor(props) {
    super(props);
    this.renderReferenceElementPreview = this.renderReferenceElementPreview.bind(this);
    this.onFocus = this.onFocus.bind(this);
  }

  renderReferenceElementPreview() {
    let {
      referenceElement,
      referenceElementType,
      setSelectedNode,
      onShowBoxModelHighlighterForNode,
      onHideBoxModelHighlighter
    } = this.props;

    if (!referenceElement) {
      return null;
    }

    return dom.div(
      {
        className: "reference-element"
      },
      dom.span({ className: "reference-element-type" }, referenceElementType),
      Rep({
        defaultRep: referenceElement,
        mode: MODE.TINY,
        object: translateNodeFrontToGrip(referenceElement),
        onInspectIconClick: () => setSelectedNode(referenceElement, "box-model"),
        onDOMNodeMouseOver: () => onShowBoxModelHighlighterForNode(referenceElement),
        onDOMNodeMouseOut: () => onHideBoxModelHighlighter(),
      })
    );
  }

  onFocus() {
    this.container.focus();
  }

  render() {
    const { name, value } = this.props;

    return dom.div(
      {
        className: "computed-property-view",
        "data-property-name": name,
        tabIndex: "0",
        ref: container => {
          this.container = container;
        },
      },
      dom.div(
        {
          className: "computed-property-name-container",
        },
        dom.div(
          {
            className: "computed-property-name theme-fg-color5",
            tabIndex: "",
            title: name,
            onClick: this.onFocus,
          },
          name
        )
      ),
      dom.div(
        {
          className: "computed-property-value-container",
        },
        dom.div(
          {
            className: "computed-property-value theme-fg-color1",
            dir: "ltr",
            tabIndex: "",
            onClick: this.onFocus,
          },
          value
        ),
        this.renderReferenceElementPreview()
      )
    );
  }
}

module.exports = ComputedProperty;
PK
!<`��Pchrome/devtools/modules/devtools/client/inspector/boxmodel/reducers/box-model.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  UPDATE_GEOMETRY_EDITOR_ENABLED,
  UPDATE_LAYOUT,
  UPDATE_OFFSET_PARENT,
} = require("../actions/index");

const INITIAL_BOX_MODEL = {
  geometryEditorEnabled: false,
  layout: {},
  offsetParent: null
};

let reducers = {

  [UPDATE_GEOMETRY_EDITOR_ENABLED](boxModel, { enabled }) {
    return Object.assign({}, boxModel, {
      geometryEditorEnabled: enabled,
    });
  },

  [UPDATE_LAYOUT](boxModel, { layout }) {
    return Object.assign({}, boxModel, {
      layout,
    });
  },

  [UPDATE_OFFSET_PARENT](boxModel, { offsetParent }) {
    return Object.assign({}, boxModel, {
      offsetParent,
    });
  },

};

module.exports = function (boxModel = INITIAL_BOX_MODEL, action) {
  let reducer = reducers[action.type];
  if (!reducer) {
    return boxModel;
  }
  return reducer(boxModel, action);
};
PK
!<J��Յ�Cchrome/devtools/modules/devtools/client/inspector/boxmodel/types.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/vendor/react-prop-types");

/**
 * The box model data for the current selected node.
 */
exports.boxModel = {
  // Whether or not the geometry editor is enabled
  geometryEditorEnabled: PropTypes.boolean,

  // The layout information of the current selected node
  layout: PropTypes.object,

  // The offset parent for the selected node
  offsetParent: PropTypes.object,
};
PK
!<�*����Schrome/devtools/modules/devtools/client/inspector/boxmodel/utils/editing-session.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Task } = require("devtools/shared/task");
const { getCssProperties } = require("devtools/shared/fronts/css-properties");

/**
 * An instance of EditingSession tracks changes that have been made during the
 * modification of box model values. All of these changes can be reverted by
 * calling revert.
 *
 * @param  {InspectorPanel} inspector
 *         The inspector panel.
 * @param  {Document} doc
 *         A DOM document that can be used to test style rules.
 * @param  {Array} rules
 *         An array of the style rules defined for the node being
 *         edited. These should be in order of priority, least
 *         important first.
 */
function EditingSession({inspector, doc, elementRules}) {
  this._doc = doc;
  this._rules = elementRules;
  this._modifications = new Map();
  this._cssProperties = getCssProperties(inspector.toolbox);
}

EditingSession.prototype = {
  /**
   * Gets the value of a single property from the CSS rule.
   *
   * @param  {StyleRuleFront} rule
   *         The CSS rule.
   * @param  {String} property
   *         The name of the property.
   * @return {String} the value.
   */
  getPropertyFromRule: function (rule, property) {
    // Use the parsed declarations in the StyleRuleFront object if available.
    let index = this.getPropertyIndex(property, rule);
    if (index !== -1) {
      return rule.declarations[index].value;
    }

    // Fallback to parsing the cssText locally otherwise.
    let dummyStyle = this._element.style;
    dummyStyle.cssText = rule.cssText;
    return dummyStyle.getPropertyValue(property);
  },

  /**
   * Returns the current value for a property as a string or the empty string if
   * no style rules affect the property.
   *
   * @param  {String} property
   *         The name of the property as a string
   */
  getProperty: function (property) {
    // Create a hidden element for getPropertyFromRule to use
    let div = this._doc.createElement("div");
    div.setAttribute("style", "display: none");
    this._doc.getElementById("inspector-main-content").appendChild(div);
    this._element = this._doc.createElement("p");
    div.appendChild(this._element);

    // As the rules are in order of priority we can just iterate until we find
    // the first that defines a value for the property and return that.
    for (let rule of this._rules) {
      let value = this.getPropertyFromRule(rule, property);
      if (value !== "") {
        div.remove();
        return value;
      }
    }
    div.remove();
    return "";
  },

  /**
   * Get the index of a given css property name in a CSS rule.
   * Or -1, if there are no properties in the rule yet.
   *
   * @param  {String} name
   *         The property name.
   * @param  {StyleRuleFront} rule
   *         Optional, defaults to the element style rule.
   * @return {Number} The property index in the rule.
   */
  getPropertyIndex: function (name, rule = this._rules[0]) {
    let elementStyleRule = this._rules[0];
    if (!elementStyleRule.declarations.length) {
      return -1;
    }

    return elementStyleRule.declarations.findIndex(p => p.name === name);
  },

  /**
   * Sets a number of properties on the node.
   *
   * @param  {Array} properties
   *         An array of properties, each is an object with name and
   *         value properties. If the value is "" then the property
   *         is removed.
   * @return {Promise} Resolves when the modifications are complete.
   */
  setProperties: Task.async(function* (properties) {
    for (let property of properties) {
      // Get a RuleModificationList or RuleRewriter helper object from the
      // StyleRuleActor to make changes to CSS properties.
      // Note that RuleRewriter doesn't support modifying several properties at
      // once, so we do this in a sequence here.
      let modifications = this._rules[0].startModifyingProperties(
        this._cssProperties);

      // Remember the property so it can be reverted.
      if (!this._modifications.has(property.name)) {
        this._modifications.set(property.name,
          this.getPropertyFromRule(this._rules[0], property.name));
      }

      // Find the index of the property to be changed, or get the next index to
      // insert the new property at.
      let index = this.getPropertyIndex(property.name);
      if (index === -1) {
        index = this._rules[0].declarations.length;
      }

      if (property.value == "") {
        modifications.removeProperty(index, property.name);
      } else {
        modifications.setProperty(index, property.name, property.value, "");
      }

      yield modifications.apply();
    }
  }),

  /**
   * Reverts all of the property changes made by this instance.
   *
   * @return {Promise} Resolves when all properties have been reverted.
   */
  revert: Task.async(function* () {
    // Revert each property that we modified previously, one by one. See
    // setProperties for information about why.
    for (let [property, value] of this._modifications) {
      let modifications = this._rules[0].startModifyingProperties(
        this._cssProperties);

      // Find the index of the property to be reverted.
      let index = this.getPropertyIndex(property);

      if (value != "") {
        // If the property doesn't exist anymore, insert at the beginning of the
        // rule.
        if (index === -1) {
          index = 0;
        }
        modifications.setProperty(index, property, value, "");
      } else {
        // If the property doesn't exist anymore, no need to remove it. It had
        // not been added after all.
        if (index === -1) {
          continue;
        }
        modifications.removeProperty(index, property);
      }

      yield modifications.apply();
    }
  }),

  destroy: function () {
    this._doc = null;
    this._rules = null;
    this._modifications.clear();
  }
};

module.exports = EditingSession;
PK
!<�����o�o@chrome/devtools/modules/devtools/client/inspector/breadcrumbs.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 promise = require("promise");

const {ELLIPSIS} = require("devtools/shared/l10n");

const MAX_LABEL_LENGTH = 40;

const NS_XHTML = "http://www.w3.org/1999/xhtml";
const SCROLL_REPEAT_MS = 100;

const EventEmitter = require("devtools/shared/old-event-emitter");
const KeyShortcuts = require("devtools/client/shared/key-shortcuts");

// Some margin may be required for visible element detection.
const SCROLL_MARGIN = 1;

/**
 * Component to replicate functionality of XUL arrowscrollbox
 * for breadcrumbs
 *
 * @param {Window} win The window containing the breadcrumbs
 * @parem {DOMNode} container The element in which to put the scroll box
 */
function ArrowScrollBox(win, container) {
  this.win = win;
  this.doc = win.document;
  this.container = container;
  EventEmitter.decorate(this);
  this.init();
}

ArrowScrollBox.prototype = {

  // Scroll behavior, exposed for testing
  scrollBehavior: "smooth",

  /**
   * Build the HTML, add to the DOM and start listening to
   * events
   */
  init: function () {
    this.constructHtml();

    this.onUnderflow();

    this.onScroll = this.onScroll.bind(this);
    this.onStartBtnClick = this.onStartBtnClick.bind(this);
    this.onEndBtnClick = this.onEndBtnClick.bind(this);
    this.onStartBtnDblClick = this.onStartBtnDblClick.bind(this);
    this.onEndBtnDblClick = this.onEndBtnDblClick.bind(this);
    this.onUnderflow = this.onUnderflow.bind(this);
    this.onOverflow = this.onOverflow.bind(this);

    this.inner.addEventListener("scroll", this.onScroll);
    this.startBtn.addEventListener("mousedown", this.onStartBtnClick);
    this.endBtn.addEventListener("mousedown", this.onEndBtnClick);
    this.startBtn.addEventListener("dblclick", this.onStartBtnDblClick);
    this.endBtn.addEventListener("dblclick", this.onEndBtnDblClick);

    // Overflow and underflow are moz specific events
    this.inner.addEventListener("underflow", this.onUnderflow);
    this.inner.addEventListener("overflow", this.onOverflow);
  },

  /**
   * Determine whether the current text directionality is RTL
   */
  isRtl: function () {
    return this.doc.dir === "rtl";
  },

  /**
   * Scroll to the specified element using the current scroll behavior
   * @param {Element} element element to scroll
   * @param {String} block desired alignment of element after scrolling
   */
  scrollToElement: function (element, block) {
    element.scrollIntoView({ block: block, behavior: this.scrollBehavior });
  },

  /**
   * Call the given function once; then continuously
   * while the mouse button is held
   * @param {Function} repeatFn the function to repeat while the button is held
   */
  clickOrHold: function (repeatFn) {
    let timer;
    let container = this.container;

    function handleClick() {
      cancelHold();
      repeatFn();
    }

    let window = this.win;
    function cancelHold() {
      window.clearTimeout(timer);
      container.removeEventListener("mouseout", cancelHold);
      container.removeEventListener("mouseup", handleClick);
    }

    function repeated() {
      repeatFn();
      timer = window.setTimeout(repeated, SCROLL_REPEAT_MS);
    }

    container.addEventListener("mouseout", cancelHold);
    container.addEventListener("mouseup", handleClick);
    timer = window.setTimeout(repeated, SCROLL_REPEAT_MS);
  },

  /**
   * When start button is dbl clicked scroll to first element
   */
  onStartBtnDblClick: function () {
    let children = this.inner.childNodes;
    if (children.length < 1) {
      return;
    }

    let element = this.inner.childNodes[0];
    this.scrollToElement(element, "start");
  },

  /**
   * When end button is dbl clicked scroll to last element
   */
  onEndBtnDblClick: function () {
    let children = this.inner.childNodes;
    if (children.length < 1) {
      return;
    }

    let element = children[children.length - 1];
    this.scrollToElement(element, "start");
  },

  /**
   * When start arrow button is clicked scroll towards first element
   */
  onStartBtnClick: function () {
    let scrollToStart = () => {
      let element = this.getFirstInvisibleElement();
      if (!element) {
        return;
      }

      let block = this.isRtl() ? "end" : "start";
      this.scrollToElement(element, block);
    };

    this.clickOrHold(scrollToStart);
  },

  /**
   * When end arrow button is clicked scroll towards last element
   */
  onEndBtnClick: function () {
    let scrollToEnd = () => {
      let element = this.getLastInvisibleElement();
      if (!element) {
        return;
      }

      let block = this.isRtl() ? "start" : "end";
      this.scrollToElement(element, block);
    };

    this.clickOrHold(scrollToEnd);
  },

  /**
   * Event handler for scrolling, update the
   * enabled/disabled status of the arrow buttons
   */
  onScroll: function () {
    let first = this.getFirstInvisibleElement();
    if (!first) {
      this.startBtn.setAttribute("disabled", "true");
    } else {
      this.startBtn.removeAttribute("disabled");
    }

    let last = this.getLastInvisibleElement();
    if (!last) {
      this.endBtn.setAttribute("disabled", "true");
    } else {
      this.endBtn.removeAttribute("disabled");
    }
  },

  /**
   * On underflow, make the arrow buttons invisible
   */
  onUnderflow: function () {
    this.startBtn.style.visibility = "collapse";
    this.endBtn.style.visibility = "collapse";
    this.emit("underflow");
  },

  /**
   * On overflow, show the arrow buttons
   */
  onOverflow: function () {
    this.startBtn.style.visibility = "visible";
    this.endBtn.style.visibility = "visible";
    this.emit("overflow");
  },

  /**
   * Check whether the element is to the left of its container but does
   * not also span the entire container.
   * @param {Number} left the left scroll point of the container
   * @param {Number} right the right edge of the container
   * @param {Number} elementLeft the left edge of the element
   * @param {Number} elementRight the right edge of the element
   */
  elementLeftOfContainer: function (left, right, elementLeft, elementRight) {
    return elementLeft < (left - SCROLL_MARGIN)
           && elementRight < (right - SCROLL_MARGIN);
  },

  /**
   * Check whether the element is to the right of its container but does
   * not also span the entire container.
   * @param {Number} left the left scroll point of the container
   * @param {Number} right the right edge of the container
   * @param {Number} elementLeft the left edge of the element
   * @param {Number} elementRight the right edge of the element
   */
  elementRightOfContainer: function (left, right, elementLeft, elementRight) {
    return elementLeft > (left + SCROLL_MARGIN)
           && elementRight > (right + SCROLL_MARGIN);
  },

  /**
   * Get the first (i.e. furthest left for LTR)
   * non or partly visible element in the scroll box
   */
  getFirstInvisibleElement: function () {
    let elementsList = Array.from(this.inner.childNodes).reverse();

    let predicate = this.isRtl() ?
      this.elementRightOfContainer : this.elementLeftOfContainer;
    return this.findFirstWithBounds(elementsList, predicate);
  },

  /**
   * Get the last (i.e. furthest right for LTR)
   * non or partly visible element in the scroll box
   */
  getLastInvisibleElement: function () {
    let predicate = this.isRtl() ?
      this.elementLeftOfContainer : this.elementRightOfContainer;
    return this.findFirstWithBounds(this.inner.childNodes, predicate);
  },

  /**
   * Find the first element that matches the given predicate, called with bounds
   * information
   * @param {Array} elements an ordered list of elements
   * @param {Function} predicate a function to be called with bounds
   * information
   */
  findFirstWithBounds: function (elements, predicate) {
    let left = this.inner.scrollLeft;
    let right = left + this.inner.clientWidth;
    for (let element of elements) {
      let elementLeft = element.offsetLeft - element.parentElement.offsetLeft;
      let elementRight = elementLeft + element.offsetWidth;

      // Check that the starting edge of the element is out of the visible area
      // and that the ending edge does not span the whole container
      if (predicate(left, right, elementLeft, elementRight)) {
        return element;
      }
    }

    return null;
  },

  /**
   * Build the HTML for the scroll box and insert it into the DOM
   */
  constructHtml: function () {
    this.startBtn = this.createElement("div", "scrollbutton-up",
                                       this.container);
    this.createElement("div", "toolbarbutton-icon", this.startBtn);

    this.createElement("div", "arrowscrollbox-overflow-start-indicator",
                       this.container);
    this.inner = this.createElement("div", "html-arrowscrollbox-inner",
                                    this.container);
    this.createElement("div", "arrowscrollbox-overflow-end-indicator",
                       this.container);

    this.endBtn = this.createElement("div", "scrollbutton-down",
                                     this.container);
    this.createElement("div", "toolbarbutton-icon", this.endBtn);
  },

  /**
   * Create an XHTML element with the given class name, and append it to the
   * parent.
   * @param {String} tagName name of the tag to create
   * @param {String} className class of the element
   * @param {DOMNode} parent the parent node to which it should be appended
   * @return {DOMNode} The new element
   */
  createElement: function (tagName, className, parent) {
    let el = this.doc.createElementNS(NS_XHTML, tagName);
    el.className = className;
    if (parent) {
      parent.appendChild(el);
    }

    return el;
  },

  /**
   * Remove event handlers and clean up
   */
  destroy: function () {
    this.inner.removeEventListener("scroll", this.onScroll);
    this.startBtn.removeEventListener("mousedown",
                                      this.onStartBtnClick);
    this.endBtn.removeEventListener("mousedown", this.onEndBtnClick);
    this.startBtn.removeEventListener("dblclick",
                                      this.onStartBtnDblClick);
    this.endBtn.removeEventListener("dblclick",
                                    this.onRightBtnDblClick);

    // Overflow and underflow are moz specific events
    this.inner.removeEventListener("underflow", this.onUnderflow);
    this.inner.removeEventListener("overflow", this.onOverflow);
  },
};

/**
 * Display the ancestors of the current node and its children.
 * Only one "branch" of children are displayed (only one line).
 *
 * Mechanism:
 * - If no nodes displayed yet:
 *   then display the ancestor of the selected node and the selected node;
 *   else select the node;
 * - If the selected node is the last node displayed, append its first (if any).
 *
 * @param {InspectorPanel} inspector The inspector hosting this widget.
 */
function HTMLBreadcrumbs(inspector) {
  this.inspector = inspector;
  this.selection = this.inspector.selection;
  this.win = this.inspector.panelWin;
  this.doc = this.inspector.panelDoc;
  this._init();
}

exports.HTMLBreadcrumbs = HTMLBreadcrumbs;

HTMLBreadcrumbs.prototype = {
  get walker() {
    return this.inspector.walker;
  },

  _init: function () {
    this.outer = this.doc.getElementById("inspector-breadcrumbs");
    this.arrowScrollBox = new ArrowScrollBox(
        this.win,
        this.outer);

    this.container = this.arrowScrollBox.inner;
    this.scroll = this.scroll.bind(this);
    this.arrowScrollBox.on("overflow", this.scroll);

    this.outer.addEventListener("click", this, true);
    this.outer.addEventListener("mouseover", this, true);
    this.outer.addEventListener("mouseout", this, true);
    this.outer.addEventListener("focus", this, true);

    this.shortcuts = new KeyShortcuts({ window: this.win, target: this.outer });
    this.handleShortcut = this.handleShortcut.bind(this);

    this.shortcuts.on("Right", this.handleShortcut);
    this.shortcuts.on("Left", this.handleShortcut);

    // We will save a list of already displayed nodes in this array.
    this.nodeHierarchy = [];

    // Last selected node in nodeHierarchy.
    this.currentIndex = -1;

    // Used to build a unique breadcrumb button Id.
    this.breadcrumbsWidgetItemId = 0;

    this.update = this.update.bind(this);
    this.updateSelectors = this.updateSelectors.bind(this);
    this.selection.on("new-node-front", this.update);
    this.selection.on("pseudoclass", this.updateSelectors);
    this.selection.on("attribute-changed", this.updateSelectors);
    this.inspector.on("markupmutation", this.update);
    this.update();
  },

  /**

   * Build a string that represents the node: tagName#id.class1.class2.
   * @param {NodeFront} node The node to pretty-print
   * @return {String}
   */
  prettyPrintNodeAsText: function (node) {
    let text = node.displayName;
    if (node.isPseudoElement) {
      text = node.isBeforePseudoElement ? "::before" : "::after";
    }

    if (node.id) {
      text += "#" + node.id;
    }

    if (node.className) {
      let classList = node.className.split(/\s+/);
      for (let i = 0; i < classList.length; i++) {
        text += "." + classList[i];
      }
    }

    for (let pseudo of node.pseudoClassLocks) {
      text += pseudo;
    }

    return text;
  },

  /**
   * Build <span>s that represent the node:
   *   <span class="breadcrumbs-widget-item-tag">tagName</span>
   *   <span class="breadcrumbs-widget-item-id">#id</span>
   *   <span class="breadcrumbs-widget-item-classes">.class1.class2</span>
   * @param {NodeFront} node The node to pretty-print
   * @returns {DocumentFragment}
   */
  prettyPrintNodeAsXHTML: function (node) {
    let tagLabel = this.doc.createElementNS(NS_XHTML, "span");
    tagLabel.className = "breadcrumbs-widget-item-tag plain";

    let idLabel = this.doc.createElementNS(NS_XHTML, "span");
    idLabel.className = "breadcrumbs-widget-item-id plain";

    let classesLabel = this.doc.createElementNS(NS_XHTML, "span");
    classesLabel.className = "breadcrumbs-widget-item-classes plain";

    let pseudosLabel = this.doc.createElementNS(NS_XHTML, "span");
    pseudosLabel.className = "breadcrumbs-widget-item-pseudo-classes plain";

    let tagText = node.displayName;
    if (node.isPseudoElement) {
      tagText = node.isBeforePseudoElement ? "::before" : "::after";
    }
    let idText = node.id ? ("#" + node.id) : "";
    let classesText = "";

    if (node.className) {
      let classList = node.className.split(/\s+/);
      for (let i = 0; i < classList.length; i++) {
        classesText += "." + classList[i];
      }
    }

    // Figure out which element (if any) needs ellipsing.
    // Substring for that element, then clear out any extras
    // (except for pseudo elements).
    let maxTagLength = MAX_LABEL_LENGTH;
    let maxIdLength = MAX_LABEL_LENGTH - tagText.length;
    let maxClassLength = MAX_LABEL_LENGTH - tagText.length - idText.length;

    if (tagText.length > maxTagLength) {
      tagText = tagText.substr(0, maxTagLength) + ELLIPSIS;
      idText = classesText = "";
    } else if (idText.length > maxIdLength) {
      idText = idText.substr(0, maxIdLength) + ELLIPSIS;
      classesText = "";
    } else if (classesText.length > maxClassLength) {
      classesText = classesText.substr(0, maxClassLength) + ELLIPSIS;
    }

    tagLabel.textContent = tagText;
    idLabel.textContent = idText;
    classesLabel.textContent = classesText;
    pseudosLabel.textContent = node.pseudoClassLocks.join("");

    let fragment = this.doc.createDocumentFragment();
    fragment.appendChild(tagLabel);
    fragment.appendChild(idLabel);
    fragment.appendChild(classesLabel);
    fragment.appendChild(pseudosLabel);

    return fragment;
  },

  /**
   * Generic event handler.
   * @param {DOMEvent} event.
   */
  handleEvent: function (event) {
    if (event.type == "click" && event.button == 0) {
      this.handleClick(event);
    } else if (event.type == "mouseover") {
      this.handleMouseOver(event);
    } else if (event.type == "mouseout") {
      this.handleMouseOut(event);
    } else if (event.type == "focus") {
      this.handleFocus(event);
    }
  },

  /**
   * Focus event handler. When breadcrumbs container gets focus,
   * aria-activedescendant needs to be updated to currently selected
   * breadcrumb. Ensures that the focus stays on the container at all times.
   * @param {DOMEvent} event.
   */
  handleFocus: function (event) {
    event.stopPropagation();

    let node = this.nodeHierarchy[this.currentIndex];
    if (node) {
      this.outer.setAttribute("aria-activedescendant", node.button.id);
    } else {
      this.outer.removeAttribute("aria-activedescendant");
    }

    this.outer.focus();
  },

  /**
   * On click navigate to the correct node.
   * @param {DOMEvent} event.
   */
  handleClick: function (event) {
    let target = event.originalTarget;
    if (target.tagName == "button") {
      target.onBreadcrumbsClick();
    }
  },

  /**
   * On mouse over, highlight the corresponding content DOM Node.
   * @param {DOMEvent} event.
   */
  handleMouseOver: function (event) {
    let target = event.originalTarget;
    if (target.tagName == "button") {
      target.onBreadcrumbsHover();
    }
  },

  /**
   * On mouse out, make sure to unhighlight.
   * @param {DOMEvent} event.
   */
  handleMouseOut: function (event) {
    this.inspector.toolbox.highlighterUtils.unhighlight();
  },

  /**
   * Handle a keyboard shortcut supported by the breadcrumbs widget.
   *
   * @param {String} name
   *        Name of the keyboard shortcut received.
   * @param {DOMEvent} event
   *        Original event that triggered the shortcut.
   */
  handleShortcut: function (event) {
    if (!this.selection.isElementNode()) {
      return;
    }

    event.preventDefault();
    event.stopPropagation();

    this.keyPromise = (this.keyPromise || promise.resolve(null)).then(() => {
      let currentnode;

      const isLeft = event.code === "ArrowLeft";
      const isRight = event.code === "ArrowRight";

      if (isLeft && this.currentIndex != 0) {
        currentnode = this.nodeHierarchy[this.currentIndex - 1];
      } else if (isRight && this.currentIndex < this.nodeHierarchy.length - 1) {
        currentnode = this.nodeHierarchy[this.currentIndex + 1];
      } else {
        return null;
      }

      this.outer.setAttribute("aria-activedescendant", currentnode.button.id);
      return this.selection.setNodeFront(currentnode.node, "breadcrumbs");
    });
  },

  /**
   * Remove nodes and clean up.
   */
  destroy: function () {
    this.selection.off("new-node-front", this.update);
    this.selection.off("pseudoclass", this.updateSelectors);
    this.selection.off("attribute-changed", this.updateSelectors);
    this.inspector.off("markupmutation", this.update);

    this.container.removeEventListener("click", this, true);
    this.container.removeEventListener("mouseover", this, true);
    this.container.removeEventListener("mouseout", this, true);
    this.container.removeEventListener("focus", this, true);
    this.shortcuts.destroy();

    this.empty();

    this.arrowScrollBox.off("overflow", this.scroll);
    this.arrowScrollBox.destroy();
    this.arrowScrollBox = null;
    this.outer = null;
    this.container = null;
    this.nodeHierarchy = null;

    this.isDestroyed = true;
  },

  /**
   * Empty the breadcrumbs container.
   */
  empty: function () {
    while (this.container.hasChildNodes()) {
      this.container.firstChild.remove();
    }
  },

  /**
   * Set which button represent the selected node.
   * @param {Number} index Index of the displayed-button to select.
   */
  setCursor: function (index) {
    // Unselect the previously selected button
    if (this.currentIndex > -1
        && this.currentIndex < this.nodeHierarchy.length) {
      this.nodeHierarchy[this.currentIndex].button.removeAttribute("checked");
    }
    if (index > -1) {
      this.nodeHierarchy[index].button.setAttribute("checked", "true");
    } else {
      // Unset active active descendant when all buttons are unselected.
      this.outer.removeAttribute("aria-activedescendant");
    }
    this.currentIndex = index;
  },

  /**
   * Get the index of the node in the cache.
   * @param {NodeFront} node.
   * @returns {Number} The index for this node or -1 if not found.
   */
  indexOf: function (node) {
    for (let i = this.nodeHierarchy.length - 1; i >= 0; i--) {
      if (this.nodeHierarchy[i].node === node) {
        return i;
      }
    }
    return -1;
  },

  /**
   * Remove all the buttons and their references in the cache after a given
   * index.
   * @param {Number} index.
   */
  cutAfter: function (index) {
    while (this.nodeHierarchy.length > (index + 1)) {
      let toRemove = this.nodeHierarchy.pop();
      this.container.removeChild(toRemove.button);
    }
  },

  /**
   * Build a button representing the node.
   * @param {NodeFront} node The node from the page.
   * @return {DOMNode} The <button> for this node.
   */
  buildButton: function (node) {
    let button = this.doc.createElementNS(NS_XHTML, "button");
    button.appendChild(this.prettyPrintNodeAsXHTML(node));
    button.className = "breadcrumbs-widget-item";
    button.id = "breadcrumbs-widget-item-" + this.breadcrumbsWidgetItemId++;

    button.setAttribute("tabindex", "-1");
    button.setAttribute("title", this.prettyPrintNodeAsText(node));

    button.onclick = () => {
      button.focus();
    };

    button.onBreadcrumbsClick = () => {
      this.selection.setNodeFront(node, "breadcrumbs");
    };

    button.onBreadcrumbsHover = () => {
      this.inspector.toolbox.highlighterUtils.highlightNodeFront(node);
    };

    return button;
  },

  /**
   * Connecting the end of the breadcrumbs to a node.
   * @param {NodeFront} node The node to reach.
   */
  expand: function (node) {
    let fragment = this.doc.createDocumentFragment();
    let lastButtonInserted = null;
    let originalLength = this.nodeHierarchy.length;
    let stopNode = null;
    if (originalLength > 0) {
      stopNode = this.nodeHierarchy[originalLength - 1].node;
    }
    while (node && node != stopNode) {
      if (node.tagName) {
        let button = this.buildButton(node);
        fragment.insertBefore(button, lastButtonInserted);
        lastButtonInserted = button;
        this.nodeHierarchy.splice(originalLength, 0, {
          node,
          button,
          currentPrettyPrintText: this.prettyPrintNodeAsText(node)
        });
      }
      node = node.parentNode();
    }
    this.container.appendChild(fragment, this.container.firstChild);
  },

  /**
   * Find the "youngest" ancestor of a node which is already in the breadcrumbs.
   * @param {NodeFront} node.
   * @return {Number} Index of the ancestor in the cache, or -1 if not found.
   */
  getCommonAncestor: function (node) {
    while (node) {
      let idx = this.indexOf(node);
      if (idx > -1) {
        return idx;
      }
      node = node.parentNode();
    }
    return -1;
  },

  /**
   * Ensure the selected node is visible.
   */
  scroll: function () {
    // FIXME bug 684352: make sure its immediate neighbors are visible too.
    if (!this.isDestroyed) {
      let element = this.nodeHierarchy[this.currentIndex].button;
      this.arrowScrollBox.scrollToElement(element, "end");
    }
  },

  /**
   * Update all button outputs.
   */
  updateSelectors: function () {
    if (this.isDestroyed) {
      return;
    }

    for (let i = this.nodeHierarchy.length - 1; i >= 0; i--) {
      let {node, button, currentPrettyPrintText} = this.nodeHierarchy[i];

      // If the output of the node doesn't change, skip the update.
      let textOutput = this.prettyPrintNodeAsText(node);
      if (currentPrettyPrintText === textOutput) {
        continue;
      }

      // Otherwise, update the whole markup for the button.
      while (button.hasChildNodes()) {
        button.firstChild.remove();
      }
      button.appendChild(this.prettyPrintNodeAsXHTML(node));
      button.setAttribute("title", textOutput);

      this.nodeHierarchy[i].currentPrettyPrintText = textOutput;
    }
  },

  /**
   * Given a list of mutation changes (passed by the markupmutation event),
   * decide whether or not they are "interesting" to the current state of the
   * breadcrumbs widget, i.e. at least one of them should cause part of the
   * widget to be updated.
   * @param {Array} mutations The mutations array.
   * @return {Boolean}
   */
  _hasInterestingMutations: function (mutations) {
    if (!mutations || !mutations.length) {
      return false;
    }

    for (let {type, added, removed, target, attributeName} of mutations) {
      if (type === "childList") {
        // Only interested in childList mutations if the added or removed
        // nodes are currently displayed.
        return added.some(node => this.indexOf(node) > -1) ||
               removed.some(node => this.indexOf(node) > -1);
      } else if (type === "attributes" && this.indexOf(target) > -1) {
        // Only interested in attributes mutations if the target is
        // currently displayed, and the attribute is either id or class.
        return attributeName === "class" || attributeName === "id";
      }
    }

    // Catch all return in case the mutations array was empty, or in case none
    // of the changes iterated above were interesting.
    return false;
  },

  /**
   * Update the breadcrumbs display when a new node is selected.
   * @param {String} reason The reason for the update, if any.
   * @param {Array} mutations An array of mutations in case this was called as
   * the "markupmutation" event listener.
   */
  update: function (reason, mutations) {
    if (this.isDestroyed) {
      return;
    }

    let hasInterestingMutations = this._hasInterestingMutations(mutations);
    if (reason === "markupmutation" && !hasInterestingMutations) {
      return;
    }

    if (!this.selection.isConnected()) {
      // remove all the crumbs
      this.cutAfter(-1);
      return;
    }

    // If this was an interesting deletion; then trim the breadcrumb trail
    let trimmed = false;
    if (reason === "markupmutation") {
      for (let {type, removed} of mutations) {
        if (type !== "childList") {
          continue;
        }

        for (let node of removed) {
          let removedIndex = this.indexOf(node);
          if (removedIndex > -1) {
            this.cutAfter(removedIndex - 1);
            trimmed = true;
          }
        }
      }
    }

    if (!this.selection.isElementNode()) {
      // no selection
      this.setCursor(-1);
      if (trimmed) {
        // Since something changed, notify the interested parties.
        this.inspector.emit("breadcrumbs-updated", this.selection.nodeFront);
      }
      return;
    }

    let idx = this.indexOf(this.selection.nodeFront);

    // Is the node already displayed in the breadcrumbs?
    // (and there are no mutations that need re-display of the crumbs)
    if (idx > -1 && !hasInterestingMutations) {
      // Yes. We select it.
      this.setCursor(idx);
    } else {
      // No. Is the breadcrumbs display empty?
      if (this.nodeHierarchy.length > 0) {
        // No. We drop all the element that are not direct ancestors
        // of the selection
        let parent = this.selection.nodeFront.parentNode();
        let ancestorIdx = this.getCommonAncestor(parent);
        this.cutAfter(ancestorIdx);
      }
      // we append the missing button between the end of the breadcrumbs display
      // and the current node.
      this.expand(this.selection.nodeFront);

      // we select the current node button
      idx = this.indexOf(this.selection.nodeFront);
      this.setCursor(idx);
    }

    let doneUpdating = this.inspector.updating("breadcrumbs");

    this.updateSelectors();

    // Make sure the selected node and its neighbours are visible.
    setTimeout(() => {
      try {
        this.scroll();
        this.inspector.emit("breadcrumbs-updated", this.selection.nodeFront);
        doneUpdating();
      } catch (e) {
        // Only log this as an error if we haven't been destroyed in the meantime.
        if (!this.isDestroyed) {
          console.error(e);
        }
      }
    }, 0);
  }
};
PK
!<3䥶��Jchrome/devtools/modules/devtools/client/inspector/changes/actions/index.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { createEnum } = require("devtools/client/shared/enum");

createEnum([

  // Update the entire changes state with the new list of changes.
  "UPDATE_CHANGES",

], module.exports);
PK
!<+2.AADchrome/devtools/modules/devtools/client/inspector/changes/changes.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/vendor/react");
const { Provider } = require("devtools/client/shared/vendor/react-redux");

const ChangesApp = createFactory(require("./components/ChangesApp"));

const { LocalizationHelper } = require("devtools/shared/l10n");
const INSPECTOR_L10N =
  new LocalizationHelper("devtools/client/locales/inspector.properties");

class ChangesView {
  constructor(inspector, window) {
    this.document = window.document;
    this.inspector = inspector;
    this.store = inspector.store;

    this.init();
  }

  init() {
    if (!this.inspector) {
      return;
    }

    let changesApp = ChangesApp({});

    let provider = createElement(Provider, {
      id: "changesview",
      key: "changesview",
      store: this.store,
      title: INSPECTOR_L10N.getStr("inspector.sidebar.changesViewTitle")
    }, changesApp);

    // Expose the provider to let inspector.js use it in setupSidebar.
    this.provider = provider;
  }

  destroy() {
    this.document = null;
    this.inspector = null;
    this.store = null;
  }
}

module.exports = ChangesView;
PK
!<�]S��Rchrome/devtools/modules/devtools/client/inspector/changes/components/ChangesApp.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/vendor/react");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const { connect } = require("devtools/client/shared/vendor/react-redux");

class ChangesApp extends PureComponent {
  static get propTypes() {
    return {};
  }

  render() {
    return dom.div(
      {
        id: "changes-container",
      }
    );
  }
}

module.exports = connect(state => state)(ChangesApp);
PK
!<��'���Mchrome/devtools/modules/devtools/client/inspector/changes/reducers/changes.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 INITIAL_CHANGES = [];

let reducers = {

};

module.exports = function (changes = INITIAL_CHANGES, action) {
  let reducer = reducers[action.type];
  if (!reducer) {
    return changes;
  }
  return reducer(changes, action);
};
PK
!<D+tKchrome/devtools/modules/devtools/client/inspector/changes/reducers/index.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

exports.changes = require("./changes");
PK
!<��4=��Rchrome/devtools/modules/devtools/client/inspector/components/InspectorTabPanel.css/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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-inspector-tab-frame {
  border: none;
  height: 100%;
  width: 100%;
}

.devtools-inspector-tab-panel {
  width: 100%;
  height: 100%;
}
PK
!<�S�Qchrome/devtools/modules/devtools/client/inspector/components/InspectorTabPanel.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 { 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");

// Shortcuts
const { div } = dom;

/**
 * Helper panel component that is using an existing DOM node
 * as the content. It's used by Sidebar as well as SplitBox
 * components.
 */
class InspectorTabPanel extends Component {
  static get propTypes() {
    return {
      // ID of the node that should be rendered as the content.
      id: PropTypes.string.isRequired,
      // Optional prefix for panel IDs.
      idPrefix: PropTypes.string,
      // Optional mount callback.
      onMount: PropTypes.func,
      // Optional unmount callback.
      onUnmount: PropTypes.func,
    };
  }

  static get defaultProps() {
    return {
      idPrefix: "",
    };
  }

  componentDidMount() {
    let doc = this.refs.content.ownerDocument;
    let panel = doc.getElementById(this.props.idPrefix + this.props.id);

    // Append existing DOM node into panel's content.
    this.refs.content.appendChild(panel);

    if (this.props.onMount) {
      this.props.onMount(this.refs.content, this.props);
    }
  }

  componentWillUnmount() {
    let doc = this.refs.content.ownerDocument;
    let panels = doc.getElementById("tabpanels");

    if (this.props.onUnmount) {
      this.props.onUnmount(this.refs.content, this.props);
    }

    // Move panel's content node back into list of tab panels.
    panels.appendChild(this.refs.content.firstChild);
  }

  render() {
    return (
      div({
        ref: "content",
        className: "devtools-inspector-tab-panel",
      })
    );
  }
}

module.exports = InspectorTabPanel;
PK
!<o9׻׻Fchrome/devtools/modules/devtools/client/inspector/computed/computed.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=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 ToolDefinitions = require("devtools/client/definitions").Tools;
const CssLogic = require("devtools/shared/inspector/css-logic");
const {ELEMENT_STYLE} = require("devtools/shared/specs/styles");
const promise = require("promise");
const OutputParser = require("devtools/client/shared/output-parser");
const {PrefObserver} = require("devtools/client/shared/prefs");
const {createChild} = require("devtools/client/inspector/shared/utils");
const {gDevTools} = require("devtools/client/framework/devtools");
const {getCssProperties} = require("devtools/shared/fronts/css-properties");
const {
  VIEW_NODE_SELECTOR_TYPE,
  VIEW_NODE_PROPERTY_TYPE,
  VIEW_NODE_VALUE_TYPE,
  VIEW_NODE_IMAGE_URL_TYPE,
  VIEW_NODE_FONT_TYPE,
} = require("devtools/client/inspector/shared/node-types");
const StyleInspectorMenu = require("devtools/client/inspector/shared/style-inspector-menu");
const TooltipsOverlay = require("devtools/client/inspector/shared/tooltips-overlay");
const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
const clipboardHelper = require("devtools/shared/platform/clipboard");

const { createElement, createFactory } = require("devtools/client/shared/vendor/react");
const ReactDOM = require("devtools/client/shared/vendor/react-dom");
const { Provider } = require("devtools/client/shared/vendor/react-redux");

const BoxModelApp = createFactory(require("devtools/client/inspector/boxmodel/components/BoxModelApp"));

const STYLE_INSPECTOR_PROPERTIES = "devtools/shared/locales/styleinspector.properties";
const {LocalizationHelper} = require("devtools/shared/l10n");
const STYLE_INSPECTOR_L10N = new LocalizationHelper(STYLE_INSPECTOR_PROPERTIES);

const FILTER_CHANGED_TIMEOUT = 150;
const HTML_NS = "http://www.w3.org/1999/xhtml";

/**
 * Helper for long-running processes that should yield occasionally to
 * the mainloop.
 *
 * @param {Window} win
 *        Timeouts will be set on this window when appropriate.
 * @param {Array} array
 *        The array of items to process.
 * @param {Object} options
 *        Options for the update process:
 *          onItem {function} Will be called with the value of each iteration.
 *          onBatch {function} Will be called after each batch of iterations,
 *            before yielding to the main loop.
 *          onDone {function} Will be called when iteration is complete.
 *          onCancel {function} Will be called if the process is canceled.
 *          threshold {int} How long to process before yielding, in ms.
 */
function UpdateProcess(win, array, options) {
  this.win = win;
  this.index = 0;
  this.array = array;

  this.onItem = options.onItem || function () {};
  this.onBatch = options.onBatch || function () {};
  this.onDone = options.onDone || function () {};
  this.onCancel = options.onCancel || function () {};
  this.threshold = options.threshold || 45;

  this.canceled = false;
}

UpdateProcess.prototype = {
  /**
   * Error thrown when the array of items to process is empty.
   */
  ERROR_ITERATION_DONE: new Error("UpdateProcess iteration done"),

  /**
   * Schedule a new batch on the main loop.
   */
  schedule: function () {
    if (this.canceled) {
      return;
    }
    this._timeout = setTimeout(this._timeoutHandler.bind(this), 0);
  },

  /**
   * Cancel the running process.  onItem will not be called again,
   * and onCancel will be called.
   */
  cancel: function () {
    if (this._timeout) {
      clearTimeout(this._timeout);
      this._timeout = 0;
    }
    this.canceled = true;
    this.onCancel();
  },

  _timeoutHandler: function () {
    this._timeout = null;
    try {
      this._runBatch();
      this.schedule();
    } catch (e) {
      if (e === this.ERROR_ITERATION_DONE) {
        this.onBatch();
        this.onDone();
        return;
      }
      console.error(e);
      throw e;
    }
  },

  _runBatch: function () {
    let time = Date.now();
    while (!this.canceled) {
      let next = this._next();
      this.onItem(next);
      if ((Date.now() - time) > this.threshold) {
        this.onBatch();
        return;
      }
    }
  },

  /**
   * Returns the item at the current index and increases the index.
   * If all items have already been processed, will throw ERROR_ITERATION_DONE.
   */
  _next: function () {
    if (this.index < this.array.length) {
      return this.array[this.index++];
    }
    throw this.ERROR_ITERATION_DONE;
  },
};

/**
 * CssComputedView is a panel that manages the display of a table
 * sorted by style. There should be one instance of CssComputedView
 * per style display (of which there will generally only be one).
 *
 * @param {Inspector} inspector
 *        Inspector toolbox panel
 * @param {Document} document
 *        The document that will contain the computed view.
 * @param {PageStyleFront} pageStyle
 *        Front for the page style actor that will be providing
 *        the style information.
 */
function CssComputedView(inspector, document, pageStyle) {
  this.inspector = inspector;
  this.highlighters = inspector.highlighters;
  this.store = inspector.store;
  this.styleDocument = document;
  this.styleWindow = this.styleDocument.defaultView;
  this.pageStyle = pageStyle;

  this.propertyViews = [];

  let cssProperties = getCssProperties(inspector.toolbox);
  this._outputParser = new OutputParser(document, cssProperties);

  // Create bound methods.
  this.focusWindow = this.focusWindow.bind(this);
  this._onContextMenu = this._onContextMenu.bind(this);
  this._onClick = this._onClick.bind(this);
  this._onCopy = this._onCopy.bind(this);
  this._onFilterStyles = this._onFilterStyles.bind(this);
  this._onClearSearch = this._onClearSearch.bind(this);
  this._onIncludeBrowserStyles = this._onIncludeBrowserStyles.bind(this);

  let doc = this.styleDocument;
  this.element = doc.getElementById("computed-property-container");
  this.boxModelWrapper = doc.getElementById("boxmodel-wrapper");
  this.searchField = doc.getElementById("computed-searchbox");
  this.searchClearButton = doc.getElementById("computed-searchinput-clear");
  this.includeBrowserStylesCheckbox = doc.getElementById("browser-style-checkbox");

  this.shortcuts = new KeyShortcuts({ window: this.styleWindow });
  this._onShortcut = this._onShortcut.bind(this);
  this.shortcuts.on("CmdOrCtrl+F", event => this._onShortcut("CmdOrCtrl+F", event));
  this.shortcuts.on("Escape", event => this._onShortcut("Escape", event));
  this.styleDocument.addEventListener("copy", this._onCopy);
  this.styleDocument.addEventListener("mousedown", this.focusWindow);
  this.element.addEventListener("click", this._onClick);
  this.element.addEventListener("contextmenu", this._onContextMenu);
  this.searchField.addEventListener("input", this._onFilterStyles);
  this.searchClearButton.addEventListener("click", this._onClearSearch);
  this.includeBrowserStylesCheckbox.addEventListener("input",
    this._onIncludeBrowserStyles);

  this.searchClearButton.hidden = true;

  // No results text.
  this.noResults = this.styleDocument.getElementById("computed-no-results");

  // Refresh panel when color unit changed or pref for showing
  // original sources changes.
  this._handlePrefChange = this._handlePrefChange.bind(this);
  this._prefObserver = new PrefObserver("devtools.");
  this._prefObserver.on("devtools.defaultColorUnit", this._handlePrefChange);

  // The element that we're inspecting, and the document that it comes from.
  this._viewedElement = null;

  this.createBoxModelView();
  this.createStyleViews();

  this._contextmenu = new StyleInspectorMenu(this, { isRuleView: false });

  // Add the tooltips and highlightersoverlay
  this.tooltips = new TooltipsOverlay(this);

  this.highlighters.addToView(this);
}

/**
 * Lookup a l10n string in the shared styleinspector string bundle.
 *
 * @param {String} name
 *        The key to lookup.
 * @returns {String} localized version of the given key.
 */
CssComputedView.l10n = function (name) {
  try {
    return STYLE_INSPECTOR_L10N.getStr(name);
  } catch (ex) {
    console.log("Error reading '" + name + "'");
    throw new Error("l10n error with " + name);
  }
};

CssComputedView.prototype = {
  // Cache the list of properties that match the selected element.
  _matchedProperties: null,

  // Used for cancelling timeouts in the style filter.
  _filterChangedTimeout: null,

  // Holds the ID of the panelRefresh timeout.
  _panelRefreshTimeout: null,

  // Toggle for zebra striping
  _darkStripe: true,

  // Number of visible properties
  numVisibleProperties: 0,

  setPageStyle: function (pageStyle) {
    this.pageStyle = pageStyle;
  },

  get includeBrowserStyles() {
    return this.includeBrowserStylesCheckbox.checked;
  },

  _handlePrefChange: function (event, data) {
    if (this._computed) {
      this.refreshPanel();
    }
  },

  /**
   * Update the view with a new selected element. The CssComputedView panel
   * will show the style information for the given element.
   *
   * @param {NodeFront} element
   *        The highlighted node to get styles for.
   * @returns a promise that will be resolved when highlighting is complete.
   */
  selectElement: function (element) {
    if (!element) {
      this._viewedElement = null;
      this.noResults.hidden = false;

      if (this._refreshProcess) {
        this._refreshProcess.cancel();
      }
      // Hiding all properties
      for (let propView of this.propertyViews) {
        propView.refresh();
      }
      return promise.resolve(undefined);
    }

    if (element === this._viewedElement) {
      return promise.resolve(undefined);
    }

    this._viewedElement = element;
    this.refreshSourceFilter();

    return this.refreshPanel();
  },

  /**
   * Get the type of a given node in the computed-view
   *
   * @param {DOMNode} node
   *        The node which we want information about
   * @return {Object} The type information object contains the following props:
   * - view {String} Always "computed" to indicate the computed view.
   * - type {String} One of the VIEW_NODE_XXX_TYPE const in
   *   client/inspector/shared/node-types
   * - value {Object} Depends on the type of the node
   * returns null if the node isn't anything we care about
   */
  getNodeInfo: function (node) {
    if (!node) {
      return null;
    }

    let classes = node.classList;

    // Check if the node isn't a selector first since this doesn't require
    // walking the DOM
    if (classes.contains("matched") ||
        classes.contains("bestmatch") ||
        classes.contains("parentmatch")) {
      let selectorText = "";

      for (let child of node.childNodes[0].childNodes) {
        if (child.nodeType === node.TEXT_NODE) {
          selectorText += child.textContent;
        }
      }
      return {
        type: VIEW_NODE_SELECTOR_TYPE,
        value: selectorText.trim()
      };
    }

    // Walk up the nodes to find out where node is
    let propertyView;
    let propertyContent;
    let parent = node;
    while (parent.parentNode) {
      if (parent.classList.contains("computed-property-view")) {
        propertyView = parent;
        break;
      }
      if (parent.classList.contains("computed-property-content")) {
        propertyContent = parent;
        break;
      }
      parent = parent.parentNode;
    }
    if (!propertyView && !propertyContent) {
      return null;
    }

    let value, type;

    // Get the property and value for a node that's a property name or value
    let isHref = classes.contains("theme-link") && !classes.contains("computed-link");
    if (propertyView && (classes.contains("computed-property-name") ||
                         classes.contains("computed-property-value") ||
                         isHref)) {
      value = {
        property: parent.querySelector(".computed-property-name").firstChild.textContent,
        value: parent.querySelector(".computed-property-value").textContent
      };
    }
    if (propertyContent && (classes.contains("computed-other-property-value") ||
                            isHref)) {
      let view = propertyContent.previousSibling;
      value = {
        property: view.querySelector(".computed-property-name").firstChild.textContent,
        value: node.textContent
      };
    }
    if (classes.contains("computed-font-family")) {
      if (propertyView) {
        value = {
          property: parent.querySelector(
            ".computed-property-name").firstChild.textContent,
          value: node.parentNode.textContent
        };
      } else if (propertyContent) {
        let view = propertyContent.previousSibling;
        value = {
          property: view.querySelector(".computed-property-name").firstChild.textContent,
          value: node.parentNode.textContent
        };
      } else {
        return null;
      }
    }

    // Get the type
    if (classes.contains("computed-property-name")) {
      type = VIEW_NODE_PROPERTY_TYPE;
    } else if (classes.contains("computed-property-value") ||
               classes.contains("computed-other-property-value")) {
      type = VIEW_NODE_VALUE_TYPE;
    } else if (classes.contains("computed-font-family")) {
      type = VIEW_NODE_FONT_TYPE;
    } else if (isHref) {
      type = VIEW_NODE_IMAGE_URL_TYPE;
      value.url = node.href;
    } else {
      return null;
    }

    return {
      view: "computed",
      type,
      value,
    };
  },

  _createPropertyViews: function () {
    if (this._createViewsPromise) {
      return this._createViewsPromise;
    }

    this.refreshSourceFilter();
    this.numVisibleProperties = 0;
    let fragment = this.styleDocument.createDocumentFragment();

    this._createViewsPromise = new Promise((resolve, reject) => {
      this._createViewsProcess = new UpdateProcess(
        this.styleWindow, CssComputedView.propertyNames, {
          onItem: (propertyName) => {
            // Per-item callback.
            let propView = new PropertyView(this, propertyName);
            fragment.appendChild(propView.buildMain());
            fragment.appendChild(propView.buildSelectorContainer());

            if (propView.visible) {
              this.numVisibleProperties++;
            }
            this.propertyViews.push(propView);
          },
          onCancel: () => {
            reject("_createPropertyViews cancelled");
          },
          onDone: () => {
            // Completed callback.
            this.element.appendChild(fragment);
            this.noResults.hidden = this.numVisibleProperties > 0;
            resolve(undefined);
          }
        }
      );
    });

    this._createViewsProcess.schedule();

    return this._createViewsPromise;
  },

  /**
   * Refresh the panel content.
   */
  refreshPanel: function () {
    if (!this._viewedElement) {
      return promise.resolve();
    }

    // Capture the current viewed element to return from the promise handler
    // early if it changed
    let viewedElement = this._viewedElement;

    return promise.all([
      this._createPropertyViews(),
      this.pageStyle.getComputed(this._viewedElement, {
        filter: this._sourceFilter,
        onlyMatched: !this.includeBrowserStyles,
        markMatched: true
      })
    ]).then(([, computed]) => {
      if (viewedElement !== this._viewedElement) {
        return promise.resolve();
      }

      this._matchedProperties = new Set();
      for (let name in computed) {
        if (computed[name].matched) {
          this._matchedProperties.add(name);
        }
      }
      this._computed = computed;

      if (this._refreshProcess) {
        this._refreshProcess.cancel();
      }

      this.noResults.hidden = true;

      // Reset visible property count
      this.numVisibleProperties = 0;

      // Reset zebra striping.
      this._darkStripe = true;

      return new Promise((resolve, reject) => {
        this._refreshProcess = new UpdateProcess(
          this.styleWindow, this.propertyViews, {
            onItem: (propView) => {
              propView.refresh();
            },
            onCancel: () => {
              reject("_refreshProcess of computed view cancelled");
            },
            onDone: () => {
              this._refreshProcess = null;
              this.noResults.hidden = this.numVisibleProperties > 0;

              if (this.searchField.value.length > 0 &&
                  !this.numVisibleProperties) {
                this.searchField.classList
                                .add("devtools-style-searchbox-no-match");
              } else {
                this.searchField.classList
                                .remove("devtools-style-searchbox-no-match");
              }

              this.inspector.emit("computed-view-refreshed");
              resolve(undefined);
            }
          }
        );
        this._refreshProcess.schedule();
      });
    }).catch(console.error);
  },

  /**
   * Handle the shortcut events in the computed view.
   */
  _onShortcut: function (name, event) {
    if (!event.target.closest("#sidebar-panel-computedview")) {
      return;
    }
    // Handle the search box's keypress event. If the escape key is pressed,
    // clear the search box field.
    if (name === "Escape" && event.target === this.searchField &&
        this._onClearSearch()) {
      event.preventDefault();
      event.stopPropagation();
    } else if (name === "CmdOrCtrl+F") {
      this.searchField.focus();
      event.preventDefault();
    }
  },

  /**
   * Set the filter style search value.
   * @param {String} value
   *        The search value.
   */
  setFilterStyles: function (value = "") {
    this.searchField.value = value;
    this.searchField.focus();
    this._onFilterStyles();
  },

  /**
   * Called when the user enters a search term in the filter style search box.
   */
  _onFilterStyles: function () {
    if (this._filterChangedTimeout) {
      clearTimeout(this._filterChangedTimeout);
    }

    let filterTimeout = (this.searchField.value.length > 0)
      ? FILTER_CHANGED_TIMEOUT : 0;
    this.searchClearButton.hidden = this.searchField.value.length === 0;

    this._filterChangedTimeout = setTimeout(() => {
      if (this.searchField.value.length > 0) {
        this.searchField.setAttribute("filled", true);
        this.boxModelWrapper.hidden = true;
      } else {
        this.searchField.removeAttribute("filled");
        this.boxModelWrapper.hidden = false;
      }

      this.refreshPanel();
      this._filterChangeTimeout = null;
    }, filterTimeout);
  },

  /**
   * Called when the user clicks on the clear button in the filter style search
   * box. Returns true if the search box is cleared and false otherwise.
   */
  _onClearSearch: function () {
    if (this.searchField.value) {
      this.setFilterStyles("");
      return true;
    }

    return false;
  },

  /**
   * The change event handler for the includeBrowserStyles checkbox.
   */
  _onIncludeBrowserStyles: function () {
    this.refreshSourceFilter();
    this.refreshPanel();
  },

  /**
   * When includeBrowserStylesCheckbox.checked is false we only display
   * properties that have matched selectors and have been included by the
   * document or one of thedocument's stylesheets. If .checked is false we
   * display all properties including those that come from UA stylesheets.
   */
  refreshSourceFilter: function () {
    this._matchedProperties = null;
    this._sourceFilter = this.includeBrowserStyles ?
                                 CssLogic.FILTER.UA :
                                 CssLogic.FILTER.USER;
  },

  /**
   * Render the box model view.
   */
  createBoxModelView: function () {
    let {
      setSelectedNode,
      onShowBoxModelHighlighterForNode,
    } = this.inspector.getCommonComponentProps();

    let {
      onHideBoxModelHighlighter,
      onShowBoxModelEditor,
      onShowBoxModelHighlighter,
      onToggleGeometryEditor,
    } = this.inspector.getPanel("boxmodel").getComponentProps();

    let provider = createElement(
      Provider,
      { store: this.store },
      BoxModelApp({
        setSelectedNode,
        showBoxModelProperties: false,
        onHideBoxModelHighlighter,
        onShowBoxModelEditor,
        onShowBoxModelHighlighter,
        onShowBoxModelHighlighterForNode,
        onToggleGeometryEditor,
      })
    );
    ReactDOM.render(provider, this.boxModelWrapper);
  },

  /**
   * The CSS as displayed by the UI.
   */
  createStyleViews: function () {
    if (CssComputedView.propertyNames) {
      return;
    }

    CssComputedView.propertyNames = [];

    // Here we build and cache a list of css properties supported by the browser
    // We could use any element but let's use the main document's root element
    let styles = this.styleWindow
      .getComputedStyle(this.styleDocument.documentElement);
    let mozProps = [];
    for (let i = 0, numStyles = styles.length; i < numStyles; i++) {
      let prop = styles.item(i);
      if (prop.startsWith("--")) {
        // Skip any CSS variables used inside of browser CSS files
        continue;
      } else if (prop.startsWith("-")) {
        mozProps.push(prop);
      } else {
        CssComputedView.propertyNames.push(prop);
      }
    }

    CssComputedView.propertyNames.sort();
    CssComputedView.propertyNames.push.apply(CssComputedView.propertyNames,
      mozProps.sort());

    this._createPropertyViews().catch(e => {
      if (!this._isDestroyed) {
        console.warn("The creation of property views was cancelled because " +
          "the computed-view was destroyed before it was done creating views");
      } else {
        console.error(e);
      }
    });
  },

  /**
   * Get a set of properties that have matched selectors.
   *
   * @return {Set} If a property name is in the set, it has matching selectors.
   */
  get matchedProperties() {
    return this._matchedProperties || new Set();
  },

  /**
   * Focus the window on mousedown.
   */
  focusWindow: function () {
    this.styleWindow.focus();
  },

  /**
   * Context menu handler.
   */
  _onContextMenu: function (event) {
    this._contextmenu.show(event);
  },

  _onClick: function (event) {
    let target = event.target;

    if (target.nodeName === "a") {
      event.stopPropagation();
      event.preventDefault();
      let browserWin = this.inspector.target.tab.ownerDocument.defaultView;
      browserWin.openUILinkIn(target.href, "tab");
    }
  },

  /**
   * Callback for copy event. Copy selected text.
   *
   * @param {Event} event
   *        copy event object.
   */
  _onCopy: function (event) {
    let win = this.styleWindow;
    let text = win.getSelection().toString().trim();
    if (text !== "") {
      this.copySelection();
      event.preventDefault();
    }
  },

  /**
   * Copy the current selection to the clipboard
   */
  copySelection: function () {
    try {
      let win = this.styleWindow;
      let text = win.getSelection().toString().trim();

      clipboardHelper.copyString(text);
    } catch (e) {
      console.error(e);
    }
  },

  /**
   * Destructor for CssComputedView.
   */
  destroy: function () {
    this._viewedElement = null;
    this._outputParser = null;

    this._prefObserver.off("devtools.defaultColorUnit", this._handlePrefChange);
    this._prefObserver.destroy();

    // Cancel tree construction
    if (this._createViewsProcess) {
      this._createViewsProcess.cancel();
    }
    if (this._refreshProcess) {
      this._refreshProcess.cancel();
    }

    // Remove context menu
    if (this._contextmenu) {
      this._contextmenu.destroy();
      this._contextmenu = null;
    }

    this.tooltips.destroy();
    this.highlighters.removeFromView(this);

    // Remove bound listeners
    this.styleDocument.removeEventListener("mousedown", this.focusWindow);
    this.element.removeEventListener("click", this._onClick);
    this.styleDocument.removeEventListener("copy", this._onCopy);
    this.element.removeEventListener("contextmenu", this._onContextMenu);
    this.searchField.removeEventListener("input", this._onFilterStyles);
    this.searchClearButton.removeEventListener("click", this._onClearSearch);
    this.includeBrowserStylesCheckbox.removeEventListener("input",
      this._onIncludeBrowserStyles);

    // Nodes used in templating
    this.element = null;
    this.boxModelWrapper = null;
    this.searchField = null;
    this.searchClearButton = null;
    this.includeBrowserStylesCheckbox = null;

    // Property views
    for (let propView of this.propertyViews) {
      propView.destroy();
    }
    this.propertyViews = null;

    this.inspector = null;
    this.highlighters = null;
    this.store = null;
    this.styleDocument = null;
    this.styleWindow = null;

    this._isDestroyed = true;
  }
};

function PropertyInfo(tree, name) {
  this.tree = tree;
  this.name = name;
}

PropertyInfo.prototype = {
  get value() {
    if (this.tree._computed) {
      let value = this.tree._computed[this.name].value;
      return value;
    }
    return null;
  }
};

/**
 * A container to give easy access to property data from the template engine.
 *
 * @param {CssComputedView} tree
 *        The CssComputedView instance we are working with.
 * @param {String} name
 *        The CSS property name for which this PropertyView
 *        instance will render the rules.
 */
function PropertyView(tree, name) {
  this.tree = tree;
  this.name = name;

  this.link = "https://developer.mozilla.org/CSS/" + name;

  this._propertyInfo = new PropertyInfo(tree, name);
}

PropertyView.prototype = {
  // The parent element which contains the open attribute
  element: null,

  // Property header node
  propertyHeader: null,

  // Destination for property names
  nameNode: null,

  // Destination for property values
  valueNode: null,

  // Are matched rules expanded?
  matchedExpanded: false,

  // Matched selector container
  matchedSelectorsContainer: null,

  // Matched selector expando
  matchedExpander: null,

  // Cache for matched selector views
  _matchedSelectorViews: null,

  // The previously selected element used for the selector view caches
  _prevViewedElement: null,

  /**
   * Get the computed style for the current property.
   *
   * @return {String} the computed style for the current property of the
   * currently highlighted element.
   */
  get value() {
    return this.propertyInfo.value;
  },

  /**
   * An easy way to access the CssPropertyInfo behind this PropertyView.
   */
  get propertyInfo() {
    return this._propertyInfo;
  },

  /**
   * Does the property have any matched selectors?
   */
  get hasMatchedSelectors() {
    return this.tree.matchedProperties.has(this.name);
  },

  /**
   * Should this property be visible?
   */
  get visible() {
    if (!this.tree._viewedElement) {
      return false;
    }

    if (!this.tree.includeBrowserStyles && !this.hasMatchedSelectors) {
      return false;
    }

    let searchTerm = this.tree.searchField.value.toLowerCase();
    let isValidSearchTerm = searchTerm.trim().length > 0;
    if (isValidSearchTerm &&
        !this.name.toLowerCase().includes(searchTerm) &&
        !this.value.toLowerCase().includes(searchTerm)) {
      return false;
    }

    return true;
  },

  /**
   * Returns the className that should be assigned to the propertyView.
   *
   * @return {String}
   */
  get propertyHeaderClassName() {
    if (this.visible) {
      let isDark = this.tree._darkStripe = !this.tree._darkStripe;
      return isDark ? "computed-property-view row-striped" : "computed-property-view";
    }
    return "computed-property-hidden";
  },

  /**
   * Returns the className that should be assigned to the propertyView content
   * container.
   *
   * @return {String}
   */
  get propertyContentClassName() {
    if (this.visible) {
      let isDark = this.tree._darkStripe;
      return isDark
        ? "computed-property-content row-striped"
        : "computed-property-content";
    }
    return "computed-property-hidden";
  },

  /**
   * Build the markup for on computed style
   *
   * @return {Element}
   */
  buildMain: function () {
    let doc = this.tree.styleDocument;

    // Build the container element
    this.onMatchedToggle = this.onMatchedToggle.bind(this);
    this.element = doc.createElementNS(HTML_NS, "div");
    this.element.setAttribute("class", this.propertyHeaderClassName);
    this.element.addEventListener("dblclick", this.onMatchedToggle);

    // Make it keyboard navigable
    this.element.setAttribute("tabindex", "0");
    this.shortcuts = new KeyShortcuts({
      window: this.tree.styleWindow,
      target: this.element
    });
    this.shortcuts.on("F1", event => {
      this.mdnLinkClick(event);
      // Prevent opening the options panel
      event.preventDefault();
      event.stopPropagation();
    });
    this.shortcuts.on("Return", this.onMatchedToggle);
    this.shortcuts.on("Space", this.onMatchedToggle);

    let nameContainer = doc.createElementNS(HTML_NS, "span");
    nameContainer.className = "computed-property-name-container";
    this.element.appendChild(nameContainer);

    // Build the twisty expand/collapse
    this.matchedExpander = doc.createElementNS(HTML_NS, "div");
    this.matchedExpander.className = "computed-expander theme-twisty";
    this.matchedExpander.addEventListener("click", this.onMatchedToggle);
    nameContainer.appendChild(this.matchedExpander);

    // Build the style name element
    this.nameNode = doc.createElementNS(HTML_NS, "span");
    this.nameNode.classList.add("computed-property-name", "theme-fg-color5");
    // Reset its tabindex attribute otherwise, if an ellipsis is applied
    // it will be reachable via TABing
    this.nameNode.setAttribute("tabindex", "");
    // Avoid english text (css properties) from being altered
    // by RTL mode
    this.nameNode.setAttribute("dir", "ltr");
    this.nameNode.textContent = this.nameNode.title = this.name;
    // Make it hand over the focus to the container
    this.onFocus = () => this.element.focus();
    this.nameNode.addEventListener("click", this.onFocus);

    // Build the style name ":" separator
    let nameSeparator = doc.createElementNS(HTML_NS, "span");
    nameSeparator.classList.add("visually-hidden");
    nameSeparator.textContent = ": ";
    this.nameNode.appendChild(nameSeparator);

    nameContainer.appendChild(this.nameNode);

    let valueContainer = doc.createElementNS(HTML_NS, "span");
    valueContainer.className = "computed-property-value-container";
    this.element.appendChild(valueContainer);

    // Build the style value element
    this.valueNode = doc.createElementNS(HTML_NS, "span");
    this.valueNode.classList.add("computed-property-value", "theme-fg-color1");
    // Reset its tabindex attribute otherwise, if an ellipsis is applied
    // it will be reachable via TABing
    this.valueNode.setAttribute("tabindex", "");
    this.valueNode.setAttribute("dir", "ltr");
    // Make it hand over the focus to the container
    this.valueNode.addEventListener("click", this.onFocus);

    // Build the style value ";" separator
    let valueSeparator = doc.createElementNS(HTML_NS, "span");
    valueSeparator.classList.add("visually-hidden");
    valueSeparator.textContent = ";";

    valueContainer.appendChild(this.valueNode);
    valueContainer.appendChild(valueSeparator);

    return this.element;
  },

  buildSelectorContainer: function () {
    let doc = this.tree.styleDocument;
    let element = doc.createElementNS(HTML_NS, "div");
    element.setAttribute("class", this.propertyContentClassName);
    this.matchedSelectorsContainer = doc.createElementNS(HTML_NS, "div");
    this.matchedSelectorsContainer.classList.add("matchedselectors");
    element.appendChild(this.matchedSelectorsContainer);

    return element;
  },

  /**
   * Refresh the panel's CSS property value.
   */
  refresh: function () {
    this.element.className = this.propertyHeaderClassName;
    this.element.nextElementSibling.className = this.propertyContentClassName;

    if (this._prevViewedElement !== this.tree._viewedElement) {
      this._matchedSelectorViews = null;
      this._prevViewedElement = this.tree._viewedElement;
    }

    if (!this.tree._viewedElement || !this.visible) {
      this.valueNode.textContent = this.valueNode.title = "";
      this.matchedSelectorsContainer.parentNode.hidden = true;
      this.matchedSelectorsContainer.textContent = "";
      this.matchedExpander.removeAttribute("open");
      return;
    }

    this.tree.numVisibleProperties++;

    let outputParser = this.tree._outputParser;
    let frag = outputParser.parseCssProperty(this.propertyInfo.name,
      this.propertyInfo.value,
      {
        colorSwatchClass: "computed-colorswatch",
        colorClass: "computed-color",
        urlClass: "theme-link",
        fontFamilyClass: "computed-font-family",
        // No need to use baseURI here as computed URIs are never relative.
      });
    this.valueNode.innerHTML = "";
    this.valueNode.appendChild(frag);

    this.refreshMatchedSelectors();
  },

  /**
   * Refresh the panel matched rules.
   */
  refreshMatchedSelectors: function () {
    let hasMatchedSelectors = this.hasMatchedSelectors;
    this.matchedSelectorsContainer.parentNode.hidden = !hasMatchedSelectors;

    if (hasMatchedSelectors) {
      this.matchedExpander.classList.add("computed-expandable");
    } else {
      this.matchedExpander.classList.remove("computed-expandable");
    }

    if (this.matchedExpanded && hasMatchedSelectors) {
      return this.tree.pageStyle
        .getMatchedSelectors(this.tree._viewedElement, this.name)
        .then(matched => {
          if (!this.matchedExpanded) {
            return;
          }

          this._matchedSelectorResponse = matched;

          this._buildMatchedSelectors();
          this.matchedExpander.setAttribute("open", "");
          this.tree.inspector.emit("computed-view-property-expanded");
        }).catch(console.error);
    }

    this.matchedSelectorsContainer.innerHTML = "";
    this.matchedExpander.removeAttribute("open");
    this.tree.inspector.emit("computed-view-property-collapsed");
    return promise.resolve(undefined);
  },

  get matchedSelectors() {
    return this._matchedSelectorResponse;
  },

  _buildMatchedSelectors: function () {
    let frag = this.element.ownerDocument.createDocumentFragment();

    for (let selector of this.matchedSelectorViews) {
      let p = createChild(frag, "p");
      let span = createChild(p, "span", {
        class: "rule-link"
      });
      let link = createChild(span, "a", {
        target: "_blank",
        class: "computed-link theme-link",
        title: selector.href,
        sourcelocation: selector.source,
        tabindex: "0",
        textContent: selector.source
      });
      link.addEventListener("click", selector.openStyleEditor);
      let shortcuts = new KeyShortcuts({
        window: this.tree.styleWindow,
        target: link
      });
      shortcuts.on("Return", () => selector.openStyleEditor());

      let status = createChild(p, "span", {
        dir: "ltr",
        class: "rule-text theme-fg-color3 " + selector.statusClass,
        title: selector.statusText
      });

      createChild(status, "div", {
        class: "fix-get-selection",
        textContent: selector.sourceText
      });

      let valueDiv = createChild(status, "div", {
        class: "fix-get-selection computed-other-property-value theme-fg-color1"
      });
      valueDiv.appendChild(selector.outputFragment);
    }

    this.matchedSelectorsContainer.innerHTML = "";
    this.matchedSelectorsContainer.appendChild(frag);
  },

  /**
   * Provide access to the matched SelectorViews that we are currently
   * displaying.
   */
  get matchedSelectorViews() {
    if (!this._matchedSelectorViews) {
      this._matchedSelectorViews = [];
      this._matchedSelectorResponse.forEach(selectorInfo => {
        let selectorView = new SelectorView(this.tree, selectorInfo);
        this._matchedSelectorViews.push(selectorView);
      }, this);
    }
    return this._matchedSelectorViews;
  },

  /**
   * The action when a user expands matched selectors.
   *
   * @param {Event} event
   *        Used to determine the class name of the targets click
   *        event.
   */
  onMatchedToggle: function (event) {
    if (event.shiftKey) {
      return;
    }
    this.matchedExpanded = !this.matchedExpanded;
    this.refreshMatchedSelectors();
    event.preventDefault();
  },

  /**
   * The action when a user clicks on the MDN help link for a property.
   */
  mdnLinkClick: function (event) {
    let inspector = this.tree.inspector;

    if (inspector.target.tab) {
      let browserWin = inspector.target.tab.ownerDocument.defaultView;
      browserWin.openUILinkIn(this.link, "tab");
    }
  },

  /**
   * Destroy this property view, removing event listeners
   */
  destroy: function () {
    if (this._matchedSelectorViews) {
      for (let view of this._matchedSelectorViews) {
        view.destroy();
      }
    }

    this.element.removeEventListener("dblclick", this.onMatchedToggle);
    this.shortcuts.destroy();
    this.element = null;

    this.matchedExpander.removeEventListener("click", this.onMatchedToggle);
    this.matchedExpander = null;

    this.nameNode.removeEventListener("click", this.onFocus);
    this.nameNode = null;

    this.valueNode.removeEventListener("click", this.onFocus);
    this.valueNode = null;
  }
};

/**
 * A container to give us easy access to display data from a CssRule
 *
 * @param CssComputedView tree
 *        the owning CssComputedView
 * @param selectorInfo
 */
function SelectorView(tree, selectorInfo) {
  this.tree = tree;
  this.selectorInfo = selectorInfo;
  this._cacheStatusNames();

  this.openStyleEditor = this.openStyleEditor.bind(this);
  this._updateLocation = this._updateLocation.bind(this);

  const rule = this.selectorInfo.rule;
  if (!rule || !rule.parentStyleSheet || rule.type == ELEMENT_STYLE) {
    this.source = CssLogic.l10n("rule.sourceElement");
  } else {
    // This always refers to the generated location.
    const sheet = rule.parentStyleSheet;
    this.source = CssLogic.shortSource(sheet) + ":" + rule.line;

    const url = sheet.href || sheet.nodeHref;
    this.currentLocation = {
      href: url,
      line: rule.line,
      column: rule.column,
    };
    this.generatedLocation = this.currentLocation;
    this.sourceMapURLService = this.tree.inspector.toolbox.sourceMapURLService;
    this.sourceMapURLService.subscribe(url, rule.line, rule.column, this._updateLocation);
  }
}

/**
 * Decode for cssInfo.rule.status
 * @see SelectorView.prototype._cacheStatusNames
 * @see CssLogic.STATUS
 */
SelectorView.STATUS_NAMES = [
  // "Parent Match", "Matched", "Best Match"
];

SelectorView.CLASS_NAMES = [
  "parentmatch", "matched", "bestmatch"
];

SelectorView.prototype = {
  /**
   * Cache localized status names.
   *
   * These statuses are localized inside the styleinspector.properties string
   * bundle.
   * @see css-logic.js - the CssLogic.STATUS array.
   */
  _cacheStatusNames: function () {
    if (SelectorView.STATUS_NAMES.length) {
      return;
    }

    for (let status in CssLogic.STATUS) {
      let i = CssLogic.STATUS[status];
      if (i > CssLogic.STATUS.UNMATCHED) {
        let value = CssComputedView.l10n("rule.status." + status);
        // Replace normal spaces with non-breaking spaces
        SelectorView.STATUS_NAMES[i] = value.replace(/ /g, "\u00A0");
      }
    }
  },

  /**
   * A localized version of cssRule.status
   */
  get statusText() {
    return SelectorView.STATUS_NAMES[this.selectorInfo.status];
  },

  /**
   * Get class name for selector depending on status
   */
  get statusClass() {
    return SelectorView.CLASS_NAMES[this.selectorInfo.status - 1];
  },

  get href() {
    if (this._href) {
      return this._href;
    }
    let sheet = this.selectorInfo.rule.parentStyleSheet;
    this._href = sheet ? sheet.href : "#";
    return this._href;
  },

  get sourceText() {
    return this.selectorInfo.sourceText;
  },

  get value() {
    return this.selectorInfo.value;
  },

  get outputFragment() {
    // Sadly, because this fragment is added to the template by DOM Templater
    // we lose any events that are attached. This means that URLs will open in a
    // new window. At some point we should fix this by stopping using the
    // templater.
    let outputParser = this.tree._outputParser;
    let frag = outputParser.parseCssProperty(
      this.selectorInfo.name,
      this.selectorInfo.value, {
        colorSwatchClass: "computed-colorswatch",
        colorClass: "computed-color",
        urlClass: "theme-link",
        fontFamilyClass: "computed-font-family",
        baseURI: this.selectorInfo.rule.href
      }
    );
    return frag;
  },

  /**
   * Update the text of the source link to reflect whether we're showing
   * original sources or not.  This is a callback for
   * SourceMapURLService.subscribe, which see.
   *
   * @param {Boolean} enabled
   *        True if the passed-in location should be used; this means
   *        that source mapping is in use and the remaining arguments
   *        are the original location.  False if the already-known
   *        (stored) location should be used.
   * @param {String} url
   *        The original URL
   * @param {Number} line
   *        The original line number
   * @param {number} column
   *        The original column number
   */
  _updateLocation: function (enabled, url, line, column) {
    if (!this.tree.element) {
      return;
    }

    // Update |currentLocation| to be whichever location is being
    // displayed at the moment.
    if (enabled) {
      this.currentLocation = { href: url, line, column };
    } else {
      this.currentLocation = this.generatedLocation;
    }

    let selector = '[sourcelocation="' + this.source + '"]';
    let link = this.tree.element.querySelector(selector);
    if (link) {
      let text = CssLogic.shortSource(this.currentLocation) +
          ":" + this.currentLocation.line;
      link.textContent = text;
    }

    this.tree.inspector.emit("computed-view-sourcelinks-updated");
  },

  /**
   * When a css link is clicked this method is called in order to either:
   *   1. Open the link in view source (for chrome stylesheets).
   *   2. Open the link in the style editor.
   *
   *   We can only view stylesheets contained in document.styleSheets inside the
   *   style editor.
   */
  openStyleEditor: function () {
    let inspector = this.tree.inspector;
    let rule = this.selectorInfo.rule;

    // The style editor can only display stylesheets coming from content because
    // chrome stylesheets are not listed in the editor's stylesheet selector.
    //
    // If the stylesheet is a content stylesheet we send it to the style
    // editor else we display it in the view source window.
    let parentStyleSheet = rule.parentStyleSheet;
    if (!parentStyleSheet || parentStyleSheet.isSystem) {
      let toolbox = gDevTools.getToolbox(inspector.target);
      toolbox.viewSource(rule.href, rule.line);
      return;
    }

    let {href, line, column} = this.currentLocation;
    let target = inspector.target;
    if (ToolDefinitions.styleEditor.isTargetSupported(target)) {
      gDevTools.showToolbox(target, "styleeditor").then(function (toolbox) {
        toolbox.getCurrentPanel().selectStyleSheet(href, line, column);
      });
    }
  },

  /**
   * Destroy this selector view, removing event listeners
   */
  destroy: function () {
    let rule = this.selectorInfo.rule;
    if (rule && rule.parentStyleSheet && rule.type != ELEMENT_STYLE) {
      const url = rule.parentStyleSheet.href || rule.parentStyleSheet.nodeHref;
      this.sourceMapURLService.unsubscribe(url, rule.line,
                                           rule.column, this._updateLocation);
    }
  },
};

function ComputedViewTool(inspector, window) {
  this.inspector = inspector;
  this.document = window.document;

  this.computedView = new CssComputedView(this.inspector, this.document,
    this.inspector.pageStyle);

  this.onSelected = this.onSelected.bind(this);
  this.refresh = this.refresh.bind(this);
  this.onPanelSelected = this.onPanelSelected.bind(this);
  this.onMutations = this.onMutations.bind(this);
  this.onResized = this.onResized.bind(this);

  this.inspector.selection.on("detached-front", this.onSelected);
  this.inspector.selection.on("new-node-front", this.onSelected);
  this.inspector.selection.on("pseudoclass", this.refresh);
  this.inspector.sidebar.on("computedview-selected", this.onPanelSelected);
  this.inspector.pageStyle.on("stylesheet-updated", this.refresh);
  this.inspector.walker.on("mutations", this.onMutations);
  this.inspector.walker.on("resize", this.onResized);

  this.computedView.selectElement(null);

  this.onSelected();
}

ComputedViewTool.prototype = {
  isSidebarActive: function () {
    if (!this.computedView) {
      return false;
    }
    return this.inspector.sidebar.getCurrentTabID() == "computedview";
  },

  onSelected: function (event) {
    // Ignore the event if the view has been destroyed, or if it's inactive.
    // But only if the current selection isn't null. If it's been set to null,
    // let the update go through as this is needed to empty the view on
    // navigation.
    if (!this.computedView) {
      return;
    }

    let isInactive = !this.isSidebarActive() &&
                     this.inspector.selection.nodeFront;
    if (isInactive) {
      return;
    }

    this.computedView.setPageStyle(this.inspector.pageStyle);

    if (!this.inspector.selection.isConnected() ||
        !this.inspector.selection.isElementNode()) {
      this.computedView.selectElement(null);
      return;
    }

    if (!event || event == "new-node-front") {
      let done = this.inspector.updating("computed-view");
      this.computedView.selectElement(this.inspector.selection.nodeFront).then(() => {
        done();
      });
    }
  },

  refresh: function () {
    if (this.isSidebarActive()) {
      this.computedView.refreshPanel();
    }
  },

  onPanelSelected: function () {
    if (this.inspector.selection.nodeFront === this.computedView._viewedElement) {
      this.refresh();
    } else {
      this.onSelected();
    }
  },

  /**
   * When markup mutations occur, if an attribute of the selected node changes,
   * we need to refresh the view as that might change the node's styles.
   */
  onMutations: function (mutations) {
    for (let {type, target} of mutations) {
      if (target === this.inspector.selection.nodeFront &&
          type === "attributes") {
        this.refresh();
        break;
      }
    }
  },

  /**
   * When the window gets resized, this may cause media-queries to match, and
   * therefore, different styles may apply.
   */
  onResized: function () {
    this.refresh();
  },

  destroy: function () {
    this.inspector.walker.off("mutations", this.onMutations);
    this.inspector.walker.off("resize", this.onResized);
    this.inspector.sidebar.off("computedview-selected", this.refresh);
    this.inspector.selection.off("pseudoclass", this.refresh);
    this.inspector.selection.off("new-node-front", this.onSelected);
    this.inspector.selection.off("detached-front", this.onSelected);
    this.inspector.sidebar.off("computedview-selected", this.onPanelSelected);
    if (this.inspector.pageStyle) {
      this.inspector.pageStyle.off("stylesheet-updated", this.refresh);
    }

    this.computedView.destroy();

    this.computedView = this.document = this.inspector = null;
  }
};

exports.CssComputedView = CssComputedView;
exports.ComputedViewTool = ComputedViewTool;
exports.PropertyView = PropertyView;
PK
!<��5���Ichrome/devtools/modules/devtools/client/inspector/events/actions/index.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { createEnum } = require("devtools/client/shared/enum");

createEnum([

  // Update the entire events state with the new list of events.
  "UPDATE_EVENTS",

], module.exports);
PK
!<z����Pchrome/devtools/modules/devtools/client/inspector/events/components/EventsApp.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/vendor/react");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const { connect } = require("devtools/client/shared/vendor/react-redux");

class EventsApp extends PureComponent {
  static get propTypes() {
    return {};
  }

  render() {
    return dom.div(
      {
        id: "events-container",
      }
    );
  }
}

module.exports = connect(state => state)(EventsApp);
PK
!<,[�A77Bchrome/devtools/modules/devtools/client/inspector/events/events.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/vendor/react");
const { Provider } = require("devtools/client/shared/vendor/react-redux");

const EventsApp = createFactory(require("./components/EventsApp"));

const { LocalizationHelper } = require("devtools/shared/l10n");
const INSPECTOR_L10N =
  new LocalizationHelper("devtools/client/locales/inspector.properties");

class EventsView {
  constructor(inspector, window) {
    this.document = window.document;
    this.inspector = inspector;
    this.store = inspector.store;

    this.init();
  }

  init() {
    if (!this.inspector) {
      return;
    }

    let eventsApp = EventsApp({});

    let provider = createElement(Provider, {
      id: "eventsview",
      key: "eventsview",
      store: this.store,
      title: INSPECTOR_L10N.getStr("inspector.sidebar.eventsViewTitle")
    }, eventsApp);

    // Expose the provider to let inspector.js use it in setupSidebar.
    this.provider = provider;
  }

  destroy() {
    this.document = null;
    this.inspector = null;
    this.store = null;
  }
}

module.exports = EventsView;
PK
!<0�![��Kchrome/devtools/modules/devtools/client/inspector/events/reducers/events.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 INITIAL_EVENTS = [];

let reducers = {

};

module.exports = function (events = INITIAL_EVENTS, action) {
  let reducer = reducers[action.type];
  if (!reducer) {
    return events;
  }
  return reducer(events, action);
};
PK
!<S�KJchrome/devtools/modules/devtools/client/inspector/events/reducers/index.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

exports.events = require("./events");
PK
!<�C�wwMchrome/devtools/modules/devtools/client/inspector/extensions/actions/index.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { createEnum } = require("devtools/client/shared/enum");

createEnum([

  // Update the extension sidebar with an object TreeView.
  "EXTENSION_SIDEBAR_OBJECT_TREEVIEW_UPDATE",

  // Update the extension sidebar with an object value grip preview.
  "EXTENSION_SIDEBAR_OBJECT_GRIP_VIEW_UPDATE",

  // Remove an extension sidebar from the inspector store.
  "EXTENSION_SIDEBAR_REMOVE"

], module.exports);
PK
!<m�-11Ochrome/devtools/modules/devtools/client/inspector/extensions/actions/sidebar.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  EXTENSION_SIDEBAR_OBJECT_TREEVIEW_UPDATE,
  EXTENSION_SIDEBAR_OBJECT_GRIP_VIEW_UPDATE,
  EXTENSION_SIDEBAR_REMOVE,
} = require("./index");

module.exports = {

  /**
   * Update the sidebar with an object treeview.
   */
  updateObjectTreeView(sidebarId, object) {
    return {
      type: EXTENSION_SIDEBAR_OBJECT_TREEVIEW_UPDATE,
      sidebarId,
      object,
    };
  },

  /**
   * Update the sidebar with an object actor preview.
   */
  updateObjectValueGripView(sidebarId, objectValueGrip, rootTitle) {
    return {
      type: EXTENSION_SIDEBAR_OBJECT_GRIP_VIEW_UPDATE,
      sidebarId,
      objectValueGrip,
      rootTitle,
    };
  },

  /**
   * Remove the extension sidebar from the inspector store.
   */
  removeExtensionSidebar(sidebarId) {
    return {
      type: EXTENSION_SIDEBAR_REMOVE,
      sidebarId,
    };
  }

};
PK
!<;�'<�	�	[chrome/devtools/modules/devtools/client/inspector/extensions/components/ExtensionSidebar.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const { connect } = require("devtools/client/shared/vendor/react-redux");

const ObjectTreeView = createFactory(require("./ObjectTreeView"));
const ObjectValueGripView = createFactory(require("./ObjectValueGripView"));
const Types = require("../types");

/**
 * The ExtensionSidebar is a React component with 2 supported viewMode:
 * - an ObjectTreeView UI, used to show the JS objects
 *   (used by the sidebar.setObject WebExtensions APIs)
 * - an ObjectValueGripView UI, used to show the objects value grips
 *   (used by sidebar.setExpression WebExtensions APIs)
 * - an ExtensionPage UI used to show an extension page
 *   (used by the sidebar.setPage WebExtensions APIs).
 *
 * TODO: implement the ExtensionPage viewMode.
 */
class ExtensionSidebar extends PureComponent {
  static get propTypes() {
    return {
      id: PropTypes.string.isRequired,
      extensionsSidebar: PropTypes.object.isRequired,
      // Helpers injected as props by extension-sidebar.js.
      serviceContainer: PropTypes.shape(Types.serviceContainer).isRequired,
    };
  }

  render() {
    const {
      id,
      extensionsSidebar,
      serviceContainer,
    } = this.props;

    let {
      viewMode = "empty-sidebar",
      object,
      objectValueGrip,
      rootTitle
    } = extensionsSidebar[id] || {};

    let sidebarContentEl;

    switch (viewMode) {
      case "object-treeview":
        sidebarContentEl = ObjectTreeView({ object });
        break;
      case "object-value-grip-view":
        sidebarContentEl = ObjectValueGripView({
          objectValueGrip,
          serviceContainer,
          rootTitle,
        });
        break;
      case "empty-sidebar":
        break;
      default:
        throw new Error(`Unknown ExtensionSidebar viewMode: "${viewMode}"`);
    }

    const className = "devtools-monospace extension-sidebar inspector-tabpanel";

    return dom.div({ id, className }, sidebarContentEl);
  }
}

module.exports = connect(state => state)(ExtensionSidebar);
PK
!<엽�++Ychrome/devtools/modules/devtools/client/inspector/extensions/components/ObjectTreeView.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/vendor/react");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");

const { REPS, MODE } = require("devtools/client/shared/components/reps/reps");
const { Rep } = REPS;
const TreeViewClass = require("devtools/client/shared/components/tree/TreeView");
const TreeView = createFactory(TreeViewClass);

/**
 * The ObjectTreeView React Component is used in the ExtensionSidebar component to provide
 * a UI viewMode which shows a tree view of the passed JavaScript object.
 */
class ObjectTreeView extends PureComponent {
  static get propTypes() {
    return {
      object: PropTypes.object.isRequired,
    };
  }

  render() {
    const { object } = this.props;

    let columns = [{
      "id": "value",
    }];

    // Render the node value (omitted on the root element if it has children).
    let renderValue = props => {
      if (props.member.level === 0 && props.member.hasChildren) {
        return undefined;
      }

      return Rep(Object.assign({}, props, {
        cropLimit: 50,
      }));
    };

    return TreeView({
      object,
      mode: MODE.SHORT,
      columns,
      renderValue,
      expandedNodes: TreeViewClass.getExpandedNodes(object, {
        maxLevel: 1, maxNodes: 1,
      }),
    });
  }
}

module.exports = ObjectTreeView;
PK
!<�1�
�
^chrome/devtools/modules/devtools/client/inspector/extensions/components/ObjectValueGripView.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/vendor/react");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");

const Accordion = createFactory(require("devtools/client/inspector/layout/components/Accordion"));
const reps = require("devtools/client/shared/components/reps/reps");
const Types = require("../types");

const { REPS, MODE } = reps;
const { Grip } = REPS;

const ObjectInspector = createFactory(reps.ObjectInspector);

class ObjectValueGripView extends PureComponent {
  static get propTypes() {
    return {
      rootTitle: PropTypes.string,
      objectValueGrip: PropTypes.oneOfType([
        PropTypes.string,
        PropTypes.number,
        PropTypes.object,
      ]).isRequired,
      // Helpers injected as props by extension-sidebar.js.
      serviceContainer: PropTypes.shape(Types.serviceContainer).isRequired,
    };
  }

  render() {
    const {
      objectValueGrip,
      serviceContainer,
      rootTitle,
    } = this.props;

    const objectInspectorProps = {
      autoExpandDepth: 1,
      mode: MODE.SHORT,
      // TODO: we disable focus since it's not currently working well in ObjectInspector.
      // Let's remove the property below when problem are fixed in OI.
      disabledFocus: true,
      roots: [{
        path: objectValueGrip && objectValueGrip.actor || JSON.stringify(objectValueGrip),
        contents: {
          value: objectValueGrip,
        }
      }],
      createObjectClient: serviceContainer.createObjectClient,
      releaseActor: serviceContainer.releaseActor,
      // TODO: evaluate if there should also be a serviceContainer.openLink.
    };

    if (objectValueGrip && objectValueGrip.actor) {
      Object.assign(objectInspectorProps, {
        onDOMNodeMouseOver: serviceContainer.highlightDomElement,
        onDOMNodeMouseOut: serviceContainer.unHighlightDomElement,
        onInspectIconClick(object, e) {
          // Stop the event propagation so we don't trigger ObjectInspector
          // expand/collapse.
          e.stopPropagation();
          serviceContainer.openNodeInInspector(object);
        },
        defaultRep: Grip,
      });
    }

    if (rootTitle) {
      return Accordion({
        items: [
          {
            component: ObjectInspector,
            componentProps: objectInspectorProps,
            header: rootTitle,
            opened: true,
          },
        ],
      });
    }

    return ObjectInspector(objectInspectorProps);
  }
}

module.exports = ObjectValueGripView;
PK
!<�P��EEQchrome/devtools/modules/devtools/client/inspector/extensions/extension-sidebar.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { createElement, createFactory } = require("devtools/client/shared/vendor/react");
const { Provider } = require("devtools/client/shared/vendor/react-redux");

const ObjectClient = require("devtools/shared/client/object-client");
const ExtensionSidebarComponent = createFactory(require("./components/ExtensionSidebar"));

const {
  updateObjectTreeView,
  updateObjectValueGripView,
  removeExtensionSidebar,
} = require("./actions/sidebar");

/**
 * ExtensionSidebar instances represents Inspector sidebars installed by add-ons
 * using the devtools.panels.elements.createSidebarPane WebExtensions API.
 *
 * The WebExtensions API registers the extensions' sidebars on the toolbox instance
 * (using the registerInspectorExtensionSidebar method) and, once the Inspector has been
 * created, the toolbox uses the Inpector createExtensionSidebar method to create the
 * ExtensionSidebar instances and then it registers them to the Inspector.
 *
 * @param {Inspector} inspector
 *        The inspector where the sidebar should be hooked to.
 * @param {Object} options
 * @param {String} options.id
 *        The unique id of the sidebar.
 * @param {String} options.title
 *        The title of the sidebar.
 */
class ExtensionSidebar {
  constructor(inspector, {id, title}) {
    this.inspector = inspector;
    this.store = inspector.store;
    this.id = id;
    this.title = title;

    this.destroyed = false;
  }

  /**
   * Lazily create a React ExtensionSidebarComponent wrapped into a Redux Provider.
   */
  get provider() {
    if (!this._provider) {
      this._provider = createElement(Provider, {
        store: this.store,
        key: this.id,
        title: this.title,
      }, ExtensionSidebarComponent({
        id: this.id,
        serviceContainer: {
          createObjectClient: (object) => {
            return new ObjectClient(this.inspector.toolbox.target.client, object);
          },
          releaseActor: (actor) => {
            if (!actor) {
              return;
            }
            this.inspector.toolbox.target.client.release(actor);
          },
          highlightDomElement: (grip, options = {}) => {
            const { highlighterUtils } = this.inspector.toolbox;

            if (!highlighterUtils) {
              return null;
            }

            return highlighterUtils.highlightDomValueGrip(grip, options);
          },
          unHighlightDomElement: (forceHide = false) => {
            const { highlighterUtils } = this.inspector.toolbox;

            if (!highlighterUtils) {
              return null;
            }

            return highlighterUtils.unhighlight(forceHide);
          },
          openNodeInInspector: async (grip) => {
            const { highlighterUtils } = this.inspector.toolbox;

            if (!highlighterUtils) {
              return null;
            }

            let front = await highlighterUtils.gripToNodeFront(grip);
            let onInspectorUpdated = this.inspector.once("inspector-updated");
            let onNodeFrontSet = this.inspector.toolbox.selection.setNodeFront(
              front, "inspector-extension-sidebar"
            );

            return Promise.all([onNodeFrontSet, onInspectorUpdated]);
          }
        },
      }));
    }

    return this._provider;
  }

  /**
   * Destroy the ExtensionSidebar instance, dispatch a removeExtensionSidebar Redux action
   * (which removes the related state from the Inspector store) and clear any reference
   * to the inspector, the Redux store and the lazily created Redux Provider component.
   *
   * This method is called by the inspector when the ExtensionSidebar is being removed
   * (or when the inspector is being destroyed).
   */
  destroy() {
    if (this.destroyed) {
      throw new Error(`ExtensionSidebar instances cannot be destroyed more than once`);
    }

    // Remove the data related to this extension from the inspector store.
    this.store.dispatch(removeExtensionSidebar(this.id));

    this.inspector = null;
    this.store = null;
    this._provider = null;

    this.destroyed = true;
  }

  /**
   * Dispatch an objectTreeView action to change the SidebarComponent into an
   * ObjectTreeView React Component, which shows the passed javascript object
   * in the sidebar.
   */
  setObject(object) {
    if (this.removed) {
      throw new Error("Unable to set an object preview on a removed ExtensionSidebar");
    }

    this.store.dispatch(updateObjectTreeView(this.id, object));
  }

  /**
   * Dispatch an objectPreview action to change the SidebarComponent into an
   * ObjectPreview React Component, which shows the passed value grip
   * in the sidebar.
   */
  setObjectValueGrip(objectValueGrip, rootTitle) {
    if (this.removed) {
      throw new Error("Unable to set an object preview on a removed ExtensionSidebar");
    }

    this.store.dispatch(updateObjectValueGripView(this.id, objectValueGrip, rootTitle));
  }
}

module.exports = ExtensionSidebar;
PK
!<om����Pchrome/devtools/modules/devtools/client/inspector/extensions/reducers/sidebar.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  EXTENSION_SIDEBAR_OBJECT_TREEVIEW_UPDATE,
  EXTENSION_SIDEBAR_OBJECT_GRIP_VIEW_UPDATE,
  EXTENSION_SIDEBAR_REMOVE,
} = require("../actions/index");

const INITIAL_SIDEBAR = {};

let reducers = {

  [EXTENSION_SIDEBAR_OBJECT_TREEVIEW_UPDATE](sidebar, {sidebarId, object}) {
    // Update the sidebar to a "object-treeview" which shows
    // the passed object.
    return Object.assign({}, sidebar, {
      [sidebarId]: {
        viewMode: "object-treeview",
        object,
      }
    });
  },

  [EXTENSION_SIDEBAR_OBJECT_GRIP_VIEW_UPDATE](
    sidebar, {sidebarId, objectValueGrip, rootTitle}
  ) {
    // Update the sidebar to a "object-treeview" which shows
    // the passed object.
    return Object.assign({}, sidebar, {
      [sidebarId]: {
        viewMode: "object-value-grip-view",
        objectValueGrip,
        rootTitle,
      }
    });
  },

  [EXTENSION_SIDEBAR_REMOVE](sidebar, {sidebarId}) {
    // Remove the sidebar from the Redux store.
    delete sidebar[sidebarId];
    return Object.assign({}, sidebar);
  },

};

module.exports = function (sidebar = INITIAL_SIDEBAR, action) {
  let reducer = reducers[action.type];
  if (!reducer) {
    return sidebar;
  }
  return reducer(sidebar, action);
};
PK
!<������Echrome/devtools/modules/devtools/client/inspector/extensions/types.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/vendor/react-prop-types");

// Helpers injected as props by extension-sidebar.js and used by the
// ObjectInspector component (which is part of the ObjectValueGripView).
exports.serviceContainer = {
  createObjectClient: PropTypes.func.isRequired,
  releaseActor: PropTypes.func.isRequired,
  highlightDomElement: PropTypes.func.isRequired,
  unHighlightDomElement: PropTypes.func.isRequired,
  openNodeInInspector: PropTypes.func.isRequired,
};
PK
!<���q��Lchrome/devtools/modules/devtools/client/inspector/flexbox/actions/flexbox.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  CLEAR_FLEXBOX,
  UPDATE_FLEXBOX,
  UPDATE_FLEXBOX_HIGHLIGHTED,
} = require("./index");

module.exports = {

  /**
   * Clears the flexbox state by resetting it back to the initial flexbox state.
   */
  clearFlexbox() {
    return {
      type: CLEAR_FLEXBOX,
    };
  },

  /**
   * Updates the flexbox state with the newly selected flexbox.
   */
  updateFlexbox(flexbox) {
    return {
      type: UPDATE_FLEXBOX,
      flexbox,
    };
  },

  /**
   * Updates the flexbox highlighted state.
   *
   * @param  {Boolean} highlighted
   *         Whether or not the flexbox highlighter is highlighting the flexbox.
   */
  updateFlexboxHighlighted(highlighted) {
    return {
      type: UPDATE_FLEXBOX_HIGHLIGHTED,
      highlighted,
    };
  },

};
PK
!<Sk��FFJchrome/devtools/modules/devtools/client/inspector/flexbox/actions/index.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { createEnum } = require("devtools/client/shared/enum");

createEnum([

  // Clears the flexbox state by resetting it back to the initial flexbox state.
  "CLEAR_FLEXBOX",

  // Updates the flexbox state with the newly selected flexbox.
  "UPDATE_FLEXBOX",

  // Updates the flexbox highlighted state.
  "UPDATE_FLEXBOX_HIGHLIGHTED",

], module.exports);
PK
!<��Ochrome/devtools/modules/devtools/client/inspector/flexbox/components/Flexbox.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const { getStr } = require("devtools/client/inspector/layout/utils/l10n");

const FlexboxItem = createFactory(require("./FlexboxItem"));

const Types = require("../types");

class Flexbox extends PureComponent {
  static get propTypes() {
    return {
      flexbox: PropTypes.shape(Types.flexbox).isRequired,
      setSelectedNode: PropTypes.func.isRequired,
      onHideBoxModelHighlighter: PropTypes.func.isRequired,
      onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
      onToggleFlexboxHighlighter: PropTypes.func.isRequired,
    };
  }

  render() {
    const {
      flexbox,
      setSelectedNode,
      onHideBoxModelHighlighter,
      onShowBoxModelHighlighterForNode,
      onToggleFlexboxHighlighter,
    } = this.props;

    return flexbox.actorID ?
      dom.div({ id: "layout-flexbox-container" },
        dom.div({ className: "flexbox-content" },
          dom.div({ className: "flexbox-container" },
            dom.span({}, getStr("flexbox.overlayFlexbox")),
            dom.ul(
              {
                id: "flexbox-list",
                className: "devtools-monospace",
              },
              FlexboxItem({
                key: flexbox.id,
                flexbox,
                setSelectedNode,
                onHideBoxModelHighlighter,
                onShowBoxModelHighlighterForNode,
                onToggleFlexboxHighlighter,
              })
            )
          )
        )
      )
      :
      dom.div({ className: "devtools-sidepanel-no-result" },
        getStr("flexbox.noFlexboxeOnThisPage")
      );
  }
}

module.exports = Flexbox;
PK
!<!�F   Schrome/devtools/modules/devtools/client/inspector/flexbox/components/FlexboxItem.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const { translateNodeFrontToGrip } = require("devtools/client/inspector/shared/utils");

// Reps
const { REPS, MODE } = require("devtools/client/shared/components/reps/reps");
const { Rep } = REPS;
const ElementNode = REPS.ElementNode;

const Types = require("../types");

class FlexboxItem extends PureComponent {
  static get propTypes() {
    return {
      flexbox: PropTypes.shape(Types.flexbox).isRequired,
      setSelectedNode: PropTypes.func.isRequired,
      onHideBoxModelHighlighter: PropTypes.func.isRequired,
      onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
      onToggleFlexboxHighlighter: PropTypes.func.isRequired,
    };
  }

  constructor(props) {
    super(props);
    this.onFlexboxCheckboxClick = this.onFlexboxCheckboxClick.bind(this);
    this.onFlexboxInspectIconClick = this.onFlexboxInspectIconClick.bind(this);
  }

  onFlexboxCheckboxClick(e) {
    // If the click was on the svg icon to select the node in the inspector, bail out.
    const originalTarget = e.nativeEvent && e.nativeEvent.explicitOriginalTarget;
    if (originalTarget && originalTarget.namespaceURI === "http://www.w3.org/2000/svg") {
      // We should be able to cancel the click event propagation after the following reps
      // issue is implemented : https://github.com/devtools-html/reps/issues/95 .
      e.preventDefault();
      return;
    }

    const {
      flexbox,
      onToggleFlexboxHighlighter,
    } = this.props;

    onToggleFlexboxHighlighter(flexbox.nodeFront);
  }

  onFlexboxInspectIconClick(nodeFront) {
    const { setSelectedNode } = this.props;
    setSelectedNode(nodeFront, "layout-panel").catch(e => console.error(e));
    nodeFront.scrollIntoView().catch(e => console.error(e));
  }

  render() {
    const {
      flexbox,
      onHideBoxModelHighlighter,
      onShowBoxModelHighlighterForNode,
    } = this.props;
    const {
      actorID,
      highlighted,
      nodeFront,
    } = flexbox;

    return dom.li(
      {},
      dom.label(
        {},
        dom.input(
          {
            type: "checkbox",
            value: actorID,
            checked: highlighted,
            onChange: this.onFlexboxCheckboxClick,
          }
        ),
        Rep(
          {
            defaultRep: ElementNode,
            mode: MODE.TINY,
            object: translateNodeFrontToGrip(nodeFront),
            onDOMNodeMouseOut: () => onHideBoxModelHighlighter(),
            onDOMNodeMouseOver: () => onShowBoxModelHighlighterForNode(nodeFront),
            onInspectIconClick: () => this.onFlexboxInspectIconClick(nodeFront),
          }
        )
      )
    );
  }
}

module.exports = FlexboxItem;
PK
!<y}� � Dchrome/devtools/modules/devtools/client/inspector/flexbox/flexbox.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { throttle } = require("devtools/client/inspector/shared/utils");

const {
  clearFlexbox,
  updateFlexbox,
  updateFlexboxHighlighted,
} = require("./actions/flexbox");

class FlexboxInspector {
  constructor(inspector, window) {
    this.document = window.document;
    this.highlighters = inspector.highlighters;
    this.inspector = inspector;
    this.store = inspector.store;
    this.walker = inspector.walker;

    this.onHighlighterChange = this.onHighlighterChange.bind(this);
    this.onReflow = throttle(this.onReflow, 500, this);
    this.onSidebarSelect = this.onSidebarSelect.bind(this);
    this.onToggleFlexboxHighlighter = this.onToggleFlexboxHighlighter.bind(this);
    this.onUpdatePanel = this.onUpdatePanel.bind(this);

    this.init();
  }

  async init() {
    if (!this.inspector) {
      return;
    }
    try {
      this.hasGetCurrentFlexbox = await this.inspector.target.actorHasMethod("layout",
        "getCurrentFlexbox");
      this.layoutInspector = await this.walker.getLayoutInspector();
    } catch (e) {
      // These calls might fail if called asynchrously after the toolbox is finished
      // closing.
      return;
    }

    this.highlighters.on("flexbox-highlighter-hidden", this.onHighlighterChange);
    this.highlighters.on("flexbox-highlighter-shown", this.onHighlighterChange);
    this.inspector.sidebar.on("select", this.onSidebarSelect);

    this.onSidebarSelect();
  }

  destroy() {
    this.highlighters.off("flexbox-highlighter-hidden", this.onHighlighterChange);
    this.highlighters.off("flexbox-highlighter-shown", this.onHighlighterChange);
    this.inspector.selection.off("new-node-front", this.onUpdatePanel);
    this.inspector.sidebar.off("select", this.onSidebarSelect);
    this.inspector.off("new-root", this.onUpdatePanel);

    this.inspector.reflowTracker.untrackReflows(this, this.onReflow);

    this.document = null;
    this.hasGetCurrentFlexbox = null;
    this.highlighters = null;
    this.inspector = null;
    this.layoutInspector = null;
    this.store = null;
    this.walker = null;
  }

  getComponentProps() {
    return {
      onToggleFlexboxHighlighter: this.onToggleFlexboxHighlighter,
    };
  }

  /**
   * Returns true if the layout panel is visible, and false otherwise.
   */
  isPanelVisible() {
    return this.inspector && this.inspector.toolbox && this.inspector.sidebar &&
           this.inspector.toolbox.currentToolId === "inspector" &&
           this.inspector.sidebar.getCurrentTabID() === "layoutview";
  }

  /**
   * Handler for "flexbox-highlighter-shown" and "flexbox-highlighter-hidden" events
   * emitted from the HighlightersOverlay. Updates the flex container highlighted state
   * only if the provided NodeFront is the current selected flex container.
   *
   * @param  {Event} event
   *         Event that was triggered.
   * @param  {NodeFront} nodeFront
   *         The NodeFront of the flex container element for which the flexbox
   *         highlighter is shown for.
   */
  onHighlighterChange(event, nodeFront) {
    const { flexbox } = this.store.getState();
    const highlighted = event === "flexbox-highlighter-shown";

    if (flexbox.nodeFront === nodeFront && flexbox.highlighted !== highlighted) {
      this.store.dispatch(updateFlexboxHighlighted(highlighted));
    }
  }

  /**
   * Handler for the "reflow" event fired by the inspector's reflow tracker. On reflows,
   * updates the flexbox panel because the shape of the flexbox on the page may have
   * changed.
   *
   * TODO: In the future, we will want to compare the flex item fragment data returned
   * for rendering the flexbox outline.
   */
  async onReflow() {
    if (!this.isPanelVisible() || !this.store || !this.inspector.selection.nodeFront) {
      return;
    }

    const { flexbox } = this.store.getState();

    let flexboxFront;
    try {
      if (!this.hasGetCurrentFlexbox) {
        return;
      }

      flexboxFront = await this.layoutInspector.getCurrentFlexbox(
        this.inspector.selection.nodeFront);
    } catch (e) {
      // This call might fail if called asynchrously after the toolbox is finished
      // closing.
      return;
    }

    // Clear the flexbox panel if there is no flex container for the current node
    // selection.
    if (!flexboxFront) {
      this.store.dispatch(clearFlexbox());
      return;
    }

    // Do nothing because the same flex container is still selected.
    if (flexbox.actorID == flexboxFront.actorID) {
      return;
    }

    // Update the flexbox panel with the new flexbox front contents.
    this.update(flexboxFront);
  }

  /**
   * Handler for the inspector sidebar "select" event. Updates the flexbox panel if it
   * is visible.
   */
  onSidebarSelect() {
    if (!this.isPanelVisible()) {
      this.inspector.reflowTracker.untrackReflows(this, this.onReflow);
      this.inspector.selection.off("new-node-front", this.onUpdatePanel);
      this.inspector.off("new-root", this.onUpdatePanel);
      return;
    }

    this.inspector.reflowTracker.trackReflows(this, this.onReflow);
    this.inspector.selection.on("new-node-front", this.onUpdatePanel);
    this.inspector.on("new-root", this.onUpdatePanel);

    this.update();
  }

  /**
   * Handler for a change in the input checkboxes in the FlexboxItem component.
   * Toggles on/off the flexbox highlighter for the provided flex container element.
   *
   * @param  {NodeFront} node
   *         The NodeFront of the flexb container element for which the flexbox
   *         highlighter is toggled on/off for.
   */
  onToggleFlexboxHighlighter(node) {
    this.highlighters.toggleFlexboxHighlighter(node);
    this.store.dispatch(updateFlexboxHighlighted(node !==
      this.highlighters.flexboxHighlighterShow));
  }

  /**
   * Handler for "new-root" event fired by the inspector and "new-node-front" event fired
   * by the inspector selection. Updates the flexbox panel if it is visible.
   */
  onUpdatePanel() {
    if (!this.isPanelVisible()) {
      return;
    }

    this.update();
  }

  /**
   * Updates the flexbox panel by dispatching the new flexbox data. This is called when
   * the layout view becomes visible or a new node is selected and needs to be update
   * with new flexbox data.
   *
   * @param  {FlexboxFront|Null} flexboxFront
   *         THe FlexboxFront of the flex container for the current node selection.
   */
  async update(flexboxFront) {
    // Stop refreshing if the inspector or store is already destroyed or no node is
    // selected.
    if (!this.inspector || !this.store || !this.inspector.selection.nodeFront) {
      return;
    }

    // Fetch the current flexbox if no flexbox front was passed into this update.
    if (!flexboxFront) {
      try {
        if (!this.hasGetCurrentFlexbox) {
          return;
        }

        flexboxFront = await this.layoutInspector.getCurrentFlexbox(
          this.inspector.selection.nodeFront);
      } catch (e) {
        // This call might fail if called asynchrously after the toolbox is finished
        // closing.
        return;
      }
    }

    // Clear the flexbox panel if there is no flex container for the current node
    // selection.
    if (!flexboxFront) {
      this.store.dispatch(clearFlexbox());
      return;
    }

    let nodeFront = flexboxFront.containerNodeFront;

    // If the FlexboxFront doesn't yet have access to the NodeFront for its container,
    // then get it from the walker. This happens when the walker hasn't seen this
    // particular DOM Node in the tree yet or when we are connected to an older server.
    if (!nodeFront) {
      try {
        nodeFront = await this.walker.getNodeFromActor(flexboxFront.actorID,
          ["containerEl"]);
      } catch (e) {
        // This call might fail if called asynchrously after the toolbox is finished
        // closing.
        return;
      }
    }

    this.store.dispatch(updateFlexbox({
      actorID: flexboxFront.actorID,
      highlighted: nodeFront == this.highlighters.flexboxHighlighterShown,
      nodeFront,
    }));
  }
}

module.exports = FlexboxInspector;
PK
!<�F>Mchrome/devtools/modules/devtools/client/inspector/flexbox/reducers/flexbox.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  CLEAR_FLEXBOX,
  UPDATE_FLEXBOX,
  UPDATE_FLEXBOX_HIGHLIGHTED,
} = require("../actions/index");

const INITIAL_FLEXBOX = {
  // The actor ID of the flex container.
  actorID: null,
  // Whether or not the flexbox highlighter is highlighting the flex container.
  highlighted: false,
  // The NodeFront of the flex container.
  nodeFront: null,
};

let reducers = {

  [CLEAR_FLEXBOX](flexbox, _) {
    return INITIAL_FLEXBOX;
  },

  [UPDATE_FLEXBOX](_, { flexbox }) {
    return flexbox;
  },

  [UPDATE_FLEXBOX_HIGHLIGHTED](flexbox, { highlighted }) {
    return Object.assign({}, flexbox, {
      highlighted,
    });
  },

};

module.exports = function (flexbox = INITIAL_FLEXBOX, action) {
  let reducer = reducers[action.type];
  if (!reducer) {
    return flexbox;
  }
  return reducer(flexbox, action);
};
PK
!<Q7�Kchrome/devtools/modules/devtools/client/inspector/flexbox/reducers/index.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

exports.flexbox = require("./flexbox");
PK
!<� �XDDBchrome/devtools/modules/devtools/client/inspector/flexbox/types.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/vendor/react-prop-types");

exports.flexbox = {

  // The actor ID of the flex container.
  actorID: PropTypes.number,

  // Whether or not the flexbox highlighter is highlighting the flex container.
  highlighted: PropTypes.bool,

  // The NodeFront of the flex container.
  nodeFront: PropTypes.object,

};
PK
!<����Ochrome/devtools/modules/devtools/client/inspector/fonts/actions/font-options.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  UPDATE_PREVIEW_TEXT,
} = require("./index");

module.exports = {

  /**
   * Update the preview text in the font inspector
   */
  updatePreviewText(previewText) {
    return {
      type: UPDATE_PREVIEW_TEXT,
      previewText,
    };
  },

};
PK
!<Y��M��Hchrome/devtools/modules/devtools/client/inspector/fonts/actions/fonts.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  UPDATE_FONTS,
} = require("./index");

module.exports = {

  /**
   * Update the list of fonts in the font inspector
   */
  updateFonts(fonts, otherFonts) {
    return {
      type: UPDATE_FONTS,
      fonts,
      otherFonts,
    };
  },

};
PK
!<޶�q��Hchrome/devtools/modules/devtools/client/inspector/fonts/actions/index.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { createEnum } = require("devtools/client/shared/enum");

createEnum([

  // Update the list of fonts.
  "UPDATE_FONTS",

  // Update the preview text.
  "UPDATE_PREVIEW_TEXT",

], module.exports);
PK
!<(\��}}Jchrome/devtools/modules/devtools/client/inspector/fonts/components/Font.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");

const FontPreview = createFactory(require("./FontPreview"));

loader.lazyRequireGetter(this, "clipboardHelper", "devtools/shared/platform/clipboard");

const { getStr } = require("../utils/l10n");
const Types = require("../types");

class Font extends PureComponent {
  static get propTypes() {
    return {
      font: PropTypes.shape(Types.font).isRequired,
      fontOptions: PropTypes.shape(Types.fontOptions).isRequired,
      onPreviewFonts: PropTypes.func.isRequired,
    };
  }

  constructor(props) {
    super(props);

    this.state = {
      isFontFaceRuleExpanded: false,
    };

    this.onFontFaceRuleToggle = this.onFontFaceRuleToggle.bind(this);
    this.onCopyURL = this.onCopyURL.bind(this);
  }

  componentWillReceiveProps(newProps) {
    if (this.props.font.name === newProps.font.name) {
      return;
    }

    this.setState({
      isFontFaceRuleExpanded: false,
    });
  }

  onCopyURL() {
    clipboardHelper.copyString(this.props.font.URI);
  }

  onFontFaceRuleToggle(event) {
    this.setState({
      isFontFaceRuleExpanded: !this.state.isFontFaceRuleExpanded
    });
    event.stopPropagation();
  }

  renderFontCSSCode(rule, ruleText) {
    if (!rule) {
      return null;
    }

    // Cut the rule text in 3 parts: the selector, the declarations, the closing brace.
    // This way we can collapse the declarations by default and display an expander icon
    // to expand them again.
    let leading = ruleText.substring(0, ruleText.indexOf("{") + 1);
    let body = ruleText.substring(ruleText.indexOf("{") + 1, ruleText.lastIndexOf("}"));
    let trailing = ruleText.substring(ruleText.lastIndexOf("}"));

    let { isFontFaceRuleExpanded } = this.state;

    return dom.pre(
      {
        className: "font-css-code",
      },
      this.renderFontCSSCodeTwisty(),
      leading,
      isFontFaceRuleExpanded ?
        body :
        dom.span(
          {
            className: "font-css-code-expander",
            onClick: this.onFontFaceRuleToggle,
          }
        ),
      trailing
    );
  }

  renderFontOrigin(url) {
    if (!url) {
      return dom.p(
        {
          className: "font-origin system"
        },
        getStr("fontinspector.system")
      );
    }

    return dom.p(
      {
        className: "font-origin remote",
      },
      dom.span(
        {
          className: "url",
          title: url
        },
        url
      ),
      dom.button(
        {
          className: "copy-icon",
          onClick: this.onCopyURL,
          title: getStr("fontinspector.copyURL"),
        }
      )
    );
  }

  renderFontName(name) {
    return dom.h1(
      {
        className: "font-name"
      },
      name
    );
  }

  renderFontCSSCodeTwisty() {
    let { isFontFaceRuleExpanded } = this.state;

    let attributes = {
      className: "theme-twisty",
      onClick: this.onFontFaceRuleToggle,
    };
    if (isFontFaceRuleExpanded) {
      attributes.open = "true";
    }

    return dom.span(attributes);
  }

  render() {
    let {
      font,
      fontOptions,
      onPreviewFonts,
    } = this.props;

    let { previewText } = fontOptions;

    let {
      name,
      previewUrl,
      rule,
      ruleText,
      URI,
    } = font;

    return dom.li(
      {
        className: "font",
      },
      this.renderFontName(name),
      FontPreview({ previewText, previewUrl, onPreviewFonts }),
      this.renderFontOrigin(URI),
      this.renderFontCSSCode(rule, ruleText)
    );
  }
}

module.exports = Font;
PK
!<da�Nchrome/devtools/modules/devtools/client/inspector/fonts/components/FontList.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");

const Font = createFactory(require("./Font"));

const Types = require("../types");

class FontList extends PureComponent {
  static get propTypes() {
    return {
      fontOptions: PropTypes.shape(Types.fontOptions).isRequired,
      fonts: PropTypes.arrayOf(PropTypes.shape(Types.font)).isRequired,
      onPreviewFonts: PropTypes.func.isRequired,
    };
  }

  render() {
    let {
      fonts,
      fontOptions,
      onPreviewFonts
    } = this.props;

    return dom.ul(
      {
        className: "fonts-list"
      },
      fonts.map((font, i) => Font({
        key: i,
        font,
        fontOptions,
        onPreviewFonts,
      }))
    );
  }
}

module.exports = FontList;
PK
!<� 	 	Qchrome/devtools/modules/devtools/client/inspector/fonts/components/FontPreview.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");

const Types = require("../types");
const { getStr } = require("../utils/l10n");

class FontPreview extends PureComponent {
  static get propTypes() {
    return {
      previewText: Types.fontOptions.previewText.isRequired,
      previewUrl: Types.font.previewUrl.isRequired,
      onPreviewFonts: PropTypes.func.isRequired,
    };
  }

  constructor(props) {
    super(props);

    this.state = {
      // Is the text preview input field currently focused?
      isFocused: false,
    };

    this.onBlur = this.onBlur.bind(this);
    this.onClick = this.onClick.bind(this);
    this.onChange = this.onChange.bind(this);
  }

  componentDidUpdate() {
    if (this.state.isFocused) {
      let input = this.fontPreviewInput;
      input.focus();
      input.selectionStart = input.selectionEnd = input.value.length;
    }
  }

  onBlur() {
    this.setState({ isFocused: false });
  }

  onClick(event) {
    this.setState({ isFocused: true });
    event.stopPropagation();
  }

  onChange(event) {
    this.props.onPreviewFonts(event.target.value);
  }

  render() {
    let {
      previewText,
      previewUrl,
    } = this.props;

    let { isFocused } = this.state;

    return dom.div(
      {
        className: "font-preview-container",
      },
      isFocused ?
        dom.input(
          {
            type: "search",
            className: "font-preview-input devtools-searchinput",
            value: previewText,
            onBlur: this.onBlur,
            onChange: this.onChange,
            ref: input => {
              this.fontPreviewInput = input;
            }
          }
        )
        :
        null,
      dom.img(
        {
          className: "font-preview",
          src: previewUrl,
          onClick: this.onClick,
          title: !isFocused ? getStr("fontinspector.editPreview") : "",
        }
      )
    );
  }
}

module.exports = FontPreview;
PK
!<O<�:$	$	Nchrome/devtools/modules/devtools/client/inspector/fonts/components/FontsApp.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const { connect } = require("devtools/client/shared/vendor/react-redux");

const Accordion = createFactory(require("devtools/client/inspector/layout/components/Accordion"));
const FontList = createFactory(require("./FontList"));

const { getStr } = require("../utils/l10n");
const Types = require("../types");

class FontsApp extends PureComponent {
  static get propTypes() {
    return {
      fontData: PropTypes.shape(Types.fontData).isRequired,
      fontOptions: PropTypes.shape(Types.fontOptions).isRequired,
      onPreviewFonts: PropTypes.func.isRequired,
    };
  }

  renderElementFonts() {
    let {
      fontData,
      fontOptions,
      onPreviewFonts,
    } = this.props;
    let { fonts } = fontData;

    return fonts.length ?
      FontList({
        fonts,
        fontOptions,
        onPreviewFonts
      })
      :
      dom.div(
        {
          className: "devtools-sidepanel-no-result"
        },
        getStr("fontinspector.noFontsOnSelectedElement")
      );
  }

  renderOtherFonts() {
    let {
      fontData,
      onPreviewFonts,
      fontOptions,
    } = this.props;
    let { otherFonts } = fontData;

    if (!otherFonts.length) {
      return null;
    }

    return Accordion({
      items: [
        {
          header: getStr("fontinspector.otherFontsInPageHeader"),
          component: FontList,
          componentProps: {
            fontOptions,
            fonts: otherFonts,
            onPreviewFonts
          },
          opened: false
        }
      ]
    });
  }

  render() {
    return dom.div(
      {
        className: "theme-sidebar inspector-tabpanel",
        id: "sidebar-panel-fontinspector"
      },
      dom.div(
        {
          id: "font-container"
        },
        this.renderElementFonts(),
        this.renderOtherFonts()
      )
    );
  }
}

module.exports = connect(state => state)(FontsApp);
PK
!<���x00@chrome/devtools/modules/devtools/client/inspector/fonts/fonts.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 { gDevTools } = require("devtools/client/framework/devtools");
const { getColor } = require("devtools/client/shared/theme");
const { createFactory, createElement } = require("devtools/client/shared/vendor/react");
const { Provider } = require("devtools/client/shared/vendor/react-redux");

const FontsApp = createFactory(require("./components/FontsApp"));

const { LocalizationHelper } = require("devtools/shared/l10n");
const INSPECTOR_L10N =
  new LocalizationHelper("devtools/client/locales/inspector.properties");

const { updateFonts } = require("./actions/fonts");
const { updatePreviewText } = require("./actions/font-options");

class FontInspector {
  constructor(inspector, window) {
    this.document = window.document;
    this.inspector = inspector;
    this.pageStyle = this.inspector.pageStyle;
    this.store = this.inspector.store;

    this.update = this.update.bind(this);

    this.onNewNode = this.onNewNode.bind(this);
    this.onPreviewFonts = this.onPreviewFonts.bind(this);
    this.onThemeChanged = this.onThemeChanged.bind(this);

    this.init();
  }

  init() {
    if (!this.inspector) {
      return;
    }

    let fontsApp = FontsApp({
      onPreviewFonts: this.onPreviewFonts,
    });

    let provider = createElement(Provider, {
      id: "fontinspector",
      key: "fontinspector",
      store: this.store,
      title: INSPECTOR_L10N.getStr("inspector.sidebar.fontInspectorTitle"),
    }, fontsApp);

    // Expose the provider to let inspector.js use it in setupSidebar.
    this.provider = provider;

    this.inspector.selection.on("new-node-front", this.onNewNode);
    this.inspector.sidebar.on("fontinspector-selected", this.onNewNode);

    // Listen for theme changes as the color of the previews depend on the theme
    gDevTools.on("theme-switched", this.onThemeChanged);

    this.store.dispatch(updatePreviewText(""));
    this.update(false, "");
  }

  /**
   * Given all fonts on the page, and given the fonts used in given node, return all fonts
   * not from the page not used in this node.
   *
   * @param  {Array} allFonts
   *         All fonts used on the entire page
   * @param  {Array} nodeFonts
   *         Fonts used only in one of the nodes
   * @return {Array}
   *         All fonts, except the ones used in the current node
   */
  excludeNodeFonts(allFonts, nodeFonts) {
    return allFonts.filter(font => {
      return !nodeFonts.some(nodeFont => nodeFont.name === font.name);
    });
  }

  /**
   * Destruction function called when the inspector is destroyed. Removes event listeners
   * and cleans up references.
   */
  destroy() {
    this.inspector.selection.off("new-node-front", this.onNewNode);
    this.inspector.sidebar.off("fontinspector-selected", this.onNewNode);
    gDevTools.off("theme-switched", this.onThemeChanged);

    this.document = null;
    this.inspector = null;
    this.pageStyle = null;
    this.store = null;
  }

  async getFontsForNode(node, options) {
    // In case we've been destroyed in the meantime
    if (!this.document) {
      return [];
    }

    let fonts = await this.pageStyle.getUsedFontFaces(node, options).catch(console.error);
    if (!fonts) {
      return [];
    }

    return fonts;
  }

  async getFontsNotInNode(nodeFonts, options) {
    // In case we've been destroyed in the meantime
    if (!this.document) {
      return [];
    }

    let allFonts = await this.pageStyle.getAllUsedFontFaces(options).catch(console.error);
    if (!allFonts) {
      allFonts = [];
    }

    return this.excludeNodeFonts(allFonts, nodeFonts);
  }

  /**
   * Returns true if the font inspector panel is visible, and false otherwise.
   */
  isPanelVisible() {
    return this.inspector.sidebar &&
           this.inspector.sidebar.getCurrentTabID() === "fontinspector";
  }

  /**
   * Selection 'new-node' event handler.
   */
  onNewNode() {
    if (this.isPanelVisible()) {
      this.update();
    }
  }

  /**
   * Handler for change in preview input.
   */
  onPreviewFonts(value) {
    this.store.dispatch(updatePreviewText(value));
    this.update();
  }

  /**
   * Handler for the "theme-switched" event.
   */
  onThemeChanged(event, frame) {
    if (frame === this.document.defaultView) {
      this.update();
    }
  }

  async update() {
    // Stop refreshing if the inspector or store is already destroyed.
    if (!this.inspector || !this.store) {
      return;
    }

    let node = this.inspector.selection.nodeFront;

    let fonts = [];
    let otherFonts = [];

    let { fontOptions } = this.store.getState();
    let { previewText } = fontOptions;

    // Clear the list of fonts if the currently selected node is not connected or a text
    // or element node unless all fonts are supposed to be shown.
    let isElementOrTextNode = this.inspector.selection.isElementNode() ||
                              this.inspector.selection.isTextNode();
    if (!node ||
        !this.isPanelVisible() ||
        !this.inspector.selection.isConnected() ||
        !isElementOrTextNode) {
      this.store.dispatch(updateFonts(fonts, otherFonts));
      return;
    }

    let options = {
      includePreviews: true,
      previewText,
      previewFillStyle: getColor("body-color")
    };

    // Add the includeVariations argument into the option to get font variation data.
    if (this.pageStyle.supportsFontVariations) {
      options.includeVariations = true;
    }

    fonts = await this.getFontsForNode(node, options);
    otherFonts = await this.getFontsNotInNode(fonts, options);

    if (!fonts.length && !otherFonts.length) {
      // No fonts to display. Clear the previously shown fonts.
      if (this.store) {
        this.store.dispatch(updateFonts(fonts, otherFonts));
      }
      return;
    }

    for (let font of [...fonts, ...otherFonts]) {
      font.previewUrl = await font.preview.data.string();
    }

    // in case we've been destroyed in the meantime
    if (!this.document) {
      return;
    }

    this.store.dispatch(updateFonts(fonts, otherFonts));

    this.inspector.emit("fontinspector-updated");
  }
}

module.exports = FontInspector;
PK
!<�*�:��Pchrome/devtools/modules/devtools/client/inspector/fonts/reducers/font-options.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  UPDATE_PREVIEW_TEXT,
} = require("../actions/index");

const INITIAL_FONT_OPTIONS = {
  previewText: "Abc",
};

let reducers = {

  [UPDATE_PREVIEW_TEXT](fontOptions, { previewText }) {
    return Object.assign({}, fontOptions, { previewText });
  },

};

module.exports = function (fontOptions = INITIAL_FONT_OPTIONS, action) {
  let reducer = reducers[action.type];
  if (!reducer) {
    return fontOptions;
  }
  return reducer(fontOptions, action);
};
PK
!<��L�||Ichrome/devtools/modules/devtools/client/inspector/fonts/reducers/fonts.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  UPDATE_FONTS,
} = require("../actions/index");

const INITIAL_FONT_DATA = {
  fonts: [],
  otherFonts: []
};

let reducers = {

  [UPDATE_FONTS](_, { fonts, otherFonts }) {
    return { fonts, otherFonts };
  },

};

module.exports = function (fontData = INITIAL_FONT_DATA, action) {
  let reducer = reducers[action.type];
  if (!reducer) {
    return fontData;
  }
  return reducer(fontData, action);
};
PK
!<�ö��@chrome/devtools/modules/devtools/client/inspector/fonts/types.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/vendor/react-prop-types");

/**
 * A font variation axis.
 */
const fontVariationAxis = exports.fontVariationAxis = {
  // The OpenType tag name of the variation axis
  tag: PropTypes.string,

  // The axis name of the variation axis
  name: PropTypes.string,

  // The minimum value of the variation axis
  minValue: PropTypes.number,

  // The maximum value of the variation axis
  maxValue: PropTypes.number,

  // The default value of the variation axis
  defaultValue: PropTypes.number,
};

const fontVariationInstanceValue = exports.fontVariationInstanceValue = {
  // The axis name of the variation axis
  axis: PropTypes.string,

  // The value of the variation axis
  value: PropTypes.number,
};

/**
 * A font variation instance.
 */
const fontVariationInstance = exports.fontVariationInstance = {
  // The variation instance name of the font
  axis: PropTypes.string,

  // The font variation values for the variation instance of the font
  values: PropTypes.arrayOf(PropTypes.shape(fontVariationInstanceValue)),
};

/**
 * A single font.
 */
const font = exports.font = {
  // The format of the font
  format: PropTypes.string,

  // The name of the font
  name: PropTypes.string,

  // URL for the font preview
  previewUrl: PropTypes.string,

  // Object containing the CSS rule for the font
  rule: PropTypes.object,

  // The text of the CSS rule
  ruleText: PropTypes.string,

  // The URI of the font file
  URI: PropTypes.string,

  // The variation axes of the font
  variationAxes: PropTypes.arrayOf(PropTypes.shape(fontVariationAxis)),

  // The variation instances of the font
  variationInstances: PropTypes.arrayOf(PropTypes.shape(fontVariationInstance))
};

exports.fontOptions = {
  // The current preview text
  previewText: PropTypes.string,
};

/**
 * Font data.
 */
exports.fontData = {
  // The fonts used in the current element.
  fonts: PropTypes.arrayOf(PropTypes.shape(font)),

  // Fonts used elsewhere.
  otherFonts: PropTypes.arrayOf(PropTypes.shape(font)),
};
PK
!<����Echrome/devtools/modules/devtools/client/inspector/fonts/utils/l10n.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/shared/l10n");
const L10N = new LocalizationHelper("devtools/client/locales/font-inspector.properties");

module.exports = {
  getStr: (...args) => L10N.getStr(...args),
};
PK
!<�..Hchrome/devtools/modules/devtools/client/inspector/grids/actions/grids.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  UPDATE_GRID_COLOR,
  UPDATE_GRID_HIGHLIGHTED,
  UPDATE_GRIDS,
} = require("./index");

module.exports = {

  /**
   * Update the color used for the grid's highlighter.
   *
   * @param  {NodeFront} nodeFront
   *         The NodeFront of the DOM node to toggle the grid highlighter.
   * @param  {String} color
   *         The color to use for this nodeFront's grid highlighter.
   */
  updateGridColor(nodeFront, color) {
    return {
      type: UPDATE_GRID_COLOR,
      color,
      nodeFront,
    };
  },

  /**
   * Update the grid highlighted state.
   *
   * @param  {NodeFront} nodeFront
   *         The NodeFront of the DOM node to toggle the grid highlighter.
   * @param  {Boolean} highlighted
   *         Whether or not the grid highlighter is highlighting the grid.
   */
  updateGridHighlighted(nodeFront, highlighted) {
    return {
      type: UPDATE_GRID_HIGHLIGHTED,
      highlighted,
      nodeFront,
    };
  },

  /**
   * Update the grid state with the new list of grids.
   */
  updateGrids(grids) {
    return {
      type: UPDATE_GRIDS,
      grids,
    };
  },

};
PK
!<5��.Wchrome/devtools/modules/devtools/client/inspector/grids/actions/highlighter-settings.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  UPDATE_SHOW_GRID_AREAS,
  UPDATE_SHOW_GRID_LINE_NUMBERS,
  UPDATE_SHOW_INFINITE_LINES,
} = require("./index");

module.exports = {

  /**
   * Update the grid highlighter's show grid areas preference.
   *
   * @param  {Boolean} enabled
   *         Whether or not the grid highlighter should show the grid areas.
   */
  updateShowGridAreas(enabled) {
    return {
      type: UPDATE_SHOW_GRID_AREAS,
      enabled,
    };
  },

  /**
   * Update the grid highlighter's show grid line numbers preference.
   *
   * @param  {Boolean} enabled
   *         Whether or not the grid highlighter should show the grid line numbers.
   */
  updateShowGridLineNumbers(enabled) {
    return {
      type: UPDATE_SHOW_GRID_LINE_NUMBERS,
      enabled,
    };
  },

  /**
   * Update the grid highlighter's show infinite lines preference.
   *
   * @param  {Boolean} enabled
   *         Whether or not the grid highlighter should extend grid lines infinitely.
   */
  updateShowInfiniteLines(enabled) {
    return {
      type: UPDATE_SHOW_INFINITE_LINES,
      enabled,
    };
  },

};
PK
!<���'@@Hchrome/devtools/modules/devtools/client/inspector/grids/actions/index.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { createEnum } = require("devtools/client/shared/enum");

createEnum([

  // Update the color used for the overlay of a grid.
  "UPDATE_GRID_COLOR",

  // Update the grid highlighted state.
  "UPDATE_GRID_HIGHLIGHTED",

  // Update the entire grids state with the new list of grids.
  "UPDATE_GRIDS",

  // Update the grid highlighter's show grid areas state.
  "UPDATE_SHOW_GRID_AREAS",

  // Update the grid highlighter's show grid line numbers state.
  "UPDATE_SHOW_GRID_LINE_NUMBERS",

  // Update the grid highlighter's show infinite lines state.
  "UPDATE_SHOW_INFINITE_LINES",

], module.exports);
PK
!<�̪066Jchrome/devtools/modules/devtools/client/inspector/grids/components/Grid.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const { getStr } = require("devtools/client/inspector/layout/utils/l10n");

const GridDisplaySettings = createFactory(require("./GridDisplaySettings"));
const GridList = createFactory(require("./GridList"));
const GridOutline = createFactory(require("./GridOutline"));

const Types = require("../types");

class Grid extends PureComponent {
  static get propTypes() {
    return {
      getSwatchColorPickerTooltip: PropTypes.func.isRequired,
      grids: PropTypes.arrayOf(PropTypes.shape(Types.grid)).isRequired,
      highlighterSettings: PropTypes.shape(Types.highlighterSettings).isRequired,
      setSelectedNode: PropTypes.func.isRequired,
      onHideBoxModelHighlighter: PropTypes.func.isRequired,
      onSetGridOverlayColor: PropTypes.func.isRequired,
      onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
      onShowGridAreaHighlight: PropTypes.func.isRequired,
      onShowGridCellHighlight: PropTypes.func.isRequired,
      onShowGridLineNamesHighlight: PropTypes.func.isRequired,
      onToggleGridHighlighter: PropTypes.func.isRequired,
      onToggleShowGridAreas: PropTypes.func.isRequired,
      onToggleShowGridLineNumbers: PropTypes.func.isRequired,
      onToggleShowInfiniteLines: PropTypes.func.isRequired,
    };
  }

  render() {
    let {
      getSwatchColorPickerTooltip,
      grids,
      highlighterSettings,
      setSelectedNode,
      onHideBoxModelHighlighter,
      onSetGridOverlayColor,
      onShowBoxModelHighlighterForNode,
      onShowGridAreaHighlight,
      onShowGridCellHighlight,
      onToggleShowGridAreas,
      onToggleGridHighlighter,
      onToggleShowGridLineNumbers,
      onToggleShowInfiniteLines,
    } = this.props;

    return grids.length ?
      dom.div(
        {
          id: "layout-grid-container",
        },
        dom.div(
          {
            className: "grid-content",
          },
          GridList({
            getSwatchColorPickerTooltip,
            grids,
            setSelectedNode,
            onHideBoxModelHighlighter,
            onSetGridOverlayColor,
            onShowBoxModelHighlighterForNode,
            onToggleGridHighlighter,
          }),
          GridDisplaySettings({
            highlighterSettings,
            onToggleShowGridAreas,
            onToggleShowGridLineNumbers,
            onToggleShowInfiniteLines,
          })
        ),
        GridOutline({
          grids,
          onShowGridAreaHighlight,
          onShowGridCellHighlight,
        })
      )
      :
      dom.div(
        {
          className: "devtools-sidepanel-no-result",
        },
        getStr("layout.noGridsOnThisPage")
      );
  }
}

module.exports = Grid;
PK
!<;4�a))Ychrome/devtools/modules/devtools/client/inspector/grids/components/GridDisplaySettings.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const { getStr } = require("devtools/client/inspector/layout/utils/l10n");

const Types = require("../types");

class GridDisplaySettings extends PureComponent {
  static get propTypes() {
    return {
      highlighterSettings: PropTypes.shape(Types.highlighterSettings).isRequired,
      onToggleShowGridAreas: PropTypes.func.isRequired,
      onToggleShowGridLineNumbers: PropTypes.func.isRequired,
      onToggleShowInfiniteLines: PropTypes.func.isRequired,
    };
  }

  constructor(props) {
    super(props);
    this.onShowGridAreasCheckboxClick = this.onShowGridAreasCheckboxClick.bind(this);
    this.onShowGridLineNumbersCheckboxClick =
      this.onShowGridLineNumbersCheckboxClick.bind(this);
    this.onShowInfiniteLinesCheckboxClick =
      this.onShowInfiniteLinesCheckboxClick.bind(this);
  }

  onShowGridAreasCheckboxClick() {
    let {
      highlighterSettings,
      onToggleShowGridAreas,
    } = this.props;

    onToggleShowGridAreas(!highlighterSettings.showGridAreasOverlay);
  }

  onShowGridLineNumbersCheckboxClick() {
    let {
      highlighterSettings,
      onToggleShowGridLineNumbers,
    } = this.props;

    onToggleShowGridLineNumbers(!highlighterSettings.showGridLineNumbers);
  }

  onShowInfiniteLinesCheckboxClick() {
    let {
      highlighterSettings,
      onToggleShowInfiniteLines,
    } = this.props;

    onToggleShowInfiniteLines(!highlighterSettings.showInfiniteLines);
  }

  render() {
    let {
      highlighterSettings,
    } = this.props;

    return dom.div(
      {
        className: "grid-container",
      },
      dom.span(
        {},
        getStr("layout.gridDisplaySettings")
      ),
      dom.ul(
        {},
        dom.li(
          {
            className: "grid-settings-item",
          },
          dom.label(
            {},
            dom.input(
              {
                id: "grid-setting-show-grid-line-numbers",
                type: "checkbox",
                checked: highlighterSettings.showGridLineNumbers,
                onChange: this.onShowGridLineNumbersCheckboxClick,
              }
            ),
            getStr("layout.displayLineNumbers")
          )
        ),
        dom.li(
          {
            className: "grid-settings-item",
          },
          dom.label(
           {},
           dom.input(
             {
               id: "grid-setting-show-grid-areas",
               type: "checkbox",
               checked: highlighterSettings.showGridAreasOverlay,
               onChange: this.onShowGridAreasCheckboxClick,
             }
           ),
           getStr("layout.displayAreaNames")
          )
        ),
        dom.li(
          {
            className: "grid-settings-item",
          },
          dom.label(
            {},
            dom.input(
              {
                id: "grid-setting-extend-grid-lines",
                type: "checkbox",
                checked: highlighterSettings.showInfiniteLines,
                onChange: this.onShowInfiniteLinesCheckboxClick,
              }
            ),
            getStr("layout.extendLinesInfinitely")
          )
        )
      )
    );
  }
}

module.exports = GridDisplaySettings;
PK
!<<<^���Nchrome/devtools/modules/devtools/client/inspector/grids/components/GridItem.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const { findDOMNode } = require("devtools/client/shared/vendor/react-dom");
const { translateNodeFrontToGrip } = require("devtools/client/inspector/shared/utils");

// Reps
const { REPS, MODE } = require("devtools/client/shared/components/reps/reps");
const { Rep } = REPS;
const ElementNode = REPS.ElementNode;

const Types = require("../types");

class GridItem extends PureComponent {
  static get propTypes() {
    return {
      getSwatchColorPickerTooltip: PropTypes.func.isRequired,
      grid: PropTypes.shape(Types.grid).isRequired,
      setSelectedNode: PropTypes.func.isRequired,
      onHideBoxModelHighlighter: PropTypes.func.isRequired,
      onSetGridOverlayColor: PropTypes.func.isRequired,
      onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
      onToggleGridHighlighter: PropTypes.func.isRequired,
    };
  }

  constructor(props) {
    super(props);
    this.setGridColor = this.setGridColor.bind(this);
    this.onGridCheckboxClick = this.onGridCheckboxClick.bind(this);
    this.onGridInspectIconClick = this.onGridInspectIconClick.bind(this);
  }

  componentDidMount() {
    let swatchEl = findDOMNode(this).querySelector(".grid-color-swatch");
    let tooltip = this.props.getSwatchColorPickerTooltip();

    let previousColor;
    tooltip.addSwatch(swatchEl, {
      onCommit: this.setGridColor,
      onPreview: this.setGridColor,
      onRevert: () => {
        this.props.onSetGridOverlayColor(this.props.grid.nodeFront, previousColor);
      },
      onShow: () => {
        previousColor = this.props.grid.color;
      },
    });
  }

  componentWillUnmount() {
    let swatchEl = findDOMNode(this).querySelector(".grid-color-swatch");
    let tooltip = this.props.getSwatchColorPickerTooltip();
    tooltip.removeSwatch(swatchEl);
  }

  setGridColor() {
    let color = findDOMNode(this).querySelector(".grid-color-value").textContent;
    this.props.onSetGridOverlayColor(this.props.grid.nodeFront, color);
  }

  onGridCheckboxClick(e) {
    // If the click was on the svg icon to select the node in the inspector, bail out.
    let originalTarget = e.nativeEvent && e.nativeEvent.explicitOriginalTarget;
    if (originalTarget && originalTarget.namespaceURI === "http://www.w3.org/2000/svg") {
      // We should be able to cancel the click event propagation after the following reps
      // issue is implemented : https://github.com/devtools-html/reps/issues/95 .
      e.preventDefault();
      return;
    }

    let {
      grid,
      onToggleGridHighlighter,
    } = this.props;

    onToggleGridHighlighter(grid.nodeFront);
  }

  onGridInspectIconClick(nodeFront) {
    let { setSelectedNode } = this.props;
    setSelectedNode(nodeFront, "layout-panel").catch(e => console.error(e));
    nodeFront.scrollIntoView().catch(e => console.error(e));
  }

  render() {
    let {
      grid,
      onHideBoxModelHighlighter,
      onShowBoxModelHighlighterForNode,
    } = this.props;
    let { nodeFront } = grid;

    return dom.li(
      {},
      dom.label(
        {},
        dom.input(
          {
            checked: grid.highlighted,
            type: "checkbox",
            value: grid.id,
            onChange: this.onGridCheckboxClick,
          }
        ),
        Rep(
          {
            defaultRep: ElementNode,
            mode: MODE.TINY,
            object: translateNodeFrontToGrip(nodeFront),
            onDOMNodeMouseOut: () => onHideBoxModelHighlighter(),
            onDOMNodeMouseOver: () => onShowBoxModelHighlighterForNode(nodeFront),
            onInspectIconClick: () => this.onGridInspectIconClick(nodeFront),
          }
        )
      ),
      dom.div(
        {
          className: "grid-color-swatch",
          style: {
            backgroundColor: grid.color,
          },
          title: grid.color,
        }
      ),
      // The SwatchColorPicker relies on the nextSibling of the swatch element to apply
      // the selected color. This is why we use a span in display: none for now.
      // Ideally we should modify the SwatchColorPickerTooltip to bypass this requirement.
      // See https://bugzilla.mozilla.org/show_bug.cgi?id=1341578
      dom.span(
        {
          className: "grid-color-value"
        },
        grid.color
      )
    );
  }
}

module.exports = GridItem;
PK
!<ﳘ���Nchrome/devtools/modules/devtools/client/inspector/grids/components/GridList.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const { getStr } = require("devtools/client/inspector/layout/utils/l10n");

const GridItem = createFactory(require("./GridItem"));

const Types = require("../types");

class GridList extends PureComponent {
  static get propTypes() {
    return {
      getSwatchColorPickerTooltip: PropTypes.func.isRequired,
      grids: PropTypes.arrayOf(PropTypes.shape(Types.grid)).isRequired,
      setSelectedNode: PropTypes.func.isRequired,
      onHideBoxModelHighlighter: PropTypes.func.isRequired,
      onSetGridOverlayColor: PropTypes.func.isRequired,
      onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
      onToggleGridHighlighter: PropTypes.func.isRequired,
    };
  }

  render() {
    let {
      getSwatchColorPickerTooltip,
      grids,
      setSelectedNode,
      onHideBoxModelHighlighter,
      onSetGridOverlayColor,
      onShowBoxModelHighlighterForNode,
      onToggleGridHighlighter,
    } = this.props;

    return dom.div(
      {
        className: "grid-container",
      },
      dom.span(
        {},
        getStr("layout.overlayGrid")
      ),
      dom.ul(
        {
          id: "grid-list",
          className: "devtools-monospace",
        },
        grids.map(grid => GridItem({
          key: grid.id,
          getSwatchColorPickerTooltip,
          grid,
          setSelectedNode,
          onHideBoxModelHighlighter,
          onSetGridOverlayColor,
          onShowBoxModelHighlighterForNode,
          onToggleGridHighlighter,
        }))
      )
    );
  }
}

module.exports = GridList;
PK
!<
�'��0�0Qchrome/devtools/modules/devtools/client/inspector/grids/components/GridOutline.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Services = require("Services");
const { PureComponent } = 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");
const { getStr } = require("devtools/client/inspector/layout/utils/l10n");
const {
  getWritingModeMatrix,
  getCSSMatrixTransform,
} = require("devtools/shared/layout/dom-matrix-2d");

const Types = require("../types");

// The delay prior to executing the grid cell highlighting.
const GRID_HIGHLIGHTING_DEBOUNCE = 50;

// Prefs for the max number of rows/cols a grid container can have for
// the outline to display.
const GRID_OUTLINE_MAX_ROWS_PREF =
  Services.prefs.getIntPref("devtools.gridinspector.gridOutlineMaxRows");
const GRID_OUTLINE_MAX_COLUMNS_PREF =
  Services.prefs.getIntPref("devtools.gridinspector.gridOutlineMaxColumns");

// Move SVG grid to the right 100 units, so that it is not flushed against the edge of
// layout border
const TRANSLATE_X = 0;
const TRANSLATE_Y = 0;

const GRID_CELL_SCALE_FACTOR = 50;

const VIEWPORT_MIN_HEIGHT = 100;
const VIEWPORT_MAX_HEIGHT = 150;

class GridOutline extends PureComponent {
  static get propTypes() {
    return {
      grids: PropTypes.arrayOf(PropTypes.shape(Types.grid)).isRequired,
      onShowGridAreaHighlight: PropTypes.func.isRequired,
      onShowGridCellHighlight: PropTypes.func.isRequired,
    };
  }

  constructor(props) {
    super(props);

    this.state = {
      height: 0,
      selectedGrid: null,
      showOutline: true,
      width: 0,
    };

    this.doHighlightCell = this.doHighlightCell.bind(this);
    this.getGridAreaName = this.getGridAreaName.bind(this);
    this.getHeight = this.getHeight.bind(this);
    this.getTotalWidthAndHeight = this.getTotalWidthAndHeight.bind(this);
    this.renderCannotShowOutlineText = this.renderCannotShowOutlineText.bind(this);
    this.renderGrid = this.renderGrid.bind(this);
    this.renderGridCell = this.renderGridCell.bind(this);
    this.renderGridOutline = this.renderGridOutline.bind(this);
    this.renderGridOutlineBorder = this.renderGridOutlineBorder.bind(this);
    this.renderOutline = this.renderOutline.bind(this);
    this.onHighlightCell = this.onHighlightCell.bind(this);
  }

  componentWillReceiveProps({ grids }) {
    let selectedGrid = grids.find(grid => grid.highlighted);

    // Store the height of the grid container in the component state to prevent overflow
    // issues. We want to store the width of the grid container as well so that the
    // viewbox is only the calculated width of the grid outline.
    let { width, height } = selectedGrid && selectedGrid.gridFragments.length
                            ? this.getTotalWidthAndHeight(selectedGrid)
                            : { width: 0, height: 0 };
    let showOutline;

    if (selectedGrid && selectedGrid.gridFragments.length) {
      const { cols, rows } = selectedGrid.gridFragments[0];

      // Show the grid outline if both the rows/columns are less than or equal
      // to their max prefs.
      showOutline = (cols.lines.length <= GRID_OUTLINE_MAX_COLUMNS_PREF) &&
                    (rows.lines.length <= GRID_OUTLINE_MAX_ROWS_PREF);
    }

    this.setState({ height, width, selectedGrid, showOutline });
  }

  doHighlightCell(target, hide) {
    const {
      grids,
      onShowGridAreaHighlight,
      onShowGridCellHighlight,
    } = this.props;
    const name = target.dataset.gridAreaName;
    const id = target.dataset.gridId;
    const fragmentIndex = target.dataset.gridFragmentIndex;
    const color = target.closest(".grid-outline-group").dataset.gridLineColor;
    const rowNumber = target.dataset.gridRow;
    const columnNumber = target.dataset.gridColumn;

    onShowGridAreaHighlight(grids[id].nodeFront, null, color);
    onShowGridCellHighlight(grids[id].nodeFront, color);

    if (hide) {
      return;
    }

    if (name) {
      onShowGridAreaHighlight(grids[id].nodeFront, name, color);
    }

    if (fragmentIndex && rowNumber && columnNumber) {
      onShowGridCellHighlight(grids[id].nodeFront, color, fragmentIndex,
        rowNumber, columnNumber);
    }
  }

  /**
   * Returns the grid area name if the given grid cell is part of a grid area, otherwise
   * null.
   *
   * @param  {Number} columnNumber
   *         The column number of the grid cell.
   * @param  {Number} rowNumber
   *         The row number of the grid cell.
   * @param  {Array} areas
   *         Array of grid areas data stored in the grid fragment.
   * @return {String} If there is a grid area return area name, otherwise null.
   */
  getGridAreaName(columnNumber, rowNumber, areas) {
    const gridArea = areas.find(area =>
      (area.rowStart <= rowNumber && area.rowEnd > rowNumber) &&
      (area.columnStart <= columnNumber && area.columnEnd > columnNumber)
    );

    if (!gridArea) {
      return null;
    }

    return gridArea.name;
  }

  /**
   * Returns the height of the grid outline ranging between a minimum and maximum height.
   *
   * @return {Number} The height of the grid outline.
   */
  getHeight() {
    const { height } = this.state;

    if (height >= VIEWPORT_MAX_HEIGHT) {
      return VIEWPORT_MAX_HEIGHT;
    } else if (height <= VIEWPORT_MIN_HEIGHT) {
      return VIEWPORT_MIN_HEIGHT;
    }

    return height;
  }

  /**
   * Get the width and height of a given grid.
   *
   * @param  {Object} grid
   *         A single grid container in the document.
   * @return {Object} An object like { width, height }
   */
  getTotalWidthAndHeight(grid) {
    // TODO: We are drawing the first fragment since only one is currently being stored.
    // In the future we will need to iterate over all fragments of a grid.
    const { gridFragments } = grid;
    const { rows, cols } = gridFragments[0];

    let height = 0;
    for (let i = 0; i < rows.lines.length - 1; i++) {
      height += GRID_CELL_SCALE_FACTOR * (rows.tracks[i].breadth / 100);
    }

    let width = 0;
    for (let i = 0; i < cols.lines.length - 1; i++) {
      width += GRID_CELL_SCALE_FACTOR * (cols.tracks[i].breadth / 100);
    }

    // All writing modes other than horizontal-tb (the initial value) involve a 90 deg
    // rotation, so swap width and height.
    if (grid.writingMode != "horizontal-tb") {
      [ width, height ] = [ height, width ];
    }

    return { width, height };
  }

  /**
   * Displays a message text "Cannot show outline for this grid".
   */
  renderCannotShowOutlineText() {
    return dom.div(
      {
        className: "grid-outline-text"
      },
      dom.span(
        {
          className: "grid-outline-text-icon",
          title: getStr("layout.cannotShowGridOutline.title")
        }
      ),
      getStr("layout.cannotShowGridOutline")
    );
  }

  /**
   * Renders the grid outline for the given grid container object.
   *
   * @param  {Object} grid
   *         A single grid container in the document.
   */
  renderGrid(grid) {
    // TODO: We are drawing the first fragment since only one is currently being stored.
    // In the future we will need to iterate over all fragments of a grid.
    let gridFragmentIndex = 0;
    const { id, color, gridFragments } = grid;
    const { rows, cols, areas } = gridFragments[gridFragmentIndex];

    const numberOfColumns = cols.lines.length - 1;
    const numberOfRows = rows.lines.length - 1;
    const rectangles = [];
    let x = 0;
    let y = 0;
    let width = 0;
    let height = 0;

    // Draw the cells contained within the grid outline border.
    for (let rowNumber = 1; rowNumber <= numberOfRows; rowNumber++) {
      height = GRID_CELL_SCALE_FACTOR * (rows.tracks[rowNumber - 1].breadth / 100);

      for (let columnNumber = 1; columnNumber <= numberOfColumns; columnNumber++) {
        width = GRID_CELL_SCALE_FACTOR * (cols.tracks[columnNumber - 1].breadth / 100);

        const gridAreaName = this.getGridAreaName(columnNumber, rowNumber, areas);
        const gridCell = this.renderGridCell(id, gridFragmentIndex, x, y,
                                             rowNumber, columnNumber, color, gridAreaName,
                                             width, height);

        rectangles.push(gridCell);
        x += width;
      }

      x = 0;
      y += height;
    }

    // Transform the cells as needed to match the grid container's writing mode.
    let cellGroupStyle = {};
    let writingModeMatrix = getWritingModeMatrix(this.state, grid);
    cellGroupStyle.transform = getCSSMatrixTransform(writingModeMatrix);
    let cellGroup = dom.g(
      {
        id: "grid-cell-group",
        style: cellGroupStyle,
      },
      rectangles
    );

    // Draw a rectangle that acts as the grid outline border.
    const border = this.renderGridOutlineBorder(this.state.width, this.state.height,
                                                color);

    return [
      border,
      cellGroup,
    ];
  }

  /**
   * Renders the grid cell of a grid fragment.
   *
   * @param  {Number} id
   *         The grid id stored on the grid fragment
   * @param  {Number} gridFragmentIndex
   *         The index of the grid fragment rendered to the document.
   * @param  {Number} x
   *         The x-position of the grid cell.
   * @param  {Number} y
   *         The y-position of the grid cell.
   * @param  {Number} rowNumber
   *         The row number of the grid cell.
   * @param  {Number} columnNumber
   *         The column number of the grid cell.
   * @param  {String|null} gridAreaName
   *         The grid area name or null if the grid cell is not part of a grid area.
   * @param  {Number} width
   *         The width of grid cell.
   * @param  {Number} height
   *         The height of the grid cell.
   */
  renderGridCell(id, gridFragmentIndex, x, y, rowNumber, columnNumber, color,
    gridAreaName, width, height) {
    return dom.rect(
      {
        key: `${id}-${rowNumber}-${columnNumber}`,
        className: "grid-outline-cell",
        "data-grid-area-name": gridAreaName,
        "data-grid-fragment-index": gridFragmentIndex,
        "data-grid-id": id,
        "data-grid-row": rowNumber,
        "data-grid-column": columnNumber,
        x,
        y,
        width,
        height,
        fill: "none",
        onMouseEnter: this.onHighlightCell,
        onMouseLeave: this.onHighlightCell,
      }
    );
  }

  renderGridOutline(grid) {
    let { color } = grid;

    return dom.g(
      {
        id: "grid-outline-group",
        className: "grid-outline-group",
        "data-grid-line-color": color,
        style: { color }
      },
      this.renderGrid(grid)
    );
  }

  renderGridOutlineBorder(borderWidth, borderHeight, color) {
    return dom.rect(
      {
        key: "border",
        className: "grid-outline-border",
        x: 0,
        y: 0,
        width: borderWidth,
        height: borderHeight
      }
    );
  }

  renderOutline() {
    const {
      height,
      selectedGrid,
      showOutline,
      width,
    } = this.state;

    return showOutline ?
      dom.svg(
        {
          id: "grid-outline",
          width: "100%",
          height: this.getHeight(),
          viewBox: `${TRANSLATE_X} ${TRANSLATE_Y} ${width} ${height}`,
        },
        this.renderGridOutline(selectedGrid)
      )
      :
      this.renderCannotShowOutlineText();
  }

  onHighlightCell({ target, type }) {
    // Debounce the highlighting of cells.
    // This way we don't end up sending many requests to the server for highlighting when
    // cells get hovered in a rapid succession We only send a request if the user settles
    // on a cell for some time.
    if (this.highlightTimeout) {
      clearTimeout(this.highlightTimeout);
    }

    this.highlightTimeout = setTimeout(() => {
      this.doHighlightCell(target, type === "mouseleave");
      this.highlightTimeout = null;
    }, GRID_HIGHLIGHTING_DEBOUNCE);
  }

  render() {
    const { selectedGrid } = this.state;

    return selectedGrid && selectedGrid.gridFragments.length ?
      dom.div(
        {
          id: "grid-outline-container",
          className: "grid-outline-container",
        },
        this.renderOutline()
      )
      :
      null;
  }
}

module.exports = GridOutline;
PK
!<���ګa�aIchrome/devtools/modules/devtools/client/inspector/grids/grid-inspector.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Services = require("Services");

const SwatchColorPickerTooltip = require("devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip");
const { throttle } = require("devtools/client/inspector/shared/utils");
const { compareFragmentsGeometry } = require("devtools/client/inspector/grids/utils/utils");
const asyncStorage = require("devtools/shared/async-storage");
const { parseURL } = require("devtools/client/shared/source-utils");

const {
  updateGridColor,
  updateGridHighlighted,
  updateGrids,
} = require("./actions/grids");
const {
  updateShowGridAreas,
  updateShowGridLineNumbers,
  updateShowInfiniteLines,
} = require("./actions/highlighter-settings");

const CSS_GRID_COUNT_HISTOGRAM_ID = "DEVTOOLS_NUMBER_OF_CSS_GRIDS_IN_A_PAGE";

const SHOW_GRID_AREAS = "devtools.gridinspector.showGridAreas";
const SHOW_GRID_LINE_NUMBERS = "devtools.gridinspector.showGridLineNumbers";
const SHOW_INFINITE_LINES_PREF = "devtools.gridinspector.showInfiniteLines";

// Default grid colors.
const GRID_COLORS = [
  "#9400FF",
  "#DF00A9",
  "#0A84FF",
  "#12BC00",
  "#EA8000",
  "#00B0BD",
  "#D70022",
  "#4B42FF",
  "#B5007F",
  "#058B00",
  "#A47F00",
  "#005A71"
];

class GridInspector {
  constructor(inspector, window) {
    this.document = window.document;
    this.highlighters = inspector.highlighters;
    this.inspector = inspector;
    this.store = inspector.store;
    this.telemetry = inspector.telemetry;
    this.walker = this.inspector.walker;

    this.getSwatchColorPickerTooltip = this.getSwatchColorPickerTooltip.bind(this);
    this.updateGridPanel = this.updateGridPanel.bind(this);

    this.onHighlighterChange = this.onHighlighterChange.bind(this);
    this.onNavigate = this.onNavigate.bind(this);
    this.onReflow = throttle(this.onReflow, 500, this);
    this.onSetGridOverlayColor = this.onSetGridOverlayColor.bind(this);
    this.onShowGridAreaHighlight = this.onShowGridAreaHighlight.bind(this);
    this.onShowGridCellHighlight = this.onShowGridCellHighlight.bind(this);
    this.onShowGridLineNamesHighlight = this.onShowGridLineNamesHighlight.bind(this);
    this.onSidebarSelect = this.onSidebarSelect.bind(this);
    this.onToggleGridHighlighter = this.onToggleGridHighlighter.bind(this);
    this.onToggleShowGridAreas = this.onToggleShowGridAreas.bind(this);
    this.onToggleShowGridLineNumbers = this.onToggleShowGridLineNumbers.bind(this);
    this.onToggleShowInfiniteLines = this.onToggleShowInfiniteLines.bind(this);

    this.init();
  }

  /**
   * Initializes the grid inspector by fetching the LayoutFront from the walker, loading
   * the highlighter settings and initalizing the SwatchColorPicker instance.
   */
  async init() {
    if (!this.inspector) {
      return;
    }

    this.layoutInspector = await this.inspector.walker.getLayoutInspector();

    this.loadHighlighterSettings();

    // Create a shared SwatchColorPicker instance to be reused by all GridItem components.
    this.swatchColorPickerTooltip = new SwatchColorPickerTooltip(
      this.inspector.toolbox.doc,
      this.inspector,
      {
        supportsCssColor4ColorFunction: () => false
      }
    );

    this.highlighters.on("grid-highlighter-hidden", this.onHighlighterChange);
    this.highlighters.on("grid-highlighter-shown", this.onHighlighterChange);
    this.inspector.sidebar.on("select", this.onSidebarSelect);
    this.inspector.on("new-root", this.onNavigate);

    this.onSidebarSelect();
  }

  /**
   * Destruction function called when the inspector is destroyed. Removes event listeners
   * and cleans up references.
   */
  destroy() {
    this.highlighters.off("grid-highlighter-hidden", this.onHighlighterChange);
    this.highlighters.off("grid-highlighter-shown", this.onHighlighterChange);
    this.inspector.sidebar.off("select", this.onSidebarSelect);
    this.inspector.off("new-root", this.onNavigate);

    this.inspector.reflowTracker.untrackReflows(this, this.onReflow);

    // The color picker may not be ready as `init` function is async,
    // and we do not wait for its completion before calling destroy in tests
    if (this.swatchColorPickerTooltip) {
      this.swatchColorPickerTooltip.destroy();
    }

    this.document = null;
    this.highlighters = null;
    this.inspector = null;
    this.layoutInspector = null;
    this.store = null;
    this.swatchColorPickerTooltip = null;
    this.walker = null;
  }

  getComponentProps() {
    return {
      getSwatchColorPickerTooltip: this.getSwatchColorPickerTooltip,
      onSetGridOverlayColor: this.onSetGridOverlayColor,
      onShowGridAreaHighlight: this.onShowGridAreaHighlight,
      onShowGridCellHighlight: this.onShowGridCellHighlight,
      onShowGridLineNamesHighlight: this.onShowGridLineNamesHighlight,
      onToggleGridHighlighter: this.onToggleGridHighlighter,
      onToggleShowGridAreas: this.onToggleShowGridAreas,
      onToggleShowGridLineNumbers: this.onToggleShowGridLineNumbers,
      onToggleShowInfiniteLines: this.onToggleShowInfiniteLines,
    };
  }

  /**
   * Returns the initial color linked to a grid container. Will attempt to check the
   * current grid highlighter state and the store.
   *
   * @param  {NodeFront} nodeFront
   *         The NodeFront for which we need the color.
   * @param  {String} customColor
   *         The color fetched from the custom palette, if it exists.
   * @param  {String} fallbackColor
   *         The color to use if no color could be found for the node front.
   * @return {String} color
   *         The color to use.
   */
  getInitialGridColor(nodeFront, customColor, fallbackColor) {
    let highlighted = nodeFront == this.highlighters.gridHighlighterShown;

    let color;
    if (customColor) {
      color = customColor;
    } else if (highlighted && this.highlighters.state.grid.options) {
      // If the node front is currently highlighted, use the color from the highlighter
      // options.
      color = this.highlighters.state.grid.options.color;
    } else {
      // Otherwise use the color defined in the store for this node front.
      color = this.getGridColorForNodeFront(nodeFront);
    }

    return color || fallbackColor;
  }

  /**
   * Returns the color set for the grid highlighter associated with the provided
   * nodeFront.
   *
   * @param  {NodeFront} nodeFront
   *         The NodeFront for which we need the color.
   */
  getGridColorForNodeFront(nodeFront) {
    let { grids } = this.store.getState();

    for (let grid of grids) {
      if (grid.nodeFront === nodeFront) {
        return grid.color;
      }
    }

    return null;
  }

  /**
   * Create a highlighter settings object for the provided nodeFront.
   *
   * @param  {NodeFront} nodeFront
   *         The NodeFront for which we need highlighter settings.
   */
  getGridHighlighterSettings(nodeFront) {
    let { highlighterSettings } = this.store.getState();

    // Get the grid color for the provided nodeFront.
    let color = this.getGridColorForNodeFront(nodeFront);

    // Merge the grid color to the generic highlighter settings.
    return Object.assign({}, highlighterSettings, {
      color
    });
  }

  /**
   * Retrieve the shared SwatchColorPicker instance.
   */
  getSwatchColorPickerTooltip() {
    return this.swatchColorPickerTooltip;
  }

  /**
   * Given a list of new grid fronts, and if we have a currently highlighted grid, check
   * if its fragments have changed.
   *
   * @param  {Array} newGridFronts
   *         A list of GridFront objects.
   * @return {Boolean}
   */
  haveCurrentFragmentsChanged(newGridFronts) {
    const currentNode = this.highlighters.gridHighlighterShown;
    if (!currentNode) {
      return false;
    }

    const newGridFront = newGridFronts.find(g => g.containerNodeFront === currentNode);
    if (!newGridFront) {
      return false;
    }

    const { grids } = this.store.getState();
    const oldFragments = grids.find(g => g.nodeFront === currentNode).gridFragments;
    const newFragments = newGridFront.gridFragments;

    return !compareFragmentsGeometry(oldFragments, newFragments);
  }

  /**
   * Returns true if the layout panel is visible, and false otherwise.
   */
  isPanelVisible() {
    return this.inspector && this.inspector.toolbox && this.inspector.sidebar &&
           this.inspector.toolbox.currentToolId === "inspector" &&
           this.inspector.sidebar.getCurrentTabID() === "layoutview";
  }

  /**
   * Load the grid highligher display settings into the store from the stored preferences.
   */
  loadHighlighterSettings() {
    let { dispatch } = this.store;

    let showGridAreas = Services.prefs.getBoolPref(SHOW_GRID_AREAS);
    let showGridLineNumbers = Services.prefs.getBoolPref(SHOW_GRID_LINE_NUMBERS);
    let showInfinteLines = Services.prefs.getBoolPref(SHOW_INFINITE_LINES_PREF);

    dispatch(updateShowGridAreas(showGridAreas));
    dispatch(updateShowGridLineNumbers(showGridLineNumbers));
    dispatch(updateShowInfiniteLines(showInfinteLines));
  }

  showGridHighlighter(node, settings) {
    this.lastHighlighterColor = settings.color;
    this.lastHighlighterNode = node;
    this.lastHighlighterState = true;

    this.highlighters.showGridHighlighter(node, settings);
  }

  toggleGridHighlighter(node, settings) {
    this.lastHighlighterColor = settings.color;
    this.lastHighlighterNode = node;
    this.lastHighlighterState = node !== this.highlighters.gridHighlighterShown;

    this.highlighters.toggleGridHighlighter(node, settings, "grid");
  }

  /**
   * Updates the grid panel by dispatching the new grid data. This is called when the
   * layout view becomes visible or the view needs to be updated with new grid data.
   */
  async updateGridPanel() {
    // Stop refreshing if the inspector or store is already destroyed.
    if (!this.inspector || !this.store) {
      return;
    }
    let currentUrl = this.inspector.target.url;
    // Get the hostname, if there is no hostname, fall back on protocol
    // ex: `data:` uri, and `about:` pages
    let hostname = parseURL(currentUrl).hostname || parseURL(currentUrl).protocol;
    let customColors = await asyncStorage.getItem("gridInspectorHostColors") || {};

    // Get all the GridFront from the server if no gridFronts were provided.
    let gridFronts;
    try {
      gridFronts = await this.layoutInspector.getGrids(this.walker.rootNode);
    } catch (e) {
      // This call might fail if called asynchrously after the toolbox is finished
      // closing.
      return;
    }

    // Log how many CSS Grid elements DevTools sees.
    if (gridFronts.length > 0 &&
        currentUrl != this.inspector.previousURL) {
      this.telemetry.log(CSS_GRID_COUNT_HISTOGRAM_ID, gridFronts.length);
      this.inspector.previousURL = currentUrl;
    }

    let grids = [];
    for (let i = 0; i < gridFronts.length; i++) {
      let grid = gridFronts[i];

      let nodeFront = grid.containerNodeFront;

      // If the GridFront didn't yet have access to the NodeFront for its container, then
      // get it from the walker. This happens when the walker hasn't yet seen this
      // particular DOM Node in the tree yet, or when we are connected to an older server.
      if (!nodeFront) {
        try {
          nodeFront = await this.walker.getNodeFromActor(grid.actorID, ["containerEl"]);
        } catch (e) {
          // This call might fail if called asynchrously after the toolbox is finished
          // closing.
          return;
        }
      }

      let colorForHost = customColors[hostname] ? customColors[hostname][i] : undefined;
      let fallbackColor = GRID_COLORS[i % GRID_COLORS.length];
      let color = this.getInitialGridColor(nodeFront, colorForHost, fallbackColor);

      grids.push({
        id: i,
        actorID: grid.actorID,
        color,
        direction: grid.direction,
        gridFragments: grid.gridFragments,
        highlighted: nodeFront == this.highlighters.gridHighlighterShown,
        nodeFront,
        writingMode: grid.writingMode,
      });
    }

    this.store.dispatch(updateGrids(grids));
    this.inspector.emit("grid-panel-updated");
  }

  /**
   * Handler for "grid-highlighter-shown" and "grid-highlighter-hidden" events emitted
   * from the HighlightersOverlay. Updates the NodeFront's grid highlighted state.
   *
   * @param  {Event} event
   *         Event that was triggered.
   * @param  {NodeFront} nodeFront
   *         The NodeFront of the grid container element for which the grid highlighter
   *         is shown for.
   * @param  {Object} options
   *         The highlighter options used for the highlighter being shown/hidden.
   */
  onHighlighterChange(event, nodeFront, options = {}) {
    let highlighted = event === "grid-highlighter-shown";
    let { color } = options;

    // Only tell the store that the highlighter changed if it did change.
    // If we're still highlighting the same node, with the same color, no need to force
    // a refresh.
    if (this.lastHighlighterState !== highlighted ||
        this.lastHighlighterNode !== nodeFront) {
      this.store.dispatch(updateGridHighlighted(nodeFront, highlighted));
    }

    if (this.lastHighlighterColor !== color || this.lastHighlighterNode !== nodeFront) {
      this.store.dispatch(updateGridColor(nodeFront, color));
    }

    this.lastHighlighterColor = null;
    this.lastHighlighterNode = null;
    this.lastHighlighterState = null;
  }

  /**
   * Handler for "new-root" event fired by the inspector, which indicates a page
   * navigation. Updates grid panel contents.
   */
  onNavigate() {
    if (this.isPanelVisible()) {
      this.updateGridPanel();
    }
  }

  /**
   * Handler for the "reflow" event fired by the inspector's reflow tracker. On reflows,
   * update the grid panel content, because the shape or number of grids on the page may
   * have changed.
   *
   * Note that there may be frequent reflows on the page and that not all of them actually
   * cause the grids to change. So, we want to limit how many times we update the grid
   * panel to only reflows that actually either change the list of grids, or those that
   * change the current outlined grid.
   * To achieve this, this function compares the list of grid containers from before and
   * after the reflow, as well as the grid fragment data on the currently highlighted
   * grid.
   */
  async onReflow() {
    if (!this.isPanelVisible()) {
      return;
    }

    // The list of grids currently displayed.
    const { grids } = this.store.getState();

    // The new list of grids from the server.
    let newGridFronts;
    try {
      newGridFronts = await this.layoutInspector.getGrids(this.walker.rootNode);
    } catch (e) {
      // This call might fail if called asynchrously after the toolbox is finished
      // closing.
      return;
    }

    // Get the node front(s) from the current grid(s) so we can compare them to them to
    // node(s) of the new grids.
    const oldNodeFronts = grids.map(grid => grid.nodeFront.actorID);

    // In some cases, the nodes for current grids may have been removed from the DOM in
    // which case we need to update.
    if (grids.length && grids.some(grid => !grid.nodeFront.actorID)) {
      this.updateGridPanel(newGridFronts);
      return;
    }

    // Otherwise, continue comparing with the new grids.
    const newNodeFronts = newGridFronts.filter(grid => grid.containerNodeFront)
                                       .map(grid => grid.containerNodeFront.actorID);
    if (grids.length === newGridFronts.length &&
        oldNodeFronts.sort().join(",") == newNodeFronts.sort().join(",")) {
      // Same list of containers, but let's check if the geometry of the current grid has
      // changed, if it hasn't we can safely abort.
      if (!this.highlighters.gridHighlighterShown ||
          (this.highlighters.gridHighlighterShown &&
           !this.haveCurrentFragmentsChanged(newGridFronts))) {
        return;
      }
    }

    // Either the list of containers or the current fragments have changed, do update.
    this.updateGridPanel(newGridFronts);
  }

  /**
   * Handler for a change in the grid overlay color picker for a grid container.
   *
   * @param  {NodeFront} node
   *         The NodeFront of the grid container element for which the grid color is
   *         being updated.
   * @param  {String} color
   *         A hex string representing the color to use.
   */
  async onSetGridOverlayColor(node, color) {
    this.store.dispatch(updateGridColor(node, color));
    let { grids } = this.store.getState();
    let currentUrl = this.inspector.target.url;
    // Get the hostname, if there is no hostname, fall back on protocol
    // ex: `data:` uri, and `about:` pages
    let hostname = parseURL(currentUrl).hostname || parseURL(currentUrl).protocol;
    let customGridColors = await asyncStorage.getItem("gridInspectorHostColors") || {};

    for (let grid of grids) {
      if (grid.nodeFront === node) {
        if (!customGridColors[hostname]) {
          customGridColors[hostname] = [];
        }
        // Update the custom color for the grid in this position.
        customGridColors[hostname][grid.id] = color;
        await asyncStorage.setItem("gridInspectorHostColors", customGridColors);

        // If the grid for which the color was updated currently has a highlighter, update
        // the color.
        if (grid.highlighted) {
          let highlighterSettings = this.getGridHighlighterSettings(node);
          this.showGridHighlighter(node, highlighterSettings);
        }
      }
    }
  }

  /**
   * Highlights the grid area in the CSS Grid Highlighter for the given grid.
   *
   * @param  {NodeFront} node
   *         The NodeFront of the grid container element for which the grid
   *         highlighter is highlighted for.
   * @param  {String} gridAreaName
   *         The name of the grid area for which the grid highlighter
   *         is highlighted for.
   * @param  {String} color
   *         The color of the grid area for which the grid highlighter
   *         is highlighted for.
   */
  onShowGridAreaHighlight(node, gridAreaName, color) {
    let { highlighterSettings } = this.store.getState();

    highlighterSettings.showGridArea = gridAreaName;
    highlighterSettings.color = color;

    this.showGridHighlighter(node, highlighterSettings);

    this.store.dispatch(updateGridHighlighted(node, true));
  }

  /**
   * Highlights the grid cell in the CSS Grid Highlighter for the given grid.
   *
   * @param  {NodeFront} node
   *         The NodeFront of the grid container element for which the grid
   *         highlighter is highlighted for.
   * @param  {String} color
   *         The color of the grid cell for which the grid highlighter
   *         is highlighted for.
   * @param  {Number|null} gridFragmentIndex
   *         The index of the grid fragment for which the grid highlighter
   *         is highlighted for.
   * @param  {Number|null} rowNumber
   *         The row number of the grid cell for which the grid highlighter
   *         is highlighted for.
   * @param  {Number|null} columnNumber
   *         The column number of the grid cell for which the grid highlighter
   *         is highlighted for.
   */
  onShowGridCellHighlight(node, color, gridFragmentIndex, rowNumber, columnNumber) {
    let { highlighterSettings } = this.store.getState();

    highlighterSettings.showGridCell = { gridFragmentIndex, rowNumber, columnNumber };
    highlighterSettings.color = color;

    this.showGridHighlighter(node, highlighterSettings);

    this.store.dispatch(updateGridHighlighted(node, true));
  }

  /**
   * Highlights the grid line in the CSS Grid Highlighter for the given grid.
   *
   * @param  {NodeFront} node
   *         The NodeFront of the grid container element for which the grid
   *         highlighter is highlighted for.
   * @param  {Number|null} gridFragmentIndex
   *         The index of the grid fragment for which the grid highlighter
   *         is highlighted for.
   * @param  {String} color
   *         The color of the grid line for which the grid highlighter
   *         is highlighted for.
   * @param  {Number|null} lineNumber
   *         The line number of the grid for which the grid highlighter
   *         is highlighted for.
   * @param  {String|null} type
   *         The type of line for which the grid line is being highlighted for.
   */
  onShowGridLineNamesHighlight(node, gridFragmentIndex, color, lineNumber, type) {
    let { highlighterSettings } = this.store.getState();

    highlighterSettings.showGridLineNames = {
      gridFragmentIndex,
      lineNumber,
      type
    };
    highlighterSettings.color = color;

    this.showGridHighlighter(node, highlighterSettings);

    this.store.dispatch(updateGridHighlighted(node, true));
  }

  /**
   * Handler for the inspector sidebar "select" event. Starts tracking reflows
   * if the layout panel is visible. Otherwise, stop tracking reflows.
   * Finally, refresh the layout view if it is visible.
   */
  onSidebarSelect() {
    if (!this.isPanelVisible()) {
      this.inspector.reflowTracker.untrackReflows(this, this.onReflow);
      return;
    }

    this.inspector.reflowTracker.trackReflows(this, this.onReflow);
    this.updateGridPanel();
  }

  /**
   * Handler for a change in the input checkboxes in the GridList component.
   * Toggles on/off the grid highlighter for the provided grid container element.
   *
   * @param  {NodeFront} node
   *         The NodeFront of the grid container element for which the grid
   *         highlighter is toggled on/off for.
   */
  onToggleGridHighlighter(node) {
    let highlighterSettings = this.getGridHighlighterSettings(node);
    this.toggleGridHighlighter(node, highlighterSettings);

    this.store.dispatch(updateGridHighlighted(node,
      node !== this.highlighters.gridHighlighterShown));
  }

  /**
    * Handler for a change in the show grid areas checkbox in the GridDisplaySettings
    * component. Toggles on/off the option to show the grid areas in the grid highlighter.
    * Refreshes the shown grid highlighter for the grids currently highlighted.
    *
    * @param  {Boolean} enabled
    *         Whether or not the grid highlighter should show the grid areas.
    */
  onToggleShowGridAreas(enabled) {
    this.store.dispatch(updateShowGridAreas(enabled));
    Services.prefs.setBoolPref(SHOW_GRID_AREAS, enabled);

    if (enabled) {
      this.telemetry.toolOpened("gridInspectorShowGridAreasOverlayChecked");
    }

    let { grids } = this.store.getState();

    for (let grid of grids) {
      if (grid.highlighted) {
        let highlighterSettings = this.getGridHighlighterSettings(grid.nodeFront);
        this.highlighters.showGridHighlighter(grid.nodeFront, highlighterSettings);
      }
    }
  }

  /**
   * Handler for a change in the show grid line numbers checkbox in the
   * GridDisplaySettings component. Toggles on/off the option to show the grid line
   * numbers in the grid highlighter. Refreshes the shown grid highlighter for the
   * grids currently highlighted.
   *
   * @param  {Boolean} enabled
   *         Whether or not the grid highlighter should show the grid line numbers.
   */
  onToggleShowGridLineNumbers(enabled) {
    this.store.dispatch(updateShowGridLineNumbers(enabled));
    Services.prefs.setBoolPref(SHOW_GRID_LINE_NUMBERS, enabled);

    if (enabled) {
      this.telemetry.toolOpened("gridInspectorShowGridLineNumbersChecked");
    }

    let { grids } = this.store.getState();

    for (let grid of grids) {
      if (grid.highlighted) {
        let highlighterSettings = this.getGridHighlighterSettings(grid.nodeFront);
        this.showGridHighlighter(grid.nodeFront, highlighterSettings);
      }
    }
  }

  /**
   * Handler for a change in the extend grid lines infinitely checkbox in the
   * GridDisplaySettings component. Toggles on/off the option to extend the grid
   * lines infinitely in the grid highlighter. Refreshes the shown grid highlighter
   * for grids currently highlighted.
   *
   * @param  {Boolean} enabled
   *         Whether or not the grid highlighter should extend grid lines infinitely.
   */
  onToggleShowInfiniteLines(enabled) {
    this.store.dispatch(updateShowInfiniteLines(enabled));
    Services.prefs.setBoolPref(SHOW_INFINITE_LINES_PREF, enabled);

    if (enabled) {
      this.telemetry.toolOpened("gridInspectorShowInfiniteLinesChecked");
    }

    let { grids } = this.store.getState();

    for (let grid of grids) {
      if (grid.highlighted) {
        let highlighterSettings = this.getGridHighlighterSettings(grid.nodeFront);
        this.showGridHighlighter(grid.nodeFront, highlighterSettings);
      }
    }
  }
}

module.exports = GridInspector;
PK
!<��7)JJIchrome/devtools/modules/devtools/client/inspector/grids/reducers/grids.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  UPDATE_GRID_COLOR,
  UPDATE_GRID_HIGHLIGHTED,
  UPDATE_GRIDS,
} = require("../actions/index");

const INITIAL_GRIDS = [];

let reducers = {

  [UPDATE_GRID_COLOR](grids, { nodeFront, color }) {
    let newGrids = grids.map(g => {
      if (g.nodeFront == nodeFront) {
        g = Object.assign({}, g, { color });
      }

      return g;
    });

    return newGrids;
  },

  [UPDATE_GRID_HIGHLIGHTED](grids, { nodeFront, highlighted }) {
    return grids.map(g => {
      let isUpdatedNode = g.nodeFront === nodeFront;

      return Object.assign({}, g, {
        highlighted: isUpdatedNode && highlighted
      });
    });
  },

  [UPDATE_GRIDS](_, { grids }) {
    return grids;
  },

};

module.exports = function (grids = INITIAL_GRIDS, action) {
  let reducer = reducers[action.type];
  if (!reducer) {
    return grids;
  }
  return reducer(grids, action);
};
PK
!<
�J���Xchrome/devtools/modules/devtools/client/inspector/grids/reducers/highlighter-settings.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  UPDATE_SHOW_GRID_AREAS,
  UPDATE_SHOW_GRID_LINE_NUMBERS,
  UPDATE_SHOW_INFINITE_LINES
} = require("../actions/index");

const INITIAL_HIGHLIGHTER_SETTINGS = {
  showGridAreasOverlay: false,
  showGridLineNumbers: false,
  showInfiniteLines: false,
};

let reducers = {

  [UPDATE_SHOW_GRID_AREAS](highlighterSettings, { enabled }) {
    return Object.assign({}, highlighterSettings, {
      showGridAreasOverlay: enabled,
    });
  },

  [UPDATE_SHOW_GRID_LINE_NUMBERS](highlighterSettings, { enabled }) {
    return Object.assign({}, highlighterSettings, {
      showGridLineNumbers: enabled,
    });
  },

  [UPDATE_SHOW_INFINITE_LINES](highlighterSettings, { enabled }) {
    return Object.assign({}, highlighterSettings, {
      showInfiniteLines: enabled,
    });
  },

};

module.exports = function (highlighterSettings = INITIAL_HIGHLIGHTER_SETTINGS, action) {
  let reducer = reducers[action.type];
  if (!reducer) {
    return highlighterSettings;
  }
  return reducer(highlighterSettings, action);
};
PK
!<R~P��@chrome/devtools/modules/devtools/client/inspector/grids/types.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/vendor/react-prop-types");

/**
 * A single grid container in the document.
 */
exports.grid = {
  // The id of the grid
  id: PropTypes.number,

  // The color for the grid overlay highlighter
  color: PropTypes.string,

  // The text direction of the grid container
  direction: PropTypes.string,

  // The grid fragment object of the grid container
  gridFragments: PropTypes.array,

  // Whether or not the grid highlighter is highlighting the grid
  highlighted: PropTypes.bool,

  // The node front of the grid container
  nodeFront: PropTypes.object,

  // The writing mode of the grid container
  writingMode: PropTypes.string,
};

/**
 * The grid highlighter settings on what to display in its grid overlay in the document.
 */
exports.highlighterSettings = {
  // Whether or not the grid highlighter should show the grid line numbers
  showGridLineNumbers: PropTypes.bool,

  // Whether or not the grid highlighter extends the grid lines infinitely
  showInfiniteLines: PropTypes.bool,
};
PK
!<��l�Fchrome/devtools/modules/devtools/client/inspector/grids/utils/utils.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

/**
 * Compares 2 sets of grid fragments to each other and checks if they have the same
 * general geometry.
 * This means that things like areas, area names or line names are ignored.
 * This only checks if the 2 sets of fragments have as many fragments, as many lines, and
 * that those lines are at the same distance.
 *
 * @param  {Array} fragments1
 *         A list of gridFragment objects.
 * @param  {Array} fragments2
 *         Another list of gridFragment objects to compare to the first list.
 * @return {Boolean}
 *         True if the fragments are the same, false otherwise.
 */
function compareFragmentsGeometry(fragments1, fragments2) {
  // Compare the number of fragments.
  if (fragments1.length !== fragments2.length) {
    return false;
  }

  // Compare the number of areas, rows and columns.
  for (let i = 0; i < fragments1.length; i++) {
    if (fragments1[i].cols.lines.length !== fragments2[i].cols.lines.length ||
        fragments1[i].rows.lines.length !== fragments2[i].rows.lines.length) {
      return false;
    }
  }

  // Compare the offset of lines.
  for (let i = 0; i < fragments1.length; i++) {
    for (let j = 0; j < fragments1[i].cols.lines.length; j++) {
      if (fragments1[i].cols.lines[j].start !== fragments2[i].cols.lines[j].start) {
        return false;
      }
    }
    for (let j = 0; j < fragments1[i].rows.lines.length; j++) {
      if (fragments1[i].rows.lines[j].start !== fragments2[i].rows.lines[j].start) {
        return false;
      }
    }
  }

  return true;
}

module.exports.compareFragmentsGeometry = compareFragmentsGeometry;
PK
!<�o��
�
Gchrome/devtools/modules/devtools/client/inspector/inspector-commands.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 l10n = require("gcli/l10n");
const {gDevTools} = require("devtools/client/framework/devtools");
/* eslint-disable mozilla/reject-some-requires */
const {HighlighterEnvironment} = require("devtools/server/actors/highlighters");
const {EyeDropper} = require("devtools/server/actors/highlighters/eye-dropper");
/* eslint-enable mozilla/reject-some-requires */
const Telemetry = require("devtools/client/shared/telemetry");

const windowEyeDroppers = new WeakMap();

exports.items = [{
  item: "command",
  runAt: "client",
  name: "inspect",
  description: l10n.lookup("inspectDesc"),
  manual: l10n.lookup("inspectManual"),
  params: [
    {
      name: "selector",
      type: "string",
      description: l10n.lookup("inspectNodeDesc"),
      manual: l10n.lookup("inspectNodeManual")
    }
  ],
  exec: function* (args, context) {
    let target = context.environment.target;
    let toolbox = yield gDevTools.showToolbox(target, "inspector");
    let walker = toolbox.getCurrentPanel().walker;
    let rootNode = yield walker.getRootNode();
    let nodeFront = yield walker.querySelector(rootNode, args.selector);
    toolbox.getCurrentPanel().selection.setNodeFront(nodeFront, "gcli");
  },
}, {
  item: "command",
  runAt: "client",
  name: "eyedropper",
  description: l10n.lookup("eyedropperDesc"),
  manual: l10n.lookup("eyedropperManual"),
  params: [{
    // This hidden parameter is only set to true when the eyedropper browser menu item is
    // used. It is useful to log a different telemetry event whether the tool was used
    // from the menu, or from the gcli command line.
    group: "hiddengroup",
    params: [{
      name: "frommenu",
      type: "boolean",
      hidden: true
    }, {
      name: "hide",
      type: "boolean",
      hidden: true
    }]
  }],
  exec: function* (args, context) {
    if (args.hide) {
      context.updateExec("eyedropper_server_hide").catch(console.error);
      return;
    }

    // If the inspector is already picking a color from the page, cancel it.
    let target = context.environment.target;
    let toolbox = gDevTools.getToolbox(target);
    if (toolbox) {
      let inspector = toolbox.getPanel("inspector");
      if (inspector) {
        yield inspector.hideEyeDropper();
      }
    }

    let telemetry = new Telemetry();
    telemetry.toolOpened(args.frommenu ? "menueyedropper" : "eyedropper");
    context.updateExec("eyedropper_server").catch(console.error);
  }
}, {
  item: "command",
  runAt: "server",
  name: "eyedropper_server",
  hidden: true,
  exec: function (args, {environment}) {
    let eyeDropper = windowEyeDroppers.get(environment.window);

    if (!eyeDropper) {
      let env = new HighlighterEnvironment();
      env.initFromWindow(environment.window);

      eyeDropper = new EyeDropper(env);
      eyeDropper.once("hidden", () => {
        eyeDropper.destroy();
        env.destroy();
        windowEyeDroppers.delete(environment.window);
      });

      windowEyeDroppers.set(environment.window, eyeDropper);
    }

    eyeDropper.show(environment.document.documentElement, {copyOnSelect: true});
  }
}, {
  item: "command",
  runAt: "server",
  name: "eyedropper_server_hide",
  hidden: true,
  exec: function (args, {environment}) {
    let eyeDropper = windowEyeDroppers.get(environment.window);
    if (eyeDropper) {
      eyeDropper.hide();
    }
  }
}];
PK
!<�T��F�FEchrome/devtools/modules/devtools/client/inspector/inspector-search.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 promise = require("promise");
const {Task} = require("devtools/shared/task");
const {KeyCodes} = require("devtools/client/shared/keycodes");

const EventEmitter = require("devtools/shared/old-event-emitter");
const AutocompletePopup = require("devtools/client/shared/autocomplete-popup");
const Services = require("Services");

// Maximum number of selector suggestions shown in the panel.
const MAX_SUGGESTIONS = 15;

/**
 * Converts any input field into a document search box.
 *
 * @param {InspectorPanel} inspector
 *        The InspectorPanel whose `walker` attribute should be used for
 *        document traversal.
 * @param {DOMNode} input
 *        The input element to which the panel will be attached and from where
 *        search input will be taken.
 * @param {DOMNode} clearBtn
 *        The clear button in the input field that will clear the input value.
 *
 * Emits the following events:
 * - search-cleared: when the search box is emptied
 * - search-result: when a search is made and a result is selected
 */
function InspectorSearch(inspector, input, clearBtn) {
  this.inspector = inspector;
  this.searchBox = input;
  this.searchClearButton = clearBtn;
  this._lastSearched = null;

  this.searchClearButton.hidden = true;

  this._onKeyDown = this._onKeyDown.bind(this);
  this._onInput = this._onInput.bind(this);
  this._onClearSearch = this._onClearSearch.bind(this);
  this.searchBox.addEventListener("keydown", this._onKeyDown, true);
  this.searchBox.addEventListener("input", this._onInput, true);
  this.searchClearButton.addEventListener("click", this._onClearSearch);

  // For testing, we need to be able to wait for the most recent node request
  // to finish.  Tests can watch this promise for that.
  this._lastQuery = promise.resolve(null);

  this.autocompleter = new SelectorAutocompleter(inspector, input);
  EventEmitter.decorate(this);
}

exports.InspectorSearch = InspectorSearch;

InspectorSearch.prototype = {
  get walker() {
    return this.inspector.walker;
  },

  destroy: function () {
    this.searchBox.removeEventListener("keydown", this._onKeyDown, true);
    this.searchBox.removeEventListener("input", this._onInput, true);
    this.searchClearButton.removeEventListener("click", this._onClearSearch);
    this.searchBox = null;
    this.searchClearButton = null;
    this.autocompleter.destroy();
  },

  _onSearch: function (reverse = false) {
    this.doFullTextSearch(this.searchBox.value, reverse)
        .catch(console.error);
  },

  doFullTextSearch: Task.async(function* (query, reverse) {
    let lastSearched = this._lastSearched;
    this._lastSearched = query;

    if (query.length === 0) {
      this.searchBox.classList.remove("devtools-style-searchbox-no-match");
      if (!lastSearched || lastSearched.length > 0) {
        this.emit("search-cleared");
      }
      return;
    }

    let res = yield this.walker.search(query, { reverse });

    // Value has changed since we started this request, we're done.
    if (query !== this.searchBox.value) {
      return;
    }

    if (res) {
      this.inspector.selection.setNodeFront(res.node, "inspectorsearch");
      this.searchBox.classList.remove("devtools-style-searchbox-no-match");

      res.query = query;
      this.emit("search-result", res);
    } else {
      this.searchBox.classList.add("devtools-style-searchbox-no-match");
      this.emit("search-result");
    }
  }),

  _onInput: function () {
    if (this.searchBox.value.length === 0) {
      this.searchClearButton.hidden = true;
      this._onSearch();
    } else {
      this.searchClearButton.hidden = false;
    }
  },

  _onKeyDown: function (event) {
    if (event.keyCode === KeyCodes.DOM_VK_RETURN) {
      this._onSearch(event.shiftKey);
    }

    const modifierKey = Services.appinfo.OS === "Darwin"
                        ? event.metaKey : event.ctrlKey;
    if (event.keyCode === KeyCodes.DOM_VK_G && modifierKey) {
      this._onSearch(event.shiftKey);
      event.preventDefault();
    }
  },

  _onClearSearch: function () {
    this.searchBox.classList.remove("devtools-style-searchbox-no-match");
    this.searchBox.value = "";
    this.searchClearButton.hidden = true;
    this.emit("search-cleared");
  }
};

/**
 * Converts any input box on a page to a CSS selector search and suggestion box.
 *
 * Emits 'processing-done' event when it is done processing the current
 * keypress, search request or selection from the list, whether that led to a
 * search or not.
 *
 * @constructor
 * @param InspectorPanel inspector
 *        The InspectorPanel whose `walker` attribute should be used for
 *        document traversal.
 * @param nsiInputElement inputNode
 *        The input element to which the panel will be attached and from where
 *        search input will be taken.
 */
function SelectorAutocompleter(inspector, inputNode) {
  this.inspector = inspector;
  this.searchBox = inputNode;
  this.panelDoc = this.searchBox.ownerDocument;

  this.showSuggestions = this.showSuggestions.bind(this);
  this._onSearchKeypress = this._onSearchKeypress.bind(this);
  this._onSearchPopupClick = this._onSearchPopupClick.bind(this);
  this._onMarkupMutation = this._onMarkupMutation.bind(this);

  // Options for the AutocompletePopup.
  let options = {
    listId: "searchbox-panel-listbox",
    autoSelect: true,
    position: "top",
    theme: "auto",
    onClick: this._onSearchPopupClick,
  };

  // The popup will be attached to the toolbox document.
  this.searchPopup = new AutocompletePopup(inspector._toolbox.doc, options);

  this.searchBox.addEventListener("input", this.showSuggestions, true);
  this.searchBox.addEventListener("keypress", this._onSearchKeypress, true);
  this.inspector.on("markupmutation", this._onMarkupMutation);

  // For testing, we need to be able to wait for the most recent node request
  // to finish.  Tests can watch this promise for that.
  this._lastQuery = promise.resolve(null);
  EventEmitter.decorate(this);
}

exports.SelectorAutocompleter = SelectorAutocompleter;

SelectorAutocompleter.prototype = {
  get walker() {
    return this.inspector.walker;
  },

  // The possible states of the query.
  States: {
    CLASS: "class",
    ID: "id",
    TAG: "tag",
    ATTRIBUTE: "attribute",
  },

  // The current state of the query.
  _state: null,

  // The query corresponding to last state computation.
  _lastStateCheckAt: null,

  /**
   * Computes the state of the query. State refers to whether the query
   * currently requires a class suggestion, or a tag, or an Id suggestion.
   * This getter will effectively compute the state by traversing the query
   * character by character each time the query changes.
   *
   * @example
   *        '#f' requires an Id suggestion, so the state is States.ID
   *        'div > .foo' requires class suggestion, so state is States.CLASS
   */
  get state() {
    if (!this.searchBox || !this.searchBox.value) {
      return null;
    }

    let query = this.searchBox.value;
    if (this._lastStateCheckAt == query) {
      // If query is the same, return early.
      return this._state;
    }
    this._lastStateCheckAt = query;

    this._state = null;
    let subQuery = "";
    // Now we iterate over the query and decide the state character by
    // character.
    // The logic here is that while iterating, the state can go from one to
    // another with some restrictions. Like, if the state is Class, then it can
    // never go to Tag state without a space or '>' character; Or like, a Class
    // state with only '.' cannot go to an Id state without any [a-zA-Z] after
    // the '.' which means that '.#' is a selector matching a class name '#'.
    // Similarily for '#.' which means a selctor matching an id '.'.
    for (let i = 1; i <= query.length; i++) {
      // Calculate the state.
      subQuery = query.slice(0, i);
      let [secondLastChar, lastChar] = subQuery.slice(-2);
      switch (this._state) {
        case null:
          // This will happen only in the first iteration of the for loop.
          lastChar = secondLastChar;

        case this.States.TAG: // eslint-disable-line
          if (lastChar === ".") {
            this._state = this.States.CLASS;
          } else if (lastChar === "#") {
            this._state = this.States.ID;
          } else if (lastChar === "[") {
            this._state = this.States.ATTRIBUTE;
          } else {
            this._state = this.States.TAG;
          }
          break;

        case this.States.CLASS:
          if (subQuery.match(/[\.]+[^\.]*$/)[0].length > 2) {
            // Checks whether the subQuery has atleast one [a-zA-Z] after the
            // '.'.
            if (lastChar === " " || lastChar === ">") {
              this._state = this.States.TAG;
            } else if (lastChar === "#") {
              this._state = this.States.ID;
            } else if (lastChar === "[") {
              this._state = this.States.ATTRIBUTE;
            } else {
              this._state = this.States.CLASS;
            }
          }
          break;

        case this.States.ID:
          if (subQuery.match(/[#]+[^#]*$/)[0].length > 2) {
            // Checks whether the subQuery has atleast one [a-zA-Z] after the
            // '#'.
            if (lastChar === " " || lastChar === ">") {
              this._state = this.States.TAG;
            } else if (lastChar === ".") {
              this._state = this.States.CLASS;
            } else if (lastChar === "[") {
              this._state = this.States.ATTRIBUTE;
            } else {
              this._state = this.States.ID;
            }
          }
          break;

        case this.States.ATTRIBUTE:
          if (subQuery.match(/[\[][^\]]+[\]]/) !== null) {
            // Checks whether the subQuery has at least one ']' after the '['.
            if (lastChar === " " || lastChar === ">") {
              this._state = this.States.TAG;
            } else if (lastChar === ".") {
              this._state = this.States.CLASS;
            } else if (lastChar === "#") {
              this._state = this.States.ID;
            } else {
              this._state = this.States.ATTRIBUTE;
            }
          }
          break;
      }
    }
    return this._state;
  },

  /**
   * Removes event listeners and cleans up references.
   */
  destroy: function () {
    this.searchBox.removeEventListener("input", this.showSuggestions, true);
    this.searchBox.removeEventListener("keypress",
      this._onSearchKeypress, true);
    this.inspector.off("markupmutation", this._onMarkupMutation);
    this.searchPopup.destroy();
    this.searchPopup = null;
    this.searchBox = null;
    this.panelDoc = null;
  },

  /**
   * Handles keypresses inside the input box.
   */
  _onSearchKeypress: function (event) {
    let popup = this.searchPopup;
    switch (event.keyCode) {
      case KeyCodes.DOM_VK_RETURN:
      case KeyCodes.DOM_VK_TAB:
        if (popup.isOpen) {
          if (popup.selectedItem) {
            this.searchBox.value = popup.selectedItem.label;
          }
          this.hidePopup();
        } else if (!popup.isOpen) {
          // When tab is pressed with focus on searchbox and closed popup,
          // do not prevent the default to avoid a keyboard trap and move focus
          // to next/previous element.
          this.emit("processing-done");
          return;
        }
        break;

      case KeyCodes.DOM_VK_UP:
        if (popup.isOpen && popup.itemCount > 0) {
          if (popup.selectedIndex === 0) {
            popup.selectedIndex = popup.itemCount - 1;
          } else {
            popup.selectedIndex--;
          }
          this.searchBox.value = popup.selectedItem.label;
        }
        break;

      case KeyCodes.DOM_VK_DOWN:
        if (popup.isOpen && popup.itemCount > 0) {
          if (popup.selectedIndex === popup.itemCount - 1) {
            popup.selectedIndex = 0;
          } else {
            popup.selectedIndex++;
          }
          this.searchBox.value = popup.selectedItem.label;
        }
        break;

      case KeyCodes.DOM_VK_ESCAPE:
        if (popup.isOpen) {
          this.hidePopup();
        } else {
          this.emit("processing-done");
          return;
        }
        break;

      default:
        return;
    }

    event.preventDefault();
    event.stopPropagation();
    this.emit("processing-done");
  },

  /**
   * Handles click events from the autocomplete popup.
   */
  _onSearchPopupClick: function (event) {
    let selectedItem = this.searchPopup.selectedItem;
    if (selectedItem) {
      this.searchBox.value = selectedItem.label;
    }
    this.hidePopup();

    event.preventDefault();
    event.stopPropagation();
  },

  /**
   * Reset previous search results on markup-mutations to make sure we search
   * again after nodes have been added/removed/changed.
   */
  _onMarkupMutation: function () {
    this._searchResults = null;
    this._lastSearched = null;
  },

  /**
   * Populates the suggestions list and show the suggestion popup.
   *
   * @return {Promise} promise that will resolve when the autocomplete popup is fully
   * displayed or hidden.
   */
  _showPopup: function (list, firstPart, popupState) {
    let total = 0;
    let query = this.searchBox.value;
    let items = [];

    for (let [value, , state] of list) {
      if (query.match(/[\s>+]$/)) {
        // for cases like 'div ' or 'div >' or 'div+'
        value = query + value;
      } else if (query.match(/[\s>+][\.#a-zA-Z][^\s>+\.#\[]*$/)) {
        // for cases like 'div #a' or 'div .a' or 'div > d' and likewise
        let lastPart = query.match(/[\s>+][\.#a-zA-Z][^\s>+\.#\[]*$/)[0];
        value = query.slice(0, -1 * lastPart.length + 1) + value;
      } else if (query.match(/[a-zA-Z][#\.][^#\.\s+>]*$/)) {
        // for cases like 'div.class' or '#foo.bar' and likewise
        let lastPart = query.match(/[a-zA-Z][#\.][^#\.\s+>]*$/)[0];
        value = query.slice(0, -1 * lastPart.length + 1) + value;
      } else if (query.match(/[a-zA-Z]*\[[^\]]*\][^\]]*/)) {
        // for cases like '[foo].bar' and likewise
        let attrPart = query.substring(0, query.lastIndexOf("]") + 1);
        value = attrPart + value;
      }

      let item = {
        preLabel: query,
        label: value
      };

      // In case the query's state is tag and the item's state is id or class
      // adjust the preLabel
      if (popupState === this.States.TAG && state === this.States.CLASS) {
        item.preLabel = "." + item.preLabel;
      }
      if (popupState === this.States.TAG && state === this.States.ID) {
        item.preLabel = "#" + item.preLabel;
      }

      items.unshift(item);
      if (++total > MAX_SUGGESTIONS - 1) {
        break;
      }
    }

    if (total > 0) {
      let onPopupOpened = this.searchPopup.once("popup-opened");
      this.searchPopup.once("popup-closed", () => {
        this.searchPopup.setItems(items);
        this.searchPopup.openPopup(this.searchBox);
      });
      this.searchPopup.hidePopup();
      return onPopupOpened;
    }

    return this.hidePopup();
  },

  /**
   * Hide the suggestion popup if necessary.
   */
  hidePopup: function () {
    let onPopupClosed = this.searchPopup.once("popup-closed");
    this.searchPopup.hidePopup();
    return onPopupClosed;
  },

  /**
   * Suggests classes,ids and tags based on the user input as user types in the
   * searchbox.
   */
  showSuggestions: function () {
    let query = this.searchBox.value;
    let state = this.state;
    let firstPart = "";

    if (query.endsWith("*") || state === this.States.ATTRIBUTE) {
      // Hide the popup if the query ends with * (because we don't want to
      // suggest all nodes) or if it is an attribute selector (because
      // it would give a lot of useless results).
      this.hidePopup();
      return;
    }

    if (state === this.States.TAG) {
      // gets the tag that is being completed. For ex. 'div.foo > s' returns
      // 's', 'di' returns 'di' and likewise.
      firstPart = (query.match(/[\s>+]?([a-zA-Z]*)$/) || ["", query])[1];
      query = query.slice(0, query.length - firstPart.length);
    } else if (state === this.States.CLASS) {
      // gets the class that is being completed. For ex. '.foo.b' returns 'b'
      firstPart = query.match(/\.([^\.]*)$/)[1];
      query = query.slice(0, query.length - firstPart.length - 1);
    } else if (state === this.States.ID) {
      // gets the id that is being completed. For ex. '.foo#b' returns 'b'
      firstPart = query.match(/#([^#]*)$/)[1];
      query = query.slice(0, query.length - firstPart.length - 1);
    }
    // TODO: implement some caching so that over the wire request is not made
    // everytime.
    if (/[\s+>~]$/.test(query)) {
      query += "*";
    }

    let suggestionsPromise = this.walker.getSuggestionsForQuery(
      query, firstPart, state);
    this._lastQuery = suggestionsPromise.then(result => {
      this.emit("processing-done");
      if (result.query !== query) {
        // This means that this response is for a previous request and the user
        // as since typed something extra leading to a new request.
        return promise.resolve(null);
      }

      if (state === this.States.CLASS) {
        firstPart = "." + firstPart;
      } else if (state === this.States.ID) {
        firstPart = "#" + firstPart;
      }

      // If there is a single tag match and it's what the user typed, then
      // don't need to show a popup.
      if (result.suggestions.length === 1 &&
          result.suggestions[0][0] === firstPart) {
        result.suggestions = [];
      }

      // Wait for the autocomplete-popup to fire its popup-opened event, to make sure
      // the autoSelect item has been selected.
      return this._showPopup(result.suggestions, firstPart, state);
    });
  }
};
PK
!<:x����Qchrome/devtools/modules/devtools/client/inspector/layout/components/Accordion.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 file is a duplicate from the debugger.html project and has diverged in terms of
 * some of styles that are kept. Any changes to the styles in this file should be synced
 * back to the appropriate styles in the debugger.html project.
 * Create a pull request at https://github.com/devtools-html/debugger.html to sync
 * any changes to the existing styles.
 */

:root {
  --accordion-header-background: var(--theme-toolbar-background);
}

:root.theme-dark {
  --accordion-header-background: #222225;
}

.accordion {
  background-color: var(--theme-sidebar-background);
  width: 100%;
}

.accordion ._header {
  background-color: var(--accordion-header-background);
  border-bottom: 1px solid var(--theme-splitter-color);
  color: var(--theme-toolbar-color);
  cursor: pointer;
  font-size: 12px;
  padding: 4px;
  transition: all 0.25s ease;
  width: 100%;
  align-items: center;
  display: flex;

  -moz-user-select: none;
}

.accordion ._header:hover {
  background-color: var(--theme-toolbar-hover);
}

.accordion ._header:hover svg {
  fill: var(--theme-comment-alt);
}

.accordion ._content {
  border-bottom: 1px solid var(--theme-splitter-color);
  font-size: 11px;
}

.accordion div:last-child ._content {
  border-bottom: none;
}

.arrow {
  vertical-align: middle;
  display: inline-block;
}
PK
!<Tw�Yo	o	Pchrome/devtools/modules/devtools/client/inspector/layout/components/Accordion.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 file should not be modified and is a duplicate from the debugger.html project.
 * Any changes to this file should be imported from the upstream debugger.html project.
 */

"use strict";

const { createElement, PureComponent } = 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");

const { div, span } = dom;

class Accordion extends PureComponent {
  static get propTypes() {
    return {
      items: PropTypes.array
    };
  }

  constructor(props) {
    super(props);

    this.state = {
      opened: props.items.map(item => item.opened),
      created: []
    };

    this.handleHeaderClick = this.handleHeaderClick.bind(this);
    this.renderContainer = this.renderContainer.bind(this);
  }

  handleHeaderClick(i, event) {
    const opened = [...this.state.opened];
    const created = [...this.state.created];
    const item = this.props.items[i];

    event.stopPropagation();

    opened[i] = !opened[i];
    created[i] = true;

    if (opened[i] && item.onOpened) {
      item.onOpened();
    }

    if (item.onToggled) {
      item.onToggled();
    }

    this.setState({ opened, created });
  }

  renderContainer(item, i) {
    const { opened, created } = this.state;
    const containerClassName =
          item.header.toLowerCase().replace(/\s/g, "-") + "-pane";
    let arrowClassName = "arrow theme-twisty";
    if (opened[i]) {
      arrowClassName += " open";
    }

    return div(
      { className: containerClassName, key: i },

      div(
        { className: "_header",
          onClick: event => this.handleHeaderClick(i, event) },
        span({ className: arrowClassName }),
        item.header
      ),

      (created[i] || opened[i]) ?
        div(
          { className: "_content",
            style: { display: opened[i] ? "block" : "none" }
          },
          createElement(item.component, item.componentProps || {})
        ) :
        null
    );
  }

  render() {
    return div(
      { className: "accordion" },
      this.props.items.map(this.renderContainer)
    );
  }
}

module.exports = Accordion;
PK
!<��*��Pchrome/devtools/modules/devtools/client/inspector/layout/components/LayoutApp.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Services = require("Services");
const { createFactory, PureComponent } = 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");
const { connect } = require("devtools/client/shared/vendor/react-redux");
const { LocalizationHelper } = require("devtools/shared/l10n");

const BoxModel = createFactory(require("devtools/client/inspector/boxmodel/components/BoxModel"));
const Flexbox = createFactory(require("devtools/client/inspector/flexbox/components/Flexbox"));
const Grid = createFactory(require("devtools/client/inspector/grids/components/Grid"));

const BoxModelTypes = require("devtools/client/inspector/boxmodel/types");
const GridTypes = require("devtools/client/inspector/grids/types");

const Accordion = createFactory(require("./Accordion"));

const BOXMODEL_STRINGS_URI = "devtools/client/locales/boxmodel.properties";
const BOXMODEL_L10N = new LocalizationHelper(BOXMODEL_STRINGS_URI);

const LAYOUT_STRINGS_URI = "devtools/client/locales/layout.properties";
const LAYOUT_L10N = new LocalizationHelper(LAYOUT_STRINGS_URI);

const FLEXBOX_ENABLED_PREF = "devtools.flexboxinspector.enabled";

const BOXMODEL_OPENED_PREF = "devtools.layout.boxmodel.opened";
const FLEXBOX_OPENED_PREF = "devtools.layout.flexbox.opened";
const GRID_OPENED_PREF = "devtools.layout.grid.opened";

class LayoutApp extends PureComponent {
  static get propTypes() {
    return {
      boxModel: PropTypes.shape(BoxModelTypes.boxModel).isRequired,
      getSwatchColorPickerTooltip: PropTypes.func.isRequired,
      grids: PropTypes.arrayOf(PropTypes.shape(GridTypes.grid)).isRequired,
      highlighterSettings: PropTypes.shape(GridTypes.highlighterSettings).isRequired,
      setSelectedNode: PropTypes.func.isRequired,
      showBoxModelProperties: PropTypes.bool.isRequired,
      onHideBoxModelHighlighter: PropTypes.func.isRequired,
      onSetGridOverlayColor: PropTypes.func.isRequired,
      onShowBoxModelEditor: PropTypes.func.isRequired,
      onShowBoxModelHighlighter: PropTypes.func.isRequired,
      onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
      onToggleGridHighlighter: PropTypes.func.isRequired,
      onToggleShowGridLineNumbers: PropTypes.func.isRequired,
      onToggleShowInfiniteLines: PropTypes.func.isRequired,
    };
  }

  render() {
    let items = [
      {
        component: Grid,
        componentProps: this.props,
        header: LAYOUT_L10N.getStr("layout.header"),
        opened: Services.prefs.getBoolPref(GRID_OPENED_PREF),
        onToggled: () => {
          let opened = Services.prefs.getBoolPref(GRID_OPENED_PREF);
          Services.prefs.setBoolPref(GRID_OPENED_PREF, !opened);
        }
      },
      {
        component: BoxModel,
        componentProps: this.props,
        header: BOXMODEL_L10N.getStr("boxmodel.title"),
        opened: Services.prefs.getBoolPref(BOXMODEL_OPENED_PREF),
        onToggled: () => {
          let opened = Services.prefs.getBoolPref(BOXMODEL_OPENED_PREF);
          Services.prefs.setBoolPref(BOXMODEL_OPENED_PREF, !opened);
        }
      },
    ];

    if (Services.prefs.getBoolPref(FLEXBOX_ENABLED_PREF)) {
      items = [
        {
          component: Flexbox,
          componentProps: this.props,
          header: LAYOUT_L10N.getStr("flexbox.header"),
          opened: Services.prefs.getBoolPref(FLEXBOX_OPENED_PREF),
          onToggled: () => {
            let opened =  Services.prefs.getBoolPref(FLEXBOX_OPENED_PREF);
            Services.prefs.setBoolPref(FLEXBOX_OPENED_PREF, !opened);
          }
        },
        ...items
      ];
    }

    return dom.div(
      { id: "layout-container" },
      Accordion({ items })
    );
  }
}

module.exports = connect(state => state)(LayoutApp);
PK
!<��4�	
	
Bchrome/devtools/modules/devtools/client/inspector/layout/layout.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/vendor/react");
const { Provider } = require("devtools/client/shared/vendor/react-redux");

const LayoutApp = createFactory(require("./components/LayoutApp"));

const { LocalizationHelper } = require("devtools/shared/l10n");
const INSPECTOR_L10N =
  new LocalizationHelper("devtools/client/locales/inspector.properties");

loader.lazyRequireGetter(this, "FlexboxInspector", "devtools/client/inspector/flexbox/flexbox");
loader.lazyRequireGetter(this, "GridInspector", "devtools/client/inspector/grids/grid-inspector");

class LayoutView {
  constructor(inspector, window) {
    this.document = window.document;
    this.inspector = inspector;
    this.store = inspector.store;

    this.init();
  }

  init() {
    if (!this.inspector) {
      return;
    }

    let {
      setSelectedNode,
      onShowBoxModelHighlighterForNode,
    } = this.inspector.getCommonComponentProps();

    let {
      onHideBoxModelHighlighter,
      onShowBoxModelEditor,
      onShowBoxModelHighlighter,
      onToggleGeometryEditor,
    } = this.inspector.getPanel("boxmodel").getComponentProps();

    this.flexboxInspector = new FlexboxInspector(this.inspector,
      this.inspector.panelWin);
    let {
      onToggleFlexboxHighlighter,
    } = this.flexboxInspector.getComponentProps();

    this.gridInspector = new GridInspector(this.inspector, this.inspector.panelWin);
    let {
      getSwatchColorPickerTooltip,
      onSetGridOverlayColor,
      onShowGridAreaHighlight,
      onShowGridCellHighlight,
      onShowGridLineNamesHighlight,
      onToggleGridHighlighter,
      onToggleShowGridAreas,
      onToggleShowGridLineNumbers,
      onToggleShowInfiniteLines,
    } = this.gridInspector.getComponentProps();

    let layoutApp = LayoutApp({
      getSwatchColorPickerTooltip,
      setSelectedNode,
      /**
       * Shows the box model properties under the box model if true, otherwise, hidden by
       * default.
       */
      showBoxModelProperties: true,
      onHideBoxModelHighlighter,
      onSetGridOverlayColor,
      onShowBoxModelEditor,
      onShowBoxModelHighlighter,
      onShowBoxModelHighlighterForNode,
      onShowGridAreaHighlight,
      onShowGridCellHighlight,
      onShowGridLineNamesHighlight,
      onToggleFlexboxHighlighter,
      onToggleGeometryEditor,
      onToggleGridHighlighter,
      onToggleShowGridAreas,
      onToggleShowGridLineNumbers,
      onToggleShowInfiniteLines,
    });

    let provider = createElement(Provider, {
      id: "layoutview",
      key: "layoutview",
      store: this.store,
      title: INSPECTOR_L10N.getStr("inspector.sidebar.layoutViewTitle2"),
    }, layoutApp);

    // Expose the provider to let inspector.js use it in setupSidebar.
    this.provider = provider;
  }

  /**
   * Destruction function called when the inspector is destroyed. Cleans up references.
   */
  destroy() {
    this.flexboxInspector.destroy();
    this.gridInspector.destroy();

    this.document = null;
    this.inspector = null;
    this.store = null;
  }
}

module.exports = LayoutView;
PK
!<�\�9��Fchrome/devtools/modules/devtools/client/inspector/layout/utils/l10n.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/shared/l10n");
const L10N = new LocalizationHelper("devtools/client/locales/layout.properties");

module.exports = {
  getStr: (...args) => L10N.getStr(...args),
  getFormatStr: (...args) => L10N.getFormatStr(...args),
  getFormatStrWithNumbers: (...args) => L10N.getFormatStrWithNumbers(...args),
  numberWithDecimals: (...args) => L10N.numberWithDecimals(...args),
};
PK
!<�&����Bchrome/devtools/modules/devtools/client/inspector/markup/markup.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 promise = require("promise");
const Services = require("Services");
const {Task} = require("devtools/shared/task");
const nodeConstants = require("devtools/shared/dom-node-constants");
const nodeFilterConstants = require("devtools/shared/dom-node-filter-constants");
const EventEmitter = require("devtools/shared/old-event-emitter");
const {LocalizationHelper} = require("devtools/shared/l10n");
const {PluralForm} = require("devtools/shared/plural-form");
const AutocompletePopup = require("devtools/client/shared/autocomplete-popup");
const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
const {scrollIntoViewIfNeeded} = require("devtools/client/shared/scroll");
const {UndoStack} = require("devtools/client/shared/undo");
const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
const {PrefObserver} = require("devtools/client/shared/prefs");
const MarkupElementContainer = require("devtools/client/inspector/markup/views/element-container");
const MarkupReadOnlyContainer = require("devtools/client/inspector/markup/views/read-only-container");
const MarkupTextContainer = require("devtools/client/inspector/markup/views/text-container");
const RootContainer = require("devtools/client/inspector/markup/views/root-container");

const INSPECTOR_L10N =
      new LocalizationHelper("devtools/client/locales/inspector.properties");

// Page size for pageup/pagedown
const PAGE_SIZE = 10;
const DEFAULT_MAX_CHILDREN = 100;
const NEW_SELECTION_HIGHLIGHTER_TIMER = 1000;
const DRAG_DROP_AUTOSCROLL_EDGE_MAX_DISTANCE = 50;
const DRAG_DROP_AUTOSCROLL_EDGE_RATIO = 0.1;
const DRAG_DROP_MIN_AUTOSCROLL_SPEED = 2;
const DRAG_DROP_MAX_AUTOSCROLL_SPEED = 8;
const DRAG_DROP_HEIGHT_TO_SPEED = 500;
const DRAG_DROP_HEIGHT_TO_SPEED_MIN = 0.5;
const DRAG_DROP_HEIGHT_TO_SPEED_MAX = 1;
const ATTR_COLLAPSE_ENABLED_PREF = "devtools.markup.collapseAttributes";
const ATTR_COLLAPSE_LENGTH_PREF = "devtools.markup.collapseAttributeLength";

/**
 * Vocabulary for the purposes of this file:
 *
 * MarkupContainer - the structure that holds an editor and its
 *  immediate children in the markup panel.
 *  - MarkupElementContainer: markup container for element nodes
 *  - MarkupTextContainer: markup container for text / comment nodes
 *  - MarkupReadonlyContainer: markup container for other nodes
 * Node - A content node.
 * object.elt - A UI element in the markup panel.
 */

/**
 * The markup tree.  Manages the mapping of nodes to MarkupContainers,
 * updating based on mutations, and the undo/redo bindings.
 *
 * @param  {Inspector} inspector
 *         The inspector we're watching.
 * @param  {iframe} frame
 *         An iframe in which the caller has kindly loaded markup.xhtml.
 */
function MarkupView(inspector, frame, controllerWindow) {
  this.inspector = inspector;
  this.walker = this.inspector.walker;
  this._frame = frame;
  this.win = this._frame.contentWindow;
  this.doc = this._frame.contentDocument;
  this._elt = this.doc.querySelector("#root");

  this.maxChildren = Services.prefs.getIntPref("devtools.markup.pagesize",
                                               DEFAULT_MAX_CHILDREN);

  this.collapseAttributes =
    Services.prefs.getBoolPref(ATTR_COLLAPSE_ENABLED_PREF);
  this.collapseAttributeLength =
    Services.prefs.getIntPref(ATTR_COLLAPSE_LENGTH_PREF);

  // Creating the popup to be used to show CSS suggestions.
  // The popup will be attached to the toolbox document.
  this.popup = new AutocompletePopup(inspector.toolbox.doc, {
    autoSelect: true,
    theme: "auto",
  });

  this.undo = new UndoStack();
  this.undo.installController(controllerWindow);

  this._containers = new Map();

  // Binding functions that need to be called in scope.
  this._handleRejectionIfNotDestroyed = this._handleRejectionIfNotDestroyed.bind(this);
  this._mutationObserver = this._mutationObserver.bind(this);
  this._onDisplayChange = this._onDisplayChange.bind(this);
  this._onMouseClick = this._onMouseClick.bind(this);
  this._onMouseUp = this._onMouseUp.bind(this);
  this._onNewSelection = this._onNewSelection.bind(this);
  this._onCopy = this._onCopy.bind(this);
  this._onFocus = this._onFocus.bind(this);
  this._onMouseMove = this._onMouseMove.bind(this);
  this._onMouseOut = this._onMouseOut.bind(this);
  this._onToolboxPickerCanceled = this._onToolboxPickerCanceled.bind(this);
  this._onToolboxPickerHover = this._onToolboxPickerHover.bind(this);
  this._onCollapseAttributesPrefChange =
    this._onCollapseAttributesPrefChange.bind(this);
  this._isImagePreviewTarget = this._isImagePreviewTarget.bind(this);
  this._onBlur = this._onBlur.bind(this);

  EventEmitter.decorate(this);

  // Listening to various events.
  this._elt.addEventListener("click", this._onMouseClick);
  this._elt.addEventListener("mousemove", this._onMouseMove);
  this._elt.addEventListener("mouseout", this._onMouseOut);
  this._elt.addEventListener("blur", this._onBlur, true);
  this.win.addEventListener("mouseup", this._onMouseUp);
  this.win.addEventListener("copy", this._onCopy);
  this._frame.addEventListener("focus", this._onFocus);
  this.walker.on("mutations", this._mutationObserver);
  this.walker.on("display-change", this._onDisplayChange);
  this.inspector.selection.on("new-node-front", this._onNewSelection);
  this.toolbox.on("picker-canceled", this._onToolboxPickerCanceled);
  this.toolbox.on("picker-node-hovered", this._onToolboxPickerHover);

  this._onNewSelection();
  this._initTooltips();

  this._prefObserver = new PrefObserver("devtools.markup");
  this._prefObserver.on(ATTR_COLLAPSE_ENABLED_PREF,
                        this._onCollapseAttributesPrefChange);
  this._prefObserver.on(ATTR_COLLAPSE_LENGTH_PREF,
                        this._onCollapseAttributesPrefChange);

  this._initShortcuts();
}

MarkupView.prototype = {
  /**
   * How long does a node flash when it mutates (in ms).
   */
  CONTAINER_FLASHING_DURATION: 500,

  _selectedContainer: null,

  get toolbox() {
    return this.inspector.toolbox;
  },

  /**
   * Handle promise rejections for various asynchronous actions, and only log errors if
   * the markup view still exists.
   * This is useful to silence useless errors that happen when the markup view is
   * destroyed while still initializing (and making protocol requests).
   */
  _handleRejectionIfNotDestroyed: function (e) {
    if (!this._destroyer) {
      console.error(e);
    }
  },

  _initTooltips: function () {
    // The tooltips will be attached to the toolbox document.
    this.eventDetailsTooltip = new HTMLTooltip(this.toolbox.doc, {
      type: "arrow",
      consumeOutsideClicks: false,
    });
    this.imagePreviewTooltip = new HTMLTooltip(this.toolbox.doc, {
      type: "arrow",
      useXulWrapper: true,
    });
    this._enableImagePreviewTooltip();
  },

  _enableImagePreviewTooltip: function () {
    this.imagePreviewTooltip.startTogglingOnHover(this._elt,
      this._isImagePreviewTarget);
  },

  _disableImagePreviewTooltip: function () {
    this.imagePreviewTooltip.stopTogglingOnHover();
  },

  _onToolboxPickerHover: function (event, nodeFront) {
    this.showNode(nodeFront).then(() => {
      this._showContainerAsHovered(nodeFront);
    }, console.error);
  },

  /**
   * If the element picker gets canceled, make sure and re-center the view on the
   * current selected element.
   */
  _onToolboxPickerCanceled: function () {
    if (this._selectedContainer) {
      scrollIntoViewIfNeeded(this._selectedContainer.editor.elt);
    }
  },

  isDragging: false,
  _draggedContainer: null,

  _onMouseMove: function (event) {
    let target = event.target;

    if (this._draggedContainer) {
      this._draggedContainer.onMouseMove(event);
    }
    // Auto-scroll if we're dragging.
    if (this.isDragging) {
      event.preventDefault();
      this._autoScroll(event);
      return;
    }

    // Show the current container as hovered and highlight it.
    // This requires finding the current MarkupContainer (walking up the DOM).
    while (!target.container) {
      if (target.tagName.toLowerCase() === "body") {
        return;
      }
      target = target.parentNode;
    }

    let container = target.container;
    if (this._hoveredNode !== container.node) {
      this._showBoxModel(container.node);
    }
    this._showContainerAsHovered(container.node);

    this.emit("node-hover");
  },

  /**
   * If focus is moved outside of the markup view document and there is a
   * selected container, make its contents not focusable by a keyboard.
   */
  _onBlur: function (event) {
    if (!this._selectedContainer) {
      return;
    }

    let {relatedTarget} = event;
    if (relatedTarget && relatedTarget.ownerDocument === this.doc) {
      return;
    }

    if (this._selectedContainer) {
      this._selectedContainer.clearFocus();
    }
  },

  /**
   * Executed on each mouse-move while a node is being dragged in the view.
   * Auto-scrolls the view to reveal nodes below the fold to drop the dragged
   * node in.
   */
  _autoScroll: function (event) {
    let docEl = this.doc.documentElement;

    if (this._autoScrollAnimationFrame) {
      this.win.cancelAnimationFrame(this._autoScrollAnimationFrame);
    }

    // Auto-scroll when the mouse approaches top/bottom edge.
    let fromBottom = docEl.clientHeight - event.pageY + this.win.scrollY;
    let fromTop = event.pageY - this.win.scrollY;
    let edgeDistance = Math.min(DRAG_DROP_AUTOSCROLL_EDGE_MAX_DISTANCE,
           docEl.clientHeight * DRAG_DROP_AUTOSCROLL_EDGE_RATIO);

    // The smaller the screen, the slower the movement.
    let heightToSpeedRatio =
      Math.max(DRAG_DROP_HEIGHT_TO_SPEED_MIN,
        Math.min(DRAG_DROP_HEIGHT_TO_SPEED_MAX,
          docEl.clientHeight / DRAG_DROP_HEIGHT_TO_SPEED));

    if (fromBottom <= edgeDistance) {
      // Map our distance range to a speed range so that the speed is not too
      // fast or too slow.
      let speed = map(
        fromBottom,
        0, edgeDistance,
        DRAG_DROP_MIN_AUTOSCROLL_SPEED, DRAG_DROP_MAX_AUTOSCROLL_SPEED);

      this._runUpdateLoop(() => {
        docEl.scrollTop -= heightToSpeedRatio *
          (speed - DRAG_DROP_MAX_AUTOSCROLL_SPEED);
      });
    }

    if (fromTop <= edgeDistance) {
      let speed = map(
        fromTop,
        0, edgeDistance,
        DRAG_DROP_MIN_AUTOSCROLL_SPEED, DRAG_DROP_MAX_AUTOSCROLL_SPEED);

      this._runUpdateLoop(() => {
        docEl.scrollTop += heightToSpeedRatio *
          (speed - DRAG_DROP_MAX_AUTOSCROLL_SPEED);
      });
    }
  },

  /**
   * Run a loop on the requestAnimationFrame.
   */
  _runUpdateLoop: function (update) {
    let loop = () => {
      update();
      this._autoScrollAnimationFrame = this.win.requestAnimationFrame(loop);
    };
    loop();
  },

  _onMouseClick: function (event) {
    // From the target passed here, let's find the parent MarkupContainer
    // and ask it if the tooltip should be shown
    let parentNode = event.target;
    let container;
    while (parentNode !== this.doc.body) {
      if (parentNode.container) {
        container = parentNode.container;
        break;
      }
      parentNode = parentNode.parentNode;
    }

    if (container instanceof MarkupElementContainer) {
      // With the newly found container, delegate the tooltip content creation
      // and decision to show or not the tooltip
      container._buildEventTooltipContent(event.target,
        this.eventDetailsTooltip);
    }
  },

  _onMouseUp: function (event) {
    if (this._draggedContainer) {
      this._draggedContainer.onMouseUp(event);
    }

    this.indicateDropTarget(null);
    this.indicateDragTarget(null);
    if (this._autoScrollAnimationFrame) {
      this.win.cancelAnimationFrame(this._autoScrollAnimationFrame);
    }
  },

  _onCollapseAttributesPrefChange: function () {
    this.collapseAttributes =
      Services.prefs.getBoolPref(ATTR_COLLAPSE_ENABLED_PREF);
    this.collapseAttributeLength =
      Services.prefs.getIntPref(ATTR_COLLAPSE_LENGTH_PREF);
    this.update();
  },

  cancelDragging: function () {
    if (!this.isDragging) {
      return;
    }

    for (let [, container] of this._containers) {
      if (container.isDragging) {
        container.cancelDragging();
        break;
      }
    }

    this.indicateDropTarget(null);
    this.indicateDragTarget(null);
    if (this._autoScrollAnimationFrame) {
      this.win.cancelAnimationFrame(this._autoScrollAnimationFrame);
    }
  },

  _hoveredNode: null,

  /**
   * Show a NodeFront's container as being hovered
   *
   * @param  {NodeFront} nodeFront
   *         The node to show as hovered
   */
  _showContainerAsHovered: function (nodeFront) {
    if (this._hoveredNode === nodeFront) {
      return;
    }

    if (this._hoveredNode) {
      this.getContainer(this._hoveredNode).hovered = false;
    }

    this.getContainer(nodeFront).hovered = true;
    this._hoveredNode = nodeFront;
    // Emit an event that the container view is actually hovered now, as this function
    // can be called by an asynchronous caller.
    this.emit("showcontainerhovered");
  },

  _onMouseOut: function (event) {
    // Emulate mouseleave by skipping any relatedTarget inside the markup-view.
    if (this._elt.contains(event.relatedTarget)) {
      return;
    }

    if (this._autoScrollAnimationFrame) {
      this.win.cancelAnimationFrame(this._autoScrollAnimationFrame);
    }
    if (this.isDragging) {
      return;
    }

    this._hideBoxModel(true);
    if (this._hoveredNode) {
      this.getContainer(this._hoveredNode).hovered = false;
    }
    this._hoveredNode = null;

    this.emit("leave");
  },

  /**
   * Show the box model highlighter on a given node front
   *
   * @param  {NodeFront} nodeFront
   *         The node to show the highlighter for
   * @return {Promise} Resolves when the highlighter for this nodeFront is
   *         shown, taking into account that there could already be highlighter
   *         requests queued up
   */
  _showBoxModel: function (nodeFront) {
    return this.toolbox.highlighterUtils.highlightNodeFront(nodeFront)
      .catch(this._handleRejectionIfNotDestroyed);
  },

  /**
   * Hide the box model highlighter on a given node front
   *
   * @param  {Boolean} forceHide
   *         See toolbox-highlighter-utils/unhighlight
   * @return {Promise} Resolves when the highlighter for this nodeFront is
   *         hidden, taking into account that there could already be highlighter
   *         requests queued up
   */
  _hideBoxModel: function (forceHide) {
    return this.toolbox.highlighterUtils.unhighlight(forceHide)
      .catch(this._handleRejectionIfNotDestroyed);
  },

  _briefBoxModelTimer: null,

  _clearBriefBoxModelTimer: function () {
    if (this._briefBoxModelTimer) {
      clearTimeout(this._briefBoxModelTimer);
      this._briefBoxModelPromise.resolve();
      this._briefBoxModelPromise = null;
      this._briefBoxModelTimer = null;
    }
  },

  _brieflyShowBoxModel: function (nodeFront) {
    this._clearBriefBoxModelTimer();
    let onShown = this._showBoxModel(nodeFront);

    let _resolve;
    this._briefBoxModelPromise = new Promise(resolve => {
      _resolve = resolve;
      this._briefBoxModelTimer = setTimeout(() => {
        this._hideBoxModel().then(resolve, resolve);
      }, NEW_SELECTION_HIGHLIGHTER_TIMER);
    });
    this._briefBoxModelPromise.resolve = _resolve;

    return promise.all([onShown, this._briefBoxModelPromise]);
  },

  /**
   * Get the MarkupContainer object for a given node, or undefined if
   * none exists.
   */
  getContainer: function (node) {
    return this._containers.get(node);
  },

  update: function () {
    let updateChildren = (node) => {
      this.getContainer(node).update();
      for (let child of node.treeChildren()) {
        updateChildren(child);
      }
    };

    // Start with the documentElement
    let documentElement;
    for (let node of this._rootNode.treeChildren()) {
      if (node.isDocumentElement === true) {
        documentElement = node;
        break;
      }
    }

    // Recursively update each node starting with documentElement.
    updateChildren(documentElement);
  },

  /**
   * Executed when the mouse hovers over a target in the markup-view and is used
   * to decide whether this target should be used to display an image preview
   * tooltip.
   * Delegates the actual decision to the corresponding MarkupContainer instance
   * if one is found.
   *
   * @return {Promise} the promise returned by
   *         MarkupElementContainer._isImagePreviewTarget
   */
  _isImagePreviewTarget: Task.async(function* (target) {
    // From the target passed here, let's find the parent MarkupContainer
    // and ask it if the tooltip should be shown
    if (this.isDragging) {
      return false;
    }

    let parent = target, container;
    while (parent) {
      if (parent.container) {
        container = parent.container;
        break;
      }
      parent = parent.parentNode;
    }

    if (container instanceof MarkupElementContainer) {
      // With the newly found container, delegate the tooltip content creation
      // and decision to show or not the tooltip
      return container.isImagePreviewTarget(target, this.imagePreviewTooltip);
    }

    return false;
  }),

  /**
   * Given the known reason, should the current selection be briefly highlighted
   * In a few cases, we don't want to highlight the node:
   * - If the reason is null (used to reset the selection),
   * - if it's "inspector-open" (when the inspector opens up, let's not
   * highlight the default node)
   * - if it's "navigateaway" (since the page is being navigated away from)
   * - if it's "test" (this is a special case for mochitest. In tests, we often
   * need to select elements but don't necessarily want the highlighter to come
   * and go after a delay as this might break test scenarios)
   * We also do not want to start a brief highlight timeout if the node is
   * already being hovered over, since in that case it will already be
   * highlighted.
   */
  _shouldNewSelectionBeHighlighted: function () {
    let reason = this.inspector.selection.reason;
    let unwantedReasons = [
      "inspector-open",
      "navigateaway",
      "nodeselected",
      "test"
    ];
    let isHighlight = this._hoveredNode === this.inspector.selection.nodeFront;
    return !isHighlight && reason && !unwantedReasons.includes(reason);
  },

  /**
   * React to new-node-front selection events.
   * Highlights the node if needed, and make sure it is shown and selected in
   * the view.
   */
  _onNewSelection: function () {
    let selection = this.inspector.selection;

    if (this.htmlEditor) {
      this.htmlEditor.hide();
    }
    if (this._hoveredNode && this._hoveredNode !== selection.nodeFront) {
      this.getContainer(this._hoveredNode).hovered = false;
      this._hoveredNode = null;
    }

    if (!selection.isNode()) {
      this.unmarkSelectedNode();
      return;
    }

    let done = this.inspector.updating("markup-view");
    let onShowBoxModel, onShow;

    // Highlight the element briefly if needed.
    if (this._shouldNewSelectionBeHighlighted()) {
      onShowBoxModel = this._brieflyShowBoxModel(selection.nodeFront);
    }

    onShow = this.showNode(selection.nodeFront).then(() => {
      // We could be destroyed by now.
      if (this._destroyer) {
        return promise.reject("markupview destroyed");
      }

      // Mark the node as selected.
      this.markNodeAsSelected(selection.nodeFront);

      // Make sure the new selection is navigated to.
      this.maybeNavigateToNewSelection();
      return undefined;
    }).catch(this._handleRejectionIfNotDestroyed);

    promise.all([onShowBoxModel, onShow]).then(done);
  },

  /**
   * Maybe make selected the current node selection's MarkupContainer depending
   * on why the current node got selected.
   */
  maybeNavigateToNewSelection: function () {
    let {reason, nodeFront} = this.inspector.selection;

    // The list of reasons that should lead to navigating to the node.
    let reasonsToNavigate = [
      // If the user picked an element with the element picker.
      "picker-node-picked",
      // If the user shift-clicked (previewed) an element.
      "picker-node-previewed",
      // If the user selected an element with the browser context menu.
      "browser-context-menu",
      // If the user added a new node by clicking in the inspector toolbar.
      "node-inserted"
    ];

    // If the user performed an action with a keyboard, move keyboard focus to
    // the markup tree container.
    if (reason && reason.endsWith("-keyboard")) {
      this.getContainer(this._rootNode).elt.focus();
    }

    if (reasonsToNavigate.includes(reason)) {
      this.getContainer(this._rootNode).elt.focus();
      this.navigate(this.getContainer(nodeFront));
    }
  },

  /**
   * Create a TreeWalker to find the next/previous
   * node for selection.
   */
  _selectionWalker: function (start) {
    let walker = this.doc.createTreeWalker(
      start || this._elt,
      nodeFilterConstants.SHOW_ELEMENT,
      function (element) {
        if (element.container &&
            element.container.elt === element &&
            element.container.visible) {
          return nodeFilterConstants.FILTER_ACCEPT;
        }
        return nodeFilterConstants.FILTER_SKIP;
      }
    );
    walker.currentNode = this._selectedContainer.elt;
    return walker;
  },

  _onCopy: function (evt) {
    // Ignore copy events from editors
    if (this._isInputOrTextarea(evt.target)) {
      return;
    }

    let selection = this.inspector.selection;
    if (selection.isNode()) {
      this.inspector.copyOuterHTML();
    }
    evt.stopPropagation();
    evt.preventDefault();
  },

  /**
   * Register all key shortcuts.
   */
  _initShortcuts: function () {
    let shortcuts = new KeyShortcuts({
      window: this.win,
    });

    this._onShortcut = this._onShortcut.bind(this);

    // Process localizable keys
    ["markupView.hide.key",
     "markupView.edit.key",
     "markupView.scrollInto.key"].forEach(name => {
       let key = INSPECTOR_L10N.getStr(name);
       shortcuts.on(key, event => this._onShortcut(name, event));
     });

    // Process generic keys:
    ["Delete", "Backspace", "Home", "Left", "Right", "Up", "Down", "PageUp",
     "PageDown", "Esc", "Enter", "Space"].forEach(key => {
       shortcuts.on(key, event => this._onShortcut(key, event));
     });
  },

  /**
   * Key shortcut listener.
   */
  _onShortcut(name, event) {
    if (this._isInputOrTextarea(event.target)) {
      return;
    }
    switch (name) {
      // Localizable keys
      case "markupView.hide.key": {
        let node = this._selectedContainer.node;
        if (node.hidden) {
          this.walker.unhideNode(node);
        } else {
          this.walker.hideNode(node);
        }
        break;
      }
      case "markupView.edit.key": {
        this.beginEditingOuterHTML(this._selectedContainer.node);
        break;
      }
      case "markupView.scrollInto.key": {
        let selection = this._selectedContainer.node;
        this.inspector.scrollNodeIntoView(selection);
        break;
      }
      // Generic keys
      case "Delete": {
        this.deleteNodeOrAttribute();
        break;
      }
      case "Backspace": {
        this.deleteNodeOrAttribute(true);
        break;
      }
      case "Home": {
        let rootContainer = this.getContainer(this._rootNode);
        this.navigate(rootContainer.children.firstChild.container);
        break;
      }
      case "Left": {
        if (this._selectedContainer.expanded) {
          this.collapseNode(this._selectedContainer.node);
        } else {
          let parent = this._selectionWalker().parentNode();
          if (parent) {
            this.navigate(parent.container);
          }
        }
        break;
      }
      case "Right": {
        if (!this._selectedContainer.expanded &&
            this._selectedContainer.hasChildren) {
          this._expandContainer(this._selectedContainer);
        } else {
          let next = this._selectionWalker().nextNode();
          if (next) {
            this.navigate(next.container);
          }
        }
        break;
      }
      case "Up": {
        let previousNode = this._selectionWalker().previousNode();
        if (previousNode) {
          this.navigate(previousNode.container);
        }
        break;
      }
      case "Down": {
        let nextNode = this._selectionWalker().nextNode();
        if (nextNode) {
          this.navigate(nextNode.container);
        }
        break;
      }
      case "PageUp": {
        let walker = this._selectionWalker();
        let selection = this._selectedContainer;
        for (let i = 0; i < PAGE_SIZE; i++) {
          let previousNode = walker.previousNode();
          if (!previousNode) {
            break;
          }
          selection = previousNode.container;
        }
        this.navigate(selection);
        break;
      }
      case "PageDown": {
        let walker = this._selectionWalker();
        let selection = this._selectedContainer;
        for (let i = 0; i < PAGE_SIZE; i++) {
          let nextNode = walker.nextNode();
          if (!nextNode) {
            break;
          }
          selection = nextNode.container;
        }
        this.navigate(selection);
        break;
      }
      case "Enter":
      case "Space": {
        if (!this._selectedContainer.canFocus) {
          this._selectedContainer.canFocus = true;
          this._selectedContainer.focus();
        } else {
          // Return early to prevent cancelling the event.
          return;
        }
        break;
      }
      case "Esc": {
        if (this.isDragging) {
          this.cancelDragging();
        } else {
          // Return early to prevent cancelling the event when not
          // dragging, to allow the split console to be toggled.
          return;
        }
        break;
      }
      default:
        console.error("Unexpected markup-view key shortcut", name);
        return;
    }
    // Prevent default for this action
    event.stopPropagation();
    event.preventDefault();
  },

  /**
   * Check if a node is an input or textarea
   */
  _isInputOrTextarea: function (element) {
    let name = element.tagName.toLowerCase();
    return name === "input" || name === "textarea";
  },

  /**
   * If there's an attribute on the current node that's currently focused, then
   * delete this attribute, otherwise delete the node itself.
   *
   * @param  {Boolean} moveBackward
   *         If set to true and if we're deleting the node, focus the previous
   *         sibling after deletion, otherwise the next one.
   */
  deleteNodeOrAttribute: function (moveBackward) {
    let focusedAttribute = this.doc.activeElement
                           ? this.doc.activeElement.closest(".attreditor")
                           : null;
    if (focusedAttribute) {
      // The focused attribute might not be in the current selected container.
      let container = focusedAttribute.closest("li.child").container;
      container.removeAttribute(focusedAttribute.dataset.attr);
    } else {
      this.deleteNode(this._selectedContainer.node, moveBackward);
    }
  },

  /**
   * Delete a node from the DOM.
   * This is an undoable action.
   *
   * @param  {NodeFront} node
   *         The node to remove.
   * @param  {Boolean} moveBackward
   *         If set to true, focus the previous sibling, otherwise the next one.
   */
  deleteNode: function (node, moveBackward) {
    if (!this.inspector.isDeletable(node)) {
      return;
    }

    let container = this.getContainer(node);

    // Retain the node so we can undo this...
    this.walker.retainNode(node).then(() => {
      let parent = node.parentNode();
      let nextSibling = null;
      this.undo.do(() => {
        this.walker.removeNode(node).then(siblings => {
          nextSibling = siblings.nextSibling;
          let prevSibling = siblings.previousSibling;
          let focusNode = moveBackward ? prevSibling : nextSibling;

          // If we can't move as the user wants, we move to the other direction.
          // If there is no sibling elements anymore, move to the parent node.
          if (!focusNode) {
            focusNode = nextSibling || prevSibling || parent;
          }

          let isNextSiblingText = nextSibling ?
            nextSibling.nodeType === nodeConstants.TEXT_NODE : false;
          let isPrevSiblingText = prevSibling ?
            prevSibling.nodeType === nodeConstants.TEXT_NODE : false;

          // If the parent had two children and the next or previous sibling
          // is a text node, then it now has only a single text node, is about
          // to be in-lined; and focus should move to the parent.
          if (parent.numChildren === 2
              && (isNextSiblingText || isPrevSiblingText)) {
            focusNode = parent;
          }

          if (container.selected) {
            this.navigate(this.getContainer(focusNode));
          }
        });
      }, () => {
        let isValidSibling = nextSibling && !nextSibling.isPseudoElement;
        nextSibling = isValidSibling ? nextSibling : null;
        this.walker.insertBefore(node, parent, nextSibling);
      });
    }).catch(console.error);
  },

  /**
   * If an editable item is focused, select its container.
   */
  _onFocus: function (event) {
    let parent = event.target;
    while (!parent.container) {
      parent = parent.parentNode;
    }
    if (parent) {
      this.navigate(parent.container);
    }
  },

  /**
   * Handle a user-requested navigation to a given MarkupContainer,
   * updating the inspector's currently-selected node.
   *
   * @param  {MarkupContainer} container
   *         The container we're navigating to.
   */
  navigate: function (container) {
    if (!container) {
      return;
    }

    let node = container.node;
    this.markNodeAsSelected(node, "treepanel");
  },

  /**
   * Make sure a node is included in the markup tool.
   *
   * @param  {NodeFront} node
   *         The node in the content document.
   * @param  {Boolean} flashNode
   *         Whether the newly imported node should be flashed
   * @return {MarkupContainer} The MarkupContainer object for this element.
   */
  importNode: function (node, flashNode) {
    if (!node) {
      return null;
    }

    if (this._containers.has(node)) {
      return this.getContainer(node);
    }

    let container;
    let {nodeType, isPseudoElement} = node;
    if (node === this.walker.rootNode) {
      container = new RootContainer(this, node);
      this._elt.appendChild(container.elt);
      this._rootNode = node;
    } else if (nodeType == nodeConstants.ELEMENT_NODE && !isPseudoElement) {
      container = new MarkupElementContainer(this, node, this.inspector);
    } else if (nodeType == nodeConstants.COMMENT_NODE ||
               nodeType == nodeConstants.TEXT_NODE) {
      container = new MarkupTextContainer(this, node, this.inspector);
    } else {
      container = new MarkupReadOnlyContainer(this, node, this.inspector);
    }

    if (flashNode) {
      container.flashMutation();
    }

    this._containers.set(node, container);
    container.childrenDirty = true;

    this._updateChildren(container);

    this.inspector.emit("container-created", container);

    return container;
  },

  /**
   * Mutation observer used for included nodes.
   */
  _mutationObserver: function (mutations) {
    for (let mutation of mutations) {
      let type = mutation.type;
      let target = mutation.target;

      if (mutation.type === "documentUnload") {
        // Treat this as a childList change of the child (maybe the protocol
        // should do this).
        type = "childList";
        target = mutation.targetParent;
        if (!target) {
          continue;
        }
      }

      let container = this.getContainer(target);
      if (!container) {
        // Container might not exist if this came from a load event for a node
        // we're not viewing.
        continue;
      }

      if (type === "attributes" || type === "characterData"
        || type === "events" || type === "pseudoClassLock") {
        container.update();
      } else if (type === "childList" || type === "nativeAnonymousChildList") {
        container.childrenDirty = true;
        // Update the children to take care of changes in the markup view DOM
        // and update container (and its subtree) DOM tree depth level for
        // accessibility where necessary.
        this._updateChildren(container, {flash: true}).then(() =>
          container.updateLevel());
      } else if (type === "inlineTextChild") {
        container.childrenDirty = true;
        this._updateChildren(container, {flash: true});
        container.update();
      }
    }

    this._waitForChildren().then(() => {
      if (this._destroyer) {
        // Could not fully update after markup mutations, the markup-view was destroyed
        // while waiting for children. Bail out silently.
        return;
      }
      this._flashMutatedNodes(mutations);
      this.inspector.emit("markupmutation", mutations);

      // Since the htmlEditor is absolutely positioned, a mutation may change
      // the location in which it should be shown.
      if (this.htmlEditor) {
        this.htmlEditor.refresh();
      }
    });
  },

  /**
   * React to display-change events from the walker
   *
   * @param  {Array} nodes
   *         An array of nodeFronts
   */
  _onDisplayChange: function (nodes) {
    for (let node of nodes) {
      let container = this.getContainer(node);
      if (container) {
        container.update();
      }
    }
  },

  /**
   * Given a list of mutations returned by the mutation observer, flash the
   * corresponding containers to attract attention.
   */
  _flashMutatedNodes: function (mutations) {
    let addedOrEditedContainers = new Set();
    let removedContainers = new Set();

    for (let {type, target, added, removed, newValue} of mutations) {
      let container = this.getContainer(target);

      if (container) {
        if (type === "characterData") {
          addedOrEditedContainers.add(container);
        } else if (type === "attributes" && newValue === null) {
          // Removed attributes should flash the entire node.
          // New or changed attributes will flash the attribute itself
          // in ElementEditor.flashAttribute.
          addedOrEditedContainers.add(container);
        } else if (type === "childList") {
          // If there has been removals, flash the parent
          if (removed.length) {
            removedContainers.add(container);
          }

          // If there has been additions, flash the nodes if their associated
          // container exist (so if their parent is expanded in the inspector).
          added.forEach(node => {
            let addedContainer = this.getContainer(node);
            if (addedContainer) {
              addedOrEditedContainers.add(addedContainer);

              // The node may be added as a result of an append, in which case
              // it will have been removed from another container first, but in
              // these cases we don't want to flash both the removal and the
              // addition
              removedContainers.delete(container);
            }
          });
        }
      }
    }

    for (let container of removedContainers) {
      container.flashMutation();
    }
    for (let container of addedOrEditedContainers) {
      container.flashMutation();
    }
  },

  /**
   * Make sure the given node's parents are expanded and the
   * node is scrolled on to screen.
   */
  showNode: function (node, centered = true) {
    let parent = node;

    this.importNode(node);

    while ((parent = parent.parentNode())) {
      this.importNode(parent);
      this.expandNode(parent);
    }

    return this._waitForChildren().then(() => {
      if (this._destroyer) {
        return promise.reject("markupview destroyed");
      }
      return this._ensureVisible(node);
    }).then(() => {
      scrollIntoViewIfNeeded(this.getContainer(node).editor.elt, centered);
    }, this._handleRejectionIfNotDestroyed);
  },

  /**
   * Expand the container's children.
   */
  _expandContainer: function (container) {
    return this._updateChildren(container, {expand: true}).then(() => {
      if (this._destroyer) {
        // Could not expand the node, the markup-view was destroyed in the meantime. Just
        // silently give up.
        return;
      }
      container.setExpanded(true);
    });
  },

  /**
   * Expand the node's children.
   */
  expandNode: function (node) {
    let container = this.getContainer(node);
    return this._expandContainer(container);
  },

  /**
   * Expand the entire tree beneath a container.
   *
   * @param  {MarkupContainer} container
   *         The container to expand.
   */
  _expandAll: function (container) {
    return this._expandContainer(container).then(() => {
      let child = container.children.firstChild;
      let promises = [];
      while (child) {
        promises.push(this._expandAll(child.container));
        child = child.nextSibling;
      }
      return promise.all(promises);
    }).catch(console.error);
  },

  /**
   * Expand the entire tree beneath a node.
   *
   * @param  {DOMNode} node
   *         The node to expand, or null to start from the top.
   */
  expandAll: function (node) {
    node = node || this._rootNode;
    return this._expandAll(this.getContainer(node));
  },

  /**
   * Collapse the node's children.
   */
  collapseNode: function (node) {
    let container = this.getContainer(node);
    container.setExpanded(false);
  },

  _collapseAll: function (container) {
    container.setExpanded(false);
    let children = container.getChildContainers() || [];
    children.forEach(child => this._collapseAll(child));
  },

  /**
   * Collapse the entire tree beneath a node.
   */
  collapseAll: function (node) {
    this._collapseAll(this.getContainer(node));
  },

  /**
   * Returns either the innerHTML or the outerHTML for a remote node.
   *
   * @param  {NodeFront} node
   *         The NodeFront to get the outerHTML / innerHTML for.
   * @param  {Boolean} isOuter
   *         If true, makes the function return the outerHTML,
   *         otherwise the innerHTML.
   * @return {Promise} that will be resolved with the outerHTML / innerHTML.
   */
  _getNodeHTML: function (node, isOuter) {
    let walkerPromise = null;

    if (isOuter) {
      walkerPromise = this.walker.outerHTML(node);
    } else {
      walkerPromise = this.walker.innerHTML(node);
    }

    return walkerPromise.then(longstr => {
      return longstr.string().then(html => {
        longstr.release().catch(console.error);
        return html;
      });
    });
  },

  /**
   * Retrieve the outerHTML for a remote node.
   *
   * @param  {NodeFront} node
   *         The NodeFront to get the outerHTML for.
   * @return {Promise} that will be resolved with the outerHTML.
   */
  getNodeOuterHTML: function (node) {
    return this._getNodeHTML(node, true);
  },

  /**
   * Retrieve the innerHTML for a remote node.
   *
   * @param  {NodeFront} node
   *         The NodeFront to get the innerHTML for.
   * @return {Promise} that will be resolved with the innerHTML.
   */
  getNodeInnerHTML: function (node) {
    return this._getNodeHTML(node);
  },

  /**
   * Listen to mutations, expect a given node to be removed and try and select
   * the node that sits at the same place instead.
   * This is useful when changing the outerHTML or the tag name so that the
   * newly inserted node gets selected instead of the one that just got removed.
   */
  reselectOnRemoved: function (removedNode, reason) {
    // Only allow one removed node reselection at a time, so that when there are
    // more than 1 request in parallel, the last one wins.
    this.cancelReselectOnRemoved();

    // Get the removedNode index in its parent node to reselect the right node.
    let isHTMLTag = removedNode.tagName.toLowerCase() === "html";
    let oldContainer = this.getContainer(removedNode);
    let parentContainer = this.getContainer(removedNode.parentNode());
    let childIndex = parentContainer.getChildContainers().indexOf(oldContainer);

    let onMutations = this._removedNodeObserver = (e, mutations) => {
      let isNodeRemovalMutation = false;
      for (let mutation of mutations) {
        let containsRemovedNode = mutation.removed &&
                                  mutation.removed.some(n => n === removedNode);
        if (mutation.type === "childList" &&
            (containsRemovedNode || isHTMLTag)) {
          isNodeRemovalMutation = true;
          break;
        }
      }
      if (!isNodeRemovalMutation) {
        return;
      }

      this.inspector.off("markupmutation", onMutations);
      this._removedNodeObserver = null;

      // Don't select the new node if the user has already changed the current
      // selection.
      if (this.inspector.selection.nodeFront === parentContainer.node ||
          (this.inspector.selection.nodeFront === removedNode && isHTMLTag)) {
        let childContainers = parentContainer.getChildContainers();
        if (childContainers && childContainers[childIndex]) {
          this.markNodeAsSelected(childContainers[childIndex].node, reason);
          if (childContainers[childIndex].hasChildren) {
            this.expandNode(childContainers[childIndex].node);
          }
          this.emit("reselectedonremoved");
        }
      }
    };

    // Start listening for mutations until we find a childList change that has
    // removedNode removed.
    this.inspector.on("markupmutation", onMutations);
  },

  /**
   * Make sure to stop listening for node removal markupmutations and not
   * reselect the corresponding node when that happens.
   * Useful when the outerHTML/tagname edition failed.
   */
  cancelReselectOnRemoved: function () {
    if (this._removedNodeObserver) {
      this.inspector.off("markupmutation", this._removedNodeObserver);
      this._removedNodeObserver = null;
      this.emit("canceledreselectonremoved");
    }
  },

  /**
   * Replace the outerHTML of any node displayed in the inspector with
   * some other HTML code
   *
   * @param  {NodeFront} node
   *         Node which outerHTML will be replaced.
   * @param  {String} newValue
   *         The new outerHTML to set on the node.
   * @param  {String} oldValue
   *         The old outerHTML that will be used if the user undoes the update.
   * @return {Promise} that will resolve when the outer HTML has been updated.
   */
  updateNodeOuterHTML: function (node, newValue) {
    let container = this.getContainer(node);
    if (!container) {
      return promise.reject();
    }

    // Changing the outerHTML removes the node which outerHTML was changed.
    // Listen to this removal to reselect the right node afterwards.
    this.reselectOnRemoved(node, "outerhtml");
    return this.walker.setOuterHTML(node, newValue).catch(() => {
      this.cancelReselectOnRemoved();
    });
  },

  /**
   * Replace the innerHTML of any node displayed in the inspector with
   * some other HTML code
   * @param  {Node} node
   *         node which innerHTML will be replaced.
   * @param  {String} newValue
   *         The new innerHTML to set on the node.
   * @param  {String} oldValue
   *         The old innerHTML that will be used if the user undoes the update.
   * @return {Promise} that will resolve when the inner HTML has been updated.
   */
  updateNodeInnerHTML: function (node, newValue, oldValue) {
    let container = this.getContainer(node);
    if (!container) {
      return promise.reject();
    }

    return new Promise((resolve, reject) => {
      container.undo.do(() => {
        this.walker.setInnerHTML(node, newValue).then(resolve, reject);
      }, () => {
        this.walker.setInnerHTML(node, oldValue);
      });
    });
  },

  /**
   * Insert adjacent HTML to any node displayed in the inspector.
   *
   * @param  {NodeFront} node
   *         The reference node.
   * @param  {String} position
   *         The position as specified for Element.insertAdjacentHTML
   *         (i.e. "beforeBegin", "afterBegin", "beforeEnd", "afterEnd").
   * @param  {String} newValue
   *         The adjacent HTML.
   * @return {Promise} that will resolve when the adjacent HTML has
   *         been inserted.
   */
  insertAdjacentHTMLToNode: function (node, position, value) {
    let container = this.getContainer(node);
    if (!container) {
      return promise.reject();
    }

    let injectedNodes = [];

    return new Promise((resolve, reject) => {
      container.undo.do(() => {
        // eslint-disable-next-line no-unsanitized/method
        this.walker
            .insertAdjacentHTML(node, position, value)
            .then(nodeArray => {
              injectedNodes = nodeArray.nodes;
              return nodeArray;
            })
            .then(resolve, reject);
      }, () => {
        this.walker.removeNodes(injectedNodes);
      });
    });
  },

  /**
   * Open an editor in the UI to allow editing of a node's outerHTML.
   *
   * @param  {NodeFront} node
   *         The NodeFront to edit.
   */
  beginEditingOuterHTML: function (node) {
    this.getNodeOuterHTML(node).then(oldValue => {
      let container = this.getContainer(node);
      if (!container) {
        return;
      }
      // Load load and create HTML Editor as it is rarely used and fetch complex deps
      if (!this.htmlEditor) {
        let HTMLEditor = require("devtools/client/inspector/markup/views/html-editor");
        this.htmlEditor = new HTMLEditor(this.doc);
      }
      this.htmlEditor.show(container.tagLine, oldValue);
      this.htmlEditor.once("popuphidden", (e, commit, value) => {
        // Need to focus the <html> element instead of the frame / window
        // in order to give keyboard focus back to doc (from editor).
        this.doc.documentElement.focus();

        if (commit) {
          this.updateNodeOuterHTML(node, value, oldValue);
        }
      });

      this.emit("begin-editing");
    });
  },

  /**
   * Expand or collapse the given node.
   *
   * @param  {NodeFront} node
   *         The NodeFront to update.
   * @param  {Boolean} expanded
   *         Whether the node should be expanded/collapsed.
   * @param  {Boolean} applyToDescendants
   *         Whether all descendants should also be expanded/collapsed
   */
  setNodeExpanded: function (node, expanded, applyToDescendants) {
    if (expanded) {
      if (applyToDescendants) {
        this.expandAll(node);
      } else {
        this.expandNode(node);
      }
    } else if (applyToDescendants) {
      this.collapseAll(node);
    } else {
      this.collapseNode(node);
    }
  },

  /**
   * Mark the given node selected, and update the inspector.selection
   * object's NodeFront to keep consistent state between UI and selection.
   *
   * @param  {NodeFront} aNode
   *         The NodeFront to mark as selected.
   * @param  {String} reason
   *         The reason for marking the node as selected.
   * @return {Boolean} False if the node is already marked as selected, true
   *         otherwise.
   */
  markNodeAsSelected: function (node, reason) {
    let container = this.getContainer(node);

    if (this._selectedContainer === container) {
      return false;
    }

    // Un-select and remove focus from the previous container.
    if (this._selectedContainer) {
      this._selectedContainer.selected = false;
      this._selectedContainer.clearFocus();
    }

    // Select the new container.
    this._selectedContainer = container;
    if (node) {
      this._selectedContainer.selected = true;
    }

    // Change the current selection if needed.
    if (this.inspector.selection.nodeFront !== node) {
      this.inspector.selection.setNodeFront(node, reason || "nodeselected");
    }

    return true;
  },

  /**
   * Make sure that every ancestor of the selection are updated
   * and included in the list of visible children.
   */
  _ensureVisible: function (node) {
    while (node) {
      let container = this.getContainer(node);
      let parent = node.parentNode();
      if (!container.elt.parentNode) {
        let parentContainer = this.getContainer(parent);
        if (parentContainer) {
          parentContainer.childrenDirty = true;
          this._updateChildren(parentContainer, {expand: true});
        }
      }

      node = parent;
    }
    return this._waitForChildren();
  },

  /**
   * Unmark selected node (no node selected).
   */
  unmarkSelectedNode: function () {
    if (this._selectedContainer) {
      this._selectedContainer.selected = false;
      this._selectedContainer = null;
    }
  },

  /**
   * Check if the current selection is a descendent of the container.
   * if so, make sure it's among the visible set for the container,
   * and set the dirty flag if needed.
   *
   * @return The node that should be made visible, if any.
   */
  _checkSelectionVisible: function (container) {
    let centered = null;
    let node = this.inspector.selection.nodeFront;
    while (node) {
      if (node.parentNode() === container.node) {
        centered = node;
        break;
      }
      node = node.parentNode();
    }

    return centered;
  },

  /**
   * Make sure all children of the given container's node are
   * imported and attached to the container in the right order.
   *
   * Children need to be updated only in the following circumstances:
   * a) We just imported this node and have never seen its children.
   *    container.childrenDirty will be set by importNode in this case.
   * b) We received a childList mutation on the node.
   *    container.childrenDirty will be set in that case too.
   * c) We have changed the selection, and the path to that selection
   *    wasn't loaded in a previous children request (because we only
   *    grab a subset).
   *    container.childrenDirty should be set in that case too!
   *
   * @param  {MarkupContainer} container
   *         The markup container whose children need updating
   * @param  {Object} options
   *         Options are {expand:boolean,flash:boolean}
   * @return {Promise} that will be resolved when the children are ready
   *         (which may be immediately).
   */
  _updateChildren: function (container, options) {
    let expand = options && options.expand;
    let flash = options && options.flash;

    container.hasChildren = container.node.hasChildren;
    // Accessibility should either ignore empty children or semantically
    // consider them a group.
    container.setChildrenRole();

    if (!this._queuedChildUpdates) {
      this._queuedChildUpdates = new Map();
    }

    if (this._queuedChildUpdates.has(container)) {
      return this._queuedChildUpdates.get(container);
    }

    if (!container.childrenDirty) {
      return promise.resolve(container);
    }

    if (container.inlineTextChild
        && container.inlineTextChild != container.node.inlineTextChild) {
      // This container was doing double duty as a container for a single
      // text child, back that out.
      this._containers.delete(container.inlineTextChild);
      container.clearInlineTextChild();

      if (container.hasChildren && container.selected) {
        container.setExpanded(true);
      }
    }

    if (container.node.inlineTextChild) {
      container.setExpanded(false);
      // this container will do double duty as the container for the single
      // text child.
      while (container.children.firstChild) {
        container.children.firstChild.remove();
      }

      container.setInlineTextChild(container.node.inlineTextChild);

      this._containers.set(container.node.inlineTextChild, container);
      container.childrenDirty = false;
      return promise.resolve(container);
    }

    if (!container.hasChildren) {
      while (container.children.firstChild) {
        container.children.firstChild.remove();
      }
      container.childrenDirty = false;
      container.setExpanded(false);
      return promise.resolve(container);
    }

    // If we're not expanded (or asked to update anyway), we're done for
    // now.  Note that this will leave the childrenDirty flag set, so when
    // expanded we'll refresh the child list.
    if (!(container.expanded || expand)) {
      return promise.resolve(container);
    }

    // We're going to issue a children request, make sure it includes the
    // centered node.
    let centered = this._checkSelectionVisible(container);

    // Children aren't updated yet, but clear the childrenDirty flag anyway.
    // If the dirty flag is re-set while we're fetching we'll need to fetch
    // again.
    container.childrenDirty = false;
    let updatePromise =
      this._getVisibleChildren(container, centered).then(children => {
        if (!this._containers) {
          return promise.reject("markup view destroyed");
        }
        this._queuedChildUpdates.delete(container);

        // If children are dirty, we got a change notification for this node
        // while the request was in progress, we need to do it again.
        if (container.childrenDirty) {
          return this._updateChildren(container, {expand: centered || expand});
        }

        let fragment = this.doc.createDocumentFragment();

        for (let child of children.nodes) {
          let childContainer = this.importNode(child, flash);
          fragment.appendChild(childContainer.elt);
        }

        while (container.children.firstChild) {
          container.children.firstChild.remove();
        }

        if (!children.hasFirst) {
          let topItem = this.buildMoreNodesButtonMarkup(container);
          fragment.insertBefore(topItem, fragment.firstChild);
        }
        if (!children.hasLast) {
          let bottomItem = this.buildMoreNodesButtonMarkup(container);
          fragment.appendChild(bottomItem);
        }

        container.children.appendChild(fragment);
        return container;
      }).catch(this._handleRejectionIfNotDestroyed);
    this._queuedChildUpdates.set(container, updatePromise);
    return updatePromise;
  },

  buildMoreNodesButtonMarkup: function (container) {
    let elt = this.doc.createElement("li");
    elt.classList.add("more-nodes", "devtools-class-comment");

    let label = this.doc.createElement("span");
    label.textContent = INSPECTOR_L10N.getStr("markupView.more.showing");
    elt.appendChild(label);

    let button = this.doc.createElement("button");
    button.setAttribute("href", "#");
    let showAllString = PluralForm.get(container.node.numChildren,
      INSPECTOR_L10N.getStr("markupView.more.showAll2"));
    button.textContent = showAllString.replace("#1", container.node.numChildren);
    elt.appendChild(button);

    button.addEventListener("click", () => {
      container.maxChildren = -1;
      container.childrenDirty = true;
      this._updateChildren(container);
    });

    return elt;
  },

  _waitForChildren: function () {
    if (!this._queuedChildUpdates) {
      return promise.resolve(undefined);
    }

    return promise.all([...this._queuedChildUpdates.values()]);
  },

  /**
   * Return a list of the children to display for this container.
   */
  _getVisibleChildren: function (container, centered) {
    let maxChildren = container.maxChildren || this.maxChildren;
    if (maxChildren == -1) {
      maxChildren = undefined;
    }

    return this.walker.children(container.node, {
      maxNodes: maxChildren,
      center: centered
    });
  },

  /**
   * Tear down the markup panel.
   */
  destroy: function () {
    if (this._destroyer) {
      return this._destroyer;
    }

    this._destroyer = promise.resolve();

    this._clearBriefBoxModelTimer();

    this._hoveredNode = null;

    if (this.htmlEditor) {
      this.htmlEditor.destroy();
      this.htmlEditor = null;
    }

    this.undo.destroy();
    this.undo = null;

    this.popup.destroy();
    this.popup = null;

    this._elt.removeEventListener("click", this._onMouseClick);
    this._elt.removeEventListener("mousemove", this._onMouseMove);
    this._elt.removeEventListener("mouseout", this._onMouseOut);
    this._elt.removeEventListener("blur", this._onBlur, true);
    this.win.removeEventListener("mouseup", this._onMouseUp);
    this.win.removeEventListener("copy", this._onCopy);
    this._frame.removeEventListener("focus", this._onFocus);
    this.walker.off("mutations", this._mutationObserver);
    this.walker.off("display-change", this._onDisplayChange);
    this.inspector.selection.off("new-node-front", this._onNewSelection);
    this.toolbox.off("picker-node-hovered",
                                this._onToolboxPickerHover);

    this._prefObserver.off(ATTR_COLLAPSE_ENABLED_PREF,
                           this._onCollapseAttributesPrefChange);
    this._prefObserver.off(ATTR_COLLAPSE_LENGTH_PREF,
                           this._onCollapseAttributesPrefChange);
    this._prefObserver.destroy();

    this._elt = null;

    for (let [, container] of this._containers) {
      container.destroy();
    }
    this._containers = null;

    this.eventDetailsTooltip.destroy();
    this.eventDetailsTooltip = null;

    this.imagePreviewTooltip.destroy();
    this.imagePreviewTooltip = null;

    this.win = null;
    this.doc = null;

    this._lastDropTarget = null;
    this._lastDragTarget = null;

    return this._destroyer;
  },

  /**
   * Find the closest element with class tag-line. These are used to indicate
   * drag and drop targets.
   *
   * @param  {DOMNode} el
   * @return {DOMNode}
   */
  findClosestDragDropTarget: function (el) {
    return el.classList.contains("tag-line")
           ? el
           : el.querySelector(".tag-line") || el.closest(".tag-line");
  },

  /**
   * Takes an element as it's only argument and marks the element
   * as the drop target
   */
  indicateDropTarget: function (el) {
    if (this._lastDropTarget) {
      this._lastDropTarget.classList.remove("drop-target");
    }

    if (!el) {
      return;
    }

    let target = this.findClosestDragDropTarget(el);
    if (target) {
      target.classList.add("drop-target");
      this._lastDropTarget = target;
    }
  },

  /**
   * Takes an element to mark it as indicator of dragging target's initial place
   */
  indicateDragTarget: function (el) {
    if (this._lastDragTarget) {
      this._lastDragTarget.classList.remove("drag-target");
    }

    if (!el) {
      return;
    }

    let target = this.findClosestDragDropTarget(el);
    if (target) {
      target.classList.add("drag-target");
      this._lastDragTarget = target;
    }
  },

  /**
   * Used to get the nodes required to modify the markup after dragging the
   * element (parent/nextSibling).
   */
  get dropTargetNodes() {
    let target = this._lastDropTarget;

    if (!target) {
      return null;
    }

    let parent, nextSibling;

    if (target.previousElementSibling &&
        target.previousElementSibling.nodeName.toLowerCase() === "ul") {
      parent = target.parentNode.container.node;
      nextSibling = null;
    } else {
      parent = target.parentNode.container.node.parentNode();
      nextSibling = target.parentNode.container.node;
    }

    if (nextSibling && nextSibling.isBeforePseudoElement) {
      nextSibling = target.parentNode.parentNode.children[1].container.node;
    }
    if (nextSibling && nextSibling.isAfterPseudoElement) {
      parent = target.parentNode.container.node.parentNode();
      nextSibling = null;
    }

    if (parent.nodeType !== nodeConstants.ELEMENT_NODE) {
      return null;
    }

    return {parent, nextSibling};
  }
};

/**
 * Map a number from one range to another.
 */
function map(value, oldMin, oldMax, newMin, newMax) {
  let ratio = oldMax - oldMin;
  if (ratio == 0) {
    return value;
  }
  return newMin + (newMax - newMin) * ((value - oldMin) / ratio);
}

module.exports = MarkupView;
PK
!<ko��Achrome/devtools/modules/devtools/client/inspector/markup/utils.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

/**
 * Apply a 'flashed' background and foreground color to elements. Intended
 * to be used with flashElementOff as a way of drawing attention to an element.
 *
 * @param  {Node} backgroundElt
 *         The element to set the highlighted background color on.
 * @param  {Node} foregroundElt
 *         The element to set the matching foreground color on.
 *         Optional.  This will equal backgroundElt if not set.
 */
function flashElementOn(backgroundElt, foregroundElt = backgroundElt) {
  if (!backgroundElt || !foregroundElt) {
    return;
  }

  // Make sure the animation class is not here
  backgroundElt.classList.remove("flash-out");

  // Change the background
  backgroundElt.classList.add("theme-bg-contrast");

  foregroundElt.classList.add("theme-fg-contrast");
  [].forEach.call(
    foregroundElt.querySelectorAll("[class*=theme-fg-color]"),
    span => span.classList.add("theme-fg-contrast")
  );
}

/**
 * Remove a 'flashed' background and foreground color to elements.
 * See flashElementOn.
 *
 * @param  {Node} backgroundElt
 *         The element to remove the highlighted background color on.
 * @param  {Node} foregroundElt
 *         The element to remove the matching foreground color on.
 *         Optional.  This will equal backgroundElt if not set.
 */
function flashElementOff(backgroundElt, foregroundElt = backgroundElt) {
  if (!backgroundElt || !foregroundElt) {
    return;
  }

  // Add the animation class to smoothly remove the background
  backgroundElt.classList.add("flash-out");

  // Remove the background
  backgroundElt.classList.remove("theme-bg-contrast");

  foregroundElt.classList.remove("theme-fg-contrast");
  [].forEach.call(
    foregroundElt.querySelectorAll("[class*=theme-fg-color]"),
    span => span.classList.remove("theme-fg-contrast")
  );
}

/**
 * Retrieve the available width between a provided element left edge and a container right
 * edge. This used can be used as a max-width for inplace-editor (autocomplete) widgets
 * replacing Editor elements of the the markup-view;
 */
function getAutocompleteMaxWidth(element, container) {
  let elementRect = element.getBoundingClientRect();
  let containerRect = container.getBoundingClientRect();
  return containerRect.right - elementRect.left - 2;
}

/**
 * Parse attribute names and values from a string.
 *
 * @param  {String} attr
 *         The input string for which names/values are to be parsed.
 * @param  {HTMLDocument} doc
 *         A document that can be used to test valid attributes.
 * @return {Array}
 *         An array of attribute names and their values.
 */
function parseAttributeValues(attr, doc) {
  attr = attr.trim();

  let parseAndGetNode = str => {
    return new DOMParser().parseFromString(str, "text/html").body.childNodes[0];
  };

  // Handle bad user inputs by appending a " or ' if it fails to parse without
  // them. Also note that a SVG tag is used to make sure the HTML parser
  // preserves mixed-case attributes
  let el = parseAndGetNode("<svg " + attr + "></svg>") ||
           parseAndGetNode("<svg " + attr + "\"></svg>") ||
           parseAndGetNode("<svg " + attr + "'></svg>");

  let div = doc.createElement("div");
  let attributes = [];
  for (let {name, value} of el.attributes) {
    // Try to set on an element in the document, throws exception on bad input.
    // Prevents InvalidCharacterError - "String contains an invalid character".
    try {
      div.setAttribute(name, value);
      attributes.push({ name, value });
    } catch (e) {
      // This may throw exceptions on bad input.
      // Prevents InvalidCharacterError - "String contains an invalid
      // character".
    }
  }

  return attributes;
}

/**
 * Truncate the string and add ellipsis to the middle of the string.
 */
function truncateString(str, maxLength) {
  if (!str || str.length <= maxLength) {
    return str;
  }

  return str.substring(0, Math.ceil(maxLength / 2)) +
         "…" +
         str.substring(str.length - Math.floor(maxLength / 2));
}

module.exports = {
  flashElementOn,
  flashElementOff,
  getAutocompleteMaxWidth,
  parseAttributeValues,
  truncateString,
};
PK
!<?�ҶaaSchrome/devtools/modules/devtools/client/inspector/markup/views/element-container.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 PREVIEW_MAX_DIM_PREF = "devtools.inspector.imagePreviewTooltipSize";

const promise = require("promise");
const Services = require("Services");
const {Task} = require("devtools/shared/task");
const nodeConstants = require("devtools/shared/dom-node-constants");
const clipboardHelper = require("devtools/shared/platform/clipboard");
const {setImageTooltip, setBrokenImageTooltip} =
      require("devtools/client/shared/widgets/tooltip/ImageTooltipHelper");
const MarkupContainer = require("devtools/client/inspector/markup/views/markup-container");
const ElementEditor = require("devtools/client/inspector/markup/views/element-editor");
const {extend} = require("devtools/shared/extend");

// Lazy load this module as _buildEventTooltipContent is only called on click
loader.lazyRequireGetter(this, "setEventTooltip",
  "devtools/client/shared/widgets/tooltip/EventTooltipHelper", true);

/**
 * An implementation of MarkupContainer for Elements that can contain
 * child nodes.
 * Allows editing of tag name, attributes, expanding / collapsing.
 *
 * @param  {MarkupView} markupView
 *         The markup view that owns this container.
 * @param  {NodeFront} node
 *         The node to display.
 */
function MarkupElementContainer(markupView, node) {
  MarkupContainer.prototype.initialize.call(this, markupView, node,
    "elementcontainer");

  if (node.nodeType === nodeConstants.ELEMENT_NODE) {
    this.editor = new ElementEditor(this, node);
  } else {
    throw new Error("Invalid node for MarkupElementContainer");
  }

  this.tagLine.appendChild(this.editor.elt);
}

MarkupElementContainer.prototype = extend(MarkupContainer.prototype, {
  _buildEventTooltipContent: Task.async(function* (target, tooltip) {
    if (target.hasAttribute("data-event")) {
      yield tooltip.hide();

      let listenerInfo = yield this.node.getEventListenerInfo();

      let toolbox = this.markup.toolbox;

      setEventTooltip(tooltip, listenerInfo, toolbox);
      // Disable the image preview tooltip while we display the event details
      this.markup._disableImagePreviewTooltip();
      tooltip.once("hidden", () => {
        // Enable the image preview tooltip after closing the event details
        this.markup._enableImagePreviewTooltip();
      });
      tooltip.show(target);
    }
  }),

  /**
   * Generates the an image preview for this Element. The element must be an
   * image or canvas (@see isPreviewable).
   *
   * @return {Promise} that is resolved with an object of form
   *         { data, size: { naturalWidth, naturalHeight, resizeRatio } } where
   *         - data is the data-uri for the image preview.
   *         - size contains information about the original image size and if
   *         the preview has been resized.
   *
   * If this element is not previewable or the preview cannot be generated for
   * some reason, the Promise is rejected.
   */
  _getPreview: function () {
    if (!this.isPreviewable()) {
      return promise.reject("_getPreview called on a non-previewable element.");
    }

    if (this.tooltipDataPromise) {
      // A preview request is already pending. Re-use that request.
      return this.tooltipDataPromise;
    }

    // Fetch the preview from the server.
    this.tooltipDataPromise = Task.spawn(function* () {
      let maxDim = Services.prefs.getIntPref(PREVIEW_MAX_DIM_PREF);
      let preview = yield this.node.getImageData(maxDim);
      let data = yield preview.data.string();

      // Clear the pending preview request. We can't reuse the results later as
      // the preview contents might have changed.
      this.tooltipDataPromise = null;
      return { data, size: preview.size };
    }.bind(this));

    return this.tooltipDataPromise;
  },

  /**
   * Executed by MarkupView._isImagePreviewTarget which is itself called when
   * the mouse hovers over a target in the markup-view.
   * Checks if the target is indeed something we want to have an image tooltip
   * preview over and, if so, inserts content into the tooltip.
   *
   * @return {Promise} that resolves when the tooltip content is ready. Resolves
   * true if the tooltip should be displayed, false otherwise.
   */
  isImagePreviewTarget: Task.async(function* (target, tooltip) {
    // Is this Element previewable.
    if (!this.isPreviewable()) {
      return false;
    }

    // If the Element has an src attribute, the tooltip is shown when hovering
    // over the src url. If not, the tooltip is shown when hovering over the tag
    // name.
    let src = this.editor.getAttributeElement("src");
    let expectedTarget = src ? src.querySelector(".link") : this.editor.tag;
    if (target !== expectedTarget) {
      return false;
    }

    try {
      let { data, size } = yield this._getPreview();
      // The preview is ready.
      let options = {
        naturalWidth: size.naturalWidth,
        naturalHeight: size.naturalHeight,
        maxDim: Services.prefs.getIntPref(PREVIEW_MAX_DIM_PREF)
      };

      setImageTooltip(tooltip, this.markup.doc, data, options);
    } catch (e) {
      // Indicate the failure but show the tooltip anyway.
      setBrokenImageTooltip(tooltip, this.markup.doc);
    }
    return true;
  }),

  copyImageDataUri: function () {
    // We need to send again a request to gettooltipData even if one was sent
    // for the tooltip, because we want the full-size image
    this.node.getImageData().then(data => {
      data.data.string().then(str => {
        clipboardHelper.copyString(str);
      });
    });
  },

  setInlineTextChild: function (inlineTextChild) {
    this.inlineTextChild = inlineTextChild;
    this.editor.updateTextEditor();
  },

  clearInlineTextChild: function () {
    this.inlineTextChild = undefined;
    this.editor.updateTextEditor();
  },

  /**
   * Trigger new attribute field for input.
   */
  addAttribute: function () {
    this.editor.newAttr.editMode();
  },

  /**
   * Trigger attribute field for editing.
   */
  editAttribute: function (attrName) {
    this.editor.attrElements.get(attrName).editMode();
  },

  /**
   * Remove attribute from container.
   * This is an undoable action.
   */
  removeAttribute: function (attrName) {
    let doMods = this.editor._startModifyingAttributes();
    let undoMods = this.editor._startModifyingAttributes();
    this.editor._saveAttribute(attrName, undoMods);
    doMods.removeAttribute(attrName);
    this.undo.do(() => {
      doMods.apply();
    }, () => {
      undoMods.apply();
    });
  }
});

module.exports = MarkupElementContainer;
PK
!</9B�XXPchrome/devtools/modules/devtools/client/inspector/markup/views/element-editor.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Services = require("Services");
const TextEditor = require("devtools/client/inspector/markup/views/text-editor");
const {
  getAutocompleteMaxWidth,
  flashElementOn,
  flashElementOff,
  parseAttributeValues,
  truncateString,
} = require("devtools/client/inspector/markup/utils");
const {editableField, InplaceEditor} =
      require("devtools/client/shared/inplace-editor");
const {parseAttribute} =
      require("devtools/client/shared/node-attribute-parser");
const {getCssProperties} = require("devtools/shared/fronts/css-properties");

// Global tooltip inspector
const {LocalizationHelper} = require("devtools/shared/l10n");
const INSPECTOR_L10N =
  new LocalizationHelper("devtools/client/locales/inspector.properties");

// Page size for pageup/pagedown
const COLLAPSE_DATA_URL_REGEX = /^data.+base64/;
const COLLAPSE_DATA_URL_LENGTH = 60;

// Contains only void (without end tag) HTML elements
const HTML_VOID_ELEMENTS = [
  "area", "base", "br", "col", "command", "embed",
  "hr", "img", "input", "keygen", "link", "meta", "param", "source",
  "track", "wbr"
];

// Contains only valid computed display property types of the node to display in the
// element markup and their respective title tooltip text.
const DISPLAY_TYPES = {
  "flex": INSPECTOR_L10N.getStr("markupView.display.flex.tooltiptext"),
  "inline-flex": INSPECTOR_L10N.getStr("markupView.display.flex.tooltiptext"),
  "grid": INSPECTOR_L10N.getStr("markupView.display.grid.tooltiptext"),
  "inline-grid": INSPECTOR_L10N.getStr("markupView.display.inlineGrid.tooltiptext"),
  "flow-root": INSPECTOR_L10N.getStr("markupView.display.flowRoot.tooltiptext"),
  "contents": INSPECTOR_L10N.getStr("markupView.display.contents.tooltiptext2"),
};

/**
 * Creates an editor for an Element node.
 *
 * @param  {MarkupContainer} container
 *         The container owning this editor.
 * @param  {Element} node
 *         The node being edited.
 */
function ElementEditor(container, node) {
  this.container = container;
  this.node = node;
  this.markup = this.container.markup;
  this.doc = this.markup.doc;
  this._cssProperties = getCssProperties(this.markup.toolbox);

  this.attrElements = new Map();
  this.animationTimers = {};

  this.elt = null;
  this.tag = null;
  this.closeTag = null;
  this.attrList = null;
  this.newAttr = null;
  this.closeElt = null;

  // Create the main editor
  this.buildMarkup();

  // Make the tag name editable (unless this is a remote node or
  // a document element)
  if (!node.isDocumentElement) {
    // Make the tag optionally tabbable but not by default.
    this.tag.setAttribute("tabindex", "-1");
    editableField({
      element: this.tag,
      multiline: true,
      maxWidth: () => getAutocompleteMaxWidth(this.tag, this.container.elt),
      trigger: "dblclick",
      stopOnReturn: true,
      done: this.onTagEdit.bind(this),
      cssProperties: this._cssProperties
    });
  }

  // Make the new attribute space editable.
  this.newAttr.editMode = editableField({
    element: this.newAttr,
    multiline: true,
    maxWidth: () => getAutocompleteMaxWidth(this.newAttr, this.container.elt),
    trigger: "dblclick",
    stopOnReturn: true,
    contentType: InplaceEditor.CONTENT_TYPES.CSS_MIXED,
    popup: this.markup.popup,
    done: (val, commit) => {
      if (!commit) {
        return;
      }

      let doMods = this._startModifyingAttributes();
      let undoMods = this._startModifyingAttributes();
      this._applyAttributes(val, null, doMods, undoMods);
      this.container.undo.do(() => {
        doMods.apply();
      }, function () {
        undoMods.apply();
      });
    },
    cssProperties: this._cssProperties
  });

  let displayName = this.node.displayName;
  this.tag.textContent = displayName;
  this.closeTag.textContent = displayName;

  let isVoidElement = HTML_VOID_ELEMENTS.includes(displayName);
  if (node.isInHTMLDocument && isVoidElement) {
    this.elt.classList.add("void-element");
  }

  this.update();
  this.initialized = true;
}

ElementEditor.prototype = {
  buildMarkup: function () {
    this.elt = this.doc.createElement("span");
    this.elt.classList.add("editor");

    let open = this.doc.createElement("span");
    open.classList.add("open");
    open.appendChild(this.doc.createTextNode("<"));
    this.elt.appendChild(open);

    this.tag = this.doc.createElement("span");
    this.tag.classList.add("tag", "theme-fg-color3");
    this.tag.setAttribute("tabindex", "-1");
    open.appendChild(this.tag);

    this.attrList = this.doc.createElement("span");
    open.appendChild(this.attrList);

    this.newAttr = this.doc.createElement("span");
    this.newAttr.classList.add("newattr");
    this.newAttr.setAttribute("tabindex", "-1");
    this.newAttr.setAttribute("aria-label",
      INSPECTOR_L10N.getStr("markupView.newAttribute.label"));
    open.appendChild(this.newAttr);

    let closingBracket = this.doc.createElement("span");
    closingBracket.classList.add("closing-bracket");
    closingBracket.textContent = ">";
    open.appendChild(closingBracket);

    let close = this.doc.createElement("span");
    close.classList.add("close");
    close.appendChild(this.doc.createTextNode("</"));
    this.elt.appendChild(close);

    this.closeTag = this.doc.createElement("span");
    this.closeTag.classList.add("tag", "theme-fg-color3");
    close.appendChild(this.closeTag);

    close.appendChild(this.doc.createTextNode(">"));

    this.eventNode = this.doc.createElement("div");
    this.eventNode.classList.add("markupview-events");
    this.eventNode.dataset.event = "true";
    this.eventNode.textContent = "ev";
    this.eventNode.title = INSPECTOR_L10N.getStr("markupView.event.tooltiptext");
    this.elt.appendChild(this.eventNode);

    this.displayNode = this.doc.createElement("div");
    this.displayNode.classList.add("markupview-display");
    this.elt.appendChild(this.displayNode);
  },

  set selected(value) {
    if (this.textEditor) {
      this.textEditor.selected = value;
    }
  },

  flashAttribute: function (attrName) {
    if (this.animationTimers[attrName]) {
      clearTimeout(this.animationTimers[attrName]);
    }

    flashElementOn(this.getAttributeElement(attrName));

    this.animationTimers[attrName] = setTimeout(() => {
      flashElementOff(this.getAttributeElement(attrName));
    }, this.markup.CONTAINER_FLASHING_DURATION);
  },

  /**
   * Returns information about node in the editor.
   *
   * @param  {DOMNode} node
   *         The node to get information from.
   * @return {Object} An object literal with the following information:
   *         {type: "attribute", name: "rel", value: "index", el: node}
   */
  getInfoAtNode: function (node) {
    if (!node) {
      return null;
    }

    let type = null;
    let name = null;
    let value = null;

    // Attribute
    let attribute = node.closest(".attreditor");
    if (attribute) {
      type = "attribute";
      name = attribute.dataset.attr;
      value = attribute.dataset.value;
    }

    return {type, name, value, el: node};
  },

  /**
   * Update the state of the editor from the node.
   */
  update: function () {
    let nodeAttributes = this.node.attributes || [];

    // Keep the data model in sync with attributes on the node.
    let currentAttributes = new Set(nodeAttributes.map(a => a.name));
    for (let name of this.attrElements.keys()) {
      if (!currentAttributes.has(name)) {
        this.removeAttribute(name);
      }
    }

    // Only loop through the current attributes on the node.  Missing
    // attributes have already been removed at this point.
    for (let attr of nodeAttributes) {
      let el = this.attrElements.get(attr.name);
      let valueChanged = el &&
        el.dataset.value !== attr.value;
      let isEditing = el && el.querySelector(".editable").inplaceEditor;
      let canSimplyShowEditor = el && (!valueChanged || isEditing);

      if (canSimplyShowEditor) {
        // Element already exists and doesn't need to be recreated.
        // Just show it (it's hidden by default).
        el.style.removeProperty("display");
      } else {
        // Create a new editor, because the value of an existing attribute
        // has changed.
        let attribute = this._createAttribute(attr, el);
        attribute.style.removeProperty("display");

        // Temporarily flash the attribute to highlight the change.
        // But not if this is the first time the editor instance has
        // been created.
        if (this.initialized) {
          this.flashAttribute(attr.name);
        }
      }
    }

    // Update the event bubble display
    this.eventNode.style.display = this.node.hasEventListeners ? "inline-block" : "none";

    // Update the display type node
    let showDisplayNode = this.node.displayType in DISPLAY_TYPES;
    this.displayNode.textContent = this.node.displayType;
    this.displayNode.dataset.display = showDisplayNode ? this.node.displayType : "";
    this.displayNode.style.display = showDisplayNode ? "inline-block" : "none";
    this.displayNode.title = showDisplayNode ? DISPLAY_TYPES[this.node.displayType] : "";

    this.updateTextEditor();
  },

  /**
   * Update the inline text editor in case of a single text child node.
   */
  updateTextEditor: function () {
    let node = this.node.inlineTextChild;

    if (this.textEditor && this.textEditor.node != node) {
      this.elt.removeChild(this.textEditor.elt);
      this.textEditor = null;
    }

    if (node && !this.textEditor) {
      // Create a text editor added to this editor.
      // This editor won't receive an update automatically, so we rely on
      // child text editors to let us know that we need updating.
      this.textEditor = new TextEditor(this.container, node, "text");
      this.elt.insertBefore(this.textEditor.elt, this.elt.querySelector(".close"));
    }

    if (this.textEditor) {
      this.textEditor.update();
    }
  },

  _startModifyingAttributes: function () {
    return this.node.startModifyingAttributes();
  },

  /**
   * Get the element used for one of the attributes of this element.
   *
   * @param  {String} attrName
   *         The name of the attribute to get the element for
   * @return {DOMNode}
   */
  getAttributeElement: function (attrName) {
    return this.attrList.querySelector(
      ".attreditor[data-attr=" + CSS.escape(attrName) + "] .attr-value");
  },

  /**
   * Remove an attribute from the attrElements object and the DOM.
   *
   * @param  {String} attrName
   *         The name of the attribute to remove
   */
  removeAttribute: function (attrName) {
    let attr = this.attrElements.get(attrName);
    if (attr) {
      this.attrElements.delete(attrName);
      attr.remove();
    }
  },

  _createAttribute: function (attribute, before = null) {
    let attr = this.doc.createElement("span");
    attr.dataset.attr = attribute.name;
    attr.dataset.value = attribute.value;
    attr.classList.add("attreditor");
    attr.style.display = "none";

    attr.appendChild(this.doc.createTextNode(" "));

    let inner = this.doc.createElement("span");
    inner.classList.add("editable");
    inner.setAttribute("tabindex", this.container.canFocus ? "0" : "-1");
    attr.appendChild(inner);

    let name = this.doc.createElement("span");
    name.classList.add("attr-name");
    name.classList.add("theme-fg-color2");
    inner.appendChild(name);

    inner.appendChild(this.doc.createTextNode('="'));

    let val = this.doc.createElement("span");
    val.classList.add("attr-value");
    val.classList.add("theme-fg-color6");
    inner.appendChild(val);

    inner.appendChild(this.doc.createTextNode('"'));

    // Double quotes need to be handled specially to prevent DOMParser failing.
    // name="v"a"l"u"e" when editing -> name='v"a"l"u"e"'
    // name="v'a"l'u"e" when editing -> name="v'a&quot;l'u&quot;e"
    let editValueDisplayed = attribute.value || "";
    let hasDoubleQuote = editValueDisplayed.includes('"');
    let hasSingleQuote = editValueDisplayed.includes("'");
    let initial = attribute.name + '="' + editValueDisplayed + '"';

    // Can't just wrap value with ' since the value contains both " and '.
    if (hasDoubleQuote && hasSingleQuote) {
      editValueDisplayed = editValueDisplayed.replace(/\"/g, "&quot;");
      initial = attribute.name + '="' + editValueDisplayed + '"';
    }

    // Wrap with ' since there are no single quotes in the attribute value.
    if (hasDoubleQuote && !hasSingleQuote) {
      initial = attribute.name + "='" + editValueDisplayed + "'";
    }

    // Make the attribute editable.
    attr.editMode = editableField({
      element: inner,
      trigger: "dblclick",
      stopOnReturn: true,
      selectAll: false,
      initial: initial,
      multiline: true,
      maxWidth: () => getAutocompleteMaxWidth(inner, this.container.elt),
      contentType: InplaceEditor.CONTENT_TYPES.CSS_MIXED,
      popup: this.markup.popup,
      start: (editor, event) => {
        // If the editing was started inside the name or value areas,
        // select accordingly.
        if (event && event.target === name) {
          editor.input.setSelectionRange(0, name.textContent.length);
        } else if (event && event.target.closest(".attr-value") === val) {
          let length = editValueDisplayed.length;
          let editorLength = editor.input.value.length;
          let start = editorLength - (length + 1);
          editor.input.setSelectionRange(start, start + length);
        } else {
          editor.input.select();
        }
      },
      done: (newValue, commit, direction) => {
        if (!commit || newValue === initial) {
          return;
        }

        let doMods = this._startModifyingAttributes();
        let undoMods = this._startModifyingAttributes();

        // Remove the attribute stored in this editor and re-add any attributes
        // parsed out of the input element. Restore original attribute if
        // parsing fails.
        this.refocusOnEdit(attribute.name, attr, direction);
        this._saveAttribute(attribute.name, undoMods);
        doMods.removeAttribute(attribute.name);
        this._applyAttributes(newValue, attr, doMods, undoMods);
        this.container.undo.do(() => {
          doMods.apply();
        }, () => {
          undoMods.apply();
        });
      },
      cssProperties: this._cssProperties
    });

    // Figure out where we should place the attribute.
    if (attribute.name == "id") {
      before = this.attrList.firstChild;
    } else if (attribute.name == "class") {
      let idNode = this.attrElements.get("id");
      before = idNode ? idNode.nextSibling : this.attrList.firstChild;
    }
    this.attrList.insertBefore(attr, before);

    this.removeAttribute(attribute.name);
    this.attrElements.set(attribute.name, attr);

    // Parse the attribute value to detect whether there are linkable parts in
    // it (make sure to pass a complete list of existing attributes to the
    // parseAttribute function, by concatenating attribute, because this could
    // be a newly added attribute not yet on this.node).
    let attributes = this.node.attributes.filter(existingAttribute => {
      return existingAttribute.name !== attribute.name;
    });
    attributes.push(attribute);
    let parsedLinksData = parseAttribute(this.node.namespaceURI,
      this.node.tagName, attributes, attribute.name);

    // Create links in the attribute value, and collapse long attributes if
    // needed.
    let collapse = value => {
      if (value && value.match(COLLAPSE_DATA_URL_REGEX)) {
        return truncateString(value, COLLAPSE_DATA_URL_LENGTH);
      }
      return this.markup.collapseAttributes
        ? truncateString(value, this.markup.collapseAttributeLength)
        : value;
    };

    val.innerHTML = "";
    for (let token of parsedLinksData) {
      if (token.type === "string") {
        val.appendChild(this.doc.createTextNode(collapse(token.value)));
      } else {
        let link = this.doc.createElement("span");
        link.classList.add("link");
        link.setAttribute("data-type", token.type);
        link.setAttribute("data-link", token.value);
        link.textContent = collapse(token.value);
        val.appendChild(link);
      }
    }

    name.textContent = attribute.name;

    return attr;
  },

  /**
   * Parse a user-entered attribute string and apply the resulting
   * attributes to the node. This operation is undoable.
   *
   * @param  {String} value
   *         The user-entered value.
   * @param  {DOMNode} attrNode
   *         The attribute editor that created this
   *         set of attributes, used to place new attributes where the
   *         user put them.
   */
  _applyAttributes: function (value, attrNode, doMods, undoMods) {
    let attrs = parseAttributeValues(value, this.doc);
    for (let attr of attrs) {
      // Create an attribute editor next to the current attribute if needed.
      this._createAttribute(attr, attrNode ? attrNode.nextSibling : null);
      this._saveAttribute(attr.name, undoMods);
      doMods.setAttribute(attr.name, attr.value);
    }
  },

  /**
   * Saves the current state of the given attribute into an attribute
   * modification list.
   */
  _saveAttribute: function (name, undoMods) {
    let node = this.node;
    if (node.hasAttribute(name)) {
      let oldValue = node.getAttribute(name);
      undoMods.setAttribute(name, oldValue);
    } else {
      undoMods.removeAttribute(name);
    }
  },

  /**
   * Listen to mutations, and when the attribute list is regenerated
   * try to focus on the attribute after the one that's being edited now.
   * If the attribute order changes, go to the beginning of the attribute list.
   */
  refocusOnEdit: function (attrName, attrNode, direction) {
    // Only allow one refocus on attribute change at a time, so when there's
    // more than 1 request in parallel, the last one wins.
    if (this._editedAttributeObserver) {
      this.markup.inspector.off("markupmutation", this._editedAttributeObserver);
      this._editedAttributeObserver = null;
    }

    let activeElement = this.markup.doc.activeElement;
    if (!activeElement || !activeElement.inplaceEditor) {
      // The focus was already removed from the current inplace editor, we should not
      // refocus the editable attribute.
      return;
    }

    let container = this.markup.getContainer(this.node);

    let activeAttrs = [...this.attrList.childNodes]
      .filter(el => el.style.display != "none");
    let attributeIndex = activeAttrs.indexOf(attrNode);

    let onMutations = this._editedAttributeObserver = (e, mutations) => {
      let isDeletedAttribute = false;
      let isNewAttribute = false;

      for (let mutation of mutations) {
        let inContainer =
          this.markup.getContainer(mutation.target) === container;
        if (!inContainer) {
          continue;
        }

        let isOriginalAttribute = mutation.attributeName === attrName;

        isDeletedAttribute = isDeletedAttribute || isOriginalAttribute &&
                             mutation.newValue === null;
        isNewAttribute = isNewAttribute || mutation.attributeName !== attrName;
      }

      let isModifiedOrder = isDeletedAttribute && isNewAttribute;
      this._editedAttributeObserver = null;

      // "Deleted" attributes are merely hidden, so filter them out.
      let visibleAttrs = [...this.attrList.childNodes]
        .filter(el => el.style.display != "none");
      let activeEditor;
      if (visibleAttrs.length > 0) {
        if (!direction) {
          // No direction was given; stay on current attribute.
          activeEditor = visibleAttrs[attributeIndex];
        } else if (isModifiedOrder) {
          // The attribute was renamed, reordering the existing attributes.
          // So let's go to the beginning of the attribute list for consistency.
          activeEditor = visibleAttrs[0];
        } else {
          let newAttributeIndex;
          if (isDeletedAttribute) {
            newAttributeIndex = attributeIndex;
          } else if (direction == Services.focus.MOVEFOCUS_FORWARD) {
            newAttributeIndex = attributeIndex + 1;
          } else if (direction == Services.focus.MOVEFOCUS_BACKWARD) {
            newAttributeIndex = attributeIndex - 1;
          }

          // The number of attributes changed (deleted), or we moved through
          // the array so check we're still within bounds.
          if (newAttributeIndex >= 0 &&
              newAttributeIndex <= visibleAttrs.length - 1) {
            activeEditor = visibleAttrs[newAttributeIndex];
          }
        }
      }

      // Either we have no attributes left,
      // or we just edited the last attribute and want to move on.
      if (!activeEditor) {
        activeEditor = this.newAttr;
      }

      // Refocus was triggered by tab or shift-tab.
      // Continue in edit mode.
      if (direction) {
        activeEditor.editMode();
      } else {
        // Refocus was triggered by enter.
        // Exit edit mode (but restore focus).
        let editable = activeEditor === this.newAttr ?
          activeEditor : activeEditor.querySelector(".editable");
        editable.focus();
      }

      this.markup.emit("refocusedonedit");
    };

    // Start listening for mutations until we find an attributes change
    // that modifies this attribute.
    this.markup.inspector.once("markupmutation", onMutations);
  },

  /**
   * Called when the tag name editor has is done editing.
   */
  onTagEdit: function (newTagName, isCommit) {
    if (!isCommit ||
        newTagName.toLowerCase() === this.node.tagName.toLowerCase() ||
        !("editTagName" in this.markup.walker)) {
      return;
    }

    // Changing the tagName removes the node. Make sure the replacing node gets
    // selected afterwards.
    this.markup.reselectOnRemoved(this.node, "edittagname");
    this.markup.walker.editTagName(this.node, newTagName).catch(() => {
      // Failed to edit the tag name, cancel the reselection.
      this.markup.cancelReselectOnRemoved();
    });
  },

  destroy: function () {
    for (let key in this.animationTimers) {
      clearTimeout(this.animationTimers[key]);
    }
    this.animationTimers = null;
  }
};

module.exports = ElementEditor;
PK
!<�ѡ%��Mchrome/devtools/modules/devtools/client/inspector/markup/views/html-editor.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Editor = require("devtools/client/sourceeditor/editor");
const Services = require("Services");
const EventEmitter = require("devtools/shared/old-event-emitter");

/**
 * A wrapper around the Editor component, that allows editing of HTML.
 *
 * The main functionality this provides around the Editor is the ability
 * to show/hide/position an editor inplace. It only appends once to the
 * body, and uses CSS to position the editor.  The reason it is done this
 * way is that the editor is loaded in an iframe, and calling appendChild
 * causes it to reload.
 *
 * Meant to be embedded inside of an HTML page, as in markup.xhtml.
 *
 * @param  {HTMLDocument} htmlDocument
 *         The document to attach the editor to.  Will also use this
 *         document as a basis for listening resize events.
 */
function HTMLEditor(htmlDocument) {
  this.doc = htmlDocument;
  this.container = this.doc.createElement("div");
  this.container.className = "html-editor theme-body";
  this.container.style.display = "none";
  this.editorInner = this.doc.createElement("div");
  this.editorInner.className = "html-editor-inner";
  this.container.appendChild(this.editorInner);

  this.doc.body.appendChild(this.container);
  this.hide = this.hide.bind(this);
  this.refresh = this.refresh.bind(this);

  EventEmitter.decorate(this);

  this.doc.defaultView.addEventListener("resize",
    this.refresh, true);

  let config = {
    mode: Editor.modes.html,
    lineWrapping: true,
    styleActiveLine: false,
    extraKeys: {},
    theme: "mozilla markup-view"
  };

  config.extraKeys[ctrl("Enter")] = this.hide;
  config.extraKeys.F2 = this.hide;
  config.extraKeys.Esc = this.hide.bind(this, false);

  this.container.addEventListener("click", this.hide);
  this.editorInner.addEventListener("click", stopPropagation);
  this.editor = new Editor(config);

  this.editor.appendToLocalElement(this.editorInner);
  this.hide(false);
}

HTMLEditor.prototype = {

  /**
   * Need to refresh position by manually setting CSS values, so this will
   * need to be called on resizes and other sizing changes.
   */
  refresh: function () {
    let element = this._attachedElement;

    if (element) {
      this.container.style.top = element.offsetTop + "px";
      this.container.style.left = element.offsetLeft + "px";
      this.container.style.width = element.offsetWidth + "px";
      this.container.style.height = element.parentNode.offsetHeight + "px";
      this.editor.refresh();
    }
  },

  /**
   * Anchor the editor to a particular element.
   *
   * @param  {DOMNode} element
   *         The element that the editor will be anchored to.
   *         Should belong to the HTMLDocument passed into the constructor.
   */
  _attach: function (element) {
    this._detach();
    this._attachedElement = element;
    element.classList.add("html-editor-container");
    this.refresh();
  },

  /**
   * Unanchor the editor from an element.
   */
  _detach: function () {
    if (this._attachedElement) {
      this._attachedElement.classList.remove("html-editor-container");
      this._attachedElement = undefined;
    }
  },

  /**
   * Anchor the editor to a particular element, and show the editor.
   *
   * @param  {DOMNode} element
   *         The element that the editor will be anchored to.
   *         Should belong to the HTMLDocument passed into the constructor.
   * @param  {String} text
   *         Value to set the contents of the editor to
   * @param  {Function} cb
   *         The function to call when hiding
   */
  show: function (element, text) {
    if (this._visible) {
      return;
    }

    this._originalValue = text;
    this.editor.setText(text);
    this._attach(element);
    this.container.style.display = "flex";
    this._visible = true;

    this.editor.refresh();
    this.editor.focus();
    this.editor.clearHistory();

    this.emit("popupshown");
  },

  /**
   * Hide the editor, optionally committing the changes
   *
   * @param  {Boolean} shouldCommit
   *         A change will be committed by default.  If this param
   *         strictly equals false, no change will occur.
   */
  hide: function (shouldCommit) {
    if (!this._visible) {
      return;
    }

    this.container.style.display = "none";
    this._detach();

    let newValue = this.editor.getText();
    let valueHasChanged = this._originalValue !== newValue;
    let preventCommit = shouldCommit === false || !valueHasChanged;
    this._originalValue = undefined;
    this._visible = undefined;
    this.emit("popuphidden", !preventCommit, newValue);
  },

  /**
   * Destroy this object and unbind all event handlers
   */
  destroy: function () {
    this.doc.defaultView.removeEventListener("resize",
      this.refresh, true);
    this.container.removeEventListener("click", this.hide);
    this.editorInner.removeEventListener("click", stopPropagation);

    this.hide(false);
    this.container.remove();
    this.editor.destroy();
  }
};

function ctrl(k) {
  return (Services.appinfo.OS == "Darwin" ? "Cmd-" : "Ctrl-") + k;
}

function stopPropagation(e) {
  e.stopPropagation();
}

module.exports = HTMLEditor;
PK
!<k�Y�YRchrome/devtools/modules/devtools/client/inspector/markup/views/markup-container.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {Task} = require("devtools/shared/task");
const {KeyCodes} = require("devtools/client/shared/keycodes");
const {flashElementOn, flashElementOff} =
      require("devtools/client/inspector/markup/utils");

const DRAG_DROP_MIN_INITIAL_DISTANCE = 10;

const TYPES = {
  TEXT_CONTAINER: "textcontainer",
  ELEMENT_CONTAINER: "elementcontainer",
  READ_ONLY_CONTAINER: "readonlycontainer",
};

/**
 * The main structure for storing a document node in the markup
 * tree.  Manages creation of the editor for the node and
 * a <ul> for placing child elements, and expansion/collapsing
 * of the element.
 *
 * This should not be instantiated directly, instead use one of:
 *    MarkupReadOnlyContainer
 *    MarkupTextContainer
 *    MarkupElementContainer
 */
function MarkupContainer() { }

/**
 * Unique identifier used to set markup container node id.
 * @type {Number}
 */
let markupContainerID = 0;

MarkupContainer.prototype = {
  /*
   * Initialize the MarkupContainer.  Should be called while one
   * of the other contain classes is instantiated.
   *
   * @param  {MarkupView} markupView
   *         The markup view that owns this container.
   * @param  {NodeFront} node
   *         The node to display.
   * @param  {String} type
   *         The type of container to build. One of TYPES.TEXT_CONTAINER,
   *         TYPES.ELEMENT_CONTAINER, TYPES.READ_ONLY_CONTAINER
   */
  initialize: function (markupView, node, type) {
    this.markup = markupView;
    this.node = node;
    this.type = type;
    this.undo = this.markup.undo;
    this.win = this.markup._frame.contentWindow;
    this.id = "treeitem-" + markupContainerID++;
    this.htmlElt = this.win.document.documentElement;

    this.buildMarkup();

    this.elt.container = this;

    this._onMouseDown = this._onMouseDown.bind(this);
    this._onToggle = this._onToggle.bind(this);
    this._onKeyDown = this._onKeyDown.bind(this);

    // Binding event listeners
    this.elt.addEventListener("mousedown", this._onMouseDown);
    this.elt.addEventListener("dblclick", this._onToggle);
    if (this.expander) {
      this.expander.addEventListener("click", this._onToggle);
    }

    // Marking the node as shown or hidden
    this.updateIsDisplayed();
  },

  buildMarkup: function () {
    this.elt = this.win.document.createElement("li");
    this.elt.classList.add("child", "collapsed");
    this.elt.setAttribute("role", "presentation");

    this.tagLine = this.win.document.createElement("div");
    this.tagLine.setAttribute("id", this.id);
    this.tagLine.classList.add("tag-line");
    this.tagLine.setAttribute("role", "treeitem");
    this.tagLine.setAttribute("aria-level", this.level);
    this.tagLine.setAttribute("aria-grabbed", this.isDragging);
    this.elt.appendChild(this.tagLine);

    this.tagState = this.win.document.createElement("span");
    this.tagState.classList.add("tag-state");
    this.tagState.setAttribute("role", "presentation");
    this.tagLine.appendChild(this.tagState);

    if (this.type !== TYPES.TEXT_CONTAINER) {
      this.expander = this.win.document.createElement("span");
      this.expander.classList.add("theme-twisty", "expander");
      this.expander.setAttribute("role", "presentation");
      this.tagLine.appendChild(this.expander);
    }

    this.children = this.win.document.createElement("ul");
    this.children.classList.add("children");
    this.children.setAttribute("role", "group");
    this.elt.appendChild(this.children);
  },

  toString: function () {
    return "[MarkupContainer for " + this.node + "]";
  },

  isPreviewable: function () {
    if (this.node.tagName && !this.node.isPseudoElement) {
      let tagName = this.node.tagName.toLowerCase();
      let srcAttr = this.editor.getAttributeElement("src");
      let isImage = tagName === "img" && srcAttr;
      let isCanvas = tagName === "canvas";

      return isImage || isCanvas;
    }

    return false;
  },

  /**
   * Show whether the element is displayed or not
   * If an element has the attribute `display: none` or has been hidden with
   * the H key, it is not displayed (faded in markup view).
   * Otherwise, it is displayed.
   */
  updateIsDisplayed: function () {
    this.elt.classList.remove("not-displayed");
    if (!this.node.isDisplayed || this.node.hidden) {
      this.elt.classList.add("not-displayed");
    }
  },

  /**
   * True if the current node has children. The MarkupView
   * will set this attribute for the MarkupContainer.
   */
  _hasChildren: false,

  get hasChildren() {
    return this._hasChildren;
  },

  set hasChildren(value) {
    this._hasChildren = value;
    this.updateExpander();
  },

  /**
   * A list of all elements with tabindex that are not in container's children.
   */
  get focusableElms() {
    return [...this.tagLine.querySelectorAll("[tabindex]")];
  },

  /**
   * An indicator that the container internals are focusable.
   */
  get canFocus() {
    return this._canFocus;
  },

  /**
   * Toggle focusable state for container internals.
   */
  set canFocus(value) {
    if (this._canFocus === value) {
      return;
    }

    this._canFocus = value;

    if (value) {
      this.tagLine.addEventListener("keydown", this._onKeyDown, true);
      this.focusableElms.forEach(elm => elm.setAttribute("tabindex", "0"));
    } else {
      this.tagLine.removeEventListener("keydown", this._onKeyDown, true);
      // Exclude from tab order.
      this.focusableElms.forEach(elm => elm.setAttribute("tabindex", "-1"));
    }
  },

  /**
   * If conatiner and its contents are focusable, exclude them from tab order,
   * and, if necessary, remove focus.
   */
  clearFocus: function () {
    if (!this.canFocus) {
      return;
    }

    this.canFocus = false;
    let doc = this.markup.doc;

    if (!doc.activeElement || doc.activeElement === doc.body) {
      return;
    }

    let parent = doc.activeElement;

    while (parent && parent !== this.elt) {
      parent = parent.parentNode;
    }

    if (parent) {
      doc.activeElement.blur();
    }
  },

  /**
   * True if the current node can be expanded.
   */
  get canExpand() {
    return this._hasChildren && !this.node.inlineTextChild;
  },

  /**
   * True if this is the root <html> element and can't be collapsed.
   */
  get mustExpand() {
    return this.node._parent === this.markup.walker.rootNode;
  },

  /**
   * True if current node can be expanded and collapsed.
   */
  get showExpander() {
    return this.canExpand && !this.mustExpand;
  },

  updateExpander: function () {
    if (!this.expander) {
      return;
    }

    if (this.showExpander) {
      this.elt.classList.add("expandable");
      this.expander.style.visibility = "visible";
      // Update accessibility expanded state.
      this.tagLine.setAttribute("aria-expanded", this.expanded);
    } else {
      this.elt.classList.remove("expandable");
      this.expander.style.visibility = "hidden";
      // No need for accessible expanded state indicator when expander is not
      // shown.
      this.tagLine.removeAttribute("aria-expanded");
    }
  },

  /**
   * If current node has no children, ignore them. Otherwise, consider them a
   * group from the accessibility point of view.
   */
  setChildrenRole: function () {
    this.children.setAttribute("role",
      this.hasChildren ? "group" : "presentation");
  },

  /**
   * Set an appropriate DOM tree depth level for a node and its subtree.
   */
  updateLevel: function () {
    // ARIA level should already be set when the container markup is created.
    let currentLevel = this.tagLine.getAttribute("aria-level");
    let newLevel = this.level;
    if (currentLevel === newLevel) {
      // If level did not change, ignore this node and its subtree.
      return;
    }

    this.tagLine.setAttribute("aria-level", newLevel);
    let childContainers = this.getChildContainers();
    if (childContainers) {
      childContainers.forEach(container => container.updateLevel());
    }
  },

  /**
   * If the node has children, return the list of containers for all these
   * children.
   */
  getChildContainers: function () {
    if (!this.hasChildren) {
      return null;
    }

    return [...this.children.children].filter(node => node.container)
                                      .map(node => node.container);
  },

  /**
   * True if the node has been visually expanded in the tree.
   */
  get expanded() {
    return !this.elt.classList.contains("collapsed");
  },

  setExpanded: function (value) {
    if (!this.expander) {
      return;
    }

    if (!this.canExpand) {
      value = false;
    }

    if (this.mustExpand) {
      value = true;
    }

    if (value && this.elt.classList.contains("collapsed")) {
      this.showCloseTagLine();

      this.elt.classList.remove("collapsed");
      this.expander.setAttribute("open", "");
      this.hovered = false;
      this.markup.emit("expanded");
    } else if (!value) {
      this.hideCloseTagLine();

      this.elt.classList.add("collapsed");
      this.expander.removeAttribute("open");
      this.markup.emit("collapsed");
    }

    if (this.showExpander) {
      this.tagLine.setAttribute("aria-expanded", this.expanded);
    }
  },

  /**
   * Expanding a node means cloning its "inline" closing tag into a new
   * tag-line that the user can interact with and showing the children.
   */
  showCloseTagLine: function () {
    // Only element containers display a closing tag line. #document has no closing line.
    if (this.type !== TYPES.ELEMENT_CONTAINER) {
      return;
    }

    // Retrieve the closest .close node for this container.
    let closingTag = this.elt.querySelector(".close");
    if (!closingTag) {
      return;
    }

    // Create the closing tag-line element if not already created.
    if (!this.closeTagLine) {
      let line = this.markup.doc.createElement("div");
      line.classList.add("tag-line");
      // Closing tag is not important for accessibility.
      line.setAttribute("role", "presentation");

      let tagState = this.markup.doc.createElement("div");
      tagState.classList.add("tag-state");
      line.appendChild(tagState);

      line.appendChild(closingTag.cloneNode(true));

      flashElementOff(line);
      this.closeTagLine = line;
    }
    this.elt.appendChild(this.closeTagLine);
  },

  /**
   * Hide the closing tag-line element which should only be displayed when the container
   * is expanded.
   */
  hideCloseTagLine: function () {
    if (!this.closeTagLine) {
      return;
    }

    this.elt.removeChild(this.closeTagLine);
    this.closeTagLine = undefined;
  },

  parentContainer: function () {
    return this.elt.parentNode ? this.elt.parentNode.container : null;
  },

  /**
   * Determine tree depth level of a given node. This is used to specify ARIA
   * level for node tree items and to give them better semantic context.
   */
  get level() {
    let level = 1;
    let parent = this.node.parentNode();
    while (parent && parent !== this.markup.walker.rootNode) {
      level++;
      parent = parent.parentNode();
    }
    return level;
  },

  _isDragging: false,
  _dragStartY: 0,

  set isDragging(isDragging) {
    let rootElt = this.markup.getContainer(this.markup._rootNode).elt;
    this._isDragging = isDragging;
    this.markup.isDragging = isDragging;
    this.tagLine.setAttribute("aria-grabbed", isDragging);

    if (isDragging) {
      this.htmlElt.classList.add("dragging");
      this.elt.classList.add("dragging");
      this.markup.doc.body.classList.add("dragging");
      rootElt.setAttribute("aria-dropeffect", "move");
    } else {
      this.htmlElt.classList.remove("dragging");
      this.elt.classList.remove("dragging");
      this.markup.doc.body.classList.remove("dragging");
      rootElt.setAttribute("aria-dropeffect", "none");
    }
  },

  get isDragging() {
    return this._isDragging;
  },

  /**
   * Check if element is draggable.
   */
  isDraggable: function () {
    let tagName = this.node.tagName && this.node.tagName.toLowerCase();

    return !this.node.isPseudoElement &&
           !this.node.isAnonymous &&
           !this.node.isDocumentElement &&
           tagName !== "body" &&
           tagName !== "head" &&
           this.win.getSelection().isCollapsed &&
           this.node.parentNode().tagName !== null;
  },

  /**
   * Move keyboard focus to a next/previous focusable element inside container
   * that is not part of its children (only if current focus is on first or last
   * element).
   *
   * @param  {DOMNode} current  currently focused element
   * @param  {Boolean} back     direction
   * @return {DOMNode}          newly focused element if any
   */
  _wrapMoveFocus: function (current, back) {
    let elms = this.focusableElms;
    let next;
    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: function (event) {
    let {target, keyCode, shiftKey} = event;
    let isInput = this.markup._isInputOrTextarea(target);

    // Ignore all keystrokes that originated in editors except for when 'Tab' is
    // pressed.
    if (isInput && keyCode !== KeyCodes.DOM_VK_TAB) {
      return;
    }

    switch (keyCode) {
      case KeyCodes.DOM_VK_TAB:
        // Only handle 'Tab' if tabbable element is on the edge (first or last).
        if (isInput) {
          // Corresponding tabbable element is editor's next sibling.
          let next = this._wrapMoveFocus(target.nextSibling, shiftKey);
          if (next) {
            event.preventDefault();
            // Keep the editing state if possible.
            if (next._editable) {
              let e = this.markup.doc.createEvent("Event");
              e.initEvent(next._trigger, true, true);
              next.dispatchEvent(e);
            }
          }
        } else {
          let next = this._wrapMoveFocus(target, shiftKey);
          if (next) {
            event.preventDefault();
          }
        }
        break;
      case KeyCodes.DOM_VK_ESCAPE:
        this.clearFocus();
        this.markup.getContainer(this.markup._rootNode).elt.focus();
        if (this.isDragging) {
          // Escape when dragging is handled by markup view itself.
          return;
        }
        event.preventDefault();
        break;
      default:
        return;
    }
    event.stopPropagation();
  },

  _onMouseDown: function (event) {
    let {target, button, metaKey, ctrlKey} = event;
    let isLeftClick = button === 0;
    let isMiddleClick = button === 1;
    let isMetaClick = isLeftClick && (metaKey || ctrlKey);

    // The "show more nodes" button already has its onclick, so early return.
    if (target.nodeName === "button") {
      return;
    }

    // target is the MarkupContainer itself.
    this.hovered = false;
    this.markup.navigate(this);
    // Make container tabbable descendants tabbable and focus in.
    this.canFocus = true;
    this.focus();
    event.stopPropagation();

    // Preventing the default behavior will avoid the body to gain focus on
    // mouseup (through bubbling) when clicking on a non focusable node in the
    // line. So, if the click happened outside of a focusable element, do
    // prevent the default behavior, so that the tagname or textcontent gains
    // focus.
    if (!target.closest(".editor [tabindex]")) {
      event.preventDefault();
    }

    // Follow attribute links if middle or meta click.
    if (isMiddleClick || isMetaClick) {
      let link = target.dataset.link;
      let type = target.dataset.type;
      // Make container tabbable descendants not tabbable (by default).
      this.canFocus = false;
      this.markup.inspector.followAttributeLink(type, link);
      return;
    }

    // Start node drag & drop (if the mouse moved, see _onMouseMove).
    if (isLeftClick && this.isDraggable()) {
      this._isPreDragging = true;
      this._dragStartY = event.pageY;
      this.markup._draggedContainer = this;
    }
  },

  /**
   * On mouse up, stop dragging.
   * This handler is called from the markup view, to reduce number of listeners.
   */
  onMouseUp: Task.async(function* () {
    this._isPreDragging = false;
    this.markup._draggedContainer = null;

    if (this.isDragging) {
      this.cancelDragging();

      let dropTargetNodes = this.markup.dropTargetNodes;

      if (!dropTargetNodes) {
        return;
      }

      yield this.markup.walker.insertBefore(this.node, dropTargetNodes.parent,
                                            dropTargetNodes.nextSibling);
      this.markup.emit("drop-completed");
    }
  }),

  /**
   * On mouse move, move the dragged element and indicate the drop target.
   * This handler is called from the markup view, to reduce number of listeners.
   */
  onMouseMove: function (event) {
    // If this is the first move after mousedown, only start dragging after the
    // mouse has travelled a few pixels and then indicate the start position.
    let initialDiff = Math.abs(event.pageY - this._dragStartY);
    if (this._isPreDragging && initialDiff >= DRAG_DROP_MIN_INITIAL_DISTANCE) {
      this._isPreDragging = false;
      this.isDragging = true;

      // If this is the last child, use the closing <div.tag-line> of parent as
      // indicator.
      let position = this.elt.nextElementSibling ||
                     this.markup.getContainer(this.node.parentNode())
                                .closeTagLine;
      this.markup.indicateDragTarget(position);
    }

    if (this.isDragging) {
      let x = 0;
      let y = event.pageY - this.win.scrollY;

      // Ensure we keep the dragged element within the markup view.
      if (y < 0) {
        y = 0;
      } else if (y >= this.markup.doc.body.offsetHeight - this.win.scrollY) {
        y = this.markup.doc.body.offsetHeight - this.win.scrollY - 1;
      }

      let diff = y - this._dragStartY + this.win.scrollY;
      this.elt.style.top = diff + "px";

      let el = this.markup.doc.elementFromPoint(x, y);
      this.markup.indicateDropTarget(el);
    }
  },

  cancelDragging: function () {
    if (!this.isDragging) {
      return;
    }

    this._isPreDragging = false;
    this.isDragging = false;
    this.elt.style.removeProperty("top");
  },

  /**
   * Temporarily flash the container to attract attention.
   * Used for markup mutations.
   */
  flashMutation: function () {
    if (!this.selected) {
      flashElementOn(this.tagState, this.editor.elt);
      if (this._flashMutationTimer) {
        clearTimeout(this._flashMutationTimer);
        this._flashMutationTimer = null;
      }
      this._flashMutationTimer = setTimeout(() => {
        flashElementOff(this.tagState, this.editor.elt);
      }, this.markup.CONTAINER_FLASHING_DURATION);
    }
  },

  _hovered: false,

  /**
   * Highlight the currently hovered tag + its closing tag if necessary
   * (that is if the tag is expanded)
   */
  set hovered(value) {
    this.tagState.classList.remove("flash-out");
    this._hovered = value;
    if (value) {
      if (!this.selected) {
        this.tagState.classList.add("tag-hover");
      }
      if (this.closeTagLine) {
        this.closeTagLine.querySelector(".tag-state").classList.add("tag-hover");
      }
    } else {
      this.tagState.classList.remove("tag-hover");
      if (this.closeTagLine) {
        this.closeTagLine.querySelector(".tag-state").classList.remove("tag-hover");
      }
    }
  },

  /**
   * True if the container is visible in the markup tree.
   */
  get visible() {
    return this.elt.getBoundingClientRect().height > 0;
  },

  /**
   * True if the container is currently selected.
   */
  _selected: false,

  get selected() {
    return this._selected;
  },

  set selected(value) {
    this.tagState.classList.remove("flash-out");
    this._selected = value;
    this.editor.selected = value;
    // Markup tree item should have accessible selected state.
    this.tagLine.setAttribute("aria-selected", value);
    if (this._selected) {
      let container = this.markup.getContainer(this.markup._rootNode);
      if (container) {
        container.elt.setAttribute("aria-activedescendant", this.id);
      }
      this.tagLine.setAttribute("selected", "");
      this.tagState.classList.add("theme-selected");
    } else {
      this.tagLine.removeAttribute("selected");
      this.tagState.classList.remove("theme-selected");
    }
  },

  /**
   * Update the container's editor to the current state of the
   * viewed node.
   */
  update: function () {
    if (this.node.pseudoClassLocks.length) {
      this.elt.classList.add("pseudoclass-locked");
    } else {
      this.elt.classList.remove("pseudoclass-locked");
    }

    this.updateIsDisplayed();

    if (this.editor.update) {
      this.editor.update();
    }
  },

  /**
   * Try to put keyboard focus on the current editor.
   */
  focus: function () {
    // Elements with tabindex of -1 are not focusable.
    let focusable = this.editor.elt.querySelector("[tabindex='0']");
    if (focusable) {
      focusable.focus();
    }
  },

  _onToggle: function (event) {
    // Prevent the html tree from expanding when an event bubble or display node is
    // clicked.
    if (event.target.dataset.event || event.target.dataset.display) {
      event.stopPropagation();
      return;
    }

    this.markup.navigate(this);
    if (this.hasChildren) {
      this.markup.setNodeExpanded(this.node, !this.expanded, event.altKey);
    }
    event.stopPropagation();
  },

  /**
   * Get rid of event listeners and references, when the container is no longer
   * needed
   */
  destroy: function () {
    // Remove event listeners
    this.elt.removeEventListener("mousedown", this._onMouseDown);
    this.elt.removeEventListener("dblclick", this._onToggle);
    this.tagLine.removeEventListener("keydown", this._onKeyDown, true);

    if (this.markup._draggedContainer === this) {
      this.markup._draggedContainer = null;
    }

    this.win = null;
    this.htmlElt = null;

    if (this.expander) {
      this.expander.removeEventListener("click", this._onToggle);
    }

    // Recursively destroy children containers
    let firstChild = this.children.firstChild;
    while (firstChild) {
      // Not all children of a container are containers themselves
      // ("show more nodes" button is one example)
      if (firstChild.container) {
        firstChild.container.destroy();
      }
      this.children.removeChild(firstChild);
      firstChild = this.children.firstChild;
    }

    this.editor.destroy();
  }
};

module.exports = MarkupContainer;
PK
!<YO�K��Uchrome/devtools/modules/devtools/client/inspector/markup/views/read-only-container.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 ReadOnlyEditor = require("devtools/client/inspector/markup/views/read-only-editor");
const MarkupContainer = require("devtools/client/inspector/markup/views/markup-container");
const {extend} = require("devtools/shared/extend");

/**
 * An implementation of MarkupContainer for Pseudo Elements,
 * Doctype nodes, or any other type generic node that doesn't
 * fit for other editors.
 * Does not allow any editing, just viewing / selecting.
 *
 * @param  {MarkupView} markupView
 *         The markup view that owns this container.
 * @param  {NodeFront} node
 *         The node to display.
 */
function MarkupReadOnlyContainer(markupView, node) {
  MarkupContainer.prototype.initialize.call(this, markupView, node,
    "readonlycontainer");

  this.editor = new ReadOnlyEditor(this, node);
  this.tagLine.appendChild(this.editor.elt);
}

MarkupReadOnlyContainer.prototype =
  extend(MarkupContainer.prototype, {});

module.exports = MarkupReadOnlyContainer;
PK
!<���&&Rchrome/devtools/modules/devtools/client/inspector/markup/views/read-only-editor.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 nodeConstants = require("devtools/shared/dom-node-constants");

/**
 * Creates an editor for non-editable nodes.
 */
function ReadOnlyEditor(container, node) {
  this.container = container;
  this.markup = this.container.markup;
  this.buildMarkup();

  if (node.isPseudoElement) {
    this.tag.classList.add("theme-fg-color5");
    this.tag.textContent = node.isBeforePseudoElement ? "::before" : "::after";
  } else if (node.nodeType == nodeConstants.DOCUMENT_TYPE_NODE) {
    this.elt.classList.add("comment", "doctype");
    this.tag.textContent = node.doctypeString;
  } else {
    this.tag.textContent = node.nodeName;
  }

  // Make the "tag" part of this editor focusable.
  this.tag.setAttribute("tabindex", "-1");
}

ReadOnlyEditor.prototype = {
  buildMarkup: function () {
    let doc = this.markup.doc;

    this.elt = doc.createElement("span");
    this.elt.classList.add("editor");

    this.tag = doc.createElement("span");
    this.tag.classList.add("tag");
    this.elt.appendChild(this.tag);
  },

  destroy: function () {
    // We might be already destroyed.
    if (!this.elt) {
      return;
    }

    this.elt.remove();
    this.elt = null;
    this.tag = null;
  },

  /**
   * Stub method for consistency with ElementEditor.
   */
  getInfoAtNode: function () {
    return null;
  }
};

module.exports = ReadOnlyEditor;
PK
!<M���Pchrome/devtools/modules/devtools/client/inspector/markup/views/root-container.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

/**
 * Dummy container node used for the root document element.
 */
function RootContainer(markupView, node) {
  this.doc = markupView.doc;
  this.elt = this.doc.createElement("ul");
  // Root container has tree semantics for accessibility.
  this.elt.setAttribute("role", "tree");
  this.elt.setAttribute("tabindex", "0");
  this.elt.setAttribute("aria-dropeffect", "none");
  this.elt.container = this;
  this.children = this.elt;
  this.node = node;
  this.toString = () => "[root container]";
}

RootContainer.prototype = {
  hasChildren: true,
  expanded: true,
  update: function () {},
  destroy: function () {},

  /**
   * If the node has children, return the list of containers for all these children.
   * @return {Array} An array of child containers or null.
   */
  getChildContainers: function () {
    return [...this.children.children].filter(node => node.container)
                                      .map(node => node.container);
  },

  /**
   * Set the expanded state of the container node.
   * @param  {Boolean} value
   */
  setExpanded: function () {},

  /**
   * Set an appropriate role of the container's children node.
   */
  setChildrenRole: function () {},

  /**
   * Set an appropriate DOM tree depth level for a node and its subtree.
   */
  updateLevel: function () {}
};

module.exports = RootContainer;
PK
!<}�b��Pchrome/devtools/modules/devtools/client/inspector/markup/views/text-container.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 nodeConstants = require("devtools/shared/dom-node-constants");
const TextEditor = require("devtools/client/inspector/markup/views/text-editor");
const MarkupContainer = require("devtools/client/inspector/markup/views/markup-container");
const {extend} = require("devtools/shared/extend");

/**
 * An implementation of MarkupContainer for text node and comment nodes.
 * Allows basic text editing in a textarea.
 *
 * @param  {MarkupView} markupView
 *         The markup view that owns this container.
 * @param  {NodeFront} node
 *         The node to display.
 * @param  {Inspector} inspector
 *         The inspector tool container the markup-view
 */
function MarkupTextContainer(markupView, node) {
  MarkupContainer.prototype.initialize.call(this, markupView, node,
    "textcontainer");

  if (node.nodeType == nodeConstants.TEXT_NODE) {
    this.editor = new TextEditor(this, node, "text");
  } else if (node.nodeType == nodeConstants.COMMENT_NODE) {
    this.editor = new TextEditor(this, node, "comment");
  } else {
    throw new Error("Invalid node for MarkupTextContainer");
  }

  this.tagLine.appendChild(this.editor.elt);
}

MarkupTextContainer.prototype = extend(MarkupContainer.prototype, {});

module.exports = MarkupTextContainer;
PK
!<g� IMchrome/devtools/modules/devtools/client/inspector/markup/views/text-editor.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {getAutocompleteMaxWidth} = require("devtools/client/inspector/markup/utils");
const {editableField} = require("devtools/client/shared/inplace-editor");
const {getCssProperties} = require("devtools/shared/fronts/css-properties");
const {LocalizationHelper} = require("devtools/shared/l10n");

const INSPECTOR_L10N =
      new LocalizationHelper("devtools/client/locales/inspector.properties");

/**
 * Creates a simple text editor node, used for TEXT and COMMENT
 * nodes.
 *
 * @param  {MarkupContainer} container
 *         The container owning this editor.
 * @param  {DOMNode} node
 *         The node being edited.
 * @param  {String} type
 *         The type of editor to build. This can be either 'text' or 'comment'.
 */
function TextEditor(container, node, type) {
  this.container = container;
  this.markup = this.container.markup;
  this.node = node;
  this._selected = false;

  this.buildMarkup(type);

  editableField({
    element: this.value,
    stopOnReturn: true,
    trigger: "dblclick",
    multiline: true,
    maxWidth: () => getAutocompleteMaxWidth(this.value, this.container.elt),
    trimOutput: false,
    done: (val, commit) => {
      if (!commit) {
        return;
      }
      this.node.getNodeValue().then(longstr => {
        longstr.string().then(oldValue => {
          longstr.release().catch(console.error);

          this.container.undo.do(() => {
            this.node.setNodeValue(val);
          }, () => {
            this.node.setNodeValue(oldValue);
          });
        });
      });
    },
    cssProperties: getCssProperties(this.markup.toolbox),
  });

  this.update();
}

TextEditor.prototype = {
  buildMarkup: function (type) {
    let doc = this.markup.doc;

    this.elt = doc.createElement("span");
    this.elt.classList.add("editor", type);

    if (type === "comment") {
      let openComment = doc.createElement("span");
      openComment.textContent = "<!--";
      this.elt.appendChild(openComment);
    }

    this.value = doc.createElement("pre");
    this.value.setAttribute("style", "display:inline-block;white-space: normal;");
    this.value.setAttribute("tabindex", "-1");
    this.elt.appendChild(this.value);

    if (type === "comment") {
      let closeComment = doc.createElement("span");
      closeComment.textContent = "-->";
      this.elt.appendChild(closeComment);
    }
  },

  get selected() {
    return this._selected;
  },

  set selected(value) {
    if (value === this._selected) {
      return;
    }
    this._selected = value;
    this.update();
  },

  update: function () {
    let longstr = null;
    this.node.getNodeValue().then(ret => {
      longstr = ret;
      return longstr.string();
    }).then(str => {
      longstr.release().catch(console.error);
      this.value.textContent = str;

      let isWhitespace = !/[^\s]/.exec(str);
      this.value.classList.toggle("whitespace", isWhitespace);

      let chars = str.replace(/\n/g, "⏎")
                     .replace(/\t/g, "⇥")
                     .replace(/ /g, "◦");
      this.value.setAttribute("title", isWhitespace
        ? INSPECTOR_L10N.getFormatStr("markupView.whitespaceOnly", chars)
        : "");
    }).catch(console.error);
  },

  destroy: function () {},

  /**
   * Stub method for consistency with ElementEditor.
   */
  getInfoAtNode: function () {
    return null;
  }
};

module.exports = TextEditor;
PK
!<u%?��:chrome/devtools/modules/devtools/client/inspector/panel.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 InspectorPanel(iframeWindow, toolbox) {
  this._inspector = new iframeWindow.Inspector(toolbox);
}
InspectorPanel.prototype = {
  open() {
    return this._inspector.init();
  },

  destroy() {
    return this._inspector.destroy();
  }
};
exports.InspectorPanel = InspectorPanel;
PK
!<[vo��=chrome/devtools/modules/devtools/client/inspector/reducers.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 exposes the Redux reducers of the box model, grid and grid highlighter
// settings.

exports.animations = require("devtools/client/inspector/animation/reducers/animations");
exports.boxModel = require("devtools/client/inspector/boxmodel/reducers/box-model");
exports.changes = require("devtools/client/inspector/changes/reducers/changes");
exports.events = require("devtools/client/inspector/events/reducers/events");
exports.extensionsSidebar = require("devtools/client/inspector/extensions/reducers/sidebar");
exports.flexbox = require("devtools/client/inspector/flexbox/reducers/flexbox");
exports.fontOptions = require("devtools/client/inspector/fonts/reducers/font-options");
exports.fontData = require("devtools/client/inspector/fonts/reducers/fonts");
exports.grids = require("devtools/client/inspector/grids/reducers/grids");
exports.highlighterSettings = require("devtools/client/inspector/grids/reducers/highlighter-settings");
PK
!<;�(6(6Ochrome/devtools/modules/devtools/client/inspector/rules/models/element-style.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=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 promise = require("promise");
const Rule = require("devtools/client/inspector/rules/models/rule");
const {promiseWarn} = require("devtools/client/inspector/shared/utils");
const {ELEMENT_STYLE} = require("devtools/shared/specs/styles");
const {getCssProperties, isCssVariable} = require("devtools/shared/fronts/css-properties");

/**
 * ElementStyle is responsible for the following:
 *   Keeps track of which properties are overridden.
 *   Maintains a list of Rule objects for a given element.
 *
 * @param {Element} element
 *        The element whose style we are viewing.
 * @param {CssRuleView} ruleView
 *        The instance of the rule-view panel.
 * @param {Object} store
 *        The ElementStyle can use this object to store metadata
 *        that might outlast the rule view, particularly the current
 *        set of disabled properties.
 * @param {PageStyleFront} pageStyle
 *        Front for the page style actor that will be providing
 *        the style information.
 * @param {Boolean} showUserAgentStyles
 *        Should user agent styles be inspected?
 */
function ElementStyle(element, ruleView, store, pageStyle,
    showUserAgentStyles) {
  this.element = element;
  this.ruleView = ruleView;
  this.store = store || {};
  this.pageStyle = pageStyle;
  this.showUserAgentStyles = showUserAgentStyles;
  this.rules = [];
  this.cssProperties = getCssProperties(this.ruleView.inspector.toolbox);
  this.variables = new Map();

  // We don't want to overwrite this.store.userProperties so we only create it
  // if it doesn't already exist.
  if (!("userProperties" in this.store)) {
    this.store.userProperties = new UserProperties();
  }

  if (!("disabled" in this.store)) {
    this.store.disabled = new WeakMap();
  }
}

ElementStyle.prototype = {
  // The element we're looking at.
  element: null,

  destroy: function () {
    if (this.destroyed) {
      return;
    }
    this.destroyed = true;

    for (let rule of this.rules) {
      if (rule.editor) {
        rule.editor.destroy();
      }
    }
  },

  /**
   * Called by the Rule object when it has been changed through the
   * setProperty* methods.
   */
  _changed: function () {
    if (this.onChanged) {
      this.onChanged();
    }
  },

  /**
   * Refresh the list of rules to be displayed for the active element.
   * Upon completion, this.rules[] will hold a list of Rule objects.
   *
   * Returns a promise that will be resolved when the elementStyle is
   * ready.
   */
  populate: function () {
    let populated = this.pageStyle.getApplied(this.element, {
      inherited: true,
      matchedSelectors: true,
      filter: this.showUserAgentStyles ? "ua" : undefined,
    }).then(entries => {
      if (this.destroyed) {
        return promise.resolve(undefined);
      }

      if (this.populated !== populated) {
        // Don't care anymore.
        return promise.resolve(undefined);
      }

      // Store the current list of rules (if any) during the population
      // process.  They will be reused if possible.
      let existingRules = this.rules;

      this.rules = [];

      for (let entry of entries) {
        this._maybeAddRule(entry, existingRules);
      }

      // Mark overridden computed styles.
      this.markOverriddenAll();

      this._sortRulesForPseudoElement();

      // We're done with the previous list of rules.
      for (let r of existingRules) {
        if (r && r.editor) {
          r.editor.destroy();
        }
      }

      return undefined;
    }).catch(e => {
      // populate is often called after a setTimeout,
      // the connection may already be closed.
      if (this.destroyed) {
        return promise.resolve(undefined);
      }
      return promiseWarn(e);
    });
    this.populated = populated;
    return this.populated;
  },

  /**
   * Get the font families in use by the element.
   *
   * Returns a promise that will be resolved to a list of CSS family
   * names.  The list might have duplicates.
   */
  getUsedFontFamilies: function () {
    return new Promise((resolve, reject) => {
      this.ruleView.styleWindow.requestIdleCallback(async () => {
        try {
          let fonts = await this.pageStyle.getUsedFontFaces(
            this.element, { includePreviews: false });
          resolve(fonts.map(font => font.CSSFamilyName));
        } catch (e) {
          reject(e);
        }
      });
    });
  },

  /**
   * Put pseudo elements in front of others.
   */
  _sortRulesForPseudoElement: function () {
    this.rules = this.rules.sort((a, b) => {
      return (a.pseudoElement || "z") > (b.pseudoElement || "z");
    });
  },

  /**
   * Add a rule if it's one we care about.  Filters out duplicates and
   * inherited styles with no inherited properties.
   *
   * @param {Object} options
   *        Options for creating the Rule, see the Rule constructor.
   * @param {Array} existingRules
   *        Rules to reuse if possible.  If a rule is reused, then it
   *        it will be deleted from this array.
   * @return {Boolean} true if we added the rule.
   */
  _maybeAddRule: function (options, existingRules) {
    // If we've already included this domRule (for example, when a
    // common selector is inherited), ignore it.
    if (options.rule &&
        this.rules.some(rule => rule.domRule === options.rule)) {
      return false;
    }

    if (options.system) {
      return false;
    }

    let rule = null;

    // If we're refreshing and the rule previously existed, reuse the
    // Rule object.
    if (existingRules) {
      let ruleIndex = existingRules.findIndex((r) => r.matches(options));
      if (ruleIndex >= 0) {
        rule = existingRules[ruleIndex];
        rule.refresh(options);
        existingRules.splice(ruleIndex, 1);
      }
    }

    // If this is a new rule, create its Rule object.
    if (!rule) {
      rule = new Rule(this, options);
    }

    // Ignore inherited rules with no visible properties.
    if (options.inherited && !rule.hasAnyVisibleProperties()) {
      return false;
    }

    this.rules.push(rule);
    return true;
  },

  /**
   * Calls markOverridden with all supported pseudo elements
   */
  markOverriddenAll: function () {
    this.variables.clear();
    this.markOverridden();

    for (let pseudo of this.cssProperties.pseudoElements) {
      this.markOverridden(pseudo);
    }
  },

  /**
   * Mark the properties listed in this.rules for a given pseudo element
   * with an overridden flag if an earlier property overrides it.
   *
   * @param {String} pseudo
   *        Which pseudo element to flag as overridden.
   *        Empty string or undefined will default to no pseudo element.
   */
  markOverridden: function (pseudo = "") {
    // Gather all the text properties applied by these rules, ordered
    // from more- to less-specific. Text properties from keyframes rule are
    // excluded from being marked as overridden since a number of criteria such
    // as time, and animation overlay are required to be check in order to
    // determine if the property is overridden.
    let textProps = [];
    for (let rule of this.rules) {
      if ((rule.matchedSelectors.length > 0 ||
           rule.domRule.type === ELEMENT_STYLE) &&
          rule.pseudoElement === pseudo && !rule.keyframes) {
        for (let textProp of rule.textProps.slice(0).reverse()) {
          if (textProp.enabled) {
            textProps.push(textProp);
          }
        }
      }
    }

    // Gather all the computed properties applied by those text
    // properties.
    let computedProps = [];
    for (let textProp of textProps) {
      computedProps = computedProps.concat(textProp.computed);
    }

    // Walk over the computed properties.  As we see a property name
    // for the first time, mark that property's name as taken by this
    // property.
    //
    // If we come across a property whose name is already taken, check
    // its priority against the property that was found first:
    //
    //   If the new property is a higher priority, mark the old
    //   property overridden and mark the property name as taken by
    //   the new property.
    //
    //   If the new property is a lower or equal priority, mark it as
    //   overridden.
    //
    // _overriddenDirty will be set on each prop, indicating whether its
    // dirty status changed during this pass.
    let taken = {};
    for (let computedProp of computedProps) {
      let earlier = taken[computedProp.name];

      // Prevent -webkit-gradient from being selected after unchecking
      // linear-gradient in this case:
      //  -moz-linear-gradient: ...;
      //  -webkit-linear-gradient: ...;
      //  linear-gradient: ...;
      if (!computedProp.textProp.isValid()) {
        computedProp.overridden = true;
        continue;
      }
      let overridden;
      if (earlier &&
          computedProp.priority === "important" &&
          earlier.priority !== "important" &&
          (earlier.textProp.rule.inherited ||
           !computedProp.textProp.rule.inherited)) {
        // New property is higher priority.  Mark the earlier property
        // overridden (which will reverse its dirty state).
        earlier._overriddenDirty = !earlier._overriddenDirty;
        earlier.overridden = true;
        overridden = false;
      } else {
        overridden = !!earlier;
      }

      computedProp._overriddenDirty =
        (!!computedProp.overridden !== overridden);
      computedProp.overridden = overridden;
      if (!computedProp.overridden && computedProp.textProp.enabled) {
        taken[computedProp.name] = computedProp;

        if (isCssVariable(computedProp.name)) {
          this.variables.set(computedProp.name, computedProp.value);
        }
      }
    }

    // For each TextProperty, mark it overridden if all of its
    // computed properties are marked overridden.  Update the text
    // property's associated editor, if any.  This will clear the
    // _overriddenDirty state on all computed properties.
    for (let textProp of textProps) {
      // _updatePropertyOverridden will return true if the
      // overridden state has changed for the text property.
      if (this._updatePropertyOverridden(textProp)) {
        textProp.updateEditor();
      }
    }
  },

  /**
   * Mark a given TextProperty as overridden or not depending on the
   * state of its computed properties.  Clears the _overriddenDirty state
   * on all computed properties.
   *
   * @param {TextProperty} prop
   *        The text property to update.
   * @return {Boolean} true if the TextProperty's overridden state (or any of
   *         its computed properties overridden state) changed.
   */
  _updatePropertyOverridden: function (prop) {
    let overridden = true;
    let dirty = false;
    for (let computedProp of prop.computed) {
      if (!computedProp.overridden) {
        overridden = false;
      }
      dirty = computedProp._overriddenDirty || dirty;
      delete computedProp._overriddenDirty;
    }

    dirty = (!!prop.overridden !== overridden) || dirty;
    prop.overridden = overridden;
    return dirty;
  },

 /**
  * Returns the current value of a CSS variable; or null if the
  * variable is not defined.
  *
  * @param  {String} name
  *         The name of the variable.
  * @return {String} the variable's value or null if the variable is
  *         not defined.
  */
  getVariable: function (name) {
    return this.variables.get(name);
  },
};

/**
 * Store of CSSStyleDeclarations mapped to properties that have been changed by
 * the user.
 */
function UserProperties() {
  this.map = new Map();
}

UserProperties.prototype = {
  /**
   * Get a named property for a given CSSStyleDeclaration.
   *
   * @param {CSSStyleDeclaration} style
   *        The CSSStyleDeclaration against which the property is mapped.
   * @param {String} name
   *        The name of the property to get.
   * @param {String} value
   *        Default value.
   * @return {String}
   *        The property value if it has previously been set by the user, null
   *        otherwise.
   */
  getProperty: function (style, name, value) {
    let key = this.getKey(style);
    let entry = this.map.get(key, null);

    if (entry && name in entry) {
      return entry[name];
    }
    return value;
  },

  /**
   * Set a named property for a given CSSStyleDeclaration.
   *
   * @param {CSSStyleDeclaration} style
   *        The CSSStyleDeclaration against which the property is to be mapped.
   * @param {String} name
   *        The name of the property to set.
   * @param {String} userValue
   *        The value of the property to set.
   */
  setProperty: function (style, name, userValue) {
    let key = this.getKey(style, name);
    let entry = this.map.get(key, null);

    if (entry) {
      entry[name] = userValue;
    } else {
      let props = {};
      props[name] = userValue;
      this.map.set(key, props);
    }
  },

  /**
   * Check whether a named property for a given CSSStyleDeclaration is stored.
   *
   * @param {CSSStyleDeclaration} style
   *        The CSSStyleDeclaration against which the property would be mapped.
   * @param {String} name
   *        The name of the property to check.
   */
  contains: function (style, name) {
    let key = this.getKey(style, name);
    let entry = this.map.get(key, null);
    return !!entry && name in entry;
  },

  getKey: function (style, name) {
    return style.actorID + ":" + name;
  },

  clear: function () {
    this.map.clear();
  }
};

module.exports = ElementStyle;
PK
!<����HRHRFchrome/devtools/modules/devtools/client/inspector/rules/models/rule.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=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 promise = require("promise");
const CssLogic = require("devtools/shared/inspector/css-logic");
const {ELEMENT_STYLE} = require("devtools/shared/specs/styles");
const TextProperty = require("devtools/client/inspector/rules/models/text-property");
const {promiseWarn} = require("devtools/client/inspector/shared/utils");
const {parseNamedDeclarations} = require("devtools/shared/css/parsing-utils");
const Services = require("Services");

const STYLE_INSPECTOR_PROPERTIES = "devtools/shared/locales/styleinspector.properties";
const {LocalizationHelper} = require("devtools/shared/l10n");
const STYLE_INSPECTOR_L10N = new LocalizationHelper(STYLE_INSPECTOR_PROPERTIES);

/**
 * Rule is responsible for the following:
 *   Manages a single style declaration or rule.
 *   Applies changes to the properties in a rule.
 *   Maintains a list of TextProperty objects.
 *
 * @param {ElementStyle} elementStyle
 *        The ElementStyle to which this rule belongs.
 * @param {Object} options
 *        The information used to construct this rule.  Properties include:
 *          rule: A StyleRuleActor
 *          inherited: An element this rule was inherited from.  If omitted,
 *            the rule applies directly to the current element.
 *          isSystem: Is this a user agent style?
 *          isUnmatched: True if the rule does not match the current selected
 *            element, otherwise, false.
 */
function Rule(elementStyle, options) {
  this.elementStyle = elementStyle;
  this.domRule = options.rule || null;
  this.style = options.rule;
  this.matchedSelectors = options.matchedSelectors || [];
  this.pseudoElement = options.pseudoElement || "";

  this.isSystem = options.isSystem;
  this.isUnmatched = options.isUnmatched || false;
  this.inherited = options.inherited || null;
  this.keyframes = options.keyframes || null;

  if (this.domRule && this.domRule.mediaText) {
    this.mediaText = this.domRule.mediaText;
  }

  this.cssProperties = this.elementStyle.ruleView.cssProperties;

  // Populate the text properties with the style's current authoredText
  // value, and add in any disabled properties from the store.
  this.textProps = this._getTextProperties();
  this.textProps = this.textProps.concat(this._getDisabledProperties());
}

Rule.prototype = {
  mediaText: "",

  get title() {
    let title = CssLogic.shortSource(this.sheet);
    if (this.domRule.type !== ELEMENT_STYLE && this.ruleLine > 0) {
      title += ":" + this.ruleLine;
    }

    return title + (this.mediaText ? " @media " + this.mediaText : "");
  },

  get inheritedSource() {
    if (this._inheritedSource) {
      return this._inheritedSource;
    }
    this._inheritedSource = "";
    if (this.inherited) {
      let eltText = this.inherited.displayName;
      if (this.inherited.id) {
        eltText += "#" + this.inherited.id;
      }
      this._inheritedSource =
        STYLE_INSPECTOR_L10N.getFormatStr("rule.inheritedFrom", eltText);
    }
    return this._inheritedSource;
  },

  get keyframesName() {
    if (this._keyframesName) {
      return this._keyframesName;
    }
    this._keyframesName = "";
    if (this.keyframes) {
      this._keyframesName =
        STYLE_INSPECTOR_L10N.getFormatStr("rule.keyframe", this.keyframes.name);
    }
    return this._keyframesName;
  },

  get selectorText() {
    return this.domRule.selectors ? this.domRule.selectors.join(", ") :
      CssLogic.l10n("rule.sourceElement");
  },

  /**
   * The rule's stylesheet.
   */
  get sheet() {
    return this.domRule ? this.domRule.parentStyleSheet : null;
  },

  /**
   * The rule's line within a stylesheet
   */
  get ruleLine() {
    return this.domRule ? this.domRule.line : -1;
  },

  /**
   * The rule's column within a stylesheet
   */
  get ruleColumn() {
    return this.domRule ? this.domRule.column : null;
  },

  /**
   * Returns true if the rule matches the creation options
   * specified.
   *
   * @param {Object} options
   *        Creation options. See the Rule constructor for documentation.
   */
  matches: function (options) {
    return this.style === options.rule;
  },

  /**
   * Create a new TextProperty to include in the rule.
   *
   * @param {String} name
   *        The text property name (such as "background" or "border-top").
   * @param {String} value
   *        The property's value (not including priority).
   * @param {String} priority
   *        The property's priority (either "important" or an empty string).
   * @param {Boolean} enabled
   *        True if the property should be enabled.
   * @param {TextProperty} siblingProp
   *        Optional, property next to which the new property will be added.
   */
  createProperty: function (name, value, priority, enabled, siblingProp) {
    let prop = new TextProperty(this, name, value, priority, enabled);

    let ind;
    if (siblingProp) {
      ind = this.textProps.indexOf(siblingProp) + 1;
      this.textProps.splice(ind, 0, prop);
    } else {
      ind = this.textProps.length;
      this.textProps.push(prop);
    }

    this.applyProperties((modifications) => {
      modifications.createProperty(ind, name, value, priority, enabled);
      // Now that the rule has been updated, the server might have given us data
      // that changes the state of the property. Update it now.
      prop.updateEditor();
    });

    return prop;
  },

  /**
   * Helper function for applyProperties that is called when the actor
   * does not support as-authored styles.  Store disabled properties
   * in the element style's store.
   */
  _applyPropertiesNoAuthored: function (modifications) {
    this.elementStyle.markOverriddenAll();

    let disabledProps = [];

    for (let prop of this.textProps) {
      if (prop.invisible) {
        continue;
      }
      if (!prop.enabled) {
        disabledProps.push({
          name: prop.name,
          value: prop.value,
          priority: prop.priority
        });
        continue;
      }
      if (prop.value.trim() === "") {
        continue;
      }

      modifications.setProperty(-1, prop.name, prop.value, prop.priority);

      prop.updateComputed();
    }

    // Store disabled properties in the disabled store.
    let disabled = this.elementStyle.store.disabled;
    if (disabledProps.length > 0) {
      disabled.set(this.style, disabledProps);
    } else {
      disabled.delete(this.style);
    }

    return modifications.apply().then(() => {
      let cssProps = {};
      // Note that even though StyleRuleActors normally provide parsed
      // declarations already, _applyPropertiesNoAuthored is only used when
      // connected to older backend that do not provide them. So parse here.
      for (let cssProp of parseNamedDeclarations(this.cssProperties.isKnown,
                                                 this.style.authoredText)) {
        cssProps[cssProp.name] = cssProp;
      }

      for (let textProp of this.textProps) {
        if (!textProp.enabled) {
          continue;
        }
        let cssProp = cssProps[textProp.name];

        if (!cssProp) {
          cssProp = {
            name: textProp.name,
            value: "",
            priority: ""
          };
        }

        textProp.priority = cssProp.priority;
      }
    });
  },

  /**
   * A helper for applyProperties that applies properties in the "as
   * authored" case; that is, when the StyleRuleActor supports
   * setRuleText.
   */
  _applyPropertiesAuthored: function (modifications) {
    return modifications.apply().then(() => {
      // The rewriting may have required some other property values to
      // change, e.g., to insert some needed terminators.  Update the
      // relevant properties here.
      for (let index in modifications.changedDeclarations) {
        let newValue = modifications.changedDeclarations[index];
        this.textProps[index].noticeNewValue(newValue);
      }
      // Recompute and redisplay the computed properties.
      for (let prop of this.textProps) {
        if (!prop.invisible && prop.enabled) {
          prop.updateComputed();
          prop.updateEditor();
        }
      }
    });
  },

  /**
   * Reapply all the properties in this rule, and update their
   * computed styles.  Will re-mark overridden properties.  Sets the
   * |_applyingModifications| property to a promise which will resolve
   * when the edit has completed.
   *
   * @param {Function} modifier a function that takes a RuleModificationList
   *        (or RuleRewriter) as an argument and that modifies it
   *        to apply the desired edit
   * @return {Promise} a promise which will resolve when the edit
   *        is complete
   */
  applyProperties: function (modifier) {
    // If there is already a pending modification, we have to wait
    // until it settles before applying the next modification.
    let resultPromise =
        promise.resolve(this._applyingModifications).then(() => {
          let modifications = this.style.startModifyingProperties(
            this.cssProperties);
          modifier(modifications);
          if (this.style.canSetRuleText) {
            return this._applyPropertiesAuthored(modifications);
          }
          return this._applyPropertiesNoAuthored(modifications);
        }).then(() => {
          this.elementStyle.markOverriddenAll();

          if (resultPromise === this._applyingModifications) {
            this._applyingModifications = null;
            this.elementStyle._changed();
          }
        }).catch(promiseWarn);

    this._applyingModifications = resultPromise;
    return resultPromise;
  },

  /**
   * Renames a property.
   *
   * @param {TextProperty} property
   *        The property to rename.
   * @param {String} name
   *        The new property name (such as "background" or "border-top").
   */
  setPropertyName: function (property, name) {
    if (name === property.name) {
      return;
    }

    let oldName = property.name;
    property.name = name;
    let index = this.textProps.indexOf(property);
    this.applyProperties((modifications) => {
      modifications.renameProperty(index, oldName, name);
    });
  },

  /**
   * Sets the value and priority of a property, then reapply all properties.
   *
   * @param {TextProperty} property
   *        The property to manipulate.
   * @param {String} value
   *        The property's value (not including priority).
   * @param {String} priority
   *        The property's priority (either "important" or an empty string).
   */
  setPropertyValue: function (property, value, priority) {
    if (value === property.value && priority === property.priority) {
      return;
    }

    property.value = value;
    property.priority = priority;

    let index = this.textProps.indexOf(property);
    this.applyProperties((modifications) => {
      modifications.setProperty(index, property.name, value, priority);
    });
  },

  /**
   * Just sets the value and priority of a property, in order to preview its
   * effect on the content document.
   *
   * @param {TextProperty} property
   *        The property which value will be previewed
   * @param {String} value
   *        The value to be used for the preview
   * @param {String} priority
   *        The property's priority (either "important" or an empty string).
   */
  previewPropertyValue: function (property, value, priority) {
    let modifications = this.style.startModifyingProperties(this.cssProperties);
    modifications.setProperty(this.textProps.indexOf(property),
                              property.name, value, priority);
    modifications.apply().then(() => {
      // Ensure dispatching a ruleview-changed event
      // also for previews
      this.elementStyle._changed();
    });
  },

  /**
   * Disables or enables given TextProperty.
   *
   * @param {TextProperty} property
   *        The property to enable/disable
   * @param {Boolean} value
   */
  setPropertyEnabled: function (property, value) {
    if (property.enabled === !!value) {
      return;
    }
    property.enabled = !!value;
    let index = this.textProps.indexOf(property);
    this.applyProperties((modifications) => {
      modifications.setPropertyEnabled(index, property.name, property.enabled);
    });
  },

  /**
   * Remove a given TextProperty from the rule and update the rule
   * accordingly.
   *
   * @param {TextProperty} property
   *        The property to be removed
   */
  removeProperty: function (property) {
    let index = this.textProps.indexOf(property);
    this.textProps.splice(index, 1);
    // Need to re-apply properties in case removing this TextProperty
    // exposes another one.
    this.applyProperties((modifications) => {
      modifications.removeProperty(index, property.name);
    });
  },

  /**
   * Get the list of TextProperties from the style. Needs
   * to parse the style's authoredText.
   */
  _getTextProperties: function () {
    let textProps = [];
    let store = this.elementStyle.store;

    // Starting with FF49, StyleRuleActors provide parsed declarations.
    let props = this.style.declarations;
    if (!props.length) {
      // If the authored text has an invalid property, it will show up
      // as nameless.  Skip these as we don't currently have a good
      // way to display them.
      props = parseNamedDeclarations(this.cssProperties.isKnown,
                                     this.style.authoredText, true);
    }

    for (let prop of props) {
      let name = prop.name;
      // In an inherited rule, we only show inherited properties.
      // However, we must keep all properties in order for rule
      // rewriting to work properly.  So, compute the "invisible"
      // property here.
      let invisible = this.inherited && !this.cssProperties.isInherited(name);
      let value = store.userProperties.getProperty(this.style, name,
                                                   prop.value);
      let textProp = new TextProperty(this, name, value, prop.priority,
                                      !("commentOffsets" in prop),
                                      invisible);
      textProps.push(textProp);
    }

    return textProps;
  },

  /**
   * Return the list of disabled properties from the store for this rule.
   */
  _getDisabledProperties: function () {
    let store = this.elementStyle.store;

    // Include properties from the disabled property store, if any.
    let disabledProps = store.disabled.get(this.style);
    if (!disabledProps) {
      return [];
    }

    let textProps = [];

    for (let prop of disabledProps) {
      let value = store.userProperties.getProperty(this.style, prop.name,
                                                   prop.value);
      let textProp = new TextProperty(this, prop.name, value, prop.priority);
      textProp.enabled = false;
      textProps.push(textProp);
    }

    return textProps;
  },

  /**
   * Reread the current state of the rules and rebuild text
   * properties as needed.
   */
  refresh: function (options) {
    this.matchedSelectors = options.matchedSelectors || [];
    let newTextProps = this._getTextProperties();

    // Update current properties for each property present on the style.
    // This will mark any touched properties with _visited so we
    // can detect properties that weren't touched (because they were
    // removed from the style).
    // Also keep track of properties that didn't exist in the current set
    // of properties.
    let brandNewProps = [];
    for (let newProp of newTextProps) {
      if (!this._updateTextProperty(newProp)) {
        brandNewProps.push(newProp);
      }
    }

    // Refresh editors and disabled state for all the properties that
    // were updated.
    for (let prop of this.textProps) {
      // Properties that weren't touched during the update
      // process must no longer exist on the node.  Mark them disabled.
      if (!prop._visited) {
        prop.enabled = false;
        prop.updateEditor();
      } else {
        delete prop._visited;
      }
    }

    // Add brand new properties.
    this.textProps = this.textProps.concat(brandNewProps);

    // Refresh the editor if one already exists.
    if (this.editor) {
      this.editor.populate();
    }
  },

  /**
   * Update the current TextProperties that match a given property
   * from the authoredText.  Will choose one existing TextProperty to update
   * with the new property's value, and will disable all others.
   *
   * When choosing the best match to reuse, properties will be chosen
   * by assigning a rank and choosing the highest-ranked property:
   *   Name, value, and priority match, enabled. (6)
   *   Name, value, and priority match, disabled. (5)
   *   Name and value match, enabled. (4)
   *   Name and value match, disabled. (3)
   *   Name matches, enabled. (2)
   *   Name matches, disabled. (1)
   *
   * If no existing properties match the property, nothing happens.
   *
   * @param {TextProperty} newProp
   *        The current version of the property, as parsed from the
   *        authoredText in Rule._getTextProperties().
   * @return {Boolean} true if a property was updated, false if no properties
   *         were updated.
   */
  _updateTextProperty: function (newProp) {
    let match = { rank: 0, prop: null };

    for (let prop of this.textProps) {
      if (prop.name !== newProp.name) {
        continue;
      }

      // Mark this property visited.
      prop._visited = true;

      // Start at rank 1 for matching name.
      let rank = 1;

      // Value and Priority matches add 2 to the rank.
      // Being enabled adds 1.  This ranks better matches higher,
      // with priority breaking ties.
      if (prop.value === newProp.value) {
        rank += 2;
        if (prop.priority === newProp.priority) {
          rank += 2;
        }
      }

      if (prop.enabled) {
        rank += 1;
      }

      if (rank > match.rank) {
        if (match.prop) {
          // We outrank a previous match, disable it.
          match.prop.enabled = false;
          match.prop.updateEditor();
        }
        match.rank = rank;
        match.prop = prop;
      } else if (rank) {
        // A previous match outranks us, disable ourself.
        prop.enabled = false;
        prop.updateEditor();
      }
    }

    // If we found a match, update its value with the new text property
    // value.
    if (match.prop) {
      match.prop.set(newProp);
      return true;
    }

    return false;
  },

  /**
   * Jump between editable properties in the UI. If the focus direction is
   * forward, begin editing the next property name if available or focus the
   * new property editor otherwise. If the focus direction is backward,
   * begin editing the previous property value or focus the selector editor if
   * this is the first element in the property list.
   *
   * @param {TextProperty} textProperty
   *        The text property that will be left to focus on a sibling.
   * @param {Number} direction
   *        The move focus direction number.
   */
  editClosestTextProperty: function (textProperty, direction) {
    let index = this.textProps.indexOf(textProperty);

    if (direction === Services.focus.MOVEFOCUS_FORWARD) {
      for (++index; index < this.textProps.length; ++index) {
        if (!this.textProps[index].invisible) {
          break;
        }
      }
      if (index === this.textProps.length) {
        textProperty.rule.editor.closeBrace.click();
      } else {
        this.textProps[index].editor.nameSpan.click();
      }
    } else if (direction === Services.focus.MOVEFOCUS_BACKWARD) {
      for (--index; index >= 0; --index) {
        if (!this.textProps[index].invisible) {
          break;
        }
      }
      if (index < 0) {
        textProperty.editor.ruleEditor.selectorText.click();
      } else {
        this.textProps[index].editor.valueSpan.click();
      }
    }
  },

  /**
   * Return a string representation of the rule.
   */
  stringifyRule: function () {
    let selectorText = this.selectorText;
    let cssText = "";
    let terminator = Services.appinfo.OS === "WINNT" ? "\r\n" : "\n";

    for (let textProp of this.textProps) {
      if (!textProp.invisible) {
        cssText += "\t" + textProp.stringifyProperty() + terminator;
      }
    }

    return selectorText + " {" + terminator + cssText + "}";
  },

  /**
   * See whether this rule has any non-invisible properties.
   * @return {Boolean} true if there is any visible property, or false
   *         if all properties are invisible
   */
  hasAnyVisibleProperties: function () {
    for (let prop of this.textProps) {
      if (!prop.invisible) {
        return true;
      }
    }
    return false;
  }
};

module.exports = Rule;
PK
!<E�_u��Ochrome/devtools/modules/devtools/client/inspector/rules/models/text-property.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=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 {escapeCSSComment} = require("devtools/shared/css/parsing-utils");
const {getCssProperties} = require("devtools/shared/fronts/css-properties");

/**
 * TextProperty is responsible for the following:
 *   Manages a single property from the authoredText attribute of the
 *     relevant declaration.
 *   Maintains a list of computed properties that come from this
 *     property declaration.
 *   Changes to the TextProperty are sent to its related Rule for
 *     application.
 *
 * @param {Rule} rule
 *        The rule this TextProperty came from.
 * @param {String} name
 *        The text property name (such as "background" or "border-top").
 * @param {String} value
 *        The property's value (not including priority).
 * @param {String} priority
 *        The property's priority (either "important" or an empty string).
 * @param {Boolean} enabled
 *        Whether the property is enabled.
 * @param {Boolean} invisible
 *        Whether the property is invisible.  An invisible property
 *        does not show up in the UI; these are needed so that the
 *        index of a property in Rule.textProps is the same as the index
 *        coming from parseDeclarations.
 */
function TextProperty(rule, name, value, priority, enabled = true,
                      invisible = false) {
  this.rule = rule;
  this.name = name;
  this.value = value;
  this.priority = priority;
  this.enabled = !!enabled;
  this.invisible = invisible;
  this.panelDoc = this.rule.elementStyle.ruleView.inspector.panelDoc;

  const toolbox = this.rule.elementStyle.ruleView.inspector.toolbox;
  this.cssProperties = getCssProperties(toolbox);

  this.updateComputed();
}

TextProperty.prototype = {
  /**
   * Update the editor associated with this text property,
   * if any.
   */
  updateEditor: function () {
    if (this.editor) {
      this.editor.update();
    }
  },

  /**
   * Update the list of computed properties for this text property.
   */
  updateComputed: function () {
    if (!this.name) {
      return;
    }

    // This is a bit funky.  To get the list of computed properties
    // for this text property, we'll set the property on a dummy element
    // and see what the computed style looks like.
    let dummyElement = this.rule.elementStyle.ruleView.dummyElement;
    let dummyStyle = dummyElement.style;
    dummyStyle.cssText = "";
    dummyStyle.setProperty(this.name, this.value, this.priority);

    this.computed = [];

    // Manually get all the properties that are set when setting a value on
    // this.name and check the computed style on dummyElement for each one.
    // If we just read dummyStyle, it would skip properties when value === "".
    let subProps = this.cssProperties.getSubproperties(this.name);

    for (let prop of subProps) {
      this.computed.push({
        textProp: this,
        name: prop,
        value: dummyStyle.getPropertyValue(prop),
        priority: dummyStyle.getPropertyPriority(prop),
      });
    }
  },

  /**
   * Set all the values from another TextProperty instance into
   * this TextProperty instance.
   *
   * @param {TextProperty} prop
   *        The other TextProperty instance.
   */
  set: function (prop) {
    let changed = false;
    for (let item of ["name", "value", "priority", "enabled"]) {
      if (this[item] !== prop[item]) {
        this[item] = prop[item];
        changed = true;
      }
    }

    if (changed) {
      this.updateEditor();
    }
  },

  setValue: function (value, priority, force = false) {
    let store = this.rule.elementStyle.store;

    if (this.editor && value !== this.editor.committed.value || force) {
      store.userProperties.setProperty(this.rule.style, this.name, value);
    }

    this.rule.setPropertyValue(this, value, priority);
    this.updateEditor();
  },

  /**
   * Called when the property's value has been updated externally, and
   * the property and editor should update.
   */
  noticeNewValue: function (value) {
    if (value !== this.value) {
      this.value = value;
      this.updateEditor();
    }
  },

  setName: function (name) {
    let store = this.rule.elementStyle.store;

    if (name !== this.name) {
      store.userProperties.setProperty(this.rule.style, name,
                                       this.editor.committed.value);
    }

    this.rule.setPropertyName(this, name);
    this.updateEditor();
  },

  setEnabled: function (value) {
    this.rule.setPropertyEnabled(this, value);
    this.updateEditor();
  },

  remove: function () {
    this.rule.removeProperty(this);
  },

  /**
   * Return a string representation of the rule property.
   */
  stringifyProperty: function () {
    // Get the displayed property value
    let declaration = this.name + ": " + this.editor.valueSpan.textContent +
      ";";

    // Comment out property declarations that are not enabled
    if (!this.enabled) {
      declaration = "/* " + escapeCSSComment(declaration) + " */";
    }

    return declaration;
  },

  /**
   * See whether this property's name is known.
   *
   * @return {Boolean} true if the property name is known, false otherwise.
   */
  isKnownProperty: function () {
    return this.cssProperties.isKnown(this.name);
  },

  /**
   * Validate this property. Does it make sense for this value to be assigned
   * to this property name?
   *
   * @return {Boolean} true if the property value is valid, false otherwise.
   */
  isValid: function () {
    // Starting with FF49, StyleRuleActors provide a list of parsed
    // declarations, with data about their validity, but if we don't have this,
    // compute validity locally (which might not be correct, but better than
    // nothing).
    if (!this.rule.domRule.declarations) {
      return this.cssProperties.isValidOnClient(this.name, this.value, this.panelDoc);
    }

    let selfIndex = this.rule.textProps.indexOf(this);

    // When adding a new property in the rule-view, the TextProperty object is
    // created right away before the rule gets updated on the server, so we're
    // not going to find the corresponding declaration object yet. Default to
    // true.
    if (!this.rule.domRule.declarations[selfIndex]) {
      return true;
    }

    return this.rule.domRule.declarations[selfIndex].isValid;
  }
};

module.exports = TextProperty;
PK
!<,b5D�D�@chrome/devtools/modules/devtools/client/inspector/rules/rules.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=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 promise = require("promise");
const Services = require("Services");
const {Task} = require("devtools/shared/task");
const {l10n} = require("devtools/shared/inspector/css-logic");
const {ELEMENT_STYLE} = require("devtools/shared/specs/styles");
const OutputParser = require("devtools/client/shared/output-parser");
const {PrefObserver} = require("devtools/client/shared/prefs");
const ElementStyle = require("devtools/client/inspector/rules/models/element-style");
const Rule = require("devtools/client/inspector/rules/models/rule");
const RuleEditor = require("devtools/client/inspector/rules/views/rule-editor");
const ClassListPreviewer = require("devtools/client/inspector/rules/views/class-list-previewer");
const {getCssProperties} = require("devtools/shared/fronts/css-properties");
const {
  VIEW_NODE_SELECTOR_TYPE,
  VIEW_NODE_PROPERTY_TYPE,
  VIEW_NODE_VALUE_TYPE,
  VIEW_NODE_IMAGE_URL_TYPE,
  VIEW_NODE_LOCATION_TYPE,
  VIEW_NODE_SHAPE_POINT_TYPE,
  VIEW_NODE_VARIABLE_TYPE,
  VIEW_NODE_FONT_TYPE,
} = require("devtools/client/inspector/shared/node-types");
const StyleInspectorMenu = require("devtools/client/inspector/shared/style-inspector-menu");
const TooltipsOverlay = require("devtools/client/inspector/shared/tooltips-overlay");
const {createChild, promiseWarn} = require("devtools/client/inspector/shared/utils");
const {debounce} = require("devtools/shared/debounce");
const EventEmitter = require("devtools/shared/old-event-emitter");
const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
const clipboardHelper = require("devtools/shared/platform/clipboard");
const AutocompletePopup = require("devtools/client/shared/autocomplete-popup");

const HTML_NS = "http://www.w3.org/1999/xhtml";
const PREF_UA_STYLES = "devtools.inspector.showUserAgentStyles";
const PREF_DEFAULT_COLOR_UNIT = "devtools.defaultColorUnit";
const FILTER_CHANGED_TIMEOUT = 150;

// This is used to parse user input when filtering.
const FILTER_PROP_RE = /\s*([^:\s]*)\s*:\s*(.*?)\s*;?$/;
// This is used to parse the filter search value to see if the filter
// should be strict or not
const FILTER_STRICT_RE = /\s*`(.*?)`\s*$/;
const INSET_POINT_TYPES = ["top", "right", "bottom", "left"];

/**
 * Our model looks like this:
 *
 * ElementStyle:
 *   Responsible for keeping track of which properties are overridden.
 *   Maintains a list of Rule objects that apply to the element.
 * Rule:
 *   Manages a single style declaration or rule.
 *   Responsible for applying changes to the properties in a rule.
 *   Maintains a list of TextProperty objects.
 * TextProperty:
 *   Manages a single property from the authoredText attribute of the
 *     relevant declaration.
 *   Maintains a list of computed properties that come from this
 *     property declaration.
 *   Changes to the TextProperty are sent to its related Rule for
 *     application.
 *
 * View hierarchy mostly follows the model hierarchy.
 *
 * CssRuleView:
 *   Owns an ElementStyle and creates a list of RuleEditors for its
 *    Rules.
 * RuleEditor:
 *   Owns a Rule object and creates a list of TextPropertyEditors
 *     for its TextProperties.
 *   Manages creation of new text properties.
 * TextPropertyEditor:
 *   Owns a TextProperty object.
 *   Manages changes to the TextProperty.
 *   Can be expanded to display computed properties.
 *   Can mark a property disabled or enabled.
 */

/**
 * CssRuleView is a view of the style rules and declarations that
 * apply to a given element.  After construction, the 'element'
 * property will be available with the user interface.
 *
 * @param {Inspector} inspector
 *        Inspector toolbox panel
 * @param {Document} document
 *        The document that will contain the rule view.
 * @param {Object} store
 *        The CSS rule view can use this object to store metadata
 *        that might outlast the rule view, particularly the current
 *        set of disabled properties.
 * @param {PageStyleFront} pageStyle
 *        The PageStyleFront for communicating with the remote server.
 */
function CssRuleView(inspector, document, store, pageStyle) {
  this.inspector = inspector;
  this.highlighters = inspector.highlighters;
  this.styleDocument = document;
  this.styleWindow = this.styleDocument.defaultView;
  this.store = store || {};
  this.pageStyle = pageStyle;

  // Allow tests to override debouncing behavior, as this can cause intermittents.
  this.debounce = debounce;

  this.cssProperties = getCssProperties(inspector.toolbox);

  this._outputParser = new OutputParser(document, this.cssProperties);

  this._onAddRule = this._onAddRule.bind(this);
  this._onContextMenu = this._onContextMenu.bind(this);
  this._onCopy = this._onCopy.bind(this);
  this._onFilterStyles = this._onFilterStyles.bind(this);
  this._onClearSearch = this._onClearSearch.bind(this);
  this._onTogglePseudoClassPanel = this._onTogglePseudoClassPanel.bind(this);
  this._onTogglePseudoClass = this._onTogglePseudoClass.bind(this);
  this._onToggleClassPanel = this._onToggleClassPanel.bind(this);

  let doc = this.styleDocument;
  this.element = doc.getElementById("ruleview-container-focusable");
  this.addRuleButton = doc.getElementById("ruleview-add-rule-button");
  this.searchField = doc.getElementById("ruleview-searchbox");
  this.searchClearButton = doc.getElementById("ruleview-searchinput-clear");
  this.pseudoClassPanel = doc.getElementById("pseudo-class-panel");
  this.pseudoClassToggle = doc.getElementById("pseudo-class-panel-toggle");
  this.classPanel = doc.getElementById("ruleview-class-panel");
  this.classToggle = doc.getElementById("class-panel-toggle");
  this.hoverCheckbox = doc.getElementById("pseudo-hover-toggle");
  this.activeCheckbox = doc.getElementById("pseudo-active-toggle");
  this.focusCheckbox = doc.getElementById("pseudo-focus-toggle");

  this.searchClearButton.hidden = true;

  this.shortcuts = new KeyShortcuts({ window: this.styleWindow });
  this._onShortcut = this._onShortcut.bind(this);
  this.shortcuts.on("Escape", event => this._onShortcut("Escape", event));
  this.shortcuts.on("Return", event => this._onShortcut("Return", event));
  this.shortcuts.on("Space", event => this._onShortcut("Space", event));
  this.shortcuts.on("CmdOrCtrl+F", event => this._onShortcut("CmdOrCtrl+F", event));
  this.element.addEventListener("copy", this._onCopy);
  this.element.addEventListener("contextmenu", this._onContextMenu);
  this.addRuleButton.addEventListener("click", this._onAddRule);
  this.searchField.addEventListener("input", this._onFilterStyles);
  this.searchClearButton.addEventListener("click", this._onClearSearch);
  this.pseudoClassToggle.addEventListener("click", this._onTogglePseudoClassPanel);
  this.classToggle.addEventListener("click", this._onToggleClassPanel);
  this.hoverCheckbox.addEventListener("click", this._onTogglePseudoClass);
  this.activeCheckbox.addEventListener("click", this._onTogglePseudoClass);
  this.focusCheckbox.addEventListener("click", this._onTogglePseudoClass);

  this._handlePrefChange = this._handlePrefChange.bind(this);
  this._handleUAStylePrefChange = this._handleUAStylePrefChange.bind(this);
  this._handleDefaultColorUnitPrefChange =
    this._handleDefaultColorUnitPrefChange.bind(this);

  this._prefObserver = new PrefObserver("devtools.");
  this._prefObserver.on(PREF_UA_STYLES, this._handleUAStylePrefChange);
  this._prefObserver.on(PREF_DEFAULT_COLOR_UNIT, this._handleDefaultColorUnitPrefChange);

  this.showUserAgentStyles = Services.prefs.getBoolPref(PREF_UA_STYLES);

  // The popup will be attached to the toolbox document.
  this.popup = new AutocompletePopup(inspector._toolbox.doc, {
    autoSelect: true,
    theme: "auto"
  });

  this._showEmpty();

  this._contextmenu = new StyleInspectorMenu(this, { isRuleView: true });

  // Add the tooltips and highlighters to the view
  this.tooltips = new TooltipsOverlay(this);

  this.highlighters.addToView(this);

  this.classListPreviewer = new ClassListPreviewer(this.inspector, this.classPanel);

  EventEmitter.decorate(this);
}

CssRuleView.prototype = {
  // The element that we're inspecting.
  _viewedElement: null,

  // Used for cancelling timeouts in the style filter.
  _filterChangedTimeout: null,

  // Empty, unconnected element of the same type as this node, used
  // to figure out how shorthand properties will be parsed.
  _dummyElement: null,

  // Get the dummy elemenet.
  get dummyElement() {
    return this._dummyElement;
  },

  // Get the filter search value.
  get searchValue() {
    return this.searchField.value.toLowerCase();
  },

  /**
   * Get an instance of SelectorHighlighter (used to highlight nodes that match
   * selectors in the rule-view). A new instance is only created the first time
   * this function is called. The same instance will then be returned.
   *
   * @return {Promise} Resolves to the instance of the highlighter.
   */
  getSelectorHighlighter: Task.async(function* () {
    if (!this.inspector) {
      return null;
    }

    let utils = this.inspector.toolbox.highlighterUtils;
    if (!utils.supportsCustomHighlighters()) {
      return null;
    }

    if (this.selectorHighlighter) {
      return this.selectorHighlighter;
    }

    try {
      let h = yield utils.getHighlighterByType("SelectorHighlighter");
      this.selectorHighlighter = h;
      return h;
    } catch (e) {
      // The SelectorHighlighter type could not be created in the
      // current target.  It could be an older server, or a XUL page.
      return null;
    }
  }),

  /**
   * Highlight/unhighlight all the nodes that match a given set of selectors
   * inside the document of the current selected node.
   * Only one selector can be highlighted at a time, so calling the method a
   * second time with a different selector will first unhighlight the previously
   * highlighted nodes.
   * Calling the method a second time with the same selector will just
   * unhighlight the highlighted nodes.
   *
   * @param {DOMNode} selectorIcon
   *        The icon that was clicked to toggle the selector. The
   *        class 'highlighted' will be added when the selector is
   *        highlighted.
   * @param {String} selector
   *        The selector used to find nodes in the page.
   */
  toggleSelectorHighlighter: Task.async(function* (selectorIcon, selector) {
    if (this.lastSelectorIcon) {
      this.lastSelectorIcon.classList.remove("highlighted");
    }
    selectorIcon.classList.remove("highlighted");

    let highlighter = yield this.getSelectorHighlighter();
    if (!highlighter) {
      return;
    }

    yield highlighter.hide();

    if (selector !== this.highlighters.selectorHighlighterShown) {
      this.highlighters.selectorHighlighterShown = selector;
      selectorIcon.classList.add("highlighted");
      this.lastSelectorIcon = selectorIcon;

      let node = this.inspector.selection.nodeFront;

      yield highlighter.show(node, {
        hideInfoBar: true,
        hideGuides: true,
        selector
      });

      this.emit("ruleview-selectorhighlighter-toggled", true);
    } else {
      this.highlighters.selectorHighlighterShown = null;
      this.emit("ruleview-selectorhighlighter-toggled", false);
    }
  }),

  /**
   * Get the type of a given node in the rule-view
   *
   * @param {DOMNode} node
   *        The node which we want information about
   * @return {Object} The type information object contains the following props:
   * - view {String} Always "rule" to indicate the rule view.
   * - type {String} One of the VIEW_NODE_XXX_TYPE const in
   *   client/inspector/shared/node-types
   * - value {Object} Depends on the type of the node
   * returns null of the node isn't anything we care about
   */
  getNodeInfo: function (node) {
    if (!node) {
      return null;
    }

    let type, value;
    let classes = node.classList;
    let prop = getParentTextProperty(node);

    if (classes.contains("ruleview-propertyname") && prop) {
      type = VIEW_NODE_PROPERTY_TYPE;
      value = {
        property: node.textContent,
        value: getPropertyNameAndValue(node).value,
        enabled: prop.enabled,
        overridden: prop.overridden,
        pseudoElement: prop.rule.pseudoElement,
        sheetHref: prop.rule.domRule.href,
        textProperty: prop
      };
    } else if (classes.contains("ruleview-propertyvalue") && prop) {
      type = VIEW_NODE_VALUE_TYPE;
      value = {
        property: getPropertyNameAndValue(node).name,
        value: node.textContent,
        enabled: prop.enabled,
        overridden: prop.overridden,
        pseudoElement: prop.rule.pseudoElement,
        sheetHref: prop.rule.domRule.href,
        textProperty: prop
      };
    } else if (classes.contains("ruleview-font-family") && prop) {
      type = VIEW_NODE_FONT_TYPE;
      value = {
        property: getPropertyNameAndValue(node).name,
        value: getPropertyNameAndValue(node).value,
        enabled: prop.enabled,
        overridden: prop.overridden,
        pseudoElement: prop.rule.pseudoElement,
        sheetHref: prop.rule.domRule.href,
        textProperty: prop
      };
    } else if (classes.contains("ruleview-shape-point") && prop) {
      type = VIEW_NODE_SHAPE_POINT_TYPE;
      value = {
        property: getPropertyNameAndValue(node).name,
        value: node.textContent,
        enabled: prop.enabled,
        overridden: prop.overridden,
        pseudoElement: prop.rule.pseudoElement,
        sheetHref: prop.rule.domRule.href,
        textProperty: prop,
        toggleActive: getShapeToggleActive(node),
        point: getShapePoint(node)
      };
    } else if ((classes.contains("ruleview-variable") ||
                classes.contains("ruleview-unmatched-variable")) && prop) {
      type = VIEW_NODE_VARIABLE_TYPE;
      value = {
        property: getPropertyNameAndValue(node).name,
        value: node.textContent,
        enabled: prop.enabled,
        overridden: prop.overridden,
        pseudoElement: prop.rule.pseudoElement,
        sheetHref: prop.rule.domRule.href,
        textProperty: prop,
        variable: node.dataset.variable
      };
    } else if (classes.contains("theme-link") &&
               !classes.contains("ruleview-rule-source") && prop) {
      type = VIEW_NODE_IMAGE_URL_TYPE;
      value = {
        property: getPropertyNameAndValue(node).name,
        value: node.parentNode.textContent,
        url: node.href,
        enabled: prop.enabled,
        overridden: prop.overridden,
        pseudoElement: prop.rule.pseudoElement,
        sheetHref: prop.rule.domRule.href,
        textProperty: prop
      };
    } else if (classes.contains("ruleview-selector-unmatched") ||
               classes.contains("ruleview-selector-matched") ||
               classes.contains("ruleview-selectorcontainer") ||
               classes.contains("ruleview-selector") ||
               classes.contains("ruleview-selector-attribute") ||
               classes.contains("ruleview-selector-pseudo-class") ||
               classes.contains("ruleview-selector-pseudo-class-lock")) {
      type = VIEW_NODE_SELECTOR_TYPE;
      value = this._getRuleEditorForNode(node).selectorText.textContent;
    } else if (classes.contains("ruleview-rule-source") ||
               classes.contains("ruleview-rule-source-label")) {
      type = VIEW_NODE_LOCATION_TYPE;
      let rule = this._getRuleEditorForNode(node).rule;
      value = (rule.sheet && rule.sheet.href) ? rule.sheet.href : rule.title;
    } else {
      return null;
    }

    return {
      view: "rule",
      type,
      value,
    };
  },

  /**
   * Retrieve the RuleEditor instance that should be stored on
   * the offset parent of the node
   */
  _getRuleEditorForNode: function (node) {
    if (!node.offsetParent) {
      // some nodes don't have an offsetParent, but their parentNode does
      node = node.parentNode;
    }
    return node.offsetParent._ruleEditor;
  },

  /**
   * Context menu handler.
   */
  _onContextMenu: function (event) {
    if (event.originalTarget.closest("input[type=text]") ||
        event.originalTarget.closest("input:not([type])") ||
        event.originalTarget.closest("textarea")) {
      return;
    }

    event.stopPropagation();
    event.preventDefault();

    this._contextmenu.show(event);
  },

  /**
   * Callback for copy event. Copy the selected text.
   *
   * @param {Event} event
   *        copy event object.
   */
  _onCopy: function (event) {
    if (event) {
      this.copySelection(event.target);
      event.preventDefault();
    }
  },

  /**
   * Copy the current selection. The current target is necessary
   * if the selection is inside an input or a textarea
   *
   * @param {DOMNode} target
   *        DOMNode target of the copy action
   */
  copySelection: function (target) {
    try {
      let text = "";

      let nodeName = target && target.nodeName;
      if (nodeName === "input" || nodeName == "textarea") {
        let start = Math.min(target.selectionStart, target.selectionEnd);
        let end = Math.max(target.selectionStart, target.selectionEnd);
        let count = end - start;
        text = target.value.substr(start, count);
      } else {
        text = this.styleWindow.getSelection().toString();

        // Remove any double newlines.
        text = text.replace(/(\r?\n)\r?\n/g, "$1");
      }

      clipboardHelper.copyString(text);
    } catch (e) {
      console.error(e);
    }
  },

  /**
   * A helper for _onAddRule that handles the case where the actor
   * does not support as-authored styles.
   */
  _onAddNewRuleNonAuthored: function () {
    let elementStyle = this._elementStyle;
    let element = elementStyle.element;
    let rules = elementStyle.rules;
    let pseudoClasses = element.pseudoClassLocks;

    this.pageStyle.addNewRule(element, pseudoClasses).then(options => {
      let newRule = new Rule(elementStyle, options);
      rules.push(newRule);
      let editor = new RuleEditor(this, newRule);
      newRule.editor = editor;

      // Insert the new rule editor after the inline element rule
      if (rules.length <= 1) {
        this.element.appendChild(editor.element);
      } else {
        for (let rule of rules) {
          if (rule.domRule.type === ELEMENT_STYLE) {
            let referenceElement = rule.editor.element.nextSibling;
            this.element.insertBefore(editor.element, referenceElement);
            break;
          }
        }
      }

      // Focus and make the new rule's selector editable
      editor.selectorText.click();
      elementStyle._changed();
    });
  },

  /**
   * Add a new rule to the current element.
   */
  _onAddRule: function () {
    let elementStyle = this._elementStyle;
    let element = elementStyle.element;
    let client = this.inspector.target.client;
    let pseudoClasses = element.pseudoClassLocks;

    if (!client.traits.addNewRule) {
      return;
    }

    if (!this.pageStyle.supportsAuthoredStyles) {
      // We're talking to an old server.
      this._onAddNewRuleNonAuthored();
      return;
    }

    // Adding a new rule with authored styles will cause the actor to
    // emit an event, which will in turn cause the rule view to be
    // updated.  So, we wait for this update and for the rule creation
    // request to complete, and then focus the new rule's selector.
    let eventPromise = this.once("ruleview-refreshed");
    let newRulePromise = this.pageStyle.addNewRule(element, pseudoClasses);
    promise.all([eventPromise, newRulePromise]).then((values) => {
      let options = values[1];
      // Be sure the reference the correct |rules| here.
      for (let rule of this._elementStyle.rules) {
        if (options.rule === rule.domRule) {
          rule.editor.selectorText.click();
          elementStyle._changed();
          break;
        }
      }
    });
  },

  /**
   * Disables add rule button when needed
   */
  refreshAddRuleButtonState: function () {
    let shouldBeDisabled = !this._viewedElement ||
                           !this.inspector.selection.isElementNode() ||
                           this.inspector.selection.isAnonymousNode();
    this.addRuleButton.disabled = shouldBeDisabled;
  },

  setPageStyle: function (pageStyle) {
    this.pageStyle = pageStyle;
  },

  /**
   * Return {Boolean} true if the rule view currently has an input
   * editor visible.
   */
  get isEditing() {
    return this.tooltips.isEditing ||
      this.element.querySelectorAll(".styleinspector-propertyeditor")
        .length > 0;
  },

  _handleUAStylePrefChange: function () {
    this.showUserAgentStyles = Services.prefs.getBoolPref(PREF_UA_STYLES);
    this._handlePrefChange(PREF_UA_STYLES);
  },

  _handleDefaultColorUnitPrefChange: function () {
    this._handlePrefChange(PREF_DEFAULT_COLOR_UNIT);
  },

  _handlePrefChange: function (pref) {
    // Reselect the currently selected element
    let refreshOnPrefs = [PREF_UA_STYLES, PREF_DEFAULT_COLOR_UNIT];
    if (refreshOnPrefs.indexOf(pref) > -1) {
      this.selectElement(this._viewedElement, true);
    }
  },

  /**
   * Set the filter style search value.
   * @param {String} value
   *        The search value.
   */
  setFilterStyles: function (value = "") {
    this.searchField.value = value;
    this.searchField.focus();
    this._onFilterStyles();
  },

  /**
   * Called when the user enters a search term in the filter style search box.
   */
  _onFilterStyles: function () {
    if (this._filterChangedTimeout) {
      clearTimeout(this._filterChangedTimeout);
    }

    let filterTimeout = (this.searchValue.length > 0) ?
                        FILTER_CHANGED_TIMEOUT : 0;
    this.searchClearButton.hidden = this.searchValue.length === 0;

    this._filterChangedTimeout = setTimeout(() => {
      if (this.searchField.value.length > 0) {
        this.searchField.setAttribute("filled", true);
      } else {
        this.searchField.removeAttribute("filled");
      }

      this.searchData = {
        searchPropertyMatch: FILTER_PROP_RE.exec(this.searchValue),
        searchPropertyName: this.searchValue,
        searchPropertyValue: this.searchValue,
        strictSearchValue: "",
        strictSearchPropertyName: false,
        strictSearchPropertyValue: false,
        strictSearchAllValues: false
      };

      if (this.searchData.searchPropertyMatch) {
        // Parse search value as a single property line and extract the
        // property name and value. If the parsed property name or value is
        // contained in backquotes (`), extract the value within the backquotes
        // and set the corresponding strict search for the property to true.
        if (FILTER_STRICT_RE.test(this.searchData.searchPropertyMatch[1])) {
          this.searchData.strictSearchPropertyName = true;
          this.searchData.searchPropertyName =
            FILTER_STRICT_RE.exec(this.searchData.searchPropertyMatch[1])[1];
        } else {
          this.searchData.searchPropertyName =
            this.searchData.searchPropertyMatch[1];
        }

        if (FILTER_STRICT_RE.test(this.searchData.searchPropertyMatch[2])) {
          this.searchData.strictSearchPropertyValue = true;
          this.searchData.searchPropertyValue =
            FILTER_STRICT_RE.exec(this.searchData.searchPropertyMatch[2])[1];
        } else {
          this.searchData.searchPropertyValue =
            this.searchData.searchPropertyMatch[2];
        }

        // Strict search for stylesheets will match the property line regex.
        // Extract the search value within the backquotes to be used
        // in the strict search for stylesheets in _highlightStyleSheet.
        if (FILTER_STRICT_RE.test(this.searchValue)) {
          this.searchData.strictSearchValue =
            FILTER_STRICT_RE.exec(this.searchValue)[1];
        }
      } else if (FILTER_STRICT_RE.test(this.searchValue)) {
        // If the search value does not correspond to a property line and
        // is contained in backquotes, extract the search value within the
        // backquotes and set the flag to perform a strict search for all
        // the values (selector, stylesheet, property and computed values).
        let searchValue = FILTER_STRICT_RE.exec(this.searchValue)[1];
        this.searchData.strictSearchAllValues = true;
        this.searchData.searchPropertyName = searchValue;
        this.searchData.searchPropertyValue = searchValue;
        this.searchData.strictSearchValue = searchValue;
      }

      this._clearHighlight(this.element);
      this._clearRules();
      this._createEditors();

      this.inspector.emit("ruleview-filtered");

      this._filterChangeTimeout = null;
    }, filterTimeout);
  },

  /**
   * Called when the user clicks on the clear button in the filter style search
   * box. Returns true if the search box is cleared and false otherwise.
   */
  _onClearSearch: function () {
    if (this.searchField.value) {
      this.setFilterStyles("");
      return true;
    }

    return false;
  },

  destroy: function () {
    this.isDestroyed = true;
    this.clear();

    this._dummyElement = null;
    // off handlers must have the same reference as their on handlers
    this._prefObserver.off(PREF_UA_STYLES, this._handleUAStylePrefChange);
    this._prefObserver.off(
      PREF_DEFAULT_COLOR_UNIT,
      this._handleDefaultColorUnitPrefChange
    );
    this._prefObserver.destroy();

    this._outputParser = null;

    // Remove context menu
    if (this._contextmenu) {
      this._contextmenu.destroy();
      this._contextmenu = null;
    }

    this.tooltips.destroy();
    this.highlighters.removeFromView(this);
    this.classListPreviewer.destroy();

    // Remove bound listeners
    this.shortcuts.destroy();
    this.element.removeEventListener("copy", this._onCopy);
    this.element.removeEventListener("contextmenu", this._onContextMenu);
    this.addRuleButton.removeEventListener("click", this._onAddRule);
    this.searchField.removeEventListener("input", this._onFilterStyles);
    this.searchClearButton.removeEventListener("click", this._onClearSearch);
    this.pseudoClassToggle.removeEventListener("click", this._onTogglePseudoClassPanel);
    this.classToggle.removeEventListener("click", this._onToggleClassPanel);
    this.hoverCheckbox.removeEventListener("click", this._onTogglePseudoClass);
    this.activeCheckbox.removeEventListener("click", this._onTogglePseudoClass);
    this.focusCheckbox.removeEventListener("click", this._onTogglePseudoClass);

    this.searchField = null;
    this.searchClearButton = null;
    this.pseudoClassPanel = null;
    this.pseudoClassToggle = null;
    this.classPanel = null;
    this.classToggle = null;
    this.hoverCheckbox = null;
    this.activeCheckbox = null;
    this.focusCheckbox = null;

    this.inspector = null;
    this.highlighters = null;
    this.styleDocument = null;
    this.styleWindow = null;

    if (this.element.parentNode) {
      this.element.remove();
    }

    if (this._elementStyle) {
      this._elementStyle.destroy();
    }

    this.popup.destroy();
  },

  /**
   * Mark the view as selecting an element, disabling all interaction, and
   * visually clearing the view after a few milliseconds to avoid confusion
   * about which element's styles the rule view shows.
   */
  _startSelectingElement: function () {
    this.element.classList.add("non-interactive");
  },

  /**
   * Mark the view as no longer selecting an element, re-enabling interaction.
   */
  _stopSelectingElement: function () {
    this.element.classList.remove("non-interactive");
  },

  /**
   * Update the view with a new selected element.
   *
   * @param {NodeActor} element
   *        The node whose style rules we'll inspect.
   * @param {Boolean} allowRefresh
   *        Update the view even if the element is the same as last time.
   */
  selectElement: function (element, allowRefresh = false) {
    let refresh = (this._viewedElement === element);
    if (refresh && !allowRefresh) {
      return promise.resolve(undefined);
    }

    if (this.popup.isOpen) {
      this.popup.hidePopup();
    }

    this.clear(false);
    this._viewedElement = element;

    this.clearPseudoClassPanel();
    this.refreshAddRuleButtonState();

    if (!this._viewedElement) {
      this._stopSelectingElement();
      this._clearRules();
      this._showEmpty();
      this.refreshPseudoClassPanel();
      return promise.resolve(undefined);
    }

    // To figure out how shorthand properties are interpreted by the
    // engine, we will set properties on a dummy element and observe
    // how their .style attribute reflects them as computed values.
    let dummyElementPromise = promise.resolve(this.styleDocument).then(document => {
      // ::before and ::after do not have a namespaceURI
      let namespaceURI = this.element.namespaceURI ||
          document.documentElement.namespaceURI;
      this._dummyElement = document.createElementNS(namespaceURI,
                                                   this.element.tagName);
    }).catch(promiseWarn);

    let elementStyle = new ElementStyle(element, this, this.store,
      this.pageStyle, this.showUserAgentStyles);
    this._elementStyle = elementStyle;

    this._startSelectingElement();

    return dummyElementPromise.then(() => {
      if (this._elementStyle === elementStyle) {
        return this._populate();
      }
      return undefined;
    }).then(() => {
      if (this._elementStyle === elementStyle) {
        if (!refresh) {
          this.element.scrollTop = 0;
        }
        this._stopSelectingElement();
        this._elementStyle.onChanged = () => {
          this._changed();
        };
      }
    }).catch(e => {
      if (this._elementStyle === elementStyle) {
        this._stopSelectingElement();
        this._clearRules();
      }
      console.error(e);
    });
  },

  /**
   * Update the rules for the currently highlighted element.
   */
  refreshPanel: function () {
    // Ignore refreshes during editing or when no element is selected.
    if (this.isEditing || !this._elementStyle) {
      return promise.resolve(undefined);
    }

    // Repopulate the element style once the current modifications are done.
    let promises = [];
    for (let rule of this._elementStyle.rules) {
      if (rule._applyingModifications) {
        promises.push(rule._applyingModifications);
      }
    }

    return promise.all(promises).then(() => {
      return this._populate();
    });
  },

  /**
   * Clear the pseudo class options panel by removing the checked and disabled
   * attributes for each checkbox.
   */
  clearPseudoClassPanel: function () {
    this.hoverCheckbox.checked = this.hoverCheckbox.disabled = false;
    this.activeCheckbox.checked = this.activeCheckbox.disabled = false;
    this.focusCheckbox.checked = this.focusCheckbox.disabled = false;
  },

  /**
   * Update the pseudo class options for the currently highlighted element.
   */
  refreshPseudoClassPanel: function () {
    if (!this._elementStyle || !this.inspector.selection.isElementNode()) {
      this.hoverCheckbox.disabled = true;
      this.activeCheckbox.disabled = true;
      this.focusCheckbox.disabled = true;
      return;
    }

    for (let pseudoClassLock of this._elementStyle.element.pseudoClassLocks) {
      switch (pseudoClassLock) {
        case ":hover": {
          this.hoverCheckbox.checked = true;
          break;
        }
        case ":active": {
          this.activeCheckbox.checked = true;
          break;
        }
        case ":focus": {
          this.focusCheckbox.checked = true;
          break;
        }
      }
    }
  },

  _populate: function () {
    let elementStyle = this._elementStyle;
    return this._elementStyle.populate().then(() => {
      if (this._elementStyle !== elementStyle || this.isDestroyed) {
        return null;
      }

      this._clearRules();
      let onEditorsReady = this._createEditors();
      this.refreshPseudoClassPanel();

      // Notify anyone that cares that we refreshed.
      return onEditorsReady.then(() => {
        this.emit("ruleview-refreshed");
      }, console.error);
    }).catch(promiseWarn);
  },

  /**
   * Show the user that the rule view has no node selected.
   */
  _showEmpty: function () {
    if (this.styleDocument.getElementById("ruleview-no-results")) {
      return;
    }

    createChild(this.element, "div", {
      id: "ruleview-no-results",
      class: "devtools-sidepanel-no-result",
      textContent: l10n("rule.empty")
    });
  },

  /**
   * Clear the rules.
   */
  _clearRules: function () {
    this.element.innerHTML = "";
  },

  /**
   * Clear the rule view.
   */
  clear: function (clearDom = true) {
    this.lastSelectorIcon = null;

    if (clearDom) {
      this._clearRules();
    }
    this._viewedElement = null;

    if (this._elementStyle) {
      this._elementStyle.destroy();
      this._elementStyle = null;
    }
  },

  /**
   * Called when the user has made changes to the ElementStyle.
   * Emits an event that clients can listen to.
   */
  _changed: function () {
    this.emit("ruleview-changed");
  },

  /**
   * Text for header that shows above rules for this element
   */
  get selectedElementLabel() {
    if (this._selectedElementLabel) {
      return this._selectedElementLabel;
    }
    this._selectedElementLabel = l10n("rule.selectedElement");
    return this._selectedElementLabel;
  },

  /**
   * Text for header that shows above rules for pseudo elements
   */
  get pseudoElementLabel() {
    if (this._pseudoElementLabel) {
      return this._pseudoElementLabel;
    }
    this._pseudoElementLabel = l10n("rule.pseudoElement");
    return this._pseudoElementLabel;
  },

  get showPseudoElements() {
    if (this._showPseudoElements === undefined) {
      this._showPseudoElements =
        Services.prefs.getBoolPref("devtools.inspector.show_pseudo_elements");
    }
    return this._showPseudoElements;
  },

  /**
   * Creates an expandable container in the rule view
   *
   * @param  {String} label
   *         The label for the container header
   * @param  {Boolean} isPseudo
   *         Whether or not the container will hold pseudo element rules
   * @return {DOMNode} The container element
   */
  createExpandableContainer: function (label, isPseudo = false) {
    let header = this.styleDocument.createElementNS(HTML_NS, "div");
    header.className = this._getRuleViewHeaderClassName(true);
    header.textContent = label;

    let twisty = this.styleDocument.createElementNS(HTML_NS, "span");
    twisty.className = "ruleview-expander theme-twisty";
    twisty.setAttribute("open", "true");

    header.insertBefore(twisty, header.firstChild);
    this.element.appendChild(header);

    let container = this.styleDocument.createElementNS(HTML_NS, "div");
    container.classList.add("ruleview-expandable-container");
    container.hidden = false;
    this.element.appendChild(container);

    header.addEventListener("click", () => {
      this._toggleContainerVisibility(twisty, container, isPseudo,
        !this.showPseudoElements);
    });

    if (isPseudo) {
      this._toggleContainerVisibility(twisty, container, isPseudo,
        this.showPseudoElements);
    }

    return container;
  },

  /**
   * Toggle the visibility of an expandable container
   *
   * @param  {DOMNode}  twisty
   *         Clickable toggle DOM Node
   * @param  {DOMNode}  container
   *         Expandable container DOM Node
   * @param  {Boolean}  isPseudo
   *         Whether or not the container will hold pseudo element rules
   * @param  {Boolean}  showPseudo
   *         Whether or not pseudo element rules should be displayed
   */
  _toggleContainerVisibility: function (twisty, container, isPseudo,
      showPseudo) {
    let isOpen = twisty.getAttribute("open");

    if (isPseudo) {
      this._showPseudoElements = !!showPseudo;

      Services.prefs.setBoolPref("devtools.inspector.show_pseudo_elements",
        this.showPseudoElements);

      container.hidden = !this.showPseudoElements;
      isOpen = !this.showPseudoElements;
    } else {
      container.hidden = !container.hidden;
    }

    if (isOpen) {
      twisty.removeAttribute("open");
    } else {
      twisty.setAttribute("open", "true");
    }
  },

  _getRuleViewHeaderClassName: function (isPseudo) {
    let baseClassName = "ruleview-header";
    return isPseudo ? baseClassName + " ruleview-expandable-header" :
      baseClassName;
  },

  /**
   * Creates editor UI for each of the rules in _elementStyle.
   */
  _createEditors: function () {
    // Run through the current list of rules, attaching
    // their editors in order.  Create editors if needed.
    let lastInheritedSource = "";
    let lastKeyframes = null;
    let seenPseudoElement = false;
    let seenNormalElement = false;
    let seenSearchTerm = false;
    let container = null;

    if (!this._elementStyle.rules) {
      return promise.resolve();
    }

    let editorReadyPromises = [];
    for (let rule of this._elementStyle.rules) {
      if (rule.domRule.system) {
        continue;
      }

      // Initialize rule editor
      if (!rule.editor) {
        rule.editor = new RuleEditor(this, rule);
        editorReadyPromises.push(rule.editor.once("source-link-updated"));
      }

      // Filter the rules and highlight any matches if there is a search input
      if (this.searchValue && this.searchData) {
        if (this.highlightRule(rule)) {
          seenSearchTerm = true;
        } else if (rule.domRule.type !== ELEMENT_STYLE) {
          continue;
        }
      }

      // Only print header for this element if there are pseudo elements
      if (seenPseudoElement && !seenNormalElement && !rule.pseudoElement) {
        seenNormalElement = true;
        let div = this.styleDocument.createElementNS(HTML_NS, "div");
        div.className = this._getRuleViewHeaderClassName();
        div.textContent = this.selectedElementLabel;
        this.element.appendChild(div);
      }

      let inheritedSource = rule.inherited;
      if (inheritedSource && inheritedSource !== lastInheritedSource) {
        let div = this.styleDocument.createElementNS(HTML_NS, "div");
        div.className = this._getRuleViewHeaderClassName();
        div.textContent = rule.inheritedSource;
        lastInheritedSource = inheritedSource;
        this.element.appendChild(div);
      }

      if (!seenPseudoElement && rule.pseudoElement) {
        seenPseudoElement = true;
        container = this.createExpandableContainer(this.pseudoElementLabel,
                                                   true);
      }

      let keyframes = rule.keyframes;
      if (keyframes && keyframes !== lastKeyframes) {
        lastKeyframes = keyframes;
        container = this.createExpandableContainer(rule.keyframesName);
      }

      if (container && (rule.pseudoElement || keyframes)) {
        container.appendChild(rule.editor.element);
      } else {
        this.element.appendChild(rule.editor.element);
      }
    }

    if (this.searchValue && !seenSearchTerm) {
      this.searchField.classList.add("devtools-style-searchbox-no-match");
    } else {
      this.searchField.classList.remove("devtools-style-searchbox-no-match");
    }

    return promise.all(editorReadyPromises);
  },

  /**
   * Highlight rules that matches the filter search value and returns a
   * boolean indicating whether or not rules were highlighted.
   *
   * @param  {Rule} rule
   *         The rule object we're highlighting if its rule selectors or
   *         property values match the search value.
   * @return {Boolean} true if the rule was highlighted, false otherwise.
   */
  highlightRule: function (rule) {
    let isRuleSelectorHighlighted = this._highlightRuleSelector(rule);
    let isStyleSheetHighlighted = this._highlightStyleSheet(rule);
    let isHighlighted = isRuleSelectorHighlighted || isStyleSheetHighlighted;

    // Highlight search matches in the rule properties
    for (let textProp of rule.textProps) {
      if (!textProp.invisible && this._highlightProperty(textProp.editor)) {
        isHighlighted = true;
      }
    }

    return isHighlighted;
  },

  /**
   * Highlights the rule selector that matches the filter search value and
   * returns a boolean indicating whether or not the selector was highlighted.
   *
   * @param  {Rule} rule
   *         The Rule object.
   * @return {Boolean} true if the rule selector was highlighted,
   *         false otherwise.
   */
  _highlightRuleSelector: function (rule) {
    let isSelectorHighlighted = false;

    let selectorNodes = [...rule.editor.selectorText.childNodes];
    if (rule.domRule.type === CSSRule.KEYFRAME_RULE) {
      selectorNodes = [rule.editor.selectorText];
    } else if (rule.domRule.type === ELEMENT_STYLE) {
      selectorNodes = [];
    }

    // Highlight search matches in the rule selectors
    for (let selectorNode of selectorNodes) {
      let selector = selectorNode.textContent.toLowerCase();
      if ((this.searchData.strictSearchAllValues &&
           selector === this.searchData.strictSearchValue) ||
          (!this.searchData.strictSearchAllValues &&
           selector.includes(this.searchValue))) {
        selectorNode.classList.add("ruleview-highlight");
        isSelectorHighlighted = true;
      }
    }

    return isSelectorHighlighted;
  },

  /**
   * Highlights the stylesheet source that matches the filter search value and
   * returns a boolean indicating whether or not the stylesheet source was
   * highlighted.
   *
   * @return {Boolean} true if the stylesheet source was highlighted, false
   *         otherwise.
   */
  _highlightStyleSheet: function (rule) {
    let styleSheetSource = rule.title.toLowerCase();
    let isStyleSheetHighlighted = this.searchData.strictSearchValue ?
      styleSheetSource === this.searchData.strictSearchValue :
      styleSheetSource.includes(this.searchValue);

    if (isStyleSheetHighlighted) {
      rule.editor.source.classList.add("ruleview-highlight");
    }

    return isStyleSheetHighlighted;
  },

  /**
   * Highlights the rule properties and computed properties that match the
   * filter search value and returns a boolean indicating whether or not the
   * property or computed property was highlighted.
   *
   * @param  {TextPropertyEditor} editor
   *         The rule property TextPropertyEditor object.
   * @return {Boolean} true if the property or computed property was
   *         highlighted, false otherwise.
   */
  _highlightProperty: function (editor) {
    let isPropertyHighlighted = this._highlightRuleProperty(editor);
    let isComputedHighlighted = this._highlightComputedProperty(editor);

    // Expand the computed list if a computed property is highlighted and the
    // property rule is not highlighted
    if (!isPropertyHighlighted && isComputedHighlighted &&
        !editor.computed.hasAttribute("user-open")) {
      editor.expandForFilter();
    }

    return isPropertyHighlighted || isComputedHighlighted;
  },

  /**
   * Called when TextPropertyEditor is updated and updates the rule property
   * highlight.
   *
   * @param  {TextPropertyEditor} editor
   *         The rule property TextPropertyEditor object.
   */
  _updatePropertyHighlight: function (editor) {
    if (!this.searchValue || !this.searchData) {
      return;
    }

    this._clearHighlight(editor.element);

    if (this._highlightProperty(editor)) {
      this.searchField.classList.remove("devtools-style-searchbox-no-match");
    }
  },

  /**
   * Highlights the rule property that matches the filter search value
   * and returns a boolean indicating whether or not the property was
   * highlighted.
   *
   * @param  {TextPropertyEditor} editor
   *         The rule property TextPropertyEditor object.
   * @return {Boolean} true if the rule property was highlighted,
   *         false otherwise.
   */
  _highlightRuleProperty: function (editor) {
    // Get the actual property value displayed in the rule view
    let propertyName = editor.prop.name.toLowerCase();
    let propertyValue = editor.valueSpan.textContent.toLowerCase();

    return this._highlightMatches(editor.container, propertyName,
                                  propertyValue);
  },

  /**
   * Highlights the computed property that matches the filter search value and
   * returns a boolean indicating whether or not the computed property was
   * highlighted.
   *
   * @param  {TextPropertyEditor} editor
   *         The rule property TextPropertyEditor object.
   * @return {Boolean} true if the computed property was highlighted, false
   *         otherwise.
   */
  _highlightComputedProperty: function (editor) {
    let isComputedHighlighted = false;

    // Highlight search matches in the computed list of properties
    editor._populateComputed();
    for (let computed of editor.prop.computed) {
      if (computed.element) {
        // Get the actual property value displayed in the computed list
        let computedName = computed.name.toLowerCase();
        let computedValue = computed.parsedValue.toLowerCase();

        isComputedHighlighted = this._highlightMatches(computed.element,
          computedName, computedValue) ? true : isComputedHighlighted;
      }
    }

    return isComputedHighlighted;
  },

  /**
   * Helper function for highlightRules that carries out highlighting the given
   * element if the search terms match the property, and returns a boolean
   * indicating whether or not the search terms match.
   *
   * @param  {DOMNode} element
   *         The node to highlight if search terms match
   * @param  {String} propertyName
   *         The property name of a rule
   * @param  {String} propertyValue
   *         The property value of a rule
   * @return {Boolean} true if the given search terms match the property, false
   *         otherwise.
   */
  _highlightMatches: function (element, propertyName, propertyValue) {
    let {
      searchPropertyName,
      searchPropertyValue,
      searchPropertyMatch,
      strictSearchPropertyName,
      strictSearchPropertyValue,
      strictSearchAllValues,
    } = this.searchData;
    let matches = false;

    // If the inputted search value matches a property line like
    // `font-family: arial`, then check to make sure the name and value match.
    // Otherwise, just compare the inputted search string directly against the
    // name and value of the rule property.
    let hasNameAndValue = searchPropertyMatch &&
                          searchPropertyName &&
                          searchPropertyValue;
    let isMatch = (value, query, isStrict) => {
      return isStrict ? value === query : query && value.includes(query);
    };

    if (hasNameAndValue) {
      matches =
        isMatch(propertyName, searchPropertyName, strictSearchPropertyName) &&
        isMatch(propertyValue, searchPropertyValue, strictSearchPropertyValue);
    } else {
      matches =
        isMatch(propertyName, searchPropertyName,
                strictSearchPropertyName || strictSearchAllValues) ||
        isMatch(propertyValue, searchPropertyValue,
                strictSearchPropertyValue || strictSearchAllValues);
    }

    if (matches) {
      element.classList.add("ruleview-highlight");
    }

    return matches;
  },

  /**
   * Clear all search filter highlights in the panel, and close the computed
   * list if toggled opened
   */
  _clearHighlight: function (element) {
    for (let el of element.querySelectorAll(".ruleview-highlight")) {
      el.classList.remove("ruleview-highlight");
    }

    for (let computed of element.querySelectorAll(
          ".ruleview-computedlist[filter-open]")) {
      computed.parentNode._textPropertyEditor.collapseForFilter();
    }
  },

  /**
   * Called when the pseudo class panel button is clicked and toggles
   * the display of the pseudo class panel.
   */
  _onTogglePseudoClassPanel: function () {
    if (this.pseudoClassPanel.hidden) {
      this.showPseudoClassPanel();
    } else {
      this.hidePseudoClassPanel();
    }
  },

  showPseudoClassPanel: function () {
    this.hideClassPanel();

    this.pseudoClassToggle.classList.add("checked");
    this.hoverCheckbox.setAttribute("tabindex", "0");
    this.activeCheckbox.setAttribute("tabindex", "0");
    this.focusCheckbox.setAttribute("tabindex", "0");

    this.pseudoClassPanel.hidden = false;
  },

  hidePseudoClassPanel: function () {
    this.pseudoClassToggle.classList.remove("checked");
    this.hoverCheckbox.setAttribute("tabindex", "-1");
    this.activeCheckbox.setAttribute("tabindex", "-1");
    this.focusCheckbox.setAttribute("tabindex", "-1");

    this.pseudoClassPanel.hidden = true;
  },

  /**
   * Called when a pseudo class checkbox is clicked and toggles
   * the pseudo class for the current selected element.
   */
  _onTogglePseudoClass: function (event) {
    let target = event.currentTarget;
    this.inspector.togglePseudoClass(target.value);
  },

  /**
   * Called when the class panel button is clicked and toggles the display of the class
   * panel.
   */
  _onToggleClassPanel: function () {
    if (this.classPanel.hidden) {
      this.showClassPanel();
    } else {
      this.hideClassPanel();
    }
  },

  showClassPanel: function () {
    this.hidePseudoClassPanel();

    this.classToggle.classList.add("checked");
    this.classPanel.hidden = false;

    this.classListPreviewer.focusAddClassField();
  },

  hideClassPanel: function () {
    this.classToggle.classList.remove("checked");
    this.classPanel.hidden = true;
  },

  /**
   * Handle the keypress event in the rule view.
   */
  _onShortcut: function (name, event) {
    if (!event.target.closest("#sidebar-panel-ruleview")) {
      return;
    }

    if (name === "CmdOrCtrl+F") {
      this.searchField.focus();
      event.preventDefault();
    } else if ((name === "Return" || name === "Space") &&
               this.element.classList.contains("non-interactive")) {
      event.preventDefault();
    } else if (name === "Escape" &&
               event.target === this.searchField &&
               this._onClearSearch()) {
      // Handle the search box's keypress event. If the escape key is pressed,
      // clear the search box field.
      event.preventDefault();
      event.stopPropagation();
    }
  }

};

/**
 * Helper functions
 */

/**
 * Walk up the DOM from a given node until a parent property holder is found.
 * For elements inside the computed property list, the non-computed parent
 * property holder will be returned
 *
 * @param {DOMNode} node
 *        The node to start from
 * @return {DOMNode} The parent property holder node, or null if not found
 */
function getParentTextPropertyHolder(node) {
  while (true) {
    if (!node || !node.classList) {
      return null;
    }
    if (node.classList.contains("ruleview-property")) {
      return node;
    }
    node = node.parentNode;
  }
}

/**
 * For any given node, find the TextProperty it is in if any
 * @param {DOMNode} node
 *        The node to start from
 * @return {TextProperty}
 */
function getParentTextProperty(node) {
  let parent = getParentTextPropertyHolder(node);
  if (!parent) {
    return null;
  }

  let propValue = parent.querySelector(".ruleview-propertyvalue");
  if (!propValue) {
    return null;
  }

  return propValue.textProperty;
}

/**
 * Walker up the DOM from a given node until a parent property holder is found,
 * and return the textContent for the name and value nodes.
 * Stops at the first property found, so if node is inside the computed property
 * list, the computed property will be returned
 *
 * @param {DOMNode} node
 *        The node to start from
 * @return {Object} {name, value}
 */
function getPropertyNameAndValue(node) {
  while (true) {
    if (!node || !node.classList) {
      return null;
    }
    // Check first for ruleview-computed since it's the deepest
    if (node.classList.contains("ruleview-computed") ||
        node.classList.contains("ruleview-property")) {
      return {
        name: node.querySelector(".ruleview-propertyname").textContent,
        value: node.querySelector(".ruleview-propertyvalue").textContent
      };
    }
    node = node.parentNode;
  }
}

/**
 * Walk up the DOM from a given node until a parent property holder is found,
 * and return an active shape toggle if one exists.
 *
 * @param {DOMNode} node
 *        The node to start from
 * @returns {DOMNode} The active shape toggle node, if one exists.
 */
function getShapeToggleActive(node) {
  while (true) {
    if (!node || !node.classList) {
      return null;
    }
    // Check first for ruleview-computed since it's the deepest
    if (node.classList.contains("ruleview-computed") ||
        node.classList.contains("ruleview-property")) {
      return node.querySelector(".ruleview-shapeswatch.active");
    }
    node = node.parentNode;
  }
}

/**
 * Get the point associated with a shape point node.
 *
 * @param {DOMNode} node
 *        A shape point node
 * @returns {String} The point associated with the given node.
 */
function getShapePoint(node) {
  let classList = node.classList;
  let point = node.dataset.point;
  // Inset points use classes instead of data because a single span can represent
  // multiple points.
  let insetClasses = [];
  classList.forEach(className => {
    if (INSET_POINT_TYPES.includes(className)) {
      insetClasses.push(className);
    }
  });
  if (insetClasses.length > 0) {
    point = insetClasses.join(",");
  }
  return point;
}

function RuleViewTool(inspector, window) {
  this.inspector = inspector;
  this.document = window.document;

  this.view = new CssRuleView(this.inspector, this.document);

  this.clearUserProperties = this.clearUserProperties.bind(this);
  this.refresh = this.refresh.bind(this);
  this.onMutations = this.onMutations.bind(this);
  this.onPanelSelected = this.onPanelSelected.bind(this);
  this.onResized = this.onResized.bind(this);
  this.onSelected = this.onSelected.bind(this);
  this.onViewRefreshed = this.onViewRefreshed.bind(this);

  this.view.on("ruleview-refreshed", this.onViewRefreshed);
  this.inspector.selection.on("detached-front", this.onSelected);
  this.inspector.selection.on("new-node-front", this.onSelected);
  this.inspector.selection.on("pseudoclass", this.refresh);
  this.inspector.target.on("navigate", this.clearUserProperties);
  this.inspector.ruleViewSideBar.on("ruleview-selected", this.onPanelSelected);
  this.inspector.sidebar.on("ruleview-selected", this.onPanelSelected);
  this.inspector.pageStyle.on("stylesheet-updated", this.refresh);
  this.inspector.walker.on("mutations", this.onMutations);
  this.inspector.walker.on("resize", this.onResized);

  this.onSelected();
}

RuleViewTool.prototype = {
  isSidebarActive: function () {
    if (!this.view) {
      return false;
    }

    return this.inspector.isSplitRuleViewEnabled ?
      true : this.inspector.sidebar.getCurrentTabID() == "ruleview";
  },

  onSelected: function (event) {
    // Ignore the event if the view has been destroyed, or if it's inactive.
    // But only if the current selection isn't null. If it's been set to null,
    // let the update go through as this is needed to empty the view on
    // navigation.
    if (!this.view) {
      return;
    }

    let isInactive = !this.isSidebarActive() &&
                     this.inspector.selection.nodeFront;
    if (isInactive) {
      return;
    }

    this.view.setPageStyle(this.inspector.pageStyle);

    if (!this.inspector.selection.isConnected() ||
        !this.inspector.selection.isElementNode()) {
      this.view.selectElement(null);
      return;
    }

    if (!event || event == "new-node-front") {
      let done = this.inspector.updating("rule-view");
      this.view.selectElement(this.inspector.selection.nodeFront)
        .then(done, done);
    }
  },

  refresh: function () {
    if (this.isSidebarActive()) {
      this.view.refreshPanel();
    }
  },

  clearUserProperties: function () {
    if (this.view && this.view.store && this.view.store.userProperties) {
      this.view.store.userProperties.clear();
    }
  },

  onPanelSelected: function () {
    if (this.inspector.selection.nodeFront === this.view._viewedElement) {
      this.refresh();
    } else {
      this.onSelected();
    }
  },

  onViewRefreshed: function () {
    this.inspector.emit("rule-view-refreshed");
  },

  /**
   * When markup mutations occur, if an attribute of the selected node changes,
   * we need to refresh the view as that might change the node's styles.
   */
  onMutations: function (mutations) {
    for (let {type, target} of mutations) {
      if (target === this.inspector.selection.nodeFront &&
          type === "attributes") {
        this.refresh();
        break;
      }
    }
  },

  /**
   * When the window gets resized, this may cause media-queries to match, and
   * therefore, different styles may apply.
   */
  onResized: function () {
    this.refresh();
  },

  destroy: function () {
    this.inspector.walker.off("mutations", this.onMutations);
    this.inspector.walker.off("resize", this.onResized);
    this.inspector.selection.off("detached-front", this.onSelected);
    this.inspector.selection.off("pseudoclass", this.refresh);
    this.inspector.selection.off("new-node-front", this.onSelected);
    this.inspector.target.off("navigate", this.clearUserProperties);
    this.inspector.sidebar.off("ruleview-selected", this.onPanelSelected);
    if (this.inspector.pageStyle) {
      this.inspector.pageStyle.off("stylesheet-updated", this.refresh);
    }

    this.view.off("ruleview-refreshed", this.onViewRefreshed);

    this.view.destroy();

    this.view = this.document = this.inspector = null;
  }
};

exports.CssRuleView = CssRuleView;
exports.RuleViewTool = RuleViewTool;
PK
!<>��$�+�+Uchrome/devtools/modules/devtools/client/inspector/rules/views/class-list-previewer.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/shared/old-event-emitter");
const {LocalizationHelper} = require("devtools/shared/l10n");

const L10N = new LocalizationHelper("devtools/client/locales/inspector.properties");

// This serves as a local cache for the classes applied to each of the node we care about
// here.
// The map is indexed by NodeFront. Any time a new node is selected in the inspector, an
// entry is added here, indexed by the corresponding NodeFront.
// The value for each entry is an array of each of the class this node has. Items of this
// array are objects like: { name, isApplied } where the name is the class itself, and
// isApplied is a Boolean indicating if the class is applied on the node or not.
const CLASSES = new WeakMap();

/**
 * Manages the list classes per DOM elements we care about.
 * The actual list is stored in the CLASSES const, indexed by NodeFront objects.
 * The responsibility of this class is to be the source of truth for anyone who wants to
 * know which classes a given NodeFront has, and which of these are enabled and which are
 * disabled.
 * It also reacts to DOM mutations so the list of classes is up to date with what is in
 * the DOM.
 * It can also be used to enable/disable a given class, or add classes.
 *
 * @param {Inspector} inspector
 *        The current inspector instance.
 */
function ClassListPreviewerModel(inspector) {
  EventEmitter.decorate(this);

  this.inspector = inspector;

  this.onMutations = this.onMutations.bind(this);
  this.inspector.on("markupmutation", this.onMutations);

  this.classListProxyNode = this.inspector.panelDoc.createElement("div");
}

ClassListPreviewerModel.prototype = {
  destroy() {
    this.inspector.off("markupmutation", this.onMutations);
    this.inspector = null;
    this.classListProxyNode = null;
  },

  /**
   * The current node selection (which only returns if the node is an ELEMENT_NODE type
   * since that's the only type this model can work with.)
   */
  get currentNode() {
    if (this.inspector.selection.isElementNode() &&
        !this.inspector.selection.isPseudoElementNode()) {
      return this.inspector.selection.nodeFront;
    }
    return null;
  },

  /**
   * The class states for the current node selection. See the documentation of the CLASSES
   * constant.
   */
  get currentClasses() {
    if (!this.currentNode) {
      return [];
    }

    if (!CLASSES.has(this.currentNode)) {
      // Use the proxy node to get a clean list of classes.
      this.classListProxyNode.className = this.currentNode.className;
      let nodeClasses = [...new Set([...this.classListProxyNode.classList])].map(name => {
        return { name, isApplied: true };
      });

      CLASSES.set(this.currentNode, nodeClasses);
    }

    return CLASSES.get(this.currentNode);
  },

  /**
   * Same as currentClasses, but returns it in the form of a className string, where only
   * enabled classes are added.
   */
  get currentClassesPreview() {
    return this.currentClasses.filter(({ isApplied }) => isApplied)
                              .map(({ name }) => name)
                              .join(" ");
  },

  /**
   * Set the state for a given class on the current node.
   *
   * @param {String} name
   *        The class which state should be changed.
   * @param {Boolean} isApplied
   *        True if the class should be enabled, false otherwise.
   * @return {Promise} Resolves when the change has been made in the DOM.
   */
  setClassState(name, isApplied) {
    // Do the change in our local model.
    let nodeClasses = this.currentClasses;
    nodeClasses.find(({ name: cName }) => cName === name).isApplied = isApplied;

    return this.applyClassState();
  },

  /**
   * Add several classes to the current node at once.
   *
   * @param {String} classNameString
   *        The string that contains all classes.
   * @return {Promise} Resolves when the change has been made in the DOM.
   */
  addClassName(classNameString) {
    this.classListProxyNode.className = classNameString;
    return Promise.all([...new Set([...this.classListProxyNode.classList])].map(name => {
      return this.addClass(name);
    }));
  },

  /**
   * Add a class to the current node at once.
   *
   * @param {String} name
   *        The class to be added.
   * @return {Promise} Resolves when the change has been made in the DOM.
   */
  addClass(name) {
    // Avoid adding the same class again.
    if (this.currentClasses.some(({ name: cName }) => cName === name)) {
      return Promise.resolve();
    }

    // Change the local model, so we retain the state of the existing classes.
    this.currentClasses.push({ name, isApplied: true });

    return this.applyClassState();
  },

  /**
   * Used internally by other functions like addClass or setClassState. Actually applies
   * the class change to the DOM.
   *
   * @return {Promise} Resolves when the change has been made in the DOM.
   */
  applyClassState() {
    // If there is no valid inspector selection, bail out silently. No need to report an
    // error here.
    if (!this.currentNode) {
      return Promise.resolve();
    }

    // Remember which node we changed and the className we applied, so we can filter out
    // dom mutations that are caused by us in onMutations.
    this.lastStateChange = {
      node: this.currentNode,
      className: this.currentClassesPreview
    };

    // Apply the change to the node.
    let mod = this.currentNode.startModifyingAttributes();
    mod.setAttribute("class", this.currentClassesPreview);
    return mod.apply();
  },

  onMutations(e, mutations) {
    for (let {type, target, attributeName} of mutations) {
      // Only care if this mutation is for the class attribute.
      if (type !== "attributes" || attributeName !== "class") {
        continue;
      }

      let isMutationForOurChange = this.lastStateChange &&
                                   target === this.lastStateChange.node &&
                                   target.className === this.lastStateChange.className;

      if (!isMutationForOurChange) {
        CLASSES.delete(target);
        if (target === this.currentNode) {
          this.emit("current-node-class-changed");
        }
      }
    }
  }
};

/**
 * This UI widget shows a textfield and a series of checkboxes in the rule-view. It is
 * used to toggle classes on the current node selection, and add new classes.
 *
 * @param {Inspector} inspector
 *        The current inspector instance.
 * @param {DomNode} containerEl
 *        The element in the rule-view where the widget should go.
 */
function ClassListPreviewer(inspector, containerEl) {
  this.inspector = inspector;
  this.containerEl = containerEl;
  this.model = new ClassListPreviewerModel(inspector);

  this.onNewSelection = this.onNewSelection.bind(this);
  this.onCheckBoxChanged = this.onCheckBoxChanged.bind(this);
  this.onKeyPress = this.onKeyPress.bind(this);
  this.onCurrentNodeClassChanged = this.onCurrentNodeClassChanged.bind(this);

  // Create the add class text field.
  this.addEl = this.doc.createElement("input");
  this.addEl.classList.add("devtools-textinput");
  this.addEl.classList.add("add-class");
  this.addEl.setAttribute("placeholder",
    L10N.getStr("inspector.classPanel.newClass.placeholder"));
  this.addEl.addEventListener("keypress", this.onKeyPress);
  this.containerEl.appendChild(this.addEl);

  // Create the class checkboxes container.
  this.classesEl = this.doc.createElement("div");
  this.classesEl.classList.add("classes");
  this.containerEl.appendChild(this.classesEl);

  // Start listening for interesting events.
  this.inspector.selection.on("new-node-front", this.onNewSelection);
  this.containerEl.addEventListener("input", this.onCheckBoxChanged);
  this.model.on("current-node-class-changed", this.onCurrentNodeClassChanged);
}

ClassListPreviewer.prototype = {
  destroy() {
    this.inspector.selection.off("new-node-front", this.onNewSelection);
    this.addEl.removeEventListener("keypress", this.onKeyPress);
    this.containerEl.removeEventListener("input", this.onCheckBoxChanged);

    this.containerEl.innerHTML = "";

    this.model.destroy();
    this.containerEl = null;
    this.inspector = null;
    this.addEl = null;
    this.classesEl = null;
  },

  get doc() {
    return this.containerEl.ownerDocument;
  },

  /**
   * Render the content of the panel. You typically don't need to call this as the panel
   * renders itself on inspector selection changes.
   */
  render() {
    this.classesEl.innerHTML = "";

    for (let { name, isApplied } of this.model.currentClasses) {
      let checkBox = this.renderCheckBox(name, isApplied);
      this.classesEl.appendChild(checkBox);
    }

    if (!this.model.currentClasses.length) {
      this.classesEl.appendChild(this.renderNoClassesMessage());
    }
  },

  /**
   * Render a single checkbox for a given classname.
   *
   * @param {String} name
   *        The name of this class.
   * @param {Boolean} isApplied
   *        Is this class currently applied on the DOM node.
   * @return {DOMNode} The DOM element for this checkbox.
   */
  renderCheckBox(name, isApplied) {
    let box = this.doc.createElement("input");
    box.setAttribute("type", "checkbox");
    if (isApplied) {
      box.setAttribute("checked", "checked");
    }
    box.dataset.name = name;

    let labelWrapper = this.doc.createElement("label");
    labelWrapper.setAttribute("title", name);
    labelWrapper.appendChild(box);

    // A child element is required to do the ellipsis.
    let label = this.doc.createElement("span");
    label.textContent = name;
    labelWrapper.appendChild(label);

    return labelWrapper;
  },

  /**
   * Render the message displayed in the panel when the current element has no classes.
   *
   * @return {DOMNode} The DOM element for the message.
   */
  renderNoClassesMessage() {
    let msg = this.doc.createElement("p");
    msg.classList.add("no-classes");
    msg.textContent = L10N.getStr("inspector.classPanel.noClasses");
    return msg;
  },

  /**
   * Focus the add-class text field.
   */
  focusAddClassField() {
    if (this.addEl) {
      this.addEl.focus();
    }
  },

  onCheckBoxChanged({ target }) {
    if (!target.dataset.name) {
      return;
    }

    this.model.setClassState(target.dataset.name, target.checked).catch(e => {
      // Only log the error if the panel wasn't destroyed in the meantime.
      if (this.containerEl) {
        console.error(e);
      }
    });
  },

  onKeyPress(event) {
    if (event.key !== "Enter" || this.addEl.value === "") {
      return;
    }

    this.model.addClassName(this.addEl.value).then(() => {
      this.render();
      this.addEl.value = "";
    }).catch(e => {
      // Only log the error if the panel wasn't destroyed in the meantime.
      if (this.containerEl) {
        console.error(e);
      }
    });
  },

  onNewSelection() {
    this.render();
  },

  onCurrentNodeClassChanged() {
    this.render();
  }
};

module.exports = ClassListPreviewer;
PK
!<��g`g`Lchrome/devtools/modules/devtools/client/inspector/rules/views/rule-editor.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {l10n} = require("devtools/shared/inspector/css-logic");
const {ELEMENT_STYLE} = require("devtools/shared/specs/styles");
const Rule = require("devtools/client/inspector/rules/models/rule");
const {
  InplaceEditor,
  editableField,
  editableItem
} = require("devtools/client/shared/inplace-editor");
const TextPropertyEditor =
  require("devtools/client/inspector/rules/views/text-property-editor");
const {
  createChild,
  blurOnMultipleProperties,
  promiseWarn
} = require("devtools/client/inspector/shared/utils");
const {
  parseNamedDeclarations,
  parsePseudoClassesAndAttributes,
  SELECTOR_ATTRIBUTE,
  SELECTOR_ELEMENT,
  SELECTOR_PSEUDO_CLASS
} = require("devtools/shared/css/parsing-utils");
const promise = require("promise");
const Services = require("Services");
const EventEmitter = require("devtools/shared/old-event-emitter");
const {Task} = require("devtools/shared/task");
const {Tools} = require("devtools/client/definitions");
const {gDevTools} = require("devtools/client/framework/devtools");
const CssLogic = require("devtools/shared/inspector/css-logic");

const STYLE_INSPECTOR_PROPERTIES = "devtools/shared/locales/styleinspector.properties";
const {LocalizationHelper} = require("devtools/shared/l10n");
const STYLE_INSPECTOR_L10N = new LocalizationHelper(STYLE_INSPECTOR_PROPERTIES);

/**
 * RuleEditor is responsible for the following:
 *   Owns a Rule object and creates a list of TextPropertyEditors
 *     for its TextProperties.
 *   Manages creation of new text properties.
 *
 * @param {CssRuleView} ruleView
 *        The CssRuleView containg the document holding this rule editor.
 * @param {Rule} rule
 *        The Rule object we're editing.
 */
function RuleEditor(ruleView, rule) {
  EventEmitter.decorate(this);

  this.ruleView = ruleView;
  this.doc = this.ruleView.styleDocument;
  this.toolbox = this.ruleView.inspector.toolbox;
  // We stash this locally so that we can refer to it from |destroy|
  // without accidentally reinstantiating the service during shutdown.
  this.sourceMapURLService = this.toolbox.sourceMapURLService;
  this.rule = rule;

  this.isEditable = !rule.isSystem;
  // Flag that blocks updates of the selector and properties when it is
  // being edited
  this.isEditing = false;

  this._onNewProperty = this._onNewProperty.bind(this);
  this._newPropertyDestroy = this._newPropertyDestroy.bind(this);
  this._onSelectorDone = this._onSelectorDone.bind(this);
  this._locationChanged = this._locationChanged.bind(this);
  this.updateSourceLink = this.updateSourceLink.bind(this);
  this._onToolChanged = this._onToolChanged.bind(this);
  this._updateLocation = this._updateLocation.bind(this);
  this._onSourceClick = this._onSourceClick.bind(this);

  this.rule.domRule.on("location-changed", this._locationChanged);
  this.toolbox.on("tool-registered", this._onToolChanged);
  this.toolbox.on("tool-unregistered", this._onToolChanged);

  this._create();
}

RuleEditor.prototype = {
  destroy: function () {
    this.rule.domRule.off("location-changed");
    this.toolbox.off("tool-registered", this._onToolChanged);
    this.toolbox.off("tool-unregistered", this._onToolChanged);

    let url = null;
    if (this.rule.sheet) {
      url = this.rule.sheet.href || this.rule.sheet.nodeHref;
    }
    if (url && !this.rule.isSystem && this.rule.domRule.type !== ELEMENT_STYLE) {
      // Only get the original source link if the rule isn't a system
      // rule and if it isn't an inline rule.
      let sourceLine = this.rule.ruleLine;
      let sourceColumn = this.rule.ruleColumn;
      this.sourceMapURLService.unsubscribe(url, sourceLine, sourceColumn,
                                           this._updateLocation);
    }
  },

  get isSelectorEditable() {
    let trait = this.isEditable &&
      this.ruleView.inspector.target.client.traits.selectorEditable &&
      this.rule.domRule.type !== ELEMENT_STYLE &&
      this.rule.domRule.type !== CSSRule.KEYFRAME_RULE;

    // Do not allow editing anonymousselectors until we can
    // detect mutations on  pseudo elements in Bug 1034110.
    return trait && !this.rule.elementStyle.element.isAnonymous;
  },

  _create: function () {
    this.element = this.doc.createElement("div");
    this.element.className = "ruleview-rule devtools-monospace";
    this.element.setAttribute("uneditable", !this.isEditable);
    this.element.setAttribute("unmatched", this.rule.isUnmatched);
    this.element._ruleEditor = this;

    // Give a relative position for the inplace editor's measurement
    // span to be placed absolutely against.
    this.element.style.position = "relative";

    // Add the source link.
    this.source = createChild(this.element, "div", {
      class: "ruleview-rule-source theme-link"
    });
    this.source.addEventListener("click", this._onSourceClick);

    let sourceLabel = this.doc.createElement("span");
    sourceLabel.classList.add("ruleview-rule-source-label");
    this.source.appendChild(sourceLabel);

    this.updateSourceLink();

    let code = createChild(this.element, "div", {
      class: "ruleview-code"
    });

    let header = createChild(code, "div", {});

    this.selectorText = createChild(header, "span", {
      class: "ruleview-selectorcontainer",
      tabindex: this.isSelectorEditable ? "0" : "-1",
    });

    if (this.isSelectorEditable) {
      this.selectorText.addEventListener("click", event => {
        // Clicks within the selector shouldn't propagate any further.
        event.stopPropagation();
      });

      editableField({
        element: this.selectorText,
        done: this._onSelectorDone,
        cssProperties: this.rule.cssProperties,
      });
    }

    if (this.rule.domRule.type !== CSSRule.KEYFRAME_RULE) {
      Task.spawn(function* () {
        let selector;

        if (this.rule.domRule.selectors) {
          // This is a "normal" rule with a selector.
          selector = this.rule.domRule.selectors.join(", ");
        } else if (this.rule.inherited) {
          // This is an inline style from an inherited rule. Need to resolve the unique
          // selector from the node which rule this is inherited from.
          selector = yield this.rule.inherited.getUniqueSelector();
        } else {
          // This is an inline style from the current node.
          selector = this.ruleView.inspector.selectionCssSelector;
        }

        let selectorHighlighter = createChild(header, "span", {
          class: "ruleview-selectorhighlighter" +
                 (this.ruleView.highlighters.selectorHighlighterShown === selector ?
                  " highlighted" : ""),
          title: l10n("rule.selectorHighlighter.tooltip")
        });
        selectorHighlighter.addEventListener("click", () => {
          this.ruleView.toggleSelectorHighlighter(selectorHighlighter, selector);
        });

        this.uniqueSelector = selector;
        this.emit("selector-icon-created");
      }.bind(this));
    }

    this.openBrace = createChild(header, "span", {
      class: "ruleview-ruleopen",
      textContent: " {"
    });

    this.propertyList = createChild(code, "ul", {
      class: "ruleview-propertylist"
    });

    this.populate();

    this.closeBrace = createChild(code, "div", {
      class: "ruleview-ruleclose",
      tabindex: this.isEditable ? "0" : "-1",
      textContent: "}"
    });

    if (this.isEditable) {
      // A newProperty editor should only be created when no editor was
      // previously displayed. Since the editors are cleared on blur,
      // check this.ruleview.isEditing on mousedown
      this._ruleViewIsEditing = false;

      code.addEventListener("mousedown", () => {
        this._ruleViewIsEditing = this.ruleView.isEditing;
      });

      code.addEventListener("click", () => {
        let selection = this.doc.defaultView.getSelection();
        if (selection.isCollapsed && !this._ruleViewIsEditing) {
          this.newProperty();
        }
        // Cleanup the _ruleViewIsEditing flag
        this._ruleViewIsEditing = false;
      });

      this.element.addEventListener("mousedown", () => {
        this.doc.defaultView.focus();
      });

      // Create a property editor when the close brace is clicked.
      editableItem({ element: this.closeBrace }, () => {
        this.newProperty();
      });
    }
  },

  /**
   * Called when a tool is registered or unregistered.
   */
  _onToolChanged: function () {
    // When the source editor is registered, update the source links
    // to be clickable; and if it is unregistered, update the links to
    // be unclickable.  However, some links are never clickable, so
    // filter those out first.
    if (this.source.getAttribute("unselectable") === "permanent") {
      // Nothing.
    } else if (this.toolbox.isToolRegistered("styleeditor")) {
      this.source.removeAttribute("unselectable");
    } else {
      this.source.setAttribute("unselectable", "true");
    }
  },

  /**
   * Event handler called when a property changes on the
   * StyleRuleActor.
   */
  _locationChanged: function () {
    this.updateSourceLink();
  },

  _onSourceClick: function () {
    if (this.source.hasAttribute("unselectable") || !this._currentLocation) {
      return;
    }

    let target = this.ruleView.inspector.target;
    if (Tools.styleEditor.isTargetSupported(target)) {
      gDevTools.showToolbox(target, "styleeditor").then(toolbox => {
        let {url, line, column} = this._currentLocation;
        toolbox.getCurrentPanel().selectStyleSheet(url, line, column);
      });
    }
  },

  /**
   * Update the text of the source link to reflect whether we're showing
   * original sources or not.  This is a callback for
   * SourceMapURLService.subscribe, which see.
   *
   * @param {Boolean} enabled
   *        True if the passed-in location should be used; this means
   *        that source mapping is in use and the remaining arguments
   *        are the original location.  False if the already-known
   *        (stored) location should be used.
   * @param {String} url
   *        The original URL
   * @param {Number} line
   *        The original line number
   * @param {number} column
   *        The original column number
   */
  _updateLocation: function (enabled, url, line, column) {
    let displayURL = url;
    if (!enabled) {
      url = null;
      displayURL = null;
      if (this.rule.sheet) {
        url = this.rule.sheet.href || this.rule.sheet.nodeHref;
        displayURL = this.rule.sheet.href;
      }
      line = this.rule.ruleLine;
      column = this.rule.ruleColumn;
    }

    this._currentLocation = {
      url,
      line,
      column
    };

    let sourceTextContent = CssLogic.shortSource({href: displayURL});
    let title = displayURL ? displayURL : sourceTextContent;
    if (line > 0) {
      sourceTextContent += ":" + line;
      title += ":" + line;
    }
    if (this.rule.mediaText) {
      sourceTextContent += " @" + this.rule.mediaText;
      title += " @" + this.rule.mediaText;
    }

    let sourceLabel = this.element.querySelector(".ruleview-rule-source-label");
    sourceLabel.setAttribute("title", title);
    sourceLabel.textContent = sourceTextContent;
  },

  updateSourceLink: function () {
    if (this.rule.isSystem) {
      let sourceLabel = this.element.querySelector(".ruleview-rule-source-label");
      let title = this.rule.title;
      let sourceHref = (this.rule.sheet && this.rule.sheet.href) ?
          this.rule.sheet.href : title;

      let uaLabel = STYLE_INSPECTOR_L10N.getStr("rule.userAgentStyles");
      sourceLabel.textContent = uaLabel + " " + title;

      // Special case about:PreferenceStyleSheet, as it is generated on the
      // fly and the URI is not registered with the about: handler.
      // https://bugzilla.mozilla.org/show_bug.cgi?id=935803#c37
      if (sourceHref === "about:PreferenceStyleSheet") {
        this.source.setAttribute("unselectable", "permanent");
        sourceLabel.textContent = uaLabel;
        sourceLabel.removeAttribute("title");
      }
    } else {
      this._updateLocation(false);
    }

    let url = null;
    if (this.rule.sheet) {
      url = this.rule.sheet.href || this.rule.sheet.nodeHref;
    }
    if (url && !this.rule.isSystem && this.rule.domRule.type !== ELEMENT_STYLE) {
      // Only get the original source link if the rule isn't a system
      // rule and if it isn't an inline rule.
      let sourceLine = this.rule.ruleLine;
      let sourceColumn = this.rule.ruleColumn;
      this.sourceMapURLService.subscribe(url, sourceLine, sourceColumn,
                                         this._updateLocation);
      // Set "unselectable" appropriately.
      this._onToolChanged();
    } else if (this.rule.domRule.type === ELEMENT_STYLE) {
      this.source.setAttribute("unselectable", "permanent");
    } else {
      // Set "unselectable" appropriately.
      this._onToolChanged();
    }

    promise.resolve().then(() => {
      this.emit("source-link-updated");
    });
  },

  /**
   * Update the rule editor with the contents of the rule.
   */
  populate: function () {
    // Clear out existing viewers.
    while (this.selectorText.hasChildNodes()) {
      this.selectorText.removeChild(this.selectorText.lastChild);
    }

    // If selector text comes from a css rule, highlight selectors that
    // actually match.  For custom selector text (such as for the 'element'
    // style, just show the text directly.
    if (this.rule.domRule.type === ELEMENT_STYLE) {
      this.selectorText.textContent = this.rule.selectorText;
    } else if (this.rule.domRule.type === CSSRule.KEYFRAME_RULE) {
      this.selectorText.textContent = this.rule.domRule.keyText;
    } else {
      this.rule.domRule.selectors.forEach((selector, i) => {
        if (i !== 0) {
          createChild(this.selectorText, "span", {
            class: "ruleview-selector-separator",
            textContent: ", "
          });
        }

        let containerClass =
          (this.rule.matchedSelectors.indexOf(selector) > -1) ?
          "ruleview-selector-matched" : "ruleview-selector-unmatched";
        let selectorContainer = createChild(this.selectorText, "span", {
          class: containerClass
        });

        let parsedSelector = parsePseudoClassesAndAttributes(selector);

        for (let selectorText of parsedSelector) {
          let selectorClass = "";

          switch (selectorText.type) {
            case SELECTOR_ATTRIBUTE:
              selectorClass = "ruleview-selector-attribute";
              break;
            case SELECTOR_ELEMENT:
              selectorClass = "ruleview-selector";
              break;
            case SELECTOR_PSEUDO_CLASS:
              selectorClass = [":active", ":focus", ":hover"].some(
                  pseudo => selectorText.value === pseudo) ?
                "ruleview-selector-pseudo-class-lock" :
                "ruleview-selector-pseudo-class";
              break;
            default:
              break;
          }

          createChild(selectorContainer, "span", {
            textContent: selectorText.value,
            class: selectorClass
          });
        }
      });
    }

    for (let prop of this.rule.textProps) {
      if (!prop.editor && !prop.invisible) {
        let editor = new TextPropertyEditor(this, prop);
        this.propertyList.appendChild(editor.element);
      }
    }
  },

  /**
   * Programatically add a new property to the rule.
   *
   * @param {String} name
   *        Property name.
   * @param {String} value
   *        Property value.
   * @param {String} priority
   *        Property priority.
   * @param {Boolean} enabled
   *        True if the property should be enabled.
   * @param {TextProperty} siblingProp
   *        Optional, property next to which the new property will be added.
   * @return {TextProperty}
   *        The new property
   */
  addProperty: function (name, value, priority, enabled, siblingProp) {
    let prop = this.rule.createProperty(name, value, priority, enabled,
      siblingProp);
    let index = this.rule.textProps.indexOf(prop);
    let editor = new TextPropertyEditor(this, prop);

    // Insert this node before the DOM node that is currently at its new index
    // in the property list.  There is currently one less node in the DOM than
    // in the property list, so this causes it to appear after siblingProp.
    // If there is no node at its index, as is the case where this is the last
    // node being inserted, then this behaves as appendChild.
    this.propertyList.insertBefore(editor.element,
      this.propertyList.children[index]);

    return prop;
  },

  /**
   * Programatically add a list of new properties to the rule.  Focus the UI
   * to the proper location after adding (either focus the value on the
   * last property if it is empty, or create a new property and focus it).
   *
   * @param {Array} properties
   *        Array of properties, which are objects with this signature:
   *        {
   *          name: {string},
   *          value: {string},
   *          priority: {string}
   *        }
   * @param {TextProperty} siblingProp
   *        Optional, the property next to which all new props should be added.
   */
  addProperties: function (properties, siblingProp) {
    if (!properties || !properties.length) {
      return;
    }

    let lastProp = siblingProp;
    for (let p of properties) {
      let isCommented = Boolean(p.commentOffsets);
      let enabled = !isCommented;
      lastProp = this.addProperty(p.name, p.value, p.priority, enabled,
        lastProp);
    }

    // Either focus on the last value if incomplete, or start a new one.
    if (lastProp && lastProp.value.trim() === "") {
      lastProp.editor.valueSpan.click();
    } else {
      this.newProperty();
    }
  },

  /**
   * Create a text input for a property name.  If a non-empty property
   * name is given, we'll create a real TextProperty and add it to the
   * rule.
   */
  newProperty: function () {
    // If we're already creating a new property, ignore this.
    if (!this.closeBrace.hasAttribute("tabindex")) {
      return;
    }

    // While we're editing a new property, it doesn't make sense to
    // start a second new property editor, so disable focusing the
    // close brace for now.
    this.closeBrace.removeAttribute("tabindex");

    this.newPropItem = createChild(this.propertyList, "li", {
      class: "ruleview-property ruleview-newproperty",
    });

    this.newPropSpan = createChild(this.newPropItem, "span", {
      class: "ruleview-propertyname",
      tabindex: "0"
    });

    this.multipleAddedProperties = null;

    this.editor = new InplaceEditor({
      element: this.newPropSpan,
      done: this._onNewProperty,
      destroy: this._newPropertyDestroy,
      advanceChars: ":",
      contentType: InplaceEditor.CONTENT_TYPES.CSS_PROPERTY,
      popup: this.ruleView.popup,
      cssProperties: this.rule.cssProperties,
    });

    // Auto-close the input if multiple rules get pasted into new property.
    this.editor.input.addEventListener("paste",
      blurOnMultipleProperties(this.rule.cssProperties));
  },

  /**
   * Called when the new property input has been dismissed.
   *
   * @param {String} value
   *        The value in the editor.
   * @param {Boolean} commit
   *        True if the value should be committed.
   */
  _onNewProperty: function (value, commit) {
    if (!value || !commit) {
      return;
    }

    // parseDeclarations allows for name-less declarations, but in the present
    // case, we're creating a new declaration, it doesn't make sense to accept
    // these entries
    this.multipleAddedProperties =
      parseNamedDeclarations(this.rule.cssProperties.isKnown, value, true);

    // Blur the editor field now and deal with adding declarations later when
    // the field gets destroyed (see _newPropertyDestroy)
    this.editor.input.blur();
  },

  /**
   * Called when the new property editor is destroyed.
   * This is where the properties (type TextProperty) are actually being
   * added, since we want to wait until after the inplace editor `destroy`
   * event has been fired to keep consistent UI state.
   */
  _newPropertyDestroy: function () {
    // We're done, make the close brace focusable again.
    this.closeBrace.setAttribute("tabindex", "0");

    this.propertyList.removeChild(this.newPropItem);
    delete this.newPropItem;
    delete this.newPropSpan;

    // If properties were added, we want to focus the proper element.
    // If the last new property has no value, focus the value on it.
    // Otherwise, start a new property and focus that field.
    if (this.multipleAddedProperties && this.multipleAddedProperties.length) {
      this.addProperties(this.multipleAddedProperties);
    }
  },

  /**
   * Called when the selector's inplace editor is closed.
   * Ignores the change if the user pressed escape, otherwise
   * commits it.
   *
   * @param {String} value
   *        The value contained in the editor.
   * @param {Boolean} commit
   *        True if the change should be applied.
   * @param {Number} direction
   *        The move focus direction number.
   */
  _onSelectorDone: Task.async(function* (value, commit, direction) {
    if (!commit || this.isEditing || value === "" ||
        value === this.rule.selectorText) {
      return;
    }

    let ruleView = this.ruleView;
    let elementStyle = ruleView._elementStyle;
    let element = elementStyle.element;
    let supportsUnmatchedRules =
      this.rule.domRule.supportsModifySelectorUnmatched;

    this.isEditing = true;

    try {
      let response = yield this.rule.domRule.modifySelector(element, value);

      if (!supportsUnmatchedRules) {
        this.isEditing = false;

        if (response) {
          this.ruleView.refreshPanel();
        }
        return;
      }

      // We recompute the list of applied styles, because editing a
      // selector might cause this rule's position to change.
      let applied = yield elementStyle.pageStyle.getApplied(element, {
        inherited: true,
        matchedSelectors: true,
        filter: elementStyle.showUserAgentStyles ? "ua" : undefined
      });

      this.isEditing = false;

      let {ruleProps, isMatching} = response;
      if (!ruleProps) {
        // Notify for changes, even when nothing changes,
        // just to allow tests being able to track end of this request.
        ruleView.emit("ruleview-invalid-selector");
        return;
      }

      ruleProps.isUnmatched = !isMatching;
      let newRule = new Rule(elementStyle, ruleProps);
      let editor = new RuleEditor(ruleView, newRule);
      let rules = elementStyle.rules;

      let newRuleIndex = applied.findIndex((r) => r.rule == ruleProps.rule);
      let oldIndex = rules.indexOf(this.rule);

      // If the selector no longer matches, then we leave the rule in
      // the same relative position.
      if (newRuleIndex === -1) {
        newRuleIndex = oldIndex;
      }

      // Remove the old rule and insert the new rule.
      rules.splice(oldIndex, 1);
      rules.splice(newRuleIndex, 0, newRule);
      elementStyle._changed();
      elementStyle.markOverriddenAll();

      // We install the new editor in place of the old -- you might
      // think we would replicate the list-modification logic above,
      // but that is complicated due to the way the UI installs
      // pseudo-element rules and the like.
      this.element.parentNode.replaceChild(editor.element, this.element);

      // Remove highlight for modified selector
      if (ruleView.highlighters.selectorHighlighterShown) {
        ruleView.toggleSelectorHighlighter(ruleView.lastSelectorIcon,
          ruleView.highlighters.selectorHighlighterShown);
      }

      editor._moveSelectorFocus(direction);
    } catch (err) {
      this.isEditing = false;
      promiseWarn(err);
    }
  }),

  /**
   * Handle moving the focus change after a tab or return keypress in the
   * selector inplace editor.
   *
   * @param {Number} direction
   *        The move focus direction number.
   */
  _moveSelectorFocus: function (direction) {
    if (!direction || direction === Services.focus.MOVEFOCUS_BACKWARD) {
      return;
    }

    if (this.rule.textProps.length > 0) {
      this.rule.textProps[0].editor.nameSpan.click();
    } else {
      this.propertyList.click();
    }
  }
};

module.exports = RuleEditor;
PK
!<�'�����Uchrome/devtools/modules/devtools/client/inspector/rules/views/text-property-editor.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {l10n} = require("devtools/shared/inspector/css-logic");
const {getCssProperties} = require("devtools/shared/fronts/css-properties");
const {InplaceEditor, editableField} =
      require("devtools/client/shared/inplace-editor");
const {
  createChild,
  appendText,
  advanceValidate,
  blurOnMultipleProperties
} = require("devtools/client/inspector/shared/utils");
const {
  parseDeclarations,
  parseSingleValue,
} = require("devtools/shared/css/parsing-utils");
const Services = require("Services");

const HTML_NS = "http://www.w3.org/1999/xhtml";

const SHARED_SWATCH_CLASS = "ruleview-swatch";
const COLOR_SWATCH_CLASS = "ruleview-colorswatch";
const BEZIER_SWATCH_CLASS = "ruleview-bezierswatch";
const FILTER_SWATCH_CLASS = "ruleview-filterswatch";
const ANGLE_SWATCH_CLASS = "ruleview-angleswatch";
const INSET_POINT_TYPES = ["top", "right", "bottom", "left"];
const FONT_FAMILY_CLASS = "ruleview-font-family";
const SHAPE_SWATCH_CLASS = "ruleview-shapeswatch";

/*
 * An actionable element is an element which on click triggers a specific action
 * (e.g. shows a color tooltip, opens a link, …).
 */
const ACTIONABLE_ELEMENTS_SELECTORS = [
  `.${COLOR_SWATCH_CLASS}`,
  `.${BEZIER_SWATCH_CLASS}`,
  `.${FILTER_SWATCH_CLASS}`,
  `.${ANGLE_SWATCH_CLASS}`,
  "a"
];

// In order to highlight the used fonts in font-family properties, we
// retrieve the list of used fonts from the server. That always
// returns the actually used font family name(s). If the property's
// authored value is sans-serif for instance, the used font might be
// arial instead.  So we need the list of all generic font family
// names to underline those when we find them.
const GENERIC_FONT_FAMILIES = [
  "serif",
  "sans-serif",
  "cursive",
  "fantasy",
  "monospace",
  "system-ui"
];

/**
 * TextPropertyEditor is responsible for the following:
 *   Owns a TextProperty object.
 *   Manages changes to the TextProperty.
 *   Can be expanded to display computed properties.
 *   Can mark a property disabled or enabled.
 *
 * @param {RuleEditor} ruleEditor
 *        The rule editor that owns this TextPropertyEditor.
 * @param {TextProperty} property
 *        The text property to edit.
 */
function TextPropertyEditor(ruleEditor, property) {
  this.ruleEditor = ruleEditor;
  this.ruleView = this.ruleEditor.ruleView;
  this.doc = this.ruleEditor.doc;
  this.popup = this.ruleView.popup;
  this.prop = property;
  this.prop.editor = this;
  this.browserWindow = this.doc.defaultView.top;
  this._populatedComputed = false;
  this._hasPendingClick = false;
  this._clickedElementOptions = null;

  const toolbox = this.ruleView.inspector.toolbox;
  this.cssProperties = getCssProperties(toolbox);

  this._onEnableClicked = this._onEnableClicked.bind(this);
  this._onExpandClicked = this._onExpandClicked.bind(this);
  this._onStartEditing = this._onStartEditing.bind(this);
  this._onNameDone = this._onNameDone.bind(this);
  this._onValueDone = this._onValueDone.bind(this);
  this._onSwatchCommit = this._onSwatchCommit.bind(this);
  this._onSwatchPreview = this._onSwatchPreview.bind(this);
  this._onSwatchRevert = this._onSwatchRevert.bind(this);
  this._onValidate = this.ruleView.debounce(this._previewValue, 10, this);
  this.update = this.update.bind(this);
  this.updatePropertyState = this.updatePropertyState.bind(this);
  this._onHoverShapePoint = this._onHoverShapePoint.bind(this);

  this._create();
  this.update();
}

TextPropertyEditor.prototype = {
  /**
   * Boolean indicating if the name or value is being currently edited.
   */
  get editing() {
    return !!(this.nameSpan.inplaceEditor || this.valueSpan.inplaceEditor ||
      this.ruleView.tooltips.isEditing) || this.popup.isOpen;
  },

  /**
   * Get the rule to the current text property
   */
  get rule() {
    return this.prop.rule;
  },

  /**
   * Create the property editor's DOM.
   */
  _create: function () {
    this.element = this.doc.createElementNS(HTML_NS, "li");
    this.element.classList.add("ruleview-property");
    this.element._textPropertyEditor = this;

    this.container = createChild(this.element, "div", {
      class: "ruleview-propertycontainer inline-tooltip-container"
    });

    // The enable checkbox will disable or enable the rule.
    this.enable = createChild(this.container, "div", {
      class: "ruleview-enableproperty theme-checkbox",
      tabindex: "-1"
    });

    this.nameContainer = createChild(this.container, "span", {
      class: "ruleview-namecontainer"
    });

    // Property name, editable when focused.  Property name
    // is committed when the editor is unfocused.
    this.nameSpan = createChild(this.nameContainer, "span", {
      class: "ruleview-propertyname theme-fg-color5",
      tabindex: this.ruleEditor.isEditable ? "0" : "-1",
    });

    appendText(this.nameContainer, ": ");

    // Click to expand the computed properties of the text property.
    this.expander = createChild(this.container, "span", {
      class: "ruleview-expander theme-twisty"
    });
    this.expander.addEventListener("click", this._onExpandClicked, true);

    // Create a span that will hold the property and semicolon.
    // Use this span to create a slightly larger click target
    // for the value.
    this.valueContainer = createChild(this.container, "span", {
      class: "ruleview-propertyvaluecontainer"
    });

    // Property value, editable when focused.  Changes to the
    // property value are applied as they are typed, and reverted
    // if the user presses escape.
    this.valueSpan = createChild(this.valueContainer, "span", {
      class: "ruleview-propertyvalue theme-fg-color1",
      tabindex: this.ruleEditor.isEditable ? "0" : "-1",
    });

    // Storing the TextProperty on the elements for easy access
    // (for instance by the tooltip)
    this.valueSpan.textProperty = this.prop;
    this.nameSpan.textProperty = this.prop;

    // If the value is a color property we need to put it through the parser
    // so that colors can be coerced into the default color type. This prevents
    // us from thinking that when colors are coerced they have been changed by
    // the user.
    let outputParser = this.ruleView._outputParser;
    let frag = outputParser.parseCssProperty(this.prop.name, this.prop.value);
    let parsedValue = frag.textContent;

    // Save the initial value as the last committed value,
    // for restoring after pressing escape.
    this.committed = { name: this.prop.name,
                       value: parsedValue,
                       priority: this.prop.priority };

    appendText(this.valueContainer, ";");

    this.warning = createChild(this.container, "div", {
      class: "ruleview-warning",
      hidden: "",
      title: l10n("rule.warning.title"),
    });

    // Filter button that filters for the current property name and is
    // displayed when the property is overridden by another rule.
    this.filterProperty = createChild(this.container, "div", {
      class: "ruleview-overridden-rule-filter",
      hidden: "",
      title: l10n("rule.filterProperty.title"),
    });

    this.filterProperty.addEventListener("click", event => {
      this.ruleEditor.ruleView.setFilterStyles("`" + this.prop.name + "`");
      event.stopPropagation();
    });

    // Holds the viewers for the computed properties.
    // will be populated in |_updateComputed|.
    this.computed = createChild(this.element, "ul", {
      class: "ruleview-computedlist",
    });

    // Holds the viewers for the overridden shorthand properties.
    // will be populated in |_updateShorthandOverridden|.
    this.shorthandOverridden = createChild(this.element, "ul", {
      class: "ruleview-overridden-items",
    });

    // Only bind event handlers if the rule is editable.
    if (this.ruleEditor.isEditable) {
      this.enable.addEventListener("click", this._onEnableClicked, true);

      this.nameContainer.addEventListener("click", (event) => {
        // Clicks within the name shouldn't propagate any further.
        event.stopPropagation();

        // Forward clicks on nameContainer to the editable nameSpan
        if (event.target === this.nameContainer) {
          this.nameSpan.click();
        }
      });

      editableField({
        start: this._onStartEditing,
        element: this.nameSpan,
        done: this._onNameDone,
        destroy: this.updatePropertyState,
        advanceChars: ":",
        contentType: InplaceEditor.CONTENT_TYPES.CSS_PROPERTY,
        popup: this.popup,
        cssProperties: this.cssProperties,
      });

      // Auto blur name field on multiple CSS rules get pasted in.
      this.nameContainer.addEventListener("paste",
        blurOnMultipleProperties(this.cssProperties));

      this.valueContainer.addEventListener("click", (event) => {
        // Clicks within the value shouldn't propagate any further.
        event.stopPropagation();

        // Forward clicks on valueContainer to the editable valueSpan
        if (event.target === this.valueContainer) {
          this.valueSpan.click();
        }
      });

      // The mousedown event could trigger a blur event on nameContainer, which
      // will trigger a call to the update function. The update function clears
      // valueSpan's markup. Thus the regular click event does not bubble up, and
      // listener's callbacks are not called.
      // So we need to remember where the user clicks in order to re-trigger the click
      // after the valueSpan's markup is re-populated. We only need to track this for
      // valueSpan's child elements, because direct click on valueSpan will always
      // trigger a click event.
      this.valueSpan.addEventListener("mousedown", (event) => {
        let clickedEl = event.target;
        if (clickedEl === this.valueSpan) {
          return;
        }
        this._hasPendingClick = true;

        let matchedSelector = ACTIONABLE_ELEMENTS_SELECTORS.find(
          (selector) => clickedEl.matches(selector));
        if (matchedSelector) {
          let similarElements = [...this.valueSpan.querySelectorAll(matchedSelector)];
          this._clickedElementOptions = {
            selector: matchedSelector,
            index: similarElements.indexOf(clickedEl)
          };
        }
      });

      this.valueSpan.addEventListener("mouseup", (event) => {
        this._clickedElementOptions = null;
        this._hasPendingClick = false;
      });

      this.valueSpan.addEventListener("click", (event) => {
        let target = event.target;

        if (target.nodeName === "a") {
          event.stopPropagation();
          event.preventDefault();
          let browserWin = this.ruleView.inspector.target.tab.ownerDocument.defaultView;
          browserWin.openUILinkIn(target.href, "tab");
        }
      });

      editableField({
        start: this._onStartEditing,
        element: this.valueSpan,
        done: this._onValueDone,
        destroy: this.update,
        validate: this._onValidate,
        advanceChars: advanceValidate,
        contentType: InplaceEditor.CONTENT_TYPES.CSS_VALUE,
        property: this.prop,
        defaultIncrement: this.prop.name === "opacity" ? 0.1 : 1,
        popup: this.popup,
        multiline: true,
        maxWidth: () => this.container.getBoundingClientRect().width,
        cssProperties: this.cssProperties,
        cssVariables: this.rule.elementStyle.variables,
      });

      this.ruleView.highlighters.on("hover-shape-point", this._onHoverShapePoint);
    }
  },

  /**
   * Get the path from which to resolve requests for this
   * rule's stylesheet.
   *
   * @return {String} the stylesheet's href.
   */
  get sheetHref() {
    let domRule = this.rule.domRule;
    if (domRule) {
      return domRule.href || domRule.nodeHref;
    }
    return undefined;
  },

  /**
   * Populate the span based on changes to the TextProperty.
   */
  update: function () {
    if (this.ruleView.isDestroyed) {
      return;
    }

    this.updatePropertyState();

    let name = this.prop.name;
    this.nameSpan.textContent = name;

    // Combine the property's value and priority into one string for
    // the value.
    let store = this.rule.elementStyle.store;
    let val = store.userProperties.getProperty(this.rule.style, name,
                                               this.prop.value);
    if (this.prop.priority) {
      val += " !" + this.prop.priority;
    }

    let propDirty = store.userProperties.contains(this.rule.style, name);

    if (propDirty) {
      this.element.setAttribute("dirty", "");
    } else {
      this.element.removeAttribute("dirty");
    }

    let outputParser = this.ruleView._outputParser;
    let parserOptions = {
      angleClass: "ruleview-angle",
      angleSwatchClass: SHARED_SWATCH_CLASS + " " + ANGLE_SWATCH_CLASS,
      bezierClass: "ruleview-bezier",
      bezierSwatchClass: SHARED_SWATCH_CLASS + " " + BEZIER_SWATCH_CLASS,
      colorClass: "ruleview-color",
      colorSwatchClass: SHARED_SWATCH_CLASS + " " + COLOR_SWATCH_CLASS,
      filterClass: "ruleview-filter",
      filterSwatchClass: SHARED_SWATCH_CLASS + " " + FILTER_SWATCH_CLASS,
      flexClass: "ruleview-flex",
      gridClass: "ruleview-grid",
      shapeClass: "ruleview-shape",
      shapeSwatchClass: SHARED_SWATCH_CLASS + " " + SHAPE_SWATCH_CLASS,
      defaultColorType: !propDirty,
      urlClass: "theme-link",
      fontFamilyClass: FONT_FAMILY_CLASS,
      baseURI: this.sheetHref,
      unmatchedVariableClass: "ruleview-unmatched-variable",
      matchedVariableClass: "ruleview-variable",
      isVariableInUse: varName => this.rule.elementStyle.getVariable(varName),
    };
    let frag = outputParser.parseCssProperty(name, val, parserOptions);
    this.valueSpan.innerHTML = "";
    this.valueSpan.appendChild(frag);

    this.ruleView.emit("property-value-updated", this.valueSpan);

    // Highlight the currently used font in font-family properties.
    // If we cannot find a match, highlight the first generic family instead.
    let fontFamilySpans = this.valueSpan.querySelectorAll("." + FONT_FAMILY_CLASS);
    if (fontFamilySpans.length && this.prop.enabled && !this.prop.overridden) {
      this.rule.elementStyle.getUsedFontFamilies().then(families => {
        const usedFontFamilies = families.map(font => font.toLowerCase());
        let foundMatchingFamily = false;
        let firstGenericSpan = null;

        for (let span of fontFamilySpans) {
          const authoredFont = span.textContent.toLowerCase();

          if (!firstGenericSpan && GENERIC_FONT_FAMILIES.includes(authoredFont)) {
            firstGenericSpan = span;
          }

          if (usedFontFamilies.includes(authoredFont)) {
            span.classList.add("used-font");
            foundMatchingFamily = true;
          }
        }

        if (!foundMatchingFamily && firstGenericSpan) {
          firstGenericSpan.classList.add("used-font");
        }

        this.ruleView.emit("font-highlighted", this.valueSpan);
      }).catch(e => console.error("Could not get the list of font families", e));
    }

    // Attach the color picker tooltip to the color swatches
    this._colorSwatchSpans =
      this.valueSpan.querySelectorAll("." + COLOR_SWATCH_CLASS);
    if (this.ruleEditor.isEditable) {
      for (let span of this._colorSwatchSpans) {
        // Adding this swatch to the list of swatches our colorpicker
        // knows about
        this.ruleView.tooltips.getTooltip("colorPicker").addSwatch(span, {
          onShow: this._onStartEditing,
          onPreview: this._onSwatchPreview,
          onCommit: this._onSwatchCommit,
          onRevert: this._onSwatchRevert
        });
        span.on("unit-change", this._onSwatchCommit);
        let title = l10n("rule.colorSwatch.tooltip");
        span.setAttribute("title", title);
        span.dataset.propertyName = this.nameSpan.textContent;
      }
    }

    // Attach the cubic-bezier tooltip to the bezier swatches
    this._bezierSwatchSpans =
      this.valueSpan.querySelectorAll("." + BEZIER_SWATCH_CLASS);
    if (this.ruleEditor.isEditable) {
      for (let span of this._bezierSwatchSpans) {
        // Adding this swatch to the list of swatches our colorpicker
        // knows about
        this.ruleView.tooltips.getTooltip("cubicBezier").addSwatch(span, {
          onShow: this._onStartEditing,
          onPreview: this._onSwatchPreview,
          onCommit: this._onSwatchCommit,
          onRevert: this._onSwatchRevert
        });
        let title = l10n("rule.bezierSwatch.tooltip");
        span.setAttribute("title", title);
      }
    }

    // Attach the filter editor tooltip to the filter swatch
    let span = this.valueSpan.querySelector("." + FILTER_SWATCH_CLASS);
    if (this.ruleEditor.isEditable) {
      if (span) {
        parserOptions.filterSwatch = true;

        this.ruleView.tooltips.getTooltip("filterEditor").addSwatch(span, {
          onShow: this._onStartEditing,
          onPreview: this._onSwatchPreview,
          onCommit: this._onSwatchCommit,
          onRevert: this._onSwatchRevert
        }, outputParser, parserOptions);
        let title = l10n("rule.filterSwatch.tooltip");
        span.setAttribute("title", title);
      }
    }

    this.angleSwatchSpans =
      this.valueSpan.querySelectorAll("." + ANGLE_SWATCH_CLASS);
    if (this.ruleEditor.isEditable) {
      for (let angleSpan of this.angleSwatchSpans) {
        angleSpan.on("unit-change", this._onSwatchCommit);
        let title = l10n("rule.angleSwatch.tooltip");
        angleSpan.setAttribute("title", title);
      }
    }

    let flexToggle = this.valueSpan.querySelector(".ruleview-flex");
    if (flexToggle) {
      flexToggle.setAttribute("title", l10n("rule.flexToggle.tooltip"));
      if (this.ruleView.highlighters.flexboxHighlighterShown ===
          this.ruleView.inspector.selection.nodeFront) {
        flexToggle.classList.add("active");
      }
    }

    let gridToggle = this.valueSpan.querySelector(".ruleview-grid");
    if (gridToggle) {
      gridToggle.setAttribute("title", l10n("rule.gridToggle.tooltip"));
      if (this.ruleView.highlighters.gridHighlighterShown ===
          this.ruleView.inspector.selection.nodeFront) {
        gridToggle.classList.add("active");
      }
    }

    let shapeToggle = this.valueSpan.querySelector(".ruleview-shapeswatch");
    if (shapeToggle) {
      let mode = "css" + name.split("-").map(s => {
        return s[0].toUpperCase() + s.slice(1);
      }).join("");
      shapeToggle.setAttribute("data-mode", mode);

      let { highlighters, inspector } = this.ruleView;
      if (highlighters.shapesHighlighterShown === inspector.selection.nodeFront &&
          highlighters.state.shapes.options.mode === mode) {
        shapeToggle.classList.add("active");
        highlighters.highlightRuleViewShapePoint(highlighters.state.shapes.hoverPoint);
      }
    }

    // Now that we have updated the property's value, we might have a pending
    // click on the value container. If we do, we have to trigger a click event
    // on the right element.
    if (this._hasPendingClick) {
      this._hasPendingClick = false;
      let elToClick;

      if (this._clickedElementOptions !== null) {
        let {selector, index} = this._clickedElementOptions;
        elToClick = this.valueSpan.querySelectorAll(selector)[index];

        this._clickedElementOptions = null;
      }

      if (!elToClick) {
        elToClick = this.valueSpan;
      }
      elToClick.click();
    }

    // Populate the computed styles and shorthand overridden styles.
    this._updateComputed();
    this._updateShorthandOverridden();

    // Update the rule property highlight.
    this.ruleView._updatePropertyHighlight(this);
  },

  _onStartEditing: function () {
    this.element.classList.remove("ruleview-overridden");
    this.filterProperty.hidden = true;
    this.enable.style.visibility = "hidden";
    this.expander.style.display = "none";
  },

  /**
   * Update the visibility of the enable checkbox, the warning indicator and
   * the filter property, as well as the overridden state of the property.
   */
  updatePropertyState: function () {
    if (this.prop.enabled) {
      this.enable.style.removeProperty("visibility");
      this.enable.setAttribute("checked", "");
    } else {
      this.enable.style.visibility = "visible";
      this.enable.removeAttribute("checked");
    }

    this.warning.hidden = this.editing || this.isValid();
    this.filterProperty.hidden = this.editing ||
                                 !this.isValid() ||
                                 !this.prop.overridden ||
                                 this.ruleEditor.rule.isUnmatched;

    let showExpander = this.prop.computed.some(c => c.name !== this.prop.name);
    this.expander.style.display = showExpander ? "inline-block" : "none";

    if (!this.editing &&
        (this.prop.overridden || !this.prop.enabled ||
         !this.prop.isKnownProperty())) {
      this.element.classList.add("ruleview-overridden");
    } else {
      this.element.classList.remove("ruleview-overridden");
    }
  },

  /**
   * Update the indicator for computed styles. The computed styles themselves
   * are populated on demand, when they become visible.
   */
  _updateComputed: function () {
    this.computed.innerHTML = "";

    let showExpander = this.prop.computed.some(c => c.name !== this.prop.name);
    this.expander.style.display = !this.editing && showExpander ? "inline-block" : "none";

    this._populatedComputed = false;
    if (this.expander.hasAttribute("open")) {
      this._populateComputed();
    }
  },

  /**
   * Populate the list of computed styles.
   */
  _populateComputed: function () {
    if (this._populatedComputed) {
      return;
    }
    this._populatedComputed = true;

    for (let computed of this.prop.computed) {
      // Don't bother to duplicate information already
      // shown in the text property.
      if (computed.name === this.prop.name) {
        continue;
      }

      // Store the computed style element for easy access when highlighting
      // styles
      computed.element = this._createComputedListItem(this.computed, computed,
        "ruleview-computed");
    }
  },

  /**
   * Update the indicator for overridden shorthand styles. The shorthand
   * overridden styles themselves are populated on demand, when they
   * become visible.
   */
  _updateShorthandOverridden: function () {
    this.shorthandOverridden.innerHTML = "";

    this._populatedShorthandOverridden = false;
    this._populateShorthandOverridden();
  },

  /**
   * Populate the list of overridden shorthand styles.
   */
  _populateShorthandOverridden: function () {
    if (this._populatedShorthandOverridden || this.prop.overridden) {
      return;
    }
    this._populatedShorthandOverridden = true;

    for (let computed of this.prop.computed) {
      // Don't display duplicate information or show properties
      // that are completely overridden.
      if (computed.name === this.prop.name || !computed.overridden) {
        continue;
      }

      this._createComputedListItem(this.shorthandOverridden, computed,
        "ruleview-overridden-item");
    }
  },

  /**
   * Creates and populates a list item with the computed CSS property.
   */
  _createComputedListItem: function (parentEl, computed, className) {
    let li = createChild(parentEl, "li", {
      class: className
    });

    if (computed.overridden) {
      li.classList.add("ruleview-overridden");
    }

    createChild(li, "span", {
      class: "ruleview-propertyname theme-fg-color5",
      textContent: computed.name
    });
    appendText(li, ": ");

    let outputParser = this.ruleView._outputParser;
    let frag = outputParser.parseCssProperty(
      computed.name, computed.value, {
        colorSwatchClass: "ruleview-swatch ruleview-colorswatch",
        urlClass: "theme-link",
        baseURI: this.sheetHref,
        fontFamilyClass: "ruleview-font-family"
      }
    );

    // Store the computed property value that was parsed for output
    computed.parsedValue = frag.textContent;

    createChild(li, "span", {
      class: "ruleview-propertyvalue theme-fg-color1",
      child: frag
    });

    appendText(li, ";");

    return li;
  },

  /**
   * Handles clicks on the disabled property.
   */
  _onEnableClicked: function (event) {
    let checked = this.enable.hasAttribute("checked");
    if (checked) {
      this.enable.removeAttribute("checked");
    } else {
      this.enable.setAttribute("checked", "");
    }
    this.prop.setEnabled(!checked);
    event.stopPropagation();
  },

  /**
   * Handles clicks on the computed property expander. If the computed list is
   * open due to user expanding or style filtering, collapse the computed list
   * and close the expander. Otherwise, add user-open attribute which is used to
   * expand the computed list and tracks whether or not the computed list is
   * expanded by manually by the user.
   */
  _onExpandClicked: function (event) {
    if (this.computed.hasAttribute("filter-open") ||
        this.computed.hasAttribute("user-open")) {
      this.expander.removeAttribute("open");
      this.computed.removeAttribute("filter-open");
      this.computed.removeAttribute("user-open");
      this.shorthandOverridden.removeAttribute("hidden");
      this._populateShorthandOverridden();
    } else {
      this.expander.setAttribute("open", "true");
      this.computed.setAttribute("user-open", "");
      this.shorthandOverridden.setAttribute("hidden", "true");
      this._populateComputed();
    }

    event.stopPropagation();
  },

  /**
   * Expands the computed list when a computed property is matched by the style
   * filtering. The filter-open attribute is used to track whether or not the
   * computed list was toggled opened by the filter.
   */
  expandForFilter: function () {
    if (!this.computed.hasAttribute("user-open")) {
      this.expander.setAttribute("open", "true");
      this.computed.setAttribute("filter-open", "");
      this._populateComputed();
    }
  },

  /**
   * Collapses the computed list that was expanded by style filtering.
   */
  collapseForFilter: function () {
    this.computed.removeAttribute("filter-open");

    if (!this.computed.hasAttribute("user-open")) {
      this.expander.removeAttribute("open");
    }
  },

  /**
   * Called when the property name's inplace editor is closed.
   * Ignores the change if the user pressed escape, otherwise
   * commits it.
   *
   * @param {String} value
   *        The value contained in the editor.
   * @param {Boolean} commit
   *        True if the change should be applied.
   * @param {Number} direction
   *        The move focus direction number.
   */
  _onNameDone: function (value, commit, direction) {
    let isNameUnchanged = (!commit && !this.ruleEditor.isEditing) ||
                          this.committed.name === value;
    if (this.prop.value && isNameUnchanged) {
      return;
    }

    // Remove a property if the name is empty
    if (!value.trim()) {
      this.remove(direction);
      return;
    }

    // Remove a property if the property value is empty and the property
    // value is not about to be focused
    if (!this.prop.value &&
        direction !== Services.focus.MOVEFOCUS_FORWARD) {
      this.remove(direction);
      return;
    }

    // Adding multiple rules inside of name field overwrites the current
    // property with the first, then adds any more onto the property list.
    let properties = parseDeclarations(this.cssProperties.isKnown, value);

    if (properties.length) {
      this.prop.setName(properties[0].name);
      this.committed.name = this.prop.name;

      if (!this.prop.enabled) {
        this.prop.setEnabled(true);
      }

      if (properties.length > 1) {
        this.prop.setValue(properties[0].value, properties[0].priority);
        this.ruleEditor.addProperties(properties.slice(1), this.prop);
      }
    }
  },

  /**
   * Remove property from style and the editors from DOM.
   * Begin editing next or previous available property given the focus
   * direction.
   *
   * @param {Number} direction
   *        The move focus direction number.
   */
  remove: function (direction) {
    if (this._colorSwatchSpans && this._colorSwatchSpans.length) {
      for (let span of this._colorSwatchSpans) {
        this.ruleView.tooltips.getTooltip("colorPicker").removeSwatch(span);
        span.off("unit-change", this._onSwatchCommit);
      }
    }

    if (this.angleSwatchSpans && this.angleSwatchSpans.length) {
      for (let span of this.angleSwatchSpans) {
        span.off("unit-change", this._onSwatchCommit);
      }
    }

    this.element.remove();
    this.ruleEditor.rule.editClosestTextProperty(this.prop, direction);
    this.nameSpan.textProperty = null;
    this.valueSpan.textProperty = null;
    this.prop.remove();
  },

  /**
   * Called when a value editor closes.  If the user pressed escape,
   * revert to the value this property had before editing.
   *
   * @param {String} value
   *        The value contained in the editor.
   * @param {Boolean} commit
   *        True if the change should be applied.
   * @param {Number} direction
   *        The move focus direction number.
   */
  _onValueDone: function (value = "", commit, direction) {
    let parsedProperties = this._getValueAndExtraProperties(value);
    let val = parseSingleValue(this.cssProperties.isKnown,
                               parsedProperties.firstValue);
    let isValueUnchanged = (!commit && !this.ruleEditor.isEditing) ||
                           !parsedProperties.propertiesToAdd.length &&
                           this.committed.value === val.value &&
                           this.committed.priority === val.priority;

    // If the value is not empty and unchanged, revert the property back to
    // its original value and enabled or disabled state
    if (value.trim() && isValueUnchanged) {
      this.ruleEditor.rule.previewPropertyValue(this.prop, val.value,
                                                val.priority);
      this.rule.setPropertyEnabled(this.prop, this.prop.enabled);
      return;
    }

    // Since the value was changed, check if the original propertywas a flex or grid
    // display declaration and hide their respective highlighters.
    if (this.isDisplayFlex()) {
      this.ruleView.highlighters.hideFlexboxHighlighter();
    }

    if (this.isDisplayGrid()) {
      this.ruleView.highlighters.hideGridHighlighter();
    }

    // First, set this property value (common case, only modified a property)
    this.prop.setValue(val.value, val.priority);

    if (!this.prop.enabled) {
      this.prop.setEnabled(true);
    }

    this.committed.value = this.prop.value;
    this.committed.priority = this.prop.priority;

    // If needed, add any new properties after this.prop.
    this.ruleEditor.addProperties(parsedProperties.propertiesToAdd, this.prop);

    // If the input value is empty and the focus is moving forward to the next
    // editable field, then remove the whole property.
    // A timeout is used here to accurately check the state, since the inplace
    // editor `done` and `destroy` events fire before the next editor
    // is focused.
    if (!value.trim() && direction !== Services.focus.MOVEFOCUS_BACKWARD) {
      setTimeout(() => {
        if (!this.editing) {
          this.remove(direction);
        }
      }, 0);
    }
  },

  /**
   * Called when the swatch editor wants to commit a value change.
   */
  _onSwatchCommit: function () {
    this._onValueDone(this.valueSpan.textContent, true);
    this.update();
  },

  /**
   * Called when the swatch editor wants to preview a value change.
   */
  _onSwatchPreview: function () {
    this._previewValue(this.valueSpan.textContent);
  },

  /**
   * Called when the swatch editor closes from an ESC. Revert to the original
   * value of this property before editing.
   */
  _onSwatchRevert: function () {
    this._previewValue(this.prop.value, true);
    this.update();
  },

  /**
   * Parse a value string and break it into pieces, starting with the
   * first value, and into an array of additional properties (if any).
   *
   * Example: Calling with "red; width: 100px" would return
   * { firstValue: "red", propertiesToAdd: [{ name: "width", value: "100px" }] }
   *
   * @param {String} value
   *        The string to parse
   * @return {Object} An object with the following properties:
   *        firstValue: A string containing a simple value, like
   *                    "red" or "100px!important"
   *        propertiesToAdd: An array with additional properties, following the
   *                         parseDeclarations format of {name,value,priority}
   */
  _getValueAndExtraProperties: function (value) {
    // The inplace editor will prevent manual typing of multiple properties,
    // but we need to deal with the case during a paste event.
    // Adding multiple properties inside of value editor sets value with the
    // first, then adds any more onto the property list (below this property).
    let firstValue = value;
    let propertiesToAdd = [];

    let properties = parseDeclarations(this.cssProperties.isKnown, value);

    // Check to see if the input string can be parsed as multiple properties
    if (properties.length) {
      // Get the first property value (if any), and any remaining
      // properties (if any)
      if (!properties[0].name && properties[0].value) {
        firstValue = properties[0].value;
        propertiesToAdd = properties.slice(1);
      } else if (properties[0].name && properties[0].value) {
        // In some cases, the value could be a property:value pair
        // itself.  Join them as one value string and append
        // potentially following properties
        firstValue = properties[0].name + ": " + properties[0].value;
        propertiesToAdd = properties.slice(1);
      }
    }

    return {
      propertiesToAdd: propertiesToAdd,
      firstValue: firstValue
    };
  },

  /**
   * Live preview this property, without committing changes.
   *
   * @param {String} value
   *        The value to set the current property to.
   * @param {Boolean} reverting
   *        True if we're reverting the previously previewed value
   */
  _previewValue: function (value, reverting = false) {
    // Since function call is debounced, we need to make sure we are still
    // editing, and any selector modifications have been completed
    if (!reverting && (!this.editing || this.ruleEditor.isEditing)) {
      return;
    }

    let val = parseSingleValue(this.cssProperties.isKnown, value);
    this.ruleEditor.rule.previewPropertyValue(this.prop, val.value,
                                              val.priority);
  },

  /**
   * Validate this property. Does it make sense for this value to be assigned
   * to this property name? This does not apply the property value
   *
   * @return {Boolean} true if the property value is valid, false otherwise.
   */
  isValid: function () {
    return this.prop.isValid();
  },

  /**
   * Returns true if the property is a `display: [inline-]flex` declaration.
   *
   * @return {Boolean} true if the property is a `display: [inline-]flex` declaration.
   */
  isDisplayFlex: function () {
    return this.prop.name === "display" &&
      (this.prop.value === "flex" || this.prop.value === "inline-flex");
  },

  /**
   * Returns true if the property is a `display: [inline-]grid` declaration.
   *
   * @return {Boolean} true if the property is a `display: [inline-]grid` declaration.
   */
  isDisplayGrid: function () {
    return this.prop.name === "display" &&
      (this.prop.value === "grid" || this.prop.value === "inline-grid");
  },

  /**
   * Highlight the given shape point in the rule view. Called when "hover-shape-point"
   * event is emitted.
   *
   * @param {Event} event
   *        The "hover-shape-point" event.
   * @param {String} point
   *        The point to highlight.
   */
  _onHoverShapePoint: function (event, point) {
    // If there is no shape toggle, or it is not active, return.
    let shapeToggle = this.valueSpan.querySelector(".ruleview-shapeswatch.active");
    if (!shapeToggle) {
      return;
    }

    let view = this.ruleView;
    let { highlighters } = view;
    let ruleViewEl = view.element;
    let selector = `.ruleview-shape-point.active`;
    for (let pointNode of ruleViewEl.querySelectorAll(selector)) {
      this._toggleShapePointActive(pointNode, false);
    }

    if (typeof point === "string") {
      if (point.includes(",")) {
        point = point.split(",")[0];
      }
      // Because one inset value can represent multiple points, inset points use classes
      // instead of data.
      selector = (INSET_POINT_TYPES.includes(point)) ?
                 `.ruleview-shape-point.${point}` :
                 `.ruleview-shape-point[data-point='${point}']`;
      for (let pointNode of this.valueSpan.querySelectorAll(selector)) {
        let nodeInfo = view.getNodeInfo(pointNode);
        if (highlighters.isRuleViewShapePoint(nodeInfo)) {
          this._toggleShapePointActive(pointNode, true);
        }
      }
    }
  },

  /**
   * Toggle the class "active" on the given shape point in the rule view if the current
   * inspector selection is highlighted by the shapes highlighter.
   *
   * @param {NodeFront} node
   *        The NodeFront of the shape point to toggle
   * @param {Boolean} active
   *        Whether the shape point should be active
   */
  _toggleShapePointActive: function (node, active) {
    let { highlighters } = this.ruleView;
    if (highlighters.inspector.selection.nodeFront !=
        highlighters.shapesHighlighterShown) {
      return;
    }

    node.classList.toggle("active", active);
  },
};

module.exports = TextPropertyEditor;
PK
!<��Y�(�(Lchrome/devtools/modules/devtools/client/inspector/shared/dom-node-preview.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {Task} = require("devtools/shared/task");
const EventEmitter = require("devtools/shared/old-event-emitter");
const {createNode} = require("devtools/client/animationinspector/utils");
const { LocalizationHelper } = require("devtools/shared/l10n");

const STRINGS_URI = "devtools/client/locales/inspector.properties";
const L10N = new LocalizationHelper(STRINGS_URI);

/**
 * UI component responsible for displaying a preview of a dom node.
 * @param {InspectorPanel} inspector Requires a reference to the inspector-panel
 * to highlight and select the node, as well as refresh it when there are
 * mutations.
 * @param {Object} options Supported properties are:
 * - compact {Boolean} Defaults to false.
 *   By default, nodes are previewed like <tag id="id" class="class">
 *   If true, nodes will be previewed like tag#id.class instead.
 */
function DomNodePreview(inspector, options = {}) {
  this.inspector = inspector;
  this.options = options;

  this.onPreviewMouseOver = this.onPreviewMouseOver.bind(this);
  this.onPreviewMouseOut = this.onPreviewMouseOut.bind(this);
  this.onSelectElClick = this.onSelectElClick.bind(this);
  this.onMarkupMutations = this.onMarkupMutations.bind(this);
  this.onHighlightElClick = this.onHighlightElClick.bind(this);
  this.onHighlighterLocked = this.onHighlighterLocked.bind(this);

  EventEmitter.decorate(this);
}

exports.DomNodePreview = DomNodePreview;

DomNodePreview.prototype = {
  init: function (containerEl) {
    let document = containerEl.ownerDocument;

    // Init the markup for displaying the target node.
    this.el = createNode({
      parent: containerEl,
      attributes: {
        "class": "animation-target"
      }
    });

    // Icon to select the node in the inspector.
    this.highlightNodeEl = createNode({
      parent: this.el,
      nodeType: "span",
      attributes: {
        "class": "node-highlighter",
        "title": L10N.getStr("inspector.nodePreview.highlightNodeLabel")
      }
    });

    // Wrapper used for mouseover/out event handling.
    this.previewEl = createNode({
      parent: this.el,
      nodeType: "span",
      attributes: {
        "title": L10N.getStr("inspector.nodePreview.selectNodeLabel")
      }
    });

    if (!this.options.compact) {
      this.previewEl.appendChild(document.createTextNode("<"));
    }

    // Only used for ::before and ::after pseudo-elements.
    this.pseudoEl = createNode({
      parent: this.previewEl,
      nodeType: "span",
      attributes: {
        "class": "pseudo-element theme-fg-color5"
      }
    });

    // Tag name.
    this.tagNameEl = createNode({
      parent: this.previewEl,
      nodeType: "span",
      attributes: {
        "class": "tag-name theme-fg-color3"
      }
    });

    // Id attribute container.
    this.idEl = createNode({
      parent: this.previewEl,
      nodeType: "span"
    });

    if (!this.options.compact) {
      createNode({
        parent: this.idEl,
        nodeType: "span",
        attributes: {
          "class": "attribute-name theme-fg-color2"
        },
        textContent: "id"
      });
      this.idEl.appendChild(document.createTextNode("=\""));
    } else {
      createNode({
        parent: this.idEl,
        nodeType: "span",
        attributes: {
          "class": "theme-fg-color6"
        },
        textContent: "#"
      });
    }

    createNode({
      parent: this.idEl,
      nodeType: "span",
      attributes: {
        "class": "attribute-value theme-fg-color6"
      }
    });

    if (!this.options.compact) {
      this.idEl.appendChild(document.createTextNode("\""));
    }

    // Class attribute container.
    this.classEl = createNode({
      parent: this.previewEl,
      nodeType: "span"
    });

    if (!this.options.compact) {
      createNode({
        parent: this.classEl,
        nodeType: "span",
        attributes: {
          "class": "attribute-name theme-fg-color2"
        },
        textContent: "class"
      });
      this.classEl.appendChild(document.createTextNode("=\""));
    } else {
      createNode({
        parent: this.classEl,
        nodeType: "span",
        attributes: {
          "class": "theme-fg-color6"
        },
        textContent: "."
      });
    }

    createNode({
      parent: this.classEl,
      nodeType: "span",
      attributes: {
        "class": "attribute-value theme-fg-color6"
      }
    });

    if (!this.options.compact) {
      this.classEl.appendChild(document.createTextNode("\""));
      this.previewEl.appendChild(document.createTextNode(">"));
    }

    this.startListeners();
  },

  startListeners: function () {
    // Init events for highlighting and selecting the node.
    this.previewEl.addEventListener("mouseover", this.onPreviewMouseOver);
    this.previewEl.addEventListener("mouseout", this.onPreviewMouseOut);
    this.previewEl.addEventListener("click", this.onSelectElClick);
    this.highlightNodeEl.addEventListener("click", this.onHighlightElClick);

    // Start to listen for markupmutation events.
    this.inspector.on("markupmutation", this.onMarkupMutations);

    // Listen to the target node highlighter.
    HighlighterLock.on("highlighted", this.onHighlighterLocked);
  },

  stopListeners: function () {
    HighlighterLock.off("highlighted", this.onHighlighterLocked);
    this.inspector.off("markupmutation", this.onMarkupMutations);
    this.previewEl.removeEventListener("mouseover", this.onPreviewMouseOver);
    this.previewEl.removeEventListener("mouseout", this.onPreviewMouseOut);
    this.previewEl.removeEventListener("click", this.onSelectElClick);
    this.highlightNodeEl.removeEventListener("click", this.onHighlightElClick);
  },

  destroy: function () {
    HighlighterLock.unhighlight().catch(console.error);

    this.stopListeners();

    this.el.remove();
    this.el = this.tagNameEl = this.idEl = this.classEl = this.pseudoEl = null;
    this.highlightNodeEl = this.previewEl = null;
    this.nodeFront = this.inspector = null;
  },

  get highlighterUtils() {
    if (this.inspector && this.inspector.toolbox) {
      return this.inspector.toolbox.highlighterUtils;
    }
    return null;
  },

  onPreviewMouseOver: function () {
    if (!this.nodeFront || !this.highlighterUtils) {
      return;
    }
    this.highlighterUtils.highlightNodeFront(this.nodeFront)
                         .catch(console.error);
  },

  onPreviewMouseOut: function () {
    if (!this.nodeFront || !this.highlighterUtils) {
      return;
    }
    this.highlighterUtils.unhighlight()
                         .catch(console.error);
  },

  onSelectElClick: function () {
    if (!this.nodeFront) {
      return;
    }
    this.inspector.selection.setNodeFront(this.nodeFront, "dom-node-preview");
  },

  onHighlightElClick: function (e) {
    e.stopPropagation();

    let classList = this.highlightNodeEl.classList;
    let isHighlighted = classList.contains("selected");

    if (isHighlighted) {
      classList.remove("selected");
      HighlighterLock.unhighlight().then(() => {
        this.emit("target-highlighter-unlocked");
      }, console.error);
    } else {
      classList.add("selected");
      HighlighterLock.highlight(this).then(() => {
        this.emit("target-highlighter-locked");
      }, console.error);
    }
  },

  onHighlighterLocked: function (e, domNodePreview) {
    if (domNodePreview !== this) {
      this.highlightNodeEl.classList.remove("selected");
    }
  },

  onMarkupMutations: function (e, mutations) {
    if (!this.nodeFront) {
      return;
    }

    for (let {target} of mutations) {
      if (target === this.nodeFront) {
        // Re-render with the same nodeFront to update the output.
        this.render(this.nodeFront);
        break;
      }
    }
  },

  render: function (nodeFront) {
    this.nodeFront = nodeFront;
    let {displayName, attributes} = nodeFront;

    if (nodeFront.isPseudoElement) {
      this.pseudoEl.textContent = nodeFront.isBeforePseudoElement
                                   ? "::before"
                                   : "::after";
      this.pseudoEl.style.display = "inline";
      this.tagNameEl.style.display = "none";
    } else {
      this.tagNameEl.textContent = displayName;
      this.pseudoEl.style.display = "none";
      this.tagNameEl.style.display = "inline";
    }

    let idIndex = attributes.findIndex(({name}) => name === "id");
    if (idIndex > -1 && attributes[idIndex].value) {
      this.idEl.querySelector(".attribute-value").textContent =
        attributes[idIndex].value;
      this.idEl.style.display = "inline";
    } else {
      this.idEl.style.display = "none";
    }

    let classIndex = attributes.findIndex(({name}) => name === "class");
    if (classIndex > -1 && attributes[classIndex].value) {
      let value = attributes[classIndex].value;
      if (this.options.compact) {
        value = value.split(" ").join(".");
      }

      this.classEl.querySelector(".attribute-value").textContent = value;
      this.classEl.style.display = "inline";
    } else {
      this.classEl.style.display = "none";
    }
  }
};

/**
 * HighlighterLock is a helper used to lock the highlighter on DOM nodes in the
 * page.
 * It instantiates a new highlighter that is then shared amongst all instances
 * of DomNodePreview. This is useful because that means showing the highlighter
 * on one node will unhighlight the previously highlighted one, but will not
 * interfere with the default inspector highlighter.
 */
var HighlighterLock = {
  highlighter: null,
  isShown: false,

  highlight: Task.async(function* (animationTargetNode) {
    if (!this.highlighter) {
      let util = animationTargetNode.inspector.toolbox.highlighterUtils;
      this.highlighter = yield util.getHighlighterByType("BoxModelHighlighter");
    }

    yield this.highlighter.show(animationTargetNode.nodeFront);
    this.isShown = true;
    this.emit("highlighted", animationTargetNode);
  }),

  unhighlight: Task.async(function* () {
    if (!this.highlighter || !this.isShown) {
      return;
    }

    yield this.highlighter.hide();
    this.isShown = false;
    this.emit("unhighlighted");
  })
};

EventEmitter.decorate(HighlighterLock);
PK
!<����}q}qPchrome/devtools/modules/devtools/client/inspector/shared/highlighters-overlay.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=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 Services = require("Services");
const EventEmitter = require("devtools/shared/old-event-emitter");
const {
  VIEW_NODE_VALUE_TYPE,
  VIEW_NODE_SHAPE_POINT_TYPE
} = require("devtools/client/inspector/shared/node-types");

const DEFAULT_GRID_COLOR = "#4B0082";
const INSET_POINT_TYPES = ["top", "right", "bottom", "left"];

/**
 * Highlighters overlay is a singleton managing all highlighters in the Inspector.
 */
class HighlightersOverlay {
  /**
   * @param  {Inspector} inspector
   *         Inspector toolbox panel.
   */
  constructor(inspector) {
    this.inspector = inspector;
    this.highlighters = {};
    this.highlighterUtils = this.inspector.toolbox.highlighterUtils;

    // Only initialize the overlay if at least one of the highlighter types is supported.
    this.supportsHighlighters = this.highlighterUtils.supportsCustomHighlighters();

    // NodeFront of the flexbox container that is highlighted.
    this.flexboxHighlighterShown = null;
    // NodeFront of element that is highlighted by the geometry editor.
    this.geometryEditorHighlighterShown = null;
    // NodeFront of the grid container that is highlighted.
    this.gridHighlighterShown = null;
    // Name of the highlighter shown on mouse hover.
    this.hoveredHighlighterShown = null;
    // Name of the selector highlighter shown.
    this.selectorHighlighterShown = null;
    // NodeFront of the shape that is highlighted
    this.shapesHighlighterShown = null;
    // Saved state to be restore on page navigation.
    this.state = {
      flexbox: {},
      grid: {},
      shapes: {},
    };

    this.onClick = this.onClick.bind(this);
    this.onMarkupMutation = this.onMarkupMutation.bind(this);
    this.onMouseMove = this.onMouseMove.bind(this);
    this.onMouseOut = this.onMouseOut.bind(this);
    this.onWillNavigate = this.onWillNavigate.bind(this);
    this.hideFlexboxHighlighter = this.hideFlexboxHighlighter.bind(this);
    this.hideGridHighlighter = this.hideGridHighlighter.bind(this);
    this.hideShapesHighlighter = this.hideShapesHighlighter.bind(this);
    this.showFlexboxHighlighter = this.showFlexboxHighlighter.bind(this);
    this.showGridHighlighter = this.showGridHighlighter.bind(this);
    this.showShapesHighlighter = this.showShapesHighlighter.bind(this);
    this._handleRejection = this._handleRejection.bind(this);
    this._onHighlighterEvent = this._onHighlighterEvent.bind(this);

    // Add inspector events, not specific to a given view.
    this.inspector.on("markupmutation", this.onMarkupMutation);
    this.inspector.target.on("will-navigate", this.onWillNavigate);

    EventEmitter.decorate(this);
  }

  /**
   * Returns whether `node` is somewhere inside the DOM of the rule view.
   *
   * @param {DOMNode} node
   * @return {Boolean}
   */
  isRuleView(node) {
    return !!node.closest("#ruleview-panel");
  }

  /**
   * Add the highlighters overlay to the view. This will start tracking mouse events
   * and display highlighters when needed.
   *
   * @param  {CssRuleView|CssComputedView|LayoutView} view
   *         Either the rule-view or computed-view panel to add the highlighters overlay.
   */
  addToView(view) {
    if (!this.supportsHighlighters) {
      return;
    }

    let el = view.element;
    el.addEventListener("click", this.onClick, true);
    el.addEventListener("mousemove", this.onMouseMove);
    el.addEventListener("mouseout", this.onMouseOut);
    el.ownerDocument.defaultView.addEventListener("mouseout", this.onMouseOut);
  }

  /**
   * Remove the overlay from the given view. This will stop tracking mouse movement and
   * showing highlighters.
   *
   * @param  {CssRuleView|CssComputedView|LayoutView} view
   *         Either the rule-view or computed-view panel to remove the highlighters
   *         overlay.
   */
  removeFromView(view) {
    if (!this.supportsHighlighters) {
      return;
    }

    let el = view.element;
    el.removeEventListener("click", this.onClick, true);
    el.removeEventListener("mousemove", this.onMouseMove);
    el.removeEventListener("mouseout", this.onMouseOut);
  }

  /**
   * Toggle the shapes highlighter for the given element with a shape.
   *
   * @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 toggleShapesHighlighter(node, options = {}) {
    options.transformMode = options.ctrlOrMetaPressed;

    if (node == this.shapesHighlighterShown &&
        options.mode === this.state.shapes.options.mode) {
      // If meta/ctrl is not pressed, hide the highlighter.
      if (!options.ctrlOrMetaPressed) {
        await this.hideShapesHighlighter(node);
        return;
      }

      // If meta/ctrl is pressed, toggle transform mode on the highlighter.
      options.transformMode = !this.state.shapes.options.transformMode;
    }

    await this.showShapesHighlighter(node, options);
  }

  /**
   * Show the shapes highlighter for the given element with a shape.
   *
   * @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 showShapesHighlighter(node, options) {
    let highlighter = await this._getHighlighter("ShapesHighlighter");
    if (!highlighter) {
      return;
    }

    let isShown = await highlighter.show(node, options);
    if (!isShown) {
      return;
    }

    this.shapesHighlighterShown = node;
    let { mode } = options;
    this._toggleRuleViewIcon(node, false, ".ruleview-shapeswatch");
    this._toggleRuleViewIcon(node, true, `.ruleview-shapeswatch[data-mode='${mode}']`);

    try {
      // Save shapes highlighter state.
      let { url } = this.inspector.target;
      let selector = await node.getUniqueSelector();
      this.state.shapes = { selector, options, url };
      this.shapesHighlighterShown = node;
      this.emit("shapes-highlighter-shown", node, options);
    } catch (e) {
      this._handleRejection(e);
    }
  }

  /**
   * Hide the shapes highlighter for the given element with a shape.
   *
   * @param  {NodeFront} node
   *         The NodeFront of the element with a shape to unhighlight.
   */
  async hideShapesHighlighter(node) {
    if (!this.shapesHighlighterShown || !this.highlighters.ShapesHighlighter) {
      return;
    }

    this._toggleRuleViewIcon(node, false, ".ruleview-shapeswatch");

    await this.highlighters.ShapesHighlighter.hide();
    this.emit("shapes-highlighter-hidden", this.shapesHighlighterShown,
      this.state.shapes.options);
    this.shapesHighlighterShown = null;
    this.state.shapes = {};
  }

  /**
   * Show the shapes highlighter for the given element, with the given point highlighted.
   *
   * @param {NodeFront} node
   *        The NodeFront of the element to highlight.
   * @param {String} point
   *        The point to highlight in the shapes highlighter.
   */
  async hoverPointShapesHighlighter(node, point) {
    if (node == this.shapesHighlighterShown) {
      let options = Object.assign({}, this.state.shapes.options);
      options.hoverPoint = point;
      await this.showShapesHighlighter(node, options);
    }
  }

  /**
   * Highlight the given shape point in the rule view.
   *
   * @param {String} point
   *        The point to highlight.
   */
  highlightRuleViewShapePoint(point) {
    let view = this.inspector.getPanel("ruleview").view;
    let ruleViewEl = view.element;
    let selector = `.ruleview-shape-point.active`;

    for (let pointNode of ruleViewEl.querySelectorAll(selector)) {
      this._toggleShapePointActive(pointNode, false);
    }

    if (point !== null && point !== undefined) {
      // Because one inset value can represent multiple points, inset points use classes
      // instead of data.
      selector = (INSET_POINT_TYPES.includes(point)) ?
                 `.ruleview-shape-point.${point}` :
                 `.ruleview-shape-point[data-point='${point}']`;

      for (let pointNode of ruleViewEl.querySelectorAll(selector)) {
        let nodeInfo = view.getNodeInfo(pointNode);
        if (this.isRuleViewShapePoint(nodeInfo)) {
          this._toggleShapePointActive(pointNode, true);
        }
      }
    }
  }

  /**
   * Toggle the flexbox highlighter for the given flexbox container element.
   *
   * @param  {NodeFront} node
   *         The NodeFront of the flexbox container element to highlight.
   * @param  {Object} options
   *         Object used for passing options to the flexbox highlighter.
   */
  async toggleFlexboxHighlighter(node, options = {}) {
    if (node == this.flexboxHighlighterShown) {
      await this.hideFlexboxHighlighter(node);
      return;
    }

    await this.showFlexboxHighlighter(node, options);
  }

  /**
   * Show the flexbox highlighter for the given flexbox container element.
   *
   * @param  {NodeFront} node
   *         The NodeFront of the flexbox container element to highlight.
   * @param  {Object} options
   *         Object used for passing options to the flexbox highlighter.
   */
  async showFlexboxHighlighter(node, options) {
    let highlighter = await this._getHighlighter("FlexboxHighlighter");
    if (!highlighter) {
      return;
    }

    let isShown = await highlighter.show(node, options);
    if (!isShown) {
      return;
    }

    this._toggleRuleViewIcon(node, true, ".ruleview-flex");

    try {
      // Save flexbox highlighter state.
      let { url } = this.inspector.target;
      let selector = await node.getUniqueSelector();
      this.state.flexbox = { selector, options, url };
      this.flexboxHighlighterShown = node;

      // Emit the NodeFront of the flexbox container element that the flexbox highlighter
      // was shown for.
      this.emit("flexbox-highlighter-shown", node, options);
    } catch (e) {
      this._handleRejection(e);
    }
  }

  /**
   * Hide the flexbox highlighter for the given flexbox container element.
   *
   * @param  {NodeFront} node
   *         The NodeFront of the flexbox container element to unhighlight.
   */
  async hideFlexboxHighlighter(node) {
    if (!this.flexboxHighlighterShown || !this.highlighters.FlexboxHighlighter) {
      return;
    }

    this._toggleRuleViewIcon(node, false, ".ruleview-flex");

    await this.highlighters.FlexboxHighlighter.hide();

    // Emit the NodeFront of the flexbox container element that the flexbox highlighter
    // was hidden for.
    this.emit("flexbox-highlighter-hidden", this.flexboxHighlighterShown);
    this.flexboxHighlighterShown = null;

    // Erase flexbox highlighter state.
    this.state.flexbox = null;
  }

  /**
   * Toggle the grid highlighter for the given grid container element.
   *
   * @param  {NodeFront} node
   *         The NodeFront of the grid container element to highlight.
   * @param  {Object} options
   *         Object used for passing options to the grid highlighter.
   * @param. {String|null} trigger
   *         String name matching "grid" or "rule" to indicate where the
   *         grid highlighter was toggled on from. "grid" represents the grid view
   *         "rule" represents the rule view.
   */
  async toggleGridHighlighter(node, options = {}, trigger) {
    if (node == this.gridHighlighterShown) {
      await this.hideGridHighlighter(node);
      return;
    }

    await this.showGridHighlighter(node, options, trigger);
  }

  /**
   * Show the grid highlighter for the given grid container element.
   *
   * @param  {NodeFront} node
   *         The NodeFront of the grid container element to highlight.
   * @param  {Object} options
   *         Object used for passing options to the grid highlighter.
   */
  async showGridHighlighter(node, options, trigger) {
    let highlighter = await this._getHighlighter("CssGridHighlighter");
    if (!highlighter) {
      return;
    }

    let isShown = await highlighter.show(node, options);
    if (!isShown) {
      return;
    }

    this._toggleRuleViewIcon(node, true, ".ruleview-grid");

    if (trigger == "grid") {
      Services.telemetry.scalarAdd("devtools.grid.gridinspector.opened", 1);
    } else if (trigger == "rule") {
      Services.telemetry.scalarAdd("devtools.rules.gridinspector.opened", 1);
    }

    try {
      // Save grid highlighter state.
      let { url } = this.inspector.target;
      let selector = await node.getUniqueSelector();
      this.state.grid = { selector, options, url };
      this.gridHighlighterShown = node;
      // Emit the NodeFront of the grid container element that the grid highlighter was
      // shown for.
      this.emit("grid-highlighter-shown", node, options);
    } catch (e) {
      this._handleRejection(e);
    }
  }

  /**
   * Hide the grid highlighter for the given grid container element.
   *
   * @param  {NodeFront} node
   *         The NodeFront of the grid container element to unhighlight.
   */
  async hideGridHighlighter(node) {
    if (!this.gridHighlighterShown || !this.highlighters.CssGridHighlighter) {
      return;
    }

    this._toggleRuleViewIcon(node, false, ".ruleview-grid");

    await this.highlighters.CssGridHighlighter.hide();

    // Emit the NodeFront of the grid container element that the grid highlighter was
    // hidden for.
    this.emit("grid-highlighter-hidden", this.gridHighlighterShown,
      this.state.grid.options);
    this.gridHighlighterShown = null;

    // Erase grid highlighter state.
    this.state.grid = {};
  }

  /**
   * Toggle the geometry editor highlighter for the given element.
   *
   * @param {NodeFront} node
   *        The NodeFront of the element to highlight.
   */
  async toggleGeometryHighlighter(node) {
    if (node == this.geometryEditorHighlighterShown) {
      await this.hideGeometryEditor();
      return;
    }

    await this.showGeometryEditor(node);
  }

  /**
   * Show the geometry editor highlightor for the given element.
   *
   * @param {NodeFront} node
   *        THe NodeFront of the element to highlight.
   */
  async showGeometryEditor(node) {
    let highlighter = await this._getHighlighter("GeometryEditorHighlighter");
    if (!highlighter) {
      return;
    }

    let isShown = await highlighter.show(node);
    if (!isShown) {
      return;
    }

    this.emit("geometry-editor-highlighter-shown");
    this.geometryEditorHighlighterShown = node;
  }

  /**
   * Hide the geometry editor highlighter.
   */
  async hideGeometryEditor() {
    if (!this.geometryEditorHighlighterShown ||
        !this.highlighters.GeometryEditorHighlighter) {
      return;
    }

    await this.highlighters.GeometryEditorHighlighter.hide();

    this.emit("geometry-editor-highlighter-hidden");
    this.geometryEditorHighlighterShown = null;
  }

  /**
   * Handle events emitted by the highlighter.
   *
   * @param {Object} data
   *        The data object sent in the event.
   */
  _onHighlighterEvent(data) {
    if (data.type === "shape-hover-on") {
      this.state.shapes.hoverPoint = data.point;
      this.emit("hover-shape-point", data.point);
    } else if (data.type === "shape-hover-off") {
      this.state.shapes.hoverPoint = null;
      this.emit("hover-shape-point", null);
    }

    this.emit("highlighter-event-handled");
  }

  /**
   * Restores the saved flexbox highlighter state.
   */
  async restoreFlexboxState() {
    try {
      await this.restoreState("flexbox", this.state.flexbox, this.showFlexboxHighlighter);
    } catch (e) {
      this._handleRejection(e);
    }
  }

  /**
   * Restores the saved grid highlighter state.
   */
  async restoreGridState() {
    try {
      await this.restoreState("grid", this.state.grid, this.showGridHighlighter);
    } catch (e) {
      this._handleRejection(e);
    }
  }

  /**
   * Restores the saved shape highlighter state.
   */
  async restoreShapeState() {
    try {
      await this.restoreState("shapes", this.state.shapes, this.showShapesHighlighter);
    } catch (e) {
      this._handleRejection(e);
    }
  }

  /**
   * Helper function called by restoreFlexboxState, restoreGridState and
   * restoreShapeState. Restores the saved highlighter state for the given highlighter
   * and their state.
   *
   * @param  {String} name
   *         The name of the highlighter to be restored
   * @param  {Object} state
   *         The state of the highlighter to be restored
   * @param  {Function} showFunction
   *         The function that shows the highlighter
   * @return {Promise} that resolves when the highlighter state was restored, and the
   *         expected highlighters are displayed.
   */
  async restoreState(name, state, showFunction) {
    let { selector, options, url } = state;

    if (!selector || url !== this.inspector.target.url) {
      // Bail out if no selector was saved, or if we are on a different page.
      this.emit(`${name}-state-restored`, { restored: false });
      return;
    }

    let walker = this.inspector.walker;
    let rootNode = await walker.getRootNode();
    let nodeFront = await walker.querySelector(rootNode, selector);

    if (nodeFront) {
      if (options.hoverPoint) {
        options.hoverPoint = null;
      }

      await showFunction(nodeFront, options);
      this.emit(`${name}-state-restored`, { restored: true });
    }

    this.emit(`${name}-state-restored`, { restored: false });
  }

  /**
   * Get a highlighter front given a type. It will only be initialized once.
   *
   * @param  {String} type
   *         The highlighter type. One of this.highlighters.
   * @return {Promise} that resolves to the highlighter
   */
  async _getHighlighter(type) {
    let utils = this.highlighterUtils;

    if (this.highlighters[type]) {
      return this.highlighters[type];
    }

    let highlighter;

    try {
      highlighter = await utils.getHighlighterByType(type);
    } catch (e) {
      // Ignore any error
    }

    if (!highlighter) {
      return null;
    }

    highlighter.on("highlighter-event", this._onHighlighterEvent);
    this.highlighters[type] = highlighter;
    return highlighter;
  }

  _handleRejection(error) {
    if (!this.destroyed) {
      console.error(error);
    }
  }

  /**
   * Toggle all the icons with the given selector in the rule view if the current
   * inspector selection is the highlighted node.
   *
   * @param  {NodeFront} node
   *         The NodeFront of the element with a shape to highlight.
   * @param  {Boolean} active
   *         Whether or not the shape icon should be active.
   * @param  {String} selector
   *         The selector of the rule view icon to toggle.
   */
  _toggleRuleViewIcon(node, active, selector) {
    if (this.inspector.selection.nodeFront != node) {
      return;
    }

    let ruleViewEl = this.inspector.getPanel("ruleview").view.element;

    for (let icon of ruleViewEl.querySelectorAll(selector)) {
      icon.classList.toggle("active", active);
    }
  }

  /**
   * Toggle the class "active" on the given shape point in the rule view if the current
   * inspector selection is highlighted by the shapes highlighter.
   *
   * @param {NodeFront} node
   *        The NodeFront of the shape point to toggle
   * @param {Boolean} active
   *        Whether the shape point should be active
   */
  _toggleShapePointActive(node, active) {
    if (this.inspector.selection.nodeFront != this.shapesHighlighterShown) {
      return;
    }

    node.classList.toggle("active", active);
  }

  /**
   * Hide the currently shown hovered highlighter.
   */
  _hideHoveredHighlighter() {
    if (!this.hoveredHighlighterShown ||
        !this.highlighters[this.hoveredHighlighterShown]) {
      return;
    }

    // For some reason, the call to highlighter.hide doesn't always return a
    // promise. This causes some tests to fail when trying to install a
    // rejection handler on the result of the call. To avoid this, check
    // whether the result is truthy before installing the handler.
    let onHidden = this.highlighters[this.hoveredHighlighterShown].hide();
    if (onHidden) {
      onHidden.catch(console.error);
    }

    this.hoveredHighlighterShown = null;
    this.emit("highlighter-hidden");
  }

  /**
   * Given a node front and a function that hides the given node's highlighter, hides
   * the highlighter if the node front is no longer in the DOM tree. This is called
   * from the "markupmutation" event handler.
   *
   * @param  {NodeFront} node
   *         The NodeFront of a highlighted DOM node.
   * @param  {Function} hideHighlighter
   *         The function that will hide the highlighter of the highlighted node.
   */
  async _hideHighlighterIfDeadNode(node, hideHighlighter) {
    if (!node) {
      return;
    }

    try {
      let isInTree = await this.inspector.walker.isInDOMTree(node);
      if (!isInTree) {
        hideHighlighter(node);
      }
    } catch (e) {
      console.error(e);
    }
  }

  /**
   * Is the current hovered node a css transform property value in the
   * computed-view.
   *
   * @param  {Object} nodeInfo
   * @return {Boolean}
   */
  _isComputedViewTransform(nodeInfo) {
    if (nodeInfo.view != "computed") {
      return false;
    }
    return nodeInfo.type === VIEW_NODE_VALUE_TYPE &&
           nodeInfo.value.property === "transform";
  }

  /**
   * Is the current clicked node a flex display property value in the
   * rule-view.
   *
   * @param  {DOMNode} node
   * @return {Boolean}
   */
  _isRuleViewDisplayFlex(node) {
    return this.isRuleView(node) && node.classList.contains("ruleview-flex");
  }

  /**
   * Is the current clicked node a grid display property value in the
   * rule-view.
   *
   * @param  {DOMNode} node
   * @return {Boolean}
   */
  _isRuleViewDisplayGrid(node) {
    return this.isRuleView(node) && node.classList.contains("ruleview-grid");
  }

  /**
   * Does the current clicked node have the shapes highlighter toggle in the
   * rule-view.
   *
   * @param  {DOMNode} node
   * @return {Boolean}
   */
  _isRuleViewShape(node) {
    return this.isRuleView(node) && node.classList.contains("ruleview-shapeswatch");
  }

  /**
   * Is the current hovered node a css transform property value in the rule-view.
   *
   * @param  {Object} nodeInfo
   * @return {Boolean}
   */
  _isRuleViewTransform(nodeInfo) {
    if (nodeInfo.view != "rule") {
      return false;
    }
    let isTransform = nodeInfo.type === VIEW_NODE_VALUE_TYPE &&
                      nodeInfo.value.property === "transform";
    let isEnabled = nodeInfo.value.enabled &&
                    !nodeInfo.value.overridden &&
                    !nodeInfo.value.pseudoElement;
    return isTransform && isEnabled;
  }

  /**
   * Is the current hovered node a highlightable shape point in the rule-view.
   *
   * @param  {Object} nodeInfo
   * @return {Boolean}
   */
  isRuleViewShapePoint(nodeInfo) {
    if (nodeInfo.view != "rule") {
      return false;
    }
    let isShape = nodeInfo.type === VIEW_NODE_SHAPE_POINT_TYPE &&
                  (nodeInfo.value.property === "clip-path" ||
                  nodeInfo.value.property === "shape-outside");
    let isEnabled = nodeInfo.value.enabled &&
                    !nodeInfo.value.overridden &&
                    !nodeInfo.value.pseudoElement;
    return isShape && isEnabled && nodeInfo.value.toggleActive &&
           !this.state.shapes.options.transformMode;
  }

  onClick(event) {
    if (this._isRuleViewDisplayGrid(event.target)) {
      event.stopPropagation();

      let { store } = this.inspector;
      let { grids, highlighterSettings } = store.getState();
      let grid = grids.find(g => g.nodeFront == this.inspector.selection.nodeFront);

      highlighterSettings.color = grid ? grid.color : DEFAULT_GRID_COLOR;

      this.toggleGridHighlighter(this.inspector.selection.nodeFront, highlighterSettings,
        "rule");
    } else if (this._isRuleViewDisplayFlex(event.target)) {
      event.stopPropagation();

      this.toggleFlexboxHighlighter(this.inspector.selection.nodeFront);
    } else if (this._isRuleViewShape(event.target)) {
      event.stopPropagation();

      let settings = {
        mode: event.target.dataset.mode,
        ctrlOrMetaPressed: event.metaKey || event.ctrlKey
      };
      this.toggleShapesHighlighter(this.inspector.selection.nodeFront, settings);
    }
  }

  onMouseMove(event) {
    // Bail out if the target is the same as for the last mousemove.
    if (event.target === this._lastHovered) {
      return;
    }

    // Only one highlighter can be displayed at a time, hide the currently shown.
    this._hideHoveredHighlighter();

    this._lastHovered = event.target;

    let view = this.isRuleView(this._lastHovered) ?
      this.inspector.getPanel("ruleview").view :
      this.inspector.getPanel("computedview").computedView;
    let nodeInfo = view.getNodeInfo(event.target);
    if (!nodeInfo) {
      return;
    }

    if (this.isRuleViewShapePoint(nodeInfo)) {
      let { point } = nodeInfo.value;
      this.hoverPointShapesHighlighter(this.inspector.selection.nodeFront, point);
      this.emit("hover-shape-point", point);
      return;
    }

    // Choose the type of highlighter required for the hovered node.
    let type;
    if (this._isRuleViewTransform(nodeInfo) ||
        this._isComputedViewTransform(nodeInfo)) {
      type = "CssTransformHighlighter";
    }

    if (type) {
      this.hoveredHighlighterShown = type;
      let node = this.inspector.selection.nodeFront;
      this._getHighlighter(type)
          .then(highlighter => highlighter.show(node))
          .then(shown => {
            if (shown) {
              this.emit("highlighter-shown");
            }
          });
    }
  }

  onMouseOut(event) {
    // Only hide the highlighter if the mouse leaves the currently hovered node.
    if (!this._lastHovered ||
        (event && this._lastHovered.contains(event.relatedTarget))) {
      return;
    }

    // Otherwise, hide the highlighter.
    let view = this.isRuleView(this._lastHovered) ?
      this.inspector.getPanel("ruleview").view :
      this.inspector.getPanel("computedview").computedView;
    let nodeInfo = view.getNodeInfo(this._lastHovered);
    if (nodeInfo && this.isRuleViewShapePoint(nodeInfo)) {
      this.hoverPointShapesHighlighter(this.inspector.selection.nodeFront, null);
      this.emit("hover-shape-point", null);
    }
    this._lastHovered = null;
    this._hideHoveredHighlighter();
  }

  /**
   * Handler function for "markupmutation" events. Hides the flexbox/grid/shapes
   * highlighter if the flexbox/grid/shapes container is no longer in the DOM tree.
   */
  async onMarkupMutation(evt, mutations) {
    let hasInterestingMutation = mutations.some(mut => mut.type === "childList");
    if (!hasInterestingMutation) {
      // Bail out if the mutations did not remove nodes, or if no grid highlighter is
      // displayed.
      return;
    }

    this._hideHighlighterIfDeadNode(this.flexboxHighlighterShown,
      this.hideFlexboxHighlighter);
    this._hideHighlighterIfDeadNode(this.gridHighlighterShown,
      this.hideGridHighlighter);
    this._hideHighlighterIfDeadNode(this.shapesHighlighterShown,
      this.hideShapesHighlighter);
  }

  /**
   * Clear saved highlighter shown properties on will-navigate.
   */
  onWillNavigate() {
    this.flexboxHighlighterShown = null;
    this.geometryEditorHighlighterShown = null;
    this.gridHighlighterShown = null;
    this.hoveredHighlighterShown = null;
    this.selectorHighlighterShown = null;
    this.shapesHighlighterShown = null;
  }

  /**
   * Destroy this overlay instance, removing it from the view and destroying
   * all initialized highlighters.
   */
  destroy() {
    for (let type in this.highlighters) {
      if (this.highlighters[type]) {
        if (this.highlighters[type].off) {
          this.highlighters[type].off("highlighter-event", this._onHighlighterEvent);
        }
        this.highlighters[type].finalize();
        this.highlighters[type] = null;
      }
    }

    // Remove inspector events.
    this.inspector.off("markupmutation", this.onMarkupMutation);
    this.inspector.target.off("will-navigate", this.onWillNavigate);

    this._lastHovered = null;

    this.inspector = null;
    this.highlighters = null;
    this.highlighterUtils = null;
    this.supportsHighlighters = null;
    this.state = null;

    this.flexboxHighlighterShown = null;
    this.geometryEditorHighlighterShown = null;
    this.gridHighlighterShown = null;
    this.hoveredHighlighterShown = null;
    this.selectorHighlighterShown = null;
    this.shapesHighlighterShown = null;

    this.destroyed = true;
  }
}

module.exports = HighlightersOverlay;
PK
!<���Ŝ�Fchrome/devtools/modules/devtools/client/inspector/shared/node-types.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=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";

/**
 * Types of nodes used in the rule and computed view.
 */

exports.VIEW_NODE_SELECTOR_TYPE = 1;
exports.VIEW_NODE_PROPERTY_TYPE = 2;
exports.VIEW_NODE_VALUE_TYPE = 3;
exports.VIEW_NODE_IMAGE_URL_TYPE = 4;
exports.VIEW_NODE_LOCATION_TYPE = 5;
exports.VIEW_NODE_SHAPE_POINT_TYPE = 6;
exports.VIEW_NODE_VARIABLE_TYPE = 7;
exports.VIEW_NODE_FONT_TYPE = 8;
PK
!<T��`�
�
Jchrome/devtools/modules/devtools/client/inspector/shared/reflow-tracker.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=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 { ReflowFront } = require("devtools/shared/fronts/reflow");

/**
 * Simple utility class that listens to reflows on a given target if and only if a
 * listener is actively listening to reflows.
 *
 * @param {Object} target
 *        The current target (as in toolbox target)
 */
function ReflowTracker(target) {
  this.target = target;

  // Hold a Map of all the listeners interested in reflows.
  this.listeners = new Map();

  this.reflowFront = null;

  this.onReflow = this.onReflow.bind(this);
}

ReflowTracker.prototype = {

  destroy() {
    if (this.reflowFront) {
      this.stopTracking();
      this.reflowFront.destroy();
      this.reflowFront = null;
    }

    this.listeners.clear();
  },

  startTracking() {
    // Initialize reflow front if necessary.
    if (!this.reflowFront && this.target.form.reflowActor) {
      let { client, form } = this.target;
      this.reflowFront = ReflowFront(client, form);
    }

    if (this.reflowFront) {
      this.reflowFront.on("reflows", this.onReflow);
      this.reflowFront.start();
    }
  },

  stopTracking() {
    if (this.reflowFront) {
      this.reflowFront.off("reflows", this.onReflow);
      this.reflowFront.stop();
    }
  },

  /**
   * Add a listener for reflows.
   *
   * @param {Object} listener
   *        Object/instance listening to reflows.
   * @param {Function} callback
   *        The associated callback.
   */
  trackReflows(listener, callback) {
    if (this.listeners.get(listener) === callback) {
      return;
    }

    // No listener interested in reflows yet, start tracking.
    if (this.listeners.size === 0) {
      this.startTracking();
    }

    this.listeners.set(listener, callback);
  },

  /**
   * Remove a listener for reflows.
   *
   * @param {Object} listener
   *        Object/instance listening to reflows.
   * @param {Function} callback
   *        The associated callback.
   */
  untrackReflows(listener, callback) {
    if (this.listeners.get(listener) !== callback) {
      return;
    }

    this.listeners.delete(listener);

    // No listener interested in reflows anymore, stop tracking.
    if (this.listeners.size === 0) {
      this.stopTracking();
    }
  },

  /**
   * Handler called when a reflow happened.
   */
  onReflow() {
    for (let [, callback] of this.listeners) {
      callback();
    }
  },
};

module.exports = ReflowTracker;
PK
!<A_88Pchrome/devtools/modules/devtools/client/inspector/shared/style-inspector-menu.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=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 Services = require("Services");
const {Task} = require("devtools/shared/task");

const Menu = require("devtools/client/framework/menu");
const MenuItem = require("devtools/client/framework/menu-item");

const {
  VIEW_NODE_SELECTOR_TYPE,
  VIEW_NODE_PROPERTY_TYPE,
  VIEW_NODE_VALUE_TYPE,
  VIEW_NODE_IMAGE_URL_TYPE,
  VIEW_NODE_LOCATION_TYPE,
} = require("devtools/client/inspector/shared/node-types");
const clipboardHelper = require("devtools/shared/platform/clipboard");

const STYLE_INSPECTOR_PROPERTIES = "devtools/shared/locales/styleinspector.properties";
const {LocalizationHelper} = require("devtools/shared/l10n");
const STYLE_INSPECTOR_L10N = new LocalizationHelper(STYLE_INSPECTOR_PROPERTIES);

const PREF_ORIG_SOURCES = "devtools.source-map.client-service.enabled";

/**
 * Style inspector context menu
 *
 * @param {RuleView|ComputedView} view
 *        RuleView or ComputedView instance controlling this menu
 * @param {Object} options
 *        Option menu configuration
 */
function StyleInspectorMenu(view, options) {
  this.view = view;
  this.inspector = this.view.inspector;
  this.styleDocument = this.view.styleDocument;
  this.styleWindow = this.view.styleWindow;

  this.isRuleView = options.isRuleView;

  this._onAddNewRule = this._onAddNewRule.bind(this);
  this._onCopy = this._onCopy.bind(this);
  this._onCopyColor = this._onCopyColor.bind(this);
  this._onCopyImageDataUrl = this._onCopyImageDataUrl.bind(this);
  this._onCopyLocation = this._onCopyLocation.bind(this);
  this._onCopyPropertyDeclaration = this._onCopyPropertyDeclaration.bind(this);
  this._onCopyPropertyName = this._onCopyPropertyName.bind(this);
  this._onCopyPropertyValue = this._onCopyPropertyValue.bind(this);
  this._onCopyRule = this._onCopyRule.bind(this);
  this._onCopySelector = this._onCopySelector.bind(this);
  this._onCopyUrl = this._onCopyUrl.bind(this);
  this._onSelectAll = this._onSelectAll.bind(this);
  this._onToggleOrigSources = this._onToggleOrigSources.bind(this);
}

module.exports = StyleInspectorMenu;

StyleInspectorMenu.prototype = {
  /**
   * Display the style inspector context menu
   */
  show: function (event) {
    try {
      this._openMenu({
        target: event.explicitOriginalTarget,
        screenX: event.screenX,
        screenY: event.screenY,
      });
    } catch (e) {
      console.error(e);
    }
  },

  _openMenu: function ({ target, screenX = 0, screenY = 0 } = { }) {
    // In the sidebar we do not have this.styleDocument.popupNode
    // so we need to save the node ourselves.
    this.styleDocument.popupNode = target;
    this.styleWindow.focus();

    let menu = new Menu();

    let menuitemCopy = new MenuItem({
      label: STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copy"),
      accesskey: STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copy.accessKey"),
      click: () => {
        this._onCopy();
      },
      disabled: !this._hasTextSelected(),
    });
    let menuitemCopyLocation = new MenuItem({
      label: STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copyLocation"),
      click: () => {
        this._onCopyLocation();
      },
      visible: false,
    });
    let menuitemCopyRule = new MenuItem({
      label: STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copyRule"),
      click: () => {
        this._onCopyRule();
      },
      visible: this.isRuleView,
    });
    let copyColorAccessKey = "styleinspector.contextmenu.copyColor.accessKey";
    let menuitemCopyColor = new MenuItem({
      label: STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copyColor"),
      accesskey: STYLE_INSPECTOR_L10N.getStr(copyColorAccessKey),
      click: () => {
        this._onCopyColor();
      },
      visible: this._isColorPopup(),
    });
    let copyUrlAccessKey = "styleinspector.contextmenu.copyUrl.accessKey";
    let menuitemCopyUrl = new MenuItem({
      label: STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copyUrl"),
      accesskey: STYLE_INSPECTOR_L10N.getStr(copyUrlAccessKey),
      click: () => {
        this._onCopyUrl();
      },
      visible: this._isImageUrl(),
    });
    let copyImageAccessKey = "styleinspector.contextmenu.copyImageDataUrl.accessKey";
    let menuitemCopyImageDataUrl = new MenuItem({
      label: STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copyImageDataUrl"),
      accesskey: STYLE_INSPECTOR_L10N.getStr(copyImageAccessKey),
      click: () => {
        this._onCopyImageDataUrl();
      },
      visible: this._isImageUrl(),
    });
    let copyPropDeclarationLabel = "styleinspector.contextmenu.copyPropertyDeclaration";
    let menuitemCopyPropertyDeclaration = new MenuItem({
      label: STYLE_INSPECTOR_L10N.getStr(copyPropDeclarationLabel),
      click: () => {
        this._onCopyPropertyDeclaration();
      },
      visible: false,
    });
    let menuitemCopyPropertyName = new MenuItem({
      label: STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copyPropertyName"),
      click: () => {
        this._onCopyPropertyName();
      },
      visible: false,
    });
    let menuitemCopyPropertyValue = new MenuItem({
      label: STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copyPropertyValue"),
      click: () => {
        this._onCopyPropertyValue();
      },
      visible: false,
    });
    let menuitemCopySelector = new MenuItem({
      label: STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copySelector"),
      click: () => {
        this._onCopySelector();
      },
      visible: false,
    });

    this._clickedNodeInfo = this._getClickedNodeInfo();
    if (this.isRuleView && this._clickedNodeInfo) {
      switch (this._clickedNodeInfo.type) {
        case VIEW_NODE_PROPERTY_TYPE :
          menuitemCopyPropertyDeclaration.visible = true;
          menuitemCopyPropertyName.visible = true;
          break;
        case VIEW_NODE_VALUE_TYPE :
          menuitemCopyPropertyDeclaration.visible = true;
          menuitemCopyPropertyValue.visible = true;
          break;
        case VIEW_NODE_SELECTOR_TYPE :
          menuitemCopySelector.visible = true;
          break;
        case VIEW_NODE_LOCATION_TYPE :
          menuitemCopyLocation.visible = true;
          break;
      }
    }

    menu.append(menuitemCopy);
    menu.append(menuitemCopyLocation);
    menu.append(menuitemCopyRule);
    menu.append(menuitemCopyColor);
    menu.append(menuitemCopyUrl);
    menu.append(menuitemCopyImageDataUrl);
    menu.append(menuitemCopyPropertyDeclaration);
    menu.append(menuitemCopyPropertyName);
    menu.append(menuitemCopyPropertyValue);
    menu.append(menuitemCopySelector);

    menu.append(new MenuItem({
      type: "separator",
    }));

    // Select All
    let selectAllAccessKey = "styleinspector.contextmenu.selectAll.accessKey";
    let menuitemSelectAll = new MenuItem({
      label: STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.selectAll"),
      accesskey: STYLE_INSPECTOR_L10N.getStr(selectAllAccessKey),
      click: () => {
        this._onSelectAll();
      },
    });
    menu.append(menuitemSelectAll);

    menu.append(new MenuItem({
      type: "separator",
    }));

    // Add new rule
    let addRuleAccessKey = "styleinspector.contextmenu.addNewRule.accessKey";
    let menuitemAddRule = new MenuItem({
      label: STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.addNewRule"),
      accesskey: STYLE_INSPECTOR_L10N.getStr(addRuleAccessKey),
      click: () => {
        this._onAddNewRule();
      },
      visible: this.isRuleView,
      disabled: !this.isRuleView ||
                this.inspector.selection.isAnonymousNode(),
    });
    menu.append(menuitemAddRule);

    // Show Original Sources
    let sourcesAccessKey = "styleinspector.contextmenu.toggleOrigSources.accessKey";
    let menuitemSources = new MenuItem({
      label: STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.toggleOrigSources"),
      accesskey: STYLE_INSPECTOR_L10N.getStr(sourcesAccessKey),
      click: () => {
        this._onToggleOrigSources();
      },
      type: "checkbox",
      checked: Services.prefs.getBoolPref(PREF_ORIG_SOURCES),
    });
    menu.append(menuitemSources);

    menu.popup(screenX, screenY, this.inspector._toolbox);
    return menu;
  },

  _hasTextSelected: function () {
    let hasTextSelected;
    let selection = this.styleWindow.getSelection();

    let node = this._getClickedNode();
    if (node.nodeName == "input" || node.nodeName == "textarea") {
      let { selectionStart, selectionEnd } = node;
      hasTextSelected = isFinite(selectionStart) && isFinite(selectionEnd)
        && selectionStart !== selectionEnd;
    } else {
      hasTextSelected = selection.toString() && !selection.isCollapsed;
    }

    return hasTextSelected;
  },

  /**
   * Get the type of the currently clicked node
   */
  _getClickedNodeInfo: function () {
    let node = this._getClickedNode();
    return this.view.getNodeInfo(node);
  },

  /**
   * A helper that determines if the popup was opened with a click to a color
   * value and saves the color to this._colorToCopy.
   *
   * @return {Boolean}
   *         true if click on color opened the popup, false otherwise.
   */
  _isColorPopup: function () {
    this._colorToCopy = "";

    let container = this._getClickedNode();
    if (!container) {
      return false;
    }

    let isColorNode = el => el.dataset && "color" in el.dataset;

    while (!isColorNode(container)) {
      container = container.parentNode;
      if (!container) {
        return false;
      }
    }

    this._colorToCopy = container.dataset.color;
    return true;
  },

  _isPropertyName: function () {
    let nodeInfo = this._getClickedNodeInfo();
    if (!nodeInfo) {
      return false;
    }
    return nodeInfo.type == VIEW_NODE_PROPERTY_TYPE;
  },

  /**
   * Check if the current node (clicked node) is an image URL
   *
   * @return {Boolean} true if the node is an image url
   */
  _isImageUrl: function () {
    let nodeInfo = this._getClickedNodeInfo();
    if (!nodeInfo) {
      return false;
    }
    return nodeInfo.type == VIEW_NODE_IMAGE_URL_TYPE;
  },

  /**
   * Get the DOM Node container for the current popupNode.
   * If popupNode is a textNode, return the parent node, otherwise return
   * popupNode itself.
   *
   * @return {DOMNode}
   */
  _getClickedNode: function () {
    let container = null;
    let node = this.styleDocument.popupNode;

    if (node) {
      let isTextNode = node.nodeType == node.TEXT_NODE;
      container = isTextNode ? node.parentElement : node;
    }

    return container;
  },

  /**
   * Select all text.
   */
  _onSelectAll: function () {
    let selection = this.styleWindow.getSelection();
    selection.selectAllChildren(this.view.element);
  },

  /**
   * Copy the most recently selected color value to clipboard.
   */
  _onCopy: function () {
    this.view.copySelection(this.styleDocument.popupNode);
  },

  /**
   * Copy the most recently selected color value to clipboard.
   */
  _onCopyColor: function () {
    clipboardHelper.copyString(this._colorToCopy);
  },

  /*
   * Retrieve the url for the selected image and copy it to the clipboard
   */
  _onCopyUrl: function () {
    if (!this._clickedNodeInfo) {
      return;
    }

    clipboardHelper.copyString(this._clickedNodeInfo.value.url);
  },

  /**
   * Retrieve the image data for the selected image url and copy it to the
   * clipboard
   */
  _onCopyImageDataUrl: Task.async(function* () {
    if (!this._clickedNodeInfo) {
      return;
    }

    let message;
    try {
      let inspectorFront = this.inspector.inspector;
      let imageUrl = this._clickedNodeInfo.value.url;
      let data = yield inspectorFront.getImageDataFromURL(imageUrl);
      message = yield data.data.string();
    } catch (e) {
      message =
        STYLE_INSPECTOR_L10N.getStr("styleinspector.copyImageDataUrlError");
    }

    clipboardHelper.copyString(message);
  }),

  /**
   * Add a new rule to the current element.
   */
  _onAddNewRule: function () {
    this.view._onAddRule();
  },

  /**
   * Copy the rule source location of the current clicked node.
   */
  _onCopyLocation: function () {
    if (!this._clickedNodeInfo) {
      return;
    }

    clipboardHelper.copyString(this._clickedNodeInfo.value);
  },

  /**
   * Copy the rule property declaration of the current clicked node.
   */
  _onCopyPropertyDeclaration: function () {
    if (!this._clickedNodeInfo) {
      return;
    }

    let textProp = this._clickedNodeInfo.value.textProperty;
    clipboardHelper.copyString(textProp.stringifyProperty());
  },

  /**
   * Copy the rule property name of the current clicked node.
   */
  _onCopyPropertyName: function () {
    if (!this._clickedNodeInfo) {
      return;
    }

    clipboardHelper.copyString(this._clickedNodeInfo.value.property);
  },

  /**
   * Copy the rule property value of the current clicked node.
   */
  _onCopyPropertyValue: function () {
    if (!this._clickedNodeInfo) {
      return;
    }

    clipboardHelper.copyString(this._clickedNodeInfo.value.value);
  },

  /**
   * Copy the rule of the current clicked node.
   */
  _onCopyRule: function () {
    let ruleEditor =
      this.styleDocument.popupNode.parentNode.offsetParent._ruleEditor;
    let rule = ruleEditor.rule;
    clipboardHelper.copyString(rule.stringifyRule());
  },

  /**
   * Copy the rule selector of the current clicked node.
   */
  _onCopySelector: function () {
    if (!this._clickedNodeInfo) {
      return;
    }

    clipboardHelper.copyString(this._clickedNodeInfo.value);
  },

  /**
   *  Toggle the original sources pref.
   */
  _onToggleOrigSources: function () {
    let isEnabled = Services.prefs.getBoolPref(PREF_ORIG_SOURCES);
    Services.prefs.setBoolPref(PREF_ORIG_SOURCES, !isEnabled);
  },

  destroy: function () {
    this.popupNode = null;
    this.styleDocument.popupNode = null;
    this.view = null;
    this.inspector = null;
    this.styleDocument = null;
    this.styleWindow = null;
  }
};
PK
!<l��9�,�,Lchrome/devtools/modules/devtools/client/inspector/shared/tooltips-overlay.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=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";

/**
 * The tooltip overlays are tooltips that appear when hovering over property values and
 * editor tooltips that appear when clicking swatch based editors.
 */

const { Task } = require("devtools/shared/task");
const Services = require("Services");
const {
  VIEW_NODE_VALUE_TYPE,
  VIEW_NODE_FONT_TYPE,
  VIEW_NODE_IMAGE_URL_TYPE,
  VIEW_NODE_VARIABLE_TYPE,
} = require("devtools/client/inspector/shared/node-types");
const { getColor } = require("devtools/client/shared/theme");
const { HTMLTooltip } = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");

loader.lazyRequireGetter(this, "getCssProperties",
  "devtools/shared/fronts/css-properties", true);

loader.lazyRequireGetter(this, "getImageDimensions",
  "devtools/client/shared/widgets/tooltip/ImageTooltipHelper", true);
loader.lazyRequireGetter(this, "setImageTooltip",
  "devtools/client/shared/widgets/tooltip/ImageTooltipHelper", true);
loader.lazyRequireGetter(this, "setBrokenImageTooltip",
  "devtools/client/shared/widgets/tooltip/ImageTooltipHelper", true);
loader.lazyRequireGetter(this, "setVariableTooltip",
  "devtools/client/shared/widgets/tooltip/VariableTooltipHelper", true);

const PREF_IMAGE_TOOLTIP_SIZE = "devtools.inspector.imagePreviewTooltipSize";

// Types of existing tooltips
const TOOLTIP_IMAGE_TYPE = "image";
const TOOLTIP_FONTFAMILY_TYPE = "font-family";
const TOOLTIP_VARIABLE_TYPE = "variable";

/**
 * Manages all tooltips in the style-inspector.
 *
 * @param {CssRuleView|CssComputedView} view
 *        Either the rule-view or computed-view panel
 */
function TooltipsOverlay(view) {
  this.view = view;
  this._instances = new Map();

  this._onNewSelection = this._onNewSelection.bind(this);
  this.view.inspector.selection.on("new-node-front", this._onNewSelection);

  this.addToView();
}

TooltipsOverlay.prototype = {
  get _cssProperties() {
    delete TooltipsOverlay.prototype._cssProperties;
    let properties = getCssProperties(this.view.inspector.toolbox);
    TooltipsOverlay.prototype._cssProperties = properties;
    return properties;
  },

  get isEditing() {
    for (let [, tooltip] of this._instances) {
      if (typeof (tooltip.isEditing) == "function" && tooltip.isEditing()) {
        return true;
      }
    }
    return false;
  },

  /**
   * Add the tooltips overlay to the view. This will start tracking mouse
   * movements and display tooltips when needed
   */
  addToView: function () {
    if (this._isStarted || this._isDestroyed) {
      return;
    }

    this._isStarted = true;

    // For now, preview tooltip has to be instanciated on startup in order to
    // call tooltip.startTogglingOnHover. Ideally startTogglingOnHover wouldn't be part
    // of HTMLTooltip and offer a way to lazy load this tooltip.
    this.getTooltip("previewTooltip");
  },

  /**
   * Lazily fetch and initialize the different tooltips that are used in the inspector.
   * These tooltips are attached to the toolbox document if they require a popup panel.
   * Otherwise, it is attached to the inspector panel document if it is an inline editor.
   *
   * @param {String} name
   *        Identifier name for the tooltip
   */
  getTooltip: function (name) {
    let tooltip = this._instances.get(name);
    if (tooltip) {
      return tooltip;
    }
    let { doc } = this.view.inspector.toolbox;
    switch (name) {
      case "colorPicker":
        const SwatchColorPickerTooltip =
          require("devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip");
        tooltip = new SwatchColorPickerTooltip(doc, this.view.inspector,
          this._cssProperties);
        break;
      case "cubicBezier":
        const SwatchCubicBezierTooltip =
          require("devtools/client/shared/widgets/tooltip/SwatchCubicBezierTooltip");
        tooltip = new SwatchCubicBezierTooltip(doc);
        break;
      case "filterEditor":
        const SwatchFilterTooltip =
          require("devtools/client/shared/widgets/tooltip/SwatchFilterTooltip");
        tooltip = new SwatchFilterTooltip(doc,
          this._cssProperties.getValidityChecker(this.view.inspector.panelDoc));
        break;
      case "previewTooltip":
        tooltip = new HTMLTooltip(doc, {
          type: "arrow",
          useXulWrapper: true
        });
        tooltip.startTogglingOnHover(this.view.element,
          this._onPreviewTooltipTargetHover.bind(this));
        break;
      default:
        throw new Error(`Unsupported tooltip '${name}'`);
    }
    this._instances.set(name, tooltip);
    return tooltip;
  },

  /**
   * Remove the tooltips overlay from the view. This will stop tracking mouse
   * movements and displaying tooltips
   */
  removeFromView: function () {
    if (!this._isStarted || this._isDestroyed) {
      return;
    }

    for (let [, tooltip] of this._instances) {
      tooltip.destroy();
    }

    this._isStarted = false;
  },

  /**
   * Given a hovered node info, find out which type of tooltip should be shown,
   * if any
   *
   * @param {Object} nodeInfo
   * @return {String} The tooltip type to be shown, or null
   */
  _getTooltipType: function ({type, value: prop}) {
    let tooltipType = null;
    let inspector = this.view.inspector;

    // Image preview tooltip
    if (type === VIEW_NODE_IMAGE_URL_TYPE &&
        inspector.hasUrlToImageDataResolver) {
      tooltipType = TOOLTIP_IMAGE_TYPE;
    }

    // Font preview tooltip
    if ((type === VIEW_NODE_VALUE_TYPE && prop.property === "font-family") ||
        (type === VIEW_NODE_FONT_TYPE)) {
      let value = prop.value.toLowerCase();
      if (value !== "inherit" && value !== "unset" && value !== "initial") {
        tooltipType = TOOLTIP_FONTFAMILY_TYPE;
      }
    }

    // Variable preview tooltip
    if (type === VIEW_NODE_VARIABLE_TYPE) {
      tooltipType = TOOLTIP_VARIABLE_TYPE;
    }

    return tooltipType;
  },

  /**
   * Executed by the tooltip when the pointer hovers over an element of the
   * view. Used to decide whether the tooltip should be shown or not and to
   * actually put content in it.
   * Checks if the hovered target is a css value we support tooltips for.
   *
   * @param {DOMNode} target The currently hovered node
   * @return {Promise}
   */
  _onPreviewTooltipTargetHover: Task.async(function* (target) {
    let nodeInfo = this.view.getNodeInfo(target);
    if (!nodeInfo) {
      // The hovered node isn't something we care about
      return false;
    }

    let type = this._getTooltipType(nodeInfo);
    if (!type) {
      // There is no tooltip type defined for the hovered node
      return false;
    }

    for (let [, tooltip] of this._instances) {
      if (tooltip.isVisible()) {
        tooltip.revert();
        tooltip.hide();
      }
    }

    let inspector = this.view.inspector;

    if (type === TOOLTIP_IMAGE_TYPE) {
      try {
        yield this._setImagePreviewTooltip(nodeInfo.value.url);
      } catch (e) {
        yield setBrokenImageTooltip(this.getTooltip("previewTooltip"),
          this.view.inspector.panelDoc);
      }
      return true;
    }

    if (type === TOOLTIP_FONTFAMILY_TYPE) {
      let font = nodeInfo.value.value;
      let nodeFront = inspector.selection.nodeFront;
      yield this._setFontPreviewTooltip(font, nodeFront);

      if (nodeInfo.type === VIEW_NODE_FONT_TYPE) {
        // If the hovered element is on the font family span, anchor
        // the tooltip on the whole property value instead.
        return target.parentNode;
      }
      return true;
    }

    if (type === TOOLTIP_VARIABLE_TYPE && nodeInfo.value.value.startsWith("--")) {
      let variable = nodeInfo.value.variable;
      yield this._setVariablePreviewTooltip(variable);
      return true;
    }

    return false;
  }),

  /**
   * Set the content of the preview tooltip to display an image preview. The image URL can
   * be relative, a call will be made to the debuggee to retrieve the image content as an
   * imageData URI.
   *
   * @param {String} imageUrl
   *        The image url value (may be relative or absolute).
   * @return {Promise} A promise that resolves when the preview tooltip content is ready
   */
  _setImagePreviewTooltip: Task.async(function* (imageUrl) {
    let doc = this.view.inspector.panelDoc;
    let maxDim = Services.prefs.getIntPref(PREF_IMAGE_TOOLTIP_SIZE);

    let naturalWidth, naturalHeight;
    if (imageUrl.startsWith("data:")) {
      // If the imageUrl already is a data-url, save ourselves a round-trip
      let size = yield getImageDimensions(doc, imageUrl);
      naturalWidth = size.naturalWidth;
      naturalHeight = size.naturalHeight;
    } else {
      let inspectorFront = this.view.inspector.inspector;
      let {data, size} = yield inspectorFront.getImageDataFromURL(imageUrl, maxDim);
      imageUrl = yield data.string();
      naturalWidth = size.naturalWidth;
      naturalHeight = size.naturalHeight;
    }

    yield setImageTooltip(this.getTooltip("previewTooltip"), doc, imageUrl,
      {maxDim, naturalWidth, naturalHeight});
  }),

  /**
   * Set the content of the preview tooltip to display a font family preview.
   *
   * @param {String} font
   *        The font family value.
   * @param {object} nodeFront
   *        The NodeActor that will used to retrieve the dataURL for the font
   *        family tooltip contents.
   * @return {Promise} A promise that resolves when the preview tooltip content is ready
   */
  _setFontPreviewTooltip: Task.async(function* (font, nodeFront) {
    if (!font || !nodeFront || typeof nodeFront.getFontFamilyDataURL !== "function") {
      throw new Error("Unable to create font preview tooltip content.");
    }

    font = font.replace(/"/g, "'");
    font = font.replace("!important", "");
    font = font.trim();

    let fillStyle = getColor("body-color");
    let {data, size: maxDim} = yield nodeFront.getFontFamilyDataURL(font, fillStyle);

    let imageUrl = yield data.string();
    let doc = this.view.inspector.panelDoc;
    let {naturalWidth, naturalHeight} = yield getImageDimensions(doc, imageUrl);

    yield setImageTooltip(this.getTooltip("previewTooltip"), doc, imageUrl,
      {hideDimensionLabel: true, hideCheckeredBackground: true,
       maxDim, naturalWidth, naturalHeight});
  }),

  /**
   * Set the content of the preview tooltip to display a variable preview.
   *
   * @param {String} text
   *        The text to display for the variable tooltip
   * @return {Promise} A promise that resolves when the preview tooltip content is ready
   */
  _setVariablePreviewTooltip: Task.async(function* (text) {
    let doc = this.view.inspector.panelDoc;
    yield setVariableTooltip(this.getTooltip("previewTooltip"), doc, text);
  }),

  _onNewSelection: function () {
    for (let [, tooltip] of this._instances) {
      tooltip.hide();
    }
  },

  /**
   * Destroy this overlay instance, removing it from the view
   */
  destroy: function () {
    this.removeFromView();

    this.view.inspector.selection.off("new-node-front", this._onNewSelection);
    this.view = null;

    this._isDestroyed = true;
  }
};

module.exports = TooltipsOverlay;
PK
!<��M���Achrome/devtools/modules/devtools/client/inspector/shared/utils.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 {parseDeclarations} = require("devtools/shared/css/parsing-utils");
const promise = require("promise");
const {getCSSLexer} = require("devtools/shared/css/lexer");
const {KeyCodes} = require("devtools/client/shared/keycodes");
const {throttle} = require("devtools/shared/throttle");

const HTML_NS = "http://www.w3.org/1999/xhtml";

/**
 * Called when a character is typed in a value editor.  This decides
 * whether to advance or not, first by checking to see if ";" was
 * typed, and then by lexing the input and seeing whether the ";"
 * would be a terminator at this point.
 *
 * @param {number} keyCode
 *        Key code to be checked.
 * @param {string} aValue
 *        Current text editor value.
 * @param {number} insertionPoint
 *        The index of the insertion point.
 * @return {Boolean} True if the focus should advance; false if
 *        the character should be inserted.
 */
function advanceValidate(keyCode, value, insertionPoint) {
  // Only ";" has special handling here.
  if (keyCode !== KeyCodes.DOM_VK_SEMICOLON) {
    return false;
  }

  // Insert the character provisionally and see what happens.  If we
  // end up with a ";" symbol token, then the semicolon terminates the
  // value.  Otherwise it's been inserted in some spot where it has a
  // valid meaning, like a comment or string.
  value = value.slice(0, insertionPoint) + ";" + value.slice(insertionPoint);
  let lexer = getCSSLexer(value);
  while (true) {
    let token = lexer.nextToken();
    if (token.endOffset > insertionPoint) {
      if (token.tokenType === "symbol" && token.text === ";") {
        // The ";" is a terminator.
        return true;
      }
      // The ";" is not a terminator in this context.
      break;
    }
  }
  return false;
}

/**
 * Append a text node to an element.
 *
 * @param {Element} parent
 *        The parent node.
 * @param {string} text
 *        The text content for the text node.
 */
function appendText(parent, text) {
  parent.appendChild(parent.ownerDocument.createTextNode(text));
}

/**
 * Event handler that causes a blur on the target if the input has
 * multiple CSS properties as the value.
 */
function blurOnMultipleProperties(cssProperties) {
  return (e) => {
    setTimeout(() => {
      let props = parseDeclarations(cssProperties.isKnown, e.target.value);
      if (props.length > 1) {
        e.target.blur();
      }
    }, 0);
  };
}

/**
 * Create a child element with a set of attributes.
 *
 * @param {Element} parent
 *        The parent node.
 * @param {string} tagName
 *        The tag name.
 * @param {object} attributes
 *        A set of attributes to set on the node.
 */
function createChild(parent, tagName, attributes = {}) {
  let elt = parent.ownerDocument.createElementNS(HTML_NS, tagName);
  for (let attr in attributes) {
    if (attributes.hasOwnProperty(attr)) {
      if (attr === "textContent") {
        elt.textContent = attributes[attr];
      } else if (attr === "child") {
        elt.appendChild(attributes[attr]);
      } else {
        elt.setAttribute(attr, attributes[attr]);
      }
    }
  }
  parent.appendChild(elt);
  return elt;
}

/**
 * Log the provided error to the console and return a rejected Promise for
 * this error.
 *
 * @param {Error} error
 *         The error to log
 * @return {Promise} A rejected promise
 */
function promiseWarn(error) {
  console.error(error);
  return promise.reject(error);
}

/**
 * While waiting for a reps fix in https://github.com/devtools-html/reps/issues/92,
 * translate nodeFront to a grip-like object that can be used with an ElementNode rep.
 *
 * @params  {NodeFront} nodeFront
 *          The NodeFront for which we want to create a grip-like object.
 * @returns {Object} a grip-like object that can be used with Reps.
 */
function translateNodeFrontToGrip(nodeFront) {
  const { attributes } = nodeFront;

  // The main difference between NodeFront and grips is that attributes are treated as
  // a map in grips and as an array in NodeFronts.
  let attributesMap = {};
  for (let {name, value} of attributes) {
    attributesMap[name] = value;
  }

  return {
    actor: nodeFront.actorID,
    preview: {
      attributes: attributesMap,
      attributesLength: attributes.length,
      // All the grid containers are assumed to be in the DOM tree.
      isConnected: true,
      // nodeName is already lowerCased in Node grips
      nodeName: nodeFront.nodeName.toLowerCase(),
      nodeType: nodeFront.nodeType,
    }
  };
}

exports.advanceValidate = advanceValidate;
exports.appendText = appendText;
exports.blurOnMultipleProperties = blurOnMultipleProperties;
exports.createChild = createChild;
exports.promiseWarn = promiseWarn;
exports.throttle = throttle;
exports.translateNodeFrontToGrip = translateNodeFrontToGrip;
PK
!<x(�ss:chrome/devtools/modules/devtools/client/inspector/store.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 } = require("devtools/client/shared/vendor/redux");
const createStore = require("devtools/client/shared/redux/create-store");
const reducers = require("devtools/client/inspector/reducers");
const flags = require("devtools/shared/flags");

module.exports = function () {
  let shouldLog = false;
  let history;

  // If testing, store the action history in an array
  // we'll later attach to the store
  if (flags.testing) {
    history = [];
    shouldLog = true;
  }

  let store = createStore({
    log: shouldLog,
    history
  })(combineReducers(reducers), {});

  if (history) {
    store.history = history;
  }

  return store;
};
PK
!<�C�|�$�$@chrome/devtools/modules/devtools/client/inspector/toolsidebar.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

var EventEmitter = require("devtools/shared/old-event-emitter");
var Telemetry = require("devtools/client/shared/telemetry");
var { Task } = require("devtools/shared/task");

/**
 * This object represents replacement for ToolSidebar
 * implemented in devtools/client/framework/sidebar.js module
 *
 * This new component is part of devtools.html aimed at
 * removing XUL and use HTML for entire DevTools UI.
 * There are currently two implementation of the side bar since
 * the `sidebar.js` module (mentioned above) is still used by
 * other panels.
 * As soon as all panels are using this HTML based
 * implementation it can be removed.
 */
function ToolSidebar(tabbox, panel, uid, options = {}) {
  EventEmitter.decorate(this);

  this._tabbox = tabbox;
  this._uid = uid;
  this._panelDoc = this._tabbox.ownerDocument;
  this._toolPanel = panel;
  this._options = options;

  if (!options.disableTelemetry) {
    this._telemetry = new Telemetry();
  }

  this._tabs = [];

  if (this._options.hideTabstripe) {
    this._tabbox.setAttribute("hidetabs", "true");
  }

  this.render();

  this._toolPanel.emit("sidebar-created", this);
}

exports.ToolSidebar = ToolSidebar;

ToolSidebar.prototype = {
  TABPANEL_ID_PREFIX: "sidebar-panel-",

  // React

  get React() {
    return this._toolPanel.React;
  },

  get ReactDOM() {
    return this._toolPanel.ReactDOM;
  },

  get browserRequire() {
    return this._toolPanel.browserRequire;
  },

  get InspectorTabPanel() {
    return this._toolPanel.InspectorTabPanel;
  },

  // Rendering

  render: function () {
    let Tabbar = this.React.createFactory(this.browserRequire(
      "devtools/client/shared/components/tabs/TabBar"));

    let sidebar = Tabbar({
      menuDocument: this._toolPanel._toolbox.doc,
      showAllTabsMenu: true,
      onSelect: this.handleSelectionChange.bind(this),
    });

    this._tabbar = this.ReactDOM.render(sidebar, this._tabbox);
  },

  /**
   * Register a side-panel tab.
   *
   * @param {String} tab uniq id
   * @param {String} title tab title
   * @param {React.Component} panel component. See `InspectorPanelTab` as an example.
   * @param {Boolean} selected true if the panel should be selected
   * @param {Number} index the position where the tab should be inserted
   */
  addTab: function (id, title, panel, selected, index) {
    this._tabbar.addTab(id, title, selected, panel, null, index);
    this.emit("new-tab-registered", id);
  },

  /**
   * Helper API for adding side-panels that use existing DOM nodes
   * (defined within inspector.xhtml) as the content.
   *
   * @param {String} tab uniq id
   * @param {String} title tab title
   * @param {Boolean} selected true if the panel should be selected
   * @param {Number} index the position where the tab should be inserted
   */
  addExistingTab: function (id, title, selected, index) {
    let panel = this.InspectorTabPanel({
      id: id,
      idPrefix: this.TABPANEL_ID_PREFIX,
      key: id,
      title: title,
    });

    this.addTab(id, title, panel, selected, index);
  },

  /**
   * Helper API for adding side-panels that use existing <iframe> nodes
   * (defined within inspector.xhtml) as the content.
   * The document must have a title, which will be used as the name of the tab.
   *
   * @param {String} tab uniq id
   * @param {String} title tab title
   * @param {String} url
   * @param {Boolean} selected true if the panel should be selected
   * @param {Number} index the position where the tab should be inserted
   */
  addFrameTab: function (id, title, url, selected, index) {
    let panel = this.InspectorTabPanel({
      id: id,
      idPrefix: this.TABPANEL_ID_PREFIX,
      key: id,
      title: title,
      url: url,
      onMount: this.onSidePanelMounted.bind(this),
      onUnmount: this.onSidePanelUnmounted.bind(this),
    });

    this.addTab(id, title, panel, selected, index);
  },

  onSidePanelMounted: function (content, props) {
    let iframe = content.querySelector("iframe");
    if (!iframe || iframe.getAttribute("src")) {
      return;
    }

    let onIFrameLoaded = (event) => {
      iframe.removeEventListener("load", onIFrameLoaded, true);

      let doc = event.target;
      let win = doc.defaultView;
      if ("setPanel" in win) {
        win.setPanel(this._toolPanel, iframe);
      }
      this.emit(props.id + "-ready");
    };

    iframe.addEventListener("load", onIFrameLoaded, true);
    iframe.setAttribute("src", props.url);
  },

  onSidePanelUnmounted: function (content, props) {
    let iframe = content.querySelector("iframe");
    if (!iframe || !iframe.hasAttribute("src")) {
      return;
    }

    let win = iframe.contentWindow;
    if ("destroy" in win) {
      win.destroy(this._toolPanel, iframe);
    }

    iframe.removeAttribute("src");
  },

  /**
   * Remove an existing tab.
   * @param {String} tabId The ID of the tab that was used to register it, or
   * the tab id attribute value if the tab existed before the sidebar
   * got created.
   * @param {String} tabPanelId Optional. If provided, this ID will be used
   * instead of the tabId to retrieve and remove the corresponding <tabpanel>
   */
  removeTab: Task.async(function* (tabId, tabPanelId) {
    this._tabbar.removeTab(tabId);

    let win = this.getWindowForTab(tabId);
    if (win && ("destroy" in win)) {
      yield win.destroy();
    }

    this.emit("tab-unregistered", tabId);
  }),

  /**
   * Show or hide a specific tab.
   * @param {Boolean} isVisible True to show the tab/tabpanel, False to hide it.
   * @param {String} id The ID of the tab to be hidden.
   */
  toggleTab: function (isVisible, id) {
    this._tabbar.toggleTab(id, isVisible);
  },

  /**
   * Select a specific tab.
   */
  select: function (id) {
    this._tabbar.select(id);
  },

  /**
   * Return the id of the selected tab.
   */
  getCurrentTabID: function () {
    return this._currentTool;
  },

  /**
   * Returns the requested tab panel based on the id.
   * @param {String} id
   * @return {DOMNode}
   */
  getTabPanel: function (id) {
    // Search with and without the ID prefix as there might have been existing
    // tabpanels by the time the sidebar got created
    return this._panelDoc.querySelector("#" +
      this.TABPANEL_ID_PREFIX + id + ", #" + id);
  },

  /**
   * Event handler.
   */
  handleSelectionChange: function (id) {
    if (this._destroyed) {
      return;
    }

    let previousTool = this._currentTool;
    if (previousTool) {
      this.emit(previousTool + "-unselected");
    }

    this._currentTool = id;

    this.updateTelemetryOnChange(id, previousTool);
    this.emit(this._currentTool + "-selected");
    this.emit("select", this._currentTool);
  },

  /**
   * Log toolClosed and toolOpened events on telemetry.
   *
   * @param  {String} currentToolId
   *         id of the tool being selected.
   * @param  {String} previousToolId
   *         id of the previously selected tool.
   */
  updateTelemetryOnChange: function (currentToolId, previousToolId) {
    if (currentToolId === previousToolId || !this._telemetry) {
      // Skip telemetry if the tool id did not change or telemetry is unavailable.
      return;
    }

    if (previousToolId) {
      this._telemetry.toolClosed(previousToolId);
    }
    this._telemetry.toolOpened(currentToolId);
  },

  /**
   * Show the sidebar.
   *
   * @param  {String} id
   *         The sidebar tab id to select.
   */
  show: function (id) {
    this._tabbox.removeAttribute("hidden");

    // If an id is given, select the corresponding sidebar tab.
    if (id) {
      this.select(id);
    }

    this.emit("show");
  },

  /**
   * Show the sidebar.
   */
  hide: function () {
    this._tabbox.setAttribute("hidden", "true");

    this.emit("hide");
  },

  /**
   * Return the window containing the tab content.
   */
  getWindowForTab: function (id) {
    // Get the tabpanel and make sure it contains an iframe
    let panel = this.getTabPanel(id);
    if (!panel || !panel.firstElementChild || !panel.firstElementChild.contentWindow) {
      return null;
    }

    return panel.firstElementChild.contentWindow;
  },

  /**
   * Clean-up.
   */
  destroy: Task.async(function* () {
    if (this._destroyed) {
      return;
    }
    this._destroyed = true;

    this.emit("destroy");

    // Note that we check for the existence of this._tabbox.tabpanels at each
    // step as the container window may have been closed by the time one of the
    // panel's destroy promise resolves.
    let tabpanels = [...this._tabbox.querySelectorAll(".tab-panel-box")];
    for (let panel of tabpanels) {
      let iframe = panel.querySelector("iframe");
      if (!iframe) {
        continue;
      }
      let win = iframe.contentWindow;
      if (win && ("destroy" in win)) {
        yield win.destroy();
      }
      panel.remove();
    }

    if (this._currentTool && this._telemetry) {
      this._telemetry.toolClosed(this._currentTool);
    }

    this._toolPanel.emit("sidebar-destroyed", this);

    this._tabs = null;
    this._tabbox = null;
    this._panelDoc = null;
    this._toolPanel = null;
  })
};
PK
!<�� KKFchrome/devtools/modules/devtools/client/jsonview/components/Headers.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

define(function (require, exports, module) {
  const { createFactory, 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 { div, span, table, tbody, tr, td, } = dom;

  /**
   * This template is responsible for rendering basic layout
   * of the 'Headers' panel. It displays HTTP headers groups such as
   * received or response headers.
   */
  class Headers extends Component {
    static get propTypes() {
      return {
        data: PropTypes.object,
      };
    }

    constructor(props) {
      super(props);
      this.state = {};
    }

    render() {
      let data = this.props.data;

      return (
        div({className: "netInfoHeadersTable"},
          div({className: "netHeadersGroup"},
            div({className: "netInfoHeadersGroup"},
              JSONView.Locale.$STR("jsonViewer.responseHeaders")
            ),
            table({cellPadding: 0, cellSpacing: 0},
              HeaderListFactory({headers: data.response})
            )
          ),
          div({className: "netHeadersGroup"},
            div({className: "netInfoHeadersGroup"},
              JSONView.Locale.$STR("jsonViewer.requestHeaders")
            ),
            table({cellPadding: 0, cellSpacing: 0},
              HeaderListFactory({headers: data.request})
            )
          )
        )
      );
    }
  }

  /**
   * This template renders headers list,
   * name + value pairs.
   */
  class HeaderList extends Component {
    static get propTypes() {
      return {
        headers: PropTypes.arrayOf(PropTypes.shape({
          name: PropTypes.string,
          value: PropTypes.string
        }))
      };
    }

    constructor(props) {
      super(props);

      this.state = {
        headers: []
      };
    }

    render() {
      let headers = this.props.headers;

      headers.sort(function (a, b) {
        return a.name > b.name ? 1 : -1;
      });

      let rows = [];
      headers.forEach(header => {
        rows.push(
          tr({key: header.name},
            td({className: "netInfoParamName"},
              span({title: header.name}, header.name)
            ),
            td({className: "netInfoParamValue"}, header.value)
          )
        );
      });

      return (
        tbody({},
          rows
        )
      );
    }
  }

  let HeaderListFactory = createFactory(HeaderList);

  // Exports from this module
  exports.Headers = Headers;
});
PK
!<&k��44Kchrome/devtools/modules/devtools/client/jsonview/components/HeadersPanel.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

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 { createFactories } = require("devtools/client/shared/react-utils");

  const { Headers } = createFactories(require("./Headers"));
  const { HeadersToolbar } = createFactories(require("./HeadersToolbar"));

  const { div } = dom;

  /**
   * This template represents the 'Headers' panel
   * s responsible for rendering its content.
   */
  class HeadersPanel extends Component {
    static get propTypes() {
      return {
        actions: PropTypes.object,
        data: PropTypes.object,
      };
    }

    constructor(props) {
      super(props);

      this.state = {
        data: {}
      };
    }

    render() {
      let data = this.props.data;

      return (
        div({className: "headersPanelBox tab-panel-inner"},
          HeadersToolbar({actions: this.props.actions}),
          div({className: "panelContent"},
            Headers({data: data})
          )
        )
      );
    }
  }

  // Exports from this module
  exports.HeadersPanel = HeadersPanel;
});
PK
!<���ؚ�Mchrome/devtools/modules/devtools/client/jsonview/components/HeadersToolbar.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

 define(function (require, exports, module) {
   const { Component } = require("devtools/client/shared/vendor/react");
   const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
   const { createFactories } = require("devtools/client/shared/react-utils");

   const { Toolbar, ToolbarButton } = createFactories(require("./reps/Toolbar"));

   /**
    * This template is responsible for rendering a toolbar
    * within the 'Headers' panel.
    */
   class HeadersToolbar extends Component {
     static get propTypes() {
       return {
         actions: PropTypes.object,
       };
     }

     constructor(props) {
       super(props);
       this.onCopy = this.onCopy.bind(this);
     }

     // Commands

     onCopy(event) {
       this.props.actions.onCopyHeaders();
     }

     render() {
       return (
         Toolbar({},
           ToolbarButton({className: "btn copy", onClick: this.onCopy},
             JSONView.Locale.$STR("jsonViewer.Copy")
           )
         )
       );
     }
   }

   // Exports from this module
   exports.HeadersToolbar = HeadersToolbar;
 });
PK
!<�JQ���Hchrome/devtools/modules/devtools/client/jsonview/components/JsonPanel.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

define(function (require, exports, module) {
  const { createFactory, 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");
  const { createFactories } = require("devtools/client/shared/react-utils");

  const TreeView =
    createFactory(require("devtools/client/shared/components/tree/TreeView"));
  const { JsonToolbar } = createFactories(require("./JsonToolbar"));

  const { REPS, MODE } = require("devtools/client/shared/components/reps/reps");
  const { Rep } = REPS;

  const { div } = dom;

  function isObject(value) {
    return Object(value) === value;
  }

  /**
   * This template represents the 'JSON' panel. The panel is
   * responsible for rendering an expandable tree that allows simple
   * inspection of JSON structure.
   */
  class JsonPanel extends Component {
    static get propTypes() {
      return {
        data: PropTypes.oneOfType([
          PropTypes.string,
          PropTypes.array,
          PropTypes.object,
          PropTypes.bool,
          PropTypes.number
        ]),
        expandedNodes: PropTypes.instanceOf(Set),
        searchFilter: PropTypes.string,
        actions: PropTypes.object,
      };
    }

    constructor(props) {
      super(props);
      this.state = {};
      this.onKeyPress = this.onKeyPress.bind(this);
      this.onFilter = this.onFilter.bind(this);
      this.renderValue = this.renderValue.bind(this);
      this.renderTree = this.renderTree.bind(this);
    }

    componentDidMount() {
      document.addEventListener("keypress", this.onKeyPress, true);
    }

    componentWillUnmount() {
      document.removeEventListener("keypress", this.onKeyPress, true);
    }

    onKeyPress(e) {
      // XXX shortcut for focusing the Filter field (see Bug 1178771).
    }

    onFilter(object) {
      if (!this.props.searchFilter) {
        return true;
      }

      let json = object.name + JSON.stringify(object.value);
      return json.toLowerCase().includes(this.props.searchFilter.toLowerCase());
    }

    renderValue(props) {
      let member = props.member;

      // Hide object summary when non-empty object is expanded (bug 1244912).
      if (isObject(member.value) && member.hasChildren && member.open) {
        return null;
      }

      // Render the value (summary) using Reps library.
      return Rep(Object.assign({}, props, {
        cropLimit: 50,
        noGrip: true,
        openLink(str) {
          try {
            let u = new URL(str);
            if (u.protocol == "https:" || u.protocol == "http:") {
              window.open(str, "_blank");
            }
          } catch (ex) { /* the link might be bust, then we do nothing */ }
        },
      }));
    }

    renderTree() {
      // Append custom column for displaying values. This column
      // Take all available horizontal space.
      let columns = [{
        id: "value",
        width: "100%"
      }];

      // Render tree component.
      return TreeView({
        object: this.props.data,
        mode: MODE.TINY,
        onFilter: this.onFilter,
        columns: columns,
        renderValue: this.renderValue,
        expandedNodes: this.props.expandedNodes,
      });
    }

    render() {
      let content;
      let data = this.props.data;

      if (!isObject(data)) {
        content = div({className: "jsonPrimitiveValue"}, Rep({
          object: data
        }));
      } else if (data instanceof Error) {
        content = div({className: "jsonParseError"},
          data + ""
        );
      } else {
        content = this.renderTree();
      }

      return (
        div({className: "jsonPanelBox tab-panel-inner"},
          JsonToolbar({actions: this.props.actions}),
          div({className: "panelContent"},
            content
          )
        )
      );
    }
  }

  // Exports from this module
  exports.JsonPanel = JsonPanel;
});
PK
!<��l��Jchrome/devtools/modules/devtools/client/jsonview/components/JsonToolbar.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

define(function (require, exports, module) {
  const { Component } = require("devtools/client/shared/vendor/react");
  const PropTypes = require("devtools/client/shared/vendor/react-prop-types");

  const { createFactories } = require("devtools/client/shared/react-utils");

  const { SearchBox } = createFactories(require("./SearchBox"));
  const { Toolbar, ToolbarButton } = createFactories(require("./reps/Toolbar"));

  /**
   * This template represents a toolbar within the 'JSON' panel.
   */
  class JsonToolbar extends Component {
    static get propTypes() {
      return {
        actions: PropTypes.object,
      };
    }

    constructor(props) {
      super(props);
      this.onSave = this.onSave.bind(this);
      this.onCopy = this.onCopy.bind(this);
    }

    // Commands

    onSave(event) {
      this.props.actions.onSaveJson();
    }

    onCopy(event) {
      this.props.actions.onCopyJson();
    }

    render() {
      return (
        Toolbar({},
          ToolbarButton({className: "btn save", onClick: this.onSave},
            JSONView.Locale.$STR("jsonViewer.Save")
          ),
          ToolbarButton({className: "btn copy", onClick: this.onCopy},
            JSONView.Locale.$STR("jsonViewer.Copy")
          ),
          SearchBox({
            actions: this.props.actions
          })
        )
      );
    }
  }

  // Exports from this module
  exports.JsonToolbar = JsonToolbar;
});
PK
!<>kƋ		Gchrome/devtools/modules/devtools/client/jsonview/components/LiveText.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

define(function (require, exports, module) {
  const { Component } = require("devtools/client/shared/vendor/react");
  const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
  const { findDOMNode } = require("devtools/client/shared/vendor/react-dom");
  const { pre } = require("devtools/client/shared/vendor/react-dom-factories");

  /**
   * This object represents a live DOM text node in a <pre>.
   */
  class LiveText extends Component {
    static get propTypes() {
      return {
        data: PropTypes.instanceOf(Text),
      };
    }

    componentDidMount() {
      this.componentDidUpdate();
    }

    componentDidUpdate() {
      let el = findDOMNode(this);
      if (el.firstChild === this.props.data) {
        return;
      }
      el.textContent = "";
      el.append(this.props.data);
    }

    render() {
      return pre({className: "data"});
    }
  }

  // Exports from this module
  exports.LiveText = LiveText;
});
PK
!<��S��Mchrome/devtools/modules/devtools/client/jsonview/components/MainTabbedArea.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

define(function (require, exports, module) {
  const { Component } = require("devtools/client/shared/vendor/react");
  const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
  const { createFactories } = require("devtools/client/shared/react-utils");
  const { JsonPanel } = createFactories(require("./JsonPanel"));
  const { TextPanel } = createFactories(require("./TextPanel"));
  const { HeadersPanel } = createFactories(require("./HeadersPanel"));
  const { Tabs, TabPanel } = createFactories(require("devtools/client/shared/components/tabs/Tabs"));

  /**
   * This object represents the root application template
   * responsible for rendering the basic tab layout.
   */
  class MainTabbedArea extends Component {
    static get propTypes() {
      return {
        jsonText: PropTypes.instanceOf(Text),
        tabActive: PropTypes.number,
        actions: PropTypes.object,
        headers: PropTypes.object,
        searchFilter: PropTypes.string,
        json: PropTypes.oneOfType([
          PropTypes.string,
          PropTypes.object,
          PropTypes.array,
          PropTypes.bool,
          PropTypes.number
        ]),
        expandedNodes: PropTypes.instanceOf(Set),
      };
    }

    constructor(props) {
      super(props);

      this.state = {
        json: props.json,
        expandedNodes: props.expandedNodes,
        jsonText: props.jsonText,
        tabActive: props.tabActive
      };

      this.onTabChanged = this.onTabChanged.bind(this);
    }

    onTabChanged(index) {
      this.setState({tabActive: index});
    }

    render() {
      return (
        Tabs({
          tabActive: this.state.tabActive,
          onAfterChange: this.onTabChanged},
          TabPanel({
            id: "json",
            className: "json",
            title: JSONView.Locale.$STR("jsonViewer.tab.JSON")},
            JsonPanel({
              data: this.state.json,
              expandedNodes: this.state.expandedNodes,
              actions: this.props.actions,
              searchFilter: this.state.searchFilter
            })
          ),
          TabPanel({
            id: "rawdata",
            className: "rawdata",
            title: JSONView.Locale.$STR("jsonViewer.tab.RawData")},
            TextPanel({
              isValidJson: !(this.state.json instanceof Error) &&
                           document.readyState != "loading",
              data: this.state.jsonText,
              actions: this.props.actions
            })
          ),
          TabPanel({
            id: "headers",
            className: "headers",
            title: JSONView.Locale.$STR("jsonViewer.tab.Headers")},
            HeadersPanel({
              data: this.props.headers,
              actions: this.props.actions,
              searchFilter: this.props.searchFilter
            })
          )
        )
      );
    }
  }

  // Exports from this module
  exports.MainTabbedArea = MainTabbedArea;
});
PK
!<#؇�$$Hchrome/devtools/modules/devtools/client/jsonview/components/SearchBox.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

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 } = dom;

  // For smooth incremental searching (in case the user is typing quickly).
  const searchDelay = 250;

  /**
   * This object represents a search box located at the
   * top right corner of the application.
   */
  class SearchBox extends Component {
    static get propTypes() {
      return {
        actions: PropTypes.object,
      };
    }

    constructor(props) {
      super(props);
      this.onSearch = this.onSearch.bind(this);
      this.doSearch = this.doSearch.bind(this);
    }

    onSearch(event) {
      let searchBox = event.target;
      let win = searchBox.ownerDocument.defaultView;

      if (this.searchTimeout) {
        win.clearTimeout(this.searchTimeout);
      }

      let callback = this.doSearch.bind(this, searchBox);
      this.searchTimeout = win.setTimeout(callback, searchDelay);
    }

    doSearch(searchBox) {
      this.props.actions.onSearch(searchBox.value);
    }

    render() {
      return (
        input({className: "searchBox devtools-filterinput",
               placeholder: JSONView.Locale.$STR("jsonViewer.filterJSON"),
               onChange: this.onSearch})
      );
    }
  }

  // Exports from this module
  exports.SearchBox = SearchBox;
});
PK
!<�T�rrHchrome/devtools/modules/devtools/client/jsonview/components/TextPanel.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

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 { createFactories } = require("devtools/client/shared/react-utils");
  const { TextToolbar } = createFactories(require("./TextToolbar"));
  const { LiveText } = createFactories(require("./LiveText"));
  const { div } = dom;

  /**
   * This template represents the 'Raw Data' panel displaying
   * JSON as a text received from the server.
   */
  class TextPanel extends Component {
    static get propTypes() {
      return {
        isValidJson: PropTypes.bool,
        actions: PropTypes.object,
        data: PropTypes.instanceOf(Text),
      };
    }

    constructor(props) {
      super(props);
      this.state = {};
    }

    render() {
      return (
        div({className: "textPanelBox tab-panel-inner"},
          TextToolbar({
            actions: this.props.actions,
            isValidJson: this.props.isValidJson
          }),
          div({className: "panelContent"},
            LiveText({data: this.props.data})
          )
        )
      );
    }
  }

  // Exports from this module
  exports.TextPanel = TextPanel;
});
PK
!<��{TTJchrome/devtools/modules/devtools/client/jsonview/components/TextToolbar.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

 define(function (require, exports, module) {
   const { Component } = require("devtools/client/shared/vendor/react");
   const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
   const { createFactories } = require("devtools/client/shared/react-utils");
   const { Toolbar, ToolbarButton } = createFactories(require("./reps/Toolbar"));

   /**
    * This object represents a toolbar displayed within the
    * 'Raw Data' panel.
    */
   class TextToolbar extends Component {
     static get propTypes() {
       return {
         actions: PropTypes.object,
         isValidJson: PropTypes.bool
       };
     }

     constructor(props) {
       super(props);
       this.onPrettify = this.onPrettify.bind(this);
       this.onSave = this.onSave.bind(this);
       this.onCopy = this.onCopy.bind(this);
     }

     // Commands

     onPrettify(event) {
       this.props.actions.onPrettify();
     }

     onSave(event) {
       this.props.actions.onSaveJson();
     }

     onCopy(event) {
       this.props.actions.onCopyJson();
     }

     render() {
       return (
         Toolbar({},
           ToolbarButton({
             className: "btn save",
             onClick: this.onSave},
             JSONView.Locale.$STR("jsonViewer.Save")
           ),
           ToolbarButton({
             className: "btn copy",
             onClick: this.onCopy},
             JSONView.Locale.$STR("jsonViewer.Copy")
           ),
           this.props.isValidJson ?
             ToolbarButton({
               className: "btn prettyprint",
               onClick: this.onPrettify},
               JSONView.Locale.$STR("jsonViewer.PrettyPrint")
             ) :
             null
         )
       );
     }
   }

   // Exports from this module
   exports.TextToolbar = TextToolbar;
 });
PK
!<(a�9��Kchrome/devtools/modules/devtools/client/jsonview/components/reps/Toolbar.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

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");

  /**
   * Renders a simple toolbar.
   */
  class Toolbar extends Component {
    static get propTypes() {
      return {
        children: PropTypes.oneOfType([
          PropTypes.array,
          PropTypes.element
        ])
      };
    }

    render() {
      return (
        dom.div({className: "toolbar"},
          this.props.children
        )
      );
    }
  }

  /**
   * Renders a simple toolbar button.
   */
  class ToolbarButton extends Component {
    static get propTypes() {
      return {
        active: PropTypes.bool,
        disabled: PropTypes.bool,
        children: PropTypes.string,
      };
    }

    render() {
      let props = Object.assign({className: "btn"}, this.props);
      return (
        dom.button(props, this.props.children)
      );
    }
  }

  // Exports from this module
  exports.Toolbar = Toolbar;
  exports.ToolbarButton = ToolbarButton;
});
PK
!</�n�['['Cchrome/devtools/modules/devtools/client/jsonview/converter-child.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 {Cc, Ci, Cu, CC} = require("chrome");
const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
const Services = require("Services");

loader.lazyRequireGetter(this, "NetworkHelper",
                               "devtools/shared/webconsole/network-helper");
loader.lazyGetter(this, "debug", function () {
  let {AppConstants} = require("resource://gre/modules/AppConstants.jsm");
  return !!(AppConstants.DEBUG || AppConstants.DEBUG_JS_MODULES);
});

const childProcessMessageManager =
  Cc["@mozilla.org/childprocessmessagemanager;1"]
    .getService(Ci.nsISyncMessageSender);
const BinaryInput = CC("@mozilla.org/binaryinputstream;1",
                       "nsIBinaryInputStream", "setInputStream");
const BufferStream = CC("@mozilla.org/io/arraybuffer-input-stream;1",
                       "nsIArrayBufferInputStream", "setData");

// Localization
loader.lazyGetter(this, "jsonViewStrings", () => {
  return Services.strings.createBundle(
    "chrome://devtools/locale/jsonview.properties");
});

/**
 * This object detects 'application/vnd.mozilla.json.view' content type
 * and converts it into a JSON Viewer application that allows simple
 * JSON inspection.
 *
 * Inspired by JSON View: https://github.com/bhollis/jsonview/
 */
function Converter() {}

Converter.prototype = {
  QueryInterface: XPCOMUtils.generateQI([
    Ci.nsIStreamConverter,
    Ci.nsIStreamListener,
    Ci.nsIRequestObserver
  ]),

  get wrappedJSObject() {
    return this;
  },

  /**
   * This component works as such:
   * 1. asyncConvertData captures the listener
   * 2. onStartRequest fires, initializes stuff, modifies the listener
   *    to match our output type
   * 3. onDataAvailable decodes and inserts data into a text node
   * 4. onStopRequest flushes data and spits back to the listener
   * 5. convert does nothing, it's just the synchronous version
   *    of asyncConvertData
   */
  convert: function (fromStream, fromType, toType, ctx) {
    return fromStream;
  },

  asyncConvertData: function (fromType, toType, listener, ctx) {
    this.listener = listener;
  },

  onDataAvailable: function (request, context, inputStream, offset, count) {
    // Decode and insert data.
    let buffer = new ArrayBuffer(count);
    new BinaryInput(inputStream).readArrayBuffer(count, buffer);
    this.decodeAndInsertBuffer(buffer);
  },

  onStartRequest: function (request, context) {
    // Set the content type to HTML in order to parse the doctype, styles
    // and scripts. The JSON will be manually inserted as text.
    request.QueryInterface(Ci.nsIChannel);
    request.contentType = "text/html";

    // Enforce strict CSP:
    try {
      request.QueryInterface(Ci.nsIHttpChannel);
      request.setResponseHeader("Content-Security-Policy",
        "default-src 'none' ; script-src resource:; ", false);
    } catch (ex) {
      // If this is not an HTTP channel we can't and won't do anything.
    }

    // Don't honor the charset parameter and use UTF-8 (see bug 741776).
    request.contentCharset = "UTF-8";
    this.decoder = new TextDecoder("UTF-8");

    // Changing the content type breaks saving functionality. Fix it.
    fixSave(request);

    // Because content might still have a reference to this window,
    // force setting it to a null principal to avoid it being same-
    // origin with (other) content.
    request.loadInfo.resetPrincipalToInheritToNullPrincipal();

    // Start the request.
    this.listener.onStartRequest(request, context);

    // Initialize stuff.
    let win = NetworkHelper.getWindowForRequest(request);
    this.data = exportData(win, request);
    insertJsonData(win, this.data.json);
    win.addEventListener("contentMessage", onContentMessage, false, true);
    keepThemeUpdated(win);

    // Send the initial HTML code.
    let buffer = new TextEncoder().encode(initialHTML(win.document)).buffer;
    let stream = new BufferStream(buffer, 0, buffer.byteLength);
    this.listener.onDataAvailable(request, context, stream, 0, stream.available());
  },

  onStopRequest: function (request, context, statusCode) {
    // Flush data.
    this.decodeAndInsertBuffer(new ArrayBuffer(0), true);

    // Stop the request.
    this.listener.onStopRequest(request, context, statusCode);
    this.listener = null;
    this.decoder = null;
    this.data = null;
  },

  // Decodes an ArrayBuffer into a string and inserts it into the page.
  decodeAndInsertBuffer: function (buffer, flush = false) {
    // Decode the buffer into a string.
    let data = this.decoder.decode(buffer, {stream: !flush});

    // Using `appendData` instead of `textContent +=` is important to avoid
    // repainting previous data.
    this.data.json.appendData(data);
  }
};

// Lets "save as" save the original JSON, not the viewer.
// To save with the proper extension we need the original content type,
// which has been replaced by application/vnd.mozilla.json.view
function fixSave(request) {
  let match;
  if (request instanceof Ci.nsIHttpChannel) {
    try {
      let header = request.getResponseHeader("Content-Type");
      match = header.match(/^(application\/(?:[^;]+\+)?json)(?:;|$)/);
    } catch (err) {
      // Handled below
    }
  } else {
    let uri = request.QueryInterface(Ci.nsIChannel).URI.spec;
    match = uri.match(/^data:(application\/(?:[^;,]+\+)?json)[;,]/);
  }
  let originalType;
  if (match) {
    originalType = match[1];
  } else {
    originalType = "application/json";
  }
  request.QueryInterface(Ci.nsIWritablePropertyBag);
  request.setProperty("contentType", originalType);
}

// Exports variables that will be accessed by the non-privileged scripts.
function exportData(win, request) {
  let data = Cu.createObjectIn(win, {
    defineAs: "JSONView"
  });

  data.debug = debug;

  data.json = new win.Text();

  data.readyState = "uninitialized";

  let Locale = {
    $STR: key => {
      try {
        return jsonViewStrings.GetStringFromName(key);
      } catch (err) {
        console.error(err);
        return undefined;
      }
    }
  };
  data.Locale = Cu.cloneInto(Locale, win, {cloneFunctions: true});

  let headers = {
    response: [],
    request: []
  };
  // The request doesn't have to be always nsIHttpChannel
  // (e.g. in case of data: URLs)
  if (request instanceof Ci.nsIHttpChannel) {
    request.visitResponseHeaders({
      visitHeader: function (name, value) {
        headers.response.push({name: name, value: value});
      }
    });
    request.visitRequestHeaders({
      visitHeader: function (name, value) {
        headers.request.push({name: name, value: value});
      }
    });
  }
  data.headers = Cu.cloneInto(headers, win);

  return data;
}

// Builds an HTML string that will be used to load stylesheets and scripts.
function initialHTML(doc) {
  // Creates an element with the specified type, attributes and children.
  function element(type, attributes = {}, children = []) {
    let el = doc.createElement(type);
    for (let [attr, value] of Object.entries(attributes)) {
      el.setAttribute(attr, value);
    }
    el.append(...children);
    return el;
  }

  let os;
  let platform = Services.appinfo.OS;
  if (platform.startsWith("WINNT")) {
    os = "win";
  } else if (platform.startsWith("Darwin")) {
    os = "mac";
  } else {
    os = "linux";
  }

  let baseURI = "resource://devtools-client-jsonview/";

  return "<!DOCTYPE html>\n" +
    element("html", {
      "platform": os,
      "class": "theme-" + Services.prefs.getCharPref("devtools.theme"),
      "dir": Services.locale.isAppLocaleRTL ? "rtl" : "ltr"
    }, [
      element("head", {}, [
        element("link", {
          rel: "stylesheet",
          type: "text/css",
          href: baseURI + "css/main.css",
        }),
      ]),
      element("body", {}, [
        element("div", {"id": "content"}, [
          element("div", {"id": "json"})
        ]),
        element("script", {
          src: baseURI + "lib/require.js",
          "data-main": baseURI + "viewer-config.js",
        }),
      ]),
    ]).outerHTML;
}

// We insert the received data into a text node, which should be appended into
// the #json element so that the JSON is still displayed even if JS is disabled.
// However, the HTML parser is not synchronous, so this function uses a mutation
// observer to detect the creation of the element. Then the text node is appended.
function insertJsonData(win, json) {
  new win.MutationObserver(function (mutations, observer) {
    for (let {target, addedNodes} of mutations) {
      if (target.nodeType == 1 && target.id == "content") {
        for (let node of addedNodes) {
          if (node.nodeType == 1 && node.id == "json") {
            observer.disconnect();
            node.append(json);
            return;
          }
        }
      }
    }
  }).observe(win.document, {
    childList: true,
    subtree: true,
  });
}

function keepThemeUpdated(win) {
  let listener = function () {
    let theme = Services.prefs.getCharPref("devtools.theme");
    win.document.documentElement.className = "theme-" + theme;
  };
  Services.prefs.addObserver("devtools.theme", listener);
  win.addEventListener("unload", function (event) {
    Services.prefs.removeObserver("devtools.theme", listener);
    win = null;
  }, {once: true});
}

// Chrome <-> Content communication
function onContentMessage(e) {
  // Do not handle events from different documents.
  let win = this;
  if (win != e.target) {
    return;
  }

  let value = e.detail.value;
  switch (e.detail.type) {
    case "save":
      childProcessMessageManager.sendAsyncMessage(
        "devtools:jsonview:save", value);
  }
}

function createInstance() {
  return new Converter();
}

exports.JsonViewService = {
  createInstance: createInstance,
};
PK
!<S����Fchrome/devtools/modules/devtools/client/jsonview/converter-observer.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 Cm = Components.manager;

const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", {});
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm", {});

// Load devtools module lazily.
XPCOMUtils.defineLazyGetter(this, "devtools", function () {
  const {devtools} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
  return devtools;
});

// Load JsonView services lazily.
XPCOMUtils.defineLazyGetter(this, "JsonViewService", function () {
  const {JsonViewService} = devtools.require("devtools/client/jsonview/converter-child");
  return JsonViewService;
});

// Constants
const JSON_VIEW_PREF = "devtools.jsonview.enabled";
const JSON_VIEW_MIME_TYPE = "application/vnd.mozilla.json.view";
const JSON_VIEW_CONTRACT_ID = "@mozilla.org/streamconv;1?from=" +
  JSON_VIEW_MIME_TYPE + "&to=*/*";
const JSON_VIEW_CLASS_ID = Components.ID("{d8c9acee-dec5-11e4-8c75-1681e6b88ec1}");
const JSON_VIEW_CLASS_DESCRIPTION = "JSONView converter";

const JSON_SNIFFER_CONTRACT_ID = "@mozilla.org/devtools/jsonview-sniffer;1";
const JSON_SNIFFER_CLASS_ID = Components.ID("{4148c488-dca1-49fc-a621-2a0097a62422}");
const JSON_SNIFFER_CLASS_DESCRIPTION = "JSONView content sniffer";
const JSON_VIEW_TYPE = "JSON View";
const CONTENT_SNIFFER_CATEGORY = "net-content-sniffers";

/**
 * This component represents a sniffer (implements nsIContentSniffer
 * interface) responsible for changing top level 'application/json'
 * document types to: 'application/vnd.mozilla.json.view'.
 *
 * This internal type is consequently rendered by JSON View component
 * that represents the JSON through a viewer interface.
 *
 * This is done in the .js file rather than a .jsm to avoid creating
 * a compartment at startup when no JSON is being viewed.
 */
function JsonViewSniffer() {}

JsonViewSniffer.prototype = {
  QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentSniffer]),

  get wrappedJSObject() {
    return this;
  },

  isTopLevelLoad: function (request) {
    let loadInfo = request.loadInfo;
    if (loadInfo && loadInfo.isTopLevelLoad) {
      return (request.loadFlags & Ci.nsIChannel.LOAD_DOCUMENT_URI);
    }
    return false;
  },

  getMIMETypeFromContent: function (request, data, length) {
    if (request instanceof Ci.nsIChannel) {
      // JSON View is enabled only for top level loads only.
      if (!this.isTopLevelLoad(request)) {
        if (request.contentType === JSON_VIEW_MIME_TYPE) {
          return "application/json";
        }
        return "";
      }
      try {
        if (request.contentDisposition ==
          Ci.nsIChannel.DISPOSITION_ATTACHMENT) {
          return "";
        }
      } catch (e) {
        // Channel doesn't support content dispositions
      }

      // Check the response content type and if it's a valid type
      // such as application/json or application/manifest+json
      // change it to new internal type consumed by JSON View.
      if (/^application\/(?:.+\+)?json$/.test(request.contentType)) {
        return JSON_VIEW_MIME_TYPE;
      }
    }

    return "";
  }
};

/*
 * Create instances of the JSON view sniffer.
 */
const JsonSnifferFactory = {
  createInstance: function (outer, iid) {
    if (outer) {
      throw Cr.NS_ERROR_NO_AGGREGATION;
    }
    return new JsonViewSniffer().QueryInterface(iid);
  }
};

/*
 * Create instances of the JSON view converter.
 * This is done in the .js file rather than a .jsm to avoid creating
 * a compartment at startup when no JSON is being viewed.
 */
const JsonViewFactory = {
  createInstance: function (outer, iid) {
    if (outer) {
      throw Cr.NS_ERROR_NO_AGGREGATION;
    }
    return JsonViewService.createInstance().QueryInterface(iid);
  }
};

/**
 * Listen for 'devtools.jsonview.enabled' preference changes and
 * register/unregister the JSON View XPCOM services as appropriate.
 */
function ConverterObserver() {
}

ConverterObserver.prototype = {
  initialize: function () {
    // Only the DevEdition has this feature available by default.
    // Users need to manually flip 'devtools.jsonview.enabled' preference
    // to have it available in other distributions.
    if (this.isEnabled()) {
      this.register();
    }

    Services.prefs.addObserver(JSON_VIEW_PREF, this);
    Services.obs.addObserver(this, "xpcom-shutdown");
  },

  observe: function (subject, topic, data) {
    switch (topic) {
      case "xpcom-shutdown":
        this.onShutdown();
        break;
      case "nsPref:changed":
        this.onPrefChanged();
        break;
    }
  },

  onShutdown: function () {
    Services.prefs.removeObserver(JSON_VIEW_PREF, observer);
    Services.obs.removeObserver(observer, "xpcom-shutdown");
  },

  onPrefChanged: function () {
    if (this.isEnabled()) {
      this.register();
    } else {
      this.unregister();
    }
  },

  register: function () {
    const registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);

    if (!registrar.isCIDRegistered(JSON_SNIFFER_CLASS_ID)) {
      registrar.registerFactory(JSON_SNIFFER_CLASS_ID,
        JSON_SNIFFER_CLASS_DESCRIPTION,
        JSON_SNIFFER_CONTRACT_ID,
        JsonSnifferFactory);
      const categoryManager = Cc["@mozilla.org/categorymanager;1"]
        .getService(Ci.nsICategoryManager);
      categoryManager.addCategoryEntry(CONTENT_SNIFFER_CATEGORY, JSON_VIEW_TYPE,
        JSON_SNIFFER_CONTRACT_ID, false, false);
    }

    if (!registrar.isCIDRegistered(JSON_VIEW_CLASS_ID)) {
      registrar.registerFactory(JSON_VIEW_CLASS_ID,
        JSON_VIEW_CLASS_DESCRIPTION,
        JSON_VIEW_CONTRACT_ID,
        JsonViewFactory);
    }
  },

  unregister: function () {
    const registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);

    if (registrar.isCIDRegistered(JSON_SNIFFER_CLASS_ID)) {
      registrar.unregisterFactory(JSON_SNIFFER_CLASS_ID, JsonSnifferFactory);
      const categoryManager = Cc["@mozilla.org/categorymanager;1"]
        .getService(Ci.nsICategoryManager);
      categoryManager.deleteCategoryEntry(CONTENT_SNIFFER_CATEGORY,
        JSON_VIEW_TYPE, false);
    }

    if (registrar.isCIDRegistered(JSON_VIEW_CLASS_ID)) {
      registrar.unregisterFactory(JSON_VIEW_CLASS_ID, JsonViewFactory);
    }
  },

  isEnabled: function () {
    return Services.prefs.getBoolPref(JSON_VIEW_PREF);
  },
};

// Listen to JSON View 'enable' pref and perform dynamic
// registration or unregistration of the main application
// component.
var observer = new ConverterObserver();
observer.initialize();
PK
!<fu���@chrome/devtools/modules/devtools/client/jsonview/css/general.css/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/******************************************************************************/
/* General */

html, body, #content {
  height: 100%;
}

body {
  color: var(--theme-body-color);
  background-color: var(--theme-body-background);
  padding: 0;
  margin: 0;
}

*:focus {
  outline: none !important;
}

pre {
  background-color: white;
  border: none;
  font-family: var(--monospace-font-family);
}

#content {
  display: flow-root;
}

#json {
  margin: 8px;
  white-space: pre-wrap;
}

/******************************************************************************/
/* Dark Theme */

body.theme-dark {
  color: var(--theme-body-color);
  background-color: var(--theme-body-background);
}

.theme-dark pre {
  background-color: var(--theme-body-background);
}
PK
!<��*��Fchrome/devtools/modules/devtools/client/jsonview/css/headers-panel.css/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/******************************************************************************/
/* Headers Panel */

.headersPanelBox {
  height: 100%;
}

.headersPanelBox .netInfoHeadersTable {
  overflow: auto;
  height: 100%;
}

.headersPanelBox .netHeadersGroup {
  padding: 10px;
}

.headersPanelBox td {
  vertical-align: bottom;
}

.headersPanelBox .netInfoHeadersGroup {
  color: var(--theme-toolbar-color);
  margin-bottom: 10px;
  border-bottom: 1px solid var(--theme-splitter-color);
  padding-top: 8px;
  padding-bottom: 4px;
  font-weight: bold;
  -moz-user-select: none;
}

.headersPanelBox .netInfoParamValue {
  word-wrap: break-word;
}

.headersPanelBox .netInfoParamName {
  padding: 2px 10px 0 0;
  font-weight: bold;
  vertical-align: top;
  text-align: right;
  white-space: nowrap;
}

/******************************************************************************/
/* Theme colors have been generated/copied from Network Panel's header view */

/* Light Theme */
.theme-light .netInfoParamName {
  color: var(--theme-highlight-red);
}

.theme-light .netInfoParamValue {
  color: var(--theme-highlight-purple);
}

/* Dark Theme */
.theme-dark .netInfoParamName {
  color: var(--theme-highlight-purple);
}

.theme-dark .netInfoParamValue {
  color: var(--theme-highlight-gray);
}

/* Firebug Theme */
.theme-firebug .netInfoHeadersTable {
  font-family: Lucida Grande, Tahoma, sans-serif;
  font-size: 11px;
  line-height: 12px;
}

.theme-firebug .netInfoParamValue {
  font-family: var(--monospace-font-family);
}
PK
!<�6� 

Cchrome/devtools/modules/devtools/client/jsonview/css/json-panel.css/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/******************************************************************************/
/* JSON Panel */

.jsonPrimitiveValue,
.jsonParseError {
  font-size: 12px;
  font-family: Lucida Grande, Tahoma, sans-serif;
  line-height: 15px;
  padding: 10px;
}

.jsonParseError {
  color: red;
}
PK
!<�Ai�jj=chrome/devtools/modules/devtools/client/jsonview/css/main.css/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 "resource://devtools/client/themes/variables.css";
@import "resource://devtools/client/themes/common.css";
@import "resource://devtools/client/themes/toolbars.css";

@import "resource://devtools/client/shared/components/tree/TreeView.css";
@import "resource://devtools/client/shared/components/tabs/Tabs.css";

@import "general.css";
@import "search-box.css";
@import "toolbar.css";
@import "json-panel.css";
@import "text-panel.css";
@import "headers-panel.css";

/******************************************************************************/
/* Panel Content */

.tab-panel-inner {
  display: flex;
  flex-direction: column;
  height: 100%;
}

.panelContent {
  direction: ltr;
  overflow-y: auto;
  width: 100%;
  flex: 1;
}

/* The tree takes the entire horizontal space within the panel content. */
.panelContent .treeTable {
  width: 100%;
  font-family: var(--monospace-font-family);
}

:root[platform="linux"] .treeTable {
  font-size: 80%; /* To handle big monospace font */
}

/* Make sure there is a little space between label and value columns. */
.panelContent .treeTable .treeLabelCell {
  padding-right: 17px;
}

/******************************************************************************/
/* Theme Firebug */

/* JSON View is using bigger font-size for the main tabs so,
  let's overwrite the default value. */
.theme-firebug .tabs .tabs-navigation {
  font-size: 14px;
}
PK
!<���4>>Cchrome/devtools/modules/devtools/client/jsonview/css/search-box.css/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 Box */

.devtools-filterinput {
  margin-inline-start: auto; /* Align to the right */
}

/* JSONView doesn't load dark-theme.css */
.theme-dark .devtools-filterinput {
  background-color: rgba(24, 29, 32, 1);
  color: rgba(184, 200, 217, 1);
}PK
!<F*��?chrome/devtools/modules/devtools/client/jsonview/css/search.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" xmlns:xlink="http://www.w3.org/1999/xlink">
  <defs>
    <linearGradient id="a">
      <stop offset="0" stop-color="#427dc2"/>
      <stop offset="1" stop-color="#5e9fce"/>
    </linearGradient>
    <linearGradient id="b">
      <stop offset="0" stop-color="#2f5d93"/>
      <stop offset="1" stop-color="#3a87bd"/>
    </linearGradient>
    <filter id="c" width="1.239" height="1.241" x="-.12" y="-.12" color-interpolation-filters="sRGB">
      <feGaussianBlur stdDeviation=".637"/>
    </filter>
    <linearGradient id="d" x1="4.094" x2="4.094" y1="13.423" y2="2.743" xlink:href="#a" gradientUnits="userSpaceOnUse"/>
    <linearGradient id="e" x1="8.711" x2="8.711" y1="13.58" y2="2.566" xlink:href="#b" gradientUnits="userSpaceOnUse"/>
  </defs>
  <path fill="#fff" stroke="#fff" stroke-width="1.5" d="M10.14 1.656c-2.35 0-4.25 1.9-4.25 4.25 0 .752.19 1.45.532 2.063L1.61 12.78l1.562 1.564 4.78-4.78c.64.384 1.387.592 2.19.592 2.35 0 4.25-1.9 4.25-4.25s-1.9-4.25-4.25-4.25zm0 1.532c1.504 0 2.72 1.214 2.72 2.718s-1.216 2.72-2.72 2.72c-1.503 0-2.718-1.216-2.718-2.72 0-1.504 1.215-2.718 2.72-2.718z" stroke-linejoin="round" filter="url(#c)"/>
  <path fill="url(#d)" stroke="url(#e)" stroke-width=".6" d="M10 2C7.79 2 6 3.79 6 6c0 .828.256 1.612.688 2.25l-4.875 4.875 1.062 1.063L7.75 9.31C8.388 9.745 9.172 10 10 10c2.21 0 4-1.79 4-4s-1.79-4-4-4zm0 1c1.657 0 3 1.343 3 3s-1.343 3-3 3-3-1.343-3-3 1.343-3 3-3z" stroke-linejoin="round"/>
</svg>
PK
!<mmCchrome/devtools/modules/devtools/client/jsonview/css/text-panel.css/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/******************************************************************************/
/* Text Panel */

.textPanelBox {
  height: 100%;
}

.textPanelBox pre {
  margin: 8px;
  white-space: pre-wrap;
  font-family: var(--monospace-font-family);
  color: var(--theme-content-color1);
}

:root[platform="linux"] .textPanelBox .data {
  font-size: 80%; /* To handle big monospace font */
}
PK
!<�'S��@chrome/devtools/modules/devtools/client/jsonview/css/toolbar.css/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/******************************************************************************/
/* Toolbar */

.toolbar {
  display: flex;
  height: 22px;
  font: message-box;
  padding: 1px;
  padding-inline-start: 2px;
  background: var(--theme-toolbar-background);
  border-bottom: 1px solid var(--theme-splitter-color);
}

.toolbar .btn {
  margin-inline-start: 5px;
  color: var(--theme-body-color);
  background: var(--toolbarbutton-background);
  border: 1px solid var(--toolbarbutton-border-color);
  text-decoration: none;
  display: inline-block;
  text-align: center;
  white-space: nowrap;
  vertical-align: middle;
  cursor: pointer;
  -moz-user-select: none;
  padding: 0 2px;
  border-radius: 2px;
}

.toolbar .btn:hover {
  background: var(--toolbarbutton-hover-background);
  border-color: var(--toolbarbutton-hover-border-color);
}

.toolbar .btn:not([disabled]):hover:active {
  background-color: var(--theme-selection-background-hover);
}

PK
!<�3.{��?chrome/devtools/modules/devtools/client/jsonview/json-viewer.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

define(function (require, exports, module) {
  const { render } = require("devtools/client/shared/vendor/react-dom");
  const { createFactories } = require("devtools/client/shared/react-utils");
  const { MainTabbedArea } = createFactories(require("./components/MainTabbedArea"));
  const TreeViewClass = require("devtools/client/shared/components/tree/TreeView");

  const AUTO_EXPAND_MAX_SIZE = 100 * 1024;
  const AUTO_EXPAND_MAX_LEVEL = 7;

  let prettyURL;

  // Application state object.
  let input = {
    jsonText: JSONView.json,
    jsonPretty: null,
    headers: JSONView.headers,
    tabActive: 0,
    prettified: false
  };

  /**
   * Application actions/commands. This list implements all commands
   * available for the JSON viewer.
   */
  input.actions = {
    onCopyJson: function () {
      let text = input.prettified ? input.jsonPretty : input.jsonText;
      copyString(text.textContent);
    },

    onSaveJson: function () {
      if (input.prettified && !prettyURL) {
        prettyURL = URL.createObjectURL(new window.Blob([input.jsonPretty.textContent]));
      }
      dispatchEvent("save", input.prettified ? prettyURL : null);
    },

    onCopyHeaders: function () {
      let value = "";
      let isWinNT = document.documentElement.getAttribute("platform") === "win";
      let eol = isWinNT ? "\r\n" : "\n";

      let responseHeaders = input.headers.response;
      for (let i = 0; i < responseHeaders.length; i++) {
        let header = responseHeaders[i];
        value += header.name + ": " + header.value + eol;
      }

      value += eol;

      let requestHeaders = input.headers.request;
      for (let i = 0; i < requestHeaders.length; i++) {
        let header = requestHeaders[i];
        value += header.name + ": " + header.value + eol;
      }

      copyString(value);
    },

    onSearch: function (value) {
      theApp.setState({searchFilter: value});
    },

    onPrettify: function (data) {
      if (input.json instanceof Error) {
        // Cannot prettify invalid JSON
        return;
      }
      if (input.prettified) {
        theApp.setState({jsonText: input.jsonText});
      } else {
        if (!input.jsonPretty) {
          input.jsonPretty = new Text(JSON.stringify(input.json, null, "  "));
        }
        theApp.setState({jsonText: input.jsonPretty});
      }

      input.prettified = !input.prettified;
    },
  };

  /**
   * Helper for copying a string to the clipboard.
   *
   * @param {String} string The text to be copied.
   */
  function copyString(string) {
    document.addEventListener("copy", event => {
      event.clipboardData.setData("text/plain", string);
      event.preventDefault();
    }, {once: true});

    document.execCommand("copy", false, null);
  }

  /**
   * Helper for dispatching an event. It's handled in chrome scope.
   *
   * @param {String} type Event detail type
   * @param {Object} value Event detail value
   */
  function dispatchEvent(type, value) {
    let data = {
      detail: {
        type,
        value,
      }
    };

    let contentMessageEvent = new CustomEvent("contentMessage", data);
    window.dispatchEvent(contentMessageEvent);
  }

  /**
   * Render the main application component. It's the main tab bar displayed
   * at the top of the window. This component also represents ReacJS root.
   */
  let content = document.getElementById("content");
  let promise = (async function parseJSON() {
    if (document.readyState == "loading") {
      // If the JSON has not been loaded yet, render the Raw Data tab first.
      input.json = {};
      input.expandedNodes = new Set();
      input.tabActive = 1;
      return new Promise(resolve => {
        document.addEventListener("DOMContentLoaded", resolve, {once: true});
      }).then(parseJSON).then(() => {
        // Now update the state and switch to the JSON tab.
        theApp.setState({
          tabActive: 0,
          json: input.json,
          expandedNodes: input.expandedNodes,
        });
      });
    }

    // If the JSON has been loaded, parse it immediately before loading the app.
    let jsonString = input.jsonText.textContent;
    try {
      input.json = JSON.parse(jsonString);
    } catch (err) {
      input.json = err;
    }

    // Expand the document by default if its size isn't bigger than 100KB.
    if (!(input.json instanceof Error) && jsonString.length <= AUTO_EXPAND_MAX_SIZE) {
      input.expandedNodes = TreeViewClass.getExpandedNodes(
        input.json,
        {maxLevel: AUTO_EXPAND_MAX_LEVEL}
      );
    }
    return undefined;
  })();

  let theApp = render(MainTabbedArea(input), content);

  // Send readyState change notification event to the window. Can be useful for
  // tests as well as extensions.
  JSONView.readyState = "interactive";
  window.dispatchEvent(new CustomEvent("AppReadyStateChange"));

  promise.then(() => {
    // Another readyState change notification event.
    JSONView.readyState = "complete";
    window.dispatchEvent(new CustomEvent("AppReadyStateChange"));
  });
});
PK
!<�A��D�D?chrome/devtools/modules/devtools/client/jsonview/lib/require.js/** vim: et:ts=4:sw=4:sts=4
 * @license RequireJS 2.1.15 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved.
 * Available via the MIT or new BSD license.
 * see: http://github.com/jrburke/requirejs for details
 */
//Not using strict: uneven strict support in browsers, #392, and causes
//problems with requirejs.exec()/transpiler plugins that may not be strict.
/*jslint regexp: true, nomen: true, sloppy: true */
/*global window, navigator, document, importScripts, setTimeout, opera */

var requirejs, require, define;
(function (global) {
    var req, s, head, baseElement, dataMain, src,
        interactiveScript, currentlyAddingScript, mainScript, subPath,
        version = '2.1.15',
        commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,
        cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,
        jsSuffixRegExp = /\.js$/,
        currDirRegExp = /^\.\//,
        op = Object.prototype,
        ostring = op.toString,
        hasOwn = op.hasOwnProperty,
        ap = Array.prototype,
        apsp = ap.splice,
        isBrowser = !!(typeof window !== 'undefined' && typeof navigator !== 'undefined' && window.document),
        isWebWorker = !isBrowser && typeof importScripts !== 'undefined',
        //PS3 indicates loaded and complete, but need to wait for complete
        //specifically. Sequence is 'loading', 'loaded', execution,
        // then 'complete'. The UA check is unfortunate, but not sure how
        //to feature test w/o causing perf issues.
        readyRegExp = isBrowser && navigator.platform === 'PLAYSTATION 3' ?
                      /^complete$/ : /^(complete|loaded)$/,
        defContextName = '_',
        //Oh the tragedy, detecting opera. See the usage of isOpera for reason.
        isOpera = typeof opera !== 'undefined' && opera.toString() === '[object Opera]',
        contexts = {},
        cfg = {},
        globalDefQueue = [],
        useInteractive = false;

    function isFunction(it) {
        return ostring.call(it) === '[object Function]';
    }

    function isArray(it) {
        return ostring.call(it) === '[object Array]';
    }

    /**
     * Helper function for iterating over an array. If the func returns
     * a true value, it will break out of the loop.
     */
    function each(ary, func) {
        if (ary) {
            var i;
            for (i = 0; i < ary.length; i += 1) {
                if (ary[i] && func(ary[i], i, ary)) {
                    break;
                }
            }
        }
    }

    /**
     * Helper function for iterating over an array backwards. If the func
     * returns a true value, it will break out of the loop.
     */
    function eachReverse(ary, func) {
        if (ary) {
            var i;
            for (i = ary.length - 1; i > -1; i -= 1) {
                if (ary[i] && func(ary[i], i, ary)) {
                    break;
                }
            }
        }
    }

    function hasProp(obj, prop) {
        return hasOwn.call(obj, prop);
    }

    function getOwn(obj, prop) {
        return hasProp(obj, prop) && obj[prop];
    }

    /**
     * Cycles over properties in an object and calls a function for each
     * property value. If the function returns a truthy value, then the
     * iteration is stopped.
     */
    function eachProp(obj, func) {
        var prop;
        for (prop in obj) {
            if (hasProp(obj, prop)) {
                if (func(obj[prop], prop)) {
                    break;
                }
            }
        }
    }

    /**
     * Simple function to mix in properties from source into target,
     * but only if target does not already have a property of the same name.
     */
    function mixin(target, source, force, deepStringMixin) {
        if (source) {
            eachProp(source, function (value, prop) {
                if (force || !hasProp(target, prop)) {
                    if (deepStringMixin && typeof value === 'object' && value &&
                        !isArray(value) && !isFunction(value) &&
                        !(value instanceof RegExp)) {

                        if (!target[prop]) {
                            target[prop] = {};
                        }
                        mixin(target[prop], value, force, deepStringMixin);
                    } else {
                        target[prop] = value;
                    }
                }
            });
        }
        return target;
    }

    //Similar to Function.prototype.bind, but the 'this' object is specified
    //first, since it is easier to read/figure out what 'this' will be.
    function bind(obj, fn) {
        return function () {
            return fn.apply(obj, arguments);
        };
    }

    function scripts() {
        return document.getElementsByTagName('script');
    }

    function defaultOnError(err) {
        throw err;
    }

    //Allow getting a global that is expressed in
    //dot notation, like 'a.b.c'.
    function getGlobal(value) {
        if (!value) {
            return value;
        }
        var g = global;
        each(value.split('.'), function (part) {
            g = g[part];
        });
        return g;
    }

    /**
     * Constructs an error with a pointer to an URL with more information.
     * @param {String} id the error ID that maps to an ID on a web page.
     * @param {String} message human readable error.
     * @param {Error} [err] the original error, if there is one.
     *
     * @returns {Error}
     */
    function makeError(id, msg, err, requireModules) {
        var e = new Error(msg + '\nhttp://requirejs.org/docs/errors.html#' + id);
        e.requireType = id;
        e.requireModules = requireModules;
        if (err) {
            e.originalError = err;
        }
        return e;
    }

    if (typeof define !== 'undefined') {
        //If a define is already in play via another AMD loader,
        //do not overwrite.
        return;
    }

    if (typeof requirejs !== 'undefined') {
        if (isFunction(requirejs)) {
            //Do not overwrite an existing requirejs instance.
            return;
        }
        cfg = requirejs;
        requirejs = undefined;
    }

    //Allow for a require config object
    if (typeof require !== 'undefined' && !isFunction(require)) {
        //assume it is a config object.
        cfg = require;
        require = undefined;
    }

    function newContext(contextName) {
        var inCheckLoaded, Module, context, handlers,
            checkLoadedTimeoutId,
            config = {
                //Defaults. Do not set a default for map
                //config to speed up normalize(), which
                //will run faster if there is no default.
                waitSeconds: 7,
                baseUrl: './',
                paths: {},
                bundles: {},
                pkgs: {},
                shim: {},
                config: {}
            },
            registry = {},
            //registry of just enabled modules, to speed
            //cycle breaking code when lots of modules
            //are registered, but not activated.
            enabledRegistry = {},
            undefEvents = {},
            defQueue = [],
            defined = {},
            urlFetched = {},
            bundlesMap = {},
            requireCounter = 1,
            unnormalizedCounter = 1;

        /**
         * Trims the . and .. from an array of path segments.
         * It will keep a leading path segment if a .. will become
         * the first path segment, to help with module name lookups,
         * which act like paths, but can be remapped. But the end result,
         * all paths that use this function should look normalized.
         * NOTE: this method MODIFIES the input array.
         * @param {Array} ary the array of path segments.
         */
        function trimDots(ary) {
            var i, part;
            for (i = 0; i < ary.length; i++) {
                part = ary[i];
                if (part === '.') {
                    ary.splice(i, 1);
                    i -= 1;
                } else if (part === '..') {
                    // If at the start, or previous value is still ..,
                    // keep them so that when converted to a path it may
                    // still work when converted to a path, even though
                    // as an ID it is less than ideal. In larger point
                    // releases, may be better to just kick out an error.
                    if (i === 0 || (i == 1 && ary[2] === '..') || ary[i - 1] === '..') {
                        continue;
                    } else if (i > 0) {
                        ary.splice(i - 1, 2);
                        i -= 2;
                    }
                }
            }
        }

        /**
         * Given a relative module name, like ./something, normalize it to
         * a real name that can be mapped to a path.
         * @param {String} name the relative name
         * @param {String} baseName a real name that the name arg is relative
         * to.
         * @param {Boolean} applyMap apply the map config to the value. Should
         * only be done if this normalization is for a dependency ID.
         * @returns {String} normalized name
         */
        function normalize(name, baseName, applyMap) {
            var pkgMain, mapValue, nameParts, i, j, nameSegment, lastIndex,
                foundMap, foundI, foundStarMap, starI, normalizedBaseParts,
                baseParts = (baseName && baseName.split('/')),
                map = config.map,
                starMap = map && map['*'];

            //Adjust any relative paths.
            if (name) {
                name = name.split('/');
                lastIndex = name.length - 1;

                // If wanting node ID compatibility, strip .js from end
                // of IDs. Have to do this here, and not in nameToUrl
                // because node allows either .js or non .js to map
                // to same file.
                if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {
                    name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');
                }

                // Starts with a '.' so need the baseName
                if (name[0].charAt(0) === '.' && baseParts) {
                    //Convert baseName to array, and lop off the last part,
                    //so that . matches that 'directory' and not name of the baseName's
                    //module. For instance, baseName of 'one/two/three', maps to
                    //'one/two/three.js', but we want the directory, 'one/two' for
                    //this normalization.
                    normalizedBaseParts = baseParts.slice(0, baseParts.length - 1);
                    name = normalizedBaseParts.concat(name);
                }

                trimDots(name);
                name = name.join('/');
            }

            //Apply map config if available.
            if (applyMap && map && (baseParts || starMap)) {
                nameParts = name.split('/');

                outerLoop: for (i = nameParts.length; i > 0; i -= 1) {
                    nameSegment = nameParts.slice(0, i).join('/');

                    if (baseParts) {
                        //Find the longest baseName segment match in the config.
                        //So, do joins on the biggest to smallest lengths of baseParts.
                        for (j = baseParts.length; j > 0; j -= 1) {
                            mapValue = getOwn(map, baseParts.slice(0, j).join('/'));

                            //baseName segment has config, find if it has one for
                            //this name.
                            if (mapValue) {
                                mapValue = getOwn(mapValue, nameSegment);
                                if (mapValue) {
                                    //Match, update name to the new value.
                                    foundMap = mapValue;
                                    foundI = i;
                                    break outerLoop;
                                }
                            }
                        }
                    }

                    //Check for a star map match, but just hold on to it,
                    //if there is a shorter segment match later in a matching
                    //config, then favor over this star map.
                    if (!foundStarMap && starMap && getOwn(starMap, nameSegment)) {
                        foundStarMap = getOwn(starMap, nameSegment);
                        starI = i;
                    }
                }

                if (!foundMap && foundStarMap) {
                    foundMap = foundStarMap;
                    foundI = starI;
                }

                if (foundMap) {
                    nameParts.splice(0, foundI, foundMap);
                    name = nameParts.join('/');
                }
            }

            // If the name points to a package's name, use
            // the package main instead.
            pkgMain = getOwn(config.pkgs, name);

            return pkgMain ? pkgMain : name;
        }

        function removeScript(name) {
            if (isBrowser) {
                each(scripts(), function (scriptNode) {
                    if (scriptNode.getAttribute('data-requiremodule') === name &&
                            scriptNode.getAttribute('data-requirecontext') === context.contextName) {
                        scriptNode.parentNode.removeChild(scriptNode);
                        return true;
                    }
                });
            }
        }

        function hasPathFallback(id) {
            var pathConfig = getOwn(config.paths, id);
            if (pathConfig && isArray(pathConfig) && pathConfig.length > 1) {
                //Pop off the first array value, since it failed, and
                //retry
                pathConfig.shift();
                context.require.undef(id);

                //Custom require that does not do map translation, since
                //ID is "absolute", already mapped/resolved.
                context.makeRequire(null, {
                    skipMap: true
                })([id]);

                return true;
            }
        }

        //Turns a plugin!resource to [plugin, resource]
        //with the plugin being undefined if the name
        //did not have a plugin prefix.
        function splitPrefix(name) {
            var prefix,
                index = name ? name.indexOf('!') : -1;
            if (index > -1) {
                prefix = name.substring(0, index);
                name = name.substring(index + 1, name.length);
            }
            return [prefix, name];
        }

        /**
         * Creates a module mapping that includes plugin prefix, module
         * name, and path. If parentModuleMap is provided it will
         * also normalize the name via require.normalize()
         *
         * @param {String} name the module name
         * @param {String} [parentModuleMap] parent module map
         * for the module name, used to resolve relative names.
         * @param {Boolean} isNormalized: is the ID already normalized.
         * This is true if this call is done for a define() module ID.
         * @param {Boolean} applyMap: apply the map config to the ID.
         * Should only be true if this map is for a dependency.
         *
         * @returns {Object}
         */
        function makeModuleMap(name, parentModuleMap, isNormalized, applyMap) {
            var url, pluginModule, suffix, nameParts,
                prefix = null,
                parentName = parentModuleMap ? parentModuleMap.name : null,
                originalName = name,
                isDefine = true,
                normalizedName = '';

            //If no name, then it means it is a require call, generate an
            //internal name.
            if (!name) {
                isDefine = false;
                name = '_@r' + (requireCounter += 1);
            }

            nameParts = splitPrefix(name);
            prefix = nameParts[0];
            name = nameParts[1];

            if (prefix) {
                prefix = normalize(prefix, parentName, applyMap);
                pluginModule = getOwn(defined, prefix);
            }

            //Account for relative paths if there is a base name.
            if (name) {
                if (prefix) {
                    if (pluginModule && pluginModule.normalize) {
                        //Plugin is loaded, use its normalize method.
                        normalizedName = pluginModule.normalize(name, function (name) {
                            return normalize(name, parentName, applyMap);
                        });
                    } else {
                        // If nested plugin references, then do not try to
                        // normalize, as it will not normalize correctly. This
                        // places a restriction on resourceIds, and the longer
                        // term solution is not to normalize until plugins are
                        // loaded and all normalizations to allow for async
                        // loading of a loader plugin. But for now, fixes the
                        // common uses. Details in #1131
                        normalizedName = name.indexOf('!') === -1 ?
                                         normalize(name, parentName, applyMap) :
                                         name;
                    }
                } else {
                    //A regular module.
                    normalizedName = normalize(name, parentName, applyMap);

                    //Normalized name may be a plugin ID due to map config
                    //application in normalize. The map config values must
                    //already be normalized, so do not need to redo that part.
                    nameParts = splitPrefix(normalizedName);
                    prefix = nameParts[0];
                    normalizedName = nameParts[1];
                    isNormalized = true;

                    url = context.nameToUrl(normalizedName);
                }
            }

            //If the id is a plugin id that cannot be determined if it needs
            //normalization, stamp it with a unique ID so two matching relative
            //ids that may conflict can be separate.
            suffix = prefix && !pluginModule && !isNormalized ?
                     '_unnormalized' + (unnormalizedCounter += 1) :
                     '';

            return {
                prefix: prefix,
                name: normalizedName,
                parentMap: parentModuleMap,
                unnormalized: !!suffix,
                url: url,
                originalName: originalName,
                isDefine: isDefine,
                id: (prefix ?
                        prefix + '!' + normalizedName :
                        normalizedName) + suffix
            };
        }

        function getModule(depMap) {
            var id = depMap.id,
                mod = getOwn(registry, id);

            if (!mod) {
                mod = registry[id] = new context.Module(depMap);
            }

            return mod;
        }

        function on(depMap, name, fn) {
            var id = depMap.id,
                mod = getOwn(registry, id);

            if (hasProp(defined, id) &&
                    (!mod || mod.defineEmitComplete)) {
                if (name === 'defined') {
                    fn(defined[id]);
                }
            } else {
                mod = getModule(depMap);
                if (mod.error && name === 'error') {
                    fn(mod.error);
                } else {
                    mod.on(name, fn);
                }
            }
        }

        function onError(err, errback) {
            var ids = err.requireModules,
                notified = false;

            if (errback) {
                errback(err);
            } else {
                each(ids, function (id) {
                    var mod = getOwn(registry, id);
                    if (mod) {
                        //Set error on module, so it skips timeout checks.
                        mod.error = err;
                        if (mod.events.error) {
                            notified = true;
                            mod.emit('error', err);
                        }
                    }
                });

                if (!notified) {
                    req.onError(err);
                }
            }
        }

        /**
         * Internal method to transfer globalQueue items to this context's
         * defQueue.
         */
        function takeGlobalQueue() {
            //Push all the globalDefQueue items into the context's defQueue
            if (globalDefQueue.length) {
                //Array splice in the values since the context code has a
                //local var ref to defQueue, so cannot just reassign the one
                //on context.
                apsp.apply(defQueue,
                           [defQueue.length, 0].concat(globalDefQueue));
                globalDefQueue = [];
            }
        }

        handlers = {
            'require': function (mod) {
                if (mod.require) {
                    return mod.require;
                } else {
                    return (mod.require = context.makeRequire(mod.map));
                }
            },
            'exports': function (mod) {
                mod.usingExports = true;
                if (mod.map.isDefine) {
                    if (mod.exports) {
                        return (defined[mod.map.id] = mod.exports);
                    } else {
                        return (mod.exports = defined[mod.map.id] = {});
                    }
                }
            },
            'module': function (mod) {
                if (mod.module) {
                    return mod.module;
                } else {
                    return (mod.module = {
                        id: mod.map.id,
                        uri: mod.map.url,
                        config: function () {
                            return  getOwn(config.config, mod.map.id) || {};
                        },
                        exports: mod.exports || (mod.exports = {})
                    });
                }
            }
        };

        function cleanRegistry(id) {
            //Clean up machinery used for waiting modules.
            delete registry[id];
            delete enabledRegistry[id];
        }

        function breakCycle(mod, traced, processed) {
            var id = mod.map.id;

            if (mod.error) {
                mod.emit('error', mod.error);
            } else {
                traced[id] = true;
                each(mod.depMaps, function (depMap, i) {
                    var depId = depMap.id,
                        dep = getOwn(registry, depId);

                    //Only force things that have not completed
                    //being defined, so still in the registry,
                    //and only if it has not been matched up
                    //in the module already.
                    if (dep && !mod.depMatched[i] && !processed[depId]) {
                        if (getOwn(traced, depId)) {
                            mod.defineDep(i, defined[depId]);
                            mod.check(); //pass false?
                        } else {
                            breakCycle(dep, traced, processed);
                        }
                    }
                });
                processed[id] = true;
            }
        }

        function checkLoaded() {
            var err, usingPathFallback,
                waitInterval = config.waitSeconds * 1000,
                //It is possible to disable the wait interval by using waitSeconds of 0.
                expired = waitInterval && (context.startTime + waitInterval) < new Date().getTime(),
                noLoads = [],
                reqCalls = [],
                stillLoading = false,
                needCycleCheck = true;

            //Do not bother if this call was a result of a cycle break.
            if (inCheckLoaded) {
                return;
            }

            inCheckLoaded = true;

            //Figure out the state of all the modules.
            eachProp(enabledRegistry, function (mod) {
                var map = mod.map,
                    modId = map.id;

                //Skip things that are not enabled or in error state.
                if (!mod.enabled) {
                    return;
                }

                if (!map.isDefine) {
                    reqCalls.push(mod);
                }

                if (!mod.error) {
                    //If the module should be executed, and it has not
                    //been inited and time is up, remember it.
                    if (!mod.inited && expired) {
                        if (hasPathFallback(modId)) {
                            usingPathFallback = true;
                            stillLoading = true;
                        } else {
                            noLoads.push(modId);
                            removeScript(modId);
                        }
                    } else if (!mod.inited && mod.fetched && map.isDefine) {
                        stillLoading = true;
                        if (!map.prefix) {
                            //No reason to keep looking for unfinished
                            //loading. If the only stillLoading is a
                            //plugin resource though, keep going,
                            //because it may be that a plugin resource
                            //is waiting on a non-plugin cycle.
                            return (needCycleCheck = false);
                        }
                    }
                }
            });

            if (expired && noLoads.length) {
                //If wait time expired, throw error of unloaded modules.
                err = makeError('timeout', 'Load timeout for modules: ' + noLoads, null, noLoads);
                err.contextName = context.contextName;
                return onError(err);
            }

            //Not expired, check for a cycle.
            if (needCycleCheck) {
                each(reqCalls, function (mod) {
                    breakCycle(mod, {}, {});
                });
            }

            //If still waiting on loads, and the waiting load is something
            //other than a plugin resource, or there are still outstanding
            //scripts, then just try back later.
            if ((!expired || usingPathFallback) && stillLoading) {
                //Something is still waiting to load. Wait for it, but only
                //if a timeout is not already in effect.
                if ((isBrowser || isWebWorker) && !checkLoadedTimeoutId) {
                    checkLoadedTimeoutId = setTimeout(function () {
                        checkLoadedTimeoutId = 0;
                        checkLoaded();
                    }, 50);
                }
            }

            inCheckLoaded = false;
        }

        Module = function (map) {
            this.events = getOwn(undefEvents, map.id) || {};
            this.map = map;
            this.shim = getOwn(config.shim, map.id);
            this.depExports = [];
            this.depMaps = [];
            this.depMatched = [];
            this.pluginMaps = {};
            this.depCount = 0;

            /* this.exports this.factory
               this.depMaps = [],
               this.enabled, this.fetched
            */
        };

        Module.prototype = {
            init: function (depMaps, factory, errback, options) {
                options = options || {};

                //Do not do more inits if already done. Can happen if there
                //are multiple define calls for the same module. That is not
                //a normal, common case, but it is also not unexpected.
                if (this.inited) {
                    return;
                }

                this.factory = factory;

                if (errback) {
                    //Register for errors on this module.
                    this.on('error', errback);
                } else if (this.events.error) {
                    //If no errback already, but there are error listeners
                    //on this module, set up an errback to pass to the deps.
                    errback = bind(this, function (err) {
                        this.emit('error', err);
                    });
                }

                //Do a copy of the dependency array, so that
                //source inputs are not modified. For example
                //"shim" deps are passed in here directly, and
                //doing a direct modification of the depMaps array
                //would affect that config.
                this.depMaps = depMaps && depMaps.slice(0);

                this.errback = errback;

                //Indicate this module has be initialized
                this.inited = true;

                this.ignore = options.ignore;

                //Could have option to init this module in enabled mode,
                //or could have been previously marked as enabled. However,
                //the dependencies are not known until init is called. So
                //if enabled previously, now trigger dependencies as enabled.
                if (options.enabled || this.enabled) {
                    //Enable this module and dependencies.
                    //Will call this.check()
                    this.enable();
                } else {
                    this.check();
                }
            },

            defineDep: function (i, depExports) {
                //Because of cycles, defined callback for a given
                //export can be called more than once.
                if (!this.depMatched[i]) {
                    this.depMatched[i] = true;
                    this.depCount -= 1;
                    this.depExports[i] = depExports;
                }
            },

            fetch: function () {
                if (this.fetched) {
                    return;
                }
                this.fetched = true;

                context.startTime = (new Date()).getTime();

                var map = this.map;

                //If the manager is for a plugin managed resource,
                //ask the plugin to load it now.
                if (this.shim) {
                    context.makeRequire(this.map, {
                        enableBuildCallback: true
                    })(this.shim.deps || [], bind(this, function () {
                        return map.prefix ? this.callPlugin() : this.load();
                    }));
                } else {
                    //Regular dependency.
                    return map.prefix ? this.callPlugin() : this.load();
                }
            },

            load: function () {
                var url = this.map.url;

                //Regular dependency.
                if (!urlFetched[url]) {
                    urlFetched[url] = true;
                    context.load(this.map.id, url);
                }
            },

            /**
             * Checks if the module is ready to define itself, and if so,
             * define it.
             */
            check: function () {
                if (!this.enabled || this.enabling) {
                    return;
                }

                var err, cjsModule,
                    id = this.map.id,
                    depExports = this.depExports,
                    exports = this.exports,
                    factory = this.factory;

                if (!this.inited) {
                    this.fetch();
                } else if (this.error) {
                    this.emit('error', this.error);
                } else if (!this.defining) {
                    //The factory could trigger another require call
                    //that would result in checking this module to
                    //define itself again. If already in the process
                    //of doing that, skip this work.
                    this.defining = true;

                    if (this.depCount < 1 && !this.defined) {
                        if (isFunction(factory)) {
                            //If there is an error listener, favor passing
                            //to that instead of throwing an error. However,
                            //only do it for define()'d  modules. require
                            //errbacks should not be called for failures in
                            //their callbacks (#699). However if a global
                            //onError is set, use that.
                            if ((this.events.error && this.map.isDefine) ||
                                req.onError !== defaultOnError) {
                                try {
                                    exports = context.execCb(id, factory, depExports, exports);
                                } catch (e) {
                                    err = e;
                                }
                            } else {
                                exports = context.execCb(id, factory, depExports, exports);
                            }

                            // Favor return value over exports. If node/cjs in play,
                            // then will not have a return value anyway. Favor
                            // module.exports assignment over exports object.
                            if (this.map.isDefine && exports === undefined) {
                                cjsModule = this.module;
                                if (cjsModule) {
                                    exports = cjsModule.exports;
                                } else if (this.usingExports) {
                                    //exports already set the defined value.
                                    exports = this.exports;
                                }
                            }

                            if (err) {
                                err.requireMap = this.map;
                                err.requireModules = this.map.isDefine ? [this.map.id] : null;
                                err.requireType = this.map.isDefine ? 'define' : 'require';
                                return onError((this.error = err));
                            }

                        } else {
                            //Just a literal value
                            exports = factory;
                        }

                        this.exports = exports;

                        if (this.map.isDefine && !this.ignore) {
                            defined[id] = exports;

                            if (req.onResourceLoad) {
                                req.onResourceLoad(context, this.map, this.depMaps);
                            }
                        }

                        //Clean up
                        cleanRegistry(id);

                        this.defined = true;
                    }

                    //Finished the define stage. Allow calling check again
                    //to allow define notifications below in the case of a
                    //cycle.
                    this.defining = false;

                    if (this.defined && !this.defineEmitted) {
                        this.defineEmitted = true;
                        this.emit('defined', this.exports);
                        this.defineEmitComplete = true;
                    }

                }
            },

            callPlugin: function () {
                var map = this.map,
                    id = map.id,
                    //Map already normalized the prefix.
                    pluginMap = makeModuleMap(map.prefix);

                //Mark this as a dependency for this plugin, so it
                //can be traced for cycles.
                this.depMaps.push(pluginMap);

                on(pluginMap, 'defined', bind(this, function (plugin) {
                    var load, normalizedMap, normalizedMod,
                        bundleId = getOwn(bundlesMap, this.map.id),
                        name = this.map.name,
                        parentName = this.map.parentMap ? this.map.parentMap.name : null,
                        localRequire = context.makeRequire(map.parentMap, {
                            enableBuildCallback: true
                        });

                    //If current map is not normalized, wait for that
                    //normalized name to load instead of continuing.
                    if (this.map.unnormalized) {
                        //Normalize the ID if the plugin allows it.
                        if (plugin.normalize) {
                            name = plugin.normalize(name, function (name) {
                                return normalize(name, parentName, true);
                            }) || '';
                        }

                        //prefix and name should already be normalized, no need
                        //for applying map config again either.
                        normalizedMap = makeModuleMap(map.prefix + '!' + name,
                                                      this.map.parentMap);
                        on(normalizedMap,
                            'defined', bind(this, function (value) {
                                this.init([], function () { return value; }, null, {
                                    enabled: true,
                                    ignore: true
                                });
                            }));

                        normalizedMod = getOwn(registry, normalizedMap.id);
                        if (normalizedMod) {
                            //Mark this as a dependency for this plugin, so it
                            //can be traced for cycles.
                            this.depMaps.push(normalizedMap);

                            if (this.events.error) {
                                normalizedMod.on('error', bind(this, function (err) {
                                    this.emit('error', err);
                                }));
                            }
                            normalizedMod.enable();
                        }

                        return;
                    }

                    //If a paths config, then just load that file instead to
                    //resolve the plugin, as it is built into that paths layer.
                    if (bundleId) {
                        this.map.url = context.nameToUrl(bundleId);
                        this.load();
                        return;
                    }

                    load = bind(this, function (value) {
                        this.init([], function () { return value; }, null, {
                            enabled: true
                        });
                    });

                    load.error = bind(this, function (err) {
                        this.inited = true;
                        this.error = err;
                        err.requireModules = [id];

                        //Remove temp unnormalized modules for this module,
                        //since they will never be resolved otherwise now.
                        eachProp(registry, function (mod) {
                            if (mod.map.id.indexOf(id + '_unnormalized') === 0) {
                                cleanRegistry(mod.map.id);
                            }
                        });

                        onError(err);
                    });

                    //Allow plugins to load other code without having to know the
                    //context or how to 'complete' the load.
                    load.fromText = bind(this, function (text, textAlt) {
                        /*jslint evil: true */
                        var moduleName = map.name,
                            moduleMap = makeModuleMap(moduleName),
                            hasInteractive = useInteractive;

                        //As of 2.1.0, support just passing the text, to reinforce
                        //fromText only being called once per resource. Still
                        //support old style of passing moduleName but discard
                        //that moduleName in favor of the internal ref.
                        if (textAlt) {
                            text = textAlt;
                        }

                        //Turn off interactive script matching for IE for any define
                        //calls in the text, then turn it back on at the end.
                        if (hasInteractive) {
                            useInteractive = false;
                        }

                        //Prime the system by creating a module instance for
                        //it.
                        getModule(moduleMap);

                        //Transfer any config to this other module.
                        if (hasProp(config.config, id)) {
                            config.config[moduleName] = config.config[id];
                        }

                        try {
                            req.exec(text);
                        } catch (e) {
                            return onError(makeError('fromtexteval',
                                             'fromText eval for ' + id +
                                            ' failed: ' + e,
                                             e,
                                             [id]));
                        }

                        if (hasInteractive) {
                            useInteractive = true;
                        }

                        //Mark this as a dependency for the plugin
                        //resource
                        this.depMaps.push(moduleMap);

                        //Support anonymous modules.
                        context.completeLoad(moduleName);

                        //Bind the value of that module to the value for this
                        //resource ID.
                        localRequire([moduleName], load);
                    });

                    //Use parentName here since the plugin's name is not reliable,
                    //could be some weird string with no path that actually wants to
                    //reference the parentName's path.
                    plugin.load(map.name, localRequire, load, config);
                }));

                context.enable(pluginMap, this);
                this.pluginMaps[pluginMap.id] = pluginMap;
            },

            enable: function () {
                enabledRegistry[this.map.id] = this;
                this.enabled = true;

                //Set flag mentioning that the module is enabling,
                //so that immediate calls to the defined callbacks
                //for dependencies do not trigger inadvertent load
                //with the depCount still being zero.
                this.enabling = true;

                //Enable each dependency
                each(this.depMaps, bind(this, function (depMap, i) {
                    var id, mod, handler;

                    if (typeof depMap === 'string') {
                        //Dependency needs to be converted to a depMap
                        //and wired up to this module.
                        depMap = makeModuleMap(depMap,
                                               (this.map.isDefine ? this.map : this.map.parentMap),
                                               false,
                                               !this.skipMap);
                        this.depMaps[i] = depMap;

                        handler = getOwn(handlers, depMap.id);

                        if (handler) {
                            this.depExports[i] = handler(this);
                            return;
                        }

                        this.depCount += 1;

                        on(depMap, 'defined', bind(this, function (depExports) {
                            this.defineDep(i, depExports);
                            this.check();
                        }));

                        if (this.errback) {
                            on(depMap, 'error', bind(this, this.errback));
                        }
                    }

                    id = depMap.id;
                    mod = registry[id];

                    //Skip special modules like 'require', 'exports', 'module'
                    //Also, don't call enable if it is already enabled,
                    //important in circular dependency cases.
                    if (!hasProp(handlers, id) && mod && !mod.enabled) {
                        context.enable(depMap, this);
                    }
                }));

                //Enable each plugin that is used in
                //a dependency
                eachProp(this.pluginMaps, bind(this, function (pluginMap) {
                    var mod = getOwn(registry, pluginMap.id);
                    if (mod && !mod.enabled) {
                        context.enable(pluginMap, this);
                    }
                }));

                this.enabling = false;

                this.check();
            },

            on: function (name, cb) {
                var cbs = this.events[name];
                if (!cbs) {
                    cbs = this.events[name] = [];
                }
                cbs.push(cb);
            },

            emit: function (name, evt) {
                each(this.events[name], function (cb) {
                    cb(evt);
                });
                if (name === 'error') {
                    //Now that the error handler was triggered, remove
                    //the listeners, since this broken Module instance
                    //can stay around for a while in the registry.
                    delete this.events[name];
                }
            }
        };

        function callGetModule(args) {
            //Skip modules already defined.
            if (!hasProp(defined, args[0])) {
                getModule(makeModuleMap(args[0], null, true)).init(args[1], args[2]);
            }
        }

        function removeListener(node, func, name, ieName) {
            //Favor detachEvent because of IE9
            //issue, see attachEvent/addEventListener comment elsewhere
            //in this file.
            if (node.detachEvent && !isOpera) {
                //Probably IE. If not it will throw an error, which will be
                //useful to know.
                if (ieName) {
                    node.detachEvent(ieName, func);
                }
            } else {
                node.removeEventListener(name, func, false);
            }
        }

        /**
         * Given an event from a script node, get the requirejs info from it,
         * and then removes the event listeners on the node.
         * @param {Event} evt
         * @returns {Object}
         */
        function getScriptData(evt) {
            //Using currentTarget instead of target for Firefox 2.0's sake. Not
            //all old browsers will be supported, but this one was easy enough
            //to support and still makes sense.
            var node = evt.currentTarget || evt.srcElement;

            //Remove the listeners once here.
            removeListener(node, context.onScriptLoad, 'load', 'onreadystatechange');
            removeListener(node, context.onScriptError, 'error');

            return {
                node: node,
                id: node && node.getAttribute('data-requiremodule')
            };
        }

        function intakeDefines() {
            var args;

            //Any defined modules in the global queue, intake them now.
            takeGlobalQueue();

            //Make sure any remaining defQueue items get properly processed.
            while (defQueue.length) {
                args = defQueue.shift();
                if (args[0] === null) {
                    return onError(makeError('mismatch', 'Mismatched anonymous define() module: ' + args[args.length - 1]));
                } else {
                    //args are id, deps, factory. Should be normalized by the
                    //define() function.
                    callGetModule(args);
                }
            }
        }

        context = {
            config: config,
            contextName: contextName,
            registry: registry,
            defined: defined,
            urlFetched: urlFetched,
            defQueue: defQueue,
            Module: Module,
            makeModuleMap: makeModuleMap,
            nextTick: req.nextTick,
            onError: onError,

            /**
             * Set a configuration for the context.
             * @param {Object} cfg config object to integrate.
             */
            configure: function (cfg) {
                //Make sure the baseUrl ends in a slash.
                if (cfg.baseUrl) {
                    if (cfg.baseUrl.charAt(cfg.baseUrl.length - 1) !== '/') {
                        cfg.baseUrl += '/';
                    }
                }

                //Save off the paths since they require special processing,
                //they are additive.
                var shim = config.shim,
                    objs = {
                        paths: true,
                        bundles: true,
                        config: true,
                        map: true
                    };

                eachProp(cfg, function (value, prop) {
                    if (objs[prop]) {
                        if (!config[prop]) {
                            config[prop] = {};
                        }
                        mixin(config[prop], value, true, true);
                    } else {
                        config[prop] = value;
                    }
                });

                //Reverse map the bundles
                if (cfg.bundles) {
                    eachProp(cfg.bundles, function (value, prop) {
                        each(value, function (v) {
                            if (v !== prop) {
                                bundlesMap[v] = prop;
                            }
                        });
                    });
                }

                //Merge shim
                if (cfg.shim) {
                    eachProp(cfg.shim, function (value, id) {
                        //Normalize the structure
                        if (isArray(value)) {
                            value = {
                                deps: value
                            };
                        }
                        if ((value.exports || value.init) && !value.exportsFn) {
                            value.exportsFn = context.makeShimExports(value);
                        }
                        shim[id] = value;
                    });
                    config.shim = shim;
                }

                //Adjust packages if necessary.
                if (cfg.packages) {
                    each(cfg.packages, function (pkgObj) {
                        var location, name;

                        pkgObj = typeof pkgObj === 'string' ? { name: pkgObj } : pkgObj;

                        name = pkgObj.name;
                        location = pkgObj.location;
                        if (location) {
                            config.paths[name] = pkgObj.location;
                        }

                        //Save pointer to main module ID for pkg name.
                        //Remove leading dot in main, so main paths are normalized,
                        //and remove any trailing .js, since different package
                        //envs have different conventions: some use a module name,
                        //some use a file name.
                        config.pkgs[name] = pkgObj.name + '/' + (pkgObj.main || 'main')
                                     .replace(currDirRegExp, '')
                                     .replace(jsSuffixRegExp, '');
                    });
                }

                //If there are any "waiting to execute" modules in the registry,
                //update the maps for them, since their info, like URLs to load,
                //may have changed.
                eachProp(registry, function (mod, id) {
                    //If module already has init called, since it is too
                    //late to modify them, and ignore unnormalized ones
                    //since they are transient.
                    if (!mod.inited && !mod.map.unnormalized) {
                        mod.map = makeModuleMap(id);
                    }
                });

                //If a deps array or a config callback is specified, then call
                //require with those args. This is useful when require is defined as a
                //config object before require.js is loaded.
                if (cfg.deps || cfg.callback) {
                    context.require(cfg.deps || [], cfg.callback);
                }
            },

            makeShimExports: function (value) {
                function fn() {
                    var ret;
                    if (value.init) {
                        ret = value.init.apply(global, arguments);
                    }
                    return ret || (value.exports && getGlobal(value.exports));
                }
                return fn;
            },

            makeRequire: function (relMap, options) {
                options = options || {};

                function localRequire(deps, callback, errback) {
                    var id, map, requireMod;

                    if (options.enableBuildCallback && callback && isFunction(callback)) {
                        callback.__requireJsBuild = true;
                    }

                    if (typeof deps === 'string') {
                        if (isFunction(callback)) {
                            //Invalid call
                            return onError(makeError('requireargs', 'Invalid require call'), errback);
                        }

                        //If require|exports|module are requested, get the
                        //value for them from the special handlers. Caveat:
                        //this only works while module is being defined.
                        if (relMap && hasProp(handlers, deps)) {
                            return handlers[deps](registry[relMap.id]);
                        }

                        //Synchronous access to one module. If require.get is
                        //available (as in the Node adapter), prefer that.
                        if (req.get) {
                            return req.get(context, deps, relMap, localRequire);
                        }

                        //Normalize module name, if it contains . or ..
                        map = makeModuleMap(deps, relMap, false, true);
                        id = map.id;

                        if (!hasProp(defined, id)) {
                            return onError(makeError('notloaded', 'Module name "' +
                                        id +
                                        '" has not been loaded yet for context: ' +
                                        contextName +
                                        (relMap ? '' : '. Use require([])')));
                        }
                        return defined[id];
                    }

                    //Grab defines waiting in the global queue.
                    intakeDefines();

                    //Mark all the dependencies as needing to be loaded.
                    context.nextTick(function () {
                        //Some defines could have been added since the
                        //require call, collect them.
                        intakeDefines();

                        requireMod = getModule(makeModuleMap(null, relMap));

                        //Store if map config should be applied to this require
                        //call for dependencies.
                        requireMod.skipMap = options.skipMap;

                        requireMod.init(deps, callback, errback, {
                            enabled: true
                        });

                        checkLoaded();
                    });

                    return localRequire;
                }

                mixin(localRequire, {
                    isBrowser: isBrowser,

                    /**
                     * Converts a module name + .extension into an URL path.
                     * *Requires* the use of a module name. It does not support using
                     * plain URLs like nameToUrl.
                     */
                    toUrl: function (moduleNamePlusExt) {
                        var ext,
                            index = moduleNamePlusExt.lastIndexOf('.'),
                            segment = moduleNamePlusExt.split('/')[0],
                            isRelative = segment === '.' || segment === '..';

                        //Have a file extension alias, and it is not the
                        //dots from a relative path.
                        if (index !== -1 && (!isRelative || index > 1)) {
                            ext = moduleNamePlusExt.substring(index, moduleNamePlusExt.length);
                            moduleNamePlusExt = moduleNamePlusExt.substring(0, index);
                        }

                        return context.nameToUrl(normalize(moduleNamePlusExt,
                                                relMap && relMap.id, true), ext,  true);
                    },

                    defined: function (id) {
                        return hasProp(defined, makeModuleMap(id, relMap, false, true).id);
                    },

                    specified: function (id) {
                        id = makeModuleMap(id, relMap, false, true).id;
                        return hasProp(defined, id) || hasProp(registry, id);
                    }
                });

                //Only allow undef on top level require calls
                if (!relMap) {
                    localRequire.undef = function (id) {
                        //Bind any waiting define() calls to this context,
                        //fix for #408
                        takeGlobalQueue();

                        var map = makeModuleMap(id, relMap, true),
                            mod = getOwn(registry, id);

                        removeScript(id);

                        delete defined[id];
                        delete urlFetched[map.url];
                        delete undefEvents[id];

                        //Clean queued defines too. Go backwards
                        //in array so that the splices do not
                        //mess up the iteration.
                        eachReverse(defQueue, function(args, i) {
                            if(args[0] === id) {
                                defQueue.splice(i, 1);
                            }
                        });

                        if (mod) {
                            //Hold on to listeners in case the
                            //module will be attempted to be reloaded
                            //using a different config.
                            if (mod.events.defined) {
                                undefEvents[id] = mod.events;
                            }

                            cleanRegistry(id);
                        }
                    };
                }

                return localRequire;
            },

            /**
             * Called to enable a module if it is still in the registry
             * awaiting enablement. A second arg, parent, the parent module,
             * is passed in for context, when this method is overridden by
             * the optimizer. Not shown here to keep code compact.
             */
            enable: function (depMap) {
                var mod = getOwn(registry, depMap.id);
                if (mod) {
                    getModule(depMap).enable();
                }
            },

            /**
             * Internal method used by environment adapters to complete a load event.
             * A load event could be a script load or just a load pass from a synchronous
             * load call.
             * @param {String} moduleName the name of the module to potentially complete.
             */
            completeLoad: function (moduleName) {
                var found, args, mod,
                    shim = getOwn(config.shim, moduleName) || {},
                    shExports = shim.exports;

                takeGlobalQueue();

                while (defQueue.length) {
                    args = defQueue.shift();
                    if (args[0] === null) {
                        args[0] = moduleName;
                        //If already found an anonymous module and bound it
                        //to this name, then this is some other anon module
                        //waiting for its completeLoad to fire.
                        if (found) {
                            break;
                        }
                        found = true;
                    } else if (args[0] === moduleName) {
                        //Found matching define call for this script!
                        found = true;
                    }

                    callGetModule(args);
                }

                //Do this after the cycle of callGetModule in case the result
                //of those calls/init calls changes the registry.
                mod = getOwn(registry, moduleName);

                if (!found && !hasProp(defined, moduleName) && mod && !mod.inited) {
                    if (config.enforceDefine && (!shExports || !getGlobal(shExports))) {
                        if (hasPathFallback(moduleName)) {
                            return;
                        } else {
                            return onError(makeError('nodefine',
                                             'No define call for ' + moduleName,
                                             null,
                                             [moduleName]));
                        }
                    } else {
                        //A script that does not call define(), so just simulate
                        //the call for it.
                        callGetModule([moduleName, (shim.deps || []), shim.exportsFn]);
                    }
                }

                checkLoaded();
            },

            /**
             * Converts a module name to a file path. Supports cases where
             * moduleName may actually be just an URL.
             * Note that it **does not** call normalize on the moduleName,
             * it is assumed to have already been normalized. This is an
             * internal API, not a public one. Use toUrl for the public API.
             */
            nameToUrl: function (moduleName, ext, skipExt) {
                var paths, syms, i, parentModule, url,
                    parentPath, bundleId,
                    pkgMain = getOwn(config.pkgs, moduleName);

                if (pkgMain) {
                    moduleName = pkgMain;
                }

                bundleId = getOwn(bundlesMap, moduleName);

                if (bundleId) {
                    return context.nameToUrl(bundleId, ext, skipExt);
                }

                //If a colon is in the URL, it indicates a protocol is used and it is just
                //an URL to a file, or if it starts with a slash, contains a query arg (i.e. ?)
                //or ends with .js, then assume the user meant to use an url and not a module id.
                //The slash is important for protocol-less URLs as well as full paths.
                if (req.jsExtRegExp.test(moduleName)) {
                    //Just a plain path, not module name lookup, so just return it.
                    //Add extension if it is included. This is a bit wonky, only non-.js things pass
                    //an extension, this method probably needs to be reworked.
                    url = moduleName + (ext || '');
                } else {
                    //A module that needs to be converted to a path.
                    paths = config.paths;

                    syms = moduleName.split('/');
                    //For each module name segment, see if there is a path
                    //registered for it. Start with most specific name
                    //and work up from it.
                    for (i = syms.length; i > 0; i -= 1) {
                        parentModule = syms.slice(0, i).join('/');

                        parentPath = getOwn(paths, parentModule);
                        if (parentPath) {
                            //If an array, it means there are a few choices,
                            //Choose the one that is desired
                            if (isArray(parentPath)) {
                                parentPath = parentPath[0];
                            }
                            syms.splice(0, i, parentPath);
                            break;
                        }
                    }

                    //Join the path parts together, then figure out if baseUrl is needed.
                    url = syms.join('/');
                    url += (ext || (/^data\:|\?/.test(url) || skipExt ? '' : '.js'));
                    url = (url.charAt(0) === '/' || url.match(/^[\w\+\.\-]+:/) ? '' : config.baseUrl) + url;
                }

                return config.urlArgs ? url +
                                        ((url.indexOf('?') === -1 ? '?' : '&') +
                                         config.urlArgs) : url;
            },

            //Delegates to req.load. Broken out as a separate function to
            //allow overriding in the optimizer.
            load: function (id, url) {
                req.load(context, id, url);
            },

            /**
             * Executes a module callback function. Broken out as a separate function
             * solely to allow the build system to sequence the files in the built
             * layer in the right sequence.
             *
             * @private
             */
            execCb: function (name, callback, args, exports) {
                return callback.apply(exports, args);
            },

            /**
             * callback for script loads, used to check status of loading.
             *
             * @param {Event} evt the event from the browser for the script
             * that was loaded.
             */
            onScriptLoad: function (evt) {
                //Using currentTarget instead of target for Firefox 2.0's sake. Not
                //all old browsers will be supported, but this one was easy enough
                //to support and still makes sense.
                if (evt.type === 'load' ||
                        (readyRegExp.test((evt.currentTarget || evt.srcElement).readyState))) {
                    //Reset interactive script so a script node is not held onto for
                    //to long.
                    interactiveScript = null;

                    //Pull out the name of the module and the context.
                    var data = getScriptData(evt);
                    context.completeLoad(data.id);
                }
            },

            /**
             * Callback for script errors.
             */
            onScriptError: function (evt) {
                var data = getScriptData(evt);
                if (!hasPathFallback(data.id)) {
                    return onError(makeError('scripterror', 'Script error for: ' + data.id, evt, [data.id]));
                }
            }
        };

        context.require = context.makeRequire();
        return context;
    }

    /**
     * Main entry point.
     *
     * If the only argument to require is a string, then the module that
     * is represented by that string is fetched for the appropriate context.
     *
     * If the first argument is an array, then it will be treated as an array
     * of dependency string names to fetch. An optional function callback can
     * be specified to execute when all of those dependencies are available.
     *
     * Make a local req variable to help Caja compliance (it assumes things
     * on a require that are not standardized), and to give a short
     * name for minification/local scope use.
     */
    req = requirejs = function (deps, callback, errback, optional) {

        //Find the right context, use default
        var context, config,
            contextName = defContextName;

        // Determine if have config object in the call.
        if (!isArray(deps) && typeof deps !== 'string') {
            // deps is a config object
            config = deps;
            if (isArray(callback)) {
                // Adjust args if there are dependencies
                deps = callback;
                callback = errback;
                errback = optional;
            } else {
                deps = [];
            }
        }

        if (config && config.context) {
            contextName = config.context;
        }

        context = getOwn(contexts, contextName);
        if (!context) {
            context = contexts[contextName] = req.s.newContext(contextName);
        }

        if (config) {
            context.configure(config);
        }

        return context.require(deps, callback, errback);
    };

    /**
     * Support require.config() to make it easier to cooperate with other
     * AMD loaders on globally agreed names.
     */
    req.config = function (config) {
        return req(config);
    };

    /**
     * Execute something after the current tick
     * of the event loop. Override for other envs
     * that have a better solution than setTimeout.
     * @param  {Function} fn function to execute later.
     */
    req.nextTick = typeof setTimeout !== 'undefined' ? function (fn) {
        setTimeout(fn, 4);
    } : function (fn) { fn(); };

    /**
     * Export require as a global, but only if it does not already exist.
     */
    if (!require) {
        require = req;
    }

    req.version = version;

    //Used to filter out dependencies that are already paths.
    req.jsExtRegExp = /^\/|:|\?|\.js$/;
    req.isBrowser = isBrowser;
    s = req.s = {
        contexts: contexts,
        newContext: newContext
    };

    //Create default context.
    req({});

    //Exports some context-sensitive methods on global require.
    each([
        'toUrl',
        'undef',
        'defined',
        'specified'
    ], function (prop) {
        //Reference from contexts instead of early binding to default context,
        //so that during builds, the latest instance of the default context
        //with its config gets used.
        req[prop] = function () {
            var ctx = contexts[defContextName];
            return ctx.require[prop].apply(ctx, arguments);
        };
    });

    if (isBrowser) {
        head = s.head = document.getElementsByTagName('head')[0];
        //If BASE tag is in play, using appendChild is a problem for IE6.
        //When that browser dies, this can be removed. Details in this jQuery bug:
        //http://dev.jquery.com/ticket/2709
        baseElement = document.getElementsByTagName('base')[0];
        if (baseElement) {
            head = s.head = baseElement.parentNode;
        }
    }

    /**
     * Any errors that require explicitly generates will be passed to this
     * function. Intercept/override it if you want custom error handling.
     * @param {Error} err the error object.
     */
    req.onError = defaultOnError;

    /**
     * Creates the node for the load command. Only used in browser envs.
     */
    req.createNode = function (config, moduleName, url) {
        var node = config.xhtml ?
                document.createElementNS('http://www.w3.org/1999/xhtml', 'html:script') :
                document.createElement('script');
        node.type = config.scriptType || 'text/javascript';
        node.charset = 'utf-8';
        node.async = true;
        return node;
    };

    /**
     * Does the request to load a module for the browser case.
     * Make this a separate function to allow other environments
     * to override it.
     *
     * @param {Object} context the require context to find state.
     * @param {String} moduleName the name of the module.
     * @param {Object} url the URL to the module.
     */
    req.load = function (context, moduleName, url) {
        var config = (context && context.config) || {},
            node;
        if (isBrowser) {
            //In the browser so use a script tag
            node = req.createNode(config, moduleName, url);

            node.setAttribute('data-requirecontext', context.contextName);
            node.setAttribute('data-requiremodule', moduleName);

            //Set up load listener. Test attachEvent first because IE9 has
            //a subtle issue in its addEventListener and script onload firings
            //that do not match the behavior of all other browsers with
            //addEventListener support, which fire the onload event for a
            //script right after the script execution. See:
            //https://connect.microsoft.com/IE/feedback/details/648057/script-onload-event-is-not-fired-immediately-after-script-execution
            //UNFORTUNATELY Opera implements attachEvent but does not follow the script
            //script execution mode.
            if (node.attachEvent &&
                    //Check if node.attachEvent is artificially added by custom script or
                    //natively supported by browser
                    //read https://github.com/jrburke/requirejs/issues/187
                    //if we can NOT find [native code] then it must NOT natively supported.
                    //in IE8, node.attachEvent does not have toString()
                    //Note the test for "[native code" with no closing brace, see:
                    //https://github.com/jrburke/requirejs/issues/273
                    !(node.attachEvent.toString && node.attachEvent.toString().indexOf('[native code') < 0) &&
                    !isOpera) {
                //Probably IE. IE (at least 6-8) do not fire
                //script onload right after executing the script, so
                //we cannot tie the anonymous define call to a name.
                //However, IE reports the script as being in 'interactive'
                //readyState at the time of the define call.
                useInteractive = true;

                node.attachEvent('onreadystatechange', context.onScriptLoad);
                //It would be great to add an error handler here to catch
                //404s in IE9+. However, onreadystatechange will fire before
                //the error handler, so that does not help. If addEventListener
                //is used, then IE will fire error before load, but we cannot
                //use that pathway given the connect.microsoft.com issue
                //mentioned above about not doing the 'script execute,
                //then fire the script load event listener before execute
                //next script' that other browsers do.
                //Best hope: IE10 fixes the issues,
                //and then destroys all installs of IE 6-9.
                //node.attachEvent('onerror', context.onScriptError);
            } else {
                node.addEventListener('load', context.onScriptLoad, false);
                node.addEventListener('error', context.onScriptError, false);
            }
            node.src = url;

            //For some cache cases in IE 6-8, the script executes before the end
            //of the appendChild execution, so to tie an anonymous define
            //call to the module name (which is stored on the node), hold on
            //to a reference to this node, but clear after the DOM insertion.
            currentlyAddingScript = node;
            if (baseElement) {
                head.insertBefore(node, baseElement);
            } else {
                head.appendChild(node);
            }
            currentlyAddingScript = null;

            return node;
        } else if (isWebWorker) {
            try {
                //In a web worker, use importScripts. This is not a very
                //efficient use of importScripts, importScripts will block until
                //its script is downloaded and evaluated. However, if web workers
                //are in play, the expectation that a build has been done so that
                //only one script needs to be loaded anyway. This may need to be
                //reevaluated if other use cases become common.
                importScripts(url);

                //Account for anonymous modules
                context.completeLoad(moduleName);
            } catch (e) {
                context.onError(makeError('importscripts',
                                'importScripts failed for ' +
                                    moduleName + ' at ' + url,
                                e,
                                [moduleName]));
            }
        }
    };

    function getInteractiveScript() {
        if (interactiveScript && interactiveScript.readyState === 'interactive') {
            return interactiveScript;
        }

        eachReverse(scripts(), function (script) {
            if (script.readyState === 'interactive') {
                return (interactiveScript = script);
            }
        });
        return interactiveScript;
    }

    //Look for a data-main script attribute, which could also adjust the baseUrl.
    if (isBrowser && !cfg.skipDataMain) {
        //Figure out baseUrl. Get it from the script tag with require.js in it.
        eachReverse(scripts(), function (script) {
            //Set the 'head' where we can append children by
            //using the script's parent.
            if (!head) {
                head = script.parentNode;
            }

            //Look for a data-main attribute to set main script for the page
            //to load. If it is there, the path to data main becomes the
            //baseUrl, if it is not already set.
            dataMain = script.getAttribute('data-main');
            if (dataMain) {
                //Preserve dataMain in case it is a path (i.e. contains '?')
                mainScript = dataMain;

                //Set final baseUrl if there is not already an explicit one.
                if (!cfg.baseUrl) {
                    //Pull off the directory of data-main for use as the
                    //baseUrl.
                    src = mainScript.split('/');
                    mainScript = src.pop();
                    subPath = src.length ? src.join('/')  + '/' : './';

                    cfg.baseUrl = subPath;
                }

                //Strip off any trailing .js since mainScript is now
                //like a module name.
                mainScript = mainScript.replace(jsSuffixRegExp, '');

                 //If mainScript is still a path, fall back to dataMain
                if (req.jsExtRegExp.test(mainScript)) {
                    mainScript = dataMain;
                }

                //Put the data-main script in the files to load.
                cfg.deps = cfg.deps ? cfg.deps.concat(mainScript) : [mainScript];

                return true;
            }
        });
    }

    /**
     * The function that handles definitions of modules. Differs from
     * require() in that a string for the module should be the first argument,
     * and the function to execute after dependencies are loaded should
     * return a value to define the module corresponding to the first argument's
     * name.
     */
    define = function (name, deps, callback) {
        var node, context;

        //Allow for anonymous modules
        if (typeof name !== 'string') {
            //Adjust args appropriately
            callback = deps;
            deps = name;
            name = null;
        }

        //This module may not have dependencies
        if (!isArray(deps)) {
            callback = deps;
            deps = null;
        }

        //If no name, and callback is a function, then figure out if it a
        //CommonJS thing with dependencies.
        if (!deps && isFunction(callback)) {
            deps = [];
            //Remove comments from the callback string,
            //look for require calls, and pull them into the dependencies,
            //but only if there are function args.
            if (callback.length) {
                callback
                    .toString()
                    .replace(commentRegExp, '')
                    .replace(cjsRequireRegExp, function (match, dep) {
                        deps.push(dep);
                    });

                //May be a CommonJS thing even without require calls, but still
                //could use exports, and module. Avoid doing exports and module
                //work though if it just needs require.
                //REQUIRES the function to expect the CommonJS variables in the
                //order listed below.
                deps = (callback.length === 1 ? ['require'] : ['require', 'exports', 'module']).concat(deps);
            }
        }

        //If in IE 6-8 and hit an anonymous define() call, do the interactive
        //work.
        if (useInteractive) {
            node = currentlyAddingScript || getInteractiveScript();
            if (node) {
                if (!name) {
                    name = node.getAttribute('data-requiremodule');
                }
                context = contexts[node.getAttribute('data-requirecontext')];
            }
        }

        //Always save off evaluating the def call until the script onload handler.
        //This allows multiple modules to be in a file without prematurely
        //tracing dependencies, and allows for anonymous module support,
        //where the module name is not known until the script onload event
        //occurs. If no context, use the global queue, and get it processed
        //in the onscript load callback.
        (context ? context.defQueue : globalDefQueue).push([name, deps, callback]);
    };

    define.amd = {
        jQuery: true
    };


    /**
     * Executes the text. Normally just uses eval, but can be modified
     * to use a better, environment-specific call. Only used for transpiling
     * loader plugins, not for plain JS modules.
     * @param {String} text the text to execute/evaluate.
     */
    req.exec = function (text) {
        /*jslint evil: true */
        return eval(text);
    };

    //Set up with config info.
    req(cfg);
}(this));
PK
!<���SSAchrome/devtools/modules/devtools/client/jsonview/viewer-config.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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/. */
/* global requirejs */

"use strict";

// Send readyState change notification event to the window. It's useful for tests.
JSONView.readyState = "loading";
window.dispatchEvent(new CustomEvent("AppReadyStateChange"));

/**
 * RequireJS configuration for JSON Viewer.
 *
 * ReactJS library is shared among DevTools. The minified (production) version
 * of the library is always available, and is used by default.
 *
 * In order to use the developer version you need to specify the following
 * in your .mozconfig (see also bug 1181646):
 * ac_add_options --enable-debug-js-modules
 *
 * React module ID is using exactly the same (relative) path as the rest
 * of the code base, so it's consistent and modules can be easily reused.
 */
require.config({
  baseUrl: "resource://devtools-client-jsonview/",
  paths: {
    "devtools/client/shared": "resource://devtools-client-shared",
    "devtools/shared": "resource://devtools/shared",
    "devtools/client/shared/vendor/react":
      JSONView.debug
      ? "resource://devtools-client-shared/vendor/react-dev"
      : "resource://devtools-client-shared/vendor/react",
    "devtools/client/shared/vendor/react-dom":
      JSONView.debug
      ? "resource://devtools-client-shared/vendor/react-dom-dev"
      : "resource://devtools-client-shared/vendor/react-dom",
    "devtools/client/shared/vendor/react-prop-types":
      JSONView.debug
      ? "resource://devtools-client-shared/vendor/react-prop-types-dev"
      : "resource://devtools-client-shared/vendor/react-prop-types",
    "devtools/client/shared/vendor/react-dom-test-utils":
      JSONView.debug
      ? "resource://devtools-client-shared/vendor/react-dom-test-utils-dev"
      : "resource://devtools-client-shared/vendor/react-dom-test-utils",
  }
});

// Load the main panel module
requirejs(["json-viewer"]);
PK
!<w��]��Echrome/devtools/modules/devtools/client/memory/actions/allocations.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { actions, ALLOCATION_RECORDING_OPTIONS } = require("../constants");

exports.toggleRecordingAllocationStacks = function (front) {
  return function* (dispatch, getState) {
    dispatch({ type: actions.TOGGLE_RECORD_ALLOCATION_STACKS_START });

    if (getState().recordingAllocationStacks) {
      yield front.stopRecordingAllocations();
    } else {
      yield front.startRecordingAllocations(ALLOCATION_RECORDING_OPTIONS);
    }

    dispatch({ type: actions.TOGGLE_RECORD_ALLOCATION_STACKS_END });
  };
};
PK
!<(
�'<<Hchrome/devtools/modules/devtools/client/memory/actions/census-display.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { assert } = require("devtools/shared/DevToolsUtils");
const { actions } = require("../constants");
const { refresh } = require("./refresh");

exports.setCensusDisplayAndRefresh = function (heapWorker, display) {
  return function* (dispatch, getState) {
    dispatch(setCensusDisplay(display));
    yield dispatch(refresh(heapWorker));
  };
};

/**
 * Clears out all cached census data in the snapshots and sets new display data
 * for censuses.
 *
 * @param {censusDisplayModel} display
 */
const setCensusDisplay = exports.setCensusDisplay = function (display) {
  assert(typeof display === "object"
         && display
         && display.breakdown
         && display.breakdown.by,
    "Breakdowns must be an object with a \`by\` property, attempted to set: " +
  uneval(display));

  return {
    type: actions.SET_CENSUS_DISPLAY,
    display,
  };
};
PK
!<ߵ����Achrome/devtools/modules/devtools/client/memory/actions/diffing.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { assert, reportException } = require("devtools/shared/DevToolsUtils");
const { actions, diffingState, viewState } = require("../constants");
const {
  getSnapshot,
  censusIsUpToDate,
  snapshotIsDiffable,
  findSelectedSnapshot,
} = require("../utils");

/**
 * Toggle diffing mode on or off.
 */
exports.toggleDiffing = function () {
  return function (dispatch, getState) {
    dispatch({
      type: actions.CHANGE_VIEW,
      newViewState: getState().diffing ? viewState.CENSUS : viewState.DIFFING,
      oldDiffing: getState().diffing,
      oldSelected: findSelectedSnapshot(getState()),
    });
  };
};

/**
 * Select the given snapshot for diffing.
 *
 * @param {snapshotModel} snapshot
 */
const selectSnapshotForDiffing = exports.selectSnapshotForDiffing = function (snapshot) {
  assert(snapshotIsDiffable(snapshot),
         "To select a snapshot for diffing, it must be diffable");
  return { type: actions.SELECT_SNAPSHOT_FOR_DIFFING, snapshot };
};

/**
 * Compute the difference between the first and second snapshots.
 *
 * @param {HeapAnalysesClient} heapWorker
 * @param {snapshotModel} first
 * @param {snapshotModel} second
 */
const takeCensusDiff = exports.takeCensusDiff = function (heapWorker, first, second) {
  return function* (dispatch, getState) {
    assert(snapshotIsDiffable(first),
           `First snapshot must be in a diffable state, found ${first.state}`);
    assert(snapshotIsDiffable(second),
           `Second snapshot must be in a diffable state, found ${second.state}`);

    let report, parentMap;
    let display = getState().censusDisplay;
    let filter = getState().filter;

    if (censusIsUpToDate(filter, display, getState().diffing.census)) {
      return;
    }

    do {
      if (!getState().diffing
          || getState().diffing.firstSnapshotId !== first.id
          || getState().diffing.secondSnapshotId !== second.id) {
        // If we stopped diffing or stopped and then started diffing a different
        // pair of snapshots, then just give up with diffing this pair. In the
        // latter case, a newly spawned task will handle the diffing for the new
        // pair.
        return;
      }

      display = getState().censusDisplay;
      filter = getState().filter;

      dispatch({
        type: actions.TAKE_CENSUS_DIFF_START,
        first,
        second,
        filter,
        display,
      });

      let opts = display.inverted
        ? { asInvertedTreeNode: true }
        : { asTreeNode: true };
      opts.filter = filter || null;

      try {
        ({ delta: report, parentMap } = yield heapWorker.takeCensusDiff(
          first.path,
          second.path,
          { breakdown: display.breakdown },
          opts));
      } catch (error) {
        reportException("actions/diffing/takeCensusDiff", error);
        dispatch({ type: actions.DIFFING_ERROR, error });
        return;
      }
    }
    while (filter !== getState().filter
           || display !== getState().censusDisplay);

    dispatch({
      type: actions.TAKE_CENSUS_DIFF_END,
      first,
      second,
      report,
      parentMap,
      filter,
      display,
    });
  };
};

/**
 * Ensure that the current diffing data is up to date with the currently
 * selected display, filter, etc. If the state is not up-to-date, then a
 * recompute is triggered.
 *
 * @param {HeapAnalysesClient} heapWorker
 */
const refreshDiffing = exports.refreshDiffing = function (heapWorker) {
  return function* (dispatch, getState) {
    if (getState().diffing.secondSnapshotId === null) {
      return;
    }

    assert(getState().diffing.firstSnapshotId,
           "Should have first snapshot id");

    if (getState().diffing.state === diffingState.TAKING_DIFF) {
      // There is an existing task that will ensure that the diffing data is
      // up-to-date.
      return;
    }

    const { firstSnapshotId, secondSnapshotId } = getState().diffing;

    const first = getSnapshot(getState(), firstSnapshotId);
    const second = getSnapshot(getState(), secondSnapshotId);
    dispatch(takeCensusDiff(heapWorker, first, second));
  };
};

/**
 * Select the given snapshot for diffing and refresh the diffing data if
 * necessary (for example, if two snapshots are now selected for diffing).
 *
 * @param {HeapAnalysesClient} heapWorker
 * @param {snapshotModel} snapshot
 */
exports.selectSnapshotForDiffingAndRefresh = function (heapWorker, snapshot) {
  return function* (dispatch, getState) {
    assert(getState().diffing,
           "If we are selecting for diffing, we must be in diffing mode");
    dispatch(selectSnapshotForDiffing(snapshot));
    yield dispatch(refreshDiffing(heapWorker));
  };
};

/**
 * Expand the given node in the diffing's census's delta-report.
 *
 * @param {CensusTreeNode} node
 */
exports.expandDiffingCensusNode = function (node) {
  return {
    type: actions.EXPAND_DIFFING_CENSUS_NODE,
    node,
  };
};

/**
 * Collapse the given node in the diffing's census's delta-report.
 *
 * @param {CensusTreeNode} node
 */
exports.collapseDiffingCensusNode = function (node) {
  return {
    type: actions.COLLAPSE_DIFFING_CENSUS_NODE,
    node,
  };
};

/**
 * Focus the given node in the snapshot's census's report.
 *
 * @param {DominatorTreeNode} node
 */
exports.focusDiffingCensusNode = function (node) {
  return {
    type: actions.FOCUS_DIFFING_CENSUS_NODE,
    node,
  };
};
PK
!<�j�HUU@chrome/devtools/modules/devtools/client/memory/actions/filter.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { actions } = require("../constants");
const { refresh } = require("./refresh");

const setFilterString = exports.setFilterString = function (filterString) {
  return {
    type: actions.SET_FILTER_STRING,
    filter: filterString
  };
};

// The number of milliseconds we should wait before kicking off a new census
// when the filter string is updated. This helps us avoid doing any work while
// the user is still typing.
const FILTER_INPUT_DEBOUNCE_MS = 250;

// The timer id for the debounced census refresh.
let timerId = null;

exports.setFilterStringAndRefresh = function (filterString, heapWorker) {
  return function* (dispatch, getState) {
    dispatch(setFilterString(filterString));

    if (timerId !== null) {
      clearTimeout(timerId);
    }

    timerId = setTimeout(() => dispatch(refresh(heapWorker)),
                         FILTER_INPUT_DEBOUNCE_MS);
  };
};
PK
!<��yy<chrome/devtools/modules/devtools/client/memory/actions/io.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { immutableUpdate, reportException, assert } = require("devtools/shared/DevToolsUtils");
const { snapshotState: states, actions} = require("../constants");
const { L10N, openFilePicker, createSnapshot } = require("../utils");
const { OS } = require("resource://gre/modules/osfile.jsm");
const {
  selectSnapshot,
  computeSnapshotData,
  readSnapshot
} = require("./snapshot");
const VALID_EXPORT_STATES = [states.SAVED, states.READ];

exports.pickFileAndExportSnapshot = function (snapshot) {
  return function* (dispatch, getState) {
    let outputFile = yield openFilePicker({
      title: L10N.getFormatStr("snapshot.io.save.window"),
      defaultName: OS.Path.basename(snapshot.path),
      filters: [[L10N.getFormatStr("snapshot.io.filter"), "*.fxsnapshot"]],
      mode: "save",
    });

    if (!outputFile) {
      return;
    }

    yield dispatch(exportSnapshot(snapshot, outputFile.path));
  };
};

const exportSnapshot = exports.exportSnapshot = function (snapshot, dest) {
  return function* (dispatch, getState) {
    dispatch({ type: actions.EXPORT_SNAPSHOT_START, snapshot });

    assert(VALID_EXPORT_STATES.includes(snapshot.state),
      `Snapshot is in invalid state for exporting: ${snapshot.state}`);

    try {
      yield OS.File.copy(snapshot.path, dest);
    } catch (error) {
      reportException("exportSnapshot", error);
      dispatch({ type: actions.EXPORT_SNAPSHOT_ERROR, snapshot, error });
    }

    dispatch({ type: actions.EXPORT_SNAPSHOT_END, snapshot });
  };
};

exports.pickFileAndImportSnapshotAndCensus = function (heapWorker) {
  return function* (dispatch, getState) {
    let input = yield openFilePicker({
      title: L10N.getFormatStr("snapshot.io.import.window"),
      filters: [[L10N.getFormatStr("snapshot.io.filter"), "*.fxsnapshot"]],
      mode: "open",
    });

    if (!input) {
      return;
    }

    yield dispatch(importSnapshotAndCensus(heapWorker, input.path));
  };
};

const importSnapshotAndCensus = function (heapWorker, path) {
  return function* (dispatch, getState) {
    const snapshot = immutableUpdate(createSnapshot(getState()), {
      path,
      state: states.IMPORTING,
      imported: true,
    });
    const id = snapshot.id;

    dispatch({ type: actions.IMPORT_SNAPSHOT_START, snapshot });
    dispatch(selectSnapshot(snapshot.id));

    try {
      yield dispatch(readSnapshot(heapWorker, id));
      yield dispatch(computeSnapshotData(heapWorker, id));
    } catch (error) {
      reportException("importSnapshot", error);
      dispatch({ type: actions.IMPORT_SNAPSHOT_ERROR, error, id });
    }

    dispatch({ type: actions.IMPORT_SNAPSHOT_END, id });
  };
};
exports.importSnapshotAndCensus = importSnapshotAndCensus;
PK
!<�M��Gchrome/devtools/modules/devtools/client/memory/actions/label-display.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { assert } = require("devtools/shared/DevToolsUtils");
const { actions } = require("../constants");
const { refresh } = require("./refresh");

/**
 * Change the display we use for labeling individual nodes and refresh the
 * current data.
 */
exports.setLabelDisplayAndRefresh = function (heapWorker, display) {
  return function* (dispatch, getState) {
    // Clears out all stored census data and sets the display.
    dispatch(setLabelDisplay(display));
    yield dispatch(refresh(heapWorker));
  };
};

/**
 * Change the display we use for labeling individual nodes.
 *
 * @param {labelDisplayModel} display
 */
const setLabelDisplay = exports.setLabelDisplay = function (display) {
  assert(typeof display === "object"
         && display
         && display.breakdown
         && display.breakdown.by,
    "Breakdowns must be an object with a \`by\` property, attempted to set: " +
  uneval(display));

  return {
    type: actions.SET_LABEL_DISPLAY,
    display,
  };
};
PK
!<��e���Achrome/devtools/modules/devtools/client/memory/actions/refresh.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { assert } = require("devtools/shared/DevToolsUtils");
const { viewState } = require("../constants");
const { refreshDiffing } = require("./diffing");
const snapshot = require("./snapshot");

/**
 * Refresh the main thread's data from the heap analyses worker, if needed.
 *
 * @param {HeapAnalysesWorker} heapWorker
 */
exports.refresh = function (heapWorker) {
  return function* (dispatch, getState) {
    switch (getState().view.state) {
      case viewState.DIFFING:
        assert(getState().diffing, "Should have diffing state if in diffing view");
        yield dispatch(refreshDiffing(heapWorker));
        return;

      case viewState.CENSUS:
        yield dispatch(snapshot.refreshSelectedCensus(heapWorker));
        return;

      case viewState.DOMINATOR_TREE:
        yield dispatch(snapshot.refreshSelectedDominatorTree(heapWorker));
        return;

      case viewState.TREE_MAP:
        yield dispatch(snapshot.refreshSelectedTreeMap(heapWorker));
        return;

      case viewState.INDIVIDUALS:
        yield dispatch(snapshot.refreshIndividuals(heapWorker));
        return;

      default:
        assert(false, `Unexpected view state: ${getState().view.state}`);
    }
  };
};
PK
!<d5hb��?chrome/devtools/modules/devtools/client/memory/actions/sizes.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { actions } = require("../constants");

exports.resizeShortestPaths = function (newSize) {
  return {
    type: actions.RESIZE_SHORTEST_PATHS,
    size: newSize,
  };
};
PK
!<eS6WdWdBchrome/devtools/modules/devtools/client/memory/actions/snapshot.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Preferences } = require("resource://gre/modules/Preferences.jsm");
const { assert, reportException, isSet } = require("devtools/shared/DevToolsUtils");
const {
  censusIsUpToDate,
  getSnapshot,
  createSnapshot,
  dominatorTreeIsComputed,
} = require("../utils");
const {
  actions,
  snapshotState: states,
  viewState,
  censusState,
  treeMapState,
  dominatorTreeState,
  individualsState,
} = require("../constants");
const view = require("./view");
const refresh = require("./refresh");
const diffing = require("./diffing");
const TaskCache = require("./task-cache");

/**
 * A series of actions are fired from this task to save, read and generate the
 * initial census from a snapshot.
 *
 * @param {MemoryFront}
 * @param {HeapAnalysesClient}
 * @param {Object}
 */
exports.takeSnapshotAndCensus = function (front, heapWorker) {
  return function* (dispatch, getState) {
    const id = yield dispatch(takeSnapshot(front));
    if (id === null) {
      return;
    }

    yield dispatch(readSnapshot(heapWorker, id));
    if (getSnapshot(getState(), id).state !== states.READ) {
      return;
    }

    yield dispatch(computeSnapshotData(heapWorker, id));
  };
};

/**
 * Create the census for the snapshot with the provided snapshot id. If the
 * current view is the DOMINATOR_TREE view, create the dominator tree for this
 * snapshot as well.
 *
 * @param {HeapAnalysesClient} heapWorker
 * @param {snapshotId} id
 */
const computeSnapshotData = exports.computeSnapshotData = function (heapWorker, id) {
  return function* (dispatch, getState) {
    if (getSnapshot(getState(), id).state !== states.READ) {
      return;
    }

    // Decide which type of census to take.
    const censusTaker = getCurrentCensusTaker(getState().view.state);
    yield dispatch(censusTaker(heapWorker, id));

    if (getState().view.state === viewState.DOMINATOR_TREE &&
        !getSnapshot(getState(), id).dominatorTree) {
      yield dispatch(computeAndFetchDominatorTree(heapWorker, id));
    }
  };
};

/**
 * Selects a snapshot and if the snapshot's census is using a different
 * display, take a new census.
 *
 * @param {HeapAnalysesClient} heapWorker
 * @param {snapshotId} id
 */
exports.selectSnapshotAndRefresh = function (heapWorker, id) {
  return function* (dispatch, getState) {
    if (getState().diffing || getState().individuals) {
      dispatch(view.changeView(viewState.CENSUS));
    }

    dispatch(selectSnapshot(id));
    yield dispatch(refresh.refresh(heapWorker));
  };
};

/**
 * Take a snapshot and return its id on success, or null on failure.
 *
 * @param {MemoryFront} front
 * @returns {Number|null}
 */
const takeSnapshot = exports.takeSnapshot = function (front) {
  return function* (dispatch, getState) {
    if (getState().diffing || getState().individuals) {
      dispatch(view.changeView(viewState.CENSUS));
    }

    const snapshot = createSnapshot(getState());
    const id = snapshot.id;
    dispatch({ type: actions.TAKE_SNAPSHOT_START, snapshot });
    dispatch(selectSnapshot(id));

    let path;
    try {
      path = yield front.saveHeapSnapshot();
    } catch (error) {
      reportException("takeSnapshot", error);
      dispatch({ type: actions.SNAPSHOT_ERROR, id, error });
      return null;
    }

    dispatch({ type: actions.TAKE_SNAPSHOT_END, id, path });
    return snapshot.id;
  };
};

/**
 * Reads a snapshot into memory; necessary to do before taking
 * a census on the snapshot. May only be called once per snapshot.
 *
 * @param {HeapAnalysesClient} heapWorker
 * @param {snapshotId} id
 */
const readSnapshot = exports.readSnapshot =
TaskCache.declareCacheableTask({
  getCacheKey(_, id) {
    return id;
  },

  task: function* (heapWorker, id, removeFromCache, dispatch, getState) {
    const snapshot = getSnapshot(getState(), id);
    assert([states.SAVED, states.IMPORTING].includes(snapshot.state),
           `Should only read a snapshot once. Found snapshot in state ${snapshot.state}`);

    let creationTime;

    dispatch({ type: actions.READ_SNAPSHOT_START, id });
    try {
      yield heapWorker.readHeapSnapshot(snapshot.path);
      creationTime = yield heapWorker.getCreationTime(snapshot.path);
    } catch (error) {
      removeFromCache();
      reportException("readSnapshot", error);
      dispatch({ type: actions.SNAPSHOT_ERROR, id, error });
      return;
    }

    removeFromCache();
    dispatch({ type: actions.READ_SNAPSHOT_END, id, creationTime });
  }
});

let takeCensusTaskCounter = 0;

/**
 * Census and tree maps both require snapshots. This function shares the logic
 * of creating snapshots, but is configurable with specific actions for the
 * individual census types.
 *
 * @param {getDisplay} Get the display object from the state.
 * @param {getCensus} Get the census from the snapshot.
 * @param {beginAction} Action to send at the beginning of a heap snapshot.
 * @param {endAction} Action to send at the end of a heap snapshot.
 * @param {errorAction} Action to send if a snapshot has an error.
 */
function makeTakeCensusTask({ getDisplay, getFilter, getCensus, beginAction,
                              endAction, errorAction, canTakeCensus }) {
  /**
   * @param {HeapAnalysesClient} heapWorker
   * @param {snapshotId} id
   *
   * @see {Snapshot} model defined in devtools/client/memory/models.js
   * @see `devtools/shared/heapsnapshot/HeapAnalysesClient.js`
   * @see `js/src/doc/Debugger/Debugger.Memory.md` for breakdown details
   */
  let thisTakeCensusTaskId = ++takeCensusTaskCounter;
  return TaskCache.declareCacheableTask({
    getCacheKey(_, id) {
      return `take-census-task-${thisTakeCensusTaskId}-${id}`;
    },

    task: function* (heapWorker, id, removeFromCache, dispatch, getState) {
      const snapshot = getSnapshot(getState(), id);
      if (!snapshot) {
        removeFromCache();
        return;
      }

      // Assert that snapshot is in a valid state
      assert(canTakeCensus(snapshot),
        "Attempting to take a census when the snapshot is not in a ready state. " +
        `snapshot.state = ${snapshot.state}, ` +
        `census.state = ${(getCensus(snapshot) || { state: null }).state}`);

      let report, parentMap;
      let display = getDisplay(getState());
      let filter = getFilter(getState());

      // If display, filter and inversion haven't changed, don't do anything.
      if (censusIsUpToDate(filter, display, getCensus(snapshot))) {
        removeFromCache();
        return;
      }

      // Keep taking a census if the display changes while our request is in
      // flight. Recheck that the display used for the census is the same as the
      // state's display.
      do {
        display = getDisplay(getState());
        filter = getState().filter;

        dispatch({
          type: beginAction,
          id,
          filter,
          display
        });

        let opts = display.inverted
          ? { asInvertedTreeNode: true }
          : { asTreeNode: true };

        opts.filter = filter || null;

        try {
          ({ report, parentMap } = yield heapWorker.takeCensus(
            snapshot.path,
            { breakdown: display.breakdown },
            opts));
        } catch (error) {
          removeFromCache();
          reportException("takeCensus", error);
          dispatch({ type: errorAction, id, error });
          return;
        }
      }
      while (filter !== getState().filter ||
             display !== getDisplay(getState()));

      removeFromCache();
      dispatch({
        type: endAction,
        id,
        display,
        filter,
        report,
        parentMap
      });
    }
  });
}

/**
 * Take a census.
 */
const takeCensus = exports.takeCensus = makeTakeCensusTask({
  getDisplay: (state) => state.censusDisplay,
  getFilter: (state) => state.filter,
  getCensus: (snapshot) => snapshot.census,
  beginAction: actions.TAKE_CENSUS_START,
  endAction: actions.TAKE_CENSUS_END,
  errorAction: actions.TAKE_CENSUS_ERROR,
  canTakeCensus: snapshot =>
    snapshot.state === states.READ &&
    (!snapshot.census || snapshot.census.state === censusState.SAVED),
});

/**
 * Take a census for the treemap.
 */
const takeTreeMap = exports.takeTreeMap = makeTakeCensusTask({
  getDisplay: (state) => state.treeMapDisplay,
  getFilter: () => null,
  getCensus: (snapshot) => snapshot.treeMap,
  beginAction: actions.TAKE_TREE_MAP_START,
  endAction: actions.TAKE_TREE_MAP_END,
  errorAction: actions.TAKE_TREE_MAP_ERROR,
  canTakeCensus: snapshot =>
    snapshot.state === states.READ &&
    (!snapshot.treeMap || snapshot.treeMap.state === treeMapState.SAVED),
});

/**
 * Define what should be the default mode for taking a census based on the
 * default view of the tool.
 */
const defaultCensusTaker = takeTreeMap;

/**
 * Pick the default census taker when taking a snapshot. This should be
 * determined by the current view. If the view doesn't include a census, then
 * use the default one defined above. Some census information is always needed
 * to display some basic information about a snapshot.
 *
 * @param {string} value from viewState
 */
const getCurrentCensusTaker = exports.getCurrentCensusTaker = function (currentView) {
  switch (currentView) {
    case viewState.TREE_MAP:
      return takeTreeMap;
    case viewState.CENSUS:
      return takeCensus;
    default:
      return defaultCensusTaker;
  }
};

/**
 * Focus the given node in the individuals view.
 *
 * @param {DominatorTreeNode} node.
 */
exports.focusIndividual = function (node) {
  return {
    type: actions.FOCUS_INDIVIDUAL,
    node,
  };
};

/**
 * Fetch the individual `DominatorTreeNodes` for the census group specified by
 * `censusBreakdown` and `reportLeafIndex`.
 *
 * @param {HeapAnalysesClient} heapWorker
 * @param {SnapshotId} id
 * @param {Object} censusBreakdown
 * @param {Set<Number> | Number} reportLeafIndex
 */
const fetchIndividuals = exports.fetchIndividuals =
function (heapWorker, id, censusBreakdown, reportLeafIndex) {
  return function* (dispatch, getState) {
    if (getState().view.state !== viewState.INDIVIDUALS) {
      dispatch(view.changeView(viewState.INDIVIDUALS));
    }

    const snapshot = getSnapshot(getState(), id);
    assert(snapshot && snapshot.state === states.READ,
           "The snapshot should already be read into memory");

    if (!dominatorTreeIsComputed(snapshot)) {
      yield dispatch(computeAndFetchDominatorTree(heapWorker, id));
    }

    const snapshot_ = getSnapshot(getState(), id);
    assert(snapshot_.dominatorTree && snapshot_.dominatorTree.root,
           "Should have a dominator tree with a root.");

    const dominatorTreeId = snapshot_.dominatorTree.dominatorTreeId;

    const indices = isSet(reportLeafIndex)
      ? reportLeafIndex
      : new Set([reportLeafIndex]);

    let labelDisplay;
    let nodes;
    do {
      labelDisplay = getState().labelDisplay;
      assert(labelDisplay && labelDisplay.breakdown && labelDisplay.breakdown.by,
             `Should have a breakdown to label nodes with, got: ${uneval(labelDisplay)}`);

      if (getState().view.state !== viewState.INDIVIDUALS) {
        // We switched views while in the process of fetching individuals -- any
        // further work is useless.
        return;
      }

      dispatch({ type: actions.FETCH_INDIVIDUALS_START });

      try {
        ({ nodes } = yield heapWorker.getCensusIndividuals({
          dominatorTreeId,
          indices,
          censusBreakdown,
          labelBreakdown: labelDisplay.breakdown,
          maxRetainingPaths: Preferences.get("devtools.memory.max-retaining-paths"),
          maxIndividuals: Preferences.get("devtools.memory.max-individuals"),
        }));
      } catch (error) {
        reportException("actions/snapshot/fetchIndividuals", error);
        dispatch({ type: actions.INDIVIDUALS_ERROR, error });
        return;
      }
    }
    while (labelDisplay !== getState().labelDisplay);

    dispatch({
      type: actions.FETCH_INDIVIDUALS_END,
      id,
      censusBreakdown,
      indices,
      labelDisplay,
      nodes,
      dominatorTree: snapshot_.dominatorTree,
    });
  };
};

/**
 * Refresh the current individuals view.
 *
 * @param {HeapAnalysesClient} heapWorker
 */
exports.refreshIndividuals = function (heapWorker) {
  return function* (dispatch, getState) {
    assert(getState().view.state === viewState.INDIVIDUALS,
           "Should be in INDIVIDUALS view.");

    const { individuals } = getState();

    switch (individuals.state) {
      case individualsState.COMPUTING_DOMINATOR_TREE:
      case individualsState.FETCHING:
        // Nothing to do here.
        return;

      case individualsState.FETCHED:
        if (getState().individuals.labelDisplay === getState().labelDisplay) {
          return;
        }
        break;

      case individualsState.ERROR:
        // Doesn't hurt to retry: maybe we won't get an error this time around?
        break;

      default:
        assert(false, `Unexpected individuals state: ${individuals.state}`);
        return;
    }

    yield dispatch(fetchIndividuals(heapWorker,
                                    individuals.id,
                                    individuals.censusBreakdown,
                                    individuals.indices));
  };
};

/**
 * Refresh the selected snapshot's census data, if need be (for example,
 * display configuration changed).
 *
 * @param {HeapAnalysesClient} heapWorker
 */
exports.refreshSelectedCensus = function (heapWorker) {
  return function* (dispatch, getState) {
    let snapshot = getState().snapshots.find(s => s.selected);
    if (!snapshot || snapshot.state !== states.READ) {
      return;
    }

    // Intermediate snapshot states will get handled by the task action that is
    // orchestrating them. For example, if the snapshot census's state is
    // SAVING, then the takeCensus action will keep taking a census until
    // the inverted property matches the inverted state. If the snapshot is
    // still in the process of being saved or read, the takeSnapshotAndCensus
    // task action will follow through and ensure that a census is taken.
    if ((snapshot.census && snapshot.census.state === censusState.SAVED) ||
        !snapshot.census) {
      yield dispatch(takeCensus(heapWorker, snapshot.id));
    }
  };
};

/**
 * Refresh the selected snapshot's tree map data, if need be (for example,
 * display configuration changed).
 *
 * @param {HeapAnalysesClient} heapWorker
 */
exports.refreshSelectedTreeMap = function (heapWorker) {
  return function* (dispatch, getState) {
    let snapshot = getState().snapshots.find(s => s.selected);
    if (!snapshot || snapshot.state !== states.READ) {
      return;
    }

    // Intermediate snapshot states will get handled by the task action that is
    // orchestrating them. For example, if the snapshot census's state is
    // SAVING, then the takeCensus action will keep taking a census until
    // the inverted property matches the inverted state. If the snapshot is
    // still in the process of being saved or read, the takeSnapshotAndCensus
    // task action will follow through and ensure that a census is taken.
    if ((snapshot.treeMap && snapshot.treeMap.state === treeMapState.SAVED) ||
        !snapshot.treeMap) {
      yield dispatch(takeTreeMap(heapWorker, snapshot.id));
    }
  };
};

/**
 * Request that the `HeapAnalysesWorker` compute the dominator tree for the
 * snapshot with the given `id`.
 *
 * @param {HeapAnalysesClient} heapWorker
 * @param {SnapshotId} id
 *
 * @returns {Promise<DominatorTreeId>}
 */
const computeDominatorTree = exports.computeDominatorTree =
TaskCache.declareCacheableTask({
  getCacheKey(_, id) {
    return id;
  },

  task: function* (heapWorker, id, removeFromCache, dispatch, getState) {
    const snapshot = getSnapshot(getState(), id);
    assert(!(snapshot.dominatorTree && snapshot.dominatorTree.dominatorTreeId),
           "Should not re-compute dominator trees");

    dispatch({ type: actions.COMPUTE_DOMINATOR_TREE_START, id });

    let dominatorTreeId;
    try {
      dominatorTreeId = yield heapWorker.computeDominatorTree(snapshot.path);
    } catch (error) {
      removeFromCache();
      reportException("actions/snapshot/computeDominatorTree", error);
      dispatch({ type: actions.DOMINATOR_TREE_ERROR, id, error });
      return null;
    }

    removeFromCache();
    dispatch({ type: actions.COMPUTE_DOMINATOR_TREE_END, id, dominatorTreeId });
    return dominatorTreeId;
  }
});

/**
 * Get the partial subtree, starting from the root, of the
 * snapshot-with-the-given-id's dominator tree.
 *
 * @param {HeapAnalysesClient} heapWorker
 * @param {SnapshotId} id
 *
 * @returns {Promise<DominatorTreeNode>}
 */
const fetchDominatorTree = exports.fetchDominatorTree =
TaskCache.declareCacheableTask({
  getCacheKey(_, id) {
    return id;
  },

  task: function* (heapWorker, id, removeFromCache, dispatch, getState) {
    const snapshot = getSnapshot(getState(), id);
    assert(dominatorTreeIsComputed(snapshot),
           "Should have dominator tree model and it should be computed");

    let display;
    let root;
    do {
      display = getState().labelDisplay;
      assert(display && display.breakdown,
             `Should have a breakdown to describe nodes with, got: ${uneval(display)}`);

      dispatch({ type: actions.FETCH_DOMINATOR_TREE_START, id, display });

      try {
        root = yield heapWorker.getDominatorTree({
          dominatorTreeId: snapshot.dominatorTree.dominatorTreeId,
          breakdown: display.breakdown,
          maxRetainingPaths: Preferences.get("devtools.memory.max-retaining-paths"),
        });
      } catch (error) {
        removeFromCache();
        reportException("actions/snapshot/fetchDominatorTree", error);
        dispatch({ type: actions.DOMINATOR_TREE_ERROR, id, error });
        return null;
      }
    }
    while (display !== getState().labelDisplay);

    removeFromCache();
    dispatch({ type: actions.FETCH_DOMINATOR_TREE_END, id, root });
    return root;
  }
});

/**
 * Fetch the immediately dominated children represented by the placeholder
 * `lazyChildren` from snapshot-with-the-given-id's dominator tree.
 *
 * @param {HeapAnalysesClient} heapWorker
 * @param {SnapshotId} id
 * @param {DominatorTreeLazyChildren} lazyChildren
 */
exports.fetchImmediatelyDominated = TaskCache.declareCacheableTask({
  getCacheKey(_, id, lazyChildren) {
    return `${id}-${lazyChildren.key()}`;
  },

  task: function* (heapWorker, id, lazyChildren, removeFromCache, dispatch, getState) {
    const snapshot = getSnapshot(getState(), id);
    assert(snapshot.dominatorTree, "Should have dominator tree model");
    assert(snapshot.dominatorTree.state === dominatorTreeState.LOADED ||
           snapshot.dominatorTree.state === dominatorTreeState.INCREMENTAL_FETCHING,
           "Cannot fetch immediately dominated nodes in a dominator tree unless " +
           " the dominator tree has already been computed");

    let display;
    let response;
    do {
      display = getState().labelDisplay;
      assert(display, "Should have a display to describe nodes with.");

      dispatch({ type: actions.FETCH_IMMEDIATELY_DOMINATED_START, id });

      try {
        response = yield heapWorker.getImmediatelyDominated({
          dominatorTreeId: snapshot.dominatorTree.dominatorTreeId,
          breakdown: display.breakdown,
          nodeId: lazyChildren.parentNodeId(),
          startIndex: lazyChildren.siblingIndex(),
          maxRetainingPaths: Preferences.get("devtools.memory.max-retaining-paths"),
        });
      } catch (error) {
        removeFromCache();
        reportException("actions/snapshot/fetchImmediatelyDominated", error);
        dispatch({ type: actions.DOMINATOR_TREE_ERROR, id, error });
        return;
      }
    }
    while (display !== getState().labelDisplay);

    removeFromCache();
    dispatch({
      type: actions.FETCH_IMMEDIATELY_DOMINATED_END,
      id,
      path: response.path,
      nodes: response.nodes,
      moreChildrenAvailable: response.moreChildrenAvailable,
    });
  }
});

/**
 * Compute and then fetch the dominator tree of the snapshot with the given
 * `id`.
 *
 * @param {HeapAnalysesClient} heapWorker
 * @param {SnapshotId} id
 *
 * @returns {Promise<DominatorTreeNode>}
 */
const computeAndFetchDominatorTree = exports.computeAndFetchDominatorTree =
TaskCache.declareCacheableTask({
  getCacheKey(_, id) {
    return id;
  },

  task: function* (heapWorker, id, removeFromCache, dispatch, getState) {
    const dominatorTreeId = yield dispatch(computeDominatorTree(heapWorker, id));
    if (dominatorTreeId === null) {
      removeFromCache();
      return null;
    }

    const root = yield dispatch(fetchDominatorTree(heapWorker, id));
    removeFromCache();

    if (!root) {
      return null;
    }

    return root;
  }
});

/**
 * Update the currently selected snapshot's dominator tree.
 *
 * @param {HeapAnalysesClient} heapWorker
 */
exports.refreshSelectedDominatorTree = function (heapWorker) {
  return function* (dispatch, getState) {
    let snapshot = getState().snapshots.find(s => s.selected);
    if (!snapshot) {
      return;
    }

    if (snapshot.dominatorTree &&
        !(snapshot.dominatorTree.state === dominatorTreeState.COMPUTED ||
          snapshot.dominatorTree.state === dominatorTreeState.LOADED ||
          snapshot.dominatorTree.state === dominatorTreeState.INCREMENTAL_FETCHING)) {
      return;
    }

    // We need to check for the snapshot state because if there was an error,
    // we can't continue and if we are still saving or reading the snapshot,
    // then takeSnapshotAndCensus will finish the job for us
    if (snapshot.state === states.READ) {
      if (snapshot.dominatorTree) {
        yield dispatch(fetchDominatorTree(heapWorker, snapshot.id));
      } else {
        yield dispatch(computeAndFetchDominatorTree(heapWorker, snapshot.id));
      }
    }
  };
};

/**
 * Select the snapshot with the given id.
 *
 * @param {snapshotId} id
 * @see {Snapshot} model defined in devtools/client/memory/models.js
 */
const selectSnapshot = exports.selectSnapshot = function (id) {
  return {
    type: actions.SELECT_SNAPSHOT,
    id
  };
};

/**
 * Delete all snapshots that are in the READ or ERROR state
 *
 * @param {HeapAnalysesClient} heapWorker
 */
exports.clearSnapshots = function (heapWorker) {
  return function* (dispatch, getState) {
    let snapshots = getState().snapshots.filter(s => {
      let snapshotReady = s.state === states.READ || s.state === states.ERROR;
      let censusReady = (s.treeMap && s.treeMap.state === treeMapState.SAVED) ||
                        (s.census && s.census.state === censusState.SAVED);

      return snapshotReady && censusReady;
    });

    let ids = snapshots.map(s => s.id);

    dispatch({ type: actions.DELETE_SNAPSHOTS_START, ids });

    if (getState().diffing) {
      dispatch(diffing.toggleDiffing());
    }
    if (getState().individuals) {
      dispatch(view.popView());
    }

    yield Promise.all(snapshots.map(snapshot => {
      return heapWorker.deleteHeapSnapshot(snapshot.path).catch(error => {
        reportException("clearSnapshots", error);
        dispatch({ type: actions.SNAPSHOT_ERROR, id: snapshot.id, error });
      });
    }));

    dispatch({ type: actions.DELETE_SNAPSHOTS_END, ids });
  };
};

/**
 * Delete a snapshot
 *
 * @param {HeapAnalysesClient} heapWorker
 * @param {snapshotModel} snapshot
 */
exports.deleteSnapshot = function (heapWorker, snapshot) {
  return function* (dispatch, getState) {
    dispatch({ type: actions.DELETE_SNAPSHOTS_START, ids: [snapshot.id] });

    try {
      yield heapWorker.deleteHeapSnapshot(snapshot.path);
    } catch (error) {
      reportException("deleteSnapshot", error);
      dispatch({ type: actions.SNAPSHOT_ERROR, id: snapshot.id, error });
    }

    dispatch({ type: actions.DELETE_SNAPSHOTS_END, ids: [snapshot.id] });
  };
};

/**
 * Expand the given node in the snapshot's census report.
 *
 * @param {CensusTreeNode} node
 */
exports.expandCensusNode = function (id, node) {
  return {
    type: actions.EXPAND_CENSUS_NODE,
    id,
    node,
  };
};

/**
 * Collapse the given node in the snapshot's census report.
 *
 * @param {CensusTreeNode} node
 */
exports.collapseCensusNode = function (id, node) {
  return {
    type: actions.COLLAPSE_CENSUS_NODE,
    id,
    node,
  };
};

/**
 * Focus the given node in the snapshot's census's report.
 *
 * @param {SnapshotId} id
 * @param {DominatorTreeNode} node
 */
exports.focusCensusNode = function (id, node) {
  return {
    type: actions.FOCUS_CENSUS_NODE,
    id,
    node,
  };
};

/**
 * Expand the given node in the snapshot's dominator tree.
 *
 * @param {DominatorTreeTreeNode} node
 */
exports.expandDominatorTreeNode = function (id, node) {
  return {
    type: actions.EXPAND_DOMINATOR_TREE_NODE,
    id,
    node,
  };
};

/**
 * Collapse the given node in the snapshot's dominator tree.
 *
 * @param {DominatorTreeTreeNode} node
 */
exports.collapseDominatorTreeNode = function (id, node) {
  return {
    type: actions.COLLAPSE_DOMINATOR_TREE_NODE,
    id,
    node,
  };
};

/**
 * Focus the given node in the snapshot's dominator tree.
 *
 * @param {SnapshotId} id
 * @param {DominatorTreeNode} node
 */
exports.focusDominatorTreeNode = function (id, node) {
  return {
    type: actions.FOCUS_DOMINATOR_TREE_NODE,
    id,
    node,
  };
};
PK
!<<�́%
%
Dchrome/devtools/modules/devtools/client/memory/actions/task-cache.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { assert } = require("devtools/shared/DevToolsUtils");

/**
 * The `TaskCache` allows for re-using active tasks when spawning a second task
 * would simply duplicate work and is unnecessary. It maps from a task's unique
 * key to the promise of its result.
 */
const TaskCache = module.exports = class TaskCache {
  constructor() {
    this._cache = new Map();
  }

  /**
   * Get the promise keyed by the given unique `key`, if one exists.
   *
   * @param {Any} key
   * @returns {Promise<Any> | undefined}
   */
  get(key) {
    return this._cache.get(key);
  }

  /**
   * Put the task result promise in the cache and associate it with the given
   * `key` which must not already have an entry in the cache.
   *
   * @param {Any} key
   * @param {Promise<Any>} promise
   */
  put(key, promise) {
    assert(!this._cache.has(key),
           "We should not override extant entries");

    this._cache.set(key, promise);
  }

  /**
   * Remove the cache entry with the given key.
   *
   * @param {Any} key
   */
  remove(key) {
    assert(this._cache.has(key),
           `Should have an extant entry for key = ${key}`);

    this._cache.delete(key);
  }
};

/**
 * Create a new action-orchestrating task that is automatically cached. The
 * tasks themselves are responsible from removing themselves from the cache.
 *
 * @param {Function(...args) -> Any} getCacheKey
 * @param {Generator(...args) -> Any} task
 *
 * @returns Cacheable, Action-Creating Task
 */
TaskCache.declareCacheableTask = function ({ getCacheKey, task }) {
  const cache = new TaskCache();

  return function (...args) {
    return function* (dispatch, getState) {
      const key = getCacheKey(...args);

      const extantResult = cache.get(key);
      if (extantResult) {
        return extantResult;
      }

      // Ensure that we have our new entry in the cache *before* dispatching the
      // task!
      let resolve;
      cache.put(key, new Promise(r => {
        resolve = r;
      }));

      resolve(dispatch(function* () {
        try {
          args.push(() => cache.remove(key), dispatch, getState);
          return yield* task(...args);
        } catch (error) {
          // Don't perma-cache errors.
          if (cache.get(key)) {
            cache.remove(key);
          }
          throw error;
        }
      }));

      return yield cache.get(key);
    };
  };
};
PK
!<zf�ʄ�Jchrome/devtools/modules/devtools/client/memory/actions/tree-map-display.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { assert } = require("devtools/shared/DevToolsUtils");
const { actions } = require("../constants");
const { refresh } = require("./refresh");
/**
 * Sets the tree map display as the current display and refreshes the tree map
 * census.
 */
exports.setTreeMapAndRefresh = function (heapWorker, display) {
  return function* (dispatch, getState) {
    dispatch(setTreeMap(display));
    yield dispatch(refresh(heapWorker));
  };
};

/**
 * Clears out all cached census data in the snapshots and sets new display data
 * for tree maps.
 *
 * @param {treeMapModel} display
 */
const setTreeMap = exports.setTreeMap = function (display) {
  assert(typeof display === "object"
         && display
         && display.breakdown
         && display.breakdown.by,
    "Breakdowns must be an object with a \`by\` property, attempted to set: " +
    uneval(display));

  return {
    type: actions.SET_TREE_MAP_DISPLAY,
    display,
  };
};
PK
!<�2q�88>chrome/devtools/modules/devtools/client/memory/actions/view.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { assert } = require("devtools/shared/DevToolsUtils");
const { actions } = require("../constants");
const { findSelectedSnapshot } = require("../utils");
const refresh = require("./refresh");

/**
 * Change the currently selected view.
 *
 * @param {viewState} view
 */
const changeView = exports.changeView = function (view) {
  return function (dispatch, getState) {
    dispatch({
      type: actions.CHANGE_VIEW,
      newViewState: view,
      oldDiffing: getState().diffing,
      oldSelected: findSelectedSnapshot(getState()),
    });
  };
};

/**
 * Given that we are in the INDIVIDUALS view state, go back to the state we were
 * in before.
 */
const popView = exports.popView = function () {
  return function (dispatch, getState) {
    const { previous } = getState().view;
    assert(previous);
    dispatch({
      type: actions.POP_VIEW,
      previousView: previous,
    });
  };
};

/**
 * Change the currently selected view and ensure all our data is up to date from
 * the heap worker.
 *
 * @param {viewState} view
 * @param {HeapAnalysesClient} heapWorker
 */
exports.changeViewAndRefresh = function (view, heapWorker) {
  return function* (dispatch, getState) {
    dispatch(changeView(view));
    yield dispatch(refresh.refresh(heapWorker));
  };
};

/**
 * Given that we are in the INDIVIDUALS view state, go back to the state we were
 * previously in and refresh our data.
 *
 * @param {HeapAnalysesClient} heapWorker
 */
exports.popViewAndRefresh = function (heapWorker) {
  return function* (dispatch, getState) {
    dispatch(popView());
    yield dispatch(refresh.refresh(heapWorker));
  };
};
PK
!<�z[��1�15chrome/devtools/modules/devtools/client/memory/app.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { assert } = require("devtools/shared/DevToolsUtils");
const { appinfo } = require("Services");
const { Component, createFactory } = 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");
const { connect } = require("devtools/client/shared/vendor/react-redux");
const { censusDisplays, labelDisplays, treeMapDisplays, diffingState, viewState } = require("./constants");
const { toggleRecordingAllocationStacks } = require("./actions/allocations");
const { setCensusDisplayAndRefresh } = require("./actions/census-display");
const { setLabelDisplayAndRefresh } = require("./actions/label-display");
const { setTreeMapDisplayAndRefresh } = require("./actions/tree-map-display");

const {
  getCustomCensusDisplays,
  getCustomLabelDisplays,
  getCustomTreeMapDisplays,
} = require("devtools/client/memory/utils");
const {
  selectSnapshotForDiffingAndRefresh,
  toggleDiffing,
  expandDiffingCensusNode,
  collapseDiffingCensusNode,
  focusDiffingCensusNode,
} = require("./actions/diffing");
const { setFilterStringAndRefresh } = require("./actions/filter");
const { pickFileAndExportSnapshot, pickFileAndImportSnapshotAndCensus } = require("./actions/io");
const {
  selectSnapshotAndRefresh,
  takeSnapshotAndCensus,
  clearSnapshots,
  deleteSnapshot,
  fetchImmediatelyDominated,
  expandCensusNode,
  collapseCensusNode,
  focusCensusNode,
  expandDominatorTreeNode,
  collapseDominatorTreeNode,
  focusDominatorTreeNode,
  fetchIndividuals,
  focusIndividual,
} = require("./actions/snapshot");
const { changeViewAndRefresh, popViewAndRefresh } = require("./actions/view");
const { resizeShortestPaths } = require("./actions/sizes");
const Toolbar = createFactory(require("./components/Toolbar"));
const List = createFactory(require("./components/List"));
const SnapshotListItem = createFactory(require("./components/SnapshotListItem"));
const Heap = createFactory(require("./components/Heap"));
const { app: appModel } = require("./models");

class MemoryApp extends Component {
  static get propTypes() {
    return appModel;
  }

  static get childContextTypes() {
    return {
      front: PropTypes.any,
      heapWorker: PropTypes.any,
      toolbox: PropTypes.any,
    };
  }

  static get defaultProps() {
    return {};
  }

  constructor(props) {
    super(props);
    this.onKeyDown = this.onKeyDown.bind(this);
    this._getCensusDisplays = this._getCensusDisplays.bind(this);
    this._getLabelDisplays = this._getLabelDisplays.bind(this);
    this._getTreeMapDisplays = this._getTreeMapDisplays.bind(this);
  }

  getChildContext() {
    return {
      front: this.props.front,
      heapWorker: this.props.heapWorker,
      toolbox: this.props.toolbox,
    };
  }

  componentDidMount() {
    // Attach the keydown listener directly to the window. When an element that
    // has the focus (such as a tree node) is removed from the DOM, the focus
    // falls back to the body.
    window.addEventListener("keydown", this.onKeyDown);
  }

  componentWillUnmount() {
    window.removeEventListener("keydown", this.onKeyDown);
  }

  onKeyDown(e) {
    let { snapshots, dispatch, heapWorker } = this.props;
    const selectedSnapshot = snapshots.find(s => s.selected);
    const selectedIndex = snapshots.indexOf(selectedSnapshot);

    let isOSX = appinfo.OS == "Darwin";
    let isAccelKey = (isOSX && e.metaKey) || (!isOSX && e.ctrlKey);

    // On ACCEL+UP, select previous snapshot.
    if (isAccelKey && e.key === "ArrowUp") {
      let previousIndex = Math.max(0, selectedIndex - 1);
      let previousSnapshotId = snapshots[previousIndex].id;
      dispatch(selectSnapshotAndRefresh(heapWorker, previousSnapshotId));
    }

    // On ACCEL+DOWN, select next snapshot.
    if (isAccelKey && e.key === "ArrowDown") {
      let nextIndex = Math.min(snapshots.length - 1, selectedIndex + 1);
      let nextSnapshotId = snapshots[nextIndex].id;
      dispatch(selectSnapshotAndRefresh(heapWorker, nextSnapshotId));
    }
  }

  _getCensusDisplays() {
    const customDisplays = getCustomCensusDisplays();
    const custom = Object.keys(customDisplays).reduce((arr, key) => {
      arr.push(customDisplays[key]);
      return arr;
    }, []);

    return [
      censusDisplays.coarseType,
      censusDisplays.allocationStack,
      censusDisplays.invertedAllocationStack,
    ].concat(custom);
  }

  _getLabelDisplays() {
    const customDisplays = getCustomLabelDisplays();
    const custom = Object.keys(customDisplays).reduce((arr, key) => {
      arr.push(customDisplays[key]);
      return arr;
    }, []);

    return [
      labelDisplays.coarseType,
      labelDisplays.allocationStack,
    ].concat(custom);
  }

  _getTreeMapDisplays() {
    const customDisplays = getCustomTreeMapDisplays();
    const custom = Object.keys(customDisplays).reduce((arr, key) => {
      arr.push(customDisplays[key]);
      return arr;
    }, []);

    return [
      treeMapDisplays.coarseType
    ].concat(custom);
  }

  render() {
    let {
      dispatch,
      snapshots,
      front,
      heapWorker,
      allocations,
      toolbox,
      filter,
      diffing,
      view,
      sizes,
      censusDisplay,
      labelDisplay,
      individuals,
    } = this.props;

    const selectedSnapshot = snapshots.find(s => s.selected);

    const onClickSnapshotListItem = diffing && diffing.state === diffingState.SELECTING
      ? snapshot => dispatch(selectSnapshotForDiffingAndRefresh(heapWorker, snapshot))
      : snapshot => dispatch(selectSnapshotAndRefresh(heapWorker, snapshot.id));

    return (
      dom.div(
        {
          id: "memory-tool"
        },

        Toolbar({
          snapshots,
          censusDisplays: this._getCensusDisplays(),
          censusDisplay,
          onCensusDisplayChange: newDisplay =>
            dispatch(setCensusDisplayAndRefresh(heapWorker, newDisplay)),
          onImportClick: () => dispatch(pickFileAndImportSnapshotAndCensus(heapWorker)),
          onClearSnapshotsClick: () => dispatch(clearSnapshots(heapWorker)),
          onTakeSnapshotClick: () => dispatch(takeSnapshotAndCensus(front, heapWorker)),
          onToggleRecordAllocationStacks: () =>
            dispatch(toggleRecordingAllocationStacks(front)),
          allocations,
          filterString: filter,
          setFilterString: filterString =>
            dispatch(setFilterStringAndRefresh(filterString, heapWorker)),
          diffing,
          onToggleDiffing: () => dispatch(toggleDiffing()),
          view,
          labelDisplays: this._getLabelDisplays(),
          labelDisplay,
          onLabelDisplayChange: newDisplay =>
            dispatch(setLabelDisplayAndRefresh(heapWorker, newDisplay)),
          treeMapDisplays: this._getTreeMapDisplays(),
          onTreeMapDisplayChange: newDisplay =>
            dispatch(setTreeMapDisplayAndRefresh(heapWorker, newDisplay)),
          onViewChange: v => dispatch(changeViewAndRefresh(v, heapWorker)),
        }),

        dom.div(
          {
            id: "memory-tool-container"
          },

          List({
            itemComponent: SnapshotListItem,
            items: snapshots,
            onSave: snapshot => dispatch(pickFileAndExportSnapshot(snapshot)),
            onDelete: snapshot => dispatch(deleteSnapshot(heapWorker, snapshot)),
            onClick: onClickSnapshotListItem,
            diffing,
          }),

          Heap({
            snapshot: selectedSnapshot,
            diffing,
            onViewSourceInDebugger: frame =>
              toolbox.viewSourceInDebugger(frame.source, frame.line),
            onSnapshotClick: () =>
              dispatch(takeSnapshotAndCensus(front, heapWorker)),
            onLoadMoreSiblings: lazyChildren =>
              dispatch(fetchImmediatelyDominated(heapWorker,
                                                 selectedSnapshot.id,
                                                 lazyChildren)),
            onPopView: () => dispatch(popViewAndRefresh(heapWorker)),
            individuals,
            onViewIndividuals: node => {
              const snapshotId = diffing
                ? diffing.secondSnapshotId
                : selectedSnapshot.id;
              dispatch(fetchIndividuals(heapWorker,
                                        snapshotId,
                                        censusDisplay.breakdown,
                                        node.reportLeafIndex));
            },
            onFocusIndividual: node => {
              assert(view.state === viewState.INDIVIDUALS,
                     "Should be in the individuals view");
              dispatch(focusIndividual(node));
            },
            onCensusExpand: (census, node) => {
              if (diffing) {
                assert(diffing.census === census,
                       "Should only expand active census");
                dispatch(expandDiffingCensusNode(node));
              } else {
                assert(selectedSnapshot && selectedSnapshot.census === census,
                       "If not diffing, " +
                       "should be expanding on selected snapshot's census");
                dispatch(expandCensusNode(selectedSnapshot.id, node));
              }
            },
            onCensusCollapse: (census, node) => {
              if (diffing) {
                assert(diffing.census === census,
                       "Should only collapse active census");
                dispatch(collapseDiffingCensusNode(node));
              } else {
                assert(selectedSnapshot && selectedSnapshot.census === census,
                       "If not diffing, " +
                       "should be collapsing on selected snapshot's census");
                dispatch(collapseCensusNode(selectedSnapshot.id, node));
              }
            },
            onCensusFocus: (census, node) => {
              if (diffing) {
                assert(diffing.census === census,
                       "Should only focus nodes in active census");
                dispatch(focusDiffingCensusNode(node));
              } else {
                assert(selectedSnapshot && selectedSnapshot.census === census,
                       "If not diffing, " +
                       "should be focusing on nodes in selected snapshot's census");
                dispatch(focusCensusNode(selectedSnapshot.id, node));
              }
            },
            onDominatorTreeExpand: node => {
              assert(view.state === viewState.DOMINATOR_TREE,
                     "If expanding dominator tree nodes, " +
                     "should be in dominator tree view");
              assert(selectedSnapshot, "...and we should have a selected snapshot");
              assert(selectedSnapshot.dominatorTree,
                     "...and that snapshot should have a dominator tree");
              dispatch(expandDominatorTreeNode(selectedSnapshot.id, node));
            },
            onDominatorTreeCollapse: node => {
              assert(view.state === viewState.DOMINATOR_TREE,
                     "If collapsing dominator tree nodes, " +
                     "should be in dominator tree view");
              assert(selectedSnapshot, "...and we should have a selected snapshot");
              assert(selectedSnapshot.dominatorTree,
                     "...and that snapshot should have a dominator tree");
              dispatch(collapseDominatorTreeNode(selectedSnapshot.id, node));
            },
            onDominatorTreeFocus: node => {
              assert(view.state === viewState.DOMINATOR_TREE,
                     "If focusing dominator tree nodes, " +
                     "should be in dominator tree view");
              assert(selectedSnapshot, "...and we should have a selected snapshot");
              assert(selectedSnapshot.dominatorTree,
                     "...and that snapshot should have a dominator tree");
              dispatch(focusDominatorTreeNode(selectedSnapshot.id, node));
            },
            onShortestPathsResize: newSize => {
              dispatch(resizeShortestPaths(newSize));
            },
            sizes,
            view,
          })
        )
      )
    );
  }
}

/**
 * Passed into react-redux's `connect` method that is called on store change
 * and passed to components.
 */
function mapStateToProps(state) {
  return state;
}

module.exports = connect(mapStateToProps)(MemoryApp);
PK
!<?��[w	w	Cchrome/devtools/modules/devtools/client/memory/components/Census.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/vendor/react");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const Tree = createFactory(require("devtools/client/shared/components/VirtualizedTree"));
const CensusTreeItem = createFactory(require("./CensusTreeItem"));
const { TREE_ROW_HEIGHT } = require("../constants");
const { censusModel, diffingModel } = require("../models");

class Census extends Component {
  static get propTypes() {
    return {
      census: censusModel,
      onExpand: PropTypes.func.isRequired,
      onCollapse: PropTypes.func.isRequired,
      onFocus: PropTypes.func.isRequired,
      onViewSourceInDebugger: PropTypes.func.isRequired,
      onViewIndividuals: PropTypes.func.isRequired,
      diffing: diffingModel,
    };
  }

  render() {
    let {
      census,
      onExpand,
      onCollapse,
      onFocus,
      diffing,
      onViewSourceInDebugger,
      onViewIndividuals,
    } = this.props;

    const report = census.report;
    let parentMap = census.parentMap;
    const { totalBytes, totalCount } = report;

    const getPercentBytes = totalBytes === 0
      ? _ => 0
      : bytes => (bytes / totalBytes) * 100;

    const getPercentCount = totalCount === 0
      ? _ => 0
      : count => (count / totalCount) * 100;

    return Tree({
      autoExpandDepth: 0,
      focused: census.focused,
      getParent: node => {
        const parent = parentMap[node.id];
        return parent === report ? null : parent;
      },
      getChildren: node => node.children || [],
      isExpanded: node => census.expanded.has(node.id),
      onExpand,
      onCollapse,
      onFocus,
      renderItem: (item, depth, focused, arrow, expanded) =>
        new CensusTreeItem({
          onViewSourceInDebugger,
          item,
          depth,
          focused,
          arrow,
          expanded,
          getPercentBytes,
          getPercentCount,
          diffing,
          inverted: census.display.inverted,
          onViewIndividuals,
        }),
      getRoots: () => report.children || [],
      getKey: node => node.id,
      itemHeight: TREE_ROW_HEIGHT,
    });
  }
}

module.exports = Census;
PK
!<9䠙vvIchrome/devtools/modules/devtools/client/memory/components/CensusHeader.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/vendor/react");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const { L10N } = require("../utils");
const models = require("../models");

class CensusHeader extends Component {
  static get propTypes() {
    return {
      diffing: models.diffingModel,
    };
  }

  render() {
    let individualsCell;
    if (!this.props.diffing) {
      individualsCell = dom.span({
        className: "heap-tree-item-field heap-tree-item-individuals"
      });
    }

    return dom.div(
      {
        className: "header"
      },

      dom.span(
        {
          className: "heap-tree-item-bytes",
          title: L10N.getStr("heapview.field.bytes.tooltip"),
        },
        L10N.getStr("heapview.field.bytes")
      ),

      dom.span(
        {
          className: "heap-tree-item-count",
          title: L10N.getStr("heapview.field.count.tooltip"),
        },
        L10N.getStr("heapview.field.count")
      ),

      dom.span(
        {
          className: "heap-tree-item-total-bytes",
          title: L10N.getStr("heapview.field.totalbytes.tooltip"),
        },
        L10N.getStr("heapview.field.totalbytes")
      ),

      dom.span(
        {
          className: "heap-tree-item-total-count",
          title: L10N.getStr("heapview.field.totalcount.tooltip"),
        },
        L10N.getStr("heapview.field.totalcount")
      ),

      individualsCell,

      dom.span(
        {
          className: "heap-tree-item-name",
          title: L10N.getStr("heapview.field.name.tooltip"),
        },
        L10N.getStr("heapview.field.name")
      )
    );
  }
}

module.exports = CensusHeader;
PK
!<�I��ggKchrome/devtools/modules/devtools/client/memory/components/CensusTreeItem.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { isSavedFrame } = require("devtools/shared/DevToolsUtils");
const { Component, createFactory } = 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");
const { L10N, formatNumber, formatPercent } = require("../utils");
const Frame = createFactory(require("devtools/client/shared/components/Frame"));
const { TREE_ROW_HEIGHT } = require("../constants");
const models = require("../models");

class CensusTreeItem extends Component {
  static get propTypes() {
    return {
      arrow: PropTypes.any,
      depth: PropTypes.number.isRequired,
      diffing: models.app.diffing,
      expanded: PropTypes.bool.isRequired,
      focused: PropTypes.bool.isRequired,
      getPercentBytes: PropTypes.func.isRequired,
      getPercentCount: PropTypes.func.isRequired,
      inverted: PropTypes.bool,
      item: PropTypes.object.isRequired,
      onViewIndividuals: PropTypes.func.isRequired,
      onViewSourceInDebugger: PropTypes.func.isRequired,
    };
  }

  constructor(props) {
    super(props);
    this.toLabel = this.toLabel.bind(this);
  }

  shouldComponentUpdate(nextProps, nextState) {
    return this.props.item != nextProps.item
      || this.props.depth != nextProps.depth
      || this.props.expanded != nextProps.expanded
      || this.props.focused != nextProps.focused
      || this.props.diffing != nextProps.diffing;
  }

  toLabel(name, linkToDebugger) {
    if (isSavedFrame(name)) {
      return Frame({
        frame: name,
        onClick: () => linkToDebugger(name),
        showFunctionName: true,
        showHost: true,
      });
    }

    if (name === null) {
      return L10N.getStr("tree-item.root");
    }

    if (name === "noStack") {
      return L10N.getStr("tree-item.nostack");
    }

    if (name === "noFilename") {
      return L10N.getStr("tree-item.nofilename");
    }

    return String(name);
  }

  render() {
    let {
      item,
      depth,
      arrow,
      focused,
      getPercentBytes,
      getPercentCount,
      diffing,
      onViewSourceInDebugger,
      onViewIndividuals,
      inverted,
    } = this.props;

    const bytes = formatNumber(item.bytes, !!diffing);
    const percentBytes = formatPercent(getPercentBytes(item.bytes), !!diffing);

    const count = formatNumber(item.count, !!diffing);
    const percentCount = formatPercent(getPercentCount(item.count), !!diffing);

    const totalBytes = formatNumber(item.totalBytes, !!diffing);
    const percentTotalBytes = formatPercent(getPercentBytes(item.totalBytes), !!diffing);

    const totalCount = formatNumber(item.totalCount, !!diffing);
    const percentTotalCount = formatPercent(getPercentCount(item.totalCount), !!diffing);

    let pointer;
    if (inverted && depth > 0) {
      pointer = dom.span({ className: "children-pointer" }, "↖");
    } else if (!inverted && item.children && item.children.length) {
      pointer = dom.span({ className: "children-pointer" }, "↘");
    }

    let individualsCell;
    if (!diffing) {
      let individualsButton;
      if (item.reportLeafIndex !== undefined) {
        individualsButton = dom.button(
          {
            key: `individuals-button-${item.id}`,
            title: L10N.getStr("tree-item.view-individuals.tooltip"),
            className: "devtools-button individuals-button",
            onClick: e => {
              // Don't let the event bubble up to cause this item to focus after
              // we have switched views, which would lead to assertion failures.
              e.preventDefault();
              e.stopPropagation();

              onViewIndividuals(item);
            },
          },
          "⁂"
        );
      }
      individualsCell = dom.span(
        { className: "heap-tree-item-field heap-tree-item-individuals" },
        individualsButton
      );
    }

    return dom.div(
      { className: `heap-tree-item ${focused ? "focused" : ""}` },
      dom.span({ className: "heap-tree-item-field heap-tree-item-bytes" },
               dom.span({ className: "heap-tree-number" }, bytes),
               dom.span({ className: "heap-tree-percent" }, percentBytes)),
      dom.span({ className: "heap-tree-item-field heap-tree-item-count" },
               dom.span({ className: "heap-tree-number" }, count),
               dom.span({ className: "heap-tree-percent" }, percentCount)),
      dom.span({ className: "heap-tree-item-field heap-tree-item-total-bytes" },
               dom.span({ className: "heap-tree-number" }, totalBytes),
               dom.span({ className: "heap-tree-percent" }, percentTotalBytes)),
      dom.span({ className: "heap-tree-item-field heap-tree-item-total-count" },
               dom.span({ className: "heap-tree-number" }, totalCount),
               dom.span({ className: "heap-tree-percent" }, percentTotalCount)),
      individualsCell,
      dom.span(
        {
          className: "heap-tree-item-field heap-tree-item-name",
          style: { marginInlineStart: depth * TREE_ROW_HEIGHT }
        },
        arrow,
        pointer,
        this.toLabel(item.name, onViewSourceInDebugger)
      )
    );
  }
}

module.exports = CensusTreeItem;
PK
!<��/��Jchrome/devtools/modules/devtools/client/memory/components/DominatorTree.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const { assert } = require("devtools/shared/DevToolsUtils");
const { createParentMap } = require("devtools/shared/heapsnapshot/CensusUtils");
const Tree = createFactory(require("devtools/client/shared/components/VirtualizedTree"));
const DominatorTreeItem = createFactory(require("./DominatorTreeItem"));
const { L10N } = require("../utils");
const { TREE_ROW_HEIGHT, dominatorTreeState } = require("../constants");
const { dominatorTreeModel } = require("../models");
const DominatorTreeLazyChildren = require("../dominator-tree-lazy-children");

const DOMINATOR_TREE_AUTO_EXPAND_DEPTH = 3;

/**
 * A throbber that represents a subtree in the dominator tree that is actively
 * being incrementally loaded and fetched from the `HeapAnalysesWorker`.
 */
class DominatorTreeSubtreeFetchingClass extends Component {
  static get propTypes() {
    return {
      depth: PropTypes.number.isRequired,
      focused: PropTypes.bool.isRequired,
    };
  }

  shouldComponentUpdate(nextProps, nextState) {
    return this.props.depth !== nextProps.depth
      || this.props.focused !== nextProps.focused;
  }

  render() {
    let {
      depth,
      focused,
    } = this.props;

    return dom.div(
      {
        className: `heap-tree-item subtree-fetching ${focused ? "focused" : ""}`
      },
      dom.span({ className: "heap-tree-item-field heap-tree-item-bytes" }),
      dom.span({ className: "heap-tree-item-field heap-tree-item-bytes" }),
      dom.span({
        className: "heap-tree-item-field heap-tree-item-name devtools-throbber",
        style: { marginInlineStart: depth * TREE_ROW_HEIGHT }
      })
    );
  }
}

/**
 * A link to fetch and load more siblings in the dominator tree, when there are
 * already many loaded above.
 */
class DominatorTreeSiblingLinkClass extends Component {
  static get propTypes() {
    return {
      depth: PropTypes.number.isRequired,
      focused: PropTypes.bool.isRequired,
      item: PropTypes.instanceOf(DominatorTreeLazyChildren).isRequired,
      onLoadMoreSiblings: PropTypes.func.isRequired,
    };
  }

  shouldComponentUpdate(nextProps, nextState) {
    return this.props.depth !== nextProps.depth
      || this.props.focused !== nextProps.focused;
  }

  render() {
    let {
      depth,
      focused,
      item,
      onLoadMoreSiblings,
    } = this.props;

    return dom.div(
      {
        className: `heap-tree-item more-children ${focused ? "focused" : ""}`
      },
      dom.span({ className: "heap-tree-item-field heap-tree-item-bytes" }),
      dom.span({ className: "heap-tree-item-field heap-tree-item-bytes" }),
      dom.span(
        {
          className: "heap-tree-item-field heap-tree-item-name",
          style: { marginInlineStart: depth * TREE_ROW_HEIGHT }
        },
        dom.a(
          {
            onClick: () => onLoadMoreSiblings(item)
          },
          L10N.getStr("tree-item.load-more")
        )
      )
    );
  }
}

class DominatorTree extends Component {
  static get propTypes() {
    return {
      dominatorTree: dominatorTreeModel.isRequired,
      onLoadMoreSiblings: PropTypes.func.isRequired,
      onViewSourceInDebugger: PropTypes.func.isRequired,
      onExpand: PropTypes.func.isRequired,
      onCollapse: PropTypes.func.isRequired,
      onFocus: PropTypes.func.isRequired,
    };
  }

  shouldComponentUpdate(nextProps, nextState) {
    // Safe to use referential equality here because all of our mutations on
    // dominator tree models use immutableUpdate in a persistent manner. The
    // exception to the rule are mutations of the expanded set, however we take
    // care that the dominatorTree model itself is still re-allocated when
    // mutations to the expanded set occur. Because of the re-allocations, we
    // can continue using referential equality here.
    return this.props.dominatorTree !== nextProps.dominatorTree;
  }

  render() {
    const { dominatorTree, onViewSourceInDebugger, onLoadMoreSiblings } = this.props;

    const parentMap = createParentMap(dominatorTree.root, node => node.nodeId);

    return Tree({
      key: "dominator-tree-tree",
      autoExpandDepth: DOMINATOR_TREE_AUTO_EXPAND_DEPTH,
      focused: dominatorTree.focused,
      getParent: node =>
        node instanceof DominatorTreeLazyChildren
          ? parentMap[node.parentNodeId()]
          : parentMap[node.nodeId],
      getChildren: node => {
        const children = node.children ? node.children.slice() : [];
        if (node.moreChildrenAvailable) {
          children.push(new DominatorTreeLazyChildren(node.nodeId, children.length));
        }
        return children;
      },
      isExpanded: node => {
        return node instanceof DominatorTreeLazyChildren
          ? false
          : dominatorTree.expanded.has(node.nodeId);
      },
      onExpand: item => {
        if (item instanceof DominatorTreeLazyChildren) {
          return;
        }

        if (item.moreChildrenAvailable && (!item.children || !item.children.length)) {
          const startIndex = item.children ? item.children.length : 0;
          onLoadMoreSiblings(new DominatorTreeLazyChildren(item.nodeId, startIndex));
        }

        this.props.onExpand(item);
      },
      onCollapse: item => {
        if (item instanceof DominatorTreeLazyChildren) {
          return;
        }

        this.props.onCollapse(item);
      },
      onFocus: item => {
        if (item instanceof DominatorTreeLazyChildren) {
          return;
        }

        this.props.onFocus(item);
      },
      renderItem: (item, depth, focused, arrow, expanded) => {
        if (item instanceof DominatorTreeLazyChildren) {
          if (item.isFirstChild()) {
            assert(dominatorTree.state === dominatorTreeState.INCREMENTAL_FETCHING,
                   "If we are displaying a throbber for loading a subtree, " +
                   "then we should be INCREMENTAL_FETCHING those children right now");
            return DominatorTreeSubtreeFetching({
              key: item.key(),
              depth,
              focused,
            });
          }

          return DominatorTreeSiblingLink({
            key: item.key(),
            item,
            depth,
            focused,
            onLoadMoreSiblings,
          });
        }

        return DominatorTreeItem({
          item,
          depth,
          focused,
          arrow,
          expanded,
          getPercentSize: size => (size / dominatorTree.root.retainedSize) * 100,
          onViewSourceInDebugger,
        });
      },
      getRoots: () => [dominatorTree.root],
      getKey: node =>
        node instanceof DominatorTreeLazyChildren ? node.key() : node.nodeId,
      itemHeight: TREE_ROW_HEIGHT,
    });
  }
}

const DominatorTreeSubtreeFetching = createFactory(DominatorTreeSubtreeFetchingClass);
const DominatorTreeSiblingLink = createFactory(DominatorTreeSiblingLinkClass);

module.exports = DominatorTree;
PK
!<]:Oe��Pchrome/devtools/modules/devtools/client/memory/components/DominatorTreeHeader.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/vendor/react");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const { L10N } = require("../utils");

class DominatorTreeHeader extends Component {
  static get propTypes() {
    return { };
  }

  render() {
    return dom.div(
      {
        className: "header"
      },

      dom.span(
        {
          className: "heap-tree-item-bytes",
          title: L10N.getStr("heapview.field.retainedSize.tooltip"),
        },
        L10N.getStr("heapview.field.retainedSize")
      ),

      dom.span(
        {
          className: "heap-tree-item-bytes",
          title: L10N.getStr("heapview.field.shallowSize.tooltip"),
        },
        L10N.getStr("heapview.field.shallowSize")
      ),

      dom.span(
        {
          className: "heap-tree-item-name",
          title: L10N.getStr("dominatortree.field.label.tooltip"),
        },
        L10N.getStr("dominatortree.field.label")
      )
    );
  }
}

module.exports = DominatorTreeHeader;
PK
!<~C9��Nchrome/devtools/modules/devtools/client/memory/components/DominatorTreeItem.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { assert, isSavedFrame } = require("devtools/shared/DevToolsUtils");
const { Component, createFactory } = 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");
const { L10N, formatNumber, formatPercent } = require("../utils");
const Frame = createFactory(require("devtools/client/shared/components/Frame"));
const { TREE_ROW_HEIGHT } = require("../constants");

class SeparatorClass extends Component {
  render() {
    return dom.span({ className: "separator" }, "›");
  }
}

const Separator = createFactory(SeparatorClass);

class DominatorTreeItem extends Component {
  static get propTypes() {
    return {
      item: PropTypes.object.isRequired,
      depth: PropTypes.number.isRequired,
      arrow: PropTypes.object,
      expanded: PropTypes.bool.isRequired,
      focused: PropTypes.bool.isRequired,
      getPercentSize: PropTypes.func.isRequired,
      onViewSourceInDebugger: PropTypes.func.isRequired,
    };
  }

  shouldComponentUpdate(nextProps, nextState) {
    return this.props.item != nextProps.item
      || this.props.depth != nextProps.depth
      || this.props.expanded != nextProps.expanded
      || this.props.focused != nextProps.focused;
  }

  render() {
    let {
      item,
      depth,
      arrow,
      focused,
      getPercentSize,
      onViewSourceInDebugger,
    } = this.props;

    const retainedSize = formatNumber(item.retainedSize);
    const percentRetainedSize = formatPercent(getPercentSize(item.retainedSize));

    const shallowSize = formatNumber(item.shallowSize);
    const percentShallowSize = formatPercent(getPercentSize(item.shallowSize));

    // Build up our label UI as an array of each label piece, which is either a
    // string or a frame, and separators in between them.

    assert(item.label.length > 0,
           "Our label should not be empty");
    const label = Array(item.label.length * 2 - 1);
    label.fill(undefined);

    for (let i = 0, length = item.label.length; i < length; i++) {
      const piece = item.label[i];
      const key = `${item.nodeId}-label-${i}`;

      // `i` is the index of the label piece we are rendering, `label[i*2]` is
      // where the rendered label piece belngs, and `label[i*2+1]` (if it isn't
      // out of bounds) is where the separator belongs.

      if (isSavedFrame(piece)) {
        label[i * 2] = Frame({
          key,
          onClick: () => onViewSourceInDebugger(piece),
          frame: piece,
          showFunctionName: true
        });
      } else if (piece === "noStack") {
        label[i * 2] = dom.span({ key, className: "not-available" },
                                L10N.getStr("tree-item.nostack"));
      } else if (piece === "noFilename") {
        label[i * 2] = dom.span({ key, className: "not-available" },
                                L10N.getStr("tree-item.nofilename"));
      } else if (piece === "JS::ubi::RootList") {
        // Don't use the usual labeling machinery for root lists: replace it
        // with the "GC Roots" string.
        label.splice(0, label.length);
        label.push(L10N.getStr("tree-item.rootlist"));
        break;
      } else {
        label[i * 2] = piece;
      }

      // If this is not the last piece of the label, add a separator.
      if (i < length - 1) {
        label[i * 2 + 1] = Separator({ key: `${item.nodeId}-separator-${i}` });
      }
    }

    return dom.div(
      {
        className: `heap-tree-item ${focused ? "focused" : ""} node-${item.nodeId}`
      },

      dom.span(
        {
          className: "heap-tree-item-field heap-tree-item-bytes"
        },
        dom.span(
          {
            className: "heap-tree-number"
          },
          retainedSize
        ),
        dom.span({ className: "heap-tree-percent" }, percentRetainedSize)
      ),

      dom.span(
        {
          className: "heap-tree-item-field heap-tree-item-bytes"
        },
        dom.span(
          {
            className: "heap-tree-number"
          },
          shallowSize
        ),
        dom.span({ className: "heap-tree-percent" }, percentShallowSize)
      ),

      dom.span(
        {
          className: "heap-tree-item-field heap-tree-item-name",
          style: { marginInlineStart: depth * TREE_ROW_HEIGHT }
        },
        arrow,
        label,
        dom.span({ className: "heap-tree-item-address" },
                 `@ 0x${item.nodeId.toString(16)}`)
      )
    );
  }
}

module.exports = DominatorTreeItem;
PK
!<��T�D7D7Achrome/devtools/modules/devtools/client/memory/components/Heap.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const { assert, safeErrorString } = require("devtools/shared/DevToolsUtils");
const Census = createFactory(require("./Census"));
const CensusHeader = createFactory(require("./CensusHeader"));
const DominatorTree = createFactory(require("./DominatorTree"));
const DominatorTreeHeader = createFactory(require("./DominatorTreeHeader"));
const TreeMap = createFactory(require("./TreeMap"));
const HSplitBox = createFactory(require("devtools/client/shared/components/HSplitBox"));
const Individuals = createFactory(require("./Individuals"));
const IndividualsHeader = createFactory(require("./IndividualsHeader"));
const ShortestPaths = createFactory(require("./ShortestPaths"));
const { getStatusTextFull, L10N } = require("../utils");
const {
  snapshotState: states,
  diffingState,
  viewState,
  censusState,
  treeMapState,
  dominatorTreeState,
  individualsState,
} = require("../constants");
const models = require("../models");
const { snapshot: snapshotModel, diffingModel } = models;

/**
 * Get the app state's current state atom.
 *
 * @see the relevant state string constants in `../constants.js`.
 *
 * @param {models.view} view
 * @param {snapshotModel} snapshot
 * @param {diffingModel} diffing
 * @param {individualsModel} individuals
 *
 * @return {snapshotState|diffingState|dominatorTreeState}
 */
function getState(view, snapshot, diffing, individuals) {
  switch (view.state) {
    case viewState.CENSUS:
      return snapshot.census
        ? snapshot.census.state
        : snapshot.state;

    case viewState.DIFFING:
      return diffing.state;

    case viewState.TREE_MAP:
      return snapshot.treeMap
      ? snapshot.treeMap.state
      : snapshot.state;

    case viewState.DOMINATOR_TREE:
      return snapshot.dominatorTree
        ? snapshot.dominatorTree.state
        : snapshot.state;

    case viewState.INDIVIDUALS:
      return individuals.state;
  }

  assert(false, `Unexpected view state: ${view.state}`);
  return null;
}

/**
 * Return true if we should display a status message when we are in the given
 * state. Return false otherwise.
 *
 * @param {snapshotState|diffingState|dominatorTreeState} state
 * @param {models.view} view
 * @param {snapshotModel} snapshot
 *
 * @returns {Boolean}
 */
function shouldDisplayStatus(state, view, snapshot) {
  switch (state) {
    case states.IMPORTING:
    case states.SAVING:
    case states.SAVED:
    case states.READING:
    case censusState.SAVING:
    case treeMapState.SAVING:
    case diffingState.SELECTING:
    case diffingState.TAKING_DIFF:
    case dominatorTreeState.COMPUTING:
    case dominatorTreeState.COMPUTED:
    case dominatorTreeState.FETCHING:
    case individualsState.COMPUTING_DOMINATOR_TREE:
    case individualsState.FETCHING:
      return true;
  }
  return view.state === viewState.DOMINATOR_TREE && !snapshot.dominatorTree;
}

/**
 * Get the status text to display for the given state.
 *
 * @param {snapshotState|diffingState|dominatorTreeState} state
 * @param {diffingModel} diffing
 *
 * @returns {String}
 */
function getStateStatusText(state, diffing) {
  if (state === diffingState.SELECTING) {
    return L10N.getStr(diffing.firstSnapshotId === null
                         ? "diffing.prompt.selectBaseline"
                         : "diffing.prompt.selectComparison");
  }

  return getStatusTextFull(state);
}

/**
 * Given that we should display a status message, return true if we should also
 * display a throbber along with the status message. Return false otherwise.
 *
 * @param {diffingModel} diffing
 *
 * @returns {Boolean}
 */
function shouldDisplayThrobber(diffing) {
  return !diffing || diffing.state !== diffingState.SELECTING;
}

/**
 * Get the current state's error, or return null if there is none.
 *
 * @param {snapshotModel} snapshot
 * @param {diffingModel} diffing
 * @param {individualsModel} individuals
 *
 * @returns {Error|null}
 */
function getError(snapshot, diffing, individuals) {
  if (diffing) {
    if (diffing.state === diffingState.ERROR) {
      return diffing.error;
    }
    if (diffing.census === censusState.ERROR) {
      return diffing.census.error;
    }
  }

  if (snapshot) {
    if (snapshot.state === states.ERROR) {
      return snapshot.error;
    }

    if (snapshot.census === censusState.ERROR) {
      return snapshot.census.error;
    }

    if (snapshot.treeMap === treeMapState.ERROR) {
      return snapshot.treeMap.error;
    }

    if (snapshot.dominatorTree &&
        snapshot.dominatorTree.state === dominatorTreeState.ERROR) {
      return snapshot.dominatorTree.error;
    }
  }

  if (individuals && individuals.state === individualsState.ERROR) {
    return individuals.error;
  }

  return null;
}

/**
 * Main view for the memory tool.
 *
 * The Heap component contains several panels for different states; an initial
 * state of only a button to take a snapshot, loading states, the census view
 * tree, the dominator tree, etc.
 */
class Heap extends Component {
  static get propTypes() {
    return {
      onSnapshotClick: PropTypes.func.isRequired,
      onLoadMoreSiblings: PropTypes.func.isRequired,
      onCensusExpand: PropTypes.func.isRequired,
      onCensusCollapse: PropTypes.func.isRequired,
      onDominatorTreeExpand: PropTypes.func.isRequired,
      onDominatorTreeCollapse: PropTypes.func.isRequired,
      onCensusFocus: PropTypes.func.isRequired,
      onDominatorTreeFocus: PropTypes.func.isRequired,
      onShortestPathsResize: PropTypes.func.isRequired,
      snapshot: snapshotModel,
      onViewSourceInDebugger: PropTypes.func.isRequired,
      onPopView: PropTypes.func.isRequired,
      individuals: models.individuals,
      onViewIndividuals: PropTypes.func.isRequired,
      onFocusIndividual: PropTypes.func.isRequired,
      diffing: diffingModel,
      view: models.view.isRequired,
      sizes: PropTypes.object.isRequired,
    };
  }

  constructor(props) {
    super(props);
    this._renderHeapView = this._renderHeapView.bind(this);
    this._renderInitial = this._renderInitial.bind(this);
    this._renderStatus = this._renderStatus.bind(this);
    this._renderError = this._renderError.bind(this);
    this._renderCensus = this._renderCensus.bind(this);
    this._renderTreeMap = this._renderTreeMap.bind(this);
    this._renderIndividuals = this._renderIndividuals.bind(this);
    this._renderDominatorTree = this._renderDominatorTree.bind(this);
  }

  /**
   * Render the heap view's container panel with the given contents inside of
   * it.
   *
   * @param {snapshotState|diffingState|dominatorTreeState} state
   * @param {...Any} contents
   */
  _renderHeapView(state, ...contents) {
    return dom.div(
      {
        id: "heap-view",
        "data-state": state
      },
      dom.div(
        {
          className: "heap-view-panel",
          "data-state": state,
        },
        ...contents
      )
    );
  }

  _renderInitial(onSnapshotClick) {
    return this._renderHeapView("initial", dom.button(
      {
        className: "devtools-button take-snapshot",
        onClick: onSnapshotClick,
        "data-standalone": true,
      },
      L10N.getStr("take-snapshot")
    ));
  }

  _renderStatus(state, statusText, diffing) {
    let throbber = "";
    if (shouldDisplayThrobber(diffing)) {
      throbber = "devtools-throbber";
    }

    return this._renderHeapView(state, dom.span(
      {
        className: `snapshot-status ${throbber}`
      },
      statusText
    ));
  }

  _renderError(state, statusText, error) {
    return this._renderHeapView(
      state,
      dom.span({ className: "snapshot-status error" }, statusText),
      dom.pre({}, safeErrorString(error))
    );
  }

  _renderCensus(state, census, diffing, onViewSourceInDebugger, onViewIndividuals) {
    assert(census.report, "Should not render census that does not have a report");

    if (!census.report.children) {
      const censusFilterMsg = census.filter ? L10N.getStr("heapview.none-match")
                                            : L10N.getStr("heapview.empty");
      const msg = diffing ? L10N.getStr("heapview.no-difference")
                          : censusFilterMsg;
      return this._renderHeapView(state, dom.div({ className: "empty" }, msg));
    }

    const contents = [];

    if (census.display.breakdown.by === "allocationStack"
        && census.report.children
        && census.report.children.length === 1
        && census.report.children[0].name === "noStack") {
      contents.push(dom.div({ className: "error no-allocation-stacks" },
                            L10N.getStr("heapview.noAllocationStacks")));
    }

    contents.push(CensusHeader({ diffing }));
    contents.push(Census({
      onViewSourceInDebugger,
      onViewIndividuals,
      diffing,
      census,
      onExpand: node => this.props.onCensusExpand(census, node),
      onCollapse: node => this.props.onCensusCollapse(census, node),
      onFocus: node => this.props.onCensusFocus(census, node),
    }));

    return this._renderHeapView(state, ...contents);
  }

  _renderTreeMap(state, treeMap) {
    return this._renderHeapView(
      state,
      TreeMap({ treeMap })
    );
  }

  _renderIndividuals(state, individuals, dominatorTree, onViewSourceInDebugger) {
    assert(individuals.state === individualsState.FETCHED,
           "Should have fetched individuals");
    assert(dominatorTree && dominatorTree.root,
           "Should have a dominator tree and its root");

    const tree = dom.div(
      {
        className: "vbox",
        style: {
          overflowY: "auto"
        }
      },
      IndividualsHeader(),
      Individuals({
        individuals,
        dominatorTree,
        onViewSourceInDebugger,
        onFocus: this.props.onFocusIndividual
      })
    );

    const shortestPaths = ShortestPaths({
      graph: individuals.focused
        ? individuals.focused.shortestPaths
        : null
    });

    return this._renderHeapView(
      state,
      dom.div(
        { className: "hbox devtools-toolbar" },
        dom.label(
          { id: "pop-view-button-label" },
          dom.button(
            {
              id: "pop-view-button",
              className: "devtools-button",
              onClick: this.props.onPopView,
            },
            L10N.getStr("toolbar.pop-view")
          ),
          L10N.getStr("toolbar.pop-view.label")
        ),
        L10N.getStr("toolbar.viewing-individuals")
      ),
      HSplitBox({
        start: tree,
        end: shortestPaths,
        startWidth: this.props.sizes.shortestPathsSize,
        onResize: this.props.onShortestPathsResize,
      })
    );
  }

  _renderDominatorTree(state, onViewSourceInDebugger, dominatorTree, onLoadMoreSiblings) {
    const tree = dom.div(
      {
        className: "vbox",
        style: {
          overflowY: "auto"
        }
      },
      DominatorTreeHeader(),
      DominatorTree({
        onViewSourceInDebugger,
        dominatorTree,
        onLoadMoreSiblings,
        onExpand: this.props.onDominatorTreeExpand,
        onCollapse: this.props.onDominatorTreeCollapse,
        onFocus: this.props.onDominatorTreeFocus,
      })
    );

    const shortestPaths = ShortestPaths({
      graph: dominatorTree.focused
        ? dominatorTree.focused.shortestPaths
        : null
    });

    return this._renderHeapView(
      state,
      HSplitBox({
        start: tree,
        end: shortestPaths,
        startWidth: this.props.sizes.shortestPathsSize,
        onResize: this.props.onShortestPathsResize,
      })
    );
  }

  render() {
    let {
      snapshot,
      diffing,
      onSnapshotClick,
      onLoadMoreSiblings,
      onViewSourceInDebugger,
      onViewIndividuals,
      individuals,
      view,
    } = this.props;

    if (!diffing && !snapshot && !individuals) {
      return this._renderInitial(onSnapshotClick);
    }

    const state = getState(view, snapshot, diffing, individuals);
    const statusText = getStateStatusText(state, diffing);

    if (shouldDisplayStatus(state, view, snapshot)) {
      return this._renderStatus(state, statusText, diffing);
    }

    const error = getError(snapshot, diffing, individuals);
    if (error) {
      return this._renderError(state, statusText, error);
    }

    if (view.state === viewState.CENSUS || view.state === viewState.DIFFING) {
      const census = view.state === viewState.CENSUS
        ? snapshot.census
        : diffing.census;
      if (!census) {
        return this._renderStatus(state, statusText, diffing);
      }
      return this._renderCensus(state, census, diffing, onViewSourceInDebugger,
                                onViewIndividuals);
    }

    if (view.state === viewState.TREE_MAP) {
      return this._renderTreeMap(state, snapshot.treeMap);
    }

    if (view.state === viewState.INDIVIDUALS) {
      assert(individuals.state === individualsState.FETCHED,
             "Should have fetched the individuals -- " +
             "other states are rendered as statuses");
      return this._renderIndividuals(state, individuals,
                                     individuals.dominatorTree,
                                     onViewSourceInDebugger);
    }

    assert(view.state === viewState.DOMINATOR_TREE,
           "If we aren't in progress, looking at a census, or diffing, then we " +
           "must be looking at a dominator tree");
    assert(!diffing, "Should not have diffing");
    assert(snapshot.dominatorTree, "Should have a dominator tree");

    return this._renderDominatorTree(state, onViewSourceInDebugger,
                                     snapshot.dominatorTree,
                                     onLoadMoreSiblings);
  }
}

module.exports = Heap;
PK
!<����DDHchrome/devtools/modules/devtools/client/memory/components/Individuals.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/vendor/react");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const Tree = createFactory(require("devtools/client/shared/components/VirtualizedTree"));
const DominatorTreeItem = createFactory(require("./DominatorTreeItem"));
const { TREE_ROW_HEIGHT } = require("../constants");
const models = require("../models");

/**
 * The list of individuals in a census group.
 */
class Individuals extends Component {
  static get propTypes() {
    return {
      onViewSourceInDebugger: PropTypes.func.isRequired,
      onFocus: PropTypes.func.isRequired,
      individuals: models.individuals,
      dominatorTree: models.dominatorTreeModel,
    };
  }

  render() {
    const {
      individuals,
      dominatorTree,
      onViewSourceInDebugger,
      onFocus,
    } = this.props;

    return Tree({
      key: "individuals-tree",
      autoExpandDepth: 0,
      focused: individuals.focused,
      getParent: node => null,
      getChildren: node => [],
      isExpanded: node => false,
      onExpand: () => {},
      onCollapse: () => {},
      onFocus,
      renderItem: (item, depth, focused, _, expanded) => {
        return DominatorTreeItem({
          item,
          depth,
          focused,
          arrow: undefined,
          expanded,
          getPercentSize: size => (size / dominatorTree.root.retainedSize) * 100,
          onViewSourceInDebugger,
        });
      },
      getRoots: () => individuals.nodes,
      getKey: node => node.nodeId,
      itemHeight: TREE_ROW_HEIGHT,
    });
  }
}

module.exports = Individuals;
PK
!<kvqL��Nchrome/devtools/modules/devtools/client/memory/components/IndividualsHeader.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/vendor/react");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const { L10N } = require("../utils");

class IndividualsHeader extends Component {
  static get propTypes() {
    return { };
  }

  render() {
    return dom.div(
      {
        className: "header"
      },

      dom.span(
        {
          className: "heap-tree-item-bytes",
          title: L10N.getStr("heapview.field.retainedSize.tooltip"),
        },
        L10N.getStr("heapview.field.retainedSize")
      ),

      dom.span(
        {
          className: "heap-tree-item-bytes",
          title: L10N.getStr("heapview.field.shallowSize.tooltip"),
        },
        L10N.getStr("heapview.field.shallowSize")
      ),

      dom.span(
        {
          className: "heap-tree-item-name",
          title: L10N.getStr("individuals.field.node.tooltip"),
        },
        L10N.getStr("individuals.field.node")
      )
    );
  }
}

module.exports = IndividualsHeader;
PK
!<^��V��Achrome/devtools/modules/devtools/client/memory/components/List.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");

/**
 * Generic list component that takes another react component to represent
 * the children nodes as `itemComponent`, and a list of items to render
 * as that component with a click handler.
 */
class List extends Component {
  static get propTypes() {
    return {
      itemComponent: PropTypes.any.isRequired,
      onClick: PropTypes.func,
      items: PropTypes.array.isRequired,
    };
  }

  render() {
    let { items, onClick, itemComponent: Item } = this.props;

    return (
      dom.ul({ className: "list" }, ...items.map((item, index) => {
        return Item(Object.assign({}, this.props, {
          key: index,
          item,
          index,
          onClick: () => onClick(item),
        }));
      }))
    );
  }
}

module.exports = List;
PK
!<�S�Jchrome/devtools/modules/devtools/client/memory/components/ShortestPaths.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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 { isSavedFrame } = require("devtools/shared/DevToolsUtils");
const { getSourceNames } = require("devtools/client/shared/source-utils");
const { L10N } = require("../utils");

const GRAPH_DEFAULTS = {
  translate: [20, 20],
  scale: 1
};

const NO_STACK = "noStack";
const NO_FILENAME = "noFilename";
const ROOT_LIST = "JS::ubi::RootList";

function stringifyLabel(label, id) {
  const sanitized = [];

  for (let i = 0, length = label.length; i < length; i++) {
    const piece = label[i];

    if (isSavedFrame(piece)) {
      const { short } = getSourceNames(piece.source);
      sanitized[i] = `${piece.functionDisplayName} @ ` +
                     `${short}:${piece.line}:${piece.column}`;
    } else if (piece === NO_STACK) {
      sanitized[i] = L10N.getStr("tree-item.nostack");
    } else if (piece === NO_FILENAME) {
      sanitized[i] = L10N.getStr("tree-item.nofilename");
    } else if (piece === ROOT_LIST) {
      // Don't use the usual labeling machinery for root lists: replace it
      // with the "GC Roots" string.
      sanitized.splice(0, label.length);
      sanitized.push(L10N.getStr("tree-item.rootlist"));
      break;
    } else {
      sanitized[i] = "" + piece;
    }
  }

  return `${sanitized.join(" › ")} @ 0x${id.toString(16)}`;
}

class ShortestPaths extends Component {
  static get propTypes() {
    return {
      graph: PropTypes.shape({
        nodes: PropTypes.arrayOf(PropTypes.object),
        edges: PropTypes.arrayOf(PropTypes.object),
      }),
    };
  }

  constructor(props) {
    super(props);
    this.state = { zoom: null };
    this._renderGraph = this._renderGraph.bind(this);
  }

  componentDidMount() {
    if (this.props.graph) {
      this._renderGraph(this.refs.container, this.props.graph);
    }
  }

  shouldComponentUpdate(nextProps) {
    return this.props.graph != nextProps.graph;
  }

  componentDidUpdate() {
    if (this.props.graph) {
      this._renderGraph(this.refs.container, this.props.graph);
    }
  }

  componentWillUnmount() {
    if (this.state.zoom) {
      this.state.zoom.on("zoom", null);
    }
  }

  _renderGraph(container, { nodes, edges }) {
    if (!container.firstChild) {
      const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
      svg.setAttribute("id", "graph-svg");
      svg.setAttribute("xlink", "http://www.w3.org/1999/xlink");
      svg.style.width = "100%";
      svg.style.height = "100%";

      const target = document.createElementNS("http://www.w3.org/2000/svg", "g");
      target.setAttribute("id", "graph-target");
      target.style.width = "100%";
      target.style.height = "100%";

      svg.appendChild(target);
      container.appendChild(svg);
    }

    const graph = new dagreD3.Digraph();

    for (let i = 0; i < nodes.length; i++) {
      graph.addNode(nodes[i].id, {
        id: nodes[i].id,
        label: stringifyLabel(nodes[i].label, nodes[i].id),
      });
    }

    for (let i = 0; i < edges.length; i++) {
      graph.addEdge(null, edges[i].from, edges[i].to, {
        label: edges[i].name
      });
    }

    const renderer = new dagreD3.Renderer();
    renderer.drawNodes();
    renderer.drawEdgePaths();

    const svg = d3.select("#graph-svg");
    const target = d3.select("#graph-target");

    let zoom = this.state.zoom;
    if (!zoom) {
      zoom = d3.behavior.zoom().on("zoom", function () {
        target.attr(
          "transform",
          `translate(${d3.event.translate}) scale(${d3.event.scale})`
        );
      });
      svg.call(zoom);
      this.setState({ zoom });
    }

    const { translate, scale } = GRAPH_DEFAULTS;
    zoom.scale(scale);
    zoom.translate(translate);
    target.attr("transform", `translate(${translate}) scale(${scale})`);

    const layout = dagreD3.layout();
    renderer.layout(layout).run(graph, target);
  }

  render() {
    let contents;
    if (this.props.graph) {
      // Let the componentDidMount or componentDidUpdate method draw the graph
      // with DagreD3. We just provide the container for the graph here.
      contents = dom.div({
        ref: "container",
        style: {
          flex: 1,
          height: "100%",
          width: "100%",
        }
      });
    } else {
      contents = dom.div(
        {
          id: "shortest-paths-select-node-msg"
        },
        L10N.getStr("shortest-paths.select-node")
      );
    }

    return dom.div(
      {
        id: "shortest-paths",
        className: "vbox",
      },
      dom.label(
        {
          id: "shortest-paths-header",
          className: "header",
        },
        L10N.getStr("shortest-paths.header")
      ),
      contents
    );
  }
}

module.exports = ShortestPaths;
PK
!<D�rMchrome/devtools/modules/devtools/client/memory/components/SnapshotListItem.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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 {
  L10N,
  getSnapshotTitle,
  getSnapshotTotals,
  getStatusText,
  snapshotIsDiffable,
  getSavedCensus
} = require("../utils");
const { diffingState } = require("../constants");
const { snapshot: snapshotModel, app: appModel } = require("../models");

class SnapshotListItem extends Component {
  static get propTypes() {
    return {
      onClick: PropTypes.func.isRequired,
      onSave: PropTypes.func.isRequired,
      onDelete: PropTypes.func.isRequired,
      item: snapshotModel.isRequired,
      index: PropTypes.number.isRequired,
      diffing: appModel.diffing,
    };
  }

  render() {
    let { item: snapshot, onClick, onSave, onDelete, diffing } = this.props;
    let className = `snapshot-list-item ${snapshot.selected ? " selected" : ""}`;
    let statusText = getStatusText(snapshot.state);
    let wantThrobber = !!statusText;
    let title = getSnapshotTitle(snapshot);

    const selectedForDiffing = diffing
          && (diffing.firstSnapshotId === snapshot.id
              || diffing.secondSnapshotId === snapshot.id);

    let checkbox;
    if (diffing && snapshotIsDiffable(snapshot)) {
      if (diffing.state === diffingState.SELECTING) {
        wantThrobber = false;
      }

      const checkboxAttrs = {
        type: "checkbox",
        checked: false,
      };

      if (selectedForDiffing) {
        checkboxAttrs.checked = true;
        checkboxAttrs.disabled = true;
        className += " selected";
        statusText = L10N.getStr(diffing.firstSnapshotId === snapshot.id
                                   ? "diffing.baseline"
                                   : "diffing.comparison");
      }

      if (selectedForDiffing || diffing.state == diffingState.SELECTING) {
        checkbox = dom.input(checkboxAttrs);
      }
    }

    let details;
    if (!selectedForDiffing) {
      // See if a tree map or census is in the read state.
      let census = getSavedCensus(snapshot);

      // If there is census data, fill in the total bytes.
      if (census) {
        let { bytes } = getSnapshotTotals(census);
        let formatBytes = L10N.getFormatStr("aggregate.mb",
          L10N.numberWithDecimals(bytes / 1000000, 2));

        details = dom.span({ className: "snapshot-totals" },
          dom.span({ className: "total-bytes" }, formatBytes)
        );
      }
    }
    if (!details) {
      details = dom.span({ className: "snapshot-state" }, statusText);
    }

    let saveLink = !snapshot.path ? void 0 : dom.a({
      onClick: () => onSave(snapshot),
      className: "save",
    }, L10N.getStr("snapshot.io.save"));

    let deleteButton = !snapshot.path ? void 0 : dom.button({
      onClick: () => onDelete(snapshot),
      className: "delete",
      title: L10N.getStr("snapshot.io.delete")
    });

    return (
      dom.li({ className, onClick },
        dom.span({
          className: `snapshot-title ${wantThrobber ? " devtools-throbber" : ""}`
        },
          checkbox,
          title,
          deleteButton
        ),
        dom.span({ className: "snapshot-info" },
          details,
          saveLink
        )
      )
    );
  }
}

module.exports = SnapshotListItem;
PK
!<}F��$�$Dchrome/devtools/modules/devtools/client/memory/components/Toolbar.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { assert } = require("devtools/shared/DevToolsUtils");
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 { L10N } = require("../utils");
const models = require("../models");
const { viewState } = require("../constants");

class Toolbar extends Component {
  static get propTypes() {
    return {
      censusDisplays: PropTypes.arrayOf(PropTypes.shape({
        displayName: PropTypes.string.isRequired,
      })).isRequired,
      censusDisplay: PropTypes.shape({
        displayName: PropTypes.string.isRequired,
      }).isRequired,
      onTakeSnapshotClick: PropTypes.func.isRequired,
      onImportClick: PropTypes.func.isRequired,
      onClearSnapshotsClick: PropTypes.func.isRequired,
      onCensusDisplayChange: PropTypes.func.isRequired,
      onToggleRecordAllocationStacks: PropTypes.func.isRequired,
      allocations: models.allocations,
      filterString: PropTypes.string,
      setFilterString: PropTypes.func.isRequired,
      diffing: models.diffingModel,
      onToggleDiffing: PropTypes.func.isRequired,
      view: models.view.isRequired,
      onViewChange: PropTypes.func.isRequired,
      labelDisplays: PropTypes.arrayOf(PropTypes.shape({
        displayName: PropTypes.string.isRequired,
      })).isRequired,
      labelDisplay: PropTypes.shape({
        displayName: PropTypes.string.isRequired,
      }).isRequired,
      onLabelDisplayChange: PropTypes.func.isRequired,
      treeMapDisplays: PropTypes.arrayOf(PropTypes.shape({
        displayName: PropTypes.string.isRequired,
      })).isRequired,
      onTreeMapDisplayChange: PropTypes.func.isRequired,
      snapshots: PropTypes.arrayOf(models.snapshot).isRequired,
    };
  }

  render() {
    let {
      onTakeSnapshotClick,
      onImportClick,
      onClearSnapshotsClick,
      onCensusDisplayChange,
      censusDisplays,
      censusDisplay,
      labelDisplays,
      labelDisplay,
      onLabelDisplayChange,
      treeMapDisplays,
      onTreeMapDisplayChange,
      onToggleRecordAllocationStacks,
      allocations,
      filterString,
      setFilterString,
      snapshots,
      diffing,
      onToggleDiffing,
      view,
      onViewChange,
    } = this.props;

    let viewToolbarOptions;
    if (view.state == viewState.CENSUS || view.state === viewState.DIFFING) {
      viewToolbarOptions = dom.div(
        {
          className: "toolbar-group"
        },

        dom.label(
          {
            className: "display-by",
            title: L10N.getStr("toolbar.displayBy.tooltip"),
          },
          L10N.getStr("toolbar.displayBy"),
          dom.select(
            {
              id: "select-display",
              className: "select-display",
              onChange: e => {
                const newDisplay =
                  censusDisplays.find(b => b.displayName === e.target.value);
                onCensusDisplayChange(newDisplay);
              },
              value: censusDisplay.displayName,
            },
            censusDisplays.map(({ tooltip, displayName }) => dom.option(
              {
                key: `display-${displayName}`,
                value: displayName,
                title: tooltip,
              },
              displayName
            ))
          )
        ),

        dom.div({ id: "toolbar-spacer", className: "spacer" }),

        dom.input({
          id: "filter",
          type: "search",
          className: "devtools-filterinput",
          placeholder: L10N.getStr("filter.placeholder"),
          title: L10N.getStr("filter.tooltip"),
          onChange: event => setFilterString(event.target.value),
          value: filterString || undefined,
        })
      );
    } else if (view.state == viewState.TREE_MAP) {
      assert(treeMapDisplays.length >= 1,
       "Should always have at least one tree map display");

      // Only show the dropdown if there are multiple display options
      viewToolbarOptions = treeMapDisplays.length > 1
        ? dom.div(
          {
            className: "toolbar-group"
          },

            dom.label(
              {
                className: "display-by",
                title: L10N.getStr("toolbar.displayBy.tooltip"),
              },
              L10N.getStr("toolbar.displayBy"),
              dom.select(
                {
                  id: "select-tree-map-display",
                  onChange: e => {
                    const newDisplay =
                      treeMapDisplays.find(b => b.displayName === e.target.value);
                    onTreeMapDisplayChange(newDisplay);
                  },
                },
                treeMapDisplays.map(({ tooltip, displayName }) => dom.option(
                  {
                    key: `tree-map-display-${displayName}`,
                    value: displayName,
                    title: tooltip,
                  },
                  displayName
                ))
              )
            )
          )
        : null;
    } else {
      assert(view.state === viewState.DOMINATOR_TREE ||
             view.state === viewState.INDIVIDUALS);

      viewToolbarOptions = dom.div(
        {
          className: "toolbar-group"
        },

        dom.label(
          {
            className: "label-by",
            title: L10N.getStr("toolbar.labelBy.tooltip"),
          },
          L10N.getStr("toolbar.labelBy"),
          dom.select(
            {
              id: "select-label-display",
              onChange: e => {
                const newDisplay =
                  labelDisplays.find(b => b.displayName === e.target.value);
                onLabelDisplayChange(newDisplay);
              },
              value: labelDisplay.displayName,
            },
            labelDisplays.map(({ tooltip, displayName }) => dom.option(
              {
                key: `label-display-${displayName}`,
                value: displayName,
                title: tooltip,
              },
              displayName
            ))
          )
        )
      );
    }

    let viewSelect;
    if (view.state !== viewState.DIFFING && view.state !== viewState.INDIVIDUALS) {
      viewSelect = dom.label(
        {
          title: L10N.getStr("toolbar.view.tooltip"),
        },
        L10N.getStr("toolbar.view"),
        dom.select(
          {
            id: "select-view",
            onChange: e => onViewChange(e.target.value),
            value: view.state,
          },
          dom.option(
            {
              value: viewState.TREE_MAP,
              title: L10N.getStr("toolbar.view.treemap.tooltip"),
            },
            L10N.getStr("toolbar.view.treemap")
          ),
          dom.option(
            {
              value: viewState.CENSUS,
              title: L10N.getStr("toolbar.view.census.tooltip"),
            },
            L10N.getStr("toolbar.view.census")
          ),
          dom.option(
            {
              value: viewState.DOMINATOR_TREE,
              title: L10N.getStr("toolbar.view.dominators.tooltip"),
            },
            L10N.getStr("toolbar.view.dominators")
          )
        )
      );
    }

    return (
      dom.div(
        {
          className: "devtools-toolbar"
        },

        dom.div(
          {
            className: "toolbar-group"
          },

          dom.button({
            id: "clear-snapshots",
            className: "clear-snapshots devtools-button",
            disabled: !snapshots.length,
            onClick: onClearSnapshotsClick,
            title: L10N.getStr("clear-snapshots.tooltip")
          }),

          dom.button({
            id: "take-snapshot",
            className: "take-snapshot devtools-button",
            onClick: onTakeSnapshotClick,
            title: L10N.getStr("take-snapshot")
          }),

          dom.button(
            {
              id: "diff-snapshots",
              className: "devtools-button devtools-monospace" +
                         (diffing ? " checked" : ""),
              disabled: snapshots.length < 2,
              onClick: onToggleDiffing,
              title: L10N.getStr("diff-snapshots.tooltip"),
            }
          ),

          dom.button(
            {
              id: "import-snapshot",
              className: "import-snapshot devtools-button",
              onClick: onImportClick,
              title: L10N.getStr("import-snapshot"),
            }
          )
        ),

        dom.label(
          {
            id: "record-allocation-stacks-label",
            title: L10N.getStr("checkbox.recordAllocationStacks.tooltip"),
          },
          dom.input({
            id: "record-allocation-stacks-checkbox",
            type: "checkbox",
            checked: allocations.recording,
            disabled: allocations.togglingInProgress,
            onChange: onToggleRecordAllocationStacks,
          }),
          L10N.getStr("checkbox.recordAllocationStacks")
        ),

        viewSelect,
        viewToolbarOptions
      )
    );
  }
}

module.exports = Toolbar;
PK
!<��4o~~Dchrome/devtools/modules/devtools/client/memory/components/TreeMap.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/vendor/react");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const { treeMapModel } = require("../models");
const startVisualization = require("./tree-map/start");

class TreeMap extends Component {
  static get propTypes() {
    return {
      treeMap: treeMapModel
    };
  }

  constructor(props) {
    super(props);
    this.state = {};
    this._stopVisualization = this._stopVisualization.bind(this);
    this._startVisualization = this._startVisualization.bind(this);
  }

  componentDidMount() {
    const { treeMap } = this.props;
    if (treeMap && treeMap.report) {
      this._startVisualization();
    }
  }

  shouldComponentUpdate(nextProps) {
    const oldTreeMap = this.props.treeMap;
    const newTreeMap = nextProps.treeMap;
    return oldTreeMap !== newTreeMap;
  }

  componentDidUpdate(prevProps) {
    this._stopVisualization();

    if (this.props.treeMap && this.props.treeMap.report) {
      this._startVisualization();
    }
  }

  componentWillUnmount() {
    if (this.state.stopVisualization) {
      this.state.stopVisualization();
    }
  }

  _stopVisualization() {
    if (this.state.stopVisualization) {
      this.state.stopVisualization();
      this.setState({ stopVisualization: null });
    }
  }

  _startVisualization() {
    const { container } = this.refs;
    const { report } = this.props.treeMap;
    const stopVisualization = startVisualization(container, report);
    this.setState({ stopVisualization });
  }

  render() {
    return dom.div(
      {
        ref: "container",
        className: "tree-map-container"
      }
    );
  }
}

module.exports = TreeMap;
PK
!<Ӡ�Rchrome/devtools/modules/devtools/client/memory/components/tree-map/canvas-utils.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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";

/**
 * Create 2 canvases and contexts for drawing onto, 1 main canvas, and 1 zoom
 * canvas. The main canvas dimensions match the parent div, but the CSS can be
 * transformed to be zoomed and dragged around (potentially creating a blurry
 * canvas once zoomed in). The zoom canvas is a zoomed in section that matches
 * the parent div's dimensions and is kept in place through CSS. A zoomed in
 * view of the visualization is drawn onto this canvas, providing a crisp zoomed
 * in view of the tree map.
 */
const { debounce } = require("devtools/shared/debounce");
const EventEmitter = require("devtools/shared/old-event-emitter");

const HTML_NS = "http://www.w3.org/1999/xhtml";
const FULLSCREEN_STYLE = {
  width: "100%",
  height: "100%",
  position: "absolute",
};

/**
 * Create the canvases, resize handlers, and return references to them all
 *
 * @param  {HTMLDivElement} parentEl
 * @param  {Number} debounceRate
 * @return {Object}
 */
function Canvases(parentEl, debounceRate) {
  EventEmitter.decorate(this);
  this.container = createContainingDiv(parentEl);

  // This canvas contains all of the treemap
  this.main = createCanvas(this.container, "main");
  // This canvas contains only the zoomed in portion, overlaying the main canvas
  this.zoom = createCanvas(this.container, "zoom");

  this.removeHandlers = handleResizes(this, debounceRate);
}

Canvases.prototype = {

  /**
   * Remove the handlers and elements
   *
   * @return {type}  description
   */
  destroy: function () {
    this.removeHandlers();
    this.container.removeChild(this.main.canvas);
    this.container.removeChild(this.zoom.canvas);
  }
};

module.exports = Canvases;

/**
 * Create the containing div
 *
 * @param  {HTMLDivElement} parentEl
 * @return {HTMLDivElement}
 */
function createContainingDiv(parentEl) {
  let div = parentEl.ownerDocument.createElementNS(HTML_NS, "div");
  Object.assign(div.style, FULLSCREEN_STYLE);
  parentEl.appendChild(div);
  return div;
}

/**
 * Create a canvas and context
 *
 * @param  {HTMLDivElement} container
 * @param  {String} className
 * @return {Object} { canvas, ctx }
 */
function createCanvas(container, className) {
  let window = container.ownerDocument.defaultView;
  let canvas = container.ownerDocument.createElementNS(HTML_NS, "canvas");
  container.appendChild(canvas);
  canvas.width = container.offsetWidth * window.devicePixelRatio;
  canvas.height = container.offsetHeight * window.devicePixelRatio;
  canvas.className = className;

  Object.assign(canvas.style, FULLSCREEN_STYLE, {
    pointerEvents: "none"
  });

  let ctx = canvas.getContext("2d");

  return { canvas, ctx };
}

/**
 * Resize the canvases' resolutions, and fires out the onResize callback
 *
 * @param  {HTMLDivElement} container
 * @param  {Object} canvases
 * @param  {Number} debounceRate
 */
function handleResizes(canvases, debounceRate) {
  let { container, main, zoom } = canvases;
  let window = container.ownerDocument.defaultView;

  function resize() {
    let width = container.offsetWidth * window.devicePixelRatio;
    let height = container.offsetHeight * window.devicePixelRatio;

    main.canvas.width = width;
    main.canvas.height = height;
    zoom.canvas.width = width;
    zoom.canvas.height = height;

    canvases.emit("resize");
  }

  // Tests may not need debouncing
  let debouncedResize = debounceRate > 0
    ? debounce(resize, debounceRate)
    : resize;

  window.addEventListener("resize", debouncedResize);
  resize();

  return function removeResizeHandlers() {
    window.removeEventListener("resize", debouncedResize);
  };
}
PK
!< �Wchrome/devtools/modules/devtools/client/memory/components/tree-map/color-coarse-type.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

/**
 * Color the boxes in the treemap
 */

const TYPES = [ "objects", "other", "strings", "scripts" ];

// The factors determine how much the hue shifts
const TYPE_FACTOR = TYPES.length * 3;
const DEPTH_FACTOR = -10;
const H = 0.5;
const S = 0.6;
const L = 0.9;

/**
 * Recursively find the index of the coarse type of a node
 *
 * @param  {Object} node
 *         d3 treemap
 * @return {Integer}
 *         index
 */
function findCoarseTypeIndex(node) {
  let index = TYPES.indexOf(node.name);

  if (node.parent) {
    return index === -1 ? findCoarseTypeIndex(node.parent) : index;
  }

  return TYPES.indexOf("other");
}

/**
 * Decide a color value for depth to be used in the HSL computation
 *
 * @param  {Object} node
 * @return {Number}
 */
function depthColorFactor(node) {
  return Math.min(1, node.depth / DEPTH_FACTOR);
}

/**
 * Decide a color value for type to be used in the HSL computation
 *
 * @param  {Object} node
 * @return {Number}
 */
function typeColorFactor(node) {
  return findCoarseTypeIndex(node) / TYPE_FACTOR;
}

/**
 * Color a node
 *
 * @param  {Object} node
 * @return {Array} HSL values ranged 0-1
 */
module.exports = function colorCoarseType(node) {
  let h = Math.min(1, H + typeColorFactor(node));
  let s = Math.min(1, S);
  let l = Math.min(1, L + depthColorFactor(node));

  return [h, s, l];
};
PK
!<M�`2((Ochrome/devtools/modules/devtools/client/memory/components/tree-map/drag-zoom.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { debounce } = require("devtools/shared/debounce");
const { lerp } = require("devtools/client/memory/utils");
const EventEmitter = require("devtools/shared/old-event-emitter");

const LERP_SPEED = 0.5;
const ZOOM_SPEED = 0.01;
const TRANSLATE_EPSILON = 1;
const ZOOM_EPSILON = 0.001;
const LINE_SCROLL_MODE = 1;
const SCROLL_LINE_SIZE = 15;

/**
 * DragZoom is a constructor that contains the state of the current dragging and
 * zooming behavior. It sets the scrolling and zooming behaviors.
 *
 * @param  {HTMLElement} container description
 *         The container for the canvases
 */
function DragZoom(container, debounceRate, requestAnimationFrame) {
  EventEmitter.decorate(this);

  this.isDragging = false;

  // The current mouse position
  this.mouseX = container.offsetWidth / 2;
  this.mouseY = container.offsetHeight / 2;

  // The total size of the visualization after being zoomed, in pixels
  this.zoomedWidth = container.offsetWidth;
  this.zoomedHeight = container.offsetHeight;

  // How much the visualization has been zoomed in
  this.zoom = 0;

  // The offset of visualization from the container. This is applied after
  // the zoom, and the visualization by default is centered
  this.translateX = 0;
  this.translateY = 0;

  // The size of the offset between the top/left of the container, and the
  // top/left of the containing element. This value takes into account
  // the device pixel ratio for canvas draws.
  this.offsetX = 0;
  this.offsetY = 0;

  // The smoothed values that are animated and eventually match the target
  // values. The values are updated by the update loop
  this.smoothZoom = 0;
  this.smoothTranslateX = 0;
  this.smoothTranslateY = 0;

  // Add the constant values for testing purposes
  this.ZOOM_SPEED = ZOOM_SPEED;
  this.ZOOM_EPSILON = ZOOM_EPSILON;

  let update = createUpdateLoop(container, this, requestAnimationFrame);

  this.destroy = setHandlers(this, container, update, debounceRate);
}

module.exports = DragZoom;

/**
 * Returns an update loop. This loop smoothly updates the visualization when
 * actions are performed. Once the animations have reached their target values
 * the animation loop is stopped.
 *
 * Any value in the `dragZoom` object that starts with "smooth" is the
 * smoothed version of a value that is interpolating toward the target value.
 * For instance `dragZoom.smoothZoom` approaches `dragZoom.zoom` on each
 * iteration of the update loop until it's sufficiently close as defined by
 * the epsilon values.
 *
 * Only these smoothed values and the container CSS are updated by the loop.
 *
 * @param {HTMLDivElement} container
 * @param {Object} dragZoom
 *        The values that represent the current dragZoom state
 * @param {Function} requestAnimationFrame
 */
function createUpdateLoop(container, dragZoom, requestAnimationFrame) {
  let isLooping = false;

  function update() {
    let isScrollChanging = (
      Math.abs(dragZoom.smoothZoom - dragZoom.zoom) > ZOOM_EPSILON
    );
    let isTranslateChanging = (
      Math.abs(dragZoom.smoothTranslateX - dragZoom.translateX)
      > TRANSLATE_EPSILON ||
      Math.abs(dragZoom.smoothTranslateY - dragZoom.translateY)
      > TRANSLATE_EPSILON
    );

    isLooping = isScrollChanging || isTranslateChanging;

    if (isScrollChanging) {
      dragZoom.smoothZoom = lerp(dragZoom.smoothZoom, dragZoom.zoom,
                                 LERP_SPEED);
    } else {
      dragZoom.smoothZoom = dragZoom.zoom;
    }

    if (isTranslateChanging) {
      dragZoom.smoothTranslateX = lerp(dragZoom.smoothTranslateX,
                                       dragZoom.translateX, LERP_SPEED);
      dragZoom.smoothTranslateY = lerp(dragZoom.smoothTranslateY,
                                       dragZoom.translateY, LERP_SPEED);
    } else {
      dragZoom.smoothTranslateX = dragZoom.translateX;
      dragZoom.smoothTranslateY = dragZoom.translateY;
    }

    let zoom = 1 + dragZoom.smoothZoom;
    let x = dragZoom.smoothTranslateX;
    let y = dragZoom.smoothTranslateY;
    container.style.transform = `translate(${x}px, ${y}px) scale(${zoom})`;

    if (isLooping) {
      requestAnimationFrame(update);
    }
  }

  // Go ahead and start the update loop
  update();

  return function restartLoopingIfStopped() {
    if (!isLooping) {
      update();
    }
  };
}

/**
 * Set the various event listeners and return a function to remove them
 *
 * @param  {Object} dragZoom
 * @param  {HTMLElement} container
 * @param  {Function} update
 * @return {Function}  The function to remove the handlers
 */
function setHandlers(dragZoom, container, update, debounceRate) {
  let emitChanged = debounce(() => dragZoom.emit("change"), debounceRate);

  let removeDragHandlers =
    setDragHandlers(container, dragZoom, emitChanged, update);
  let removeScrollHandlers =
    setScrollHandlers(container, dragZoom, emitChanged, update);

  return function removeHandlers() {
    removeDragHandlers();
    removeScrollHandlers();
  };
}

/**
 * Sets handlers for when the user drags on the canvas. It will update dragZoom
 * object with new translate and offset values.
 *
 * @param  {HTMLElement} container
 * @param  {Object} dragZoom
 * @param  {Function} changed
 * @param  {Function} update
 */
function setDragHandlers(container, dragZoom, emitChanged, update) {
  let parentEl = container.parentElement;

  function startDrag() {
    dragZoom.isDragging = true;
    container.style.cursor = "grabbing";
  }

  function stopDrag() {
    dragZoom.isDragging = false;
    container.style.cursor = "grab";
  }

  function drag(event) {
    let prevMouseX = dragZoom.mouseX;
    let prevMouseY = dragZoom.mouseY;

    dragZoom.mouseX = event.clientX - parentEl.offsetLeft;
    dragZoom.mouseY = event.clientY - parentEl.offsetTop;

    if (!dragZoom.isDragging) {
      return;
    }

    dragZoom.translateX += dragZoom.mouseX - prevMouseX;
    dragZoom.translateY += dragZoom.mouseY - prevMouseY;

    keepInView(container, dragZoom);

    emitChanged();
    update();
  }

  parentEl.addEventListener("mousedown", startDrag);
  parentEl.addEventListener("mouseup", stopDrag);
  parentEl.addEventListener("mouseout", stopDrag);
  parentEl.addEventListener("mousemove", drag);

  return function removeListeners() {
    parentEl.removeEventListener("mousedown", startDrag);
    parentEl.removeEventListener("mouseup", stopDrag);
    parentEl.removeEventListener("mouseout", stopDrag);
    parentEl.removeEventListener("mousemove", drag);
  };
}

/**
 * Sets the handlers for when the user scrolls. It updates the dragZoom object
 * and keeps the canvases all within the view. After changing values update
 * loop is called, and the changed event is emitted.
 *
 * @param  {HTMLDivElement} container
 * @param  {Object} dragZoom
 * @param  {Function} changed
 * @param  {Function} update
 */
function setScrollHandlers(container, dragZoom, emitChanged, update) {
  let window = container.ownerDocument.defaultView;

  function handleWheel(event) {
    event.preventDefault();

    if (dragZoom.isDragging) {
      return;
    }

    // Update the zoom level
    let scrollDelta = getScrollDelta(event, window);
    let prevZoom = dragZoom.zoom;
    dragZoom.zoom = Math.max(0, dragZoom.zoom - scrollDelta * ZOOM_SPEED);

    // Calculate the updated width and height
    let prevZoomedWidth = container.offsetWidth * (1 + prevZoom);
    let prevZoomedHeight = container.offsetHeight * (1 + prevZoom);
    dragZoom.zoomedWidth = container.offsetWidth * (1 + dragZoom.zoom);
    dragZoom.zoomedHeight = container.offsetHeight * (1 + dragZoom.zoom);
    let deltaWidth = dragZoom.zoomedWidth - prevZoomedWidth;
    let deltaHeight = dragZoom.zoomedHeight - prevZoomedHeight;

    let mouseOffsetX = dragZoom.mouseX - container.offsetWidth / 2;
    let mouseOffsetY = dragZoom.mouseY - container.offsetHeight / 2;

    // The ratio of where the center of the mouse is in regards to the total
    // zoomed width/height
    let ratioZoomX = (prevZoomedWidth / 2 + mouseOffsetX - dragZoom.translateX)
      / prevZoomedWidth;
    let ratioZoomY = (prevZoomedHeight / 2 + mouseOffsetY - dragZoom.translateY)
      / prevZoomedHeight;

    // Distribute the change in width and height based on the above ratio
    dragZoom.translateX -= lerp(-deltaWidth / 2, deltaWidth / 2, ratioZoomX);
    dragZoom.translateY -= lerp(-deltaHeight / 2, deltaHeight / 2, ratioZoomY);

    // Keep the canvas in range of the container
    keepInView(container, dragZoom);
    emitChanged();
    update();
  }

  container.addEventListener("wheel", handleWheel);

  return function removeListener() {
    container.removeEventListener("wheel", handleWheel);
  };
}

/**
 * Account for the various mouse wheel event types, per pixel or per line
 *
 * @param  {WheelEvent} event
 * @param  {Window} window
 * @return {Number} The scroll size in pixels
 */
function getScrollDelta(event, window) {
  if (event.deltaMode === LINE_SCROLL_MODE) {
    // Update by a fixed arbitrary value to normalize scroll types
    return event.deltaY * SCROLL_LINE_SIZE;
  }
  return event.deltaY;
}

/**
 * Keep the dragging and zooming within the view by updating the values in the
 * `dragZoom` object.
 *
 * @param  {HTMLDivElement} container
 * @param  {Object} dragZoom
 */
function keepInView(container, dragZoom) {
  let { devicePixelRatio } = container.ownerDocument.defaultView;
  let overdrawX = (dragZoom.zoomedWidth - container.offsetWidth) / 2;
  let overdrawY = (dragZoom.zoomedHeight - container.offsetHeight) / 2;

  dragZoom.translateX = Math.max(-overdrawX,
                                 Math.min(overdrawX, dragZoom.translateX));
  dragZoom.translateY = Math.max(-overdrawY,
                                 Math.min(overdrawY, dragZoom.translateY));

  dragZoom.offsetX = devicePixelRatio * (
    (dragZoom.zoomedWidth - container.offsetWidth) / 2 - dragZoom.translateX
  );
  dragZoom.offsetY = devicePixelRatio * (
    (dragZoom.zoomedHeight - container.offsetHeight) / 2 - dragZoom.translateY
  );
}
PK
!<��k�?&?&Jchrome/devtools/modules/devtools/client/memory/components/tree-map/draw.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
/**
 * Draw the treemap into the provided canvases using the 2d context. The treemap
 * layout is computed with d3. There are 2 canvases provided, each matching
 * the resolution of the window. The main canvas is a fully drawn version of
 * the treemap that is positioned and zoomed using css. It gets blurry the more
 * you zoom in as it doesn't get redrawn when zooming. The zoom canvas is
 * repositioned absolutely after every change in the dragZoom object, and then
 * redrawn to provide a full-resolution (non-blurry) view of zoomed in segment
 * of the treemap.
 */

const colorCoarseType = require("./color-coarse-type");
const {
  hslToStyle,
  formatAbbreviatedBytes,
  L10N
} = require("devtools/client/memory/utils");

// A constant fully zoomed out dragZoom object for the main canvas
const NO_SCROLL = {
  translateX: 0,
  translateY: 0,
  zoom: 0,
  offsetX: 0,
  offsetY: 0
};

// Drawing constants
const ELLIPSIS = "...";
const TEXT_MARGIN = 2;
const TEXT_COLOR = "#000000";
const TEXT_LIGHT_COLOR = "rgba(0,0,0,0.5)";
const LINE_WIDTH = 1;
const FONT_SIZE = 10;
const FONT_LINE_HEIGHT = 2;
const PADDING = [5 + FONT_SIZE, 5, 5, 5];
const COUNT_LABEL = L10N.getStr("tree-map.node-count");

/**
 * Setup and start drawing the treemap visualization
 *
 * @param  {Object} report
 * @param  {Object} canvases
 *         A CanvasUtils object that contains references to the main and zoom
 *         canvases and contexts
 * @param  {Object} dragZoom
 *         A DragZoom object representing the current state of the dragging
 *         and zooming behavior
 */
exports.setupDraw = function (report, canvases, dragZoom) {
  let getTreemap = configureD3Treemap.bind(null, canvases.main.canvas);

  let treemap, nodes;

  function drawFullTreemap() {
    treemap = getTreemap();
    nodes = treemap(report);
    drawTreemap(canvases.main, nodes, NO_SCROLL);
    drawTreemap(canvases.zoom, nodes, dragZoom);
  }

  function drawZoomedTreemap() {
    drawTreemap(canvases.zoom, nodes, dragZoom);
    positionZoomedCanvas(canvases.zoom.canvas, dragZoom);
  }

  drawFullTreemap();
  canvases.on("resize", drawFullTreemap);
  dragZoom.on("change", drawZoomedTreemap);
};

/**
 * Returns a configured d3 treemap function
 *
 * @param  {HTMLCanvasElement} canvas
 * @return {Function}
 */
const configureD3Treemap = exports.configureD3Treemap = function (canvas) {
  let window = canvas.ownerDocument.defaultView;
  let ratio = window.devicePixelRatio;
  let treemap = window.d3.layout.treemap()
    .size([
      // The d3 layout includes the padding around everything, add some
      // extra padding to the size to compensate for thi
      canvas.width + (PADDING[1] + PADDING[3]) * ratio,
      canvas.height + (PADDING[0] + PADDING[2]) * ratio
    ])
    .sticky(true)
    .padding([
      PADDING[0] * ratio,
      PADDING[1] * ratio,
      PADDING[2] * ratio,
      PADDING[3] * ratio,
    ])
    .value(d => d.bytes);

  /**
   * Create treemap nodes from a census report that are sorted by depth
   *
   * @param  {Object} report
   * @return {Array} An array of d3 treemap nodes
   *         // https://github.com/mbostock/d3/wiki/Treemap-Layout
   *         parent - the parent node, or null for the root.
   *         children - the array of child nodes, or null for leaf nodes.
   *         value - the node value, as returned by the value accessor.
   *         depth - the depth of the node, starting at 0 for the root.
   *         area - the computed pixel area of this node.
   *         x - the minimum x-coordinate of the node position.
   *         y - the minimum y-coordinate of the node position.
   *         z - the orientation of this cell’s subdivision, if any.
   *         dx - the x-extent of the node position.
   *         dy - the y-extent of the node position.
   */
  return function depthSortedNodes(report) {
    let nodes = treemap(report);
    nodes.sort((a, b) => a.depth - b.depth);
    return nodes;
  };
};

/**
 * Draw the text, cut it in half every time it doesn't fit until it fits or
 * it's smaller than the "..." text.
 *
 * @param  {CanvasRenderingContext2D} ctx
 * @param  {Number} x
 *         the position of the text
 * @param  {Number} y
 *         the position of the text
 * @param  {Number} innerWidth
 *         the inner width of the containing treemap cell
 * @param  {Text} name
 */
const drawTruncatedName = exports.drawTruncatedName = function (ctx, x, y,
                                                               innerWidth,
                                                               name) {
  let truncated = name.substr(0, Math.floor(name.length / 2));
  let formatted = truncated + ELLIPSIS;

  if (ctx.measureText(formatted).width > innerWidth) {
    drawTruncatedName(ctx, x, y, innerWidth, truncated);
  } else {
    ctx.fillText(formatted, x, y);
  }
};

/**
 * Fit and draw the text in a node with the following strategies to shrink
 * down the text size:
 *
 * Function 608KB 9083 count
 * Function
 * Func...
 * Fu...
 * ...
 *
 * @param  {CanvasRenderingContext2D} ctx
 * @param  {Object} node
 * @param  {Number} borderWidth
 * @param  {Object} dragZoom
 * @param  {Array}  padding
 */
const drawText = exports.drawText = function (ctx, node, borderWidth, ratio,
                                              dragZoom, padding) {
  let { dx, dy, name, totalBytes, totalCount } = node;
  let scale = dragZoom.zoom + 1;
  dx *= scale;
  dy *= scale;

  // Start checking to see how much text we can fit in, optimizing for the
  // common case of lots of small leaf nodes
  if (FONT_SIZE * FONT_LINE_HEIGHT < dy) {
    let margin = borderWidth(node) * 1.5 + ratio * TEXT_MARGIN;
    let x = margin + (node.x - padding[0]) * scale - dragZoom.offsetX;
    let y = margin + (node.y - padding[1]) * scale - dragZoom.offsetY;
    let innerWidth = dx - margin * 2;
    let nameSize = ctx.measureText(name).width;

    if (ctx.measureText(ELLIPSIS).width > innerWidth) {
      return;
    }

    ctx.fillStyle = TEXT_COLOR;

    if (nameSize > innerWidth) {
      // The name is too long - halve the name as an expediant way to shorten it
      drawTruncatedName(ctx, x, y, innerWidth, name);
    } else {
      let bytesFormatted = formatAbbreviatedBytes(totalBytes);
      let countFormatted = `${totalCount} ${COUNT_LABEL}`;
      let byteSize = ctx.measureText(bytesFormatted).width;
      let countSize = ctx.measureText(countFormatted).width;
      let spaceSize = ctx.measureText(" ").width;

      if (nameSize + byteSize + countSize + spaceSize * 3 > innerWidth) {
        // The full name will fit
        ctx.fillText(`${name}`, x, y);
      } else {
        // The full name plus the byte information will fit
        ctx.fillText(name, x, y);
        ctx.fillStyle = TEXT_LIGHT_COLOR;
        ctx.fillText(`${bytesFormatted} ${countFormatted}`,
          x + nameSize + spaceSize, y);
      }
    }
  }
};

/**
 * Draw a box given a node
 *
 * @param  {CanvasRenderingContext2D} ctx
 * @param  {Object} node
 * @param  {Number} borderWidth
 * @param  {Number} ratio
 * @param  {Object} dragZoom
 * @param  {Array}  padding
 */
const drawBox = exports.drawBox = function (ctx, node, borderWidth, dragZoom,
                                           padding) {
  let border = borderWidth(node);
  let fillHSL = colorCoarseType(node);
  let strokeHSL = [fillHSL[0], fillHSL[1], fillHSL[2] * 0.5];
  let scale = 1 + dragZoom.zoom;

  // Offset the draw so that box strokes don't overlap
  let x = scale * (node.x - padding[0]) - dragZoom.offsetX + border / 2;
  let y = scale * (node.y - padding[1]) - dragZoom.offsetY + border / 2;
  let dx = scale * node.dx - border;
  let dy = scale * node.dy - border;

  ctx.fillStyle = hslToStyle(...fillHSL);
  ctx.fillRect(x, y, dx, dy);

  ctx.strokeStyle = hslToStyle(...strokeHSL);
  ctx.lineWidth = border;
  ctx.strokeRect(x, y, dx, dy);
};

/**
 * Draw the overall treemap
 *
 * @param  {HTMLCanvasElement} canvas
 * @param  {CanvasRenderingContext2D} ctx
 * @param  {Array} nodes
 * @param  {Objbect} dragZoom
 */
const drawTreemap = exports.drawTreemap = function ({canvas, ctx}, nodes,
                                                   dragZoom) {
  let window = canvas.ownerDocument.defaultView;
  let ratio = window.devicePixelRatio;
  let canvasArea = canvas.width * canvas.height;
  // Subtract the outer padding from the tree map layout.
  let padding = [PADDING[3] * ratio, PADDING[0] * ratio];

  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.font = `${FONT_SIZE * ratio}px sans-serif`;
  ctx.textBaseline = "top";

  function borderWidth(node) {
    let areaRatio = Math.sqrt(node.area / canvasArea);
    return ratio * Math.max(1, LINE_WIDTH * areaRatio);
  }

  for (let i = 0; i < nodes.length; i++) {
    let node = nodes[i];
    if (node.parent === undefined) {
      continue;
    }

    drawBox(ctx, node, borderWidth, dragZoom, padding);
    drawText(ctx, node, borderWidth, ratio, dragZoom, padding);
  }
};

/**
 * Set the position of the zoomed in canvas. It always take up 100% of the view
 * window, but is transformed relative to the zoomed in containing element,
 * essentially reversing the transform of the containing element.
 *
 * @param  {HTMLCanvasElement} canvas
 * @param  {Object} dragZoom
 */
const positionZoomedCanvas = function (canvas, dragZoom) {
  let scale = 1 / (1 + dragZoom.zoom);
  let x = -dragZoom.translateX;
  let y = -dragZoom.translateY;
  canvas.style.transform = `scale(${scale}) translate(${x}px, ${y}px)`;
};

exports.positionZoomedCanvas = positionZoomedCanvas;
PK
!<��Kchrome/devtools/modules/devtools/client/memory/components/tree-map/start.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { setupDraw } = require("./draw");
const DragZoom = require("./drag-zoom");
const CanvasUtils = require("./canvas-utils");

/**
 * Start the tree map visualization
 *
 * @param  {HTMLDivElement} container
 * @param  {Object} report
 *                  the report from a census
 * @param  {Number} debounceRate
 */
module.exports = function startVisualization(parentEl, report,
                                              debounceRate = 60) {
  let window = parentEl.ownerDocument.defaultView;
  let canvases = new CanvasUtils(parentEl, debounceRate);
  let dragZoom = new DragZoom(canvases.container, debounceRate,
                              window.requestAnimationFrame);

  setupDraw(report, canvases, dragZoom);

  return function stopVisualization() {
    canvases.destroy();
    dragZoom.destroy();
  };
};
PK
!<v�u��-�-;chrome/devtools/modules/devtools/client/memory/constants.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

// Options passed to MemoryFront's startRecordingAllocations never change.
exports.ALLOCATION_RECORDING_OPTIONS = {
  probability: 1,
  maxLogLength: 1
};

// If TREE_ROW_HEIGHT changes, be sure to change `var(--heap-tree-row-height)`
// in `devtools/client/themes/memory.css`
exports.TREE_ROW_HEIGHT = 18;

/** * Actions ******************************************************************/

const actions = exports.actions = {};

// Fired by UI to request a snapshot from the actor.
actions.TAKE_SNAPSHOT_START = "take-snapshot-start";
actions.TAKE_SNAPSHOT_END = "take-snapshot-end";

// When a heap snapshot is read into memory -- only fired
// once per snapshot.
actions.READ_SNAPSHOT_START = "read-snapshot-start";
actions.READ_SNAPSHOT_END = "read-snapshot-end";

// When a census is being performed on a heap snapshot
actions.TAKE_CENSUS_START = "take-census-start";
actions.TAKE_CENSUS_END = "take-census-end";
actions.TAKE_CENSUS_ERROR = "take-census-error";

// When a tree map is being calculated on a heap snapshot
actions.TAKE_TREE_MAP_START = "take-tree-map-start";
actions.TAKE_TREE_MAP_END = "take-tree-map-end";
actions.TAKE_TREE_MAP_ERROR = "take-tree-map-error";

// When requesting that the server start/stop recording allocation stacks.
actions.TOGGLE_RECORD_ALLOCATION_STACKS_START = "toggle-record-allocation-stacks-start";
actions.TOGGLE_RECORD_ALLOCATION_STACKS_END = "toggle-record-allocation-stacks-end";

// When a heap snapshot is being saved to a user-specified
// location on disk.
actions.EXPORT_SNAPSHOT_START = "export-snapshot-start";
actions.EXPORT_SNAPSHOT_END = "export-snapshot-end";
actions.EXPORT_SNAPSHOT_ERROR = "export-snapshot-error";

// When a heap snapshot is being read from a user selected file,
// and represents the entire state until the census is available.
actions.IMPORT_SNAPSHOT_START = "import-snapshot-start";
actions.IMPORT_SNAPSHOT_END = "import-snapshot-end";
actions.IMPORT_SNAPSHOT_ERROR = "import-snapshot-error";

// Fired by UI to select a snapshot to view.
actions.SELECT_SNAPSHOT = "select-snapshot";

// Fired to delete a provided list of snapshots
actions.DELETE_SNAPSHOTS_START = "delete-snapshots-start";
actions.DELETE_SNAPSHOTS_END = "delete-snapshots-end";

// Fired to toggle tree inversion on or off.
actions.TOGGLE_INVERTED = "toggle-inverted";

// Fired when a snapshot is selected for diffing.
actions.SELECT_SNAPSHOT_FOR_DIFFING = "select-snapshot-for-diffing";

// Fired when taking a census diff.
actions.TAKE_CENSUS_DIFF_START = "take-census-diff-start";
actions.TAKE_CENSUS_DIFF_END = "take-census-diff-end";
actions.DIFFING_ERROR = "diffing-error";

// Fired to set a new census display.
actions.SET_CENSUS_DISPLAY = "set-census-display";

// Fired to change the display that controls the dominator tree labels.
actions.SET_LABEL_DISPLAY = "set-label-display";

// Fired to set a tree map display
actions.SET_TREE_MAP_DISPLAY = "set-tree-map-display";

// Fired when changing between census or dominators view.
actions.CHANGE_VIEW = "change-view";
actions.POP_VIEW = "pop-view";

// Fired when there is an error processing a snapshot or taking a census.
actions.SNAPSHOT_ERROR = "snapshot-error";

// Fired when there is a new filter string set.
actions.SET_FILTER_STRING = "set-filter-string";

// Fired to expand or collapse nodes in census reports.
actions.EXPAND_CENSUS_NODE = "expand-census-node";
actions.EXPAND_DIFFING_CENSUS_NODE = "expand-diffing-census-node";
actions.COLLAPSE_CENSUS_NODE = "collapse-census-node";
actions.COLLAPSE_DIFFING_CENSUS_NODE = "collapse-diffing-census-node";

// Fired when nodes in various trees are focused.
actions.FOCUS_CENSUS_NODE = "focus-census-node";
actions.FOCUS_DIFFING_CENSUS_NODE = "focus-diffing-census-node";
actions.FOCUS_DOMINATOR_TREE_NODE = "focus-dominator-tree-node";

actions.FOCUS_INDIVIDUAL = "focus-individual";
actions.FETCH_INDIVIDUALS_START = "fetch-individuals-start";
actions.FETCH_INDIVIDUALS_END = "fetch-individuals-end";
actions.INDIVIDUALS_ERROR = "individuals-error";

actions.COMPUTE_DOMINATOR_TREE_START = "compute-dominator-tree-start";
actions.COMPUTE_DOMINATOR_TREE_END = "compute-dominator-tree-end";
actions.FETCH_DOMINATOR_TREE_START = "fetch-dominator-tree-start";
actions.FETCH_DOMINATOR_TREE_END = "fetch-dominator-tree-end";
actions.DOMINATOR_TREE_ERROR = "dominator-tree-error";
actions.FETCH_IMMEDIATELY_DOMINATED_START = "fetch-immediately-dominated-start";
actions.FETCH_IMMEDIATELY_DOMINATED_END = "fetch-immediately-dominated-end";
actions.EXPAND_DOMINATOR_TREE_NODE = "expand-dominator-tree-node";
actions.COLLAPSE_DOMINATOR_TREE_NODE = "collapse-dominator-tree-node";

actions.RESIZE_SHORTEST_PATHS = "resize-shortest-paths";

/** * Census Displays ***************************************************************/

const COUNT = Object.freeze({ by: "count", count: true, bytes: true });
const INTERNAL_TYPE = Object.freeze({ by: "internalType", then: COUNT });
const ALLOCATION_STACK = Object.freeze({
  by: "allocationStack", then: COUNT,
  noStack: COUNT
});
const OBJECT_CLASS = Object.freeze({ by: "objectClass", then: COUNT, other: COUNT });
const COARSE_TYPE = Object.freeze({
  by: "coarseType",
  objects: OBJECT_CLASS,
  strings: COUNT,
  scripts: {
    by: "filename",
    then: INTERNAL_TYPE,
    noFilename: INTERNAL_TYPE
  },
  other: INTERNAL_TYPE,
});

exports.censusDisplays = Object.freeze({
  coarseType: Object.freeze({
    displayName: "Type",
    get tooltip() {
      // Importing down here is necessary because of the circular dependency
      // this introduces with `./utils.js`.
      const { L10N } = require("./utils");
      return L10N.getStr("censusDisplays.coarseType.tooltip");
    },
    inverted: true,
    breakdown: COARSE_TYPE
  }),

  allocationStack: Object.freeze({
    displayName: "Call Stack",
    get tooltip() {
      const { L10N } = require("./utils");
      return L10N.getStr("censusDisplays.allocationStack.tooltip");
    },
    inverted: false,
    breakdown: ALLOCATION_STACK,
  }),

  invertedAllocationStack: Object.freeze({
    displayName: "Inverted Call Stack",
    get tooltip() {
      const { L10N } = require("./utils");
      return L10N.getStr("censusDisplays.invertedAllocationStack.tooltip");
    },
    inverted: true,
    breakdown: ALLOCATION_STACK,
  }),
});

const DOMINATOR_TREE_LABEL_COARSE_TYPE = Object.freeze({
  by: "coarseType",
  objects: OBJECT_CLASS,
  scripts: Object.freeze({
    by: "internalType",
    then: Object.freeze({
      by: "filename",
      then: COUNT,
      noFilename: COUNT,
    }),
  }),
  strings: INTERNAL_TYPE,
  other: INTERNAL_TYPE,
});

exports.labelDisplays = Object.freeze({
  coarseType: Object.freeze({
    displayName: "Type",
    get tooltip() {
      const { L10N } = require("./utils");
      return L10N.getStr("dominatorTreeDisplays.coarseType.tooltip");
    },
    breakdown: DOMINATOR_TREE_LABEL_COARSE_TYPE
  }),

  allocationStack: Object.freeze({
    displayName: "Call Stack",
    get tooltip() {
      const { L10N } = require("./utils");
      return L10N.getStr("dominatorTreeDisplays.allocationStack.tooltip");
    },
    breakdown: Object.freeze({
      by: "allocationStack",
      then: DOMINATOR_TREE_LABEL_COARSE_TYPE,
      noStack: DOMINATOR_TREE_LABEL_COARSE_TYPE,
    }),
  }),
});

exports.treeMapDisplays = Object.freeze({
  coarseType: Object.freeze({
    displayName: "Type",
    get tooltip() {
      const { L10N } = require("./utils");
      return L10N.getStr("treeMapDisplays.coarseType.tooltip");
    },
    breakdown: COARSE_TYPE,
    inverted: false,
  })
});

/** * View States **************************************************************/

/**
 * The various main views that the tool can be in.
 */
const viewState = exports.viewState = Object.create(null);
viewState.CENSUS = "view-state-census";
viewState.DIFFING = "view-state-diffing";
viewState.DOMINATOR_TREE = "view-state-dominator-tree";
viewState.TREE_MAP = "view-state-tree-map";
viewState.INDIVIDUALS = "view-state-individuals";

/** * Snapshot States **********************************************************/

const snapshotState = exports.snapshotState = Object.create(null);

/**
 * Various states a snapshot can be in.
 * An FSM describing snapshot states:
 *
 *     SAVING -> SAVED -> READING -> READ
 *                       ↗
 *              IMPORTING
 *
 * Any of these states may go to the ERROR state, from which they can never
 * leave (mwah ha ha ha!)
 */
snapshotState.ERROR = "snapshot-state-error";
snapshotState.IMPORTING = "snapshot-state-importing";
snapshotState.SAVING = "snapshot-state-saving";
snapshotState.SAVED = "snapshot-state-saved";
snapshotState.READING = "snapshot-state-reading";
snapshotState.READ = "snapshot-state-read";

/*
 * Various states the census model can be in.
 *
 *     SAVING <-> SAVED
 *       |
 *       V
 *     ERROR
 */

const censusState = exports.censusState = Object.create(null);

censusState.SAVING = "census-state-saving";
censusState.SAVED = "census-state-saved";
censusState.ERROR = "census-state-error";

/*
 * Various states the tree map model can be in.
 *
 *     SAVING <-> SAVED
 *       |
 *       V
 *     ERROR
 */

const treeMapState = exports.treeMapState = Object.create(null);

treeMapState.SAVING = "tree-map-state-saving";
treeMapState.SAVED = "tree-map-state-saved";
treeMapState.ERROR = "tree-map-state-error";

/** * Diffing States ***********************************************************/

/*
 * Various states the diffing model can be in.
 *
 *     SELECTING --> TAKING_DIFF <---> TOOK_DIFF
 *                       |
 *                       V
 *                     ERROR
 */
const diffingState = exports.diffingState = Object.create(null);

// Selecting the two snapshots to diff.
diffingState.SELECTING = "diffing-state-selecting";

// Currently computing the diff between the two selected snapshots.
diffingState.TAKING_DIFF = "diffing-state-taking-diff";

// Have the diff between the two selected snapshots.
diffingState.TOOK_DIFF = "diffing-state-took-diff";

// An error occurred while computing the diff.
diffingState.ERROR = "diffing-state-error";

/** * Dominator Tree States ****************************************************/

/*
 * Various states the dominator tree model can be in.
 *
 *     COMPUTING -> COMPUTED -> FETCHING -> LOADED <--> INCREMENTAL_FETCHING
 *
 * Any state may lead to the ERROR state, from which it can never leave.
 */
const dominatorTreeState = exports.dominatorTreeState = Object.create(null);
dominatorTreeState.COMPUTING = "dominator-tree-state-computing";
dominatorTreeState.COMPUTED = "dominator-tree-state-computed";
dominatorTreeState.FETCHING = "dominator-tree-state-fetching";
dominatorTreeState.LOADED = "dominator-tree-state-loaded";
dominatorTreeState.INCREMENTAL_FETCHING = "dominator-tree-state-incremental-fetching";
dominatorTreeState.ERROR = "dominator-tree-state-error";

/** * States for Individuals Model *********************************************/

/*
 * Various states the individuals model can be in.
 *
 *     COMPUTING_DOMINATOR_TREE -> FETCHING -> FETCHED
 *
 * Any state may lead to the ERROR state, from which it can never leave.
 */
const individualsState = exports.individualsState = Object.create(null);
individualsState.COMPUTING_DOMINATOR_TREE = "individuals-state-computing-dominator-tree";
individualsState.FETCHING = "individuals-state-fetching";
individualsState.FETCHED = "individuals-state-fetched";
individualsState.ERROR = "individuals-state-error";
PK
!<�a���Nchrome/devtools/modules/devtools/client/memory/dominator-tree-lazy-children.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 `DominatorTreeLazyChildren` is a placeholder that represents a future
 * subtree in an existing `DominatorTreeNode` tree that is currently being
 * incrementally fetched from the `HeapAnalysesWorker`.
 *
 * @param {NodeId} parentNodeId
 * @param {Number} siblingIndex
 */
function DominatorTreeLazyChildren(parentNodeId, siblingIndex) {
  this._parentNodeId = parentNodeId;
  this._siblingIndex = siblingIndex;
}

/**
 * Generate a unique key for this `DominatorTreeLazyChildren` instance. This can
 * be used as the key in a hash table or as the `key` property for a React
 * component, for example.
 *
 * @returns {String}
 */
DominatorTreeLazyChildren.prototype.key = function () {
  return `dominator-tree-lazy-children-${this._parentNodeId}-${this._siblingIndex}`;
};

/**
 * Return true if this is a placeholder for the first child of its
 * parent. Return false if it is a placeholder for loading more of its parent's
 * children.
 *
 * @returns {Boolean}
 */
DominatorTreeLazyChildren.prototype.isFirstChild = function () {
  return this._siblingIndex === 0;
};

/**
 * Get this subtree's parent node's identifier.
 *
 * @returns {NodeId}
 */
DominatorTreeLazyChildren.prototype.parentNodeId = function () {
  return this._parentNodeId;
};

/**
 * Get this subtree's index in its parent's children array.
 *
 * @returns {Number}
 */
DominatorTreeLazyChildren.prototype.siblingIndex = function () {
  return this._siblingIndex;
};

module.exports = DominatorTreeLazyChildren;
PK
!<���TCTC8chrome/devtools/modules/devtools/client/memory/models.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 treeMapState, censusState */
/* eslint no-shadow: ["error", { "allow": ["app"] }] */

"use strict";

const { assert } = require("devtools/shared/DevToolsUtils");
const { MemoryFront } = require("devtools/shared/fronts/memory");
const HeapAnalysesClient = require("devtools/shared/heapsnapshot/HeapAnalysesClient");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const {
  snapshotState: states,
  diffingState,
  dominatorTreeState,
  viewState,
  individualsState,
} = require("./constants");

/**
 * ONLY USE THIS FOR MODEL VALIDATORS IN CONJUCTION WITH assert()!
 *
 * React checks that the returned values from validator functions are instances
 * of Error, but because React is loaded in its own global, that check is always
 * false and always results in a warning.
 *
 * To work around this and still get model validation, just call assert() inside
 * a function passed to catchAndIgnore. The assert() function will still report
 * assertion failures, but this funciton will swallow the errors so that React
 * doesn't go crazy and drown out the real error in irrelevant and incorrect
 * warnings.
 *
 * Example usage:
 *
 *     const MyModel = PropTypes.shape({
 *       someProperty: catchAndIgnore(function (model) {
 *         assert(someInvariant(model.someProperty), "Should blah blah");
 *       })
 *     });
 */
function catchAndIgnore(fn) {
  return function (...args) {
    try {
      fn(...args);
    } catch (err) {
      // continue regardless of error
    }

    return null;
  };
}

/**
 * The data describing the census report's shape, and its associated metadata.
 *
 * @see `js/src/doc/Debugger/Debugger.Memory.md`
 */
const censusDisplayModel = exports.censusDisplay = PropTypes.shape({
  displayName: PropTypes.string.isRequired,
  tooltip: PropTypes.string.isRequired,
  inverted: PropTypes.bool.isRequired,
  breakdown: PropTypes.shape({
    by: PropTypes.string.isRequired,
  })
});

/**
 * How we want to label nodes in the dominator tree, and associated
 * metadata. The notable difference from `censusDisplayModel` is the lack of
 * an `inverted` property.
 *
 * @see `js/src/doc/Debugger/Debugger.Memory.md`
 */
const labelDisplayModel = exports.labelDisplay = PropTypes.shape({
  displayName: PropTypes.string.isRequired,
  tooltip: PropTypes.string.isRequired,
  breakdown: PropTypes.shape({
    by: PropTypes.string.isRequired,
  })
});

/**
 * The data describing the tree map's shape, and its associated metadata.
 *
 * @see `js/src/doc/Debugger/Debugger.Memory.md`
 */
const treeMapDisplayModel = exports.treeMapDisplay = PropTypes.shape({
  displayName: PropTypes.string.isRequired,
  tooltip: PropTypes.string.isRequired,
  inverted: PropTypes.bool.isRequired,
  breakdown: PropTypes.shape({
    by: PropTypes.string.isRequired,
  })
});

/**
 * Tree map model.
 */
const treeMapModel = exports.treeMapModel = PropTypes.shape({
  // The current census report data.
  report: PropTypes.object,
  // The display data used to generate the current census.
  display: treeMapDisplayModel,
  // The current treeMapState this is in
  state: catchAndIgnore(function (treeMap) {
    switch (treeMap.state) {
      case treeMapState.SAVING:
        assert(!treeMap.report, "Should not have a report");
        assert(!treeMap.error, "Should not have an error");
        break;

      case treeMapState.SAVED:
        assert(treeMap.report, "Should have a report");
        assert(!treeMap.error, "Should not have an error");
        break;

      case treeMapState.ERROR:
        assert(treeMap.error, "Should have an error");
        break;

      default:
        assert(false, `Unexpected treeMap state: ${treeMap.state}`);
    }
  })
});

let censusModel = exports.censusModel = PropTypes.shape({
  // The current census report data.
  report: PropTypes.object,
  // The parent map for the report.
  parentMap: PropTypes.object,
  // The display data used to generate the current census.
  display: censusDisplayModel,
  // If present, the currently cached report's filter string used for pruning
  // the tree items.
  filter: PropTypes.string,
  // The Immutable.Set<CensusTreeNode.id> of expanded node ids in the report
  // tree.
  expanded: catchAndIgnore(function (census) {
    if (census.report) {
      assert(census.expanded,
             "If we have a report, we should also have the set of expanded nodes");
    }
  }),
  // If a node is currently focused in the report tree, then this is it.
  focused: PropTypes.object,
  // The censusModelState that this census is currently in.
  state: catchAndIgnore(function (census) {
    switch (census.state) {
      case censusState.SAVING:
        assert(!census.report, "Should not have a report");
        assert(!census.parentMap, "Should not have a parent map");
        assert(census.expanded, "Should not have an expanded set");
        assert(!census.error, "Should not have an error");
        break;

      case censusState.SAVED:
        assert(census.report, "Should have a report");
        assert(census.parentMap, "Should have a parent map");
        assert(census.expanded, "Should have an expanded set");
        assert(!census.error, "Should not have an error");
        break;

      case censusState.ERROR:
        assert(!census.report, "Should not have a report");
        assert(census.error, "Should have an error");
        break;

      default:
        assert(false, `Unexpected census state: ${census.state}`);
    }
  })
});

/**
 * Dominator tree model.
 */
let dominatorTreeModel = exports.dominatorTreeModel = PropTypes.shape({
  // The id of this dominator tree.
  dominatorTreeId: PropTypes.number,

  // The root DominatorTreeNode of this dominator tree.
  root: PropTypes.object,

  // The Set<NodeId> of expanded nodes in this dominator tree.
  expanded: PropTypes.object,

  // If a node is currently focused in the dominator tree, then this is it.
  focused: PropTypes.object,

  // If an error was thrown while getting this dominator tree, the `Error`
  // instance (or an error string message) is attached here.
  error: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.object,
  ]),

  // The display used to generate descriptive labels of nodes in this dominator
  // tree.
  display: labelDisplayModel,

  // The number of active requests to incrementally fetch subtrees. This should
  // only be non-zero when the state is INCREMENTAL_FETCHING.
  activeFetchRequestCount: PropTypes.number,

  // The dominatorTreeState that this domintor tree is currently in.
  state: catchAndIgnore(function (dominatorTree) {
    switch (dominatorTree.state) {
      case dominatorTreeState.COMPUTING:
        assert(dominatorTree.dominatorTreeId == null,
                "Should not have a dominator tree id yet");
        assert(!dominatorTree.root,
               "Should not have the root of the tree yet");
        assert(!dominatorTree.error,
               "Should not have an error");
        break;

      case dominatorTreeState.COMPUTED:
      case dominatorTreeState.FETCHING:
        assert(dominatorTree.dominatorTreeId != null,
               "Should have a dominator tree id");
        assert(!dominatorTree.root,
               "Should not have the root of the tree yet");
        assert(!dominatorTree.error,
               "Should not have an error");
        break;

      case dominatorTreeState.INCREMENTAL_FETCHING:
        assert(typeof dominatorTree.activeFetchRequestCount === "number",
               "The active fetch request count is a number when we are in the " +
               "INCREMENTAL_FETCHING state");
        assert(dominatorTree.activeFetchRequestCount > 0,
               "We are keeping track of how many active requests are in flight.");
        // Fall through...
      case dominatorTreeState.LOADED:
        assert(dominatorTree.dominatorTreeId != null,
               "Should have a dominator tree id");
        assert(dominatorTree.root,
               "Should have the root of the tree");
        assert(dominatorTree.expanded,
               "Should have an expanded set");
        assert(!dominatorTree.error,
               "Should not have an error");
        break;

      case dominatorTreeState.ERROR:
        assert(dominatorTree.error, "Should have an error");
        break;

      default:
        assert(false,
               `Unexpected dominator tree state: ${dominatorTree.state}`);
    }
  }),
});

/**
 * Snapshot model.
 */
let stateKeys = Object.keys(states).map(state => states[state]);
const snapshotId = PropTypes.number;
let snapshotModel = exports.snapshot = PropTypes.shape({
  // Unique ID for a snapshot
  id: snapshotId.isRequired,
  // Whether or not this snapshot is currently selected.
  selected: PropTypes.bool.isRequired,
  // Filesystem path to where the snapshot is stored; used to identify the
  // snapshot for HeapAnalysesClient.
  path: PropTypes.string,
  // Current census data for this snapshot.
  census: censusModel,
  // Current dominator tree data for this snapshot.
  dominatorTree: dominatorTreeModel,
  // Current tree map data for this snapshot.
  treeMap: treeMapModel,
  // If an error was thrown while processing this snapshot, the `Error` instance
  // is attached here.
  error: PropTypes.object,
  // Boolean indicating whether or not this snapshot was imported.
  imported: PropTypes.bool.isRequired,
  // The creation time of the snapshot; required after the snapshot has been
  // read.
  creationTime: PropTypes.number,
  // The current state the snapshot is in.
  // @see ./constants.js
  state: catchAndIgnore(function (snapshot, propName) {
    let current = snapshot.state;
    let shouldHavePath = [states.IMPORTING, states.SAVED, states.READ];
    let shouldHaveCreationTime = [states.READ];

    if (!stateKeys.includes(current)) {
      throw new Error(`Snapshot state must be one of ${stateKeys}.`);
    }
    if (shouldHavePath.includes(current) && !snapshot.path) {
      throw new Error(`Snapshots in state ${current} must have a snapshot path.`);
    }
    if (shouldHaveCreationTime.includes(current) && !snapshot.creationTime) {
      throw new Error(`Snapshots in state ${current} must have a creation time.`);
    }
  }),
});

let allocationsModel = exports.allocations = PropTypes.shape({
  // True iff we are recording allocation stacks right now.
  recording: PropTypes.bool.isRequired,
  // True iff we are in the process of toggling the recording of allocation
  // stacks on or off right now.
  togglingInProgress: PropTypes.bool.isRequired,
});

let diffingModel = exports.diffingModel = PropTypes.shape({
  // The id of the first snapshot to diff.
  firstSnapshotId: snapshotId,

  // The id of the second snapshot to diff.
  secondSnapshotId: catchAndIgnore(function (diffing, propName) {
    if (diffing.secondSnapshotId && !diffing.firstSnapshotId) {
      throw new Error("Cannot have second snapshot without already having " +
                      "first snapshot");
    }
    return snapshotId(diffing, propName);
  }),

  // The current census data for the diffing.
  census: censusModel,

  // If an error was thrown while diffing, the `Error` instance is attached
  // here.
  error: PropTypes.object,

  // The current state the diffing is in.
  // @see ./constants.js
  state: catchAndIgnore(function (diffing) {
    switch (diffing.state) {
      case diffingState.TOOK_DIFF:
        assert(diffing.census, "If we took a diff, we should have a census");
        // Fall through...
      case diffingState.TAKING_DIFF:
        assert(diffing.firstSnapshotId, "Should have first snapshot");
        assert(diffing.secondSnapshotId, "Should have second snapshot");
        break;

      case diffingState.SELECTING:
        break;

      case diffingState.ERROR:
        assert(diffing.error, "Should have error");
        break;

      default:
        assert(false, `Bad diffing state: ${diffing.state}`);
    }
  }),
});

let previousViewModel = exports.previousView = PropTypes.shape({
  state: catchAndIgnore(function (previous) {
    switch (previous.state) {
      case viewState.DIFFING:
        assert(previous.diffing, "Should have previous diffing state.");
        assert(!previous.selected, "Should not have a previously selected snapshot.");
        break;

      case viewState.CENSUS:
      case viewState.DOMINATOR_TREE:
      case viewState.TREE_MAP:
        assert(previous.selected, "Should have a previously selected snapshot.");
        break;

      case viewState.INDIVIDUALS:
      default:
        assert(false, `Unexpected previous view state: ${previous.state}.`);
    }
  }),

  // The previous diffing state, if any.
  diffing: diffingModel,

  // The previously selected snapshot, if any.
  selected: snapshotId,
});

exports.view = PropTypes.shape({
  // The current view state.
  state: catchAndIgnore(function (view) {
    switch (view.state) {
      case viewState.DIFFING:
      case viewState.CENSUS:
      case viewState.DOMINATOR_TREE:
      case viewState.INDIVIDUALS:
      case viewState.TREE_MAP:
        break;

      default:
        assert(false, `Unexpected type of view: ${view.state}`);
    }
  }),

  // The previous view state.
  previous: previousViewModel,
});

const individualsModel = exports.individuals = PropTypes.shape({
  error: PropTypes.object,

  nodes: PropTypes.arrayOf(PropTypes.object),

  dominatorTree: dominatorTreeModel,

  id: snapshotId,

  censusBreakdown: PropTypes.object,

  indices: PropTypes.object,

  labelDisplay: labelDisplayModel,

  focused: PropTypes.object,

  state: catchAndIgnore(function (individuals) {
    switch (individuals.state) {
      case individualsState.COMPUTING_DOMINATOR_TREE:
      case individualsState.FETCHING:
        assert(!individuals.nodes, "Should not have individual nodes");
        assert(!individuals.dominatorTree, "Should not have dominator tree");
        assert(!individuals.id, "Should not have an id");
        assert(!individuals.censusBreakdown, "Should not have a censusBreakdown");
        assert(!individuals.indices, "Should not have indices");
        assert(!individuals.labelDisplay, "Should not have a labelDisplay");
        break;

      case individualsState.FETCHED:
        assert(individuals.nodes, "Should have individual nodes");
        assert(individuals.dominatorTree, "Should have dominator tree");
        assert(individuals.id, "Should have an id");
        assert(individuals.censusBreakdown, "Should have a censusBreakdown");
        assert(individuals.indices, "Should have indices");
        assert(individuals.labelDisplay, "Should have a labelDisplay");
        break;

      case individualsState.ERROR:
        assert(individuals.error, "Should have an error object");
        break;

      default:
        assert(false, `Unexpected individuals state: ${individuals.state}`);
        break;
    }
  }),
});

exports.app = {
  // {MemoryFront} Used to communicate with platform
  front: PropTypes.instanceOf(MemoryFront),

  // Allocations recording related data.
  allocations: allocationsModel.isRequired,

  // {HeapAnalysesClient} Used to interface with snapshots
  heapWorker: PropTypes.instanceOf(HeapAnalysesClient),

  // The display data describing how we want the census data to be.
  censusDisplay: censusDisplayModel.isRequired,

  // The display data describing how we want the dominator tree labels to be
  // computed.
  labelDisplay: labelDisplayModel.isRequired,

  // The display data describing how we want the dominator tree labels to be
  // computed.
  treeMapDisplay: treeMapDisplayModel.isRequired,

  // List of reference to all snapshots taken
  snapshots: PropTypes.arrayOf(snapshotModel).isRequired,

  // If present, a filter string for pruning the tree items.
  filter: PropTypes.string,

  // If present, the current diffing state.
  diffing: diffingModel,

  // If present, the current individuals state.
  individuals: individualsModel,

  // The current type of view.
  view: function (app) {
    catchAndIgnore(function (app) {
      switch (app.view.state) {
        case viewState.DIFFING:
          assert(app.diffing, "Should be diffing");
          break;

        case viewState.INDIVIDUALS:
        case viewState.CENSUS:
        case viewState.DOMINATOR_TREE:
        case viewState.TREE_MAP:
          assert(!app.diffing, "Should not be diffing");
          break;

        default:
          assert(false, `Unexpected type of view: ${app.view.state}`);
      }
    })(app);

    catchAndIgnore(function (app) {
      switch (app.view.state) {
        case viewState.INDIVIDUALS:
          assert(app.individuals, "Should have individuals state");
          break;

        case viewState.DIFFING:
        case viewState.CENSUS:
        case viewState.DOMINATOR_TREE:
        case viewState.TREE_MAP:
          assert(!app.individuals, "Should not have individuals state");
          break;

        default:
          assert(false, `Unexpected type of view: ${app.view.state}`);
      }
    })(app);
  },
};
PK
!<�9@���7chrome/devtools/modules/devtools/client/memory/panel.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Task } = require("devtools/shared/task");
const EventEmitter = require("devtools/shared/old-event-emitter");
const { MemoryFront } = require("devtools/shared/fronts/memory");
const HeapAnalysesClient = require("devtools/shared/heapsnapshot/HeapAnalysesClient");

function MemoryPanel(iframeWindow, toolbox) {
  this.panelWin = iframeWindow;
  this._toolbox = toolbox;

  EventEmitter.decorate(this);
}

MemoryPanel.prototype = {
  open: Task.async(function* () {
    if (this._opening) {
      return this._opening;
    }

    this.panelWin.gToolbox = this._toolbox;
    this.panelWin.gTarget = this.target;

    const rootForm = yield this.target.root;
    this.panelWin.gFront = new MemoryFront(this.target.client,
                                           this.target.form,
                                           rootForm);
    this.panelWin.gHeapAnalysesClient = new HeapAnalysesClient();

    yield this.panelWin.gFront.attach();

    this._opening = this.panelWin.initialize().then(() => {
      this.isReady = true;
      this.emit("ready");
      return this;
    });

    return this._opening;
  }),

  // DevToolPanel API

  get target() {
    return this._toolbox.target;
  },

  destroy: Task.async(function* () {
    // Make sure this panel is not already destroyed.
    if (this._destroyer) {
      return this._destroyer;
    }

    yield this.panelWin.gFront.detach();

    this._destroyer = this.panelWin.destroy().then(() => {
      // Destroy front to ensure packet handler is removed from client
      this.panelWin.gFront.destroy();
      this.panelWin.gHeapAnalysesClient.destroy();
      this.panelWin = null;
      this._opening = null;
      this.isReady = false;
      this.emit("destroyed");
    });

    return this._destroyer;
  })
};

exports.MemoryPanel = MemoryPanel;
PK
!<x�����Fchrome/devtools/modules/devtools/client/memory/reducers/allocations.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { assert } = require("devtools/shared/DevToolsUtils");
const { actions } = require("../constants");

let handlers = Object.create(null);

handlers[actions.TOGGLE_RECORD_ALLOCATION_STACKS_START] = function (state, action) {
  assert(!state.togglingInProgress,
         "Changing recording state must not be reentrant.");

  return {
    recording: !state.recording,
    togglingInProgress: true,
  };
};

handlers[actions.TOGGLE_RECORD_ALLOCATION_STACKS_END] = function (state, action) {
  assert(state.togglingInProgress,
         "Should not complete changing recording state if we weren't changing "
         + "recording state already.");

  return {
    recording: state.recording,
    togglingInProgress: false,
  };
};

const DEFAULT_ALLOCATIONS_STATE = {
  recording: false,
  togglingInProgress: false
};

module.exports = function (state = DEFAULT_ALLOCATIONS_STATE, action) {
  let handle = handlers[action.type];
  if (handle) {
    return handle(state, action);
  }
  return state;
};
PK
!<&�q���Ichrome/devtools/modules/devtools/client/memory/reducers/census-display.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { actions, censusDisplays } = require("../constants");
const DEFAULT_CENSUS_DISPLAY = censusDisplays.coarseType;

let handlers = Object.create(null);

handlers[actions.SET_CENSUS_DISPLAY] = function (_, { display }) {
  return display;
};

module.exports = function (state = DEFAULT_CENSUS_DISPLAY, action) {
  let handle = handlers[action.type];
  if (handle) {
    return handle(state, action);
  }
  return state;
};
PK
!<��_��Bchrome/devtools/modules/devtools/client/memory/reducers/diffing.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Immutable = require("devtools/client/shared/vendor/immutable");
const { immutableUpdate, assert } = require("devtools/shared/DevToolsUtils");
const { actions, diffingState, viewState } = require("../constants");
const { snapshotIsDiffable } = require("../utils");

const handlers = Object.create(null);

handlers[actions.POP_VIEW] = function (diffing, { previousView }) {
  if (previousView.state === viewState.DIFFING) {
    assert(previousView.diffing, "Should have previousView.diffing");
    return previousView.diffing;
  }

  return null;
};

handlers[actions.CHANGE_VIEW] = function (diffing, { newViewState }) {
  if (newViewState === viewState.DIFFING) {
    assert(!diffing, "Should not switch to diffing view when already diffing");
    return Object.freeze({
      firstSnapshotId: null,
      secondSnapshotId: null,
      census: null,
      state: diffingState.SELECTING,
    });
  }

  return null;
};

handlers[actions.SELECT_SNAPSHOT_FOR_DIFFING] = function (diffing, { snapshot }) {
  assert(diffing,
         "Should never select a snapshot for diffing when we aren't diffing " +
         "anything");
  assert(diffing.state === diffingState.SELECTING,
         "Can't select when not in SELECTING state");
  assert(snapshotIsDiffable(snapshot),
         "snapshot must be in a diffable state");

  if (!diffing.firstSnapshotId) {
    return immutableUpdate(diffing, {
      firstSnapshotId: snapshot.id
    });
  }

  assert(!diffing.secondSnapshotId,
         "If we aren't selecting the first, then we must be selecting the " +
         "second");

  if (snapshot.id === diffing.firstSnapshotId) {
    // Ignore requests to select the same snapshot.
    return diffing;
  }

  return immutableUpdate(diffing, {
    secondSnapshotId: snapshot.id
  });
};

handlers[actions.TAKE_CENSUS_DIFF_START] = function (diffing, action) {
  assert(diffing, "Should be diffing when starting a census diff");
  assert(action.first.id === diffing.firstSnapshotId,
         "First snapshot's id should match");
  assert(action.second.id === diffing.secondSnapshotId,
         "Second snapshot's id should match");

  return immutableUpdate(diffing, {
    state: diffingState.TAKING_DIFF,
    census: {
      report: null,
      inverted: action.inverted,
      filter: action.filter,
      display: action.display,
    }
  });
};

handlers[actions.TAKE_CENSUS_DIFF_END] = function (diffing, action) {
  assert(diffing, "Should be diffing when ending a census diff");
  assert(action.first.id === diffing.firstSnapshotId,
         "First snapshot's id should match");
  assert(action.second.id === diffing.secondSnapshotId,
         "Second snapshot's id should match");

  return immutableUpdate(diffing, {
    state: diffingState.TOOK_DIFF,
    census: {
      report: action.report,
      parentMap: action.parentMap,
      expanded: Immutable.Set(),
      inverted: action.inverted,
      filter: action.filter,
      display: action.display,
    }
  });
};

handlers[actions.DIFFING_ERROR] = function (diffing, action) {
  return {
    state: diffingState.ERROR,
    error: action.error
  };
};

handlers[actions.EXPAND_DIFFING_CENSUS_NODE] = function (diffing, { node }) {
  assert(diffing, "Should be diffing if expanding diffing's census nodes");
  assert(diffing.state === diffingState.TOOK_DIFF,
         "Should have taken the census diff if expanding nodes");
  assert(diffing.census, "Should have a census");
  assert(diffing.census.report, "Should have a census report");
  assert(diffing.census.expanded, "Should have a census's expanded set");

  const expanded = diffing.census.expanded.add(node.id);
  const census = immutableUpdate(diffing.census, { expanded });
  return immutableUpdate(diffing, { census });
};

handlers[actions.COLLAPSE_DIFFING_CENSUS_NODE] = function (diffing, { node }) {
  assert(diffing, "Should be diffing if expanding diffing's census nodes");
  assert(diffing.state === diffingState.TOOK_DIFF,
         "Should have taken the census diff if expanding nodes");
  assert(diffing.census, "Should have a census");
  assert(diffing.census.report, "Should have a census report");
  assert(diffing.census.expanded, "Should have a census's expanded set");

  const expanded = diffing.census.expanded.delete(node.id);
  const census = immutableUpdate(diffing.census, { expanded });
  return immutableUpdate(diffing, { census });
};

handlers[actions.FOCUS_DIFFING_CENSUS_NODE] = function (diffing, { node }) {
  assert(diffing, "Should be diffing.");
  assert(diffing.census, "Should have a census");
  const census = immutableUpdate(diffing.census, { focused: node });
  return immutableUpdate(diffing, { census });
};

module.exports = function (diffing = null, action) {
  const handler = handlers[action.type];
  return handler ? handler(diffing, action) : diffing;
};
PK
!<}N�WWAchrome/devtools/modules/devtools/client/memory/reducers/errors.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { ERROR_TYPE: TASK_ERROR_TYPE } = require("devtools/client/shared/redux/middleware/task");

/**
 * Handle errors dispatched from task middleware and
 * store them so we can check in tests or dump them out.
 */
module.exports = function (state = [], action) {
  switch (action.type) {
    case TASK_ERROR_TYPE:
      return [...state, action.error];
  }
  return state;
};
PK
!<^�u��Achrome/devtools/modules/devtools/client/memory/reducers/filter.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { actions } = require("../constants");

module.exports = function (filterString = null, action) {
  if (action.type === actions.SET_FILTER_STRING) {
    return action.filter || null;
  }

  return filterString;
};
PK
!<�%���Fchrome/devtools/modules/devtools/client/memory/reducers/individuals.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { assert, immutableUpdate } = require("devtools/shared/DevToolsUtils");
const { actions, individualsState, viewState } = require("../constants");

const handlers = Object.create(null);

handlers[actions.POP_VIEW] = function (_state, _action) {
  return null;
};

handlers[actions.CHANGE_VIEW] = function (individuals, { newViewState }) {
  if (newViewState === viewState.INDIVIDUALS) {
    assert(!individuals,
           "Should not switch to individuals view when already in individuals view");
    return Object.freeze({
      state: individualsState.COMPUTING_DOMINATOR_TREE,
    });
  }

  return null;
};

handlers[actions.FOCUS_INDIVIDUAL] = function (individuals, { node }) {
  assert(individuals, "Should have individuals");
  return immutableUpdate(individuals, { focused: node });
};

handlers[actions.FETCH_INDIVIDUALS_START] = function (individuals, action) {
  assert(individuals, "Should have individuals");
  return Object.freeze({
    state: individualsState.FETCHING,
    focused: individuals.focused,
  });
};

handlers[actions.FETCH_INDIVIDUALS_END] = function (individuals, action) {
  assert(individuals, "Should have individuals");
  assert(!individuals.nodes, "Should not have nodes");
  assert(individuals.state === individualsState.FETCHING,
         "Should only end fetching individuals after starting.");

  const focused = individuals.focused
    ? action.nodes.find(n => n.nodeId === individuals.focused.nodeId)
    : null;

  return Object.freeze({
    state: individualsState.FETCHED,
    nodes: action.nodes,
    id: action.id,
    censusBreakdown: action.censusBreakdown,
    indices: action.indices,
    labelDisplay: action.labelDisplay,
    focused,
    dominatorTree: action.dominatorTree,
  });
};

handlers[actions.INDIVIDUALS_ERROR] = function (_, { error }) {
  return Object.freeze({
    error,
    nodes: null,
    state: individualsState.ERROR,
  });
};

module.exports = function (individuals = null, action) {
  const handler = handlers[action.type];
  return handler ? handler(individuals, action) : individuals;
};
PK
!<j'ttHchrome/devtools/modules/devtools/client/memory/reducers/label-display.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { actions, labelDisplays } = require("../constants");
const DEFAULT_LABEL_DISPLAY = labelDisplays.coarseType;

const handlers = Object.create(null);

handlers[actions.SET_LABEL_DISPLAY] = function (_, { display }) {
  return display;
};

module.exports = function (state = DEFAULT_LABEL_DISPLAY, action) {
  const handler = handlers[action.type];
  return handler ? handler(state, action) : state;
};
PK
!<R����@chrome/devtools/modules/devtools/client/memory/reducers/sizes.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { actions } = require("../constants");
const { immutableUpdate } = require("devtools/shared/DevToolsUtils");

const handlers = Object.create(null);

handlers[actions.RESIZE_SHORTEST_PATHS] = function (sizes, { size }) {
  return immutableUpdate(sizes, { shortestPathsSize: size });
};

module.exports = function (sizes = { shortestPathsSize: .5 }, action) {
  const handler = handlers[action.type];
  return handler ? handler(sizes, action) : sizes;
};
PK
!<�x`�_7_7Dchrome/devtools/modules/devtools/client/memory/reducers/snapshots.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Immutable = require("devtools/client/shared/vendor/immutable");
const { immutableUpdate, assert } = require("devtools/shared/DevToolsUtils");
const {
  actions,
  snapshotState: states,
  censusState,
  treeMapState,
  dominatorTreeState,
  viewState,
} = require("../constants");
const DominatorTreeNode = require("devtools/shared/heapsnapshot/DominatorTreeNode");

const handlers = Object.create(null);

handlers[actions.SNAPSHOT_ERROR] = function (snapshots, { id, error }) {
  return snapshots.map(snapshot => {
    return snapshot.id === id
      ? immutableUpdate(snapshot, { state: states.ERROR, error })
      : snapshot;
  });
};

handlers[actions.TAKE_SNAPSHOT_START] = function (snapshots, { snapshot }) {
  return [...snapshots, snapshot];
};

handlers[actions.TAKE_SNAPSHOT_END] = function (snapshots, { id, path }) {
  return snapshots.map(snapshot => {
    return snapshot.id === id
      ? immutableUpdate(snapshot, { state: states.SAVED, path })
      : snapshot;
  });
};

handlers[actions.IMPORT_SNAPSHOT_START] = handlers[actions.TAKE_SNAPSHOT_START];

handlers[actions.READ_SNAPSHOT_START] = function (snapshots, { id }) {
  return snapshots.map(snapshot => {
    return snapshot.id === id
      ? immutableUpdate(snapshot, { state: states.READING })
      : snapshot;
  });
};

handlers[actions.READ_SNAPSHOT_END] = function (snapshots, { id, creationTime }) {
  return snapshots.map(snapshot => {
    return snapshot.id === id
      ? immutableUpdate(snapshot, { state: states.READ, creationTime })
      : snapshot;
  });
};

handlers[actions.TAKE_CENSUS_START] = function (snapshots, { id, display, filter }) {
  const census = {
    report: null,
    display,
    filter,
    state: censusState.SAVING
  };

  return snapshots.map(snapshot => {
    return snapshot.id === id
      ? immutableUpdate(snapshot, { census })
      : snapshot;
  });
};

handlers[actions.TAKE_CENSUS_END] = function (snapshots, { id,
                                                           report,
                                                           parentMap,
                                                           display,
                                                           filter }) {
  const census = {
    report,
    parentMap,
    expanded: Immutable.Set(),
    display,
    filter,
    state: censusState.SAVED
  };

  return snapshots.map(snapshot => {
    return snapshot.id === id
      ? immutableUpdate(snapshot, { census })
      : snapshot;
  });
};

handlers[actions.TAKE_CENSUS_ERROR] = function (snapshots, { id, error }) {
  assert(error, "actions with TAKE_CENSUS_ERROR should have an error");

  return snapshots.map(snapshot => {
    if (snapshot.id !== id) {
      return snapshot;
    }

    const census = Object.freeze({
      state: censusState.ERROR,
      error,
    });

    return immutableUpdate(snapshot, { census });
  });
};

handlers[actions.TAKE_TREE_MAP_START] = function (snapshots, { id, display }) {
  const treeMap = {
    report: null,
    display,
    state: treeMapState.SAVING
  };

  return snapshots.map(snapshot => {
    return snapshot.id === id
      ? immutableUpdate(snapshot, { treeMap })
      : snapshot;
  });
};

handlers[actions.TAKE_TREE_MAP_END] = function (snapshots, action) {
  const { id, report, display } = action;
  const treeMap = {
    report,
    display,
    state: treeMapState.SAVED
  };

  return snapshots.map(snapshot => {
    return snapshot.id === id
      ? immutableUpdate(snapshot, { treeMap })
      : snapshot;
  });
};

handlers[actions.TAKE_TREE_MAP_ERROR] = function (snapshots, { id, error }) {
  assert(error, "actions with TAKE_TREE_MAP_ERROR should have an error");

  return snapshots.map(snapshot => {
    if (snapshot.id !== id) {
      return snapshot;
    }

    const treeMap = Object.freeze({
      state: treeMapState.ERROR,
      error,
    });

    return immutableUpdate(snapshot, { treeMap });
  });
};

handlers[actions.EXPAND_CENSUS_NODE] = function (snapshots, { id, node }) {
  return snapshots.map(snapshot => {
    if (snapshot.id !== id) {
      return snapshot;
    }

    assert(snapshot.census, "Should have a census");
    assert(snapshot.census.report, "Should have a census report");
    assert(snapshot.census.expanded, "Should have a census's expanded set");

    const expanded = snapshot.census.expanded.add(node.id);
    const census = immutableUpdate(snapshot.census, { expanded });
    return immutableUpdate(snapshot, { census });
  });
};

handlers[actions.COLLAPSE_CENSUS_NODE] = function (snapshots, { id, node }) {
  return snapshots.map(snapshot => {
    if (snapshot.id !== id) {
      return snapshot;
    }

    assert(snapshot.census, "Should have a census");
    assert(snapshot.census.report, "Should have a census report");
    assert(snapshot.census.expanded, "Should have a census's expanded set");

    const expanded = snapshot.census.expanded.delete(node.id);
    const census = immutableUpdate(snapshot.census, { expanded });
    return immutableUpdate(snapshot, { census });
  });
};

handlers[actions.FOCUS_CENSUS_NODE] = function (snapshots, { id, node }) {
  return snapshots.map(snapshot => {
    if (snapshot.id !== id) {
      return snapshot;
    }

    assert(snapshot.census, "Should have a census");
    const census = immutableUpdate(snapshot.census, { focused: node });
    return immutableUpdate(snapshot, { census });
  });
};

handlers[actions.SELECT_SNAPSHOT] = function (snapshots, { id }) {
  return snapshots.map(s => immutableUpdate(s, { selected: s.id === id }));
};

handlers[actions.DELETE_SNAPSHOTS_START] = function (snapshots, { ids }) {
  return snapshots.filter(s => !ids.includes(s.id));
};

handlers[actions.DELETE_SNAPSHOTS_END] = function (snapshots) {
  return snapshots;
};

handlers[actions.CHANGE_VIEW] = function (snapshots, { newViewState }) {
  return newViewState === viewState.DIFFING
    ? snapshots.map(s => immutableUpdate(s, { selected: false }))
    : snapshots;
};

handlers[actions.POP_VIEW] = function (snapshots, { previousView }) {
  return snapshots.map(s => immutableUpdate(s, {
    selected: s.id === previousView.selected
  }));
};

handlers[actions.COMPUTE_DOMINATOR_TREE_START] = function (snapshots, { id }) {
  const dominatorTree = Object.freeze({
    state: dominatorTreeState.COMPUTING,
    dominatorTreeId: undefined,
    root: undefined,
  });

  return snapshots.map(snapshot => {
    if (snapshot.id !== id) {
      return snapshot;
    }

    assert(!snapshot.dominatorTree,
           "Should not have a dominator tree model");
    return immutableUpdate(snapshot, { dominatorTree });
  });
};

handlers[actions.COMPUTE_DOMINATOR_TREE_END] =
  function (snapshots, { id, dominatorTreeId }) {
    return snapshots.map(snapshot => {
      if (snapshot.id !== id) {
        return snapshot;
      }

      assert(snapshot.dominatorTree, "Should have a dominator tree model");
      assert(snapshot.dominatorTree.state == dominatorTreeState.COMPUTING,
            "Should be in the COMPUTING state");

      const dominatorTree = immutableUpdate(snapshot.dominatorTree, {
        state: dominatorTreeState.COMPUTED,
        dominatorTreeId,
      });
      return immutableUpdate(snapshot, { dominatorTree });
    });
  };

handlers[actions.FETCH_DOMINATOR_TREE_START] = function (snapshots, { id, display }) {
  return snapshots.map(snapshot => {
    if (snapshot.id !== id) {
      return snapshot;
    }

    assert(snapshot.dominatorTree, "Should have a dominator tree model");
    assert(snapshot.dominatorTree.state !== dominatorTreeState.COMPUTING &&
           snapshot.dominatorTree.state !== dominatorTreeState.ERROR,
           "Should have already computed the dominator tree, found state = " +
           snapshot.dominatorTree.state);

    const dominatorTree = immutableUpdate(snapshot.dominatorTree, {
      state: dominatorTreeState.FETCHING,
      root: undefined,
      display,
    });
    return immutableUpdate(snapshot, { dominatorTree });
  });
};

handlers[actions.FETCH_DOMINATOR_TREE_END] = function (snapshots, { id, root }) {
  return snapshots.map(snapshot => {
    if (snapshot.id !== id) {
      return snapshot;
    }

    assert(snapshot.dominatorTree, "Should have a dominator tree model");
    assert(snapshot.dominatorTree.state == dominatorTreeState.FETCHING,
           "Should be in the FETCHING state");

    let focused;
    if (snapshot.dominatorTree.focused) {
      focused = (function findFocused(node) {
        if (node.nodeId === snapshot.dominatorTree.focused.nodeId) {
          return node;
        }

        if (node.children) {
          const length = node.children.length;
          for (let i = 0; i < length; i++) {
            const result = findFocused(node.children[i]);
            if (result) {
              return result;
            }
          }
        }

        return undefined;
      }(root));
    }

    const dominatorTree = immutableUpdate(snapshot.dominatorTree, {
      state: dominatorTreeState.LOADED,
      root,
      expanded: Immutable.Set(),
      focused,
    });

    return immutableUpdate(snapshot, { dominatorTree });
  });
};

handlers[actions.EXPAND_DOMINATOR_TREE_NODE] = function (snapshots, { id, node }) {
  return snapshots.map(snapshot => {
    if (snapshot.id !== id) {
      return snapshot;
    }

    assert(snapshot.dominatorTree, "Should have a dominator tree");
    assert(snapshot.dominatorTree.expanded,
           "Should have the dominator tree's expanded set");

    const expanded = snapshot.dominatorTree.expanded.add(node.nodeId);
    const dominatorTree = immutableUpdate(snapshot.dominatorTree, { expanded });
    return immutableUpdate(snapshot, { dominatorTree });
  });
};

handlers[actions.COLLAPSE_DOMINATOR_TREE_NODE] = function (snapshots, { id, node }) {
  return snapshots.map(snapshot => {
    if (snapshot.id !== id) {
      return snapshot;
    }

    assert(snapshot.dominatorTree, "Should have a dominator tree");
    assert(snapshot.dominatorTree.expanded,
           "Should have the dominator tree's expanded set");

    const expanded = snapshot.dominatorTree.expanded.delete(node.nodeId);
    const dominatorTree = immutableUpdate(snapshot.dominatorTree, { expanded });
    return immutableUpdate(snapshot, { dominatorTree });
  });
};

handlers[actions.FOCUS_DOMINATOR_TREE_NODE] = function (snapshots, { id, node }) {
  return snapshots.map(snapshot => {
    if (snapshot.id !== id) {
      return snapshot;
    }

    assert(snapshot.dominatorTree, "Should have a dominator tree");
    const dominatorTree = immutableUpdate(snapshot.dominatorTree, { focused: node });
    return immutableUpdate(snapshot, { dominatorTree });
  });
};

handlers[actions.FETCH_IMMEDIATELY_DOMINATED_START] = function (snapshots, { id }) {
  return snapshots.map(snapshot => {
    if (snapshot.id !== id) {
      return snapshot;
    }

    assert(snapshot.dominatorTree, "Should have a dominator tree model");
    assert(snapshot.dominatorTree.state == dominatorTreeState.INCREMENTAL_FETCHING ||
           snapshot.dominatorTree.state == dominatorTreeState.LOADED,
           "The dominator tree should be loaded if we are going to " +
           "incrementally fetch children.");

    const activeFetchRequestCount = snapshot.dominatorTree.activeFetchRequestCount
      ? snapshot.dominatorTree.activeFetchRequestCount + 1
      : 1;

    const dominatorTree = immutableUpdate(snapshot.dominatorTree, {
      state: dominatorTreeState.INCREMENTAL_FETCHING,
      activeFetchRequestCount,
    });

    return immutableUpdate(snapshot, { dominatorTree });
  });
};

handlers[actions.FETCH_IMMEDIATELY_DOMINATED_END] =
  function (snapshots, { id, path, nodes, moreChildrenAvailable}) {
    return snapshots.map(snapshot => {
      if (snapshot.id !== id) {
        return snapshot;
      }

      assert(snapshot.dominatorTree, "Should have a dominator tree model");
      assert(snapshot.dominatorTree.root, "Should have a dominator tree model root");
      assert(snapshot.dominatorTree.state === dominatorTreeState.INCREMENTAL_FETCHING,
             "The dominator tree state should be INCREMENTAL_FETCHING");

      const root = DominatorTreeNode.insert(snapshot.dominatorTree.root,
                                            path,
                                            nodes,
                                            moreChildrenAvailable);

      const focused = snapshot.dominatorTree.focused
        ? DominatorTreeNode.getNodeByIdAlongPath(snapshot.dominatorTree.focused.nodeId,
                                                 root,
                                                 path)
        : undefined;

      const activeFetchRequestCount = snapshot.dominatorTree.activeFetchRequestCount === 1
        ? undefined
        : snapshot.dominatorTree.activeFetchRequestCount - 1;

      // If there are still outstanding requests, we need to stay in the
      // INCREMENTAL_FETCHING state until they complete.
      const state = activeFetchRequestCount
        ? dominatorTreeState.INCREMENTAL_FETCHING
        : dominatorTreeState.LOADED;

      const dominatorTree = immutableUpdate(snapshot.dominatorTree, {
        state,
        root,
        focused,
        activeFetchRequestCount,
      });

      return immutableUpdate(snapshot, { dominatorTree });
    });
  };

handlers[actions.DOMINATOR_TREE_ERROR] = function (snapshots, { id, error }) {
  assert(error, "actions with DOMINATOR_TREE_ERROR should have an error");

  return snapshots.map(snapshot => {
    if (snapshot.id !== id) {
      return snapshot;
    }

    const dominatorTree = Object.freeze({
      state: dominatorTreeState.ERROR,
      error,
    });

    return immutableUpdate(snapshot, { dominatorTree });
  });
};

module.exports = function (snapshots = [], action) {
  const handler = handlers[action.type];
  if (handler) {
    return handler(snapshots, action);
  }
  return snapshots;
};
PK
!<ќ�i��Kchrome/devtools/modules/devtools/client/memory/reducers/tree-map-display.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { actions, treeMapDisplays } = require("../constants");
const DEFAULT_TREE_MAP_DISPLAY = treeMapDisplays.coarseType;

const handlers = Object.create(null);

handlers[actions.SET_TREE_MAP_DISPLAY] = function (_, { display }) {
  return display;
};

module.exports = function (state = DEFAULT_TREE_MAP_DISPLAY, action) {
  const handler = handlers[action.type];
  return handler ? handler(state, action) : state;
};
PK
!<.E��--?chrome/devtools/modules/devtools/client/memory/reducers/view.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { assert } = require("devtools/shared/DevToolsUtils");
const { actions, viewState } = require("../constants");

const handlers = Object.create(null);

handlers[actions.POP_VIEW] = function (view, _) {
  assert(view.previous, "Had better have a previous view state when POP_VIEW");
  return Object.freeze({
    state: view.previous.state,
    previous: null,
  });
};

handlers[actions.CHANGE_VIEW] = function (view, action) {
  const { newViewState, oldDiffing, oldSelected } = action;
  assert(newViewState);

  if (newViewState === viewState.INDIVIDUALS) {
    assert(oldDiffing || oldSelected);
    return Object.freeze({
      state: newViewState,
      previous: Object.freeze({
        state: view.state,
        selected: oldSelected,
        diffing: oldDiffing,
      }),
    });
  }

  return Object.freeze({
    state: newViewState,
    previous: null,
  });
};

const DEFAULT_VIEW = {
  state: viewState.TREE_MAP,
  previous: null,
};

module.exports = function (view = DEFAULT_VIEW, action) {
  const handler = handlers[action.type];
  return handler ? handler(view, action) : view;
};
PK
!<A�)J%%:chrome/devtools/modules/devtools/client/memory/reducers.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";

exports.allocations = require("./reducers/allocations");
exports.censusDisplay = require("./reducers/census-display");
exports.diffing = require("./reducers/diffing");
exports.individuals = require("./reducers/individuals");
exports.labelDisplay = require("./reducers/label-display");
exports.treeMapDisplay = require("./reducers/tree-map-display");
exports.errors = require("./reducers/errors");
exports.filter = require("./reducers/filter");
exports.sizes = require("./reducers/sizes");
exports.snapshots = require("./reducers/snapshots");
exports.view = require("./reducers/view");
PK
!<���xx7chrome/devtools/modules/devtools/client/memory/store.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 } = require("../shared/vendor/redux");
const createStore = require("../shared/redux/create-store");
const reducers = require("./reducers");
const flags = require("devtools/shared/flags");

module.exports = function () {
  let shouldLog = false;
  let history;

  // If testing, store the action history in an array
  // we'll later attach to the store
  if (flags.testing) {
    history = [];
    // Uncomment this for TONS of logging in tests.
    // shouldLog = true;
  }

  let store = createStore({
    log: shouldLog,
    history
  })(combineReducers(reducers), {});

  if (history) {
    store.history = history;
  }

  return store;
};
PK
!<��HW�:�:7chrome/devtools/modules/devtools/client/memory/utils.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Cc, Ci } = require("chrome");

const { LocalizationHelper } = require("devtools/shared/l10n");
const STRINGS_URI = "devtools/client/locales/memory.properties";
const L10N = exports.L10N = new LocalizationHelper(STRINGS_URI);

const { OS } = require("resource://gre/modules/osfile.jsm");
const { assert } = require("devtools/shared/DevToolsUtils");
const { Preferences } = require("resource://gre/modules/Preferences.jsm");
const CUSTOM_CENSUS_DISPLAY_PREF = "devtools.memory.custom-census-displays";
const CUSTOM_LABEL_DISPLAY_PREF = "devtools.memory.custom-label-displays";
const CUSTOM_TREE_MAP_DISPLAY_PREF = "devtools.memory.custom-tree-map-displays";
const BYTES = 1024;
const KILOBYTES = Math.pow(BYTES, 2);
const MEGABYTES = Math.pow(BYTES, 3);
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const {
  snapshotState: states,
  diffingState,
  censusState,
  treeMapState,
  dominatorTreeState,
  individualsState,
} = require("./constants");

/**
 * Takes a snapshot object and returns the localized form of its timestamp to be
 * used as a title.
 *
 * @param {Snapshot} snapshot
 * @return {String}
 */
exports.getSnapshotTitle = function (snapshot) {
  if (!snapshot.creationTime) {
    return L10N.getStr("snapshot-title.loading");
  }

  if (snapshot.imported) {
    // Strip out the extension if it's the expected ".fxsnapshot"
    return OS.Path.basename(snapshot.path.replace(/\.fxsnapshot$/, ""));
  }

  let date = new Date(snapshot.creationTime / 1000);
  return date.toLocaleTimeString(void 0, {
    year: "2-digit",
    month: "2-digit",
    day: "2-digit",
    hour12: false
  });
};

function getCustomDisplaysHelper(pref) {
  let customDisplays = Object.create(null);
  try {
    customDisplays = JSON.parse(Preferences.get(pref)) || Object.create(null);
  } catch (e) {
    DevToolsUtils.reportException(
      `String stored in "${pref}" pref cannot be parsed by \`JSON.parse()\`.`);
  }
  return Object.freeze(customDisplays);
}

/**
 * Returns custom displays defined in `devtools.memory.custom-census-displays`
 * pref.
 *
 * @return {Object}
 */
exports.getCustomCensusDisplays = function () {
  return getCustomDisplaysHelper(CUSTOM_CENSUS_DISPLAY_PREF);
};

/**
 * Returns custom displays defined in
 * `devtools.memory.custom-label-displays` pref.
 *
 * @return {Object}
 */
exports.getCustomLabelDisplays = function () {
  return getCustomDisplaysHelper(CUSTOM_LABEL_DISPLAY_PREF);
};

/**
 * Returns custom displays defined in
 * `devtools.memory.custom-tree-map-displays` pref.
 *
 * @return {Object}
 */
exports.getCustomTreeMapDisplays = function () {
  return getCustomDisplaysHelper(CUSTOM_TREE_MAP_DISPLAY_PREF);
};

/**
 * Returns a string representing a readable form of the snapshot's state. More
 * concise than `getStatusTextFull`.
 *
 * @param {snapshotState | diffingState} state
 * @return {String}
 */
exports.getStatusText = function (state) {
  assert(state, "Must have a state");

  switch (state) {
    case diffingState.ERROR:
      return L10N.getStr("diffing.state.error");

    case states.ERROR:
      return L10N.getStr("snapshot.state.error");

    case states.SAVING:
      return L10N.getStr("snapshot.state.saving");

    case states.IMPORTING:
      return L10N.getStr("snapshot.state.importing");

    case states.SAVED:
    case states.READING:
      return L10N.getStr("snapshot.state.reading");

    case censusState.SAVING:
      return L10N.getStr("snapshot.state.saving-census");

    case treeMapState.SAVING:
      return L10N.getStr("snapshot.state.saving-tree-map");

    case diffingState.TAKING_DIFF:
      return L10N.getStr("diffing.state.taking-diff");

    case diffingState.SELECTING:
      return L10N.getStr("diffing.state.selecting");

    case dominatorTreeState.COMPUTING:
    case individualsState.COMPUTING_DOMINATOR_TREE:
      return L10N.getStr("dominatorTree.state.computing");

    case dominatorTreeState.COMPUTED:
    case dominatorTreeState.FETCHING:
      return L10N.getStr("dominatorTree.state.fetching");

    case dominatorTreeState.INCREMENTAL_FETCHING:
      return L10N.getStr("dominatorTree.state.incrementalFetching");

    case dominatorTreeState.ERROR:
      return L10N.getStr("dominatorTree.state.error");

    case individualsState.ERROR:
      return L10N.getStr("individuals.state.error");

    case individualsState.FETCHING:
      return L10N.getStr("individuals.state.fetching");

    // These states do not have any message to show as other content will be
    // displayed.
    case dominatorTreeState.LOADED:
    case diffingState.TOOK_DIFF:
    case states.READ:
    case censusState.SAVED:
    case treeMapState.SAVED:
    case individualsState.FETCHED:
      return "";

    default:
      assert(false, `Unexpected state: ${state}`);
      return "";
  }
};

/**
 * Returns a string representing a readable form of the snapshot's state;
 * more verbose than `getStatusText`.
 *
 * @param {snapshotState | diffingState} state
 * @return {String}
 */
exports.getStatusTextFull = function (state) {
  assert(!!state, "Must have a state");

  switch (state) {
    case diffingState.ERROR:
      return L10N.getStr("diffing.state.error.full");

    case states.ERROR:
      return L10N.getStr("snapshot.state.error.full");

    case states.SAVING:
      return L10N.getStr("snapshot.state.saving.full");

    case states.IMPORTING:
      return L10N.getStr("snapshot.state.importing");

    case states.SAVED:
    case states.READING:
      return L10N.getStr("snapshot.state.reading.full");

    case censusState.SAVING:
      return L10N.getStr("snapshot.state.saving-census.full");

    case treeMapState.SAVING:
      return L10N.getStr("snapshot.state.saving-tree-map.full");

    case diffingState.TAKING_DIFF:
      return L10N.getStr("diffing.state.taking-diff.full");

    case diffingState.SELECTING:
      return L10N.getStr("diffing.state.selecting.full");

    case dominatorTreeState.COMPUTING:
    case individualsState.COMPUTING_DOMINATOR_TREE:
      return L10N.getStr("dominatorTree.state.computing.full");

    case dominatorTreeState.COMPUTED:
    case dominatorTreeState.FETCHING:
      return L10N.getStr("dominatorTree.state.fetching.full");

    case dominatorTreeState.INCREMENTAL_FETCHING:
      return L10N.getStr("dominatorTree.state.incrementalFetching.full");

    case dominatorTreeState.ERROR:
      return L10N.getStr("dominatorTree.state.error.full");

    case individualsState.ERROR:
      return L10N.getStr("individuals.state.error.full");

    case individualsState.FETCHING:
      return L10N.getStr("individuals.state.fetching.full");

    // These states do not have any full message to show as other content will
    // be displayed.
    case dominatorTreeState.LOADED:
    case diffingState.TOOK_DIFF:
    case states.READ:
    case censusState.SAVED:
    case treeMapState.SAVED:
    case individualsState.FETCHED:
      return "";

    default:
      assert(false, `Unexpected state: ${state}`);
      return "";
  }
};

/**
 * Return true if the snapshot is in a diffable state, false otherwise.
 *
 * @param {snapshotModel} snapshot
 * @returns {Boolean}
 */
exports.snapshotIsDiffable = function snapshotIsDiffable(snapshot) {
  return (snapshot.census && snapshot.census.state === censusState.SAVED)
    || (snapshot.census && snapshot.census.state === censusState.SAVING)
    || snapshot.state === states.SAVED
    || snapshot.state === states.READ;
};

/**
 * Takes an array of snapshots and a snapshot and returns
 * the snapshot instance in `snapshots` that matches
 * the snapshot passed in.
 *
 * @param {appModel} state
 * @param {snapshotId} id
 * @return {snapshotModel|null}
 */
exports.getSnapshot = function getSnapshot(state, id) {
  const found = state.snapshots.find(s => s.id === id);
  assert(found, `No matching snapshot found with id = ${id}`);
  return found;
};

/**
 * Get the ID of the selected snapshot, if one is selected, null otherwise.
 *
 * @returns {SnapshotId|null}
 */
exports.findSelectedSnapshot = function (state) {
  const found = state.snapshots.find(s => s.selected);
  return found ? found.id : null;
};

/**
 * Creates a new snapshot object.
 *
 * @param {appModel} state
 * @return {Snapshot}
 */
let ID_COUNTER = 0;
exports.createSnapshot = function createSnapshot(state) {
  let dominatorTree = null;
  if (state.view.state === dominatorTreeState.DOMINATOR_TREE) {
    dominatorTree = Object.freeze({
      dominatorTreeId: null,
      root: null,
      error: null,
      state: dominatorTreeState.COMPUTING,
    });
  }

  return Object.freeze({
    id: ++ID_COUNTER,
    state: states.SAVING,
    dominatorTree,
    census: null,
    treeMap: null,
    path: null,
    imported: false,
    selected: false,
    error: null,
  });
};

/**
 * Return true if the census is up to date with regards to the current filtering
 * and requested display, false otherwise.
 *
 * @param {String} filter
 * @param {censusDisplayModel} display
 * @param {censusModel} census
 *
 * @returns {Boolean}
 */
exports.censusIsUpToDate = function (filter, display, census) {
  return census
      // Filter could be null == undefined so use loose equality.
      && filter == census.filter
      && display === census.display;
};

/**
 * Check to see if the snapshot is in a state that it can take a census.
 *
 * @param {SnapshotModel} A snapshot to check.
 * @param {Boolean} Assert that the snapshot must be in a ready state.
 * @returns {Boolean}
 */
exports.canTakeCensus = function (snapshot) {
  return snapshot.state === states.READ &&
    ((!snapshot.census || snapshot.census.state === censusState.SAVED) ||
     (!snapshot.treeMap || snapshot.treeMap.state === treeMapState.SAVED));
};

/**
 * Returns true if the given snapshot's dominator tree has been computed, false
 * otherwise.
 *
 * @param {SnapshotModel} snapshot
 * @returns {Boolean}
 */
exports.dominatorTreeIsComputed = function (snapshot) {
  return snapshot.dominatorTree &&
    (snapshot.dominatorTree.state === dominatorTreeState.COMPUTED ||
     snapshot.dominatorTree.state === dominatorTreeState.LOADED ||
     snapshot.dominatorTree.state === dominatorTreeState.INCREMENTAL_FETCHING);
};

/**
 * Find the first SAVED census, either from the tree map or the normal
 * census.
 *
 * @param {SnapshotModel} snapshot
 * @returns {Object|null} Either the census, or null if one hasn't completed
 */
exports.getSavedCensus = function (snapshot) {
  if (snapshot.treeMap && snapshot.treeMap.state === treeMapState.SAVED) {
    return snapshot.treeMap;
  }
  if (snapshot.census && snapshot.census.state === censusState.SAVED) {
    return snapshot.census;
  }
  return null;
};

/**
 * Takes a snapshot and returns the total bytes and total count that this
 * snapshot represents.
 *
 * @param {CensusModel} census
 * @return {Object}
 */
exports.getSnapshotTotals = function (census) {
  let bytes = 0;
  let count = 0;

  let report = census.report;
  if (report) {
    bytes = report.totalBytes;
    count = report.totalCount;
  }

  return { bytes, count };
};

/**
 * Takes some configurations and opens up a file picker and returns
 * a promise to the chosen file if successful.
 *
 * @param {String} .title
 *        The title displayed in the file picker window.
 * @param {Array<Array<String>>} .filters
 *        An array of filters to display in the file picker. Each filter in the array
 *        is a duple of two strings, one a name for the filter, and one the filter itself
 *        (like "*.json").
 * @param {String} .defaultName
 *        The default name chosen by the file picker window.
 * @param {String} .mode
 *        The mode that this filepicker should open in. Can be "open" or "save".
 * @return {Promise<?nsIFile>}
 *        The file selected by the user, or null, if cancelled.
 */
exports.openFilePicker = function ({ title, filters, defaultName, mode }) {
  let fpMode;
  if (mode === "save") {
    fpMode = Ci.nsIFilePicker.modeSave;
  } else if (mode === "open") {
    fpMode = Ci.nsIFilePicker.modeOpen;
  } else {
    throw new Error("No valid mode specified for nsIFilePicker.");
  }

  let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
  fp.init(window, title, fpMode);

  for (let filter of (filters || [])) {
    fp.appendFilter(filter[0], filter[1]);
  }
  fp.defaultString = defaultName;

  return new Promise(resolve => {
    fp.open({
      done: result => {
        if (result === Ci.nsIFilePicker.returnCancel) {
          resolve(null);
          return;
        }
        resolve(fp.file);
      }
    });
  });
};

/**
 * Format the provided number with a space every 3 digits, and optionally
 * prefixed by its sign.
 *
 * @param {Number} number
 * @param {Boolean} showSign (defaults to false)
 */
exports.formatNumber = function (number, showSign = false) {
  const rounded = Math.round(number);
  if (rounded === 0 || rounded === -0) {
    return "0";
  }

  const abs = String(Math.abs(rounded));
  // replace every digit followed by (sets of 3 digits) by (itself and a space)
  const formatted = abs.replace(/(\d)(?=(\d{3})+$)/g, "$1 ");

  if (showSign) {
    const sign = rounded < 0 ? "-" : "+";
    return sign + formatted;
  }
  return formatted;
};

/**
 * Format the provided percentage following the same logic as formatNumber and
 * an additional % suffix.
 *
 * @param {Number} percent
 * @param {Boolean} showSign (defaults to false)
 */
exports.formatPercent = function (percent, showSign = false) {
  return exports.L10N.getFormatStr("tree-item.percent2",
                           exports.formatNumber(percent, showSign));
};

/**
 * Change an HSL color array with values ranged 0-1 to a properly formatted
 * ctx.fillStyle string.
 *
 * @param  {Number} h
 *         hue values ranged between [0 - 1]
 * @param  {Number} s
 *         hue values ranged between [0 - 1]
 * @param  {Number} l
 *         hue values ranged between [0 - 1]
 * @return {type}
 */
exports.hslToStyle = function (h, s, l) {
  h = parseInt(h * 360, 10);
  s = parseInt(s * 100, 10);
  l = parseInt(l * 100, 10);

  return `hsl(${h},${s}%,${l}%)`;
};

/**
 * Linearly interpolate between 2 numbers.
 *
 * @param {Number} a
 * @param {Number} b
 * @param {Number} t
 *        A value of 0 returns a, and 1 returns b
 * @return {Number}
 */
exports.lerp = function (a, b, t) {
  return a * (1 - t) + b * t;
};

/**
 * Format a number of bytes as human readable, e.g. 13434 => '13KiB'.
 *
 * @param  {Number} n
 *         Number of bytes
 * @return {String}
 */
exports.formatAbbreviatedBytes = function (n) {
  if (n < BYTES) {
    return n + "B";
  } else if (n < KILOBYTES) {
    return Math.floor(n / BYTES) + "KiB";
  } else if (n < MEGABYTES) {
    return Math.floor(n / KILOBYTES) + "MiB";
  }
  return Math.floor(n / MEGABYTES) + "GiB";
};
PK
!<챸�880chrome/devtools/modules/devtools/client/menus.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 defines the sorted list of menuitems inserted into the
 * "Web Developer" menu.
 * It also defines the key shortcuts that relates to them.
 *
 * Various fields are necessary for historical compatiblity with XUL/addons:
 * - id:
 *   used as <xul:menuitem> id attribute
 * - l10nKey:
 *   prefix used to locale localization strings from menus.properties
 * - oncommand:
 *   function called when the menu item or key shortcut are fired
 * - keyId:
 *   Identifier used in devtools/client/devtools-startup.js
 *   Helps figuring out the DOM id for the related <xul:key>
 *   in order to have the key text displayed in menus.
 * - disabled:
 *   If true, the menuitem and key shortcut are going to be hidden and disabled
 *   on startup, until some runtime code eventually enable them.
 * - checkbox:
 *   If true, the menuitem is prefixed by a checkbox and runtime code can
 *   toggle it.
 */

loader.lazyRequireGetter(this, "gDevToolsBrowser", "devtools/client/framework/devtools-browser", true);
loader.lazyRequireGetter(this, "CommandUtils", "devtools/client/shared/developer-toolbar", true);
loader.lazyRequireGetter(this, "TargetFactory", "devtools/client/framework/target", true);
loader.lazyRequireGetter(this, "ResponsiveUIManager", "devtools/client/responsive.html/manager", true);

loader.lazyImporter(this, "BrowserToolboxProcess", "resource://devtools/client/framework/ToolboxProcess.jsm");
loader.lazyImporter(this, "ScratchpadManager", "resource://devtools/client/scratchpad/scratchpad-manager.jsm");

exports.menuitems = [
  { id: "menu_devToolbox",
    l10nKey: "devToolboxMenuItem",
    oncommand(event) {
      let window = event.target.ownerDocument.defaultView;
      gDevToolsBrowser.toggleToolboxCommand(window.gBrowser, window.performance.now());
    },
    keyId: "toggleToolbox",
    checkbox: true
  },
  { id: "menu_devtools_separator",
    separator: true },
  { id: "menu_devToolbar",
    l10nKey: "devToolbarMenu",
    disabled: true,
    oncommand(event) {
      let window = event.target.ownerDocument.defaultView;
      // Distinguish events when selecting a menuitem, where we either open
      // or close the toolbar and when hitting the key shortcut where we just
      // focus the toolbar if it doesn't already has it.
      if (event.target.tagName.toLowerCase() == "menuitem") {
        gDevToolsBrowser.getDeveloperToolbar(window).toggle();
      } else {
        gDevToolsBrowser.getDeveloperToolbar(window).focusToggle();
      }
    },
    keyId: "toggleToolbar",
    checkbox: true
  },
  { id: "menu_webide",
    l10nKey: "webide",
    disabled: true,
    oncommand() {
      gDevToolsBrowser.openWebIDE();
    },
    keyId: "webide",
  },
  { id: "menu_browserToolbox",
    l10nKey: "browserToolboxMenu",
    disabled: true,
    oncommand() {
      BrowserToolboxProcess.init();
    },
    keyId: "browserToolbox",
  },
  { id: "menu_browserContentToolbox",
    l10nKey: "browserContentToolboxMenu",
    disabled: true,
    oncommand(event) {
      let window = event.target.ownerDocument.defaultView;
      gDevToolsBrowser.openContentProcessToolbox(window.gBrowser);
    }
  },
  { id: "menu_browserConsole",
    l10nKey: "browserConsoleCmd",
    oncommand() {
      let {HUDService} = require("devtools/client/webconsole/hudservice");
      HUDService.openBrowserConsoleOrFocus();
    },
    keyId: "browserConsole",
  },
  { id: "menu_responsiveUI",
    l10nKey: "responsiveDesignMode",
    oncommand(event) {
      let window = event.target.ownerDocument.defaultView;
      ResponsiveUIManager.toggle(window, window.gBrowser.selectedTab);
    },
    keyId: "responsiveDesignMode",
    checkbox: true
  },
  { id: "menu_eyedropper",
    l10nKey: "eyedropper",
    oncommand(event) {
      let window = event.target.ownerDocument.defaultView;
      let target = TargetFactory.forTab(window.gBrowser.selectedTab);

      CommandUtils.executeOnTarget(target, "eyedropper --frommenu");
    },
    checkbox: true
  },
  { id: "menu_scratchpad",
    l10nKey: "scratchpad",
    oncommand() {
      ScratchpadManager.openScratchpad();
    },
    keyId: "scratchpad",
  },
  { id: "menu_devtools_serviceworkers",
    l10nKey: "devtoolsServiceWorkers",
    disabled: true,
    oncommand(event) {
      let window = event.target.ownerDocument.defaultView;
      gDevToolsBrowser.openAboutDebugging(window.gBrowser, "workers");
    }
  },
  { id: "menu_devtools_connect",
    l10nKey: "devtoolsConnect",
    disabled: true,
    oncommand(event) {
      let window = event.target.ownerDocument.defaultView;
      gDevToolsBrowser.openConnectScreen(window.gBrowser);
    }
  },
  { separator: true,
    id: "devToolsEndSeparator"
  },
  { id: "getMoreDevtools",
    l10nKey: "getMoreDevtoolsCmd",
    oncommand(event) {
      let window = event.target.ownerDocument.defaultView;
      window.openUILinkIn("https://addons.mozilla.org/firefox/collections/mozilla/webdeveloper/", "tab");
    }
  },
];
PK
!<�~**;chrome/devtools/modules/devtools/client/netmonitor/panel.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 NetMonitorPanel(iframeWindow, toolbox) {
  this.panelWin = iframeWindow;
  this.toolbox = toolbox;
}

NetMonitorPanel.prototype = {
  async open() {
    if (!this.toolbox.target.isRemote) {
      await this.toolbox.target.makeRemote();
    }
    await this.panelWin.Netmonitor.bootstrap({
      toolbox: this.toolbox,
      panel: this,
    });
    this.emit("ready");
    this.isReady = true;
    return this;
  },

  async destroy() {
    await this.panelWin.Netmonitor.destroy();
    this.emit("destroyed");
    return this;
  },
};

exports.NetMonitorPanel = NetMonitorPanel;
PK
!<��//Jchrome/devtools/modules/devtools/client/netmonitor/src/actions/batching.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  BATCH_ACTIONS,
  BATCH_ENABLE,
  BATCH_RESET,
} = require("../constants");

/**
 * Process multiple actions at once as part of one dispatch, and produce only one
 * state update at the end. This action is not processed by any reducer, but by a
 * special store enhancer.
 */
function batchActions(actions) {
  return {
    type: BATCH_ACTIONS,
    actions
  };
}

function batchEnable(enabled) {
  return {
    type: BATCH_ENABLE,
    enabled
  };
}

function batchReset() {
  return {
    type: BATCH_RESET,
  };
}

module.exports = {
  batchActions,
  batchEnable,
  batchReset,
};
PK
!<9�q�uuIchrome/devtools/modules/devtools/client/netmonitor/src/actions/filters.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  ENABLE_REQUEST_FILTER_TYPE_ONLY,
  TOGGLE_REQUEST_FILTER_TYPE,
  SET_REQUEST_FILTER_TEXT,
} = require("../constants");

/**
 * Toggle an existing filter type state.
 * If type 'all' is specified, all the other filter types are set to false.
 * Available filter types are defined in filters reducer.
 *
 * @param {string} filter - A filter type is going to be updated
 */
function toggleRequestFilterType(filter) {
  return {
    type: TOGGLE_REQUEST_FILTER_TYPE,
    filter,
  };
}

/**
 * Enable filter type exclusively.
 * Except filter type is set to true, all the other filter types are set
 * to false.
 * Available filter types are defined in filters reducer.
 *
 * @param {string} filter - A filter type is going to be updated
 */
function enableRequestFilterTypeOnly(filter) {
  return {
    type: ENABLE_REQUEST_FILTER_TYPE_ONLY,
    filter,
  };
}

/**
 * Set filter text in toolbar.
 *
 * @param {string} text - A filter text is going to be set
 */
function setRequestFilterText(text) {
  return {
    type: SET_REQUEST_FILTER_TEXT,
    text,
  };
}

module.exports = {
  enableRequestFilterTypeOnly,
  toggleRequestFilterType,
  setRequestFilterText,
};
PK
!<��l~UUGchrome/devtools/modules/devtools/client/netmonitor/src/actions/index.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 batching = require("./batching");
const filters = require("./filters");
const requests = require("./requests");
const selection = require("./selection");
const sort = require("./sort");
const timingMarkers = require("./timing-markers");
const ui = require("./ui");

Object.assign(exports,
  batching,
  filters,
  requests,
  selection,
  sort,
  timingMarkers,
  ui
);
PK
!<�tdA#	#	Jchrome/devtools/modules/devtools/client/netmonitor/src/actions/requests.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  ADD_REQUEST,
  CLEAR_REQUESTS,
  CLONE_SELECTED_REQUEST,
  REMOVE_SELECTED_CUSTOM_REQUEST,
  SEND_CUSTOM_REQUEST,
  TOGGLE_RECORDING,
  UPDATE_REQUEST,
} = require("../constants");
const { getSelectedRequest } = require("../selectors/index");

function addRequest(id, data, batch) {
  return {
    type: ADD_REQUEST,
    id,
    data,
    meta: { batch },
  };
}

function updateRequest(id, data, batch) {
  return {
    type: UPDATE_REQUEST,
    id,
    data,
    meta: { batch },
  };
}

/**
 * Clone the currently selected request, set the "isCustom" attribute.
 * Used by the "Edit and Resend" feature.
 */
function cloneSelectedRequest() {
  return {
    type: CLONE_SELECTED_REQUEST
  };
}

/**
 * Send a new HTTP request using the data in the custom request form.
 */
function sendCustomRequest(connector) {
  return (dispatch, getState) => {
    const selected = getSelectedRequest(getState());

    if (!selected) {
      return;
    }

    // Send a new HTTP request using the data in the custom request form
    let data = {
      url: selected.url,
      method: selected.method,
      httpVersion: selected.httpVersion,
    };
    if (selected.requestHeaders) {
      data.headers = selected.requestHeaders.headers;
    }
    if (selected.requestPostData) {
      data.body = selected.requestPostData.postData.text;
    }

    connector.sendHTTPRequest(data, (response) => {
      return dispatch({
        type: SEND_CUSTOM_REQUEST,
        id: response.eventActor.actor,
      });
    });
  };
}

/**
 * Remove a request from the list. Supports removing only cloned requests with a
 * "isCustom" attribute. Other requests never need to be removed.
 */
function removeSelectedCustomRequest() {
  return {
    type: REMOVE_SELECTED_CUSTOM_REQUEST
  };
}

function clearRequests() {
  return {
    type: CLEAR_REQUESTS
  };
}

/**
 * Toggle monitoring
 */
function toggleRecording() {
  return {
    type: TOGGLE_RECORDING
  };
}

module.exports = {
  addRequest,
  clearRequests,
  cloneSelectedRequest,
  removeSelectedCustomRequest,
  sendCustomRequest,
  toggleRecording,
  updateRequest,
};
PK
!<�*CrrKchrome/devtools/modules/devtools/client/netmonitor/src/actions/selection.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { SELECT_REQUEST } = require("../constants");
const {
  getDisplayedRequests,
  getSortedRequests,
} = require("../selectors/index");

const PAGE_SIZE_ITEM_COUNT_RATIO = 5;

/**
 * Select request with a given id.
 */
function selectRequest(id) {
  return {
    type: SELECT_REQUEST,
    id,
  };
}

/**
 * Select request with a given index (sorted order)
 */
function selectRequestByIndex(index) {
  return (dispatch, getState) => {
    const requests = getSortedRequests(getState());
    let itemId;
    if (index >= 0 && index < requests.size) {
      itemId = requests.get(index).id;
    }
    dispatch(selectRequest(itemId));
  };
}

/**
 * Move the selection up to down according to the "delta" parameter. Possible values:
 * - Number: positive or negative, move up or down by specified distance
 * - "PAGE_UP" | "PAGE_DOWN" (String): page up or page down
 * - +Infinity | -Infinity: move to the start or end of the list
 */
function selectDelta(delta) {
  return (dispatch, getState) => {
    const state = getState();
    const requests = getDisplayedRequests(state);

    if (requests.isEmpty()) {
      return;
    }

    const selIndex = requests.findIndex(r => r.id === state.requests.selectedId);

    if (delta === "PAGE_DOWN") {
      delta = Math.ceil(requests.size / PAGE_SIZE_ITEM_COUNT_RATIO);
    } else if (delta === "PAGE_UP") {
      delta = -Math.ceil(requests.size / PAGE_SIZE_ITEM_COUNT_RATIO);
    }

    const newIndex = Math.min(Math.max(0, selIndex + delta), requests.size - 1);
    const newItem = requests.get(newIndex);
    dispatch(selectRequest(newItem.id));
  };
}

module.exports = {
  selectRequest,
  selectRequestByIndex,
  selectDelta,
};
PK
!<D�Q&yyFchrome/devtools/modules/devtools/client/netmonitor/src/actions/sort.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { SORT_BY } = require("../constants");

function sortBy(sortType) {
  return {
    type: SORT_BY,
    sortType
  };
}

module.exports = {
  sortBy
};
PK
!<����Pchrome/devtools/modules/devtools/client/netmonitor/src/actions/timing-markers.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { ADD_TIMING_MARKER, CLEAR_TIMING_MARKERS } = require("../constants");

exports.addTimingMarker = (marker) => {
  return {
    type: ADD_TIMING_MARKER,
    marker
  };
};

exports.clearTimingMarkers = () => {
  return {
    type: CLEAR_TIMING_MARKERS
  };
};
PK
!<�����Dchrome/devtools/modules/devtools/client/netmonitor/src/actions/ui.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  ACTIVITY_TYPE,
  OPEN_NETWORK_DETAILS,
  RESIZE_NETWORK_DETAILS,
  ENABLE_PERSISTENT_LOGS,
  DISABLE_BROWSER_CACHE,
  OPEN_STATISTICS,
  RESET_COLUMNS,
  SELECT_DETAILS_PANEL_TAB,
  TOGGLE_COLUMN,
  WATERFALL_RESIZE,
} = require("../constants");

const { getDisplayedRequests } = require("../selectors/index");

/**
 * Change network details panel.
 *
 * @param {boolean} open - expected network details panel open state
 */
function openNetworkDetails(open) {
  return (dispatch, getState) => {
    const visibleRequestItems = getDisplayedRequests(getState());
    let defaultSelectedId = visibleRequestItems.length ? visibleRequestItems[0].id : null;

    return dispatch({
      type: OPEN_NETWORK_DETAILS,
      open,
      defaultSelectedId,
    });
  };
}

/**
 * Change network details panel size.
 *
 * @param {integer} width
 * @param {integer} height
 */
function resizeNetworkDetails(width, height) {
  return {
    type: RESIZE_NETWORK_DETAILS,
    width,
    height,
  };
}

/**
 * Change persistent logs state.
 *
 * @param {boolean} enabled - expected persistent logs enabled state
 */
function enablePersistentLogs(enabled) {
  return {
    type: ENABLE_PERSISTENT_LOGS,
    enabled,
  };
}

/**
 * Change browser cache state.
 *
 * @param {boolean} disabled - expected browser cache in disable state
 */
function disableBrowserCache(disabled) {
  return {
    type: DISABLE_BROWSER_CACHE,
    disabled,
  };
}

/**
 * Change performance statistics panel open state.
 *
 * @param {Object} connector - connector object to the backend
 * @param {boolean} visible - expected performance statistics panel open state
 */
function openStatistics(connector, open) {
  if (open) {
    connector.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED);
  }
  return {
    type: OPEN_STATISTICS,
    open,
  };
}

/**
 * Resets all columns to their default state.
 *
 */
function resetColumns() {
  return {
    type: RESET_COLUMNS,
  };
}

/**
 * Waterfall width has changed (likely on window resize). Update the UI.
 */
function resizeWaterfall(width) {
  return {
    type: WATERFALL_RESIZE,
    width
  };
}

/**
 * Change the selected tab for network details panel.
 *
 * @param {string} id - tab id to be selected
 */
function selectDetailsPanelTab(id) {
  return {
    type: SELECT_DETAILS_PANEL_TAB,
    id,
  };
}

/**
 * Toggles a column
 *
 * @param {string} column - The column that is going to be toggled
 */
function toggleColumn(column) {
  return {
    type: TOGGLE_COLUMN,
    column,
  };
}

/**
 * Toggle network details panel.
 */
function toggleNetworkDetails() {
  return (dispatch, getState) =>
    dispatch(openNetworkDetails(!getState().ui.networkDetailsOpen));
}

/**
 * Toggle persistent logs status.
 */
function togglePersistentLogs() {
  return (dispatch, getState) =>
    dispatch(enablePersistentLogs(!getState().ui.persistentLogsEnabled));
}

/**
 * Toggle browser cache status.
 */
function toggleBrowserCache() {
  return (dispatch, getState) =>
    dispatch(disableBrowserCache(!getState().ui.browserCacheDisabled));
}

/**
 * Toggle performance statistics panel.
 */
function toggleStatistics(connector) {
  return (dispatch, getState) =>
    dispatch(openStatistics(connector, !getState().ui.statisticsOpen));
}

module.exports = {
  openNetworkDetails,
  resizeNetworkDetails,
  enablePersistentLogs,
  disableBrowserCache,
  openStatistics,
  resetColumns,
  resizeWaterfall,
  selectDetailsPanelTab,
  toggleColumn,
  toggleNetworkDetails,
  togglePersistentLogs,
  toggleBrowserCache,
  toggleStatistics,
};
PK
!<h'J���Hchrome/devtools/modules/devtools/client/netmonitor/src/components/App.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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 { connect } = require("devtools/client/shared/redux/visibility-handler-connect");

// Components
loader.lazyGetter(this, "MonitorPanel", function () {
  return createFactory(require("./MonitorPanel"));
});
loader.lazyGetter(this, "StatisticsPanel", function () {
  return createFactory(require("./StatisticsPanel"));
});
loader.lazyGetter(this, "DropHarHandler", function () {
  return createFactory(require("./DropHarHandler"));
});

const { div } = dom;

/**
 * App component
 * The top level component for representing main panel
 */
class App extends Component {
  static get propTypes() {
    return {
      // List of actions passed to HAR importer.
      actions: PropTypes.object.isRequired,
      // The backend connector object.
      connector: PropTypes.object.isRequired,
      // Callback for opening links in the UI
      openLink: PropTypes.func,
      // Callback for opening split console.
      openSplitConsole: PropTypes.func,
      // Service to enable the source map feature.
      sourceMapService: PropTypes.object,
      // True if the stats panel is opened.
      statisticsOpen: PropTypes.bool.isRequired,
    };
  }

  // Rendering

  render() {
    let {
      actions,
      connector,
      openLink,
      openSplitConsole,
      sourceMapService,
      statisticsOpen,
    } = this.props;

    return (
      div({className: "network-monitor"},
        !statisticsOpen ?
          DropHarHandler({
            actions,
            openSplitConsole,
          },
            MonitorPanel({
              connector,
              sourceMapService,
              openLink,
            })
          ) : StatisticsPanel({
            connector
          }),
      )
    );
  }
}

// Exports

module.exports = connect(
  (state) => ({ statisticsOpen: state.ui.statisticsOpen }),
)(App);
PK
!<�"na�
�
Qchrome/devtools/modules/devtools/client/netmonitor/src/components/CookiesPanel.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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 { L10N } = require("../utils/l10n");
const { fetchNetworkUpdatePacket } = require("../utils/request-utils");
const { sortObjectKeys } = require("../utils/sort-utils");

// Component
const PropertiesView = createFactory(require("./PropertiesView"));

const { div } = dom;

const COOKIES_EMPTY_TEXT = L10N.getStr("cookiesEmptyText");
const COOKIES_FILTER_TEXT = L10N.getStr("cookiesFilterText");
const REQUEST_COOKIES = L10N.getStr("requestCookies");
const RESPONSE_COOKIES = L10N.getStr("responseCookies");
const SECTION_NAMES = [
  RESPONSE_COOKIES,
  REQUEST_COOKIES,
];

/*
 * Cookies panel component
 * This tab lists full details of any cookies sent with the request or response
 */
class CookiesPanel extends Component {
  static get propTypes() {
    return {
      connector: PropTypes.object.isRequired,
      openLink: PropTypes.func,
      request: PropTypes.object.isRequired,
    };
  }

  componentDidMount() {
    let { connector, request } = this.props;
    fetchNetworkUpdatePacket(connector.requestData, request, [
      "requestCookies",
      "responseCookies",
    ]);
  }

  componentWillReceiveProps(nextProps) {
    let { connector, request } = nextProps;
    fetchNetworkUpdatePacket(connector.requestData, request, [
      "requestCookies",
      "responseCookies",
    ]);
  }

  /**
   * Mapping array to dict for TreeView usage.
   * Since TreeView only support Object(dict) format.
   *
   * @param {Object[]} arr - key-value pair array like cookies or params
   * @returns {Object}
   */
  getProperties(arr) {
    return arr.reduce((map, obj) => {
      // Generally cookies object contains only name and value properties and can
      // be rendered as name: value pair.
      // When there are more properties in cookies object such as extra or path,
      // We will pass the object to display these extra information
      if (Object.keys(obj).length > 2) {
        map[obj.name] = Object.assign({}, obj);
        delete map[obj.name].name;
      } else {
        map[obj.name] = obj.value;
      }
      return map;
    }, {});
  }

  render() {
    let {
      request: {
        requestCookies = { cookies: [] },
        responseCookies = { cookies: [] },
      },
      openLink,
    } = this.props;

    requestCookies = requestCookies.cookies || requestCookies;
    responseCookies = responseCookies.cookies || responseCookies;

    if (!requestCookies.length && !responseCookies.length) {
      return div({ className: "empty-notice" },
        COOKIES_EMPTY_TEXT
      );
    }

    let object = {};

    if (responseCookies.length) {
      object[RESPONSE_COOKIES] = sortObjectKeys(this.getProperties(responseCookies));
    }

    if (requestCookies.length) {
      object[REQUEST_COOKIES] = sortObjectKeys(this.getProperties(requestCookies));
    }

    return (
      div({ className: "panel-container" },
        PropertiesView({
          object,
          filterPlaceHolder: COOKIES_FILTER_TEXT,
          sectionNames: SECTION_NAMES,
          openLink,
        })
      )
    );
  }
}

module.exports = CookiesPanel;
PK
!<׬(&H$H$Wchrome/devtools/modules/devtools/client/netmonitor/src/components/CustomRequestPanel.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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 { connect } = require("devtools/client/shared/redux/visibility-handler-connect");
const { L10N } = require("../utils/l10n");
const { fetchNetworkUpdatePacket } = require("../utils/request-utils");
const Actions = require("../actions/index");
const { getSelectedRequest } = require("../selectors/index");
const {
  getUrlQuery,
  parseQueryString,
  writeHeaderText,
} = require("../utils/request-utils");

const {
  button,
  div,
  input,
  textarea,
} = dom;

const CUSTOM_CANCEL = L10N.getStr("netmonitor.custom.cancel");
const CUSTOM_HEADERS = L10N.getStr("netmonitor.custom.headers");
const CUSTOM_NEW_REQUEST = L10N.getStr("netmonitor.custom.newRequest");
const CUSTOM_POSTDATA = L10N.getStr("netmonitor.custom.postData");
const CUSTOM_QUERY = L10N.getStr("netmonitor.custom.query");
const CUSTOM_SEND = L10N.getStr("netmonitor.custom.send");

/*
 * Custom request panel component
 * A network request editor which simply provide edit and resend interface
 * for netowrk development.
 */
class CustomRequestPanel extends Component {
  static get propTypes() {
    return {
      connector: PropTypes.object.isRequired,
      removeSelectedCustomRequest: PropTypes.func.isRequired,
      request: PropTypes.object,
      sendCustomRequest: PropTypes.func.isRequired,
      updateRequest: PropTypes.func.isRequired,
    };
  }

  componentDidMount() {
    let { request, connector } = this.props;
    fetchNetworkUpdatePacket(connector.requestData, request, [
      "requestHeaders",
      "responseHeaders",
      "requestPostData",
    ]);
  }

  componentWillReceiveProps(nextProps) {
    let { request, connector } = nextProps;
    fetchNetworkUpdatePacket(connector.requestData, request, [
      "requestHeaders",
      "responseHeaders",
      "requestPostData",
    ]);
  }

  /**
   * Parse a text representation of a name[divider]value list with
   * the given name regex and divider character.
   *
   * @param {string} text - Text of list
   * @return {array} array of headers info {name, value}
   */
  parseRequestText(text, namereg, divider) {
    let regex = new RegExp(`(${namereg})\\${divider}\\s*(.+)`);
    let pairs = [];

    for (let line of text.split("\n")) {
      let matches = regex.exec(line);
      if (matches) {
        let [, name, value] = matches;
        pairs.push({ name, value });
      }
    }
    return pairs;
  }

  /**
   * Update Custom Request Fields
   *
   * @param {Object} evt click event
   * @param {Object} request current request
   * @param {updateRequest} updateRequest action
   */
  updateCustomRequestFields(evt, request, updateRequest) {
    const val = evt.target.value;
    let data;

    switch (evt.target.id) {
      case "custom-headers-value":
        let customHeadersValue = val || "";
        // Parse text representation of multiple HTTP headers
        let headersArray = this.parseRequestText(customHeadersValue, "\\S+?", ":");
        // Remove temp customHeadersValue while query string is parsable
        if (customHeadersValue === "" ||
          headersArray.length === customHeadersValue.split("\n").length) {
          customHeadersValue = null;
        }
        data = {
          requestHeaders: {
            customHeadersValue,
            headers: headersArray,
          },
        };
        break;
      case "custom-method-value":
        data = { method: val.trim() };
        break;
      case "custom-postdata-value":
        data = {
          requestPostData: {
            postData: { text: val },
          }
        };
        break;
      case "custom-query-value":
        let customQueryValue = val || "";
        // Parse readable text list of a query string
        let queryArray = customQueryValue ?
          this.parseRequestText(customQueryValue, ".+?", "=") : [];
        // Write out a list of query params into a query string
        let queryString = queryArray.map(
          ({ name, value }) => name + "=" + value).join("&");
        let url = queryString ? [request.url.split("?")[0], queryString].join("?") :
          request.url.split("?")[0];
        // Remove temp customQueryValue while query string is parsable
        if (customQueryValue === "" ||
          queryArray.length === customQueryValue.split("\n").length) {
          customQueryValue = null;
        }
        data = {
          customQueryValue,
          url,
        };
        break;
      case "custom-url-value":
        data = {
          customQueryValue: null,
          url: val
        };
        break;
      default:
        break;
    }
    if (data) {
      // All updateRequest batch mode should be disabled to make UI editing in sync
      updateRequest(request.id, data, false);
    }
  }

  render() {
    let {
      removeSelectedCustomRequest,
      request = {},
      sendCustomRequest,
      updateRequest,
    } = this.props;
    let {
      method,
      customQueryValue,
      requestHeaders,
      requestPostData,
      url,
    } = request;

    let headers = "";
    if (requestHeaders) {
      headers = requestHeaders.customHeadersValue ?
        requestHeaders.customHeadersValue : writeHeaderText(requestHeaders.headers);
    }
    let queryArray = url ? parseQueryString(getUrlQuery(url)) : [];
    let params = customQueryValue;
    if (!params) {
      params = queryArray ?
        queryArray.map(({ name, value }) => name + "=" + value).join("\n") : "";
    }
    let postData = requestPostData && requestPostData.postData.text ?
      requestPostData.postData.text : "";

    return (
      div({ className: "custom-request-panel" },
        div({ className: "tabpanel-summary-container custom-request" },
          div({ className: "custom-request-label custom-header" },
            CUSTOM_NEW_REQUEST,
          ),
          button({
            className: "devtools-button",
            id: "custom-request-send-button",
            onClick: sendCustomRequest,
          },
            CUSTOM_SEND,
          ),
          button({
            className: "devtools-button",
            id: "custom-request-close-button",
            onClick: removeSelectedCustomRequest,
          },
            CUSTOM_CANCEL,
          ),
        ),
        div({
          className: "tabpanel-summary-container custom-method-and-url",
          id: "custom-method-and-url",
        },
          input({
            className: "custom-method-value",
            id: "custom-method-value",
            onChange: (evt) =>
              this.updateCustomRequestFields(evt, request, updateRequest),
            value: method || "GET",
          }),
          input({
            className: "custom-url-value",
            id: "custom-url-value",
            onChange: (evt) =>
              this.updateCustomRequestFields(evt, request, updateRequest),
            value: url || "http://",
          }),
        ),
        // Hide query field when there is no params
        params ? div({
          className: "tabpanel-summary-container custom-section",
          id: "custom-query",
        },
          div({ className: "custom-request-label" }, CUSTOM_QUERY),
          textarea({
            className: "tabpanel-summary-input",
            id: "custom-query-value",
            onChange: (evt) =>
              this.updateCustomRequestFields(evt, request, updateRequest),
            rows: 4,
            value: params,
            wrap: "off",
          }),
        ) : null,
        div({
          id: "custom-headers",
          className: "tabpanel-summary-container custom-section",
        },
          div({ className: "custom-request-label" }, CUSTOM_HEADERS),
          textarea({
            className: "tabpanel-summary-input",
            id: "custom-headers-value",
            onChange: (evt) =>
              this.updateCustomRequestFields(evt, request, updateRequest),
            rows: 8,
            value: headers,
            wrap: "off",
          }),
        ),
        div({
          id: "custom-postdata",
          className: "tabpanel-summary-container custom-section",
        },
          div({ className: "custom-request-label" }, CUSTOM_POSTDATA),
          textarea({
            className: "tabpanel-summary-input",
            id: "custom-postdata-value",
            onChange: (evt) =>
              this.updateCustomRequestFields(evt, request, updateRequest),
            rows: 6,
            value: postData,
            wrap: "off",
          }),
        ),
      )
    );
  }
}

module.exports = connect(
  (state) => ({ request: getSelectedRequest(state) }),
  (dispatch, props) => ({
    removeSelectedCustomRequest: () => dispatch(Actions.removeSelectedCustomRequest()),
    sendCustomRequest: () => dispatch(Actions.sendCustomRequest(props.connector)),
    updateRequest: (id, data, batch) => dispatch(Actions.updateRequest(id, data, batch)),
  })
)(CustomRequestPanel);
PK
!<ֽÐ��Schrome/devtools/modules/devtools/client/netmonitor/src/components/DropHarHandler.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const { L10N } = require("../utils/l10n");

loader.lazyGetter(this, "HarImporter", function () {
  return require("../har/har-importer").HarImporter;
});

const { div } = dom;

const DROP_HAR_FILES = L10N.getStr("netmonitor.label.dropHarFiles");

/**
 * Helper component responsible for handling and  importing
 * dropped *.har files.
 */
class DropHarHandler extends Component {
  static get propTypes() {
    return {
      // List of actions passed to HAR importer.
      actions: PropTypes.object.isRequired,
      // Child component.
      children: PropTypes.element.isRequired,
      // Callback for opening split console.
      openSplitConsole: PropTypes.func,
    };
  }

  constructor(props) {
    super(props);

    // Drag and drop event handlers.
    this.onDragEnter = this.onDragEnter.bind(this);
    this.onDragOver = this.onDragOver.bind(this);
    this.onDragExit = this.onDragExit.bind(this);
    this.onDrop = this.onDrop.bind(this);
  }

  // Drag Events

  onDragEnter(event) {
    event.preventDefault();
    startDragging(findDOMNode(this));
  }

  onDragExit(event) {
    event.preventDefault();
    stopDragging(findDOMNode(this));
  }

  onDragOver(event) {
    event.preventDefault();
    event.dataTransfer.dropEffect = "copy";
  }

  onDrop(event) {
    event.preventDefault();
    stopDragging(findDOMNode(this));

    let files = event.dataTransfer.files;
    if (!files) {
      return;
    }

    // Import only the first dragged file for now
    // See also:
    // https://bugzilla.mozilla.org/show_bug.cgi?id=1438792
    if (files.length) {
      let file = files[0];
      readFile(file).then(har => {
        if (har) {
          this.appendPreview(har);
        }
      });
    }
  }

  appendPreview(har) {
    let {
      openSplitConsole
    } = this.props;

    try {
      let importer = new HarImporter(this.props.actions);
      importer.import(har);
    } catch (err) {
      if (openSplitConsole) {
        openSplitConsole("Error while processing HAR file: " + err.message);
      }
    }
  }

  // Rendering

  render() {
    return (
      div({
        onDragEnter: this.onDragEnter,
        onDragOver: this.onDragOver,
        onDragExit: this.onDragExit,
        onDrop: this.onDrop},
        this.props.children,
        div({className: "dropHarFiles"},
          div({}, DROP_HAR_FILES)
        )
      )
    );
  }
}

// Helpers

function readFile(file) {
  return new Promise(resolve => {
    let fileReader = new FileReader();
    fileReader.onloadend = () => {
      resolve(fileReader.result);
    };
    fileReader.readAsText(file);
  });
}

function startDragging(node) {
  node.setAttribute("dragging", "true");
}

function stopDragging(node) {
  node.removeAttribute("dragging");
}

// Exports

module.exports = DropHarHandler;
PK
!<&��%�%Qchrome/devtools/modules/devtools/client/netmonitor/src/components/HeadersPanel.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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 {
  getFormattedIPAndPort,
  getFormattedSize,
} = require("../utils/format-utils");
const { L10N } = require("../utils/l10n");
const {
  getHeadersURL,
  getHTTPStatusCodeURL,
} = require("../utils/mdn-utils");
const {
  fetchNetworkUpdatePacket,
  writeHeaderText,
} = require("../utils/request-utils");
const {
  HeadersProvider,
  HeaderList,
} = require("../utils/headers-provider");

// Components
const PropertiesView = createFactory(require("./PropertiesView"));

loader.lazyGetter(this, "MDNLink", function () {
  return createFactory(require("./MdnLink"));
});

loader.lazyGetter(this, "Rep", function () {
  return require("devtools/client/shared/components/reps/reps").REPS.Rep;
});
loader.lazyGetter(this, "MODE", function () {
  return require("devtools/client/shared/components/reps/reps").MODE;
});

const { button, div, input, textarea, span } = dom;

const EDIT_AND_RESEND = L10N.getStr("netmonitor.summary.editAndResend");
const RAW_HEADERS = L10N.getStr("netmonitor.summary.rawHeaders");
const RAW_HEADERS_REQUEST = L10N.getStr("netmonitor.summary.rawHeaders.requestHeaders");
const RAW_HEADERS_RESPONSE = L10N.getStr("netmonitor.summary.rawHeaders.responseHeaders");
const HEADERS_EMPTY_TEXT = L10N.getStr("headersEmptyText");
const HEADERS_FILTER_TEXT = L10N.getStr("headersFilterText");
const REQUEST_HEADERS = L10N.getStr("requestHeaders");
const REQUEST_HEADERS_FROM_UPLOAD = L10N.getStr("requestHeadersFromUpload");
const RESPONSE_HEADERS = L10N.getStr("responseHeaders");
const SUMMARY_ADDRESS = L10N.getStr("netmonitor.summary.address");
const SUMMARY_METHOD = L10N.getStr("netmonitor.summary.method");
const SUMMARY_URL = L10N.getStr("netmonitor.summary.url");
const SUMMARY_STATUS = L10N.getStr("netmonitor.summary.status");
const SUMMARY_VERSION = L10N.getStr("netmonitor.summary.version");

/**
 * Headers panel component
 * Lists basic information about the request
 */
class HeadersPanel extends Component {
  static get propTypes() {
    return {
      connector: PropTypes.object.isRequired,
      cloneSelectedRequest: PropTypes.func.isRequired,
      request: PropTypes.object.isRequired,
      renderValue: PropTypes.func,
      openLink: PropTypes.func,
    };
  }

  constructor(props) {
    super(props);

    this.state = {
      rawHeadersOpened: false,
    };

    this.getProperties = this.getProperties.bind(this);
    this.toggleRawHeaders = this.toggleRawHeaders.bind(this);
    this.renderSummary = this.renderSummary.bind(this);
    this.renderValue = this.renderValue.bind(this);
  }

  componentDidMount() {
    let { request, connector } = this.props;
    fetchNetworkUpdatePacket(connector.requestData, request, [
      "requestHeaders",
      "responseHeaders",
      "requestPostData",
    ]);
  }

  componentWillReceiveProps(nextProps) {
    let { request, connector } = nextProps;
    fetchNetworkUpdatePacket(connector.requestData, request, [
      "requestHeaders",
      "responseHeaders",
      "requestPostData",
    ]);
  }

  getProperties(headers, title) {
    if (headers && headers.headers.length) {
      let headerKey = `${title} (${getFormattedSize(headers.headersSize, 3)})`;
      let propertiesResult = {
        [headerKey]: new HeaderList(headers.headers)
      };
      return propertiesResult;
    }

    return null;
  }

  toggleRawHeaders() {
    this.setState({
      rawHeadersOpened: !this.state.rawHeadersOpened,
    });
  }

  renderSummary(label, value) {
    return (
      div({ className: "tabpanel-summary-container headers-summary" },
        div({
          className: "tabpanel-summary-label headers-summary-label",
        }, label),
        input({
          className: "tabpanel-summary-value textbox-input devtools-monospace",
          readOnly: true,
          value,
        }),
      )
    );
  }

  renderValue(props) {
    const member = props.member;
    const value = props.value;

    if (typeof value !== "string") {
      return null;
    }

    let headerDocURL = getHeadersURL(member.name);

    return (
      div({ className: "treeValueCellDivider" },
        Rep(Object.assign(props, {
          // FIXME: A workaround for the issue in StringRep
          // Force StringRep to crop the text everytime
          member: Object.assign({}, member, { open: false }),
          mode: MODE.TINY,
          cropLimit: 60,
        })),
        headerDocURL ? MDNLink({
          url: headerDocURL,
        }) : null
      )
    );
  }

  render() {
    const {
      openLink,
      cloneSelectedRequest,
      request: {
        fromCache,
        fromServiceWorker,
        httpVersion,
        method,
        remoteAddress,
        remotePort,
        requestHeaders,
        requestHeadersFromUploadStream: uploadHeaders,
        responseHeaders,
        status,
        statusText,
        urlDetails,
      },
    } = this.props;

    if ((!requestHeaders || !requestHeaders.headers.length) &&
        (!uploadHeaders || !uploadHeaders.headers.length) &&
        (!responseHeaders || !responseHeaders.headers.length)) {
      return div({ className: "empty-notice" },
        HEADERS_EMPTY_TEXT
      );
    }

    let object = Object.assign({},
      this.getProperties(responseHeaders, RESPONSE_HEADERS),
      this.getProperties(requestHeaders, REQUEST_HEADERS),
      this.getProperties(uploadHeaders, REQUEST_HEADERS_FROM_UPLOAD),
    );

    // not showing #hash in url
    let summaryUrl = urlDetails.unicodeUrl ?
      this.renderSummary(SUMMARY_URL, urlDetails.unicodeUrl.split("#")[0]) : null;

    let summaryMethod = method ?
      this.renderSummary(SUMMARY_METHOD, method) : null;

    let summaryAddress = remoteAddress ?
      this.renderSummary(SUMMARY_ADDRESS,
        getFormattedIPAndPort(remoteAddress, remotePort)) : null;

    let summaryStatus;

    if (status) {
      let code;
      if (fromCache) {
        code = "cached";
      } else if (fromServiceWorker) {
        code = "service worker";
      } else {
        code = status;
      }

      let statusCodeDocURL = getHTTPStatusCodeURL(status.toString());
      let inputWidth = status.toString().length + statusText.length + 1;
      let toggleRawHeadersClassList = ["devtools-button", "raw-headers-button"];
      if (this.state.rawHeadersOpened) {
        toggleRawHeadersClassList.push("checked");
      }

      summaryStatus = (
        div({ className: "tabpanel-summary-container headers-summary" },
          div({
            className: "tabpanel-summary-label headers-summary-label",
          }, SUMMARY_STATUS),
          div({
            className: "requests-list-status-icon",
            "data-code": code,
          }),
          input({
            className: "tabpanel-summary-value textbox-input devtools-monospace"
              + " status-text",
            readOnly: true,
            value: `${status} ${statusText}`,
            size: `${inputWidth}`,
          }),
          statusCodeDocURL ? MDNLink({
            url: statusCodeDocURL,
          }) : span({
            className: "headers-summary learn-more-link",
          }),
          button({
            className: "devtools-button edit-and-resend-button",
            onClick: cloneSelectedRequest,
          }, EDIT_AND_RESEND),
          button({
            "aria-pressed": this.state.rawHeadersOpened,
            className: toggleRawHeadersClassList.join(" "),
            onClick: this.toggleRawHeaders,
          }, RAW_HEADERS),
        )
      );
    }

    let summaryVersion = httpVersion ?
      this.renderSummary(SUMMARY_VERSION, httpVersion) : null;
    // display Status-Line above other response headers
    let statusLine = `${httpVersion} ${status} ${statusText}\n`;

    let summaryRawHeaders;
    if (this.state.rawHeadersOpened) {
      summaryRawHeaders = (
        div({ className: "tabpanel-summary-container headers-summary" },
          div({ className: "raw-headers-container" },
            div({ className: "raw-headers" },
              div({ className: "tabpanel-summary-label" }, RAW_HEADERS_REQUEST),
              textarea({
                className: "raw-request-headers-textarea",
                value: writeHeaderText(requestHeaders.headers),
                readOnly: true,
              }),
            ),
            div({ className: "raw-headers" },
              div({ className: "tabpanel-summary-label" }, RAW_HEADERS_RESPONSE),
              textarea({
                className: "raw-response-headers-textarea",
                value: statusLine + writeHeaderText(responseHeaders.headers),
                readOnly: true,
              }),
            ),
          )
        )
      );
    }

    return (
      div({ className: "panel-container" },
        div({ className: "headers-overview" },
          summaryUrl,
          summaryMethod,
          summaryAddress,
          summaryStatus,
          summaryVersion,
          summaryRawHeaders,
        ),
        PropertiesView({
          object,
          provider: HeadersProvider,
          filterPlaceHolder: HEADERS_FILTER_TEXT,
          sectionNames: Object.keys(object),
          renderValue: this.renderValue,
          openLink,
        }),
      )
    );
  }
}

module.exports = HeadersPanel;
PK
!<�ԗ��Pchrome/devtools/modules/devtools/client/netmonitor/src/components/HtmlPreview.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 dom = require("devtools/client/shared/vendor/react-dom-factories");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const { div, iframe } = dom;

/*
 * Response preview component
 * Display HTML content within a sandbox enabled iframe
 */
function HTMLPreview({ responseContent }) {
  const htmlBody = responseContent ? responseContent.content.text : "";

  return (
    div({ className: "html-preview" },
      iframe({
        sandbox: "",
        srcDoc: typeof htmlBody === "string" ? htmlBody : "",
      })
    )
  );
}

HTMLPreview.displayName = "HTMLPreview";

HTMLPreview.propTypes = {
  requestContent: PropTypes.object.isRequired,
};

module.exports = HTMLPreview;
PK
!<h�2)++Lchrome/devtools/modules/devtools/client/netmonitor/src/components/MdnLink.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Services = require("Services");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const { gDevTools } = require("devtools/client/framework/devtools");
const { L10N } = require("../utils/l10n");

const { a } = dom;

const LEARN_MORE = L10N.getStr("netmonitor.headers.learnMore");

function MDNLink({ url }) {
  return (
    a({
      className: "devtools-button learn-more-link",
      title: LEARN_MORE,
      onClick: (e) => onLearnMoreClick(e, url),
    })
  );
}

MDNLink.displayName = "MDNLink";

MDNLink.propTypes = {
  url: PropTypes.string.isRequired,
};

function onLearnMoreClick(e, url) {
  e.stopPropagation();
  e.preventDefault();

  let win = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
  let { button, ctrlKey, metaKey } = e;
  let isOSX = Services.appinfo.OS == "Darwin";
  if (button === 1 || (button === 0 && (isOSX ? metaKey : ctrlKey))) {
    win.openUILinkIn(url, "tabshifted");
  } else {
    win.openUILinkIn(url, "tab");
  }
}

module.exports = MDNLink;
PK
!<��o���Qchrome/devtools/modules/devtools/client/netmonitor/src/components/MonitorPanel.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Services = require("Services");
const { Component, createFactory } = require("devtools/client/shared/vendor/react");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const { div } = dom;
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const { connect } = require("devtools/client/shared/redux/visibility-handler-connect");
const { findDOMNode } = require("devtools/client/shared/vendor/react-dom");
const Actions = require("../actions/index");
const { updateFormDataSections } = require("../utils/request-utils");
const {
  getSelectedRequest,
  isSelectedRequestVisible,
} = require("../selectors/index");

// Components
const SplitBox = createFactory(require("devtools/client/shared/components/splitter/SplitBox"));
const RequestList = createFactory(require("./RequestList"));
const Toolbar = createFactory(require("./Toolbar"));

loader.lazyGetter(this, "NetworkDetailsPanel", function () {
  return createFactory(require("./NetworkDetailsPanel"));
});

const MediaQueryList = window.matchMedia("(min-width: 700px)");

/**
 * Monitor panel component
 * The main panel for displaying various network request information
 */
class MonitorPanel extends Component {
  static get propTypes() {
    return {
      connector: PropTypes.object.isRequired,
      isEmpty: PropTypes.bool.isRequired,
      networkDetailsOpen: PropTypes.bool.isRequired,
      openNetworkDetails: PropTypes.func.isRequired,
      onNetworkDetailsResized: PropTypes.func.isRequired,
      request: PropTypes.object,
      selectedRequestVisible: PropTypes.bool.isRequired,
      sourceMapService: PropTypes.object,
      openLink: PropTypes.func,
      updateRequest: PropTypes.func.isRequired,
    };
  }

  constructor(props) {
    super(props);

    this.state = {
      isVerticalSpliter: MediaQueryList.matches,
    };

    this.onLayoutChange = this.onLayoutChange.bind(this);
    this.onNetworkDetailsResized = this.onNetworkDetailsResized.bind(this);
  }

  componentDidMount() {
    MediaQueryList.addListener(this.onLayoutChange);
  }

  componentWillReceiveProps(nextProps) {
    updateFormDataSections(nextProps);
  }

  componentDidUpdate() {
    let { selectedRequestVisible, openNetworkDetails } = this.props;
    if (!selectedRequestVisible) {
      openNetworkDetails(false);
    }
  }

  componentWillUnmount() {
    MediaQueryList.removeListener(this.onLayoutChange);

    let { clientWidth, clientHeight } = findDOMNode(this.refs.endPanel) || {};

    if (this.state.isVerticalSpliter && clientWidth) {
      Services.prefs.setIntPref(
        "devtools.netmonitor.panes-network-details-width", clientWidth);
    }
    if (!this.state.isVerticalSpliter && clientHeight) {
      Services.prefs.setIntPref(
        "devtools.netmonitor.panes-network-details-height", clientHeight);
    }
  }

  onLayoutChange() {
    this.setState({
      isVerticalSpliter: MediaQueryList.matches,
    });
  }

  onNetworkDetailsResized(width, height) {
   // Cleaning width and height parameters, because SplitBox passes ALWAYS two values,
   // while depending on orientation ONLY ONE dimension is managed by it at a time.
    let { isVerticalSpliter }  = this.state;
    return this.props.onNetworkDetailsResized(
      isVerticalSpliter ? width : null,
      isVerticalSpliter ? null : height
    );
  }

  render() {
    let {
      connector,
      isEmpty,
      networkDetailsOpen,
      openLink,
      sourceMapService,
    } = this.props;

    let initialWidth = Services.prefs.getIntPref(
        "devtools.netmonitor.panes-network-details-width");
    let initialHeight = Services.prefs.getIntPref(
        "devtools.netmonitor.panes-network-details-height");

    return (
      div({ className: "monitor-panel" },
        Toolbar({ connector }),
        SplitBox({
          className: "devtools-responsive-container",
          initialWidth: initialWidth,
          initialHeight: initialHeight,
          minSize: "50px",
          maxSize: "80%",
          splitterSize: 1,
          startPanel: RequestList({ isEmpty, connector }),
          endPanel: networkDetailsOpen && NetworkDetailsPanel({
            ref: "endPanel",
            connector,
            openLink,
            sourceMapService,
          }),
          endPanelCollapsed: !networkDetailsOpen,
          endPanelControl: true,
          vert: this.state.isVerticalSpliter,
          onControlledPanelResized: this.onNetworkDetailsResized,
        }),
      )
    );
  }
}

module.exports = connect(
  (state) => ({
    isEmpty: state.requests.requests.size == 0,
    networkDetailsOpen: state.ui.networkDetailsOpen,
    request: getSelectedRequest(state),
    selectedRequestVisible: isSelectedRequestVisible(state),
  }),
  (dispatch) => ({
    openNetworkDetails: (open) => dispatch(Actions.openNetworkDetails(open)),
    onNetworkDetailsResized: (width, height) => dispatch(
      Actions.resizeNetworkDetails(width, height)
    ),
    updateRequest: (id, data, batch) => dispatch(Actions.updateRequest(id, data, batch)),
  }),
)(MonitorPanel);
PK
!<CU
���Xchrome/devtools/modules/devtools/client/netmonitor/src/components/NetworkDetailsPanel.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 } = 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 { connect } = require("devtools/client/shared/redux/visibility-handler-connect");
const Actions = require("../actions/index");
const { getSelectedRequest } = require("../selectors/index");

// Components
loader.lazyGetter(this, "CustomRequestPanel", function () {
  return createFactory(require("./CustomRequestPanel"));
});
loader.lazyGetter(this, "TabboxPanel", function () {
  return createFactory(require("./TabboxPanel"));
});

const { div } = dom;

/**
 * Network details panel component
 */
function NetworkDetailsPanel({
  connector,
  activeTabId,
  cloneSelectedRequest,
  request,
  selectTab,
  sourceMapService,
  openLink,
}) {
  if (!request) {
    return null;
  }

  return (
    div({ className: "network-details-panel" },
      !request.isCustom ?
        TabboxPanel({
          activeTabId,
          cloneSelectedRequest,
          connector,
          openLink,
          request,
          selectTab,
          sourceMapService,
        }) :
        CustomRequestPanel({
          connector,
          request,
        })
    )
  );
}

NetworkDetailsPanel.displayName = "NetworkDetailsPanel";

NetworkDetailsPanel.propTypes = {
  connector: PropTypes.object.isRequired,
  activeTabId: PropTypes.string,
  cloneSelectedRequest: PropTypes.func.isRequired,
  open: PropTypes.bool,
  request: PropTypes.object,
  selectTab: PropTypes.func.isRequired,
  sourceMapService: PropTypes.object,
  openLink: PropTypes.func,
};

module.exports = connect(
  (state) => ({
    activeTabId: state.ui.detailsPanelSelectedTab,
    request: getSelectedRequest(state),
  }),
  (dispatch) => ({
    cloneSelectedRequest: () => dispatch(Actions.cloneSelectedRequest()),
    selectTab: (tabId) => dispatch(Actions.selectDetailsPanelTab(tabId)),
  }),
)(NetworkDetailsPanel);
PK
!<i���Pchrome/devtools/modules/devtools/client/netmonitor/src/components/ParamsPanel.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const { connect } = require("devtools/client/shared/redux/visibility-handler-connect");
const { L10N } = require("../utils/l10n");
const {
  fetchNetworkUpdatePacket,
  getUrlQuery,
  parseQueryString,
  parseFormData,
} = require("../utils/request-utils");
const { sortObjectKeys } = require("../utils/sort-utils");
const { updateFormDataSections } = require("../utils/request-utils");
const Actions = require("../actions/index");

// Components
const PropertiesView = createFactory(require("./PropertiesView"));

const { div } = dom;

const JSON_SCOPE_NAME = L10N.getStr("jsonScopeName");
const PARAMS_EMPTY_TEXT = L10N.getStr("paramsEmptyText");
const PARAMS_FILTER_TEXT = L10N.getStr("paramsFilterText");
const PARAMS_FORM_DATA = L10N.getStr("paramsFormData");
const PARAMS_POST_PAYLOAD = L10N.getStr("paramsPostPayload");
const PARAMS_QUERY_STRING = L10N.getStr("paramsQueryString");
const SECTION_NAMES = [
  JSON_SCOPE_NAME,
  PARAMS_FORM_DATA,
  PARAMS_POST_PAYLOAD,
  PARAMS_QUERY_STRING,
];

/**
 * Params panel component
 * Displays the GET parameters and POST data of a request
 */
class ParamsPanel extends Component {
  static get propTypes() {
    return {
      connector: PropTypes.object.isRequired,
      openLink: PropTypes.func,
      request: PropTypes.object.isRequired,
      updateRequest: PropTypes.func.isRequired,
    };
  }

  constructor(props) {
    super(props);
  }

  componentDidMount() {
    let { request, connector } = this.props;
    fetchNetworkUpdatePacket(connector.requestData, request, ["requestPostData"]);
    updateFormDataSections(this.props);
  }

  componentWillReceiveProps(nextProps) {
    let { request, connector } = nextProps;
    fetchNetworkUpdatePacket(connector.requestData, request, ["requestPostData"]);
    updateFormDataSections(nextProps);
  }

  /**
   * Mapping array to dict for TreeView usage.
   * Since TreeView only support Object(dict) format.
   * This function also deal with duplicate key case
   * (for multiple selection and query params with same keys)
   *
   * @param {Object[]} arr - key-value pair array like query or form params
   * @returns {Object} Rep compatible object
   */
  getProperties(arr) {
    return sortObjectKeys(arr.reduce((map, obj) => {
      let value = map[obj.name];
      if (value) {
        if (typeof value !== "object") {
          map[obj.name] = [value];
        }
        map[obj.name].push(obj.value);
      } else {
        map[obj.name] = obj.value;
      }
      return map;
    }, {}));
  }

  render() {
    let {
      openLink,
      request
    } = this.props;
    let {
      formDataSections,
      mimeType,
      requestPostData,
      url,
    } = request;
    let postData = requestPostData ? requestPostData.postData.text : null;
    let query = getUrlQuery(url);

    if ((!formDataSections || formDataSections.length === 0) && !postData && !query) {
      return div({ className: "empty-notice" },
        PARAMS_EMPTY_TEXT
      );
    }

    let object = {};
    let json;

    // Query String section
    if (query) {
      object[PARAMS_QUERY_STRING] = this.getProperties(parseQueryString(query));
    }

    // Form Data section
    if (formDataSections && formDataSections.length > 0) {
      let sections = formDataSections.filter((str) => /\S/.test(str)).join("&");
      object[PARAMS_FORM_DATA] = this.getProperties(parseFormData(sections));
    }

    // Request payload section
    if (formDataSections && formDataSections.length === 0 && postData) {
      try {
        json = JSON.parse(postData);
      } catch (error) {
        // Continue regardless of parsing error
      }

      if (json) {
        object[JSON_SCOPE_NAME] = sortObjectKeys(json);
      } else {
        object[PARAMS_POST_PAYLOAD] = {
          EDITOR_CONFIG: {
            text: postData,
            mode: mimeType.replace(/;.+/, ""),
          },
        };
      }
    } else {
      postData = "";
    }

    return (
      div({ className: "panel-container" },
        PropertiesView({
          object,
          filterPlaceHolder: PARAMS_FILTER_TEXT,
          sectionNames: SECTION_NAMES,
          openLink,
        })
      )
    );
  }
}

module.exports = connect(null,
  (dispatch) => ({
    updateRequest: (id, data, batch) => dispatch(Actions.updateRequest(id, data, batch)),
  }),
)(ParamsPanel);
PK
!<�7k�22Schrome/devtools/modules/devtools/client/netmonitor/src/components/PropertiesView.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 react/prop-types */

"use strict";

const { Component, createFactory } = 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");

const { FILTER_SEARCH_DELAY } = require("../constants");

// Components
const TreeViewClass = require("devtools/client/shared/components/tree/TreeView");
const TreeView = createFactory(TreeViewClass);

loader.lazyGetter(this, "SearchBox", function () {
  return createFactory(require("devtools/client/shared/components/SearchBox"));
});
loader.lazyGetter(this, "TreeRow", function () {
  return createFactory(require("devtools/client/shared/components/tree/TreeRow"));
});
loader.lazyGetter(this, "SourceEditor", function () {
  return createFactory(require("./SourceEditor"));
});
loader.lazyGetter(this, "HTMLPreview", function () {
  return createFactory(require("./HtmlPreview"));
});

loader.lazyGetter(this, "Rep", function () {
  return require("devtools/client/shared/components/reps/reps").REPS.Rep;
});
loader.lazyGetter(this, "MODE", function () {
  return require("devtools/client/shared/components/reps/reps").MODE;
});

const { div, tr, td } = dom;
const AUTO_EXPAND_MAX_LEVEL = 7;
const AUTO_EXPAND_MAX_NODES = 50;
const EDITOR_CONFIG_ID = "EDITOR_CONFIG";
const HTML_PREVIEW_ID = "HTML_PREVIEW";

/**
 * Properties View component
 * A scrollable tree view component which provides some useful features for
 * representing object properties.
 *
 * Search filter - Set enableFilter to enable / disable SearchBox feature.
 * Tree view - Default enabled.
 * Source editor - Enable by specifying object level 1 property name to EDITOR_CONFIG_ID.
 * HTML preview - Enable by specifying object level 1 property name to HTML_PREVIEW_ID.
 * Rep - Default enabled.
 */
class PropertiesView extends Component {
  static get propTypes() {
    return {
      object: PropTypes.object,
      provider: PropTypes.object,
      enableInput: PropTypes.bool,
      expandableStrings: PropTypes.bool,
      filterPlaceHolder: PropTypes.string,
      sectionNames: PropTypes.array,
      openLink: PropTypes.func,
      cropLimit: PropTypes.number
    };
  }

  static get defaultProps() {
    return {
      enableInput: true,
      enableFilter: true,
      expandableStrings: false,
      filterPlaceHolder: "",
      sectionNames: [],
      cropLimit: 1024
    };
  }

  constructor(props) {
    super(props);

    this.state = {
      filterText: "",
    };

    this.getRowClass = this.getRowClass.bind(this);
    this.onFilter = this.onFilter.bind(this);
    this.renderRowWithExtras = this.renderRowWithExtras.bind(this);
    this.renderValueWithRep = this.renderValueWithRep.bind(this);
    this.shouldRenderSearchBox = this.shouldRenderSearchBox.bind(this);
    this.updateFilterText = this.updateFilterText.bind(this);
  }

  getRowClass(object, sectionNames) {
    return sectionNames.includes(object.name) ? "tree-section" : "";
  }

  onFilter(object, whiteList) {
    let { name, value } = object;
    let filterText = this.state.filterText;

    if (!filterText || whiteList.includes(name)) {
      return true;
    }

    let jsonString = JSON.stringify({ [name]: value }).toLowerCase();
    return jsonString.includes(filterText.toLowerCase());
  }

  renderRowWithExtras(props) {
    const { level, name, value, path } = props.member;

    // Display source editor when specifying to EDITOR_CONFIG_ID along with config
    if (level === 1 && name === EDITOR_CONFIG_ID) {
      return (
        tr({ key: EDITOR_CONFIG_ID, className: "editor-row-container" },
          td({ colSpan: 2 },
            SourceEditor(value)
          )
        )
      );
    }

    // Similar to the source editor, display a preview when specifying HTML_PREVIEW_ID
    if (level === 1 && name === HTML_PREVIEW_ID) {
      return (
        tr({ key: HTML_PREVIEW_ID, className: "response-preview-container" },
          td({ colSpan: 2 },
            HTMLPreview(value)
          )
        )
      );
    }

    // Skip for editor config and HTML previews
    if ((path.includes(EDITOR_CONFIG_ID) || path.includes(HTML_PREVIEW_ID))
      && level >= 1) {
      return null;
    }

    return TreeRow(props);
  }

  renderValueWithRep(props) {
    const { member } = props;

    // Hide strings with following conditions
    // 1. this row is a togglable section and content is object ('cause it shouldn't hide
    //    when string or number)
    // 2. the `value` object has a `value` property, only happened in Cookies panel
    // Put 2 here to not dup this method
    if (member.level === 0 && member.type === "object" ||
      (typeof member.value === "object" && member.value && member.value.value)) {
      return null;
    }

    return Rep(Object.assign(props, {
      // FIXME: A workaround for the issue in StringRep
      // Force StringRep to crop the text every time
      member: Object.assign({}, member, { open: false }),
      mode: MODE.TINY,
      cropLimit: this.props.cropLimit,
    }));
  }

  sectionIsSearchable(object, section) {
    return !(object[section][EDITOR_CONFIG_ID] || object[section][HTML_PREVIEW_ID]);
  }

  shouldRenderSearchBox(object) {
    return this.props.enableFilter && object && Object.keys(object)
      .filter((section) => this.sectionIsSearchable(object, section)).length > 0;
  }

  updateFilterText(filterText) {
    this.setState({
      filterText,
    });
  }

  render() {
    const {
      decorator,
      enableInput,
      expandableStrings,
      filterPlaceHolder,
      object,
      renderRow,
      renderValue,
      sectionNames,
      openLink,
      provider,
    } = this.props;

    return (
      div({ className: "properties-view" },
        this.shouldRenderSearchBox(object) &&
          div({ className: "searchbox-section" },
            SearchBox({
              delay: FILTER_SEARCH_DELAY,
              type: "filter",
              onChange: this.updateFilterText,
              placeholder: filterPlaceHolder,
            }),
          ),
        div({ className: "tree-container" },
          TreeView({
            object,
            provider,
            columns: [{
              id: "value",
              width: "100%",
            }],
            decorator: decorator || {
              getRowClass: (rowObject) => this.getRowClass(rowObject, sectionNames),
            },
            enableInput,
            expandableStrings,
            useQuotes: false,
            expandedNodes: TreeViewClass.getExpandedNodes(
              object,
              {maxLevel: AUTO_EXPAND_MAX_LEVEL, maxNodes: AUTO_EXPAND_MAX_NODES}
            ),
            onFilter: (props) => this.onFilter(props, sectionNames),
            renderRow: renderRow || this.renderRowWithExtras,
            renderValue: renderValue || this.renderValueWithRep,
            openLink,
          }),
        ),
      )
    );
  }
}

module.exports = PropertiesView;
PK
!<�j
���Pchrome/devtools/modules/devtools/client/netmonitor/src/components/RequestList.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 } = require("devtools/client/shared/vendor/react");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const { div } = dom;
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");

// Components
const StatusBar = createFactory(require("./StatusBar"));

loader.lazyGetter(this, "RequestListContent", function () {
  return createFactory(require("./RequestListContent"));
});
loader.lazyGetter(this, "RequestListEmptyNotice", function () {
  return createFactory(require("./RequestListEmptyNotice"));
});

/**
 * Request panel component
 */
function RequestList({
  connector,
  isEmpty,
}) {
  return (
    div({ className: "request-list-container" },
      isEmpty ? RequestListEmptyNotice({ connector }) : RequestListContent({ connector }),
      StatusBar({ connector }),
    )
  );
}

RequestList.displayName = "RequestList";

RequestList.propTypes = {
  connector: PropTypes.object.isRequired,
  isEmpty: PropTypes.bool.isRequired,
};

module.exports = RequestList;
PK
!<8� ��[chrome/devtools/modules/devtools/client/netmonitor/src/components/RequestListColumnCause.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");

const { div } = dom;

class RequestListColumnCause extends Component {
  static get propTypes() {
    return {
      item: PropTypes.object.isRequired,
      onCauseBadgeMouseDown: PropTypes.func.isRequired,
    };
  }

  shouldComponentUpdate(nextProps) {
    return this.props.item.cause !== nextProps.item.cause;
  }

  render() {
    let {
      item: { cause },
      onCauseBadgeMouseDown,
    } = this.props;

    let causeType = "unknown";
    let causeHasStack = false;

    if (cause) {
      // Legacy server might send a numeric value. Display it as "unknown"
      causeType = typeof cause.type === "string" ? cause.type : "unknown";
      causeHasStack = cause.stacktrace && cause.stacktrace.length > 0;
    }

    return (
      div({ className: "requests-list-column requests-list-cause", title: causeType },
        causeHasStack && div({
          className: "requests-list-cause-stack",
          onMouseDown: onCauseBadgeMouseDown,
        }, "JS"),
        causeType
      )
    );
  }
}

module.exports = RequestListColumnCause;
PK
!<�
BEEachrome/devtools/modules/devtools/client/netmonitor/src/components/RequestListColumnContentSize.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const { getFormattedSize } = require("../utils/format-utils");

const { div } = dom;

class RequestListColumnContentSize extends Component {
  static get propTypes() {
    return {
      item: PropTypes.object.isRequired,
    };
  }

  shouldComponentUpdate(nextProps) {
    return this.props.item.contentSize !== nextProps.item.contentSize;
  }

  render() {
    let { contentSize } = this.props.item;
    let size = typeof contentSize === "number" ? getFormattedSize(contentSize) : null;
    return (
      div({ className: "requests-list-column requests-list-size", title: size }, size)
    );
  }
}

module.exports = RequestListColumnContentSize;
PK
!<7��Pii]chrome/devtools/modules/devtools/client/netmonitor/src/components/RequestListColumnCookies.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const { fetchNetworkUpdatePacket } = require("../utils/request-utils");

const { div } = dom;

class RequestListColumnCookies extends Component {
  static get propTypes() {
    return {
      connector: PropTypes.object.isRequired,
      item: PropTypes.object.isRequired,
    };
  }

  componentDidMount() {
    let { item, connector } = this.props;
    fetchNetworkUpdatePacket(connector.requestData, item, ["requestCookies"]);
  }

  componentWillReceiveProps(nextProps) {
    let { item, connector } = nextProps;
    fetchNetworkUpdatePacket(connector.requestData, item, ["requestCookies"]);
  }

  shouldComponentUpdate(nextProps) {
    let { requestCookies: currRequestCookies = { cookies: [] } } = this.props.item;
    let { requestCookies: nextRequestCookies = { cookies: [] } } = nextProps.item;
    currRequestCookies = currRequestCookies.cookies || currRequestCookies;
    nextRequestCookies = nextRequestCookies.cookies || nextRequestCookies;
    return currRequestCookies !== nextRequestCookies;
  }

  render() {
    let { requestCookies = { cookies: [] } } = this.props.item;
    requestCookies = requestCookies.cookies || requestCookies;
    let requestCookiesLength = requestCookies.length > 0 ? requestCookies.length : "";
    return (
      div({
        className: "requests-list-column requests-list-cookies",
        title: requestCookiesLength
      }, requestCookiesLength)
    );
  }
}

module.exports = RequestListColumnCookies;
PK
!<@���\chrome/devtools/modules/devtools/client/netmonitor/src/components/RequestListColumnDomain.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const { getFormattedIPAndPort } = require("../utils/format-utils");
const { L10N } = require("../utils/l10n");
const { propertiesEqual } = require("../utils/request-utils");

const { div } = dom;

const UPDATED_DOMAIN_PROPS = [
  "remoteAddress",
  "securityState",
  "urlDetails",
];

class RequestListColumnDomain extends Component {
  static get propTypes() {
    return {
      item: PropTypes.object.isRequired,
      onSecurityIconMouseDown: PropTypes.func.isRequired,
    };
  }

  shouldComponentUpdate(nextProps) {
    return !propertiesEqual(UPDATED_DOMAIN_PROPS, this.props.item, nextProps.item);
  }

  render() {
    let { item, onSecurityIconMouseDown } = this.props;
    let { remoteAddress, remotePort, securityState,
      urlDetails: { host, isLocal } } = item;
    let iconClassList = ["requests-security-state-icon"];
    let iconTitle;
    let title = host + (remoteAddress ?
      ` (${getFormattedIPAndPort(remoteAddress, remotePort)})` : "");

    if (isLocal) {
      iconClassList.push("security-state-local");
      iconTitle = L10N.getStr("netmonitor.security.state.secure");
    } else if (securityState) {
      iconClassList.push(`security-state-${securityState}`);
      iconTitle = L10N.getStr(`netmonitor.security.state.${securityState}`);
    }

    return (
      div({ className: "requests-list-column requests-list-domain", title },
        div({
          className: iconClassList.join(" "),
          onMouseDown: onSecurityIconMouseDown,
          title: iconTitle,
        }),
        host,
      )
    );
  }
}

module.exports = RequestListColumnDomain;
PK
!<���{{Zchrome/devtools/modules/devtools/client/netmonitor/src/components/RequestListColumnFile.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const { propertiesEqual } = require("../utils/request-utils");

const { div } = dom;

const UPDATED_FILE_PROPS = [
  "urlDetails",
];

class RequestListColumnFile extends Component {
  static get propTypes() {
    return {
      item: PropTypes.object.isRequired,
    };
  }

  shouldComponentUpdate(nextProps) {
    return !propertiesEqual(UPDATED_FILE_PROPS, this.props.item, nextProps.item);
  }

  render() {
    let {
      item: { urlDetails },
    } = this.props;

    return (
      div({
        className: "requests-list-column requests-list-file",
        title: urlDetails.unicodeUrl,
      },
        urlDetails.baseNameWithQuery
      )
    );
  }
}

module.exports = RequestListColumnFile;
PK
!<��M�\chrome/devtools/modules/devtools/client/netmonitor/src/components/RequestListColumnMethod.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");

const { div } = dom;

class RequestListColumnMethod extends Component {
  static get propTypes() {
    return {
      item: PropTypes.object.isRequired,
    };
  }

  shouldComponentUpdate(nextProps) {
    return this.props.item.method !== nextProps.item.method;
  }

  render() {
    let { method } = this.props.item;
    return div({ className: "requests-list-column requests-list-method" }, method);
  }
}

module.exports = RequestListColumnMethod;
PK
!<~�ԪII^chrome/devtools/modules/devtools/client/netmonitor/src/components/RequestListColumnProtocol.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const { getFormattedProtocol } = require("../utils/request-utils");

const { div } = dom;

class RequestListColumnProtocol extends Component {
  static get propTypes() {
    return {
      item: PropTypes.object.isRequired,
    };
  }

  shouldComponentUpdate(nextProps) {
    return getFormattedProtocol(this.props.item) !==
      getFormattedProtocol(nextProps.item);
  }

  render() {
    let protocol = getFormattedProtocol(this.props.item);

    return (
      div({
        className: "requests-list-column requests-list-protocol",
        title: protocol,
      },
        protocol
      )
    );
  }
}

module.exports = RequestListColumnProtocol;
PK
!<,�X��^chrome/devtools/modules/devtools/client/netmonitor/src/components/RequestListColumnRemoteIP.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const { getFormattedIPAndPort } = require("../utils/format-utils");

const { div } = dom;

class RequestListColumnRemoteIP extends Component {
  static get propTypes() {
    return {
      item: PropTypes.object.isRequired,
    };
  }

  shouldComponentUpdate(nextProps) {
    return this.props.item.remoteAddress !== nextProps.item.remoteAddress;
  }

  render() {
    let { remoteAddress, remotePort } = this.props.item;
    let remoteIP = remoteAddress ?
      getFormattedIPAndPort(remoteAddress, remotePort) : "unknown";

    return (
      div({ className: "requests-list-column requests-list-remoteip", title: remoteIP },
        remoteIP
      )
    );
  }
}

module.exports = RequestListColumnRemoteIP;
PK
!<
Y���dchrome/devtools/modules/devtools/client/netmonitor/src/components/RequestListColumnResponseHeader.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const {
  getResponseHeader,
  fetchNetworkUpdatePacket
} = require("../utils/request-utils");

const { div } = dom;

/**
 * Renders a response header column in the requests list.  The actual
 * header to show is passed as a prop.
 */
class RequestListColumnResponseHeader extends Component {
  static get propTypes() {
    return {
      connector: PropTypes.object.isRequired,
      item: PropTypes.object.isRequired,
      header: PropTypes.string.isRequired,
    };
  }

  componentDidMount() {
    let { item, connector } = this.props;
    fetchNetworkUpdatePacket(connector.requestData, item, ["responseHeaders"]);
  }

  componentWillReceiveProps(nextProps) {
    let { item, connector } = nextProps;
    fetchNetworkUpdatePacket(connector.requestData, item, ["responseHeaders"]);
  }

  shouldComponentUpdate(nextProps) {
    const currHeader = getResponseHeader(this.props.item, this.props.header);
    const nextHeader = getResponseHeader(nextProps.item, nextProps.header);
    return currHeader !== nextHeader;
  }

  render() {
    let header = getResponseHeader(this.props.item, this.props.header);
    return (
      div({
        className: "requests-list-column requests-list-response-header",
        title: header
      },
        header
      )
    );
  }
}

module.exports = RequestListColumnResponseHeader;
PK
!<yu�!��\chrome/devtools/modules/devtools/client/netmonitor/src/components/RequestListColumnScheme.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");

const { div } = dom;

class RequestListColumnScheme extends Component {
  static get propTypes() {
    return {
      item: PropTypes.object.isRequired,
    };
  }

  shouldComponentUpdate(nextProps) {
    return this.props.item.urlDetails.scheme !== nextProps.item.urlDetails.scheme;
  }

  render() {
    const { urlDetails } = this.props.item;
    return (
      div({
        className: "requests-list-column requests-list-scheme",
        title: urlDetails.scheme
      },
        urlDetails.scheme
      )
    );
  }
}

module.exports = RequestListColumnScheme;
PK
!<���_��`chrome/devtools/modules/devtools/client/netmonitor/src/components/RequestListColumnSetCookies.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const { fetchNetworkUpdatePacket } = require("../utils/request-utils");

const { div } = dom;

class RequestListColumnSetCookies extends Component {
  static get propTypes() {
    return {
      connector: PropTypes.object.isRequired,
      item: PropTypes.object.isRequired,
    };
  }

  componentDidMount() {
    let { item, connector } = this.props;
    fetchNetworkUpdatePacket(connector.requestData, item, ["responseCookies"]);
  }

  componentWillReceiveProps(nextProps) {
    let { item, connector } = nextProps;
    fetchNetworkUpdatePacket(connector.requestData, item, ["responseCookies"]);
  }

  shouldComponentUpdate(nextProps) {
    let { responseCookies: currResponseCookies = { cookies: [] } } = this.props.item;
    let { responseCookies: nextResponseCookies = { cookies: [] } } = nextProps.item;
    currResponseCookies = currResponseCookies.cookies || currResponseCookies;
    nextResponseCookies = nextResponseCookies.cookies || nextResponseCookies;
    return currResponseCookies !== nextResponseCookies;
  }

  render() {
    let { responseCookies = { cookies: [] } } = this.props.item;
    responseCookies = responseCookies.cookies || responseCookies;
    let responseCookiesLength = responseCookies.length > 0 ? responseCookies.length : "";
    return (
      div({
        className: "requests-list-column requests-list-set-cookies",
        title: responseCookiesLength
      }, responseCookiesLength)
    );
  }
}

module.exports = RequestListColumnSetCookies;
PK
!<rQ;G^
^
\chrome/devtools/modules/devtools/client/netmonitor/src/components/RequestListColumnStatus.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const { L10N } = require("../utils/l10n");
const { propertiesEqual } = require("../utils/request-utils");

const { div } = dom;

const UPDATED_STATUS_PROPS = [
  "fromCache",
  "fromServiceWorker",
  "status",
  "statusText",
];

class RequestListColumnStatus extends Component {
  static get propTypes() {
    return {
      item: PropTypes.object.isRequired,
    };
  }

  shouldComponentUpdate(nextProps) {
    return !propertiesEqual(UPDATED_STATUS_PROPS, this.props.item, nextProps.item);
  }

  render() {
    let { item } = this.props;
    let { fromCache, fromServiceWorker, status, statusText } = item;
    let code;

    if (status) {
      if (fromCache) {
        code = "cached";
      } else if (fromServiceWorker) {
        code = "service worker";
      } else {
        code = status;
      }
    }

    return (
      div({
        className: "requests-list-column requests-list-status",
        onMouseOver: function ({ target }) {
          if (status && statusText && !target.title) {
            target.title = getColumnTitle(item);
          }
        },
      },

      /*
        `data-code` refers to the status-code
        `data-status-code` can be one of "cached", "service worker"
        or the status-code itself
        For example - if a resource is cached, `data-code` would be 200
        and the `data-status-code` would be "cached"
      */
      div({
        className: "requests-list-status-code status-code",
        "data-status-code": code,
        "data-code": status,
      }, status)
    ));
  }
}

function getColumnTitle(item) {
  let { fromCache, fromServiceWorker, status, statusText } = item;
  let title;
  if (fromCache && fromServiceWorker) {
    title = L10N.getFormatStr("netmonitor.status.tooltip.cachedworker",
      status, statusText);
  } else if (fromCache) {
    title = L10N.getFormatStr("netmonitor.status.tooltip.cached",
      status, statusText);
  } else if (fromServiceWorker) {
    title = L10N.getFormatStr("netmonitor.status.tooltip.worker",
      status, statusText);
  } else {
    title = L10N.getFormatStr("netmonitor.status.tooltip.simple",
      status, statusText);
  }
  return title;
}

module.exports = RequestListColumnStatus;
PK
!<�\�	�	Zchrome/devtools/modules/devtools/client/netmonitor/src/components/RequestListColumnTime.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const { getFormattedTime } = require("../utils/format-utils");
const {
  fetchNetworkUpdatePacket,
  getResponseTime,
  getStartTime,
  getEndTime,
} = require("../utils/request-utils");

const { div } = dom;

/**
 * This component represents a column displaying selected
 * timing value. There are following possible values this
 * column can render:
 * - Start Time
 * - End Time
 * - Response Time
 * - Duration Time
 * - Latency Time
 */
class RequestListColumnTime extends Component {
  static get propTypes() {
    return {
      connector: PropTypes.object.isRequired,
      firstRequestStartedMillis: PropTypes.number.isRequired,
      item: PropTypes.object.isRequired,
      type: PropTypes.oneOf([
        "start",
        "end",
        "response",
        "duration",
        "latency",
      ]).isRequired,
    };
  }

  componentDidMount() {
    let { item, connector } = this.props;
    fetchNetworkUpdatePacket(connector.requestData, item, ["eventTimings"]);
  }

  componentWillReceiveProps(nextProps) {
    let { item, connector } = nextProps;
    fetchNetworkUpdatePacket(connector.requestData, item, ["eventTimings"]);
  }

  shouldComponentUpdate(nextProps) {
    return this.getTime(this.props) !== this.getTime(nextProps);
  }

  getTime(props) {
    let {
      firstRequestStartedMillis,
      item,
      type,
    } = props;

    switch (type) {
      case "start":
        return getStartTime(item, firstRequestStartedMillis);
      case "end":
        return getEndTime(item, firstRequestStartedMillis);
      case "response":
        return getResponseTime(item, firstRequestStartedMillis);
      case "duration":
        return item.totalTime;
      case "latency":
        return item.eventTimings ? item.eventTimings.timings.wait : undefined;
    }

    return 0;
  }

  render() {
    let { type } = this.props;
    let time = getFormattedTime(this.getTime(this.props));

    return (
      div({
        className: "requests-list-column requests-list-" + type + "-time",
        title: time,
      },
        time
      )
    );
  }
}

module.exports = RequestListColumnTime;
PK
!<Vi�-,,echrome/devtools/modules/devtools/client/netmonitor/src/components/RequestListColumnTransferredSize.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const { getFormattedSize } = require("../utils/format-utils");
const { L10N } = require("../utils/l10n");
const { propertiesEqual } = require("../utils/request-utils");

const { div } = dom;
const SIZE_CACHED = L10N.getStr("networkMenu.sizeCached");
const SIZE_SERVICE_WORKER = L10N.getStr("networkMenu.sizeServiceWorker");
const SIZE_UNAVAILABLE = L10N.getStr("networkMenu.sizeUnavailable");
const UPDATED_TRANSFERRED_PROPS = [
  "transferredSize",
  "fromCache",
  "fromServiceWorker",
];

class RequestListColumnTransferredSize extends Component {
  static get propTypes() {
    return {
      item: PropTypes.object.isRequired,
    };
  }

  shouldComponentUpdate(nextProps) {
    return !propertiesEqual(UPDATED_TRANSFERRED_PROPS, this.props.item, nextProps.item);
  }

  render() {
    let { fromCache, fromServiceWorker, status, transferredSize } = this.props.item;
    let text;

    if (fromCache || status === "304") {
      text = SIZE_CACHED;
    } else if (fromServiceWorker) {
      text = SIZE_SERVICE_WORKER;
    } else if (typeof transferredSize == "number") {
      text = getFormattedSize(transferredSize);
    } else if (transferredSize === null) {
      text = SIZE_UNAVAILABLE;
    }

    return (
      div({ className: "requests-list-column requests-list-transferred", title: text },
        text
      )
    );
  }
}

module.exports = RequestListColumnTransferredSize;
PK
!<�yssZchrome/devtools/modules/devtools/client/netmonitor/src/components/RequestListColumnType.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const { getAbbreviatedMimeType } = require("../utils/request-utils");

const { div } = dom;

class RequestListColumnType extends Component {
  static get propTypes() {
    return {
      item: PropTypes.object.isRequired,
    };
  }

  shouldComponentUpdate(nextProps) {
    return this.props.item.mimeType !== nextProps.item.mimeType;
  }

  render() {
    let { mimeType } = this.props.item;
    let abbrevType;

    if (mimeType) {
      abbrevType = getAbbreviatedMimeType(mimeType);
    }

    return (
      div({
        className: "requests-list-column requests-list-type",
        title: mimeType,
      },
        abbrevType
      )
    );
  }
}

module.exports = RequestListColumnType;
PK
!<�an���_chrome/devtools/modules/devtools/client/netmonitor/src/components/RequestListColumnWaterfall.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");

const { L10N } = require("../utils/l10n");
const {
  fetchNetworkUpdatePacket,
  propertiesEqual,
} = require("../utils/request-utils");

// List of properties of the timing info we want to create boxes for
const { TIMING_KEYS } = require("../constants");

const { div } = dom;

const UPDATED_WATERFALL_PROPS = [
  "eventTimings",
  "fromCache",
  "fromServiceWorker",
  "totalTime",
];

class RequestListColumnWaterfall extends Component {
  static get propTypes() {
    return {
      connector: PropTypes.object.isRequired,
      firstRequestStartedMillis: PropTypes.number.isRequired,
      item: PropTypes.object.isRequired,
      onWaterfallMouseDown: PropTypes.func.isRequired,
    };
  }

  componentDidMount() {
    let { connector, item } = this.props;
    fetchNetworkUpdatePacket(connector.requestData, item, ["eventTimings"]);
  }

  componentWillReceiveProps(nextProps) {
    let { connector, item } = nextProps;
    fetchNetworkUpdatePacket(connector.requestData, item, ["eventTimings"]);
  }

  shouldComponentUpdate(nextProps) {
    return !propertiesEqual(UPDATED_WATERFALL_PROPS, this.props.item, nextProps.item) ||
      this.props.firstRequestStartedMillis !== nextProps.firstRequestStartedMillis;
  }

  timingTooltip() {
    let { eventTimings, fromCache, fromServiceWorker, totalTime } = this.props.item;
    let tooltip = [];

    if (fromCache || fromServiceWorker) {
      return tooltip;
    }

    if (eventTimings) {
      for (let key of TIMING_KEYS) {
        let width = eventTimings.timings[key];

        if (width > 0) {
          tooltip.push(L10N.getFormatStr("netmonitor.waterfall.tooltip." + key, width));
        }
      }
    }

    if (typeof totalTime === "number") {
      tooltip.push(L10N.getFormatStr("netmonitor.waterfall.tooltip.total", totalTime));
    }

    return tooltip.join(L10N.getStr("netmonitor.waterfall.tooltip.separator"));
  }

  timingBoxes() {
    let { eventTimings, fromCache, fromServiceWorker, totalTime } = this.props.item;
    let boxes = [];

    if (fromCache || fromServiceWorker) {
      return boxes;
    }

    if (eventTimings) {
      // Add a set of boxes representing timing information.
      for (let key of TIMING_KEYS) {
        let width = eventTimings.timings[key];

        // Don't render anything if it surely won't be visible.
        // One millisecond == one unscaled pixel.
        if (width > 0) {
          boxes.push(
            div({
              key,
              className: `requests-list-timings-box ${key}`,
              style: { width },
            })
          );
        }
      }
    }

    if (typeof totalTime === "number") {
      let title = L10N.getFormatStr("networkMenu.totalMS", totalTime);
      boxes.push(
        div({
          key: "total",
          className: "requests-list-timings-total",
          title,
        }, title)
      );
    }

    return boxes;
  }

  render() {
    let {
      firstRequestStartedMillis,
      item,
      onWaterfallMouseDown,
    } = this.props;

    return (
      div({
        className: "requests-list-column requests-list-waterfall",
        onMouseOver: ({ target }) => {
          if (!target.title) {
            target.title = this.timingTooltip();
          }
        }
      },
        div({
          className: "requests-list-timings",
          style: {
            paddingInlineStart: `${item.startedMillis - firstRequestStartedMillis}px`,
          },
          onMouseDown: onWaterfallMouseDown,
        },
          this.timingBoxes(),
        )
      )
    );
  }
}

module.exports = RequestListColumnWaterfall;
PK
!<�����-�-Wchrome/devtools/modules/devtools/client/netmonitor/src/components/RequestListContent.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const { connect } = require("devtools/client/shared/redux/visibility-handler-connect");
const { HTMLTooltip } = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");

const Actions = require("../actions/index");
const { formDataURI } = require("../utils/request-utils");
const {
  getDisplayedRequests,
  getSelectedRequest,
  getSortedRequests,
  getWaterfallScale,
} = require("../selectors/index");

loader.lazyGetter(this, "setImageTooltip", function () {
  return require("devtools/client/shared/widgets/tooltip/ImageTooltipHelper")
    .setImageTooltip;
});
loader.lazyGetter(this, "getImageDimensions", function () {
  return require("devtools/client/shared/widgets/tooltip/ImageTooltipHelper")
    .getImageDimensions;
});

// Components
const RequestListHeader = createFactory(require("./RequestListHeader"));
const RequestListItem = createFactory(require("./RequestListItem"));
const RequestListContextMenu = require("../widgets/RequestListContextMenu");

const { div } = dom;

// Tooltip show / hide delay in ms
const REQUESTS_TOOLTIP_TOGGLE_DELAY = 500;
// Tooltip image maximum dimension in px
const REQUESTS_TOOLTIP_IMAGE_MAX_DIM = 400;
// Gecko's scrollTop is int32_t, so the maximum value is 2^31 - 1 = 2147483647
const MAX_SCROLL_HEIGHT = 2147483647;

/**
 * Renders the actual contents of the request list.
 */
class RequestListContent extends Component {
  static get propTypes() {
    return {
      connector: PropTypes.object.isRequired,
      columns: PropTypes.object.isRequired,
      networkDetailsOpen: PropTypes.bool.isRequired,
      networkDetailsWidth: PropTypes.number.isRequired,
      networkDetailsHeight: PropTypes.number.isRequired,
      cloneSelectedRequest: PropTypes.func.isRequired,
      displayedRequests: PropTypes.array.isRequired,
      firstRequestStartedMillis: PropTypes.number.isRequired,
      fromCache: PropTypes.bool,
      onCauseBadgeMouseDown: PropTypes.func.isRequired,
      onItemMouseDown: PropTypes.func.isRequired,
      onSecurityIconMouseDown: PropTypes.func.isRequired,
      onSelectDelta: PropTypes.func.isRequired,
      onWaterfallMouseDown: PropTypes.func.isRequired,
      openStatistics: PropTypes.func.isRequired,
      scale: PropTypes.number,
      selectedRequest: PropTypes.object,
      sortedRequests: PropTypes.array.isRequired,
      requestFilterTypes: PropTypes.object.isRequired,
    };
  }

  constructor(props) {
    super(props);
    this.isScrolledToBottom = this.isScrolledToBottom.bind(this);
    this.onHover = this.onHover.bind(this);
    this.onScroll = this.onScroll.bind(this);
    this.onResize = this.onResize.bind(this);
    this.onKeyDown = this.onKeyDown.bind(this);
    this.onContextMenu = this.onContextMenu.bind(this);
    this.onFocusedNodeChange = this.onFocusedNodeChange.bind(this);
  }

  componentWillMount() {
    const { connector, cloneSelectedRequest, openStatistics } = this.props;
    this.contextMenu = new RequestListContextMenu({
      connector,
      cloneSelectedRequest,
      openStatistics,
    });
    this.tooltip = new HTMLTooltip(window.parent.document, { type: "arrow" });
    window.addEventListener("resize", this.onResize);
  }

  componentDidMount() {
    // Install event handler for displaying a tooltip
    this.tooltip.startTogglingOnHover(this.refs.contentEl, this.onHover, {
      toggleDelay: REQUESTS_TOOLTIP_TOGGLE_DELAY,
      interactive: true
    });
    // Install event handler to hide the tooltip on scroll
    this.refs.contentEl.addEventListener("scroll", this.onScroll, true);
    this.onResize();
  }

  componentWillUpdate(nextProps) {
    // Check if the list is scrolled to bottom before the UI update.
    // The scroll is ever needed only if new rows are added to the list.
    const delta = nextProps.displayedRequests.size - this.props.displayedRequests.size;
    this.shouldScrollBottom = delta > 0 && this.isScrolledToBottom();
  }

  componentDidUpdate(prevProps) {
    let node = this.refs.contentEl;
    // Keep the list scrolled to bottom if a new row was added
    if (this.shouldScrollBottom && node.scrollTop !== MAX_SCROLL_HEIGHT) {
      // Using maximum scroll height rather than node.scrollHeight to avoid sync reflow.
      node.scrollTop = MAX_SCROLL_HEIGHT;
    }
    if (prevProps.networkDetailsOpen !== this.props.networkDetailsOpen ||
      prevProps.networkDetailsWidth !== this.props.networkDetailsWidth ||
      prevProps.networkDetailsHeight !== this.props.networkDetailsHeight
    ) {
      this.onResize();
    }
  }

  componentWillUnmount() {
    this.refs.contentEl.removeEventListener("scroll", this.onScroll, true);

    // Uninstall the tooltip event handler
    this.tooltip.stopTogglingOnHover();
    window.removeEventListener("resize", this.onResize);
  }

  onResize() {
    let parent = this.refs.contentEl.parentNode;
    this.refs.contentEl.style.width = parent.offsetWidth + "px";
    this.refs.contentEl.style.height = parent.offsetHeight + "px";
  }

  isScrolledToBottom() {
    const { contentEl } = this.refs;
    const lastChildEl = contentEl.lastElementChild;

    if (!lastChildEl) {
      return false;
    }

    let lastChildRect = lastChildEl.getBoundingClientRect();
    let contentRect = contentEl.getBoundingClientRect();

    return (lastChildRect.height + lastChildRect.top) <= contentRect.bottom;
  }

  /**
   * The predicate used when deciding whether a popup should be shown
   * over a request item or not.
   *
   * @param nsIDOMNode target
   *        The element node currently being hovered.
   * @param object tooltip
   *        The current tooltip instance.
   * @return {Promise}
   */
  async onHover(target, tooltip) {
    let itemEl = target.closest(".request-list-item");
    if (!itemEl) {
      return false;
    }
    let itemId = itemEl.dataset.id;
    if (!itemId) {
      return false;
    }
    let requestItem = this.props.displayedRequests.find(r => r.id == itemId);
    if (!requestItem) {
      return false;
    }

    if (!target.closest(".requests-list-file")) {
      return false;
    }

    let { mimeType } = requestItem;
    if (!mimeType || !mimeType.includes("image/")) {
      return false;
    }

    let responseContent = await this.props.connector
      .requestData(requestItem.id, "responseContent");
    let { encoding, text } = responseContent.content;
    let src = formDataURI(mimeType, encoding, text);
    let maxDim = REQUESTS_TOOLTIP_IMAGE_MAX_DIM;
    let { naturalWidth, naturalHeight } = await getImageDimensions(tooltip.doc, src);
    let options = { maxDim, naturalWidth, naturalHeight };
    setImageTooltip(tooltip, tooltip.doc, src, options);

    return itemEl.querySelector(".requests-list-file");
  }

  /**
   * Scroll listener for the requests menu view.
   */
  onScroll() {
    this.tooltip.hide();
  }

  /**
   * Handler for keyboard events. For arrow up/down, page up/down, home/end,
   * move the selection up or down.
   */
  onKeyDown(evt) {
    let delta;

    switch (evt.key) {
      case "ArrowUp":
      case "ArrowLeft":
        delta = -1;
        break;
      case "ArrowDown":
      case "ArrowRight":
        delta = +1;
        break;
      case "PageUp":
        delta = "PAGE_UP";
        break;
      case "PageDown":
        delta = "PAGE_DOWN";
        break;
      case "Home":
        delta = -Infinity;
        break;
      case "End":
        delta = +Infinity;
        break;
    }

    if (delta) {
      // Prevent scrolling when pressing navigation keys.
      evt.preventDefault();
      evt.stopPropagation();
      this.props.onSelectDelta(delta);
    }
  }

  onContextMenu(evt) {
    evt.preventDefault();
    let { selectedRequest, sortedRequests } = this.props;
    this.contextMenu.open(evt, selectedRequest, sortedRequests);
  }

  /**
   * If selection has just changed (by keyboard navigation), don't keep the list
   * scrolled to bottom, but allow scrolling up with the selection.
   */
  onFocusedNodeChange() {
    this.shouldScrollBottom = false;
  }

  render() {
    const {
      connector,
      columns,
      displayedRequests,
      firstRequestStartedMillis,
      onCauseBadgeMouseDown,
      onItemMouseDown,
      onSecurityIconMouseDown,
      onWaterfallMouseDown,
      requestFilterTypes,
      scale,
      selectedRequest,
    } = this.props;

    return (
      div({ className: "requests-list-wrapper" },
        div({ className: "requests-list-table" },
          div({
            ref: "contentEl",
            className: "requests-list-contents",
            tabIndex: 0,
            onKeyDown: this.onKeyDown,
            style: { "--timings-scale": scale, "--timings-rev-scale": 1 / scale }
          },
            RequestListHeader(),
            displayedRequests.map((item, index) => RequestListItem({
              firstRequestStartedMillis,
              fromCache: item.status === "304" || item.fromCache,
              connector,
              columns,
              item,
              index,
              isSelected: item.id === (selectedRequest && selectedRequest.id),
              key: item.id,
              onContextMenu: this.onContextMenu,
              onFocusedNodeChange: this.onFocusedNodeChange,
              onMouseDown: () => onItemMouseDown(item.id),
              onCauseBadgeMouseDown: () => onCauseBadgeMouseDown(item.cause),
              onSecurityIconMouseDown: () => onSecurityIconMouseDown(item.securityState),
              onWaterfallMouseDown: () => onWaterfallMouseDown(),
              requestFilterTypes,
            }))
          )
        )
      )
    );
  }
}

module.exports = connect(
  (state) => ({
    columns: state.ui.columns,
    networkDetailsOpen: state.ui.networkDetailsOpen,
    networkDetailsWidth: state.ui.networkDetailsWidth,
    networkDetailsHeight: state.ui.networkDetailsHeight,
    displayedRequests: getDisplayedRequests(state),
    firstRequestStartedMillis: state.requests.firstStartedMillis,
    selectedRequest: getSelectedRequest(state),
    scale: getWaterfallScale(state),
    sortedRequests: getSortedRequests(state),
    requestFilterTypes: state.filters.requestFilterTypes,
  }),
  (dispatch, props) => ({
    cloneSelectedRequest: () => dispatch(Actions.cloneSelectedRequest()),
    openStatistics: (open) => dispatch(Actions.openStatistics(props.connector, open)),
    /**
     * A handler that opens the stack trace tab when a stack trace is available
     */
    onCauseBadgeMouseDown: (cause) => {
      if (cause.stacktrace && cause.stacktrace.length > 0) {
        dispatch(Actions.selectDetailsPanelTab("stack-trace"));
      }
    },
    onItemMouseDown: (id) => dispatch(Actions.selectRequest(id)),
    /**
     * A handler that opens the security tab in the details view if secure or
     * broken security indicator is clicked.
     */
    onSecurityIconMouseDown: (securityState) => {
      if (securityState && securityState !== "insecure") {
        dispatch(Actions.selectDetailsPanelTab("security"));
      }
    },
    onSelectDelta: (delta) => dispatch(Actions.selectDelta(delta)),
    /**
     * A handler that opens the timing sidebar panel if the waterfall is clicked.
     */
    onWaterfallMouseDown: () => {
      dispatch(Actions.selectDetailsPanelTab("timings"));
    },
  }),
)(RequestListContent);
PK
!<LY{}��[chrome/devtools/modules/devtools/client/netmonitor/src/components/RequestListEmptyNotice.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const { connect } = require("devtools/client/shared/redux/visibility-handler-connect");
const Actions = require("../actions/index");
const { ACTIVITY_TYPE } = require("../constants");
const { L10N } = require("../utils/l10n");
const { getPerformanceAnalysisURL } = require("../utils/mdn-utils");

// Components
const MDNLink = createFactory(require("./MdnLink"));
const RequestListHeader = createFactory(require("./RequestListHeader"));

const { button, div, span } = dom;

const RELOAD_NOTICE_1 = L10N.getStr("netmonitor.reloadNotice1");
const RELOAD_NOTICE_2 = L10N.getStr("netmonitor.reloadNotice2");
const RELOAD_NOTICE_3 = L10N.getStr("netmonitor.reloadNotice3");
const PERFORMANCE_NOTICE_1 = L10N.getStr("netmonitor.perfNotice1");
const PERFORMANCE_NOTICE_2 = L10N.getStr("netmonitor.perfNotice2");
const PERFORMANCE_NOTICE_3 = L10N.getStr("netmonitor.perfNotice3");

/**
 * UI displayed when the request list is empty. Contains instructions on reloading
 * the page and on triggering performance analysis of the page.
 */
class RequestListEmptyNotice extends Component {
  static get propTypes() {
    return {
      connector: PropTypes.object.isRequired,
      onReloadClick: PropTypes.func.isRequired,
      onPerfClick: PropTypes.func.isRequired,
    };
  }

  render() {
    return div(
      {
        className: "request-list-empty-notice",
      },
      RequestListHeader(),
      div({ className: "notice-reload-message empty-notice-element" },
        span(null, RELOAD_NOTICE_1),
        button(
          {
            className: "devtools-button requests-list-reload-notice-button",
            "data-standalone": true,
            onClick: this.props.onReloadClick,
          },
          RELOAD_NOTICE_2
        ),
        span(null, RELOAD_NOTICE_3)
      ),
      div({ className: "notice-perf-message empty-notice-element" },
        span(null, PERFORMANCE_NOTICE_1),
        button({
          title: PERFORMANCE_NOTICE_3,
          className: "devtools-button requests-list-perf-notice-button",
          "data-standalone": true,
          onClick: this.props.onPerfClick,
        }),
        span(null, PERFORMANCE_NOTICE_2),
        MDNLink({ url: getPerformanceAnalysisURL() })
      )
    );
  }
}

module.exports = connect(
  undefined,
  (dispatch, props) => ({
    onPerfClick: () => dispatch(Actions.openStatistics(props.connector, true)),
    onReloadClick: () => props.connector.triggerActivity(
      ACTIVITY_TYPE.RELOAD.WITH_CACHE_DEFAULT),
  })
)(RequestListEmptyNotice);
PK
!<k%���Vchrome/devtools/modules/devtools/client/netmonitor/src/components/RequestListHeader.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const { connect } = require("devtools/client/shared/redux/visibility-handler-connect");
const { getTheme, addThemeObserver, removeThemeObserver } =
  require("devtools/client/shared/theme");
const Actions = require("../actions/index");
const { HEADERS, REQUESTS_WATERFALL } = require("../constants");
const { getWaterfallScale } = require("../selectors/index");
const { getFormattedTime } = require("../utils/format-utils");
const { L10N } = require("../utils/l10n");
const RequestListHeaderContextMenu = require("../widgets/RequestListHeaderContextMenu");
const WaterfallBackground = require("../widgets/WaterfallBackground");

const { div, button } = dom;

/**
 * Render the request list header with sorting arrows for columns.
 * Displays tick marks in the waterfall column header.
 * Also draws the waterfall background canvas and updates it when needed.
 */
class RequestListHeader extends Component {
  static get propTypes() {
    return {
      columns: PropTypes.object.isRequired,
      resetColumns: PropTypes.func.isRequired,
      resizeWaterfall: PropTypes.func.isRequired,
      scale: PropTypes.number,
      sort: PropTypes.object,
      sortBy: PropTypes.func.isRequired,
      toggleColumn: PropTypes.func.isRequired,
      waterfallWidth: PropTypes.number,
    };
  }

  constructor(props) {
    super(props);
    this.onContextMenu = this.onContextMenu.bind(this);
    this.drawBackground = this.drawBackground.bind(this);
    this.resizeWaterfall = this.resizeWaterfall.bind(this);
    this.waterfallDivisionLabels = this.waterfallDivisionLabels.bind(this);
    this.waterfallLabel = this.waterfallLabel.bind(this);
  }

  componentWillMount() {
    const { resetColumns, toggleColumn } = this.props;
    this.contextMenu = new RequestListHeaderContextMenu({
      resetColumns,
      toggleColumn,
    });
  }

  componentDidMount() {
    // Create the object that takes care of drawing the waterfall canvas background
    this.background = new WaterfallBackground(document);
    this.drawBackground();
    this.resizeWaterfall();
    window.addEventListener("resize", this.resizeWaterfall);
    addThemeObserver(this.drawBackground);
  }

  componentDidUpdate() {
    this.drawBackground();
  }

  componentWillUnmount() {
    this.background.destroy();
    this.background = null;
    window.removeEventListener("resize", this.resizeWaterfall);
    removeThemeObserver(this.drawBackground);
  }

  onContextMenu(evt) {
    evt.preventDefault();
    this.contextMenu.open(evt, this.props.columns);
  }

  drawBackground() {
    // The background component is theme dependent, so add the current theme to the props.
    let props = Object.assign({}, this.props, {
      theme: getTheme()
    });
    this.background.draw(props);
  }

  resizeWaterfall() {
    let waterfallHeader = this.refs.waterfallHeader;
    if (waterfallHeader) {
      // Measure its width and update the 'waterfallWidth' property in the store.
      // The 'waterfallWidth' will be further updated on every window resize.
      window.cancelIdleCallback(this._resizeTimerId);
      this._resizeTimerId = window.requestIdleCallback(() =>
        this.props.resizeWaterfall(waterfallHeader.getBoundingClientRect().width));
    }
  }

  /**
   * Build the waterfall header - timing tick marks with the right spacing
   */
  waterfallDivisionLabels(waterfallWidth, scale) {
    let labels = [];

    // Build new millisecond tick labels...
    let timingStep = REQUESTS_WATERFALL.HEADER_TICKS_MULTIPLE;
    let scaledStep = scale * timingStep;

    // Ignore any divisions that would end up being too close to each other.
    while (scaledStep < REQUESTS_WATERFALL.HEADER_TICKS_SPACING_MIN) {
      scaledStep *= 2;
    }

    // Insert one label for each division on the current scale.
    for (let x = 0; x < waterfallWidth; x += scaledStep) {
      let millisecondTime = x / scale;
      let divisionScale = "millisecond";

      // If the division is greater than 1 minute.
      if (millisecondTime > 60000) {
        divisionScale = "minute";
      } else if (millisecondTime > 1000) {
        // If the division is greater than 1 second.
        divisionScale = "second";
      }

      let width = (x + scaledStep | 0) - (x | 0);
      // Adjust the first marker for the borders
      if (x == 0) {
        width -= 2;
      }
      // Last marker doesn't need a width specified at all
      if (x + scaledStep >= waterfallWidth) {
        width = undefined;
      }

      labels.push(div(
        {
          key: labels.length,
          className: "requests-list-timings-division",
          "data-division-scale": divisionScale,
          style: { width }
        },
        getFormattedTime(millisecondTime)
      ));
    }

    return labels;
  }

  waterfallLabel(waterfallWidth, scale, label) {
    let className = "button-text requests-list-waterfall-label-wrapper";

    if (waterfallWidth !== null && scale !== null) {
      label = this.waterfallDivisionLabels(waterfallWidth, scale);
      className += " requests-list-waterfall-visible";
    }

    return div({ className }, label);
  }

  render() {
    let { columns, scale, sort, sortBy, waterfallWidth } = this.props;

    return (
      div({ className: "devtools-toolbar requests-list-headers-wrapper" },
        div({
          className: "devtools-toolbar requests-list-headers",
          onContextMenu: this.onContextMenu
        },
          HEADERS.filter((header) => columns[header.name]).map((header) => {
            let name = header.name;
            let boxName = header.boxName || name;
            let label = header.noLocalization
              ? name : L10N.getStr(`netmonitor.toolbar.${header.label || name}`);
            let sorted, sortedTitle;
            let active = sort.type == name ? true : undefined;

            if (active) {
              sorted = sort.ascending ? "ascending" : "descending";
              sortedTitle = L10N.getStr(sort.ascending
                ? "networkMenu.sortedAsc"
                : "networkMenu.sortedDesc");
            }

            return (
              div({
                id: `requests-list-${boxName}-header-box`,
                className: `requests-list-column requests-list-${boxName}`,
                key: name,
                ref: `${name}Header`,
                // Used to style the next column.
                "data-active": active,
              },
                button({
                  id: `requests-list-${name}-button`,
                  className: `requests-list-header-button`,
                  "data-sorted": sorted,
                  title: sortedTitle ? `${label} (${sortedTitle})` : label,
                  onClick: () => sortBy(name),
                },
                  name === "waterfall"
                    ? this.waterfallLabel(waterfallWidth, scale, label)
                    : div({ className: "button-text" }, label),
                  div({ className: "button-icon" })
                )
              )
            );
          })
        )
      )
    );
  }
}

module.exports = connect(
  (state) => ({
    columns: state.ui.columns,
    firstRequestStartedMillis: state.requests.firstStartedMillis,
    scale: getWaterfallScale(state),
    sort: state.sort,
    timingMarkers: state.timingMarkers,
    waterfallWidth: state.ui.waterfallWidth,
  }),
  (dispatch) => ({
    resetColumns: () => dispatch(Actions.resetColumns()),
    resizeWaterfall: (width) => dispatch(Actions.resizeWaterfall(width)),
    sortBy: (type) => dispatch(Actions.sortBy(type)),
    toggleColumn: (column) => dispatch(Actions.toggleColumn(column)),
  })
)(RequestListHeader);
PK
!<��&c#c#Tchrome/devtools/modules/devtools/client/netmonitor/src/components/RequestListItem.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/vendor/react");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const { div } = dom;
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const {
  fetchNetworkUpdatePacket,
  propertiesEqual,
} = require("../utils/request-utils");
const { RESPONSE_HEADERS } = require("../constants");

// Components
/* global
  RequestListColumnCause,
  RequestListColumnContentSize,
  RequestListColumnCookies,
  RequestListColumnDomain,
  RequestListColumnFile,
  RequestListColumnMethod,
  RequestListColumnProtocol,
  RequestListColumnRemoteIP,
  RequestListColumnResponseHeader,
  RequestListColumnScheme,
  RequestListColumnSetCookies,
  RequestListColumnStatus,
  RequestListColumnTime,
  RequestListColumnTransferredSize,
  RequestListColumnType,
  RequestListColumnWaterfall
*/

loader.lazyGetter(this, "RequestListColumnCause", function () {
  return createFactory(require("./RequestListColumnCause"));
});
loader.lazyGetter(this, "RequestListColumnContentSize", function () {
  return createFactory(require("./RequestListColumnContentSize"));
});
loader.lazyGetter(this, "RequestListColumnCookies", function () {
  return createFactory(require("./RequestListColumnCookies"));
});
loader.lazyGetter(this, "RequestListColumnDomain", function () {
  return createFactory(require("./RequestListColumnDomain"));
});
loader.lazyGetter(this, "RequestListColumnFile", function () {
  return createFactory(require("./RequestListColumnFile"));
});
loader.lazyGetter(this, "RequestListColumnMethod", function () {
  return createFactory(require("./RequestListColumnMethod"));
});
loader.lazyGetter(this, "RequestListColumnProtocol", function () {
  return createFactory(require("./RequestListColumnProtocol"));
});
loader.lazyGetter(this, "RequestListColumnRemoteIP", function () {
  return createFactory(require("./RequestListColumnRemoteIP"));
});
loader.lazyGetter(this, "RequestListColumnResponseHeader", function () {
  return createFactory(require("./RequestListColumnResponseHeader"));
});
loader.lazyGetter(this, "RequestListColumnTime", function () {
  return createFactory(require("./RequestListColumnTime"));
});
loader.lazyGetter(this, "RequestListColumnScheme", function () {
  return createFactory(require("./RequestListColumnScheme"));
});
loader.lazyGetter(this, "RequestListColumnSetCookies", function () {
  return createFactory(require("./RequestListColumnSetCookies"));
});
loader.lazyGetter(this, "RequestListColumnStatus", function () {
  return createFactory(require("./RequestListColumnStatus"));
});
loader.lazyGetter(this, "RequestListColumnTransferredSize", function () {
  return createFactory(require("./RequestListColumnTransferredSize"));
});
loader.lazyGetter(this, "RequestListColumnType", function () {
  return createFactory(require("./RequestListColumnType"));
});
loader.lazyGetter(this, "RequestListColumnWaterfall", function () {
  return createFactory(require("./RequestListColumnWaterfall"));
});

/**
 * Used by shouldComponentUpdate: compare two items, and compare only properties
 * relevant for rendering the RequestListItem. Other properties (like request and
 * response headers, cookies, bodies) are ignored. These are very useful for the
 * network details, but not here.
 */
const UPDATED_REQ_ITEM_PROPS = [
  "mimeType",
  "eventTimings",
  "securityState",
  "status",
  "statusText",
  "fromCache",
  "fromServiceWorker",
  "method",
  "url",
  "remoteAddress",
  "cause",
  "contentSize",
  "transferredSize",
  "startedMillis",
  "totalTime",
  "requestCookies",
  "requestHeaders",
  "responseCookies",
  "responseHeaders",
];

const UPDATED_REQ_PROPS = [
  "firstRequestStartedMillis",
  "index",
  "isSelected",
  "requestFilterTypes",
  "waterfallWidth",
];

/**
 * Render one row in the request list.
 */
class RequestListItem extends Component {
  static get propTypes() {
    return {
      connector: PropTypes.object.isRequired,
      columns: PropTypes.object.isRequired,
      item: PropTypes.object.isRequired,
      index: PropTypes.number.isRequired,
      isSelected: PropTypes.bool.isRequired,
      firstRequestStartedMillis: PropTypes.number.isRequired,
      fromCache: PropTypes.bool,
      onCauseBadgeMouseDown: PropTypes.func.isRequired,
      onContextMenu: PropTypes.func.isRequired,
      onFocusedNodeChange: PropTypes.func,
      onMouseDown: PropTypes.func.isRequired,
      onSecurityIconMouseDown: PropTypes.func.isRequired,
      onWaterfallMouseDown: PropTypes.func.isRequired,
      requestFilterTypes: PropTypes.object.isRequired,
      waterfallWidth: PropTypes.number,
    };
  }

  componentDidMount() {
    if (this.props.isSelected) {
      this.refs.listItem.focus();
    }

    let { connector, item, requestFilterTypes } = this.props;
    // Filtering XHR & WS require to lazily fetch requestHeaders & responseHeaders
    if (requestFilterTypes.xhr || requestFilterTypes.ws) {
      fetchNetworkUpdatePacket(connector.requestData, item, [
        "requestHeaders",
        "responseHeaders",
      ]);
    }
  }

  componentWillReceiveProps(nextProps) {
    let { connector, item, requestFilterTypes } = nextProps;
    // Filtering XHR & WS require to lazily fetch requestHeaders & responseHeaders
    if (requestFilterTypes.xhr || requestFilterTypes.ws) {
      fetchNetworkUpdatePacket(connector.requestData, item, [
        "requestHeaders",
        "responseHeaders",
      ]);
    }
  }

  shouldComponentUpdate(nextProps) {
    return !propertiesEqual(UPDATED_REQ_ITEM_PROPS, this.props.item, nextProps.item) ||
      !propertiesEqual(UPDATED_REQ_PROPS, this.props, nextProps) ||
      this.props.columns !== nextProps.columns;
  }

  componentDidUpdate(prevProps) {
    if (!prevProps.isSelected && this.props.isSelected) {
      this.refs.listItem.focus();
      if (this.props.onFocusedNodeChange) {
        this.props.onFocusedNodeChange();
      }
    }
  }

  render() {
    let {
      connector,
      columns,
      item,
      index,
      isSelected,
      firstRequestStartedMillis,
      fromCache,
      onContextMenu,
      onMouseDown,
      onCauseBadgeMouseDown,
      onSecurityIconMouseDown,
      onWaterfallMouseDown,
    } = this.props;

    let classList = ["request-list-item", index % 2 ? "odd" : "even"];
    isSelected && classList.push("selected");
    fromCache && classList.push("fromCache");

    return (
      div({
        ref: "listItem",
        className: classList.join(" "),
        "data-id": item.id,
        tabIndex: 0,
        onContextMenu,
        onMouseDown,
      },
        columns.status && RequestListColumnStatus({ item }),
        columns.method && RequestListColumnMethod({ item }),
        columns.file && RequestListColumnFile({ item }),
        columns.protocol && RequestListColumnProtocol({ item }),
        columns.scheme && RequestListColumnScheme({ item }),
        columns.domain && RequestListColumnDomain({
          item,
          onSecurityIconMouseDown
        }),
        columns.remoteip && RequestListColumnRemoteIP({ item }),
        columns.cause && RequestListColumnCause({
          item,
          onCauseBadgeMouseDown
        }),
        columns.type && RequestListColumnType({ item }),
        columns.cookies && RequestListColumnCookies({ connector, item }),
        columns.setCookies && RequestListColumnSetCookies({ connector, item }),
        columns.transferred && RequestListColumnTransferredSize({ item }),
        columns.contentSize && RequestListColumnContentSize({ item }),
        columns.startTime && RequestListColumnTime({
          connector,
          item,
          firstRequestStartedMillis,
          type: "start",
        }),
        columns.endTime && RequestListColumnTime({
          connector,
          item,
          firstRequestStartedMillis,
          type: "end",
        }),
        columns.responseTime && RequestListColumnTime({
          connector,
          item,
          firstRequestStartedMillis,
          type: "response",
        }),
        columns.duration && RequestListColumnTime({
          connector,
          item,
          firstRequestStartedMillis,
          type: "duration",
        }),
        columns.latency && RequestListColumnTime({
          connector,
          item,
          firstRequestStartedMillis,
          type: "latency",
        }),
        ...RESPONSE_HEADERS.filter(header => columns[header]).map(
          header => RequestListColumnResponseHeader({
            connector,
            item,
            header
          }),
        ),
        columns.waterfall && RequestListColumnWaterfall({
          connector,
          firstRequestStartedMillis,
          item,
          onWaterfallMouseDown,
        }),
      )
    );
  }
}

module.exports = RequestListItem;
PK
!<��L��Rchrome/devtools/modules/devtools/client/netmonitor/src/components/ResponsePanel.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const { L10N } = require("../utils/l10n");
const {
  decodeUnicodeBase64,
  fetchNetworkUpdatePacket,
  formDataURI,
  getUrlBaseName,
} = require("../utils/request-utils");
const { Filters } = require("../utils/filter-predicates");

// Components
const PropertiesView = createFactory(require("./PropertiesView"));

const { div, img } = dom;
const JSON_SCOPE_NAME = L10N.getStr("jsonScopeName");
const JSON_FILTER_TEXT = L10N.getStr("jsonFilterText");
const RESPONSE_IMG_NAME = L10N.getStr("netmonitor.response.name");
const RESPONSE_IMG_DIMENSIONS = L10N.getStr("netmonitor.response.dimensions");
const RESPONSE_IMG_MIMETYPE = L10N.getStr("netmonitor.response.mime");
const RESPONSE_PAYLOAD = L10N.getStr("responsePayload");
const RESPONSE_PREVIEW = L10N.getStr("responsePreview");

const JSON_VIEW_MIME_TYPE = "application/vnd.mozilla.json.view";

/*
 * Response panel component
 * Displays the GET parameters and POST data of a request
 */
class ResponsePanel extends Component {
  static get propTypes() {
    return {
      request: PropTypes.object.isRequired,
      openLink: PropTypes.func,
      connector: PropTypes.object.isRequired,
    };
  }

  constructor(props) {
    super(props);

    this.state = {
      imageDimensions: {
        width: 0,
        height: 0,
      },
    };

    this.updateImageDimemsions = this.updateImageDimemsions.bind(this);
    this.isJSON = this.isJSON.bind(this);
  }

  componentDidMount() {
    let { request, connector } = this.props;
    fetchNetworkUpdatePacket(connector.requestData, request, ["responseContent"]);
  }

  componentWillReceiveProps(nextProps) {
    let { request, connector } = nextProps;
    fetchNetworkUpdatePacket(connector.requestData, request, ["responseContent"]);
  }

  updateImageDimemsions({ target }) {
    this.setState({
      imageDimensions: {
        width: target.naturalWidth,
        height: target.naturalHeight,
      },
    });
  }

  // Handle json, which we tentatively identify by checking the MIME type
  // for "json" after any word boundary. This works for the standard
  // "application/json", and also for custom types like "x-bigcorp-json".
  // Additionally, we also directly parse the response text content to
  // verify whether it's json or not, to handle responses incorrectly
  // labeled as text/plain instead.
  isJSON(mimeType, response) {
    let json, error;
    try {
      json = JSON.parse(response);
    } catch (err) {
      try {
        json = JSON.parse(atob(response));
      } catch (err64) {
        error = err;
      }
    }

    if (/\bjson/.test(mimeType) || json) {
      // Extract the actual json substring in case this might be a "JSONP".
      // This regex basically parses a function call and captures the
      // function name and arguments in two separate groups.
      let jsonpRegex = /^\s*([\w$]+)\s*\(\s*([^]*)\s*\)\s*;?\s*$/;
      let [, jsonpCallback, jsonp] = response.match(jsonpRegex) || [];
      let result = {};

      // Make sure this is a valid JSON object first. If so, nicely display
      // the parsing results in a tree view.
      if (jsonpCallback && jsonp) {
        error = null;
        try {
          json = JSON.parse(jsonp);
        } catch (err) {
          error = err;
        }
      }

      // Valid JSON
      if (json) {
        result.json = json;
      }
      // Valid JSONP
      if (jsonpCallback) {
        result.jsonpCallback = jsonpCallback;
      }
      // Malformed JSON
      if (error) {
        result.error = "" + error;
      }

      return result;
    }

    return null;
  }

  render() {
    let { openLink, request } = this.props;
    let { responseContent, url } = request;

    if (!responseContent || typeof responseContent.content.text !== "string") {
      return null;
    }

    let { encoding, mimeType, text } = responseContent.content;

    if (mimeType.includes("image/")) {
      let { width, height } = this.state.imageDimensions;

      return (
        div({ className: "panel-container response-image-box devtools-monospace" },
          img({
            className: "response-image",
            src: formDataURI(mimeType, encoding, text),
            onLoad: this.updateImageDimemsions,
          }),
          div({ className: "response-summary" },
            div({ className: "tabpanel-summary-label" }, RESPONSE_IMG_NAME),
            div({ className: "tabpanel-summary-value" }, getUrlBaseName(url)),
          ),
          div({ className: "response-summary" },
            div({ className: "tabpanel-summary-label" }, RESPONSE_IMG_DIMENSIONS),
            div({ className: "tabpanel-summary-value" }, `${width} × ${height}`),
          ),
          div({ className: "response-summary" },
            div({ className: "tabpanel-summary-label" }, RESPONSE_IMG_MIMETYPE),
            div({ className: "tabpanel-summary-value" }, mimeType),
          ),
        )
      );
    }

    // Decode response if it's coming from JSONView.
    if (mimeType.includes(JSON_VIEW_MIME_TYPE) && encoding === "base64") {
      text = decodeUnicodeBase64(text);
    }

    // Display Properties View
    let { json, jsonpCallback, error } = this.isJSON(mimeType, text) || {};
    let object = {};
    let sectionName;

    if (json) {
      if (jsonpCallback) {
        sectionName = L10N.getFormatStr("jsonpScopeName", jsonpCallback);
      } else {
        sectionName = JSON_SCOPE_NAME;
      }
      object[sectionName] = json;
    }

    // Display HTML under Properties View
    if (Filters.html(this.props.request)) {
      object[RESPONSE_PREVIEW] = {
        HTML_PREVIEW: { responseContent }
      };
    }

    // Others like text/html, text/plain, application/javascript
    object[RESPONSE_PAYLOAD] = {
      EDITOR_CONFIG: {
        text,
        mode: json ? "application/json" : mimeType.replace(/;.+/, ""),
      },
    };

    let classList = ["panel-container"];
    if (Filters.html(this.props.request)) {
      classList.push("contains-html-preview");
    }

    return (
      div({ className: classList.join(" ") },
        error && div({ className: "response-error-header", title: error },
          error
        ),
        PropertiesView({
          object,
          filterPlaceHolder: JSON_FILTER_TEXT,
          sectionNames: Object.keys(object),
          openLink,
        }),
      )
    );
  }
}

module.exports = ResponsePanel;
PK
!<J}פRchrome/devtools/modules/devtools/client/netmonitor/src/components/SecurityPanel.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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 { L10N } = require("../utils/l10n");
const {
  fetchNetworkUpdatePacket,
  getUrlHost,
} = require("../utils/request-utils");

// Components
const TreeViewClass = require("devtools/client/shared/components/tree/TreeView");
const PropertiesView = createFactory(require("./PropertiesView"));

const { div, input, span } = dom;
const NOT_AVAILABLE = L10N.getStr("netmonitor.security.notAvailable");
const ERROR_LABEL = L10N.getStr("netmonitor.security.error");
const CIPHER_SUITE_LABEL = L10N.getStr("netmonitor.security.cipherSuite");
const WARNING_CIPHER_LABEL = L10N.getStr("netmonitor.security.warning.cipher");
const ENABLED_LABEL = L10N.getStr("netmonitor.security.enabled");
const DISABLED_LABEL = L10N.getStr("netmonitor.security.disabled");
const CONNECTION_LABEL = L10N.getStr("netmonitor.security.connection");
const PROTOCOL_VERSION_LABEL = L10N.getStr("netmonitor.security.protocolVersion");
const KEA_GROUP_LABEL = L10N.getStr("netmonitor.security.keaGroup");
const SIGNATURE_SCHEME_LABEL = L10N.getStr("netmonitor.security.signatureScheme");
const HSTS_LABEL = L10N.getStr("netmonitor.security.hsts");
const HPKP_LABEL = L10N.getStr("netmonitor.security.hpkp");
const CERTIFICATE_LABEL = L10N.getStr("netmonitor.security.certificate");
const SUBJECT_INFO_LABEL = L10N.getStr("certmgr.subjectinfo.label");
const CERT_DETAIL_COMMON_NAME_LABEL = L10N.getStr("certmgr.certdetail.cn");
const CERT_DETAIL_ORG_LABEL = L10N.getStr("certmgr.certdetail.o");
const CERT_DETAIL_ORG_UNIT_LABEL = L10N.getStr("certmgr.certdetail.ou");
const ISSUER_INFO_LABEL = L10N.getStr("certmgr.issuerinfo.label");
const PERIOD_OF_VALIDITY_LABEL = L10N.getStr("certmgr.periodofvalidity.label");
const BEGINS_LABEL = L10N.getStr("certmgr.begins");
const EXPIRES_LABEL = L10N.getStr("certmgr.expires");
const FINGERPRINTS_LABEL = L10N.getStr("certmgr.fingerprints.label");
const SHA256_FINGERPRINT_LABEL =
  L10N.getStr("certmgr.certdetail.sha256fingerprint");
const SHA1_FINGERPRINT_LABEL = L10N.getStr("certmgr.certdetail.sha1fingerprint");

/*
 * Security panel component
 * If the site is being served over HTTPS, you get an extra tab labeled "Security".
 * This contains details about the secure connection used including the protocol,
 * the cipher suite, and certificate details
 */
class SecurityPanel extends Component {
  static get propTypes() {
    return {
      connector: PropTypes.object.isRequired,
      openLink: PropTypes.func,
      request: PropTypes.object.isRequired,
    };
  }

  componentDidMount() {
    let { request, connector } = this.props;
    fetchNetworkUpdatePacket(connector.requestData, request, ["securityInfo"]);
  }

  componentWillReceiveProps(nextProps) {
    let { request, connector } = nextProps;
    fetchNetworkUpdatePacket(connector.requestData, request, ["securityInfo"]);
  }

  renderValue(props, weaknessReasons = []) {
    const { member, value } = props;

    // Hide object summary
    if (typeof member.value === "object") {
      return null;
    }

    return span({ className: "security-info-value" },
      member.name === ERROR_LABEL ?
        // Display multiline text for security error
        value
        :
        // Display one line selectable text for security details
        input({
          className: "textbox-input",
          readOnly: "true",
          value,
        })
      ,
      weaknessReasons.includes("cipher") &&
      member.name === CIPHER_SUITE_LABEL ?
        // Display an extra warning icon after the cipher suite
        div({
          id: "security-warning-cipher",
          className: "security-warning-icon",
          title: WARNING_CIPHER_LABEL,
        })
        :
        null
    );
  }

  render() {
    let { openLink, request } = this.props;
    const { securityInfo, url } = request;

    if (!securityInfo || !url) {
      return null;
    }

    let object;

    if (securityInfo.state === "secure" || securityInfo.state === "weak") {
      const { subject, issuer, validity, fingerprint } = securityInfo.cert;
      const HOST_HEADER_LABEL = L10N.getFormatStr("netmonitor.security.hostHeader",
        getUrlHost(url));

      object = {
        [CONNECTION_LABEL]: {
          [PROTOCOL_VERSION_LABEL]:
            securityInfo.protocolVersion || NOT_AVAILABLE,
          [CIPHER_SUITE_LABEL]:
            securityInfo.cipherSuite || NOT_AVAILABLE,
          [KEA_GROUP_LABEL]:
            securityInfo.keaGroupName || NOT_AVAILABLE,
          [SIGNATURE_SCHEME_LABEL]:
            securityInfo.signatureSchemeName || NOT_AVAILABLE,
        },
        [HOST_HEADER_LABEL]: {
          [HSTS_LABEL]:
            securityInfo.hsts ? ENABLED_LABEL : DISABLED_LABEL,
          [HPKP_LABEL]:
            securityInfo.hpkp ? ENABLED_LABEL : DISABLED_LABEL,
        },
        [CERTIFICATE_LABEL]: {
          [SUBJECT_INFO_LABEL]: {
            [CERT_DETAIL_COMMON_NAME_LABEL]:
              subject.commonName || NOT_AVAILABLE,
            [CERT_DETAIL_ORG_LABEL]:
              subject.organization || NOT_AVAILABLE,
            [CERT_DETAIL_ORG_UNIT_LABEL]:
              subject.organizationUnit || NOT_AVAILABLE,
          },
          [ISSUER_INFO_LABEL]: {
            [CERT_DETAIL_COMMON_NAME_LABEL]:
              issuer.commonName || NOT_AVAILABLE,
            [CERT_DETAIL_ORG_LABEL]:
              issuer.organization || NOT_AVAILABLE,
            [CERT_DETAIL_ORG_UNIT_LABEL]:
              issuer.organizationUnit || NOT_AVAILABLE,
          },
          [PERIOD_OF_VALIDITY_LABEL]: {
            [BEGINS_LABEL]:
              validity.start || NOT_AVAILABLE,
            [EXPIRES_LABEL]:
              validity.end || NOT_AVAILABLE,
          },
          [FINGERPRINTS_LABEL]: {
            [SHA256_FINGERPRINT_LABEL]:
              fingerprint.sha256 || NOT_AVAILABLE,
            [SHA1_FINGERPRINT_LABEL]:
              fingerprint.sha1 || NOT_AVAILABLE,
          },
        },
      };
    } else {
      object = {
        [ERROR_LABEL]:
          new DOMParser().parseFromString(securityInfo.errorMessage, "text/html")
            .body.textContent || NOT_AVAILABLE
      };
    }

    return div({ className: "panel-container security-panel" },
      PropertiesView({
        object,
        renderValue: (props) => this.renderValue(props, securityInfo.weaknessReasons),
        enableFilter: false,
        expandedNodes: TreeViewClass.getExpandedNodes(object),
        openLink,
      })
    );
  }
}

module.exports = SecurityPanel;
PK
!<�o�Qchrome/devtools/modules/devtools/client/netmonitor/src/components/SourceEditor.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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 Editor = require("devtools/client/sourceeditor/editor");

const { div } = dom;

/**
 * CodeMirror editor as a React component
 */
class SourceEditor extends Component {
  static get propTypes() {
    return {
      // Source editor syntax highlight mode, which is a mime type defined in CodeMirror
      mode: PropTypes.string,
      // Source editor content
      text: PropTypes.string,
    };
  }

  componentDidMount() {
    const { mode, text } = this.props;

    this.editor = new Editor({
      lineNumbers: true,
      lineWrapping: false,
      mode: null, // Disable auto syntax detection, but then we set mode asynchronously
      readOnly: true,
      theme: "mozilla",
      value: text,
    });

    // Delay to CodeMirror initialization content to prevent UI freezing
    this.editorTimeout = setTimeout(() => {
      this.editor.appendToLocalElement(this.refs.editorElement);
      // CodeMirror's setMode() (syntax highlight) is the performance bottleneck when
      // processing large content, so we enable it asynchronously within the setTimeout
      // to avoid UI blocking. (rendering source code -> drawing syntax highlight)
      this.editorSetModeTimeout = setTimeout(() => {
        this.editor.setMode(mode);
      });
    });
  }

  shouldComponentUpdate(nextProps) {
    return nextProps.mode !== this.props.mode || nextProps.text !== this.props.text;
  }

  componentDidUpdate(prevProps) {
    const { mode, text } = this.props;

    if (prevProps.text !== text) {
      // Reset the existed 'mode' attribute in order to make setText() process faster
      // to prevent drawing unnecessary syntax highlight.
      this.editor.setMode(null);
      this.editor.setText(text);

      // CodeMirror's setMode() (syntax highlight) is the performance bottleneck when
      // processing large content, so we enable it asynchronously within the setTimeout
      // to avoid UI blocking. (rendering source code -> drawing syntax highlight)
      this.editorSetModeTimeout = setTimeout(() => {
        this.editor.setMode(mode);
      });
    }
  }

  componentWillUnmount() {
    clearTimeout(this.editorTimeout);
    clearTimeout(this.editorSetModeTimeout);
    this.editor.destroy();
  }

  render() {
    return (
      div({
        ref: "editorElement",
        className: "source-editor-mount devtools-monospace",
      })
    );
  }
}

module.exports = SourceEditor;
PK
!<��AATchrome/devtools/modules/devtools/client/netmonitor/src/components/StackTracePanel.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const { fetchNetworkUpdatePacket } = require("../utils/request-utils");

const { div } = dom;

// Components
const StackTrace = createFactory(require("devtools/client/shared/components/StackTrace"));

/**
 * This component represents a side panel responsible for
 * rendering stack-trace info for selected request.
 */
class StackTracePanel extends Component {
  static get propTypes() {
    return {
      connector: PropTypes.object.isRequired,
      request: PropTypes.object.isRequired,
      sourceMapService: PropTypes.object,
      openLink: PropTypes.func,
    };
  }

  /**
   * `componentDidMount` is called when opening the StackTracePanel
   * for the first time
   */
  componentDidMount() {
    let { request, connector } = this.props;
    fetchNetworkUpdatePacket(connector.requestData, request, ["stackTrace"]);
  }

  /**
   * `componentWillReceiveProps` is the only method called when
   * switching between two requests while this panel is displayed.
   */
  componentWillReceiveProps(nextProps) {
    let { request, connector } = nextProps;
    fetchNetworkUpdatePacket(connector.requestData, request, ["stackTrace"]);
  }

  render() {
    let {
      connector,
      openLink,
      request,
      sourceMapService,
    } = this.props;

    let {
      stacktrace = []
    } = request;

    return (
      div({ className: "panel-container" },
        StackTrace({
          stacktrace,
          onViewSourceInDebugger: ({ url, line }) => {
            return connector.viewSourceInDebugger(url, line);
          },
          sourceMapService,
          openLink,
        }),
      )
    );
  }
}

module.exports = StackTracePanel;
PK
!<��I�,,Tchrome/devtools/modules/devtools/client/netmonitor/src/components/StatisticsPanel.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 ReactDOM = require("devtools/client/shared/vendor/react-dom");
const { FILTER_TAGS } = require("../constants");
const { Component, createFactory } = 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");
const { connect } = require("devtools/client/shared/redux/visibility-handler-connect");
const { Chart } = require("devtools/client/shared/widgets/Chart");
const { PluralForm } = require("devtools/shared/plural-form");
const Actions = require("../actions/index");
const { Filters } = require("../utils/filter-predicates");
const { getSizeWithDecimals, getTimeWithDecimals } = require("../utils/format-utils");
const { L10N } = require("../utils/l10n");
const { getPerformanceAnalysisURL } = require("../utils/mdn-utils");
const { fetchNetworkUpdatePacket } = require("../utils/request-utils");

// Components
const MDNLink = createFactory(require("./MdnLink"));

const { button, div } = dom;
const MediaQueryList = window.matchMedia("(min-width: 700px)");

const NETWORK_ANALYSIS_PIE_CHART_DIAMETER = 200;
const BACK_BUTTON = L10N.getStr("netmonitor.backButton");
const CHARTS_CACHE_ENABLED = L10N.getStr("charts.cacheEnabled");
const CHARTS_CACHE_DISABLED = L10N.getStr("charts.cacheDisabled");

/*
 * Statistics panel component
 * Performance analysis tool which shows you how long the browser takes to
 * download the different parts of your site.
 */
class StatisticsPanel extends Component {
  static get propTypes() {
    return {
      connector: PropTypes.object.isRequired,
      closeStatistics: PropTypes.func.isRequired,
      enableRequestFilterTypeOnly: PropTypes.func.isRequired,
      requests: PropTypes.array,
    };
  }

  constructor(props) {
    super(props);

    this.state = {
      isVerticalSpliter: MediaQueryList.matches,
    };

    this.createMDNLink = this.createMDNLink.bind(this);
    this.unmountMDNLinkContainers = this.unmountMDNLinkContainers.bind(this);
    this.createChart = this.createChart.bind(this);
    this.sanitizeChartDataSource = this.sanitizeChartDataSource.bind(this);
    this.responseIsFresh = this.responseIsFresh.bind(this);
    this.onLayoutChange = this.onLayoutChange.bind(this);
  }

  componentWillMount() {
    this.mdnLinkContainerNodes = new Map();
  }

  componentDidMount() {
    let { requests, connector } = this.props;
    requests.forEach((request) => {
      fetchNetworkUpdatePacket(connector.requestData, request, [
        "eventTimings",
        "responseHeaders",
      ]);
    });
  }

  componentWillReceiveProps(nextProps) {
    let { requests, connector } = nextProps;
    requests.forEach((request) => {
      fetchNetworkUpdatePacket(connector.requestData, request, [
        "eventTimings",
        "responseHeaders",
      ]);
    });
  }

  componentDidUpdate(prevProps) {
    MediaQueryList.addListener(this.onLayoutChange);

    const { requests } = this.props;
    let ready = requests && requests.length && requests.every((req) =>
      req.contentSize !== undefined && req.mimeType && req.responseHeaders &&
      req.status !== undefined && req.totalTime !== undefined
    );

    this.createChart({
      id: "primedCacheChart",
      title: CHARTS_CACHE_ENABLED,
      data: ready ? this.sanitizeChartDataSource(requests, false) : null,
    });

    this.createChart({
      id: "emptyCacheChart",
      title: CHARTS_CACHE_DISABLED,
      data: ready ? this.sanitizeChartDataSource(requests, true) : null,
    });

    this.createMDNLink("primedCacheChart", getPerformanceAnalysisURL());
    this.createMDNLink("emptyCacheChart", getPerformanceAnalysisURL());
  }

  componentWillUnmount() {
    MediaQueryList.removeListener(this.onLayoutChange);
    this.unmountMDNLinkContainers();
  }

  createMDNLink(chartId, url) {
    if (this.mdnLinkContainerNodes.has(chartId)) {
      ReactDOM.unmountComponentAtNode(this.mdnLinkContainerNodes.get(chartId));
    }

    // MDNLink is a React component but Chart isn't.  To get the link
    // into the chart we mount a new ReactDOM at the appropriate
    // location after the chart has been created.
    let title = this.refs[chartId].querySelector(".table-chart-title");
    let containerNode = document.createElement("span");
    title.appendChild(containerNode);
    ReactDOM.render(MDNLink({ url }), containerNode);
    this.mdnLinkContainerNodes.set(chartId, containerNode);
  }

  unmountMDNLinkContainers() {
    for (let [, node] of this.mdnLinkContainerNodes) {
      ReactDOM.unmountComponentAtNode(node);
    }
  }

  createChart({ id, title, data }) {
    // Create a new chart.
    let chart = Chart.PieTable(document, {
      diameter: NETWORK_ANALYSIS_PIE_CHART_DIAMETER,
      title,
      header: {
        cached: "",
        count: "",
        label: L10N.getStr("charts.type"),
        size: L10N.getStr("charts.size"),
        transferredSize: L10N.getStr("charts.transferred"),
        time: L10N.getStr("charts.time"),
        nonBlockingTime: L10N.getStr("charts.nonBlockingTime"),
      },
      data,
      strings: {
        size: (value) =>
          L10N.getFormatStr("charts.sizeKB", getSizeWithDecimals(value / 1024)),
        transferredSize: (value) =>
          L10N.getFormatStr("charts.transferredSizeKB",
            getSizeWithDecimals(value / 1024)),
        time: (value) =>
          L10N.getFormatStr("charts.totalS", getTimeWithDecimals(value / 1000)),
        nonBlockingTime: (value) =>
          L10N.getFormatStr("charts.totalS", getTimeWithDecimals(value / 1000)),
      },
      totals: {
        cached: (total) => L10N.getFormatStr("charts.totalCached", total),
        count: (total) => L10N.getFormatStr("charts.totalCount", total),
        size: (total) =>
          L10N.getFormatStr("charts.totalSize", getSizeWithDecimals(total / 1024)),
        transferredSize: total =>
          L10N.getFormatStr("charts.totalTransferredSize",
            getSizeWithDecimals(total / 1024)),
        time: (total) => {
          let seconds = total / 1000;
          let string = getTimeWithDecimals(seconds);
          return PluralForm.get(seconds,
            L10N.getStr("charts.totalSeconds")).replace("#1", string);
        },
        nonBlockingTime: (total) => {
          let seconds = total / 1000;
          let string = getTimeWithDecimals(seconds);
          return PluralForm.get(seconds,
            L10N.getStr("charts.totalSecondsNonBlocking")).replace("#1", string);
        },
      },
      sorted: true,
    });

    chart.on("click", ({ label }) => {
      // Reset FilterButtons and enable one filter exclusively
      this.props.closeStatistics();
      this.props.enableRequestFilterTypeOnly(label);
    });

    let container = this.refs[id];

    // Nuke all existing charts of the specified type.
    while (container.hasChildNodes()) {
      container.firstChild.remove();
    }

    container.appendChild(chart.node);
  }

  sanitizeChartDataSource(requests, emptyCache) {
    const data = FILTER_TAGS.map((type) => ({
      cached: 0,
      count: 0,
      label: type,
      size: 0,
      transferredSize: 0,
      time: 0,
      nonBlockingTime: 0,
    }));

    for (let request of requests) {
      let type;

      if (Filters.html(request)) {
        // "html"
        type = 0;
      } else if (Filters.css(request)) {
        // "css"
        type = 1;
      } else if (Filters.js(request)) {
        // "js"
        type = 2;
      } else if (Filters.fonts(request)) {
        // "fonts"
        type = 4;
      } else if (Filters.images(request)) {
        // "images"
        type = 5;
      } else if (Filters.media(request)) {
        // "media"
        type = 6;
      } else if (Filters.ws(request)) {
        // "ws"
        type = 7;
      } else if (Filters.xhr(request)) {
        // Verify XHR last, to categorize other mime types in their own blobs.
        // "xhr"
        type = 3;
      } else {
        // "other"
        type = 8;
      }

      if (emptyCache || !this.responseIsFresh(request)) {
        data[type].time += request.totalTime || 0;
        data[type].size += request.contentSize || 0;
        data[type].transferredSize += request.transferredSize || 0;
        let nonBlockingTime =
           request.eventTimings.totalTime - request.eventTimings.timings.blocked;
        data[type].nonBlockingTime += nonBlockingTime || 0;
      } else {
        data[type].cached++;
      }
      data[type].count++;
    }

    return data.filter(e => e.count > 0);
  }

  /**
   * Checks if the "Expiration Calculations" defined in section 13.2.4 of the
   * "HTTP/1.1: Caching in HTTP" spec holds true for a collection of headers.
   *
   * @param object
   *        An object containing the { responseHeaders, status } properties.
   * @return boolean
   *         True if the response is fresh and loaded from cache.
   */
  responseIsFresh({ responseHeaders, status }) {
    // Check for a "304 Not Modified" status and response headers availability.
    if (status != 304 || !responseHeaders) {
      return false;
    }

    let list = responseHeaders.headers;
    let cacheControl = list.find(e => e.name.toLowerCase() === "cache-control");
    let expires = list.find(e => e.name.toLowerCase() === "expires");

    // Check the "Cache-Control" header for a maximum age value.
    if (cacheControl) {
      let maxAgeMatch =
        cacheControl.value.match(/s-maxage\s*=\s*(\d+)/) ||
        cacheControl.value.match(/max-age\s*=\s*(\d+)/);

      if (maxAgeMatch && maxAgeMatch.pop() > 0) {
        return true;
      }
    }

    // Check the "Expires" header for a valid date.
    if (expires && Date.parse(expires.value)) {
      return true;
    }

    return false;
  }

  onLayoutChange() {
    this.setState({
      isVerticalSpliter: MediaQueryList.matches,
    });
  }

  render() {
    const { closeStatistics } = this.props;
    let splitterClassName = ["splitter"];

    if (this.state.isVerticalSpliter) {
      splitterClassName.push("devtools-side-splitter");
    } else {
      splitterClassName.push("devtools-horizontal-splitter");
    }

    return (
      div({ className: "statistics-panel" },
        button({
          className: "back-button devtools-button",
          "data-text-only": "true",
          title: BACK_BUTTON,
          onClick: closeStatistics,
        }, BACK_BUTTON),
        div({ className: "charts-container" },
          div({ ref: "primedCacheChart", className: "charts primed-cache-chart" }),
          div({ className: splitterClassName.join(" ") }),
          div({ ref: "emptyCacheChart", className: "charts empty-cache-chart" }),
        ),
      )
    );
  }
}

module.exports = connect(
  (state) => ({
    requests: [...state.requests.requests.values()],
  }),
  (dispatch, props) => ({
    closeStatistics: () => dispatch(Actions.openStatistics(props.connector, false)),
    enableRequestFilterTypeOnly: (label) =>
      dispatch(Actions.enableRequestFilterTypeOnly(label)),
  })
)(StatisticsPanel);
PK
!<�9�_,,Nchrome/devtools/modules/devtools/client/netmonitor/src/components/StatusBar.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/vendor/react-prop-types");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const { connect } = require("devtools/client/shared/redux/visibility-handler-connect");
const { PluralForm } = require("devtools/shared/plural-form");
const Actions = require("../actions/index");
const {
  getDisplayedRequestsSummary,
  getDisplayedTimingMarker,
} = require("../selectors/index");
const {
  getFormattedSize,
  getFormattedTime,
} = require("../utils/format-utils");
const { L10N } = require("../utils/l10n");

const { button, div } = dom;

const REQUESTS_COUNT_EMPTY = L10N.getStr("networkMenu.summary.requestsCountEmpty");
const TOOLTIP_PERF = L10N.getStr("networkMenu.summary.tooltip.perf");
const TOOLTIP_REQUESTS_COUNT = L10N.getStr("networkMenu.summary.tooltip.requestsCount");
const TOOLTIP_TRANSFERRED = L10N.getStr("networkMenu.summary.tooltip.transferred");
const TOOLTIP_FINISH = L10N.getStr("networkMenu.summary.tooltip.finish");
const TOOLTIP_DOM_CONTENT_LOADED =
        L10N.getStr("networkMenu.summary.tooltip.domContentLoaded");
const TOOLTIP_LOAD = L10N.getStr("networkMenu.summary.tooltip.load");

function StatusBar({ summary, openStatistics, timingMarkers }) {
  let { count, contentSize, transferredSize, millis } = summary;
  let {
    DOMContentLoaded,
    load,
  } = timingMarkers;

  let countText = count === 0 ? REQUESTS_COUNT_EMPTY :
    PluralForm.get(count,
      L10N.getStr("networkMenu.summary.requestsCount2")).replace("#1", count);
  let transferText = L10N.getFormatStrWithNumbers("networkMenu.summary.transferred",
    getFormattedSize(contentSize), getFormattedSize(transferredSize));
  let finishText = L10N.getFormatStrWithNumbers("networkMenu.summary.finish",
    getFormattedTime(millis));

  return (
    div({ className: "devtools-toolbar devtools-toolbar-bottom" },
      button({
        className: "devtools-button requests-list-network-summary-button",
        title: TOOLTIP_PERF,
        onClick: openStatistics,
      },
        div({ className: "summary-info-icon" }),
      ),
      div({
        className: "status-bar-label requests-list-network-summary-count",
        title: TOOLTIP_REQUESTS_COUNT,
      }, countText),
      count !== 0 &&
        div({
          className: "status-bar-label requests-list-network-summary-transfer",
          title: TOOLTIP_TRANSFERRED,
        }, transferText),
      count !== 0 &&
        div({
          className: "status-bar-label requests-list-network-summary-finish",
          title: TOOLTIP_FINISH,
        }, finishText),
      DOMContentLoaded > -1 &&
        div({
          className: "status-bar-label dom-content-loaded",
          title: TOOLTIP_DOM_CONTENT_LOADED,
        }, `DOMContentLoaded: ${getFormattedTime(DOMContentLoaded)}`),
      load > -1 &&
        div({
          className: "status-bar-label load",
          title: TOOLTIP_LOAD,
        }, `load: ${getFormattedTime(load)}`),
    )
  );
}

StatusBar.displayName = "StatusBar";

StatusBar.propTypes = {
  connector: PropTypes.object.isRequired,
  openStatistics: PropTypes.func.isRequired,
  summary: PropTypes.object.isRequired,
  timingMarkers: PropTypes.object.isRequired,
};

module.exports = connect(
  (state) => ({
    summary: getDisplayedRequestsSummary(state),
    timingMarkers: {
      DOMContentLoaded:
        getDisplayedTimingMarker(state, "firstDocumentDOMContentLoadedTimestamp"),
      load: getDisplayedTimingMarker(state, "firstDocumentLoadTimestamp"),
    },
  }),
  (dispatch, props) => ({
    openStatistics: () => dispatch(Actions.openStatistics(props.connector, true)),
  }),
)(StatusBar);
PK
!<�ؾW��Pchrome/devtools/modules/devtools/client/netmonitor/src/components/TabboxPanel.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 } = require("devtools/client/shared/vendor/react");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const { L10N } = require("../utils/l10n");
const { PANELS } = require("../constants");

// Components
const Tabbar = createFactory(require("devtools/client/shared/components/tabs/TabBar"));
const TabPanel = createFactory(require("devtools/client/shared/components/tabs/Tabs").TabPanel);
const CookiesPanel = createFactory(require("./CookiesPanel"));
const HeadersPanel = createFactory(require("./HeadersPanel"));
const ParamsPanel = createFactory(require("./ParamsPanel"));
const ResponsePanel = createFactory(require("./ResponsePanel"));
const SecurityPanel = createFactory(require("./SecurityPanel"));
const StackTracePanel = createFactory(require("./StackTracePanel"));
const TimingsPanel = createFactory(require("./TimingsPanel"));

const COOKIES_TITLE = L10N.getStr("netmonitor.tab.cookies");
const HEADERS_TITLE = L10N.getStr("netmonitor.tab.headers");
const PARAMS_TITLE = L10N.getStr("netmonitor.tab.params");
const RESPONSE_TITLE = L10N.getStr("netmonitor.tab.response");
const SECURITY_TITLE = L10N.getStr("netmonitor.tab.security");
const STACK_TRACE_TITLE = L10N.getStr("netmonitor.tab.stackTrace");
const TIMINGS_TITLE = L10N.getStr("netmonitor.tab.timings");

/**
 * Tabbox panel component
 * Display the network request details
 */
function TabboxPanel({
  activeTabId,
  cloneSelectedRequest = () => {},
  connector,
  openLink,
  request,
  selectTab,
  sourceMapService,
}) {
  if (!request) {
    return null;
  }

  return (
    Tabbar({
      activeTabId,
      menuDocument: window.parent.document,
      onSelect: selectTab,
      renderOnlySelected: true,
      showAllTabsMenu: true,
    },
      TabPanel({
        id: PANELS.HEADERS,
        title: HEADERS_TITLE,
      },
        HeadersPanel({
          cloneSelectedRequest,
          connector,
          openLink,
          request,
        }),
      ),
      TabPanel({
        id: PANELS.COOKIES,
        title: COOKIES_TITLE,
      },
        CookiesPanel({
          connector,
          openLink,
          request,
        }),
      ),
      TabPanel({
        id: PANELS.PARAMS,
        title: PARAMS_TITLE,
      },
        ParamsPanel({ connector, openLink, request }),
      ),
      TabPanel({
        id: PANELS.RESPONSE,
        title: RESPONSE_TITLE,
      },
        ResponsePanel({ request, openLink, connector }),
      ),
      TabPanel({
        id: PANELS.TIMINGS,
        title: TIMINGS_TITLE,
      },
        TimingsPanel({
          connector,
          request,
        }),
      ),
      request.cause && request.cause.stacktraceAvailable &&
      TabPanel({
        id: PANELS.STACK_TRACE,
        title: STACK_TRACE_TITLE,
      },
        StackTracePanel({ connector, openLink, request, sourceMapService }),
      ),
      request.securityState && request.securityState !== "insecure" &&
      TabPanel({
        id: PANELS.SECURITY,
        title: SECURITY_TITLE,
      },
        SecurityPanel({
          connector,
          openLink,
          request,
        }),
      ),
    )
  );
}

TabboxPanel.displayName = "TabboxPanel";

TabboxPanel.propTypes = {
  activeTabId: PropTypes.string,
  cloneSelectedRequest: PropTypes.func,
  connector: PropTypes.object.isRequired,
  openLink: PropTypes.func,
  request: PropTypes.object,
  selectTab: PropTypes.func.isRequired,
  sourceMapService: PropTypes.object,
};

module.exports = TabboxPanel;
PK
!<T%N���Qchrome/devtools/modules/devtools/client/netmonitor/src/components/TimingsPanel.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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 { L10N } = require("../utils/l10n");
const { getNetMonitorTimingsURL } = require("../utils/mdn-utils");
const { fetchNetworkUpdatePacket } = require("../utils/request-utils");
const { TIMING_KEYS } = require("../constants");

// Components
const MDNLink = require("./MdnLink");

const { div, span } = dom;

const TIMINGS_END_PADDING = "80px";

/**
 * Timings panel component
 * Display timeline bars that shows the total wait time for various stages
 */
class TimingsPanel extends Component {
  static get propTypes() {
    return {
      connector: PropTypes.object.isRequired,
      request: PropTypes.object.isRequired,
    };
  }

  componentDidMount() {
    let { connector, request } = this.props;
    fetchNetworkUpdatePacket(connector.requestData, request, ["eventTimings"]);
  }

  componentWillReceiveProps(nextProps) {
    let { connector, request } = nextProps;
    fetchNetworkUpdatePacket(connector.requestData, request, ["eventTimings"]);
  }

  render() {
    const {
      eventTimings,
      totalTime,
    } = this.props.request;

    if (!eventTimings) {
      return null;
    }

    const { timings, offsets } = eventTimings;
    const timelines = TIMING_KEYS.map((type, idx) => {
      // Determine the relative offset for each timings box. For example, the
      // offset of third timings box will be 0 + blocked offset + dns offset
      // If offsets sent from the backend aren't available calculate it
      // from the timing info.
      let offset = offsets ? offsets[type] : TIMING_KEYS.slice(0, idx)
        .reduce((acc, cur) => (acc + timings[cur] || 0), 0);

      const offsetScale = offset / totalTime || 0;
      const timelineScale = timings[type] / totalTime || 0;

      return div({
        key: type,
        id: `timings-summary-${type}`,
        className: "tabpanel-summary-container timings-container",
      },
        span({ className: "tabpanel-summary-label timings-label" },
          L10N.getStr(`netmonitor.timings.${type}`)
        ),
        div({ className: "requests-list-timings-container" },
          span({
            className: "requests-list-timings-offset",
            style: {
              width: `calc(${offsetScale} * (100% - ${TIMINGS_END_PADDING})`,
            },
          }),
          span({
            className: `requests-list-timings-box ${type}`,
            style: {
              width: `calc(${timelineScale} * (100% - ${TIMINGS_END_PADDING}))`,
            },
          }),
          span({ className: "requests-list-timings-total" },
            L10N.getFormatStr("networkMenu.totalMS", timings[type])
          )
        ),
      );
    });

    return (
      div({ className: "panel-container" },
        timelines,
        MDNLink({
          url: getNetMonitorTimingsURL(),
        }),
      )
    );
  }
}

module.exports = TimingsPanel;
PK
!<^?)�Y+Y+Lchrome/devtools/modules/devtools/client/netmonitor/src/components/Toolbar.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Services = require("Services");
const { Component, createFactory } = 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");
const { connect } = require("devtools/client/shared/redux/visibility-handler-connect");

const Actions = require("../actions/index");
const { FILTER_SEARCH_DELAY, FILTER_TAGS } = require("../constants");
const {
  getRecordingState,
  getTypeFilteredRequests,
  isNetworkDetailsToggleButtonDisabled,
} = require("../selectors/index");
const { autocompleteProvider } = require("../utils/filter-autocomplete-provider");
const { L10N } = require("../utils/l10n");
const { fetchNetworkUpdatePacket } = require("../utils/request-utils");

// Components
const SearchBox = createFactory(require("devtools/client/shared/components/SearchBox"));

const { button, div, input, label, span } = dom;

// Localization
const COLLAPSE_DETAILS_PANE = L10N.getStr("collapseDetailsPane");
const EXPAND_DETAILS_PANE = L10N.getStr("expandDetailsPane");
const SEARCH_KEY_SHORTCUT = L10N.getStr("netmonitor.toolbar.filterFreetext.key");
const SEARCH_PLACE_HOLDER = L10N.getStr("netmonitor.toolbar.filterFreetext.label");
const TOOLBAR_CLEAR = L10N.getStr("netmonitor.toolbar.clear");
const TOOLBAR_TOGGLE_RECORDING = L10N.getStr("netmonitor.toolbar.toggleRecording");

// Preferences
const DEVTOOLS_DISABLE_CACHE_PREF = "devtools.cache.disabled";
const DEVTOOLS_ENABLE_PERSISTENT_LOG_PREF = "devtools.netmonitor.persistlog";
const TOOLBAR_FILTER_LABELS = FILTER_TAGS.concat("all").reduce((o, tag) =>
  Object.assign(o, { [tag]: L10N.getStr(`netmonitor.toolbar.filter.${tag}`) }), {});
const ENABLE_PERSISTENT_LOGS_TOOLTIP =
  L10N.getStr("netmonitor.toolbar.enablePersistentLogs.tooltip");
const ENABLE_PERSISTENT_LOGS_LABEL =
  L10N.getStr("netmonitor.toolbar.enablePersistentLogs.label");
const DISABLE_CACHE_TOOLTIP = L10N.getStr("netmonitor.toolbar.disableCache.tooltip");
const DISABLE_CACHE_LABEL = L10N.getStr("netmonitor.toolbar.disableCache.label");

/**
 * Network monitor toolbar component.
 *
 * Toolbar contains a set of useful tools to control network requests
 * as well as set of filters for filtering the content.
 */
class Toolbar extends Component {
  static get propTypes() {
    return {
      connector: PropTypes.object.isRequired,
      toggleRecording: PropTypes.func.isRequired,
      recording: PropTypes.bool.isRequired,
      clearRequests: PropTypes.func.isRequired,
      requestFilterTypes: PropTypes.object.isRequired,
      setRequestFilterText: PropTypes.func.isRequired,
      networkDetailsToggleDisabled: PropTypes.bool.isRequired,
      networkDetailsOpen: PropTypes.bool.isRequired,
      toggleNetworkDetails: PropTypes.func.isRequired,
      enablePersistentLogs: PropTypes.func.isRequired,
      togglePersistentLogs: PropTypes.func.isRequired,
      persistentLogsEnabled: PropTypes.bool.isRequired,
      disableBrowserCache: PropTypes.func.isRequired,
      toggleBrowserCache: PropTypes.func.isRequired,
      browserCacheDisabled: PropTypes.bool.isRequired,
      toggleRequestFilterType: PropTypes.func.isRequired,
      filteredRequests: PropTypes.array.isRequired,
    };
  }

  constructor(props) {
    super(props);
    this.autocompleteProvider = this.autocompleteProvider.bind(this);
    this.onSearchBoxFocus = this.onSearchBoxFocus.bind(this);
    this.toggleRequestFilterType = this.toggleRequestFilterType.bind(this);
    this.updatePersistentLogsEnabled = this.updatePersistentLogsEnabled.bind(this);
    this.updateBrowserCacheDisabled = this.updateBrowserCacheDisabled.bind(this);
  }

  componentDidMount() {
    Services.prefs.addObserver(DEVTOOLS_ENABLE_PERSISTENT_LOG_PREF,
                               this.updatePersistentLogsEnabled);
    Services.prefs.addObserver(DEVTOOLS_DISABLE_CACHE_PREF,
                               this.updateBrowserCacheDisabled);
  }

  shouldComponentUpdate(nextProps) {
    return this.props.networkDetailsOpen !== nextProps.networkDetailsOpen
    || this.props.networkDetailsToggleDisabled !== nextProps.networkDetailsToggleDisabled
    || this.props.persistentLogsEnabled !== nextProps.persistentLogsEnabled
    || this.props.browserCacheDisabled !== nextProps.browserCacheDisabled
    || this.props.recording !== nextProps.recording
    || !Object.is(this.props.requestFilterTypes, nextProps.requestFilterTypes)

    // Filtered requests are useful only when searchbox is focused
    || !!(this.refs.searchbox && this.refs.searchbox.focused);
  }

  componentWillUnmount() {
    Services.prefs.removeObserver(DEVTOOLS_ENABLE_PERSISTENT_LOG_PREF,
                                  this.updatePersistentLogsEnabled);
    Services.prefs.removeObserver(DEVTOOLS_DISABLE_CACHE_PREF,
                                  this.updateBrowserCacheDisabled);
  }

  toggleRequestFilterType(evt) {
    if (evt.type === "keydown" && (evt.key !== "" || evt.key !== "Enter")) {
      return;
    }
    this.props.toggleRequestFilterType(evt.target.dataset.key);
  }

  updatePersistentLogsEnabled() {
    this.props.enablePersistentLogs(
      Services.prefs.getBoolPref(DEVTOOLS_ENABLE_PERSISTENT_LOG_PREF));
  }

  updateBrowserCacheDisabled() {
    this.props.disableBrowserCache(
      Services.prefs.getBoolPref(DEVTOOLS_DISABLE_CACHE_PREF));
  }

  autocompleteProvider(filter) {
    return autocompleteProvider(filter, this.props.filteredRequests);
  }

  onSearchBoxFocus() {
    let { connector, filteredRequests } = this.props;

    // Fetch responseCookies & responseHeaders for building autocomplete list
    filteredRequests.forEach((request) => {
      fetchNetworkUpdatePacket(connector.requestData, request, [
        "responseCookies",
        "responseHeaders",
      ]);
    });
  }

  render() {
    let {
      toggleRecording,
      clearRequests,
      requestFilterTypes,
      setRequestFilterText,
      networkDetailsToggleDisabled,
      networkDetailsOpen,
      toggleNetworkDetails,
      togglePersistentLogs,
      persistentLogsEnabled,
      toggleBrowserCache,
      browserCacheDisabled,
      recording,
    } = this.props;

    // Render list of filter-buttons.
    let buttons = Object.entries(requestFilterTypes).map(([type, checked]) => {
      let classList = ["devtools-button", `requests-list-filter-${type}-button`];
      checked && classList.push("checked");

      return (
        button({
          className: classList.join(" "),
          key: type,
          onClick: this.toggleRequestFilterType,
          onKeyDown: this.toggleRequestFilterType,
          "aria-pressed": checked,
          "data-key": type,
        },
          TOOLBAR_FILTER_LABELS[type]
        )
      );
    });

    // Calculate class-list for toggle recording button. The button
    // has two states: pause/play.
    let toggleRecordingButtonClass = [
      "devtools-button",
      "requests-list-pause-button",
      recording ? "devtools-pause-icon" : "devtools-play-icon",
    ].join(" ");

    // Detail toggle button
    let toggleDetailButtonClassList = [
      "network-details-panel-toggle",
      "devtools-button",
    ];

    if (!networkDetailsOpen) {
      toggleDetailButtonClassList.push("pane-collapsed");
    }
    let toggleDetailButtonClass = toggleDetailButtonClassList.join(" ");
    let toggleDetailButtonTitle = networkDetailsOpen ? COLLAPSE_DETAILS_PANE :
      EXPAND_DETAILS_PANE;

    // Render the entire toolbar.
    return (
      span({ className: "devtools-toolbar devtools-toolbar-container" },
        span({ className: "devtools-toolbar-group" },
          button({
            className: toggleRecordingButtonClass,
            title: TOOLBAR_TOGGLE_RECORDING,
            onClick: toggleRecording,
          }),
          button({
            className: "devtools-button devtools-clear-icon requests-list-clear-button",
            title: TOOLBAR_CLEAR,
            onClick: clearRequests,
          }),
          div({ className: "requests-list-filter-buttons" }, buttons),
          label(
            {
              className: "devtools-checkbox-label",
              title: ENABLE_PERSISTENT_LOGS_TOOLTIP,
            },
            input({
              id: "devtools-persistlog-checkbox",
              className: "devtools-checkbox",
              type: "checkbox",
              checked: persistentLogsEnabled,
              onChange: togglePersistentLogs,
            }),
            ENABLE_PERSISTENT_LOGS_LABEL
          ),
          label(
            {
              className: "devtools-checkbox-label",
              title: DISABLE_CACHE_TOOLTIP,
            },
            input({
              id: "devtools-cache-checkbox",
              className: "devtools-checkbox",
              type: "checkbox",
              checked: browserCacheDisabled,
              onChange: toggleBrowserCache,
            }),
            DISABLE_CACHE_LABEL,
          ),
        ),
        span({ className: "devtools-toolbar-group" },
          SearchBox({
            delay: FILTER_SEARCH_DELAY,
            keyShortcut: SEARCH_KEY_SHORTCUT,
            placeholder: SEARCH_PLACE_HOLDER,
            type: "filter",
            ref: "searchbox",
            onChange: setRequestFilterText,
            onFocus: this.onSearchBoxFocus,
            autocompleteProvider: this.autocompleteProvider,
          }),
          button({
            className: toggleDetailButtonClass,
            title: toggleDetailButtonTitle,
            disabled: networkDetailsToggleDisabled,
            tabIndex: "0",
            onClick: toggleNetworkDetails,
          }),
        )
      )
    );
  }
}

module.exports = connect(
  (state) => ({
    browserCacheDisabled: state.ui.browserCacheDisabled,
    filteredRequests: getTypeFilteredRequests(state),
    networkDetailsToggleDisabled: isNetworkDetailsToggleButtonDisabled(state),
    networkDetailsOpen: state.ui.networkDetailsOpen,
    persistentLogsEnabled: state.ui.persistentLogsEnabled,
    recording: getRecordingState(state),
    requestFilterTypes: state.filters.requestFilterTypes,
  }),
  (dispatch) => ({
    clearRequests: () => dispatch(Actions.clearRequests()),
    disableBrowserCache: (disabled) => dispatch(Actions.disableBrowserCache(disabled)),
    enablePersistentLogs: (enabled) => dispatch(Actions.enablePersistentLogs(enabled)),
    setRequestFilterText: (text) => dispatch(Actions.setRequestFilterText(text)),
    toggleBrowserCache: () => dispatch(Actions.toggleBrowserCache()),
    toggleNetworkDetails: () => dispatch(Actions.toggleNetworkDetails()),
    toggleRecording: () => dispatch(Actions.toggleRecording()),
    togglePersistentLogs: () => dispatch(Actions.togglePersistentLogs()),
    toggleRequestFilterType: (type) => dispatch(Actions.toggleRequestFilterType(type)),
  }),
)(Toolbar);
PK
!<�U�G00Uchrome/devtools/modules/devtools/client/netmonitor/src/connector/firefox-connector.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Services = require("Services");
const { ACTIVITY_TYPE, EVENTS } = require("../constants");
const FirefoxDataProvider = require("./firefox-data-provider");
const { getDisplayedTimingMarker } = require("../selectors/index");

// To be removed once FF60 is deprecated
loader.lazyRequireGetter(this, "TimelineFront", "devtools/shared/fronts/timeline", true);

class FirefoxConnector {
  constructor() {
    // Public methods
    this.connect = this.connect.bind(this);
    this.disconnect = this.disconnect.bind(this);
    this.willNavigate = this.willNavigate.bind(this);
    this.navigate = this.navigate.bind(this);
    this.displayCachedEvents = this.displayCachedEvents.bind(this);
    this.onDocLoadingMarker = this.onDocLoadingMarker.bind(this);
    this.onDocEvent = this.onDocEvent.bind(this);
    this.sendHTTPRequest = this.sendHTTPRequest.bind(this);
    this.setPreferences = this.setPreferences.bind(this);
    this.triggerActivity = this.triggerActivity.bind(this);
    this.getTabTarget = this.getTabTarget.bind(this);
    this.viewSourceInDebugger = this.viewSourceInDebugger.bind(this);
    this.requestData = this.requestData.bind(this);
    this.getTimingMarker = this.getTimingMarker.bind(this);

    // Internals
    this.getLongString = this.getLongString.bind(this);
    this.getNetworkRequest = this.getNetworkRequest.bind(this);
  }

  async connect(connection, actions, getState) {
    this.actions = actions;
    this.getState = getState;
    this.tabTarget = connection.tabConnection.tabTarget;
    this.toolbox = connection.toolbox;
    this.panel = connection.panel;

    this.webConsoleClient = this.tabTarget.activeConsole;

    this.dataProvider = new FirefoxDataProvider({
      webConsoleClient: this.webConsoleClient,
      actions: this.actions,
    });

    await this.addListeners();

    // Listener for `will-navigate` event is (un)registered outside
    // of the `addListeners` and `removeListeners` methods since
    // these are used to pause/resume the connector.
    // Paused network panel should be automatically resumed when page
    // reload, so `will-navigate` listener needs to be there all the time.
    this.tabTarget.on("will-navigate", this.willNavigate);
    this.tabTarget.on("navigate", this.navigate);

    this.displayCachedEvents();
  }

  async disconnect() {
    this.actions.batchReset();

    await this.removeListeners();

    if (this.tabTarget) {
      this.tabTarget.off("will-navigate");
      this.tabTarget = null;
    }

    this.webConsoleClient = null;
    this.dataProvider = null;
    this.panel = null;
  }

  async pause() {
    await this.removeListeners();
  }

  async resume() {
    await this.addListeners();
  }

  async addListeners() {
    this.tabTarget.on("close", this.disconnect);
    this.webConsoleClient.on("networkEvent",
      this.dataProvider.onNetworkEvent);
    this.webConsoleClient.on("networkEventUpdate",
      this.dataProvider.onNetworkEventUpdate);
    this.webConsoleClient.on("documentEvent", this.onDocEvent);

    // With FF60+ console actor supports listening to document events like
    // DOMContentLoaded and load. We used to query Timeline actor, but it was too CPU
    // intensive.
    let { startedListeners } = await this.webConsoleClient.startListeners(
      ["DocumentEvents"]);
    // Allows to know if we are on FF60 and support these events.
    let supportsDocEvents = startedListeners.includes("DocumentEvents");

    // Don't start up waiting for timeline markers if the server isn't
    // recent enough (<FF45) to emit the markers we're interested in.
    if (!supportsDocEvents && !this.timelineFront &&
        this.tabTarget.getTrait("documentLoadingMarkers")) {
      this.timelineFront = new TimelineFront(this.tabTarget.client, this.tabTarget.form);
      this.timelineFront.on("doc-loading", this.onDocLoadingMarker);
      await this.timelineFront.start({ withDocLoadingEvents: true });
    }
  }

  async removeListeners() {
    if (this.tabTarget) {
      this.tabTarget.off("close");
    }
    if (this.timelineFront) {
      this.timelineFront.off("doc-loading", this.onDocLoadingMarker);
      await this.timelineFront.destroy();
      this.timelineFront = null;
    }
    if (this.webConsoleClient) {
      this.webConsoleClient.off("networkEvent");
      this.webConsoleClient.off("networkEventUpdate");
      this.webConsoleClient.off("docEvent");
    }
  }

  enableActions(enable) {
    this.dataProvider.enableActions(enable);
  }

  willNavigate() {
    if (!Services.prefs.getBoolPref("devtools.netmonitor.persistlog")) {
      this.actions.batchReset();
      this.actions.clearRequests();
    } else {
      // If the log is persistent, just clear all accumulated timing markers.
      this.actions.clearTimingMarkers();
    }

    // Resume is done automatically on page reload/navigation.
    let state = this.getState();
    if (!state.requests.recording) {
      this.actions.toggleRecording();
    }
  }

  navigate() {
    if (this.dataProvider.isPayloadQueueEmpty()) {
      this.onReloaded();
      return;
    }
    let listener = () => {
      if (this.dataProvider && !this.dataProvider.isPayloadQueueEmpty()) {
        return;
      }
      window.off(EVENTS.PAYLOAD_READY, listener);
      // Netmonitor may already be destroyed,
      // so do not try to notify the listeners
      if (this.dataProvider) {
        this.onReloaded();
      }
    };
    window.on(EVENTS.PAYLOAD_READY, listener);
  }

  onReloaded() {
    if (this.panel) {
      this.panel.emit("reloaded");
    }
  }

  /**
   * Display any network events already in the cache.
   */
  displayCachedEvents() {
    for (let networkInfo of this.webConsoleClient.getNetworkEvents()) {
      // First add the request to the timeline.
      this.dataProvider.onNetworkEvent("networkEvent", networkInfo);
      // Then replay any updates already received.
      for (let updateType of networkInfo.updates) {
        this.dataProvider.onNetworkEventUpdate("networkEventUpdate", {
          packet: { updateType },
          networkInfo,
        });
      }
    }
  }

  /**
   * The "DOMContentLoaded" and "Load" events sent by the timeline actor.
   *
   * To be removed once FF60 is deprecated.
   *
   * @param {object} marker
   */
  onDocLoadingMarker(marker) {
    // Translate marker into event similar to newer "docEvent" event sent by the console
    // actor
    let event = {
      name: marker.name == "document::DOMContentLoaded" ?
            "dom-interactive" : "dom-complete",
      time: marker.unixTime / 1000
    };
    this.actions.addTimingMarker(event);
    window.emit(EVENTS.TIMELINE_EVENT, event);
  }

  /**
   * The "DOMContentLoaded" and "Load" events sent by the console actor.
   *
   * Only used by FF60+.
   *
   * @param {object} marker
   */
  onDocEvent(type, event) {
    this.actions.addTimingMarker(event);
    window.emit(EVENTS.TIMELINE_EVENT, event);
  }

  /**
   * Send a HTTP request data payload
   *
   * @param {object} data data payload would like to sent to backend
   * @param {function} callback callback will be invoked after the request finished
   */
  sendHTTPRequest(data, callback) {
    this.webConsoleClient.sendHTTPRequest(data, callback);
  }

  /**
   * Set network preferences to control network flow
   *
   * @param {object} request request payload would like to sent to backend
   * @param {function} callback callback will be invoked after the request finished
   */
  setPreferences(request, callback) {
    this.webConsoleClient.setPreferences(request, callback);
  }

  /**
   * Triggers a specific "activity" to be performed by the frontend.
   * This can be, for example, triggering reloads or enabling/disabling cache.
   *
   * @param {number} type The activity type. See the ACTIVITY_TYPE const.
   * @return {object} A promise resolved once the activity finishes and the frontend
   *                  is back into "standby" mode.
   */
  triggerActivity(type) {
    // Puts the frontend into "standby" (when there's no particular activity).
    let standBy = () => {
      this.currentActivity = ACTIVITY_TYPE.NONE;
    };

    // Waits for a series of "navigation start" and "navigation stop" events.
    let waitForNavigation = () => {
      return new Promise((resolve) => {
        this.tabTarget.once("will-navigate", () => {
          this.tabTarget.once("navigate", () => {
            resolve();
          });
        });
      });
    };

    // Reconfigures the tab, optionally triggering a reload.
    let reconfigureTab = (options) => {
      return new Promise((resolve) => {
        this.tabTarget.activeTab.reconfigure(options, resolve);
      });
    };

    // Reconfigures the tab and waits for the target to finish navigating.
    let reconfigureTabAndWaitForNavigation = (options) => {
      options.performReload = true;
      let navigationFinished = waitForNavigation();
      return reconfigureTab(options).then(() => navigationFinished);
    };
    switch (type) {
      case ACTIVITY_TYPE.RELOAD.WITH_CACHE_DEFAULT:
        return reconfigureTabAndWaitForNavigation({}).then(standBy);
      case ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED:
        this.currentActivity = ACTIVITY_TYPE.ENABLE_CACHE;
        this.tabTarget.once("will-navigate", () => {
          this.currentActivity = type;
        });
        return reconfigureTabAndWaitForNavigation({
          cacheDisabled: false,
          performReload: true,
        }).then(standBy);
      case ACTIVITY_TYPE.RELOAD.WITH_CACHE_DISABLED:
        this.currentActivity = ACTIVITY_TYPE.DISABLE_CACHE;
        this.tabTarget.once("will-navigate", () => {
          this.currentActivity = type;
        });
        return reconfigureTabAndWaitForNavigation({
          cacheDisabled: true,
          performReload: true,
        }).then(standBy);
      case ACTIVITY_TYPE.ENABLE_CACHE:
        this.currentActivity = type;
        return reconfigureTab({
          cacheDisabled: false,
          performReload: false,
        }).then(standBy);
      case ACTIVITY_TYPE.DISABLE_CACHE:
        this.currentActivity = type;
        return reconfigureTab({
          cacheDisabled: true,
          performReload: false,
        }).then(standBy);
    }
    this.currentActivity = ACTIVITY_TYPE.NONE;
    return Promise.reject(new Error("Invalid activity type"));
  }

  /**
   * Fetches the network information packet from actor server
   *
   * @param {string} id request id
   * @return {object} networkInfo data packet
   */
  getNetworkRequest(id) {
    return this.dataProvider.getNetworkRequest(id);
  }

  /**
   * Fetches the full text of a LongString.
   *
   * @param {object|string} stringGrip
   *        The long string grip containing the corresponding actor.
   *        If you pass in a plain string (by accident or because you're lazy),
   *        then a promise of the same string is simply returned.
   * @return {object}
   *         A promise that is resolved when the full string contents
   *         are available, or rejected if something goes wrong.
   */
  getLongString(stringGrip) {
    return this.dataProvider.getLongString(stringGrip);
  }

  /**
   * Getter that access tab target instance.
   * @return {object} browser tab target instance
   */
  getTabTarget() {
    return this.tabTarget;
  }

  /**
   * Open a given source in Debugger
   * @param {string} sourceURL source url
   * @param {number} sourceLine source line number
   */
  viewSourceInDebugger(sourceURL, sourceLine) {
    if (this.toolbox) {
      this.toolbox.viewSourceInDebugger(sourceURL, sourceLine);
    }
  }

  /**
   * Fetch networkEventUpdate websocket message from back-end when
   * data provider is connected.
   * @param {object} request network request instance
   * @param {string} type NetworkEventUpdate type
   */
  requestData(request, type) {
    return this.dataProvider.requestData(request, type);
  }

  getTimingMarker(name) {
    let state = this.getState();
    return getDisplayedTimingMarker(state, name);
  }
}

module.exports = new FirefoxConnector();
PK
!<d
TVMMYchrome/devtools/modules/devtools/client/netmonitor/src/connector/firefox-data-provider.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 block-scoped-var */

"use strict";

const { EVENTS } = require("../constants");
const { CurlUtils } = require("devtools/client/shared/curl");
const { fetchHeaders } = require("../utils/request-utils");

/**
 * This object is responsible for fetching additional HTTP
 * data from the backend over RDP protocol.
 *
 * The object also keeps track of RDP requests in-progress,
 * so it's possible to determine whether all has been fetched
 * or not.
 */
class FirefoxDataProvider {
  constructor({webConsoleClient, actions}) {
    // Options
    this.webConsoleClient = webConsoleClient;
    this.actions = actions;
    this.actionsEnabled = true;

    // Internal properties
    this.payloadQueue = new Map();

    // Map[key string => Promise] used by `requestData` to prevent requesting the same
    // request data twice.
    this.lazyRequestData = new Map();

    // Fetching data from the backend
    this.getLongString = this.getLongString.bind(this);

    // Event handlers
    this.onNetworkEvent = this.onNetworkEvent.bind(this);
    this.onNetworkEventUpdate = this.onNetworkEventUpdate.bind(this);
  }

  /**
   * Enable/disable firing redux actions (enabled by default).
   *
   * @param {boolean} enable Set to true to fire actions.
   */
  enableActions(enable) {
    this.actionsEnabled = enable;
  }

  /**
   * Add a new network request to application state.
   *
   * @param {string} id request id
   * @param {object} data data payload will be added to application state
   */
  async addRequest(id, data) {
    let {
      method,
      url,
      isXHR,
      cause,
      startedDateTime,
      fromCache,
      fromServiceWorker,
    } = data;

    if (this.actionsEnabled && this.actions.addRequest) {
      await this.actions.addRequest(id, {
        // Convert the received date/time string to a unix timestamp.
        startedMillis: Date.parse(startedDateTime),
        method,
        url,
        isXHR,
        cause,

        // Compatibility code to support Firefox 58 and earlier that always
        // send stack-trace immediately on networkEvent message.
        // FF59+ supports fetching the traces lazily via requestData.
        stacktrace: cause.stacktrace,

        fromCache,
        fromServiceWorker,
      }, true);
    }

    emit(EVENTS.REQUEST_ADDED, id);
  }

  /**
   * Update a network request if it already exists in application state.
   *
   * @param {string} id request id
   * @param {object} data data payload will be updated to application state
   */
  async updateRequest(id, data) {
    let {
      responseContent,
      responseCookies,
      responseHeaders,
      requestCookies,
      requestHeaders,
      requestPostData,
    } = data;

    // fetch request detail contents in parallel
    let [
      responseContentObj,
      requestHeadersObj,
      responseHeadersObj,
      postDataObj,
      requestCookiesObj,
      responseCookiesObj,
    ] = await Promise.all([
      this.fetchResponseContent(responseContent),
      this.fetchRequestHeaders(requestHeaders),
      this.fetchResponseHeaders(responseHeaders),
      this.fetchPostData(requestPostData),
      this.fetchRequestCookies(requestCookies),
      this.fetchResponseCookies(responseCookies),
    ]);

    let payload = Object.assign({},
      data,
      responseContentObj,
      requestHeadersObj,
      responseHeadersObj,
      postDataObj,
      requestCookiesObj,
      responseCookiesObj
    );

    if (this.actionsEnabled && this.actions.updateRequest) {
      await this.actions.updateRequest(id, payload, true);
    }

    return payload;
  }

  async fetchResponseContent(responseContent) {
    let payload = {};
    if (responseContent && responseContent.content) {
      let { text } = responseContent.content;
      let response = await this.getLongString(text);
      responseContent.content.text = response;
      payload.responseContent = responseContent;
    }
    return payload;
  }

  async fetchRequestHeaders(requestHeaders) {
    let payload = {};
    if (requestHeaders && requestHeaders.headers && requestHeaders.headers.length) {
      let headers = await fetchHeaders(requestHeaders, this.getLongString);
      if (headers) {
        payload.requestHeaders = headers;
      }
    }
    return payload;
  }

  async fetchResponseHeaders(responseHeaders) {
    let payload = {};
    if (responseHeaders && responseHeaders.headers && responseHeaders.headers.length) {
      let headers = await fetchHeaders(responseHeaders, this.getLongString);
      if (headers) {
        payload.responseHeaders = headers;
      }
    }
    return payload;
  }

  async fetchPostData(requestPostData) {
    let payload = {};
    if (requestPostData && requestPostData.postData) {
      let { text } = requestPostData.postData;
      let postData = await this.getLongString(text);
      const headers = CurlUtils.getHeadersFromMultipartText(postData);

      // Calculate total header size and don't forget to include
      // two new-line characters at the end.
      const headersSize = headers.reduce((acc, { name, value }) => {
        return acc + name.length + value.length + 2;
      }, 0);

      requestPostData.postData.text = postData;
      payload.requestPostData = Object.assign({}, requestPostData);
      payload.requestHeadersFromUploadStream = { headers, headersSize };
    }
    return payload;
  }

  async fetchRequestCookies(requestCookies) {
    let payload = {};
    if (requestCookies) {
      let reqCookies = [];
      // request store cookies in requestCookies or requestCookies.cookies
      let cookies = requestCookies.cookies ?
        requestCookies.cookies : requestCookies;
      // make sure cookies is iterable
      if (typeof cookies[Symbol.iterator] === "function") {
        for (let cookie of cookies) {
          reqCookies.push(Object.assign({}, cookie, {
            value: await this.getLongString(cookie.value),
          }));
        }
        if (reqCookies.length) {
          payload.requestCookies = reqCookies;
        }
      }
    }
    return payload;
  }

  async fetchResponseCookies(responseCookies) {
    let payload = {};
    if (responseCookies) {
      let resCookies = [];
      // response store cookies in responseCookies or responseCookies.cookies
      let cookies = responseCookies.cookies ?
        responseCookies.cookies : responseCookies;
      // make sure cookies is iterable
      if (typeof cookies[Symbol.iterator] === "function") {
        for (let cookie of cookies) {
          resCookies.push(Object.assign({}, cookie, {
            value: await this.getLongString(cookie.value),
          }));
        }
        if (resCookies.length) {
          payload.responseCookies = resCookies;
        }
      }
    }
    return payload;
  }

  /**
   * Public API used by the Toolbox: Tells if there is still any pending request.
   *
   * @return {boolean} returns true if the payload queue is empty
   */
  isPayloadQueueEmpty() {
    return this.payloadQueue.size === 0;
  }

  /**
   * Merge upcoming networkEventUpdate payload into existing one.
   *
   * @param {string} id request actor id
   * @param {object} payload request data payload
   */
  pushRequestToQueue(id, payload) {
    let payloadFromQueue = this.payloadQueue.get(id);
    if (!payloadFromQueue) {
      payloadFromQueue = {};
      this.payloadQueue.set(id, payloadFromQueue);
    }
    Object.assign(payloadFromQueue, payload);
  }

  /**
   * Fetches the network information packet from actor server
   *
   * @param {string} id request id
   * @return {object} networkInfo data packet
   */
  getNetworkRequest(id) {
    return this.webConsoleClient.getNetworkRequest(id);
  }

  /**
   * Fetches the full text of a LongString.
   *
   * @param {object|string} stringGrip
   *        The long string grip containing the corresponding actor.
   *        If you pass in a plain string (by accident or because you're lazy),
   *        then a promise of the same string is simply returned.
   * @return {object}
   *         A promise that is resolved when the full string contents
   *         are available, or rejected if something goes wrong.
   */
  getLongString(stringGrip) {
    return this.webConsoleClient.getString(stringGrip);
  }

  /**
   * The "networkEvent" message type handler.
   *
   * @param {string} type message type
   * @param {object} networkInfo network request information
   */
  async onNetworkEvent(type, networkInfo) {
    let {
      actor,
      cause,
      fromCache,
      fromServiceWorker,
      isXHR,
      request: {
        method,
        url,
      },
      startedDateTime,
    } = networkInfo;

    await this.addRequest(actor, {
      cause,
      fromCache,
      fromServiceWorker,
      isXHR,
      method,
      startedDateTime,
      url,
    });

    emit(EVENTS.NETWORK_EVENT, actor);
  }

  /**
   * The "networkEventUpdate" message type handler.
   *
   * @param {string} type message type
   * @param {object} packet the message received from the server.
   * @param {object} networkInfo the network request information.
   */
  onNetworkEventUpdate(type, data) {
    let { packet, networkInfo } = data;
    let { actor } = networkInfo;
    let { updateType } = packet;

    switch (updateType) {
      case "securityInfo":
        this.pushRequestToQueue(actor, { securityState: networkInfo.securityInfo });
        break;
      case "responseStart":
        this.pushRequestToQueue(actor, {
          httpVersion: networkInfo.response.httpVersion,
          remoteAddress: networkInfo.response.remoteAddress,
          remotePort: networkInfo.response.remotePort,
          status: networkInfo.response.status,
          statusText: networkInfo.response.statusText,
          headersSize: networkInfo.response.headersSize
        });
        emit(EVENTS.STARTED_RECEIVING_RESPONSE, actor);
        break;
      case "responseContent":
        this.pushRequestToQueue(actor, {
          contentSize: networkInfo.response.bodySize,
          transferredSize: networkInfo.response.transferredSize,
          mimeType: networkInfo.response.content.mimeType,
        });
        break;
      case "eventTimings":
        // Total time doesn't have to be always set e.g. net provider enhancer
        // in Console panel is using this method to fetch data when network log
        // is expanded. So, make sure to not push undefined into the payload queue
        // (it could overwrite an existing value).
        if (typeof networkInfo.totalTime !== "undefined") {
          this.pushRequestToQueue(actor, { totalTime: networkInfo.totalTime });
        }
        break;
    }

    // This available field helps knowing when/if updateType property is arrived
    // and can be requested via `requestData`
    this.pushRequestToQueue(actor, { [`${updateType}Available`]: true });

    this.onPayloadDataReceived(actor);

    emit(EVENTS.NETWORK_EVENT_UPDATED, actor);
  }

  /**
   * Notify actions when messages from onNetworkEventUpdate are done, networkEventUpdate
   * messages contain initial network info for each updateType and then we can invoke
   * requestData to fetch its corresponded data lazily.
   * Once all updateTypes of networkEventUpdate message are arrived, we flush merged
   * request payload from pending queue and then update component.
   */
  async onPayloadDataReceived(actor) {
    let payload = this.payloadQueue.get(actor) || {};

    if (!payload.requestHeadersAvailable || !payload.requestCookiesAvailable ||
        !payload.eventTimingsAvailable || !payload.responseContentAvailable) {
      return;
    }

    this.payloadQueue.delete(actor);

    if (this.actionsEnabled && this.actions.updateRequest) {
      await this.actions.updateRequest(actor, payload, true);
    }

    // This event is fired only once per request, once all the properties are fetched
    // from `onNetworkEventUpdate`. There should be no more RDP requests after this.
    emit(EVENTS.PAYLOAD_READY, actor);
  }

  /**
   * Public connector API to lazily request HTTP details from the backend.
   *
   * The method focus on:
   * - calling the right actor method,
   * - emitting an event to tell we start fetching some request data,
   * - call data processing method.
   *
   * @param {string} actor actor id (used as request id)
   * @param {string} method identifier of the data we want to fetch
   *
   * @return {Promise} return a promise resolved when data is received.
   */
  requestData(actor, method) {
    // Key string used in `lazyRequestData`. We use this Map to prevent requesting
    // the same data twice at the same time.
    let key = `${actor}-${method}`;
    let promise = this.lazyRequestData.get(key);
    // If a request is pending, reuse it.
    if (promise) {
      return promise;
    }
    // Fetch the data
    promise = this._requestData(actor, method).then(async (payload) => {
      // Remove the request from the cache, any new call to requestData will fetch the
      // data again.
      this.lazyRequestData.delete(key);

      if (this.actionsEnabled && this.actions.updateRequest) {
        await this.actions.updateRequest(actor, {
          ...payload,
          // Lockdown *Available property once we fetch data from back-end.
          // Using this as a flag to prevent fetching arrived data again.
          [`${method}Available`]: false,
        }, true);
      }

      return payload;
    });

    this.lazyRequestData.set(key, promise);

    return promise;
  }

  /**
   * Internal helper used to request HTTP details from the backend.
   *
   * This is internal method that focus on:
   * - calling the right actor method,
   * - emitting an event to tell we start fetching some request data,
   * - call data processing method.
   *
   * @param {string} actor actor id (used as request id)
   * @param {string} method identifier of the data we want to fetch
   *
   * @return {Promise} return a promise resolved when data is received.
   */
  async _requestData(actor, method) {
    // Calculate real name of the client getter.
    let clientMethodName = `get${method.charAt(0).toUpperCase()}${method.slice(1)}`;
    // The name of the callback that processes request response
    let callbackMethodName = `on${method.charAt(0).toUpperCase()}${method.slice(1)}`;
    // And the event to fire before updating this data
    let updatingEventName = `UPDATING_${method.replace(/([A-Z])/g, "_$1").toUpperCase()}`;

    // Emit event that tell we just start fetching some data
    emit(EVENTS[updatingEventName], actor);

    let response = await new Promise((resolve, reject) => {
      // Do a RDP request to fetch data from the actor.
      if (typeof this.webConsoleClient[clientMethodName] === "function") {
        // Make sure we fetch the real actor data instead of cloned actor
        // e.g. CustomRequestPanel will clone a request with additional '-clone' actor id
        this.webConsoleClient[clientMethodName](actor.replace("-clone", ""), (res) => {
          if (res.error) {
            reject(new Error(res.message));
          }
          resolve(res);
        });
      } else {
        reject(new Error(`Error: No such client method '${clientMethodName}'!`));
      }
    });

    // Restore clone actor id
    if (actor.includes("-clone")) {
      // Because response's properties are read-only, we create a new response
      response = { ...response, from: `${response.from}-clone` };
    }

    // Call data processing method.
    return this[callbackMethodName](response);
  }

  /**
   * Handles additional information received for a "requestHeaders" packet.
   *
   * @param {object} response the message received from the server.
   */
  async onRequestHeaders(response) {
    let payload = await this.updateRequest(response.from, {
      requestHeaders: response
    });
    emit(EVENTS.RECEIVED_REQUEST_HEADERS, response.from);
    return payload.requestHeaders;
  }

  /**
   * Handles additional information received for a "responseHeaders" packet.
   *
   * @param {object} response the message received from the server.
   */
  async onResponseHeaders(response) {
    let payload = await this.updateRequest(response.from, {
      responseHeaders: response
    });
    emit(EVENTS.RECEIVED_RESPONSE_HEADERS, response.from);
    return payload.responseHeaders;
  }

  /**
   * Handles additional information received for a "requestCookies" packet.
   *
   * @param {object} response the message received from the server.
   */
  async onRequestCookies(response) {
    let payload = await this.updateRequest(response.from, {
      requestCookies: response
    });
    emit(EVENTS.RECEIVED_REQUEST_COOKIES, response.from);
    return payload.requestCookies;
  }

  /**
   * Handles additional information received for a "requestPostData" packet.
   *
   * @param {object} response the message received from the server.
   */
  async onRequestPostData(response) {
    let payload = await this.updateRequest(response.from, {
      requestPostData: response
    });
    emit(EVENTS.RECEIVED_REQUEST_POST_DATA, response.from);
    return payload;
  }

  /**
   * Handles additional information received for a "securityInfo" packet.
   *
   * @param {object} response the message received from the server.
   */
  async onSecurityInfo(response) {
    let payload = await this.updateRequest(response.from, {
      securityInfo: response.securityInfo
    });
    emit(EVENTS.RECEIVED_SECURITY_INFO, response.from);
    return payload.securityInfo;
  }

  /**
   * Handles additional information received for a "responseCookies" packet.
   *
   * @param {object} response the message received from the server.
   */
  async onResponseCookies(response) {
    let payload = await this.updateRequest(response.from, {
      responseCookies: response
    });
    emit(EVENTS.RECEIVED_RESPONSE_COOKIES, response.from);
    return payload.responseCookies;
  }

  /**
   * Handles additional information received via "getResponseContent" request.
   *
   * @param {object} response the message received from the server.
   */
  async onResponseContent(response) {
    let payload = await this.updateRequest(response.from, {
      // We have to ensure passing mimeType as fetchResponseContent needs it from
      // updateRequest. It will convert the LongString in `response.content.text` to a
      // string.
      mimeType: response.content.mimeType,
      responseContent: response,
    });
    emit(EVENTS.RECEIVED_RESPONSE_CONTENT, response.from);
    return payload.responseContent;
  }

  /**
   * Handles additional information received for a "eventTimings" packet.
   *
   * @param {object} response the message received from the server.
   */
  async onEventTimings(response) {
    let payload = await this.updateRequest(response.from, {
      eventTimings: response
    });
    emit(EVENTS.RECEIVED_EVENT_TIMINGS, response.from);
    return payload.eventTimings;
  }

  /**
   * Handles information received for a "stackTrace" packet.
   *
   * @param {object} response the message received from the server.
   */
  async onStackTrace(response) {
    let payload = await this.updateRequest(response.from, {
      stacktrace: response.stacktrace
    });
    emit(EVENTS.RECEIVED_EVENT_STACKTRACE, response.from);
    return payload.stacktrace;
  }
}

/**
 * Guard 'emit' to avoid exception in non-window environment.
 */
function emit(type, data) {
  if (typeof window != "undefined") {
    window.emit(type, data);
  }
}

module.exports = FirefoxDataProvider;
PK
!<�I Ichrome/devtools/modules/devtools/client/netmonitor/src/connector/index.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

/**
 * Generic connector wrapper object that is responsible for
 * instantiating specific connector implementation according
 * to the client type.
 */
class Connector {
  constructor() {
    this.connector = null;

    // Bind public API
    this.connect = this.connect.bind(this);
    this.disconnect = this.disconnect.bind(this);
    this.connectChrome = this.connectChrome.bind(this);
    this.connectFirefox = this.connectFirefox.bind(this);
    this.getLongString = this.getLongString.bind(this);
    this.getNetworkRequest = this.getNetworkRequest.bind(this);
    this.getTabTarget = this.getTabTarget.bind(this);
    this.sendHTTPRequest = this.sendHTTPRequest.bind(this);
    this.setPreferences = this.setPreferences.bind(this);
    this.triggerActivity = this.triggerActivity.bind(this);
    this.viewSourceInDebugger = this.viewSourceInDebugger.bind(this);
    this.requestData = this.requestData.bind(this);
    this.getTimingMarker = this.getTimingMarker.bind(this);
  }

  // Connect/Disconnect API

  async connect(connection, actions, getState) {
    if (!connection || !connection.tab) {
      return;
    }

    let { clientType } = connection.tab;
    switch (clientType) {
      case "chrome":
        await this.connectChrome(connection, actions, getState);
        break;
      case "firefox":
        await this.connectFirefox(connection, actions, getState);
        break;
      default:
        throw Error(`Unknown client type - ${clientType}`);
    }
  }

  disconnect() {
    this.connector && this.connector.disconnect();
  }

  connectChrome(connection, actions, getState) {
    this.connector = require("./chrome-connector");
    return this.connector.connect(connection, actions, getState);
  }

  connectFirefox(connection, actions, getState) {
    this.connector = require("./firefox-connector");
    return this.connector.connect(connection, actions, getState);
  }

  pause() {
    return this.connector.pause();
  }

  resume() {
    return this.connector.resume();
  }

  enableActions() {
    this.connector.enableActions(...arguments);
  }

  // Public API

  getLongString() {
    return this.connector.getLongString(...arguments);
  }

  getNetworkRequest() {
    return this.connector.getNetworkRequest(...arguments);
  }

  getTabTarget() {
    return this.connector.getTabTarget();
  }

  sendHTTPRequest() {
    return this.connector.sendHTTPRequest(...arguments);
  }

  setPreferences() {
    return this.connector.setPreferences(...arguments);
  }

  triggerActivity() {
    return this.connector.triggerActivity(...arguments);
  }

  viewSourceInDebugger() {
    return this.connector.viewSourceInDebugger(...arguments);
  }

  requestData() {
    return this.connector.requestData(...arguments);
  }

  getTimingMarker() {
    return this.connector.getTimingMarker(...arguments);
  }
}

module.exports.Connector = Connector;
PK
!<ڃ��d!d!Cchrome/devtools/modules/devtools/client/netmonitor/src/constants.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 = {
  ADD_REQUEST: "ADD_REQUEST",
  ADD_TIMING_MARKER: "ADD_TIMING_MARKER",
  BATCH_ACTIONS: "BATCH_ACTIONS",
  BATCH_ENABLE: "BATCH_ENABLE",
  CLEAR_REQUESTS: "CLEAR_REQUESTS",
  CLEAR_TIMING_MARKERS: "CLEAR_TIMING_MARKERS",
  CLONE_SELECTED_REQUEST: "CLONE_SELECTED_REQUEST",
  ENABLE_REQUEST_FILTER_TYPE_ONLY: "ENABLE_REQUEST_FILTER_TYPE_ONLY",
  OPEN_NETWORK_DETAILS: "OPEN_NETWORK_DETAILS",
  RESIZE_NETWORK_DETAILS: "RESIZE_NETWORK_DETAILS",
  ENABLE_PERSISTENT_LOGS: "ENABLE_PERSISTENT_LOGS",
  DISABLE_BROWSER_CACHE: "DISABLE_BROWSER_CACHE",
  OPEN_STATISTICS: "OPEN_STATISTICS",
  REMOVE_SELECTED_CUSTOM_REQUEST: "REMOVE_SELECTED_CUSTOM_REQUEST",
  RESET_COLUMNS: "RESET_COLUMNS",
  SELECT_REQUEST: "SELECT_REQUEST",
  SELECT_DETAILS_PANEL_TAB: "SELECT_DETAILS_PANEL_TAB",
  SEND_CUSTOM_REQUEST: "SEND_CUSTOM_REQUEST",
  SET_REQUEST_FILTER_TEXT: "SET_REQUEST_FILTER_TEXT",
  SORT_BY: "SORT_BY",
  TOGGLE_COLUMN: "TOGGLE_COLUMN",
  TOGGLE_RECORDING: "TOGGLE_RECORDING",
  TOGGLE_REQUEST_FILTER_TYPE: "TOGGLE_REQUEST_FILTER_TYPE",
  UPDATE_REQUEST: "UPDATE_REQUEST",
  WATERFALL_RESIZE: "WATERFALL_RESIZE",
};

// Descriptions for what this frontend is currently doing.
const ACTIVITY_TYPE = {
  // Standing by and handling requests normally.
  NONE: 0,

  // Forcing the target to reload with cache enabled or disabled.
  RELOAD: {
    WITH_CACHE_ENABLED: 1,
    WITH_CACHE_DISABLED: 2,
    WITH_CACHE_DEFAULT: 3
  },

  // Enabling or disabling the cache without triggering a reload.
  ENABLE_CACHE: 3,
  DISABLE_CACHE: 4
};

// The panel's window global is an EventEmitter firing the following events:
const EVENTS = {
  // When a network or timeline event is received.
  // See https://developer.mozilla.org/docs/Tools/Web_Console/remoting for
  // more information about what each packet is supposed to deliver.
  NETWORK_EVENT: "NetMonitor:NetworkEvent",
  NETWORK_EVENT_UPDATED: "NetMonitor:NetworkEventUpdated",
  TIMELINE_EVENT: "NetMonitor:TimelineEvent",

  // When a network event is added to the view
  REQUEST_ADDED: "NetMonitor:RequestAdded",

  // When request headers begin and finish receiving.
  UPDATING_REQUEST_HEADERS: "NetMonitor:NetworkEventUpdating:RequestHeaders",
  RECEIVED_REQUEST_HEADERS: "NetMonitor:NetworkEventUpdated:RequestHeaders",

  // When request cookies begin and finish receiving.
  UPDATING_REQUEST_COOKIES: "NetMonitor:NetworkEventUpdating:RequestCookies",
  RECEIVED_REQUEST_COOKIES: "NetMonitor:NetworkEventUpdated:RequestCookies",

  // When request post data begins and finishes receiving.
  UPDATING_REQUEST_POST_DATA: "NetMonitor:NetworkEventUpdating:RequestPostData",
  RECEIVED_REQUEST_POST_DATA: "NetMonitor:NetworkEventUpdated:RequestPostData",

  // When security information begins and finishes receiving.
  UPDATING_SECURITY_INFO: "NetMonitor:NetworkEventUpdating:SecurityInfo",
  RECEIVED_SECURITY_INFO: "NetMonitor:NetworkEventUpdated:SecurityInfo",

  // When response headers begin and finish receiving.
  UPDATING_RESPONSE_HEADERS: "NetMonitor:NetworkEventUpdating:ResponseHeaders",
  RECEIVED_RESPONSE_HEADERS: "NetMonitor:NetworkEventUpdated:ResponseHeaders",

  // When response cookies begin and finish receiving.
  UPDATING_RESPONSE_COOKIES: "NetMonitor:NetworkEventUpdating:ResponseCookies",
  RECEIVED_RESPONSE_COOKIES: "NetMonitor:NetworkEventUpdated:ResponseCookies",

  // When event timings begin and finish receiving.
  UPDATING_EVENT_TIMINGS: "NetMonitor:NetworkEventUpdating:EventTimings",
  RECEIVED_EVENT_TIMINGS: "NetMonitor:NetworkEventUpdated:EventTimings",

  // When response content begins, updates and finishes receiving.
  STARTED_RECEIVING_RESPONSE: "NetMonitor:NetworkEventUpdating:ResponseStart",
  UPDATING_RESPONSE_CONTENT: "NetMonitor:NetworkEventUpdating:ResponseContent",
  RECEIVED_RESPONSE_CONTENT: "NetMonitor:NetworkEventUpdated:ResponseContent",

  // When stack-trace finishes receiving.
  RECEIVED_EVENT_STACKTRACE: "NetMonitor:NetworkEventUpdated:StackTrace",

  // Fired once the connection is established
  CONNECTED: "connected",

  // When request payload (HTTP details data) are fetched from the backend.
  PAYLOAD_READY: "NetMonitor:PayloadReady",
};

const UPDATE_PROPS = [
  "method",
  "url",
  "remotePort",
  "remoteAddress",
  "status",
  "statusText",
  "httpVersion",
  "securityState",
  "securityInfo",
  "securityInfoAvailable",
  "mimeType",
  "contentSize",
  "transferredSize",
  "totalTime",
  "eventTimings",
  "eventTimingsAvailable",
  "headersSize",
  "customQueryValue",
  "requestHeaders",
  "requestHeadersAvailable",
  "requestHeadersFromUploadStream",
  "requestCookies",
  "requestCookiesAvailable",
  "requestPostData",
  "requestPostDataAvailable",
  "responseHeaders",
  "responseHeadersAvailable",
  "responseCookies",
  "responseCookiesAvailable",
  "responseContent",
  "responseContentAvailable",
  "formDataSections",
  "stacktrace",
];

const PANELS = {
  COOKIES: "cookies",
  HEADERS: "headers",
  PARAMS: "params",
  RESPONSE: "response",
  SECURITY: "security",
  STACK_TRACE: "stack-trace",
  TIMINGS: "timings",
};

const RESPONSE_HEADERS = [
  "Cache-Control",
  "Connection",
  "Content-Encoding",
  "Content-Length",
  "ETag",
  "Keep-Alive",
  "Last-Modified",
  "Server",
  "Vary"
];

const HEADERS = [
  {
    name: "status",
    label: "status3",
    canFilter: true,
    filterKey: "status-code"
  },
  {
    name: "method",
    canFilter: true,
  },
  {
    name: "file",
    canFilter: false,
  },
  {
    name: "protocol",
    canFilter: true,
  },
  {
    name: "scheme",
    canFilter: true,
  },
  {
    name: "domain",
    canFilter: true,
  },
  {
    name: "remoteip",
    canFilter: true,
    filterKey: "remote-ip",
  },
  {
    name: "cause",
    canFilter: true,
  },
  {
    name: "type",
    canFilter: false,
  },
  {
    name: "cookies",
    canFilter: false,
  },
  {
    name: "setCookies",
    boxName: "set-cookies",
    canFilter: false,
  },
  {
    name: "transferred",
    canFilter: true,
  },
  {
    name: "contentSize",
    boxName: "size",
    filterKey: "size",
    canFilter: true,
  },
  {
    name: "startTime",
    boxName: "start-time",
    canFilter: false,
    subMenu: "timings",
  },
  {
    name: "endTime",
    boxName: "end-time",
    canFilter: false,
    subMenu: "timings",
  },
  {
    name: "responseTime",
    boxName: "response-time",
    canFilter: false,
    subMenu: "timings",
  },
  {
    name: "duration",
    canFilter: false,
    subMenu: "timings",
  },
  {
    name: "latency",
    canFilter: false,
    subMenu: "timings",
  },
  ...RESPONSE_HEADERS
    .map(header => ({
      name: header,
      boxName: "response-header",
      canFilter: false,
      subMenu: "responseHeaders",
      noLocalization: true
    })),
  {
    name: "waterfall",
    canFilter: false,
  }
];

const HEADER_FILTERS = HEADERS
  .filter(h => h.canFilter)
  .map(h => h.filterKey || h.name);

const FILTER_FLAGS = [
  ...HEADER_FILTERS,
  "set-cookie-domain",
  "set-cookie-name",
  "set-cookie-value",
  "mime-type",
  "larger-than",
  "transferred-larger-than",
  "is",
  "has-response-header",
  "regexp",
];

const FILTER_TAGS = [
  "html",
  "css",
  "js",
  "xhr",
  "fonts",
  "images",
  "media",
  "ws",
  "other",
];

const REQUESTS_WATERFALL = {
  BACKGROUND_TICKS_MULTIPLE: 5, // ms
  BACKGROUND_TICKS_SCALES: 3,
  BACKGROUND_TICKS_SPACING_MIN: 10, // px
  BACKGROUND_TICKS_COLOR_RGB: [128, 136, 144],
  // 8-bit value of the alpha component of the tick color
  BACKGROUND_TICKS_OPACITY_MIN: 32,
  BACKGROUND_TICKS_OPACITY_ADD: 32,
  // Colors for timing markers (theme colors, see variables.css)
  DOMCONTENTLOADED_TICKS_COLOR: "highlight-blue",
  LOAD_TICKS_COLOR: "highlight-red",
  // Opacity for the timing markers
  TICKS_COLOR_OPACITY: 192,
  HEADER_TICKS_MULTIPLE: 5, // ms
  HEADER_TICKS_SPACING_MIN: 60, // px
  // Reserve extra space for rendering waterfall time label
  LABEL_WIDTH: 50, // px
};

const TIMING_KEYS = [
  "blocked",
  "dns",
  "connect",
  "ssl",
  "send",
  "wait",
  "receive",
];

const general = {
  ACTIVITY_TYPE,
  EVENTS,
  FILTER_SEARCH_DELAY: 200,
  UPDATE_PROPS,
  HEADERS,
  RESPONSE_HEADERS,
  FILTER_FLAGS,
  FILTER_TAGS,
  REQUESTS_WATERFALL,
  PANELS,
  TIMING_KEYS,
};

// flatten constants
module.exports = Object.assign({}, general, actionTypes);
PK
!<�/��Fchrome/devtools/modules/devtools/client/netmonitor/src/create-store.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Services = require("Services");
const { applyMiddleware, createStore } = require("devtools/client/shared/vendor/redux");

// Middleware
const batching = require("./middleware/batching");
const prefs = require("./middleware/prefs");
const thunk = require("./middleware/thunk");
const recording = require("./middleware/recording");

// Reducers
const rootReducer = require("./reducers/index");
const { FilterTypes, Filters } = require("./reducers/filters");
const { Requests } = require("./reducers/requests");
const { Sort } = require("./reducers/sort");
const { TimingMarkers } = require("./reducers/timing-markers");
const { UI, Columns } = require("./reducers/ui");

/**
 * Configure state and middleware for the Network monitor tool.
 */
function configureStore(connector) {
  // Prepare initial state.
  const initialState = {
    filters: new Filters({
      requestFilterTypes: getFilterState()
    }),
    requests: new Requests(),
    sort: new Sort(),
    timingMarkers: new TimingMarkers(),
    ui: UI({
      columns: getColumnState()
    }),
  };

  // Prepare middleware.
  let middleware = applyMiddleware(
    thunk,
    prefs,
    batching,
    recording(connector)
  );

  return createStore(rootReducer, initialState, middleware);
}

// Helpers

/**
 * Get column state from preferences.
 */
function getColumnState() {
  let columns = Columns();
  let visibleColumns = getPref("devtools.netmonitor.visibleColumns");

  const state = {};
  for (let col in columns) {
    state[col] = visibleColumns.includes(col);
  }

  return state;
}

/**
 * Get filter state from preferences.
 */
function getFilterState() {
  let activeFilters = {};
  let filters = getPref("devtools.netmonitor.filters");
  filters.forEach((filter) => {
    activeFilters[filter] = true;
  });
  return new FilterTypes(activeFilters);
}

function getPref(pref) {
  try {
    return JSON.parse(Services.prefs.getCharPref(pref));
  } catch (_) {
    return [];
  }
}

exports.configureStore = configureStore;
PK
!<�QQ���Lchrome/devtools/modules/devtools/client/netmonitor/src/har/har-automation.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 mozilla/reject-some-requires */

"use strict";

const { Ci } = require("chrome");
const Services = require("Services");
const { HarCollector } = require("./har-collector");
const { HarExporter } = require("./har-exporter");
const { HarUtils } = require("./har-utils");

const prefDomain = "devtools.netmonitor.har.";

// Helper tracer. Should be generic sharable by other modules (bug 1171927)
const trace = {
  log: function (...args) {
  }
};

/**
 * This object is responsible for automated HAR export. It listens
 * for Network activity, collects all HTTP data and triggers HAR
 * export when the page is loaded.
 *
 * The user needs to enable the following preference to make the
 * auto-export work: devtools.netmonitor.har.enableAutoExportToFile
 *
 * HAR files are stored within directory that is specified in this
 * preference: devtools.netmonitor.har.defaultLogDir
 *
 * If the default log directory preference isn't set the following
 * directory is used by default: <profile>/har/logs
 */
function HarAutomation(toolbox) {
  this.initialize(toolbox);
}

HarAutomation.prototype = {
  // Initialization

  initialize: function (toolbox) {
    this.toolbox = toolbox;

    let target = toolbox.target;
    target.makeRemote().then(() => {
      this.startMonitoring(target.client, target.form);
    });
  },

  destroy: function () {
    if (this.collector) {
      this.collector.stop();
    }

    if (this.tabWatcher) {
      this.tabWatcher.disconnect();
    }
  },

  // Automation

  startMonitoring: function (client, tabGrip, callback) {
    if (!client) {
      return;
    }

    if (!tabGrip) {
      return;
    }

    this.debuggerClient = client;
    this.tabClient = this.toolbox.target.activeTab;
    this.webConsoleClient = this.toolbox.target.activeConsole;

    this.tabWatcher = new TabWatcher(this.toolbox, this);
    this.tabWatcher.connect();
  },

  pageLoadBegin: function (response) {
    this.resetCollector();
  },

  resetCollector: function () {
    if (this.collector) {
      this.collector.stop();
    }

    // A page is about to be loaded, start collecting HTTP
    // data from events sent from the backend.
    this.collector = new HarCollector({
      webConsoleClient: this.webConsoleClient,
      debuggerClient: this.debuggerClient
    });

    this.collector.start();
  },

  /**
   * A page is done loading, export collected data. Note that
   * some requests for additional page resources might be pending,
   * so export all after all has been properly received from the backend.
   *
   * This collector still works and collects any consequent HTTP
   * traffic (e.g. XHRs) happening after the page is loaded and
   * The additional traffic can be exported by executing
   * triggerExport on this object.
   */
  pageLoadDone: function (response) {
    trace.log("HarAutomation.pageLoadDone; ", response);

    if (this.collector) {
      this.collector.waitForHarLoad().then(collector => {
        return this.autoExport();
      });
    }
  },

  autoExport: function () {
    let autoExport = Services.prefs.getBoolPref(prefDomain +
      "enableAutoExportToFile");

    if (!autoExport) {
      return Promise.resolve();
    }

    // Auto export to file is enabled, so save collected data
    // into a file and use all the default options.
    let data = {
      fileName: Services.prefs.getCharPref(prefDomain + "defaultFileName"),
    };

    return this.executeExport(data);
  },

  // Public API

  /**
   * Export all what is currently collected.
   */
  triggerExport: function (data) {
    if (!data.fileName) {
      data.fileName = Services.prefs.getCharPref(prefDomain +
        "defaultFileName");
    }

    return this.executeExport(data);
  },

  /**
   * Clear currently collected data.
   */
  clear: function () {
    this.resetCollector();
  },

  // HAR Export

  /**
   * Execute HAR export. This method fetches all data from the
   * Network panel (asynchronously) and saves it into a file.
   */
  executeExport: function (data) {
    let items = this.collector.getItems();
    let form = this.toolbox.target.form;
    let title = form.title || form.url;

    let options = {
      requestData: null,
      getTimingMarker: null,
      getString: this.getString.bind(this),
      view: this,
      items: items,
    };

    options.defaultFileName = data.fileName;
    options.compress = data.compress;
    options.title = data.title || title;
    options.id = data.id;
    options.jsonp = data.jsonp;
    options.includeResponseBodies = data.includeResponseBodies;
    options.jsonpCallback = data.jsonpCallback;
    options.forceExport = data.forceExport;

    trace.log("HarAutomation.executeExport; " + data.fileName, options);

    return HarExporter.fetchHarData(options).then(jsonString => {
      // Save the HAR file if the file name is provided.
      if (jsonString && options.defaultFileName) {
        let file = getDefaultTargetFile(options);
        if (file) {
          HarUtils.saveToFile(file, jsonString, options.compress);
        }
      }

      return jsonString;
    });
  },

  /**
   * Fetches the full text of a string.
   */
  getString: function (stringGrip) {
    return this.webConsoleClient.getString(stringGrip);
  },
};

// Helpers

function TabWatcher(toolbox, listener) {
  this.target = toolbox.target;
  this.listener = listener;

  this.onTabNavigated = this.onTabNavigated.bind(this);
}

TabWatcher.prototype = {
  // Connection

  connect: function () {
    this.target.on("navigate", this.onTabNavigated);
    this.target.on("will-navigate", this.onTabNavigated);
  },

  disconnect: function () {
    if (!this.target) {
      return;
    }

    this.target.off("navigate", this.onTabNavigated);
    this.target.off("will-navigate", this.onTabNavigated);
  },

  // Event Handlers

  /**
   * Called for each location change in the monitored tab.
   *
   * @param string aType
   *        Packet type.
   * @param object aPacket
   *        Packet received from the server.
   */
  onTabNavigated: function (type, packet) {
    switch (type) {
      case "will-navigate": {
        this.listener.pageLoadBegin(packet);
        break;
      }
      case "navigate": {
        this.listener.pageLoadDone(packet);
        break;
      }
    }
  },
};

// Protocol Helpers

/**
 * Returns target file for exported HAR data.
 */
function getDefaultTargetFile(options) {
  let path = options.defaultLogDir ||
    Services.prefs.getCharPref("devtools.netmonitor.har.defaultLogDir");
  let folder = HarUtils.getLocalDirectory(path);
  let fileName = HarUtils.getHarFileName(options.defaultFileName,
    options.jsonp, options.compress);

  folder.append(fileName);
  folder.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("0666", 8));

  return folder;
}

// Exports from this module
exports.HarAutomation = HarAutomation;
PK
!<�5�zzOchrome/devtools/modules/devtools/client/netmonitor/src/har/har-builder-utils.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

/**
 * Currently supported HAR version.
 */
const HAR_VERSION = "1.1";

function buildHarLog(appInfo) {
  return {
    log: {
      version: HAR_VERSION,
      creator: {
        name: appInfo.name,
        version: appInfo.version
      },
      browser: {
        name: appInfo.name,
        version: appInfo.version
      },
      pages: [],
      entries: [],
    }
  };
}

exports.buildHarLog = buildHarLog;
PK
!<��,g�<�<Ichrome/devtools/modules/devtools/client/netmonitor/src/har/har-builder.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Services = require("Services");
const appInfo = Services.appinfo;
const { LocalizationHelper } = require("devtools/shared/l10n");
const { CurlUtils } = require("devtools/client/shared/curl");
const {
  getFormDataSections,
  getUrlQuery,
  parseQueryString,
} = require("../utils/request-utils");
const { buildHarLog } = require("./har-builder-utils");
const L10N = new LocalizationHelper("devtools/client/locales/har.properties");
const {
  TIMING_KEYS
} = require("../constants");

/**
 * This object is responsible for building HAR file. See HAR spec:
 * https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/HAR/Overview.html
 * http://www.softwareishard.com/blog/har-12-spec/
 *
 * @param {Object} options configuration object
 *
 * The following options are supported:
 *
 * - items {Array}: List of Network requests to be exported.
 *
 * - id {String}: ID of the exported page.
 *
 * - title {String}: Title of the exported page.
 *
 * - includeResponseBodies {Boolean}: Set to true to include HTTP response
 *   bodies in the result data structure.
 */
var HarBuilder = function (options) {
  this._options = options;
  this._pageMap = [];
};

HarBuilder.prototype = {
  // Public API

  /**
   * This is the main method used to build the entire result HAR data.
   * The process is asynchronous since it can involve additional RDP
   * communication (e.g. resolving long strings).
   *
   * @returns {Promise} A promise that resolves to the HAR object when
   * the entire build process is done.
   */
  build: async function () {
    this.promises = [];

    // Build basic structure for data.
    let log = buildHarLog(appInfo);

    // Build entries.
    for (let file of this._options.items) {
      log.log.entries.push(await this.buildEntry(log.log, file));
    }

    // Some data needs to be fetched from the backend during the
    // build process, so wait till all is done.
    await Promise.all(this.promises);

    return log;
  },

  // Helpers

  buildPage: function (file) {
    let page = {};

    // Page start time is set when the first request is processed
    // (see buildEntry)
    page.startedDateTime = 0;
    page.id = "page_" + this._options.id;
    page.title = this._options.title;

    return page;
  },

  getPage: function (log, file) {
    let id = this._options.id;
    let page = this._pageMap[id];
    if (page) {
      return page;
    }

    this._pageMap[id] = page = this.buildPage(file);
    log.pages.push(page);

    return page;
  },

  buildEntry: async function (log, file) {
    let page = this.getPage(log, file);

    let entry = {};
    entry.pageref = page.id;
    entry.startedDateTime = dateToJSON(new Date(file.startedMillis));

    let eventTimings = file.eventTimings;
    if (!eventTimings && this._options.requestData) {
      eventTimings = await this._options.requestData(file.id, "eventTimings");
    }

    entry.request = await this.buildRequest(file);
    entry.response = await this.buildResponse(file);
    entry.cache = this.buildCache(file);
    entry.timings = eventTimings ? eventTimings.timings : {};

    // Calculate total time by summing all timings. Note that
    // `file.totalTime` can't be used since it doesn't have to
    // correspond to plain summary of individual timings.
    // With TCP Fast Open and TLS early data sending data can
    // start at the same time as connect (we can send data on
    // TCP syn packet). Also TLS handshake can carry application
    // data thereby overlapping a sending data period and TLS
    // handshake period.
    entry.time = TIMING_KEYS.reduce((sum, type) => {
      let time = entry.timings[type];
      return (typeof time != "undefined") ? (sum + time) : sum;
    }, 0);

    // Security state isn't part of HAR spec, and so create
    // custom field that needs to use '_' prefix.
    entry._securityState = file.securityState;

    if (file.remoteAddress) {
      entry.serverIPAddress = file.remoteAddress;
    }

    if (file.remotePort) {
      entry.connection = file.remotePort + "";
    }

    // Compute page load start time according to the first request start time.
    if (!page.startedDateTime) {
      page.startedDateTime = entry.startedDateTime;
      page.pageTimings = this.buildPageTimings(page, file);
    }

    return entry;
  },

  buildPageTimings: function (page, file) {
    // Event timing info isn't available
    let timings = {
      onContentLoad: -1,
      onLoad: -1
    };

    let getTimingMarker = this._options.getTimingMarker;
    if (getTimingMarker) {
      timings.onContentLoad = getTimingMarker("firstDocumentDOMContentLoadedTimestamp");
      timings.onLoad = getTimingMarker("firstDocumentLoadTimestamp");
    }

    return timings;
  },

  buildRequest: async function (file) {
    // When using HarAutomation, HarCollector will automatically fetch requestHeaders
    // and requestCookies, but when we use it from netmonitor, FirefoxDataProvider
    // should fetch it itself lazily, via requestData.

    let requestHeaders = file.requestHeaders;
    if (!requestHeaders && this._options.requestData) {
      requestHeaders = await this._options.requestData(file.id, "requestHeaders");
    }

    let requestCookies = file.requestCookies;
    if (!requestCookies && this._options.requestData) {
      requestCookies = await this._options.requestData(file.id, "requestCookies");
    }

    let request = {
      bodySize: 0
    };
    request.method = file.method;
    request.url = file.url;
    request.httpVersion = file.httpVersion || "";
    request.headers = this.buildHeaders(requestHeaders);
    request.headers = this.appendHeadersPostData(request.headers, file);
    request.cookies = this.buildCookies(requestCookies);
    request.queryString = parseQueryString(getUrlQuery(file.url)) || [];
    request.headersSize = requestHeaders.headersSize;
    request.postData = await this.buildPostData(file);

    if (request.postData && request.postData.text) {
      request.bodySize = request.postData.text.length;
    }

    return request;
  },

  /**
   * Fetch all header values from the backend (if necessary) and
   * build the result HAR structure.
   *
   * @param {Object} input Request or response header object.
   */
  buildHeaders: function (input) {
    if (!input) {
      return [];
    }

    return this.buildNameValuePairs(input.headers);
  },

  appendHeadersPostData: function (input = [], file) {
    if (!file.requestPostData) {
      return input;
    }

    this.fetchData(file.requestPostData.postData.text).then(value => {
      let multipartHeaders = CurlUtils.getHeadersFromMultipartText(value);
      for (let header of multipartHeaders) {
        input.push(header);
      }
    });

    return input;
  },

  buildCookies: function (input) {
    if (!input) {
      return [];
    }

    return this.buildNameValuePairs(input.cookies || input);
  },

  buildNameValuePairs: function (entries) {
    let result = [];

    // HAR requires headers array to be presented, so always
    // return at least an empty array.
    if (!entries) {
      return result;
    }

    // Make sure header values are fully fetched from the server.
    entries.forEach(entry => {
      this.fetchData(entry.value).then(value => {
        result.push({
          name: entry.name,
          value: value
        });
      });
    });

    return result;
  },

  buildPostData: async function (file) {
    // When using HarAutomation, HarCollector will automatically fetch requestPostData
    // and requestHeaders, but when we use it from netmonitor, FirefoxDataProvider
    // should fetch it itself lazily, via requestData.
    let requestPostData = file.requestPostData;
    let requestHeaders = file.requestHeaders;
    let requestHeadersFromUploadStream;

    if (!requestPostData && this._options.requestData) {
      let payload = await this._options.requestData(file.id, "requestPostData");
      requestPostData = payload.requestPostData;
      requestHeadersFromUploadStream = payload.requestHeadersFromUploadStream;
    }

    if (!requestPostData.postData.text) {
      return undefined;
    }

    if (!requestHeaders && this._options.requestData) {
      requestHeaders = await this._options.requestData(file.id, "requestHeaders");
    }

    let postData = {
      mimeType: findValue(requestHeaders.headers, "content-type"),
      params: [],
      text: requestPostData.postData.text,
    };

    if (requestPostData.postDataDiscarded) {
      postData.comment = L10N.getStr("har.requestBodyNotIncluded");
      return postData;
    }

    // If we are dealing with URL encoded body, parse parameters.
    if (CurlUtils.isUrlEncodedRequest({
      headers: requestHeaders.headers,
      postDataText: postData.text,
    })) {
      postData.mimeType = "application/x-www-form-urlencoded";
      // Extract form parameters and produce nice HAR array.
      let formDataSections = await getFormDataSections(
        requestHeaders,
        requestHeadersFromUploadStream,
        requestPostData,
        this._options.getString,
      );

      formDataSections.forEach((section) => {
        let paramsArray = parseQueryString(section);
        if (paramsArray) {
          postData.params = [...postData.params, ...paramsArray];
        }
      });
    }

    return postData;
  },

  buildResponse: async function (file) {
    // When using HarAutomation, HarCollector will automatically fetch responseHeaders
    // and responseCookies, but when we use it from netmonitor, FirefoxDataProvider
    // should fetch it itself lazily, via requestData.

    let responseHeaders = file.responseHeaders;
    if (!responseHeaders && this._options.requestData) {
      responseHeaders = await this._options.requestData(file.id, "responseHeaders");
    }

    let responseCookies = file.responseCookies;
    if (!responseCookies && this._options.requestData) {
      responseCookies = await this._options.requestData(file.id, "responseCookies");
    }

    let response = {
      status: 0
    };

    // Arbitrary value if it's aborted to make sure status has a number
    if (file.status) {
      response.status = parseInt(file.status, 10);
    }
    response.statusText = file.statusText || "";
    response.httpVersion = file.httpVersion || "";

    response.headers = this.buildHeaders(responseHeaders);
    response.cookies = this.buildCookies(responseCookies);
    response.content = await this.buildContent(file);

    let headers = responseHeaders ? responseHeaders.headers : null;
    let headersSize = responseHeaders ? responseHeaders.headersSize : -1;

    response.redirectURL = findValue(headers, "Location");
    response.headersSize = headersSize;

    // 'bodySize' is size of the received response body in bytes.
    // Set to zero in case of responses coming from the cache (304).
    // Set to -1 if the info is not available.
    if (typeof file.transferredSize != "number") {
      response.bodySize = (response.status == 304) ? 0 : -1;
    } else {
      response.bodySize = file.transferredSize;
    }

    return response;
  },

  buildContent: async function (file) {
    let content = {
      mimeType: file.mimeType,
      size: -1
    };

    // When using HarAutomation, HarCollector will automatically fetch responseContent,
    // but when we use it from netmonitor, FirefoxDataProvider should fetch it itself
    // lazily, via requestData.
    let responseContent = file.responseContent;
    if (!responseContent && this._options.requestData) {
      responseContent = await this._options.requestData(file.id, "responseContent");
    }
    if (responseContent && responseContent.content) {
      content.size = responseContent.content.size;
      content.encoding = responseContent.content.encoding;
    }

    let includeBodies = this._options.includeResponseBodies;
    let contentDiscarded = responseContent ?
      responseContent.contentDiscarded : false;

    // The comment is appended only if the response content
    // is explicitly discarded.
    if (!includeBodies || contentDiscarded) {
      content.comment = L10N.getStr("har.responseBodyNotIncluded");
      return content;
    }

    if (responseContent) {
      let text = responseContent.content.text;
      this.fetchData(text).then(value => {
        content.text = value;
      });
    }

    return content;
  },

  buildCache: function (file) {
    let cache = {};

    if (!file.fromCache) {
      return cache;
    }

    // There is no such info yet in the Net panel.
    // cache.beforeRequest = {};

    if (file.cacheEntry) {
      cache.afterRequest = this.buildCacheEntry(file.cacheEntry);
    } else {
      cache.afterRequest = null;
    }

    return cache;
  },

  buildCacheEntry: function (cacheEntry) {
    let cache = {};

    cache.expires = findValue(cacheEntry, "Expires");
    cache.lastAccess = findValue(cacheEntry, "Last Fetched");
    cache.eTag = "";
    cache.hitCount = findValue(cacheEntry, "Fetch Count");

    return cache;
  },

  getBlockingEndTime: function (file) {
    if (file.resolveStarted && file.connectStarted) {
      return file.resolvingTime;
    }

    if (file.connectStarted) {
      return file.connectingTime;
    }

    if (file.sendStarted) {
      return file.sendingTime;
    }

    return (file.sendingTime > file.startTime) ?
      file.sendingTime : file.waitingForTime;
  },

  // RDP Helpers

  fetchData: function (string) {
    let promise = this._options.getString(string).then(value => {
      return value;
    });

    // Building HAR is asynchronous and not done till all
    // collected promises are resolved.
    this.promises.push(promise);

    return promise;
  }
};

// Helpers

/**
 * Find specified value within an array of name-value pairs
 * (used for headers, cookies and cache entries)
 */
function findValue(arr, name) {
  if (!arr) {
    return "";
  }

  name = name.toLowerCase();
  let result = arr.find(entry => entry.name.toLowerCase() == name);
  return result ? result.value : "";
}

/**
 * Generate HAR representation of a date.
 * (YYYY-MM-DDThh:mm:ss.sTZD, e.g. 2009-07-24T19:20:30.45+01:00)
 * See also HAR Schema: http://janodvarko.cz/har/viewer/
 *
 * Note: it would be great if we could utilize Date.toJSON(), but
 * it doesn't return proper time zone offset.
 *
 * An example:
 * This helper returns:    2015-05-29T16:10:30.424+02:00
 * Date.toJSON() returns:  2015-05-29T14:10:30.424Z
 *
 * @param date {Date} The date object we want to convert.
 */
function dateToJSON(date) {
  function f(n, c) {
    if (!c) {
      c = 2;
    }
    let s = new String(n);
    while (s.length < c) {
      s = "0" + s;
    }
    return s;
  }

  let result = date.getFullYear() + "-" +
    f(date.getMonth() + 1) + "-" +
    f(date.getDate()) + "T" +
    f(date.getHours()) + ":" +
    f(date.getMinutes()) + ":" +
    f(date.getSeconds()) + "." +
    f(date.getMilliseconds(), 3);

  let offset = date.getTimezoneOffset();
  let positive = offset > 0;

  // Convert to positive number before using Math.floor (see issue 5512)
  offset = Math.abs(offset);
  let offsetHours = Math.floor(offset / 60);
  let offsetMinutes = Math.floor(offset % 60);
  let prettyOffset = (positive > 0 ? "-" : "+") + f(offsetHours) +
    ":" + f(offsetMinutes);

  return result + prettyOffset;
}

// Exports from this module
exports.HarBuilder = HarBuilder;
PK
!<�k���3�3Kchrome/devtools/modules/devtools/client/netmonitor/src/har/har-collector.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Services = require("Services");

// Helper tracer. Should be generic sharable by other modules (bug 1171927)
const trace = {
  log: function (...args) {
  }
};

/**
 * This object is responsible for collecting data related to all
 * HTTP requests executed by the page (including inner iframes).
 */
function HarCollector(options) {
  this.webConsoleClient = options.webConsoleClient;
  this.debuggerClient = options.debuggerClient;

  this.onNetworkEvent = this.onNetworkEvent.bind(this);
  this.onNetworkEventUpdate = this.onNetworkEventUpdate.bind(this);
  this.onRequestHeaders = this.onRequestHeaders.bind(this);
  this.onRequestCookies = this.onRequestCookies.bind(this);
  this.onRequestPostData = this.onRequestPostData.bind(this);
  this.onResponseHeaders = this.onResponseHeaders.bind(this);
  this.onResponseCookies = this.onResponseCookies.bind(this);
  this.onResponseContent = this.onResponseContent.bind(this);
  this.onEventTimings = this.onEventTimings.bind(this);

  this.clear();
}

HarCollector.prototype = {
  // Connection

  start: function () {
    this.debuggerClient.addListener("networkEvent", this.onNetworkEvent);
    this.debuggerClient.addListener("networkEventUpdate",
      this.onNetworkEventUpdate);
  },

  stop: function () {
    this.debuggerClient.removeListener("networkEvent", this.onNetworkEvent);
    this.debuggerClient.removeListener("networkEventUpdate",
      this.onNetworkEventUpdate);
  },

  clear: function () {
    // Any pending requests events will be ignored (they turn
    // into zombies, since not present in the files array).
    this.files = new Map();
    this.items = [];
    this.firstRequestStart = -1;
    this.lastRequestStart = -1;
    this.requests = [];
  },

  waitForHarLoad: function () {
    // There should be yet another timeout e.g.:
    // 'devtools.netmonitor.har.pageLoadTimeout'
    // that should force export even if page isn't fully loaded.
    return new Promise((resolve) => {
      this.waitForResponses().then(() => {
        trace.log("HarCollector.waitForHarLoad; DONE HAR loaded!");
        resolve(this);
      });
    });
  },

  waitForResponses: function () {
    trace.log("HarCollector.waitForResponses; " + this.requests.length);

    // All requests for additional data must be received to have complete
    // HTTP info to generate the result HAR file. So, wait for all current
    // promises. Note that new promises (requests) can be generated during the
    // process of HTTP data collection.
    return waitForAll(this.requests).then(() => {
      // All responses are received from the backend now. We yet need to
      // wait for a little while to see if a new request appears. If yes,
      // lets's start gathering HTTP data again. If no, we can declare
      // the page loaded.
      // If some new requests appears in the meantime the promise will
      // be rejected and we need to wait for responses all over again.

      this.pageLoadDeferred = this.waitForTimeout().then(() => {
        // Page loaded!
      }, () => {
        trace.log("HarCollector.waitForResponses; NEW requests " +
          "appeared during page timeout!");
        // New requests executed, let's wait again.
        return this.waitForResponses();
      });
      return this.pageLoadDeferred;
    });
  },

  // Page Loaded Timeout

  /**
   * The page is loaded when there are no new requests within given period
   * of time. The time is set in preferences:
   * 'devtools.netmonitor.har.pageLoadedTimeout'
   */
  waitForTimeout: function () {
    // The auto-export is not done if the timeout is set to zero (or less).
    // This is useful in cases where the export is done manually through
    // API exposed to the content.
    let timeout = Services.prefs.getIntPref(
      "devtools.netmonitor.har.pageLoadedTimeout");

    trace.log("HarCollector.waitForTimeout; " + timeout);

    return new Promise((resolve, reject) => {
      if (timeout <= 0) {
        resolve();
      }
      this.pageLoadReject = reject;
      this.pageLoadTimeout = setTimeout(() => {
        trace.log("HarCollector.onPageLoadTimeout;");
        resolve();
      }, timeout);
    });
  },

  resetPageLoadTimeout: function () {
    // Remove the current timeout.
    if (this.pageLoadTimeout) {
      trace.log("HarCollector.resetPageLoadTimeout;");

      clearTimeout(this.pageLoadTimeout);
      this.pageLoadTimeout = null;
    }

    // Reject the current page load promise
    if (this.pageLoadReject) {
      this.pageLoadReject();
      this.pageLoadReject = null;
    }
  },

  // Collected Data

  getFile: function (actorId) {
    return this.files.get(actorId);
  },

  getItems: function () {
    return this.items;
  },

  // Event Handlers

  onNetworkEvent: function (type, packet) {
    // Skip events from different console actors.
    if (packet.from != this.webConsoleClient.actor) {
      return;
    }

    trace.log("HarCollector.onNetworkEvent; " + type, packet);

    let { actor, startedDateTime, method, url, isXHR } = packet.eventActor;
    let startTime = Date.parse(startedDateTime);

    if (this.firstRequestStart == -1) {
      this.firstRequestStart = startTime;
    }

    if (this.lastRequestEnd < startTime) {
      this.lastRequestEnd = startTime;
    }

    let file = this.getFile(actor);
    if (file) {
      console.error("HarCollector.onNetworkEvent; ERROR " +
                    "existing file conflict!");
      return;
    }

    file = {
      startedDeltaMillis: startTime - this.firstRequestStart,
      startedMillis: startTime,
      method: method,
      url: url,
      isXHR: isXHR
    };

    this.files.set(actor, file);

    // Mimic the Net panel data structure
    this.items.push(file);
  },

  onNetworkEventUpdate: function (type, packet) {
    let actor = packet.from;

    // Skip events from unknown actors (not in the list).
    // It can happen when there are zombie requests received after
    // the target is closed or multiple tabs are attached through
    // one connection (one DebuggerClient object).
    let file = this.getFile(packet.from);
    if (!file) {
      return;
    }

    trace.log("HarCollector.onNetworkEventUpdate; " +
      packet.updateType, packet);

    let includeResponseBodies = Services.prefs.getBoolPref(
      "devtools.netmonitor.har.includeResponseBodies");

    let request;
    switch (packet.updateType) {
      case "requestHeaders":
        request = this.getData(actor, "getRequestHeaders",
          this.onRequestHeaders);
        break;
      case "requestCookies":
        request = this.getData(actor, "getRequestCookies",
          this.onRequestCookies);
        break;
      case "requestPostData":
        request = this.getData(actor, "getRequestPostData",
          this.onRequestPostData);
        break;
      case "responseHeaders":
        request = this.getData(actor, "getResponseHeaders",
          this.onResponseHeaders);
        break;
      case "responseCookies":
        request = this.getData(actor, "getResponseCookies",
          this.onResponseCookies);
        break;
      case "responseStart":
        file.httpVersion = packet.response.httpVersion;
        file.status = packet.response.status;
        file.statusText = packet.response.statusText;
        break;
      case "responseContent":
        file.contentSize = packet.contentSize;
        file.mimeType = packet.mimeType;
        file.transferredSize = packet.transferredSize;

        if (includeResponseBodies) {
          request = this.getData(actor, "getResponseContent",
            this.onResponseContent);
        }
        break;
      case "eventTimings":
        request = this.getData(actor, "getEventTimings",
          this.onEventTimings);
        break;
    }

    if (request) {
      this.requests.push(request);
    }

    this.resetPageLoadTimeout();
  },

  getData: function (actor, method, callback) {
    return new Promise((resolve) => {
      if (!this.webConsoleClient[method]) {
        console.error("HarCollector.getData: ERROR Unknown method!");
        resolve();
      }

      let file = this.getFile(actor);

      trace.log("HarCollector.getData; REQUEST " + method +
        ", " + file.url, file);

      this.webConsoleClient[method](actor, response => {
        trace.log("HarCollector.getData; RESPONSE " + method +
          ", " + file.url, response);
        callback(response);
        resolve(response);
      });
    });
  },

  /**
   * Handles additional information received for a "requestHeaders" packet.
   *
   * @param object response
   *        The message received from the server.
   */
  onRequestHeaders: function (response) {
    let file = this.getFile(response.from);
    file.requestHeaders = response;

    this.getLongHeaders(response.headers);
  },

  /**
   * Handles additional information received for a "requestCookies" packet.
   *
   * @param object response
   *        The message received from the server.
   */
  onRequestCookies: function (response) {
    let file = this.getFile(response.from);
    file.requestCookies = response;

    this.getLongHeaders(response.cookies);
  },

  /**
   * Handles additional information received for a "requestPostData" packet.
   *
   * @param object response
   *        The message received from the server.
   */
  onRequestPostData: function (response) {
    trace.log("HarCollector.onRequestPostData;", response);

    let file = this.getFile(response.from);
    file.requestPostData = response;

    // Resolve long string
    let text = response.postData.text;
    if (typeof text == "object") {
      this.getString(text).then(value => {
        response.postData.text = value;
      });
    }
  },

  /**
   * Handles additional information received for a "responseHeaders" packet.
   *
   * @param object response
   *        The message received from the server.
   */
  onResponseHeaders: function (response) {
    let file = this.getFile(response.from);
    file.responseHeaders = response;

    this.getLongHeaders(response.headers);
  },

  /**
   * Handles additional information received for a "responseCookies" packet.
   *
   * @param object response
   *        The message received from the server.
   */
  onResponseCookies: function (response) {
    let file = this.getFile(response.from);
    file.responseCookies = response;

    this.getLongHeaders(response.cookies);
  },

  /**
   * Handles additional information received for a "responseContent" packet.
   *
   * @param object response
   *        The message received from the server.
   */
  onResponseContent: function (response) {
    let file = this.getFile(response.from);
    file.responseContent = response;

    // Resolve long string
    let text = response.content.text;
    if (typeof text == "object") {
      this.getString(text).then(value => {
        response.content.text = value;
      });
    }
  },

  /**
   * Handles additional information received for a "eventTimings" packet.
   *
   * @param object response
   *        The message received from the server.
   */
  onEventTimings: function (response) {
    let file = this.getFile(response.from);
    file.eventTimings = response;
    file.totalTime = response.totalTime;
  },

  // Helpers

  getLongHeaders: function (headers) {
    for (let header of headers) {
      if (typeof header.value == "object") {
        try {
          this.getString(header.value).then(value => {
            header.value = value;
          });
        } catch (error) {
          trace.log("HarCollector.getLongHeaders; ERROR when getString", error);
        }
      }
    }
  },

  /**
   * Fetches the full text of a string.
   *
   * @param object | string stringGrip
   *        The long string grip containing the corresponding actor.
   *        If you pass in a plain string (by accident or because you're lazy),
   *        then a promise of the same string is simply returned.
   * @return object Promise
   *         A promise that is resolved when the full string contents
   *         are available, or rejected if something goes wrong.
   */
  getString: function (stringGrip) {
    let promise = this.webConsoleClient.getString(stringGrip);
    this.requests.push(promise);
    return promise;
  }
};

// Helpers

/**
 * Helper function that allows to wait for array of promises. It is
 * possible to dynamically add new promises in the provided array.
 * The function will wait even for the newly added promises.
 * (this isn't possible with the default Promise.all);
 */
function waitForAll(promises) {
  // Remove all from the original array and get clone of it.
  let clone = promises.splice(0, promises.length);

  // Wait for all promises in the given array.
  return Promise.all(clone).then(() => {
    // If there are new promises (in the original array)
    // to wait for - chain them!
    if (promises.length) {
      return waitForAll(promises);
    }

    return undefined;
  });
}

// Exports from this module
exports.HarCollector = HarCollector;
PK
!<�wf��Jchrome/devtools/modules/devtools/client/netmonitor/src/har/har-exporter.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Services = require("Services");
const FileSaver = require("devtools/client/shared/file-saver");
const JSZip = require("devtools/client/shared/vendor/jszip");
const clipboardHelper = require("devtools/shared/platform/clipboard");
const { HarBuilder } = require("./har-builder");

var uid = 1;

// Helper tracer. Should be generic sharable by other modules (bug 1171927)
const trace = {
  log: function (...args) {
  }
};

/**
 * This object represents the main public API designed to access
 * Network export logic. Clients, such as the Network panel itself,
 * should use this API to export collected HTTP data from the panel.
 */
const HarExporter = {
  // Public API

  /**
   * Save collected HTTP data from the Network panel into HAR file.
   *
   * @param Object options
   *        Configuration object
   *
   * The following options are supported:
   *
   * - includeResponseBodies {Boolean}: If set to true, HTTP response bodies
   *   are also included in the HAR file (can produce significantly bigger
   *   amount of data).
   *
   * - items {Array}: List of Network requests to be exported.
   *
   * - jsonp {Boolean}: If set to true the export format is HARP (support
   *   for JSONP syntax).
   *
   * - jsonpCallback {String}: Default name of JSONP callback (used for
   *   HARP format).
   *
   * - compress {Boolean}: If set to true the final HAR file is zipped.
   *   This represents great disk-space optimization.
   *
   * - defaultFileName {String}: Default name of the target HAR file.
   *   The default file name supports the format specifier %date to output the
   *   current date/time.
   *
   * - defaultLogDir {String}: Default log directory for automated logs.
   *
   * - id {String}: ID of the page (used in the HAR file).
   *
   * - title {String}: Title of the page (used in the HAR file).
   *
   * - forceExport {Boolean}: The result HAR file is created even if
   *   there are no HTTP entries.
   */
  async save(options) {
    // Set default options related to save operation.
    let defaultFileName = Services.prefs.getCharPref(
      "devtools.netmonitor.har.defaultFileName");
    let compress = Services.prefs.getBoolPref(
      "devtools.netmonitor.har.compress");

    trace.log("HarExporter.save; " + defaultFileName, options);

    let data = await this.fetchHarData(options);
    let fileName = this.getHarFileName(defaultFileName, options.jsonp, compress);

    if (compress) {
      data = await JSZip().file(fileName, data).generateAsync({
        compression: "DEFLATE",
        platform: Services.appinfo.OS === "WINNT" ? "DOS" : "UNIX",
        type: "blob",
      });
    }

    fileName = `${fileName}${compress ? ".zip" : ""}`;
    let blob = compress ? data : new Blob([data], { type: "application/json" });

    FileSaver.saveAs(blob, fileName, document);
  },

  formatDate(date) {
    let year = String(date.getFullYear() % 100).padStart(2, "0");
    let month = String(date.getMonth() + 1).padStart(2, "0");
    let day = String(date.getDate()).padStart(2, "0");
    let hour = String(date.getHours()).padStart(2, "0");
    let minutes = String(date.getMinutes()).padStart(2, "0");
    let seconds = String(date.getSeconds()).padStart(2, "0");

    return `${year}-${month}-${day} ${hour}-${minutes}-${seconds}`;
  },

  getHarFileName(defaultFileName, jsonp, compress) {
    let name = defaultFileName.replace(/%date/g, this.formatDate(new Date()));
    name = name.replace(/\:/gm, "-", "");
    name = name.replace(/\//gm, "_", "");
    return `${name}.${jsonp ? "harp" : "har"}`;
  },

  /**
   * Copy HAR string into the clipboard.
   *
   * @param Object options
   *        Configuration object, see save() for detailed description.
   */
  copy: function (options) {
    return this.fetchHarData(options).then(jsonString => {
      clipboardHelper.copyString(jsonString);
      return jsonString;
    });
  },

  /**
   * Get HAR data as JSON object.
   *
   * @param Object options
   *        Configuration object, see save() for detailed description.
   */
  getHar: function (options) {
    return this.fetchHarData(options).then(JSON.parse);
  },

  // Helpers

  fetchHarData: function (options) {
    // Generate page ID
    options.id = options.id || uid++;

    // Set default generic HAR export options.
    if (typeof options.jsonp != "boolean") {
      options.jsonp = Services.prefs.getBoolPref(
        "devtools.netmonitor.har.jsonp");
    }
    if (typeof options.includeResponseBodies != "boolean") {
      options.includeResponseBodies = Services.prefs.getBoolPref(
        "devtools.netmonitor.har.includeResponseBodies");
    }
    if (typeof options.jsonpCallback != "boolean") {
      options.jsonpCallback = Services.prefs.getCharPref(
        "devtools.netmonitor.har.jsonpCallback");
    }
    if (typeof options.forceExport != "boolean") {
      options.forceExport = Services.prefs.getBoolPref(
        "devtools.netmonitor.har.forceExport");
    }

    // Build HAR object.
    return this.buildHarData(options).then(har => {
      // Do not export an empty HAR file, unless the user
      // explicitly says so (using the forceExport option).
      if (!har.log.entries.length && !options.forceExport) {
        return Promise.resolve();
      }

      let jsonString = this.stringify(har);
      if (!jsonString) {
        return Promise.resolve();
      }

      // If JSONP is wanted, wrap the string in a function call
      if (options.jsonp) {
        // This callback name is also used in HAR Viewer by default.
        // http://www.softwareishard.com/har/viewer/
        let callbackName = options.jsonpCallback || "onInputData";
        jsonString = callbackName + "(" + jsonString + ");";
      }

      return jsonString;
    }).catch(function onError(err) {
      console.error(err);
    });
  },

  /**
   * Build HAR data object. This object contains all HTTP data
   * collected by the Network panel. The process is asynchronous
   * since it can involve additional RDP communication (e.g. resolving
   * long strings).
   */
  buildHarData: async function (options) {
    let { connector } = options;
    let {
      getTabTarget,
    } = connector;
    let {
      form: { title, url }
    } = getTabTarget();

    // Disconnect from redux actions/store.
    connector.enableActions(false);

    options = {
      ...options,
      title: title || url,
      getString: connector.getLongString,
      getTimingMarker: connector.getTimingMarker,
      requestData: connector.requestData,
    };

    // Build HAR object from collected data.
    let builder = new HarBuilder(options);
    let result = await builder.build();

    // Connect to redux actions again.
    connector.enableActions(true);

    return result;
  },

  /**
   * Build JSON string from the HAR data object.
   */
  stringify: function (har) {
    if (!har) {
      return null;
    }

    try {
      return JSON.stringify(har, null, "  ");
    } catch (err) {
      console.error(err);
      return undefined;
    }
  },
};

// Exports from this module
exports.HarExporter = HarExporter;
PK
!<W����Jchrome/devtools/modules/devtools/client/netmonitor/src/har/har-importer.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { TIMING_KEYS } = require("../constants");

var guid = 0;

/**
 * This object is responsible for importing HAR file. See HAR spec:
 * https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/HAR/Overview.html
 * http://www.softwareishard.com/blog/har-12-spec/
 */
var HarImporter = function (actions) {
  this.actions = actions;
};

HarImporter.prototype = {
  /**
   * This is the main method used to import HAR data.
   */
  import: function (har) {
    let json = JSON.parse(har);
    this.doImport(json);
  },

  doImport: function (har) {
    this.actions.clearRequests();

    // Helper map for pages.
    let pages = new Map();
    har.log.pages.forEach(page => {
      pages.set(page.id, page);
    });

    // Iterate all entries/requests and generate state.
    har.log.entries.forEach(entry => {
      let requestId = String(++guid);
      let startedMillis = Date.parse(entry.startedDateTime);

      // Add request
      this.actions.addRequest(requestId, {
        startedMillis: startedMillis,
        method: entry.request.method,
        url: entry.request.url,
        isXHR: false,
        cause: {
          loadingDocumentUri: "",
          stackTraceAvailable: false,
          type: "",
        },
        fromCache: false,
        fromServiceWorker: false,
      }, false);

      // Update request
      this.actions.updateRequest(requestId, {
        requestHeaders: {
          headers: entry.request.headers,
          headersSize: entry.request.headersSize,
          rawHeaders: "",
        },
        responseHeaders: {
          headers: entry.response.headers,
          headersSize: entry.response.headersSize,
          rawHeaders: "",
        },
        requestCookies: entry.request.cookies,
        responseCookies: entry.response.cookies,
        requestPostData: {
          postData: entry.request.postData || {},
          postDataDiscarded: false,
        },
        responseContent: {
          content: entry.response.content,
          contentDiscarded: false,
        },
        eventTimings: {
          timings: entry.timings,
        },
        totalTime: TIMING_KEYS.reduce((sum, type) => {
          let time = entry.timings[type];
          return (time != -1) ? (sum + time) : sum;
        }, 0),

        httpVersion: entry.request.httpVersion,
        contentSize: entry.response.content.size,
        mimeType: entry.response.content.mimeType,
        remoteAddress: entry.serverIPAddress,
        remotePort: entry.connection,
        status: entry.response.status,
        statusText: entry.response.statusText,
        transferredSize: entry.response.bodySize,
        securityState: entry._securityState,

        // Avoid auto-fetching data from the backend
        eventTimingsAvailable: false,
        requestCookiesAvailable: false,
        requestHeadersAvailable: false,
        responseContentAvailable: false,
        responseStartAvailable: false,
        responseCookiesAvailable: false,
        responseHeadersAvailable: false,
        securityInfoAvailable: false,
        requestPostDataAvailable: false,
      }, false);

      // Page timing markers
      let pageTimings = pages.get(entry.pageref).pageTimings;
      let onContentLoad = pageTimings.onContentLoad || 0;
      let onLoad = pageTimings.onLoad || 0;

      // Set 0 as the default value
      onContentLoad = (onContentLoad != -1) ? onContentLoad : 0;
      onLoad = (onLoad != -1) ? onLoad : 0;

      // Add timing markers
      if (onContentLoad > 0) {
        this.actions.addTimingMarker({
          name: "dom-interactive",
          time: startedMillis + onContentLoad,
        });
      }

      if (onLoad > 0) {
        this.actions.addTimingMarker({
          name: "dom-complete",
          time: startedMillis + onLoad,
        });
      }
    });
  },
};

// Exports from this module
exports.HarImporter = HarImporter;
PK
!<=�σGchrome/devtools/modules/devtools/client/netmonitor/src/har/har-utils.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 mozilla/reject-some-requires */

"use strict";

const Services = require("Services");
const { Ci, Cc, CC } = require("chrome");
const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
const { gDevTools } = require("devtools/client/framework/devtools");

XPCOMUtils.defineLazyGetter(this, "ZipWriter", function () {
  return CC("@mozilla.org/zipwriter;1", "nsIZipWriter");
});

XPCOMUtils.defineLazyGetter(this, "LocalFile", function () {
  return new CC("@mozilla.org/file/local;1", "nsIFile", "initWithPath");
});

XPCOMUtils.defineLazyGetter(this, "getMostRecentBrowserWindow", function () {
  return Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
});

const OPEN_FLAGS = {
  RDONLY: parseInt("0x01", 16),
  WRONLY: parseInt("0x02", 16),
  CREATE_FILE: parseInt("0x08", 16),
  APPEND: parseInt("0x10", 16),
  TRUNCATE: parseInt("0x20", 16),
  EXCL: parseInt("0x80", 16)
};

function formatDate(date) {
  let year = String(date.getFullYear() % 100).padStart(2, "0");
  let month = String(date.getMonth() + 1).padStart(2, "0");
  let day = String(date.getDate()).padStart(2, "0");
  let hour = String(date.getHours()).padStart(2, "0");
  let minutes = String(date.getMinutes()).padStart(2, "0");
  let seconds = String(date.getSeconds()).padStart(2, "0");

  return `${year}-${month}-${day} ${hour}-${minutes}-${seconds}`;
}

/**
 * Helper API for HAR export features.
 */
var HarUtils = {
  getHarFileName: function (defaultFileName, jsonp, compress) {
    let extension = jsonp ? ".harp" : ".har";

    let now = new Date();
    let name = defaultFileName.replace(/%date/g, formatDate(now));
    name = name.replace(/\:/gm, "-", "");
    name = name.replace(/\//gm, "_", "");

    let fileName = name + extension;

    // Default file extension is zip if compressing is on.
    if (compress) {
      fileName += ".zip";
    }

    return fileName;
  },

  /**
   * Save HAR string into a given file. The file might be compressed
   * if specified in the options.
   *
   * @param {File} file Target file where the HAR string (JSON)
   * should be stored.
   * @param {String} jsonString HAR data (JSON or JSONP)
   * @param {Boolean} compress The result file is zipped if set to true.
   */
  saveToFile: function (file, jsonString, compress) {
    let openFlags = OPEN_FLAGS.WRONLY | OPEN_FLAGS.CREATE_FILE |
      OPEN_FLAGS.TRUNCATE;

    try {
      let foStream = Cc["@mozilla.org/network/file-output-stream;1"]
        .createInstance(Ci.nsIFileOutputStream);

      let permFlags = parseInt("0666", 8);
      foStream.init(file, openFlags, permFlags, 0);

      let convertor = Cc["@mozilla.org/intl/converter-output-stream;1"]
        .createInstance(Ci.nsIConverterOutputStream);
      convertor.init(foStream, "UTF-8");

      // The entire jsonString can be huge so, write the data in chunks.
      let chunkLength = 1024 * 1024;
      for (let i = 0; i <= jsonString.length; i++) {
        let data = jsonString.substr(i, chunkLength + 1);
        if (data) {
          convertor.writeString(data);
        }

        i = i + chunkLength;
      }

      // this closes foStream
      convertor.close();
    } catch (err) {
      console.error(err);
      return false;
    }

    // If no compressing then bail out.
    if (!compress) {
      return true;
    }

    // Remember name of the original file, it'll be replaced by a zip file.
    let originalFilePath = file.path;
    let originalFileName = file.leafName;

    try {
      // Rename using unique name (the file is going to be removed).
      file.moveTo(null, "temp" + (new Date()).getTime() + "temphar");

      // Create compressed file with the original file path name.
      let zipFile = Cc["@mozilla.org/file/local;1"]
        .createInstance(Ci.nsIFile);
      zipFile.initWithPath(originalFilePath);

      // The file within the zipped file doesn't use .zip extension.
      let fileName = originalFileName;
      if (fileName.indexOf(".zip") == fileName.length - 4) {
        fileName = fileName.substr(0, fileName.indexOf(".zip"));
      }

      let zip = new ZipWriter();
      zip.open(zipFile, openFlags);
      zip.addEntryFile(fileName, Ci.nsIZipWriter.COMPRESSION_DEFAULT,
        file, false);
      zip.close();

      // Remove the original file (now zipped).
      file.remove(true);
      return true;
    } catch (err) {
      console.error(err);

      // Something went wrong (disk space?) rename the original file back.
      file.moveTo(null, originalFileName);
    }

    return false;
  },

  getLocalDirectory: function (path) {
    let dir;

    if (!path) {
      dir = Services.dirsvc.get("ProfD", Ci.nsIFile);
      dir.append("har");
      dir.append("logs");
    } else {
      dir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
      dir.initWithPath(path);
    }

    return dir;
  },
};

// Exports from this module
exports.HarUtils = HarUtils;
PK
!<�����Mchrome/devtools/modules/devtools/client/netmonitor/src/har/toolbox-overlay.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Services = require("Services");

loader.lazyRequireGetter(this, "HarAutomation", "devtools/client/netmonitor/src/har/har-automation", true);

// Map of all created overlays. There is always one instance of
// an overlay per Toolbox instance (i.e. one per browser tab).
const overlays = new WeakMap();

/**
 * This object is responsible for initialization and cleanup for HAR
 * export feature. It represents an overlay for the Toolbox
 * following the same life time by listening to its events.
 *
 * HAR APIs are designed for integration with tools (such as Selenium)
 * that automates the browser. Primarily, it is for automating web apps
 * and getting HAR file for every loaded page.
 */
function ToolboxOverlay(toolbox) {
  this.toolbox = toolbox;

  this.onInit = this.onInit.bind(this);
  this.onDestroy = this.onDestroy.bind(this);

  this.toolbox.on("ready", this.onInit);
  this.toolbox.on("destroy", this.onDestroy);
}

ToolboxOverlay.prototype = {
  /**
   * Executed when the toolbox is ready.
   */
  onInit: function () {
    let autoExport = Services.prefs.getBoolPref(
      "devtools.netmonitor.har.enableAutoExportToFile");

    if (!autoExport) {
      return;
    }

    this.initAutomation();
  },

  /**
   * Executed when the toolbox is destroyed.
   */
  onDestroy: function (eventId, toolbox) {
    this.destroyAutomation();
  },

  // Automation

  initAutomation: function () {
    this.automation = new HarAutomation(this.toolbox);
  },

  destroyAutomation: function () {
    if (this.automation) {
      this.automation.destroy();
    }
  },
};

// Registration
function register(toolbox) {
  if (overlays.has(toolbox)) {
    throw Error("There is an existing overlay for the toolbox");
  }

  // Instantiate an overlay for the toolbox.
  let overlay = new ToolboxOverlay(toolbox);
  overlays.set(toolbox, overlay);
}

function get(toolbox) {
  return overlays.get(toolbox);
}

// Exports from this module
exports.register = register;
exports.get = get;
PK
!<��^��Mchrome/devtools/modules/devtools/client/netmonitor/src/middleware/batching.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { BATCH_ACTIONS, BATCH_ENABLE, BATCH_RESET } = require("../constants");

const REQUESTS_REFRESH_RATE = 50; // ms

/**
 * Middleware that watches for actions with a "batch = true" value in their meta field.
 * These actions are queued and dispatched as one batch after a timeout.
 * Special actions that are handled by this middleware:
 * - BATCH_ENABLE can be used to enable and disable the batching.
 * - BATCH_RESET discards the actions that are currently in the queue.
 */
function batchingMiddleware(store) {
  return next => {
    let queuedActions = [];
    let enabled = true;
    let flushTask = null;

    return action => {
      if (action.type === BATCH_ENABLE) {
        return setEnabled(action.enabled);
      }

      if (action.type === BATCH_RESET) {
        return resetQueue();
      }

      if (action.meta && action.meta.batch) {
        if (!enabled) {
          next(action);
          return Promise.resolve();
        }

        queuedActions.push(action);

        if (!flushTask) {
          flushTask = new DelayedTask(flushActions, REQUESTS_REFRESH_RATE);
        }

        return flushTask.promise;
      }

      return next(action);
    };

    function setEnabled(value) {
      enabled = value;

      // If disabling the batching, flush the actions that have been queued so far
      if (!enabled && flushTask) {
        flushTask.runNow();
      }
    }

    function resetQueue() {
      queuedActions = [];

      if (flushTask) {
        flushTask.cancel();
        flushTask = null;
      }
    }

    function flushActions() {
      const actions = queuedActions;
      queuedActions = [];

      next({
        type: BATCH_ACTIONS,
        actions,
      });

      flushTask = null;
    }
  };
}

/**
 * Create a delayed task that calls the specified task function after a delay.
 */
function DelayedTask(taskFn, delay) {
  this._promise = new Promise((resolve, reject) => {
    this.runTask = (cancel) => {
      if (cancel) {
        reject("Task cancelled");
      } else {
        taskFn();
        resolve();
      }
      this.runTask = null;
    };
    this.timeout = setTimeout(this.runTask, delay);
  }).catch(console.error);
}

DelayedTask.prototype = {
  /**
   * Return a promise that is resolved after the task is performed or canceled.
   */
  get promise() {
    return this._promise;
  },

  /**
   * Cancel the execution of the task.
   */
  cancel() {
    clearTimeout(this.timeout);
    if (this.runTask) {
      this.runTask(true);
    }
  },

  /**
   * Execute the scheduled task immediately, without waiting for the timeout.
   * Resolves the promise correctly.
   */
  runNow() {
    clearTimeout(this.timeout);
    if (this.runTask) {
      this.runTask();
    }
  }
};

module.exports = batchingMiddleware;
PK
!<�B�>>Jchrome/devtools/modules/devtools/client/netmonitor/src/middleware/prefs.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Services = require("Services");
const {
  ENABLE_REQUEST_FILTER_TYPE_ONLY,
  RESET_COLUMNS,
  TOGGLE_COLUMN,
  TOGGLE_REQUEST_FILTER_TYPE,
  ENABLE_PERSISTENT_LOGS,
  DISABLE_BROWSER_CACHE,
} = require("../constants");

/**
  * Update the relevant prefs when:
  *   - a column has been toggled
  *   - a filter type has been set
  */
function prefsMiddleware(store) {
  return next => action => {
    const res = next(action);
    switch (action.type) {
      case ENABLE_REQUEST_FILTER_TYPE_ONLY:
      case TOGGLE_REQUEST_FILTER_TYPE:
        let filters = Object.entries(store.getState().filters.requestFilterTypes)
          .filter(([type, check]) => check)
          .map(([type, check]) => type);
        Services.prefs.setCharPref(
          "devtools.netmonitor.filters", JSON.stringify(filters));
        break;
      case ENABLE_PERSISTENT_LOGS:
        Services.prefs.setBoolPref(
          "devtools.netmonitor.persistlog", store.getState().ui.persistentLogsEnabled);
        break;
      case DISABLE_BROWSER_CACHE:
        Services.prefs.setBoolPref(
          "devtools.cache.disabled", store.getState().ui.browserCacheDisabled);
        break;
      case TOGGLE_COLUMN:
      case RESET_COLUMNS:
        let visibleColumns = [];
        let columns = store.getState().ui.columns;
        for (let column in columns) {
          if (columns[column]) {
            visibleColumns.push(column);
          }
        }
        Services.prefs.setCharPref(
          "devtools.netmonitor.visibleColumns", JSON.stringify(visibleColumns));
        break;
    }
    return res;
  };
}

module.exports = prefsMiddleware;
PK
!<ň'%��Nchrome/devtools/modules/devtools/client/netmonitor/src/middleware/recording.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  TOGGLE_RECORDING,
} = require("../constants");

const {
  getRecordingState,
} = require("../selectors/index");

/**
 * Start/stop HTTP traffic recording.
 *
 * The UI state of the toolbar toggle button is stored in UI
 * reducer and the backend connection is managed here in the
 * middleware.
 */
function recordingMiddleware(connector) {
  return store => next => action => {
    const res = next(action);

    // Pause/resume HTTP monitoring according to
    // the user action.
    if (action.type === TOGGLE_RECORDING) {
      let recording = getRecordingState(store.getState());
      if (recording) {
        connector.resume();
      } else {
        connector.pause();
      }
    }

    return res;
  };
}

module.exports = recordingMiddleware;
PK
!<�F���Jchrome/devtools/modules/devtools/client/netmonitor/src/middleware/thunk.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 `dispatch` and `getState`,
 * allowing the action to create multiple actions (most likely
 * asynchronously).
 */
function thunk({ dispatch, getState }) {
  return next => action => {
    return (typeof action === "function")
      ? action(dispatch, getState)
      : next(action);
  };
}

module.exports = thunk;
PK
!<%�b�Kchrome/devtools/modules/devtools/client/netmonitor/src/reducers/batching.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { BATCH_ACTIONS } = require("../constants");

/**
 * A reducer to handle batched actions. For each action in the BATCH_ACTIONS array,
 * the reducer is called successively on the array of batched actions, resulting in
 * only one state update.
 */
function batchingReducer(nextReducer) {
  return function reducer(state, action) {
    switch (action.type) {
      case BATCH_ACTIONS:
        return action.actions.reduce(reducer, state);
      default:
        return nextReducer(state, action);
    }
  };
}

module.exports = batchingReducer;
PK
!<��#+	+	Jchrome/devtools/modules/devtools/client/netmonitor/src/reducers/filters.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  ENABLE_REQUEST_FILTER_TYPE_ONLY,
  TOGGLE_REQUEST_FILTER_TYPE,
  SET_REQUEST_FILTER_TEXT,
  FILTER_TAGS
} = require("../constants");

function FilterTypes(overrideParams = {}) {
  let allFilterTypes = ["all"].concat(FILTER_TAGS);
  // filter only those keys which are valid filter tags
  overrideParams = Object.keys(overrideParams)
    .filter(key => allFilterTypes.includes(key))
    .reduce((obj, key) => {
      obj[key] = overrideParams[key];
      return obj;
    }, {});
  let filterTypes = allFilterTypes
    .reduce((o, tag) => Object.assign(o, { [tag]: false }), {});
  return Object.assign({}, filterTypes, overrideParams);
}

function Filters(overrideParams = {}) {
  return Object.assign({
    requestFilterTypes: new FilterTypes({ all: true }),
    requestFilterText: "",
  }, overrideParams);
}

function toggleRequestFilterType(state, action) {
  let { filter } = action;
  let newState;

  // Ignore unknown filter type
  if (!state.hasOwnProperty(filter)) {
    return state;
  }
  if (filter === "all") {
    return new FilterTypes({ all: true });
  }

  newState = { ...state };
  newState.all = false;
  newState[filter] = !state[filter];

  if (!Object.values(newState).includes(true)) {
    newState = new FilterTypes({ all: true });
  }

  return newState;
}

function enableRequestFilterTypeOnly(state, action) {
  let { filter } = action;

  // Ignore unknown filter type
  if (!state.hasOwnProperty(filter)) {
    return state;
  }

  return new FilterTypes({ [filter]: true });
}

function filters(state = new Filters(), action) {
  state = { ...state };
  switch (action.type) {
    case ENABLE_REQUEST_FILTER_TYPE_ONLY:
      state.requestFilterTypes =
        enableRequestFilterTypeOnly(state.requestFilterTypes, action);
      break;
    case TOGGLE_REQUEST_FILTER_TYPE:
      state.requestFilterTypes =
        toggleRequestFilterType(state.requestFilterTypes, action);
      break;
    case SET_REQUEST_FILTER_TEXT:
      state.requestFilterText = action.text;
      break;
    default:
      break;
  }
  return state;
}

module.exports = {
  FilterTypes,
  Filters,
  filters
};
PK
!<�Ŭ���Hchrome/devtools/modules/devtools/client/netmonitor/src/reducers/index.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 } = require("devtools/client/shared/vendor/redux");
const batchingReducer = require("./batching");
const { requestsReducer } = require("./requests");
const { sortReducer } = require("./sort");
const { filters } = require("./filters");
const { timingMarkers } = require("./timing-markers");
const { ui } = require("./ui");

module.exports = batchingReducer(
  combineReducers({
    requests: requestsReducer,
    sort: sortReducer,
    filters,
    timingMarkers,
    ui,
  })
);
PK
!<Z�?A��Kchrome/devtools/modules/devtools/client/netmonitor/src/reducers/requests.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  getUrlDetails,
  processNetworkUpdates,
} = require("../utils/request-utils");
const {
  ADD_REQUEST,
  CLEAR_REQUESTS,
  CLONE_SELECTED_REQUEST,
  OPEN_NETWORK_DETAILS,
  REMOVE_SELECTED_CUSTOM_REQUEST,
  SELECT_REQUEST,
  SEND_CUSTOM_REQUEST,
  TOGGLE_RECORDING,
  UPDATE_REQUEST,
} = require("../constants");

/**
 * This structure stores list of all HTTP requests received
 * from the backend. It's using plain JS structures to store
 * data instead of ImmutableJS, which is performance expensive.
 */
function Requests() {
  return {
    // Map with all requests (key = actor ID, value = request object)
    requests: new Map(),
    // Selected request ID
    selectedId: null,
    preselectedId: null,
    // True if the monitor is recording HTTP traffic
    recording: true,
    // Auxiliary fields to hold requests stats
    firstStartedMillis: +Infinity,
    lastEndedMillis: -Infinity,
  };
}

/**
 * This reducer is responsible for maintaining list of request
 * within the Network panel.
 */
function requestsReducer(state = Requests(), action) {
  switch (action.type) {
    // Appending new request into the list/map.
    case ADD_REQUEST: {
      let nextState = { ...state };

      let newRequest = {
        id: action.id,
        ...action.data,
        urlDetails: getUrlDetails(action.data.url),
      };

      nextState.requests = mapSet(state.requests, newRequest.id, newRequest);

      // Update the started/ended timestamps.
      let { startedMillis } = action.data;
      if (startedMillis < state.firstStartedMillis) {
        nextState.firstStartedMillis = startedMillis;
      }
      if (startedMillis > state.lastEndedMillis) {
        nextState.lastEndedMillis = startedMillis;
      }

      // Select the request if it was preselected and there is no other selection.
      if (state.preselectedId && state.preselectedId === action.id) {
        nextState.selectedId = state.selectedId || state.preselectedId;
        nextState.preselectedId = null;
      }

      return nextState;
    }

    // Update an existing request (with received data).
    case UPDATE_REQUEST: {
      let { requests, lastEndedMillis } = state;

      let request = requests.get(action.id);
      if (!request) {
        return state;
      }

      request = {
        ...request,
        ...processNetworkUpdates(action.data),
      };
      let requestEndTime = request.startedMillis +
        (request.eventTimings ? request.eventTimings.totalTime : 0);

      return {
        ...state,
        requests: mapSet(state.requests, action.id, request),
        lastEndedMillis: requestEndTime > lastEndedMillis ?
          requestEndTime : lastEndedMillis,
      };
    }

    // Remove all requests in the list. Create fresh new state
    // object, but keep value of the `recording` field.
    case CLEAR_REQUESTS: {
      return {
        ...Requests(),
        recording: state.recording,
      };
    }

    // Select specific request.
    case SELECT_REQUEST: {
      return {
        ...state,
        selectedId: action.id,
      };
    }

    // Clone selected request for re-send.
    case CLONE_SELECTED_REQUEST: {
      let { requests, selectedId } = state;

      if (!selectedId) {
        return state;
      }

      let clonedRequest = requests.get(selectedId);
      if (!clonedRequest) {
        return state;
      }

      let newRequest = {
        id: clonedRequest.id + "-clone",
        method: clonedRequest.method,
        url: clonedRequest.url,
        urlDetails: clonedRequest.urlDetails,
        requestHeaders: clonedRequest.requestHeaders,
        requestPostData: clonedRequest.requestPostData,
        requestPostDataAvailable: clonedRequest.requestPostDataAvailable,
        isCustom: true
      };

      return {
        ...state,
        requests: mapSet(requests, newRequest.id, newRequest),
        selectedId: newRequest.id,
      };
    }

    // Removing temporary cloned request (created for re-send, but canceled).
    case REMOVE_SELECTED_CUSTOM_REQUEST: {
      return closeCustomRequest(state);
    }

    // Re-sending an existing request.
    case SEND_CUSTOM_REQUEST: {
      // When a new request with a given id is added in future, select it immediately.
      // where we know in advance the ID of the request, at a time when it
      // wasn't sent yet.
      return closeCustomRequest({...state, preselectedId: action.id});
    }

    // Pause/resume button clicked.
    case TOGGLE_RECORDING: {
      return {
        ...state,
        recording: !state.recording,
      };
    }

    // Side bar with request details opened.
    case OPEN_NETWORK_DETAILS: {
      let nextState = { ...state };
      if (!action.open) {
        nextState.selectedId = null;
        return nextState;
      }

      if (!state.selectedId && action.defaultSelectedId) {
        nextState.selectedId = action.defaultSelectedId;
        return nextState;
      }

      return state;
    }

    default:
      return state;
  }
}

// Helpers

/**
 * Remove the currently selected custom request.
 */
function closeCustomRequest(state) {
  let { requests, selectedId } = state;

  if (!selectedId) {
    return state;
  }

  let removedRequest = requests.get(selectedId);

  // Only custom requests can be removed
  if (!removedRequest || !removedRequest.isCustom) {
    return state;
  }

  return {
    ...state,
    requests: mapDelete(state.requests, selectedId),
    selectedId: null,
  };
}

/**
 * Append new item into existing map and return new map.
 */
function mapSet(map, key, value) {
  let newMap = new Map(map);
  return newMap.set(key, value);
}

/**
 * Remove an item from existing map and return new map.
 */
function mapDelete(map, key) {
  let newMap = new Map(map);
  newMap.delete(key);
  return newMap;
}

module.exports = {
  Requests,
  requestsReducer,
};
PK
!<T��@@Gchrome/devtools/modules/devtools/client/netmonitor/src/reducers/sort.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { SORT_BY } = require("../constants");

function Sort() {
  return {
    // null means: sort by "waterfall", but don't highlight the table header
    type: null,
    ascending: true,
  };
}

function sortReducer(state = new Sort(), action) {
  switch (action.type) {
    case SORT_BY: {
      state = { ...state };
      if (action.sortType == state.type) {
        state.ascending = !state.ascending;
      } else {
        state.type = action.sortType;
        state.ascending = true;
      }
      return state;
    }
    default:
      return state;
  }
}

module.exports = {
  Sort,
  sortReducer
};
PK
!<
Y�Z@@Qchrome/devtools/modules/devtools/client/netmonitor/src/reducers/timing-markers.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  ADD_TIMING_MARKER,
  CLEAR_TIMING_MARKERS,
  CLEAR_REQUESTS,
} = require("../constants");

function TimingMarkers() {
  return {
    firstDocumentDOMContentLoadedTimestamp: -1,
    firstDocumentLoadTimestamp: -1,
  };
}

function addTimingMarker(state, action) {
  state = { ...state };

  if (action.marker.name === "dom-interactive" &&
      state.firstDocumentDOMContentLoadedTimestamp === -1) {
    state.firstDocumentDOMContentLoadedTimestamp = action.marker.time;
    return state;
  }

  if (action.marker.name === "dom-complete" &&
      state.firstDocumentLoadTimestamp === -1) {
    state.firstDocumentLoadTimestamp = action.marker.time;
    return state;
  }

  return state;
}

function clearTimingMarkers(state) {
  return new TimingMarkers();
}

function timingMarkers(state = new TimingMarkers(), action) {
  switch (action.type) {
    case ADD_TIMING_MARKER:
      return addTimingMarker(state, action);

    case CLEAR_REQUESTS:
    case CLEAR_TIMING_MARKERS:
      return clearTimingMarkers(state);

    default:
      return state;
  }
}

module.exports = {
  TimingMarkers,
  timingMarkers
};
PK
!<މk�88Echrome/devtools/modules/devtools/client/netmonitor/src/reducers/ui.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Services = require("Services");
const {
  CLEAR_REQUESTS,
  OPEN_NETWORK_DETAILS,
  RESIZE_NETWORK_DETAILS,
  ENABLE_PERSISTENT_LOGS,
  DISABLE_BROWSER_CACHE,
  OPEN_STATISTICS,
  REMOVE_SELECTED_CUSTOM_REQUEST,
  RESET_COLUMNS,
  RESPONSE_HEADERS,
  SELECT_DETAILS_PANEL_TAB,
  SEND_CUSTOM_REQUEST,
  SELECT_REQUEST,
  TOGGLE_COLUMN,
  WATERFALL_RESIZE,
  PANELS,
} = require("../constants");

const cols = {
  status: true,
  method: true,
  file: true,
  protocol: false,
  scheme: false,
  domain: true,
  remoteip: false,
  cause: true,
  type: true,
  cookies: false,
  setCookies: false,
  transferred: true,
  contentSize: true,
  startTime: false,
  endTime: false,
  responseTime: false,
  duration: false,
  latency: false,
  waterfall: true,
};
function Columns() {
  return Object.assign(
    cols,
    RESPONSE_HEADERS.reduce((acc, header) => Object.assign(acc, { [header]: false }), {})
  );
}

function UI(initialState = {}) {
  return {
    columns: Columns(),
    detailsPanelSelectedTab: PANELS.HEADERS,
    networkDetailsOpen: false,
    networkDetailsWidth: null,
    networkDetailsHeight: null,
    persistentLogsEnabled: Services.prefs.getBoolPref("devtools.netmonitor.persistlog"),
    browserCacheDisabled: Services.prefs.getBoolPref("devtools.cache.disabled"),
    statisticsOpen: false,
    waterfallWidth: null,
    ...initialState,
  };
}

function resetColumns(state) {
  return {
    ...state,
    columns: Columns()
  };
}

function resizeWaterfall(state, action) {
  return {
    ...state,
    waterfallWidth: action.width
  };
}

function openNetworkDetails(state, action) {
  return {
    ...state,
    networkDetailsOpen: action.open
  };
}

function resizeNetworkDetails(state, action) {
  return {
    ...state,
    networkDetailsWidth: action.width,
    networkDetailsHeight: action.height,
  };
}

function enablePersistentLogs(state, action) {
  return {
    ...state,
    persistentLogsEnabled: action.enabled
  };
}

function disableBrowserCache(state, action) {
  return {
    ...state,
    browserCacheDisabled: action.disabled
  };
}

function openStatistics(state, action) {
  return {
    ...state,
    statisticsOpen: action.open
  };
}

function setDetailsPanelTab(state, action) {
  return {
    ...state,
    detailsPanelSelectedTab: action.id
  };
}

function toggleColumn(state, action) {
  let { column } = action;

  if (!state.columns.hasOwnProperty(column)) {
    return state;
  }

  return {
    ...state,
    columns: {
      ...state.columns,
      [column]: !state.columns[column]
    }
  };
}

function ui(state = UI(), action) {
  switch (action.type) {
    case CLEAR_REQUESTS:
      return openNetworkDetails(state, { open: false });
    case OPEN_NETWORK_DETAILS:
      return openNetworkDetails(state, action);
    case RESIZE_NETWORK_DETAILS:
      return resizeNetworkDetails(state, action);
    case ENABLE_PERSISTENT_LOGS:
      return enablePersistentLogs(state, action);
    case DISABLE_BROWSER_CACHE:
      return disableBrowserCache(state, action);
    case OPEN_STATISTICS:
      return openStatistics(state, action);
    case RESET_COLUMNS:
      return resetColumns(state);
    case REMOVE_SELECTED_CUSTOM_REQUEST:
    case SEND_CUSTOM_REQUEST:
      return openNetworkDetails(state, { open: false });
    case SELECT_DETAILS_PANEL_TAB:
      return setDetailsPanelTab(state, action);
    case SELECT_REQUEST:
      return openNetworkDetails(state, { open: true });
    case TOGGLE_COLUMN:
      return toggleColumn(state, action);
    case WATERFALL_RESIZE:
      return resizeWaterfall(state, action);
    default:
      return state;
  }
}

module.exports = {
  Columns,
  UI,
  ui
};
PK
!<��"��Ichrome/devtools/modules/devtools/client/netmonitor/src/selectors/index.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 requests = require("./requests");
const timingMarkers = require("./timing-markers");
const ui = require("./ui");

Object.assign(exports,
  requests,
  timingMarkers,
  ui
);
PK
!<[����Lchrome/devtools/modules/devtools/client/netmonitor/src/selectors/requests.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { createSelector } = require("devtools/client/shared/vendor/reselect");
const { Filters, isFreetextMatch } = require("../utils/filter-predicates");
const { Sorters } = require("../utils/sort-predicates");

/**
 * Take clones into account when sorting.
 * If a request is a clone, use the original request for comparison.
 * If one of the compared request is a clone of the other, sort them next to each other.
 */
function sortWithClones(requests, sorter, a, b) {
  const aId = a.id, bId = b.id;

  if (aId.endsWith("-clone")) {
    const aOrigId = aId.replace(/-clone$/, "");
    if (aOrigId === bId) {
      return +1;
    }
    a = requests.get(aOrigId);
  }

  if (bId.endsWith("-clone")) {
    const bOrigId = bId.replace(/-clone$/, "");
    if (bOrigId === aId) {
      return -1;
    }
    b = requests.get(bOrigId);
  }

  return sorter(a, b);
}

const getFilterFn = createSelector(
  state => state.filters,
  filters => r => {
    const matchesType = Object.keys(filters.requestFilterTypes).some(filter => {
      return filters.requestFilterTypes[filter] && Filters[filter] && Filters[filter](r);
    });
    return matchesType && isFreetextMatch(r, filters.requestFilterText);
  }
);

const getTypeFilterFn = createSelector(
  state => state.filters,
  filters => r => {
    return Object.keys(filters.requestFilterTypes).some(filter => {
      return filters.requestFilterTypes[filter] && Filters[filter] && Filters[filter](r);
    });
  }
);

const getSortFn = createSelector(
  state => state.requests,
  state => state.sort,
  ({ requests }, sort) => {
    const sorter = Sorters[sort.type || "waterfall"];
    const ascending = sort.ascending ? +1 : -1;
    return (a, b) => ascending * sortWithClones(requests, sorter, a, b);
  }
);

const getSortedRequests = createSelector(
  state => state.requests,
  getSortFn,
  ({ requests }, sortFn) => {
    let arr = [...requests.values()].sort(sortFn);
    arr.get = index => arr[index];
    arr.isEmpty = () => this.length == 0;
    arr.size = arr.length;
    return arr;
  }
);

const getDisplayedRequests = createSelector(
  state => state.requests,
  getFilterFn,
  getSortFn,
  ({ requests }, filterFn, sortFn) => {
    let arr = [...requests.values()].filter(filterFn).sort(sortFn);
    arr.get = index => arr[index];
    arr.isEmpty = () => this.length == 0;
    arr.size = arr.length;
    return arr;
  }
);

const getTypeFilteredRequests = createSelector(
  state => state.requests,
  getTypeFilterFn,
  ({ requests }, filterFn) => [...requests.values()].filter(filterFn)
);

const getDisplayedRequestsSummary = createSelector(
  getDisplayedRequests,
  state => state.requests.lastEndedMillis - state.requests.firstStartedMillis,
  (requests, totalMillis) => {
    if (requests.size == 0) {
      return { count: 0, bytes: 0, millis: 0 };
    }

    const totalBytes = requests.reduce((totals, item) => {
      if (typeof item.contentSize == "number") {
        totals.contentSize += item.contentSize;
      }

      if (typeof item.transferredSize == "number" && !item.fromCache) {
        totals.transferredSize += item.transferredSize;
      }

      return totals;
    }, { contentSize: 0, transferredSize: 0 });

    return {
      count: requests.size,
      contentSize: totalBytes.contentSize,
      millis: totalMillis,
      transferredSize: totalBytes.transferredSize,
    };
  }
);

const getSelectedRequest = createSelector(
  state => state.requests,
  ({ selectedId, requests }) => selectedId ? requests.get(selectedId) : undefined
);

const isSelectedRequestVisible = createSelector(
  state => state.requests,
  getDisplayedRequests,
  ({ selectedId }, displayedRequests) =>
    displayedRequests.some(r => r.id === selectedId)
);

function getRequestById(state, id) {
  return state.requests.requests.get(id);
}

function getDisplayedRequestById(state, id) {
  return getDisplayedRequests(state).find(r => r.id === id);
}

/**
 * Returns the current recording boolean state (HTTP traffic is
 * monitored or not monitored)
 */
function getRecordingState(state) {
  return state.requests.recording;
}

module.exports = {
  getDisplayedRequestById,
  getDisplayedRequests,
  getDisplayedRequestsSummary,
  getRecordingState,
  getRequestById,
  getSelectedRequest,
  getSortedRequests,
  getTypeFilteredRequests,
  isSelectedRequestVisible,
};
PK
!<�|����Rchrome/devtools/modules/devtools/client/netmonitor/src/selectors/timing-markers.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 getDisplayedTimingMarker(state, marker) {
  let value = state.timingMarkers[marker];
  if (value == -1) {
    return value;
  }

  return value - state.requests.firstStartedMillis;
}

module.exports = {
  getDisplayedTimingMarker,
};
PK
!<Q%cۀ�Fchrome/devtools/modules/devtools/client/netmonitor/src/selectors/ui.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { createSelector } = require("devtools/client/shared/vendor/reselect");
const { REQUESTS_WATERFALL } = require("../constants");
const { getDisplayedRequests } = require("./requests");

const EPSILON = 0.001;

function isNetworkDetailsToggleButtonDisabled(state) {
  return getDisplayedRequests(state).length == 0;
}

const getWaterfallScale = createSelector(
  state => state.requests,
  state => state.timingMarkers,
  state => state.ui,
  (requests, timingMarkers, ui) => {
    if (requests.firstStartedMillis === +Infinity || ui.waterfallWidth === null) {
      return null;
    }

    const lastEventMillis = Math.max(requests.lastEndedMillis,
                                     timingMarkers.firstDocumentDOMContentLoadedTimestamp,
                                     timingMarkers.firstDocumentLoadTimestamp);
    const longestWidth = lastEventMillis - requests.firstStartedMillis;

  // Reduce 20px for the last request's requests-list-timings-total
    return Math.min(Math.max(
      (ui.waterfallWidth - REQUESTS_WATERFALL.LABEL_WIDTH - 20) / longestWidth,
      EPSILON), 1);
  }
);

module.exports = {
  isNetworkDetailsToggleButtonDisabled,
  getWaterfallScale,
};
PK
!<����\chrome/devtools/modules/devtools/client/netmonitor/src/utils/filter-autocomplete-provider.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { FILTER_FLAGS } = require("../constants");

/**
 * Generates a value for the given filter
 * ie. if flag = status-code, will generate "200" from the given request item.
 * For flags related to cookies, it might generate an array based on the request
 * ie. ["cookie-name-1", "cookie-name-2", ...]
 *
 * @param {string} flag - flag specified in filter, ie. "status-code"
 * @param {object} request - Network request item
 * @return {string|Array} - The output is a string or an array based on the request
 */
function getAutocompleteValuesForFlag(flag, request) {
  let values = [];
  let { responseCookies = { cookies: [] } } = request;
  responseCookies = responseCookies.cookies || responseCookies;

  switch (flag) {
    case "status-code":
      // Sometimes status comes as Number
      values.push(String(request.status));
      break;
    case "scheme":
      values.push(request.urlDetails.scheme);
      break;
    case "domain":
      values.push(request.urlDetails.host);
      break;
    case "remote-ip":
      values.push(request.remoteAddress);
      break;
    case "cause":
      values.push(request.cause.type);
      break;
    case "mime-type":
      values.push(request.mimeType.replace(/;.+/, ""));
      break;
    case "set-cookie-name":
      values = responseCookies.map(c => c.name);
      break;
    case "set-cookie-value":
      values = responseCookies.map(c => c.value);
      break;
    case "set-cookie-domain":
      values = responseCookies.map(c => c.hasOwnProperty("domain") ?
          c.domain : request.urlDetails.host);
      break;
    case "is":
      values = ["cached", "from-cache", "running"];
      break;
    case "has-response-header":
      // Some requests not having responseHeaders..?
      values = request.responseHeaders ?
        request.responseHeaders.headers.map(h => h.name) : [];
      break;
    case "protocol":
      values.push(request.httpVersion);
      break;
    case "method":
    default:
      values.push(request[flag]);
  }

  return values;
}

/**
 * For a given lastToken passed ie. "is:", returns an array of populated flag
 * values for consumption in autocompleteProvider
 * ie. ["is:cached", "is:running", "is:from-cache"]
 *
 * @param {string} lastToken - lastToken parsed from filter input, ie "is:"
 * @param {object} requests - List of requests from which values are generated
 * @return {Array} - array of autocomplete values
 */
function getLastTokenFlagValues(lastToken, requests) {
  // The last token must be a string like "method:GET" or "method:", Any token
  // without a ":" cant be used to parse out flag values
  if (!lastToken.includes(":")) {
    return [];
  }

  // Parse out possible flag from lastToken
  let [flag, typedFlagValue] = lastToken.split(":");
  let isNegativeFlag = false;

  // Check if flag is used with negative match
  if (flag.startsWith("-")) {
    flag = flag.slice(1);
    isNegativeFlag = true;
  }

  // Flag is some random string, return
  if (!FILTER_FLAGS.includes(flag)) {
    return [];
  }

  let values = [];
  for (let request of requests) {
    values.push(...getAutocompleteValuesForFlag(flag, request));
  }
  values = [...new Set(values)];

  return values
    .filter(value => value)
    .filter(value => {
      if (typedFlagValue && value) {
        let lowerTyped = typedFlagValue.toLowerCase();
        let lowerValue = value.toLowerCase();
        return lowerValue.includes(lowerTyped) && lowerValue !== lowerTyped;
      }
      return typeof value !== "undefined" && value !== "" && value !== "undefined";
    })
    .sort()
    .map(value => isNegativeFlag ? `-${flag}:${value}` : `${flag}:${value}`);
}

/**
 * Generates an autocomplete list for the search-box for network monitor
 *
 * It expects an entire string of the searchbox ie "is:cached pr".
 * The string is then tokenized into "is:cached" and "pr"
 *
 * @param {string} filter - The entire search string of the search box
 * @param {object} requests - Iteratable object of requests displayed
 * @return {Array} - The output is an array of objects as 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
 */
function autocompleteProvider(filter, requests) {
  if (!filter) {
    return [];
  }

  let negativeAutocompleteList = FILTER_FLAGS.map((item) => `-${item}`);
  let baseList = [...FILTER_FLAGS, ...negativeAutocompleteList]
    .map((item) => `${item}:`);

  // The last token is used to filter the base autocomplete list
  let tokens = filter.split(/\s+/g);
  let lastToken = tokens[tokens.length - 1];
  let previousTokens = tokens.slice(0, tokens.length - 1);

  // Autocomplete list is not generated for empty lastToken
  if (!lastToken) {
    return [];
  }

  let autocompleteList;
  let availableValues = getLastTokenFlagValues(lastToken, requests);
  if (availableValues.length > 0) {
    autocompleteList = availableValues;
  } else {
    autocompleteList = baseList
      .filter((item) => {
        return item.toLowerCase().startsWith(lastToken.toLowerCase())
          && item.toLowerCase() !== lastToken.toLowerCase();
      });
  }

  return autocompleteList
    .sort()
    .map(item => ({
      value: [...previousTokens, item].join(" "),
      displayValue: item,
    }));
}

module.exports = {
  autocompleteProvider,
};
PK
!<4Ȉ99Qchrome/devtools/modules/devtools/client/netmonitor/src/utils/filter-predicates.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { isFreetextMatch } = require("./filter-text-utils");

/**
 * Predicates used when filtering items.
 *
 * @param object item
 *        The filtered item.
 * @return boolean
 *         True if the item should be visible, false otherwise.
 */
function all() {
  return true;
}

function isHtml({ mimeType }) {
  return mimeType && mimeType.includes("/html");
}

function isCss({ mimeType }) {
  return mimeType && mimeType.includes("/css");
}

function isJs({ mimeType }) {
  return mimeType && (
    mimeType.includes("/ecmascript") ||
    mimeType.includes("/javascript") ||
    mimeType.includes("/x-javascript"));
}

function isXHR(item) {
  // Show the request it is XHR, except if the request is a WS upgrade
  return item.isXHR && !isWS(item);
}

function isFont({ url, mimeType }) {
  // Fonts are a mess.
  return (mimeType && (
      mimeType.includes("font/") ||
      mimeType.includes("/font"))) ||
    url.includes(".eot") ||
    url.includes(".ttf") ||
    url.includes(".otf") ||
    url.includes(".woff");
}

function isImage({ mimeType }) {
  return mimeType && mimeType.includes("image/");
}

function isMedia({ mimeType }) {
  // Not including images.
  return mimeType && (
    mimeType.includes("audio/") ||
    mimeType.includes("video/") ||
    mimeType.includes("model/"));
}

function isWS({ requestHeaders, responseHeaders }) {
  // Detect a websocket upgrade if request has an Upgrade header with value 'websocket'
  if (!requestHeaders || !Array.isArray(requestHeaders.headers)) {
    return false;
  }

  // Find the 'upgrade' header.
  let upgradeHeader = requestHeaders.headers.find(header => {
    return (header.name.toLowerCase() == "upgrade");
  });

  // If no header found on request, check response - mainly to get
  // something we can unit test, as it is impossible to set
  // the Upgrade header on outgoing XHR as per the spec.
  if (!upgradeHeader && responseHeaders &&
      Array.isArray(responseHeaders.headers)) {
    upgradeHeader = responseHeaders.headers.find(header => {
      return (header.name.toLowerCase() == "upgrade");
    });
  }

  // Return false if there is no such header or if its value isn't 'websocket'.
  if (!upgradeHeader || upgradeHeader.value != "websocket") {
    return false;
  }

  return true;
}

function isOther(item) {
  let tests = [isHtml, isCss, isJs, isXHR, isFont, isImage, isMedia, isWS];
  return tests.every(is => !is(item));
}

module.exports = {
  Filters: {
    all: all,
    html: isHtml,
    css: isCss,
    js: isJs,
    xhr: isXHR,
    fonts: isFont,
    images: isImage,
    media: isMedia,
    ws: isWS,
    other: isOther,
  },
  isFreetextMatch,
};
PK
!<^����Qchrome/devtools/modules/devtools/client/netmonitor/src/utils/filter-text-utils.js/*
 * Copyright (c) 2013 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.
 */

"use strict";

const { FILTER_FLAGS } = require("../constants");
const { getFormattedIPAndPort } = require("./format-utils");

/*
  The function `parseFilters` is from:
  https://github.com/ChromeDevTools/devtools-frontend/

  front_end/network/FilterSuggestionBuilder.js#L138-L163
  Commit f340aefd7ec9b702de9366a812288cfb12111fce
*/

function parseFilters(query) {
  let flags = [];
  let text = [];
  let parts = query.split(/\s+/);

  for (let part of parts) {
    if (!part) {
      continue;
    }
    let colonIndex = part.indexOf(":");
    if (colonIndex === -1) {
      text.push(part);
      continue;
    }
    let key = part.substring(0, colonIndex);
    let negative = key.startsWith("-");
    if (negative) {
      key = key.substring(1);
    }
    if (!FILTER_FLAGS.includes(key)) {
      text.push(part);
      continue;
    }
    let value = part.substring(colonIndex + 1);
    value = processFlagFilter(key, value);
    flags.push({
      type: key,
      value,
      negative,
    });
  }

  return { text, flags };
}

function processFlagFilter(type, value) {
  switch (type) {
    case "regexp":
      return value;
    case "size":
    case "transferred":
    case "larger-than":
    case "transferred-larger-than":
      let multiplier = 1;
      if (value.endsWith("k")) {
        multiplier = 1024;
        value = value.substring(0, value.length - 1);
      } else if (value.endsWith("m")) {
        multiplier = 1024 * 1024;
        value = value.substring(0, value.length - 1);
      }
      let quantity = Number(value);
      if (isNaN(quantity)) {
        return null;
      }
      return quantity * multiplier;
    default:
      return value.toLowerCase();
  }
}

function isFlagFilterMatch(item, { type, value, negative }) {
  // Ensures when filter token is exactly a flag ie. "remote-ip:", all values are shown
  if (value.length < 1) {
    return true;
  }

  let match = true;
  let { responseCookies = { cookies: [] } } = item;
  responseCookies = responseCookies.cookies || responseCookies;
  switch (type) {
    case "status-code":
      match = item.status === value;
      break;
    case "method":
      match = item.method.toLowerCase() === value;
      break;
    case "protocol":
      let protocol = item.httpVersion;
      match = typeof protocol === "string" ?
                protocol.toLowerCase().includes(value) : false;
      break;
    case "domain":
      match = item.urlDetails.host.toLowerCase().includes(value);
      break;
    case "remote-ip":
      match = getFormattedIPAndPort(item.remoteAddress, item.remotePort)
        .toLowerCase().includes(value);
      break;
    case "has-response-header":
      if (typeof item.responseHeaders === "object") {
        let { headers } = item.responseHeaders;
        match = headers.findIndex(h => h.name.toLowerCase() === value) > -1;
      } else {
        match = false;
      }
      break;
    case "cause":
      let causeType = item.cause.type;
      match = typeof causeType === "string" ?
                causeType.toLowerCase().includes(value) : false;
      break;
    case "transferred":
      if (item.fromCache) {
        match = false;
      } else {
        match = isSizeMatch(value, item.transferredSize);
      }
      break;
    case "size":
      match = isSizeMatch(value, item.contentSize);
      break;
    case "larger-than":
      match = item.contentSize > value;
      break;
    case "transferred-larger-than":
      if (item.fromCache) {
        match = false;
      } else {
        match = item.transferredSize > value;
      }
      break;
    case "mime-type":
      match = item.mimeType.includes(value);
      break;
    case "is":
      if (value === "from-cache" ||
          value === "cached") {
        match = item.fromCache || item.status === "304";
      } else if (value === "running") {
        match = !item.status;
      }
      break;
    case "scheme":
      match = item.urlDetails.scheme === value;
      break;
    case "regexp":
      try {
        let pattern = new RegExp(value);
        match = pattern.test(item.url);
      } catch (e) {
        match = false;
      }
      break;
    case "set-cookie-domain":
      if (responseCookies.length > 0) {
        let host = item.urlDetails.host;
        let i = responseCookies.findIndex(c => {
          let domain = c.hasOwnProperty("domain") ? c.domain : host;
          return domain.includes(value);
        });
        match = i > -1;
      } else {
        match = false;
      }
      break;
    case "set-cookie-name":
      match = responseCookies.findIndex(c =>
        c.name.toLowerCase().includes(value)) > -1;
      break;
    case "set-cookie-value":
      match = responseCookies.findIndex(c =>
        c.value.toLowerCase().includes(value)) > -1;
      break;
  }
  if (negative) {
    return !match;
  }
  return match;
}

function isSizeMatch(value, size) {
  return value >= (size - size / 10) && value <= (size + size / 10);
}

function isTextFilterMatch({ url }, text) {
  let lowerCaseUrl = url.toLowerCase();
  let lowerCaseText = text.toLowerCase();
  let textLength = text.length;
  // Support negative filtering
  if (text.startsWith("-") && textLength > 1) {
    lowerCaseText = lowerCaseText.substring(1, textLength);
    return !lowerCaseUrl.includes(lowerCaseText);
  }

  // no text is a positive match
  return !text || lowerCaseUrl.includes(lowerCaseText);
}

function isFreetextMatch(item, text) {
  if (!text) {
    return true;
  }

  let filters = parseFilters(text);
  let match = true;

  for (let textFilter of filters.text) {
    match = match && isTextFilterMatch(item, textFilter);
  }

  for (let flagFilter of filters.flags) {
    match = match && isFlagFilterMatch(item, flagFilter);
  }

  return match;
}

module.exports = {
  isFreetextMatch,
};
PK
!<���11[chrome/devtools/modules/devtools/client/netmonitor/src/utils/firefox/open-request-in-tab.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 mozilla/reject-some-requires */

"use strict";

let { Cc, Ci } = require("chrome");
const Services = require("Services");
const { gDevTools } = require("devtools/client/framework/devtools");

/**
 * Opens given request in a new tab.
 */
function openRequestInTab(request) {
  let win = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
  let rawData = request.requestPostData ? request.requestPostData.postData : null;
  let postData;

  if (rawData && rawData.text) {
    let stringStream = getInputStreamFromString(rawData.text);
    postData = Cc["@mozilla.org/network/mime-input-stream;1"]
      .createInstance(Ci.nsIMIMEInputStream);
    postData.addHeader("Content-Type", "application/x-www-form-urlencoded");
    postData.setData(stringStream);
  }

  win.gBrowser.selectedTab = win.gBrowser.addTab(request.url, null, null, postData);
}

function getInputStreamFromString(data) {
  let stringStream = Cc["@mozilla.org/io/string-input-stream;1"]
    .createInstance(Ci.nsIStringInputStream);
  stringStream.data = data;
  return stringStream;
}

module.exports = {
  openRequestInTab,
};
PK
!<^s���Lchrome/devtools/modules/devtools/client/netmonitor/src/utils/format-utils.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { L10N } = require("./l10n");

// Constants for formatting bytes.
const BYTES_IN_KB = 1024;
const BYTES_IN_MB = Math.pow(BYTES_IN_KB, 2);
const BYTES_IN_GB = Math.pow(BYTES_IN_KB, 3);
const MAX_BYTES_SIZE = 1000;
const MAX_KB_SIZE = 1000 * BYTES_IN_KB;
const MAX_MB_SIZE = 1000 * BYTES_IN_MB;

// Constants for formatting time.
const MAX_MILLISECOND = 1000;
const MAX_SECOND = 60 * MAX_MILLISECOND;

const REQUEST_DECIMALS = 2;

function getSizeWithDecimals(size, decimals = REQUEST_DECIMALS) {
  return L10N.numberWithDecimals(size, decimals);
}

function getTimeWithDecimals(time) {
  return L10N.numberWithDecimals(time, REQUEST_DECIMALS);
}

function formatDecimals(size, decimals) {
  return (size % 1 > 0) ? decimals : 0;
}

/**
 * Get a human-readable string from a number of bytes, with the B, KB, MB, or
 * GB value. Note that the transition between abbreviations is by 1000 rather
 * than 1024 in order to keep the displayed digits smaller as "1016 KB" is
 * more awkward than 0.99 MB"
 */
function getFormattedSize(bytes, decimals = REQUEST_DECIMALS) {
  if (bytes < MAX_BYTES_SIZE) {
    return L10N.getFormatStr("networkMenu.sizeB", bytes);
  }
  if (bytes < MAX_KB_SIZE) {
    const kb = bytes / BYTES_IN_KB;
    const formattedDecimals = formatDecimals(kb, decimals);

    return L10N.getFormatStr("networkMenu.sizeKB",
      getSizeWithDecimals(kb, formattedDecimals));
  }
  if (bytes < MAX_MB_SIZE) {
    const mb = bytes / BYTES_IN_MB;
    const formattedDecimals = formatDecimals(mb, decimals);
    return L10N.getFormatStr("networkMenu.sizeMB",
      getSizeWithDecimals(mb, formattedDecimals));
  }
  const gb = bytes / BYTES_IN_GB;
  const formattedDecimals = formatDecimals(gb, decimals);
  return L10N.getFormatStr("networkMenu.sizeGB",
    getSizeWithDecimals(gb, formattedDecimals));
}

/**
 * Get a human-readable string from a number of time, with the ms, s, or min
 * value.
 */
function getFormattedTime(ms) {
  if (ms < MAX_MILLISECOND) {
    return L10N.getFormatStr("networkMenu.millisecond", ms | 0);
  }
  if (ms < MAX_SECOND) {
    const sec = ms / MAX_MILLISECOND;
    return L10N.getFormatStr("networkMenu.second", getTimeWithDecimals(sec));
  }
  const min = ms / MAX_SECOND;
  return L10N.getFormatStr("networkMenu.minute", getTimeWithDecimals(min));
}

/**
 * Formats IP (v4 and v6) and port
 *
 * @param {string} ip - IP address
 * @param {string} port
 * @return {string} the formatted IP + port
 */
function getFormattedIPAndPort(ip, port) {
  if (!port) {
    return ip;
  }
  return ip.match(/:+/) ? `[${ip}]:${port}` : `${ip}:${port}`;
}

module.exports = {
  getFormattedIPAndPort,
  getFormattedSize,
  getFormattedTime,
  getSizeWithDecimals,
  getTimeWithDecimals,
};
PK
!<���sPchrome/devtools/modules/devtools/client/netmonitor/src/utils/headers-provider.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { ObjectProvider } = require("devtools/client/shared/components/tree/ObjectProvider");

/**
 * Custom tree provider.
 *
 * This provider is used to provide set of headers and is
 * utilized by the HeadersPanel.
 * The default ObjectProvider can't be used since it doesn't
 * allow duplicities by design and so it can't support duplicity
 * headers (more headers with the same name).
 */
var HeadersProvider = {
  ...ObjectProvider,

  getChildren(object) {
    if (object.value instanceof HeaderList) {
      return object.value.headers.map((header, index) =>
        new Header(header.name, header.value, index));
    }
    return ObjectProvider.getChildren(object);
  },

  hasChildren: function (object) {
    if (object.value instanceof HeaderList) {
      return object.value.headers.length > 0;
    } else if (object instanceof Header) {
      return false;
    }
    return ObjectProvider.hasChildren(object);
  },

  getLabel: function (object) {
    if (object instanceof Header) {
      return object.name;
    }
    return ObjectProvider.getLabel(object);
  },

  getValue: function (object) {
    if (object instanceof Header) {
      return object.value;
    }
    return ObjectProvider.getValue(object);
  },

  getKey(object) {
    if (object instanceof Header) {
      return object.key;
    }
    return ObjectProvider.getKey(object);
  },

  getType: function (object) {
    if (object instanceof Header) {
      return "string";
    }
    return ObjectProvider.getType(object);
  }
};

/**
 * Helper data structures for list of headers.
 */
function HeaderList(headers) {
  // Clone, so the sort doesn't affect the original array.
  this.headers = headers.slice(0);
  this.headers.sort((a, b) => {
    return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
  });
}

function Header(name, value, key) {
  this.name = name;
  this.value = value;
  this.key = key;
}

module.exports = {
  HeadersProvider,
  HeaderList
};
PK
!<eGp`��Dchrome/devtools/modules/devtools/client/netmonitor/src/utils/l10n.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/shared/l10n");

const NET_STRINGS_URI = "devtools/client/locales/netmonitor.properties";

exports.L10N = new LocalizationHelper(NET_STRINGS_URI);
PK
!<d�Ichrome/devtools/modules/devtools/client/netmonitor/src/utils/mdn-utils.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 mapping of header names to external documentation. Any header included
 * here will show a MDN link alongside it.
 */
const SUPPORTED_HEADERS = [
  "Accept",
  "Accept-Charset",
  "Accept-Encoding",
  "Accept-Language",
  "Accept-Ranges",
  "Access-Control-Allow-Credentials",
  "Access-Control-Allow-Headers",
  "Access-Control-Allow-Methods",
  "Access-Control-Allow-Origin",
  "Access-Control-Expose-Headers",
  "Access-Control-Max-Age",
  "Access-Control-Request-Headers",
  "Access-Control-Request-Method",
  "Age",
  "Allow",
  "Authorization",
  "Cache-Control",
  "Connection",
  "Content-Disposition",
  "Content-Encoding",
  "Content-Language",
  "Content-Length",
  "Content-Location",
  "Content-Range",
  "Content-Security-Policy",
  "Content-Security-Policy-Report-Only",
  "Content-Type",
  "Cookie",
  "Cookie2",
  "DNT",
  "Date",
  "ETag",
  "Expect",
  "Expires",
  "Forwarded",
  "From",
  "Host",
  "If-Match",
  "If-Modified-Since",
  "If-None-Match",
  "If-Range",
  "If-Unmodified-Since",
  "Keep-Alive",
  "Large-Allocation",
  "Last-Modified",
  "Location",
  "Origin",
  "Pragma",
  "Proxy-Authenticate",
  "Proxy-Authorization",
  "Public-Key-Pins",
  "Public-Key-Pins-Report-Only",
  "Range",
  "Referer",
  "Referrer-Policy",
  "Retry-After",
  "Server",
  "Set-Cookie",
  "Set-Cookie2",
  "Strict-Transport-Security",
  "TE",
  "Tk",
  "Trailer",
  "Transfer-Encoding",
  "Upgrade-Insecure-Requests",
  "User-Agent",
  "Vary",
  "Via",
  "WWW-Authenticate",
  "Warning",
  "X-Content-Type-Options",
  "X-DNS-Prefetch-Control",
  "X-Forwarded-For",
  "X-Forwarded-Host",
  "X-Forwarded-Proto",
  "X-Frame-Options",
  "X-XSS-Protection"
];

/**
 * A mapping of HTTP status codes to external documentation. Any code included
 * here will show a MDN link alongside it.
 */
const SUPPORTED_HTTP_CODES = [
    "100",
    "101",
    "200",
    "201",
    "202",
    "203",
    "204",
    "205",
    "206",
    "300",
    "301",
    "302",
    "303",
    "304",
    "307",
    "308",
    "400",
    "401",
    "403",
    "404",
    "405",
    "406",
    "407",
    "408",
    "409",
    "410",
    "411",
    "412",
    "413",
    "414",
    "415",
    "416",
    "417",
    "418",
    "426",
    "428",
    "429",
    "431",
    "451",
    "500",
    "501",
    "502",
    "503",
    "504",
    "505",
    "511"
];

const MDN_URL = "https://developer.mozilla.org/docs/";
const MDN_STATUS_CODES_LIST_URL = `${MDN_URL}Web/HTTP/Status`;
const getGAParams = (panelId = "netmonitor") => {
  return `?utm_source=mozilla&utm_medium=devtools-${panelId}&utm_campaign=default`;
};

/**
 * Get the MDN URL for the specified header.
 *
 * @param {string} header Name of the header for the baseURL to use.
 *
 * @return {string} The MDN URL for the header, or null if not available.
 */
function getHeadersURL(header) {
  const lowerCaseHeader = header.toLowerCase();
  let idx = SUPPORTED_HEADERS.findIndex(item =>
    item.toLowerCase() === lowerCaseHeader);
  return idx > -1 ?
    `${MDN_URL}Web/HTTP/Headers/${SUPPORTED_HEADERS[idx] + getGAParams()}` : null;
}

/**
 * Get the MDN URL for the specified HTTP status code.
 *
 * @param {string} HTTP status code for the baseURL to use.
 *
 * @return {string} The MDN URL for the HTTP status code, or null if not available.
 */
function getHTTPStatusCodeURL(statusCode, panelId) {
  return (
    SUPPORTED_HTTP_CODES.includes(statusCode)
      ? `${MDN_URL}Web/HTTP/Status/${statusCode}`
      : MDN_STATUS_CODES_LIST_URL
    ) + getGAParams(panelId);
}

/**
 * Get the MDN URL of the Timings tag for Network Monitor.
 *
 * @return {string} the MDN URL of the Timings tag for Network Monitor.
 */
function getNetMonitorTimingsURL() {
  return `${MDN_URL}Tools/Network_Monitor${getGAParams()}#Timings`;
}

/**
 * Get the MDN URL for Performance Analysis
 *
 * @return {string} The MDN URL for the documentation of Performance Analysis.
 */
function getPerformanceAnalysisURL() {
  return `${MDN_URL}Tools/Network_Monitor${getGAParams()}#Performance_analysis`;
}

module.exports = {
  getHeadersURL,
  getHTTPStatusCodeURL,
  getNetMonitorTimingsURL,
  getPerformanceAnalysisURL,
};
PK
!<-�zBnnDchrome/devtools/modules/devtools/client/netmonitor/src/utils/menu.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/framework/menu");
const MenuItem = require("devtools/client/framework/menu-item");

function showMenu(evt, items) {
  if (items.length === 0) {
    return;
  }

  let menu = new Menu();
  items.forEach((item) => {
    let menuItem = new MenuItem(item);
    let subItems = item.submenu;

    if (subItems) {
      let subMenu = new Menu();
      subItems.forEach((subItem) => {
        subMenu.append(new MenuItem(subItem));
      });
      menuItem.submenu = subMenu;
    }

    menu.append(menuItem);
  });

  menu.popup(evt.screenX, evt.screenY, { doc: window.parent.document });
}

module.exports = {
  showMenu,
};
PK
!<h1|
Schrome/devtools/modules/devtools/client/netmonitor/src/utils/open-request-in-tab.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Services = require("Services");
const { gDevTools } = require("devtools/client/framework/devtools");

/**
 * Opens given request in a new tab.
 */
function openRequestInTab(request) {
  let win = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
  if (request.method.toLowerCase() !== "get") {
    win.openUILinkIn(this.selectedRequest.url, "tab", {
      relatedToCurrent: true
    });
  } else {
    openRequestInTabHelper({
      url: request.url,
      method: request.method,
      data: request.requestPostData ? request.requestPostData.postData : null,
    });
  }
}

function openRequestInTabHelper({url, method, data}) {
  let form = document.createElement("form");
  form.target = "_blank";
  form.action = url;
  form.method = method;

  if (data) {
    for (let key in data) {
      let input = document.createElement("input");
      input.name = key;
      input.value = data[key];
      form.appendChild(input);
    }
  }

  form.hidden = true;
  document.body.appendChild(form);
  form.submit();
  form.remove();
}

module.exports = {
  openRequestInTab,
};
PK
!<�4��qqEchrome/devtools/modules/devtools/client/netmonitor/src/utils/prefs.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { PrefsHelper } = require("devtools/client/shared/prefs");

/**
 * Shortcuts for accessing various network monitor preferences.
 */
exports.Prefs = new PrefsHelper("devtools.netmonitor", {
  networkDetailsWidth: ["Int", "panes-network-details-width"],
  networkDetailsHeight: ["Int", "panes-network-details-height"],
  visibleColumns: ["Json", "visibleColumns"],
  filters: ["Json", "filters"]
});
PK
!<��IA�7�7Mchrome/devtools/modules/devtools/client/netmonitor/src/utils/request-utils.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 mozilla/reject-some-requires */

"use strict";

const {
  UPDATE_PROPS,
} = require("devtools/client/netmonitor/src/constants");

const CONTENT_MIME_TYPE_ABBREVIATIONS = {
  "ecmascript": "js",
  "javascript": "js",
  "x-javascript": "js"
};

/**
 * Extracts any urlencoded form data sections (e.g. "?foo=bar&baz=42") from a
 * POST request.
 *
 * @param {object} headers - the "requestHeaders".
 * @param {object} uploadHeaders - the "requestHeadersFromUploadStream".
 * @param {object} postData - the "requestPostData".
 * @return {array} a promise list that is resolved with the extracted form data.
 */
async function getFormDataSections(headers, uploadHeaders, postData, getLongString) {
  let formDataSections = [];

  let requestHeaders = headers.headers;
  let payloadHeaders = uploadHeaders ? uploadHeaders.headers : [];
  let allHeaders = [...payloadHeaders, ...requestHeaders];

  let contentTypeHeader = allHeaders.find(e => {
    return e.name.toLowerCase() == "content-type";
  });

  let contentTypeLongString = contentTypeHeader ? contentTypeHeader.value : "";

  let contentType = await getLongString(contentTypeLongString);

  if (contentType.includes("x-www-form-urlencoded")) {
    let postDataLongString = postData.postData.text;
    let text = await getLongString(postDataLongString);

    for (let section of text.split(/\r\n|\r|\n/)) {
      // Before displaying it, make sure this section of the POST data
      // isn't a line containing upload stream headers.
      if (payloadHeaders.every(header => !section.startsWith(header.name))) {
        formDataSections.push(section);
      }
    }
  }

  return formDataSections;
}

/**
 * Fetch headers full content from actor server
 *
 * @param {object} headers - a object presents headers data
 * @return {object} a headers object with updated content payload
 */
async function fetchHeaders(headers, getLongString) {
  for (let { value } of headers.headers) {
    headers.headers.value = await getLongString(value);
  }
  return headers;
}

/**
 * Fetch network event update packets from actor server
 * Expect to fetch a couple of network update packets from a given request.
 *
 * @param {function} requestData - requestData function for lazily fetch data
 * @param {object} request - request object
 * @param {array} updateTypes - a list of network event update types
 */
function fetchNetworkUpdatePacket(requestData, request, updateTypes) {
  updateTypes.forEach((updateType) => {
    // Only stackTrace will be handled differently
    if (updateType === "stackTrace") {
      if (request.cause.stacktraceAvailable && !request.stacktrace) {
        requestData(request.id, updateType);
      }
      return;
    }

    if (request[`${updateType}Available`] && !request[updateType]) {
      requestData(request.id, updateType);
    }
  });
}

/**
 * Form a data: URI given a mime type, encoding, and some text.
 *
 * @param {string} mimeType - mime type
 * @param {string} encoding - encoding to use; if not set, the
 *                            text will be base64-encoded.
 * @param {string} text - text of the URI.
 * @return {string} a data URI
 */
function formDataURI(mimeType, encoding, text) {
  if (!encoding) {
    encoding = "base64";
    text = btoa(unescape(encodeURIComponent(text)));
  }
  return "data:" + mimeType + ";" + encoding + "," + text;
}

/**
 * Write out a list of headers into a chunk of text
 *
 * @param {array} headers - array of headers info { name, value }
 * @return {string} list of headers in text format
 */
function writeHeaderText(headers) {
  return headers.map(({name, value}) => name + ": " + value).join("\n");
}

/**
 * Convert a string into unicode if string is valid.
 * If there is a malformed URI sequence, it returns input string.
 *
 * @param {string} url - a string
 * @return {string} unicode string
 */
function decodeUnicodeUrl(string) {
  try {
    return decodeURIComponent(string);
  } catch (err) {
    // Ignore error and return input string directly.
  }
  return string;
}

/**
 * Decode base64 string.
 *
 * @param {string} url - a string
 * @return {string} decoded string
 */
function decodeUnicodeBase64(string) {
  try {
    return decodeURIComponent(atob(string));
  } catch (err) {
    // Ignore error and return input string directly.
  }
  return string;
}

/**
 * Helper for getting an abbreviated string for a mime type.
 *
 * @param {string} mimeType - mime type
 * @return {string} abbreviated mime type
 */
function getAbbreviatedMimeType(mimeType) {
  if (!mimeType) {
    return "";
  }
  let abbrevType = (mimeType.split(";")[0].split("/")[1] || "").split("+")[0];
  return CONTENT_MIME_TYPE_ABBREVIATIONS[abbrevType] || abbrevType;
}

/**
 * Helpers for getting the last portion of a url.
 * For example helper returns "basename" from http://domain.com/path/basename
 * If basename portion is empty, it returns the url pathname.
 *
 * @param {string} url - url string
 * @return {string} unicode basename of a url
 */
function getUrlBaseName(url) {
  const pathname = (new URL(url)).pathname;
  return decodeUnicodeUrl(
    pathname.replace(/\S*\//, "") || pathname || "/");
}

/**
 * Helpers for getting the query portion of a url.
 *
 * @param {string} url - url string
 * @return {string} unicode query of a url
 */
function getUrlQuery(url) {
  return (new URL(url)).search.replace(/^\?/, "");
}

/**
 * Helpers for getting unicode name and query portions of a url.
 *
 * @param {string} url - url string
 * @return {string} unicode basename and query portions of a url
 */
function getUrlBaseNameWithQuery(url) {
  return getUrlBaseName(url) + decodeUnicodeUrl((new URL(url)).search);
}

/**
 * Helpers for getting unicode hostname portion of an URL.
 *
 * @param {string} url - url string
 * @return {string} unicode hostname of a url
 */
function getUrlHostName(url) {
  return decodeUnicodeUrl((new URL(url)).hostname);
}

/**
 * Helpers for getting unicode host portion of an URL.
 *
 * @param {string} url - url string
 * @return {string} unicode host of a url
 */
function getUrlHost(url) {
  return decodeUnicodeUrl((new URL(url)).host);
}

/**
 * Helpers for getting the shceme portion of a url.
 * For example helper returns "http" from http://domain.com/path/basename
 *
 * @param {string} url - url string
 * @return {string} string scheme of a url
 */
function getUrlScheme(url) {
  return (new URL(url)).protocol.replace(":", "").toLowerCase();
}

/**
 * Extract several details fields from a URL at once.
 */
function getUrlDetails(url) {
  let baseNameWithQuery = getUrlBaseNameWithQuery(url);
  let host = getUrlHost(url);
  let hostname = getUrlHostName(url);
  let unicodeUrl = decodeUnicodeUrl(url);
  let scheme = getUrlScheme(url);

  // Mark local hosts specially, where "local" is  as defined in the W3C
  // spec for secure contexts.
  // http://www.w3.org/TR/powerful-features/
  //
  //  * If the name falls under 'localhost'
  //  * If the name is an IPv4 address within 127.0.0.0/8
  //  * If the name is an IPv6 address within ::1/128
  //
  // IPv6 parsing is a little sloppy; it assumes that the address has
  // been validated before it gets here.
  let isLocal = hostname.match(/(.+\.)?localhost$/) ||
                hostname.match(/^127\.\d{1,3}\.\d{1,3}\.\d{1,3}/) ||
                hostname.match(/\[[0:]+1\]/);

  return {
    baseNameWithQuery,
    host,
    scheme,
    unicodeUrl,
    isLocal
  };
}

/**
 * Parse a url's query string into its components
 *
 * @param {string} query - query string of a url portion
 * @return {array} array of query params { name, value }
 */
function parseQueryString(query) {
  if (!query) {
    return null;
  }

  return query.replace(/^[?&]/, "").split("&").map(e => {
    let param = e.split("=");
    return {
      name: param[0] ? decodeUnicodeUrl(param[0]) : "",
      value: param[1] ? decodeUnicodeUrl(param[1]) : "",
    };
  });
}

/**
 * Parse a string of formdata sections into its components
 *
 * @param {string} sections - sections of formdata joined by &
 * @return {array} array of formdata params { name, value }
 */
function parseFormData(sections) {
  if (!sections) {
    return null;
  }

  return sections.replace(/^&/, "").split("&").map(e => {
    let param = e.split("=");
    return {
      name: param[0] ? decodeUnicodeUrl(param[0]) : "",
      value: param[1] ? decodeUnicodeUrl(param[1]) : "",
    };
  });
}

/**
 * Reduces an IP address into a number for easier sorting
 *
 * @param {string} ip - IP address to reduce
 * @return {number} the number representing the IP address
 */
function ipToLong(ip) {
  if (!ip) {
    // Invalid IP
    return -1;
  }

  let base;
  let octets = ip.split(".");

  if (octets.length === 4) { // IPv4
    base = 10;
  } else if (ip.includes(":")) { // IPv6
    let numberOfZeroSections = 8 - ip.replace(/^:+|:+$/g, "").split(/:+/g).length;
    octets = ip
      .replace("::", `:${"0:".repeat(numberOfZeroSections)}`)
      .replace(/^:|:$/g, "")
      .split(":");
    base = 16;
  } else { // Invalid IP
    return -1;
  }
  return octets.map((val, ix, arr) => {
    return parseInt(val, base) * Math.pow(256, (arr.length - 1) - ix);
  }).reduce((sum, val) => {
    return sum + val;
  }, 0);
}

/**
 * Compare two objects on a subset of their properties
 */
function propertiesEqual(props, item1, item2) {
  return item1 === item2 || props.every(p => item1[p] === item2[p]);
}

/**
 * Calculate the start time of a request, which is the time from start
 * of 1st request until the start of this request.
 *
 * Without a firstRequestStartedMillis argument the wrong time will be returned.
 * However, it can be omitted when comparing two start times and neither supplies
 * a firstRequestStartedMillis.
 */
function getStartTime(item, firstRequestStartedMillis = 0) {
  return item.startedMillis - firstRequestStartedMillis;
}

/**
 * Calculate the end time of a request, which is the time from start
 * of 1st request until the end of this response.
 *
 * Without a firstRequestStartedMillis argument the wrong time will be returned.
 * However, it can be omitted when comparing two end times and neither supplies
 * a firstRequestStartedMillis.
 */
function getEndTime(item, firstRequestStartedMillis = 0) {
  let { startedMillis, totalTime } = item;
  return startedMillis + totalTime - firstRequestStartedMillis;
}

/**
 * Calculate the response time of a request, which is the time from start
 * of 1st request until the beginning of download of this response.
 *
 * Without a firstRequestStartedMillis argument the wrong time will be returned.
 * However, it can be omitted when comparing two response times and neither supplies
 * a firstRequestStartedMillis.
 */
function getResponseTime(item, firstRequestStartedMillis = 0) {
  let { startedMillis, totalTime, eventTimings = { timings: {} } } = item;
  return startedMillis + totalTime - firstRequestStartedMillis -
    eventTimings.timings.receive;
}

/**
 * Format the protocols used by the request.
 */
function getFormattedProtocol(item) {
  let { httpVersion = "", responseHeaders = { headers: [] } } = item;
  let protocol = [httpVersion];
  responseHeaders.headers.some(h => {
    if (h.hasOwnProperty("name") && h.name.toLowerCase() === "x-firefox-spdy") {
      protocol.push(h.value);
      return true;
    }
    return false;
  });
  return protocol.join("+");
}

/**
 * Get the value of a particular response header, or null if not
 * present.
 */
function getResponseHeader(item, header) {
  let { responseHeaders } = item;
  if (!responseHeaders || !responseHeaders.headers.length) {
    return null;
  }
  header = header.toLowerCase();
  for (let responseHeader of responseHeaders.headers) {
    if (responseHeader.name.toLowerCase() == header) {
      return responseHeader.value;
    }
  }
  return null;
}

/**
 * Extracts any urlencoded form data sections from a POST request.
 */
async function updateFormDataSections(props) {
  let {
    connector,
    request = {},
    updateRequest,
  } = props;
  let {
    id,
    formDataSections,
    requestHeaders,
    requestHeadersAvailable,
    requestHeadersFromUploadStream,
    requestPostData,
    requestPostDataAvailable,
  } = request;

  if (requestHeadersAvailable && !requestHeaders) {
    requestHeaders = await connector.requestData(id, "requestHeaders");
  }

  if (requestPostDataAvailable && !requestPostData) {
    requestPostData = await connector.requestData(id, "requestPostData");
  }

  if (!formDataSections && requestHeaders && requestPostData &&
      requestHeadersFromUploadStream) {
    formDataSections = await getFormDataSections(
      requestHeaders,
      requestHeadersFromUploadStream,
      requestPostData,
      connector.getLongString,
    );

    updateRequest(request.id, { formDataSections }, true);
  }
}

/**
 * This helper function is used for additional processing of
 * incoming network update packets. It's used by Network and
 * Console panel reducers.
 */
function processNetworkUpdates(request = {}) {
  let result = {};
  for (let [key, value] of Object.entries(request)) {
    if (UPDATE_PROPS.includes(key)) {
      result[key] = value;

      switch (key) {
        case "securityInfo":
          result.securityState = value.state;
          break;
        case "totalTime":
          result.totalTime = request.totalTime;
          break;
        case "requestPostData":
          result.requestHeadersFromUploadStream = {
            headers: [],
            headersSize: 0,
          };
          break;
      }
    }
  }
  return result;
}

module.exports = {
  decodeUnicodeBase64,
  getFormDataSections,
  fetchHeaders,
  fetchNetworkUpdatePacket,
  formDataURI,
  writeHeaderText,
  decodeUnicodeUrl,
  getAbbreviatedMimeType,
  getEndTime,
  getFormattedProtocol,
  getResponseHeader,
  getResponseTime,
  getStartTime,
  getUrlBaseName,
  getUrlBaseNameWithQuery,
  getUrlDetails,
  getUrlHost,
  getUrlHostName,
  getUrlQuery,
  getUrlScheme,
  parseQueryString,
  parseFormData,
  updateFormDataSections,
  processNetworkUpdates,
  propertiesEqual,
  ipToLong,
};
PK
!<��^v66Ochrome/devtools/modules/devtools/client/netmonitor/src/utils/sort-predicates.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  getAbbreviatedMimeType,
  getEndTime,
  getResponseTime,
  getResponseHeader,
  getStartTime,
  ipToLong,
} = require("./request-utils");
const { RESPONSE_HEADERS } = require("../constants");

/**
 * Predicates used when sorting items.
 *
 * @param object first
 *        The first item used in the comparison.
 * @param object second
 *        The second item used in the comparison.
 * @return number
 *         <0 to sort first to a lower index than second
 *         =0 to leave first and second unchanged with respect to each other
 *         >0 to sort second to a lower index than first
 */

function compareValues(first, second) {
  if (first === second) {
    return 0;
  }
  return first > second ? 1 : -1;
}

function waterfall(first, second) {
  const result = compareValues(first.startedMillis, second.startedMillis);
  return result || compareValues(first.id, second.id);
}

function status(first, second) {
  const result = compareValues(first.status, second.status);
  return result || waterfall(first, second);
}

function method(first, second) {
  const result = compareValues(first.method, second.method);
  return result || waterfall(first, second);
}

function file(first, second) {
  const firstUrl = first.urlDetails.baseNameWithQuery.toLowerCase();
  const secondUrl = second.urlDetails.baseNameWithQuery.toLowerCase();
  const result = compareValues(firstUrl, secondUrl);
  return result || waterfall(first, second);
}

function protocol(first, second) {
  const result = compareValues(first.httpVersion, second.httpVersion);
  return result || waterfall(first, second);
}

function scheme(first, second) {
  const result = compareValues(first.urlDetails.scheme, second.urlDetails.scheme);
  return result || waterfall(first, second);
}

function startTime(first, second) {
  const result = compareValues(getStartTime(first), getStartTime(second));
  return result || waterfall(first, second);
}

function endTime(first, second) {
  const result = compareValues(getEndTime(first), getEndTime(second));
  return result || waterfall(first, second);
}

function responseTime(first, second) {
  const result = compareValues(getResponseTime(first), getResponseTime(second));
  return result || waterfall(first, second);
}

function duration(first, second) {
  const result = compareValues(first.totalTime, second.totalTime);
  return result || waterfall(first, second);
}

function latency(first, second) {
  let { eventTimings: firstEventTimings = { timings: {} } } = first;
  let { eventTimings: secondEventTimings = { timings: {} } } = second;
  const result = compareValues(firstEventTimings.timings.wait,
   secondEventTimings.timings.wait);
  return result || waterfall(first, second);
}

function compareHeader(header, first, second) {
  const result = compareValues(getResponseHeader(first, header) || "",
                               getResponseHeader(second, header) || "");
  return result || waterfall(first, second);
}

const responseHeaders = RESPONSE_HEADERS
  .reduce((acc, header) => Object.assign(
    acc, {[header]: (first, second) => compareHeader(header, first, second)}), {});

function domain(first, second) {
  const firstDomain = first.urlDetails.host.toLowerCase();
  const secondDomain = second.urlDetails.host.toLowerCase();
  const result = compareValues(firstDomain, secondDomain);
  return result || waterfall(first, second);
}

function remoteip(first, second) {
  const firstIP = ipToLong(first.remoteAddress);
  const secondIP = ipToLong(second.remoteAddress);
  const result = compareValues(firstIP, secondIP);
  return result || waterfall(first, second);
}

function cause(first, second) {
  const firstCause = first.cause.type;
  const secondCause = second.cause.type;
  const result = compareValues(firstCause, secondCause);
  return result || waterfall(first, second);
}

function setCookies(first, second) {
  let { responseCookies: firstResponseCookies = { cookies: [] } } = first;
  let { responseCookies: secondResponseCookies = { cookies: [] } } = second;
  firstResponseCookies = firstResponseCookies.cookies || firstResponseCookies;
  secondResponseCookies = secondResponseCookies.cookies || secondResponseCookies;
  const result =
    compareValues(firstResponseCookies.length, secondResponseCookies.length);
  return result || waterfall(first, second);
}

function cookies(first, second) {
  let { requestCookies: firstRequestCookies = { cookies: [] } } = first;
  let { requestCookies: secondRequestCookies = { cookies: [] } } = second;
  firstRequestCookies = firstRequestCookies.cookies || firstRequestCookies;
  secondRequestCookies = secondRequestCookies.cookies || secondRequestCookies;
  const result =
    compareValues(firstRequestCookies.length, secondRequestCookies.length);
  return result || waterfall(first, second);
}

function type(first, second) {
  const firstType = getAbbreviatedMimeType(first.mimeType).toLowerCase();
  const secondType = getAbbreviatedMimeType(second.mimeType).toLowerCase();
  const result = compareValues(firstType, secondType);
  return result || waterfall(first, second);
}

function transferred(first, second) {
  const result = compareValues(first.transferredSize, second.transferredSize);
  return result || waterfall(first, second);
}

function contentSize(first, second) {
  const result = compareValues(first.contentSize, second.contentSize);
  return result || waterfall(first, second);
}

const sorters = {
  status,
  method,
  file,
  protocol,
  scheme,
  cookies,
  setCookies,
  domain,
  remoteip,
  cause,
  type,
  transferred,
  contentSize,
  startTime,
  endTime,
  responseTime,
  duration,
  latency,
  waterfall,
};
exports.Sorters = Object.assign(sorters, responseHeaders);
PK
!<0���\\Jchrome/devtools/modules/devtools/client/netmonitor/src/utils/sort-utils.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

/**
 * Sorts object by keys in alphabetical order
 * If object has nested children, it sorts the child-elements also by keys
 * @param {object} which should be sorted by keys in alphabetical order
 */
function sortObjectKeys(object) {
  if (object == null) {
    return null;
  }
  return Object.keys(object).sort(function (left, right) {
    return left.toLowerCase().localeCompare(right.toLowerCase());
  }).reduce((acc, key) => {
    if (typeof object[key] === "object") {
      acc[key] = sortObjectKeys(object[key]);
    } else {
      acc[key] = object[key];
    }
    return acc;
  }, {});
}

module.exports = {
  sortObjectKeys
};
PK
!<D]�E88Xchrome/devtools/modules/devtools/client/netmonitor/src/widgets/RequestListContextMenu.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Services = require("Services");
const { gDevTools } = require("devtools/client/framework/devtools");
const { L10N } = require("../utils/l10n");
const {
  formDataURI,
  getUrlQuery,
  getUrlBaseName,
  parseQueryString,
} = require("../utils/request-utils");

loader.lazyRequireGetter(this, "Curl", "devtools/client/shared/curl", true);
loader.lazyRequireGetter(this, "saveAs", "devtools/client/shared/file-saver", true);
loader.lazyRequireGetter(this, "copyString", "devtools/shared/platform/clipboard", true);
loader.lazyRequireGetter(this, "showMenu", "devtools/client/netmonitor/src/utils/menu", true);
loader.lazyRequireGetter(this, "openRequestInTab", "devtools/client/netmonitor/src/utils/firefox/open-request-in-tab", true);
loader.lazyRequireGetter(this, "HarExporter", "devtools/client/netmonitor/src/har/har-exporter", true);

class RequestListContextMenu {
  constructor(props) {
    this.props = props;
  }

  open(event, selectedRequest, sortedRequests) {
    let {
      id,
      isCustom,
      formDataSections,
      method,
      mimeType,
      httpVersion,
      requestHeaders,
      requestHeadersAvailable,
      requestPostData,
      requestPostDataAvailable,
      responseHeaders,
      responseHeadersAvailable,
      responseContent,
      responseContentAvailable,
      url,
    } = selectedRequest;
    let {
      cloneSelectedRequest,
      openStatistics,
    } = this.props;
    let menu = [];
    let copySubmenu = [];

    copySubmenu.push({
      id: "request-list-context-copy-url",
      label: L10N.getStr("netmonitor.context.copyUrl"),
      accesskey: L10N.getStr("netmonitor.context.copyUrl.accesskey"),
      visible: !!selectedRequest,
      click: () => this.copyUrl(url),
    });

    copySubmenu.push({
      id: "request-list-context-copy-url-params",
      label: L10N.getStr("netmonitor.context.copyUrlParams"),
      accesskey: L10N.getStr("netmonitor.context.copyUrlParams.accesskey"),
      visible: !!(selectedRequest && getUrlQuery(url)),
      click: () => this.copyUrlParams(url),
    });

    copySubmenu.push({
      id: "request-list-context-copy-post-data",
      label: L10N.getStr("netmonitor.context.copyPostData"),
      accesskey: L10N.getStr("netmonitor.context.copyPostData.accesskey"),
      // Menu item will be visible even if data hasn't arrived, so we need to check
      // *Available property and then fetch data lazily once user triggers the action.
      visible: !!(selectedRequest && (requestPostDataAvailable || requestPostData)),
      click: () => this.copyPostData(id, formDataSections, requestPostData),
    });

    copySubmenu.push({
      id: "request-list-context-copy-as-curl",
      label: L10N.getStr("netmonitor.context.copyAsCurl"),
      accesskey: L10N.getStr("netmonitor.context.copyAsCurl.accesskey"),
      // Menu item will be visible even if data hasn't arrived, so we need to check
      // *Available property and then fetch data lazily once user triggers the action.
      visible: !!(selectedRequest &&
        (requestHeadersAvailable || requestHeaders) &&
        (responseContentAvailable || responseContent)),
      click: () =>
        this.copyAsCurl(id, url, method, httpVersion, requestHeaders, responseContent),
    });

    copySubmenu.push({
      type: "separator",
      visible: copySubmenu.slice(0, 4).some((subMenu) => subMenu.visible),
    });

    copySubmenu.push({
      id: "request-list-context-copy-request-headers",
      label: L10N.getStr("netmonitor.context.copyRequestHeaders"),
      accesskey: L10N.getStr("netmonitor.context.copyRequestHeaders.accesskey"),
      // Menu item will be visible even if data hasn't arrived, so we need to check
      // *Available property and then fetch data lazily once user triggers the action.
      visible: !!(selectedRequest && (requestHeadersAvailable || requestHeaders)),
      click: () => this.copyRequestHeaders(id, requestHeaders),
    });

    copySubmenu.push({
      id: "response-list-context-copy-response-headers",
      label: L10N.getStr("netmonitor.context.copyResponseHeaders"),
      accesskey: L10N.getStr("netmonitor.context.copyResponseHeaders.accesskey"),
      // Menu item will be visible even if data hasn't arrived, so we need to check
      // *Available property and then fetch data lazily once user triggers the action.
      visible: !!(selectedRequest && (responseHeadersAvailable || responseHeaders)),
      click: () => this.copyResponseHeaders(id, responseHeaders),
    });

    copySubmenu.push({
      id: "request-list-context-copy-response",
      label: L10N.getStr("netmonitor.context.copyResponse"),
      accesskey: L10N.getStr("netmonitor.context.copyResponse.accesskey"),
      // Menu item will be visible even if data hasn't arrived, so we need to check
      // *Available property and then fetch data lazily once user triggers the action.
      visible: !!(selectedRequest && (responseContentAvailable || responseContent)),
      click: () => this.copyResponse(id, responseContent),
    });

    copySubmenu.push({
      id: "request-list-context-copy-image-as-data-uri",
      label: L10N.getStr("netmonitor.context.copyImageAsDataUri"),
      accesskey: L10N.getStr("netmonitor.context.copyImageAsDataUri.accesskey"),
      visible: !!(selectedRequest && (responseContentAvailable || responseContent) &&
        mimeType && mimeType.includes("image/")),
      click: () => this.copyImageAsDataUri(id, mimeType, responseContent),
    });

    copySubmenu.push({
      type: "separator",
      visible: copySubmenu.slice(5, 9).some((subMenu) => subMenu.visible),
    });

    copySubmenu.push({
      id: "request-list-context-copy-all-as-har",
      label: L10N.getStr("netmonitor.context.copyAllAsHar"),
      accesskey: L10N.getStr("netmonitor.context.copyAllAsHar.accesskey"),
      visible: sortedRequests.size > 0,
      click: () => this.copyAllAsHar(sortedRequests),
    });

    menu.push({
      label: L10N.getStr("netmonitor.context.copy"),
      accesskey: L10N.getStr("netmonitor.context.copy.accesskey"),
      visible: !!selectedRequest,
      submenu: copySubmenu,
    });

    menu.push({
      id: "request-list-context-save-all-as-har",
      label: L10N.getStr("netmonitor.context.saveAllAsHar"),
      accesskey: L10N.getStr("netmonitor.context.saveAllAsHar.accesskey"),
      visible: sortedRequests.size > 0,
      click: () => this.saveAllAsHar(sortedRequests),
    });

    menu.push({
      id: "request-list-context-save-image-as",
      label: L10N.getStr("netmonitor.context.saveImageAs"),
      accesskey: L10N.getStr("netmonitor.context.saveImageAs.accesskey"),
      visible: !!(selectedRequest && (responseContentAvailable || responseContent) &&
        mimeType && mimeType.includes("image/")),
      click: () => this.saveImageAs(id, url, responseContent),
    });

    menu.push({
      type: "separator",
      visible: copySubmenu.slice(10, 14).some((subMenu) => subMenu.visible),
    });

    menu.push({
      id: "request-list-context-resend",
      label: L10N.getStr("netmonitor.context.editAndResend"),
      accesskey: L10N.getStr("netmonitor.context.editAndResend.accesskey"),
      visible: !!(selectedRequest && !isCustom),
      click: cloneSelectedRequest,
    });

    menu.push({
      type: "separator",
      visible: copySubmenu.slice(15, 16).some((subMenu) => subMenu.visible),
    });

    menu.push({
      id: "request-list-context-newtab",
      label: L10N.getStr("netmonitor.context.newTab"),
      accesskey: L10N.getStr("netmonitor.context.newTab.accesskey"),
      visible: !!selectedRequest,
      click: () => this.openRequestInTab(selectedRequest),
    });

    menu.push({
      id: "request-list-context-open-in-debugger",
      label: L10N.getStr("netmonitor.context.openInDebugger"),
      accesskey: L10N.getStr("netmonitor.context.openInDebugger.accesskey"),
      visible: !!(selectedRequest && mimeType && mimeType.includes("javascript")),
      click: () => this.openInDebugger(url),
    });

    menu.push({
      id: "request-list-context-open-in-style-editor",
      label: L10N.getStr("netmonitor.context.openInStyleEditor"),
      accesskey: L10N.getStr("netmonitor.context.openInStyleEditor.accesskey"),
      visible: !!(selectedRequest &&
        Services.prefs.getBoolPref("devtools.styleeditor.enabled") &&
        mimeType && mimeType.includes("css")),
      click: () => this.openInStyleEditor(url),
    });

    menu.push({
      id: "request-list-context-perf",
      label: L10N.getStr("netmonitor.context.perfTools"),
      accesskey: L10N.getStr("netmonitor.context.perfTools.accesskey"),
      visible: sortedRequests.size > 0,
      click: () => openStatistics(true),
    });

    showMenu(event, menu);
  }

  /**
   * Opens selected item in a new tab.
   */
  openRequestInTab(selectedRequest) {
    openRequestInTab(selectedRequest);
  }

  /**
   * Opens selected item in the debugger
   */
  openInDebugger(url) {
    let toolbox = gDevTools.getToolbox(this.props.connector.getTabTarget());
    toolbox.viewSourceInDebugger(url, 0);
  }

  /**
   * Opens selected item in the style editor
   */
  openInStyleEditor(url) {
    let toolbox = gDevTools.getToolbox(this.props.connector.getTabTarget());
    toolbox.viewSourceInStyleEditor(url, 0);
  }

  /**
   * Copy the request url from the currently selected item.
   */
  copyUrl(url) {
    copyString(url);
  }

  /**
   * Copy the request url query string parameters from the currently
   * selected item.
   */
  copyUrlParams(url) {
    let params = getUrlQuery(url).split("&");
    copyString(params.join(Services.appinfo.OS === "WINNT" ? "\r\n" : "\n"));
  }

  /**
   * Copy the request form data parameters (or raw payload) from
   * the currently selected item.
   */
  async copyPostData(id, formDataSections, requestPostData) {
    let params = [];
    // Try to extract any form data parameters.
    formDataSections.forEach(section => {
      let paramsArray = parseQueryString(section);
      if (paramsArray) {
        params = [...params, ...paramsArray];
      }
    });

    let string = params
      .map(param => param.name + (param.value ? "=" + param.value : ""))
      .join(Services.appinfo.OS === "WINNT" ? "\r\n" : "\n");

    // Fall back to raw payload.
    if (!string) {
      requestPostData = requestPostData ||
        await this.props.connector.requestData(id, "requestPostData").requestPostData;

      string = requestPostData.postData.text;
      if (Services.appinfo.OS !== "WINNT") {
        string = string.replace(/\r/g, "");
      }
    }
    copyString(string);
  }

  /**
   * Copy a cURL command from the currently selected item.
   */
  async copyAsCurl(id, url, method, httpVersion, requestHeaders, requestPostData) {
    requestHeaders = requestHeaders ||
      await this.props.connector.requestData(id, "requestHeaders");

    requestPostData = requestPostData ||
      await this.props.connector.requestData(id, "requestPostData").requestPostData;

    // Create a sanitized object for the Curl command generator.
    let data = {
      url,
      method,
      headers: requestHeaders.headers,
      httpVersion,
      postDataText: requestPostData ? requestPostData.postData.text : "",
    };
    copyString(Curl.generateCommand(data));
  }

  /**
   * Copy the raw request headers from the currently selected item.
   */
  async copyRequestHeaders(id, requestHeaders) {
    requestHeaders = requestHeaders ||
      await this.props.connector.requestData(id, "requestHeaders");

    let rawHeaders = requestHeaders.rawHeaders.trim();

    if (Services.appinfo.OS !== "WINNT") {
      rawHeaders = rawHeaders.replace(/\r/g, "");
    }
    copyString(rawHeaders);
  }

  /**
   * Copy the raw response headers from the currently selected item.
   */
  async copyResponseHeaders(id, responseHeaders) {
    responseHeaders = responseHeaders ||
      await this.props.connector.requestData(id, "responseHeaders");

    let rawHeaders = responseHeaders.rawHeaders.trim();

    if (Services.appinfo.OS !== "WINNT") {
      rawHeaders = rawHeaders.replace(/\r/g, "");
    }
    copyString(rawHeaders);
  }

  /**
   * Copy image as data uri.
   */
  async copyImageAsDataUri(id, mimeType, responseContent) {
    responseContent = responseContent ||
      await this.props.connector.requestData(id, "responseContent");

    let { encoding, text } = responseContent.content;
    copyString(formDataURI(mimeType, encoding, text));
  }

  /**
   * Save image as.
   */
  async saveImageAs(id, url, responseContent) {
    responseContent = responseContent ||
      await this.props.connector.requestData(id, "responseContent");

    let { encoding, text } = responseContent.content;
    let fileName = getUrlBaseName(url);
    let data;
    if (encoding === "base64") {
      let decoded = atob(text);
      data = new Uint8Array(decoded.length);
      for (let i = 0; i < decoded.length; ++i) {
        data[i] = decoded.charCodeAt(i);
      }
    } else {
      data = text;
    }
    saveAs(new Blob([data]), fileName, document);
  }

  /**
   * Copy response data as a string.
   */
  async copyResponse(id, responseContent) {
    responseContent = responseContent ||
      await this.props.connector.requestData(id, "responseContent");

    copyString(responseContent.content.text);
  }

  /**
   * Copy HAR from the network panel content to the clipboard.
   */
  copyAllAsHar(sortedRequests) {
    return HarExporter.copy(this.getDefaultHarOptions(sortedRequests));
  }

  /**
   * Save HAR from the network panel content to a file.
   */
  saveAllAsHar(sortedRequests) {
    // This will not work in launchpad
    // document.execCommand(‘cut’/‘copy’) was denied because it was not called from
    // inside a short running user-generated event handler.
    // https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Interact_with_the_clipboard
    return HarExporter.save(this.getDefaultHarOptions(sortedRequests));
  }

  getDefaultHarOptions(sortedRequests) {
    return {
      connector: this.props.connector,
      items: sortedRequests,
    };
  }
}

module.exports = RequestListContextMenu;
PK
!<���	�	^chrome/devtools/modules/devtools/client/netmonitor/src/widgets/RequestListHeaderContextMenu.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { showMenu } = require("devtools/client/netmonitor/src/utils/menu");
const { HEADERS } = require("../constants");
const { L10N } = require("../utils/l10n");

const stringMap = HEADERS
  .filter((header) => header.hasOwnProperty("label"))
  .reduce((acc, { name, label }) => Object.assign(acc, { [name]: label }), {});

const subMenuMap = HEADERS
  .filter((header) => header.hasOwnProperty("subMenu"))
  .reduce((acc, { name, subMenu }) => Object.assign(acc, { [name]: subMenu }), {});

const nonLocalizedHeaders = HEADERS
  .filter((header) => header.hasOwnProperty("noLocalization"))
  .map((header) => header.name);

class RequestListHeaderContextMenu {
  constructor(props) {
    this.props = props;
  }

  /**
   * Handle the context menu opening.
   */
  open(event = {}, columns) {
    let menu = [];
    let subMenu = { timings: [], responseHeaders: [] };
    let visibleColumns = Object.entries(columns).filter(([column, shown]) => shown);
    let onlyOneColumn = visibleColumns.length === 1;

    for (let column in columns) {
      let shown = columns[column];
      let label = nonLocalizedHeaders.includes(column)
          ? stringMap[column] || column
          : L10N.getStr(`netmonitor.toolbar.${stringMap[column] || column}`);
      let entry = {
        id: `request-list-header-${column}-toggle`,
        label,
        type: "checkbox",
        checked: shown,
        click: () => this.props.toggleColumn(column),
        // We don't want to allow hiding the last visible column
        disabled: onlyOneColumn && shown,
      };
      subMenuMap.hasOwnProperty(column) ?
        subMenu[subMenuMap[column]].push(entry) :
        menu.push(entry);
    }

    menu.push({ type: "separator" });
    menu.push({
      label: L10N.getStr("netmonitor.toolbar.timings"),
      submenu: subMenu.timings,
    });
    menu.push({
      label: L10N.getStr("netmonitor.toolbar.responseHeaders"),
      submenu: subMenu.responseHeaders,
    });

    menu.push({ type: "separator" });
    menu.push({
      id: "request-list-header-reset-columns",
      label: L10N.getStr("netmonitor.toolbar.resetColumns"),
      click: () => this.props.resetColumns(),
    });

    return showMenu(event, menu);
  }
}

module.exports = RequestListHeaderContextMenu;
PK
!<�
�ooUchrome/devtools/modules/devtools/client/netmonitor/src/widgets/WaterfallBackground.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { getColor } = require("devtools/client/shared/theme");
const { colorUtils } = require("devtools/shared/css/color");
const { REQUESTS_WATERFALL } = require("../constants");

const HTML_NS = "http://www.w3.org/1999/xhtml";
const STATE_KEYS = [
  "firstRequestStartedMillis",
  "scale",
  "timingMarkers",
  "waterfallWidth",
];

/**
 * Creates the background displayed on each waterfall view in this container.
 */
class WaterfallBackground {
  constructor() {
    this.canvas = document.createElementNS(HTML_NS, "canvas");
    this.ctx = this.canvas.getContext("2d");
    this.prevState = {};
  }

  /**
   * Changes the element being used as the CSS background for a background
   * with a given background element ID.
   *
   * The funtion wrap the Firefox only API. Waterfall Will not draw the
   * vertical line when running on non-firefox browser.
   * Could be fixed by Bug 1308695
   */
  setImageElement(imageElementId, imageElement) {
    if (document.mozSetImageElement) {
      document.mozSetImageElement(imageElementId, imageElement);
    }
  }

  draw(state) {
    // Do a shallow compare of the previous and the new state
    const shouldUpdate = STATE_KEYS.some(key => this.prevState[key] !== state[key]);
    if (!shouldUpdate) {
      return;
    }

    this.prevState = state;

    if (state.waterfallWidth === null || state.scale === null) {
      this.setImageElement("waterfall-background", null);
      return;
    }

    // Nuke the context.
    let canvasWidth = this.canvas.width =
      state.waterfallWidth - REQUESTS_WATERFALL.LABEL_WIDTH;
    // Awww yeah, 1px, repeats on Y axis.
    let canvasHeight = this.canvas.height = 1;

    // Start over.
    let imageData = this.ctx.createImageData(canvasWidth, canvasHeight);
    let pixelArray = imageData.data;

    let buf = new ArrayBuffer(pixelArray.length);
    let view8bit = new Uint8ClampedArray(buf);
    let view32bit = new Uint32Array(buf);

    // Build new millisecond tick lines...
    let timingStep = REQUESTS_WATERFALL.BACKGROUND_TICKS_MULTIPLE;
    let optimalTickIntervalFound = false;
    let scaledStep;

    while (!optimalTickIntervalFound) {
      // Ignore any divisions that would end up being too close to each other.
      scaledStep = state.scale * timingStep;
      if (scaledStep < REQUESTS_WATERFALL.BACKGROUND_TICKS_SPACING_MIN) {
        timingStep <<= 1;
        continue;
      }
      optimalTickIntervalFound = true;
    }

    const isRTL = document.dir === "rtl";
    const [r, g, b] = REQUESTS_WATERFALL.BACKGROUND_TICKS_COLOR_RGB;
    let alphaComponent = REQUESTS_WATERFALL.BACKGROUND_TICKS_OPACITY_MIN;

    function drawPixelAt(offset, color) {
      let position = (isRTL ? canvasWidth - offset : offset - 1) | 0;
      let [rc, gc, bc, ac] = color;
      view32bit[position] = (ac << 24) | (bc << 16) | (gc << 8) | rc;
    }

    // Insert one pixel for each division on each scale.
    for (let i = 1; i <= REQUESTS_WATERFALL.BACKGROUND_TICKS_SCALES; i++) {
      let increment = scaledStep * Math.pow(2, i);
      for (let x = 0; x < canvasWidth; x += increment) {
        drawPixelAt(x, [r, g, b, alphaComponent]);
      }
      alphaComponent += REQUESTS_WATERFALL.BACKGROUND_TICKS_OPACITY_ADD;
    }

    function drawTimestamp(timestamp, color) {
      if (timestamp === -1) {
        return;
      }

      let delta = Math.floor((timestamp - state.firstRequestStartedMillis) * state.scale);
      drawPixelAt(delta, color);
    }

    let { DOMCONTENTLOADED_TICKS_COLOR, LOAD_TICKS_COLOR } = REQUESTS_WATERFALL;
    drawTimestamp(state.timingMarkers.firstDocumentDOMContentLoadedTimestamp,
      this.getThemeColorAsRgba(DOMCONTENTLOADED_TICKS_COLOR, state.theme));

    drawTimestamp(state.timingMarkers.firstDocumentLoadTimestamp,
      this.getThemeColorAsRgba(LOAD_TICKS_COLOR, state.theme));

    // Flush the image data and cache the waterfall background.
    pixelArray.set(view8bit);
    this.ctx.putImageData(imageData, 0, 0);

    this.setImageElement("waterfall-background", this.canvas);
  }

  /**
   * Retrieve a color defined for the provided theme as a rgba array. The alpha channel is
   * forced to the waterfall constant TICKS_COLOR_OPACITY.
   *
   * @param {String} colorName
   *        The name of the theme color
   * @param {String} theme
   *        The name of the theme
   * @return {Array} RGBA array for the color.
   */
  getThemeColorAsRgba(colorName, theme) {
    let colorStr = getColor(colorName, theme);
    let color = new colorUtils.CssColor(colorStr);
    let { r, g, b } = color.getRGBATuple();
    return [r, g, b, REQUESTS_WATERFALL.TICKS_COLOR_OPACITY];
  }

  destroy() {
    this.setImageElement("waterfall-background", null);
  }
}

module.exports = WaterfallBackground;
PK
!<�B��Xchrome/devtools/modules/devtools/client/performance/components/jit-optimizations-item.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/shared/l10n");
const STRINGS_URI = "devtools/client/locales/jit-optimizations.properties";
const L10N = new LocalizationHelper(STRINGS_URI);

const { PluralForm } = require("devtools/shared/plural-form");
const { Component, createFactory } = 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");
const Frame = createFactory(require("devtools/client/shared/components/Frame"));
const PROPNAME_MAX_LENGTH = 4;
// If TREE_ROW_HEIGHT changes, be sure to change `var(--jit-tree-row-height)`
// in `devtools/client/themes/jit-optimizations.css`
const TREE_ROW_HEIGHT = 14;

const OPTIMIZATION_ITEM_TYPES = ["site", "attempts", "types", "attempt", "type",
                                 "observedtype"];

/* eslint-disable no-unused-vars */
/**
 * TODO - Re-enable this eslint rule. The JIT tool is a work in progress, and isn't fully
 *        integrated as of yet.
 */
const {
  JITOptimizations, hasSuccessfulOutcome, isSuccessfulOutcome
} = require("devtools/client/performance/modules/logic/jit");
const OPTIMIZATION_FAILURE = L10N.getStr("jit.optimizationFailure");
const JIT_SAMPLES = L10N.getStr("jit.samples");
const JIT_TYPES = L10N.getStr("jit.types");
const JIT_ATTEMPTS = L10N.getStr("jit.attempts");

/* eslint-enable no-unused-vars */

class JITOptimizationsItem extends Component {
  static get propTypes() {
    return {
      onViewSourceInDebugger: PropTypes.func.isRequired,
      frameData: PropTypes.object.isRequired,
      type: PropTypes.oneOf(OPTIMIZATION_ITEM_TYPES).isRequired,
      depth: PropTypes.number.isRequired,
      arrow: PropTypes.element.isRequired,
      item: PropTypes.object,
      focused: PropTypes.bool
    };
  }

  constructor(props) {
    super(props);
    this._renderSite = this._renderSite.bind(this);
    this._renderAttempts = this._renderAttempts.bind(this);
    this._renderTypes = this._renderTypes.bind(this);
    this._renderAttempt = this._renderAttempt.bind(this);
    this._renderType = this._renderType.bind(this);
    this._renderObservedType = this._renderObservedType.bind(this);
  }

  _renderSite({ item: site, onViewSourceInDebugger, frameData }) {
    let attempts = site.data.attempts;
    let lastStrategy = attempts[attempts.length - 1].strategy;
    let propString = "";
    let propertyName = site.data.propertyName;

    // Display property name if it exists
    if (propertyName) {
      if (propertyName.length > PROPNAME_MAX_LENGTH) {
        propString = ` (.${propertyName.substr(0, PROPNAME_MAX_LENGTH)}…)`;
      } else {
        propString = ` (.${propertyName})`;
      }
    }

    let sampleString = PluralForm.get(site.samples, JIT_SAMPLES)
      .replace("#1", site.samples);
    let text = dom.span(
      { className: "optimization-site-title" },
      `${lastStrategy}${propString} – (${sampleString})`
    );
    let frame = Frame({
      onClick: () => onViewSourceInDebugger(frameData.url, site.data.line),
      frame: {
        source: frameData.url,
        line: +site.data.line,
        column: site.data.column,
      }
    });
    let children = [text, frame];

    if (!hasSuccessfulOutcome(site)) {
      children.unshift(dom.span({ className: "opt-icon warning" }));
    }

    return dom.span({ className: "optimization-site" }, ...children);
  }

  _renderAttempts({ item: attempts }) {
    return dom.span({ className: "optimization-attempts" },
      `${JIT_ATTEMPTS} (${attempts.length})`
    );
  }

  _renderTypes({ item: types }) {
    return dom.span({ className: "optimization-types" },
      `${JIT_TYPES} (${types.length})`
    );
  }

  _renderAttempt({ item: attempt }) {
    let success = isSuccessfulOutcome(attempt.outcome);
    let { strategy, outcome } = attempt;
    return dom.span({ className: "optimization-attempt" },
      dom.span({ className: "optimization-strategy" }, strategy),
      " → ",
      dom.span({ className: `optimization-outcome ${success ? "success" : "failure"}` },
               outcome)
    );
  }

  _renderType({ item: type }) {
    return dom.span({ className: "optimization-ion-type" },
                    `${type.site}:${type.mirType}`);
  }

  _renderObservedType({ onViewSourceInDebugger, item: type }) {
    let children = [
      dom.span({ className: "optimization-observed-type-keyed" },
        `${type.keyedBy}${type.name ? ` → ${type.name}` : ""}`)
    ];

    // If we have a line and location, make a link to the debugger
    if (type.location && type.line) {
      children.push(
        Frame({
          onClick: () => onViewSourceInDebugger(type.location, type.line),
          frame: {
            source: type.location,
            line: type.line,
            column: type.column,
          }
        })
      );
    // Otherwise if we just have a location, it's probably just a memory location.
    } else if (type.location) {
      children.push(`@${type.location}`);
    }

    return dom.span({ className: "optimization-observed-type" }, ...children);
  }

  render() {
    /* eslint-disable no-unused-vars */
    /**
     * TODO - Re-enable this eslint rule. The JIT tool is a work in progress, and these
     *        undefined variables may represent intended functionality.
     */
    let {
      depth,
      arrow,
      type,
      // TODO - The following are currently unused.
      item,
      focused,
      frameData,
      onViewSourceInDebugger,
    } = this.props;
    /* eslint-enable no-unused-vars */

    let content;
    switch (type) {
      case "site": content = this._renderSite(this.props); break;
      case "attempts": content = this._renderAttempts(this.props); break;
      case "types": content = this._renderTypes(this.props); break;
      case "attempt": content = this._renderAttempt(this.props); break;
      case "type": content = this._renderType(this.props); break;
      case "observedtype": content = this._renderObservedType(this.props); break;
    }

    return dom.div(
      {
        className: `optimization-tree-item optimization-tree-item-${type}`,
        style: { marginInlineStart: depth * TREE_ROW_HEIGHT }
      },
      arrow,
      content
    );
  }
}

module.exports = JITOptimizationsItem;
PK
!<G�zllSchrome/devtools/modules/devtools/client/performance/components/jit-optimizations.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/shared/l10n");
const STRINGS_URI = "devtools/client/locales/jit-optimizations.properties";
const L10N = new LocalizationHelper(STRINGS_URI);

const { assert } = require("devtools/shared/DevToolsUtils");
const { Component, createFactory } = 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");
const Tree = createFactory(require("devtools/client/shared/components/VirtualizedTree"));
const OptimizationsItem = createFactory(require("./jit-optimizations-item"));
const FrameView = createFactory(require("../../shared/components/Frame"));
const JIT_TITLE = L10N.getStr("jit.title");
// If TREE_ROW_HEIGHT changes, be sure to change `var(--jit-tree-row-height)`
// in `devtools/client/themes/jit-optimizations.css`
const TREE_ROW_HEIGHT = 14;

/* eslint-disable no-unused-vars */
/**
 * TODO - Re-enable this eslint rule. The JIT tool is a work in progress, and isn't fully
 *        integrated as of yet, and this may represent intended functionality.
 */
const onClickTooltipString = frame =>
  L10N.getFormatStr("viewsourceindebugger",
                    `${frame.source}:${frame.line}:${frame.column}`);
/* eslint-enable no-unused-vars */

const optimizationAttemptModel = {
  id: PropTypes.number.isRequired,
  strategy: PropTypes.string.isRequired,
  outcome: PropTypes.string.isRequired,
};

const optimizationObservedTypeModel = {
  keyedBy: PropTypes.string.isRequired,
  name: PropTypes.string,
  location: PropTypes.string,
  line: PropTypes.string,
};

const optimizationIonTypeModel = {
  id: PropTypes.number.isRequired,
  typeset: PropTypes.arrayOf(optimizationObservedTypeModel),
  site: PropTypes.number.isRequired,
  mirType: PropTypes.number.isRequired,
};

const optimizationSiteModel = {
  id: PropTypes.number.isRequired,
  propertyName: PropTypes.string,
  line: PropTypes.number.isRequired,
  column: PropTypes.number.isRequired,
  data: PropTypes.shape({
    attempts: PropTypes.arrayOf(optimizationAttemptModel).isRequired,
    types: PropTypes.arrayOf(optimizationIonTypeModel).isRequired,
  }).isRequired,
};

class JITOptimizations extends Component {
  static get propTypes() {
    return {
      onViewSourceInDebugger: PropTypes.func.isRequired,
      frameData: PropTypes.object.isRequired,
      optimizationSites: PropTypes.arrayOf(optimizationSiteModel).isRequired,
      autoExpandDepth: PropTypes.number,
    };
  }

  static get defaultProps() {
    return {
      autoExpandDepth: 0
    };
  }

  constructor(props) {
    super(props);

    this.state = {
      expanded: new Set()
    };

    this._createHeader = this._createHeader.bind(this);
    this._createTree = this._createTree.bind(this);
  }

  /**
   * Frame data generated from `frameNode.getInfo()`, or an empty
   * object, as well as a handler for clicking on the frame component.
   *
   * @param {?Object} .frameData
   * @param {Function} .onViewSourceInDebugger
   * @return {ReactElement}
   */
  _createHeader({ frameData, onViewSourceInDebugger }) {
    let { isMetaCategory, url, line } = frameData;
    let name = isMetaCategory ? frameData.categoryData.label :
               frameData.functionName || "";

    // Simulate `SavedFrame`s interface
    let frame = { source: url, line: +line, functionDisplayName: name };

    // Neither Meta Category nodes, or the lack of a selected frame node,
    // renders out a frame source, like "file.js:123"; so just use
    // an empty span.
    let frameComponent;
    if (isMetaCategory || !name) {
      frameComponent = dom.span();
    } else {
      frameComponent = FrameView({
        frame,
        onClick: () => onViewSourceInDebugger(frame),
      });
    }

    return dom.div({ className: "optimization-header" },
      dom.span({ className: "header-title" }, JIT_TITLE),
      dom.span({ className: "header-function-name" }, name),
      frameComponent
    );
  }

  _createTree(props) {
    let {
      autoExpandDepth,
      frameData,
      onViewSourceInDebugger,
      optimizationSites: sites
    } = this.props;

    let getSite = id => sites.find(site => site.id === id);
    let getIonTypeForObserved = type => {
      return getSite(type.id).data.types
        .find(iontype => (iontype.typeset || [])
        .includes(type));
    };
    let isSite = site => getSite(site.id) === site;
    let isAttempts = attempts => getSite(attempts.id).data.attempts === attempts;
    let isAttempt = attempt => getSite(attempt.id).data.attempts.includes(attempt);
    let isTypes = types => getSite(types.id).data.types === types;
    let isType = type => getSite(type.id).data.types.includes(type);
    let isObservedType = type => getIonTypeForObserved(type);

    let getRowType = node => {
      if (isSite(node)) {
        return "site";
      }
      if (isAttempts(node)) {
        return "attempts";
      }
      if (isTypes(node)) {
        return "types";
      }
      if (isAttempt(node)) {
        return "attempt";
      }
      if (isType(node)) {
        return "type";
      }
      if (isObservedType(node)) {
        return "observedtype";
      }
      return null;
    };

    // Creates a unique key for each node in the
    // optimizations data
    let getKey = node => {
      let site = getSite(node.id);
      if (isSite(node)) {
        return node.id;
      } else if (isAttempts(node)) {
        return `${node.id}-A`;
      } else if (isTypes(node)) {
        return `${node.id}-T`;
      } else if (isType(node)) {
        return `${node.id}-T-${site.data.types.indexOf(node)}`;
      } else if (isAttempt(node)) {
        return `${node.id}-A-${site.data.attempts.indexOf(node)}`;
      } else if (isObservedType(node)) {
        let iontype = getIonTypeForObserved(node);
        return `${getKey(iontype)}-O-${iontype.typeset.indexOf(node)}`;
      }
      return "";
    };

    return Tree({
      autoExpandDepth,
      getParent: node => {
        let site = getSite(node.id);
        let parent;
        if (isAttempts(node) || isTypes(node)) {
          parent = site;
        } else if (isType(node)) {
          parent = site.data.types;
        } else if (isAttempt(node)) {
          parent = site.data.attempts;
        } else if (isObservedType(node)) {
          parent = getIonTypeForObserved(node);
        }
        assert(parent, "Could not find a parent for optimization data node");

        return parent;
      },
      getChildren: node => {
        if (isSite(node)) {
          return [node.data.types, node.data.attempts];
        } else if (isAttempts(node) || isTypes(node)) {
          return node;
        } else if (isType(node)) {
          return node.typeset || [];
        }
        return [];
      },
      isExpanded: node => this.state.expanded.has(node),
      onExpand: node => this.setState(state => {
        let expanded = new Set(state.expanded);
        expanded.add(node);
        return { expanded };
      }),
      onCollapse: node => this.setState(state => {
        let expanded = new Set(state.expanded);
        expanded.delete(node);
        return { expanded };
      }),
      onFocus: function () {},
      getKey,
      getRoots: () => sites || [],
      itemHeight: TREE_ROW_HEIGHT,
      renderItem: (item, depth, focused, arrow, expanded) =>
        new OptimizationsItem({
          onViewSourceInDebugger,
          item,
          depth,
          focused,
          arrow,
          expanded,
          type: getRowType(item),
          frameData,
        }),
    });
  }

  render() {
    let header = this._createHeader(this.props);
    let tree = this._createTree(this.props);

    return dom.div({}, header, tree);
  }
}

module.exports = JITOptimizations;
PK
!<��/�**Rchrome/devtools/modules/devtools/client/performance/components/recording-button.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { L10N } = require("devtools/client/performance/modules/global");
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 { button } = dom;

class RecordingButton extends Component {
  static get propTypes() {
    return {
      onRecordButtonClick: PropTypes.func.isRequired,
      isRecording: PropTypes.bool,
      isLocked: PropTypes.bool
    };
  }

  render() {
    let {
      onRecordButtonClick,
      isRecording,
      isLocked
    } = this.props;

    let classList = ["devtools-button", "record-button"];

    if (isRecording) {
      classList.push("checked");
    }

    return button(
      {
        className: classList.join(" "),
        onClick: onRecordButtonClick,
        "data-standalone": "true",
        "data-text-only": "true",
        disabled: isLocked
      },
      isRecording ? L10N.getStr("recordings.stop") : L10N.getStr("recordings.start")
    );
  }
}

module.exports = RecordingButton;
PK
!<]h���Tchrome/devtools/modules/devtools/client/performance/components/recording-controls.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { L10N } = require("devtools/client/performance/modules/global");
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 { div, button } = dom;

class RecordingControls extends Component {
  static get propTypes() {
    return {
      onClearButtonClick: PropTypes.func.isRequired,
      onRecordButtonClick: PropTypes.func.isRequired,
      onImportButtonClick: PropTypes.func.isRequired,
      isRecording: PropTypes.bool,
      isLocked: PropTypes.bool
    };
  }

  render() {
    let {
      onClearButtonClick,
      onRecordButtonClick,
      onImportButtonClick,
      isRecording,
      isLocked
    } = this.props;

    let recordButtonClassList = ["devtools-button", "record-button"];

    if (isRecording) {
      recordButtonClassList.push("checked");
    }

    return (
      div({ className: "devtools-toolbar" },
        div({ className: "toolbar-group" },
          button({
            id: "clear-button",
            className: "devtools-button",
            title: L10N.getStr("recordings.clear.tooltip"),
            onClick: onClearButtonClick
          }),
          button({
            id: "main-record-button",
            className: recordButtonClassList.join(" "),
            disabled: isLocked,
            title: L10N.getStr("recordings.start.tooltip"),
            onClick: onRecordButtonClick
          }),
          button({
            id: "import-button",
            className: "devtools-button",
            title: L10N.getStr("recordings.import.tooltip"),
            onClick: onImportButtonClick
          })
        )
      )
    );
  }
}

module.exports = RecordingControls;
PK
!<#��w��Uchrome/devtools/modules/devtools/client/performance/components/recording-list-item.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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 { div, li, span, button } = dom;
const { L10N } = require("devtools/client/performance/modules/global");

class RecordingListItem extends Component {
  static get propTypes() {
    return {
      label: PropTypes.string.isRequired,
      duration: PropTypes.string,
      onSelect: PropTypes.func.isRequired,
      onSave: PropTypes.func.isRequired,
      isLoading: PropTypes.bool,
      isSelected: PropTypes.bool,
      isRecording: PropTypes.bool
    };
  }

  render() {
    const {
      label,
      duration,
      onSelect,
      onSave,
      isLoading,
      isSelected,
      isRecording
    } = this.props;

    const className = `recording-list-item ${isSelected ? "selected" : ""}`;

    let durationText;
    if (isLoading) {
      durationText = L10N.getStr("recordingsList.loadingLabel");
    } else if (isRecording) {
      durationText = L10N.getStr("recordingsList.recordingLabel");
    } else {
      durationText = L10N.getFormatStr("recordingsList.durationLabel", duration);
    }

    return (
      li({ className, onClick: onSelect },
        div({ className: "recording-list-item-label" },
          label
        ),
        div({ className: "recording-list-item-footer" },
          span({ className: "recording-list-item-duration" }, durationText),
          button({ className: "recording-list-item-save", onClick: onSave },
            L10N.getStr("recordingsList.saveLabel")
          )
        )
      )
    );
  }
}

module.exports = RecordingListItem;
PK
!<��e.Pchrome/devtools/modules/devtools/client/performance/components/recording-list.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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 { L10N } = require("devtools/client/performance/modules/global");
const { ul, div } = dom;

class RecordingList extends Component {
  static get propTypes() {
    return {
      items: PropTypes.arrayOf(PropTypes.object).isRequired,
      itemComponent: PropTypes.func.isRequired
    };
  }

  render() {
    const {
      items,
      itemComponent: Item,
    } = this.props;

    return items.length > 0
      ? ul({ className: "recording-list" }, ...items.map(Item))
      : div({ className: "recording-list-empty" }, L10N.getStr("noRecordingsText"));
  }
}

module.exports = RecordingList;
PK
!<��==Rchrome/devtools/modules/devtools/client/performance/components/waterfall-header.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 "waterfall ticks" view, a header for the markers displayed in the waterfall.
 */

const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const { L10N } = require("../modules/global");
const { TickUtils } = require("../modules/waterfall-ticks");

const WATERFALL_HEADER_TICKS_MULTIPLE = 5; // ms
const WATERFALL_HEADER_TICKS_SPACING_MIN = 50; // px
const WATERFALL_HEADER_TEXT_PADDING = 3; // px

function WaterfallHeader(props) {
  let { startTime, dataScale, sidebarWidth, waterfallWidth } = props;

  let tickInterval = TickUtils.findOptimalTickInterval({
    ticksMultiple: WATERFALL_HEADER_TICKS_MULTIPLE,
    ticksSpacingMin: WATERFALL_HEADER_TICKS_SPACING_MIN,
    dataScale: dataScale
  });

  let ticks = [];
  for (let x = 0; x < waterfallWidth; x += tickInterval) {
    let left = x + WATERFALL_HEADER_TEXT_PADDING;
    let time = Math.round(x / dataScale + startTime);
    let label = L10N.getFormatStr("timeline.tick", time);

    let node = dom.div({
      key: x,
      className: "plain waterfall-header-tick",
      style: { transform: `translateX(${left}px)` }
    }, label);
    ticks.push(node);
  }

  return dom.div(
    { className: "waterfall-header" },
    dom.div(
      {
        className: "waterfall-sidebar theme-sidebar waterfall-header-name",
        style: { width: sidebarWidth + "px" }
      },
      L10N.getStr("timeline.records")
    ),
    dom.div(
      { className: "waterfall-header-ticks waterfall-background-ticks" },
      ticks
    )
  );
}

WaterfallHeader.displayName = "WaterfallHeader";

WaterfallHeader.propTypes = {
  startTime: PropTypes.number.isRequired,
  dataScale: PropTypes.number.isRequired,
  sidebarWidth: PropTypes.number.isRequired,
  waterfallWidth: PropTypes.number.isRequired,
};

module.exports = WaterfallHeader;
PK
!<y�8^$$Tchrome/devtools/modules/devtools/client/performance/components/waterfall-tree-row.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 single row (node) in the waterfall tree
 */

const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const { MarkerBlueprintUtils } = require("../modules/marker-blueprint-utils");

const LEVEL_INDENT = 10; // px
const ARROW_NODE_OFFSET = -14; // px
const WATERFALL_MARKER_TIMEBAR_WIDTH_MIN = 5; // px

function buildMarkerSidebar(blueprint, props) {
  const { marker, level, sidebarWidth } = props;

  let bullet = dom.div({
    className: `waterfall-marker-bullet marker-color-${blueprint.colorName}`,
    style: { transform: `translateX(${level * LEVEL_INDENT}px)` },
    "data-type": marker.name
  });

  let label = MarkerBlueprintUtils.getMarkerLabel(marker);

  let name = dom.div({
    className: "plain waterfall-marker-name",
    style: { transform: `translateX(${level * LEVEL_INDENT}px)` },
    title: label
  }, label);

  return dom.div({
    className: "waterfall-sidebar theme-sidebar",
    style: { width: sidebarWidth + "px" }
  }, bullet, name);
}

function buildMarkerTimebar(blueprint, props) {
  const { marker, startTime, dataScale, arrow } = props;
  const offset = (marker.start - startTime) * dataScale + ARROW_NODE_OFFSET;
  const width = Math.max((marker.end - marker.start) * dataScale,
                         WATERFALL_MARKER_TIMEBAR_WIDTH_MIN);

  let bar = dom.div(
    {
      className: "waterfall-marker-wrap",
      style: { transform: `translateX(${offset}px)` }
    },
    arrow,
    dom.div({
      className: `waterfall-marker-bar marker-color-${blueprint.colorName}`,
      style: { width: `${width}px` },
      "data-type": marker.name
    })
  );

  return dom.div(
    { className: "waterfall-marker waterfall-background-ticks" },
    bar
  );
}

function WaterfallTreeRow(props) {
  const { marker, focused } = props;
  const blueprint = MarkerBlueprintUtils.getBlueprintFor(marker);

  let attrs = {
    className: "waterfall-tree-item" + (focused ? " focused" : ""),
    "data-otmt": marker.isOffMainThread
  };

  // Don't render an expando-arrow for leaf nodes.
  let submarkers = marker.submarkers;
  let hasDescendants = submarkers && submarkers.length > 0;
  if (hasDescendants) {
    attrs["data-expandable"] = "";
  } else {
    attrs["data-invisible"] = "";
  }

  return dom.div(
    attrs,
    buildMarkerSidebar(blueprint, props),
    buildMarkerTimebar(blueprint, props)
  );
}

WaterfallTreeRow.displayName = "WaterfallTreeRow";

WaterfallTreeRow.propTypes = {
  marker: PropTypes.object.isRequired,
  level: PropTypes.number.isRequired,
  arrow: PropTypes.element.isRequired,
  expanded: PropTypes.bool.isRequired,
  focused: PropTypes.bool.isRequired,
  startTime: PropTypes.number.isRequired,
  dataScale: PropTypes.number.isRequired,
  sidebarWidth: PropTypes.number.isRequired,
};

module.exports = WaterfallTreeRow;
PK
!<&b���Pchrome/devtools/modules/devtools/client/performance/components/waterfall-tree.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/vendor/react");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const Tree = createFactory(require("devtools/client/shared/components/VirtualizedTree"));
const WaterfallTreeRow = createFactory(require("./waterfall-tree-row"));

// Keep in sync with var(--waterfall-tree-row-height) in performance.css
const WATERFALL_TREE_ROW_HEIGHT = 15; // px

/**
 * Checks if a given marker is in the specified time range.
 *
 * @param object e
 *        The marker containing the { start, end } timestamps.
 * @param number start
 *        The earliest allowed time.
 * @param number end
 *        The latest allowed time.
 * @return boolean
 *         True if the marker fits inside the specified time range.
 */
function isMarkerInRange(e, start, end) {
  let mStart = e.start | 0;
  let mEnd = e.end | 0;

  return (
    // bounds inside
    (mStart >= start && mEnd <= end) ||
    // bounds outside
    (mStart < start && mEnd > end) ||
    // overlap start
    (mStart < start && mEnd >= start && mEnd <= end) ||
    // overlap end
    (mEnd > end && mStart >= start && mStart <= end)
  );
}

class WaterfallTree extends Component {
  static get propTypes() {
    return {
      marker: PropTypes.object.isRequired,
      startTime: PropTypes.number.isRequired,
      endTime: PropTypes.number.isRequired,
      dataScale: PropTypes.number.isRequired,
      sidebarWidth: PropTypes.number.isRequired,
      waterfallWidth: PropTypes.number.isRequired,
      onFocus: PropTypes.func,
    };
  }

  constructor(props) {
    super(props);

    this.state = {
      focused: null,
      expanded: new Set()
    };

    this._getRoots = this._getRoots.bind(this);
    this._getParent = this._getParent.bind(this);
    this._getChildren = this._getChildren.bind(this);
    this._getKey = this._getKey.bind(this);
    this._isExpanded = this._isExpanded.bind(this);
    this._onExpand = this._onExpand.bind(this);
    this._onCollapse = this._onCollapse.bind(this);
    this._onFocus = this._onFocus.bind(this);
    this._filter = this._filter.bind(this);
    this._renderItem = this._renderItem.bind(this);
  }

  _getRoots(node) {
    let roots = this.props.marker.submarkers || [];
    return roots.filter(this._filter);
  }

  /**
   * Find the parent node of 'node' with a depth-first search of the marker tree
   */
  _getParent(node) {
    function findParent(marker) {
      if (marker.submarkers) {
        for (let submarker of marker.submarkers) {
          if (submarker === node) {
            return marker;
          }

          let parent = findParent(submarker);
          if (parent) {
            return parent;
          }
        }
      }

      return null;
    }

    let rootMarker = this.props.marker;
    let parent = findParent(rootMarker);

    // We are interested only in parent markers that are rendered,
    // which rootMarker is not. Return null if the parent is rootMarker.
    return parent !== rootMarker ? parent : null;
  }

  _getChildren(node) {
    let submarkers = node.submarkers || [];
    return submarkers.filter(this._filter);
  }

  _getKey(node) {
    return `marker-${node.index}`;
  }

  _isExpanded(node) {
    return this.state.expanded.has(node);
  }

  _onExpand(node) {
    this.setState(state => {
      let expanded = new Set(state.expanded);
      expanded.add(node);
      return { expanded };
    });
  }

  _onCollapse(node) {
    this.setState(state => {
      let expanded = new Set(state.expanded);
      expanded.delete(node);
      return { expanded };
    });
  }

  _onFocus(node) {
    this.setState({ focused: node });
    if (this.props.onFocus) {
      this.props.onFocus(node);
    }
  }

  _filter(node) {
    let { startTime, endTime } = this.props;
    return isMarkerInRange(node, startTime, endTime);
  }

  _renderItem(marker, level, focused, arrow, expanded) {
    let { startTime, dataScale, sidebarWidth } = this.props;
    return WaterfallTreeRow({
      marker,
      level,
      arrow,
      expanded,
      focused,
      startTime,
      dataScale,
      sidebarWidth
    });
  }

  render() {
    return Tree({
      getRoots: this._getRoots,
      getParent: this._getParent,
      getChildren: this._getChildren,
      getKey: this._getKey,
      isExpanded: this._isExpanded,
      onExpand: this._onExpand,
      onCollapse: this._onCollapse,
      onFocus: this._onFocus,
      renderItem: this._renderItem,
      focused: this.state.focused,
      itemHeight: WATERFALL_TREE_ROW_HEIGHT
    });
  }
}

module.exports = WaterfallTree;
PK
!<�
����Kchrome/devtools/modules/devtools/client/performance/components/waterfall.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 contains the "waterfall" view, essentially a detailed list
 * of all the markers in the timeline data.
 */

const { createFactory } = 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 WaterfallHeader = createFactory(require("./waterfall-header"));
const WaterfallTree = createFactory(require("./waterfall-tree"));

function Waterfall(props) {
  return dom.div(
    { className: "waterfall-markers" },
    WaterfallHeader(props),
    WaterfallTree(props)
  );
}

Waterfall.displayName = "Waterfall";

Waterfall.propTypes = {
  marker: PropTypes.object.isRequired,
  startTime: PropTypes.number.isRequired,
  endTime: PropTypes.number.isRequired,
  dataScale: PropTypes.number.isRequired,
  sidebarWidth: PropTypes.number.isRequired,
  waterfallWidth: PropTypes.number.isRequired,
  onFocus: PropTypes.func,
  onBlur: PropTypes.func,
};

module.exports = Waterfall;
PK
!<iP�9��=chrome/devtools/modules/devtools/client/performance/events.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 ControllerEvents = {
  // Fired when a performance pref changes (either because the user changed it
  // via the tool's UI, by changing something about:config or programatically).
  PREF_CHANGED: "Performance:PrefChanged",

  // Fired when the devtools theme changes.
  THEME_CHANGED: "Performance:ThemeChanged",

  // When a new recording model is received by the controller.
  RECORDING_ADDED: "Performance:RecordingAdded",

  // When a recording model gets removed from the controller.
  RECORDING_DELETED: "Performance:RecordingDeleted",

  // When a recording model becomes "started", "stopping" or "stopped".
  RECORDING_STATE_CHANGE: "Performance:RecordingStateChange",

  // When a recording is offering information on the profiler's circular buffer.
  RECORDING_PROFILER_STATUS_UPDATE: "Performance:RecordingProfilerStatusUpdate",

  // When a recording model becomes marked as selected.
  RECORDING_SELECTED: "Performance:RecordingSelected",

  // When starting a recording is attempted and fails because the backend
  // does not permit it at this time.
  BACKEND_FAILED_AFTER_RECORDING_START: "Performance:BackendFailedRecordingStart",

  // When a recording is started and the backend has started working.
  BACKEND_READY_AFTER_RECORDING_START: "Performance:BackendReadyRecordingStart",

  // When a recording is stopped and the backend has finished cleaning up.
  BACKEND_READY_AFTER_RECORDING_STOP: "Performance:BackendReadyRecordingStop",

  // When a recording is exported.
  RECORDING_EXPORTED: "Performance:RecordingExported",

  // When a recording is imported.
  RECORDING_IMPORTED: "Performance:RecordingImported",

  // When a source is shown in the JavaScript Debugger at a specific location.
  SOURCE_SHOWN_IN_JS_DEBUGGER: "Performance:UI:SourceShownInJsDebugger",
  SOURCE_NOT_FOUND_IN_JS_DEBUGGER: "Performance:UI:SourceNotFoundInJsDebugger",

  // Fired by the PerformanceController when `populateWithRecordings` is finished.
  RECORDINGS_SEEDED: "Performance:RecordingsSeeded",
};

const ViewEvents = {
  // Emitted by the `ToolbarView` when a preference changes.
  UI_PREF_CHANGED: "Performance:UI:PrefChanged",

  // When the state (display mode) changes, for example when switching between
  // "empty", "recording" or "recorded". This causes certain parts of the UI
  // to be hidden or visible.
  UI_STATE_CHANGED: "Performance:UI:StateChanged",

  // Emitted by the `PerformanceView` on clear button click.
  UI_CLEAR_RECORDINGS: "Performance:UI:ClearRecordings",

  // Emitted by the `PerformanceView` on record button click.
  UI_START_RECORDING: "Performance:UI:StartRecording",
  UI_STOP_RECORDING: "Performance:UI:StopRecording",

  // Emitted by the `PerformanceView` on import/export button click.
  UI_IMPORT_RECORDING: "Performance:UI:ImportRecording",
  UI_EXPORT_RECORDING: "Performance:UI:ExportRecording",

  // Emitted by the `PerformanceView` when the profiler's circular buffer
  // status has been rendered.
  UI_RECORDING_PROFILER_STATUS_RENDERED: "Performance:UI:RecordingProfilerStatusRendered",

  // When a recording is selected in the UI.
  UI_RECORDING_SELECTED: "Performance:UI:RecordingSelected",

  // Emitted by the `DetailsView` when a subview is selected
  UI_DETAILS_VIEW_SELECTED: "Performance:UI:DetailsViewSelected",

  // Emitted by the `OverviewView` after something has been rendered.
  UI_OVERVIEW_RENDERED: "Performance:UI:OverviewRendered",
  UI_MARKERS_GRAPH_RENDERED: "Performance:UI:OverviewMarkersRendered",
  UI_MEMORY_GRAPH_RENDERED: "Performance:UI:OverviewMemoryRendered",
  UI_FRAMERATE_GRAPH_RENDERED: "Performance:UI:OverviewFramerateRendered",

  // Emitted by the `OverviewView` when a range has been selected in the graphs.
  UI_OVERVIEW_RANGE_SELECTED: "Performance:UI:OverviewRangeSelected",

  // Emitted by the `WaterfallView` when it has been rendered.
  UI_WATERFALL_RENDERED: "Performance:UI:WaterfallRendered",

  // Emitted by the `JsCallTreeView` when it has been rendered.
  UI_JS_CALL_TREE_RENDERED: "Performance:UI:JsCallTreeRendered",

  // Emitted by the `JsFlameGraphView` when it has been rendered.
  UI_JS_FLAMEGRAPH_RENDERED: "Performance:UI:JsFlameGraphRendered",

  // Emitted by the `MemoryCallTreeView` when it has been rendered.
  UI_MEMORY_CALL_TREE_RENDERED: "Performance:UI:MemoryCallTreeRendered",

  // Emitted by the `MemoryFlameGraphView` when it has been rendered.
  UI_MEMORY_FLAMEGRAPH_RENDERED: "Performance:UI:MemoryFlameGraphRendered",
};

module.exports = Object.assign({}, ControllerEvents, ViewEvents);
PK
!<'Gr��Ichrome/devtools/modules/devtools/client/performance/modules/categories.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { L10N } = require("devtools/client/performance/modules/global");

/**
 * Details about each profile pseudo-stack entry cateogry.
 * @see CATEGORY_MAPPINGS.
 */
const CATEGORIES = [{
  color: "#5e88b0",
  abbrev: "other",
  label: L10N.getStr("category.other")
}, {
  color: "#46afe3",
  abbrev: "css",
  label: L10N.getStr("category.css")
}, {
  color: "#d96629",
  abbrev: "js",
  label: L10N.getStr("category.js")
}, {
  color: "#eb5368",
  abbrev: "gc",
  label: L10N.getStr("category.gc")
}, {
  color: "#df80ff",
  abbrev: "network",
  label: L10N.getStr("category.network")
}, {
  color: "#70bf53",
  abbrev: "graphics",
  label: L10N.getStr("category.graphics")
}, {
  color: "#8fa1b2",
  abbrev: "storage",
  label: L10N.getStr("category.storage")
}, {
  color: "#d99b28",
  abbrev: "events",
  label: L10N.getStr("category.events")
}, {
  color: "#8fa1b2",
  abbrev: "tools",
  label: L10N.getStr("category.tools")
}];

/**
 * Mapping from category bitmasks in the profiler data to additional details.
 * To be kept in sync with the js::ProfileEntry::Category in ProfilingStack.h
 */
const CATEGORY_MAPPINGS = {
  // js::ProfileEntry::Category::OTHER
  "16": CATEGORIES[0],
  // js::ProfileEntry::Category::CSS
  "32": CATEGORIES[1],
  // js::ProfileEntry::Category::JS
  "64": CATEGORIES[2],
  // js::ProfileEntry::Category::GC
  "128": CATEGORIES[3],
  // js::ProfileEntry::Category::CC
  "256": CATEGORIES[3],
  // js::ProfileEntry::Category::NETWORK
  "512": CATEGORIES[4],
  // js::ProfileEntry::Category::GRAPHICS
  "1024": CATEGORIES[5],
  // js::ProfileEntry::Category::STORAGE
  "2048": CATEGORIES[6],
  // js::ProfileEntry::Category::EVENTS
  "4096": CATEGORIES[7],
  // non-bitmasks for specially-assigned categories
  "9000": CATEGORIES[8],
};

/**
 * Get the numeric bitmask (or set of masks) for the given category
 * abbreviation. See `CATEGORIES` and `CATEGORY_MAPPINGS` above.
 *
 * CATEGORY_MASK can be called with just a name if it is expected that the
 * category is mapped to by exactly one bitmask. If the category is mapped
 * to by multiple masks, CATEGORY_MASK for that name must be called with
 * an additional argument specifying the desired id (in ascending order).
 */
const [CATEGORY_MASK, CATEGORY_MASK_LIST] = (() => {
  let bitmasksForCategory = {};
  let all = Object.keys(CATEGORY_MAPPINGS);

  for (let category of CATEGORIES) {
    bitmasksForCategory[category.abbrev] = all
      .filter(mask => CATEGORY_MAPPINGS[mask] == category)
      .map(mask => +mask)
      .sort();
  }

  return [
    function (name, index) {
      if (!(name in bitmasksForCategory)) {
        throw new Error(`Category abbreviation "${name}" does not exist.`);
      }
      if (arguments.length == 1) {
        if (bitmasksForCategory[name].length != 1) {
          throw new Error(`Expected exactly one category number for "${name}".`);
        } else {
          return bitmasksForCategory[name][0];
        }
      } else {
        if (index > bitmasksForCategory[name].length) {
          throw new Error(`Index "${index}" too high for category "${name}".`);
        }
        return bitmasksForCategory[name][index - 1];
      }
    },

    function (name) {
      if (!(name in bitmasksForCategory)) {
        throw new Error(`Category abbreviation "${name}" does not exist.`);
      }
      return bitmasksForCategory[name];
    }
  ];
})();

exports.CATEGORIES = CATEGORIES;
exports.CATEGORY_MAPPINGS = CATEGORY_MAPPINGS;
exports.CATEGORY_MASK = CATEGORY_MASK;
exports.CATEGORY_MASK_LIST = CATEGORY_MASK_LIST;
PK
!<��WWHchrome/devtools/modules/devtools/client/performance/modules/constants.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";

exports.Constants = {
  // ms
  FRAMERATE_GRAPH_LOW_RES_INTERVAL: 100,
  // ms
  FRAMERATE_GRAPH_HIGH_RES_INTERVAL: 16,
};
PK
!<����QQEchrome/devtools/modules/devtools/client/performance/modules/global.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { MultiLocalizationHelper } = require("devtools/shared/l10n");
const { PrefsHelper } = require("devtools/client/shared/prefs");

/**
 * Localization convenience methods.
 */
exports.L10N = new MultiLocalizationHelper(
  "devtools/client/locales/markers.properties",
  "devtools/client/locales/performance.properties"
);

/**
 * A list of preferences for this tool. The values automatically update
 * if somebody edits edits about:config or the prefs change somewhere else.
 *
 * This needs to be registered and unregistered when used for the auto-update
 * functionality to work. The PerformanceController handles this, but if you
 * just use this module in a test independently, ensure you call
 * `registerObserver()` and `unregisterUnobserver()`.
 */
exports.PREFS = new PrefsHelper("devtools.performance", {
  "show-triggers-for-gc-types": ["Char", "ui.show-triggers-for-gc-types"],
  "show-platform-data": ["Bool", "ui.show-platform-data"],
  "hidden-markers": ["Json", "timeline.hidden-markers"],
  "memory-sample-probability": ["Float", "memory.sample-probability"],
  "memory-max-log-length": ["Int", "memory.max-log-length"],
  "profiler-buffer-size": ["Int", "profiler.buffer-size"],
  "profiler-sample-frequency": ["Int", "profiler.sample-frequency-khz"],
  // TODO: re-enable once we flame charts via bug 1148663.
  "enable-memory-flame": ["Bool", "ui.enable-memory-flame"],
});
PK
!<�,O��Achrome/devtools/modules/devtools/client/performance/modules/io.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Cc, Ci } = require("chrome");

const RecordingUtils = require("devtools/shared/performance/recording-utils");
const { FileUtils } = require("resource://gre/modules/FileUtils.jsm");
const { NetUtil } = require("resource://gre/modules/NetUtil.jsm");

// This identifier string is used to tentatively ascertain whether or not
// a JSON loaded from disk is actually something generated by this tool.
// It isn't, of course, a definitive verification, but a Good Enough™
// approximation before continuing the import. Don't localize this.
const PERF_TOOL_SERIALIZER_IDENTIFIER = "Recorded Performance Data";
const PERF_TOOL_SERIALIZER_LEGACY_VERSION = 1;
const PERF_TOOL_SERIALIZER_CURRENT_VERSION = 2;

/**
 * Helpers for importing/exporting JSON.
 */

/**
 * Gets a nsIScriptableUnicodeConverter instance with a default UTF-8 charset.
 * @return object
 */
function getUnicodeConverter() {
  let cname = "@mozilla.org/intl/scriptableunicodeconverter";
  let converter = Cc[cname].createInstance(Ci.nsIScriptableUnicodeConverter);
  converter.charset = "UTF-8";
  return converter;
}

/**
 * Saves a recording as JSON to a file. The provided data is assumed to be
 * acyclical, so that it can be properly serialized.
 *
 * @param object recordingData
 *        The recording data to stream as JSON.
 * @param nsIFile file
 *        The file to stream the data into.
 * @return object
 *         A promise that is resolved once streaming finishes, or rejected
 *         if there was an error.
 */
function saveRecordingToFile(recordingData, file) {
  recordingData.fileType = PERF_TOOL_SERIALIZER_IDENTIFIER;
  recordingData.version = PERF_TOOL_SERIALIZER_CURRENT_VERSION;

  let string = JSON.stringify(recordingData);
  let inputStream = getUnicodeConverter().convertToInputStream(string);
  let outputStream = FileUtils.openSafeFileOutputStream(file);

  return new Promise(resolve => {
    NetUtil.asyncCopy(inputStream, outputStream, resolve);
  });
}

/**
 * Loads a recording stored as JSON from a file.
 *
 * @param nsIFile file
 *        The file to import the data from.
 * @return object
 *         A promise that is resolved once importing finishes, or rejected
 *         if there was an error.
 */
function loadRecordingFromFile(file) {
  let channel = NetUtil.newChannel({
    uri: NetUtil.newURI(file),
    loadUsingSystemPrincipal: true
  });

  channel.contentType = "text/plain";

  return new Promise((resolve, reject) => {
    NetUtil.asyncFetch(channel, (inputStream) => {
      let recordingData;

      try {
        let string = NetUtil.readInputStreamToString(inputStream,
                                                     inputStream.available());
        recordingData = JSON.parse(string);
      } catch (e) {
        reject(new Error("Could not read recording data file."));
        return;
      }

      if (recordingData.fileType != PERF_TOOL_SERIALIZER_IDENTIFIER) {
        reject(new Error("Unrecognized recording data file."));
        return;
      }

      if (!isValidSerializerVersion(recordingData.version)) {
        reject(new Error("Unsupported recording data file version."));
        return;
      }

      if (recordingData.version === PERF_TOOL_SERIALIZER_LEGACY_VERSION) {
        recordingData = convertLegacyData(recordingData);
      }

      if (recordingData.profile.meta.version === 2) {
        RecordingUtils.deflateProfile(recordingData.profile);
      }

      // If the recording has no label, set it to be the
      // filename without its extension.
      if (!recordingData.label) {
        recordingData.label = file.leafName.replace(/\.[^.]+$/, "");
      }

      resolve(recordingData);
    });
  });
}

/**
 * Returns a boolean indicating whether or not the passed in `version`
 * is supported by this serializer.
 *
 * @param number version
 * @return boolean
 */
function isValidSerializerVersion(version) {
  return !!~[
    PERF_TOOL_SERIALIZER_LEGACY_VERSION,
    PERF_TOOL_SERIALIZER_CURRENT_VERSION
  ].indexOf(version);
}

/**
 * Takes recording data (with version `1`, from the original profiler tool),
 * and massages the data to be line with the current performance tool's
 * property names and values.
 *
 * @param object legacyData
 * @return object
 */
function convertLegacyData(legacyData) {
  let { profilerData, ticksData, recordingDuration } = legacyData;

  // The `profilerData` and `ticksData` stay, but the previously unrecorded
  // fields just are empty arrays or objects.
  let data = {
    label: profilerData.profilerLabel,
    duration: recordingDuration,
    markers: [],
    frames: [],
    memory: [],
    ticks: ticksData,
    allocations: { sites: [], timestamps: [], frames: [], sizes: [] },
    profile: profilerData.profile,
    // Fake a configuration object here if there's tick data,
    // so that it can be rendered.
    configuration: {
      withTicks: !!ticksData.length,
      withMarkers: false,
      withMemory: false,
      withAllocations: false
    },
    systemHost: {},
    systemClient: {},
  };

  return data;
}

exports.saveRecordingToFile = saveRecordingToFile;
exports.loadRecordingFromFile = loadRecordingFromFile;
PK
!<^�r�>>Pchrome/devtools/modules/devtools/client/performance/modules/logic/frame-utils.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 global = require("devtools/client/performance/modules/global");
const demangle = require("devtools/client/shared/demangle");
const { assert } = require("devtools/shared/DevToolsUtils");
const { isChromeScheme, isContentScheme, isWASM, parseURL } =
  require("devtools/client/shared/source-utils");

const { CATEGORY_MASK, CATEGORY_MAPPINGS } = require("devtools/client/performance/modules/categories");

// Character codes used in various parsing helper functions.
const CHAR_CODE_R = "r".charCodeAt(0);
const CHAR_CODE_0 = "0".charCodeAt(0);
const CHAR_CODE_9 = "9".charCodeAt(0);
const CHAR_CODE_CAP_Z = "Z".charCodeAt(0);

const CHAR_CODE_LPAREN = "(".charCodeAt(0);
const CHAR_CODE_RPAREN = ")".charCodeAt(0);
const CHAR_CODE_COLON = ":".charCodeAt(0);
const CHAR_CODE_SPACE = " ".charCodeAt(0);
const CHAR_CODE_UNDERSCORE = "_".charCodeAt(0);

const EVAL_TOKEN = "%20%3E%20eval";

// The cache used to store inflated frames.
const gInflatedFrameStore = new WeakMap();

// The cache used to store frame data from `getInfo`.
const gFrameData = new WeakMap();

/**
 * Parses the raw location of this function call to retrieve the actual
 * function name, source url, host name, line and column.
 */
function parseLocation(location, fallbackLine, fallbackColumn) {
  // Parse the `location` for the function name, source url, line, column etc.

  let line, column, url;

  // These two indices are used to extract the resource substring, which is
  // location[parenIndex + 1 .. lineAndColumnIndex].
  //
  // There are 3 variants of location strings in the profiler (with optional
  // column numbers):
  //   1) "name (resource:line)"
  //   2) "resource:line"
  //   3) "resource"
  //
  // For example for (1), take "foo (bar.js:1)".
  //                                ^      ^
  //                                |      |
  //                                |      |
  //                                |      |
  // parenIndex will point to ------+      |
  //                                       |
  // lineAndColumnIndex will point to -----+
  //
  // For an example without parentheses, take "bar.js:2".
  //                                          ^      ^
  //                                          |      |
  // parenIndex will point to ----------------+      |
  //                                                 |
  // lineAndColumIndex will point to ----------------+
  //
  // To parse, we look for the last occurrence of the string ' ('.
  //
  // For 1), all occurrences of space ' ' characters in the resource string
  // are urlencoded, so the last occurrence of ' (' is the separator between
  // the function name and the resource.
  //
  // For 2) and 3), there can be no occurences of ' (' since ' ' characters
  // are urlencoded in the resource string.
  //
  // XXX: Note that 3) is ambiguous with Gecko Profiler marker locations like
  // "EnterJIT". We can't distinguish the two, so we treat 3) like a function
  // name.
  let parenIndex = -1;
  let lineAndColumnIndex = -1;

  let lastCharCode = location.charCodeAt(location.length - 1);
  let i;
  if (lastCharCode === CHAR_CODE_RPAREN) {
    // Case 1)
    i = location.length - 2;
  } else if (isNumeric(lastCharCode)) {
    // Case 2)
    i = location.length - 1;
  } else {
    // Case 3)
    i = 0;
  }

  if (i !== 0) {
    // Look for a :number.
    let end = i;
    while (isNumeric(location.charCodeAt(i))) {
      i--;
    }
    if (location.charCodeAt(i) === CHAR_CODE_COLON) {
      column = location.substr(i + 1, end - i);
      i--;
    }

    // Look for a preceding :number.
    end = i;
    while (isNumeric(location.charCodeAt(i))) {
      i--;
    }

    // If two were found, the first is the line and the second is the
    // column. If only a single :number was found, then it is the line number.
    if (location.charCodeAt(i) === CHAR_CODE_COLON) {
      line = location.substr(i + 1, end - i);
      lineAndColumnIndex = i;
      i--;
    } else {
      lineAndColumnIndex = i + 1;
      line = column;
      column = undefined;
    }
  }

  // Look for the last occurrence of ' (' in case 1).
  if (lastCharCode === CHAR_CODE_RPAREN) {
    for (; i >= 0; i--) {
      if (location.charCodeAt(i) === CHAR_CODE_LPAREN &&
          i > 0 &&
          location.charCodeAt(i - 1) === CHAR_CODE_SPACE) {
        parenIndex = i;
        break;
      }
    }
  }

  let parsedUrl;
  if (lineAndColumnIndex > 0) {
    let resource = location.substring(parenIndex + 1, lineAndColumnIndex);
    url = resource.split(" -> ").pop();
    if (url) {
      parsedUrl = parseURL(url);
    }
  }

  let functionName, fileName, port, host;
  line = line || fallbackLine;
  column = column || fallbackColumn;

  // If the URL digged out from the `location` is valid, this is a JS frame.
  if (parsedUrl) {
    functionName = location.substring(0, parenIndex - 1);
    fileName = parsedUrl.fileName;
    port = parsedUrl.port;
    host = parsedUrl.host;

    // Check for the case of the filename containing eval
    // e.g. "file.js%20line%2065%20%3E%20eval"
    let evalIndex = fileName.indexOf(EVAL_TOKEN);
    if (evalIndex !== -1 && evalIndex === (fileName.length - EVAL_TOKEN.length)) {
      // Match the filename
      let evalLine = line;
      let [, _fileName, , _line] = fileName.match(/(.+)(%20line%20(\d+)%20%3E%20eval)/)
                                   || [];
      fileName = `${_fileName} (eval:${evalLine})`;
      line = _line;
      assert(_fileName !== undefined,
             "Filename could not be found from an eval location site");
      assert(_line !== undefined,
             "Line could not be found from an eval location site");

      // Match the url as well
      [, url] = url.match(/(.+)( line (\d+) > eval)/) || [];
      assert(url !== undefined,
             "The URL could not be parsed correctly from an eval location site");
    }
  } else {
    functionName = location;
    url = null;
  }

  return { functionName, fileName, host, port, url, line, column };
}

/**
 * Sets the properties of `isContent` and `category` on a frame.
 *
 * @param {InflatedFrame} frame
 */
function computeIsContentAndCategory(frame) {
  // Only C++ stack frames have associated category information.
  if (frame.category) {
    return;
  }

  let location = frame.location;

  // There are 3 variants of location strings in the profiler (with optional
  // column numbers):
  //   1) "name (resource:line)"
  //   2) "resource:line"
  //   3) "resource"
  let lastCharCode = location.charCodeAt(location.length - 1);
  let schemeStartIndex = -1;
  if (lastCharCode === CHAR_CODE_RPAREN) {
    // Case 1)
    //
    // Need to search for the last occurrence of ' (' to find the start of the
    // resource string.
    for (let i = location.length - 2; i >= 0; i--) {
      if (location.charCodeAt(i) === CHAR_CODE_LPAREN &&
          i > 0 &&
          location.charCodeAt(i - 1) === CHAR_CODE_SPACE) {
        schemeStartIndex = i + 1;
        break;
      }
    }
  } else {
    // Cases 2) and 3)
    schemeStartIndex = 0;
  }

  // We can't know if WASM frames are content or not at the time of this writing, so label
  // them all as content.
  if (isContentScheme(location, schemeStartIndex) || isWASM(location)) {
    frame.isContent = true;
    return;
  }

  if (schemeStartIndex !== 0) {
    for (let j = schemeStartIndex; j < location.length; j++) {
      if (location.charCodeAt(j) === CHAR_CODE_R &&
          isChromeScheme(location, j) &&
          (location.includes("resource://devtools") ||
           location.includes("resource://devtools"))) {
        frame.category = CATEGORY_MASK("tools");
        return;
      }
    }
  }

  if (location === "EnterJIT") {
    frame.category = CATEGORY_MASK("js");
    return;
  }

  frame.category = CATEGORY_MASK("other");
}

/**
 * Get caches to cache inflated frames and computed frame keys of a frame
 * table.
 *
 * @param object framesTable
 * @return object
 */
function getInflatedFrameCache(frameTable) {
  let inflatedCache = gInflatedFrameStore.get(frameTable);
  if (inflatedCache !== undefined) {
    return inflatedCache;
  }

  // Fill with nulls to ensure no holes.
  inflatedCache = Array.from({ length: frameTable.data.length }, () => null);
  gInflatedFrameStore.set(frameTable, inflatedCache);
  return inflatedCache;
}

/**
 * Get or add an inflated frame to a cache.
 *
 * @param object cache
 * @param number index
 * @param object frameTable
 * @param object stringTable
 */
function getOrAddInflatedFrame(cache, index, frameTable, stringTable) {
  let inflatedFrame = cache[index];
  if (inflatedFrame === null) {
    inflatedFrame = cache[index] = new InflatedFrame(index, frameTable, stringTable);
  }
  return inflatedFrame;
}

/**
 * An intermediate data structured used to hold inflated frames.
 *
 * @param number index
 * @param object frameTable
 * @param object stringTable
 */
function InflatedFrame(index, frameTable, stringTable) {
  const LOCATION_SLOT = frameTable.schema.location;
  const IMPLEMENTATION_SLOT = frameTable.schema.implementation;
  const OPTIMIZATIONS_SLOT = frameTable.schema.optimizations;
  const LINE_SLOT = frameTable.schema.line;
  const CATEGORY_SLOT = frameTable.schema.category;

  let frame = frameTable.data[index];
  let category = frame[CATEGORY_SLOT];
  this.location = stringTable[frame[LOCATION_SLOT]];
  this.implementation = frame[IMPLEMENTATION_SLOT];
  this.optimizations = frame[OPTIMIZATIONS_SLOT];
  this.line = frame[LINE_SLOT];
  this.column = undefined;
  this.category = category;
  this.isContent = false;

  // Attempt to compute if this frame is a content frame, and if not,
  // its category.
  //
  // Since only C++ stack frames have associated category information,
  // attempt to generate a useful category, fallback to the one provided
  // by the profiling data, or fallback to an unknown category.
  computeIsContentAndCategory(this);
}

/**
 * Gets the frame key (i.e., equivalence group) according to options. Content
 * frames are always identified by location. Chrome frames are identified by
 * location if content-only filtering is off. If content-filtering is on, they
 * are identified by their category.
 *
 * @param object options
 * @return string
 */
InflatedFrame.prototype.getFrameKey = function getFrameKey(options) {
  if (this.isContent || !options.contentOnly || options.isRoot) {
    options.isMetaCategoryOut = false;
    return this.location;
  }

  if (options.isLeaf) {
    // We only care about leaf platform frames if we are displaying content
    // only. If no category is present, give the default category of "other".
    //
    // 1. The leaf is where time is _actually_ being spent, so we _need_ to
    // show it to developers in some way to give them accurate profiling
    // data. We decide to split the platform into various category buckets
    // and just show time spent in each bucket.
    //
    // 2. The calls leading to the leaf _aren't_ where we are spending time,
    // but _do_ give the developer context for how they got to the leaf
    // where they _are_ spending time. For non-platform hackers, the
    // non-leaf platform frames don't give any meaningful context, and so we
    // can safely filter them out.
    options.isMetaCategoryOut = true;
    return this.category;
  }

  // Return an empty string denoting that this frame should be skipped.
  return "";
};

function isNumeric(c) {
  return c >= CHAR_CODE_0 && c <= CHAR_CODE_9;
}

function shouldDemangle(name) {
  return name && name.charCodeAt &&
         name.charCodeAt(0) === CHAR_CODE_UNDERSCORE &&
         name.charCodeAt(1) === CHAR_CODE_UNDERSCORE &&
         name.charCodeAt(2) === CHAR_CODE_CAP_Z;
}

/**
 * Calculates the relative costs of this frame compared to a root,
 * and generates allocations information if specified. Uses caching
 * if possible.
 *
 * @param {ThreadNode|FrameNode} node
 *                               The node we are calculating.
 * @param {ThreadNode} options.root
 *                     The root thread node to calculate relative costs.
 *                     Generates [self|total] [duration|percentage] values.
 * @param {boolean} options.allocations
 *                  Generates `totalAllocations` and `selfAllocations`.
 *
 * @return {object}
 */
function getFrameInfo(node, options) {
  let data = gFrameData.get(node);

  if (!data) {
    if (node.nodeType === "Thread") {
      data = Object.create(null);
      data.functionName = global.L10N.getStr("table.root");
    } else {
      data = parseLocation(node.location, node.line, node.column);
      data.hasOptimizations = node.hasOptimizations();
      data.isContent = node.isContent;
      data.isMetaCategory = node.isMetaCategory;
    }
    data.samples = node.youngestFrameSamples;
    data.categoryData = CATEGORY_MAPPINGS[node.category] || {};
    data.nodeType = node.nodeType;

    // Frame name (function location or some meta information)
    if (data.isMetaCategory) {
      data.name = data.categoryData.label;
    } else if (shouldDemangle(data.functionName)) {
      data.name = demangle(data.functionName);
    } else {
      data.name = data.functionName;
    }

    data.tooltiptext = data.isMetaCategory ?
      data.categoryData.label :
      node.location || "";

    gFrameData.set(node, data);
  }

  // If no options specified, we can't calculate relative values, abort here
  if (!options) {
    return data;
  }

  // If a root specified, calculate the relative costs in the context of
  // this call tree. The cached store may already have this, but generate
  // if it does not.
  let totalSamples = options.root.samples;
  let totalDuration = options.root.duration;
  if (options && options.root && !data.COSTS_CALCULATED) {
    data.selfDuration = node.youngestFrameSamples / totalSamples * totalDuration;
    data.selfPercentage = node.youngestFrameSamples / totalSamples * 100;
    data.totalDuration = node.samples / totalSamples * totalDuration;
    data.totalPercentage = node.samples / totalSamples * 100;
    data.COSTS_CALCULATED = true;
  }

  if (options && options.allocations && !data.ALLOCATION_DATA_CALCULATED) {
    let totalBytes = options.root.byteSize;
    data.selfCount = node.youngestFrameSamples;
    data.totalCount = node.samples;
    data.selfCountPercentage = node.youngestFrameSamples / totalSamples * 100;
    data.totalCountPercentage = node.samples / totalSamples * 100;
    data.selfSize = node.youngestFrameByteSize;
    data.totalSize = node.byteSize;
    data.selfSizePercentage = node.youngestFrameByteSize / totalBytes * 100;
    data.totalSizePercentage = node.byteSize / totalBytes * 100;
    data.ALLOCATION_DATA_CALCULATED = true;
  }

  return data;
}

exports.getFrameInfo = getFrameInfo;

/**
 * Takes an inverted ThreadNode and searches its youngest frames for
 * a FrameNode with matching location.
 *
 * @param {ThreadNode} threadNode
 * @param {string} location
 * @return {?FrameNode}
 */
function findFrameByLocation(threadNode, location) {
  if (!threadNode.inverted) {
    throw new Error(
      "FrameUtils.findFrameByLocation only supports leaf nodes in an inverted tree.");
  }

  let calls = threadNode.calls;
  for (let i = 0; i < calls.length; i++) {
    if (calls[i].location === location) {
      return calls[i];
    }
  }
  return null;
}

exports.findFrameByLocation = findFrameByLocation;
exports.computeIsContentAndCategory = computeIsContentAndCategory;
exports.parseLocation = parseLocation;
exports.getInflatedFrameCache = getInflatedFrameCache;
exports.getOrAddInflatedFrame = getOrAddInflatedFrame;
exports.InflatedFrame = InflatedFrame;
exports.shouldDemangle = shouldDemangle;
PK
!<�т.�0�0Hchrome/devtools/modules/devtools/client/performance/modules/logic/jit.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";

// An outcome of an OptimizationAttempt that is considered successful.
const SUCCESSFUL_OUTCOMES = [
  "GenericSuccess", "Inlined", "DOM", "Monomorphic", "Polymorphic"
];

/**
 * Model representing JIT optimization sites from the profiler
 * for a frame (represented by a FrameNode). Requires optimization data from
 * a profile, which is an array of RawOptimizationSites.
 *
 * When the ThreadNode for the profile iterates over the samples' frames, each
 * frame's optimizations are accumulated in their respective FrameNodes. Each
 * FrameNode may contain many different optimization sites. One sample may
 * pick up optimization X on line Y in the frame, with the next sample
 * containing optimization Z on line W in the same frame, as each frame is
 * only function.
 *
 * An OptimizationSite contains a record of how many times the
 * RawOptimizationSite was sampled, as well as the unique id based off of the
 * original profiler array, and the RawOptimizationSite itself as a reference.
 * @see devtools/client/performance/modules/logic/tree-model.js
 *
 * @struct RawOptimizationSite
 * A structure describing a location in a script that was attempted to be optimized.
 * Contains all the IonTypes observed, and the sequence of OptimizationAttempts that
 * were attempted, and the line and column in the script. This is retrieved from the
 * profiler after a recording, and our base data structure. Should always be referenced,
 * and unmodified.
 *
 * Note that propertyName is an index into a string table, which needs to be
 * provided in order for the raw optimization site to be inflated.
 *
 * @type {Array<IonType>} types
 * @type {Array<OptimizationAttempt>} attempts
 * @type {?number} propertyName
 * @type {number} line
 * @type {number} column
 *
 *
 * @struct IonType
 * IonMonkey attempts to classify each value in an optimization site by some type.
 * Based off of the observed types for a value (like a variable that could be a
 * string or an instance of an object), it determines what kind of type it should be
 * classified as. Each IonType here contains an array of all ObservedTypes under `types`,
 * the Ion type that IonMonkey decided this value should be (Int32, Object, etc.) as
 * `mirType`, and the component of this optimization type that this value refers to --
 * like a "getter" optimization, `a[b]`, has site `a` (the "Receiver") and `b`
 * (the "Index").
 *
 * Generally the more ObservedTypes, the more deoptimized this OptimizationSite is.
 * There could be no ObservedTypes, in which case `typeset` is undefined.
 *
 * @type {?Array<ObservedType>} typeset
 * @type {string} site
 * @type {string} mirType
 *
 *
 * @struct ObservedType
 * When IonMonkey attempts to determine what type a value is, it checks on each sample.
 * The ObservedType can be thought of in more of JavaScripty-terms, rather than C++.
 * The `keyedBy` property is a high level description of the type, like "primitive",
 * "constructor", "function", "singleton", "alloc-site" (that one is a bit more weird).
 * If the `keyedBy` type is a function or constructor, the ObservedType should have a
 * `name` property, referring to the function or constructor name from the JS source.
 * If IonMonkey can determine the origin of this type (like where the constructor is
 * defined), the ObservedType will also have `location` and `line` properties, but
 * `location` can sometimes be non-URL strings like "self-hosted" or a memory location
 * like "102ca7880", or no location at all, and maybe `line` is 0 or undefined.
 *
 * @type {string} keyedBy
 * @type {?string} name
 * @type {?string} location
 * @type {?string} line
 *
 *
 * @struct OptimizationAttempt
 * Each RawOptimizationSite contains an array of OptimizationAttempts. Generally,
 * IonMonkey goes through a series of strategies for each kind of optimization, starting
 * from most-niche and optimized, to the less-optimized, but more general strategies --
 * for example, a getter opt may first try to optimize for the scenario of a getter on an
 * `arguments` object -- that will fail most of the time, as most objects are not
 * arguments objects, but it will attempt several strategies in order until it finds a
 * strategy that works, or fails. Even in the best scenarios, some attempts will fail
 * (like the arguments getter example), which is OK, as long as some attempt succeeds
 * (with the earlier attempts preferred, as those are more optimized). In an
 * OptimizationAttempt structure, we store just the `strategy` name and `outcome` name,
 * both from enums in js/public/TrackedOptimizationInfo.h as TRACKED_STRATEGY_LIST and
 * TRACKED_OUTCOME_LIST, respectively. An array of successful outcome strings are above
 * in SUCCESSFUL_OUTCOMES.
 *
 * @see js/public/TrackedOptimizationInfo.h
 *
 * @type {string} strategy
 * @type {string} outcome
 */

/*
 * A wrapper around RawOptimizationSite to record sample count and ID (referring to the
 * index of where this is in the initially seeded optimizations data), so we don't mutate
 * the original data from the profiler. Provides methods to access the underlying
 * optimization data easily, so understanding the semantics of JIT data isn't necessary.
 *
 * @constructor
 *
 * @param {Array<RawOptimizationSite>} optimizations
 * @param {number} optsIndex
 *
 * @type {RawOptimizationSite} data
 * @type {number} samples
 * @type {number} id
 */

const OptimizationSite = function (id, opts) {
  this.id = id;
  this.data = opts;
  this.samples = 1;
};

/**
 * Constructor for JITOptimizations. A collection of OptimizationSites for a frame.
 *
 * @constructor
 * @param {Array<RawOptimizationSite>} rawSites
 *                                     Array of raw optimization sites.
 * @param {Array<string>} stringTable
 *                        Array of strings from the profiler used to inflate
 *                        JIT optimizations. Do not modify this!
 */

const JITOptimizations = function (rawSites, stringTable) {
  // Build a histogram of optimization sites.
  let sites = [];

  for (let rawSite of rawSites) {
    let existingSite = sites.find((site) => site.data === rawSite);
    if (existingSite) {
      existingSite.samples++;
    } else {
      sites.push(new OptimizationSite(sites.length, rawSite));
    }
  }

  // Inflate the optimization information.
  for (let site of sites) {
    let data = site.data;
    let STRATEGY_SLOT = data.attempts.schema.strategy;
    let OUTCOME_SLOT = data.attempts.schema.outcome;
    let attempts = data.attempts.data.map((a) => {
      return {
        id: site.id,
        strategy: stringTable[a[STRATEGY_SLOT]],
        outcome: stringTable[a[OUTCOME_SLOT]]
      };
    });
    let types = data.types.map((t) => {
      let typeset = maybeTypeset(t.typeset, stringTable);
      if (typeset) {
        typeset.forEach(ts => {
          ts.id = site.id;
        });
      }

      return {
        id: site.id,
        typeset,
        site: stringTable[t.site],
        mirType: stringTable[t.mirType]
      };
    });
    // Add IDs to to all children objects, so we can correllate sites when
    // just looking at a specific type, attempt, etc..
    attempts.id = types.id = site.id;

    site.data = {
      attempts,
      types,
      propertyName: maybeString(stringTable, data.propertyName),
      line: data.line,
      column: data.column
    };
  }

  this.optimizationSites = sites.sort((a, b) => b.samples - a.samples);
};

/**
 * Make JITOptimizations iterable.
 */
JITOptimizations.prototype = {
  [Symbol.iterator]: function* () {
    yield* this.optimizationSites;
  },

  get length() {
    return this.optimizationSites.length;
  }
};

/**
 * Takes an "outcome" string from an OptimizationAttempt and returns
 * a boolean indicating whether or not its a successful outcome.
 *
 * @return {boolean}
 */

function isSuccessfulOutcome(outcome) {
  return !!~SUCCESSFUL_OUTCOMES.indexOf(outcome);
}

/**
 * Takes an OptimizationSite. Returns a boolean indicating if the passed
 * in OptimizationSite has a "good" outcome at the end of its attempted strategies.
 *
 * @param {OptimizationSite} optimizationSite
 * @return {boolean}
 */

function hasSuccessfulOutcome(optimizationSite) {
  let attempts = optimizationSite.data.attempts;
  let lastOutcome = attempts[attempts.length - 1].outcome;
  return isSuccessfulOutcome(lastOutcome);
}

function maybeString(stringTable, index) {
  return index ? stringTable[index] : undefined;
}

function maybeTypeset(typeset, stringTable) {
  if (!typeset) {
    return undefined;
  }
  return typeset.map((ty) => {
    return {
      keyedBy: maybeString(stringTable, ty.keyedBy),
      name: maybeString(stringTable, ty.name),
      location: maybeString(stringTable, ty.location),
      line: ty.line
    };
  });
}

// Map of optimization implementation names to an enum.
const IMPLEMENTATION_MAP = {
  "interpreter": 0,
  "baseline": 1,
  "ion": 2
};
const IMPLEMENTATION_NAMES = Object.keys(IMPLEMENTATION_MAP);

/**
 * Takes data from a FrameNode and computes rendering positions for
 * a stacked mountain graph, to visualize JIT optimization tiers over time.
 *
 * @param {FrameNode} frameNode
 *                    The FrameNode who's optimizations we're iterating.
 * @param {Array<number>} sampleTimes
 *                        An array of every sample time within the range we're counting.
 *                        From a ThreadNode's `sampleTimes` property.
 * @param {number} bucketSize
 *                 Size of each bucket in milliseconds.
 *                 `duration / resolution = bucketSize` in OptimizationsGraph.
 * @return {?Array<object>}
 */
function createTierGraphDataFromFrameNode(frameNode, sampleTimes, bucketSize) {
  let tierData = frameNode.getTierData();
  let stringTable = frameNode._stringTable;
  let output = [];
  let implEnum;

  let tierDataIndex = 0;
  let nextOptSample = tierData[tierDataIndex];

  // Bucket data
  let samplesInCurrentBucket = 0;
  let currentBucketStartTime = sampleTimes[0];
  let bucket = [];

  // Store previous data point so we can have straight vertical lines
  let previousValues;

  // Iterate one after the samples, so we can finalize the last bucket
  for (let i = 0; i <= sampleTimes.length; i++) {
    let sampleTime = sampleTimes[i];

    // If this sample is in the next bucket, or we're done
    // checking sampleTimes and on the last iteration, finalize previous bucket
    if (sampleTime >= (currentBucketStartTime + bucketSize) ||
        i >= sampleTimes.length) {
      let dataPoint = {};
      dataPoint.values = [];
      dataPoint.delta = currentBucketStartTime;

      // Map the opt site counts as a normalized percentage (0-1)
      // of its count in context of total samples this bucket
      for (let j = 0; j < IMPLEMENTATION_NAMES.length; j++) {
        dataPoint.values[j] = (bucket[j] || 0) / (samplesInCurrentBucket || 1);
      }

      // Push the values from the previous bucket to the same time
      // as the current bucket so we get a straight vertical line.
      if (previousValues) {
        let data = Object.create(null);
        data.values = previousValues;
        data.delta = currentBucketStartTime;
        output.push(data);
      }

      output.push(dataPoint);

      // Set the new start time of this bucket and reset its count
      currentBucketStartTime += bucketSize;
      samplesInCurrentBucket = 0;
      previousValues = dataPoint.values;
      bucket = [];
    }

    // If this sample observed an optimization in this frame, record it
    if (nextOptSample && nextOptSample.time === sampleTime) {
      // If no implementation defined, it was the "interpreter".
      implEnum = IMPLEMENTATION_MAP[stringTable[nextOptSample.implementation] ||
                                    "interpreter"];
      bucket[implEnum] = (bucket[implEnum] || 0) + 1;
      nextOptSample = tierData[++tierDataIndex];
    }

    samplesInCurrentBucket++;
  }

  return output;
}

exports.createTierGraphDataFromFrameNode = createTierGraphDataFromFrameNode;
exports.OptimizationSite = OptimizationSite;
exports.JITOptimizations = JITOptimizations;
exports.hasSuccessfulOutcome = hasSuccessfulOutcome;
exports.isSuccessfulOutcome = isSuccessfulOutcome;
exports.SUCCESSFUL_OUTCOMES = SUCCESSFUL_OUTCOMES;
PK
!<��sNchrome/devtools/modules/devtools/client/performance/modules/logic/telemetry.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Telemetry = require("devtools/client/shared/telemetry");
const flags = require("devtools/shared/flags");
const EVENTS = require("devtools/client/performance/events");

const EVENT_MAP_FLAGS = new Map([
  [EVENTS.RECORDING_IMPORTED, "DEVTOOLS_PERFTOOLS_RECORDING_IMPORT_FLAG"],
  [EVENTS.RECORDING_EXPORTED, "DEVTOOLS_PERFTOOLS_RECORDING_EXPORT_FLAG"],
]);

const RECORDING_FEATURES = [
  "withMarkers", "withTicks", "withMemory", "withAllocations"
];

const SELECTED_VIEW_HISTOGRAM_NAME = "DEVTOOLS_PERFTOOLS_SELECTED_VIEW_MS";

function PerformanceTelemetry(emitter) {
  this._emitter = emitter;
  this._telemetry = new Telemetry();
  this.onFlagEvent = this.onFlagEvent.bind(this);
  this.onRecordingStateChange = this.onRecordingStateChange.bind(this);
  this.onViewSelected = this.onViewSelected.bind(this);

  for (let [event] of EVENT_MAP_FLAGS) {
    this._emitter.on(event, this.onFlagEvent);
  }

  this._emitter.on(EVENTS.RECORDING_STATE_CHANGE, this.onRecordingStateChange);
  this._emitter.on(EVENTS.UI_DETAILS_VIEW_SELECTED, this.onViewSelected);

  if (flags.testing) {
    this.recordLogs();
  }
}

PerformanceTelemetry.prototype.destroy = function () {
  if (this._previousView) {
    this._telemetry.stopTimer(SELECTED_VIEW_HISTOGRAM_NAME, this._previousView);
  }

  this._telemetry.destroy();
  for (let [event] of EVENT_MAP_FLAGS) {
    this._emitter.off(event, this.onFlagEvent);
  }
  this._emitter.off(EVENTS.RECORDING_STATE_CHANGE, this.onRecordingStateChange);
  this._emitter.off(EVENTS.UI_DETAILS_VIEW_SELECTED, this.onViewSelected);
  this._emitter = null;
};

PerformanceTelemetry.prototype.onFlagEvent = function (eventName, ...data) {
  this._telemetry.log(EVENT_MAP_FLAGS.get(eventName), true);
};

PerformanceTelemetry.prototype.onRecordingStateChange = function (_, status, model) {
  if (status != "recording-stopped") {
    return;
  }

  if (model.isConsole()) {
    this._telemetry.log("DEVTOOLS_PERFTOOLS_CONSOLE_RECORDING_COUNT", true);
  } else {
    this._telemetry.log("DEVTOOLS_PERFTOOLS_RECORDING_COUNT", true);
  }

  this._telemetry.log("DEVTOOLS_PERFTOOLS_RECORDING_DURATION_MS", model.getDuration());

  let config = model.getConfiguration();
  for (let k in config) {
    if (RECORDING_FEATURES.includes(k)) {
      this._telemetry.logKeyed("DEVTOOLS_PERFTOOLS_RECORDING_FEATURES_USED", k,
                               config[k]);
    }
  }
};

PerformanceTelemetry.prototype.onViewSelected = function (_, viewName) {
  if (this._previousView) {
    this._telemetry.stopTimer(SELECTED_VIEW_HISTOGRAM_NAME, this._previousView);
  }
  this._previousView = viewName;
  this._telemetry.startTimer(SELECTED_VIEW_HISTOGRAM_NAME);
};

/**
 * Utility to record histogram calls to this instance.
 * Should only be used in testing mode; throws otherwise.
 */
PerformanceTelemetry.prototype.recordLogs = function () {
  if (!flags.testing) {
    throw new Error("Can only record telemetry logs in tests.");
  }

  let originalLog = this._telemetry.log;
  let originalLogKeyed = this._telemetry.logKeyed;
  this._log = {};

  this._telemetry.log = (histo, data) => {
    let results = this._log[histo] = this._log[histo] || [];
    results.push(data);
    originalLog(histo, data);
  };

  this._telemetry.logKeyed = (histo, key, data) => {
    let results = this._log[histo] = this._log[histo] || [];
    results.push([key, data]);
    originalLogKeyed(histo, key, data);
  };
};

PerformanceTelemetry.prototype.getLogs = function () {
  if (!flags.testing) {
    throw new Error("Can only get telemetry logs in tests.");
  }

  return this._log;
};

exports.PerformanceTelemetry = PerformanceTelemetry;
PK
!<g�.1I1IOchrome/devtools/modules/devtools/client/performance/modules/logic/tree-model.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { JITOptimizations } = require("devtools/client/performance/modules/logic/jit");
const FrameUtils = require("devtools/client/performance/modules/logic/frame-utils");

/**
 * A call tree for a thread. This is essentially a linkage between all frames
 * of all samples into a single tree structure, with additional information
 * on each node, like the time spent (in milliseconds) and samples count.
 *
 * @param object thread
 *        The raw thread object received from the backend. Contains samples,
 *        stackTable, frameTable, and stringTable.
 * @param object options
 *        Additional supported options
 *          - number startTime
 *          - number endTime
 *          - boolean contentOnly [optional]
 *          - boolean invertTree [optional]
 *          - boolean flattenRecursion [optional]
 */
function ThreadNode(thread, options = {}) {
  if (options.endTime == void 0 || options.startTime == void 0) {
    throw new Error("ThreadNode requires both `startTime` and `endTime`.");
  }
  this.samples = 0;
  this.sampleTimes = [];
  this.youngestFrameSamples = 0;
  this.calls = [];
  this.duration = options.endTime - options.startTime;
  this.nodeType = "Thread";
  this.inverted = options.invertTree;

  // Total bytesize of all allocations if enabled
  this.byteSize = 0;
  this.youngestFrameByteSize = 0;

  let { samples, stackTable, frameTable, stringTable } = thread;

  // Nothing to do if there are no samples.
  if (samples.data.length === 0) {
    return;
  }

  this._buildInverted(samples, stackTable, frameTable, stringTable, options);
  if (!options.invertTree) {
    this._uninvert();
  }
}

ThreadNode.prototype = {
  /**
   * Build an inverted call tree from profile samples. The format of the
   * samples is described in tools/profiler/ProfileEntry.h, under the heading
   * "Thread profile JSON Format".
   *
   * The profile data is naturally presented inverted. Inverting the call tree
   * is also the default in the Performance tool.
   *
   * @param object samples
   *        The raw samples array received from the backend.
   * @param object stackTable
   *        The table of deduplicated stacks from the backend.
   * @param object frameTable
   *        The table of deduplicated frames from the backend.
   * @param object stringTable
   *        The table of deduplicated strings from the backend.
   * @param object options
   *        Additional supported options
   *          - number startTime
   *          - number endTime
   *          - boolean contentOnly [optional]
   *          - boolean invertTree [optional]
   */
  _buildInverted: function buildInverted(samples, stackTable, frameTable, stringTable,
                                         options) {
    function getOrAddFrameNode(calls, isLeaf, frameKey, inflatedFrame, isMetaCategory,
                               leafTable) {
      // Insert the inflated frame into the call tree at the current level.
      let frameNode;

      // Leaf nodes have fan out much greater than non-leaf nodes, thus the
      // use of a hash table. Otherwise, do linear search.
      //
      // Note that this method is very hot, thus the manual looping over
      // Array.prototype.find.
      if (isLeaf) {
        frameNode = leafTable[frameKey];
      } else {
        for (let i = 0; i < calls.length; i++) {
          if (calls[i].key === frameKey) {
            frameNode = calls[i];
            break;
          }
        }
      }

      if (!frameNode) {
        frameNode = new FrameNode(frameKey, inflatedFrame, isMetaCategory);
        if (isLeaf) {
          leafTable[frameKey] = frameNode;
        }
        calls.push(frameNode);
      }

      return frameNode;
    }

    const SAMPLE_STACK_SLOT = samples.schema.stack;
    const SAMPLE_TIME_SLOT = samples.schema.time;
    const SAMPLE_BYTESIZE_SLOT = samples.schema.size;

    const STACK_PREFIX_SLOT = stackTable.schema.prefix;
    const STACK_FRAME_SLOT = stackTable.schema.frame;

    const getOrAddInflatedFrame = FrameUtils.getOrAddInflatedFrame;

    let samplesData = samples.data;
    let stacksData = stackTable.data;

    // Caches.
    let inflatedFrameCache = FrameUtils.getInflatedFrameCache(frameTable);
    let leafTable = Object.create(null);

    let startTime = options.startTime;
    let endTime = options.endTime;
    let flattenRecursion = options.flattenRecursion;

    // Reused options object passed to InflatedFrame.prototype.getFrameKey.
    let mutableFrameKeyOptions = {
      contentOnly: options.contentOnly,
      isRoot: false,
      isLeaf: false,
      isMetaCategoryOut: false
    };

    let byteSize = 0;
    for (let i = 0; i < samplesData.length; i++) {
      let sample = samplesData[i];
      let sampleTime = sample[SAMPLE_TIME_SLOT];

      if (SAMPLE_BYTESIZE_SLOT !== void 0) {
        byteSize = sample[SAMPLE_BYTESIZE_SLOT];
      }

      // A sample's end time is considered to be its time of sampling. Its
      // start time is the sampling time of the previous sample.
      //
      // Thus, we compare sampleTime <= start instead of < to filter out
      // samples that end exactly at the start time.
      if (!sampleTime || sampleTime <= startTime || sampleTime > endTime) {
        continue;
      }

      let stackIndex = sample[SAMPLE_STACK_SLOT];
      let calls = this.calls;
      let prevCalls = this.calls;
      let prevFrameKey;
      let isLeaf = mutableFrameKeyOptions.isLeaf = true;
      let skipRoot = options.invertTree;

      // Inflate the stack and build the FrameNode call tree directly.
      //
      // In the profiler data, each frame's stack is referenced by an index
      // into stackTable.
      //
      // Each entry in stackTable is a pair [ prefixIndex, frameIndex ]. The
      // prefixIndex is itself an index into stackTable, referencing the
      // prefix of the current stack (that is, the younger frames). In other
      // words, the stackTable is encoded as a trie of the inverted
      // callstack. The frameIndex is an index into frameTable, describing the
      // frame at the current depth.
      //
      // This algorithm inflates each frame in the frame table while walking
      // the stack trie as described above.
      //
      // The frame key is then computed from the inflated frame /and/ the
      // current depth in the FrameNode call tree.  That is, the frame key is
      // not wholly determinable from just the inflated frame.
      //
      // For content frames, the frame key is just its location. For chrome
      // frames, the key may be a metacategory or its location, depending on
      // rendering options and its position in the FrameNode call tree.
      //
      // The frame key is then used to build up the inverted FrameNode call
      // tree.
      //
      // Note that various filtering functions, such as filtering for content
      // frames or flattening recursion, are inlined into the stack inflation
      // loop. This is important for performance as it avoids intermediate
      // structures and multiple passes.
      while (stackIndex !== null) {
        let stackEntry = stacksData[stackIndex];
        let frameIndex = stackEntry[STACK_FRAME_SLOT];

        // Fetch the stack prefix (i.e. older frames) index.
        stackIndex = stackEntry[STACK_PREFIX_SLOT];

        // Do not include the (root) node in this sample, as the costs of each frame
        // will make it clear to differentiate (root)->B vs (root)->A->B
        // when a tree is inverted, a revert of bug 1147604
        if (stackIndex === null && skipRoot) {
          break;
        }

        // Inflate the frame.
        let inflatedFrame = getOrAddInflatedFrame(inflatedFrameCache, frameIndex,
                                                  frameTable, stringTable);

        // Compute the frame key.
        mutableFrameKeyOptions.isRoot = stackIndex === null;
        let frameKey = inflatedFrame.getFrameKey(mutableFrameKeyOptions);

        // An empty frame key means this frame should be skipped.
        if (frameKey === "") {
          continue;
        }

        // If we shouldn't flatten the current frame into the previous one, advance a
        // level in the call tree.
        let shouldFlatten = flattenRecursion && frameKey === prevFrameKey;
        if (!shouldFlatten) {
          calls = prevCalls;
        }

        let frameNode = getOrAddFrameNode(calls, isLeaf, frameKey, inflatedFrame,
                                          mutableFrameKeyOptions.isMetaCategoryOut,
                                          leafTable);
        if (isLeaf) {
          frameNode.youngestFrameSamples++;
          frameNode._addOptimizations(inflatedFrame.optimizations,
                                      inflatedFrame.implementation, sampleTime,
                                      stringTable);

          if (byteSize) {
            frameNode.youngestFrameByteSize += byteSize;
          }
        }

        // Don't overcount flattened recursive frames.
        if (!shouldFlatten) {
          frameNode.samples++;
          if (byteSize) {
            frameNode.byteSize += byteSize;
          }
        }

        prevFrameKey = frameKey;
        prevCalls = frameNode.calls;
        isLeaf = mutableFrameKeyOptions.isLeaf = false;
      }

      this.samples++;
      this.sampleTimes.push(sampleTime);
      if (byteSize) {
        this.byteSize += byteSize;
      }
    }
  },

  /**
   * Uninverts the call tree after its having been built.
   */
  _uninvert: function uninvert() {
    function mergeOrAddFrameNode(calls, node, samples, size) {
      // Unlike the inverted call tree, we don't use a root table for the top
      // level, as in general, there are many fewer entry points than
      // leaves. Instead, linear search is used regardless of level.
      for (let i = 0; i < calls.length; i++) {
        if (calls[i].key === node.key) {
          let foundNode = calls[i];
          foundNode._merge(node, samples, size);
          return foundNode.calls;
        }
      }
      let copy = node._clone(samples, size);
      calls.push(copy);
      return copy.calls;
    }

    let workstack = [{ node: this, level: 0 }];
    let spine = [];
    let entry;

    // The new root.
    let rootCalls = [];

    // Walk depth-first and keep the current spine (e.g., callstack).
    do {
      entry = workstack.pop();
      if (entry) {
        spine[entry.level] = entry;

        let node = entry.node;
        let calls = node.calls;
        let callSamples = 0;
        let callByteSize = 0;

        // Continue the depth-first walk.
        for (let i = 0; i < calls.length; i++) {
          workstack.push({ node: calls[i], level: entry.level + 1 });
          callSamples += calls[i].samples;
          callByteSize += calls[i].byteSize;
        }

        // The sample delta is used to distinguish stacks.
        //
        // Suppose we have the following stack samples:
        //
        //   A -> B
        //   A -> C
        //   A
        //
        // The inverted tree is:
        //
        //     A
        //    / \
        //   B   C
        //
        // with A.samples = 3, B.samples = 1, C.samples = 1.
        //
        // A is distinguished as being its own stack because
        // A.samples - (B.samples + C.samples) > 0.
        //
        // Note that bottoming out is a degenerate where callSamples = 0.

        let samplesDelta = node.samples - callSamples;
        let byteSizeDelta = node.byteSize - callByteSize;
        if (samplesDelta > 0) {
          // Reverse the spine and add them to the uninverted call tree.
          let uninvertedCalls = rootCalls;
          for (let level = entry.level; level > 0; level--) {
            let callee = spine[level];
            uninvertedCalls = mergeOrAddFrameNode(uninvertedCalls, callee.node,
                                                  samplesDelta, byteSizeDelta);
          }
        }
      }
    } while (entry);

    // Replace the toplevel calls with rootCalls, which now contains the
    // uninverted roots.
    this.calls = rootCalls;
  },

  /**
   * Gets additional details about this node.
   * @see FrameNode.prototype.getInfo for more information.
   *
   * @return object
   */
  getInfo: function (options) {
    return FrameUtils.getFrameInfo(this, options);
  },

  /**
   * Mimicks the interface of FrameNode, and a ThreadNode can never have
   * optimization data (at the moment, anyway), so provide a function
   * to return null so we don't need to check if a frame node is a thread
   * or not everytime we fetch optimization data.
   *
   * @return {null}
   */

  hasOptimizations: function () {
    return null;
  }
};

/**
 * A function call node in a tree. Represents a function call with a unique context,
 * resulting in each FrameNode having its own row in the corresponding tree view.
 * Take samples:
 *  A()->B()->C()
 *  A()->B()
 *  Q()->B()
 *
 * In inverted tree, A()->B()->C() would have one frame node, and A()->B() and
 * Q()->B() would share a frame node.
 * In an uninverted tree, A()->B()->C() and A()->B() would share a frame node,
 * with Q()->B() having its own.
 *
 * In all cases, all the frame nodes originated from the same InflatedFrame.
 *
 * @param string frameKey
 *        The key associated with this frame. The key determines identity of
 *        the node.
 * @param string location
 *        The location of this function call. Note that this isn't sanitized,
 *        so it may very well (not?) include the function name, url, etc.
 * @param number line
 *        The line number inside the source containing this function call.
 * @param number category
 *        The category type of this function call ("js", "graphics" etc.).
 * @param number allocations
 *        The number of memory allocations performed in this frame.
 * @param number isContent
 *        Whether this frame is content.
 * @param boolean isMetaCategory
 *        Whether or not this is a platform node that should appear as a
 *        generalized meta category or not.
 */
function FrameNode(frameKey, { location, line, category, isContent }, isMetaCategory) {
  this.key = frameKey;
  this.location = location;
  this.line = line;
  this.youngestFrameSamples = 0;
  this.samples = 0;
  this.calls = [];
  this.isContent = !!isContent;
  this._optimizations = null;
  this._tierData = [];
  this._stringTable = null;
  this.isMetaCategory = !!isMetaCategory;
  this.category = category;
  this.nodeType = "Frame";
  this.byteSize = 0;
  this.youngestFrameByteSize = 0;
}

FrameNode.prototype = {
  /**
   * Take optimization data observed for this frame.
   *
   * @param object optimizationSite
   *               Any JIT optimization information attached to the current
   *               sample. Lazily inflated via stringTable.
   * @param number implementation
   *               JIT implementation used for this observed frame (baseline, ion);
   *               can be null indicating "interpreter"
   * @param number time
   *               The time this optimization occurred.
   * @param object stringTable
   *               The string table used to inflate the optimizationSite.
   */
  _addOptimizations: function (site, implementation, time, stringTable) {
    // Simply accumulate optimization sites for now. Processing is done lazily
    // by JITOptimizations, if optimization information is actually displayed.
    if (site) {
      let opts = this._optimizations;
      if (opts === null) {
        opts = this._optimizations = [];
      }
      opts.push(site);
    }

    if (!this._stringTable) {
      this._stringTable = stringTable;
    }

    // Record type of implementation used and the sample time
    this._tierData.push({ implementation, time });
  },

  _clone: function (samples, size) {
    let newNode = new FrameNode(this.key, this, this.isMetaCategory);
    newNode._merge(this, samples, size);
    return newNode;
  },

  _merge: function (otherNode, samples, size) {
    if (this === otherNode) {
      return;
    }

    this.samples += samples;
    this.byteSize += size;
    if (otherNode.youngestFrameSamples > 0) {
      this.youngestFrameSamples += samples;
    }

    if (otherNode.youngestFrameByteSize > 0) {
      this.youngestFrameByteSize += otherNode.youngestFrameByteSize;
    }

    if (this._stringTable === null) {
      this._stringTable = otherNode._stringTable;
    }

    if (otherNode._optimizations) {
      if (!this._optimizations) {
        this._optimizations = [];
      }
      let opts = this._optimizations;
      let otherOpts = otherNode._optimizations;
      for (let i = 0; i < otherOpts.length; i++) {
        opts.push(otherOpts[i]);
      }
    }

    if (otherNode._tierData.length) {
      let tierData = this._tierData;
      let otherTierData = otherNode._tierData;
      for (let i = 0; i < otherTierData.length; i++) {
        tierData.push(otherTierData[i]);
      }
      tierData.sort((a, b) => a.time - b.time);
    }
  },

  /**
   * Returns the parsed location and additional data describing
   * this frame. Uses cached data if possible. Takes the following
   * options:
   *
   * @param {ThreadNode} options.root
   *                     The root thread node to calculate relative costs.
   *                     Generates [self|total] [duration|percentage] values.
   * @param {boolean} options.allocations
   *                  Generates `totalAllocations` and `selfAllocations`.
   *
   * @return object
   *         The computed { name, file, url, line } properties for this
   *         function call, as well as additional params if options specified.
   */
  getInfo: function (options) {
    return FrameUtils.getFrameInfo(this, options);
  },

  /**
   * Returns whether or not the frame node has an JITOptimizations model.
   *
   * @return {Boolean}
   */
  hasOptimizations: function () {
    return !this.isMetaCategory && !!this._optimizations;
  },

  /**
   * Returns the underlying JITOptimizations model representing
   * the optimization attempts occuring in this frame.
   *
   * @return {JITOptimizations|null}
   */
  getOptimizations: function () {
    if (!this._optimizations) {
      return null;
    }
    return new JITOptimizations(this._optimizations, this._stringTable);
  },

  /**
   * Returns the tiers used overtime.
   *
   * @return {Array<object>}
   */
  getTierData: function () {
    return this._tierData;
  }
};

exports.ThreadNode = ThreadNode;
exports.FrameNode = FrameNode;
PK
!<IeUppTchrome/devtools/modules/devtools/client/performance/modules/logic/waterfall-utils.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";

/**
 * Utility functions for collapsing markers into a waterfall.
 */

const { MarkerBlueprintUtils } = require("devtools/client/performance/modules/marker-blueprint-utils");

/**
 * Creates a parent marker, which functions like a regular marker,
 * but is able to hold additional child markers.
 *
 * The marker is seeded with values from `marker`.
 * @param object marker
 * @return object
 */
function createParentNode(marker) {
  return Object.assign({}, marker, { submarkers: [] });
}

/**
 * Collapses markers into a tree-like structure.
 * @param object rootNode
 * @param array markersList
 * @param array filter
 */
function collapseMarkersIntoNode({ rootNode, markersList, filter }) {
  let {
    getCurrentParentNode,
    pushNode,
    popParentNode
  } = createParentNodeFactory(rootNode);

  for (let i = 0, len = markersList.length; i < len; i++) {
    let curr = markersList[i];

    // If this marker type should not be displayed, just skip
    if (!MarkerBlueprintUtils.shouldDisplayMarker(curr, filter)) {
      continue;
    }

    let parentNode = getCurrentParentNode();
    let blueprint = MarkerBlueprintUtils.getBlueprintFor(curr);

    let nestable = "nestable" in blueprint ? blueprint.nestable : true;
    let collapsible = "collapsible" in blueprint ? blueprint.collapsible : true;

    let finalized = false;

    // Extend the marker with extra properties needed in the marker tree
    let extendedProps = { index: i };
    if (collapsible) {
      extendedProps.submarkers = [];
    }
    Object.assign(curr, extendedProps);

    // If not nestible, just push it inside the root node. Additionally,
    // markers originating outside the main thread are considered to be
    // "never collapsible", to avoid confusion.
    // A beter solution would be to collapse every marker with its siblings
    // from the same thread, but that would require a thread id attached
    // to all markers, which is potentially expensive and rather useless at
    // the moment, since we don't really have that many OTMT markers.
    if (!nestable || curr.isOffMainThread) {
      pushNode(rootNode, curr);
      continue;
    }

    // First off, if any parent nodes exist, finish them off
    // recursively upwards if this marker is outside their ranges and nestable.
    while (!finalized && parentNode) {
      // If this marker is eclipsed by the current parent marker,
      // make it a child of the current parent and stop going upwards.
      // If the markers aren't from the same process, attach them to the root
      // node as well. Every process has its own main thread.
      if (nestable &&
          curr.start >= parentNode.start &&
          curr.end <= parentNode.end &&
          curr.processType == parentNode.processType) {
        pushNode(parentNode, curr);
        finalized = true;
        break;
      }

      // If this marker is still nestable, but outside of the range
      // of the current parent, iterate upwards on the next parent
      // and finalize the current parent.
      if (nestable) {
        popParentNode();
        parentNode = getCurrentParentNode();
        continue;
      }
    }

    if (!finalized) {
      pushNode(rootNode, curr);
    }
  }
}

/**
 * Takes a root marker node and creates a hash of functions used
 * to manage the creation and nesting of additional parent markers.
 *
 * @param {object} root
 * @return {object}
 */
function createParentNodeFactory(root) {
  let parentMarkers = [];
  let factory = {
    /**
     * Pops the most recent parent node off the stack, finalizing it.
     * Sets the `end` time based on the most recent child if not defined.
     */
    popParentNode: () => {
      if (parentMarkers.length === 0) {
        throw new Error("Cannot pop parent markers when none exist.");
      }

      let lastParent = parentMarkers.pop();

      // If this finished parent marker doesn't have an end time,
      // so probably a synthesized marker, use the last marker's end time.
      if (lastParent.end == void 0) {
        lastParent.end = lastParent.submarkers[lastParent.submarkers.length - 1].end;
      }

      // If no children were ever pushed into this parent node,
      // remove its submarkers so it behaves like a non collapsible
      // node.
      if (!lastParent.submarkers.length) {
        delete lastParent.submarkers;
      }

      return lastParent;
    },

    /**
     * Returns the most recent parent node.
     */
    getCurrentParentNode: () => parentMarkers.length
      ? parentMarkers[parentMarkers.length - 1]
      : null,

    /**
     * Push this marker into the most recent parent node.
     */
    pushNode: (parent, marker) => {
      parent.submarkers.push(marker);

      // If pushing a parent marker, track it as the top of
      // the parent stack.
      if (marker.submarkers) {
        parentMarkers.push(marker);
      }
    }
  };

  return factory;
}

exports.createParentNode = createParentNode;
exports.collapseMarkersIntoNode = collapseMarkersIntoNode;
PK
!<�o!�
�
Uchrome/devtools/modules/devtools/client/performance/modules/marker-blueprint-utils.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { TIMELINE_BLUEPRINT } = require("devtools/client/performance/modules/markers");

/**
 * This file contains utilities for parsing out the markers blueprint
 * to generate strings to be displayed in the UI.
 */

exports.MarkerBlueprintUtils = {
  /**
   * Takes a marker and a list of marker names that should be hidden, and
   * determines if this marker should be filtered or not.
   *
   * @param object marker
   * @return boolean
   */
  shouldDisplayMarker: function (marker, hiddenMarkerNames) {
    if (!hiddenMarkerNames || hiddenMarkerNames.length == 0) {
      return true;
    }

    // If this marker isn't yet defined in the blueprint, simply check if the
    // entire category of "UNKNOWN" markers are supposed to be visible or not.
    let isUnknown = !(marker.name in TIMELINE_BLUEPRINT);
    if (isUnknown) {
      return !hiddenMarkerNames.includes("UNKNOWN");
    }

    return !hiddenMarkerNames.includes(marker.name);
  },

  /**
   * Takes a marker and returns the blueprint definition for that marker type,
   * falling back to the UNKNOWN blueprint definition if undefined.
   *
   * @param object marker
   * @return object
   */
  getBlueprintFor: function (marker) {
    return TIMELINE_BLUEPRINT[marker.name] || TIMELINE_BLUEPRINT.UNKNOWN;
  },

  /**
   * Returns the label to display for a marker, based off the blueprints.
   *
   * @param object marker
   * @return string
   */
  getMarkerLabel: function (marker) {
    let blueprint = this.getBlueprintFor(marker);
    let dynamic = typeof blueprint.label === "function";
    let label = dynamic ? blueprint.label(marker) : blueprint.label;
    return label;
  },

  /**
   * Returns the generic label to display for a marker name.
   * (e.g. "Function Call" for JS markers, rather than "setTimeout", etc.)
   *
   * @param string type
   * @return string
   */
  getMarkerGenericName: function (markerName) {
    let blueprint = this.getBlueprintFor({ name: markerName });
    let dynamic = typeof blueprint.label === "function";
    let generic = dynamic ? blueprint.label() : blueprint.label;

    // If no class name found, attempt to throw a descriptive error as to
    // how the marker implementor can fix this.
    if (!generic) {
      let message = `Could not find marker generic name for "${markerName}".`;
      if (typeof blueprint.label === "function") {
        message += ` The following function must return a generic name string when no` +
                   ` marker passed: ${blueprint.label}`;
      } else {
        message += ` ${markerName}.label must be defined in the marker blueprint.`;
      }
      throw new Error(message);
    }

    return generic;
  },

  /**
   * Returns an array of objects with key/value pairs of what should be rendered
   * in the marker details view.
   *
   * @param object marker
   * @return array<object>
   */
  getMarkerFields: function (marker) {
    let blueprint = this.getBlueprintFor(marker);
    let dynamic = typeof blueprint.fields === "function";
    let fields = dynamic ? blueprint.fields(marker) : blueprint.fields;

    return Object.entries(fields || {})
      .filter(([_, value]) => dynamic ? true : value in marker)
      .map(([label, value]) => ({ label, value: dynamic ? value : marker[value] }));
  },
};
PK
!<դ"��Ochrome/devtools/modules/devtools/client/performance/modules/marker-dom-utils.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 contains utilities for creating DOM nodes for markers
 * to be displayed in the UI.
 */

const { L10N, PREFS } = require("devtools/client/performance/modules/global");
const { MarkerBlueprintUtils } = require("devtools/client/performance/modules/marker-blueprint-utils");
const { getSourceNames } = require("devtools/client/shared/source-utils");

/**
 * Utilites for creating elements for markers.
 */
exports.MarkerDOMUtils = {
  /**
   * Builds all the fields possible for the given marker. Returns an
   * array of elements to be appended to a parent element.
   *
   * @param document doc
   * @param object marker
   * @return array<nsIDOMNode>
   */
  buildFields: function (doc, marker) {
    let fields = MarkerBlueprintUtils.getMarkerFields(marker);
    return fields.map(({ label, value }) => this.buildNameValueLabel(doc, label, value));
  },

  /**
   * Builds the label representing the marker's type.
   *
   * @param document doc
   * @param object marker
   * @return nsIDOMNode
   */
  buildTitle: function (doc, marker) {
    let blueprint = MarkerBlueprintUtils.getBlueprintFor(marker);

    let hbox = doc.createElement("hbox");
    hbox.setAttribute("align", "center");

    let bullet = doc.createElement("hbox");
    bullet.className = `marker-details-bullet marker-color-${blueprint.colorName}`;

    let title = MarkerBlueprintUtils.getMarkerLabel(marker);
    let label = doc.createElement("label");
    label.className = "marker-details-type";
    label.setAttribute("value", title);

    hbox.appendChild(bullet);
    hbox.appendChild(label);

    return hbox;
  },

  /**
   * Builds the label representing the marker's duration.
   *
   * @param document doc
   * @param object marker
   * @return nsIDOMNode
   */
  buildDuration: function (doc, marker) {
    let label = L10N.getStr("marker.field.duration");
    let start = L10N.getFormatStrWithNumbers("timeline.tick", marker.start);
    let end = L10N.getFormatStrWithNumbers("timeline.tick", marker.end);
    let duration = L10N.getFormatStrWithNumbers("timeline.tick",
                                                marker.end - marker.start);

    let el = this.buildNameValueLabel(doc, label, duration);
    el.classList.add("marker-details-duration");
    el.setAttribute("tooltiptext", `${start} → ${end}`);

    return el;
  },

  /**
   * Builds labels for name:value pairs.
   * E.g. "Start: 100ms", "Duration: 200ms", ...
   *
   * @param document doc
   * @param string field
   * @param string value
   * @return nsIDOMNode
   */
  buildNameValueLabel: function (doc, field, value) {
    let hbox = doc.createElement("hbox");
    hbox.className = "marker-details-labelcontainer";

    let nameLabel = doc.createElement("label");
    nameLabel.className = "plain marker-details-name-label";
    nameLabel.setAttribute("value", field);
    hbox.appendChild(nameLabel);

    let valueLabel = doc.createElement("label");
    valueLabel.className = "plain marker-details-value-label";
    valueLabel.setAttribute("value", value);
    hbox.appendChild(valueLabel);

    return hbox;
  },

  /**
   * Builds a stack trace in an element.
   *
   * @param document doc
   * @param object params
   *        An options object with the following members:
   *          - string type: string identifier for type of stack ("stack", "startStack"
                             or "endStack"
   *          - number frameIndex: the index of the topmost stack frame
   *          - array frames: array of stack frames
   */
  buildStackTrace: function (doc, { type, frameIndex, frames }) {
    let container = doc.createElement("vbox");
    container.className = "marker-details-stack";
    container.setAttribute("type", type);

    let nameLabel = doc.createElement("label");
    nameLabel.className = "plain marker-details-name-label";
    nameLabel.setAttribute("value", L10N.getStr(`marker.field.${type}`));
    container.appendChild(nameLabel);

    // Workaround for profiles that have looping stack traces.  See
    // bug 1246555.
    let wasAsyncParent = false;
    let seen = new Set();

    while (frameIndex > 0) {
      if (seen.has(frameIndex)) {
        break;
      }
      seen.add(frameIndex);

      let frame = frames[frameIndex];
      let url = frame.source;
      let displayName = frame.functionDisplayName;
      let line = frame.line;

      // If the previous frame had an async parent, then the async
      // cause is in this frame and should be displayed.
      if (wasAsyncParent) {
        let asyncStr = L10N.getFormatStr("marker.field.asyncStack", frame.asyncCause);
        let asyncBox = doc.createElement("hbox");
        let asyncLabel = doc.createElement("label");
        asyncLabel.className = "devtools-monospace";
        asyncLabel.setAttribute("value", asyncStr);
        asyncBox.appendChild(asyncLabel);
        container.appendChild(asyncBox);
        wasAsyncParent = false;
      }

      let hbox = doc.createElement("hbox");

      if (displayName) {
        let functionLabel = doc.createElement("label");
        functionLabel.className = "devtools-monospace";
        functionLabel.setAttribute("value", displayName);
        hbox.appendChild(functionLabel);
      }

      if (url) {
        let linkNode = doc.createElement("a");
        linkNode.className = "waterfall-marker-location devtools-source-link";
        linkNode.href = url;
        linkNode.draggable = false;
        linkNode.setAttribute("title", url);

        let urlLabel = doc.createElement("label");
        urlLabel.className = "filename";
        urlLabel.setAttribute("value", getSourceNames(url).short);
        linkNode.appendChild(urlLabel);

        let lineLabel = doc.createElement("label");
        lineLabel.className = "line-number";
        lineLabel.setAttribute("value", `:${line}`);
        linkNode.appendChild(lineLabel);

        hbox.appendChild(linkNode);

        // Clicking here will bubble up to the parent,
        // which handles the view source.
        linkNode.setAttribute("data-action", JSON.stringify({
          url: url,
          line: line,
          action: "view-source"
        }));
      }

      if (!displayName && !url) {
        let unknownLabel = doc.createElement("label");
        unknownLabel.setAttribute("value", L10N.getStr("marker.value.unknownFrame"));
        hbox.appendChild(unknownLabel);
      }

      container.appendChild(hbox);

      if (frame.asyncParent) {
        frameIndex = frame.asyncParent;
        wasAsyncParent = true;
      } else {
        frameIndex = frame.parent;
      }
    }

    return container;
  },

  /**
   * Builds any custom fields specific to the marker.
   *
   * @param document doc
   * @param object marker
   * @param object options
   * @return array<nsIDOMNode>
   */
  buildCustom: function (doc, marker, options) {
    let elements = [];

    if (options.allocations && shouldShowAllocationsTrigger(marker)) {
      let hbox = doc.createElement("hbox");
      hbox.className = "marker-details-customcontainer";

      let label = doc.createElement("label");
      label.className = "custom-button";
      label.setAttribute("value", "Show allocation triggers");
      label.setAttribute("type", "show-allocations");
      label.setAttribute("data-action", JSON.stringify({
        endTime: marker.start,
        action: "show-allocations"
      }));

      hbox.appendChild(label);
      elements.push(hbox);
    }

    return elements;
  },
};

/**
 * Takes a marker and determines if this marker should display
 * the allocations trigger button.
 *
 * @param object marker
 * @return boolean
 */
function shouldShowAllocationsTrigger(marker) {
  if (marker.name == "GarbageCollection") {
    let showTriggers = PREFS["show-triggers-for-gc-types"];
    return showTriggers.split(" ").includes(marker.causeName);
  }
  return false;
}
PK
!<�w��OOPchrome/devtools/modules/devtools/client/performance/modules/marker-formatters.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 contains utilities for creating elements for markers to be displayed,
 * and parsing out the blueprint to generate correct values for markers.
 */
const { Ci } = require("chrome");
const { L10N, PREFS } = require("devtools/client/performance/modules/global");

// String used to fill in platform data when it should be hidden.
const GECKO_SYMBOL = "(Gecko)";

/**
 * Mapping of JS marker causes to a friendlier form. Only
 * markers that are considered "from content" should be labeled here.
 */
const JS_MARKER_MAP = {
  "<script> element": L10N.getStr("marker.label.javascript.scriptElement"),
  "promise callback": L10N.getStr("marker.label.javascript.promiseCallback"),
  "promise initializer": L10N.getStr("marker.label.javascript.promiseInit"),
  "Worker runnable": L10N.getStr("marker.label.javascript.workerRunnable"),
  "javascript: URI": L10N.getStr("marker.label.javascript.jsURI"),
  // The difference between these two event handler markers are differences
  // in their WebIDL implementation, so distinguishing them is not necessary.
  "EventHandlerNonNull": L10N.getStr("marker.label.javascript.eventHandler"),
  "EventListener.handleEvent": L10N.getStr("marker.label.javascript.eventHandler"),
  // These markers do not get L10N'd because they're JS names.
  "setInterval handler": "setInterval",
  "setTimeout handler": "setTimeout",
  "FrameRequestCallback": "requestAnimationFrame",
};

/**
 * A series of formatters used by the blueprint.
 */
exports.Formatters = {
  /**
   * Uses the marker name as the label for markers that do not have
   * a blueprint entry. Uses "Other" in the marker filter menu.
   */
  UnknownLabel: function (marker = {}) {
    return marker.name || L10N.getStr("marker.label.unknown");
  },

  /* Group 0 - Reflow and Rendering pipeline */

  StylesFields: function (marker) {
    if ("isAnimationOnly" in marker) {
      return {
        [L10N.getStr("marker.field.isAnimationOnly")]: marker.isAnimationOnly
      };
    }
    return null;
  },

  /* Group 1 - JS */

  DOMEventFields: function (marker) {
    let fields = Object.create(null);

    if ("type" in marker) {
      fields[L10N.getStr("marker.field.DOMEventType")] = marker.type;
    }

    if ("eventPhase" in marker) {
      let label;
      switch (marker.eventPhase) {
        case Ci.nsIDOMEvent.AT_TARGET:
          label = L10N.getStr("marker.value.DOMEventTargetPhase");
          break;
        case Ci.nsIDOMEvent.CAPTURING_PHASE:
          label = L10N.getStr("marker.value.DOMEventCapturingPhase");
          break;
        case Ci.nsIDOMEvent.BUBBLING_PHASE:
          label = L10N.getStr("marker.value.DOMEventBubblingPhase");
          break;
      }
      fields[L10N.getStr("marker.field.DOMEventPhase")] = label;
    }

    return fields;
  },

  JSLabel: function (marker = {}) {
    let generic = L10N.getStr("marker.label.javascript");
    if ("causeName" in marker) {
      return JS_MARKER_MAP[marker.causeName] || generic;
    }
    return generic;
  },

  JSFields: function (marker) {
    if ("causeName" in marker && !JS_MARKER_MAP[marker.causeName]) {
      let label = PREFS["show-platform-data"] ? marker.causeName : GECKO_SYMBOL;
      return {
        [L10N.getStr("marker.field.causeName")]: label
      };
    }
    return null;
  },

  GCLabel: function (marker) {
    if (!marker) {
      return L10N.getStr("marker.label.garbageCollection2");
    }
    // Only if a `nonincrementalReason` exists, do we want to label
    // this as a non incremental GC event.
    if ("nonincrementalReason" in marker) {
      return L10N.getStr("marker.label.garbageCollection.nonIncremental");
    }
    return L10N.getStr("marker.label.garbageCollection.incremental");
  },

  GCFields: function (marker) {
    let fields = Object.create(null);

    if ("causeName" in marker) {
      let cause = marker.causeName;
      let label = L10N.getStr(`marker.gcreason.label.${cause}`) || cause;
      fields[L10N.getStr("marker.field.causeName")] = label;
    }

    if ("nonincrementalReason" in marker) {
      let label = marker.nonincrementalReason;
      fields[L10N.getStr("marker.field.nonIncrementalCause")] = label;
    }

    return fields;
  },

  MinorGCFields: function (marker) {
    let fields = Object.create(null);

    if ("causeName" in marker) {
      let cause = marker.causeName;
      let label = L10N.getStr(`marker.gcreason.label.${cause}`) || cause;
      fields[L10N.getStr("marker.field.causeName")] = label;
    }

    fields[L10N.getStr("marker.field.type")] = L10N.getStr("marker.nurseryCollection");

    return fields;
  },

  CycleCollectionFields: function (marker) {
    let label = marker.name.replace(/nsCycleCollector::/g, "");
    return {
      [L10N.getStr("marker.field.type")]: label
    };
  },

  WorkerFields: function (marker) {
    if ("workerOperation" in marker) {
      let label = L10N.getStr(`marker.worker.${marker.workerOperation}`);
      return {
        [L10N.getStr("marker.field.type")]: label
      };
    }
    return null;
  },

  MessagePortFields: function (marker) {
    if ("messagePortOperation" in marker) {
      let label = L10N.getStr(`marker.messagePort.${marker.messagePortOperation}`);
      return {
        [L10N.getStr("marker.field.type")]: label
      };
    }
    return null;
  },

  /* Group 2 - User Controlled */

  ConsoleTimeFields: {
    [L10N.getStr("marker.field.consoleTimerName")]: "causeName"
  },

  TimeStampFields: {
    [L10N.getStr("marker.field.label")]: "causeName"
  }
};

/**
 * Takes a main label (e.g. "Timestamp") and a property name (e.g. "causeName"),
 * and returns a string that represents that property value for a marker if it
 * exists (e.g. "Timestamp (rendering)"), or just the main label if it does not.
 *
 * @param string mainLabel
 * @param string propName
 */
exports.Formatters.labelForProperty = function (mainLabel, propName) {
  return (marker = {}) => marker[propName]
    ? `${mainLabel} (${marker[propName]})`
    : mainLabel;
};
PK
!<��׃��Fchrome/devtools/modules/devtools/client/performance/modules/markers.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { L10N } = require("devtools/client/performance/modules/global");
const { Formatters } = require("devtools/client/performance/modules/marker-formatters");

/**
 * A simple schema for mapping markers to the timeline UI. The keys correspond
 * to marker names, while the values are objects with the following format:
 *
 * - group: The row index in the overview graph; multiple markers
 *          can be added on the same row. @see <overview.js/buildGraphImage>
 * - label: The label used in the waterfall to identify the marker. Can be a
 *          string or just a function that accepts the marker and returns a
 *          string (if you want to use a dynamic property for the main label).
 *          If you use a function for a label, it *must* handle the case where
 *          no marker is provided, to get a generic label used to describe
 *          all markers of this type.
 * - fields: The fields used in the marker details view to display more
 *           information about a currently selected marker. Can either be an
 *           object of fields, or simply a function that accepts the marker and
 *           returns such an object (if you want to use properties dynamically).
 *           For example, a field in the object such as { "Cause": "causeName" }
 *           would render something like `Cause: ${marker.causeName}` in the UI.
 * - colorName: The label of the DevTools color used for this marker. If
 *              adding a new color, be sure to check that there's an entry
 *              for `.marker-color-graphs-{COLORNAME}` for the equivilent
 *              entry in "./devtools/client/themes/performance.css"
 *              https://developer.mozilla.org/en-US/docs/Tools/DevToolsColors
 * - collapsible: Whether or not this marker can contain other markers it
 *                eclipses, and becomes collapsible to reveal its nestable
 *                children. Defaults to true.
 * - nestable: Whether or not this marker can be nested inside an eclipsing
 *             collapsible marker. Defaults to true.
 */
const TIMELINE_BLUEPRINT = {
  /* Default definition used for markers that occur but are not defined here.
   * Should ultimately be defined, but this gives us room to work on the
   * front end separately from the platform. */
  "UNKNOWN": {
    group: 2,
    colorName: "graphs-grey",
    label: Formatters.UnknownLabel,
  },

  /* Group 0 - Reflow and Rendering pipeline */

  "Styles": {
    group: 0,
    colorName: "graphs-purple",
    label: L10N.getStr("marker.label.styles"),
    fields: Formatters.StylesFields,
  },
  "StylesApplyChanges": {
    group: 0,
    colorName: "graphs-purple",
    label: L10N.getStr("marker.label.stylesApplyChanges"),
  },
  "Reflow": {
    group: 0,
    colorName: "graphs-purple",
    label: L10N.getStr("marker.label.reflow"),
  },
  "Paint": {
    group: 0,
    colorName: "graphs-green",
    label: L10N.getStr("marker.label.paint"),
  },
  "Composite": {
    group: 0,
    colorName: "graphs-green",
    label: L10N.getStr("marker.label.composite"),
  },
  "CompositeForwardTransaction": {
    group: 0,
    colorName: "graphs-bluegrey",
    label: L10N.getStr("marker.label.compositeForwardTransaction"),
  },

  /* Group 1 - JS */

  "DOMEvent": {
    group: 1,
    colorName: "graphs-yellow",
    label: L10N.getStr("marker.label.domevent"),
    fields: Formatters.DOMEventFields,
  },
  "document::DOMContentLoaded": {
    group: 1,
    colorName: "graphs-full-red",
    label: "DOMContentLoaded"
  },
  "document::Load": {
    group: 1,
    colorName: "graphs-full-blue",
    label: "Load"
  },
  "Javascript": {
    group: 1,
    colorName: "graphs-yellow",
    label: Formatters.JSLabel,
    fields: Formatters.JSFields
  },
  "Parse HTML": {
    group: 1,
    colorName: "graphs-yellow",
    label: L10N.getStr("marker.label.parseHTML"),
  },
  "Parse XML": {
    group: 1,
    colorName: "graphs-yellow",
    label: L10N.getStr("marker.label.parseXML"),
  },
  "GarbageCollection": {
    group: 1,
    colorName: "graphs-red",
    label: Formatters.GCLabel,
    fields: Formatters.GCFields,
  },
  "MinorGC": {
    group: 1,
    colorName: "graphs-red",
    label: L10N.getStr("marker.label.minorGC"),
    fields: Formatters.MinorGCFields,
  },
  "nsCycleCollector::Collect": {
    group: 1,
    colorName: "graphs-red",
    label: L10N.getStr("marker.label.cycleCollection"),
    fields: Formatters.CycleCollectionFields,
  },
  "nsCycleCollector::ForgetSkippable": {
    group: 1,
    colorName: "graphs-red",
    label: L10N.getStr("marker.label.cycleCollection.forgetSkippable"),
    fields: Formatters.CycleCollectionFields,
  },
  "Worker": {
    group: 1,
    colorName: "graphs-orange",
    label: L10N.getStr("marker.label.worker"),
    fields: Formatters.WorkerFields
  },
  "MessagePort": {
    group: 1,
    colorName: "graphs-orange",
    label: L10N.getStr("marker.label.messagePort"),
    fields: Formatters.MessagePortFields
  },

  /* Group 2 - User Controlled */

  "ConsoleTime": {
    group: 2,
    colorName: "graphs-blue",
    label: Formatters.labelForProperty(L10N.getStr("marker.label.consoleTime"),
                                       "causeName"),
    fields: Formatters.ConsoleTimeFields,
    nestable: false,
    collapsible: false,
  },
  "TimeStamp": {
    group: 2,
    colorName: "graphs-blue",
    label: Formatters.labelForProperty(L10N.getStr("marker.label.timestamp"),
                                       "causeName"),
    fields: Formatters.TimeStampFields,
    collapsible: false,
  },
};

// Exported symbols.
exports.TIMELINE_BLUEPRINT = TIMELINE_BLUEPRINT;
PK
!<M�����Dchrome/devtools/modules/devtools/client/performance/modules/utils.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 document */

/**
 * React components grab the namespace of the element they are mounting to. This function
 * takes a XUL element, and makes sure to create a properly namespaced HTML element to
 * avoid React creating XUL elements.
 *
 * {XULElement} xulElement
 * return {HTMLElement} div
 */

exports.createHtmlMount = function (xulElement) {
  let htmlElement = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
  xulElement.appendChild(htmlElement);
  return htmlElement;
};
PK
!<�c��Nchrome/devtools/modules/devtools/client/performance/modules/waterfall-ticks.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 WATERFALL_BACKGROUND_TICKS_MULTIPLE = 5; // ms
const WATERFALL_BACKGROUND_TICKS_SCALES = 3;
const WATERFALL_BACKGROUND_TICKS_SPACING_MIN = 10; // px
const WATERFALL_BACKGROUND_TICKS_COLOR_RGB = [128, 136, 144];
const WATERFALL_BACKGROUND_TICKS_OPACITY_MIN = 32; // byte
const WATERFALL_BACKGROUND_TICKS_OPACITY_ADD = 32; // byte

const FIND_OPTIMAL_TICK_INTERVAL_MAX_ITERS = 100;

/**
 * Creates the background displayed on the marker's waterfall.
 */
function drawWaterfallBackground(doc, dataScale, waterfallWidth) {
  let canvas = doc.createElementNS(HTML_NS, "canvas");
  let ctx = canvas.getContext("2d");

  // Nuke the context.
  let canvasWidth = canvas.width = waterfallWidth;
  // Awww yeah, 1px, repeats on Y axis.
  let canvasHeight = canvas.height = 1;

  // Start over.
  let imageData = ctx.createImageData(canvasWidth, canvasHeight);
  let pixelArray = imageData.data;

  let buf = new ArrayBuffer(pixelArray.length);
  let view8bit = new Uint8ClampedArray(buf);
  let view32bit = new Uint32Array(buf);

  // Build new millisecond tick lines...
  let [r, g, b] = WATERFALL_BACKGROUND_TICKS_COLOR_RGB;
  let alphaComponent = WATERFALL_BACKGROUND_TICKS_OPACITY_MIN;
  let tickInterval = findOptimalTickInterval({
    ticksMultiple: WATERFALL_BACKGROUND_TICKS_MULTIPLE,
    ticksSpacingMin: WATERFALL_BACKGROUND_TICKS_SPACING_MIN,
    dataScale: dataScale
  });

  // Insert one pixel for each division on each scale.
  for (let i = 1; i <= WATERFALL_BACKGROUND_TICKS_SCALES; i++) {
    let increment = tickInterval * Math.pow(2, i);
    for (let x = 0; x < canvasWidth; x += increment) {
      let position = x | 0;
      view32bit[position] = (alphaComponent << 24) | (b << 16) | (g << 8) | r;
    }
    alphaComponent += WATERFALL_BACKGROUND_TICKS_OPACITY_ADD;
  }

  // Flush the image data and cache the waterfall background.
  pixelArray.set(view8bit);
  ctx.putImageData(imageData, 0, 0);
  doc.mozSetImageElement("waterfall-background", canvas);

  return canvas;
}

/**
 * Finds the optimal tick interval between time markers in this timeline.
 *
 * @param number ticksMultiple
 * @param number ticksSpacingMin
 * @param number dataScale
 * @return number
 */
function findOptimalTickInterval({ ticksMultiple, ticksSpacingMin, dataScale }) {
  let timingStep = ticksMultiple;
  let maxIters = FIND_OPTIMAL_TICK_INTERVAL_MAX_ITERS;
  let numIters = 0;

  if (dataScale > ticksSpacingMin) {
    return dataScale;
  }

  while (true) {
    let scaledStep = dataScale * timingStep;
    if (++numIters > maxIters) {
      return scaledStep;
    }
    if (scaledStep < ticksSpacingMin) {
      timingStep <<= 1;
      continue;
    }
    return scaledStep;
  }
}

exports.TickUtils = { findOptimalTickInterval, drawWaterfallBackground };
PK
!<3��;�;Mchrome/devtools/modules/devtools/client/performance/modules/widgets/graphs.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 contains the base line graph that all Performance line graphs use.
 */

const { Task } = require("devtools/shared/task");
const { extend } = require("devtools/shared/extend");
const LineGraphWidget = require("devtools/client/shared/widgets/LineGraphWidget");
const MountainGraphWidget = require("devtools/client/shared/widgets/MountainGraphWidget");
const { CanvasGraphUtils } = require("devtools/client/shared/widgets/Graphs");

const defer = require("devtools/shared/defer");
const EventEmitter = require("devtools/shared/old-event-emitter");

const { colorUtils } = require("devtools/shared/css/color");
const { getColor } = require("devtools/client/shared/theme");
const ProfilerGlobal = require("devtools/client/performance/modules/global");
const { MarkersOverview } = require("devtools/client/performance/modules/widgets/markers-overview");
const { createTierGraphDataFromFrameNode } = require("devtools/client/performance/modules/logic/jit");

/**
 * For line graphs
 */
const HEIGHT = 35; // px
const STROKE_WIDTH = 1; // px
const DAMPEN_VALUES = 0.95;
const CLIPHEAD_LINE_COLOR = "#666";
const SELECTION_LINE_COLOR = "#555";
const SELECTION_BACKGROUND_COLOR_NAME = "graphs-blue";
const FRAMERATE_GRAPH_COLOR_NAME = "graphs-green";
const MEMORY_GRAPH_COLOR_NAME = "graphs-blue";

/**
 * For timeline overview
 */
const MARKERS_GRAPH_HEADER_HEIGHT = 14; // px
const MARKERS_GRAPH_ROW_HEIGHT = 10; // px
const MARKERS_GROUP_VERTICAL_PADDING = 4; // px

/**
 * For optimization graph
 */
const OPTIMIZATIONS_GRAPH_RESOLUTION = 100;

/**
 * A base class for performance graphs to inherit from.
 *
 * @param nsIDOMNode parent
 *        The parent node holding the overview.
 * @param string metric
 *        The unit of measurement for this graph.
 */
function PerformanceGraph(parent, metric) {
  LineGraphWidget.call(this, parent, { metric });
  this.setTheme();
}

PerformanceGraph.prototype = extend(LineGraphWidget.prototype, {
  strokeWidth: STROKE_WIDTH,
  dampenValuesFactor: DAMPEN_VALUES,
  fixedHeight: HEIGHT,
  clipheadLineColor: CLIPHEAD_LINE_COLOR,
  selectionLineColor: SELECTION_LINE_COLOR,
  withTooltipArrows: false,
  withFixedTooltipPositions: true,

  /**
   * Disables selection and empties this graph.
   */
  clearView: function () {
    this.selectionEnabled = false;
    this.dropSelection();
    this.setData([]);
  },

  /**
   * Sets the theme via `theme` to either "light" or "dark",
   * and updates the internal styling to match. Requires a redraw
   * to see the effects.
   */
  setTheme: function (theme) {
    theme = theme || "light";
    let mainColor = getColor(this.mainColor || "graphs-blue", theme);
    this.backgroundColor = getColor("body-background", theme);
    this.strokeColor = mainColor;
    this.backgroundGradientStart = colorUtils.setAlpha(mainColor, 0.2);
    this.backgroundGradientEnd = colorUtils.setAlpha(mainColor, 0.2);
    this.selectionBackgroundColor = colorUtils.setAlpha(
      getColor(SELECTION_BACKGROUND_COLOR_NAME, theme), 0.25);
    this.selectionStripesColor = "rgba(255, 255, 255, 0.1)";
    this.maximumLineColor = colorUtils.setAlpha(mainColor, 0.4);
    this.averageLineColor = colorUtils.setAlpha(mainColor, 0.7);
    this.minimumLineColor = colorUtils.setAlpha(mainColor, 0.9);
  }
});

/**
 * Constructor for the framerate graph. Inherits from PerformanceGraph.
 *
 * @param nsIDOMNode parent
 *        The parent node holding the overview.
 */
function FramerateGraph(parent) {
  PerformanceGraph.call(this, parent, ProfilerGlobal.L10N.getStr("graphs.fps"));
}

FramerateGraph.prototype = extend(PerformanceGraph.prototype, {
  mainColor: FRAMERATE_GRAPH_COLOR_NAME,
  setPerformanceData: function ({ duration, ticks }, resolution) {
    this.dataDuration = duration;
    return this.setDataFromTimestamps(ticks, resolution, duration);
  }
});

/**
 * Constructor for the memory graph. Inherits from PerformanceGraph.
 *
 * @param nsIDOMNode parent
 *        The parent node holding the overview.
 */
function MemoryGraph(parent) {
  PerformanceGraph.call(this, parent, ProfilerGlobal.L10N.getStr("graphs.memory"));
}

MemoryGraph.prototype = extend(PerformanceGraph.prototype, {
  mainColor: MEMORY_GRAPH_COLOR_NAME,
  setPerformanceData: function ({ duration, memory }) {
    this.dataDuration = duration;
    return this.setData(memory);
  }
});

function TimelineGraph(parent, filter) {
  MarkersOverview.call(this, parent, filter);
}

TimelineGraph.prototype = extend(MarkersOverview.prototype, {
  headerHeight: MARKERS_GRAPH_HEADER_HEIGHT,
  rowHeight: MARKERS_GRAPH_ROW_HEIGHT,
  groupPadding: MARKERS_GROUP_VERTICAL_PADDING,
  setPerformanceData: MarkersOverview.prototype.setData
});

/**
 * Definitions file for GraphsController, indicating the constructor,
 * selector and other meta for each of the graphs controller by
 * GraphsController.
 */
const GRAPH_DEFINITIONS = {
  memory: {
    constructor: MemoryGraph,
    selector: "#memory-overview",
  },
  framerate: {
    constructor: FramerateGraph,
    selector: "#time-framerate",
  },
  timeline: {
    constructor: TimelineGraph,
    selector: "#markers-overview",
    primaryLink: true
  }
};

/**
 * A controller for orchestrating the performance's tool overview graphs. Constructs,
 * syncs, toggles displays and defines the memory, framerate and timeline view.
 *
 * @param {object} definition
 * @param {DOMElement} root
 * @param {function} getFilter
 * @param {function} getTheme
 */
function GraphsController({ definition, root, getFilter, getTheme }) {
  this._graphs = {};
  this._enabled = new Set();
  this._definition = definition || GRAPH_DEFINITIONS;
  this._root = root;
  this._getFilter = getFilter;
  this._getTheme = getTheme;
  this._primaryLink = Object.keys(this._definition)
                            .filter(name => this._definition[name].primaryLink)[0];
  this.$ = root.ownerDocument.querySelector.bind(root.ownerDocument);

  EventEmitter.decorate(this);
  this._onSelecting = this._onSelecting.bind(this);
}

GraphsController.prototype = {

  /**
   * Returns the corresponding graph by `graphName`.
   */
  get: function (graphName) {
    return this._graphs[graphName];
  },

  /**
   * Iterates through all graphs and renders the data
   * from a RecordingModel. Takes a resolution value used in
   * some graphs.
   * Saves rendering progress as a promise to be consumed by `destroy`,
   * to wait for cleaning up rendering during destruction.
   */
  render: Task.async(function* (recordingData, resolution) {
    // Get the previous render promise so we don't start rendering
    // until the previous render cycle completes, which can occur
    // especially when a recording is finished, and triggers a
    // fresh rendering at a higher rate
    yield (this._rendering && this._rendering.promise);

    // Check after yielding to ensure we're not tearing down,
    // as this can create a race condition in tests
    if (this._destroyed) {
      return;
    }

    this._rendering = defer();
    for (let graph of (yield this._getEnabled())) {
      yield graph.setPerformanceData(recordingData, resolution);
      this.emit("rendered", graph.graphName);
    }
    this._rendering.resolve();
  }),

  /**
   * Destroys the underlying graphs.
   */
  destroy: Task.async(function* () {
    let primary = this._getPrimaryLink();

    this._destroyed = true;

    if (primary) {
      primary.off("selecting", this._onSelecting);
    }

    // If there was rendering, wait until the most recent render cycle
    // has finished
    if (this._rendering) {
      yield this._rendering.promise;
    }

    for (let graph of this.getWidgets()) {
      yield graph.destroy();
    }
  }),

  /**
   * Applies the theme to the underlying graphs. Optionally takes
   * a `redraw` boolean in the options to force redraw.
   */
  setTheme: function (options = {}) {
    let theme = options.theme || this._getTheme();
    for (let graph of this.getWidgets()) {
      graph.setTheme(theme);
      graph.refresh({ force: options.redraw });
    }
  },

  /**
   * Sets up the graph, if needed. Returns a promise resolving
   * to the graph if it is enabled once it's ready, or otherwise returns
   * null if disabled.
   */
  isAvailable: Task.async(function* (graphName) {
    if (!this._enabled.has(graphName)) {
      return null;
    }

    let graph = this.get(graphName);

    if (!graph) {
      graph = yield this._construct(graphName);
    }

    yield graph.ready();
    return graph;
  }),

  /**
   * Enable or disable a subgraph controlled by GraphsController.
   * This determines what graphs are visible and get rendered.
   */
  enable: function (graphName, isEnabled) {
    let el = this.$(this._definition[graphName].selector);
    el.classList[isEnabled ? "remove" : "add"]("hidden");

    // If no status change, just return
    if (this._enabled.has(graphName) === isEnabled) {
      return;
    }
    if (isEnabled) {
      this._enabled.add(graphName);
    } else {
      this._enabled.delete(graphName);
    }

    // Invalidate our cache of ready-to-go graphs
    this._enabledGraphs = null;
  },

  /**
   * Disables all graphs controller by the GraphsController, and
   * also hides the root element. This is a one way switch, and used
   * when older platforms do not have any timeline data.
   */
  disableAll: function () {
    this._root.classList.add("hidden");
    // Hide all the subelements
    Object.keys(this._definition).forEach(graphName => this.enable(graphName, false));
  },

  /**
   * Sets a mapped selection on the graph that is the main controller
   * for keeping the graphs' selections in sync.
   */
  setMappedSelection: function (selection, { mapStart, mapEnd }) {
    return this._getPrimaryLink().setMappedSelection(selection, { mapStart, mapEnd });
  },

  /**
   * Fetches the currently mapped selection. If graphs are not yet rendered,
   * (which throws in Graphs.js), return null.
   */
  getMappedSelection: function ({ mapStart, mapEnd }) {
    let primary = this._getPrimaryLink();
    if (primary && primary.hasData()) {
      return primary.getMappedSelection({ mapStart, mapEnd });
    }
    return null;
  },

  /**
   * Returns an array of graphs that have been created, not necessarily
   * enabled currently.
   */
  getWidgets: function () {
    return Object.keys(this._graphs).map(name => this._graphs[name]);
  },

  /**
   * Drops the selection.
   */
  dropSelection: function () {
    if (this._getPrimaryLink()) {
      return this._getPrimaryLink().dropSelection();
    }
    return null;
  },

  /**
   * Makes sure the selection is enabled or disabled in all the graphs.
   */
  selectionEnabled: Task.async(function* (enabled) {
    for (let graph of (yield this._getEnabled())) {
      graph.selectionEnabled = enabled;
    }
  }),

  /**
   * Creates the graph `graphName` and initializes it.
   */
  _construct: Task.async(function* (graphName) {
    let def = this._definition[graphName];
    let el = this.$(def.selector);
    let filter = this._getFilter();
    let graph = this._graphs[graphName] = new def.constructor(el, filter);
    graph.graphName = graphName;

    yield graph.ready();

    // Sync the graphs' animations and selections together
    if (def.primaryLink) {
      graph.on("selecting", this._onSelecting);
    } else {
      CanvasGraphUtils.linkAnimation(this._getPrimaryLink(), graph);
      CanvasGraphUtils.linkSelection(this._getPrimaryLink(), graph);
    }

    // Sets the container element's visibility based off of enabled status
    el.classList[this._enabled.has(graphName) ? "remove" : "add"]("hidden");

    this.setTheme();
    return graph;
  }),

  /**
   * Returns the main graph for this collection, that all graphs
   * are bound to for syncing and selection.
   */
  _getPrimaryLink: function () {
    return this.get(this._primaryLink);
  },

  /**
   * Emitted when a selection occurs.
   */
  _onSelecting: function () {
    this.emit("selecting");
  },

  /**
   * Resolves to an array with all graphs that are enabled, and
   * creates them if needed. Different than just iterating over `this._graphs`,
   * as those could be enabled. Uses caching, as rendering happens many times per second,
   * compared to how often which graphs/features are changed (rarely).
   */
  _getEnabled: Task.async(function* () {
    if (this._enabledGraphs) {
      return this._enabledGraphs;
    }
    let enabled = [];
    for (let graphName of this._enabled) {
      let graph = yield this.isAvailable(graphName);
      if (graph) {
        enabled.push(graph);
      }
    }
    this._enabledGraphs = enabled;
    return this._enabledGraphs;
  }),
};

/**
 * A base class for performance graphs to inherit from.
 *
 * @param nsIDOMNode parent
 *        The parent node holding the overview.
 * @param string metric
 *        The unit of measurement for this graph.
 */
function OptimizationsGraph(parent) {
  MountainGraphWidget.call(this, parent);
  this.setTheme();
}

OptimizationsGraph.prototype = extend(MountainGraphWidget.prototype, {

  render: Task.async(function* (threadNode, frameNode) {
    // Regardless if we draw or clear the graph, wait
    // until it's ready.
    yield this.ready();

    if (!threadNode || !frameNode) {
      this.setData([]);
      return;
    }

    let { sampleTimes } = threadNode;

    if (!sampleTimes.length) {
      this.setData([]);
      return;
    }

    // Take startTime/endTime from samples recorded, rather than
    // using duration directly from threadNode, as the first sample that
    // equals the startTime does not get recorded.
    let startTime = sampleTimes[0];
    let endTime = sampleTimes[sampleTimes.length - 1];

    let bucketSize = (endTime - startTime) / OPTIMIZATIONS_GRAPH_RESOLUTION;
    let data = createTierGraphDataFromFrameNode(frameNode, sampleTimes, bucketSize);

    // If for some reason we don't have data (like the frameNode doesn't
    // have optimizations, but it shouldn't be at this point if it doesn't),
    // log an error.
    if (!data) {
      console.error(
        `FrameNode#${frameNode.location} does not have optimizations data to render.`);
      return;
    }

    this.dataOffsetX = startTime;
    yield this.setData(data);
  }),

  /**
   * Sets the theme via `theme` to either "light" or "dark",
   * and updates the internal styling to match. Requires a redraw
   * to see the effects.
   */
  setTheme: function (theme) {
    theme = theme || "light";

    let interpreterColor = getColor("graphs-red", theme);
    let baselineColor = getColor("graphs-blue", theme);
    let ionColor = getColor("graphs-green", theme);

    this.format = [
      { color: interpreterColor },
      { color: baselineColor },
      { color: ionColor },
    ];

    this.backgroundColor = getColor("sidebar-background", theme);
  }
});

exports.OptimizationsGraph = OptimizationsGraph;
exports.FramerateGraph = FramerateGraph;
exports.MemoryGraph = MemoryGraph;
exports.TimelineGraph = TimelineGraph;
exports.GraphsController = GraphsController;
PK
!<Ug;�Uchrome/devtools/modules/devtools/client/performance/modules/widgets/marker-details.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 contains the rendering code for the marker sidebar.
 */

const EventEmitter = require("devtools/shared/old-event-emitter");
const { MarkerDOMUtils } = require("devtools/client/performance/modules/marker-dom-utils");

/**
 * A detailed view for one single marker.
 *
 * @param nsIDOMNode parent
 *        The parent node holding the view.
 * @param nsIDOMNode splitter
 *        The splitter node that the resize event is bound to.
 */
function MarkerDetails(parent, splitter) {
  EventEmitter.decorate(this);

  this._document = parent.ownerDocument;
  this._parent = parent;
  this._splitter = splitter;

  this._onClick = this._onClick.bind(this);
  this._onSplitterMouseUp = this._onSplitterMouseUp.bind(this);

  this._parent.addEventListener("click", this._onClick);
  this._splitter.addEventListener("mouseup", this._onSplitterMouseUp);

  this.hidden = true;
}

MarkerDetails.prototype = {
  /**
   * Sets this view's width.
   * @param number
   */
  set width(value) {
    this._parent.setAttribute("width", value);
  },

  /**
   * Sets this view's width.
   * @return number
   */
  get width() {
    return +this._parent.getAttribute("width");
  },

  /**
   * Sets this view's visibility.
   * @param boolean
   */
  set hidden(value) {
    if (this._parent.hidden != value) {
      this._parent.hidden = value;
      this.emit("resize");
    }
  },

  /**
   * Gets this view's visibility.
   * @param boolean
   */
  get hidden() {
    return this._parent.hidden;
  },

  /**
   * Clears the marker details from this view.
   */
  empty: function () {
    this._parent.innerHTML = "";
  },

  /**
   * Populates view with marker's details.
   *
   * @param object params
   *        An options object holding:
   *          - marker: The marker to display.
   *          - frames: Array of stack frame information; see stack.js.
   *          - allocations: Whether or not allocations were enabled for this
   *                         recording. [optional]
   */
  render: function (options) {
    let { marker, frames } = options;
    this.empty();

    let elements = [];
    elements.push(MarkerDOMUtils.buildTitle(this._document, marker));
    elements.push(MarkerDOMUtils.buildDuration(this._document, marker));
    MarkerDOMUtils.buildFields(this._document, marker).forEach(f => elements.push(f));
    MarkerDOMUtils.buildCustom(this._document, marker, options)
                  .forEach(f => elements.push(f));

    // Build a stack element -- and use the "startStack" label if
    // we have both a startStack and endStack.
    if (marker.stack) {
      let type = marker.endStack ? "startStack" : "stack";
      elements.push(MarkerDOMUtils.buildStackTrace(this._document, {
        frameIndex: marker.stack, frames, type
      }));
    }
    if (marker.endStack) {
      let type = "endStack";
      elements.push(MarkerDOMUtils.buildStackTrace(this._document, {
        frameIndex: marker.endStack, frames, type
      }));
    }

    elements.forEach(el => this._parent.appendChild(el));
  },

  /**
   * Handles click in the marker details view. Based on the target,
   * can handle different actions -- only supporting view source links
   * for the moment.
   */
  _onClick: function (e) {
    let data = findActionFromEvent(e.target, this._parent);
    if (!data) {
      return;
    }

    this.emit(data.action, data);
  },

  /**
   * Handles the "mouseup" event on the marker details view splitter.
   */
  _onSplitterMouseUp: function () {
    this.emit("resize");
  }
};

/**
 * Take an element from an event `target`, and ascend through
 * the DOM, looking for an element with a `data-action` attribute. Return
 * the parsed `data-action` value found, or null if none found before
 * reaching the parent `container`.
 *
 * @param {Element} target
 * @param {Element} container
 * @return {?object}
 */
function findActionFromEvent(target, container) {
  let el = target;
  let action;
  while (el !== container) {
    action = el.getAttribute("data-action");
    if (action) {
      return JSON.parse(action);
    }
    el = el.parentNode;
  }
  return null;
}

exports.MarkerDetails = MarkerDetails;
PK
!<8�pqS S Wchrome/devtools/modules/devtools/client/performance/modules/widgets/markers-overview.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 contains the "markers overview" graph, which is a minimap of all
 * the timeline data. Regions inside it may be selected, determining which
 * markers are visible in the "waterfall".
 */

const { extend } = require("devtools/shared/extend");
const { AbstractCanvasGraph } = require("devtools/client/shared/widgets/Graphs");

const { colorUtils } = require("devtools/shared/css/color");
const { getColor } = require("devtools/client/shared/theme");
const ProfilerGlobal = require("devtools/client/performance/modules/global");
const { MarkerBlueprintUtils } = require("devtools/client/performance/modules/marker-blueprint-utils");
const { TickUtils } = require("devtools/client/performance/modules/waterfall-ticks");
const { TIMELINE_BLUEPRINT } = require("devtools/client/performance/modules/markers");

const OVERVIEW_HEADER_HEIGHT = 14; // px
const OVERVIEW_ROW_HEIGHT = 11; // px

const OVERVIEW_SELECTION_LINE_COLOR = "#666";
const OVERVIEW_CLIPHEAD_LINE_COLOR = "#555";

const OVERVIEW_HEADER_TICKS_MULTIPLE = 100; // ms
const OVERVIEW_HEADER_TICKS_SPACING_MIN = 75; // px
const OVERVIEW_HEADER_TEXT_FONT_SIZE = 9; // px
const OVERVIEW_HEADER_TEXT_FONT_FAMILY = "sans-serif";
const OVERVIEW_HEADER_TEXT_PADDING_LEFT = 6; // px
const OVERVIEW_HEADER_TEXT_PADDING_TOP = 1; // px
const OVERVIEW_MARKER_WIDTH_MIN = 4; // px
const OVERVIEW_GROUP_VERTICAL_PADDING = 5; // px

/**
 * An overview for the markers data.
 *
 * @param nsIDOMNode parent
 *        The parent node holding the overview.
 * @param Array<String> filter
 *        List of names of marker types that should not be shown.
 */
function MarkersOverview(parent, filter = [], ...args) {
  AbstractCanvasGraph.apply(this, [parent, "markers-overview", ...args]);
  this.setTheme();
  this.setFilter(filter);
}

MarkersOverview.prototype = extend(AbstractCanvasGraph.prototype, {
  clipheadLineColor: OVERVIEW_CLIPHEAD_LINE_COLOR,
  selectionLineColor: OVERVIEW_SELECTION_LINE_COLOR,
  headerHeight: OVERVIEW_HEADER_HEIGHT,
  rowHeight: OVERVIEW_ROW_HEIGHT,
  groupPadding: OVERVIEW_GROUP_VERTICAL_PADDING,

  /**
   * Compute the height of the overview.
   */
  get fixedHeight() {
    return this.headerHeight + this.rowHeight * this._numberOfGroups;
  },

  /**
   * List of marker types that should not be shown in the graph.
   */
  setFilter: function (filter) {
    this._paintBatches = new Map();
    this._filter = filter;
    this._groupMap = Object.create(null);

    let observedGroups = new Set();

    for (let type in TIMELINE_BLUEPRINT) {
      if (filter.includes(type)) {
        continue;
      }
      this._paintBatches.set(type, { definition: TIMELINE_BLUEPRINT[type], batch: [] });
      observedGroups.add(TIMELINE_BLUEPRINT[type].group);
    }

    // Take our set of observed groups and order them and map
    // the group numbers to fill in the holes via `_groupMap`.
    // This normalizes our rows by removing rows that aren't used
    // if filters are enabled.
    let actualPosition = 0;
    for (let groupNumber of Array.from(observedGroups).sort()) {
      this._groupMap[groupNumber] = actualPosition++;
    }
    this._numberOfGroups = Object.keys(this._groupMap).length;
  },

  /**
   * Disables selection and empties this graph.
   */
  clearView: function () {
    this.selectionEnabled = false;
    this.dropSelection();
    this.setData({ duration: 0, markers: [] });
  },

  /**
   * Renders the graph's data source.
   * @see AbstractCanvasGraph.prototype.buildGraphImage
   */
  buildGraphImage: function () {
    let { markers, duration } = this._data;

    let { canvas, ctx } = this._getNamedCanvas("markers-overview-data");
    let canvasWidth = this._width;
    let canvasHeight = this._height;

    // Group markers into separate paint batches. This is necessary to
    // draw all markers sharing the same style at once.
    for (let marker of markers) {
      // Again skip over markers that we're filtering -- we don't want them
      // to be labeled as "Unknown"
      if (!MarkerBlueprintUtils.shouldDisplayMarker(marker, this._filter)) {
        continue;
      }

      let markerType = this._paintBatches.get(marker.name) ||
                                              this._paintBatches.get("UNKNOWN");
      markerType.batch.push(marker);
    }

    // Calculate each row's height, and the time-based scaling.

    let groupHeight = this.rowHeight * this._pixelRatio;
    let groupPadding = this.groupPadding * this._pixelRatio;
    let headerHeight = this.headerHeight * this._pixelRatio;
    let dataScale = this.dataScaleX = canvasWidth / duration;

    // Draw the header and overview background.

    ctx.fillStyle = this.headerBackgroundColor;
    ctx.fillRect(0, 0, canvasWidth, headerHeight);

    ctx.fillStyle = this.backgroundColor;
    ctx.fillRect(0, headerHeight, canvasWidth, canvasHeight);

    // Draw the alternating odd/even group backgrounds.

    ctx.fillStyle = this.alternatingBackgroundColor;
    ctx.beginPath();

    for (let i = 0; i < this._numberOfGroups; i += 2) {
      let top = headerHeight + i * groupHeight;
      ctx.rect(0, top, canvasWidth, groupHeight);
    }

    ctx.fill();

    // Draw the timeline header ticks.

    let fontSize = OVERVIEW_HEADER_TEXT_FONT_SIZE * this._pixelRatio;
    let fontFamily = OVERVIEW_HEADER_TEXT_FONT_FAMILY;
    let textPaddingLeft = OVERVIEW_HEADER_TEXT_PADDING_LEFT * this._pixelRatio;
    let textPaddingTop = OVERVIEW_HEADER_TEXT_PADDING_TOP * this._pixelRatio;

    let tickInterval = TickUtils.findOptimalTickInterval({
      ticksMultiple: OVERVIEW_HEADER_TICKS_MULTIPLE,
      ticksSpacingMin: OVERVIEW_HEADER_TICKS_SPACING_MIN,
      dataScale: dataScale
    });

    ctx.textBaseline = "middle";
    ctx.font = fontSize + "px " + fontFamily;
    ctx.fillStyle = this.headerTextColor;
    ctx.strokeStyle = this.headerTimelineStrokeColor;
    ctx.beginPath();

    for (let x = 0; x < canvasWidth; x += tickInterval) {
      let lineLeft = x;
      let textLeft = lineLeft + textPaddingLeft;
      let time = Math.round(x / dataScale);
      let label = ProfilerGlobal.L10N.getFormatStr("timeline.tick", time);
      ctx.fillText(label, textLeft, headerHeight / 2 + textPaddingTop);
      ctx.moveTo(lineLeft, 0);
      ctx.lineTo(lineLeft, canvasHeight);
    }

    ctx.stroke();

    // Draw the timeline markers.

    for (let [, { definition, batch }] of this._paintBatches) {
      let group = this._groupMap[definition.group];
      let top = headerHeight + group * groupHeight + groupPadding / 2;
      let height = groupHeight - groupPadding;

      let color = getColor(definition.colorName, this.theme);
      ctx.fillStyle = color;
      ctx.beginPath();

      for (let { start, end } of batch) {
        let left = start * dataScale;
        let width = Math.max((end - start) * dataScale, OVERVIEW_MARKER_WIDTH_MIN);
        ctx.rect(left, top, width, height);
      }

      ctx.fill();

      // Since all the markers in this batch (thus sharing the same style) have
      // been drawn, empty it. The next time new markers will be available,
      // they will be sorted and drawn again.
      batch.length = 0;
    }

    return canvas;
  },

  /**
   * Sets the theme via `theme` to either "light" or "dark",
   * and updates the internal styling to match. Requires a redraw
   * to see the effects.
   */
  setTheme: function (theme) {
    this.theme = theme = theme || "light";
    this.backgroundColor = getColor("body-background", theme);
    this.selectionBackgroundColor = colorUtils.setAlpha(
      getColor("selection-background", theme), 0.25);
    this.selectionStripesColor = colorUtils.setAlpha("#fff", 0.1);
    this.headerBackgroundColor = getColor("body-background", theme);
    this.headerTextColor = getColor("body-color", theme);
    this.headerTimelineStrokeColor = colorUtils.setAlpha(
      getColor("body-color-alt", theme), 0.25);
    this.alternatingBackgroundColor = colorUtils.setAlpha(
      getColor("body-color", theme), 0.05);
  }
});

exports.MarkersOverview = MarkersOverview;
PK
!<��á�8�8Pchrome/devtools/modules/devtools/client/performance/modules/widgets/tree-view.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 contains the tree view, displaying all the samples and frames
 * received from the proviler in a tree-like structure.
 */

const { L10N } = require("devtools/client/performance/modules/global");
const { extend } = require("devtools/shared/extend");
const { AbstractTreeItem } = require("resource://devtools/client/shared/widgets/AbstractTreeItem.jsm");

const URL_LABEL_TOOLTIP = L10N.getStr("table.url.tooltiptext");
const VIEW_OPTIMIZATIONS_TOOLTIP = L10N.getStr("table.view-optimizations.tooltiptext2");

const CALL_TREE_INDENTATION = 16; // px

// Used for rendering values in cells
const FORMATTERS = {
  TIME: (value) => L10N.getFormatStr("table.ms2", L10N.numberWithDecimals(value, 2)),
  PERCENT: (value) => L10N.getFormatStr("table.percentage3",
                                        L10N.numberWithDecimals(value, 2)),
  NUMBER: (value) => value || 0,
  BYTESIZE: (value) => L10N.getFormatStr("table.bytes", (value || 0))
};

/**
 * Definitions for rendering cells. Triads of class name, property name from
 * `frame.getInfo()`, and a formatter function.
 */
const CELLS = {
  duration: ["duration", "totalDuration", FORMATTERS.TIME],
  percentage: ["percentage", "totalPercentage", FORMATTERS.PERCENT],
  selfDuration: ["self-duration", "selfDuration", FORMATTERS.TIME],
  selfPercentage: ["self-percentage", "selfPercentage", FORMATTERS.PERCENT],
  samples: ["samples", "samples", FORMATTERS.NUMBER],

  selfSize: ["self-size", "selfSize", FORMATTERS.BYTESIZE],
  selfSizePercentage: ["self-size-percentage", "selfSizePercentage", FORMATTERS.PERCENT],
  selfCount: ["self-count", "selfCount", FORMATTERS.NUMBER],
  selfCountPercentage: ["self-count-percentage", "selfCountPercentage",
                        FORMATTERS.PERCENT],
  size: ["size", "totalSize", FORMATTERS.BYTESIZE],
  sizePercentage: ["size-percentage", "totalSizePercentage", FORMATTERS.PERCENT],
  count: ["count", "totalCount", FORMATTERS.NUMBER],
  countPercentage: ["count-percentage", "totalCountPercentage", FORMATTERS.PERCENT],
};
const CELL_TYPES = Object.keys(CELLS);

const DEFAULT_SORTING_PREDICATE = (frameA, frameB) => {
  let dataA = frameA.getDisplayedData();
  let dataB = frameB.getDisplayedData();
  let isAllocations = "totalSize" in dataA;

  if (isAllocations) {
    if (this.inverted && dataA.selfSize !== dataB.selfSize) {
      return dataA.selfSize < dataB.selfSize ? 1 : -1;
    }
    return dataA.totalSize < dataB.totalSize ? 1 : -1;
  }

  if (this.inverted && dataA.selfPercentage !== dataB.selfPercentage) {
    return dataA.selfPercentage < dataB.selfPercentage ? 1 : -1;
  }
  return dataA.totalPercentage < dataB.totalPercentage ? 1 : -1;
};

// depth
const DEFAULT_AUTO_EXPAND_DEPTH = 3;
const DEFAULT_VISIBLE_CELLS = {
  duration: true,
  percentage: true,
  selfDuration: true,
  selfPercentage: true,
  samples: true,
  function: true,

  // allocation columns
  count: false,
  selfCount: false,
  size: false,
  selfSize: false,
  countPercentage: false,
  selfCountPercentage: false,
  sizePercentage: false,
  selfSizePercentage: false,
};

/**
 * An item in a call tree view, which looks like this:
 *
 *   Time (ms)  |   Cost   | Calls | Function
 * ============================================================================
 *     1,000.00 |  100.00% |       | ▼ (root)
 *       500.12 |   50.01% |   300 |   ▼ foo                          Categ. 1
 *       300.34 |   30.03% |  1500 |     ▼ bar                        Categ. 2
 *        10.56 |    0.01% |    42 |       ▶ call_with_children       Categ. 3
 *        90.78 |    0.09% |    25 |         call_without_children    Categ. 4
 *
 * Every instance of a `CallView` represents a row in the call tree. The same
 * parent node is used for all rows.
 *
 * @param CallView caller
 *        The CallView considered the "caller" frame. This newly created
 *        instance will be represent the "callee". Should be null for root nodes.
 * @param ThreadNode | FrameNode frame
 *        Details about this function, like { samples, duration, calls } etc.
 * @param number level [optional]
 *        The indentation level in the call tree. The root node is at level 0.
 * @param boolean hidden [optional]
 *        Whether this node should be hidden and not contribute to depth/level
 *        calculations. Defaults to false.
 * @param boolean inverted [optional]
 *        Whether the call tree has been inverted (bottom up, rather than
 *        top-down). Defaults to false.
 * @param function sortingPredicate [optional]
 *        The predicate used to sort the tree items when created. Defaults to
 *        the caller's `sortingPredicate` if a caller exists, otherwise defaults
 *        to DEFAULT_SORTING_PREDICATE. The two passed arguments are FrameNodes.
 * @param number autoExpandDepth [optional]
 *        The depth to which the tree should automatically expand. Defualts to
 *        the caller's `autoExpandDepth` if a caller exists, otherwise defaults
 *        to DEFAULT_AUTO_EXPAND_DEPTH.
 * @param object visibleCells
 *        An object specifying which cells are visible in the tree. Defaults to
 *        the caller's `visibleCells` if a caller exists, otherwise defaults
 *        to DEFAULT_VISIBLE_CELLS.
 * @param boolean showOptimizationHint [optional]
 *        Whether or not to show an icon indicating if the frame has optimization
 *        data.
 */
function CallView({
  caller, frame, level, hidden, inverted,
  sortingPredicate, autoExpandDepth, visibleCells,
  showOptimizationHint
}) {
  AbstractTreeItem.call(this, {
    parent: caller,
    level: level | 0 - (hidden ? 1 : 0)
  });

  if (sortingPredicate != null) {
    this.sortingPredicate = sortingPredicate;
  } else if (caller) {
    this.sortingPredicate = caller.sortingPredicate;
  } else {
    this.sortingPredicate = DEFAULT_SORTING_PREDICATE;
  }

  if (autoExpandDepth != null) {
    this.autoExpandDepth = autoExpandDepth;
  } else if (caller) {
    this.autoExpandDepth = caller.autoExpandDepth;
  } else {
    this.autoExpandDepth = DEFAULT_AUTO_EXPAND_DEPTH;
  }

  if (visibleCells != null) {
    this.visibleCells = visibleCells;
  } else if (caller) {
    this.visibleCells = caller.visibleCells;
  } else {
    this.visibleCells = Object.create(DEFAULT_VISIBLE_CELLS);
  }

  this.caller = caller;
  this.frame = frame;
  this.hidden = hidden;
  this.inverted = inverted;
  this.showOptimizationHint = showOptimizationHint;

  this._onUrlClick = this._onUrlClick.bind(this);
}

CallView.prototype = extend(AbstractTreeItem.prototype, {
  /**
   * Creates the view for this tree node.
   * @param nsIDOMNode document
   * @param nsIDOMNode arrowNode
   * @return nsIDOMNode
   */
  _displaySelf: function (document, arrowNode) {
    let frameInfo = this.getDisplayedData();
    let cells = [];

    for (let type of CELL_TYPES) {
      if (this.visibleCells[type]) {
        // Inline for speed, but pass in the formatted value via
        // cell definition, as well as the element type.
        cells.push(this._createCell(document, CELLS[type][2](frameInfo[CELLS[type][1]]),
                                    CELLS[type][0]));
      }
    }

    if (this.visibleCells.function) {
      cells.push(this._createFunctionCell(document, arrowNode, frameInfo.name, frameInfo,
                                          this.level));
    }

    let targetNode = document.createElement("hbox");
    targetNode.className = "call-tree-item";
    targetNode.setAttribute("origin", frameInfo.isContent ? "content" : "chrome");
    targetNode.setAttribute("category", frameInfo.categoryData.abbrev || "");
    targetNode.setAttribute("tooltiptext", frameInfo.tooltiptext);

    if (this.hidden) {
      targetNode.style.display = "none";
    }

    for (let i = 0; i < cells.length; i++) {
      targetNode.appendChild(cells[i]);
    }

    return targetNode;
  },

  /**
   * Populates this node in the call tree with the corresponding "callees".
   * These are defined in the `frame` data source for this call view.
   * @param array:AbstractTreeItem children
   */
  _populateSelf: function (children) {
    let newLevel = this.level + 1;

    for (let newFrame of this.frame.calls) {
      children.push(new CallView({
        caller: this,
        frame: newFrame,
        level: newLevel,
        inverted: this.inverted
      }));
    }

    // Sort the "callees" asc. by samples, before inserting them in the tree,
    // if no other sorting predicate was specified on this on the root item.
    children.sort(this.sortingPredicate.bind(this));
  },

  /**
   * Functions creating each cell in this call view.
   * Invoked by `_displaySelf`.
   */
  _createCell: function (doc, value, type) {
    let cell = doc.createElement("description");
    cell.className = "plain call-tree-cell";
    cell.setAttribute("type", type);
    cell.setAttribute("crop", "end");
    // Add a tabulation to the cell text in case it's is selected and copied.
    cell.textContent = value + "\t";
    return cell;
  },

  _createFunctionCell: function (doc, arrowNode, frameName, frameInfo, frameLevel) {
    let cell = doc.createElement("hbox");
    cell.className = "call-tree-cell";
    cell.style.marginInlineStart = (frameLevel * CALL_TREE_INDENTATION) + "px";
    cell.setAttribute("type", "function");
    cell.appendChild(arrowNode);

    // Render optimization hint if this frame has opt data.
    if (this.root.showOptimizationHint && frameInfo.hasOptimizations &&
        !frameInfo.isMetaCategory) {
      let icon = doc.createElement("description");
      icon.setAttribute("tooltiptext", VIEW_OPTIMIZATIONS_TOOLTIP);
      icon.className = "opt-icon";
      cell.appendChild(icon);
    }

    // Don't render a name label node if there's no function name. A different
    // location label node will be rendered instead.
    if (frameName) {
      let nameNode = doc.createElement("description");
      nameNode.className = "plain call-tree-name";
      nameNode.textContent = frameName;
      cell.appendChild(nameNode);
    }

    // Don't render detailed labels for meta category frames
    if (!frameInfo.isMetaCategory) {
      this._appendFunctionDetailsCells(doc, cell, frameInfo);
    }

    // Don't render an expando-arrow for leaf nodes.
    let hasDescendants = Object.keys(this.frame.calls).length > 0;
    if (!hasDescendants) {
      arrowNode.setAttribute("invisible", "");
    }

    // Add a line break to the last description of the row in case it's selected
    // and copied.
    let lastDescription = cell.querySelector("description:last-of-type");
    lastDescription.textContent = lastDescription.textContent + "\n";

    // Add spaces as frameLevel indicators in case the row is selected and
    // copied. These spaces won't be displayed in the cell content.
    let firstDescription = cell.querySelector("description:first-of-type");
    let levelIndicator = frameLevel > 0 ? " ".repeat(frameLevel) : "";
    firstDescription.textContent = levelIndicator + firstDescription.textContent;

    return cell;
  },

  _appendFunctionDetailsCells: function (doc, cell, frameInfo) {
    if (frameInfo.fileName) {
      let urlNode = doc.createElement("description");
      urlNode.className = "plain call-tree-url";
      urlNode.textContent = frameInfo.fileName;
      urlNode.setAttribute("tooltiptext", URL_LABEL_TOOLTIP + " → " + frameInfo.url);
      urlNode.addEventListener("mousedown", this._onUrlClick);
      cell.appendChild(urlNode);
    }

    if (frameInfo.line) {
      let lineNode = doc.createElement("description");
      lineNode.className = "plain call-tree-line";
      lineNode.textContent = ":" + frameInfo.line;
      cell.appendChild(lineNode);
    }

    if (frameInfo.column) {
      let columnNode = doc.createElement("description");
      columnNode.className = "plain call-tree-column";
      columnNode.textContent = ":" + frameInfo.column;
      cell.appendChild(columnNode);
    }

    if (frameInfo.host) {
      let hostNode = doc.createElement("description");
      hostNode.className = "plain call-tree-host";
      hostNode.textContent = frameInfo.host;
      cell.appendChild(hostNode);
    }

    if (frameInfo.categoryData.label) {
      let categoryNode = doc.createElement("description");
      categoryNode.className = "plain call-tree-category";
      categoryNode.style.color = frameInfo.categoryData.color;
      categoryNode.textContent = frameInfo.categoryData.label;
      cell.appendChild(categoryNode);
    }
  },

  /**
   * Gets the data displayed about this tree item, based on the FrameNode
   * model associated with this view.
   *
   * @return object
   */
  getDisplayedData: function () {
    if (this._cachedDisplayedData) {
      return this._cachedDisplayedData;
    }

    this._cachedDisplayedData = this.frame.getInfo({
      root: this.root.frame,
      allocations: (this.visibleCells.count || this.visibleCells.selfCount)
    });

    return this._cachedDisplayedData;

    /**
     * When inverting call tree, the costs and times are dependent on position
     * in the tree. We must only count leaf nodes with self cost, and total costs
     * dependent on how many times the leaf node was found with a full stack path.
     *
     *   Total |  Self | Calls | Function
     * ============================================================================
     *  100%   |  100% |   100 | ▼ C
     *   50%   |   0%  |    50 |   ▼ B
     *   50%   |   0%  |    50 |     ▼ A
     *   50%   |   0%  |    50 |   ▼ B
     *
     * Every instance of a `CallView` represents a row in the call tree. The same
     * container node is used for all rows.
     */
  },

  /**
   * Toggles the category information hidden or visible.
   * @param boolean visible
   */
  toggleCategories: function (visible) {
    if (!visible) {
      this.container.setAttribute("categories-hidden", "");
    } else {
      this.container.removeAttribute("categories-hidden");
    }
  },

  /**
   * Handler for the "click" event on the url node of this call view.
   */
  _onUrlClick: function (e) {
    e.preventDefault();
    e.stopPropagation();
    // Only emit for left click events
    if (e.button === 0) {
      this.root.emit("link", this);
    }
  },
});

exports.CallView = CallView;
PK
!<ᕮ��<chrome/devtools/modules/devtools/client/performance/panel.js/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 { Task } = require("devtools/shared/task");
const defer = require("devtools/shared/defer");

loader.lazyRequireGetter(this, "EventEmitter",
  "devtools/shared/old-event-emitter");

function PerformancePanel(iframeWindow, toolbox) {
  this.panelWin = iframeWindow;
  this.toolbox = toolbox;

  EventEmitter.decorate(this);
}

exports.PerformancePanel = PerformancePanel;

PerformancePanel.prototype = {
  /**
   * Open is effectively an asynchronous constructor.
   *
   * @return object
   *         A promise that is resolved when the Performance tool
   *         completes opening.
   */
  open: Task.async(function* () {
    if (this._opening) {
      return this._opening;
    }
    let deferred = defer();
    this._opening = deferred.promise;

    this.panelWin.gToolbox = this.toolbox;
    this.panelWin.gTarget = this.target;
    this._checkRecordingStatus = this._checkRecordingStatus.bind(this);

    // Actor is already created in the toolbox; reuse
    // the same front, and the toolbox will also initialize the front,
    // but redo it here so we can hook into the same event to prevent race conditions
    // in the case of the front still being in the process of opening.
    let front = yield this.panelWin.gToolbox.initPerformance();

    // This should only happen if this is completely unsupported (when profiler
    // does not exist), and in that case, the tool shouldn't be available,
    // so let's ensure this assertion.
    if (!front) {
      console.error("No PerformanceFront found in toolbox.");
    }

    this.panelWin.gFront = front;
    let { PerformanceController, EVENTS } = this.panelWin;
    PerformanceController.on(EVENTS.RECORDING_ADDED, this._checkRecordingStatus);
    PerformanceController.on(EVENTS.RECORDING_STATE_CHANGE, this._checkRecordingStatus);
    yield this.panelWin.startupPerformance();

    // Fire this once incase we have an in-progress recording (console profile)
    // that caused this start up, and no state change yet, so we can highlight the
    // tab if we need.
    this._checkRecordingStatus();

    this.isReady = true;
    this.emit("ready");

    deferred.resolve(this);
    return this._opening;
  }),

  // DevToolPanel API

  get target() {
    return this.toolbox.target;
  },

  destroy: Task.async(function* () {
    // Make sure this panel is not already destroyed.
    if (this._destroyed) {
      return;
    }

    let { PerformanceController, EVENTS } = this.panelWin;
    PerformanceController.off(EVENTS.RECORDING_ADDED, this._checkRecordingStatus);
    PerformanceController.off(EVENTS.RECORDING_STATE_CHANGE, this._checkRecordingStatus);
    yield this.panelWin.shutdownPerformance();
    this.emit("destroyed");
    this._destroyed = true;
  }),

  _checkRecordingStatus: function () {
    if (this.panelWin.PerformanceController.isRecording()) {
      this.toolbox.highlightTool("performance");
    } else {
      this.toolbox.unhighlightTool("performance");
    }
  }
};
PK
!<]�<4��Kchrome/devtools/modules/devtools/client/performance/test/helpers/actions.js/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";

const { Constants } = require("devtools/client/performance/modules/constants");
const { once, times } = require("devtools/client/performance/test/helpers/event-utils");
const { waitUntil } = require("devtools/client/performance/test/helpers/wait-utils");

/**
 * Starts a manual recording in the given performance tool panel and
 * waits for it to finish starting.
 */
exports.startRecording = function (panel, options = {}) {
  let controller = panel.panelWin.PerformanceController;

  return Promise.all([
    controller.startRecording(),
    exports.waitForRecordingStartedEvents(panel, options)
  ]);
};

/**
 * Stops the latest recording in the given performance tool panel and
 * waits for it to finish stopping.
 */
exports.stopRecording = function (panel, options = {}) {
  let controller = panel.panelWin.PerformanceController;

  return Promise.all([
    controller.stopRecording(),
    exports.waitForRecordingStoppedEvents(panel, options)
  ]);
};

/**
 * Waits for all the necessary events to be emitted after a recording starts.
 */
exports.waitForRecordingStartedEvents = function (panel, options = {}) {
  options.expectedViewState = options.expectedViewState || /^(console-)?recording$/;

  let EVENTS = panel.panelWin.EVENTS;
  let controller = panel.panelWin.PerformanceController;
  let view = panel.panelWin.PerformanceView;
  let overview = panel.panelWin.OverviewView;

  return Promise.all([
    options.skipWaitingForBackendReady
      ? null
      : once(controller, EVENTS.BACKEND_READY_AFTER_RECORDING_START),
    options.skipWaitingForRecordingStarted
      ? null
      : once(controller, EVENTS.RECORDING_STATE_CHANGE, {
        expectedArgs: { "1": "recording-started" }
      }),
    options.skipWaitingForViewState
      ? null
      : once(view, EVENTS.UI_STATE_CHANGED, {
        expectedArgs: { "1": options.expectedViewState }
      }),
    options.skipWaitingForOverview
      ? null
      : once(overview, EVENTS.UI_OVERVIEW_RENDERED, {
        expectedArgs: { "1": Constants.FRAMERATE_GRAPH_LOW_RES_INTERVAL }
      }),
  ]);
};

/**
 * Waits for all the necessary events to be emitted after a recording finishes.
 */
exports.waitForRecordingStoppedEvents = function (panel, options = {}) {
  options.expectedViewClass = options.expectedViewClass || "WaterfallView";
  options.expectedViewEvent = options.expectedViewEvent || "UI_WATERFALL_RENDERED";
  options.expectedViewState = options.expectedViewState || "recorded";

  let EVENTS = panel.panelWin.EVENTS;
  let controller = panel.panelWin.PerformanceController;
  let view = panel.panelWin.PerformanceView;
  let overview = panel.panelWin.OverviewView;
  let subview = panel.panelWin[options.expectedViewClass];

  return Promise.all([
    options.skipWaitingForBackendReady
      ? null
      : once(controller, EVENTS.BACKEND_READY_AFTER_RECORDING_STOP),
    options.skipWaitingForRecordingStop
      ? null
      : once(controller, EVENTS.RECORDING_STATE_CHANGE, {
        expectedArgs: { "1": "recording-stopping" }
      }),
    options.skipWaitingForRecordingStop
      ? null
      : once(controller, EVENTS.RECORDING_STATE_CHANGE, {
        expectedArgs: { "1": "recording-stopped" }
      }),
    options.skipWaitingForViewState
      ? null
      : once(view, EVENTS.UI_STATE_CHANGED, {
        expectedArgs: { "1": options.expectedViewState }
      }),
    options.skipWaitingForOverview
      ? null
      : once(overview, EVENTS.UI_OVERVIEW_RENDERED, {
        expectedArgs: { "1": Constants.FRAMERATE_GRAPH_HIGH_RES_INTERVAL }
      }),
    options.skipWaitingForSubview
      ? null
      : once(subview, EVENTS[options.expectedViewEvent]),
  ]);
};

/**
 * Waits for rendering to happen once on all the performance tool's widgets.
 */
exports.waitForAllWidgetsRendered = (panel) => {
  let { panelWin } = panel;
  let { EVENTS } = panelWin;

  return Promise.all([
    once(panelWin.OverviewView, EVENTS.UI_MARKERS_GRAPH_RENDERED),
    once(panelWin.OverviewView, EVENTS.UI_MEMORY_GRAPH_RENDERED),
    once(panelWin.OverviewView, EVENTS.UI_FRAMERATE_GRAPH_RENDERED),
    once(panelWin.OverviewView, EVENTS.UI_OVERVIEW_RENDERED),
    once(panelWin.WaterfallView, EVENTS.UI_WATERFALL_RENDERED),
    once(panelWin.JsCallTreeView, EVENTS.UI_JS_CALL_TREE_RENDERED),
    once(panelWin.JsFlameGraphView, EVENTS.UI_JS_FLAMEGRAPH_RENDERED),
    once(panelWin.MemoryCallTreeView, EVENTS.UI_MEMORY_CALL_TREE_RENDERED),
    once(panelWin.MemoryFlameGraphView, EVENTS.UI_MEMORY_FLAMEGRAPH_RENDERED)
  ]);
};

/**
 * Waits for rendering to happen on the performance tool's overview graph,
 * making sure some markers were also rendered.
 */
exports.waitForOverviewRenderedWithMarkers = (panel, minTimes = 3, minMarkers = 1) => {
  let { EVENTS, OverviewView, PerformanceController } = panel.panelWin;

  return Promise.all([
    times(OverviewView, EVENTS.UI_OVERVIEW_RENDERED, minTimes, {
      expectedArgs: { "1": Constants.FRAMERATE_GRAPH_LOW_RES_INTERVAL }
    }),
    waitUntil(() =>
      PerformanceController.getCurrentRecording().getMarkers().length >= minMarkers
    ),
  ]);
};

/**
 * Reloads the given tab target.
 */
exports.reload = (target) => {
  target.activeTab.reload();
  return once(target, "navigate");
};
PK
!<�I�Y��Mchrome/devtools/modules/devtools/client/performance/test/helpers/dom-utils.js/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";

const Services = require("Services");
const { waitForMozAfterPaint } = require("devtools/client/performance/test/helpers/wait-utils");

/**
 * Checks if a DOM node is considered visible.
 */
exports.isVisible = (element) => {
  return !element.classList.contains("hidden") && !element.hidden;
};

/**
 * Appends the provided element to the provided parent node. If run in e10s
 * mode, will also wait for MozAfterPaint to make sure the tab is rendered.
 * Should be reviewed if Bug 1240509 lands.
 */
exports.appendAndWaitForPaint = function (parent, element) {
  let isE10s = Services.appinfo.browserTabsRemoteAutostart;
  if (isE10s) {
    let win = parent.ownerDocument.defaultView;
    let onMozAfterPaint = waitForMozAfterPaint(win);
    parent.appendChild(element);
    return onMozAfterPaint;
  }
  parent.appendChild(element);
  return null;
};
PK
!<[�6}}Ochrome/devtools/modules/devtools/client/performance/test/helpers/event-utils.js/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";

/* globals dump */

const Services = require("Services");

const KNOWN_EE_APIS = [
  ["on", "off"],
  ["addEventListener", "removeEventListener"],
  ["addListener", "removeListener"]
];

/**
 * Listens for any event for a single time on a target, no matter what kind of
 * event emitter it is, returning a promise resolved with the passed arguments
 * once the event is fired.
 */
exports.once = function (target, eventName, options = {}) {
  return exports.times(target, eventName, 1, options);
};

/**
 * Waits for any event to be fired a specified amount of times on a target, no
 * matter what kind of event emitter.
 * Possible options: `useCapture`, `spreadArgs`, `expectedArgs`
 */
exports.times = function (target, eventName, receiveCount, options = {}) {
  let msg = `Waiting for event: '${eventName}' on ${target} for ${receiveCount} time(s)`;
  if ("expectedArgs" in options) {
    dump(`${msg} with arguments: ${JSON.stringify(options.expectedArgs)}.\n`);
  } else {
    dump(`${msg}.\n`);
  }

  return new Promise((resolve, reject) => {
    if (typeof eventName != "string") {
      reject(new Error(`Unexpected event name: ${eventName}.`));
    }

    let API = KNOWN_EE_APIS.find(([a, r]) => (a in target) && (r in target));
    if (!API) {
      reject(new Error("Target is not a supported event listener."));
      return;
    }

    let [add, remove] = API;

    target[add](eventName, function onEvent(...args) {
      if ("expectedArgs" in options) {
        for (let index of Object.keys(options.expectedArgs)) {
          if (
            // Expected argument matches this regexp.
            (options.expectedArgs[index] instanceof RegExp &&
             !options.expectedArgs[index].exec(args[index])) ||
            // Expected argument is not a regexp and equal to the received arg.
            (!(options.expectedArgs[index] instanceof RegExp) &&
             options.expectedArgs[index] != args[index])
          ) {
            dump(`Ignoring event '${eventName}' with unexpected argument at index ` +
                 `${index}: ${args[index]}\n`);
            return;
          }
        }
      }
      if (--receiveCount > 0) {
        dump(`Event: '${eventName}' on ${target} needs to be fired ${receiveCount} ` +
             `more time(s).\n`);
      } else if (!receiveCount) {
        dump(`Event: '${eventName}' on ${target} received.\n`);
        target[remove](eventName, onEvent, options.useCapture);
        resolve(options.spreadArgs ? args : args[0]);
      }
    }, options.useCapture);
  });
};

/**
 * Like `once`, but for observer notifications.
 */
exports.observeOnce = function (notificationName, options = {}) {
  return exports.observeTimes(notificationName, 1, options);
};

/**
 * Like `times`, but for observer notifications.
 * Possible options: `expectedSubject`
 */
exports.observeTimes = function (notificationName, receiveCount, options = {}) {
  dump(`Waiting for notification: '${notificationName}' for ${receiveCount} time(s).\n`);

  return new Promise((resolve, reject) => {
    if (typeof notificationName != "string") {
      reject(new Error(`Unexpected notification name: ${notificationName}.`));
    }

    Services.obs.addObserver(function onObserve(subject, topic, data) {
      if ("expectedSubject" in options && options.expectedSubject != subject) {
        dump(`Ignoring notification '${notificationName}' with unexpected subject: ` +
             `${subject}\n`);
        return;
      }
      if (--receiveCount > 0) {
        dump(`Notification: '${notificationName}' needs to be fired ${receiveCount} ` +
             `more time(s).\n`);
      } else if (!receiveCount) {
        dump(`Notification: '${notificationName}' received.\n`);
        Services.obs.removeObserver(onObserve, topic);
        resolve(data);
      }
    }, notificationName);
  });
};
PK
!<�Q*��Ochrome/devtools/modules/devtools/client/performance/test/helpers/input-utils.js/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";

exports.HORIZONTAL_AXIS = 1;
exports.VERTICAL_AXIS = 2;

/**
 * Simulates a command event on an element.
 */
exports.command = (node) => {
  let ev = node.ownerDocument.createEvent("XULCommandEvent");
  ev.initCommandEvent("command", true, true, node.ownerDocument.defaultView, 0, false,
                      false, false, false, null, 0);
  node.dispatchEvent(ev);
};

/**
 * Simulates a click event on a devtools canvas graph.
 */
exports.clickCanvasGraph = (graph, { x, y }) => {
  x = x || 0;
  y = y || 0;
  x /= graph._window.devicePixelRatio;
  y /= graph._window.devicePixelRatio;
  graph._onMouseMove({ testX: x, testY: y });
  graph._onMouseDown({ testX: x, testY: y });
  graph._onMouseUp({ testX: x, testY: y });
};

/**
 * Simulates a drag start event on a devtools canvas graph.
 */
exports.dragStartCanvasGraph = (graph, { x, y }) => {
  x = x || 0;
  y = y || 0;
  x /= graph._window.devicePixelRatio;
  y /= graph._window.devicePixelRatio;
  graph._onMouseMove({ testX: x, testY: y });
  graph._onMouseDown({ testX: x, testY: y });
};

/**
 * Simulates a drag stop event on a devtools canvas graph.
 */
exports.dragStopCanvasGraph = (graph, { x, y }) => {
  x = x || 0;
  y = y || 0;
  x /= graph._window.devicePixelRatio;
  y /= graph._window.devicePixelRatio;
  graph._onMouseMove({ testX: x, testY: y });
  graph._onMouseUp({ testX: x, testY: y });
};

/**
 * Simulates a scroll event on a devtools canvas graph.
 */
exports.scrollCanvasGraph = (graph, { axis, wheel, x, y }) => {
  x = x || 1;
  y = y || 1;
  x /= graph._window.devicePixelRatio;
  y /= graph._window.devicePixelRatio;
  graph._onMouseMove({
    testX: x,
    testY: y
  });
  graph._onMouseWheel({
    testX: x,
    testY: y,
    axis: axis,
    detail: wheel,
    HORIZONTAL_AXIS: exports.HORIZONTAL_AXIS,
    VERTICAL_AXIS: exports.VERTICAL_AXIS
  });
};
PK
!<�n��Ochrome/devtools/modules/devtools/client/performance/test/helpers/panel-utils.js/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";

/* globals dump */

const { gDevTools } = require("devtools/client/framework/devtools");
const { TargetFactory } = require("devtools/client/framework/target");
const { addTab, removeTab } = require("devtools/client/performance/test/helpers/tab-utils");
const { once } = require("devtools/client/performance/test/helpers/event-utils");

/**
 * Initializes a toolbox panel in a new tab.
 */
exports.initPanelInNewTab = function* ({ tool, url, win }, options = {}) {
  let tab = yield addTab({ url, win }, options);
  return (yield exports.initPanelInTab({ tool, tab }));
};

/**
 * Initializes a toolbox panel in the specified tab.
 */
exports.initPanelInTab = function* ({ tool, tab }) {
  dump(`Initializing a ${tool} panel.\n`);

  let target = TargetFactory.forTab(tab);
  yield target.makeRemote();

  // Open a toolbox and wait for the connection to the performance actors
  // to be opened. This is necessary because of the WebConsole's
  // `profile` and `profileEnd` methods.
  let toolbox = yield gDevTools.showToolbox(target, tool);
  yield toolbox.initPerformance();

  let panel = toolbox.getCurrentPanel();
  return { target, toolbox, panel };
};

/**
 * Initializes a performance panel in a new tab.
 */
exports.initPerformanceInNewTab = function* ({ url, win }, options = {}) {
  let tab = yield addTab({ url, win }, options);
  return (yield exports.initPerformanceInTab({ tab }));
};

/**
 * Initializes a performance panel in the specified tab.
 */
exports.initPerformanceInTab = function* ({ tab }) {
  return (yield exports.initPanelInTab({
    tool: "performance",
    tab: tab
  }));
};

/**
 * Initializes a webconsole panel in a new tab.
 * Returns a console property that allows calls to `profile` and `profileEnd`.
 */
exports.initConsoleInNewTab = function* ({ url, win }, options = {}) {
  let tab = yield addTab({ url, win }, options);
  return (yield exports.initConsoleInTab({ tab }));
};

/**
 * Initializes a webconsole panel in the specified tab.
 * Returns a console property that allows calls to `profile` and `profileEnd`.
 */
exports.initConsoleInTab = function* ({ tab }) {
  let { target, toolbox, panel } = yield exports.initPanelInTab({
    tool: "webconsole",
    tab: tab
  });

  let consoleMethod = function* (method, label, event) {
    let recordingEventReceived = once(toolbox.performance, event);
    if (label === undefined) {
      yield panel.hud.jsterm.execute(`console.${method}()`);
    } else {
      yield panel.hud.jsterm.execute(`console.${method}("${label}")`);
    }
    yield recordingEventReceived;
  };

  let profile = function* (label) {
    return yield consoleMethod("profile", label, "recording-started");
  };

  let profileEnd = function* (label) {
    return yield consoleMethod("profileEnd", label, "recording-stopped");
  };

  return { target, toolbox, panel, console: { profile, profileEnd } };
};

/**
 * Tears down a toolbox panel and removes an associated tab.
 */
exports.teardownToolboxAndRemoveTab = function* (panel, options) {
  dump("Destroying panel.\n");

  let tab = panel.target.tab;
  yield panel.toolbox.destroy();
  yield removeTab(tab, options);
};
PK
!<��VIchrome/devtools/modules/devtools/client/performance/test/helpers/prefs.js/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";

const Services = require("Services");
const { Preferences } = require("resource://gre/modules/Preferences.jsm");

// Prefs to revert to default once tests finish. Keep these in sync with
// all the preferences defined in devtools/client/preferences/devtools.js.
exports.MEMORY_SAMPLE_PROB_PREF = "devtools.performance.memory.sample-probability";
exports.MEMORY_MAX_LOG_LEN_PREF = "devtools.performance.memory.max-log-length";
exports.PROFILER_BUFFER_SIZE_PREF = "devtools.performance.profiler.buffer-size";
exports.PROFILER_SAMPLE_RATE_PREF = "devtools.performance.profiler.sample-frequency-khz";

exports.UI_EXPERIMENTAL_PREF = "devtools.performance.ui.experimental";
exports.UI_INVERT_CALL_TREE_PREF = "devtools.performance.ui.invert-call-tree";
exports.UI_INVERT_FLAME_PREF = "devtools.performance.ui.invert-flame-graph";
exports.UI_FLATTEN_RECURSION_PREF = "devtools.performance.ui.flatten-tree-recursion";
exports.UI_SHOW_PLATFORM_DATA_PREF = "devtools.performance.ui.show-platform-data";
exports.UI_SHOW_IDLE_BLOCKS_PREF = "devtools.performance.ui.show-idle-blocks";
exports.UI_ENABLE_FRAMERATE_PREF = "devtools.performance.ui.enable-framerate";
exports.UI_ENABLE_MEMORY_PREF = "devtools.performance.ui.enable-memory";
exports.UI_ENABLE_ALLOCATIONS_PREF = "devtools.performance.ui.enable-allocations";
exports.UI_ENABLE_MEMORY_FLAME_CHART = "devtools.performance.ui.enable-memory-flame";

exports.DEFAULT_PREF_VALUES = [
  "devtools.debugger.log",
  "devtools.performance.enabled",
  "devtools.performance.timeline.hidden-markers",
  exports.MEMORY_SAMPLE_PROB_PREF,
  exports.MEMORY_MAX_LOG_LEN_PREF,
  exports.PROFILER_BUFFER_SIZE_PREF,
  exports.PROFILER_SAMPLE_RATE_PREF,
  exports.UI_EXPERIMENTAL_PREF,
  exports.UI_INVERT_CALL_TREE_PREF,
  exports.UI_INVERT_FLAME_PREF,
  exports.UI_FLATTEN_RECURSION_PREF,
  exports.UI_SHOW_PLATFORM_DATA_PREF,
  exports.UI_SHOW_IDLE_BLOCKS_PREF,
  exports.UI_ENABLE_FRAMERATE_PREF,
  exports.UI_ENABLE_MEMORY_PREF,
  exports.UI_ENABLE_ALLOCATIONS_PREF,
  exports.UI_ENABLE_MEMORY_FLAME_CHART,
  "devtools.performance.ui.show-jit-optimizations",
  "devtools.performance.ui.show-triggers-for-gc-types",
].reduce((prefValues, prefName) => {
  prefValues[prefName] = Preferences.get(prefName);
  return prefValues;
}, {});

/**
 * Invokes callback when a pref which is not in the `DEFAULT_PREF_VALUES` store
 * is changed. Returns a cleanup function.
 */
exports.whenUnknownPrefChanged = function (branch, callback) {
  function onObserve(subject, topic, data) {
    if (!(data in exports.DEFAULT_PREF_VALUES)) {
      callback(data);
    }
  }
  Services.prefs.addObserver(branch, onObserve);
  return () => Services.prefs.removeObserver(branch, onObserve);
};

/**
 * Reverts all known preferences to their default values.
 */
exports.rollbackPrefsToDefault = function () {
  for (let prefName of Object.keys(exports.DEFAULT_PREF_VALUES)) {
    Preferences.set(prefName, exports.DEFAULT_PREF_VALUES[prefName]);
  }
};
PK
!<�6�ZnnUchrome/devtools/modules/devtools/client/performance/test/helpers/profiler-mm-utils.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 following functions are used in testing to control and inspect
 * the nsIProfiler in child process content. These should be called from
 * the parent process.
 */

const { Cc, Ci } = require("chrome");
const { Task } = require("devtools/shared/task");

const FRAME_SCRIPT_UTILS_URL = "chrome://mochitests/content/browser/devtools/client/shared/test/frame-script-utils.js";

let gMM = null;

/**
 * Loads the relevant frame scripts into the provided browser's message manager.
 */
exports.pmmLoadFrameScripts = (gBrowser) => {
  gMM = gBrowser.selectedBrowser.messageManager;
  gMM.loadFrameScript(FRAME_SCRIPT_UTILS_URL, false);
};

/**
 * Clears the cached message manager.
 */
exports.pmmClearFrameScripts = () => {
  gMM = null;
};

/**
 * Sends a message to the message listener, attaching an id to the payload data.
 * Resolves a returned promise when the response is received from the message
 * listener, with the same id as part of the response payload data.
 */
exports.pmmUniqueMessage = function (message, payload) {
  if (!gMM) {
    throw new Error("`pmmLoadFrameScripts()` must be called when using MessageManager.");
  }

  let { generateUUID } = Cc["@mozilla.org/uuid-generator;1"]
    .getService(Ci.nsIUUIDGenerator);
  payload.id = generateUUID().toString();

  return new Promise(resolve => {
    gMM.addMessageListener(message + ":response", function onHandler({ data }) {
      if (payload.id == data.id) {
        gMM.removeMessageListener(message + ":response", onHandler);
        resolve(data.data);
      }
    });
    gMM.sendAsyncMessage(message, payload);
  });
};

/**
 * Checks if the nsProfiler module is active.
 */
exports.pmmIsProfilerActive = () => {
  return exports.pmmSendProfilerCommand("IsActive");
};

/**
 * Starts the nsProfiler module.
 */
exports.pmmStartProfiler = Task.async(function* ({ entries, interval, features }) {
  let isActive = (yield exports.pmmSendProfilerCommand("IsActive")).isActive;
  if (!isActive) {
    return exports.pmmSendProfilerCommand("StartProfiler", [entries, interval, features,
                                                            features.length]);
  }
  return null;
});
/**
 * Stops the nsProfiler module.
 */
exports.pmmStopProfiler = Task.async(function* () {
  let isActive = (yield exports.pmmSendProfilerCommand("IsActive")).isActive;
  if (isActive) {
    return exports.pmmSendProfilerCommand("StopProfiler");
  }
  return null;
});

/**
 * Calls a method on the nsProfiler module.
 */
exports.pmmSendProfilerCommand = (method, args = []) => {
  return exports.pmmUniqueMessage("devtools:test:profiler", { method, args });
};

/**
 * Evaluates a script in content, returning a promise resolved with the
 * returned result.
 */
exports.pmmEvalInDebuggee = (script) => {
  return exports.pmmUniqueMessage("devtools:test:eval", { script });
};

/**
 * Evaluates a console method in content.
 */
exports.pmmConsoleMethod = function (method, ...args) {
  // Terrible ugly hack -- this gets stringified when it uses the
  // message manager, so an undefined arg in `console.profileEnd()`
  // turns into a stringified "null", which is terrible. This method
  // is only used for test helpers, so swap out the argument if its undefined
  // with an empty string. Differences between empty string and undefined are
  // tested on the front itself.
  if (args[0] == null) {
    args[0] = "";
  }
  return exports.pmmUniqueMessage("devtools:test:console", { method, args });
};
PK
!<��=�11Schrome/devtools/modules/devtools/client/performance/test/helpers/recording-utils.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";

/**
 * These utilities provide a functional interface for accessing the particulars
 * about the recording's details.
 */

/**
 * Access the selected view from the panel's recording list.
 *
 * @param {object} panel - The current panel.
 * @return {object} The recording model.
 */
exports.getSelectedRecording = function (panel) {
  const view = panel.panelWin.RecordingsView;
  return view.selected;
};

/**
 * Set the selected index of the recording via the panel.
 *
 * @param {object} panel - The current panel.
 * @return {number} index
 */
exports.setSelectedRecording = function (panel, index) {
  const view = panel.panelWin.RecordingsView;
  view.setSelectedByIndex(index);
  return index;
};

/**
 * Access the selected view from the panel's recording list.
 *
 * @param {object} panel - The current panel.
 * @return {number} index
 */
exports.getSelectedRecordingIndex = function (panel) {
  const view = panel.panelWin.RecordingsView;
  return view.getSelectedIndex();
};

exports.getDurationLabelText = function (panel, elementIndex) {
  const { $$ } = panel.panelWin;
  const elements = $$(".recording-list-item-duration", panel.panelWin.document);
  return elements[elementIndex].innerHTML;
};

exports.getRecordingsCount = function (panel) {
  const { $$ } = panel.panelWin;
  return $$(".recording-list-item", panel.panelWin.document).length;
};
PK
!<O��I	
	
Ochrome/devtools/modules/devtools/client/performance/test/helpers/synth-utils.js/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";

/**
 * Generates a generalized profile with some samples.
 */
exports.synthesizeProfile = () => {
  const { CATEGORY_MASK } = require("devtools/client/performance/modules/categories");
  const RecordingUtils = require("devtools/shared/performance/recording-utils");

  return RecordingUtils.deflateProfile({
    meta: { version: 2 },
    threads: [{
      samples: [{
        time: 1,
        frames: [
          { category: CATEGORY_MASK("other"), location: "(root)" },
          { category: CATEGORY_MASK("other"), location: "A (http://foo/bar/baz:12)" },
          { category: CATEGORY_MASK("css"), location: "B (http://foo/bar/baz:34)" },
          { category: CATEGORY_MASK("js"), location: "C (http://foo/bar/baz:56)" }
        ]
      }, {
        time: 1 + 1,
        frames: [
          { category: CATEGORY_MASK("other"), location: "(root)" },
          { category: CATEGORY_MASK("other"), location: "A (http://foo/bar/baz:12)" },
          { category: CATEGORY_MASK("css"), location: "B (http://foo/bar/baz:34)" },
          { category: CATEGORY_MASK("gc", 1), location: "D (http://foo/bar/baz:78:9)" }
        ]
      }, {
        time: 1 + 1 + 2,
        frames: [
          { category: CATEGORY_MASK("other"), location: "(root)" },
          { category: CATEGORY_MASK("other"), location: "A (http://foo/bar/baz:12)" },
          { category: CATEGORY_MASK("css"), location: "B (http://foo/bar/baz:34)" },
          { category: CATEGORY_MASK("gc", 1), location: "D (http://foo/bar/baz:78:9)" }
        ]
      }, {
        time: 1 + 1 + 2 + 3,
        frames: [
          { category: CATEGORY_MASK("other"), location: "(root)" },
          { category: CATEGORY_MASK("other"), location: "A (http://foo/bar/baz:12)" },
          { category: CATEGORY_MASK("gc", 2), location: "E (http://foo/bar/baz:90)" },
          { category: CATEGORY_MASK("network"), location: "F (http://foo/bar/baz:99)" }
        ]
      }]
    }]
  });
};

/**
 * Generates a simple implementation for a tree class.
 */
exports.synthesizeCustomTreeClass = () => {
  const { AbstractTreeItem } = require("resource://devtools/client/shared/widgets/AbstractTreeItem.jsm");
  const { extend } = require("devtools/shared/extend");

  function MyCustomTreeItem(dataSrc, properties) {
    AbstractTreeItem.call(this, properties);
    this.itemDataSrc = dataSrc;
  }

  MyCustomTreeItem.prototype = extend(AbstractTreeItem.prototype, {
    _displaySelf: function (document, arrowNode) {
      let node = document.createElement("hbox");
      node.style.marginInlineStart = (this.level * 10) + "px";
      node.appendChild(arrowNode);
      node.appendChild(document.createTextNode(this.itemDataSrc.label));
      return node;
    },

    _populateSelf: function (children) {
      for (let childDataSrc of this.itemDataSrc.children) {
        children.push(new MyCustomTreeItem(childDataSrc, {
          parent: this,
          level: this.level + 1
        }));
      }
    }
  });

  const myDataSrc = {
    label: "root",
    children: [{
      label: "foo",
      children: []
    }, {
      label: "bar",
      children: [{
        label: "baz",
        children: []
      }]
    }]
  };

  return { MyCustomTreeItem, myDataSrc };
};
PK
!<�e9��Mchrome/devtools/modules/devtools/client/performance/test/helpers/tab-utils.js/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";

/* globals dump */

const { BrowserTestUtils } = require("resource://testing-common/BrowserTestUtils.jsm");
const Services = require("Services");
const { waitForDelayedStartupFinished } = require("devtools/client/performance/test/helpers/wait-utils");
const { gDevTools } = require("devtools/client/framework/devtools");

/**
 * Gets a random integer in between an interval. Used to uniquely identify
 * added tabs by augmenting the URL.
 */
function getRandomInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

/**
 * Adds a browser tab with the given url in the specified window and waits
 * for it to load.
 */
exports.addTab = function ({ url, win }, options = {}) {
  let id = getRandomInt(0, Number.MAX_SAFE_INTEGER - 1);
  url += `#${id}`;

  dump(`Adding tab with url: ${url}.\n`);

  let { gBrowser } = win || window;
  return BrowserTestUtils.openNewForegroundTab(gBrowser, url,
                                               !options.dontWaitForTabReady);
};

/**
 * Removes a browser tab from the specified window and waits for it to close.
 */
exports.removeTab = function (tab, options = {}) {
  dump(`Removing tab: ${tab.linkedBrowser.currentURI.spec}.\n`);

  return new Promise(resolve => {
    BrowserTestUtils.removeTab(tab).then(() => resolve(tab));

    if (options.dontWaitForTabClose) {
      resolve(tab);
    }
  });
};

/**
 * Adds a browser window with the provided options.
 */
exports.addWindow = function* (options) {
  let { OpenBrowserWindow } = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
  let win = OpenBrowserWindow(options);
  yield waitForDelayedStartupFinished(win);
  return win;
};
PK
!<a�V�Hchrome/devtools/modules/devtools/client/performance/test/helpers/urls.js/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";

exports.EXAMPLE_URL = "http://example.com/browser/devtools/client/performance/test";
exports.SIMPLE_URL = `${exports.EXAMPLE_URL}/doc_simple-test.html`;
PK
!<�_v;��Nchrome/devtools/modules/devtools/client/performance/test/helpers/wait-utils.js/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";

/* globals dump */

const { CC } = require("chrome");
const { Task } = require("devtools/shared/task");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const { once, observeOnce } = require("devtools/client/performance/test/helpers/event-utils");

/**
 * Blocks the main thread for the specified amount of time.
 */
exports.busyWait = function (time) {
  dump(`Busy waiting for: ${time} milliseconds.\n`);
  let start = Date.now();
  /* eslint-disable no-unused-vars */
  let stack;
  while (Date.now() - start < time) {
    stack = CC.stack;
  }
  /* eslint-enable no-unused-vars */
};

/**
 * Idly waits for the specified amount of time.
 */
exports.idleWait = function (time) {
  dump(`Idly waiting for: ${time} milliseconds.\n`);
  return DevToolsUtils.waitForTime(time);
};

/**
 * Waits until a predicate returns true.
 */
exports.waitUntil = function* (predicate, interval = 100, tries = 100) {
  for (let i = 1; i <= tries; i++) {
    if (yield Task.spawn(predicate)) {
      dump(`Predicate returned true after ${i} tries.\n`);
      return;
    }
    yield exports.idleWait(interval);
  }
  throw new Error(`Predicate returned false after ${tries} tries, aborting.\n`);
};

/**
 * Waits for a `MozAfterPaint` event to be fired on the specified window.
 */
exports.waitForMozAfterPaint = function (window) {
  return once(window, "MozAfterPaint");
};

/**
 * Waits for the `browser-delayed-startup-finished` observer notification
 * to be fired on the specified window.
 */
exports.waitForDelayedStartupFinished = function (window) {
  return observeOnce("browser-delayed-startup-finished", { expectedSubject: window });
};
PK
!<�rA[�-�-Jchrome/devtools/modules/devtools/client/performance-new/components/Perf.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/vendor/react");
const { div, button } = require("devtools/client/shared/vendor/react-dom-factories");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");

/**
 * The recordingState is one of the following:
 **/

// The initial state before we've queried the PerfActor
const NOT_YET_KNOWN = "not-yet-known";
// The profiler is available, we haven't started recording yet.
const AVAILABLE_TO_RECORD = "available-to-record";
// An async request has been sent to start the profiler.
const REQUEST_TO_START_RECORDING = "request-to-start-recording";
// An async request has been sent to get the profile and stop the profiler.
const REQUEST_TO_GET_PROFILE_AND_STOP_PROFILER =
  "request-to-get-profile-and-stop-profiler";
// An async request has been sent to stop the profiler.
const REQUEST_TO_STOP_PROFILER = "request-to-stop-profiler";
// The profiler notified us that our request to start it actually started it.
const RECORDING = "recording";
// Some other code with access to the profiler started it.
const OTHER_IS_RECORDING = "other-is-recording";
// Profiling is not available when in private browsing mode.
const LOCKED_BY_PRIVATE_BROWSING = "locked-by-private-browsing";

class Perf extends PureComponent {
  static get propTypes() {
    return {
      perfFront: PropTypes.object.isRequired,
      receiveProfile: PropTypes.func.isRequired
    };
  }

  constructor(props) {
    super(props);
    this.state = {
      recordingState: NOT_YET_KNOWN,
      recordingUnexpectedlyStopped: false,
      // The following is either "null" for unknown, or a boolean value.
      isSupportedPlatform: null
    };
    this.startRecording = this.startRecording.bind(this);
    this.getProfileAndStopProfiler = this.getProfileAndStopProfiler.bind(this);
    this.stopProfilerAndDiscardProfile = this.stopProfilerAndDiscardProfile.bind(this);
    this.handleProfilerStarting = this.handleProfilerStarting.bind(this);
    this.handleProfilerStopping = this.handleProfilerStopping.bind(this);
    this.handlePrivateBrowsingStarting = this.handlePrivateBrowsingStarting.bind(this);
    this.handlePrivateBrowsingEnding = this.handlePrivateBrowsingEnding.bind(this);
  }

  componentDidMount() {
    const { perfFront } = this.props;

    // Ask for the initial state of the profiler.
    Promise.all([
      perfFront.isActive(),
      perfFront.isSupportedPlatform(),
      perfFront.isLockedForPrivateBrowsing(),
    ]).then((results) => {
      const [
        isActive,
        isSupportedPlatform,
        isLockedForPrivateBrowsing
      ] = results;

      let recordingState = this.state.recordingState;
      // It's theoretically possible we got an event that already let us know about
      // the current state of the profiler.
      if (recordingState === NOT_YET_KNOWN && isSupportedPlatform) {
        if (isLockedForPrivateBrowsing) {
          recordingState = LOCKED_BY_PRIVATE_BROWSING;
        } else {
          recordingState = isActive
            ? OTHER_IS_RECORDING
            : AVAILABLE_TO_RECORD;
        }
      }
      this.setState({ isSupportedPlatform, recordingState });
    });

    // Handle when the profiler changes state. It might be us, it might be someone else.
    this.props.perfFront.on("profiler-started", this.handleProfilerStarting);
    this.props.perfFront.on("profiler-stopped", this.handleProfilerStopping);
    this.props.perfFront.on("profile-locked-by-private-browsing",
      this.handlePrivateBrowsingStarting);
    this.props.perfFront.on("profile-unlocked-from-private-browsing",
      this.handlePrivateBrowsingEnding);
  }

  componentWillUnmount() {
    switch (this.state.recordingState) {
      case NOT_YET_KNOWN:
      case AVAILABLE_TO_RECORD:
      case REQUEST_TO_STOP_PROFILER:
      case REQUEST_TO_GET_PROFILE_AND_STOP_PROFILER:
      case LOCKED_BY_PRIVATE_BROWSING:
      case OTHER_IS_RECORDING:
        // Do nothing for these states.
        break;

      case RECORDING:
      case REQUEST_TO_START_RECORDING:
        this.props.perfFront.stopProfilerAndDiscardProfile();
        break;

      default:
        throw new Error("Unhandled recording state.");
    }
  }

  getRecordingStateForTesting() {
    return this.state.recordingState;
  }

  handleProfilerStarting() {
    switch (this.state.recordingState) {
      case NOT_YET_KNOWN:
        // We couldn't have started it yet, so it must have been someone
        // else. (fallthrough)
      case AVAILABLE_TO_RECORD:
        // We aren't recording, someone else started it up. (fallthrough)
      case REQUEST_TO_STOP_PROFILER:
        // We requested to stop the profiler, but someone else already started
        // it up. (fallthrough)
      case REQUEST_TO_GET_PROFILE_AND_STOP_PROFILER:
        // Someone re-started the profiler while we were asking for the completed
        // profile.

        this.setState({
          recordingState: OTHER_IS_RECORDING,
          recordingUnexpectedlyStopped: false
        });
        break;

      case REQUEST_TO_START_RECORDING:
        // Wait for the profiler to tell us that it has started.
        this.setState({
          recordingState: RECORDING,
          recordingUnexpectedlyStopped: false
        });
        break;

      case LOCKED_BY_PRIVATE_BROWSING:
      case OTHER_IS_RECORDING:
      case RECORDING:
        // These state cases don't make sense to happen, and means we have a logical
        // fallacy somewhere.
        throw new Error(
          "The profiler started recording, when it shouldn't have " +
          `been able to. Current state: "${this.state.recordingState}"`);
      default:
        throw new Error("Unhandled recording state");
    }
  }

  handleProfilerStopping() {
    switch (this.state.recordingState) {
      case NOT_YET_KNOWN:
      case REQUEST_TO_GET_PROFILE_AND_STOP_PROFILER:
      case REQUEST_TO_STOP_PROFILER:
      case OTHER_IS_RECORDING:
        this.setState({
          recordingState: AVAILABLE_TO_RECORD,
          recordingUnexpectedlyStopped: false
        });
        break;

      case REQUEST_TO_START_RECORDING:
        // Highly unlikely, but someone stopped the recorder, this is fine.
        // Do nothing (fallthrough).
      case LOCKED_BY_PRIVATE_BROWSING:
        // The profiler is already locked, so we know about this already.
        break;

      case RECORDING:
        this.setState({
          recordingState: AVAILABLE_TO_RECORD,
          recordingUnexpectedlyStopped: true
        });
        break;

      case AVAILABLE_TO_RECORD:
        throw new Error(
          "The profiler stopped recording, when it shouldn't have been able to.");
      default:
        throw new Error("Unhandled recording state");
    }
  }

  handlePrivateBrowsingStarting() {
    switch (this.state.recordingState) {
      case REQUEST_TO_GET_PROFILE_AND_STOP_PROFILER:
        // This one is a tricky case. Go ahead and act like nothing went wrong, maybe
        // it will resolve correctly? (fallthrough)
      case REQUEST_TO_STOP_PROFILER:
      case AVAILABLE_TO_RECORD:
      case OTHER_IS_RECORDING:
      case NOT_YET_KNOWN:
        this.setState({
          recordingState: LOCKED_BY_PRIVATE_BROWSING,
          recordingUnexpectedlyStopped: false
        });
        break;

      case REQUEST_TO_START_RECORDING:
      case RECORDING:
        this.setState({
          recordingState: LOCKED_BY_PRIVATE_BROWSING,
          recordingUnexpectedlyStopped: true
        });
        break;

      case LOCKED_BY_PRIVATE_BROWSING:
        // Do nothing
        break;

      default:
        throw new Error("Unhandled recording state");
    }
  }

  handlePrivateBrowsingEnding() {
    // No matter the state, go ahead and set this as ready to record. This should
    // be the only logical state to go into.
    this.setState({
      recordingState: AVAILABLE_TO_RECORD,
      recordingUnexpectedlyStopped: false
    });
  }

  startRecording() {
    this.setState({
      recordingState: REQUEST_TO_START_RECORDING,
      // Reset this error state since it's no longer valid.
      recordingUnexpectedlyStopped: false,
    });
    this.props.perfFront.startProfiler();
  }

  async getProfileAndStopProfiler() {
    this.setState({ recordingState: REQUEST_TO_GET_PROFILE_AND_STOP_PROFILER });
    const profile = await this.props.perfFront.getProfileAndStopProfiler();
    this.setState({ recordingState: AVAILABLE_TO_RECORD });
    console.log("getProfileAndStopProfiler");
    this.props.receiveProfile(profile);
  }

  stopProfilerAndDiscardProfile() {
    this.setState({ recordingState: REQUEST_TO_STOP_PROFILER });
    this.props.perfFront.stopProfilerAndDiscardProfile();
  }

  render() {
    const { recordingState, isSupportedPlatform } = this.state;

    // Handle the cases of platform support.
    switch (isSupportedPlatform) {
      case null:
        // We don't know yet if this is a supported platform, wait for a response.
        return null;
      case false:
        return renderButton({
          label: "Start recording",
          disabled: true,
          additionalMessage: "Your platform is not supported. The Gecko Profiler only " +
                             "supports Tier-1 platforms."
        });
      case true:
        // Continue on and render the panel.
        break;
    }

    // TODO - L10N all of the messages. Bug 1418056
    switch (recordingState) {
      case NOT_YET_KNOWN:
        return null;

      case AVAILABLE_TO_RECORD:
        return renderButton({
          onClick: this.startRecording,
          label: "Start recording",
          additionalMessage: this.state.recordingUnexpectedlyStopped
            ? div(null, "The recording was stopped by another tool.")
            : null
        });

      case REQUEST_TO_STOP_PROFILER:
        return renderButton({
          label: "Stopping the recording",
          disabled: true
        });

      case REQUEST_TO_GET_PROFILE_AND_STOP_PROFILER:
        return renderButton({
          label: "Stopping the recording, and capturing the profile",
          disabled: true
        });

      case REQUEST_TO_START_RECORDING:
      case RECORDING:
        return renderButton({
          label: "Stop and grab the recording",
          onClick: this.getProfileAndStopProfiler,
          disabled: this.state.recordingState === REQUEST_TO_START_RECORDING
        });

      case OTHER_IS_RECORDING:
        return renderButton({
          label: "Stop and discard the other recording",
          onClick: this.stopProfilerAndDiscardProfile,
          additionalMessage: "Another tool is currently recording."
        });

      case LOCKED_BY_PRIVATE_BROWSING:
        return renderButton({
          label: "Start recording",
          disabled: true,
          additionalMessage: `The profiler is disabled when Private Browsing is enabled.
                              Close all Private Windows to re-enable the profiler`
        });

      default:
        throw new Error("Unhandled recording state");
    }
  }
}

module.exports = Perf;

function renderButton(props) {
  const { disabled, label, onClick, additionalMessage } = props;
  const nbsp = "\u00A0";

  return div(
    { className: "perf" },
    div({ className: "perf-additional-message" }, additionalMessage || nbsp),
    div(
      null,
      button(
        {
          className: "devtools-button perf-button",
          "data-standalone": true,
          disabled,
          onClick
        },
        label
      )
    )
  );
}
PK
!<��eG��@chrome/devtools/modules/devtools/client/performance-new/panel.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { PerfFront } = require("devtools/shared/fronts/perf");

loader.lazyRequireGetter(this, "EventEmitter",
  "devtools/shared/old-event-emitter");

class PerformancePanel {
  constructor(iframeWindow, toolbox) {
    this.panelWin = iframeWindow;
    this.toolbox = toolbox;

    EventEmitter.decorate(this);
  }

  /**
   * Open is effectively an asynchronous constructor.
   * @return {Promise} Resolves when the Perf tool completes opening.
   */
  open() {
    if (!this._opening) {
      this._opening = this._doOpen();
    }
    return this._opening;
  }

  async _doOpen() {
    this.panelWin.gToolbox = this.toolbox;
    this.panelWin.gTarget = this.target;

    const rootForm = await this.target.root;
    const perfFront = new PerfFront(this.target.client, rootForm);

    this.isReady = true;
    this.emit("ready");
    this.panelWin.gInit(perfFront);
    return this;
  }

  // DevToolPanel API:

  get target() {
    return this.toolbox.target;
  }

  async destroy() {
    // Make sure this panel is not already destroyed.
    if (this._destroyed) {
      return;
    }
    this.panelWin.gDestroy();
    this.emit("destroyed");
    this._destroyed = true;
  }
}
exports.PerformancePanel = PerformancePanel;
PK
!<H��p��Jchrome/devtools/modules/devtools/client/responsive.html/actions/devices.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  ADD_DEVICE,
  ADD_DEVICE_TYPE,
  LOAD_DEVICE_LIST_START,
  LOAD_DEVICE_LIST_ERROR,
  LOAD_DEVICE_LIST_END,
  REMOVE_DEVICE,
  UPDATE_DEVICE_DISPLAYED,
  UPDATE_DEVICE_MODAL,
} = require("./index");
const { removeDeviceAssociation } = require("./viewports");

const { addDevice, getDevices, removeDevice } = require("devtools/client/shared/devices");

const Services = require("Services");
const DISPLAYED_DEVICES_PREF = "devtools.responsive.html.displayedDeviceList";

/**
 * Returns an object containing the user preference of displayed devices.
 *
 * @return {Object} containing two Sets:
 * - added: Names of the devices that were explicitly enabled by the user
 * - removed: Names of the devices that were explicitly removed by the user
 */
function loadPreferredDevices() {
  let preferredDevices = {
    "added": new Set(),
    "removed": new Set(),
  };

  if (Services.prefs.prefHasUserValue(DISPLAYED_DEVICES_PREF)) {
    try {
      let savedData = Services.prefs.getStringPref(DISPLAYED_DEVICES_PREF);
      savedData = JSON.parse(savedData);
      if (savedData.added && savedData.removed) {
        preferredDevices.added = new Set(savedData.added);
        preferredDevices.removed = new Set(savedData.removed);
      }
    } catch (e) {
      console.error(e);
    }
  }

  return preferredDevices;
}

/**
 * Update the displayed device list preference with the given device list.
 *
 * @param {Object} containing two Sets:
 * - added: Names of the devices that were explicitly enabled by the user
 * - removed: Names of the devices that were explicitly removed by the user
 */
function updatePreferredDevices(devices) {
  let devicesToSave = {
    added: Array.from(devices.added),
    removed: Array.from(devices.removed),
  };
  devicesToSave = JSON.stringify(devicesToSave);
  Services.prefs.setStringPref(DISPLAYED_DEVICES_PREF, devicesToSave);
}

module.exports = {

  // This function is only exported for testing purposes
  _loadPreferredDevices: loadPreferredDevices,

  updatePreferredDevices: updatePreferredDevices,

  addCustomDevice(device) {
    return async function (dispatch) {
      // Add custom device to device storage
      await addDevice(device, "custom");
      dispatch({
        type: ADD_DEVICE,
        device,
        deviceType: "custom",
      });
    };
  },

  addDevice(device, deviceType) {
    return {
      type: ADD_DEVICE,
      device,
      deviceType,
    };
  },

  addDeviceType(deviceType) {
    return {
      type: ADD_DEVICE_TYPE,
      deviceType,
    };
  },

  removeCustomDevice(device) {
    return async function (dispatch, getState) {
      // Check if the custom device is currently associated with any viewports
      let { viewports } = getState();
      for (let viewport of viewports) {
        if (viewport.device == device.name) {
          dispatch(removeDeviceAssociation(viewport.id));
        }
      }

      // Remove custom device from device storage
      await removeDevice(device, "custom");
      dispatch({
        type: REMOVE_DEVICE,
        device,
        deviceType: "custom",
      });
    };
  },

  updateDeviceDisplayed(device, deviceType, displayed) {
    return {
      type: UPDATE_DEVICE_DISPLAYED,
      device,
      deviceType,
      displayed,
    };
  },

  loadDevices() {
    return async function (dispatch) {
      dispatch({ type: LOAD_DEVICE_LIST_START });
      let preferredDevices = loadPreferredDevices();
      let devices;

      try {
        devices = await getDevices();
      } catch (e) {
        console.error("Could not load device list: " + e);
        dispatch({ type: LOAD_DEVICE_LIST_ERROR });
        return;
      }

      for (let type of devices.TYPES) {
        dispatch(module.exports.addDeviceType(type));
        for (let device of devices[type]) {
          if (device.os == "fxos") {
            continue;
          }

          let newDevice = Object.assign({}, device, {
            displayed: preferredDevices.added.has(device.name) ||
              (device.featured && !(preferredDevices.removed.has(device.name))),
          });

          dispatch(module.exports.addDevice(newDevice, type));
        }
      }

      // Add an empty "custom" type if it doesn't exist in device storage
      if (!devices.TYPES.find(type => type == "custom")) {
        dispatch(module.exports.addDeviceType("custom"));
      }

      dispatch({ type: LOAD_DEVICE_LIST_END });
    };
  },

  updateDeviceModal(isOpen, modalOpenedFromViewport = null) {
    return {
      type: UPDATE_DEVICE_MODAL,
      isOpen,
      modalOpenedFromViewport,
    };
  },

};
PK
!<���Ŝ�Vchrome/devtools/modules/devtools/client/responsive.html/actions/display-pixel-ratio.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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_DISPLAY_PIXEL_RATIO } = require("./index");

module.exports = {

  /**
   * The pixel ratio of the display has changed. This may be triggered by the user
   * when changing the monitor resolution, or when the window is dragged to a different
   * display with a different pixel ratio.
   */
  changeDisplayPixelRatio(displayPixelRatio) {
    return {
      type: CHANGE_DISPLAY_PIXEL_RATIO,
      displayPixelRatio,
    };
  },

};
PK
!<ÿH�	�	Hchrome/devtools/modules/devtools/client/responsive.html/actions/index.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 lists all of the actions available in responsive design.  This
// central list of constants makes it easy to see all possible action names at
// a glance.  Please add a comment with each new action type.

const { createEnum } = require("devtools/client/shared/enum");

createEnum([

  // Add a new device.
  "ADD_DEVICE",

  // Add a new device type.
  "ADD_DEVICE_TYPE",

  // Add an additional viewport to display the document.
  "ADD_VIEWPORT",

  // Change the device displayed in the viewport.
  "CHANGE_DEVICE",

  // Change the location of the page.  This may be triggered by the user
  // directly entering a new URL, navigating with links, etc.
  "CHANGE_LOCATION",

  // The pixel ratio of the display has changed. This may be triggered by the user
  // when changing the monitor resolution, or when the window is dragged to a different
  // display with a different pixel ratio.
  "CHANGE_DISPLAY_PIXEL_RATIO",

  // Change the network throttling profile.
  "CHANGE_NETWORK_THROTTLING",

  // The pixel ratio of the viewport has changed. This may be triggered by the user
  // when changing the device displayed in the viewport, or when a pixel ratio is
  // selected from the device pixel ratio dropdown.
  "CHANGE_PIXEL_RATIO",

  // Change one of the reload conditions.
  "CHANGE_RELOAD_CONDITION",

  // Change the touch simulation state.
  "CHANGE_TOUCH_SIMULATION",

  // Indicates that the device list is being loaded.
  "LOAD_DEVICE_LIST_START",

  // Indicates that the device list loading action threw an error.
  "LOAD_DEVICE_LIST_ERROR",

  // Indicates that the device list has been loaded successfully.
  "LOAD_DEVICE_LIST_END",

  // Indicates that the reload conditions have been loaded successfully.
  "LOAD_RELOAD_CONDITIONS_END",

  // Remove a device.
  "REMOVE_DEVICE",

  // Remove the viewport's device assocation.
  "REMOVE_DEVICE_ASSOCIATION",

  // Resize the viewport.
  "RESIZE_VIEWPORT",

  // Rotate the viewport.
  "ROTATE_VIEWPORT",

  // Take a screenshot of the viewport.
  "TAKE_SCREENSHOT_START",

  // Indicates when the screenshot action ends.
  "TAKE_SCREENSHOT_END",

  // Update the device display state in the device selector.
  "UPDATE_DEVICE_DISPLAYED",

  // Update the device modal state.
  "UPDATE_DEVICE_MODAL",

], module.exports);
PK
!<P#*U!!Kchrome/devtools/modules/devtools/client/responsive.html/actions/location.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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_LOCATION } = require("./index");

module.exports = {

  /**
   * The location of the page has changed.  This may be triggered by the user
   * directly entering a new URL, navigating with links, etc.
   */
  changeLocation(location) {
    return {
      type: CHANGE_LOCATION,
      location,
    };
  },

};
PK
!<me8��Uchrome/devtools/modules/devtools/client/responsive.html/actions/network-throttling.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("./index");

module.exports = {

  changeNetworkThrottling(enabled, profile) {
    return {
      type: CHANGE_NETWORK_THROTTLING,
      enabled,
      profile,
    };
  },

};
PK
!<X=�E��Tchrome/devtools/modules/devtools/client/responsive.html/actions/reload-conditions.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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_RELOAD_CONDITION,
  LOAD_RELOAD_CONDITIONS_END,
} = require("./index");

const Types = require("../types");
const Services = require("Services");

const PREF_PREFIX = "devtools.responsive.reloadConditions.";

module.exports = {

  changeReloadCondition(id, value) {
    return dispatch => {
      let pref = PREF_PREFIX + id;
      Services.prefs.setBoolPref(pref, value);
      dispatch({
        type: CHANGE_RELOAD_CONDITION,
        id,
        value,
      });
    };
  },

  loadReloadConditions() {
    return dispatch => {
      // Loop over the conditions and load their values from prefs.
      for (let id in Types.reloadConditions) {
        // Skip over the loading state of the list.
        if (id == "state") {
          return;
        }
        let pref = PREF_PREFIX + id;
        let value = Services.prefs.getBoolPref(pref, false);
        dispatch({
          type: CHANGE_RELOAD_CONDITION,
          id,
          value,
        });
      }

      dispatch({ type: LOAD_RELOAD_CONDITIONS_END });
    };
  },

};
PK
!<o�S��	�	Mchrome/devtools/modules/devtools/client/responsive.html/actions/screenshot.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 {
  TAKE_SCREENSHOT_START,
  TAKE_SCREENSHOT_END,
} = require("./index");

const { getFormatStr } = require("../utils/l10n");
const { getToplevelWindow } = require("../utils/window");
const e10s = require("../utils/e10s");
const Services = require("Services");

const CAMERA_AUDIO_URL = "resource://devtools/client/themes/audio/shutter.wav";

const animationFrame = () => new Promise(resolve => {
  window.requestAnimationFrame(resolve);
});

function getFileName() {
  let date = new Date();
  let month = ("0" + (date.getMonth() + 1)).substr(-2);
  let day = ("0" + date.getDate()).substr(-2);
  let dateString = [date.getFullYear(), month, day].join("-");
  let timeString = date.toTimeString().replace(/:/g, ".").split(" ")[0];

  return getFormatStr("responsive.screenshotGeneratedFilename", dateString,
                      timeString);
}

function createScreenshotFor(node) {
  let mm = node.frameLoader.messageManager;

  return e10s.request(mm, "RequestScreenshot");
}

function saveToFile(data, filename) {
  const chromeWindow = getToplevelWindow(window);
  const chromeDocument = chromeWindow.document;

  // append .png extension to filename if it doesn't exist
  filename = filename.replace(/\.png$|$/i, ".png");

  chromeWindow.saveURL(data, filename, null,
                        true, true,
                        chromeDocument.documentURIObject, chromeDocument);
}

function simulateCameraEffects(node) {
  if (Services.prefs.getBoolPref("devtools.screenshot.audio.enabled")) {
    let cameraAudio = new window.Audio(CAMERA_AUDIO_URL);
    cameraAudio.play();
  }
  node.animate({ opacity: [ 0, 1 ] }, 500);
}

module.exports = {

  takeScreenshot() {
    return async function (dispatch, getState) {
      await dispatch({ type: TAKE_SCREENSHOT_START });

      // Waiting the next repaint, to ensure the react components
      // can be properly render after the action dispatched above
      await animationFrame();

      let iframe = document.querySelector("iframe");
      let data = await createScreenshotFor(iframe);

      simulateCameraEffects(iframe);

      saveToFile(data, getFileName());

      dispatch({ type: TAKE_SCREENSHOT_END });
    };
  }
};
PK
!<�
�!��Schrome/devtools/modules/devtools/client/responsive.html/actions/touch-simulation.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 {
  CHANGE_TOUCH_SIMULATION
} = require("./index");

module.exports = {

  changeTouchSimulation(enabled) {
    return {
      type: CHANGE_TOUCH_SIMULATION,
      enabled,
    };
  },

};
PK
!<�[���Lchrome/devtools/modules/devtools/client/responsive.html/actions/viewports.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 {
  ADD_VIEWPORT,
  CHANGE_DEVICE,
  CHANGE_PIXEL_RATIO,
  REMOVE_DEVICE_ASSOCIATION,
  RESIZE_VIEWPORT,
  ROTATE_VIEWPORT
} = require("./index");

const { post } = require("../utils/message");

module.exports = {

  /**
   * Add an additional viewport to display the document.
   */
  addViewport() {
    return {
      type: ADD_VIEWPORT,
    };
  },

  /**
   * Change the viewport device.
   */
  changeDevice(id, device, deviceType) {
    return {
      type: CHANGE_DEVICE,
      id,
      device,
      deviceType,
    };
  },

  /**
   * Change the viewport pixel ratio.
   */
  changePixelRatio(id, pixelRatio = 0) {
    return {
      type: CHANGE_PIXEL_RATIO,
      id,
      pixelRatio,
    };
  },

  /**
   * Remove the viewport's device assocation.
   */
  removeDeviceAssociation(id) {
    post(window, "remove-device-association");
    return {
      type: REMOVE_DEVICE_ASSOCIATION,
      id,
    };
  },

  /**
   * Resize the viewport.
   */
  resizeViewport(id, width, height) {
    return {
      type: RESIZE_VIEWPORT,
      id,
      width,
      height,
    };
  },

  /**
   * Rotate the viewport.
   */
  rotateViewport(id) {
    return {
      type: ROTATE_VIEWPORT,
      id,
    };
  },

};
PK
!<�5��>chrome/devtools/modules/devtools/client/responsive.html/app.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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("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");
const { connect } = require("devtools/client/shared/vendor/react-redux");

const {
  addCustomDevice,
  removeCustomDevice,
  updateDeviceDisplayed,
  updateDeviceModal,
  updatePreferredDevices,
} = require("./actions/devices");
const { changeNetworkThrottling } = require("./actions/network-throttling");
const { changeReloadCondition } = require("./actions/reload-conditions");
const { takeScreenshot } = require("./actions/screenshot");
const { changeTouchSimulation } = require("./actions/touch-simulation");
const {
  changeDevice,
  changePixelRatio,
  removeDeviceAssociation,
  resizeViewport,
  rotateViewport,
} = require("./actions/viewports");
const DeviceModal = createFactory(require("./components/DeviceModal"));
const GlobalToolbar = createFactory(require("./components/GlobalToolbar"));
const Viewports = createFactory(require("./components/Viewports"));
const Types = require("./types");

class App extends Component {
  static get propTypes() {
    return {
      devices: PropTypes.shape(Types.devices).isRequired,
      dispatch: PropTypes.func.isRequired,
      displayPixelRatio: Types.pixelRatio.value.isRequired,
      networkThrottling: PropTypes.shape(Types.networkThrottling).isRequired,
      reloadConditions: PropTypes.shape(Types.reloadConditions).isRequired,
      screenshot: PropTypes.shape(Types.screenshot).isRequired,
      touchSimulation: PropTypes.shape(Types.touchSimulation).isRequired,
      viewports: PropTypes.arrayOf(PropTypes.shape(Types.viewport)).isRequired,
    };
  }

  constructor(props) {
    super(props);
    this.onAddCustomDevice = this.onAddCustomDevice.bind(this);
    this.onBrowserMounted = this.onBrowserMounted.bind(this);
    this.onChangeDevice = this.onChangeDevice.bind(this);
    this.onChangeNetworkThrottling = this.onChangeNetworkThrottling.bind(this);
    this.onChangePixelRatio = this.onChangePixelRatio.bind(this);
    this.onChangeReloadCondition = this.onChangeReloadCondition.bind(this);
    this.onChangeTouchSimulation = this.onChangeTouchSimulation.bind(this);
    this.onContentResize = this.onContentResize.bind(this);
    this.onDeviceListUpdate = this.onDeviceListUpdate.bind(this);
    this.onExit = this.onExit.bind(this);
    this.onRemoveCustomDevice = this.onRemoveCustomDevice.bind(this);
    this.onRemoveDeviceAssociation = this.onRemoveDeviceAssociation.bind(this);
    this.onResizeViewport = this.onResizeViewport.bind(this);
    this.onRotateViewport = this.onRotateViewport.bind(this);
    this.onScreenshot = this.onScreenshot.bind(this);
    this.onUpdateDeviceDisplayed = this.onUpdateDeviceDisplayed.bind(this);
    this.onUpdateDeviceModal = this.onUpdateDeviceModal.bind(this);
  }

  onAddCustomDevice(device) {
    this.props.dispatch(addCustomDevice(device));
  }

  onBrowserMounted() {
    window.postMessage({ type: "browser-mounted" }, "*");
  }

  onChangeDevice(id, device, deviceType) {
    // TODO: Bug 1332754: Move messaging and logic into the action creator so that the
    // message is sent from the action creator and device property changes are sent from
    // there instead of this function.
    window.postMessage({
      type: "change-device",
      device,
    }, "*");
    this.props.dispatch(changeDevice(id, device.name, deviceType));
    this.props.dispatch(changeTouchSimulation(device.touch));
    this.props.dispatch(changePixelRatio(id, device.pixelRatio));
  }

  onChangeNetworkThrottling(enabled, profile) {
    window.postMessage({
      type: "change-network-throtting",
      enabled,
      profile,
    }, "*");
    this.props.dispatch(changeNetworkThrottling(enabled, profile));
  }

  onChangePixelRatio(pixelRatio) {
    window.postMessage({
      type: "change-pixel-ratio",
      pixelRatio,
    }, "*");
    this.props.dispatch(changePixelRatio(0, pixelRatio));
  }

  onChangeReloadCondition(id, value) {
    this.props.dispatch(changeReloadCondition(id, value));
  }

  onChangeTouchSimulation(enabled) {
    window.postMessage({
      type: "change-touch-simulation",
      enabled,
    }, "*");
    this.props.dispatch(changeTouchSimulation(enabled));
  }

  onContentResize({ width, height }) {
    window.postMessage({
      type: "content-resize",
      width,
      height,
    }, "*");
  }

  onDeviceListUpdate(devices) {
    updatePreferredDevices(devices);
  }

  onExit() {
    window.postMessage({ type: "exit" }, "*");
  }

  onRemoveCustomDevice(device) {
    this.props.dispatch(removeCustomDevice(device));
  }

  onRemoveDeviceAssociation(id) {
    // TODO: Bug 1332754: Move messaging and logic into the action creator so that device
    // property changes are sent from there instead of this function.
    this.props.dispatch(removeDeviceAssociation(id));
    this.props.dispatch(changeTouchSimulation(false));
    this.props.dispatch(changePixelRatio(id, 0));
  }

  onResizeViewport(id, width, height) {
    this.props.dispatch(resizeViewport(id, width, height));
  }

  onRotateViewport(id) {
    this.props.dispatch(rotateViewport(id));
  }

  onScreenshot() {
    this.props.dispatch(takeScreenshot());
  }

  onUpdateDeviceDisplayed(device, deviceType, displayed) {
    this.props.dispatch(updateDeviceDisplayed(device, deviceType, displayed));
  }

  onUpdateDeviceModal(isOpen, modalOpenedFromViewport) {
    this.props.dispatch(updateDeviceModal(isOpen, modalOpenedFromViewport));
  }

  render() {
    let {
      devices,
      displayPixelRatio,
      networkThrottling,
      reloadConditions,
      screenshot,
      touchSimulation,
      viewports,
    } = this.props;

    let {
      onAddCustomDevice,
      onBrowserMounted,
      onChangeDevice,
      onChangeNetworkThrottling,
      onChangePixelRatio,
      onChangeReloadCondition,
      onChangeTouchSimulation,
      onContentResize,
      onDeviceListUpdate,
      onExit,
      onRemoveCustomDevice,
      onRemoveDeviceAssociation,
      onResizeViewport,
      onRotateViewport,
      onScreenshot,
      onUpdateDeviceDisplayed,
      onUpdateDeviceModal,
    } = this;

    let selectedDevice = "";
    let selectedPixelRatio = { value: 0 };

    if (viewports.length) {
      selectedDevice = viewports[0].device;
      selectedPixelRatio = viewports[0].pixelRatio;
    }

    let deviceAdderViewportTemplate = {};
    if (devices.modalOpenedFromViewport !== null) {
      deviceAdderViewportTemplate = viewports[devices.modalOpenedFromViewport];
    }

    return dom.div(
      {
        id: "app",
      },
      GlobalToolbar({
        devices,
        displayPixelRatio,
        networkThrottling,
        reloadConditions,
        screenshot,
        selectedDevice,
        selectedPixelRatio,
        touchSimulation,
        onChangeNetworkThrottling,
        onChangePixelRatio,
        onChangeReloadCondition,
        onChangeTouchSimulation,
        onExit,
        onScreenshot,
      }),
      Viewports({
        devices,
        screenshot,
        viewports,
        onBrowserMounted,
        onChangeDevice,
        onContentResize,
        onRemoveDeviceAssociation,
        onRotateViewport,
        onResizeViewport,
        onUpdateDeviceModal,
      }),
      DeviceModal({
        deviceAdderViewportTemplate,
        devices,
        onAddCustomDevice,
        onDeviceListUpdate,
        onRemoveCustomDevice,
        onUpdateDeviceDisplayed,
        onUpdateDeviceModal,
      })
    );
  }
}

module.exports = connect(state => state)(App);
PK
!<�_�PPJchrome/devtools/modules/devtools/client/responsive.html/browser/content.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 content, docShell, addEventListener, addMessageListener,
   removeEventListener, removeMessageListener, sendAsyncMessage, Services */

var global = this;

// Guard against loading this frame script mutiple times
(function () {
  if (global.responsiveFrameScriptLoaded) {
    return;
  }

  const gDeviceSizeWasPageSize = docShell.deviceSizeIsPageSize;
  const gFloatingScrollbarsStylesheet = Services.io.newURI("chrome://devtools/skin/floating-scrollbars-responsive-design.css");

  let requiresFloatingScrollbars;
  let active = false;
  let resizeNotifications = false;

  addMessageListener("ResponsiveMode:Start", startResponsiveMode);
  addMessageListener("ResponsiveMode:Stop", stopResponsiveMode);
  addMessageListener("ResponsiveMode:IsActive", isActive);

  function debug(msg) {
    // dump(`RDM CHILD: ${msg}\n`);
  }

  /**
   * Used by tests to verify the state of responsive mode.
   */
  function isActive() {
    sendAsyncMessage("ResponsiveMode:IsActive:Done", { active });
  }

  function startResponsiveMode({ data }) {
    debug("START");
    if (active) {
      debug("ALREADY STARTED");
      sendAsyncMessage("ResponsiveMode:Start:Done");
      return;
    }
    addMessageListener("ResponsiveMode:RequestScreenshot", screenshot);
    let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                              .getInterface(Ci.nsIWebProgress);
    webProgress.addProgressListener(WebProgressListener, Ci.nsIWebProgress.NOTIFY_ALL);
    docShell.deviceSizeIsPageSize = true;
    requiresFloatingScrollbars = data.requiresFloatingScrollbars;
    if (data.notifyOnResize) {
      startOnResize();
    }

    // At this point, a content viewer might not be loaded for this
    // docshell. makeScrollbarsFloating will be triggered by onLocationChange.
    if (docShell.contentViewer) {
      makeScrollbarsFloating();
    }
    active = true;
    sendAsyncMessage("ResponsiveMode:Start:Done");
  }

  function onResize() {
    let { width, height } = content.screen;
    debug(`EMIT RESIZE: ${width} x ${height}`);
    sendAsyncMessage("ResponsiveMode:OnContentResize", {
      width,
      height,
    });
  }

  function bindOnResize() {
    content.addEventListener("resize", onResize);
  }

  function startOnResize() {
    debug("START ON RESIZE");
    if (resizeNotifications) {
      return;
    }
    resizeNotifications = true;
    bindOnResize();
    addEventListener("DOMWindowCreated", bindOnResize, false);
  }

  function stopOnResize() {
    debug("STOP ON RESIZE");
    if (!resizeNotifications) {
      return;
    }
    resizeNotifications = false;
    content.removeEventListener("resize", onResize);
    removeEventListener("DOMWindowCreated", bindOnResize, false);
  }

  function stopResponsiveMode() {
    debug("STOP");
    if (!active) {
      debug("ALREADY STOPPED, ABORT");
      return;
    }
    active = false;
    removeMessageListener("ResponsiveMode:RequestScreenshot", screenshot);
    let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                              .getInterface(Ci.nsIWebProgress);
    webProgress.removeProgressListener(WebProgressListener);
    docShell.deviceSizeIsPageSize = gDeviceSizeWasPageSize;
    restoreScrollbars();
    stopOnResize();
    sendAsyncMessage("ResponsiveMode:Stop:Done");
  }

  function makeScrollbarsFloating() {
    if (!requiresFloatingScrollbars) {
      return;
    }

    let allDocShells = [docShell];

    for (let i = 0; i < docShell.childCount; i++) {
      let child = docShell.getChildAt(i).QueryInterface(Ci.nsIDocShell);
      allDocShells.push(child);
    }

    for (let d of allDocShells) {
      let win = d.contentViewer.DOMDocument.defaultView;
      let winUtils = win.QueryInterface(Ci.nsIInterfaceRequestor)
                        .getInterface(Ci.nsIDOMWindowUtils);
      try {
        winUtils.loadSheet(gFloatingScrollbarsStylesheet, win.AGENT_SHEET);
      } catch (e) { }
    }

    flushStyle();
  }

  function restoreScrollbars() {
    let allDocShells = [docShell];
    for (let i = 0; i < docShell.childCount; i++) {
      allDocShells.push(docShell.getChildAt(i).QueryInterface(Ci.nsIDocShell));
    }
    for (let d of allDocShells) {
      let win = d.contentViewer.DOMDocument.defaultView;
      let winUtils = win.QueryInterface(Ci.nsIInterfaceRequestor)
                        .getInterface(Ci.nsIDOMWindowUtils);
      try {
        winUtils.removeSheet(gFloatingScrollbarsStylesheet, win.AGENT_SHEET);
      } catch (e) { }
    }
    flushStyle();
  }

  function flushStyle() {
    // Force presContext destruction
    let isSticky = docShell.contentViewer.sticky;
    docShell.contentViewer.sticky = false;
    docShell.contentViewer.hide();
    docShell.contentViewer.show();
    docShell.contentViewer.sticky = isSticky;
  }

  function screenshot() {
    let canvas = content.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
    let ratio = content.devicePixelRatio;
    let width = content.innerWidth * ratio;
    let height = content.innerHeight * ratio;
    canvas.mozOpaque = true;
    canvas.width = width;
    canvas.height = height;
    let ctx = canvas.getContext("2d");
    ctx.scale(ratio, ratio);
    ctx.drawWindow(content, content.scrollX, content.scrollY, width, height, "#fff");
    sendAsyncMessage("ResponsiveMode:RequestScreenshot:Done", canvas.toDataURL());
  }

  let WebProgressListener = {
    onLocationChange(webProgress, request, URI, flags) {
      if (flags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT) {
        return;
      }
      makeScrollbarsFloating();
    },
    QueryInterface: function QueryInterface(iid) {
      if (iid.equals(Ci.nsIWebProgressListener) ||
          iid.equals(Ci.nsISupportsWeakReference) ||
          iid.equals(Ci.nsISupports)) {
        return this;
      }
      throw Cr.NS_ERROR_NO_INTERFACE;
    }
  };
})();

global.responsiveFrameScriptLoaded = true;
sendAsyncMessage("ResponsiveMode:ChildScriptReady");
PK
!<1yNY@Y@Gchrome/devtools/modules/devtools/client/responsive.html/browser/swap.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Ci } = require("chrome");
const { tunnelToInnerBrowser } = require("./tunnel");

function debug(msg) {
  // console.log(`RDM swap: ${msg}`);
}

/**
 * Swap page content from an existing tab into a new browser within a container
 * page.  Page state is preserved by using `swapFrameLoaders`, just like when
 * you move a tab to a new window.  This provides a seamless transition for the
 * user since the page is not reloaded.
 *
 * See /devtools/docs/responsive-design-mode.md for a high level overview of how
 * this is used in RDM.  The steps described there are copied into the code
 * below.
 *
 * @param tab
 *        A browser tab with content to be swapped.
 * @param containerURL
 *        URL to a page that holds an inner browser.
 * @param getInnerBrowser
 *        Function that returns a Promise to the inner browser within the
 *        container page.  It is called with the outer browser that loaded the
 *        container page.
 */
function swapToInnerBrowser({ tab, containerURL, getInnerBrowser }) {
  let browserWindow = tab.ownerGlobal;
  let gBrowser = browserWindow.gBrowser;
  let innerBrowser;
  let tunnel;

  // Dispatch a custom event each time the _viewport content_ is swapped from one browser
  // to another.  DevTools server code uses this to follow the content if there is an
  // active DevTools connection.  While browser.xml does dispatch it's own SwapDocShells
  // event, this one is easier for DevTools to follow because it's only emitted once per
  // transition, instead of twice like SwapDocShells.
  let dispatchDevToolsBrowserSwap = (from, to) => {
    let CustomEvent = browserWindow.CustomEvent;
    let event = new CustomEvent("DevTools:BrowserSwap", {
      detail: to,
      bubbles: true,
    });
    from.dispatchEvent(event);
  };

  // A version of `gBrowser.addTab` that absorbs the `TabOpen` event.
  // The swap process uses a temporary tab, and there's no real need for others to hear
  // about it.  This hides the temporary tab from things like WebExtensions.
  let addTabSilently = (uri, options) => {
    browserWindow.addEventListener("TabOpen", event => {
      event.stopImmediatePropagation();
    }, { capture: true, once: true });
    return gBrowser.addTab(uri, options);
  };

  // A version of `gBrowser.swapBrowsersAndCloseOther` that absorbs the `TabClose` event.
  // The swap process uses a temporary tab, and there's no real need for others to hear
  // about it.  This hides the temporary tab from things like WebExtensions.
  let swapBrowsersAndCloseOtherSilently = (ourTab, otherTab) => {
    browserWindow.addEventListener("TabClose", event => {
      event.stopImmediatePropagation();
    }, { capture: true, once: true });
    gBrowser.swapBrowsersAndCloseOther(ourTab, otherTab);
  };

  // It is possible for the frame loader swap within `gBrowser._swapBrowserDocShells` to
  // fail when various frame state is either not ready yet or doesn't match between the
  // two browsers you're trying to swap.  However, such errors are currently caught and
  // silenced in the browser, because they are apparently expected in certain cases.
  // So, here we do our own check to verify that the swap actually did in fact take place,
  // making it much easier to track such errors when they happen.
  let swapBrowserDocShells = (ourTab, otherBrowser) => {
    // The verification step here assumes both browsers are remote.
    if (!ourTab.linkedBrowser.isRemoteBrowser || !otherBrowser.isRemoteBrowser) {
      throw new Error("Both browsers should be remote before swapping.");
    }
    let contentTabId = ourTab.linkedBrowser.frameLoader.tabParent.tabId;
    gBrowser._swapBrowserDocShells(ourTab, otherBrowser);
    if (otherBrowser.frameLoader.tabParent.tabId != contentTabId) {
      // Bug 1408602: Try to unwind to save tab content from being lost.
      throw new Error("Swapping tab content between browsers failed.");
    }
  };

  return {

    async start() {
      tab.isResponsiveDesignMode = true;

      // Hide the browser content temporarily while things move around to avoid displaying
      // strange intermediate states.
      tab.linkedBrowser.style.visibility = "hidden";

      // Freeze navigation temporarily to avoid "blinking" in the location bar.
      freezeNavigationState(tab);

      // 1. Create a temporary, hidden tab to load the tool UI.
      debug("Add blank tool tab");
      let containerTab = addTabSilently("about:blank", {
        skipAnimation: true,
        forceNotRemote: true,
      });
      gBrowser.hideTab(containerTab);
      let containerBrowser = containerTab.linkedBrowser;
      // Even though we load the `containerURL` with `LOAD_FLAGS_BYPASS_HISTORY` below,
      // `SessionHistory.jsm` has a fallback path for tabs with no history which
      // fabricates a history entry by reading the current URL, and this can cause the
      // container URL to be recorded in the session store.  To avoid this, we send a
      // bogus `epoch` value to our container tab, which causes all future history
      // messages to be ignored.  (Actual navigations are still correctly recorded because
      // this only affects the container frame, not the content.)  A better fix would be
      // to just not load the `content-sessionStore.js` frame script at all in the
      // container tab, but it's loaded for all tab browsers, so this seems a bit harder
      // to achieve in a nice way.
      containerBrowser.messageManager.sendAsyncMessage("SessionStore:flush", {
        epoch: -1,
      });
      // Prevent the `containerURL` from ending up in the tab's history.
      debug("Load container URL");
      containerBrowser.loadURIWithFlags(containerURL, {
        flags: Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_HISTORY,
      });

      // Copy tab listener state flags to container tab.  Each tab gets its own tab
      // listener and state flags which cache document loading progress.  The state flags
      // are checked when switching tabs to update the browser UI.  The later step of
      // `swapBrowsersAndCloseOther` will fold the state back into the main tab.
      let stateFlags = gBrowser._tabListeners.get(tab).mStateFlags;
      gBrowser._tabListeners.get(containerTab).mStateFlags = stateFlags;

      // 2. Mark the tool tab browser's docshell as active so the viewport frame
      //    is created eagerly and will be ready to swap.
      // This line is crucial when the tool UI is loaded into a background tab.
      // Without it, the viewport browser's frame is created lazily, leading to
      // a multi-second delay before it would be possible to `swapFrameLoaders`.
      // Even worse than the delay, there appears to be no obvious event fired
      // after the frame is set lazily, so it's unclear how to know that work
      // has finished.
      debug("Set container docShell active");
      containerBrowser.docShellIsActive = true;

      // 3. Create the initial viewport inside the tool UI.
      // The calling application will use container page loaded into the tab to
      // do whatever it needs to create the inner browser.
      debug("Wait until container tab loaded");
      await tabLoaded(containerTab);
      debug("Wait until inner browser available");
      innerBrowser = await getInnerBrowser(containerBrowser);
      addXULBrowserDecorations(innerBrowser);
      if (innerBrowser.isRemoteBrowser != tab.linkedBrowser.isRemoteBrowser) {
        throw new Error("The inner browser's remoteness must match the " +
                        "original tab.");
      }

      // 4. Swap tab content from the regular browser tab to the browser within
      //    the viewport in the tool UI, preserving all state via
      //    `gBrowser._swapBrowserDocShells`.
      dispatchDevToolsBrowserSwap(tab.linkedBrowser, innerBrowser);
      debug("Swap content to inner browser");
      swapBrowserDocShells(tab, innerBrowser);

      // 5. Force the original browser tab to be non-remote since the tool UI
      //    must be loaded in the parent process, and we're about to swap the
      //    tool UI into this tab.
      debug("Flip original tab to remote false");
      gBrowser.updateBrowserRemoteness(tab.linkedBrowser, false);

      // 6. Swap the tool UI (with viewport showing the content) into the
      //    original browser tab and close the temporary tab used to load the
      //    tool via `swapBrowsersAndCloseOther`.
      debug("Swap tool UI to original tab");
      swapBrowsersAndCloseOtherSilently(tab, containerTab);

      // 7. Start a tunnel from the tool tab's browser to the viewport browser
      //    so that some browser UI functions, like navigation, are connected to
      //    the content in the viewport, instead of the tool page.
      tunnel = tunnelToInnerBrowser(tab.linkedBrowser, innerBrowser);
      debug("Wait until tunnel start");
      await tunnel.start();

      // Swapping browsers disconnects the find bar UI from the browser.
      // If the find bar has been initialized, reconnect it.
      if (gBrowser.isFindBarInitialized(tab)) {
        let findBar = gBrowser.getFindBar(tab);
        findBar.browser = tab.linkedBrowser;
        if (!findBar.hidden) {
          // Force the find bar to activate again, restoring the search string.
          findBar.onFindCommand();
        }
      }

      // Force the browser UI to match the new state of the tab and browser.
      thawNavigationState(tab);
      gBrowser.setTabTitle(tab);
      gBrowser.updateCurrentBrowser(true);

      // Show the browser content again now that the move is done.
      tab.linkedBrowser.style.visibility = "";
    },

    stop() {
      // Hide the browser content temporarily while things move around to avoid displaying
      // strange intermediate states.
      tab.linkedBrowser.style.visibility = "hidden";

      // 1. Stop the tunnel between outer and inner browsers.
      tunnel.stop();
      tunnel = null;

      // 2. Create a temporary, hidden tab to hold the content.
      let contentTab = addTabSilently("about:blank", {
        skipAnimation: true,
      });
      gBrowser.hideTab(contentTab);
      let contentBrowser = contentTab.linkedBrowser;

      // 3. Mark the content tab browser's docshell as active so the frame
      //    is created eagerly and will be ready to swap.
      contentBrowser.docShellIsActive = true;

      // 4. Swap tab content from the browser within the viewport in the tool UI
      //    to the regular browser tab, preserving all state via
      //    `gBrowser._swapBrowserDocShells`.
      dispatchDevToolsBrowserSwap(innerBrowser, contentBrowser);
      swapBrowserDocShells(contentTab, innerBrowser);
      innerBrowser = null;

      // Copy tab listener state flags to content tab.  See similar comment in `start`
      // above for more details.
      let stateFlags = gBrowser._tabListeners.get(tab).mStateFlags;
      gBrowser._tabListeners.get(contentTab).mStateFlags = stateFlags;

      // 5. Force the original browser tab to be remote since web content is
      //    loaded in the child process, and we're about to swap the content
      //    into this tab.
      gBrowser.updateBrowserRemoteness(tab.linkedBrowser, true, {
        remoteType: contentBrowser.remoteType,
      });

      // 6. Swap the content into the original browser tab and close the
      //    temporary tab used to hold the content via
      //    `swapBrowsersAndCloseOther`.
      dispatchDevToolsBrowserSwap(contentBrowser, tab.linkedBrowser);
      swapBrowsersAndCloseOtherSilently(tab, contentTab);

      // Swapping browsers disconnects the find bar UI from the browser.
      // If the find bar has been initialized, reconnect it.
      if (gBrowser.isFindBarInitialized(tab)) {
        let findBar = gBrowser.getFindBar(tab);
        findBar.browser = tab.linkedBrowser;
        if (!findBar.hidden) {
          // Force the find bar to activate again, restoring the search string.
          findBar.onFindCommand();
        }
      }

      gBrowser = null;
      browserWindow = null;

      // The focus manager seems to get a little dizzy after all this swapping.  If a
      // content element had been focused inside the viewport before stopping, it will
      // have lost focus.  Activate the frame to restore expected focus.
      tab.linkedBrowser.frameLoader.activateRemoteFrame();

      delete tab.isResponsiveDesignMode;

      // Show the browser content again now that the move is done.
      tab.linkedBrowser.style.visibility = "";
    },

  };
}

/**
 * Browser navigation properties we'll freeze temporarily to avoid "blinking" in the
 * location bar, etc. caused by the containerURL peeking through before the swap is
 * complete.
 */
const NAVIGATION_PROPERTIES = [
  "currentURI",
  "contentTitle",
  "securityUI",
];

function freezeNavigationState(tab) {
  // Browser navigation properties we'll freeze temporarily to avoid "blinking" in the
  // location bar, etc. caused by the containerURL peeking through before the swap is
  // complete.
  for (let property of NAVIGATION_PROPERTIES) {
    let value = tab.linkedBrowser[property];
    Object.defineProperty(tab.linkedBrowser, property, {
      get() {
        return value;
      },
      configurable: true,
      enumerable: true,
    });
  }
}

function thawNavigationState(tab) {
  // Thaw out the properties we froze at the beginning now that the swap is complete.
  for (let property of NAVIGATION_PROPERTIES) {
    delete tab.linkedBrowser[property];
  }
}

/**
 * Browser elements that are passed to `gBrowser._swapBrowserDocShells` are
 * expected to have certain properties that currently exist only on
 * <xul:browser> elements.  In particular, <iframe mozbrowser> elements don't
 * have them.
 *
 * Rather than duplicate the swapping code used by the browser to work around
 * this, we stub out the missing properties needed for the swap to complete.
 */
function addXULBrowserDecorations(browser) {
  if (browser.isRemoteBrowser == undefined) {
    Object.defineProperty(browser, "isRemoteBrowser", {
      get() {
        return this.getAttribute("remote") == "true";
      },
      configurable: true,
      enumerable: true,
    });
  }
  if (browser.remoteType == undefined) {
    Object.defineProperty(browser, "remoteType", {
      get() {
        return this.getAttribute("remoteType");
      },
      configurable: true,
      enumerable: true,
    });
  }
  if (browser.messageManager == undefined) {
    Object.defineProperty(browser, "messageManager", {
      get() {
        return this.frameLoader.messageManager;
      },
      configurable: true,
      enumerable: true,
    });
  }
  if (browser.outerWindowID == undefined) {
    Object.defineProperty(browser, "outerWindowID", {
      get() {
        return browser._outerWindowID;
      },
      configurable: true,
      enumerable: true,
    });
  }

  // It's not necessary for these to actually do anything.  These properties are
  // swapped between browsers in browser.xml's `swapDocShells`, and then their
  // `swapBrowser` methods are called, so we define them here for that to work
  // without errors.  During the swap process above, these will move from the
  // the new inner browser to the original tab's browser (step 4) and then to
  // the temporary container tab's browser (step 7), which is then closed.
  if (browser._remoteWebNavigationImpl == undefined) {
    browser._remoteWebNavigationImpl = {
      swapBrowser() {},
    };
  }
  if (browser._remoteWebProgressManager == undefined) {
    browser._remoteWebProgressManager = {
      swapBrowser() {},
    };
  }
  if (browser._remoteWebProgress == undefined) {
    browser._remoteWebProgress = {
      addProgressListener() {},
      removeProgressListener() {},
    };
  }
}

function tabLoaded(tab) {
  return new Promise(resolve => {
    function handle(event) {
      if (event.originalTarget != tab.linkedBrowser.contentDocument ||
          event.target.location.href == "about:blank") {
        return;
      }
      tab.linkedBrowser.removeEventListener("load", handle, true);
      resolve(event);
    }

    tab.linkedBrowser.addEventListener("load", handle, true);
  });
}

exports.swapToInnerBrowser = swapToInnerBrowser;
PK
!<��.;|\|\Ichrome/devtools/modules/devtools/client/responsive.html/browser/tunnel.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Ci, Cu } = require("chrome");
const Services = require("Services");
const { BrowserElementWebNavigation } = require("./web-navigation");
const { getStack } = require("devtools/shared/platform/stack");

// A symbol used to hold onto the frame loader from the outer browser while tunneling.
const FRAME_LOADER = Symbol("devtools/responsive/frame-loader");
// Export for use in tests.
exports.OUTER_FRAME_LOADER_SYMBOL = FRAME_LOADER;

function debug(msg) {
  // console.log(msg);
}

/**
 * Properties swapped between browsers by browser.xml's `swapDocShells`.
 */
const SWAPPED_BROWSER_STATE = [
  "_remoteFinder",
  "_securityUI",
  "_documentURI",
  "_documentContentType",
  "_contentTitle",
  "_characterSet",
  "_contentPrincipal",
  "_imageDocument",
  "_fullZoom",
  "_textZoom",
  "_isSyntheticDocument",
  "_innerWindowID",
  "_manifestURI",
];

/**
 * Various parts of the Firefox code base expect to access properties on the browser
 * window in response to events (by reaching for the window via the event's target).
 *
 * When RDM is enabled, these bits of code instead reach the RDM tool's window instead of
 * the browser window, which won't have the properties they are looking for. At the
 * moment, we address this by exposing them from the browser window on RDM's window as
 * needed.
 */
const PROPERTIES_FROM_BROWSER_WINDOW = [
  // This is used by PermissionUI.jsm for permission doorhangers.
  "PopupNotifications",
  // These are used by ContentClick.jsm when opening links in ways other than just
  // navigating the viewport, such as a new tab by pressing Cmd-Click.
  "whereToOpenLink",
  "openLinkIn",
  // This is used by various event handlers, typically to call `getTabForBrowser` to map
  // a browser back to a tab.
  "gBrowser",
];

/**
 * This module takes an "outer" <xul:browser> from a browser tab as described by
 * Firefox's tabbrowser.xml and wires it up to an "inner" <iframe mozbrowser>
 * browser element containing arbitrary page content of interest.
 *
 * The inner <iframe mozbrowser> element is _just_ the page content.  It is not
 * enough to to replace <xul:browser> on its own.  <xul:browser> comes along
 * with lots of associated functionality via XBL bindings defined for such
 * elements in browser.xml and remote-browser.xml, and the Firefox UI depends on
 * these various things to make the UI function.
 *
 * By mapping various methods, properties, and messages from the outer browser
 * to the inner browser, we can control the content inside the inner browser
 * using the standard Firefox UI elements for navigation, reloading, and more.
 *
 * The approaches used in this module were chosen to avoid needing changes to
 * the core browser for this specialized use case.  If we start to increase
 * usage of <iframe mozbrowser> in the core browser, we should avoid this module
 * and instead refactor things to work with mozbrowser directly.
 *
 * For the moment though, this serves as a sufficient path to connect the
 * Firefox UI to a mozbrowser.
 *
 * @param outer
 *        A <xul:browser> from a regular browser tab.
 * @param inner
 *        A <iframe mozbrowser> containing page content to be wired up to the
 *        primary browser UI via the outer browser.
 */
function tunnelToInnerBrowser(outer, inner) {
  let browserWindow = outer.ownerDocument.defaultView;
  let gBrowser = browserWindow.gBrowser;
  let mmTunnel;

  return {

    async start() {
      if (outer.isRemoteBrowser) {
        throw new Error("The outer browser must be non-remote.");
      }
      if (!inner.isRemoteBrowser) {
        throw new Error("The inner browser must be remote.");
      }

      // Various browser methods access the `frameLoader` property, including:
      //   * `saveBrowser` from contentAreaUtils.js
      //   * `docShellIsActive` from remote-browser.xml
      //   * `hasContentOpener` from remote-browser.xml
      //   * `preserveLayers` from remote-browser.xml
      //   * `receiveMessage` from SessionStore.jsm
      // In general, these methods are interested in the `frameLoader` for the content,
      // so we redirect them to the inner browser's `frameLoader`.
      outer[FRAME_LOADER] = outer.frameLoader;
      Object.defineProperty(outer, "frameLoader", {
        get() {
          let stack = getStack();
          // One exception is `receiveMessage` from SessionStore.jsm.  SessionStore
          // expects data updates to come in as messages targeted to a <xul:browser>.
          // In addition, it verifies[1] correctness by checking that the received
          // message's `targetFrameLoader` property matches the `frameLoader` of the
          // <xul:browser>. To keep SessionStore functioning as expected, we give it the
          // outer `frameLoader` as if nothing has changed.
          // [1]: https://dxr.mozilla.org/mozilla-central/rev/b1b18f25c0ea69d9ee57c4198d577dfcd0129ce1/browser/components/sessionstore/SessionStore.jsm#716
          if (stack.caller.filename.endsWith("SessionStore.jsm")) {
            return outer[FRAME_LOADER];
          }
          return inner.frameLoader;
        },
        configurable: true,
        enumerable: true,
      });

      // The `outerWindowID` of the content is used by browser actions like view source
      // and print.  They send the ID down to the client to find the right content frame
      // to act on.
      Object.defineProperty(outer, "outerWindowID", {
        get() {
          return inner.outerWindowID;
        },
        configurable: true,
        enumerable: true,
      });

      // The `permanentKey` property on a <xul:browser> is used to index into various maps
      // held by the session store.  When you swap content around with
      // `_swapBrowserDocShells`, these keys are also swapped so they follow the content.
      // This means the key that matches the content is on the inner browser.  Since we
      // want the browser UI to believe the page content is part of the outer browser, we
      // copy the content's `permanentKey` up to the outer browser.
      debug("Copy inner permanentKey to outer browser");
      outer.permanentKey = inner.permanentKey;

      // Replace the outer browser's native messageManager with a message manager tunnel
      // which we can use to route messages of interest to the inner browser instead.
      // Note: The _actual_ messageManager accessible from
      // `browser.frameLoader.messageManager` is not overridable and is left unchanged.
      // Only the XBL getter `browser.messageManager` is overridden.  Browser UI code
      // always uses this getter instead of `browser.frameLoader.messageManager` directly,
      // so this has the effect of overriding the message manager for browser UI code.
      mmTunnel = new MessageManagerTunnel(outer, inner);

      // We are tunneling to an inner browser with a specific remoteness, so it is simpler
      // for the logic of the browser UI to assume this tab has taken on that remoteness,
      // even though it's not true.  Since the actions the browser UI performs are sent
      // down to the inner browser by this tunnel, the tab's remoteness effectively is the
      // remoteness of the inner browser.
      // Setting this attribute changes the browser to the remote XBL binding via CSS.
      // The XBL binding for remote browsers uses the message manager for many actions in
      // the UI and that works well here, since it gives us one main thing we need to
      // route to the inner browser (the messages), instead of having to tweak many
      // different browser properties.  It is safe to alter a XBL binding dynamically.
      // The content within is not reloaded.
      outer.setAttribute("remote", "true");
      outer.setAttribute("remoteType", inner.remoteType);

      // Clear out any cached state that references the current non-remote XBL binding,
      // such as form fill controllers.  Otherwise they will remain in place and leak the
      // outer docshell.
      outer.destroy();

      // The constructor of the new XBL binding is run asynchronously and there is no
      // event to signal its completion.  Spin an event loop to watch for properties that
      // are set by the contructor.
      Services.tm.spinEventLoopUntil(() => {
        return outer._remoteWebNavigation;
      });
      // Verify that we indeed have the correct binding.
      if (!outer.isRemoteBrowser) {
        throw new Error("Browser failed to switch to remote browser binding");
      }

      // Replace the `webNavigation` object with our own version which tries to use
      // mozbrowser APIs where possible.  This replaces the webNavigation object that the
      // remote-browser.xml binding creates.  We do not care about it's original value
      // because stop() will remove the remote-browser.xml binding and these will no
      // longer be used.
      let webNavigation = new BrowserElementWebNavigation(inner);
      webNavigation.copyStateFrom(inner._remoteWebNavigationImpl);
      outer._remoteWebNavigation = webNavigation;
      outer._remoteWebNavigationImpl = webNavigation;

      // Now that we've flipped to the remote browser XBL binding, add `progressListener`
      // onto the remote version of `webProgress`.  Normally tabbrowser.xml does this step
      // when it creates a new browser, etc.  Since we manually changed the XBL binding
      // above, it caused a fresh webProgress object to be created which does not have any
      // listeners added.  So, we get the listener that gBrowser is using for the tab and
      // reattach it here.
      let tab = gBrowser.getTabForBrowser(outer);
      let filteredProgressListener = gBrowser._tabFilters.get(tab);
      outer.webProgress.addProgressListener(filteredProgressListener);

      // Add the inner browser to tabbrowser's WeakMap from browser to tab.  This assists
      // with tabbrowser's processing of some events such as MozLayerTreeReady which
      // bubble up from the remote content frame and trigger tabbrowser to lookup the tab
      // associated with the browser that triggered the event.
      gBrowser._tabForBrowser.set(inner, tab);

      // All of the browser state from content was swapped onto the inner browser.  Pull
      // this state up to the outer browser.
      for (let property of SWAPPED_BROWSER_STATE) {
        outer[property] = inner[property];
      }

      // Expose various properties from the browser window on the RDM tool's global.  This
      // aids various bits of code that expect to find a browser window, such as event
      // handlers that reach for the window via the event's target.
      for (let property of PROPERTIES_FROM_BROWSER_WINDOW) {
        Object.defineProperty(inner.ownerGlobal, property, {
          get() {
            return outer.ownerGlobal[property];
          },
          configurable: true,
          enumerable: true,
        });
      }

      // Add mozbrowser event handlers
      inner.addEventListener("mozbrowseropenwindow", this);
    },

    handleEvent(event) {
      if (event.type != "mozbrowseropenwindow") {
        return;
      }

      // Minimal support for <a target/> and window.open() which just ensures we at
      // least open them somewhere (in a new tab).  The following things are ignored:
      //   * Specific target names (everything treated as _blank)
      //   * Window features
      //   * window.opener
      // These things are deferred for now, since content which does depend on them seems
      // outside the main focus of RDM.
      let { detail } = event;
      event.preventDefault();
      let uri = Services.io.newURI(detail.url);
      // This API is used mainly because it's near the path used for <a target/> with
      // regular browser tabs (which calls `openURIInFrame`).  The more elaborate APIs
      // that support openers, window features, etc. didn't seem callable from JS and / or
      // this event doesn't give enough info to use them.
      browserWindow.browserDOMWindow
        .openURI(uri, null, Ci.nsIBrowserDOMWindow.OPEN_NEWTAB,
                 Ci.nsIBrowserDOMWindow.OPEN_NEW,
                 outer.contentPrincipal);
    },

    stop() {
      let tab = gBrowser.getTabForBrowser(outer);
      let filteredProgressListener = gBrowser._tabFilters.get(tab);

      // The browser's state has changed over time while the tunnel was active.  Push the
      // the current state down to the inner browser, so that it follows the content in
      // case that browser will be swapped elsewhere.
      for (let property of SWAPPED_BROWSER_STATE) {
        inner[property] = outer[property];
      }

      // Remove the inner browser from the WeakMap from browser to tab.
      gBrowser._tabForBrowser.delete(inner);

      // Remove the progress listener we added manually.
      outer.webProgress.removeProgressListener(filteredProgressListener);

      // Reset the XBL binding back to the default.
      outer.destroy();

      // Reset @remote since this is now back to a regular, non-remote browser
      outer.setAttribute("remote", "false");
      outer.removeAttribute("remoteType");

      // Delete browser window properties exposed on content's owner global
      for (let property of PROPERTIES_FROM_BROWSER_WINDOW) {
        delete inner.ownerGlobal[property];
      }

      // Remove mozbrowser event handlers
      inner.removeEventListener("mozbrowseropenwindow", this);

      mmTunnel.destroy();
      mmTunnel = null;

      // Reset overridden XBL properties and methods.  Deleting the override
      // means it will fallback to the original XBL binding definitions which
      // are on the prototype.
      delete outer.frameLoader;
      delete outer[FRAME_LOADER];
      delete outer.outerWindowID;

      // Invalidate outer's permanentKey so that SessionStore stops associating
      // things that happen to the outer browser with the content inside in the
      // inner browser.
      outer.permanentKey = { id: "zombie" };

      browserWindow = null;
      gBrowser = null;
    },

  };
}

exports.tunnelToInnerBrowser = tunnelToInnerBrowser;

/**
 * This module allows specific messages of interest to be directed from the
 * outer browser to the inner browser (and vice versa) in a targetted fashion
 * without having to touch the original code paths that use them.
 */
function MessageManagerTunnel(outer, inner) {
  if (outer.isRemoteBrowser) {
    throw new Error("The outer browser must be non-remote.");
  }
  this.outerRef = Cu.getWeakReference(outer);
  this.innerRef = Cu.getWeakReference(inner);
  this.tunneledMessageNames = new Set();
  this.init();
}

MessageManagerTunnel.prototype = {

  /**
   * Most message manager methods are left alone and are just passed along to
   * the outer browser's real message manager.
   */
  PASS_THROUGH_METHODS: [
    "removeDelayedFrameScript",
    "getDelayedFrameScripts",
    "loadProcessScript",
    "removeDelayedProcessScript",
    "getDelayedProcessScripts",
    "addWeakMessageListener",
    "removeWeakMessageListener",
  ],

  /**
   * The following methods are overridden with special behavior while tunneling.
   */
  OVERRIDDEN_METHODS: [
    "loadFrameScript",
    "addMessageListener",
    "removeMessageListener",
    "sendAsyncMessage",
  ],

  OUTER_TO_INNER_MESSAGES: [
    // Messages sent from remote-browser.xml
    "Browser:PurgeSessionHistory",
    "InPermitUnload",
    "PermitUnload",
    // Messages sent from browser.js
    "Browser:Reload",
    // Messages sent from SelectParentHelper.jsm
    "Forms:DismissedDropDown",
    "Forms:MouseOut",
    "Forms:MouseOver",
    "Forms:SelectDropDownItem",
    // Messages sent from SessionStore.jsm
    "SessionStore:flush",
  ],

  INNER_TO_OUTER_MESSAGES: [
    // Messages sent to RemoteWebProgress.jsm
    "Content:LoadURIResult",
    "Content:LocationChange",
    "Content:ProgressChange",
    "Content:SecurityChange",
    "Content:StateChange",
    "Content:StatusChange",
    // Messages sent to remote-browser.xml
    "DOMTitleChanged",
    "ImageDocumentLoaded",
    "Forms:ShowDropDown",
    "Forms:HideDropDown",
    "InPermitUnload",
    "PermitUnload",
    // Messages sent to tabbrowser.xml
    "contextmenu",
    // Messages sent to SelectParentHelper.jsm
    "Forms:UpdateDropDown",
    // Messages sent to browser.js
    "PageVisibility:Hide",
    "PageVisibility:Show",
    // Messages sent to SessionStore.jsm
    "SessionStore:update",
    // Messages sent to BrowserTestUtils.jsm
    "browser-test-utils:loadEvent",
  ],

  OUTER_TO_INNER_MESSAGE_PREFIXES: [
    // Messages sent from nsContextMenu.js
    "ContextMenu:",
    // Messages sent from DevTools
    "debug:",
    // Messages sent from findbar.xml
    "Findbar:",
    // Messages sent from RemoteFinder.jsm
    "Finder:",
    // Messages sent from InlineSpellChecker.jsm
    "InlineSpellChecker:",
    // Messages sent from MessageChannel.jsm
    "MessageChannel:",
    // Messages sent from pageinfo.js
    "PageInfo:",
    // Messages sent from printUtils.js
    "Printing:",
    "PageMetadata:",
    // Messages sent from viewSourceUtils.js
    "ViewSource:",
  ],

  INNER_TO_OUTER_MESSAGE_PREFIXES: [
    // Messages sent to nsContextMenu.js
    "ContextMenu:",
    // Messages sent to DevTools
    "debug:",
    // Messages sent to findbar.xml
    "Findbar:",
    // Messages sent to RemoteFinder.jsm
    "Finder:",
    // Messages sent to MessageChannel.jsm
    "MessageChannel:",
    // Messages sent to pageinfo.js
    "PageInfo:",
    // Messages sent to printUtils.js
    "Printing:",
    "PageMetadata:",
    // Messages sent to viewSourceUtils.js
    "ViewSource:",
  ],

  OUTER_TO_INNER_FRAME_SCRIPTS: [
    // DevTools server for OOP frames
    "resource://devtools/server/child.js"
  ],

  get outer() {
    return this.outerRef.get();
  },

  get outerParentMM() {
    if (!this.outer[FRAME_LOADER]) {
      return null;
    }
    return this.outer[FRAME_LOADER].messageManager;
  },

  get outerChildMM() {
    // This is only possible because we require the outer browser to be
    // non-remote, so we're able to reach into its window and use the child
    // side message manager there.
    let docShell = this.outer[FRAME_LOADER].docShell;
    return docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                   .getInterface(Ci.nsIContentFrameMessageManager);
  },

  get inner() {
    return this.innerRef.get();
  },

  get innerParentMM() {
    if (!this.inner.frameLoader) {
      return null;
    }
    return this.inner.frameLoader.messageManager;
  },

  init() {
    for (let method of this.PASS_THROUGH_METHODS) {
      this[method] = (...args) => {
        if (!this.outerParentMM) {
          return null;
        }
        return this.outerParentMM[method](...args);
      };
    }

    for (let name of this.INNER_TO_OUTER_MESSAGES) {
      this.innerParentMM.addMessageListener(name, this);
      this.tunneledMessageNames.add(name);
    }

    Services.obs.addObserver(this, "message-manager-close");

    // Replace the outer browser's messageManager with this tunnel
    Object.defineProperty(this.outer, "messageManager", {
      value: this,
      writable: false,
      configurable: true,
      enumerable: true,
    });
  },

  destroy() {
    if (this.destroyed) {
      return;
    }
    this.destroyed = true;
    debug("Destroy tunnel");

    // Watch for the messageManager to close.  In most cases, the caller will stop the
    // tunnel gracefully before this, but when the browser window closes or application
    // exits, we may not see the high-level close events.
    Services.obs.removeObserver(this, "message-manager-close");

    // Reset the messageManager.  Deleting the override means it will fallback to the
    // original XBL binding definitions which are on the prototype.
    delete this.outer.messageManager;

    for (let name of this.tunneledMessageNames) {
      this.innerParentMM.removeMessageListener(name, this);
    }

    // Some objects may have cached this tunnel as the messageManager for a frame.  To
    // ensure it keeps working after tunnel close, rewrite the overidden methods as pass
    // through methods.
    for (let method of this.OVERRIDDEN_METHODS) {
      this[method] = (...args) => {
        if (!this.outerParentMM) {
          return null;
        }
        return this.outerParentMM[method](...args);
      };
    }
  },

  observe(subject, topic, data) {
    if (topic != "message-manager-close") {
      return;
    }
    if (subject == this.innerParentMM) {
      debug("Inner messageManager has closed");
      this.destroy();
    }
    if (subject == this.outerParentMM) {
      debug("Outer messageManager has closed");
      this.destroy();
    }
  },

  loadFrameScript(url, ...args) {
    debug(`Calling loadFrameScript for ${url}`);

    if (!this.OUTER_TO_INNER_FRAME_SCRIPTS.includes(url)) {
      debug(`Should load ${url} into inner?`);
      this.outerParentMM.loadFrameScript(url, ...args);
      return;
    }

    debug(`Load ${url} into inner`);
    this.innerParentMM.loadFrameScript(url, ...args);
  },

  addMessageListener(name, ...args) {
    debug(`Calling addMessageListener for ${name}`);

    debug(`Add outer listener for ${name}`);
    // Add an outer listener, just like a simple pass through
    this.outerParentMM.addMessageListener(name, ...args);

    // If the message name is part of a prefix we're tunneling, we also need to add the
    // tunnel as an inner listener.
    if (this.INNER_TO_OUTER_MESSAGE_PREFIXES.some(prefix => name.startsWith(prefix))) {
      debug(`Add inner listener for ${name}`);
      this.innerParentMM.addMessageListener(name, this);
      this.tunneledMessageNames.add(name);
    }
  },

  removeMessageListener(name, ...args) {
    debug(`Calling removeMessageListener for ${name}`);

    debug(`Remove outer listener for ${name}`);
    // Remove an outer listener, just like a simple pass through
    this.outerParentMM.removeMessageListener(name, ...args);

    // Leave the tunnel as an inner listener for the case of prefix messages to avoid
    // tracking counts of add calls.  The inner listener will get removed on destroy.
  },

  sendAsyncMessage(name, ...args) {
    debug(`Calling sendAsyncMessage for ${name}`);

    if (!this._shouldTunnelOuterToInner(name)) {
      debug(`Should ${name} go to inner?`);
      this.outerParentMM.sendAsyncMessage(name, ...args);
      return;
    }

    debug(`${name} outer -> inner`);
    this.innerParentMM.sendAsyncMessage(name, ...args);
  },

  receiveMessage({ name, data, objects, principal, sync }) {
    if (!this._shouldTunnelInnerToOuter(name)) {
      debug(`Received unexpected message ${name}`);
      return undefined;
    }

    debug(`${name} inner -> outer, sync: ${sync}`);
    if (sync) {
      return this.outerChildMM.sendSyncMessage(name, data, objects, principal);
    }
    this.outerChildMM.sendAsyncMessage(name, data, objects, principal);
    return undefined;
  },

  _shouldTunnelOuterToInner(name) {
    return this.OUTER_TO_INNER_MESSAGES.includes(name) ||
           this.OUTER_TO_INNER_MESSAGE_PREFIXES.some(prefix => name.startsWith(prefix));
  },

  _shouldTunnelInnerToOuter(name) {
    return this.INNER_TO_OUTER_MESSAGES.includes(name) ||
           this.INNER_TO_OUTER_MESSAGE_PREFIXES.some(prefix => name.startsWith(prefix));
  },

  toString() {
    return "[object MessageManagerTunnel]";
  },

};
PK
!<���C55Qchrome/devtools/modules/devtools/client/responsive.html/browser/web-navigation.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Ci, Cu, Cr } = require("chrome");
const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
const Services = require("Services");
const { NetUtil } = require("resource://gre/modules/NetUtil.jsm");
const { Utils } = require("resource://gre/modules/sessionstore/Utils.jsm");

function readInputStreamToString(stream) {
  return NetUtil.readInputStreamToString(stream, stream.available());
}

/**
 * This object aims to provide the nsIWebNavigation interface for mozbrowser elements.
 * nsIWebNavigation is one of the interfaces expected on <xul:browser>s, so this wrapper
 * helps mozbrowser elements support this.
 *
 * It attempts to use the mozbrowser API wherever possible, however some methods don't
 * exist yet, so we fallback to the WebNavigation frame script messages in those cases.
 * Ideally the mozbrowser API would eventually be extended to cover all properties and
 * methods used here.
 *
 * This is largely copied from RemoteWebNavigation.js, which uses the message manager to
 * perform all actions.
 */
function BrowserElementWebNavigation(browser) {
  this._browser = browser;
}

BrowserElementWebNavigation.prototype = {

  QueryInterface: XPCOMUtils.generateQI([
    Ci.nsIWebNavigation,
    Ci.nsISupports
  ]),

  get _mm() {
    return this._browser.frameLoader.messageManager;
  },

  canGoBack: false,
  canGoForward: false,

  goBack() {
    this._browser.goBack();
  },

  goForward() {
    this._browser.goForward();
  },

  gotoIndex(index) {
    // No equivalent in the current BrowserElement API
    this._sendMessage("WebNavigation:GotoIndex", { index });
  },

  loadURI(uri, flags, referrer, postData, headers) {
    // No equivalent in the current BrowserElement API
    this.loadURIWithOptions(uri, flags, referrer,
                            Ci.nsIHttpChannel.REFERRER_POLICY_UNSET,
                            postData, headers, null, null);
  },

  loadURIWithOptions(uri, flags, referrer, referrerPolicy, postData, headers,
                     baseURI, triggeringPrincipal) {
    // No equivalent in the current BrowserElement API
    this._sendMessage("WebNavigation:LoadURI", {
      uri,
      flags,
      referrer: referrer ? referrer.spec : null,
      referrerPolicy: referrerPolicy,
      postData: postData ? readInputStreamToString(postData) : null,
      headers: headers ? readInputStreamToString(headers) : null,
      baseURI: baseURI ? baseURI.spec : null,
      triggeringPrincipal: triggeringPrincipal
                           ? Utils.serializePrincipal(triggeringPrincipal)
                           : null,
      requestTime: Services.telemetry.msSystemNow(),
    });
  },

  setOriginAttributesBeforeLoading(originAttributes) {
    // No equivalent in the current BrowserElement API
    this._sendMessage("WebNavigation:SetOriginAttributes", {
      originAttributes,
    });
  },

  reload(flags) {
    let hardReload = false;
    if (flags & this.LOAD_FLAGS_BYPASS_PROXY ||
        flags & this.LOAD_FLAGS_BYPASS_CACHE) {
      hardReload = true;
    }
    this._browser.reload(hardReload);
  },

  stop(flags) {
    // No equivalent in the current BrowserElement API
    this._sendMessage("WebNavigation:Stop", { flags });
  },

  get document() {
    return this._browser.contentDocument;
  },

  _currentURI: null,
  get currentURI() {
    if (!this._currentURI) {
      this._currentURI = Services.io.newURI("about:blank");
    }
    return this._currentURI;
  },
  set currentURI(uri) {
    this._browser.src = uri.spec;
  },

  referringURI: null,

  // Bug 1233803 - accessing the sessionHistory of remote browsers should be
  // done in content scripts.
  get sessionHistory() {
    throw Cr.NS_ERROR_NOT_IMPLEMENTED;
  },
  set sessionHistory(value) {
    throw Cr.NS_ERROR_NOT_IMPLEMENTED;
  },

  _sendMessage(message, data) {
    try {
      this._mm.sendAsyncMessage(message, data);
    } catch (e) {
      Cu.reportError(e);
    }
  },

  swapBrowser(browser) {
    throw Cr.NS_ERROR_NOT_IMPLEMENTED;
  },

  copyStateFrom(otherWebNavigation) {
    const state = [
      "canGoBack",
      "canGoForward",
      "_currentURI",
    ];
    for (let property of state) {
      this[property] = otherWebNavigation[property];
    }
  },

};

const FLAGS = [
  "LOAD_FLAGS_MASK",
  "LOAD_FLAGS_NONE",
  "LOAD_FLAGS_IS_REFRESH",
  "LOAD_FLAGS_IS_LINK",
  "LOAD_FLAGS_BYPASS_HISTORY",
  "LOAD_FLAGS_REPLACE_HISTORY",
  "LOAD_FLAGS_BYPASS_CACHE",
  "LOAD_FLAGS_BYPASS_PROXY",
  "LOAD_FLAGS_CHARSET_CHANGE",
  "LOAD_FLAGS_STOP_CONTENT",
  "LOAD_FLAGS_FROM_EXTERNAL",
  "LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP",
  "LOAD_FLAGS_FIRST_LOAD",
  "LOAD_FLAGS_ALLOW_POPUPS",
  "LOAD_FLAGS_BYPASS_CLASSIFIER",
  "LOAD_FLAGS_FORCE_ALLOW_COOKIES",
  "STOP_NETWORK",
  "STOP_CONTENT",
  "STOP_ALL",
];

for (let flag of FLAGS) {
  BrowserElementWebNavigation.prototype[flag] = Ci.nsIWebNavigation[flag];
}

exports.BrowserElementWebNavigation = BrowserElementWebNavigation;
PK
!<�����Cchrome/devtools/modules/devtools/client/responsive.html/commands.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Services = require("Services");

loader.lazyRequireGetter(this, "ResponsiveUIManager", "devtools/client/responsive.html/manager", true);

const BRAND_SHORT_NAME = Services.strings.createBundle("chrome://branding/locale/brand.properties")
                                 .GetStringFromName("brandShortName");

const osString = Services.appinfo.OS;
const l10n = require("gcli/l10n");

exports.items = [
  {
    name: "resize",
    description: l10n.lookup("resizeModeDesc")
  },
  {
    item: "command",
    runAt: "client",
    name: "resize on",
    description: l10n.lookup("resizeModeOnDesc"),
    manual: l10n.lookupFormat("resizeModeManual2", [BRAND_SHORT_NAME]),
    exec: resize
  },
  {
    item: "command",
    runAt: "client",
    name: "resize off",
    description: l10n.lookup("resizeModeOffDesc"),
    manual: l10n.lookupFormat("resizeModeManual2", [BRAND_SHORT_NAME]),
    exec: resize
  },
  {
    item: "command",
    runAt: "client",
    name: "resize toggle",
    buttonId: "command-button-responsive",
    buttonClass: "command-button",
    tooltipText: l10n.lookupFormat(
      "resizeModeToggleTooltip2",
      [(osString == "Darwin" ? "Cmd+Opt+M" : "Ctrl+Shift+M")]
    ),
    description: l10n.lookup("resizeModeToggleDesc"),
    manual: l10n.lookupFormat("resizeModeManual2", [BRAND_SHORT_NAME]),
    state: {
      isChecked: function (target) {
        if (!target.tab) {
          return false;
        }
        return ResponsiveUIManager.isActiveForTab(target.tab);
      },
      onChange: function (target, changeHandler) {
        if (target.tab) {
          ResponsiveUIManager.on("on", changeHandler);
          ResponsiveUIManager.on("off", changeHandler);
        }
      },
      offChange: function (target, changeHandler) {
        // Do not check for target.tab as it may already be null during destroy
        ResponsiveUIManager.off("on", changeHandler);
        ResponsiveUIManager.off("off", changeHandler);
      },
    },
    exec: resize
  },
  {
    item: "command",
    runAt: "client",
    name: "resize to",
    description: l10n.lookup("resizeModeToDesc"),
    params: [
      {
        name: "width",
        type: "number",
        description: l10n.lookup("resizePageArgWidthDesc"),
      },
      {
        name: "height",
        type: "number",
        description: l10n.lookup("resizePageArgHeightDesc"),
      },
    ],
    exec: resize
  }
];

async function resize(args, context) {
  let browserWindow = context.environment.chromeWindow;
  await ResponsiveUIManager.handleGcliCommand(browserWindow,
                                              browserWindow.gBrowser.selectedTab,
                                              this.name,
                                              args);
}
PK
!<l>�qMchrome/devtools/modules/devtools/client/responsive.html/components/Browser.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 Services = require("Services");
const flags = require("devtools/shared/flags");
const { PureComponent } = 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 e10s = require("../utils/e10s");
const message = require("../utils/message");
const { getToplevelWindow } = require("../utils/window");

const FRAME_SCRIPT = "resource://devtools/client/responsive.html/browser/content.js";

class Browser extends PureComponent {
  /**
   * This component is not allowed to depend directly on frequently changing data (width,
   * height). Any changes in props would cause the <iframe> to be removed and added again,
   * throwing away the current state of the page.
   */
  static get propTypes() {
    return {
      swapAfterMount: PropTypes.bool.isRequired,
      onBrowserMounted: PropTypes.func.isRequired,
      onContentResize: PropTypes.func.isRequired,
    };
  }

  constructor(props) {
    super(props);
    this.onContentResize = this.onContentResize.bind(this);
  }

  /**
   * Before the browser is mounted, listen for `remote-browser-shown` so that we know when
   * the browser is fully ready.  Without waiting for an event such as this, we don't know
   * whether all frame state for the browser is fully initialized (since some happens
   * async after the element is added), and swapping browsers can fail if this state is
   * not ready.
   */
  componentWillMount() {
    this.browserShown = new Promise(resolve => {
      let handler = frameLoader => {
        if (frameLoader.ownerElement != this.browser) {
          return;
        }
        Services.obs.removeObserver(handler, "remote-browser-shown");
        resolve();
      };
      Services.obs.addObserver(handler, "remote-browser-shown");
    });
  }

  /**
   * Once the browser element has mounted, load the frame script and enable
   * various features, like floating scrollbars.
   */
  async componentDidMount() {
    // If we are not swapping browsers after mount, it's safe to start the frame
    // script now.
    if (!this.props.swapAfterMount) {
      await this.startFrameScript();
    }

    // Notify manager.js that this browser has mounted, so that it can trigger
    // a swap if needed and continue with the rest of its startup.
    await this.browserShown;
    this.props.onBrowserMounted();

    // If we are swapping browsers after mount, wait for the swap to complete
    // and start the frame script after that.
    if (this.props.swapAfterMount) {
      await message.wait(window, "start-frame-script");
      await this.startFrameScript();
      message.post(window, "start-frame-script:done");
    }

    // Stop the frame script when requested in the future.
    message.wait(window, "stop-frame-script").then(() => {
      this.stopFrameScript();
    });
  }

  onContentResize(msg) {
    let { onContentResize } = this.props;
    let { width, height } = msg.data;
    onContentResize({
      width,
      height,
    });
  }

  async startFrameScript() {
    let {
      browser,
      onContentResize,
    } = this;
    let mm = browser.frameLoader.messageManager;

    // Notify tests when the content has received a resize event.  This is not
    // quite the same timing as when we _set_ a new size around the browser,
    // since it still needs to do async work before the content is actually
    // resized to match.
    e10s.on(mm, "OnContentResize", onContentResize);

    let ready = e10s.once(mm, "ChildScriptReady");
    mm.loadFrameScript(FRAME_SCRIPT, true);
    await ready;

    let browserWindow = getToplevelWindow(window);
    let requiresFloatingScrollbars =
      !browserWindow.matchMedia("(-moz-overlay-scrollbars)").matches;

    await e10s.request(mm, "Start", {
      requiresFloatingScrollbars,
      // Tests expect events on resize to wait for various size changes
      notifyOnResize: flags.testing,
    });
  }

  async stopFrameScript() {
    let {
      browser,
      onContentResize,
    } = this;
    let mm = browser.frameLoader.messageManager;

    e10s.off(mm, "OnContentResize", onContentResize);
    await e10s.request(mm, "Stop");
    message.post(window, "stop-frame-script:done");
  }

  render() {
    // In the case of @remote and @remoteType, the attribute must be set before the
    // element is added to the DOM to have any effect, which we are able to do with this
    // approach.
    //
    // @noisolation and @allowfullscreen are needed so that these frames have the same
    // access to browser features as regular browser tabs. The `swapFrameLoaders` platform
    // API we use compares such features before allowing the swap to proceed.
    return dom.iframe(
      {
        allowFullScreen: "true",
        className: "browser",
        height: "100%",
        mozbrowser: "true",
        noisolation: "true",
        remote: "true",
        remotetype: "web",
        src: "about:blank",
        width: "100%",
        ref: browser => {
          this.browser = browser;
        },
      }
    );
  }
}

module.exports = Browser;
PK
!<XvG���Qchrome/devtools/modules/devtools/client/responsive.html/components/DeviceAdder.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 { PureComponent, createFactory } = 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 { getFormatStr, getStr } = require("../utils/l10n");
const Types = require("../types");
const ViewportDimension = createFactory(require("./ViewportDimension.js"));

class DeviceAdder extends PureComponent {
  static get propTypes() {
    return {
      devices: PropTypes.shape(Types.devices).isRequired,
      viewportTemplate: PropTypes.shape(Types.viewport).isRequired,
      onAddCustomDevice: PropTypes.func.isRequired,
    };
  }

  constructor(props) {
    super(props);
    this.state = {};
    this.onChangeSize = this.onChangeSize.bind(this);
    this.onDeviceAdderShow = this.onDeviceAdderShow.bind(this);
    this.onDeviceAdderSave = this.onDeviceAdderSave.bind(this);
  }

  componentWillReceiveProps(nextProps) {
    let {
      width,
      height,
    } = nextProps.viewportTemplate;

    this.setState({
      width,
      height,
    });
  }

  onChangeSize(width, height) {
    this.setState({
      width,
      height,
    });
  }

  onDeviceAdderShow() {
    this.setState({
      deviceAdderDisplayed: true,
    });
  }

  onDeviceAdderSave() {
    let {
      devices,
      onAddCustomDevice,
    } = this.props;

    if (!this.pixelRatioInput.checkValidity()) {
      return;
    }
    if (devices.custom.find(device => device.name == this.nameInput.value)) {
      this.nameInput.setCustomValidity("Device name already in use");
      return;
    }

    this.setState({
      deviceAdderDisplayed: false,
    });
    onAddCustomDevice({
      name: this.nameInput.value,
      width: this.state.width,
      height: this.state.height,
      pixelRatio: parseFloat(this.pixelRatioInput.value),
      userAgent: this.userAgentInput.value,
      touch: this.touchInput.checked,
    });
  }

  render() {
    let {
      devices,
      viewportTemplate,
    } = this.props;

    let {
      deviceAdderDisplayed,
      height,
      width,
    } = this.state;

    if (!deviceAdderDisplayed) {
      return dom.div(
        {
          id: "device-adder"
        },
        dom.button(
          {
            id: "device-adder-show",
            onClick: this.onDeviceAdderShow,
          },
          getStr("responsive.addDevice")
        )
      );
    }

    // If a device is currently selected, fold its attributes into a single object for use
    // as the starting values of the form.  If no device is selected, use the values for
    // the current window.
    let deviceName;
    let normalizedViewport = Object.assign({}, viewportTemplate);
    if (viewportTemplate.device) {
      let device = devices[viewportTemplate.deviceType].find(d => {
        return d.name == viewportTemplate.device;
      });
      deviceName = getFormatStr("responsive.customDeviceNameFromBase", device.name);
      Object.assign(normalizedViewport, {
        pixelRatio: device.pixelRatio,
        userAgent: device.userAgent,
        touch: device.touch,
      });
    } else {
      deviceName = getStr("responsive.customDeviceName");
      Object.assign(normalizedViewport, {
        pixelRatio: window.devicePixelRatio,
        userAgent: navigator.userAgent,
        touch: false,
      });
    }

    return dom.div(
      {
        id: "device-adder"
      },
      dom.div(
        {
          id: "device-adder-content",
        },
        dom.div(
          {
            id: "device-adder-column-1",
          },
          dom.label(
            {
              id: "device-adder-name",
            },
            dom.span(
              {
                className: "device-adder-label",
              },
              getStr("responsive.deviceAdderName")
            ),
            dom.input({
              defaultValue: deviceName,
              ref: input => {
                this.nameInput = input;
              },
            })
          ),
          dom.label(
            {
              id: "device-adder-size",
            },
            dom.span(
              {
                className: "device-adder-label"
              },
              getStr("responsive.deviceAdderSize")
            ),
            ViewportDimension({
              viewport: {
                width,
                height,
              },
              onChangeSize: this.onChangeSize,
              onRemoveDeviceAssociation: () => {},
            })
          ),
          dom.label(
            {
              id: "device-adder-pixel-ratio",
            },
            dom.span(
              {
                className: "device-adder-label"
              },
              getStr("responsive.deviceAdderPixelRatio")
            ),
            dom.input({
              type: "number",
              step: "any",
              defaultValue: normalizedViewport.pixelRatio,
              ref: input => {
                this.pixelRatioInput = input;
              },
            })
          )
        ),
        dom.div(
          {
            id: "device-adder-column-2",
          },
          dom.label(
            {
              id: "device-adder-user-agent",
            },
            dom.span(
              {
                className: "device-adder-label"
              },
              getStr("responsive.deviceAdderUserAgent")
            ),
            dom.input({
              defaultValue: normalizedViewport.userAgent,
              ref: input => {
                this.userAgentInput = input;
              },
            })
          ),
          dom.label(
            {
              id: "device-adder-touch",
            },
            dom.span(
              {
                className: "device-adder-label"
              },
              getStr("responsive.deviceAdderTouch")
            ),
            dom.input({
              defaultChecked: normalizedViewport.touch,
              type: "checkbox",
              ref: input => {
                this.touchInput = input;
              },
            })
          )
        ),
      ),
      dom.button(
        {
          id: "device-adder-save",
          onClick: this.onDeviceAdderSave,
        },
        getStr("responsive.deviceAdderSave")
      )
    );
  }
}

module.exports = DeviceAdder;
PK
!<G�`��Qchrome/devtools/modules/devtools/client/responsive.html/components/DeviceModal.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 { PureComponent, createFactory } = 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 { getStr, getFormatStr } = require("../utils/l10n");
const Types = require("../types");
const DeviceAdder = createFactory(require("./DeviceAdder"));

class DeviceModal extends PureComponent {
  static get propTypes() {
    return {
      deviceAdderViewportTemplate: PropTypes.shape(Types.viewport).isRequired,
      devices: PropTypes.shape(Types.devices).isRequired,
      onAddCustomDevice: PropTypes.func.isRequired,
      onDeviceListUpdate: PropTypes.func.isRequired,
      onRemoveCustomDevice: PropTypes.func.isRequired,
      onUpdateDeviceDisplayed: PropTypes.func.isRequired,
      onUpdateDeviceModal: PropTypes.func.isRequired,
    };
  }

  constructor(props) {
    super(props);
    this.state = {};
    this.onAddCustomDevice = this.onAddCustomDevice.bind(this);
    this.onDeviceCheckboxChange = this.onDeviceCheckboxChange.bind(this);
    this.onDeviceModalSubmit = this.onDeviceModalSubmit.bind(this);
    this.onKeyDown = this.onKeyDown.bind(this);
  }

  componentDidMount() {
    window.addEventListener("keydown", this.onKeyDown, true);
  }

  componentWillReceiveProps(nextProps) {
    let {
      devices: oldDevices,
    } = this.props;
    let {
      devices,
    } = nextProps;

    // Refresh component state only when model transitions from closed to open
    if (!oldDevices.isModalOpen && devices.isModalOpen) {
      for (let type of devices.types) {
        for (let device of devices[type]) {
          this.setState({
            [device.name]: device.displayed,
          });
        }
      }
    }
  }

  componentWillUnmount() {
    window.removeEventListener("keydown", this.onKeyDown, true);
  }

  onAddCustomDevice(device) {
    this.props.onAddCustomDevice(device);
    // Default custom devices to enabled
    this.setState({
      [device.name]: true,
    });
  }

  onDeviceCheckboxChange({ nativeEvent: { button }, target }) {
    if (button !== 0) {
      return;
    }
    this.setState({
      [target.value]: !this.state[target.value]
    });
  }

  onDeviceModalSubmit() {
    let {
      devices,
      onDeviceListUpdate,
      onUpdateDeviceDisplayed,
      onUpdateDeviceModal,
    } = this.props;

    let preferredDevices = {
      "added": new Set(),
      "removed": new Set(),
    };

    for (let type of devices.types) {
      for (let device of devices[type]) {
        let newState = this.state[device.name];

        if (device.featured && !newState) {
          preferredDevices.removed.add(device.name);
        } else if (!device.featured && newState) {
          preferredDevices.added.add(device.name);
        }

        if (this.state[device.name] != device.displayed) {
          onUpdateDeviceDisplayed(device, type, this.state[device.name]);
        }
      }
    }

    onDeviceListUpdate(preferredDevices);
    onUpdateDeviceModal(false);
  }

  onKeyDown(event) {
    if (!this.props.devices.isModalOpen) {
      return;
    }
    // Escape keycode
    if (event.keyCode === 27) {
      let {
        onUpdateDeviceModal
      } = this.props;
      onUpdateDeviceModal(false);
    }
  }

  render() {
    let {
      deviceAdderViewportTemplate,
      devices,
      onRemoveCustomDevice,
      onUpdateDeviceModal,
    } = this.props;

    let {
      onAddCustomDevice,
    } = this;

    const sortedDevices = {};
    for (let type of devices.types) {
      sortedDevices[type] = Object.assign([], devices[type])
        .sort((a, b) => a.name.localeCompare(b.name));
    }

    return dom.div(
      {
        id: "device-modal-wrapper",
        className: this.props.devices.isModalOpen ? "opened" : "closed",
      },
      dom.div(
        {
          className: "device-modal container",
        },
        dom.button({
          id: "device-close-button",
          className: "toolbar-button devtools-button",
          onClick: () => onUpdateDeviceModal(false),
        }),
        dom.div(
          {
            className: "device-modal-content",
          },
          devices.types.map(type => {
            return dom.div(
              {
                className: `device-type device-type-${type}`,
                key: type,
              },
              dom.header(
                {
                  className: "device-header",
                },
                type
              ),
              sortedDevices[type].map(device => {
                let details = getFormatStr(
                  "responsive.deviceDetails", device.width, device.height,
                  device.pixelRatio, device.userAgent, device.touch
                );

                let removeDeviceButton;
                if (type == "custom") {
                  removeDeviceButton = dom.button({
                    className: "device-remove-button toolbar-button devtools-button",
                    onClick: () => onRemoveCustomDevice(device),
                  });
                }

                return dom.label(
                  {
                    className: "device-label",
                    key: device.name,
                    title: details,
                  },
                  dom.input({
                    className: "device-input-checkbox",
                    type: "checkbox",
                    value: device.name,
                    checked: this.state[device.name],
                    onChange: this.onDeviceCheckboxChange,
                  }),
                  dom.span(
                    {
                      className: "device-name",
                    },
                    device.name
                  ),
                  removeDeviceButton
                );
              })
            );
          })
        ),
        DeviceAdder({
          devices,
          viewportTemplate: deviceAdderViewportTemplate,
          onAddCustomDevice,
        }),
        dom.button(
          {
            id: "device-submit-button",
            onClick: this.onDeviceModalSubmit,
          },
          getStr("responsive.done")
        )
      ),
      dom.div(
        {
          className: "modal-overlay",
          onClick: () => onUpdateDeviceModal(false),
        }
      )
    );
  }
}

module.exports = DeviceModal;
PK
!<���

^chrome/devtools/modules/devtools/client/responsive.html/components/DevicePixelRatioSelector.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 { PureComponent} = 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");

const Types = require("../types");
const { getStr, getFormatStr } = require("../utils/l10n");
const labelForOption = value => getFormatStr("responsive.devicePixelRatioOption", value);

const PIXEL_RATIO_PRESET = [1, 2, 3];

const createVisibleOption = value => {
  let label = labelForOption(value);
  return dom.option({
    value,
    title: label,
    key: value,
  }, label);
};

const createHiddenOption = value => {
  let label = labelForOption(value);
  return dom.option({
    value,
    title: label,
    hidden: true,
    disabled: true,
  }, label);
};

class DevicePixelRatioSelector extends PureComponent {
  static get propTypes() {
    return {
      devices: PropTypes.shape(Types.devices).isRequired,
      displayPixelRatio: Types.pixelRatio.value.isRequired,
      selectedDevice: PropTypes.string.isRequired,
      selectedPixelRatio: PropTypes.shape(Types.pixelRatio).isRequired,
      onChangePixelRatio: PropTypes.func.isRequired,
    };
  }

  constructor(props) {
    super(props);

    this.state = {
      isFocused: false
    };

    this.onFocusChange = this.onFocusChange.bind(this);
    this.onSelectChange = this.onSelectChange.bind(this);
  }

  onFocusChange({type}) {
    this.setState({
      isFocused: type === "focus"
    });
  }

  onSelectChange({ target }) {
    this.props.onChangePixelRatio(+target.value);
  }

  render() {
    let {
      devices,
      displayPixelRatio,
      selectedDevice,
      selectedPixelRatio,
    } = this.props;

    let hiddenOptions = [];

    for (let type of devices.types) {
      for (let device of devices[type]) {
        if (device.displayed &&
            !hiddenOptions.includes(device.pixelRatio) &&
            !PIXEL_RATIO_PRESET.includes(device.pixelRatio)) {
          hiddenOptions.push(device.pixelRatio);
        }
      }
    }

    if (!PIXEL_RATIO_PRESET.includes(displayPixelRatio)) {
      hiddenOptions.push(displayPixelRatio);
    }

    let state = devices.listState;
    let isDisabled = (state !== Types.loadableState.LOADED) || (selectedDevice !== "");
    let selectorClass = "toolbar-dropdown";
    let title;

    if (isDisabled) {
      selectorClass += " disabled";
      title = getFormatStr("responsive.devicePixelRatio.auto", selectedDevice);
    } else {
      title = getStr("responsive.changeDevicePixelRatio");

      if (selectedPixelRatio.value) {
        selectorClass += " selected";
      }
    }

    if (this.state.isFocused) {
      selectorClass += " focused";
    }

    let listContent = PIXEL_RATIO_PRESET.map(createVisibleOption);

    if (state == Types.loadableState.LOADED) {
      listContent = listContent.concat(hiddenOptions.map(createHiddenOption));
    }

    return dom.select(
      {
        id: "global-device-pixel-ratio-selector",
        value: selectedPixelRatio.value || displayPixelRatio,
        disabled: isDisabled,
        onChange: this.onSelectChange,
        onFocus: this.onFocusChange,
        onBlur: this.onFocusChange,
        className: selectorClass,
        title: title
      },
      ...listContent
    );
  }
}

module.exports = DevicePixelRatioSelector;
PK
!<��
�
Tchrome/devtools/modules/devtools/client/responsive.html/components/DeviceSelector.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { getStr } = require("../utils/l10n");
const { PureComponent } = 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 Types = require("../types");
const OPEN_DEVICE_MODAL_VALUE = "OPEN_DEVICE_MODAL";

class DeviceSelector extends PureComponent {
  static get propTypes() {
    return {
      devices: PropTypes.shape(Types.devices).isRequired,
      selectedDevice: PropTypes.string.isRequired,
      viewportId: PropTypes.number.isRequired,
      onChangeDevice: PropTypes.func.isRequired,
      onResizeViewport: PropTypes.func.isRequired,
      onUpdateDeviceModal: PropTypes.func.isRequired,
    };
  }

  constructor(props) {
    super(props);
    this.onSelectChange = this.onSelectChange.bind(this);
  }

  onSelectChange({ target }) {
    let {
      devices,
      viewportId,
      onChangeDevice,
      onResizeViewport,
      onUpdateDeviceModal,
    } = this.props;

    if (target.value === OPEN_DEVICE_MODAL_VALUE) {
      onUpdateDeviceModal(true, viewportId);
      return;
    }
    for (let type of devices.types) {
      for (let device of devices[type]) {
        if (device.name === target.value) {
          onResizeViewport(device.width, device.height);
          onChangeDevice(device, type);
          return;
        }
      }
    }
  }

  render() {
    let {
      devices,
      selectedDevice,
    } = this.props;

    let options = [];
    for (let type of devices.types) {
      for (let device of devices[type]) {
        if (device.displayed) {
          options.push(device);
        }
      }
    }

    options.sort(function (a, b) {
      return a.name.localeCompare(b.name);
    });

    let selectClass = "viewport-device-selector toolbar-dropdown";
    if (selectedDevice) {
      selectClass += " selected";
    }

    let state = devices.listState;
    let listContent;

    if (state == Types.loadableState.LOADED) {
      listContent = [
        dom.option({
          value: "",
          title: "",
          disabled: true,
          hidden: true,
        },
        getStr("responsive.noDeviceSelected")),
        options.map(device => {
          return dom.option({
            key: device.name,
            value: device.name,
            title: "",
          }, device.name);
        }),
        dom.option({
          value: OPEN_DEVICE_MODAL_VALUE,
          title: "",
        }, getStr("responsive.editDeviceList"))];
    } else if (state == Types.loadableState.LOADING
      || state == Types.loadableState.INITIALIZED) {
      listContent = [dom.option({
        value: "",
        title: "",
        disabled: true,
      }, getStr("responsive.deviceListLoading"))];
    } else if (state == Types.loadableState.ERROR) {
      listContent = [dom.option({
        value: "",
        title: "",
        disabled: true,
      }, getStr("responsive.deviceListError"))];
    }

    return dom.select(
      {
        className: selectClass,
        value: selectedDevice,
        title: selectedDevice,
        onChange: this.onSelectChange,
        disabled: (state !== Types.loadableState.LOADED),
      },
      ...listContent
    );
  }
}

module.exports = DeviceSelector;
PK
!<�Schrome/devtools/modules/devtools/client/responsive.html/components/GlobalToolbar.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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, createFactory } = 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 { getStr } = require("../utils/l10n");
const Types = require("../types");
const DevicePixelRatioSelector = createFactory(require("./DevicePixelRatioSelector"));
const NetworkThrottlingSelector = createFactory(require("./NetworkThrottlingSelector"));
const ReloadConditions = createFactory(require("./ReloadConditions"));

class GlobalToolbar extends PureComponent {
  static get propTypes() {
    return {
      devices: PropTypes.shape(Types.devices).isRequired,
      displayPixelRatio: Types.pixelRatio.value.isRequired,
      networkThrottling: PropTypes.shape(Types.networkThrottling).isRequired,
      reloadConditions: PropTypes.shape(Types.reloadConditions).isRequired,
      screenshot: PropTypes.shape(Types.screenshot).isRequired,
      selectedDevice: PropTypes.string.isRequired,
      selectedPixelRatio: PropTypes.shape(Types.pixelRatio).isRequired,
      touchSimulation: PropTypes.shape(Types.touchSimulation).isRequired,
      onChangeNetworkThrottling: PropTypes.func.isRequired,
      onChangePixelRatio: PropTypes.func.isRequired,
      onChangeReloadCondition: PropTypes.func.isRequired,
      onChangeTouchSimulation: PropTypes.func.isRequired,
      onExit: PropTypes.func.isRequired,
      onScreenshot: PropTypes.func.isRequired,
    };
  }

  render() {
    let {
      devices,
      displayPixelRatio,
      networkThrottling,
      reloadConditions,
      screenshot,
      selectedDevice,
      selectedPixelRatio,
      touchSimulation,
      onChangeNetworkThrottling,
      onChangePixelRatio,
      onChangeReloadCondition,
      onChangeTouchSimulation,
      onExit,
      onScreenshot,
    } = this.props;

    let touchButtonClass = "toolbar-button devtools-button";
    if (touchSimulation.enabled) {
      touchButtonClass += " checked";
    }

    return dom.header(
      {
        id: "global-toolbar",
        className: "container",
      },
      dom.span(
        {
          className: "title",
        },
        getStr("responsive.title")
      ),
      NetworkThrottlingSelector({
        networkThrottling,
        onChangeNetworkThrottling,
      }),
      DevicePixelRatioSelector({
        devices,
        displayPixelRatio,
        selectedDevice,
        selectedPixelRatio,
        onChangePixelRatio,
      }),
      ReloadConditions({
        reloadConditions,
        onChangeReloadCondition,
      }),
      dom.button({
        id: "global-touch-simulation-button",
        className: touchButtonClass,
        title: (touchSimulation.enabled ?
          getStr("responsive.disableTouch") : getStr("responsive.enableTouch")),
        onClick: () => onChangeTouchSimulation(!touchSimulation.enabled),
      }),
      dom.button({
        id: "global-screenshot-button",
        className: "toolbar-button devtools-button",
        title: getStr("responsive.screenshot"),
        onClick: onScreenshot,
        disabled: screenshot.isCapturing,
      }),
      dom.button({
        id: "global-exit-button",
        className: "toolbar-button devtools-button",
        title: getStr("responsive.exit"),
        onClick: onExit,
      })
    );
  }
}

module.exports = GlobalToolbar;
PK
!<}�n�r	r	_chrome/devtools/modules/devtools/client/responsive.html/components/NetworkThrottlingSelector.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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 Types = require("../types");
const { getStr } = require("../utils/l10n");
const throttlingProfiles = require("devtools/client/shared/network-throttling-profiles");

class NetworkThrottlingSelector extends PureComponent {
  static get propTypes() {
    return {
      networkThrottling: PropTypes.shape(Types.networkThrottling).isRequired,
      onChangeNetworkThrottling: PropTypes.func.isRequired,
    };
  }

  constructor(props) {
    super(props);
    this.onSelectChange = this.onSelectChange.bind(this);
  }

  onSelectChange({ target }) {
    let {
      onChangeNetworkThrottling,
    } = this.props;

    if (target.value == getStr("responsive.noThrottling")) {
      onChangeNetworkThrottling(false, "");
      return;
    }

    for (let profile of throttlingProfiles) {
      if (profile.id === target.value) {
        onChangeNetworkThrottling(true, profile.id);
        return;
      }
    }
  }

  render() {
    let {
      networkThrottling,
    } = this.props;

    let selectClass = "toolbar-dropdown";
    let selectedProfile;
    if (networkThrottling.enabled) {
      selectClass += " selected";
      selectedProfile = networkThrottling.profile;
    } else {
      selectedProfile = getStr("responsive.noThrottling");
    }

    let listContent = [
      dom.option(
        {
          key: "disabled",
        },
        getStr("responsive.noThrottling")
      ),
      dom.option(
        {
          key: "divider",
          className: "divider",
          disabled: true,
        }
      ),
      throttlingProfiles.map(profile => {
        return dom.option(
          {
            key: profile.id,
          },
          profile.id
        );
      }),
    ];

    return dom.select(
      {
        id: "global-network-throttling-selector",
        className: selectClass,
        value: selectedProfile,
        onChange: this.onSelectChange,
      },
      ...listContent
    );
  }
}

module.exports = NetworkThrottlingSelector;
PK
!<�r�3��Vchrome/devtools/modules/devtools/client/responsive.html/components/ReloadConditions.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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, createFactory } = require("devtools/client/shared/vendor/react");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");

const Types = require("../types");
const { getStr } = require("../utils/l10n");
const ToggleMenu = createFactory(require("./ToggleMenu"));

class ReloadConditions extends PureComponent {
  static get propTypes() {
    return {
      reloadConditions: PropTypes.shape(Types.reloadConditions).isRequired,
      onChangeReloadCondition: PropTypes.func.isRequired,
    };
  }

  render() {
    let {
      reloadConditions,
      onChangeReloadCondition,
    } = this.props;

    return ToggleMenu({
      id: "global-reload-conditions-menu",
      items: [
        {
          id: "touchSimulation",
          label: getStr("responsive.reloadConditions.touchSimulation"),
          checked: reloadConditions.touchSimulation,
        },
        {
          id: "userAgent",
          label: getStr("responsive.reloadConditions.userAgent"),
          checked: reloadConditions.userAgent,
        },
      ],
      label: getStr("responsive.reloadConditions.label"),
      title: getStr("responsive.reloadConditions.title"),
      onChange: onChangeReloadCondition,
    });
  }
}

module.exports = ReloadConditions;
PK
!<�#�Wchrome/devtools/modules/devtools/client/responsive.html/components/ResizableViewport.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 { Component, createFactory } = 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 Constants = require("../constants");
const Types = require("../types");
const Browser = createFactory(require("./Browser"));
const ViewportToolbar = createFactory(require("./ViewportToolbar"));

const VIEWPORT_MIN_WIDTH = Constants.MIN_VIEWPORT_DIMENSION;
const VIEWPORT_MIN_HEIGHT = Constants.MIN_VIEWPORT_DIMENSION;

class ResizableViewport extends Component {
  static get propTypes() {
    return {
      devices: PropTypes.shape(Types.devices).isRequired,
      screenshot: PropTypes.shape(Types.screenshot).isRequired,
      swapAfterMount: PropTypes.bool.isRequired,
      viewport: PropTypes.shape(Types.viewport).isRequired,
      onBrowserMounted: PropTypes.func.isRequired,
      onChangeDevice: PropTypes.func.isRequired,
      onContentResize: PropTypes.func.isRequired,
      onRemoveDeviceAssociation: PropTypes.func.isRequired,
      onResizeViewport: PropTypes.func.isRequired,
      onRotateViewport: PropTypes.func.isRequired,
      onUpdateDeviceModal: PropTypes.func.isRequired,
    };
  }

  constructor(props) {
    super(props);

    this.state = {
      isResizing: false,
      lastClientX: 0,
      lastClientY: 0,
      ignoreX: false,
      ignoreY: false,
    };

    this.onResizeStart = this.onResizeStart.bind(this);
    this.onResizeStop = this.onResizeStop.bind(this);
    this.onResizeDrag = this.onResizeDrag.bind(this);
  }

  onResizeStart({ target, clientX, clientY }) {
    window.addEventListener("mousemove", this.onResizeDrag, true);
    window.addEventListener("mouseup", this.onResizeStop, true);

    this.setState({
      isResizing: true,
      lastClientX: clientX,
      lastClientY: clientY,
      ignoreX: target === this.refs.resizeBarY,
      ignoreY: target === this.refs.resizeBarX,
    });
  }

  onResizeStop() {
    window.removeEventListener("mousemove", this.onResizeDrag, true);
    window.removeEventListener("mouseup", this.onResizeStop, true);

    this.setState({
      isResizing: false,
      lastClientX: 0,
      lastClientY: 0,
      ignoreX: false,
      ignoreY: false,
    });
  }

  onResizeDrag({ clientX, clientY }) {
    if (!this.state.isResizing) {
      return;
    }

    let { lastClientX, lastClientY, ignoreX, ignoreY } = this.state;
    // the viewport is centered horizontally, so horizontal resize resizes
    // by twice the distance the mouse was dragged - on left and right side.
    let deltaX = 2 * (clientX - lastClientX);
    let deltaY = (clientY - lastClientY);

    if (ignoreX) {
      deltaX = 0;
    }
    if (ignoreY) {
      deltaY = 0;
    }

    let width = this.props.viewport.width + deltaX;
    let height = this.props.viewport.height + deltaY;

    if (width < VIEWPORT_MIN_WIDTH) {
      width = VIEWPORT_MIN_WIDTH;
    } else {
      lastClientX = clientX;
    }

    if (height < VIEWPORT_MIN_HEIGHT) {
      height = VIEWPORT_MIN_HEIGHT;
    } else {
      lastClientY = clientY;
    }

    // Update the viewport store with the new width and height.
    this.props.onResizeViewport(width, height);
    // Change the device selector back to an unselected device
    // TODO: Bug 1332754: Logic like this probably belongs in the action creator.
    if (this.props.viewport.device) {
      // In bug 1329843 and others, we may eventually stop this approach of removing the
      // the properties of the device on resize.  However, at the moment, there is no
      // way to edit dPR when a device is selected, and there is no UI at all for editing
      // UA, so it's important to keep doing this for now.
      this.props.onRemoveDeviceAssociation();
    }

    this.setState({
      lastClientX,
      lastClientY
    });
  }

  render() {
    let {
      devices,
      screenshot,
      swapAfterMount,
      viewport,
      onBrowserMounted,
      onChangeDevice,
      onContentResize,
      onResizeViewport,
      onRotateViewport,
      onUpdateDeviceModal,
    } = this.props;

    let resizeHandleClass = "viewport-resize-handle";
    if (screenshot.isCapturing) {
      resizeHandleClass += " hidden";
    }

    let contentClass = "viewport-content";
    if (this.state.isResizing) {
      contentClass += " resizing";
    }

    return dom.div(
      {
        className: "resizable-viewport",
      },
      ViewportToolbar({
        devices,
        viewport,
        onChangeDevice,
        onResizeViewport,
        onRotateViewport,
        onUpdateDeviceModal,
      }),
      dom.div(
        {
          className: contentClass,
          style: {
            width: viewport.width + "px",
            height: viewport.height + "px",
          },
        },
        Browser({
          swapAfterMount,
          onBrowserMounted,
          onContentResize,
        })
      ),
      dom.div({
        className: resizeHandleClass,
        onMouseDown: this.onResizeStart,
      }),
      dom.div({
        ref: "resizeBarX",
        className: "viewport-horizontal-resize-handle",
        onMouseDown: this.onResizeStart,
      }),
      dom.div({
        ref: "resizeBarY",
        className: "viewport-vertical-resize-handle",
        onMouseDown: this.onResizeStart,
      })
    );
  }
}

module.exports = ResizableViewport;
PK
!<�l���
�
Pchrome/devtools/modules/devtools/client/responsive.html/components/ToggleMenu.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");

let MenuItem = {
  id: PropTypes.string.isRequired,
  label: PropTypes.string.isRequired,
  checked: PropTypes.bool,
};

class ToggleMenu extends PureComponent {
  static get propTypes() {
    return {
      id: PropTypes.string,
      items: PropTypes.arrayOf(PropTypes.shape(MenuItem)).isRequired,
      label: PropTypes.string,
      title: PropTypes.string,
      onChange: PropTypes.func.isRequired,
    };
  }

  constructor(props) {
    super(props);

    this.state = {
      isOpen: false,
    };

    this.onItemChange = this.onItemChange.bind(this);
    this.onToggleOpen = this.onToggleOpen.bind(this);
  }

  onItemChange({ target }) {
    let {
      onChange,
    } = this.props;

    // Close menu after changing an item
    this.setState({
      isOpen: false,
    });

    let id = target.name;
    onChange(id, target.checked);
  }

  onToggleOpen() {
    let {
      isOpen,
    } = this.state;

    this.setState({
      isOpen: !isOpen,
    });
  }

  render() {
    let {
      id: menuID,
      items,
      label: toggleLabel,
      title,
    } = this.props;

    let {
      isOpen,
    } = this.state;

    let {
      onItemChange,
      onToggleOpen,
    } = this;

    let menuItems = items.map(({ id, label, checked }) => {
      let inputID = `devtools-menu-item-${id}`;

      return dom.div(
        {
          className: "devtools-menu-item",
          key: id,
        },
        dom.input({
          type: "checkbox",
          id: inputID,
          name: id,
          checked,
          onChange: onItemChange,
        }),
        dom.label({
          htmlFor: inputID,
        }, label)
      );
    });

    let menuClass = "devtools-menu";
    if (isOpen) {
      menuClass += " opened";
    }
    let menu = dom.div(
      {
        className: menuClass,
      },
      menuItems
    );

    let buttonClass = "devtools-toggle-menu";
    buttonClass += " toolbar-dropdown toolbar-button devtools-button";
    if (isOpen || items.some(({ checked }) => checked)) {
      buttonClass += " selected";
    }
    return dom.div(
      {
        id: menuID,
        className: buttonClass,
        title,
        onClick: onToggleOpen,
      },
      toggleLabel,
      menu
    );
  }
}

module.exports = ToggleMenu;
PK
!<)k�m++Nchrome/devtools/modules/devtools/client/responsive.html/components/Viewport.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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 Types = require("../types");
const ResizableViewport = createFactory(require("./ResizableViewport"));
const ViewportDimension = createFactory(require("./ViewportDimension"));

class Viewport extends Component {
  static get propTypes() {
    return {
      devices: PropTypes.shape(Types.devices).isRequired,
      screenshot: PropTypes.shape(Types.screenshot).isRequired,
      swapAfterMount: PropTypes.bool.isRequired,
      viewport: PropTypes.shape(Types.viewport).isRequired,
      onBrowserMounted: PropTypes.func.isRequired,
      onChangeDevice: PropTypes.func.isRequired,
      onContentResize: PropTypes.func.isRequired,
      onRemoveDeviceAssociation: PropTypes.func.isRequired,
      onResizeViewport: PropTypes.func.isRequired,
      onRotateViewport: PropTypes.func.isRequired,
      onUpdateDeviceModal: PropTypes.func.isRequired,
    };
  }

  constructor(props) {
    super(props);
    this.onChangeDevice = this.onChangeDevice.bind(this);
    this.onRemoveDeviceAssociation = this.onRemoveDeviceAssociation.bind(this);
    this.onResizeViewport = this.onResizeViewport.bind(this);
    this.onRotateViewport = this.onRotateViewport.bind(this);
  }

  onChangeDevice(device, deviceType) {
    let {
      viewport,
      onChangeDevice,
    } = this.props;

    onChangeDevice(viewport.id, device, deviceType);
  }

  onRemoveDeviceAssociation() {
    let {
      viewport,
      onRemoveDeviceAssociation,
    } = this.props;

    onRemoveDeviceAssociation(viewport.id);
  }

  onResizeViewport(width, height) {
    let {
      viewport,
      onResizeViewport,
    } = this.props;

    onResizeViewport(viewport.id, width, height);
  }

  onRotateViewport() {
    let {
      viewport,
      onRotateViewport,
    } = this.props;

    onRotateViewport(viewport.id);
  }

  render() {
    let {
      devices,
      screenshot,
      swapAfterMount,
      viewport,
      onBrowserMounted,
      onContentResize,
      onUpdateDeviceModal,
    } = this.props;

    let {
      onChangeDevice,
      onRemoveDeviceAssociation,
      onRotateViewport,
      onResizeViewport,
    } = this;

    return dom.div(
      {
        className: "viewport",
      },
      ViewportDimension({
        viewport,
        onChangeSize: onResizeViewport,
        onRemoveDeviceAssociation,
      }),
      ResizableViewport({
        devices,
        screenshot,
        swapAfterMount,
        viewport,
        onBrowserMounted,
        onChangeDevice,
        onContentResize,
        onRemoveDeviceAssociation,
        onResizeViewport,
        onRotateViewport,
        onUpdateDeviceModal,
      })
    );
  }
}

module.exports = Viewport;
PK
!<�<b���Wchrome/devtools/modules/devtools/client/responsive.html/components/ViewportDimension.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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 { isKeyIn } = require("../utils/key");

const Constants = require("../constants");
const Types = require("../types");

/**
 * Get the increment/decrement step to use for the provided key event.
 */
function getIncrement(event) {
  const defaultIncrement = 1;
  const largeIncrement = 100;
  const mediumIncrement = 10;

  let increment = 0;
  let key = event.keyCode;

  if (isKeyIn(key, "UP", "PAGE_UP")) {
    increment = 1 * defaultIncrement;
  } else if (isKeyIn(key, "DOWN", "PAGE_DOWN")) {
    increment = -1 * defaultIncrement;
  }

  if (event.shiftKey) {
    if (isKeyIn(key, "PAGE_UP", "PAGE_DOWN")) {
      increment *= largeIncrement;
    } else {
      increment *= mediumIncrement;
    }
  }

  return increment;
}

class ViewportDimension extends Component {
  static get propTypes() {
    return {
      viewport: PropTypes.shape(Types.viewport).isRequired,
      onChangeSize: PropTypes.func.isRequired,
      onRemoveDeviceAssociation: PropTypes.func.isRequired,
    };
  }

  constructor(props) {
    super(props);
    let { width, height } = props.viewport;

    this.state = {
      width,
      height,
      isEditing: false,
      isInvalid: false,
    };

    this.validateInput = this.validateInput.bind(this);
    this.onInputBlur = this.onInputBlur.bind(this);
    this.onInputChange = this.onInputChange.bind(this);
    this.onInputFocus = this.onInputFocus.bind(this);
    this.onInputKeyDown = this.onInputKeyDown.bind(this);
    this.onInputKeyUp = this.onInputKeyUp.bind(this);
    this.onInputSubmit = this.onInputSubmit.bind(this);
  }

  componentWillReceiveProps(nextProps) {
    let { width, height } = nextProps.viewport;

    this.setState({
      width,
      height,
    });
  }

  validateInput(value) {
    let isInvalid = true;

    // Check the value is a number and greater than MIN_VIEWPORT_DIMENSION
    if (/^\d{3,4}$/.test(value) &&
        parseInt(value, 10) >= Constants.MIN_VIEWPORT_DIMENSION) {
      isInvalid = false;
    }

    this.setState({
      isInvalid,
    });
  }

  onInputBlur() {
    let { width, height } = this.props.viewport;

    if (this.state.width != width || this.state.height != height) {
      this.onInputSubmit();
    }

    this.setState({
      isEditing: false,
      inInvalid: false,
    });
  }

  onInputChange({ target }, callback) {
    if (target.value.length > 4) {
      return;
    }

    if (this.refs.widthInput == target) {
      this.setState({ width: target.value }, callback);
      this.validateInput(target.value);
    }

    if (this.refs.heightInput == target) {
      this.setState({ height: target.value }, callback);
      this.validateInput(target.value);
    }
  }

  onInputFocus() {
    this.setState({
      isEditing: true,
    });
  }

  onInputKeyDown(event) {
    let { target } = event;
    let increment = getIncrement(event);
    if (!increment) {
      return;
    }
    target.value = parseInt(target.value, 10) + increment;
    this.onInputChange(event, this.onInputSubmit);
  }

  onInputKeyUp({ target, keyCode }) {
    // On Enter, submit the input
    if (keyCode == 13) {
      this.onInputSubmit();
    }

    // On Esc, blur the target
    if (keyCode == 27) {
      target.blur();
    }
  }

  onInputSubmit() {
    if (this.state.isInvalid) {
      let { width, height } = this.props.viewport;

      this.setState({
        width,
        height,
        isInvalid: false,
      });

      return;
    }

    // Change the device selector back to an unselected device
    // TODO: Bug 1332754: Logic like this probably belongs in the action creator.
    if (this.props.viewport.device) {
      this.props.onRemoveDeviceAssociation();
    }
    this.props.onChangeSize(parseInt(this.state.width, 10),
                            parseInt(this.state.height, 10));
  }

  render() {
    let editableClass = "viewport-dimension-editable";
    let inputClass = "viewport-dimension-input";

    if (this.state.isEditing) {
      editableClass += " editing";
      inputClass += " editing";
    }

    if (this.state.isInvalid) {
      editableClass += " invalid";
    }

    return dom.div(
      {
        className: "viewport-dimension",
      },
      dom.div(
        {
          className: editableClass,
        },
        dom.input({
          ref: "widthInput",
          className: inputClass,
          size: 4,
          value: this.state.width,
          onBlur: this.onInputBlur,
          onChange: this.onInputChange,
          onFocus: this.onInputFocus,
          onKeyDown: this.onInputKeyDown,
          onKeyUp: this.onInputKeyUp,
        }),
        dom.span({
          className: "viewport-dimension-separator",
        }, "×"),
        dom.input({
          ref: "heightInput",
          className: inputClass,
          size: 4,
          value: this.state.height,
          onBlur: this.onInputBlur,
          onChange: this.onInputChange,
          onFocus: this.onInputFocus,
          onKeyDown: this.onInputKeyDown,
          onKeyUp: this.onInputKeyUp,
        })
      )
    );
  }
}

module.exports = ViewportDimension;
PK
!<ݘ»�Uchrome/devtools/modules/devtools/client/responsive.html/components/ViewportToolbar.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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, createFactory } = 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 { getStr } = require("../utils/l10n");
const Types = require("../types");
const DeviceSelector = createFactory(require("./DeviceSelector"));

class ViewportToolbar extends PureComponent {
  static get propTypes() {
    return {
      devices: PropTypes.shape(Types.devices).isRequired,
      viewport: PropTypes.shape(Types.viewport).isRequired,
      onChangeDevice: PropTypes.func.isRequired,
      onResizeViewport: PropTypes.func.isRequired,
      onRotateViewport: PropTypes.func.isRequired,
      onUpdateDeviceModal: PropTypes.func.isRequired,
    };
  }

  render() {
    let {
      devices,
      viewport,
      onChangeDevice,
      onResizeViewport,
      onRotateViewport,
      onUpdateDeviceModal,
    } = this.props;

    return dom.div(
      {
        className: "viewport-toolbar container",
      },
      DeviceSelector({
        devices,
        selectedDevice: viewport.device,
        viewportId: viewport.id,
        onChangeDevice,
        onResizeViewport,
        onUpdateDeviceModal,
      }),
      dom.button({
        className: "viewport-rotate-button toolbar-button devtools-button",
        onClick: onRotateViewport,
        title: getStr("responsive.rotate"),
      })
    );
  }
}

module.exports = ViewportToolbar;
PK
!<x��q��Ochrome/devtools/modules/devtools/client/responsive.html/components/Viewports.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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 Types = require("../types");
const Viewport = createFactory(require("./Viewport"));

class Viewports extends Component {
  static get propTypes() {
    return {
      devices: PropTypes.shape(Types.devices).isRequired,
      screenshot: PropTypes.shape(Types.screenshot).isRequired,
      viewports: PropTypes.arrayOf(PropTypes.shape(Types.viewport)).isRequired,
      onBrowserMounted: PropTypes.func.isRequired,
      onChangeDevice: PropTypes.func.isRequired,
      onContentResize: PropTypes.func.isRequired,
      onRemoveDeviceAssociation: PropTypes.func.isRequired,
      onResizeViewport: PropTypes.func.isRequired,
      onRotateViewport: PropTypes.func.isRequired,
      onUpdateDeviceModal: PropTypes.func.isRequired,
    };
  }

  render() {
    let {
      devices,
      screenshot,
      viewports,
      onBrowserMounted,
      onChangeDevice,
      onContentResize,
      onRemoveDeviceAssociation,
      onResizeViewport,
      onRotateViewport,
      onUpdateDeviceModal,
    } = this.props;

    return dom.div(
      {
        id: "viewports",
      },
      viewports.map((viewport, i) => {
        return Viewport({
          key: viewport.id,
          devices,
          screenshot,
          swapAfterMount: i == 0,
          viewport,
          onBrowserMounted,
          onChangeDevice,
          onContentResize,
          onRemoveDeviceAssociation,
          onResizeViewport,
          onRotateViewport,
          onUpdateDeviceModal,
        });
      })
    );
  }
}

module.exports = Viewports;
PK
!<v�,,Dchrome/devtools/modules/devtools/client/responsive.html/constants.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 minimum viewport width and height
exports.MIN_VIEWPORT_DIMENSION = 280;
PK
!<cO�ڈ�Kchrome/devtools/modules/devtools/client/responsive.html/images/grippers.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 16 16" fill="#A5A5A5">
  <path d="M16 3.2L3.1 16h1.7L16 4.9zM16 7.2L7.1 16h1.8L16 8.9zM16 11.1L11.1 16h1.8l3.1-3.1z" />
</svg>
PK
!<9���vvRchrome/devtools/modules/devtools/client/responsive.html/images/rotate-viewport.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b">
  <path d="M3.8 13.4c-1.2 0-3.4-.6-3.7-2.8s1.3-3.3 2.1-3.5c.2-.1.4.1.5.3.1.2-.1.4-.3.5-.1 0-1.8.6-1.6 2.7.2 1.5 1.6 1.9 2.4 2l-.7-2.4c0-.2.2-.5.4-.5.2-.1.4 0 .5.2l.9 3c0 .1 0 .3-.1.4-.1.1-.2.1-.4.1zM12.3 1.7c1.2 0 3.4.6 3.7 2.8.3 2.2-1.3 3.3-2.1 3.5-.2.1-.4-.1-.5-.3s.1-.4.3-.5c.1 0 1.8-.6 1.6-2.7-.2-1.5-1.6-1.9-2.4-2l.7 2.4c.1.2-.1.4-.3.5s-.4-.1-.5-.3l-.9-3c0-.1 0-.3.1-.4h.3zM9.6 2.5L4.3 4.1c-.2.1-.4.4-.3.8l2.5 8c.1.3.4.6.8.5l5.2-1.6c.3-.1.6-.5.4-.8l-2.5-8c0-.1-.7-.6-.8-.5zm2.5 8.6l-5 1.5-.6-1.9 5-1.5.6 1.9zm-.8-2.6l-5 1.5-1.6-5.3 5-1.5 1.6 5.3z"/>
</svg>
PK
!<ȭ9��Mchrome/devtools/modules/devtools/client/responsive.html/images/screenshot.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16" fill="context-fill #0b0b0b">
  <path d="M14.4,4.1h-3l0-1.3c0-0.9-1.2-1.6-2.1-1.6H6.6c-0.9,0-1.9,0.7-1.9,1.6l0,1.3h-3c-0.9,0-1.6,0.7-1.6,1.6v7.4c0,0.9,0.7,1.3,1.6,1.3h12.7c0.9,0,1.6-0.3,1.6-1.3V5.7C16,4.8,15.3,4.1,14.4,4.1z M14.8,13.2H1.2v-8h4.5l0-3h4.5l0,3h4.4L14.8,13.2z"/>
  <path d="M8,6.7c-1.3,0-2.4,1.1-2.4,2.4s1.1,2.4,2.4,2.4s2.4-1.1,2.4-2.4S9.3,6.7,8,6.7z M8,10.2c-0.7,0-1.2-0.5-1.2-1.1S7.3,8,8,8s1.2,0.5,1.2,1.1S8.7,10.2,8,10.2z"/>
</svg>
PK
!<��EEOchrome/devtools/modules/devtools/client/responsive.html/images/select-arrow.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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"
     width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" d="M7.9 16.3c-.3 0-.6-.1-.8-.4l-4-4.8c-.2-.3-.3-.5-.1-.8.1-.3.5-.3.9-.3h8c.4 0 .7 0 .9.3.2.4.1.6-.1.9l-4 4.8c-.2.3-.5.3-.8.3zM7.8 0c.3 0 .6.1.7.4L12.4 5c.2.3.3.4.1.7-.1.4-.5.3-.8.3H3.9c-.4 0-.8.1-.9-.2-.2-.4-.1-.6.1-.9L7 .3c.2-.3.5-.3.8-.3z"/>
</svg>

PK
!<x�̄��Ochrome/devtools/modules/devtools/client/responsive.html/images/touch-events.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16" fill="context-fill #0b0b0b">
  <path d="M12.5 5.3c-.2 0-.4 0-.6.1-.2-.6-.8-1-1.4-1-.3 0-.5.1-.8.2C9.4 4.2 9 4 8.6 4h-.4V1.5C8.2.7 7.5 0 6.7 0S5.2.7 5.2 1.5v6.6l-.7-.6c-.6-.6-1.6-.6-2.2 0-.5.6-.5 1.4-.1 2.1.3.4.6 1.1 1 1.8C4.2 13.6 5.3 16 7 16h3.9s3.1-1 3.1-4V6.7c.1-.8-.7-1.4-1.5-1.4zm.6 6.7c0 2-2.1 3-2.4 3H7c-1 0-2.1-2.4-2.9-4-.3-.8-.7-1.6-1-2-.2-.3-.2-.5-.1-.7.1-.1.2-.1.3-.1.1 0 .2 0 .3.1l1.5 1.5c.3.2.6.2.7.1.1 0 .4-.2.4-.5V1.5c0-.2.2-.4.5-.4s.5.2.5.4v5.3c0 .3.2.5.5.5s.5-.2.5-.5V5.5c0-.4.2-.5.5-.5.2 0 .5.2.5.4v2c-.1.3.2.6.4.6.3 0 .5-.2.5-.5V5.8c0-.2.2-.4.5-.4s.5.2.5.4v2.3c0 .3.2.5.5.5s.5-.2.5-.5V6.7c0-.2.2-.4.5-.4s.5.2.5.4V12z"/>
</svg>
PK
!<BB��d0d0Achrome/devtools/modules/devtools/client/responsive.html/index.css/* TODO: May break up into component local CSS.  Pending future discussions by
 * React component group on how to best handle CSS. */

/**
 * CSS Variables specific to the responsive design mode
 */

.theme-light {
  --rdm-box-shadow: 0 4px 4px 0 rgba(155, 155, 155, 0.26);
  --submit-button-active-background-color: rgba(0,0,0,0.12);
  --submit-button-active-color: var(--theme-body-color);
  --viewport-color: #999797;
  --viewport-hover-color: var(--theme-body-color);
  --viewport-active-color: #3b3b3b;
  --viewport-selection-arrow: url("./images/select-arrow.svg");
}

.theme-dark {
  --rdm-box-shadow: 0 4px 4px 0 rgba(105, 105, 105, 0.26);
  --submit-button-active-background-color: var(--theme-toolbar-hover-active);
  --submit-button-active-color: var(--theme-selection-color);
  --viewport-color: #c6ccd0;
  --viewport-hover-color: #dde1e4;
  --viewport-active-color: #fcfcfc;
  --viewport-selection-arrow: url("./images/select-arrow.svg");
}

* {
  box-sizing: border-box;
}

:root,
input,
select,
button {
  font-size: 11px;
}

html,
body,
#root {
  height: 100%;
  margin: 0;
}

#app {
  /* Center the viewports container */
  display: flex;
  align-items: center;
  flex-direction: column;
  padding-top: 15px;
  padding-bottom: 1%;
  position: relative;
  height: 100%;
}

/**
 * Common styles for shared components
 */

.container {
  background-color: var(--theme-toolbar-background);
  border: 1px solid var(--theme-splitter-color);
}

.toolbar-button {
  margin: 0;
  padding: 0;
  border: none;
  color: var(--viewport-color);
}

.toolbar-button:empty:hover:not(:disabled),
.toolbar-button:empty:-moz-any(:hover:active, .checked):not(:disabled),
.toolbar-button:not(:empty),
.toolbar-button:hover:not(:empty):not(:disabled):not(.checked) {
  /* Reset background from .devtools-button */
  background: none;
}

.toolbar-button:active::before {
  filter: var(--theme-icon-checked-filter);
}

.toolbar-button.selected {
  color: var(--viewport-active-color);
}

.toolbar-button:not(:disabled):hover {
  color: var(--viewport-hover-color);
}

select {
  -moz-appearance: none;
  color: var(--viewport-color);
  border: none;
  height: 100%;
  padding: 0 8px;
  text-align: center;
  text-overflow: ellipsis;
}

select.selected {
  color: var(--viewport-active-color);
}

select:not(:disabled):hover {
  color: var(--viewport-hover-color);
}

/* This is (believed to be?) separate from the identical select.selected rule
   set so that it overrides select:hover because of file ordering once the
   select is focused.  It's unclear whether the visual effect that results here
   is intentional and desired. */
select:focus {
  color: var(--viewport-active-color);
}

select > option {
  text-align: left;
  padding: 5px 10px;
}

select > option,
select > option:hover {
  color: var(--viewport-active-color);
}

select > option.divider {
  border-top: 1px solid var(--theme-splitter-color);
  height: 0px;
  padding: 0;
  font-size: 0px;
}

/**
 * Toggle Menu
 */

.devtools-toggle-menu {
  position: relative;
}

.devtools-toggle-menu .devtools-menu {
  display: none;
  flex-direction: column;
  align-items: start;
  position: absolute;
  left: 0;
  top: 100%;
  z-index: 1;
  padding: 5px;
  border-radius: 2px;
  background-color: var(--theme-toolbar-background);
  box-shadow: var(--rdm-box-shadow);
}

.devtools-toggle-menu .devtools-menu.opened {
  display: flex;
}

.devtools-toggle-menu .devtools-menu-item {
  display: flex;
  align-items: center;
}

/**
 * Common background for dropdowns like select and toggle menu
 */
.toolbar-dropdown,
.toolbar-dropdown.devtools-button,
.toolbar-dropdown.devtools-button:hover:not(:empty):not(:disabled):not(.checked) {
  background-color: var(--theme-toolbar-background);
  background-image: var(--viewport-selection-arrow);
  background-position: 100% 50%;
  background-repeat: no-repeat;
  background-size: 7px;
  -moz-context-properties: fill;
  fill: currentColor;
}

/**
 * Global Toolbar
 */

#global-toolbar {
  color: var(--theme-body-color-alt);
  border-radius: 2px;
  box-shadow: var(--rdm-box-shadow);
  margin: 0 0 15px 0;
  padding: 4px 5px;
  display: inline-flex;
  align-items: center;
  -moz-user-select: none;
}

#global-toolbar > .title {
  border-right: 1px solid var(--theme-splitter-color);
  padding: 1px 6px 0 2px;
}

#global-toolbar > .toolbar-button:first-of-type {
  margin-inline-start: 8px;
}

#global-toolbar > .toolbar-button::before {
  width: 12px;
  height: 12px;
}

#global-toolbar .toolbar-dropdown {
  background-position-x: right 5px;
  border-right: 1px solid var(--theme-splitter-color);
  padding-right: 15px;
  /* padding-left: 0; */
}

#global-touch-simulation-button::before {
  background-image: url("./images/touch-events.svg");
}

#global-screenshot-button::before {
  background-image: url("./images/screenshot.svg");
}

#global-exit-button::before {
  background-image: url("chrome://devtools/skin/images/close.svg");
}

#global-screenshot-button:disabled {
  filter: var(--theme-icon-checked-filter);
  opacity: 1 !important;
}

#global-network-throttling-selector {
  height: 15px;
  padding-left: 0;
  width: 103px;
}

#global-device-pixel-ratio-selector {
  -moz-user-select: none;
  color: var(--viewport-color);
  height: 15px;
  /* `max-width` is here to keep the UI compact if the device pixel ratio changes to a
     repeating decimal value.  This can happen if you zoom the UI (Cmd + Plus / Minus on
     macOS for example). */
  max-width: 8em;
}

#global-device-pixel-ratio-selector.focused,
#global-device-pixel-ratio-selector:not(.disabled):hover {
  color: var(--viewport-hover-color);
}

#global-device-pixel-ratio-selector:focus {
  color: var(--viewport-active-color);
}

#global-device-pixel-ratio-selector.selected {
  color: var(--viewport-active-color);
}

#global-device-pixel-ratio-selector > option {
  padding: 5px;
}

#viewports {
  /* Make sure left-most viewport is visible when there's horizontal overflow.
     That is, when the horizontal space become smaller than the viewports and a
     scrollbar appears, then the first viewport will still be visible */
  position: sticky;
  left: 0;
  /* Individual viewports are inline elements, make sure they stay on a single
     line */
  white-space: nowrap;
}

/**
 * Viewport Container
 */

.viewport {
  display: inline-block;
  /* Align all viewports to the top */
  vertical-align: top;
}

.resizable-viewport {
  border: 1px solid var(--theme-splitter-color);
  box-shadow: var(--rdm-box-shadow);
  position: relative;
}

/**
 * Viewport Toolbar
 */

.viewport-toolbar {
  border-width: 0;
  border-bottom-width: 1px;
  display: flex;
  flex-direction: row;
  justify-content: center;
  height: 16px;
}

.viewport-rotate-button {
  position: absolute;
  right: 0;
}

.viewport-rotate-button::before {
  background-image: url("./images/rotate-viewport.svg");
}

/**
 * Viewport Content
 */

.viewport-content.resizing {
  pointer-events: none;
}

/**
 * Viewport Browser
 */

.browser {
  display: block;
  border: 0;
  -moz-user-select: none;
}

.browser:-moz-focusring {
  outline: none;
}

/**
 * Viewport Resize Handles
 */

.viewport-resize-handle {
  position: absolute;
  width: 16px;
  height: 16px;
  bottom: 0;
  right: 0;
  background-image: url("./images/grippers.svg");
  background-position: bottom right;
  padding: 0 1px 1px 0;
  background-repeat: no-repeat;
  background-origin: content-box;
  cursor: se-resize;
}

.viewport-resize-handle.hidden {
  display: none;
}

.viewport-horizontal-resize-handle {
  position: absolute;
  width: 5px;
  height: calc(100% - 16px);
  right: -4px;
  top: 0;
  cursor: e-resize;
}

.viewport-vertical-resize-handle {
  position: absolute;
  width: calc(100% - 16px);
  height: 5px;
  left: 0;
  bottom: -4px;
  cursor: s-resize;
}

/**
 * Viewport Dimension Label
 */

.viewport-dimension {
  display: flex;
  justify-content: center;
  font: 10px sans-serif;
  margin-bottom: 10px;
}

.viewport-dimension-editable {
  border-bottom: 1px solid transparent;
}

.viewport-dimension-editable,
.viewport-dimension-input {
  color: var(--theme-body-color-inactive);
  transition: all 0.25s ease;
}

.viewport-dimension-editable.editing,
.viewport-dimension-input.editing {
  color: var(--viewport-active-color);
  outline: none;
}

.viewport-dimension-editable.editing {
  border-bottom: 1px solid var(--theme-selection-background);
}

.viewport-dimension-editable.editing.invalid {
  border-bottom: 1px solid #d92215;
}

.viewport-dimension-input {
  background: transparent;
  border: none;
  text-align: center;
}

.viewport-dimension-separator {
  -moz-user-select: none;
}

/**
 * Device Modal
 */

@keyframes fade-in-and-up {
  0% {
    opacity: 0;
    transform: translateY(5px);
  }
  100% {
    opacity: 1;
    transform: translateY(0px);
  }
}

@keyframes fade-down-and-out {
  0% {
    opacity: 1;
    transform: translateY(0px);
  }
  100% {
    opacity: 0;
    transform: translateY(5px);
    visibility: hidden;
  }
}

.device-modal {
  border-radius: 2px;
  box-shadow: var(--rdm-box-shadow);
  position: absolute;
  margin: auto;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  width: 800px;
  max-width: 90%;
  height: 650px;
  z-index: 1;
}

/* Handles the opening/closing of the modal */
#device-modal-wrapper.opened .device-modal {
  animation: fade-in-and-up 0.3s ease forwards;
}

#device-modal-wrapper.closed .device-modal {
  animation: fade-down-and-out 0.3s ease forwards;
}

#device-modal-wrapper.opened .modal-overlay {
  background-color: var(--theme-splitter-color);
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  width: 100%;
  z-index: 0;
  opacity: 0.5;
}

.device-modal-content {
  display: flex;
  flex-direction: column;
  flex-wrap: wrap;
  overflow: auto;
  height: 515px;
  margin: 20px 20px 0;
}

#device-close-button,
#device-close-button::before {
  position: absolute;
  top: 5px;
  right: 2px;
  width: 12px;
  height: 12px;
}

#device-close-button::before {
  background-image: url("chrome://devtools/skin/images/close.svg");
  margin: -6px 0 0 -6px;
}

.device-type {
  display: flex;
  flex-direction: column;
  padding: 10px;
}

.device-header {
  font-weight: bold;
  text-transform: capitalize;
  padding: 0 0 3px 23px;
}

.device-label {
  padding-bottom: 3px;
  display: flex;
  align-items: center;
  /* Largest size without horizontal scrollbars */
  max-width: 181px;
}

.device-input-checkbox {
  margin-right: 5px;
}

.device-name {
  flex: 1;
}

.device-remove-button,
.device-remove-button:empty::before {
  width: 12px;
  height: 12px;
}

.device-remove-button:empty::before {
  background-image: url("chrome://devtools/skin/images/close.svg");
  margin: -6px 0 0 -6px;
}

#device-submit-button {
  background-color: var(--theme-tab-toolbar-background);
  border-width: 1px 0 0 0;
  border-top-width: 1px;
  border-top-style: solid;
  border-top-color: var(--theme-splitter-color);
  color: var(--theme-body-color);
  width: 100%;
  height: 20px;
  position: absolute;
  bottom: 0;
}

#device-submit-button:hover {
  background-color: var(--theme-toolbar-hover);
}

#device-submit-button:hover:active {
  background-color: var(--submit-button-active-background-color);
  color: var(--submit-button-active-color);
}

/**
 * Device Adder
 */

#device-adder {
  display: flex;
  flex-direction: column;
  margin: 0 20px;
}

#device-adder-content {
  display: flex;
}

#device-adder-column-1 {
  flex: 1;
  margin-right: 10px;
}

#device-adder-column-2 {
  flex: 2;
}

#device-adder button {
  background-color: var(--theme-tab-toolbar-background);
  border: 1px solid var(--theme-splitter-color);
  border-radius: 2px;
  color: var(--theme-body-color);
  margin: 0 auto;
}

#device-adder label {
  display: flex;
  margin-bottom: 5px;
  align-items: center;
}

#device-adder label > input,
#device-adder label > .viewport-dimension {
  flex: 1;
  margin: 0;
}

#device-adder input {
  background: transparent;
  border: 1px solid transparent;
  text-align: center;
  color: var(--theme-body-color-inactive);
  transition: all 0.25s ease;
}

#device-adder input:focus {
  color: var(--viewport-active-color);
}

#device-adder label > input:focus,
#device-adder label > .viewport-dimension:focus  {
  border-bottom: 1px solid var(--theme-selection-background);
  outline: none;
}

.device-adder-label {
  display: inline-block;
  margin-right: 5px;
  min-width: 35px;
}
PK
!<h\dOdOBchrome/devtools/modules/devtools/client/responsive.html/manager.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Ci } = require("chrome");
const promise = require("promise");
const Services = require("Services");
const EventEmitter = require("devtools/shared/old-event-emitter");

const TOOL_URL = "chrome://devtools/content/responsive.html/index.xhtml";

loader.lazyRequireGetter(this, "DebuggerClient", "devtools/shared/client/debugger-client", true);
loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true);
loader.lazyRequireGetter(this, "throttlingProfiles", "devtools/client/shared/network-throttling-profiles");
loader.lazyRequireGetter(this, "swapToInnerBrowser", "devtools/client/responsive.html/browser/swap", true);
loader.lazyRequireGetter(this, "startup", "devtools/client/responsive.html/utils/window", true);
loader.lazyRequireGetter(this, "message", "devtools/client/responsive.html/utils/message");
loader.lazyRequireGetter(this, "showNotification", "devtools/client/responsive.html/utils/notification", true);
loader.lazyRequireGetter(this, "l10n", "devtools/client/responsive.html/utils/l10n");
loader.lazyRequireGetter(this, "EmulationFront", "devtools/shared/fronts/emulation", true);
loader.lazyRequireGetter(this, "PriorityLevels", "devtools/client/shared/components/NotificationBox", true);

const RELOAD_CONDITION_PREF_PREFIX = "devtools.responsive.reloadConditions.";
const RELOAD_NOTIFICATION_PREF = "devtools.responsive.reloadNotification.enabled";

function debug(msg) {
  // console.log(`RDM manager: ${msg}`);
}

/**
 * ResponsiveUIManager is the external API for the browser UI, etc. to use when
 * opening and closing the responsive UI.
 */
const ResponsiveUIManager = exports.ResponsiveUIManager = {
  activeTabs: new Map(),

  /**
   * Toggle the responsive UI for a tab.
   *
   * @param window
   *        The main browser chrome window.
   * @param tab
   *        The browser tab.
   * @param options
   *        Other options associated with toggling.  Currently includes:
   *        - `command`: Whether initiated via GCLI command bar or toolbox button
   * @return Promise
   *         Resolved when the toggling has completed.  If the UI has opened,
   *         it is resolved to the ResponsiveUI instance for this tab.  If the
   *         the UI has closed, there is no resolution value.
   */
  toggle(window, tab, options) {
    let action = this.isActiveForTab(tab) ? "close" : "open";
    let completed = this[action + "IfNeeded"](window, tab, options);
    completed.catch(console.error);
    return completed;
  },

  /**
   * Opens the responsive UI, if not already open.
   *
   * @param window
   *        The main browser chrome window.
   * @param tab
   *        The browser tab.
   * @param options
   *        Other options associated with opening.  Currently includes:
   *        - `command`: Whether initiated via GCLI command bar or toolbox button
   * @return Promise
   *         Resolved to the ResponsiveUI instance for this tab when opening is
   *         complete.
   */
  async openIfNeeded(window, tab, options) {
    if (!tab.linkedBrowser.isRemoteBrowser) {
      this.showRemoteOnlyNotification(window, tab, options);
      return promise.reject(new Error("RDM only available for remote tabs."));
    }
    // Remove this once we support this case in bug 1306975.
    if (tab.linkedBrowser.hasAttribute("usercontextid")) {
      this.showNoContainerTabsNotification(window, tab, options);
      return promise.reject(new Error("RDM not available for container tabs."));
    }
    if (!this.isActiveForTab(tab)) {
      this.initMenuCheckListenerFor(window);

      let ui = new ResponsiveUI(window, tab);
      this.activeTabs.set(tab, ui);
      await this.setMenuCheckFor(tab, window);
      await ui.inited;
      this.emit("on", { tab });
    }

    return this.getResponsiveUIForTab(tab);
  },

  /**
   * Closes the responsive UI, if not already closed.
   *
   * @param window
   *        The main browser chrome window.
   * @param tab
   *        The browser tab.
   * @param options
   *        Other options associated with closing.  Currently includes:
   *        - `command`: Whether initiated via GCLI command bar or toolbox button
   *        - `reason`: String detailing the specific cause for closing
   * @return Promise
   *         Resolved (with no value) when closing is complete.
   */
  async closeIfNeeded(window, tab, options) {
    if (this.isActiveForTab(tab)) {
      let ui = this.activeTabs.get(tab);
      let destroyed = await ui.destroy(options);
      if (!destroyed) {
        // Already in the process of destroying, abort.
        return;
      }
      this.activeTabs.delete(tab);

      if (!this.isActiveForWindow(window)) {
        this.removeMenuCheckListenerFor(window);
      }
      this.emit("off", { tab });
      await this.setMenuCheckFor(tab, window);
    }
  },

  /**
   * Returns true if responsive UI is active for a given tab.
   *
   * @param tab
   *        The browser tab.
   * @return boolean
   */
  isActiveForTab(tab) {
    return this.activeTabs.has(tab);
  },

  /**
   * Returns true if responsive UI is active in any tab in the given window.
   *
   * @param window
   *        The main browser chrome window.
   * @return boolean
   */
  isActiveForWindow(window) {
    return [...this.activeTabs.keys()].some(t => t.ownerGlobal === window);
  },

  /**
   * Return the responsive UI controller for a tab.
   *
   * @param tab
   *        The browser tab.
   * @return ResponsiveUI
   *         The UI instance for this tab.
   */
  getResponsiveUIForTab(tab) {
    return this.activeTabs.get(tab);
  },

  /**
   * Handle GCLI commands.
   *
   * @param window
   *        The main browser chrome window.
   * @param tab
   *        The browser tab.
   * @param command
   *        The GCLI command name.
   * @param args
   *        The GCLI command arguments.
   */
  handleGcliCommand(window, tab, command, args) {
    let completed;
    switch (command) {
      case "resize to":
        completed = this.openIfNeeded(window, tab, { command: true });
        this.activeTabs.get(tab).setViewportSize(args);
        break;
      case "resize on":
        completed = this.openIfNeeded(window, tab, { command: true });
        break;
      case "resize off":
        completed = this.closeIfNeeded(window, tab, { command: true });
        break;
      case "resize toggle":
        completed = this.toggle(window, tab, { command: true });
        break;
      default:
    }
    completed.catch(console.error);
  },

  handleMenuCheck({target}) {
    ResponsiveUIManager.setMenuCheckFor(target);
  },

  initMenuCheckListenerFor(window) {
    let { tabContainer } = window.gBrowser;
    tabContainer.addEventListener("TabSelect", this.handleMenuCheck);
  },

  removeMenuCheckListenerFor(window) {
    if (window && window.gBrowser && window.gBrowser.tabContainer) {
      let { tabContainer } = window.gBrowser;
      tabContainer.removeEventListener("TabSelect", this.handleMenuCheck);
    }
  },

  async setMenuCheckFor(tab, window = tab.ownerGlobal) {
    await startup(window);

    let menu = window.document.getElementById("menu_responsiveUI");
    if (menu) {
      menu.setAttribute("checked", this.isActiveForTab(tab));
    }
  },

  showRemoteOnlyNotification(window, tab, { command } = {}) {
    showNotification(window, tab, {
      command,
      msg: l10n.getStr("responsive.remoteOnly"),
      priority: PriorityLevels.PRIORITY_CRITICAL_MEDIUM,
    });
  },

  showNoContainerTabsNotification(window, tab, { command } = {}) {
    showNotification(window, tab, {
      command,
      msg: l10n.getStr("responsive.noContainerTabs"),
      priority: PriorityLevels.PRIORITY_CRITICAL_MEDIUM,
    });
  },
};

// GCLI commands in ./commands.js listen for events from this object to know
// when the UI for a tab has opened or closed.
EventEmitter.decorate(ResponsiveUIManager);

/**
 * ResponsiveUI manages the responsive design tool for a specific tab.  The
 * actual tool itself lives in a separate chrome:// document that is loaded into
 * the tab upon opening responsive design.  This object acts a helper to
 * integrate the tool into the surrounding browser UI as needed.
 */
function ResponsiveUI(window, tab) {
  this.browserWindow = window;
  this.tab = tab;
  this.inited = this.init();
}

ResponsiveUI.prototype = {

  /**
   * The main browser chrome window (that holds many tabs).
   */
  browserWindow: null,

  /**
   * The specific browser tab this responsive instance is for.
   */
  tab: null,

  /**
   * Promise resovled when the UI init has completed.
   */
  inited: null,

  /**
   * Flag set when destruction has begun.
   */
  destroying: false,

  /**
   * Flag set when destruction has ended.
   */
  destroyed: false,

  /**
   * A window reference for the chrome:// document that displays the responsive
   * design tool.  It is safe to reference this window directly even with e10s,
   * as the tool UI is always loaded in the parent process.  The web content
   * contained *within* the tool UI on the other hand is loaded in the child
   * process.
   */
  toolWindow: null,

  /**
   * Open RDM while preserving the state of the page.  We use `swapFrameLoaders`
   * to ensure all in-page state is preserved, just like when you move a tab to
   * a new window.
   *
   * For more details, see /devtools/docs/responsive-design-mode.md.
   */
  async init() {
    debug("Init start");

    let ui = this;

    // Watch for tab close and window close so we can clean up RDM synchronously
    this.tab.addEventListener("TabClose", this);
    this.browserWindow.addEventListener("unload", this);

    // Swap page content from the current tab into a viewport within RDM
    debug("Create browser swapper");
    this.swap = swapToInnerBrowser({
      tab: this.tab,
      containerURL: TOOL_URL,
      async getInnerBrowser(containerBrowser) {
        let toolWindow = ui.toolWindow = containerBrowser.contentWindow;
        toolWindow.addEventListener("message", ui);
        debug("Wait until init from inner");
        await message.request(toolWindow, "init");
        toolWindow.addInitialViewport("about:blank");
        debug("Wait until browser mounted");
        await message.wait(toolWindow, "browser-mounted");
        return ui.getViewportBrowser();
      }
    });
    debug("Wait until swap start");
    await this.swap.start();

    this.tab.addEventListener("BeforeTabRemotenessChange", this);

    // Notify the inner browser to start the frame script
    debug("Wait until start frame script");
    await message.request(this.toolWindow, "start-frame-script");

    // Get the protocol ready to speak with emulation actor
    debug("Wait until RDP server connect");
    await this.connectToServer();

    // Non-blocking message to tool UI to start any delayed init activities
    message.post(this.toolWindow, "post-init");

    debug("Init done");
  },

  /**
   * Close RDM and restore page content back into a regular tab.
   *
   * @param object
   *        Destroy options, which currently includes a `reason` string.
   * @return boolean
   *         Whether this call is actually destroying.  False means destruction
   *         was already in progress.
   */
  async destroy(options) {
    if (this.destroying) {
      return false;
    }
    this.destroying = true;

    // If our tab is about to be closed, there's not enough time to exit
    // gracefully, but that shouldn't be a problem since the tab will go away.
    // So, skip any waiting when we're about to close the tab.
    let isWindowClosing = options && options.reason === "unload";
    let isTabContentDestroying =
      isWindowClosing || (options && (options.reason === "TabClose" ||
                                      options.reason === "BeforeTabRemotenessChange"));

    // Ensure init has finished before starting destroy
    if (!isTabContentDestroying) {
      await this.inited;
    }

    this.tab.removeEventListener("TabClose", this);
    this.tab.removeEventListener("BeforeTabRemotenessChange", this);
    this.browserWindow.removeEventListener("unload", this);
    this.toolWindow.removeEventListener("message", this);

    if (!isTabContentDestroying) {
      // Notify the inner browser to stop the frame script
      await message.request(this.toolWindow, "stop-frame-script");
    }

    // Ensure the tab is reloaded if required when exiting RDM so that no emulated
    // settings are left in a customized state.
    if (!isTabContentDestroying) {
      let reloadNeeded = false;
      await this.updateDPPX();
      await this.updateNetworkThrottling();
      reloadNeeded |= await this.updateUserAgent() &&
                      this.reloadOnChange("userAgent");
      reloadNeeded |= await this.updateTouchSimulation() &&
                      this.reloadOnChange("touchSimulation");
      if (reloadNeeded) {
        this.getViewportBrowser().reload();
      }
    }

    // Destroy local state
    let swap = this.swap;
    this.browserWindow = null;
    this.tab = null;
    this.inited = null;
    this.toolWindow = null;
    this.swap = null;

    // Close the debugger client used to speak with emulation actor.
    // The actor handles clearing any overrides itself, so it's not necessary to clear
    // anything on shutdown client side.
    let clientClosed = this.client.close();
    if (!isTabContentDestroying) {
      await clientClosed;
    }
    this.client = this.emulationFront = null;

    if (!isWindowClosing) {
      // Undo the swap and return the content back to a normal tab
      swap.stop();
    }

    this.destroyed = true;

    return true;
  },

  async connectToServer() {
    DebuggerServer.init();
    DebuggerServer.registerAllActors();
    this.client = new DebuggerClient(DebuggerServer.connectPipe());
    await this.client.connect();
    let { tab } = await this.client.getTab();
    this.emulationFront = EmulationFront(this.client, tab);
  },

  /**
   * Show one-time notification about reloads for emulation.
   */
  showReloadNotification() {
    if (Services.prefs.getBoolPref(RELOAD_NOTIFICATION_PREF, false)) {
      showNotification(this.browserWindow, this.tab, {
        msg: l10n.getFormatStr("responsive.reloadNotification.description",
                               l10n.getStr("responsive.reloadConditions.label")),
      });
      Services.prefs.setBoolPref(RELOAD_NOTIFICATION_PREF, false);
    }
  },

  reloadOnChange(id) {
    this.showReloadNotification();
    let pref = RELOAD_CONDITION_PREF_PREFIX + id;
    return Services.prefs.getBoolPref(pref, false);
  },

  handleEvent(event) {
    let { browserWindow, tab } = this;

    switch (event.type) {
      case "message":
        this.handleMessage(event);
        break;
      case "BeforeTabRemotenessChange":
      case "TabClose":
      case "unload":
        ResponsiveUIManager.closeIfNeeded(browserWindow, tab, {
          reason: event.type,
        });
        break;
    }
  },

  handleMessage(event) {
    if (event.origin !== "chrome://devtools") {
      return;
    }

    switch (event.data.type) {
      case "change-device":
        this.onChangeDevice(event);
        break;
      case "change-network-throtting":
        this.onChangeNetworkThrottling(event);
        break;
      case "change-pixel-ratio":
        this.onChangePixelRatio(event);
        break;
      case "change-touch-simulation":
        this.onChangeTouchSimulation(event);
        break;
      case "content-resize":
        this.onContentResize(event);
        break;
      case "exit":
        this.onExit();
        break;
      case "remove-device-association":
        this.onRemoveDeviceAssociation(event);
        break;
    }
  },

  async onChangeDevice(event) {
    let { userAgent, pixelRatio, touch } = event.data.device;
    let reloadNeeded = false;
    await this.updateDPPX(pixelRatio);
    reloadNeeded |= await this.updateUserAgent(userAgent) &&
                    this.reloadOnChange("userAgent");
    reloadNeeded |= await this.updateTouchSimulation(touch) &&
                    this.reloadOnChange("touchSimulation");
    if (reloadNeeded) {
      this.getViewportBrowser().reload();
    }
    // Used by tests
    this.emit("device-changed");
  },

  async onChangeNetworkThrottling(event) {
    let { enabled, profile } = event.data;
    await this.updateNetworkThrottling(enabled, profile);
    // Used by tests
    this.emit("network-throttling-changed");
  },

  onChangePixelRatio(event) {
    let { pixelRatio } = event.data;
    this.updateDPPX(pixelRatio);
  },

  async onChangeTouchSimulation(event) {
    let { enabled } = event.data;
    let reloadNeeded = await this.updateTouchSimulation(enabled) &&
                       this.reloadOnChange("touchSimulation");
    if (reloadNeeded) {
      this.getViewportBrowser().reload();
    }
    // Used by tests
    this.emit("touch-simulation-changed");
  },

  onContentResize(event) {
    let { width, height } = event.data;
    this.emit("content-resize", {
      width,
      height,
    });
  },

  onExit() {
    let { browserWindow, tab } = this;
    ResponsiveUIManager.closeIfNeeded(browserWindow, tab);
  },

  async onRemoveDeviceAssociation(event) {
    let reloadNeeded = false;
    await this.updateDPPX();
    reloadNeeded |= await this.updateUserAgent() &&
                    this.reloadOnChange("userAgent");
    reloadNeeded |= await this.updateTouchSimulation() &&
                    this.reloadOnChange("touchSimulation");
    if (reloadNeeded) {
      this.getViewportBrowser().reload();
    }
    // Used by tests
    this.emit("device-association-removed");
  },

  /**
   * Set or clear the emulated device pixel ratio.
   *
   * @return boolean
   *         Whether a reload is needed to apply the change.
   *         (This is always immediate, so it's always false.)
   */
  async updateDPPX(dppx) {
    if (!dppx) {
      await this.emulationFront.clearDPPXOverride();
      return false;
    }
    await this.emulationFront.setDPPXOverride(dppx);
    return false;
  },

  /**
   * Set or clear network throttling.
   *
   * @return boolean
   *         Whether a reload is needed to apply the change.
   *         (This is always immediate, so it's always false.)
   */
  async updateNetworkThrottling(enabled, profile) {
    if (!enabled) {
      await this.emulationFront.clearNetworkThrottling();
      return false;
    }
    let data = throttlingProfiles.find(({ id }) => id == profile);
    let { download, upload, latency } = data;
    await this.emulationFront.setNetworkThrottling({
      downloadThroughput: download,
      uploadThroughput: upload,
      latency,
    });
    return false;
  },

  /**
   * Set or clear the emulated user agent.
   *
   * @return boolean
   *         Whether a reload is needed to apply the change.
   */
  updateUserAgent(userAgent) {
    if (!userAgent) {
      return this.emulationFront.clearUserAgentOverride();
    }
    return this.emulationFront.setUserAgentOverride(userAgent);
  },

  /**
   * Set or clear touch simulation.
   *
   * @return boolean
   *         Whether a reload is needed to apply the change.
   */
  updateTouchSimulation(enabled) {
    if (!enabled) {
      return this.emulationFront.clearTouchEventsOverride();
    }
    return this.emulationFront.setTouchEventsOverride(
      Ci.nsIDocShell.TOUCHEVENTS_OVERRIDE_ENABLED
    );
  },

  /**
   * Helper for tests. Assumes a single viewport for now.
   */
  getViewportSize() {
    return this.toolWindow.getViewportSize();
  },

  /**
   * Helper for tests, GCLI, etc. Assumes a single viewport for now.
   */
  async setViewportSize(size) {
    await this.inited;
    this.toolWindow.setViewportSize(size);
  },

  /**
   * Helper for tests/reloading the viewport. Assumes a single viewport for now.
   */
  getViewportBrowser() {
    return this.toolWindow.getViewportBrowser();
  },

  /**
   * Helper for contacting the viewport content. Assumes a single viewport for now.
   */
  getViewportMessageManager() {
    return this.getViewportBrowser().messageManager;
  },

};

EventEmitter.decorate(ResponsiveUI.prototype);
PK
!<|��sH	H	Kchrome/devtools/modules/devtools/client/responsive.html/reducers/devices.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  ADD_DEVICE,
  ADD_DEVICE_TYPE,
  LOAD_DEVICE_LIST_START,
  LOAD_DEVICE_LIST_ERROR,
  LOAD_DEVICE_LIST_END,
  REMOVE_DEVICE,
  UPDATE_DEVICE_DISPLAYED,
  UPDATE_DEVICE_MODAL,
} = require("../actions/index");

const Types = require("../types");

const INITIAL_DEVICES = {
  types: [],
  isModalOpen: false,
  modalOpenedFromViewport: null,
  listState: Types.loadableState.INITIALIZED,
};

let reducers = {

  [ADD_DEVICE](devices, { device, deviceType }) {
    return Object.assign({}, devices, {
      [deviceType]: [...devices[deviceType], device],
    });
  },

  [ADD_DEVICE_TYPE](devices, { deviceType }) {
    return Object.assign({}, devices, {
      types: [...devices.types, deviceType],
      [deviceType]: [],
    });
  },

  [UPDATE_DEVICE_DISPLAYED](devices, { device, deviceType, displayed }) {
    let newDevices = devices[deviceType].map(d => {
      if (d == device) {
        d.displayed = displayed;
      }

      return d;
    });

    return Object.assign({}, devices, {
      [deviceType]: newDevices,
    });
  },

  [LOAD_DEVICE_LIST_START](devices, action) {
    return Object.assign({}, devices, {
      listState: Types.loadableState.LOADING,
    });
  },

  [LOAD_DEVICE_LIST_ERROR](devices, action) {
    return Object.assign({}, devices, {
      listState: Types.loadableState.ERROR,
    });
  },

  [LOAD_DEVICE_LIST_END](devices, action) {
    return Object.assign({}, devices, {
      listState: Types.loadableState.LOADED,
    });
  },

  [REMOVE_DEVICE](devices, { device, deviceType }) {
    let index = devices[deviceType].indexOf(device);
    if (index < 0) {
      return devices;
    }

    let list = [...devices[deviceType]];
    list.splice(index, 1);
    return Object.assign({}, devices, {
      [deviceType]: list
    });
  },

  [UPDATE_DEVICE_MODAL](devices, { isOpen, modalOpenedFromViewport }) {
    return Object.assign({}, devices, {
      isModalOpen: isOpen,
      modalOpenedFromViewport,
    });
  },

};

module.exports = function (devices = INITIAL_DEVICES, action) {
  let reducer = reducers[action.type];
  if (!reducer) {
    return devices;
  }
  return reducer(devices, action);
};
PK
!<ݕ1���Wchrome/devtools/modules/devtools/client/responsive.html/reducers/display-pixel-ratio.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 { CHANGE_DISPLAY_PIXEL_RATIO } = require("../actions/index");
const INITIAL_DISPLAY_PIXEL_RATIO = 0;

let reducers = {

  [CHANGE_DISPLAY_PIXEL_RATIO](_, action) {
    return action.displayPixelRatio;
  },

};

module.exports = function (displayPixelRatio = INITIAL_DISPLAY_PIXEL_RATIO, action) {
  let reducer = reducers[action.type];
  if (!reducer) {
    return displayPixelRatio;
  }
  return reducer(displayPixelRatio, action);
};
PK
!<#��TTLchrome/devtools/modules/devtools/client/responsive.html/reducers/location.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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_LOCATION } = require("../actions/index");

const INITIAL_LOCATION = "about:blank";

let reducers = {

  [CHANGE_LOCATION](_, action) {
    return action.location;
  },

};

module.exports = function (location = INITIAL_LOCATION, action) {
  let reducer = reducers[action.type];
  if (!reducer) {
    return location;
  }
  return reducer(location, action);
};
PK
!<�¨_��Vchrome/devtools/modules/devtools/client/responsive.html/reducers/network-throttling.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("../actions/index");

const INITIAL_NETWORK_THROTTLING = {
  enabled: false,
  profile: "",
};

let reducers = {

  [CHANGE_NETWORK_THROTTLING](throttling, { enabled, profile }) {
    return {
      enabled,
      profile,
    };
  },

};

module.exports = function (throttling = INITIAL_NETWORK_THROTTLING, action) {
  let reducer = reducers[action.type];
  if (!reducer) {
    return throttling;
  }
  return reducer(throttling, action);
};
PK
!<���x��Uchrome/devtools/modules/devtools/client/responsive.html/reducers/reload-conditions.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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_RELOAD_CONDITION,
  LOAD_RELOAD_CONDITIONS_END,
} = require("../actions/index");

const Types = require("../types");

const INITIAL_RELOAD_CONDITIONS = {
  touchSimulation: false,
  userAgent: false,
  state: Types.loadableState.INITIALIZED,
};

let reducers = {

  [CHANGE_RELOAD_CONDITION](conditions, { id, value }) {
    return Object.assign({}, conditions, {
      [id]: value,
    });
  },

  [LOAD_RELOAD_CONDITIONS_END](conditions) {
    return Object.assign({}, conditions, {
      state: Types.loadableState.LOADED,
    });
  },

};

module.exports = function (conditions = INITIAL_RELOAD_CONDITIONS, action) {
  let reducer = reducers[action.type];
  if (!reducer) {
    return conditions;
  }
  return reducer(conditions, action);
};
PK
!<|�ޞ00Nchrome/devtools/modules/devtools/client/responsive.html/reducers/screenshot.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  TAKE_SCREENSHOT_END,
  TAKE_SCREENSHOT_START,
} = require("../actions/index");

const INITIAL_SCREENSHOT = { isCapturing: false };

let reducers = {

  [TAKE_SCREENSHOT_END](screenshot, action) {
    return Object.assign({}, screenshot, { isCapturing: false });
  },

  [TAKE_SCREENSHOT_START](screenshot, action) {
    return Object.assign({}, screenshot, { isCapturing: true });
  },
};

module.exports = function (screenshot = INITIAL_SCREENSHOT, action) {
  let reducer = reducers[action.type];
  if (!reducer) {
    return screenshot;
  }
  return reducer(screenshot, action);
};
PK
!<ޭ=o��Tchrome/devtools/modules/devtools/client/responsive.html/reducers/touch-simulation.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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_TOUCH_SIMULATION,
} = require("../actions/index");

const INITIAL_TOUCH_SIMULATION = {
  enabled: false,
};

let reducers = {

  [CHANGE_TOUCH_SIMULATION](touchSimulation, { enabled }) {
    return Object.assign({}, touchSimulation, {
      enabled,
    });
  },

};

module.exports = function (touchSimulation = INITIAL_TOUCH_SIMULATION, action) {
  let reducer = reducers[action.type];
  if (!reducer) {
    return touchSimulation;
  }
  return reducer(touchSimulation, action);
};
PK
!<�\��	�	Mchrome/devtools/modules/devtools/client/responsive.html/reducers/viewports.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  ADD_VIEWPORT,
  CHANGE_DEVICE,
  CHANGE_PIXEL_RATIO,
  REMOVE_DEVICE_ASSOCIATION,
  RESIZE_VIEWPORT,
  ROTATE_VIEWPORT,
} = require("../actions/index");

let nextViewportId = 0;

const INITIAL_VIEWPORTS = [];
const INITIAL_VIEWPORT = {
  id: nextViewportId++,
  device: "",
  deviceType: "",
  width: 320,
  height: 480,
  pixelRatio: {
    value: 0,
  },
};

let reducers = {

  [ADD_VIEWPORT](viewports) {
    // For the moment, there can be at most one viewport.
    if (viewports.length === 1) {
      return viewports;
    }
    return [...viewports, Object.assign({}, INITIAL_VIEWPORT)];
  },

  [CHANGE_DEVICE](viewports, { id, device, deviceType }) {
    return viewports.map(viewport => {
      if (viewport.id !== id) {
        return viewport;
      }

      return Object.assign({}, viewport, {
        device,
        deviceType,
      });
    });
  },

  [CHANGE_PIXEL_RATIO](viewports, { id, pixelRatio }) {
    return viewports.map(viewport => {
      if (viewport.id !== id) {
        return viewport;
      }

      return Object.assign({}, viewport, {
        pixelRatio: {
          value: pixelRatio
        },
      });
    });
  },

  [REMOVE_DEVICE_ASSOCIATION](viewports, { id }) {
    return viewports.map(viewport => {
      if (viewport.id !== id) {
        return viewport;
      }

      return Object.assign({}, viewport, {
        device: "",
        deviceType: "",
      });
    });
  },

  [RESIZE_VIEWPORT](viewports, { id, width, height }) {
    return viewports.map(viewport => {
      if (viewport.id !== id) {
        return viewport;
      }

      if (!width) {
        width = viewport.width;
      }
      if (!height) {
        height = viewport.height;
      }

      return Object.assign({}, viewport, {
        width,
        height,
      });
    });
  },

  [ROTATE_VIEWPORT](viewports, { id }) {
    return viewports.map(viewport => {
      if (viewport.id !== id) {
        return viewport;
      }

      return Object.assign({}, viewport, {
        width: viewport.height,
        height: viewport.width,
      });
    });
  },

};

module.exports = function (viewports = INITIAL_VIEWPORTS, action) {
  let reducer = reducers[action.type];
  if (!reducer) {
    return viewports;
  }
  return reducer(viewports, action);
};
PK
!<��[6��Cchrome/devtools/modules/devtools/client/responsive.html/reducers.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

exports.devices = require("./reducers/devices");
exports.displayPixelRatio = require("./reducers/display-pixel-ratio");
exports.location = require("./reducers/location");
exports.networkThrottling = require("./reducers/network-throttling");
exports.reloadConditions = require("./reducers/reload-conditions");
exports.screenshot = require("./reducers/screenshot");
exports.touchSimulation = require("./reducers/touch-simulation");
exports.viewports = require("./reducers/viewports");
PK
!<�W3Q��Ichrome/devtools/modules/devtools/client/responsive.html/responsive-ua.css@namespace url(http://www.w3.org/1999/xhtml);

/* Reset default UA styles for dropdown options */
*|*::-moz-dropdown-list {
  border: 0 !important;
}
PK
!<��m[[@chrome/devtools/modules/devtools/client/responsive.html/store.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 } = require("devtools/client/shared/vendor/redux");
const createStore = require("devtools/client/shared/redux/create-store");
const reducers = require("./reducers");
const flags = require("devtools/shared/flags");

module.exports = function () {
  let shouldLog = false;
  let history;

  // If testing, store the action history in an array
  // we'll later attach to the store
  if (flags.testing) {
    history = [];
    shouldLog = true;
  }

  let store = createStore({
    log: shouldLog,
    history
  })(combineReducers(reducers), {});

  if (history) {
    store.history = history;
  }

  return store;
};
PK
!<Q�JX99@chrome/devtools/modules/devtools/client/responsive.html/types.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/vendor/react-prop-types");
const { createEnum } = require("devtools/client/shared/enum");

// React PropTypes are used to describe the expected "shape" of various common
// objects that get passed down as props to components.

/* ENUMS */

/**
 * An enum containing the possible states for loadable things.
 */
exports.loadableState = createEnum([
  "INITIALIZED",
  "LOADING",
  "LOADED",
  "ERROR",
]);

/* GLOBAL */

/**
 * The location of the document displayed in the viewport(s).
 */
exports.location = PropTypes.string;

/**
 * Whether to reload the page automatically when certain actions occur.
 */
exports.reloadConditions = {

  // Whether to reload when touch simulation is toggled
  touchSimulation: PropTypes.bool,

  // Whether to reload when user agent is changed
  userAgent: PropTypes.bool,

  // Loaded state of these conditions
  state: PropTypes.oneOf(Object.keys(exports.loadableState)),

};

/* DEVICE */

/**
 * A single device that can be displayed in the viewport.
 */
const device = {

  // The name of the device
  name: PropTypes.string,

  // The width of the device
  width: PropTypes.number,

  // The height of the device
  height: PropTypes.number,

  // The pixel ratio of the device
  pixelRatio: PropTypes.number,

  // The user agent string of the device
  userAgent: PropTypes.string,

  // Whether or not it is a touch device
  touch: PropTypes.bool,

  // The operating system of the device
  os: PropTypes.String,

  // Whether or not the device is displayed in the device selector
  displayed: PropTypes.bool,

};

/**
 * A list of devices and their types that can be displayed in the viewport.
 */
exports.devices = {

  // An array of device types
  types: PropTypes.arrayOf(PropTypes.string),

  // An array of phone devices
  phones: PropTypes.arrayOf(PropTypes.shape(device)),

  // An array of tablet devices
  tablets: PropTypes.arrayOf(PropTypes.shape(device)),

  // An array of laptop devices
  laptops: PropTypes.arrayOf(PropTypes.shape(device)),

  // An array of television devices
  televisions: PropTypes.arrayOf(PropTypes.shape(device)),

  // An array of console devices
  consoles: PropTypes.arrayOf(PropTypes.shape(device)),

  // An array of watch devices
  watches: PropTypes.arrayOf(PropTypes.shape(device)),

  // Whether or not the device modal is open
  isModalOpen: PropTypes.bool,

  // Viewport id that triggered the modal to open
  modalOpenedFromViewport: PropTypes.number,

  // Device list state, possible values are exported above in an enum
  listState: PropTypes.oneOf(Object.keys(exports.loadableState)),

};

/* VIEWPORT */

/**
 * Network throttling state for a given viewport.
 */
exports.networkThrottling = {

  // Whether or not network throttling is enabled
  enabled: PropTypes.bool,

  // Name of the selected throttling profile
  profile: PropTypes.string,

};

/**
 * Device pixel ratio for a given viewport.
 */
const pixelRatio = exports.pixelRatio = {

  // The device pixel ratio value
  value: PropTypes.number,

};

/**
 * Touch simulation state for a given viewport.
 */
exports.touchSimulation = {

  // Whether or not touch simulation is enabled
  enabled: PropTypes.bool,

};

/**
 * A single viewport displaying a document.
 */
exports.viewport = {

  // The id of the viewport
  id: PropTypes.number,

  // The currently selected device applied to the viewport
  device: PropTypes.string,

  // The currently selected device type applied to the viewport
  deviceType: PropTypes.string,

  // The width of the viewport
  width: PropTypes.number,

  // The height of the viewport
  height: PropTypes.number,

  // The device pixel ratio of the viewport
  pixelRatio: PropTypes.shape(pixelRatio),

};

/* ACTIONS IN PROGRESS */

/**
 * The progression of the screenshot.
 */
exports.screenshot = {

  // Whether screenshot capturing is in progress
  isCapturing: PropTypes.bool,

};
PK
!<��b���Dchrome/devtools/modules/devtools/client/responsive.html/utils/css.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { getDOMWindowUtils } = require("./window");

/**
 * Synchronously loads an agent style sheet from `uri` and adds it to the list of
 * additional style sheets of the document.  The sheets added takes effect immediately,
 * and only on the document of the `window` given.
 */
function loadAgentSheet(window, url) {
  let winUtils = getDOMWindowUtils(window);
  winUtils.loadSheetUsingURIString(url, winUtils.AGENT_SHEET);
}
exports.loadAgentSheet = loadAgentSheet;
PK
!<�'�hhEchrome/devtools/modules/devtools/client/responsive.html/utils/e10s.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 prefix used for RDM messages in content.
// see: devtools/client/responsive.html/browser/content.js
const MESSAGE_PREFIX = "ResponsiveMode:";
const REQUEST_DONE_SUFFIX = ":Done";

/**
 * Registers a message `listener` that is called every time messages of
 * specified `message` is emitted on the given message manager.
 * @param {nsIMessageListenerManager} mm
 *    The Message Manager
 * @param {String} message
 *    The message. It will be prefixed with the constant `MESSAGE_PREFIX`
 * @param {Function} listener
 *    The listener function that processes the message.
 */
function on(mm, message, listener) {
  mm.addMessageListener(MESSAGE_PREFIX + message, listener);
}
exports.on = on;

/**
 * Removes a message `listener` for the specified `message` on the given
 * message manager.
 * @param {nsIMessageListenerManager} mm
 *    The Message Manager
 * @param {String} message
 *    The message. It will be prefixed with the constant `MESSAGE_PREFIX`
 * @param {Function} listener
 *    The listener function that processes the message.
 */
function off(mm, message, listener) {
  mm.removeMessageListener(MESSAGE_PREFIX + message, listener);
}
exports.off = off;

/**
 * Resolves a promise the next time the specified `message` is sent over the
 * given message manager.
 * @param {nsIMessageListenerManager} mm
 *    The Message Manager
 * @param {String} message
 *    The message. It will be prefixed with the constant `MESSAGE_PREFIX`
 * @returns {Promise}
 *    A promise that is resolved when the given message is emitted.
 */
function once(mm, message) {
  return new Promise(resolve => {
    on(mm, message, function onMessage({data}) {
      off(mm, message, onMessage);
      resolve(data);
    });
  });
}
exports.once = once;

/**
 * Asynchronously emit a `message` to the listeners of the given message
 * manager.
 *
 * @param {nsIMessageListenerManager} mm
 *    The Message Manager
 * @param {String} message
 *    The message. It will be prefixed with the constant `MESSAGE_PREFIX`.
 * @param {Object} data
 *    A JSON object containing data to be delivered to the listeners.
 */
function emit(mm, message, data) {
  mm.sendAsyncMessage(MESSAGE_PREFIX + message, data);
}
exports.emit = emit;

/**
 * Asynchronously send a "request" over the given message manager, and returns
 * a promise that is resolved when the request is complete.
 *
 * @param {nsIMessageListenerManager} mm
 *    The Message Manager
 * @param {String} message
 *    The message. It will be prefixed with the constant `MESSAGE_PREFIX`, and
 *    also suffixed with `REQUEST_DONE_SUFFIX` for the reply.
 * @param {Object} data
 *    A JSON object containing data to be delivered to the listeners.
 * @returns {Promise}
 *    A promise that is resolved when the request is done.
 */
function request(mm, message, data) {
  let done = once(mm, message + REQUEST_DONE_SUFFIX);

  emit(mm, message, data);

  return done;
}
exports.request = request;
PK
!<��h�$$Dchrome/devtools/modules/devtools/client/responsive.html/utils/key.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/keycodes");

/**
 * 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];
  });
}

exports.isKeyIn = isKeyIn;
PK
!<]�y��Echrome/devtools/modules/devtools/client/responsive.html/utils/l10n.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/shared/l10n");
const STRINGS_URI = "devtools/client/locales/responsive.properties";
const L10N = new LocalizationHelper(STRINGS_URI);

module.exports = {
  getStr: (...args) => L10N.getStr(...args),
  getFormatStr: (...args) => L10N.getFormatStr(...args),
  getFormatStrWithNumbers: (...args) => L10N.getFormatStrWithNumbers(...args),
  numberWithDecimals: (...args) => L10N.numberWithDecimals(...args),
};
PK
!<_t�wwHchrome/devtools/modules/devtools/client/responsive.html/utils/message.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 REQUEST_DONE_SUFFIX = ":done";

function wait(win, type) {
  return new Promise(resolve => {
    let onMessage = event => {
      if (event.data.type !== type) {
        return;
      }
      win.removeEventListener("message", onMessage);
      resolve();
    };
    win.addEventListener("message", onMessage);
  });
}

/**
 * Post a message to some window.
 *
 * @param win
 *        The window to post to.
 * @param typeOrMessage
 *        Either a string or and an object representing the message to send.
 *        If this is a string, it will be expanded into an object with the string as the
 *        `type` field.  If this is an object, it will be sent as is.
 */
function post(win, typeOrMessage) {
  // When running unit tests on XPCShell, there is no window to send messages to.
  if (!win) {
    return;
  }

  let message = typeOrMessage;
  if (typeof typeOrMessage == "string") {
    message = {
      type: typeOrMessage,
    };
  }
  win.postMessage(message, "*");
}

function request(win, type) {
  let done = wait(win, type + REQUEST_DONE_SUFFIX);
  post(win, type);
  return done;
}

exports.wait = wait;
exports.post = post;
exports.request = request;
PK
!<P�*1��Mchrome/devtools/modules/devtools/client/responsive.html/utils/notification.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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, "TargetFactory", "devtools/client/framework/target", true);
loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);

/**
 * Displays a notification either at the browser or toolbox level, depending on whether
 * a toolbox is currently open for this tab.
 *
 * @param window
 *        The main browser chrome window.
 * @param tab
 *        The browser tab.
 * @param options
 *        Other options associated with opening.  Currently includes:
 *        - `command`: Whether initiated via GCLI command bar or toolbox button
 *        - `msg`: String to show in the notification
 *        - `priority`: Priority level for the notification, which affects the icon and
 *                      overall appearance.
 */
function showNotification(window, tab, { command, msg, priority } = {}) {
  // Default to using the browser's per-tab notification box
  let nbox = window.gBrowser.getNotificationBox(tab.linkedBrowser);

  // If opening was initiated by GCLI command bar or toolbox button, check for an open
  // toolbox for the tab.  If one exists, use the toolbox's notification box so that the
  // message is placed closer to the action taken by the user.
  if (command) {
    let target = TargetFactory.forTab(tab);
    let toolbox = gDevTools.getToolbox(target);
    if (toolbox) {
      nbox = toolbox.notificationBox;
    }
  }

  let value = "devtools-responsive";
  if (nbox.getNotificationWithValue(value)) {
    // Notification already displayed
    return;
  }

  if (!priority) {
    priority = nbox.PRIORITY_INFO_MEDIUM;
  }

  nbox.appendNotification(msg, value, null, priority, []);
}

exports.showNotification = showNotification;
PK
!<��==Gchrome/devtools/modules/devtools/client/responsive.html/utils/window.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Ci } = require("chrome");
const Services = require("Services");

/**
 * Returns the `nsIDOMWindow` toplevel window for any child/inner window
 */
function getToplevelWindow(window) {
  return window.QueryInterface(Ci.nsIInterfaceRequestor)
               .getInterface(Ci.nsIWebNavigation)
               .QueryInterface(Ci.nsIDocShellTreeItem)
               .rootTreeItem
               .QueryInterface(Ci.nsIInterfaceRequestor)
               .getInterface(Ci.nsIDOMWindow);
}
exports.getToplevelWindow = getToplevelWindow;

function getDOMWindowUtils(window) {
  return window.QueryInterface(Ci.nsIInterfaceRequestor)
               .getInterface(Ci.nsIDOMWindowUtils);
}
exports.getDOMWindowUtils = getDOMWindowUtils;

/**
 * Check if the given browser window has finished the startup.
 * @params {nsIDOMWindow} window
 */
const isStartupFinished = (window) =>
  window.gBrowserInit &&
  window.gBrowserInit.delayedStartupFinished;

function startup(window) {
  return new Promise(resolve => {
    if (isStartupFinished(window)) {
      resolve(window);
      return;
    }
    Services.obs.addObserver(function listener({ subject }) {
      if (subject === window) {
        Services.obs.removeObserver(listener, "browser-delayed-startup-finished");
        resolve(window);
      }
    }, "browser-delayed-startup-finished");
  });
}
exports.startup = startup;
PK
!<>Heh��Ichrome/devtools/modules/devtools/client/scratchpad/scratchpad-commands.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 l10n = require("gcli/l10n");
const {Cu} = require("chrome");

exports.items = [{
  item: "command",
  runAt: "client",
  name: "scratchpad",
  buttonId: "command-button-scratchpad",
  buttonClass: "command-button",
  tooltipText: l10n.lookup("scratchpadOpenTooltip"),
  hidden: true,
  exec: function (args, context) {
    const {ScratchpadManager} = require("resource://devtools/client/scratchpad/scratchpad-manager.jsm");
    ScratchpadManager.openScratchpad();
  }
}];
PK
!<�ٰSIchrome/devtools/modules/devtools/client/scratchpad/scratchpad-manager.jsm/* vim:set ts=2 sw=2 sts=2 et tw=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";

this.EXPORTED_SYMBOLS = ["ScratchpadManager"];

const SCRATCHPAD_WINDOW_URL = "chrome://devtools/content/scratchpad/scratchpad.xul";
const SCRATCHPAD_WINDOW_FEATURES = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";

const {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
const Services = require("Services");
const Telemetry = require("devtools/client/shared/telemetry");


/**
 * The ScratchpadManager object opens new Scratchpad windows and manages the state
 * of open scratchpads for session restore. There's only one ScratchpadManager in
 * the life of the browser.
 */
this.ScratchpadManager = {

  _nextUid: 1,
  _scratchpads: [],

  _telemetry: new Telemetry(),

  /**
   * Get the saved states of open scratchpad windows. Called by
   * session restore.
   *
   * @return array
   *         The array of scratchpad states.
   */
  getSessionState: function SPM_getSessionState()
  {
    return this._scratchpads;
  },

  /**
   * Restore scratchpad windows from the scratchpad session store file.
   * Called by session restore.
   *
   * @param function aSession
   *        The session object with scratchpad states.
   *
   * @return array
   *         The restored scratchpad windows.
   */
  restoreSession: function SPM_restoreSession(aSession)
  {
    if (!Array.isArray(aSession)) {
      return [];
    }

    let wins = [];
    aSession.forEach(function (state) {
      let win = this.openScratchpad(state);
      wins.push(win);
    }, this);

    return wins;
  },

  /**
   * Iterate through open scratchpad windows and save their states.
   */
  saveOpenWindows: function SPM_saveOpenWindows() {
    this._scratchpads = [];

    function clone(src) {
      let dest = {};

      for (let key in src) {
        if (src.hasOwnProperty(key)) {
          dest[key] = src[key];
        }
      }

      return dest;
    }

    // We need to clone objects we get from Scratchpad instances
    // because such (cross-window) objects have a property 'parent'
    // that holds on to a ChromeWindow instance. This means that
    // such objects are not primitive-values-only anymore so they
    // can leak.

    let enumerator = Services.wm.getEnumerator("devtools:scratchpad");
    while (enumerator.hasMoreElements()) {
      let win = enumerator.getNext();
      if (!win.closed && win.Scratchpad.initialized) {
        this._scratchpads.push(clone(win.Scratchpad.getState()));
      }
    }
  },

  /**
   * Open a new scratchpad window with an optional initial state.
   *
   * @param object aState
   *        Optional. The initial state of the scratchpad, an object
   *        with properties filename, text, and executionContext.
   *
   * @return nsIDomWindow
   *         The opened scratchpad window.
   */
  openScratchpad: function SPM_openScratchpad(aState)
  {
    let params = Cc["@mozilla.org/embedcomp/dialogparam;1"]
                 .createInstance(Ci.nsIDialogParamBlock);

    params.SetNumberStrings(2);
    params.SetString(0, this.createUid());

    if (aState) {
      if (typeof aState != "object") {
        return;
      }

      params.SetString(1, JSON.stringify(aState));
    }

    let win = Services.ww.openWindow(null, SCRATCHPAD_WINDOW_URL, "_blank",
                                     SCRATCHPAD_WINDOW_FEATURES, params);

    this._telemetry.toolOpened("scratchpad-window");
    let onClose = () => {
      this._telemetry.toolClosed("scratchpad-window");
    };
    win.addEventListener("unload", onClose);

    // Only add the shutdown observer if we've opened a scratchpad window.
    ShutdownObserver.init();

    return win;
  },

  /**
   * Create a unique ID for a new Scratchpad.
   */
  createUid: function SPM_createUid()
  {
    return JSON.stringify(this._nextUid++);
  }
};


/**
 * The ShutdownObserver listens for app shutdown and saves the current state
 * of the scratchpads for session restore.
 */
var ShutdownObserver = {
  _initialized: false,
  init() {
    if (this._initialized) {
      return;
    }

    Services.obs.addObserver(this, "quit-application-granted");

    this._initialized = true;
  },

  observe(message, topic) {
    if (topic == "quit-application-granted") {
      ScratchpadManager.saveOpenWindows();
      this.uninit();
    }
  },

  uninit() {
    Services.obs.removeObserver(this, "quit-application-granted");
  }
};
PK
!<�E�Q��Fchrome/devtools/modules/devtools/client/scratchpad/scratchpad-panel.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 {Cu} = require("chrome");
const EventEmitter = require("devtools/shared/event-emitter");
const promise = require("promise");
const defer = require("devtools/shared/defer");


function ScratchpadPanel(iframeWindow, toolbox) {
  let { Scratchpad } = iframeWindow;
  this._toolbox = toolbox;
  this.panelWin = iframeWindow;
  this.scratchpad = Scratchpad;

  Scratchpad.target = this.target;
  Scratchpad.hideMenu();

  let deferred = defer();
  this._readyObserver = deferred.promise;
  Scratchpad.addObserver({
    onReady: function () {
      Scratchpad.removeObserver(this);
      deferred.resolve();
    }
  });

  EventEmitter.decorate(this);
}
exports.ScratchpadPanel = ScratchpadPanel;

ScratchpadPanel.prototype = {
  /**
   * Open is effectively an asynchronous constructor. For the ScratchpadPanel,
   * by the time this is called, the Scratchpad will already be ready.
   */
  open: function () {
    return this._readyObserver.then(() => {
      this.isReady = true;
      this.emit("ready");
      return this;
    });
  },

  get target() {
    return this._toolbox.target;
  },

  destroy: function () {
    this.emit("destroyed");
    return promise.resolve();
  }
};
PK
!<F�$0��=chrome/devtools/modules/devtools/client/shadereditor/panel.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 { Cc, Ci, Cu, Cr } = require("chrome");
const promise = require("promise");
const EventEmitter = require("devtools/shared/old-event-emitter");
const { WebGLFront } = require("devtools/shared/fronts/webgl");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");

function ShaderEditorPanel(iframeWindow, toolbox) {
  this.panelWin = iframeWindow;
  this._toolbox = toolbox;
  this._destroyer = null;

  EventEmitter.decorate(this);
}

exports.ShaderEditorPanel = ShaderEditorPanel;

ShaderEditorPanel.prototype = {
  /**
   * Open is effectively an asynchronous constructor.
   *
   * @return object
   *         A promise that is resolved when the Shader Editor completes opening.
   */
  open: function () {
    let targetPromise;

    // Local debugging needs to make the target remote.
    if (!this.target.isRemote) {
      targetPromise = this.target.makeRemote();
    } else {
      targetPromise = promise.resolve(this.target);
    }

    return targetPromise
      .then(() => {
        this.panelWin.gToolbox = this._toolbox;
        this.panelWin.gTarget = this.target;
        this.panelWin.gFront = new WebGLFront(this.target.client, this.target.form);
        return this.panelWin.startupShaderEditor();
      })
      .then(() => {
        this.isReady = true;
        this.emit("ready");
        return this;
      })
      .catch(function onError(aReason) {
        DevToolsUtils.reportException("ShaderEditorPanel.prototype.open", aReason);
      });
  },

  // DevToolPanel API

  get target() {
    return this._toolbox.target;
  },

  destroy: function () {
    // Make sure this panel is not already destroyed.
    if (this._destroyer) {
      return this._destroyer;
    }

    return this._destroyer = this.panelWin.shutdownShaderEditor().then(() => {
      // Destroy front to ensure packet handler is removed from client
      this.panelWin.gFront.destroy();
      this.emit("destroyed");
    });
  }
};
PK
!<��=�xKxK@chrome/devtools/modules/devtools/client/shared/AppCacheUtils.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
 * validateManifest() warns of the following errors:
 *  - No manifest specified in page
 *  - Manifest is not utf-8
 *  - Manifest mimetype not text/cache-manifest
 *  - Manifest does not begin with "CACHE MANIFEST"
 *  - Page modified since appcache last changed
 *  - Duplicate entries
 *  - Conflicting entries e.g. in both CACHE and NETWORK sections or in cache
 *    but blocked by FALLBACK namespace
 *  - Detect referenced files that are not available
 *  - Detect referenced files that have cache-control set to no-store
 *  - Wildcards used in a section other than NETWORK
 *  - Spaces in URI not replaced with %20
 *  - Completely invalid URIs
 *  - Too many dot dot slash operators
 *  - SETTINGS section is valid
 *  - Invalid section name
 *  - etc.
 */

"use strict";

var { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", {});
var { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm", {});
var { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});

var { gDevTools } = require("devtools/client/framework/devtools");
var Services = require("Services");
var promise = require("promise");

this.EXPORTED_SYMBOLS = ["AppCacheUtils"];

function AppCacheUtils(documentOrUri) {
  this._parseManifest = this._parseManifest.bind(this);

  if (documentOrUri) {
    if (typeof documentOrUri == "string") {
      this.uri = documentOrUri;
    }
    if (/HTMLDocument/.test(documentOrUri.toString())) {
      this.doc = documentOrUri;
    }
  }
}

AppCacheUtils.prototype = {
  get cachePath() {
    return "";
  },

  validateManifest: function ACU_validateManifest() {
    return new Promise((resolve, reject) => {
      this.errors = [];
      // Check for missing manifest.
      this._getManifestURI().then(manifestURI => {
        this.manifestURI = manifestURI;

        if (!this.manifestURI) {
          this._addError(0, "noManifest");
          resolve(this.errors);
        }

        this._getURIInfo(this.manifestURI).then(uriInfo => {
          this._parseManifest(uriInfo).then(() => {
            // Sort errors by line number.
            this.errors.sort(function (a, b) {
              return a.line - b.line;
            });
            resolve(this.errors);
          });
        });
      });
    });
  },

  _parseManifest: function ACU__parseManifest(uriInfo) {
    return new Promise((resolve, reject) => {
      let manifestName = uriInfo.name;
      let manifestLastModified = new Date(uriInfo.responseHeaders["last-modified"]);

      if (uriInfo.charset.toLowerCase() != "utf-8") {
        this._addError(0, "notUTF8", uriInfo.charset);
      }

      if (uriInfo.mimeType != "text/cache-manifest") {
        this._addError(0, "badMimeType", uriInfo.mimeType);
      }

      let parser = new ManifestParser(uriInfo.text, this.manifestURI);
      let parsed = parser.parse();

      if (parsed.errors.length > 0) {
        this.errors.push.apply(this.errors, parsed.errors);
      }

      // Check for duplicate entries.
      let dupes = {};
      for (let parsedUri of parsed.uris) {
        dupes[parsedUri.uri] = dupes[parsedUri.uri] || [];
        dupes[parsedUri.uri].push({
          line: parsedUri.line,
          section: parsedUri.section,
          original: parsedUri.original
        });
      }
      for (let [uri, value] of Object.entries(dupes)) {
        if (value.length > 1) {
          this._addError(0, "duplicateURI", uri, JSON.stringify(value));
        }
      }

      // Loop through network entries making sure that fallback and cache don't
      // contain uris starting with the network uri.
      for (let neturi of parsed.uris) {
        if (neturi.section == "NETWORK") {
          for (let parsedUri of parsed.uris) {
            if (parsedUri.section !== "NETWORK" &&
                parsedUri.uri.startsWith(neturi.uri)) {
              this._addError(neturi.line, "networkBlocksURI", neturi.line,
                             neturi.original, parsedUri.line, parsedUri.original,
                             parsedUri.section);
            }
          }
        }
      }

      // Loop through fallback entries making sure that fallback and cache don't
      // contain uris starting with the network uri.
      for (let fb of parsed.fallbacks) {
        for (let parsedUri of parsed.uris) {
          if (parsedUri.uri.startsWith(fb.namespace)) {
            this._addError(fb.line, "fallbackBlocksURI", fb.line,
                           fb.original, parsedUri.line, parsedUri.original,
                           parsedUri.section);
          }
        }
      }

      // Check that all resources exist and that their cach-control headers are
      // not set to no-store.
      let current = -1;
      for (let i = 0, len = parsed.uris.length; i < len; i++) {
        let parsedUri = parsed.uris[i];
        this._getURIInfo(parsedUri.uri).then(uriInfo => {
          current++;

          if (uriInfo.success) {
            // Check that the resource was not modified after the manifest was last
            // modified. If it was then the manifest file should be refreshed.
            let resourceLastModified =
              new Date(uriInfo.responseHeaders["last-modified"]);

            if (manifestLastModified < resourceLastModified) {
              this._addError(parsedUri.line, "fileChangedButNotManifest",
                             uriInfo.name, manifestName, parsedUri.line);
            }

            // If cache-control: no-store the file will not be added to the
            // appCache.
            if (uriInfo.nocache) {
              this._addError(parsedUri.line, "cacheControlNoStore",
                             parsedUri.original, parsedUri.line);
            }
          } else if (parsedUri.original !== "*") {
            this._addError(parsedUri.line, "notAvailable",
                           parsedUri.original, parsedUri.line);
          }

          if (current == len - 1) {
            resolve();
          }
        });
      }
    });
  },

  _getURIInfo: function ACU__getURIInfo(uri) {
    return new Promise((resolve, reject) => {
      let inputStream = Cc["@mozilla.org/scriptableinputstream;1"]
                          .createInstance(Ci.nsIScriptableInputStream);
      let buffer = "";
      var channel = NetUtil.newChannel({
        uri: uri,
        loadUsingSystemPrincipal: true,
        securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL
      });

      // Avoid the cache:
      channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
      channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;

      channel.asyncOpen2({
        onStartRequest: function (request, context) {
          // This empty method is needed in order for onDataAvailable to be
          // called.
        },

        onDataAvailable: function (request, context, stream, offset, count) {
          request.QueryInterface(Ci.nsIHttpChannel);
          inputStream.init(stream);
          buffer = buffer.concat(inputStream.read(count));
        },

        onStopRequest: function onStartRequest(request, context, statusCode) {
          if (statusCode === 0) {
            request.QueryInterface(Ci.nsIHttpChannel);

            let result = {
              name: request.name,
              success: request.requestSucceeded,
              status: request.responseStatus + " - " + request.responseStatusText,
              charset: request.contentCharset || "utf-8",
              mimeType: request.contentType,
              contentLength: request.contentLength,
              nocache: request.isNoCacheResponse() || request.isNoStoreResponse(),
              prePath: request.URI.prePath + "/",
              text: buffer
            };

            result.requestHeaders = {};
            request.visitRequestHeaders(function (header, value) {
              result.requestHeaders[header.toLowerCase()] = value;
            });

            result.responseHeaders = {};
            request.visitResponseHeaders(function (header, value) {
              result.responseHeaders[header.toLowerCase()] = value;
            });

            resolve(result);
          } else {
            resolve({
              name: request.name,
              success: false
            });
          }
        }
      });
    });
  },

  listEntries: function ACU_show(searchTerm) {
    if (!Services.prefs.getBoolPref("browser.cache.disk.enable")) {
      throw new Error(l10n.GetStringFromName("cacheDisabled"));
    }

    let entries = [];

    let appCacheStorage = Services.cache2.appCacheStorage(Services.loadContextInfo.default, null);
    appCacheStorage.asyncVisitStorage({
      onCacheStorageInfo: function () {},

      onCacheEntryInfo: function (aURI, aIdEnhance, aDataSize, aFetchCount, aLastModifiedTime, aExpirationTime) {
        let lowerKey = aURI.asciiSpec.toLowerCase();

        if (searchTerm && !lowerKey.includes(searchTerm.toLowerCase())) {
          return;
        }

        if (aIdEnhance) {
          aIdEnhance += ":";
        }

        let entry = {
          "deviceID": "offline",
          "key": aIdEnhance + aURI.asciiSpec,
          "fetchCount": aFetchCount,
          "lastFetched": null,
          "lastModified": new Date(aLastModifiedTime * 1000),
          "expirationTime": new Date(aExpirationTime * 1000),
          "dataSize": aDataSize
        };

        entries.push(entry);
        return true;
      }
    }, true);

    if (entries.length === 0) {
      throw new Error(l10n.GetStringFromName("noResults"));
    }
    return entries;
  },

  viewEntry: function ACU_viewEntry(key) {
    let wm = Cc["@mozilla.org/appshell/window-mediator;1"]
               .getService(Ci.nsIWindowMediator);
    let win = wm.getMostRecentWindow(gDevTools.chromeWindowType);
    let url = "about:cache-entry?storage=appcache&context=&eid=&uri=" + key;
    win.openUILinkIn(url, "tab");
  },

  clearAll: function ACU_clearAll() {
    if (!Services.prefs.getBoolPref("browser.cache.disk.enable")) {
      throw new Error(l10n.GetStringFromName("cacheDisabled"));
    }

    let appCacheStorage = Services.cache2.appCacheStorage(Services.loadContextInfo.default, null);
    appCacheStorage.asyncEvictStorage({
      onCacheEntryDoomed: function (result) {}
    });
  },

  _getManifestURI: function ACU__getManifestURI() {
    return new Promise((resolve, reject) => {
      let getURI = () => {
        let htmlNode = this.doc.querySelector("html[manifest]");
        if (htmlNode) {
          let pageUri = this.doc.location ? this.doc.location.href : this.uri;
          let manifestURI = htmlNode.getAttribute("manifest");

          let originRegExp = new RegExp(/([a-z]*:\/\/[^/]*\/)/);
          if (originRegExp.test(manifestURI)) {
            return manifestURI;
          } else if (manifestURI.startsWith("/")) {
            return pageUri.match(originRegExp)[0] + manifestURI.substring(1);
          }

          return pageUri.substring(0, pageUri.lastIndexOf("/") + 1) + manifestURI;
        }
      };

      if (this.doc) {
        let uri = getURI();
        return resolve(uri);
      } else {
        this._getURIInfo(this.uri).then(uriInfo => {
          if (uriInfo.success) {
            let html = uriInfo.text;
            let parser = _DOMParser;
            this.doc = parser.parseFromString(html, "text/html");
            let uri = getURI();
            resolve(uri);
          } else {
            this.errors.push({
              line: 0,
              msg: l10n.GetStringFromName("invalidURI")
            });
          }
        });
      }
    });
  },

  _addError: function ACU__addError(line, l10nString, ...params) {
    let msg;

    if (params) {
      msg = l10n.formatStringFromName(l10nString, params, params.length);
    } else {
      msg = l10n.GetStringFromName(l10nString);
    }

    this.errors.push({
      line: line,
      msg: msg
    });
  },
};

/**
 * We use our own custom parser because we need far more detailed information
 * than the system manifest parser provides.
 *
 * @param {String} manifestText
 *        The text content of the manifest file.
 * @param {String} manifestURI
 *        The URI of the manifest file. This is used in calculating the path of
 *        relative URIs.
 */
function ManifestParser(manifestText, manifestURI) {
  this.manifestText = manifestText;
  this.origin = manifestURI.substr(0, manifestURI.lastIndexOf("/") + 1)
                           .replace(" ", "%20");
}

ManifestParser.prototype = {
  parse: function OCIMP_parse() {
    let lines = this.manifestText.split(/\r?\n/);
    let fallbacks = this.fallbacks = [];
    let settings = this.settings = [];
    let errors = this.errors = [];
    let uris = this.uris = [];

    this.currSection = "CACHE";

    for (let i = 0; i < lines.length; i++) {
      let text = this.text = lines[i].trim();
      this.currentLine = i + 1;

      if (i === 0 && text !== "CACHE MANIFEST") {
        this._addError(1, "firstLineMustBeCacheManifest", 1);
      }

      // Ignore comments
      if (/^#/.test(text) || !text.length) {
        continue;
      }

      if (text == "CACHE MANIFEST") {
        if (this.currentLine != 1) {
          this._addError(this.currentLine, "cacheManifestOnlyFirstLine2",
                         this.currentLine);
        }
        continue;
      }

      if (this._maybeUpdateSectionName()) {
        continue;
      }

      switch (this.currSection) {
        case "CACHE":
        case "NETWORK":
          this.parseLine();
          break;
        case "FALLBACK":
          this.parseFallbackLine();
          break;
        case "SETTINGS":
          this.parseSettingsLine();
          break;
      }
    }

    return {
      uris: uris,
      fallbacks: fallbacks,
      settings: settings,
      errors: errors
    };
  },

  parseLine: function OCIMP_parseLine() {
    let text = this.text;

    if (text.includes("*")) {
      if (this.currSection != "NETWORK" || text.length != 1) {
        this._addError(this.currentLine, "asteriskInWrongSection2",
                       this.currSection, this.currentLine);
        return;
      }
    }

    if (/\s/.test(text)) {
      this._addError(this.currentLine, "escapeSpaces1", this.currentLine);
      text = text.replace(/\s/g, "%20");
    }

    if (text[0] == "/") {
      if (text.substr(0, 4) == "/../") {
        this._addError(this.currentLine, "slashDotDotSlashBad", this.currentLine);
      } else {
        this.uris.push(this._wrapURI(this.origin + text.substring(1)));
      }
    } else if (text.substr(0, 2) == "./") {
      this.uris.push(this._wrapURI(this.origin + text.substring(2)));
    } else if (text.substr(0, 4) == "http") {
      this.uris.push(this._wrapURI(text));
    } else {
      let origin = this.origin;
      let path = text;

      while (path.substr(0, 3) == "../" && /^https?:\/\/.*?\/.*?\//.test(origin)) {
        let trimIdx = origin.substr(0, origin.length - 1).lastIndexOf("/") + 1;
        origin = origin.substr(0, trimIdx);
        path = path.substr(3);
      }

      if (path.substr(0, 3) == "../") {
        this._addError(this.currentLine, "tooManyDotDotSlashes", this.currentLine);
        return;
      }

      if (/^https?:\/\//.test(path)) {
        this.uris.push(this._wrapURI(path));
        return;
      }
      this.uris.push(this._wrapURI(origin + path));
    }
  },

  parseFallbackLine: function OCIMP_parseFallbackLine() {
    let split = this.text.split(/\s+/);
    let origURI = this.text;

    if (split.length != 2) {
      this._addError(this.currentLine, "fallbackUseSpaces", this.currentLine);
      return;
    }

    let [ namespace, fallback ] = split;

    if (namespace.includes("*")) {
      this._addError(this.currentLine, "fallbackAsterisk2", this.currentLine);
    }

    if (/\s/.test(namespace)) {
      this._addError(this.currentLine, "escapeSpaces1", this.currentLine);
      namespace = namespace.replace(/\s/g, "%20");
    }

    if (namespace.substr(0, 4) == "/../") {
      this._addError(this.currentLine, "slashDotDotSlashBad", this.currentLine);
    }

    if (namespace.substr(0, 2) == "./") {
      namespace = this.origin + namespace.substring(2);
    }

    if (namespace.substr(0, 4) != "http") {
      let origin = this.origin;
      let path = namespace;

      while (path.substr(0, 3) == "../" && /^https?:\/\/.*?\/.*?\//.test(origin)) {
        let trimIdx = origin.substr(0, origin.length - 1).lastIndexOf("/") + 1;
        origin = origin.substr(0, trimIdx);
        path = path.substr(3);
      }

      if (path.substr(0, 3) == "../") {
        this._addError(this.currentLine, "tooManyDotDotSlashes", this.currentLine);
      }

      if (/^https?:\/\//.test(path)) {
        namespace = path;
      } else {
        if (path[0] == "/") {
          path = path.substring(1);
        }
        namespace = origin + path;
      }
    }

    this.text = fallback;
    this.parseLine();

    this.fallbacks.push({
      line: this.currentLine,
      original: origURI,
      namespace: namespace,
      fallback: fallback
    });
  },

  parseSettingsLine: function OCIMP_parseSettingsLine() {
    let text = this.text;

    if (this.settings.length == 1 || !/prefer-online|fast/.test(text)) {
      this._addError(this.currentLine, "settingsBadValue", this.currentLine);
      return;
    }

    switch (text) {
      case "prefer-online":
        this.settings.push(this._wrapURI(text));
        break;
      case "fast":
        this.settings.push(this._wrapURI(text));
        break;
    }
  },

  _wrapURI: function OCIMP__wrapURI(uri) {
    return {
      section: this.currSection,
      line: this.currentLine,
      uri: uri,
      original: this.text
    };
  },

  _addError: function OCIMP__addError(line, l10nString, ...params) {
    let msg;

    if (params) {
      msg = l10n.formatStringFromName(l10nString, params, params.length);
    } else {
      msg = l10n.GetStringFromName(l10nString);
    }

    this.errors.push({
      line: line,
      msg: msg
    });
  },

  _maybeUpdateSectionName: function OCIMP__maybeUpdateSectionName() {
    let text = this.text;

    if (text == text.toUpperCase() && text.charAt(text.length - 1) == ":") {
      text = text.substr(0, text.length - 1);

      switch (text) {
        case "CACHE":
        case "NETWORK":
        case "FALLBACK":
        case "SETTINGS":
          this.currSection = text;
          return true;
        default:
          this._addError(this.currentLine,
                         "invalidSectionName", text, this.currentLine);
          return false;
      }
    }
  },
};

XPCOMUtils.defineLazyGetter(this, "l10n", () => Services.strings
  .createBundle("chrome://devtools/locale/appcacheutils.properties"));

XPCOMUtils.defineLazyGetter(this, "appcacheservice", function () {
  return Cc["@mozilla.org/network/application-cache-service;1"]
           .getService(Ci.nsIApplicationCacheService);

});

XPCOMUtils.defineLazyGetter(this, "_DOMParser", function () {
  return Cc["@mozilla.org/xmlextras/domparser;1"].createInstance(Ci.nsIDOMParser);
});
PK
!<2�����=chrome/devtools/modules/devtools/client/shared/DOMHelpers.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm", {});
const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
const nodeFilterConstants = require("devtools/shared/dom-node-filter-constants");

this.EXPORTED_SYMBOLS = ["DOMHelpers"];

/**
 * DOMHelpers
 * Makes DOM traversal easier. Goes through iframes.
 *
 * @constructor
 * @param nsIDOMWindow aWindow
 *        The content window, owning the document to traverse.
 */
this.DOMHelpers = function DOMHelpers(aWindow) {
  if (!aWindow) {
    throw new Error("window can't be null or undefined");
  }
  this.window = aWindow;
};

DOMHelpers.prototype = {
  getParentObject: function Helpers_getParentObject(node)
  {
    let parentNode = node ? node.parentNode : null;

    if (!parentNode) {
      // Documents have no parentNode; Attr, Document, DocumentFragment, Entity,
      // and Notation. top level windows have no parentNode
      if (node && node == this.window.Node.DOCUMENT_NODE) {
        // document type
        if (node.defaultView) {
          let embeddingFrame = node.defaultView.frameElement;
          if (embeddingFrame)
            return embeddingFrame.parentNode;
        }
      }
      // a Document object without a parentNode or window
      return null;  // top level has no parent
    }

    if (parentNode.nodeType == this.window.Node.DOCUMENT_NODE) {
      if (parentNode.defaultView) {
        return parentNode.defaultView.frameElement;
      }
      // parent is document element, but no window at defaultView.
      return null;
    }

    if (!parentNode.localName)
      return null;

    return parentNode;
  },

  getChildObject: function Helpers_getChildObject(node, index, previousSibling,
                                                showTextNodesWithWhitespace)
  {
    if (!node)
      return null;

    if (node.contentDocument) {
      // then the node is a frame
      if (index == 0) {
        return node.contentDocument.documentElement;  // the node's HTMLElement
      }
      return null;
    }

    if (node.getSVGDocument) {
      let svgDocument = node.getSVGDocument();
      if (svgDocument) {
        // then the node is a frame
        if (index == 0) {
          return svgDocument.documentElement;  // the node's SVGElement
        }
        return null;
      }
    }

    let child = null;
    if (previousSibling)  // then we are walking
      child = this.getNextSibling(previousSibling);
    else
      child = this.getFirstChild(node);

    if (showTextNodesWithWhitespace)
      return child;

    for (; child; child = this.getNextSibling(child)) {
      if (!this.isWhitespaceText(child))
        return child;
    }

    return null;  // we have no children worth showing.
  },

  getFirstChild: function Helpers_getFirstChild(node)
  {
    let SHOW_ALL = nodeFilterConstants.SHOW_ALL;
    this.treeWalker = node.ownerDocument.createTreeWalker(node,
      SHOW_ALL, null);
    return this.treeWalker.firstChild();
  },

  getNextSibling: function Helpers_getNextSibling(node)
  {
    let next = this.treeWalker.nextSibling();

    if (!next)
      delete this.treeWalker;

    return next;
  },

  isWhitespaceText: function Helpers_isWhitespaceText(node)
  {
    return node.nodeType == this.window.Node.TEXT_NODE &&
                            !/[^\s]/.exec(node.nodeValue);
  },

  destroy: function Helpers_destroy()
  {
    delete this.window;
    delete this.treeWalker;
  },

  /**
   * A simple way to be notified (once) when a window becomes
   * interactive (DOMContentLoaded).
   *
   * It is based on the chromeEventHandler. This is useful when
   * chrome iframes are loaded in content docshells (in Firefox
   * tabs for example).
   */
  onceDOMReady: function Helpers_onLocationChange(callback, targetURL) {
    let window = this.window;
    let docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsIWebNavigation)
                         .QueryInterface(Ci.nsIDocShell);
    let onReady = function (event) {
      if (event.target == window.document) {
        docShell.chromeEventHandler.removeEventListener("DOMContentLoaded", onReady);
        // If in `callback` the URL of the window is changed and a listener to DOMContentLoaded
        // is attached, the event we just received will be also be caught by the new listener.
        // We want to avoid that so we execute the callback in the next queue.
        Services.tm.dispatchToMainThread(callback);
      }
    };
    if ((window.document.readyState == "complete" ||
         window.document.readyState == "interactive") &&
         window.location.href == targetURL) {
      Services.tm.dispatchToMainThread(callback);
    } else {
      docShell.chromeEventHandler.addEventListener("DOMContentLoaded", onReady);
    }
  }
};
PK
!<����$�$<chrome/devtools/modules/devtools/client/shared/SplitView.jsm/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
const {KeyCodes} = require("devtools/client/shared/keycodes");

this.EXPORTED_SYMBOLS = ["SplitView"];

/* this must be kept in sync with CSS (ie. splitview.css) */
const LANDSCAPE_MEDIA_QUERY = "(min-width: 701px)";

var bindings = new WeakMap();

/**
 * SplitView constructor
 *
 * Initialize the split view UI on an existing DOM element.
 *
 * A split view contains items, each of those having one summary and one details
 * elements.
 * It is adaptive as it behaves similarly to a richlistbox when there the aspect
 * ratio is narrow or as a pair listbox-box otherwise.
 *
 * @param DOMElement aRoot
 * @see appendItem
 */
this.SplitView = function SplitView(aRoot)
{
  this._root = aRoot;
  this._controller = aRoot.querySelector(".splitview-controller");
  this._nav = aRoot.querySelector(".splitview-nav");
  this._side = aRoot.querySelector(".splitview-side-details");
  this._activeSummary = null;

  this._mql = aRoot.ownerDocument.defaultView.matchMedia(LANDSCAPE_MEDIA_QUERY);

  // items list focus and search-on-type handling
  this._nav.addEventListener("keydown", (aEvent) => {
    function getFocusedItemWithin(nav) {
      let node = nav.ownerDocument.activeElement;
      while (node && node.parentNode != nav) {
        node = node.parentNode;
      }
      return node;
    }

    // do not steal focus from inside iframes or textboxes
    if (aEvent.target.ownerDocument != this._nav.ownerDocument ||
        aEvent.target.tagName == "input" ||
        aEvent.target.tagName == "textbox" ||
        aEvent.target.tagName == "textarea" ||
        aEvent.target.classList.contains("textbox")) {
      return false;
    }

    // handle keyboard navigation within the items list
    let newFocusOrdinal;
    if (aEvent.keyCode == KeyCodes.DOM_VK_PAGE_UP ||
        aEvent.keyCode == KeyCodes.DOM_VK_HOME) {
      newFocusOrdinal = 0;
    } else if (aEvent.keyCode == KeyCodes.DOM_VK_PAGE_DOWN ||
               aEvent.keyCode == KeyCodes.DOM_VK_END) {
      newFocusOrdinal = this._nav.childNodes.length - 1;
    } else if (aEvent.keyCode == KeyCodes.DOM_VK_UP) {
      newFocusOrdinal = getFocusedItemWithin(this._nav).getAttribute("data-ordinal");
      newFocusOrdinal--;
    } else if (aEvent.keyCode == KeyCodes.DOM_VK_DOWN) {
      newFocusOrdinal = getFocusedItemWithin(this._nav).getAttribute("data-ordinal");
      newFocusOrdinal++;
    }
    if (newFocusOrdinal !== undefined) {
      aEvent.stopPropagation();
      let el = this.getSummaryElementByOrdinal(newFocusOrdinal);
      if (el) {
        el.focus();
      }
      return false;
    }
  });
};

SplitView.prototype = {
  /**
    * Retrieve whether the UI currently has a landscape orientation.
    *
    * @return boolean
    */
  get isLandscape()
  {
    return this._mql.matches;
  },

  /**
    * Retrieve the root element.
    *
    * @return DOMElement
    */
  get rootElement()
  {
    return this._root;
  },

  /**
    * Retrieve the active item's summary element or null if there is none.
    *
    * @return DOMElement
    */
  get activeSummary()
  {
    return this._activeSummary;
  },

  /**
    * Set the active item's summary element.
    *
    * @param DOMElement aSummary
    */
  set activeSummary(aSummary)
  {
    if (aSummary == this._activeSummary) {
      return;
    }

    if (this._activeSummary) {
      let binding = bindings.get(this._activeSummary);

      if (binding.onHide) {
        binding.onHide(this._activeSummary, binding._details, binding.data);
      }

      this._activeSummary.classList.remove("splitview-active");
      binding._details.classList.remove("splitview-active");
    }

    if (!aSummary) {
      return;
    }

    let binding = bindings.get(aSummary);
    aSummary.classList.add("splitview-active");
    binding._details.classList.add("splitview-active");

    this._activeSummary = aSummary;

    if (binding.onShow) {
      binding.onShow(aSummary, binding._details, binding.data);
    }
  },

  /**
    * Retrieve the active item's details element or null if there is none.
    * @return DOMElement
    */
  get activeDetails()
  {
    let summary = this.activeSummary;
    return summary ? bindings.get(summary)._details : null;
  },

  /**
   * Retrieve the summary element for a given ordinal.
   *
   * @param number aOrdinal
   * @return DOMElement
   *         Summary element with given ordinal or null if not found.
   * @see appendItem
   */
  getSummaryElementByOrdinal: function SEC_getSummaryElementByOrdinal(aOrdinal)
  {
    return this._nav.querySelector("* > li[data-ordinal='" + aOrdinal + "']");
  },

  /**
   * Append an item to the split view.
   *
   * @param DOMElement aSummary
   *        The summary element for the item.
   * @param DOMElement aDetails
   *        The details element for the item.
   * @param object aOptions
   *     Optional object that defines custom behavior and data for the item.
   *     All properties are optional :
   *     - function(DOMElement summary, DOMElement details, object data) onCreate
   *         Called when the item has been added.
   *     - function(summary, details, data) onShow
   *         Called when the item is shown/active.
   *     - function(summary, details, data) onHide
   *         Called when the item is hidden/inactive.
   *     - function(summary, details, data) onDestroy
   *         Called when the item has been removed.
   *     - object data
   *         Object to pass to the callbacks above.
   *     - number ordinal
   *         Items with a lower ordinal are displayed before those with a
   *         higher ordinal.
   */
  appendItem: function ASV_appendItem(aSummary, aDetails, aOptions)
  {
    let binding = aOptions || {};

    binding._summary = aSummary;
    binding._details = aDetails;
    bindings.set(aSummary, binding);

    this._nav.appendChild(aSummary);

    aSummary.addEventListener("click", (aEvent) => {
      aEvent.stopPropagation();
      this.activeSummary = aSummary;
    });

    this._side.appendChild(aDetails);

    if (binding.onCreate) {
      binding.onCreate(aSummary, aDetails, binding.data);
    }
  },

  /**
   * Append an item to the split view according to two template elements
   * (one for the item's summary and the other for the item's details).
   *
   * @param string aName
   *        Name of the template elements to instantiate.
   *        Requires two (hidden) DOM elements with id "splitview-tpl-summary-"
   *        and "splitview-tpl-details-" suffixed with aName.
   * @param object aOptions
   *        Optional object that defines custom behavior and data for the item.
   *        See appendItem for full description.
   * @return object{summary:,details:}
   *         Object with the new DOM elements created for summary and details.
   * @see appendItem
   */
  appendTemplatedItem: function ASV_appendTemplatedItem(aName, aOptions)
  {
    aOptions = aOptions || {};
    let summary = this._root.querySelector("#splitview-tpl-summary-" + aName);
    let details = this._root.querySelector("#splitview-tpl-details-" + aName);

    summary = summary.cloneNode(true);
    summary.id = "";
    if (aOptions.ordinal !== undefined) { // can be zero
      summary.style.MozBoxOrdinalGroup = aOptions.ordinal;
      summary.setAttribute("data-ordinal", aOptions.ordinal);
    }
    details = details.cloneNode(true);
    details.id = "";

    this.appendItem(summary, details, aOptions);
    return {summary: summary, details: details};
  },

  /**
    * Remove an item from the split view.
    *
    * @param DOMElement aSummary
    *        Summary element of the item to remove.
    */
  removeItem: function ASV_removeItem(aSummary)
  {
    if (aSummary == this._activeSummary) {
      this.activeSummary = null;
    }

    let binding = bindings.get(aSummary);
    aSummary.remove();
    binding._details.remove();

    if (binding.onDestroy) {
      binding.onDestroy(aSummary, binding._details, binding.data);
    }
  },

  /**
   * Remove all items from the split view.
   */
  removeAll: function ASV_removeAll()
  {
    while (this._nav.hasChildNodes()) {
      this.removeItem(this._nav.firstChild);
    }
  },

  /**
   * Set the item's CSS class name.
   * This sets the class on both the summary and details elements, retaining
   * any SplitView-specific classes.
   *
   * @param DOMElement aSummary
   *        Summary element of the item to set.
   * @param string aClassName
   *        One or more space-separated CSS classes.
   */
  setItemClassName: function ASV_setItemClassName(aSummary, aClassName)
  {
    let binding = bindings.get(aSummary);
    let viewSpecific;

    viewSpecific = aSummary.className.match(/(splitview\-[\w-]+)/g);
    viewSpecific = viewSpecific ? viewSpecific.join(" ") : "";
    aSummary.className = viewSpecific + " " + aClassName;

    viewSpecific = binding._details.className.match(/(splitview\-[\w-]+)/g);
    viewSpecific = viewSpecific ? viewSpecific.join(" ") : "";
    binding._details.className = viewSpecific + " " + aClassName;
  },
};
PK
!<8W^�qCqCDchrome/devtools/modules/devtools/client/shared/autocomplete-popup.js/* vim: set ft=javascript ts=2 et sw=2 tw=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 HTML_NS = "http://www.w3.org/1999/xhtml";
const Services = require("Services");
const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
const EventEmitter = require("devtools/shared/event-emitter");
const {PrefObserver} = require("devtools/client/shared/prefs");

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 <LI> element.
 *        - position {String} The position for the tooltip ("top" or "bottom").
 *        - theme {String} String related to the theme of the popup
 *        - 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.
 */
function AutocompletePopup(toolboxDoc, options = {}) {
  EventEmitter.decorate(this);

  this._document = toolboxDoc;

  this.autoSelect = options.autoSelect || false;
  this.position = options.position || "bottom";
  let theme = options.theme || "dark";

  this.onSelectCallback = options.onSelect;
  this.onClickCallback = options.onClick;

  // If theme is auto, use the devtools.theme pref
  if (theme === "auto") {
    theme = Services.prefs.getCharPref("devtools.theme");
    this.autoThemeEnabled = true;
    // Setup theme change listener.
    this._handleThemeChange = this._handleThemeChange.bind(this);
    this._prefObserver = new PrefObserver("devtools.");
    this._prefObserver.on("devtools.theme", this._handleThemeChange);
    this._currentTheme = theme;
  }

  // Create HTMLTooltip instance
  this._tooltip = new HTMLTooltip(this._document);
  this._tooltip.panel.classList.add(
    "devtools-autocomplete-popup",
    "devtools-monospace",
    theme + "-theme");
  // Stop this appearing as an alert to accessibility.
  this._tooltip.panel.setAttribute("role", "presentation");

  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 (options.listId) {
    this._list.setAttribute("id", options.listId);
  }
  this._list.className = "devtools-autocomplete-listbox " + theme + "-theme";

  this._tooltip.setContent(this._list);

  this.onClick = this.onClick.bind(this);
  this._list.addEventListener("click", this.onClick);

  // Array of raw autocomplete items
  this.items = [];
  // Map of autocompleteItem to HTMLElement
  this.elements = new WeakMap();

  this.selectedIndex = -1;
}

AutocompletePopup.prototype = {
  _document: null,
  _tooltip: null,
  _list: null,

  onSelect: function (e) {
    if (this.onSelectCallback) {
      this.onSelectCallback(e);
    }
  },

  onClick: function (e) {
    let item = e.target.closest(".autocomplete-item");
    if (item && typeof item.dataset.index !== "undefined") {
      this.selectedIndex = parseInt(item.dataset.index, 10);
    }

    this.emit("popup-click");
    if (this.onClickCallback) {
      this.onClickCallback(e);
    }
  },

  /**
   * Open the autocomplete popup panel.
   *
   * @param {nsIDOMNode} anchor
   *        Optional node to anchor the panel to.
   * @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.
   */
  openPopup: function (anchor, xOffset = 0, yOffset = 0, index) {
    this.__maxLabelLength = -1;
    this._updateSize();

    // Retrieve the anchor's document active element to add accessibility metadata.
    this._activeElement = anchor.ownerDocument.activeElement;

    this._tooltip.show(anchor, {
      x: xOffset,
      y: yOffset,
      position: this.position,
    });

    this._tooltip.once("shown", () => {
      if (this.autoSelect) {
        this.selectItemAtIndex(index);
      }

      this.emit("popup-opened");
    });
  },

  /**
   * Select item at the provided index.
   *
   * @param {Number} index
   *        The position of the item to select.
   */
  selectItemAtIndex: function (index) {
    if (typeof index !== "number") {
      // If no index was provided, select the item closest to the input.
      let isAboveInput = this.position === "top";
      index = isAboveInput ? this.itemCount - 1 : 0;
    }
    this.selectedIndex = index;
  },

  /**
   * Hide the autocomplete popup panel.
   */
  hidePopup: function () {
    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: function () {
    if (this.isOpen) {
      this.hidePopup();
    }

    this._list.removeEventListener("click", this.onClick);

    if (this.autoThemeEnabled) {
      this._prefObserver.off("devtools.theme", this._handleThemeChange);
      this._prefObserver.destroy();
    }

    this._list.remove();
    this._listClone.remove();
    this._tooltip.destroy();
    this._document = null;
    this._list = null;
    this._tooltip = 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: function (index) {
    return this.items[index];
  },

  /**
   * Get the autocomplete items array.
   *
   * @return {Array} The array of autocomplete items.
   */
  getItems: function () {
    // 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} index
   *        The position of the item to select.
   */
  setItems: function (items, index) {
    this.clearItems();
    items.forEach(this.appendItem, this);

    if (this.isOpen && this.autoSelect) {
      this.selectItemAtIndex(index);
    }
  },

  __maxLabelLength: -1,

  get _maxLabelLength() {
    if (this.__maxLabelLength !== -1) {
      return this.__maxLabelLength;
    }

    let max = 0;
    for (let {label, count} of this.items) {
      if (count) {
        label += count + "";
      }
      max = Math.max(label.length, max);
    }

    this.__maxLabelLength = max;
    return this.__maxLabelLength;
  },

  /**
   * Update the panel size to fit the content.
   */
  _updateSize: function () {
    if (!this._tooltip) {
      return;
    }

    this._list.style.width = (this._maxLabelLength + 3) + "ch";
    let selectedItem = this.selectedItem;
    if (selectedItem) {
      this._scrollElementIntoViewIfNeeded(this.elements.get(selectedItem));
    }
  },

  _scrollElementIntoViewIfNeeded: function (element) {
    let quads = element.getBoxQuads({relativeTo: this._tooltip.panel});
    if (!quads || !quads[0]) {
      return;
    }

    let {top, height} = quads[0].bounds;
    let containerHeight = this._tooltip.panel.getBoundingClientRect().height;
    if (top < 0) {
      // Element is above container.
      element.scrollIntoView(true);
    } else if ((top + height) > containerHeight) {
      // Element is beloew container.
      element.scrollIntoView(false);
    }
  },

  /**
   * Clear all the items from the autocomplete list.
   */
  clearItems: function () {
    // Reset the selectedIndex to -1 before clearing the list
    this.selectedIndex = -1;
    this._list.innerHTML = "";
    this.__maxLabelLength = -1;
    this.items = [];
    this.elements = new WeakMap();
  },

  /**
   * Getter for the index of the selected item.
   *
   * @type {Number}
   */
  get selectedIndex() {
    return this._selectedIndex;
  },

  /**
   * Setter for the selected index.
   *
   * @param {Number} index
   *        The number (index) of the item you want to select in the list.
   */
  set selectedIndex(index) {
    let previousSelected = this._list.querySelector(".autocomplete-selected");
    if (previousSelected) {
      previousSelected.classList.remove("autocomplete-selected");
    }

    let item = this.items[index];
    if (this.isOpen && item) {
      let element = this.elements.get(item);

      element.classList.add("autocomplete-selected");
      this._scrollElementIntoViewIfNeeded(element);
      this._setActiveDescendant(element.id);
    } else {
      this._clearActiveDescendant();
    }
    this._selectedIndex = index;

    if (this.isOpen && item && this.onSelectCallback) {
      // Call the user-defined select callback if defined.
      this.onSelectCallback();
    }
  },

  /**
   * 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) {
    let index = this.items.indexOf(item);
    if (index !== -1 && this.isOpen) {
      this.selectedIndex = 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: function (id) {
    if (!this._activeElement) {
      return;
    }

    // Make sure the list clone is in the same document as the anchor.
    let anchorDoc = this._activeElement.ownerDocument;
    if (!this._listClone.parentNode || this._listClone.ownerDocument !== anchorDoc) {
      anchorDoc.documentElement.appendChild(this._listClone);
    }

    // Update the clone content to match the current list content.
    const clone = this._list.cloneNode(true);
    clone.className = "devtools-autocomplete-list-aria-clone";
    this._listClone.replaceWith(clone);

    // We also need to update the reference.
    this._listClone = clone;

    this._activeElement.setAttribute("aria-activedescendant", id);
  },

  /**
   * Clear the aria-activedescendant attribute on the current active element.
   */
  _clearActiveDescendant: function () {
    if (!this._activeElement) {
      return;
    }

    this._activeElement.removeAttribute("aria-activedescendant");
  },

  /**
   * Append an item into the autocomplete list.
   *
   * @param {Object} item
   *        The item you want appended to the list.
   *        The item object can have the following properties:
   *        - label {String} Property which is used as the displayed value.
   *        - preLabel {String} [Optional] The String that will be displayed
   *                   before the label indicating that this is the already
   *                   present text in the input box, and label is the text
   *                   that will be auto completed. When this property is
   *                   present, |preLabel.length| starting characters will be
   *                   removed from label.
   *        - count {Number} [Optional] The number to represent the count of
   *                autocompleted label.
   */
  appendItem: function (item) {
    let listItem = this._document.createElementNS(HTML_NS, "li");
    // Items must have an id for accessibility.
    listItem.setAttribute("id", "autocomplete-item-" + itemIdCounter++);
    listItem.className = "autocomplete-item";
    listItem.setAttribute("data-index", this.items.length);
    if (this.direction) {
      listItem.setAttribute("dir", this.direction);
    }
    let label = this._document.createElementNS(HTML_NS, "span");
    label.textContent = item.label;
    label.className = "autocomplete-value";
    if (item.preLabel) {
      let 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.count && item.count > 1) {
      let countDesc = this._document.createElementNS(HTML_NS, "span");
      countDesc.textContent = item.count;
      countDesc.setAttribute("flex", "1");
      countDesc.className = "autocomplete-count";
      listItem.appendChild(countDesc);
    }

    this._list.appendChild(listItem);
    this.items.push(item);
    this.elements.set(item, listItem);
  },

  /**
   * Remove an item from the popup list.
   *
   * @param {Object} item
   *        The item you want removed.
   */
  removeItem: function (item) {
    if (!this.items.includes(item)) {
      return;
    }

    let itemIndex = this.items.indexOf(item);
    let selectedIndex = this.selectedIndex;

    // Remove autocomplete item.
    this.items.splice(itemIndex, 1);

    // Remove corresponding DOM element from the elements WeakMap and from the DOM.
    let elementToRemove = this.elements.get(item);
    this.elements.delete(elementToRemove);
    elementToRemove.remove();

    if (itemIndex <= selectedIndex) {
      // If the removed item index was before or equal to the selected index, shift the
      // selected index by 1.
      this.selectedIndex = Math.max(0, selectedIndex - 1);
    }
  },

  /**
   * 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) {
      let listHeight = this._tooltip.panel.clientHeight;
      let element = this.elements.get(this.items[0]);
      let 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: function () {
    if (this.selectedIndex < (this.items.length - 1)) {
      this.selectedIndex++;
    } else {
      this.selectedIndex = 0;
    }
    return this.selectedItem;
  },

  /**
   * Select the previous item in the list.
   *
   * @return {Object}
   *         The newly-selected item object.
   */
  selectPreviousItem: function () {
    if (this.selectedIndex > 0) {
      this.selectedIndex--;
    } else {
      this.selectedIndex = 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: function () {
    let nextPageIndex = this.selectedIndex + this._itemsPerPane + 1;
    this.selectedIndex = 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: function () {
    let prevPageIndex = this.selectedIndex - this._itemsPerPane - 1;
    this.selectedIndex = Math.max(prevPageIndex, 0);
    return this.selectedItem;
  },

  /**
   * Manages theme switching for the popup based on the devtools.theme pref.
   */
  _handleThemeChange: function () {
    const oldValue = this._currentTheme;
    const newValue = Services.prefs.getCharPref("devtools.theme");

    this._tooltip.panel.classList.toggle(oldValue + "-theme", false);
    this._tooltip.panel.classList.toggle(newValue + "-theme", true);
    this._list.classList.toggle(oldValue + "-theme", false);
    this._list.classList.toggle(newValue + "-theme", true);

    this._currentTheme = newValue;
  },

  /**
   * Used by tests.
   */
  get _panel() {
    return this._tooltip.panel;
  },

  /**
   * Used by tests.
   */
  get _window() {
    return this._document.defaultView;
  },
};

module.exports = AutocompletePopup;
PK
!<D�?!!@chrome/devtools/modules/devtools/client/shared/browser-loader.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 loaders = ChromeUtils.import("resource://devtools/shared/base-loader.js", {});
const { devtools } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
const { joinURI } = devtools.require("devtools/shared/path");
const { assert } = devtools.require("devtools/shared/DevToolsUtils");
const { AppConstants } = devtools.require("resource://gre/modules/AppConstants.jsm");

const BROWSER_BASED_DIRS = [
  "resource://devtools/client/inspector/boxmodel",
  "resource://devtools/client/inspector/changes",
  "resource://devtools/client/inspector/computed",
  "resource://devtools/client/inspector/events",
  "resource://devtools/client/inspector/flexbox",
  "resource://devtools/client/inspector/fonts",
  "resource://devtools/client/inspector/grids",
  "resource://devtools/client/inspector/layout",
  "resource://devtools/client/jsonview",
  "resource://devtools/client/netmonitor/src/utils",
  "resource://devtools/client/shared/source-map",
  "resource://devtools/client/shared/redux",
  "resource://devtools/client/shared/vendor",
];

const COMMON_LIBRARY_DIRS = [
  "resource://devtools/client/shared/vendor",
];

// Any directory that matches the following regular expression
// is also considered as browser based module directory.
// ('resource://devtools/client/.*/components/')
//
// An example:
// * `resource://devtools/client/inspector/components`
// * `resource://devtools/client/inspector/shared/components`
const browserBasedDirsRegExp =
  /^resource\:\/\/devtools\/client\/\S*\/components\//;

/*
 * Create a loader to be used in a browser environment. This evaluates
 * modules in their own environment, but sets window (the normal
 * global object) as the sandbox prototype, so when a variable is not
 * defined it checks `window` before throwing an error. This makes all
 * browser APIs available to modules by default, like a normal browser
 * environment, but modules are still evaluated in their own scope.
 *
 * Another very important feature of this loader is that it *only*
 * deals with modules loaded from under `baseURI`. Anything loaded
 * outside of that path will still be loaded from the devtools loader,
 * so all system modules are still shared and cached across instances.
 * An exception to this is anything under
 * `devtools/client/shared/{vendor/components}`, which is where shared libraries
 * and React components live that should be evaluated in a browser environment.
 *
 * @param string baseURI
 *        Base path to load modules from. If null or undefined, only
 *        the shared vendor/components modules are loaded with the browser
 *        loader.
 * @param Object window
 *        The window instance to evaluate modules within
 * @param Boolean useOnlyShared
 *        If true, ignores `baseURI` and only loads the shared
 *        BROWSER_BASED_DIRS via BrowserLoader.
 * @return Object
 *         An object with two properties:
 *         - loader: the Loader instance
 *         - require: a function to require modules with
 */
function BrowserLoader(options) {
  const browserLoaderBuilder = new BrowserLoaderBuilder(options);
  return {
    loader: browserLoaderBuilder.loader,
    require: browserLoaderBuilder.require
  };
}

/**
 * Private class used to build the Loader instance and require method returned
 * by BrowserLoader(baseURI, window).
 *
 * @param string baseURI
 *        Base path to load modules from.
 * @param Object window
 *        The window instance to evaluate modules within
 * @param Boolean useOnlyShared
 *        If true, ignores `baseURI` and only loads the shared
 *        BROWSER_BASED_DIRS via BrowserLoader.
 * @param Function commonLibRequire
 *        Require function that should be used to load common libraries, like React.
 *        Allows for sharing common modules between tools, instead of loading a new
 *        instance into each tool. For example, pass "toolbox.browserRequire" here.
 */
function BrowserLoaderBuilder({ baseURI, window, useOnlyShared, commonLibRequire }) {
  assert(!!baseURI !== !!useOnlyShared,
    "Cannot use both `baseURI` and `useOnlyShared`.");

  const loaderOptions = devtools.require("@loader/options");
  const dynamicPaths = {};

  if (AppConstants.DEBUG || AppConstants.DEBUG_JS_MODULES) {
    dynamicPaths["devtools/client/shared/vendor/react"] =
      "resource://devtools/client/shared/vendor/react-dev";
    dynamicPaths["devtools/client/shared/vendor/react-dom"] =
      "resource://devtools/client/shared/vendor/react-dom-dev";
    dynamicPaths["devtools/client/shared/vendor/react-dom-server"] =
      "resource://devtools/client/shared/vendor/react-dom-server-dev";
    dynamicPaths["devtools/client/shared/vendor/react-prop-types"] =
      "resource://devtools/client/shared/vendor/react-prop-types-dev";
    dynamicPaths["devtools/client/shared/vendor/react-dom-test-utils"] =
      "resource://devtools/client/shared/vendor/react-dom-test-utils-dev";
  }

  const opts = {
    sharedGlobal: true,
    sandboxPrototype: window,
    sandboxName: "DevTools (UI loader)",
    paths: Object.assign({}, dynamicPaths, loaderOptions.paths),
    invisibleToDebugger: loaderOptions.invisibleToDebugger,
    requireHook: (id, require) => {
      // If |id| requires special handling, simply defer to devtools
      // immediately.
      if (devtools.isLoaderPluginId(id)) {
        return devtools.require(id);
      }

      const uri = require.resolve(id);

      if (commonLibRequire && COMMON_LIBRARY_DIRS.some(dir => uri.startsWith(dir))) {
        return commonLibRequire(uri);
      }

      // Check if the URI matches one of hardcoded paths or a regexp.
      let isBrowserDir = BROWSER_BASED_DIRS.some(dir => uri.startsWith(dir)) ||
                         uri.match(browserBasedDirsRegExp) != null;

      if ((useOnlyShared || !uri.startsWith(baseURI)) && !isBrowserDir) {
        return devtools.require(uri);
      }

      return require(uri);
    },
    globals: {
      // Allow modules to use the window's console to ensure logs appear in a
      // tab toolbox, if one exists, instead of just the browser console.
      console: window.console,
      // Make sure `define` function exists.  This allows defining some modules
      // in AMD format while retaining CommonJS compatibility through this hook.
      // JSON Viewer needs modules in AMD format, as it currently uses RequireJS
      // from a content document and can't access our usual loaders.  So, any
      // modules shared with the JSON Viewer should include a define wrapper:
      //
      //   // Make this available to both AMD and CJS environments
      //   define(function(require, exports, module) {
      //     ... code ...
      //   });
      //
      // Bug 1248830 will work out a better plan here for our content module
      // loading needs, especially as we head towards devtools.html.
      define(factory) {
        factory(this.require, this.exports, this.module);
      },
      // Allow modules to use the DevToolsLoader lazy loading helpers.
      loader: {
        lazyGetter: devtools.lazyGetter,
        lazyImporter: devtools.lazyImporter,
        lazyServiceGetter: devtools.lazyServiceGetter,
        lazyRequireGetter: this.lazyRequireGetter.bind(this),
      },
    }
  };

  const mainModule = loaders.Module(baseURI, joinURI(baseURI, "main.js"));
  this.loader = loaders.Loader(opts);
  this.require = loaders.Require(this.loader, mainModule);
}

BrowserLoaderBuilder.prototype = {
  /**
   * Define a getter property on the given object that requires the given
   * module. This enables delaying importing modules until the module is
   * actually used.
   *
   * @param Object obj
   *    The object to define the property on.
   * @param String property
   *    The property name.
   * @param String module
   *    The module path.
   * @param Boolean destructure
   *    Pass true if the property name is a member of the module's exports.
   */
  lazyRequireGetter: function (obj, property, module, destructure) {
    devtools.lazyGetter(obj, property, () => {
      return destructure
          ? this.require(module)[property]
          : this.require(module || property);
    });
  }
};

this.BrowserLoader = BrowserLoader;

this.EXPORTED_SYMBOLS = ["BrowserLoader"];
PK
!<z��kkNchrome/devtools/modules/devtools/client/shared/components/AutoCompletePopup.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");

class AutocompletePopup 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);
  }

  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 }) {
    let list = autocompleteProvider(filter);
    let selectedIndex = list.length > 0 ? 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) {
    let { 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() {
    let { list } = this.state;

    return list.length > 0 && dom.div(
      { className: "devtools-autocomplete-popup devtools-monospace" },
      dom.ul(
        { className: "devtools-autocomplete-listbox" },
        list.map((item, i) => {
          let isSelected = this.state.selectedIndex == i;
          let 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 = AutocompletePopup;
PK
!<�&��Bchrome/devtools/modules/devtools/client/shared/components/Frame.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const { getSourceNames, parseURL, isScratchpadScheme, getSourceMappedFile } =
  require("devtools/client/shared/source-utils");
const { LocalizationHelper } = require("devtools/shared/l10n");

const l10n = new LocalizationHelper("devtools/client/locales/components.properties");
const webl10n = new LocalizationHelper("devtools/client/locales/webconsole.properties");

class Frame extends Component {
  static get propTypes() {
    return {
      // SavedFrame, or an object containing all the required properties.
      frame: PropTypes.shape({
        functionDisplayName: PropTypes.string,
        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.
      sourceMapService: PropTypes.object,
    };
  }

  static get defaultProps() {
    return {
      showFunctionName: false,
      showAnonymousFunctionName: false,
      showHost: false,
      showEmptyPathAsHost: false,
      showFullSourceUrl: false,
    };
  }

  constructor(props) {
    super(props);
    this._locationChanged = this._locationChanged.bind(this);
    this.getSourceForClick = this.getSourceForClick.bind(this);
  }

  componentWillMount() {
    if (this.props.sourceMapService) {
      const { source, line, column } = this.props.frame;
      this.props.sourceMapService.subscribe(source, line, column,
                                            this._locationChanged);
    }
  }

  componentWillUnmount() {
    if (this.props.sourceMapService) {
      const { source, line, column } = this.props.frame;
      this.props.sourceMapService.unsubscribe(source, line, column,
                                              this._locationChanged);
    }
  }

  _locationChanged(isSourceMapped, url, line, column) {
    let newState = {
      isSourceMapped,
    };
    if (isSourceMapped) {
      newState.frame = {
        source: url,
        line,
        column,
        functionDisplayName: this.props.frame.functionDisplayName,
      };
    }

    this.setState(newState);
  }

  /**
   * Utility method to convert the Frame object model to the
   * object model required by the onClick callback.
   * @param Frame frame
   * @returns {{url: *, line: *, column: *, functionDisplayName: *}}
   */
  getSourceForClick(frame) {
    const { source, line, column } = frame;
    return {
      url: source,
      line,
      column,
      functionDisplayName: this.props.frame.functionDisplayName,
    };
  }

  render() {
    let frame, isSourceMapped;
    let {
      onClick,
      showFunctionName,
      showAnonymousFunctionName,
      showHost,
      showEmptyPathAsHost,
      showFullSourceUrl
    } = this.props;

    if (this.state && this.state.isSourceMapped && this.state.frame) {
      frame = this.state.frame;
      isSourceMapped = this.state.isSourceMapped;
    } else {
      frame = this.props.frame;
    }

    // If the resource was loaded by browser-loader.js, `frame.source` looks like:
    // resource://devtools/shared/base-loader.js -> resource://devtools/path/to/file.js .
    // What's needed is only the last part after " -> ".
    let source = frame.source
      ? String(frame.source).split(" -> ").pop()
      : "";
    let line = frame.line != void 0 ? Number(frame.line) : null;
    let column = frame.column != void 0 ? Number(frame.column) : null;

    const { short, long, host } = getSourceNames(source);
    // 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)". However, we do want to link
    // to Scratchpad URIs.
    // Source mapped sources might not necessary linkable, but they
    // are still valid in the debugger.
    const isLinkable = !!(isScratchpadScheme(source) || parseURL(source))
      || isSourceMapped;
    const elements = [];
    const sourceElements = [];
    let sourceEl;
    let tooltip = long;

    // Exclude all falsy values, including `0`, as line numbers start with 1.
    if (line) {
      tooltip += `:${line}`;
      // Intentionally exclude 0
      if (column) {
        tooltip += `:${column}`;
      }
    }

    let attributes = {
      "data-url": long,
      className: "frame-link",
    };

    if (showFunctionName) {
      let functionDisplayName = frame.functionDisplayName;
      if (!functionDisplayName && showAnonymousFunctionName) {
        functionDisplayName = webl10n.getStr("stacktrace.anonymousFunction");
      }

      if (functionDisplayName) {
        elements.push(
          dom.span({
            key: "function-display-name",
            className: "frame-link-function-display-name",
          }, functionDisplayName),
          " "
        );
      }
    }

    let displaySource = showFullSourceUrl ? long : short;
    if (isSourceMapped) {
      displaySource = getSourceMappedFile(displaySource);
    } else if (showEmptyPathAsHost && (displaySource === "" || displaySource === "/")) {
      displaySource = host;
    }

    sourceElements.push(dom.span({
      key: "filename",
      className: "frame-link-filename",
    }, displaySource));

    // If we have a line number > 0.
    if (line) {
      let lineInfo = `:${line}`;
      // Add `data-line` attribute for testing
      attributes["data-line"] = line;

      // Intentionally exclude 0
      if (column) {
        lineInfo += `:${column}`;
        // Add `data-column` attribute for testing
        attributes["data-column"] = column;
      }

      sourceElements.push(dom.span({
        key: "line",
        className: "frame-link-line"
      }, lineInfo));
    }

    // 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.
    let sourceInnerEl = dom.span({
      key: "source-inner",
      className: "frame-link-source-inner",
      title: isLinkable ?
        l10n.getFormatStr("frame.viewsourceindebugger", tooltip) : tooltip,
    }, 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.
    if (isLinkable) {
      sourceEl = dom.a({
        onClick: e => {
          e.preventDefault();
          e.stopPropagation();
          onClick(this.getSourceForClick({...frame, source}));
        },
        href: source,
        className: "frame-link-source",
        draggable: false,
      }, sourceInnerEl);
    } else {
      sourceEl = dom.span({
        key: "source",
        className: "frame-link-source",
      }, sourceInnerEl);
    }
    elements.push(sourceEl);

    if (showHost && host) {
      elements.push(" ");
      elements.push(dom.span({
        key: "host",
        className: "frame-link-host",
      }, host));
    }

    return dom.span(attributes, ...elements);
  }
}

module.exports = Frame;
PK
!<�/���Fchrome/devtools/modules/devtools/client/shared/components/HSplitBox.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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("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 { assert } = require("devtools/shared/DevToolsUtils");

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;
PK
!<g��%��Mchrome/devtools/modules/devtools/client/shared/components/NotificationBox.css/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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;
}

.notificationbox .details {
  flex-grow: 1;
  display: flex;
  flex-direction: row;
  align-items: center;
}

.notificationbox .notification-button {
  text-align: right;
}

.notificationbox .messageText {
  flex-grow: 1;
}

.notificationbox .details:dir(rtl)
.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-bottom: 1px solid var(--theme-splitter-color);
}

.notificationbox .notification[data-type="info"] {
  color: -moz-DialogText;
  background-color: -moz-Dialog;
}

.notificationbox .notification[data-type="critical"] {
  color: white;
  background-image: linear-gradient(rgb(212,0,0), rgb(152,0,0));
}

.notificationbox .messageImage {
  display: inline-block;
  background-size: 16px;
  width: 16px;
  height: 16px;
  margin: 6px;
}

/* Default icons for notifications */

.notificationbox .messageImage[data-type="info"] {
  background-image: url("chrome://global/skin/icons/info.svg");
}

.notificationbox .messageImage[data-type="warning"] {
  background-image: url("chrome://global/skin/icons/warning-16.png");
}

.notificationbox .messageImage[data-type="critical"] {
  background-image: url("chrome://global/skin/icons/error-16.png");
}

/* Close button */

.notificationbox .messageCloseButton {
  width: 20px;
  height: 20px;
  margin: 4px;
  margin-inline-end: 8px;
  background-image: url("chrome://devtools/skin/images/close.svg");
  background-position: center;
  background-color: transparent;
  background-repeat: no-repeat;
  border-radius: 11px;
  filter: var(--theme-messageCloseButtonFilter);
}

.notificationbox .messageCloseButton:hover {
  background-color: gray;
  filter: invert(1);
}

.notificationbox .messageCloseButton:active {
  background-color: rgba(170, 170, 170, .4); /* --toolbar-tab-hover-active */
}
PK
!<�/�Q Q Lchrome/devtools/modules/devtools/client/shared/components/NotificationBox.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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 Immutable = require("devtools/client/shared/vendor/immutable");
const { LocalizationHelper } = require("devtools/shared/l10n");

const l10n = new LocalizationHelper("devtools/client/locales/components.properties");
const { div, span, button } = dom;

// Priority Levels
const PriorityLevels = {
  PRIORITY_INFO_LOW: 1,
  PRIORITY_INFO_MEDIUM: 2,
  PRIORITY_INFO_HIGH: 3,
  PRIORITY_WARNING_LOW: 4,
  PRIORITY_WARNING_MEDIUM: 5,
  PRIORITY_WARNING_HIGH: 6,
  PRIORITY_CRITICAL_LOW: 7,
  PRIORITY_CRITICAL_MEDIUM: 8,
  PRIORITY_CRITICAL_HIGH: 9,
  PRIORITY_CRITICAL_BLOCK: 10,
};

/**
 * 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
 */
class NotificationBox extends Component {
  static get propTypes() {
    return {
      // List of notifications appended into the box.
      notifications: PropTypes.arrayOf(PropTypes.shape({
        // label to appear on the notification.
        label: PropTypes.string.isRequired,

        // Value used to identify the notification
        value: PropTypes.string.isRequired,

        // URL of image to appear on the notification. If "" then an icon
        // appropriate for the priority level is used.
        image: PropTypes.string.isRequired,

        // Notification priority; see Priority Levels.
        priority: PropTypes.number.isRequired,

        // Array of button descriptions to appear on the notification.
        buttons: PropTypes.arrayOf(PropTypes.shape({
          // Function to be called when the button is activated.
          // This function is passed three 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.
          callback: PropTypes.func.isRequired,

          // The label to appear on the button.
          label: PropTypes.string.isRequired,

          // The accesskey attribute set on the <button> element.
          accesskey: PropTypes.string,
        })),

        // A function to call to notify you of interesting things that happen
        // with the notification box.
        eventCallback: PropTypes.func,
      })),

      // Message that should be shown when hovering over the close button
      closeButtonTooltip: PropTypes.string
    };
  }

  static get defaultProps() {
    return {
      closeButtonTooltip: l10n.getStr("notificationBox.closeTooltip")
    };
  }

  constructor(props) {
    super(props);

    this.state = {
      notifications: new Immutable.OrderedMap()
    };

    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) {
    // 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_CRITICAL_LOW) {
      type = "critical";
    } else if (priority <= PriorityLevels.PRIORITY_INFO_HIGH) {
      type = "info";
    }

    let notifications = this.state.notifications.set(value, {
      label: label,
      value: value,
      image: image,
      priority: priority,
      type: type,
      buttons: Array.isArray(buttons) ? buttons : [],
      eventCallback: eventCallback,
    });

    // High priorities must be on top.
    notifications = notifications.sortBy((val, key) => {
      return -val.priority;
    });

    this.setState({
      notifications: notifications
    });
  }

  /**
   * 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) {
    let 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 this.state.notifications.first();
  }

  /**
   * Close specified notification.
   */
  close(notification) {
    if (!notification) {
      return;
    }

    if (notification.eventCallback) {
      notification.eventCallback("removed");
    }

    this.setState({
      notifications: this.state.notifications.remove(notification.value)
    });
  }

  /**
   * Render a button. A notification can have a set of custom buttons.
   * These are used to execute custom callback.
   */
  renderButton(props, notification) {
    let onClick = event => {
      if (props.callback) {
        let result = props.callback(this, props, event.target);
        if (!result) {
          this.close(notification);
        }
        event.stopPropagation();
      }
    };

    return (
      button({
        key: props.label,
        className: "notification-button",
        accesskey: props.accesskey,
        onClick: 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: "details"},
            div({
              className: "messageImage",
              "data-type": notification.type}),
            span({className: "messageText"},
              notification.label
            ),
            notification.buttons.map(props =>
              this.renderButton(props, notification)
            )
          ),
          div({
            className: "messageCloseButton",
            title: this.props.closeButtonTooltip,
            onClick: this.close.bind(this, notification)}
          )
        )
      )
    );
  }

  /**
   * Render the top (highest priority) notification. Only one
   * notification is rendered at a time.
   */
  render() {
    let notification = this.state.notifications.first();
    let content = notification ?
      this.renderNotification(notification) :
      null;

    return div({className: "notificationbox"},
      content
    );
  }
}

module.exports.NotificationBox = NotificationBox;
module.exports.PriorityLevels = PriorityLevels;
PK
!<
q/�>>Fchrome/devtools/modules/devtools/client/shared/components/SearchBox.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 { Component, createFactory } = 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 KeyShortcuts = require("devtools/client/shared/key-shortcuts");
const AutocompletePopup = createFactory(require("devtools/client/shared/components/AutoCompletePopup"));

class SearchBox extends Component {
  static get propTypes() {
    return {
      delay: PropTypes.number,
      keyShortcut: PropTypes.string,
      onChange: PropTypes.func,
      onFocus: PropTypes.func,
      onBlur: PropTypes.func,
      onKeyDown: PropTypes.func,
      placeholder: PropTypes.string,
      type: PropTypes.string,
      autocompleteProvider: PropTypes.func,
    };
  }

  constructor(props) {
    super(props);

    this.state = {
      value: "",
      focused: false,
    };

    this.onChange = this.onChange.bind(this);
    this.onClearButtonClick = this.onClearButtonClick.bind(this);
    this.onFocus = this.onFocus.bind(this);
    this.onBlur = this.onBlur.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 => {
      event.preventDefault();
      this.refs.input.focus();
    });
  }

  componentWillUnmount() {
    if (this.shortcuts) {
      this.shortcuts.destroy();
    }

    // Clean up an existing timeout.
    if (this.searchTimeout) {
      clearTimeout(this.searchTimeout);
    }
  }

  onChange() {
    if (this.state.value !== this.refs.input.value) {
      this.setState({
        focused: true,
        value: this.refs.input.value,
      });
    }

    if (!this.props.delay) {
      this.props.onChange(this.state.value);
      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.refs.input.value = "";
    this.onChange();
  }

  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();
    }

    let { autocomplete } = this.refs;
    if (!autocomplete || autocomplete.state.list.length <= 0) {
      return;
    }

    switch (e.key) {
      case "ArrowDown":
        autocomplete.jumpBy(1);
        break;
      case "ArrowUp":
        autocomplete.jumpBy(-1);
        break;
      case "PageDown":
        autocomplete.jumpBy(5);
        break;
      case "PageUp":
        autocomplete.jumpBy(-5);
        break;
      case "Enter":
      case "Tab":
        e.preventDefault();
        autocomplete.select();
        break;
      case "Escape":
        e.preventDefault();
        this.onBlur();
        break;
      case "Home":
        autocomplete.jumpToTop();
        break;
      case "End":
        autocomplete.jumpToBottom();
        break;
    }
  }

  render() {
    let {
      type = "search",
      placeholder,
      autocompleteProvider,
    } = this.props;
    let { value } = this.state;
    let divClassList = ["devtools-searchbox", "has-clear-btn"];
    let inputClassList = [`devtools-${type}input`];
    let showAutocomplete = autocompleteProvider && this.state.focused && value !== "";

    if (value !== "") {
      inputClassList.push("filled");
    }
    return dom.div(
      { className: divClassList.join(" ") },
      dom.input({
        className: inputClassList.join(" "),
        onChange: this.onChange,
        onFocus: this.onFocus,
        onBlur: this.onBlur,
        onKeyDown: this.onKeyDown,
        placeholder,
        ref: "input",
        value,
      }),
      dom.button({
        className: "devtools-searchinput-clear",
        hidden: value == "",
        onClick: this.onClearButtonClick
      }),
      showAutocomplete && AutocompletePopup({
        autocompleteProvider,
        filter: value,
        ref: "autocomplete",
        onItemSelected: (itemValue) => {
          this.setState({ value: itemValue });
          this.onChange();
        }
      })
    );
  }
}

module.exports = SearchBox;
PK
!<BaD���Kchrome/devtools/modules/devtools/client/shared/components/SidebarToggle.css/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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: var(--theme-pane-collapse-image);
}

.sidebar-toggle.pane-collapsed::before,
.sidebar-toggle:dir(rtl)::before {
  background-image: var(--theme-pane-expand-image);
}

/* Rotate button icon 90deg if the toolbox container is
  in vertical mode (sidebar displayed under the main panel) */
@media (max-width: 700px) {
  .sidebar-toggle::before {
    transform: rotate(90deg);
  }

  /* Since RTL swaps the used images, we need to flip them
     the other way round */
  .sidebar-toggle:dir(rtl)::before {
    transform: rotate(-90deg);
  }
}
PK
!<g��GGJchrome/devtools/modules/devtools/client/shared/components/SidebarToggle.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 { 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");

// 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,
    };
  }

  constructor(props) {
    super(props);

    this.state = {
      collapsed: props.collapsed,
    };

    this.onClick = this.onClick.bind(this);
  }

  // Events

  onClick(event) {
    event.stopPropagation();
    this.props.onClick(event);
  }

  // Rendering

  render() {
    let title = this.state.collapsed ?
      this.props.expandPaneTitle :
      this.props.collapsePaneTitle;

    let classNames = ["devtools-button", "sidebar-toggle"];
    if (this.state.collapsed) {
      classNames.push("pane-collapsed");
    }

    return (
      button({
        className: classNames.join(" "),
        title: title,
        onClick: this.onClick
      })
    );
  }
}

module.exports = SidebarToggle;
PK
!<��S	S	Gchrome/devtools/modules/devtools/client/shared/components/StackTrace.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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 { LocalizationHelper } = require("devtools/shared/l10n");
const Frame = createFactory(require("./Frame"));

const l10n = new LocalizationHelper("devtools/client/locales/webconsole.properties");

class AsyncFrameClass extends Component {
  static get propTypes() {
    return {
      asyncCause: PropTypes.string.isRequired
    };
  }

  render() {
    let { 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,
      onViewSourceInScratchpad: PropTypes.func,
      // Service to enable the source map feature.
      sourceMapService: PropTypes.object,
    };
  }

  render() {
    let {
      stacktrace,
      onViewSourceInDebugger,
      onViewSourceInScratchpad,
      sourceMapService,
    } = this.props;

    let frames = [];
    stacktrace.forEach((s, i) => {
      if (s.asyncCause) {
        frames.push("\t", AsyncFrame({
          key: `${i}-asyncframe`,
          asyncCause: s.asyncCause
        }), "\n");
      }

      let source = s.filename.split(" -> ").pop();
      frames.push("\t", Frame({
        key: `${i}-frame`,
        frame: {
          functionDisplayName: s.functionName,
          source,
          line: s.lineNumber,
          column: s.columnNumber,
        },
        showFunctionName: true,
        showAnonymousFunctionName: true,
        showFullSourceUrl: true,
        onClick: (/^Scratchpad\/\d+$/.test(source))
          ? onViewSourceInScratchpad
          : onViewSourceInDebugger,
        sourceMapService,
      }), "\n");
    });

    return dom.div({ className: "stack-trace" }, frames);
  }
}

const AsyncFrame = createFactory(AsyncFrameClass);

module.exports = StackTrace;
PK
!<,��=b=bLchrome/devtools/modules/devtools/client/shared/components/VirtualizedTree.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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("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 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,

      // Handle when item is activated with a keyboard (using Space or Enter)
      onActivate: PropTypes.func,

      // 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,
    };
  }

  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._activateNode = oncePerAnimationFrame(this._activateNode).bind(this);

    this._autoExpand = this._autoExpand.bind(this);
    this._preventArrowKeyScrolling = this._preventArrowKeyScrolling.bind(this);
    this._updateHeight = this._updateHeight.bind(this);
    this._dfs = this._dfs.bind(this);
    this._dfsFromRoots = this._dfsFromRoots.bind(this);
    this._focus = this._focus.bind(this);
    this._onBlur = this._onBlur.bind(this);
    this._onKeyDown = this._onKeyDown.bind(this);
  }

  componentDidMount() {
    window.addEventListener("resize", this._updateHeight);
    this._autoExpand();
    this._updateHeight();
  }

  componentWillReceiveProps(nextProps) {
    this._autoExpand();
    this._updateHeight();
  }

  shouldComponentUpdate(nextProps, nextState) {
    let { scroll, height, seen, mouseDown } = this.state;

    return scroll !== nextState.scroll ||
           height !== nextState.height ||
           seen !== nextState.seen ||
           mouseDown === nextState.mouseDown;
  }

  componentWillUnmount() {
    window.removeEventListener("resize", this._updateHeight);
  }

  _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":
        e.preventDefault();
        e.stopPropagation();
        if (e.nativeEvent) {
          if (e.nativeEvent.preventDefault) {
            e.nativeEvent.preventDefault();
          }
          if (e.nativeEvent.stopPropagation) {
            e.nativeEvent.stopPropagation();
          }
        }
    }
  }

  /**
   * 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);
    }
  }

  /**
   * 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) {
    if (item !== undefined) {
      const itemStartPosition = index * this.props.itemHeight;
      const itemEndPosition = (index + 1) * this.props.itemHeight;

      // Note that if the height of the viewport (this.state.height) is less
      // than `this.props.itemHeight`, we could accidentally try and scroll both
      // up and down in a futile attempt to make both the item's start and end
      // positions visible. Instead, give priority to the start of the item by
      // checking its position first, and then using an "else if", rather than
      // a separate "if", for the end position.
      if (this.state.scroll > itemStartPosition) {
        this.refs.tree.scrollTo(0, itemStartPosition);
      } else if ((this.state.scroll + this.state.height) < itemEndPosition) {
        this.refs.tree.scrollTo(0, itemEndPosition - this.state.height);
      }
    }

    if (this.props.onFocus) {
      this.props.onFocus(item);
    }
  }

  /**
   * Sets the state to have no focused item.
   */
  _onBlur() {
    this._focus(0, undefined);
  }

  /**
   * 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
   */
  _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.isExpanded(this.props.focused)) {
          this._onExpand(this.props.focused);
        } else {
          this._focusNextNode();
        }
        break;

      case "Home":
        this._focusFirstNode();
        break;

      case "End":
        this._focusLastNode();
        break;

      case "Enter":
      case " ":
        this._activateNode();
        break;
    }
  }

  _activateNode() {
    if (this.props.onActivate) {
      this.props.onActivate(this.props.focused);
    }
  }

  _focusFirstNode() {
    const traversal = this._dfsFromRoots();
    this._focus(0, traversal[0].item);
  }

  _focusLastNode() {
    const traversal = this._dfsFromRoots();
    const lastIndex = traversal.length - 1;
    this._focus(lastIndex, traversal[lastIndex].item);
  }

  /**
   * 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);
  }

  /**
   * 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);
    }
  }

  /**
   * 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);
  }

  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, 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({
        key,
        index,
        first,
        last,
        item,
        depth,
        id: key,
        renderItem: this.props.renderItem,
        focused: focused === item,
        expanded: this.props.isExpanded(item),
        hasChildren: !!this.props.getChildren(item).length,
        onExpand: this._onExpand,
        onCollapse: this._onCollapse,
        onClick: () => this._focus(begin + i, item),
      }));
    }

    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);
        },
        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",
      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,
      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,
    };
  }

  render() {
    const arrow = ArrowExpander({
      item: this.props.item,
      expanded: this.props.expanded,
      visible: this.props.hasChildren,
      onExpand: this.props.onExpand,
      onCollapse: this.props.onCollapse,
    });

    let 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");
    }

    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",
        "aria-level": this.props.depth + 1,
        onClick: this.props.onClick,
        "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;
PK
!<�v��^^Nchrome/devtools/modules/devtools/client/shared/components/VisibilityHandler.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/vendor/react");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");

class VisibilityHandler extends Component {
  static get propTypes() {
    return {
      children: PropTypes.element.isRequired
    };
  }

  constructor(props) {
    super(props);

    this.onVisibilityChange = this.onVisibilityChange.bind(this);
  }

  componentDidMount() {
    window.addEventListener("visibilitychange", this.onVisibilityChange);
  }

  shouldComponentUpdate() {
    return document.visibilityState == "visible";
  }

  componentWillUnmount() {
    window.removeEventListener("visibilitychange", this.onVisibilityChange);
  }

  onVisibilityChange() {
    if (document.visibilityState == "visible") {
      this.forceUpdate();
    }
  }

  render() {
    return this.props.children;
  }
}

module.exports = VisibilityHandler;
PK
!<o�����Gchrome/devtools/modules/devtools/client/shared/components/reps/reps.css/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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-highlight-blue);
  --node-color: var(--theme-highlight-purple);
  --reference-color: var(--theme-highlight-blue);
  --comment-node-color: var(--theme-comment);
}

.theme-firebug {
  --number-color: #000088;
  --string-color: #FF0000;
  --null-color: #787878;
  --object-color: DarkGreen;
  --caption-color: #444444;
  --location-color: #555555;
  --source-link-color: blue;
  --node-color: rgb(0, 0, 136);
  --reference-color: rgb(102, 102, 255);
}

/******************************************************************************/

.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;
}

.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-string a, .objectBox-string a:visited {
  color: currentColor;
  text-decoration: none;
  font-style: italic;
}

.objectBox-string a:hover {
  text-decoration: underline;
}

.objectBox-function,
.objectBox-stackTrace,
.objectBox-profile {
  color: var(--object-color);
}

.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-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);
}

.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 button */

.open-inspector svg {
  fill: var(--comment-node-color);
  height: 16px;
  width: 16px;
  margin-left: .25em;
  cursor: pointer;
  vertical-align: middle;
}

.objectBox-node:hover .open-inspector svg,
.objectBox-textNode:hover .open-inspector svg,
.open-inspector svg:hover {
  fill: var(--theme-highlight-blue);
}

/******************************************************************************/
/* Jump to definition button */

.jump-definition svg {
  stroke: var(--comment-node-color);
  height: 16px;
  width: 16px;
  margin-left: .25em;
  cursor: pointer;
  vertical-align: middle;
}

.jump-definition svg:hover {
  stroke: var(--theme-highlight-blue);
}

/******************************************************************************/
/* "more…" ellipsis */
.more-ellipsis {
  color: var(--comment-node-color);
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 {
  overflow: auto;
}

.tree.inline {
  display: inline-block;
}

.tree.nowrap {
  white-space: nowrap;
}

.tree.noselect {
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  -o-user-select: none;
  user-select: none;
}

.tree button {
  display: block;
}

.tree .tree-node {
  display: flex;
}

.tree-indent {
  display: inline-block;
  width: 12px;
  margin-inline-start: 5px;
  border-inline-start: 1px solid #A2D1FF;
  flex-shrink: 0;
}

/* Align with expandables siblings (where we have the arrow) */
.tree-node[data-expandable="false"] .tree-indent:last-of-type {
  margin-inline-end: 15px;
}

/* For non expandable root nodes, we don't have .tree-indent elements, so we declare
   the margin on the start of the node */
.tree-node[data-expandable="false"][aria-level="0"] {
  padding-inline-start: 15px
}

.tree .tree-node[data-expandable="true"] {
  cursor: default;
}

.tree .tree-node:not(.focused):hover {
  background-color: #F0F9FE;
}

.tree .tree-node.focused {
  color: white;
  background-color: var(--theme-selection-background, #0a84ff);
}

.tree-node.focused .arrow svg {
  fill: currentColor;
}

.arrow svg {
  fill: var(--theme-splitter-color, #9B9B9B);
  transition: transform 0.125s ease;
  width: 10px;
  margin-inline-end: 5px;
  transform: rotate(-90deg);
}

html[dir="rtl"] .arrow svg,
.arrow svg:dir(rtl),
.arrow svg:-moz-locale-dir(rtl) {
  transform: rotate(90deg);
}

.arrow.expanded.expanded svg {
  transform: rotate(0deg);
}
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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);
}

.object-inspector .object-delimiter {
  color: var(--theme-comment);
}
PK
!<Cm��e�e�Fchrome/devtools/modules/devtools/client/shared/components/reps/reps.js(function webpackUniversalModuleDefinition(root, factory) {
	if(typeof exports === 'object' && typeof module === 'object')
		module.exports = factory(require("devtools/client/shared/vendor/react-dom-factories"), require("devtools/client/shared/vendor/lodash"), require("devtools/client/shared/vendor/react-prop-types"), require("devtools/client/shared/vendor/react"));
	else if(typeof define === 'function' && define.amd)
		define(["devtools/client/shared/vendor/react-dom-factories", "devtools/client/shared/vendor/lodash", "devtools/client/shared/vendor/react-prop-types", "devtools/client/shared/vendor/react"], factory);
	else {
		var a = typeof exports === 'object' ? factory(require("devtools/client/shared/vendor/react-dom-factories"), require("devtools/client/shared/vendor/lodash"), require("devtools/client/shared/vendor/react-prop-types"), require("devtools/client/shared/vendor/react")) : factory(root["devtools/client/shared/vendor/react-dom-factories"], root["devtools/client/shared/vendor/lodash"], root["devtools/client/shared/vendor/react-prop-types"], root["devtools/client/shared/vendor/react"]);
		for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
	}
})(this, function(__WEBPACK_EXTERNAL_MODULE_1__, __WEBPACK_EXTERNAL_MODULE_56__, __WEBPACK_EXTERNAL_MODULE_2__, __WEBPACK_EXTERNAL_MODULE_8__) {
return /******/ (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, {
/******/ 				configurable: false,
/******/ 				enumerable: true,
/******/ 				get: getter
/******/ 			});
/******/ 		}
/******/ 	};
/******/
/******/ 	// 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 = "/assets/build";
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(__webpack_require__.s = 20);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Dependencies
const validProtocols = /^(http|https|ftp|data|resource|chrome):/i;
const tokenSplitRegex = /(\s|\'|\"|\\)+/;
const ELLIPSIS = "\u2026";
const dom = __webpack_require__(1);
const { span } = dom;

/**
 * Returns true if the given object is a grip (see RDP protocol)
 */
function isGrip(object) {
  return object && object.actor;
}

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: "\\\\"
};

// Regexp that matches any character we might possibly want to escape.
// 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 escapeRegexp = new RegExp("[" +
// Quote and 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" + "]", "g");

/**
 * Escape a string so that the result is viewable and valid JS.
 * Control characters, other invisibles, invalid characters,
 * backslash, and double quotes are escaped.  The resulting string is
 * surrounded by double quotes.
 *
 * @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) {
  return "\"" + str.replace(escapeRegexp, (match, offset) => {
    let 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;
      }
      let 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);
  }) + "\"";
}

/**
 * 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;
  }

  let 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
  let 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) {
  let params = [];

  // In case the text is empty just return the empty parameters
  if (text == "") {
    return params;
  }

  let searchParams = new URLSearchParams(text);
  let entries = [...searchParams.entries()];
  return entries.map(entry => {
    return {
      name: entry[0],
      value: entry[1]
    };
  });
}

function getFileName(url) {
  let 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}([^\/]*)(.*?)([^\/]*?)($|\?.*)/;
  let 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 [];
  }

  // Promise resolved value Grip
  if (grip.promiseState && grip.promiseState.value) {
    return [grip.promiseState.value];
  }

  // 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) {
    let keys = Object.keys(grip.preview.properties);
    let 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);

    let 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) {
  if (typeof grip !== "string") {
    return false;
  }

  let tokens = grip.split(tokenSplitRegex);
  return tokens.some(isURL);
}

/**
 * Determines whether a string token is a valid URL.
 *
 * @param string token
 *        The token.
 * @return boolean
 *         Whenther the token is a URL.
 */
function isURL(token) {
  try {
    if (!validProtocols.test(token)) {
      return false;
    }
    new URL(token);
    return true;
  } catch (e) {
    return false;
  }
}

const ellipsisElement = span({
  key: "more",
  className: "more-ellipsis",
  title: `more${ELLIPSIS}`
}, ELLIPSIS);

module.exports = {
  isGrip,
  isURL,
  cropString,
  containsURL,
  rawCropString,
  sanitizeString,
  escapeString,
  wrapRender,
  cropMultipleLines,
  parseURLParams,
  parseURLEncodedText,
  getFileName,
  getURLDisplayString,
  maybeEscapePropertyName,
  getGripPreviewItems,
  getGripType,
  tokenSplitRegex,
  ellipsisElement,
  ELLIPSIS
};

/***/ }),
/* 1 */
/***/ (function(module, exports) {

module.exports = __WEBPACK_EXTERNAL_MODULE_1__;

/***/ }),
/* 2 */
/***/ (function(module, exports) {

module.exports = __WEBPACK_EXTERNAL_MODULE_2__;

/***/ }),
/* 3 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

module.exports = {
  MODE: {
    TINY: Symbol("TINY"),
    SHORT: Symbol("SHORT"),
    LONG: Symbol("LONG")
  }
};

/***/ }),
/* 4 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

__webpack_require__(21);

// Load all existing rep templates
const Undefined = __webpack_require__(22);
const Null = __webpack_require__(23);
const StringRep = __webpack_require__(7);
const Number = __webpack_require__(24);
const ArrayRep = __webpack_require__(5);
const Obj = __webpack_require__(25);
const SymbolRep = __webpack_require__(26);
const InfinityRep = __webpack_require__(27);
const NaNRep = __webpack_require__(28);
const Accessor = __webpack_require__(29);

// DOM types (grips)
const Attribute = __webpack_require__(30);
const DateTime = __webpack_require__(31);
const Document = __webpack_require__(32);
const Event = __webpack_require__(33);
const Func = __webpack_require__(34);
const PromiseRep = __webpack_require__(38);
const RegExp = __webpack_require__(39);
const StyleSheet = __webpack_require__(40);
const CommentNode = __webpack_require__(41);
const ElementNode = __webpack_require__(42);
const TextNode = __webpack_require__(43);
const ErrorRep = __webpack_require__(44);
const Window = __webpack_require__(45);
const ObjectWithText = __webpack_require__(46);
const ObjectWithURL = __webpack_require__(47);
const GripArray = __webpack_require__(13);
const GripMap = __webpack_require__(15);
const GripMapEntry = __webpack_require__(16);
const Grip = __webpack_require__(9);

// List of all registered template.
// XXX there should be a way for extensions to register a new
// or modify an existing rep.
let reps = [RegExp, StyleSheet, Event, DateTime, CommentNode, ElementNode, TextNode, Attribute, Func, PromiseRep, ArrayRep, Document, Window, ObjectWithText, ObjectWithURL, ErrorRep, GripArray, GripMap, GripMapEntry, Grip, Undefined, Null, StringRep, Number, SymbolRep, InfinityRep, NaNRep, Accessor];

/**
 * Generic rep that is using 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 is as 'object'
 * property.
 */
const Rep = function (props) {
  let {
    object,
    defaultRep
  } = props;
  let rep = getRep(object, defaultRep, props.noGrip);
  return rep(props);
};

// 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 defaultObject {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.
 */
function getRep(object, defaultRep = Obj, noGrip = false) {
  for (let i = 0; i < reps.length; i++) {
    let rep = reps[i];
    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: {
    Accessor,
    ArrayRep,
    Attribute,
    CommentNode,
    DateTime,
    Document,
    ElementNode,
    ErrorRep,
    Event,
    Func,
    Grip,
    GripArray,
    GripMap,
    GripMapEntry,
    InfinityRep,
    NaNRep,
    Null,
    Number,
    Obj,
    ObjectWithText,
    ObjectWithURL,
    PromiseRep,
    RegExp,
    Rep,
    StringRep,
    StyleSheet,
    SymbolRep,
    TextNode,
    Undefined,
    Window
  },
  // Exporting for tests
  getRep
};

/***/ }),
/* 5 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Dependencies
const dom = __webpack_require__(1);
const PropTypes = __webpack_require__(2);
const {
  wrapRender
} = __webpack_require__(0);
const { MODE } = __webpack_require__(3);
const { span } = dom;

const ModePropType = PropTypes.oneOf(
// @TODO Change this to Object.values once it's supported in Node's version of V8
Object.keys(MODE).map(key => MODE[key]));

/**
 * 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
};

function ArrayRep(props) {
  let {
    object,
    mode = MODE.SHORT
  } = props;

  let items;
  let brackets;
  let needSpace = function (space) {
    return space ? { left: "[ ", right: " ]" } : { left: "[", right: "]" };
  };

  if (mode === MODE.TINY) {
    let isEmpty = object.length === 0;
    if (isEmpty) {
      items = [];
    } else {
      items = [span({
        className: "more-ellipsis",
        title: "more…"
      }, "…")];
    }
    brackets = needSpace(false);
  } else {
    items = arrayIterator(props, object, maxLengthMap.get(mode));
    brackets = needSpace(items.length > 0);
  }

  return span({
    className: "objectBox objectBox-array" }, span({
    className: "arrayLeftBracket"
  }, brackets.left), ...items, span({
    className: "arrayRightBracket"
  }, brackets.right), span({
    className: "arrayProperties",
    role: "group" }));
}

function arrayIterator(props, array, max) {
  let items = [];

  for (let i = 0; i < array.length && i < max; i++) {
    let config = {
      mode: MODE.TINY,
      delim: i == array.length - 1 ? "" : ", "
    };
    let item;

    try {
      item = ItemRep(Object.assign({}, props, config, {
        object: array[i]
      }));
    } catch (exc) {
      item = ItemRep(Object.assign({}, props, config, {
        object: exc
      }));
    }
    items.push(item);
  }

  if (array.length > max) {
    items.push(span({
      className: "more-ellipsis",
      title: "more…"
    }, "…"));
  }

  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 } = __webpack_require__(4);

  let {
    object,
    delim,
    mode
  } = props;
  return span({}, Rep(Object.assign({}, props, {
    object: object,
    mode: mode
  })), delim);
}

function getLength(object) {
  return object.length;
}

function supportsObject(object) {
  return 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,
  ModePropType
};

/***/ }),
/* 6 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Dependencies
const PropTypes = __webpack_require__(2);
const {
  maybeEscapePropertyName,
  wrapRender
} = __webpack_require__(0);
const { MODE } = __webpack_require__(3);

const dom = __webpack_require__(1);
const { span } = dom;

/**
 * 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 = {
  // Property name.
  name: PropTypes.oneOfType([PropTypes.string, PropTypes.object]).isRequired,
  // Equal character rendered between property name and value.
  equal: PropTypes.string,
  // @TODO Change this to Object.values once it's supported in Node's version of V8
  mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
  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
};

/**
 * 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 = __webpack_require__(9);
  const { Rep } = __webpack_require__(4);

  let {
    name,
    mode,
    equal,
    suppressQuotes
  } = props;

  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": "nodeName" }, name);
  } else {
    key = Rep(Object.assign({}, props, {
      className: "nodeName",
      object: name,
      mode: mode || MODE.TINY,
      defaultRep: Grip
    }));
  }

  return [key, span({
    "className": "objectEqual"
  }, equal), Rep(Object.assign({}, props))];
}

// Exports from this module
module.exports = wrapRender(PropRep);

/***/ }),
/* 7 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Dependencies
const PropTypes = __webpack_require__(2);

const {
  containsURL,
  isURL,
  escapeString,
  getGripType,
  rawCropString,
  sanitizeString,
  wrapRender,
  isGrip,
  tokenSplitRegex,
  ELLIPSIS
} = __webpack_require__(0);

const dom = __webpack_require__(1);
const { a, span } = dom;

/**
 * Renders a string. String value is enclosed within quotes.
 */
StringRep.propTypes = {
  useQuotes: PropTypes.bool,
  escapeWhitespace: PropTypes.bool,
  style: PropTypes.object,
  cropLimit: PropTypes.number.isRequired,
  member: PropTypes.string,
  object: PropTypes.object.isRequired,
  openLink: PropTypes.func,
  className: PropTypes.string,
  omitLinkHref: PropTypes.bool
};

function StringRep(props) {
  let {
    className,
    style,
    cropLimit,
    object,
    useQuotes = true,
    escapeWhitespace = true,
    member,
    openLink,
    omitLinkHref = true
  } = props;

  let text = object;

  const isLong = isLongString(object);
  const shouldCrop = (!member || !member.open) && cropLimit && text.length > cropLimit;

  if (isLong) {
    text = maybeCropLongString({
      shouldCrop,
      cropLimit
    }, text);
  }

  text = formatText({
    useQuotes,
    escapeWhitespace
  }, text);

  const config = getElementConfig({
    className,
    style,
    actor: object.actor
  });

  if (!isLong) {
    if (containsURL(text)) {
      return span(config, ...getLinkifiedElements(text, shouldCrop && cropLimit, omitLinkHref, openLink));
    }

    // Cropping of longString has been handled before formatting.
    text = maybeCropString({
      isLong,
      shouldCrop,
      cropLimit
    }, text);
  }

  return span(config, text);
}

function maybeCropLongString(opts, text) {
  const {
    shouldCrop,
    cropLimit
  } = opts;

  const {
    fullText,
    initial,
    length
  } = text;

  text = shouldCrop ? initial.substring(0, cropLimit) : fullText || initial;

  if (text.length < length) {
    text += ELLIPSIS;
  }

  return text;
}

function formatText(opts, text) {
  let {
    useQuotes,
    escapeWhitespace
  } = opts;

  return useQuotes ? escapeString(text, escapeWhitespace) : sanitizeString(text);
}

function getElementConfig(opts) {
  const {
    className,
    style,
    actor
  } = opts;

  const config = {};

  if (actor) {
    config["data-link-actor-id"] = actor;
  }

  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 {String} text: The actual string to linkify.
 * @param {Integer | null} cropLimit
 * @param {Boolean} omitLinkHref: Do not create an href attribute if true.
 * @param {Function} openLink: Function handling the link opening.
 * @returns {Array<String|ReactElement>}
 */
function getLinkifiedElements(text, cropLimit, omitLinkHref, openLink) {
  const halfLimit = Math.ceil((cropLimit - ELLIPSIS.length) / 2);
  const startCropIndex = cropLimit ? halfLimit : null;
  const endCropIndex = cropLimit ? text.length - halfLimit : null;

  // As we walk through the tokens of the source string, we make sure to preserve
  // the original whitespace that separated the tokens.
  let currentIndex = 0;
  const items = [];
  for (let token of text.split(tokenSplitRegex)) {
    if (isURL(token)) {
      // Let's grab all the non-url strings before the link.
      const tokenStart = text.indexOf(token, currentIndex);
      let nonUrlText = text.slice(currentIndex, tokenStart);
      nonUrlText = getCroppedString(nonUrlText, currentIndex, startCropIndex, endCropIndex);
      if (nonUrlText) {
        items.push(nonUrlText);
      }

      // Update the index to match the beginning of the token.
      currentIndex = tokenStart;

      let linkText = getCroppedString(token, currentIndex, startCropIndex, endCropIndex);
      if (linkText) {
        items.push(a({
          className: "url",
          title: token,
          href: omitLinkHref === true ? null : token,
          draggable: false,
          onClick: openLink ? e => {
            e.preventDefault();
            openLink(token);
          } : null
        }, linkText));
      }

      currentIndex = tokenStart + token.length;
    }
  }

  // Clean up any non-URL text at the end of the source string,
  // i.e. not handled in the loop.
  if (currentIndex !== text.length) {
    let nonUrlText = text.slice(currentIndex, text.length);
    if (currentIndex < endCropIndex) {
      const cutIndex = endCropIndex - currentIndex;
      nonUrlText = nonUrlText.substring(cutIndex);
    }
    items.push(nonUrlText);
  }

  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) {
  return object && object.type === "longString";
}

function supportsObject(object, noGrip = false) {
  if (noGrip === false && isGrip(object)) {
    return isLongString(object);
  }

  return getGripType(object, noGrip) == "string";
}

// Exports from this module

module.exports = {
  rep: wrapRender(StringRep),
  supportsObject
};

/***/ }),
/* 8 */
/***/ (function(module, exports) {

module.exports = __WEBPACK_EXTERNAL_MODULE_8__;

/***/ }),
/* 9 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// ReactJS
const PropTypes = __webpack_require__(2);

// Dependencies
const {
  isGrip,
  wrapRender
} = __webpack_require__(0);
const PropRep = __webpack_require__(6);
const { MODE } = __webpack_require__(3);

const dom = __webpack_require__(1);
const { span } = dom;

/**
 * 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,
  // @TODO Change this to Object.values once it's supported in Node's version of V8
  mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
  isInterestingProp: PropTypes.func,
  title: PropTypes.string,
  onDOMNodeMouseOver: PropTypes.func,
  onDOMNodeMouseOut: PropTypes.func,
  onInspectIconClick: PropTypes.func,
  noGrip: PropTypes.bool
};

const DEFAULT_TITLE = "Object";

function GripRep(props) {
  let {
    mode = MODE.SHORT,
    object
  } = props;

  const config = {
    "data-link-actor-id": object.actor,
    className: "objectBox objectBox-object"
  };

  if (mode === MODE.TINY) {
    let 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",
        title: "more…"
      }, "…") : null, span({
        className: "objectRightBrace"
      }, "}"));
    }

    return span(config, ...tinyModeItems);
  }

  let propsArray = safePropIterator(props, object, maxLengthMap.get(mode));

  return span(config, getTitleElement(props, object), span({
    className: "objectLeftBrace"
  }, " { "), ...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;
  }

  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 } = __webpack_require__(4);

    return [Rep({
      object: object.preview.wrappedValue,
      mode: props.mode || MODE.TINY,
      defaultRep: Grip
    })];
  }

  // Property filter. Show only interesting properties to the user.
  let isInterestingProp = props.isInterestingProp || ((type, value) => {
    return type == "boolean" || type == "number" || type == "string" && value.length != 0;
  });

  let properties = object.preview ? object.preview.ownProperties : {};
  let propertiesLength = getPropertiesLength(object);

  if (object.preview && object.preview.safeGetterValues) {
    properties = Object.assign({}, 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.
  const suppressQuotes = object.class === "Proxy";
  let propsArray = getProps(props, properties, indexes, suppressQuotes);

  // 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 => {
      return PropRep(Object.assign({}, props, {
        mode: MODE.TINY,
        name: symbolItem,
        object: symbolItem.descriptor.value,
        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",
      title: "more…"
    }, "…"));
  }

  return unfoldProps(propsArray);
}

function unfoldProps(items) {
  return items.reduce((res, item, index) => {
    if (Array.isArray(item)) {
      res = res.concat(item);
    } else {
      res.push(item);
    }

    // Interleave commas between elements
    if (index !== items.length - 1) {
      res.push(", ");
    }
    return res;
  }, []);
}

/**
 * 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 => {
    let name = propertiesKeys[i];
    let value = getPropValue(properties[name]);

    return PropRep(Object.assign({}, 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) {
  let indexes = [];

  try {
    let i = 0;
    for (let name in properties) {
      if (indexes.length >= max) {
        return indexes;
      }

      // Type is specified in grip's "class" field and for primitive
      // values use typeof.
      let 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") {
    let 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 (noGrip === true || !isGrip(object)) {
    return 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.
let Grip = {
  rep: wrapRender(GripRep),
  supportsObject,
  maxLengthMap
};

// Exports from this module
module.exports = Grip;

/***/ }),
/* 10 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var _svgInlineReact = __webpack_require__(11);

var _svgInlineReact2 = _interopRequireDefault(_svgInlineReact);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 React = __webpack_require__(8);
const PropTypes = __webpack_require__(2);


const svg = {
  "open-inspector": __webpack_require__(36),
  "jump-definition": __webpack_require__(37)
};

Svg.propTypes = {
  className: PropTypes.string
};

function Svg(name, props) {
  if (!svg[name]) {
    throw new Error("Unknown SVG: " + name);
  }
  let className = name;
  if (props && props.className) {
    className = `${name} ${props.className}`;
  }
  if (name === "subSettings") {
    className = "";
  }
  props = Object.assign({}, props, { className, src: svg[name] });
  return React.createElement(_svgInlineReact2.default, props);
}

module.exports = Svg;

/***/ }),
/* 11 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
    value: true
});

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 _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; }; }();

var _react = __webpack_require__(8);

var _react2 = _interopRequireDefault(_react);

var _propTypes = __webpack_require__(2);

var _util = __webpack_require__(35);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

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(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; }

var process = process || { env: {} };

var InlineSVG = function (_React$Component) {
    _inherits(InlineSVG, _React$Component);

    function InlineSVG() {
        _classCallCheck(this, InlineSVG);

        return _possibleConstructorReturn(this, (InlineSVG.__proto__ || Object.getPrototypeOf(InlineSVG)).apply(this, arguments));
    }

    _createClass(InlineSVG, [{
        key: 'componentWillReceiveProps',
        value: function componentWillReceiveProps(_ref) {
            var children = _ref.children;

            if ("production" !== process.env.NODE_ENV && children != null) {
                console.info('<InlineSVG />: `children` prop will be ignored.');
            }
        }
    }, {
        key: 'render',
        value: function render() {
            var Element = void 0,
                __html = void 0,
                svgProps = void 0;

            var _props = this.props,
                element = _props.element,
                raw = _props.raw,
                src = _props.src,
                otherProps = _objectWithoutProperties(_props, ['element', 'raw', 'src']);

            if (raw === true) {
                Element = 'svg';
                svgProps = (0, _util.extractSVGProps)(src);
                __html = (0, _util.getSVGFromSource)(src).innerHTML;
            }
            __html = __html || src;
            Element = Element || element;
            svgProps = svgProps || {};

            return _react2.default.createElement(Element, _extends({}, svgProps, otherProps, { src: null, children: null,
                dangerouslySetInnerHTML: { __html: __html } }));
        }
    }]);

    return InlineSVG;
}(_react2.default.Component);

exports.default = InlineSVG;


InlineSVG.defaultProps = {
    element: 'i',
    raw: false,
    src: ''
};

InlineSVG.propTypes = {
    src: _propTypes.string.isRequired,
    element: _propTypes.string,
    raw: _propTypes.bool
};

/***/ }),
/* 12 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

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
};

/***/ }),
/* 13 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Dependencies
const PropTypes = __webpack_require__(2);

const { lengthBubble } = __webpack_require__(14);
const {
  getGripType,
  isGrip,
  wrapRender,
  ellipsisElement
} = __webpack_require__(0);
const { MODE } = __webpack_require__(3);

const dom = __webpack_require__(1);
const { span } = dom;
const { ModePropType } = __webpack_require__(5);

/**
 * 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,
  // @TODO Change this to Object.values once it's supported in Node's version of V8
  mode: ModePropType,
  provider: PropTypes.object,
  onDOMNodeMouseOver: PropTypes.func,
  onDOMNodeMouseOut: PropTypes.func,
  onInspectIconClick: PropTypes.func
};

function GripArray(props) {
  let {
    object,
    mode = MODE.SHORT
  } = props;

  let items;
  let brackets;
  let needSpace = function (space) {
    return space ? { left: "[ ", right: " ]" } : { left: "[", right: "]" };
  };

  const config = {
    "data-link-actor-id": object.actor,
    className: "objectBox objectBox-array"
  };

  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));
  }

  let max = maxLengthMap.get(mode);
  items = arrayIterator(props, object, max);
  brackets = needSpace(items.length > 0);

  return span({
    "data-link-actor-id": object.actor,
    className: "objectBox objectBox-array" }, title, span({
    className: "arrayLeftBracket"
  }, brackets.left), ...interleaveCommas(items), span({
    className: "arrayRightBracket"
  }, brackets.right), span({
    className: "arrayProperties",
    role: "group" }));
}

function interleaveCommas(items) {
  return items.reduce((res, item, index) => {
    if (index !== items.length - 1) {
      return res.concat(item, ", ");
    }
    return res.concat(item);
  }, []);
}

function getLength(grip) {
  if (!grip.preview) {
    return 0;
  }

  return grip.preview.length || grip.preview.childNodesLength || 0;
}

function getTitle(props, object) {
  let objectLength = getLength(object);
  let isEmpty = objectLength === 0;

  let title = props.title || object.class || "Array";

  const length = lengthBubble({
    object,
    mode: props.mode,
    maxLengthMap,
    getLength
  });

  if (props.mode === MODE.TINY) {
    if (isEmpty) {
      return object.class === "Array" ? "" : span({
        className: "objectTitle" }, title, " ");
    }

    let trailingSpace;
    if (object.class === "Array") {
      title = "";
      trailingSpace = " ";
    }

    return span({
      className: "objectTitle" }, title, length, trailingSpace);
  }

  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) {
  let { Rep } = __webpack_require__(4);

  let items = [];
  const gripLength = getLength(grip);

  if (!gripLength) {
    return items;
  }

  const previewItems = getPreviewItems(grip);
  let 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(Object.assign({}, 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/devtools-html/reps/issues/141
  return `<${number} empty slot${number > 1 ? "s" : ""}>`;
}

function supportsObject(grip, noGrip = false) {
  if (noGrip === true || !isGrip(grip)) {
    return 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
};

/***/ }),
/* 14 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


const PropTypes = __webpack_require__(2);

const { wrapRender } = __webpack_require__(0);
const { MODE } = __webpack_require__(3);
const { ModePropType } = __webpack_require__(5);

const dom = __webpack_require__(1);
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)
};

/***/ }),
/* 15 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Dependencies

const { lengthBubble } = __webpack_require__(14);
const PropTypes = __webpack_require__(2);
const {
  isGrip,
  wrapRender,
  ellipsisElement
} = __webpack_require__(0);
const PropRep = __webpack_require__(6);
const { MODE } = __webpack_require__(3);
const { ModePropType } = __webpack_require__(5);

const dom = __webpack_require__(1);
const { span } = dom;

/**
 * Renders an map. A map is represented by a list of its
 * entries enclosed in curly brackets.
 */
GripMap.propTypes = {
  object: PropTypes.object,
  // @TODO Change this to Object.values once it's supported in Node's version of V8
  mode: ModePropType,
  isInterestingEntry: PropTypes.func,
  onDOMNodeMouseOver: PropTypes.func,
  onDOMNodeMouseOut: PropTypes.func,
  onInspectIconClick: PropTypes.func,
  title: PropTypes.string
};

function GripMap(props) {
  let {
    mode,
    object
  } = props;

  const config = {
    "data-link-actor-id": object.actor,
    className: "objectBox objectBox-object"
  };

  const title = getTitle(props, object);
  const isEmpty = getLength(object) === 0;

  if (isEmpty || mode === MODE.TINY) {
    return span(config, title);
  }

  const propsArray = safeEntriesIterator(props, object, maxLengthMap.get(mode));

  return span(config, title, span({
    className: "objectLeftBrace"
  }, " { "), ...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 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.
  let isInterestingEntry = props.isInterestingEntry || ((type, value) => {
    return type == "boolean" || type == "number" || type == "string" && value.length != 0;
  });

  let 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);
    }));
  }

  let entries = getEntries(props, mapEntries, indexes);
  if (entries.length < getLength(object)) {
    // There are some undisplayed entries. Then display "…".
    entries.push(ellipsisElement);
  }

  return unfoldEntries(entries);
}

function unfoldEntries(items) {
  return items.reduce((res, item, index) => {
    if (Array.isArray(item)) {
      res = res.concat(item);
    } else {
      res.push(item);
    }

    // Interleave commas between elements
    if (index !== items.length - 1) {
      res.push(", ");
    }
    return res;
  }, []);
}

/**
 * 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) {
  let {
    onDOMNodeMouseOver,
    onDOMNodeMouseOut,
    onInspectIconClick
  } = props;

  // Make indexes ordered by ascending.
  indexes.sort(function (a, b) {
    return a - b;
  });

  return indexes.map((index, i) => {
    let [key, entryValue] = entries[index];
    let value = entryValue.value !== undefined ? entryValue.value : entryValue;

    return PropRep({
      name: key,
      equal: " \u2192 ",
      object: 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) {
      let value = entry && entry.value !== undefined ? entry.value : entry;
      // Type is specified in grip's "class" field and for primitive
      // values use typeof.
      let 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, noGrip = false) {
  if (noGrip === true || !isGrip(grip)) {
    return false;
  }
  return grip.preview && 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
};

/***/ }),
/* 16 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Dependencies
const PropTypes = __webpack_require__(2);
// Shortcuts
const dom = __webpack_require__(1);
const { span } = dom;
const {
  wrapRender
} = __webpack_require__(0);
const PropRep = __webpack_require__(6);
const { MODE } = __webpack_require__(3);
/**
 * Renders an map entry. A map entry is represented by its key, a column and its value.
 */
GripMapEntry.propTypes = {
  object: PropTypes.object,
  // @TODO Change this to Object.values once it's supported in Node's version of V8
  mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
  onDOMNodeMouseOver: PropTypes.func,
  onDOMNodeMouseOut: PropTypes.func,
  onInspectIconClick: PropTypes.func
};

function GripMapEntry(props) {
  const {
    object
  } = props;

  const {
    key,
    value
  } = object.preview;

  return span({
    className: "objectBox objectBox-map-entry"
  }, ...PropRep(Object.assign({}, 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 === "mapEntry" && grip.preview;
}

function createGripMapEntry(key, value) {
  return {
    type: "mapEntry",
    preview: {
      key,
      value
    }
  };
}

// Exports from this module
module.exports = {
  rep: wrapRender(GripMapEntry),
  createGripMapEntry,
  supportsObject
};

/***/ }),
/* 17 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 = __webpack_require__(18);
const loadProperties = __webpack_require__(55);
const node = __webpack_require__(19);

module.exports = {
  client,
  loadProperties,
  node
};

/***/ }),
/* 18 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


async function enumIndexedProperties(objectClient, start, end) {
  try {
    const { iterator } = await objectClient.enumProperties({ ignoreNonIndexedProperties: true });
    const response = await iteratorSlice(iterator, start, end);
    return response;
  } catch (e) {
    console.error("Error in enumIndexedProperties", e);
    return {};
  }
} /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

async function enumNonIndexedProperties(objectClient, start, end) {
  try {
    const { iterator } = await objectClient.enumProperties({ ignoreIndexedProperties: true });
    const response = await iteratorSlice(iterator, start, end);
    return response;
  } catch (e) {
    console.error("Error in enumNonIndexedProperties", e);
    return {};
  }
}

async function enumEntries(objectClient, start, end) {
  try {
    const { iterator } = await objectClient.enumEntries();
    const response = await iteratorSlice(iterator, start, end);
    return response;
  } catch (e) {
    console.error("Error in enumEntries", e);
    return {};
  }
}

async function enumSymbols(objectClient, start, end) {
  try {
    const { iterator } = await objectClient.enumSymbols();
    const response = await iteratorSlice(iterator, start, end);
    return response;
  } catch (e) {
    console.error("Error in enumSymbols", e);
    return {};
  }
}

async function getPrototype(objectClient) {
  if (typeof objectClient.getPrototype !== "function") {
    console.error("objectClient.getPrototype is not a function");
    return Promise.resolve({});
  }
  return objectClient.getPrototype();
}

function iteratorSlice(iterator, start, end) {
  start = start || 0;
  const count = end ? end - start + 1 : iterator.count;
  return iterator.slice(start, count);
}

module.exports = {
  enumEntries,
  enumIndexedProperties,
  enumNonIndexedProperties,
  enumSymbols,
  getPrototype
};

/***/ }),
/* 19 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 { get, has } = __webpack_require__(56);
const { maybeEscapePropertyName } = __webpack_require__(0);
const ArrayRep = __webpack_require__(5);
const GripArrayRep = __webpack_require__(13);
const GripMap = __webpack_require__(15);
const GripMapEntryRep = __webpack_require__(16);

const MAX_NUMERICAL_PROPERTIES = 100;

const NODE_TYPES = {
  BUCKET: Symbol("[n…n]"),
  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("__proto__")
};

let WINDOW_PROPERTIES = {};

if (typeof window === "object") {
  WINDOW_PROPERTIES = Object.getOwnPropertyNames(window);
}

const SAFE_PATH_PREFIX = "##-";

function getType(item) {
  return item.type;
}

function getValue(item) {
  if (has(item, "contents.value")) {
    return get(item, "contents.value");
  }

  if (has(item, "contents.getterValue")) {
    return get(item, "contents.getterValue", undefined);
  }

  if (nodeHasAccessors(item)) {
    return item.contents;
  }

  return undefined;
}

function nodeIsBucket(item) {
  return getType(item) === NODE_TYPES.BUCKET;
}

function nodeIsEntries(item) {
  return getType(item) === NODE_TYPES.ENTRIES;
}

function nodeIsMapEntry(item) {
  return GripMapEntryRep.supportsObject(getValue(item));
}

function nodeHasChildren(item) {
  return Array.isArray(item.contents);
}

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
// debugger 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 !nodeHasChildren(item) && !nodeHasProperties(item) && !nodeIsEntries(item) && !nodeIsMapEntry(item) && !nodeHasAccessors(item) && !nodeIsBucket(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 nodeHasAccessors(item) {
  return !!getNodeGetter(item) || !!getNodeSetter(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;
  }

  return value.class === "Map" || value.class === "Set" || value.class === "WeakMap" || value.class === "WeakSet";
}

function nodeHasAllEntriesInPreview(item) {
  const { preview } = getValue(item) || {};
  if (!preview) {
    return false;
  }

  const {
    entries,
    items,
    length,
    size
  } = preview;

  if (!entries && !items) {
    return false;
  }

  return entries ? entries.length === size : items.length === length;
}

function nodeNeedsNumericalBuckets(item) {
  return nodeSupportsNumericalBucketing(item) && getNumericalPropertiesCount(item) > MAX_NUMERICAL_PROPERTIES;
}

function makeNodesForPromiseProperties(item) {
  const { promiseState: { reason, value, state } } = getValue(item);

  const properties = [];

  if (state) {
    properties.push(createNode(item, "<state>", `${item.path}/${SAFE_PATH_PREFIX}state`, { value: state }, NODE_TYPES.PROMISE_STATE));
  }

  if (reason) {
    properties.push(createNode(item, "<reason>", `${item.path}/${SAFE_PATH_PREFIX}reason`, { value: reason }, NODE_TYPES.PROMISE_REASON));
  }

  if (value) {
    properties.push(createNode(item, "<value>", `${item.path}/${SAFE_PATH_PREFIX}value`, { value: value }, NODE_TYPES.PROMISE_VALUE));
  }

  return properties;
}

function makeNodesForProxyProperties(item) {
  const {
    proxyHandler,
    proxyTarget
  } = getValue(item);

  return [createNode(item, "<target>", `${item.path}/${SAFE_PATH_PREFIX}target`, { value: proxyTarget }, NODE_TYPES.PROXY_TARGET), createNode(item, "<handler>", `${item.path}/${SAFE_PATH_PREFIX}handler`, { value: proxyHandler }, NODE_TYPES.PROXY_HANDLER)];
}

function makeNodesForEntries(item) {
  const { path } = item;
  const nodeName = "<entries>";
  const entriesPath = `${path}/${SAFE_PATH_PREFIX}entries`;

  if (nodeHasAllEntriesInPreview(item)) {
    let entriesNodes = [];
    const { preview } = getValue(item);
    if (preview.entries) {
      entriesNodes = preview.entries.map(([key, value], index) => {
        return createNode(item, index, `${entriesPath}/${index}`, {
          value: GripMapEntryRep.createGripMapEntry(key, value)
        });
      });
    } else if (preview.items) {
      entriesNodes = preview.items.map((value, index) => {
        return createNode(item, index, `${entriesPath}/${index}`, { value });
      });
    }
    return createNode(item, nodeName, entriesPath, entriesNodes, NODE_TYPES.ENTRIES);
  }
  return createNode(item, nodeName, entriesPath, null, NODE_TYPES.ENTRIES);
}

function makeNodesForMapEntry(item) {
  const nodeValue = getValue(item);
  if (!nodeValue || !nodeValue.preview) {
    return [];
  }

  const { key, value } = nodeValue.preview;
  const path = item.path;

  return [createNode(item, "<key>", `${path}/##key`, { value: key }, NODE_TYPES.MAP_ENTRY_KEY), createNode(item, "<value>", `${path}/##value`, { value }, NODE_TYPES.MAP_ENTRY_VALUE)];
}

function getNodeGetter(item) {
  return get(item, "contents.get", undefined);
}

function getNodeSetter(item) {
  return get(item, "contents.set", undefined);
}

function makeNodesForAccessors(item) {
  const accessors = [];

  const getter = getNodeGetter(item);
  if (getter && getter.type !== "undefined") {
    accessors.push(createNode(item, "<get>", `${item.path}/${SAFE_PATH_PREFIX}get`, { value: getter }, NODE_TYPES.GET));
  }

  const setter = getNodeSetter(item);
  if (setter && setter.type !== "undefined") {
    accessors.push(createNode(item, "<set>", `${item.path}/${SAFE_PATH_PREFIX}set`, { value: setter }, NODE_TYPES.SET));
  }

  return accessors;
}

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 parentPath = parent.path;
  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);

  let 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 bucketKey = `${SAFE_PATH_PREFIX}bucket_${minIndex}-${maxIndex}`;
    const bucketName = `[${minIndex}…${maxIndex}]`;

    buckets.push(createNode(parent, bucketName, `${parentPath}/${bucketKey}`, null, NODE_TYPES.BUCKET, {
      startIndex: minIndex,
      endIndex: maxIndex
    }));
  }
  return buckets;
}

function makeDefaultPropsBucket(propertiesNames, parent, ownProperties) {
  const parentPath = parent.path;

  const userPropertiesNames = [];
  const defaultProperties = [];

  propertiesNames.forEach(name => {
    if (isDefaultWindowProperty(name)) {
      defaultProperties.push(name);
    } else {
      userPropertiesNames.push(name);
    }
  });

  let nodes = makeNodesForOwnProps(userPropertiesNames, parent, ownProperties);

  if (defaultProperties.length > 0) {
    const defaultPropertiesNode = createNode(parent, "[default properties]", `${parentPath}/${SAFE_PATH_PREFIX}default`, null, NODE_TYPES.DEFAULT_PROPERTIES);

    const defaultNodes = defaultProperties.map((name, index) => createNode(defaultPropertiesNode, maybeEscapePropertyName(name), `${parentPath}/${SAFE_PATH_PREFIX}bucket${index}/${name}`, ownProperties[name]));
    nodes.push(setNodeChildren(defaultPropertiesNode, defaultNodes));
  }
  return nodes;
}

function makeNodesForOwnProps(propertiesNames, parent, ownProperties) {
  const parentPath = parent.path;
  return propertiesNames.map(name => createNode(parent, maybeEscapePropertyName(name), `${parentPath}/${name}`, ownProperties[name]));
}

function makeNodesForProperties(objProps, parent) {
  const {
    ownProperties = {},
    ownSymbols,
    prototype,
    safeGetterValues
  } = objProps;

  const parentPath = parent.path;
  const parentValue = getValue(parent);

  let allProperties = Object.assign({}, 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));
  });

  let nodes = [];
  if (parentValue && parentValue.class == "Window") {
    nodes = makeDefaultPropsBucket(propertiesNames, parent, allProperties);
  } else {
    nodes = makeNodesForOwnProps(propertiesNames, parent, allProperties);
  }

  if (Array.isArray(ownSymbols)) {
    ownSymbols.forEach((ownSymbol, index) => {
      nodes.push(createNode(parent, ownSymbol.name, `${parentPath}/${SAFE_PATH_PREFIX}symbol-${index}`, ownSymbol.descriptor || null));
    }, this);
  }

  if (nodeIsPromise(parent)) {
    nodes.push(...makeNodesForPromiseProperties(parent));
  }

  if (nodeHasEntries(parent)) {
    nodes.push(makeNodesForEntries(parent));
  }

  // Add the prototype if it exists and is not null
  if (prototype && prototype.type !== "null") {
    nodes.push(makeNodeForPrototype(objProps, parent));
  }

  return nodes;
}

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, "__proto__", `${parent.path}/__proto__`, { value: prototype }, NODE_TYPES.PROTOTYPE);
  }

  return null;
}

function createNode(parent, name, path, contents, type = NODE_TYPES.GRIP, meta) {
  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,
  // i.e. `{ foo: { bar: { baz: 5 }}}` will have a path of `foo/bar/baz`
  // for the inner object.
  return {
    parent,
    name,
    path,
    contents,
    type,
    meta
  };
}

function setNodeChildren(node, children) {
  node.contents = children;
  return node;
}

function getChildren(options) {
  const {
    cachedNodes,
    loadedProperties = new Map(),
    item
  } = options;

  const key = item.path;
  if (cachedNodes && cachedNodes.has(key)) {
    return cachedNodes.get(key);
  }

  const loadedProps = loadedProperties.get(key);
  const {
    ownProperties,
    ownSymbols,
    safeGetterValues,
    prototype
  } = loadedProps || {};
  const hasLoadedProps = ownProperties || ownSymbols || safeGetterValues || prototype;

  // 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 (nodeHasAccessors(item)) {
    return addToCache(makeNodesForAccessors(item));
  }

  if (nodeIsMapEntry(item)) {
    return addToCache(makeNodesForMapEntry(item));
  }

  if (nodeIsProxy(item)) {
    const nodes = makeNodesForProxyProperties(item);
    const protoNode = makeNodeForPrototype(loadedProps, item);
    if (protoNode) {
      return addToCache(nodes.concat(protoNode));
    }
    return nodes;
  }

  if (nodeNeedsNumericalBuckets(item)) {
    const bucketNodes = makeNumericalBuckets(item);
    // Even if we have numerical buckets, we might have loaded non indexed properties,
    // like length for example.
    if (hasLoadedProps) {
      return addToCache(bucketNodes.concat(makeNodesForProperties(loadedProps, item)));
    }

    // We don't cache the result here so we can have the prototype, properties and symbols
    // when they are loaded.
    return bucketNodes;
  }

  if (!nodeIsEntries(item) && !nodeIsBucket(item) && !nodeHasProperties(item)) {
    return [];
  }

  if (!hasLoadedProps) {
    return [];
  }

  return addToCache(makeNodesForProperties(loadedProps, item));
}

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);
}

module.exports = {
  createNode,
  getChildren,
  getClosestGripNode,
  getClosestNonBucketNode,
  getParent,
  getNumericalPropertiesCount,
  getValue,
  makeNodesForEntries,
  makeNodesForPromiseProperties,
  makeNodesForProperties,
  makeNumericalBuckets,
  nodeHasAccessors,
  nodeHasAllEntriesInPreview,
  nodeHasChildren,
  nodeHasEntries,
  nodeHasProperties,
  nodeIsBucket,
  nodeIsDefaultProperties,
  nodeIsEntries,
  nodeIsFunction,
  nodeIsGetter,
  nodeIsMapEntry,
  nodeIsMissingArguments,
  nodeIsObject,
  nodeIsOptimizedOut,
  nodeIsPrimitive,
  nodeIsPromise,
  nodeIsPrototype,
  nodeIsProxy,
  nodeIsSetter,
  nodeIsUninitializedBinding,
  nodeIsUnmappedBinding,
  nodeIsUnscopedBinding,
  nodeIsWindow,
  nodeNeedsNumericalBuckets,
  nodeSupportsNumericalBucketing,
  setNodeChildren,
  sortProperties,
  NODE_TYPES,
  // Export for testing purpose.
  SAFE_PATH_PREFIX
};

/***/ }),
/* 20 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 } = __webpack_require__(3);
const { REPS, getRep } = __webpack_require__(4);
const ObjectInspector = __webpack_require__(48);
const ObjectInspectorUtils = __webpack_require__(17);

const {
  parseURLEncodedText,
  parseURLParams,
  maybeEscapePropertyName,
  getGripPreviewItems
} = __webpack_require__(0);

module.exports = {
  REPS,
  getRep,
  MODE,
  maybeEscapePropertyName,
  parseURLEncodedText,
  parseURLParams,
  getGripPreviewItems,
  ObjectInspector,
  ObjectInspectorUtils
};

/***/ }),
/* 21 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 22 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Dependencies
const {
  getGripType,
  wrapRender
} = __webpack_require__(0);

const dom = __webpack_require__(1);
const { span } = dom;

/**
 * Renders undefined value
 */
const Undefined = function () {
  return span({ className: "objectBox objectBox-undefined" }, "undefined");
};

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
};

/***/ }),
/* 23 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Dependencies
const { wrapRender } = __webpack_require__(0);
const dom = __webpack_require__(1);
const { span } = dom;

/**
 * Renders null value
 */
function Null(props) {
  return span({ className: "objectBox objectBox-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
};

/***/ }),
/* 24 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Dependencies
const PropTypes = __webpack_require__(2);

const {
  getGripType,
  wrapRender
} = __webpack_require__(0);

const dom = __webpack_require__(1);
const { span } = dom;

/**
 * Renders a number
 */
Number.propTypes = {
  object: PropTypes.oneOfType([PropTypes.object, PropTypes.number, PropTypes.bool]).isRequired
};

function Number(props) {
  let value = props.object;

  return span({ className: "objectBox objectBox-number" }, stringify(value));
}

function stringify(object) {
  let isNegativeZero = Object.is(object, -0) || object.type && object.type == "-0";

  return isNegativeZero ? "-0" : String(object);
}

function supportsObject(object, noGrip = false) {
  return ["boolean", "number", "-0"].includes(getGripType(object, noGrip));
}

// Exports from this module

module.exports = {
  rep: wrapRender(Number),
  supportsObject
};

/***/ }),
/* 25 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Dependencies
const PropTypes = __webpack_require__(2);
const {
  wrapRender,
  ellipsisElement
} = __webpack_require__(0);
const PropRep = __webpack_require__(6);
const { MODE } = __webpack_require__(3);

const dom = __webpack_require__(1);
const { span } = dom;

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,
  // @TODO Change this to Object.values once it's supported in Node's version of V8
  mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
  title: PropTypes.string
};

function ObjectRep(props) {
  let object = props.object;
  let propsArray = safePropIterator(props, object);

  if (props.mode === MODE.TINY) {
    const tinyModeItems = [];
    if (getTitle(props, object) !== DEFAULT_TITLE) {
      tinyModeItems.push(getTitleElement(props, object));
    } else {
      tinyModeItems.push(span({
        className: "objectLeftBrace"
      }, "{"), propsArray.length > 0 ? ellipsisElement : null, span({
        className: "objectRightBrace"
      }, "}"));
    }

    return span({ className: "objectBox objectBox-object" }, ...tinyModeItems);
  }

  return span({ className: "objectBox objectBox-object" }, getTitleElement(props, object), span({
    className: "objectLeftBrace"
  }, " { "), ...propsArray, span({
    className: "objectRightBrace"
  }, " }"));
}

function getTitleElement(props, object) {
  return span({ className: "objectTitle" }, getTitle(props, object));
}

function getTitle(props, object) {
  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(Object.assign({}, props, {
      key: name,
      mode: MODE.TINY,
      name,
      object: value,
      equal: ": "
    })));
    propertiesNumber++;

    if (propertiesNumber < propertiesNames.length) {
      elements.push(", ");
    }
  };

  try {
    for (let 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 (let 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) {
  return true;
}

// Exports from this module
module.exports = {
  rep: wrapRender(ObjectRep),
  supportsObject
};

/***/ }),
/* 26 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Dependencies
const PropTypes = __webpack_require__(2);

const {
  getGripType,
  wrapRender
} = __webpack_require__(0);

const dom = __webpack_require__(1);
const { span } = dom;

/**
 * Renders a symbol.
 */
SymbolRep.propTypes = {
  object: PropTypes.object.isRequired
};

function SymbolRep(props) {
  let {
    className = "objectBox objectBox-symbol",
    object
  } = props;
  let { name } = object;

  return span({
    className,
    "data-link-actor-id": object.actor
  }, `Symbol(${name || ""})`);
}

function supportsObject(object, noGrip = false) {
  return getGripType(object, noGrip) == "symbol";
}

// Exports from this module
module.exports = {
  rep: wrapRender(SymbolRep),
  supportsObject
};

/***/ }),
/* 27 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Dependencies
const PropTypes = __webpack_require__(2);

const {
  getGripType,
  wrapRender
} = __webpack_require__(0);

const dom = __webpack_require__(1);
const { span } = dom;

/**
 * Renders a Infinity object
 */
InfinityRep.propTypes = {
  object: PropTypes.object.isRequired
};

function InfinityRep(props) {
  const {
    object
  } = props;

  return span({ className: "objectBox objectBox-number" }, object.type);
}

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
};

/***/ }),
/* 28 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Dependencies
const {
  getGripType,
  wrapRender
} = __webpack_require__(0);

const dom = __webpack_require__(1);
const { span } = dom;

/**
 * Renders a NaN object
 */
function NaNRep(props) {
  return span({ className: "objectBox objectBox-nan" }, "NaN");
}

function supportsObject(object, noGrip = false) {
  return getGripType(object, noGrip) == "NaN";
}

// Exports from this module
module.exports = {
  rep: wrapRender(NaNRep),
  supportsObject
};

/***/ }),
/* 29 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Dependencies
const dom = __webpack_require__(1);
const PropTypes = __webpack_require__(2);
const {
  wrapRender
} = __webpack_require__(0);
const { MODE } = __webpack_require__(3);
const { span } = dom;

/**
 * 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))
};

function Accessor(props) {
  const {
    object
  } = props;

  const accessors = [];
  if (hasGetter(object)) {
    accessors.push("Getter");
  }
  if (hasSetter(object)) {
    accessors.push("Setter");
  }
  const title = accessors.join(" & ");

  return span({ className: "objectBox objectBox-accessor" }, span({
    className: "objectTitle"
  }, title));
}

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, noGrip = false) {
  if (noGrip !== true && (hasGetter(object) || hasSetter(object))) {
    return true;
  }

  return false;
}

// Exports from this module
module.exports = {
  rep: wrapRender(Accessor),
  supportsObject
};

/***/ }),
/* 30 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// ReactJS
const PropTypes = __webpack_require__(2);
const dom = __webpack_require__(1);
const { span } = dom;

// Reps
const {
  getGripType,
  isGrip,
  wrapRender
} = __webpack_require__(0);
const { rep: StringRep } = __webpack_require__(7);

/**
 * Renders DOM attribute
 */
Attribute.propTypes = {
  object: PropTypes.object.isRequired
};

function Attribute(props) {
  let {
    object
  } = props;
  let value = object.preview.value;

  return span({
    "data-link-actor-id": object.actor,
    className: "objectBox-Attr"
  }, span({ className: "attrName" }, getTitle(object)), span({ className: "attrEqual" }, "="), StringRep({ className: "attrValue", object: value }));
}

function getTitle(grip) {
  return grip.preview.nodeName;
}

// Registration
function supportsObject(grip, noGrip = false) {
  if (noGrip === true || !isGrip(grip)) {
    return false;
  }

  return getGripType(grip, noGrip) == "Attr" && grip.preview;
}

module.exports = {
  rep: wrapRender(Attribute),
  supportsObject
};

/***/ }),
/* 31 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// ReactJS
const PropTypes = __webpack_require__(2);

// Reps
const {
  getGripType,
  isGrip,
  wrapRender
} = __webpack_require__(0);

const dom = __webpack_require__(1);
const { span } = dom;

/**
 * Used to render JS built-in Date() object.
 */
DateTime.propTypes = {
  object: PropTypes.object.isRequired
};

function DateTime(props) {
  let grip = props.object;
  let date;
  try {
    date = span({
      "data-link-actor-id": grip.actor,
      className: "objectBox"
    }, getTitle(grip), span({ className: "Date" }, new Date(grip.preview.timestamp).toISOString()));
  } catch (e) {
    date = span({ className: "objectBox" }, "Invalid Date");
  }

  return date;
}

function getTitle(grip) {
  return span({
    className: "objectTitle"
  }, grip.class + " ");
}

// Registration
function supportsObject(grip, noGrip = false) {
  if (noGrip === true || !isGrip(grip)) {
    return false;
  }

  return getGripType(grip, noGrip) == "Date" && grip.preview;
}

// Exports from this module
module.exports = {
  rep: wrapRender(DateTime),
  supportsObject
};

/***/ }),
/* 32 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// ReactJS
const PropTypes = __webpack_require__(2);

// Reps
const {
  getGripType,
  isGrip,
  getURLDisplayString,
  wrapRender
} = __webpack_require__(0);

const dom = __webpack_require__(1);
const { span } = dom;

/**
 * Renders DOM document object.
 */
Document.propTypes = {
  object: PropTypes.object.isRequired
};

function Document(props) {
  let grip = props.object;
  const location = getLocation(grip);
  return span({
    "data-link-actor-id": grip.actor,
    className: "objectBox objectBox-document"
  }, getTitle(grip), location ? " " : null, location ? span({ className: "location" }, location) : null);
}

function getLocation(grip) {
  let location = grip.preview.location;
  return location ? getURLDisplayString(location) : null;
}

function getTitle(grip) {
  return span({
    className: "objectTitle"
  }, grip.class);
}

// Registration
function supportsObject(object, noGrip = false) {
  if (noGrip === true || !isGrip(object)) {
    return false;
  }

  const type = getGripType(object, noGrip);
  return object.preview && (type === "HTMLDocument" || type === "XULDocument");
}

// Exports from this module
module.exports = {
  rep: wrapRender(Document),
  supportsObject
};

/***/ }),
/* 33 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// ReactJS
const PropTypes = __webpack_require__(2);

// Reps
const {
  isGrip,
  wrapRender
} = __webpack_require__(0);

const { MODE } = __webpack_require__(3);
const { rep } = __webpack_require__(9);

/**
 * Renders DOM event objects.
 */
Event.propTypes = {
  object: PropTypes.object.isRequired,
  // @TODO Change this to Object.values once it's supported in Node's version of V8
  mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
  onDOMNodeMouseOver: PropTypes.func,
  onDOMNodeMouseOut: PropTypes.func,
  onInspectIconClick: PropTypes.func
};

function Event(props) {
  // Use `Object.assign` to keep `props` without changes because:
  // 1. JSON.stringify/JSON.parse is slow.
  // 2. Immutable.js is planned for the future.
  let gripProps = Object.assign({}, props, {
    title: getTitle(props)
  });
  gripProps.object = Object.assign({}, props.object);
  gripProps.object.preview = Object.assign({}, props.object.preview);

  gripProps.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) {
  let 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, noGrip = false) {
  if (noGrip === true || !isGrip(grip)) {
    return false;
  }

  return grip.preview && grip.preview.kind == "DOMEvent";
}

// Exports from this module
module.exports = {
  rep: wrapRender(Event),
  supportsObject
};

/***/ }),
/* 34 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// ReactJS
const PropTypes = __webpack_require__(2);

// Reps
const {
  getGripType,
  isGrip,
  cropString,
  wrapRender
} = __webpack_require__(0);
const { MODE } = __webpack_require__(3);
const Svg = __webpack_require__(10);

const dom = __webpack_require__(1);
const { span } = dom;

const IGNORED_SOURCE_URLS = ["debugger eval code"];

/**
 * This component represents a template for Function objects.
 */
FunctionRep.propTypes = {
  object: PropTypes.object.isRequired,
  parameterNames: PropTypes.array,
  onViewSourceInDebugger: PropTypes.func
};

function FunctionRep(props) {
  let {
    object: grip,
    onViewSourceInDebugger
  } = props;

  let jumpToDefinitionButton;
  if (onViewSourceInDebugger && grip.location && grip.location.url && !IGNORED_SOURCE_URLS.includes(grip.location.url)) {
    jumpToDefinitionButton = Svg("jump-definition", {
      element: "a",
      draggable: false,
      title: "Jump to definition",
      onClick: e => {
        // Stop the event propagation so we don't trigger ObjectInspector expand/collapse.
        e.stopPropagation();
        onViewSourceInDebugger(grip.location);
      }
    });
  }

  return span({
    "data-link-actor-id": grip.actor,
    className: "objectBox objectBox-function",
    // Set dir="ltr" to prevent function parentheses from
    // appearing in the wrong direction
    dir: "ltr"
  }, getTitle(grip, props), getFunctionName(grip, props), "(", ...renderParams(props), ")", jumpToDefinitionButton);
}

function getTitle(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);
}

// Decodes an anonymous naming scheme that
// spider monkey implements based on "Naming Anonymous JavaScript Functions"
// http://johnjbarton.github.io/nonymous/index.html
const objectProperty = /([\w\d]+)$/;
const arrayProperty = /\[(.*?)\]$/;
const functionProperty = /([\w\d]+)[\/\.<]*?$/;
const annonymousProperty = /([\w\d]+)\(\^\)$/;

function getFunctionName(grip, props = {}) {
  let { functionName } = props;
  let name;

  if (functionName) {
    let 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 = grip.userDisplayName || grip.displayName || grip.name || props.functionName || "";

    const scenarios = [objectProperty, arrayProperty, functionProperty, annonymousProperty];

    scenarios.some(reg => {
      const match = reg.exec(name);
      if (match) {
        name = match[1];
        return true;
      }
      return false;
    });
  }

  return cropString(name, 100);
}

function renderParams(props) {
  const {
    parameterNames = []
  } = props;

  return parameterNames.filter(param => param).reduce((res, param, index, arr) => {
    res.push(span({ className: "param" }, param));
    if (index < arr.length - 1) {
      res.push(span({ className: "delimiter" }, ", "));
    }
    return res;
  }, []);
}

// Registration
function supportsObject(grip, noGrip = false) {
  const type = getGripType(grip, noGrip);
  if (noGrip === true || !isGrip(grip)) {
    return type == "function";
  }

  return type == "Function";
}

// Exports from this module

module.exports = {
  rep: wrapRender(FunctionRep),
  supportsObject,
  // exported for testing purpose.
  getFunctionName
};

/***/ }),
/* 35 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
    value: true
});
exports.convertReactSVGDOMProperty = convertReactSVGDOMProperty;
exports.startsWith = startsWith;
exports.serializeAttrs = serializeAttrs;
exports.getSVGFromSource = getSVGFromSource;
exports.extractSVGProps = extractSVGProps;
// Transform DOM prop/attr names applicable to `<svg>` element but react-limited

function convertReactSVGDOMProperty(str) {
    return str.replace(/[-|:]([a-z])/g, function (g) {
        return g[1].toUpperCase();
    });
}

function startsWith(str, substring) {
    return str.indexOf(substring) === 0;
}

var DataPropPrefix = 'data-';
// Serialize `Attr` objects in `NamedNodeMap`
function serializeAttrs(map) {
    var ret = {};
    for (var prop, i = 0; i < map.length; i++) {
        var key = map[i].name;
        if (!startsWith(key, DataPropPrefix)) {
            prop = convertReactSVGDOMProperty(key);
        }
        ret[prop] = map[i].value;
    }
    return ret;
}

function getSVGFromSource(src) {
    var svgContainer = document.createElement('div');
    svgContainer.innerHTML = src;
    var svg = svgContainer.firstElementChild;
    svg.remove(); // deref from parent element
    return svg;
}

// get <svg /> element props
function extractSVGProps(src) {
    var map = getSVGFromSource(src).attributes;
    return map.length > 0 ? serializeAttrs(map) : null;
}

/***/ }),
/* 36 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M8,3L12,3L12,7L14,7L14,8L12,8L12,12L8,12L8,14L7,14L7,12L3,12L3,8L1,8L1,7L3,7L3,3L7,3L7,1L8,1L8,3ZM10,10L10,5L5,5L5,10L10,10Z\"></path></svg>"

/***/ }),
/* 37 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\"><g stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\" stroke-linecap=\"round\"><g id=\"arrow\" transform=\"translate(1.000000, 3.000000)\"><path d=\"M4.5,0.5 L6.5,2.5\"></path><path d=\"M4.5,2.5 L6.5,4.5\" transform=\"translate(5.500000, 3.500000) scale(1, -1) translate(-5.500000, -3.500000) \"></path><path d=\"M6.00090144,2.5 C4.67806937,2.5 3.67938478,2.5 3.00484766,2.5 C1.99304199,2.5 1.01049805,3.5168457 0.993840144,4.52403846 C0.988750751,4.54723808 0.988750751,5.87097168 0.993840144,8.49523926\" id=\"Path-2\" stroke-linejoin=\"round\"></path></g><g id=\"content-lines\" transform=\"translate(9.000000, 2.000000)\"><path d=\"M1.5,3.5 L5.5,3.5\"></path><path d=\"M0.5,1.5 L5.5,1.5\"></path><path d=\"M0.5,5.5 L5.5,5.5\"></path></g></g></svg>"

/***/ }),
/* 38 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// ReactJS
const PropTypes = __webpack_require__(2);
// Dependencies
const {
  getGripType,
  isGrip,
  wrapRender
} = __webpack_require__(0);

const PropRep = __webpack_require__(6);
const { MODE } = __webpack_require__(3);

const dom = __webpack_require__(1);
const { span } = dom;

/**
 * Renders a DOM Promise object.
 */
PromiseRep.propTypes = {
  object: PropTypes.object.isRequired,
  // @TODO Change this to Object.values once it's supported in Node's version of V8
  mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
  onDOMNodeMouseOver: PropTypes.func,
  onDOMNodeMouseOut: PropTypes.func,
  onInspectIconClick: PropTypes.func
};

function PromiseRep(props) {
  const object = props.object;
  const { promiseState } = object;

  const config = {
    "data-link-actor-id": object.actor,
    className: "objectBox objectBox-object"
  };

  if (props.mode === MODE.TINY) {
    let { Rep } = __webpack_require__(4);

    return span(config, getTitle(object), span({
      className: "objectLeftBrace"
    }, " { "), Rep({ object: promiseState.state }), span({
      className: "objectRightBrace"
    }, " }"));
  }

  const propsArray = getProps(props, promiseState);
  return span(config, getTitle(object), span({
    className: "objectLeftBrace"
  }, " { "), ...propsArray, span({
    className: "objectRightBrace"
  }, " }"));
}

function getTitle(object) {
  return span({
    className: "objectTitle"
  }, object.class);
}

function getProps(props, promiseState) {
  const keys = ["state"];
  if (Object.keys(promiseState).includes("value")) {
    keys.push("value");
  }

  return keys.reduce((res, key, i) => {
    let object = promiseState[key];
    res = res.concat(PropRep(Object.assign({}, props, {
      mode: MODE.TINY,
      name: `<${key}>`,
      object,
      equal: ": ",
      suppressQuotes: true
    })));

    // Interleave commas between elements
    if (i !== keys.length - 1) {
      res.push(", ");
    }

    return res;
  }, []);
}

// Registration
function supportsObject(object, noGrip = false) {
  if (noGrip === true || !isGrip(object)) {
    return false;
  }
  return getGripType(object, noGrip) == "Promise";
}

// Exports from this module
module.exports = {
  rep: wrapRender(PromiseRep),
  supportsObject
};

/***/ }),
/* 39 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// ReactJS
const PropTypes = __webpack_require__(2);

// Reps
const {
  getGripType,
  isGrip,
  wrapRender
} = __webpack_require__(0);

const dom = __webpack_require__(1);
const { span } = dom;

/**
 * Renders a grip object with regular expression.
 */
RegExp.propTypes = {
  object: PropTypes.object.isRequired
};

function RegExp(props) {
  let { object } = props;

  return span({
    "data-link-actor-id": object.actor,
    className: "objectBox objectBox-regexp regexpSource"
  }, getSource(object));
}

function getSource(grip) {
  return grip.displayString;
}

// Registration
function supportsObject(object, noGrip = false) {
  if (noGrip === true || !isGrip(object)) {
    return false;
  }

  return getGripType(object, noGrip) == "RegExp";
}

// Exports from this module
module.exports = {
  rep: wrapRender(RegExp),
  supportsObject
};

/***/ }),
/* 40 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// ReactJS
const PropTypes = __webpack_require__(2);

// Reps
const {
  getGripType,
  isGrip,
  getURLDisplayString,
  wrapRender
} = __webpack_require__(0);

const dom = __webpack_require__(1);
const { span } = dom;

/**
 * Renders a grip representing CSSStyleSheet
 */
StyleSheet.propTypes = {
  object: PropTypes.object.isRequired
};

function StyleSheet(props) {
  let grip = props.object;

  return span({
    "data-link-actor-id": grip.actor,
    className: "objectBox objectBox-object"
  }, getTitle(grip), span({ className: "objectPropValue" }, getLocation(grip)));
}

function getTitle(grip) {
  let title = "StyleSheet ";
  return span({ className: "objectBoxTitle" }, title);
}

function getLocation(grip) {
  // Embedded stylesheets don't have URL and so, no preview.
  let url = grip.preview ? grip.preview.url : "";
  return url ? getURLDisplayString(url) : "";
}

// Registration
function supportsObject(object, noGrip = false) {
  if (noGrip === true || !isGrip(object)) {
    return false;
  }

  return getGripType(object, noGrip) == "CSSStyleSheet";
}

// Exports from this module

module.exports = {
  rep: wrapRender(StyleSheet),
  supportsObject
};

/***/ }),
/* 41 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Dependencies
const PropTypes = __webpack_require__(2);
const {
  isGrip,
  cropString,
  cropMultipleLines,
  wrapRender
} = __webpack_require__(0);
const { MODE } = __webpack_require__(3);
const nodeConstants = __webpack_require__(12);
const dom = __webpack_require__(1);
const { span } = dom;

/**
 * Renders DOM comment node.
 */
CommentNode.propTypes = {
  object: PropTypes.object.isRequired,
  // @TODO Change this to Object.values once it's supported in Node's version of V8
  mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key]))
};

function CommentNode(props) {
  let {
    object,
    mode = MODE.SHORT
  } = props;

  let { textContent } = object.preview;
  if (mode === MODE.TINY) {
    textContent = cropMultipleLines(textContent, 30);
  } else if (mode === MODE.SHORT) {
    textContent = cropString(textContent, 50);
  }

  return span({
    className: "objectBox theme-comment",
    "data-link-actor-id": object.actor
  }, `<!-- ${textContent} -->`);
}

// Registration
function supportsObject(object, noGrip = false) {
  if (noGrip === true || !isGrip(object)) {
    return false;
  }
  return object.preview && object.preview.nodeType === nodeConstants.COMMENT_NODE;
}

// Exports from this module
module.exports = {
  rep: wrapRender(CommentNode),
  supportsObject
};

/***/ }),
/* 42 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// ReactJS
const PropTypes = __webpack_require__(2);

// Utils
const {
  isGrip,
  wrapRender
} = __webpack_require__(0);
const { rep: StringRep } = __webpack_require__(7);
const { MODE } = __webpack_require__(3);
const nodeConstants = __webpack_require__(12);
const Svg = __webpack_require__(10);

const dom = __webpack_require__(1);
const { span } = dom;

/**
 * Renders DOM element node.
 */
ElementNode.propTypes = {
  object: PropTypes.object.isRequired,
  // @TODO Change this to Object.values once it's supported in Node's version of V8
  mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
  onDOMNodeMouseOver: PropTypes.func,
  onDOMNodeMouseOut: PropTypes.func,
  onInspectIconClick: PropTypes.func
};

function ElementNode(props) {
  let {
    object,
    mode,
    onDOMNodeMouseOver,
    onDOMNodeMouseOut,
    onInspectIconClick
  } = props;
  let elements = getElements(object, mode);

  let isInTree = object.preview && object.preview.isConnected === true;

  let baseConfig = {
    "data-link-actor-id": object.actor,
    className: "objectBox objectBox-node"
  };
  let inspectIcon;
  if (isInTree) {
    if (onDOMNodeMouseOver) {
      Object.assign(baseConfig, {
        onMouseOver: _ => onDOMNodeMouseOver(object)
      });
    }

    if (onDOMNodeMouseOut) {
      Object.assign(baseConfig, {
        onMouseOut: onDOMNodeMouseOut
      });
    }

    if (onInspectIconClick) {
      inspectIcon = Svg("open-inspector", {
        element: "a",
        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)
      });
    }
  }

  return span(baseConfig, ...elements, inspectIcon);
}

function getElements(grip, mode) {
  let { attributes, nodeName } = grip.preview;
  const nodeNameElement = span({
    className: "tag-name"
  }, nodeName);

  if (mode === MODE.TINY) {
    let elements = [nodeNameElement];
    if (attributes.id) {
      elements.push(span({ className: "attrName" }, `#${attributes.id}`));
    }
    if (attributes.class) {
      elements.push(span({ className: "attrName" }, attributes.class.trim().split(/\s+/).map(cls => `.${cls}`).join("")));
    }
    return elements;
  }
  let 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) => {
    let value = attributes[name];
    let attribute = span({}, span({ className: "attrName" }, name), span({ className: "attrEqual" }, "="), StringRep({ className: "attrValue", object: value }));

    return arr.concat([" ", attribute]);
  }, []);

  return [span({ className: "angleBracket" }, "<"), nodeNameElement, ...attributeElements, span({ className: "angleBracket" }, ">")];
}

// Registration
function supportsObject(object, noGrip = false) {
  if (noGrip === true || !isGrip(object)) {
    return false;
  }
  return object.preview && object.preview.nodeType === nodeConstants.ELEMENT_NODE;
}

// Exports from this module
module.exports = {
  rep: wrapRender(ElementNode),
  supportsObject
};

/***/ }),
/* 43 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// ReactJS
const PropTypes = __webpack_require__(2);

// Reps
const {
  isGrip,
  cropString,
  wrapRender
} = __webpack_require__(0);
const { MODE } = __webpack_require__(3);
const Svg = __webpack_require__(10);

const dom = __webpack_require__(1);
const { span } = dom;

/**
 * Renders DOM #text node.
 */
TextNode.propTypes = {
  object: PropTypes.object.isRequired,
  // @TODO Change this to Object.values once it's supported in Node's version of V8
  mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
  onDOMNodeMouseOver: PropTypes.func,
  onDOMNodeMouseOut: PropTypes.func,
  onInspectIconClick: PropTypes.func
};

function TextNode(props) {
  let {
    object: grip,
    mode = MODE.SHORT,
    onDOMNodeMouseOver,
    onDOMNodeMouseOut,
    onInspectIconClick
  } = props;

  let baseConfig = {
    "data-link-actor-id": grip.actor,
    className: "objectBox objectBox-textNode"
  };
  let inspectIcon;
  let isInTree = grip.preview && grip.preview.isConnected === true;

  if (isInTree) {
    if (onDOMNodeMouseOver) {
      Object.assign(baseConfig, {
        onMouseOver: _ => onDOMNodeMouseOver(grip)
      });
    }

    if (onDOMNodeMouseOut) {
      Object.assign(baseConfig, {
        onMouseOut: onDOMNodeMouseOut
      });
    }

    if (onInspectIconClick) {
      inspectIcon = Svg("open-inspector", {
        element: "a",
        draggable: false,
        // TODO: Localize this with "openNodeInInspector" when Bug 1317038 lands
        title: "Click to select the node in the inspector",
        onClick: e => onInspectIconClick(grip, e)
      });
    }
  }

  if (mode === MODE.TINY) {
    return span(baseConfig, getTitle(grip), inspectIcon);
  }

  return span(baseConfig, getTitle(grip), span({ className: "nodeValue" }, " ", `"${getTextContent(grip)}"`), inspectIcon);
}

function getTextContent(grip) {
  return cropString(grip.preview.textContent);
}

function getTitle(grip) {
  const title = "#text";
  return span({}, title);
}

// Registration
function supportsObject(grip, noGrip = false) {
  if (noGrip === true || !isGrip(grip)) {
    return false;
  }

  return grip.preview && grip.class == "Text";
}

// Exports from this module
module.exports = {
  rep: wrapRender(TextNode),
  supportsObject
};

/***/ }),
/* 44 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// ReactJS
const PropTypes = __webpack_require__(2);
// Utils
const {
  getGripType,
  isGrip,
  wrapRender
} = __webpack_require__(0);
const { MODE } = __webpack_require__(3);

const dom = __webpack_require__(1);
const { span } = dom;

/**
 * Renders Error objects.
 */
ErrorRep.propTypes = {
  object: PropTypes.object.isRequired,
  // @TODO Change this to Object.values once it's supported in Node's version of V8
  mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key]))
};

function ErrorRep(props) {
  let object = props.object;
  let preview = object.preview;

  let name;
  if (preview && preview.name && 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";
  }

  let content = props.mode === MODE.TINY ? name : `${name}: ${preview.message}`;

  if (preview.stack && props.mode !== MODE.TINY) {
    /*
      * Since Reps are used in the JSON Viewer, we can't localize
      * the "Stack trace" label (defined in debugger.properties as
      * "variablesViewErrorStacktrace" property), until Bug 1317038 lands.
      */
    content = `${content}\nStack trace:\n${preview.stack}`;
  }

  return span({
    "data-link-actor-id": object.actor,
    className: "objectBox-stackTrace"
  }, content);
}

// Registration
function supportsObject(object, noGrip = false) {
  if (noGrip === true || !isGrip(object)) {
    return false;
  }
  return object.preview && getGripType(object, noGrip) === "Error" || object.class === "DOMException";
}

// Exports from this module
module.exports = {
  rep: wrapRender(ErrorRep),
  supportsObject
};

/***/ }),
/* 45 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// ReactJS
const PropTypes = __webpack_require__(2);

// Reps
const {
  getGripType,
  isGrip,
  getURLDisplayString,
  wrapRender
} = __webpack_require__(0);

const { MODE } = __webpack_require__(3);

const dom = __webpack_require__(1);
const { span } = dom;

/**
 * Renders a grip representing a window.
 */
WindowRep.propTypes = {
  // @TODO Change this to Object.values once it's supported in Node's version of V8
  mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
  object: PropTypes.object.isRequired
};

function WindowRep(props) {
  let {
    mode,
    object
  } = props;

  const config = {
    "data-link-actor-id": object.actor,
    className: "objectBox objectBox-Window"
  };

  if (mode === MODE.TINY) {
    return span(config, getTitle(object));
  }

  return span(config, getTitle(object), " ", span({ className: "location" }, getLocation(object)));
}

function getTitle(object) {
  let title = object.displayClass || object.class || "Window";
  return span({ className: "objectTitle" }, title);
}

function getLocation(object) {
  return getURLDisplayString(object.preview.url);
}

// Registration
function supportsObject(object, noGrip = false) {
  if (noGrip === true || !isGrip(object)) {
    return false;
  }

  return object.preview && getGripType(object, noGrip) == "Window";
}

// Exports from this module
module.exports = {
  rep: wrapRender(WindowRep),
  supportsObject
};

/***/ }),
/* 46 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// ReactJS
const PropTypes = __webpack_require__(2);

// Reps
const {
  isGrip,
  wrapRender
} = __webpack_require__(0);

const String = __webpack_require__(7).rep;

const dom = __webpack_require__(1);
const { span } = dom;

/**
 * Renders a grip object with textual data.
 */
ObjectWithText.propTypes = {
  object: PropTypes.object.isRequired
};

function ObjectWithText(props) {
  let grip = props.object;
  return span({
    "data-link-actor-id": grip.actor,
    className: "objectTitle objectBox objectBox-" + getType(grip)
  }, getType(grip), " ", getDescription(grip));
}

function getType(grip) {
  return grip.class;
}

function getDescription(grip) {
  return String({
    object: grip.preview.text
  });
}

// Registration
function supportsObject(grip, noGrip = false) {
  if (noGrip === true || !isGrip(grip)) {
    return false;
  }

  return grip.preview && grip.preview.kind == "ObjectWithText";
}

// Exports from this module
module.exports = {
  rep: wrapRender(ObjectWithText),
  supportsObject
};

/***/ }),
/* 47 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// ReactJS
const PropTypes = __webpack_require__(2);

// Reps
const {
  isGrip,
  getURLDisplayString,
  wrapRender
} = __webpack_require__(0);

const dom = __webpack_require__(1);
const { span } = dom;

/**
 * Renders a grip object with URL data.
 */
ObjectWithURL.propTypes = {
  object: PropTypes.object.isRequired
};

function ObjectWithURL(props) {
  let grip = props.object;
  return span({
    "data-link-actor-id": grip.actor,
    className: "objectBox objectBox-" + getType(grip)
  }, getTitle(grip), span({ className: "objectPropValue" }, getDescription(grip)));
}

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, noGrip = false) {
  if (noGrip === true || !isGrip(grip)) {
    return false;
  }

  return grip.preview && grip.preview.kind == "ObjectWithURL";
}

// Exports from this module
module.exports = {
  rep: wrapRender(ObjectWithURL),
  supportsObject
};

/***/ }),
/* 48 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var _devtoolsComponents = __webpack_require__(49);

var _devtoolsComponents2 = _interopRequireDefault(_devtoolsComponents);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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, createFactory } = __webpack_require__(8);
const PropTypes = __webpack_require__(2);
const dom = __webpack_require__(1);

const Tree = createFactory(_devtoolsComponents2.default.Tree);
__webpack_require__(53);

const classnames = __webpack_require__(54);

const {
  REPS: {
    Rep,
    Grip
  }
} = __webpack_require__(4);
const {
  MODE
} = __webpack_require__(3);

const Utils = __webpack_require__(17);

const {
  getChildren,
  getClosestGripNode,
  getParent,
  getValue,
  nodeHasAccessors,
  nodeHasProperties,
  nodeIsDefaultProperties,
  nodeIsFunction,
  nodeIsGetter,
  nodeIsMapEntry,
  nodeIsMissingArguments,
  nodeIsOptimizedOut,
  nodeIsPrimitive,
  nodeIsPrototype,
  nodeIsSetter,
  nodeIsUninitializedBinding,
  nodeIsUnmappedBinding,
  nodeIsUnscopedBinding,
  nodeIsWindow
} = Utils.node;

const {
  loadItemProperties
} = Utils.loadProperties;

// 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 child
// 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 {
  constructor(props) {
    super();
    this.cachedNodes = new Map();

    this.state = {
      actors: new Set(),
      expandedPaths: new Set(),
      focusedItem: null,
      loadedProperties: props.loadedProperties || new Map(),
      loading: new Map()
    };

    const self = this;

    self.getChildren = this.getChildren.bind(this);
    self.renderTreeItem = this.renderTreeItem.bind(this);
    self.setExpanded = this.setExpanded.bind(this);
    self.focusItem = this.focusItem.bind(this);
    self.getRoots = this.getRoots.bind(this);
  }

  shouldComponentUpdate(nextProps, nextState) {
    const {
      expandedPaths,
      loadedProperties
    } = this.state;

    if (this.props.roots !== nextProps.roots) {
      // Since the roots changed, we assume the properties did as well. Thus we can clear
      // the cachedNodes to avoid bugs and memory leaks.
      this.cachedNodes.clear();
      return true;
    }

    return expandedPaths.size !== nextState.expandedPaths.size || loadedProperties.size !== nextState.loadedProperties.size || [...expandedPaths].some(key => !nextState.expandedPaths.has(key));
  }

  componentWillUnmount() {
    const { releaseActor } = this.props;
    if (typeof releaseActor !== "function") {
      return;
    }

    const { actors } = this.state;
    for (let actor of actors) {
      releaseActor(actor);
    }
  }

  getChildren(item) {
    const {
      loadedProperties
    } = this.state;
    const { cachedNodes } = this;

    return getChildren({
      loadedProperties,
      cachedNodes,
      item
    });
  }

  getRoots() {
    return this.props.roots;
  }

  getKey(item) {
    return item.path;
  }

  /**
   * This function is responsible for expanding/collapsing a given node,
   * which also means that it will check if we need to fetch properties,
   * entries, prototype and symbols for the said node. If we do, it will call
   * the appropriate ObjectClient functions, and change the state of the component
   * with the results it gets from those functions.
   */
  async setExpanded(item, expand) {
    if (nodeIsPrimitive(item)) {
      return;
    }

    const {
      loadedProperties
    } = this.state;

    const key = this.getKey(item);

    this.setState((prevState, props) => {
      const newPaths = new Set(prevState.expandedPaths);
      if (expand === true) {
        newPaths.add(key);
      } else {
        newPaths.delete(key);
      }
      return {
        expandedPaths: newPaths
      };
    });

    if (expand === true) {
      const gripItem = getClosestGripNode(item);
      const value = getValue(gripItem);
      const path = item.path;

      const onItemPropertiesLoaded = loadItemProperties(item, this.props.createObjectClient, loadedProperties);
      if (onItemPropertiesLoaded !== null) {
        this.setState((prevState, props) => {
          const nextLoading = new Map(prevState.loading);
          nextLoading.set(path, onItemPropertiesLoaded);
          return {
            loading: nextLoading
          };
        });

        const properties = await onItemPropertiesLoaded;

        this.setState((prevState, props) => {
          const nextLoading = new Map(prevState.loading);
          nextLoading.delete(path);

          const isRoot = this.props.roots.some(root => {
            const rootValue = getValue(root);
            return rootValue && rootValue.actor === value.actor;
          });

          return {
            actors: isRoot ? prevState.actors : new Set(prevState.actors).add(value.actor),
            loadedProperties: new Map(prevState.loadedProperties).set(path, properties),
            loading: nextLoading
          };
        });
      }
    }
  }

  focusItem(item) {
    if (!this.props.disabledFocus && this.state.focusedItem !== item) {
      this.setState({
        focusedItem: item
      });

      if (this.props.onFocus) {
        this.props.onFocus(item);
      }
    }
  }

  renderTreeItem(item, depth, focused, arrow, expanded) {
    let objectValue;
    let label = item.name;
    let itemValue = getValue(item);

    const isPrimitive = nodeIsPrimitive(item);

    const unavailable = isPrimitive && itemValue && itemValue.hasOwnProperty && itemValue.hasOwnProperty("unavailable");

    if (nodeIsUninitializedBinding(item)) {
      objectValue = dom.span({ className: "unavailable" }, "(uninitialized)");
    } else if (nodeIsUnmappedBinding(item)) {
      objectValue = dom.span({ className: "unavailable" }, "(unmapped)");
    } else if (nodeIsUnscopedBinding(item)) {
      objectValue = dom.span({ className: "unavailable" }, "(unscoped)");
    } else if (nodeIsOptimizedOut(item)) {
      objectValue = dom.span({ className: "unavailable" }, "(optimized away)");
    } else if (nodeIsMissingArguments(item) || unavailable) {
      objectValue = dom.span({ className: "unavailable" }, "(unavailable)");
    } else if (nodeIsFunction(item) && !nodeIsGetter(item) && !nodeIsSetter(item) && (this.props.mode === MODE.TINY || !this.props.mode)) {
      objectValue = undefined;
      label = this.renderGrip(item, Object.assign({}, this.props, {
        functionName: label
      }));
    } else if (nodeHasProperties(item) || nodeHasAccessors(item) || nodeIsMapEntry(item) || isPrimitive) {
      let repsProp = Object.assign({}, this.props);
      if (depth > 0) {
        repsProp.mode = this.props.mode === MODE.LONG ? MODE.SHORT : MODE.TINY;
      }
      if (expanded) {
        repsProp.mode = MODE.TINY;
      }

      objectValue = this.renderGrip(item, repsProp);
    }

    const hasLabel = label !== null && typeof label !== "undefined";
    const hasValue = typeof objectValue !== "undefined";

    const {
      onDoubleClick,
      onLabelClick,
      dimTopLevelWindow
    } = this.props;

    return dom.div({
      className: classnames("node object-node", {
        focused,
        lessen: !expanded && (nodeIsDefaultProperties(item) || nodeIsPrototype(item) || dimTopLevelWindow === true && nodeIsWindow(item) && depth === 0)
      }),
      onClick: e => {
        e.stopPropagation();
        if (isPrimitive === false) {
          this.setExpanded(item, !expanded);
        }
      },
      onDoubleClick: onDoubleClick ? e => {
        e.stopPropagation();
        onDoubleClick(item, {
          depth,
          focused,
          expanded
        });
      } : null
    }, arrow, hasLabel ? dom.span({
      className: "object-label",
      onClick: onLabelClick ? event => {
        event.stopPropagation();
        onLabelClick(item, {
          depth,
          focused,
          expanded,
          setExpanded: this.setExpanded
        });
      } : null
    }, label) : null, hasLabel && hasValue ? dom.span({ className: "object-delimiter" }, ": ") : null, hasValue ? objectValue : null);
  }

  renderGrip(item, props) {
    const object = getValue(item);
    return Rep(Object.assign({}, props, {
      object,
      mode: props.mode || MODE.TINY,
      defaultRep: Grip
    }));
  }

  render() {
    const {
      autoExpandDepth = 1,
      autoExpandAll = true,
      disabledFocus,
      inline,
      itemHeight = 20,
      disableWrap = false
    } = this.props;

    const {
      expandedPaths,
      focusedItem
    } = this.state;

    let roots = this.getRoots();
    if (roots.length === 1) {
      const root = roots[0];
      const name = root && root.name;
      if (nodeIsPrimitive(root) && (name === null || typeof name === "undefined")) {
        return this.renderGrip(root, this.props);
      }
    }

    return Tree({
      className: classnames({
        inline,
        nowrap: disableWrap,
        "object-inspector": true
      }),
      autoExpandAll,
      autoExpandDepth,
      disabledFocus,
      itemHeight,

      isExpanded: item => expandedPaths.has(this.getKey(item)),
      isExpandable: item => nodeIsPrimitive(item) === false,
      focused: focusedItem,

      getRoots: this.getRoots,
      getParent,
      getChildren: this.getChildren,
      getKey: this.getKey,

      onExpand: item => this.setExpanded(item, true),
      onCollapse: item => this.setExpanded(item, false),
      onFocus: this.focusItem,

      renderItem: this.renderTreeItem
    });
  }
}

ObjectInspector.displayName = "ObjectInspector";

ObjectInspector.propTypes = {
  autoExpandAll: PropTypes.bool,
  autoExpandDepth: PropTypes.number,
  disabledFocus: PropTypes.bool,
  disableWrap: PropTypes.bool,
  inline: PropTypes.bool,
  roots: PropTypes.array,
  itemHeight: PropTypes.number,
  mode: PropTypes.oneOf(Object.values(MODE)),
  createObjectClient: PropTypes.func.isRequired,
  onFocus: PropTypes.func,
  onDoubleClick: PropTypes.func,
  onLabelClick: PropTypes.func
};

module.exports = ObjectInspector;

/***/ }),
/* 49 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var _tree = __webpack_require__(50);

var _tree2 = _interopRequireDefault(_tree);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

module.exports = {
  Tree: _tree2.default
}; /* This Source Code Form is subject to the terms of the Mozilla Public
    * License, v. 2.0. If a copy of the MPL was not distributed with this
    * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/***/ }),
/* 50 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _react = __webpack_require__(8);

var _react2 = _interopRequireDefault(_react);

var _reactDomFactories = __webpack_require__(1);

var _reactDomFactories2 = _interopRequireDefault(_reactDomFactories);

var _propTypes = __webpack_require__(2);

var _propTypes2 = _interopRequireDefault(_propTypes);

var _svgInlineReact = __webpack_require__(11);

var _svgInlineReact2 = _interopRequireDefault(_svgInlineReact);

var _arrow = __webpack_require__(51);

var _arrow2 = _interopRequireDefault(_arrow);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

const { Component, createFactory, createElement } = _react2.default; /* This Source Code Form is subject to the terms of the Mozilla Public
                                                                      * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                                      * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

__webpack_require__(52);

const AUTO_EXPAND_DEPTH = 0; // depth

/**
 * 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: _propTypes2.default.bool
    };
  }

  shouldComponentUpdate(nextProps, nextState) {
    return this.props.expanded !== nextProps.expanded;
  }

  render() {
    const {
      expanded
    } = this.props;

    const classNames = ["arrow"];
    if (expanded) {
      classNames.push("expanded");
    }
    return createElement(_svgInlineReact2.default, {
      className: classNames.join(" "),
      src: _arrow2.default
    });
  }
}

const treeIndent = _reactDomFactories2.default.span({ className: "tree-indent" }, "\u200B");

class TreeNode extends Component {
  static get propTypes() {
    return {
      id: _propTypes2.default.any.isRequired,
      index: _propTypes2.default.number.isRequired,
      depth: _propTypes2.default.number.isRequired,
      focused: _propTypes2.default.bool.isRequired,
      expanded: _propTypes2.default.bool.isRequired,
      item: _propTypes2.default.any.isRequired,
      isExpandable: _propTypes2.default.bool.isRequired,
      onClick: _propTypes2.default.func,
      renderItem: _propTypes2.default.func.isRequired
    };
  }

  shouldComponentUpdate(nextProps) {
    return this.props.item !== nextProps.item || this.props.focused !== nextProps.focused || this.props.expanded !== nextProps.expanded;
  }

  render() {
    const {
      depth,
      id,
      item,
      focused,
      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 }).fill(treeIndent);
    let items = indents.concat(renderItem(item, depth, focused, arrow, expanded));

    return _reactDomFactories2.default.div({
      id,
      className: "tree-node" + (focused ? " focused" : ""),
      onClick: this.props.onClick,
      role: "treeitem",
      "aria-level": depth,
      "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
 * @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;
    });
  };
}

/**
 * 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 performance and memory panels 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({
 *           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,
 *             },
 *               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: _propTypes2.default.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: _propTypes2.default.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: _propTypes2.default.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: _propTypes2.default.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: _propTypes2.default.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: _propTypes2.default.func.isRequired,

      // Optional props

      // The currently focused item, if any such item exists.
      focused: _propTypes2.default.any,

      // Handle when a new item is focused.
      onFocus: _propTypes2.default.func,

      // The depth to which we should automatically expand new items.
      autoExpandDepth: _propTypes2.default.number,
      // Should auto expand all new items or just the new items under the first
      // root item.
      autoExpandAll: _propTypes2.default.bool,

      // 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: _propTypes2.default.string,
      // Accessibility label for a tree widget.
      label: _propTypes2.default.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: _propTypes2.default.func,
      onCollapse: _propTypes2.default.func,
      isExpandable: _propTypes2.default.func,
      // Additional classes to add to the root element.
      className: _propTypes2.default.string,
      // style object to be applied to the root element.
      style: _propTypes2.default.object
    };
  }

  static get defaultProps() {
    return {
      autoExpandDepth: AUTO_EXPAND_DEPTH,
      autoExpandAll: true
    };
  }

  constructor(props) {
    super(props);

    this.state = {
      seen: new Set()
    };

    this._onExpand = oncePerAnimationFrame(this._onExpand).bind(this);
    this._onCollapse = oncePerAnimationFrame(this._onCollapse).bind(this);
    this._focusPrevNode = oncePerAnimationFrame(this._focusPrevNode).bind(this);
    this._focusNextNode = oncePerAnimationFrame(this._focusNextNode).bind(this);
    this._focusParentNode = oncePerAnimationFrame(this._focusParentNode).bind(this);

    this._autoExpand = this._autoExpand.bind(this);
    this._preventArrowKeyScrolling = this._preventArrowKeyScrolling.bind(this);
    this._dfs = this._dfs.bind(this);
    this._dfsFromRoots = this._dfsFromRoots.bind(this);
    this._focus = this._focus.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);
    }
  }

  componentWillReceiveProps(nextProps) {
    this._autoExpand();
  }

  componentDidUpdate(prevProps, prevState) {
    if (prevProps.focused !== this.props.focused) {
      this._scrollNodeIntoView(this.props.focused);
    }
  }

  _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;
    if (this.props.autoExpandAll) {
      for (let i = 0; i < length; i++) {
        autoExpand(roots[i], 0);
      }
    } else if (length != 0) {
      autoExpand(roots[0], 0);
    }
  }

  _preventArrowKeyScrolling(e) {
    switch (e.key) {
      case "ArrowUp":
      case "ArrowDown":
      case "ArrowLeft":
      case "ArrowRight":
        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) {
    this._scrollNodeIntoView(item, options);
    if (this.props.onFocus) {
      this.props.onFocus(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;
      const element = document.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 isVisible = !scrolledParent || top >= 0 && bottom <= scrolledParent.clientHeight;

        if (!isVisible) {
          let scrollToTop = !options.alignTo && top < 0 || options.alignTo === "top";
          element.scrollIntoView(scrollToTop);
        }
      }
    }
  }

  /**
   * Sets the state to have no focused item.
   */
  _onBlur() {
    this._focus(undefined);
  }

  /**
   * Handles key down events in the tree's container.
   *
   * @param {Event} e
   */
  _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();
        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();
        }
    }
  }

  /**
   * 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;
    }

    const traversal = this._dfsFromRoots();
    const length = traversal.length;
    let parentIndex = 0;
    for (; parentIndex < length; parentIndex++) {
      if (traversal[parentIndex].item === parent) {
        break;
      }
    }

    this._focus(parent, { alignTo: "top" });
  }

  _nodeIsExpandable(item) {
    return this.props.isExpandable ? this.props.isExpandable(item) : !!this.props.getChildren(item).length;
  }

  render() {
    const traversal = this._dfsFromRoots();
    const {
      focused
    } = this.props;

    const nodes = traversal.map((v, i) => {
      const { item, depth } = traversal[i];
      const key = this.props.getKey(item, i);
      return TreeNodeFactory({
        key,
        id: key,
        index: i,
        item,
        depth,
        renderItem: this.props.renderItem,
        focused: focused === item,
        expanded: this.props.isExpanded(item),
        isExpandable: this._nodeIsExpandable(item),
        onExpand: this._onExpand,
        onCollapse: this._onCollapse,
        onClick: e => {
          this._focus(item);
          if (this.props.isExpanded(item)) {
            this.props.onCollapse(item);
          } else {
            this.props.onExpand(item, e.altKey);
          }
        }
      });
    });

    const style = Object.assign({}, this.props.style || {}, {
      padding: 0,
      margin: 0
    });

    return _reactDomFactories2.default.div({
      className: `tree ${this.props.className ? this.props.className : ""}`,
      ref: el => {
        this.treeRef = el;
      },
      role: "tree",
      tabIndex: "0",
      onKeyDown: this._onKeyDown,
      onKeyPress: this._preventArrowKeyScrolling,
      onKeyUp: this._preventArrowKeyScrolling,
      onFocus: ({ nativeEvent }) => {
        if (focused || !nativeEvent || !this.treeRef) {
          return;
        }

        let { 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 && !this.treeRef.contains(explicitOriginalTarget)) {
          this._focus(traversal[0].item);
        }
      },
      onBlur: this._onBlur,
      onClick: () => {
        // Focus should always remain on the tree container itself.
        this.treeRef.focus();
      },
      "aria-label": this.props.label,
      "aria-labelledby": this.props.labelledby,
      "aria-activedescendant": focused && this.props.getKey(focused),
      style
    }, nodes);
  }
}

exports.default = Tree;

/***/ }),
/* 51 */
/***/ (function(module, exports) {

module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.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 viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M8 13.4c-.5 0-.9-.2-1.2-.6L.4 5.2C0 4.7-.1 4.3.2 3.7S1 3 1.6 3h12.8c.6 0 1.2.1 1.4.7.3.6.2 1.1-.2 1.6l-6.4 7.6c-.3.4-.7.5-1.2.5z\"></path></svg>"

/***/ }),
/* 52 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 53 */
/***/ (function(module, exports) {

// removed by extract-text-webpack-plugin

/***/ }),
/* 54 */
/***/ (function(module, exports, __webpack_require__) {

var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*!
  Copyright (c) 2016 Jed Watson.
  Licensed under the MIT License (MIT), see
  http://jedwatson.github.io/classnames
*/
/* global define */

(function () {
	'use strict';

	var hasOwn = {}.hasOwnProperty;

	function classNames () {
		var classes = [];

		for (var i = 0; i < arguments.length; i++) {
			var arg = arguments[i];
			if (!arg) continue;

			var argType = typeof arg;

			if (argType === 'string' || argType === 'number') {
				classes.push(arg);
			} else if (Array.isArray(arg)) {
				classes.push(classNames.apply(null, arg));
			} else if (argType === 'object') {
				for (var key in arg) {
					if (hasOwn.call(arg, key) && arg[key]) {
						classes.push(key);
					}
				}
			}
		}

		return classes.join(' ');
	}

	if (typeof module !== 'undefined' && module.exports) {
		module.exports = classNames;
	} else if (true) {
		// register as 'classnames', consistent with npm package name
		!(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = function () {
			return classNames;
		}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
	} else {
		window.classNames = classNames;
	}
}());


/***/ }),
/* 55 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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,
  getPrototype,
  enumSymbols
} = __webpack_require__(18);

const {
  getClosestGripNode,
  getClosestNonBucketNode,
  getValue,
  nodeHasAccessors,
  nodeHasAllEntriesInPreview,
  nodeHasProperties,
  nodeIsBucket,
  nodeIsDefaultProperties,
  nodeIsEntries,
  nodeIsMapEntry,
  nodeIsPrimitive,
  nodeIsProxy,
  nodeNeedsNumericalBuckets
} = __webpack_require__(19);

function loadItemProperties(item, createObjectClient, loadedProperties) {
  const [start, end] = item.meta ? [item.meta.startIndex, item.meta.endIndex] : [];

  let objectClient;
  const getObjectClient = () => {
    if (objectClient) {
      return objectClient;
    }

    const gripItem = getClosestGripNode(item);
    const value = getValue(gripItem);
    return createObjectClient(value);
  };

  let loadingPromises = [];
  if (shouldLoadItemIndexedProperties(item, loadedProperties)) {
    loadingPromises.push(enumIndexedProperties(getObjectClient(), start, end));
  }

  if (shouldLoadItemNonIndexedProperties(item, loadedProperties)) {
    loadingPromises.push(enumNonIndexedProperties(getObjectClient(), start, end));
  }

  if (shouldLoadItemEntries(item, loadedProperties)) {
    loadingPromises.push(enumEntries(getObjectClient(), start, end));
  }

  if (shouldLoadItemPrototype(item, loadedProperties)) {
    loadingPromises.push(getPrototype(getObjectClient()));
  }

  if (shouldLoadItemSymbols(item, loadedProperties)) {
    loadingPromises.push(enumSymbols(getObjectClient(), start, end));
  }

  if (loadingPromises.length === 0) {
    return null;
  }

  return Promise.all(loadingPromises).then(responses => responses.reduce((accumulator, res) => {
    // Let's loop through the responses to build a single response object.
    Object.entries(res).forEach(([k, v]) => {
      if (accumulator.hasOwnProperty(k)) {
        if (Array.isArray(accumulator[k])) {
          accumulator[k].push(...v);
        } else if (typeof accumulator[k] === "object") {
          accumulator[k] = Object.assign({}, accumulator[k], v);
        }
      } else {
        accumulator[k] = v;
      }
    });
    return accumulator;
  }, {}));
}

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)) && !nodeHasAllEntriesInPreview(gripItem) && !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);
}

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) && !nodeIsProxy(item);
}

module.exports = {
  loadItemProperties,
  shouldLoadItemEntries,
  shouldLoadItemIndexedProperties,
  shouldLoadItemNonIndexedProperties,
  shouldLoadItemPrototype,
  shouldLoadItemSymbols
};

/***/ }),
/* 56 */
/***/ (function(module, exports) {

module.exports = __WEBPACK_EXTERNAL_MODULE_56__;

/***/ })
/******/ ]);
});PK
!<�XQQOchrome/devtools/modules/devtools/client/shared/components/splitter/Draggable.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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 ReactDOM = require("devtools/client/shared/vendor/react-dom");

class Draggable extends Component {
  static get propTypes() {
    return {
      onMove: PropTypes.func.isRequired,
      onStart: PropTypes.func,
      onStop: PropTypes.func,
      style: PropTypes.object,
      className: PropTypes.string
    };
  }

  constructor(props) {
    super(props);
    this.startDragging = this.startDragging.bind(this);
    this.onMove = this.onMove.bind(this);
    this.onUp = this.onUp.bind(this);
  }

  startDragging(ev) {
    ev.preventDefault();
    const doc = ReactDOM.findDOMNode(this).ownerDocument;
    doc.addEventListener("mousemove", this.onMove);
    doc.addEventListener("mouseup", this.onUp);
    this.props.onStart && this.props.onStart();
  }

  onMove(ev) {
    ev.preventDefault();
    // Use viewport coordinates so, moving mouse over iframes
    // doesn't mangle (relative) coordinates.
    this.props.onMove(ev.clientX, ev.clientY);
  }

  onUp(ev) {
    ev.preventDefault();
    const doc = ReactDOM.findDOMNode(this).ownerDocument;
    doc.removeEventListener("mousemove", this.onMove);
    doc.removeEventListener("mouseup", this.onUp);
    this.props.onStop && this.props.onStop();
  }

  render() {
    return dom.div({
      role: "presentation",
      style: this.props.style,
      className: this.props.className,
      onMouseDown: this.startDragging
    });
  }
}

module.exports = Draggable;
PK
!<믮[��Ochrome/devtools/modules/devtools/client/shared/components/splitter/SplitBox.css/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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: calc(var(--devtools-splitter-inline-start-width) +
    var(--devtools-splitter-inline-end-width) + 1px);

  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 {
  min-height: calc(var(--devtools-splitter-top-width) +
    var(--devtools-splitter-bottom-width) + 1px);

  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;
}

.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;
}
PK
!<�ǦE��Nchrome/devtools/modules/devtools/client/shared/components/splitter/SplitBox.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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 ReactDOM = require("devtools/client/shared/vendor/react-dom");
const Draggable = createFactory(require("devtools/client/shared/components/splitter/Draggable"));

/**
 * 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.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,
      // 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,
      // 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,
    };
  }

  static get defaultProps() {
    return {
      splitterSize: 5,
      vert: true,
      endPanelControl: false
    };
  }

  constructor(props) {
    super(props);

    /**
     * The state stores the current orientation (vertical or horizontal)
     * and the current size (width/height). All these values can change
     * during the component's life time.
     */
    this.state = {
      vert: props.vert,
      splitterSize: props.splitterSize,
      width: props.initialWidth || props.initialSize,
      height: props.initialHeight || props.initialSize
    };

    this.onStartMove = this.onStartMove.bind(this);
    this.onStopMove = this.onStopMove.bind(this);
    this.onMove = this.onMove.bind(this);
  }

  componentWillReceiveProps(nextProps) {
    let { splitterSize, vert } = nextProps;

    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.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.endPanelControl != this.props.endPanelControl ||
      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 splitBox = ReactDOM.findDOMNode(this);
    const doc = splitBox.ownerDocument;
    let defaultCursor = doc.documentElement.style.cursor;
    doc.documentElement.style.cursor = (this.state.vert ? "ew-resize" : "ns-resize");

    splitBox.classList.add("dragging");

    this.setState({
      defaultCursor: defaultCursor
    });
  }

  onStopMove() {
    const splitBox = ReactDOM.findDOMNode(this);
    const doc = splitBox.ownerDocument;
    doc.documentElement.style.cursor = this.state.defaultCursor;

    splitBox.classList.remove("dragging");
  }

  /**
   * 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 node = ReactDOM.findDOMNode(this);

    let size;
    let { endPanelControl } = this.props;

    if (this.state.vert) {
      // Switch the control flag in case of RTL. Note that RTL
      // has impact on vertical splitter only.
      if (document.dir === "rtl") {
        endPanelControl = !endPanelControl;
      }

      size = endPanelControl ?
        (node.offsetLeft + node.offsetWidth) - x :
        x - node.offsetLeft;

      this.setState({
        width: size
      });
    } else {
      size = endPanelControl ?
        (node.offsetTop + node.offsetHeight) - y :
        y - node.offsetTop;

      this.setState({
        height: size
      });
    }
  }

  // Rendering

  render() {
    const { splitterSize, vert } = this.state;
    const { startPanel, endPanel, endPanelControl, minSize, maxSize } = this.props;

    let style = Object.assign({}, 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
    let splitterStyle = {
      flex: "0 0 " + splitterSize + "px"
    };

    return (
      dom.div({
        className: classNames.join(" "),
        style: style },
        startPanel ?
          dom.div({
            className: endPanelControl ? "uncontrolled" : "controlled",
            style: leftPanelStyle,
            role: "presentation",
            ref: div => {
              this.startPanelContainer = div;
            }},
            startPanel
          ) : null,
        splitterSize > 0 ?
          Draggable({
            className: "splitter",
            style: splitterStyle,
            onStart: this.onStartMove,
            onStop: this.onStopMove,
            onMove: this.onMove
          }) : null,
        endPanel ?
          dom.div({
            className: endPanelControl ? "controlled" : "uncontrolled",
            style: rightPanelStyle,
            role: "presentation",
            ref: div => {
              this.endPanelContainer = div;
            }},
            endPanel
          ) : null
      )
    );
  }
}

module.exports = SplitBox;
PK
!<�8CgLLIchrome/devtools/modules/devtools/client/shared/components/tabs/TabBar.css/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* Hides the tab strip in the TabBar */
div[hidetabs=true] .tabs .tabs-navigation {
  display: none;
}

.tabs .tabs-navigation {
  line-height: 15px;
}

.tabs .tabs-navigation {
  height: 24px;
}

.tabs .tabs-menu-item:first-child {
  border-inline-start-width: 0;
}

.tabs .tabs-navigation .tabs-menu-item:focus {
  outline: var(--theme-focus-outline);
  outline-offset: -2px;
}

.tabs .tabs-menu-item.is-active {
  height: 23px;
}

/* Firebug theme is using slightly different height. */
.theme-firebug .tabs .tabs-navigation {
  height: 28px;
}

/* 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;
}

.tabs .tabs-navigation .tabs-menu-item {
  flex-grow: 1;
}

.tabs .tabs-navigation .tabs-menu-item a {
  text-align: center;
}

/* Firebug theme doesn't stretch the tabs. */
.theme-firebug .tabs .tabs-navigation .tabs-menu-item {
  flex-grow: 0;
}

PK
!<)$�|""Hchrome/devtools/modules/devtools/client/shared/components/tabs/TabBar.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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/. */

/* eslint-env browser */

"use strict";

const { Component, createFactory } = 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 Tabs = createFactory(require("devtools/client/shared/components/tabs/Tabs").Tabs);

const Menu = require("devtools/client/framework/menu");
const MenuItem = require("devtools/client/framework/menu-item");

// 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,
      activeTabId: PropTypes.string,
      renderOnlySelected: PropTypes.bool,
    };
  }

  static get defaultProps() {
    return {
      menuDocument: window.parent.document,
      showAllTabsMenu: false,
    };
  }

  constructor(props, context) {
    super(props, context);
    let { activeTabId, children = [] } = props;
    let tabs = this.createTabs(children);
    let activeTab = tabs.findIndex((tab, index) => tab.id === activeTabId);

    this.state = {
      activeTab: activeTab === -1 ? 0 : activeTab,
      tabs,
    };

    this.createTabs = this.createTabs.bind(this);
    this.addTab = this.addTab.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);
  }

  componentWillReceiveProps(nextProps) {
    let { activeTabId, children = [] } = nextProps;
    let tabs = this.createTabs(children);
    let 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) {
    let tabs = this.state.tabs.slice();

    if (index >= 0) {
      tabs.splice(index, 0, {id, title, panel, url});
    } else {
      tabs.push({id, title, panel, url});
    }

    let newState = Object.assign({}, this.state, {
      tabs: tabs,
    });

    if (selected) {
      newState.activeTab = index >= 0 ? index : tabs.length - 1;
    }

    this.setState(newState, () => {
      if (this.props.onSelect && selected) {
        this.props.onSelect(id);
      }
    });
  }

  toggleTab(tabId, isVisible) {
    let index = this.getTabIndex(tabId);
    if (index < 0) {
      return;
    }

    let tabs = this.state.tabs.slice();
    tabs[index] = Object.assign({}, tabs[index], {
      isVisible: isVisible
    });

    this.setState(Object.assign({}, this.state, {
      tabs: tabs,
    }));
  }

  removeTab(tabId) {
    let index = this.getTabIndex(tabId);
    if (index < 0) {
      return;
    }

    let 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 > 0 && this.props.onSelect) {
        this.props.onSelect(this.getTabId(activeTab));
      }
    });
  }

  select(tabId) {
    let index = this.getTabIndex(tabId);
    if (index < 0) {
      return;
    }

    let newState = Object.assign({}, this.state, {
      activeTab: index,
    });

    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) {
    let menu = new Menu();
    let 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.
    // XXX Missing menu API for specifying target (anchor)
    // and relative position to it. See also:
    // https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL/Method/openPopup
    // https://bugzilla.mozilla.org/show_bug.cgi?id=1274551
    let rect = target.getBoundingClientRect();
    let screenX = target.ownerDocument.defaultView.mozInnerScreenX;
    let screenY = target.ownerDocument.defaultView.mozInnerScreenY;
    menu.popup(rect.left + screenX, rect.bottom + screenY,
      { doc: this.props.menuDocument });

    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() {
    let tabs = this.state.tabs.map((tab) => this.renderTab(tab));

    return (
      div({className: "devtools-sidebar-tabs"},
        Tabs({
          onAllTabsMenuClick: this.onAllTabsMenuClick,
          renderOnlySelected: this.props.renderOnlySelected,
          showAllTabsMenu: this.props.showAllTabsMenu,
          tabActive: this.state.activeTab,
          onAfterChange: this.onTabChanged,
        },
          tabs
        )
      )
    );
  }
}

module.exports = Tabbar;
PK
!<pz���Gchrome/devtools/modules/devtools/client/shared/components/tabs/Tabs.css/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 {
  height: 100%;
  background: var(--theme-sidebar-background);
  display: flex;
  flex-direction: column;
}

.tabs .tabs-menu {
  list-style: none;
  padding: 0;
  margin: 0;
}

.tabs .tabs-menu-item {
  display: inline-block;
  position: relative;
}

.tabs .tabs-menu-item a {
  display: flex;
  justify-content: center;
  padding: 4px 8px;
  border: 1px solid transparent;
  font-size: 12px;
  text-decoration: none;
  white-space: nowrap;
}

.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 * {
  -moz-user-select: none !important;
}

.tabs .tabs-menu-item a {
  cursor: default;
  -moz-user-select: none;
}

/* 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;
}

.tabs .tabs-navigation,
.tabs .tabs-navigation {
  position: relative;
  border-bottom: 1px solid var(--theme-splitter-color);
  background: var(--theme-tab-toolbar-background);
}

.theme-dark .tabs .tabs-menu-item,
.theme-light .tabs .tabs-menu-item {
  margin: 0;
  padding: 0;
  color: var(--theme-toolbar-color);
}

.theme-dark .tabs .tabs-menu-item.is-active,
.theme-light .tabs .tabs-menu-item.is-active {
  color: var(--theme-toolbar-selected-color);
}

.theme-dark .tabs .tabs-menu-item:last-child,
.theme-light:not(.theme-firebug) .tabs .tabs-menu-item:last-child {
  border-inline-end-width: 1px;
}

.theme-dark .tabs .tabs-menu-item a,
.theme-light .tabs .tabs-menu-item a {
  padding: 3px 10px;
}

.theme-dark .tabs .tabs-menu-item:hover,
.theme-light .tabs .tabs-menu-item:hover {
  background-color: var(--theme-toolbar-hover);
}

.theme-dark .tabs .tabs-menu-item:hover:active:not(.is-active),
.theme-light .tabs .tabs-menu-item:hover:active:not(.is-active) {
  background-color: var(--theme-toolbar-hover-active);
}

/* Firebug Theme */

.theme-firebug .tabs .tabs-navigation {
  padding-top: 3px;
  padding-left: 3px;
}

.theme-firebug .tabs .tabs-menu {
  margin-bottom: -1px;
}

.theme-firebug .tabs .tabs-menu-item.is-active,
.theme-firebug .tabs .tabs-menu-item.is-active:hover {
  background-color: transparent;
}

.theme-firebug .tabs .tabs-menu-item {
  position: relative;
  border-inline-start-width: 0;
}

.theme-firebug .tabs .tabs-menu-item a {
  font-family: var(--proportional-font-family);
  font-weight: bold;
  color: var(--theme-body-color);
  border-radius: 4px 4px 0 0;
}

.theme-firebug .tabs .tabs-menu-item:hover:not(.is-active) a {
  border: 1px solid var(--theme-splitter-color);
  border-bottom: 1px solid transparent;
  background-color: transparent;
}

.theme-firebug .tabs .tabs-menu-item.is-active a {
  background-color: var(--theme-toolbar-tab-selected-background);
  border: 1px solid var(--theme-splitter-color);
  border-bottom-color: transparent;
  color: var(--theme-body-color);
}

.theme-firebug .tabs .tabs-menu-item:hover:active a {
  background-color: var(--theme-toolbar-hover-active);
}

.theme-firebug .tabs .tabs-menu-item.is-active:hover:active a {
  background-color: var(--theme-selection-background);
  color: var(--theme-selection-color);
}

.theme-firebug .tabs .tabs-menu-item a {
  border: 1px solid transparent;
  padding: 4px 8px;
}
PK
!<�@;��2�2Fchrome/devtools/modules/devtools/client/shared/components/tabs/Tabs.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

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");
  const { findDOMNode } = require("devtools/client/shared/vendor/react-dom");

  /**
   * 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
        ]),
        tabActive: PropTypes.number,
        onMount: PropTypes.func,
        onBeforeChange: PropTypes.func,
        onAfterChange: PropTypes.func,
        children: PropTypes.oneOfType([
          PropTypes.array,
          PropTypes.element
        ]).isRequired,
        showAllTabsMenu: PropTypes.bool,
        onAllTabsMenuClick: 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 {
        tabActive: 0,
        showAllTabsMenu: false,
        renderOnlySelected: false,
      };
    }

    constructor(props) {
      super(props);

      this.state = {
        tabActive: props.tabActive,

        // 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.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() {
      let node = findDOMNode(this);
      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);
      }

      let index = this.state.tabActive;
      if (this.props.onMount) {
        this.props.onMount(index);
      }
    }

    componentWillReceiveProps(nextProps) {
      let { children, tabActive } = nextProps;
      let 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.
          let createdEntry = this.state.created.find(entry => {
            return entry && entry.tabId === panel.props.id;
          });
          let isCreated = !!createdEntry && createdEntry.isCreated;
          let tabId = panel.props.id;

          return {
            isCreated,
            tabId,
          };
        });
      }

      // Check type of 'tabActive' props to see if it's valid (it's 0-based index).
      if (typeof tabActive === "number") {
        // Reset to index 0 if index overflows the range of panel array
        tabActive = (tabActive < panels.length && tabActive >= 0) ?
          tabActive : 0;

        created[tabActive] = Object.assign({}, created[tabActive], {
          isCreated: true,
        });

        this.setState({
          tabActive,
        });
      }

      this.setState({
        created,
      });
    }

    componentWillUnmount() {
      let node = findDOMNode(this);
      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 tabActive = this.state.tabActive;
      let tabCount = this.props.children.length;

      switch (event.code) {
        case "ArrowRight":
          tabActive = Math.min(tabCount - 1, tabActive + 1);
          break;
        case "ArrowLeft":
          tabActive = Math.max(0, tabActive - 1);
          break;
      }

      if (this.state.tabActive != tabActive) {
        this.setActive(tabActive);
      }
    }

    onClickTab(index, event) {
      this.setActive(index);

      if (event) {
        event.preventDefault();
      }
    }

    // API

    setActive(index) {
      let onAfterChange = this.props.onAfterChange;
      let onBeforeChange = this.props.onBeforeChange;

      if (onBeforeChange) {
        let cancel = onBeforeChange(index);
        if (cancel) {
          return;
        }
      }

      let created = [...this.state.created];
      created[index] = Object.assign({}, created[index], {
        isCreated: true,
      });

      let newState = Object.assign({}, this.state, {
        created,
        tabActive: index,
      });

      this.setState(newState, () => {
        // Properly set focus on selected tab.
        let node = findDOMNode(this);
        let selectedTab = node.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];
      }

      let tabs = this.props.children
        .map((tab) => typeof tab === "function" ? tab() : tab)
        .filter((tab) => tab)
        .map((tab, index) => {
          let {
            id,
            className: tabClassName,
            title,
            badge,
            showBadge,
          } = tab.props;

          let ref = "tab-menu-" + index;
          let isTabSelected = this.state.tabActive === index;

          let 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),
              },
                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).
      let allTabsMenu = this.state.overflow ? (
        dom.div({
          className: "all-tabs-menu",
          onClick: this.props.onAllTabsMenuClick,
        })
      ) : null;

      return (
        dom.nav({className: "tabs-navigation"},
          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];
      }

      let selectedIndex = this.state.tabActive;

      let panels = children
        .map((tab) => typeof tab === "function" ? tab() : tab)
        .filter((tab) => tab)
        .map((tab, index) => {
          let selected = selectedIndex === index;
          if (renderOnlySelected && !selected) {
            return null;
          }

          let id = tab.props.id;
          let 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.
          let 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);
          }
          let panel = tab.panel || tab;

          return (
            dom.div({
              id: id ? id + "-panel" : "panel-" + index,
              key: id,
              style: 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.className].join(" ") },
          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() {
      return dom.div({className: "tab-panel"},
        this.props.children
      );
    }
  }

  // Exports from this module
  exports.TabPanel = Panel;
  exports.Tabs = Tabs;
});
PK
!<�s�
qqKchrome/devtools/modules/devtools/client/shared/components/tree/LabelCell.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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) {
  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,
        member: PropTypes.object.isRequired
      };
    }

    render() {
      let id = this.props.id;
      let member = this.props.member;
      let level = member.level || 0;

      // Compute indentation dynamically. The deeper the item is
      // inside the hierarchy, the bigger is the left padding.
      let rowStyle = {
        "paddingInlineStart": (level * 16) + "px",
      };

      let 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",
          key: "default",
          style: rowStyle,
          role: "presentation"},
          dom.span({
            className: iconClassList.join(" "),
            role: "presentation"
          }),
          dom.span({
            className: "treeLabel " + member.type + "Label",
            "aria-labelledby": id,
            "data-level": level
          }, member.name)
        )
      );
    }
  }

  // Exports from this module
  module.exports = LabelCell;
});
PK
!<����Pchrome/devtools/modules/devtools/client/shared/components/tree/ObjectProvider.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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) {
  /**
   * 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.
   */
  let ObjectProvider = {
    getChildren: function (object) {
      let children = [];

      if (object instanceof ObjectProperty) {
        object = object.value;
      }

      if (!object) {
        return [];
      }

      if (typeof (object) == "string") {
        return [];
      }

      for (let prop in object) {
        try {
          children.push(new ObjectProperty(prop, object[prop]));
        } catch (e) {
          console.error(e);
        }
      }
      return children;
    },

    hasChildren: function (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 > 0;
    },

    getLabel: function (object) {
      return (object instanceof ObjectProperty) ?
        object.name : null;
    },

    getValue: function (object) {
      return (object instanceof ObjectProperty) ?
        object.value : null;
    },

    getKey: function (object) {
      return (object instanceof ObjectProperty) ?
        object.name : null;
    },

    getType: function (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;
});
PK
!<!:3�Jchrome/devtools/modules/devtools/client/shared/components/tree/TreeCell.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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) {
  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) {
      let 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;
      let type = member.type || "";

      // Compute class name list for the <td> element.
      let 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 && decorator.renderValue) {
        renderValue = decorator.renderValue(member.object, id) || renderValue;
      }

      let 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.
  let defaultRenderValue = props => {
    return (
      props.object + ""
    );
  };

  // Exports from this module
  module.exports = TreeCell;
});
PK
!<
�OwwLchrome/devtools/modules/devtools/client/shared/components/tree/TreeHeader.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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) {
  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) {
      let 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() {
      let cells = [];
      let visible = this.props.header;

      // Render the rest of the columns (if any)
      this.props.columns.forEach(col => {
        let 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;
});
PK
!<�t�xxIchrome/devtools/modules/devtools/client/shared/components/tree/TreeRow.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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) {
  const { Component, createFactory } = 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("./TreeCell"));
  const LabelCell = createFactory(require("./LabelCell"));

  // Scroll
  const { scrollIntoViewIfNeeded } = require("devtools/client/shared/scroll");

  /**
   * 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.obSject,
          name: PropTypes.sring,
          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,
        }),
        decorator: PropTypes.object,
        renderCell: PropTypes.object,
        renderLabelCell: PropTypes.object,
        columns: PropTypes.array.isRequired,
        id: PropTypes.string.isRequired,
        provider: PropTypes.object.isRequired,
        onClick: PropTypes.func.isRequired,
        onMouseOver: PropTypes.func,
        onMouseOut: PropTypes.func
      };
    }

    constructor(props) {
      super(props);
      this.getRowClass = this.getRowClass.bind(this);
    }

    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) {
        let 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) {
      let props = ["name", "open", "value", "loading", "selected", "hasChildren"];
      for (let p in props) {
        if (nextProps.member[props[p]] != this.props.member[props[p]]) {
          return true;
        }
      }

      return false;
    }

    componentDidUpdate() {
      if (this.props.member.selected) {
        let row = findDOMNode(this);
        // Because this is called asynchronously, context window might be
        // already gone.
        if (row.ownerDocument.defaultView) {
          scrollIntoViewIfNeeded(row);
        }
      }
    }

    getRowClass(object) {
      let 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() {
      let member = this.props.member;
      let decorator = this.props.decorator;
      let props = {
        id: this.props.id,
        role: "treeitem",
        "aria-level": member.level,
        "aria-selected": !!member.selected,
        onClick: this.props.onClick,
        onMouseOver: this.props.onMouseOver,
        onMouseOut: this.props.onMouseOut
      };

      // Compute class name list for the <tr> element.
      let classNames = this.getRowClass(member.object) || [];
      classNames.push("treeRow");
      classNames.push(member.type + "Row");

      if (member.hasChildren) {
        classNames.push("hasChildren");
        props["aria-expanded"] = false;
      }

      if (member.open) {
        classNames.push("opened");
        props["aria-expanded"] = true;
      }

      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.
      let cells = [];

      // Get components for rendering cells.
      let renderCell = this.props.renderCell || RenderCell;
      let renderLabelCell = this.props.renderLabelCell || RenderLabelCell;
      if (decorator && decorator.renderLabelCell) {
        renderLabelCell = decorator.renderLabelCell(member.object) ||
          renderLabelCell;
      }

      // Render a cell for every column.
      this.props.columns.forEach(col => {
        let cellProps = Object.assign({}, this.props, {
          key: col.id,
          id: col.id,
          value: this.props.provider.getValue(member.object, col.id)
        });

        if (decorator && decorator.renderCell) {
          renderCell = decorator.renderCell(member.object, col.id);
        }

        let 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

  let RenderCell = props => {
    return TreeCell(props);
  };

  let RenderLabelCell = props => {
    return LabelCell(props);
  };

  // Exports from this module
  module.exports = TreeRow;
});
PK
!<��-p��Kchrome/devtools/modules/devtools/client/shared/components/tree/TreeView.css/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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('resource://devtools/client/shared/components/reps/reps.css');

/******************************************************************************/
/* TreeView Colors */

:root {
  --tree-link-color: blue;
  --tree-header-background: #C8D2DC;
  --tree-header-sorted-background: #AAC3DC;
}

/******************************************************************************/
/* TreeView Table*/

.treeTable .treeLabelCell {
  padding: 2px 0;
  vertical-align: top;
  white-space: nowrap;
}

.treeTable .treeLabelCell::after {
  content: ":";
  color: var(--object-color);
}

.treeTable .treeValueCell {
  padding: 2px 0;
  padding-inline-start: 5px;
  overflow: hidden;
}

.treeTable .treeValueCell.inputEnabled {
  padding-top: 0;
  padding-bottom: 0;
}

.treeTable .treeValueCell input {
  width: 100%;
  background: none;
  border: none;
  color: inherit;
  margin-inline-end: 2px;
}

.treeTable .treeValueCell input:focus {
  outline: none;
  box-shadow: var(--theme-focus-box-shadow-textbox);
  transition: all 0.2s ease-in-out;
}

.treeTable .treeLabelCell {
  cursor: default;
  overflow: hidden;
  padding-inline-start: 4px;
  white-space: nowrap;
  unicode-bidi: plaintext;
}

/* 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;
}

.treeTable .treeRow.selected {
  background-color: var(--theme-selection-background);
}

.treeTable .treeRow.selected *,
.treeTable .treeRow.selected .treeLabelCell::after {
  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 {
  -moz-user-select: none;
  color: var(--theme-highlight-blue);
  cursor: pointer;
  margin: 0 5px;
}

.treeTable .treeValueCell .learn-more-link:hover {
  text-decoration: underline;
}

/******************************************************************************/
/* Toggle Icon */

.treeTable .treeRow .treeIcon {
  height: 14px;
  width: 14px;
  font-size: 10px; /* Set the size of loading spinner */
  display: inline-block;
  vertical-align: bottom;
  margin-inline-start: 3px;
  padding-top: 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;
  -moz-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: 2px 0;
  padding-inline-start: 10px;
  padding-inline-end: 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/firebug/arrow-down.svg) no-repeat calc(100% - 4px);
}

.treeTable .treeHeaderSorted.sortedAscending > .treeHeaderCellBox {
  background-image: url(chrome://devtools/skin/images/firebug/arrow-up.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%);
}

/******************************************************************************/
/* Themes */

/* :not(.selected) is used because row selection styles should have
   more precedence than row hovering. */
.theme-light .treeTable .treeRow:not(.selected):hover,
.theme-dark .treeTable .treeRow:not(.selected):hover {
  background-color: var(--theme-selection-background-hover) !important;
}

.theme-firebug .treeTable .treeRow:not(.selected):hover {
  background-color: var(--theme-body-background);
}

.theme-light .treeTable,
.theme-dark .treeTable {
  color: var(--theme-highlight-blue);
}

.theme-firebug .treeTable {
  color: var(--theme-body-color);
}

.theme-firebug .treeTable .treeRow.hasChildren:not(.selected) > .treeLabelCell > .treeLabel:hover {
  color: var(--tree-link-color);
}
PK
!<�N
YDYDJchrome/devtools/modules/devtools/client/shared/components/tree/TreeView.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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) {
  const { cloneElement, Component, createFactory } =
    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("./ObjectProvider");
  const TreeRow = createFactory(require("./TreeRow"));
  const TreeHeader = createFactory(require("./TreeHeader"));

  const SUPPORTED_KEYS = [
    "ArrowUp",
    "ArrowDown",
    "ArrowLeft",
    "ArrowRight",
    "End",
    "Home"
  ];

  const defaultProps = {
    object: null,
    renderRow: null,
    provider: ObjectProvider,
    expandedNodes: new Set(),
    selected: 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);
   *   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,
          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,
        // Custom filtering callback
        onFilter: PropTypes.func,
        // Custom sorting callback
        onSort: PropTypes.func,
        // Custom row click callback
        onClickRow: 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;
    }

    constructor(props) {
      super(props);

      this.state = {
        expandedNodes: props.expandedNodes,
        columns: ensureDefaultColumn(props.columns),
        selected: props.selected,
        lastSelectedIndex: 0
      };

      this.toggle = this.toggle.bind(this);
      this.isExpanded = this.isExpanded.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.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);
    }

    componentWillReceiveProps(nextProps) {
      let { expandedNodes, selected } = nextProps;
      let state = {
        expandedNodes,
        lastSelectedIndex: this.getSelectedRowIndex()
      };

      if (selected) {
        state.selected = selected;
      }

      this.setState(Object.assign({}, this.state, state));
    }

    componentDidUpdate() {
      let selected = this.getSelectedRow();
      if (!selected && this.rows.length > 0) {
        this.selectRow(this.rows[
          Math.min(this.state.lastSelectedIndex, this.rows.length - 1)]);
      }
    }

    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 } = {}) {
      let expandedNodes = new Set();
      let queue = [{
        object: rootObj,
        level: 1,
        path: ""
      }];
      while (queue.length) {
        let {object, level, path} = queue.shift();
        if (Object(object) !== object) {
          continue;
        }
        let keys = Object.keys(object);
        if (expandedNodes.size + keys.length > maxNodes) {
          // Avoid having children half expanded.
          break;
        }
        for (let key of keys) {
          let nodePath = TreeView.subPath(path, key);
          expandedNodes.add(nodePath);
          if (level < maxLevel) {
            queue.push({
              object: object[key],
              level: level + 1,
              path: nodePath
            });
          }
        }
      }
      return expandedNodes;
    }

    // Node expand/collapse

    toggle(nodePath) {
      let 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

    onKeyDown(event) {
      if (!SUPPORTED_KEYS.includes(event.key)) {
        return;
      }

      let row = this.getSelectedRow();
      if (!row) {
        return;
      }

      let index = this.rows.indexOf(row);
      switch (event.key) {
        case "ArrowRight":
          let { hasChildren, open } = row.props.member;
          if (hasChildren && !open) {
            this.toggle(this.state.selected);
          }
          break;
        case "ArrowLeft":
          if (row && row.props.member.open) {
            this.toggle(this.state.selected);
          }
          break;
        case "ArrowDown":
          let nextRow = this.rows[index + 1];
          if (nextRow) {
            this.selectRow(nextRow);
          }
          break;
        case "ArrowUp":
          let previousRow = this.rows[index - 1];
          if (previousRow) {
            this.selectRow(previousRow);
          }
          break;
        case "Home":
          let firstRow = this.rows[0];
          if (firstRow) {
            this.selectRow(firstRow);
          }
          break;
        case "End":
          let lastRow = this.rows[this.rows.length - 1];
          if (lastRow) {
            this.selectRow(lastRow);
          }
          break;
      }

      // Focus should always remain on the tree container itself.
      this.tree.focus();
      event.preventDefault();
    }

    onClickRow(nodePath, event) {
      let onClickRow = this.props.onClickRow;

      if (onClickRow) {
        onClickRow.call(this, nodePath, event);
        return;
      }

      event.stopPropagation();
      let cell = event.target.closest("td");
      if (cell && cell.classList.contains("treeLabelCell")) {
        this.toggle(nodePath);
      }
      this.selectRow(event.currentTarget);
    }

    getSelectedRow() {
      if (!this.state.selected || this.rows.length === 0) {
        return null;
      }
      return this.rows.find(row => this.isSelected(row.props.member.path));
    }

    getSelectedRowIndex() {
      let row = this.getSelectedRow();
      if (!row) {
        // If selected row is not found, return index of the first row.
        return 0;
      }

      return this.rows.indexOf(row);
    }

    selectRow(row) {
      row = findDOMNode(row);
      if (this.state.selected === row.id) {
        return;
      }

      this.setState(Object.assign({}, this.state, {
        selected: row.id
      }));
      row.scrollIntoView({block: "nearest"});
    }

    isSelected(nodePath) {
      return nodePath === this.state.selected;
    }

    // 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) {
      let onFilter = this.props.onFilter;
      return onFilter ? onFilter(object) : true;
    }

    onSort(parent, children) {
      let 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 [];
      }

      let { 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 => {
        let key = provider.getKey(child);
        let nodePath = TreeView.subPath(path, key);
        let 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.
        let 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: type,
          // Class attribute computed from the type.
          rowClass: "treeRow-" + type,
          // Level of the child within the hierarchy (top == 0)
          level: level,
          // True if this node has children.
          hasChildren: hasChildren,
          // Value associated with this node (as provided by the data provider)
          value: 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)
        };
      });
    }

    /**
     * Render tree rows/nodes.
     */
    renderRows(parent, level = 0, path = "") {
      let rows = [];
      let 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.
      let members = this.getMembers(parent, level, path);
      if (!Array.isArray(members)) {
        return members;
      }

      members.forEach(member => {
        if (decorator && decorator.renderRow) {
          renderRow = decorator.renderRow(member.object) || renderRow;
        }

        let props = Object.assign({}, this.props, {
          key: member.path,
          member: member,
          columns: this.state.columns,
          id: member.path,
          ref: row => row && this.rows.push(row),
          onClick: this.onClickRow.bind(this, member.path)
        });

        // Render single row.
        rows.push(renderRow(props));

        // If a child node is expanded render its rows too.
        if (member.hasChildren && member.open) {
          let 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)) {
            let lastIndex = rows.length - 1;
            props.member.loading = true;
            rows[lastIndex] = cloneElement(rows[lastIndex], props);
          } else {
            rows = rows.concat(childRows);
          }
        }
      });

      return rows;
    }

    render() {
      let root = this.props.object;
      let classNames = ["treeTable"];
      this.rows = [];

      // Use custom class name from props.
      let className = this.props.className;
      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 = [];
      }

      let props = Object.assign({}, this.props, {
        columns: this.state.columns
      });

      return (
        dom.table({
          className: classNames.join(" "),
          role: "tree",
          ref: tree => {
            this.tree = tree;
          },
          tabIndex: 0,
          onKeyDown: this.onKeyDown,
          "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 = [];
    }

    let 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;
});
PK
!<Gl��b$b$;chrome/devtools/modules/devtools/client/shared/css-angle.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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, "CSS_ANGLEUNIT",
  "devtools/shared/css/properties-db", true);

const SPECIALVALUES = new Set([
  "initial",
  "inherit",
  "unset"
]);

const {getCSSLexer} = require("devtools/shared/css/lexer");

/**
 * 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: CssAngle,
  classifyAngle: 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() {
    let 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() {
    let invalidOrSpecialValue = this._getInvalidOrSpecialValue();
    if (invalidOrSpecialValue !== false) {
      return invalidOrSpecialValue;
    }

    let 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() {
    let invalidOrSpecialValue = this._getInvalidOrSpecialValue();
    if (invalidOrSpecialValue !== false) {
      return invalidOrSpecialValue;
    }

    let 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() {
    let invalidOrSpecialValue = this._getInvalidOrSpecialValue();
    if (invalidOrSpecialValue !== false) {
      return invalidOrSpecialValue;
    }

    let 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() {
    let invalidOrSpecialValue = this._getInvalidOrSpecialValue();
    if (invalidOrSpecialValue !== false) {
      return invalidOrSpecialValue;
    }

    let 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: function () {
    if (this.specialValue) {
      return this.specialValue;
    }
    if (!this.valid) {
      return "";
    }
    return false;
  },

  /**
   * Change angle
   *
   * @param  {String} angle
   *         Any valid angle value + unit string
   */
  newAngle: function (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;

    let reg = new RegExp(
      `(${Object.keys(this.ANGLEUNIT).join("|")})$`, "i");
    let unitStartIdx = angle.search(reg);
    this.authoredAngleValue = angle.substring(0, unitStartIdx);
    this.authoredAngleUnit = angle.substring(unitStartIdx, angle.length);

    return this;
  },

  nextAngleUnit: function () {
    // 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);
    let putOnEnd = formats.splice(0, formats.indexOf(this.angleUnit));
    formats = formats.concat(putOnEnd);
    let currentDisplayedValue = this[formats[0]];

    for (let 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: function () {
    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: function () {
    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;
}
PK
!<"�G�P3P36chrome/devtools/modules/devtools/client/shared/curl.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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/. */

/*
 * Copyright (C) 2007, 2008 Apple Inc.  All rights reserved.
 * Copyright (C) 2008, 2009 Anthony Ricaud <[email protected]>
 * 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 Services = require("Services");

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.
   *
   * @return string
   *         A cURL command.
   */
  generateCommand: function (data) {
    let utils = CurlUtils;

    let command = ["curl"];
    let ignoredHeaders = new Set();

    // The cURL command is expected to run on the same platform that Firefox runs
    // (it may be different from the inspected page platform).
    let escapeString = Services.appinfo.OS == "WINNT" ?
                       utils.escapeStringWin : utils.escapeStringPosix;

    // Add URL.
    command.push(escapeString(data.url));

    let postDataText = null;
    let multipartRequest = utils.isMultipartRequest(data);

    // Create post data.
    let postData = [];
    if (utils.isUrlEncodedRequest(data) ||
          ["PUT", "POST", "PATCH"].includes(data.method)) {
      postDataText = data.postDataText;
      postData.push("--data");
      postData.push(escapeString(utils.writePostDataTextParams(postDataText)));
      ignoredHeaders.add("content-length");
    } else if (multipartRequest) {
      postDataText = data.postDataText;
      postData.push("--data-binary");
      let boundary = utils.getMultipartBoundary(data);
      let text = utils.removeBinaryDataFromMultipartText(postDataText, boundary);
      postData.push(escapeString(text));
      ignoredHeaders.add("content-length");
    }

    // Add method.
    // For GET and POST requests this is not necessary as GET is the
    // default. If --data or --binary is added POST is the default.
    if (!(data.method == "GET" || data.method == "POST")) {
      command.push("-X");
      command.push(data.method);
    }

    // Add -I (HEAD)
    // For servers that supports HEAD.
    // This will fetch the header of a document only.
    if (data.method == "HEAD") {
      command.push("-I");
    }

    // Add request headers.
    let headers = data.headers;
    if (multipartRequest) {
      let multipartHeaders = utils.getHeadersFromMultipartText(postDataText);
      headers = headers.concat(multipartHeaders);
    }
    for (let i = 0; i < headers.length; i++) {
      let header = headers[i];
      if (header.name.toLowerCase() === "accept-encoding") {
        command.push("--compressed");
        continue;
      }
      if (ignoredHeaders.has(header.name.toLowerCase())) {
        continue;
      }
      command.push("-H");
      command.push(escapeString(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: function (data) {
    let postDataText = data.postDataText;
    if (!postDataText) {
      return false;
    }

    postDataText = postDataText.toLowerCase();
    if (postDataText.includes("content-type: application/x-www-form-urlencoded")) {
      return true;
    }

    let 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: function (data) {
    let postDataText = data.postDataText;
    if (!postDataText) {
      return false;
    }

    postDataText = postDataText.toLowerCase();
    if (postDataText.includes("content-type: multipart/form-data")) {
      return true;
    }

    let contentType = this.findHeader(data.headers, "content-type");

    return (contentType &&
      contentType.toLowerCase().includes("multipart/form-data;"));
  },

  /**
   * Write out paramters from post data text.
   *
   * @param object postDataText
   *        Post data text.
   * @return string
   *         Post data parameters.
   */
  writePostDataTextParams: function (postDataText) {
    if (!postDataText) {
      return "";
    }
    let 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: function (headers, name) {
    if (!headers) {
      return null;
    }

    name = name.toLowerCase();
    for (let 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: function (data) {
    let boundaryRe = /\bboundary=(-{3,}\w+)/i;

    // Get the boundary string from the Content-Type request header.
    let 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.
    let 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: function (multipartText, boundary) {
    let result = "";
    boundary = "--" + boundary;
    let parts = multipartText.split(boundary);
    for (let 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.
          let headers = part.split("\r\n\r\n")[0];
          result += boundary + "\r\n" + headers + "\r\n\r\n";
        } else {
          result += boundary + "\r\n" + 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: function (multipartText) {
    let headers = [];
    if (!multipartText || multipartText.startsWith("---")) {
      return headers;
    }

    // Get the header section.
    let index = multipartText.indexOf("\r\n\r\n");
    if (index == -1) {
      return headers;
    }

    // Parse the header lines.
    let headersText = multipartText.substring(0, index);
    let headerLines = headersText.split("\r\n");
    let lastHeaderName = null;

    for (let 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;
      }

      let indexOfColon = line.indexOf(":");
      if (indexOfColon == -1) {
        continue;
      }

      let 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: function (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(/[^\x20-\x7E]/g, escapeCharacter) + "'";
    }

    // Use single quote syntax.
    return "'" + str + "'";
  },

  /**
   * Escape util function for Windows systems.
   * Credit: Google DevTools
   */
  escapeStringWin: function (str) {
    /* Replace quote by double quote (but not by \") because it is
       recognized by both cmd.exe and MS Crt arguments parser.

       Replace % by "%" because it could be expanded to an environment
       variable value. So %% becomes "%""%". Even if an env variable ""
       (2 doublequotes) is declared, the cmd.exe will not
       substitute it with its value.

       Replace each backslash with double backslash to make sure
       MS Crt arguments parser won't collapse them.

       Replace new line outside of quotes since cmd.exe doesn't let
       to do it inside.
    */
    return "\"" + str.replace(/"/g, "\"\"")
                     .replace(/%/g, "\"%\"")
                     .replace(/\\/g, "\\\\")
                     .replace(/[\r\n]+/g, "\"^$&\"") + "\"";
  }
};

exports.CurlUtils = CurlUtils;
PK
!<��f
;
;:chrome/devtools/modules/devtools/client/shared/demangle.js/**
 * Exposes a function that demangles function names.
 * Can be found at: https://github.com/kripken/cxx_demangle
 */
var demangle = (function() {
  // In Firefox CommonJS environment, the module boilerplate thinks it's node,
  // but `process` does not exist.
  if (typeof process !== "object" && typeof window !== "object" && typeof require === "function") {
    // null out `require` since no filesystem is necessary in this module, and this
    // way the boilerplate thinks its in a shell.
    require = null;
    // The `print` function only exists in scope when in a node environment,
    // and there doesn't seem to account for when in a shell environment and NOT node.js,
    // so just stub out the print function.
    var print = function(){}
  }

var Module = function(Module) {
  Module = Module || {};

var Module;if(!Module)Module=(typeof Module!=="undefined"?Module:null)||{};var moduleOverrides={};for(var key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}var ENVIRONMENT_IS_WEB=typeof window==="object";var ENVIRONMENT_IS_WORKER=typeof importScripts==="function";var ENVIRONMENT_IS_NODE=typeof process==="object"&&typeof require==="function"&&!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_WORKER;var ENVIRONMENT_IS_SHELL=!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_NODE&&!ENVIRONMENT_IS_WORKER;if(ENVIRONMENT_IS_NODE){if(!Module["print"])Module["print"]=function print(x){process["stdout"].write(x+"\n")};if(!Module["printErr"])Module["printErr"]=function printErr(x){process["stderr"].write(x+"\n")};var nodeFS=require("fs");var nodePath=require("path");Module["read"]=function read(filename,binary){filename=nodePath["normalize"](filename);var ret=nodeFS["readFileSync"](filename);if(!ret&&filename!=nodePath["resolve"](filename)){filename=path.join(__dirname,"..","src",filename);ret=nodeFS["readFileSync"](filename)}if(ret&&!binary)ret=ret.toString();return ret};Module["readBinary"]=function readBinary(filename){var ret=Module["read"](filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}assert(ret.buffer);return ret};Module["load"]=function load(f){globalEval(read(f))};if(!Module["thisProgram"]){if(process["argv"].length>1){Module["thisProgram"]=process["argv"][1].replace(/\\/g,"/")}else{Module["thisProgram"]="unknown-program"}}Module["arguments"]=process["argv"].slice(2);if(typeof module!=="undefined"){module["exports"]=Module}process["on"]("uncaughtException",(function(ex){if(!(ex instanceof ExitStatus)){throw ex}}));Module["inspect"]=(function(){return"[Emscripten Module object]"})}else if(ENVIRONMENT_IS_SHELL){if(!Module["print"])Module["print"]=print;if(typeof printErr!="undefined")Module["printErr"]=printErr;if(typeof read!="undefined"){Module["read"]=read}else{Module["read"]=function read(){throw"no read() available (jsc?)"}}Module["readBinary"]=function readBinary(f){if(typeof readbuffer==="function"){return new Uint8Array(readbuffer(f))}var data=read(f,"binary");assert(typeof data==="object");return data};if(typeof scriptArgs!="undefined"){Module["arguments"]=scriptArgs}else if(typeof arguments!="undefined"){Module["arguments"]=arguments}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){Module["read"]=function read(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(typeof arguments!="undefined"){Module["arguments"]=arguments}if(typeof console!=="undefined"){if(!Module["print"])Module["print"]=function print(x){console.log(x)};if(!Module["printErr"])Module["printErr"]=function printErr(x){console.log(x)}}else{var TRY_USE_DUMP=false;if(!Module["print"])Module["print"]=TRY_USE_DUMP&&typeof dump!=="undefined"?(function(x){dump(x)}):(function(x){})}if(ENVIRONMENT_IS_WORKER){Module["load"]=importScripts}if(typeof Module["setWindowTitle"]==="undefined"){Module["setWindowTitle"]=(function(title){document.title=title})}}else{throw"Unknown runtime environment. Where are we?"}function globalEval(x){eval.call(null,x)}if(!Module["load"]&&Module["read"]){Module["load"]=function load(f){globalEval(Module["read"](f))}}if(!Module["print"]){Module["print"]=(function(){})}if(!Module["printErr"]){Module["printErr"]=Module["print"]}if(!Module["arguments"]){Module["arguments"]=[]}if(!Module["thisProgram"]){Module["thisProgram"]="./this.program"}Module.print=Module["print"];Module.printErr=Module["printErr"];Module["preRun"]=[];Module["postRun"]=[];for(var key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}var Runtime={setTempRet0:(function(value){tempRet0=value}),getTempRet0:(function(){return tempRet0}),stackSave:(function(){return STACKTOP}),stackRestore:(function(stackTop){STACKTOP=stackTop}),getNativeTypeSize:(function(type){switch(type){case"i1":case"i8":return 1;case"i16":return 2;case"i32":return 4;case"i64":return 8;case"float":return 4;case"double":return 8;default:{if(type[type.length-1]==="*"){return Runtime.QUANTUM_SIZE}else if(type[0]==="i"){var bits=parseInt(type.substr(1));assert(bits%8===0);return bits/8}else{return 0}}}}),getNativeFieldSize:(function(type){return Math.max(Runtime.getNativeTypeSize(type),Runtime.QUANTUM_SIZE)}),STACK_ALIGN:16,prepVararg:(function(ptr,type){if(type==="double"||type==="i64"){if(ptr&7){assert((ptr&7)===4);ptr+=4}}else{assert((ptr&3)===0)}return ptr}),getAlignSize:(function(type,size,vararg){if(!vararg&&(type=="i64"||type=="double"))return 8;if(!type)return Math.min(size,8);return Math.min(size||(type?Runtime.getNativeFieldSize(type):0),Runtime.QUANTUM_SIZE)}),dynCall:(function(sig,ptr,args){if(args&&args.length){if(!args.splice)args=Array.prototype.slice.call(args);args.splice(0,0,ptr);return Module["dynCall_"+sig].apply(null,args)}else{return Module["dynCall_"+sig].call(null,ptr)}}),functionPointers:[],addFunction:(function(func){for(var i=0;i<Runtime.functionPointers.length;i++){if(!Runtime.functionPointers[i]){Runtime.functionPointers[i]=func;return 2*(1+i)}}throw"Finished up all reserved function pointers. Use a higher value for RESERVED_FUNCTION_POINTERS."}),removeFunction:(function(index){Runtime.functionPointers[(index-2)/2]=null}),warnOnce:(function(text){if(!Runtime.warnOnce.shown)Runtime.warnOnce.shown={};if(!Runtime.warnOnce.shown[text]){Runtime.warnOnce.shown[text]=1;Module.printErr(text)}}),funcWrappers:{},getFuncWrapper:(function(func,sig){assert(sig);if(!Runtime.funcWrappers[sig]){Runtime.funcWrappers[sig]={}}var sigCache=Runtime.funcWrappers[sig];if(!sigCache[func]){sigCache[func]=function dynCall_wrapper(){return Runtime.dynCall(sig,func,arguments)}}return sigCache[func]}),getCompilerSetting:(function(name){throw"You must build with -s RETAIN_COMPILER_SETTINGS=1 for Runtime.getCompilerSetting or emscripten_get_compiler_setting to work"}),stackAlloc:(function(size){var ret=STACKTOP;STACKTOP=STACKTOP+size|0;STACKTOP=STACKTOP+15&-16;return ret}),staticAlloc:(function(size){var ret=STATICTOP;STATICTOP=STATICTOP+size|0;STATICTOP=STATICTOP+15&-16;return ret}),dynamicAlloc:(function(size){var ret=DYNAMICTOP;DYNAMICTOP=DYNAMICTOP+size|0;DYNAMICTOP=DYNAMICTOP+15&-16;if(DYNAMICTOP>=TOTAL_MEMORY){var success=enlargeMemory();if(!success){DYNAMICTOP=ret;return 0}}return ret}),alignMemory:(function(size,quantum){var ret=size=Math.ceil(size/(quantum?quantum:16))*(quantum?quantum:16);return ret}),makeBigInt:(function(low,high,unsigned){var ret=unsigned?+(low>>>0)+ +(high>>>0)*+4294967296:+(low>>>0)+ +(high|0)*+4294967296;return ret}),GLOBAL_BASE:8,QUANTUM_SIZE:4,__dummy__:0};var __THREW__=0;var ABORT=false;var EXITSTATUS=0;var undef=0;var tempValue,tempInt,tempBigInt,tempInt2,tempBigInt2,tempPair,tempBigIntI,tempBigIntR,tempBigIntS,tempBigIntP,tempBigIntD,tempDouble,tempFloat;var tempI64,tempI64b;var tempRet0,tempRet1,tempRet2,tempRet3,tempRet4,tempRet5,tempRet6,tempRet7,tempRet8,tempRet9;function assert(condition,text){if(!condition){abort("Assertion failed: "+text)}}var globalScope=this;function getCFunc(ident){var func=Module["_"+ident];if(!func){try{func=eval("_"+ident)}catch(e){}}assert(func,"Cannot call unknown function "+ident+" (perhaps LLVM optimizations or closure removed it?)");return func}var cwrap,ccall;((function(){var JSfuncs={"stackSave":(function(){Runtime.stackSave()}),"stackRestore":(function(){Runtime.stackRestore()}),"arrayToC":(function(arr){var ret=Runtime.stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}),"stringToC":(function(str){var ret=0;if(str!==null&&str!==undefined&&str!==0){ret=Runtime.stackAlloc((str.length<<2)+1);writeStringToMemory(str,ret)}return ret})};var toC={"string":JSfuncs["stringToC"],"array":JSfuncs["arrayToC"]};ccall=function ccallFunc(ident,returnType,argTypes,args,opts){var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i<args.length;i++){var converter=toC[argTypes[i]];if(converter){if(stack===0)stack=Runtime.stackSave();cArgs[i]=converter(args[i])}else{cArgs[i]=args[i]}}}var ret=func.apply(null,cArgs);if(returnType==="string")ret=Pointer_stringify(ret);if(stack!==0){if(opts&&opts.async){EmterpreterAsync.asyncFinalizers.push((function(){Runtime.stackRestore(stack)}));return}Runtime.stackRestore(stack)}return ret};var sourceRegex=/^function\s*\(([^)]*)\)\s*{\s*([^*]*?)[\s;]*(?:return\s*(.*?)[;\s]*)?}$/;function parseJSFunc(jsfunc){var parsed=jsfunc.toString().match(sourceRegex).slice(1);return{arguments:parsed[0],body:parsed[1],returnValue:parsed[2]}}var JSsource={};for(var fun in JSfuncs){if(JSfuncs.hasOwnProperty(fun)){JSsource[fun]=parseJSFunc(JSfuncs[fun])}}cwrap=function cwrap(ident,returnType,argTypes){argTypes=argTypes||[];var cfunc=getCFunc(ident);var numericArgs=argTypes.every((function(type){return type==="number"}));var numericRet=returnType!=="string";if(numericRet&&numericArgs){return cfunc}var argNames=argTypes.map((function(x,i){return"$"+i}));var funcstr="(function("+argNames.join(",")+") {";var nargs=argTypes.length;if(!numericArgs){funcstr+="var stack = "+JSsource["stackSave"].body+";";for(var i=0;i<nargs;i++){var arg=argNames[i],type=argTypes[i];if(type==="number")continue;var convertCode=JSsource[type+"ToC"];funcstr+="var "+convertCode.arguments+" = "+arg+";";funcstr+=convertCode.body+";";funcstr+=arg+"=("+convertCode.returnValue+");"}}var cfuncname=parseJSFunc((function(){return cfunc})).returnValue;funcstr+="var ret = "+cfuncname+"("+argNames.join(",")+");";if(!numericRet){var strgfy=parseJSFunc((function(){return Pointer_stringify})).returnValue;funcstr+="ret = "+strgfy+"(ret);"}if(!numericArgs){funcstr+=JSsource["stackRestore"].body.replace("()","(stack)")+";"}funcstr+="return ret})";return eval(funcstr)}}))();function setValue(ptr,value,type,noSafe){type=type||"i8";if(type.charAt(type.length-1)==="*")type="i32";switch(type){case"i1":HEAP8[ptr>>0]=value;break;case"i8":HEAP8[ptr>>0]=value;break;case"i16":HEAP16[ptr>>1]=value;break;case"i32":HEAP32[ptr>>2]=value;break;case"i64":tempI64=[value>>>0,(tempDouble=value,+Math_abs(tempDouble)>=+1?tempDouble>+0?(Math_min(+Math_floor(tempDouble/+4294967296),+4294967295)|0)>>>0:~~+Math_ceil((tempDouble- +(~~tempDouble>>>0))/+4294967296)>>>0:0)],HEAP32[ptr>>2]=tempI64[0],HEAP32[ptr+4>>2]=tempI64[1];break;case"float":HEAPF32[ptr>>2]=value;break;case"double":HEAPF64[ptr>>3]=value;break;default:abort("invalid type for setValue: "+type)}}function getValue(ptr,type,noSafe){type=type||"i8";if(type.charAt(type.length-1)==="*")type="i32";switch(type){case"i1":return HEAP8[ptr>>0];case"i8":return HEAP8[ptr>>0];case"i16":return HEAP16[ptr>>1];case"i32":return HEAP32[ptr>>2];case"i64":return HEAP32[ptr>>2];case"float":return HEAPF32[ptr>>2];case"double":return HEAPF64[ptr>>3];default:abort("invalid type for setValue: "+type)}return null}var ALLOC_NORMAL=0;var ALLOC_STACK=1;var ALLOC_STATIC=2;var ALLOC_DYNAMIC=3;var ALLOC_NONE=4;function allocate(slab,types,allocator,ptr){var zeroinit,size;if(typeof slab==="number"){zeroinit=true;size=slab}else{zeroinit=false;size=slab.length}var singleType=typeof types==="string"?types:null;var ret;if(allocator==ALLOC_NONE){ret=ptr}else{ret=[typeof _malloc==="function"?_malloc:null,Runtime.stackAlloc,Runtime.staticAlloc,Runtime.dynamicAlloc][allocator===undefined?ALLOC_STATIC:allocator](Math.max(size,singleType?1:types.length))}if(zeroinit){var ptr=ret,stop;assert((ret&3)==0);stop=ret+(size&~3);for(;ptr<stop;ptr+=4){HEAP32[ptr>>2]=0}stop=ret+size;while(ptr<stop){HEAP8[ptr++>>0]=0}return ret}if(singleType==="i8"){if(slab.subarray||slab.slice){HEAPU8.set(slab,ret)}else{HEAPU8.set(new Uint8Array(slab),ret)}return ret}var i=0,type,typeSize,previousType;while(i<size){var curr=slab[i];if(typeof curr==="function"){curr=Runtime.getFunctionIndex(curr)}type=singleType||types[i];if(type===0){i++;continue}if(type=="i64")type="i32";setValue(ret+i,curr,type);if(previousType!==type){typeSize=Runtime.getNativeTypeSize(type);previousType=type}i+=typeSize}return ret}function getMemory(size){if(!staticSealed)return Runtime.staticAlloc(size);if(typeof _sbrk!=="undefined"&&!_sbrk.called||!runtimeInitialized)return Runtime.dynamicAlloc(size);return _malloc(size)}function Pointer_stringify(ptr,length){if(length===0||!ptr)return"";var hasUtf=0;var t;var i=0;while(1){t=HEAPU8[ptr+i>>0];hasUtf|=t;if(t==0&&!length)break;i++;if(length&&i==length)break}if(!length)length=i;var ret="";if(hasUtf<128){var MAX_CHUNK=1024;var curr;while(length>0){curr=String.fromCharCode.apply(String,HEAPU8.subarray(ptr,ptr+Math.min(length,MAX_CHUNK)));ret=ret?ret+curr:curr;ptr+=MAX_CHUNK;length-=MAX_CHUNK}return ret}return Module["UTF8ToString"](ptr)}Module["Pointer_stringify"]=Pointer_stringify;function AsciiToString(ptr){var str="";while(1){var ch=HEAP8[ptr++>>0];if(!ch)return str;str+=String.fromCharCode(ch)}}function stringToAscii(str,outPtr){return writeAsciiToMemory(str,outPtr,false)}function UTF8ArrayToString(u8Array,idx){var u0,u1,u2,u3,u4,u5;var str="";while(1){u0=u8Array[idx++];if(!u0)return str;if(!(u0&128)){str+=String.fromCharCode(u0);continue}u1=u8Array[idx++]&63;if((u0&224)==192){str+=String.fromCharCode((u0&31)<<6|u1);continue}u2=u8Array[idx++]&63;if((u0&240)==224){u0=(u0&15)<<12|u1<<6|u2}else{u3=u8Array[idx++]&63;if((u0&248)==240){u0=(u0&7)<<18|u1<<12|u2<<6|u3}else{u4=u8Array[idx++]&63;if((u0&252)==248){u0=(u0&3)<<24|u1<<18|u2<<12|u3<<6|u4}else{u5=u8Array[idx++]&63;u0=(u0&1)<<30|u1<<24|u2<<18|u3<<12|u4<<6|u5}}}if(u0<65536){str+=String.fromCharCode(u0)}else{var ch=u0-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}}}function UTF8ToString(ptr){return UTF8ArrayToString(HEAPU8,ptr)}function stringToUTF8Array(str,outU8Array,outIdx,maxBytesToWrite){if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i<str.length;++i){var u=str.charCodeAt(i);if(u>=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127){if(outIdx>=endIdx)break;outU8Array[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;outU8Array[outIdx++]=192|u>>6;outU8Array[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;outU8Array[outIdx++]=224|u>>12;outU8Array[outIdx++]=128|u>>6&63;outU8Array[outIdx++]=128|u&63}else if(u<=2097151){if(outIdx+3>=endIdx)break;outU8Array[outIdx++]=240|u>>18;outU8Array[outIdx++]=128|u>>12&63;outU8Array[outIdx++]=128|u>>6&63;outU8Array[outIdx++]=128|u&63}else if(u<=67108863){if(outIdx+4>=endIdx)break;outU8Array[outIdx++]=248|u>>24;outU8Array[outIdx++]=128|u>>18&63;outU8Array[outIdx++]=128|u>>12&63;outU8Array[outIdx++]=128|u>>6&63;outU8Array[outIdx++]=128|u&63}else{if(outIdx+5>=endIdx)break;outU8Array[outIdx++]=252|u>>30;outU8Array[outIdx++]=128|u>>24&63;outU8Array[outIdx++]=128|u>>18&63;outU8Array[outIdx++]=128|u>>12&63;outU8Array[outIdx++]=128|u>>6&63;outU8Array[outIdx++]=128|u&63}}outU8Array[outIdx]=0;return outIdx-startIdx}function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite)}function lengthBytesUTF8(str){var len=0;for(var i=0;i<str.length;++i){var u=str.charCodeAt(i);if(u>=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127){++len}else if(u<=2047){len+=2}else if(u<=65535){len+=3}else if(u<=2097151){len+=4}else if(u<=67108863){len+=5}else{len+=6}}return len}function UTF16ToString(ptr){var i=0;var str="";while(1){var codeUnit=HEAP16[ptr+i*2>>1];if(codeUnit==0)return str;++i;str+=String.fromCharCode(codeUnit)}}function stringToUTF16(str,outPtr,maxBytesToWrite){if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<2)return 0;maxBytesToWrite-=2;var startPtr=outPtr;var numCharsToWrite=maxBytesToWrite<str.length*2?maxBytesToWrite/2:str.length;for(var i=0;i<numCharsToWrite;++i){var codeUnit=str.charCodeAt(i);HEAP16[outPtr>>1]=codeUnit;outPtr+=2}HEAP16[outPtr>>1]=0;return outPtr-startPtr}function lengthBytesUTF16(str){return str.length*2}function UTF32ToString(ptr){var i=0;var str="";while(1){var utf32=HEAP32[ptr+i*4>>2];if(utf32==0)return str;++i;if(utf32>=65536){var ch=utf32-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}else{str+=String.fromCharCode(utf32)}}}function stringToUTF32(str,outPtr,maxBytesToWrite){if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<4)return 0;var startPtr=outPtr;var endPtr=startPtr+maxBytesToWrite-4;for(var i=0;i<str.length;++i){var codeUnit=str.charCodeAt(i);if(codeUnit>=55296&&codeUnit<=57343){var trailSurrogate=str.charCodeAt(++i);codeUnit=65536+((codeUnit&1023)<<10)|trailSurrogate&1023}HEAP32[outPtr>>2]=codeUnit;outPtr+=4;if(outPtr+4>endPtr)break}HEAP32[outPtr>>2]=0;return outPtr-startPtr}function lengthBytesUTF32(str){var len=0;for(var i=0;i<str.length;++i){var codeUnit=str.charCodeAt(i);if(codeUnit>=55296&&codeUnit<=57343)++i;len+=4}return len}function demangle(func){var hasLibcxxabi=!!Module["___cxa_demangle"];if(hasLibcxxabi){try{var buf=_malloc(func.length);writeStringToMemory(func.substr(1),buf);var status=_malloc(4);var ret=Module["___cxa_demangle"](buf,0,0,status);if(getValue(status,"i32")===0&&ret){return Pointer_stringify(ret)}}catch(e){}finally{if(buf)_free(buf);if(status)_free(status);if(ret)_free(ret)}}var i=3;var basicTypes={"v":"void","b":"bool","c":"char","s":"short","i":"int","l":"long","f":"float","d":"double","w":"wchar_t","a":"signed char","h":"unsigned char","t":"unsigned short","j":"unsigned int","m":"unsigned long","x":"long long","y":"unsigned long long","z":"..."};var subs=[];var first=true;function dump(x){if(x)Module.print(x);Module.print(func);var pre="";for(var a=0;a<i;a++)pre+=" ";Module.print(pre+"^")}function parseNested(){i++;if(func[i]==="K")i++;var parts=[];while(func[i]!=="E"){if(func[i]==="S"){i++;var next=func.indexOf("_",i);var num=func.substring(i,next)||0;parts.push(subs[num]||"?");i=next+1;continue}if(func[i]==="C"){parts.push(parts[parts.length-1]);i+=2;continue}var size=parseInt(func.substr(i));var pre=size.toString().length;if(!size||!pre){i--;break}var curr=func.substr(i+pre,size);parts.push(curr);subs.push(curr);i+=pre+size}i++;return parts}function parse(rawList,limit,allowVoid){limit=limit||Infinity;var ret="",list=[];function flushList(){return"("+list.join(", ")+")"}var name;if(func[i]==="N"){name=parseNested().join("::");limit--;if(limit===0)return rawList?[name]:name}else{if(func[i]==="K"||first&&func[i]==="L")i++;var size=parseInt(func.substr(i));if(size){var pre=size.toString().length;name=func.substr(i+pre,size);i+=pre+size}}first=false;if(func[i]==="I"){i++;var iList=parse(true);var iRet=parse(true,1,true);ret+=iRet[0]+" "+name+"<"+iList.join(", ")+">"}else{ret=name}paramLoop:while(i<func.length&&limit-->0){var c=func[i++];if(c in basicTypes){list.push(basicTypes[c])}else{switch(c){case"P":list.push(parse(true,1,true)[0]+"*");break;case"R":list.push(parse(true,1,true)[0]+"&");break;case"L":{i++;var end=func.indexOf("E",i);var size=end-i;list.push(func.substr(i,size));i+=size+2;break};case"A":{var size=parseInt(func.substr(i));i+=size.toString().length;if(func[i]!=="_")throw"?";i++;list.push(parse(true,1,true)[0]+" ["+size+"]");break};case"E":break paramLoop;default:ret+="?"+c;break paramLoop}}}if(!allowVoid&&list.length===1&&list[0]==="void")list=[];if(rawList){if(ret){list.push(ret+"?")}return list}else{return ret+flushList()}}var parsed=func;try{if(func=="Object._main"||func=="_main"){return"main()"}if(typeof func==="number")func=Pointer_stringify(func);if(func[0]!=="_")return func;if(func[1]!=="_")return func;if(func[2]!=="Z")return func;switch(func[3]){case"n":return"operator new()";case"d":return"operator delete()"}parsed=parse()}catch(e){parsed+="?"}if(parsed.includes("?")&&!hasLibcxxabi){Runtime.warnOnce("warning: a problem occurred in builtin C++ name demangling; build with  -s DEMANGLE_SUPPORT=1  to link in libcxxabi demangling")}return parsed}function demangleAll(text){return text.replace(/__Z[\w\d_]+/g,(function(x){var y=demangle(x);return x===y?x:x+" ["+y+"]"}))}function jsStackTrace(){var err=new Error;if(!err.stack){try{throw new Error(0)}catch(e){err=e}if(!err.stack){return"(no stack trace available)"}}return err.stack.toString()}function stackTrace(){return demangleAll(jsStackTrace())}var PAGE_SIZE=4096;function alignMemoryPage(x){if(x%4096>0){x+=4096-x%4096}return x}var HEAP;var buffer;var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBuffer(buf){Module["buffer"]=buffer=buf}function updateGlobalBufferViews(){Module["HEAP8"]=HEAP8=new Int8Array(buffer);Module["HEAP16"]=HEAP16=new Int16Array(buffer);Module["HEAP32"]=HEAP32=new Int32Array(buffer);Module["HEAPU8"]=HEAPU8=new Uint8Array(buffer);Module["HEAPU16"]=HEAPU16=new Uint16Array(buffer);Module["HEAPU32"]=HEAPU32=new Uint32Array(buffer);Module["HEAPF32"]=HEAPF32=new Float32Array(buffer);Module["HEAPF64"]=HEAPF64=new Float64Array(buffer)}var STATIC_BASE=0,STATICTOP=0,staticSealed=false;var STACK_BASE=0,STACKTOP=0,STACK_MAX=0;var DYNAMIC_BASE=0,DYNAMICTOP=0;function abortOnCannotGrowMemory(){abort("Cannot enlarge memory arrays. Either (1) compile with  -s TOTAL_MEMORY=X  with X higher than the current value "+TOTAL_MEMORY+", (2) compile with  -s ALLOW_MEMORY_GROWTH=1  which adjusts the size at runtime but prevents some optimizations, (3) set Module.TOTAL_MEMORY to a higher value before the program runs, or if you want malloc to return NULL (0) instead of this abort, compile with  -s ABORTING_MALLOC=0 ")}function enlargeMemory(){abortOnCannotGrowMemory()}var TOTAL_STACK=Module["TOTAL_STACK"]||65536;var TOTAL_MEMORY=Module["TOTAL_MEMORY"]||1048576;var totalMemory=64*1024;while(totalMemory<TOTAL_MEMORY||totalMemory<2*TOTAL_STACK){if(totalMemory<16*1024*1024){totalMemory*=2}else{totalMemory+=16*1024*1024}}if(totalMemory!==TOTAL_MEMORY){TOTAL_MEMORY=totalMemory}assert(typeof Int32Array!=="undefined"&&typeof Float64Array!=="undefined"&&!!(new Int32Array(1))["subarray"]&&!!(new Int32Array(1))["set"],"JS engine does not provide full typed array support");if(Module["buffer"]){buffer=Module["buffer"];assert(buffer.byteLength===TOTAL_MEMORY,"provided buffer should be "+TOTAL_MEMORY+" bytes, but it is "+buffer.byteLength)}else{buffer=new ArrayBuffer(TOTAL_MEMORY)}updateGlobalBufferViews();HEAP32[0]=255;assert(HEAPU8[0]===255&&HEAPU8[3]===0,"Typed arrays 2 must be run on a little-endian system");Module["HEAP"]=HEAP;Module["buffer"]=buffer;Module["HEAP8"]=HEAP8;Module["HEAP16"]=HEAP16;Module["HEAP32"]=HEAP32;Module["HEAPU8"]=HEAPU8;Module["HEAPU16"]=HEAPU16;Module["HEAPU32"]=HEAPU32;Module["HEAPF32"]=HEAPF32;Module["HEAPF64"]=HEAPF64;function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback();continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){Runtime.dynCall("v",func)}else{Runtime.dynCall("vi",func,[callback.arg])}}else{func(callback.arg===undefined?null:callback.arg)}}}var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATEXIT__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;var runtimeExited=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function ensureInitRuntime(){if(runtimeInitialized)return;runtimeInitialized=true;callRuntimeCallbacks(__ATINIT__)}function preMain(){callRuntimeCallbacks(__ATMAIN__)}function exitRuntime(){callRuntimeCallbacks(__ATEXIT__);runtimeExited=true}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnInit(cb){__ATINIT__.unshift(cb)}function addOnPreMain(cb){__ATMAIN__.unshift(cb)}function addOnExit(cb){__ATEXIT__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}function intArrayFromString(stringy,dontAddNull,length){var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array}function intArrayToString(array){var ret=[];for(var i=0;i<array.length;i++){var chr=array[i];if(chr>255){chr&=255}ret.push(String.fromCharCode(chr))}return ret.join("")}function writeStringToMemory(string,buffer,dontAddNull){var array=intArrayFromString(string,dontAddNull);var i=0;while(i<array.length){var chr=array[i];HEAP8[buffer+i>>0]=chr;i=i+1}}Module["writeStringToMemory"]=writeStringToMemory;function writeArrayToMemory(array,buffer){for(var i=0;i<array.length;i++){HEAP8[buffer++>>0]=array[i]}}function writeAsciiToMemory(str,buffer,dontAddNull){for(var i=0;i<str.length;++i){HEAP8[buffer++>>0]=str.charCodeAt(i)}if(!dontAddNull)HEAP8[buffer>>0]=0}function unSign(value,bits,ignore){if(value>=0){return value}return bits<=32?2*Math.abs(1<<bits-1)+value:Math.pow(2,bits)+value}function reSign(value,bits,ignore){if(value<=0){return value}var half=bits<=32?Math.abs(1<<bits-1):Math.pow(2,bits-1);if(value>=half&&(bits<=32||value>half)){value=-2*half+value}return value}if(!Math["imul"]||Math["imul"](4294967295,5)!==-5)Math["imul"]=function imul(a,b){var ah=a>>>16;var al=a&65535;var bh=b>>>16;var bl=b&65535;return al*bl+(ah*bl+al*bh<<16)|0};Math.imul=Math["imul"];if(!Math["clz32"])Math["clz32"]=(function(x){x=x>>>0;for(var i=0;i<32;i++){if(x&1<<31-i)return i}return 32});Math.clz32=Math["clz32"];var Math_abs=Math.abs;var Math_cos=Math.cos;var Math_sin=Math.sin;var Math_tan=Math.tan;var Math_acos=Math.acos;var Math_asin=Math.asin;var Math_atan=Math.atan;var Math_atan2=Math.atan2;var Math_exp=Math.exp;var Math_log=Math.log;var Math_sqrt=Math.sqrt;var Math_ceil=Math.ceil;var Math_floor=Math.floor;var Math_pow=Math.pow;var Math_imul=Math.imul;var Math_fround=Math.fround;var Math_min=Math.min;var Math_clz32=Math.clz32;var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function getUniqueRunDependency(id){return id}function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["preloadedImages"]={};Module["preloadedAudios"]={};var memoryInitializer=null;var ASM_CONSTS=[];STATIC_BASE=8;STATICTOP=STATIC_BASE+5360;__ATINIT__.push();allocate([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,33,34,118,101,99,116,111,114,32,108,101,110,103,116,104,95,101,114,114,111,114,34,0,47,109,101,100,105,97,47,97,108,111,110,47,100,54,57,100,100,57,98,50,45,52,55,57,49,45,52,98,56,101,45,97,101,98,51,45,102,54,51,53,51,98,52,53,100,55,49,48,47,104,111,109,101,47,97,108,111,110,47,68,101,118,47,101,109,115,99,114,105,112,116,101,110,47,115,121,115,116,101,109,47,105,110,99,108,117,100,101,47,108,105,98,99,120,120,47,118,101,99,116,111,114,0,95,95,116,104,114,111,119,95,108,101,110,103,116,104,95,101,114,114,111,114,0,32,99,111,110,115,116,0,33,34,98,97,115,105,99,95,115,116,114,105,110,103,32,111,117,116,95,111,102,95,114,97,110,103,101,34,0,47,109,101,100,105,97,47,97,108,111,110,47,100,54,57,100,100,57,98,50,45,52,55,57,49,45,52,98,56,101,45,97,101,98,51,45,102,54,51,53,51,98,52,53,100,55,49,48,47,104,111,109,101,47,97,108,111,110,47,68,101,118,47,101,109,115,99,114,105,112,116,101,110,47,115,121,115,116,101,109,47,105,110,99,108,117,100,101,47,108,105,98,99,120,120,47,115,116,114,105,110,103,0,95,95,116,104,114,111,119,95,111,117,116,95,111,102,95,114,97,110,103,101,0,33,34,98,97,115,105,99,95,115,116,114,105,110,103,32,108,101,110,103,116,104,95,101,114,114,111,114,34,0,32,118,111,108,97,116,105,108,101,0,32,114,101,115,116,114,105,99,116,0,118,111,105,100,0,119,99,104,97,114,95,116,0,98,111,111,108,0,99,104,97,114,0,115,105,103,110,101,100,32,99,104,97,114,0,117,110,115,105,103,110,101,100,32,99,104,97,114,0,115,104,111,114,116,0,117,110,115,105,103,110,101,100,32,115,104,111,114,116,0,105,110,116,0,117,110,115,105,103,110,101,100,32,105,110,116,0,108,111,110,103,0,117,110,115,105,103,110,101,100,32,108,111,110,103,0,108,111,110,103,32,108,111,110,103,0,117,110,115,105,103,110,101,100,32,108,111,110,103,32,108,111,110,103,0,95,95,105,110,116,49,50,56,0,117,110,115,105,103,110,101,100,32,95,95,105,110,116,49,50,56,0,102,108,111,97,116,0,100,111,117,98,108,101,0,108,111,110,103,32,100,111,117,98,108,101,0,95,95,102,108,111,97,116,49,50,56,0,46,46,46,0,95,71,76,79,66,65,76,95,95,78,0,40,97,110,111,110,121,109,111,117,115,32,110,97,109,101,115,112,97,99,101,41,0,100,101,99,105,109,97,108,54,52,0,100,101,99,105,109,97,108,49,50,56,0,100,101,99,105,109,97,108,51,50,0,100,101,99,105,109,97,108,49,54,0,99,104,97,114,51,50,95,116,0,99,104,97,114,49,54,95,116,0,97,117,116,111,0,115,116,100,58,58,110,117,108,108,112,116,114,95,116,0,32,91,0,32,91,93,0,40,0,41,0,102,97,108,115,101,0,116,114,117,101,0,117,0,108,0,117,108,0,108,108,0,117,108,108,0,37,97,102,0,37,97,0,37,76,97,76,0,102,112,0,38,38,0,62,0,41,32,0,32,40,0,38,0,38,61,0,61,0,97,108,105,103,110,111,102,32,40,0,99,111,110,115,116,95,99,97,115,116,60,0,62,40,0,44,0,126,0,41,40,0,58,58,0,100,101,108,101,116,101,91,93,32,0,100,121,110,97,109,105,99,95,99,97,115,116,60,0,100,101,108,101,116,101,32,0,111,112,101,114,97,116,111,114,38,38,0,111,112,101,114,97,116,111,114,38,0,111,112,101,114,97,116,111,114,38,61,0,111,112,101,114,97,116,111,114,61,0,111,112,101,114,97,116,111,114,40,41,0,111,112,101,114,97,116,111,114,44,0,111,112,101,114,97,116,111,114,126,0,111,112,101,114,97,116,111,114,32,0,111,112,101,114,97,116,111,114,32,100,101,108,101,116,101,91,93,0,111,112,101,114,97,116,111,114,42,0,111,112,101,114,97,116,111,114,32,100,101,108,101,116,101,0,111,112,101,114,97,116,111,114,47,0,111,112,101,114,97,116,111,114,47,61,0,111,112,101,114,97,116,111,114,94,0,111,112,101,114,97,116,111,114,94,61,0,111,112,101,114,97,116,111,114,61,61,0,111,112,101,114,97,116,111,114,62,61,0,111,112,101,114,97,116,111,114,62,0,111,112,101,114,97,116,111,114,91,93,0,111,112,101,114,97,116,111,114,60,61,0,111,112,101,114,97,116,111,114,34,34,32,0,111,112,101,114,97,116,111,114,60,60,0,111,112,101,114,97,116,111,114,60,60,61,0,111,112,101,114,97,116,111,114,60,0,111,112,101,114,97,116,111,114,45,0,111,112,101,114,97,116,111,114,45,61,0,111,112,101,114,97,116,111,114,42,61,0,111,112,101,114,97,116,111,114,45,45,0,111,112,101,114,97,116,111,114,32,110,101,119,91,93,0,111,112,101,114,97,116,111,114,33,61,0,111,112,101,114,97,116,111,114,33,0,111,112,101,114,97,116,111,114,32,110,101,119,0,111,112,101,114,97,116,111,114,124,124,0,111,112,101,114,97,116,111,114,124,0,111,112,101,114,97,116,111,114,124,61,0,111,112,101,114,97,116,111,114,45,62,42,0,111,112,101,114,97,116,111,114,43,0,111,112,101,114,97,116,111,114,43,61,0,111,112,101,114,97,116,111,114,43,43,0,111,112,101,114,97,116,111,114,45,62,0,111,112,101,114,97,116,111,114,63,0,111,112,101,114,97,116,111,114,37,0,111,112,101,114,97,116,111,114,37,61,0,111,112,101,114,97,116,111,114,62,62,0,111,112,101,114,97,116,111,114,62,62,61,0,60,0,44,32,0,32,62,0,100,101,99,108,116,121,112,101,40,0,115,116,100,58,58,97,108,108,111,99,97,116,111,114,0,115,116,100,58,58,98,97,115,105,99,95,115,116,114,105,110,103,0,115,116,100,58,58,115,116,114,105,110,103,0,115,116,100,58,58,105,115,116,114,101,97,109,0,115,116,100,58,58,111,115,116,114,101,97,109,0,115,116,100,58,58,105,111,115,116,114,101,97,109,0,115,116,100,58,58,98,97,115,105,99,95,115,116,114,105,110,103,60,99,104,97,114,44,32,115,116,100,58,58,99,104,97,114,95,116,114,97,105,116,115,60,99,104,97,114,62,44,32,115,116,100,58,58,97,108,108,111,99,97,116,111,114,60,99,104,97,114,62,32,62,0,98,97,115,105,99,95,115,116,114,105,110,103,0,115,116,100,58,58,98,97,115,105,99,95,105,115,116,114,101,97,109,60,99,104,97,114,44,32,115,116,100,58,58,99,104,97,114,95,116,114,97,105,116,115,60,99,104,97,114,62,32,62,0,98,97,115,105,99,95,105,115,116,114,101,97,109,0,115,116,100,58,58,98,97,115,105,99,95,111,115,116,114,101,97,109,60,99,104,97,114,44,32,115,116,100,58,58,99,104,97,114,95,116,114,97,105,116,115,60,99,104,97,114,62,32,62,0,98,97,115,105,99,95,111,115,116,114,101,97,109,0,115,116,100,58,58,98,97,115,105,99,95,105,111,115,116,114,101,97,109,60,99,104,97,114,44,32,115,116,100,58,58,99,104,97,114,95,116,114,97,105,116,115,60,99,104,97,114,62,32,62,0,98,97,115,105,99,95,105,111,115,116,114,101,97,109,0,39,117,110,110,97,109,101,100,0,39,108,97,109,98,100,97,39,40,0,115,116,100,58,58,0,46,42,0,47,61,0,94,0,94,61,0,61,61,0,62,61,0,41,91,0,60,61,0,60,60,0,60,60,61,0,45,0,45,61,0,42,61,0,45,45,0,41,45,45,0,91,93,32,0,32,0,33,61,0,33,0,110,111,101,120,99,101,112,116,32,40,0,124,124,0,124,0,124,61,0,45,62,42,0,43,0,43,61,0,43,43,0,41,43,43,0,45,62,0,41,32,63,32,40,0,41,32,58,32,40,0,114,101,105,110,116,101,114,112,114,101,116,95,99,97,115,116,60,0,37,0,37,61,0,62,62,0,62,62,61,0,115,116,97,116,105,99,95,99,97,115,116,60,0,115,105,122,101,111,102,32,40,0,115,105,122,101,111,102,46,46,46,40,0,116,121,112,101,105,100,40,0,116,104,114,111,119,0,116,104,114,111,119,32,0,32,99,111,109,112,108,101,120,0,32,38,0,32,38,38,0,32,105,109,97,103,105,110,97,114,121,0,58,58,42,0,111,98,106,99,95,111,98,106,101,99,116,60,0,105,100,0,111,98,106,99,112,114,111,116,111,0,115,116,100,0,58,58,115,116,114,105,110,103,32,108,105,116,101,114,97,108,0,32,118,101,99,116,111,114,91,0,112,105,120,101,108,32,118,101,99,116,111,114,91,0,118,116,97,98,108,101,32,102,111,114,32,0,86,84,84,32,102,111,114,32,0,116,121,112,101,105,110,102,111,32,102,111,114,32,0,116,121,112,101,105,110,102,111,32,110,97,109,101,32,102,111,114,32,0,99,111,118,97,114,105,97,110,116,32,114,101,116,117,114,110,32,116,104,117,110,107,32,116,111,32,0,99,111,110,115,116,114,117,99,116,105,111,110,32,118,116,97,98,108,101,32,102,111,114,32,0,45,105,110,45,0,118,105,114,116,117,97,108,32,116,104,117,110,107,32,116,111,32,0,110,111,110,45,118,105,114,116,117,97,108,32,116,104,117,110,107,32,116,111,32,0,103,117,97,114,100,32,118,97,114,105,97,98,108,101,32,102,111,114,32,0,114,101,102,101,114,101,110,99,101,32,116,101,109,112,111,114,97,114,121,32,102,111,114,32,0,95,98,108,111,99,107,95,105,110,118,111,107,101,0,105,110,118,111,99,97,116,105,111,110,32,102,117,110,99,116,105,111,110,32,102,111,114,32,98,108,111,99,107,32,105,110,32,0,47,0,84,33,34,25,13,1,2,3,17,75,28,12,16,4,11,29,18,30,39,104,110,111,112,113,98,32,5,6,15,19,20,21,26,8,22,7,40,36,23,24,9,10,14,27,31,37,35,131,130,125,38,42,43,60,61,62,63,67,71,74,77,88,89,90,91,92,93,94,95,96,97,99,100,101,102,103,105,106,107,108,114,115,116,121,122,123,124,0,73,108,108,101,103,97,108,32,98,121,116,101,32,115,101,113,117,101,110,99,101,0,68,111,109,97,105,110,32,101,114,114,111,114,0,82,101,115,117,108,116,32,110,111,116,32,114,101,112,114,101,115,101,110,116,97,98,108,101,0,78,111,116,32,97,32,116,116,121,0,80,101,114,109,105,115,115,105,111,110,32,100,101,110,105,101,100,0,79,112,101,114,97,116,105,111,110,32,110,111,116,32,112,101,114,109,105,116,116,101,100,0,78,111,32,115,117,99,104,32,102,105,108,101,32,111,114,32,100,105,114,101,99,116,111,114,121,0,78,111,32,115,117,99,104,32,112,114,111,99,101,115,115,0,70,105,108,101,32,101,120,105,115,116,115,0,86,97,108,117,101,32,116,111,111,32,108,97,114,103,101,32,102,111,114,32,100,97,116,97,32,116,121,112,101,0,78,111,32,115,112,97,99,101,32,108,101,102,116,32,111,110,32,100,101,118,105,99,101,0,79,117,116,32,111,102,32,109,101,109,111,114,121,0,82,101,115,111,117,114,99,101,32,98,117,115,121,0,73,110,116,101,114,114,117,112,116,101,100,32,115,121,115,116,101,109,32,99,97,108,108,0,82,101,115,111,117,114,99,101,32,116,101,109,112,111,114,97,114,105,108,121,32,117,110,97,118,97,105,108,97,98,108,101,0,73,110,118,97,108,105,100,32,115,101,101,107,0,67,114,111,115,115,45,100,101,118,105,99,101,32,108,105,110,107,0,82,101,97,100,45,111,110,108,121,32,102,105,108,101,32,115,121,115,116,101,109,0,68,105,114,101,99,116,111,114,121,32,110,111,116,32,101,109,112,116,121,0,67,111,110,110,101,99,116,105,111,110,32,114,101,115,101,116,32,98,121,32,112,101,101,114,0,79,112,101,114,97,116,105,111,110,32,116,105,109,101,100,32,111,117,116,0,67,111,110,110,101,99,116,105,111,110,32,114,101,102,117,115,101,100,0,72,111,115,116,32,105,115,32,100,111,119,110,0,72,111,115,116,32,105,115,32,117,110,114,101,97,99,104,97,98,108,101,0,65,100,100,114,101,115,115,32,105,110,32,117,115,101,0,66,114,111,107,101,110,32,112,105,112,101,0,73,47,79,32,101,114,114,111,114,0,78,111,32,115,117,99,104,32,100,101,118,105,99,101,32,111,114,32,97,100,100,114,101,115,115,0,66,108,111,99,107,32,100,101,118,105,99,101,32,114,101,113,117,105,114,101,100,0,78,111,32,115,117,99,104,32,100,101,118,105,99,101,0,78,111,116,32,97,32,100,105,114,101,99,116,111,114,121,0,73,115,32,97,32,100,105,114,101,99,116,111,114,121,0,84,101,120,116,32,102,105,108,101,32,98,117,115,121,0,69,120,101,99,32,102,111,114,109,97,116,32,101,114,114,111,114,0,73,110,118,97,108,105,100,32,97,114,103,117,109,101,110,116,0,65,114,103,117,109,101,110,116,32,108,105,115,116,32,116,111,111,32,108,111,110,103,0,83,121,109,98,111,108,105,99,32,108,105,110,107,32,108,111,111,112,0,70,105,108,101,110,97,109,101,32,116,111,111,32,108,111,110,103,0,84,111,111,32,109,97,110,121,32,111,112,101,110,32,102,105,108,101,115,32,105,110,32,115,121,115,116,101,109,0,78,111,32,102,105,108,101,32,100,101,115,99,114,105,112,116,111,114,115,32,97,118,97,105,108,97,98,108,101,0,66,97,100,32,102,105,108,101,32,100,101,115,99,114,105,112,116,111,114,0,78,111,32,99,104,105,108,100,32,112,114,111,99,101,115,115,0,66,97,100,32,97,100,100,114,101,115,115,0,70,105,108,101,32,116,111,111,32,108,97,114,103,101,0,84,111,111,32,109,97,110,121,32,108,105,110,107,115,0,78,111,32,108,111,99,107,115,32,97,118,97,105,108,97,98,108,101,0,82,101,115,111,117,114,99,101,32,100,101,97,100,108,111,99,107,32,119,111,117,108,100,32,111,99,99,117,114,0,83,116,97,116,101,32,110,111,116,32,114,101,99,111,118,101,114,97,98,108,101,0,80,114,101,118,105,111,117,115,32,111,119,110,101,114,32,100,105,101,100,0,79,112,101,114,97,116,105,111,110,32,99,97,110,99,101,108,101,100,0,70,117,110,99,116,105,111,110,32,110,111,116,32,105,109,112,108,101,109,101,110,116,101,100,0,78,111,32,109,101,115,115,97,103,101,32,111,102,32,100,101,115,105,114,101,100,32,116,121,112,101,0,73,100,101,110,116,105,102,105,101,114,32,114,101,109,111,118,101,100,0,68,101,118,105,99,101,32,110,111,116,32,97,32,115,116,114,101,97,109,0,78,111,32,100,97,116,97,32,97,118,97,105,108,97,98,108,101,0,68,101,118,105,99,101,32,116,105,109,101,111,117,116,0,79,117,116,32,111,102,32,115,116,114,101,97,109,115,32,114,101,115,111,117,114,99,101,115,0,76,105,110,107,32,104,97,115,32,98,101,101,110,32,115,101,118,101,114,101,100,0,80,114,111,116,111,99,111,108,32,101,114,114,111,114,0,66,97,100,32,109,101,115,115,97,103,101,0,70,105,108,101,32,100,101,115,99,114,105,112,116,111,114,32,105,110,32,98,97,100,32,115,116,97,116,101,0,78,111,116,32,97,32,115,111,99,107,101,116,0,68,101,115,116,105,110,97,116,105,111,110,32,97,100,100,114,101,115,115,32,114,101,113,117,105,114,101,100,0,77,101,115,115,97,103,101,32,116,111,111,32,108,97,114,103,101,0,80,114,111,116,111,99,111,108,32,119,114,111,110,103,32,116,121,112,101,32,102,111,114,32,115,111,99,107,101,116,0,80,114,111,116,111,99,111,108,32,110,111,116,32,97,118,97,105,108,97,98,108,101,0,80,114,111,116,111,99,111,108,32,110,111,116,32,115,117,112,112,111,114,116,101,100,0,83,111,99,107,101,116,32,116,121,112,101,32,110,111,116,32,115,117,112,112,111,114,116,101,100,0,78,111,116,32,115,117,112,112,111,114,116,101,100,0,80,114,111,116,111,99,111,108,32,102,97,109,105,108,121,32,110,111,116,32,115,117,112,112,111,114,116,101,100,0,65,100,100,114,101,115,115,32,102,97,109,105,108,121,32,110,111,116,32,115,117,112,112,111,114,116,101,100,32,98,121,32,112,114,111,116,111,99,111,108,0,65,100,100,114,101,115,115,32,110,111,116,32,97,118,97,105,108,97,98,108,101,0,78,101,116,119,111,114,107,32,105,115,32,100,111,119,110,0,78,101,116,119,111,114,107,32,117,110,114,101,97,99,104,97,98,108,101,0,67,111,110,110,101,99,116,105,111,110,32,114,101,115,101,116,32,98,121,32,110,101,116,119,111,114,107,0,67,111,110,110,101,99,116,105,111,110,32,97,98,111,114,116,101,100,0,78,111,32,98,117,102,102,101,114,32,115,112,97,99,101,32,97,118,97,105,108,97,98,108,101,0,83,111,99,107,101,116,32,105,115,32,99,111,110,110,101,99,116,101,100,0,83,111,99,107,101,116,32,110,111,116,32,99,111,110,110,101,99,116,101,100,0,67,97,110,110,111,116,32,115,101,110,100,32,97,102,116,101,114,32,115,111,99,107,101,116,32,115,104,117,116,100,111,119,110,0,79,112,101,114,97,116,105,111,110,32,97,108,114,101,97,100,121,32,105,110,32,112,114,111,103,114,101,115,115,0,79,112,101,114,97,116,105,111,110,32,105,110,32,112,114,111,103,114,101,115,115,0,83,116,97,108,101,32,102,105,108,101,32,104,97,110,100,108,101,0,82,101,109,111,116,101,32,73,47,79,32,101,114,114,111,114,0,81,117,111,116,97,32,101,120,99,101,101,100,101,100,0,78,111,32,109,101,100,105,117,109,32,102,111,117,110,100,0,87,114,111,110,103,32,109,101,100,105,117,109,32,116,121,112,101,0,78,111,32,101,114,114,111,114,32,105,110,102,111,114,109,97,116,105,111,110,0,0,42,0,93,0,17,0,10,0,17,17,17,0,0,0,0,5,0,0,0,0,0,0,9,0,0,0,0,11,0,0,0,0,0,0,0,0,17,0,15,10,17,17,17,3,10,7,0,1,19,9,11,11,0,0,9,6,11,0,0,11,0,6,17,0,0,0,17,17,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,17,0,10,10,17,17,17,0,10,0,0,2,0,9,11,0,0,0,9,0,11,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,12,0,0,0,0,9,12,0,0,0,0,0,12,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,0,0,0,0,0,0,0,0,0,0,0,13,0,0,0,4,13,0,0,0,0,9,14,0,0,0,0,0,14,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,0,0,0,0,0,0,0,0,0,0,0,15,0,0,0,0,15,0,0,0,0,9,16,0,0,0,0,0,16,0,0,16,0,0,18,0,0,0,18,18,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,0,0,0,18,18,18,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,0,0,0,10,0,0,0,0,10,0,0,0,0,9,11,0,0,0,0,0,11,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,12,0,0,0,0,9,12,0,0,0,0,0,12,0,0,12,0,0,48,49,50,51,52,53,54,55,56,57,65,66,67,68,69,70,45,43,32,32,32,48,88,48,120,0,40,110,117,108,108,41,0,45,48,88,43,48,88,32,48,88,45,48,120,43,48,120,32,48,120,0,105,110,102,0,73,78,70,0,110,97,110,0,78,65,78,0,46,0],"i8",ALLOC_NONE,Runtime.GLOBAL_BASE);var tempDoublePtr=Runtime.alignMemory(allocate(12,"i8",ALLOC_STATIC),8);assert(tempDoublePtr%8==0);function copyTempFloat(ptr){HEAP8[tempDoublePtr]=HEAP8[ptr];HEAP8[tempDoublePtr+1]=HEAP8[ptr+1];HEAP8[tempDoublePtr+2]=HEAP8[ptr+2];HEAP8[tempDoublePtr+3]=HEAP8[ptr+3]}function copyTempDouble(ptr){HEAP8[tempDoublePtr]=HEAP8[ptr];HEAP8[tempDoublePtr+1]=HEAP8[ptr+1];HEAP8[tempDoublePtr+2]=HEAP8[ptr+2];HEAP8[tempDoublePtr+3]=HEAP8[ptr+3];HEAP8[tempDoublePtr+4]=HEAP8[ptr+4];HEAP8[tempDoublePtr+5]=HEAP8[ptr+5];HEAP8[tempDoublePtr+6]=HEAP8[ptr+6];HEAP8[tempDoublePtr+7]=HEAP8[ptr+7]}Module["_memset"]=_memset;Module["_i64Subtract"]=_i64Subtract;function ___setErrNo(value){if(Module["___errno_location"])HEAP32[Module["___errno_location"]()>>2]=value;return value}var ERRNO_CODES={EPERM:1,ENOENT:2,ESRCH:3,EINTR:4,EIO:5,ENXIO:6,E2BIG:7,ENOEXEC:8,EBADF:9,ECHILD:10,EAGAIN:11,EWOULDBLOCK:11,ENOMEM:12,EACCES:13,EFAULT:14,ENOTBLK:15,EBUSY:16,EEXIST:17,EXDEV:18,ENODEV:19,ENOTDIR:20,EISDIR:21,EINVAL:22,ENFILE:23,EMFILE:24,ENOTTY:25,ETXTBSY:26,EFBIG:27,ENOSPC:28,ESPIPE:29,EROFS:30,EMLINK:31,EPIPE:32,EDOM:33,ERANGE:34,ENOMSG:42,EIDRM:43,ECHRNG:44,EL2NSYNC:45,EL3HLT:46,EL3RST:47,ELNRNG:48,EUNATCH:49,ENOCSI:50,EL2HLT:51,EDEADLK:35,ENOLCK:37,EBADE:52,EBADR:53,EXFULL:54,ENOANO:55,EBADRQC:56,EBADSLT:57,EDEADLOCK:35,EBFONT:59,ENOSTR:60,ENODATA:61,ETIME:62,ENOSR:63,ENONET:64,ENOPKG:65,EREMOTE:66,ENOLINK:67,EADV:68,ESRMNT:69,ECOMM:70,EPROTO:71,EMULTIHOP:72,EDOTDOT:73,EBADMSG:74,ENOTUNIQ:76,EBADFD:77,EREMCHG:78,ELIBACC:79,ELIBBAD:80,ELIBSCN:81,ELIBMAX:82,ELIBEXEC:83,ENOSYS:38,ENOTEMPTY:39,ENAMETOOLONG:36,ELOOP:40,EOPNOTSUPP:95,EPFNOSUPPORT:96,ECONNRESET:104,ENOBUFS:105,EAFNOSUPPORT:97,EPROTOTYPE:91,ENOTSOCK:88,ENOPROTOOPT:92,ESHUTDOWN:108,ECONNREFUSED:111,EADDRINUSE:98,ECONNABORTED:103,ENETUNREACH:101,ENETDOWN:100,ETIMEDOUT:110,EHOSTDOWN:112,EHOSTUNREACH:113,EINPROGRESS:115,EALREADY:114,EDESTADDRREQ:89,EMSGSIZE:90,EPROTONOSUPPORT:93,ESOCKTNOSUPPORT:94,EADDRNOTAVAIL:99,ENETRESET:102,EISCONN:106,ENOTCONN:107,ETOOMANYREFS:109,EUSERS:87,EDQUOT:122,ESTALE:116,ENOTSUP:95,ENOMEDIUM:123,EILSEQ:84,EOVERFLOW:75,ECANCELED:125,ENOTRECOVERABLE:131,EOWNERDEAD:130,ESTRPIPE:86};function _sysconf(name){switch(name){case 30:return PAGE_SIZE;case 85:return totalMemory/PAGE_SIZE;case 132:case 133:case 12:case 137:case 138:case 15:case 235:case 16:case 17:case 18:case 19:case 20:case 149:case 13:case 10:case 236:case 153:case 9:case 21:case 22:case 159:case 154:case 14:case 77:case 78:case 139:case 80:case 81:case 82:case 68:case 67:case 164:case 11:case 29:case 47:case 48:case 95:case 52:case 51:case 46:return 200809;case 79:return 0;case 27:case 246:case 127:case 128:case 23:case 24:case 160:case 161:case 181:case 182:case 242:case 183:case 184:case 243:case 244:case 245:case 165:case 178:case 179:case 49:case 50:case 168:case 169:case 175:case 170:case 171:case 172:case 97:case 76:case 32:case 173:case 35:return-1;case 176:case 177:case 7:case 155:case 8:case 157:case 125:case 126:case 92:case 93:case 129:case 130:case 131:case 94:case 91:return 1;case 74:case 60:case 69:case 70:case 4:return 1024;case 31:case 42:case 72:return 32;case 87:case 26:case 33:return 2147483647;case 34:case 1:return 47839;case 38:case 36:return 99;case 43:case 37:return 2048;case 0:return 2097152;case 3:return 65536;case 28:return 32768;case 44:return 32767;case 75:return 16384;case 39:return 1e3;case 89:return 700;case 71:return 256;case 40:return 255;case 2:return 100;case 180:return 64;case 25:return 20;case 5:return 16;case 6:return 6;case 73:return 4;case 84:{if(typeof navigator==="object")return navigator["hardwareConcurrency"]||1;return 1}}___setErrNo(ERRNO_CODES.EINVAL);return-1}var _BDtoIHigh=true;var _BDtoILow=true;Module["_bitshift64Lshr"]=_bitshift64Lshr;var _BItoD=true;Module["_bitshift64Shl"]=_bitshift64Shl;function _abort(){Module["abort"]()}function _emscripten_memcpy_big(dest,src,num){HEAPU8.set(HEAPU8.subarray(src,src+num),dest);return dest}Module["_memcpy"]=_memcpy;Module["_i64Add"]=_i64Add;function ___assert_fail(condition,filename,line,func){ABORT=true;throw"Assertion failed: "+Pointer_stringify(condition)+", at: "+[filename?Pointer_stringify(filename):"unknown filename",line,func?Pointer_stringify(func):"unknown function"]+" at "+stackTrace()}function _sbrk(bytes){var self=_sbrk;if(!self.called){DYNAMICTOP=alignMemoryPage(DYNAMICTOP);self.called=true;assert(Runtime.dynamicAlloc);self.alloc=Runtime.dynamicAlloc;Runtime.dynamicAlloc=(function(){abort("cannot dynamically allocate, sbrk now has control")})}var ret=DYNAMICTOP;if(bytes!=0){var success=self.alloc(bytes);if(!success)return-1>>>0}return ret}Module["_memmove"]=_memmove;function __ZSt18uncaught_exceptionv(){return!!__ZSt18uncaught_exceptionv.uncaught_exception}var EXCEPTIONS={last:0,caught:[],infos:{},deAdjust:(function(adjusted){if(!adjusted||EXCEPTIONS.infos[adjusted])return adjusted;for(var ptr in EXCEPTIONS.infos){var info=EXCEPTIONS.infos[ptr];if(info.adjusted===adjusted){return ptr}}return adjusted}),addRef:(function(ptr){if(!ptr)return;var info=EXCEPTIONS.infos[ptr];info.refcount++}),decRef:(function(ptr){if(!ptr)return;var info=EXCEPTIONS.infos[ptr];assert(info.refcount>0);info.refcount--;if(info.refcount===0){if(info.destructor){Runtime.dynCall("vi",info.destructor,[ptr])}delete EXCEPTIONS.infos[ptr];___cxa_free_exception(ptr)}}),clearRef:(function(ptr){if(!ptr)return;var info=EXCEPTIONS.infos[ptr];info.refcount=0})};function ___resumeException(ptr){if(!EXCEPTIONS.last){EXCEPTIONS.last=ptr}EXCEPTIONS.clearRef(EXCEPTIONS.deAdjust(ptr));throw ptr+" - Exception catching is disabled, this exception cannot be caught. Compile with -s DISABLE_EXCEPTION_CATCHING=0 or DISABLE_EXCEPTION_CATCHING=2 to catch."}function ___cxa_find_matching_catch(){var thrown=EXCEPTIONS.last;if(!thrown){return(asm["setTempRet0"](0),0)|0}var info=EXCEPTIONS.infos[thrown];var throwntype=info.type;if(!throwntype){return(asm["setTempRet0"](0),thrown)|0}var typeArray=Array.prototype.slice.call(arguments);var pointer=Module["___cxa_is_pointer_type"](throwntype);if(!___cxa_find_matching_catch.buffer)___cxa_find_matching_catch.buffer=_malloc(4);HEAP32[___cxa_find_matching_catch.buffer>>2]=thrown;thrown=___cxa_find_matching_catch.buffer;for(var i=0;i<typeArray.length;i++){if(typeArray[i]&&Module["___cxa_can_catch"](typeArray[i],throwntype,thrown)){thrown=HEAP32[thrown>>2];info.adjusted=thrown;return(asm["setTempRet0"](typeArray[i]),thrown)|0}}thrown=HEAP32[thrown>>2];return(asm["setTempRet0"](throwntype),thrown)|0}function ___gxx_personality_v0(){}function _time(ptr){var ret=Date.now()/1e3|0;if(ptr){HEAP32[ptr>>2]=ret}return ret}function _pthread_self(){return 0}STACK_BASE=STACKTOP=Runtime.alignMemory(STATICTOP);staticSealed=true;STACK_MAX=STACK_BASE+TOTAL_STACK;DYNAMIC_BASE=DYNAMICTOP=Runtime.alignMemory(STACK_MAX);assert(DYNAMIC_BASE<TOTAL_MEMORY,"TOTAL_MEMORY not big enough for stack");var cttz_i8=allocate([8,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,7,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0],"i8",ALLOC_DYNAMIC);function invoke_iiii(index,a1,a2,a3){try{return Module["dynCall_iiii"](index,a1,a2,a3)}catch(e){if(typeof e!=="number"&&e!=="longjmp")throw e;asm["setThrew"](1,0)}}Module.asmGlobalArg={"Math":Math,"Int8Array":Int8Array,"Int16Array":Int16Array,"Int32Array":Int32Array,"Uint8Array":Uint8Array,"Uint16Array":Uint16Array,"Uint32Array":Uint32Array,"Float32Array":Float32Array,"Float64Array":Float64Array,"NaN":NaN,"Infinity":Infinity};Module.asmLibraryArg={"abort":abort,"assert":assert,"invoke_iiii":invoke_iiii,"_sysconf":_sysconf,"_pthread_self":_pthread_self,"_abort":_abort,"___setErrNo":___setErrNo,"_sbrk":_sbrk,"_time":_time,"_emscripten_memcpy_big":_emscripten_memcpy_big,"___gxx_personality_v0":___gxx_personality_v0,"___resumeException":___resumeException,"__ZSt18uncaught_exceptionv":__ZSt18uncaught_exceptionv,"___assert_fail":___assert_fail,"___cxa_find_matching_catch":___cxa_find_matching_catch,"STACKTOP":STACKTOP,"STACK_MAX":STACK_MAX,"tempDoublePtr":tempDoublePtr,"ABORT":ABORT,"cttz_i8":cttz_i8};// EMSCRIPTEN_START_ASM
var asm=(function(global,env,buffer) {
"use asm";var a=new global.Int8Array(buffer);var b=new global.Int16Array(buffer);var c=new global.Int32Array(buffer);var d=new global.Uint8Array(buffer);var e=new global.Uint16Array(buffer);var f=new global.Uint32Array(buffer);var g=new global.Float32Array(buffer);var h=new global.Float64Array(buffer);var i=env.STACKTOP|0;var j=env.STACK_MAX|0;var k=env.tempDoublePtr|0;var l=env.ABORT|0;var m=env.cttz_i8|0;var n=0;var o=0;var p=0;var q=0;var r=global.NaN,s=global.Infinity;var t=0,u=0,v=0,w=0,x=0.0,y=0,z=0,A=0,B=0.0;var C=0;var D=0;var E=0;var F=0;var G=0;var H=0;var I=0;var J=0;var K=0;var L=0;var M=global.Math.floor;var N=global.Math.abs;var O=global.Math.sqrt;var P=global.Math.pow;var Q=global.Math.cos;var R=global.Math.sin;var S=global.Math.tan;var T=global.Math.acos;var U=global.Math.asin;var V=global.Math.atan;var W=global.Math.atan2;var X=global.Math.exp;var Y=global.Math.log;var Z=global.Math.ceil;var _=global.Math.imul;var $=global.Math.min;var aa=global.Math.clz32;var ba=env.abort;var ca=env.assert;var da=env.invoke_iiii;var ea=env._sysconf;var fa=env._pthread_self;var ga=env._abort;var ha=env.___setErrNo;var ia=env._sbrk;var ja=env._time;var ka=env._emscripten_memcpy_big;var la=env.___gxx_personality_v0;var ma=env.___resumeException;var na=env.__ZSt18uncaught_exceptionv;var oa=env.___assert_fail;var pa=env.___cxa_find_matching_catch;var qa=0.0;
// EMSCRIPTEN_START_FUNCS
function ub(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,aa=0,ba=0,ca=0,da=0,ea=0,fa=0,ga=0,ha=0,ia=0,ja=0,ka=0,la=0,ma=0,na=0,oa=0,pa=0,qa=0,ra=0,sa=0,ta=0,ua=0,va=0,wa=0,xa=0,ya=0,za=0,Aa=0,Ba=0,Ca=0,Da=0,Ea=0,Fa=0,Ga=0,Ha=0,Ka=0,La=0,Ma=0,Oa=0,Qa=0,Ra=0,Sa=0,Ua=0,Va=0,Wa=0,Xa=0,_a=0,eb=0,fb=0,gb=0,hb=0,jb=0,kb=0,lb=0,mb=0,nb=0,ob=0,pb=0,qb=0,sb=0,tb=0,wb=0,yb=0,zb=0,Ab=0,Bb=0,Ib=0,Kb=0,Lb=0,Mb=0,Nb=0,Ob=0,Pb=0,Qb=0,Rb=0,Sb=0,Tb=0,Vb=0,Wb=0,Xb=0,Yb=0,Zb=0,_b=0,$b=0,ac=0,bc=0,cc=0,dc=0,ec=0,fc=0;fc=i;i=i+1104|0;dc=fc+1072|0;ec=fc+1048|0;cc=fc+1032|0;bc=fc+1020|0;$b=fc+1008|0;_b=fc+984|0;ac=fc+972|0;Sb=fc+596|0;Tb=fc+572|0;Xb=fc+548|0;Wb=fc+524|0;Yb=fc+488|0;Zb=fc+460|0;f=fc+960|0;k=fc+948|0;n=fc+936|0;r=fc+924|0;u=fc+912|0;w=fc+900|0;x=fc+888|0;Ib=fc+876|0;Kb=fc+864|0;Lb=fc+852|0;Mb=fc+840|0;y=fc+828|0;Nb=fc+816|0;Ob=fc+804|0;Pb=fc+792|0;Qb=fc+780|0;B=fc+768|0;C=fc+756|0;D=fc+744|0;F=fc+732|0;G=fc+720|0;H=fc+708|0;I=fc+696|0;fb=fc+672|0;gb=fc+656|0;hb=fc+644|0;jb=fc+632|0;kb=fc+620|0;J=fc+608|0;K=fc+584|0;L=fc+560|0;M=fc+536|0;N=fc+512|0;O=fc+472|0;P=fc+448|0;Q=fc+436|0;na=fc+424|0;Ga=fc+400|0;Ha=fc+384|0;Ka=fc+372|0;La=fc+360|0;R=fc+348|0;S=fc+336|0;T=fc+324|0;U=fc+312|0;V=fc+300|0;W=fc+288|0;X=fc+276|0;_=fc+264|0;$=fc+252|0;oa=fc+240|0;Ma=fc+216|0;Oa=fc+204|0;Qa=fc+192|0;Ra=fc+180|0;aa=fc+168|0;sb=fc+144|0;tb=fc+132|0;wb=fc+120|0;yb=fc+108|0;zb=fc+96|0;Ab=fc+84|0;Bb=fc+72|0;da=fc+60|0;fa=fc+48|0;ha=fc+36|0;ia=fc+24|0;Sa=fc;lb=d;ja=lb-b|0;a:do if((ja|0)>1){ka=(ja|0)>3;if(ka?(a[b>>0]|0)==103:0){la=(a[b+1>>0]|0)==115;Ua=la;la=la?b+2|0:b}else{Ua=0;la=b}do switch(a[la>>0]|0){case 76:{b=vb(b,d,e)|0;break a}case 84:{b=Eb(b,d,e)|0;break a}case 102:{b=Fb(b,d,e)|0;break a}case 97:switch(a[la+1>>0]|0){case 97:{dc=b+2|0;$a(f,841,2);ec=Gb(dc,d,f,e)|0;Ja(f);b=(ec|0)==(dc|0)?b:ec;break a}case 100:{dc=b+2|0;$a(k,852,1);ec=Hb(dc,d,k,e)|0;Ja(k);b=(ec|0)==(dc|0)?b:ec;break a}case 110:{dc=b+2|0;$a(n,852,1);ec=Gb(dc,d,n,e)|0;Ja(n);b=(ec|0)==(dc|0)?b:ec;break a}case 78:{dc=b+2|0;$a(r,854,2);ec=Gb(dc,d,r,e)|0;Ja(r);b=(ec|0)==(dc|0)?b:ec;break a}case 83:{dc=b+2|0;$a(u,857,1);ec=Gb(dc,d,u,e)|0;Ja(u);b=(ec|0)==(dc|0)?b:ec;break a}case 116:{if(((((ja|0)>2?(a[b>>0]|0)==97:0)?(a[b+1>>0]|0)==116:0)?(bc=b+2|0,nb=Na(bc,d,e)|0,(nb|0)!=(bc|0)):0)?(Da=c[e+4>>2]|0,(c[e>>2]|0)!=(Da|0)):0){o=Da+-24|0;Cb(cc,o);b=Ta(cc,0,859)|0;c[ec>>2]=c[b>>2];c[ec+4>>2]=c[b+4>>2];c[ec+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}b=Ya(ec,799)|0;c[dc>>2]=c[b>>2];c[dc+4>>2]=c[b+4>>2];c[dc+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}do if(a[o>>0]&1){n=Da+-16|0;a[c[n>>2]>>0]=0;k=Da+-20|0;c[k>>2]=0;b=a[o>>0]|0;if(!(b&1))j=10;else{j=c[o>>2]|0;b=j&255;j=(j&-2)+-1|0}if(!(b&1)){f=(b&255)>>>1;if((b&255)<22){h=10;m=1;l=f}else{h=(f+16&240)+-1|0;m=1;l=f}}else{h=10;m=0;l=0}if((h|0)!=(j|0)){if((h|0)==10){g=o+1|0;f=c[n>>2]|0;if(m){Fc(g|0,f|0,((b&255)>>>1)+1|0)|0;wc(f)}else{a[g>>0]=a[f>>0]|0;wc(f)}a[o>>0]=l<<1;break}f=h+1|0;g=vc(f)|0;if(!(h>>>0<=j>>>0&(g|0)==0)){if(m)Fc(g|0,o+1|0,((b&255)>>>1)+1|0)|0;else{bc=c[n>>2]|0;a[g>>0]=a[bc>>0]|0;wc(bc)}c[o>>2]=f|1;c[k>>2]=l;c[n>>2]=g}}}else{a[o+1>>0]=0;a[o>>0]=0}while(0);c[o>>2]=c[dc>>2];c[o+4>>2]=c[dc+4>>2];c[o+8>>2]=c[dc+8>>2];b=0;while(1){if((b|0)==3)break;c[dc+(b<<2)>>2]=0;b=b+1|0}Ja(dc);Ja(ec);Ja(cc);b=nb}break a}case 122:{if(((((ja|0)>2?(a[b>>0]|0)==97:0)?(a[b+1>>0]|0)==122:0)?(bc=b+2|0,ob=ub(bc,d,e)|0,(ob|0)!=(bc|0)):0)?(Ea=c[e+4>>2]|0,(c[e>>2]|0)!=(Ea|0)):0){o=Ea+-24|0;Cb(cc,o);b=Ta(cc,0,859)|0;c[ec>>2]=c[b>>2];c[ec+4>>2]=c[b+4>>2];c[ec+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}b=Ya(ec,799)|0;c[dc>>2]=c[b>>2];c[dc+4>>2]=c[b+4>>2];c[dc+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}do if(a[o>>0]&1){n=Ea+-16|0;a[c[n>>2]>>0]=0;k=Ea+-20|0;c[k>>2]=0;b=a[o>>0]|0;if(!(b&1))j=10;else{j=c[o>>2]|0;b=j&255;j=(j&-2)+-1|0}if(!(b&1)){f=(b&255)>>>1;if((b&255)<22){m=1;h=10;l=f}else{m=1;h=(f+16&240)+-1|0;l=f}}else{m=0;h=10;l=0}if((h|0)!=(j|0)){if((h|0)==10){g=o+1|0;f=c[n>>2]|0;if(m){Fc(g|0,f|0,((b&255)>>>1)+1|0)|0;wc(f)}else{a[g>>0]=a[f>>0]|0;wc(f)}a[o>>0]=l<<1;break}f=h+1|0;g=vc(f)|0;if(!(h>>>0<=j>>>0&(g|0)==0)){if(m)Fc(g|0,o+1|0,((b&255)>>>1)+1|0)|0;else{bc=c[n>>2]|0;a[g>>0]=a[bc>>0]|0;wc(bc)}c[o>>2]=f|1;c[k>>2]=l;c[n>>2]=g}}}else{a[o+1>>0]=0;a[o>>0]=0}while(0);c[o>>2]=c[dc>>2];c[o+4>>2]=c[dc+4>>2];c[o+8>>2]=c[dc+8>>2];b=0;while(1){if((b|0)==3)break;c[dc+(b<<2)>>2]=0;b=b+1|0}Ja(dc);Ja(ec);Ja(cc);b=ob}break a}default:break a}case 99:switch(a[la+1>>0]|0){case 99:{if((((((ja|0)>2?(a[b>>0]|0)==99:0)?(a[b+1>>0]|0)==99:0)?(Zb=b+2|0,z=Na(Zb,d,e)|0,(z|0)!=(Zb|0)):0)?(Xa=ub(z,d,e)|0,(Xa|0)!=(z|0)):0)?(ua=e+4|0,A=c[ua>>2]|0,((A-(c[e>>2]|0)|0)/24|0)>>>0>=2):0){Cb(dc,A+-24|0);b=c[ua>>2]|0;f=b+-24|0;g=b;while(1){if((g|0)==(f|0))break;e=g+-24|0;c[ua>>2]=e;Ia(e);g=c[ua>>2]|0}g=b+-48|0;Cb(ac,g);b=Ta(ac,0,869)|0;c[_b>>2]=c[b>>2];c[_b+4>>2]=c[b+4>>2];c[_b+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}b=Ya(_b,881)|0;c[$b>>2]=c[b>>2];c[$b+4>>2]=c[b+4>>2];c[$b+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}b=a[dc>>0]|0;f=(b&1)==0;b=Za($b,f?dc+1|0:c[dc+8>>2]|0,f?(b&255)>>>1:c[dc+4>>2]|0)|0;c[bc>>2]=c[b>>2];c[bc+4>>2]=c[b+4>>2];c[bc+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}b=Ya(bc,799)|0;c[cc>>2]=c[b>>2];c[cc+4>>2]=c[b+4>>2];c[cc+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}rb(ec,cc);Db(g,ec);Ia(ec);Ja(cc);Ja(bc);Ja($b);Ja(_b);Ja(ac);Ja(dc);b=Xa}break a}case 108:{b:do if((((ka?(a[b>>0]|0)==99:0)?(a[b+1>>0]|0)==108:0)?(cc=b+2|0,pb=ub(cc,d,e)|0,!((pb|0)==(cc|0)|(pb|0)==(d|0))):0)?(Rb=e+4|0,E=c[Rb>>2]|0,(c[e>>2]|0)!=(E|0)):0){cc=E+-12|0;g=a[cc>>0]|0;f=(g&1)==0;Za(E+-24|0,f?cc+1|0:c[E+-4>>2]|0,f?(g&255)>>>1:c[E+-8>>2]|0)|0;g=c[Rb>>2]|0;f=0;while(1){if((f|0)==3)break;c[dc+(f<<2)>>2]=0;f=f+1|0}p=g+-12|0;do if(a[p>>0]&1){o=g+-4|0;a[c[o>>2]>>0]=0;l=g+-8|0;c[l>>2]=0;f=a[p>>0]|0;if(!(f&1))k=10;else{k=c[p>>2]|0;f=k&255;k=(k&-2)+-1|0}if(!(f&1)){g=(f&255)>>>1;if((f&255)<22){n=1;j=10;m=g}else{n=1;j=(g+16&240)+-1|0;m=g}}else{n=0;j=10;m=0}if((j|0)!=(k|0)){if((j|0)==10){h=p+1|0;g=c[o>>2]|0;if(n){Fc(h|0,g|0,((f&255)>>>1)+1|0)|0;wc(g)}else{a[h>>0]=a[g>>0]|0;wc(g)}a[p>>0]=m<<1;break}g=j+1|0;h=vc(g)|0;if(!(j>>>0<=k>>>0&(h|0)==0)){if(n)Fc(h|0,p+1|0,((f&255)>>>1)+1|0)|0;else{cc=c[o>>2]|0;a[h>>0]=a[cc>>0]|0;wc(cc)}c[p>>2]=g|1;c[l>>2]=m;c[o>>2]=h}}}else{a[p+1>>0]=0;a[p>>0]=0}while(0);c[p>>2]=c[dc>>2];c[p+4>>2]=c[dc+4>>2];c[p+8>>2]=c[dc+8>>2];f=0;while(1){if((f|0)==3)break;c[dc+(f<<2)>>2]=0;f=f+1|0}Ja(dc);Ya((c[Rb>>2]|0)+-24|0,797)|0;l=ec+4|0;m=ec+8|0;n=ec+1|0;g=pb;while(1){if((a[g>>0]|0)==69)break;k=ub(g,d,e)|0;if((k|0)==(g|0)|(k|0)==(d|0))break b;f=c[Rb>>2]|0;if((c[e>>2]|0)==(f|0))break b;Cb(ec,f+-24|0);h=c[Rb>>2]|0;j=h+-24|0;f=h;while(1){if((f|0)==(j|0))break;dc=f+-24|0;c[Rb>>2]=dc;Ia(dc);f=c[Rb>>2]|0}g=a[ec>>0]|0;f=(g&1)==0;g=f?(g&255)>>>1:c[l>>2]|0;if(g){if((c[e>>2]|0)==(j|0)){Vb=147;break}Za(h+-48|0,f?n:c[m>>2]|0,g)|0}Ja(ec);g=k}if((Vb|0)==147){Ja(ec);break}f=c[Rb>>2]|0;if((c[e>>2]|0)!=(f|0)){Ya(f+-24|0,799)|0;b=g+1|0}}while(0);break a}case 109:{dc=b+2|0;$a(w,884,1);ec=Gb(dc,d,w,e)|0;Ja(w);b=(ec|0)==(dc|0)?b:ec;break a}case 111:{dc=b+2|0;$a(x,886,1);ec=Hb(dc,d,x,e)|0;Ja(x);b=(ec|0)==(dc|0)?b:ec;break a}case 118:{c:do if((((ja|0)>2?(a[b>>0]|0)==99:0)?(a[b+1>>0]|0)==118:0)?(Yb=e+63|0,Xb=a[Yb>>0]|0,a[Yb>>0]=0,Zb=b+2|0,ma=Na(Zb,d,e)|0,a[Yb>>0]=Xb,!((ma|0)==(Zb|0)|(ma|0)==(d|0))):0){if((a[ma>>0]|0)!=95){f=ub(ma,d,e)|0;if((f|0)==(ma|0))break}else{f=ma+1|0;if((f|0)==(d|0))break;g=a[f>>0]|0;d:do if(g<<24>>24==69){j=e+4|0;h=c[j>>2]|0;Zb=c[e+8>>2]|0;k=Zb;if(h>>>0<Zb>>>0){c[h>>2]=0;c[h+4>>2]=0;c[h+8>>2]=0;c[h+12>>2]=0;c[h+16>>2]=0;c[h+20>>2]=0;g=0;while(1){if((g|0)==3)break;c[h+(g<<2)>>2]=0;g=g+1|0}g=h+12|0;h=0;while(1){if((h|0)==3)break;c[g+(h<<2)>>2]=0;h=h+1|0}c[j>>2]=(c[j>>2]|0)+24;break}g=c[e>>2]|0;Zb=h-g|0;j=(Zb|0)/24|0;h=j+1|0;if((Zb|0)<-24)Pa();g=(k-g|0)/24|0;if(g>>>0<1073741823){g=g<<1;g=g>>>0<h>>>0?h:g}else g=2147483647;ab(dc,g,j,e+12|0);j=dc+8|0;k=c[j>>2]|0;c[k>>2]=0;c[k+4>>2]=0;c[k+8>>2]=0;c[k+12>>2]=0;c[k+16>>2]=0;c[k+20>>2]=0;g=0;while(1){if((g|0)==3)break;c[k+(g<<2)>>2]=0;g=g+1|0}g=k+12|0;h=0;while(1){if((h|0)==3)break;c[g+(h<<2)>>2]=0;h=h+1|0}c[j>>2]=k+24;cb(e,dc);bb(dc)}else while(1){if(g<<24>>24==69)break d;h=ub(f,d,e)|0;if((h|0)==(f|0)|(h|0)==(d|0))break c;g=a[h>>0]|0;f=h}while(0);f=f+1|0}j=e+4|0;g=c[j>>2]|0;if(((g-(c[e>>2]|0)|0)/24|0)>>>0>=2){Cb(dc,g+-24|0);b=c[j>>2]|0;g=b+-24|0;h=b;while(1){if((h|0)==(g|0))break;e=h+-24|0;c[j>>2]=e;Ia(e);h=c[j>>2]|0}h=b+-48|0;Cb(ac,h);b=Ta(ac,0,797)|0;c[_b>>2]=c[b>>2];c[_b+4>>2]=c[b+4>>2];c[_b+8>>2]=c[b+8>>2];g=0;while(1){if((g|0)==3)break;c[b+(g<<2)>>2]=0;g=g+1|0}b=Ya(_b,888)|0;c[$b>>2]=c[b>>2];c[$b+4>>2]=c[b+4>>2];c[$b+8>>2]=c[b+8>>2];g=0;while(1){if((g|0)==3)break;c[b+(g<<2)>>2]=0;g=g+1|0}b=a[dc>>0]|0;g=(b&1)==0;b=Za($b,g?dc+1|0:c[dc+8>>2]|0,g?(b&255)>>>1:c[dc+4>>2]|0)|0;c[bc>>2]=c[b>>2];c[bc+4>>2]=c[b+4>>2];c[bc+8>>2]=c[b+8>>2];g=0;while(1){if((g|0)==3)break;c[b+(g<<2)>>2]=0;g=g+1|0}b=Ya(bc,799)|0;c[cc>>2]=c[b>>2];c[cc+4>>2]=c[b+4>>2];c[cc+8>>2]=c[b+8>>2];g=0;while(1){if((g|0)==3)break;c[b+(g<<2)>>2]=0;g=g+1|0}rb(ec,cc);Db(h,ec);Ia(ec);Ja(cc);Ja(bc);Ja($b);Ja(_b);Ja(ac);Ja(dc);b=f}}while(0);break a}default:break a}case 100:switch(a[la+1>>0]|0){case 97:{ec=la+2|0;p=ub(ec,d,e)|0;if((p|0)==(ec|0))break a;g=e+4|0;h=c[g>>2]|0;if((c[e>>2]|0)==(h|0))break a;o=h+-24|0;e:do if(Ua)$a(Lb,891,2);else{b=0;while(1){if((b|0)==3)break e;c[Lb+(b<<2)>>2]=0;b=b+1|0}}while(0);b=Ya(Lb,894)|0;c[Kb>>2]=c[b>>2];c[Kb+4>>2]=c[b+4>>2];c[Kb+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}Cb(Mb,(c[g>>2]|0)+-24|0);b=a[Mb>>0]|0;f=(b&1)==0;b=Za(Kb,f?Mb+1|0:c[Mb+8>>2]|0,f?(b&255)>>>1:c[Mb+4>>2]|0)|0;c[Ib>>2]=c[b>>2];c[Ib+4>>2]=c[b+4>>2];c[Ib+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}do if(a[o>>0]&1){n=h+-16|0;a[c[n>>2]>>0]=0;k=h+-20|0;c[k>>2]=0;b=a[o>>0]|0;if(!(b&1))j=10;else{j=c[o>>2]|0;b=j&255;j=(j&-2)+-1|0}if(!(b&1)){f=(b&255)>>>1;if((b&255)<22){m=1;h=10;l=f}else{m=1;h=(f+16&240)+-1|0;l=f}}else{m=0;h=10;l=0}if((h|0)!=(j|0)){if((h|0)==10){g=o+1|0;f=c[n>>2]|0;if(m){Fc(g|0,f|0,((b&255)>>>1)+1|0)|0;wc(f)}else{a[g>>0]=a[f>>0]|0;wc(f)}a[o>>0]=l<<1;break}f=h+1|0;g=vc(f)|0;if(!(h>>>0<=j>>>0&(g|0)==0)){if(m)Fc(g|0,o+1|0,((b&255)>>>1)+1|0)|0;else{ec=c[n>>2]|0;a[g>>0]=a[ec>>0]|0;wc(ec)}c[o>>2]=f|1;c[k>>2]=l;c[n>>2]=g}}}else{a[o+1>>0]=0;a[o>>0]=0}while(0);c[o>>2]=c[Ib>>2];c[o+4>>2]=c[Ib+4>>2];c[o+8>>2]=c[Ib+8>>2];b=0;while(1){if((b|0)==3)break;c[Ib+(b<<2)>>2]=0;b=b+1|0}Ja(Ib);Ja(Mb);Ja(Kb);Ja(Lb);b=p;break a}case 99:{if((((((ja|0)>2?(a[b>>0]|0)==100:0)?(a[b+1>>0]|0)==99:0)?(Zb=b+2|0,Y=Na(Zb,d,e)|0,(Y|0)!=(Zb|0)):0)?(_a=ub(Y,d,e)|0,(_a|0)!=(Y|0)):0)?(va=e+4|0,Z=c[va>>2]|0,((Z-(c[e>>2]|0)|0)/24|0)>>>0>=2):0){Cb(dc,Z+-24|0);b=c[va>>2]|0;f=b+-24|0;g=b;while(1){if((g|0)==(f|0))break;e=g+-24|0;c[va>>2]=e;Ia(e);g=c[va>>2]|0}g=b+-48|0;Cb(ac,g);b=Ta(ac,0,904)|0;c[_b>>2]=c[b>>2];c[_b+4>>2]=c[b+4>>2];c[_b+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}b=Ya(_b,881)|0;c[$b>>2]=c[b>>2];c[$b+4>>2]=c[b+4>>2];c[$b+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}b=a[dc>>0]|0;f=(b&1)==0;b=Za($b,f?dc+1|0:c[dc+8>>2]|0,f?(b&255)>>>1:c[dc+4>>2]|0)|0;c[bc>>2]=c[b>>2];c[bc+4>>2]=c[b+4>>2];c[bc+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}b=Ya(bc,799)|0;c[cc>>2]=c[b>>2];c[cc+4>>2]=c[b+4>>2];c[cc+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}rb(ec,cc);Db(g,ec);Ia(ec);Ja(cc);Ja(bc);Ja($b);Ja(_b);Ja(ac);Ja(dc);b=_a}break a}case 101:{dc=b+2|0;$a(y,4262,1);ec=Hb(dc,d,y,e)|0;Ja(y);b=(ec|0)==(dc|0)?b:ec;break a}case 108:{ec=la+2|0;p=ub(ec,d,e)|0;if((p|0)==(ec|0))break a;g=e+4|0;h=c[g>>2]|0;if((c[e>>2]|0)==(h|0))break a;o=h+-24|0;f:do if(Ua)$a(Pb,891,2);else{b=0;while(1){if((b|0)==3)break f;c[Pb+(b<<2)>>2]=0;b=b+1|0}}while(0);b=Ya(Pb,918)|0;c[Ob>>2]=c[b>>2];c[Ob+4>>2]=c[b+4>>2];c[Ob+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}Cb(Qb,(c[g>>2]|0)+-24|0);b=a[Qb>>0]|0;f=(b&1)==0;b=Za(Ob,f?Qb+1|0:c[Qb+8>>2]|0,f?(b&255)>>>1:c[Qb+4>>2]|0)|0;c[Nb>>2]=c[b>>2];c[Nb+4>>2]=c[b+4>>2];c[Nb+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}do if(a[o>>0]&1){n=h+-16|0;a[c[n>>2]>>0]=0;k=h+-20|0;c[k>>2]=0;b=a[o>>0]|0;if(!(b&1))j=10;else{j=c[o>>2]|0;b=j&255;j=(j&-2)+-1|0}if(!(b&1)){f=(b&255)>>>1;if((b&255)<22){m=1;h=10;l=f}else{m=1;h=(f+16&240)+-1|0;l=f}}else{m=0;h=10;l=0}if((h|0)!=(j|0)){if((h|0)==10){g=o+1|0;f=c[n>>2]|0;if(m){Fc(g|0,f|0,((b&255)>>>1)+1|0)|0;wc(f)}else{a[g>>0]=a[f>>0]|0;wc(f)}a[o>>0]=l<<1;break}f=h+1|0;g=vc(f)|0;if(!(h>>>0<=j>>>0&(g|0)==0)){if(m)Fc(g|0,o+1|0,((b&255)>>>1)+1|0)|0;else{ec=c[n>>2]|0;a[g>>0]=a[ec>>0]|0;wc(ec)}c[o>>2]=f|1;c[k>>2]=l;c[n>>2]=g}}}else{a[o+1>>0]=0;a[o>>0]=0}while(0);c[o>>2]=c[Nb>>2];c[o+4>>2]=c[Nb+4>>2];c[o+8>>2]=c[Nb+8>>2];b=0;while(1){if((b|0)==3)break;c[Nb+(b<<2)>>2]=0;b=b+1|0}Ja(Nb);Ja(Qb);Ja(Ob);Ja(Pb);b=p;break a}case 110:{b=Jb(b,d,e)|0;break a}case 115:{if((((((ja|0)>2?(a[b>>0]|0)==100:0)?(a[b+1>>0]|0)==115:0)?(cc=b+2|0,ba=ub(cc,d,e)|0,(ba|0)!=(cc|0)):0)?(wa=ub(ba,d,e)|0,(wa|0)!=(ba|0)):0)?(xa=e+4|0,ca=c[xa>>2]|0,((ca-(c[e>>2]|0)|0)/24|0)>>>0>=2):0){Cb(dc,ca+-24|0);b=c[xa>>2]|0;f=b+-24|0;g=b;while(1){if((g|0)==(f|0))break;cc=g+-24|0;c[xa>>2]=cc;Ia(cc);g=c[xa>>2]|0}xb(ec,1833,dc);cc=a[ec>>0]|0;bc=(cc&1)==0;Za(b+-48|0,bc?ec+1|0:c[ec+8>>2]|0,bc?(cc&255)>>>1:c[ec+4>>2]|0)|0;Ja(ec);Ja(dc);b=wa}break a}case 116:{if((((((ja|0)>2?(a[b>>0]|0)==100:0)?(a[b+1>>0]|0)==116:0)?(cc=b+2|0,ea=ub(cc,d,e)|0,(ea|0)!=(cc|0)):0)?(ya=Jb(ea,d,e)|0,(ya|0)!=(ea|0)):0)?(za=e+4|0,ga=c[za>>2]|0,((ga-(c[e>>2]|0)|0)/24|0)>>>0>=2):0){Cb(dc,ga+-24|0);b=c[za>>2]|0;f=b+-24|0;g=b;while(1){if((g|0)==(f|0))break;cc=g+-24|0;c[za>>2]=cc;Ia(cc);g=c[za>>2]|0}xb(ec,4798,dc);cc=a[ec>>0]|0;bc=(cc&1)==0;Za(b+-48|0,bc?ec+1|0:c[ec+8>>2]|0,bc?(cc&255)>>>1:c[ec+4>>2]|0)|0;Ja(ec);Ja(dc);b=ya}break a}case 118:{dc=b+2|0;$a(B,2368,1);ec=Gb(dc,d,B,e)|0;Ja(B);b=(ec|0)==(dc|0)?b:ec;break a}case 86:{dc=b+2|0;$a(C,1836,2);ec=Gb(dc,d,C,e)|0;Ja(C);b=(ec|0)==(dc|0)?b:ec;break a}default:break a}case 101:switch(a[la+1>>0]|0){case 111:{dc=b+2|0;$a(D,1839,1);ec=Gb(dc,d,D,e)|0;Ja(D);b=(ec|0)==(dc|0)?b:ec;break a}case 79:{dc=b+2|0;$a(F,1841,2);ec=Gb(dc,d,F,e)|0;Ja(F);b=(ec|0)==(dc|0)?b:ec;break a}case 113:{dc=b+2|0;$a(G,1844,2);ec=Gb(dc,d,G,e)|0;Ja(G);b=(ec|0)==(dc|0)?b:ec;break a}default:break a}case 103:switch(a[la+1>>0]|0){case 101:{dc=b+2|0;$a(H,1847,2);ec=Gb(dc,d,H,e)|0;Ja(H);b=(ec|0)==(dc|0)?b:ec;break a}case 116:{dc=b+2|0;$a(I,844,1);ec=Gb(dc,d,I,e)|0;Ja(I);b=(ec|0)==(dc|0)?b:ec;break a}default:break a}case 105:{if((a[la+1>>0]|0)!=120)break a;cc=b+2|0;f=ub(cc,d,e)|0;if((f|0)==(cc|0))break a;j=ub(f,d,e)|0;h=e+4|0;if((j|0)==(f|0)){g=c[h>>2]|0;f=g+-24|0;while(1){if((g|0)==(f|0))break a;ec=g+-24|0;c[h>>2]=ec;Ia(ec);g=c[h>>2]|0}}f=c[h>>2]|0;if(((f-(c[e>>2]|0)|0)/24|0)>>>0<2)break a;Cb(dc,f+-24|0);b=c[h>>2]|0;f=b+-24|0;g=b;while(1){if((g|0)==(f|0))break;cc=g+-24|0;c[h>>2]=cc;Ia(cc);g=c[h>>2]|0}Cb(ec,b+-48|0);g=(c[h>>2]|0)+-24|0;xb(kb,797,ec);b=Ya(kb,1850)|0;c[jb>>2]=c[b>>2];c[jb+4>>2]=c[b+4>>2];c[jb+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}b=a[dc>>0]|0;f=(b&1)==0;b=Za(jb,f?dc+1|0:c[dc+8>>2]|0,f?(b&255)>>>1:c[dc+4>>2]|0)|0;c[hb>>2]=c[b>>2];c[hb+4>>2]=c[b+4>>2];c[hb+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}b=Ya(hb,4264)|0;c[gb>>2]=c[b>>2];c[gb+4>>2]=c[b+4>>2];c[gb+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}rb(fb,gb);Db(g,fb);Ia(fb);Ja(gb);Ja(hb);Ja(jb);Ja(kb);Ja(ec);Ja(dc);b=j;break a}case 108:switch(a[la+1>>0]|0){case 101:{dc=b+2|0;$a(J,1853,2);ec=Gb(dc,d,J,e)|0;Ja(J);b=(ec|0)==(dc|0)?b:ec;break a}case 115:{dc=b+2|0;$a(K,1856,2);ec=Gb(dc,d,K,e)|0;Ja(K);b=(ec|0)==(dc|0)?b:ec;break a}case 83:{dc=b+2|0;$a(L,1859,3);ec=Gb(dc,d,L,e)|0;Ja(L);b=(ec|0)==(dc|0)?b:ec;break a}case 116:{dc=b+2|0;$a(M,1427,1);ec=Gb(dc,d,M,e)|0;Ja(M);b=(ec|0)==(dc|0)?b:ec;break a}default:break a}case 109:switch(a[la+1>>0]|0){case 105:{dc=b+2|0;$a(N,1863,1);ec=Gb(dc,d,N,e)|0;Ja(N);b=(ec|0)==(dc|0)?b:ec;break a}case 73:{dc=b+2|0;$a(O,1865,2);ec=Gb(dc,d,O,e)|0;Ja(O);b=(ec|0)==(dc|0)?b:ec;break a}case 108:{dc=b+2|0;$a(P,4262,1);ec=Gb(dc,d,P,e)|0;Ja(P);b=(ec|0)==(dc|0)?b:ec;break a}case 76:{dc=b+2|0;$a(Q,1868,2);ec=Gb(dc,d,Q,e)|0;Ja(Q);b=(ec|0)==(dc|0)?b:ec;break a}case 109:{f=b+2|0;if((f|0)!=(d|0)?(a[f>>0]|0)==95:0){dc=b+3|0;$a(na,1871,2);ec=Hb(dc,d,na,e)|0;Ja(na);b=(ec|0)==(dc|0)?b:ec;break a}h=ub(f,d,e)|0;if((h|0)==(f|0))break a;f=c[e+4>>2]|0;if((c[e>>2]|0)==(f|0))break a;g=f+-24|0;Cb(La,g);b=Ta(La,0,797)|0;c[Ka>>2]=c[b>>2];c[Ka+4>>2]=c[b+4>>2];c[Ka+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}b=Ya(Ka,1874)|0;c[Ha>>2]=c[b>>2];c[Ha+4>>2]=c[b+4>>2];c[Ha+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}rb(Ga,Ha);Db(g,Ga);Ia(Ga);Ja(Ha);Ja(Ka);Ja(La);b=h;break a}default:break a}case 110:switch(a[la+1>>0]|0){case 119:case 97:{g:do if(ka){f=a[b>>0]|0;if(f<<24>>24==103){s=(a[b+1>>0]|0)==115;g=s?b+2|0:b;f=a[g>>0]|0}else{s=0;g=b}if(f<<24>>24==110){f=a[g+1>>0]|0;switch(f<<24>>24){case 97:case 119:break;default:break g}q=f<<24>>24==97;f=g+2|0;h:do if((f|0)!=(d|0)){p=0;while(1){if((a[f>>0]|0)==95)break;h=ub(f,d,e)|0;f=(h|0)==(f|0);g=(h|0)==(d|0);if(f|g)break h;else{p=p|(f|g)^1;f=h}}Rb=f+1|0;g=Na(Rb,d,e)|0;if(!((g|0)==(Rb|0)|(g|0)==(d|0))){f=a[g>>0]|0;i:do if(!((lb-g|0)>2&f<<24>>24==112))if(f<<24>>24==69){o=0;r=g}else break h;else{if((a[g+1>>0]|0)!=105)break h;f=g+2|0;while(1){if((a[f>>0]|0)==69){o=1;r=f;break i}Rb=f;f=ub(f,d,e)|0;if((f|0)==(Rb|0)|(f|0)==(d|0))break h}}while(0);f=0;while(1){if((f|0)==3)break;c[ec+(f<<2)>>2]=0;f=f+1|0}j:do if(o){n=e+4|0;f=c[n>>2]|0;if((c[e>>2]|0)==(f|0)){g=b;f=1}else{Cb(cc,f+-24|0);k:do if(!(a[ec>>0]&1)){a[ec+1>>0]=0;a[ec>>0]=0}else{k=ec+8|0;g=c[k>>2]|0;a[g>>0]=0;l=ec+4|0;c[l>>2]=0;f=c[ec>>2]|0;m=(f&-2)+-1|0;h=f&255;do if(!(h&1)){f=f>>>1&127;if((h&255)<22){Fc(ec+1|0,g|0,f+1|0)|0;wc(g);break}g=f+16&240;j=g+-1|0;if((j|0)==(m|0))break k;h=vc(g)|0;if(j>>>0<=m>>>0&(h|0)==0)break k;Fc(h|0,ec+1|0,f+1|0)|0;c[ec>>2]=g|1;c[l>>2]=f;c[k>>2]=h;break k}else{a[ec+1>>0]=0;wc(g);f=0}while(0);a[ec>>0]=f<<1}while(0);c[ec>>2]=c[cc>>2];c[ec+4>>2]=c[cc+4>>2];c[ec+8>>2]=c[cc+8>>2];f=0;while(1){if((f|0)==3)break;c[cc+(f<<2)>>2]=0;f=f+1|0}Ja(cc);f=c[n>>2]|0;g=f+-24|0;while(1){if((f|0)==(g|0)){j=e;f=g;Vb=409;break j}cc=f+-24|0;c[n>>2]=cc;Ia(cc);f=c[n>>2]|0}}}else{f=e+4|0;n=f;j=e;f=c[f>>2]|0;Vb=409}while(0);if((Vb|0)==409)if((c[j>>2]|0)==(f|0)){g=b;f=1}else{Cb(bc,f+-24|0);g=c[n>>2]|0;h=g+-24|0;f=g;while(1){if((f|0)==(h|0))break;cc=f+-24|0;c[n>>2]=cc;Ia(cc);f=c[n>>2]|0}f=0;while(1){if((f|0)==3)break;c[$b+(f<<2)>>2]=0;f=f+1|0}l:do if(p)if((c[j>>2]|0)==(h|0)){g=b;f=1}else{Cb(_b,g+-48|0);m:do if(!(a[$b>>0]&1)){a[$b+1>>0]=0;a[$b>>0]=0}else{k=$b+8|0;g=c[k>>2]|0;a[g>>0]=0;l=$b+4|0;c[l>>2]=0;f=c[$b>>2]|0;m=(f&-2)+-1|0;h=f&255;do if(!(h&1)){f=f>>>1&127;if((h&255)<22){Fc($b+1|0,g|0,f+1|0)|0;wc(g);break}g=f+16&240;j=g+-1|0;if((j|0)==(m|0))break m;h=vc(g)|0;if(j>>>0<=m>>>0&(h|0)==0)break m;Fc(h|0,$b+1|0,f+1|0)|0;c[$b>>2]=g|1;c[l>>2]=f;c[k>>2]=h;break m}else{a[$b+1>>0]=0;wc(g);f=0}while(0);a[$b>>0]=f<<1}while(0);c[$b>>2]=c[_b>>2];c[$b+4>>2]=c[_b+4>>2];c[$b+8>>2]=c[_b+8>>2];f=0;while(1){if((f|0)==3)break;c[_b+(f<<2)>>2]=0;f=f+1|0}Ja(_b);g=c[n>>2]|0;f=g+-24|0;while(1){if((g|0)==(f|0)){Vb=434;break l}cc=g+-24|0;c[n>>2]=cc;Ia(cc);g=c[n>>2]|0}}else Vb=434;while(0);if((Vb|0)==434){f=0;while(1){if((f|0)==3)break;c[ac+(f<<2)>>2]=0;f=f+1|0}if(s)Ub(ac,891,2);if(q)Ya(ac,1878)|0;else Ya(ac,1882)|0;if(p){xb(Tb,797,$b);f=Ya(Tb,846)|0;c[Sb>>2]=c[f>>2];c[Sb+4>>2]=c[f+4>>2];c[Sb+8>>2]=c[f+8>>2];g=0;while(1){if((g|0)==3)break;c[f+(g<<2)>>2]=0;g=g+1|0}cc=a[Sb>>0]|0;_b=(cc&1)==0;Za(ac,_b?Sb+1|0:c[Sb+8>>2]|0,_b?(cc&255)>>>1:c[Sb+4>>2]|0)|0;Ja(Sb);Ja(Tb)}cc=a[bc>>0]|0;_b=(cc&1)==0;Za(ac,_b?bc+1|0:c[bc+8>>2]|0,_b?(cc&255)>>>1:c[bc+4>>2]|0)|0;if(o){xb(Wb,849,ec);f=Ya(Wb,799)|0;c[Xb>>2]=c[f>>2];c[Xb+4>>2]=c[f+4>>2];c[Xb+8>>2]=c[f+8>>2];g=0;while(1){if((g|0)==3)break;c[f+(g<<2)>>2]=0;g=g+1|0}cc=a[Xb>>0]|0;_b=(cc&1)==0;Za(ac,_b?Xb+1|0:c[Xb+8>>2]|0,_b?(cc&255)>>>1:c[Xb+4>>2]|0)|0;Ja(Xb);Ja(Wb)};c[Zb>>2]=c[ac>>2];c[Zb+4>>2]=c[ac+4>>2];c[Zb+8>>2]=c[ac+8>>2];f=0;while(1){if((f|0)==3)break;c[ac+(f<<2)>>2]=0;f=f+1|0}rb(Yb,Zb);f=c[n>>2]|0;cc=c[e+8>>2]|0;j=cc;if(f>>>0<cc>>>0){db(f,Yb);c[n>>2]=(c[n>>2]|0)+24}else{g=c[e>>2]|0;cc=f-g|0;k=(cc|0)/24|0;h=k+1|0;if((cc|0)<-24)Pa();f=(j-g|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<h>>>0?h:f}else f=2147483647;ab(dc,f,k,e+12|0);cc=dc+8|0;_b=c[cc>>2]|0;db(_b,Yb);c[cc>>2]=_b+24;cb(e,dc);bb(dc)}Ia(Yb);Ja(Zb);Ja(ac);g=r+1|0;f=0}Ja($b);Ja(bc)}Ja(ec);if(!f){b=g;break g}}}while(0)}}while(0);break a}case 101:{dc=b+2|0;$a(R,1884,2);ec=Gb(dc,d,R,e)|0;Ja(R);b=(ec|0)==(dc|0)?b:ec;break a}case 103:{dc=b+2|0;$a(S,1863,1);ec=Hb(dc,d,S,e)|0;Ja(S);b=(ec|0)==(dc|0)?b:ec;break a}case 116:{dc=b+2|0;$a(T,1887,1);ec=Hb(dc,d,T,e)|0;Ja(T);b=(ec|0)==(dc|0)?b:ec;break a}case 120:{r=b+2|0;f=ub(r,d,e)|0;if((f|0)!=(r|0)?(Fa=c[e+4>>2]|0,(c[e>>2]|0)!=(Fa|0)):0){q=Fa+-24|0;Cb(cc,q);g=Ta(cc,0,1889)|0;c[ec>>2]=c[g>>2];c[ec+4>>2]=c[g+4>>2];c[ec+8>>2]=c[g+8>>2];h=0;while(1){if((h|0)==3)break;c[g+(h<<2)>>2]=0;h=h+1|0}g=Ya(ec,799)|0;c[dc>>2]=c[g>>2];c[dc+4>>2]=c[g+4>>2];c[dc+8>>2]=c[g+8>>2];h=0;while(1){if((h|0)==3)break;c[g+(h<<2)>>2]=0;h=h+1|0}do if(a[q>>0]&1){p=Fa+-16|0;a[c[p>>2]>>0]=0;m=Fa+-20|0;c[m>>2]=0;g=a[q>>0]|0;if(!(g&1))l=10;else{l=c[q>>2]|0;g=l&255;l=(l&-2)+-1|0}if(!(g&1)){h=(g&255)>>>1;if((g&255)<22){k=10;n=h;o=1}else{k=(h+16&240)+-1|0;n=h;o=1}}else{k=10;n=0;o=0}if((k|0)!=(l|0)){if((k|0)==10){j=q+1|0;h=c[p>>2]|0;if(o){Fc(j|0,h|0,((g&255)>>>1)+1|0)|0;wc(h)}else{a[j>>0]=a[h>>0]|0;wc(h)}a[q>>0]=n<<1;break}h=k+1|0;j=vc(h)|0;if(!(k>>>0<=l>>>0&(j|0)==0)){if(o)Fc(j|0,q+1|0,((g&255)>>>1)+1|0)|0;else{bc=c[p>>2]|0;a[j>>0]=a[bc>>0]|0;wc(bc)}c[q>>2]=h|1;c[m>>2]=n;c[p>>2]=j}}}else{a[q+1>>0]=0;a[q>>0]=0}while(0);c[q>>2]=c[dc>>2];c[q+4>>2]=c[dc+4>>2];c[q+8>>2]=c[dc+8>>2];g=0;while(1){if((g|0)==3)break;c[dc+(g<<2)>>2]=0;g=g+1|0}Ja(dc);Ja(ec);Ja(cc)}else f=r;b=(f|0)==(r|0)?b:f;break a}default:break a}case 111:switch(a[la+1>>0]|0){case 110:{b=Jb(b,d,e)|0;break a}case 111:{dc=b+2|0;$a(U,1900,2);ec=Gb(dc,d,U,e)|0;Ja(U);b=(ec|0)==(dc|0)?b:ec;break a}case 114:{dc=b+2|0;$a(V,1903,1);ec=Gb(dc,d,V,e)|0;Ja(V);b=(ec|0)==(dc|0)?b:ec;break a}case 82:{dc=b+2|0;$a(W,1905,2);ec=Gb(dc,d,W,e)|0;Ja(W);b=(ec|0)==(dc|0)?b:ec;break a}default:break a}case 112:switch(a[la+1>>0]|0){case 109:{dc=b+2|0;$a(X,1908,3);ec=Gb(dc,d,X,e)|0;Ja(X);b=(ec|0)==(dc|0)?b:ec;break a}case 108:{dc=b+2|0;$a(_,1912,1);ec=Gb(dc,d,_,e)|0;Ja(_);b=(ec|0)==(dc|0)?b:ec;break a}case 76:{dc=b+2|0;$a($,1914,2);ec=Gb(dc,d,$,e)|0;Ja($);b=(ec|0)==(dc|0)?b:ec;break a}case 112:{f=b+2|0;if((f|0)!=(d|0)?(a[f>>0]|0)==95:0){dc=b+3|0;$a(oa,1917,2);ec=Hb(dc,d,oa,e)|0;Ja(oa);b=(ec|0)==(dc|0)?b:ec;break a}h=ub(f,d,e)|0;if((h|0)==(f|0))break a;f=c[e+4>>2]|0;if((c[e>>2]|0)==(f|0))break a;g=f+-24|0;Cb(Ra,g);b=Ta(Ra,0,797)|0;c[Qa>>2]=c[b>>2];c[Qa+4>>2]=c[b+4>>2];c[Qa+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}b=Ya(Qa,1920)|0;c[Oa>>2]=c[b>>2];c[Oa+4>>2]=c[b+4>>2];c[Oa+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}rb(Ma,Oa);Db(g,Ma);Ia(Ma);Ja(Oa);Ja(Qa);Ja(Ra);b=h;break a}case 115:{dc=b+2|0;$a(aa,1912,1);ec=Hb(dc,d,aa,e)|0;Ja(aa);b=(ec|0)==(dc|0)?b:ec;break a}case 116:{if((ja|0)<=2)break a;if((a[b>>0]|0)!=112)break a;if((a[b+1>>0]|0)!=116)break a;ec=b+2|0;f=ub(ec,d,e)|0;if((f|0)==(ec|0))break a;j=ub(f,d,e)|0;if((j|0)==(f|0))break a;h=e+4|0;f=c[h>>2]|0;if(((f-(c[e>>2]|0)|0)/24|0)>>>0<2)break a;Cb(dc,f+-24|0);b=c[h>>2]|0;f=b+-24|0;g=b;while(1){if((g|0)==(f|0))break;ec=g+-24|0;c[h>>2]=ec;Ia(ec);g=c[h>>2]|0}Ya(b+-48|0,1924)|0;b=a[dc>>0]|0;ec=(b&1)==0;Za((c[h>>2]|0)+-24|0,ec?dc+1|0:c[dc+8>>2]|0,ec?(b&255)>>>1:c[dc+4>>2]|0)|0;Ja(dc);b=j;break a}default:break a}case 113:{if((a[la+1>>0]|0)!=117)break a;bc=b+2|0;f=ub(bc,d,e)|0;if((f|0)==(bc|0))break a;g=ub(f,d,e)|0;if((g|0)==(f|0)){f=e+4|0;h=c[f>>2]|0;g=h+-24|0;while(1){if((h|0)==(g|0))break a;ec=h+-24|0;c[f>>2]=ec;Ia(ec);h=c[f>>2]|0}}h=ub(g,d,e)|0;j=e+4|0;if((h|0)==(g|0)){g=c[j>>2]|0;f=g+-24|0;h=g;while(1){if((h|0)==(f|0))break;ec=h+-24|0;c[j>>2]=ec;Ia(ec);h=c[j>>2]|0}g=g+-48|0;while(1){if((f|0)==(g|0))break a;ec=f+-24|0;c[j>>2]=ec;Ia(ec);f=c[j>>2]|0}}f=c[j>>2]|0;if(((f-(c[e>>2]|0)|0)/24|0)>>>0<3)break a;Cb(dc,f+-24|0);b=c[j>>2]|0;f=b+-24|0;g=b;while(1){if((g|0)==(f|0))break;bc=g+-24|0;c[j>>2]=bc;Ia(bc);g=c[j>>2]|0}Cb(ec,b+-48|0);b=c[j>>2]|0;f=b+-24|0;g=b;while(1){if((g|0)==(f|0))break;bc=g+-24|0;c[j>>2]=bc;Ia(bc);g=c[j>>2]|0}Cb(cc,b+-48|0);g=(c[j>>2]|0)+-24|0;xb(Bb,797,cc);b=Ya(Bb,1927)|0;c[Ab>>2]=c[b>>2];c[Ab+4>>2]=c[b+4>>2];c[Ab+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}b=a[ec>>0]|0;f=(b&1)==0;b=Za(Ab,f?ec+1|0:c[ec+8>>2]|0,f?(b&255)>>>1:c[ec+4>>2]|0)|0;c[zb>>2]=c[b>>2];c[zb+4>>2]=c[b+4>>2];c[zb+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}b=Ya(zb,1933)|0;c[yb>>2]=c[b>>2];c[yb+4>>2]=c[b+4>>2];c[yb+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}b=a[dc>>0]|0;f=(b&1)==0;b=Za(yb,f?dc+1|0:c[dc+8>>2]|0,f?(b&255)>>>1:c[dc+4>>2]|0)|0;c[wb>>2]=c[b>>2];c[wb+4>>2]=c[b+4>>2];c[wb+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}b=Ya(wb,799)|0;c[tb>>2]=c[b>>2];c[tb+4>>2]=c[b+4>>2];c[tb+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}rb(sb,tb);Db(g,sb);Ia(sb);Ja(tb);Ja(wb);Ja(yb);Ja(zb);Ja(Ab);Ja(Bb);Ja(cc);Ja(ec);Ja(dc);b=h;break a}case 114:switch(a[la+1>>0]|0){case 99:{if((((((ja|0)>2?(a[b>>0]|0)==114:0)?(a[b+1>>0]|0)==99:0)?(Zb=b+2|0,h=Na(Zb,d,e)|0,(h|0)!=(Zb|0)):0)?(Va=ub(h,d,e)|0,(Va|0)!=(h|0)):0)?(pa=e+4|0,j=c[pa>>2]|0,((j-(c[e>>2]|0)|0)/24|0)>>>0>=2):0){Cb(dc,j+-24|0);b=c[pa>>2]|0;f=b+-24|0;g=b;while(1){if((g|0)==(f|0))break;e=g+-24|0;c[pa>>2]=e;Ia(e);g=c[pa>>2]|0}g=b+-48|0;Cb(ac,g);b=Ta(ac,0,1939)|0;c[_b>>2]=c[b>>2];c[_b+4>>2]=c[b+4>>2];c[_b+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}b=Ya(_b,881)|0;c[$b>>2]=c[b>>2];c[$b+4>>2]=c[b+4>>2];c[$b+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}b=a[dc>>0]|0;f=(b&1)==0;b=Za($b,f?dc+1|0:c[dc+8>>2]|0,f?(b&255)>>>1:c[dc+4>>2]|0)|0;c[bc>>2]=c[b>>2];c[bc+4>>2]=c[b+4>>2];c[bc+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}b=Ya(bc,799)|0;c[cc>>2]=c[b>>2];c[cc+4>>2]=c[b+4>>2];c[cc+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}rb(ec,cc);Db(g,ec);Ia(ec);Ja(cc);Ja(bc);Ja($b);Ja(_b);Ja(ac);Ja(dc);b=Va}break a}case 109:{dc=b+2|0;$a(da,1957,1);ec=Gb(dc,d,da,e)|0;Ja(da);b=(ec|0)==(dc|0)?b:ec;break a}case 77:{dc=b+2|0;$a(fa,1959,2);ec=Gb(dc,d,fa,e)|0;Ja(fa);b=(ec|0)==(dc|0)?b:ec;break a}case 115:{dc=b+2|0;$a(ha,1962,2);ec=Gb(dc,d,ha,e)|0;Ja(ha);b=(ec|0)==(dc|0)?b:ec;break a}case 83:{dc=b+2|0;$a(ia,1965,3);ec=Gb(dc,d,ia,e)|0;Ja(ia);b=(ec|0)==(dc|0)?b:ec;break a}default:break a}case 115:switch(a[la+1>>0]|0){case 99:{if((((((ja|0)>2?(a[b>>0]|0)==115:0)?(a[b+1>>0]|0)==99:0)?(Zb=b+2|0,l=Na(Zb,d,e)|0,(l|0)!=(Zb|0)):0)?(Wa=ub(l,d,e)|0,(Wa|0)!=(l|0)):0)?(qa=e+4|0,m=c[qa>>2]|0,((m-(c[e>>2]|0)|0)/24|0)>>>0>=2):0){Cb(dc,m+-24|0);b=c[qa>>2]|0;f=b+-24|0;g=b;while(1){if((g|0)==(f|0))break;e=g+-24|0;c[qa>>2]=e;Ia(e);g=c[qa>>2]|0}g=b+-48|0;Cb(ac,g);b=Ta(ac,0,1969)|0;c[_b>>2]=c[b>>2];c[_b+4>>2]=c[b+4>>2];c[_b+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}b=Ya(_b,881)|0;c[$b>>2]=c[b>>2];c[$b+4>>2]=c[b+4>>2];c[$b+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}b=a[dc>>0]|0;f=(b&1)==0;b=Za($b,f?dc+1|0:c[dc+8>>2]|0,f?(b&255)>>>1:c[dc+4>>2]|0)|0;c[bc>>2]=c[b>>2];c[bc+4>>2]=c[b+4>>2];c[bc+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}b=Ya(bc,799)|0;c[cc>>2]=c[b>>2];c[cc+4>>2]=c[b+4>>2];c[cc+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}rb(ec,cc);Db(g,ec);Ia(ec);Ja(cc);Ja(bc);Ja($b);Ja(_b);Ja(ac);Ja(dc);b=Wa}break a}case 112:{if((ja|0)<=2)break a;if((a[b>>0]|0)!=115)break a;if((a[b+1>>0]|0)!=112)break a;dc=b+2|0;ec=ub(dc,d,e)|0;b=(ec|0)==(dc|0)?b:ec;break a}case 114:{b=Jb(b,d,e)|0;break a}case 116:{if(((((ja|0)>2?(a[b>>0]|0)==115:0)?(a[b+1>>0]|0)==116:0)?(ac=b+2|0,Aa=Na(ac,d,e)|0,(Aa|0)!=(ac|0)):0)?(o=c[e+4>>2]|0,(c[e>>2]|0)!=(o|0)):0){g=o+-24|0;Cb(bc,g);b=Ta(bc,0,1982)|0;c[cc>>2]=c[b>>2];c[cc+4>>2]=c[b+4>>2];c[cc+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}b=Ya(cc,799)|0;c[ec>>2]=c[b>>2];c[ec+4>>2]=c[b+4>>2];c[ec+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}rb(dc,ec);Db(g,dc);Ia(dc);Ja(ec);Ja(cc);Ja(bc);b=Aa}break a}case 122:{if(((((ja|0)>2?(a[b>>0]|0)==115:0)?(a[b+1>>0]|0)==122:0)?(ac=b+2|0,Ba=ub(ac,d,e)|0,(Ba|0)!=(ac|0)):0)?(p=c[e+4>>2]|0,(c[e>>2]|0)!=(p|0)):0){g=p+-24|0;Cb(bc,g);b=Ta(bc,0,1982)|0;c[cc>>2]=c[b>>2];c[cc+4>>2]=c[b+4>>2];c[cc+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}b=Ya(cc,799)|0;c[ec>>2]=c[b>>2];c[ec+4>>2]=c[b+4>>2];c[ec+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}rb(dc,ec);Db(g,dc);Ia(dc);Ja(ec);Ja(cc);Ja(bc);b=Ba}break a}case 90:{if((lb-la|0)<=2)break a;switch(a[la+2>>0]|0){case 84:break;case 102:{if((((((ja|0)>2?(a[b>>0]|0)==115:0)?(a[b+1>>0]|0)==90:0)?(s=b+2|0,(a[s>>0]|0)==102):0)?(Ca=Fb(s,d,e)|0,(Ca|0)!=(s|0)):0)?(t=c[e+4>>2]|0,(c[e>>2]|0)!=(t|0)):0){g=t+-24|0;Cb(bc,g);b=Ta(bc,0,1991)|0;c[cc>>2]=c[b>>2];c[cc+4>>2]=c[b+4>>2];c[cc+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}b=Ya(cc,799)|0;c[ec>>2]=c[b>>2];c[ec+4>>2]=c[b+4>>2];c[ec+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}rb(dc,ec);Db(g,dc);Ia(dc);Ja(ec);Ja(cc);Ja(bc);b=Ca}break a}default:break a}if(((((ja|0)>2?(a[b>>0]|0)==115:0)?(a[b+1>>0]|0)==90:0)?(q=b+2|0,(a[q>>0]|0)==84):0)?(mb=e+4|0,eb=((c[mb>>2]|0)-(c[e>>2]|0)|0)/24|0,qb=Eb(q,d,e)|0,ra=c[e>>2]|0,g=((c[mb>>2]|0)-ra|0)/24|0,ra,(qb|0)!=(q|0)):0){a[ec>>0]=20;b=ec+1|0;f=1991;h=b+10|0;do{a[b>>0]=a[f>>0]|0;b=b+1|0;f=f+1|0}while((b|0)<(h|0));a[ec+11>>0]=0;n:do if((eb|0)!=(g|0)){Cb(cc,ra+(eb*24|0)|0);j=a[cc>>0]|0;k=(j&1)==0;Za(ec,k?cc+1|0:c[cc+8>>2]|0,k?(j&255)>>>1:c[cc+4>>2]|0)|0;Ja(cc);j=bc+8|0;k=bc+1|0;l=bc+4|0;b=eb;while(1){b=b+1|0;if((b|0)==(g|0))break n;Cb($b,(c[e>>2]|0)+(b*24|0)|0);f=Ta($b,0,1429)|0;c[bc>>2]=c[f>>2];c[bc+4>>2]=c[f+4>>2];c[bc+8>>2]=c[f+8>>2];h=0;while(1){if((h|0)==3)break;c[f+(h<<2)>>2]=0;h=h+1|0}cc=a[bc>>0]|0;Zb=(cc&1)==0;Za(ec,Zb?k:c[j>>2]|0,Zb?(cc&255)>>>1:c[l>>2]|0)|0;Ja(bc);Ja($b)}}while(0);Ya(ec,799)|0;while(1){if((g|0)==(eb|0))break;f=c[mb>>2]|0;b=f+-24|0;while(1){if((f|0)==(b|0))break;cc=f+-24|0;c[mb>>2]=cc;Ia(cc);f=c[mb>>2]|0}g=g+-1|0}c[ac>>2]=c[ec>>2];c[ac+4>>2]=c[ec+4>>2];c[ac+8>>2]=c[ec+8>>2];b=0;while(1){if((b|0)==3)break;c[ec+(b<<2)>>2]=0;b=b+1|0}rb(_b,ac);b=c[mb>>2]|0;cc=c[e+8>>2]|0;h=cc;if(b>>>0<cc>>>0){db(b,_b);c[mb>>2]=(c[mb>>2]|0)+24}else{f=c[e>>2]|0;cc=b-f|0;j=(cc|0)/24|0;g=j+1|0;if((cc|0)<-24)Pa();b=(h-f|0)/24|0;if(b>>>0<1073741823){b=b<<1;b=b>>>0<g>>>0?g:b}else b=2147483647;ab(dc,b,j,e+12|0);cc=dc+8|0;bc=c[cc>>2]|0;db(bc,_b);c[cc>>2]=bc+24;cb(e,dc);bb(dc)}Ia(_b);Ja(ac);Ja(ec);b=qb}break a}default:break a}case 116:switch(a[la+1>>0]|0){case 105:case 101:{o:do if((ja|0)>2?(a[b>>0]|0)==116:0){f=a[b+1>>0]|0;switch(f<<24>>24){case 105:case 101:break;default:break o}g=b+2|0;if(f<<24>>24==101)h=ub(g,d,e)|0;else h=Na(g,d,e)|0;if((h|0)!=(g|0)?(sa=c[e+4>>2]|0,(c[e>>2]|0)!=(sa|0)):0){g=sa+-24|0;Cb(bc,g);b=Ta(bc,0,2002)|0;c[cc>>2]=c[b>>2];c[cc+4>>2]=c[b+4>>2];c[cc+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}b=Ya(cc,799)|0;c[ec>>2]=c[b>>2];c[ec+4>>2]=c[b+4>>2];c[ec+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}rb(dc,ec);Db(g,dc);Ia(dc);Ja(ec);Ja(cc);Ja(bc);b=h}}while(0);break a}case 114:{ib(Sa,2010);f=e+4|0;g=c[f>>2]|0;ec=c[e+8>>2]|0;h=ec;if(g>>>0<ec>>>0){db(g,Sa);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;ec=g-f|0;j=(ec|0)/24|0;g=j+1|0;if((ec|0)<-24)Pa();f=(h-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<g>>>0?g:f}else f=2147483647;ab(dc,f,j,e+12|0);ec=dc+8|0;cc=c[ec>>2]|0;db(cc,Sa);c[ec>>2]=cc+24;cb(e,dc);bb(dc)}Ia(Sa);b=b+2|0;break a}case 119:{if(((((ja|0)>2?(a[b>>0]|0)==116:0)?(a[b+1>>0]|0)==119:0)?(bc=b+2|0,ta=ub(bc,d,e)|0,(ta|0)!=(bc|0)):0)?(v=c[e+4>>2]|0,(c[e>>2]|0)!=(v|0)):0){b=v+-24|0;Cb(cc,b);f=Ta(cc,0,2016)|0;c[ec>>2]=c[f>>2];c[ec+4>>2]=c[f+4>>2];c[ec+8>>2]=c[f+8>>2];g=0;while(1){if((g|0)==3)break;c[f+(g<<2)>>2]=0;g=g+1|0}rb(dc,ec);Db(b,dc);Ia(dc);Ja(ec);Ja(cc);b=ta}break a}default:break a}case 57:case 56:case 55:case 54:case 53:case 52:case 51:case 50:case 49:{b=Jb(b,d,e)|0;break a}default:break a}while(0)}while(0);i=fc;return b|0}function vb(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0;O=i;i=i+480|0;J=O+72|0;I=O+48|0;H=O+24|0;L=O;K=O+432|0;M=O+408|0;N=O+384|0;r=O+396|0;y=O+360|0;z=O+336|0;s=O+320|0;t=O+308|0;u=O+296|0;v=O+284|0;f=O+272|0;j=O+260|0;k=O+248|0;l=O+236|0;m=O+224|0;n=O+212|0;o=O+200|0;p=O+188|0;q=O+176|0;A=O+152|0;B=O+140|0;C=O+128|0;D=O+116|0;E=O+104|0;F=O+92|0;x=d;a:do if((x-b|0)>3?(a[b>>0]|0)==76:0){w=b+1|0;do switch(a[w>>0]|0){case 84:break a;case 119:{N=b+2|0;$a(r,481,7);e=wb(N,d,r,e)|0;Ja(r);b=(e|0)==(N|0)?b:e;break a}case 98:{if((a[b+3>>0]|0)!=69)break a;switch(a[b+2>>0]|0){case 48:{ib(y,801);f=e+4|0;j=c[f>>2]|0;N=c[e+8>>2]|0;k=N;if(j>>>0<N>>>0){db(j,y);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;N=j-f|0;l=(N|0)/24|0;j=l+1|0;if((N|0)<-24)Pa();f=(k-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<j>>>0?j:f}else f=2147483647;ab(L,f,l,e+12|0);N=L+8|0;M=c[N>>2]|0;db(M,y);c[N>>2]=M+24;cb(e,L);bb(L)}Ia(y);b=b+4|0;break a}case 49:{fb(z,807);f=e+4|0;j=c[f>>2]|0;N=c[e+8>>2]|0;k=N;if(j>>>0<N>>>0){db(j,z);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;N=j-f|0;l=(N|0)/24|0;j=l+1|0;if((N|0)<-24)Pa();f=(k-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<j>>>0?j:f}else f=2147483647;ab(L,f,l,e+12|0);N=L+8|0;M=c[N>>2]|0;db(M,z);c[N>>2]=M+24;cb(e,L);bb(L)}Ia(z);b=b+4|0;break a}default:break a}}case 99:{N=b+2|0;$a(s,494,4);e=wb(N,d,s,e)|0;Ja(s);b=(e|0)==(N|0)?b:e;break a}case 97:{N=b+2|0;$a(t,499,11);e=wb(N,d,t,e)|0;Ja(t);b=(e|0)==(N|0)?b:e;break a}case 104:{N=b+2|0;$a(u,511,13);e=wb(N,d,u,e)|0;Ja(u);b=(e|0)==(N|0)?b:e;break a}case 115:{N=b+2|0;$a(v,525,5);e=wb(N,d,v,e)|0;Ja(v);b=(e|0)==(N|0)?b:e;break a}case 116:{N=b+2|0;$a(f,531,14);e=wb(N,d,f,e)|0;Ja(f);b=(e|0)==(N|0)?b:e;break a}case 105:{N=b+2|0;$a(j,5344,0);e=wb(N,d,j,e)|0;Ja(j);b=(e|0)==(N|0)?b:e;break a}case 106:{N=b+2|0;$a(k,812,1);e=wb(N,d,k,e)|0;Ja(k);b=(e|0)==(N|0)?b:e;break a}case 108:{N=b+2|0;$a(l,814,1);e=wb(N,d,l,e)|0;Ja(l);b=(e|0)==(N|0)?b:e;break a}case 109:{N=b+2|0;$a(m,816,2);e=wb(N,d,m,e)|0;Ja(m);b=(e|0)==(N|0)?b:e;break a}case 120:{N=b+2|0;$a(n,819,2);e=wb(N,d,n,e)|0;Ja(n);b=(e|0)==(N|0)?b:e;break a}case 121:{N=b+2|0;$a(o,822,3);e=wb(N,d,o,e)|0;Ja(o);b=(e|0)==(N|0)?b:e;break a}case 110:{N=b+2|0;$a(p,611,8);e=wb(N,d,p,e)|0;Ja(p);b=(e|0)==(N|0)?b:e;break a}case 111:{N=b+2|0;$a(q,620,17);e=wb(N,d,q,e)|0;Ja(q);b=(e|0)==(N|0)?b:e;break a}case 102:{m=b+2|0;b:do if((x-m|0)>>>0>8){k=b+10|0;f=L;l=m;while(1){j=a[l>>0]|0;if((l|0)==(k|0)){G=41;break}j=j<<24>>24;if(!(fc(j)|0))break;J=a[l+1>>0]|0;a[f>>0]=(((J<<24>>24)+-48|0)>>>0<10?208:169)+(J&255)+(((j+-48|0)>>>0<10?0:9)+j<<4);f=f+1|0;l=l+2|0}do if((G|0)==41){if(j<<24>>24==69){c:do if((L|0)!=(f|0)){j=L;while(1){f=f+-1|0;if(j>>>0>=f>>>0)break c;J=a[j>>0]|0;a[j>>0]=a[f>>0]|0;a[f>>0]=J;j=j+1|0}}while(0);f=K;j=f+24|0;do{a[f>>0]=0;f=f+1|0}while((f|0)<(j|0));h[H>>3]=+g[L>>2];f=jc(K,24,826,H)|0;if(f>>>0>23)break;$a(N,K,f);rb(M,N);f=e+4|0;j=c[f>>2]|0;L=c[e+8>>2]|0;k=L;if(j>>>0<L>>>0){db(j,M);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;L=j-f|0;l=(L|0)/24|0;j=l+1|0;if((L|0)<-24)Pa();f=(k-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<j>>>0?j:f}else f=2147483647;ab(H,f,l,e+12|0);L=H+8|0;K=c[L>>2]|0;db(K,M);c[L>>2]=K+24;cb(e,H);bb(H)}Ia(M);Ja(N);f=b+11|0}else f=m;break b}while(0);f=m}else f=m;while(0);b=(f|0)==(m|0)?b:f;break a}case 100:{m=b+2|0;d:do if((x-m|0)>>>0>16){k=b+18|0;f=L;l=m;while(1){j=a[l>>0]|0;if((l|0)==(k|0)){G=63;break}j=j<<24>>24;if(!(fc(j)|0))break;J=a[l+1>>0]|0;a[f>>0]=(((J<<24>>24)+-48|0)>>>0<10?208:169)+(J&255)+(((j+-48|0)>>>0<10?0:9)+j<<4);f=f+1|0;l=l+2|0}do if((G|0)==63){if(j<<24>>24==69){e:do if((L|0)!=(f|0)){j=L;while(1){f=f+-1|0;if(j>>>0>=f>>>0)break e;J=a[j>>0]|0;a[j>>0]=a[f>>0]|0;a[f>>0]=J;j=j+1|0}}while(0);f=K;j=f+32|0;do{a[f>>0]=0;f=f+1|0}while((f|0)<(j|0));h[I>>3]=+h[L>>3];f=jc(K,32,830,I)|0;if(f>>>0>31)break;$a(N,K,f);rb(M,N);f=e+4|0;j=c[f>>2]|0;L=c[e+8>>2]|0;k=L;if(j>>>0<L>>>0){db(j,M);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;L=j-f|0;l=(L|0)/24|0;j=l+1|0;if((L|0)<-24)Pa();f=(k-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<j>>>0?j:f}else f=2147483647;ab(I,f,l,e+12|0);L=I+8|0;K=c[L>>2]|0;db(K,M);c[L>>2]=K+24;cb(e,I);bb(I)}Ia(M);Ja(N);f=b+19|0}else f=m;break d}while(0);f=m}else f=m;while(0);b=(f|0)==(m|0)?b:f;break a}case 101:{m=b+2|0;f:do if((x-m|0)>>>0>20){k=b+22|0;f=L;l=m;while(1){j=a[l>>0]|0;if((l|0)==(k|0)){G=85;break}j=j<<24>>24;if(!(fc(j)|0))break;I=a[l+1>>0]|0;a[f>>0]=(((I<<24>>24)+-48|0)>>>0<10?208:169)+(I&255)+(((j+-48|0)>>>0<10?0:9)+j<<4);f=f+1|0;l=l+2|0}do if((G|0)==85){if(j<<24>>24==69){g:do if((L|0)!=(f|0)){j=L;while(1){f=f+-1|0;if(j>>>0>=f>>>0)break g;I=a[j>>0]|0;a[j>>0]=a[f>>0]|0;a[f>>0]=I;j=j+1|0}}while(0);f=K;j=f+40|0;do{a[f>>0]=0;f=f+1|0}while((f|0)<(j|0));h[J>>3]=+h[L>>3];f=jc(K,40,833,J)|0;if(f>>>0>39)break;$a(N,K,f);rb(M,N);f=e+4|0;j=c[f>>2]|0;L=c[e+8>>2]|0;k=L;if(j>>>0<L>>>0){db(j,M);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;L=j-f|0;l=(L|0)/24|0;j=l+1|0;if((L|0)<-24)Pa();f=(k-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<j>>>0?j:f}else f=2147483647;ab(J,f,l,e+12|0);L=J+8|0;K=c[L>>2]|0;db(K,M);c[L>>2]=K+24;cb(e,J);bb(J)}Ia(M);Ja(N);f=b+23|0}else f=m;break f}while(0);f=m}else f=m;while(0);b=(f|0)==(m|0)?b:f;break a}case 95:{if((a[b+2>>0]|0)!=90)break a;N=b+3|0;f=Ma(N,d,e)|0;if((f|0)==(N|0)|(f|0)==(d|0))break a;b=(a[f>>0]|0)==69?f+1|0:b;break a}default:{m=Na(w,d,e)|0;if((m|0)==(w|0)|(m|0)==(d|0))break a;if((a[m>>0]|0)==69){b=m+1|0;break a}else n=m;while(1){if((n|0)==(d|0))break a;f=a[n>>0]|0;if(((f<<24>>24)+-48|0)>>>0>=10)break;n=n+1|0}if(!((n|0)!=(m|0)&f<<24>>24==69))break a;f=c[e+4>>2]|0;if((c[e>>2]|0)==(f|0))break a;l=f+-24|0;Cb(E,l);b=Ta(E,0,797)|0;c[D>>2]=c[b>>2];c[D+4>>2]=c[b+4>>2];c[D+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}b=Ya(D,799)|0;c[C>>2]=c[b>>2];c[C+4>>2]=c[b+4>>2];c[C+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}j=n-m|0;if(j>>>0>4294967279)Xa();if(j>>>0<11){a[F>>0]=j<<1;k=F+1|0}else{e=j+16&-16;k=vc(e)|0;c[F+8>>2]=k;c[F>>2]=e|1;c[F+4>>2]=j}b=m;f=k;while(1){if((b|0)==(n|0))break;a[f>>0]=a[b>>0]|0;b=b+1|0;f=f+1|0}a[k+j>>0]=0;b=a[F>>0]|0;f=(b&1)==0;b=Za(C,f?F+1|0:c[F+8>>2]|0,f?(b&255)>>>1:c[F+4>>2]|0)|0;c[B>>2]=c[b>>2];c[B+4>>2]=c[b+4>>2];c[B+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}rb(A,B);Db(l,A);Ia(A);Ja(B);Ja(F);Ja(C);Ja(D);Ja(E);b=n+1|0;break a}}while(0)}while(0);i=O;return b|0}function wb(b,d,e,f){b=b|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0;s=i;i=i+80|0;p=s+48|0;m=s+24|0;n=s+12|0;o=s;r=tb(b,d)|0;if(!((r|0)==(b|0)|(r|0)==(d|0))?(a[r>>0]|0)==69:0){l=a[e>>0]|0;q=e+4|0;do if(((l&1)==0?(l&255)>>>1:c[q>>2]|0)>>>0>3){xb(o,797,e);d=Ya(o,799)|0;c[n>>2]=c[d>>2];c[n+4>>2]=c[d+4>>2];c[n+8>>2]=c[d+8>>2];g=0;while(1){if((g|0)==3)break;c[d+(g<<2)>>2]=0;g=g+1|0}rb(m,n);g=f+4|0;d=c[g>>2]|0;l=c[f+8>>2]|0;h=l;if(d>>>0<l>>>0){db(d,m);c[g>>2]=(c[g>>2]|0)+24}else{j=c[f>>2]|0;d=d-j|0;l=(d|0)/24|0;k=l+1|0;if((d|0)<-24)Pa();d=(h-j|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<k>>>0?k:d}else d=2147483647;ab(p,d,l,f+12|0);l=p+8|0;k=c[l>>2]|0;db(k,m);c[l>>2]=k+24;cb(f,p);bb(p)}Ia(m);Ja(n);Ja(o)}else{k=f+4|0;g=c[k>>2]|0;o=c[f+8>>2]|0;d=o;if(g>>>0<o>>>0){c[g>>2]=0;c[g+4>>2]=0;c[g+8>>2]=0;c[g+12>>2]=0;c[g+16>>2]=0;c[g+20>>2]=0;d=0;while(1){if((d|0)==3)break;c[g+(d<<2)>>2]=0;d=d+1|0}d=g+12|0;g=0;while(1){if((g|0)==3)break;c[d+(g<<2)>>2]=0;g=g+1|0}c[k>>2]=(c[k>>2]|0)+24;g=k;break}h=c[f>>2]|0;o=g-h|0;j=(o|0)/24|0;g=j+1|0;if((o|0)<-24)Pa();d=(d-h|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<g>>>0?g:d}else d=2147483647;ab(p,d,j,f+12|0);h=p+8|0;j=c[h>>2]|0;c[j>>2]=0;c[j+4>>2]=0;c[j+8>>2]=0;c[j+12>>2]=0;c[j+16>>2]=0;c[j+20>>2]=0;d=0;while(1){if((d|0)==3)break;c[j+(d<<2)>>2]=0;d=d+1|0}d=j+12|0;g=0;while(1){if((g|0)==3)break;c[d+(g<<2)>>2]=0;g=g+1|0}c[h>>2]=j+24;cb(f,p);bb(p);g=k}while(0);if((a[b>>0]|0)==110){zb((c[g>>2]|0)+-24|0,45);b=b+1|0}Bb((c[g>>2]|0)+-24|0,b,r);b=a[e>>0]|0;d=(b&1)==0;b=d?(b&255)>>>1:c[q>>2]|0;if(b>>>0<4)Za((c[g>>2]|0)+-24|0,d?e+1|0:c[e+8>>2]|0,b)|0;b=r+1|0}i=s;return b|0}function xb(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0;f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}g=bc(d)|0;f=a[e>>0]|0;f=(f&1)==0?(f&255)>>>1:c[e+4>>2]|0;yb(b,d,g,f+g|0);Za(b,(a[e>>0]&1)==0?e+1|0:c[e+8>>2]|0,f)|0;return}function yb(b,d,e,f){b=b|0;d=d|0;e=e|0;f=f|0;var g=0;if(f>>>0>4294967279)Xa();if(f>>>0<11){a[b>>0]=e<<1;f=b+1|0}else{g=f+16&-16;f=vc(g)|0;c[b+8>>2]=f;c[b>>2]=g|1;c[b+4>>2]=e}Fc(f|0,d|0,e|0)|0;a[f+e>>0]=0;return}function zb(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0;e=a[b>>0]|0;f=(e&1)!=0;if(f){g=(c[b>>2]&-2)+-1|0;h=c[b+4>>2]|0}else{g=10;h=(e&255)>>>1}if((h|0)==(g|0)){Ab(b,g,1,g,g,0);if(!(a[b>>0]&1))f=7;else f=8}else if(f)f=8;else f=7;if((f|0)==7){a[b>>0]=(h<<1)+2;e=b+1|0}else if((f|0)==8){e=c[b+8>>2]|0;c[b+4>>2]=h+1}b=e+h|0;a[b>>0]=d;a[b+1>>0]=0;return}function Ab(b,d,e,f,g,h){b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;var i=0,j=0;if((-17-d|0)>>>0<e>>>0)Xa();if(!(a[b>>0]&1))j=b+1|0;else j=c[b+8>>2]|0;if(d>>>0<2147483623){e=e+d|0;i=d<<1;e=e>>>0<i>>>0?i:e;e=e>>>0<11?11:e+16&-16}else e=-17;i=vc(e)|0;if(g)Fc(i|0,j|0,g|0)|0;if((f|0)!=(g|0))Fc(i+g+h|0,j+g|0,f-g|0)|0;if((d|0)!=10)wc(j);c[b+8>>2]=i;c[b>>2]=e|1;return}function Bb(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,i=0,j=0,k=0;h=d;f=a[b>>0]|0;if(!(f&1)){k=(f&255)>>>1;g=10}else{f=c[b>>2]|0;k=c[b+4>>2]|0;g=(f&-2)+-1|0;f=f&255}j=e-h|0;do if((e|0)!=(d|0)){if((g-k|0)>>>0<j>>>0){Ab(b,g,k+j-g|0,k,k,0);f=a[b>>0]|0}if(!(f&1))i=b+1|0;else i=c[b+8>>2]|0;h=e+(k-h)|0;f=d;g=i+k|0;while(1){if((f|0)==(e|0))break;a[g>>0]=a[f>>0]|0;f=f+1|0;g=g+1|0}a[i+h>>0]=0;f=k+j|0;if(!(a[b>>0]&1)){a[b>>0]=f<<1;break}else{c[b+4>>2]=f;break}}while(0);return}function Cb(b,d){b=b|0;d=d|0;var e=0,f=0,g=0;g=d+12|0;e=a[g>>0]|0;f=(e&1)==0;e=Za(d,f?g+1|0:c[d+20>>2]|0,f?(e&255)>>>1:c[d+16>>2]|0)|0;c[b>>2]=c[e>>2];c[b+4>>2]=c[e+4>>2];c[b+8>>2]=c[e+8>>2];d=0;while(1){if((d|0)==3)break;c[e+(d<<2)>>2]=0;d=d+1|0}return}function Db(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0;do if(a[b>>0]&1){m=b+8|0;a[c[m>>2]>>0]=0;k=b+4|0;c[k>>2]=0;e=a[b>>0]|0;if(!(e&1))i=10;else{i=c[b>>2]|0;e=i&255;i=(i&-2)+-1|0}if(!(e&1)){f=(e&255)>>>1;if((e&255)<22){h=10;j=f;l=1}else{h=(f+16&240)+-1|0;j=f;l=1}}else{h=10;j=0;l=0}if((h|0)!=(i|0)){if((h|0)==10){g=b+1|0;f=c[m>>2]|0;if(l){Fc(g|0,f|0,((e&255)>>>1)+1|0)|0;wc(f)}else{a[g>>0]=a[f>>0]|0;wc(f)}a[b>>0]=j<<1;break}f=h+1|0;g=vc(f)|0;if(!(h>>>0<=i>>>0&(g|0)==0)){if(l)Fc(g|0,b+1|0,((e&255)>>>1)+1|0)|0;else{n=c[m>>2]|0;a[g>>0]=a[n>>0]|0;wc(n)}c[b>>2]=f|1;c[k>>2]=j;c[m>>2]=g}}}else{a[b+1>>0]=0;a[b>>0]=0}while(0);c[b>>2]=c[d>>2];c[b+4>>2]=c[d+4>>2];c[b+8>>2]=c[d+8>>2];e=0;while(1){if((e|0)==3)break;c[d+(e<<2)>>2]=0;e=e+1|0}n=b+12|0;d=d+12|0;do if(a[n>>0]&1){m=b+20|0;a[c[m>>2]>>0]=0;j=b+16|0;c[j>>2]=0;e=a[n>>0]|0;if(!(e&1))i=10;else{i=c[n>>2]|0;e=i&255;i=(i&-2)+-1|0}if(!(e&1)){f=(e&255)>>>1;if((e&255)<22){h=10;k=f;l=1}else{h=(f+16&240)+-1|0;k=f;l=1}}else{h=10;k=0;l=0}if((h|0)!=(i|0)){if((h|0)==10){g=n+1|0;f=c[m>>2]|0;if(l){Fc(g|0,f|0,((e&255)>>>1)+1|0)|0;wc(f)}else{a[g>>0]=a[f>>0]|0;wc(f)}a[n>>0]=k<<1;break}f=h+1|0;g=vc(f)|0;if(!(h>>>0<=i>>>0&(g|0)==0)){if(l)Fc(g|0,n+1|0,((e&255)>>>1)+1|0)|0;else{b=c[m>>2]|0;a[g>>0]=a[b>>0]|0;wc(b)}c[n>>2]=f|1;c[j>>2]=k;c[m>>2]=g}}}else{a[n+1>>0]=0;a[n>>0]=0}while(0);c[n>>2]=c[d>>2];c[n+4>>2]=c[d+4>>2];c[n+8>>2]=c[d+8>>2];e=0;while(1){if((e|0)==3)break;c[d+(e<<2)>>2]=0;e=e+1|0}return}function Eb(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0;x=i;i=i+96|0;w=x+64|0;k=x+40|0;u=x+16|0;v=x;t=b;a:do if((d-t|0)>1?(a[b>>0]|0)==84:0){r=a[b+1>>0]|0;if(r<<24>>24==95){f=c[e+36>>2]|0;if((c[e+32>>2]|0)==(f|0)){f=b;break}g=c[f+-16>>2]|0;if((g|0)!=(c[f+-12>>2]|0)){m=c[g+4>>2]|0;n=e+4|0;o=e+8|0;p=e+12|0;q=w+8|0;l=c[g>>2]|0;while(1){if((l|0)==(m|0)){f=8;break}f=c[n>>2]|0;k=c[o>>2]|0;g=k;if((f|0)==(k|0)){h=c[e>>2]|0;f=f-h|0;k=(f|0)/24|0;j=k+1|0;if((f|0)<-24){f=12;break}f=(g-h|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<j>>>0?j:f}else f=2147483647;ab(w,f,k,p);k=c[q>>2]|0;_a(k,l);_a(k+12|0,l+12|0);c[q>>2]=k+24;cb(e,w);bb(w)}else{_a(f,l);_a(f+12|0,l+12|0);c[n>>2]=(c[n>>2]|0)+24}l=l+24|0}if((f|0)==8){f=b+2|0;break}else if((f|0)==12)Pa()}else{a[k>>0]=4;f=k+1|0;a[f>>0]=84;a[f+1>>0]=95;a[k+3>>0]=0;f=k+12|0;g=0;while(1){if((g|0)==3)break;c[f+(g<<2)>>2]=0;g=g+1|0}f=e+4|0;g=c[f>>2]|0;v=c[e+8>>2]|0;h=v;if(g>>>0<v>>>0){db(g,k);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;v=g-f|0;j=(v|0)/24|0;g=j+1|0;if((v|0)<-24)Pa();f=(h-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<g>>>0?g:f}else f=2147483647;ab(w,f,j,e+12|0);v=w+8|0;u=c[v>>2]|0;db(u,k);c[v>>2]=u+24;cb(e,w);bb(w)}Ia(k);a[e+62>>0]=1;f=b+2|0;break}}f=(r<<24>>24)+-48|0;if(f>>>0<10){r=b+2|0;while(1){if((r|0)==(d|0)){f=b;break a}g=a[r>>0]|0;h=(g<<24>>24)+-48|0;if(h>>>0>=10)break;f=h+(f*10|0)|0;r=r+1|0}if(g<<24>>24==95?(s=c[e+36>>2]|0,(c[e+32>>2]|0)!=(s|0)):0){f=f+1|0;d=c[s+-16>>2]|0;g=d;if(f>>>0<(c[s+-12>>2]|0)-d>>4>>>0){m=c[g+(f<<4)+4>>2]|0;n=e+4|0;o=e+8|0;p=e+12|0;q=w+8|0;l=c[g+(f<<4)>>2]|0;while(1){if((l|0)==(m|0)){f=38;break}f=c[n>>2]|0;s=c[o>>2]|0;g=s;if((f|0)==(s|0)){h=c[e>>2]|0;s=f-h|0;k=(s|0)/24|0;j=k+1|0;if((s|0)<-24){f=42;break}f=(g-h|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<j>>>0?j:f}else f=2147483647;ab(w,f,k,p);s=c[q>>2]|0;_a(s,l);_a(s+12|0,l+12|0);c[q>>2]=s+24;cb(e,w);bb(w)}else{_a(f,l);_a(f+12|0,l+12|0);c[n>>2]=(c[n>>2]|0)+24}l=l+24|0}if((f|0)==38){f=r+1|0;break}else if((f|0)==42)Pa()}f=r+1|0;j=f-t|0;if(j>>>0>4294967279)Xa();if(j>>>0<11){a[v>>0]=j<<1;k=v+1|0}else{t=j+16&-16;k=vc(t)|0;c[v+8>>2]=k;c[v>>2]=t|1;c[v+4>>2]=j}g=b;h=k;while(1){if((g|0)==(f|0))break;a[h>>0]=a[g>>0]|0;g=g+1|0;h=h+1|0}a[k+j>>0]=0;rb(u,v);g=e+4|0;h=c[g>>2]|0;b=c[e+8>>2]|0;j=b;if(h>>>0<b>>>0){db(h,u);c[g>>2]=(c[g>>2]|0)+24}else{g=c[e>>2]|0;b=h-g|0;k=(b|0)/24|0;h=k+1|0;if((b|0)<-24)Pa();g=(j-g|0)/24|0;if(g>>>0<1073741823){g=g<<1;g=g>>>0<h>>>0?h:g}else g=2147483647;ab(w,g,k,e+12|0);b=w+8|0;t=c[b>>2]|0;db(t,u);c[b>>2]=t+24;cb(e,w);bb(w)}Ia(u);Ja(v);a[e+62>>0]=1}else f=b}else f=b}else f=b;while(0);i=x;return f|0}function Fb(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0;r=i;i=i+128|0;q=r+104|0;g=r+72|0;n=r+80|0;o=r+60|0;p=r+48|0;j=r+24|0;k=r+12|0;l=r;a:do if((d-b|0)>2?(a[b>>0]|0)==102:0){switch(a[b+1>>0]|0){case 112:{f=Oa(b+2|0,d,g)|0;h=tb(f,d)|0;if((h|0)!=(d|0)?(a[h>>0]|0)==95:0){g=h-f|0;if(g>>>0>4294967279)Xa();if(g>>>0<11){a[p>>0]=g<<1;d=p+1|0}else{m=g+16&-16;d=vc(m)|0;c[p+8>>2]=d;c[p>>2]=m|1;c[p+4>>2]=g}b=f;f=d;while(1){if((b|0)==(h|0))break;a[f>>0]=a[b>>0]|0;b=b+1|0;f=f+1|0}a[d+g>>0]=0;b=Ta(p,0,838)|0;c[o>>2]=c[b>>2];c[o+4>>2]=c[b+4>>2];c[o+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}rb(n,o);b=e+4|0;f=c[b>>2]|0;m=c[e+8>>2]|0;g=m;if(f>>>0<m>>>0){db(f,n);c[b>>2]=(c[b>>2]|0)+24}else{b=c[e>>2]|0;m=f-b|0;d=(m|0)/24|0;f=d+1|0;if((m|0)<-24)Pa();b=(g-b|0)/24|0;if(b>>>0<1073741823){b=b<<1;b=b>>>0<f>>>0?f:b}else b=2147483647;ab(q,b,d,e+12|0);m=q+8|0;l=c[m>>2]|0;db(l,n);c[m>>2]=l+24;cb(e,q);bb(q)}Ia(n);Ja(o);Ja(p);b=h+1|0}break a}case 76:break;default:break a}f=tb(b+2|0,d)|0;if((((f|0)!=(d|0)?(a[f>>0]|0)==112:0)?(h=Oa(f+1|0,d,g)|0,m=tb(h,d)|0,(m|0)!=(d|0)):0)?(a[m>>0]|0)==95:0){g=m-h|0;if(g>>>0>4294967279)Xa();if(g>>>0<11){a[l>>0]=g<<1;d=l+1|0}else{p=g+16&-16;d=vc(p)|0;c[l+8>>2]=d;c[l>>2]=p|1;c[l+4>>2]=g}b=h;f=d;while(1){if((b|0)==(m|0))break;a[f>>0]=a[b>>0]|0;b=b+1|0;f=f+1|0}a[d+g>>0]=0;b=Ta(l,0,838)|0;c[k>>2]=c[b>>2];c[k+4>>2]=c[b+4>>2];c[k+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}rb(j,k);b=e+4|0;f=c[b>>2]|0;p=c[e+8>>2]|0;g=p;if(f>>>0<p>>>0){db(f,j);c[b>>2]=(c[b>>2]|0)+24}else{b=c[e>>2]|0;p=f-b|0;d=(p|0)/24|0;f=d+1|0;if((p|0)<-24)Pa();b=(g-b|0)/24|0;if(b>>>0<1073741823){b=b<<1;b=b>>>0<f>>>0?f:b}else b=2147483647;ab(q,b,d,e+12|0);p=q+8|0;o=c[p>>2]|0;db(o,j);c[p>>2]=o+24;cb(e,q);bb(q)}Ia(j);Ja(k);Ja(l);b=m+1|0}}while(0);i=r;return b|0}function Gb(b,d,e,f){b=b|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0;t=i;i=i+96|0;s=t+84|0;r=t+72|0;l=t+60|0;m=t+48|0;n=t+36|0;o=t+24|0;p=t+12|0;q=t;g=ub(b,d,f)|0;a:do if((g|0)!=(b|0)){d=ub(g,d,f)|0;h=f+4|0;if((d|0)==(g|0)){g=c[h>>2]|0;d=g+-24|0;while(1){if((g|0)==(d|0)){d=b;break a}s=g+-24|0;c[h>>2]=s;Ia(s);g=c[h>>2]|0}}g=c[h>>2]|0;if(((g-(c[f>>2]|0)|0)/24|0)>>>0>=2){Cb(s,g+-24|0);g=c[h>>2]|0;f=g+-24|0;b=g;while(1){if((b|0)==(f|0))break;k=b+-24|0;c[h>>2]=k;Ia(k);b=c[h>>2]|0}Cb(r,g+-48|0);g=c[h>>2]|0;k=g+-24|0;if(!(a[k>>0]&1)){a[k+1>>0]=0;a[k>>0]=0}else{a[c[g+-16>>2]>>0]=0;c[g+-20>>2]=0}u=a[e>>0]|0;f=(u&1)==0;b=e+4|0;u=f?(u&255)>>>1:c[b>>2]|0;h=e+8|0;j=e+1|0;g=u>>>0>1;f=ac(f?j:c[h>>2]|0,844,g?1:u)|0;if(!(((f|0)==0?((u|0)==0?-1:g&1):f)|0))zb(k,40);xb(q,797,r);g=Ya(q,846)|0;c[p>>2]=c[g>>2];c[p+4>>2]=c[g+4>>2];c[p+8>>2]=c[g+8>>2];f=0;while(1){if((f|0)==3)break;c[g+(f<<2)>>2]=0;f=f+1|0}g=a[e>>0]|0;f=(g&1)==0;g=Za(p,f?j:c[h>>2]|0,f?(g&255)>>>1:c[b>>2]|0)|0;c[o>>2]=c[g>>2];c[o+4>>2]=c[g+4>>2];c[o+8>>2]=c[g+8>>2];f=0;while(1){if((f|0)==3)break;c[g+(f<<2)>>2]=0;f=f+1|0}g=Ya(o,849)|0;c[n>>2]=c[g>>2];c[n+4>>2]=c[g+4>>2];c[n+8>>2]=c[g+8>>2];f=0;while(1){if((f|0)==3)break;c[g+(f<<2)>>2]=0;f=f+1|0}g=a[s>>0]|0;f=(g&1)==0;g=Za(n,f?s+1|0:c[s+8>>2]|0,f?(g&255)>>>1:c[s+4>>2]|0)|0;c[m>>2]=c[g>>2];c[m+4>>2]=c[g+4>>2];c[m+8>>2]=c[g+8>>2];f=0;while(1){if((f|0)==3)break;c[g+(f<<2)>>2]=0;f=f+1|0}g=Ya(m,799)|0;c[l>>2]=c[g>>2];c[l+4>>2]=c[g+4>>2];c[l+8>>2]=c[g+8>>2];f=0;while(1){if((f|0)==3)break;c[g+(f<<2)>>2]=0;f=f+1|0}u=a[l>>0]|0;f=(u&1)==0;Za(k,f?l+1|0:c[l+8>>2]|0,f?(u&255)>>>1:c[l+4>>2]|0)|0;Ja(l);Ja(m);Ja(n);Ja(o);Ja(p);Ja(q);q=a[e>>0]|0;u=(q&1)==0;q=u?(q&255)>>>1:c[b>>2]|0;e=q>>>0>1;u=ac(u?j:c[h>>2]|0,844,e?1:q)|0;if(!(((u|0)==0?((q|0)==0?-1:e&1):u)|0))zb(k,41);Ja(r);Ja(s)}else d=b}else d=b;while(0);i=t;return d|0}function Hb(b,d,e,f){b=b|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0;s=i;i=i+48|0;o=s+36|0;p=s+24|0;q=s+12|0;r=s;d=ub(b,d,f)|0;if((d|0)!=(b|0)?(g=f+4|0,h=c[g>>2]|0,(c[f>>2]|0)!=(h|0)):0){n=h+-24|0;Ib(q,e,797);Cb(r,(c[g>>2]|0)+-24|0);f=a[r>>0]|0;b=(f&1)==0;f=Za(q,b?r+1|0:c[r+8>>2]|0,b?(f&255)>>>1:c[r+4>>2]|0)|0;c[p>>2]=c[f>>2];c[p+4>>2]=c[f+4>>2];c[p+8>>2]=c[f+8>>2];b=0;while(1){if((b|0)==3)break;c[f+(b<<2)>>2]=0;b=b+1|0}f=Ya(p,799)|0;c[o>>2]=c[f>>2];c[o+4>>2]=c[f+4>>2];c[o+8>>2]=c[f+8>>2];b=0;while(1){if((b|0)==3)break;c[f+(b<<2)>>2]=0;b=b+1|0}do if(a[n>>0]&1){m=h+-16|0;a[c[m>>2]>>0]=0;j=h+-20|0;c[j>>2]=0;f=a[n>>0]|0;if(!(f&1))h=10;else{h=c[n>>2]|0;f=h&255;h=(h&-2)+-1|0}if(!(f&1)){b=(f&255)>>>1;if((f&255)<22){e=10;k=b;l=1}else{e=(b+16&240)+-1|0;k=b;l=1}}else{e=10;k=0;l=0}if((e|0)!=(h|0)){if((e|0)==10){g=n+1|0;b=c[m>>2]|0;if(l){Fc(g|0,b|0,((f&255)>>>1)+1|0)|0;wc(b)}else{a[g>>0]=a[b>>0]|0;wc(b)}a[n>>0]=k<<1;break}b=e+1|0;g=vc(b)|0;if(!(e>>>0<=h>>>0&(g|0)==0)){if(l)Fc(g|0,n+1|0,((f&255)>>>1)+1|0)|0;else{l=c[m>>2]|0;a[g>>0]=a[l>>0]|0;wc(l)}c[n>>2]=b|1;c[j>>2]=k;c[m>>2]=g}}}else{a[n+1>>0]=0;a[n>>0]=0}while(0);c[n>>2]=c[o>>2];c[n+4>>2]=c[o+4>>2];c[n+8>>2]=c[o+8>>2];f=0;while(1){if((f|0)==3)break;c[o+(f<<2)>>2]=0;f=f+1|0}Ja(o);Ja(p);Ja(r);Ja(q)}else d=b;i=s;return d|0}function Ib(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0;f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}g=a[d>>0]|0;h=(g&1)==0;g=h?(g&255)>>>1:c[d+4>>2]|0;f=bc(e)|0;yb(b,h?d+1|0:c[d+8>>2]|0,g,g+f|0);Za(b,e,f)|0;return}function Jb(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0;t=i;i=i+80|0;s=t+60|0;p=t+48|0;r=t+36|0;l=t+24|0;o=t+12|0;q=t;g=d;a:do if((g-b|0)>2){if((a[b>>0]|0)==103){h=(a[b+1>>0]|0)==115;j=h;h=h?b+2|0:b}else{j=0;h=b}f=Kb(h,d,e)|0;if((f|0)!=(h|0)){if(!j)break;g=c[e+4>>2]|0;if((c[e>>2]|0)==(g|0)){f=b;break}Ta(g+-24|0,0,891)|0;break}if(((g-h|0)>2?(a[h>>0]|0)==115:0)?(a[h+1>>0]|0)==114:0){f=h+2|0;if((a[f>>0]|0)==78){q=h+3|0;f=Ob(q,d,e)|0;if((f|0)==(q|0)|(f|0)==(d|0)){f=b;break}j=Mb(f,d,e)|0;o=e+4|0;do if((j|0)==(f|0))n=e;else{f=c[o>>2]|0;if(((f-(c[e>>2]|0)|0)/24|0)>>>0<2){f=b;break a}Cb(s,f+-24|0);f=c[o>>2]|0;g=f+-24|0;h=f;while(1){if((h|0)==(g|0))break;q=h+-24|0;c[o>>2]=q;Ia(q);h=c[o>>2]|0}q=a[s>>0]|0;n=(q&1)==0;Za(f+-48|0,n?s+1|0:c[s+8>>2]|0,n?(q&255)>>>1:c[s+4>>2]|0)|0;if((j|0)!=(d|0)){Ja(s);n=e;f=j;break}g=c[o>>2]|0;f=g+-24|0;while(1){if((g|0)==(f|0))break;e=g+-24|0;c[o>>2]=e;Ia(e);g=c[o>>2]|0}Ja(s);f=b;break a}while(0);k=p+8|0;l=p+1|0;m=p+4|0;while(1){if((a[f>>0]|0)==69)break;j=Vb(f,d,e)|0;if((j|0)==(f|0)|(j|0)==(d|0)){f=b;break a}f=c[o>>2]|0;if(((f-(c[n>>2]|0)|0)/24|0)>>>0<2){f=b;break a}Cb(s,f+-24|0);h=c[o>>2]|0;f=h+-24|0;g=h;while(1){if((g|0)==(f|0))break;q=g+-24|0;c[o>>2]=q;Ia(q);g=c[o>>2]|0}f=Ta(s,0,891)|0;c[p>>2]=c[f>>2];c[p+4>>2]=c[f+4>>2];c[p+8>>2]=c[f+8>>2];g=0;while(1){if((g|0)==3)break;c[f+(g<<2)>>2]=0;g=g+1|0}f=a[p>>0]|0;q=(f&1)==0;Za(h+-48|0,q?l:c[k>>2]|0,q?(f&255)>>>1:c[m>>2]|0)|0;Ja(p);Ja(s);f=j}q=f+1|0;f=Kb(q,d,e)|0;if((f|0)==(q|0)){f=c[o>>2]|0;if((c[e>>2]|0)==(f|0)){f=b;break}g=f+-24|0;while(1){if((f|0)==(g|0)){f=b;break a}s=f+-24|0;c[o>>2]=s;Ia(s);f=c[o>>2]|0}}g=c[o>>2]|0;if(((g-(c[n>>2]|0)|0)/24|0)>>>0<2){f=b;break}Cb(s,g+-24|0);j=c[o>>2]|0;g=j+-24|0;h=j;while(1){if((h|0)==(g|0))break;b=h+-24|0;c[o>>2]=b;Ia(b);h=c[o>>2]|0}g=Ta(s,0,891)|0;c[r>>2]=c[g>>2];c[r+4>>2]=c[g+4>>2];c[r+8>>2]=c[g+8>>2];h=0;while(1){if((h|0)==3)break;c[g+(h<<2)>>2]=0;h=h+1|0}b=a[r>>0]|0;e=(b&1)==0;Za(j+-48|0,e?r+1|0:c[r+8>>2]|0,e?(b&255)>>>1:c[r+4>>2]|0)|0;Ja(r);Ja(s);break}g=Ob(f,d,e)|0;if((g|0)!=(f|0)){k=Mb(g,d,e)|0;if((k|0)!=(g|0)){j=e+4|0;f=c[j>>2]|0;if(((f-(c[e>>2]|0)|0)/24|0)>>>0<2){f=b;break}Cb(s,f+-24|0);f=c[j>>2]|0;g=f+-24|0;h=f;while(1){if((h|0)==(g|0))break;r=h+-24|0;c[j>>2]=r;Ia(r);h=c[j>>2]|0}g=a[s>>0]|0;r=(g&1)==0;Za(f+-48|0,r?s+1|0:c[s+8>>2]|0,r?(g&255)>>>1:c[s+4>>2]|0)|0;Ja(s);g=k}f=Kb(g,d,e)|0;if((f|0)==(g|0)){h=e+4|0;f=c[h>>2]|0;if((c[e>>2]|0)==(f|0)){f=b;break}g=f+-24|0;while(1){if((f|0)==(g|0)){f=b;break a}s=f+-24|0;c[h>>2]=s;Ia(s);f=c[h>>2]|0}}k=e+4|0;g=c[k>>2]|0;if(((g-(c[e>>2]|0)|0)/24|0)>>>0<2){f=b;break}Cb(s,g+-24|0);j=c[k>>2]|0;g=j+-24|0;h=j;while(1){if((h|0)==(g|0))break;b=h+-24|0;c[k>>2]=b;Ia(b);h=c[k>>2]|0}g=Ta(s,0,891)|0;c[l>>2]=c[g>>2];c[l+4>>2]=c[g+4>>2];c[l+8>>2]=c[g+8>>2];h=0;while(1){if((h|0)==3)break;c[g+(h<<2)>>2]=0;h=h+1|0}b=a[l>>0]|0;e=(b&1)==0;Za(j+-48|0,e?l+1|0:c[l+8>>2]|0,e?(b&255)>>>1:c[l+4>>2]|0)|0;Ja(l);Ja(s);break}h=Vb(f,d,e)|0;if(!((h|0)==(f|0)|(h|0)==(d|0))){if(j){f=e+4|0;g=c[f>>2]|0;if((c[e>>2]|0)==(g|0)){f=b;break}Ta(g+-24|0,0,891)|0;n=f}else n=e+4|0;k=o+8|0;l=o+1|0;m=o+4|0;f=h;while(1){if((a[f>>0]|0)==69)break;j=Vb(f,d,e)|0;if((j|0)==(f|0)|(j|0)==(d|0)){f=b;break a}f=c[n>>2]|0;if(((f-(c[e>>2]|0)|0)/24|0)>>>0<2){f=b;break a}Cb(s,f+-24|0);h=c[n>>2]|0;f=h+-24|0;g=h;while(1){if((g|0)==(f|0))break;r=g+-24|0;c[n>>2]=r;Ia(r);g=c[n>>2]|0}f=Ta(s,0,891)|0;c[o>>2]=c[f>>2];c[o+4>>2]=c[f+4>>2];c[o+8>>2]=c[f+8>>2];g=0;while(1){if((g|0)==3)break;c[f+(g<<2)>>2]=0;g=g+1|0}f=a[o>>0]|0;r=(f&1)==0;Za(h+-48|0,r?l:c[k>>2]|0,r?(f&255)>>>1:c[m>>2]|0)|0;Ja(o);Ja(s);f=j}r=f+1|0;f=Kb(r,d,e)|0;if((f|0)==(r|0)){f=c[n>>2]|0;if((c[e>>2]|0)==(f|0)){f=b;break}g=f+-24|0;while(1){if((f|0)==(g|0)){f=b;break a}s=f+-24|0;c[n>>2]=s;Ia(s);f=c[n>>2]|0}}g=c[n>>2]|0;if(((g-(c[e>>2]|0)|0)/24|0)>>>0>=2){Cb(s,g+-24|0);j=c[n>>2]|0;g=j+-24|0;h=j;while(1){if((h|0)==(g|0))break;b=h+-24|0;c[n>>2]=b;Ia(b);h=c[n>>2]|0}g=Ta(s,0,891)|0;c[q>>2]=c[g>>2];c[q+4>>2]=c[g+4>>2];c[q+8>>2]=c[g+8>>2];h=0;while(1){if((h|0)==3)break;c[g+(h<<2)>>2]=0;h=h+1|0}b=a[q>>0]|0;e=(b&1)==0;Za(j+-48|0,e?q+1|0:c[q+8>>2]|0,e?(b&255)>>>1:c[q+4>>2]|0)|0;Ja(q);Ja(s)}else f=b}else f=b}else f=b}else f=b;while(0);i=t;return f|0}function Kb(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0;k=i;i=i+16|0;j=k;a:do if((d-b|0)>1){f=a[b>>0]|0;switch(f<<24>>24){case 100:case 111:{if((a[b+1>>0]|0)==110){h=b+2|0;if(f<<24>>24==111){f=Lb(h,d,e)|0;if((f|0)==(h|0)){f=b;break a}b=Mb(f,d,e)|0;if((b|0)==(f|0))break a;d=e+4|0;f=c[d>>2]|0;if(((f-(c[e>>2]|0)|0)/24|0)>>>0<2){f=b;break a}Cb(j,f+-24|0);f=c[d>>2]|0;g=f+-24|0;h=f;while(1){if((h|0)==(g|0))break;e=h+-24|0;c[d>>2]=e;Ia(e);h=c[d>>2]|0}e=a[j>>0]|0;d=(e&1)==0;Za(f+-48|0,d?j+1|0:c[j+8>>2]|0,d?(e&255)>>>1:c[j+4>>2]|0)|0;Ja(j);f=b;break a}else{if((h|0)!=(d|0)){f=Ob(h,d,e)|0;if((f|0)==(h|0))f=Vb(h,d,e)|0;if((f|0)!=(h|0)?(g=c[e+4>>2]|0,(c[e>>2]|0)!=(g|0)):0)Ta(g+-24|0,0,886)|0;else f=h}else f=d;f=(f|0)==(h|0)?b:f;break a}}break}default:{}}f=Vb(b,d,e)|0;if((f|0)==(b|0)){f=Lb(b,d,e)|0;if((f|0)!=(b|0)){b=Mb(f,d,e)|0;if((b|0)!=(f|0)){d=e+4|0;f=c[d>>2]|0;if(((f-(c[e>>2]|0)|0)/24|0)>>>0<2)f=b;else{Cb(j,f+-24|0);f=c[d>>2]|0;g=f+-24|0;h=f;while(1){if((h|0)==(g|0))break;e=h+-24|0;c[d>>2]=e;Ia(e);h=c[d>>2]|0}e=a[j>>0]|0;d=(e&1)==0;Za(f+-48|0,d?j+1|0:c[j+8>>2]|0,d?(e&255)>>>1:c[j+4>>2]|0)|0;Ja(j);f=b}}}else f=b}}else f=b;while(0);i=k;return f|0}function Lb(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,aa=0,ba=0,ca=0;ca=i;i=i+1136|0;ba=ca+1104|0;j=ca+1080|0;k=ca+1056|0;v=ca+1032|0;G=ca+1008|0;R=ca+984|0;Y=ca+960|0;Z=ca+936|0;_=ca+912|0;$=ca+888|0;aa=ca+864|0;l=ca+840|0;m=ca+816|0;n=ca+792|0;o=ca+768|0;p=ca+744|0;q=ca+720|0;r=ca+696|0;s=ca+672|0;t=ca+648|0;u=ca+624|0;w=ca+600|0;x=ca+576|0;y=ca+552|0;z=ca+528|0;A=ca+504|0;B=ca+480|0;C=ca+456|0;D=ca+432|0;E=ca+408|0;F=ca+384|0;H=ca+360|0;I=ca+336|0;J=ca+312|0;K=ca+288|0;L=ca+264|0;M=ca+240|0;N=ca+216|0;O=ca+192|0;P=ca+168|0;Q=ca+144|0;S=ca+120|0;T=ca+96|0;U=ca+72|0;V=ca+48|0;W=ca+24|0;X=ca;a:do if((d-b|0)>1)do switch(a[b>>0]|0){case 97:switch(a[b+1>>0]|0){case 97:{pb(j,926);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,j);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,j);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(j);d=b+2|0;break a}case 110:case 100:{mb(k,937);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,k);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,k);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(k);d=b+2|0;break a}case 78:{pb(v,947);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,v);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,v);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(v);d=b+2|0;break a}case 83:{mb(G,958);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,G);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,G);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(G);d=b+2|0;break a}default:{d=b;break a}}case 99:switch(a[b+1>>0]|0){case 108:{pb(R,968);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,R);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,R);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(R);d=b+2|0;break a}case 109:{mb(Y,979);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,Y);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,Y);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(Y);d=b+2|0;break a}case 111:{mb(Z,989);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,Z);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,Z);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(Z);d=b+2|0;break a}case 118:{aa=e+63|0;$=a[aa>>0]|0;a[aa>>0]=0;ba=b+2|0;d=Na(ba,d,e)|0;a[aa>>0]=$;if((d|0)==(ba|0)){d=b;break a}f=c[e+4>>2]|0;if((c[e>>2]|0)==(f|0)){d=b;break a}Ta(f+-24|0,0,999)|0;a[e+60>>0]=1;break a}default:{d=b;break a}}case 100:switch(a[b+1>>0]|0){case 97:{ob(_,1009);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,_);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,_);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(_);d=b+2|0;break a}case 101:{mb($,1027);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,$);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;_=c[aa>>2]|0;db(_,$);c[aa>>2]=_+24;cb(e,ba);bb(ba)}Ia($);d=b+2|0;break a}case 108:{d=vc(16)|0;c[aa+8>>2]=d;c[aa>>2]=17;c[aa+4>>2]=15;f=d;g=1037;h=f+15|0;do{a[f>>0]=a[g>>0]|0;f=f+1|0;g=g+1|0}while((f|0)<(h|0));a[d+15>>0]=0;d=aa+12|0;f=0;while(1){if((f|0)==3)break;c[d+(f<<2)>>2]=0;f=f+1|0}d=e+4|0;f=c[d>>2]|0;$=c[e+8>>2]|0;g=$;if(f>>>0<$>>>0){db(f,aa);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;$=f-d|0;h=($|0)/24|0;f=h+1|0;if(($|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);$=ba+8|0;_=c[$>>2]|0;db(_,aa);c[$>>2]=_+24;cb(e,ba);bb(ba)}Ia(aa);d=b+2|0;break a}case 118:{mb(l,1053);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,l);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,l);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(l);d=b+2|0;break a}case 86:{pb(m,1063);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,m);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,m);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(m);d=b+2|0;break a}default:{d=b;break a}}case 101:switch(a[b+1>>0]|0){case 111:{mb(n,1074);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,n);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,n);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(n);d=b+2|0;break a}case 79:{pb(o,1084);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,o);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,o);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(o);d=b+2|0;break a}case 113:{pb(p,1095);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,p);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,p);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(p);d=b+2|0;break a}default:{d=b;break a}}case 103:switch(a[b+1>>0]|0){case 101:{pb(q,1106);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,q);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,q);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(q);d=b+2|0;break a}case 116:{mb(r,1117);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,r);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,r);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(r);d=b+2|0;break a}default:{d=b;break a}}case 105:{if((a[b+1>>0]|0)!=120){d=b;break a}pb(s,1127);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,s);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,s);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(s);d=b+2|0;break a}case 108:switch(a[b+1>>0]|0){case 101:{pb(t,1138);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,t);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,t);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(t);d=b+2|0;break a}case 105:{ba=b+2|0;d=qb(ba,d,e)|0;if((d|0)==(ba|0)){d=b;break a}f=c[e+4>>2]|0;if((c[e>>2]|0)==(f|0)){d=b;break a}Ta(f+-24|0,0,1149)|0;break a}case 115:{pb(u,1161);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,u);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,u);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(u);d=b+2|0;break a}case 83:{gb(w,1172);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,w);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,w);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(w);d=b+2|0;break a}case 116:{mb(x,1184);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,x);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,x);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(x);d=b+2|0;break a}default:{d=b;break a}}case 109:switch(a[b+1>>0]|0){case 105:{mb(y,1194);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,y);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,y);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(y);d=b+2|0;break a}case 73:{pb(z,1204);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,z);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,z);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(z);d=b+2|0;break a}case 108:{mb(A,1027);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,A);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,A);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(A);d=b+2|0;break a}case 76:{pb(B,1215);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,B);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,B);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(B);d=b+2|0;break a}case 109:{pb(C,1226);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,C);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,C);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(C);d=b+2|0;break a}default:{d=b;break a}}case 110:switch(a[b+1>>0]|0){case 97:{jb(D,1237);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,D);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,D);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(D);d=b+2|0;break a}case 101:{pb(E,1252);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,E);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,E);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(E);d=b+2|0;break a}case 103:{mb(F,1194);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,F);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,F);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(F);d=b+2|0;break a}case 116:{mb(H,1263);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,H);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,H);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(H);d=b+2|0;break a}case 119:{lb(I,1273);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,I);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,I);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(I);d=b+2|0;break a}default:{d=b;break a}}case 111:switch(a[b+1>>0]|0){case 111:{pb(J,1286);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,J);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,J);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(J);d=b+2|0;break a}case 114:{mb(K,1297);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,K);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,K);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(K);d=b+2|0;break a}case 82:{pb(L,1307);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,L);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,L);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(L);d=b+2|0;break a}default:{d=b;break a}}case 112:switch(a[b+1>>0]|0){case 109:{gb(M,1318);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,M);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,M);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(M);d=b+2|0;break a}case 108:{mb(N,1330);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,N);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,N);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(N);d=b+2|0;break a}case 76:{pb(O,1340);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,O);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,O);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(O);d=b+2|0;break a}case 112:{pb(P,1351);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,P);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,P);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(P);d=b+2|0;break a}case 115:{mb(Q,1330);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,Q);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,Q);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(Q);d=b+2|0;break a}case 116:{pb(S,1362);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,S);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,S);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(S);d=b+2|0;break a}default:{d=b;break a}}case 113:{if((a[b+1>>0]|0)!=117){d=b;break a}mb(T,1373);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,T);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,T);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(T);d=b+2|0;break a}case 114:switch(a[b+1>>0]|0){case 109:{mb(U,1383);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,U);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,U);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(U);d=b+2|0;break a}case 77:{pb(V,1393);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,V);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,V);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(V);d=b+2|0;break a}case 115:{pb(W,1404);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,W);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,W);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(W);d=b+2|0;break a}case 83:{gb(X,1415);d=e+4|0;f=c[d>>2]|0;aa=c[e+8>>2]|0;g=aa;if(f>>>0<aa>>>0){db(f,X);c[d>>2]=(c[d>>2]|0)+24}else{d=c[e>>2]|0;aa=f-d|0;h=(aa|0)/24|0;f=h+1|0;if((aa|0)<-24)Pa();d=(g-d|0)/24|0;if(d>>>0<1073741823){d=d<<1;d=d>>>0<f>>>0?f:d}else d=2147483647;ab(ba,d,h,e+12|0);aa=ba+8|0;$=c[aa>>2]|0;db($,X);c[aa>>2]=$+24;cb(e,ba);bb(ba)}Ia(X);d=b+2|0;break a}default:{d=b;break a}}case 118:{if(((a[b+1>>0]|0)+-48|0)>>>0>=10){d=b;break a}ba=b+2|0;d=qb(ba,d,e)|0;if((d|0)==(ba|0)){d=b;break a}f=c[e+4>>2]|0;if((c[e>>2]|0)==(f|0)){d=b;break a}Ta(f+-24|0,0,999)|0;break a}default:{d=b;break a}}while(0);else d=b;while(0);i=ca;return d|0}function Mb(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0;M=i;i=i+80|0;K=M+60|0;L=M;F=M+48|0;I=M+24|0;J=M+12|0;do if((d-b|0)>1?(a[b>>0]|0)==73:0){G=e+61|0;E=e+36|0;a:do if(a[G>>0]|0){g=c[E>>2]|0;f=c[g+-16>>2]|0;g=g+-12|0;while(1){h=c[g>>2]|0;if((h|0)==(f|0))break a;H=h+-16|0;c[g>>2]=H;Ha(H)}}while(0);$a(L,1427,1);H=e+4|0;t=e+12|0;u=K+8|0;v=K+8|0;D=L+4|0;w=F+8|0;x=F+1|0;y=F+4|0;z=e+32|0;A=e+40|0;B=e+44|0;C=K+8|0;n=b+1|0;b:while(1){if((a[n>>0]|0)==69){f=48;break}do if(a[G>>0]|0){m=c[t>>2]|0;f=c[E>>2]|0;g=c[A>>2]|0;if(f>>>0<g>>>0){c[f>>2]=0;c[f+4>>2]=0;c[f+8>>2]=0;c[f+12>>2]=m;c[E>>2]=(c[E>>2]|0)+16;break}h=c[z>>2]|0;s=f-h|0;k=s>>4;j=k+1|0;if((s|0)<-16){f=13;break b}f=g-h|0;if(f>>4>>>0<1073741823){f=f>>3;f=f>>>0<j>>>0?j:f}else f=2147483647;Ca(K,f,k,B);s=c[C>>2]|0;c[s>>2]=0;c[s+4>>2]=0;c[s+8>>2]=0;c[s+12>>2]=m;c[C>>2]=s+16;Ea(z,K);Fa(K)}while(0);r=((c[H>>2]|0)-(c[e>>2]|0)|0)/24|0;s=Nb(n,d,e)|0;h=((c[H>>2]|0)-(c[e>>2]|0)|0)/24|0;c:do if(a[G>>0]|0){g=c[E>>2]|0;f=g+-16|0;while(1){if((g|0)==(f|0))break c;q=g+-16|0;c[E>>2]=q;Ga(q);g=c[E>>2]|0}}while(0);if((s|0)==(n|0)|(s|0)==(d|0)){f=62;break}d:do if(!(a[G>>0]|0))f=r;else{m=c[E>>2]|0;n=m+-16|0;o=c[t>>2]|0;f=m+-12|0;g=c[f>>2]|0;q=c[m+-8>>2]|0;k=q;if(g>>>0<q>>>0){c[g>>2]=0;c[g+4>>2]=0;c[g+8>>2]=0;c[g+12>>2]=o;c[f>>2]=(c[f>>2]|0)+16;q=r}else{f=c[n>>2]|0;q=g-f|0;j=q>>4;g=j+1|0;if((q|0)<-16){f=26;break b}f=k-f|0;if(f>>4>>>0<1073741823){f=f>>3;f=f>>>0<g>>>0?g:f}else f=2147483647;Qa(K,f,j,m+-4|0);q=c[u>>2]|0;c[q>>2]=0;c[q+4>>2]=0;c[q+8>>2]=0;c[q+12>>2]=o;c[u>>2]=q+16;Ra(n,K);Sa(K);q=r}while(1){if(q>>>0>=h>>>0){f=r;break d}m=c[(c[E>>2]|0)+-12>>2]|0;n=m+-16|0;o=c[e>>2]|0;p=o+(q*24|0)|0;f=m+-12|0;g=c[f>>2]|0;k=c[m+-8>>2]|0;j=k;if((g|0)==(k|0)){f=c[n>>2]|0;N=g-f|0;k=(N|0)/24|0;g=k+1|0;if((N|0)<-24){f=34;break b}f=(j-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<g>>>0?g:f}else f=2147483647;ab(K,f,k,m+-4|0);N=c[v>>2]|0;_a(N,p);_a(N+12|0,o+(q*24|0)+12|0);c[v>>2]=N+24;cb(n,K);bb(K)}else{_a(g,p);_a(g+12|0,o+(q*24|0)+12|0);c[f>>2]=(c[f>>2]|0)+24}q=q+1|0}}while(0);while(1){if(f>>>0>=h>>>0)break;N=a[L>>0]|0;if(((N&1)==0?(N&255)>>>1:c[D>>2]|0)>>>0>1)Ya(L,1429)|0;Cb(F,(c[e>>2]|0)+(f*24|0)|0);N=a[F>>0]|0;q=(N&1)==0;Za(L,q?x:c[w>>2]|0,q?(N&255)>>>1:c[y>>2]|0)|0;Ja(F);f=f+1|0}while(1){if((h|0)==(r|0)){n=s;continue b}g=c[H>>2]|0;f=g+-24|0;while(1){if((g|0)==(f|0))break;N=g+-24|0;c[H>>2]=N;Ia(N);g=c[H>>2]|0}h=h+-1|0}}if((f|0)==13)Pa();else if((f|0)==26)Pa();else if((f|0)==34)Pa();else if((f|0)==48){l=n+1|0;N=a[L>>0]|0;b=(N&1)==0;if((a[(b?L+1|0:c[L+8>>2]|0)+(b?(N&255)>>>1:c[D>>2]|0)+-1>>0]|0)==62)Ya(L,1432)|0;else Ya(L,844)|0;c[J>>2]=c[L>>2];c[J+4>>2]=c[L+4>>2];c[J+8>>2]=c[L+8>>2];f=0;while(1){if((f|0)==3)break;c[L+(f<<2)>>2]=0;f=f+1|0}rb(I,J);f=c[H>>2]|0;N=c[e+8>>2]|0;j=N;if(f>>>0<N>>>0){db(f,I);c[H>>2]=(c[H>>2]|0)+24}else{g=c[e>>2]|0;N=f-g|0;k=(N|0)/24|0;h=k+1|0;if((N|0)<-24)Pa();f=(j-g|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<h>>>0?h:f}else f=2147483647;ab(K,f,k,e+12|0);N=K+8|0;H=c[N>>2]|0;db(H,I);c[N>>2]=H+24;cb(e,K);bb(K)}Ia(I);Ja(J);Ja(L);break}else if((f|0)==62){Ja(L);l=b;break}}else l=b;while(0);i=M;return l|0}function Nb(b,c,d){b=b|0;c=c|0;d=d|0;var e=0,f=0;a:do if((b|0)!=(c|0))switch(a[b>>0]|0){case 88:{f=b+1|0;e=ub(f,c,d)|0;if((e|0)==(f|0)|(e|0)==(c|0))break a;b=(a[e>>0]|0)==69?e+1|0:b;break a}case 74:{e=b+1|0;if((e|0)==(c|0))break a;while(1){if((a[e>>0]|0)==69)break;f=Nb(e,c,d)|0;if((f|0)==(e|0))break a;else e=f}b=e+1|0;break a}case 76:{f=b+1|0;if((f|0)!=(c|0)?(a[f>>0]|0)==90:0){f=b+2|0;e=Ma(f,c,d)|0;if((e|0)==(f|0)|(e|0)==(c|0))break a;b=(a[e>>0]|0)==69?e+1|0:b;break a}b=vb(b,c,d)|0;break a}default:{b=Na(b,c,d)|0;break a}}while(0);return b|0}function Ob(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;p=i;i=i+96|0;o=p+72|0;n=p+56|0;k=p+48|0;l=p+32|0;g=p+24|0;m=p+8|0;h=p;a:do if((b|0)==(d|0))f=b;else switch(a[b>>0]|0){case 84:{j=e+4|0;h=((c[j>>2]|0)-(c[e>>2]|0)|0)/24|0;f=Eb(b,d,e)|0;d=c[j>>2]|0;g=(d-(c[e>>2]|0)|0)/24|0;if(!((f|0)!=(b|0)&(g|0)==(h+1|0))){f=d;while(1){if((g|0)==(h|0)){f=b;break a}d=f+-24|0;while(1){if((f|0)==(d|0))break;e=f+-24|0;c[j>>2]=e;Ia(e);f=c[j>>2]|0}f=d;g=g+-1|0}}b=e+16|0;c[k>>2]=c[e+12>>2];Pb(n,d+-24|0,k);d=e+20|0;g=c[d>>2]|0;m=c[e+24>>2]|0;h=m;if(g>>>0<m>>>0){c[g+12>>2]=c[n+12>>2];c[g>>2]=c[n>>2];e=n+4|0;c[g+4>>2]=c[e>>2];o=n+8|0;c[g+8>>2]=c[o>>2];c[o>>2]=0;c[e>>2]=0;c[n>>2]=0;c[d>>2]=(c[d>>2]|0)+16}else{d=c[b>>2]|0;m=g-d|0;j=m>>4;g=j+1|0;if((m|0)<-16)Pa();d=h-d|0;if(d>>4>>>0<1073741823){d=d>>3;d=d>>>0<g>>>0?g:d}else d=2147483647;Qa(o,d,j,e+28|0);e=o+8|0;m=c[e>>2]|0;c[m+12>>2]=c[n+12>>2];c[m>>2]=c[n>>2];l=n+4|0;c[m+4>>2]=c[l>>2];k=n+8|0;c[m+8>>2]=c[k>>2];c[k>>2]=0;c[l>>2]=0;c[n>>2]=0;c[e>>2]=m+16;Ra(b,o);Sa(o)}Ha(n);break a}case 68:{f=Qb(b,d,e)|0;if((f|0)==(b|0)){f=b;break a}d=c[e+4>>2]|0;if((c[e>>2]|0)==(d|0)){f=b;break a}b=e+16|0;c[g>>2]=c[e+12>>2];Pb(l,d+-24|0,g);d=e+20|0;g=c[d>>2]|0;n=c[e+24>>2]|0;j=n;if(g>>>0<n>>>0){c[g+12>>2]=c[l+12>>2];c[g>>2]=c[l>>2];e=l+4|0;c[g+4>>2]=c[e>>2];o=l+8|0;c[g+8>>2]=c[o>>2];c[o>>2]=0;c[e>>2]=0;c[l>>2]=0;c[d>>2]=(c[d>>2]|0)+16}else{d=c[b>>2]|0;n=g-d|0;h=n>>4;g=h+1|0;if((n|0)<-16)Pa();d=j-d|0;if(d>>4>>>0<1073741823){d=d>>3;d=d>>>0<g>>>0?g:d}else d=2147483647;Qa(o,d,h,e+28|0);e=o+8|0;n=c[e>>2]|0;c[n+12>>2]=c[l+12>>2];c[n>>2]=c[l>>2];m=l+4|0;c[n+4>>2]=c[m>>2];k=l+8|0;c[n+8>>2]=c[k>>2];c[k>>2]=0;c[m>>2]=0;c[l>>2]=0;c[e>>2]=n+16;Ra(b,o);Sa(o)}Ha(l);break a}case 83:{f=Rb(b,d,e)|0;if((f|0)!=(b|0))break a;if((d-b|0)<=2){f=b;break a}if((a[b+1>>0]|0)!=116){f=b;break a}n=b+2|0;f=Sb(n,d,e)|0;if((f|0)==(n|0)){f=b;break a}g=e+4|0;d=c[g>>2]|0;if((c[e>>2]|0)==(d|0)){f=b;break a}Ta(d+-24|0,0,1827)|0;b=e+16|0;d=(c[g>>2]|0)+-24|0;c[h>>2]=c[e+12>>2];Pb(m,d,h);d=e+20|0;g=c[d>>2]|0;n=c[e+24>>2]|0;h=n;if(g>>>0<n>>>0){c[g+12>>2]=c[m+12>>2];c[g>>2]=c[m>>2];e=m+4|0;c[g+4>>2]=c[e>>2];o=m+8|0;c[g+8>>2]=c[o>>2];c[o>>2]=0;c[e>>2]=0;c[m>>2]=0;c[d>>2]=(c[d>>2]|0)+16}else{d=c[b>>2]|0;n=g-d|0;j=n>>4;g=j+1|0;if((n|0)<-16)Pa();d=h-d|0;if(d>>4>>>0<1073741823){d=d>>3;d=d>>>0<g>>>0?g:d}else d=2147483647;Qa(o,d,j,e+28|0);e=o+8|0;n=c[e>>2]|0;c[n+12>>2]=c[m+12>>2];c[n>>2]=c[m>>2];l=m+4|0;c[n+4>>2]=c[l>>2];k=m+8|0;c[n+8>>2]=c[k>>2];c[k>>2]=0;c[l>>2]=0;c[m>>2]=0;c[e>>2]=n+16;Ra(b,o);Sa(o)}Ha(m);break a}default:{f=b;break a}}while(0);i=p;return f|0}function Pb(a,b,d){a=a|0;b=b|0;d=d|0;var e=0;c[a>>2]=0;e=a+4|0;c[e>>2]=0;d=c[d>>2]|0;c[a+8>>2]=0;c[a+12>>2]=d;d=Da(d,24)|0;c[e>>2]=d;c[a>>2]=d;c[a+8>>2]=d+24;_a(d,b);_a(d+12|0,b+12|0);c[e>>2]=(c[e>>2]|0)+24;return}function Qb(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0;m=i;i=i+64|0;g=m+40|0;h=m+24|0;k=m+12|0;l=m;a:do if((d-b|0)>3?(a[b>>0]|0)==68:0){switch(a[b+1>>0]|0){case 84:case 116:break;default:break a}n=b+2|0;j=ub(n,d,e)|0;if((!((j|0)==(n|0)|(j|0)==(d|0))?(a[j>>0]|0)==69:0)?(f=c[e+4>>2]|0,(c[e>>2]|0)!=(f|0)):0){e=f+-24|0;Cb(l,e);b=Ta(l,0,1435)|0;c[k>>2]=c[b>>2];c[k+4>>2]=c[b+4>>2];c[k+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}b=Ya(k,799)|0;c[h>>2]=c[b>>2];c[h+4>>2]=c[b+4>>2];c[h+8>>2]=c[b+8>>2];f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}rb(g,h);Db(e,g);Ia(g);Ja(h);Ja(k);Ja(l);b=j+1|0}}while(0);i=m;return b|0}function Rb(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0;t=i;i=i+176|0;s=t+144|0;k=t+120|0;l=t+96|0;m=t+72|0;n=t+48|0;o=t+24|0;p=t;a:do if((d-b|0)>1?(a[b>>0]|0)==83:0){h=a[b+1>>0]|0;switch(h|0){case 97:{jb(k,1445);f=e+4|0;g=c[f>>2]|0;r=c[e+8>>2]|0;h=r;if(g>>>0<r>>>0){db(g,k);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;r=g-f|0;j=(r|0)/24|0;g=j+1|0;if((r|0)<-24)Pa();f=(h-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<g>>>0?g:f}else f=2147483647;ab(s,f,j,e+12|0);r=s+8|0;q=c[r>>2]|0;db(q,k);c[r>>2]=q+24;cb(e,s);bb(s)}Ia(k);r=b+2|0;break a}case 98:{ob(l,1460);f=e+4|0;g=c[f>>2]|0;r=c[e+8>>2]|0;h=r;if(g>>>0<r>>>0){db(g,l);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;r=g-f|0;j=(r|0)/24|0;g=j+1|0;if((r|0)<-24)Pa();f=(h-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<g>>>0?g:f}else f=2147483647;ab(s,f,j,e+12|0);r=s+8|0;q=c[r>>2]|0;db(q,l);c[r>>2]=q+24;cb(e,s);bb(s)}Ia(l);r=b+2|0;break a}case 115:{gb(m,1478);f=e+4|0;g=c[f>>2]|0;r=c[e+8>>2]|0;h=r;if(g>>>0<r>>>0){db(g,m);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;r=g-f|0;j=(r|0)/24|0;g=j+1|0;if((r|0)<-24)Pa();f=(h-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<g>>>0?g:f}else f=2147483647;ab(s,f,j,e+12|0);r=s+8|0;q=c[r>>2]|0;db(q,m);c[r>>2]=q+24;cb(e,s);bb(s)}Ia(m);r=b+2|0;break a}case 105:{lb(n,1490);f=e+4|0;g=c[f>>2]|0;r=c[e+8>>2]|0;j=r;if(g>>>0<r>>>0){db(g,n);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;r=g-f|0;h=(r|0)/24|0;g=h+1|0;if((r|0)<-24)Pa();f=(j-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<g>>>0?g:f}else f=2147483647;ab(s,f,h,e+12|0);r=s+8|0;q=c[r>>2]|0;db(q,n);c[r>>2]=q+24;cb(e,s);bb(s)}Ia(n);r=b+2|0;break a}case 111:{lb(o,1503);f=e+4|0;g=c[f>>2]|0;r=c[e+8>>2]|0;h=r;if(g>>>0<r>>>0){db(g,o);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;r=g-f|0;j=(r|0)/24|0;g=j+1|0;if((r|0)<-24)Pa();f=(h-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<g>>>0?g:f}else f=2147483647;ab(s,f,j,e+12|0);r=s+8|0;q=c[r>>2]|0;db(q,o);c[r>>2]=q+24;cb(e,s);bb(s)}Ia(o);r=b+2|0;break a}case 100:{hb(p,1516);f=e+4|0;g=c[f>>2]|0;r=c[e+8>>2]|0;h=r;if(g>>>0<r>>>0){db(g,p);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;r=g-f|0;j=(r|0)/24|0;g=j+1|0;if((r|0)<-24)Pa();f=(h-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<g>>>0?g:f}else f=2147483647;ab(s,f,j,e+12|0);r=s+8|0;q=c[r>>2]|0;db(q,p);c[r>>2]=q+24;cb(e,s);bb(s)}Ia(p);r=b+2|0;break a}case 95:{f=c[e+16>>2]|0;if((f|0)==(c[e+20>>2]|0)){r=b;break a}m=c[f+4>>2]|0;n=e+4|0;o=e+8|0;p=e+12|0;d=s+8|0;l=c[f>>2]|0;while(1){if((l|0)==(m|0)){f=55;break}f=c[n>>2]|0;q=c[o>>2]|0;g=q;if((f|0)==(q|0)){h=c[e>>2]|0;q=f-h|0;k=(q|0)/24|0;j=k+1|0;if((q|0)<-24){f=59;break}f=(g-h|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<j>>>0?j:f}else f=2147483647;ab(s,f,k,p);q=c[d>>2]|0;_a(q,l);_a(q+12|0,l+12|0);c[d>>2]=q+24;cb(e,s);bb(s)}else{_a(f,l);_a(f+12|0,l+12|0);c[n>>2]=(c[n>>2]|0)+24}l=l+24|0}if((f|0)==55){r=b+2|0;break a}else if((f|0)==59)Pa();break}default:{g=h+-48|0;f=g>>>0<10;if(!f?(gc(h)|0)==0:0){r=b;break a}k=f?g:h+-55|0;q=b+2|0;while(1){if((q|0)==(d|0)){r=b;break a}f=a[q>>0]|0;g=f<<24>>24;j=g+-48|0;h=j>>>0<10;if(!h?(gc(g)|0)==0:0)break;k=(h?j:g+-55|0)+(k*36|0)|0;q=q+1|0}if(f<<24>>24!=95){r=b;break a}f=k+1|0;d=c[e+16>>2]|0;g=d;if(f>>>0>=(c[e+20>>2]|0)-d>>4>>>0){r=b;break a}m=c[g+(f<<4)+4>>2]|0;n=e+4|0;o=e+8|0;p=e+12|0;d=s+8|0;l=c[g+(f<<4)>>2]|0;while(1){if((l|0)==(m|0)){f=75;break}f=c[n>>2]|0;b=c[o>>2]|0;g=b;if((f|0)==(b|0)){h=c[e>>2]|0;b=f-h|0;k=(b|0)/24|0;j=k+1|0;if((b|0)<-24){f=79;break}f=(g-h|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<j>>>0?j:f}else f=2147483647;ab(s,f,k,p);b=c[d>>2]|0;_a(b,l);_a(b+12|0,l+12|0);c[d>>2]=b+24;cb(e,s);bb(s)}else{_a(f,l);_a(f+12|0,l+12|0);c[n>>2]=(c[n>>2]|0)+24}l=l+24|0}if((f|0)==75){r=q+1|0;break a}else if((f|0)==79)Pa()}}}else r=b;while(0);i=t;return r|0}
function sa(a){a=a|0;var b=0;b=i;i=i+a|0;i=i+15&-16;return b|0}function ta(){return i|0}function ua(a){a=a|0;i=a}function va(a,b){a=a|0;b=b|0;i=a;j=b}function wa(a,b){a=a|0;b=b|0;if(!n){n=a;o=b}}function xa(b){b=b|0;a[k>>0]=a[b>>0];a[k+1>>0]=a[b+1>>0];a[k+2>>0]=a[b+2>>0];a[k+3>>0]=a[b+3>>0]}function ya(b){b=b|0;a[k>>0]=a[b>>0];a[k+1>>0]=a[b+1>>0];a[k+2>>0]=a[b+2>>0];a[k+3>>0]=a[b+3>>0];a[k+4>>0]=a[b+4>>0];a[k+5>>0]=a[b+5>>0];a[k+6>>0]=a[b+6>>0];a[k+7>>0]=a[b+7>>0]}function za(a){a=a|0;C=a}function Aa(){return C|0}function Ba(b,d,e,f){b=b|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0;v=i;i=i+4208|0;t=v+4176|0;h=v;u=v+4112|0;if((b|0)!=0?(g=(d|0)!=0,s=(e|0)==0,!(g&s)):0){if(g)q=c[e>>2]|0;else q=0;c[h+4096>>2]=h;g=h;c[u>>2]=0;r=u+4|0;c[r>>2]=0;c[u+8>>2]=0;c[u+12>>2]=g;l=u+16|0;c[l>>2]=0;m=u+20|0;c[m>>2]=0;c[u+24>>2]=0;c[u+28>>2]=g;c[u+32>>2]=0;h=u+36|0;c[h>>2]=0;c[u+40>>2]=0;n=u+44|0;c[n>>2]=g;k=u+48|0;j=u+61|0;c[k>>2]=0;c[k+4>>2]=0;c[k+8>>2]=0;a[k+12>>0]=0;a[j>>0]=1;k=u+32|0;Ca(t,1,0,n);n=t+8|0;o=c[n>>2]|0;c[o>>2]=0;c[o+4>>2]=0;c[o+8>>2]=0;c[o+12>>2]=g;c[n>>2]=o+16;Ea(k,t);Fa(t);n=u+62|0;a[n>>0]=0;a[u+63>>0]=1;c[t>>2]=0;o=b+(bc(b)|0)|0;La(b,o,u,t);g=c[t>>2]|0;do if(!((g|0)!=0|(a[n>>0]|0)==0)){k=c[k>>2]|0;if((k|0)!=(c[h>>2]|0)?(c[k>>2]|0)!=(c[k+4>>2]|0):0){a[n>>0]=0;a[j>>0]=0;g=c[u>>2]|0;while(1){h=c[r>>2]|0;if((h|0)==(g|0))break;k=h+-24|0;c[r>>2]=k;Ia(k)}g=c[l>>2]|0;while(1){h=c[m>>2]|0;if((h|0)==(g|0))break;l=h+-16|0;c[m>>2]=l;Ha(l)}La(b,o,u,t);if(!(a[n>>0]|0)){g=c[t>>2]|0;p=19;break}else{c[t>>2]=-2;d=0;g=-2;break}}else p=20}else p=19;while(0);if((p|0)==19)if(!g)p=20;else d=0;do if((p|0)==20){h=c[r>>2]|0;g=a[h+-24>>0]|0;if(!(g&1))j=(g&255)>>>1;else j=c[h+-20>>2]|0;g=a[h+-12>>0]|0;if(!(g&1))g=(g&255)>>>1;else g=c[h+-8>>2]|0;j=g+j|0;g=j+1|0;if(g>>>0>q>>>0){d=xc(d,g)|0;if(!d){c[t>>2]=-1;d=0;g=-1;break}if(!s)c[e>>2]=g}else if(!d){d=0;g=0;break}g=c[r>>2]|0;t=g+-12|0;h=a[t>>0]|0;e=(h&1)==0;Za(g+-24|0,e?t+1|0:c[g+-4>>2]|0,e?(h&255)>>>1:c[g+-8>>2]|0)|0;g=c[r>>2]|0;h=g+-24|0;if(!(a[h>>0]&1))g=h+1|0;else g=c[g+-16>>2]|0;Fc(d|0,g|0,j|0)|0;a[d+j>>0]=0;g=0}while(0);if(f)c[f>>2]=g;_b(u)}else if(!f)d=0;else{c[f>>2]=-3;d=0}i=v;return d|0}function Ca(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;c[a+12>>2]=0;c[a+16>>2]=e;if(!b)e=0;else e=Da(c[e>>2]|0,b<<4)|0;c[a>>2]=e;d=e+(d<<4)|0;c[a+8>>2]=d;c[a+4>>2]=d;c[a+12>>2]=e+(b<<4);return}function Da(a,b){a=a|0;b=b|0;var d=0,e=0;d=b+15&-16;e=a+4096|0;b=c[e>>2]|0;if((a+4096-b|0)>>>0<d>>>0)b=vc(d)|0;else c[e>>2]=b+d;return b|0}function Ea(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0;e=c[a>>2]|0;f=a+4|0;g=b+4|0;d=c[f>>2]|0;while(1){if((d|0)==(e|0))break;k=c[g>>2]|0;i=k+-16|0;h=d+-16|0;c[i>>2]=0;j=k+-12|0;c[j>>2]=0;l=c[d+-4>>2]|0;c[k+-8>>2]=0;c[k+-4>>2]=l;c[i>>2]=c[h>>2];i=d+-12|0;c[j>>2]=c[i>>2];j=d+-8|0;c[k+-8>>2]=c[j>>2];c[j>>2]=0;c[i>>2]=0;c[h>>2]=0;c[g>>2]=(c[g>>2]|0)+-16;d=h}j=c[a>>2]|0;c[a>>2]=c[g>>2];c[g>>2]=j;j=b+8|0;l=c[f>>2]|0;c[f>>2]=c[j>>2];c[j>>2]=l;j=a+8|0;l=b+12|0;k=c[j>>2]|0;c[j>>2]=c[l>>2];c[l>>2]=k;c[b>>2]=c[g>>2];return}function Fa(a){a=a|0;var b=0,d=0,e=0;b=c[a+4>>2]|0;d=a+8|0;while(1){e=c[d>>2]|0;if((e|0)==(b|0))break;e=e+-16|0;c[d>>2]=e;Ga(e)}b=c[a>>2]|0;if(b)Ka(c[c[a+16>>2]>>2]|0,b,(c[a+12>>2]|0)-b|0);return}function Ga(a){a=a|0;var b=0,d=0,e=0;b=c[a>>2]|0;if(b){d=a+4|0;while(1){e=c[d>>2]|0;if((e|0)==(b|0))break;e=e+-16|0;c[d>>2]=e;Ha(e)}e=c[a>>2]|0;Ka(c[a+12>>2]|0,e,(c[a+8>>2]|0)-e|0)}return}function Ha(a){a=a|0;var b=0,d=0,e=0;b=c[a>>2]|0;if(b){d=a+4|0;while(1){e=c[d>>2]|0;if((e|0)==(b|0))break;e=e+-24|0;c[d>>2]=e;Ia(e)}e=c[a>>2]|0;Ka(c[a+12>>2]|0,e,(c[a+8>>2]|0)-e|0)}return}function Ia(a){a=a|0;Ja(a+12|0);Ja(a);return}function Ja(b){b=b|0;if(a[b>>0]&1)wc(c[b+8>>2]|0);return}function Ka(a,b,d){a=a|0;b=b|0;d=d|0;if(a>>>0<=b>>>0&(a+4096|0)>>>0>=b>>>0){a=a+4096|0;if((b+(d+15&-16)|0)==(c[a>>2]|0))c[a>>2]=b}else wc(b);return}function La(b,d,e,f){b=b|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0;o=i;i=i+48|0;l=o+24|0;m=o+12|0;n=o;a:do if(b>>>0<d>>>0){b:do if((a[b>>0]|0)!=95){if((Na(b,d,e)|0)!=(d|0)){c[f>>2]=-2;break a}}else{h=d;if((h-b|0)<=3){c[f>>2]=-2;break a}switch(a[b+1>>0]|0){case 90:{k=b+2|0;b=Ma(k,d,e)|0;if(!((b|0)==(k|0)|(b|0)==(d|0))?(a[b>>0]|0)==46:0){g=c[e+4>>2]|0;if((c[e>>2]|0)!=(g|0)){k=g+-24|0;h=h-b|0;if(h>>>0>4294967279)Xa();if(h>>>0<11){a[n>>0]=h<<1;j=n+1|0}else{g=h+16&-16;j=vc(g)|0;c[n+8>>2]=j;c[n>>2]=g|1;c[n+4>>2]=h}g=j;while(1){if((b|0)==(d|0))break;a[g>>0]=a[b>>0]|0;b=b+1|0;g=g+1|0}a[j+h>>0]=0;b=Ta(n,0,849)|0;c[m>>2]=c[b>>2];c[m+4>>2]=c[b+4>>2];c[m+8>>2]=c[b+8>>2];g=0;while(1){if((g|0)==3)break;c[b+(g<<2)>>2]=0;g=g+1|0}b=Ya(m,799)|0;c[l>>2]=c[b>>2];c[l+4>>2]=c[b+4>>2];c[l+8>>2]=c[b+8>>2];g=0;while(1){if((g|0)==3)break;c[b+(g<<2)>>2]=0;g=g+1|0}b=a[l>>0]|0;j=(b&1)==0;Za(k,j?l+1|0:c[l+8>>2]|0,j?(b&255)>>>1:c[l+4>>2]|0)|0;Ja(l);Ja(m);Ja(n);b=d}}if((b|0)==(d|0))break b;c[f>>2]=-2;break a}case 95:{if((a[b+2>>0]|0)==95?(a[b+3>>0]|0)==90:0){n=b+4|0;b=Ma(n,d,e)|0;if((b|0)==(n|0)|(b|0)==(d|0)){c[f>>2]=-2;break a}c:do if((h-b|0)>12){h=0;g=b;while(1){if((h|0)>=13)break;if((a[g>>0]|0)!=(a[2320+h>>0]|0))break c;h=h+1|0;g=g+1|0}d:do if((g|0)==(d|0))g=d;else{if((a[g>>0]|0)==95){h=g+1|0;if((h|0)==(d|0))break c;if(((a[h>>0]|0)+-48|0)>>>0>=10)break c;g=g+2|0}while(1){if((g|0)==(d|0)){g=d;break d}if(((a[g>>0]|0)+-48|0)>>>0>=10)break d;g=g+1|0}}while(0);h=c[e+4>>2]|0;if((c[e>>2]|0)!=(h|0)){Ta(h+-24|0,0,2334)|0;b=g}}while(0);if((b|0)==(d|0))break b;c[f>>2]=-2;break a}break}default:{}}c[f>>2]=-2;break a}while(0);if((c[f>>2]|0)==0?(c[e>>2]|0)==(c[e+4>>2]|0):0)c[f>>2]=-2}else c[f>>2]=-2;while(0);i=o;return}function Ma(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0;E=i;i=i+80|0;z=E+60|0;y=E+48|0;r=E+36|0;s=E+24|0;t=E+12|0;w=E;a:do if((b|0)==(d|0))f=b;else{B=e+56|0;C=c[B>>2]|0;x=C+1|0;c[B>>2]=x;D=e+61|0;A=a[D>>0]|0;if(x>>>0>1)a[D>>0]=1;f=a[b>>0]|0;b:do switch(f|0){case 84:case 71:{c:do if((d-b|0)>2){switch(f|0){case 84:break;case 71:switch(a[b+1>>0]|0){case 86:{z=b+2|0;f=Wb(z,d,e)|0;if((f|0)==(z|0)){f=b;break c}g=c[e+4>>2]|0;if((c[e>>2]|0)==(g|0)){f=b;break c}Ta(g+-24|0,0,2275)|0;break c}case 82:{z=b+2|0;f=Wb(z,d,e)|0;if((f|0)==(z|0)){f=b;break c}g=c[e+4>>2]|0;if((c[e>>2]|0)==(g|0)){f=b;break c}Ta(g+-24|0,0,2295)|0;break c}default:{f=b;break c}}default:{f=b;break c}}f=b+1|0;switch(a[f>>0]|0){case 86:{z=b+2|0;f=Na(z,d,e)|0;if((f|0)==(z|0)){f=b;break c}g=c[e+4>>2]|0;if((c[e>>2]|0)==(g|0)){f=b;break c}Ta(g+-24|0,0,2124)|0;break c}case 84:{z=b+2|0;f=Na(z,d,e)|0;if((f|0)==(z|0)){f=b;break c}g=c[e+4>>2]|0;if((c[e>>2]|0)==(g|0)){f=b;break c}Ta(g+-24|0,0,2136)|0;break c}case 73:{z=b+2|0;f=Na(z,d,e)|0;if((f|0)==(z|0)){f=b;break c}g=c[e+4>>2]|0;if((c[e>>2]|0)==(g|0)){f=b;break c}Ta(g+-24|0,0,2145)|0;break c}case 83:{z=b+2|0;f=Na(z,d,e)|0;if((f|0)==(z|0)){f=b;break c}g=c[e+4>>2]|0;if((c[e>>2]|0)==(g|0)){f=b;break c}Ta(g+-24|0,0,2159)|0;break c}case 99:{z=b+2|0;f=Zb(z,d)|0;if((f|0)==(z|0)){f=b;break c}g=Zb(f,d)|0;if((g|0)==(f|0)){f=b;break c}f=Ma(g,d,e)|0;if((f|0)==(g|0)){f=b;break c}g=c[e+4>>2]|0;if((c[e>>2]|0)==(g|0)){f=b;break c}Ta(g+-24|0,0,2178)|0;break c}case 67:{x=b+2|0;f=Na(x,d,e)|0;if((f|0)==(x|0)){f=b;break c}g=tb(f,d)|0;if((g|0)==(f|0)|(g|0)==(d|0)){f=b;break c}if((a[g>>0]|0)!=95){f=b;break c}x=g+1|0;f=Na(x,d,e)|0;if((f|0)==(x|0)){f=b;break c}j=e+4|0;g=c[j>>2]|0;if(((g-(c[e>>2]|0)|0)/24|0)>>>0<2){f=b;break c}Cb(z,g+-24|0);k=c[j>>2]|0;g=k+-24|0;h=k;while(1){if((h|0)==(g|0))break;b=h+-24|0;c[j>>2]=b;Ia(b);h=c[j>>2]|0}q=k+-48|0;g=Ta(z,0,2205)|0;c[s>>2]=c[g>>2];c[s+4>>2]=c[g+4>>2];c[s+8>>2]=c[g+8>>2];h=0;while(1){if((h|0)==3)break;c[g+(h<<2)>>2]=0;h=h+1|0}g=Ya(s,2230)|0;c[r>>2]=c[g>>2];c[r+4>>2]=c[g+4>>2];c[r+8>>2]=c[g+8>>2];h=0;while(1){if((h|0)==3)break;c[g+(h<<2)>>2]=0;h=h+1|0}Cb(t,(c[j>>2]|0)+-24|0);g=a[t>>0]|0;h=(g&1)==0;g=Za(r,h?t+1|0:c[t+8>>2]|0,h?(g&255)>>>1:c[t+4>>2]|0)|0;c[y>>2]=c[g>>2];c[y+4>>2]=c[g+4>>2];c[y+8>>2]=c[g+8>>2];h=0;while(1){if((h|0)==3)break;c[g+(h<<2)>>2]=0;h=h+1|0}do if(a[q>>0]&1){p=k+-40|0;a[c[p>>2]>>0]=0;m=k+-44|0;c[m>>2]=0;g=a[q>>0]|0;if(!(g&1))l=10;else{l=c[q>>2]|0;g=l&255;l=(l&-2)+-1|0}if(!(g&1)){h=(g&255)>>>1;if((g&255)<22){k=10;n=h;o=1}else{k=(h+16&240)+-1|0;n=h;o=1}}else{k=10;n=0;o=0}if((k|0)!=(l|0)){if((k|0)==10){j=q+1|0;h=c[p>>2]|0;if(o){Fc(j|0,h|0,((g&255)>>>1)+1|0)|0;wc(h)}else{a[j>>0]=a[h>>0]|0;wc(h)}a[q>>0]=n<<1;break}h=k+1|0;j=vc(h)|0;if(!(k>>>0<=l>>>0&(j|0)==0)){if(o)Fc(j|0,q+1|0,((g&255)>>>1)+1|0)|0;else{b=c[p>>2]|0;a[j>>0]=a[b>>0]|0;wc(b)}c[q>>2]=h|1;c[m>>2]=n;c[p>>2]=j}}}else{a[q+1>>0]=0;a[q>>0]=0}while(0);c[q>>2]=c[y>>2];c[q+4>>2]=c[y+4>>2];c[q+8>>2]=c[y+8>>2];g=0;while(1){if((g|0)==3)break;c[y+(g<<2)>>2]=0;g=g+1|0}Ja(y);Ja(t);Ja(r);Ja(s);Ja(z);break c}default:{g=Zb(f,d)|0;if((g|0)==(f|0)){f=b;break c}f=Ma(g,d,e)|0;if((f|0)==(g|0)){f=b;break c}g=c[e+4>>2]|0;if((c[e>>2]|0)==(g|0)){f=b;break c}g=g+-24|0;if((a[b+2>>0]|0)==118){Ta(g,0,2235)|0;break c}else{Ta(g,0,2253)|0;break c}}}}else f=b;while(0);break}default:{f=Wb(b,d,e)|0;u=c[e+48>>2]|0;v=c[e+52>>2]|0;if((f|0)!=(b|0))if((f|0)!=(d|0)){switch(a[f>>0]|0){case 46:case 69:break b;default:{}}x=a[D>>0]|0;a[D>>0]=0;g=0;while(1){if((g|0)==3)break;c[z+(g<<2)>>2]=0;g=g+1|0}t=e+4|0;m=c[t>>2]|0;d:do if((c[e>>2]|0)!=(m|0)){l=m+-24|0;j=a[l>>0]|0;k=(j&1)==0;if(k)g=(j&255)>>>1;else g=c[m+-20>>2]|0;if(g){if(!(a[e+60>>0]|0)){if(k){g=l+1|0;h=(j&255)>>>1}else{g=c[m+-16>>2]|0;h=c[m+-20>>2]|0}if((a[g+h+-1>>0]|0)==62){if(k){g=(j&255)>>>1;h=l+1|0}else{g=c[m+-20>>2]|0;h=c[m+-16>>2]|0}if((a[h+(g+-2)>>0]|0)!=45){if(k){h=(j&255)>>>1;g=l+1|0}else{h=c[m+-20>>2]|0;g=c[m+-16>>2]|0}if((a[g+(h+-2)>>0]|0)!=62){o=Na(f,d,e)|0;if((o|0)==(f|0)){f=b;g=0;break}s=c[t>>2]|0;f=s;if(((s-(c[e>>2]|0)|0)/24|0)>>>0<2){f=b;g=0;break}g=f+-24|0;c[y>>2]=c[g>>2];c[y+4>>2]=c[g+4>>2];c[y+8>>2]=c[g+8>>2];h=0;while(1){if((h|0)==3)break;c[g+(h<<2)>>2]=0;h=h+1|0}n=f+-12|0;e:do if(!(a[z>>0]&1)){a[z+1>>0]=0;a[z>>0]=0}else{k=z+8|0;g=c[k>>2]|0;a[g>>0]=0;l=z+4|0;c[l>>2]=0;f=c[z>>2]|0;m=(f&-2)+-1|0;h=f&255;do if(!(h&1)){f=f>>>1&127;if((h&255)<22){Fc(z+1|0,g|0,f+1|0)|0;wc(g);break}g=f+16&240;j=g+-1|0;if((j|0)==(m|0))break e;h=vc(g)|0;if(j>>>0<=m>>>0&(h|0)==0)break e;Fc(h|0,z+1|0,f+1|0)|0;c[z>>2]=g|1;c[l>>2]=f;c[k>>2]=h;break e}else{a[z+1>>0]=0;wc(g);f=0}while(0);a[z>>0]=f<<1}while(0);c[z>>2]=c[n>>2];c[z+4>>2]=c[n+4>>2];c[z+8>>2]=c[n+8>>2];f=0;while(1){if((f|0)==3)break;c[n+(f<<2)>>2]=0;f=f+1|0}s=a[z>>0]|0;if(!(((s&1)==0?(s&255)>>>1:c[z+4>>2]|0)|0))zb(y,32);f=c[t>>2]|0;g=f+-24|0;h=f;while(1){if((h|0)==(g|0))break;s=h+-24|0;c[t>>2]=s;Ia(s);h=c[t>>2]|0}g=a[y>>0]|0;s=(g&1)==0;Ua(f+-48|0,0,s?y+1|0:c[y+8>>2]|0,s?(g&255)>>>1:c[y+4>>2]|0)|0;Ja(y);g=c[t>>2]|0;f=o}else g=m}else g=m}else g=m}else g=m;zb(g+-24|0,40);if((f|0)!=(d|0)?(a[f>>0]|0)==118:0){h=c[e>>2]|0;g=c[t>>2]|0;f=f+1|0}else p=128;do if((p|0)==128){n=y+4|0;o=w+8|0;p=w+1|0;q=w+4|0;r=y+8|0;s=y+1|0;l=1;f:while(1){h=c[e>>2]|0;g=c[t>>2]|0;while(1){j=(g-h|0)/24|0;m=Na(f,d,e)|0;g=c[t>>2]|0;h=c[e>>2]|0;k=(g-h|0)/24|0;if((m|0)==(f|0)){p=151;break f}if(k>>>0>j>>>0)break;else f=m}f=0;while(1){if((f|0)==3){f=j;break}c[y+(f<<2)>>2]=0;f=f+1|0}while(1){if(f>>>0>=k>>>0){h=j;break}h=a[y>>0]|0;if(((h&1)==0?(h&255)>>>1:c[n>>2]|0)|0)Ya(y,1429)|0;Cb(w,(c[e>>2]|0)+(f*24|0)|0);h=a[w>>0]|0;g=(h&1)==0;Za(y,g?p:c[o>>2]|0,g?(h&255)>>>1:c[q>>2]|0)|0;Ja(w);f=f+1|0}while(1){if(h>>>0>=k>>>0)break;g=c[t>>2]|0;f=g+-24|0;while(1){if((g|0)==(f|0))break;j=g+-24|0;c[t>>2]=j;Ia(j);g=c[t>>2]|0}h=h+1|0}h=a[y>>0]|0;f=c[n>>2]|0;if(!(((h&1)==0?(h&255)>>>1:f)|0))f=l;else{g=c[t>>2]|0;if((c[e>>2]|0)==(g|0)){p=163;break}if(!l){Ya(g+-24|0,1429)|0;g=c[t>>2]|0;h=a[y>>0]|0;f=c[n>>2]|0}l=(h&1)==0;Za(g+-24|0,l?s:c[r>>2]|0,l?(h&255)>>>1:f)|0;f=0}Ja(y);l=f;f=m}if((p|0)==151)break;else if((p|0)==163){Ja(y);f=b;g=0;break d}}while(0);if((h|0)!=(g|0)){zb(g+-24|0,41);if(u&1)Ya((c[t>>2]|0)+-24|0,267)|0;if(u&2)Ya((c[t>>2]|0)+-24|0,456)|0;if(u&4)Ya((c[t>>2]|0)+-24|0,466)|0;switch(v|0){case 1:{Ya((c[t>>2]|0)+-24|0,2032)|0;break}case 2:{Ya((c[t>>2]|0)+-24|0,2035)|0;break}default:{}}g=a[z>>0]|0;y=(g&1)==0;Za((c[t>>2]|0)+-24|0,y?z+1|0:c[z+8>>2]|0,y?(g&255)>>>1:c[z+4>>2]|0)|0;g=1}else{f=b;g=0}}else{f=b;g=0}}else{f=b;g=0}while(0);Ja(z);a[D>>0]=x;if(!g){a[D>>0]=A;c[B>>2]=C;f=b;break a}}else f=d;else f=b}}while(0);a[D>>0]=A;c[B>>2]=C}while(0);i=E;return f|0}function Na(d,e,f){d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,aa=0,ba=0,ca=0,da=0,ea=0,fa=0,ga=0,ha=0,ia=0,ja=0,ka=0,la=0,ma=0,na=0,oa=0,pa=0,qa=0,ra=0,sa=0,ta=0,ua=0,va=0,wa=0,xa=0,ya=0,za=0;za=i;i=i+736|0;ya=za+704|0;xa=za+680|0;la=za+668|0;ca=za+656|0;fa=za+632|0;pa=za+608|0;sa=za+584|0;ia=za+572|0;oa=za+560|0;qa=za+548|0;ra=za+536|0;$=za+384|0;ja=za+520|0;ha=za+512|0;A=za+496|0;o=za+488|0;S=za+472|0;O=za+464|0;B=za+448|0;p=za+440|0;na=za+424|0;ma=za+420|0;T=za+408|0;da=za+396|0;ea=za+372|0;U=za+360|0;X=za+344|0;V=za+340|0;t=za+328|0;v=za+304|0;w=za+288|0;x=za+276|0;y=za+264|0;E=za+240|0;F=za+228|0;G=za+216|0;H=za+204|0;I=za+192|0;L=za+168|0;M=za+156|0;N=za+144|0;W=za+128|0;R=za+120|0;z=za+104|0;n=za+96|0;D=za+80|0;s=za+72|0;C=za+56|0;r=za+48|0;ga=za+32|0;ba=za+24|0;wa=za+8|0;va=za;a:do if((d|0)!=(e|0)){switch(a[d>>0]|0){case 75:case 86:case 114:{c[xa>>2]=0;h=Oa(d,e,xa)|0;b:do if((h|0)!=(d|0)?(j=a[h>>0]|0,Z=f+4|0,q=((c[Z>>2]|0)-(c[f>>2]|0)|0)/24|0,Y=Na(h,e,f)|0,Z=((c[Z>>2]|0)-(c[f>>2]|0)|0)/24|0,(Y|0)!=(h|0)):0){v=j<<24>>24==70;w=f+20|0;h=c[w>>2]|0;c:do if(v){j=h+-16|0;while(1){if((h|0)==(j|0)){h=j;break c}d=h+-16|0;c[w>>2]=d;Ha(d);h=c[w>>2]|0}}while(0);n=f+16|0;o=c[f+12>>2]|0;d=c[f+24>>2]|0;j=d;if(h>>>0<d>>>0){c[h>>2]=0;c[h+4>>2]=0;c[h+8>>2]=0;c[h+12>>2]=o;c[w>>2]=(c[w>>2]|0)+16}else{k=c[n>>2]|0;d=h-k|0;m=d>>4;l=m+1|0;if((d|0)<-16)Pa();h=j-k|0;if(h>>4>>>0<1073741823){h=h>>3;h=h>>>0<l>>>0?l:h}else h=2147483647;Qa(ya,h,m,f+28|0);d=ya+8|0;e=c[d>>2]|0;c[e>>2]=0;c[e+4>>2]=0;c[e+8>>2]=0;c[e+12>>2]=o;c[d>>2]=e+16;Ra(n,ya);Sa(ya)}t=c[xa>>2]|0;r=(t&1|0)==0;s=(t&2|0)==0;t=(t&4|0)==0;u=ya+8|0;while(1){if(q>>>0>=Z>>>0){g=Y;break b}if(v){k=c[f>>2]|0;o=k+(q*24|0)+12|0;l=a[o>>0]|0;h=(l&1)==0;if(h){m=(l&255)>>>1;j=o+1|0}else{m=c[k+(q*24|0)+16>>2]|0;j=c[k+(q*24|0)+20>>2]|0}n=m+-2|0;if((a[j+n>>0]|0)==38)h=m+-3|0;else{if(h){j=o+1|0;h=(l&255)>>>1}else{j=c[k+(q*24|0)+20>>2]|0;h=c[k+(q*24|0)+16>>2]|0}h=(a[j+h+-1>>0]|0)==38?n:m}if(!r){Ta(o,h,267)|0;h=h+6|0}if(!s){Ta((c[f>>2]|0)+(q*24|0)+12|0,h,456)|0;h=h+9|0}if(!t)Ta((c[f>>2]|0)+(q*24|0)+12|0,h,466)|0}else{if(!r)Ya((c[f>>2]|0)+(q*24|0)|0,267)|0;if(!s)Ya((c[f>>2]|0)+(q*24|0)|0,456)|0;if(!t)Ya((c[f>>2]|0)+(q*24|0)|0,466)|0}m=c[w>>2]|0;n=m+-16|0;o=c[f>>2]|0;p=o+(q*24|0)|0;h=m+-12|0;j=c[h>>2]|0;d=c[m+-8>>2]|0;k=d;if((j|0)==(d|0)){h=c[n>>2]|0;d=j-h|0;l=(d|0)/24|0;j=l+1|0;if((d|0)<-24)break;h=(k-h|0)/24|0;if(h>>>0<1073741823){h=h<<1;h=h>>>0<j>>>0?j:h}else h=2147483647;ab(ya,h,l,m+-4|0);d=c[u>>2]|0;_a(d,p);_a(d+12|0,o+(q*24|0)+12|0);c[u>>2]=d+24;cb(n,ya);bb(ya)}else{_a(j,p);_a(j+12|0,o+(q*24|0)+12|0);c[h>>2]=(c[h>>2]|0)+24}q=q+1|0}Pa()}else g=d;while(0);break a}default:{}}g=eb(d,e,f)|0;if((g|0)==(d|0)){h=a[d>>0]|0;d:do switch(h<<24>>24|0){case 65:{do if(h<<24>>24==65?(u=d+1|0,(u|0)!=(e|0)):0){g=a[u>>0]|0;if(g<<24>>24==95){xa=d+2|0;g=Na(xa,e,f)|0;if((g|0)==(xa|0)){g=d;break}h=f+4|0;j=c[h>>2]|0;if((c[f>>2]|0)==(j|0)){g=d;break}e=j+-12|0;wa=a[e>>0]|0;xa=(wa&1)==0;wa=xa?(wa&255)>>>1:c[j+-8>>2]|0;$a(ya,xa?e+1|0:c[j+-4>>2]|0,wa>>>0<2?wa:2);wa=a[ya>>0]|0;e=(wa&1)==0;wa=e?(wa&255)>>>1:c[ya+4>>2]|0;xa=wa>>>0>2;e=ac(e?ya+1|0:c[ya+8>>2]|0,790,xa?2:wa)|0;Ja(ya);if(!(((e|0)==0?(wa>>>0<2?-1:xa&1):e)|0))sb((c[h>>2]|0)+-12|0);Ta((c[h>>2]|0)+-12|0,0,793)|0;break}if((g+-49&255)<9){m=tb(u,e)|0;if((m|0)==(e|0)){g=d;break}if((a[m>>0]|0)!=95){g=d;break}wa=m+1|0;g=Na(wa,e,f)|0;if((g|0)==(wa|0)){g=d;break}h=f+4|0;j=c[h>>2]|0;if((c[f>>2]|0)==(j|0)){g=d;break}e=j+-12|0;va=a[e>>0]|0;wa=(va&1)==0;va=wa?(va&255)>>>1:c[j+-8>>2]|0;$a(xa,wa?e+1|0:c[j+-4>>2]|0,va>>>0<2?va:2);va=a[xa>>0]|0;e=(va&1)==0;va=e?(va&255)>>>1:c[xa+4>>2]|0;wa=va>>>0>2;e=ac(e?xa+1|0:c[xa+8>>2]|0,790,wa?2:va)|0;Ja(xa);if(!(((e|0)==0?(va>>>0<2?-1:wa&1):e)|0))sb((c[h>>2]|0)+-12|0);n=(c[h>>2]|0)+-12|0;k=m-u|0;if(k>>>0>4294967279)Xa();if(k>>>0<11){a[fa>>0]=k<<1;l=fa+1|0}else{e=k+16&-16;l=vc(e)|0;c[fa+8>>2]=l;c[fa>>2]=e|1;c[fa+4>>2]=k}h=u;j=l;while(1){if((h|0)==(m|0))break;a[j>>0]=a[h>>0]|0;h=h+1|0;j=j+1|0}a[l+k>>0]=0;h=Ta(fa,0,790)|0;c[ca>>2]=c[h>>2];c[ca+4>>2]=c[h+4>>2];c[ca+8>>2]=c[h+8>>2];j=0;while(1){if((j|0)==3)break;c[h+(j<<2)>>2]=0;j=j+1|0}h=Ya(ca,4264)|0;c[la>>2]=c[h>>2];c[la+4>>2]=c[h+4>>2];c[la+8>>2]=c[h+8>>2];j=0;while(1){if((j|0)==3)break;c[h+(j<<2)>>2]=0;j=j+1|0}e=a[la>>0]|0;xa=(e&1)==0;Ua(n,0,xa?la+1|0:c[la+8>>2]|0,xa?(e&255)>>>1:c[la+4>>2]|0)|0;Ja(la);Ja(ca);Ja(fa);break}g=ub(u,e,f)|0;if(((!((g|0)==(u|0)|(g|0)==(e|0))?(a[g>>0]|0)==95:0)?(xa=g+1|0,aa=Na(xa,e,f)|0,(aa|0)!=(xa|0)):0)?(P=f+4|0,m=c[P>>2]|0,((m-(c[f>>2]|0)|0)/24|0)>>>0>=2):0){db(pa,m+-24|0);g=c[P>>2]|0;h=g+-24|0;j=g;while(1){if((j|0)==(h|0))break;e=j+-24|0;c[P>>2]=e;Ia(e);j=c[P>>2]|0}db(sa,g+-48|0);g=c[P>>2]|0;q=g+-24|0;do if(a[q>>0]&1){p=g+-16|0;a[c[p>>2]>>0]=0;m=g+-20|0;c[m>>2]=0;g=a[q>>0]|0;if(!(g&1))l=10;else{l=c[q>>2]|0;g=l&255;l=(l&-2)+-1|0}if(!(g&1)){h=(g&255)>>>1;if((g&255)<22){o=1;k=10;n=h}else{o=1;k=(h+16&240)+-1|0;n=h}}else{o=0;k=10;n=0}if((k|0)!=(l|0)){if((k|0)==10){j=q+1|0;h=c[p>>2]|0;if(o){Fc(j|0,h|0,((g&255)>>>1)+1|0)|0;wc(h)}else{a[j>>0]=a[h>>0]|0;wc(h)}a[q>>0]=n<<1;break}h=k+1|0;j=vc(h)|0;if(!(k>>>0<=l>>>0&(j|0)==0)){if(o)Fc(j|0,q+1|0,((g&255)>>>1)+1|0)|0;else{e=c[p>>2]|0;a[j>>0]=a[e>>0]|0;wc(e)}c[q>>2]=h|1;c[m>>2]=n;c[p>>2]=j}}}else{a[q+1>>0]=0;a[q>>0]=0}while(0);c[q>>2]=c[pa>>2];c[q+4>>2]=c[pa+4>>2];c[q+8>>2]=c[pa+8>>2];g=0;while(1){if((g|0)==3)break;c[pa+(g<<2)>>2]=0;g=g+1|0}j=pa+12|0;wa=a[j>>0]|0;e=(wa&1)==0;k=pa+16|0;wa=e?(wa&255)>>>1:c[k>>2]|0;l=pa+20|0;m=j+1|0;$a(ia,e?m:c[l>>2]|0,wa>>>0<2?wa:2);wa=a[ia>>0]|0;e=(wa&1)==0;wa=e?(wa&255)>>>1:c[ia+4>>2]|0;xa=wa>>>0>2;e=ac(e?ia+1|0:c[ia+8>>2]|0,790,xa?2:wa)|0;Ja(ia);if(!(((e|0)==0?(wa>>>0<2?-1:xa&1):e)|0))sb(j);n=c[P>>2]|0;q=n+-12|0;Cb($,sa);g=Ta($,0,790)|0;c[ra>>2]=c[g>>2];c[ra+4>>2]=c[g+4>>2];c[ra+8>>2]=c[g+8>>2];h=0;while(1){if((h|0)==3)break;c[g+(h<<2)>>2]=0;h=h+1|0}g=Ya(ra,4264)|0;c[qa>>2]=c[g>>2];c[qa+4>>2]=c[g+4>>2];c[qa+8>>2]=c[g+8>>2];h=0;while(1){if((h|0)==3)break;c[g+(h<<2)>>2]=0;h=h+1|0}g=a[j>>0]|0;h=(g&1)==0;g=Za(qa,h?m:c[l>>2]|0,h?(g&255)>>>1:c[k>>2]|0)|0;c[oa>>2]=c[g>>2];c[oa+4>>2]=c[g+4>>2];c[oa+8>>2]=c[g+8>>2];h=0;while(1){if((h|0)==3)break;c[g+(h<<2)>>2]=0;h=h+1|0}do if(a[q>>0]&1){p=n+-4|0;a[c[p>>2]>>0]=0;m=n+-8|0;c[m>>2]=0;g=a[q>>0]|0;if(!(g&1))l=10;else{l=c[q>>2]|0;g=l&255;l=(l&-2)+-1|0}do if(!(g&1)){h=(g&255)>>>1;if((g&255)<22){o=1;k=10;n=h;break}o=1;k=(h+16&240)+-1|0;n=h}else{o=0;k=10;n=0}while(0);if((k|0)!=(l|0)){if((k|0)==10){j=q+1|0;h=c[p>>2]|0;if(o){Fc(j|0,h|0,((g&255)>>>1)+1|0)|0;wc(h)}else{a[j>>0]=a[h>>0]|0;wc(h)}a[q>>0]=n<<1;break}h=k+1|0;j=vc(h)|0;if(k>>>0<=l>>>0&(j|0)==0)break;if(o)Fc(j|0,q+1|0,((g&255)>>>1)+1|0)|0;else{e=c[p>>2]|0;a[j>>0]=a[e>>0]|0;wc(e)}c[q>>2]=h|1;c[m>>2]=n;c[p>>2]=j}}else{a[q+1>>0]=0;a[q>>0]=0}while(0);c[q>>2]=c[oa>>2];c[q+4>>2]=c[oa+4>>2];c[q+8>>2]=c[oa+8>>2];g=0;while(1){if((g|0)==3)break;c[oa+(g<<2)>>2]=0;g=g+1|0}Ja(oa);Ja(qa);Ja(ra);Ja($);Ia(sa);Ia(pa);g=aa}else g=d}else g=d;while(0);if((g|0)==(d|0)){g=d;break a}h=c[f+4>>2]|0;if((c[f>>2]|0)==(h|0)){g=d;break a}m=f+16|0;c[ha>>2]=c[f+12>>2];Pb(ja,h+-24|0,ha);h=f+20|0;j=c[h>>2]|0;d=c[f+24>>2]|0;k=d;if(j>>>0<d>>>0){c[j+12>>2]=c[ja+12>>2];c[j>>2]=c[ja>>2];ya=ja+4|0;c[j+4>>2]=c[ya>>2];f=ja+8|0;c[j+8>>2]=c[f>>2];c[f>>2]=0;c[ya>>2]=0;c[ja>>2]=0;c[h>>2]=(c[h>>2]|0)+16}else{h=c[m>>2]|0;d=j-h|0;l=d>>4;j=l+1|0;if((d|0)<-16)Pa();h=k-h|0;if(h>>4>>>0<1073741823){h=h>>3;h=h>>>0<j>>>0?j:h}else h=2147483647;Qa(ya,h,l,f+28|0);f=ya+8|0;d=c[f>>2]|0;c[d+12>>2]=c[ja+12>>2];c[d>>2]=c[ja>>2];e=ja+4|0;c[d+4>>2]=c[e>>2];xa=ja+8|0;c[d+8>>2]=c[xa>>2];c[xa>>2]=0;c[e>>2]=0;c[ja>>2]=0;c[f>>2]=d+16;Ra(m,ya);Sa(ya)}Ha(ja);break a}case 67:{xa=d+1|0;g=Na(xa,e,f)|0;if((g|0)==(xa|0)){g=d;break a}j=f+4|0;h=c[j>>2]|0;if((c[f>>2]|0)==(h|0)){g=d;break a}Ya(h+-24|0,2023)|0;m=f+16|0;h=(c[j>>2]|0)+-24|0;c[o>>2]=c[f+12>>2];Pb(A,h,o);h=f+20|0;j=c[h>>2]|0;d=c[f+24>>2]|0;k=d;if(j>>>0<d>>>0){c[j+12>>2]=c[A+12>>2];c[j>>2]=c[A>>2];ya=A+4|0;c[j+4>>2]=c[ya>>2];f=A+8|0;c[j+8>>2]=c[f>>2];c[f>>2]=0;c[ya>>2]=0;c[A>>2]=0;c[h>>2]=(c[h>>2]|0)+16}else{h=c[m>>2]|0;d=j-h|0;l=d>>4;j=l+1|0;if((d|0)<-16)Pa();h=k-h|0;if(h>>4>>>0<1073741823){h=h>>3;h=h>>>0<j>>>0?j:h}else h=2147483647;Qa(ya,h,l,f+28|0);f=ya+8|0;d=c[f>>2]|0;c[d+12>>2]=c[A+12>>2];c[d>>2]=c[A>>2];e=A+4|0;c[d+4>>2]=c[e>>2];xa=A+8|0;c[d+8>>2]=c[xa>>2];c[xa>>2]=0;c[e>>2]=0;c[A>>2]=0;c[f>>2]=d+16;Ra(m,ya);Sa(ya)}Ha(A);break a}case 70:{do if(h<<24>>24==70){g=d+1|0;if((g|0)!=(e|0)){if((a[g>>0]|0)==89){g=d+2|0;if((g|0)==(e|0))break}h=Na(g,e,f)|0;if((h|0)!=(g|0)){$a(ya,797,1);r=f+4|0;q=ya+4|0;m=xa+8|0;n=xa+1|0;o=xa+4|0;p=0;g=h;e:while(1){j=g;f:while(1){if((j|0)==(e|0)){ta=170;break e}switch(a[j>>0]|0){case 69:{ta=174;break e}case 118:{j=j+1|0;continue f}case 82:{g=j+1|0;if((g|0)!=(e|0)?(a[g>>0]|0)==69:0){p=1;continue e}break}case 79:{g=j+1|0;if((g|0)!=(e|0)?(a[g>>0]|0)==69:0){p=2;continue e}break}default:{}}h=((c[r>>2]|0)-(c[f>>2]|0)|0)/24|0;k=Na(j,e,f)|0;l=((c[r>>2]|0)-(c[f>>2]|0)|0)/24|0;if((k|0)==(j|0)|(k|0)==(e|0))break e;else g=h;while(1){if(g>>>0>=l>>>0)break;wa=a[ya>>0]|0;if(((wa&1)==0?(wa&255)>>>1:c[q>>2]|0)>>>0>1)Ya(ya,1429)|0;Cb(xa,(c[f>>2]|0)+(g*24|0)|0);wa=a[xa>>0]|0;va=(wa&1)==0;Za(ya,va?n:c[m>>2]|0,va?(wa&255)>>>1:c[o>>2]|0)|0;Ja(xa);g=g+1|0}while(1){if(h>>>0>=l>>>0){j=k;continue f}j=c[r>>2]|0;g=j+-24|0;while(1){if((j|0)==(g|0))break;wa=j+-24|0;c[r>>2]=wa;Ia(wa);j=c[r>>2]|0}h=h+1|0}}}g:do if((ta|0)==170){h=c[r>>2]|0;g=h+-24|0;while(1){if((h|0)==(g|0))break g;f=h+-24|0;c[r>>2]=f;Ia(f);h=c[r>>2]|0}}else if((ta|0)==174){g=j+1|0;Ya(ya,799)|0;switch(p|0){case 1:{Ya(ya,2032)|0;break}case 2:{Ya(ya,2035)|0;break}default:{}}h=c[r>>2]|0;if((c[f>>2]|0)!=(h|0)){Ya(h+-24|0,1882)|0;e=a[ya>>0]|0;xa=(e&1)==0;Ua((c[r>>2]|0)+-12|0,0,xa?ya+1|0:c[ya+8>>2]|0,xa?(e&255)>>>1:c[q>>2]|0)|0;Ja(ya);if((g|0)==(d|0)){g=d;break a}h=c[r>>2]|0;if((c[f>>2]|0)==(h|0)){g=d;break a}m=f+16|0;c[O>>2]=c[f+12>>2];Pb(S,h+-24|0,O);h=f+20|0;j=c[h>>2]|0;d=c[f+24>>2]|0;k=d;if(j>>>0<d>>>0){c[j+12>>2]=c[S+12>>2];c[j>>2]=c[S>>2];ya=S+4|0;c[j+4>>2]=c[ya>>2];f=S+8|0;c[j+8>>2]=c[f>>2];c[f>>2]=0;c[ya>>2]=0;c[S>>2]=0;c[h>>2]=(c[h>>2]|0)+16}else{h=c[m>>2]|0;d=j-h|0;l=d>>4;j=l+1|0;if((d|0)<-16)Pa();h=k-h|0;if(h>>4>>>0<1073741823){h=h>>3;h=h>>>0<j>>>0?j:h}else h=2147483647;Qa(ya,h,l,f+28|0);f=ya+8|0;d=c[f>>2]|0;c[d+12>>2]=c[S+12>>2];c[d>>2]=c[S>>2];e=S+4|0;c[d+4>>2]=c[e>>2];xa=S+8|0;c[d+8>>2]=c[xa>>2];c[xa>>2]=0;c[e>>2]=0;c[S>>2]=0;c[f>>2]=d+16;Ra(m,ya);Sa(ya)}Ha(S);break a}}while(0);Ja(ya);break}}g=d;break a}while(0);g=d;break a}case 71:{xa=d+1|0;g=Na(xa,e,f)|0;if((g|0)==(xa|0)){g=d;break a}j=f+4|0;h=c[j>>2]|0;if((c[f>>2]|0)==(h|0)){g=d;break a}Ya(h+-24|0,2039)|0;m=f+16|0;h=(c[j>>2]|0)+-24|0;c[p>>2]=c[f+12>>2];Pb(B,h,p);h=f+20|0;j=c[h>>2]|0;d=c[f+24>>2]|0;k=d;if(j>>>0<d>>>0){c[j+12>>2]=c[B+12>>2];c[j>>2]=c[B>>2];ya=B+4|0;c[j+4>>2]=c[ya>>2];f=B+8|0;c[j+8>>2]=c[f>>2];c[f>>2]=0;c[ya>>2]=0;c[B>>2]=0;c[h>>2]=(c[h>>2]|0)+16}else{h=c[m>>2]|0;d=j-h|0;l=d>>4;j=l+1|0;if((d|0)<-16)Pa();h=k-h|0;if(h>>4>>>0<1073741823){h=h>>3;h=h>>>0<j>>>0?j:h}else h=2147483647;Qa(ya,h,l,f+28|0);f=ya+8|0;d=c[f>>2]|0;c[d+12>>2]=c[B+12>>2];c[d>>2]=c[B>>2];e=B+4|0;c[d+4>>2]=c[e>>2];xa=B+8|0;c[d+8>>2]=c[xa>>2];c[xa>>2]=0;c[e>>2]=0;c[B>>2]=0;c[f>>2]=d+16;Ra(m,ya);Sa(ya)}Ha(B);break a}case 77:{if(((h<<24>>24==77?(wa=d+1|0,k=Na(wa,e,f)|0,(k|0)!=(wa|0)):0)?(ka=Na(k,e,f)|0,(ka|0)!=(k|0)):0)?(_=f+4|0,l=c[_>>2]|0,((l-(c[f>>2]|0)|0)/24|0)>>>0>=2):0){db(ya,l+-24|0);g=c[_>>2]|0;h=g+-24|0;j=g;while(1){if((j|0)==(h|0))break;e=j+-24|0;c[_>>2]=e;Ia(e);j=c[_>>2]|0}db(xa,g+-48|0);r=ya+12|0;j=c[_>>2]|0;q=j+-24|0;h:do if((a[((a[r>>0]&1)==0?r+1|0:c[ya+20>>2]|0)>>0]|0)==40){g=Ya(ya,797)|0;c[fa>>2]=c[g>>2];c[fa+4>>2]=c[g+4>>2];c[fa+8>>2]=c[g+8>>2];h=0;while(1){if((h|0)==3)break;c[g+(h<<2)>>2]=0;h=h+1|0}Cb(pa,xa);g=a[pa>>0]|0;h=(g&1)==0;g=Za(fa,h?pa+1|0:c[pa+8>>2]|0,h?(g&255)>>>1:c[pa+4>>2]|0)|0;c[ca>>2]=c[g>>2];c[ca+4>>2]=c[g+4>>2];c[ca+8>>2]=c[g+8>>2];h=0;while(1){if((h|0)==3)break;c[g+(h<<2)>>2]=0;h=h+1|0}g=Ya(ca,2050)|0;c[la>>2]=c[g>>2];c[la+4>>2]=c[g+4>>2];c[la+8>>2]=c[g+8>>2];h=0;while(1){if((h|0)==3)break;c[g+(h<<2)>>2]=0;h=h+1|0}do if(a[q>>0]&1){p=j+-16|0;a[c[p>>2]>>0]=0;m=j+-20|0;c[m>>2]=0;g=a[q>>0]|0;if(!(g&1))l=10;else{l=c[q>>2]|0;g=l&255;l=(l&-2)+-1|0}if(!(g&1)){h=(g&255)>>>1;if((g&255)<22){o=1;k=10;n=h}else{o=1;k=(h+16&240)+-1|0;n=h}}else{o=0;k=10;n=0}if((k|0)!=(l|0)){if((k|0)==10){j=q+1|0;h=c[p>>2]|0;if(o){Fc(j|0,h|0,((g&255)>>>1)+1|0)|0;wc(h)}else{a[j>>0]=a[h>>0]|0;wc(h)}a[q>>0]=n<<1;break}h=k+1|0;j=vc(h)|0;if(!(k>>>0<=l>>>0&(j|0)==0)){if(o)Fc(j|0,q+1|0,((g&255)>>>1)+1|0)|0;else{e=c[p>>2]|0;a[j>>0]=a[e>>0]|0;wc(e)}c[q>>2]=h|1;c[m>>2]=n;c[p>>2]=j}}}else{a[q+1>>0]=0;a[q>>0]=0}while(0);c[q>>2]=c[la>>2];c[q+4>>2]=c[la+4>>2];c[q+8>>2]=c[la+8>>2];g=0;while(1){if((g|0)==3)break;c[la+(g<<2)>>2]=0;g=g+1|0}Ja(la);Ja(ca);Ja(pa);Ja(fa);j=c[_>>2]|0;g=Ta(r,0,799)|0;c[sa>>2]=c[g>>2];c[sa+4>>2]=c[g+4>>2];c[sa+8>>2]=c[g+8>>2];h=0;while(1){if((h|0)==3)break;c[g+(h<<2)>>2]=0;h=h+1|0}q=j+-12|0;do if(a[q>>0]&1){p=j+-4|0;a[c[p>>2]>>0]=0;m=j+-8|0;c[m>>2]=0;g=a[q>>0]|0;if(!(g&1))l=10;else{l=c[q>>2]|0;g=l&255;l=(l&-2)+-1|0}if(!(g&1)){h=(g&255)>>>1;if((g&255)<22){o=1;k=10;n=h}else{o=1;k=(h+16&240)+-1|0;n=h}}else{o=0;k=10;n=0}if((k|0)!=(l|0)){if((k|0)==10){j=q+1|0;h=c[p>>2]|0;if(o){Fc(j|0,h|0,((g&255)>>>1)+1|0)|0;wc(h)}else{a[j>>0]=a[h>>0]|0;wc(h)}a[q>>0]=n<<1;break}h=k+1|0;j=vc(h)|0;if(!(k>>>0<=l>>>0&(j|0)==0)){if(o)Fc(j|0,q+1|0,((g&255)>>>1)+1|0)|0;else{e=c[p>>2]|0;a[j>>0]=a[e>>0]|0;wc(e)}c[q>>2]=h|1;c[m>>2]=n;c[p>>2]=j}}}else{a[q+1>>0]=0;a[q>>0]=0}while(0);c[q>>2]=c[sa>>2];c[q+4>>2]=c[sa+4>>2];c[q+8>>2]=c[sa+8>>2];g=0;while(1){if((g|0)==3)break;c[sa+(g<<2)>>2]=0;g=g+1|0}Ja(sa)}else{g=Ya(ya,1882)|0;c[qa>>2]=c[g>>2];c[qa+4>>2]=c[g+4>>2];c[qa+8>>2]=c[g+8>>2];h=0;while(1){if((h|0)==3)break;c[g+(h<<2)>>2]=0;h=h+1|0}Cb(ra,xa);g=a[ra>>0]|0;h=(g&1)==0;g=Za(qa,h?ra+1|0:c[ra+8>>2]|0,h?(g&255)>>>1:c[ra+4>>2]|0)|0;c[oa>>2]=c[g>>2];c[oa+4>>2]=c[g+4>>2];c[oa+8>>2]=c[g+8>>2];h=0;while(1){if((h|0)==3)break;c[g+(h<<2)>>2]=0;h=h+1|0}g=Ya(oa,2050)|0;c[ia>>2]=c[g>>2];c[ia+4>>2]=c[g+4>>2];c[ia+8>>2]=c[g+8>>2];h=0;while(1){if((h|0)==3)break;c[g+(h<<2)>>2]=0;h=h+1|0}do if(a[q>>0]&1){p=j+-16|0;a[c[p>>2]>>0]=0;m=j+-20|0;c[m>>2]=0;g=a[q>>0]|0;if(!(g&1))l=10;else{g=c[q>>2]|0;l=(g&-2)+-1|0;g=g&255}if(!(g&1)){h=(g&255)>>>1;if((g&255)<22){n=h;o=1;k=10}else{n=h;o=1;k=(h+16&240)+-1|0}}else{n=0;o=0;k=10}if((k|0)!=(l|0)){if((k|0)==10){j=q+1|0;h=c[p>>2]|0;if(o){Fc(j|0,h|0,((g&255)>>>1)+1|0)|0;wc(h)}else{a[j>>0]=a[h>>0]|0;wc(h)}a[q>>0]=n<<1;break}h=k+1|0;j=vc(h)|0;if(!(k>>>0<=l>>>0&(j|0)==0)){if(o)Fc(j|0,q+1|0,((g&255)>>>1)+1|0)|0;else{e=c[p>>2]|0;a[j>>0]=a[e>>0]|0;wc(e)}c[q>>2]=h|1;c[m>>2]=n;c[p>>2]=j}}}else{a[q+1>>0]=0;a[q>>0]=0}while(0);c[q>>2]=c[ia>>2];c[q+4>>2]=c[ia+4>>2];c[q+8>>2]=c[ia+8>>2];g=0;while(1){if((g|0)==3)break;c[ia+(g<<2)>>2]=0;g=g+1|0}Ja(ia);Ja(oa);Ja(ra);Ja(qa);g=c[_>>2]|0;q=g+-12|0;do if(a[q>>0]&1){p=g+-4|0;a[c[p>>2]>>0]=0;m=g+-8|0;c[m>>2]=0;g=a[q>>0]|0;if(!(g&1))l=10;else{l=c[q>>2]|0;g=l&255;l=(l&-2)+-1|0}if(!(g&1)){h=(g&255)>>>1;if((g&255)<22){k=10;n=h;o=1}else{k=(h+16&240)+-1|0;n=h;o=1}}else{k=10;n=0;o=0}if((k|0)!=(l|0)){if((k|0)==10){j=q+1|0;h=c[p>>2]|0;if(o){Fc(j|0,h|0,((g&255)>>>1)+1|0)|0;wc(h)}else{a[j>>0]=a[h>>0]|0;wc(h)}a[q>>0]=n<<1;break}h=k+1|0;j=vc(h)|0;if(!(k>>>0<=l>>>0&(j|0)==0)){if(o)Fc(j|0,q+1|0,((g&255)>>>1)+1|0)|0;else{e=c[p>>2]|0;a[j>>0]=a[e>>0]|0;wc(e)}c[q>>2]=h|1;c[m>>2]=n;c[p>>2]=j}}}else{a[q+1>>0]=0;a[q>>0]=0}while(0);c[q>>2]=c[r>>2];c[q+4>>2]=c[r+4>>2];c[q+8>>2]=c[r+8>>2];g=0;while(1){if((g|0)==3)break h;c[r+(g<<2)>>2]=0;g=g+1|0}}while(0);Ia(xa);Ia(ya);g=ka}else g=d;if((g|0)==(d|0)){g=d;break a}h=c[f+4>>2]|0;if((c[f>>2]|0)==(h|0)){g=d;break a}m=f+16|0;c[ma>>2]=c[f+12>>2];Pb(na,h+-24|0,ma);h=f+20|0;j=c[h>>2]|0;d=c[f+24>>2]|0;k=d;if(j>>>0<d>>>0){c[j+12>>2]=c[na+12>>2];c[j>>2]=c[na>>2];ya=na+4|0;c[j+4>>2]=c[ya>>2];f=na+8|0;c[j+8>>2]=c[f>>2];c[f>>2]=0;c[ya>>2]=0;c[na>>2]=0;c[h>>2]=(c[h>>2]|0)+16}else{h=c[m>>2]|0;d=j-h|0;l=d>>4;j=l+1|0;if((d|0)<-16)Pa();h=k-h|0;if(h>>4>>>0<1073741823){h=h>>3;h=h>>>0<j>>>0?j:h}else h=2147483647;Qa(ya,h,l,f+28|0);f=ya+8|0;d=c[f>>2]|0;c[d+12>>2]=c[na+12>>2];c[d>>2]=c[na>>2];e=na+4|0;c[d+4>>2]=c[e>>2];xa=na+8|0;c[d+8>>2]=c[xa>>2];c[xa>>2]=0;c[e>>2]=0;c[na>>2]=0;c[f>>2]=d+16;Ra(m,ya);Sa(ya)}Ha(na);break a}case 79:{v=f+4|0;p=((c[v>>2]|0)-(c[f>>2]|0)|0)/24|0;xa=d+1|0;g=Na(xa,e,f)|0;v=((c[v>>2]|0)-(c[f>>2]|0)|0)/24|0;if((g|0)==(xa|0)){g=d;break a}n=f+16|0;o=c[f+12>>2]|0;w=f+20|0;h=c[w>>2]|0;xa=c[f+24>>2]|0;j=xa;if(h>>>0<xa>>>0){c[h>>2]=0;c[h+4>>2]=0;c[h+8>>2]=0;c[h+12>>2]=o;c[w>>2]=(c[w>>2]|0)+16}else{k=c[n>>2]|0;xa=h-k|0;m=xa>>4;l=m+1|0;if((xa|0)<-16)Pa();h=j-k|0;if(h>>4>>>0<1073741823){h=h>>3;h=h>>>0<l>>>0?l:h}else h=2147483647;Qa(ya,h,m,f+28|0);xa=ya+8|0;ta=c[xa>>2]|0;c[ta>>2]=0;c[ta+4>>2]=0;c[ta+8>>2]=0;c[ta+12>>2]=o;c[xa>>2]=ta+16;Ra(n,ya);Sa(ya)}r=T+4|0;s=T+8|0;t=T+1|0;u=ya+8|0;while(1){if(p>>>0>=v>>>0)break a;xa=c[f>>2]|0;ta=xa+(p*24|0)+12|0;sa=a[ta>>0]|0;j=(sa&1)==0;sa=j?(sa&255)>>>1:c[xa+(p*24|0)+16>>2]|0;$a(T,j?ta+1|0:c[xa+(p*24|0)+20>>2]|0,sa>>>0<2?sa:2);sa=a[T>>0]|0;xa=(sa&1)==0;sa=xa?(sa&255)>>>1:c[r>>2]|0;ta=sa>>>0>2;xa=ac(xa?t:c[s>>2]|0,790,ta?2:sa)|0;Ja(T);j=c[f>>2]|0;if(((xa|0)==0?(sa>>>0<2?-1:ta&1):xa)|0){h=b[j+(p*24|0)+12>>1]|0;if(!(h&1))h=(h&65535)>>>8&255;else h=a[c[j+(p*24|0)+20>>2]>>0]|0;if(h<<24>>24==40){Ya(j+(p*24|0)|0,797)|0;Ta((c[f>>2]|0)+(p*24|0)+12|0,0,799)|0}}else{Ya(j+(p*24|0)|0,849)|0;Ta((c[f>>2]|0)+(p*24|0)+12|0,0,799)|0}Ya((c[f>>2]|0)+(p*24|0)|0,841)|0;m=c[w>>2]|0;n=m+-16|0;o=c[f>>2]|0;q=o+(p*24|0)|0;h=m+-12|0;j=c[h>>2]|0;xa=c[m+-8>>2]|0;k=xa;if((j|0)==(xa|0)){h=c[n>>2]|0;xa=j-h|0;l=(xa|0)/24|0;j=l+1|0;if((xa|0)<-24)break;h=(k-h|0)/24|0;if(h>>>0<1073741823){h=h<<1;h=h>>>0<j>>>0?j:h}else h=2147483647;ab(ya,h,l,m+-4|0);xa=c[u>>2]|0;_a(xa,q);_a(xa+12|0,o+(p*24|0)+12|0);c[u>>2]=xa+24;cb(n,ya);bb(ya)}else{_a(j,q);_a(j+12|0,o+(p*24|0)+12|0);c[h>>2]=(c[h>>2]|0)+24}p=p+1|0}Pa();break}case 80:{B=f+4|0;p=((c[B>>2]|0)-(c[f>>2]|0)|0)/24|0;A=d+1|0;g=Na(A,e,f)|0;B=((c[B>>2]|0)-(c[f>>2]|0)|0)/24|0;if((g|0)==(A|0)){g=d;break a}n=f+16|0;o=c[f+12>>2]|0;C=f+20|0;h=c[C>>2]|0;xa=c[f+24>>2]|0;j=xa;if(h>>>0<xa>>>0){c[h>>2]=0;c[h+4>>2]=0;c[h+8>>2]=0;c[h+12>>2]=o;c[C>>2]=(c[C>>2]|0)+16}else{k=c[n>>2]|0;xa=h-k|0;m=xa>>4;l=m+1|0;if((xa|0)<-16)Pa();h=j-k|0;if(h>>4>>>0<1073741823){h=h>>3;h=h>>>0<l>>>0?l:h}else h=2147483647;Qa(ya,h,m,f+28|0);xa=ya+8|0;sa=c[xa>>2]|0;c[sa>>2]=0;c[sa+4>>2]=0;c[sa+8>>2]=0;c[sa+12>>2]=o;c[xa>>2]=sa+16;Ra(n,ya);Sa(ya)}t=da+4|0;u=da+8|0;v=da+1|0;w=ea+4|0;x=ea+8|0;y=ea+1|0;z=ya+8|0;while(1){if(p>>>0>=B>>>0)break a;xa=c[f>>2]|0;sa=xa+(p*24|0)+12|0;ra=a[sa>>0]|0;j=(ra&1)==0;ra=j?(ra&255)>>>1:c[xa+(p*24|0)+16>>2]|0;$a(da,j?sa+1|0:c[xa+(p*24|0)+20>>2]|0,ra>>>0<2?ra:2);ra=a[da>>0]|0;xa=(ra&1)==0;ra=xa?(ra&255)>>>1:c[t>>2]|0;sa=ra>>>0>2;xa=ac(xa?v:c[u>>2]|0,790,sa?2:ra)|0;Ja(da);j=c[f>>2]|0;if(((xa|0)==0?(ra>>>0<2?-1:sa&1):xa)|0){h=b[j+(p*24|0)+12>>1]|0;if(!(h&1))h=(h&65535)>>>8&255;else h=a[c[j+(p*24|0)+20>>2]>>0]|0;if(h<<24>>24==40){Ya(j+(p*24|0)|0,797)|0;Ta((c[f>>2]|0)+(p*24|0)+12|0,0,799)|0}}else{Ya(j+(p*24|0)|0,849)|0;Ta((c[f>>2]|0)+(p*24|0)+12|0,0,799)|0}j=c[f>>2]|0;h=j+(p*24|0)|0;do if((a[A>>0]|0)==85){ra=a[h>>0]|0;xa=(ra&1)==0;ra=xa?(ra&255)>>>1:c[j+(p*24|0)+4>>2]|0;$a(ea,xa?h+1|0:c[j+(p*24|0)+8>>2]|0,ra>>>0<12?ra:12);ra=a[ea>>0]|0;xa=(ra&1)==0;ra=xa?(ra&255)>>>1:c[w>>2]|0;sa=ra>>>0>12;xa=ac(xa?y:c[x>>2]|0,2054,sa?12:ra)|0;Ja(ea);s=c[f>>2]|0;h=s+(p*24|0)|0;if(!(((xa|0)==0?(ra>>>0<12?-1:sa&1):xa)|0)){j=a[h>>0]|0;if(!(j&1)){o=(j&255)>>>1;r=o;o=o>>>0<11?o:11;k=10}else{o=c[s+(p*24|0)+4>>2]|0;j=c[h>>2]|0;r=o;o=o>>>0<11?o:11;k=(j&-2)+-1|0;j=j&255}if((o-r+k|0)>>>0<2){Wa(h,k,2-o+r-k|0,r,0,o,2,2067);break}if(!(j&1))q=h+1|0;else q=c[s+(p*24|0)+8>>2]|0;do if((o|0)!=2){n=r-o|0;if((r|0)==(o|0)){k=o;m=0;l=2067;j=2;ta=402}else{if(o>>>0>2){a[q>>0]=105;a[q+1>>0]=100;Hc(q+2|0,q+o|0,n|0)|0;k=o;j=2;break}do if(q>>>0<2067>>>0&(q+r|0)>>>0>2067>>>0)if((q+o|0)>>>0>2067>>>0){Fc(q|0,2067,o|0)|0;m=o;l=2069;k=0;j=2-o|0;break}else{m=0;l=2067+(2-o)|0;k=o;j=2;break}else{m=0;l=2067;k=o;j=2}while(0);ta=q+m|0;Hc(ta+j|0,ta+k|0,n|0)|0;ta=402}}else{k=2;m=0;l=2067;j=2;ta=402}while(0);if((ta|0)==402){ta=0;Hc(q+m|0,l|0,j|0)|0}j=j-k+r|0;if(!(a[h>>0]&1))a[h>>0]=j<<1;else c[s+(p*24|0)+4>>2]=j;a[q+j>>0]=0}else ta=385}else ta=385;while(0);if((ta|0)==385){ta=0;Ya(h,4262)|0}m=c[C>>2]|0;n=m+-16|0;o=c[f>>2]|0;q=o+(p*24|0)|0;h=m+-12|0;j=c[h>>2]|0;xa=c[m+-8>>2]|0;k=xa;if((j|0)==(xa|0)){h=c[n>>2]|0;xa=j-h|0;l=(xa|0)/24|0;j=l+1|0;if((xa|0)<-24)break;h=(k-h|0)/24|0;if(h>>>0<1073741823){h=h<<1;h=h>>>0<j>>>0?j:h}else h=2147483647;ab(ya,h,l,m+-4|0);xa=c[z>>2]|0;_a(xa,q);_a(xa+12|0,o+(p*24|0)+12|0);c[z>>2]=xa+24;cb(n,ya);bb(ya)}else{_a(j,q);_a(j+12|0,o+(p*24|0)+12|0);c[h>>2]=(c[h>>2]|0)+24}p=p+1|0}Pa();break}case 82:{v=f+4|0;p=((c[v>>2]|0)-(c[f>>2]|0)|0)/24|0;xa=d+1|0;g=Na(xa,e,f)|0;v=((c[v>>2]|0)-(c[f>>2]|0)|0)/24|0;if((g|0)==(xa|0)){g=d;break a}n=f+16|0;o=c[f+12>>2]|0;w=f+20|0;h=c[w>>2]|0;xa=c[f+24>>2]|0;j=xa;if(h>>>0<xa>>>0){c[h>>2]=0;c[h+4>>2]=0;c[h+8>>2]=0;c[h+12>>2]=o;c[w>>2]=(c[w>>2]|0)+16}else{k=c[n>>2]|0;xa=h-k|0;m=xa>>4;l=m+1|0;if((xa|0)<-16)Pa();h=j-k|0;if(h>>4>>>0<1073741823){h=h>>3;h=h>>>0<l>>>0?l:h}else h=2147483647;Qa(ya,h,m,f+28|0);xa=ya+8|0;ta=c[xa>>2]|0;c[ta>>2]=0;c[ta+4>>2]=0;c[ta+8>>2]=0;c[ta+12>>2]=o;c[xa>>2]=ta+16;Ra(n,ya);Sa(ya)}r=U+4|0;s=U+8|0;t=U+1|0;u=ya+8|0;while(1){if(p>>>0>=v>>>0)break a;xa=c[f>>2]|0;ta=xa+(p*24|0)+12|0;sa=a[ta>>0]|0;j=(sa&1)==0;sa=j?(sa&255)>>>1:c[xa+(p*24|0)+16>>2]|0;$a(U,j?ta+1|0:c[xa+(p*24|0)+20>>2]|0,sa>>>0<2?sa:2);sa=a[U>>0]|0;xa=(sa&1)==0;sa=xa?(sa&255)>>>1:c[r>>2]|0;ta=sa>>>0>2;xa=ac(xa?t:c[s>>2]|0,790,ta?2:sa)|0;Ja(U);j=c[f>>2]|0;if(((xa|0)==0?(sa>>>0<2?-1:ta&1):xa)|0){h=b[j+(p*24|0)+12>>1]|0;if(!(h&1))h=(h&65535)>>>8&255;else h=a[c[j+(p*24|0)+20>>2]>>0]|0;if(h<<24>>24==40){Ya(j+(p*24|0)|0,797)|0;Ta((c[f>>2]|0)+(p*24|0)+12|0,0,799)|0}}else{Ya(j+(p*24|0)|0,849)|0;Ta((c[f>>2]|0)+(p*24|0)+12|0,0,799)|0}Ya((c[f>>2]|0)+(p*24|0)|0,852)|0;m=c[w>>2]|0;n=m+-16|0;o=c[f>>2]|0;q=o+(p*24|0)|0;h=m+-12|0;j=c[h>>2]|0;xa=c[m+-8>>2]|0;k=xa;if((j|0)==(xa|0)){h=c[n>>2]|0;xa=j-h|0;l=(xa|0)/24|0;j=l+1|0;if((xa|0)<-24)break;h=(k-h|0)/24|0;if(h>>>0<1073741823){h=h<<1;h=h>>>0<j>>>0?j:h}else h=2147483647;ab(ya,h,l,m+-4|0);xa=c[u>>2]|0;_a(xa,q);_a(xa+12|0,o+(p*24|0)+12|0);c[u>>2]=xa+24;cb(n,ya);bb(ya)}else{_a(j,q);_a(j+12|0,o+(p*24|0)+12|0);c[h>>2]=(c[h>>2]|0)+24}p=p+1|0}Pa();break}case 84:{v=f+4|0;s=((c[v>>2]|0)-(c[f>>2]|0)|0)/24|0;g=Eb(d,e,f)|0;t=((c[v>>2]|0)-(c[f>>2]|0)|0)/24|0;if((g|0)==(d|0)){g=d;break a}y=f+16|0;u=f+12|0;n=c[u>>2]|0;x=f+20|0;h=c[x>>2]|0;w=f+24|0;d=c[w>>2]|0;j=d;if(h>>>0<d>>>0){c[h>>2]=0;c[h+4>>2]=0;c[h+8>>2]=0;c[h+12>>2]=n;c[x>>2]=(c[x>>2]|0)+16}else{k=c[y>>2]|0;d=h-k|0;m=d>>4;l=m+1|0;if((d|0)<-16)Pa();h=j-k|0;if(h>>4>>>0<1073741823){h=h>>3;h=h>>>0<l>>>0?l:h}else h=2147483647;Qa(ya,h,m,f+28|0);d=ya+8|0;wa=c[d>>2]|0;c[wa>>2]=0;c[wa+4>>2]=0;c[wa+8>>2]=0;c[wa+12>>2]=n;c[d>>2]=wa+16;Ra(y,ya);Sa(ya)}m=ya+8|0;r=s;while(1){if(r>>>0>=t>>>0)break;n=c[x>>2]|0;o=n+-16|0;p=c[f>>2]|0;q=p+(r*24|0)|0;h=n+-12|0;j=c[h>>2]|0;d=c[n+-8>>2]|0;k=d;if((j|0)==(d|0)){h=c[o>>2]|0;d=j-h|0;l=(d|0)/24|0;j=l+1|0;if((d|0)<-24){ta=455;break}h=(k-h|0)/24|0;if(h>>>0<1073741823){h=h<<1;h=h>>>0<j>>>0?j:h}else h=2147483647;ab(ya,h,l,n+-4|0);d=c[m>>2]|0;_a(d,q);_a(d+12|0,p+(r*24|0)+12|0);c[m>>2]=d+24;cb(o,ya);bb(ya)}else{_a(j,q);_a(j+12|0,p+(r*24|0)+12|0);c[h>>2]=(c[h>>2]|0)+24}r=r+1|0}if((ta|0)==455)Pa();if(!((t|0)==(s+1|0)&(a[f+63>>0]|0)!=0))break a;m=Mb(g,e,f)|0;if((m|0)==(g|0))break a;Cb(xa,(c[v>>2]|0)+-24|0);g=c[v>>2]|0;h=g+-24|0;j=g;while(1){if((j|0)==(h|0))break;d=j+-24|0;c[v>>2]=d;Ia(d);j=c[v>>2]|0}d=a[xa>>0]|0;k=(d&1)==0;Za(g+-48|0,k?xa+1|0:c[xa+8>>2]|0,k?(d&255)>>>1:c[xa+4>>2]|0)|0;g=(c[v>>2]|0)+-24|0;c[V>>2]=c[u>>2];Pb(X,g,V);g=c[x>>2]|0;d=c[w>>2]|0;k=d;if(g>>>0<d>>>0){c[g+12>>2]=c[X+12>>2];c[g>>2]=c[X>>2];ya=X+4|0;c[g+4>>2]=c[ya>>2];f=X+8|0;c[g+8>>2]=c[f>>2];c[f>>2]=0;c[ya>>2]=0;c[X>>2]=0;c[x>>2]=(c[x>>2]|0)+16}else{h=c[y>>2]|0;d=g-h|0;l=d>>4;j=l+1|0;if((d|0)<-16)Pa();g=k-h|0;if(g>>4>>>0<1073741823){g=g>>3;g=g>>>0<j>>>0?j:g}else g=2147483647;Qa(ya,g,l,f+28|0);f=ya+8|0;d=c[f>>2]|0;c[d+12>>2]=c[X+12>>2];c[d>>2]=c[X>>2];e=X+4|0;c[d+4>>2]=c[e>>2];wa=X+8|0;c[d+8>>2]=c[wa>>2];c[wa>>2]=0;c[e>>2]=0;c[X>>2]=0;c[f>>2]=d+16;Ra(y,ya);Sa(ya)}Ha(X);Ja(xa);g=m;break a}case 85:{g=d+1|0;if((g|0)==(e|0)){g=d;break a}h=qb(g,e,f)|0;if((h|0)==(g|0)){g=d;break a}g=Na(h,e,f)|0;if((g|0)==(h|0)){g=d;break a}n=f+4|0;h=c[n>>2]|0;if(((h-(c[f>>2]|0)|0)/24|0)>>>0<2){g=d;break a}Cb(xa,h+-24|0);h=c[n>>2]|0;j=h+-24|0;k=h;while(1){if((k|0)==(j|0))break;d=k+-24|0;c[n>>2]=d;Ia(d);k=c[n>>2]|0}d=h+-48|0;wa=a[d>>0]|0;e=(wa&1)==0;wa=e?(wa&255)>>>1:c[h+-44>>2]|0;$a(t,e?d+1|0:c[h+-40>>2]|0,wa>>>0<9?wa:9);wa=a[t>>0]|0;d=(wa&1)==0;wa=d?(wa&255)>>>1:c[t+4>>2]|0;e=wa>>>0>9;d=ac(d?t+1|0:c[t+8>>2]|0,2070,e?9:wa)|0;Ja(t);if(!(((d|0)==0?(wa>>>0<9?-1:e&1):d)|0)){Cb(la,(c[n>>2]|0)+-24|0);j=c[n>>2]|0;h=j+-24|0;while(1){if((j|0)==(h|0))break;d=j+-24|0;c[n>>2]=d;Ia(d);j=c[n>>2]|0}d=a[la>>0]|0;e=(d&1)==0;h=la+8|0;j=la+1|0;wa=e?j:c[h>>2]|0;k=la+4|0;d=qb(wa+9|0,wa+(e?(d&255)>>>1:c[k>>2]|0)|0,f)|0;if((d|0)==(((a[la>>0]&1)==0?j:c[h>>2]|0)+9|0)){Ib(N,xa,1882);d=a[la>>0]|0;e=(d&1)==0;h=Za(N,e?j:c[h>>2]|0,e?(d&255)>>>1:c[k>>2]|0)|0;c[M>>2]=c[h>>2];c[M+4>>2]=c[h+4>>2];c[M+8>>2]=c[h+8>>2];j=0;while(1){if((j|0)==3)break;c[h+(j<<2)>>2]=0;j=j+1|0}rb(L,M);h=c[n>>2]|0;d=c[f+8>>2]|0;j=d;if(h>>>0<d>>>0){db(h,L);c[n>>2]=(c[n>>2]|0)+24}else{k=c[f>>2]|0;d=h-k|0;m=(d|0)/24|0;l=m+1|0;if((d|0)<-24)Pa();h=(j-k|0)/24|0;if(h>>>0<1073741823){h=h<<1;h=h>>>0<l>>>0?l:h}else h=2147483647;ab(ya,h,m,f+12|0);d=ya+8|0;e=c[d>>2]|0;db(e,L);c[d>>2]=e+24;cb(f,ya);bb(ya)}Ia(L);Ja(M);Ja(N)}else{k=(c[n>>2]|0)+-24|0;Ib(H,xa,1427);Cb(I,(c[n>>2]|0)+-24|0);h=a[I>>0]|0;j=(h&1)==0;h=Za(H,j?I+1|0:c[I+8>>2]|0,j?(h&255)>>>1:c[I+4>>2]|0)|0;c[G>>2]=c[h>>2];c[G+4>>2]=c[h+4>>2];c[G+8>>2]=c[h+8>>2];j=0;while(1){if((j|0)==3)break;c[h+(j<<2)>>2]=0;j=j+1|0}h=Ya(G,844)|0;c[F>>2]=c[h>>2];c[F+4>>2]=c[h+4>>2];c[F+8>>2]=c[h+8>>2];j=0;while(1){if((j|0)==3)break;c[h+(j<<2)>>2]=0;j=j+1|0}rb(E,F);Db(k,E);Ia(E);Ja(F);Ja(G);Ja(I);Ja(H)}Ja(la)}else{h=(c[n>>2]|0)+-24|0;Ib(x,xa,1882);Cb(y,(c[n>>2]|0)+-24|0);j=a[y>>0]|0;k=(j&1)==0;j=Za(x,k?y+1|0:c[y+8>>2]|0,k?(j&255)>>>1:c[y+4>>2]|0)|0;c[w>>2]=c[j>>2];c[w+4>>2]=c[j+4>>2];c[w+8>>2]=c[j+8>>2];k=0;while(1){if((k|0)==3)break;c[j+(k<<2)>>2]=0;k=k+1|0}rb(v,w);Db(h,v);Ia(v);Ja(w);Ja(y);Ja(x)}m=(c[n>>2]|0)+-24|0;c[R>>2]=c[f+12>>2];Pb(W,m,R);m=f+16|0;h=f+20|0;j=c[h>>2]|0;d=c[f+24>>2]|0;k=d;if(j>>>0<d>>>0){c[j+12>>2]=c[W+12>>2];c[j>>2]=c[W>>2];ya=W+4|0;c[j+4>>2]=c[ya>>2];f=W+8|0;c[j+8>>2]=c[f>>2];c[f>>2]=0;c[ya>>2]=0;c[W>>2]=0;c[h>>2]=(c[h>>2]|0)+16}else{h=c[m>>2]|0;d=j-h|0;l=d>>4;j=l+1|0;if((d|0)<-16)Pa();h=k-h|0;if(h>>4>>>0<1073741823){h=h>>3;h=h>>>0<j>>>0?j:h}else h=2147483647;Qa(ya,h,l,f+28|0);f=ya+8|0;d=c[f>>2]|0;c[d+12>>2]=c[W+12>>2];c[d>>2]=c[W>>2];e=W+4|0;c[d+4>>2]=c[e>>2];wa=W+8|0;c[d+8>>2]=c[wa>>2];c[wa>>2]=0;c[e>>2]=0;c[W>>2]=0;c[f>>2]=d+16;Ra(m,ya);Sa(ya)}Ha(W);Ja(xa);break a}case 83:{wa=d+1|0;if((wa|0)!=(e|0)?(a[wa>>0]|0)==116:0){g=Wb(d,e,f)|0;if((g|0)==(d|0)){g=d;break a}h=c[f+4>>2]|0;if((c[f>>2]|0)==(h|0)){g=d;break a}m=f+16|0;c[n>>2]=c[f+12>>2];Pb(z,h+-24|0,n);h=f+20|0;j=c[h>>2]|0;d=c[f+24>>2]|0;k=d;if(j>>>0<d>>>0){c[j+12>>2]=c[z+12>>2];c[j>>2]=c[z>>2];ya=z+4|0;c[j+4>>2]=c[ya>>2];f=z+8|0;c[j+8>>2]=c[f>>2];c[f>>2]=0;c[ya>>2]=0;c[z>>2]=0;c[h>>2]=(c[h>>2]|0)+16}else{h=c[m>>2]|0;d=j-h|0;l=d>>4;j=l+1|0;if((d|0)<-16)Pa();h=k-h|0;if(h>>4>>>0<1073741823){h=h>>3;h=h>>>0<j>>>0?j:h}else h=2147483647;Qa(ya,h,l,f+28|0);f=ya+8|0;d=c[f>>2]|0;c[d+12>>2]=c[z+12>>2];c[d>>2]=c[z>>2];e=z+4|0;c[d+4>>2]=c[e>>2];xa=z+8|0;c[d+8>>2]=c[xa>>2];c[xa>>2]=0;c[e>>2]=0;c[z>>2]=0;c[f>>2]=d+16;Ra(m,ya);Sa(ya)}Ha(z);break a}g=Rb(d,e,f)|0;if((g|0)==(d|0)){g=d;break a}m=Mb(g,e,f)|0;if((m|0)==(g|0))break a;k=f+4|0;h=c[k>>2]|0;if(((h-(c[f>>2]|0)|0)/24|0)>>>0<2)break a;Cb(xa,h+-24|0);g=c[k>>2]|0;h=g+-24|0;j=g;while(1){if((j|0)==(h|0))break;d=j+-24|0;c[k>>2]=d;Ia(d);j=c[k>>2]|0}l=a[xa>>0]|0;h=(l&1)==0;Za(g+-48|0,h?xa+1|0:c[xa+8>>2]|0,h?(l&255)>>>1:c[xa+4>>2]|0)|0;l=(c[k>>2]|0)+-24|0;c[s>>2]=c[f+12>>2];Pb(D,l,s);l=f+16|0;g=f+20|0;h=c[g>>2]|0;d=c[f+24>>2]|0;j=d;if(h>>>0<d>>>0){c[h+12>>2]=c[D+12>>2];c[h>>2]=c[D>>2];ya=D+4|0;c[h+4>>2]=c[ya>>2];f=D+8|0;c[h+8>>2]=c[f>>2];c[f>>2]=0;c[ya>>2]=0;c[D>>2]=0;c[g>>2]=(c[g>>2]|0)+16}else{g=c[l>>2]|0;d=h-g|0;k=d>>4;h=k+1|0;if((d|0)<-16)Pa();g=j-g|0;if(g>>4>>>0<1073741823){g=g>>3;g=g>>>0<h>>>0?h:g}else g=2147483647;Qa(ya,g,k,f+28|0);f=ya+8|0;d=c[f>>2]|0;c[d+12>>2]=c[D+12>>2];c[d>>2]=c[D>>2];e=D+4|0;c[d+4>>2]=c[e>>2];wa=D+8|0;c[d+8>>2]=c[wa>>2];c[wa>>2]=0;c[e>>2]=0;c[D>>2]=0;c[f>>2]=d+16;Ra(l,ya);Sa(ya)}Ha(D);Ja(xa);g=m;break a}case 68:{g=d+1|0;if((g|0)!=(e|0)){g=a[g>>0]|0;switch(g<<24>>24|0){case 112:{s=f+4|0;p=((c[s>>2]|0)-(c[f>>2]|0)|0)/24|0;xa=d+2|0;g=Na(xa,e,f)|0;s=((c[s>>2]|0)-(c[f>>2]|0)|0)/24|0;if((g|0)==(xa|0))break d;n=f+16|0;o=c[f+12>>2]|0;t=f+20|0;h=c[t>>2]|0;xa=c[f+24>>2]|0;j=xa;if(h>>>0<xa>>>0){c[h>>2]=0;c[h+4>>2]=0;c[h+8>>2]=0;c[h+12>>2]=o;c[t>>2]=(c[t>>2]|0)+16}else{k=c[n>>2]|0;xa=h-k|0;m=xa>>4;l=m+1|0;if((xa|0)<-16)Pa();h=j-k|0;if(h>>4>>>0<1073741823){h=h>>3;h=h>>>0<l>>>0?l:h}else h=2147483647;Qa(ya,h,m,f+28|0);xa=ya+8|0;ta=c[xa>>2]|0;c[ta>>2]=0;c[ta+4>>2]=0;c[ta+8>>2]=0;c[ta+12>>2]=o;c[xa>>2]=ta+16;Ra(n,ya);Sa(ya)}r=ya+8|0;while(1){if(p>>>0>=s>>>0)break a;m=c[t>>2]|0;n=m+-16|0;o=c[f>>2]|0;q=o+(p*24|0)|0;h=m+-12|0;j=c[h>>2]|0;xa=c[m+-8>>2]|0;k=xa;if((j|0)==(xa|0)){h=c[n>>2]|0;xa=j-h|0;l=(xa|0)/24|0;j=l+1|0;if((xa|0)<-24)break;h=(k-h|0)/24|0;if(h>>>0<1073741823){h=h<<1;h=h>>>0<j>>>0?j:h}else h=2147483647;ab(ya,h,l,m+-4|0);xa=c[r>>2]|0;_a(xa,q);_a(xa+12|0,o+(p*24|0)+12|0);c[r>>2]=xa+24;cb(n,ya);bb(ya)}else{_a(j,q);_a(j+12|0,o+(p*24|0)+12|0);c[h>>2]=(c[h>>2]|0)+24}p=p+1|0}Pa();break}case 84:case 116:{g=Qb(d,e,f)|0;if((g|0)==(d|0))break d;h=c[f+4>>2]|0;if((c[f>>2]|0)==(h|0)){g=d;break a}m=f+16|0;c[r>>2]=c[f+12>>2];Pb(C,h+-24|0,r);h=f+20|0;j=c[h>>2]|0;d=c[f+24>>2]|0;k=d;if(j>>>0<d>>>0){c[j+12>>2]=c[C+12>>2];c[j>>2]=c[C>>2];ya=C+4|0;c[j+4>>2]=c[ya>>2];f=C+8|0;c[j+8>>2]=c[f>>2];c[f>>2]=0;c[ya>>2]=0;c[C>>2]=0;c[h>>2]=(c[h>>2]|0)+16}else{h=c[m>>2]|0;d=j-h|0;l=d>>4;j=l+1|0;if((d|0)<-16)Pa();h=k-h|0;if(h>>4>>>0<1073741823){h=h>>3;h=h>>>0<j>>>0?j:h}else h=2147483647;Qa(ya,h,l,f+28|0);f=ya+8|0;d=c[f>>2]|0;c[d+12>>2]=c[C+12>>2];c[d>>2]=c[C>>2];e=C+4|0;c[d+4>>2]=c[e>>2];xa=C+8|0;c[d+8>>2]=c[xa>>2];c[xa>>2]=0;c[e>>2]=0;c[C>>2]=0;c[f>>2]=d+16;Ra(m,ya);Sa(ya)}Ha(C);break a}case 118:{i:do if((e-d|0)>3&h<<24>>24==68&g<<24>>24==118){l=d+2|0;h=a[l>>0]|0;if((h+-49&255)<9){g=tb(l,e)|0;if((g|0)==(e|0)){g=d;break}if((a[g>>0]|0)!=95){g=d;break}j=g-l|0;h=g+1|0;if((h|0)==(e|0)){g=d;break}if((a[h>>0]|0)!=112){g=Na(h,e,f)|0;if((g|0)==(h|0)){g=d;break}h=c[f+4>>2]|0;if((c[f>>2]|0)==(h|0)){g=d;break}k=h+-24|0;$a(ca,l,j);h=Ta(ca,0,2101)|0;c[la>>2]=c[h>>2];c[la+4>>2]=c[h+4>>2];c[la+8>>2]=c[h+8>>2];j=0;while(1){if((j|0)==3)break;c[h+(j<<2)>>2]=0;j=j+1|0}h=Ya(la,4264)|0;c[xa>>2]=c[h>>2];c[xa+4>>2]=c[h+4>>2];c[xa+8>>2]=c[h+8>>2];j=0;while(1){if((j|0)==3)break;c[h+(j<<2)>>2]=0;j=j+1|0}ta=a[xa>>0]|0;sa=(ta&1)==0;Za(k,sa?xa+1|0:c[xa+8>>2]|0,sa?(ta&255)>>>1:c[xa+4>>2]|0)|0;Ja(xa);Ja(la);Ja(ca);break}g=g+2|0;$a(ia,l,j);h=Ta(ia,0,2110)|0;c[sa>>2]=c[h>>2];c[sa+4>>2]=c[h+4>>2];c[sa+8>>2]=c[h+8>>2];j=0;while(1){if((j|0)==3)break;c[h+(j<<2)>>2]=0;j=j+1|0}h=Ya(sa,4264)|0;c[pa>>2]=c[h>>2];c[pa+4>>2]=c[h+4>>2];c[pa+8>>2]=c[h+8>>2];j=0;while(1){if((j|0)==3)break;c[h+(j<<2)>>2]=0;j=j+1|0}rb(fa,pa);h=f+4|0;j=c[h>>2]|0;xa=c[f+8>>2]|0;k=xa;if(j>>>0<xa>>>0){db(j,fa);c[h>>2]=(c[h>>2]|0)+24}else{h=c[f>>2]|0;xa=j-h|0;l=(xa|0)/24|0;j=l+1|0;if((xa|0)<-24)Pa();h=(k-h|0)/24|0;if(h>>>0<1073741823){h=h<<1;h=h>>>0<j>>>0?j:h}else h=2147483647;ab(ya,h,l,f+12|0);xa=ya+8|0;ta=c[xa>>2]|0;db(ta,fa);c[xa>>2]=ta+24;cb(f,ya);bb(ya)}Ia(fa);Ja(pa);Ja(sa);Ja(ia);break}g=0;while(1){if((g|0)==3)break;c[ya+(g<<2)>>2]=0;g=g+1|0}j:do if(h<<24>>24!=95?(J=ub(l,e,f)|0,(J|0)!=(l|0)):0){o=f+4|0;g=c[o>>2]|0;if((c[f>>2]|0)!=(g|0)){Cb(oa,g+-24|0);k:do if(!(a[ya>>0]&1)){a[ya+1>>0]=0;a[ya>>0]=0}else{l=ya+8|0;h=c[l>>2]|0;a[h>>0]=0;m=ya+4|0;c[m>>2]=0;g=c[ya>>2]|0;n=(g&-2)+-1|0;j=g&255;do if(!(j&1)){g=g>>>1&127;if((j&255)<22){Fc(ya+1|0,h|0,g+1|0)|0;wc(h);break}h=g+16&240;k=h+-1|0;if((k|0)==(n|0))break k;j=vc(h)|0;if(k>>>0<=n>>>0&(j|0)==0)break k;Fc(j|0,ya+1|0,g+1|0)|0;c[ya>>2]=h|1;c[m>>2]=g;c[l>>2]=j;break k}else{a[ya+1>>0]=0;wc(h);g=0}while(0);a[ya>>0]=g<<1}while(0);c[ya>>2]=c[oa>>2];c[ya+4>>2]=c[oa+4>>2];c[ya+8>>2]=c[oa+8>>2];g=0;while(1){if((g|0)==3)break;c[oa+(g<<2)>>2]=0;g=g+1|0}Ja(oa);h=c[o>>2]|0;g=h+-24|0;while(1){if((h|0)==(g|0)){g=J;ta=622;break j}xa=h+-24|0;c[o>>2]=xa;Ia(xa);h=c[o>>2]|0}}}else{g=l;ta=622}while(0);do if((ta|0)==622){if((((g|0)!=(e|0)?(a[g>>0]|0)==95:0)?(K=g+1|0,(K|0)!=(e|0)):0)?(Q=Na(K,e,f)|0,(Q|0)!=(K|0)):0){g=c[f+4>>2]|0;if((c[f>>2]|0)==(g|0))break;g=g+-24|0;xb(ra,2101,ya);h=Ya(ra,4264)|0;c[qa>>2]=c[h>>2];c[qa+4>>2]=c[h+4>>2];c[qa+8>>2]=c[h+8>>2];j=0;while(1){if((j|0)==3)break;c[h+(j<<2)>>2]=0;j=j+1|0}xa=a[qa>>0]|0;ta=(xa&1)==0;Za(g,ta?qa+1|0:c[qa+8>>2]|0,ta?(xa&255)>>>1:c[qa+4>>2]|0)|0;Ja(qa);Ja(ra);g=Q}else g=d;Ja(ya);break i}while(0);Ja(ya);g=d}else g=d;while(0);if((g|0)==(d|0))break d;h=c[f+4>>2]|0;if((c[f>>2]|0)==(h|0)){g=d;break a}m=f+16|0;c[ba>>2]=c[f+12>>2];Pb(ga,h+-24|0,ba);h=f+20|0;j=c[h>>2]|0;d=c[f+24>>2]|0;k=d;if(j>>>0<d>>>0){c[j+12>>2]=c[ga+12>>2];c[j>>2]=c[ga>>2];ya=ga+4|0;c[j+4>>2]=c[ya>>2];f=ga+8|0;c[j+8>>2]=c[f>>2];c[f>>2]=0;c[ya>>2]=0;c[ga>>2]=0;c[h>>2]=(c[h>>2]|0)+16}else{h=c[m>>2]|0;d=j-h|0;l=d>>4;j=l+1|0;if((d|0)<-16)Pa();h=k-h|0;if(h>>4>>>0<1073741823){h=h>>3;h=h>>>0<j>>>0?j:h}else h=2147483647;Qa(ya,h,l,f+28|0);f=ya+8|0;d=c[f>>2]|0;c[d+12>>2]=c[ga+12>>2];c[d>>2]=c[ga>>2];e=ga+4|0;c[d+4>>2]=c[e>>2];xa=ga+8|0;c[d+8>>2]=c[xa>>2];c[xa>>2]=0;c[e>>2]=0;c[ga>>2]=0;c[f>>2]=d+16;Ra(m,ya);Sa(ya)}Ha(ga);break a}default:break d}}break}default:{}}while(0);g=eb(d,e,f)|0;if((g|0)==(d|0)){g=Wb(d,e,f)|0;if((g|0)!=(d|0)?(ua=c[f+4>>2]|0,(c[f>>2]|0)!=(ua|0)):0){m=f+16|0;c[va>>2]=c[f+12>>2];Pb(wa,ua+-24|0,va);h=f+20|0;j=c[h>>2]|0;d=c[f+24>>2]|0;k=d;if(j>>>0<d>>>0){c[j+12>>2]=c[wa+12>>2];c[j>>2]=c[wa>>2];ya=wa+4|0;c[j+4>>2]=c[ya>>2];f=wa+8|0;c[j+8>>2]=c[f>>2];c[f>>2]=0;c[ya>>2]=0;c[wa>>2]=0;c[h>>2]=(c[h>>2]|0)+16}else{h=c[m>>2]|0;d=j-h|0;l=d>>4;j=l+1|0;if((d|0)<-16)Pa();h=k-h|0;if(h>>4>>>0<1073741823){h=h>>3;h=h>>>0<j>>>0?j:h}else h=2147483647;Qa(ya,h,l,f+28|0);f=ya+8|0;d=c[f>>2]|0;c[d+12>>2]=c[wa+12>>2];c[d>>2]=c[wa>>2];e=wa+4|0;c[d+4>>2]=c[e>>2];xa=wa+8|0;c[d+8>>2]=c[xa>>2];c[xa>>2]=0;c[e>>2]=0;c[wa>>2]=0;c[f>>2]=d+16;Ra(m,ya);Sa(ya)}Ha(wa)}else g=d}}}else g=d;while(0);i=za;return g|0}function Oa(b,d,e){b=b|0;d=d|0;e=e|0;var f=0;c[e>>2]=0;if((b|0)!=(d|0)){d=a[b>>0]|0;if(d<<24>>24==114){c[e>>2]=4;d=b+1|0;b=d;d=a[d>>0]|0;f=4}else f=0;if(d<<24>>24==86){f=f|2;c[e>>2]=f;d=b+1|0;b=d;d=a[d>>0]|0}if(d<<24>>24==75){c[e>>2]=f|1;b=b+1|0}}return b|0}function Pa(){oa(120,143,303,246)}function Qa(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;c[a+12>>2]=0;c[a+16>>2]=e;if(!b)e=0;else e=Da(c[e>>2]|0,b<<4)|0;c[a>>2]=e;d=e+(d<<4)|0;c[a+8>>2]=d;c[a+4>>2]=d;c[a+12>>2]=e+(b<<4);return}function Ra(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0;e=c[a>>2]|0;f=a+4|0;g=b+4|0;d=c[f>>2]|0;while(1){if((d|0)==(e|0))break;k=c[g>>2]|0;i=k+-16|0;h=d+-16|0;c[i>>2]=0;j=k+-12|0;c[j>>2]=0;l=c[d+-4>>2]|0;c[k+-8>>2]=0;c[k+-4>>2]=l;c[i>>2]=c[h>>2];i=d+-12|0;c[j>>2]=c[i>>2];j=d+-8|0;c[k+-8>>2]=c[j>>2];c[j>>2]=0;c[i>>2]=0;c[h>>2]=0;c[g>>2]=(c[g>>2]|0)+-16;d=h}j=c[a>>2]|0;c[a>>2]=c[g>>2];c[g>>2]=j;j=b+8|0;l=c[f>>2]|0;c[f>>2]=c[j>>2];c[j>>2]=l;j=a+8|0;l=b+12|0;k=c[j>>2]|0;c[j>>2]=c[l>>2];c[l>>2]=k;c[b>>2]=c[g>>2];return}function Sa(a){a=a|0;var b=0,d=0,e=0;b=c[a+4>>2]|0;d=a+8|0;while(1){e=c[d>>2]|0;if((e|0)==(b|0))break;e=e+-16|0;c[d>>2]=e;Ha(e)}b=c[a>>2]|0;if(b)Ka(c[c[a+16>>2]>>2]|0,b,(c[a+12>>2]|0)-b|0);return}function Ta(a,b,c){a=a|0;b=b|0;c=c|0;return Ua(a,b,c,bc(c)|0)|0}function Ua(b,d,e,f){b=b|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,i=0;g=a[b>>0]|0;h=(g&1)==0;if(h)i=(g&255)>>>1;else i=c[b+4>>2]|0;if(i>>>0<d>>>0)Va();if(h)h=10;else{g=c[b>>2]|0;h=(g&-2)+-1|0;g=g&255}if((h-i|0)>>>0>=f>>>0){if(f){if(!(g&1))h=b+1|0;else h=c[b+8>>2]|0;if((i|0)==(d|0))g=h+d|0;else{g=h+d|0;Hc(g+f|0,g|0,i-d|0)|0;e=g>>>0<=e>>>0&(h+i|0)>>>0>e>>>0?e+f|0:e}Hc(g|0,e|0,f|0)|0;g=i+f|0;if(!(a[b>>0]&1))a[b>>0]=g<<1;else c[b+4>>2]=g;a[h+g>>0]=0}}else Wa(b,h,i+f-h|0,i,d,0,f,e);return b|0}function Va(){oa(274,303,1175,406)}function Wa(b,d,e,f,g,h,i,j){b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;i=i|0;j=j|0;var k=0,l=0,m=0;if((-18-d|0)>>>0<e>>>0)Xa();if(!(a[b>>0]&1))m=b+1|0;else m=c[b+8>>2]|0;if(d>>>0<2147483623){k=e+d|0;l=d<<1;k=k>>>0<l>>>0?l:k;k=k>>>0<11?11:k+16&-16}else k=-17;l=vc(k)|0;if(g)Fc(l|0,m|0,g|0)|0;if(i)Fc(l+g|0,j|0,i|0)|0;e=f-h|0;if((e|0)!=(g|0))Fc(l+g+i|0,m+g+h|0,e-g|0)|0;if((d|0)!=10)wc(m);c[b+8>>2]=l;c[b>>2]=k|1;d=e+i|0;c[b+4>>2]=d;a[l+d>>0]=0;return}function Xa(){oa(427,303,1164,246)}function Ya(a,b){a=a|0;b=b|0;return Za(a,b,bc(b)|0)|0}function Za(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0;f=a[b>>0]|0;if(!(f&1))h=10;else{f=c[b>>2]|0;h=(f&-2)+-1|0;f=f&255}g=(f&1)==0;if(g)f=(f&255)>>>1;else f=c[b+4>>2]|0;if((h-f|0)>>>0>=e>>>0){if(e){if(g)g=b+1|0;else g=c[b+8>>2]|0;Fc(g+f|0,d|0,e|0)|0;f=f+e|0;if(!(a[b>>0]&1))a[b>>0]=f<<1;else c[b+4>>2]=f;a[g+f>>0]=0}}else Wa(b,h,e-h+f|0,f,f,0,e,d);return b|0}function _a(b,d){b=b|0;d=d|0;if(!(a[d>>0]&1)){c[b>>2]=c[d>>2];c[b+4>>2]=c[d+4>>2];c[b+8>>2]=c[d+8>>2]}else $a(b,c[d+8>>2]|0,c[d+4>>2]|0);return}function $a(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0;if(e>>>0>4294967279)Xa();if(e>>>0<11){a[b>>0]=e<<1;b=b+1|0}else{g=e+16&-16;f=vc(g)|0;c[b+8>>2]=f;c[b>>2]=g|1;c[b+4>>2]=e;b=f}Fc(b|0,d|0,e|0)|0;a[b+e>>0]=0;return}function ab(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;c[a+12>>2]=0;c[a+16>>2]=e;if(!b)e=0;else e=Da(c[e>>2]|0,b*24|0)|0;c[a>>2]=e;d=e+(d*24|0)|0;c[a+8>>2]=d;c[a+4>>2]=d;c[a+12>>2]=e+(b*24|0);return}function bb(a){a=a|0;var b=0,d=0,e=0;b=c[a+4>>2]|0;d=a+8|0;while(1){e=c[d>>2]|0;if((e|0)==(b|0))break;e=e+-24|0;c[d>>2]=e;Ia(e)}b=c[a>>2]|0;if(b)Ka(c[c[a+16>>2]>>2]|0,b,(c[a+12>>2]|0)-b|0);return}function cb(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0;e=c[a>>2]|0;f=a+4|0;g=b+4|0;d=c[f>>2]|0;while(1){if((d|0)==(e|0))break;h=d+-24|0;db((c[g>>2]|0)+-24|0,h);c[g>>2]=(c[g>>2]|0)+-24;d=h}h=c[a>>2]|0;c[a>>2]=c[g>>2];c[g>>2]=h;h=b+8|0;e=c[f>>2]|0;c[f>>2]=c[h>>2];c[h>>2]=e;f=a+8|0;h=b+12|0;a=c[f>>2]|0;c[f>>2]=c[h>>2];c[h>>2]=a;c[b>>2]=c[g>>2];return}function db(a,b){a=a|0;b=b|0;var d=0;c[a>>2]=c[b>>2];c[a+4>>2]=c[b+4>>2];c[a+8>>2]=c[b+8>>2];d=0;while(1){if((d|0)==3)break;c[b+(d<<2)>>2]=0;d=d+1|0}d=a+12|0;b=b+12|0;c[d>>2]=c[b>>2];c[d+4>>2]=c[b+4>>2];c[d+8>>2]=c[b+8>>2];d=0;while(1){if((d|0)==3)break;c[b+(d<<2)>>2]=0;d=d+1|0}return}function eb(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0;N=i;i=i+720|0;M=N+696|0;j=N+672|0;J=N+648|0;s=N+624|0;u=N+600|0;v=N+576|0;w=N+552|0;x=N+528|0;y=N+504|0;z=N+480|0;A=N+456|0;k=N+432|0;l=N+408|0;m=N+384|0;L=N+360|0;n=N+336|0;o=N+312|0;p=N+288|0;K=N+264|0;q=N+240|0;r=N+216|0;t=N+192|0;B=N+168|0;C=N+144|0;D=N+120|0;E=N+96|0;F=N+72|0;G=N+48|0;H=N+24|0;I=N;a:do if((b|0)!=(d|0))do switch(a[b>>0]|0){case 118:{fb(j,476);f=e+4|0;d=c[f>>2]|0;L=c[e+8>>2]|0;g=L;if(d>>>0<L>>>0){db(d,j);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;L=d-f|0;h=(L|0)/24|0;d=h+1|0;if((L|0)<-24)Pa();f=(g-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<d>>>0?d:f}else f=2147483647;ab(M,f,h,e+12|0);L=M+8|0;K=c[L>>2]|0;db(K,j);c[L>>2]=K+24;cb(e,M);bb(M)}Ia(j);b=b+1|0;break a}case 119:{a[J>>0]=14;f=J+1|0;a[f>>0]=a[481]|0;a[f+1>>0]=a[482]|0;a[f+2>>0]=a[483]|0;a[f+3>>0]=a[484]|0;a[f+4>>0]=a[485]|0;a[f+5>>0]=a[486]|0;a[f+6>>0]=a[487]|0;a[J+8>>0]=0;f=J+12|0;d=0;while(1){if((d|0)==3)break;c[f+(d<<2)>>2]=0;d=d+1|0}f=e+4|0;d=c[f>>2]|0;L=c[e+8>>2]|0;g=L;if(d>>>0<L>>>0){db(d,J);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;L=d-f|0;h=(L|0)/24|0;d=h+1|0;if((L|0)<-24)Pa();f=(g-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<d>>>0?d:f}else f=2147483647;ab(M,f,h,e+12|0);L=M+8|0;K=c[L>>2]|0;db(K,J);c[L>>2]=K+24;cb(e,M);bb(M)}Ia(J);b=b+1|0;break a}case 98:{fb(s,489);f=e+4|0;d=c[f>>2]|0;L=c[e+8>>2]|0;g=L;if(d>>>0<L>>>0){db(d,s);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;L=d-f|0;h=(L|0)/24|0;d=h+1|0;if((L|0)<-24)Pa();f=(g-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<d>>>0?d:f}else f=2147483647;ab(M,f,h,e+12|0);L=M+8|0;K=c[L>>2]|0;db(K,s);c[L>>2]=K+24;cb(e,M);bb(M)}Ia(s);b=b+1|0;break a}case 99:{fb(u,494);f=e+4|0;d=c[f>>2]|0;L=c[e+8>>2]|0;g=L;if(d>>>0<L>>>0){db(d,u);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;L=d-f|0;h=(L|0)/24|0;d=h+1|0;if((L|0)<-24)Pa();f=(g-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<d>>>0?d:f}else f=2147483647;ab(M,f,h,e+12|0);L=M+8|0;K=c[L>>2]|0;db(K,u);c[L>>2]=K+24;cb(e,M);bb(M)}Ia(u);b=b+1|0;break a}case 97:{gb(v,499);f=e+4|0;d=c[f>>2]|0;L=c[e+8>>2]|0;g=L;if(d>>>0<L>>>0){db(d,v);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;L=d-f|0;h=(L|0)/24|0;d=h+1|0;if((L|0)<-24)Pa();f=(g-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<d>>>0?d:f}else f=2147483647;ab(M,f,h,e+12|0);L=M+8|0;K=c[L>>2]|0;db(K,v);c[L>>2]=K+24;cb(e,M);bb(M)}Ia(v);b=b+1|0;break a}case 104:{hb(w,511);f=e+4|0;d=c[f>>2]|0;L=c[e+8>>2]|0;g=L;if(d>>>0<L>>>0){db(d,w);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;L=d-f|0;h=(L|0)/24|0;d=h+1|0;if((L|0)<-24)Pa();f=(g-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<d>>>0?d:f}else f=2147483647;ab(M,f,h,e+12|0);L=M+8|0;K=c[L>>2]|0;db(K,w);c[L>>2]=K+24;cb(e,M);bb(M)}Ia(w);b=b+1|0;break a}case 115:{ib(x,525);f=e+4|0;d=c[f>>2]|0;L=c[e+8>>2]|0;g=L;if(d>>>0<L>>>0){db(d,x);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;L=d-f|0;h=(L|0)/24|0;d=h+1|0;if((L|0)<-24)Pa();f=(g-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<d>>>0?d:f}else f=2147483647;ab(M,f,h,e+12|0);L=M+8|0;K=c[L>>2]|0;db(K,x);c[L>>2]=K+24;cb(e,M);bb(M)}Ia(x);b=b+1|0;break a}case 116:{jb(y,531);f=e+4|0;d=c[f>>2]|0;L=c[e+8>>2]|0;g=L;if(d>>>0<L>>>0){db(d,y);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;L=d-f|0;h=(L|0)/24|0;d=h+1|0;if((L|0)<-24)Pa();f=(g-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<d>>>0?d:f}else f=2147483647;ab(M,f,h,e+12|0);L=M+8|0;K=c[L>>2]|0;db(K,y);c[L>>2]=K+24;cb(e,M);bb(M)}Ia(y);b=b+1|0;break a}case 105:{kb(z,546);f=e+4|0;d=c[f>>2]|0;L=c[e+8>>2]|0;g=L;if(d>>>0<L>>>0){db(d,z);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;L=d-f|0;h=(L|0)/24|0;d=h+1|0;if((L|0)<-24)Pa();f=(g-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<d>>>0?d:f}else f=2147483647;ab(M,f,h,e+12|0);L=M+8|0;K=c[L>>2]|0;db(K,z);c[L>>2]=K+24;cb(e,M);bb(M)}Ia(z);b=b+1|0;break a}case 106:{lb(A,550);f=e+4|0;d=c[f>>2]|0;L=c[e+8>>2]|0;g=L;if(d>>>0<L>>>0){db(d,A);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;L=d-f|0;h=(L|0)/24|0;d=h+1|0;if((L|0)<-24)Pa();f=(g-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<d>>>0?d:f}else f=2147483647;ab(M,f,h,e+12|0);L=M+8|0;K=c[L>>2]|0;db(K,A);c[L>>2]=K+24;cb(e,M);bb(M)}Ia(A);b=b+1|0;break a}case 108:{fb(k,563);f=e+4|0;d=c[f>>2]|0;L=c[e+8>>2]|0;g=L;if(d>>>0<L>>>0){db(d,k);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;L=d-f|0;h=(L|0)/24|0;d=h+1|0;if((L|0)<-24)Pa();f=(g-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<d>>>0?d:f}else f=2147483647;ab(M,f,h,e+12|0);L=M+8|0;K=c[L>>2]|0;db(K,k);c[L>>2]=K+24;cb(e,M);bb(M)}Ia(k);b=b+1|0;break a}case 109:{hb(l,568);f=e+4|0;d=c[f>>2]|0;L=c[e+8>>2]|0;g=L;if(d>>>0<L>>>0){db(d,l);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;L=d-f|0;h=(L|0)/24|0;d=h+1|0;if((L|0)<-24)Pa();f=(g-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<d>>>0?d:f}else f=2147483647;ab(M,f,h,e+12|0);L=M+8|0;K=c[L>>2]|0;db(K,l);c[L>>2]=K+24;cb(e,M);bb(M)}Ia(l);b=b+1|0;break a}case 120:{mb(m,582);f=e+4|0;d=c[f>>2]|0;L=c[e+8>>2]|0;g=L;if(d>>>0<L>>>0){db(d,m);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;L=d-f|0;h=(L|0)/24|0;d=h+1|0;if((L|0)<-24)Pa();f=(g-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<d>>>0?d:f}else f=2147483647;ab(M,f,h,e+12|0);L=M+8|0;K=c[L>>2]|0;db(K,m);c[L>>2]=K+24;cb(e,M);bb(M)}Ia(m);b=b+1|0;break a}case 121:{f=vc(32)|0;c[L+8>>2]=f;c[L>>2]=33;c[L+4>>2]=18;d=f;g=592;h=d+18|0;do{a[d>>0]=a[g>>0]|0;d=d+1|0;g=g+1|0}while((d|0)<(h|0));a[f+18>>0]=0;f=L+12|0;d=0;while(1){if((d|0)==3)break;c[f+(d<<2)>>2]=0;d=d+1|0}f=e+4|0;d=c[f>>2]|0;K=c[e+8>>2]|0;g=K;if(d>>>0<K>>>0){db(d,L);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;K=d-f|0;h=(K|0)/24|0;d=h+1|0;if((K|0)<-24)Pa();f=(g-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<d>>>0?d:f}else f=2147483647;ab(M,f,h,e+12|0);K=M+8|0;J=c[K>>2]|0;db(J,L);c[K>>2]=J+24;cb(e,M);bb(M)}Ia(L);b=b+1|0;break a}case 110:{nb(n,611);f=e+4|0;d=c[f>>2]|0;L=c[e+8>>2]|0;g=L;if(d>>>0<L>>>0){db(d,n);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;L=d-f|0;h=(L|0)/24|0;d=h+1|0;if((L|0)<-24)Pa();f=(g-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<d>>>0?d:f}else f=2147483647;ab(M,f,h,e+12|0);L=M+8|0;K=c[L>>2]|0;db(K,n);c[L>>2]=K+24;cb(e,M);bb(M)}Ia(n);b=b+1|0;break a}case 111:{ob(o,620);f=e+4|0;d=c[f>>2]|0;L=c[e+8>>2]|0;g=L;if(d>>>0<L>>>0){db(d,o);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;L=d-f|0;h=(L|0)/24|0;d=h+1|0;if((L|0)<-24)Pa();f=(g-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<d>>>0?d:f}else f=2147483647;ab(M,f,h,e+12|0);L=M+8|0;K=c[L>>2]|0;db(K,o);c[L>>2]=K+24;cb(e,M);bb(M)}Ia(o);b=b+1|0;break a}case 102:{ib(p,638);f=e+4|0;d=c[f>>2]|0;L=c[e+8>>2]|0;g=L;if(d>>>0<L>>>0){db(d,p);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;L=d-f|0;h=(L|0)/24|0;d=h+1|0;if((L|0)<-24)Pa();f=(g-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<d>>>0?d:f}else f=2147483647;ab(M,f,h,e+12|0);L=M+8|0;K=c[L>>2]|0;db(K,p);c[L>>2]=K+24;cb(e,M);bb(M)}Ia(p);b=b+1|0;break a}case 100:{a[K>>0]=12;f=K+1|0;a[f>>0]=a[644]|0;a[f+1>>0]=a[645]|0;a[f+2>>0]=a[646]|0;a[f+3>>0]=a[647]|0;a[f+4>>0]=a[648]|0;a[f+5>>0]=a[649]|0;a[K+7>>0]=0;f=K+12|0;d=0;while(1){if((d|0)==3)break;c[f+(d<<2)>>2]=0;d=d+1|0}f=e+4|0;d=c[f>>2]|0;L=c[e+8>>2]|0;g=L;if(d>>>0<L>>>0){db(d,K);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;L=d-f|0;h=(L|0)/24|0;d=h+1|0;if((L|0)<-24)Pa();f=(g-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<d>>>0?d:f}else f=2147483647;ab(M,f,h,e+12|0);L=M+8|0;J=c[L>>2]|0;db(J,K);c[L>>2]=J+24;cb(e,M);bb(M)}Ia(K);b=b+1|0;break a}case 101:{gb(q,651);f=e+4|0;d=c[f>>2]|0;L=c[e+8>>2]|0;g=L;if(d>>>0<L>>>0){db(d,q);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;L=d-f|0;h=(L|0)/24|0;d=h+1|0;if((L|0)<-24)Pa();f=(g-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<d>>>0?d:f}else f=2147483647;ab(M,f,h,e+12|0);L=M+8|0;K=c[L>>2]|0;db(K,q);c[L>>2]=K+24;cb(e,M);bb(M)}Ia(q);b=b+1|0;break a}case 103:{pb(r,663);f=e+4|0;d=c[f>>2]|0;L=c[e+8>>2]|0;g=L;if(d>>>0<L>>>0){db(d,r);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;L=d-f|0;h=(L|0)/24|0;d=h+1|0;if((L|0)<-24)Pa();f=(g-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<d>>>0?d:f}else f=2147483647;ab(M,f,h,e+12|0);L=M+8|0;K=c[L>>2]|0;db(K,r);c[L>>2]=K+24;cb(e,M);bb(M)}Ia(r);b=b+1|0;break a}case 122:{kb(t,674);f=e+4|0;d=c[f>>2]|0;L=c[e+8>>2]|0;g=L;if(d>>>0<L>>>0){db(d,t);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;L=d-f|0;h=(L|0)/24|0;d=h+1|0;if((L|0)<-24)Pa();f=(g-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<d>>>0?d:f}else f=2147483647;ab(M,f,h,e+12|0);L=M+8|0;K=c[L>>2]|0;db(K,t);c[L>>2]=K+24;cb(e,M);bb(M)}Ia(t);b=b+1|0;break a}case 117:{M=b+1|0;e=qb(M,d,e)|0;b=(e|0)==(M|0)?b:e;break a}case 68:{f=b+1|0;if((f|0)==(d|0))break a;switch(a[f>>0]|0){case 100:{mb(B,711);f=e+4|0;d=c[f>>2]|0;L=c[e+8>>2]|0;g=L;if(d>>>0<L>>>0){db(d,B);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;L=d-f|0;h=(L|0)/24|0;d=h+1|0;if((L|0)<-24)Pa();f=(g-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<d>>>0?d:f}else f=2147483647;ab(M,f,h,e+12|0);L=M+8|0;K=c[L>>2]|0;db(K,B);c[L>>2]=K+24;cb(e,M);bb(M)}Ia(B);b=b+2|0;break a}case 101:{pb(C,721);f=e+4|0;d=c[f>>2]|0;L=c[e+8>>2]|0;g=L;if(d>>>0<L>>>0){db(d,C);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;L=d-f|0;h=(L|0)/24|0;d=h+1|0;if((L|0)<-24)Pa();f=(g-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<d>>>0?d:f}else f=2147483647;ab(M,f,h,e+12|0);L=M+8|0;K=c[L>>2]|0;db(K,C);c[L>>2]=K+24;cb(e,M);bb(M)}Ia(C);b=b+2|0;break a}case 102:{mb(D,732);f=e+4|0;d=c[f>>2]|0;L=c[e+8>>2]|0;g=L;if(d>>>0<L>>>0){db(d,D);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;L=d-f|0;h=(L|0)/24|0;d=h+1|0;if((L|0)<-24)Pa();f=(g-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<d>>>0?d:f}else f=2147483647;ab(M,f,h,e+12|0);L=M+8|0;K=c[L>>2]|0;db(K,D);c[L>>2]=K+24;cb(e,M);bb(M)}Ia(D);b=b+2|0;break a}case 104:{mb(E,742);f=e+4|0;d=c[f>>2]|0;L=c[e+8>>2]|0;g=L;if(d>>>0<L>>>0){db(d,E);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;L=d-f|0;h=(L|0)/24|0;d=h+1|0;if((L|0)<-24)Pa();f=(g-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<d>>>0?d:f}else f=2147483647;ab(M,f,h,e+12|0);L=M+8|0;K=c[L>>2]|0;db(K,E);c[L>>2]=K+24;cb(e,M);bb(M)}Ia(E);b=b+2|0;break a}case 105:{nb(F,752);f=e+4|0;d=c[f>>2]|0;L=c[e+8>>2]|0;g=L;if(d>>>0<L>>>0){db(d,F);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;L=d-f|0;h=(L|0)/24|0;d=h+1|0;if((L|0)<-24)Pa();f=(g-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<d>>>0?d:f}else f=2147483647;ab(M,f,h,e+12|0);L=M+8|0;K=c[L>>2]|0;db(K,F);c[L>>2]=K+24;cb(e,M);bb(M)}Ia(F);b=b+2|0;break a}case 115:{nb(G,761);f=e+4|0;d=c[f>>2]|0;L=c[e+8>>2]|0;g=L;if(d>>>0<L>>>0){db(d,G);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;L=d-f|0;h=(L|0)/24|0;d=h+1|0;if((L|0)<-24)Pa();f=(g-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<d>>>0?d:f}else f=2147483647;ab(M,f,h,e+12|0);L=M+8|0;K=c[L>>2]|0;db(K,G);c[L>>2]=K+24;cb(e,M);bb(M)}Ia(G);b=b+2|0;break a}case 97:{fb(H,770);f=e+4|0;d=c[f>>2]|0;L=c[e+8>>2]|0;g=L;if(d>>>0<L>>>0){db(d,H);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;L=d-f|0;h=(L|0)/24|0;d=h+1|0;if((L|0)<-24)Pa();f=(g-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<d>>>0?d:f}else f=2147483647;ab(M,f,h,e+12|0);L=M+8|0;K=c[L>>2]|0;db(K,H);c[L>>2]=K+24;cb(e,M);bb(M)}Ia(H);b=b+2|0;break a}case 110:{jb(I,775);f=e+4|0;d=c[f>>2]|0;L=c[e+8>>2]|0;g=L;if(d>>>0<L>>>0){db(d,I);c[f>>2]=(c[f>>2]|0)+24}else{f=c[e>>2]|0;L=d-f|0;h=(L|0)/24|0;d=h+1|0;if((L|0)<-24)Pa();f=(g-f|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<d>>>0?d:f}else f=2147483647;ab(M,f,h,e+12|0);L=M+8|0;K=c[L>>2]|0;db(K,I);c[L>>2]=K+24;cb(e,M);bb(M)}Ia(I);b=b+2|0;break a}default:break a}}default:break a}while(0);while(0);i=N;return b|0}function fb(b,e){b=b|0;e=e|0;var f=0;a[b>>0]=8;f=b+1|0;e=d[e>>0]|d[e+1>>0]<<8|d[e+2>>0]<<16|d[e+3>>0]<<24;a[f>>0]=e;a[f+1>>0]=e>>8;a[f+2>>0]=e>>16;a[f+3>>0]=e>>24;a[b+5>>0]=0;e=b+12|0;b=0;while(1){if((b|0)==3)break;c[e+(b<<2)>>2]=0;b=b+1|0}return}function gb(a,b){a=a|0;b=b|0;$a(a,b,11);b=a+12|0;a=0;while(1){if((a|0)==3)break;c[b+(a<<2)>>2]=0;a=a+1|0}return}function hb(a,b){a=a|0;b=b|0;$a(a,b,13);b=a+12|0;a=0;while(1){if((a|0)==3)break;c[b+(a<<2)>>2]=0;a=a+1|0}return}function ib(b,d){b=b|0;d=d|0;var e=0;a[b>>0]=10;e=b+1|0;a[e>>0]=a[d>>0]|0;a[e+1>>0]=a[d+1>>0]|0;a[e+2>>0]=a[d+2>>0]|0;a[e+3>>0]=a[d+3>>0]|0;a[e+4>>0]=a[d+4>>0]|0;a[b+6>>0]=0;d=b+12|0;b=0;while(1){if((b|0)==3)break;c[d+(b<<2)>>2]=0;b=b+1|0}return}function jb(a,b){a=a|0;b=b|0;$a(a,b,14);b=a+12|0;a=0;while(1){if((a|0)==3)break;c[b+(a<<2)>>2]=0;a=a+1|0}return}function kb(b,d){b=b|0;d=d|0;var e=0;a[b>>0]=6;e=b+1|0;a[e>>0]=a[d>>0]|0;a[e+1>>0]=a[d+1>>0]|0;a[e+2>>0]=a[d+2>>0]|0;a[b+4>>0]=0;d=b+12|0;b=0;while(1){if((b|0)==3)break;c[d+(b<<2)>>2]=0;b=b+1|0}return}function lb(a,b){a=a|0;b=b|0;$a(a,b,12);b=a+12|0;a=0;while(1){if((a|0)==3)break;c[b+(a<<2)>>2]=0;a=a+1|0}return}function mb(b,d){b=b|0;d=d|0;var e=0,f=0;a[b>>0]=18;f=b+1|0;e=f+9|0;do{a[f>>0]=a[d>>0]|0;f=f+1|0;d=d+1|0}while((f|0)<(e|0));a[b+10>>0]=0;d=b+12|0;e=0;while(1){if((e|0)==3)break;c[d+(e<<2)>>2]=0;e=e+1|0}return}function nb(b,e){b=b|0;e=e|0;var f=0,g=0,h=0;a[b>>0]=16;f=e;h=f;h=d[h>>0]|d[h+1>>0]<<8|d[h+2>>0]<<16|d[h+3>>0]<<24;f=f+4|0;f=d[f>>0]|d[f+1>>0]<<8|d[f+2>>0]<<16|d[f+3>>0]<<24;e=b+1|0;g=e;a[g>>0]=h;a[g+1>>0]=h>>8;a[g+2>>0]=h>>16;a[g+3>>0]=h>>24;e=e+4|0;a[e>>0]=f;a[e+1>>0]=f>>8;a[e+2>>0]=f>>16;a[e+3>>0]=f>>24;a[b+9>>0]=0;e=b+12|0;b=0;while(1){if((b|0)==3)break;c[e+(b<<2)>>2]=0;b=b+1|0}return}function ob(a,b){a=a|0;b=b|0;$a(a,b,17);b=a+12|0;a=0;while(1){if((a|0)==3)break;c[b+(a<<2)>>2]=0;a=a+1|0}return}function pb(b,d){b=b|0;d=d|0;var e=0,f=0;a[b>>0]=20;f=b+1|0;e=f+10|0;do{a[f>>0]=a[d>>0]|0;f=f+1|0;d=d+1|0}while((f|0)<(e|0));a[b+11>>0]=0;d=b+12|0;e=0;while(1){if((e|0)==3)break;c[d+(e<<2)>>2]=0;e=e+1|0}return}function qb(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0;q=i;i=i+112|0;o=q+88|0;p=q+64|0;h=q+76|0;l=q+40|0;j=q+16|0;k=q;a:do if(((b|0)!=(d|0)?(g=(a[b>>0]|0)+-48|0,g>>>0<10):0)?(f=b+1|0,(f|0)!=(d|0)):0){m=f;n=g;while(1){g=(a[m>>0]|0)+-48|0;if(g>>>0>=10)break;f=m+1|0;if((f|0)==(d|0))break a;m=f;n=g+(n*10|0)|0}if((d-m|0)>>>0>=n>>>0){$a(p,m,n);f=a[p>>0]|0;d=(f&1)==0;f=d?(f&255)>>>1:c[p+4>>2]|0;$a(h,d?p+1|0:c[p+8>>2]|0,f>>>0<10?f:10);f=a[h>>0]|0;d=(f&1)==0;f=d?(f&255)>>>1:c[h+4>>2]|0;g=f>>>0>10;d=ac(d?h+1|0:c[h+8>>2]|0,678,g?10:f)|0;Ja(h);if(!(((d|0)==0?(f>>>0<10?-1:g&1):d)|0)){b=vc(32)|0;c[l+8>>2]=b;c[l>>2]=33;c[l+4>>2]=21;f=b;g=689;h=f+21|0;do{a[f>>0]=a[g>>0]|0;f=f+1|0;g=g+1|0}while((f|0)<(h|0));a[b+21>>0]=0;b=l+12|0;f=0;while(1){if((f|0)==3)break;c[b+(f<<2)>>2]=0;f=f+1|0}b=e+4|0;f=c[b>>2]|0;k=c[e+8>>2]|0;g=k;if(f>>>0<k>>>0){db(f,l);c[b>>2]=(c[b>>2]|0)+24}else{b=c[e>>2]|0;k=f-b|0;h=(k|0)/24|0;f=h+1|0;if((k|0)<-24)Pa();b=(g-b|0)/24|0;if(b>>>0<1073741823){b=b<<1;b=b>>>0<f>>>0?f:b}else b=2147483647;ab(o,b,h,e+12|0);k=o+8|0;j=c[k>>2]|0;db(j,l);c[k>>2]=j+24;cb(e,o);bb(o)}Ia(l)}else{c[k>>2]=c[p>>2];c[k+4>>2]=c[p+4>>2];c[k+8>>2]=c[p+8>>2];b=0;while(1){if((b|0)==3)break;c[p+(b<<2)>>2]=0;b=b+1|0}rb(j,k);b=e+4|0;f=c[b>>2]|0;l=c[e+8>>2]|0;g=l;if(f>>>0<l>>>0){db(f,j);c[b>>2]=(c[b>>2]|0)+24}else{b=c[e>>2]|0;l=f-b|0;h=(l|0)/24|0;f=h+1|0;if((l|0)<-24)Pa();b=(g-b|0)/24|0;if(b>>>0<1073741823){b=b<<1;b=b>>>0<f>>>0?f:b}else b=2147483647;ab(o,b,h,e+12|0);l=o+8|0;d=c[l>>2]|0;db(d,j);c[l>>2]=d+24;cb(e,o);bb(o)}Ia(j);Ja(k)}Ja(p);b=m+n|0}}while(0);i=q;return b|0}function rb(a,b){a=a|0;b=b|0;var d=0;c[a>>2]=c[b>>2];c[a+4>>2]=c[b+4>>2];c[a+8>>2]=c[b+8>>2];d=0;while(1){if((d|0)==3)break;c[b+(d<<2)>>2]=0;d=d+1|0}d=a+12|0;b=0;while(1){if((b|0)==3)break;c[d+(b<<2)>>2]=0;b=b+1|0}return}function sb(b){b=b|0;var d=0,e=0,f=0,g=0,h=0;d=a[b>>0]|0;if(!(d&1)){e=(d&255)>>>1;h=b+1|0}else{e=c[b+4>>2]|0;h=c[b+8>>2]|0}f=(e|0)!=0&1;g=e-f|0;if((e|0)!=(f|0)){Hc(h|0,h+f|0,g|0)|0;d=a[b>>0]|0}if(!(d&1))a[b>>0]=g<<1;else c[b+4>>2]=g;a[h+g>>0]=0;return}function tb(b,c){b=b|0;c=c|0;var d=0,e=0;a:do if((b|0)!=(c|0)?(d=(a[b>>0]|0)==110?b+1|0:b,(d|0)!=(c|0)):0){e=a[d>>0]|0;if(e<<24>>24==48){d=d+1|0;break}if((e+-49&255)<9)do{d=d+1|0;if((d|0)==(c|0)){d=c;break a}}while(((a[d>>0]|0)+-48|0)>>>0<10);else d=b}else d=b;while(0);return d|0}
function Sb(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0;s=i;i=i+112|0;r=s+88|0;m=s+64|0;n=s+48|0;l=s+24|0;o=s+12|0;p=s;a:do if((b|0)!=(d|0)){g=a[b>>0]|0;h=g<<24>>24;switch(h|0){case 68:case 67:{b:do if((d-b|0)>1?(k=e+4|0,f=c[k>>2]|0,(c[e>>2]|0)!=(f|0)):0){switch(h|0){case 67:{switch(a[b+1>>0]|0){case 53:case 51:case 50:case 49:break;default:break b}Tb(n,f+-24|0);rb(m,n);f=c[k>>2]|0;d=c[e+8>>2]|0;j=d;if(f>>>0<d>>>0){db(f,m);c[k>>2]=(c[k>>2]|0)+24}else{g=c[e>>2]|0;d=f-g|0;k=(d|0)/24|0;h=k+1|0;if((d|0)<-24)Pa();f=(j-g|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<h>>>0?h:f}else f=2147483647;ab(r,f,k,e+12|0);d=r+8|0;q=c[d>>2]|0;db(q,m);c[d>>2]=q+24;cb(e,r);bb(r)}Ia(m);Ja(n);a[e+60>>0]=1;b=b+2|0;break b}case 68:break;default:break b}switch(a[b+1>>0]|0){case 53:case 50:case 49:case 48:break;default:break b}Tb(p,f+-24|0);f=Ta(p,0,886)|0;c[o>>2]=c[f>>2];c[o+4>>2]=c[f+4>>2];c[o+8>>2]=c[f+8>>2];g=0;while(1){if((g|0)==3)break;c[f+(g<<2)>>2]=0;g=g+1|0}rb(l,o);f=c[k>>2]|0;d=c[e+8>>2]|0;j=d;if(f>>>0<d>>>0){db(f,l);c[k>>2]=(c[k>>2]|0)+24}else{g=c[e>>2]|0;d=f-g|0;k=(d|0)/24|0;h=k+1|0;if((d|0)<-24)Pa();f=(j-g|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<h>>>0?h:f}else f=2147483647;ab(r,f,k,e+12|0);d=r+8|0;q=c[d>>2]|0;db(q,l);c[d>>2]=q+24;cb(e,r);bb(r)}Ia(l);Ja(o);Ja(p);a[e+60>>0]=1;b=b+2|0}while(0);break a}case 85:{c:do if((d-b|0)>2&g<<24>>24==85){switch(a[b+1>>0]|0){case 116:{$a(n,1808,8);rb(m,n);l=e+4|0;f=c[l>>2]|0;q=c[e+8>>2]|0;g=q;if(f>>>0<q>>>0){db(f,m);c[l>>2]=(c[l>>2]|0)+24}else{h=c[e>>2]|0;q=f-h|0;k=(q|0)/24|0;j=k+1|0;if((q|0)<-24)Pa();f=(g-h|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<j>>>0?j:f}else f=2147483647;ab(r,f,k,e+12|0);q=r+8|0;p=c[q>>2]|0;db(p,m);c[q>>2]=p+24;cb(e,r);bb(r)}Ia(m);Ja(n);f=b+2|0;if((f|0)==(d|0)){g=c[l>>2]|0;f=g+-24|0;while(1){if((g|0)==(f|0))break c;d=g+-24|0;c[l>>2]=d;Ia(d);g=c[l>>2]|0}}if(((a[f>>0]|0)+-48|0)>>>0<10){g=b+3|0;while(1){if((g|0)==(d|0)){g=d;break}if(((a[g>>0]|0)+-48|0)>>>0>=10)break;g=g+1|0}Bb((c[l>>2]|0)+-24|0,f,g);f=g}zb((c[l>>2]|0)+-24|0,39);if((f|0)!=(d|0)?(a[f>>0]|0)==95:0){b=f+1|0;break c}g=c[l>>2]|0;f=g+-24|0;while(1){if((g|0)==(f|0))break c;d=g+-24|0;c[l>>2]=d;Ia(d);g=c[l>>2]|0}}case 108:break;default:break c}$a(o,1817,9);rb(l,o);q=e+4|0;f=c[q>>2]|0;n=c[e+8>>2]|0;g=n;if(f>>>0<n>>>0){db(f,l);c[q>>2]=(c[q>>2]|0)+24}else{h=c[e>>2]|0;n=f-h|0;k=(n|0)/24|0;j=k+1|0;if((n|0)<-24)Pa();f=(g-h|0)/24|0;if(f>>>0<1073741823){f=f<<1;f=f>>>0<j>>>0?j:f}else f=2147483647;ab(r,f,k,e+12|0);n=r+8|0;m=c[n>>2]|0;db(m,l);c[n>>2]=m+24;cb(e,r);bb(r)}Ia(l);Ja(o);f=b+2|0;do if((a[f>>0]|0)!=118){g=Na(f,d,e)|0;if((g|0)==(f|0)){g=c[q>>2]|0;f=g+-24|0;while(1){if((g|0)==(f|0))break c;d=g+-24|0;c[q>>2]=d;Ia(d);g=c[q>>2]|0}}f=c[q>>2]|0;if(((f-(c[e>>2]|0)|0)/24|0)>>>0<2)break c;Cb(r,f+-24|0);j=c[q>>2]|0;f=j+-24|0;h=j;while(1){if((h|0)==(f|0))break;o=h+-24|0;c[q>>2]=o;Ia(o);h=c[q>>2]|0}h=a[r>>0]|0;l=(h&1)==0;m=r+8|0;n=r+1|0;o=r+4|0;Za(j+-48|0,l?n:c[m>>2]|0,l?(h&255)>>>1:c[o>>2]|0)|0;while(1){l=Na(g,d,e)|0;if((l|0)==(g|0)){f=91;break}f=c[q>>2]|0;if(((f-(c[e>>2]|0)|0)/24|0)>>>0<2){f=129;break}Cb(p,f+-24|0);d:do if(!(h&1)){a[n>>0]=0;a[r>>0]=0}else{g=c[m>>2]|0;a[g>>0]=0;c[o>>2]=0;f=c[r>>2]|0;k=(f&-2)+-1|0;h=f&255;do if(!(h&1)){f=f>>>1&127;if((h&255)<22){Fc(n|0,g|0,f+1|0)|0;wc(g);break}g=f+16&240;j=g+-1|0;if((j|0)==(k|0))break d;h=vc(g)|0;if(j>>>0<=k>>>0&(h|0)==0)break d;Fc(h|0,n|0,f+1|0)|0;c[r>>2]=g|1;c[o>>2]=f;c[m>>2]=h;break d}else{a[n>>0]=0;wc(g);f=0}while(0);a[r>>0]=f<<1}while(0);c[r>>2]=c[p>>2];c[r+4>>2]=c[p+4>>2];c[r+8>>2]=c[p+8>>2];f=0;while(1){if((f|0)==3)break;c[p+(f<<2)>>2]=0;f=f+1|0}Ja(p);j=c[q>>2]|0;f=j+-24|0;g=j;while(1){if((g|0)==(f|0))break;k=g+-24|0;c[q>>2]=k;Ia(k);g=c[q>>2]|0}h=a[r>>0]|0;f=(h&1)==0;g=f?(h&255)>>>1:c[o>>2]|0;if(!g){g=l;continue}Ya(j+-48|0,1429)|0;Za((c[q>>2]|0)+-24|0,f?n:c[m>>2]|0,g)|0;g=l}if((f|0)==91){Ya((c[q>>2]|0)+-24|0,799)|0;Ja(r);break}else if((f|0)==129){Ja(r);break c}}else{zb((c[q>>2]|0)+-24|0,41);g=b+3|0}while(0);if((g|0)!=(d|0)?(a[g>>0]|0)==69:0){f=g+1|0;if((f|0)==(d|0)){g=c[q>>2]|0;f=g+-24|0;while(1){if((g|0)==(f|0))break c;d=g+-24|0;c[q>>2]=d;Ia(d);g=c[q>>2]|0}}e:do if(((a[f>>0]|0)+-48|0)>>>0<10){g=g+2|0;while(1){if((g|0)==(d|0)){g=d;break}if(((a[g>>0]|0)+-48|0)>>>0>=10)break;g=g+1|0}p=c[q>>2]|0;e=p+-24|0;h=a[e>>0]|0;l=p+-16|0;if(!(h&1)){j=l;k=e+1|0;o=(h&255)>>>1;m=10}else{k=c[l>>2]|0;h=c[e>>2]|0;j=k+7|0;o=c[p+-20>>2]|0;m=(h&-2)+-1|0;h=h&255}n=j-k|0;k=g-f|0;if((g|0)!=(f|0)){if((m-o|0)>>>0>=k>>>0){if(!(h&1))h=e+1|0;else h=c[l>>2]|0;if((o|0)==(n|0))j=h;else{j=h+n|0;Hc(j+k|0,j|0,o-n|0)|0;j=h}}else{Ab(e,m,o+k-m|0,o,n,k);j=c[l>>2]|0}h=o+k|0;if(!(a[e>>0]&1))a[e>>0]=h<<1;else c[p+-20>>2]=h;a[j+h>>0]=0;h=j+n|0;while(1){if((f|0)==(g|0)){f=g;break e}a[h>>0]=a[f>>0]|0;f=f+1|0;h=h+1|0}}}while(0);if((f|0)!=(d|0)?(a[f>>0]|0)==95:0){b=f+1|0;break}g=c[q>>2]|0;f=g+-24|0;while(1){if((g|0)==(f|0))break c;d=g+-24|0;c[q>>2]=d;Ia(d);g=c[q>>2]|0}}g=c[q>>2]|0;f=g+-24|0;while(1){if((g|0)==(f|0))break c;d=g+-24|0;c[q>>2]=d;Ia(d);g=c[q>>2]|0}}while(0);break a}case 57:case 56:case 55:case 54:case 53:case 52:case 51:case 50:case 49:{b=qb(b,d,e)|0;break a}default:{d=Lb(b,d,e)|0;i=s;return d|0}}}while(0);i=s;return b|0}function Tb(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,i=0;h=a[d>>0]|0;e=(h&1)==0;h=e?(h&255)>>>1:c[d+4>>2]|0;a:do if(!h)_a(b,d);else{f=e?d+1|0:c[d+8>>2]|0;e=h>>>0>11;g=ac(f,1478,e?11:h)|0;if(!(((g|0)==0?(h>>>0<11?-1:e&1):g)|0)){Ub(d,1530,70);$a(b,1601,12);break}e=h>>>0>12;g=e?12:h;i=ac(f,1490,g)|0;e=h>>>0<12?-1:e&1;if(!(((i|0)==0?e:i)|0)){Ub(d,1614,49);$a(b,1664,13);break}i=ac(f,1503,g)|0;if(!(((i|0)==0?e:i)|0)){Ub(d,1678,49);$a(b,1728,13);break}g=h>>>0>13;i=ac(f,1516,g?13:h)|0;if(!(((i|0)==0?(h>>>0<13?-1:g&1):i)|0)){Ub(d,1742,50);$a(b,1793,14);break}e=f+h|0;b:do if((a[e+-1>>0]|0)==62){g=1;c:while(1){h=e;d:while(1){e=h+-1|0;if((e|0)==(f|0))break c;h=h+-2|0;switch(a[h>>0]|0){case 60:{d=18;break d}case 62:{d=19;break d}default:h=e}}if((d|0)==18){g=g+-1|0;if(!g){e=h;break b}else continue}else if((d|0)==19){g=g+1|0;continue}}e=0;while(1){if((e|0)==3)break a;c[b+(e<<2)>>2]=0;e=e+1|0}}while(0);h=e;while(1){g=h+-1|0;if((g|0)==(f|0))break;if((a[g>>0]|0)==58){f=h;break}else h=g}d=e-f|0;if(d>>>0>4294967279)Xa();if(d>>>0<11){a[b>>0]=d<<1;h=b+1|0}else{i=d+16&-16;h=vc(i)|0;c[b+8>>2]=h;c[b>>2]=i|1;c[b+4>>2]=d}g=h;while(1){if((f|0)==(e|0))break;a[g>>0]=a[f>>0]|0;f=f+1|0;g=g+1|0}a[h+d>>0]=0}while(0);return}function Ub(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0;f=a[b>>0]|0;if(!(f&1))h=10;else{f=c[b>>2]|0;h=(f&-2)+-1|0;f=f&255}g=(f&1)==0;do if(h>>>0>=e>>>0){if(g)f=b+1|0;else f=c[b+8>>2]|0;Hc(f|0,d|0,e|0)|0;a[f+e>>0]=0;if(!(a[b>>0]&1)){a[b>>0]=e<<1;break}else{c[b+4>>2]=e;break}}else{if(g)f=(f&255)>>>1;else f=c[b+4>>2]|0;Wa(b,h,e-h|0,f,0,f,e,d)}while(0);return}function Vb(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0;k=i;i=i+16|0;j=k;if((b|0)!=(d|0)?(f=qb(b,d,e)|0,(f|0)!=(b|0)):0){h=Mb(f,d,e)|0;if((h|0)!=(f|0)){g=e+4|0;f=c[g>>2]|0;if(((f-(c[e>>2]|0)|0)/24|0)>>>0<2)f=b;else{Cb(j,f+-24|0);f=c[g>>2]|0;d=f+-24|0;e=f;while(1){if((e|0)==(d|0))break;b=e+-24|0;c[g>>2]=b;Ia(b);e=c[g>>2]|0}g=a[j>>0]|0;b=(g&1)==0;Za(f+-48|0,b?j+1|0:c[j+8>>2]|0,b?(g&255)>>>1:c[j+4>>2]|0)|0;Ja(j);f=h}}}else f=b;i=k;return f|0}function Wb(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,aa=0,ba=0,ca=0,da=0,ea=0,fa=0,ga=0,ha=0,ia=0,ja=0,ka=0,la=0,ma=0,na=0,oa=0,pa=0,qa=0,ra=0,sa=0,ta=0,ua=0;ua=i;i=i+208|0;ta=ua+188|0;ra=ua+184|0;pa=ua+172|0;ba=ua+160|0;ca=ua+144|0;ha=ua+140|0;ia=ua+128|0;ja=ua+112|0;ka=ua+108|0;la=ua+96|0;ma=ua+64|0;na=ua+56|0;oa=ua+40|0;da=ua+36|0;ea=ua+24|0;fa=ua+8|0;ga=ua;n=ua+80|0;k=ua+60|0;m=d;a:do if((m-b|0)>1){sa=(a[b>>0]|0)==76?b+1|0:b;f=a[sa>>0]|0;switch(f<<24>>24|0){case 78:{b:do if((sa|0)!=(d|0))if(f<<24>>24==78){f=Oa(sa+1|0,d,ra)|0;c:do if((f|0)!=(d|0)){h=e+52|0;c[h>>2]=0;switch(a[f>>0]|0){case 82:{c[h>>2]=1;f=f+1|0;break}case 79:{c[h>>2]=2;f=f+1|0;break}default:{}}aa=e+4|0;j=c[aa>>2]|0;$=c[e+8>>2]|0;h=$;if(j>>>0<$>>>0){c[j>>2]=0;c[j+4>>2]=0;c[j+8>>2]=0;c[j+12>>2]=0;c[j+16>>2]=0;c[j+20>>2]=0;h=0;while(1){if((h|0)==3)break;c[j+(h<<2)>>2]=0;h=h+1|0}h=j+12|0;j=0;while(1){if((j|0)==3)break;c[h+(j<<2)>>2]=0;j=j+1|0}c[aa>>2]=(c[aa>>2]|0)+24}else{k=c[e>>2]|0;$=j-k|0;l=($|0)/24|0;j=l+1|0;if(($|0)<-24)Pa();h=(h-k|0)/24|0;if(h>>>0<1073741823){h=h<<1;h=h>>>0<j>>>0?j:h}else h=2147483647;ab(ta,h,l,e+12|0);k=ta+8|0;l=c[k>>2]|0;c[l>>2]=0;c[l+4>>2]=0;c[l+8>>2]=0;c[l+12>>2]=0;c[l+16>>2]=0;c[l+20>>2]=0;h=0;while(1){if((h|0)==3)break;c[l+(h<<2)>>2]=0;h=h+1|0}h=l+12|0;j=0;while(1){if((j|0)==3)break;c[h+(j<<2)>>2]=0;j=j+1|0}c[k>>2]=l+24;cb(e,ta);bb(ta)}if(((m-f|0)>1?(a[f>>0]|0)==83:0)?(a[f+1>>0]|0)==116:0){Ub((c[aa>>2]|0)+-24|0,2080,3);f=f+2|0}if((f|0)==(d|0)){g=c[aa>>2]|0;f=g+-24|0;while(1){if((g|0)==(f|0))break c;ta=g+-24|0;c[aa>>2]=ta;Ia(ta);g=c[aa>>2]|0}}I=pa+8|0;J=pa+1|0;K=pa+4|0;L=e+12|0;M=e+16|0;$=e+20|0;N=e+24|0;O=oa+12|0;P=oa+4|0;Q=oa+8|0;R=e+28|0;S=ta+8|0;T=fa+12|0;U=fa+4|0;V=fa+8|0;W=ta+8|0;X=ea+8|0;Y=ea+1|0;Z=ea+4|0;_=ba+8|0;o=ba+1|0;p=ba+4|0;q=ca+12|0;r=ca+4|0;s=ca+8|0;t=ta+8|0;u=ja+12|0;v=ja+4|0;w=ja+8|0;x=ta+8|0;y=ia+8|0;z=ia+1|0;A=ia+4|0;B=ma+12|0;C=ma+4|0;D=ma+8|0;E=ta+8|0;F=la+8|0;G=la+1|0;H=la+4|0;n=0;d:while(1){h=f;e:while(1){f=a[h>>0]|0;if(f<<24>>24==69){qa=129;break d}switch(f<<24>>24|0){case 83:{qa=39;break e}case 84:{qa=59;break e}case 68:{qa=77;break e}case 73:break;case 76:{f=h+1|0;if((f|0)==(d|0))break c;else{h=f;continue e}}default:break e}m=Mb(h,d,e)|0;if((m|0)==(h|0)|(m|0)==(d|0))break c;Cb(pa,(c[aa>>2]|0)+-24|0);f=c[aa>>2]|0;h=f+-24|0;j=f;while(1){if((j|0)==(h|0))break;l=j+-24|0;c[aa>>2]=l;Ia(l);j=c[aa>>2]|0}l=a[pa>>0]|0;h=(l&1)==0;Za(f+-48|0,h?J:c[I>>2]|0,h?(l&255)>>>1:c[K>>2]|0)|0;f=(c[aa>>2]|0)+-24|0;c[da>>2]=c[L>>2];Pb(oa,f,da);f=c[$>>2]|0;l=c[N>>2]|0;h=l;if(f>>>0<l>>>0){c[f+12>>2]=c[O>>2];c[f>>2]=c[oa>>2];c[f+4>>2]=c[P>>2];c[f+8>>2]=c[Q>>2];c[Q>>2]=0;c[P>>2]=0;c[oa>>2]=0;c[$>>2]=(c[$>>2]|0)+16}else{j=c[M>>2]|0;f=f-j|0;l=f>>4;k=l+1|0;if((f|0)<-16){qa=104;break d}f=h-j|0;if(f>>4>>>0<1073741823){f=f>>3;f=f>>>0<k>>>0?k:f}else f=2147483647;Qa(ta,f,l,R);l=c[S>>2]|0;c[l+12>>2]=c[O>>2];c[l>>2]=c[oa>>2];c[l+4>>2]=c[P>>2];c[l+8>>2]=c[Q>>2];c[Q>>2]=0;c[P>>2]=0;c[oa>>2]=0;c[S>>2]=l+16;Ra(M,ta);Sa(ta)}Ha(oa);Ja(pa);h=m}f:do if((qa|0)==39){qa=0;n=h+1|0;if((n|0)!=(d|0)?(a[n>>0]|0)==116:0)break;f=Rb(h,d,e)|0;if((f|0)==(h|0)|(f|0)==(d|0))break c;Cb(pa,(c[aa>>2]|0)+-24|0);k=c[aa>>2]|0;h=k+-24|0;j=k;while(1){if((j|0)==(h|0))break;n=j+-24|0;c[aa>>2]=n;Ia(n);j=c[aa>>2]|0}j=k+-48|0;h=a[j>>0]|0;if(!(h&1))h=(h&255)>>>1;else h=c[k+-44>>2]|0;if(!h)Xb(j,pa);else{xb(ba,891,pa);h=a[ba>>0]|0;n=(h&1)==0;Za(j,n?o:c[_>>2]|0,n?(h&255)>>>1:c[p>>2]|0)|0;Ja(ba);h=(c[aa>>2]|0)+-24|0;c[ha>>2]=c[L>>2];Pb(ca,h,ha);h=c[$>>2]|0;n=c[N>>2]|0;j=n;if(h>>>0<n>>>0){c[h+12>>2]=c[q>>2];c[h>>2]=c[ca>>2];c[h+4>>2]=c[r>>2];c[h+8>>2]=c[s>>2];c[s>>2]=0;c[r>>2]=0;c[ca>>2]=0;c[$>>2]=(c[$>>2]|0)+16}else{k=c[M>>2]|0;n=h-k|0;m=n>>4;l=m+1|0;if((n|0)<-16){qa=52;break d}h=j-k|0;if(h>>4>>>0<1073741823){h=h>>3;h=h>>>0<l>>>0?l:h}else h=2147483647;Qa(ta,h,m,R);n=c[t>>2]|0;c[n+12>>2]=c[q>>2];c[n>>2]=c[ca>>2];c[n+4>>2]=c[r>>2];c[n+8>>2]=c[s>>2];c[s>>2]=0;c[r>>2]=0;c[ca>>2]=0;c[t>>2]=n+16;Ra(M,ta);Sa(ta)}Ha(ca)}Ja(pa);n=1;continue d}else if((qa|0)==59){qa=0;f=Eb(h,d,e)|0;if((f|0)==(h|0)|(f|0)==(d|0))break c;Cb(pa,(c[aa>>2]|0)+-24|0);k=c[aa>>2]|0;h=k+-24|0;j=k;while(1){if((j|0)==(h|0))break;n=j+-24|0;c[aa>>2]=n;Ia(n);j=c[aa>>2]|0}j=k+-48|0;h=a[j>>0]|0;if(!(h&1))h=(h&255)>>>1;else h=c[k+-44>>2]|0;if(!h)Xb(j,pa);else{xb(ia,891,pa);n=a[ia>>0]|0;m=(n&1)==0;Za(j,m?z:c[y>>2]|0,m?(n&255)>>>1:c[A>>2]|0)|0;Ja(ia)}h=(c[aa>>2]|0)+-24|0;c[ka>>2]=c[L>>2];Pb(ja,h,ka);h=c[$>>2]|0;n=c[N>>2]|0;j=n;if(h>>>0<n>>>0){c[h+12>>2]=c[u>>2];c[h>>2]=c[ja>>2];c[h+4>>2]=c[v>>2];c[h+8>>2]=c[w>>2];c[w>>2]=0;c[v>>2]=0;c[ja>>2]=0;c[$>>2]=(c[$>>2]|0)+16}else{k=c[M>>2]|0;n=h-k|0;m=n>>4;l=m+1|0;if((n|0)<-16){qa=72;break d}h=j-k|0;if(h>>4>>>0<1073741823){h=h>>3;h=h>>>0<l>>>0?l:h}else h=2147483647;Qa(ta,h,m,R);n=c[x>>2]|0;c[n+12>>2]=c[u>>2];c[n>>2]=c[ja>>2];c[n+4>>2]=c[v>>2];c[n+8>>2]=c[w>>2];c[w>>2]=0;c[v>>2]=0;c[ja>>2]=0;c[x>>2]=n+16;Ra(M,ta);Sa(ta)}Ha(ja);Ja(pa);n=1;continue d}else if((qa|0)==77){qa=0;f=h+1|0;if((f|0)!=(d|0))switch(a[f>>0]|0){case 84:case 116:break;default:break f}f=Qb(h,d,e)|0;if((f|0)==(h|0)|(f|0)==(d|0))break c;Cb(pa,(c[aa>>2]|0)+-24|0);k=c[aa>>2]|0;h=k+-24|0;j=k;while(1){if((j|0)==(h|0))break;n=j+-24|0;c[aa>>2]=n;Ia(n);j=c[aa>>2]|0}j=k+-48|0;h=a[j>>0]|0;if(!(h&1))h=(h&255)>>>1;else h=c[k+-44>>2]|0;if(!h)Xb(j,pa);else{xb(la,891,pa);n=a[la>>0]|0;m=(n&1)==0;Za(j,m?G:c[F>>2]|0,m?(n&255)>>>1:c[H>>2]|0)|0;Ja(la)}h=(c[aa>>2]|0)+-24|0;c[na>>2]=c[L>>2];Pb(ma,h,na);h=c[$>>2]|0;n=c[N>>2]|0;j=n;if(h>>>0<n>>>0){c[h+12>>2]=c[B>>2];c[h>>2]=c[ma>>2];c[h+4>>2]=c[C>>2];c[h+8>>2]=c[D>>2];c[D>>2]=0;c[C>>2]=0;c[ma>>2]=0;c[$>>2]=(c[$>>2]|0)+16}else{k=c[M>>2]|0;n=h-k|0;m=n>>4;l=m+1|0;if((n|0)<-16){qa=92;break d}h=j-k|0;if(h>>4>>>0<1073741823){h=h>>3;h=h>>>0<l>>>0?l:h}else h=2147483647;Qa(ta,h,m,R);n=c[E>>2]|0;c[n+12>>2]=c[B>>2];c[n>>2]=c[ma>>2];c[n+4>>2]=c[C>>2];c[n+8>>2]=c[D>>2];c[D>>2]=0;c[C>>2]=0;c[ma>>2]=0;c[E>>2]=n+16;Ra(M,ta);Sa(ta)}Ha(ma);Ja(pa);n=1;continue d}while(0);f=Sb(h,d,e)|0;if((f|0)==(h|0)|(f|0)==(d|0))break c;Cb(pa,(c[aa>>2]|0)+-24|0);k=c[aa>>2]|0;h=k+-24|0;j=k;while(1){if((j|0)==(h|0))break;n=j+-24|0;c[aa>>2]=n;Ia(n);j=c[aa>>2]|0}j=k+-48|0;h=a[j>>0]|0;if(!(h&1))h=(h&255)>>>1;else h=c[k+-44>>2]|0;if(!h)Xb(j,pa);else{xb(ea,891,pa);n=a[ea>>0]|0;m=(n&1)==0;Za(j,m?Y:c[X>>2]|0,m?(n&255)>>>1:c[Z>>2]|0)|0;Ja(ea)}h=(c[aa>>2]|0)+-24|0;c[ga>>2]=c[L>>2];Pb(fa,h,ga);h=c[$>>2]|0;n=c[N>>2]|0;j=n;if(h>>>0<n>>>0){c[h+12>>2]=c[T>>2];c[h>>2]=c[fa>>2];c[h+4>>2]=c[U>>2];c[h+8>>2]=c[V>>2];c[V>>2]=0;c[U>>2]=0;c[fa>>2]=0;c[$>>2]=(c[$>>2]|0)+16}else{k=c[M>>2]|0;n=h-k|0;m=n>>4;l=m+1|0;if((n|0)<-16){qa=123;break}h=j-k|0;if(h>>4>>>0<1073741823){h=h>>3;h=h>>>0<l>>>0?l:h}else h=2147483647;Qa(ta,h,m,R);n=c[W>>2]|0;c[n+12>>2]=c[T>>2];c[n>>2]=c[fa>>2];c[n+4>>2]=c[U>>2];c[n+8>>2]=c[V>>2];c[V>>2]=0;c[U>>2]=0;c[fa>>2]=0;c[W>>2]=n+16;Ra(M,ta);Sa(ta)}Ha(fa);Ja(pa);n=1}if((qa|0)==52)Pa();else if((qa|0)==72)Pa();else if((qa|0)==92)Pa();else if((qa|0)==104)Pa();else if((qa|0)==123)Pa();else if((qa|0)==129){f=h+1|0;c[e+48>>2]=c[ra>>2];g:do if(n?(g=c[$>>2]|0,(c[e+16>>2]|0)!=(g|0)):0){h=g+-16|0;while(1){if((g|0)==(h|0))break g;ta=g+-16|0;c[$>>2]=ta;Ha(ta);g=c[$>>2]|0}}while(0);break b}}while(0);f=sa}else f=sa;else f=d;while(0);f=(f|0)==(sa|0)?b:f;break a}case 90:{h:do if(((f<<24>>24==90&(sa|0)!=(d|0)?(ra=sa+1|0,h=Ma(ra,d,e)|0,!((h|0)==(ra|0)|(h|0)==(d|0))):0)?(a[h>>0]|0)==69:0)?(j=h+1|0,(j|0)!=(d|0)):0)switch(a[j>>0]|0){case 115:{f=Yb(h+2|0,d)|0;g=c[e+4>>2]|0;if((c[e>>2]|0)==(g|0))break h;Ya(g+-24|0,2084)|0;break h}case 100:{f=h+2|0;if((f|0)==(d|0)){f=sa;break h}f=tb(f,d)|0;if((f|0)==(d|0)){f=sa;break h}if((a[f>>0]|0)!=95){f=sa;break h}ra=f+1|0;f=Wb(ra,d,e)|0;k=e+4|0;if((f|0)==(ra|0)){g=c[k>>2]|0;f=g+-24|0;while(1){if((g|0)==(f|0)){f=sa;break h}ta=g+-24|0;c[k>>2]=ta;Ia(ta);g=c[k>>2]|0}}g=c[k>>2]|0;if(((g-(c[e>>2]|0)|0)/24|0)>>>0<2){f=sa;break h}Cb(ta,g+-24|0);g=c[k>>2]|0;h=g+-24|0;j=g;while(1){if((j|0)==(h|0))break;e=j+-24|0;c[k>>2]=e;Ia(e);j=c[k>>2]|0}Ya(g+-48|0,891)|0;e=a[ta>>0]|0;d=(e&1)==0;Za((c[k>>2]|0)+-24|0,d?ta+1|0:c[ta+8>>2]|0,d?(e&255)>>>1:c[ta+4>>2]|0)|0;Ja(ta);break h}default:{f=Wb(j,d,e)|0;if((f|0)==(j|0)){f=e+4|0;h=c[f>>2]|0;g=h+-24|0;while(1){if((h|0)==(g|0)){f=sa;break h}ta=h+-24|0;c[f>>2]=ta;Ia(ta);h=c[f>>2]|0}}f=Yb(f,d)|0;k=e+4|0;g=c[k>>2]|0;if(((g-(c[e>>2]|0)|0)/24|0)>>>0<2)break h;Cb(ta,g+-24|0);g=c[k>>2]|0;h=g+-24|0;j=g;while(1){if((j|0)==(h|0))break;e=j+-24|0;c[k>>2]=e;Ia(e);j=c[k>>2]|0}Ya(g+-48|0,891)|0;e=a[ta>>0]|0;d=(e&1)==0;Za((c[k>>2]|0)+-24|0,d?ta+1|0:c[ta+8>>2]|0,d?(e&255)>>>1:c[ta+4>>2]|0)|0;Ja(ta);break h}}else f=sa;while(0);f=(f|0)==(sa|0)?b:f;break a}default:{do if((m-sa|0)>1){if(f<<24>>24==83?(a[sa+1>>0]|0)==116:0){f=sa+2|0;if((f|0)==(d|0)){h=0;g=d}else{h=0;g=(a[f>>0]|0)==76?sa+3|0:f}}else{h=1;g=sa}f=Sb(g,d,e)|0;g=(f|0)==(g|0);if(h|g)f=g?sa:f;else{g=c[e+4>>2]|0;if((c[e>>2]|0)==(g|0))break;Ta(g+-24|0,0,1827)|0}if((f|0)!=(sa|0)){if((f|0)==(d|0)){f=d;break a}if((a[f>>0]|0)!=73)break a;m=e+4|0;g=c[m>>2]|0;if((c[e>>2]|0)==(g|0)){f=b;break a}l=e+16|0;c[k>>2]=c[e+12>>2];Pb(n,g+-24|0,k);g=e+20|0;h=c[g>>2]|0;sa=c[e+24>>2]|0;j=sa;if(h>>>0<sa>>>0){c[h+12>>2]=c[n+12>>2];c[h>>2]=c[n>>2];sa=n+4|0;c[h+4>>2]=c[sa>>2];ra=n+8|0;c[h+8>>2]=c[ra>>2];c[ra>>2]=0;c[sa>>2]=0;c[n>>2]=0;c[g>>2]=(c[g>>2]|0)+16}else{g=c[l>>2]|0;sa=h-g|0;k=sa>>4;h=k+1|0;if((sa|0)<-16)Pa();g=j-g|0;if(g>>4>>>0<1073741823){g=g>>3;g=g>>>0<h>>>0?h:g}else g=2147483647;Qa(ta,g,k,e+28|0);sa=ta+8|0;ra=c[sa>>2]|0;c[ra+12>>2]=c[n+12>>2];c[ra>>2]=c[n>>2];qa=n+4|0;c[ra+4>>2]=c[qa>>2];pa=n+8|0;c[ra+8>>2]=c[pa>>2];c[pa>>2]=0;c[qa>>2]=0;c[n>>2]=0;c[sa>>2]=ra+16;Ra(l,ta);Sa(ta)}Ha(n);j=Mb(f,d,e)|0;if((j|0)==(f|0)){f=b;break a}f=c[m>>2]|0;if(((f-(c[e>>2]|0)|0)/24|0)>>>0<2){f=b;break a}Cb(ta,f+-24|0);f=c[m>>2]|0;g=f+-24|0;h=f;while(1){if((h|0)==(g|0))break;b=h+-24|0;c[m>>2]=b;Ia(b);h=c[m>>2]|0}b=a[ta>>0]|0;sa=(b&1)==0;Za(f+-48|0,sa?ta+1|0:c[ta+8>>2]|0,sa?(b&255)>>>1:c[ta+4>>2]|0)|0;Ja(ta);f=j;break a}}while(0);g=Rb(sa,d,e)|0;if((g|0)==(sa|0)|(g|0)==(d|0)){f=b;break a}if((a[g>>0]|0)!=73){f=b;break a}f=Mb(g,d,e)|0;if((f|0)==(g|0)){f=b;break a}k=e+4|0;g=c[k>>2]|0;if(((g-(c[e>>2]|0)|0)/24|0)>>>0<2){f=b;break a}Cb(ta,g+-24|0);g=c[k>>2]|0;h=g+-24|0;j=g;while(1){if((j|0)==(h|0))break;b=j+-24|0;c[k>>2]=b;Ia(b);j=c[k>>2]|0}b=a[ta>>0]|0;sa=(b&1)==0;Za(g+-48|0,sa?ta+1|0:c[ta+8>>2]|0,sa?(b&255)>>>1:c[ta+4>>2]|0)|0;Ja(ta);break a}}}else f=b;while(0);i=ua;return f|0}function Xb(b,d){b=b|0;d=d|0;var e=0,f=0;if((b|0)!=(d|0)){e=a[d>>0]|0;f=(e&1)==0;Ub(b,f?d+1|0:c[d+8>>2]|0,f?(e&255)>>>1:c[d+4>>2]|0)}return}function Yb(b,c){b=b|0;c=c|0;var d=0,e=0;a:do if((b|0)!=(c|0)){d=a[b>>0]|0;if(d<<24>>24!=95){if(((d<<24>>24)+-48|0)>>>0>=10)break;while(1){b=b+1|0;if((b|0)==(c|0)){b=c;break a}if(((a[b>>0]|0)+-48|0)>>>0>=10)break a}}d=b+1|0;if((d|0)!=(c|0)){d=a[d>>0]|0;if(((d<<24>>24)+-48|0)>>>0<10){b=b+2|0;break}if(d<<24>>24==95){e=b+2|0;while(1){if((e|0)==(c|0))break a;d=a[e>>0]|0;if(((d<<24>>24)+-48|0)>>>0>=10)break;e=e+1|0}return (d<<24>>24==95?e+1|0:b)|0}}}while(0);return b|0}function Zb(b,c){b=b|0;c=c|0;var d=0,e=0,f=0;a:do if((b|0)!=(c|0)){switch(a[b>>0]|0){case 104:{e=b+1|0;d=tb(e,c)|0;if((d|0)==(e|0)|(d|0)==(c|0))break a;return ((a[d>>0]|0)==95?d+1|0:b)|0}case 118:break;default:break a}f=b+1|0;d=tb(f,c)|0;if((!((d|0)==(f|0)|(d|0)==(c|0))?(a[d>>0]|0)==95:0)?(f=d+1|0,e=tb(f,c)|0,!((e|0)==(f|0)|(e|0)==(c|0))):0)b=(a[e>>0]|0)==95?e+1|0:b}while(0);return b|0}function _b(a){a=a|0;$b(a+32|0);Ga(a+16|0);Ha(a);return}function $b(a){a=a|0;var b=0,d=0,e=0;b=c[a>>2]|0;if(b){d=a+4|0;while(1){e=c[d>>2]|0;if((e|0)==(b|0))break;e=e+-16|0;c[d>>2]=e;Ga(e)}e=c[a>>2]|0;Ka(c[a+12>>2]|0,e,(c[a+8>>2]|0)-e|0)}return}function ac(b,c,d){b=b|0;c=c|0;d=d|0;var e=0,f=0;a:do if(!d)d=0;else{while(1){e=a[b>>0]|0;f=a[c>>0]|0;if(e<<24>>24!=f<<24>>24)break;d=d+-1|0;if(!d){d=0;break a}else{b=b+1|0;c=c+1|0}}d=(e&255)-(f&255)|0}while(0);return d|0}function bc(b){b=b|0;var d=0,e=0,f=0;f=b;a:do if(!(f&3))e=4;else{d=b;b=f;while(1){if(!(a[d>>0]|0))break a;d=d+1|0;b=d;if(!(b&3)){b=d;e=4;break}}}while(0);if((e|0)==4){while(1){d=c[b>>2]|0;if(!((d&-2139062144^-2139062144)&d+-16843009))b=b+4|0;else break}if((d&255)<<24>>24)do b=b+1|0;while((a[b>>0]|0)!=0)}return b-f|0}function cc(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,i=0;h=d&255;f=(e|0)!=0;a:do if(f&(b&3|0)!=0){g=d&255;while(1){if((a[b>>0]|0)==g<<24>>24)break a;b=b+1|0;e=e+-1|0;f=(e|0)!=0;if(!(f&(b&3|0)!=0)){i=5;break}}}else i=5;while(0);b:do if((i|0)==5)if(f){g=d&255;if((a[b>>0]|0)!=g<<24>>24){f=_(h,16843009)|0;c:do if(e>>>0>3)while(1){h=c[b>>2]^f;if((h&-2139062144^-2139062144)&h+-16843009)break;b=b+4|0;e=e+-4|0;if(e>>>0<=3){i=11;break c}}else i=11;while(0);if((i|0)==11)if(!e){e=0;break}while(1){if((a[b>>0]|0)==g<<24>>24)break b;b=b+1|0;e=e+-1|0;if(!e){e=0;break}}}}else e=0;while(0);return ((e|0)!=0?b:0)|0}function dc(b){b=b|0;var c=0,e=0;c=0;while(1){if((d[2370+c>>0]|0)==(b|0)){e=2;break}c=c+1|0;if((c|0)==87){c=87;b=2458;e=5;break}}if((e|0)==2)if(!c)c=2458;else{b=2458;e=5}if((e|0)==5)while(1){do{e=b;b=b+1|0}while((a[e>>0]|0)!=0);c=c+-1|0;if(!c){c=b;break}else e=5}return c|0}function ec(){var a=0;if(!(c[1200]|0))a=4844;else a=c[(fa()|0)+60>>2]|0;return a|0}function fc(a){a=a|0;if((a+-48|0)>>>0<10)a=1;else a=((a|32)+-97|0)>>>0<6;return a&1|0}function gc(a){a=a|0;return (a+-65|0)>>>0<26|0}function hc(b,d,e,f){b=b|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0,n=0;n=i;i=i+128|0;g=n+112|0;m=n;h=m;j=8;k=h+112|0;do{c[h>>2]=c[j>>2];h=h+4|0;j=j+4|0}while((h|0)<(k|0));if((d+-1|0)>>>0>2147483646)if(!d){d=1;l=4}else{c[(ec()|0)>>2]=75;d=-1}else{g=b;l=4}if((l|0)==4){l=-2-g|0;l=d>>>0>l>>>0?l:d;c[m+48>>2]=l;b=m+20|0;c[b>>2]=g;c[m+44>>2]=g;d=g+l|0;g=m+16|0;c[g>>2]=d;c[m+28>>2]=d;d=lc(m,e,f)|0;if(l){e=c[b>>2]|0;a[e+(((e|0)==(c[g>>2]|0))<<31>>31)>>0]=0}}i=n;return d|0}function ic(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,i=0;f=e+16|0;g=c[f>>2]|0;if(!g)if(!(kc(e)|0)){g=c[f>>2]|0;h=5}else f=0;else h=5;a:do if((h|0)==5){i=e+20|0;f=c[i>>2]|0;h=f;if((g-f|0)>>>0<d>>>0){f=ra[c[e+36>>2]&1](e,b,d)|0;break}b:do if((a[e+75>>0]|0)>-1){f=d;while(1){if(!f){g=h;f=0;break b}g=f+-1|0;if((a[b+g>>0]|0)==10)break;else f=g}if((ra[c[e+36>>2]&1](e,b,f)|0)>>>0<f>>>0)break a;d=d-f|0;b=b+f|0;g=c[i>>2]|0}else{g=h;f=0}while(0);Fc(g|0,b|0,d|0)|0;c[i>>2]=(c[i>>2]|0)+d;f=f+d|0}while(0);return f|0}function jc(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,g=0;f=i;i=i+16|0;g=f;c[g>>2]=e;e=hc(a,b,d,g)|0;i=f;return e|0}function kc(b){b=b|0;var d=0,e=0;d=b+74|0;e=a[d>>0]|0;a[d>>0]=e+255|e;d=c[b>>2]|0;if(!(d&8)){c[b+8>>2]=0;c[b+4>>2]=0;d=c[b+44>>2]|0;c[b+28>>2]=d;c[b+20>>2]=d;c[b+16>>2]=d+(c[b+48>>2]|0);d=0}else{c[b>>2]=d|32;d=-1}return d|0}function lc(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0;r=i;i=i+224|0;n=r+120|0;q=r+80|0;p=r;o=r+136|0;f=q;g=f+40|0;do{c[f>>2]=0;f=f+4|0}while((f|0)<(g|0));c[n>>2]=c[e>>2];if((rc(0,d,n,p,q)|0)<0)e=-1;else{e=c[b>>2]|0;m=e&32;if((a[b+74>>0]|0)<1)c[b>>2]=e&-33;l=b+48|0;if(!(c[l>>2]|0)){f=b+44|0;g=c[f>>2]|0;c[f>>2]=o;h=b+28|0;c[h>>2]=o;j=b+20|0;c[j>>2]=o;c[l>>2]=80;k=b+16|0;c[k>>2]=o+80;e=rc(b,d,n,p,q)|0;if(g){ra[c[b+36>>2]&1](b,0,0)|0;e=(c[j>>2]|0)==0?-1:e;c[f>>2]=g;c[l>>2]=0;c[k>>2]=0;c[h>>2]=0;c[j>>2]=0}}else e=rc(b,d,n,p,q)|0;q=c[b>>2]|0;c[b>>2]=q|m;e=(q&32|0)==0?e:-1}i=r;return e|0}function mc(b,d){b=b|0;d=d|0;do if(b){if(d>>>0<128){a[b>>0]=d;b=1;break}if(d>>>0<2048){a[b>>0]=d>>>6|192;a[b+1>>0]=d&63|128;b=2;break}if(d>>>0<55296|(d&-8192|0)==57344){a[b>>0]=d>>>12|224;a[b+1>>0]=d>>>6&63|128;a[b+2>>0]=d&63|128;b=3;break}if((d+-65536|0)>>>0<1048576){a[b>>0]=d>>>18|240;a[b+1>>0]=d>>>12&63|128;a[b+2>>0]=d>>>6&63|128;a[b+3>>0]=d&63|128;b=4;break}else{c[(ec()|0)>>2]=84;b=-1;break}}else b=1;while(0);return b|0}function nc(a,b){a=a|0;b=b|0;if(!a)a=0;else a=mc(a,b)|0;return a|0}function oc(a,b){a=+a;b=b|0;return +(+pc(a,b))}function pc(a,b){a=+a;b=b|0;var d=0,e=0,f=0;h[k>>3]=a;d=c[k>>2]|0;e=c[k+4>>2]|0;f=Dc(d|0,e|0,52)|0;f=f&2047;switch(f|0){case 0:{if(a!=0.0){a=+pc(a*18446744073709551616.0,b);d=(c[b>>2]|0)+-64|0}else d=0;c[b>>2]=d;break}case 2047:break;default:{c[b>>2]=f+-1022;c[k>>2]=d;c[k+4>>2]=e&-2146435073|1071644672;a=+h[k>>3]}}return +a}function qc(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0;e=a+20|0;f=c[e>>2]|0;a=(c[a+16>>2]|0)-f|0;a=a>>>0>d>>>0?d:a;Fc(f|0,b|0,a|0)|0;c[e>>2]=(c[e>>2]|0)+a;return d|0}function rc(e,f,g,j,l){e=e|0;f=f|0;g=g|0;j=j|0;l=l|0;var m=0,n=0,o=0,p=0.0,q=0,r=0,s=0,t=0,u=0,v=0,w=0.0,x=0,y=0,z=0,A=0,B=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,$=0,aa=0,ba=0,ca=0,da=0,ea=0,fa=0,ga=0,ha=0,ia=0;ia=i;i=i+624|0;da=ia+24|0;fa=ia+16|0;ea=ia+588|0;aa=ia+576|0;ca=ia;W=ia+536|0;ha=ia+8|0;ga=ia+528|0;M=(e|0)!=0;N=W+40|0;V=N;W=W+39|0;X=ha+4|0;Y=ea;Z=0-Y|0;$=aa+12|0;aa=aa+11|0;ba=$;O=ba-Y|0;P=-2-Y|0;Q=ba+2|0;R=da+288|0;S=ea+9|0;T=S;U=ea+8|0;m=0;n=0;r=0;x=f;a:while(1){do if((m|0)>-1)if((n|0)>(2147483647-m|0)){c[(ec()|0)>>2]=75;m=-1;break}else{m=n+m|0;break}while(0);f=a[x>>0]|0;if(!(f<<24>>24)){L=244;break}else n=x;b:while(1){switch(f<<24>>24){case 37:{f=n;L=9;break b}case 0:{f=n;break b}default:{}}K=n+1|0;f=a[K>>0]|0;n=K}c:do if((L|0)==9)while(1){L=0;if((a[f+1>>0]|0)!=37)break c;n=n+1|0;f=f+2|0;if((a[f>>0]|0)==37)L=9;else break}while(0);v=n-x|0;if(M?(c[e>>2]&32|0)==0:0)ic(x,v,e)|0;if((n|0)!=(x|0)){n=v;x=f;continue}q=f+1|0;n=a[q>>0]|0;o=(n<<24>>24)+-48|0;if(o>>>0<10){K=(a[f+2>>0]|0)==36;q=K?f+3|0:q;n=a[q>>0]|0;t=K?o:-1;r=K?1:r}else t=-1;f=n<<24>>24;d:do if((f&-32|0)==32){o=0;do{if(!(1<<f+-32&75913))break d;o=1<<(n<<24>>24)+-32|o;q=q+1|0;n=a[q>>0]|0;f=n<<24>>24}while((f&-32|0)==32)}else o=0;while(0);do if(n<<24>>24==42){n=q+1|0;f=(a[n>>0]|0)+-48|0;if(f>>>0<10?(a[q+2>>0]|0)==36:0){c[l+(f<<2)>>2]=10;f=1;q=q+3|0;n=c[j+((a[n>>0]|0)+-48<<3)>>2]|0}else{if(r){m=-1;break a}if(!M){u=o;K=0;q=n;J=0;break}f=(c[g>>2]|0)+(4-1)&~(4-1);K=c[f>>2]|0;c[g>>2]=f+4;f=0;q=n;n=K}if((n|0)<0){u=o|8192;K=f;J=0-n|0}else{u=o;K=f;J=n}}else{f=(n<<24>>24)+-48|0;if(f>>>0<10){n=0;do{n=(n*10|0)+f|0;q=q+1|0;f=(a[q>>0]|0)+-48|0}while(f>>>0<10);if((n|0)<0){m=-1;break a}else{u=o;K=r;J=n}}else{u=o;K=r;J=0}}while(0);e:do if((a[q>>0]|0)==46){f=q+1|0;n=a[f>>0]|0;if(n<<24>>24!=42){o=(n<<24>>24)+-48|0;if(o>>>0<10)n=0;else{r=0;break}while(1){n=(n*10|0)+o|0;f=f+1|0;o=(a[f>>0]|0)+-48|0;if(o>>>0>=10){r=n;break e}}}f=q+2|0;n=(a[f>>0]|0)+-48|0;if(n>>>0<10?(a[q+3>>0]|0)==36:0){c[l+(n<<2)>>2]=10;r=c[j+((a[f>>0]|0)+-48<<3)>>2]|0;f=q+4|0;break}if(K){m=-1;break a}if(M){I=(c[g>>2]|0)+(4-1)&~(4-1);r=c[I>>2]|0;c[g>>2]=I+4}else r=0}else{r=-1;f=q}while(0);s=0;while(1){n=(a[f>>0]|0)+-65|0;if(n>>>0>57){m=-1;break a}I=f+1|0;n=a[4266+(s*58|0)+n>>0]|0;o=n&255;if((o+-1|0)>>>0<8){f=I;s=o}else break}if(!(n<<24>>24)){m=-1;break}q=(t|0)>-1;do if(n<<24>>24==19)if(q){m=-1;break a}else L=52;else{if(q){c[l+(t<<2)>>2]=o;G=j+(t<<3)|0;H=c[G+4>>2]|0;L=ca;c[L>>2]=c[G>>2];c[L+4>>2]=H;L=52;break}if(!M){m=0;break a}sc(ca,o,g)}while(0);if((L|0)==52?(L=0,!M):0){n=v;r=K;x=I;continue}t=a[f>>0]|0;t=(s|0)!=0&(t&15|0)==3?t&-33:t;o=u&-65537;H=(u&8192|0)==0?u:o;f:do switch(t|0){case 110:switch(s|0){case 0:{c[c[ca>>2]>>2]=m;n=v;r=K;x=I;continue a}case 1:{c[c[ca>>2]>>2]=m;n=v;r=K;x=I;continue a}case 2:{n=c[ca>>2]|0;c[n>>2]=m;c[n+4>>2]=((m|0)<0)<<31>>31;n=v;r=K;x=I;continue a}case 3:{b[c[ca>>2]>>1]=m;n=v;r=K;x=I;continue a}case 4:{a[c[ca>>2]>>0]=m;n=v;r=K;x=I;continue a}case 6:{c[c[ca>>2]>>2]=m;n=v;r=K;x=I;continue a}case 7:{n=c[ca>>2]|0;c[n>>2]=m;c[n+4>>2]=((m|0)<0)<<31>>31;n=v;r=K;x=I;continue a}default:{n=v;r=K;x=I;continue a}}case 112:{s=H|8;r=r>>>0>8?r:8;t=120;L=64;break}case 88:case 120:{s=H;L=64;break}case 111:{o=ca;n=c[o>>2]|0;o=c[o+4>>2]|0;if((n|0)==0&(o|0)==0)f=N;else{f=N;do{f=f+-1|0;a[f>>0]=n&7|48;n=Dc(n|0,o|0,3)|0;o=C}while(!((n|0)==0&(o|0)==0))}if(!(H&8)){n=H;s=0;q=4746;L=77}else{s=V-f|0;n=H;r=(r|0)>(s|0)?r:s+1|0;s=0;q=4746;L=77}break}case 105:case 100:{n=ca;f=c[n>>2]|0;n=c[n+4>>2]|0;if((n|0)<0){f=Cc(0,0,f|0,n|0)|0;n=C;o=ca;c[o>>2]=f;c[o+4>>2]=n;o=1;q=4746;L=76;break f}if(!(H&2048)){q=H&1;o=q;q=(q|0)==0?4746:4748;L=76}else{o=1;q=4747;L=76}break}case 117:{n=ca;f=c[n>>2]|0;n=c[n+4>>2]|0;o=0;q=4746;L=76;break}case 99:{a[W>>0]=c[ca>>2];f=W;t=1;v=0;u=4746;n=N;break}case 109:{n=dc(c[(ec()|0)>>2]|0)|0;L=82;break}case 115:{n=c[ca>>2]|0;n=(n|0)!=0?n:4756;L=82;break}case 67:{c[ha>>2]=c[ca>>2];c[X>>2]=0;c[ca>>2]=ha;f=ha;r=-1;L=86;break}case 83:{f=c[ca>>2]|0;if(!r){uc(e,32,J,0,H);f=0;L=97}else L=86;break}case 65:case 71:case 70:case 69:case 97:case 103:case 102:case 101:{p=+h[ca>>3];c[fa>>2]=0;h[k>>3]=p;if((c[k+4>>2]|0)>=0)if(!(H&2048)){G=H&1;F=G;G=(G|0)==0?4764:4769}else{F=1;G=4766}else{p=-p;F=1;G=4763}h[k>>3]=p;E=c[k+4>>2]&2146435072;do if(E>>>0<2146435072|(E|0)==2146435072&0<0){w=+oc(p,fa)*2.0;n=w!=0.0;if(n)c[fa>>2]=(c[fa>>2]|0)+-1;B=t|32;if((B|0)==97){u=t&32;x=(u|0)==0?G:G+9|0;v=F|2;f=12-r|0;do if(!(r>>>0>11|(f|0)==0)){p=8.0;do{f=f+-1|0;p=p*16.0}while((f|0)!=0);if((a[x>>0]|0)==45){p=-(p+(-w-p));break}else{p=w+p-p;break}}else p=w;while(0);n=c[fa>>2]|0;f=(n|0)<0?0-n|0:n;f=tc(f,((f|0)<0)<<31>>31,$)|0;if((f|0)==($|0)){a[aa>>0]=48;f=aa}a[f+-1>>0]=(n>>31&2)+43;s=f+-2|0;a[s>>0]=t+15;q=(r|0)<1;o=(H&8|0)==0;n=ea;while(1){G=~~p;f=n+1|0;a[n>>0]=d[4730+G>>0]|u;p=(p-+(G|0))*16.0;do if((f-Y|0)==1){if(o&(q&p==0.0))break;a[f>>0]=46;f=n+2|0}while(0);if(!(p!=0.0))break;else n=f}o=s;r=(r|0)!=0&(P+f|0)<(r|0)?Q+r-o|0:O-o+f|0;q=r+v|0;uc(e,32,J,q,H);if(!(c[e>>2]&32))ic(x,v,e)|0;uc(e,48,J,q,H^65536);n=f-Y|0;if(!(c[e>>2]&32))ic(ea,n,e)|0;f=ba-o|0;uc(e,48,r-(n+f)|0,0,0);if(!(c[e>>2]&32))ic(s,f,e)|0;uc(e,32,J,q,H^8192);f=(q|0)<(J|0)?J:q;break}f=(r|0)<0?6:r;if(n){n=(c[fa>>2]|0)+-28|0;c[fa>>2]=n;p=w*268435456.0}else{p=w;n=c[fa>>2]|0}E=(n|0)<0?da:R;D=E;o=E;do{A=~~p>>>0;c[o>>2]=A;o=o+4|0;p=(p-+(A>>>0))*1.0e9}while(p!=0.0);n=c[fa>>2]|0;if((n|0)>0){q=E;r=o;while(1){s=(n|0)>29?29:n;n=r+-4|0;do if(n>>>0>=q>>>0){o=0;do{z=Ec(c[n>>2]|0,0,s|0)|0;z=Gc(z|0,C|0,o|0,0)|0;A=C;y=Pc(z|0,A|0,1e9,0)|0;c[n>>2]=y;o=Oc(z|0,A|0,1e9,0)|0;n=n+-4|0}while(n>>>0>=q>>>0);if(!o)break;q=q+-4|0;c[q>>2]=o}while(0);o=r;while(1){if(o>>>0<=q>>>0)break;n=o+-4|0;if(!(c[n>>2]|0))o=n;else break}n=(c[fa>>2]|0)-s|0;c[fa>>2]=n;if((n|0)>0)r=o;else break}}else q=E;if((n|0)<0){x=((f+25|0)/9|0)+1|0;y=(B|0)==102;do{v=0-n|0;v=(v|0)>9?9:v;do if(q>>>0<o>>>0){n=(1<<v)+-1|0;r=1e9>>>v;u=0;s=q;do{A=c[s>>2]|0;c[s>>2]=(A>>>v)+u;u=_(A&n,r)|0;s=s+4|0}while(s>>>0<o>>>0);n=(c[q>>2]|0)==0?q+4|0:q;if(!u){q=n;n=o;break}c[o>>2]=u;q=n;n=o+4|0}else{q=(c[q>>2]|0)==0?q+4|0:q;n=o}while(0);o=y?E:q;o=(n-o>>2|0)>(x|0)?o+(x<<2)|0:n;n=(c[fa>>2]|0)+v|0;c[fa>>2]=n}while((n|0)<0);x=q;y=o}else{x=q;y=o}do if(x>>>0<y>>>0){n=(D-x>>2)*9|0;q=c[x>>2]|0;if(q>>>0<10)break;else o=10;do{o=o*10|0;n=n+1|0}while(q>>>0>=o>>>0)}else n=0;while(0);z=(B|0)==103;A=(f|0)!=0;o=f-((B|0)!=102?n:0)+((A&z)<<31>>31)|0;if((o|0)<(((y-D>>2)*9|0)+-9|0)){r=o+9216|0;o=E+4+(((r|0)/9|0)+-1024<<2)|0;r=((r|0)%9|0)+1|0;if((r|0)<9){q=10;do{q=q*10|0;r=r+1|0}while((r|0)!=9)}else q=10;u=c[o>>2]|0;v=(u>>>0)%(q>>>0)|0;r=(o+4|0)==(y|0);do if(r&(v|0)==0)q=x;else{w=(((u>>>0)/(q>>>0)|0)&1|0)==0?9007199254740992.0:9007199254740994.0;s=(q|0)/2|0;if(v>>>0<s>>>0)p=.5;else p=r&(v|0)==(s|0)?1.0:1.5;do if(F){if((a[G>>0]|0)!=45)break;w=-w;p=-p}while(0);r=u-v|0;c[o>>2]=r;if(!(w+p!=w)){q=x;break}B=r+q|0;c[o>>2]=B;if(B>>>0>999999999){n=x;while(1){q=o+-4|0;c[o>>2]=0;if(q>>>0<n>>>0){n=n+-4|0;c[n>>2]=0}B=(c[q>>2]|0)+1|0;c[q>>2]=B;if(B>>>0>999999999)o=q;else{s=n;o=q;break}}}else s=x;n=(D-s>>2)*9|0;r=c[s>>2]|0;if(r>>>0<10){q=s;break}else q=10;do{q=q*10|0;n=n+1|0}while(r>>>0>=q>>>0);q=s}while(0);o=o+4|0;x=q;o=y>>>0>o>>>0?o:y}else o=y;v=0-n|0;B=o;while(1){if(B>>>0<=x>>>0){y=0;break}o=B+-4|0;if(!(c[o>>2]|0))B=o;else{y=1;break}}do if(z){f=(A&1^1)+f|0;if((f|0)>(n|0)&(n|0)>-5){t=t+-1|0;f=f+-1-n|0}else{t=t+-2|0;f=f+-1|0}o=H&8;if(o)break;do if(y){o=c[B+-4>>2]|0;if(!o){q=9;break}if(!((o>>>0)%10|0)){r=10;q=0}else{q=0;break}do{r=r*10|0;q=q+1|0}while(((o>>>0)%(r>>>0)|0|0)==0)}else q=9;while(0);o=((B-D>>2)*9|0)+-9|0;if((t|32|0)==102){o=o-q|0;o=(o|0)<0?0:o;f=(f|0)<(o|0)?f:o;o=0;break}else{o=o+n-q|0;o=(o|0)<0?0:o;f=(f|0)<(o|0)?f:o;o=0;break}}else o=H&8;while(0);u=f|o;r=(u|0)!=0&1;s=(t|32|0)==102;if(s){n=(n|0)>0?n:0;t=0}else{q=(n|0)<0?v:n;q=tc(q,((q|0)<0)<<31>>31,$)|0;if((ba-q|0)<2)do{q=q+-1|0;a[q>>0]=48}while((ba-q|0)<2);a[q+-1>>0]=(n>>31&2)+43;D=q+-2|0;a[D>>0]=t;n=ba-D|0;t=D}v=F+1+f+r+n|0;uc(e,32,J,v,H);if(!(c[e>>2]&32))ic(G,F,e)|0;uc(e,48,J,v,H^65536);do if(s){q=x>>>0>E>>>0?E:x;o=q;do{n=tc(c[o>>2]|0,0,S)|0;do if((o|0)==(q|0)){if((n|0)!=(S|0))break;a[U>>0]=48;n=U}else{if(n>>>0<=ea>>>0)break;Bc(ea|0,48,n-Y|0)|0;do n=n+-1|0;while(n>>>0>ea>>>0)}while(0);if(!(c[e>>2]&32))ic(n,T-n|0,e)|0;o=o+4|0}while(o>>>0<=E>>>0);do if(u){if(c[e>>2]&32)break;ic(4798,1,e)|0}while(0);if((f|0)>0&o>>>0<B>>>0)while(1){n=tc(c[o>>2]|0,0,S)|0;if(n>>>0>ea>>>0){Bc(ea|0,48,n-Y|0)|0;do n=n+-1|0;while(n>>>0>ea>>>0)}if(!(c[e>>2]&32))ic(n,(f|0)>9?9:f,e)|0;o=o+4|0;n=f+-9|0;if(!((f|0)>9&o>>>0<B>>>0)){f=n;break}else f=n}uc(e,48,f+9|0,9,0)}else{s=y?B:x+4|0;if((f|0)>-1){r=(o|0)==0;q=x;do{n=tc(c[q>>2]|0,0,S)|0;if((n|0)==(S|0)){a[U>>0]=48;n=U}do if((q|0)==(x|0)){o=n+1|0;if(!(c[e>>2]&32))ic(n,1,e)|0;if(r&(f|0)<1){n=o;break}if(c[e>>2]&32){n=o;break}ic(4798,1,e)|0;n=o}else{if(n>>>0<=ea>>>0)break;Bc(ea|0,48,n+Z|0)|0;do n=n+-1|0;while(n>>>0>ea>>>0)}while(0);o=T-n|0;if(!(c[e>>2]&32))ic(n,(f|0)>(o|0)?o:f,e)|0;f=f-o|0;q=q+4|0}while(q>>>0<s>>>0&(f|0)>-1)}uc(e,48,f+18|0,18,0);if(c[e>>2]&32)break;ic(t,ba-t|0,e)|0}while(0);uc(e,32,J,v,H^8192);f=(v|0)<(J|0)?J:v}else{s=(t&32|0)!=0;r=p!=p|0.0!=0.0;n=r?0:F;q=n+3|0;uc(e,32,J,q,o);f=c[e>>2]|0;if(!(f&32)){ic(G,n,e)|0;f=c[e>>2]|0}if(!(f&32))ic(r?(s?4790:4794):s?4782:4786,3,e)|0;uc(e,32,J,q,H^8192);f=(q|0)<(J|0)?J:q}while(0);n=f;r=K;x=I;continue a}default:{f=x;o=H;t=r;v=0;u=4746;n=N}}while(0);g:do if((L|0)==64){o=ca;n=c[o>>2]|0;o=c[o+4>>2]|0;q=t&32;if(!((n|0)==0&(o|0)==0)){f=N;do{f=f+-1|0;a[f>>0]=d[4730+(n&15)>>0]|q;n=Dc(n|0,o|0,4)|0;o=C}while(!((n|0)==0&(o|0)==0));L=ca;if((s&8|0)==0|(c[L>>2]|0)==0&(c[L+4>>2]|0)==0){n=s;s=0;q=4746;L=77}else{n=s;s=2;q=4746+(t>>4)|0;L=77}}else{f=N;n=s;s=0;q=4746;L=77}}else if((L|0)==76){f=tc(f,n,N)|0;n=H;s=o;L=77}else if((L|0)==82){L=0;H=cc(n,0,r)|0;G=(H|0)==0;f=n;t=G?r:H-n|0;v=0;u=4746;n=G?n+r|0:H}else if((L|0)==86){L=0;o=0;n=0;s=f;while(1){q=c[s>>2]|0;if(!q)break;n=nc(ga,q)|0;if((n|0)<0|n>>>0>(r-o|0)>>>0)break;o=n+o|0;if(r>>>0>o>>>0)s=s+4|0;else break}if((n|0)<0){m=-1;break a}uc(e,32,J,o,H);if(!o){f=0;L=97}else{q=0;while(1){n=c[f>>2]|0;if(!n){f=o;L=97;break g}n=nc(ga,n)|0;q=n+q|0;if((q|0)>(o|0)){f=o;L=97;break g}if(!(c[e>>2]&32))ic(ga,n,e)|0;if(q>>>0>=o>>>0){f=o;L=97;break}else f=f+4|0}}}while(0);if((L|0)==97){L=0;uc(e,32,J,f,H^8192);n=(J|0)>(f|0)?J:f;r=K;x=I;continue}if((L|0)==77){L=0;o=(r|0)>-1?n&-65537:n;n=ca;n=(c[n>>2]|0)!=0|(c[n+4>>2]|0)!=0;if((r|0)!=0|n){t=(n&1^1)+(V-f)|0;t=(r|0)>(t|0)?r:t;v=s;u=q;n=N}else{f=N;t=0;v=s;u=q;n=N}}s=n-f|0;q=(t|0)<(s|0)?s:t;r=v+q|0;n=(J|0)<(r|0)?r:J;uc(e,32,n,r,o);if(!(c[e>>2]&32))ic(u,v,e)|0;uc(e,48,n,r,o^65536);uc(e,48,q,s,0);if(!(c[e>>2]&32))ic(f,s,e)|0;uc(e,32,n,r,o^8192);r=K;x=I}h:do if((L|0)==244)if(!e)if(!r)m=0;else{m=1;while(1){f=c[l+(m<<2)>>2]|0;if(!f){f=0;break}sc(j+(m<<3)|0,f,g);m=m+1|0;if((m|0)>=10){m=1;break h}}while(1){m=m+1|0;if(f){m=-1;break h}if((m|0)>=10){m=1;break h}f=c[l+(m<<2)>>2]|0}}while(0);i=ia;return m|0}function sc(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0.0;a:do if(b>>>0<=20)do switch(b|0){case 9:{e=(c[d>>2]|0)+(4-1)&~(4-1);b=c[e>>2]|0;c[d>>2]=e+4;c[a>>2]=b;break a}case 10:{e=(c[d>>2]|0)+(4-1)&~(4-1);b=c[e>>2]|0;c[d>>2]=e+4;e=a;c[e>>2]=b;c[e+4>>2]=((b|0)<0)<<31>>31;break a}case 11:{e=(c[d>>2]|0)+(4-1)&~(4-1);b=c[e>>2]|0;c[d>>2]=e+4;e=a;c[e>>2]=b;c[e+4>>2]=0;break a}case 12:{e=(c[d>>2]|0)+(8-1)&~(8-1);b=e;f=c[b>>2]|0;b=c[b+4>>2]|0;c[d>>2]=e+8;e=a;c[e>>2]=f;c[e+4>>2]=b;break a}case 13:{f=(c[d>>2]|0)+(4-1)&~(4-1);e=c[f>>2]|0;c[d>>2]=f+4;e=(e&65535)<<16>>16;f=a;c[f>>2]=e;c[f+4>>2]=((e|0)<0)<<31>>31;break a}case 14:{f=(c[d>>2]|0)+(4-1)&~(4-1);e=c[f>>2]|0;c[d>>2]=f+4;f=a;c[f>>2]=e&65535;c[f+4>>2]=0;break a}case 15:{f=(c[d>>2]|0)+(4-1)&~(4-1);e=c[f>>2]|0;c[d>>2]=f+4;e=(e&255)<<24>>24;f=a;c[f>>2]=e;c[f+4>>2]=((e|0)<0)<<31>>31;break a}case 16:{f=(c[d>>2]|0)+(4-1)&~(4-1);e=c[f>>2]|0;c[d>>2]=f+4;f=a;c[f>>2]=e&255;c[f+4>>2]=0;break a}case 17:{f=(c[d>>2]|0)+(8-1)&~(8-1);g=+h[f>>3];c[d>>2]=f+8;h[a>>3]=g;break a}case 18:{f=(c[d>>2]|0)+(8-1)&~(8-1);g=+h[f>>3];c[d>>2]=f+8;h[a>>3]=g;break a}default:break a}while(0);while(0);return}function tc(b,c,d){b=b|0;c=c|0;d=d|0;var e=0;if(c>>>0>0|(c|0)==0&b>>>0>4294967295)while(1){e=Pc(b|0,c|0,10,0)|0;d=d+-1|0;a[d>>0]=e|48;e=b;b=Oc(b|0,c|0,10,0)|0;if(!(c>>>0>9|(c|0)==9&e>>>0>4294967295))break;else c=C}if(b)while(1){d=d+-1|0;a[d>>0]=(b>>>0)%10|0|48;if(b>>>0<10)break;else b=(b>>>0)/10|0}return d|0}function uc(a,b,d,e,f){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;var g=0,h=0;h=i;i=i+256|0;g=h;do if((d|0)>(e|0)&(f&73728|0)==0){f=d-e|0;Bc(g|0,b|0,(f>>>0>256?256:f)|0)|0;e=c[a>>2]|0;d=(e&32|0)==0;if(f>>>0>255){b=f;do{if(d){ic(g,256,a)|0;e=c[a>>2]|0}b=b+-256|0;d=(e&32|0)==0}while(b>>>0>255);if(d)f=f&255;else break}else if(!d)break;ic(g,f,a)|0}while(0);i=h;return}function vc(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0;do if(a>>>0<245){o=a>>>0<11?16:a+11&-8;a=o>>>3;j=c[1212]|0;b=j>>>a;if(b&3){b=(b&1^1)+a|0;d=4888+(b<<1<<2)|0;e=d+8|0;f=c[e>>2]|0;g=f+8|0;h=c[g>>2]|0;do if((d|0)!=(h|0)){if(h>>>0<(c[1216]|0)>>>0)ga();a=h+12|0;if((c[a>>2]|0)==(f|0)){c[a>>2]=d;c[e>>2]=h;break}else ga()}else c[1212]=j&~(1<<b);while(0);G=b<<3;c[f+4>>2]=G|3;G=f+G+4|0;c[G>>2]=c[G>>2]|1;G=g;return G|0}h=c[1214]|0;if(o>>>0>h>>>0){if(b){d=2<<a;d=b<<a&(d|0-d);d=(d&0-d)+-1|0;i=d>>>12&16;d=d>>>i;f=d>>>5&8;d=d>>>f;g=d>>>2&4;d=d>>>g;e=d>>>1&2;d=d>>>e;b=d>>>1&1;b=(f|i|g|e|b)+(d>>>b)|0;d=4888+(b<<1<<2)|0;e=d+8|0;g=c[e>>2]|0;i=g+8|0;f=c[i>>2]|0;do if((d|0)!=(f|0)){if(f>>>0<(c[1216]|0)>>>0)ga();a=f+12|0;if((c[a>>2]|0)==(g|0)){c[a>>2]=d;c[e>>2]=f;k=c[1214]|0;break}else ga()}else{c[1212]=j&~(1<<b);k=h}while(0);h=(b<<3)-o|0;c[g+4>>2]=o|3;e=g+o|0;c[e+4>>2]=h|1;c[e+h>>2]=h;if(k){f=c[1217]|0;b=k>>>3;d=4888+(b<<1<<2)|0;a=c[1212]|0;b=1<<b;if(a&b){a=d+8|0;b=c[a>>2]|0;if(b>>>0<(c[1216]|0)>>>0)ga();else{l=a;m=b}}else{c[1212]=a|b;l=d+8|0;m=d}c[l>>2]=f;c[m+12>>2]=f;c[f+8>>2]=m;c[f+12>>2]=d}c[1214]=h;c[1217]=e;G=i;return G|0}a=c[1213]|0;if(a){i=(a&0-a)+-1|0;F=i>>>12&16;i=i>>>F;E=i>>>5&8;i=i>>>E;G=i>>>2&4;i=i>>>G;b=i>>>1&2;i=i>>>b;j=i>>>1&1;j=c[5152+((E|F|G|b|j)+(i>>>j)<<2)>>2]|0;i=(c[j+4>>2]&-8)-o|0;b=j;while(1){a=c[b+16>>2]|0;if(!a){a=c[b+20>>2]|0;if(!a)break}b=(c[a+4>>2]&-8)-o|0;G=b>>>0<i>>>0;i=G?b:i;b=a;j=G?a:j}f=c[1216]|0;if(j>>>0<f>>>0)ga();h=j+o|0;if(j>>>0>=h>>>0)ga();g=c[j+24>>2]|0;d=c[j+12>>2]|0;do if((d|0)==(j|0)){b=j+20|0;a=c[b>>2]|0;if(!a){b=j+16|0;a=c[b>>2]|0;if(!a){n=0;break}}while(1){d=a+20|0;e=c[d>>2]|0;if(e){a=e;b=d;continue}d=a+16|0;e=c[d>>2]|0;if(!e)break;else{a=e;b=d}}if(b>>>0<f>>>0)ga();else{c[b>>2]=0;n=a;break}}else{e=c[j+8>>2]|0;if(e>>>0<f>>>0)ga();a=e+12|0;if((c[a>>2]|0)!=(j|0))ga();b=d+8|0;if((c[b>>2]|0)==(j|0)){c[a>>2]=d;c[b>>2]=e;n=d;break}else ga()}while(0);do if(g){a=c[j+28>>2]|0;b=5152+(a<<2)|0;if((j|0)==(c[b>>2]|0)){c[b>>2]=n;if(!n){c[1213]=c[1213]&~(1<<a);break}}else{if(g>>>0<(c[1216]|0)>>>0)ga();a=g+16|0;if((c[a>>2]|0)==(j|0))c[a>>2]=n;else c[g+20>>2]=n;if(!n)break}b=c[1216]|0;if(n>>>0<b>>>0)ga();c[n+24>>2]=g;a=c[j+16>>2]|0;do if(a)if(a>>>0<b>>>0)ga();else{c[n+16>>2]=a;c[a+24>>2]=n;break}while(0);a=c[j+20>>2]|0;if(a)if(a>>>0<(c[1216]|0)>>>0)ga();else{c[n+20>>2]=a;c[a+24>>2]=n;break}}while(0);if(i>>>0<16){G=i+o|0;c[j+4>>2]=G|3;G=j+G+4|0;c[G>>2]=c[G>>2]|1}else{c[j+4>>2]=o|3;c[h+4>>2]=i|1;c[h+i>>2]=i;a=c[1214]|0;if(a){e=c[1217]|0;b=a>>>3;d=4888+(b<<1<<2)|0;a=c[1212]|0;b=1<<b;if(a&b){a=d+8|0;b=c[a>>2]|0;if(b>>>0<(c[1216]|0)>>>0)ga();else{p=a;q=b}}else{c[1212]=a|b;p=d+8|0;q=d}c[p>>2]=e;c[q+12>>2]=e;c[e+8>>2]=q;c[e+12>>2]=d}c[1214]=i;c[1217]=h}G=j+8|0;return G|0}}}else if(a>>>0<=4294967231){a=a+11|0;o=a&-8;k=c[1213]|0;if(k){d=0-o|0;a=a>>>8;if(a)if(o>>>0>16777215)j=31;else{q=(a+1048320|0)>>>16&8;z=a<<q;p=(z+520192|0)>>>16&4;z=z<<p;j=(z+245760|0)>>>16&2;j=14-(p|q|j)+(z<<j>>>15)|0;j=o>>>(j+7|0)&1|j<<1}else j=0;b=c[5152+(j<<2)>>2]|0;a:do if(!b){a=0;b=0;z=86}else{f=d;a=0;h=o<<((j|0)==31?0:25-(j>>>1)|0);i=b;b=0;while(1){e=c[i+4>>2]&-8;d=e-o|0;if(d>>>0<f>>>0)if((e|0)==(o|0)){a=i;b=i;z=90;break a}else b=i;else d=f;e=c[i+20>>2]|0;i=c[i+16+(h>>>31<<2)>>2]|0;a=(e|0)==0|(e|0)==(i|0)?a:e;e=(i|0)==0;if(e){z=86;break}else{f=d;h=h<<(e&1^1)}}}while(0);if((z|0)==86){if((a|0)==0&(b|0)==0){a=2<<j;a=k&(a|0-a);if(!a)break;q=(a&0-a)+-1|0;m=q>>>12&16;q=q>>>m;l=q>>>5&8;q=q>>>l;n=q>>>2&4;q=q>>>n;p=q>>>1&2;q=q>>>p;a=q>>>1&1;a=c[5152+((l|m|n|p|a)+(q>>>a)<<2)>>2]|0}if(!a){i=d;j=b}else z=90}if((z|0)==90)while(1){z=0;q=(c[a+4>>2]&-8)-o|0;e=q>>>0<d>>>0;d=e?q:d;b=e?a:b;e=c[a+16>>2]|0;if(e){a=e;z=90;continue}a=c[a+20>>2]|0;if(!a){i=d;j=b;break}else z=90}if((j|0)!=0?i>>>0<((c[1214]|0)-o|0)>>>0:0){f=c[1216]|0;if(j>>>0<f>>>0)ga();h=j+o|0;if(j>>>0>=h>>>0)ga();g=c[j+24>>2]|0;d=c[j+12>>2]|0;do if((d|0)==(j|0)){b=j+20|0;a=c[b>>2]|0;if(!a){b=j+16|0;a=c[b>>2]|0;if(!a){s=0;break}}while(1){d=a+20|0;e=c[d>>2]|0;if(e){a=e;b=d;continue}d=a+16|0;e=c[d>>2]|0;if(!e)break;else{a=e;b=d}}if(b>>>0<f>>>0)ga();else{c[b>>2]=0;s=a;break}}else{e=c[j+8>>2]|0;if(e>>>0<f>>>0)ga();a=e+12|0;if((c[a>>2]|0)!=(j|0))ga();b=d+8|0;if((c[b>>2]|0)==(j|0)){c[a>>2]=d;c[b>>2]=e;s=d;break}else ga()}while(0);do if(g){a=c[j+28>>2]|0;b=5152+(a<<2)|0;if((j|0)==(c[b>>2]|0)){c[b>>2]=s;if(!s){c[1213]=c[1213]&~(1<<a);break}}else{if(g>>>0<(c[1216]|0)>>>0)ga();a=g+16|0;if((c[a>>2]|0)==(j|0))c[a>>2]=s;else c[g+20>>2]=s;if(!s)break}b=c[1216]|0;if(s>>>0<b>>>0)ga();c[s+24>>2]=g;a=c[j+16>>2]|0;do if(a)if(a>>>0<b>>>0)ga();else{c[s+16>>2]=a;c[a+24>>2]=s;break}while(0);a=c[j+20>>2]|0;if(a)if(a>>>0<(c[1216]|0)>>>0)ga();else{c[s+20>>2]=a;c[a+24>>2]=s;break}}while(0);do if(i>>>0>=16){c[j+4>>2]=o|3;c[h+4>>2]=i|1;c[h+i>>2]=i;a=i>>>3;if(i>>>0<256){d=4888+(a<<1<<2)|0;b=c[1212]|0;a=1<<a;if(b&a){a=d+8|0;b=c[a>>2]|0;if(b>>>0<(c[1216]|0)>>>0)ga();else{t=a;v=b}}else{c[1212]=b|a;t=d+8|0;v=d}c[t>>2]=h;c[v+12>>2]=h;c[h+8>>2]=v;c[h+12>>2]=d;break}a=i>>>8;if(a)if(i>>>0>16777215)d=31;else{F=(a+1048320|0)>>>16&8;G=a<<F;E=(G+520192|0)>>>16&4;G=G<<E;d=(G+245760|0)>>>16&2;d=14-(E|F|d)+(G<<d>>>15)|0;d=i>>>(d+7|0)&1|d<<1}else d=0;e=5152+(d<<2)|0;c[h+28>>2]=d;a=h+16|0;c[a+4>>2]=0;c[a>>2]=0;a=c[1213]|0;b=1<<d;if(!(a&b)){c[1213]=a|b;c[e>>2]=h;c[h+24>>2]=e;c[h+12>>2]=h;c[h+8>>2]=h;break}d=i<<((d|0)==31?0:25-(d>>>1)|0);e=c[e>>2]|0;while(1){if((c[e+4>>2]&-8|0)==(i|0)){z=148;break}b=e+16+(d>>>31<<2)|0;a=c[b>>2]|0;if(!a){z=145;break}else{d=d<<1;e=a}}if((z|0)==145)if(b>>>0<(c[1216]|0)>>>0)ga();else{c[b>>2]=h;c[h+24>>2]=e;c[h+12>>2]=h;c[h+8>>2]=h;break}else if((z|0)==148){a=e+8|0;b=c[a>>2]|0;G=c[1216]|0;if(b>>>0>=G>>>0&e>>>0>=G>>>0){c[b+12>>2]=h;c[a>>2]=h;c[h+8>>2]=b;c[h+12>>2]=e;c[h+24>>2]=0;break}else ga()}}else{G=i+o|0;c[j+4>>2]=G|3;G=j+G+4|0;c[G>>2]=c[G>>2]|1}while(0);G=j+8|0;return G|0}}}else o=-1;while(0);d=c[1214]|0;if(d>>>0>=o>>>0){a=d-o|0;b=c[1217]|0;if(a>>>0>15){G=b+o|0;c[1217]=G;c[1214]=a;c[G+4>>2]=a|1;c[G+a>>2]=a;c[b+4>>2]=o|3}else{c[1214]=0;c[1217]=0;c[b+4>>2]=d|3;G=b+d+4|0;c[G>>2]=c[G>>2]|1}G=b+8|0;return G|0}a=c[1215]|0;if(a>>>0>o>>>0){E=a-o|0;c[1215]=E;G=c[1218]|0;F=G+o|0;c[1218]=F;c[F+4>>2]=E|1;c[G+4>>2]=o|3;G=G+8|0;return G|0}do if(!(c[1330]|0)){a=ea(30)|0;if(!(a+-1&a)){c[1332]=a;c[1331]=a;c[1333]=-1;c[1334]=-1;c[1335]=0;c[1323]=0;c[1330]=(ja(0)|0)&-16^1431655768;break}else ga()}while(0);h=o+48|0;e=c[1332]|0;i=o+47|0;d=e+i|0;e=0-e|0;j=d&e;if(j>>>0<=o>>>0){G=0;return G|0}a=c[1322]|0;if((a|0)!=0?(t=c[1320]|0,v=t+j|0,v>>>0<=t>>>0|v>>>0>a>>>0):0){G=0;return G|0}b:do if(!(c[1323]&4)){b=c[1218]|0;c:do if(b){f=5296;while(1){a=c[f>>2]|0;if(a>>>0<=b>>>0?(r=f+4|0,(a+(c[r>>2]|0)|0)>>>0>b>>>0):0)break;a=c[f+8>>2]|0;if(!a){z=173;break c}else f=a}a=d-(c[1215]|0)&e;if(a>>>0<2147483647){b=ia(a|0)|0;if((b|0)==((c[f>>2]|0)+(c[r>>2]|0)|0)){if((b|0)!=(-1|0)){h=b;g=a;z=193;break b}}else z=183}}else z=173;while(0);do if((z|0)==173?(u=ia(0)|0,(u|0)!=(-1|0)):0){a=u;b=c[1331]|0;d=b+-1|0;if(!(d&a))a=j;else a=j-a+(d+a&0-b)|0;b=c[1320]|0;d=b+a|0;if(a>>>0>o>>>0&a>>>0<2147483647){v=c[1322]|0;if((v|0)!=0?d>>>0<=b>>>0|d>>>0>v>>>0:0)break;b=ia(a|0)|0;if((b|0)==(u|0)){h=u;g=a;z=193;break b}else z=183}}while(0);d:do if((z|0)==183){d=0-a|0;do if(h>>>0>a>>>0&(a>>>0<2147483647&(b|0)!=(-1|0))?(w=c[1332]|0,w=i-a+w&0-w,w>>>0<2147483647):0)if((ia(w|0)|0)==(-1|0)){ia(d|0)|0;break d}else{a=w+a|0;break}while(0);if((b|0)!=(-1|0)){h=b;g=a;z=193;break b}}while(0);c[1323]=c[1323]|4;z=190}else z=190;while(0);if((((z|0)==190?j>>>0<2147483647:0)?(x=ia(j|0)|0,y=ia(0)|0,x>>>0<y>>>0&((x|0)!=(-1|0)&(y|0)!=(-1|0))):0)?(g=y-x|0,g>>>0>(o+40|0)>>>0):0){h=x;z=193}if((z|0)==193){a=(c[1320]|0)+g|0;c[1320]=a;if(a>>>0>(c[1321]|0)>>>0)c[1321]=a;k=c[1218]|0;do if(k){f=5296;while(1){a=c[f>>2]|0;b=f+4|0;d=c[b>>2]|0;if((h|0)==(a+d|0)){z=203;break}e=c[f+8>>2]|0;if(!e)break;else f=e}if(((z|0)==203?(c[f+12>>2]&8|0)==0:0)?k>>>0<h>>>0&k>>>0>=a>>>0:0){c[b>>2]=d+g;G=k+8|0;G=(G&7|0)==0?0:0-G&7;F=k+G|0;G=g-G+(c[1215]|0)|0;c[1218]=F;c[1215]=G;c[F+4>>2]=G|1;c[F+G+4>>2]=40;c[1219]=c[1334];break}a=c[1216]|0;if(h>>>0<a>>>0){c[1216]=h;i=h}else i=a;b=h+g|0;a=5296;while(1){if((c[a>>2]|0)==(b|0)){z=211;break}a=c[a+8>>2]|0;if(!a){b=5296;break}}if((z|0)==211)if(!(c[a+12>>2]&8)){c[a>>2]=h;m=a+4|0;c[m>>2]=(c[m>>2]|0)+g;m=h+8|0;m=h+((m&7|0)==0?0:0-m&7)|0;a=b+8|0;a=b+((a&7|0)==0?0:0-a&7)|0;l=m+o|0;j=a-m-o|0;c[m+4>>2]=o|3;do if((a|0)!=(k|0)){if((a|0)==(c[1217]|0)){G=(c[1214]|0)+j|0;c[1214]=G;c[1217]=l;c[l+4>>2]=G|1;c[l+G>>2]=G;break}b=c[a+4>>2]|0;if((b&3|0)==1){h=b&-8;f=b>>>3;e:do if(b>>>0>=256){g=c[a+24>>2]|0;e=c[a+12>>2]|0;do if((e|0)==(a|0)){e=a+16|0;d=e+4|0;b=c[d>>2]|0;if(!b){b=c[e>>2]|0;if(!b){E=0;break}else d=e}while(1){e=b+20|0;f=c[e>>2]|0;if(f){b=f;d=e;continue}e=b+16|0;f=c[e>>2]|0;if(!f)break;else{b=f;d=e}}if(d>>>0<i>>>0)ga();else{c[d>>2]=0;E=b;break}}else{f=c[a+8>>2]|0;if(f>>>0<i>>>0)ga();b=f+12|0;if((c[b>>2]|0)!=(a|0))ga();d=e+8|0;if((c[d>>2]|0)==(a|0)){c[b>>2]=e;c[d>>2]=f;E=e;break}else ga()}while(0);if(!g)break;b=c[a+28>>2]|0;d=5152+(b<<2)|0;do if((a|0)!=(c[d>>2]|0)){if(g>>>0<(c[1216]|0)>>>0)ga();b=g+16|0;if((c[b>>2]|0)==(a|0))c[b>>2]=E;else c[g+20>>2]=E;if(!E)break e}else{c[d>>2]=E;if(E)break;c[1213]=c[1213]&~(1<<b);break e}while(0);e=c[1216]|0;if(E>>>0<e>>>0)ga();c[E+24>>2]=g;b=a+16|0;d=c[b>>2]|0;do if(d)if(d>>>0<e>>>0)ga();else{c[E+16>>2]=d;c[d+24>>2]=E;break}while(0);b=c[b+4>>2]|0;if(!b)break;if(b>>>0<(c[1216]|0)>>>0)ga();else{c[E+20>>2]=b;c[b+24>>2]=E;break}}else{d=c[a+8>>2]|0;e=c[a+12>>2]|0;b=4888+(f<<1<<2)|0;do if((d|0)!=(b|0)){if(d>>>0<i>>>0)ga();if((c[d+12>>2]|0)==(a|0))break;ga()}while(0);if((e|0)==(d|0)){c[1212]=c[1212]&~(1<<f);break}do if((e|0)==(b|0))B=e+8|0;else{if(e>>>0<i>>>0)ga();b=e+8|0;if((c[b>>2]|0)==(a|0)){B=b;break}ga()}while(0);c[d+12>>2]=e;c[B>>2]=d}while(0);a=a+h|0;f=h+j|0}else f=j;a=a+4|0;c[a>>2]=c[a>>2]&-2;c[l+4>>2]=f|1;c[l+f>>2]=f;a=f>>>3;if(f>>>0<256){d=4888+(a<<1<<2)|0;b=c[1212]|0;a=1<<a;do if(!(b&a)){c[1212]=b|a;F=d+8|0;G=d}else{a=d+8|0;b=c[a>>2]|0;if(b>>>0>=(c[1216]|0)>>>0){F=a;G=b;break}ga()}while(0);c[F>>2]=l;c[G+12>>2]=l;c[l+8>>2]=G;c[l+12>>2]=d;break}a=f>>>8;do if(!a)d=0;else{if(f>>>0>16777215){d=31;break}F=(a+1048320|0)>>>16&8;G=a<<F;E=(G+520192|0)>>>16&4;G=G<<E;d=(G+245760|0)>>>16&2;d=14-(E|F|d)+(G<<d>>>15)|0;d=f>>>(d+7|0)&1|d<<1}while(0);e=5152+(d<<2)|0;c[l+28>>2]=d;a=l+16|0;c[a+4>>2]=0;c[a>>2]=0;a=c[1213]|0;b=1<<d;if(!(a&b)){c[1213]=a|b;c[e>>2]=l;c[l+24>>2]=e;c[l+12>>2]=l;c[l+8>>2]=l;break}d=f<<((d|0)==31?0:25-(d>>>1)|0);e=c[e>>2]|0;while(1){if((c[e+4>>2]&-8|0)==(f|0)){z=281;break}b=e+16+(d>>>31<<2)|0;a=c[b>>2]|0;if(!a){z=278;break}else{d=d<<1;e=a}}if((z|0)==278)if(b>>>0<(c[1216]|0)>>>0)ga();else{c[b>>2]=l;c[l+24>>2]=e;c[l+12>>2]=l;c[l+8>>2]=l;break}else if((z|0)==281){a=e+8|0;b=c[a>>2]|0;G=c[1216]|0;if(b>>>0>=G>>>0&e>>>0>=G>>>0){c[b+12>>2]=l;c[a>>2]=l;c[l+8>>2]=b;c[l+12>>2]=e;c[l+24>>2]=0;break}else ga()}}else{G=(c[1215]|0)+j|0;c[1215]=G;c[1218]=l;c[l+4>>2]=G|1}while(0);G=m+8|0;return G|0}else b=5296;while(1){a=c[b>>2]|0;if(a>>>0<=k>>>0?(A=a+(c[b+4>>2]|0)|0,A>>>0>k>>>0):0)break;b=c[b+8>>2]|0}f=A+-47|0;b=f+8|0;b=f+((b&7|0)==0?0:0-b&7)|0;f=k+16|0;b=b>>>0<f>>>0?k:b;a=b+8|0;d=h+8|0;d=(d&7|0)==0?0:0-d&7;G=h+d|0;d=g+-40-d|0;c[1218]=G;c[1215]=d;c[G+4>>2]=d|1;c[G+d+4>>2]=40;c[1219]=c[1334];d=b+4|0;c[d>>2]=27;c[a>>2]=c[1324];c[a+4>>2]=c[1325];c[a+8>>2]=c[1326];c[a+12>>2]=c[1327];c[1324]=h;c[1325]=g;c[1327]=0;c[1326]=a;a=b+24|0;do{a=a+4|0;c[a>>2]=7}while((a+4|0)>>>0<A>>>0);if((b|0)!=(k|0)){g=b-k|0;c[d>>2]=c[d>>2]&-2;c[k+4>>2]=g|1;c[b>>2]=g;a=g>>>3;if(g>>>0<256){d=4888+(a<<1<<2)|0;b=c[1212]|0;a=1<<a;if(b&a){a=d+8|0;b=c[a>>2]|0;if(b>>>0<(c[1216]|0)>>>0)ga();else{C=a;D=b}}else{c[1212]=b|a;C=d+8|0;D=d}c[C>>2]=k;c[D+12>>2]=k;c[k+8>>2]=D;c[k+12>>2]=d;break}a=g>>>8;if(a)if(g>>>0>16777215)d=31;else{F=(a+1048320|0)>>>16&8;G=a<<F;E=(G+520192|0)>>>16&4;G=G<<E;d=(G+245760|0)>>>16&2;d=14-(E|F|d)+(G<<d>>>15)|0;d=g>>>(d+7|0)&1|d<<1}else d=0;e=5152+(d<<2)|0;c[k+28>>2]=d;c[k+20>>2]=0;c[f>>2]=0;a=c[1213]|0;b=1<<d;if(!(a&b)){c[1213]=a|b;c[e>>2]=k;c[k+24>>2]=e;c[k+12>>2]=k;c[k+8>>2]=k;break}d=g<<((d|0)==31?0:25-(d>>>1)|0);e=c[e>>2]|0;while(1){if((c[e+4>>2]&-8|0)==(g|0)){z=307;break}b=e+16+(d>>>31<<2)|0;a=c[b>>2]|0;if(!a){z=304;break}else{d=d<<1;e=a}}if((z|0)==304)if(b>>>0<(c[1216]|0)>>>0)ga();else{c[b>>2]=k;c[k+24>>2]=e;c[k+12>>2]=k;c[k+8>>2]=k;break}else if((z|0)==307){a=e+8|0;b=c[a>>2]|0;G=c[1216]|0;if(b>>>0>=G>>>0&e>>>0>=G>>>0){c[b+12>>2]=k;c[a>>2]=k;c[k+8>>2]=b;c[k+12>>2]=e;c[k+24>>2]=0;break}else ga()}}}else{G=c[1216]|0;if((G|0)==0|h>>>0<G>>>0)c[1216]=h;c[1324]=h;c[1325]=g;c[1327]=0;c[1221]=c[1330];c[1220]=-1;a=0;do{G=4888+(a<<1<<2)|0;c[G+12>>2]=G;c[G+8>>2]=G;a=a+1|0}while((a|0)!=32);G=h+8|0;G=(G&7|0)==0?0:0-G&7;F=h+G|0;G=g+-40-G|0;c[1218]=F;c[1215]=G;c[F+4>>2]=G|1;c[F+G+4>>2]=40;c[1219]=c[1334]}while(0);a=c[1215]|0;if(a>>>0>o>>>0){E=a-o|0;c[1215]=E;G=c[1218]|0;F=G+o|0;c[1218]=F;c[F+4>>2]=E|1;c[G+4>>2]=o|3;G=G+8|0;return G|0}}c[(ec()|0)>>2]=12;G=0;return G|0}function wc(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0;if(!a)return;d=a+-8|0;h=c[1216]|0;if(d>>>0<h>>>0)ga();a=c[a+-4>>2]|0;b=a&3;if((b|0)==1)ga();e=a&-8;m=d+e|0;do if(!(a&1)){a=c[d>>2]|0;if(!b)return;k=d+(0-a)|0;j=a+e|0;if(k>>>0<h>>>0)ga();if((k|0)==(c[1217]|0)){a=m+4|0;b=c[a>>2]|0;if((b&3|0)!=3){q=k;f=j;break}c[1214]=j;c[a>>2]=b&-2;c[k+4>>2]=j|1;c[k+j>>2]=j;return}e=a>>>3;if(a>>>0<256){b=c[k+8>>2]|0;d=c[k+12>>2]|0;a=4888+(e<<1<<2)|0;if((b|0)!=(a|0)){if(b>>>0<h>>>0)ga();if((c[b+12>>2]|0)!=(k|0))ga()}if((d|0)==(b|0)){c[1212]=c[1212]&~(1<<e);q=k;f=j;break}if((d|0)!=(a|0)){if(d>>>0<h>>>0)ga();a=d+8|0;if((c[a>>2]|0)==(k|0))g=a;else ga()}else g=d+8|0;c[b+12>>2]=d;c[g>>2]=b;q=k;f=j;break}g=c[k+24>>2]|0;d=c[k+12>>2]|0;do if((d|0)==(k|0)){d=k+16|0;b=d+4|0;a=c[b>>2]|0;if(!a){a=c[d>>2]|0;if(!a){i=0;break}else b=d}while(1){d=a+20|0;e=c[d>>2]|0;if(e){a=e;b=d;continue}d=a+16|0;e=c[d>>2]|0;if(!e)break;else{a=e;b=d}}if(b>>>0<h>>>0)ga();else{c[b>>2]=0;i=a;break}}else{e=c[k+8>>2]|0;if(e>>>0<h>>>0)ga();a=e+12|0;if((c[a>>2]|0)!=(k|0))ga();b=d+8|0;if((c[b>>2]|0)==(k|0)){c[a>>2]=d;c[b>>2]=e;i=d;break}else ga()}while(0);if(g){a=c[k+28>>2]|0;b=5152+(a<<2)|0;if((k|0)==(c[b>>2]|0)){c[b>>2]=i;if(!i){c[1213]=c[1213]&~(1<<a);q=k;f=j;break}}else{if(g>>>0<(c[1216]|0)>>>0)ga();a=g+16|0;if((c[a>>2]|0)==(k|0))c[a>>2]=i;else c[g+20>>2]=i;if(!i){q=k;f=j;break}}d=c[1216]|0;if(i>>>0<d>>>0)ga();c[i+24>>2]=g;a=k+16|0;b=c[a>>2]|0;do if(b)if(b>>>0<d>>>0)ga();else{c[i+16>>2]=b;c[b+24>>2]=i;break}while(0);a=c[a+4>>2]|0;if(a)if(a>>>0<(c[1216]|0)>>>0)ga();else{c[i+20>>2]=a;c[a+24>>2]=i;q=k;f=j;break}else{q=k;f=j}}else{q=k;f=j}}else{q=d;f=e}while(0);if(q>>>0>=m>>>0)ga();a=m+4|0;b=c[a>>2]|0;if(!(b&1))ga();if(!(b&2)){if((m|0)==(c[1218]|0)){p=(c[1215]|0)+f|0;c[1215]=p;c[1218]=q;c[q+4>>2]=p|1;if((q|0)!=(c[1217]|0))return;c[1217]=0;c[1214]=0;return}if((m|0)==(c[1217]|0)){p=(c[1214]|0)+f|0;c[1214]=p;c[1217]=q;c[q+4>>2]=p|1;c[q+p>>2]=p;return}f=(b&-8)+f|0;e=b>>>3;do if(b>>>0>=256){g=c[m+24>>2]|0;a=c[m+12>>2]|0;do if((a|0)==(m|0)){d=m+16|0;b=d+4|0;a=c[b>>2]|0;if(!a){a=c[d>>2]|0;if(!a){n=0;break}else b=d}while(1){d=a+20|0;e=c[d>>2]|0;if(e){a=e;b=d;continue}d=a+16|0;e=c[d>>2]|0;if(!e)break;else{a=e;b=d}}if(b>>>0<(c[1216]|0)>>>0)ga();else{c[b>>2]=0;n=a;break}}else{b=c[m+8>>2]|0;if(b>>>0<(c[1216]|0)>>>0)ga();d=b+12|0;if((c[d>>2]|0)!=(m|0))ga();e=a+8|0;if((c[e>>2]|0)==(m|0)){c[d>>2]=a;c[e>>2]=b;n=a;break}else ga()}while(0);if(g){a=c[m+28>>2]|0;b=5152+(a<<2)|0;if((m|0)==(c[b>>2]|0)){c[b>>2]=n;if(!n){c[1213]=c[1213]&~(1<<a);break}}else{if(g>>>0<(c[1216]|0)>>>0)ga();a=g+16|0;if((c[a>>2]|0)==(m|0))c[a>>2]=n;else c[g+20>>2]=n;if(!n)break}d=c[1216]|0;if(n>>>0<d>>>0)ga();c[n+24>>2]=g;a=m+16|0;b=c[a>>2]|0;do if(b)if(b>>>0<d>>>0)ga();else{c[n+16>>2]=b;c[b+24>>2]=n;break}while(0);a=c[a+4>>2]|0;if(a)if(a>>>0<(c[1216]|0)>>>0)ga();else{c[n+20>>2]=a;c[a+24>>2]=n;break}}}else{b=c[m+8>>2]|0;d=c[m+12>>2]|0;a=4888+(e<<1<<2)|0;if((b|0)!=(a|0)){if(b>>>0<(c[1216]|0)>>>0)ga();if((c[b+12>>2]|0)!=(m|0))ga()}if((d|0)==(b|0)){c[1212]=c[1212]&~(1<<e);break}if((d|0)!=(a|0)){if(d>>>0<(c[1216]|0)>>>0)ga();a=d+8|0;if((c[a>>2]|0)==(m|0))l=a;else ga()}else l=d+8|0;c[b+12>>2]=d;c[l>>2]=b}while(0);c[q+4>>2]=f|1;c[q+f>>2]=f;if((q|0)==(c[1217]|0)){c[1214]=f;return}}else{c[a>>2]=b&-2;c[q+4>>2]=f|1;c[q+f>>2]=f}a=f>>>3;if(f>>>0<256){d=4888+(a<<1<<2)|0;b=c[1212]|0;a=1<<a;if(b&a){a=d+8|0;b=c[a>>2]|0;if(b>>>0<(c[1216]|0)>>>0)ga();else{o=a;p=b}}else{c[1212]=b|a;o=d+8|0;p=d}c[o>>2]=q;c[p+12>>2]=q;c[q+8>>2]=p;c[q+12>>2]=d;return}a=f>>>8;if(a)if(f>>>0>16777215)d=31;else{o=(a+1048320|0)>>>16&8;p=a<<o;n=(p+520192|0)>>>16&4;p=p<<n;d=(p+245760|0)>>>16&2;d=14-(n|o|d)+(p<<d>>>15)|0;d=f>>>(d+7|0)&1|d<<1}else d=0;e=5152+(d<<2)|0;c[q+28>>2]=d;c[q+20>>2]=0;c[q+16>>2]=0;a=c[1213]|0;b=1<<d;do if(a&b){d=f<<((d|0)==31?0:25-(d>>>1)|0);e=c[e>>2]|0;while(1){if((c[e+4>>2]&-8|0)==(f|0)){a=130;break}b=e+16+(d>>>31<<2)|0;a=c[b>>2]|0;if(!a){a=127;break}else{d=d<<1;e=a}}if((a|0)==127)if(b>>>0<(c[1216]|0)>>>0)ga();else{c[b>>2]=q;c[q+24>>2]=e;c[q+12>>2]=q;c[q+8>>2]=q;break}else if((a|0)==130){a=e+8|0;b=c[a>>2]|0;p=c[1216]|0;if(b>>>0>=p>>>0&e>>>0>=p>>>0){c[b+12>>2]=q;c[a>>2]=q;c[q+8>>2]=b;c[q+12>>2]=e;c[q+24>>2]=0;break}else ga()}}else{c[1213]=a|b;c[e>>2]=q;c[q+24>>2]=e;c[q+12>>2]=q;c[q+8>>2]=q}while(0);q=(c[1220]|0)+-1|0;c[1220]=q;if(!q)a=5304;else return;while(1){a=c[a>>2]|0;if(!a)break;else a=a+8|0}c[1220]=-1;return}function xc(a,b){a=a|0;b=b|0;var d=0,e=0;if(!a){a=vc(b)|0;return a|0}if(b>>>0>4294967231){c[(ec()|0)>>2]=12;a=0;return a|0}d=yc(a+-8|0,b>>>0<11?16:b+11&-8)|0;if(d){a=d+8|0;return a|0}d=vc(b)|0;if(!d){a=0;return a|0}e=c[a+-4>>2]|0;e=(e&-8)-((e&3|0)==0?8:4)|0;Fc(d|0,a|0,(e>>>0<b>>>0?e:b)|0)|0;wc(a);a=d;return a|0}function yc(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0;n=a+4|0;o=c[n>>2]|0;d=o&-8;k=a+d|0;i=c[1216]|0;e=o&3;if(!((e|0)!=1&a>>>0>=i>>>0&a>>>0<k>>>0))ga();f=c[k+4>>2]|0;if(!(f&1))ga();if(!e){if(b>>>0<256){a=0;return a|0}if(d>>>0>=(b+4|0)>>>0?(d-b|0)>>>0<=c[1332]<<1>>>0:0)return a|0;a=0;return a|0}if(d>>>0>=b>>>0){d=d-b|0;if(d>>>0<=15)return a|0;m=a+b|0;c[n>>2]=o&1|b|2;c[m+4>>2]=d|3;b=m+d+4|0;c[b>>2]=c[b>>2]|1;zc(m,d);return a|0}if((k|0)==(c[1218]|0)){d=(c[1215]|0)+d|0;if(d>>>0<=b>>>0){a=0;return a|0}m=d-b|0;l=a+b|0;c[n>>2]=o&1|b|2;c[l+4>>2]=m|1;c[1218]=l;c[1215]=m;return a|0}if((k|0)==(c[1217]|0)){e=(c[1214]|0)+d|0;if(e>>>0<b>>>0){a=0;return a|0}d=e-b|0;if(d>>>0>15){e=a+b|0;m=e+d|0;c[n>>2]=o&1|b|2;c[e+4>>2]=d|1;c[m>>2]=d;b=m+4|0;c[b>>2]=c[b>>2]&-2}else{c[n>>2]=o&1|e|2;e=a+e+4|0;c[e>>2]=c[e>>2]|1;e=0;d=0}c[1214]=d;c[1217]=e;return a|0}if(f&2){a=0;return a|0}l=(f&-8)+d|0;if(l>>>0<b>>>0){a=0;return a|0}m=l-b|0;g=f>>>3;do if(f>>>0>=256){h=c[k+24>>2]|0;f=c[k+12>>2]|0;do if((f|0)==(k|0)){f=k+16|0;e=f+4|0;d=c[e>>2]|0;if(!d){d=c[f>>2]|0;if(!d){j=0;break}else e=f}while(1){f=d+20|0;g=c[f>>2]|0;if(g){d=g;e=f;continue}f=d+16|0;g=c[f>>2]|0;if(!g)break;else{d=g;e=f}}if(e>>>0<i>>>0)ga();else{c[e>>2]=0;j=d;break}}else{g=c[k+8>>2]|0;if(g>>>0<i>>>0)ga();d=g+12|0;if((c[d>>2]|0)!=(k|0))ga();e=f+8|0;if((c[e>>2]|0)==(k|0)){c[d>>2]=f;c[e>>2]=g;j=f;break}else ga()}while(0);if(h){d=c[k+28>>2]|0;e=5152+(d<<2)|0;if((k|0)==(c[e>>2]|0)){c[e>>2]=j;if(!j){c[1213]=c[1213]&~(1<<d);break}}else{if(h>>>0<(c[1216]|0)>>>0)ga();d=h+16|0;if((c[d>>2]|0)==(k|0))c[d>>2]=j;else c[h+20>>2]=j;if(!j)break}f=c[1216]|0;if(j>>>0<f>>>0)ga();c[j+24>>2]=h;d=k+16|0;e=c[d>>2]|0;do if(e)if(e>>>0<f>>>0)ga();else{c[j+16>>2]=e;c[e+24>>2]=j;break}while(0);d=c[d+4>>2]|0;if(d)if(d>>>0<(c[1216]|0)>>>0)ga();else{c[j+20>>2]=d;c[d+24>>2]=j;break}}}else{e=c[k+8>>2]|0;f=c[k+12>>2]|0;d=4888+(g<<1<<2)|0;if((e|0)!=(d|0)){if(e>>>0<i>>>0)ga();if((c[e+12>>2]|0)!=(k|0))ga()}if((f|0)==(e|0)){c[1212]=c[1212]&~(1<<g);break}if((f|0)!=(d|0)){if(f>>>0<i>>>0)ga();d=f+8|0;if((c[d>>2]|0)==(k|0))h=d;else ga()}else h=f+8|0;c[e+12>>2]=f;c[h>>2]=e}while(0);if(m>>>0<16){c[n>>2]=l|o&1|2;b=a+l+4|0;c[b>>2]=c[b>>2]|1;return a|0}else{l=a+b|0;c[n>>2]=o&1|b|2;c[l+4>>2]=m|3;b=l+m+4|0;c[b>>2]=c[b>>2]|1;zc(l,m);return a|0}return 0}function zc(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0;o=a+b|0;d=c[a+4>>2]|0;do if(!(d&1)){g=c[a>>2]|0;if(!(d&3))return;l=a+(0-g)|0;k=g+b|0;i=c[1216]|0;if(l>>>0<i>>>0)ga();if((l|0)==(c[1217]|0)){a=o+4|0;d=c[a>>2]|0;if((d&3|0)!=3){r=l;f=k;break}c[1214]=k;c[a>>2]=d&-2;c[l+4>>2]=k|1;c[l+k>>2]=k;return}e=g>>>3;if(g>>>0<256){a=c[l+8>>2]|0;b=c[l+12>>2]|0;d=4888+(e<<1<<2)|0;if((a|0)!=(d|0)){if(a>>>0<i>>>0)ga();if((c[a+12>>2]|0)!=(l|0))ga()}if((b|0)==(a|0)){c[1212]=c[1212]&~(1<<e);r=l;f=k;break}if((b|0)!=(d|0)){if(b>>>0<i>>>0)ga();d=b+8|0;if((c[d>>2]|0)==(l|0))h=d;else ga()}else h=b+8|0;c[a+12>>2]=b;c[h>>2]=a;r=l;f=k;break}g=c[l+24>>2]|0;b=c[l+12>>2]|0;do if((b|0)==(l|0)){b=l+16|0;a=b+4|0;d=c[a>>2]|0;if(!d){d=c[b>>2]|0;if(!d){j=0;break}else a=b}while(1){b=d+20|0;e=c[b>>2]|0;if(e){d=e;a=b;continue}b=d+16|0;e=c[b>>2]|0;if(!e)break;else{d=e;a=b}}if(a>>>0<i>>>0)ga();else{c[a>>2]=0;j=d;break}}else{e=c[l+8>>2]|0;if(e>>>0<i>>>0)ga();d=e+12|0;if((c[d>>2]|0)!=(l|0))ga();a=b+8|0;if((c[a>>2]|0)==(l|0)){c[d>>2]=b;c[a>>2]=e;j=b;break}else ga()}while(0);if(g){d=c[l+28>>2]|0;a=5152+(d<<2)|0;if((l|0)==(c[a>>2]|0)){c[a>>2]=j;if(!j){c[1213]=c[1213]&~(1<<d);r=l;f=k;break}}else{if(g>>>0<(c[1216]|0)>>>0)ga();d=g+16|0;if((c[d>>2]|0)==(l|0))c[d>>2]=j;else c[g+20>>2]=j;if(!j){r=l;f=k;break}}b=c[1216]|0;if(j>>>0<b>>>0)ga();c[j+24>>2]=g;d=l+16|0;a=c[d>>2]|0;do if(a)if(a>>>0<b>>>0)ga();else{c[j+16>>2]=a;c[a+24>>2]=j;break}while(0);d=c[d+4>>2]|0;if(d)if(d>>>0<(c[1216]|0)>>>0)ga();else{c[j+20>>2]=d;c[d+24>>2]=j;r=l;f=k;break}else{r=l;f=k}}else{r=l;f=k}}else{r=a;f=b}while(0);h=c[1216]|0;if(o>>>0<h>>>0)ga();d=o+4|0;a=c[d>>2]|0;if(!(a&2)){if((o|0)==(c[1218]|0)){q=(c[1215]|0)+f|0;c[1215]=q;c[1218]=r;c[r+4>>2]=q|1;if((r|0)!=(c[1217]|0))return;c[1217]=0;c[1214]=0;return}if((o|0)==(c[1217]|0)){q=(c[1214]|0)+f|0;c[1214]=q;c[1217]=r;c[r+4>>2]=q|1;c[r+q>>2]=q;return}f=(a&-8)+f|0;e=a>>>3;do if(a>>>0>=256){g=c[o+24>>2]|0;b=c[o+12>>2]|0;do if((b|0)==(o|0)){b=o+16|0;a=b+4|0;d=c[a>>2]|0;if(!d){d=c[b>>2]|0;if(!d){n=0;break}else a=b}while(1){b=d+20|0;e=c[b>>2]|0;if(e){d=e;a=b;continue}b=d+16|0;e=c[b>>2]|0;if(!e)break;else{d=e;a=b}}if(a>>>0<h>>>0)ga();else{c[a>>2]=0;n=d;break}}else{e=c[o+8>>2]|0;if(e>>>0<h>>>0)ga();d=e+12|0;if((c[d>>2]|0)!=(o|0))ga();a=b+8|0;if((c[a>>2]|0)==(o|0)){c[d>>2]=b;c[a>>2]=e;n=b;break}else ga()}while(0);if(g){d=c[o+28>>2]|0;a=5152+(d<<2)|0;if((o|0)==(c[a>>2]|0)){c[a>>2]=n;if(!n){c[1213]=c[1213]&~(1<<d);break}}else{if(g>>>0<(c[1216]|0)>>>0)ga();d=g+16|0;if((c[d>>2]|0)==(o|0))c[d>>2]=n;else c[g+20>>2]=n;if(!n)break}b=c[1216]|0;if(n>>>0<b>>>0)ga();c[n+24>>2]=g;d=o+16|0;a=c[d>>2]|0;do if(a)if(a>>>0<b>>>0)ga();else{c[n+16>>2]=a;c[a+24>>2]=n;break}while(0);d=c[d+4>>2]|0;if(d)if(d>>>0<(c[1216]|0)>>>0)ga();else{c[n+20>>2]=d;c[d+24>>2]=n;break}}}else{a=c[o+8>>2]|0;b=c[o+12>>2]|0;d=4888+(e<<1<<2)|0;if((a|0)!=(d|0)){if(a>>>0<h>>>0)ga();if((c[a+12>>2]|0)!=(o|0))ga()}if((b|0)==(a|0)){c[1212]=c[1212]&~(1<<e);break}if((b|0)!=(d|0)){if(b>>>0<h>>>0)ga();d=b+8|0;if((c[d>>2]|0)==(o|0))m=d;else ga()}else m=b+8|0;c[a+12>>2]=b;c[m>>2]=a}while(0);c[r+4>>2]=f|1;c[r+f>>2]=f;if((r|0)==(c[1217]|0)){c[1214]=f;return}}else{c[d>>2]=a&-2;c[r+4>>2]=f|1;c[r+f>>2]=f}d=f>>>3;if(f>>>0<256){b=4888+(d<<1<<2)|0;a=c[1212]|0;d=1<<d;if(a&d){d=b+8|0;a=c[d>>2]|0;if(a>>>0<(c[1216]|0)>>>0)ga();else{p=d;q=a}}else{c[1212]=a|d;p=b+8|0;q=b}c[p>>2]=r;c[q+12>>2]=r;c[r+8>>2]=q;c[r+12>>2]=b;return}d=f>>>8;if(d)if(f>>>0>16777215)b=31;else{p=(d+1048320|0)>>>16&8;q=d<<p;o=(q+520192|0)>>>16&4;q=q<<o;b=(q+245760|0)>>>16&2;b=14-(o|p|b)+(q<<b>>>15)|0;b=f>>>(b+7|0)&1|b<<1}else b=0;e=5152+(b<<2)|0;c[r+28>>2]=b;c[r+20>>2]=0;c[r+16>>2]=0;d=c[1213]|0;a=1<<b;if(!(d&a)){c[1213]=d|a;c[e>>2]=r;c[r+24>>2]=e;c[r+12>>2]=r;c[r+8>>2]=r;return}b=f<<((b|0)==31?0:25-(b>>>1)|0);e=c[e>>2]|0;while(1){if((c[e+4>>2]&-8|0)==(f|0)){d=127;break}a=e+16+(b>>>31<<2)|0;d=c[a>>2]|0;if(!d){d=124;break}else{b=b<<1;e=d}}if((d|0)==124){if(a>>>0<(c[1216]|0)>>>0)ga();c[a>>2]=r;c[r+24>>2]=e;c[r+12>>2]=r;c[r+8>>2]=r;return}else if((d|0)==127){d=e+8|0;a=c[d>>2]|0;q=c[1216]|0;if(!(a>>>0>=q>>>0&e>>>0>=q>>>0))ga();c[a+12>>2]=r;c[d>>2]=r;c[r+8>>2]=a;c[r+12>>2]=e;c[r+24>>2]=0;return}}function Ac(){}function Bc(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,i=0;f=b+e|0;if((e|0)>=20){d=d&255;h=b&3;i=d|d<<8|d<<16|d<<24;g=f&~3;if(h){h=b+4-h|0;while((b|0)<(h|0)){a[b>>0]=d;b=b+1|0}}while((b|0)<(g|0)){c[b>>2]=i;b=b+4|0}}while((b|0)<(f|0)){a[b>>0]=d;b=b+1|0}return b-e|0}function Cc(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;d=b-d-(c>>>0>a>>>0|0)>>>0;return (C=d,a-c>>>0|0)|0}function Dc(a,b,c){a=a|0;b=b|0;c=c|0;if((c|0)<32){C=b>>>c;return a>>>c|(b&(1<<c)-1)<<32-c}C=0;return b>>>c-32|0}function Ec(a,b,c){a=a|0;b=b|0;c=c|0;if((c|0)<32){C=b<<c|(a&(1<<c)-1<<32-c)>>>32-c;return a<<c}C=a<<c-32;return 0}function Fc(b,d,e){b=b|0;d=d|0;e=e|0;var f=0;if((e|0)>=4096)return ka(b|0,d|0,e|0)|0;f=b|0;if((b&3)==(d&3)){while(b&3){if(!e)return f|0;a[b>>0]=a[d>>0]|0;b=b+1|0;d=d+1|0;e=e-1|0}while((e|0)>=4){c[b>>2]=c[d>>2];b=b+4|0;d=d+4|0;e=e-4|0}}while((e|0)>0){a[b>>0]=a[d>>0]|0;b=b+1|0;d=d+1|0;e=e-1|0}return f|0}function Gc(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;c=a+c>>>0;return (C=b+d+(c>>>0<a>>>0|0)>>>0,c|0)|0}function Hc(b,c,d){b=b|0;c=c|0;d=d|0;var e=0;if((c|0)<(b|0)&(b|0)<(c+d|0)){e=b;c=c+d|0;b=b+d|0;while((d|0)>0){b=b-1|0;c=c-1|0;d=d-1|0;a[b>>0]=a[c>>0]|0}b=e}else Fc(b,c,d)|0;return b|0}function Ic(a,b,c){a=a|0;b=b|0;c=c|0;if((c|0)<32){C=b>>c;return a>>>c|(b&(1<<c)-1)<<32-c}C=(b|0)<0?-1:0;return b>>c-32|0}function Jc(b){b=b|0;var c=0;c=a[m+(b&255)>>0]|0;if((c|0)<8)return c|0;c=a[m+(b>>8&255)>>0]|0;if((c|0)<8)return c+8|0;c=a[m+(b>>16&255)>>0]|0;if((c|0)<8)return c+16|0;return (a[m+(b>>>24)>>0]|0)+24|0}function Kc(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0;f=a&65535;e=b&65535;c=_(e,f)|0;d=a>>>16;a=(c>>>16)+(_(e,d)|0)|0;e=b>>>16;b=_(e,f)|0;return (C=(a>>>16)+(_(e,d)|0)+(((a&65535)+b|0)>>>16)|0,a+b<<16|c&65535|0)|0}function Lc(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0;j=b>>31|((b|0)<0?-1:0)<<1;i=((b|0)<0?-1:0)>>31|((b|0)<0?-1:0)<<1;f=d>>31|((d|0)<0?-1:0)<<1;e=((d|0)<0?-1:0)>>31|((d|0)<0?-1:0)<<1;h=Cc(j^a,i^b,j,i)|0;g=C;a=f^j;b=e^i;return Cc((Qc(h,g,Cc(f^c,e^d,f,e)|0,C,0)|0)^a,C^b,a,b)|0}function Mc(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0;f=i;i=i+16|0;j=f|0;h=b>>31|((b|0)<0?-1:0)<<1;g=((b|0)<0?-1:0)>>31|((b|0)<0?-1:0)<<1;l=e>>31|((e|0)<0?-1:0)<<1;k=((e|0)<0?-1:0)>>31|((e|0)<0?-1:0)<<1;a=Cc(h^a,g^b,h,g)|0;b=C;Qc(a,b,Cc(l^d,k^e,l,k)|0,C,j)|0;e=Cc(c[j>>2]^h,c[j+4>>2]^g,h,g)|0;d=C;i=f;return (C=d,e)|0}function Nc(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0;e=a;f=c;c=Kc(e,f)|0;a=C;return (C=(_(b,f)|0)+(_(d,e)|0)+a|a&0,c|0|0)|0}function Oc(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;return Qc(a,b,c,d,0)|0}function Pc(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,g=0;g=i;i=i+16|0;f=g|0;Qc(a,b,d,e,f)|0;i=g;return (C=c[f+4>>2]|0,c[f>>2]|0)|0}function Qc(a,b,d,e,f){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;l=a;j=b;k=j;h=d;n=e;i=n;if(!k){g=(f|0)!=0;if(!i){if(g){c[f>>2]=(l>>>0)%(h>>>0);c[f+4>>2]=0}n=0;f=(l>>>0)/(h>>>0)>>>0;return (C=n,f)|0}else{if(!g){n=0;f=0;return (C=n,f)|0}c[f>>2]=a|0;c[f+4>>2]=b&0;n=0;f=0;return (C=n,f)|0}}g=(i|0)==0;do if(h){if(!g){g=(aa(i|0)|0)-(aa(k|0)|0)|0;if(g>>>0<=31){m=g+1|0;i=31-g|0;b=g-31>>31;h=m;a=l>>>(m>>>0)&b|k<<i;b=k>>>(m>>>0)&b;g=0;i=l<<i;break}if(!f){n=0;f=0;return (C=n,f)|0}c[f>>2]=a|0;c[f+4>>2]=j|b&0;n=0;f=0;return (C=n,f)|0}g=h-1|0;if(g&h){i=(aa(h|0)|0)+33-(aa(k|0)|0)|0;p=64-i|0;m=32-i|0;j=m>>31;o=i-32|0;b=o>>31;h=i;a=m-1>>31&k>>>(o>>>0)|(k<<m|l>>>(i>>>0))&b;b=b&k>>>(i>>>0);g=l<<p&j;i=(k<<p|l>>>(o>>>0))&j|l<<m&i-33>>31;break}if(f){c[f>>2]=g&l;c[f+4>>2]=0}if((h|0)==1){o=j|b&0;p=a|0|0;return (C=o,p)|0}else{p=Jc(h|0)|0;o=k>>>(p>>>0)|0;p=k<<32-p|l>>>(p>>>0)|0;return (C=o,p)|0}}else{if(g){if(f){c[f>>2]=(k>>>0)%(h>>>0);c[f+4>>2]=0}o=0;p=(k>>>0)/(h>>>0)>>>0;return (C=o,p)|0}if(!l){if(f){c[f>>2]=0;c[f+4>>2]=(k>>>0)%(i>>>0)}o=0;p=(k>>>0)/(i>>>0)>>>0;return (C=o,p)|0}g=i-1|0;if(!(g&i)){if(f){c[f>>2]=a|0;c[f+4>>2]=g&k|b&0}o=0;p=k>>>((Jc(i|0)|0)>>>0);return (C=o,p)|0}g=(aa(i|0)|0)-(aa(k|0)|0)|0;if(g>>>0<=30){b=g+1|0;i=31-g|0;h=b;a=k<<i|l>>>(b>>>0);b=k>>>(b>>>0);g=0;i=l<<i;break}if(!f){o=0;p=0;return (C=o,p)|0}c[f>>2]=a|0;c[f+4>>2]=j|b&0;o=0;p=0;return (C=o,p)|0}while(0);if(!h){k=i;j=0;i=0}else{m=d|0|0;l=n|e&0;k=Gc(m|0,l|0,-1,-1)|0;d=C;j=i;i=0;do{e=j;j=g>>>31|j<<1;g=i|g<<1;e=a<<1|e>>>31|0;n=a>>>31|b<<1|0;Cc(k,d,e,n)|0;p=C;o=p>>31|((p|0)<0?-1:0)<<1;i=o&1;a=Cc(e,n,o&m,(((p|0)<0?-1:0)>>31|((p|0)<0?-1:0)<<1)&l)|0;b=C;h=h-1|0}while((h|0)!=0);k=j;j=0}h=0;if(f){c[f>>2]=a;c[f+4>>2]=b}o=(g|0)>>>31|(k|h)<<1|(h<<1|g>>>31)&0|j;p=(g<<1|0>>>31)&-2|i;return (C=o,p)|0}function Rc(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;return ra[a&1](b|0,c|0,d|0)|0}function Sc(a,b,c){a=a|0;b=b|0;c=c|0;ba(0);return 0}

// EMSCRIPTEN_END_FUNCS
var ra=[Sc,qc];return{_malloc:vc,_i64Subtract:Cc,_free:wc,_i64Add:Gc,_memmove:Hc,_memset:Bc,___cxa_demangle:Ba,_memcpy:Fc,_bitshift64Lshr:Dc,_bitshift64Shl:Ec,runPostSets:Ac,stackAlloc:sa,stackSave:ta,stackRestore:ua,establishStackSpace:va,setThrew:wa,setTempRet0:za,getTempRet0:Aa,dynCall_iiii:Rc}})


// EMSCRIPTEN_END_ASM
(Module.asmGlobalArg,Module.asmLibraryArg,buffer);var ___cxa_demangle=Module["___cxa_demangle"]=asm["___cxa_demangle"];var _i64Subtract=Module["_i64Subtract"]=asm["_i64Subtract"];var _free=Module["_free"]=asm["_free"];var runPostSets=Module["runPostSets"]=asm["runPostSets"];var _i64Add=Module["_i64Add"]=asm["_i64Add"];var _memmove=Module["_memmove"]=asm["_memmove"];var _memset=Module["_memset"]=asm["_memset"];var _malloc=Module["_malloc"]=asm["_malloc"];var _memcpy=Module["_memcpy"]=asm["_memcpy"];var _bitshift64Lshr=Module["_bitshift64Lshr"]=asm["_bitshift64Lshr"];var _bitshift64Shl=Module["_bitshift64Shl"]=asm["_bitshift64Shl"];var dynCall_iiii=Module["dynCall_iiii"]=asm["dynCall_iiii"];Runtime.stackAlloc=asm["stackAlloc"];Runtime.stackSave=asm["stackSave"];Runtime.stackRestore=asm["stackRestore"];Runtime.establishStackSpace=asm["establishStackSpace"];Runtime.setTempRet0=asm["setTempRet0"];Runtime.getTempRet0=asm["getTempRet0"];function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}ExitStatus.prototype=new Error;ExitStatus.prototype.constructor=ExitStatus;var initialStackTop;var preloadStartTime=null;var calledMain=false;dependenciesFulfilled=function runCaller(){if(!Module["calledRun"])run();if(!Module["calledRun"])dependenciesFulfilled=runCaller};Module["callMain"]=Module.callMain=function callMain(args){assert(runDependencies==0,"cannot call main when async dependencies remain! (listen on __ATMAIN__)");assert(__ATPRERUN__.length==0,"cannot call main when preRun functions remain to be called");args=args||[];ensureInitRuntime();var argc=args.length+1;function pad(){for(var i=0;i<4-1;i++){argv.push(0)}}var argv=[allocate(intArrayFromString(Module["thisProgram"]),"i8",ALLOC_NORMAL)];pad();for(var i=0;i<argc-1;i=i+1){argv.push(allocate(intArrayFromString(args[i]),"i8",ALLOC_NORMAL));pad()}argv.push(0);argv=allocate(argv,"i32",ALLOC_NORMAL);try{var ret=Module["_main"](argc,argv,0);exit(ret,true)}catch(e){if(e instanceof ExitStatus){return}else if(e=="SimulateInfiniteLoop"){Module["noExitRuntime"]=true;return}else{if(e&&typeof e==="object"&&e.stack)Module.printErr("exception thrown: "+[e,e.stack]);throw e}}finally{calledMain=true}};function run(args){args=args||Module["arguments"];if(preloadStartTime===null)preloadStartTime=Date.now();if(runDependencies>0){return}preRun();if(runDependencies>0)return;if(Module["calledRun"])return;function doRun(){if(Module["calledRun"])return;Module["calledRun"]=true;if(ABORT)return;ensureInitRuntime();preMain();if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();if(Module["_main"]&&shouldRunNow)Module["callMain"](args);postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout((function(){setTimeout((function(){Module["setStatus"]("")}),1);doRun()}),1)}else{doRun()}}Module["run"]=Module.run=run;function exit(status,implicit){if(implicit&&Module["noExitRuntime"]){return}if(Module["noExitRuntime"]){}else{ABORT=true;EXITSTATUS=status;STACKTOP=initialStackTop;exitRuntime();if(Module["onExit"])Module["onExit"](status)}if(ENVIRONMENT_IS_NODE){process["stdout"]["once"]("drain",(function(){process["exit"](status)}));console.log(" ");setTimeout((function(){process["exit"](status)}),500)}else if(ENVIRONMENT_IS_SHELL&&typeof quit==="function"){quit(status)}throw new ExitStatus(status)}Module["exit"]=Module.exit=exit;var abortDecorators=[];function abort(what){if(what!==undefined){Module.print(what);Module.printErr(what);what=JSON.stringify(what)}else{what=""}ABORT=true;EXITSTATUS=1;var extra="\nIf this abort() is unexpected, build with -s ASSERTIONS=1 which can give more information.";var output="abort("+what+") at "+stackTrace()+extra;if(abortDecorators){abortDecorators.forEach((function(decorator){output=decorator(output,what)}))}throw output}Module["abort"]=Module.abort=abort;if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}var shouldRunNow=true;if(Module["noInitialRun"]){shouldRunNow=false}run()





  return Module;
};

  var m = Module();
  var status = m._malloc(4);
  var buf = m._malloc(2048);

  return function(func) {
    if (func.length >= 2048) return null;
    m.writeStringToMemory(func.substr(1), buf);
    var ret = m['___cxa_demangle'](buf, 0, 0, status);
    var result = null;
    if (m.HEAP32[status >> 2] === 0 && ret) {
      result = m.Pointer_stringify(ret);
      m._free(ret);
    }
    return result;
  };
})();

// The emscripten compiler exports the Module object; we just want
// the demangle function
if (typeof module === "object" && typeof module.exports === "object") {
  module.exports = demangle;
}
PK
!<��a�<�<�Cchrome/devtools/modules/devtools/client/shared/developer-toolbar.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Ci } = require("chrome");
const promise = require("promise");
const Services = require("Services");
const { TargetFactory } = require("devtools/client/framework/target");
const Telemetry = require("devtools/client/shared/telemetry");
const {LocalizationHelper} = require("devtools/shared/l10n");
const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
const {Task} = require("devtools/shared/task");

const NS_XHTML = "http://www.w3.org/1999/xhtml";

const { PluralForm } = require("devtools/shared/plural-form");

loader.lazyGetter(this, "prefBranch", function () {
  return Services.prefs.getBranch(null)
                    .QueryInterface(Ci.nsIPrefBranch);
});

loader.lazyRequireGetter(this, "gcliInit", "devtools/shared/gcli/commands/index");
loader.lazyRequireGetter(this, "util", "gcli/util/util");
loader.lazyRequireGetter(this, "ConsoleServiceListener", "devtools/server/actors/webconsole/listeners", true);
loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
loader.lazyRequireGetter(this, "gDevToolsBrowser", "devtools/client/framework/devtools-browser", true);
loader.lazyRequireGetter(this, "nodeConstants", "devtools/shared/dom-node-constants");
loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");

/**
 * A collection of utilities to help working with commands
 */
var CommandUtils = {
  /**
   * Caches requisitions created when calling executeOnTarget:
   * Target => Requisition Promise
   */
  _requisitions: new WeakMap(),

  /**
   * Utility to execute a command string on a given target
   */
  executeOnTarget: Task.async(function* (target, command) {
    let requisitionPromise = this._requisitions.get(target);
    if (!requisitionPromise) {
      requisitionPromise = this.createRequisition(target, {
        environment: CommandUtils.createEnvironment({ target }, "target")
      });
      // Store the promise to avoid races by storing the promise immediately
      this._requisitions.set(target, requisitionPromise);
    }
    let requisition = yield requisitionPromise;
    requisition.updateExec(command);
  }),

  /**
   * Utility to ensure that things are loaded in the correct order
   */
  createRequisition: function (target, options) {
    if (!gcliInit) {
      return promise.reject("Unable to load gcli");
    }
    return gcliInit.getSystem(target).then(system => {
      let Requisition = require("gcli/cli").Requisition;
      return new Requisition(system, options);
    });
  },

  /**
   * Destroy the remote side of the requisition as well as the local side
   */
  destroyRequisition: function (requisition, target) {
    requisition.destroy();
    gcliInit.releaseSystem(target);
  },

  /**
   * A helper function to create the environment object that is passed to
   * GCLI commands.
   * @param targetContainer An object containing a 'target' property which
   * reflects the current debug target
   */
  createEnvironment: function (container, targetProperty = "target") {
    if (!container[targetProperty].toString ||
        !/TabTarget/.test(container[targetProperty].toString())) {
      throw new Error("Missing target");
    }

    return {
      get target() {
        if (!container[targetProperty].toString ||
            !/TabTarget/.test(container[targetProperty].toString())) {
          throw new Error("Removed target");
        }

        return container[targetProperty];
      },

      get chromeWindow() {
        return this.target.tab.ownerDocument.defaultView;
      },

      get chromeDocument() {
        return this.target.tab.ownerDocument.defaultView.document;
      },

      get window() {
        // throw new
        //    Error("environment.window is not available in runAt:client commands");
        return this.chromeWindow.gBrowser.contentWindowAsCPOW;
      },

      get document() {
        // throw new
        //    Error("environment.document is not available in runAt:client commands");
        return this.chromeWindow.gBrowser.contentDocumentAsCPOW;
      }
    };
  },
};

exports.CommandUtils = CommandUtils;

/**
 * Due to a number of panel bugs we need a way to check if we are running on
 * Linux. See the comments for TooltipPanel and OutputPanel for further details.
 *
 * When bug 780102 is fixed all isLinux checks can be removed and we can revert
 * to using panels.
 */
loader.lazyGetter(this, "isLinux", function () {
  return Services.appinfo.OS == "Linux";
});
loader.lazyGetter(this, "isMac", function () {
  return Services.appinfo.OS == "Darwin";
});

/**
 * A component to manage the global developer toolbar, which contains a GCLI
 * and buttons for various developer tools.
 * @param chromeWindow The browser window to which this toolbar is attached
 */
function DeveloperToolbar(chromeWindow) {
  this._chromeWindow = chromeWindow;

  // Will be setup when show() is called
  this.target = null;

  // The `_showPromise` will be set once `show` is called the first time, and resolved
  // when the toolbar is shown. Since it will be set to `null` only when `hide` method
  // is called, multiple calls to `show` method will returns this promise instead of
  // process again all the initialization.
  this._showPromise = null;
  // The `_hidePromise` will be set once `hide` is called, and resolved when the method
  // has finished. Once the toolbar is hidden, both `_showPromise` and `_hidePromise`
  // will be set to `null`.
  this._hidePromise = null;

  this._doc = chromeWindow.document;

  this._telemetry = new Telemetry();
  this._errorsCount = {};
  this._warningsCount = {};
  this._errorListeners = {};

  this._onToolboxReady = this._onToolboxReady.bind(this);
  this._onToolboxDestroyed = this._onToolboxDestroyed.bind(this);

  EventEmitter.decorate(this);
}
exports.DeveloperToolbar = DeveloperToolbar;

/**
 * Inspector notifications dispatched through the nsIObserverService
 */
const NOTIFICATIONS = {
  /** DeveloperToolbar.show() has been called, and we're working on it */
  LOAD: "developer-toolbar-load",

  /** DeveloperToolbar.show() has completed */
  SHOW: "developer-toolbar-show",

  /** DeveloperToolbar.hide() has been called */
  HIDE: "developer-toolbar-hide"
};

/**
 * Attach notification constants to the object prototype so tests etc can
 * use them without needing to import anything
 */
DeveloperToolbar.prototype.NOTIFICATIONS = NOTIFICATIONS;

/**
 * Is the toolbar open?
 */
Object.defineProperty(DeveloperToolbar.prototype, "visible", {
  get: function () {
    return this._element && !this._element.hidden;
  },
  enumerable: true
});

var _gSequenceId = 0;

/**
 * Getter for a unique ID.
 */
Object.defineProperty(DeveloperToolbar.prototype, "sequenceId", {
  get: function () {
    return _gSequenceId++;
  },
  enumerable: true
});

/**
 * Create the <toolbar> element to insert within browser UI
 */
DeveloperToolbar.prototype.createToolbar = function () {
  if (this._element) {
    return;
  }
  let toolbar = this._doc.createElement("toolbar");
  toolbar.setAttribute("id", "developer-toolbar");
  toolbar.setAttribute("hidden", "true");
  toolbar.setAttribute("fullscreentoolbar", "true");

  let close = this._doc.createElement("toolbarbutton");
  close.setAttribute("id", "developer-toolbar-closebutton");
  close.setAttribute("class", "close-icon");
  close.addEventListener("command", (event) => {
    this.hide();
  });
  let closeTooltip = L10N.getStr("toolbar.closeButton.tooltip");
  close.setAttribute("tooltiptext", closeTooltip);

  let stack = this._doc.createElement("stack");
  stack.setAttribute("flex", "1");

  let input = this._doc.createElement("textbox");
  input.setAttribute("class", "gclitoolbar-input-node");
  input.setAttribute("rows", "1");
  stack.appendChild(input);

  let hbox = this._doc.createElement("hbox");
  hbox.setAttribute("class", "gclitoolbar-complete-node");
  stack.appendChild(hbox);

  let toolboxBtn = this._doc.createElement("toolbarbutton");
  toolboxBtn.setAttribute("id", "developer-toolbar-toolbox-button");
  toolboxBtn.setAttribute("class", "developer-toolbar-button");
  let toolboxTooltip = L10N.getStr("toolbar.toolsButton.tooltip");
  toolboxBtn.setAttribute("tooltiptext", toolboxTooltip);
  let toolboxOpen = gDevToolsBrowser.hasToolboxOpened(this._chromeWindow);
  toolboxBtn.setAttribute("checked", toolboxOpen);
  toolboxBtn.addEventListener("command", function (event) {
    let window = event.target.ownerDocument.defaultView;
    gDevToolsBrowser.toggleToolboxCommand(window.gBrowser);
  });
  this._errorCounterButton = toolboxBtn;
  this._errorCounterButton._defaultTooltipText = toolboxTooltip;

  // On Mac, the close button is on the left,
  // while it is on the right on every other platforms.
  if (isMac) {
    toolbar.appendChild(close);
    toolbar.appendChild(stack);
    toolbar.appendChild(toolboxBtn);
  } else {
    toolbar.appendChild(stack);
    toolbar.appendChild(toolboxBtn);
    toolbar.appendChild(close);
  }

  this._element = toolbar;
  let bottomBox = this._doc.getElementById("browser-bottombox");
  if (bottomBox) {
    bottomBox.appendChild(this._element);
  } else {
    // SeaMonkey does not have a "browser-bottombox".
    let statusBar = this._doc.getElementById("status-bar");
    if (statusBar) {
      statusBar.parentNode.insertBefore(this._element, statusBar);
    }
  }
};

/**
 * Called from browser.xul in response to menu-click or keyboard shortcut to
 * toggle the toolbar
 */
DeveloperToolbar.prototype.toggle = function () {
  if (this.visible) {
    return this.hide().catch(console.error);
  }
  return this.show(true).catch(console.error);
};

/**
 * Called from browser.xul in response to menu-click or keyboard shortcut to
 * toggle the toolbar.
 * The method returns a promise that would be resolved once focused; if the toolbar is not
 * visible yet it will be automatically shown.
 */
DeveloperToolbar.prototype.focus = function () {
  if (this.visible) {
    // If the toolbar was just inserted, the <textbox> may still have
    // its binding in process of being applied and not be focusable yet
    return new Promise((resolve, reject) => {
      let checkBinding = () => {
        // Bail out if the toolbar has been destroyed in the meantime
        if (!this._input) {
          reject();
          return;
        }
        // mInputField is a xbl field of <xul:textbox>
        if (typeof this._input.mInputField != "undefined") {
          this._input.focus();
          resolve();
        } else {
          this._input.ownerDocument.defaultView.setTimeout(checkBinding, 50);
        }
      };
      checkBinding();
    });
  }

  return this.show(true);
};

/**
 * Called from browser.xul in response to menu-click or keyboard shortcut to
 * toggle the toolbar
 */
DeveloperToolbar.prototype.focusToggle = function () {
  if (this.visible) {
    // If we have focus then the active element is the HTML input contained
    // inside the xul input element
    let active = this._chromeWindow.document.activeElement;
    let position = this._input.compareDocumentPosition(active);
    if (position & nodeConstants.DOCUMENT_POSITION_CONTAINED_BY) {
      this.hide();
    } else {
      this._input.focus();
    }
  } else {
    this.show(true);
  }
};

/**
 * Even if the user has not clicked on 'Got it' in the intro, we only show it
 * once per session.
 * Warning this is slightly messed up because this.DeveloperToolbar is not the
 * same as this.DeveloperToolbar when in browser.js context.
 */
DeveloperToolbar.introShownThisSession = false;

/**
 * Show the developer toolbar
 */
DeveloperToolbar.prototype.show = function (focus) {
  // if `_showPromise` is set, just returns it instead of process all the initialization
  // again; ensuring we're focusing the element too if `focus` argument is set to `true`.
  if (this._showPromise !== null) {
    if (focus) {
      return this.focus();
    }
    return this._showPromise;
  }

  this._showPromise = Task.spawn((function* () {
    // hide() is async, so ensure we don't need to wait for hide() to
    // finish.  We unconditionally yield here, even if _hidePromise is
    // null, so that the spawn call returns a promise before starting
    // to do any real work.
    yield this._hidePromise;

    // Append the browser-level stylesheet to the browser document.
    yield gDevToolsBrowser.loadBrowserStyleSheet(this._chromeWindow);

    this.createToolbar();

    Services.prefs.setBoolPref("devtools.toolbar.visible", true);

    this._telemetry.toolOpened("developertoolbar");

    this._notify(NOTIFICATIONS.LOAD);

    this._input = this._doc.querySelector(".gclitoolbar-input-node");

    // Initializing GCLI can only be done when we've got content windows to
    // write to, so this needs to be done asynchronously.
    let panelPromises = [
      TooltipPanel.create(this),
      OutputPanel.create(this)
    ];
    let panels = yield promise.all(panelPromises);

    [ this.tooltipPanel, this.outputPanel ] = panels;

    this._doc.getElementById("menu_devToolbar").setAttribute("checked", "true");

    this.target = TargetFactory.forTab(this._chromeWindow.gBrowser.selectedTab);
    const options = {
      environment: CommandUtils.createEnvironment(this, "target"),
      document: this.outputPanel.document,
    };
    let requisition = yield CommandUtils.createRequisition(this.target, options);
    this.requisition = requisition;

    // The <textbox> `value` may still be undefined on the XUL binding if
    // we fetch it early
    let value = this._input.value || "";
    yield this.requisition.update(value);

    const Inputter = require("gcli/mozui/inputter").Inputter;
    const Completer = require("gcli/mozui/completer").Completer;
    const Tooltip = require("gcli/mozui/tooltip").Tooltip;
    const FocusManager = require("gcli/ui/focus").FocusManager;

    this.onOutput = this.requisition.commandOutputManager.onOutput;

    this.focusManager = new FocusManager(this._doc, requisition.system.settings);

    this.inputter = new Inputter({
      requisition: this.requisition,
      focusManager: this.focusManager,
      element: this._input,
    });

    this.completer = new Completer({
      requisition: this.requisition,
      inputter: this.inputter,
      backgroundElement: this._doc.querySelector(".gclitoolbar-stack-node"),
      element: this._doc.querySelector(".gclitoolbar-complete-node"),
    });

    this.tooltip = new Tooltip({
      requisition: this.requisition,
      focusManager: this.focusManager,
      inputter: this.inputter,
      element: this.tooltipPanel.hintElement,
    });

    this.inputter.tooltip = this.tooltip;

    this.focusManager.addMonitoredElement(this.outputPanel._frame);
    this.focusManager.addMonitoredElement(this._element);

    this.focusManager.onVisibilityChange.add(this.outputPanel._visibilityChanged,
                                             this.outputPanel);
    this.focusManager.onVisibilityChange.add(this.tooltipPanel._visibilityChanged,
                                             this.tooltipPanel);
    this.onOutput.add(this.outputPanel._outputChanged, this.outputPanel);

    let tabbrowser = this._chromeWindow.gBrowser;
    tabbrowser.tabContainer.addEventListener("TabSelect", this);
    tabbrowser.tabContainer.addEventListener("TabClose", this);
    tabbrowser.addEventListener("load", this, true);
    tabbrowser.addEventListener("beforeunload", this, true);

    gDevTools.on("toolbox-ready", this._onToolboxReady);
    gDevTools.on("toolbox-destroyed", this._onToolboxDestroyed);

    this._initErrorsCount(tabbrowser.selectedTab);

    this._element.hidden = false;

    if (focus) {
      yield this.focus();
    }
    this._notify(NOTIFICATIONS.SHOW);

    if (!DeveloperToolbar.introShownThisSession) {
      let intro = require("gcli/ui/intro");
      intro.maybeShowIntro(this.requisition.commandOutputManager,
                           this.requisition.conversionContext,
                           this.outputPanel);
      DeveloperToolbar.introShownThisSession = true;
    }
  }).bind(this));

  return this._showPromise;
};

/**
 * Hide the developer toolbar.
 */
DeveloperToolbar.prototype.hide = function () {
  // If we're already in the process of hiding, just returns the promise
  if (this._hidePromise !== null) {
    return this._hidePromise;
  }

  // If `_showPromise` is `null`, it means `show` method was never called, so just
  // returns a resolved promise.
  if (this._showPromise === null) {
    return promise.resolve();
  }

  // show() is async, so ensure we don't need to wait for show() to finish
  this._hidePromise = this._showPromise.then(() => {
    this._element.hidden = true;

    Services.prefs.setBoolPref("devtools.toolbar.visible", false);

    this._doc.getElementById("menu_devToolbar").setAttribute("checked", "false");
    this.destroy();

    this._telemetry.toolClosed("developertoolbar");
    this._notify(NOTIFICATIONS.HIDE);

    // The developer toolbar is now closed, is neither shown or in process of hiding,
    // so we're set to `null` both `_showPromise` and `_hidePromise`.
    this._showPromise = null;
    this._hidePromise = null;
  });

  return this._hidePromise;
};

/**
 * Initialize the listeners needed for tracking the number of errors for a given
 * tab.
 *
 * @private
 * @param nsIDOMNode tab the xul:tab for which you want to track the number of
 * errors.
 */
DeveloperToolbar.prototype._initErrorsCount = function (tab) {
  let tabId = tab.linkedPanel;
  if (tabId in this._errorsCount) {
    this._updateErrorsCount();
    return;
  }

  let window = tab.linkedBrowser.contentWindow;
  let listener = new ConsoleServiceListener(window, {
    onConsoleServiceMessage: this._onPageError.bind(this, tabId),
  });
  listener.init();

  this._errorListeners[tabId] = listener;
  this._errorsCount[tabId] = 0;
  this._warningsCount[tabId] = 0;

  let messages = listener.getCachedMessages();
  messages.forEach(this._onPageError.bind(this, tabId));

  this._updateErrorsCount();
};

/**
 * Stop the listeners needed for tracking the number of errors for a given
 * tab.
 *
 * @private
 * @param nsIDOMNode tab the xul:tab for which you want to stop tracking the
 * number of errors.
 */
DeveloperToolbar.prototype._stopErrorsCount = function (tab) {
  let tabId = tab.linkedPanel;
  if (!(tabId in this._errorsCount) || !(tabId in this._warningsCount)) {
    this._updateErrorsCount();
    return;
  }

  this._errorListeners[tabId].destroy();
  delete this._errorListeners[tabId];
  delete this._errorsCount[tabId];
  delete this._warningsCount[tabId];

  this._updateErrorsCount();
};

/**
 * Hide the developer toolbar
 */
DeveloperToolbar.prototype.destroy = function () {
  if (this._input == null) {
    // Already destroyed
    return;
  }

  let tabbrowser = this._chromeWindow.gBrowser;
  tabbrowser.tabContainer.removeEventListener("TabSelect", this);
  tabbrowser.tabContainer.removeEventListener("TabClose", this);
  tabbrowser.removeEventListener("load", this, true);
  tabbrowser.removeEventListener("beforeunload", this, true);

  gDevTools.off("toolbox-ready", this._onToolboxReady);
  gDevTools.off("toolbox-destroyed", this._onToolboxDestroyed);

  Array.prototype.forEach.call(tabbrowser.tabs, this._stopErrorsCount, this);

  this.focusManager.removeMonitoredElement(this.outputPanel._frame);
  this.focusManager.removeMonitoredElement(this._element);

  this.focusManager.onVisibilityChange.remove(this.outputPanel._visibilityChanged,
                                              this.outputPanel);
  this.focusManager.onVisibilityChange.remove(this.tooltipPanel._visibilityChanged,
                                              this.tooltipPanel);
  this.onOutput.remove(this.outputPanel._outputChanged, this.outputPanel);

  this.tooltip.destroy();
  this.completer.destroy();
  this.inputter.destroy();
  this.focusManager.destroy();

  this.outputPanel.destroy();
  this.tooltipPanel.destroy();
  delete this._input;

  CommandUtils.destroyRequisition(this.requisition, this.target);
  this.target = undefined;

  this._element.remove();
  delete this._element;
};

/**
 * Utility for sending notifications
 * @param topic a NOTIFICATION constant
 */
DeveloperToolbar.prototype._notify = function (topic) {
  let data = { toolbar: this };
  data.wrappedJSObject = data;
  Services.obs.notifyObservers(data, topic);
};

/**
 * Update various parts of the UI when the current tab changes
 */
DeveloperToolbar.prototype.handleEvent = function (ev) {
  if (ev.type == "TabSelect" || ev.type == "load") {
    if (this.visible) {
      let tab = this._chromeWindow.gBrowser.selectedTab;
      this.target = TargetFactory.forTab(tab);
      gcliInit.getSystem(this.target).then(system => {
        this.requisition.system = system;
      }, error => {
        if (!this._chromeWindow.gBrowser.getBrowserForTab(tab)) {
          // The tab was closed, suppress the error and print a warning as the
          // destroyed tab was likely the cause.
          console.warn("An error occurred as the tab was closed while " +
            "updating Developer Toolbar state. The error was: ", error);
          return;
        }

        // Propagate other errors as they're more likely to cause real issues
        // and thus should cause tests to fail.
        throw error;
      });

      if (ev.type == "TabSelect") {
        let toolboxOpen = gDevToolsBrowser.hasToolboxOpened(this._chromeWindow);
        this._errorCounterButton.setAttribute("checked", toolboxOpen);
        this._initErrorsCount(ev.target);
      }
    }
  } else if (ev.type == "TabClose") {
    this._stopErrorsCount(ev.target);
  } else if (ev.type == "beforeunload") {
    this._onPageBeforeUnload(ev);
  }
};

/**
 * Update toolbox toggle button when toolbox goes on and off
 */
DeveloperToolbar.prototype._onToolboxReady = function () {
  this._errorCounterButton.setAttribute("checked", "true");
};
DeveloperToolbar.prototype._onToolboxDestroyed = function () {
  this._errorCounterButton.setAttribute("checked", "false");
};

/**
 * Count a page error received for the currently selected tab. This
 * method counts the JavaScript exceptions received and CSS errors/warnings.
 *
 * @private
 * @param string tabId the ID of the tab from where the page error comes.
 * @param object pageError the page error object received from the
 * PageErrorListener.
 */
DeveloperToolbar.prototype._onPageError = function (tabId, pageError) {
  if (pageError.category == "CSS Parser" ||
      pageError.category == "CSS Loader") {
    return;
  }
  if ((pageError.flags & pageError.warningFlag) ||
      (pageError.flags & pageError.strictFlag)) {
    this._warningsCount[tabId]++;
  } else {
    this._errorsCount[tabId]++;
  }
  this._updateErrorsCount(tabId);
};

/**
 * The |beforeunload| event handler. This function resets the errors count when
 * a different page starts loading.
 *
 * @private
 * @param nsIDOMEvent ev the beforeunload DOM event.
 */
DeveloperToolbar.prototype._onPageBeforeUnload = function (ev) {
  let window = ev.target.defaultView;
  if (window.top !== window) {
    return;
  }

  let tabs = this._chromeWindow.gBrowser.tabs;
  Array.prototype.some.call(tabs, function (tab) {
    if (tab.linkedBrowser.contentWindow === window) {
      let tabId = tab.linkedPanel;
      if (tabId in this._errorsCount || tabId in this._warningsCount) {
        this._errorsCount[tabId] = 0;
        this._warningsCount[tabId] = 0;
        this._updateErrorsCount(tabId);
      }
      return true;
    }
    return false;
  }, this);
};

/**
 * Update the page errors count displayed in the Web Console button for the
 * currently selected tab.
 *
 * @private
 * @param string [changedTabId] Optional. The tab ID that had its page errors
 * count changed. If this is provided and it doesn't match the currently
 * selected tab, then the button is not updated.
 */
DeveloperToolbar.prototype._updateErrorsCount = function (changedTabId) {
  let tabId = this._chromeWindow.gBrowser.selectedTab.linkedPanel;
  if (changedTabId && tabId != changedTabId) {
    return;
  }

  let errors = this._errorsCount[tabId];
  let warnings = this._warningsCount[tabId];
  let btn = this._errorCounterButton;
  if (errors) {
    let errorsText = L10N.getStr("toolboxToggleButton.errors");
    errorsText = PluralForm.get(errors, errorsText).replace("#1", errors);

    let warningsText = L10N.getStr("toolboxToggleButton.warnings");
    warningsText = PluralForm.get(warnings, warningsText).replace("#1", warnings);

    let tooltiptext = L10N.getFormatStr("toolboxToggleButton.tooltip",
                                            errorsText, warningsText);

    btn.setAttribute("error-count", errors);
    btn.setAttribute("tooltiptext", tooltiptext);
  } else {
    btn.removeAttribute("error-count");
    btn.setAttribute("tooltiptext", btn._defaultTooltipText);
  }

  this.emit("errors-counter-updated");
};

/**
 * Reset the errors counter for the given tab.
 *
 * @param nsIDOMElement tab The xul:tab for which you want to reset the page
 * errors counters.
 */
DeveloperToolbar.prototype.resetErrorsCount = function (tab) {
  let tabId = tab.linkedPanel;
  if (tabId in this._errorsCount || tabId in this._warningsCount) {
    this._errorsCount[tabId] = 0;
    this._warningsCount[tabId] = 0;
    this._updateErrorsCount(tabId);
  }
};

/**
 * Creating a OutputPanel is asynchronous
 */
function OutputPanel() {
  throw new Error("Use OutputPanel.create()");
}

/**
 * Panel to handle command line output.
 *
 * There is a tooltip bug on Windows and OSX that prevents tooltips from being
 * positioned properly (bug 786975). There is a Gnome panel bug on Linux that
 * causes ugly focus issues (https://bugzilla.gnome.org/show_bug.cgi?id=621848).
 * We now use a tooltip on Linux and a panel on OSX & Windows.
 *
 * If a panel has no content and no height it is not shown when openPopup is
 * called on Windows and OSX (bug 692348) ... this prevents the panel from
 * appearing the first time it is shown. Setting the panel's height to 1px
 * before calling openPopup works around this issue as we resize it ourselves
 * anyway.
 *
 * @param devtoolbar The parent DeveloperToolbar object
 */
OutputPanel.create = function (devtoolbar) {
  let outputPanel = Object.create(OutputPanel.prototype);
  return outputPanel._init(devtoolbar);
};

/**
 * @private See OutputPanel.create
 */
OutputPanel.prototype._init = function (devtoolbar) {
  return new Promise((resolve, reject) => {
    this._devtoolbar = devtoolbar;
    this._input = this._devtoolbar._input;
    this._toolbar = this._devtoolbar._doc.getElementById("developer-toolbar");

    /*
    <tooltip|panel id="gcli-output"
           noautofocus="true"
           noautohide="true"
           class="gcli-panel">
      <html:iframe xmlns:html="http://www.w3.org/1999/xhtml"
                   id="gcli-output-frame"
                   src="chrome://devtools/content/commandline/commandlineoutput.xhtml"
                   sandbox="allow-same-origin"/>
    </tooltip|panel>
    */

    // TODO: Switch back from tooltip to panel when metacity focus issue is fixed:
    // https://bugzilla.mozilla.org/show_bug.cgi?id=780102
    this._panel = this._devtoolbar._doc.createElement(isLinux ? "tooltip" : "panel");

    this._panel.id = "gcli-output";
    this._panel.classList.add("gcli-panel");

    if (isLinux) {
      this.canHide = false;
      this._onpopuphiding = this._onpopuphiding.bind(this);
      this._panel.addEventListener("popuphiding", this._onpopuphiding, true);
    } else {
      this._panel.setAttribute("noautofocus", "true");
      this._panel.setAttribute("noautohide", "true");

      // Bug 692348: On Windows and OSX if a panel has no content and no height
      // openPopup fails to display it. Setting the height to 1px alows the panel
      // to be displayed before has content or a real height i.e. the first time
      // it is displayed.
      this._panel.setAttribute("height", "1px");
    }

    this._toolbar.parentElement.insertBefore(this._panel, this._toolbar);

    this._frame = this._devtoolbar._doc.createElementNS(NS_XHTML, "iframe");
    this._frame.id = "gcli-output-frame";
    this._frame.setAttribute("src", "chrome://devtools/content/commandline/commandlineoutput.xhtml");
    this._frame.setAttribute("sandbox", "allow-same-origin");
    this._panel.appendChild(this._frame);

    this.displayedOutput = undefined;

    this._update = this._update.bind(this);

    // Wire up the element from the iframe, and resolve the promise
    let onload = () => {
      this._frame.removeEventListener("load", onload, true);

      this.document = this._frame.contentDocument;
      this._copyTheme();

      this._div = this.document.getElementById("gcli-output-root");
      this._div.classList.add("gcli-row-out");
      this._div.setAttribute("aria-live", "assertive");

      let styles = this._toolbar.ownerDocument.defaultView
                      .getComputedStyle(this._toolbar);
      this._div.setAttribute("dir", styles.direction);

      resolve(this);
    };
    this._frame.addEventListener("load", onload, true);
  });
};

/* Copy the current devtools theme attribute into the iframe,
   so it can be styled correctly. */
OutputPanel.prototype._copyTheme = function () {
  if (this.document) {
    let theme = this._devtoolbar._doc.getElementById("browser-bottombox")
                  .getAttribute("devtoolstheme");
    this.document.documentElement.setAttribute("devtoolstheme", theme);
  }
};

/**
 * Prevent the popup from hiding if it is not permitted via this.canHide.
 */
OutputPanel.prototype._onpopuphiding = function (ev) {
  // TODO: When we switch back from tooltip to panel we can remove this hack:
  // https://bugzilla.mozilla.org/show_bug.cgi?id=780102
  if (isLinux && !this.canHide) {
    ev.preventDefault();
  }
};

/**
 * Display the OutputPanel.
 */
OutputPanel.prototype.show = function () {
  if (isLinux) {
    this.canHide = false;
  }

  // We need to reset the iframe size in order for future size calculations to
  // be correct
  this._frame.style.minHeight = this._frame.style.maxHeight = 0;
  this._frame.style.minWidth = 0;

  this._copyTheme();
  this._panel.openPopup(this._input, "before_start", 0, 0, false, false, null);
  this._resize();

  this._input.focus();
};

/**
 * Internal helper to set the height of the output panel to fit the available
 * content;
 */
OutputPanel.prototype._resize = function () {
  if (this._panel == null || this.document == null || !this._panel.state == "closed") {
    return;
  }

  // Set max panel width to match any content with a max of the width of the
  // browser window.
  let maxWidth = this._panel.ownerDocument.documentElement.clientWidth;

  // Adjust max width according to OS.
  // We'd like to put this in CSS but we can't:
  //   body { width: calc(min(-5px, max-content)); }
  //   #_panel { max-width: -5px; }
  switch (Services.appinfo.OS) {
    case "Linux":
      maxWidth -= 5;
      break;
    case "Darwin":
      maxWidth -= 25;
      break;
    case "WINNT":
      maxWidth -= 5;
      break;
  }

  this.document.body.style.width = "-moz-max-content";
  let style = this._frame.contentWindow.getComputedStyle(this.document.body);
  let frameWidth = parseInt(style.width, 10);
  let width = Math.min(maxWidth, frameWidth);
  this.document.body.style.width = width + "px";

  // Set the width of the iframe.
  this._frame.style.minWidth = width + "px";
  this._panel.style.maxWidth = maxWidth + "px";

  // browserAdjustment is used to correct the panel height according to the
  // browsers borders etc.
  const browserAdjustment = 15;

  // Set max panel height to match any content with a max of the height of the
  // browser window.
  let maxHeight =
    this._panel.ownerDocument.documentElement.clientHeight - browserAdjustment;
  let height = Math.min(maxHeight, this.document.documentElement.scrollHeight);

  // Set the height of the iframe. Setting iframe.height does not work.
  this._frame.style.minHeight = this._frame.style.maxHeight = height + "px";

  // Set the height and width of the panel to match the iframe.
  this._panel.sizeTo(width, height);

  // Move the panel to the correct position in the case that it has been
  // positioned incorrectly.
  let screenX = this._input.boxObject.screenX;
  let screenY = this._toolbar.boxObject.screenY;
  this._panel.moveTo(screenX, screenY - height);
};

/**
 * Called by GCLI when a command is executed.
 */
OutputPanel.prototype._outputChanged = function (ev) {
  if (ev.output.hidden) {
    return;
  }

  this.remove();

  this.displayedOutput = ev.output;

  if (this.displayedOutput.completed) {
    this._update();
  } else {
    this.displayedOutput.promise.then(this._update, this._update)
                                .catch(console.error);
  }
};

/**
 * Called when displayed Output says it's changed or from outputChanged, which
 * happens when there is a new displayed Output.
 */
OutputPanel.prototype._update = function () {
  // destroy has been called, bail out
  if (this._div == null) {
    return;
  }

  // Empty this._div
  while (this._div.hasChildNodes()) {
    this._div.firstChild.remove();
  }

  if (this.displayedOutput.data != null) {
    let context = this._devtoolbar.requisition.conversionContext;
    this.displayedOutput.convert("dom", context).then(node => {
      if (node == null) {
        return;
      }

      while (this._div.hasChildNodes()) {
        this._div.firstChild.remove();
      }

      let links = node.querySelectorAll("*[href]");
      for (let i = 0; i < links.length; i++) {
        links[i].setAttribute("target", "_blank");
      }

      this._div.appendChild(node);
      this.show();
    });
  }
};

/**
 * Detach listeners from the currently displayed Output.
 */
OutputPanel.prototype.remove = function () {
  if (isLinux) {
    this.canHide = true;
  }

  if (this._panel && this._panel.hidePopup) {
    this._panel.hidePopup();
  }

  if (this.displayedOutput) {
    delete this.displayedOutput;
  }
};

/**
 * Detach listeners from the currently displayed Output.
 */
OutputPanel.prototype.destroy = function () {
  this.remove();

  this._panel.removeEventListener("popuphiding", this._onpopuphiding, true);

  this._panel.removeChild(this._frame);
  this._toolbar.parentElement.removeChild(this._panel);

  delete this._devtoolbar;
  delete this._input;
  delete this._toolbar;
  delete this._onpopuphiding;
  delete this._panel;
  delete this._frame;
  delete this._content;
  delete this._div;
  delete this.document;
};

/**
 * Called by GCLI to indicate that we should show or hide one either the
 * tooltip panel or the output panel.
 */
OutputPanel.prototype._visibilityChanged = function (ev) {
  if (ev.outputVisible === true) {
    // this.show is called by _outputChanged
  } else {
    if (isLinux) {
      this.canHide = true;
    }
    this._panel.hidePopup();
  }
};

/**
 * Creating a TooltipPanel is asynchronous
 */
function TooltipPanel() {
  throw new Error("Use TooltipPanel.create()");
}

/**
 * Panel to handle tooltips.
 *
 * There is a tooltip bug on Windows and OSX that prevents tooltips from being
 * positioned properly (bug 786975). There is a Gnome panel bug on Linux that
 * causes ugly focus issues (https://bugzilla.gnome.org/show_bug.cgi?id=621848).
 * We now use a tooltip on Linux and a panel on OSX & Windows.
 *
 * If a panel has no content and no height it is not shown when openPopup is
 * called on Windows and OSX (bug 692348) ... this prevents the panel from
 * appearing the first time it is shown. Setting the panel's height to 1px
 * before calling openPopup works around this issue as we resize it ourselves
 * anyway.
 *
 * @param devtoolbar The parent DeveloperToolbar object
 */
TooltipPanel.create = function (devtoolbar) {
  let tooltipPanel = Object.create(TooltipPanel.prototype);
  return tooltipPanel._init(devtoolbar);
};

/**
 * @private See TooltipPanel.create
 */
TooltipPanel.prototype._init = function (devtoolbar) {
  return new Promise((resolve, reject) => {
    this._devtoolbar = devtoolbar;
    this._input = devtoolbar._doc.querySelector(".gclitoolbar-input-node");
    this._toolbar = devtoolbar._doc.querySelector("#developer-toolbar");
    this._dimensions = { start: 0, end: 0 };

    /*
    <tooltip|panel id="gcli-tooltip"
           type="arrow"
           noautofocus="true"
           noautohide="true"
           class="gcli-panel">
      <html:iframe xmlns:html="http://www.w3.org/1999/xhtml"
                   id="gcli-tooltip-frame"
                   src="chrome://devtools/content/commandline/commandlinetooltip.xhtml"
                   flex="1"
                   sandbox="allow-same-origin"/>
    </tooltip|panel>
    */

    // TODO: Switch back from tooltip to panel when metacity focus issue is fixed:
    // https://bugzilla.mozilla.org/show_bug.cgi?id=780102
    this._panel = devtoolbar._doc.createElement(isLinux ? "tooltip" : "panel");

    this._panel.id = "gcli-tooltip";
    this._panel.classList.add("gcli-panel");

    if (isLinux) {
      this.canHide = false;
      this._onpopuphiding = this._onpopuphiding.bind(this);
      this._panel.addEventListener("popuphiding", this._onpopuphiding, true);
    } else {
      this._panel.setAttribute("noautofocus", "true");
      this._panel.setAttribute("noautohide", "true");

      // Bug 692348: On Windows and OSX if a panel has no content and no height
      // openPopup fails to display it. Setting the height to 1px alows the panel
      // to be displayed before has content or a real height i.e. the first time
      // it is displayed.
      this._panel.setAttribute("height", "1px");
    }

    this._toolbar.parentElement.insertBefore(this._panel, this._toolbar);

    this._frame = devtoolbar._doc.createElementNS(NS_XHTML, "iframe");
    this._frame.id = "gcli-tooltip-frame";
    this._frame.setAttribute("src", "chrome://devtools/content/commandline/commandlinetooltip.xhtml");
    this._frame.setAttribute("flex", "1");
    this._frame.setAttribute("sandbox", "allow-same-origin");
    this._panel.appendChild(this._frame);

    /**
     * Wire up the element from the iframe, and resolve the promise.
     */
    let onload = () => {
      this._frame.removeEventListener("load", onload, true);

      this.document = this._frame.contentDocument;
      this._copyTheme();
      this.hintElement = this.document.getElementById("gcli-tooltip-root");
      this._connector = this.document.getElementById("gcli-tooltip-connector");

      let styles = this._toolbar.ownerDocument.defaultView
                      .getComputedStyle(this._toolbar);
      this.hintElement.setAttribute("dir", styles.direction);

      resolve(this);
    };
    this._frame.addEventListener("load", onload, true);
  });
};

/* Copy the current devtools theme attribute into the iframe,
   so it can be styled correctly. */
TooltipPanel.prototype._copyTheme = function () {
  if (this.document) {
    let theme = this._devtoolbar._doc.getElementById("browser-bottombox")
                  .getAttribute("devtoolstheme");
    this.document.documentElement.setAttribute("devtoolstheme", theme);
  }
};

/**
 * Prevent the popup from hiding if it is not permitted via this.canHide.
 */
TooltipPanel.prototype._onpopuphiding = function (ev) {
  // TODO: When we switch back from tooltip to panel we can remove this hack:
  // https://bugzilla.mozilla.org/show_bug.cgi?id=780102
  if (isLinux && !this.canHide) {
    ev.preventDefault();
  }
};

/**
 * Display the TooltipPanel.
 */
TooltipPanel.prototype.show = function (dimensions) {
  if (!dimensions) {
    dimensions = { start: 0, end: 0 };
  }
  this._dimensions = dimensions;

  // This is nasty, but displaying the panel causes it to re-flow, which can
  // change the size it should be, so we need to resize the iframe after the
  // panel has displayed
  this._panel.ownerDocument.defaultView.setTimeout(() => {
    this._resize();
  }, 0);

  if (isLinux) {
    this.canHide = false;
  }

  this._copyTheme();
  this._resize();
  this._panel.openPopup(this._input, "before_start", dimensions.start * 10, 0,
                        false, false, null);
  this._input.focus();
};

/**
 * One option is to spend lots of time taking an average width of characters
 * in the current font, dynamically, and weighting for the frequency of use of
 * various characters, or even to render the given string off screen, and then
 * measure the width.
 * Or we could do this...
 */
const AVE_CHAR_WIDTH = 4.5;

/**
 * Display the TooltipPanel.
 */
TooltipPanel.prototype._resize = function () {
  if (this._panel == null || this.document == null || !this._panel.state == "closed") {
    return;
  }

  let offset = 10 + Math.floor(this._dimensions.start * AVE_CHAR_WIDTH);
  this._panel.style.marginLeft = offset + "px";

  /*
  // Bug 744906: UX review - Not sure if we want this code to fatten connector
  // with param width
  let width = Math.floor(this._dimensions.end * AVE_CHAR_WIDTH);
  width = Math.min(width, 100);
  width = Math.max(width, 10);
  this._connector.style.width = width + "px";
  */

  this._frame.height = this.document.body.scrollHeight;
};

/**
 * Hide the TooltipPanel.
 */
TooltipPanel.prototype.remove = function () {
  if (isLinux) {
    this.canHide = true;
  }
  if (this._panel && this._panel.hidePopup) {
    this._panel.hidePopup();
  }
};

/**
 * Hide the TooltipPanel.
 */
TooltipPanel.prototype.destroy = function () {
  this.remove();

  this._panel.removeEventListener("popuphiding", this._onpopuphiding, true);

  this._panel.removeChild(this._frame);
  this._toolbar.parentElement.removeChild(this._panel);

  delete this._connector;
  delete this._dimensions;
  delete this._input;
  delete this._onpopuphiding;
  delete this._panel;
  delete this._frame;
  delete this._toolbar;
  delete this._content;
  delete this.document;
  delete this.hintElement;
};

/**
 * Called by GCLI to indicate that we should show or hide one either the
 * tooltip panel or the output panel.
 */
TooltipPanel.prototype._visibilityChanged = function (ev) {
  if (ev.tooltipVisible === true) {
    this.show(ev.dimensions);
  } else {
    if (isLinux) {
      this.canHide = true;
    }
    this._panel.hidePopup();
  }
};
PK
!<P�vJ9chrome/devtools/modules/devtools/client/shared/devices.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { getJSON } = require("devtools/client/shared/getjson");
const { LocalizationHelper } = require("devtools/shared/l10n");
const L10N = new LocalizationHelper("devtools/client/locales/device.properties");

loader.lazyRequireGetter(this, "asyncStorage", "devtools/shared/async-storage");

const DEVICES_URL = "devtools.devices.url";
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, check out the repo at
 * https://github.com/mozilla/simulated-devices and file a pull request.
 *
 * 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
  let 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;
}

/**
 * Remove a device from the local catalog.
 * Returns `true` if the device is removed, `false` otherwise.
 */
async function removeDevice(device, type = "phones") {
  await loadLocalDevices();
  let list = localDevices[type];
  if (!list) {
    return false;
  }

  let 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() {
  // Fetch common devices from Mozilla's CDN.
  let devices = await getJSON(DEVICES_URL);
  await loadLocalDevices();
  for (let type in localDevices) {
    if (!devices[type]) {
      devices.TYPES.push(type);
      devices[type] = [];
    }
    devices[type] = localDevices[type].concat(devices[type]);
  }
  return devices;
}

/**
 * Get the localized string for a device type.
 */
function getDeviceString(deviceType) {
  return L10N.getStr("device." + deviceType);
}

module.exports = {
  addDevice,
  removeDevice,
  removeLocalDevices,
  getDevices,
  getDeviceString,
};
PK
!<k�J��<chrome/devtools/modules/devtools/client/shared/doorhanger.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Services = require("Services");
const { DOMHelpers } = require("resource://devtools/client/shared/DOMHelpers.jsm");
const { Task } = require("devtools/shared/task");

const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const DEV_EDITION_PROMO_URL = "chrome://devtools/content/framework/dev-edition-promo/dev-edition-promo.xul";
const DEV_EDITION_PROMO_ENABLED_PREF = "devtools.devedition.promo.enabled";
const DEV_EDITION_PROMO_SHOWN_PREF = "devtools.devedition.promo.shown";
const DEV_EDITION_PROMO_URL_PREF = "devtools.devedition.promo.url";

/**
 * Only show Dev Edition promo if it's enabled (beta channel),
 * if it has not been shown before, and it's a locale build
 * for `en-US`
 */
function shouldDevEditionPromoShow() {
  return Services.prefs.getBoolPref(DEV_EDITION_PROMO_ENABLED_PREF) &&
         !Services.prefs.getBoolPref(DEV_EDITION_PROMO_SHOWN_PREF) &&
         Services.locale.getAppLocaleAsLangTag() === "en-US";
}

var TYPES = {
  // The Developer Edition promo doorhanger, called by
  // opening the toolbox, browser console, WebIDE, or responsive design mode
  // in Beta releases. Only displayed once per profile.
  deveditionpromo: {
    predicate: shouldDevEditionPromoShow,
    success: () => {
      return Services.prefs.setBoolPref(DEV_EDITION_PROMO_SHOWN_PREF, true);
    },
    action: () => {
      let url = Services.prefs.getCharPref(DEV_EDITION_PROMO_URL_PREF);
      getGBrowser().selectedTab = getGBrowser().addTab(url);
    },
    url: DEV_EDITION_PROMO_URL
  }
};

var panelAttrs = {
  orient: "vertical",
  hidden: "false",
  consumeoutsideclicks: "true",
  noautofocus: "true",
  align: "start",
  role: "alert"
};

/**
 * Helper to call a doorhanger, defined in `TYPES`, with defined conditions,
 * success handlers and loads its own XUL in a frame. Takes an object with
 * several properties:
 *
 * @param {XULWindow} window
 *        The window that should house the doorhanger.
 * @param {String} type
 *        The type of doorhanger to be displayed is, using the `TYPES`
 *        definition.
 * @param {String} selector
 *        The selector that the doorhanger should be appended to within
 *        `window`.  Defaults to a XUL Document's `window` element.
 */
exports.showDoorhanger = Task.async(function* ({ window, type, anchor }) {
  let { predicate, success, url, action } = TYPES[type];
  // Abort if predicate fails
  if (!predicate()) {
    return;
  }

  // Call success function to set preferences/cleanup immediately,
  // so if triggered multiple times, only happens once (Windows/Linux)
  success();

  // Wait 200ms to prevent flickering where the popup is displayed
  // before the underlying window (Windows 7, 64bit)
  yield wait(200);

  let document = window.document;

  let panel = document.createElementNS(XULNS, "panel");
  let frame = document.createElementNS(XULNS, "iframe");
  let parentEl = document.querySelector("window");

  frame.setAttribute("src", url);
  let close = () => parentEl.removeChild(panel);

  setDoorhangerStyle(panel, frame);

  panel.appendChild(frame);
  parentEl.appendChild(panel);

  yield onFrameLoad(frame);

  panel.openPopup(anchor);

  let closeBtn = frame.contentDocument.querySelector("#close");
  if (closeBtn) {
    closeBtn.addEventListener("click", close);
  }

  let goBtn = frame.contentDocument.querySelector("#go");
  if (goBtn) {
    goBtn.addEventListener("click", () => {
      if (action) {
        action();
      }
      close();
    });
  }
});

function setDoorhangerStyle(panel, frame) {
  Object.keys(panelAttrs).forEach(prop => {
    return panel.setAttribute(prop, panelAttrs[prop]);
  });
  panel.style.margin = "20px";
  panel.style.borderRadius = "5px";
  panel.style.border = "none";
  panel.style.MozAppearance = "none";
  panel.style.backgroundColor = "transparent";

  frame.style.borderRadius = "5px";
  frame.setAttribute("flex", "1");
  frame.setAttribute("width", "450");
  frame.setAttribute("height", "179");
}

function onFrameLoad(frame) {
  return new Promise((resolve, reject) => {
    if (frame.contentWindow) {
      let domHelper = new DOMHelpers(frame.contentWindow);
      domHelper.onceDOMReady(resolve);
    } else {
      let callback = () => {
        frame.removeEventListener("DOMContentLoaded", callback);
        resolve();
      };
      frame.addEventListener("DOMContentLoaded", callback);
    }
  });
}

function getGBrowser() {
  return Services.wm.getMostRecentWindow("navigator:browser").gBrowser;
}

function wait(n) {
  return new Promise((resolve, reject) => {
    setTimeout(resolve, n);
  });
}
PK
!<�q556chrome/devtools/modules/devtools/client/shared/enum.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 (let key of array) {
      target[key] = key;
    }
    return target;
  }

};
PK
!<[OͶ�<chrome/devtools/modules/devtools/client/shared/file-saver.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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";

/**
 * HTML5 file saver to provide a standard download interface with a "Save As"
 * dialog
 *
 * @param {object} blob - A blob object will be downloaded
 * @param {string} filename - Given a file name which will display in "Save As" dialog
 * @param {object} document - Optional. A HTML document for creating a temporary anchor
 *                            for triggering a file download.
 */
function saveAs(blob, filename = "", doc = document) {
  let url = URL.createObjectURL(blob);
  let a = doc.createElement("a");
  doc.body.appendChild(a);
  a.style = "display: none";
  a.href = url;
  a.download = filename;
  a.click();
  URL.revokeObjectURL(url);
  a.remove();
}

exports.saveAs = saveAs;
PK
!<���*��9chrome/devtools/modules/devtools/client/shared/getjson.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Services = require("Services");

loader.lazyRequireGetter(this, "asyncStorage", "devtools/shared/async-storage");

/**
 * Downloads and caches a JSON file from an URL given by a pref.
 *
 * @param {String} prefName
 *        The preference for the target URL
 *
 * @return {Promise}
 *         - Resolved with the JSON object in case of successful request
 *           or cache hit
 *         - Rejected with an error message in case of failure
 */
exports.getJSON = function (prefName) {
  return new Promise((resolve, reject) => {
    let xhr = new XMLHttpRequest();

    // We used to store cached data in preferences, but now we use asyncStorage
    // Migration step: if it still exists, move this now useless preference in its
    // new location and clear it
    if (Services.prefs.prefHasUserValue(prefName + "_cache")) {
      let json = Services.prefs.getCharPref(prefName + "_cache");
      asyncStorage.setItem(prefName + "_cache", json).catch(function (e) {
        // Could not move the cache, let's log the error but continue
        console.error(e);
      });
      Services.prefs.clearUserPref(prefName + "_cache");
    }

    function readFromStorage(networkError) {
      asyncStorage.getItem(prefName + "_cache").then(function (json) {
        if (!json) {
          return reject("Empty cache for " + prefName);
        }
        return resolve(json);
      }).catch(function (e) {
        reject("JSON not available, CDN error: " + networkError +
                        ", storage error: " + e);
      });
    }

    xhr.onload = () => {
      try {
        let json = JSON.parse(xhr.responseText);
        asyncStorage.setItem(prefName + "_cache", json).catch(function (e) {
          // Could not update cache, let's log the error but continue
          console.error(e);
        });
        resolve(json);
      } catch (e) {
        readFromStorage(e);
      }
    };

    xhr.onerror = (e) => {
      readFromStorage(e);
    };

    xhr.open("get", Services.prefs.getCharPref(prefName));
    xhr.send();
  });
};
PK
!<�)7�����@chrome/devtools/modules/devtools/client/shared/inplace-editor.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=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/. */

/**
 * Basic use:
 * let spanToEdit = document.getElementById("somespan");
 *
 * editableField({
 *   element: spanToEdit,
 *   done: function(value, commit, direction) {
 *     if (commit) {
 *       spanToEdit.textContent = value;
 *     }
 *   },
 *   trigger: "dblclick"
 * });
 *
 * See editableField() for more options.
 */

"use strict";

const Services = require("Services");
const focusManager = Services.focus;
const {KeyCodes} = require("devtools/client/shared/keycodes");

loader.lazyRequireGetter(this, "system", "devtools/shared/system");

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 EventEmitter = require("devtools/shared/event-emitter");
const { findMostRelevantCssPropertyIndex } = require("./suggestion-picker");

/**
 * 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, including:
 *    {Element} element:
 *      (required) The span to be edited on focus.
 *    {Function} canEdit:
 *       Will be called before creating the inplace editor.  Editor
 *       won't be created if canEdit returns false.
 *    {Function} start:
 *       Will be called when the inplace editor is initialized.
 *    {Function} change:
 *       Will be called when the text input changes.  Will be called
 *       with the current value of the text input.
 *    {Function} done:
 *       Called when input is committed or blurred.  Called with
 *       current value, a boolean telling the caller whether to
 *       commit the change, and the direction of the next element to be
 *       selected. 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.
 *    {Function} destroy:
 *       Called when the editor is destroyed and has been torn down.
 *    {Function} contextMenu:
 *       Called when the user triggers a contextmenu event on the input.
 *    {Object} 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.
 *    {Boolean} stopOnReturn:
 *       If true, the return key will not advance the editor to the next
 *       focusable element.
 *    {Boolean} stopOnTab:
 *       If true, the tab key will not advance the editor to the next
 *       focusable element.
 *    {Boolean} stopOnShiftTab:
 *       If true, shift tab will not advance the editor to the previous
 *       focusable element.
 *    {String} trigger: The DOM event that should trigger editing,
 *      defaults to "click"
 *    {Boolean} multiline: Should the editor be a multiline textarea?
 *      defaults to false
 *    {Function or 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.
 *    {Boolean} trimOutput: Should the returned string be trimmed?
 *      defaults to true
 *    {Boolean} preserveTextStyles: If true, do not copy text-related styles
 *              from `element` to the new input.
 *      defaults to false
 *    {Object} cssProperties: An instance of CSSProperties.
 *    {Object} cssVariables: A Map object containing all CSS variables.
 *    {Number} defaultIncrement: The value by which the input is incremented
 *      or decremented by default (0.1 for properties like opacity and 1 by default)
 */
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) {
  let trigger = options.trigger || "click";
  let element = options.element;
  element.addEventListener(trigger, function (evt) {
    if (evt.target.nodeName !== "a") {
      let win = this.ownerDocument.defaultView;
      let 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 (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") {
      let 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;

function InplaceEditor(options, event) {
  this.elt = options.element;
  let 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._onBlur = this._onBlur.bind(this);
  this._onWindowBlur = this._onWindowBlur.bind(this);
  this._onKeyPress = this._onKeyPress.bind(this);
  this._onInput = this._onInput.bind(this);
  this._onKeyup = this._onKeyup.bind(this);
  this._onAutocompletePopupClick = this._onAutocompletePopupClick.bind(this);
  this._onContextMenu = this._onContextMenu.bind(this);

  this._createInput();

  // 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 {
    let advanceCharcodes = {};
    let 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();
  }

  if (this.contentType == CONTENT_TYPES.CSS_VALUE && this.input.value == "") {
    this._maybeSuggestCompletion(false);
  }

  this.input.addEventListener("blur", this._onBlur);
  this.input.addEventListener("keypress", this._onKeyPress);
  this.input.addEventListener("input", this._onInput);
  this.input.addEventListener("dblclick", this._stopEventPropagation);
  this.input.addEventListener("click", this._stopEventPropagation);
  this.input.addEventListener("mousedown", this._stopEventPropagation);
  this.input.addEventListener("contextmenu", this._onContextMenu);
  this.doc.defaultView.addEventListener("blur", this._onWindowBlur);

  this.validate = options.validate;

  if (this.validate) {
    this.input.addEventListener("keyup", this._onKeyup);
  }

  this._updateSize();

  EventEmitter.decorate(this);

  if (options.start) {
    options.start(this, event);
  }
}

exports.InplaceEditor = InplaceEditor;

InplaceEditor.CONTENT_TYPES = CONTENT_TYPES;

InplaceEditor.prototype = {

  get currentInputValue() {
    let val = this.trimOutput ? this.input.value.trim() : this.input.value;
    return val;
  },

  _createInput: function () {
    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";
    }

    this.input.classList.add("styleinspector-propertyeditor");
    this.input.value = this.initial;
    if (!this.preserveTextStyles) {
      copyTextStyles(this.elt, this.input);
    }
  },

  /**
   * Get rid of the editor.
   */
  _clear: function () {
    if (!this.input) {
      // Already cleared.
      return;
    }

    this.input.removeEventListener("blur", this._onBlur);
    this.input.removeEventListener("keypress", this._onKeyPress);
    this.input.removeEventListener("keyup", this._onKeyup);
    this.input.removeEventListener("input", this._onInput);
    this.input.removeEventListener("dblclick", this._stopEventPropagation);
    this.input.removeEventListener("click", this._stopEventPropagation);
    this.input.removeEventListener("mousedown", this._stopEventPropagation);
    this.input.removeEventListener("contextmenu", this._onContextMenu);
    this.doc.defaultView.removeEventListener("blur", this._onWindowBlur);

    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: function () {
    // 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);
    let 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: function () {
    if (!this._measurement) {
      return;
    }
    this._measurement.remove();
    delete this._measurement;
  },

  /**
   * Size the editor to fit its current contents.
   */
  _updateSize: function () {
    // 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;
    let 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 + 2;
    if (this.multiline) {
      if (this.maxWidth) {
        width = Math.min(this.maxWidth, width);
      }
      let 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: function () {
    // 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";
    let width = this._measurement.clientWidth;
    let 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: function (increment) {
    let value = this.input.value;
    let selectionStart = this.input.selectionStart;
    let selectionEnd = this.input.selectionEnd;

    let 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: function (value, increment, selStart, selEnd) {
    let range = this._parseCSSValue(value, selStart);
    let type = (range && range.type) || "";
    let rawValue = range ? value.substring(range.start, range.end) : "";
    let preRawValue = range ? value.substr(0, range.start) : "";
    let 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);
      }

      let newValue = this._incrementRawValue(rawValue, increment, info);
      if (newValue !== null) {
        incrementedValue = newValue;
        selection = [0, incrementedValue.length];
      }
    } else if (type === "hex") {
      let exprOffset = selStart - range.start;
      let exprOffsetEnd = selEnd - range.start;
      let newValue = this._incHexColor(rawValue, increment, exprOffset,
                                       exprOffsetEnd);
      if (newValue) {
        incrementedValue = newValue.value;
        selection = newValue.selection;
      }
    } else {
      if (type === "rgb" || type === "hsl") {
        info = {};
        let part = value.substring(range.start, selStart).split(",").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 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: function (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.
    let el = this.doc.createElement("div");
    let units = ["px", "deg", "s"];
    for (let unit of units) {
      let value = beforeValue + "1" + unit + afterValue;
      el.style.setProperty(this.property.name, "");
      el.style.setProperty(this.property.name, value);
      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: function (value, offset) {
    /* eslint-disable max-len */
    const reSplitCSS = /(url\("?[^"\)]+"?\)?)|(rgba?\([^)]*\)?)|(hsla?\([^)]*\)?)|(#[\dA-Fa-f]+)|(-?\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.substr(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[1]) {
      type = "url";
    } else if (m[2]) {
      type = "rgb";
    } else if (m[3]) {
      type = "hsl";
    } else if (m[4]) {
      type = "hex";
    } else if (m[5]) {
      type = "num";
    }

    return {
      value: m[0],
      start: start + m.index,
      end: start + m.index + m[0].length,
      type: type
    };
  },

  /**
   * Increment the property value for types other than
   * number or hex, such as rgb, hsl, 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: function (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.)
      let pattern = "[" + (info ? "0-9." : "0-9") + "]*";
      let before = new RegExp(pattern + "$")
        .exec(value.substr(0, offset))[0].length;
      let 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;
      }

      let first = value.substr(0, start);
      let mid = value.substring(start, end);
      let last = value.substr(end);

      mid = this._incrementRawValue(mid, increment, info);

      if (mid !== null) {
        return {
          value: first + mid + last,
          start: 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: function (rawValue, increment, info) {
    let num = parseFloat(rawValue);

    if (isNaN(num)) {
      return null;
    }

    let 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: function (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);
    }

    let 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);
      let 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: function (reverse, noSelect) {
    // selectedItem can be null when nothing is selected in an empty editor.
    let {label, preLabel} = this.popup.selectedItem ||
                            {label: "", preLabel: ""};
    if (reverse) {
      this.popup.selectPreviousItem();
    } else {
      this.popup.selectNextItem();
    }

    this._selectedIndex = this.popup.selectedIndex;
    let 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);
    }

    let post = input.value.slice(input.selectionEnd, input.value.length);
    let item = this.popup.selectedItem;
    let 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: function (event, direction) {
    if (this._applied) {
      return null;
    }

    this._applied = true;

    if (this.done) {
      let val = this.cancelled ? this.initial : this.currentInputValue;
      return this.done(val, !this.cancelled, direction);
    }

    return null;
  },

  /**
   * Hide the popup and cancel any pending popup opening.
   */
  _onWindowBlur: function () {
    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: function (event) {
    if (event && this.popup && this.popup.isOpen &&
      this.popup.selectedIndex >= 0) {
      this._acceptPopupSuggestion();
    } else {
      this._apply();
      this._clear();
    }
  },

  /**
   * Event handler called by the autocomplete popup when receiving a click
   * event.
   */
  _onAutocompletePopupClick: function () {
    this._acceptPopupSuggestion();
  },

  _acceptPopupSuggestion: function () {
    let label, preLabel;

    if (this._selectedIndex === undefined) {
      ({label, preLabel} = this.popup.getItemAtIndex(this.popup.selectedIndex));
    } else {
      ({label, preLabel} = this.popup.getItemAtIndex(this._selectedIndex));
    }

    let 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);
    }
    let post = input.value.slice(input.selectionEnd, input.value.length);
    let item = this.popup.selectedItem;
    this._selectedIndex = this.popup.selectedIndex;
    let 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.
    let 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.
   */
  _onKeyPress: function (event) {
    let prevent = false;

    let key = event.keyCode;
    let input = this.input;

    let multilineNavigation = !this._isSingleLine() &&
      isKeyIn(key, "UP", "DOWN", "LEFT", "RIGHT");
    let isPlainText = this.contentType == CONTENT_TYPES.PLAIN_TEXT;
    let 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._hideAutocompletePopup();
      }
    } else if (!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;

      let direction;
      if ((this.stopOnReturn && isKeyIn(key, "RETURN")) ||
          (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(event, direction);

      // 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.
        let next = moveFocus(this.doc.defaultView, direction);

        // 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) {
          let 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: function (event) {
    if (this.contextMenu) {
      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: function (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: function () {
    this.popup.off("popup-click", this._onAutocompletePopupClick);
    this.popup.hidePopup();
  },

  /**
   * Get the increment/decrement step to use for the provided key event.
   */
  _getIncrement: function (event) {
    const getSmallIncrementKey = (evt) => {
      if (system.constants.platform === "macosx") {
        return evt.altKey;
      }
      return evt.ctrlKey;
    };

    const largeIncrement = 100;
    const mediumIncrement = 10;
    const smallIncrement = 0.1;

    let increment = 0;
    let 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: function () {
    this._applied = false;
  },

  /**
   * Handle changes to the input text.
   */
  _onInput: function () {
    // 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);
    }
  },

  /**
   * Stop propagation on the provided event
   */
  _stopEventPropagation: function (e) {
    e.stopPropagation();
  },

  /**
   * Fire validation callback with current input
   */
  _doValidation: function () {
    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: function (autoInsert) {
    // Input can be null in cases when you intantaneously switch out of it.
    if (!this.input) {
      return;
    }
    let 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.
    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;
      }
      let input = this.input;
      // The length of input.value should be increased by 1
      if (input.value.length - preTimeoutQuery.length > 1) {
        return;
      }
      let 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) {
        let 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 = [];
      if (this.contentType == CONTENT_TYPES.CSS_PROPERTY) {
        list = this._getCSSPropertyList();
      } else if (this.contentType == CONTENT_TYPES.CSS_VALUE) {
        // Get the last query to be completed before the caret.
        let match = /([^\s,.\/]+$)/.exec(query);
        if (match) {
          startCheckQuery = match[0];
        } else {
          startCheckQuery = "";
        }

        // Check if the query to be completed is a CSS variable.
        let varMatch = /^var\(([^\s]+$)/.exec(startCheckQuery);

        if (varMatch && varMatch.length == 2) {
          startCheckQuery = varMatch[1];
          list = this._getCSSVariableNames();
        } 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.
        let 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;
        let match = query.match(/([:;"'=]?)\s*([^"';:=]+)?$/);
        if (match && match.length >= 2) {
          if (match[1] == ":") {
            // We are in CSS value completion
            let propertyName =
              query.match(/[;"'=]\s*([^"';:= ]+)\s*:\s*[^"';:=]*$/)[1];
            list = ["!important;",
                    ...this._getCSSValuesForPropertyName(propertyName)];
            let 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._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;
      }

      let finalList = [];
      let 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]
          });
        } 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.
        let 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]) {
        let 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.
        let 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
        let selectedIndex = autoInsert ? index : -1;

        // Open the suggestions popup.
        this.popup.setItems(finalList);
        this._openAutocompletePopup(offset, selectedIndex);
      } else {
        this._hideAutocompletePopup();
      }
      // This emit is mainly for the purpose of making the test flow simpler.
      this.emit("after-suggest");
      this._doValidation();
    }, 0);
  },

  /**
   * 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: function () {
    let 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: function () {
    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: function (propertyName) {
    return this.cssProperties.getValues(propertyName);
  },

  /**
   * Returns the list of all CSS variables to use for the autocompletion.
   *
   * @return {Array} array of CSS variable names (Strings)
   */
  _getCSSVariableNames: function () {
    return Array.from(this.cssVariables.keys()).sort();
  },
};

/**
 * Copy text-related styles from one element to another.
 */
function copyTextStyles(from, to) {
  let win = from.ownerDocument.defaultView;
  let 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) {
  let win = from.ownerDocument.defaultView;
  let 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.
  let 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) {
  let properties = [
    // Copy all paddings.
    "paddingTop",
    "paddingRight",
    "paddingBottom",
    "paddingLeft",
    // Copy border styles.
    "borderTopStyle",
    "borderRightStyle",
    "borderBottomStyle",
    "borderLeftStyle",
    // Copy border widths.
    "borderTopWidth",
    "borderRightWidth",
    "borderBottomWidth",
    "borderLeftWidth"
  ];

  let win = from.ownerDocument.defaultView;
  let style = win.getComputedStyle(from);
  for (let property of properties) {
    to.style[property] = style[property];
  }
}

/**
 * Trigger a focus change similar to pressing tab/shift-tab.
 */
function moveFocus(win, direction) {
  return focusManager.moveFocus(win, null, direction, 0);
}
PK
!<G]�I%%?chrome/devtools/modules/devtools/client/shared/key-shortcuts.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Services = require("Services");
const EventEmitter = require("devtools/shared/event-emitter");
const isOSX = Services.appinfo.OS === "Darwin";
const {KeyCodes} = require("devtools/client/shared/keycodes");

// 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 decribed 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) {
  let modifiers = str.split("+");
  let key = modifiers.pop();

  let shortcut = {
    ctrl: false,
    meta: false,
    alt: false,
    shift: false,
    // Set for character keys
    key: undefined,
    // Set for non-character keys
    keyCode: undefined,
  };
  for (let 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) {
    // 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) {
  let 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("+");
};

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) {
      // Shift is a special modifier, it may implicitely be required if the expected key
      // is a special character accessible via shift.
      let isAlphabetical = event.key && event.key.match(/[a-zA-Z]/);
      // OSX: distinguish cmd+[key] from cmd+shift+[key] shortcuts (Bug 1300458)
      let 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.
    let 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 (let [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)) {
      let 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;
PK
!<Y���
�
:chrome/devtools/modules/devtools/client/shared/keycodes.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 was copied (and slightly modified) from
// devtools/shared/gcli/source/lib/gcli/util/util.js, which in turn
// says:

/**
 * 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: GCLI's 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,

  // A few that did not appear in gcli, but that are apparently used
  // in devtools.
  DOM_VK_COLON: 58,
  DOM_VK_VOLUME_MUTE: 181,
  DOM_VK_VOLUME_DOWN: 182,
  DOM_VK_VOLUME_UP: 183,
};
PK
!<��`�M
M
>chrome/devtools/modules/devtools/client/shared/natural-sort.js/*
 * Natural Sort algorithm for Javascript - Version 0.8.1 - Released under MIT license
 * Author: Jim Palmer (based on chunking idea from Dave Koelle)
 *
 * Includes pull request to move regexes out of main function for performance
 * increases.
 *
 * Repository:
 *   https://github.com/overset/javascript-natural-sort/
 */

"use strict";

var re = /(^([+\-]?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?(?=\D|\s|$))|^0x[\da-fA-F]+$|\d+)/g;
var sre = /^\s+|\s+$/g; // trim pre-post whitespace
var snre = /\s+/g; // normalize all whitespace to single ' ' character

// eslint-disable-next-line
var dre = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/;
var hre = /^0x[0-9a-f]+$/i;
var ore = /^0/;
var b0re = /^\0/;
var e0re = /\0$/;

exports.naturalSortCaseSensitive =
function naturalSortCaseSensitive(a, b) {
  return naturalSort(a, b, false);
};

exports.naturalSortCaseInsensitive =
function naturalSortCaseInsensitive(a, b) {
  return naturalSort(a, b, true);
};

/**
 * Sort numbers, strings, IP Addresses, Dates, Filenames, version numbers etc.
 * "the way humans do."
 *
 * This function should only be called via naturalSortCaseSensitive and
 * naturalSortCaseInsensitive.
 *
 * e.g. [3, 2, 1, 10].sort(naturalSort)
 *
 * @param  {Object} a
 *         Passed in by Array.sort(a, b)
 * @param  {Object} b
 *         Passed in by Array.sort(a, b)
 * @param  {Boolean} insensitive
 *         Should the search be case insensitive?
 */
function naturalSort(a, b, insensitive) {
  // convert all to strings strip whitespace
  let i = function (s) {
    return (insensitive && ("" + s).toLowerCase() || "" + s)
                                   .replace(sre, "");
  };
  let x = i(a) || "";
  let y = i(b) || "";
  // chunk/tokenize
  let xN = x.replace(re, "\0$1\0").replace(e0re, "").replace(b0re, "").split("\0");
  let yN = y.replace(re, "\0$1\0").replace(e0re, "").replace(b0re, "").split("\0");
  // numeric, hex or date detection
  let xD = parseInt(x.match(hre), 16) || (xN.length !== 1 && Date.parse(x));
  let yD = parseInt(y.match(hre), 16) || xD && y.match(dre) && Date.parse(y) || null;
  let normChunk = function (s, l) {
    // normalize spaces; find floats not starting with '0', string or 0 if
    // not defined (Clint Priest)
    return (!s.match(ore) || l == 1) &&
           parseFloat(s) || s.replace(snre, " ").replace(sre, "") || 0;
  };
  let oFxNcL;
  let oFyNcL;

  // first try and sort Hex codes or Dates
  if (yD) {
    if (xD < yD) {
      return -1;
    } else if (xD > yD) {
      return 1;
    }
  }

  // natural sorting through split numeric strings and default strings
  // eslint-disable-next-line
  for (let cLoc = 0, xNl = xN.length, yNl = yN.length, numS = Math.max(xNl, yNl); cLoc < numS; cLoc++) {
    oFxNcL = normChunk(xN[cLoc] || "", xNl);
    oFyNcL = normChunk(yN[cLoc] || "", yNl);

    // handle numeric vs string comparison - number < string - (Kyle Adams)
    if (isNaN(oFxNcL) !== isNaN(oFyNcL)) {
      return isNaN(oFxNcL) ? 1 : -1;
    }
    // if unicode use locale comparison
    // eslint-disable-next-line
    if (/[^\x00-\x80]/.test(oFxNcL + oFyNcL) && oFxNcL.localeCompare) {
      let comp = oFxNcL.localeCompare(oFyNcL);
      return comp / Math.abs(comp);
    }
    if (oFxNcL < oFyNcL) {
      return -1;
    } else if (oFxNcL > oFyNcL) {
      return 1;
    }
  }
  return null;
}
PK
!<v!����Mchrome/devtools/modules/devtools/client/shared/network-throttling-profiles.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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;

/**
 * Predefined network throttling profiles.
 * Speeds are in bytes per second.  Latency is in ms.
 */
/* eslint-disable key-spacing */
module.exports = [
  {
    id:          "GPRS",
    download:    50 * KBps,
    upload:      20 * KBps,
    latency:     500,
  },
  {
    id:          "Regular 2G",
    download:    250 * KBps,
    upload:      50 * KBps,
    latency:     300,
  },
  {
    id:          "Good 2G",
    download:    450 * KBps,
    upload:      150 * KBps,
    latency:     150,
  },
  {
    id:          "Regular 3G",
    download:    750 * KBps,
    upload:      250 * KBps,
    latency:     100,
  },
  {
    id:          "Good 3G",
    download:    1.5 * MBps,
    upload:      750 * KBps,
    latency:     40,
  },
  {
    id:          "Regular 4G / LTE",
    download:    4 * MBps,
    upload:      3 * MBps,
    latency:     20,
  },
  {
    id:          "DSL",
    download:    2 * MBps,
    upload:      1 * MBps,
    latency:     5,
  },
  {
    id:          "Wi-Fi",
    download:    30 * MBps,
    upload:      15 * MBps,
    latency:     2,
  },
];
/* eslint-enable key-spacing */
PK
!<"⶛�3�3Gchrome/devtools/modules/devtools/client/shared/node-attribute-parser.js/* -*- indent-tabs-mode: nil; js-indent-level: 2; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

/**
 * 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";

/* eslint-disable max-len */
const ATTRIBUTE_TYPES = [
  {namespaceURI: HTML_NS, attributeName: "action", tagName: "form", type: TYPE_URI},
  {namespaceURI: HTML_NS, attributeName: "background", tagName: "body", type: TYPE_URI},
  {namespaceURI: HTML_NS, attributeName: "cite", tagName: "blockquote", type: TYPE_URI},
  {namespaceURI: HTML_NS, attributeName: "cite", tagName: "q", type: TYPE_URI},
  {namespaceURI: HTML_NS, attributeName: "cite", tagName: "del", type: TYPE_URI},
  {namespaceURI: HTML_NS, attributeName: "cite", tagName: "ins", type: TYPE_URI},
  {namespaceURI: HTML_NS, attributeName: "classid", tagName: "object", type: TYPE_URI},
  {namespaceURI: HTML_NS, attributeName: "codebase", tagName: "object", type: TYPE_URI},
  {namespaceURI: HTML_NS, attributeName: "codebase", tagName: "applet", type: TYPE_URI},
  {namespaceURI: HTML_NS, attributeName: "command", tagName: "menuitem", type: TYPE_IDREF},
  {namespaceURI: "*", attributeName: "contextmenu", tagName: "*", type: TYPE_IDREF},
  {namespaceURI: HTML_NS, attributeName: "data", tagName: "object", type: TYPE_URI},
  {namespaceURI: HTML_NS, attributeName: "for", tagName: "label", type: TYPE_IDREF},
  {namespaceURI: HTML_NS, attributeName: "for", tagName: "output", type: TYPE_IDREF_LIST},
  {namespaceURI: HTML_NS, attributeName: "form", tagName: "button", type: TYPE_IDREF},
  {namespaceURI: HTML_NS, attributeName: "form", tagName: "fieldset", type: TYPE_IDREF},
  {namespaceURI: HTML_NS, attributeName: "form", tagName: "input", type: TYPE_IDREF},
  {namespaceURI: HTML_NS, attributeName: "form", tagName: "keygen", type: TYPE_IDREF},
  {namespaceURI: HTML_NS, attributeName: "form", tagName: "label", type: TYPE_IDREF},
  {namespaceURI: HTML_NS, attributeName: "form", tagName: "object", type: TYPE_IDREF},
  {namespaceURI: HTML_NS, attributeName: "form", tagName: "output", type: TYPE_IDREF},
  {namespaceURI: HTML_NS, attributeName: "form", tagName: "select", type: TYPE_IDREF},
  {namespaceURI: HTML_NS, attributeName: "form", tagName: "textarea", type: TYPE_IDREF},
  {namespaceURI: HTML_NS, attributeName: "formaction", tagName: "button", type: TYPE_URI},
  {namespaceURI: HTML_NS, attributeName: "formaction", tagName: "input", type: TYPE_URI},
  {namespaceURI: HTML_NS, attributeName: "headers", tagName: "td", type: TYPE_IDREF_LIST},
  {namespaceURI: HTML_NS, attributeName: "headers", tagName: "th", type: TYPE_IDREF_LIST},
  {namespaceURI: HTML_NS, attributeName: "href", tagName: "a", type: TYPE_URI},
  {namespaceURI: HTML_NS, attributeName: "href", tagName: "area", type: TYPE_URI},
  {namespaceURI: "*", attributeName: "href", tagName: "link", type: TYPE_CSS_RESOURCE_URI,
  /* eslint-enable */
   isValid: (namespaceURI, tagName, attributes) => {
     return getAttribute(attributes, "rel") === "stylesheet";
   }},
  /* eslint-disable max-len */
  {namespaceURI: "*", attributeName: "href", tagName: "link", type: TYPE_URI},
  {namespaceURI: HTML_NS, attributeName: "href", tagName: "base", type: TYPE_URI},
  {namespaceURI: HTML_NS, attributeName: "icon", tagName: "menuitem", type: TYPE_URI},
  {namespaceURI: HTML_NS, attributeName: "list", tagName: "input", type: TYPE_IDREF},
  {namespaceURI: HTML_NS, attributeName: "longdesc", tagName: "img", type: TYPE_URI},
  {namespaceURI: HTML_NS, attributeName: "longdesc", tagName: "frame", type: TYPE_URI},
  {namespaceURI: HTML_NS, attributeName: "longdesc", tagName: "iframe", type: TYPE_URI},
  {namespaceURI: HTML_NS, attributeName: "manifest", tagName: "html", type: TYPE_URI},
  {namespaceURI: HTML_NS, attributeName: "menu", tagName: "button", type: TYPE_IDREF},
  {namespaceURI: HTML_NS, attributeName: "ping", tagName: "a", type: TYPE_URI_LIST},
  {namespaceURI: HTML_NS, attributeName: "ping", tagName: "area", type: TYPE_URI_LIST},
  {namespaceURI: HTML_NS, attributeName: "poster", tagName: "video", type: TYPE_URI},
  {namespaceURI: HTML_NS, attributeName: "profile", tagName: "head", type: TYPE_URI},
  {namespaceURI: "*", attributeName: "src", tagName: "script", type: TYPE_JS_RESOURCE_URI},
  {namespaceURI: HTML_NS, attributeName: "src", tagName: "input", type: TYPE_URI},
  {namespaceURI: HTML_NS, attributeName: "src", tagName: "frame", type: TYPE_URI},
  {namespaceURI: HTML_NS, attributeName: "src", tagName: "iframe", type: TYPE_URI},
  {namespaceURI: HTML_NS, attributeName: "src", tagName: "img", type: TYPE_URI},
  {namespaceURI: HTML_NS, attributeName: "src", tagName: "audio", type: TYPE_URI},
  {namespaceURI: HTML_NS, attributeName: "src", tagName: "embed", type: TYPE_URI},
  {namespaceURI: HTML_NS, attributeName: "src", tagName: "source", type: TYPE_URI},
  {namespaceURI: HTML_NS, attributeName: "src", tagName: "track", type: TYPE_URI},
  {namespaceURI: HTML_NS, attributeName: "src", tagName: "video", type: TYPE_URI},
  {namespaceURI: HTML_NS, attributeName: "usemap", tagName: "img", type: TYPE_URI},
  {namespaceURI: HTML_NS, attributeName: "usemap", tagName: "input", type: TYPE_URI},
  {namespaceURI: HTML_NS, attributeName: "usemap", tagName: "object", type: TYPE_URI},
  {namespaceURI: "*", attributeName: "xmlns", tagName: "*", type: TYPE_URI},
  {namespaceURI: XUL_NS, attributeName: "command", tagName: "key", type: TYPE_IDREF},
  {namespaceURI: XUL_NS, attributeName: "containment", tagName: "*", type: TYPE_URI},
  {namespaceURI: XUL_NS, attributeName: "context", tagName: "*", type: TYPE_IDREF},
  {namespaceURI: XUL_NS, attributeName: "datasources", tagName: "*", type: TYPE_URI_LIST},
  {namespaceURI: XUL_NS, attributeName: "insertafter", tagName: "*", type: TYPE_IDREF},
  {namespaceURI: XUL_NS, attributeName: "insertbefore", tagName: "*", type: TYPE_IDREF},
  {namespaceURI: XUL_NS, attributeName: "menu", tagName: "*", type: TYPE_IDREF},
  {namespaceURI: XUL_NS, attributeName: "observes", tagName: "*", type: TYPE_IDREF},
  {namespaceURI: XUL_NS, attributeName: "popup", tagName: "*", type: TYPE_IDREF},
  {namespaceURI: XUL_NS, attributeName: "ref", tagName: "*", type: TYPE_URI},
  {namespaceURI: XUL_NS, attributeName: "removeelement", tagName: "*", type: TYPE_IDREF},
  {namespaceURI: XUL_NS, attributeName: "src", tagName: "stringbundle", type: TYPE_URI},
  {namespaceURI: XUL_NS, attributeName: "template", tagName: "*", type: TYPE_IDREF},
  {namespaceURI: XUL_NS, attributeName: "tooltip", tagName: "*", type: TYPE_IDREF},
  /* eslint-enable */
  // SVG links aren't handled yet, see bug 1158831.
  // {namespaceURI: SVG_NS, attributeName: "fill", tagName: "*", type: },
  // {namespaceURI: SVG_NS, attributeName: "stroke", tagName: "*", type: },
  // {namespaceURI: SVG_NS, attributeName: "markerstart", tagName: "*", type: },
  // {namespaceURI: SVG_NS, attributeName: "markermid", tagName: "*", type: },
  // {namespaceURI: SVG_NS, attributeName: "markerend", tagName: "*", type: },
  // {namespaceURI: SVG_NS, attributeName: "xlink:href", tagName: "*", type: }
];

var parsers = {
  [TYPE_URI]: function (attributeValue) {
    return [{
      type: TYPE_URI,
      value: attributeValue
    }];
  },
  [TYPE_URI_LIST]: function (attributeValue) {
    let data = splitBy(attributeValue, " ");
    for (let token of data) {
      if (!token.type) {
        token.type = TYPE_URI;
      }
    }
    return data;
  },
  [TYPE_JS_RESOURCE_URI]: function (attributeValue) {
    return [{
      type: TYPE_JS_RESOURCE_URI,
      value: attributeValue
    }];
  },
  [TYPE_CSS_RESOURCE_URI]: function (attributeValue) {
    return [{
      type: TYPE_CSS_RESOURCE_URI,
      value: attributeValue
    }];
  },
  [TYPE_IDREF]: function (attributeValue) {
    return [{
      type: TYPE_IDREF,
      value: attributeValue
    }];
  },
  [TYPE_IDREF_LIST]: function (attributeValue) {
    let data = splitBy(attributeValue, " ");
    for (let 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.
 * @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) {
  if (!hasAttribute(attributes, attributeName)) {
    throw new Error(`Attribute ${attributeName} isn't part of the ` +
                    "provided attributes");
  }

  let type = getType(namespaceURI, tagName, attributes, attributeName);
  if (!type) {
    return [{
      type: TYPE_STRING,
      value: getAttribute(attributes, attributeName)
    }];
  }

  return parsers[type](getAttribute(attributes, attributeName));
}

/**
 * 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) {
  for (let typeData of ATTRIBUTE_TYPES) {
    let containsAttribute = attributeName === typeData.attributeName ||
                            typeData.attributeName === "*";
    let hasNamespace = namespaceURI === typeData.namespaceURI ||
                       typeData.namespaceURI === "*";
    let hasTagName = tagName.toLowerCase() === typeData.tagName ||
                     typeData.tagName === "*";
    let isValid = typeData.isValid
                  ? typeData.isValid(namespaceURI,
                                     tagName,
                                     attributes,
                                     attributeName)
                  : true;

    if (containsAttribute && hasNamespace && hasTagName && isValid) {
      return typeData.type;
    }
  }

  return null;
}

function getAttribute(attributes, attributeName) {
  for (let {name, value} of attributes) {
    if (name === attributeName) {
      return value;
    }
  }
  return null;
}

function hasAttribute(attributes, attributeName) {
  for (let {name} of attributes) {
    if (name === attributeName) {
      return true;
    }
  }
  return false;
}

/**
 * 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) {
  let data = [], 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;
// Exported for testing only.
exports.splitBy = splitBy;
PK
!<>chrome/devtools/modules/devtools/client/shared/options-view.js"use strict";

const EventEmitter = require("devtools/shared/event-emitter");
const Services = require("Services");
const { Preferences } = require("resource://gre/modules/Preferences.jsm");
const OPTIONS_SHOWN_EVENT = "options-shown";
const OPTIONS_HIDDEN_EVENT = "options-hidden";
const PREF_CHANGE_EVENT = "pref-changed";

/**
 * OptionsView constructor. Takes several options, all required:
 * - branchName: The name of the prefs branch, like "devtools.debugger."
 * - menupopup: The XUL `menupopup` item that contains the pref buttons.
 *
 * Fires an event, PREF_CHANGE_EVENT, with the preference name that changed as
 * the second argument. Fires events on opening/closing the XUL panel
 * (OPTIONS_SHOW_EVENT, OPTIONS_HIDDEN_EVENT) as the second argument in the
 * listener, used for tests mostly.
 */
const OptionsView = function (options = {}) {
  this.branchName = options.branchName;
  this.menupopup = options.menupopup;
  this.window = this.menupopup.ownerDocument.defaultView;
  let { document } = this.window;
  this.$ = document.querySelector.bind(document);
  this.$$ = (selector, parent = document) => parent.querySelectorAll(selector);
  // Get the corresponding button that opens the popup by looking
  // for an element with a `popup` attribute matching the menu's ID
  this.button = this.$(`[popup=${this.menupopup.getAttribute("id")}]`);

  this.prefObserver = new PrefObserver(this.branchName);

  EventEmitter.decorate(this);
};
exports.OptionsView = OptionsView;

OptionsView.prototype = {
  /**
   * Binds the events and observers for the OptionsView.
   */
  initialize: function () {
    let { MutationObserver } = this.window;
    this._onPrefChange = this._onPrefChange.bind(this);
    this._onOptionChange = this._onOptionChange.bind(this);
    this._onPopupShown = this._onPopupShown.bind(this);
    this._onPopupHidden = this._onPopupHidden.bind(this);

    // We use a mutation observer instead of a click handler
    // because the click handler is fired before the XUL menuitem updates its
    // checked status, which cascades incorrectly with the Preference observer.
    this.mutationObserver = new MutationObserver(this._onOptionChange);
    let observerConfig = { attributes: true, attributeFilter: ["checked"]};

    // Sets observers and default options for all options
    for (let $el of this.$$("menuitem", this.menupopup)) {
      let prefName = $el.getAttribute("data-pref");

      if (this.prefObserver.get(prefName)) {
        $el.setAttribute("checked", "true");
      } else {
        $el.removeAttribute("checked");
      }
      this.mutationObserver.observe($el, observerConfig);
    }

    // Listen to any preference change in the specified branch
    this.prefObserver.register();
    this.prefObserver.on(PREF_CHANGE_EVENT, this._onPrefChange);

    // Bind to menupopup's open and close event
    this.menupopup.addEventListener("popupshown", this._onPopupShown);
    this.menupopup.addEventListener("popuphidden", this._onPopupHidden);
  },

  /**
   * Removes event handlers for all of the option buttons and
   * preference observer.
   */
  destroy: function () {
    this.mutationObserver.disconnect();
    this.prefObserver.off(PREF_CHANGE_EVENT, this._onPrefChange);
    this.menupopup.removeEventListener("popupshown", this._onPopupShown);
    this.menupopup.removeEventListener("popuphidden", this._onPopupHidden);
  },

  /**
   * Returns the value for the specified `prefName`
   */
  getPref: function (prefName) {
    return this.prefObserver.get(prefName);
  },

  /**
   * Called when a preference is changed (either via clicking an option
   * button or by changing it in about:config). Updates the checked status
   * of the corresponding button.
   */
  _onPrefChange: function (prefName) {
    let $el = this.$(`menuitem[data-pref="${prefName}"]`, this.menupopup);
    let value = this.prefObserver.get(prefName);

    // If options panel does not contain a menuitem for the
    // pref, emit an event and do nothing.
    if (!$el) {
      this.emit(PREF_CHANGE_EVENT, prefName);
      return;
    }

    if (value) {
      $el.setAttribute("checked", value);
    } else {
      $el.removeAttribute("checked");
    }

    this.emit(PREF_CHANGE_EVENT, prefName);
  },

  /**
   * Mutation handler for handling a change on an options button.
   * Sets the preference accordingly.
   */
  _onOptionChange: function (mutations) {
    let { target } = mutations[0];
    let prefName = target.getAttribute("data-pref");
    let value = target.getAttribute("checked") === "true";

    this.prefObserver.set(prefName, value);
  },

  /**
   * Fired when the `menupopup` is opened, bound via XUL.
   * Fires an event used in tests.
   */
  _onPopupShown: function () {
    this.button.setAttribute("open", true);
    this.emit(OPTIONS_SHOWN_EVENT);
  },

  /**
   * Fired when the `menupopup` is closed, bound via XUL.
   * Fires an event used in tests.
   */
  _onPopupHidden: function () {
    this.button.removeAttribute("open");
    this.emit(OPTIONS_HIDDEN_EVENT);
  }
};

/**
 * Constructor for PrefObserver. Small helper for observing changes
 * on a preference branch. Takes a `branchName`, like "devtools.debugger."
 *
 * Fires an event of PREF_CHANGE_EVENT with the preference name that changed
 * as the second argument in the listener.
 */
const PrefObserver = function (branchName) {
  this.branchName = branchName;
  this.branch = Services.prefs.getBranch(branchName);
  EventEmitter.decorate(this);
};

PrefObserver.prototype = {
  /**
   * Returns `prefName`'s value. Does not require the branch name.
   */
  get: function (prefName) {
    let fullName = this.branchName + prefName;
    return Preferences.get(fullName);
  },
  /**
   * Sets `prefName`'s `value`. Does not require the branch name.
   */
  set: function (prefName, value) {
    let fullName = this.branchName + prefName;
    Preferences.set(fullName, value);
  },
  register: function () {
    this.branch.addObserver("", this);
  },
  unregister: function () {
    this.branch.removeObserver("", this);
  },
  observe: function (subject, topic, prefName) {
    this.emit(PREF_CHANGE_EVENT, prefName);
  }
};
PK
!<�R������?chrome/devtools/modules/devtools/client/shared/output-parser.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Services = require("Services");
const {angleUtils} = require("devtools/client/shared/css-angle");
const {colorUtils} = require("devtools/shared/css/color");
const {getCSSLexer} = require("devtools/shared/css/lexer");
const EventEmitter = require("devtools/shared/event-emitter");
const {appendText} = require("devtools/client/inspector/shared/utils");

loader.lazyRequireGetter(this, "ANGLE_TAKING_FUNCTIONS",
  "devtools/shared/css/properties-db", true);
loader.lazyRequireGetter(this, "BASIC_SHAPE_FUNCTIONS",
  "devtools/shared/css/properties-db", true);
loader.lazyRequireGetter(this, "BEZIER_KEYWORDS",
  "devtools/shared/css/properties-db", true);
loader.lazyRequireGetter(this, "COLOR_TAKING_FUNCTIONS",
  "devtools/shared/css/properties-db", true);
loader.lazyRequireGetter(this, "CSS_TYPES",
  "devtools/shared/css/properties-db", true);

const STYLE_INSPECTOR_PROPERTIES = "devtools/shared/locales/styleinspector.properties";
const {LocalizationHelper} = require("devtools/shared/l10n");
const STYLE_INSPECTOR_L10N = new LocalizationHelper(STYLE_INSPECTOR_PROPERTIES);

const HTML_NS = "http://www.w3.org/1999/xhtml";

const FLEXBOX_HIGHLIGHTER_ENABLED_PREF = "devtools.inspector.flexboxHighlighter.enabled";
const CSS_SHAPES_ENABLED_PREF = "devtools.inspector.shapesHighlighter.enabled";
const CSS_SHAPE_OUTSIDE_ENABLED_PREF = "layout.css.shape-outside.enabled";

/**
 * This module is used to process text for output by developer tools. This means
 * linking JS files with the debugger, CSS files with the style editor, JS
 * functions with the debugger, placing color swatches next to colors and
 * adding doorhanger previews where possible (images, angles, lengths,
 * border radius, cubic-bezier etc.).
 *
 * Usage:
 *   const OutputParser = require("devtools/client/shared/output-parser");
 *
 *   let parser = new OutputParser(document, supportsType);
 *
 *   parser.parseCssProperty("color", "red"); // Returns document fragment.
 *
 * @param {Document} document Used to create DOM nodes.
 * @param {Function} supportsTypes - A function that returns a boolean when asked if a css
 *                   property name supports a given css type.
 *                   The function is executed like supportsType("color", CSS_TYPES.COLOR)
 *                   where CSS_TYPES is defined in devtools/shared/css/properties-db.js
 * @param {Function} isValidOnClient - A function that checks if a css property
 *                   name/value combo is valid.
 * @param {Function} supportsCssColor4ColorFunction - A function for checking
 *                   the supporting of css-color-4 color function.
 */
function OutputParser(document,
                      {supportsType, isValidOnClient, supportsCssColor4ColorFunction}) {
  this.parsed = [];
  this.doc = document;
  this.supportsType = supportsType;
  this.isValidOnClient = isValidOnClient;
  this.colorSwatches = new WeakMap();
  this.angleSwatches = new WeakMap();
  this._onColorSwatchMouseDown = this._onColorSwatchMouseDown.bind(this);
  this._onAngleSwatchMouseDown = this._onAngleSwatchMouseDown.bind(this);

  this.cssColor4 = supportsCssColor4ColorFunction();
}

OutputParser.prototype = {
  /**
   * 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: function (name, value, options = {}) {
    options = this._mergeOptions(options);

    options.expectCubicBezier = this.supportsType(name, CSS_TYPES.TIMING_FUNCTION);
    options.expectDisplay = name === "display";
    options.expectFilter = name === "filter";
    options.expectShape = name === "clip-path" ||
                          (name === "shape-outside"
                           && Services.prefs.getBoolPref(CSS_SHAPE_OUTSIDE_ENABLED_PREF));
    options.expectFont = name === "font-family";
    options.supportsColor = this.supportsType(name, CSS_TYPES.COLOR) ||
                            this.supportsType(name, CSS_TYPES.GRADIENT);

    // 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: function (text, tokenStream, options, stopAtComma) {
    let depth = 1;
    let functionData = [];
    let tokens = [];
    let sawVariable = false;

    while (depth > 0) {
      let 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.isVariableInUse) {
        sawVariable = true;
        let variableNode = this._parseVariable(token, text, tokenStream, options);
        functionData.push(variableNode);
      } else if (token.tokenType === "function") {
        ++depth;
      }

      if (token.tokenType !== "function" || token.text !== "var" ||
          !options.isVariableInUse) {
        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}
   *         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".
   */
  _parseVariable: function (initialToken, text, tokenStream, options) {
    // Handle the "var(".
    let varText = text.substring(initialToken.startOffset,
                                 initialToken.endOffset);
    let variableNode = this._createNode("span", {}, varText);

    // Parse the first variable name within the parens of var().
    let {tokens, functionData, sawComma, sawVariable} =
        this._parseMatchingParens(text, tokenStream, options, true);

    let result = sawVariable ? "" : functionData.join("");

    // Display options for the first and second argument in the var().
    let firstOpts = {};
    let secondOpts = {};

    let varValue;

    // Get the variable value if it is in use.
    if (tokens && tokens.length === 1) {
      varValue = options.isVariableInUse(tokens[0].text);
    }

    // Get the variable name.
    let 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.
      let subOptions = Object.assign({}, options);
      subOptions.expectFilter = false;
      let saveParsed = this.parsed;
      this.parsed = [];
      let rest = this._doParse(text, subOptions, tokenStream, true);
      this.parsed = saveParsed;

      let span = this._createNode("span", secondOpts);
      span.appendChild(rest);
      variableNode.appendChild(span);
    }
    variableNode.appendChild(this.doc.createTextNode(")"));

    return variableNode;
  },

  /* eslint-disable complexity */
  /**
   * 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.
   */
  _doParse: function (text, options, tokenStream, stopAtCloseParen) {
    let parenDepth = stopAtCloseParen ? 1 : 0;
    let outerMostFunctionTakesColor = false;
    let fontFamilyNameParts = [];

    let colorOK = function () {
      return options.supportsColor ||
        (options.expectFilter && parenDepth === 1 &&
         outerMostFunctionTakesColor);
    };

    let angleOK = function (angle) {
      return (new angleUtils.CssAngle(angle)).valid;
    };

    let spaceNeeded = false;
    let done = false;

    while (!done) {
      let token = tokenStream.nextToken();
      if (!token) {
        if (options.expectFont && fontFamilyNameParts.length !== 0) {
          this._appendFontFamily(fontFamilyNameParts.join(""), options);
        }
        break;
      }

      if (token.tokenType === "comment") {
        // This doesn't change spaceNeeded, because we didn't emit
        // anything to the output.
        continue;
      }

      switch (token.tokenType) {
        case "function": {
          if (COLOR_TAKING_FUNCTIONS.includes(token.text) ||
              ANGLE_TAKING_FUNCTIONS.includes(token.text)) {
            // 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 = COLOR_TAKING_FUNCTIONS.includes(
                token.text);
            }
            ++parenDepth;
          } else if (token.text === "var" && options.isVariableInUse) {
            let variableNode = this._parseVariable(token, text, tokenStream, options);
            this.parsed.push(variableNode);
          } else {
            let {functionData, sawVariable} = this._parseMatchingParens(text, tokenStream,
              options);

            let 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 (let 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.
              let functionText = functionName + functionData.join("") + ")";

              if (options.expectCubicBezier && token.text === "cubic-bezier") {
                this._appendCubicBezier(functionText, options);
              } else if (colorOK() &&
                         colorUtils.isValidCSSColor(functionText, this.cssColor4)) {
                this._appendColor(functionText, options);
              } else if (options.expectShape &&
                         Services.prefs.getBoolPref(CSS_SHAPES_ENABLED_PREF) &&
                         BASIC_SHAPE_FUNCTIONS.includes(token.text)) {
                this._appendShape(functionText, options);
              } else {
                this._appendTextNode(functionText);
              }
            }
          }
          break;
        }

        case "ident":
          if (options.expectCubicBezier &&
              BEZIER_KEYWORDS.includes(token.text)) {
            this._appendCubicBezier(token.text, options);
          } else if (this._isDisplayFlex(text, token, options) &&
                     Services.prefs.getBoolPref(FLEXBOX_HIGHLIGHTER_ENABLED_PREF)) {
            this._appendHighlighterToggle(token.text, options.flexClass);
          } else if (this._isDisplayGrid(text, token, options)) {
            this._appendHighlighterToggle(token.text, options.gridClass);
          } else if (colorOK() &&
                     colorUtils.isValidCSSColor(token.text, this.cssColor4)) {
            this._appendColor(token.text, options);
          } else if (angleOK(token.text)) {
            this._appendAngle(token.text, options);
          } else if (options.expectFont) {
            fontFamilyNameParts.push(token.text);
          } else {
            this._appendTextNode(text.substring(token.startOffset,
                                                token.endOffset));
          }
          break;

        case "id":
        case "hash": {
          let original = text.substring(token.startOffset, token.endOffset);
          if (colorOK() && colorUtils.isValidCSSColor(original, this.cssColor4)) {
            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);
          } else {
            this._appendTextNode(original);
          }
          break;
        }
        case "dimension":
          let 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 (stopAtCloseParen && parenDepth === 0) {
              done = true;
              break;
            }

            if (parenDepth === 0) {
              outerMostFunctionTakesColor = false;
            }
          } else if (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");
    }

    let result = this._toDOM();

    if (options.expectFilter && !options.filterSwatch) {
      result = this._wrapFilter(text, options, result);
    }

    return result;
  },
  /* eslint-enable complexity */

  /**
   * 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: function (text, options = {}) {
    text = text.trim();
    this.parsed.length = 0;

    let 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: function (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: function (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: function (bezier, options) {
    let container = this._createNode("span", {
      "data-bezier": bezier
    });

    if (options.bezierSwatchClass) {
      let swatch = this._createNode("span", {
        class: options.bezierSwatchClass
      });
      container.appendChild(swatch);
    }

    let value = this._createNode("span", {
      class: options.bezierClass
    }, bezier);

    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: function (text, className) {
    let container = this._createNode("span", {});

    let toggle = this._createNode("span", {
      class: className
    });

    let 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: function (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)
    }];

    let container = this._createNode("span", {});

    let toggle = this._createNode("span", {
      class: options.shapeSwatchClass
    });

    for (let { prefix, coordParser } of shapeTypes) {
      if (shape.includes(prefix)) {
        let coordsBegin = prefix.length;
        let coordsEnd = shape.lastIndexOf(")");
        let valContainer = this._createNode("span", {
          class: options.shapeClass
        });

        container.appendChild(toggle);

        appendText(valContainer, shape.substring(0, coordsBegin));

        let 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.
   */
  _addPolygonPointNodes: function (coords, container) {
    let 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
          let 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
        let 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.
          let 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) {
      let 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.
   */
  _addCirclePointNodes: function (coords, container) {
    let tokenStream = getCSSLexer(coords);
    let token = tokenStream.nextToken();
    let depth = 0;
    let coord = "";
    let point = "radius";
    let 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
        let 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
        let 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) {
          let 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.
          let 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") {
        let node = this._createNode("span", {
          class: "ruleview-shape-point",
          "data-point": "radius"
        }, coord);
        container.appendChild(node);
      } else {
        let 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.
   */
  _addEllipsePointNodes: function (coords, container) {
    let tokenStream = getCSSLexer(coords);
    let token = tokenStream.nextToken();
    let depth = 0;
    let coord = "";
    let point = "rx";
    let 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
          let 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
          let 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) {
          let 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.
          let 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.
          let 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") {
        let node = this._createNode("span", {
          class: "ruleview-shape-point",
          "data-point": point
        }, coord);
        container.appendChild(node);
      } else {
        let 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.
   */
  _addInsetPointNodes: function (coords, container) {
    const insetPoints = ["top", "right", "bottom", "left"];
    let 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].
    let nodes = [];
    let 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
        let 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.
          let 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
          let 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 {
        let 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++) {
      let point = insetPoints[j];
      let nodeIndex = (point === "left" && nodes.length === 3) ? 1 : j % nodes.length;
      nodes[nodeIndex].classList.add(point);
    }

    nodes.forEach((node, j, array) => {
      for (let 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 (let 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: function (angle, options) {
    let angleObj = new angleUtils.CssAngle(angle);
    let container = this._createNode("span", {
      "data-angle": angle
    });

    if (options.angleSwatchClass) {
      let swatch = this._createNode("span", {
        class: options.angleSwatchClass
      });
      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();
        }
      });
      EventEmitter.decorate(swatch);
      container.appendChild(swatch);
    }

    let 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: function (name, value) {
    return this.isValidOnClient(name, value, this.doc);
  },

  /**
   * 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: function (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: function (color, options = {}) {
    let colorObj = new colorUtils.CssColor(color, this.cssColor4);

    if (this._isValidColor(colorObj)) {
      let container = this._createNode("span", {
        "data-color": color
      });

      if (options.colorSwatchClass) {
        let swatch = this._createNode("span", {
          class: options.colorSwatchClass,
          style: "background-color:" + color
        });
        this.colorSwatches.set(swatch, colorObj);
        swatch.addEventListener("mousedown", this._onColorSwatchMouseDown);
        EventEmitter.decorate(swatch);
        container.appendChild(swatch);
      }

      if (options.defaultColorType) {
        color = colorObj.toString();
        container.dataset.color = color;
      }

      let 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: function (filters, options, nodes) {
    let container = this._createNode("span", {
      "data-filters": filters
    });

    if (options.filterSwatchClass) {
      let swatch = this._createNode("span", {
        class: options.filterSwatchClass
      });
      container.appendChild(swatch);
    }

    let value = this._createNode("span", {
      class: options.filterClass
    });
    value.appendChild(nodes);
    container.appendChild(value);

    return container;
  },

  _onColorSwatchMouseDown: function (event) {
    if (!event.shiftKey) {
      return;
    }

    // Prevent click event to be fired to not show the tooltip
    event.stopPropagation();

    let swatch = event.target;
    let color = this.colorSwatches.get(swatch);
    let val = color.nextColorUnit();

    swatch.nextElementSibling.textContent = val;
    swatch.emit("unit-change", val);
  },

  _onAngleSwatchMouseDown: function (event) {
    if (!event.shiftKey) {
      return;
    }

    event.stopPropagation();

    let swatch = event.target;
    let angle = this.angleSwatches.get(swatch);
    let val = angle.nextAngleUnit();

    swatch.nextElementSibling.textContent = val;
    swatch.emit("unit-change", val);
  },

  /**
   * A helper function that sanitizes a possibly-unterminated URL.
   */
  _sanitizeURL: function (url) {
    // Re-lex the URL and add any needed termination characters.
    let 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: function (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.
      let [, leader, , body, trailer] =
        /^(url\([ \t\r\n\f]*(["']?))(.*?)(\2[ \t\r\n\f]*\))$/i.exec(match);

      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: 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: function (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: function (tagName, attributes, value = "") {
    let node = this.doc.createElementNS(HTML_NS, tagName);
    let attrs = Object.getOwnPropertyNames(attributes);

    for (let attr of attrs) {
      if (attributes[attr]) {
        node.setAttribute(attr, attributes[attr]);
      }
    }

    if (value) {
      let 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: function (tagName, attributes, value = "") {
    let node = this._createNode(tagName, attributes, value);
    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: function (text) {
    let lastItem = this.parsed[this.parsed.length - 1];
    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: function () {
    let frag = this.doc.createDocumentFragment();

    for (let 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})
   *
   *         Valid options are:
   *           - defaultColorType: true // Convert colors to the default type
   *                                    // selected in the options panel.
   *           - angleClass: ""         // The class to use for the angle value
   *                                    // that follows the swatch.
   *           - angleSwatchClass: ""   // The class to use for angle swatches.
   *           - bezierClass: ""        // The class to use for the bezier value
   *                                    // that follows the swatch.
   *           - bezierSwatchClass: ""  // The class to use for bezier swatches.
   *           - colorClass: ""         // The class to use for the color value
   *                                    // that follows the swatch.
   *           - colorSwatchClass: ""   // The class to use for color swatches.
   *           - 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.
   *           - flexClass: ""          // The class to use for the flex icon.
   *           - gridClass: ""          // The class to use for the grid icon.
   *           - shapeClass: ""         // The class to use for the shape value
   *                                    // that follows the swatch.
   *           - shapeSwatchClass: ""   // The class to use for the shape swatch.
   *           - supportsColor: false   // Does the CSS property support colors?
   *           - urlClass: ""           // The class to be used for url() links.
   *           - fontFamilyClass: ""    // The class to be used for font families.
   *           - baseURI: undefined     // A string used to resolve
   *                                    // relative links.
   *           - isVariableInUse        // 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: function (overrides) {
    let defaults = {
      defaultColorType: true,
      angleClass: "",
      angleSwatchClass: "",
      bezierClass: "",
      bezierSwatchClass: "",
      colorClass: "",
      colorSwatchClass: "",
      filterSwatch: false,
      flexClass: "",
      gridClass: "",
      shapeClass: "",
      shapeSwatchClass: "",
      supportsColor: false,
      urlClass: "",
      fontFamilyClass: "",
      baseURI: undefined,
      isVariableInUse: null,
      unmatchedVariableClass: null,
    };

    for (let item in overrides) {
      defaults[item] = overrides[item];
    }
    return defaults;
  }
};

module.exports = OutputParser;
PK
!<�R��8chrome/devtools/modules/devtools/client/shared/poller.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

/**
 * @constructor Poller
 * Takes a function that is to be called on an interval,
 * and can be turned on and off via methods to execute `fn` on the interval
 * specified during `on`. If `fn` returns a promise, the polling waits for
 * that promise to resolve before waiting the interval to call again.
 *
 * Specify the `wait` duration between polling here, and optionally
 * an `immediate` boolean, indicating whether the function should be called
 * immediately when toggling on.
 *
 * @param {function} fn
 * @param {number} wait
 * @param {boolean?} immediate
 */
function Poller(fn, wait, immediate) {
  this._fn = fn;
  this._wait = wait;
  this._immediate = immediate;
  this._poll = this._poll.bind(this);
  this._preparePoll = this._preparePoll.bind(this);
}
exports.Poller = Poller;

/**
 * Returns a boolean indicating whether or not poller
 * is polling.
 *
 * @return {boolean}
 */
Poller.prototype.isPolling = function pollerIsPolling() {
  return !!this._timer;
};

/**
 * Turns polling on.
 *
 * @return {Poller}
 */
Poller.prototype.on = function pollerOn() {
  if (this._destroyed) {
    throw Error("Poller cannot be turned on after destruction.");
  }
  if (this._timer) {
    this.off();
  }
  this._immediate ? this._poll() : this._preparePoll();
  return this;
};

/**
 * Turns off polling. Returns a promise that resolves when
 * the last outstanding `fn` call finishes if it's an async function.
 *
 * @return {Promise}
 */
Poller.prototype.off = function pollerOff() {
  return new Promise((resolve, reject) => {
    if (this._timer) {
      clearTimeout(this._timer);
      this._timer = null;
    }

    // Settle an inflight poll call before resolving
    // if using a promise-backed poll function
    if (this._inflight) {
      this._inflight.then(resolve);
    } else {
      resolve();
    }
  });
};

/**
 * Turns off polling and removes the reference to the poller function.
 * Resolves when the last outstanding `fn` call finishes if it's an async
 * function.
 */
Poller.prototype.destroy = function pollerDestroy() {
  return this.off().then(() => {
    this._destroyed = true;
    this._fn = null;
  });
};

Poller.prototype._preparePoll = function pollerPrepare() {
  this._timer = setTimeout(this._poll, this._wait);
};

Poller.prototype._poll = function pollerPoll() {
  let response = this._fn();
  if (response && typeof response.then === "function") {
    // Store the most recent in-flight polling
    // call so we can clean it up when disabling
    this._inflight = response;
    response.then(() => {
      // Only queue up the next call if poller was not turned off
      // while this async poll call was in flight.
      if (this._timer) {
        this._preparePoll();
      }
    });
  } else {
    this._preparePoll();
  }
};
PK
!<J�3'AA7chrome/devtools/modules/devtools/client/shared/prefs.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Services = require("Services");
const EventEmitter = require("devtools/shared/event-emitter");

/**
 * 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);

  let cache = new Map();

  for (let accessorName in prefsBlueprint) {
    let [prefType, prefName] = prefsBlueprint[accessorName];
    map(this, cache, accessorName, prefType, prefsRoot, prefName);
  }

  let 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
 * @return any
 */
function get(cache, prefType, prefsRoot, prefName) {
  let cachedPref = cache.get(prefName);
  if (cachedPref !== undefined) {
    return cachedPref;
  }
  let value = Services.prefs["get" + prefType + "Pref"](
    [prefsRoot, prefName].join(".")
  );
  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 array serializer [optional]
 */
function map(self, cache, accessorName, prefType, prefsRoot, prefName,
             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, "Char", prefsRoot, prefName, {
      in: JSON.parse,
      out: JSON.stringify
    });
    return;
  }
  if (prefType == "Float") {
    map(self, cache, accessorName, "Char", prefsRoot, prefName, {
      in: Number.parseFloat,
      out: (n) => n + ""
    });
    return;
  }

  Object.defineProperty(self, accessorName, {
    get: () => serializer.in(get(cache, prefType, prefsRoot, prefName)),
    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 (let accessorName in prefsBlueprint) {
    let [, 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: function () {
      this._branch = Services.prefs.getBranch(prefsRoot + ".");
      this._branch.addObserver("", this);
    },
    unregister: function () {
      this._branch.removeObserver("", this);
    },
    observe: function (subject, topic, prefName) {
      // If this particular pref isn't handled by the blueprint object,
      // even though it's in the specified branch, ignore it.
      let 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.
 */
function PrefObserver(branchName) {
  this.branchName = branchName;
  this.branch = Services.prefs.getBranch(branchName);
  this.branch.addObserver("", this);

  EventEmitter.decorate(this);
}

exports.PrefObserver = PrefObserver;

PrefObserver.prototype = {
  observe: function (subject, topic, data) {
    if (topic == "nsPref:changed") {
      this.emit(this.branchName + data);
    }
  },

  destroy: function () {
    if (this.branch) {
      this.branch.removeObserver("", this);
    }
  },
};
PK
!<-�66=chrome/devtools/modules/devtools/client/shared/react-utils.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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) {
    let result = {};
    for (let p in args) {
      result[p] = React.createFactory(args[p]);
    }
    return result;
  }

  module.exports = {
    createFactories,
  };
});
PK
!<XY���Dchrome/devtools/modules/devtools/client/shared/redux/create-store.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/vendor/redux");
const { thunk } = require("./middleware/thunk");
const { waitUntilService } = require("./middleware/wait-service");
const { task } = require("./middleware/task");
const { log } = require("./middleware/log");
const { promise } = require("./middleware/promise");
const { history } = require("./middleware/history");

/**
 * 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:
 *        - log: log all dispatched actions to console
 *        - history: an array to store every action in. Should only be
 *                   used in tests.
 *        - middleware: array of middleware to be included in the redux store
 */
module.exports = (opts = {}) => {
  const middleware = [
    task,
    thunk,
    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.history) {
    middleware.push(history(opts.history));
  }

  if (opts.middleware) {
    opts.middleware.forEach(fn => middleware.push(fn));
  }

  if (opts.log) {
    middleware.push(log);
  }

  return applyMiddleware(...middleware)(createStore);
};
PK
!<�Ĕo�	�	Kchrome/devtools/modules/devtools/client/shared/redux/middleware/debounce.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 => {
    let 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;
  let doFunction = () => {
    clearTimeout(timeout);
    clearTimeout(maxTimeout);
    timeout = maxTimeout = null;
    cb();
  };

  return () => {
    return new Promise(resolve => {
      let 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,
};
PK
!<�>*�]]Jchrome/devtools/modules/devtools/client/shared/redux/middleware/history.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 flags = require("devtools/shared/flags");

/**
 * A middleware that stores every action coming through the store in the passed
 * in logging object. Should only be used for tests, as it collects all
 * action information, which will cause memory bloat.
 */
exports.history = (log = []) => ({ dispatch, getState }) => {
  if (!flags.testing) {
    console.warn("Using history middleware stores all actions in state for " +
                 "testing and devtools is not currently running in test " +
                 "mode. Be sure this is intentional.");
  }
  return next => action => {
    log.push(action);
    next(action);
  };
};
PK
!<b(1�55Fchrome/devtools/modules/devtools/client/shared/redux/middleware/log.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
      console.log("[DISPATCH]", JSON.stringify(action, null, 2));
    } catch (e) {
      console.log("[DISPATCH]", action);
    }
    next(action);
  };
}

exports.log = log;
PK
!<Fh#�PPJchrome/devtools/modules/devtools/client/shared/redux/middleware/promise.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { generateUUID } = require("devtools/shared/generate-uuid");
const {
  entries, toObject, executeSoon
} = require("devtools/shared/DevToolsUtils");
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: value
          }));
          resolve(value);
        });
      }, error => {
        executeSoon(() => {
          dispatch(Object.assign({}, action, {
            status: "error",
            error: error.message || error
          }));
          reject(error);
        });
      });
    });
  };
}

exports.promise = promiseMiddleware;
PK
!<q��κ�Gchrome/devtools/modules/devtools/client/shared/redux/middleware/task.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Task } = require("devtools/shared/task");
const { executeSoon, isGenerator, isAsyncFunction, reportException } =
  require("devtools/shared/DevToolsUtils");
const ERROR_TYPE = exports.ERROR_TYPE = "@@redux/middleware/task#error";

/**
 * A middleware that allows generator thunks (functions) and promise
 * to be dispatched. If it's a generator, it is called with `dispatch`
 * and `getState`, allowing the action to create multiple actions (most likely
 * asynchronously) and yield on each. If called with a promise, calls `dispatch`
 * on the results.
 */

function task({ dispatch, getState }) {
  return next => action => {
    if (isGenerator(action)) {
      return Task.spawn(action.bind(null, dispatch, getState))
        .catch(handleError.bind(null, dispatch));
    }
    if (isAsyncFunction(action)) {
      return action(dispatch, getState)
        .catch(handleError.bind(null, dispatch));
    }

    /*
    if (isPromise(action)) {
      return action.then(dispatch, handleError.bind(null, dispatch));
    }
    */

    return next(action);
  };
}

function handleError(dispatch, error) {
  executeSoon(() => {
    reportException(ERROR_TYPE, error);
    dispatch({ type: ERROR_TYPE, error });
  });
}

exports.task = task;
PK
!<G���Hchrome/devtools/modules/devtools/client/shared/redux/middleware/thunk.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 `dispatch` and `getState`,
 * allowing the action to create multiple actions (most likely
 * asynchronously).
 */
function thunk({ dispatch, getState }) {
  return next => action => {
    return (typeof action === "function")
      ? action(dispatch, getState)
      : next(action);
  };
}
exports.thunk = thunk;
PK
!<_���Ochrome/devtools/modules/devtools/client/shared/redux/middleware/wait-service.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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) {
    let readyRequests = [];
    let 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 (let request of pending) {
      if (request.predicate(action)) {
        readyRequests.push(request);
      } else {
        stillPending.push(request);
      }
    }

    pending = stillPending;
    for (let request of readyRequests) {
      request.run(dispatch, getState, action);
    }
  }

  return next => action => {
    if (action.type === NAME) {
      pending.push(action);
      return null;
    }
    let result = next(action);
    checkPending(action);
    return result;
  };
}
exports.waitUntilService = waitUntilService;
PK
!<�;?��Lchrome/devtools/modules/devtools/client/shared/redux/non-react-subscriber.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 defines functions to add the ability for redux reducers
 * to broadcast specific state changes to a non-React UI. You should
 * *never* use this for new code that uses React, as it violates the
 * core principals of a functional UI. This should only be used when
 * migrating old code to redux, because it allows you to use redux
 * with event-listening UI elements. The typical way to set all of
 * this up is this:
 *
 *  const emitter = makeEmitter();
 *  let store = createStore(combineEmittingReducers(
 *    reducers,
 *    emitter.emit
 *  ));
 *  store = enhanceStoreWithEmitter(store, emitter);
 *
 * Now reducers will receive a 3rd argument, `emit`, for emitting
 * events, and the store has an `on` function for listening to them.
 * For example, a reducer can now do this:
 *
 * function update(state = initialState, action, emitChange) {
 *   if (action.type === constants.ADD_BREAKPOINT) {
 *     const id = action.breakpoint.id;
 *     emitChange('add-breakpoint', action.breakpoint);
 *     return state.merge({ [id]: action.breakpoint });
 *   }
 *   return state;
 * }
 *
 * `emitChange` is *not* synchronous, the state changes will be
 * broadcasted *after* all reducers are run and the state has been
 * updated.
 *
 * Now, a non-React widget can do this:
 *
 * store.on('add-breakpoint', breakpoint => { ... });
 */

const { combineReducers } = require("devtools/client/shared/vendor/redux");

/**
 * Make an emitter that is meant to be used in redux reducers. This
 * does not run listeners immediately when an event is emitted; it
 * waits until all reducers have run and the store has updated the
 * state, and then fires any enqueued events. Events *are* fired
 * synchronously, but just later in the process.
 *
 * This is important because you never want the UI to be updating in
 * the middle of a reducing process. Reducers will fire these events
 * in the middle of generating new state, but the new state is *not*
 * available from the store yet. So if the UI executes in the middle
 * of the reducing process and calls `getState()` to get something
 * from the state, it will get stale state.
 *
 * We want the reducing and the UI updating phases to execute
 * atomically and independent from each other.
 *
 * @param {Function} stillAliveFunc
 *        A function that indicates the app is still active. If this
 *        returns false, changes will stop being broadcasted.
 */
function makeStateBroadcaster(stillAliveFunc) {
  const listeners = {};
  let enqueuedChanges = [];

  return {
    onChange: (name, cb) => {
      if (!listeners[name]) {
        listeners[name] = [];
      }
      listeners[name].push(cb);
    },

    offChange: (name, cb) => {
      listeners[name] = listeners[name].filter(listener => listener !== cb);
    },

    emitChange: (name, payload) => {
      enqueuedChanges.push([name, payload]);
    },

    subscribeToStore: store => {
      store.subscribe(() => {
        if (stillAliveFunc()) {
          enqueuedChanges.forEach(([name, payload]) => {
            if (listeners[name]) {
              listeners[name].forEach(listener => {
                listener(payload);
              });
            }
          });
          enqueuedChanges = [];
        }
      });
    }
  };
}

/**
 * Make a store fire any enqueued events whenever the state changes,
 * and add an `on` function to allow users to listen for specific
 * events.
 *
 * @param {Object} store
 * @param {Object} broadcaster
 * @return {Object}
 */
function enhanceStoreWithBroadcaster(store, broadcaster) {
  broadcaster.subscribeToStore(store);
  store.onChange = broadcaster.onChange;
  store.offChange = broadcaster.offChange;
  return store;
}

/**
 * Function that takes a hash of reducers, like `combineReducers`, and
 * an `emitChange` function and returns a function to be used as a
 * reducer for a Redux store. This allows all reducers defined here to
 * receive a third argument, the `emitChange` function, for
 * event-based subscriptions from within reducers.
 *
 * @param {Object} reducers
 * @param {Function} emitChange
 * @return {Function}
 */
function combineBroadcastingReducers(reducers, emitChange) {
  // Wrap each reducer with a wrapper function that calls
  // the reducer with a third argument, an `emitChange` function.
  // Use this rather than a new custom top level reducer that would ultimately
  // have to replicate redux's `combineReducers` so we only pass in correct
  // state, the error checking, and other edge cases.
  function wrapReduce(newReducers, key) {
    newReducers[key] = (state, action) => {
      return reducers[key](state, action, emitChange);
    };
    return newReducers;
  }

  return combineReducers(
    Object.keys(reducers).reduce(wrapReduce, Object.create(null))
  );
}

module.exports = {
  makeStateBroadcaster,
  enhanceStoreWithBroadcaster,
  combineBroadcastingReducers
};
PK
!<W�nRchrome/devtools/modules/devtools/client/shared/redux/visibility-handler-connect.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/vendor/react");
const VisibilityHandler = createFactory(require("devtools/client/shared/components/VisibilityHandler"));
const { connect } = require("devtools/client/shared/vendor/react-redux");

/**
 * 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() {
  let args = [].slice.call(arguments);
  return component => {
    return connect(...args)(props => {
      return VisibilityHandler(null, createElement(component, props));
    });
  };
}

module.exports = {
  connect: visibilityHandlerConnect
};
PK
!<��|�``8chrome/devtools/modules/devtools/client/shared/scroll.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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.
   */
  function scrollIntoViewIfNeeded(elem, centered = true) {
    let win = elem.ownerDocument.defaultView;
    let 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.

    let topToBottom = clientRect.bottom;
    let bottomToTop = clientRect.top - win.innerHeight;
    // We allow one translation on the y axis.
    let yAllowed = true;

    // Whatever `centered` is, the behavior is the same if the box is
    // (even partially) visible.
    if ((topToBottom > 0 || !centered) && topToBottom <= elem.offsetHeight) {
      win.scrollBy(0, topToBottom - elem.offsetHeight);
      yAllowed = false;
    } else if ((bottomToTop < 0 || !centered) &&
              bottomToTop >= -elem.offsetHeight) {
      win.scrollBy(0, bottomToTop + elem.offsetHeight);
      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)) {
        win.scroll(win.scrollX,
                  win.scrollY + clientRect.top
                  - (win.innerHeight - elem.offsetHeight) / 2);
      }
    }
  }
  // Exports from this module
  module.exports.scrollIntoViewIfNeeded = scrollIntoViewIfNeeded;
});
PK
!<�@Rw"R"RBchrome/devtools/modules/devtools/client/shared/source-map/index.js(function webpackUniversalModuleDefinition(root, factory) {
	if(typeof exports === 'object' && typeof module === 'object')
		module.exports = factory();
	else if(typeof define === 'function' && define.amd)
		define([], factory);
	else {
		var a = factory();
		for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
	}
})(this, function() {
return /******/ (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__) {

	/* This Source Code Form is subject to the terms of the Mozilla Public
	 * License, v. 2.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 {
	  originalToGeneratedId,
	  generatedToOriginalId,
	  isGeneratedId,
	  isOriginalId
	} = __webpack_require__(1);

	const { workerUtils: { WorkerDispatcher } } = __webpack_require__(6);

	const dispatcher = new WorkerDispatcher();

	const getOriginalURLs = dispatcher.task("getOriginalURLs");
	const getGeneratedLocation = dispatcher.task("getGeneratedLocation");
	const getOriginalLocation = dispatcher.task("getOriginalLocation");
	const getLocationScopes = dispatcher.task("getLocationScopes");
	const getOriginalSourceText = dispatcher.task("getOriginalSourceText");
	const applySourceMap = dispatcher.task("applySourceMap");
	const clearSourceMaps = dispatcher.task("clearSourceMaps");
	const hasMappedSource = dispatcher.task("hasMappedSource");

	module.exports = {
	  originalToGeneratedId,
	  generatedToOriginalId,
	  isGeneratedId,
	  isOriginalId,
	  hasMappedSource,
	  getOriginalURLs,
	  getGeneratedLocation,
	  getOriginalLocation,
	  getLocationScopes,
	  getOriginalSourceText,
	  applySourceMap,
	  clearSourceMaps,
	  startSourceMapWorker: dispatcher.start.bind(dispatcher),
	  stopSourceMapWorker: dispatcher.stop.bind(dispatcher)
	};

/***/ },
/* 1 */
/***/ function(module, exports, __webpack_require__) {

	/* This Source Code Form is subject to the terms of the Mozilla Public
	 * License, v. 2.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 md5 = __webpack_require__(2);

	function originalToGeneratedId(originalId) {
	  const match = originalId.match(/(.*)\/originalSource/);
	  return match ? match[1] : "";
	}

	function generatedToOriginalId(generatedId, url) {
	  return `${generatedId}/originalSource-${md5(url)}`;
	}

	function isOriginalId(id) {
	  return !!id.match(/\/originalSource/);
	}

	function isGeneratedId(id) {
	  return !isOriginalId(id);
	}

	/**
	 * Trims the query part or reference identifier of a URL string, if necessary.
	 */
	function trimUrlQuery(url) {
	  let length = url.length;
	  let q1 = url.indexOf("?");
	  let q2 = url.indexOf("&");
	  let q3 = url.indexOf("#");
	  let q = Math.min(q1 != -1 ? q1 : length, q2 != -1 ? q2 : length, q3 != -1 ? q3 : length);

	  return url.slice(0, q);
	}

	// Map suffix to content type.
	const contentMap = {
	  "js": "text/javascript",
	  "jsm": "text/javascript",
	  "ts": "text/typescript",
	  "tsx": "text/typescript-jsx",
	  "jsx": "text/jsx",
	  "coffee": "text/coffeescript",
	  "elm": "text/elm",
	  "cljs": "text/x-clojure"
	};

	/**
	 * 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);
	  let dot = url.lastIndexOf(".");
	  if (dot >= 0) {
	    let name = url.substring(dot + 1);
	    if (name in contentMap) {
	      return contentMap[name];
	    }
	  }
	  return "text/plain";
	}

	module.exports = {
	  originalToGeneratedId,
	  generatedToOriginalId,
	  isOriginalId,
	  isGeneratedId,
	  getContentType,
	  contentMapForTesting: contentMap
	};

/***/ },
/* 2 */
/***/ function(module, exports, __webpack_require__) {

	(function () {
	  var crypt = __webpack_require__(3),
	      utf8 = __webpack_require__(4).utf8,
	      isBuffer = __webpack_require__(5),
	      bin = __webpack_require__(4).bin,


	  // The core
	  md5 = function (message, options) {
	    // Convert to byte array
	    if (message.constructor == String) {
	      if (options && options.encoding === 'binary') message = bin.stringToBytes(message);else message = utf8.stringToBytes(message);
	    } else if (isBuffer(message)) message = Array.prototype.slice.call(message, 0);else if (!Array.isArray(message)) message = message.toString();
	    // else, assume byte array already

	    var m = crypt.bytesToWords(message),
	        l = message.length * 8,
	        a = 1732584193,
	        b = -271733879,
	        c = -1732584194,
	        d = 271733878;

	    // Swap endian
	    for (var i = 0; i < m.length; i++) {
	      m[i] = (m[i] << 8 | m[i] >>> 24) & 0x00FF00FF | (m[i] << 24 | m[i] >>> 8) & 0xFF00FF00;
	    }

	    // Padding
	    m[l >>> 5] |= 0x80 << l % 32;
	    m[(l + 64 >>> 9 << 4) + 14] = l;

	    // Method shortcuts
	    var FF = md5._ff,
	        GG = md5._gg,
	        HH = md5._hh,
	        II = md5._ii;

	    for (var i = 0; i < m.length; i += 16) {

	      var aa = a,
	          bb = b,
	          cc = c,
	          dd = d;

	      a = FF(a, b, c, d, m[i + 0], 7, -680876936);
	      d = FF(d, a, b, c, m[i + 1], 12, -389564586);
	      c = FF(c, d, a, b, m[i + 2], 17, 606105819);
	      b = FF(b, c, d, a, m[i + 3], 22, -1044525330);
	      a = FF(a, b, c, d, m[i + 4], 7, -176418897);
	      d = FF(d, a, b, c, m[i + 5], 12, 1200080426);
	      c = FF(c, d, a, b, m[i + 6], 17, -1473231341);
	      b = FF(b, c, d, a, m[i + 7], 22, -45705983);
	      a = FF(a, b, c, d, m[i + 8], 7, 1770035416);
	      d = FF(d, a, b, c, m[i + 9], 12, -1958414417);
	      c = FF(c, d, a, b, m[i + 10], 17, -42063);
	      b = FF(b, c, d, a, m[i + 11], 22, -1990404162);
	      a = FF(a, b, c, d, m[i + 12], 7, 1804603682);
	      d = FF(d, a, b, c, m[i + 13], 12, -40341101);
	      c = FF(c, d, a, b, m[i + 14], 17, -1502002290);
	      b = FF(b, c, d, a, m[i + 15], 22, 1236535329);

	      a = GG(a, b, c, d, m[i + 1], 5, -165796510);
	      d = GG(d, a, b, c, m[i + 6], 9, -1069501632);
	      c = GG(c, d, a, b, m[i + 11], 14, 643717713);
	      b = GG(b, c, d, a, m[i + 0], 20, -373897302);
	      a = GG(a, b, c, d, m[i + 5], 5, -701558691);
	      d = GG(d, a, b, c, m[i + 10], 9, 38016083);
	      c = GG(c, d, a, b, m[i + 15], 14, -660478335);
	      b = GG(b, c, d, a, m[i + 4], 20, -405537848);
	      a = GG(a, b, c, d, m[i + 9], 5, 568446438);
	      d = GG(d, a, b, c, m[i + 14], 9, -1019803690);
	      c = GG(c, d, a, b, m[i + 3], 14, -187363961);
	      b = GG(b, c, d, a, m[i + 8], 20, 1163531501);
	      a = GG(a, b, c, d, m[i + 13], 5, -1444681467);
	      d = GG(d, a, b, c, m[i + 2], 9, -51403784);
	      c = GG(c, d, a, b, m[i + 7], 14, 1735328473);
	      b = GG(b, c, d, a, m[i + 12], 20, -1926607734);

	      a = HH(a, b, c, d, m[i + 5], 4, -378558);
	      d = HH(d, a, b, c, m[i + 8], 11, -2022574463);
	      c = HH(c, d, a, b, m[i + 11], 16, 1839030562);
	      b = HH(b, c, d, a, m[i + 14], 23, -35309556);
	      a = HH(a, b, c, d, m[i + 1], 4, -1530992060);
	      d = HH(d, a, b, c, m[i + 4], 11, 1272893353);
	      c = HH(c, d, a, b, m[i + 7], 16, -155497632);
	      b = HH(b, c, d, a, m[i + 10], 23, -1094730640);
	      a = HH(a, b, c, d, m[i + 13], 4, 681279174);
	      d = HH(d, a, b, c, m[i + 0], 11, -358537222);
	      c = HH(c, d, a, b, m[i + 3], 16, -722521979);
	      b = HH(b, c, d, a, m[i + 6], 23, 76029189);
	      a = HH(a, b, c, d, m[i + 9], 4, -640364487);
	      d = HH(d, a, b, c, m[i + 12], 11, -421815835);
	      c = HH(c, d, a, b, m[i + 15], 16, 530742520);
	      b = HH(b, c, d, a, m[i + 2], 23, -995338651);

	      a = II(a, b, c, d, m[i + 0], 6, -198630844);
	      d = II(d, a, b, c, m[i + 7], 10, 1126891415);
	      c = II(c, d, a, b, m[i + 14], 15, -1416354905);
	      b = II(b, c, d, a, m[i + 5], 21, -57434055);
	      a = II(a, b, c, d, m[i + 12], 6, 1700485571);
	      d = II(d, a, b, c, m[i + 3], 10, -1894986606);
	      c = II(c, d, a, b, m[i + 10], 15, -1051523);
	      b = II(b, c, d, a, m[i + 1], 21, -2054922799);
	      a = II(a, b, c, d, m[i + 8], 6, 1873313359);
	      d = II(d, a, b, c, m[i + 15], 10, -30611744);
	      c = II(c, d, a, b, m[i + 6], 15, -1560198380);
	      b = II(b, c, d, a, m[i + 13], 21, 1309151649);
	      a = II(a, b, c, d, m[i + 4], 6, -145523070);
	      d = II(d, a, b, c, m[i + 11], 10, -1120210379);
	      c = II(c, d, a, b, m[i + 2], 15, 718787259);
	      b = II(b, c, d, a, m[i + 9], 21, -343485551);

	      a = a + aa >>> 0;
	      b = b + bb >>> 0;
	      c = c + cc >>> 0;
	      d = d + dd >>> 0;
	    }

	    return crypt.endian([a, b, c, d]);
	  };

	  // Auxiliary functions
	  md5._ff = function (a, b, c, d, x, s, t) {
	    var n = a + (b & c | ~b & d) + (x >>> 0) + t;
	    return (n << s | n >>> 32 - s) + b;
	  };
	  md5._gg = function (a, b, c, d, x, s, t) {
	    var n = a + (b & d | c & ~d) + (x >>> 0) + t;
	    return (n << s | n >>> 32 - s) + b;
	  };
	  md5._hh = function (a, b, c, d, x, s, t) {
	    var n = a + (b ^ c ^ d) + (x >>> 0) + t;
	    return (n << s | n >>> 32 - s) + b;
	  };
	  md5._ii = function (a, b, c, d, x, s, t) {
	    var n = a + (c ^ (b | ~d)) + (x >>> 0) + t;
	    return (n << s | n >>> 32 - s) + b;
	  };

	  // Package private blocksize
	  md5._blocksize = 16;
	  md5._digestsize = 16;

	  module.exports = function (message, options) {
	    if (message === undefined || message === null) throw new Error('Illegal argument ' + message);

	    var digestbytes = crypt.wordsToBytes(md5(message, options));
	    return options && options.asBytes ? digestbytes : options && options.asString ? bin.bytesToString(digestbytes) : crypt.bytesToHex(digestbytes);
	  };
	})();

/***/ },
/* 3 */
/***/ function(module, exports) {

	(function () {
	  var base64map = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
	      crypt = {
	    // Bit-wise rotation left
	    rotl: function (n, b) {
	      return n << b | n >>> 32 - b;
	    },

	    // Bit-wise rotation right
	    rotr: function (n, b) {
	      return n << 32 - b | n >>> b;
	    },

	    // Swap big-endian to little-endian and vice versa
	    endian: function (n) {
	      // If number given, swap endian
	      if (n.constructor == Number) {
	        return crypt.rotl(n, 8) & 0x00FF00FF | crypt.rotl(n, 24) & 0xFF00FF00;
	      }

	      // Else, assume array and swap all items
	      for (var i = 0; i < n.length; i++) n[i] = crypt.endian(n[i]);
	      return n;
	    },

	    // Generate an array of any length of random bytes
	    randomBytes: function (n) {
	      for (var bytes = []; n > 0; n--) bytes.push(Math.floor(Math.random() * 256));
	      return bytes;
	    },

	    // Convert a byte array to big-endian 32-bit words
	    bytesToWords: function (bytes) {
	      for (var words = [], i = 0, b = 0; i < bytes.length; i++, b += 8) words[b >>> 5] |= bytes[i] << 24 - b % 32;
	      return words;
	    },

	    // Convert big-endian 32-bit words to a byte array
	    wordsToBytes: function (words) {
	      for (var bytes = [], b = 0; b < words.length * 32; b += 8) bytes.push(words[b >>> 5] >>> 24 - b % 32 & 0xFF);
	      return bytes;
	    },

	    // Convert a byte array to a hex string
	    bytesToHex: function (bytes) {
	      for (var hex = [], i = 0; i < bytes.length; i++) {
	        hex.push((bytes[i] >>> 4).toString(16));
	        hex.push((bytes[i] & 0xF).toString(16));
	      }
	      return hex.join('');
	    },

	    // Convert a hex string to a byte array
	    hexToBytes: function (hex) {
	      for (var bytes = [], c = 0; c < hex.length; c += 2) bytes.push(parseInt(hex.substr(c, 2), 16));
	      return bytes;
	    },

	    // Convert a byte array to a base-64 string
	    bytesToBase64: function (bytes) {
	      for (var base64 = [], i = 0; i < bytes.length; i += 3) {
	        var triplet = bytes[i] << 16 | bytes[i + 1] << 8 | bytes[i + 2];
	        for (var j = 0; j < 4; j++) if (i * 8 + j * 6 <= bytes.length * 8) base64.push(base64map.charAt(triplet >>> 6 * (3 - j) & 0x3F));else base64.push('=');
	      }
	      return base64.join('');
	    },

	    // Convert a base-64 string to a byte array
	    base64ToBytes: function (base64) {
	      // Remove non-base-64 characters
	      base64 = base64.replace(/[^A-Z0-9+\/]/ig, '');

	      for (var bytes = [], i = 0, imod4 = 0; i < base64.length; imod4 = ++i % 4) {
	        if (imod4 == 0) continue;
	        bytes.push((base64map.indexOf(base64.charAt(i - 1)) & Math.pow(2, -2 * imod4 + 8) - 1) << imod4 * 2 | base64map.indexOf(base64.charAt(i)) >>> 6 - imod4 * 2);
	      }
	      return bytes;
	    }
	  };

	  module.exports = crypt;
	})();

/***/ },
/* 4 */
/***/ function(module, exports) {

	var charenc = {
	  // UTF-8 encoding
	  utf8: {
	    // Convert a string to a byte array
	    stringToBytes: function (str) {
	      return charenc.bin.stringToBytes(unescape(encodeURIComponent(str)));
	    },

	    // Convert a byte array to a string
	    bytesToString: function (bytes) {
	      return decodeURIComponent(escape(charenc.bin.bytesToString(bytes)));
	    }
	  },

	  // Binary encoding
	  bin: {
	    // Convert a string to a byte array
	    stringToBytes: function (str) {
	      for (var bytes = [], i = 0; i < str.length; i++) bytes.push(str.charCodeAt(i) & 0xFF);
	      return bytes;
	    },

	    // Convert a byte array to a string
	    bytesToString: function (bytes) {
	      for (var str = [], i = 0; i < bytes.length; i++) str.push(String.fromCharCode(bytes[i]));
	      return str.join('');
	    }
	  }
	};

	module.exports = charenc;

/***/ },
/* 5 */
/***/ function(module, exports) {

	/*!
	 * Determine if an object is a Buffer
	 *
	 * @author   Feross Aboukhadijeh <[email protected]> <http://feross.org>
	 * @license  MIT
	 */

	// The _isBuffer check is for Safari 5-7 support, because it's missing
	// Object.prototype.constructor. Remove this eventually
	module.exports = function (obj) {
	  return obj != null && (isBuffer(obj) || isSlowBuffer(obj) || !!obj._isBuffer);
	};

	function isBuffer(obj) {
	  return !!obj.constructor && typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj);
	}

	// For Node v0.10 support. Remove this eventually.
	function isSlowBuffer(obj) {
	  return typeof obj.readFloatLE === 'function' && typeof obj.slice === 'function' && isBuffer(obj.slice(0, 0));
	}

/***/ },
/* 6 */
/***/ function(module, exports, __webpack_require__) {

	/* This Source Code Form is subject to the terms of the Mozilla Public
	 * License, v. 2.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 networkRequest = __webpack_require__(7);
	const workerUtils = __webpack_require__(8);

	module.exports = {
	  networkRequest,
	  workerUtils
	};

/***/ },
/* 7 */
/***/ function(module, exports) {

	/* This Source Code Form is subject to the terms of the Mozilla Public
	 * License, v. 2.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 networkRequest(url, opts) {
	  return fetch(url, {
	    cache: opts.loadFromCache ? "default" : "no-cache"
	  }).then(res => {
	    if (res.status >= 200 && res.status < 300) {
	      return res.text().then(text => ({ content: text }));
	    }
	    return Promise.reject(`request failed with status ${res.status}`);
	  });
	}

	module.exports = networkRequest;

/***/ },
/* 8 */
/***/ function(module, exports) {

	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"); }); }; }

	function WorkerDispatcher() {
	  this.msgId = 1;
	  this.worker = null;
	} /* This Source Code Form is subject to the terms of the Mozilla Public
	   * License, v. 2.0. If a copy of the MPL was not distributed with this
	   * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

	WorkerDispatcher.prototype = {
	  start(url) {
	    this.worker = new Worker(url);
	    this.worker.onerror = () => {
	      console.error(`Error in worker ${url}`);
	    };
	  },

	  stop() {
	    if (!this.worker) {
	      return;
	    }

	    this.worker.terminate();
	    this.worker = null;
	  },

	  task(method) {
	    return (...args) => {
	      return new Promise((resolve, reject) => {
	        const id = this.msgId++;
	        this.worker.postMessage({ id, method, args });

	        const listener = ({ data: result }) => {
	          if (result.id !== id) {
	            return;
	          }

	          if (!this.worker) {
	            reject("Oops, The worker has shutdown!");
	            return;
	          }
	          this.worker.removeEventListener("message", listener);
	          if (result.error) {
	            reject(result.error);
	          } else {
	            resolve(result.response);
	          }
	        };

	        this.worker.addEventListener("message", listener);
	      });
	    };
	  }
	};

	function workerHandler(publicInterface) {
	  return function (msg) {
	    const { id, method, args } = msg.data;
	    try {
	      const response = publicInterface[method].apply(undefined, args);
	      if (response instanceof Promise) {
	        response.then(val => self.postMessage({ id, response: val }),
	        // Error can't be sent via postMessage, so be sure to
	        // convert to string.
	        err => self.postMessage({ id, error: err.toString() }));
	      } else {
	        self.postMessage({ id, response });
	      }
	    } catch (error) {
	      // Error can't be sent via postMessage, so be sure to convert to
	      // string.
	      self.postMessage({ id, error: error.toString() });
	    }
	  };
	}

	function streamingWorkerHandler(publicInterface, { timeout = 100 } = {}, worker = self) {
	  let streamingWorker = (() => {
	    var _ref = _asyncToGenerator(function* (id, tasks) {
	      let isWorking = true;

	      const intervalId = setTimeout(function () {
	        isWorking = false;
	      }, timeout);

	      const results = [];
	      while (tasks.length !== 0 && isWorking) {
	        const { callback, context, args } = tasks.shift();
	        const result = yield callback.call(context, args);
	        results.push(result);
	      }
	      worker.postMessage({ id, status: "pending", data: results });
	      clearInterval(intervalId);

	      if (tasks.length !== 0) {
	        yield streamingWorker(id, tasks);
	      }
	    });

	    return function streamingWorker(_x, _x2) {
	      return _ref.apply(this, arguments);
	    };
	  })();

	  return (() => {
	    var _ref2 = _asyncToGenerator(function* (msg) {
	      const { id, method, args } = msg.data;
	      const workerMethod = publicInterface[method];
	      if (!workerMethod) {
	        console.error(`Could not find ${method} defined in worker.`);
	      }
	      worker.postMessage({ id, status: "start" });

	      try {
	        const tasks = workerMethod(args);
	        yield streamingWorker(id, tasks);
	        worker.postMessage({ id, status: "done" });
	      } catch (error) {
	        worker.postMessage({ id, status: "error", error });
	      }
	    });

	    return function (_x3) {
	      return _ref2.apply(this, arguments);
	    };
	  })();
	}

	module.exports = {
	  WorkerDispatcher,
	  workerHandler,
	  streamingWorkerHandler
	};

/***/ }
/******/ ])
});
;PK
!<�96
�
�Cchrome/devtools/modules/devtools/client/shared/source-map/worker.js(function webpackUniversalModuleDefinition(root, factory) {
	if(typeof exports === 'object' && typeof module === 'object')
		module.exports = factory();
	else if(typeof define === 'function' && define.amd)
		define([], factory);
	else {
		var a = factory();
		for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
	}
})(this, function() {
return /******/ (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__) {

	/* This Source Code Form is subject to the terms of the Mozilla Public
	 * License, v. 2.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 {
	  getOriginalURLs,
	  getGeneratedLocation,
	  getOriginalLocation,
	  getOriginalSourceText,
	  getLocationScopes,
	  hasMappedSource,
	  applySourceMap
	} = __webpack_require__(9);

	const { clearSourceMaps } = __webpack_require__(23);

	const { workerUtils: { workerHandler } } = __webpack_require__(6);

	// The interface is implemented in source-map to be
	// easier to unit test.
	self.onmessage = workerHandler({
	  getOriginalURLs,
	  getGeneratedLocation,
	  getOriginalLocation,
	  getLocationScopes,
	  getOriginalSourceText,
	  hasMappedSource,
	  applySourceMap,
	  clearSourceMaps
	});

/***/ },
/* 1 */
/***/ function(module, exports, __webpack_require__) {

	/* This Source Code Form is subject to the terms of the Mozilla Public
	 * License, v. 2.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 md5 = __webpack_require__(2);

	function originalToGeneratedId(originalId) {
	  const match = originalId.match(/(.*)\/originalSource/);
	  return match ? match[1] : "";
	}

	function generatedToOriginalId(generatedId, url) {
	  return `${generatedId}/originalSource-${md5(url)}`;
	}

	function isOriginalId(id) {
	  return !!id.match(/\/originalSource/);
	}

	function isGeneratedId(id) {
	  return !isOriginalId(id);
	}

	/**
	 * Trims the query part or reference identifier of a URL string, if necessary.
	 */
	function trimUrlQuery(url) {
	  let length = url.length;
	  let q1 = url.indexOf("?");
	  let q2 = url.indexOf("&");
	  let q3 = url.indexOf("#");
	  let q = Math.min(q1 != -1 ? q1 : length, q2 != -1 ? q2 : length, q3 != -1 ? q3 : length);

	  return url.slice(0, q);
	}

	// Map suffix to content type.
	const contentMap = {
	  "js": "text/javascript",
	  "jsm": "text/javascript",
	  "ts": "text/typescript",
	  "tsx": "text/typescript-jsx",
	  "jsx": "text/jsx",
	  "coffee": "text/coffeescript",
	  "elm": "text/elm",
	  "cljs": "text/x-clojure"
	};

	/**
	 * 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);
	  let dot = url.lastIndexOf(".");
	  if (dot >= 0) {
	    let name = url.substring(dot + 1);
	    if (name in contentMap) {
	      return contentMap[name];
	    }
	  }
	  return "text/plain";
	}

	module.exports = {
	  originalToGeneratedId,
	  generatedToOriginalId,
	  isOriginalId,
	  isGeneratedId,
	  getContentType,
	  contentMapForTesting: contentMap
	};

/***/ },
/* 2 */
/***/ function(module, exports, __webpack_require__) {

	(function () {
	  var crypt = __webpack_require__(3),
	      utf8 = __webpack_require__(4).utf8,
	      isBuffer = __webpack_require__(5),
	      bin = __webpack_require__(4).bin,


	  // The core
	  md5 = function (message, options) {
	    // Convert to byte array
	    if (message.constructor == String) {
	      if (options && options.encoding === 'binary') message = bin.stringToBytes(message);else message = utf8.stringToBytes(message);
	    } else if (isBuffer(message)) message = Array.prototype.slice.call(message, 0);else if (!Array.isArray(message)) message = message.toString();
	    // else, assume byte array already

	    var m = crypt.bytesToWords(message),
	        l = message.length * 8,
	        a = 1732584193,
	        b = -271733879,
	        c = -1732584194,
	        d = 271733878;

	    // Swap endian
	    for (var i = 0; i < m.length; i++) {
	      m[i] = (m[i] << 8 | m[i] >>> 24) & 0x00FF00FF | (m[i] << 24 | m[i] >>> 8) & 0xFF00FF00;
	    }

	    // Padding
	    m[l >>> 5] |= 0x80 << l % 32;
	    m[(l + 64 >>> 9 << 4) + 14] = l;

	    // Method shortcuts
	    var FF = md5._ff,
	        GG = md5._gg,
	        HH = md5._hh,
	        II = md5._ii;

	    for (var i = 0; i < m.length; i += 16) {

	      var aa = a,
	          bb = b,
	          cc = c,
	          dd = d;

	      a = FF(a, b, c, d, m[i + 0], 7, -680876936);
	      d = FF(d, a, b, c, m[i + 1], 12, -389564586);
	      c = FF(c, d, a, b, m[i + 2], 17, 606105819);
	      b = FF(b, c, d, a, m[i + 3], 22, -1044525330);
	      a = FF(a, b, c, d, m[i + 4], 7, -176418897);
	      d = FF(d, a, b, c, m[i + 5], 12, 1200080426);
	      c = FF(c, d, a, b, m[i + 6], 17, -1473231341);
	      b = FF(b, c, d, a, m[i + 7], 22, -45705983);
	      a = FF(a, b, c, d, m[i + 8], 7, 1770035416);
	      d = FF(d, a, b, c, m[i + 9], 12, -1958414417);
	      c = FF(c, d, a, b, m[i + 10], 17, -42063);
	      b = FF(b, c, d, a, m[i + 11], 22, -1990404162);
	      a = FF(a, b, c, d, m[i + 12], 7, 1804603682);
	      d = FF(d, a, b, c, m[i + 13], 12, -40341101);
	      c = FF(c, d, a, b, m[i + 14], 17, -1502002290);
	      b = FF(b, c, d, a, m[i + 15], 22, 1236535329);

	      a = GG(a, b, c, d, m[i + 1], 5, -165796510);
	      d = GG(d, a, b, c, m[i + 6], 9, -1069501632);
	      c = GG(c, d, a, b, m[i + 11], 14, 643717713);
	      b = GG(b, c, d, a, m[i + 0], 20, -373897302);
	      a = GG(a, b, c, d, m[i + 5], 5, -701558691);
	      d = GG(d, a, b, c, m[i + 10], 9, 38016083);
	      c = GG(c, d, a, b, m[i + 15], 14, -660478335);
	      b = GG(b, c, d, a, m[i + 4], 20, -405537848);
	      a = GG(a, b, c, d, m[i + 9], 5, 568446438);
	      d = GG(d, a, b, c, m[i + 14], 9, -1019803690);
	      c = GG(c, d, a, b, m[i + 3], 14, -187363961);
	      b = GG(b, c, d, a, m[i + 8], 20, 1163531501);
	      a = GG(a, b, c, d, m[i + 13], 5, -1444681467);
	      d = GG(d, a, b, c, m[i + 2], 9, -51403784);
	      c = GG(c, d, a, b, m[i + 7], 14, 1735328473);
	      b = GG(b, c, d, a, m[i + 12], 20, -1926607734);

	      a = HH(a, b, c, d, m[i + 5], 4, -378558);
	      d = HH(d, a, b, c, m[i + 8], 11, -2022574463);
	      c = HH(c, d, a, b, m[i + 11], 16, 1839030562);
	      b = HH(b, c, d, a, m[i + 14], 23, -35309556);
	      a = HH(a, b, c, d, m[i + 1], 4, -1530992060);
	      d = HH(d, a, b, c, m[i + 4], 11, 1272893353);
	      c = HH(c, d, a, b, m[i + 7], 16, -155497632);
	      b = HH(b, c, d, a, m[i + 10], 23, -1094730640);
	      a = HH(a, b, c, d, m[i + 13], 4, 681279174);
	      d = HH(d, a, b, c, m[i + 0], 11, -358537222);
	      c = HH(c, d, a, b, m[i + 3], 16, -722521979);
	      b = HH(b, c, d, a, m[i + 6], 23, 76029189);
	      a = HH(a, b, c, d, m[i + 9], 4, -640364487);
	      d = HH(d, a, b, c, m[i + 12], 11, -421815835);
	      c = HH(c, d, a, b, m[i + 15], 16, 530742520);
	      b = HH(b, c, d, a, m[i + 2], 23, -995338651);

	      a = II(a, b, c, d, m[i + 0], 6, -198630844);
	      d = II(d, a, b, c, m[i + 7], 10, 1126891415);
	      c = II(c, d, a, b, m[i + 14], 15, -1416354905);
	      b = II(b, c, d, a, m[i + 5], 21, -57434055);
	      a = II(a, b, c, d, m[i + 12], 6, 1700485571);
	      d = II(d, a, b, c, m[i + 3], 10, -1894986606);
	      c = II(c, d, a, b, m[i + 10], 15, -1051523);
	      b = II(b, c, d, a, m[i + 1], 21, -2054922799);
	      a = II(a, b, c, d, m[i + 8], 6, 1873313359);
	      d = II(d, a, b, c, m[i + 15], 10, -30611744);
	      c = II(c, d, a, b, m[i + 6], 15, -1560198380);
	      b = II(b, c, d, a, m[i + 13], 21, 1309151649);
	      a = II(a, b, c, d, m[i + 4], 6, -145523070);
	      d = II(d, a, b, c, m[i + 11], 10, -1120210379);
	      c = II(c, d, a, b, m[i + 2], 15, 718787259);
	      b = II(b, c, d, a, m[i + 9], 21, -343485551);

	      a = a + aa >>> 0;
	      b = b + bb >>> 0;
	      c = c + cc >>> 0;
	      d = d + dd >>> 0;
	    }

	    return crypt.endian([a, b, c, d]);
	  };

	  // Auxiliary functions
	  md5._ff = function (a, b, c, d, x, s, t) {
	    var n = a + (b & c | ~b & d) + (x >>> 0) + t;
	    return (n << s | n >>> 32 - s) + b;
	  };
	  md5._gg = function (a, b, c, d, x, s, t) {
	    var n = a + (b & d | c & ~d) + (x >>> 0) + t;
	    return (n << s | n >>> 32 - s) + b;
	  };
	  md5._hh = function (a, b, c, d, x, s, t) {
	    var n = a + (b ^ c ^ d) + (x >>> 0) + t;
	    return (n << s | n >>> 32 - s) + b;
	  };
	  md5._ii = function (a, b, c, d, x, s, t) {
	    var n = a + (c ^ (b | ~d)) + (x >>> 0) + t;
	    return (n << s | n >>> 32 - s) + b;
	  };

	  // Package private blocksize
	  md5._blocksize = 16;
	  md5._digestsize = 16;

	  module.exports = function (message, options) {
	    if (message === undefined || message === null) throw new Error('Illegal argument ' + message);

	    var digestbytes = crypt.wordsToBytes(md5(message, options));
	    return options && options.asBytes ? digestbytes : options && options.asString ? bin.bytesToString(digestbytes) : crypt.bytesToHex(digestbytes);
	  };
	})();

/***/ },
/* 3 */
/***/ function(module, exports) {

	(function () {
	  var base64map = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
	      crypt = {
	    // Bit-wise rotation left
	    rotl: function (n, b) {
	      return n << b | n >>> 32 - b;
	    },

	    // Bit-wise rotation right
	    rotr: function (n, b) {
	      return n << 32 - b | n >>> b;
	    },

	    // Swap big-endian to little-endian and vice versa
	    endian: function (n) {
	      // If number given, swap endian
	      if (n.constructor == Number) {
	        return crypt.rotl(n, 8) & 0x00FF00FF | crypt.rotl(n, 24) & 0xFF00FF00;
	      }

	      // Else, assume array and swap all items
	      for (var i = 0; i < n.length; i++) n[i] = crypt.endian(n[i]);
	      return n;
	    },

	    // Generate an array of any length of random bytes
	    randomBytes: function (n) {
	      for (var bytes = []; n > 0; n--) bytes.push(Math.floor(Math.random() * 256));
	      return bytes;
	    },

	    // Convert a byte array to big-endian 32-bit words
	    bytesToWords: function (bytes) {
	      for (var words = [], i = 0, b = 0; i < bytes.length; i++, b += 8) words[b >>> 5] |= bytes[i] << 24 - b % 32;
	      return words;
	    },

	    // Convert big-endian 32-bit words to a byte array
	    wordsToBytes: function (words) {
	      for (var bytes = [], b = 0; b < words.length * 32; b += 8) bytes.push(words[b >>> 5] >>> 24 - b % 32 & 0xFF);
	      return bytes;
	    },

	    // Convert a byte array to a hex string
	    bytesToHex: function (bytes) {
	      for (var hex = [], i = 0; i < bytes.length; i++) {
	        hex.push((bytes[i] >>> 4).toString(16));
	        hex.push((bytes[i] & 0xF).toString(16));
	      }
	      return hex.join('');
	    },

	    // Convert a hex string to a byte array
	    hexToBytes: function (hex) {
	      for (var bytes = [], c = 0; c < hex.length; c += 2) bytes.push(parseInt(hex.substr(c, 2), 16));
	      return bytes;
	    },

	    // Convert a byte array to a base-64 string
	    bytesToBase64: function (bytes) {
	      for (var base64 = [], i = 0; i < bytes.length; i += 3) {
	        var triplet = bytes[i] << 16 | bytes[i + 1] << 8 | bytes[i + 2];
	        for (var j = 0; j < 4; j++) if (i * 8 + j * 6 <= bytes.length * 8) base64.push(base64map.charAt(triplet >>> 6 * (3 - j) & 0x3F));else base64.push('=');
	      }
	      return base64.join('');
	    },

	    // Convert a base-64 string to a byte array
	    base64ToBytes: function (base64) {
	      // Remove non-base-64 characters
	      base64 = base64.replace(/[^A-Z0-9+\/]/ig, '');

	      for (var bytes = [], i = 0, imod4 = 0; i < base64.length; imod4 = ++i % 4) {
	        if (imod4 == 0) continue;
	        bytes.push((base64map.indexOf(base64.charAt(i - 1)) & Math.pow(2, -2 * imod4 + 8) - 1) << imod4 * 2 | base64map.indexOf(base64.charAt(i)) >>> 6 - imod4 * 2);
	      }
	      return bytes;
	    }
	  };

	  module.exports = crypt;
	})();

/***/ },
/* 4 */
/***/ function(module, exports) {

	var charenc = {
	  // UTF-8 encoding
	  utf8: {
	    // Convert a string to a byte array
	    stringToBytes: function (str) {
	      return charenc.bin.stringToBytes(unescape(encodeURIComponent(str)));
	    },

	    // Convert a byte array to a string
	    bytesToString: function (bytes) {
	      return decodeURIComponent(escape(charenc.bin.bytesToString(bytes)));
	    }
	  },

	  // Binary encoding
	  bin: {
	    // Convert a string to a byte array
	    stringToBytes: function (str) {
	      for (var bytes = [], i = 0; i < str.length; i++) bytes.push(str.charCodeAt(i) & 0xFF);
	      return bytes;
	    },

	    // Convert a byte array to a string
	    bytesToString: function (bytes) {
	      for (var str = [], i = 0; i < bytes.length; i++) str.push(String.fromCharCode(bytes[i]));
	      return str.join('');
	    }
	  }
	};

	module.exports = charenc;

/***/ },
/* 5 */
/***/ function(module, exports) {

	/*!
	 * Determine if an object is a Buffer
	 *
	 * @author   Feross Aboukhadijeh <[email protected]> <http://feross.org>
	 * @license  MIT
	 */

	// The _isBuffer check is for Safari 5-7 support, because it's missing
	// Object.prototype.constructor. Remove this eventually
	module.exports = function (obj) {
	  return obj != null && (isBuffer(obj) || isSlowBuffer(obj) || !!obj._isBuffer);
	};

	function isBuffer(obj) {
	  return !!obj.constructor && typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj);
	}

	// For Node v0.10 support. Remove this eventually.
	function isSlowBuffer(obj) {
	  return typeof obj.readFloatLE === 'function' && typeof obj.slice === 'function' && isBuffer(obj.slice(0, 0));
	}

/***/ },
/* 6 */
/***/ function(module, exports, __webpack_require__) {

	/* This Source Code Form is subject to the terms of the Mozilla Public
	 * License, v. 2.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 networkRequest = __webpack_require__(7);
	const workerUtils = __webpack_require__(8);

	module.exports = {
	  networkRequest,
	  workerUtils
	};

/***/ },
/* 7 */
/***/ function(module, exports) {

	/* This Source Code Form is subject to the terms of the Mozilla Public
	 * License, v. 2.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 networkRequest(url, opts) {
	  return fetch(url, {
	    cache: opts.loadFromCache ? "default" : "no-cache"
	  }).then(res => {
	    if (res.status >= 200 && res.status < 300) {
	      return res.text().then(text => ({ content: text }));
	    }
	    return Promise.reject(`request failed with status ${res.status}`);
	  });
	}

	module.exports = networkRequest;

/***/ },
/* 8 */
/***/ function(module, exports) {

	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"); }); }; }

	function WorkerDispatcher() {
	  this.msgId = 1;
	  this.worker = null;
	} /* This Source Code Form is subject to the terms of the Mozilla Public
	   * License, v. 2.0. If a copy of the MPL was not distributed with this
	   * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

	WorkerDispatcher.prototype = {
	  start(url) {
	    this.worker = new Worker(url);
	    this.worker.onerror = () => {
	      console.error(`Error in worker ${url}`);
	    };
	  },

	  stop() {
	    if (!this.worker) {
	      return;
	    }

	    this.worker.terminate();
	    this.worker = null;
	  },

	  task(method) {
	    return (...args) => {
	      return new Promise((resolve, reject) => {
	        const id = this.msgId++;
	        this.worker.postMessage({ id, method, args });

	        const listener = ({ data: result }) => {
	          if (result.id !== id) {
	            return;
	          }

	          if (!this.worker) {
	            reject("Oops, The worker has shutdown!");
	            return;
	          }
	          this.worker.removeEventListener("message", listener);
	          if (result.error) {
	            reject(result.error);
	          } else {
	            resolve(result.response);
	          }
	        };

	        this.worker.addEventListener("message", listener);
	      });
	    };
	  }
	};

	function workerHandler(publicInterface) {
	  return function (msg) {
	    const { id, method, args } = msg.data;
	    try {
	      const response = publicInterface[method].apply(undefined, args);
	      if (response instanceof Promise) {
	        response.then(val => self.postMessage({ id, response: val }),
	        // Error can't be sent via postMessage, so be sure to
	        // convert to string.
	        err => self.postMessage({ id, error: err.toString() }));
	      } else {
	        self.postMessage({ id, response });
	      }
	    } catch (error) {
	      // Error can't be sent via postMessage, so be sure to convert to
	      // string.
	      self.postMessage({ id, error: error.toString() });
	    }
	  };
	}

	function streamingWorkerHandler(publicInterface, { timeout = 100 } = {}, worker = self) {
	  let streamingWorker = (() => {
	    var _ref = _asyncToGenerator(function* (id, tasks) {
	      let isWorking = true;

	      const intervalId = setTimeout(function () {
	        isWorking = false;
	      }, timeout);

	      const results = [];
	      while (tasks.length !== 0 && isWorking) {
	        const { callback, context, args } = tasks.shift();
	        const result = yield callback.call(context, args);
	        results.push(result);
	      }
	      worker.postMessage({ id, status: "pending", data: results });
	      clearInterval(intervalId);

	      if (tasks.length !== 0) {
	        yield streamingWorker(id, tasks);
	      }
	    });

	    return function streamingWorker(_x, _x2) {
	      return _ref.apply(this, arguments);
	    };
	  })();

	  return (() => {
	    var _ref2 = _asyncToGenerator(function* (msg) {
	      const { id, method, args } = msg.data;
	      const workerMethod = publicInterface[method];
	      if (!workerMethod) {
	        console.error(`Could not find ${method} defined in worker.`);
	      }
	      worker.postMessage({ id, status: "start" });

	      try {
	        const tasks = workerMethod(args);
	        yield streamingWorker(id, tasks);
	        worker.postMessage({ id, status: "done" });
	      } catch (error) {
	        worker.postMessage({ id, status: "error", error });
	      }
	    });

	    return function (_x3) {
	      return _ref2.apply(this, arguments);
	    };
	  })();
	}

	module.exports = {
	  WorkerDispatcher,
	  workerHandler,
	  streamingWorkerHandler
	};

/***/ },
/* 9 */
/***/ function(module, exports, __webpack_require__) {

	let getOriginalURLs = (() => {
	  var _ref = _asyncToGenerator(function* (generatedSource) {
	    const map = yield fetchSourceMap(generatedSource);
	    return map && map.sources;
	  });

	  return function getOriginalURLs(_x) {
	    return _ref.apply(this, arguments);
	  };
	})();

	let getGeneratedLocation = (() => {
	  var _ref2 = _asyncToGenerator(function* (location, originalSource) {
	    if (!isOriginalId(location.sourceId)) {
	      return location;
	    }

	    const generatedSourceId = originalToGeneratedId(location.sourceId);
	    const map = yield getSourceMap(generatedSourceId);
	    if (!map) {
	      return location;
	    }

	    const { line, column } = map.generatedPositionFor({
	      source: originalSource.url,
	      line: location.line,
	      column: location.column == null ? 0 : location.column,
	      bias: SourceMapConsumer.LEAST_UPPER_BOUND
	    });

	    return {
	      sourceId: generatedSourceId,
	      line,
	      column
	    };
	  });

	  return function getGeneratedLocation(_x2, _x3) {
	    return _ref2.apply(this, arguments);
	  };
	})();

	let getOriginalLocation = (() => {
	  var _ref3 = _asyncToGenerator(function* (location) {
	    if (!isGeneratedId(location.sourceId)) {
	      return location;
	    }

	    const map = yield getSourceMap(location.sourceId);
	    if (!map) {
	      return location;
	    }

	    const { source: sourceUrl, line, column } = map.originalPositionFor({
	      line: location.line,
	      column: location.column == null ? 0 : location.column
	    });

	    if (sourceUrl == null) {
	      // No url means the location didn't map.
	      return location;
	    }

	    return {
	      sourceId: generatedToOriginalId(location.sourceId, sourceUrl),
	      sourceUrl,
	      line,
	      column
	    };
	  });

	  return function getOriginalLocation(_x4) {
	    return _ref3.apply(this, arguments);
	  };
	})();

	let getOriginalSourceText = (() => {
	  var _ref4 = _asyncToGenerator(function* (originalSource) {
	    assert(isOriginalId(originalSource.id), "Source is not an original source");

	    const generatedSourceId = originalToGeneratedId(originalSource.id);
	    const map = yield getSourceMap(generatedSourceId);
	    if (!map) {
	      return null;
	    }

	    let text = map.sourceContentFor(originalSource.url);
	    if (!text) {
	      text = (yield networkRequest(originalSource.url, { loadFromCache: false })).content;
	    }

	    return {
	      text,
	      contentType: getContentType(originalSource.url || "")
	    };
	  });

	  return function getOriginalSourceText(_x5) {
	    return _ref4.apply(this, arguments);
	  };
	})();

	/**
	 * Finds the scope binding information at the source map based on locations and
	 * generated source scopes. The generated source scopes contain variables names
	 * and all their references in the generated source. This information is merged
	 * with the source map original names information and returned as result.
	 * @memberof utils/source-map-worker
	 * @static
	 */


	let getLocationScopes = (() => {
	  var _ref5 = _asyncToGenerator(function* (location, generatedSourceScopes) {
	    if (!isGeneratedId(location.sourceId)) {
	      return null;
	    }

	    const map = yield getSourceMap(location.sourceId);
	    if (!map) {
	      return null;
	    }

	    return getLocationScopesFromMap(map, generatedSourceScopes, location);
	  });

	  return function getLocationScopes(_x6, _x7) {
	    return _ref5.apply(this, arguments);
	  };
	})();

	let hasMappedSource = (() => {
	  var _ref6 = _asyncToGenerator(function* (location) {
	    if (isOriginalId(location.sourceId)) {
	      return true;
	    }

	    const loc = yield getOriginalLocation(location);
	    return loc.sourceId !== location.sourceId;
	  });

	  return function hasMappedSource(_x8) {
	    return _ref6.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"); }); }; }

	/* This Source Code Form is subject to the terms of the Mozilla Public
	 * License, v. 2.0. If a copy of the MPL was not distributed with this
	 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

	/**
	 * Source Map Worker
	 * @module utils/source-map-worker
	 */

	const { networkRequest } = __webpack_require__(6);
	const { SourceMapConsumer, SourceMapGenerator } = __webpack_require__(10);

	const assert = __webpack_require__(21);
	const { fetchSourceMap } = __webpack_require__(22);
	const {
	  getSourceMap,
	  setSourceMap,
	  clearSourceMaps
	} = __webpack_require__(23);
	const {
	  originalToGeneratedId,
	  generatedToOriginalId,
	  isGeneratedId,
	  isOriginalId,
	  getContentType
	} = __webpack_require__(1);
	const {
	  getLocationScopes: getLocationScopesFromMap
	} = __webpack_require__(40);

	const { WasmRemap } = __webpack_require__(24);

	function applySourceMap(generatedId, url, code, mappings) {
	  const generator = new SourceMapGenerator({ file: url });
	  mappings.forEach(mapping => generator.addMapping(mapping));
	  generator.setSourceContent(url, code);

	  const map = SourceMapConsumer(generator.toJSON());
	  setSourceMap(generatedId, Promise.resolve(map));
	}

	module.exports = {
	  getOriginalURLs,
	  getGeneratedLocation,
	  getOriginalLocation,
	  getOriginalSourceText,
	  getLocationScopes,
	  applySourceMap,
	  clearSourceMaps,
	  hasMappedSource
	};

/***/ },
/* 10 */
/***/ function(module, exports, __webpack_require__) {

	/*
	 * 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 = __webpack_require__(11).SourceMapGenerator;
	exports.SourceMapConsumer = __webpack_require__(17).SourceMapConsumer;
	exports.SourceNode = __webpack_require__(20).SourceNode;

/***/ },
/* 11 */
/***/ function(module, exports, __webpack_require__) {

	/* -*- 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
	 */

	var base64VLQ = __webpack_require__(12);
	var util = __webpack_require__(14);
	var ArraySet = __webpack_require__(15).ArraySet;
	var MappingList = __webpack_require__(16).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.
	 */
	function SourceMapGenerator(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;
	}

	SourceMapGenerator.prototype._version = 3;

	/**
	 * Creates a new SourceMapGenerator based on a SourceMapConsumer
	 *
	 * @param aSourceMapConsumer The SourceMap.
	 */
	SourceMapGenerator.fromSourceMap = function SourceMapGenerator_fromSourceMap(aSourceMapConsumer) {
	  var sourceRoot = aSourceMapConsumer.sourceRoot;
	  var generator = new SourceMapGenerator({
	    file: aSourceMapConsumer.file,
	    sourceRoot: sourceRoot
	  });
	  aSourceMapConsumer.eachMapping(function (mapping) {
	    var 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) {
	    var sourceRelative = sourceFile;
	    if (sourceRoot !== null) {
	      sourceRelative = util.relative(sourceRoot, sourceFile);
	    }

	    if (!generator._sources.has(sourceRelative)) {
	      generator._sources.add(sourceRelative);
	    }

	    var 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.
	 */
	SourceMapGenerator.prototype.addMapping = function SourceMapGenerator_addMapping(aArgs) {
	  var generated = util.getArg(aArgs, 'generated');
	  var original = util.getArg(aArgs, 'original', null);
	  var source = util.getArg(aArgs, 'source', null);
	  var 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 != null && original.line,
	    originalColumn: original != null && original.column,
	    source: source,
	    name: name
	  });
	};

	/**
	 * Set the source content for a source file.
	 */
	SourceMapGenerator.prototype.setSourceContent = function SourceMapGenerator_setSourceContent(aSourceFile, aSourceContent) {
	  var 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.
	 */
	SourceMapGenerator.prototype.applySourceMap = function SourceMapGenerator_applySourceMap(aSourceMapConsumer, aSourceFile, aSourceMapPath) {
	  var 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;
	  }
	  var 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.
	  var newSources = new ArraySet();
	  var 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.
	      var 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;
	        }
	      }
	    }

	    var source = mapping.source;
	    if (source != null && !newSources.has(source)) {
	      newSources.add(source);
	    }

	    var 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 (sourceFile) {
	    var content = aSourceMapConsumer.sourceContentFor(sourceFile);
	    if (content != null) {
	      if (aSourceMapPath != null) {
	        sourceFile = util.join(aSourceMapPath, sourceFile);
	      }
	      if (sourceRoot != null) {
	        sourceFile = util.relative(sourceRoot, sourceFile);
	      }
	      this.setSourceContent(sourceFile, 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.
	 */
	SourceMapGenerator.prototype._validateMapping = function SourceMapGenerator_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.
	    return;
	  } 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.
	    return;
	  } 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.
	 */
	SourceMapGenerator.prototype._serializeMappings = function SourceMapGenerator_serializeMappings() {
	  var previousGeneratedColumn = 0;
	  var previousGeneratedLine = 1;
	  var previousOriginalColumn = 0;
	  var previousOriginalLine = 0;
	  var previousName = 0;
	  var previousSource = 0;
	  var result = '';
	  var next;
	  var mapping;
	  var nameIdx;
	  var sourceIdx;

	  var mappings = this._mappings.toArray();
	  for (var 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;
	};

	SourceMapGenerator.prototype._generateSourcesContent = function SourceMapGenerator_generateSourcesContent(aSources, aSourceRoot) {
	  return aSources.map(function (source) {
	    if (!this._sourcesContents) {
	      return null;
	    }
	    if (aSourceRoot != null) {
	      source = util.relative(aSourceRoot, source);
	    }
	    var key = util.toSetString(source);
	    return Object.prototype.hasOwnProperty.call(this._sourcesContents, key) ? this._sourcesContents[key] : null;
	  }, this);
	};

	/**
	 * Externalize the source map.
	 */
	SourceMapGenerator.prototype.toJSON = function SourceMapGenerator_toJSON() {
	  var 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.
	 */
	SourceMapGenerator.prototype.toString = function SourceMapGenerator_toString() {
	  return JSON.stringify(this.toJSON());
	};

	exports.SourceMapGenerator = SourceMapGenerator;

/***/ },
/* 12 */
/***/ function(module, exports, __webpack_require__) {

	/* -*- 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.
	 */

	var base64 = __webpack_require__(13);

	// 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

	var VLQ_BASE_SHIFT = 5;

	// binary: 100000
	var VLQ_BASE = 1 << VLQ_BASE_SHIFT;

	// binary: 011111
	var VLQ_BASE_MASK = VLQ_BASE - 1;

	// binary: 100000
	var 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;
	}

	/**
	 * Converts to a two-complement value from a value where the sign bit is
	 * placed in the least significant bit.  For example, as decimals:
	 *   2 (10 binary) becomes 1, 3 (11 binary) becomes -1
	 *   4 (100 binary) becomes 2, 5 (101 binary) becomes -2
	 */
	function fromVLQSigned(aValue) {
	  var isNegative = (aValue & 1) === 1;
	  var shifted = aValue >> 1;
	  return isNegative ? -shifted : shifted;
	}

	/**
	 * Returns the base 64 VLQ encoded value.
	 */
	exports.encode = function base64VLQ_encode(aValue) {
	  var encoded = "";
	  var digit;

	  var 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;
	};

	/**
	 * Decodes the next base 64 VLQ value from the given string and returns the
	 * value and the rest of the string via the out parameter.
	 */
	exports.decode = function base64VLQ_decode(aStr, aIndex, aOutParam) {
	  var strLen = aStr.length;
	  var result = 0;
	  var shift = 0;
	  var continuation, digit;

	  do {
	    if (aIndex >= strLen) {
	      throw new Error("Expected more digits in base 64 VLQ value.");
	    }

	    digit = base64.decode(aStr.charCodeAt(aIndex++));
	    if (digit === -1) {
	      throw new Error("Invalid base64 digit: " + aStr.charAt(aIndex - 1));
	    }

	    continuation = !!(digit & VLQ_CONTINUATION_BIT);
	    digit &= VLQ_BASE_MASK;
	    result = result + (digit << shift);
	    shift += VLQ_BASE_SHIFT;
	  } while (continuation);

	  aOutParam.value = fromVLQSigned(result);
	  aOutParam.rest = aIndex;
	};

/***/ },
/* 13 */
/***/ function(module, exports) {

	/* -*- 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
	 */

	var 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);
	};

	/**
	 * Decode a single base 64 character code digit to an integer. Returns -1 on
	 * failure.
	 */
	exports.decode = function (charCode) {
	  var bigA = 65; // 'A'
	  var bigZ = 90; // 'Z'

	  var littleA = 97; // 'a'
	  var littleZ = 122; // 'z'

	  var zero = 48; // '0'
	  var nine = 57; // '9'

	  var plus = 43; // '+'
	  var slash = 47; // '/'

	  var littleOffset = 26;
	  var numberOffset = 52;

	  // 0 - 25: ABCDEFGHIJKLMNOPQRSTUVWXYZ
	  if (bigA <= charCode && charCode <= bigZ) {
	    return charCode - bigA;
	  }

	  // 26 - 51: abcdefghijklmnopqrstuvwxyz
	  if (littleA <= charCode && charCode <= littleZ) {
	    return charCode - littleA + littleOffset;
	  }

	  // 52 - 61: 0123456789
	  if (zero <= charCode && charCode <= nine) {
	    return charCode - zero + numberOffset;
	  }

	  // 62: +
	  if (charCode == plus) {
	    return 62;
	  }

	  // 63: /
	  if (charCode == slash) {
	    return 63;
	  }

	  // Invalid base64 digit.
	  return -1;
	};

/***/ },
/* 14 */
/***/ function(module, exports) {

	/* -*- 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
	 */

	/**
	 * 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;
	  } else {
	    throw new Error('"' + aName + '" is a required argument.');
	  }
	}
	exports.getArg = getArg;

	var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.-]*)(?::(\d+))?(.*)$/;
	var dataUrlRegexp = /^data:.+\,.+$/;

	function urlParse(aUrl) {
	  var match = aUrl.match(urlRegexp);
	  if (!match) {
	    return null;
	  }
	  return {
	    scheme: match[1],
	    auth: match[2],
	    host: match[3],
	    port: match[4],
	    path: match[5]
	  };
	}
	exports.urlParse = urlParse;

	function urlGenerate(aParsedUrl) {
	  var url = '';
	  if (aParsedUrl.scheme) {
	    url += aParsedUrl.scheme + ':';
	  }
	  url += '//';
	  if (aParsedUrl.auth) {
	    url += aParsedUrl.auth + '@';
	  }
	  if (aParsedUrl.host) {
	    url += aParsedUrl.host;
	  }
	  if (aParsedUrl.port) {
	    url += ":" + aParsedUrl.port;
	  }
	  if (aParsedUrl.path) {
	    url += aParsedUrl.path;
	  }
	  return url;
	}
	exports.urlGenerate = urlGenerate;

	/**
	 * Normalizes a path, or the path portion of a URL:
	 *
	 * - Replaces consecutive slashes with one slash.
	 * - Removes unnecessary '.' parts.
	 * - Removes unnecessary '<dir>/..' parts.
	 *
	 * Based on code in the Node.js 'path' core module.
	 *
	 * @param aPath The path or url to normalize.
	 */
	function normalize(aPath) {
	  var path = aPath;
	  var url = urlParse(aPath);
	  if (url) {
	    if (!url.path) {
	      return aPath;
	    }
	    path = url.path;
	  }
	  var isAbsolute = exports.isAbsolute(path);

	  var parts = path.split(/\/+/);
	  for (var part, up = 0, i = parts.length - 1; i >= 0; i--) {
	    part = parts[i];
	    if (part === '.') {
	      parts.splice(i, 1);
	    } else if (part === '..') {
	      up++;
	    } else if (up > 0) {
	      if (part === '') {
	        // The first part is blank if the path is absolute. Trying to go
	        // above the root is a no-op. Therefore we can remove all '..' parts
	        // directly after the root.
	        parts.splice(i + 1, up);
	        up = 0;
	      } else {
	        parts.splice(i, 2);
	        up--;
	      }
	    }
	  }
	  path = parts.join('/');

	  if (path === '') {
	    path = isAbsolute ? '/' : '.';
	  }

	  if (url) {
	    url.path = path;
	    return urlGenerate(url);
	  }
	  return path;
	}
	exports.normalize = normalize;

	/**
	 * Joins two paths/URLs.
	 *
	 * @param aRoot The root path or URL.
	 * @param aPath The path or URL to be joined with the root.
	 *
	 * - If aPath is a URL or a data URI, aPath is returned, unless aPath is a
	 *   scheme-relative URL: Then the scheme of aRoot, if any, is prepended
	 *   first.
	 * - Otherwise aPath is a path. If aRoot is a URL, then its path portion
	 *   is updated with the result and aRoot is returned. Otherwise the result
	 *   is returned.
	 *   - If aPath is absolute, the result is aPath.
	 *   - Otherwise the two paths are joined with a slash.
	 * - Joining for example 'http://' and 'www.example.com' is also supported.
	 */
	function join(aRoot, aPath) {
	  if (aRoot === "") {
	    aRoot = ".";
	  }
	  if (aPath === "") {
	    aPath = ".";
	  }
	  var aPathUrl = urlParse(aPath);
	  var aRootUrl = urlParse(aRoot);
	  if (aRootUrl) {
	    aRoot = aRootUrl.path || '/';
	  }

	  // `join(foo, '//www.example.org')`
	  if (aPathUrl && !aPathUrl.scheme) {
	    if (aRootUrl) {
	      aPathUrl.scheme = aRootUrl.scheme;
	    }
	    return urlGenerate(aPathUrl);
	  }

	  if (aPathUrl || aPath.match(dataUrlRegexp)) {
	    return aPath;
	  }

	  // `join('http://', 'www.example.com')`
	  if (aRootUrl && !aRootUrl.host && !aRootUrl.path) {
	    aRootUrl.host = aPath;
	    return urlGenerate(aRootUrl);
	  }

	  var joined = aPath.charAt(0) === '/' ? aPath : normalize(aRoot.replace(/\/+$/, '') + '/' + aPath);

	  if (aRootUrl) {
	    aRootUrl.path = joined;
	    return urlGenerate(aRootUrl);
	  }
	  return joined;
	}
	exports.join = join;

	exports.isAbsolute = function (aPath) {
	  return aPath.charAt(0) === '/' || urlRegexp.test(aPath);
	};

	/**
	 * Make a path relative to a URL or another path.
	 *
	 * @param aRoot The root path or URL.
	 * @param aPath The path or URL to be made relative to aRoot.
	 */
	function relative(aRoot, aPath) {
	  if (aRoot === "") {
	    aRoot = ".";
	  }

	  aRoot = aRoot.replace(/\/$/, '');

	  // It is possible for the path to be above the root. In this case, simply
	  // checking whether the root is a prefix of the path won't work. Instead, we
	  // need to remove components from the root one by one, until either we find
	  // a prefix that fits, or we run out of components to remove.
	  var level = 0;
	  while (aPath.indexOf(aRoot + '/') !== 0) {
	    var index = aRoot.lastIndexOf("/");
	    if (index < 0) {
	      return aPath;
	    }

	    // If the only part of the root that is left is the scheme (i.e. http://,
	    // file:///, etc.), one or more slashes (/), or simply nothing at all, we
	    // have exhausted all components, so the path is not relative to the root.
	    aRoot = aRoot.slice(0, index);
	    if (aRoot.match(/^([^\/]+:\/)?\/*$/)) {
	      return aPath;
	    }

	    ++level;
	  }

	  // Make sure we add a "../" for each component we removed from the root.
	  return Array(level + 1).join("../") + aPath.substr(aRoot.length + 1);
	}
	exports.relative = relative;

	var supportsNullProto = function () {
	  var 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;
	  }

	  var length = s.length;

	  if (length < 9 /* "__proto__".length */) {
	      return false;
	    }

	  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;
	    }

	  for (var i = length - 10; i >= 0; i--) {
	    if (s.charCodeAt(i) !== 36 /* '$' */) {
	        return false;
	      }
	  }

	  return true;
	}

	/**
	 * Comparator between two mappings where the original positions are compared.
	 *
	 * Optionally pass in `true` as `onlyCompareGenerated` to consider two
	 * mappings with the same original source/line/column, but different generated
	 * line and column the same. Useful when searching for a mapping with a
	 * stubbed out mapping.
	 */
	function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) {
	  var 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 || onlyCompareOriginal) {
	    return cmp;
	  }

	  cmp = mappingA.generatedColumn - mappingB.generatedColumn;
	  if (cmp !== 0) {
	    return cmp;
	  }

	  cmp = mappingA.generatedLine - mappingB.generatedLine;
	  if (cmp !== 0) {
	    return cmp;
	  }

	  return strcmp(mappingA.name, mappingB.name);
	}
	exports.compareByOriginalPositions = compareByOriginalPositions;

	/**
	 * Comparator between two mappings with deflated source and name indices where
	 * the generated positions are compared.
	 *
	 * Optionally pass in `true` as `onlyCompareGenerated` to consider two
	 * mappings with the same generated line and column, but different
	 * source/name/original line and column the same. Useful when searching for a
	 * mapping with a stubbed out mapping.
	 */
	function compareByGeneratedPositionsDeflated(mappingA, mappingB, onlyCompareGenerated) {
	  var cmp = mappingA.generatedLine - mappingB.generatedLine;
	  if (cmp !== 0) {
	    return cmp;
	  }

	  cmp = mappingA.generatedColumn - mappingB.generatedColumn;
	  if (cmp !== 0 || onlyCompareGenerated) {
	    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.compareByGeneratedPositionsDeflated = compareByGeneratedPositionsDeflated;

	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) {
	  var 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;

	/**
	 * 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) {
	  sourceURL = sourceURL || '';

	  if (sourceRoot) {
	    // This follows what Chrome does.
	    if (sourceRoot[sourceRoot.length - 1] !== '/' && sourceURL[0] !== '/') {
	      sourceRoot += '/';
	    }
	    // The spec says:
	    //   Line 4: An optional source root, useful for relocating source
	    //   files on a server or removing repeated values in the
	    //   “sources” entry.  This value is prepended to the individual
	    //   entries in the “source” field.
	    sourceURL = sourceRoot + sourceURL;
	  }

	  // Historically, SourceMapConsumer did not take the sourceMapURL as
	  // a parameter.  This mode is still somewhat supported, which is why
	  // this code block is conditional.  However, it's preferable to pass
	  // the source map URL to SourceMapConsumer, so that this function
	  // can implement the source URL resolution algorithm as outlined in
	  // the spec.  This block is basically the equivalent of:
	  //    new URL(sourceURL, sourceMapURL).toString()
	  // ... except it avoids using URL, which wasn't available in the
	  // older releases of node still supported by this library.
	  //
	  // The spec says:
	  //   If the sources are not absolute URLs after prepending of the
	  //   “sourceRoot”, the sources are resolved relative to the
	  //   SourceMap (like resolving script src in a html document).
	  if (sourceMapURL) {
	    var parsed = urlParse(sourceMapURL);
	    if (!parsed) {
	      throw new Error("sourceMapURL could not be parsed");
	    }
	    if (parsed.path) {
	      // Strip the last path component, but keep the "/".
	      var index = parsed.path.lastIndexOf('/');
	      if (index >= 0) {
	        parsed.path = parsed.path.substring(0, index + 1);
	      }
	    }
	    sourceURL = join(urlGenerate(parsed), sourceURL);
	  }

	  return normalize(sourceURL);
	}
	exports.computeSourceURL = computeSourceURL;

/***/ },
/* 15 */
/***/ function(module, exports, __webpack_require__) {

	/* -*- 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
	 */

	var util = __webpack_require__(14);
	var has = Object.prototype.hasOwnProperty;
	var hasNativeMap = typeof Map !== "undefined";

	/**
	 * 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.
	 */
	function ArraySet() {
	  this._array = [];
	  this._set = hasNativeMap ? new Map() : Object.create(null);
	}

	/**
	 * Static method for creating ArraySet instances from an existing array.
	 */
	ArraySet.fromArray = function ArraySet_fromArray(aArray, aAllowDuplicates) {
	  var set = new ArraySet();
	  for (var 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
	 */
	ArraySet.prototype.size = function ArraySet_size() {
	  return hasNativeMap ? this._set.size : Object.getOwnPropertyNames(this._set).length;
	};

	/**
	 * Add the given string to this set.
	 *
	 * @param String aStr
	 */
	ArraySet.prototype.add = function ArraySet_add(aStr, aAllowDuplicates) {
	  var sStr = hasNativeMap ? aStr : util.toSetString(aStr);
	  var isDuplicate = hasNativeMap ? this.has(aStr) : has.call(this._set, sStr);
	  var idx = this._array.length;
	  if (!isDuplicate || aAllowDuplicates) {
	    this._array.push(aStr);
	  }
	  if (!isDuplicate) {
	    if (hasNativeMap) {
	      this._set.set(aStr, idx);
	    } else {
	      this._set[sStr] = idx;
	    }
	  }
	};

	/**
	 * Is the given string a member of this set?
	 *
	 * @param String aStr
	 */
	ArraySet.prototype.has = function ArraySet_has(aStr) {
	  if (hasNativeMap) {
	    return this._set.has(aStr);
	  } else {
	    var sStr = util.toSetString(aStr);
	    return has.call(this._set, sStr);
	  }
	};

	/**
	 * What is the index of the given string in the array?
	 *
	 * @param String aStr
	 */
	ArraySet.prototype.indexOf = function ArraySet_indexOf(aStr) {
	  if (hasNativeMap) {
	    var idx = this._set.get(aStr);
	    if (idx >= 0) {
	      return idx;
	    }
	  } else {
	    var sStr = util.toSetString(aStr);
	    if (has.call(this._set, sStr)) {
	      return this._set[sStr];
	    }
	  }

	  throw new Error('"' + aStr + '" is not in the set.');
	};

	/**
	 * What is the element at the given index?
	 *
	 * @param Number aIdx
	 */
	ArraySet.prototype.at = function ArraySet_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.
	 */
	ArraySet.prototype.toArray = function ArraySet_toArray() {
	  return this._array.slice();
	};

	exports.ArraySet = ArraySet;

/***/ },
/* 16 */
/***/ function(module, exports, __webpack_require__) {

	/* -*- 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
	 */

	var util = __webpack_require__(14);

	/**
	 * Determine whether mappingB is after mappingA with respect to generated
	 * position.
	 */
	function generatedPositionAfter(mappingA, mappingB) {
	  // Optimized for most common case
	  var lineA = mappingA.generatedLine;
	  var lineB = mappingB.generatedLine;
	  var columnA = mappingA.generatedColumn;
	  var 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 neglibable overhead in general
	 * case for a large speedup in case of mappings being added in order.
	 */
	function MappingList() {
	  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.
	 */
	MappingList.prototype.unsortedForEach = function MappingList_forEach(aCallback, aThisArg) {
	  this._array.forEach(aCallback, aThisArg);
	};

	/**
	 * Add the given source mapping.
	 *
	 * @param Object aMapping
	 */
	MappingList.prototype.add = function MappingList_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.
	 */
	MappingList.prototype.toArray = function MappingList_toArray() {
	  if (!this._sorted) {
	    this._array.sort(util.compareByGeneratedPositionsInflated);
	    this._sorted = true;
	  }
	  return this._array;
	};

	exports.MappingList = MappingList;

/***/ },
/* 17 */
/***/ function(module, exports, __webpack_require__) {

	/* -*- 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
	 */

	var util = __webpack_require__(14);
	var binarySearch = __webpack_require__(18);
	var ArraySet = __webpack_require__(15).ArraySet;
	var base64VLQ = __webpack_require__(12);
	var quickSort = __webpack_require__(19).quickSort;

	function SourceMapConsumer(aSourceMap, aSourceMapURL) {
	  var sourceMap = aSourceMap;
	  if (typeof aSourceMap === 'string') {
	    sourceMap = util.parseSourceMapInput(aSourceMap);
	  }

	  return sourceMap.sections != null ? new IndexedSourceMapConsumer(sourceMap, aSourceMapURL) : new BasicSourceMapConsumer(sourceMap, aSourceMapURL);
	}

	SourceMapConsumer.fromSourceMap = function (aSourceMap, aSourceMapURL) {
	  return BasicSourceMapConsumer.fromSourceMap(aSourceMap, aSourceMapURL);
	};

	/**
	 * The version of the source mapping spec that we are consuming.
	 */
	SourceMapConsumer.prototype._version = 3;

	// `__generatedMappings` and `__originalMappings` are arrays that hold the
	// parsed mapping coordinates from the source map's "mappings" attribute. They
	// are lazily instantiated, accessed via the `_generatedMappings` and
	// `_originalMappings` getters respectively, and we only parse the mappings
	// and create these arrays once queried for a source location. We jump through
	// these hoops because there can be many thousands of mappings, and parsing
	// them is expensive, so we only want to do it if we must.
	//
	// Each object in the arrays is of the form:
	//
	//     {
	//       generatedLine: The line number in the generated code,
	//       generatedColumn: The column number in the generated code,
	//       source: The path to the original source file that generated this
	//               chunk of code,
	//       originalLine: The line number in the original source that
	//                     corresponds to this chunk of generated code,
	//       originalColumn: The column number in the original source that
	//                       corresponds to this chunk of generated code,
	//       name: The name of the original symbol which generated this chunk of
	//             code.
	//     }
	//
	// All properties except for `generatedLine` and `generatedColumn` can be
	// `null`.
	//
	// `_generatedMappings` is ordered by the generated positions.
	//
	// `_originalMappings` is ordered by the original positions.

	SourceMapConsumer.prototype.__generatedMappings = null;
	Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', {
	  configurable: true,
	  enumerable: true,
	  get: function () {
	    if (!this.__generatedMappings) {
	      this._parseMappings(this._mappings, this.sourceRoot);
	    }

	    return this.__generatedMappings;
	  }
	});

	SourceMapConsumer.prototype.__originalMappings = null;
	Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', {
	  configurable: true,
	  enumerable: true,
	  get: function () {
	    if (!this.__originalMappings) {
	      this._parseMappings(this._mappings, this.sourceRoot);
	    }

	    return this.__originalMappings;
	  }
	});

	SourceMapConsumer.prototype._charIsMappingSeparator = function SourceMapConsumer_charIsMappingSeparator(aStr, index) {
	  var c = aStr.charAt(index);
	  return c === ";" || c === ",";
	};

	/**
	 * 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).
	 */
	SourceMapConsumer.prototype._parseMappings = function SourceMapConsumer_parseMappings(aStr, aSourceRoot) {
	  throw new Error("Subclasses must implement _parseMappings");
	};

	SourceMapConsumer.GENERATED_ORDER = 1;
	SourceMapConsumer.ORIGINAL_ORDER = 2;

	SourceMapConsumer.GREATEST_LOWER_BOUND = 1;
	SourceMapConsumer.LEAST_UPPER_BOUND = 2;

	/**
	 * 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`.
	 */
	SourceMapConsumer.prototype.eachMapping = function SourceMapConsumer_eachMapping(aCallback, aContext, aOrder) {
	  var context = aContext || null;
	  var order = aOrder || SourceMapConsumer.GENERATED_ORDER;

	  var mappings;
	  switch (order) {
	    case SourceMapConsumer.GENERATED_ORDER:
	      mappings = this._generatedMappings;
	      break;
	    case SourceMapConsumer.ORIGINAL_ORDER:
	      mappings = this._originalMappings;
	      break;
	    default:
	      throw new Error("Unknown order of iteration.");
	  }

	  var sourceRoot = this.sourceRoot;
	  mappings.map(function (mapping) {
	    var source = mapping.source === null ? null : this._sources.at(mapping.source);
	    source = util.computeSourceURL(sourceRoot, source, this._sourceMapURL);
	    return {
	      source: source,
	      generatedLine: mapping.generatedLine,
	      generatedColumn: mapping.generatedColumn,
	      originalLine: mapping.originalLine,
	      originalColumn: mapping.originalColumn,
	      name: mapping.name === null ? null : this._names.at(mapping.name)
	    };
	  }, this).forEach(aCallback, context);
	};

	/**
	 * 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.
	 */
	SourceMapConsumer.prototype.allGeneratedPositionsFor = function SourceMapConsumer_allGeneratedPositionsFor(aArgs) {
	  var line = util.getArg(aArgs, 'line');

	  // When there is no exact match, BasicSourceMapConsumer.prototype._findMapping
	  // returns the index of the closest mapping less than the needle. By
	  // setting needle.originalColumn to 0, we thus find the last mapping for
	  // the given line, provided such a mapping exists.
	  var needle = {
	    source: util.getArg(aArgs, 'source'),
	    originalLine: line,
	    originalColumn: util.getArg(aArgs, 'column', 0)
	  };

	  needle.source = this._findSourceIndex(needle.source);
	  if (needle.source < 0) {
	    return [];
	  }

	  var mappings = [];

	  var index = this._findMapping(needle, this._originalMappings, "originalLine", "originalColumn", util.compareByOriginalPositions, binarySearch.LEAST_UPPER_BOUND);
	  if (index >= 0) {
	    var mapping = this._originalMappings[index];

	    if (aArgs.column === undefined) {
	      var originalLine = mapping.originalLine;

	      // Iterate until either we run out of mappings, or we run into
	      // a mapping for a different line than the one we found. Since
	      // mappings are sorted, this is guaranteed to find all mappings for
	      // the line we found.
	      while (mapping && mapping.originalLine === originalLine) {
	        mappings.push({
	          line: util.getArg(mapping, 'generatedLine', null),
	          column: util.getArg(mapping, 'generatedColumn', null),
	          lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null)
	        });

	        mapping = this._originalMappings[++index];
	      }
	    } else {
	      var originalColumn = mapping.originalColumn;

	      // Iterate until either we run out of mappings, or we run into
	      // a mapping for a different line than the one we were searching for.
	      // Since mappings are sorted, this is guaranteed to find all mappings for
	      // the line we are searching for.
	      while (mapping && mapping.originalLine === line && mapping.originalColumn == originalColumn) {
	        mappings.push({
	          line: util.getArg(mapping, 'generatedLine', null),
	          column: util.getArg(mapping, 'generatedColumn', null),
	          lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null)
	        });

	        mapping = this._originalMappings[++index];
	      }
	    }
	  }

	  return mappings;
	};

	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 referrenced 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#
	 */
	function BasicSourceMapConsumer(aSourceMap, aSourceMapURL) {
	  var sourceMap = aSourceMap;
	  if (typeof aSourceMap === 'string') {
	    sourceMap = util.parseSourceMapInput(aSourceMap);
	  }

	  var version = util.getArg(sourceMap, 'version');
	  var sources = util.getArg(sourceMap, 'sources');
	  // Sass 3.3 leaves out the 'names' array, so we deviate from the spec (which
	  // requires the array) to play nice here.
	  var names = util.getArg(sourceMap, 'names', []);
	  var sourceRoot = util.getArg(sourceMap, 'sourceRoot', null);
	  var sourcesContent = util.getArg(sourceMap, 'sourcesContent', null);
	  var mappings = util.getArg(sourceMap, 'mappings');
	  var file = util.getArg(sourceMap, 'file', 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 != this._version) {
	    throw new Error('Unsupported version: ' + version);
	  }

	  if (sourceRoot) {
	    sourceRoot = util.normalize(sourceRoot);
	  }

	  sources = sources.map(String)
	  // Some source maps produce relative source paths like "./foo.js" instead of
	  // "foo.js".  Normalize these first so that future comparisons will succeed.
	  // See bugzil.la/1090768.
	  .map(util.normalize)
	  // Always ensure that absolute sources are internally stored relative to
	  // the source root, if the source root is absolute. Not doing this would
	  // be particularly problematic when the source root is a prefix of the
	  // source (valid, but why??). See github issue #199 and bugzil.la/1188982.
	  .map(function (source) {
	    return sourceRoot && util.isAbsolute(sourceRoot) && util.isAbsolute(source) ? util.relative(sourceRoot, source) : source;
	  });

	  // 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.
	  this._names = ArraySet.fromArray(names.map(String), true);
	  this._sources = ArraySet.fromArray(sources, true);

	  this._absoluteSources = this._sources.toArray().map(function (s) {
	    return util.computeSourceURL(sourceRoot, s, aSourceMapURL);
	  });

	  this.sourceRoot = sourceRoot;
	  this.sourcesContent = sourcesContent;
	  this._mappings = mappings;
	  this._sourceMapURL = aSourceMapURL;
	  this.file = file;
	}

	BasicSourceMapConsumer.prototype = Object.create(SourceMapConsumer.prototype);
	BasicSourceMapConsumer.prototype.consumer = SourceMapConsumer;

	/**
	 * Utility function to find the index of a source.  Returns -1 if not
	 * found.
	 */
	BasicSourceMapConsumer.prototype._findSourceIndex = function (aSource) {
	  var relativeSource = aSource;
	  if (this.sourceRoot != null) {
	    relativeSource = util.relative(this.sourceRoot, relativeSource);
	  }

	  if (this._sources.has(relativeSource)) {
	    return this._sources.indexOf(relativeSource);
	  }

	  // Maybe aSource is an absolute URL as returned by |sources|.  In
	  // this case we can't simply undo the transform.
	  var i;
	  for (i = 0; i < this._absoluteSources.length; ++i) {
	    if (this._absoluteSources[i] == aSource) {
	      return i;
	    }
	  }

	  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
	 */
	BasicSourceMapConsumer.fromSourceMap = function SourceMapConsumer_fromSourceMap(aSourceMap, aSourceMapURL) {
	  var smc = Object.create(BasicSourceMapConsumer.prototype);

	  var names = smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true);
	  var sources = smc._sources = ArraySet.fromArray(aSourceMap._sources.toArray(), true);
	  smc.sourceRoot = aSourceMap._sourceRoot;
	  smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(), smc.sourceRoot);
	  smc.file = aSourceMap._file;
	  smc._sourceMapURL = aSourceMapURL;
	  smc._absoluteSources = smc._sources.toArray().map(function (s) {
	    return util.computeSourceURL(smc.sourceRoot, s, aSourceMapURL);
	  });

	  // Because we are modifying the entries (by converting string sources and
	  // names to indices into the sources and names ArraySets), we have to make
	  // a copy of the entry or else bad things happen. Shared mutable state
	  // strikes again! See github issue #191.

	  var generatedMappings = aSourceMap._mappings.toArray().slice();
	  var destGeneratedMappings = smc.__generatedMappings = [];
	  var destOriginalMappings = smc.__originalMappings = [];

	  for (var i = 0, length = generatedMappings.length; i < length; i++) {
	    var srcMapping = generatedMappings[i];
	    var destMapping = new Mapping();
	    destMapping.generatedLine = srcMapping.generatedLine;
	    destMapping.generatedColumn = srcMapping.generatedColumn;

	    if (srcMapping.source) {
	      destMapping.source = sources.indexOf(srcMapping.source);
	      destMapping.originalLine = srcMapping.originalLine;
	      destMapping.originalColumn = srcMapping.originalColumn;

	      if (srcMapping.name) {
	        destMapping.name = names.indexOf(srcMapping.name);
	      }

	      destOriginalMappings.push(destMapping);
	    }

	    destGeneratedMappings.push(destMapping);
	  }

	  quickSort(smc.__originalMappings, util.compareByOriginalPositions);

	  return smc;
	};

	/**
	 * The version of the source mapping spec that we are consuming.
	 */
	BasicSourceMapConsumer.prototype._version = 3;

	/**
	 * The list of original sources.
	 */
	Object.defineProperty(BasicSourceMapConsumer.prototype, 'sources', {
	  get: function () {
	    return this._absoluteSources.slice();
	  }
	});

	/**
	 * Provide the JIT with a nice shape / hidden class.
	 */
	function Mapping() {
	  this.generatedLine = 0;
	  this.generatedColumn = 0;
	  this.source = null;
	  this.originalLine = null;
	  this.originalColumn = null;
	  this.name = null;
	}

	/**
	 * 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).
	 */
	BasicSourceMapConsumer.prototype._parseMappings = function SourceMapConsumer_parseMappings(aStr, aSourceRoot) {
	  var generatedLine = 1;
	  var previousGeneratedColumn = 0;
	  var previousOriginalLine = 0;
	  var previousOriginalColumn = 0;
	  var previousSource = 0;
	  var previousName = 0;
	  var length = aStr.length;
	  var index = 0;
	  var cachedSegments = {};
	  var temp = {};
	  var originalMappings = [];
	  var generatedMappings = [];
	  var mapping, str, segment, end, value;

	  while (index < length) {
	    if (aStr.charAt(index) === ';') {
	      generatedLine++;
	      index++;
	      previousGeneratedColumn = 0;
	    } else if (aStr.charAt(index) === ',') {
	      index++;
	    } else {
	      mapping = new Mapping();
	      mapping.generatedLine = generatedLine;

	      // Because each offset is encoded relative to the previous one,
	      // many segments often have the same encoding. We can exploit this
	      // fact by caching the parsed variable length fields of each segment,
	      // allowing us to avoid a second parse if we encounter the same
	      // segment again.
	      for (end = index; end < length; end++) {
	        if (this._charIsMappingSeparator(aStr, end)) {
	          break;
	        }
	      }
	      str = aStr.slice(index, end);

	      segment = cachedSegments[str];
	      if (segment) {
	        index += str.length;
	      } else {
	        segment = [];
	        while (index < end) {
	          base64VLQ.decode(aStr, index, temp);
	          value = temp.value;
	          index = temp.rest;
	          segment.push(value);
	        }

	        if (segment.length === 2) {
	          throw new Error('Found a source, but no line and column');
	        }

	        if (segment.length === 3) {
	          throw new Error('Found a source and line, but no column');
	        }

	        cachedSegments[str] = segment;
	      }

	      // Generated column.
	      mapping.generatedColumn = previousGeneratedColumn + segment[0];
	      previousGeneratedColumn = mapping.generatedColumn;

	      if (segment.length > 1) {
	        // Original source.
	        mapping.source = previousSource + segment[1];
	        previousSource += segment[1];

	        // Original line.
	        mapping.originalLine = previousOriginalLine + segment[2];
	        previousOriginalLine = mapping.originalLine;
	        // Lines are stored 0-based
	        mapping.originalLine += 1;

	        // Original column.
	        mapping.originalColumn = previousOriginalColumn + segment[3];
	        previousOriginalColumn = mapping.originalColumn;

	        if (segment.length > 4) {
	          // Original name.
	          mapping.name = previousName + segment[4];
	          previousName += segment[4];
	        }
	      }

	      generatedMappings.push(mapping);
	      if (typeof mapping.originalLine === 'number') {
	        originalMappings.push(mapping);
	      }
	    }
	  }

	  quickSort(generatedMappings, util.compareByGeneratedPositionsDeflated);
	  this.__generatedMappings = generatedMappings;

	  quickSort(originalMappings, util.compareByOriginalPositions);
	  this.__originalMappings = originalMappings;
	};

	/**
	 * Find the mapping that best matches the hypothetical "needle" mapping that
	 * we are searching for in the given "haystack" of mappings.
	 */
	BasicSourceMapConsumer.prototype._findMapping = function SourceMapConsumer_findMapping(aNeedle, aMappings, aLineName, aColumnName, aComparator, aBias) {
	  // To return the position we are searching for, we must first find the
	  // mapping for the given position and then return the opposite position it
	  // points to. Because the mappings are sorted, we can use binary search to
	  // find the best mapping.

	  if (aNeedle[aLineName] <= 0) {
	    throw new TypeError('Line must be greater than or equal to 1, got ' + aNeedle[aLineName]);
	  }
	  if (aNeedle[aColumnName] < 0) {
	    throw new TypeError('Column must be greater than or equal to 0, got ' + aNeedle[aColumnName]);
	  }

	  return binarySearch.search(aNeedle, aMappings, aComparator, aBias);
	};

	/**
	 * Compute the last column for each generated mapping. The last column is
	 * inclusive.
	 */
	BasicSourceMapConsumer.prototype.computeColumnSpans = function SourceMapConsumer_computeColumnSpans() {
	  for (var index = 0; index < this._generatedMappings.length; ++index) {
	    var mapping = this._generatedMappings[index];

	    // Mappings do not contain a field for the last generated columnt. We
	    // can come up with an optimistic estimate, however, by assuming that
	    // mappings are contiguous (i.e. given two consecutive mappings, the
	    // first mapping ends where the second one starts).
	    if (index + 1 < this._generatedMappings.length) {
	      var nextMapping = this._generatedMappings[index + 1];

	      if (mapping.generatedLine === nextMapping.generatedLine) {
	        mapping.lastGeneratedColumn = nextMapping.generatedColumn - 1;
	        continue;
	      }
	    }

	    // The last mapping for each line spans the entire line.
	    mapping.lastGeneratedColumn = Infinity;
	  }
	};

	/**
	 * 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.
	 */
	BasicSourceMapConsumer.prototype.originalPositionFor = function SourceMapConsumer_originalPositionFor(aArgs) {
	  var needle = {
	    generatedLine: util.getArg(aArgs, 'line'),
	    generatedColumn: util.getArg(aArgs, 'column')
	  };

	  var index = this._findMapping(needle, this._generatedMappings, "generatedLine", "generatedColumn", util.compareByGeneratedPositionsDeflated, util.getArg(aArgs, 'bias', SourceMapConsumer.GREATEST_LOWER_BOUND));

	  if (index >= 0) {
	    var mapping = this._generatedMappings[index];

	    if (mapping.generatedLine === needle.generatedLine) {
	      var source = util.getArg(mapping, 'source', null);
	      if (source !== null) {
	        source = this._sources.at(source);
	        source = util.computeSourceURL(this.sourceRoot, source, this._sourceMapURL);
	      }
	      var name = util.getArg(mapping, 'name', null);
	      if (name !== null) {
	        name = this._names.at(name);
	      }
	      return {
	        source: source,
	        line: util.getArg(mapping, 'originalLine', null),
	        column: util.getArg(mapping, 'originalColumn', null),
	        name: 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.
	 */
	BasicSourceMapConsumer.prototype.hasContentsOfAllSources = function BasicSourceMapConsumer_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.
	 */
	BasicSourceMapConsumer.prototype.sourceContentFor = function SourceMapConsumer_sourceContentFor(aSource, nullOnMissing) {
	  if (!this.sourcesContent) {
	    return null;
	  }

	  var index = this._findSourceIndex(aSource);
	  if (index >= 0) {
	    return this.sourcesContent[index];
	  }

	  var relativeSource = aSource;
	  if (this.sourceRoot != null) {
	    relativeSource = util.relative(this.sourceRoot, relativeSource);
	  }

	  var url;
	  if (this.sourceRoot != null && (url = util.urlParse(this.sourceRoot))) {
	    // XXX: file:// URIs and absolute paths lead to unexpected behavior for
	    // many users. We can help them out when they expect file:// URIs to
	    // behave like it would if they were running a local HTTP server. See
	    // https://bugzilla.mozilla.org/show_bug.cgi?id=885597.
	    var fileUriAbsPath = relativeSource.replace(/^file:\/\//, "");
	    if (url.scheme == "file" && this._sources.has(fileUriAbsPath)) {
	      return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)];
	    }

	    if ((!url.path || url.path == "/") && this._sources.has("/" + relativeSource)) {
	      return this.sourcesContent[this._sources.indexOf("/" + relativeSource)];
	    }
	  }

	  // 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;
	  } else {
	    throw new Error('"' + relativeSource + '" 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.
	 */
	BasicSourceMapConsumer.prototype.generatedPositionFor = function SourceMapConsumer_generatedPositionFor(aArgs) {
	  var source = util.getArg(aArgs, 'source');
	  source = this._findSourceIndex(source);
	  if (source < 0) {
	    return {
	      line: null,
	      column: null,
	      lastColumn: null
	    };
	  }

	  var needle = {
	    source: source,
	    originalLine: util.getArg(aArgs, 'line'),
	    originalColumn: util.getArg(aArgs, 'column')
	  };

	  var index = this._findMapping(needle, this._originalMappings, "originalLine", "originalColumn", util.compareByOriginalPositions, util.getArg(aArgs, 'bias', SourceMapConsumer.GREATEST_LOWER_BOUND));

	  if (index >= 0) {
	    var mapping = this._originalMappings[index];

	    if (mapping.source === needle.source) {
	      return {
	        line: util.getArg(mapping, 'generatedLine', null),
	        column: util.getArg(mapping, 'generatedColumn', null),
	        lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null)
	      };
	    }
	  }

	  return {
	    line: null,
	    column: null,
	    lastColumn: null
	  };
	};

	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
	 */
	function IndexedSourceMapConsumer(aSourceMap, aSourceMapURL) {
	  var sourceMap = aSourceMap;
	  if (typeof aSourceMap === 'string') {
	    sourceMap = util.parseSourceMapInput(aSourceMap);
	  }

	  var version = util.getArg(sourceMap, 'version');
	  var sections = util.getArg(sourceMap, 'sections');

	  if (version != this._version) {
	    throw new Error('Unsupported version: ' + version);
	  }

	  this._sources = new ArraySet();
	  this._names = new ArraySet();

	  var lastOffset = {
	    line: -1,
	    column: 0
	  };
	  this._sections = sections.map(function (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.');
	    }
	    var offset = util.getArg(s, 'offset');
	    var offsetLine = util.getArg(offset, 'line');
	    var 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;

	    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: new SourceMapConsumer(util.getArg(s, 'map'), aSourceMapURL)
	    };
	  });
	}

	IndexedSourceMapConsumer.prototype = Object.create(SourceMapConsumer.prototype);
	IndexedSourceMapConsumer.prototype.constructor = SourceMapConsumer;

	/**
	 * The version of the source mapping spec that we are consuming.
	 */
	IndexedSourceMapConsumer.prototype._version = 3;

	/**
	 * The list of original sources.
	 */
	Object.defineProperty(IndexedSourceMapConsumer.prototype, 'sources', {
	  get: function () {
	    var sources = [];
	    for (var i = 0; i < this._sections.length; i++) {
	      for (var 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.
	 */
	IndexedSourceMapConsumer.prototype.originalPositionFor = function IndexedSourceMapConsumer_originalPositionFor(aArgs) {
	  var 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.
	  var sectionIndex = binarySearch.search(needle, this._sections, function (needle, section) {
	    var cmp = needle.generatedLine - section.generatedOffset.generatedLine;
	    if (cmp) {
	      return cmp;
	    }

	    return needle.generatedColumn - section.generatedOffset.generatedColumn;
	  });
	  var 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.
	 */
	IndexedSourceMapConsumer.prototype.hasContentsOfAllSources = function IndexedSourceMapConsumer_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.
	 */
	IndexedSourceMapConsumer.prototype.sourceContentFor = function IndexedSourceMapConsumer_sourceContentFor(aSource, nullOnMissing) {
	  for (var i = 0; i < this._sections.length; i++) {
	    var section = this._sections[i];

	    var content = section.consumer.sourceContentFor(aSource, true);
	    if (content) {
	      return content;
	    }
	  }
	  if (nullOnMissing) {
	    return null;
	  } else {
	    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.
	 *
	 * 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.
	 */
	IndexedSourceMapConsumer.prototype.generatedPositionFor = function IndexedSourceMapConsumer_generatedPositionFor(aArgs) {
	  for (var i = 0; i < this._sections.length; i++) {
	    var section = this._sections[i];

	    // Only consider this section if the requested source is in the list of
	    // sources of the consumer.
	    if (section.consumer._findSourceIndex(util.getArg(aArgs, 'source')) === -1) {
	      continue;
	    }
	    var generatedPosition = section.consumer.generatedPositionFor(aArgs);
	    if (generatedPosition) {
	      var ret = {
	        line: generatedPosition.line + (section.generatedOffset.generatedLine - 1),
	        column: generatedPosition.column + (section.generatedOffset.generatedLine === generatedPosition.line ? section.generatedOffset.generatedColumn - 1 : 0)
	      };
	      return ret;
	    }
	  }

	  return {
	    line: null,
	    column: null
	  };
	};

	/**
	 * 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).
	 */
	IndexedSourceMapConsumer.prototype._parseMappings = function IndexedSourceMapConsumer_parseMappings(aStr, aSourceRoot) {
	  this.__generatedMappings = [];
	  this.__originalMappings = [];
	  for (var i = 0; i < this._sections.length; i++) {
	    var section = this._sections[i];
	    var sectionMappings = section.consumer._generatedMappings;
	    for (var j = 0; j < sectionMappings.length; j++) {
	      var mapping = sectionMappings[j];

	      var source = section.consumer._sources.at(mapping.source);
	      source = util.computeSourceURL(section.consumer.sourceRoot, source, this._sourceMapURL);
	      this._sources.add(source);
	      source = this._sources.indexOf(source);

	      var name = null;
	      if (mapping.name) {
	        name = section.consumer._names.at(mapping.name);
	        this._names.add(name);
	        name = this._names.indexOf(name);
	      }

	      // The mappings coming from the consumer for the section have
	      // generated positions relative to the start of the section, so we
	      // need to offset them to be relative to the start of the concatenated
	      // generated file.
	      var adjustedMapping = {
	        source: source,
	        generatedLine: mapping.generatedLine + (section.generatedOffset.generatedLine - 1),
	        generatedColumn: mapping.generatedColumn + (section.generatedOffset.generatedLine === mapping.generatedLine ? section.generatedOffset.generatedColumn - 1 : 0),
	        originalLine: mapping.originalLine,
	        originalColumn: mapping.originalColumn,
	        name: name
	      };

	      this.__generatedMappings.push(adjustedMapping);
	      if (typeof adjustedMapping.originalLine === 'number') {
	        this.__originalMappings.push(adjustedMapping);
	      }
	    }
	  }

	  quickSort(this.__generatedMappings, util.compareByGeneratedPositionsDeflated);
	  quickSort(this.__originalMappings, util.compareByOriginalPositions);
	};

	exports.IndexedSourceMapConsumer = IndexedSourceMapConsumer;

/***/ },
/* 18 */
/***/ function(module, exports) {

	/* -*- 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.
	  var mid = Math.floor((aHigh - aLow) / 2) + aLow;
	  var 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;
	    } else {
	      return mid;
	    }
	  } else {
	    // 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;
	    } else {
	      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;
	  }

	  var 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 than
	  // 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;
	};

/***/ },
/* 19 */
/***/ function(module, exports) {

	/* -*- 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
	 */

	// It turns out that some (most?) JavaScript engines don't self-host
	// `Array.prototype.sort`. This makes sense because C++ will likely remain
	// faster than JS when doing raw CPU-intensive sorting. However, when using a
	// custom comparator function, calling back and forth between the VM's C++ and
	// JIT'd JS is rather slow *and* loses JIT type information, resulting in
	// worse generated code for the comparator function than would be optimal. In
	// fact, when sorting with a comparator, these costs outweigh the benefits of
	// sorting in C++. By using our own JS-implemented Quick Sort (below), we get
	// a ~3500ms mean speed-up in `bench/bench.html`.

	/**
	 * Swap the elements indexed by `x` and `y` in the array `ary`.
	 *
	 * @param {Array} ary
	 *        The array.
	 * @param {Number} x
	 *        The index of the first item.
	 * @param {Number} y
	 *        The index of the second item.
	 */
	function swap(ary, x, y) {
	  var temp = ary[x];
	  ary[x] = ary[y];
	  ary[y] = temp;
	}

	/**
	 * Returns a random integer within the range `low .. high` inclusive.
	 *
	 * @param {Number} low
	 *        The lower bound on the range.
	 * @param {Number} high
	 *        The upper bound on the range.
	 */
	function randomIntInRange(low, high) {
	  return Math.round(low + Math.random() * (high - low));
	}

	/**
	 * The Quick Sort algorithm.
	 *
	 * @param {Array} ary
	 *        An array to sort.
	 * @param {function} comparator
	 *        Function to use to compare two items.
	 * @param {Number} p
	 *        Start index of the array
	 * @param {Number} r
	 *        End index of the array
	 */
	function doQuickSort(ary, comparator, p, r) {
	  // If our lower bound is less than our upper bound, we (1) partition the
	  // array into two pieces and (2) recurse on each half. If it is not, this is
	  // the empty array and our base case.

	  if (p < r) {
	    // (1) Partitioning.
	    //
	    // The partitioning chooses a pivot between `p` and `r` and moves all
	    // elements that are less than or equal to the pivot to the before it, and
	    // all the elements that are greater than it after it. The effect is that
	    // once partition is done, the pivot is in the exact place it will be when
	    // the array is put in sorted order, and it will not need to be moved
	    // again. This runs in O(n) time.

	    // Always choose a random pivot so that an input array which is reverse
	    // sorted does not cause O(n^2) running time.
	    var pivotIndex = randomIntInRange(p, r);
	    var i = p - 1;

	    swap(ary, pivotIndex, r);
	    var pivot = ary[r];

	    // Immediately after `j` is incremented in this loop, the following hold
	    // true:
	    //
	    //   * Every element in `ary[p .. i]` is less than or equal to the pivot.
	    //
	    //   * Every element in `ary[i+1 .. j-1]` is greater than the pivot.
	    for (var j = p; j < r; j++) {
	      if (comparator(ary[j], pivot) <= 0) {
	        i += 1;
	        swap(ary, i, j);
	      }
	    }

	    swap(ary, i + 1, j);
	    var q = i + 1;

	    // (2) Recurse on each half.

	    doQuickSort(ary, comparator, p, q - 1);
	    doQuickSort(ary, comparator, q + 1, r);
	  }
	}

	/**
	 * Sort the given array in-place with the given comparator function.
	 *
	 * @param {Array} ary
	 *        An array to sort.
	 * @param {function} comparator
	 *        Function to use to compare two items.
	 */
	exports.quickSort = function (ary, comparator) {
	  doQuickSort(ary, comparator, 0, ary.length - 1);
	};

/***/ },
/* 20 */
/***/ function(module, exports, __webpack_require__) {

	/* -*- 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
	 */

	var SourceMapGenerator = __webpack_require__(11).SourceMapGenerator;
	var util = __webpack_require__(14);

	// Matches a Windows-style `\r\n` newline or a `\n` newline used by all other
	// operating systems these days (capturing the result).
	var REGEX_NEWLINE = /(\r?\n)/;

	// Newline character code for charCodeAt() comparisons
	var 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!
	var 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.
	 */
	function SourceNode(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.
	 */
	SourceNode.fromStringWithSourceMap = function SourceNode_fromStringWithSourceMap(aGeneratedCode, aSourceMapConsumer, aRelativePath) {
	  // The SourceNode we want to fill with the generated code
	  // and the SourceMap
	  var 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`.
	  var remainingLines = aGeneratedCode.split(REGEX_NEWLINE);
	  var remainingLinesIndex = 0;
	  var shiftNextLine = function () {
	    var lineContents = getNextLine();
	    // The last line of a file might not have a newline.
	    var newLine = getNextLine() || "";
	    return lineContents + newLine;

	    function getNextLine() {
	      return remainingLinesIndex < remainingLines.length ? remainingLines[remainingLinesIndex++] : undefined;
	    }
	  };

	  // We need to remember the position of "remainingLines"
	  var 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.
	  var lastMapping = null;

	  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"
	        var nextLine = remainingLines[remainingLinesIndex] || '';
	        var 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) {
	      var 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) {
	    var 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 {
	      var 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.
	 */
	SourceNode.prototype.add = function SourceNode_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.
	 */
	SourceNode.prototype.prepend = function SourceNode_prepend(aChunk) {
	  if (Array.isArray(aChunk)) {
	    for (var 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.
	 */
	SourceNode.prototype.walk = function SourceNode_walk(aFn) {
	  var chunk;
	  for (var 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.
	 */
	SourceNode.prototype.join = function SourceNode_join(aSep) {
	  var newChildren;
	  var i;
	  var 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.
	 */
	SourceNode.prototype.replaceRight = function SourceNode_replaceRight(aPattern, aReplacement) {
	  var 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
	 */
	SourceNode.prototype.setSourceContent = function SourceNode_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.
	 */
	SourceNode.prototype.walkSourceContents = function SourceNode_walkSourceContents(aFn) {
	  for (var i = 0, len = this.children.length; i < len; i++) {
	    if (this.children[i][isSourceNode]) {
	      this.children[i].walkSourceContents(aFn);
	    }
	  }

	  var sources = Object.keys(this.sourceContents);
	  for (var 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.
	 */
	SourceNode.prototype.toString = function SourceNode_toString() {
	  var str = "";
	  this.walk(function (chunk) {
	    str += chunk;
	  });
	  return str;
	};

	/**
	 * Returns the string representation of this source node along with a source
	 * map.
	 */
	SourceNode.prototype.toStringWithSourceMap = function SourceNode_toStringWithSourceMap(aArgs) {
	  var generated = {
	    code: "",
	    line: 1,
	    column: 0
	  };
	  var map = new SourceMapGenerator(aArgs);
	  var sourceMappingActive = false;
	  var lastOriginalSource = null;
	  var lastOriginalLine = null;
	  var lastOriginalColumn = null;
	  var 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 (var 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: map };
	};

	exports.SourceNode = SourceNode;

/***/ },
/* 21 */
/***/ function(module, exports) {

	/* This Source Code Form is subject to the terms of the Mozilla Public
	 * License, v. 2.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 assert(condition, message) {
	  if (!condition) {
	    throw new Error(`Assertion failure: ${message}`);
	  }
	}

	module.exports = assert;

/***/ },
/* 22 */
/***/ function(module, exports, __webpack_require__) {

	let _resolveAndFetch = (() => {
	  var _ref = _asyncToGenerator(function* (generatedSource) {
	    // Fetch the sourcemap over the network and create it.
	    const { sourceMapURL, baseURL } = _resolveSourceMapURL(generatedSource);

	    const fetched = yield networkRequest(sourceMapURL, { loadFromCache: false });

	    // Create the source map and fix it up.
	    let map = new SourceMapConsumer(fetched.content, baseURL);
	    if (generatedSource.isWasm) {
	      map = new WasmRemap(map);
	    }

	    return map;
	  });

	  return function _resolveAndFetch(_x) {
	    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"); }); }; }

	/* This Source Code Form is subject to the terms of the Mozilla Public
	 * License, v. 2.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 { networkRequest } = __webpack_require__(6);
	const { getSourceMap, setSourceMap } = __webpack_require__(23);
	const { WasmRemap } = __webpack_require__(24);
	const { SourceMapConsumer } = __webpack_require__(10);

	function _resolveSourceMapURL(source) {
	  const { url = "", sourceMapURL = "" } = source;

	  if (!url) {
	    // If the source doesn't have a URL, don't resolve anything.
	    return { sourceMapURL, baseURL: sourceMapURL };
	  }

	  const resolvedURL = new URL(sourceMapURL, url);
	  const resolvedString = resolvedURL.toString();

	  let baseURL = resolvedString;
	  // When the sourceMap is a data: URL, fall back to using the
	  // source's URL, if possible.
	  if (resolvedURL.protocol == "data:") {
	    baseURL = url;
	  }

	  return { sourceMapURL: resolvedString, baseURL };
	}

	function fetchSourceMap(generatedSource) {
	  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 Promise.resolve(null);
	  }

	  // Fire off the request, set it in the cache, and return it.
	  const req = _resolveAndFetch(generatedSource);
	  // 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 };

/***/ },
/* 23 */
/***/ function(module, exports) {

	
	let sourceMapRequests = new Map();

	function clearSourceMaps() {
	  sourceMapRequests.clear();
	}

	function getSourceMap(generatedSourceId) {
	  return sourceMapRequests.get(generatedSourceId);
	}

	function setSourceMap(generatedId, request) {
	  sourceMapRequests.set(generatedId, request);
	}

	module.exports = {
	  clearSourceMaps,
	  getSourceMap,
	  setSourceMap
	};

/***/ },
/* 24 */
/***/ function(module, exports) {

	/* This Source Code Form is subject to the terms of the Mozilla Public
	 * License, v. 2.0. If 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) {
	    let result = this._map.originalPositionFor({
	      line: 1,
	      column: generatedPosition.line,
	      bias: generatedPosition.bias
	    });
	    return result;
	  }

	  _remapGeneratedPosition(position) {
	    let generatedPosition = {
	      line: position.column,
	      column: 0
	    };
	    if (this._computeColumnSpans) {
	      generatedPosition.lastColumn = Infinity;
	    }
	    return generatedPosition;
	  }

	  generatedPositionFor(originalPosition) {
	    let position = this._map.generatedPositionFor(originalPosition);
	    return this._remapGeneratedPosition(position);
	  }

	  allGeneratedPositionsFor(originalPosition) {
	    let 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 => {
	      let {
	        source,
	        generatedColumn,
	        originalLine,
	        originalColumn,
	        name
	      } = entry;
	      callback({
	        source,
	        generatedLine: generatedColumn,
	        generatedColumn: 0,
	        originalLine,
	        originalColumn,
	        name
	      });
	    }, context, order);
	  }
	}

	exports.WasmRemap = WasmRemap;

/***/ },
/* 25 */,
/* 26 */,
/* 27 */,
/* 28 */,
/* 29 */,
/* 30 */,
/* 31 */,
/* 32 */,
/* 33 */,
/* 34 */,
/* 35 */,
/* 36 */,
/* 37 */,
/* 38 */,
/* 39 */,
/* 40 */
/***/ function(module, exports, __webpack_require__) {

	/* This Source Code Form is subject to the terms of the Mozilla Public
	 * License, v. 2.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 { SourceMapConsumer } = __webpack_require__(41);

	function comparePositions(a, b) {
	  if (a.line === b.line) {
	    return a.column - b.column;
	  } else {
	    return a.line - b.line;
	  }
	}

	function getOriginalPositionName(map, generatedPosition) {
	  const originalPosition = map.originalPositionFor(generatedPosition);
	  if (!originalPosition || !originalPosition.name) {
	    return null;
	  }
	  // Verify if it's the exact position.
	  const originalPositionTest = map.originalPositionFor({
	    line: generatedPosition.line,
	    column: generatedPosition.column,
	    bias: SourceMapConsumer.LEAST_UPPER_BOUND
	  });
	  if (!originalPositionTest || originalPositionTest.column !== originalPosition.column || originalPositionTest.line !== originalPosition.line) {
	    return null;
	  }
	  return originalPosition.name;
	}

	// The `scope` has bindings that contains positions/references to the
	// generated names (these may has different original name). Going over all
	// these names and positions, to check what original name is listed there.
	// To make things more interesting the same generated name can potentially
	// be used for different original names, so building trackedBindings for all
	// names and positions.
	// Example of MappedScopeBindings:
	// ```
	// { type: 'block',
	//   bindings: {
	//     'current': 't',
	//     'start': 't',
	//     'end': 'e'
	//   } }
	// ```

	function applyBestNamePosition(trackedBindings, generatedName, nameLocation, map, currentPosition) {
	  const namePosition = {
	    line: nameLocation.line,
	    column: nameLocation.column || 0
	  };
	  const originalName = getOriginalPositionName(map, namePosition);
	  if (!originalName) {
	    return trackedBindings;
	  }

	  let trackedBinding = trackedBindings[originalName];
	  if (!trackedBinding) {
	    // No best position was found for original name yet -- using first
	    // found position.
	    trackedBindings[originalName] = {
	      generatedName,
	      bestPosition: namePosition
	    };
	  } else if (comparePositions(namePosition, currentPosition) <= 0) {
	    // The position is before location, the more recient will have needed
	    // name assignment, also discard all positions past currentPosition.
	    if (comparePositions(trackedBinding.bestPosition, namePosition) < 0 || comparePositions(trackedBinding.bestPosition, currentPosition) > 0) {
	      trackedBinding.generatedName = generatedName;
	      trackedBinding.bestPosition = namePosition;
	    }
	  } else {
	    // The position is past location, need to select more recient after
	    // specified location, but only if "before" bestPosition is not found.
	    if (comparePositions(trackedBinding.bestPosition, namePosition) > 0 && comparePositions(trackedBinding.bestPosition, currentPosition) < 0) {
	      trackedBinding.generatedName = generatedName;
	      trackedBinding.bestPosition = namePosition;
	    }
	  }
	  return trackedBindings;
	}

	function applyTrackedBindings(trackedBindings, generatedName, locations, map, currentPosition) {
	  return locations.reduce((trackedBindings, nameLocation) => applyBestNamePosition(trackedBindings, generatedName, nameLocation, map, currentPosition), trackedBindings);
	}

	/**
	 * Finds the scope binding information at the source map based on locations and
	 * generated source scopes. The generated source scopes contain variables names
	 * and all their references in the generated source. This information is merged
	 * with the source map original names information and returned as result.
	 */
	function getLocationScopes(map, generatedSourceScopes, location) {
	  const currentPosition = {
	    line: location.line,
	    column: location.column || 0
	  };

	  return generatedSourceScopes.map(scope => {
	    const trackedBindings = Object.keys(scope.bindings).reduce((trackedBindings, generatedName) => applyTrackedBindings(trackedBindings, generatedName, scope.bindings[generatedName], map, currentPosition), Object.create(null));

	    const bindings = Object.keys(trackedBindings).reduce((bindings, name) => {
	      const { generatedName } = trackedBindings[name];
	      // No need to record the binding if generated name matches original one.
	      if (name !== generatedName) {
	        bindings[name] = generatedName;
	      }
	      return bindings;
	    }, Object.create(null));
	    return {
	      type: scope.type,
	      bindings
	    };
	  });
	}

	module.exports = {
	  getLocationScopes
	};

/***/ },
/* 41 */
/***/ function(module, exports, __webpack_require__) {

	/*
	 * 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 = __webpack_require__(42).SourceMapGenerator;
	exports.SourceMapConsumer = __webpack_require__(48).SourceMapConsumer;
	exports.SourceNode = __webpack_require__(51).SourceNode;

/***/ },
/* 42 */
/***/ function(module, exports, __webpack_require__) {

	/* -*- 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
	 */

	var base64VLQ = __webpack_require__(43);
	var util = __webpack_require__(45);
	var ArraySet = __webpack_require__(46).ArraySet;
	var MappingList = __webpack_require__(47).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.
	 */
	function SourceMapGenerator(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;
	}

	SourceMapGenerator.prototype._version = 3;

	/**
	 * Creates a new SourceMapGenerator based on a SourceMapConsumer
	 *
	 * @param aSourceMapConsumer The SourceMap.
	 */
	SourceMapGenerator.fromSourceMap = function SourceMapGenerator_fromSourceMap(aSourceMapConsumer) {
	  var sourceRoot = aSourceMapConsumer.sourceRoot;
	  var generator = new SourceMapGenerator({
	    file: aSourceMapConsumer.file,
	    sourceRoot: sourceRoot
	  });
	  aSourceMapConsumer.eachMapping(function (mapping) {
	    var 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) {
	    var 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.
	 */
	SourceMapGenerator.prototype.addMapping = function SourceMapGenerator_addMapping(aArgs) {
	  var generated = util.getArg(aArgs, 'generated');
	  var original = util.getArg(aArgs, 'original', null);
	  var source = util.getArg(aArgs, 'source', null);
	  var 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 != null && original.line,
	    originalColumn: original != null && original.column,
	    source: source,
	    name: name
	  });
	};

	/**
	 * Set the source content for a source file.
	 */
	SourceMapGenerator.prototype.setSourceContent = function SourceMapGenerator_setSourceContent(aSourceFile, aSourceContent) {
	  var 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.
	 */
	SourceMapGenerator.prototype.applySourceMap = function SourceMapGenerator_applySourceMap(aSourceMapConsumer, aSourceFile, aSourceMapPath) {
	  var 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;
	  }
	  var 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.
	  var newSources = new ArraySet();
	  var 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.
	      var 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;
	        }
	      }
	    }

	    var source = mapping.source;
	    if (source != null && !newSources.has(source)) {
	      newSources.add(source);
	    }

	    var 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 (sourceFile) {
	    var content = aSourceMapConsumer.sourceContentFor(sourceFile);
	    if (content != null) {
	      if (aSourceMapPath != null) {
	        sourceFile = util.join(aSourceMapPath, sourceFile);
	      }
	      if (sourceRoot != null) {
	        sourceFile = util.relative(sourceRoot, sourceFile);
	      }
	      this.setSourceContent(sourceFile, 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.
	 */
	SourceMapGenerator.prototype._validateMapping = function SourceMapGenerator_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.
	    return;
	  } 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.
	    return;
	  } 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.
	 */
	SourceMapGenerator.prototype._serializeMappings = function SourceMapGenerator_serializeMappings() {
	  var previousGeneratedColumn = 0;
	  var previousGeneratedLine = 1;
	  var previousOriginalColumn = 0;
	  var previousOriginalLine = 0;
	  var previousName = 0;
	  var previousSource = 0;
	  var result = '';
	  var next;
	  var mapping;
	  var nameIdx;
	  var sourceIdx;

	  var mappings = this._mappings.toArray();
	  for (var 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;
	};

	SourceMapGenerator.prototype._generateSourcesContent = function SourceMapGenerator_generateSourcesContent(aSources, aSourceRoot) {
	  return aSources.map(function (source) {
	    if (!this._sourcesContents) {
	      return null;
	    }
	    if (aSourceRoot != null) {
	      source = util.relative(aSourceRoot, source);
	    }
	    var key = util.toSetString(source);
	    return Object.prototype.hasOwnProperty.call(this._sourcesContents, key) ? this._sourcesContents[key] : null;
	  }, this);
	};

	/**
	 * Externalize the source map.
	 */
	SourceMapGenerator.prototype.toJSON = function SourceMapGenerator_toJSON() {
	  var 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.
	 */
	SourceMapGenerator.prototype.toString = function SourceMapGenerator_toString() {
	  return JSON.stringify(this.toJSON());
	};

	exports.SourceMapGenerator = SourceMapGenerator;

/***/ },
/* 43 */
/***/ function(module, exports, __webpack_require__) {

	/* -*- 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.
	 */

	var base64 = __webpack_require__(44);

	// 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

	var VLQ_BASE_SHIFT = 5;

	// binary: 100000
	var VLQ_BASE = 1 << VLQ_BASE_SHIFT;

	// binary: 011111
	var VLQ_BASE_MASK = VLQ_BASE - 1;

	// binary: 100000
	var 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;
	}

	/**
	 * Converts to a two-complement value from a value where the sign bit is
	 * placed in the least significant bit.  For example, as decimals:
	 *   2 (10 binary) becomes 1, 3 (11 binary) becomes -1
	 *   4 (100 binary) becomes 2, 5 (101 binary) becomes -2
	 */
	function fromVLQSigned(aValue) {
	  var isNegative = (aValue & 1) === 1;
	  var shifted = aValue >> 1;
	  return isNegative ? -shifted : shifted;
	}

	/**
	 * Returns the base 64 VLQ encoded value.
	 */
	exports.encode = function base64VLQ_encode(aValue) {
	  var encoded = "";
	  var digit;

	  var 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;
	};

	/**
	 * Decodes the next base 64 VLQ value from the given string and returns the
	 * value and the rest of the string via the out parameter.
	 */
	exports.decode = function base64VLQ_decode(aStr, aIndex, aOutParam) {
	  var strLen = aStr.length;
	  var result = 0;
	  var shift = 0;
	  var continuation, digit;

	  do {
	    if (aIndex >= strLen) {
	      throw new Error("Expected more digits in base 64 VLQ value.");
	    }

	    digit = base64.decode(aStr.charCodeAt(aIndex++));
	    if (digit === -1) {
	      throw new Error("Invalid base64 digit: " + aStr.charAt(aIndex - 1));
	    }

	    continuation = !!(digit & VLQ_CONTINUATION_BIT);
	    digit &= VLQ_BASE_MASK;
	    result = result + (digit << shift);
	    shift += VLQ_BASE_SHIFT;
	  } while (continuation);

	  aOutParam.value = fromVLQSigned(result);
	  aOutParam.rest = aIndex;
	};

/***/ },
/* 44 */
/***/ function(module, exports) {

	/* -*- 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
	 */

	var 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);
	};

	/**
	 * Decode a single base 64 character code digit to an integer. Returns -1 on
	 * failure.
	 */
	exports.decode = function (charCode) {
	  var bigA = 65; // 'A'
	  var bigZ = 90; // 'Z'

	  var littleA = 97; // 'a'
	  var littleZ = 122; // 'z'

	  var zero = 48; // '0'
	  var nine = 57; // '9'

	  var plus = 43; // '+'
	  var slash = 47; // '/'

	  var littleOffset = 26;
	  var numberOffset = 52;

	  // 0 - 25: ABCDEFGHIJKLMNOPQRSTUVWXYZ
	  if (bigA <= charCode && charCode <= bigZ) {
	    return charCode - bigA;
	  }

	  // 26 - 51: abcdefghijklmnopqrstuvwxyz
	  if (littleA <= charCode && charCode <= littleZ) {
	    return charCode - littleA + littleOffset;
	  }

	  // 52 - 61: 0123456789
	  if (zero <= charCode && charCode <= nine) {
	    return charCode - zero + numberOffset;
	  }

	  // 62: +
	  if (charCode == plus) {
	    return 62;
	  }

	  // 63: /
	  if (charCode == slash) {
	    return 63;
	  }

	  // Invalid base64 digit.
	  return -1;
	};

/***/ },
/* 45 */
/***/ function(module, exports) {

	/* -*- 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
	 */

	/**
	 * 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;
	  } else {
	    throw new Error('"' + aName + '" is a required argument.');
	  }
	}
	exports.getArg = getArg;

	var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.]*)(?::(\d+))?(\S*)$/;
	var dataUrlRegexp = /^data:.+\,.+$/;

	function urlParse(aUrl) {
	  var match = aUrl.match(urlRegexp);
	  if (!match) {
	    return null;
	  }
	  return {
	    scheme: match[1],
	    auth: match[2],
	    host: match[3],
	    port: match[4],
	    path: match[5]
	  };
	}
	exports.urlParse = urlParse;

	function urlGenerate(aParsedUrl) {
	  var url = '';
	  if (aParsedUrl.scheme) {
	    url += aParsedUrl.scheme + ':';
	  }
	  url += '//';
	  if (aParsedUrl.auth) {
	    url += aParsedUrl.auth + '@';
	  }
	  if (aParsedUrl.host) {
	    url += aParsedUrl.host;
	  }
	  if (aParsedUrl.port) {
	    url += ":" + aParsedUrl.port;
	  }
	  if (aParsedUrl.path) {
	    url += aParsedUrl.path;
	  }
	  return url;
	}
	exports.urlGenerate = urlGenerate;

	/**
	 * Normalizes a path, or the path portion of a URL:
	 *
	 * - Replaces consecutive slashes with one slash.
	 * - Removes unnecessary '.' parts.
	 * - Removes unnecessary '<dir>/..' parts.
	 *
	 * Based on code in the Node.js 'path' core module.
	 *
	 * @param aPath The path or url to normalize.
	 */
	function normalize(aPath) {
	  var path = aPath;
	  var url = urlParse(aPath);
	  if (url) {
	    if (!url.path) {
	      return aPath;
	    }
	    path = url.path;
	  }
	  var isAbsolute = exports.isAbsolute(path);

	  var parts = path.split(/\/+/);
	  for (var part, up = 0, i = parts.length - 1; i >= 0; i--) {
	    part = parts[i];
	    if (part === '.') {
	      parts.splice(i, 1);
	    } else if (part === '..') {
	      up++;
	    } else if (up > 0) {
	      if (part === '') {
	        // The first part is blank if the path is absolute. Trying to go
	        // above the root is a no-op. Therefore we can remove all '..' parts
	        // directly after the root.
	        parts.splice(i + 1, up);
	        up = 0;
	      } else {
	        parts.splice(i, 2);
	        up--;
	      }
	    }
	  }
	  path = parts.join('/');

	  if (path === '') {
	    path = isAbsolute ? '/' : '.';
	  }

	  if (url) {
	    url.path = path;
	    return urlGenerate(url);
	  }
	  return path;
	}
	exports.normalize = normalize;

	/**
	 * Joins two paths/URLs.
	 *
	 * @param aRoot The root path or URL.
	 * @param aPath The path or URL to be joined with the root.
	 *
	 * - If aPath is a URL or a data URI, aPath is returned, unless aPath is a
	 *   scheme-relative URL: Then the scheme of aRoot, if any, is prepended
	 *   first.
	 * - Otherwise aPath is a path. If aRoot is a URL, then its path portion
	 *   is updated with the result and aRoot is returned. Otherwise the result
	 *   is returned.
	 *   - If aPath is absolute, the result is aPath.
	 *   - Otherwise the two paths are joined with a slash.
	 * - Joining for example 'http://' and 'www.example.com' is also supported.
	 */
	function join(aRoot, aPath) {
	  if (aRoot === "") {
	    aRoot = ".";
	  }
	  if (aPath === "") {
	    aPath = ".";
	  }
	  var aPathUrl = urlParse(aPath);
	  var aRootUrl = urlParse(aRoot);
	  if (aRootUrl) {
	    aRoot = aRootUrl.path || '/';
	  }

	  // `join(foo, '//www.example.org')`
	  if (aPathUrl && !aPathUrl.scheme) {
	    if (aRootUrl) {
	      aPathUrl.scheme = aRootUrl.scheme;
	    }
	    return urlGenerate(aPathUrl);
	  }

	  if (aPathUrl || aPath.match(dataUrlRegexp)) {
	    return aPath;
	  }

	  // `join('http://', 'www.example.com')`
	  if (aRootUrl && !aRootUrl.host && !aRootUrl.path) {
	    aRootUrl.host = aPath;
	    return urlGenerate(aRootUrl);
	  }

	  var joined = aPath.charAt(0) === '/' ? aPath : normalize(aRoot.replace(/\/+$/, '') + '/' + aPath);

	  if (aRootUrl) {
	    aRootUrl.path = joined;
	    return urlGenerate(aRootUrl);
	  }
	  return joined;
	}
	exports.join = join;

	exports.isAbsolute = function (aPath) {
	  return aPath.charAt(0) === '/' || !!aPath.match(urlRegexp);
	};

	/**
	 * Make a path relative to a URL or another path.
	 *
	 * @param aRoot The root path or URL.
	 * @param aPath The path or URL to be made relative to aRoot.
	 */
	function relative(aRoot, aPath) {
	  if (aRoot === "") {
	    aRoot = ".";
	  }

	  aRoot = aRoot.replace(/\/$/, '');

	  // It is possible for the path to be above the root. In this case, simply
	  // checking whether the root is a prefix of the path won't work. Instead, we
	  // need to remove components from the root one by one, until either we find
	  // a prefix that fits, or we run out of components to remove.
	  var level = 0;
	  while (aPath.indexOf(aRoot + '/') !== 0) {
	    var index = aRoot.lastIndexOf("/");
	    if (index < 0) {
	      return aPath;
	    }

	    // If the only part of the root that is left is the scheme (i.e. http://,
	    // file:///, etc.), one or more slashes (/), or simply nothing at all, we
	    // have exhausted all components, so the path is not relative to the root.
	    aRoot = aRoot.slice(0, index);
	    if (aRoot.match(/^([^\/]+:\/)?\/*$/)) {
	      return aPath;
	    }

	    ++level;
	  }

	  // Make sure we add a "../" for each component we removed from the root.
	  return Array(level + 1).join("../") + aPath.substr(aRoot.length + 1);
	}
	exports.relative = relative;

	var supportsNullProto = function () {
	  var 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;
	  }

	  var length = s.length;

	  if (length < 9 /* "__proto__".length */) {
	      return false;
	    }

	  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;
	    }

	  for (var i = length - 10; i >= 0; i--) {
	    if (s.charCodeAt(i) !== 36 /* '$' */) {
	        return false;
	      }
	  }

	  return true;
	}

	/**
	 * Comparator between two mappings where the original positions are compared.
	 *
	 * Optionally pass in `true` as `onlyCompareGenerated` to consider two
	 * mappings with the same original source/line/column, but different generated
	 * line and column the same. Useful when searching for a mapping with a
	 * stubbed out mapping.
	 */
	function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) {
	  var cmp = 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 || onlyCompareOriginal) {
	    return cmp;
	  }

	  cmp = mappingA.generatedColumn - mappingB.generatedColumn;
	  if (cmp !== 0) {
	    return cmp;
	  }

	  cmp = mappingA.generatedLine - mappingB.generatedLine;
	  if (cmp !== 0) {
	    return cmp;
	  }

	  return mappingA.name - mappingB.name;
	}
	exports.compareByOriginalPositions = compareByOriginalPositions;

	/**
	 * Comparator between two mappings with deflated source and name indices where
	 * the generated positions are compared.
	 *
	 * Optionally pass in `true` as `onlyCompareGenerated` to consider two
	 * mappings with the same generated line and column, but different
	 * source/name/original line and column the same. Useful when searching for a
	 * mapping with a stubbed out mapping.
	 */
	function compareByGeneratedPositionsDeflated(mappingA, mappingB, onlyCompareGenerated) {
	  var cmp = mappingA.generatedLine - mappingB.generatedLine;
	  if (cmp !== 0) {
	    return cmp;
	  }

	  cmp = mappingA.generatedColumn - mappingB.generatedColumn;
	  if (cmp !== 0 || onlyCompareGenerated) {
	    return cmp;
	  }

	  cmp = 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 mappingA.name - mappingB.name;
	}
	exports.compareByGeneratedPositionsDeflated = compareByGeneratedPositionsDeflated;

	function strcmp(aStr1, aStr2) {
	  if (aStr1 === aStr2) {
	    return 0;
	  }

	  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) {
	  var 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;

/***/ },
/* 46 */
/***/ function(module, exports, __webpack_require__) {

	/* -*- 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
	 */

	var util = __webpack_require__(45);
	var has = Object.prototype.hasOwnProperty;
	var hasNativeMap = typeof Map !== "undefined";

	/**
	 * 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.
	 */
	function ArraySet() {
	  this._array = [];
	  this._set = hasNativeMap ? new Map() : Object.create(null);
	}

	/**
	 * Static method for creating ArraySet instances from an existing array.
	 */
	ArraySet.fromArray = function ArraySet_fromArray(aArray, aAllowDuplicates) {
	  var set = new ArraySet();
	  for (var 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
	 */
	ArraySet.prototype.size = function ArraySet_size() {
	  return hasNativeMap ? this._set.size : Object.getOwnPropertyNames(this._set).length;
	};

	/**
	 * Add the given string to this set.
	 *
	 * @param String aStr
	 */
	ArraySet.prototype.add = function ArraySet_add(aStr, aAllowDuplicates) {
	  var sStr = hasNativeMap ? aStr : util.toSetString(aStr);
	  var isDuplicate = hasNativeMap ? this.has(aStr) : has.call(this._set, sStr);
	  var idx = this._array.length;
	  if (!isDuplicate || aAllowDuplicates) {
	    this._array.push(aStr);
	  }
	  if (!isDuplicate) {
	    if (hasNativeMap) {
	      this._set.set(aStr, idx);
	    } else {
	      this._set[sStr] = idx;
	    }
	  }
	};

	/**
	 * Is the given string a member of this set?
	 *
	 * @param String aStr
	 */
	ArraySet.prototype.has = function ArraySet_has(aStr) {
	  if (hasNativeMap) {
	    return this._set.has(aStr);
	  } else {
	    var sStr = util.toSetString(aStr);
	    return has.call(this._set, sStr);
	  }
	};

	/**
	 * What is the index of the given string in the array?
	 *
	 * @param String aStr
	 */
	ArraySet.prototype.indexOf = function ArraySet_indexOf(aStr) {
	  if (hasNativeMap) {
	    var idx = this._set.get(aStr);
	    if (idx >= 0) {
	      return idx;
	    }
	  } else {
	    var sStr = util.toSetString(aStr);
	    if (has.call(this._set, sStr)) {
	      return this._set[sStr];
	    }
	  }

	  throw new Error('"' + aStr + '" is not in the set.');
	};

	/**
	 * What is the element at the given index?
	 *
	 * @param Number aIdx
	 */
	ArraySet.prototype.at = function ArraySet_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.
	 */
	ArraySet.prototype.toArray = function ArraySet_toArray() {
	  return this._array.slice();
	};

	exports.ArraySet = ArraySet;

/***/ },
/* 47 */
/***/ function(module, exports, __webpack_require__) {

	/* -*- 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
	 */

	var util = __webpack_require__(45);

	/**
	 * Determine whether mappingB is after mappingA with respect to generated
	 * position.
	 */
	function generatedPositionAfter(mappingA, mappingB) {
	  // Optimized for most common case
	  var lineA = mappingA.generatedLine;
	  var lineB = mappingB.generatedLine;
	  var columnA = mappingA.generatedColumn;
	  var 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 neglibable overhead in general
	 * case for a large speedup in case of mappings being added in order.
	 */
	function MappingList() {
	  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.
	 */
	MappingList.prototype.unsortedForEach = function MappingList_forEach(aCallback, aThisArg) {
	  this._array.forEach(aCallback, aThisArg);
	};

	/**
	 * Add the given source mapping.
	 *
	 * @param Object aMapping
	 */
	MappingList.prototype.add = function MappingList_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.
	 */
	MappingList.prototype.toArray = function MappingList_toArray() {
	  if (!this._sorted) {
	    this._array.sort(util.compareByGeneratedPositionsInflated);
	    this._sorted = true;
	  }
	  return this._array;
	};

	exports.MappingList = MappingList;

/***/ },
/* 48 */
/***/ function(module, exports, __webpack_require__) {

	/* -*- 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
	 */

	var util = __webpack_require__(45);
	var binarySearch = __webpack_require__(49);
	var ArraySet = __webpack_require__(46).ArraySet;
	var base64VLQ = __webpack_require__(43);
	var quickSort = __webpack_require__(50).quickSort;

	function SourceMapConsumer(aSourceMap) {
	  var sourceMap = aSourceMap;
	  if (typeof aSourceMap === 'string') {
	    sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
	  }

	  return sourceMap.sections != null ? new IndexedSourceMapConsumer(sourceMap) : new BasicSourceMapConsumer(sourceMap);
	}

	SourceMapConsumer.fromSourceMap = function (aSourceMap) {
	  return BasicSourceMapConsumer.fromSourceMap(aSourceMap);
	};

	/**
	 * The version of the source mapping spec that we are consuming.
	 */
	SourceMapConsumer.prototype._version = 3;

	// `__generatedMappings` and `__originalMappings` are arrays that hold the
	// parsed mapping coordinates from the source map's "mappings" attribute. They
	// are lazily instantiated, accessed via the `_generatedMappings` and
	// `_originalMappings` getters respectively, and we only parse the mappings
	// and create these arrays once queried for a source location. We jump through
	// these hoops because there can be many thousands of mappings, and parsing
	// them is expensive, so we only want to do it if we must.
	//
	// Each object in the arrays is of the form:
	//
	//     {
	//       generatedLine: The line number in the generated code,
	//       generatedColumn: The column number in the generated code,
	//       source: The path to the original source file that generated this
	//               chunk of code,
	//       originalLine: The line number in the original source that
	//                     corresponds to this chunk of generated code,
	//       originalColumn: The column number in the original source that
	//                       corresponds to this chunk of generated code,
	//       name: The name of the original symbol which generated this chunk of
	//             code.
	//     }
	//
	// All properties except for `generatedLine` and `generatedColumn` can be
	// `null`.
	//
	// `_generatedMappings` is ordered by the generated positions.
	//
	// `_originalMappings` is ordered by the original positions.

	SourceMapConsumer.prototype.__generatedMappings = null;
	Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', {
	  get: function () {
	    if (!this.__generatedMappings) {
	      this._parseMappings(this._mappings, this.sourceRoot);
	    }

	    return this.__generatedMappings;
	  }
	});

	SourceMapConsumer.prototype.__originalMappings = null;
	Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', {
	  get: function () {
	    if (!this.__originalMappings) {
	      this._parseMappings(this._mappings, this.sourceRoot);
	    }

	    return this.__originalMappings;
	  }
	});

	SourceMapConsumer.prototype._charIsMappingSeparator = function SourceMapConsumer_charIsMappingSeparator(aStr, index) {
	  var c = aStr.charAt(index);
	  return c === ";" || c === ",";
	};

	/**
	 * 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).
	 */
	SourceMapConsumer.prototype._parseMappings = function SourceMapConsumer_parseMappings(aStr, aSourceRoot) {
	  throw new Error("Subclasses must implement _parseMappings");
	};

	SourceMapConsumer.GENERATED_ORDER = 1;
	SourceMapConsumer.ORIGINAL_ORDER = 2;

	SourceMapConsumer.GREATEST_LOWER_BOUND = 1;
	SourceMapConsumer.LEAST_UPPER_BOUND = 2;

	/**
	 * 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`.
	 */
	SourceMapConsumer.prototype.eachMapping = function SourceMapConsumer_eachMapping(aCallback, aContext, aOrder) {
	  var context = aContext || null;
	  var order = aOrder || SourceMapConsumer.GENERATED_ORDER;

	  var mappings;
	  switch (order) {
	    case SourceMapConsumer.GENERATED_ORDER:
	      mappings = this._generatedMappings;
	      break;
	    case SourceMapConsumer.ORIGINAL_ORDER:
	      mappings = this._originalMappings;
	      break;
	    default:
	      throw new Error("Unknown order of iteration.");
	  }

	  var sourceRoot = this.sourceRoot;
	  mappings.map(function (mapping) {
	    var source = mapping.source === null ? null : this._sources.at(mapping.source);
	    if (source != null && sourceRoot != null) {
	      source = util.join(sourceRoot, source);
	    }
	    return {
	      source: source,
	      generatedLine: mapping.generatedLine,
	      generatedColumn: mapping.generatedColumn,
	      originalLine: mapping.originalLine,
	      originalColumn: mapping.originalColumn,
	      name: mapping.name === null ? null : this._names.at(mapping.name)
	    };
	  }, this).forEach(aCallback, context);
	};

	/**
	 * 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.
	 *   - column: Optional. the column number in the original source.
	 *
	 * and an array of objects is returned, each with the following properties:
	 *
	 *   - line: The line number in the generated source, or null.
	 *   - column: The column number in the generated source, or null.
	 */
	SourceMapConsumer.prototype.allGeneratedPositionsFor = function SourceMapConsumer_allGeneratedPositionsFor(aArgs) {
	  var line = util.getArg(aArgs, 'line');

	  // When there is no exact match, BasicSourceMapConsumer.prototype._findMapping
	  // returns the index of the closest mapping less than the needle. By
	  // setting needle.originalColumn to 0, we thus find the last mapping for
	  // the given line, provided such a mapping exists.
	  var needle = {
	    source: util.getArg(aArgs, 'source'),
	    originalLine: line,
	    originalColumn: util.getArg(aArgs, 'column', 0)
	  };

	  if (this.sourceRoot != null) {
	    needle.source = util.relative(this.sourceRoot, needle.source);
	  }
	  if (!this._sources.has(needle.source)) {
	    return [];
	  }
	  needle.source = this._sources.indexOf(needle.source);

	  var mappings = [];

	  var index = this._findMapping(needle, this._originalMappings, "originalLine", "originalColumn", util.compareByOriginalPositions, binarySearch.LEAST_UPPER_BOUND);
	  if (index >= 0) {
	    var mapping = this._originalMappings[index];

	    if (aArgs.column === undefined) {
	      var originalLine = mapping.originalLine;

	      // Iterate until either we run out of mappings, or we run into
	      // a mapping for a different line than the one we found. Since
	      // mappings are sorted, this is guaranteed to find all mappings for
	      // the line we found.
	      while (mapping && mapping.originalLine === originalLine) {
	        mappings.push({
	          line: util.getArg(mapping, 'generatedLine', null),
	          column: util.getArg(mapping, 'generatedColumn', null),
	          lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null)
	        });

	        mapping = this._originalMappings[++index];
	      }
	    } else {
	      var originalColumn = mapping.originalColumn;

	      // Iterate until either we run out of mappings, or we run into
	      // a mapping for a different line than the one we were searching for.
	      // Since mappings are sorted, this is guaranteed to find all mappings for
	      // the line we are searching for.
	      while (mapping && mapping.originalLine === line && mapping.originalColumn == originalColumn) {
	        mappings.push({
	          line: util.getArg(mapping, 'generatedLine', null),
	          column: util.getArg(mapping, 'generatedColumn', null),
	          lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null)
	        });

	        mapping = this._originalMappings[++index];
	      }
	    }
	  }

	  return mappings;
	};

	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 only 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 referrenced 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;"
	 *     }
	 *
	 * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1#
	 */
	function BasicSourceMapConsumer(aSourceMap) {
	  var sourceMap = aSourceMap;
	  if (typeof aSourceMap === 'string') {
	    sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
	  }

	  var version = util.getArg(sourceMap, 'version');
	  var sources = util.getArg(sourceMap, 'sources');
	  // Sass 3.3 leaves out the 'names' array, so we deviate from the spec (which
	  // requires the array) to play nice here.
	  var names = util.getArg(sourceMap, 'names', []);
	  var sourceRoot = util.getArg(sourceMap, 'sourceRoot', null);
	  var sourcesContent = util.getArg(sourceMap, 'sourcesContent', null);
	  var mappings = util.getArg(sourceMap, 'mappings');
	  var file = util.getArg(sourceMap, 'file', 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 != this._version) {
	    throw new Error('Unsupported version: ' + version);
	  }

	  sources = sources.map(String)
	  // Some source maps produce relative source paths like "./foo.js" instead of
	  // "foo.js".  Normalize these first so that future comparisons will succeed.
	  // See bugzil.la/1090768.
	  .map(util.normalize)
	  // Always ensure that absolute sources are internally stored relative to
	  // the source root, if the source root is absolute. Not doing this would
	  // be particularly problematic when the source root is a prefix of the
	  // source (valid, but why??). See github issue #199 and bugzil.la/1188982.
	  .map(function (source) {
	    return sourceRoot && util.isAbsolute(sourceRoot) && util.isAbsolute(source) ? util.relative(sourceRoot, source) : source;
	  });

	  // 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.
	  this._names = ArraySet.fromArray(names.map(String), true);
	  this._sources = ArraySet.fromArray(sources, true);

	  this.sourceRoot = sourceRoot;
	  this.sourcesContent = sourcesContent;
	  this._mappings = mappings;
	  this.file = file;
	}

	BasicSourceMapConsumer.prototype = Object.create(SourceMapConsumer.prototype);
	BasicSourceMapConsumer.prototype.consumer = SourceMapConsumer;

	/**
	 * Create a BasicSourceMapConsumer from a SourceMapGenerator.
	 *
	 * @param SourceMapGenerator aSourceMap
	 *        The source map that will be consumed.
	 * @returns BasicSourceMapConsumer
	 */
	BasicSourceMapConsumer.fromSourceMap = function SourceMapConsumer_fromSourceMap(aSourceMap) {
	  var smc = Object.create(BasicSourceMapConsumer.prototype);

	  var names = smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true);
	  var sources = smc._sources = ArraySet.fromArray(aSourceMap._sources.toArray(), true);
	  smc.sourceRoot = aSourceMap._sourceRoot;
	  smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(), smc.sourceRoot);
	  smc.file = aSourceMap._file;

	  // Because we are modifying the entries (by converting string sources and
	  // names to indices into the sources and names ArraySets), we have to make
	  // a copy of the entry or else bad things happen. Shared mutable state
	  // strikes again! See github issue #191.

	  var generatedMappings = aSourceMap._mappings.toArray().slice();
	  var destGeneratedMappings = smc.__generatedMappings = [];
	  var destOriginalMappings = smc.__originalMappings = [];

	  for (var i = 0, length = generatedMappings.length; i < length; i++) {
	    var srcMapping = generatedMappings[i];
	    var destMapping = new Mapping();
	    destMapping.generatedLine = srcMapping.generatedLine;
	    destMapping.generatedColumn = srcMapping.generatedColumn;

	    if (srcMapping.source) {
	      destMapping.source = sources.indexOf(srcMapping.source);
	      destMapping.originalLine = srcMapping.originalLine;
	      destMapping.originalColumn = srcMapping.originalColumn;

	      if (srcMapping.name) {
	        destMapping.name = names.indexOf(srcMapping.name);
	      }

	      destOriginalMappings.push(destMapping);
	    }

	    destGeneratedMappings.push(destMapping);
	  }

	  quickSort(smc.__originalMappings, util.compareByOriginalPositions);

	  return smc;
	};

	/**
	 * The version of the source mapping spec that we are consuming.
	 */
	BasicSourceMapConsumer.prototype._version = 3;

	/**
	 * The list of original sources.
	 */
	Object.defineProperty(BasicSourceMapConsumer.prototype, 'sources', {
	  get: function () {
	    return this._sources.toArray().map(function (s) {
	      return this.sourceRoot != null ? util.join(this.sourceRoot, s) : s;
	    }, this);
	  }
	});

	/**
	 * Provide the JIT with a nice shape / hidden class.
	 */
	function Mapping() {
	  this.generatedLine = 0;
	  this.generatedColumn = 0;
	  this.source = null;
	  this.originalLine = null;
	  this.originalColumn = null;
	  this.name = null;
	}

	/**
	 * 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).
	 */
	BasicSourceMapConsumer.prototype._parseMappings = function SourceMapConsumer_parseMappings(aStr, aSourceRoot) {
	  var generatedLine = 1;
	  var previousGeneratedColumn = 0;
	  var previousOriginalLine = 0;
	  var previousOriginalColumn = 0;
	  var previousSource = 0;
	  var previousName = 0;
	  var length = aStr.length;
	  var index = 0;
	  var cachedSegments = {};
	  var temp = {};
	  var originalMappings = [];
	  var generatedMappings = [];
	  var mapping, str, segment, end, value;

	  while (index < length) {
	    if (aStr.charAt(index) === ';') {
	      generatedLine++;
	      index++;
	      previousGeneratedColumn = 0;
	    } else if (aStr.charAt(index) === ',') {
	      index++;
	    } else {
	      mapping = new Mapping();
	      mapping.generatedLine = generatedLine;

	      // Because each offset is encoded relative to the previous one,
	      // many segments often have the same encoding. We can exploit this
	      // fact by caching the parsed variable length fields of each segment,
	      // allowing us to avoid a second parse if we encounter the same
	      // segment again.
	      for (end = index; end < length; end++) {
	        if (this._charIsMappingSeparator(aStr, end)) {
	          break;
	        }
	      }
	      str = aStr.slice(index, end);

	      segment = cachedSegments[str];
	      if (segment) {
	        index += str.length;
	      } else {
	        segment = [];
	        while (index < end) {
	          base64VLQ.decode(aStr, index, temp);
	          value = temp.value;
	          index = temp.rest;
	          segment.push(value);
	        }

	        if (segment.length === 2) {
	          throw new Error('Found a source, but no line and column');
	        }

	        if (segment.length === 3) {
	          throw new Error('Found a source and line, but no column');
	        }

	        cachedSegments[str] = segment;
	      }

	      // Generated column.
	      mapping.generatedColumn = previousGeneratedColumn + segment[0];
	      previousGeneratedColumn = mapping.generatedColumn;

	      if (segment.length > 1) {
	        // Original source.
	        mapping.source = previousSource + segment[1];
	        previousSource += segment[1];

	        // Original line.
	        mapping.originalLine = previousOriginalLine + segment[2];
	        previousOriginalLine = mapping.originalLine;
	        // Lines are stored 0-based
	        mapping.originalLine += 1;

	        // Original column.
	        mapping.originalColumn = previousOriginalColumn + segment[3];
	        previousOriginalColumn = mapping.originalColumn;

	        if (segment.length > 4) {
	          // Original name.
	          mapping.name = previousName + segment[4];
	          previousName += segment[4];
	        }
	      }

	      generatedMappings.push(mapping);
	      if (typeof mapping.originalLine === 'number') {
	        originalMappings.push(mapping);
	      }
	    }
	  }

	  quickSort(generatedMappings, util.compareByGeneratedPositionsDeflated);
	  this.__generatedMappings = generatedMappings;

	  quickSort(originalMappings, util.compareByOriginalPositions);
	  this.__originalMappings = originalMappings;
	};

	/**
	 * Find the mapping that best matches the hypothetical "needle" mapping that
	 * we are searching for in the given "haystack" of mappings.
	 */
	BasicSourceMapConsumer.prototype._findMapping = function SourceMapConsumer_findMapping(aNeedle, aMappings, aLineName, aColumnName, aComparator, aBias) {
	  // To return the position we are searching for, we must first find the
	  // mapping for the given position and then return the opposite position it
	  // points to. Because the mappings are sorted, we can use binary search to
	  // find the best mapping.

	  if (aNeedle[aLineName] <= 0) {
	    throw new TypeError('Line must be greater than or equal to 1, got ' + aNeedle[aLineName]);
	  }
	  if (aNeedle[aColumnName] < 0) {
	    throw new TypeError('Column must be greater than or equal to 0, got ' + aNeedle[aColumnName]);
	  }

	  return binarySearch.search(aNeedle, aMappings, aComparator, aBias);
	};

	/**
	 * Compute the last column for each generated mapping. The last column is
	 * inclusive.
	 */
	BasicSourceMapConsumer.prototype.computeColumnSpans = function SourceMapConsumer_computeColumnSpans() {
	  for (var index = 0; index < this._generatedMappings.length; ++index) {
	    var mapping = this._generatedMappings[index];

	    // Mappings do not contain a field for the last generated columnt. We
	    // can come up with an optimistic estimate, however, by assuming that
	    // mappings are contiguous (i.e. given two consecutive mappings, the
	    // first mapping ends where the second one starts).
	    if (index + 1 < this._generatedMappings.length) {
	      var nextMapping = this._generatedMappings[index + 1];

	      if (mapping.generatedLine === nextMapping.generatedLine) {
	        mapping.lastGeneratedColumn = nextMapping.generatedColumn - 1;
	        continue;
	      }
	    }

	    // The last mapping for each line spans the entire line.
	    mapping.lastGeneratedColumn = Infinity;
	  }
	};

	/**
	 * 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.
	 *   - column: The column number in the generated source.
	 *   - 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.
	 *   - column: The column number in the original source, or null.
	 *   - name: The original identifier, or null.
	 */
	BasicSourceMapConsumer.prototype.originalPositionFor = function SourceMapConsumer_originalPositionFor(aArgs) {
	  var needle = {
	    generatedLine: util.getArg(aArgs, 'line'),
	    generatedColumn: util.getArg(aArgs, 'column')
	  };

	  var index = this._findMapping(needle, this._generatedMappings, "generatedLine", "generatedColumn", util.compareByGeneratedPositionsDeflated, util.getArg(aArgs, 'bias', SourceMapConsumer.GREATEST_LOWER_BOUND));

	  if (index >= 0) {
	    var mapping = this._generatedMappings[index];

	    if (mapping.generatedLine === needle.generatedLine) {
	      var source = util.getArg(mapping, 'source', null);
	      if (source !== null) {
	        source = this._sources.at(source);
	        if (this.sourceRoot != null) {
	          source = util.join(this.sourceRoot, source);
	        }
	      }
	      var name = util.getArg(mapping, 'name', null);
	      if (name !== null) {
	        name = this._names.at(name);
	      }
	      return {
	        source: source,
	        line: util.getArg(mapping, 'originalLine', null),
	        column: util.getArg(mapping, 'originalColumn', null),
	        name: 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.
	 */
	BasicSourceMapConsumer.prototype.hasContentsOfAllSources = function BasicSourceMapConsumer_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.
	 */
	BasicSourceMapConsumer.prototype.sourceContentFor = function SourceMapConsumer_sourceContentFor(aSource, nullOnMissing) {
	  if (!this.sourcesContent) {
	    return null;
	  }

	  if (this.sourceRoot != null) {
	    aSource = util.relative(this.sourceRoot, aSource);
	  }

	  if (this._sources.has(aSource)) {
	    return this.sourcesContent[this._sources.indexOf(aSource)];
	  }

	  var url;
	  if (this.sourceRoot != null && (url = util.urlParse(this.sourceRoot))) {
	    // XXX: file:// URIs and absolute paths lead to unexpected behavior for
	    // many users. We can help them out when they expect file:// URIs to
	    // behave like it would if they were running a local HTTP server. See
	    // https://bugzilla.mozilla.org/show_bug.cgi?id=885597.
	    var fileUriAbsPath = aSource.replace(/^file:\/\//, "");
	    if (url.scheme == "file" && this._sources.has(fileUriAbsPath)) {
	      return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)];
	    }

	    if ((!url.path || url.path == "/") && this._sources.has("/" + aSource)) {
	      return this.sourcesContent[this._sources.indexOf("/" + aSource)];
	    }
	  }

	  // 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;
	  } else {
	    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.
	 *   - column: The column number in the original source.
	 *   - 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.
	 *   - column: The column number in the generated source, or null.
	 */
	BasicSourceMapConsumer.prototype.generatedPositionFor = function SourceMapConsumer_generatedPositionFor(aArgs) {
	  var source = util.getArg(aArgs, 'source');
	  if (this.sourceRoot != null) {
	    source = util.relative(this.sourceRoot, source);
	  }
	  if (!this._sources.has(source)) {
	    return {
	      line: null,
	      column: null,
	      lastColumn: null
	    };
	  }
	  source = this._sources.indexOf(source);

	  var needle = {
	    source: source,
	    originalLine: util.getArg(aArgs, 'line'),
	    originalColumn: util.getArg(aArgs, 'column')
	  };

	  var index = this._findMapping(needle, this._originalMappings, "originalLine", "originalColumn", util.compareByOriginalPositions, util.getArg(aArgs, 'bias', SourceMapConsumer.GREATEST_LOWER_BOUND));

	  if (index >= 0) {
	    var mapping = this._originalMappings[index];

	    if (mapping.source === needle.source) {
	      return {
	        line: util.getArg(mapping, 'generatedLine', null),
	        column: util.getArg(mapping, 'generatedColumn', null),
	        lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null)
	      };
	    }
	  }

	  return {
	    line: null,
	    column: null,
	    lastColumn: null
	  };
	};

	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 only 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;"
	 *      }
	 *    }],
	 *  }
	 *
	 * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#heading=h.535es3xeprgt
	 */
	function IndexedSourceMapConsumer(aSourceMap) {
	  var sourceMap = aSourceMap;
	  if (typeof aSourceMap === 'string') {
	    sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
	  }

	  var version = util.getArg(sourceMap, 'version');
	  var sections = util.getArg(sourceMap, 'sections');

	  if (version != this._version) {
	    throw new Error('Unsupported version: ' + version);
	  }

	  this._sources = new ArraySet();
	  this._names = new ArraySet();

	  var lastOffset = {
	    line: -1,
	    column: 0
	  };
	  this._sections = sections.map(function (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.');
	    }
	    var offset = util.getArg(s, 'offset');
	    var offsetLine = util.getArg(offset, 'line');
	    var 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;

	    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: new SourceMapConsumer(util.getArg(s, 'map'))
	    };
	  });
	}

	IndexedSourceMapConsumer.prototype = Object.create(SourceMapConsumer.prototype);
	IndexedSourceMapConsumer.prototype.constructor = SourceMapConsumer;

	/**
	 * The version of the source mapping spec that we are consuming.
	 */
	IndexedSourceMapConsumer.prototype._version = 3;

	/**
	 * The list of original sources.
	 */
	Object.defineProperty(IndexedSourceMapConsumer.prototype, 'sources', {
	  get: function () {
	    var sources = [];
	    for (var i = 0; i < this._sections.length; i++) {
	      for (var 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.
	 *   - column: The column number in the generated source.
	 *
	 * 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.
	 *   - column: The column number in the original source, or null.
	 *   - name: The original identifier, or null.
	 */
	IndexedSourceMapConsumer.prototype.originalPositionFor = function IndexedSourceMapConsumer_originalPositionFor(aArgs) {
	  var 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.
	  var sectionIndex = binarySearch.search(needle, this._sections, function (needle, section) {
	    var cmp = needle.generatedLine - section.generatedOffset.generatedLine;
	    if (cmp) {
	      return cmp;
	    }

	    return needle.generatedColumn - section.generatedOffset.generatedColumn;
	  });
	  var 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.
	 */
	IndexedSourceMapConsumer.prototype.hasContentsOfAllSources = function IndexedSourceMapConsumer_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.
	 */
	IndexedSourceMapConsumer.prototype.sourceContentFor = function IndexedSourceMapConsumer_sourceContentFor(aSource, nullOnMissing) {
	  for (var i = 0; i < this._sections.length; i++) {
	    var section = this._sections[i];

	    var content = section.consumer.sourceContentFor(aSource, true);
	    if (content) {
	      return content;
	    }
	  }
	  if (nullOnMissing) {
	    return null;
	  } else {
	    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.
	 *   - column: The column number in the original source.
	 *
	 * and an object is returned with the following properties:
	 *
	 *   - line: The line number in the generated source, or null.
	 *   - column: The column number in the generated source, or null.
	 */
	IndexedSourceMapConsumer.prototype.generatedPositionFor = function IndexedSourceMapConsumer_generatedPositionFor(aArgs) {
	  for (var i = 0; i < this._sections.length; i++) {
	    var section = this._sections[i];

	    // Only consider this section if the requested source is in the list of
	    // sources of the consumer.
	    if (!section.consumer.sources.includes(util.getArg(aArgs, 'source'))) {
	      continue;
	    }
	    var generatedPosition = section.consumer.generatedPositionFor(aArgs);
	    if (generatedPosition) {
	      var ret = {
	        line: generatedPosition.line + (section.generatedOffset.generatedLine - 1),
	        column: generatedPosition.column + (section.generatedOffset.generatedLine === generatedPosition.line ? section.generatedOffset.generatedColumn - 1 : 0)
	      };
	      return ret;
	    }
	  }

	  return {
	    line: null,
	    column: null
	  };
	};

	/**
	 * 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).
	 */
	IndexedSourceMapConsumer.prototype._parseMappings = function IndexedSourceMapConsumer_parseMappings(aStr, aSourceRoot) {
	  this.__generatedMappings = [];
	  this.__originalMappings = [];
	  for (var i = 0; i < this._sections.length; i++) {
	    var section = this._sections[i];
	    var sectionMappings = section.consumer._generatedMappings;
	    for (var j = 0; j < sectionMappings.length; j++) {
	      var mapping = sectionMappings[j];

	      var source = section.consumer._sources.at(mapping.source);
	      if (section.consumer.sourceRoot !== null) {
	        source = util.join(section.consumer.sourceRoot, source);
	      }
	      this._sources.add(source);
	      source = this._sources.indexOf(source);

	      var name = section.consumer._names.at(mapping.name);
	      this._names.add(name);
	      name = this._names.indexOf(name);

	      // The mappings coming from the consumer for the section have
	      // generated positions relative to the start of the section, so we
	      // need to offset them to be relative to the start of the concatenated
	      // generated file.
	      var adjustedMapping = {
	        source: source,
	        generatedLine: mapping.generatedLine + (section.generatedOffset.generatedLine - 1),
	        generatedColumn: mapping.generatedColumn + (section.generatedOffset.generatedLine === mapping.generatedLine ? section.generatedOffset.generatedColumn - 1 : 0),
	        originalLine: mapping.originalLine,
	        originalColumn: mapping.originalColumn,
	        name: name
	      };

	      this.__generatedMappings.push(adjustedMapping);
	      if (typeof adjustedMapping.originalLine === 'number') {
	        this.__originalMappings.push(adjustedMapping);
	      }
	    }
	  }

	  quickSort(this.__generatedMappings, util.compareByGeneratedPositionsDeflated);
	  quickSort(this.__originalMappings, util.compareByOriginalPositions);
	};

	exports.IndexedSourceMapConsumer = IndexedSourceMapConsumer;

/***/ },
/* 49 */
/***/ function(module, exports) {

	/* -*- 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.
	  var mid = Math.floor((aHigh - aLow) / 2) + aLow;
	  var 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;
	    } else {
	      return mid;
	    }
	  } else {
	    // 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;
	    } else {
	      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;
	  }

	  var 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 than
	  // 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;
	};

/***/ },
/* 50 */
/***/ function(module, exports) {

	/* -*- 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
	 */

	// It turns out that some (most?) JavaScript engines don't self-host
	// `Array.prototype.sort`. This makes sense because C++ will likely remain
	// faster than JS when doing raw CPU-intensive sorting. However, when using a
	// custom comparator function, calling back and forth between the VM's C++ and
	// JIT'd JS is rather slow *and* loses JIT type information, resulting in
	// worse generated code for the comparator function than would be optimal. In
	// fact, when sorting with a comparator, these costs outweigh the benefits of
	// sorting in C++. By using our own JS-implemented Quick Sort (below), we get
	// a ~3500ms mean speed-up in `bench/bench.html`.

	/**
	 * Swap the elements indexed by `x` and `y` in the array `ary`.
	 *
	 * @param {Array} ary
	 *        The array.
	 * @param {Number} x
	 *        The index of the first item.
	 * @param {Number} y
	 *        The index of the second item.
	 */
	function swap(ary, x, y) {
	  var temp = ary[x];
	  ary[x] = ary[y];
	  ary[y] = temp;
	}

	/**
	 * Returns a random integer within the range `low .. high` inclusive.
	 *
	 * @param {Number} low
	 *        The lower bound on the range.
	 * @param {Number} high
	 *        The upper bound on the range.
	 */
	function randomIntInRange(low, high) {
	  return Math.round(low + Math.random() * (high - low));
	}

	/**
	 * The Quick Sort algorithm.
	 *
	 * @param {Array} ary
	 *        An array to sort.
	 * @param {function} comparator
	 *        Function to use to compare two items.
	 * @param {Number} p
	 *        Start index of the array
	 * @param {Number} r
	 *        End index of the array
	 */
	function doQuickSort(ary, comparator, p, r) {
	  // If our lower bound is less than our upper bound, we (1) partition the
	  // array into two pieces and (2) recurse on each half. If it is not, this is
	  // the empty array and our base case.

	  if (p < r) {
	    // (1) Partitioning.
	    //
	    // The partitioning chooses a pivot between `p` and `r` and moves all
	    // elements that are less than or equal to the pivot to the before it, and
	    // all the elements that are greater than it after it. The effect is that
	    // once partition is done, the pivot is in the exact place it will be when
	    // the array is put in sorted order, and it will not need to be moved
	    // again. This runs in O(n) time.

	    // Always choose a random pivot so that an input array which is reverse
	    // sorted does not cause O(n^2) running time.
	    var pivotIndex = randomIntInRange(p, r);
	    var i = p - 1;

	    swap(ary, pivotIndex, r);
	    var pivot = ary[r];

	    // Immediately after `j` is incremented in this loop, the following hold
	    // true:
	    //
	    //   * Every element in `ary[p .. i]` is less than or equal to the pivot.
	    //
	    //   * Every element in `ary[i+1 .. j-1]` is greater than the pivot.
	    for (var j = p; j < r; j++) {
	      if (comparator(ary[j], pivot) <= 0) {
	        i += 1;
	        swap(ary, i, j);
	      }
	    }

	    swap(ary, i + 1, j);
	    var q = i + 1;

	    // (2) Recurse on each half.

	    doQuickSort(ary, comparator, p, q - 1);
	    doQuickSort(ary, comparator, q + 1, r);
	  }
	}

	/**
	 * Sort the given array in-place with the given comparator function.
	 *
	 * @param {Array} ary
	 *        An array to sort.
	 * @param {function} comparator
	 *        Function to use to compare two items.
	 */
	exports.quickSort = function (ary, comparator) {
	  doQuickSort(ary, comparator, 0, ary.length - 1);
	};

/***/ },
/* 51 */
/***/ function(module, exports, __webpack_require__) {

	/* -*- 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
	 */

	var SourceMapGenerator = __webpack_require__(42).SourceMapGenerator;
	var util = __webpack_require__(45);

	// Matches a Windows-style `\r\n` newline or a `\n` newline used by all other
	// operating systems these days (capturing the result).
	var REGEX_NEWLINE = /(\r?\n)/;

	// Newline character code for charCodeAt() comparisons
	var 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!
	var 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.
	 */
	function SourceNode(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.
	 */
	SourceNode.fromStringWithSourceMap = function SourceNode_fromStringWithSourceMap(aGeneratedCode, aSourceMapConsumer, aRelativePath) {
	  // The SourceNode we want to fill with the generated code
	  // and the SourceMap
	  var 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`.
	  var remainingLines = aGeneratedCode.split(REGEX_NEWLINE);
	  var remainingLinesIndex = 0;
	  var shiftNextLine = function () {
	    var lineContents = getNextLine();
	    // The last line of a file might not have a newline.
	    var newLine = getNextLine() || "";
	    return lineContents + newLine;

	    function getNextLine() {
	      return remainingLinesIndex < remainingLines.length ? remainingLines[remainingLinesIndex++] : undefined;
	    }
	  };

	  // We need to remember the position of "remainingLines"
	  var 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.
	  var lastMapping = null;

	  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"
	        var nextLine = remainingLines[remainingLinesIndex];
	        var 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) {
	      var 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) {
	    var 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 {
	      var 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.
	 */
	SourceNode.prototype.add = function SourceNode_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.
	 */
	SourceNode.prototype.prepend = function SourceNode_prepend(aChunk) {
	  if (Array.isArray(aChunk)) {
	    for (var 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.
	 */
	SourceNode.prototype.walk = function SourceNode_walk(aFn) {
	  var chunk;
	  for (var 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.
	 */
	SourceNode.prototype.join = function SourceNode_join(aSep) {
	  var newChildren;
	  var i;
	  var 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.
	 */
	SourceNode.prototype.replaceRight = function SourceNode_replaceRight(aPattern, aReplacement) {
	  var 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
	 */
	SourceNode.prototype.setSourceContent = function SourceNode_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.
	 */
	SourceNode.prototype.walkSourceContents = function SourceNode_walkSourceContents(aFn) {
	  for (var i = 0, len = this.children.length; i < len; i++) {
	    if (this.children[i][isSourceNode]) {
	      this.children[i].walkSourceContents(aFn);
	    }
	  }

	  var sources = Object.keys(this.sourceContents);
	  for (var 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.
	 */
	SourceNode.prototype.toString = function SourceNode_toString() {
	  var str = "";
	  this.walk(function (chunk) {
	    str += chunk;
	  });
	  return str;
	};

	/**
	 * Returns the string representation of this source node along with a source
	 * map.
	 */
	SourceNode.prototype.toStringWithSourceMap = function SourceNode_toStringWithSourceMap(aArgs) {
	  var generated = {
	    code: "",
	    line: 1,
	    column: 0
	  };
	  var map = new SourceMapGenerator(aArgs);
	  var sourceMappingActive = false;
	  var lastOriginalSource = null;
	  var lastOriginalLine = null;
	  var lastOriginalColumn = null;
	  var 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 (var 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: map };
	};

	exports.SourceNode = SourceNode;

/***/ }
/******/ ])
});
;PK
!<�4D<P.P.>chrome/devtools/modules/devtools/client/shared/source-utils.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/shared/l10n");

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);
const CHAR_CODE_CAP_S = "S".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"
    let 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) {
  let data = gSourceNamesStore.get(source);

  if (data) {
    return data;
  }

  let short, long, host;
  const sourceStr = source ? String(source) : "";

  // If `data:...` uri
  if (isDataScheme(sourceStr)) {
    let 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);
      let result = { short, long: sourceStr };
      gSourceNamesStore.set(source, result);
      return result;
    }
  }

  // If Scratchpad URI, like "Scratchpad/1"; no modifications,
  // and short/long are the same.
  if (isScratchpadScheme(sourceStr)) {
    let result = { short: sourceStr, 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);
  }

  let 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;
}

/**
 * Checks for a Scratchpad URI, like "Scratchpad/1"
 */
function isScratchpadScheme(location, i = 0) {
  return location.charCodeAt(i) === CHAR_CODE_CAP_S &&
         location.charCodeAt(++i) === CHAR_CODE_C &&
         location.charCodeAt(++i) === CHAR_CODE_R &&
         location.charCodeAt(++i) === CHAR_CODE_A &&
         location.charCodeAt(++i) === CHAR_CODE_T &&
         location.charCodeAt(++i) === CHAR_CODE_C &&
         location.charCodeAt(++i) === CHAR_CODE_H &&
         location.charCodeAt(++i) === CHAR_CODE_P &&
         location.charCodeAt(++i) === CHAR_CODE_A &&
         location.charCodeAt(++i) === CHAR_CODE_D &&
         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) {
  let 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 isChromeScheme(location, i = 0) {
  let firstChar = location.charCodeAt(i);

  switch (firstChar) {
    // "chrome://"
    case CHAR_CODE_C:
      if (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;

    // "resource://"
    case CHAR_CODE_R:
      if (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;

    // "jar:file://"
    case CHAR_CODE_J:
      if (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;

    default:
      return false;
  }
}

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.isScratchpadScheme = isScratchpadScheme;
exports.isChromeScheme = isChromeScheme;
exports.isContentScheme = isContentScheme;
exports.isWASM = isWASM;
exports.isDataScheme = isDataScheme;
exports.getSourceMappedFile = getSourceMappedFile;
PK
!<���J))Bchrome/devtools/modules/devtools/client/shared/stylesheet-utils.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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";

/*
 * Append a stylesheet to the provided XUL document.
 *
 * @param  {Document} xulDocument
 *         The XUL 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(xulDocument, url) {
  let styleSheetAttr = `href="${url}" type="text/css"`;
  let styleSheet = xulDocument.createProcessingInstruction(
    "xml-stylesheet", styleSheetAttr);
  let loadPromise = new Promise((resolve, reject) => {
    function onload() {
      styleSheet.removeEventListener("load", onload);
      styleSheet.removeEventListener("error", onerror);
      resolve();
    }
    function onerror() {
      styleSheet.removeEventListener("load", onload);
      styleSheet.removeEventListener("error", onerror);
      reject("Failed to load theme file " + url);
    }

    styleSheet.addEventListener("load", onload);
    styleSheet.addEventListener("error", onerror);
  });
  xulDocument.insertBefore(styleSheet, xulDocument.documentElement);
  return {styleSheet, loadPromise};
}

exports.appendStyleSheet = appendStyleSheet;
PK
!<H����Cchrome/devtools/modules/devtools/client/shared/suggestion-picker.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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) => {
    let 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;
PK
!<��A�.�.;chrome/devtools/modules/devtools/client/shared/telemetry.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 TOOLS_OPENED_PREF = "devtools.telemetry.tools.opened.version";

function Telemetry() {
  // Bind pretty much all functions so that callers do not need to.
  this.toolOpened = this.toolOpened.bind(this);
  this.toolClosed = this.toolClosed.bind(this);
  this.log = this.log.bind(this);
  this.logScalar = this.logScalar.bind(this);
  this.logKeyedScalar = this.logKeyedScalar.bind(this);
  this.logOncePerBrowserVersion = this.logOncePerBrowserVersion.bind(this);
  this.destroy = this.destroy.bind(this);

  this._timers = new Map();
}

module.exports = Telemetry;

var Services = require("Services");

Telemetry.prototype = {
  _histograms: {
    toolbox: {
      histogram: "DEVTOOLS_TOOLBOX_OPENED_COUNT",
      timerHistogram: "DEVTOOLS_TOOLBOX_TIME_ACTIVE_SECONDS"
    },
    options: {
      histogram: "DEVTOOLS_OPTIONS_OPENED_COUNT",
      timerHistogram: "DEVTOOLS_OPTIONS_TIME_ACTIVE_SECONDS"
    },
    webconsole: {
      histogram: "DEVTOOLS_WEBCONSOLE_OPENED_COUNT",
      timerHistogram: "DEVTOOLS_WEBCONSOLE_TIME_ACTIVE_SECONDS"
    },
    browserconsole: {
      histogram: "DEVTOOLS_BROWSERCONSOLE_OPENED_COUNT",
      timerHistogram: "DEVTOOLS_BROWSERCONSOLE_TIME_ACTIVE_SECONDS"
    },
    inspector: {
      histogram: "DEVTOOLS_INSPECTOR_OPENED_COUNT",
      timerHistogram: "DEVTOOLS_INSPECTOR_TIME_ACTIVE_SECONDS"
    },
    ruleview: {
      histogram: "DEVTOOLS_RULEVIEW_OPENED_COUNT",
      timerHistogram: "DEVTOOLS_RULEVIEW_TIME_ACTIVE_SECONDS"
    },
    computedview: {
      histogram: "DEVTOOLS_COMPUTEDVIEW_OPENED_COUNT",
      timerHistogram: "DEVTOOLS_COMPUTEDVIEW_TIME_ACTIVE_SECONDS"
    },
    layoutview: {
      histogram: "DEVTOOLS_LAYOUTVIEW_OPENED_COUNT",
      timerHistogram: "DEVTOOLS_LAYOUTVIEW_TIME_ACTIVE_SECONDS"
    },
    fontinspector: {
      histogram: "DEVTOOLS_FONTINSPECTOR_OPENED_COUNT",
      timerHistogram: "DEVTOOLS_FONTINSPECTOR_TIME_ACTIVE_SECONDS"
    },
    animationinspector: {
      histogram: "DEVTOOLS_ANIMATIONINSPECTOR_OPENED_COUNT",
      timerHistogram: "DEVTOOLS_ANIMATIONINSPECTOR_TIME_ACTIVE_SECONDS"
    },
    jsdebugger: {
      histogram: "DEVTOOLS_JSDEBUGGER_OPENED_COUNT",
      timerHistogram: "DEVTOOLS_JSDEBUGGER_TIME_ACTIVE_SECONDS"
    },
    jsbrowserdebugger: {
      histogram: "DEVTOOLS_JSBROWSERDEBUGGER_OPENED_COUNT",
      timerHistogram: "DEVTOOLS_JSBROWSERDEBUGGER_TIME_ACTIVE_SECONDS"
    },
    styleeditor: {
      histogram: "DEVTOOLS_STYLEEDITOR_OPENED_COUNT",
      timerHistogram: "DEVTOOLS_STYLEEDITOR_TIME_ACTIVE_SECONDS"
    },
    shadereditor: {
      histogram: "DEVTOOLS_SHADEREDITOR_OPENED_COUNT",
      timerHistogram: "DEVTOOLS_SHADEREDITOR_TIME_ACTIVE_SECONDS"
    },
    webaudioeditor: {
      histogram: "DEVTOOLS_WEBAUDIOEDITOR_OPENED_COUNT",
      timerHistogram: "DEVTOOLS_WEBAUDIOEDITOR_TIME_ACTIVE_SECONDS"
    },
    canvasdebugger: {
      histogram: "DEVTOOLS_CANVASDEBUGGER_OPENED_COUNT",
      timerHistogram: "DEVTOOLS_CANVASDEBUGGER_TIME_ACTIVE_SECONDS"
    },
    performance: {
      histogram: "DEVTOOLS_JSPROFILER_OPENED_COUNT",
      timerHistogram: "DEVTOOLS_JSPROFILER_TIME_ACTIVE_SECONDS"
    },
    memory: {
      histogram: "DEVTOOLS_MEMORY_OPENED_COUNT",
      timerHistogram: "DEVTOOLS_MEMORY_TIME_ACTIVE_SECONDS"
    },
    netmonitor: {
      histogram: "DEVTOOLS_NETMONITOR_OPENED_COUNT",
      timerHistogram: "DEVTOOLS_NETMONITOR_TIME_ACTIVE_SECONDS"
    },
    storage: {
      histogram: "DEVTOOLS_STORAGE_OPENED_COUNT",
      timerHistogram: "DEVTOOLS_STORAGE_TIME_ACTIVE_SECONDS"
    },
    dom: {
      histogram: "DEVTOOLS_DOM_OPENED_COUNT",
      timerHistogram: "DEVTOOLS_DOM_TIME_ACTIVE_SECONDS"
    },
    paintflashing: {
      histogram: "DEVTOOLS_PAINTFLASHING_OPENED_COUNT",
      timerHistogram: "DEVTOOLS_PAINTFLASHING_TIME_ACTIVE_SECONDS"
    },
    scratchpad: {
      histogram: "DEVTOOLS_SCRATCHPAD_OPENED_COUNT",
      timerHistogram: "DEVTOOLS_SCRATCHPAD_TIME_ACTIVE_SECONDS"
    },
    "scratchpad-window": {
      histogram: "DEVTOOLS_SCRATCHPAD_WINDOW_OPENED_COUNT",
    },
    responsive: {
      histogram: "DEVTOOLS_RESPONSIVE_OPENED_COUNT",
      timerHistogram: "DEVTOOLS_RESPONSIVE_TIME_ACTIVE_SECONDS"
    },
    eyedropper: {
      histogram: "DEVTOOLS_EYEDROPPER_OPENED_COUNT",
    },
    menueyedropper: {
      histogram: "DEVTOOLS_MENU_EYEDROPPER_OPENED_COUNT",
    },
    pickereyedropper: {
      histogram: "DEVTOOLS_PICKER_EYEDROPPER_OPENED_COUNT",
    },
    toolbareyedropper: {
      scalar: "devtools.toolbar.eyedropper.opened",
    },
    copyuniquecssselector: {
      scalar: "devtools.copy.unique.css.selector.opened",
    },
    copyfullcssselector: {
      scalar: "devtools.copy.full.css.selector.opened",
    },
    copyxpath: {
      scalar: "devtools.copy.xpath.opened",
    },
    developertoolbar: {
      histogram: "DEVTOOLS_DEVELOPERTOOLBAR_OPENED_COUNT",
      timerHistogram: "DEVTOOLS_DEVELOPERTOOLBAR_TIME_ACTIVE_SECONDS"
    },
    aboutdebugging: {
      histogram: "DEVTOOLS_ABOUTDEBUGGING_OPENED_COUNT",
      timerHistogram: "DEVTOOLS_ABOUTDEBUGGING_TIME_ACTIVE_SECONDS"
    },
    webide: {
      histogram: "DEVTOOLS_WEBIDE_OPENED_COUNT",
      timerHistogram: "DEVTOOLS_WEBIDE_TIME_ACTIVE_SECONDS"
    },
    webideNewProject: {
      histogram: "DEVTOOLS_WEBIDE_NEW_PROJECT_COUNT",
    },
    webideImportProject: {
      histogram: "DEVTOOLS_WEBIDE_IMPORT_PROJECT_COUNT",
    },
    custom: {
      histogram: "DEVTOOLS_CUSTOM_OPENED_COUNT",
      timerHistogram: "DEVTOOLS_CUSTOM_TIME_ACTIVE_SECONDS"
    },
    gridInspectorShowGridAreasOverlayChecked: {
      scalar: "devtools.grid.showGridAreasOverlay.checked",
    },
    gridInspectorShowGridLineNumbersChecked: {
      scalar: "devtools.grid.showGridLineNumbers.checked",
    },
    gridInspectorShowInfiniteLinesChecked: {
      scalar: "devtools.grid.showInfiniteLines.checked",
    },
  },

  /**
   * Add an entry to a histogram.
   *
   * @param  {String} id
   *         Used to look up the relevant histogram ID and log true to that
   *         histogram.
   */
  toolOpened: function (id) {
    let charts = this._histograms[id] || this._histograms.custom;

    if (charts.histogram) {
      this.log(charts.histogram, true);
    }
    if (charts.timerHistogram) {
      this.startTimer(charts.timerHistogram);
    }
    if (charts.scalar) {
      this.logScalar(charts.scalar, 1);
    }
  },

  /**
   * Record that an action occurred.  Aliases to `toolOpened`, so it's just for
   * readability at the call site for cases where we aren't actually opening
   * tools.
   */
  actionOccurred(id) {
    this.toolOpened(id);
  },

  toolClosed: function (id) {
    let charts = this._histograms[id];

    if (!charts || !charts.timerHistogram) {
      return;
    }

    this.stopTimer(charts.timerHistogram);
  },

  /**
   * Record the start time for a timing-based histogram entry.
   *
   * @param String histogramId
   *        Histogram in which the data is to be stored.
   */
  startTimer: function (histogramId) {
    this._timers.set(histogramId, new Date());
  },

  /**
   * Stop the timer and log elasped time for a timing-based histogram entry.
   *
   * @param String histogramId
   *        Histogram in which the data is to be stored.
   * @param String key [optional]
   *        Optional key for a keyed histogram.
   */
  stopTimer: function (histogramId, key) {
    let startTime = this._timers.get(histogramId);
    if (startTime) {
      let time = (new Date() - startTime) / 1000;
      if (!key) {
        this.log(histogramId, time);
      } else {
        this.logKeyed(histogramId, key, time);
      }
      this._timers.delete(histogramId);
    }
  },

  /**
   * Log a value to a histogram.
   *
   * @param  {String} histogramId
   *         Histogram in which the data is to be stored.
   * @param  value
   *         Value to store.
   */
  log: function (histogramId, value) {
    if (!histogramId) {
      return;
    }

    try {
      let histogram = Services.telemetry.getHistogramById(histogramId);
      histogram.add(value);
    } catch (e) {
      dump(`Warning: An attempt was made to write to the ${histogramId} ` +
           `histogram, which is not defined in Histograms.json\n`);
    }
  },

  /**
   * Log a value to a scalar.
   *
   * @param  {String} scalarId
   *         Scalar in which the data is to be stored.
   * @param  value
   *         Value to store.
   */
  logScalar: function (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.`);

        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`);
    }
  },

  /**
   * 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.
   */
  logKeyedScalar: function (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.`);

        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`);
    }
  },

  /**
   * Log a value to a keyed histogram.
   *
   * @param  {String} histogramId
   *         Histogram in which the data is to be stored.
   * @param  {String} key
   *         The key within the single histogram.
   * @param  [value]
   *         Optional value to store.
   */
  logKeyed: function (histogramId, key, value) {
    if (histogramId) {
      try {
        let histogram = Services.telemetry.getKeyedHistogramById(histogramId);

        if (typeof value === "undefined") {
          histogram.add(key);
        } else {
          histogram.add(key, value);
        }
      } catch (e) {
        dump(`Warning: An attempt was made to write to the ${histogramId} ` +
             `histogram, which is not defined in Histograms.json\n`);
      }
    }
  },

  /**
   * Log info about usage once per browser version. This allows us to discover
   * how many individual users are using our tools for each browser version.
   *
   * @param  {String} perUserHistogram
   *         Histogram in which the data is to be stored.
   */
  logOncePerBrowserVersion: function (perUserHistogram, value) {
    let currentVersion = Services.appinfo.version;
    let latest = Services.prefs.getCharPref(TOOLS_OPENED_PREF);
    let latestObj = JSON.parse(latest);

    let lastVersionHistogramUpdated = latestObj[perUserHistogram];

    if (typeof lastVersionHistogramUpdated == "undefined" ||
        lastVersionHistogramUpdated !== currentVersion) {
      latestObj[perUserHistogram] = currentVersion;
      latest = JSON.stringify(latestObj);
      Services.prefs.setCharPref(TOOLS_OPENED_PREF, latest);
      this.log(perUserHistogram, value);
    }
  },

  destroy: function () {
    for (let histogramId of this._timers.keys()) {
      this.stopTimer(histogramId);
    }
  }
};
PK
!<�����7chrome/devtools/modules/devtools/client/shared/theme.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

/**
 * Colors for themes taken from:
 * https://developer.mozilla.org/en-US/docs/Tools/DevToolsColors
 */

const Services = require("Services");

const variableFileContents = require("theme-loader!devtools/client/themes/variables.css");

const THEME_SELECTOR_STRINGS = {
  light: ":root.theme-light {",
  dark: ":root.theme-dark {",
  firebug: ":root.theme-firebug {",
  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.
  let 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 string value of the current theme,
 * like "dark" or "light".
 */
const getTheme = exports.getTheme = () => {
  return Services.prefs.getCharPref(THEME_PREF);
};

/**
 * 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) => {
  let themeName = theme || getTheme();
  let themeFile = getThemeFile(themeName);
  let match = themeFile.match(new RegExp("--theme-" + type + ": (.*);"));
  let 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.prefs.addObserver(THEME_PREF, observer);
};

/**
 * Remove an observer for theme changes.
 */
const removeThemeObserver = exports.removeThemeObserver = observer => {
  Services.prefs.removeObserver(THEME_PREF, observer);
};
/* eslint-enable */
PK
!<�����6chrome/devtools/modules/devtools/client/shared/undo.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

/**
 * 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: function () {
    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: function () {
    if (this._batchDepth++ === 0) {
      this._batch = [];
    }
  },

  /**
   * End a batch of related changes, performing its action and adding
   * it to the undo stack.
   */
  endBatch: function () {
    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.
    let start = Math.max((this._index + 1) - this.maxUndo, 0);
    this._stack = this._stack.slice(start, this._index);

    let batch = this._batch;
    delete this._batch;
    let entry = {
      do: function () {
        for (let item of batch) {
          item.do();
        }
      },
      undo: function () {
        for (let i = batch.length - 1; i >= 0; i--) {
          batch[i].undo();
        }
      }
    };
    this._stack.push(entry);
    this._index = this._stack.length;
    entry.do();
    this._change();
  },

  /**
   * 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: function (toDo, undo) {
    this.startBatch();
    this._batch.push({ do: toDo, undo });
    this.endBatch();
  },

  /*
   * Returns true if undo() will do anything.
   */
  canUndo: function () {
    return this._index > 0;
  },

  /**
   * Undo the top of the undo stack.
   *
   * @return true if an action was undone.
   */
  undo: function () {
    if (!this.canUndo()) {
      return false;
    }
    this._stack[--this._index].undo();
    this._change();
    return true;
  },

  /**
   * Returns true if redo() will do anything.
   */
  canRedo: function () {
    return this._stack.length > this._index;
  },

  /**
   * Redo the most recently undone action.
   *
   * @return true if an action was redone.
   */
  redo: function () {
    if (!this.canRedo()) {
      return false;
    }
    this._stack[this._index++].do();
    this._change();
    return true;
  },

  _change: function () {
    if (this._controllerWindow) {
      this._controllerWindow.goUpdateCommand("cmd_undo");
      this._controllerWindow.goUpdateCommand("cmd_redo");
    }
  },

  /**
   * ViewController implementation for undo/redo.
   */

  /**
   * Install this object as a command controller.
   */
  installController: function (controllerWindow) {
    let 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: function () {
    if (!this._controllerWindow) {
      return;
    }
    this._controllerWindow.controllers.removeController(this);
  },

  supportsCommand: function (command) {
    return (command == "cmd_undo" ||
            command == "cmd_redo");
  },

  isCommandEnabled: function (command) {
    switch (command) {
      case "cmd_undo": return this.canUndo();
      case "cmd_redo": return this.canRedo();
    }
    return false;
  },

  doCommand: function (command) {
    switch (command) {
      case "cmd_undo": return this.undo();
      case "cmd_redo": return this.redo();
      default: return null;
    }
  },

  onEvent: function (event) {},
};
PK
!<��c}�}�@chrome/devtools/modules/devtools/client/shared/vendor/WasmDis.js"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/* 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_1 = require("./WasmParser");
function typeToString(type) {
    switch (type) {
        case -1 /* i32 */: return 'i32';
        case -2 /* i64 */: return 'i64';
        case -3 /* f32 */: return 'f32';
        case -4 /* f64 */: return 'f64';
        case -16 /* anyfunc */: return 'anyfunc';
        default: throw new Error('Unexpected type');
    }
}
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 ? '-infinity' : 'infinity';
    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 ? '-infinity' : 'infinity';
    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 memoryAddressToString(address, code) {
    var defaultAlignFlags;
    switch (code) {
        case 41 /* i64_load */:
        case 55 /* i64_store */:
        case 43 /* f64_load */:
        case 57 /* f64_store */:
        case 65026 /* i64_atomic_wait */:
        case 65041 /* i64_atomic_load */:
        case 65048 /* i64_atomic_store */:
        case 65055 /* i64_atomic_rmw_add */:
        case 65062 /* i64_atomic_rmw_sub */:
        case 65069 /* i64_atomic_rmw_and */:
        case 65076 /* i64_atomic_rmw_or */:
        case 65083 /* i64_atomic_rmw_xor */:
        case 65090 /* i64_atomic_rmw_xchg */:
        case 65097 /* i64_atomic_rmw_cmpxchg */:
            defaultAlignFlags = 3;
            break;
        case 40 /* i32_load */:
        case 52 /* i64_load32_s */:
        case 53 /* i64_load32_u */:
        case 54 /* i32_store */:
        case 62 /* i64_store32 */:
        case 42 /* f32_load */:
        case 56 /* f32_store */:
        case 65024 /* atomic_wake */:
        case 65025 /* i32_atomic_wait */:
        case 65040 /* i32_atomic_load */:
        case 65046 /* i64_atomic_load32_u */:
        case 65047 /* i32_atomic_store */:
        case 65053 /* i64_atomic_store32 */:
        case 65054 /* i32_atomic_rmw_add */:
        case 65060 /* i64_atomic_rmw32_u_add */:
        case 65061 /* i32_atomic_rmw_sub */:
        case 65067 /* i64_atomic_rmw32_u_sub */:
        case 65068 /* i32_atomic_rmw_and */:
        case 65074 /* i64_atomic_rmw32_u_and */:
        case 65075 /* i32_atomic_rmw_or */:
        case 65081 /* i64_atomic_rmw32_u_or */:
        case 65082 /* i32_atomic_rmw_xor */:
        case 65088 /* i64_atomic_rmw32_u_xor */:
        case 65089 /* i32_atomic_rmw_xchg */:
        case 65095 /* i64_atomic_rmw32_u_xchg */:
        case 65096 /* i32_atomic_rmw_cmpxchg */:
        case 65102 /* i64_atomic_rmw32_u_cmpxchg */:
            defaultAlignFlags = 2;
            break;
        case 46 /* i32_load16_s */:
        case 47 /* i32_load16_u */:
        case 50 /* i64_load16_s */:
        case 51 /* i64_load16_u */:
        case 59 /* i32_store16 */:
        case 61 /* i64_store16 */:
        case 65043 /* i32_atomic_load16_u */:
        case 65045 /* i64_atomic_load16_u */:
        case 65050 /* i32_atomic_store16 */:
        case 65052 /* i64_atomic_store16 */:
        case 65057 /* i32_atomic_rmw16_u_add */:
        case 65059 /* i64_atomic_rmw16_u_add */:
        case 65064 /* i32_atomic_rmw16_u_sub */:
        case 65066 /* i64_atomic_rmw16_u_sub */:
        case 65071 /* i32_atomic_rmw16_u_and */:
        case 65073 /* i64_atomic_rmw16_u_and */:
        case 65078 /* i32_atomic_rmw16_u_or */:
        case 65080 /* i64_atomic_rmw16_u_or */:
        case 65085 /* i32_atomic_rmw16_u_xor */:
        case 65087 /* i64_atomic_rmw16_u_xor */:
        case 65092 /* i32_atomic_rmw16_u_xchg */:
        case 65094 /* i64_atomic_rmw16_u_xchg */:
        case 65099 /* i32_atomic_rmw16_u_cmpxchg */:
        case 65101 /* i64_atomic_rmw16_u_cmpxchg */:
            defaultAlignFlags = 1;
            break;
        case 44 /* i32_load8_s */:
        case 45 /* i32_load8_u */:
        case 48 /* i64_load8_s */:
        case 49 /* i64_load8_u */:
        case 58 /* i32_store8 */:
        case 60 /* i64_store8 */:
        case 65042 /* i32_atomic_load8_u */:
        case 65044 /* i64_atomic_load8_u */:
        case 65049 /* i32_atomic_store8 */:
        case 65051 /* i64_atomic_store8 */:
        case 65056 /* i32_atomic_rmw8_u_add */:
        case 65058 /* i64_atomic_rmw8_u_add */:
        case 65063 /* i32_atomic_rmw8_u_sub */:
        case 65065 /* i64_atomic_rmw8_u_sub */:
        case 65070 /* i32_atomic_rmw8_u_and */:
        case 65072 /* i64_atomic_rmw8_u_and */:
        case 65077 /* i32_atomic_rmw8_u_or */:
        case 65079 /* i64_atomic_rmw8_u_or */:
        case 65084 /* i32_atomic_rmw8_u_xor */:
        case 65086 /* i64_atomic_rmw8_u_xor */:
        case 65091 /* i32_atomic_rmw8_u_xchg */:
        case 65093 /* i64_atomic_rmw8_u_xchg */:
        case 65098 /* i32_atomic_rmw8_u_cmpxchg */:
        case 65100 /* i64_atomic_rmw8_u_cmpxchg */:
            defaultAlignFlags = 0;
            break;
    }
    if (address.flags == defaultAlignFlags)
        return !address.offset ? null : "offset=" + address.offset;
    if (!address.offset)
        return "align=" + (1 << address.flags);
    return "offset=" + (address.offset | 0) + " align=" + (1 << address.flags);
}
function globalTypeToString(type) {
    if (!type.mutability)
        return typeToString(type.contentType);
    return "(mut " + typeToString(type.contentType) + ")";
}
function limitsToString(limits) {
    return limits.initial + (limits.maximum !== undefined ? ' ' + limits.maximum : '');
}
var paddingCache = ['0', '00', '000'];
function formatHex(n, width) {
    var s = n.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 = '  ';
var operatorCodeNamesCache = null;
function getOperatorName(code) {
    if (!operatorCodeNamesCache) {
        operatorCodeNamesCache = Object.create(null);
        Object.keys(WasmParser_1.OperatorCodeNames).forEach(function (key) {
            var value = WasmParser_1.OperatorCodeNames[key];
            if (typeof value !== 'string')
                return;
            operatorCodeNamesCache[key] = value.replace(/^([if](32|64))_/, "$1.").replace(/_([if](32|64))$/, "\/$1");
        });
    }
    return operatorCodeNamesCache[code];
}
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) {
        // TODO '$memory' + index;
        return isRef ? '' + index : "(;" + index + ";)";
    };
    DefaultNameResolver.prototype.getGlobalName = function (index, isRef) {
        return '$global' + 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.getLabel = function (index) {
        return '$label' + index;
    };
    return DefaultNameResolver;
}());
exports.DefaultNameResolver = DefaultNameResolver;
var NumericNameResolver = /** @class */ (function () {
    function NumericNameResolver() {
    }
    NumericNameResolver.prototype.getTypeName = function (index, isRef) {
        return isRef ? '' + index : "(;" + index + ";)";
    };
    NumericNameResolver.prototype.getTableName = function (index, isRef) {
        return isRef ? '' + index : "(;" + index + ";)";
    };
    NumericNameResolver.prototype.getMemoryName = function (index, isRef) {
        return isRef ? '' + index : "(;" + index + ";)";
    };
    NumericNameResolver.prototype.getGlobalName = function (index, isRef) {
        return isRef ? '' + index : "(;" + index + ";)";
    };
    NumericNameResolver.prototype.getFunctionName = function (index, isImport, isRef) {
        return isRef ? '' + index : "(;" + index + ";)";
    };
    NumericNameResolver.prototype.getVariableName = function (funcIndex, index, isRef) {
        return isRef ? '' + index : "(;" + index + ";)";
    };
    NumericNameResolver.prototype.getLabel = function (index) {
        return null;
    };
    return NumericNameResolver;
}());
exports.NumericNameResolver = NumericNameResolver;
var LineBuffer = /** @class */ (function () {
    function LineBuffer() {
        this._firstPart = '';
        this._secondPart = '';
        this._thirdPart = '';
        this._count = 0;
    }
    Object.defineProperty(LineBuffer.prototype, "length", {
        get: function () {
            switch (this._count) {
                case 0:
                    return 0;
                case 1:
                    return this._firstPart.length;
                case 2:
                    return this._firstPart.length + this._secondPart.length;
                default:
                    return this._firstPart.length +
                        this._secondPart.length +
                        this._thirdPart.length;
            }
        },
        enumerable: true,
        configurable: true
    });
    LineBuffer.prototype.append = function (part) {
        switch (this._count) {
            case 0:
                this._firstPart = part;
                this._count = 1;
                break;
            case 1:
                this._secondPart = part;
                this._count = 2;
                break;
            case 2:
                this._thirdPart = part;
                this._count = 3;
                break;
            default:
                this._count = 1;
                this._firstPart = this._firstPart + this._secondPart +
                    this._thirdPart + part;
                break;
        }
    };
    LineBuffer.prototype.finalize = function () {
        switch (this._count) {
            case 0:
                return '';
            case 1:
                this._count = 0;
                return this._firstPart;
            case 2:
                this._count = 0;
                return this._firstPart + this._secondPart;
            default:
                this._count = 0;
                return this._firstPart + this._secondPart + this._thirdPart;
        }
    };
    return LineBuffer;
}());
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._lines = [];
        this._offsets = [];
        this._buffer = new LineBuffer();
        this._indent = null;
        this._indentLevel = 0;
        this._addOffsets = false;
        this._done = false;
        this._currentPosition = 0;
        this._nameResolver = new DefaultNameResolver();
        this._labelMode = LabelMode.WhenUsed;
        this._reset();
    }
    WasmDisassembler.prototype._reset = function () {
        this._types = [];
        this._funcIndex = 0;
        this._funcTypes = [];
        this._importCount = 0;
        this._globalCount = 0;
        this._memoryCount = 0;
        this._tableCount = 0;
        this._initExpression = [];
        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: true,
        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: true,
        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: true,
        configurable: true
    });
    WasmDisassembler.prototype.appendBuffer = function (s) {
        this._buffer.append(s);
    };
    WasmDisassembler.prototype.newLine = function () {
        if (this.addOffsets)
            this._offsets.push(this._currentPosition);
        this._lines.push(this._buffer.finalize());
    };
    WasmDisassembler.prototype.printFuncType = function (typeIndex) {
        var type = this._types[typeIndex];
        if (type.form !== -32 /* func */)
            throw new Error('NYI other function form');
        if (type.params.length > 0) {
            this.appendBuffer(' (param');
            for (var i = 0; i < type.params.length; i++) {
                this.appendBuffer(' ');
                this.appendBuffer(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(typeToString(type.returns[i]));
            }
            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.useLabel = function (depth) {
        if (!this._backrefLabels) {
            return '' + depth;
        }
        var i = this._backrefLabels.length - depth - 1;
        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(getOperatorName(code));
        switch (code) {
            case 2 /* block */:
            case 3 /* loop */:
            case 4 /* if */:
                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);
                }
                if (operator.blockType !== -64 /* empty_block_type */) {
                    this.appendBuffer(' (result ');
                    this.appendBuffer(typeToString(operator.blockType));
                    this.appendBuffer(')');
                }
                break;
            case 11 /* end */:
                if (this._labelMode === LabelMode.Depth) {
                    break;
                }
                var backrefLabel = this._backrefLabels.pop();
                if (backrefLabel.label) {
                    this.appendBuffer(' ');
                    this.appendBuffer(backrefLabel.label);
                }
                break;
            case 12 /* br */:
            case 13 /* br_if */:
                this.appendBuffer(' ');
                this.appendBuffer(this.useLabel(operator.brDepth));
                break;
            case 14 /* br_table */:
                for (var i = 0; i < operator.brTable.length; i++) {
                    this.appendBuffer(' ');
                    this.appendBuffer(this.useLabel(operator.brTable[i]));
                }
                break;
            case 16 /* call */:
                var funcName = this._nameResolver.getFunctionName(operator.funcIndex, operator.funcIndex < this._importCount, true);
                this.appendBuffer(" " + funcName);
                break;
            case 17 /* call_indirect */:
                var typeName = this._nameResolver.getTypeName(operator.typeIndex, true);
                this.appendBuffer(" " + typeName);
                break;
            case 32 /* get_local */:
            case 33 /* set_local */:
            case 34 /* tee_local */:
                var paramName = this._nameResolver.getVariableName(this._funcIndex, operator.localIndex, true);
                this.appendBuffer(" " + paramName);
                break;
            case 35 /* get_global */:
            case 36 /* set_global */:
                var globalName = this._nameResolver.getGlobalName(operator.globalIndex, true);
                this.appendBuffer(" " + globalName);
                break;
            case 40 /* i32_load */:
            case 41 /* i64_load */:
            case 42 /* f32_load */:
            case 43 /* f64_load */:
            case 44 /* i32_load8_s */:
            case 45 /* i32_load8_u */:
            case 46 /* i32_load16_s */:
            case 47 /* i32_load16_u */:
            case 48 /* i64_load8_s */:
            case 49 /* i64_load8_u */:
            case 50 /* i64_load16_s */:
            case 51 /* i64_load16_u */:
            case 52 /* i64_load32_s */:
            case 53 /* i64_load32_u */:
            case 54 /* i32_store */:
            case 55 /* i64_store */:
            case 56 /* f32_store */:
            case 57 /* f64_store */:
            case 58 /* i32_store8 */:
            case 59 /* i32_store16 */:
            case 60 /* i64_store8 */:
            case 61 /* i64_store16 */:
            case 62 /* i64_store32 */:
            case 65024 /* atomic_wake */:
            case 65025 /* i32_atomic_wait */:
            case 65026 /* i64_atomic_wait */:
            case 65040 /* i32_atomic_load */:
            case 65041 /* i64_atomic_load */:
            case 65042 /* i32_atomic_load8_u */:
            case 65043 /* i32_atomic_load16_u */:
            case 65044 /* i64_atomic_load8_u */:
            case 65045 /* i64_atomic_load16_u */:
            case 65046 /* i64_atomic_load32_u */:
            case 65047 /* i32_atomic_store */:
            case 65048 /* i64_atomic_store */:
            case 65049 /* i32_atomic_store8 */:
            case 65050 /* i32_atomic_store16 */:
            case 65051 /* i64_atomic_store8 */:
            case 65052 /* i64_atomic_store16 */:
            case 65053 /* i64_atomic_store32 */:
            case 65054 /* i32_atomic_rmw_add */:
            case 65055 /* i64_atomic_rmw_add */:
            case 65056 /* i32_atomic_rmw8_u_add */:
            case 65057 /* i32_atomic_rmw16_u_add */:
            case 65058 /* i64_atomic_rmw8_u_add */:
            case 65059 /* i64_atomic_rmw16_u_add */:
            case 65060 /* i64_atomic_rmw32_u_add */:
            case 65061 /* i32_atomic_rmw_sub */:
            case 65062 /* i64_atomic_rmw_sub */:
            case 65063 /* i32_atomic_rmw8_u_sub */:
            case 65064 /* i32_atomic_rmw16_u_sub */:
            case 65065 /* i64_atomic_rmw8_u_sub */:
            case 65066 /* i64_atomic_rmw16_u_sub */:
            case 65067 /* i64_atomic_rmw32_u_sub */:
            case 65068 /* i32_atomic_rmw_and */:
            case 65069 /* i64_atomic_rmw_and */:
            case 65070 /* i32_atomic_rmw8_u_and */:
            case 65071 /* i32_atomic_rmw16_u_and */:
            case 65072 /* i64_atomic_rmw8_u_and */:
            case 65073 /* i64_atomic_rmw16_u_and */:
            case 65074 /* i64_atomic_rmw32_u_and */:
            case 65075 /* i32_atomic_rmw_or */:
            case 65076 /* i64_atomic_rmw_or */:
            case 65077 /* i32_atomic_rmw8_u_or */:
            case 65078 /* i32_atomic_rmw16_u_or */:
            case 65079 /* i64_atomic_rmw8_u_or */:
            case 65080 /* i64_atomic_rmw16_u_or */:
            case 65081 /* i64_atomic_rmw32_u_or */:
            case 65082 /* i32_atomic_rmw_xor */:
            case 65083 /* i64_atomic_rmw_xor */:
            case 65084 /* i32_atomic_rmw8_u_xor */:
            case 65085 /* i32_atomic_rmw16_u_xor */:
            case 65086 /* i64_atomic_rmw8_u_xor */:
            case 65087 /* i64_atomic_rmw16_u_xor */:
            case 65088 /* i64_atomic_rmw32_u_xor */:
            case 65089 /* i32_atomic_rmw_xchg */:
            case 65090 /* i64_atomic_rmw_xchg */:
            case 65091 /* i32_atomic_rmw8_u_xchg */:
            case 65092 /* i32_atomic_rmw16_u_xchg */:
            case 65093 /* i64_atomic_rmw8_u_xchg */:
            case 65094 /* i64_atomic_rmw16_u_xchg */:
            case 65095 /* i64_atomic_rmw32_u_xchg */:
            case 65096 /* i32_atomic_rmw_cmpxchg */:
            case 65097 /* i64_atomic_rmw_cmpxchg */:
            case 65098 /* i32_atomic_rmw8_u_cmpxchg */:
            case 65099 /* i32_atomic_rmw16_u_cmpxchg */:
            case 65100 /* i64_atomic_rmw8_u_cmpxchg */:
            case 65101 /* i64_atomic_rmw16_u_cmpxchg */:
            case 65102 /* i64_atomic_rmw32_u_cmpxchg */:
                var memoryAddress = memoryAddressToString(operator.memoryAddress, operator.code);
                if (memoryAddress !== null) {
                    this.appendBuffer(' ');
                    this.appendBuffer(memoryAddress);
                }
                break;
            case 63 /* current_memory */:
            case 64 /* grow_memory */:
                break;
            case 65 /* i32_const */:
                this.appendBuffer(" " + operator.literal.toString());
                break;
            case 66 /* i64_const */:
                this.appendBuffer(" " + operator.literal.toDouble());
                break;
            case 67 /* f32_const */:
                this.appendBuffer(" " + formatFloat32(operator.literal));
                break;
            case 68 /* f64_const */:
                this.appendBuffer(" " + formatFloat64(operator.literal));
                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;
        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,
            };
        }
        if (linesReady === this._lines.length) {
            var result_1 = {
                lines: this._lines,
                offsets: this._addOffsets ? this._offsets : undefined,
                done: this._done,
            };
            this._lines = [];
            if (this._addOffsets)
                this._offsets = [];
            return result_1;
        }
        var result = {
            lines: this._lines.splice(0, linesReady),
            offsets: this._addOffsets ? this._offsets.splice(0, linesReady) : undefined,
            done: false,
        };
        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 /* END_WASM */:
                    this.appendBuffer(')');
                    this.newLine();
                    this._reset();
                    if (!reader.hasMoreBytes()) {
                        this._done = true;
                        return true;
                    }
                    break;
                case -1 /* ERROR */:
                    throw reader.error;
                case 1 /* BEGIN_WASM */:
                    this.appendBuffer('(module');
                    this.newLine();
                    break;
                case 4 /* END_SECTION */:
                    break;
                case 3 /* BEGIN_SECTION */:
                    var sectionInfo = reader.result;
                    switch (sectionInfo.id) {
                        case 1 /* Type */:
                        case 2 /* Import */:
                        case 7 /* Export */:
                        case 6 /* Global */:
                        case 3 /* Function */:
                        case 8 /* Start */:
                        case 10 /* Code */:
                        case 5 /* Memory */:
                        case 11 /* Data */:
                        case 4 /* Table */:
                        case 9 /* Element */:
                            break; // reading known section;
                        default:
                            reader.skipSection();
                            break;
                    }
                    break;
                case 15 /* MEMORY_SECTION_ENTRY */:
                    var memoryInfo = reader.result;
                    var memoryName = this._nameResolver.getMemoryName(this._memoryCount++, false);
                    this.appendBuffer("  (memory " + memoryName + " ");
                    if (memoryInfo.shared) {
                        this.appendBuffer("(shared " + limitsToString(memoryInfo.limits) + ")");
                    }
                    else {
                        this.appendBuffer(limitsToString(memoryInfo.limits));
                    }
                    this.appendBuffer(')');
                    this.newLine();
                    break;
                case 14 /* TABLE_SECTION_ENTRY */:
                    var tableInfo = reader.result;
                    var tableName = this._nameResolver.getTableName(this._tableCount++, false);
                    this.appendBuffer("  (table " + tableName + " " + limitsToString(tableInfo.limits) + " " + typeToString(tableInfo.elementType) + ")");
                    this.newLine();
                    break;
                case 17 /* EXPORT_SECTION_ENTRY */:
                    var exportInfo = reader.result;
                    this.appendBuffer('  (export ');
                    this.printString(exportInfo.field);
                    this.appendBuffer(' ');
                    switch (exportInfo.kind) {
                        case 0 /* Function */:
                            var funcName = this._nameResolver.getFunctionName(exportInfo.index, exportInfo.index < this._importCount, true);
                            this.appendBuffer("(func " + funcName + ")");
                            break;
                        case 1 /* Table */:
                            var tableName = this._nameResolver.getTableName(exportInfo.index, true);
                            this.appendBuffer("(table " + tableName + ")");
                            break;
                        case 2 /* Memory */:
                            var memoryName = this._nameResolver.getMemoryName(exportInfo.index, true);
                            this.appendBuffer("(memory " + memoryName + ")");
                            break;
                        case 3 /* Global */:
                            var globalName = this._nameResolver.getGlobalName(exportInfo.index, true);
                            this.appendBuffer("(global " + globalName + ")");
                            break;
                        default:
                            throw new Error("Unsupported export " + exportInfo.kind);
                    }
                    this.appendBuffer(')');
                    this.newLine();
                    break;
                case 12 /* IMPORT_SECTION_ENTRY */:
                    var importInfo = reader.result;
                    this.appendBuffer('  (import ');
                    this.printImportSource(importInfo);
                    switch (importInfo.kind) {
                        case 0 /* Function */:
                            this._importCount++;
                            var funcName = this._nameResolver.getFunctionName(this._funcIndex++, true, false);
                            this.appendBuffer(" (func " + funcName);
                            this.printFuncType(importInfo.funcTypeIndex);
                            this.appendBuffer(')');
                            break;
                        case 1 /* Table */:
                            var tableImportInfo = importInfo.type;
                            var tableName = this._nameResolver.getTableName(this._tableCount++, false);
                            this.appendBuffer(" (table " + tableName + " " + limitsToString(tableImportInfo.limits) + " " + typeToString(tableImportInfo.elementType) + ")");
                            break;
                        case 2 /* Memory */:
                            var memoryImportInfo = importInfo.type;
                            var memoryName = this._nameResolver.getMemoryName(this._memoryCount++, false);
                            this.appendBuffer(" (memory " + memoryName + " ");
                            if (memoryImportInfo.shared) {
                                this.appendBuffer("(shared " + limitsToString(memoryImportInfo.limits) + ")");
                            }
                            else {
                                this.appendBuffer(limitsToString(memoryImportInfo.limits));
                            }
                            this.appendBuffer(')');
                            break;
                        case 3 /* Global */:
                            var globalImportInfo = importInfo.type;
                            var globalName = this._nameResolver.getGlobalName(this._globalCount++, false);
                            this.appendBuffer(" (global " + globalName + " " + globalTypeToString(globalImportInfo) + ")");
                            break;
                        default:
                            throw new Error("NYI other import types: " + importInfo.kind);
                    }
                    this.appendBuffer(')');
                    this.newLine();
                    break;
                case 33 /* BEGIN_ELEMENT_SECTION_ENTRY */:
                    var elementSegmentInfo = reader.result;
                    this.appendBuffer('  (elem ');
                    break;
                case 35 /* END_ELEMENT_SECTION_ENTRY */:
                    this.appendBuffer(')');
                    this.newLine();
                    break;
                case 34 /* ELEMENT_SECTION_ENTRY_BODY */:
                    var elementSegmentBody = reader.result;
                    elementSegmentBody.elements.forEach(function (funcIndex) {
                        var funcName = _this._nameResolver.getFunctionName(funcIndex, funcIndex < _this._importCount, true);
                        _this.appendBuffer(" " + funcName);
                    });
                    break;
                case 39 /* BEGIN_GLOBAL_SECTION_ENTRY */:
                    var globalInfo = reader.result;
                    var globalName = this._nameResolver.getGlobalName(this._globalCount++, false);
                    this.appendBuffer("  (global " + globalName + " " + globalTypeToString(globalInfo.type) + " ");
                    break;
                case 40 /* END_GLOBAL_SECTION_ENTRY */:
                    this.appendBuffer(')');
                    this.newLine();
                    break;
                case 11 /* TYPE_SECTION_ENTRY */:
                    var funcType = reader.result;
                    var typeIndex = this._types.length;
                    this._types.push(funcType);
                    var typeName = this._nameResolver.getTypeName(typeIndex, false);
                    this.appendBuffer("  (type " + typeName + " (func");
                    this.printFuncType(typeIndex);
                    this.appendBuffer('))');
                    this.newLine();
                    break;
                case 22 /* START_SECTION_ENTRY */:
                    var startEntry = reader.result;
                    var funcName = this._nameResolver.getFunctionName(startEntry.index, startEntry.index < this._importCount, true);
                    this.appendBuffer("  (start " + funcName + ")");
                    this.newLine();
                    break;
                case 36 /* BEGIN_DATA_SECTION_ENTRY */:
                    this.appendBuffer('  (data ');
                    break;
                case 37 /* DATA_SECTION_ENTRY_BODY */:
                    var body = reader.result;
                    this.newLine();
                    this.appendBuffer('    ');
                    this.printString(body.data);
                    this.newLine();
                    break;
                case 38 /* END_DATA_SECTION_ENTRY */:
                    this.appendBuffer('  )');
                    this.newLine();
                    break;
                case 25 /* BEGIN_INIT_EXPRESSION_BODY */:
                    break;
                case 26 /* INIT_EXPRESSION_OPERATOR */:
                    this._initExpression.push(reader.result);
                    break;
                case 27 /* END_INIT_EXPRESSION_BODY */:
                    this.appendBuffer('(');
                    // TODO fix printing when more that one operator is used.
                    this._initExpression.forEach(function (op, index) {
                        if (op.code === 11 /* end */) {
                            return; // do not print end
                        }
                        if (index > 0) {
                            _this.appendBuffer(' ');
                        }
                        _this.printOperator(op);
                    });
                    this.appendBuffer(')');
                    this._initExpression.length = 0;
                    break;
                case 13 /* FUNCTION_SECTION_ENTRY */:
                    this._funcTypes.push(reader.result.typeIndex);
                    break;
                case 28 /* 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));
                    for (var i = 0; i < type.params.length; i++) {
                        var paramName = this._nameResolver.getVariableName(this._funcIndex, i, false);
                        this.appendBuffer(" (param " + paramName + " " + typeToString(type.params[i]) + ")");
                    }
                    for (var i = 0; i < type.returns.length; i++) {
                        this.appendBuffer(" (result " + typeToString(type.returns[i]) + ")");
                    }
                    this.newLine();
                    var localIndex = type.params.length;
                    if (func.locals.length > 0) {
                        this.appendBuffer('   ');
                        for (var _i = 0, _a = func.locals; _i < _a.length; _i++) {
                            var l = _a[_i];
                            for (var i = 0; i < l.count; i++) {
                                var paramName = this._nameResolver.getVariableName(this._funcIndex, localIndex++, false);
                                this.appendBuffer(" (local " + paramName + " " + typeToString(l.type) + ")");
                            }
                        }
                        this.newLine();
                    }
                    this._indent = '    ';
                    this._indentLevel = 0;
                    this._labelIndex = 0;
                    this._backrefLabels = this._labelMode === LabelMode.Depth ? null : [];
                    break;
                case 30 /* CODE_OPERATOR */:
                    var operator = reader.result;
                    if (operator.code == 11 /* end */ && this._indentLevel == 0) {
                        // reached of the function, skipping the operator
                        break;
                    }
                    switch (operator.code) {
                        case 11 /* end */:
                        case 5 /* else */:
                            this.decreaseIndent();
                            break;
                    }
                    this.appendBuffer(this._indent);
                    this.printOperator(operator);
                    this.newLine();
                    switch (operator.code) {
                        case 4 /* if */:
                        case 2 /* block */:
                        case 3 /* loop */:
                        case 5 /* else */:
                            this.increaseIndent();
                            break;
                    }
                    break;
                case 31 /* END_FUNCTION_BODY */:
                    this._funcIndex++;
                    this._backrefLabels = null;
                    this.appendBuffer("  )");
                    this.newLine();
                    break;
                default:
                    throw new Error("Expectected state: " + reader.state);
            }
        }
    };
    return WasmDisassembler;
}());
exports.WasmDisassembler = WasmDisassembler;
PK
!<̛��CCCchrome/devtools/modules/devtools/client/shared/vendor/WasmParser.js"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/* 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.
 */
// 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 = 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["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["drop"] = 26] = "drop";
    OperatorCode[OperatorCode["select"] = 27] = "select";
    OperatorCode[OperatorCode["get_local"] = 32] = "get_local";
    OperatorCode[OperatorCode["set_local"] = 33] = "set_local";
    OperatorCode[OperatorCode["tee_local"] = 34] = "tee_local";
    OperatorCode[OperatorCode["get_global"] = 35] = "get_global";
    OperatorCode[OperatorCode["set_global"] = 36] = "set_global";
    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["current_memory"] = 63] = "current_memory";
    OperatorCode[OperatorCode["grow_memory"] = 64] = "grow_memory";
    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_s_f32"] = 168] = "i32_trunc_s_f32";
    OperatorCode[OperatorCode["i32_trunc_u_f32"] = 169] = "i32_trunc_u_f32";
    OperatorCode[OperatorCode["i32_trunc_s_f64"] = 170] = "i32_trunc_s_f64";
    OperatorCode[OperatorCode["i32_trunc_u_f64"] = 171] = "i32_trunc_u_f64";
    OperatorCode[OperatorCode["i64_extend_s_i32"] = 172] = "i64_extend_s_i32";
    OperatorCode[OperatorCode["i64_extend_u_i32"] = 173] = "i64_extend_u_i32";
    OperatorCode[OperatorCode["i64_trunc_s_f32"] = 174] = "i64_trunc_s_f32";
    OperatorCode[OperatorCode["i64_trunc_u_f32"] = 175] = "i64_trunc_u_f32";
    OperatorCode[OperatorCode["i64_trunc_s_f64"] = 176] = "i64_trunc_s_f64";
    OperatorCode[OperatorCode["i64_trunc_u_f64"] = 177] = "i64_trunc_u_f64";
    OperatorCode[OperatorCode["f32_convert_s_i32"] = 178] = "f32_convert_s_i32";
    OperatorCode[OperatorCode["f32_convert_u_i32"] = 179] = "f32_convert_u_i32";
    OperatorCode[OperatorCode["f32_convert_s_i64"] = 180] = "f32_convert_s_i64";
    OperatorCode[OperatorCode["f32_convert_u_i64"] = 181] = "f32_convert_u_i64";
    OperatorCode[OperatorCode["f32_demote_f64"] = 182] = "f32_demote_f64";
    OperatorCode[OperatorCode["f64_convert_s_i32"] = 183] = "f64_convert_s_i32";
    OperatorCode[OperatorCode["f64_convert_u_i32"] = 184] = "f64_convert_u_i32";
    OperatorCode[OperatorCode["f64_convert_s_i64"] = 185] = "f64_convert_s_i64";
    OperatorCode[OperatorCode["f64_convert_u_i64"] = 186] = "f64_convert_u_i64";
    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_0xfc"] = 252] = "prefix_0xfc";
    OperatorCode[OperatorCode["prefix_0xfe"] = 254] = "prefix_0xfe";
    OperatorCode[OperatorCode["i32_trunc_s_sat_f32"] = 64512] = "i32_trunc_s_sat_f32";
    OperatorCode[OperatorCode["i32_trunc_u_sat_f32"] = 64513] = "i32_trunc_u_sat_f32";
    OperatorCode[OperatorCode["i32_trunc_s_sat_f64"] = 64514] = "i32_trunc_s_sat_f64";
    OperatorCode[OperatorCode["i32_trunc_u_sat_f64"] = 64515] = "i32_trunc_u_sat_f64";
    OperatorCode[OperatorCode["i64_trunc_s_sat_f32"] = 64516] = "i64_trunc_s_sat_f32";
    OperatorCode[OperatorCode["i64_trunc_u_sat_f32"] = 64517] = "i64_trunc_u_sat_f32";
    OperatorCode[OperatorCode["i64_trunc_s_sat_f64"] = 64518] = "i64_trunc_s_sat_f64";
    OperatorCode[OperatorCode["i64_trunc_u_sat_f64"] = 64519] = "i64_trunc_u_sat_f64";
    OperatorCode[OperatorCode["atomic_wake"] = 65024] = "atomic_wake";
    OperatorCode[OperatorCode["i32_atomic_wait"] = 65025] = "i32_atomic_wait";
    OperatorCode[OperatorCode["i64_atomic_wait"] = 65026] = "i64_atomic_wait";
    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_u_add"] = 65056] = "i32_atomic_rmw8_u_add";
    OperatorCode[OperatorCode["i32_atomic_rmw16_u_add"] = 65057] = "i32_atomic_rmw16_u_add";
    OperatorCode[OperatorCode["i64_atomic_rmw8_u_add"] = 65058] = "i64_atomic_rmw8_u_add";
    OperatorCode[OperatorCode["i64_atomic_rmw16_u_add"] = 65059] = "i64_atomic_rmw16_u_add";
    OperatorCode[OperatorCode["i64_atomic_rmw32_u_add"] = 65060] = "i64_atomic_rmw32_u_add";
    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_u_sub"] = 65063] = "i32_atomic_rmw8_u_sub";
    OperatorCode[OperatorCode["i32_atomic_rmw16_u_sub"] = 65064] = "i32_atomic_rmw16_u_sub";
    OperatorCode[OperatorCode["i64_atomic_rmw8_u_sub"] = 65065] = "i64_atomic_rmw8_u_sub";
    OperatorCode[OperatorCode["i64_atomic_rmw16_u_sub"] = 65066] = "i64_atomic_rmw16_u_sub";
    OperatorCode[OperatorCode["i64_atomic_rmw32_u_sub"] = 65067] = "i64_atomic_rmw32_u_sub";
    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_u_and"] = 65070] = "i32_atomic_rmw8_u_and";
    OperatorCode[OperatorCode["i32_atomic_rmw16_u_and"] = 65071] = "i32_atomic_rmw16_u_and";
    OperatorCode[OperatorCode["i64_atomic_rmw8_u_and"] = 65072] = "i64_atomic_rmw8_u_and";
    OperatorCode[OperatorCode["i64_atomic_rmw16_u_and"] = 65073] = "i64_atomic_rmw16_u_and";
    OperatorCode[OperatorCode["i64_atomic_rmw32_u_and"] = 65074] = "i64_atomic_rmw32_u_and";
    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_u_or"] = 65077] = "i32_atomic_rmw8_u_or";
    OperatorCode[OperatorCode["i32_atomic_rmw16_u_or"] = 65078] = "i32_atomic_rmw16_u_or";
    OperatorCode[OperatorCode["i64_atomic_rmw8_u_or"] = 65079] = "i64_atomic_rmw8_u_or";
    OperatorCode[OperatorCode["i64_atomic_rmw16_u_or"] = 65080] = "i64_atomic_rmw16_u_or";
    OperatorCode[OperatorCode["i64_atomic_rmw32_u_or"] = 65081] = "i64_atomic_rmw32_u_or";
    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_u_xor"] = 65084] = "i32_atomic_rmw8_u_xor";
    OperatorCode[OperatorCode["i32_atomic_rmw16_u_xor"] = 65085] = "i32_atomic_rmw16_u_xor";
    OperatorCode[OperatorCode["i64_atomic_rmw8_u_xor"] = 65086] = "i64_atomic_rmw8_u_xor";
    OperatorCode[OperatorCode["i64_atomic_rmw16_u_xor"] = 65087] = "i64_atomic_rmw16_u_xor";
    OperatorCode[OperatorCode["i64_atomic_rmw32_u_xor"] = 65088] = "i64_atomic_rmw32_u_xor";
    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_u_xchg"] = 65091] = "i32_atomic_rmw8_u_xchg";
    OperatorCode[OperatorCode["i32_atomic_rmw16_u_xchg"] = 65092] = "i32_atomic_rmw16_u_xchg";
    OperatorCode[OperatorCode["i64_atomic_rmw8_u_xchg"] = 65093] = "i64_atomic_rmw8_u_xchg";
    OperatorCode[OperatorCode["i64_atomic_rmw16_u_xchg"] = 65094] = "i64_atomic_rmw16_u_xchg";
    OperatorCode[OperatorCode["i64_atomic_rmw32_u_xchg"] = 65095] = "i64_atomic_rmw32_u_xchg";
    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_u_cmpxchg"] = 65098] = "i32_atomic_rmw8_u_cmpxchg";
    OperatorCode[OperatorCode["i32_atomic_rmw16_u_cmpxchg"] = 65099] = "i32_atomic_rmw16_u_cmpxchg";
    OperatorCode[OperatorCode["i64_atomic_rmw8_u_cmpxchg"] = 65100] = "i64_atomic_rmw8_u_cmpxchg";
    OperatorCode[OperatorCode["i64_atomic_rmw16_u_cmpxchg"] = 65101] = "i64_atomic_rmw16_u_cmpxchg";
    OperatorCode[OperatorCode["i64_atomic_rmw32_u_cmpxchg"] = 65102] = "i64_atomic_rmw32_u_cmpxchg";
})(OperatorCode = exports.OperatorCode || (exports.OperatorCode = {}));
;
exports.OperatorCodeNames = [
    "unreachable", "nop", "block", "loop", "if", "else", undefined, undefined, undefined, undefined, undefined, "end", "br", "br_if", "br_table", "return", "call", "call_indirect", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, "drop", "select", undefined, undefined, undefined, undefined, "get_local", "set_local", "tee_local", "get_global", "set_global", undefined, undefined, 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", "current_memory", "grow_memory", "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_s/f32", "i32.trunc_u/f32", "i32.trunc_s/f64", "i32.trunc_u/f64", "i64.extend_s/i32", "i64.extend_u/i32", "i64.trunc_s/f32", "i64.trunc_u/f32", "i64.trunc_s/f64", "i64.trunc_u/f64", "f32.convert_s/i32", "f32.convert_u/i32", "f32.convert_s/i64", "f32.convert_u/i64", "f32.demote/f64", "f64.convert_s/i32", "f64.convert_u/i32", "f64.convert_s/i64", "f64.convert_u/i64", "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, 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, undefined, undefined, undefined, undefined, undefined, undefined, undefined
];
["i32.trunc_s:sat/f32", "i32.trunc_u:sat/f32", "i32.trunc_s:sat/f64", "i32.trunc_u:sat/f64", "i64.trunc_s:sat/f32", "i64.trunc_u:sat/f32", "i64.trunc_s:sat/f64", "i64.trunc_u:sat/f64"].forEach(function (s, i) {
    exports.OperatorCodeNames[0xfc00 | i] = s;
});
["atomic.wake", "i32.atomic.wait", "i64.atomic.wait", undefined, 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_u.add", "i32.atomic.rmw16_u.add", "i64.atomic.rmw8_u.add", "i64.atomic.rmw16_u.add", "i64.atomic.rmw32_u.add", "i32.atomic.rmw.sub", "i64.atomic.rmw.sub", "i32.atomic.rmw8_u.sub", "i32.atomic.rmw16_u.sub", "i64.atomic.rmw8_u.sub", "i64.atomic.rmw16_u.sub", "i64.atomic.rmw32_u.sub", "i32.atomic.rmw.and", "i64.atomic.rmw.and", "i32.atomic.rmw8_u.and", "i32.atomic.rmw16_u.and", "i64.atomic.rmw8_u.and", "i64.atomic.rmw16_u.and", "i64.atomic.rmw32_u.and", "i32.atomic.rmw.or", "i64.atomic.rmw.or", "i32.atomic.rmw8_u.or", "i32.atomic.rmw16_u.or", "i64.atomic.rmw8_u.or", "i64.atomic.rmw16_u.or", "i64.atomic.rmw32_u.or", "i32.atomic.rmw.xor", "i64.atomic.rmw.xor", "i32.atomic.rmw8_u.xor", "i32.atomic.rmw16_u.xor", "i64.atomic.rmw8_u.xor", "i64.atomic.rmw16_u.xor", "i64.atomic.rmw32_u.xor", "i32.atomic.rmw.xchg", "i64.atomic.rmw.xchg", "i32.atomic.rmw8_u.xchg", "i32.atomic.rmw16_u.xchg", "i64.atomic.rmw8_u.xchg", "i64.atomic.rmw16_u.xchg", "i64.atomic.rmw32_u.xchg", "i32.atomic.rmw.cmpxchg", "i64.atomic.rmw.cmpxchg", "i32.atomic.rmw8_u.cmpxchg", "i32.atomic.rmw16_u.cmpxchg", "i64.atomic.rmw8_u.cmpxchg", "i64.atomic.rmw16_u.cmpxchg", "i64.atomic.rmw32_u.cmpxchg"].forEach(function (s, i) {
    exports.OperatorCodeNames[0xfe00 | 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 = exports.ExternalKind || (exports.ExternalKind = {}));
var Type;
(function (Type) {
    Type[Type["i32"] = -1] = "i32";
    Type[Type["i64"] = -2] = "i64";
    Type[Type["f32"] = -3] = "f32";
    Type[Type["f64"] = -4] = "f64";
    Type[Type["anyfunc"] = -16] = "anyfunc";
    Type[Type["func"] = -32] = "func";
    Type[Type["empty_block_type"] = -64] = "empty_block_type";
})(Type = exports.Type || (exports.Type = {}));
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 = 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["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 = exports.BinaryReaderState || (exports.BinaryReaderState = {}));
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 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;
    };
    Object.defineProperty(Int64.prototype, "data", {
        get: function () {
            return this._data;
        },
        enumerable: true,
        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 /* INITIAL */;
        this.result = null;
        this.error = null;
        this._sectionEntriesLeft = 0;
        this._sectionId = -1 /* Unknown */;
        this._sectionRange = null;
        this._functionRange = null;
    }
    Object.defineProperty(BinaryReader.prototype, "currentSection", {
        get: function () {
            return this.result; // TODO remove currentSection()
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(BinaryReader.prototype, "currentFunction", {
        get: function () {
            return this.result; // TODO remove currentFunction()
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(BinaryReader.prototype, "data", {
        get: function () {
            return this._data;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(BinaryReader.prototype, "position", {
        get: function () {
            return this._pos;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(BinaryReader.prototype, "length", {
        get: function () {
            return this._length;
        },
        enumerable: true,
        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.readUint16 = function () {
        var b1 = this._data[this._pos++];
        var b2 = this._data[this._pos++];
        return b1 | (b2 << 8);
    };
    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.peekUint32 = function () {
        return this.peekInt32();
    };
    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;
    };
    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);
    };
    BinaryReader.prototype.readStringBytes = function () {
        var length = this.readVarUint32() >>> 0;
        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.hasStringBytes = function () {
        if (!this.hasVarIntBytes())
            return false;
        var pos = this._pos;
        var length = this.readVarUint32() >>> 0;
        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 form = this.readVarInt7();
        var paramCount = this.readVarUint32() >>> 0;
        var paramTypes = new Int8Array(paramCount);
        for (var i = 0; i < paramCount; i++)
            paramTypes[i] = this.readVarInt7();
        var returnCount = this.readVarUint1();
        var returnTypes = new Int8Array(returnCount);
        for (var i = 0; i < returnCount; i++)
            returnTypes[i] = this.readVarInt7();
        return {
            form: form,
            params: paramTypes,
            returns: returnTypes
        };
    };
    BinaryReader.prototype.readResizableLimits = function (maxPresent) {
        var initial = this.readVarUint32() >>> 0;
        var maximum;
        if (maxPresent) {
            maximum = this.readVarUint32() >>> 0;
        }
        return { initial: initial, maximum: maximum };
    };
    BinaryReader.prototype.readTableType = function () {
        var elementType = this.readVarInt7();
        var flags = this.readVarUint32() >>> 0;
        var limits = this.readResizableLimits(!!(flags & 0x01));
        return { elementType: elementType, limits: limits };
    };
    BinaryReader.prototype.readMemoryType = function () {
        var flags = this.readVarUint32() >>> 0;
        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.readVarInt7();
        if (!this.hasVarIntBytes()) {
            this._pos = pos;
            return null;
        }
        var mutability = this.readVarUint1();
        return { contentType: contentType, mutability: mutability };
    };
    BinaryReader.prototype.readTypeEntry = function () {
        if (this._sectionEntriesLeft === 0) {
            this.skipSection();
            return this.read();
        }
        this.state = 11 /* TYPE_SECTION_ENTRY */;
        this.result = this.readFuncType();
        this._sectionEntriesLeft--;
        return true;
    };
    BinaryReader.prototype.readImportEntry = function () {
        if (this._sectionEntriesLeft === 0) {
            this.skipSection();
            return this.read();
        }
        this.state = 12 /* IMPORT_SECTION_ENTRY */;
        var module = this.readStringBytes();
        var field = this.readStringBytes();
        var kind = this.readUint8();
        var funcTypeIndex;
        var type;
        switch (kind) {
            case 0 /* Function */:
                funcTypeIndex = this.readVarUint32() >>> 0;
                break;
            case 1 /* Table */:
                type = this.readTableType();
                break;
            case 2 /* Memory */:
                type = this.readMemoryType();
                break;
            case 3 /* Global */:
                type = this.readGlobalType();
                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() >>> 0;
        this.state = 17 /* 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() >>> 0;
        this.state = 13 /* 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 /* 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 /* MEMORY_SECTION_ENTRY */;
        this.result = this.readMemoryType();
        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 /* GLOBAL_SECTION_ENTRY */;
            return false;
        }
        this.state = 39 /* 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();
        }
        if (!this.hasVarIntBytes()) {
            this.state = 20 /* ELEMENT_SECTION_ENTRY */;
            return false;
        }
        var tableIndex = this.readVarUint32();
        this.state = 33 /* BEGIN_ELEMENT_SECTION_ENTRY */;
        this.result = { index: tableIndex };
        this._sectionEntriesLeft--;
        return true;
    };
    BinaryReader.prototype.readElementEntryBody = function () {
        if (!this.hasVarIntBytes())
            return false;
        var pos = this._pos;
        var numElemements = this.readVarUint32();
        if (!this.hasBytes(numElemements)) {
            // Shall have at least the numElemements amount of bytes.
            this._pos = pos;
            return false;
        }
        var elements = new Uint32Array(numElemements);
        for (var i = 0; i < numElemements; i++) {
            if (!this.hasVarIntBytes()) {
                this._pos = pos;
                return false;
            }
            elements[i] = this.readVarUint32();
        }
        this.state = 34 /* ELEMENT_SECTION_ENTRY_BODY */;
        this.result = { elements: elements };
        return true;
    };
    BinaryReader.prototype.readDataEntry = function () {
        if (this._sectionEntriesLeft === 0) {
            this.skipSection();
            return this.read();
        }
        if (!this.hasVarIntBytes()) {
            return false;
        }
        this.state = 36 /* BEGIN_DATA_SECTION_ENTRY */;
        this.result = {
            index: this.readVarUint32()
        };
        this._sectionEntriesLeft--;
        return true;
    };
    BinaryReader.prototype.readDataEntryBody = function () {
        if (!this.hasStringBytes()) {
            return false;
        }
        this.state = 37 /* DATA_SECTION_ENTRY_BODY */;
        this.result = {
            data: this.readStringBytes()
        };
        return true;
    };
    BinaryReader.prototype.readInitExpressionBody = function () {
        this.state = 25 /* BEGIN_INIT_EXPRESSION_BODY */;
        this.result = null;
        return true;
    };
    BinaryReader.prototype.readMemoryImmediate = function () {
        var flags = this.readVarUint32() >>> 0;
        var offset = this.readVarUint32() >>> 0;
        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 /* Module */:
                result = {
                    type: type,
                    moduleName: this.readStringBytes()
                };
                break;
            case 1 /* Function */:
                result = {
                    type: type,
                    names: this.readNameMap()
                };
                break;
            case 2 /* 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;
            default:
                this.error = new Error("Bad name entry type: " + type);
                this.state = -1 /* ERROR */;
                return true;
        }
        this.state = 19 /* 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 /* Custom */) {
            if (!this.hasStringBytes()) {
                this._pos = pos;
                return false;
            }
            sectionName = this.readStringBytes();
        }
        this.state = 41 /* 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() >>> 0;
        var index;
        switch (type) {
            case 1 /* StackPointer */:
                if (!this.hasVarIntBytes()) {
                    this._pos = pos;
                    return false;
                }
                index = this.readVarUint32();
                break;
            default:
                this.error = new Error("Bad linking type: " + type);
                this.state = -1 /* ERROR */;
                return true;
        }
        this.state = 21 /* 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 /* 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 /* FunctionIndex_LEB */:
            case 1 /* TableIndex_SLEB */:
            case 2 /* TableIndex_I32 */:
            case 6 /* TypeIndex_LEB */:
            case 7 /* GlobalIndex_LEB */:
                break;
            case 3 /* GlobalAddr_LEB */:
            case 4 /* GlobalAddr_SLEB */:
            case 5 /* GlobalAddr_I32 */:
                if (!this.hasVarIntBytes()) {
                    this._pos = pos;
                    return false;
                }
                addend = this.readVarUint32();
                break;
            default:
                this.error = new Error("Bad relocation type: " + type);
                this.state = -1 /* ERROR */;
                return true;
        }
        this.state = 42 /* RELOC_SECTION_ENTRY */;
        this.result = {
            type: type,
            offset: offset,
            index: index,
            addend: addend
        };
        this._sectionEntriesLeft--;
        return true;
    };
    BinaryReader.prototype.readCodeOperator_0xfc = function () {
        var code = this._data[this._pos++] | 0xfc00;
        switch (code) {
            case 64512 /* i32_trunc_s_sat_f32 */:
            case 64513 /* i32_trunc_u_sat_f32 */:
            case 64514 /* i32_trunc_s_sat_f64 */:
            case 64515 /* i32_trunc_u_sat_f64 */:
            case 64516 /* i64_trunc_s_sat_f32 */:
            case 64517 /* i64_trunc_u_sat_f32 */:
            case 64518 /* i64_trunc_s_sat_f64 */:
            case 64519 /* i64_trunc_u_sat_f64 */:
                break;
            default:
                this.error = new Error("Unknown operator: " + code);
                this.state = -1 /* ERROR */;
                return true;
        }
        this.result = { code: code,
            blockType: undefined, brDepth: undefined, brTable: undefined,
            funcIndex: undefined, typeIndex: undefined, localIndex: undefined,
            globalIndex: undefined, memoryAddress: undefined, literal: undefined };
        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;
        }
        var code = this._data[this._pos++] | 0xfe00;
        var memoryAddress;
        switch (code) {
            case 65024 /* atomic_wake */:
            case 65025 /* i32_atomic_wait */:
            case 65026 /* i64_atomic_wait */:
            case 65040 /* i32_atomic_load */:
            case 65041 /* i64_atomic_load */:
            case 65042 /* i32_atomic_load8_u */:
            case 65043 /* i32_atomic_load16_u */:
            case 65044 /* i64_atomic_load8_u */:
            case 65045 /* i64_atomic_load16_u */:
            case 65046 /* i64_atomic_load32_u */:
            case 65047 /* i32_atomic_store */:
            case 65048 /* i64_atomic_store */:
            case 65049 /* i32_atomic_store8 */:
            case 65050 /* i32_atomic_store16 */:
            case 65051 /* i64_atomic_store8 */:
            case 65052 /* i64_atomic_store16 */:
            case 65053 /* i64_atomic_store32 */:
            case 65054 /* i32_atomic_rmw_add */:
            case 65055 /* i64_atomic_rmw_add */:
            case 65056 /* i32_atomic_rmw8_u_add */:
            case 65057 /* i32_atomic_rmw16_u_add */:
            case 65058 /* i64_atomic_rmw8_u_add */:
            case 65059 /* i64_atomic_rmw16_u_add */:
            case 65060 /* i64_atomic_rmw32_u_add */:
            case 65061 /* i32_atomic_rmw_sub */:
            case 65062 /* i64_atomic_rmw_sub */:
            case 65063 /* i32_atomic_rmw8_u_sub */:
            case 65064 /* i32_atomic_rmw16_u_sub */:
            case 65065 /* i64_atomic_rmw8_u_sub */:
            case 65066 /* i64_atomic_rmw16_u_sub */:
            case 65067 /* i64_atomic_rmw32_u_sub */:
            case 65068 /* i32_atomic_rmw_and */:
            case 65069 /* i64_atomic_rmw_and */:
            case 65070 /* i32_atomic_rmw8_u_and */:
            case 65071 /* i32_atomic_rmw16_u_and */:
            case 65072 /* i64_atomic_rmw8_u_and */:
            case 65073 /* i64_atomic_rmw16_u_and */:
            case 65074 /* i64_atomic_rmw32_u_and */:
            case 65075 /* i32_atomic_rmw_or */:
            case 65076 /* i64_atomic_rmw_or */:
            case 65077 /* i32_atomic_rmw8_u_or */:
            case 65078 /* i32_atomic_rmw16_u_or */:
            case 65079 /* i64_atomic_rmw8_u_or */:
            case 65080 /* i64_atomic_rmw16_u_or */:
            case 65081 /* i64_atomic_rmw32_u_or */:
            case 65082 /* i32_atomic_rmw_xor */:
            case 65083 /* i64_atomic_rmw_xor */:
            case 65084 /* i32_atomic_rmw8_u_xor */:
            case 65085 /* i32_atomic_rmw16_u_xor */:
            case 65086 /* i64_atomic_rmw8_u_xor */:
            case 65087 /* i64_atomic_rmw16_u_xor */:
            case 65088 /* i64_atomic_rmw32_u_xor */:
            case 65089 /* i32_atomic_rmw_xchg */:
            case 65090 /* i64_atomic_rmw_xchg */:
            case 65091 /* i32_atomic_rmw8_u_xchg */:
            case 65092 /* i32_atomic_rmw16_u_xchg */:
            case 65093 /* i64_atomic_rmw8_u_xchg */:
            case 65094 /* i64_atomic_rmw16_u_xchg */:
            case 65095 /* i64_atomic_rmw32_u_xchg */:
            case 65096 /* i32_atomic_rmw_cmpxchg */:
            case 65097 /* i64_atomic_rmw_cmpxchg */:
            case 65098 /* i32_atomic_rmw8_u_cmpxchg */:
            case 65099 /* i32_atomic_rmw16_u_cmpxchg */:
            case 65100 /* i64_atomic_rmw8_u_cmpxchg */:
            case 65101 /* i64_atomic_rmw16_u_cmpxchg */:
            case 65102 /* i64_atomic_rmw32_u_cmpxchg */:
                memoryAddress = this.readMemoryImmediate();
                break;
            default:
                this.error = new Error("Unknown operator: " + code);
                this.state = -1 /* ERROR */;
                return true;
        }
        this.result = { code: code,
            blockType: undefined, brDepth: undefined, brTable: undefined,
            funcIndex: undefined, typeIndex: undefined, localIndex: undefined,
            globalIndex: undefined, memoryAddress: memoryAddress, literal: undefined };
        return true;
    };
    BinaryReader.prototype.readCodeOperator = function () {
        if (this.state === 30 /* CODE_OPERATOR */ &&
            this._pos >= this._functionRange.end) {
            this.skipFunctionBody();
            return this.read();
        }
        else if (this.state === 26 /* INIT_EXPRESSION_OPERATOR */ &&
            this.result &&
            this.result.code === 11 /* end */) {
            this.state = 27 /* END_INIT_EXPRESSION_BODY */;
            this.result = null;
            return true;
        }
        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;
        }
        var code = this._data[this._pos++];
        var blockType, brDepth, brTable, funcIndex, typeIndex, localIndex, globalIndex, memoryAddress, literal, reserved;
        switch (code) {
            case 2 /* block */:
            case 3 /* loop */:
            case 4 /* if */:
                blockType = this.readVarInt7();
                break;
            case 12 /* br */:
            case 13 /* br_if */:
                brDepth = this.readVarUint32() >>> 0;
                break;
            case 14 /* br_table */:
                var tableCount = this.readVarUint32() >>> 0;
                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++) {
                    if (!this.hasVarIntBytes()) {
                        this._pos = pos;
                        return false;
                    }
                    brTable.push(this.readVarUint32() >>> 0);
                }
                break;
            case 16 /* call */:
                funcIndex = this.readVarUint32() >>> 0;
                break;
            case 17 /* call_indirect */:
                typeIndex = this.readVarUint32() >>> 0;
                reserved = this.readVarUint1();
                break;
            case 32 /* get_local */:
            case 33 /* set_local */:
            case 34 /* tee_local */:
                localIndex = this.readVarUint32() >>> 0;
                break;
            case 35 /* get_global */:
            case 36 /* set_global */:
                globalIndex = this.readVarUint32() >>> 0;
                break;
            case 40 /* i32_load */:
            case 41 /* i64_load */:
            case 42 /* f32_load */:
            case 43 /* f64_load */:
            case 44 /* i32_load8_s */:
            case 45 /* i32_load8_u */:
            case 46 /* i32_load16_s */:
            case 47 /* i32_load16_u */:
            case 48 /* i64_load8_s */:
            case 49 /* i64_load8_u */:
            case 50 /* i64_load16_s */:
            case 51 /* i64_load16_u */:
            case 52 /* i64_load32_s */:
            case 53 /* i64_load32_u */:
            case 54 /* i32_store */:
            case 55 /* i64_store */:
            case 56 /* f32_store */:
            case 57 /* f64_store */:
            case 58 /* i32_store8 */:
            case 59 /* i32_store16 */:
            case 60 /* i64_store8 */:
            case 61 /* i64_store16 */:
            case 62 /* i64_store32 */:
                memoryAddress = this.readMemoryImmediate();
                break;
            case 63 /* current_memory */:
            case 64 /* grow_memory */:
                reserved = this.readVarUint1();
                break;
            case 65 /* i32_const */:
                literal = this.readVarInt32();
                break;
            case 66 /* i64_const */:
                literal = this.readVarInt64();
                break;
            case 67 /* f32_const */:
                literal = new DataView(this._data.buffer, this._data.byteOffset).getFloat32(this._pos, true);
                this._pos += 4;
                break;
            case 68 /* f64_const */:
                literal = new DataView(this._data.buffer, this._data.byteOffset).getFloat64(this._pos, true);
                this._pos += 8;
                break;
            case 252 /* prefix_0xfc */:
                if (this.readCodeOperator_0xfc()) {
                    return true;
                }
                this._pos = pos;
                return false;
            case 254 /* prefix_0xfe */:
                if (this.readCodeOperator_0xfe()) {
                    return true;
                }
                this._pos = pos;
                return false;
            case 0 /* unreachable */:
            case 1 /* nop */:
            case 5 /* else */:
            case 11 /* end */:
            case 15 /* return */:
            case 26 /* drop */:
            case 27 /* select */:
            case 69 /* i32_eqz */:
            case 70 /* i32_eq */:
            case 71 /* i32_ne */:
            case 72 /* i32_lt_s */:
            case 73 /* i32_lt_u */:
            case 74 /* i32_gt_s */:
            case 75 /* i32_gt_u */:
            case 76 /* i32_le_s */:
            case 77 /* i32_le_u */:
            case 78 /* i32_ge_s */:
            case 79 /* i32_ge_u */:
            case 80 /* i64_eqz */:
            case 81 /* i64_eq */:
            case 82 /* i64_ne */:
            case 83 /* i64_lt_s */:
            case 84 /* i64_lt_u */:
            case 85 /* i64_gt_s */:
            case 86 /* i64_gt_u */:
            case 87 /* i64_le_s */:
            case 88 /* i64_le_u */:
            case 89 /* i64_ge_s */:
            case 90 /* i64_ge_u */:
            case 91 /* f32_eq */:
            case 92 /* f32_ne */:
            case 93 /* f32_lt */:
            case 94 /* f32_gt */:
            case 95 /* f32_le */:
            case 96 /* f32_ge */:
            case 97 /* f64_eq */:
            case 98 /* f64_ne */:
            case 99 /* f64_lt */:
            case 100 /* f64_gt */:
            case 101 /* f64_le */:
            case 102 /* f64_ge */:
            case 103 /* i32_clz */:
            case 104 /* i32_ctz */:
            case 105 /* i32_popcnt */:
            case 106 /* i32_add */:
            case 107 /* i32_sub */:
            case 108 /* i32_mul */:
            case 109 /* i32_div_s */:
            case 110 /* i32_div_u */:
            case 111 /* i32_rem_s */:
            case 112 /* i32_rem_u */:
            case 113 /* i32_and */:
            case 114 /* i32_or */:
            case 115 /* i32_xor */:
            case 116 /* i32_shl */:
            case 117 /* i32_shr_s */:
            case 118 /* i32_shr_u */:
            case 119 /* i32_rotl */:
            case 120 /* i32_rotr */:
            case 121 /* i64_clz */:
            case 122 /* i64_ctz */:
            case 123 /* i64_popcnt */:
            case 124 /* i64_add */:
            case 125 /* i64_sub */:
            case 126 /* i64_mul */:
            case 127 /* i64_div_s */:
            case 128 /* i64_div_u */:
            case 129 /* i64_rem_s */:
            case 130 /* i64_rem_u */:
            case 131 /* i64_and */:
            case 132 /* i64_or */:
            case 133 /* i64_xor */:
            case 134 /* i64_shl */:
            case 135 /* i64_shr_s */:
            case 136 /* i64_shr_u */:
            case 137 /* i64_rotl */:
            case 138 /* i64_rotr */:
            case 139 /* f32_abs */:
            case 140 /* f32_neg */:
            case 141 /* f32_ceil */:
            case 142 /* f32_floor */:
            case 143 /* f32_trunc */:
            case 144 /* f32_nearest */:
            case 145 /* f32_sqrt */:
            case 146 /* f32_add */:
            case 147 /* f32_sub */:
            case 148 /* f32_mul */:
            case 149 /* f32_div */:
            case 150 /* f32_min */:
            case 151 /* f32_max */:
            case 152 /* f32_copysign */:
            case 153 /* f64_abs */:
            case 154 /* f64_neg */:
            case 155 /* f64_ceil */:
            case 156 /* f64_floor */:
            case 157 /* f64_trunc */:
            case 158 /* f64_nearest */:
            case 159 /* f64_sqrt */:
            case 160 /* f64_add */:
            case 161 /* f64_sub */:
            case 162 /* f64_mul */:
            case 163 /* f64_div */:
            case 164 /* f64_min */:
            case 165 /* f64_max */:
            case 166 /* f64_copysign */:
            case 167 /* i32_wrap_i64 */:
            case 168 /* i32_trunc_s_f32 */:
            case 169 /* i32_trunc_u_f32 */:
            case 170 /* i32_trunc_s_f64 */:
            case 171 /* i32_trunc_u_f64 */:
            case 172 /* i64_extend_s_i32 */:
            case 173 /* i64_extend_u_i32 */:
            case 174 /* i64_trunc_s_f32 */:
            case 175 /* i64_trunc_u_f32 */:
            case 176 /* i64_trunc_s_f64 */:
            case 177 /* i64_trunc_u_f64 */:
            case 178 /* f32_convert_s_i32 */:
            case 179 /* f32_convert_u_i32 */:
            case 180 /* f32_convert_s_i64 */:
            case 181 /* f32_convert_u_i64 */:
            case 182 /* f32_demote_f64 */:
            case 183 /* f64_convert_s_i32 */:
            case 184 /* f64_convert_u_i32 */:
            case 185 /* f64_convert_s_i64 */:
            case 186 /* f64_convert_u_i64 */:
            case 187 /* f64_promote_f32 */:
            case 188 /* i32_reinterpret_f32 */:
            case 189 /* i64_reinterpret_f64 */:
            case 190 /* f32_reinterpret_i32 */:
            case 191 /* f64_reinterpret_i64 */:
            case 192 /* i32_extend8_s */:
            case 193 /* i32_extend16_s */:
            case 194 /* i64_extend8_s */:
            case 195 /* i64_extend16_s */:
            case 196 /* i64_extend32_s */:
                break;
            default:
                this.error = new Error("Unknown operator: " + code);
                this.state = -1 /* ERROR */;
                return true;
        }
        this.result = { code: code,
            blockType: blockType, brDepth: brDepth, brTable: brTable,
            funcIndex: funcIndex, typeIndex: typeIndex, localIndex: localIndex,
            globalIndex: globalIndex, memoryAddress: memoryAddress, literal: literal };
        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() >>> 0;
        var bodyEnd = this._pos + size;
        if (!this.hasVarIntBytes()) {
            this._pos = pos;
            return false;
        }
        var localCount = this.readVarUint32() >>> 0;
        var locals = [];
        for (var i = 0; i < localCount; i++) {
            if (!this.hasVarIntBytes()) {
                this._pos = pos;
                return false;
            }
            var count = this.readVarUint32() >>> 0;
            if (!this.hasVarIntBytes()) {
                this._pos = pos;
                return false;
            }
            var type = this.readVarInt7();
            locals.push({ count: count, type: type });
        }
        var bodyStart = this._pos;
        this.state = 28 /* 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 /* Unknown */;
            this._sectionRange = null;
            this.result = null;
            this.state = 2 /* 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 /* Unknown */;
                this._sectionRange = null;
                this.result = null;
                this.state = 2 /* 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() >>> 0;
        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 /* 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 /* 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 /* END_SECTION */;
            this._sectionId = -1 /* Unknown */;
            this._sectionRange = null;
            return true;
        }
        var currentSection = this.result;
        switch (currentSection.id) {
            case 1 /* Type */:
                if (!this.hasSectionPayload())
                    return false;
                this._sectionEntriesLeft = this.readVarUint32() >>> 0;
                return this.readTypeEntry();
            case 2 /* Import */:
                if (!this.hasSectionPayload())
                    return false;
                this._sectionEntriesLeft = this.readVarUint32() >>> 0;
                return this.readImportEntry();
            case 7 /* Export */:
                if (!this.hasSectionPayload())
                    return false;
                this._sectionEntriesLeft = this.readVarUint32() >>> 0;
                return this.readExportEntry();
            case 3 /* Function */:
                if (!this.hasSectionPayload())
                    return false;
                this._sectionEntriesLeft = this.readVarUint32() >>> 0;
                return this.readFunctionEntry();
            case 4 /* Table */:
                if (!this.hasSectionPayload())
                    return false;
                this._sectionEntriesLeft = this.readVarUint32() >>> 0;
                return this.readTableEntry();
            case 5 /* Memory */:
                if (!this.hasSectionPayload())
                    return false;
                this._sectionEntriesLeft = this.readVarUint32() >>> 0;
                return this.readMemoryEntry();
            case 6 /* Global */:
                if (!this.hasVarIntBytes())
                    return false;
                this._sectionEntriesLeft = this.readVarUint32() >>> 0;
                return this.readGlobalEntry();
            case 8 /* Start */:
                if (!this.hasVarIntBytes())
                    return false;
                this.state = 22 /* START_SECTION_ENTRY */;
                this.result = { index: this.readVarUint32() };
                return true;
            case 10 /* Code */:
                if (!this.hasVarIntBytes())
                    return false;
                this._sectionEntriesLeft = this.readVarUint32() >>> 0;
                this.state = 29 /* READING_FUNCTION_HEADER */;
                return this.readFunctionBody();
            case 9 /* Element */:
                if (!this.hasVarIntBytes())
                    return false;
                this._sectionEntriesLeft = this.readVarUint32() >>> 0;
                return this.readElementEntry();
            case 11 /* Data */:
                if (!this.hasVarIntBytes())
                    return false;
                this._sectionEntriesLeft = this.readVarUint32() >>> 0;
                this.state = 18 /* DATA_SECTION_ENTRY */;
                return this.readDataEntry();
            case 0 /* Custom */:
                var customSectionName = 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() >>> 0;
                    return this.readLinkingEntry();
                }
                if (customSectionName === 'sourceMappingURL') {
                    return this.readSourceMappingURL();
                }
                return this.readSectionRawData();
            default:
                this.error = new Error("Unsupported section: " + this._sectionId);
                this.state = -1 /* ERROR */;
                return true;
        }
    };
    BinaryReader.prototype.read = function () {
        switch (this.state) {
            case 0 /* 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 /* ERROR */;
                    return true;
                }
                var version = this.readUint32();
                if (version != WASM_SUPPORTED_VERSION &&
                    version != WASM_SUPPORTED_EXPERIMENTAL_VERSION) {
                    this.error = new Error("Bad version number " + version);
                    this.state = -1 /* ERROR */;
                    return true;
                }
                this.result = { magicNumber: magicNumber, version: version };
                this.state = 1 /* BEGIN_WASM */;
                return true;
            case 2 /* END_WASM */:
                this.result = null;
                this.state = 1 /* BEGIN_WASM */;
                if (this.hasMoreBytes()) {
                    this.state = 0 /* INITIAL */;
                    return this.read();
                }
                return false;
            case -1 /* ERROR */:
                return true;
            case 1 /* BEGIN_WASM */:
            case 4 /* END_SECTION */:
                return this.readSectionHeader();
            case 3 /* BEGIN_SECTION */:
                return this.readSectionBody();
            case 5 /* SKIPPING_SECTION */:
                if (!this.hasSectionPayload()) {
                    return false;
                }
                this.state = 4 /* END_SECTION */;
                this._pos = this._sectionRange.end;
                this._sectionId = -1 /* Unknown */;
                this._sectionRange = null;
                this.result = null;
                return true;
            case 32 /* SKIPPING_FUNCTION_BODY */:
                this.state = 31 /* END_FUNCTION_BODY */;
                this._pos = this._functionRange.end;
                this._functionRange = null;
                this.result = null;
                return true;
            case 11 /* TYPE_SECTION_ENTRY */:
                return this.readTypeEntry();
            case 12 /* IMPORT_SECTION_ENTRY */:
                return this.readImportEntry();
            case 17 /* EXPORT_SECTION_ENTRY */:
                return this.readExportEntry();
            case 13 /* FUNCTION_SECTION_ENTRY */:
                return this.readFunctionEntry();
            case 14 /* TABLE_SECTION_ENTRY */:
                return this.readTableEntry();
            case 15 /* MEMORY_SECTION_ENTRY */:
                return this.readMemoryEntry();
            case 16 /* GLOBAL_SECTION_ENTRY */:
            case 40 /* END_GLOBAL_SECTION_ENTRY */:
                return this.readGlobalEntry();
            case 39 /* BEGIN_GLOBAL_SECTION_ENTRY */:
                return this.readInitExpressionBody();
            case 20 /* ELEMENT_SECTION_ENTRY */:
            case 35 /* END_ELEMENT_SECTION_ENTRY */:
                return this.readElementEntry();
            case 33 /* BEGIN_ELEMENT_SECTION_ENTRY */:
                return this.readInitExpressionBody();
            case 34 /* ELEMENT_SECTION_ENTRY_BODY */:
                this.state = 35 /* END_ELEMENT_SECTION_ENTRY */;
                this.result = null;
                return true;
            case 18 /* DATA_SECTION_ENTRY */:
            case 38 /* END_DATA_SECTION_ENTRY */:
                return this.readDataEntry();
            case 36 /* BEGIN_DATA_SECTION_ENTRY */:
                return this.readInitExpressionBody();
            case 37 /* DATA_SECTION_ENTRY_BODY */:
                this.state = 38 /* END_DATA_SECTION_ENTRY */;
                this.result = null;
                return true;
            case 27 /* END_INIT_EXPRESSION_BODY */:
                switch (this._sectionId) {
                    case 6 /* Global */:
                        this.state = 40 /* END_GLOBAL_SECTION_ENTRY */;
                        return true;
                    case 11 /* Data */:
                        return this.readDataEntryBody();
                    case 9 /* Element */:
                        return this.readElementEntryBody();
                }
                this.error = new Error("Unexpected section type: " + this._sectionId);
                this.state = -1 /* ERROR */;
                return true;
            case 19 /* NAME_SECTION_ENTRY */:
                return this.readNameEntry();
            case 41 /* RELOC_SECTION_HEADER */:
                if (!this.hasVarIntBytes())
                    return false;
                this._sectionEntriesLeft = this.readVarUint32() >>> 0;
                return this.readRelocEntry();
            case 21 /* LINKING_SECTION_ENTRY */:
                return this.readLinkingEntry();
            case 43 /* SOURCE_MAPPING_URL */:
                this.state = 4 /* END_SECTION */;
                this.result = null;
                return true;
            case 42 /* RELOC_SECTION_ENTRY */:
                return this.readRelocEntry();
            case 29 /* READING_FUNCTION_HEADER */:
            case 31 /* END_FUNCTION_BODY */:
                return this.readFunctionBody();
            case 28 /* BEGIN_FUNCTION_BODY */:
                this.state = 30 /* CODE_OPERATOR */;
                return this.readCodeOperator();
            case 25 /* BEGIN_INIT_EXPRESSION_BODY */:
                this.state = 26 /* INIT_EXPRESSION_OPERATOR */;
                return this.readCodeOperator();
            case 30 /* CODE_OPERATOR */:
            case 26 /* INIT_EXPRESSION_OPERATOR */:
                return this.readCodeOperator();
            case 6 /* READING_SECTION_RAW_DATA */:
                return this.readSectionRawData();
            case 22 /* START_SECTION_ENTRY */:
            case 7 /* SECTION_RAW_DATA */:
                this.state = 4 /* END_SECTION */;
                this.result = null;
                return true;
            default:
                this.error = new Error("Unsupported state: " + this.state);
                this.state = -1 /* ERROR */;
                return true;
        }
    };
    BinaryReader.prototype.skipSection = function () {
        if (this.state === -1 /* ERROR */ ||
            this.state === 0 /* INITIAL */ ||
            this.state === 4 /* END_SECTION */ ||
            this.state === 1 /* BEGIN_WASM */ ||
            this.state === 2 /* END_WASM */)
            return;
        this.state = 5 /* SKIPPING_SECTION */;
    };
    BinaryReader.prototype.skipFunctionBody = function () {
        if (this.state !== 28 /* BEGIN_FUNCTION_BODY */ &&
            this.state !== 30 /* CODE_OPERATOR */)
            return;
        this.state = 32 /* SKIPPING_FUNCTION_BODY */;
    };
    BinaryReader.prototype.skipInitExpression = function () {
        while (this.state === 26 /* INIT_EXPRESSION_OPERATOR */)
            this.readCodeOperator();
    };
    BinaryReader.prototype.fetchSectionRawData = function () {
        if (this.state !== 3 /* BEGIN_SECTION */) {
            this.error = new Error("Unsupported state: " + this.state);
            this.state = -1 /* ERROR */;
            return;
        }
        this.state = 6 /* 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 (_) { }
}
if (!exports.bytesToString) {
    exports.bytesToString = function (b) {
        var str = String.fromCharCode.apply(null, b);
        return decodeURIComponent(escape(str));
    };
}
PK
!<pѤq�.�.Bchrome/devtools/modules/devtools/client/shared/vendor/immutable.js/**
 *  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;

}));PK
!<?���=chrome/devtools/modules/devtools/client/shared/vendor/jsol.js/*
 * Copyright 2010, 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 () {
  /**
   JSOL stands for JavaScript Object Literal which is a string representing
   an object in JavaScript syntax.

   For example:

   {foo:"bar"} is equivalent to {"foo":"bar"} in JavaScript. Both are valid JSOL.

   Note that {"foo":"bar"} is proper JSON[1] therefore you can use one of the many
   JSON parsers out there like json2.js[2] or even the native browser's JSON parser,
   if available.

   However, {foo:"bar"} is NOT proper JSON but valid Javascript syntax for
   representing an object with one key, "foo" and its value, "bar".
   Using a JSON parser is not an option since this is NOT proper JSON.

   You can use JSOL.parse to safely parse any string that reprsents a JavaScript Object Literal.
   JSOL.parse will throw an Invalid JSOL exception on function calls, function declarations and variable references.

   Examples:

   JSOL.parse('{foo:"bar"}');  // valid

   JSOL.parse('{evil:(function(){alert("I\'m evil");})()}');  // invalid function calls

   JSOL.parse('{fn:function() { }}'); // invalid function declarations

   var bar = "bar";
   JSOL.parse('{foo:bar}');  // invalid variable references

   [1] http://www.json.org
   [2] http://www.json.org/json2.js
   */
  var trim =  /^(\s|\u00A0)+|(\s|\u00A0)+$/g; // Used for trimming whitespace
  var JSOL = {
    parse: function(text) {
      // make sure text is a "string"
      if (typeof text !== "string" || !text) {
        return null;
      }
      // Make sure leading/trailing whitespace is removed
      text = text.replace(trim, "");
      // Make sure the incoming text is actual JSOL (or Javascript Object Literal)
      // Logic borrowed from http://json.org/json2.js
      if ( /^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, "@")
           .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, "]")
           .replace(/(?:^|:|,)(?:\s*\[)+/g, ":")
           /** everything up to this point is json2.js **/
           /** this is the 5th stage where it accepts unquoted keys **/
           .replace(/\w*\s*\:/g, ":")) ) {
        return (new Function("return " + text))();
      }
      else {
        throw("Invalid JSOL: " + text);
      }
    }
  };

  if (typeof define === "function" && define.amd) {
    define(JSOL);
  } else if (typeof module === "object" && module.exports) {
    module.exports = JSOL;
  } else {
    this.JSOL = JSOL;
  }
})();
PK
!<�X�
����>chrome/devtools/modules/devtools/client/shared/vendor/jszip.js/*!

JSZip v3.1.3 - 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);
    }
};
// vim: set shiftwidth=4 softtabstop=4:

},{"./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":58}],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 = new pako[action]({
        raw:true,
        level : options.level || -1 // default compression
    });
    // the `meta` object from the last chunk received
    // this allow this worker to pass around metadata
    this.meta = {};

    var self = this;
    this._pako.onData = function(data) {
        self.push({
            data : data,
            meta : self.meta
        });
    };
}

utils.inherits(FlateWorker, GenericWorker);

/**
 * @see GenericWorker.processChunk
 */
FlateWorker.prototype.processChunk = function (chunk) {
    this.meta = chunk.meta;
    this._pako.push(utils.transformTo(ARRAY_TYPE, chunk.data), false);
};

/**
 * @see GenericWorker.flush
 */
FlateWorker.prototype.flush = function () {
    GenericWorker.prototype.flush.call(this);
    this._pako.push([], true);
};
/**
 * @see GenericWorker.cleanUp
 */
FlateWorker.prototype.cleanUp = function () {
    GenericWorker.prototype.cleanUp.call(this);
    this._pako = null;
};

exports.compressWorker = function (compressionOptions) {
    return new FlateWorker("Deflate", compressionOptions);
};
exports.uncompressWorker = function () {
    return new FlateWorker("Inflate", {});
};

},{"./stream/GenericWorker":28,"./utils":32,"pako":59}],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.1.3";

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 util = require('util');
util.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;

},{"readable-stream":16,"util":undefined}],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.
     * @param {Object} data the data to pass to the constructor.
     * @param {String} encoding the encoding to use.
     * @return {Buffer} a new Buffer.
     */
    newBuffer : function(data, encoding){
        return new Buffer(data, encoding);
    },
    /**
     * 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 specifed 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));
};

// vim: set shiftwidth=4 softtabstop=4:

},{"../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} resultType the name of the final type
 * @param {String} chunkType the type of the data in the given array.
 * @param {Array} dataArray the array containing the data chunks to concatenate
 * @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(resultType, chunkType, dataArray, mimeType) {
    var content = null;
    switch(resultType) {
        case "blob" :
            return utils.newBlob(dataArray, mimeType);
        case "base64" :
            content = concat(chunkType, dataArray);
            return base64.encode(content);
        default :
            content = concat(chunkType, dataArray);
            return utils.transformTo(resultType, 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, 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":
            internalType = "arraybuffer";
        break;
        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 = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.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.newBuffer(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('core-js/library/fn/set-immediate');
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 {Array[String|ArrayBuffer]} parts 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]").
 * @param {String} type the mime type of the blob.
 * @return {Blob} the created blob.
 */
exports.newBlob = function(parts, type) {
    exports.checkSupport("blob");

    try {
        // Blob constructor
        return new Blob(parts, {
            type: type
        });
    }
    catch (e) {

        try {
            // deprecated, browser only, old way
            var Builder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder;
            var builder = new Builder();
            for (var i = 0; i < parts.length; i++) {
                builder.append(parts[i]);
            }
            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.newBuffer(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.newBuffer(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.newBuffer(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.newBuffer(new Uint8Array(input));
    }
};

// uint8array to ?
transform["uint8array"] = {
    "string": arrayLikeToString,
    "array": function(input) {
        return arrayLikeToArrayLike(input, new Array(input.length));
    },
    "arraybuffer": function(input) {
        // copy the uint8array: DO NOT propagate the original ArrayBuffer, it
        // can be way larger (the whole zip file for example).
        var copy = new Uint8Array(input.length);
        if (input.length) {
            copy.set(input, 0);
        }
        return copy.buffer;
    },
    "uint8array": identity,
    "nodebuffer": function(input) {
        return nodejsUtils.newBuffer(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("The data of '" + name + "' is in an unsupported format !")
            );
        }
        // 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,"core-js/library/fn/set-immediate":36}],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 speficied 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 http://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 outputType = type.toLowerCase();
        var askUnicodeString = outputType === "string" || outputType === "text";
        if (outputType === "binarystring" || outputType === "text") {
            outputType = "string";
        }
        var 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());
        }

        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){
require('../modules/web.immediate');
module.exports = require('../modules/_core').setImmediate;
},{"../modules/_core":40,"../modules/web.immediate":56}],37:[function(require,module,exports){
module.exports = function(it){
  if(typeof it != 'function')throw TypeError(it + ' is not a function!');
  return it;
};
},{}],38:[function(require,module,exports){
var isObject = require('./_is-object');
module.exports = function(it){
  if(!isObject(it))throw TypeError(it + ' is not an object!');
  return it;
};
},{"./_is-object":51}],39:[function(require,module,exports){
var toString = {}.toString;

module.exports = function(it){
  return toString.call(it).slice(8, -1);
};
},{}],40:[function(require,module,exports){
var core = module.exports = {version: '2.3.0'};
if(typeof __e == 'number')__e = core; // eslint-disable-line no-undef
},{}],41:[function(require,module,exports){
// optional / simple context binding
var aFunction = require('./_a-function');
module.exports = function(fn, that, length){
  aFunction(fn);
  if(that === undefined)return fn;
  switch(length){
    case 1: return function(a){
      return fn.call(that, a);
    };
    case 2: return function(a, b){
      return fn.call(that, a, b);
    };
    case 3: return function(a, b, c){
      return fn.call(that, a, b, c);
    };
  }
  return function(/* ...args */){
    return fn.apply(that, arguments);
  };
};
},{"./_a-function":37}],42:[function(require,module,exports){
// Thank's IE8 for his funny defineProperty
module.exports = !require('./_fails')(function(){
  return Object.defineProperty({}, 'a', {get: function(){ return 7; }}).a != 7;
});
},{"./_fails":45}],43:[function(require,module,exports){
var isObject = require('./_is-object')
  , document = require('./_global').document
  // in old IE typeof document.createElement is 'object'
  , is = isObject(document) && isObject(document.createElement);
module.exports = function(it){
  return is ? document.createElement(it) : {};
};
},{"./_global":46,"./_is-object":51}],44:[function(require,module,exports){
var global    = require('./_global')
  , core      = require('./_core')
  , ctx       = require('./_ctx')
  , hide      = require('./_hide')
  , PROTOTYPE = 'prototype';

var $export = function(type, name, source){
  var IS_FORCED = type & $export.F
    , IS_GLOBAL = type & $export.G
    , IS_STATIC = type & $export.S
    , IS_PROTO  = type & $export.P
    , IS_BIND   = type & $export.B
    , IS_WRAP   = type & $export.W
    , exports   = IS_GLOBAL ? core : core[name] || (core[name] = {})
    , expProto  = exports[PROTOTYPE]
    , target    = IS_GLOBAL ? global : IS_STATIC ? global[name] : (global[name] || {})[PROTOTYPE]
    , key, own, out;
  if(IS_GLOBAL)source = name;
  for(key in source){
    // contains in native
    own = !IS_FORCED && target && target[key] !== undefined;
    if(own && key in exports)continue;
    // export native or passed
    out = own ? target[key] : source[key];
    // prevent global pollution for namespaces
    exports[key] = IS_GLOBAL && typeof target[key] != 'function' ? source[key]
    // bind timers to global for call from export context
    : IS_BIND && own ? ctx(out, global)
    // wrap global constructors for prevent change them in library
    : IS_WRAP && target[key] == out ? (function(C){
      var F = function(a, b, c){
        if(this instanceof C){
          switch(arguments.length){
            case 0: return new C;
            case 1: return new C(a);
            case 2: return new C(a, b);
          } return new C(a, b, c);
        } return C.apply(this, arguments);
      };
      F[PROTOTYPE] = C[PROTOTYPE];
      return F;
    // make static versions for prototype methods
    })(out) : IS_PROTO && typeof out == 'function' ? ctx(Function.call, out) : out;
    // export proto methods to core.%CONSTRUCTOR%.methods.%NAME%
    if(IS_PROTO){
      (exports.virtual || (exports.virtual = {}))[key] = out;
      // export proto methods to core.%CONSTRUCTOR%.prototype.%NAME%
      if(type & $export.R && expProto && !expProto[key])hide(expProto, key, out);
    }
  }
};
// type bitmap
$export.F = 1;   // forced
$export.G = 2;   // global
$export.S = 4;   // static
$export.P = 8;   // proto
$export.B = 16;  // bind
$export.W = 32;  // wrap
$export.U = 64;  // safe
$export.R = 128; // real proto method for `library` 
module.exports = $export;
},{"./_core":40,"./_ctx":41,"./_global":46,"./_hide":47}],45:[function(require,module,exports){
module.exports = function(exec){
  try {
    return !!exec();
  } catch(e){
    return true;
  }
};
},{}],46:[function(require,module,exports){
// https://github.com/zloirock/core-js/issues/86#issuecomment-115759028
var global = module.exports = typeof window != 'undefined' && window.Math == Math
  ? window : typeof self != 'undefined' && self.Math == Math ? self : Function('return this')();
if(typeof __g == 'number')__g = global; // eslint-disable-line no-undef
},{}],47:[function(require,module,exports){
var dP         = require('./_object-dp')
  , createDesc = require('./_property-desc');
module.exports = require('./_descriptors') ? function(object, key, value){
  return dP.f(object, key, createDesc(1, value));
} : function(object, key, value){
  object[key] = value;
  return object;
};
},{"./_descriptors":42,"./_object-dp":52,"./_property-desc":53}],48:[function(require,module,exports){
module.exports = require('./_global').document && document.documentElement;
},{"./_global":46}],49:[function(require,module,exports){
module.exports = !require('./_descriptors') && !require('./_fails')(function(){
  return Object.defineProperty(require('./_dom-create')('div'), 'a', {get: function(){ return 7; }}).a != 7;
});
},{"./_descriptors":42,"./_dom-create":43,"./_fails":45}],50:[function(require,module,exports){
// fast apply, http://jsperf.lnkit.com/fast-apply/5
module.exports = function(fn, args, that){
  var un = that === undefined;
  switch(args.length){
    case 0: return un ? fn()
                      : fn.call(that);
    case 1: return un ? fn(args[0])
                      : fn.call(that, args[0]);
    case 2: return un ? fn(args[0], args[1])
                      : fn.call(that, args[0], args[1]);
    case 3: return un ? fn(args[0], args[1], args[2])
                      : fn.call(that, args[0], args[1], args[2]);
    case 4: return un ? fn(args[0], args[1], args[2], args[3])
                      : fn.call(that, args[0], args[1], args[2], args[3]);
  } return              fn.apply(that, args);
};
},{}],51:[function(require,module,exports){
module.exports = function(it){
  return typeof it === 'object' ? it !== null : typeof it === 'function';
};
},{}],52:[function(require,module,exports){
var anObject       = require('./_an-object')
  , IE8_DOM_DEFINE = require('./_ie8-dom-define')
  , toPrimitive    = require('./_to-primitive')
  , dP             = Object.defineProperty;

exports.f = require('./_descriptors') ? Object.defineProperty : function defineProperty(O, P, Attributes){
  anObject(O);
  P = toPrimitive(P, true);
  anObject(Attributes);
  if(IE8_DOM_DEFINE)try {
    return dP(O, P, Attributes);
  } catch(e){ /* empty */ }
  if('get' in Attributes || 'set' in Attributes)throw TypeError('Accessors not supported!');
  if('value' in Attributes)O[P] = Attributes.value;
  return O;
};
},{"./_an-object":38,"./_descriptors":42,"./_ie8-dom-define":49,"./_to-primitive":55}],53:[function(require,module,exports){
module.exports = function(bitmap, value){
  return {
    enumerable  : !(bitmap & 1),
    configurable: !(bitmap & 2),
    writable    : !(bitmap & 4),
    value       : value
  };
};
},{}],54:[function(require,module,exports){
var ctx                = require('./_ctx')
  , invoke             = require('./_invoke')
  , html               = require('./_html')
  , cel                = require('./_dom-create')
  , global             = require('./_global')
  , process            = global.process
  , setTask            = global.setImmediate
  , clearTask          = global.clearImmediate
  , MessageChannel     = global.MessageChannel
  , counter            = 0
  , queue              = {}
  , ONREADYSTATECHANGE = 'onreadystatechange'
  , defer, channel, port;
var run = function(){
  var id = +this;
  if(queue.hasOwnProperty(id)){
    var fn = queue[id];
    delete queue[id];
    fn();
  }
};
var listener = function(event){
  run.call(event.data);
};
// Node.js 0.9+ & IE10+ has setImmediate, otherwise:
if(!setTask || !clearTask){
  setTask = function setImmediate(fn){
    var args = [], i = 1;
    while(arguments.length > i)args.push(arguments[i++]);
    queue[++counter] = function(){
      invoke(typeof fn == 'function' ? fn : Function(fn), args);
    };
    defer(counter);
    return counter;
  };
  clearTask = function clearImmediate(id){
    delete queue[id];
  };
  // Node.js 0.8-
  if(require('./_cof')(process) == 'process'){
    defer = function(id){
      process.nextTick(ctx(run, id, 1));
    };
  // Browsers with MessageChannel, includes WebWorkers
  } else if(MessageChannel){
    channel = new MessageChannel;
    port    = channel.port2;
    channel.port1.onmessage = listener;
    defer = ctx(port.postMessage, port, 1);
  // Browsers with postMessage, skip WebWorkers
  // IE8 has postMessage, but it's sync & typeof its postMessage is 'object'
  } else if(global.addEventListener && typeof postMessage == 'function' && !global.importScripts){
    defer = function(id){
      global.postMessage(id + '', '*');
    };
    global.addEventListener('message', listener, false);
  // IE8-
  } else if(ONREADYSTATECHANGE in cel('script')){
    defer = function(id){
      html.appendChild(cel('script'))[ONREADYSTATECHANGE] = function(){
        html.removeChild(this);
        run.call(id);
      };
    };
  // Rest old browsers
  } else {
    defer = function(id){
      setTimeout(ctx(run, id, 1), 0);
    };
  }
}
module.exports = {
  set:   setTask,
  clear: clearTask
};
},{"./_cof":39,"./_ctx":41,"./_dom-create":43,"./_global":46,"./_html":48,"./_invoke":50}],55:[function(require,module,exports){
// 7.1.1 ToPrimitive(input [, PreferredType])
var isObject = require('./_is-object');
// instead of the ES6 spec version, we didn't implement @@toPrimitive case
// and the second argument - flag - preferred type is a string
module.exports = function(it, S){
  if(!isObject(it))return it;
  var fn, val;
  if(S && typeof (fn = it.toString) == 'function' && !isObject(val = fn.call(it)))return val;
  if(typeof (fn = it.valueOf) == 'function' && !isObject(val = fn.call(it)))return val;
  if(!S && typeof (fn = it.toString) == 'function' && !isObject(val = fn.call(it)))return val;
  throw TypeError("Can't convert object to primitive value");
};
},{"./_is-object":51}],56:[function(require,module,exports){
var $export = require('./_export')
  , $task   = require('./_task');
$export($export.G + $export.B, {
  setImmediate:   $task.set,
  clearImmediate: $task.clear
});
},{"./_export":44,"./_task":54}],57:[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 : {})
},{}],58:[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["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 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":57}],59:[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":60,"./lib/inflate":61,"./lib/utils/common":62,"./lib/zlib/constants":65}],60:[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; }

  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":62,"./utils/strings":63,"./zlib/deflate":67,"./zlib/messages":72,"./zlib/zstream":74}],61:[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; }

  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":62,"./utils/strings":63,"./zlib/constants":65,"./zlib/gzheader":68,"./zlib/inflate":70,"./zlib/messages":72,"./zlib/zstream":74}],62:[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);

},{}],63:[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":62}],64:[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.

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;

},{}],65:[function(require,module,exports){
'use strict';


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
};

},{}],66:[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.


// 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;

},{}],67:[function(require,module,exports){
'use strict';

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":62,"./adler32":64,"./crc32":66,"./messages":72,"./trees":73}],68:[function(require,module,exports){
'use strict';


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;

},{}],69:[function(require,module,exports){
'use strict';

// 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;
};

},{}],70:[function(require,module,exports){
'use strict';


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":62,"./adler32":64,"./crc32":66,"./inffast":69,"./inftrees":71}],71:[function(require,module,exports){
'use strict';


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;
  }

  var i = 0;
  /* process all codes and make table entries */
  for (;;) {
    i++;
    /* 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":62}],72:[function(require,module,exports){
'use strict';

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) */
};

},{}],73:[function(require,module,exports){
'use strict';


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":62}],74:[function(require,module,exports){
'use strict';


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;

},{}]},{},[10])(10)
});PK
!<�k���;�;?chrome/devtools/modules/devtools/client/shared/vendor/lodash.js/**
 * @license
 * Lodash <https://lodash.com/>
 * Copyright JS Foundation and other contributors <https://js.foundation/>
 * 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
 */
;(function() {

  /** Used as a safe reference for `undefined` in pre-ES5 environments. */
  var undefined;

  /** Used as the semantic version number. */
  var VERSION = '4.17.4';

  /** Used as the size to enable large array optimizations. */
  var LARGE_ARRAY_SIZE = 200;

  /** Error message constants. */
  var CORE_ERROR_TEXT = 'Unsupported core-js use. Try https://npms.io/search?q=ponyfill.',
      FUNC_ERROR_TEXT = 'Expected a function';

  /** Used to stand-in for `undefined` hash values. */
  var HASH_UNDEFINED = '__lodash_hash_undefined__';

  /** Used as the maximum memoize cache size. */
  var MAX_MEMOIZE_SIZE = 500;

  /** Used as the internal argument placeholder. */
  var PLACEHOLDER = '__lodash_placeholder__';

  /** Used to compose bitmasks for cloning. */
  var CLONE_DEEP_FLAG = 1,
      CLONE_FLAT_FLAG = 2,
      CLONE_SYMBOLS_FLAG = 4;

  /** Used to compose bitmasks for value comparisons. */
  var COMPARE_PARTIAL_FLAG = 1,
      COMPARE_UNORDERED_FLAG = 2;

  /** Used to compose bitmasks for function metadata. */
  var WRAP_BIND_FLAG = 1,
      WRAP_BIND_KEY_FLAG = 2,
      WRAP_CURRY_BOUND_FLAG = 4,
      WRAP_CURRY_FLAG = 8,
      WRAP_CURRY_RIGHT_FLAG = 16,
      WRAP_PARTIAL_FLAG = 32,
      WRAP_PARTIAL_RIGHT_FLAG = 64,
      WRAP_ARY_FLAG = 128,
      WRAP_REARG_FLAG = 256,
      WRAP_FLIP_FLAG = 512;

  /** Used as default options for `_.truncate`. */
  var DEFAULT_TRUNC_LENGTH = 30,
      DEFAULT_TRUNC_OMISSION = '...';

  /** Used to detect hot functions by number of calls within a span of milliseconds. */
  var HOT_COUNT = 800,
      HOT_SPAN = 16;

  /** Used to indicate the type of lazy iteratees. */
  var LAZY_FILTER_FLAG = 1,
      LAZY_MAP_FLAG = 2,
      LAZY_WHILE_FLAG = 3;

  /** Used as references for various `Number` constants. */
  var INFINITY = 1 / 0,
      MAX_SAFE_INTEGER = 9007199254740991,
      MAX_INTEGER = 1.7976931348623157e+308,
      NAN = 0 / 0;

  /** Used as references for the maximum length and index of an array. */
  var MAX_ARRAY_LENGTH = 4294967295,
      MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1,
      HALF_MAX_ARRAY_LENGTH = MAX_ARRAY_LENGTH >>> 1;

  /** Used to associate wrap methods with their bit flags. */
  var wrapFlags = [
    ['ary', WRAP_ARY_FLAG],
    ['bind', WRAP_BIND_FLAG],
    ['bindKey', WRAP_BIND_KEY_FLAG],
    ['curry', WRAP_CURRY_FLAG],
    ['curryRight', WRAP_CURRY_RIGHT_FLAG],
    ['flip', WRAP_FLIP_FLAG],
    ['partial', WRAP_PARTIAL_FLAG],
    ['partialRight', WRAP_PARTIAL_RIGHT_FLAG],
    ['rearg', WRAP_REARG_FLAG]
  ];

  /** `Object#toString` result references. */
  var argsTag = '[object Arguments]',
      arrayTag = '[object Array]',
      asyncTag = '[object AsyncFunction]',
      boolTag = '[object Boolean]',
      dateTag = '[object Date]',
      domExcTag = '[object DOMException]',
      errorTag = '[object Error]',
      funcTag = '[object Function]',
      genTag = '[object GeneratorFunction]',
      mapTag = '[object Map]',
      numberTag = '[object Number]',
      nullTag = '[object Null]',
      objectTag = '[object Object]',
      promiseTag = '[object Promise]',
      proxyTag = '[object Proxy]',
      regexpTag = '[object RegExp]',
      setTag = '[object Set]',
      stringTag = '[object String]',
      symbolTag = '[object Symbol]',
      undefinedTag = '[object Undefined]',
      weakMapTag = '[object WeakMap]',
      weakSetTag = '[object WeakSet]';

  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 empty string literals in compiled template source. */
  var reEmptyStringLeading = /\b__p \+= '';/g,
      reEmptyStringMiddle = /\b(__p \+=) '' \+/g,
      reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g;

  /** Used to match HTML entities and HTML characters. */
  var reEscapedHtml = /&(?:amp|lt|gt|quot|#39);/g,
      reUnescapedHtml = /[&<>"']/g,
      reHasEscapedHtml = RegExp(reEscapedHtml.source),
      reHasUnescapedHtml = RegExp(reUnescapedHtml.source);

  /** Used to match template delimiters. */
  var reEscape = /<%-([\s\S]+?)%>/g,
      reEvaluate = /<%([\s\S]+?)%>/g,
      reInterpolate = /<%=([\s\S]+?)%>/g;

  /** 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,
      reHasRegExpChar = RegExp(reRegExpChar.source);

  /** Used to match leading and trailing whitespace. */
  var reTrim = /^\s+|\s+$/g,
      reTrimStart = /^\s+/,
      reTrimEnd = /\s+$/;

  /** Used to match wrap detail comments. */
  var reWrapComment = /\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/,
      reWrapDetails = /\{\n\/\* \[wrapped with (.+)\] \*/,
      reSplitDetails = /,? & /;

  /** Used to match words composed of alphanumeric characters. */
  var reAsciiWord = /[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g;

  /** Used to match backslashes in property paths. */
  var reEscapeChar = /\\(\\)?/g;

  /**
   * Used to match
   * [ES template delimiters](http://ecma-international.org/ecma-262/7.0/#sec-template-literal-lexical-components).
   */
  var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g;

  /** Used to match `RegExp` flags from their coerced string values. */
  var reFlags = /\w*$/;

  /** Used to detect bad signed hexadecimal string values. */
  var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;

  /** Used to detect binary string values. */
  var reIsBinary = /^0b[01]+$/i;

  /** Used to detect host constructors (Safari). */
  var reIsHostCtor = /^\[object .+?Constructor\]$/;

  /** Used to detect octal string values. */
  var reIsOctal = /^0o[0-7]+$/i;

  /** Used to detect unsigned integer values. */
  var reIsUint = /^(?:0|[1-9]\d*)$/;

  /** Used to match Latin Unicode letters (excluding mathematical operators). */
  var reLatin = /[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g;

  /** Used to ensure capturing order of template delimiters. */
  var reNoMatch = /($^)/;

  /** Used to match unescaped characters in compiled string literals. */
  var reUnescapedString = /['\n\r\u2028\u2029\\]/g;

  /** Used to compose unicode character classes. */
  var rsAstralRange = '\\ud800-\\udfff',
      rsComboMarksRange = '\\u0300-\\u036f',
      reComboHalfMarksRange = '\\ufe20-\\ufe2f',
      rsComboSymbolsRange = '\\u20d0-\\u20ff',
      rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange,
      rsDingbatRange = '\\u2700-\\u27bf',
      rsLowerRange = 'a-z\\xdf-\\xf6\\xf8-\\xff',
      rsMathOpRange = '\\xac\\xb1\\xd7\\xf7',
      rsNonCharRange = '\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf',
      rsPunctuationRange = '\\u2000-\\u206f',
      rsSpaceRange = ' \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000',
      rsUpperRange = 'A-Z\\xc0-\\xd6\\xd8-\\xde',
      rsVarRange = '\\ufe0e\\ufe0f',
      rsBreakRange = rsMathOpRange + rsNonCharRange + rsPunctuationRange + rsSpaceRange;

  /** Used to compose unicode capture groups. */
  var rsApos = "['\u2019]",
      rsAstral = '[' + rsAstralRange + ']',
      rsBreak = '[' + rsBreakRange + ']',
      rsCombo = '[' + rsComboRange + ']',
      rsDigits = '\\d+',
      rsDingbat = '[' + rsDingbatRange + ']',
      rsLower = '[' + rsLowerRange + ']',
      rsMisc = '[^' + rsAstralRange + rsBreakRange + rsDigits + rsDingbatRange + rsLowerRange + rsUpperRange + ']',
      rsFitz = '\\ud83c[\\udffb-\\udfff]',
      rsModifier = '(?:' + rsCombo + '|' + rsFitz + ')',
      rsNonAstral = '[^' + rsAstralRange + ']',
      rsRegional = '(?:\\ud83c[\\udde6-\\uddff]){2}',
      rsSurrPair = '[\\ud800-\\udbff][\\udc00-\\udfff]',
      rsUpper = '[' + rsUpperRange + ']',
      rsZWJ = '\\u200d';

  /** Used to compose unicode regexes. */
  var rsMiscLower = '(?:' + rsLower + '|' + rsMisc + ')',
      rsMiscUpper = '(?:' + rsUpper + '|' + rsMisc + ')',
      rsOptContrLower = '(?:' + rsApos + '(?:d|ll|m|re|s|t|ve))?',
      rsOptContrUpper = '(?:' + rsApos + '(?:D|LL|M|RE|S|T|VE))?',
      reOptMod = rsModifier + '?',
      rsOptVar = '[' + rsVarRange + ']?',
      rsOptJoin = '(?:' + rsZWJ + '(?:' + [rsNonAstral, rsRegional, rsSurrPair].join('|') + ')' + rsOptVar + reOptMod + ')*',
      rsOrdLower = '\\d*(?:(?:1st|2nd|3rd|(?![123])\\dth)\\b)',
      rsOrdUpper = '\\d*(?:(?:1ST|2ND|3RD|(?![123])\\dTH)\\b)',
      rsSeq = rsOptVar + reOptMod + rsOptJoin,
      rsEmoji = '(?:' + [rsDingbat, rsRegional, rsSurrPair].join('|') + ')' + rsSeq,
      rsSymbol = '(?:' + [rsNonAstral + rsCombo + '?', rsCombo, rsRegional, rsSurrPair, rsAstral].join('|') + ')';

  /** Used to match apostrophes. */
  var reApos = RegExp(rsApos, 'g');

  /**
   * Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks) and
   * [combining diacritical marks for symbols](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks_for_Symbols).
   */
  var reComboMark = RegExp(rsCombo, 'g');

  /** Used to match [string symbols](https://mathiasbynens.be/notes/javascript-unicode). */
  var reUnicode = RegExp(rsFitz + '(?=' + rsFitz + ')|' + rsSymbol + rsSeq, 'g');

  /** Used to match complex or compound words. */
  var reUnicodeWord = RegExp([
    rsUpper + '?' + rsLower + '+' + rsOptContrLower + '(?=' + [rsBreak, rsUpper, '$'].join('|') + ')',
    rsMiscUpper + '+' + rsOptContrUpper + '(?=' + [rsBreak, rsUpper + rsMiscLower, '$'].join('|') + ')',
    rsUpper + '?' + rsMiscLower + '+' + rsOptContrLower,
    rsUpper + '+' + rsOptContrUpper,
    rsOrdUpper,
    rsOrdLower,
    rsDigits,
    rsEmoji
  ].join('|'), 'g');

  /** Used to detect strings with [zero-width joiners or code points from the astral planes](http://eev.ee/blog/2015/09/12/dark-corners-of-unicode/). */
  var reHasUnicode = RegExp('[' + rsZWJ + rsAstralRange  + rsComboRange + rsVarRange + ']');

  /** Used to detect strings that need a more robust regexp to match words. */
  var reHasUnicodeWord = /[a-z][A-Z]|[A-Z]{2,}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/;

  /** Used to assign default `context` object properties. */
  var contextProps = [
    'Array', 'Buffer', 'DataView', 'Date', 'Error', 'Float32Array', 'Float64Array',
    'Function', 'Int8Array', 'Int16Array', 'Int32Array', 'Map', 'Math', 'Object',
    'Promise', 'RegExp', 'Set', 'String', 'Symbol', 'TypeError', 'Uint8Array',
    'Uint8ClampedArray', 'Uint16Array', 'Uint32Array', 'WeakMap',
    '_', 'clearTimeout', 'isFinite', 'parseInt', 'setTimeout'
  ];

  /** Used to make template sourceURLs easier to identify. */
  var templateCounter = -1;

  /** 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;

  /** Used to identify `toStringTag` values supported by `_.clone`. */
  var cloneableTags = {};
  cloneableTags[argsTag] = cloneableTags[arrayTag] =
  cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] =
  cloneableTags[boolTag] = cloneableTags[dateTag] =
  cloneableTags[float32Tag] = cloneableTags[float64Tag] =
  cloneableTags[int8Tag] = cloneableTags[int16Tag] =
  cloneableTags[int32Tag] = cloneableTags[mapTag] =
  cloneableTags[numberTag] = cloneableTags[objectTag] =
  cloneableTags[regexpTag] = cloneableTags[setTag] =
  cloneableTags[stringTag] = cloneableTags[symbolTag] =
  cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] =
  cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;
  cloneableTags[errorTag] = cloneableTags[funcTag] =
  cloneableTags[weakMapTag] = false;

  /** Used to map Latin Unicode letters to basic Latin letters. */
  var deburredLetters = {
    // Latin-1 Supplement block.
    '\xc0': 'A',  '\xc1': 'A', '\xc2': 'A', '\xc3': 'A', '\xc4': 'A', '\xc5': 'A',
    '\xe0': 'a',  '\xe1': 'a', '\xe2': 'a', '\xe3': 'a', '\xe4': 'a', '\xe5': 'a',
    '\xc7': 'C',  '\xe7': 'c',
    '\xd0': 'D',  '\xf0': 'd',
    '\xc8': 'E',  '\xc9': 'E', '\xca': 'E', '\xcb': 'E',
    '\xe8': 'e',  '\xe9': 'e', '\xea': 'e', '\xeb': 'e',
    '\xcc': 'I',  '\xcd': 'I', '\xce': 'I', '\xcf': 'I',
    '\xec': 'i',  '\xed': 'i', '\xee': 'i', '\xef': 'i',
    '\xd1': 'N',  '\xf1': 'n',
    '\xd2': 'O',  '\xd3': 'O', '\xd4': 'O', '\xd5': 'O', '\xd6': 'O', '\xd8': 'O',
    '\xf2': 'o',  '\xf3': 'o', '\xf4': 'o', '\xf5': 'o', '\xf6': 'o', '\xf8': 'o',
    '\xd9': 'U',  '\xda': 'U', '\xdb': 'U', '\xdc': 'U',
    '\xf9': 'u',  '\xfa': 'u', '\xfb': 'u', '\xfc': 'u',
    '\xdd': 'Y',  '\xfd': 'y', '\xff': 'y',
    '\xc6': 'Ae', '\xe6': 'ae',
    '\xde': 'Th', '\xfe': 'th',
    '\xdf': 'ss',
    // Latin Extended-A block.
    '\u0100': 'A',  '\u0102': 'A', '\u0104': 'A',
    '\u0101': 'a',  '\u0103': 'a', '\u0105': 'a',
    '\u0106': 'C',  '\u0108': 'C', '\u010a': 'C', '\u010c': 'C',
    '\u0107': 'c',  '\u0109': 'c', '\u010b': 'c', '\u010d': 'c',
    '\u010e': 'D',  '\u0110': 'D', '\u010f': 'd', '\u0111': 'd',
    '\u0112': 'E',  '\u0114': 'E', '\u0116': 'E', '\u0118': 'E', '\u011a': 'E',
    '\u0113': 'e',  '\u0115': 'e', '\u0117': 'e', '\u0119': 'e', '\u011b': 'e',
    '\u011c': 'G',  '\u011e': 'G', '\u0120': 'G', '\u0122': 'G',
    '\u011d': 'g',  '\u011f': 'g', '\u0121': 'g', '\u0123': 'g',
    '\u0124': 'H',  '\u0126': 'H', '\u0125': 'h', '\u0127': 'h',
    '\u0128': 'I',  '\u012a': 'I', '\u012c': 'I', '\u012e': 'I', '\u0130': 'I',
    '\u0129': 'i',  '\u012b': 'i', '\u012d': 'i', '\u012f': 'i', '\u0131': 'i',
    '\u0134': 'J',  '\u0135': 'j',
    '\u0136': 'K',  '\u0137': 'k', '\u0138': 'k',
    '\u0139': 'L',  '\u013b': 'L', '\u013d': 'L', '\u013f': 'L', '\u0141': 'L',
    '\u013a': 'l',  '\u013c': 'l', '\u013e': 'l', '\u0140': 'l', '\u0142': 'l',
    '\u0143': 'N',  '\u0145': 'N', '\u0147': 'N', '\u014a': 'N',
    '\u0144': 'n',  '\u0146': 'n', '\u0148': 'n', '\u014b': 'n',
    '\u014c': 'O',  '\u014e': 'O', '\u0150': 'O',
    '\u014d': 'o',  '\u014f': 'o', '\u0151': 'o',
    '\u0154': 'R',  '\u0156': 'R', '\u0158': 'R',
    '\u0155': 'r',  '\u0157': 'r', '\u0159': 'r',
    '\u015a': 'S',  '\u015c': 'S', '\u015e': 'S', '\u0160': 'S',
    '\u015b': 's',  '\u015d': 's', '\u015f': 's', '\u0161': 's',
    '\u0162': 'T',  '\u0164': 'T', '\u0166': 'T',
    '\u0163': 't',  '\u0165': 't', '\u0167': 't',
    '\u0168': 'U',  '\u016a': 'U', '\u016c': 'U', '\u016e': 'U', '\u0170': 'U', '\u0172': 'U',
    '\u0169': 'u',  '\u016b': 'u', '\u016d': 'u', '\u016f': 'u', '\u0171': 'u', '\u0173': 'u',
    '\u0174': 'W',  '\u0175': 'w',
    '\u0176': 'Y',  '\u0177': 'y', '\u0178': 'Y',
    '\u0179': 'Z',  '\u017b': 'Z', '\u017d': 'Z',
    '\u017a': 'z',  '\u017c': 'z', '\u017e': 'z',
    '\u0132': 'IJ', '\u0133': 'ij',
    '\u0152': 'Oe', '\u0153': 'oe',
    '\u0149': "'n", '\u017f': 's'
  };

  /** Used to map characters to HTML entities. */
  var htmlEscapes = {
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    "'": '&#39;'
  };

  /** Used to map HTML entities to characters. */
  var htmlUnescapes = {
    '&amp;': '&',
    '&lt;': '<',
    '&gt;': '>',
    '&quot;': '"',
    '&#39;': "'"
  };

  /** Used to escape characters for inclusion in compiled string literals. */
  var stringEscapes = {
    '\\': '\\',
    "'": "'",
    '\n': 'n',
    '\r': 'r',
    '\u2028': 'u2028',
    '\u2029': 'u2029'
  };

  /** Built-in method references without a dependency on `root`. */
  var freeParseFloat = parseFloat,
      freeParseInt = parseInt;

  /** 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 && freeProcess.binding('util');
    } catch (e) {}
  }());

  /* Node.js helper references. */
  var nodeIsArrayBuffer = nodeUtil && nodeUtil.isArrayBuffer,
      nodeIsDate = nodeUtil && nodeUtil.isDate,
      nodeIsMap = nodeUtil && nodeUtil.isMap,
      nodeIsRegExp = nodeUtil && nodeUtil.isRegExp,
      nodeIsSet = nodeUtil && nodeUtil.isSet,
      nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray;

  /*--------------------------------------------------------------------------*/

  /**
   * Adds the key-value `pair` to `map`.
   *
   * @private
   * @param {Object} map The map to modify.
   * @param {Array} pair The key-value pair to add.
   * @returns {Object} Returns `map`.
   */
  function addMapEntry(map, pair) {
    // Don't return `map.set` because it's not chainable in IE 11.
    map.set(pair[0], pair[1]);
    return map;
  }

  /**
   * Adds `value` to `set`.
   *
   * @private
   * @param {Object} set The set to modify.
   * @param {*} value The value to add.
   * @returns {Object} Returns `set`.
   */
  function addSetEntry(set, value) {
    // Don't return `set.add` because it's not chainable in IE 11.
    set.add(value);
    return set;
  }

  /**
   * 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 `baseAggregator` for arrays.
   *
   * @private
   * @param {Array} [array] The array to iterate over.
   * @param {Function} setter The function to set `accumulator` values.
   * @param {Function} iteratee The iteratee to transform keys.
   * @param {Object} accumulator The initial aggregated object.
   * @returns {Function} Returns `accumulator`.
   */
  function arrayAggregator(array, setter, iteratee, accumulator) {
    var index = -1,
        length = array == null ? 0 : array.length;

    while (++index < length) {
      var value = array[index];
      setter(accumulator, value, iteratee(value), array);
    }
    return accumulator;
  }

  /**
   * A specialized version of `_.forEach` 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 `array`.
   */
  function arrayEach(array, iteratee) {
    var index = -1,
        length = array == null ? 0 : array.length;

    while (++index < length) {
      if (iteratee(array[index], index, array) === false) {
        break;
      }
    }
    return array;
  }

  /**
   * A specialized version of `_.forEachRight` 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 `array`.
   */
  function arrayEachRight(array, iteratee) {
    var length = array == null ? 0 : array.length;

    while (length--) {
      if (iteratee(array[length], length, array) === false) {
        break;
      }
    }
    return array;
  }

  /**
   * A specialized version of `_.every` 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 all elements pass the predicate check,
   *  else `false`.
   */
  function arrayEvery(array, predicate) {
    var index = -1,
        length = array == null ? 0 : array.length;

    while (++index < length) {
      if (!predicate(array[index], index, array)) {
        return false;
      }
    }
    return true;
  }

  /**
   * A specialized version of `_.filter` 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 {Array} Returns the new filtered array.
   */
  function arrayFilter(array, predicate) {
    var index = -1,
        length = array == null ? 0 : array.length,
        resIndex = 0,
        result = [];

    while (++index < length) {
      var value = array[index];
      if (predicate(value, index, array)) {
        result[resIndex++] = value;
      }
    }
    return result;
  }

  /**
   * A specialized version of `_.includes` for arrays without support for
   * specifying an index to search from.
   *
   * @private
   * @param {Array} [array] The array to inspect.
   * @param {*} target The value to search for.
   * @returns {boolean} Returns `true` if `target` is found, else `false`.
   */
  function arrayIncludes(array, value) {
    var length = array == null ? 0 : array.length;
    return !!length && baseIndexOf(array, value, 0) > -1;
  }

  /**
   * This function is like `arrayIncludes` except that it accepts a comparator.
   *
   * @private
   * @param {Array} [array] The array to inspect.
   * @param {*} target The value to search for.
   * @param {Function} comparator The comparator invoked per element.
   * @returns {boolean} Returns `true` if `target` is found, else `false`.
   */
  function arrayIncludesWith(array, value, comparator) {
    var index = -1,
        length = array == null ? 0 : array.length;

    while (++index < length) {
      if (comparator(value, array[index])) {
        return true;
      }
    }
    return false;
  }

  /**
   * 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 == null ? 0 : array.length,
        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 `_.reduce` for arrays without support for
   * iteratee shorthands.
   *
   * @private
   * @param {Array} [array] The array to iterate over.
   * @param {Function} iteratee The function invoked per iteration.
   * @param {*} [accumulator] The initial value.
   * @param {boolean} [initAccum] Specify using the first element of `array` as
   *  the initial value.
   * @returns {*} Returns the accumulated value.
   */
  function arrayReduce(array, iteratee, accumulator, initAccum) {
    var index = -1,
        length = array == null ? 0 : array.length;

    if (initAccum && length) {
      accumulator = array[++index];
    }
    while (++index < length) {
      accumulator = iteratee(accumulator, array[index], index, array);
    }
    return accumulator;
  }

  /**
   * A specialized version of `_.reduceRight` for arrays without support for
   * iteratee shorthands.
   *
   * @private
   * @param {Array} [array] The array to iterate over.
   * @param {Function} iteratee The function invoked per iteration.
   * @param {*} [accumulator] The initial value.
   * @param {boolean} [initAccum] Specify using the last element of `array` as
   *  the initial value.
   * @returns {*} Returns the accumulated value.
   */
  function arrayReduceRight(array, iteratee, accumulator, initAccum) {
    var length = array == null ? 0 : array.length;
    if (initAccum && length) {
      accumulator = array[--length];
    }
    while (length--) {
      accumulator = iteratee(accumulator, array[length], length, array);
    }
    return accumulator;
  }

  /**
   * 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 == null ? 0 : array.length;

    while (++index < length) {
      if (predicate(array[index], index, array)) {
        return true;
      }
    }
    return false;
  }

  /**
   * Gets the size of an ASCII `string`.
   *
   * @private
   * @param {string} string The string inspect.
   * @returns {number} Returns the string size.
   */
  var asciiSize = baseProperty('length');

  /**
   * Converts an ASCII `string` to an array.
   *
   * @private
   * @param {string} string The string to convert.
   * @returns {Array} Returns the converted array.
   */
  function asciiToArray(string) {
    return string.split('');
  }

  /**
   * Splits an ASCII `string` into an array of its words.
   *
   * @private
   * @param {string} The string to inspect.
   * @returns {Array} Returns the words of `string`.
   */
  function asciiWords(string) {
    return string.match(reAsciiWord) || [];
  }

  /**
   * The base implementation of methods like `_.findKey` and `_.findLastKey`,
   * without support for iteratee shorthands, which iterates over `collection`
   * using `eachFunc`.
   *
   * @private
   * @param {Array|Object} collection The collection to inspect.
   * @param {Function} predicate The function invoked per iteration.
   * @param {Function} eachFunc The function to iterate over `collection`.
   * @returns {*} Returns the found element or its key, else `undefined`.
   */
  function baseFindKey(collection, predicate, eachFunc) {
    var result;
    eachFunc(collection, function(value, key, collection) {
      if (predicate(value, key, collection)) {
        result = key;
        return false;
      }
    });
    return result;
  }

  /**
   * The base implementation of `_.findIndex` and `_.findLastIndex` without
   * support for iteratee shorthands.
   *
   * @private
   * @param {Array} array The array to inspect.
   * @param {Function} predicate The function invoked per iteration.
   * @param {number} fromIndex The index to search from.
   * @param {boolean} [fromRight] Specify iterating from right to left.
   * @returns {number} Returns the index of the matched value, else `-1`.
   */
  function baseFindIndex(array, predicate, fromIndex, fromRight) {
    var length = array.length,
        index = fromIndex + (fromRight ? 1 : -1);

    while ((fromRight ? index-- : ++index < length)) {
      if (predicate(array[index], index, array)) {
        return index;
      }
    }
    return -1;
  }

  /**
   * The base implementation of `_.indexOf` without `fromIndex` bounds checks.
   *
   * @private
   * @param {Array} array The array to inspect.
   * @param {*} value The value to search for.
   * @param {number} fromIndex The index to search from.
   * @returns {number} Returns the index of the matched value, else `-1`.
   */
  function baseIndexOf(array, value, fromIndex) {
    return value === value
      ? strictIndexOf(array, value, fromIndex)
      : baseFindIndex(array, baseIsNaN, fromIndex);
  }

  /**
   * This function is like `baseIndexOf` except that it accepts a comparator.
   *
   * @private
   * @param {Array} array The array to inspect.
   * @param {*} value The value to search for.
   * @param {number} fromIndex The index to search from.
   * @param {Function} comparator The comparator invoked per element.
   * @returns {number} Returns the index of the matched value, else `-1`.
   */
  function baseIndexOfWith(array, value, fromIndex, comparator) {
    var index = fromIndex - 1,
        length = array.length;

    while (++index < length) {
      if (comparator(array[index], value)) {
        return index;
      }
    }
    return -1;
  }

  /**
   * The base implementation of `_.isNaN` without support for number objects.
   *
   * @private
   * @param {*} value The value to check.
   * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`.
   */
  function baseIsNaN(value) {
    return value !== value;
  }

  /**
   * The base implementation of `_.mean` and `_.meanBy` without support for
   * iteratee shorthands.
   *
   * @private
   * @param {Array} array The array to iterate over.
   * @param {Function} iteratee The function invoked per iteration.
   * @returns {number} Returns the mean.
   */
  function baseMean(array, iteratee) {
    var length = array == null ? 0 : array.length;
    return length ? (baseSum(array, iteratee) / length) : NAN;
  }

  /**
   * 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 `_.propertyOf` without support for deep paths.
   *
   * @private
   * @param {Object} object The object to query.
   * @returns {Function} Returns the new accessor function.
   */
  function basePropertyOf(object) {
    return function(key) {
      return object == null ? undefined : object[key];
    };
  }

  /**
   * The base implementation of `_.reduce` and `_.reduceRight`, without support
   * for iteratee shorthands, which iterates over `collection` using `eachFunc`.
   *
   * @private
   * @param {Array|Object} collection The collection to iterate over.
   * @param {Function} iteratee The function invoked per iteration.
   * @param {*} accumulator The initial value.
   * @param {boolean} initAccum Specify using the first or last element of
   *  `collection` as the initial value.
   * @param {Function} eachFunc The function to iterate over `collection`.
   * @returns {*} Returns the accumulated value.
   */
  function baseReduce(collection, iteratee, accumulator, initAccum, eachFunc) {
    eachFunc(collection, function(value, index, collection) {
      accumulator = initAccum
        ? (initAccum = false, value)
        : iteratee(accumulator, value, index, collection);
    });
    return accumulator;
  }

  /**
   * 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 `_.sum` and `_.sumBy` without support for
   * iteratee shorthands.
   *
   * @private
   * @param {Array} array The array to iterate over.
   * @param {Function} iteratee The function invoked per iteration.
   * @returns {number} Returns the sum.
   */
  function baseSum(array, iteratee) {
    var result,
        index = -1,
        length = array.length;

    while (++index < length) {
      var current = iteratee(array[index]);
      if (current !== undefined) {
        result = result === undefined ? current : (result + current);
      }
    }
    return result;
  }

  /**
   * 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 `_.toPairs` and `_.toPairsIn` which creates an array
   * of key-value pairs for `object` corresponding to the property names of `props`.
   *
   * @private
   * @param {Object} object The object to query.
   * @param {Array} props The property names to get values for.
   * @returns {Object} Returns the key-value pairs.
   */
  function baseToPairs(object, props) {
    return arrayMap(props, function(key) {
      return [key, object[key]];
    });
  }

  /**
   * 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);
    };
  }

  /**
   * The base implementation of `_.values` and `_.valuesIn` which creates an
   * array of `object` property values corresponding to the property names
   * of `props`.
   *
   * @private
   * @param {Object} object The object to query.
   * @param {Array} props The property names to get values for.
   * @returns {Object} Returns the array of property values.
   */
  function baseValues(object, props) {
    return arrayMap(props, function(key) {
      return object[key];
    });
  }

  /**
   * Checks if a `cache` value for `key` exists.
   *
   * @private
   * @param {Object} cache The cache to query.
   * @param {string} key The key of the entry to check.
   * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
   */
  function cacheHas(cache, key) {
    return cache.has(key);
  }

  /**
   * Used by `_.trim` and `_.trimStart` to get the index of the first string symbol
   * that is not found in the character symbols.
   *
   * @private
   * @param {Array} strSymbols The string symbols to inspect.
   * @param {Array} chrSymbols The character symbols to find.
   * @returns {number} Returns the index of the first unmatched string symbol.
   */
  function charsStartIndex(strSymbols, chrSymbols) {
    var index = -1,
        length = strSymbols.length;

    while (++index < length && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {}
    return index;
  }

  /**
   * Used by `_.trim` and `_.trimEnd` to get the index of the last string symbol
   * that is not found in the character symbols.
   *
   * @private
   * @param {Array} strSymbols The string symbols to inspect.
   * @param {Array} chrSymbols The character symbols to find.
   * @returns {number} Returns the index of the last unmatched string symbol.
   */
  function charsEndIndex(strSymbols, chrSymbols) {
    var index = strSymbols.length;

    while (index-- && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {}
    return index;
  }

  /**
   * Gets the number of `placeholder` occurrences in `array`.
   *
   * @private
   * @param {Array} array The array to inspect.
   * @param {*} placeholder The placeholder to search for.
   * @returns {number} Returns the placeholder count.
   */
  function countHolders(array, placeholder) {
    var length = array.length,
        result = 0;

    while (length--) {
      if (array[length] === placeholder) {
        ++result;
      }
    }
    return result;
  }

  /**
   * Used by `_.deburr` to convert Latin-1 Supplement and Latin Extended-A
   * letters to basic Latin letters.
   *
   * @private
   * @param {string} letter The matched letter to deburr.
   * @returns {string} Returns the deburred letter.
   */
  var deburrLetter = basePropertyOf(deburredLetters);

  /**
   * Used by `_.escape` to convert characters to HTML entities.
   *
   * @private
   * @param {string} chr The matched character to escape.
   * @returns {string} Returns the escaped character.
   */
  var escapeHtmlChar = basePropertyOf(htmlEscapes);

  /**
   * Used by `_.template` to escape characters for inclusion in compiled string literals.
   *
   * @private
   * @param {string} chr The matched character to escape.
   * @returns {string} Returns the escaped character.
   */
  function escapeStringChar(chr) {
    return '\\' + stringEscapes[chr];
  }

  /**
   * 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 `string` contains Unicode symbols.
   *
   * @private
   * @param {string} string The string to inspect.
   * @returns {boolean} Returns `true` if a symbol is found, else `false`.
   */
  function hasUnicode(string) {
    return reHasUnicode.test(string);
  }

  /**
   * Checks if `string` contains a word composed of Unicode symbols.
   *
   * @private
   * @param {string} string The string to inspect.
   * @returns {boolean} Returns `true` if a word is found, else `false`.
   */
  function hasUnicodeWord(string) {
    return reHasUnicodeWord.test(string);
  }

  /**
   * Converts `iterator` to an array.
   *
   * @private
   * @param {Object} iterator The iterator to convert.
   * @returns {Array} Returns the converted array.
   */
  function iteratorToArray(iterator) {
    var data,
        result = [];

    while (!(data = iterator.next()).done) {
      result.push(data.value);
    }
    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));
    };
  }

  /**
   * Replaces all `placeholder` elements in `array` with an internal placeholder
   * and returns an array of their indexes.
   *
   * @private
   * @param {Array} array The array to modify.
   * @param {*} placeholder The placeholder to replace.
   * @returns {Array} Returns the new array of placeholder indexes.
   */
  function replaceHolders(array, placeholder) {
    var index = -1,
        length = array.length,
        resIndex = 0,
        result = [];

    while (++index < length) {
      var value = array[index];
      if (value === placeholder || value === PLACEHOLDER) {
        array[index] = PLACEHOLDER;
        result[resIndex++] = index;
      }
    }
    return result;
  }

  /**
   * 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;
  }

  /**
   * Converts `set` to its value-value pairs.
   *
   * @private
   * @param {Object} set The set to convert.
   * @returns {Array} Returns the value-value pairs.
   */
  function setToPairs(set) {
    var index = -1,
        result = Array(set.size);

    set.forEach(function(value) {
      result[++index] = [value, value];
    });
    return result;
  }

  /**
   * A specialized version of `_.indexOf` which performs strict equality
   * comparisons of values, i.e. `===`.
   *
   * @private
   * @param {Array} array The array to inspect.
   * @param {*} value The value to search for.
   * @param {number} fromIndex The index to search from.
   * @returns {number} Returns the index of the matched value, else `-1`.
   */
  function strictIndexOf(array, value, fromIndex) {
    var index = fromIndex - 1,
        length = array.length;

    while (++index < length) {
      if (array[index] === value) {
        return index;
      }
    }
    return -1;
  }

  /**
   * A specialized version of `_.lastIndexOf` which performs strict equality
   * comparisons of values, i.e. `===`.
   *
   * @private
   * @param {Array} array The array to inspect.
   * @param {*} value The value to search for.
   * @param {number} fromIndex The index to search from.
   * @returns {number} Returns the index of the matched value, else `-1`.
   */
  function strictLastIndexOf(array, value, fromIndex) {
    var index = fromIndex + 1;
    while (index--) {
      if (array[index] === value) {
        return index;
      }
    }
    return index;
  }

  /**
   * Gets the number of symbols in `string`.
   *
   * @private
   * @param {string} string The string to inspect.
   * @returns {number} Returns the string size.
   */
  function stringSize(string) {
    return hasUnicode(string)
      ? unicodeSize(string)
      : asciiSize(string);
  }

  /**
   * Converts `string` to an array.
   *
   * @private
   * @param {string} string The string to convert.
   * @returns {Array} Returns the converted array.
   */
  function stringToArray(string) {
    return hasUnicode(string)
      ? unicodeToArray(string)
      : asciiToArray(string);
  }

  /**
   * Used by `_.unescape` to convert HTML entities to characters.
   *
   * @private
   * @param {string} chr The matched character to unescape.
   * @returns {string} Returns the unescaped character.
   */
  var unescapeHtmlChar = basePropertyOf(htmlUnescapes);

  /**
   * Gets the size of a Unicode `string`.
   *
   * @private
   * @param {string} string The string inspect.
   * @returns {number} Returns the string size.
   */
  function unicodeSize(string) {
    var result = reUnicode.lastIndex = 0;
    while (reUnicode.test(string)) {
      ++result;
    }
    return result;
  }

  /**
   * Converts a Unicode `string` to an array.
   *
   * @private
   * @param {string} string The string to convert.
   * @returns {Array} Returns the converted array.
   */
  function unicodeToArray(string) {
    return string.match(reUnicode) || [];
  }

  /**
   * Splits a Unicode `string` into an array of its words.
   *
   * @private
   * @param {string} The string to inspect.
   * @returns {Array} Returns the words of `string`.
   */
  function unicodeWords(string) {
    return string.match(reUnicodeWord) || [];
  }

  /*--------------------------------------------------------------------------*/

  /**
   * Create a new pristine `lodash` function using the `context` object.
   *
   * @static
   * @memberOf _
   * @since 1.1.0
   * @category Util
   * @param {Object} [context=root] The context object.
   * @returns {Function} Returns a new `lodash` function.
   * @example
   *
   * _.mixin({ 'foo': _.constant('foo') });
   *
   * var lodash = _.runInContext();
   * lodash.mixin({ 'bar': lodash.constant('bar') });
   *
   * _.isFunction(_.foo);
   * // => true
   * _.isFunction(_.bar);
   * // => false
   *
   * lodash.isFunction(lodash.foo);
   * // => false
   * lodash.isFunction(lodash.bar);
   * // => true
   *
   * // Create a suped-up `defer` in Node.js.
   * var defer = _.runInContext({ 'setTimeout': setImmediate }).defer;
   */
  var runInContext = (function runInContext(context) {
    context = context == null ? root : _.defaults(root.Object(), context, _.pick(root, contextProps));

    /** Built-in constructor references. */
    var Array = context.Array,
        Date = context.Date,
        Error = context.Error,
        Function = context.Function,
        Math = context.Math,
        Object = context.Object,
        RegExp = context.RegExp,
        String = context.String,
        TypeError = context.TypeError;

    /** 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 = context['__core-js_shared__'];

    /** 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 generate unique IDs. */
    var idCounter = 0;

    /** 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
     * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
     * of values.
     */
    var nativeObjectToString = objectProto.toString;

    /** Used to infer the `Object` constructor. */
    var objectCtorString = funcToString.call(Object);

    /** Used to restore the original `_` reference in `_.noConflict`. */
    var oldDash = root._;

    /** 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 Buffer = moduleExports ? context.Buffer : undefined,
        Symbol = context.Symbol,
        Uint8Array = context.Uint8Array,
        allocUnsafe = Buffer ? Buffer.allocUnsafe : undefined,
        getPrototype = overArg(Object.getPrototypeOf, Object),
        objectCreate = Object.create,
        propertyIsEnumerable = objectProto.propertyIsEnumerable,
        splice = arrayProto.splice,
        spreadableSymbol = Symbol ? Symbol.isConcatSpreadable : undefined,
        symIterator = Symbol ? Symbol.iterator : undefined,
        symToStringTag = Symbol ? Symbol.toStringTag : undefined;

    var defineProperty = (function() {
      try {
        var func = getNative(Object, 'defineProperty');
        func({}, '', {});
        return func;
      } catch (e) {}
    }());

    /** Mocked built-ins. */
    var ctxClearTimeout = context.clearTimeout !== root.clearTimeout && context.clearTimeout,
        ctxNow = Date && Date.now !== root.Date.now && Date.now,
        ctxSetTimeout = context.setTimeout !== root.setTimeout && context.setTimeout;

    /* Built-in method references for those with the same name as other `lodash` methods. */
    var nativeCeil = Math.ceil,
        nativeFloor = Math.floor,
        nativeGetSymbols = Object.getOwnPropertySymbols,
        nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined,
        nativeIsFinite = context.isFinite,
        nativeJoin = arrayProto.join,
        nativeKeys = overArg(Object.keys, Object),
        nativeMax = Math.max,
        nativeMin = Math.min,
        nativeNow = Date.now,
        nativeParseInt = context.parseInt,
        nativeRandom = Math.random,
        nativeReverse = arrayProto.reverse;

    /* Built-in method references that are verified to be native. */
    var DataView = getNative(context, 'DataView'),
        Map = getNative(context, 'Map'),
        Promise = getNative(context, 'Promise'),
        Set = getNative(context, 'Set'),
        WeakMap = getNative(context, 'WeakMap'),
        nativeCreate = getNative(Object, 'create');

    /** Used to store function metadata. */
    var metaMap = WeakMap && new WeakMap;

    /** Used to lookup unminified function names. */
    var realNames = {};

    /** 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 `lodash` object which wraps `value` to enable implicit method
     * chain sequences. Methods that operate on and return arrays, collections,
     * and functions can be chained together. Methods that retrieve a single value
     * or may return a primitive value will automatically end the chain sequence
     * and return the unwrapped value. Otherwise, the value must be unwrapped
     * with `_#value`.
     *
     * Explicit chain sequences, which must be unwrapped with `_#value`, may be
     * enabled using `_.chain`.
     *
     * The execution of chained methods is lazy, that is, it's deferred until
     * `_#value` is implicitly or explicitly called.
     *
     * Lazy evaluation allows several methods to support shortcut fusion.
     * Shortcut fusion is an optimization to merge iteratee calls; this avoids
     * the creation of intermediate arrays and can greatly reduce the number of
     * iteratee executions. Sections of a chain sequence qualify for shortcut
     * fusion if the section is applied to an array and iteratees accept only
     * one argument. The heuristic for whether a section qualifies for shortcut
     * fusion is subject to change.
     *
     * Chaining is supported in custom builds as long as the `_#value` method is
     * directly or indirectly included in the build.
     *
     * In addition to lodash methods, wrappers have `Array` and `String` methods.
     *
     * The wrapper `Array` methods are:
     * `concat`, `join`, `pop`, `push`, `shift`, `sort`, `splice`, and `unshift`
     *
     * The wrapper `String` methods are:
     * `replace` and `split`
     *
     * The wrapper methods that support shortcut fusion are:
     * `at`, `compact`, `drop`, `dropRight`, `dropWhile`, `filter`, `find`,
     * `findLast`, `head`, `initial`, `last`, `map`, `reject`, `reverse`, `slice`,
     * `tail`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, and `toArray`
     *
     * The chainable wrapper methods are:
     * `after`, `ary`, `assign`, `assignIn`, `assignInWith`, `assignWith`, `at`,
     * `before`, `bind`, `bindAll`, `bindKey`, `castArray`, `chain`, `chunk`,
     * `commit`, `compact`, `concat`, `conforms`, `constant`, `countBy`, `create`,
     * `curry`, `debounce`, `defaults`, `defaultsDeep`, `defer`, `delay`,
     * `difference`, `differenceBy`, `differenceWith`, `drop`, `dropRight`,
     * `dropRightWhile`, `dropWhile`, `extend`, `extendWith`, `fill`, `filter`,
     * `flatMap`, `flatMapDeep`, `flatMapDepth`, `flatten`, `flattenDeep`,
     * `flattenDepth`, `flip`, `flow`, `flowRight`, `fromPairs`, `functions`,
     * `functionsIn`, `groupBy`, `initial`, `intersection`, `intersectionBy`,
     * `intersectionWith`, `invert`, `invertBy`, `invokeMap`, `iteratee`, `keyBy`,
     * `keys`, `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`, `matchesProperty`,
     * `memoize`, `merge`, `mergeWith`, `method`, `methodOf`, `mixin`, `negate`,
     * `nthArg`, `omit`, `omitBy`, `once`, `orderBy`, `over`, `overArgs`,
     * `overEvery`, `overSome`, `partial`, `partialRight`, `partition`, `pick`,
     * `pickBy`, `plant`, `property`, `propertyOf`, `pull`, `pullAll`, `pullAllBy`,
     * `pullAllWith`, `pullAt`, `push`, `range`, `rangeRight`, `rearg`, `reject`,
     * `remove`, `rest`, `reverse`, `sampleSize`, `set`, `setWith`, `shuffle`,
     * `slice`, `sort`, `sortBy`, `splice`, `spread`, `tail`, `take`, `takeRight`,
     * `takeRightWhile`, `takeWhile`, `tap`, `throttle`, `thru`, `toArray`,
     * `toPairs`, `toPairsIn`, `toPath`, `toPlainObject`, `transform`, `unary`,
     * `union`, `unionBy`, `unionWith`, `uniq`, `uniqBy`, `uniqWith`, `unset`,
     * `unshift`, `unzip`, `unzipWith`, `update`, `updateWith`, `values`,
     * `valuesIn`, `without`, `wrap`, `xor`, `xorBy`, `xorWith`, `zip`,
     * `zipObject`, `zipObjectDeep`, and `zipWith`
     *
     * The wrapper methods that are **not** chainable by default are:
     * `add`, `attempt`, `camelCase`, `capitalize`, `ceil`, `clamp`, `clone`,
     * `cloneDeep`, `cloneDeepWith`, `cloneWith`, `conformsTo`, `deburr`,
     * `defaultTo`, `divide`, `each`, `eachRight`, `endsWith`, `eq`, `escape`,
     * `escapeRegExp`, `every`, `find`, `findIndex`, `findKey`, `findLast`,
     * `findLastIndex`, `findLastKey`, `first`, `floor`, `forEach`, `forEachRight`,
     * `forIn`, `forInRight`, `forOwn`, `forOwnRight`, `get`, `gt`, `gte`, `has`,
     * `hasIn`, `head`, `identity`, `includes`, `indexOf`, `inRange`, `invoke`,
     * `isArguments`, `isArray`, `isArrayBuffer`, `isArrayLike`, `isArrayLikeObject`,
     * `isBoolean`, `isBuffer`, `isDate`, `isElement`, `isEmpty`, `isEqual`,
     * `isEqualWith`, `isError`, `isFinite`, `isFunction`, `isInteger`, `isLength`,
     * `isMap`, `isMatch`, `isMatchWith`, `isNaN`, `isNative`, `isNil`, `isNull`,
     * `isNumber`, `isObject`, `isObjectLike`, `isPlainObject`, `isRegExp`,
     * `isSafeInteger`, `isSet`, `isString`, `isUndefined`, `isTypedArray`,
     * `isWeakMap`, `isWeakSet`, `join`, `kebabCase`, `last`, `lastIndexOf`,
     * `lowerCase`, `lowerFirst`, `lt`, `lte`, `max`, `maxBy`, `mean`, `meanBy`,
     * `min`, `minBy`, `multiply`, `noConflict`, `noop`, `now`, `nth`, `pad`,
     * `padEnd`, `padStart`, `parseInt`, `pop`, `random`, `reduce`, `reduceRight`,
     * `repeat`, `result`, `round`, `runInContext`, `sample`, `shift`, `size`,
     * `snakeCase`, `some`, `sortedIndex`, `sortedIndexBy`, `sortedLastIndex`,
     * `sortedLastIndexBy`, `startCase`, `startsWith`, `stubArray`, `stubFalse`,
     * `stubObject`, `stubString`, `stubTrue`, `subtract`, `sum`, `sumBy`,
     * `template`, `times`, `toFinite`, `toInteger`, `toJSON`, `toLength`,
     * `toLower`, `toNumber`, `toSafeInteger`, `toString`, `toUpper`, `trim`,
     * `trimEnd`, `trimStart`, `truncate`, `unescape`, `uniqueId`, `upperCase`,
     * `upperFirst`, `value`, and `words`
     *
     * @name _
     * @constructor
     * @category Seq
     * @param {*} value The value to wrap in a `lodash` instance.
     * @returns {Object} Returns the new `lodash` wrapper instance.
     * @example
     *
     * function square(n) {
     *   return n * n;
     * }
     *
     * var wrapped = _([1, 2, 3]);
     *
     * // Returns an unwrapped value.
     * wrapped.reduce(_.add);
     * // => 6
     *
     * // Returns a wrapped value.
     * var squares = wrapped.map(square);
     *
     * _.isArray(squares);
     * // => false
     *
     * _.isArray(squares.value());
     * // => true
     */
    function lodash(value) {
      if (isObjectLike(value) && !isArray(value) && !(value instanceof LazyWrapper)) {
        if (value instanceof LodashWrapper) {
          return value;
        }
        if (hasOwnProperty.call(value, '__wrapped__')) {
          return wrapperClone(value);
        }
      }
      return new LodashWrapper(value);
    }

    /**
     * The base implementation of `_.create` without support for assigning
     * properties to the created object.
     *
     * @private
     * @param {Object} proto The object to inherit from.
     * @returns {Object} Returns the new object.
     */
    var baseCreate = (function() {
      function object() {}
      return function(proto) {
        if (!isObject(proto)) {
          return {};
        }
        if (objectCreate) {
          return objectCreate(proto);
        }
        object.prototype = proto;
        var result = new object;
        object.prototype = undefined;
        return result;
      };
    }());

    /**
     * The function whose prototype chain sequence wrappers inherit from.
     *
     * @private
     */
    function baseLodash() {
      // No operation performed.
    }

    /**
     * The base constructor for creating `lodash` wrapper objects.
     *
     * @private
     * @param {*} value The value to wrap.
     * @param {boolean} [chainAll] Enable explicit method chain sequences.
     */
    function LodashWrapper(value, chainAll) {
      this.__wrapped__ = value;
      this.__actions__ = [];
      this.__chain__ = !!chainAll;
      this.__index__ = 0;
      this.__values__ = undefined;
    }

    /**
     * By default, the template delimiters used by lodash are like those in
     * embedded Ruby (ERB) as well as ES2015 template strings. Change the
     * following template settings to use alternative delimiters.
     *
     * @static
     * @memberOf _
     * @type {Object}
     */
    lodash.templateSettings = {

      /**
       * Used to detect `data` property values to be HTML-escaped.
       *
       * @memberOf _.templateSettings
       * @type {RegExp}
       */
      'escape': reEscape,

      /**
       * Used to detect code to be evaluated.
       *
       * @memberOf _.templateSettings
       * @type {RegExp}
       */
      'evaluate': reEvaluate,

      /**
       * Used to detect `data` property values to inject.
       *
       * @memberOf _.templateSettings
       * @type {RegExp}
       */
      'interpolate': reInterpolate,

      /**
       * Used to reference the data object in the template text.
       *
       * @memberOf _.templateSettings
       * @type {string}
       */
      'variable': '',

      /**
       * Used to import variables into the compiled template.
       *
       * @memberOf _.templateSettings
       * @type {Object}
       */
      'imports': {

        /**
         * A reference to the `lodash` function.
         *
         * @memberOf _.templateSettings.imports
         * @type {Function}
         */
        '_': lodash
      }
    };

    // Ensure wrappers are instances of `baseLodash`.
    lodash.prototype = baseLodash.prototype;
    lodash.prototype.constructor = lodash;

    LodashWrapper.prototype = baseCreate(baseLodash.prototype);
    LodashWrapper.prototype.constructor = LodashWrapper;

    /*------------------------------------------------------------------------*/

    /**
     * Creates a lazy wrapper object which wraps `value` to enable lazy evaluation.
     *
     * @private
     * @constructor
     * @param {*} value The value to wrap.
     */
    function LazyWrapper(value) {
      this.__wrapped__ = value;
      this.__actions__ = [];
      this.__dir__ = 1;
      this.__filtered__ = false;
      this.__iteratees__ = [];
      this.__takeCount__ = MAX_ARRAY_LENGTH;
      this.__views__ = [];
    }

    /**
     * Creates a clone of the lazy wrapper object.
     *
     * @private
     * @name clone
     * @memberOf LazyWrapper
     * @returns {Object} Returns the cloned `LazyWrapper` object.
     */
    function lazyClone() {
      var result = new LazyWrapper(this.__wrapped__);
      result.__actions__ = copyArray(this.__actions__);
      result.__dir__ = this.__dir__;
      result.__filtered__ = this.__filtered__;
      result.__iteratees__ = copyArray(this.__iteratees__);
      result.__takeCount__ = this.__takeCount__;
      result.__views__ = copyArray(this.__views__);
      return result;
    }

    /**
     * Reverses the direction of lazy iteration.
     *
     * @private
     * @name reverse
     * @memberOf LazyWrapper
     * @returns {Object} Returns the new reversed `LazyWrapper` object.
     */
    function lazyReverse() {
      if (this.__filtered__) {
        var result = new LazyWrapper(this);
        result.__dir__ = -1;
        result.__filtered__ = true;
      } else {
        result = this.clone();
        result.__dir__ *= -1;
      }
      return result;
    }

    /**
     * Extracts the unwrapped value from its lazy wrapper.
     *
     * @private
     * @name value
     * @memberOf LazyWrapper
     * @returns {*} Returns the unwrapped value.
     */
    function lazyValue() {
      var array = this.__wrapped__.value(),
          dir = this.__dir__,
          isArr = isArray(array),
          isRight = dir < 0,
          arrLength = isArr ? array.length : 0,
          view = getView(0, arrLength, this.__views__),
          start = view.start,
          end = view.end,
          length = end - start,
          index = isRight ? end : (start - 1),
          iteratees = this.__iteratees__,
          iterLength = iteratees.length,
          resIndex = 0,
          takeCount = nativeMin(length, this.__takeCount__);

      if (!isArr || (!isRight && arrLength == length && takeCount == length)) {
        return baseWrapperValue(array, this.__actions__);
      }
      var result = [];

      outer:
      while (length-- && resIndex < takeCount) {
        index += dir;

        var iterIndex = -1,
            value = array[index];

        while (++iterIndex < iterLength) {
          var data = iteratees[iterIndex],
              iteratee = data.iteratee,
              type = data.type,
              computed = iteratee(value);

          if (type == LAZY_MAP_FLAG) {
            value = computed;
          } else if (!computed) {
            if (type == LAZY_FILTER_FLAG) {
              continue outer;
            } else {
              break outer;
            }
          }
        }
        result[resIndex++] = value;
      }
      return result;
    }

    // Ensure `LazyWrapper` is an instance of `baseLodash`.
    LazyWrapper.prototype = baseCreate(baseLodash.prototype);
    LazyWrapper.prototype.constructor = LazyWrapper;

    /*------------------------------------------------------------------------*/

    /**
     * Creates a hash object.
     *
     * @private
     * @constructor
     * @param {Array} [entries] The key-value pairs to cache.
     */
    function Hash(entries) {
      var index = -1,
          length = entries == null ? 0 : entries.length;

      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) : {};
      this.size = 0;
    }

    /**
     * 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) {
      var result = this.has(key) && delete this.__data__[key];
      this.size -= result ? 1 : 0;
      return result;
    }

    /**
     * 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__;
      this.size += this.has(key) ? 0 : 1;
      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 == null ? 0 : entries.length;

      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__ = [];
      this.size = 0;
    }

    /**
     * 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);
      }
      --this.size;
      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) {
        ++this.size;
        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 == null ? 0 : entries.length;

      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.size = 0;
      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) {
      var result = getMapData(this, key)['delete'](key);
      this.size -= result ? 1 : 0;
      return result;
    }

    /**
     * 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) {
      var data = getMapData(this, key),
          size = data.size;

      data.set(key, value);
      this.size += data.size == size ? 0 : 1;
      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 == null ? 0 : values.length;

      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) {
      var data = this.__data__ = new ListCache(entries);
      this.size = data.size;
    }

    /**
     * Removes all key-value entries from the stack.
     *
     * @private
     * @name clear
     * @memberOf Stack
     */
    function stackClear() {
      this.__data__ = new ListCache;
      this.size = 0;
    }

    /**
     * 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) {
      var data = this.__data__,
          result = data['delete'](key);

      this.size = data.size;
      return result;
    }

    /**
     * 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 data = this.__data__;
      if (data instanceof ListCache) {
        var pairs = data.__data__;
        if (!Map || (pairs.length < LARGE_ARRAY_SIZE - 1)) {
          pairs.push([key, value]);
          this.size = ++data.size;
          return this;
        }
        data = this.__data__ = new MapCache(pairs);
      }
      data.set(key, value);
      this.size = data.size;
      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) {
      var isArr = isArray(value),
          isArg = !isArr && isArguments(value),
          isBuff = !isArr && !isArg && isBuffer(value),
          isType = !isArr && !isArg && !isBuff && isTypedArray(value),
          skipIndexes = isArr || isArg || isBuff || isType,
          result = skipIndexes ? baseTimes(value.length, String) : [],
          length = result.length;

      for (var key in value) {
        if ((inherited || hasOwnProperty.call(value, key)) &&
            !(skipIndexes && (
               // Safari 9 has enumerable `arguments.length` in strict mode.
               key == 'length' ||
               // Node.js 0.10 has enumerable non-index properties on buffers.
               (isBuff && (key == 'offset' || key == 'parent')) ||
               // PhantomJS 2 has enumerable non-index properties on typed arrays.
               (isType && (key == 'buffer' || key == 'byteLength' || key == 'byteOffset')) ||
               // Skip index properties.
               isIndex(key, length)
            ))) {
          result.push(key);
        }
      }
      return result;
    }

    /**
     * A specialized version of `_.sample` for arrays.
     *
     * @private
     * @param {Array} array The array to sample.
     * @returns {*} Returns the random element.
     */
    function arraySample(array) {
      var length = array.length;
      return length ? array[baseRandom(0, length - 1)] : undefined;
    }

    /**
     * A specialized version of `_.sampleSize` for arrays.
     *
     * @private
     * @param {Array} array The array to sample.
     * @param {number} n The number of elements to sample.
     * @returns {Array} Returns the random elements.
     */
    function arraySampleSize(array, n) {
      return shuffleSelf(copyArray(array), baseClamp(n, 0, array.length));
    }

    /**
     * A specialized version of `_.shuffle` for arrays.
     *
     * @private
     * @param {Array} array The array to shuffle.
     * @returns {Array} Returns the new shuffled array.
     */
    function arrayShuffle(array) {
      return shuffleSelf(copyArray(array));
    }

    /**
     * This function is like `assignValue` except that it doesn't assign
     * `undefined` values.
     *
     * @private
     * @param {Object} object The object to modify.
     * @param {string} key The key of the property to assign.
     * @param {*} value The value to assign.
     */
    function assignMergeValue(object, key, value) {
      if ((value !== undefined && !eq(object[key], value)) ||
          (value === undefined && !(key in object))) {
        baseAssignValue(object, key, value);
      }
    }

    /**
     * Assigns `value` to `key` of `object` if the existing value is not equivalent
     * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
     * for equality comparisons.
     *
     * @private
     * @param {Object} object The object to modify.
     * @param {string} key The key of the property to assign.
     * @param {*} value The value to assign.
     */
    function assignValue(object, key, value) {
      var objValue = object[key];
      if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) ||
          (value === undefined && !(key in object))) {
        baseAssignValue(object, key, value);
      }
    }

    /**
     * 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;
    }

    /**
     * Aggregates elements of `collection` on `accumulator` with keys transformed
     * by `iteratee` and values set by `setter`.
     *
     * @private
     * @param {Array|Object} collection The collection to iterate over.
     * @param {Function} setter The function to set `accumulator` values.
     * @param {Function} iteratee The iteratee to transform keys.
     * @param {Object} accumulator The initial aggregated object.
     * @returns {Function} Returns `accumulator`.
     */
    function baseAggregator(collection, setter, iteratee, accumulator) {
      baseEach(collection, function(value, key, collection) {
        setter(accumulator, value, iteratee(value), collection);
      });
      return accumulator;
    }

    /**
     * The base implementation of `_.assign` without support for multiple sources
     * or `customizer` functions.
     *
     * @private
     * @param {Object} object The destination object.
     * @param {Object} source The source object.
     * @returns {Object} Returns `object`.
     */
    function baseAssign(object, source) {
      return object && copyObject(source, keys(source), object);
    }

    /**
     * The base implementation of `_.assignIn` without support for multiple sources
     * or `customizer` functions.
     *
     * @private
     * @param {Object} object The destination object.
     * @param {Object} source The source object.
     * @returns {Object} Returns `object`.
     */
    function baseAssignIn(object, source) {
      return object && copyObject(source, keysIn(source), object);
    }

    /**
     * The base implementation of `assignValue` and `assignMergeValue` without
     * value checks.
     *
     * @private
     * @param {Object} object The object to modify.
     * @param {string} key The key of the property to assign.
     * @param {*} value The value to assign.
     */
    function baseAssignValue(object, key, value) {
      if (key == '__proto__' && defineProperty) {
        defineProperty(object, key, {
          'configurable': true,
          'enumerable': true,
          'value': value,
          'writable': true
        });
      } else {
        object[key] = value;
      }
    }

    /**
     * The base implementation of `_.at` without support for individual paths.
     *
     * @private
     * @param {Object} object The object to iterate over.
     * @param {string[]} paths The property paths to pick.
     * @returns {Array} Returns the picked elements.
     */
    function baseAt(object, paths) {
      var index = -1,
          length = paths.length,
          result = Array(length),
          skip = object == null;

      while (++index < length) {
        result[index] = skip ? undefined : get(object, paths[index]);
      }
      return result;
    }

    /**
     * The base implementation of `_.clamp` which doesn't coerce arguments.
     *
     * @private
     * @param {number} number The number to clamp.
     * @param {number} [lower] The lower bound.
     * @param {number} upper The upper bound.
     * @returns {number} Returns the clamped number.
     */
    function baseClamp(number, lower, upper) {
      if (number === number) {
        if (upper !== undefined) {
          number = number <= upper ? number : upper;
        }
        if (lower !== undefined) {
          number = number >= lower ? number : lower;
        }
      }
      return number;
    }

    /**
     * The base implementation of `_.clone` and `_.cloneDeep` which tracks
     * traversed objects.
     *
     * @private
     * @param {*} value The value to clone.
     * @param {boolean} bitmask The bitmask flags.
     *  1 - Deep clone
     *  2 - Flatten inherited properties
     *  4 - Clone symbols
     * @param {Function} [customizer] The function to customize cloning.
     * @param {string} [key] The key of `value`.
     * @param {Object} [object] The parent object of `value`.
     * @param {Object} [stack] Tracks traversed objects and their clone counterparts.
     * @returns {*} Returns the cloned value.
     */
    function baseClone(value, bitmask, customizer, key, object, stack) {
      var result,
          isDeep = bitmask & CLONE_DEEP_FLAG,
          isFlat = bitmask & CLONE_FLAT_FLAG,
          isFull = bitmask & CLONE_SYMBOLS_FLAG;

      if (customizer) {
        result = object ? customizer(value, key, object, stack) : customizer(value);
      }
      if (result !== undefined) {
        return result;
      }
      if (!isObject(value)) {
        return value;
      }
      var isArr = isArray(value);
      if (isArr) {
        result = initCloneArray(value);
        if (!isDeep) {
          return copyArray(value, result);
        }
      } else {
        var tag = getTag(value),
            isFunc = tag == funcTag || tag == genTag;

        if (isBuffer(value)) {
          return cloneBuffer(value, isDeep);
        }
        if (tag == objectTag || tag == argsTag || (isFunc && !object)) {
          result = (isFlat || isFunc) ? {} : initCloneObject(value);
          if (!isDeep) {
            return isFlat
              ? copySymbolsIn(value, baseAssignIn(result, value))
              : copySymbols(value, baseAssign(result, value));
          }
        } else {
          if (!cloneableTags[tag]) {
            return object ? value : {};
          }
          result = initCloneByTag(value, tag, baseClone, isDeep);
        }
      }
      // Check for circular references and return its corresponding clone.
      stack || (stack = new Stack);
      var stacked = stack.get(value);
      if (stacked) {
        return stacked;
      }
      stack.set(value, result);

      var keysFunc = isFull
        ? (isFlat ? getAllKeysIn : getAllKeys)
        : (isFlat ? keysIn : keys);

      var props = isArr ? undefined : keysFunc(value);
      arrayEach(props || value, function(subValue, key) {
        if (props) {
          key = subValue;
          subValue = value[key];
        }
        // Recursively populate clone (susceptible to call stack limits).
        assignValue(result, key, baseClone(subValue, bitmask, customizer, key, value, stack));
      });
      return result;
    }

    /**
     * The base implementation of `_.conforms` which doesn't clone `source`.
     *
     * @private
     * @param {Object} source The object of property predicates to conform to.
     * @returns {Function} Returns the new spec function.
     */
    function baseConforms(source) {
      var props = keys(source);
      return function(object) {
        return baseConformsTo(object, source, props);
      };
    }

    /**
     * The base implementation of `_.conformsTo` which accepts `props` to check.
     *
     * @private
     * @param {Object} object The object to inspect.
     * @param {Object} source The object of property predicates to conform to.
     * @returns {boolean} Returns `true` if `object` conforms, else `false`.
     */
    function baseConformsTo(object, source, props) {
      var length = props.length;
      if (object == null) {
        return !length;
      }
      object = Object(object);
      while (length--) {
        var key = props[length],
            predicate = source[key],
            value = object[key];

        if ((value === undefined && !(key in object)) || !predicate(value)) {
          return false;
        }
      }
      return true;
    }

    /**
     * The base implementation of `_.delay` and `_.defer` which accepts `args`
     * to provide to `func`.
     *
     * @private
     * @param {Function} func The function to delay.
     * @param {number} wait The number of milliseconds to delay invocation.
     * @param {Array} args The arguments to provide to `func`.
     * @returns {number|Object} Returns the timer id or timeout object.
     */
    function baseDelay(func, wait, args) {
      if (typeof func != 'function') {
        throw new TypeError(FUNC_ERROR_TEXT);
      }
      return setTimeout(function() { func.apply(undefined, args); }, wait);
    }

    /**
     * The base implementation of methods like `_.difference` without support
     * for excluding multiple arrays or iteratee shorthands.
     *
     * @private
     * @param {Array} array The array to inspect.
     * @param {Array} values The values to exclude.
     * @param {Function} [iteratee] The iteratee invoked per element.
     * @param {Function} [comparator] The comparator invoked per element.
     * @returns {Array} Returns the new array of filtered values.
     */
    function baseDifference(array, values, iteratee, comparator) {
      var index = -1,
          includes = arrayIncludes,
          isCommon = true,
          length = array.length,
          result = [],
          valuesLength = values.length;

      if (!length) {
        return result;
      }
      if (iteratee) {
        values = arrayMap(values, baseUnary(iteratee));
      }
      if (comparator) {
        includes = arrayIncludesWith;
        isCommon = false;
      }
      else if (values.length >= LARGE_ARRAY_SIZE) {
        includes = cacheHas;
        isCommon = false;
        values = new SetCache(values);
      }
      outer:
      while (++index < length) {
        var value = array[index],
            computed = iteratee == null ? value : iteratee(value);

        value = (comparator || value !== 0) ? value : 0;
        if (isCommon && computed === computed) {
          var valuesIndex = valuesLength;
          while (valuesIndex--) {
            if (values[valuesIndex] === computed) {
              continue outer;
            }
          }
          result.push(value);
        }
        else if (!includes(values, computed, comparator)) {
          result.push(value);
        }
      }
      return result;
    }

    /**
     * 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 `_.forEachRight` 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 baseEachRight = createBaseEach(baseForOwnRight, true);

    /**
     * The base implementation of `_.every` without support for iteratee shorthands.
     *
     * @private
     * @param {Array|Object} collection The collection to iterate over.
     * @param {Function} predicate The function invoked per iteration.
     * @returns {boolean} Returns `true` if all elements pass the predicate check,
     *  else `false`
     */
    function baseEvery(collection, predicate) {
      var result = true;
      baseEach(collection, function(value, index, collection) {
        result = !!predicate(value, index, collection);
        return result;
      });
      return result;
    }

    /**
     * The base implementation of methods like `_.max` and `_.min` which accepts a
     * `comparator` to determine the extremum value.
     *
     * @private
     * @param {Array} array The array to iterate over.
     * @param {Function} iteratee The iteratee invoked per iteration.
     * @param {Function} comparator The comparator used to compare values.
     * @returns {*} Returns the extremum value.
     */
    function baseExtremum(array, iteratee, comparator) {
      var index = -1,
          length = array.length;

      while (++index < length) {
        var value = array[index],
            current = iteratee(value);

        if (current != null && (computed === undefined
              ? (current === current && !isSymbol(current))
              : comparator(current, computed)
            )) {
          var computed = current,
              result = value;
        }
      }
      return result;
    }

    /**
     * The base implementation of `_.fill` without an iteratee call guard.
     *
     * @private
     * @param {Array} array The array to fill.
     * @param {*} value The value to fill `array` with.
     * @param {number} [start=0] The start position.
     * @param {number} [end=array.length] The end position.
     * @returns {Array} Returns `array`.
     */
    function baseFill(array, value, start, end) {
      var length = array.length;

      start = toInteger(start);
      if (start < 0) {
        start = -start > length ? 0 : (length + start);
      }
      end = (end === undefined || end > length) ? length : toInteger(end);
      if (end < 0) {
        end += length;
      }
      end = start > end ? 0 : toLength(end);
      while (start < end) {
        array[start++] = value;
      }
      return array;
    }

    /**
     * The base implementation of `_.filter` without support for iteratee shorthands.
     *
     * @private
     * @param {Array|Object} collection The collection to iterate over.
     * @param {Function} predicate The function invoked per iteration.
     * @returns {Array} Returns the new filtered array.
     */
    function baseFilter(collection, predicate) {
      var result = [];
      baseEach(collection, function(value, index, collection) {
        if (predicate(value, index, collection)) {
          result.push(value);
        }
      });
      return result;
    }

    /**
     * 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();

    /**
     * This function is like `baseFor` except that it iterates over properties
     * in the opposite order.
     *
     * @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 baseForRight = createBaseFor(true);

    /**
     * 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 `_.forOwnRight` 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 baseForOwnRight(object, iteratee) {
      return object && baseForRight(object, iteratee, keys);
    }

    /**
     * The base implementation of `_.functions` which creates an array of
     * `object` function property names filtered from `props`.
     *
     * @private
     * @param {Object} object The object to inspect.
     * @param {Array} props The property names to filter.
     * @returns {Array} Returns the function names.
     */
    function baseFunctions(object, props) {
      return arrayFilter(props, function(key) {
        return isFunction(object[key]);
      });
    }

    /**
     * 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 = castPath(path, object);

      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 `getAllKeys` and `getAllKeysIn` which uses
     * `keysFunc` and `symbolsFunc` to get the enumerable property names and
     * symbols of `object`.
     *
     * @private
     * @param {Object} object The object to query.
     * @param {Function} keysFunc The function to get the keys of `object`.
     * @param {Function} symbolsFunc The function to get the symbols of `object`.
     * @returns {Array} Returns the array of property names and symbols.
     */
    function baseGetAllKeys(object, keysFunc, symbolsFunc) {
      var result = keysFunc(object);
      return isArray(object) ? result : arrayPush(result, symbolsFunc(object));
    }

    /**
     * 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 && symToStringTag in Object(value))
        ? getRawTag(value)
        : objectToString(value);
    }

    /**
     * The base implementation of `_.gt` which doesn't coerce arguments.
     *
     * @private
     * @param {*} value The value to compare.
     * @param {*} other The other value to compare.
     * @returns {boolean} Returns `true` if `value` is greater than `other`,
     *  else `false`.
     */
    function baseGt(value, other) {
      return value > other;
    }

    /**
     * The base implementation of `_.has` 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 baseHas(object, key) {
      return object != null && hasOwnProperty.call(object, key);
    }

    /**
     * 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 `_.inRange` which doesn't coerce arguments.
     *
     * @private
     * @param {number} number The number to check.
     * @param {number} start The start of the range.
     * @param {number} end The end of the range.
     * @returns {boolean} Returns `true` if `number` is in the range, else `false`.
     */
    function baseInRange(number, start, end) {
      return number >= nativeMin(start, end) && number < nativeMax(start, end);
    }

    /**
     * The base implementation of methods like `_.intersection`, without support
     * for iteratee shorthands, that accepts an array of arrays to inspect.
     *
     * @private
     * @param {Array} arrays The arrays to inspect.
     * @param {Function} [iteratee] The iteratee invoked per element.
     * @param {Function} [comparator] The comparator invoked per element.
     * @returns {Array} Returns the new array of shared values.
     */
    function baseIntersection(arrays, iteratee, comparator) {
      var includes = comparator ? arrayIncludesWith : arrayIncludes,
          length = arrays[0].length,
          othLength = arrays.length,
          othIndex = othLength,
          caches = Array(othLength),
          maxLength = Infinity,
          result = [];

      while (othIndex--) {
        var array = arrays[othIndex];
        if (othIndex && iteratee) {
          array = arrayMap(array, baseUnary(iteratee));
        }
        maxLength = nativeMin(array.length, maxLength);
        caches[othIndex] = !comparator && (iteratee || (length >= 120 && array.length >= 120))
          ? new SetCache(othIndex && array)
          : undefined;
      }
      array = arrays[0];

      var index = -1,
          seen = caches[0];

      outer:
      while (++index < length && result.length < maxLength) {
        var value = array[index],
            computed = iteratee ? iteratee(value) : value;

        value = (comparator || value !== 0) ? value : 0;
        if (!(seen
              ? cacheHas(seen, computed)
              : includes(result, computed, comparator)
            )) {
          othIndex = othLength;
          while (--othIndex) {
            var cache = caches[othIndex];
            if (!(cache
                  ? cacheHas(cache, computed)
                  : includes(arrays[othIndex], computed, comparator))
                ) {
              continue outer;
            }
          }
          if (seen) {
            seen.push(computed);
          }
          result.push(value);
        }
      }
      return result;
    }

    /**
     * The base implementation of `_.invert` and `_.invertBy` which inverts
     * `object` with values transformed by `iteratee` and set by `setter`.
     *
     * @private
     * @param {Object} object The object to iterate over.
     * @param {Function} setter The function to set `accumulator` values.
     * @param {Function} iteratee The iteratee to transform values.
     * @param {Object} accumulator The initial inverted object.
     * @returns {Function} Returns `accumulator`.
     */
    function baseInverter(object, setter, iteratee, accumulator) {
      baseForOwn(object, function(value, key, object) {
        setter(accumulator, iteratee(value), key, object);
      });
      return accumulator;
    }

    /**
     * The base implementation of `_.invoke` without support for individual
     * method arguments.
     *
     * @private
     * @param {Object} object The object to query.
     * @param {Array|string} path The path of the method to invoke.
     * @param {Array} args The arguments to invoke the method with.
     * @returns {*} Returns the result of the invoked method.
     */
    function baseInvoke(object, path, args) {
      path = castPath(path, object);
      object = parent(object, path);
      var func = object == null ? object : object[toKey(last(path))];
      return func == null ? undefined : apply(func, object, args);
    }

    /**
     * The base implementation of `_.isArguments`.
     *
     * @private
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is an `arguments` object,
     */
    function baseIsArguments(value) {
      return isObjectLike(value) && baseGetTag(value) == argsTag;
    }

    /**
     * The base implementation of `_.isArrayBuffer` without Node.js optimizations.
     *
     * @private
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is an array buffer, else `false`.
     */
    function baseIsArrayBuffer(value) {
      return isObjectLike(value) && baseGetTag(value) == arrayBufferTag;
    }

    /**
     * The base implementation of `_.isDate` without Node.js optimizations.
     *
     * @private
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is a date object, else `false`.
     */
    function baseIsDate(value) {
      return isObjectLike(value) && baseGetTag(value) == dateTag;
    }

    /**
     * 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 {boolean} bitmask The bitmask flags.
     *  1 - Unordered comparison
     *  2 - Partial comparison
     * @param {Function} [customizer] The function to customize comparisons.
     * @param {Object} [stack] Tracks traversed `value` and `other` objects.
     * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
     */
    function baseIsEqual(value, other, bitmask, customizer, stack) {
      if (value === other) {
        return true;
      }
      if (value == null || other == null || (!isObjectLike(value) && !isObjectLike(other))) {
        return value !== value && other !== other;
      }
      return baseIsEqualDeep(value, other, bitmask, customizer, baseIsEqual, 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 {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
     * @param {Function} customizer The function to customize comparisons.
     * @param {Function} equalFunc The function to determine equivalents of values.
     * @param {Object} [stack] Tracks traversed `object` and `other` objects.
     * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
     */
    function baseIsEqualDeep(object, other, bitmask, customizer, equalFunc, stack) {
      var objIsArr = isArray(object),
          othIsArr = isArray(other),
          objTag = objIsArr ? arrayTag : getTag(object),
          othTag = othIsArr ? arrayTag : getTag(other);

      objTag = objTag == argsTag ? objectTag : objTag;
      othTag = othTag == argsTag ? objectTag : othTag;

      var objIsObj = objTag == objectTag,
          othIsObj = othTag == objectTag,
          isSameTag = objTag == othTag;

      if (isSameTag && isBuffer(object)) {
        if (!isBuffer(other)) {
          return false;
        }
        objIsArr = true;
        objIsObj = false;
      }
      if (isSameTag && !objIsObj) {
        stack || (stack = new Stack);
        return (objIsArr || isTypedArray(object))
          ? equalArrays(object, other, bitmask, customizer, equalFunc, stack)
          : equalByTag(object, other, objTag, bitmask, customizer, equalFunc, stack);
      }
      if (!(bitmask & COMPARE_PARTIAL_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, bitmask, customizer, stack);
        }
      }
      if (!isSameTag) {
        return false;
      }
      stack || (stack = new Stack);
      return equalObjects(object, other, bitmask, customizer, equalFunc, stack);
    }

    /**
     * The base implementation of `_.isMap` without Node.js optimizations.
     *
     * @private
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is a map, else `false`.
     */
    function baseIsMap(value) {
      return isObjectLike(value) && getTag(value) == mapTag;
    }

    /**
     * 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, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG, customizer, 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) ? reIsNative : reIsHostCtor;
      return pattern.test(toSource(value));
    }

    /**
     * The base implementation of `_.isRegExp` without Node.js optimizations.
     *
     * @private
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is a regexp, else `false`.
     */
    function baseIsRegExp(value) {
      return isObjectLike(value) && baseGetTag(value) == regexpTag;
    }

    /**
     * The base implementation of `_.isSet` without Node.js optimizations.
     *
     * @private
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is a set, else `false`.
     */
    function baseIsSet(value) {
      return isObjectLike(value) && getTag(value) == setTag;
    }

    /**
     * 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[baseGetTag(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 `_.keysIn` 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 baseKeysIn(object) {
      if (!isObject(object)) {
        return nativeKeysIn(object);
      }
      var isProto = isPrototype(object),
          result = [];

      for (var key in object) {
        if (!(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) {
          result.push(key);
        }
      }
      return result;
    }

    /**
     * The base implementation of `_.lt` which doesn't coerce arguments.
     *
     * @private
     * @param {*} value The value to compare.
     * @param {*} other The other value to compare.
     * @returns {boolean} Returns `true` if `value` is less than `other`,
     *  else `false`.
     */
    function baseLt(value, other) {
      return value < other;
    }

    /**
     * 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, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG);
      };
    }

    /**
     * The base implementation of `_.merge` without support for multiple sources.
     *
     * @private
     * @param {Object} object The destination object.
     * @param {Object} source The source object.
     * @param {number} srcIndex The index of `source`.
     * @param {Function} [customizer] The function to customize merged values.
     * @param {Object} [stack] Tracks traversed source values and their merged
     *  counterparts.
     */
    function baseMerge(object, source, srcIndex, customizer, stack) {
      if (object === source) {
        return;
      }
      baseFor(source, function(srcValue, key) {
        if (isObject(srcValue)) {
          stack || (stack = new Stack);
          baseMergeDeep(object, source, key, srcIndex, baseMerge, customizer, stack);
        }
        else {
          var newValue = customizer
            ? customizer(object[key], srcValue, (key + ''), object, source, stack)
            : undefined;

          if (newValue === undefined) {
            newValue = srcValue;
          }
          assignMergeValue(object, key, newValue);
        }
      }, keysIn);
    }

    /**
     * A specialized version of `baseMerge` for arrays and objects which performs
     * deep merges and tracks traversed objects enabling objects with circular
     * references to be merged.
     *
     * @private
     * @param {Object} object The destination object.
     * @param {Object} source The source object.
     * @param {string} key The key of the value to merge.
     * @param {number} srcIndex The index of `source`.
     * @param {Function} mergeFunc The function to merge values.
     * @param {Function} [customizer] The function to customize assigned values.
     * @param {Object} [stack] Tracks traversed source values and their merged
     *  counterparts.
     */
    function baseMergeDeep(object, source, key, srcIndex, mergeFunc, customizer, stack) {
      var objValue = object[key],
          srcValue = source[key],
          stacked = stack.get(srcValue);

      if (stacked) {
        assignMergeValue(object, key, stacked);
        return;
      }
      var newValue = customizer
        ? customizer(objValue, srcValue, (key + ''), object, source, stack)
        : undefined;

      var isCommon = newValue === undefined;

      if (isCommon) {
        var isArr = isArray(srcValue),
            isBuff = !isArr && isBuffer(srcValue),
            isTyped = !isArr && !isBuff && isTypedArray(srcValue);

        newValue = srcValue;
        if (isArr || isBuff || isTyped) {
          if (isArray(objValue)) {
            newValue = objValue;
          }
          else if (isArrayLikeObject(objValue)) {
            newValue = copyArray(objValue);
          }
          else if (isBuff) {
            isCommon = false;
            newValue = cloneBuffer(srcValue, true);
          }
          else if (isTyped) {
            isCommon = false;
            newValue = cloneTypedArray(srcValue, true);
          }
          else {
            newValue = [];
          }
        }
        else if (isPlainObject(srcValue) || isArguments(srcValue)) {
          newValue = objValue;
          if (isArguments(objValue)) {
            newValue = toPlainObject(objValue);
          }
          else if (!isObject(objValue) || (srcIndex && isFunction(objValue))) {
            newValue = initCloneObject(srcValue);
          }
        }
        else {
          isCommon = false;
        }
      }
      if (isCommon) {
        // Recursively merge objects and arrays (susceptible to call stack limits).
        stack.set(srcValue, newValue);
        mergeFunc(newValue, srcValue, srcIndex, customizer, stack);
        stack['delete'](srcValue);
      }
      assignMergeValue(object, key, newValue);
    }

    /**
     * The base implementation of `_.nth` which doesn't coerce arguments.
     *
     * @private
     * @param {Array} array The array to query.
     * @param {number} n The index of the element to return.
     * @returns {*} Returns the nth element of `array`.
     */
    function baseNth(array, n) {
      var length = array.length;
      if (!length) {
        return;
      }
      n += n < 0 ? length : 0;
      return isIndex(n, length) ? array[n] : undefined;
    }

    /**
     * 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(getIteratee()));

      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);
      });
    }

    /**
     * The base implementation of `_.pick` without support for individual
     * property identifiers.
     *
     * @private
     * @param {Object} object The source object.
     * @param {string[]} paths The property paths to pick.
     * @returns {Object} Returns the new object.
     */
    function basePick(object, paths) {
      return basePickBy(object, paths, function(value, path) {
        return hasIn(object, path);
      });
    }

    /**
     * The base implementation of  `_.pickBy` without support for iteratee shorthands.
     *
     * @private
     * @param {Object} object The source object.
     * @param {string[]} paths The property paths to pick.
     * @param {Function} predicate The function invoked per property.
     * @returns {Object} Returns the new object.
     */
    function basePickBy(object, paths, predicate) {
      var index = -1,
          length = paths.length,
          result = {};

      while (++index < length) {
        var path = paths[index],
            value = baseGet(object, path);

        if (predicate(value, path)) {
          baseSet(result, castPath(path, object), value);
        }
      }
      return result;
    }

    /**
     * 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 `_.pullAllBy` without support for iteratee
     * shorthands.
     *
     * @private
     * @param {Array} array The array to modify.
     * @param {Array} values The values to remove.
     * @param {Function} [iteratee] The iteratee invoked per element.
     * @param {Function} [comparator] The comparator invoked per element.
     * @returns {Array} Returns `array`.
     */
    function basePullAll(array, values, iteratee, comparator) {
      var indexOf = comparator ? baseIndexOfWith : baseIndexOf,
          index = -1,
          length = values.length,
          seen = array;

      if (array === values) {
        values = copyArray(values);
      }
      if (iteratee) {
        seen = arrayMap(array, baseUnary(iteratee));
      }
      while (++index < length) {
        var fromIndex = 0,
            value = values[index],
            computed = iteratee ? iteratee(value) : value;

        while ((fromIndex = indexOf(seen, computed, fromIndex, comparator)) > -1) {
          if (seen !== array) {
            splice.call(seen, fromIndex, 1);
          }
          splice.call(array, fromIndex, 1);
        }
      }
      return array;
    }

    /**
     * The base implementation of `_.pullAt` without support for individual
     * indexes or capturing the removed elements.
     *
     * @private
     * @param {Array} array The array to modify.
     * @param {number[]} indexes The indexes of elements to remove.
     * @returns {Array} Returns `array`.
     */
    function basePullAt(array, indexes) {
      var length = array ? indexes.length : 0,
          lastIndex = length - 1;

      while (length--) {
        var index = indexes[length];
        if (length == lastIndex || index !== previous) {
          var previous = index;
          if (isIndex(index)) {
            splice.call(array, index, 1);
          } else {
            baseUnset(array, index);
          }
        }
      }
      return array;
    }

    /**
     * The base implementation of `_.random` without support for returning
     * floating-point numbers.
     *
     * @private
     * @param {number} lower The lower bound.
     * @param {number} upper The upper bound.
     * @returns {number} Returns the random number.
     */
    function baseRandom(lower, upper) {
      return lower + nativeFloor(nativeRandom() * (upper - lower + 1));
    }

    /**
     * The base implementation of `_.range` and `_.rangeRight` which doesn't
     * coerce arguments.
     *
     * @private
     * @param {number} start The start of the range.
     * @param {number} end The end of the range.
     * @param {number} step The value to increment or decrement by.
     * @param {boolean} [fromRight] Specify iterating from right to left.
     * @returns {Array} Returns the range of numbers.
     */
    function baseRange(start, end, step, fromRight) {
      var index = -1,
          length = nativeMax(nativeCeil((end - start) / (step || 1)), 0),
          result = Array(length);

      while (length--) {
        result[fromRight ? length : ++index] = start;
        start += step;
      }
      return result;
    }

    /**
     * The base implementation of `_.repeat` which doesn't coerce arguments.
     *
     * @private
     * @param {string} string The string to repeat.
     * @param {number} n The number of times to repeat the string.
     * @returns {string} Returns the repeated string.
     */
    function baseRepeat(string, n) {
      var result = '';
      if (!string || n < 1 || n > MAX_SAFE_INTEGER) {
        return result;
      }
      // Leverage the exponentiation by squaring algorithm for a faster repeat.
      // See https://en.wikipedia.org/wiki/Exponentiation_by_squaring for more details.
      do {
        if (n % 2) {
          result += string;
        }
        n = nativeFloor(n / 2);
        if (n) {
          string += string;
        }
      } while (n);

      return result;
    }

    /**
     * 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) {
      return setToString(overRest(func, start, identity), func + '');
    }

    /**
     * The base implementation of `_.sample`.
     *
     * @private
     * @param {Array|Object} collection The collection to sample.
     * @returns {*} Returns the random element.
     */
    function baseSample(collection) {
      return arraySample(values(collection));
    }

    /**
     * The base implementation of `_.sampleSize` without param guards.
     *
     * @private
     * @param {Array|Object} collection The collection to sample.
     * @param {number} n The number of elements to sample.
     * @returns {Array} Returns the random elements.
     */
    function baseSampleSize(collection, n) {
      var array = values(collection);
      return shuffleSelf(array, baseClamp(n, 0, array.length));
    }

    /**
     * The base implementation of `_.set`.
     *
     * @private
     * @param {Object} object The object to modify.
     * @param {Array|string} path The path of the property to set.
     * @param {*} value The value to set.
     * @param {Function} [customizer] The function to customize path creation.
     * @returns {Object} Returns `object`.
     */
    function baseSet(object, path, value, customizer) {
      if (!isObject(object)) {
        return object;
      }
      path = castPath(path, object);

      var index = -1,
          length = path.length,
          lastIndex = length - 1,
          nested = object;

      while (nested != null && ++index < length) {
        var key = toKey(path[index]),
            newValue = value;

        if (index != lastIndex) {
          var objValue = nested[key];
          newValue = customizer ? customizer(objValue, key, nested) : undefined;
          if (newValue === undefined) {
            newValue = isObject(objValue)
              ? objValue
              : (isIndex(path[index + 1]) ? [] : {});
          }
        }
        assignValue(nested, key, newValue);
        nested = nested[key];
      }
      return object;
    }

    /**
     * The base implementation of `setData` without support for hot loop shorting.
     *
     * @private
     * @param {Function} func The function to associate metadata with.
     * @param {*} data The metadata.
     * @returns {Function} Returns `func`.
     */
    var baseSetData = !metaMap ? identity : function(func, data) {
      metaMap.set(func, data);
      return func;
    };

    /**
     * The base implementation of `setToString` without support for hot loop shorting.
     *
     * @private
     * @param {Function} func The function to modify.
     * @param {Function} string The `toString` result.
     * @returns {Function} Returns `func`.
     */
    var baseSetToString = !defineProperty ? identity : function(func, string) {
      return defineProperty(func, 'toString', {
        'configurable': true,
        'enumerable': false,
        'value': constant(string),
        'writable': true
      });
    };

    /**
     * The base implementation of `_.shuffle`.
     *
     * @private
     * @param {Array|Object} collection The collection to shuffle.
     * @returns {Array} Returns the new shuffled array.
     */
    function baseShuffle(collection) {
      return shuffleSelf(values(collection));
    }

    /**
     * The base implementation of `_.slice` without an iteratee call guard.
     *
     * @private
     * @param {Array} array The array to slice.
     * @param {number} [start=0] The start position.
     * @param {number} [end=array.length] The end position.
     * @returns {Array} Returns the slice of `array`.
     */
    function baseSlice(array, start, end) {
      var index = -1,
          length = array.length;

      if (start < 0) {
        start = -start > length ? 0 : (length + start);
      }
      end = end > length ? length : end;
      if (end < 0) {
        end += length;
      }
      length = start > end ? 0 : ((end - start) >>> 0);
      start >>>= 0;

      var result = Array(length);
      while (++index < length) {
        result[index] = array[index + start];
      }
      return result;
    }

    /**
     * The base implementation of `_.some` without support for iteratee shorthands.
     *
     * @private
     * @param {Array|Object} collection The collection 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 baseSome(collection, predicate) {
      var result;

      baseEach(collection, function(value, index, collection) {
        result = predicate(value, index, collection);
        return !result;
      });
      return !!result;
    }

    /**
     * The base implementation of `_.sortedIndex` and `_.sortedLastIndex` which
     * performs a binary search of `array` to determine the index at which `value`
     * should be inserted into `array` in order to maintain its sort order.
     *
     * @private
     * @param {Array} array The sorted array to inspect.
     * @param {*} value The value to evaluate.
     * @param {boolean} [retHighest] Specify returning the highest qualified index.
     * @returns {number} Returns the index at which `value` should be inserted
     *  into `array`.
     */
    function baseSortedIndex(array, value, retHighest) {
      var low = 0,
          high = array == null ? low : array.length;

      if (typeof value == 'number' && value === value && high <= HALF_MAX_ARRAY_LENGTH) {
        while (low < high) {
          var mid = (low + high) >>> 1,
              computed = array[mid];

          if (computed !== null && !isSymbol(computed) &&
              (retHighest ? (computed <= value) : (computed < value))) {
            low = mid + 1;
          } else {
            high = mid;
          }
        }
        return high;
      }
      return baseSortedIndexBy(array, value, identity, retHighest);
    }

    /**
     * The base implementation of `_.sortedIndexBy` and `_.sortedLastIndexBy`
     * which invokes `iteratee` for `value` and each element of `array` to compute
     * their sort ranking. The iteratee is invoked with one argument; (value).
     *
     * @private
     * @param {Array} array The sorted array to inspect.
     * @param {*} value The value to evaluate.
     * @param {Function} iteratee The iteratee invoked per element.
     * @param {boolean} [retHighest] Specify returning the highest qualified index.
     * @returns {number} Returns the index at which `value` should be inserted
     *  into `array`.
     */
    function baseSortedIndexBy(array, value, iteratee, retHighest) {
      value = iteratee(value);

      var low = 0,
          high = array == null ? 0 : array.length,
          valIsNaN = value !== value,
          valIsNull = value === null,
          valIsSymbol = isSymbol(value),
          valIsUndefined = value === undefined;

      while (low < high) {
        var mid = nativeFloor((low + high) / 2),
            computed = iteratee(array[mid]),
            othIsDefined = computed !== undefined,
            othIsNull = computed === null,
            othIsReflexive = computed === computed,
            othIsSymbol = isSymbol(computed);

        if (valIsNaN) {
          var setLow = retHighest || othIsReflexive;
        } else if (valIsUndefined) {
          setLow = othIsReflexive && (retHighest || othIsDefined);
        } else if (valIsNull) {
          setLow = othIsReflexive && othIsDefined && (retHighest || !othIsNull);
        } else if (valIsSymbol) {
          setLow = othIsReflexive && othIsDefined && !othIsNull && (retHighest || !othIsSymbol);
        } else if (othIsNull || othIsSymbol) {
          setLow = false;
        } else {
          setLow = retHighest ? (computed <= value) : (computed < value);
        }
        if (setLow) {
          low = mid + 1;
        } else {
          high = mid;
        }
      }
      return nativeMin(high, MAX_ARRAY_INDEX);
    }

    /**
     * The base implementation of `_.sortedUniq` and `_.sortedUniqBy` without
     * support for iteratee shorthands.
     *
     * @private
     * @param {Array} array The array to inspect.
     * @param {Function} [iteratee] The iteratee invoked per element.
     * @returns {Array} Returns the new duplicate free array.
     */
    function baseSortedUniq(array, iteratee) {
      var index = -1,
          length = array.length,
          resIndex = 0,
          result = [];

      while (++index < length) {
        var value = array[index],
            computed = iteratee ? iteratee(value) : value;

        if (!index || !eq(computed, seen)) {
          var seen = computed;
          result[resIndex++] = value === 0 ? 0 : value;
        }
      }
      return result;
    }

    /**
     * The base implementation of `_.toNumber` which doesn't ensure correct
     * conversions of binary, hexadecimal, or octal string values.
     *
     * @private
     * @param {*} value The value to process.
     * @returns {number} Returns the number.
     */
    function baseToNumber(value) {
      if (typeof value == 'number') {
        return value;
      }
      if (isSymbol(value)) {
        return NAN;
      }
      return +value;
    }

    /**
     * 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 (isArray(value)) {
        // Recursively convert values (susceptible to call stack limits).
        return arrayMap(value, baseToString) + '';
      }
      if (isSymbol(value)) {
        return symbolToString ? symbolToString.call(value) : '';
      }
      var result = (value + '');
      return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
    }

    /**
     * The base implementation of `_.uniqBy` without support for iteratee shorthands.
     *
     * @private
     * @param {Array} array The array to inspect.
     * @param {Function} [iteratee] The iteratee invoked per element.
     * @param {Function} [comparator] The comparator invoked per element.
     * @returns {Array} Returns the new duplicate free array.
     */
    function baseUniq(array, iteratee, comparator) {
      var index = -1,
          includes = arrayIncludes,
          length = array.length,
          isCommon = true,
          result = [],
          seen = result;

      if (comparator) {
        isCommon = false;
        includes = arrayIncludesWith;
      }
      else if (length >= LARGE_ARRAY_SIZE) {
        var set = iteratee ? null : createSet(array);
        if (set) {
          return setToArray(set);
        }
        isCommon = false;
        includes = cacheHas;
        seen = new SetCache;
      }
      else {
        seen = iteratee ? [] : result;
      }
      outer:
      while (++index < length) {
        var value = array[index],
            computed = iteratee ? iteratee(value) : value;

        value = (comparator || value !== 0) ? value : 0;
        if (isCommon && computed === computed) {
          var seenIndex = seen.length;
          while (seenIndex--) {
            if (seen[seenIndex] === computed) {
              continue outer;
            }
          }
          if (iteratee) {
            seen.push(computed);
          }
          result.push(value);
        }
        else if (!includes(seen, computed, comparator)) {
          if (seen !== result) {
            seen.push(computed);
          }
          result.push(value);
        }
      }
      return result;
    }

    /**
     * The base implementation of `_.unset`.
     *
     * @private
     * @param {Object} object The object to modify.
     * @param {Array|string} path The property path to unset.
     * @returns {boolean} Returns `true` if the property is deleted, else `false`.
     */
    function baseUnset(object, path) {
      path = castPath(path, object);
      object = parent(object, path);
      return object == null || delete object[toKey(last(path))];
    }

    /**
     * The base implementation of `_.update`.
     *
     * @private
     * @param {Object} object The object to modify.
     * @param {Array|string} path The path of the property to update.
     * @param {Function} updater The function to produce the updated value.
     * @param {Function} [customizer] The function to customize path creation.
     * @returns {Object} Returns `object`.
     */
    function baseUpdate(object, path, updater, customizer) {
      return baseSet(object, path, updater(baseGet(object, path)), customizer);
    }

    /**
     * The base implementation of methods like `_.dropWhile` and `_.takeWhile`
     * without support for iteratee shorthands.
     *
     * @private
     * @param {Array} array The array to query.
     * @param {Function} predicate The function invoked per iteration.
     * @param {boolean} [isDrop] Specify dropping elements instead of taking them.
     * @param {boolean} [fromRight] Specify iterating from right to left.
     * @returns {Array} Returns the slice of `array`.
     */
    function baseWhile(array, predicate, isDrop, fromRight) {
      var length = array.length,
          index = fromRight ? length : -1;

      while ((fromRight ? index-- : ++index < length) &&
        predicate(array[index], index, array)) {}

      return isDrop
        ? baseSlice(array, (fromRight ? 0 : index), (fromRight ? index + 1 : length))
        : baseSlice(array, (fromRight ? index + 1 : 0), (fromRight ? length : index));
    }

    /**
     * The base implementation of `wrapperValue` which returns the result of
     * performing a sequence of actions on the unwrapped `value`, where each
     * successive action is supplied the return value of the previous.
     *
     * @private
     * @param {*} value The unwrapped value.
     * @param {Array} actions Actions to perform to resolve the unwrapped value.
     * @returns {*} Returns the resolved value.
     */
    function baseWrapperValue(value, actions) {
      var result = value;
      if (result instanceof LazyWrapper) {
        result = result.value();
      }
      return arrayReduce(actions, function(result, action) {
        return action.func.apply(action.thisArg, arrayPush([result], action.args));
      }, result);
    }

    /**
     * The base implementation of methods like `_.xor`, without support for
     * iteratee shorthands, that accepts an array of arrays to inspect.
     *
     * @private
     * @param {Array} arrays The arrays to inspect.
     * @param {Function} [iteratee] The iteratee invoked per element.
     * @param {Function} [comparator] The comparator invoked per element.
     * @returns {Array} Returns the new array of values.
     */
    function baseXor(arrays, iteratee, comparator) {
      var length = arrays.length;
      if (length < 2) {
        return length ? baseUniq(arrays[0]) : [];
      }
      var index = -1,
          result = Array(length);

      while (++index < length) {
        var array = arrays[index],
            othIndex = -1;

        while (++othIndex < length) {
          if (othIndex != index) {
            result[index] = baseDifference(result[index] || array, arrays[othIndex], iteratee, comparator);
          }
        }
      }
      return baseUniq(baseFlatten(result, 1), iteratee, comparator);
    }

    /**
     * This base implementation of `_.zipObject` which assigns values using `assignFunc`.
     *
     * @private
     * @param {Array} props The property identifiers.
     * @param {Array} values The property values.
     * @param {Function} assignFunc The function to assign values.
     * @returns {Object} Returns the new object.
     */
    function baseZipObject(props, values, assignFunc) {
      var index = -1,
          length = props.length,
          valsLength = values.length,
          result = {};

      while (++index < length) {
        var value = index < valsLength ? values[index] : undefined;
        assignFunc(result, props[index], value);
      }
      return result;
    }

    /**
     * Casts `value` to an empty array if it's not an array like object.
     *
     * @private
     * @param {*} value The value to inspect.
     * @returns {Array|Object} Returns the cast array-like object.
     */
    function castArrayLikeObject(value) {
      return isArrayLikeObject(value) ? value : [];
    }

    /**
     * Casts `value` to `identity` if it's not a function.
     *
     * @private
     * @param {*} value The value to inspect.
     * @returns {Function} Returns cast function.
     */
    function castFunction(value) {
      return typeof value == 'function' ? value : identity;
    }

    /**
     * Casts `value` to a path array if it's not one.
     *
     * @private
     * @param {*} value The value to inspect.
     * @param {Object} [object] The object to query keys on.
     * @returns {Array} Returns the cast property path array.
     */
    function castPath(value, object) {
      if (isArray(value)) {
        return value;
      }
      return isKey(value, object) ? [value] : stringToPath(toString(value));
    }

    /**
     * A `baseRest` alias which can be replaced with `identity` by module
     * replacement plugins.
     *
     * @private
     * @type {Function}
     * @param {Function} func The function to apply a rest parameter to.
     * @returns {Function} Returns the new function.
     */
    var castRest = baseRest;

    /**
     * Casts `array` to a slice if it's needed.
     *
     * @private
     * @param {Array} array The array to inspect.
     * @param {number} start The start position.
     * @param {number} [end=array.length] The end position.
     * @returns {Array} Returns the cast slice.
     */
    function castSlice(array, start, end) {
      var length = array.length;
      end = end === undefined ? length : end;
      return (!start && end >= length) ? array : baseSlice(array, start, end);
    }

    /**
     * A simple wrapper around the global [`clearTimeout`](https://mdn.io/clearTimeout).
     *
     * @private
     * @param {number|Object} id The timer id or timeout object of the timer to clear.
     */
    var clearTimeout = ctxClearTimeout || function(id) {
      return root.clearTimeout(id);
    };

    /**
     * Creates a clone of  `buffer`.
     *
     * @private
     * @param {Buffer} buffer The buffer to clone.
     * @param {boolean} [isDeep] Specify a deep clone.
     * @returns {Buffer} Returns the cloned buffer.
     */
    function cloneBuffer(buffer, isDeep) {
      if (isDeep) {
        return buffer.slice();
      }
      var length = buffer.length,
          result = allocUnsafe ? allocUnsafe(length) : new buffer.constructor(length);

      buffer.copy(result);
      return result;
    }

    /**
     * Creates a clone of `arrayBuffer`.
     *
     * @private
     * @param {ArrayBuffer} arrayBuffer The array buffer to clone.
     * @returns {ArrayBuffer} Returns the cloned array buffer.
     */
    function cloneArrayBuffer(arrayBuffer) {
      var result = new arrayBuffer.constructor(arrayBuffer.byteLength);
      new Uint8Array(result).set(new Uint8Array(arrayBuffer));
      return result;
    }

    /**
     * Creates a clone of `dataView`.
     *
     * @private
     * @param {Object} dataView The data view to clone.
     * @param {boolean} [isDeep] Specify a deep clone.
     * @returns {Object} Returns the cloned data view.
     */
    function cloneDataView(dataView, isDeep) {
      var buffer = isDeep ? cloneArrayBuffer(dataView.buffer) : dataView.buffer;
      return new dataView.constructor(buffer, dataView.byteOffset, dataView.byteLength);
    }

    /**
     * Creates a clone of `map`.
     *
     * @private
     * @param {Object} map The map to clone.
     * @param {Function} cloneFunc The function to clone values.
     * @param {boolean} [isDeep] Specify a deep clone.
     * @returns {Object} Returns the cloned map.
     */
    function cloneMap(map, isDeep, cloneFunc) {
      var array = isDeep ? cloneFunc(mapToArray(map), CLONE_DEEP_FLAG) : mapToArray(map);
      return arrayReduce(array, addMapEntry, new map.constructor);
    }

    /**
     * Creates a clone of `regexp`.
     *
     * @private
     * @param {Object} regexp The regexp to clone.
     * @returns {Object} Returns the cloned regexp.
     */
    function cloneRegExp(regexp) {
      var result = new regexp.constructor(regexp.source, reFlags.exec(regexp));
      result.lastIndex = regexp.lastIndex;
      return result;
    }

    /**
     * Creates a clone of `set`.
     *
     * @private
     * @param {Object} set The set to clone.
     * @param {Function} cloneFunc The function to clone values.
     * @param {boolean} [isDeep] Specify a deep clone.
     * @returns {Object} Returns the cloned set.
     */
    function cloneSet(set, isDeep, cloneFunc) {
      var array = isDeep ? cloneFunc(setToArray(set), CLONE_DEEP_FLAG) : setToArray(set);
      return arrayReduce(array, addSetEntry, new set.constructor);
    }

    /**
     * Creates a clone of the `symbol` object.
     *
     * @private
     * @param {Object} symbol The symbol object to clone.
     * @returns {Object} Returns the cloned symbol object.
     */
    function cloneSymbol(symbol) {
      return symbolValueOf ? Object(symbolValueOf.call(symbol)) : {};
    }

    /**
     * Creates a clone of `typedArray`.
     *
     * @private
     * @param {Object} typedArray The typed array to clone.
     * @param {boolean} [isDeep] Specify a deep clone.
     * @returns {Object} Returns the cloned typed array.
     */
    function cloneTypedArray(typedArray, isDeep) {
      var buffer = isDeep ? cloneArrayBuffer(typedArray.buffer) : typedArray.buffer;
      return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length);
    }

    /**
     * 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 an array that is the composition of partially applied arguments,
     * placeholders, and provided arguments into a single array of arguments.
     *
     * @private
     * @param {Array} args The provided arguments.
     * @param {Array} partials The arguments to prepend to those provided.
     * @param {Array} holders The `partials` placeholder indexes.
     * @params {boolean} [isCurried] Specify composing for a curried function.
     * @returns {Array} Returns the new array of composed arguments.
     */
    function composeArgs(args, partials, holders, isCurried) {
      var argsIndex = -1,
          argsLength = args.length,
          holdersLength = holders.length,
          leftIndex = -1,
          leftLength = partials.length,
          rangeLength = nativeMax(argsLength - holdersLength, 0),
          result = Array(leftLength + rangeLength),
          isUncurried = !isCurried;

      while (++leftIndex < leftLength) {
        result[leftIndex] = partials[leftIndex];
      }
      while (++argsIndex < holdersLength) {
        if (isUncurried || argsIndex < argsLength) {
          result[holders[argsIndex]] = args[argsIndex];
        }
      }
      while (rangeLength--) {
        result[leftIndex++] = args[argsIndex++];
      }
      return result;
    }

    /**
     * This function is like `composeArgs` except that the arguments composition
     * is tailored for `_.partialRight`.
     *
     * @private
     * @param {Array} args The provided arguments.
     * @param {Array} partials The arguments to append to those provided.
     * @param {Array} holders The `partials` placeholder indexes.
     * @params {boolean} [isCurried] Specify composing for a curried function.
     * @returns {Array} Returns the new array of composed arguments.
     */
    function composeArgsRight(args, partials, holders, isCurried) {
      var argsIndex = -1,
          argsLength = args.length,
          holdersIndex = -1,
          holdersLength = holders.length,
          rightIndex = -1,
          rightLength = partials.length,
          rangeLength = nativeMax(argsLength - holdersLength, 0),
          result = Array(rangeLength + rightLength),
          isUncurried = !isCurried;

      while (++argsIndex < rangeLength) {
        result[argsIndex] = args[argsIndex];
      }
      var offset = argsIndex;
      while (++rightIndex < rightLength) {
        result[offset + rightIndex] = partials[rightIndex];
      }
      while (++holdersIndex < holdersLength) {
        if (isUncurried || argsIndex < argsLength) {
          result[offset + holders[holdersIndex]] = args[argsIndex++];
        }
      }
      return result;
    }

    /**
     * Copies the values of `source` to `array`.
     *
     * @private
     * @param {Array} source The array to copy values from.
     * @param {Array} [array=[]] The array to copy values to.
     * @returns {Array} Returns `array`.
     */
    function copyArray(source, array) {
      var index = -1,
          length = source.length;

      array || (array = Array(length));
      while (++index < length) {
        array[index] = source[index];
      }
      return array;
    }

    /**
     * Copies properties of `source` to `object`.
     *
     * @private
     * @param {Object} source The object to copy properties from.
     * @param {Array} props The property identifiers to copy.
     * @param {Object} [object={}] The object to copy properties to.
     * @param {Function} [customizer] The function to customize copied values.
     * @returns {Object} Returns `object`.
     */
    function copyObject(source, props, object, customizer) {
      var isNew = !object;
      object || (object = {});

      var index = -1,
          length = props.length;

      while (++index < length) {
        var key = props[index];

        var newValue = customizer
          ? customizer(object[key], source[key], key, object, source)
          : undefined;

        if (newValue === undefined) {
          newValue = source[key];
        }
        if (isNew) {
          baseAssignValue(object, key, newValue);
        } else {
          assignValue(object, key, newValue);
        }
      }
      return object;
    }

    /**
     * Copies own symbols of `source` to `object`.
     *
     * @private
     * @param {Object} source The object to copy symbols from.
     * @param {Object} [object={}] The object to copy symbols to.
     * @returns {Object} Returns `object`.
     */
    function copySymbols(source, object) {
      return copyObject(source, getSymbols(source), object);
    }

    /**
     * Copies own and inherited symbols of `source` to `object`.
     *
     * @private
     * @param {Object} source The object to copy symbols from.
     * @param {Object} [object={}] The object to copy symbols to.
     * @returns {Object} Returns `object`.
     */
    function copySymbolsIn(source, object) {
      return copyObject(source, getSymbolsIn(source), object);
    }

    /**
     * Creates a function like `_.groupBy`.
     *
     * @private
     * @param {Function} setter The function to set accumulator values.
     * @param {Function} [initializer] The accumulator object initializer.
     * @returns {Function} Returns the new aggregator function.
     */
    function createAggregator(setter, initializer) {
      return function(collection, iteratee) {
        var func = isArray(collection) ? arrayAggregator : baseAggregator,
            accumulator = initializer ? initializer() : {};

        return func(collection, setter, getIteratee(iteratee, 2), accumulator);
      };
    }

    /**
     * Creates a function like `_.assign`.
     *
     * @private
     * @param {Function} assigner The function to assign values.
     * @returns {Function} Returns the new assigner function.
     */
    function createAssigner(assigner) {
      return baseRest(function(object, sources) {
        var index = -1,
            length = sources.length,
            customizer = length > 1 ? sources[length - 1] : undefined,
            guard = length > 2 ? sources[2] : undefined;

        customizer = (assigner.length > 3 && typeof customizer == 'function')
          ? (length--, customizer)
          : undefined;

        if (guard && isIterateeCall(sources[0], sources[1], guard)) {
          customizer = length < 3 ? undefined : customizer;
          length = 1;
        }
        object = Object(object);
        while (++index < length) {
          var source = sources[index];
          if (source) {
            assigner(object, source, index, customizer);
          }
        }
        return object;
      });
    }

    /**
     * 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;
      };
    }

    /**
     * Creates a function that wraps `func` to invoke it with the optional `this`
     * binding of `thisArg`.
     *
     * @private
     * @param {Function} func The function to wrap.
     * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
     * @param {*} [thisArg] The `this` binding of `func`.
     * @returns {Function} Returns the new wrapped function.
     */
    function createBind(func, bitmask, thisArg) {
      var isBind = bitmask & WRAP_BIND_FLAG,
          Ctor = createCtor(func);

      function wrapper() {
        var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
        return fn.apply(isBind ? thisArg : this, arguments);
      }
      return wrapper;
    }

    /**
     * Creates a function like `_.lowerFirst`.
     *
     * @private
     * @param {string} methodName The name of the `String` case method to use.
     * @returns {Function} Returns the new case function.
     */
    function createCaseFirst(methodName) {
      return function(string) {
        string = toString(string);

        var strSymbols = hasUnicode(string)
          ? stringToArray(string)
          : undefined;

        var chr = strSymbols
          ? strSymbols[0]
          : string.charAt(0);

        var trailing = strSymbols
          ? castSlice(strSymbols, 1).join('')
          : string.slice(1);

        return chr[methodName]() + trailing;
      };
    }

    /**
     * Creates a function like `_.camelCase`.
     *
     * @private
     * @param {Function} callback The function to combine each word.
     * @returns {Function} Returns the new compounder function.
     */
    function createCompounder(callback) {
      return function(string) {
        return arrayReduce(words(deburr(string).replace(reApos, '')), callback, '');
      };
    }

    /**
     * Creates a function that produces an instance of `Ctor` regardless of
     * whether it was invoked as part of a `new` expression or by `call` or `apply`.
     *
     * @private
     * @param {Function} Ctor The constructor to wrap.
     * @returns {Function} Returns the new wrapped function.
     */
    function createCtor(Ctor) {
      return function() {
        // Use a `switch` statement to work with class constructors. See
        // http://ecma-international.org/ecma-262/7.0/#sec-ecmascript-function-objects-call-thisargument-argumentslist
        // for more details.
        var args = arguments;
        switch (args.length) {
          case 0: return new Ctor;
          case 1: return new Ctor(args[0]);
          case 2: return new Ctor(args[0], args[1]);
          case 3: return new Ctor(args[0], args[1], args[2]);
          case 4: return new Ctor(args[0], args[1], args[2], args[3]);
          case 5: return new Ctor(args[0], args[1], args[2], args[3], args[4]);
          case 6: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5]);
          case 7: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
        }
        var thisBinding = baseCreate(Ctor.prototype),
            result = Ctor.apply(thisBinding, args);

        // Mimic the constructor's `return` behavior.
        // See https://es5.github.io/#x13.2.2 for more details.
        return isObject(result) ? result : thisBinding;
      };
    }

    /**
     * Creates a function that wraps `func` to enable currying.
     *
     * @private
     * @param {Function} func The function to wrap.
     * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
     * @param {number} arity The arity of `func`.
     * @returns {Function} Returns the new wrapped function.
     */
    function createCurry(func, bitmask, arity) {
      var Ctor = createCtor(func);

      function wrapper() {
        var length = arguments.length,
            args = Array(length),
            index = length,
            placeholder = getHolder(wrapper);

        while (index--) {
          args[index] = arguments[index];
        }
        var holders = (length < 3 && args[0] !== placeholder && args[length - 1] !== placeholder)
          ? []
          : replaceHolders(args, placeholder);

        length -= holders.length;
        if (length < arity) {
          return createRecurry(
            func, bitmask, createHybrid, wrapper.placeholder, undefined,
            args, holders, undefined, undefined, arity - length);
        }
        var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
        return apply(fn, this, args);
      }
      return wrapper;
    }

    /**
     * Creates a `_.find` or `_.findLast` function.
     *
     * @private
     * @param {Function} findIndexFunc The function to find the collection index.
     * @returns {Function} Returns the new find function.
     */
    function createFind(findIndexFunc) {
      return function(collection, predicate, fromIndex) {
        var iterable = Object(collection);
        if (!isArrayLike(collection)) {
          var iteratee = getIteratee(predicate, 3);
          collection = keys(collection);
          predicate = function(key) { return iteratee(iterable[key], key, iterable); };
        }
        var index = findIndexFunc(collection, predicate, fromIndex);
        return index > -1 ? iterable[iteratee ? collection[index] : index] : undefined;
      };
    }

    /**
     * Creates a `_.flow` or `_.flowRight` function.
     *
     * @private
     * @param {boolean} [fromRight] Specify iterating from right to left.
     * @returns {Function} Returns the new flow function.
     */
    function createFlow(fromRight) {
      return flatRest(function(funcs) {
        var length = funcs.length,
            index = length,
            prereq = LodashWrapper.prototype.thru;

        if (fromRight) {
          funcs.reverse();
        }
        while (index--) {
          var func = funcs[index];
          if (typeof func != 'function') {
            throw new TypeError(FUNC_ERROR_TEXT);
          }
          if (prereq && !wrapper && getFuncName(func) == 'wrapper') {
            var wrapper = new LodashWrapper([], true);
          }
        }
        index = wrapper ? index : length;
        while (++index < length) {
          func = funcs[index];

          var funcName = getFuncName(func),
              data = funcName == 'wrapper' ? getData(func) : undefined;

          if (data && isLaziable(data[0]) &&
                data[1] == (WRAP_ARY_FLAG | WRAP_CURRY_FLAG | WRAP_PARTIAL_FLAG | WRAP_REARG_FLAG) &&
                !data[4].length && data[9] == 1
              ) {
            wrapper = wrapper[getFuncName(data[0])].apply(wrapper, data[3]);
          } else {
            wrapper = (func.length == 1 && isLaziable(func))
              ? wrapper[funcName]()
              : wrapper.thru(func);
          }
        }
        return function() {
          var args = arguments,
              value = args[0];

          if (wrapper && args.length == 1 && isArray(value)) {
            return wrapper.plant(value).value();
          }
          var index = 0,
              result = length ? funcs[index].apply(this, args) : value;

          while (++index < length) {
            result = funcs[index].call(this, result);
          }
          return result;
        };
      });
    }

    /**
     * Creates a function that wraps `func` to invoke it with optional `this`
     * binding of `thisArg`, partial application, and currying.
     *
     * @private
     * @param {Function|string} func The function or method name to wrap.
     * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
     * @param {*} [thisArg] The `this` binding of `func`.
     * @param {Array} [partials] The arguments to prepend to those provided to
     *  the new function.
     * @param {Array} [holders] The `partials` placeholder indexes.
     * @param {Array} [partialsRight] The arguments to append to those provided
     *  to the new function.
     * @param {Array} [holdersRight] The `partialsRight` placeholder indexes.
     * @param {Array} [argPos] The argument positions of the new function.
     * @param {number} [ary] The arity cap of `func`.
     * @param {number} [arity] The arity of `func`.
     * @returns {Function} Returns the new wrapped function.
     */
    function createHybrid(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) {
      var isAry = bitmask & WRAP_ARY_FLAG,
          isBind = bitmask & WRAP_BIND_FLAG,
          isBindKey = bitmask & WRAP_BIND_KEY_FLAG,
          isCurried = bitmask & (WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG),
          isFlip = bitmask & WRAP_FLIP_FLAG,
          Ctor = isBindKey ? undefined : createCtor(func);

      function wrapper() {
        var length = arguments.length,
            args = Array(length),
            index = length;

        while (index--) {
          args[index] = arguments[index];
        }
        if (isCurried) {
          var placeholder = getHolder(wrapper),
              holdersCount = countHolders(args, placeholder);
        }
        if (partials) {
          args = composeArgs(args, partials, holders, isCurried);
        }
        if (partialsRight) {
          args = composeArgsRight(args, partialsRight, holdersRight, isCurried);
        }
        length -= holdersCount;
        if (isCurried && length < arity) {
          var newHolders = replaceHolders(args, placeholder);
          return createRecurry(
            func, bitmask, createHybrid, wrapper.placeholder, thisArg,
            args, newHolders, argPos, ary, arity - length
          );
        }
        var thisBinding = isBind ? thisArg : this,
            fn = isBindKey ? thisBinding[func] : func;

        length = args.length;
        if (argPos) {
          args = reorder(args, argPos);
        } else if (isFlip && length > 1) {
          args.reverse();
        }
        if (isAry && ary < length) {
          args.length = ary;
        }
        if (this && this !== root && this instanceof wrapper) {
          fn = Ctor || createCtor(fn);
        }
        return fn.apply(thisBinding, args);
      }
      return wrapper;
    }

    /**
     * Creates a function like `_.invertBy`.
     *
     * @private
     * @param {Function} setter The function to set accumulator values.
     * @param {Function} toIteratee The function to resolve iteratees.
     * @returns {Function} Returns the new inverter function.
     */
    function createInverter(setter, toIteratee) {
      return function(object, iteratee) {
        return baseInverter(object, setter, toIteratee(iteratee), {});
      };
    }

    /**
     * Creates a function that performs a mathematical operation on two values.
     *
     * @private
     * @param {Function} operator The function to perform the operation.
     * @param {number} [defaultValue] The value used for `undefined` arguments.
     * @returns {Function} Returns the new mathematical operation function.
     */
    function createMathOperation(operator, defaultValue) {
      return function(value, other) {
        var result;
        if (value === undefined && other === undefined) {
          return defaultValue;
        }
        if (value !== undefined) {
          result = value;
        }
        if (other !== undefined) {
          if (result === undefined) {
            return other;
          }
          if (typeof value == 'string' || typeof other == 'string') {
            value = baseToString(value);
            other = baseToString(other);
          } else {
            value = baseToNumber(value);
            other = baseToNumber(other);
          }
          result = operator(value, other);
        }
        return result;
      };
    }

    /**
     * Creates a function like `_.over`.
     *
     * @private
     * @param {Function} arrayFunc The function to iterate over iteratees.
     * @returns {Function} Returns the new over function.
     */
    function createOver(arrayFunc) {
      return flatRest(function(iteratees) {
        iteratees = arrayMap(iteratees, baseUnary(getIteratee()));
        return baseRest(function(args) {
          var thisArg = this;
          return arrayFunc(iteratees, function(iteratee) {
            return apply(iteratee, thisArg, args);
          });
        });
      });
    }

    /**
     * Creates the padding for `string` based on `length`. The `chars` string
     * is truncated if the number of characters exceeds `length`.
     *
     * @private
     * @param {number} length The padding length.
     * @param {string} [chars=' '] The string used as padding.
     * @returns {string} Returns the padding for `string`.
     */
    function createPadding(length, chars) {
      chars = chars === undefined ? ' ' : baseToString(chars);

      var charsLength = chars.length;
      if (charsLength < 2) {
        return charsLength ? baseRepeat(chars, length) : chars;
      }
      var result = baseRepeat(chars, nativeCeil(length / stringSize(chars)));
      return hasUnicode(chars)
        ? castSlice(stringToArray(result), 0, length).join('')
        : result.slice(0, length);
    }

    /**
     * Creates a function that wraps `func` to invoke it with the `this` binding
     * of `thisArg` and `partials` prepended to the arguments it receives.
     *
     * @private
     * @param {Function} func The function to wrap.
     * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
     * @param {*} thisArg The `this` binding of `func`.
     * @param {Array} partials The arguments to prepend to those provided to
     *  the new function.
     * @returns {Function} Returns the new wrapped function.
     */
    function createPartial(func, bitmask, thisArg, partials) {
      var isBind = bitmask & WRAP_BIND_FLAG,
          Ctor = createCtor(func);

      function wrapper() {
        var argsIndex = -1,
            argsLength = arguments.length,
            leftIndex = -1,
            leftLength = partials.length,
            args = Array(leftLength + argsLength),
            fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;

        while (++leftIndex < leftLength) {
          args[leftIndex] = partials[leftIndex];
        }
        while (argsLength--) {
          args[leftIndex++] = arguments[++argsIndex];
        }
        return apply(fn, isBind ? thisArg : this, args);
      }
      return wrapper;
    }

    /**
     * Creates a `_.range` or `_.rangeRight` function.
     *
     * @private
     * @param {boolean} [fromRight] Specify iterating from right to left.
     * @returns {Function} Returns the new range function.
     */
    function createRange(fromRight) {
      return function(start, end, step) {
        if (step && typeof step != 'number' && isIterateeCall(start, end, step)) {
          end = step = undefined;
        }
        // Ensure the sign of `-0` is preserved.
        start = toFinite(start);
        if (end === undefined) {
          end = start;
          start = 0;
        } else {
          end = toFinite(end);
        }
        step = step === undefined ? (start < end ? 1 : -1) : toFinite(step);
        return baseRange(start, end, step, fromRight);
      };
    }

    /**
     * Creates a function that performs a relational operation on two values.
     *
     * @private
     * @param {Function} operator The function to perform the operation.
     * @returns {Function} Returns the new relational operation function.
     */
    function createRelationalOperation(operator) {
      return function(value, other) {
        if (!(typeof value == 'string' && typeof other == 'string')) {
          value = toNumber(value);
          other = toNumber(other);
        }
        return operator(value, other);
      };
    }

    /**
     * Creates a function that wraps `func` to continue currying.
     *
     * @private
     * @param {Function} func The function to wrap.
     * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
     * @param {Function} wrapFunc The function to create the `func` wrapper.
     * @param {*} placeholder The placeholder value.
     * @param {*} [thisArg] The `this` binding of `func`.
     * @param {Array} [partials] The arguments to prepend to those provided to
     *  the new function.
     * @param {Array} [holders] The `partials` placeholder indexes.
     * @param {Array} [argPos] The argument positions of the new function.
     * @param {number} [ary] The arity cap of `func`.
     * @param {number} [arity] The arity of `func`.
     * @returns {Function} Returns the new wrapped function.
     */
    function createRecurry(func, bitmask, wrapFunc, placeholder, thisArg, partials, holders, argPos, ary, arity) {
      var isCurry = bitmask & WRAP_CURRY_FLAG,
          newHolders = isCurry ? holders : undefined,
          newHoldersRight = isCurry ? undefined : holders,
          newPartials = isCurry ? partials : undefined,
          newPartialsRight = isCurry ? undefined : partials;

      bitmask |= (isCurry ? WRAP_PARTIAL_FLAG : WRAP_PARTIAL_RIGHT_FLAG);
      bitmask &= ~(isCurry ? WRAP_PARTIAL_RIGHT_FLAG : WRAP_PARTIAL_FLAG);

      if (!(bitmask & WRAP_CURRY_BOUND_FLAG)) {
        bitmask &= ~(WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG);
      }
      var newData = [
        func, bitmask, thisArg, newPartials, newHolders, newPartialsRight,
        newHoldersRight, argPos, ary, arity
      ];

      var result = wrapFunc.apply(undefined, newData);
      if (isLaziable(func)) {
        setData(result, newData);
      }
      result.placeholder = placeholder;
      return setWrapToString(result, func, bitmask);
    }

    /**
     * Creates a function like `_.round`.
     *
     * @private
     * @param {string} methodName The name of the `Math` method to use when rounding.
     * @returns {Function} Returns the new round function.
     */
    function createRound(methodName) {
      var func = Math[methodName];
      return function(number, precision) {
        number = toNumber(number);
        precision = precision == null ? 0 : nativeMin(toInteger(precision), 292);
        if (precision) {
          // Shift with exponential notation to avoid floating-point issues.
          // See [MDN](https://mdn.io/round#Examples) for more details.
          var pair = (toString(number) + 'e').split('e'),
              value = func(pair[0] + 'e' + (+pair[1] + precision));

          pair = (toString(value) + 'e').split('e');
          return +(pair[0] + 'e' + (+pair[1] - precision));
        }
        return func(number);
      };
    }

    /**
     * Creates a set object of `values`.
     *
     * @private
     * @param {Array} values The values to add to the set.
     * @returns {Object} Returns the new set.
     */
    var createSet = !(Set && (1 / setToArray(new Set([,-0]))[1]) == INFINITY) ? noop : function(values) {
      return new Set(values);
    };

    /**
     * Creates a `_.toPairs` or `_.toPairsIn` function.
     *
     * @private
     * @param {Function} keysFunc The function to get the keys of a given object.
     * @returns {Function} Returns the new pairs function.
     */
    function createToPairs(keysFunc) {
      return function(object) {
        var tag = getTag(object);
        if (tag == mapTag) {
          return mapToArray(object);
        }
        if (tag == setTag) {
          return setToPairs(object);
        }
        return baseToPairs(object, keysFunc(object));
      };
    }

    /**
     * Creates a function that either curries or invokes `func` with optional
     * `this` binding and partially applied arguments.
     *
     * @private
     * @param {Function|string} func The function or method name to wrap.
     * @param {number} bitmask The bitmask flags.
     *    1 - `_.bind`
     *    2 - `_.bindKey`
     *    4 - `_.curry` or `_.curryRight` of a bound function
     *    8 - `_.curry`
     *   16 - `_.curryRight`
     *   32 - `_.partial`
     *   64 - `_.partialRight`
     *  128 - `_.rearg`
     *  256 - `_.ary`
     *  512 - `_.flip`
     * @param {*} [thisArg] The `this` binding of `func`.
     * @param {Array} [partials] The arguments to be partially applied.
     * @param {Array} [holders] The `partials` placeholder indexes.
     * @param {Array} [argPos] The argument positions of the new function.
     * @param {number} [ary] The arity cap of `func`.
     * @param {number} [arity] The arity of `func`.
     * @returns {Function} Returns the new wrapped function.
     */
    function createWrap(func, bitmask, thisArg, partials, holders, argPos, ary, arity) {
      var isBindKey = bitmask & WRAP_BIND_KEY_FLAG;
      if (!isBindKey && typeof func != 'function') {
        throw new TypeError(FUNC_ERROR_TEXT);
      }
      var length = partials ? partials.length : 0;
      if (!length) {
        bitmask &= ~(WRAP_PARTIAL_FLAG | WRAP_PARTIAL_RIGHT_FLAG);
        partials = holders = undefined;
      }
      ary = ary === undefined ? ary : nativeMax(toInteger(ary), 0);
      arity = arity === undefined ? arity : toInteger(arity);
      length -= holders ? holders.length : 0;

      if (bitmask & WRAP_PARTIAL_RIGHT_FLAG) {
        var partialsRight = partials,
            holdersRight = holders;

        partials = holders = undefined;
      }
      var data = isBindKey ? undefined : getData(func);

      var newData = [
        func, bitmask, thisArg, partials, holders, partialsRight, holdersRight,
        argPos, ary, arity
      ];

      if (data) {
        mergeData(newData, data);
      }
      func = newData[0];
      bitmask = newData[1];
      thisArg = newData[2];
      partials = newData[3];
      holders = newData[4];
      arity = newData[9] = newData[9] === undefined
        ? (isBindKey ? 0 : func.length)
        : nativeMax(newData[9] - length, 0);

      if (!arity && bitmask & (WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG)) {
        bitmask &= ~(WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG);
      }
      if (!bitmask || bitmask == WRAP_BIND_FLAG) {
        var result = createBind(func, bitmask, thisArg);
      } else if (bitmask == WRAP_CURRY_FLAG || bitmask == WRAP_CURRY_RIGHT_FLAG) {
        result = createCurry(func, bitmask, arity);
      } else if ((bitmask == WRAP_PARTIAL_FLAG || bitmask == (WRAP_BIND_FLAG | WRAP_PARTIAL_FLAG)) && !holders.length) {
        result = createPartial(func, bitmask, thisArg, partials);
      } else {
        result = createHybrid.apply(undefined, newData);
      }
      var setter = data ? baseSetData : setData;
      return setWrapToString(setter(result, newData), func, bitmask);
    }

    /**
     * Used by `_.defaults` to customize its `_.assignIn` use to assign properties
     * of source objects to the destination object for all destination properties
     * that resolve to `undefined`.
     *
     * @private
     * @param {*} objValue The destination value.
     * @param {*} srcValue The source value.
     * @param {string} key The key of the property to assign.
     * @param {Object} object The parent object of `objValue`.
     * @returns {*} Returns the value to assign.
     */
    function customDefaultsAssignIn(objValue, srcValue, key, object) {
      if (objValue === undefined ||
          (eq(objValue, objectProto[key]) && !hasOwnProperty.call(object, key))) {
        return srcValue;
      }
      return objValue;
    }

    /**
     * Used by `_.defaultsDeep` to customize its `_.merge` use to merge source
     * objects into destination objects that are passed thru.
     *
     * @private
     * @param {*} objValue The destination value.
     * @param {*} srcValue The source value.
     * @param {string} key The key of the property to merge.
     * @param {Object} object The parent object of `objValue`.
     * @param {Object} source The parent object of `srcValue`.
     * @param {Object} [stack] Tracks traversed source values and their merged
     *  counterparts.
     * @returns {*} Returns the value to assign.
     */
    function customDefaultsMerge(objValue, srcValue, key, object, source, stack) {
      if (isObject(objValue) && isObject(srcValue)) {
        // Recursively merge objects and arrays (susceptible to call stack limits).
        stack.set(srcValue, objValue);
        baseMerge(objValue, srcValue, undefined, customDefaultsMerge, stack);
        stack['delete'](srcValue);
      }
      return objValue;
    }

    /**
     * Used by `_.omit` to customize its `_.cloneDeep` use to only clone plain
     * objects.
     *
     * @private
     * @param {*} value The value to inspect.
     * @param {string} key The key of the property to inspect.
     * @returns {*} Returns the uncloned value or `undefined` to defer cloning to `_.cloneDeep`.
     */
    function customOmitClone(value) {
      return isPlainObject(value) ? undefined : value;
    }

    /**
     * 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 {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
     * @param {Function} customizer The function to customize comparisons.
     * @param {Function} equalFunc The function to determine equivalents of values.
     * @param {Object} stack Tracks traversed `array` and `other` objects.
     * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.
     */
    function equalArrays(array, other, bitmask, customizer, equalFunc, stack) {
      var isPartial = bitmask & COMPARE_PARTIAL_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 & COMPARE_UNORDERED_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 (!cacheHas(seen, othIndex) &&
                    (arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) {
                  return seen.push(othIndex);
                }
              })) {
            result = false;
            break;
          }
        } else if (!(
              arrValue === othValue ||
                equalFunc(arrValue, othValue, bitmask, customizer, 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 {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
     * @param {Function} customizer The function to customize comparisons.
     * @param {Function} equalFunc The function to determine equivalents of values.
     * @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, bitmask, customizer, equalFunc, 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 & COMPARE_PARTIAL_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 |= COMPARE_UNORDERED_FLAG;

          // Recursively compare objects (susceptible to call stack limits).
          stack.set(object, other);
          var result = equalArrays(convert(object), convert(other), bitmask, customizer, equalFunc, 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 {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
     * @param {Function} customizer The function to customize comparisons.
     * @param {Function} equalFunc The function to determine equivalents of values.
     * @param {Object} stack Tracks traversed `object` and `other` objects.
     * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
     */
    function equalObjects(object, other, bitmask, customizer, equalFunc, stack) {
      var isPartial = bitmask & COMPARE_PARTIAL_FLAG,
          objProps = getAllKeys(object),
          objLength = objProps.length,
          othProps = getAllKeys(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, bitmask, customizer, 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;
    }

    /**
     * A specialized version of `baseRest` which flattens the rest array.
     *
     * @private
     * @param {Function} func The function to apply a rest parameter to.
     * @returns {Function} Returns the new function.
     */
    function flatRest(func) {
      return setToString(overRest(func, undefined, flatten), func + '');
    }

    /**
     * Creates an array of own enumerable property names and symbols of `object`.
     *
     * @private
     * @param {Object} object The object to query.
     * @returns {Array} Returns the array of property names and symbols.
     */
    function getAllKeys(object) {
      return baseGetAllKeys(object, keys, getSymbols);
    }

    /**
     * Creates an array of own and inherited enumerable property names and
     * symbols of `object`.
     *
     * @private
     * @param {Object} object The object to query.
     * @returns {Array} Returns the array of property names and symbols.
     */
    function getAllKeysIn(object) {
      return baseGetAllKeys(object, keysIn, getSymbolsIn);
    }

    /**
     * Gets metadata for `func`.
     *
     * @private
     * @param {Function} func The function to query.
     * @returns {*} Returns the metadata for `func`.
     */
    var getData = !metaMap ? noop : function(func) {
      return metaMap.get(func);
    };

    /**
     * Gets the name of `func`.
     *
     * @private
     * @param {Function} func The function to query.
     * @returns {string} Returns the function name.
     */
    function getFuncName(func) {
      var result = (func.name + ''),
          array = realNames[result],
          length = hasOwnProperty.call(realNames, result) ? array.length : 0;

      while (length--) {
        var data = array[length],
            otherFunc = data.func;
        if (otherFunc == null || otherFunc == func) {
          return data.name;
        }
      }
      return result;
    }

    /**
     * Gets the argument placeholder value for `func`.
     *
     * @private
     * @param {Function} func The function to inspect.
     * @returns {*} Returns the placeholder value.
     */
    function getHolder(func) {
      var object = hasOwnProperty.call(lodash, 'placeholder') ? lodash : func;
      return object.placeholder;
    }

    /**
     * Gets the appropriate "iteratee" function. If `_.iteratee` is customized,
     * this function returns the custom method, otherwise it returns `baseIteratee`.
     * If arguments are provided, the chosen function is invoked with them and
     * its result is returned.
     *
     * @private
     * @param {*} [value] The value to convert to an iteratee.
     * @param {number} [arity] The arity of the created iteratee.
     * @returns {Function} Returns the chosen function or its result.
     */
    function getIteratee() {
      var result = lodash.iteratee || iteratee;
      result = result === iteratee ? baseIteratee : result;
      return arguments.length ? result(arguments[0], arguments[1]) : 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;
    }

    /**
     * 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.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;
    }

    /**
     * Creates an array of the own enumerable symbols of `object`.
     *
     * @private
     * @param {Object} object The object to query.
     * @returns {Array} Returns the array of symbols.
     */
    var getSymbols = !nativeGetSymbols ? stubArray : function(object) {
      if (object == null) {
        return [];
      }
      object = Object(object);
      return arrayFilter(nativeGetSymbols(object), function(symbol) {
        return propertyIsEnumerable.call(object, symbol);
      });
    };

    /**
     * Creates an array of the own and inherited enumerable symbols of `object`.
     *
     * @private
     * @param {Object} object The object to query.
     * @returns {Array} Returns the array of symbols.
     */
    var getSymbolsIn = !nativeGetSymbols ? stubArray : function(object) {
      var result = [];
      while (object) {
        arrayPush(result, getSymbols(object));
        object = getPrototype(object);
      }
      return result;
    };

    /**
     * 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 and promises in Node.js < 6.
    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 = baseGetTag(value),
            Ctor = result == objectTag ? value.constructor : undefined,
            ctorString = Ctor ? toSource(Ctor) : '';

        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;
      };
    }

    /**
     * Gets the view, applying any `transforms` to the `start` and `end` positions.
     *
     * @private
     * @param {number} start The start of the view.
     * @param {number} end The end of the view.
     * @param {Array} transforms The transformations to apply to the view.
     * @returns {Object} Returns an object containing the `start` and `end`
     *  positions of the view.
     */
    function getView(start, end, transforms) {
      var index = -1,
          length = transforms.length;

      while (++index < length) {
        var data = transforms[index],
            size = data.size;

        switch (data.type) {
          case 'drop':      start += size; break;
          case 'dropRight': end -= size; break;
          case 'take':      end = nativeMin(end, start + size); break;
          case 'takeRight': start = nativeMax(start, end - size); break;
        }
      }
      return { 'start': start, 'end': end };
    }

    /**
     * Extracts wrapper details from the `source` body comment.
     *
     * @private
     * @param {string} source The source to inspect.
     * @returns {Array} Returns the wrapper details.
     */
    function getWrapDetails(source) {
      var match = source.match(reWrapDetails);
      return match ? match[1].split(reSplitDetails) : [];
    }

    /**
     * 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 = castPath(path, object);

      var index = -1,
          length = path.length,
          result = false;

      while (++index < length) {
        var key = toKey(path[index]);
        if (!(result = object != null && hasFunc(object, key))) {
          break;
        }
        object = object[key];
      }
      if (result || ++index != length) {
        return result;
      }
      length = object == null ? 0 : object.length;
      return !!length && isLength(length) && isIndex(key, length) &&
        (isArray(object) || isArguments(object));
    }

    /**
     * Initializes an array clone.
     *
     * @private
     * @param {Array} array The array to clone.
     * @returns {Array} Returns the initialized clone.
     */
    function initCloneArray(array) {
      var length = array.length,
          result = array.constructor(length);

      // Add properties assigned by `RegExp#exec`.
      if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {
        result.index = array.index;
        result.input = array.input;
      }
      return result;
    }

    /**
     * Initializes an object clone.
     *
     * @private
     * @param {Object} object The object to clone.
     * @returns {Object} Returns the initialized clone.
     */
    function initCloneObject(object) {
      return (typeof object.constructor == 'function' && !isPrototype(object))
        ? baseCreate(getPrototype(object))
        : {};
    }

    /**
     * Initializes an object clone based on its `toStringTag`.
     *
     * **Note:** This function only supports cloning values with tags of
     * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
     *
     * @private
     * @param {Object} object The object to clone.
     * @param {string} tag The `toStringTag` of the object to clone.
     * @param {Function} cloneFunc The function to clone values.
     * @param {boolean} [isDeep] Specify a deep clone.
     * @returns {Object} Returns the initialized clone.
     */
    function initCloneByTag(object, tag, cloneFunc, isDeep) {
      var Ctor = object.constructor;
      switch (tag) {
        case arrayBufferTag:
          return cloneArrayBuffer(object);

        case boolTag:
        case dateTag:
          return new Ctor(+object);

        case dataViewTag:
          return cloneDataView(object, isDeep);

        case float32Tag: case float64Tag:
        case int8Tag: case int16Tag: case int32Tag:
        case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag:
          return cloneTypedArray(object, isDeep);

        case mapTag:
          return cloneMap(object, isDeep, cloneFunc);

        case numberTag:
        case stringTag:
          return new Ctor(object);

        case regexpTag:
          return cloneRegExp(object);

        case setTag:
          return cloneSet(object, isDeep, cloneFunc);

        case symbolTag:
          return cloneSymbol(object);
      }
    }

    /**
     * Inserts wrapper `details` in a comment at the top of the `source` body.
     *
     * @private
     * @param {string} source The source to modify.
     * @returns {Array} details The details to insert.
     * @returns {string} Returns the modified source.
     */
    function insertWrapDetails(source, details) {
      var length = details.length;
      if (!length) {
        return source;
      }
      var lastIndex = length - 1;
      details[lastIndex] = (length > 1 ? '& ' : '') + details[lastIndex];
      details = details.join(length > 2 ? ', ' : ' ');
      return source.replace(reWrapComment, '{\n/* [wrapped with ' + details + '] */\n');
    }

    /**
     * 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 a lazy counterpart.
     *
     * @private
     * @param {Function} func The function to check.
     * @returns {boolean} Returns `true` if `func` has a lazy counterpart,
     *  else `false`.
     */
    function isLaziable(func) {
      var funcName = getFuncName(func),
          other = lodash[funcName];

      if (typeof other != 'function' || !(funcName in LazyWrapper.prototype)) {
        return false;
      }
      if (func === other) {
        return true;
      }
      var data = getData(other);
      return !!data && func === data[0];
    }

    /**
     * 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 `func` is capable of being masked.
     *
     * @private
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `func` is maskable, else `false`.
     */
    var isMaskable = coreJsData ? isFunction : stubFalse;

    /**
     * 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)));
      };
    }

    /**
     * A specialized version of `_.memoize` which clears the memoized function's
     * cache when it exceeds `MAX_MEMOIZE_SIZE`.
     *
     * @private
     * @param {Function} func The function to have its output memoized.
     * @returns {Function} Returns the new memoized function.
     */
    function memoizeCapped(func) {
      var result = memoize(func, function(key) {
        if (cache.size === MAX_MEMOIZE_SIZE) {
          cache.clear();
        }
        return key;
      });

      var cache = result.cache;
      return result;
    }

    /**
     * Merges the function metadata of `source` into `data`.
     *
     * Merging metadata reduces the number of wrappers used to invoke a function.
     * This is possible because methods like `_.bind`, `_.curry`, and `_.partial`
     * may be applied regardless of execution order. Methods like `_.ary` and
     * `_.rearg` modify function arguments, making the order in which they are
     * executed important, preventing the merging of metadata. However, we make
     * an exception for a safe combined case where curried functions have `_.ary`
     * and or `_.rearg` applied.
     *
     * @private
     * @param {Array} data The destination metadata.
     * @param {Array} source The source metadata.
     * @returns {Array} Returns `data`.
     */
    function mergeData(data, source) {
      var bitmask = data[1],
          srcBitmask = source[1],
          newBitmask = bitmask | srcBitmask,
          isCommon = newBitmask < (WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG | WRAP_ARY_FLAG);

      var isCombo =
        ((srcBitmask == WRAP_ARY_FLAG) && (bitmask == WRAP_CURRY_FLAG)) ||
        ((srcBitmask == WRAP_ARY_FLAG) && (bitmask == WRAP_REARG_FLAG) && (data[7].length <= source[8])) ||
        ((srcBitmask == (WRAP_ARY_FLAG | WRAP_REARG_FLAG)) && (source[7].length <= source[8]) && (bitmask == WRAP_CURRY_FLAG));

      // Exit early if metadata can't be merged.
      if (!(isCommon || isCombo)) {
        return data;
      }
      // Use source `thisArg` if available.
      if (srcBitmask & WRAP_BIND_FLAG) {
        data[2] = source[2];
        // Set when currying a bound function.
        newBitmask |= bitmask & WRAP_BIND_FLAG ? 0 : WRAP_CURRY_BOUND_FLAG;
      }
      // Compose partial arguments.
      var value = source[3];
      if (value) {
        var partials = data[3];
        data[3] = partials ? composeArgs(partials, value, source[4]) : value;
        data[4] = partials ? replaceHolders(data[3], PLACEHOLDER) : source[4];
      }
      // Compose partial right arguments.
      value = source[5];
      if (value) {
        partials = data[5];
        data[5] = partials ? composeArgsRight(partials, value, source[6]) : value;
        data[6] = partials ? replaceHolders(data[5], PLACEHOLDER) : source[6];
      }
      // Use source `argPos` if available.
      value = source[7];
      if (value) {
        data[7] = value;
      }
      // Use source `ary` if it's smaller.
      if (srcBitmask & WRAP_ARY_FLAG) {
        data[8] = data[8] == null ? source[8] : nativeMin(data[8], source[8]);
      }
      // Use source `arity` if one is not provided.
      if (data[9] == null) {
        data[9] = source[9];
      }
      // Use source `func` and merge bitmasks.
      data[0] = source[0];
      data[1] = newBitmask;

      return data;
    }

    /**
     * This function is like
     * [`Object.keys`](http://ecma-international.org/ecma-262/7.0/#sec-object.keys)
     * except that it includes inherited enumerable properties.
     *
     * @private
     * @param {Object} object The object to query.
     * @returns {Array} Returns the array of property names.
     */
    function nativeKeysIn(object) {
      var result = [];
      if (object != null) {
        for (var key in Object(object)) {
          result.push(key);
        }
      }
      return result;
    }

    /**
     * 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.call(value);
    }

    /**
     * A specialized version of `baseRest` which transforms the rest array.
     *
     * @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.
     * @param {Function} transform The rest array transform.
     * @returns {Function} Returns the new function.
     */
    function overRest(func, start, transform) {
      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] = transform(array);
        return apply(func, this, otherArgs);
      };
    }

    /**
     * Gets the parent value at `path` of `object`.
     *
     * @private
     * @param {Object} object The object to query.
     * @param {Array} path The path to get the parent value of.
     * @returns {*} Returns the parent value.
     */
    function parent(object, path) {
      return path.length < 2 ? object : baseGet(object, baseSlice(path, 0, -1));
    }

    /**
     * Reorder `array` according to the specified indexes where the element at
     * the first index is assigned as the first element, the element at
     * the second index is assigned as the second element, and so on.
     *
     * @private
     * @param {Array} array The array to reorder.
     * @param {Array} indexes The arranged array indexes.
     * @returns {Array} Returns `array`.
     */
    function reorder(array, indexes) {
      var arrLength = array.length,
          length = nativeMin(indexes.length, arrLength),
          oldArray = copyArray(array);

      while (length--) {
        var index = indexes[length];
        array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined;
      }
      return array;
    }

    /**
     * Sets metadata for `func`.
     *
     * **Note:** If this function becomes hot, i.e. is invoked a lot in a short
     * period of time, it will trip its breaker and transition to an identity
     * function to avoid garbage collection pauses in V8. See
     * [V8 issue 2070](https://bugs.chromium.org/p/v8/issues/detail?id=2070)
     * for more details.
     *
     * @private
     * @param {Function} func The function to associate metadata with.
     * @param {*} data The metadata.
     * @returns {Function} Returns `func`.
     */
    var setData = shortOut(baseSetData);

    /**
     * A simple wrapper around the global [`setTimeout`](https://mdn.io/setTimeout).
     *
     * @private
     * @param {Function} func The function to delay.
     * @param {number} wait The number of milliseconds to delay invocation.
     * @returns {number|Object} Returns the timer id or timeout object.
     */
    var setTimeout = ctxSetTimeout || function(func, wait) {
      return root.setTimeout(func, wait);
    };

    /**
     * Sets the `toString` method of `func` to return `string`.
     *
     * @private
     * @param {Function} func The function to modify.
     * @param {Function} string The `toString` result.
     * @returns {Function} Returns `func`.
     */
    var setToString = shortOut(baseSetToString);

    /**
     * Sets the `toString` method of `wrapper` to mimic the source of `reference`
     * with wrapper details in a comment at the top of the source body.
     *
     * @private
     * @param {Function} wrapper The function to modify.
     * @param {Function} reference The reference function.
     * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
     * @returns {Function} Returns `wrapper`.
     */
    function setWrapToString(wrapper, reference, bitmask) {
      var source = (reference + '');
      return setToString(wrapper, insertWrapDetails(source, updateWrapDetails(getWrapDetails(source), bitmask)));
    }

    /**
     * Creates a function that'll short out and invoke `identity` instead
     * of `func` when it's called `HOT_COUNT` or more times in `HOT_SPAN`
     * milliseconds.
     *
     * @private
     * @param {Function} func The function to restrict.
     * @returns {Function} Returns the new shortable function.
     */
    function shortOut(func) {
      var count = 0,
          lastCalled = 0;

      return function() {
        var stamp = nativeNow(),
            remaining = HOT_SPAN - (stamp - lastCalled);

        lastCalled = stamp;
        if (remaining > 0) {
          if (++count >= HOT_COUNT) {
            return arguments[0];
          }
        } else {
          count = 0;
        }
        return func.apply(undefined, arguments);
      };
    }

    /**
     * A specialized version of `_.shuffle` which mutates and sets the size of `array`.
     *
     * @private
     * @param {Array} array The array to shuffle.
     * @param {number} [size=array.length] The size of `array`.
     * @returns {Array} Returns `array`.
     */
    function shuffleSelf(array, size) {
      var index = -1,
          length = array.length,
          lastIndex = length - 1;

      size = size === undefined ? length : size;
      while (++index < size) {
        var rand = baseRandom(index, lastIndex),
            value = array[rand];

        array[rand] = array[index];
        array[index] = value;
      }
      array.length = size;
      return array;
    }

    /**
     * Converts `string` to a property path array.
     *
     * @private
     * @param {string} string The string to convert.
     * @returns {Array} Returns the property path array.
     */
    var stringToPath = memoizeCapped(function(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 convert.
     * @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 '';
    }

    /**
     * Updates wrapper `details` based on `bitmask` flags.
     *
     * @private
     * @returns {Array} details The details to modify.
     * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
     * @returns {Array} Returns `details`.
     */
    function updateWrapDetails(details, bitmask) {
      arrayEach(wrapFlags, function(pair) {
        var value = '_.' + pair[0];
        if ((bitmask & pair[1]) && !arrayIncludes(details, value)) {
          details.push(value);
        }
      });
      return details.sort();
    }

    /**
     * Creates a clone of `wrapper`.
     *
     * @private
     * @param {Object} wrapper The wrapper to clone.
     * @returns {Object} Returns the cloned wrapper.
     */
    function wrapperClone(wrapper) {
      if (wrapper instanceof LazyWrapper) {
        return wrapper.clone();
      }
      var result = new LodashWrapper(wrapper.__wrapped__, wrapper.__chain__);
      result.__actions__ = copyArray(wrapper.__actions__);
      result.__index__  = wrapper.__index__;
      result.__values__ = wrapper.__values__;
      return result;
    }

    /*------------------------------------------------------------------------*/

    /**
     * Creates an array of elements split into groups the length of `size`.
     * If `array` can't be split evenly, the final chunk will be the remaining
     * elements.
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category Array
     * @param {Array} array The array to process.
     * @param {number} [size=1] The length of each chunk
     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
     * @returns {Array} Returns the new array of chunks.
     * @example
     *
     * _.chunk(['a', 'b', 'c', 'd'], 2);
     * // => [['a', 'b'], ['c', 'd']]
     *
     * _.chunk(['a', 'b', 'c', 'd'], 3);
     * // => [['a', 'b', 'c'], ['d']]
     */
    function chunk(array, size, guard) {
      if ((guard ? isIterateeCall(array, size, guard) : size === undefined)) {
        size = 1;
      } else {
        size = nativeMax(toInteger(size), 0);
      }
      var length = array == null ? 0 : array.length;
      if (!length || size < 1) {
        return [];
      }
      var index = 0,
          resIndex = 0,
          result = Array(nativeCeil(length / size));

      while (index < length) {
        result[resIndex++] = baseSlice(array, index, (index += size));
      }
      return result;
    }

    /**
     * Creates an array with all falsey values removed. The values `false`, `null`,
     * `0`, `""`, `undefined`, and `NaN` are falsey.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Array
     * @param {Array} array The array to compact.
     * @returns {Array} Returns the new array of filtered values.
     * @example
     *
     * _.compact([0, 1, false, 2, '', 3]);
     * // => [1, 2, 3]
     */
    function compact(array) {
      var index = -1,
          length = array == null ? 0 : array.length,
          resIndex = 0,
          result = [];

      while (++index < length) {
        var value = array[index];
        if (value) {
          result[resIndex++] = value;
        }
      }
      return result;
    }

    /**
     * Creates a new array concatenating `array` with any additional arrays
     * and/or values.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Array
     * @param {Array} array The array to concatenate.
     * @param {...*} [values] The values to concatenate.
     * @returns {Array} Returns the new concatenated array.
     * @example
     *
     * var array = [1];
     * var other = _.concat(array, 2, [3], [[4]]);
     *
     * console.log(other);
     * // => [1, 2, 3, [4]]
     *
     * console.log(array);
     * // => [1]
     */
    function concat() {
      var length = arguments.length;
      if (!length) {
        return [];
      }
      var args = Array(length - 1),
          array = arguments[0],
          index = length;

      while (index--) {
        args[index - 1] = arguments[index];
      }
      return arrayPush(isArray(array) ? copyArray(array) : [array], baseFlatten(args, 1));
    }

    /**
     * Creates an array of `array` values not included in the other given arrays
     * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
     * for equality comparisons. The order and references of result values are
     * determined by the first array.
     *
     * **Note:** Unlike `_.pullAll`, this method returns a new array.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Array
     * @param {Array} array The array to inspect.
     * @param {...Array} [values] The values to exclude.
     * @returns {Array} Returns the new array of filtered values.
     * @see _.without, _.xor
     * @example
     *
     * _.difference([2, 1], [2, 3]);
     * // => [1]
     */
    var difference = baseRest(function(array, values) {
      return isArrayLikeObject(array)
        ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true))
        : [];
    });

    /**
     * This method is like `_.difference` except that it accepts `iteratee` which
     * is invoked for each element of `array` and `values` to generate the criterion
     * by which they're compared. The order and references of result values are
     * determined by the first array. The iteratee is invoked with one argument:
     * (value).
     *
     * **Note:** Unlike `_.pullAllBy`, this method returns a new array.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Array
     * @param {Array} array The array to inspect.
     * @param {...Array} [values] The values to exclude.
     * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
     * @returns {Array} Returns the new array of filtered values.
     * @example
     *
     * _.differenceBy([2.1, 1.2], [2.3, 3.4], Math.floor);
     * // => [1.2]
     *
     * // The `_.property` iteratee shorthand.
     * _.differenceBy([{ 'x': 2 }, { 'x': 1 }], [{ 'x': 1 }], 'x');
     * // => [{ 'x': 2 }]
     */
    var differenceBy = baseRest(function(array, values) {
      var iteratee = last(values);
      if (isArrayLikeObject(iteratee)) {
        iteratee = undefined;
      }
      return isArrayLikeObject(array)
        ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), getIteratee(iteratee, 2))
        : [];
    });

    /**
     * This method is like `_.difference` except that it accepts `comparator`
     * which is invoked to compare elements of `array` to `values`. The order and
     * references of result values are determined by the first array. The comparator
     * is invoked with two arguments: (arrVal, othVal).
     *
     * **Note:** Unlike `_.pullAllWith`, this method returns a new array.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Array
     * @param {Array} array The array to inspect.
     * @param {...Array} [values] The values to exclude.
     * @param {Function} [comparator] The comparator invoked per element.
     * @returns {Array} Returns the new array of filtered values.
     * @example
     *
     * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
     *
     * _.differenceWith(objects, [{ 'x': 1, 'y': 2 }], _.isEqual);
     * // => [{ 'x': 2, 'y': 1 }]
     */
    var differenceWith = baseRest(function(array, values) {
      var comparator = last(values);
      if (isArrayLikeObject(comparator)) {
        comparator = undefined;
      }
      return isArrayLikeObject(array)
        ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), undefined, comparator)
        : [];
    });

    /**
     * Creates a slice of `array` with `n` elements dropped from the beginning.
     *
     * @static
     * @memberOf _
     * @since 0.5.0
     * @category Array
     * @param {Array} array The array to query.
     * @param {number} [n=1] The number of elements to drop.
     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
     * @returns {Array} Returns the slice of `array`.
     * @example
     *
     * _.drop([1, 2, 3]);
     * // => [2, 3]
     *
     * _.drop([1, 2, 3], 2);
     * // => [3]
     *
     * _.drop([1, 2, 3], 5);
     * // => []
     *
     * _.drop([1, 2, 3], 0);
     * // => [1, 2, 3]
     */
    function drop(array, n, guard) {
      var length = array == null ? 0 : array.length;
      if (!length) {
        return [];
      }
      n = (guard || n === undefined) ? 1 : toInteger(n);
      return baseSlice(array, n < 0 ? 0 : n, length);
    }

    /**
     * Creates a slice of `array` with `n` elements dropped from the end.
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category Array
     * @param {Array} array The array to query.
     * @param {number} [n=1] The number of elements to drop.
     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
     * @returns {Array} Returns the slice of `array`.
     * @example
     *
     * _.dropRight([1, 2, 3]);
     * // => [1, 2]
     *
     * _.dropRight([1, 2, 3], 2);
     * // => [1]
     *
     * _.dropRight([1, 2, 3], 5);
     * // => []
     *
     * _.dropRight([1, 2, 3], 0);
     * // => [1, 2, 3]
     */
    function dropRight(array, n, guard) {
      var length = array == null ? 0 : array.length;
      if (!length) {
        return [];
      }
      n = (guard || n === undefined) ? 1 : toInteger(n);
      n = length - n;
      return baseSlice(array, 0, n < 0 ? 0 : n);
    }

    /**
     * Creates a slice of `array` excluding elements dropped from the end.
     * Elements are dropped until `predicate` returns falsey. The predicate is
     * invoked with three arguments: (value, index, array).
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category Array
     * @param {Array} array The array to query.
     * @param {Function} [predicate=_.identity] The function invoked per iteration.
     * @returns {Array} Returns the slice of `array`.
     * @example
     *
     * var users = [
     *   { 'user': 'barney',  'active': true },
     *   { 'user': 'fred',    'active': false },
     *   { 'user': 'pebbles', 'active': false }
     * ];
     *
     * _.dropRightWhile(users, function(o) { return !o.active; });
     * // => objects for ['barney']
     *
     * // The `_.matches` iteratee shorthand.
     * _.dropRightWhile(users, { 'user': 'pebbles', 'active': false });
     * // => objects for ['barney', 'fred']
     *
     * // The `_.matchesProperty` iteratee shorthand.
     * _.dropRightWhile(users, ['active', false]);
     * // => objects for ['barney']
     *
     * // The `_.property` iteratee shorthand.
     * _.dropRightWhile(users, 'active');
     * // => objects for ['barney', 'fred', 'pebbles']
     */
    function dropRightWhile(array, predicate) {
      return (array && array.length)
        ? baseWhile(array, getIteratee(predicate, 3), true, true)
        : [];
    }

    /**
     * Creates a slice of `array` excluding elements dropped from the beginning.
     * Elements are dropped until `predicate` returns falsey. The predicate is
     * invoked with three arguments: (value, index, array).
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category Array
     * @param {Array} array The array to query.
     * @param {Function} [predicate=_.identity] The function invoked per iteration.
     * @returns {Array} Returns the slice of `array`.
     * @example
     *
     * var users = [
     *   { 'user': 'barney',  'active': false },
     *   { 'user': 'fred',    'active': false },
     *   { 'user': 'pebbles', 'active': true }
     * ];
     *
     * _.dropWhile(users, function(o) { return !o.active; });
     * // => objects for ['pebbles']
     *
     * // The `_.matches` iteratee shorthand.
     * _.dropWhile(users, { 'user': 'barney', 'active': false });
     * // => objects for ['fred', 'pebbles']
     *
     * // The `_.matchesProperty` iteratee shorthand.
     * _.dropWhile(users, ['active', false]);
     * // => objects for ['pebbles']
     *
     * // The `_.property` iteratee shorthand.
     * _.dropWhile(users, 'active');
     * // => objects for ['barney', 'fred', 'pebbles']
     */
    function dropWhile(array, predicate) {
      return (array && array.length)
        ? baseWhile(array, getIteratee(predicate, 3), true)
        : [];
    }

    /**
     * Fills elements of `array` with `value` from `start` up to, but not
     * including, `end`.
     *
     * **Note:** This method mutates `array`.
     *
     * @static
     * @memberOf _
     * @since 3.2.0
     * @category Array
     * @param {Array} array The array to fill.
     * @param {*} value The value to fill `array` with.
     * @param {number} [start=0] The start position.
     * @param {number} [end=array.length] The end position.
     * @returns {Array} Returns `array`.
     * @example
     *
     * var array = [1, 2, 3];
     *
     * _.fill(array, 'a');
     * console.log(array);
     * // => ['a', 'a', 'a']
     *
     * _.fill(Array(3), 2);
     * // => [2, 2, 2]
     *
     * _.fill([4, 6, 8, 10], '*', 1, 3);
     * // => [4, '*', '*', 10]
     */
    function fill(array, value, start, end) {
      var length = array == null ? 0 : array.length;
      if (!length) {
        return [];
      }
      if (start && typeof start != 'number' && isIterateeCall(array, value, start)) {
        start = 0;
        end = length;
      }
      return baseFill(array, value, start, end);
    }

    /**
     * This method is like `_.find` except that it returns the index of the first
     * element `predicate` returns truthy for instead of the element itself.
     *
     * @static
     * @memberOf _
     * @since 1.1.0
     * @category Array
     * @param {Array} array The array to inspect.
     * @param {Function} [predicate=_.identity] The function invoked per iteration.
     * @param {number} [fromIndex=0] The index to search from.
     * @returns {number} Returns the index of the found element, else `-1`.
     * @example
     *
     * var users = [
     *   { 'user': 'barney',  'active': false },
     *   { 'user': 'fred',    'active': false },
     *   { 'user': 'pebbles', 'active': true }
     * ];
     *
     * _.findIndex(users, function(o) { return o.user == 'barney'; });
     * // => 0
     *
     * // The `_.matches` iteratee shorthand.
     * _.findIndex(users, { 'user': 'fred', 'active': false });
     * // => 1
     *
     * // The `_.matchesProperty` iteratee shorthand.
     * _.findIndex(users, ['active', false]);
     * // => 0
     *
     * // The `_.property` iteratee shorthand.
     * _.findIndex(users, 'active');
     * // => 2
     */
    function findIndex(array, predicate, fromIndex) {
      var length = array == null ? 0 : array.length;
      if (!length) {
        return -1;
      }
      var index = fromIndex == null ? 0 : toInteger(fromIndex);
      if (index < 0) {
        index = nativeMax(length + index, 0);
      }
      return baseFindIndex(array, getIteratee(predicate, 3), index);
    }

    /**
     * This method is like `_.findIndex` except that it iterates over elements
     * of `collection` from right to left.
     *
     * @static
     * @memberOf _
     * @since 2.0.0
     * @category Array
     * @param {Array} array The array to inspect.
     * @param {Function} [predicate=_.identity] The function invoked per iteration.
     * @param {number} [fromIndex=array.length-1] The index to search from.
     * @returns {number} Returns the index of the found element, else `-1`.
     * @example
     *
     * var users = [
     *   { 'user': 'barney',  'active': true },
     *   { 'user': 'fred',    'active': false },
     *   { 'user': 'pebbles', 'active': false }
     * ];
     *
     * _.findLastIndex(users, function(o) { return o.user == 'pebbles'; });
     * // => 2
     *
     * // The `_.matches` iteratee shorthand.
     * _.findLastIndex(users, { 'user': 'barney', 'active': true });
     * // => 0
     *
     * // The `_.matchesProperty` iteratee shorthand.
     * _.findLastIndex(users, ['active', false]);
     * // => 2
     *
     * // The `_.property` iteratee shorthand.
     * _.findLastIndex(users, 'active');
     * // => 0
     */
    function findLastIndex(array, predicate, fromIndex) {
      var length = array == null ? 0 : array.length;
      if (!length) {
        return -1;
      }
      var index = length - 1;
      if (fromIndex !== undefined) {
        index = toInteger(fromIndex);
        index = fromIndex < 0
          ? nativeMax(length + index, 0)
          : nativeMin(index, length - 1);
      }
      return baseFindIndex(array, getIteratee(predicate, 3), index, true);
    }

    /**
     * Flattens `array` a single level deep.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Array
     * @param {Array} array The array to flatten.
     * @returns {Array} Returns the new flattened array.
     * @example
     *
     * _.flatten([1, [2, [3, [4]], 5]]);
     * // => [1, 2, [3, [4]], 5]
     */
    function flatten(array) {
      var length = array == null ? 0 : array.length;
      return length ? baseFlatten(array, 1) : [];
    }

    /**
     * Recursively flattens `array`.
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category Array
     * @param {Array} array The array to flatten.
     * @returns {Array} Returns the new flattened array.
     * @example
     *
     * _.flattenDeep([1, [2, [3, [4]], 5]]);
     * // => [1, 2, 3, 4, 5]
     */
    function flattenDeep(array) {
      var length = array == null ? 0 : array.length;
      return length ? baseFlatten(array, INFINITY) : [];
    }

    /**
     * Recursively flatten `array` up to `depth` times.
     *
     * @static
     * @memberOf _
     * @since 4.4.0
     * @category Array
     * @param {Array} array The array to flatten.
     * @param {number} [depth=1] The maximum recursion depth.
     * @returns {Array} Returns the new flattened array.
     * @example
     *
     * var array = [1, [2, [3, [4]], 5]];
     *
     * _.flattenDepth(array, 1);
     * // => [1, 2, [3, [4]], 5]
     *
     * _.flattenDepth(array, 2);
     * // => [1, 2, 3, [4], 5]
     */
    function flattenDepth(array, depth) {
      var length = array == null ? 0 : array.length;
      if (!length) {
        return [];
      }
      depth = depth === undefined ? 1 : toInteger(depth);
      return baseFlatten(array, depth);
    }

    /**
     * The inverse of `_.toPairs`; this method returns an object composed
     * from key-value `pairs`.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Array
     * @param {Array} pairs The key-value pairs.
     * @returns {Object} Returns the new object.
     * @example
     *
     * _.fromPairs([['a', 1], ['b', 2]]);
     * // => { 'a': 1, 'b': 2 }
     */
    function fromPairs(pairs) {
      var index = -1,
          length = pairs == null ? 0 : pairs.length,
          result = {};

      while (++index < length) {
        var pair = pairs[index];
        result[pair[0]] = pair[1];
      }
      return result;
    }

    /**
     * Gets the first element of `array`.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @alias first
     * @category Array
     * @param {Array} array The array to query.
     * @returns {*} Returns the first element of `array`.
     * @example
     *
     * _.head([1, 2, 3]);
     * // => 1
     *
     * _.head([]);
     * // => undefined
     */
    function head(array) {
      return (array && array.length) ? array[0] : undefined;
    }

    /**
     * Gets the index at which the first occurrence of `value` is found in `array`
     * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
     * for equality comparisons. If `fromIndex` is negative, it's used as the
     * offset from the end of `array`.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Array
     * @param {Array} array The array to inspect.
     * @param {*} value The value to search for.
     * @param {number} [fromIndex=0] The index to search from.
     * @returns {number} Returns the index of the matched value, else `-1`.
     * @example
     *
     * _.indexOf([1, 2, 1, 2], 2);
     * // => 1
     *
     * // Search from the `fromIndex`.
     * _.indexOf([1, 2, 1, 2], 2, 2);
     * // => 3
     */
    function indexOf(array, value, fromIndex) {
      var length = array == null ? 0 : array.length;
      if (!length) {
        return -1;
      }
      var index = fromIndex == null ? 0 : toInteger(fromIndex);
      if (index < 0) {
        index = nativeMax(length + index, 0);
      }
      return baseIndexOf(array, value, index);
    }

    /**
     * Gets all but the last element of `array`.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Array
     * @param {Array} array The array to query.
     * @returns {Array} Returns the slice of `array`.
     * @example
     *
     * _.initial([1, 2, 3]);
     * // => [1, 2]
     */
    function initial(array) {
      var length = array == null ? 0 : array.length;
      return length ? baseSlice(array, 0, -1) : [];
    }

    /**
     * Creates an array of unique values that are included in all given arrays
     * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
     * for equality comparisons. The order and references of result values are
     * determined by the first array.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Array
     * @param {...Array} [arrays] The arrays to inspect.
     * @returns {Array} Returns the new array of intersecting values.
     * @example
     *
     * _.intersection([2, 1], [2, 3]);
     * // => [2]
     */
    var intersection = baseRest(function(arrays) {
      var mapped = arrayMap(arrays, castArrayLikeObject);
      return (mapped.length && mapped[0] === arrays[0])
        ? baseIntersection(mapped)
        : [];
    });

    /**
     * This method is like `_.intersection` except that it accepts `iteratee`
     * which is invoked for each element of each `arrays` to generate the criterion
     * by which they're compared. The order and references of result values are
     * determined by the first array. The iteratee is invoked with one argument:
     * (value).
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Array
     * @param {...Array} [arrays] The arrays to inspect.
     * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
     * @returns {Array} Returns the new array of intersecting values.
     * @example
     *
     * _.intersectionBy([2.1, 1.2], [2.3, 3.4], Math.floor);
     * // => [2.1]
     *
     * // The `_.property` iteratee shorthand.
     * _.intersectionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');
     * // => [{ 'x': 1 }]
     */
    var intersectionBy = baseRest(function(arrays) {
      var iteratee = last(arrays),
          mapped = arrayMap(arrays, castArrayLikeObject);

      if (iteratee === last(mapped)) {
        iteratee = undefined;
      } else {
        mapped.pop();
      }
      return (mapped.length && mapped[0] === arrays[0])
        ? baseIntersection(mapped, getIteratee(iteratee, 2))
        : [];
    });

    /**
     * This method is like `_.intersection` except that it accepts `comparator`
     * which is invoked to compare elements of `arrays`. The order and references
     * of result values are determined by the first array. The comparator is
     * invoked with two arguments: (arrVal, othVal).
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Array
     * @param {...Array} [arrays] The arrays to inspect.
     * @param {Function} [comparator] The comparator invoked per element.
     * @returns {Array} Returns the new array of intersecting values.
     * @example
     *
     * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
     * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];
     *
     * _.intersectionWith(objects, others, _.isEqual);
     * // => [{ 'x': 1, 'y': 2 }]
     */
    var intersectionWith = baseRest(function(arrays) {
      var comparator = last(arrays),
          mapped = arrayMap(arrays, castArrayLikeObject);

      comparator = typeof comparator == 'function' ? comparator : undefined;
      if (comparator) {
        mapped.pop();
      }
      return (mapped.length && mapped[0] === arrays[0])
        ? baseIntersection(mapped, undefined, comparator)
        : [];
    });

    /**
     * Converts all elements in `array` into a string separated by `separator`.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Array
     * @param {Array} array The array to convert.
     * @param {string} [separator=','] The element separator.
     * @returns {string} Returns the joined string.
     * @example
     *
     * _.join(['a', 'b', 'c'], '~');
     * // => 'a~b~c'
     */
    function join(array, separator) {
      return array == null ? '' : nativeJoin.call(array, separator);
    }

    /**
     * Gets the last element of `array`.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Array
     * @param {Array} array The array to query.
     * @returns {*} Returns the last element of `array`.
     * @example
     *
     * _.last([1, 2, 3]);
     * // => 3
     */
    function last(array) {
      var length = array == null ? 0 : array.length;
      return length ? array[length - 1] : undefined;
    }

    /**
     * This method is like `_.indexOf` except that it iterates over elements of
     * `array` from right to left.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Array
     * @param {Array} array The array to inspect.
     * @param {*} value The value to search for.
     * @param {number} [fromIndex=array.length-1] The index to search from.
     * @returns {number} Returns the index of the matched value, else `-1`.
     * @example
     *
     * _.lastIndexOf([1, 2, 1, 2], 2);
     * // => 3
     *
     * // Search from the `fromIndex`.
     * _.lastIndexOf([1, 2, 1, 2], 2, 2);
     * // => 1
     */
    function lastIndexOf(array, value, fromIndex) {
      var length = array == null ? 0 : array.length;
      if (!length) {
        return -1;
      }
      var index = length;
      if (fromIndex !== undefined) {
        index = toInteger(fromIndex);
        index = index < 0 ? nativeMax(length + index, 0) : nativeMin(index, length - 1);
      }
      return value === value
        ? strictLastIndexOf(array, value, index)
        : baseFindIndex(array, baseIsNaN, index, true);
    }

    /**
     * Gets the element at index `n` of `array`. If `n` is negative, the nth
     * element from the end is returned.
     *
     * @static
     * @memberOf _
     * @since 4.11.0
     * @category Array
     * @param {Array} array The array to query.
     * @param {number} [n=0] The index of the element to return.
     * @returns {*} Returns the nth element of `array`.
     * @example
     *
     * var array = ['a', 'b', 'c', 'd'];
     *
     * _.nth(array, 1);
     * // => 'b'
     *
     * _.nth(array, -2);
     * // => 'c';
     */
    function nth(array, n) {
      return (array && array.length) ? baseNth(array, toInteger(n)) : undefined;
    }

    /**
     * Removes all given values from `array` using
     * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
     * for equality comparisons.
     *
     * **Note:** Unlike `_.without`, this method mutates `array`. Use `_.remove`
     * to remove elements from an array by predicate.
     *
     * @static
     * @memberOf _
     * @since 2.0.0
     * @category Array
     * @param {Array} array The array to modify.
     * @param {...*} [values] The values to remove.
     * @returns {Array} Returns `array`.
     * @example
     *
     * var array = ['a', 'b', 'c', 'a', 'b', 'c'];
     *
     * _.pull(array, 'a', 'c');
     * console.log(array);
     * // => ['b', 'b']
     */
    var pull = baseRest(pullAll);

    /**
     * This method is like `_.pull` except that it accepts an array of values to remove.
     *
     * **Note:** Unlike `_.difference`, this method mutates `array`.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Array
     * @param {Array} array The array to modify.
     * @param {Array} values The values to remove.
     * @returns {Array} Returns `array`.
     * @example
     *
     * var array = ['a', 'b', 'c', 'a', 'b', 'c'];
     *
     * _.pullAll(array, ['a', 'c']);
     * console.log(array);
     * // => ['b', 'b']
     */
    function pullAll(array, values) {
      return (array && array.length && values && values.length)
        ? basePullAll(array, values)
        : array;
    }

    /**
     * This method is like `_.pullAll` except that it accepts `iteratee` which is
     * invoked for each element of `array` and `values` to generate the criterion
     * by which they're compared. The iteratee is invoked with one argument: (value).
     *
     * **Note:** Unlike `_.differenceBy`, this method mutates `array`.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Array
     * @param {Array} array The array to modify.
     * @param {Array} values The values to remove.
     * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
     * @returns {Array} Returns `array`.
     * @example
     *
     * var array = [{ 'x': 1 }, { 'x': 2 }, { 'x': 3 }, { 'x': 1 }];
     *
     * _.pullAllBy(array, [{ 'x': 1 }, { 'x': 3 }], 'x');
     * console.log(array);
     * // => [{ 'x': 2 }]
     */
    function pullAllBy(array, values, iteratee) {
      return (array && array.length && values && values.length)
        ? basePullAll(array, values, getIteratee(iteratee, 2))
        : array;
    }

    /**
     * This method is like `_.pullAll` except that it accepts `comparator` which
     * is invoked to compare elements of `array` to `values`. The comparator is
     * invoked with two arguments: (arrVal, othVal).
     *
     * **Note:** Unlike `_.differenceWith`, this method mutates `array`.
     *
     * @static
     * @memberOf _
     * @since 4.6.0
     * @category Array
     * @param {Array} array The array to modify.
     * @param {Array} values The values to remove.
     * @param {Function} [comparator] The comparator invoked per element.
     * @returns {Array} Returns `array`.
     * @example
     *
     * var array = [{ 'x': 1, 'y': 2 }, { 'x': 3, 'y': 4 }, { 'x': 5, 'y': 6 }];
     *
     * _.pullAllWith(array, [{ 'x': 3, 'y': 4 }], _.isEqual);
     * console.log(array);
     * // => [{ 'x': 1, 'y': 2 }, { 'x': 5, 'y': 6 }]
     */
    function pullAllWith(array, values, comparator) {
      return (array && array.length && values && values.length)
        ? basePullAll(array, values, undefined, comparator)
        : array;
    }

    /**
     * Removes elements from `array` corresponding to `indexes` and returns an
     * array of removed elements.
     *
     * **Note:** Unlike `_.at`, this method mutates `array`.
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category Array
     * @param {Array} array The array to modify.
     * @param {...(number|number[])} [indexes] The indexes of elements to remove.
     * @returns {Array} Returns the new array of removed elements.
     * @example
     *
     * var array = ['a', 'b', 'c', 'd'];
     * var pulled = _.pullAt(array, [1, 3]);
     *
     * console.log(array);
     * // => ['a', 'c']
     *
     * console.log(pulled);
     * // => ['b', 'd']
     */
    var pullAt = flatRest(function(array, indexes) {
      var length = array == null ? 0 : array.length,
          result = baseAt(array, indexes);

      basePullAt(array, arrayMap(indexes, function(index) {
        return isIndex(index, length) ? +index : index;
      }).sort(compareAscending));

      return result;
    });

    /**
     * Removes all elements from `array` that `predicate` returns truthy for
     * and returns an array of the removed elements. The predicate is invoked
     * with three arguments: (value, index, array).
     *
     * **Note:** Unlike `_.filter`, this method mutates `array`. Use `_.pull`
     * to pull elements from an array by value.
     *
     * @static
     * @memberOf _
     * @since 2.0.0
     * @category Array
     * @param {Array} array The array to modify.
     * @param {Function} [predicate=_.identity] The function invoked per iteration.
     * @returns {Array} Returns the new array of removed elements.
     * @example
     *
     * var array = [1, 2, 3, 4];
     * var evens = _.remove(array, function(n) {
     *   return n % 2 == 0;
     * });
     *
     * console.log(array);
     * // => [1, 3]
     *
     * console.log(evens);
     * // => [2, 4]
     */
    function remove(array, predicate) {
      var result = [];
      if (!(array && array.length)) {
        return result;
      }
      var index = -1,
          indexes = [],
          length = array.length;

      predicate = getIteratee(predicate, 3);
      while (++index < length) {
        var value = array[index];
        if (predicate(value, index, array)) {
          result.push(value);
          indexes.push(index);
        }
      }
      basePullAt(array, indexes);
      return result;
    }

    /**
     * Reverses `array` so that the first element becomes the last, the second
     * element becomes the second to last, and so on.
     *
     * **Note:** This method mutates `array` and is based on
     * [`Array#reverse`](https://mdn.io/Array/reverse).
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Array
     * @param {Array} array The array to modify.
     * @returns {Array} Returns `array`.
     * @example
     *
     * var array = [1, 2, 3];
     *
     * _.reverse(array);
     * // => [3, 2, 1]
     *
     * console.log(array);
     * // => [3, 2, 1]
     */
    function reverse(array) {
      return array == null ? array : nativeReverse.call(array);
    }

    /**
     * Creates a slice of `array` from `start` up to, but not including, `end`.
     *
     * **Note:** This method is used instead of
     * [`Array#slice`](https://mdn.io/Array/slice) to ensure dense arrays are
     * returned.
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category Array
     * @param {Array} array The array to slice.
     * @param {number} [start=0] The start position.
     * @param {number} [end=array.length] The end position.
     * @returns {Array} Returns the slice of `array`.
     */
    function slice(array, start, end) {
      var length = array == null ? 0 : array.length;
      if (!length) {
        return [];
      }
      if (end && typeof end != 'number' && isIterateeCall(array, start, end)) {
        start = 0;
        end = length;
      }
      else {
        start = start == null ? 0 : toInteger(start);
        end = end === undefined ? length : toInteger(end);
      }
      return baseSlice(array, start, end);
    }

    /**
     * Uses a binary search to determine the lowest index at which `value`
     * should be inserted into `array` in order to maintain its sort order.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Array
     * @param {Array} array The sorted array to inspect.
     * @param {*} value The value to evaluate.
     * @returns {number} Returns the index at which `value` should be inserted
     *  into `array`.
     * @example
     *
     * _.sortedIndex([30, 50], 40);
     * // => 1
     */
    function sortedIndex(array, value) {
      return baseSortedIndex(array, value);
    }

    /**
     * This method is like `_.sortedIndex` except that it accepts `iteratee`
     * which is invoked for `value` and each element of `array` to compute their
     * sort ranking. The iteratee is invoked with one argument: (value).
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Array
     * @param {Array} array The sorted array to inspect.
     * @param {*} value The value to evaluate.
     * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
     * @returns {number} Returns the index at which `value` should be inserted
     *  into `array`.
     * @example
     *
     * var objects = [{ 'x': 4 }, { 'x': 5 }];
     *
     * _.sortedIndexBy(objects, { 'x': 4 }, function(o) { return o.x; });
     * // => 0
     *
     * // The `_.property` iteratee shorthand.
     * _.sortedIndexBy(objects, { 'x': 4 }, 'x');
     * // => 0
     */
    function sortedIndexBy(array, value, iteratee) {
      return baseSortedIndexBy(array, value, getIteratee(iteratee, 2));
    }

    /**
     * This method is like `_.indexOf` except that it performs a binary
     * search on a sorted `array`.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Array
     * @param {Array} array The array to inspect.
     * @param {*} value The value to search for.
     * @returns {number} Returns the index of the matched value, else `-1`.
     * @example
     *
     * _.sortedIndexOf([4, 5, 5, 5, 6], 5);
     * // => 1
     */
    function sortedIndexOf(array, value) {
      var length = array == null ? 0 : array.length;
      if (length) {
        var index = baseSortedIndex(array, value);
        if (index < length && eq(array[index], value)) {
          return index;
        }
      }
      return -1;
    }

    /**
     * This method is like `_.sortedIndex` except that it returns the highest
     * index at which `value` should be inserted into `array` in order to
     * maintain its sort order.
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category Array
     * @param {Array} array The sorted array to inspect.
     * @param {*} value The value to evaluate.
     * @returns {number} Returns the index at which `value` should be inserted
     *  into `array`.
     * @example
     *
     * _.sortedLastIndex([4, 5, 5, 5, 6], 5);
     * // => 4
     */
    function sortedLastIndex(array, value) {
      return baseSortedIndex(array, value, true);
    }

    /**
     * This method is like `_.sortedLastIndex` except that it accepts `iteratee`
     * which is invoked for `value` and each element of `array` to compute their
     * sort ranking. The iteratee is invoked with one argument: (value).
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Array
     * @param {Array} array The sorted array to inspect.
     * @param {*} value The value to evaluate.
     * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
     * @returns {number} Returns the index at which `value` should be inserted
     *  into `array`.
     * @example
     *
     * var objects = [{ 'x': 4 }, { 'x': 5 }];
     *
     * _.sortedLastIndexBy(objects, { 'x': 4 }, function(o) { return o.x; });
     * // => 1
     *
     * // The `_.property` iteratee shorthand.
     * _.sortedLastIndexBy(objects, { 'x': 4 }, 'x');
     * // => 1
     */
    function sortedLastIndexBy(array, value, iteratee) {
      return baseSortedIndexBy(array, value, getIteratee(iteratee, 2), true);
    }

    /**
     * This method is like `_.lastIndexOf` except that it performs a binary
     * search on a sorted `array`.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Array
     * @param {Array} array The array to inspect.
     * @param {*} value The value to search for.
     * @returns {number} Returns the index of the matched value, else `-1`.
     * @example
     *
     * _.sortedLastIndexOf([4, 5, 5, 5, 6], 5);
     * // => 3
     */
    function sortedLastIndexOf(array, value) {
      var length = array == null ? 0 : array.length;
      if (length) {
        var index = baseSortedIndex(array, value, true) - 1;
        if (eq(array[index], value)) {
          return index;
        }
      }
      return -1;
    }

    /**
     * This method is like `_.uniq` except that it's designed and optimized
     * for sorted arrays.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Array
     * @param {Array} array The array to inspect.
     * @returns {Array} Returns the new duplicate free array.
     * @example
     *
     * _.sortedUniq([1, 1, 2]);
     * // => [1, 2]
     */
    function sortedUniq(array) {
      return (array && array.length)
        ? baseSortedUniq(array)
        : [];
    }

    /**
     * This method is like `_.uniqBy` except that it's designed and optimized
     * for sorted arrays.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Array
     * @param {Array} array The array to inspect.
     * @param {Function} [iteratee] The iteratee invoked per element.
     * @returns {Array} Returns the new duplicate free array.
     * @example
     *
     * _.sortedUniqBy([1.1, 1.2, 2.3, 2.4], Math.floor);
     * // => [1.1, 2.3]
     */
    function sortedUniqBy(array, iteratee) {
      return (array && array.length)
        ? baseSortedUniq(array, getIteratee(iteratee, 2))
        : [];
    }

    /**
     * Gets all but the first element of `array`.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Array
     * @param {Array} array The array to query.
     * @returns {Array} Returns the slice of `array`.
     * @example
     *
     * _.tail([1, 2, 3]);
     * // => [2, 3]
     */
    function tail(array) {
      var length = array == null ? 0 : array.length;
      return length ? baseSlice(array, 1, length) : [];
    }

    /**
     * Creates a slice of `array` with `n` elements taken from the beginning.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Array
     * @param {Array} array The array to query.
     * @param {number} [n=1] The number of elements to take.
     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
     * @returns {Array} Returns the slice of `array`.
     * @example
     *
     * _.take([1, 2, 3]);
     * // => [1]
     *
     * _.take([1, 2, 3], 2);
     * // => [1, 2]
     *
     * _.take([1, 2, 3], 5);
     * // => [1, 2, 3]
     *
     * _.take([1, 2, 3], 0);
     * // => []
     */
    function take(array, n, guard) {
      if (!(array && array.length)) {
        return [];
      }
      n = (guard || n === undefined) ? 1 : toInteger(n);
      return baseSlice(array, 0, n < 0 ? 0 : n);
    }

    /**
     * Creates a slice of `array` with `n` elements taken from the end.
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category Array
     * @param {Array} array The array to query.
     * @param {number} [n=1] The number of elements to take.
     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
     * @returns {Array} Returns the slice of `array`.
     * @example
     *
     * _.takeRight([1, 2, 3]);
     * // => [3]
     *
     * _.takeRight([1, 2, 3], 2);
     * // => [2, 3]
     *
     * _.takeRight([1, 2, 3], 5);
     * // => [1, 2, 3]
     *
     * _.takeRight([1, 2, 3], 0);
     * // => []
     */
    function takeRight(array, n, guard) {
      var length = array == null ? 0 : array.length;
      if (!length) {
        return [];
      }
      n = (guard || n === undefined) ? 1 : toInteger(n);
      n = length - n;
      return baseSlice(array, n < 0 ? 0 : n, length);
    }

    /**
     * Creates a slice of `array` with elements taken from the end. Elements are
     * taken until `predicate` returns falsey. The predicate is invoked with
     * three arguments: (value, index, array).
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category Array
     * @param {Array} array The array to query.
     * @param {Function} [predicate=_.identity] The function invoked per iteration.
     * @returns {Array} Returns the slice of `array`.
     * @example
     *
     * var users = [
     *   { 'user': 'barney',  'active': true },
     *   { 'user': 'fred',    'active': false },
     *   { 'user': 'pebbles', 'active': false }
     * ];
     *
     * _.takeRightWhile(users, function(o) { return !o.active; });
     * // => objects for ['fred', 'pebbles']
     *
     * // The `_.matches` iteratee shorthand.
     * _.takeRightWhile(users, { 'user': 'pebbles', 'active': false });
     * // => objects for ['pebbles']
     *
     * // The `_.matchesProperty` iteratee shorthand.
     * _.takeRightWhile(users, ['active', false]);
     * // => objects for ['fred', 'pebbles']
     *
     * // The `_.property` iteratee shorthand.
     * _.takeRightWhile(users, 'active');
     * // => []
     */
    function takeRightWhile(array, predicate) {
      return (array && array.length)
        ? baseWhile(array, getIteratee(predicate, 3), false, true)
        : [];
    }

    /**
     * Creates a slice of `array` with elements taken from the beginning. Elements
     * are taken until `predicate` returns falsey. The predicate is invoked with
     * three arguments: (value, index, array).
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category Array
     * @param {Array} array The array to query.
     * @param {Function} [predicate=_.identity] The function invoked per iteration.
     * @returns {Array} Returns the slice of `array`.
     * @example
     *
     * var users = [
     *   { 'user': 'barney',  'active': false },
     *   { 'user': 'fred',    'active': false },
     *   { 'user': 'pebbles', 'active': true }
     * ];
     *
     * _.takeWhile(users, function(o) { return !o.active; });
     * // => objects for ['barney', 'fred']
     *
     * // The `_.matches` iteratee shorthand.
     * _.takeWhile(users, { 'user': 'barney', 'active': false });
     * // => objects for ['barney']
     *
     * // The `_.matchesProperty` iteratee shorthand.
     * _.takeWhile(users, ['active', false]);
     * // => objects for ['barney', 'fred']
     *
     * // The `_.property` iteratee shorthand.
     * _.takeWhile(users, 'active');
     * // => []
     */
    function takeWhile(array, predicate) {
      return (array && array.length)
        ? baseWhile(array, getIteratee(predicate, 3))
        : [];
    }

    /**
     * Creates an array of unique values, in order, from all given arrays using
     * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
     * for equality comparisons.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Array
     * @param {...Array} [arrays] The arrays to inspect.
     * @returns {Array} Returns the new array of combined values.
     * @example
     *
     * _.union([2], [1, 2]);
     * // => [2, 1]
     */
    var union = baseRest(function(arrays) {
      return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true));
    });

    /**
     * This method is like `_.union` except that it accepts `iteratee` which is
     * invoked for each element of each `arrays` to generate the criterion by
     * which uniqueness is computed. Result values are chosen from the first
     * array in which the value occurs. The iteratee is invoked with one argument:
     * (value).
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Array
     * @param {...Array} [arrays] The arrays to inspect.
     * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
     * @returns {Array} Returns the new array of combined values.
     * @example
     *
     * _.unionBy([2.1], [1.2, 2.3], Math.floor);
     * // => [2.1, 1.2]
     *
     * // The `_.property` iteratee shorthand.
     * _.unionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');
     * // => [{ 'x': 1 }, { 'x': 2 }]
     */
    var unionBy = baseRest(function(arrays) {
      var iteratee = last(arrays);
      if (isArrayLikeObject(iteratee)) {
        iteratee = undefined;
      }
      return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true), getIteratee(iteratee, 2));
    });

    /**
     * This method is like `_.union` except that it accepts `comparator` which
     * is invoked to compare elements of `arrays`. Result values are chosen from
     * the first array in which the value occurs. The comparator is invoked
     * with two arguments: (arrVal, othVal).
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Array
     * @param {...Array} [arrays] The arrays to inspect.
     * @param {Function} [comparator] The comparator invoked per element.
     * @returns {Array} Returns the new array of combined values.
     * @example
     *
     * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
     * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];
     *
     * _.unionWith(objects, others, _.isEqual);
     * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }]
     */
    var unionWith = baseRest(function(arrays) {
      var comparator = last(arrays);
      comparator = typeof comparator == 'function' ? comparator : undefined;
      return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true), undefined, comparator);
    });

    /**
     * Creates a duplicate-free version of an array, using
     * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
     * for equality comparisons, in which only the first occurrence of each element
     * is kept. The order of result values is determined by the order they occur
     * in the array.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Array
     * @param {Array} array The array to inspect.
     * @returns {Array} Returns the new duplicate free array.
     * @example
     *
     * _.uniq([2, 1, 2]);
     * // => [2, 1]
     */
    function uniq(array) {
      return (array && array.length) ? baseUniq(array) : [];
    }

    /**
     * This method is like `_.uniq` except that it accepts `iteratee` which is
     * invoked for each element in `array` to generate the criterion by which
     * uniqueness is computed. The order of result values is determined by the
     * order they occur in the array. The iteratee is invoked with one argument:
     * (value).
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Array
     * @param {Array} array The array to inspect.
     * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
     * @returns {Array} Returns the new duplicate free array.
     * @example
     *
     * _.uniqBy([2.1, 1.2, 2.3], Math.floor);
     * // => [2.1, 1.2]
     *
     * // The `_.property` iteratee shorthand.
     * _.uniqBy([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');
     * // => [{ 'x': 1 }, { 'x': 2 }]
     */
    function uniqBy(array, iteratee) {
      return (array && array.length) ? baseUniq(array, getIteratee(iteratee, 2)) : [];
    }

    /**
     * This method is like `_.uniq` except that it accepts `comparator` which
     * is invoked to compare elements of `array`. The order of result values is
     * determined by the order they occur in the array.The comparator is invoked
     * with two arguments: (arrVal, othVal).
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Array
     * @param {Array} array The array to inspect.
     * @param {Function} [comparator] The comparator invoked per element.
     * @returns {Array} Returns the new duplicate free array.
     * @example
     *
     * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }];
     *
     * _.uniqWith(objects, _.isEqual);
     * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]
     */
    function uniqWith(array, comparator) {
      comparator = typeof comparator == 'function' ? comparator : undefined;
      return (array && array.length) ? baseUniq(array, undefined, comparator) : [];
    }

    /**
     * This method is like `_.zip` except that it accepts an array of grouped
     * elements and creates an array regrouping the elements to their pre-zip
     * configuration.
     *
     * @static
     * @memberOf _
     * @since 1.2.0
     * @category Array
     * @param {Array} array The array of grouped elements to process.
     * @returns {Array} Returns the new array of regrouped elements.
     * @example
     *
     * var zipped = _.zip(['a', 'b'], [1, 2], [true, false]);
     * // => [['a', 1, true], ['b', 2, false]]
     *
     * _.unzip(zipped);
     * // => [['a', 'b'], [1, 2], [true, false]]
     */
    function unzip(array) {
      if (!(array && array.length)) {
        return [];
      }
      var length = 0;
      array = arrayFilter(array, function(group) {
        if (isArrayLikeObject(group)) {
          length = nativeMax(group.length, length);
          return true;
        }
      });
      return baseTimes(length, function(index) {
        return arrayMap(array, baseProperty(index));
      });
    }

    /**
     * This method is like `_.unzip` except that it accepts `iteratee` to specify
     * how regrouped values should be combined. The iteratee is invoked with the
     * elements of each group: (...group).
     *
     * @static
     * @memberOf _
     * @since 3.8.0
     * @category Array
     * @param {Array} array The array of grouped elements to process.
     * @param {Function} [iteratee=_.identity] The function to combine
     *  regrouped values.
     * @returns {Array} Returns the new array of regrouped elements.
     * @example
     *
     * var zipped = _.zip([1, 2], [10, 20], [100, 200]);
     * // => [[1, 10, 100], [2, 20, 200]]
     *
     * _.unzipWith(zipped, _.add);
     * // => [3, 30, 300]
     */
    function unzipWith(array, iteratee) {
      if (!(array && array.length)) {
        return [];
      }
      var result = unzip(array);
      if (iteratee == null) {
        return result;
      }
      return arrayMap(result, function(group) {
        return apply(iteratee, undefined, group);
      });
    }

    /**
     * Creates an array excluding all given values using
     * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
     * for equality comparisons.
     *
     * **Note:** Unlike `_.pull`, this method returns a new array.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Array
     * @param {Array} array The array to inspect.
     * @param {...*} [values] The values to exclude.
     * @returns {Array} Returns the new array of filtered values.
     * @see _.difference, _.xor
     * @example
     *
     * _.without([2, 1, 2, 3], 1, 2);
     * // => [3]
     */
    var without = baseRest(function(array, values) {
      return isArrayLikeObject(array)
        ? baseDifference(array, values)
        : [];
    });

    /**
     * Creates an array of unique values that is the
     * [symmetric difference](https://en.wikipedia.org/wiki/Symmetric_difference)
     * of the given arrays. The order of result values is determined by the order
     * they occur in the arrays.
     *
     * @static
     * @memberOf _
     * @since 2.4.0
     * @category Array
     * @param {...Array} [arrays] The arrays to inspect.
     * @returns {Array} Returns the new array of filtered values.
     * @see _.difference, _.without
     * @example
     *
     * _.xor([2, 1], [2, 3]);
     * // => [1, 3]
     */
    var xor = baseRest(function(arrays) {
      return baseXor(arrayFilter(arrays, isArrayLikeObject));
    });

    /**
     * This method is like `_.xor` except that it accepts `iteratee` which is
     * invoked for each element of each `arrays` to generate the criterion by
     * which by which they're compared. The order of result values is determined
     * by the order they occur in the arrays. The iteratee is invoked with one
     * argument: (value).
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Array
     * @param {...Array} [arrays] The arrays to inspect.
     * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
     * @returns {Array} Returns the new array of filtered values.
     * @example
     *
     * _.xorBy([2.1, 1.2], [2.3, 3.4], Math.floor);
     * // => [1.2, 3.4]
     *
     * // The `_.property` iteratee shorthand.
     * _.xorBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');
     * // => [{ 'x': 2 }]
     */
    var xorBy = baseRest(function(arrays) {
      var iteratee = last(arrays);
      if (isArrayLikeObject(iteratee)) {
        iteratee = undefined;
      }
      return baseXor(arrayFilter(arrays, isArrayLikeObject), getIteratee(iteratee, 2));
    });

    /**
     * This method is like `_.xor` except that it accepts `comparator` which is
     * invoked to compare elements of `arrays`. The order of result values is
     * determined by the order they occur in the arrays. The comparator is invoked
     * with two arguments: (arrVal, othVal).
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Array
     * @param {...Array} [arrays] The arrays to inspect.
     * @param {Function} [comparator] The comparator invoked per element.
     * @returns {Array} Returns the new array of filtered values.
     * @example
     *
     * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
     * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];
     *
     * _.xorWith(objects, others, _.isEqual);
     * // => [{ 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }]
     */
    var xorWith = baseRest(function(arrays) {
      var comparator = last(arrays);
      comparator = typeof comparator == 'function' ? comparator : undefined;
      return baseXor(arrayFilter(arrays, isArrayLikeObject), undefined, comparator);
    });

    /**
     * Creates an array of grouped elements, the first of which contains the
     * first elements of the given arrays, the second of which contains the
     * second elements of the given arrays, and so on.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Array
     * @param {...Array} [arrays] The arrays to process.
     * @returns {Array} Returns the new array of grouped elements.
     * @example
     *
     * _.zip(['a', 'b'], [1, 2], [true, false]);
     * // => [['a', 1, true], ['b', 2, false]]
     */
    var zip = baseRest(unzip);

    /**
     * This method is like `_.fromPairs` except that it accepts two arrays,
     * one of property identifiers and one of corresponding values.
     *
     * @static
     * @memberOf _
     * @since 0.4.0
     * @category Array
     * @param {Array} [props=[]] The property identifiers.
     * @param {Array} [values=[]] The property values.
     * @returns {Object} Returns the new object.
     * @example
     *
     * _.zipObject(['a', 'b'], [1, 2]);
     * // => { 'a': 1, 'b': 2 }
     */
    function zipObject(props, values) {
      return baseZipObject(props || [], values || [], assignValue);
    }

    /**
     * This method is like `_.zipObject` except that it supports property paths.
     *
     * @static
     * @memberOf _
     * @since 4.1.0
     * @category Array
     * @param {Array} [props=[]] The property identifiers.
     * @param {Array} [values=[]] The property values.
     * @returns {Object} Returns the new object.
     * @example
     *
     * _.zipObjectDeep(['a.b[0].c', 'a.b[1].d'], [1, 2]);
     * // => { 'a': { 'b': [{ 'c': 1 }, { 'd': 2 }] } }
     */
    function zipObjectDeep(props, values) {
      return baseZipObject(props || [], values || [], baseSet);
    }

    /**
     * This method is like `_.zip` except that it accepts `iteratee` to specify
     * how grouped values should be combined. The iteratee is invoked with the
     * elements of each group: (...group).
     *
     * @static
     * @memberOf _
     * @since 3.8.0
     * @category Array
     * @param {...Array} [arrays] The arrays to process.
     * @param {Function} [iteratee=_.identity] The function to combine
     *  grouped values.
     * @returns {Array} Returns the new array of grouped elements.
     * @example
     *
     * _.zipWith([1, 2], [10, 20], [100, 200], function(a, b, c) {
     *   return a + b + c;
     * });
     * // => [111, 222]
     */
    var zipWith = baseRest(function(arrays) {
      var length = arrays.length,
          iteratee = length > 1 ? arrays[length - 1] : undefined;

      iteratee = typeof iteratee == 'function' ? (arrays.pop(), iteratee) : undefined;
      return unzipWith(arrays, iteratee);
    });

    /*------------------------------------------------------------------------*/

    /**
     * Creates a `lodash` wrapper instance that wraps `value` with explicit method
     * chain sequences enabled. The result of such sequences must be unwrapped
     * with `_#value`.
     *
     * @static
     * @memberOf _
     * @since 1.3.0
     * @category Seq
     * @param {*} value The value to wrap.
     * @returns {Object} Returns the new `lodash` wrapper instance.
     * @example
     *
     * var users = [
     *   { 'user': 'barney',  'age': 36 },
     *   { 'user': 'fred',    'age': 40 },
     *   { 'user': 'pebbles', 'age': 1 }
     * ];
     *
     * var youngest = _
     *   .chain(users)
     *   .sortBy('age')
     *   .map(function(o) {
     *     return o.user + ' is ' + o.age;
     *   })
     *   .head()
     *   .value();
     * // => 'pebbles is 1'
     */
    function chain(value) {
      var result = lodash(value);
      result.__chain__ = true;
      return result;
    }

    /**
     * This method invokes `interceptor` and returns `value`. The interceptor
     * is invoked with one argument; (value). The purpose of this method is to
     * "tap into" a method chain sequence in order to modify intermediate results.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Seq
     * @param {*} value The value to provide to `interceptor`.
     * @param {Function} interceptor The function to invoke.
     * @returns {*} Returns `value`.
     * @example
     *
     * _([1, 2, 3])
     *  .tap(function(array) {
     *    // Mutate input array.
     *    array.pop();
     *  })
     *  .reverse()
     *  .value();
     * // => [2, 1]
     */
    function tap(value, interceptor) {
      interceptor(value);
      return value;
    }

    /**
     * This method is like `_.tap` except that it returns the result of `interceptor`.
     * The purpose of this method is to "pass thru" values replacing intermediate
     * results in a method chain sequence.
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category Seq
     * @param {*} value The value to provide to `interceptor`.
     * @param {Function} interceptor The function to invoke.
     * @returns {*} Returns the result of `interceptor`.
     * @example
     *
     * _('  abc  ')
     *  .chain()
     *  .trim()
     *  .thru(function(value) {
     *    return [value];
     *  })
     *  .value();
     * // => ['abc']
     */
    function thru(value, interceptor) {
      return interceptor(value);
    }

    /**
     * This method is the wrapper version of `_.at`.
     *
     * @name at
     * @memberOf _
     * @since 1.0.0
     * @category Seq
     * @param {...(string|string[])} [paths] The property paths to pick.
     * @returns {Object} Returns the new `lodash` wrapper instance.
     * @example
     *
     * var object = { 'a': [{ 'b': { 'c': 3 } }, 4] };
     *
     * _(object).at(['a[0].b.c', 'a[1]']).value();
     * // => [3, 4]
     */
    var wrapperAt = flatRest(function(paths) {
      var length = paths.length,
          start = length ? paths[0] : 0,
          value = this.__wrapped__,
          interceptor = function(object) { return baseAt(object, paths); };

      if (length > 1 || this.__actions__.length ||
          !(value instanceof LazyWrapper) || !isIndex(start)) {
        return this.thru(interceptor);
      }
      value = value.slice(start, +start + (length ? 1 : 0));
      value.__actions__.push({
        'func': thru,
        'args': [interceptor],
        'thisArg': undefined
      });
      return new LodashWrapper(value, this.__chain__).thru(function(array) {
        if (length && !array.length) {
          array.push(undefined);
        }
        return array;
      });
    });

    /**
     * Creates a `lodash` wrapper instance with explicit method chain sequences enabled.
     *
     * @name chain
     * @memberOf _
     * @since 0.1.0
     * @category Seq
     * @returns {Object} Returns the new `lodash` wrapper instance.
     * @example
     *
     * var users = [
     *   { 'user': 'barney', 'age': 36 },
     *   { 'user': 'fred',   'age': 40 }
     * ];
     *
     * // A sequence without explicit chaining.
     * _(users).head();
     * // => { 'user': 'barney', 'age': 36 }
     *
     * // A sequence with explicit chaining.
     * _(users)
     *   .chain()
     *   .head()
     *   .pick('user')
     *   .value();
     * // => { 'user': 'barney' }
     */
    function wrapperChain() {
      return chain(this);
    }

    /**
     * Executes the chain sequence and returns the wrapped result.
     *
     * @name commit
     * @memberOf _
     * @since 3.2.0
     * @category Seq
     * @returns {Object} Returns the new `lodash` wrapper instance.
     * @example
     *
     * var array = [1, 2];
     * var wrapped = _(array).push(3);
     *
     * console.log(array);
     * // => [1, 2]
     *
     * wrapped = wrapped.commit();
     * console.log(array);
     * // => [1, 2, 3]
     *
     * wrapped.last();
     * // => 3
     *
     * console.log(array);
     * // => [1, 2, 3]
     */
    function wrapperCommit() {
      return new LodashWrapper(this.value(), this.__chain__);
    }

    /**
     * Gets the next value on a wrapped object following the
     * [iterator protocol](https://mdn.io/iteration_protocols#iterator).
     *
     * @name next
     * @memberOf _
     * @since 4.0.0
     * @category Seq
     * @returns {Object} Returns the next iterator value.
     * @example
     *
     * var wrapped = _([1, 2]);
     *
     * wrapped.next();
     * // => { 'done': false, 'value': 1 }
     *
     * wrapped.next();
     * // => { 'done': false, 'value': 2 }
     *
     * wrapped.next();
     * // => { 'done': true, 'value': undefined }
     */
    function wrapperNext() {
      if (this.__values__ === undefined) {
        this.__values__ = toArray(this.value());
      }
      var done = this.__index__ >= this.__values__.length,
          value = done ? undefined : this.__values__[this.__index__++];

      return { 'done': done, 'value': value };
    }

    /**
     * Enables the wrapper to be iterable.
     *
     * @name Symbol.iterator
     * @memberOf _
     * @since 4.0.0
     * @category Seq
     * @returns {Object} Returns the wrapper object.
     * @example
     *
     * var wrapped = _([1, 2]);
     *
     * wrapped[Symbol.iterator]() === wrapped;
     * // => true
     *
     * Array.from(wrapped);
     * // => [1, 2]
     */
    function wrapperToIterator() {
      return this;
    }

    /**
     * Creates a clone of the chain sequence planting `value` as the wrapped value.
     *
     * @name plant
     * @memberOf _
     * @since 3.2.0
     * @category Seq
     * @param {*} value The value to plant.
     * @returns {Object} Returns the new `lodash` wrapper instance.
     * @example
     *
     * function square(n) {
     *   return n * n;
     * }
     *
     * var wrapped = _([1, 2]).map(square);
     * var other = wrapped.plant([3, 4]);
     *
     * other.value();
     * // => [9, 16]
     *
     * wrapped.value();
     * // => [1, 4]
     */
    function wrapperPlant(value) {
      var result,
          parent = this;

      while (parent instanceof baseLodash) {
        var clone = wrapperClone(parent);
        clone.__index__ = 0;
        clone.__values__ = undefined;
        if (result) {
          previous.__wrapped__ = clone;
        } else {
          result = clone;
        }
        var previous = clone;
        parent = parent.__wrapped__;
      }
      previous.__wrapped__ = value;
      return result;
    }

    /**
     * This method is the wrapper version of `_.reverse`.
     *
     * **Note:** This method mutates the wrapped array.
     *
     * @name reverse
     * @memberOf _
     * @since 0.1.0
     * @category Seq
     * @returns {Object} Returns the new `lodash` wrapper instance.
     * @example
     *
     * var array = [1, 2, 3];
     *
     * _(array).reverse().value()
     * // => [3, 2, 1]
     *
     * console.log(array);
     * // => [3, 2, 1]
     */
    function wrapperReverse() {
      var value = this.__wrapped__;
      if (value instanceof LazyWrapper) {
        var wrapped = value;
        if (this.__actions__.length) {
          wrapped = new LazyWrapper(this);
        }
        wrapped = wrapped.reverse();
        wrapped.__actions__.push({
          'func': thru,
          'args': [reverse],
          'thisArg': undefined
        });
        return new LodashWrapper(wrapped, this.__chain__);
      }
      return this.thru(reverse);
    }

    /**
     * Executes the chain sequence to resolve the unwrapped value.
     *
     * @name value
     * @memberOf _
     * @since 0.1.0
     * @alias toJSON, valueOf
     * @category Seq
     * @returns {*} Returns the resolved unwrapped value.
     * @example
     *
     * _([1, 2, 3]).value();
     * // => [1, 2, 3]
     */
    function wrapperValue() {
      return baseWrapperValue(this.__wrapped__, this.__actions__);
    }

    /*------------------------------------------------------------------------*/

    /**
     * Creates an object composed of keys generated from the results of running
     * each element of `collection` thru `iteratee`. The corresponding value of
     * each key is the number of times the key was returned by `iteratee`. The
     * iteratee is invoked with one argument: (value).
     *
     * @static
     * @memberOf _
     * @since 0.5.0
     * @category Collection
     * @param {Array|Object} collection The collection to iterate over.
     * @param {Function} [iteratee=_.identity] The iteratee to transform keys.
     * @returns {Object} Returns the composed aggregate object.
     * @example
     *
     * _.countBy([6.1, 4.2, 6.3], Math.floor);
     * // => { '4': 1, '6': 2 }
     *
     * // The `_.property` iteratee shorthand.
     * _.countBy(['one', 'two', 'three'], 'length');
     * // => { '3': 2, '5': 1 }
     */
    var countBy = createAggregator(function(result, value, key) {
      if (hasOwnProperty.call(result, key)) {
        ++result[key];
      } else {
        baseAssignValue(result, key, 1);
      }
    });

    /**
     * Checks if `predicate` returns truthy for **all** elements of `collection`.
     * Iteration is stopped once `predicate` returns falsey. The predicate is
     * invoked with three arguments: (value, index|key, collection).
     *
     * **Note:** This method returns `true` for
     * [empty collections](https://en.wikipedia.org/wiki/Empty_set) because
     * [everything is true](https://en.wikipedia.org/wiki/Vacuous_truth) of
     * elements of empty collections.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Collection
     * @param {Array|Object} collection The collection to iterate over.
     * @param {Function} [predicate=_.identity] The function invoked per iteration.
     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
     * @returns {boolean} Returns `true` if all elements pass the predicate check,
     *  else `false`.
     * @example
     *
     * _.every([true, 1, null, 'yes'], Boolean);
     * // => false
     *
     * var users = [
     *   { 'user': 'barney', 'age': 36, 'active': false },
     *   { 'user': 'fred',   'age': 40, 'active': false }
     * ];
     *
     * // The `_.matches` iteratee shorthand.
     * _.every(users, { 'user': 'barney', 'active': false });
     * // => false
     *
     * // The `_.matchesProperty` iteratee shorthand.
     * _.every(users, ['active', false]);
     * // => true
     *
     * // The `_.property` iteratee shorthand.
     * _.every(users, 'active');
     * // => false
     */
    function every(collection, predicate, guard) {
      var func = isArray(collection) ? arrayEvery : baseEvery;
      if (guard && isIterateeCall(collection, predicate, guard)) {
        predicate = undefined;
      }
      return func(collection, getIteratee(predicate, 3));
    }

    /**
     * Iterates over elements of `collection`, returning an array of all elements
     * `predicate` returns truthy for. The predicate is invoked with three
     * arguments: (value, index|key, collection).
     *
     * **Note:** Unlike `_.remove`, this method returns a new array.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Collection
     * @param {Array|Object} collection The collection to iterate over.
     * @param {Function} [predicate=_.identity] The function invoked per iteration.
     * @returns {Array} Returns the new filtered array.
     * @see _.reject
     * @example
     *
     * var users = [
     *   { 'user': 'barney', 'age': 36, 'active': true },
     *   { 'user': 'fred',   'age': 40, 'active': false }
     * ];
     *
     * _.filter(users, function(o) { return !o.active; });
     * // => objects for ['fred']
     *
     * // The `_.matches` iteratee shorthand.
     * _.filter(users, { 'age': 36, 'active': true });
     * // => objects for ['barney']
     *
     * // The `_.matchesProperty` iteratee shorthand.
     * _.filter(users, ['active', false]);
     * // => objects for ['fred']
     *
     * // The `_.property` iteratee shorthand.
     * _.filter(users, 'active');
     * // => objects for ['barney']
     */
    function filter(collection, predicate) {
      var func = isArray(collection) ? arrayFilter : baseFilter;
      return func(collection, getIteratee(predicate, 3));
    }

    /**
     * Iterates over elements of `collection`, returning the first element
     * `predicate` returns truthy for. The predicate is invoked with three
     * arguments: (value, index|key, collection).
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Collection
     * @param {Array|Object} collection The collection to inspect.
     * @param {Function} [predicate=_.identity] The function invoked per iteration.
     * @param {number} [fromIndex=0] The index to search from.
     * @returns {*} Returns the matched element, else `undefined`.
     * @example
     *
     * var users = [
     *   { 'user': 'barney',  'age': 36, 'active': true },
     *   { 'user': 'fred',    'age': 40, 'active': false },
     *   { 'user': 'pebbles', 'age': 1,  'active': true }
     * ];
     *
     * _.find(users, function(o) { return o.age < 40; });
     * // => object for 'barney'
     *
     * // The `_.matches` iteratee shorthand.
     * _.find(users, { 'age': 1, 'active': true });
     * // => object for 'pebbles'
     *
     * // The `_.matchesProperty` iteratee shorthand.
     * _.find(users, ['active', false]);
     * // => object for 'fred'
     *
     * // The `_.property` iteratee shorthand.
     * _.find(users, 'active');
     * // => object for 'barney'
     */
    var find = createFind(findIndex);

    /**
     * This method is like `_.find` except that it iterates over elements of
     * `collection` from right to left.
     *
     * @static
     * @memberOf _
     * @since 2.0.0
     * @category Collection
     * @param {Array|Object} collection The collection to inspect.
     * @param {Function} [predicate=_.identity] The function invoked per iteration.
     * @param {number} [fromIndex=collection.length-1] The index to search from.
     * @returns {*} Returns the matched element, else `undefined`.
     * @example
     *
     * _.findLast([1, 2, 3, 4], function(n) {
     *   return n % 2 == 1;
     * });
     * // => 3
     */
    var findLast = createFind(findLastIndex);

    /**
     * Creates a flattened array of values by running each element in `collection`
     * thru `iteratee` and flattening the mapped results. The iteratee is invoked
     * with three arguments: (value, index|key, collection).
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Collection
     * @param {Array|Object} collection The collection to iterate over.
     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
     * @returns {Array} Returns the new flattened array.
     * @example
     *
     * function duplicate(n) {
     *   return [n, n];
     * }
     *
     * _.flatMap([1, 2], duplicate);
     * // => [1, 1, 2, 2]
     */
    function flatMap(collection, iteratee) {
      return baseFlatten(map(collection, iteratee), 1);
    }

    /**
     * This method is like `_.flatMap` except that it recursively flattens the
     * mapped results.
     *
     * @static
     * @memberOf _
     * @since 4.7.0
     * @category Collection
     * @param {Array|Object} collection The collection to iterate over.
     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
     * @returns {Array} Returns the new flattened array.
     * @example
     *
     * function duplicate(n) {
     *   return [[[n, n]]];
     * }
     *
     * _.flatMapDeep([1, 2], duplicate);
     * // => [1, 1, 2, 2]
     */
    function flatMapDeep(collection, iteratee) {
      return baseFlatten(map(collection, iteratee), INFINITY);
    }

    /**
     * This method is like `_.flatMap` except that it recursively flattens the
     * mapped results up to `depth` times.
     *
     * @static
     * @memberOf _
     * @since 4.7.0
     * @category Collection
     * @param {Array|Object} collection The collection to iterate over.
     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
     * @param {number} [depth=1] The maximum recursion depth.
     * @returns {Array} Returns the new flattened array.
     * @example
     *
     * function duplicate(n) {
     *   return [[[n, n]]];
     * }
     *
     * _.flatMapDepth([1, 2], duplicate, 2);
     * // => [[1, 1], [2, 2]]
     */
    function flatMapDepth(collection, iteratee, depth) {
      depth = depth === undefined ? 1 : toInteger(depth);
      return baseFlatten(map(collection, iteratee), depth);
    }

    /**
     * Iterates over elements of `collection` and invokes `iteratee` for each element.
     * The iteratee is invoked with three arguments: (value, index|key, collection).
     * Iteratee functions may exit iteration early by explicitly returning `false`.
     *
     * **Note:** As with other "Collections" methods, objects with a "length"
     * property are iterated like arrays. To avoid this behavior use `_.forIn`
     * or `_.forOwn` for object iteration.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @alias each
     * @category Collection
     * @param {Array|Object} collection The collection to iterate over.
     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
     * @returns {Array|Object} Returns `collection`.
     * @see _.forEachRight
     * @example
     *
     * _.forEach([1, 2], function(value) {
     *   console.log(value);
     * });
     * // => Logs `1` then `2`.
     *
     * _.forEach({ 'a': 1, 'b': 2 }, function(value, key) {
     *   console.log(key);
     * });
     * // => Logs 'a' then 'b' (iteration order is not guaranteed).
     */
    function forEach(collection, iteratee) {
      var func = isArray(collection) ? arrayEach : baseEach;
      return func(collection, getIteratee(iteratee, 3));
    }

    /**
     * This method is like `_.forEach` except that it iterates over elements of
     * `collection` from right to left.
     *
     * @static
     * @memberOf _
     * @since 2.0.0
     * @alias eachRight
     * @category Collection
     * @param {Array|Object} collection The collection to iterate over.
     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
     * @returns {Array|Object} Returns `collection`.
     * @see _.forEach
     * @example
     *
     * _.forEachRight([1, 2], function(value) {
     *   console.log(value);
     * });
     * // => Logs `2` then `1`.
     */
    function forEachRight(collection, iteratee) {
      var func = isArray(collection) ? arrayEachRight : baseEachRight;
      return func(collection, getIteratee(iteratee, 3));
    }

    /**
     * Creates an object composed of keys generated from the results of running
     * each element of `collection` thru `iteratee`. The order of grouped values
     * is determined by the order they occur in `collection`. The corresponding
     * value of each key is an array of elements responsible for generating the
     * key. The iteratee is invoked with one argument: (value).
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Collection
     * @param {Array|Object} collection The collection to iterate over.
     * @param {Function} [iteratee=_.identity] The iteratee to transform keys.
     * @returns {Object} Returns the composed aggregate object.
     * @example
     *
     * _.groupBy([6.1, 4.2, 6.3], Math.floor);
     * // => { '4': [4.2], '6': [6.1, 6.3] }
     *
     * // The `_.property` iteratee shorthand.
     * _.groupBy(['one', 'two', 'three'], 'length');
     * // => { '3': ['one', 'two'], '5': ['three'] }
     */
    var groupBy = createAggregator(function(result, value, key) {
      if (hasOwnProperty.call(result, key)) {
        result[key].push(value);
      } else {
        baseAssignValue(result, key, [value]);
      }
    });

    /**
     * Checks if `value` is in `collection`. If `collection` is a string, it's
     * checked for a substring of `value`, otherwise
     * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
     * is used for equality comparisons. If `fromIndex` is negative, it's used as
     * the offset from the end of `collection`.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Collection
     * @param {Array|Object|string} collection The collection to inspect.
     * @param {*} value The value to search for.
     * @param {number} [fromIndex=0] The index to search from.
     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`.
     * @returns {boolean} Returns `true` if `value` is found, else `false`.
     * @example
     *
     * _.includes([1, 2, 3], 1);
     * // => true
     *
     * _.includes([1, 2, 3], 1, 2);
     * // => false
     *
     * _.includes({ 'a': 1, 'b': 2 }, 1);
     * // => true
     *
     * _.includes('abcd', 'bc');
     * // => true
     */
    function includes(collection, value, fromIndex, guard) {
      collection = isArrayLike(collection) ? collection : values(collection);
      fromIndex = (fromIndex && !guard) ? toInteger(fromIndex) : 0;

      var length = collection.length;
      if (fromIndex < 0) {
        fromIndex = nativeMax(length + fromIndex, 0);
      }
      return isString(collection)
        ? (fromIndex <= length && collection.indexOf(value, fromIndex) > -1)
        : (!!length && baseIndexOf(collection, value, fromIndex) > -1);
    }

    /**
     * Invokes the method at `path` of each element in `collection`, returning
     * an array of the results of each invoked method. Any additional arguments
     * are provided to each invoked method. If `path` is a function, it's invoked
     * for, and `this` bound to, each element in `collection`.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Collection
     * @param {Array|Object} collection The collection to iterate over.
     * @param {Array|Function|string} path The path of the method to invoke or
     *  the function invoked per iteration.
     * @param {...*} [args] The arguments to invoke each method with.
     * @returns {Array} Returns the array of results.
     * @example
     *
     * _.invokeMap([[5, 1, 7], [3, 2, 1]], 'sort');
     * // => [[1, 5, 7], [1, 2, 3]]
     *
     * _.invokeMap([123, 456], String.prototype.split, '');
     * // => [['1', '2', '3'], ['4', '5', '6']]
     */
    var invokeMap = baseRest(function(collection, path, args) {
      var index = -1,
          isFunc = typeof path == 'function',
          result = isArrayLike(collection) ? Array(collection.length) : [];

      baseEach(collection, function(value) {
        result[++index] = isFunc ? apply(path, value, args) : baseInvoke(value, path, args);
      });
      return result;
    });

    /**
     * Creates an object composed of keys generated from the results of running
     * each element of `collection` thru `iteratee`. The corresponding value of
     * each key is the last element responsible for generating the key. The
     * iteratee is invoked with one argument: (value).
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Collection
     * @param {Array|Object} collection The collection to iterate over.
     * @param {Function} [iteratee=_.identity] The iteratee to transform keys.
     * @returns {Object} Returns the composed aggregate object.
     * @example
     *
     * var array = [
     *   { 'dir': 'left', 'code': 97 },
     *   { 'dir': 'right', 'code': 100 }
     * ];
     *
     * _.keyBy(array, function(o) {
     *   return String.fromCharCode(o.code);
     * });
     * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
     *
     * _.keyBy(array, 'dir');
     * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } }
     */
    var keyBy = createAggregator(function(result, value, key) {
      baseAssignValue(result, key, value);
    });

    /**
     * Creates an array of values by running each element in `collection` thru
     * `iteratee`. The iteratee is invoked with three arguments:
     * (value, index|key, collection).
     *
     * Many lodash methods are guarded to work as iteratees for methods like
     * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`.
     *
     * The guarded methods are:
     * `ary`, `chunk`, `curry`, `curryRight`, `drop`, `dropRight`, `every`,
     * `fill`, `invert`, `parseInt`, `random`, `range`, `rangeRight`, `repeat`,
     * `sampleSize`, `slice`, `some`, `sortBy`, `split`, `take`, `takeRight`,
     * `template`, `trim`, `trimEnd`, `trimStart`, and `words`
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Collection
     * @param {Array|Object} collection The collection to iterate over.
     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
     * @returns {Array} Returns the new mapped array.
     * @example
     *
     * function square(n) {
     *   return n * n;
     * }
     *
     * _.map([4, 8], square);
     * // => [16, 64]
     *
     * _.map({ 'a': 4, 'b': 8 }, square);
     * // => [16, 64] (iteration order is not guaranteed)
     *
     * var users = [
     *   { 'user': 'barney' },
     *   { 'user': 'fred' }
     * ];
     *
     * // The `_.property` iteratee shorthand.
     * _.map(users, 'user');
     * // => ['barney', 'fred']
     */
    function map(collection, iteratee) {
      var func = isArray(collection) ? arrayMap : baseMap;
      return func(collection, getIteratee(iteratee, 3));
    }

    /**
     * This method is like `_.sortBy` except that it allows specifying the sort
     * orders of the iteratees to sort by. 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.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Collection
     * @param {Array|Object} collection The collection to iterate over.
     * @param {Array[]|Function[]|Object[]|string[]} [iteratees=[_.identity]]
     *  The iteratees to sort by.
     * @param {string[]} [orders] The sort orders of `iteratees`.
     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`.
     * @returns {Array} Returns the new sorted array.
     * @example
     *
     * var users = [
     *   { 'user': 'fred',   'age': 48 },
     *   { 'user': 'barney', 'age': 34 },
     *   { 'user': 'fred',   'age': 40 },
     *   { 'user': 'barney', 'age': 36 }
     * ];
     *
     * // Sort by `user` in ascending order and by `age` in descending order.
     * _.orderBy(users, ['user', 'age'], ['asc', 'desc']);
     * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]
     */
    function orderBy(collection, iteratees, orders, guard) {
      if (collection == null) {
        return [];
      }
      if (!isArray(iteratees)) {
        iteratees = iteratees == null ? [] : [iteratees];
      }
      orders = guard ? undefined : orders;
      if (!isArray(orders)) {
        orders = orders == null ? [] : [orders];
      }
      return baseOrderBy(collection, iteratees, orders);
    }

    /**
     * Creates an array of elements split into two groups, the first of which
     * contains elements `predicate` returns truthy for, the second of which
     * contains elements `predicate` returns falsey for. The predicate is
     * invoked with one argument: (value).
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category Collection
     * @param {Array|Object} collection The collection to iterate over.
     * @param {Function} [predicate=_.identity] The function invoked per iteration.
     * @returns {Array} Returns the array of grouped elements.
     * @example
     *
     * var users = [
     *   { 'user': 'barney',  'age': 36, 'active': false },
     *   { 'user': 'fred',    'age': 40, 'active': true },
     *   { 'user': 'pebbles', 'age': 1,  'active': false }
     * ];
     *
     * _.partition(users, function(o) { return o.active; });
     * // => objects for [['fred'], ['barney', 'pebbles']]
     *
     * // The `_.matches` iteratee shorthand.
     * _.partition(users, { 'age': 1, 'active': false });
     * // => objects for [['pebbles'], ['barney', 'fred']]
     *
     * // The `_.matchesProperty` iteratee shorthand.
     * _.partition(users, ['active', false]);
     * // => objects for [['barney', 'pebbles'], ['fred']]
     *
     * // The `_.property` iteratee shorthand.
     * _.partition(users, 'active');
     * // => objects for [['fred'], ['barney', 'pebbles']]
     */
    var partition = createAggregator(function(result, value, key) {
      result[key ? 0 : 1].push(value);
    }, function() { return [[], []]; });

    /**
     * Reduces `collection` to a value which is the accumulated result of running
     * each element in `collection` thru `iteratee`, where each successive
     * invocation is supplied the return value of the previous. If `accumulator`
     * is not given, the first element of `collection` is used as the initial
     * value. The iteratee is invoked with four arguments:
     * (accumulator, value, index|key, collection).
     *
     * Many lodash methods are guarded to work as iteratees for methods like
     * `_.reduce`, `_.reduceRight`, and `_.transform`.
     *
     * The guarded methods are:
     * `assign`, `defaults`, `defaultsDeep`, `includes`, `merge`, `orderBy`,
     * and `sortBy`
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Collection
     * @param {Array|Object} collection The collection to iterate over.
     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
     * @param {*} [accumulator] The initial value.
     * @returns {*} Returns the accumulated value.
     * @see _.reduceRight
     * @example
     *
     * _.reduce([1, 2], function(sum, n) {
     *   return sum + n;
     * }, 0);
     * // => 3
     *
     * _.reduce({ 'a': 1, 'b': 2, 'c': 1 }, function(result, value, key) {
     *   (result[value] || (result[value] = [])).push(key);
     *   return result;
     * }, {});
     * // => { '1': ['a', 'c'], '2': ['b'] } (iteration order is not guaranteed)
     */
    function reduce(collection, iteratee, accumulator) {
      var func = isArray(collection) ? arrayReduce : baseReduce,
          initAccum = arguments.length < 3;

      return func(collection, getIteratee(iteratee, 4), accumulator, initAccum, baseEach);
    }

    /**
     * This method is like `_.reduce` except that it iterates over elements of
     * `collection` from right to left.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Collection
     * @param {Array|Object} collection The collection to iterate over.
     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
     * @param {*} [accumulator] The initial value.
     * @returns {*} Returns the accumulated value.
     * @see _.reduce
     * @example
     *
     * var array = [[0, 1], [2, 3], [4, 5]];
     *
     * _.reduceRight(array, function(flattened, other) {
     *   return flattened.concat(other);
     * }, []);
     * // => [4, 5, 2, 3, 0, 1]
     */
    function reduceRight(collection, iteratee, accumulator) {
      var func = isArray(collection) ? arrayReduceRight : baseReduce,
          initAccum = arguments.length < 3;

      return func(collection, getIteratee(iteratee, 4), accumulator, initAccum, baseEachRight);
    }

    /**
     * The opposite of `_.filter`; this method returns the elements of `collection`
     * that `predicate` does **not** return truthy for.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Collection
     * @param {Array|Object} collection The collection to iterate over.
     * @param {Function} [predicate=_.identity] The function invoked per iteration.
     * @returns {Array} Returns the new filtered array.
     * @see _.filter
     * @example
     *
     * var users = [
     *   { 'user': 'barney', 'age': 36, 'active': false },
     *   { 'user': 'fred',   'age': 40, 'active': true }
     * ];
     *
     * _.reject(users, function(o) { return !o.active; });
     * // => objects for ['fred']
     *
     * // The `_.matches` iteratee shorthand.
     * _.reject(users, { 'age': 40, 'active': true });
     * // => objects for ['barney']
     *
     * // The `_.matchesProperty` iteratee shorthand.
     * _.reject(users, ['active', false]);
     * // => objects for ['fred']
     *
     * // The `_.property` iteratee shorthand.
     * _.reject(users, 'active');
     * // => objects for ['barney']
     */
    function reject(collection, predicate) {
      var func = isArray(collection) ? arrayFilter : baseFilter;
      return func(collection, negate(getIteratee(predicate, 3)));
    }

    /**
     * Gets a random element from `collection`.
     *
     * @static
     * @memberOf _
     * @since 2.0.0
     * @category Collection
     * @param {Array|Object} collection The collection to sample.
     * @returns {*} Returns the random element.
     * @example
     *
     * _.sample([1, 2, 3, 4]);
     * // => 2
     */
    function sample(collection) {
      var func = isArray(collection) ? arraySample : baseSample;
      return func(collection);
    }

    /**
     * Gets `n` random elements at unique keys from `collection` up to the
     * size of `collection`.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Collection
     * @param {Array|Object} collection The collection to sample.
     * @param {number} [n=1] The number of elements to sample.
     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
     * @returns {Array} Returns the random elements.
     * @example
     *
     * _.sampleSize([1, 2, 3], 2);
     * // => [3, 1]
     *
     * _.sampleSize([1, 2, 3], 4);
     * // => [2, 3, 1]
     */
    function sampleSize(collection, n, guard) {
      if ((guard ? isIterateeCall(collection, n, guard) : n === undefined)) {
        n = 1;
      } else {
        n = toInteger(n);
      }
      var func = isArray(collection) ? arraySampleSize : baseSampleSize;
      return func(collection, n);
    }

    /**
     * Creates an array of shuffled values, using a version of the
     * [Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher-Yates_shuffle).
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Collection
     * @param {Array|Object} collection The collection to shuffle.
     * @returns {Array} Returns the new shuffled array.
     * @example
     *
     * _.shuffle([1, 2, 3, 4]);
     * // => [4, 1, 3, 2]
     */
    function shuffle(collection) {
      var func = isArray(collection) ? arrayShuffle : baseShuffle;
      return func(collection);
    }

    /**
     * Gets the size of `collection` by returning its length for array-like
     * values or the number of own enumerable string keyed properties for objects.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Collection
     * @param {Array|Object|string} collection The collection to inspect.
     * @returns {number} Returns the collection size.
     * @example
     *
     * _.size([1, 2, 3]);
     * // => 3
     *
     * _.size({ 'a': 1, 'b': 2 });
     * // => 2
     *
     * _.size('pebbles');
     * // => 7
     */
    function size(collection) {
      if (collection == null) {
        return 0;
      }
      if (isArrayLike(collection)) {
        return isString(collection) ? stringSize(collection) : collection.length;
      }
      var tag = getTag(collection);
      if (tag == mapTag || tag == setTag) {
        return collection.size;
      }
      return baseKeys(collection).length;
    }

    /**
     * Checks if `predicate` returns truthy for **any** element of `collection`.
     * Iteration is stopped once `predicate` returns truthy. The predicate is
     * invoked with three arguments: (value, index|key, collection).
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Collection
     * @param {Array|Object} collection The collection to iterate over.
     * @param {Function} [predicate=_.identity] The function invoked per iteration.
     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
     * @returns {boolean} Returns `true` if any element passes the predicate check,
     *  else `false`.
     * @example
     *
     * _.some([null, 0, 'yes', false], Boolean);
     * // => true
     *
     * var users = [
     *   { 'user': 'barney', 'active': true },
     *   { 'user': 'fred',   'active': false }
     * ];
     *
     * // The `_.matches` iteratee shorthand.
     * _.some(users, { 'user': 'barney', 'active': false });
     * // => false
     *
     * // The `_.matchesProperty` iteratee shorthand.
     * _.some(users, ['active', false]);
     * // => true
     *
     * // The `_.property` iteratee shorthand.
     * _.some(users, 'active');
     * // => true
     */
    function some(collection, predicate, guard) {
      var func = isArray(collection) ? arraySome : baseSome;
      if (guard && isIterateeCall(collection, predicate, guard)) {
        predicate = undefined;
      }
      return func(collection, getIteratee(predicate, 3));
    }

    /**
     * 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]]
     */
    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), []);
    });

    /*------------------------------------------------------------------------*/

    /**
     * Gets the timestamp of the number of milliseconds that have elapsed since
     * the Unix epoch (1 January 1970 00:00:00 UTC).
     *
     * @static
     * @memberOf _
     * @since 2.4.0
     * @category Date
     * @returns {number} Returns the timestamp.
     * @example
     *
     * _.defer(function(stamp) {
     *   console.log(_.now() - stamp);
     * }, _.now());
     * // => Logs the number of milliseconds it took for the deferred invocation.
     */
    var now = ctxNow || function() {
      return root.Date.now();
    };

    /*------------------------------------------------------------------------*/

    /**
     * The opposite of `_.before`; this method creates a function that invokes
     * `func` once it's called `n` or more times.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Function
     * @param {number} n The number of calls before `func` is invoked.
     * @param {Function} func The function to restrict.
     * @returns {Function} Returns the new restricted function.
     * @example
     *
     * var saves = ['profile', 'settings'];
     *
     * var done = _.after(saves.length, function() {
     *   console.log('done saving!');
     * });
     *
     * _.forEach(saves, function(type) {
     *   asyncSave({ 'type': type, 'complete': done });
     * });
     * // => Logs 'done saving!' after the two async saves have completed.
     */
    function after(n, func) {
      if (typeof func != 'function') {
        throw new TypeError(FUNC_ERROR_TEXT);
      }
      n = toInteger(n);
      return function() {
        if (--n < 1) {
          return func.apply(this, arguments);
        }
      };
    }

    /**
     * Creates a function that invokes `func`, with up to `n` arguments,
     * ignoring any additional arguments.
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category Function
     * @param {Function} func The function to cap arguments for.
     * @param {number} [n=func.length] The arity cap.
     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
     * @returns {Function} Returns the new capped function.
     * @example
     *
     * _.map(['6', '8', '10'], _.ary(parseInt, 1));
     * // => [6, 8, 10]
     */
    function ary(func, n, guard) {
      n = guard ? undefined : n;
      n = (func && n == null) ? func.length : n;
      return createWrap(func, WRAP_ARY_FLAG, undefined, undefined, undefined, undefined, n);
    }

    /**
     * Creates a function that invokes `func`, with the `this` binding and arguments
     * of the created function, while it's called less than `n` times. Subsequent
     * calls to the created function return the result of the last `func` invocation.
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category Function
     * @param {number} n The number of calls at which `func` is no longer invoked.
     * @param {Function} func The function to restrict.
     * @returns {Function} Returns the new restricted function.
     * @example
     *
     * jQuery(element).on('click', _.before(5, addContactToList));
     * // => Allows adding up to 4 contacts to the list.
     */
    function before(n, func) {
      var result;
      if (typeof func != 'function') {
        throw new TypeError(FUNC_ERROR_TEXT);
      }
      n = toInteger(n);
      return function() {
        if (--n > 0) {
          result = func.apply(this, arguments);
        }
        if (n <= 1) {
          func = undefined;
        }
        return result;
      };
    }

    /**
     * Creates a function that invokes `func` with the `this` binding of `thisArg`
     * and `partials` prepended to the arguments it receives.
     *
     * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds,
     * may be used as a placeholder for partially applied arguments.
     *
     * **Note:** Unlike native `Function#bind`, this method doesn't set the "length"
     * property of bound functions.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Function
     * @param {Function} func The function to bind.
     * @param {*} thisArg The `this` binding of `func`.
     * @param {...*} [partials] The arguments to be partially applied.
     * @returns {Function} Returns the new bound function.
     * @example
     *
     * function greet(greeting, punctuation) {
     *   return greeting + ' ' + this.user + punctuation;
     * }
     *
     * var object = { 'user': 'fred' };
     *
     * var bound = _.bind(greet, object, 'hi');
     * bound('!');
     * // => 'hi fred!'
     *
     * // Bound with placeholders.
     * var bound = _.bind(greet, object, _, '!');
     * bound('hi');
     * // => 'hi fred!'
     */
    var bind = baseRest(function(func, thisArg, partials) {
      var bitmask = WRAP_BIND_FLAG;
      if (partials.length) {
        var holders = replaceHolders(partials, getHolder(bind));
        bitmask |= WRAP_PARTIAL_FLAG;
      }
      return createWrap(func, bitmask, thisArg, partials, holders);
    });

    /**
     * Creates a function that invokes the method at `object[key]` with `partials`
     * prepended to the arguments it receives.
     *
     * This method differs from `_.bind` by allowing bound functions to reference
     * methods that may be redefined or don't yet exist. See
     * [Peter Michaux's article](http://peter.michaux.ca/articles/lazy-function-definition-pattern)
     * for more details.
     *
     * The `_.bindKey.placeholder` value, which defaults to `_` in monolithic
     * builds, may be used as a placeholder for partially applied arguments.
     *
     * @static
     * @memberOf _
     * @since 0.10.0
     * @category Function
     * @param {Object} object The object to invoke the method on.
     * @param {string} key The key of the method.
     * @param {...*} [partials] The arguments to be partially applied.
     * @returns {Function} Returns the new bound function.
     * @example
     *
     * var object = {
     *   'user': 'fred',
     *   'greet': function(greeting, punctuation) {
     *     return greeting + ' ' + this.user + punctuation;
     *   }
     * };
     *
     * var bound = _.bindKey(object, 'greet', 'hi');
     * bound('!');
     * // => 'hi fred!'
     *
     * object.greet = function(greeting, punctuation) {
     *   return greeting + 'ya ' + this.user + punctuation;
     * };
     *
     * bound('!');
     * // => 'hiya fred!'
     *
     * // Bound with placeholders.
     * var bound = _.bindKey(object, 'greet', _, '!');
     * bound('hi');
     * // => 'hiya fred!'
     */
    var bindKey = baseRest(function(object, key, partials) {
      var bitmask = WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG;
      if (partials.length) {
        var holders = replaceHolders(partials, getHolder(bindKey));
        bitmask |= WRAP_PARTIAL_FLAG;
      }
      return createWrap(key, bitmask, object, partials, holders);
    });

    /**
     * Creates a function that accepts arguments of `func` and either invokes
     * `func` returning its result, if at least `arity` number of arguments have
     * been provided, or returns a function that accepts the remaining `func`
     * arguments, and so on. The arity of `func` may be specified if `func.length`
     * is not sufficient.
     *
     * The `_.curry.placeholder` value, which defaults to `_` in monolithic builds,
     * may be used as a placeholder for provided arguments.
     *
     * **Note:** This method doesn't set the "length" property of curried functions.
     *
     * @static
     * @memberOf _
     * @since 2.0.0
     * @category Function
     * @param {Function} func The function to curry.
     * @param {number} [arity=func.length] The arity of `func`.
     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
     * @returns {Function} Returns the new curried function.
     * @example
     *
     * var abc = function(a, b, c) {
     *   return [a, b, c];
     * };
     *
     * var curried = _.curry(abc);
     *
     * curried(1)(2)(3);
     * // => [1, 2, 3]
     *
     * curried(1, 2)(3);
     * // => [1, 2, 3]
     *
     * curried(1, 2, 3);
     * // => [1, 2, 3]
     *
     * // Curried with placeholders.
     * curried(1)(_, 3)(2);
     * // => [1, 2, 3]
     */
    function curry(func, arity, guard) {
      arity = guard ? undefined : arity;
      var result = createWrap(func, WRAP_CURRY_FLAG, undefined, undefined, undefined, undefined, undefined, arity);
      result.placeholder = curry.placeholder;
      return result;
    }

    /**
     * This method is like `_.curry` except that arguments are applied to `func`
     * in the manner of `_.partialRight` instead of `_.partial`.
     *
     * The `_.curryRight.placeholder` value, which defaults to `_` in monolithic
     * builds, may be used as a placeholder for provided arguments.
     *
     * **Note:** This method doesn't set the "length" property of curried functions.
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category Function
     * @param {Function} func The function to curry.
     * @param {number} [arity=func.length] The arity of `func`.
     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
     * @returns {Function} Returns the new curried function.
     * @example
     *
     * var abc = function(a, b, c) {
     *   return [a, b, c];
     * };
     *
     * var curried = _.curryRight(abc);
     *
     * curried(3)(2)(1);
     * // => [1, 2, 3]
     *
     * curried(2, 3)(1);
     * // => [1, 2, 3]
     *
     * curried(1, 2, 3);
     * // => [1, 2, 3]
     *
     * // Curried with placeholders.
     * curried(3)(1, _)(2);
     * // => [1, 2, 3]
     */
    function curryRight(func, arity, guard) {
      arity = guard ? undefined : arity;
      var result = createWrap(func, WRAP_CURRY_RIGHT_FLAG, undefined, undefined, undefined, undefined, undefined, arity);
      result.placeholder = curryRight.placeholder;
      return result;
    }

    /**
     * Creates a debounced function that delays invoking `func` until after `wait`
     * milliseconds have elapsed since the last time the debounced function was
     * invoked. The debounced function comes with a `cancel` method to cancel
     * delayed `func` invocations and a `flush` method to immediately invoke them.
     * Provide `options` to indicate whether `func` should be invoked on the
     * leading and/or trailing edge of the `wait` timeout. The `func` is invoked
     * with the last arguments provided to the debounced function. Subsequent
     * calls to the debounced function return the result of the last `func`
     * invocation.
     *
     * **Note:** If `leading` and `trailing` options are `true`, `func` is
     * invoked on the trailing edge of the timeout only if the debounced function
     * is invoked more than once during the `wait` timeout.
     *
     * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
     * until to the next tick, similar to `setTimeout` with a timeout of `0`.
     *
     * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
     * for details over the differences between `_.debounce` and `_.throttle`.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Function
     * @param {Function} func The function to debounce.
     * @param {number} [wait=0] The number of milliseconds to delay.
     * @param {Object} [options={}] The options object.
     * @param {boolean} [options.leading=false]
     *  Specify invoking on the leading edge of the timeout.
     * @param {number} [options.maxWait]
     *  The maximum time `func` is allowed to be delayed before it's invoked.
     * @param {boolean} [options.trailing=true]
     *  Specify invoking on the trailing edge of the timeout.
     * @returns {Function} Returns the new debounced function.
     * @example
     *
     * // Avoid costly calculations while the window size is in flux.
     * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
     *
     * // Invoke `sendMail` when clicked, debouncing subsequent calls.
     * jQuery(element).on('click', _.debounce(sendMail, 300, {
     *   'leading': true,
     *   'trailing': false
     * }));
     *
     * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
     * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
     * var source = new EventSource('/stream');
     * jQuery(source).on('message', debounced);
     *
     * // Cancel the trailing debounced invocation.
     * jQuery(window).on('popstate', debounced.cancel);
     */
    function debounce(func, wait, options) {
      var lastArgs,
          lastThis,
          maxWait,
          result,
          timerId,
          lastCallTime,
          lastInvokeTime = 0,
          leading = false,
          maxing = false,
          trailing = true;

      if (typeof func != 'function') {
        throw new TypeError(FUNC_ERROR_TEXT);
      }
      wait = toNumber(wait) || 0;
      if (isObject(options)) {
        leading = !!options.leading;
        maxing = 'maxWait' in options;
        maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;
        trailing = 'trailing' in options ? !!options.trailing : trailing;
      }

      function invokeFunc(time) {
        var args = lastArgs,
            thisArg = lastThis;

        lastArgs = lastThis = undefined;
        lastInvokeTime = time;
        result = func.apply(thisArg, args);
        return result;
      }

      function leadingEdge(time) {
        // Reset any `maxWait` timer.
        lastInvokeTime = time;
        // Start the timer for the trailing edge.
        timerId = setTimeout(timerExpired, wait);
        // Invoke the leading edge.
        return leading ? invokeFunc(time) : result;
      }

      function remainingWait(time) {
        var timeSinceLastCall = time - lastCallTime,
            timeSinceLastInvoke = time - lastInvokeTime,
            result = wait - timeSinceLastCall;

        return maxing ? nativeMin(result, maxWait - timeSinceLastInvoke) : result;
      }

      function shouldInvoke(time) {
        var timeSinceLastCall = time - lastCallTime,
            timeSinceLastInvoke = time - lastInvokeTime;

        // Either this is the first call, activity has stopped and we're at the
        // trailing edge, the system time has gone backwards and we're treating
        // it as the trailing edge, or we've hit the `maxWait` limit.
        return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
          (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));
      }

      function timerExpired() {
        var time = now();
        if (shouldInvoke(time)) {
          return trailingEdge(time);
        }
        // Restart the timer.
        timerId = setTimeout(timerExpired, remainingWait(time));
      }

      function trailingEdge(time) {
        timerId = undefined;

        // Only invoke if we have `lastArgs` which means `func` has been
        // debounced at least once.
        if (trailing && lastArgs) {
          return invokeFunc(time);
        }
        lastArgs = lastThis = undefined;
        return result;
      }

      function cancel() {
        if (timerId !== undefined) {
          clearTimeout(timerId);
        }
        lastInvokeTime = 0;
        lastArgs = lastCallTime = lastThis = timerId = undefined;
      }

      function flush() {
        return timerId === undefined ? result : trailingEdge(now());
      }

      function debounced() {
        var time = now(),
            isInvoking = shouldInvoke(time);

        lastArgs = arguments;
        lastThis = this;
        lastCallTime = time;

        if (isInvoking) {
          if (timerId === undefined) {
            return leadingEdge(lastCallTime);
          }
          if (maxing) {
            // Handle invocations in a tight loop.
            timerId = setTimeout(timerExpired, wait);
            return invokeFunc(lastCallTime);
          }
        }
        if (timerId === undefined) {
          timerId = setTimeout(timerExpired, wait);
        }
        return result;
      }
      debounced.cancel = cancel;
      debounced.flush = flush;
      return debounced;
    }

    /**
     * Defers invoking the `func` until the current call stack has cleared. Any
     * additional arguments are provided to `func` when it's invoked.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Function
     * @param {Function} func The function to defer.
     * @param {...*} [args] The arguments to invoke `func` with.
     * @returns {number} Returns the timer id.
     * @example
     *
     * _.defer(function(text) {
     *   console.log(text);
     * }, 'deferred');
     * // => Logs 'deferred' after one millisecond.
     */
    var defer = baseRest(function(func, args) {
      return baseDelay(func, 1, args);
    });

    /**
     * Invokes `func` after `wait` milliseconds. Any additional arguments are
     * provided to `func` when it's invoked.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Function
     * @param {Function} func The function to delay.
     * @param {number} wait The number of milliseconds to delay invocation.
     * @param {...*} [args] The arguments to invoke `func` with.
     * @returns {number} Returns the timer id.
     * @example
     *
     * _.delay(function(text) {
     *   console.log(text);
     * }, 1000, 'later');
     * // => Logs 'later' after one second.
     */
    var delay = baseRest(function(func, wait, args) {
      return baseDelay(func, toNumber(wait) || 0, args);
    });

    /**
     * Creates a function that invokes `func` with arguments reversed.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Function
     * @param {Function} func The function to flip arguments for.
     * @returns {Function} Returns the new flipped function.
     * @example
     *
     * var flipped = _.flip(function() {
     *   return _.toArray(arguments);
     * });
     *
     * flipped('a', 'b', 'c', 'd');
     * // => ['d', 'c', 'b', 'a']
     */
    function flip(func) {
      return createWrap(func, WRAP_FLIP_FLAG);
    }

    /**
     * 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 `clear`, `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 != null && 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) || cache;
        return result;
      };
      memoized.cache = new (memoize.Cache || MapCache);
      return memoized;
    }

    // Expose `MapCache`.
    memoize.Cache = MapCache;

    /**
     * Creates a function that negates the result of the predicate `func`. The
     * `func` predicate is invoked with the `this` binding and arguments of the
     * created function.
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category Function
     * @param {Function} predicate The predicate to negate.
     * @returns {Function} Returns the new negated function.
     * @example
     *
     * function isEven(n) {
     *   return n % 2 == 0;
     * }
     *
     * _.filter([1, 2, 3, 4, 5, 6], _.negate(isEven));
     * // => [1, 3, 5]
     */
    function negate(predicate) {
      if (typeof predicate != 'function') {
        throw new TypeError(FUNC_ERROR_TEXT);
      }
      return function() {
        var args = arguments;
        switch (args.length) {
          case 0: return !predicate.call(this);
          case 1: return !predicate.call(this, args[0]);
          case 2: return !predicate.call(this, args[0], args[1]);
          case 3: return !predicate.call(this, args[0], args[1], args[2]);
        }
        return !predicate.apply(this, args);
      };
    }

    /**
     * Creates a function that is restricted to invoking `func` once. Repeat calls
     * to the function return the value of the first invocation. The `func` is
     * invoked with the `this` binding and arguments of the created function.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Function
     * @param {Function} func The function to restrict.
     * @returns {Function} Returns the new restricted function.
     * @example
     *
     * var initialize = _.once(createApplication);
     * initialize();
     * initialize();
     * // => `createApplication` is invoked once
     */
    function once(func) {
      return before(2, func);
    }

    /**
     * Creates a function that invokes `func` with its arguments transformed.
     *
     * @static
     * @since 4.0.0
     * @memberOf _
     * @category Function
     * @param {Function} func The function to wrap.
     * @param {...(Function|Function[])} [transforms=[_.identity]]
     *  The argument transforms.
     * @returns {Function} Returns the new function.
     * @example
     *
     * function doubled(n) {
     *   return n * 2;
     * }
     *
     * function square(n) {
     *   return n * n;
     * }
     *
     * var func = _.overArgs(function(x, y) {
     *   return [x, y];
     * }, [square, doubled]);
     *
     * func(9, 3);
     * // => [81, 6]
     *
     * func(10, 5);
     * // => [100, 10]
     */
    var overArgs = castRest(function(func, transforms) {
      transforms = (transforms.length == 1 && isArray(transforms[0]))
        ? arrayMap(transforms[0], baseUnary(getIteratee()))
        : arrayMap(baseFlatten(transforms, 1), baseUnary(getIteratee()));

      var funcsLength = transforms.length;
      return baseRest(function(args) {
        var index = -1,
            length = nativeMin(args.length, funcsLength);

        while (++index < length) {
          args[index] = transforms[index].call(this, args[index]);
        }
        return apply(func, this, args);
      });
    });

    /**
     * Creates a function that invokes `func` with `partials` prepended to the
     * arguments it receives. This method is like `_.bind` except it does **not**
     * alter the `this` binding.
     *
     * The `_.partial.placeholder` value, which defaults to `_` in monolithic
     * builds, may be used as a placeholder for partially applied arguments.
     *
     * **Note:** This method doesn't set the "length" property of partially
     * applied functions.
     *
     * @static
     * @memberOf _
     * @since 0.2.0
     * @category Function
     * @param {Function} func The function to partially apply arguments to.
     * @param {...*} [partials] The arguments to be partially applied.
     * @returns {Function} Returns the new partially applied function.
     * @example
     *
     * function greet(greeting, name) {
     *   return greeting + ' ' + name;
     * }
     *
     * var sayHelloTo = _.partial(greet, 'hello');
     * sayHelloTo('fred');
     * // => 'hello fred'
     *
     * // Partially applied with placeholders.
     * var greetFred = _.partial(greet, _, 'fred');
     * greetFred('hi');
     * // => 'hi fred'
     */
    var partial = baseRest(function(func, partials) {
      var holders = replaceHolders(partials, getHolder(partial));
      return createWrap(func, WRAP_PARTIAL_FLAG, undefined, partials, holders);
    });

    /**
     * This method is like `_.partial` except that partially applied arguments
     * are appended to the arguments it receives.
     *
     * The `_.partialRight.placeholder` value, which defaults to `_` in monolithic
     * builds, may be used as a placeholder for partially applied arguments.
     *
     * **Note:** This method doesn't set the "length" property of partially
     * applied functions.
     *
     * @static
     * @memberOf _
     * @since 1.0.0
     * @category Function
     * @param {Function} func The function to partially apply arguments to.
     * @param {...*} [partials] The arguments to be partially applied.
     * @returns {Function} Returns the new partially applied function.
     * @example
     *
     * function greet(greeting, name) {
     *   return greeting + ' ' + name;
     * }
     *
     * var greetFred = _.partialRight(greet, 'fred');
     * greetFred('hi');
     * // => 'hi fred'
     *
     * // Partially applied with placeholders.
     * var sayHelloTo = _.partialRight(greet, 'hello', _);
     * sayHelloTo('fred');
     * // => 'hello fred'
     */
    var partialRight = baseRest(function(func, partials) {
      var holders = replaceHolders(partials, getHolder(partialRight));
      return createWrap(func, WRAP_PARTIAL_RIGHT_FLAG, undefined, partials, holders);
    });

    /**
     * Creates a function that invokes `func` with arguments arranged according
     * to the specified `indexes` where the argument value at the first index is
     * provided as the first argument, the argument value at the second index is
     * provided as the second argument, and so on.
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category Function
     * @param {Function} func The function to rearrange arguments for.
     * @param {...(number|number[])} indexes The arranged argument indexes.
     * @returns {Function} Returns the new function.
     * @example
     *
     * var rearged = _.rearg(function(a, b, c) {
     *   return [a, b, c];
     * }, [2, 0, 1]);
     *
     * rearged('b', 'c', 'a')
     * // => ['a', 'b', 'c']
     */
    var rearg = flatRest(function(func, indexes) {
      return createWrap(func, WRAP_REARG_FLAG, undefined, undefined, undefined, indexes);
    });

    /**
     * Creates a function that invokes `func` with the `this` binding of the
     * created function and arguments from `start` and beyond provided as
     * an array.
     *
     * **Note:** This method is based on the
     * [rest parameter](https://mdn.io/rest_parameters).
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Function
     * @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.
     * @example
     *
     * var say = _.rest(function(what, names) {
     *   return what + ' ' + _.initial(names).join(', ') +
     *     (_.size(names) > 1 ? ', & ' : '') + _.last(names);
     * });
     *
     * say('hello', 'fred', 'barney', 'pebbles');
     * // => 'hello fred, barney, & pebbles'
     */
    function rest(func, start) {
      if (typeof func != 'function') {
        throw new TypeError(FUNC_ERROR_TEXT);
      }
      start = start === undefined ? start : toInteger(start);
      return baseRest(func, start);
    }

    /**
     * Creates a function that invokes `func` with the `this` binding of the
     * create function and an array of arguments much like
     * [`Function#apply`](http://www.ecma-international.org/ecma-262/7.0/#sec-function.prototype.apply).
     *
     * **Note:** This method is based on the
     * [spread operator](https://mdn.io/spread_operator).
     *
     * @static
     * @memberOf _
     * @since 3.2.0
     * @category Function
     * @param {Function} func The function to spread arguments over.
     * @param {number} [start=0] The start position of the spread.
     * @returns {Function} Returns the new function.
     * @example
     *
     * var say = _.spread(function(who, what) {
     *   return who + ' says ' + what;
     * });
     *
     * say(['fred', 'hello']);
     * // => 'fred says hello'
     *
     * var numbers = Promise.all([
     *   Promise.resolve(40),
     *   Promise.resolve(36)
     * ]);
     *
     * numbers.then(_.spread(function(x, y) {
     *   return x + y;
     * }));
     * // => a Promise of 76
     */
    function spread(func, start) {
      if (typeof func != 'function') {
        throw new TypeError(FUNC_ERROR_TEXT);
      }
      start = start == null ? 0 : nativeMax(toInteger(start), 0);
      return baseRest(function(args) {
        var array = args[start],
            otherArgs = castSlice(args, 0, start);

        if (array) {
          arrayPush(otherArgs, array);
        }
        return apply(func, this, otherArgs);
      });
    }

    /**
     * Creates a throttled function that only invokes `func` at most once per
     * every `wait` milliseconds. The throttled function comes with a `cancel`
     * method to cancel delayed `func` invocations and a `flush` method to
     * immediately invoke them. Provide `options` to indicate whether `func`
     * should be invoked on the leading and/or trailing edge of the `wait`
     * timeout. The `func` is invoked with the last arguments provided to the
     * throttled function. Subsequent calls to the throttled function return the
     * result of the last `func` invocation.
     *
     * **Note:** If `leading` and `trailing` options are `true`, `func` is
     * invoked on the trailing edge of the timeout only if the throttled function
     * is invoked more than once during the `wait` timeout.
     *
     * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
     * until to the next tick, similar to `setTimeout` with a timeout of `0`.
     *
     * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
     * for details over the differences between `_.throttle` and `_.debounce`.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Function
     * @param {Function} func The function to throttle.
     * @param {number} [wait=0] The number of milliseconds to throttle invocations to.
     * @param {Object} [options={}] The options object.
     * @param {boolean} [options.leading=true]
     *  Specify invoking on the leading edge of the timeout.
     * @param {boolean} [options.trailing=true]
     *  Specify invoking on the trailing edge of the timeout.
     * @returns {Function} Returns the new throttled function.
     * @example
     *
     * // Avoid excessively updating the position while scrolling.
     * jQuery(window).on('scroll', _.throttle(updatePosition, 100));
     *
     * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.
     * var throttled = _.throttle(renewToken, 300000, { 'trailing': false });
     * jQuery(element).on('click', throttled);
     *
     * // Cancel the trailing throttled invocation.
     * jQuery(window).on('popstate', throttled.cancel);
     */
    function throttle(func, wait, options) {
      var leading = true,
          trailing = true;

      if (typeof func != 'function') {
        throw new TypeError(FUNC_ERROR_TEXT);
      }
      if (isObject(options)) {
        leading = 'leading' in options ? !!options.leading : leading;
        trailing = 'trailing' in options ? !!options.trailing : trailing;
      }
      return debounce(func, wait, {
        'leading': leading,
        'maxWait': wait,
        'trailing': trailing
      });
    }

    /**
     * Creates a function that accepts up to one argument, ignoring any
     * additional arguments.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Function
     * @param {Function} func The function to cap arguments for.
     * @returns {Function} Returns the new capped function.
     * @example
     *
     * _.map(['6', '8', '10'], _.unary(parseInt));
     * // => [6, 8, 10]
     */
    function unary(func) {
      return ary(func, 1);
    }

    /**
     * Creates a function that provides `value` to `wrapper` as its first
     * argument. Any additional arguments provided to the function are appended
     * to those provided to the `wrapper`. The wrapper is invoked with the `this`
     * binding of the created function.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Function
     * @param {*} value The value to wrap.
     * @param {Function} [wrapper=identity] The wrapper function.
     * @returns {Function} Returns the new function.
     * @example
     *
     * var p = _.wrap(_.escape, function(func, text) {
     *   return '<p>' + func(text) + '</p>';
     * });
     *
     * p('fred, barney, & pebbles');
     * // => '<p>fred, barney, &amp; pebbles</p>'
     */
    function wrap(value, wrapper) {
      return partial(castFunction(wrapper), value);
    }

    /*------------------------------------------------------------------------*/

    /**
     * Casts `value` as an array if it's not one.
     *
     * @static
     * @memberOf _
     * @since 4.4.0
     * @category Lang
     * @param {*} value The value to inspect.
     * @returns {Array} Returns the cast array.
     * @example
     *
     * _.castArray(1);
     * // => [1]
     *
     * _.castArray({ 'a': 1 });
     * // => [{ 'a': 1 }]
     *
     * _.castArray('abc');
     * // => ['abc']
     *
     * _.castArray(null);
     * // => [null]
     *
     * _.castArray(undefined);
     * // => [undefined]
     *
     * _.castArray();
     * // => []
     *
     * var array = [1, 2, 3];
     * console.log(_.castArray(array) === array);
     * // => true
     */
    function castArray() {
      if (!arguments.length) {
        return [];
      }
      var value = arguments[0];
      return isArray(value) ? value : [value];
    }

    /**
     * Creates a shallow clone of `value`.
     *
     * **Note:** This method is loosely based on the
     * [structured clone algorithm](https://mdn.io/Structured_clone_algorithm)
     * and supports cloning arrays, array buffers, booleans, date objects, maps,
     * numbers, `Object` objects, regexes, sets, strings, symbols, and typed
     * arrays. The own enumerable properties of `arguments` objects are cloned
     * as plain objects. An empty object is returned for uncloneable values such
     * as error objects, functions, DOM nodes, and WeakMaps.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Lang
     * @param {*} value The value to clone.
     * @returns {*} Returns the cloned value.
     * @see _.cloneDeep
     * @example
     *
     * var objects = [{ 'a': 1 }, { 'b': 2 }];
     *
     * var shallow = _.clone(objects);
     * console.log(shallow[0] === objects[0]);
     * // => true
     */
    function clone(value) {
      return baseClone(value, CLONE_SYMBOLS_FLAG);
    }

    /**
     * This method is like `_.clone` except that it accepts `customizer` which
     * is invoked to produce the cloned value. If `customizer` returns `undefined`,
     * cloning is handled by the method instead. The `customizer` is invoked with
     * up to four arguments; (value [, index|key, object, stack]).
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Lang
     * @param {*} value The value to clone.
     * @param {Function} [customizer] The function to customize cloning.
     * @returns {*} Returns the cloned value.
     * @see _.cloneDeepWith
     * @example
     *
     * function customizer(value) {
     *   if (_.isElement(value)) {
     *     return value.cloneNode(false);
     *   }
     * }
     *
     * var el = _.cloneWith(document.body, customizer);
     *
     * console.log(el === document.body);
     * // => false
     * console.log(el.nodeName);
     * // => 'BODY'
     * console.log(el.childNodes.length);
     * // => 0
     */
    function cloneWith(value, customizer) {
      customizer = typeof customizer == 'function' ? customizer : undefined;
      return baseClone(value, CLONE_SYMBOLS_FLAG, customizer);
    }

    /**
     * This method is like `_.clone` except that it recursively clones `value`.
     *
     * @static
     * @memberOf _
     * @since 1.0.0
     * @category Lang
     * @param {*} value The value to recursively clone.
     * @returns {*} Returns the deep cloned value.
     * @see _.clone
     * @example
     *
     * var objects = [{ 'a': 1 }, { 'b': 2 }];
     *
     * var deep = _.cloneDeep(objects);
     * console.log(deep[0] === objects[0]);
     * // => false
     */
    function cloneDeep(value) {
      return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG);
    }

    /**
     * This method is like `_.cloneWith` except that it recursively clones `value`.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Lang
     * @param {*} value The value to recursively clone.
     * @param {Function} [customizer] The function to customize cloning.
     * @returns {*} Returns the deep cloned value.
     * @see _.cloneWith
     * @example
     *
     * function customizer(value) {
     *   if (_.isElement(value)) {
     *     return value.cloneNode(true);
     *   }
     * }
     *
     * var el = _.cloneDeepWith(document.body, customizer);
     *
     * console.log(el === document.body);
     * // => false
     * console.log(el.nodeName);
     * // => 'BODY'
     * console.log(el.childNodes.length);
     * // => 20
     */
    function cloneDeepWith(value, customizer) {
      customizer = typeof customizer == 'function' ? customizer : undefined;
      return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG, customizer);
    }

    /**
     * Checks if `object` conforms to `source` by invoking the predicate
     * properties of `source` with the corresponding property values of `object`.
     *
     * **Note:** This method is equivalent to `_.conforms` when `source` is
     * partially applied.
     *
     * @static
     * @memberOf _
     * @since 4.14.0
     * @category Lang
     * @param {Object} object The object to inspect.
     * @param {Object} source The object of property predicates to conform to.
     * @returns {boolean} Returns `true` if `object` conforms, else `false`.
     * @example
     *
     * var object = { 'a': 1, 'b': 2 };
     *
     * _.conformsTo(object, { 'b': function(n) { return n > 1; } });
     * // => true
     *
     * _.conformsTo(object, { 'b': function(n) { return n > 2; } });
     * // => false
     */
    function conformsTo(object, source) {
      return source == null || baseConformsTo(object, source, keys(source));
    }

    /**
     * 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 greater than `other`.
     *
     * @static
     * @memberOf _
     * @since 3.9.0
     * @category Lang
     * @param {*} value The value to compare.
     * @param {*} other The other value to compare.
     * @returns {boolean} Returns `true` if `value` is greater than `other`,
     *  else `false`.
     * @see _.lt
     * @example
     *
     * _.gt(3, 1);
     * // => true
     *
     * _.gt(3, 3);
     * // => false
     *
     * _.gt(1, 3);
     * // => false
     */
    var gt = createRelationalOperation(baseGt);

    /**
     * Checks if `value` is greater than or equal to `other`.
     *
     * @static
     * @memberOf _
     * @since 3.9.0
     * @category Lang
     * @param {*} value The value to compare.
     * @param {*} other The other value to compare.
     * @returns {boolean} Returns `true` if `value` is greater than or equal to
     *  `other`, else `false`.
     * @see _.lte
     * @example
     *
     * _.gte(3, 1);
     * // => true
     *
     * _.gte(3, 3);
     * // => true
     *
     * _.gte(1, 3);
     * // => false
     */
    var gte = createRelationalOperation(function(value, other) {
      return value >= 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
     */
    var isArguments = baseIsArguments(function() { return arguments; }()) ? baseIsArguments : function(value) {
      return isObjectLike(value) && hasOwnProperty.call(value, 'callee') &&
        !propertyIsEnumerable.call(value, 'callee');
    };

    /**
     * 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 classified as an `ArrayBuffer` object.
     *
     * @static
     * @memberOf _
     * @since 4.3.0
     * @category Lang
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is an array buffer, else `false`.
     * @example
     *
     * _.isArrayBuffer(new ArrayBuffer(2));
     * // => true
     *
     * _.isArrayBuffer(new Array(2));
     * // => false
     */
    var isArrayBuffer = nodeIsArrayBuffer ? baseUnary(nodeIsArrayBuffer) : baseIsArrayBuffer;

    /**
     * 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 boolean primitive or object.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Lang
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is a boolean, else `false`.
     * @example
     *
     * _.isBoolean(false);
     * // => true
     *
     * _.isBoolean(null);
     * // => false
     */
    function isBoolean(value) {
      return value === true || value === false ||
        (isObjectLike(value) && baseGetTag(value) == boolTag);
    }

    /**
     * Checks if `value` is a buffer.
     *
     * @static
     * @memberOf _
     * @since 4.3.0
     * @category Lang
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is a buffer, else `false`.
     * @example
     *
     * _.isBuffer(new Buffer(2));
     * // => true
     *
     * _.isBuffer(new Uint8Array(2));
     * // => false
     */
    var isBuffer = nativeIsBuffer || stubFalse;

    /**
     * Checks if `value` is classified as a `Date` object.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Lang
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is a date object, else `false`.
     * @example
     *
     * _.isDate(new Date);
     * // => true
     *
     * _.isDate('Mon April 23 2012');
     * // => false
     */
    var isDate = nodeIsDate ? baseUnary(nodeIsDate) : baseIsDate;

    /**
     * Checks if `value` is likely a DOM element.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Lang
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is a DOM element, else `false`.
     * @example
     *
     * _.isElement(document.body);
     * // => true
     *
     * _.isElement('<body>');
     * // => false
     */
    function isElement(value) {
      return isObjectLike(value) && value.nodeType === 1 && !isPlainObject(value);
    }

    /**
     * Checks if `value` is an empty object, collection, map, or set.
     *
     * Objects are considered empty if they have no own enumerable string keyed
     * properties.
     *
     * Array-like values such as `arguments` objects, arrays, buffers, strings, or
     * jQuery-like collections are considered empty if they have a `length` of `0`.
     * Similarly, maps and sets are considered empty if they have a `size` of `0`.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Lang
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is empty, else `false`.
     * @example
     *
     * _.isEmpty(null);
     * // => true
     *
     * _.isEmpty(true);
     * // => true
     *
     * _.isEmpty(1);
     * // => true
     *
     * _.isEmpty([1, 2, 3]);
     * // => false
     *
     * _.isEmpty({ 'a': 1 });
     * // => false
     */
    function isEmpty(value) {
      if (value == null) {
        return true;
      }
      if (isArrayLike(value) &&
          (isArray(value) || typeof value == 'string' || typeof value.splice == 'function' ||
            isBuffer(value) || isTypedArray(value) || isArguments(value))) {
        return !value.length;
      }
      var tag = getTag(value);
      if (tag == mapTag || tag == setTag) {
        return !value.size;
      }
      if (isPrototype(value)) {
        return !baseKeys(value).length;
      }
      for (var key in value) {
        if (hasOwnProperty.call(value, key)) {
          return false;
        }
      }
      return true;
    }

    /**
     * Performs a deep comparison between two values to determine if they are
     * equivalent.
     *
     * **Note:** This method supports comparing arrays, array buffers, booleans,
     * date objects, error objects, maps, numbers, `Object` objects, regexes,
     * sets, strings, symbols, and typed arrays. `Object` objects are compared
     * by their own, not inherited, enumerable properties. Functions and DOM
     * nodes are compared by strict equality, i.e. `===`.
     *
     * @static
     * @memberOf _
     * @since 0.1.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 };
     *
     * _.isEqual(object, other);
     * // => true
     *
     * object === other;
     * // => false
     */
    function isEqual(value, other) {
      return baseIsEqual(value, other);
    }

    /**
     * This method is like `_.isEqual` except that it accepts `customizer` which
     * is invoked to compare values. If `customizer` returns `undefined`, comparisons
     * are handled by the method instead. The `customizer` is invoked with up to
     * six arguments: (objValue, othValue [, index|key, object, other, stack]).
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Lang
     * @param {*} value The value to compare.
     * @param {*} other The other value to compare.
     * @param {Function} [customizer] The function to customize comparisons.
     * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
     * @example
     *
     * function isGreeting(value) {
     *   return /^h(?:i|ello)$/.test(value);
     * }
     *
     * function customizer(objValue, othValue) {
     *   if (isGreeting(objValue) && isGreeting(othValue)) {
     *     return true;
     *   }
     * }
     *
     * var array = ['hello', 'goodbye'];
     * var other = ['hi', 'goodbye'];
     *
     * _.isEqualWith(array, other, customizer);
     * // => true
     */
    function isEqualWith(value, other, customizer) {
      customizer = typeof customizer == 'function' ? customizer : undefined;
      var result = customizer ? customizer(value, other) : undefined;
      return result === undefined ? baseIsEqual(value, other, undefined, customizer) : !!result;
    }

    /**
     * Checks if `value` is an `Error`, `EvalError`, `RangeError`, `ReferenceError`,
     * `SyntaxError`, `TypeError`, or `URIError` object.
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category Lang
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is an error object, else `false`.
     * @example
     *
     * _.isError(new Error);
     * // => true
     *
     * _.isError(Error);
     * // => false
     */
    function isError(value) {
      if (!isObjectLike(value)) {
        return false;
      }
      var tag = baseGetTag(value);
      return tag == errorTag || tag == domExcTag ||
        (typeof value.message == 'string' && typeof value.name == 'string' && !isPlainObject(value));
    }

    /**
     * Checks if `value` is a finite primitive number.
     *
     * **Note:** This method is based on
     * [`Number.isFinite`](https://mdn.io/Number/isFinite).
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Lang
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is a finite number, else `false`.
     * @example
     *
     * _.isFinite(3);
     * // => true
     *
     * _.isFinite(Number.MIN_VALUE);
     * // => true
     *
     * _.isFinite(Infinity);
     * // => false
     *
     * _.isFinite('3');
     * // => false
     */
    function isFinite(value) {
      return typeof value == 'number' && nativeIsFinite(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) {
      if (!isObject(value)) {
        return false;
      }
      // The use of `Object#toString` avoids issues with the `typeof` operator
      // in Safari 9 which returns 'object' for typed arrays and other constructors.
      var tag = baseGetTag(value);
      return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag;
    }

    /**
     * Checks if `value` is an integer.
     *
     * **Note:** This method is based on
     * [`Number.isInteger`](https://mdn.io/Number/isInteger).
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Lang
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is an integer, else `false`.
     * @example
     *
     * _.isInteger(3);
     * // => true
     *
     * _.isInteger(Number.MIN_VALUE);
     * // => false
     *
     * _.isInteger(Infinity);
     * // => false
     *
     * _.isInteger('3');
     * // => false
     */
    function isInteger(value) {
      return typeof value == 'number' && value == toInteger(value);
    }

    /**
     * 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 != null && (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 != null && typeof value == 'object';
    }

    /**
     * Checks if `value` is classified as a `Map` object.
     *
     * @static
     * @memberOf _
     * @since 4.3.0
     * @category Lang
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is a map, else `false`.
     * @example
     *
     * _.isMap(new Map);
     * // => true
     *
     * _.isMap(new WeakMap);
     * // => false
     */
    var isMap = nodeIsMap ? baseUnary(nodeIsMap) : baseIsMap;

    /**
     * Performs a partial deep comparison between `object` and `source` to
     * determine if `object` contains equivalent property values.
     *
     * **Note:** This method is equivalent to `_.matches` when `source` is
     * partially applied.
     *
     * Partial comparisons will match empty array and empty object `source`
     * values against any array or object value, respectively. See `_.isEqual`
     * for a list of supported value comparisons.
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category Lang
     * @param {Object} object The object to inspect.
     * @param {Object} source The object of property values to match.
     * @returns {boolean} Returns `true` if `object` is a match, else `false`.
     * @example
     *
     * var object = { 'a': 1, 'b': 2 };
     *
     * _.isMatch(object, { 'b': 2 });
     * // => true
     *
     * _.isMatch(object, { 'b': 1 });
     * // => false
     */
    function isMatch(object, source) {
      return object === source || baseIsMatch(object, source, getMatchData(source));
    }

    /**
     * This method is like `_.isMatch` except that it accepts `customizer` which
     * is invoked to compare values. If `customizer` returns `undefined`, comparisons
     * are handled by the method instead. The `customizer` is invoked with five
     * arguments: (objValue, srcValue, index|key, object, source).
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Lang
     * @param {Object} object The object to inspect.
     * @param {Object} source The object of property values to match.
     * @param {Function} [customizer] The function to customize comparisons.
     * @returns {boolean} Returns `true` if `object` is a match, else `false`.
     * @example
     *
     * function isGreeting(value) {
     *   return /^h(?:i|ello)$/.test(value);
     * }
     *
     * function customizer(objValue, srcValue) {
     *   if (isGreeting(objValue) && isGreeting(srcValue)) {
     *     return true;
     *   }
     * }
     *
     * var object = { 'greeting': 'hello' };
     * var source = { 'greeting': 'hi' };
     *
     * _.isMatchWith(object, source, customizer);
     * // => true
     */
    function isMatchWith(object, source, customizer) {
      customizer = typeof customizer == 'function' ? customizer : undefined;
      return baseIsMatch(object, source, getMatchData(source), customizer);
    }

    /**
     * Checks if `value` is `NaN`.
     *
     * **Note:** This method is based on
     * [`Number.isNaN`](https://mdn.io/Number/isNaN) and is not the same as
     * global [`isNaN`](https://mdn.io/isNaN) which returns `true` for
     * `undefined` and other non-number values.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Lang
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`.
     * @example
     *
     * _.isNaN(NaN);
     * // => true
     *
     * _.isNaN(new Number(NaN));
     * // => true
     *
     * isNaN(undefined);
     * // => true
     *
     * _.isNaN(undefined);
     * // => false
     */
    function isNaN(value) {
      // An `NaN` primitive is the only value that is not equal to itself.
      // Perform the `toStringTag` check first to avoid errors with some
      // ActiveX objects in IE.
      return isNumber(value) && value != +value;
    }

    /**
     * Checks if `value` is a pristine native function.
     *
     * **Note:** This method can't reliably detect native functions in the presence
     * of the core-js package because core-js circumvents this kind of detection.
     * Despite multiple requests, the core-js maintainer has made it clear: any
     * attempt to fix the detection will be obstructed. As a result, we're left
     * with little choice but to throw an error. Unfortunately, this also affects
     * packages, like [babel-polyfill](https://www.npmjs.com/package/babel-polyfill),
     * which rely on core-js.
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category Lang
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is a native function,
     *  else `false`.
     * @example
     *
     * _.isNative(Array.prototype.push);
     * // => true
     *
     * _.isNative(_);
     * // => false
     */
    function isNative(value) {
      if (isMaskable(value)) {
        throw new Error(CORE_ERROR_TEXT);
      }
      return baseIsNative(value);
    }

    /**
     * Checks if `value` is `null`.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Lang
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is `null`, else `false`.
     * @example
     *
     * _.isNull(null);
     * // => true
     *
     * _.isNull(void 0);
     * // => false
     */
    function isNull(value) {
      return value === null;
    }

    /**
     * Checks if `value` is `null` or `undefined`.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Lang
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is nullish, else `false`.
     * @example
     *
     * _.isNil(null);
     * // => true
     *
     * _.isNil(void 0);
     * // => true
     *
     * _.isNil(NaN);
     * // => false
     */
    function isNil(value) {
      return value == null;
    }

    /**
     * Checks if `value` is classified as a `Number` primitive or object.
     *
     * **Note:** To exclude `Infinity`, `-Infinity`, and `NaN`, which are
     * classified as numbers, use the `_.isFinite` method.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Lang
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is a number, else `false`.
     * @example
     *
     * _.isNumber(3);
     * // => true
     *
     * _.isNumber(Number.MIN_VALUE);
     * // => true
     *
     * _.isNumber(Infinity);
     * // => true
     *
     * _.isNumber('3');
     * // => false
     */
    function isNumber(value) {
      return typeof value == 'number' ||
        (isObjectLike(value) && baseGetTag(value) == numberTag);
    }

    /**
     * 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.call(proto, 'constructor') && proto.constructor;
      return typeof Ctor == 'function' && Ctor instanceof Ctor &&
        funcToString.call(Ctor) == objectCtorString;
    }

    /**
     * Checks if `value` is classified as a `RegExp` object.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Lang
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is a regexp, else `false`.
     * @example
     *
     * _.isRegExp(/abc/);
     * // => true
     *
     * _.isRegExp('/abc/');
     * // => false
     */
    var isRegExp = nodeIsRegExp ? baseUnary(nodeIsRegExp) : baseIsRegExp;

    /**
     * Checks if `value` is a safe integer. An integer is safe if it's an IEEE-754
     * double precision number which isn't the result of a rounded unsafe integer.
     *
     * **Note:** This method is based on
     * [`Number.isSafeInteger`](https://mdn.io/Number/isSafeInteger).
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Lang
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is a safe integer, else `false`.
     * @example
     *
     * _.isSafeInteger(3);
     * // => true
     *
     * _.isSafeInteger(Number.MIN_VALUE);
     * // => false
     *
     * _.isSafeInteger(Infinity);
     * // => false
     *
     * _.isSafeInteger('3');
     * // => false
     */
    function isSafeInteger(value) {
      return isInteger(value) && value >= -MAX_SAFE_INTEGER && value <= MAX_SAFE_INTEGER;
    }

    /**
     * Checks if `value` is classified as a `Set` object.
     *
     * @static
     * @memberOf _
     * @since 4.3.0
     * @category Lang
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is a set, else `false`.
     * @example
     *
     * _.isSet(new Set);
     * // => true
     *
     * _.isSet(new WeakSet);
     * // => false
     */
    var isSet = nodeIsSet ? baseUnary(nodeIsSet) : baseIsSet;

    /**
     * Checks if `value` is classified as a `String` primitive or object.
     *
     * @static
     * @since 0.1.0
     * @memberOf _
     * @category Lang
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is a string, else `false`.
     * @example
     *
     * _.isString('abc');
     * // => true
     *
     * _.isString(1);
     * // => false
     */
    function isString(value) {
      return typeof value == 'string' ||
        (!isArray(value) && isObjectLike(value) && baseGetTag(value) == stringTag);
    }

    /**
     * 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) && baseGetTag(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;

    /**
     * Checks if `value` is `undefined`.
     *
     * @static
     * @since 0.1.0
     * @memberOf _
     * @category Lang
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`.
     * @example
     *
     * _.isUndefined(void 0);
     * // => true
     *
     * _.isUndefined(null);
     * // => false
     */
    function isUndefined(value) {
      return value === undefined;
    }

    /**
     * Checks if `value` is classified as a `WeakMap` object.
     *
     * @static
     * @memberOf _
     * @since 4.3.0
     * @category Lang
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is a weak map, else `false`.
     * @example
     *
     * _.isWeakMap(new WeakMap);
     * // => true
     *
     * _.isWeakMap(new Map);
     * // => false
     */
    function isWeakMap(value) {
      return isObjectLike(value) && getTag(value) == weakMapTag;
    }

    /**
     * Checks if `value` is classified as a `WeakSet` object.
     *
     * @static
     * @memberOf _
     * @since 4.3.0
     * @category Lang
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is a weak set, else `false`.
     * @example
     *
     * _.isWeakSet(new WeakSet);
     * // => true
     *
     * _.isWeakSet(new Set);
     * // => false
     */
    function isWeakSet(value) {
      return isObjectLike(value) && baseGetTag(value) == weakSetTag;
    }

    /**
     * Checks if `value` is less than `other`.
     *
     * @static
     * @memberOf _
     * @since 3.9.0
     * @category Lang
     * @param {*} value The value to compare.
     * @param {*} other The other value to compare.
     * @returns {boolean} Returns `true` if `value` is less than `other`,
     *  else `false`.
     * @see _.gt
     * @example
     *
     * _.lt(1, 3);
     * // => true
     *
     * _.lt(3, 3);
     * // => false
     *
     * _.lt(3, 1);
     * // => false
     */
    var lt = createRelationalOperation(baseLt);

    /**
     * Checks if `value` is less than or equal to `other`.
     *
     * @static
     * @memberOf _
     * @since 3.9.0
     * @category Lang
     * @param {*} value The value to compare.
     * @param {*} other The other value to compare.
     * @returns {boolean} Returns `true` if `value` is less than or equal to
     *  `other`, else `false`.
     * @see _.gte
     * @example
     *
     * _.lte(1, 3);
     * // => true
     *
     * _.lte(3, 3);
     * // => true
     *
     * _.lte(3, 1);
     * // => false
     */
    var lte = createRelationalOperation(function(value, other) {
      return value <= other;
    });

    /**
     * Converts `value` to an array.
     *
     * @static
     * @since 0.1.0
     * @memberOf _
     * @category Lang
     * @param {*} value The value to convert.
     * @returns {Array} Returns the converted array.
     * @example
     *
     * _.toArray({ 'a': 1, 'b': 2 });
     * // => [1, 2]
     *
     * _.toArray('abc');
     * // => ['a', 'b', 'c']
     *
     * _.toArray(1);
     * // => []
     *
     * _.toArray(null);
     * // => []
     */
    function toArray(value) {
      if (!value) {
        return [];
      }
      if (isArrayLike(value)) {
        return isString(value) ? stringToArray(value) : copyArray(value);
      }
      if (symIterator && value[symIterator]) {
        return iteratorToArray(value[symIterator]());
      }
      var tag = getTag(value),
          func = tag == mapTag ? mapToArray : (tag == setTag ? setToArray : values);

      return func(value);
    }

    /**
     * Converts `value` to a finite number.
     *
     * @static
     * @memberOf _
     * @since 4.12.0
     * @category Lang
     * @param {*} value The value to convert.
     * @returns {number} Returns the converted number.
     * @example
     *
     * _.toFinite(3.2);
     * // => 3.2
     *
     * _.toFinite(Number.MIN_VALUE);
     * // => 5e-324
     *
     * _.toFinite(Infinity);
     * // => 1.7976931348623157e+308
     *
     * _.toFinite('3.2');
     * // => 3.2
     */
    function toFinite(value) {
      if (!value) {
        return value === 0 ? value : 0;
      }
      value = toNumber(value);
      if (value === INFINITY || value === -INFINITY) {
        var sign = (value < 0 ? -1 : 1);
        return sign * MAX_INTEGER;
      }
      return value === value ? value : 0;
    }

    /**
     * Converts `value` to an integer.
     *
     * **Note:** This method is loosely based on
     * [`ToInteger`](http://www.ecma-international.org/ecma-262/7.0/#sec-tointeger).
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Lang
     * @param {*} value The value to convert.
     * @returns {number} Returns the converted integer.
     * @example
     *
     * _.toInteger(3.2);
     * // => 3
     *
     * _.toInteger(Number.MIN_VALUE);
     * // => 0
     *
     * _.toInteger(Infinity);
     * // => 1.7976931348623157e+308
     *
     * _.toInteger('3.2');
     * // => 3
     */
    function toInteger(value) {
      var result = toFinite(value),
          remainder = result % 1;

      return result === result ? (remainder ? result - remainder : result) : 0;
    }

    /**
     * Converts `value` to an integer suitable for use as the length of an
     * array-like object.
     *
     * **Note:** This method is 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 convert.
     * @returns {number} Returns the converted integer.
     * @example
     *
     * _.toLength(3.2);
     * // => 3
     *
     * _.toLength(Number.MIN_VALUE);
     * // => 0
     *
     * _.toLength(Infinity);
     * // => 4294967295
     *
     * _.toLength('3.2');
     * // => 3
     */
    function toLength(value) {
      return value ? baseClamp(toInteger(value), 0, MAX_ARRAY_LENGTH) : 0;
    }

    /**
     * Converts `value` to a number.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Lang
     * @param {*} value The value to process.
     * @returns {number} Returns the number.
     * @example
     *
     * _.toNumber(3.2);
     * // => 3.2
     *
     * _.toNumber(Number.MIN_VALUE);
     * // => 5e-324
     *
     * _.toNumber(Infinity);
     * // => Infinity
     *
     * _.toNumber('3.2');
     * // => 3.2
     */
    function toNumber(value) {
      if (typeof value == 'number') {
        return value;
      }
      if (isSymbol(value)) {
        return NAN;
      }
      if (isObject(value)) {
        var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
        value = isObject(other) ? (other + '') : other;
      }
      if (typeof value != 'string') {
        return value === 0 ? value : +value;
      }
      value = value.replace(reTrim, '');
      var isBinary = reIsBinary.test(value);
      return (isBinary || reIsOctal.test(value))
        ? freeParseInt(value.slice(2), isBinary ? 2 : 8)
        : (reIsBadHex.test(value) ? NAN : +value);
    }

    /**
     * Converts `value` to a plain object flattening inherited enumerable string
     * keyed properties of `value` to own properties of the plain object.
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category Lang
     * @param {*} value The value to convert.
     * @returns {Object} Returns the converted plain object.
     * @example
     *
     * function Foo() {
     *   this.b = 2;
     * }
     *
     * Foo.prototype.c = 3;
     *
     * _.assign({ 'a': 1 }, new Foo);
     * // => { 'a': 1, 'b': 2 }
     *
     * _.assign({ 'a': 1 }, _.toPlainObject(new Foo));
     * // => { 'a': 1, 'b': 2, 'c': 3 }
     */
    function toPlainObject(value) {
      return copyObject(value, keysIn(value));
    }

    /**
     * Converts `value` to a safe integer. A safe integer can be compared and
     * represented correctly.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Lang
     * @param {*} value The value to convert.
     * @returns {number} Returns the converted integer.
     * @example
     *
     * _.toSafeInteger(3.2);
     * // => 3
     *
     * _.toSafeInteger(Number.MIN_VALUE);
     * // => 0
     *
     * _.toSafeInteger(Infinity);
     * // => 9007199254740991
     *
     * _.toSafeInteger('3.2');
     * // => 3
     */
    function toSafeInteger(value) {
      return value
        ? baseClamp(toInteger(value), -MAX_SAFE_INTEGER, MAX_SAFE_INTEGER)
        : (value === 0 ? value : 0);
    }

    /**
     * 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 convert.
     * @returns {string} Returns the converted string.
     * @example
     *
     * _.toString(null);
     * // => ''
     *
     * _.toString(-0);
     * // => '-0'
     *
     * _.toString([1, 2, 3]);
     * // => '1,2,3'
     */
    function toString(value) {
      return value == null ? '' : baseToString(value);
    }

    /*------------------------------------------------------------------------*/

    /**
     * Assigns own enumerable string keyed properties of source objects to the
     * destination object. Source objects are applied from left to right.
     * Subsequent sources overwrite property assignments of previous sources.
     *
     * **Note:** This method mutates `object` and is loosely based on
     * [`Object.assign`](https://mdn.io/Object/assign).
     *
     * @static
     * @memberOf _
     * @since 0.10.0
     * @category Object
     * @param {Object} object The destination object.
     * @param {...Object} [sources] The source objects.
     * @returns {Object} Returns `object`.
     * @see _.assignIn
     * @example
     *
     * function Foo() {
     *   this.a = 1;
     * }
     *
     * function Bar() {
     *   this.c = 3;
     * }
     *
     * Foo.prototype.b = 2;
     * Bar.prototype.d = 4;
     *
     * _.assign({ 'a': 0 }, new Foo, new Bar);
     * // => { 'a': 1, 'c': 3 }
     */
    var assign = createAssigner(function(object, source) {
      if (isPrototype(source) || isArrayLike(source)) {
        copyObject(source, keys(source), object);
        return;
      }
      for (var key in source) {
        if (hasOwnProperty.call(source, key)) {
          assignValue(object, key, source[key]);
        }
      }
    });

    /**
     * This method is like `_.assign` except that it iterates over own and
     * inherited source properties.
     *
     * **Note:** This method mutates `object`.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @alias extend
     * @category Object
     * @param {Object} object The destination object.
     * @param {...Object} [sources] The source objects.
     * @returns {Object} Returns `object`.
     * @see _.assign
     * @example
     *
     * function Foo() {
     *   this.a = 1;
     * }
     *
     * function Bar() {
     *   this.c = 3;
     * }
     *
     * Foo.prototype.b = 2;
     * Bar.prototype.d = 4;
     *
     * _.assignIn({ 'a': 0 }, new Foo, new Bar);
     * // => { 'a': 1, 'b': 2, 'c': 3, 'd': 4 }
     */
    var assignIn = createAssigner(function(object, source) {
      copyObject(source, keysIn(source), object);
    });

    /**
     * This method is like `_.assignIn` except that it accepts `customizer`
     * which is invoked to produce the assigned values. If `customizer` returns
     * `undefined`, assignment is handled by the method instead. The `customizer`
     * is invoked with five arguments: (objValue, srcValue, key, object, source).
     *
     * **Note:** This method mutates `object`.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @alias extendWith
     * @category Object
     * @param {Object} object The destination object.
     * @param {...Object} sources The source objects.
     * @param {Function} [customizer] The function to customize assigned values.
     * @returns {Object} Returns `object`.
     * @see _.assignWith
     * @example
     *
     * function customizer(objValue, srcValue) {
     *   return _.isUndefined(objValue) ? srcValue : objValue;
     * }
     *
     * var defaults = _.partialRight(_.assignInWith, customizer);
     *
     * defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 });
     * // => { 'a': 1, 'b': 2 }
     */
    var assignInWith = createAssigner(function(object, source, srcIndex, customizer) {
      copyObject(source, keysIn(source), object, customizer);
    });

    /**
     * This method is like `_.assign` except that it accepts `customizer`
     * which is invoked to produce the assigned values. If `customizer` returns
     * `undefined`, assignment is handled by the method instead. The `customizer`
     * is invoked with five arguments: (objValue, srcValue, key, object, source).
     *
     * **Note:** This method mutates `object`.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Object
     * @param {Object} object The destination object.
     * @param {...Object} sources The source objects.
     * @param {Function} [customizer] The function to customize assigned values.
     * @returns {Object} Returns `object`.
     * @see _.assignInWith
     * @example
     *
     * function customizer(objValue, srcValue) {
     *   return _.isUndefined(objValue) ? srcValue : objValue;
     * }
     *
     * var defaults = _.partialRight(_.assignWith, customizer);
     *
     * defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 });
     * // => { 'a': 1, 'b': 2 }
     */
    var assignWith = createAssigner(function(object, source, srcIndex, customizer) {
      copyObject(source, keys(source), object, customizer);
    });

    /**
     * Creates an array of values corresponding to `paths` of `object`.
     *
     * @static
     * @memberOf _
     * @since 1.0.0
     * @category Object
     * @param {Object} object The object to iterate over.
     * @param {...(string|string[])} [paths] The property paths to pick.
     * @returns {Array} Returns the picked values.
     * @example
     *
     * var object = { 'a': [{ 'b': { 'c': 3 } }, 4] };
     *
     * _.at(object, ['a[0].b.c', 'a[1]']);
     * // => [3, 4]
     */
    var at = flatRest(baseAt);

    /**
     * Creates an object that inherits from the `prototype` object. If a
     * `properties` object is given, its own enumerable string keyed properties
     * are assigned to the created object.
     *
     * @static
     * @memberOf _
     * @since 2.3.0
     * @category Object
     * @param {Object} prototype The object to inherit from.
     * @param {Object} [properties] The properties to assign to the object.
     * @returns {Object} Returns the new object.
     * @example
     *
     * function Shape() {
     *   this.x = 0;
     *   this.y = 0;
     * }
     *
     * function Circle() {
     *   Shape.call(this);
     * }
     *
     * Circle.prototype = _.create(Shape.prototype, {
     *   'constructor': Circle
     * });
     *
     * var circle = new Circle;
     * circle instanceof Circle;
     * // => true
     *
     * circle instanceof Shape;
     * // => true
     */
    function create(prototype, properties) {
      var result = baseCreate(prototype);
      return properties == null ? result : baseAssign(result, properties);
    }

    /**
     * Assigns own and inherited enumerable string keyed properties of source
     * objects to the destination object for all destination properties that
     * resolve to `undefined`. Source objects are applied from left to right.
     * Once a property is set, additional values of the same property are ignored.
     *
     * **Note:** This method mutates `object`.
     *
     * @static
     * @since 0.1.0
     * @memberOf _
     * @category Object
     * @param {Object} object The destination object.
     * @param {...Object} [sources] The source objects.
     * @returns {Object} Returns `object`.
     * @see _.defaultsDeep
     * @example
     *
     * _.defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 });
     * // => { 'a': 1, 'b': 2 }
     */
    var defaults = baseRest(function(args) {
      args.push(undefined, customDefaultsAssignIn);
      return apply(assignInWith, undefined, args);
    });

    /**
     * This method is like `_.defaults` except that it recursively assigns
     * default properties.
     *
     * **Note:** This method mutates `object`.
     *
     * @static
     * @memberOf _
     * @since 3.10.0
     * @category Object
     * @param {Object} object The destination object.
     * @param {...Object} [sources] The source objects.
     * @returns {Object} Returns `object`.
     * @see _.defaults
     * @example
     *
     * _.defaultsDeep({ 'a': { 'b': 2 } }, { 'a': { 'b': 1, 'c': 3 } });
     * // => { 'a': { 'b': 2, 'c': 3 } }
     */
    var defaultsDeep = baseRest(function(args) {
      args.push(undefined, customDefaultsMerge);
      return apply(mergeWith, undefined, args);
    });

    /**
     * This method is like `_.find` except that it returns the key of the first
     * element `predicate` returns truthy for instead of the element itself.
     *
     * @static
     * @memberOf _
     * @since 1.1.0
     * @category Object
     * @param {Object} object The object to inspect.
     * @param {Function} [predicate=_.identity] The function invoked per iteration.
     * @returns {string|undefined} Returns the key of the matched element,
     *  else `undefined`.
     * @example
     *
     * var users = {
     *   'barney':  { 'age': 36, 'active': true },
     *   'fred':    { 'age': 40, 'active': false },
     *   'pebbles': { 'age': 1,  'active': true }
     * };
     *
     * _.findKey(users, function(o) { return o.age < 40; });
     * // => 'barney' (iteration order is not guaranteed)
     *
     * // The `_.matches` iteratee shorthand.
     * _.findKey(users, { 'age': 1, 'active': true });
     * // => 'pebbles'
     *
     * // The `_.matchesProperty` iteratee shorthand.
     * _.findKey(users, ['active', false]);
     * // => 'fred'
     *
     * // The `_.property` iteratee shorthand.
     * _.findKey(users, 'active');
     * // => 'barney'
     */
    function findKey(object, predicate) {
      return baseFindKey(object, getIteratee(predicate, 3), baseForOwn);
    }

    /**
     * This method is like `_.findKey` except that it iterates over elements of
     * a collection in the opposite order.
     *
     * @static
     * @memberOf _
     * @since 2.0.0
     * @category Object
     * @param {Object} object The object to inspect.
     * @param {Function} [predicate=_.identity] The function invoked per iteration.
     * @returns {string|undefined} Returns the key of the matched element,
     *  else `undefined`.
     * @example
     *
     * var users = {
     *   'barney':  { 'age': 36, 'active': true },
     *   'fred':    { 'age': 40, 'active': false },
     *   'pebbles': { 'age': 1,  'active': true }
     * };
     *
     * _.findLastKey(users, function(o) { return o.age < 40; });
     * // => returns 'pebbles' assuming `_.findKey` returns 'barney'
     *
     * // The `_.matches` iteratee shorthand.
     * _.findLastKey(users, { 'age': 36, 'active': true });
     * // => 'barney'
     *
     * // The `_.matchesProperty` iteratee shorthand.
     * _.findLastKey(users, ['active', false]);
     * // => 'fred'
     *
     * // The `_.property` iteratee shorthand.
     * _.findLastKey(users, 'active');
     * // => 'pebbles'
     */
    function findLastKey(object, predicate) {
      return baseFindKey(object, getIteratee(predicate, 3), baseForOwnRight);
    }

    /**
     * Iterates over own and inherited enumerable string keyed properties of an
     * object and invokes `iteratee` for each property. The iteratee is invoked
     * with three arguments: (value, key, object). Iteratee functions may exit
     * iteration early by explicitly returning `false`.
     *
     * @static
     * @memberOf _
     * @since 0.3.0
     * @category Object
     * @param {Object} object The object to iterate over.
     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
     * @returns {Object} Returns `object`.
     * @see _.forInRight
     * @example
     *
     * function Foo() {
     *   this.a = 1;
     *   this.b = 2;
     * }
     *
     * Foo.prototype.c = 3;
     *
     * _.forIn(new Foo, function(value, key) {
     *   console.log(key);
     * });
     * // => Logs 'a', 'b', then 'c' (iteration order is not guaranteed).
     */
    function forIn(object, iteratee) {
      return object == null
        ? object
        : baseFor(object, getIteratee(iteratee, 3), keysIn);
    }

    /**
     * This method is like `_.forIn` except that it iterates over properties of
     * `object` in the opposite order.
     *
     * @static
     * @memberOf _
     * @since 2.0.0
     * @category Object
     * @param {Object} object The object to iterate over.
     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
     * @returns {Object} Returns `object`.
     * @see _.forIn
     * @example
     *
     * function Foo() {
     *   this.a = 1;
     *   this.b = 2;
     * }
     *
     * Foo.prototype.c = 3;
     *
     * _.forInRight(new Foo, function(value, key) {
     *   console.log(key);
     * });
     * // => Logs 'c', 'b', then 'a' assuming `_.forIn` logs 'a', 'b', then 'c'.
     */
    function forInRight(object, iteratee) {
      return object == null
        ? object
        : baseForRight(object, getIteratee(iteratee, 3), keysIn);
    }

    /**
     * Iterates over own enumerable string keyed properties of an object and
     * invokes `iteratee` for each property. The iteratee is invoked with three
     * arguments: (value, key, object). Iteratee functions may exit iteration
     * early by explicitly returning `false`.
     *
     * @static
     * @memberOf _
     * @since 0.3.0
     * @category Object
     * @param {Object} object The object to iterate over.
     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
     * @returns {Object} Returns `object`.
     * @see _.forOwnRight
     * @example
     *
     * function Foo() {
     *   this.a = 1;
     *   this.b = 2;
     * }
     *
     * Foo.prototype.c = 3;
     *
     * _.forOwn(new Foo, function(value, key) {
     *   console.log(key);
     * });
     * // => Logs 'a' then 'b' (iteration order is not guaranteed).
     */
    function forOwn(object, iteratee) {
      return object && baseForOwn(object, getIteratee(iteratee, 3));
    }

    /**
     * This method is like `_.forOwn` except that it iterates over properties of
     * `object` in the opposite order.
     *
     * @static
     * @memberOf _
     * @since 2.0.0
     * @category Object
     * @param {Object} object The object to iterate over.
     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
     * @returns {Object} Returns `object`.
     * @see _.forOwn
     * @example
     *
     * function Foo() {
     *   this.a = 1;
     *   this.b = 2;
     * }
     *
     * Foo.prototype.c = 3;
     *
     * _.forOwnRight(new Foo, function(value, key) {
     *   console.log(key);
     * });
     * // => Logs 'b' then 'a' assuming `_.forOwn` logs 'a' then 'b'.
     */
    function forOwnRight(object, iteratee) {
      return object && baseForOwnRight(object, getIteratee(iteratee, 3));
    }

    /**
     * Creates an array of function property names from own enumerable properties
     * of `object`.
     *
     * @static
     * @since 0.1.0
     * @memberOf _
     * @category Object
     * @param {Object} object The object to inspect.
     * @returns {Array} Returns the function names.
     * @see _.functionsIn
     * @example
     *
     * function Foo() {
     *   this.a = _.constant('a');
     *   this.b = _.constant('b');
     * }
     *
     * Foo.prototype.c = _.constant('c');
     *
     * _.functions(new Foo);
     * // => ['a', 'b']
     */
    function functions(object) {
      return object == null ? [] : baseFunctions(object, keys(object));
    }

    /**
     * Creates an array of function property names from own and inherited
     * enumerable properties of `object`.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Object
     * @param {Object} object The object to inspect.
     * @returns {Array} Returns the function names.
     * @see _.functions
     * @example
     *
     * function Foo() {
     *   this.a = _.constant('a');
     *   this.b = _.constant('b');
     * }
     *
     * Foo.prototype.c = _.constant('c');
     *
     * _.functionsIn(new Foo);
     * // => ['a', 'b', 'c']
     */
    function functionsIn(object) {
      return object == null ? [] : baseFunctions(object, keysIn(object));
    }

    /**
     * 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 property of `object`.
     *
     * @static
     * @since 0.1.0
     * @memberOf _
     * @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 = { 'a': { 'b': 2 } };
     * var other = _.create({ 'a': _.create({ 'b': 2 }) });
     *
     * _.has(object, 'a');
     * // => true
     *
     * _.has(object, 'a.b');
     * // => true
     *
     * _.has(object, ['a', 'b']);
     * // => true
     *
     * _.has(other, 'a');
     * // => false
     */
    function has(object, path) {
      return object != null && hasPath(object, path, baseHas);
    }

    /**
     * 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 object composed of the inverted keys and values of `object`.
     * If `object` contains duplicate values, subsequent values overwrite
     * property assignments of previous values.
     *
     * @static
     * @memberOf _
     * @since 0.7.0
     * @category Object
     * @param {Object} object The object to invert.
     * @returns {Object} Returns the new inverted object.
     * @example
     *
     * var object = { 'a': 1, 'b': 2, 'c': 1 };
     *
     * _.invert(object);
     * // => { '1': 'c', '2': 'b' }
     */
    var invert = createInverter(function(result, value, key) {
      result[value] = key;
    }, constant(identity));

    /**
     * This method is like `_.invert` except that the inverted object is generated
     * from the results of running each element of `object` thru `iteratee`. The
     * corresponding inverted value of each inverted key is an array of keys
     * responsible for generating the inverted value. The iteratee is invoked
     * with one argument: (value).
     *
     * @static
     * @memberOf _
     * @since 4.1.0
     * @category Object
     * @param {Object} object The object to invert.
     * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
     * @returns {Object} Returns the new inverted object.
     * @example
     *
     * var object = { 'a': 1, 'b': 2, 'c': 1 };
     *
     * _.invertBy(object);
     * // => { '1': ['a', 'c'], '2': ['b'] }
     *
     * _.invertBy(object, function(value) {
     *   return 'group' + value;
     * });
     * // => { 'group1': ['a', 'c'], 'group2': ['b'] }
     */
    var invertBy = createInverter(function(result, value, key) {
      if (hasOwnProperty.call(result, value)) {
        result[value].push(key);
      } else {
        result[value] = [key];
      }
    }, getIteratee);

    /**
     * Invokes the method at `path` of `object`.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Object
     * @param {Object} object The object to query.
     * @param {Array|string} path The path of the method to invoke.
     * @param {...*} [args] The arguments to invoke the method with.
     * @returns {*} Returns the result of the invoked method.
     * @example
     *
     * var object = { 'a': [{ 'b': { 'c': [1, 2, 3, 4] } }] };
     *
     * _.invoke(object, 'a[0].b.c.slice', 1, 3);
     * // => [2, 3]
     */
    var invoke = baseRest(baseInvoke);

    /**
     * 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);
    }

    /**
     * Creates an array of the own and inherited enumerable property names of `object`.
     *
     * **Note:** Non-object values are coerced to objects.
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @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;
     *
     * _.keysIn(new Foo);
     * // => ['a', 'b', 'c'] (iteration order is not guaranteed)
     */
    function keysIn(object) {
      return isArrayLike(object) ? arrayLikeKeys(object, true) : baseKeysIn(object);
    }

    /**
     * The opposite of `_.mapValues`; this method creates an object with the
     * same values as `object` and keys generated by running each own enumerable
     * string keyed property of `object` thru `iteratee`. The iteratee is invoked
     * with three arguments: (value, key, object).
     *
     * @static
     * @memberOf _
     * @since 3.8.0
     * @category Object
     * @param {Object} object The object to iterate over.
     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
     * @returns {Object} Returns the new mapped object.
     * @see _.mapValues
     * @example
     *
     * _.mapKeys({ 'a': 1, 'b': 2 }, function(value, key) {
     *   return key + value;
     * });
     * // => { 'a1': 1, 'b2': 2 }
     */
    function mapKeys(object, iteratee) {
      var result = {};
      iteratee = getIteratee(iteratee, 3);

      baseForOwn(object, function(value, key, object) {
        baseAssignValue(result, iteratee(value, key, object), value);
      });
      return result;
    }

    /**
     * Creates an object with the same keys as `object` and values generated
     * by running each own enumerable string keyed property of `object` thru
     * `iteratee`. The iteratee is invoked with three arguments:
     * (value, key, object).
     *
     * @static
     * @memberOf _
     * @since 2.4.0
     * @category Object
     * @param {Object} object The object to iterate over.
     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
     * @returns {Object} Returns the new mapped object.
     * @see _.mapKeys
     * @example
     *
     * var users = {
     *   'fred':    { 'user': 'fred',    'age': 40 },
     *   'pebbles': { 'user': 'pebbles', 'age': 1 }
     * };
     *
     * _.mapValues(users, function(o) { return o.age; });
     * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed)
     *
     * // The `_.property` iteratee shorthand.
     * _.mapValues(users, 'age');
     * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed)
     */
    function mapValues(object, iteratee) {
      var result = {};
      iteratee = getIteratee(iteratee, 3);

      baseForOwn(object, function(value, key, object) {
        baseAssignValue(result, key, iteratee(value, key, object));
      });
      return result;
    }

    /**
     * This method is like `_.assign` except that it recursively merges own and
     * inherited enumerable string keyed properties of source objects into the
     * destination object. Source properties that resolve to `undefined` are
     * skipped if a destination value exists. Array and plain object properties
     * are merged recursively. Other objects and value types are overridden by
     * assignment. Source objects are applied from left to right. Subsequent
     * sources overwrite property assignments of previous sources.
     *
     * **Note:** This method mutates `object`.
     *
     * @static
     * @memberOf _
     * @since 0.5.0
     * @category Object
     * @param {Object} object The destination object.
     * @param {...Object} [sources] The source objects.
     * @returns {Object} Returns `object`.
     * @example
     *
     * var object = {
     *   'a': [{ 'b': 2 }, { 'd': 4 }]
     * };
     *
     * var other = {
     *   'a': [{ 'c': 3 }, { 'e': 5 }]
     * };
     *
     * _.merge(object, other);
     * // => { 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] }
     */
    var merge = createAssigner(function(object, source, srcIndex) {
      baseMerge(object, source, srcIndex);
    });

    /**
     * This method is like `_.merge` except that it accepts `customizer` which
     * is invoked to produce the merged values of the destination and source
     * properties. If `customizer` returns `undefined`, merging is handled by the
     * method instead. The `customizer` is invoked with six arguments:
     * (objValue, srcValue, key, object, source, stack).
     *
     * **Note:** This method mutates `object`.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Object
     * @param {Object} object The destination object.
     * @param {...Object} sources The source objects.
     * @param {Function} customizer The function to customize assigned values.
     * @returns {Object} Returns `object`.
     * @example
     *
     * function customizer(objValue, srcValue) {
     *   if (_.isArray(objValue)) {
     *     return objValue.concat(srcValue);
     *   }
     * }
     *
     * var object = { 'a': [1], 'b': [2] };
     * var other = { 'a': [3], 'b': [4] };
     *
     * _.mergeWith(object, other, customizer);
     * // => { 'a': [1, 3], 'b': [2, 4] }
     */
    var mergeWith = createAssigner(function(object, source, srcIndex, customizer) {
      baseMerge(object, source, srcIndex, customizer);
    });

    /**
     * The opposite of `_.pick`; this method creates an object composed of the
     * own and inherited enumerable property paths of `object` that are not omitted.
     *
     * **Note:** This method is considerably slower than `_.pick`.
     *
     * @static
     * @since 0.1.0
     * @memberOf _
     * @category Object
     * @param {Object} object The source object.
     * @param {...(string|string[])} [paths] The property paths to omit.
     * @returns {Object} Returns the new object.
     * @example
     *
     * var object = { 'a': 1, 'b': '2', 'c': 3 };
     *
     * _.omit(object, ['a', 'c']);
     * // => { 'b': '2' }
     */
    var omit = flatRest(function(object, paths) {
      var result = {};
      if (object == null) {
        return result;
      }
      var isDeep = false;
      paths = arrayMap(paths, function(path) {
        path = castPath(path, object);
        isDeep || (isDeep = path.length > 1);
        return path;
      });
      copyObject(object, getAllKeysIn(object), result);
      if (isDeep) {
        result = baseClone(result, CLONE_DEEP_FLAG | CLONE_FLAT_FLAG | CLONE_SYMBOLS_FLAG, customOmitClone);
      }
      var length = paths.length;
      while (length--) {
        baseUnset(result, paths[length]);
      }
      return result;
    });

    /**
     * The opposite of `_.pickBy`; this method creates an object composed of
     * the own and inherited enumerable string keyed properties of `object` that
     * `predicate` doesn't return truthy for. The predicate is invoked with two
     * arguments: (value, key).
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Object
     * @param {Object} object The source object.
     * @param {Function} [predicate=_.identity] The function invoked per property.
     * @returns {Object} Returns the new object.
     * @example
     *
     * var object = { 'a': 1, 'b': '2', 'c': 3 };
     *
     * _.omitBy(object, _.isNumber);
     * // => { 'b': '2' }
     */
    function omitBy(object, predicate) {
      return pickBy(object, negate(getIteratee(predicate)));
    }

    /**
     * Creates an object composed of the picked `object` properties.
     *
     * @static
     * @since 0.1.0
     * @memberOf _
     * @category Object
     * @param {Object} object The source object.
     * @param {...(string|string[])} [paths] The property paths to pick.
     * @returns {Object} Returns the new object.
     * @example
     *
     * var object = { 'a': 1, 'b': '2', 'c': 3 };
     *
     * _.pick(object, ['a', 'c']);
     * // => { 'a': 1, 'c': 3 }
     */
    var pick = flatRest(function(object, paths) {
      return object == null ? {} : basePick(object, paths);
    });

    /**
     * Creates an object composed of the `object` properties `predicate` returns
     * truthy for. The predicate is invoked with two arguments: (value, key).
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Object
     * @param {Object} object The source object.
     * @param {Function} [predicate=_.identity] The function invoked per property.
     * @returns {Object} Returns the new object.
     * @example
     *
     * var object = { 'a': 1, 'b': '2', 'c': 3 };
     *
     * _.pickBy(object, _.isNumber);
     * // => { 'a': 1, 'c': 3 }
     */
    function pickBy(object, predicate) {
      if (object == null) {
        return {};
      }
      var props = arrayMap(getAllKeysIn(object), function(prop) {
        return [prop];
      });
      predicate = getIteratee(predicate);
      return basePickBy(object, props, function(value, path) {
        return predicate(value, path[0]);
      });
    }

    /**
     * This method is like `_.get` except that if the resolved value is a
     * function it's invoked with the `this` binding of its parent object and
     * its result is returned.
     *
     * @static
     * @since 0.1.0
     * @memberOf _
     * @category Object
     * @param {Object} object The object to query.
     * @param {Array|string} path The path of the property to resolve.
     * @param {*} [defaultValue] The value returned for `undefined` resolved values.
     * @returns {*} Returns the resolved value.
     * @example
     *
     * var object = { 'a': [{ 'b': { 'c1': 3, 'c2': _.constant(4) } }] };
     *
     * _.result(object, 'a[0].b.c1');
     * // => 3
     *
     * _.result(object, 'a[0].b.c2');
     * // => 4
     *
     * _.result(object, 'a[0].b.c3', 'default');
     * // => 'default'
     *
     * _.result(object, 'a[0].b.c3', _.constant('default'));
     * // => 'default'
     */
    function result(object, path, defaultValue) {
      path = castPath(path, object);

      var index = -1,
          length = path.length;

      // Ensure the loop is entered when path is empty.
      if (!length) {
        length = 1;
        object = undefined;
      }
      while (++index < length) {
        var value = object == null ? undefined : object[toKey(path[index])];
        if (value === undefined) {
          index = length;
          value = defaultValue;
        }
        object = isFunction(value) ? value.call(object) : value;
      }
      return object;
    }

    /**
     * Sets the value at `path` of `object`. If a portion of `path` doesn't exist,
     * it's created. Arrays are created for missing index properties while objects
     * are created for all other missing properties. Use `_.setWith` to customize
     * `path` creation.
     *
     * **Note:** This method mutates `object`.
     *
     * @static
     * @memberOf _
     * @since 3.7.0
     * @category Object
     * @param {Object} object The object to modify.
     * @param {Array|string} path The path of the property to set.
     * @param {*} value The value to set.
     * @returns {Object} Returns `object`.
     * @example
     *
     * var object = { 'a': [{ 'b': { 'c': 3 } }] };
     *
     * _.set(object, 'a[0].b.c', 4);
     * console.log(object.a[0].b.c);
     * // => 4
     *
     * _.set(object, ['x', '0', 'y', 'z'], 5);
     * console.log(object.x[0].y.z);
     * // => 5
     */
    function set(object, path, value) {
      return object == null ? object : baseSet(object, path, value);
    }

    /**
     * This method is like `_.set` except that it accepts `customizer` which is
     * invoked to produce the objects of `path`.  If `customizer` returns `undefined`
     * path creation is handled by the method instead. The `customizer` is invoked
     * with three arguments: (nsValue, key, nsObject).
     *
     * **Note:** This method mutates `object`.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Object
     * @param {Object} object The object to modify.
     * @param {Array|string} path The path of the property to set.
     * @param {*} value The value to set.
     * @param {Function} [customizer] The function to customize assigned values.
     * @returns {Object} Returns `object`.
     * @example
     *
     * var object = {};
     *
     * _.setWith(object, '[0][1]', 'a', Object);
     * // => { '0': { '1': 'a' } }
     */
    function setWith(object, path, value, customizer) {
      customizer = typeof customizer == 'function' ? customizer : undefined;
      return object == null ? object : baseSet(object, path, value, customizer);
    }

    /**
     * Creates an array of own enumerable string keyed-value pairs for `object`
     * which can be consumed by `_.fromPairs`. If `object` is a map or set, its
     * entries are returned.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @alias entries
     * @category Object
     * @param {Object} object The object to query.
     * @returns {Array} Returns the key-value pairs.
     * @example
     *
     * function Foo() {
     *   this.a = 1;
     *   this.b = 2;
     * }
     *
     * Foo.prototype.c = 3;
     *
     * _.toPairs(new Foo);
     * // => [['a', 1], ['b', 2]] (iteration order is not guaranteed)
     */
    var toPairs = createToPairs(keys);

    /**
     * Creates an array of own and inherited enumerable string keyed-value pairs
     * for `object` which can be consumed by `_.fromPairs`. If `object` is a map
     * or set, its entries are returned.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @alias entriesIn
     * @category Object
     * @param {Object} object The object to query.
     * @returns {Array} Returns the key-value pairs.
     * @example
     *
     * function Foo() {
     *   this.a = 1;
     *   this.b = 2;
     * }
     *
     * Foo.prototype.c = 3;
     *
     * _.toPairsIn(new Foo);
     * // => [['a', 1], ['b', 2], ['c', 3]] (iteration order is not guaranteed)
     */
    var toPairsIn = createToPairs(keysIn);

    /**
     * An alternative to `_.reduce`; this method transforms `object` to a new
     * `accumulator` object which is the result of running each of its own
     * enumerable string keyed properties thru `iteratee`, with each invocation
     * potentially mutating the `accumulator` object. If `accumulator` is not
     * provided, a new object with the same `[[Prototype]]` will be used. The
     * iteratee is invoked with four arguments: (accumulator, value, key, object).
     * Iteratee functions may exit iteration early by explicitly returning `false`.
     *
     * @static
     * @memberOf _
     * @since 1.3.0
     * @category Object
     * @param {Object} object The object to iterate over.
     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
     * @param {*} [accumulator] The custom accumulator value.
     * @returns {*} Returns the accumulated value.
     * @example
     *
     * _.transform([2, 3, 4], function(result, n) {
     *   result.push(n *= n);
     *   return n % 2 == 0;
     * }, []);
     * // => [4, 9]
     *
     * _.transform({ 'a': 1, 'b': 2, 'c': 1 }, function(result, value, key) {
     *   (result[value] || (result[value] = [])).push(key);
     * }, {});
     * // => { '1': ['a', 'c'], '2': ['b'] }
     */
    function transform(object, iteratee, accumulator) {
      var isArr = isArray(object),
          isArrLike = isArr || isBuffer(object) || isTypedArray(object);

      iteratee = getIteratee(iteratee, 4);
      if (accumulator == null) {
        var Ctor = object && object.constructor;
        if (isArrLike) {
          accumulator = isArr ? new Ctor : [];
        }
        else if (isObject(object)) {
          accumulator = isFunction(Ctor) ? baseCreate(getPrototype(object)) : {};
        }
        else {
          accumulator = {};
        }
      }
      (isArrLike ? arrayEach : baseForOwn)(object, function(value, index, object) {
        return iteratee(accumulator, value, index, object);
      });
      return accumulator;
    }

    /**
     * Removes the property at `path` of `object`.
     *
     * **Note:** This method mutates `object`.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Object
     * @param {Object} object The object to modify.
     * @param {Array|string} path The path of the property to unset.
     * @returns {boolean} Returns `true` if the property is deleted, else `false`.
     * @example
     *
     * var object = { 'a': [{ 'b': { 'c': 7 } }] };
     * _.unset(object, 'a[0].b.c');
     * // => true
     *
     * console.log(object);
     * // => { 'a': [{ 'b': {} }] };
     *
     * _.unset(object, ['a', '0', 'b', 'c']);
     * // => true
     *
     * console.log(object);
     * // => { 'a': [{ 'b': {} }] };
     */
    function unset(object, path) {
      return object == null ? true : baseUnset(object, path);
    }

    /**
     * This method is like `_.set` except that accepts `updater` to produce the
     * value to set. Use `_.updateWith` to customize `path` creation. The `updater`
     * is invoked with one argument: (value).
     *
     * **Note:** This method mutates `object`.
     *
     * @static
     * @memberOf _
     * @since 4.6.0
     * @category Object
     * @param {Object} object The object to modify.
     * @param {Array|string} path The path of the property to set.
     * @param {Function} updater The function to produce the updated value.
     * @returns {Object} Returns `object`.
     * @example
     *
     * var object = { 'a': [{ 'b': { 'c': 3 } }] };
     *
     * _.update(object, 'a[0].b.c', function(n) { return n * n; });
     * console.log(object.a[0].b.c);
     * // => 9
     *
     * _.update(object, 'x[0].y.z', function(n) { return n ? n + 1 : 0; });
     * console.log(object.x[0].y.z);
     * // => 0
     */
    function update(object, path, updater) {
      return object == null ? object : baseUpdate(object, path, castFunction(updater));
    }

    /**
     * This method is like `_.update` except that it accepts `customizer` which is
     * invoked to produce the objects of `path`.  If `customizer` returns `undefined`
     * path creation is handled by the method instead. The `customizer` is invoked
     * with three arguments: (nsValue, key, nsObject).
     *
     * **Note:** This method mutates `object`.
     *
     * @static
     * @memberOf _
     * @since 4.6.0
     * @category Object
     * @param {Object} object The object to modify.
     * @param {Array|string} path The path of the property to set.
     * @param {Function} updater The function to produce the updated value.
     * @param {Function} [customizer] The function to customize assigned values.
     * @returns {Object} Returns `object`.
     * @example
     *
     * var object = {};
     *
     * _.updateWith(object, '[0][1]', _.constant('a'), Object);
     * // => { '0': { '1': 'a' } }
     */
    function updateWith(object, path, updater, customizer) {
      customizer = typeof customizer == 'function' ? customizer : undefined;
      return object == null ? object : baseUpdate(object, path, castFunction(updater), customizer);
    }

    /**
     * Creates an array of the own enumerable string keyed property values of `object`.
     *
     * **Note:** Non-object values are coerced to objects.
     *
     * @static
     * @since 0.1.0
     * @memberOf _
     * @category Object
     * @param {Object} object The object to query.
     * @returns {Array} Returns the array of property values.
     * @example
     *
     * function Foo() {
     *   this.a = 1;
     *   this.b = 2;
     * }
     *
     * Foo.prototype.c = 3;
     *
     * _.values(new Foo);
     * // => [1, 2] (iteration order is not guaranteed)
     *
     * _.values('hi');
     * // => ['h', 'i']
     */
    function values(object) {
      return object == null ? [] : baseValues(object, keys(object));
    }

    /**
     * Creates an array of the own and inherited enumerable string keyed property
     * values of `object`.
     *
     * **Note:** Non-object values are coerced to objects.
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category Object
     * @param {Object} object The object to query.
     * @returns {Array} Returns the array of property values.
     * @example
     *
     * function Foo() {
     *   this.a = 1;
     *   this.b = 2;
     * }
     *
     * Foo.prototype.c = 3;
     *
     * _.valuesIn(new Foo);
     * // => [1, 2, 3] (iteration order is not guaranteed)
     */
    function valuesIn(object) {
      return object == null ? [] : baseValues(object, keysIn(object));
    }

    /*------------------------------------------------------------------------*/

    /**
     * Clamps `number` within the inclusive `lower` and `upper` bounds.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Number
     * @param {number} number The number to clamp.
     * @param {number} [lower] The lower bound.
     * @param {number} upper The upper bound.
     * @returns {number} Returns the clamped number.
     * @example
     *
     * _.clamp(-10, -5, 5);
     * // => -5
     *
     * _.clamp(10, -5, 5);
     * // => 5
     */
    function clamp(number, lower, upper) {
      if (upper === undefined) {
        upper = lower;
        lower = undefined;
      }
      if (upper !== undefined) {
        upper = toNumber(upper);
        upper = upper === upper ? upper : 0;
      }
      if (lower !== undefined) {
        lower = toNumber(lower);
        lower = lower === lower ? lower : 0;
      }
      return baseClamp(toNumber(number), lower, upper);
    }

    /**
     * Checks if `n` is between `start` and up to, but not including, `end`. If
     * `end` is not specified, it's set to `start` with `start` then set to `0`.
     * If `start` is greater than `end` the params are swapped to support
     * negative ranges.
     *
     * @static
     * @memberOf _
     * @since 3.3.0
     * @category Number
     * @param {number} number The number to check.
     * @param {number} [start=0] The start of the range.
     * @param {number} end The end of the range.
     * @returns {boolean} Returns `true` if `number` is in the range, else `false`.
     * @see _.range, _.rangeRight
     * @example
     *
     * _.inRange(3, 2, 4);
     * // => true
     *
     * _.inRange(4, 8);
     * // => true
     *
     * _.inRange(4, 2);
     * // => false
     *
     * _.inRange(2, 2);
     * // => false
     *
     * _.inRange(1.2, 2);
     * // => true
     *
     * _.inRange(5.2, 4);
     * // => false
     *
     * _.inRange(-3, -2, -6);
     * // => true
     */
    function inRange(number, start, end) {
      start = toFinite(start);
      if (end === undefined) {
        end = start;
        start = 0;
      } else {
        end = toFinite(end);
      }
      number = toNumber(number);
      return baseInRange(number, start, end);
    }

    /**
     * Produces a random number between the inclusive `lower` and `upper` bounds.
     * If only one argument is provided a number between `0` and the given number
     * is returned. If `floating` is `true`, or either `lower` or `upper` are
     * floats, a floating-point number is returned instead of an integer.
     *
     * **Note:** JavaScript follows the IEEE-754 standard for resolving
     * floating-point values which can produce unexpected results.
     *
     * @static
     * @memberOf _
     * @since 0.7.0
     * @category Number
     * @param {number} [lower=0] The lower bound.
     * @param {number} [upper=1] The upper bound.
     * @param {boolean} [floating] Specify returning a floating-point number.
     * @returns {number} Returns the random number.
     * @example
     *
     * _.random(0, 5);
     * // => an integer between 0 and 5
     *
     * _.random(5);
     * // => also an integer between 0 and 5
     *
     * _.random(5, true);
     * // => a floating-point number between 0 and 5
     *
     * _.random(1.2, 5.2);
     * // => a floating-point number between 1.2 and 5.2
     */
    function random(lower, upper, floating) {
      if (floating && typeof floating != 'boolean' && isIterateeCall(lower, upper, floating)) {
        upper = floating = undefined;
      }
      if (floating === undefined) {
        if (typeof upper == 'boolean') {
          floating = upper;
          upper = undefined;
        }
        else if (typeof lower == 'boolean') {
          floating = lower;
          lower = undefined;
        }
      }
      if (lower === undefined && upper === undefined) {
        lower = 0;
        upper = 1;
      }
      else {
        lower = toFinite(lower);
        if (upper === undefined) {
          upper = lower;
          lower = 0;
        } else {
          upper = toFinite(upper);
        }
      }
      if (lower > upper) {
        var temp = lower;
        lower = upper;
        upper = temp;
      }
      if (floating || lower % 1 || upper % 1) {
        var rand = nativeRandom();
        return nativeMin(lower + (rand * (upper - lower + freeParseFloat('1e-' + ((rand + '').length - 1)))), upper);
      }
      return baseRandom(lower, upper);
    }

    /*------------------------------------------------------------------------*/

    /**
     * Converts `string` to [camel case](https://en.wikipedia.org/wiki/CamelCase).
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category String
     * @param {string} [string=''] The string to convert.
     * @returns {string} Returns the camel cased string.
     * @example
     *
     * _.camelCase('Foo Bar');
     * // => 'fooBar'
     *
     * _.camelCase('--foo-bar--');
     * // => 'fooBar'
     *
     * _.camelCase('__FOO_BAR__');
     * // => 'fooBar'
     */
    var camelCase = createCompounder(function(result, word, index) {
      word = word.toLowerCase();
      return result + (index ? capitalize(word) : word);
    });

    /**
     * Converts the first character of `string` to upper case and the remaining
     * to lower case.
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category String
     * @param {string} [string=''] The string to capitalize.
     * @returns {string} Returns the capitalized string.
     * @example
     *
     * _.capitalize('FRED');
     * // => 'Fred'
     */
    function capitalize(string) {
      return upperFirst(toString(string).toLowerCase());
    }

    /**
     * Deburrs `string` by converting
     * [Latin-1 Supplement](https://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block)#Character_table)
     * and [Latin Extended-A](https://en.wikipedia.org/wiki/Latin_Extended-A)
     * letters to basic Latin letters and removing
     * [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks).
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category String
     * @param {string} [string=''] The string to deburr.
     * @returns {string} Returns the deburred string.
     * @example
     *
     * _.deburr('déjà vu');
     * // => 'deja vu'
     */
    function deburr(string) {
      string = toString(string);
      return string && string.replace(reLatin, deburrLetter).replace(reComboMark, '');
    }

    /**
     * Checks if `string` ends with the given target string.
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category String
     * @param {string} [string=''] The string to inspect.
     * @param {string} [target] The string to search for.
     * @param {number} [position=string.length] The position to search up to.
     * @returns {boolean} Returns `true` if `string` ends with `target`,
     *  else `false`.
     * @example
     *
     * _.endsWith('abc', 'c');
     * // => true
     *
     * _.endsWith('abc', 'b');
     * // => false
     *
     * _.endsWith('abc', 'b', 2);
     * // => true
     */
    function endsWith(string, target, position) {
      string = toString(string);
      target = baseToString(target);

      var length = string.length;
      position = position === undefined
        ? length
        : baseClamp(toInteger(position), 0, length);

      var end = position;
      position -= target.length;
      return position >= 0 && string.slice(position, end) == target;
    }

    /**
     * Converts the characters "&", "<", ">", '"', and "'" in `string` to their
     * corresponding HTML entities.
     *
     * **Note:** No other characters are escaped. To escape additional
     * characters use a third-party library like [_he_](https://mths.be/he).
     *
     * Though the ">" character is escaped for symmetry, characters like
     * ">" and "/" don't need escaping in HTML and have no special meaning
     * unless they're part of a tag or unquoted attribute value. See
     * [Mathias Bynens's article](https://mathiasbynens.be/notes/ambiguous-ampersands)
     * (under "semi-related fun fact") for more details.
     *
     * When working with HTML you should always
     * [quote attribute values](http://wonko.com/post/html-escaping) to reduce
     * XSS vectors.
     *
     * @static
     * @since 0.1.0
     * @memberOf _
     * @category String
     * @param {string} [string=''] The string to escape.
     * @returns {string} Returns the escaped string.
     * @example
     *
     * _.escape('fred, barney, & pebbles');
     * // => 'fred, barney, &amp; pebbles'
     */
    function escape(string) {
      string = toString(string);
      return (string && reHasUnescapedHtml.test(string))
        ? string.replace(reUnescapedHtml, escapeHtmlChar)
        : string;
    }

    /**
     * Escapes the `RegExp` special characters "^", "$", "\", ".", "*", "+",
     * "?", "(", ")", "[", "]", "{", "}", and "|" in `string`.
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category String
     * @param {string} [string=''] The string to escape.
     * @returns {string} Returns the escaped string.
     * @example
     *
     * _.escapeRegExp('[lodash](https://lodash.com/)');
     * // => '\[lodash\]\(https://lodash\.com/\)'
     */
    function escapeRegExp(string) {
      string = toString(string);
      return (string && reHasRegExpChar.test(string))
        ? string.replace(reRegExpChar, '\\$&')
        : string;
    }

    /**
     * Converts `string` to
     * [kebab case](https://en.wikipedia.org/wiki/Letter_case#Special_case_styles).
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category String
     * @param {string} [string=''] The string to convert.
     * @returns {string} Returns the kebab cased string.
     * @example
     *
     * _.kebabCase('Foo Bar');
     * // => 'foo-bar'
     *
     * _.kebabCase('fooBar');
     * // => 'foo-bar'
     *
     * _.kebabCase('__FOO_BAR__');
     * // => 'foo-bar'
     */
    var kebabCase = createCompounder(function(result, word, index) {
      return result + (index ? '-' : '') + word.toLowerCase();
    });

    /**
     * Converts `string`, as space separated words, to lower case.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category String
     * @param {string} [string=''] The string to convert.
     * @returns {string} Returns the lower cased string.
     * @example
     *
     * _.lowerCase('--Foo-Bar--');
     * // => 'foo bar'
     *
     * _.lowerCase('fooBar');
     * // => 'foo bar'
     *
     * _.lowerCase('__FOO_BAR__');
     * // => 'foo bar'
     */
    var lowerCase = createCompounder(function(result, word, index) {
      return result + (index ? ' ' : '') + word.toLowerCase();
    });

    /**
     * Converts the first character of `string` to lower case.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category String
     * @param {string} [string=''] The string to convert.
     * @returns {string} Returns the converted string.
     * @example
     *
     * _.lowerFirst('Fred');
     * // => 'fred'
     *
     * _.lowerFirst('FRED');
     * // => 'fRED'
     */
    var lowerFirst = createCaseFirst('toLowerCase');

    /**
     * Pads `string` on the left and right sides if it's shorter than `length`.
     * Padding characters are truncated if they can't be evenly divided by `length`.
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category String
     * @param {string} [string=''] The string to pad.
     * @param {number} [length=0] The padding length.
     * @param {string} [chars=' '] The string used as padding.
     * @returns {string} Returns the padded string.
     * @example
     *
     * _.pad('abc', 8);
     * // => '  abc   '
     *
     * _.pad('abc', 8, '_-');
     * // => '_-abc_-_'
     *
     * _.pad('abc', 3);
     * // => 'abc'
     */
    function pad(string, length, chars) {
      string = toString(string);
      length = toInteger(length);

      var strLength = length ? stringSize(string) : 0;
      if (!length || strLength >= length) {
        return string;
      }
      var mid = (length - strLength) / 2;
      return (
        createPadding(nativeFloor(mid), chars) +
        string +
        createPadding(nativeCeil(mid), chars)
      );
    }

    /**
     * Pads `string` on the right side if it's shorter than `length`. Padding
     * characters are truncated if they exceed `length`.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category String
     * @param {string} [string=''] The string to pad.
     * @param {number} [length=0] The padding length.
     * @param {string} [chars=' '] The string used as padding.
     * @returns {string} Returns the padded string.
     * @example
     *
     * _.padEnd('abc', 6);
     * // => 'abc   '
     *
     * _.padEnd('abc', 6, '_-');
     * // => 'abc_-_'
     *
     * _.padEnd('abc', 3);
     * // => 'abc'
     */
    function padEnd(string, length, chars) {
      string = toString(string);
      length = toInteger(length);

      var strLength = length ? stringSize(string) : 0;
      return (length && strLength < length)
        ? (string + createPadding(length - strLength, chars))
        : string;
    }

    /**
     * Pads `string` on the left side if it's shorter than `length`. Padding
     * characters are truncated if they exceed `length`.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category String
     * @param {string} [string=''] The string to pad.
     * @param {number} [length=0] The padding length.
     * @param {string} [chars=' '] The string used as padding.
     * @returns {string} Returns the padded string.
     * @example
     *
     * _.padStart('abc', 6);
     * // => '   abc'
     *
     * _.padStart('abc', 6, '_-');
     * // => '_-_abc'
     *
     * _.padStart('abc', 3);
     * // => 'abc'
     */
    function padStart(string, length, chars) {
      string = toString(string);
      length = toInteger(length);

      var strLength = length ? stringSize(string) : 0;
      return (length && strLength < length)
        ? (createPadding(length - strLength, chars) + string)
        : string;
    }

    /**
     * Converts `string` to an integer of the specified radix. If `radix` is
     * `undefined` or `0`, a `radix` of `10` is used unless `value` is a
     * hexadecimal, in which case a `radix` of `16` is used.
     *
     * **Note:** This method aligns with the
     * [ES5 implementation](https://es5.github.io/#x15.1.2.2) of `parseInt`.
     *
     * @static
     * @memberOf _
     * @since 1.1.0
     * @category String
     * @param {string} string The string to convert.
     * @param {number} [radix=10] The radix to interpret `value` by.
     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
     * @returns {number} Returns the converted integer.
     * @example
     *
     * _.parseInt('08');
     * // => 8
     *
     * _.map(['6', '08', '10'], _.parseInt);
     * // => [6, 8, 10]
     */
    function parseInt(string, radix, guard) {
      if (guard || radix == null) {
        radix = 0;
      } else if (radix) {
        radix = +radix;
      }
      return nativeParseInt(toString(string).replace(reTrimStart, ''), radix || 0);
    }

    /**
     * Repeats the given string `n` times.
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category String
     * @param {string} [string=''] The string to repeat.
     * @param {number} [n=1] The number of times to repeat the string.
     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
     * @returns {string} Returns the repeated string.
     * @example
     *
     * _.repeat('*', 3);
     * // => '***'
     *
     * _.repeat('abc', 2);
     * // => 'abcabc'
     *
     * _.repeat('abc', 0);
     * // => ''
     */
    function repeat(string, n, guard) {
      if ((guard ? isIterateeCall(string, n, guard) : n === undefined)) {
        n = 1;
      } else {
        n = toInteger(n);
      }
      return baseRepeat(toString(string), n);
    }

    /**
     * Replaces matches for `pattern` in `string` with `replacement`.
     *
     * **Note:** This method is based on
     * [`String#replace`](https://mdn.io/String/replace).
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category String
     * @param {string} [string=''] The string to modify.
     * @param {RegExp|string} pattern The pattern to replace.
     * @param {Function|string} replacement The match replacement.
     * @returns {string} Returns the modified string.
     * @example
     *
     * _.replace('Hi Fred', 'Fred', 'Barney');
     * // => 'Hi Barney'
     */
    function replace() {
      var args = arguments,
          string = toString(args[0]);

      return args.length < 3 ? string : string.replace(args[1], args[2]);
    }

    /**
     * Converts `string` to
     * [snake case](https://en.wikipedia.org/wiki/Snake_case).
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category String
     * @param {string} [string=''] The string to convert.
     * @returns {string} Returns the snake cased string.
     * @example
     *
     * _.snakeCase('Foo Bar');
     * // => 'foo_bar'
     *
     * _.snakeCase('fooBar');
     * // => 'foo_bar'
     *
     * _.snakeCase('--FOO-BAR--');
     * // => 'foo_bar'
     */
    var snakeCase = createCompounder(function(result, word, index) {
      return result + (index ? '_' : '') + word.toLowerCase();
    });

    /**
     * Splits `string` by `separator`.
     *
     * **Note:** This method is based on
     * [`String#split`](https://mdn.io/String/split).
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category String
     * @param {string} [string=''] The string to split.
     * @param {RegExp|string} separator The separator pattern to split by.
     * @param {number} [limit] The length to truncate results to.
     * @returns {Array} Returns the string segments.
     * @example
     *
     * _.split('a-b-c', '-', 2);
     * // => ['a', 'b']
     */
    function split(string, separator, limit) {
      if (limit && typeof limit != 'number' && isIterateeCall(string, separator, limit)) {
        separator = limit = undefined;
      }
      limit = limit === undefined ? MAX_ARRAY_LENGTH : limit >>> 0;
      if (!limit) {
        return [];
      }
      string = toString(string);
      if (string && (
            typeof separator == 'string' ||
            (separator != null && !isRegExp(separator))
          )) {
        separator = baseToString(separator);
        if (!separator && hasUnicode(string)) {
          return castSlice(stringToArray(string), 0, limit);
        }
      }
      return string.split(separator, limit);
    }

    /**
     * Converts `string` to
     * [start case](https://en.wikipedia.org/wiki/Letter_case#Stylistic_or_specialised_usage).
     *
     * @static
     * @memberOf _
     * @since 3.1.0
     * @category String
     * @param {string} [string=''] The string to convert.
     * @returns {string} Returns the start cased string.
     * @example
     *
     * _.startCase('--foo-bar--');
     * // => 'Foo Bar'
     *
     * _.startCase('fooBar');
     * // => 'Foo Bar'
     *
     * _.startCase('__FOO_BAR__');
     * // => 'FOO BAR'
     */
    var startCase = createCompounder(function(result, word, index) {
      return result + (index ? ' ' : '') + upperFirst(word);
    });

    /**
     * Checks if `string` starts with the given target string.
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category String
     * @param {string} [string=''] The string to inspect.
     * @param {string} [target] The string to search for.
     * @param {number} [position=0] The position to search from.
     * @returns {boolean} Returns `true` if `string` starts with `target`,
     *  else `false`.
     * @example
     *
     * _.startsWith('abc', 'a');
     * // => true
     *
     * _.startsWith('abc', 'b');
     * // => false
     *
     * _.startsWith('abc', 'b', 1);
     * // => true
     */
    function startsWith(string, target, position) {
      string = toString(string);
      position = position == null
        ? 0
        : baseClamp(toInteger(position), 0, string.length);

      target = baseToString(target);
      return string.slice(position, position + target.length) == target;
    }

    /**
     * Creates a compiled template function that can interpolate data properties
     * in "interpolate" delimiters, HTML-escape interpolated data properties in
     * "escape" delimiters, and execute JavaScript in "evaluate" delimiters. Data
     * properties may be accessed as free variables in the template. If a setting
     * object is given, it takes precedence over `_.templateSettings` values.
     *
     * **Note:** In the development build `_.template` utilizes
     * [sourceURLs](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl)
     * for easier debugging.
     *
     * For more information on precompiling templates see
     * [lodash's custom builds documentation](https://lodash.com/custom-builds).
     *
     * For more information on Chrome extension sandboxes see
     * [Chrome's extensions documentation](https://developer.chrome.com/extensions/sandboxingEval).
     *
     * @static
     * @since 0.1.0
     * @memberOf _
     * @category String
     * @param {string} [string=''] The template string.
     * @param {Object} [options={}] The options object.
     * @param {RegExp} [options.escape=_.templateSettings.escape]
     *  The HTML "escape" delimiter.
     * @param {RegExp} [options.evaluate=_.templateSettings.evaluate]
     *  The "evaluate" delimiter.
     * @param {Object} [options.imports=_.templateSettings.imports]
     *  An object to import into the template as free variables.
     * @param {RegExp} [options.interpolate=_.templateSettings.interpolate]
     *  The "interpolate" delimiter.
     * @param {string} [options.sourceURL='lodash.templateSources[n]']
     *  The sourceURL of the compiled template.
     * @param {string} [options.variable='obj']
     *  The data object variable name.
     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
     * @returns {Function} Returns the compiled template function.
     * @example
     *
     * // Use the "interpolate" delimiter to create a compiled template.
     * var compiled = _.template('hello <%= user %>!');
     * compiled({ 'user': 'fred' });
     * // => 'hello fred!'
     *
     * // Use the HTML "escape" delimiter to escape data property values.
     * var compiled = _.template('<b><%- value %></b>');
     * compiled({ 'value': '<script>' });
     * // => '<b>&lt;script&gt;</b>'
     *
     * // Use the "evaluate" delimiter to execute JavaScript and generate HTML.
     * var compiled = _.template('<% _.forEach(users, function(user) { %><li><%- user %></li><% }); %>');
     * compiled({ 'users': ['fred', 'barney'] });
     * // => '<li>fred</li><li>barney</li>'
     *
     * // Use the internal `print` function in "evaluate" delimiters.
     * var compiled = _.template('<% print("hello " + user); %>!');
     * compiled({ 'user': 'barney' });
     * // => 'hello barney!'
     *
     * // Use the ES template literal delimiter as an "interpolate" delimiter.
     * // Disable support by replacing the "interpolate" delimiter.
     * var compiled = _.template('hello ${ user }!');
     * compiled({ 'user': 'pebbles' });
     * // => 'hello pebbles!'
     *
     * // Use backslashes to treat delimiters as plain text.
     * var compiled = _.template('<%= "\\<%- value %\\>" %>');
     * compiled({ 'value': 'ignored' });
     * // => '<%- value %>'
     *
     * // Use the `imports` option to import `jQuery` as `jq`.
     * var text = '<% jq.each(users, function(user) { %><li><%- user %></li><% }); %>';
     * var compiled = _.template(text, { 'imports': { 'jq': jQuery } });
     * compiled({ 'users': ['fred', 'barney'] });
     * // => '<li>fred</li><li>barney</li>'
     *
     * // Use the `sourceURL` option to specify a custom sourceURL for the template.
     * var compiled = _.template('hello <%= user %>!', { 'sourceURL': '/basic/greeting.jst' });
     * compiled(data);
     * // => Find the source of "greeting.jst" under the Sources tab or Resources panel of the web inspector.
     *
     * // Use the `variable` option to ensure a with-statement isn't used in the compiled template.
     * var compiled = _.template('hi <%= data.user %>!', { 'variable': 'data' });
     * compiled.source;
     * // => function(data) {
     * //   var __t, __p = '';
     * //   __p += 'hi ' + ((__t = ( data.user )) == null ? '' : __t) + '!';
     * //   return __p;
     * // }
     *
     * // Use custom template delimiters.
     * _.templateSettings.interpolate = /{{([\s\S]+?)}}/g;
     * var compiled = _.template('hello {{ user }}!');
     * compiled({ 'user': 'mustache' });
     * // => 'hello mustache!'
     *
     * // Use the `source` property to inline compiled templates for meaningful
     * // line numbers in error messages and stack traces.
     * fs.writeFileSync(path.join(process.cwd(), 'jst.js'), '\
     *   var JST = {\
     *     "main": ' + _.template(mainText).source + '\
     *   };\
     * ');
     */
    function template(string, options, guard) {
      // Based on John Resig's `tmpl` implementation
      // (http://ejohn.org/blog/javascript-micro-templating/)
      // and Laura Doktorova's doT.js (https://github.com/olado/doT).
      var settings = lodash.templateSettings;

      if (guard && isIterateeCall(string, options, guard)) {
        options = undefined;
      }
      string = toString(string);
      options = assignInWith({}, options, settings, customDefaultsAssignIn);

      var imports = assignInWith({}, options.imports, settings.imports, customDefaultsAssignIn),
          importsKeys = keys(imports),
          importsValues = baseValues(imports, importsKeys);

      var isEscaping,
          isEvaluating,
          index = 0,
          interpolate = options.interpolate || reNoMatch,
          source = "__p += '";

      // Compile the regexp to match each delimiter.
      var reDelimiters = RegExp(
        (options.escape || reNoMatch).source + '|' +
        interpolate.source + '|' +
        (interpolate === reInterpolate ? reEsTemplate : reNoMatch).source + '|' +
        (options.evaluate || reNoMatch).source + '|$'
      , 'g');

      // Use a sourceURL for easier debugging.
      var sourceURL = '//# sourceURL=' +
        ('sourceURL' in options
          ? options.sourceURL
          : ('lodash.templateSources[' + (++templateCounter) + ']')
        ) + '\n';

      string.replace(reDelimiters, function(match, escapeValue, interpolateValue, esTemplateValue, evaluateValue, offset) {
        interpolateValue || (interpolateValue = esTemplateValue);

        // Escape characters that can't be included in string literals.
        source += string.slice(index, offset).replace(reUnescapedString, escapeStringChar);

        // Replace delimiters with snippets.
        if (escapeValue) {
          isEscaping = true;
          source += "' +\n__e(" + escapeValue + ") +\n'";
        }
        if (evaluateValue) {
          isEvaluating = true;
          source += "';\n" + evaluateValue + ";\n__p += '";
        }
        if (interpolateValue) {
          source += "' +\n((__t = (" + interpolateValue + ")) == null ? '' : __t) +\n'";
        }
        index = offset + match.length;

        // The JS engine embedded in Adobe products needs `match` returned in
        // order to produce the correct `offset` value.
        return match;
      });

      source += "';\n";

      // If `variable` is not specified wrap a with-statement around the generated
      // code to add the data object to the top of the scope chain.
      var variable = options.variable;
      if (!variable) {
        source = 'with (obj) {\n' + source + '\n}\n';
      }
      // Cleanup code by stripping empty strings.
      source = (isEvaluating ? source.replace(reEmptyStringLeading, '') : source)
        .replace(reEmptyStringMiddle, '$1')
        .replace(reEmptyStringTrailing, '$1;');

      // Frame code as the function body.
      source = 'function(' + (variable || 'obj') + ') {\n' +
        (variable
          ? ''
          : 'obj || (obj = {});\n'
        ) +
        "var __t, __p = ''" +
        (isEscaping
           ? ', __e = _.escape'
           : ''
        ) +
        (isEvaluating
          ? ', __j = Array.prototype.join;\n' +
            "function print() { __p += __j.call(arguments, '') }\n"
          : ';\n'
        ) +
        source +
        'return __p\n}';

      var result = attempt(function() {
        return Function(importsKeys, sourceURL + 'return ' + source)
          .apply(undefined, importsValues);
      });

      // Provide the compiled function's source by its `toString` method or
      // the `source` property as a convenience for inlining compiled templates.
      result.source = source;
      if (isError(result)) {
        throw result;
      }
      return result;
    }

    /**
     * Converts `string`, as a whole, to lower case just like
     * [String#toLowerCase](https://mdn.io/toLowerCase).
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category String
     * @param {string} [string=''] The string to convert.
     * @returns {string} Returns the lower cased string.
     * @example
     *
     * _.toLower('--Foo-Bar--');
     * // => '--foo-bar--'
     *
     * _.toLower('fooBar');
     * // => 'foobar'
     *
     * _.toLower('__FOO_BAR__');
     * // => '__foo_bar__'
     */
    function toLower(value) {
      return toString(value).toLowerCase();
    }

    /**
     * Converts `string`, as a whole, to upper case just like
     * [String#toUpperCase](https://mdn.io/toUpperCase).
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category String
     * @param {string} [string=''] The string to convert.
     * @returns {string} Returns the upper cased string.
     * @example
     *
     * _.toUpper('--foo-bar--');
     * // => '--FOO-BAR--'
     *
     * _.toUpper('fooBar');
     * // => 'FOOBAR'
     *
     * _.toUpper('__foo_bar__');
     * // => '__FOO_BAR__'
     */
    function toUpper(value) {
      return toString(value).toUpperCase();
    }

    /**
     * Removes leading and trailing whitespace or specified characters from `string`.
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category String
     * @param {string} [string=''] The string to trim.
     * @param {string} [chars=whitespace] The characters to trim.
     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
     * @returns {string} Returns the trimmed string.
     * @example
     *
     * _.trim('  abc  ');
     * // => 'abc'
     *
     * _.trim('-_-abc-_-', '_-');
     * // => 'abc'
     *
     * _.map(['  foo  ', '  bar  '], _.trim);
     * // => ['foo', 'bar']
     */
    function trim(string, chars, guard) {
      string = toString(string);
      if (string && (guard || chars === undefined)) {
        return string.replace(reTrim, '');
      }
      if (!string || !(chars = baseToString(chars))) {
        return string;
      }
      var strSymbols = stringToArray(string),
          chrSymbols = stringToArray(chars),
          start = charsStartIndex(strSymbols, chrSymbols),
          end = charsEndIndex(strSymbols, chrSymbols) + 1;

      return castSlice(strSymbols, start, end).join('');
    }

    /**
     * Removes trailing whitespace or specified characters from `string`.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category String
     * @param {string} [string=''] The string to trim.
     * @param {string} [chars=whitespace] The characters to trim.
     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
     * @returns {string} Returns the trimmed string.
     * @example
     *
     * _.trimEnd('  abc  ');
     * // => '  abc'
     *
     * _.trimEnd('-_-abc-_-', '_-');
     * // => '-_-abc'
     */
    function trimEnd(string, chars, guard) {
      string = toString(string);
      if (string && (guard || chars === undefined)) {
        return string.replace(reTrimEnd, '');
      }
      if (!string || !(chars = baseToString(chars))) {
        return string;
      }
      var strSymbols = stringToArray(string),
          end = charsEndIndex(strSymbols, stringToArray(chars)) + 1;

      return castSlice(strSymbols, 0, end).join('');
    }

    /**
     * Removes leading whitespace or specified characters from `string`.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category String
     * @param {string} [string=''] The string to trim.
     * @param {string} [chars=whitespace] The characters to trim.
     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
     * @returns {string} Returns the trimmed string.
     * @example
     *
     * _.trimStart('  abc  ');
     * // => 'abc  '
     *
     * _.trimStart('-_-abc-_-', '_-');
     * // => 'abc-_-'
     */
    function trimStart(string, chars, guard) {
      string = toString(string);
      if (string && (guard || chars === undefined)) {
        return string.replace(reTrimStart, '');
      }
      if (!string || !(chars = baseToString(chars))) {
        return string;
      }
      var strSymbols = stringToArray(string),
          start = charsStartIndex(strSymbols, stringToArray(chars));

      return castSlice(strSymbols, start).join('');
    }

    /**
     * Truncates `string` if it's longer than the given maximum string length.
     * The last characters of the truncated string are replaced with the omission
     * string which defaults to "...".
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category String
     * @param {string} [string=''] The string to truncate.
     * @param {Object} [options={}] The options object.
     * @param {number} [options.length=30] The maximum string length.
     * @param {string} [options.omission='...'] The string to indicate text is omitted.
     * @param {RegExp|string} [options.separator] The separator pattern to truncate to.
     * @returns {string} Returns the truncated string.
     * @example
     *
     * _.truncate('hi-diddly-ho there, neighborino');
     * // => 'hi-diddly-ho there, neighbo...'
     *
     * _.truncate('hi-diddly-ho there, neighborino', {
     *   'length': 24,
     *   'separator': ' '
     * });
     * // => 'hi-diddly-ho there,...'
     *
     * _.truncate('hi-diddly-ho there, neighborino', {
     *   'length': 24,
     *   'separator': /,? +/
     * });
     * // => 'hi-diddly-ho there...'
     *
     * _.truncate('hi-diddly-ho there, neighborino', {
     *   'omission': ' [...]'
     * });
     * // => 'hi-diddly-ho there, neig [...]'
     */
    function truncate(string, options) {
      var length = DEFAULT_TRUNC_LENGTH,
          omission = DEFAULT_TRUNC_OMISSION;

      if (isObject(options)) {
        var separator = 'separator' in options ? options.separator : separator;
        length = 'length' in options ? toInteger(options.length) : length;
        omission = 'omission' in options ? baseToString(options.omission) : omission;
      }
      string = toString(string);

      var strLength = string.length;
      if (hasUnicode(string)) {
        var strSymbols = stringToArray(string);
        strLength = strSymbols.length;
      }
      if (length >= strLength) {
        return string;
      }
      var end = length - stringSize(omission);
      if (end < 1) {
        return omission;
      }
      var result = strSymbols
        ? castSlice(strSymbols, 0, end).join('')
        : string.slice(0, end);

      if (separator === undefined) {
        return result + omission;
      }
      if (strSymbols) {
        end += (result.length - end);
      }
      if (isRegExp(separator)) {
        if (string.slice(end).search(separator)) {
          var match,
              substring = result;

          if (!separator.global) {
            separator = RegExp(separator.source, toString(reFlags.exec(separator)) + 'g');
          }
          separator.lastIndex = 0;
          while ((match = separator.exec(substring))) {
            var newEnd = match.index;
          }
          result = result.slice(0, newEnd === undefined ? end : newEnd);
        }
      } else if (string.indexOf(baseToString(separator), end) != end) {
        var index = result.lastIndexOf(separator);
        if (index > -1) {
          result = result.slice(0, index);
        }
      }
      return result + omission;
    }

    /**
     * The inverse of `_.escape`; this method converts the HTML entities
     * `&amp;`, `&lt;`, `&gt;`, `&quot;`, and `&#39;` in `string` to
     * their corresponding characters.
     *
     * **Note:** No other HTML entities are unescaped. To unescape additional
     * HTML entities use a third-party library like [_he_](https://mths.be/he).
     *
     * @static
     * @memberOf _
     * @since 0.6.0
     * @category String
     * @param {string} [string=''] The string to unescape.
     * @returns {string} Returns the unescaped string.
     * @example
     *
     * _.unescape('fred, barney, &amp; pebbles');
     * // => 'fred, barney, & pebbles'
     */
    function unescape(string) {
      string = toString(string);
      return (string && reHasEscapedHtml.test(string))
        ? string.replace(reEscapedHtml, unescapeHtmlChar)
        : string;
    }

    /**
     * Converts `string`, as space separated words, to upper case.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category String
     * @param {string} [string=''] The string to convert.
     * @returns {string} Returns the upper cased string.
     * @example
     *
     * _.upperCase('--foo-bar');
     * // => 'FOO BAR'
     *
     * _.upperCase('fooBar');
     * // => 'FOO BAR'
     *
     * _.upperCase('__foo_bar__');
     * // => 'FOO BAR'
     */
    var upperCase = createCompounder(function(result, word, index) {
      return result + (index ? ' ' : '') + word.toUpperCase();
    });

    /**
     * Converts the first character of `string` to upper case.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category String
     * @param {string} [string=''] The string to convert.
     * @returns {string} Returns the converted string.
     * @example
     *
     * _.upperFirst('fred');
     * // => 'Fred'
     *
     * _.upperFirst('FRED');
     * // => 'FRED'
     */
    var upperFirst = createCaseFirst('toUpperCase');

    /**
     * Splits `string` into an array of its words.
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category String
     * @param {string} [string=''] The string to inspect.
     * @param {RegExp|string} [pattern] The pattern to match words.
     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
     * @returns {Array} Returns the words of `string`.
     * @example
     *
     * _.words('fred, barney, & pebbles');
     * // => ['fred', 'barney', 'pebbles']
     *
     * _.words('fred, barney, & pebbles', /[^, ]+/g);
     * // => ['fred', 'barney', '&', 'pebbles']
     */
    function words(string, pattern, guard) {
      string = toString(string);
      pattern = guard ? undefined : pattern;

      if (pattern === undefined) {
        return hasUnicodeWord(string) ? unicodeWords(string) : asciiWords(string);
      }
      return string.match(pattern) || [];
    }

    /*------------------------------------------------------------------------*/

    /**
     * Attempts to invoke `func`, returning either the result or the caught error
     * object. Any additional arguments are provided to `func` when it's invoked.
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category Util
     * @param {Function} func The function to attempt.
     * @param {...*} [args] The arguments to invoke `func` with.
     * @returns {*} Returns the `func` result or error object.
     * @example
     *
     * // Avoid throwing errors for invalid selectors.
     * var elements = _.attempt(function(selector) {
     *   return document.querySelectorAll(selector);
     * }, '>_>');
     *
     * if (_.isError(elements)) {
     *   elements = [];
     * }
     */
    var attempt = baseRest(function(func, args) {
      try {
        return apply(func, undefined, args);
      } catch (e) {
        return isError(e) ? e : new Error(e);
      }
    });

    /**
     * Binds methods of an object to the object itself, overwriting the existing
     * method.
     *
     * **Note:** This method doesn't set the "length" property of bound functions.
     *
     * @static
     * @since 0.1.0
     * @memberOf _
     * @category Util
     * @param {Object} object The object to bind and assign the bound methods to.
     * @param {...(string|string[])} methodNames The object method names to bind.
     * @returns {Object} Returns `object`.
     * @example
     *
     * var view = {
     *   'label': 'docs',
     *   'click': function() {
     *     console.log('clicked ' + this.label);
     *   }
     * };
     *
     * _.bindAll(view, ['click']);
     * jQuery(element).on('click', view.click);
     * // => Logs 'clicked docs' when clicked.
     */
    var bindAll = flatRest(function(object, methodNames) {
      arrayEach(methodNames, function(key) {
        key = toKey(key);
        baseAssignValue(object, key, bind(object[key], object));
      });
      return object;
    });

    /**
     * Creates a function that iterates over `pairs` and invokes the corresponding
     * function of the first predicate to return truthy. The predicate-function
     * pairs are invoked with the `this` binding and arguments of the created
     * function.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Util
     * @param {Array} pairs The predicate-function pairs.
     * @returns {Function} Returns the new composite function.
     * @example
     *
     * var func = _.cond([
     *   [_.matches({ 'a': 1 }),           _.constant('matches A')],
     *   [_.conforms({ 'b': _.isNumber }), _.constant('matches B')],
     *   [_.stubTrue,                      _.constant('no match')]
     * ]);
     *
     * func({ 'a': 1, 'b': 2 });
     * // => 'matches A'
     *
     * func({ 'a': 0, 'b': 1 });
     * // => 'matches B'
     *
     * func({ 'a': '1', 'b': '2' });
     * // => 'no match'
     */
    function cond(pairs) {
      var length = pairs == null ? 0 : pairs.length,
          toIteratee = getIteratee();

      pairs = !length ? [] : arrayMap(pairs, function(pair) {
        if (typeof pair[1] != 'function') {
          throw new TypeError(FUNC_ERROR_TEXT);
        }
        return [toIteratee(pair[0]), pair[1]];
      });

      return baseRest(function(args) {
        var index = -1;
        while (++index < length) {
          var pair = pairs[index];
          if (apply(pair[0], this, args)) {
            return apply(pair[1], this, args);
          }
        }
      });
    }

    /**
     * Creates a function that invokes the predicate properties of `source` with
     * the corresponding property values of a given object, returning `true` if
     * all predicates return truthy, else `false`.
     *
     * **Note:** The created function is equivalent to `_.conformsTo` with
     * `source` partially applied.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Util
     * @param {Object} source The object of property predicates to conform to.
     * @returns {Function} Returns the new spec function.
     * @example
     *
     * var objects = [
     *   { 'a': 2, 'b': 1 },
     *   { 'a': 1, 'b': 2 }
     * ];
     *
     * _.filter(objects, _.conforms({ 'b': function(n) { return n > 1; } }));
     * // => [{ 'a': 1, 'b': 2 }]
     */
    function conforms(source) {
      return baseConforms(baseClone(source, CLONE_DEEP_FLAG));
    }

    /**
     * Creates a function that returns `value`.
     *
     * @static
     * @memberOf _
     * @since 2.4.0
     * @category Util
     * @param {*} value The value to return from the new function.
     * @returns {Function} Returns the new constant function.
     * @example
     *
     * var objects = _.times(2, _.constant({ 'a': 1 }));
     *
     * console.log(objects);
     * // => [{ 'a': 1 }, { 'a': 1 }]
     *
     * console.log(objects[0] === objects[1]);
     * // => true
     */
    function constant(value) {
      return function() {
        return value;
      };
    }

    /**
     * Checks `value` to determine whether a default value should be returned in
     * its place. The `defaultValue` is returned if `value` is `NaN`, `null`,
     * or `undefined`.
     *
     * @static
     * @memberOf _
     * @since 4.14.0
     * @category Util
     * @param {*} value The value to check.
     * @param {*} defaultValue The default value.
     * @returns {*} Returns the resolved value.
     * @example
     *
     * _.defaultTo(1, 10);
     * // => 1
     *
     * _.defaultTo(undefined, 10);
     * // => 10
     */
    function defaultTo(value, defaultValue) {
      return (value == null || value !== value) ? defaultValue : value;
    }

    /**
     * Creates a function that returns the result of invoking the given functions
     * with the `this` binding of the created function, where each successive
     * invocation is supplied the return value of the previous.
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category Util
     * @param {...(Function|Function[])} [funcs] The functions to invoke.
     * @returns {Function} Returns the new composite function.
     * @see _.flowRight
     * @example
     *
     * function square(n) {
     *   return n * n;
     * }
     *
     * var addSquare = _.flow([_.add, square]);
     * addSquare(1, 2);
     * // => 9
     */
    var flow = createFlow();

    /**
     * This method is like `_.flow` except that it creates a function that
     * invokes the given functions from right to left.
     *
     * @static
     * @since 3.0.0
     * @memberOf _
     * @category Util
     * @param {...(Function|Function[])} [funcs] The functions to invoke.
     * @returns {Function} Returns the new composite function.
     * @see _.flow
     * @example
     *
     * function square(n) {
     *   return n * n;
     * }
     *
     * var addSquare = _.flowRight([square, _.add]);
     * addSquare(1, 2);
     * // => 9
     */
    var flowRight = createFlow(true);

    /**
     * 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 invokes `func` with the arguments of the created
     * function. If `func` is a property name, the created function returns the
     * property value for a given element. If `func` is an array or object, the
     * created function returns `true` for elements that contain the equivalent
     * source properties, otherwise it returns `false`.
     *
     * @static
     * @since 4.0.0
     * @memberOf _
     * @category Util
     * @param {*} [func=_.identity] The value to convert to a callback.
     * @returns {Function} Returns the callback.
     * @example
     *
     * var users = [
     *   { 'user': 'barney', 'age': 36, 'active': true },
     *   { 'user': 'fred',   'age': 40, 'active': false }
     * ];
     *
     * // The `_.matches` iteratee shorthand.
     * _.filter(users, _.iteratee({ 'user': 'barney', 'active': true }));
     * // => [{ 'user': 'barney', 'age': 36, 'active': true }]
     *
     * // The `_.matchesProperty` iteratee shorthand.
     * _.filter(users, _.iteratee(['user', 'fred']));
     * // => [{ 'user': 'fred', 'age': 40 }]
     *
     * // The `_.property` iteratee shorthand.
     * _.map(users, _.iteratee('user'));
     * // => ['barney', 'fred']
     *
     * // Create custom iteratee shorthands.
     * _.iteratee = _.wrap(_.iteratee, function(iteratee, func) {
     *   return !_.isRegExp(func) ? iteratee(func) : function(string) {
     *     return func.test(string);
     *   };
     * });
     *
     * _.filter(['abc', 'def'], /ef/);
     * // => ['def']
     */
    function iteratee(func) {
      return baseIteratee(typeof func == 'function' ? func : baseClone(func, CLONE_DEEP_FLAG));
    }

    /**
     * Creates a function that performs a partial deep comparison between a given
     * object and `source`, returning `true` if the given object has equivalent
     * property values, else `false`.
     *
     * **Note:** The created function is equivalent to `_.isMatch` with `source`
     * partially applied.
     *
     * Partial comparisons will match empty array and empty object `source`
     * values against any array or object value, respectively. See `_.isEqual`
     * for a list of supported value comparisons.
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category Util
     * @param {Object} source The object of property values to match.
     * @returns {Function} Returns the new spec function.
     * @example
     *
     * var objects = [
     *   { 'a': 1, 'b': 2, 'c': 3 },
     *   { 'a': 4, 'b': 5, 'c': 6 }
     * ];
     *
     * _.filter(objects, _.matches({ 'a': 4, 'c': 6 }));
     * // => [{ 'a': 4, 'b': 5, 'c': 6 }]
     */
    function matches(source) {
      return baseMatches(baseClone(source, CLONE_DEEP_FLAG));
    }

    /**
     * Creates a function that performs a partial deep comparison between the
     * value at `path` of a given object to `srcValue`, returning `true` if the
     * object value is equivalent, else `false`.
     *
     * **Note:** Partial comparisons will match empty array and empty object
     * `srcValue` values against any array or object value, respectively. See
     * `_.isEqual` for a list of supported value comparisons.
     *
     * @static
     * @memberOf _
     * @since 3.2.0
     * @category Util
     * @param {Array|string} path The path of the property to get.
     * @param {*} srcValue The value to match.
     * @returns {Function} Returns the new spec function.
     * @example
     *
     * var objects = [
     *   { 'a': 1, 'b': 2, 'c': 3 },
     *   { 'a': 4, 'b': 5, 'c': 6 }
     * ];
     *
     * _.find(objects, _.matchesProperty('a', 4));
     * // => { 'a': 4, 'b': 5, 'c': 6 }
     */
    function matchesProperty(path, srcValue) {
      return baseMatchesProperty(path, baseClone(srcValue, CLONE_DEEP_FLAG));
    }

    /**
     * Creates a function that invokes the method at `path` of a given object.
     * Any additional arguments are provided to the invoked method.
     *
     * @static
     * @memberOf _
     * @since 3.7.0
     * @category Util
     * @param {Array|string} path The path of the method to invoke.
     * @param {...*} [args] The arguments to invoke the method with.
     * @returns {Function} Returns the new invoker function.
     * @example
     *
     * var objects = [
     *   { 'a': { 'b': _.constant(2) } },
     *   { 'a': { 'b': _.constant(1) } }
     * ];
     *
     * _.map(objects, _.method('a.b'));
     * // => [2, 1]
     *
     * _.map(objects, _.method(['a', 'b']));
     * // => [2, 1]
     */
    var method = baseRest(function(path, args) {
      return function(object) {
        return baseInvoke(object, path, args);
      };
    });

    /**
     * The opposite of `_.method`; this method creates a function that invokes
     * the method at a given path of `object`. Any additional arguments are
     * provided to the invoked method.
     *
     * @static
     * @memberOf _
     * @since 3.7.0
     * @category Util
     * @param {Object} object The object to query.
     * @param {...*} [args] The arguments to invoke the method with.
     * @returns {Function} Returns the new invoker function.
     * @example
     *
     * var array = _.times(3, _.constant),
     *     object = { 'a': array, 'b': array, 'c': array };
     *
     * _.map(['a[2]', 'c[0]'], _.methodOf(object));
     * // => [2, 0]
     *
     * _.map([['a', '2'], ['c', '0']], _.methodOf(object));
     * // => [2, 0]
     */
    var methodOf = baseRest(function(object, args) {
      return function(path) {
        return baseInvoke(object, path, args);
      };
    });

    /**
     * Adds all own enumerable string keyed function properties of a source
     * object to the destination object. If `object` is a function, then methods
     * are added to its prototype as well.
     *
     * **Note:** Use `_.runInContext` to create a pristine `lodash` function to
     * avoid conflicts caused by modifying the original.
     *
     * @static
     * @since 0.1.0
     * @memberOf _
     * @category Util
     * @param {Function|Object} [object=lodash] The destination object.
     * @param {Object} source The object of functions to add.
     * @param {Object} [options={}] The options object.
     * @param {boolean} [options.chain=true] Specify whether mixins are chainable.
     * @returns {Function|Object} Returns `object`.
     * @example
     *
     * function vowels(string) {
     *   return _.filter(string, function(v) {
     *     return /[aeiou]/i.test(v);
     *   });
     * }
     *
     * _.mixin({ 'vowels': vowels });
     * _.vowels('fred');
     * // => ['e']
     *
     * _('fred').vowels().value();
     * // => ['e']
     *
     * _.mixin({ 'vowels': vowels }, { 'chain': false });
     * _('fred').vowels();
     * // => ['e']
     */
    function mixin(object, source, options) {
      var props = keys(source),
          methodNames = baseFunctions(source, props);

      if (options == null &&
          !(isObject(source) && (methodNames.length || !props.length))) {
        options = source;
        source = object;
        object = this;
        methodNames = baseFunctions(source, keys(source));
      }
      var chain = !(isObject(options) && 'chain' in options) || !!options.chain,
          isFunc = isFunction(object);

      arrayEach(methodNames, function(methodName) {
        var func = source[methodName];
        object[methodName] = func;
        if (isFunc) {
          object.prototype[methodName] = function() {
            var chainAll = this.__chain__;
            if (chain || chainAll) {
              var result = object(this.__wrapped__),
                  actions = result.__actions__ = copyArray(this.__actions__);

              actions.push({ 'func': func, 'args': arguments, 'thisArg': object });
              result.__chain__ = chainAll;
              return result;
            }
            return func.apply(object, arrayPush([this.value()], arguments));
          };
        }
      });

      return object;
    }

    /**
     * Reverts the `_` variable to its previous value and returns a reference to
     * the `lodash` function.
     *
     * @static
     * @since 0.1.0
     * @memberOf _
     * @category Util
     * @returns {Function} Returns the `lodash` function.
     * @example
     *
     * var lodash = _.noConflict();
     */
    function noConflict() {
      if (root._ === this) {
        root._ = oldDash;
      }
      return this;
    }

    /**
     * This method returns `undefined`.
     *
     * @static
     * @memberOf _
     * @since 2.3.0
     * @category Util
     * @example
     *
     * _.times(2, _.noop);
     * // => [undefined, undefined]
     */
    function noop() {
      // No operation performed.
    }

    /**
     * Creates a function that gets the argument at index `n`. If `n` is negative,
     * the nth argument from the end is returned.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Util
     * @param {number} [n=0] The index of the argument to return.
     * @returns {Function} Returns the new pass-thru function.
     * @example
     *
     * var func = _.nthArg(1);
     * func('a', 'b', 'c', 'd');
     * // => 'b'
     *
     * var func = _.nthArg(-2);
     * func('a', 'b', 'c', 'd');
     * // => 'c'
     */
    function nthArg(n) {
      n = toInteger(n);
      return baseRest(function(args) {
        return baseNth(args, n);
      });
    }

    /**
     * Creates a function that invokes `iteratees` with the arguments it receives
     * and returns their results.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Util
     * @param {...(Function|Function[])} [iteratees=[_.identity]]
     *  The iteratees to invoke.
     * @returns {Function} Returns the new function.
     * @example
     *
     * var func = _.over([Math.max, Math.min]);
     *
     * func(1, 2, 3, 4);
     * // => [4, 1]
     */
    var over = createOver(arrayMap);

    /**
     * Creates a function that checks if **all** of the `predicates` return
     * truthy when invoked with the arguments it receives.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Util
     * @param {...(Function|Function[])} [predicates=[_.identity]]
     *  The predicates to check.
     * @returns {Function} Returns the new function.
     * @example
     *
     * var func = _.overEvery([Boolean, isFinite]);
     *
     * func('1');
     * // => true
     *
     * func(null);
     * // => false
     *
     * func(NaN);
     * // => false
     */
    var overEvery = createOver(arrayEvery);

    /**
     * Creates a function that checks if **any** of the `predicates` return
     * truthy when invoked with the arguments it receives.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Util
     * @param {...(Function|Function[])} [predicates=[_.identity]]
     *  The predicates to check.
     * @returns {Function} Returns the new function.
     * @example
     *
     * var func = _.overSome([Boolean, isFinite]);
     *
     * func('1');
     * // => true
     *
     * func(null);
     * // => true
     *
     * func(NaN);
     * // => false
     */
    var overSome = createOver(arraySome);

    /**
     * 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);
    }

    /**
     * The opposite of `_.property`; this method creates a function that returns
     * the value at a given path of `object`.
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category Util
     * @param {Object} object The object to query.
     * @returns {Function} Returns the new accessor function.
     * @example
     *
     * var array = [0, 1, 2],
     *     object = { 'a': array, 'b': array, 'c': array };
     *
     * _.map(['a[2]', 'c[0]'], _.propertyOf(object));
     * // => [2, 0]
     *
     * _.map([['a', '2'], ['c', '0']], _.propertyOf(object));
     * // => [2, 0]
     */
    function propertyOf(object) {
      return function(path) {
        return object == null ? undefined : baseGet(object, path);
      };
    }

    /**
     * Creates an array of numbers (positive and/or negative) progressing from
     * `start` up to, but not including, `end`. A step of `-1` is used if a negative
     * `start` is specified without an `end` or `step`. If `end` is not specified,
     * it's set to `start` with `start` then set to `0`.
     *
     * **Note:** JavaScript follows the IEEE-754 standard for resolving
     * floating-point values which can produce unexpected results.
     *
     * @static
     * @since 0.1.0
     * @memberOf _
     * @category Util
     * @param {number} [start=0] The start of the range.
     * @param {number} end The end of the range.
     * @param {number} [step=1] The value to increment or decrement by.
     * @returns {Array} Returns the range of numbers.
     * @see _.inRange, _.rangeRight
     * @example
     *
     * _.range(4);
     * // => [0, 1, 2, 3]
     *
     * _.range(-4);
     * // => [0, -1, -2, -3]
     *
     * _.range(1, 5);
     * // => [1, 2, 3, 4]
     *
     * _.range(0, 20, 5);
     * // => [0, 5, 10, 15]
     *
     * _.range(0, -4, -1);
     * // => [0, -1, -2, -3]
     *
     * _.range(1, 4, 0);
     * // => [1, 1, 1]
     *
     * _.range(0);
     * // => []
     */
    var range = createRange();

    /**
     * This method is like `_.range` except that it populates values in
     * descending order.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Util
     * @param {number} [start=0] The start of the range.
     * @param {number} end The end of the range.
     * @param {number} [step=1] The value to increment or decrement by.
     * @returns {Array} Returns the range of numbers.
     * @see _.inRange, _.range
     * @example
     *
     * _.rangeRight(4);
     * // => [3, 2, 1, 0]
     *
     * _.rangeRight(-4);
     * // => [-3, -2, -1, 0]
     *
     * _.rangeRight(1, 5);
     * // => [4, 3, 2, 1]
     *
     * _.rangeRight(0, 20, 5);
     * // => [15, 10, 5, 0]
     *
     * _.rangeRight(0, -4, -1);
     * // => [-3, -2, -1, 0]
     *
     * _.rangeRight(1, 4, 0);
     * // => [1, 1, 1]
     *
     * _.rangeRight(0);
     * // => []
     */
    var rangeRight = createRange(true);

    /**
     * This method returns a new empty array.
     *
     * @static
     * @memberOf _
     * @since 4.13.0
     * @category Util
     * @returns {Array} Returns the new empty array.
     * @example
     *
     * var arrays = _.times(2, _.stubArray);
     *
     * console.log(arrays);
     * // => [[], []]
     *
     * console.log(arrays[0] === arrays[1]);
     * // => false
     */
    function stubArray() {
      return [];
    }

    /**
     * This method returns `false`.
     *
     * @static
     * @memberOf _
     * @since 4.13.0
     * @category Util
     * @returns {boolean} Returns `false`.
     * @example
     *
     * _.times(2, _.stubFalse);
     * // => [false, false]
     */
    function stubFalse() {
      return false;
    }

    /**
     * This method returns a new empty object.
     *
     * @static
     * @memberOf _
     * @since 4.13.0
     * @category Util
     * @returns {Object} Returns the new empty object.
     * @example
     *
     * var objects = _.times(2, _.stubObject);
     *
     * console.log(objects);
     * // => [{}, {}]
     *
     * console.log(objects[0] === objects[1]);
     * // => false
     */
    function stubObject() {
      return {};
    }

    /**
     * This method returns an empty string.
     *
     * @static
     * @memberOf _
     * @since 4.13.0
     * @category Util
     * @returns {string} Returns the empty string.
     * @example
     *
     * _.times(2, _.stubString);
     * // => ['', '']
     */
    function stubString() {
      return '';
    }

    /**
     * This method returns `true`.
     *
     * @static
     * @memberOf _
     * @since 4.13.0
     * @category Util
     * @returns {boolean} Returns `true`.
     * @example
     *
     * _.times(2, _.stubTrue);
     * // => [true, true]
     */
    function stubTrue() {
      return true;
    }

    /**
     * Invokes the iteratee `n` times, returning an array of the results of
     * each invocation. The iteratee is invoked with one argument; (index).
     *
     * @static
     * @since 0.1.0
     * @memberOf _
     * @category Util
     * @param {number} n The number of times to invoke `iteratee`.
     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
     * @returns {Array} Returns the array of results.
     * @example
     *
     * _.times(3, String);
     * // => ['0', '1', '2']
     *
     *  _.times(4, _.constant(0));
     * // => [0, 0, 0, 0]
     */
    function times(n, iteratee) {
      n = toInteger(n);
      if (n < 1 || n > MAX_SAFE_INTEGER) {
        return [];
      }
      var index = MAX_ARRAY_LENGTH,
          length = nativeMin(n, MAX_ARRAY_LENGTH);

      iteratee = getIteratee(iteratee);
      n -= MAX_ARRAY_LENGTH;

      var result = baseTimes(length, iteratee);
      while (++index < n) {
        iteratee(index);
      }
      return result;
    }

    /**
     * Converts `value` to a property path array.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Util
     * @param {*} value The value to convert.
     * @returns {Array} Returns the new property path array.
     * @example
     *
     * _.toPath('a.b.c');
     * // => ['a', 'b', 'c']
     *
     * _.toPath('a[0].b.c');
     * // => ['a', '0', 'b', 'c']
     */
    function toPath(value) {
      if (isArray(value)) {
        return arrayMap(value, toKey);
      }
      return isSymbol(value) ? [value] : copyArray(stringToPath(toString(value)));
    }

    /**
     * Generates a unique ID. If `prefix` is given, the ID is appended to it.
     *
     * @static
     * @since 0.1.0
     * @memberOf _
     * @category Util
     * @param {string} [prefix=''] The value to prefix the ID with.
     * @returns {string} Returns the unique ID.
     * @example
     *
     * _.uniqueId('contact_');
     * // => 'contact_104'
     *
     * _.uniqueId();
     * // => '105'
     */
    function uniqueId(prefix) {
      var id = ++idCounter;
      return toString(prefix) + id;
    }

    /*------------------------------------------------------------------------*/

    /**
     * Adds two numbers.
     *
     * @static
     * @memberOf _
     * @since 3.4.0
     * @category Math
     * @param {number} augend The first number in an addition.
     * @param {number} addend The second number in an addition.
     * @returns {number} Returns the total.
     * @example
     *
     * _.add(6, 4);
     * // => 10
     */
    var add = createMathOperation(function(augend, addend) {
      return augend + addend;
    }, 0);

    /**
     * Computes `number` rounded up to `precision`.
     *
     * @static
     * @memberOf _
     * @since 3.10.0
     * @category Math
     * @param {number} number The number to round up.
     * @param {number} [precision=0] The precision to round up to.
     * @returns {number} Returns the rounded up number.
     * @example
     *
     * _.ceil(4.006);
     * // => 5
     *
     * _.ceil(6.004, 2);
     * // => 6.01
     *
     * _.ceil(6040, -2);
     * // => 6100
     */
    var ceil = createRound('ceil');

    /**
     * Divide two numbers.
     *
     * @static
     * @memberOf _
     * @since 4.7.0
     * @category Math
     * @param {number} dividend The first number in a division.
     * @param {number} divisor The second number in a division.
     * @returns {number} Returns the quotient.
     * @example
     *
     * _.divide(6, 4);
     * // => 1.5
     */
    var divide = createMathOperation(function(dividend, divisor) {
      return dividend / divisor;
    }, 1);

    /**
     * Computes `number` rounded down to `precision`.
     *
     * @static
     * @memberOf _
     * @since 3.10.0
     * @category Math
     * @param {number} number The number to round down.
     * @param {number} [precision=0] The precision to round down to.
     * @returns {number} Returns the rounded down number.
     * @example
     *
     * _.floor(4.006);
     * // => 4
     *
     * _.floor(0.046, 2);
     * // => 0.04
     *
     * _.floor(4060, -2);
     * // => 4000
     */
    var floor = createRound('floor');

    /**
     * Computes the maximum value of `array`. If `array` is empty or falsey,
     * `undefined` is returned.
     *
     * @static
     * @since 0.1.0
     * @memberOf _
     * @category Math
     * @param {Array} array The array to iterate over.
     * @returns {*} Returns the maximum value.
     * @example
     *
     * _.max([4, 2, 8, 6]);
     * // => 8
     *
     * _.max([]);
     * // => undefined
     */
    function max(array) {
      return (array && array.length)
        ? baseExtremum(array, identity, baseGt)
        : undefined;
    }

    /**
     * This method is like `_.max` except that it accepts `iteratee` which is
     * invoked for each element in `array` to generate the criterion by which
     * the value is ranked. The iteratee is invoked with one argument: (value).
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Math
     * @param {Array} array The array to iterate over.
     * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
     * @returns {*} Returns the maximum value.
     * @example
     *
     * var objects = [{ 'n': 1 }, { 'n': 2 }];
     *
     * _.maxBy(objects, function(o) { return o.n; });
     * // => { 'n': 2 }
     *
     * // The `_.property` iteratee shorthand.
     * _.maxBy(objects, 'n');
     * // => { 'n': 2 }
     */
    function maxBy(array, iteratee) {
      return (array && array.length)
        ? baseExtremum(array, getIteratee(iteratee, 2), baseGt)
        : undefined;
    }

    /**
     * Computes the mean of the values in `array`.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Math
     * @param {Array} array The array to iterate over.
     * @returns {number} Returns the mean.
     * @example
     *
     * _.mean([4, 2, 8, 6]);
     * // => 5
     */
    function mean(array) {
      return baseMean(array, identity);
    }

    /**
     * This method is like `_.mean` except that it accepts `iteratee` which is
     * invoked for each element in `array` to generate the value to be averaged.
     * The iteratee is invoked with one argument: (value).
     *
     * @static
     * @memberOf _
     * @since 4.7.0
     * @category Math
     * @param {Array} array The array to iterate over.
     * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
     * @returns {number} Returns the mean.
     * @example
     *
     * var objects = [{ 'n': 4 }, { 'n': 2 }, { 'n': 8 }, { 'n': 6 }];
     *
     * _.meanBy(objects, function(o) { return o.n; });
     * // => 5
     *
     * // The `_.property` iteratee shorthand.
     * _.meanBy(objects, 'n');
     * // => 5
     */
    function meanBy(array, iteratee) {
      return baseMean(array, getIteratee(iteratee, 2));
    }

    /**
     * Computes the minimum value of `array`. If `array` is empty or falsey,
     * `undefined` is returned.
     *
     * @static
     * @since 0.1.0
     * @memberOf _
     * @category Math
     * @param {Array} array The array to iterate over.
     * @returns {*} Returns the minimum value.
     * @example
     *
     * _.min([4, 2, 8, 6]);
     * // => 2
     *
     * _.min([]);
     * // => undefined
     */
    function min(array) {
      return (array && array.length)
        ? baseExtremum(array, identity, baseLt)
        : undefined;
    }

    /**
     * This method is like `_.min` except that it accepts `iteratee` which is
     * invoked for each element in `array` to generate the criterion by which
     * the value is ranked. The iteratee is invoked with one argument: (value).
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Math
     * @param {Array} array The array to iterate over.
     * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
     * @returns {*} Returns the minimum value.
     * @example
     *
     * var objects = [{ 'n': 1 }, { 'n': 2 }];
     *
     * _.minBy(objects, function(o) { return o.n; });
     * // => { 'n': 1 }
     *
     * // The `_.property` iteratee shorthand.
     * _.minBy(objects, 'n');
     * // => { 'n': 1 }
     */
    function minBy(array, iteratee) {
      return (array && array.length)
        ? baseExtremum(array, getIteratee(iteratee, 2), baseLt)
        : undefined;
    }

    /**
     * Multiply two numbers.
     *
     * @static
     * @memberOf _
     * @since 4.7.0
     * @category Math
     * @param {number} multiplier The first number in a multiplication.
     * @param {number} multiplicand The second number in a multiplication.
     * @returns {number} Returns the product.
     * @example
     *
     * _.multiply(6, 4);
     * // => 24
     */
    var multiply = createMathOperation(function(multiplier, multiplicand) {
      return multiplier * multiplicand;
    }, 1);

    /**
     * Computes `number` rounded to `precision`.
     *
     * @static
     * @memberOf _
     * @since 3.10.0
     * @category Math
     * @param {number} number The number to round.
     * @param {number} [precision=0] The precision to round to.
     * @returns {number} Returns the rounded number.
     * @example
     *
     * _.round(4.006);
     * // => 4
     *
     * _.round(4.006, 2);
     * // => 4.01
     *
     * _.round(4060, -2);
     * // => 4100
     */
    var round = createRound('round');

    /**
     * Subtract two numbers.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Math
     * @param {number} minuend The first number in a subtraction.
     * @param {number} subtrahend The second number in a subtraction.
     * @returns {number} Returns the difference.
     * @example
     *
     * _.subtract(6, 4);
     * // => 2
     */
    var subtract = createMathOperation(function(minuend, subtrahend) {
      return minuend - subtrahend;
    }, 0);

    /**
     * Computes the sum of the values in `array`.
     *
     * @static
     * @memberOf _
     * @since 3.4.0
     * @category Math
     * @param {Array} array The array to iterate over.
     * @returns {number} Returns the sum.
     * @example
     *
     * _.sum([4, 2, 8, 6]);
     * // => 20
     */
    function sum(array) {
      return (array && array.length)
        ? baseSum(array, identity)
        : 0;
    }

    /**
     * This method is like `_.sum` except that it accepts `iteratee` which is
     * invoked for each element in `array` to generate the value to be summed.
     * The iteratee is invoked with one argument: (value).
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Math
     * @param {Array} array The array to iterate over.
     * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
     * @returns {number} Returns the sum.
     * @example
     *
     * var objects = [{ 'n': 4 }, { 'n': 2 }, { 'n': 8 }, { 'n': 6 }];
     *
     * _.sumBy(objects, function(o) { return o.n; });
     * // => 20
     *
     * // The `_.property` iteratee shorthand.
     * _.sumBy(objects, 'n');
     * // => 20
     */
    function sumBy(array, iteratee) {
      return (array && array.length)
        ? baseSum(array, getIteratee(iteratee, 2))
        : 0;
    }

    /*------------------------------------------------------------------------*/

    // Add methods that return wrapped values in chain sequences.
    lodash.after = after;
    lodash.ary = ary;
    lodash.assign = assign;
    lodash.assignIn = assignIn;
    lodash.assignInWith = assignInWith;
    lodash.assignWith = assignWith;
    lodash.at = at;
    lodash.before = before;
    lodash.bind = bind;
    lodash.bindAll = bindAll;
    lodash.bindKey = bindKey;
    lodash.castArray = castArray;
    lodash.chain = chain;
    lodash.chunk = chunk;
    lodash.compact = compact;
    lodash.concat = concat;
    lodash.cond = cond;
    lodash.conforms = conforms;
    lodash.constant = constant;
    lodash.countBy = countBy;
    lodash.create = create;
    lodash.curry = curry;
    lodash.curryRight = curryRight;
    lodash.debounce = debounce;
    lodash.defaults = defaults;
    lodash.defaultsDeep = defaultsDeep;
    lodash.defer = defer;
    lodash.delay = delay;
    lodash.difference = difference;
    lodash.differenceBy = differenceBy;
    lodash.differenceWith = differenceWith;
    lodash.drop = drop;
    lodash.dropRight = dropRight;
    lodash.dropRightWhile = dropRightWhile;
    lodash.dropWhile = dropWhile;
    lodash.fill = fill;
    lodash.filter = filter;
    lodash.flatMap = flatMap;
    lodash.flatMapDeep = flatMapDeep;
    lodash.flatMapDepth = flatMapDepth;
    lodash.flatten = flatten;
    lodash.flattenDeep = flattenDeep;
    lodash.flattenDepth = flattenDepth;
    lodash.flip = flip;
    lodash.flow = flow;
    lodash.flowRight = flowRight;
    lodash.fromPairs = fromPairs;
    lodash.functions = functions;
    lodash.functionsIn = functionsIn;
    lodash.groupBy = groupBy;
    lodash.initial = initial;
    lodash.intersection = intersection;
    lodash.intersectionBy = intersectionBy;
    lodash.intersectionWith = intersectionWith;
    lodash.invert = invert;
    lodash.invertBy = invertBy;
    lodash.invokeMap = invokeMap;
    lodash.iteratee = iteratee;
    lodash.keyBy = keyBy;
    lodash.keys = keys;
    lodash.keysIn = keysIn;
    lodash.map = map;
    lodash.mapKeys = mapKeys;
    lodash.mapValues = mapValues;
    lodash.matches = matches;
    lodash.matchesProperty = matchesProperty;
    lodash.memoize = memoize;
    lodash.merge = merge;
    lodash.mergeWith = mergeWith;
    lodash.method = method;
    lodash.methodOf = methodOf;
    lodash.mixin = mixin;
    lodash.negate = negate;
    lodash.nthArg = nthArg;
    lodash.omit = omit;
    lodash.omitBy = omitBy;
    lodash.once = once;
    lodash.orderBy = orderBy;
    lodash.over = over;
    lodash.overArgs = overArgs;
    lodash.overEvery = overEvery;
    lodash.overSome = overSome;
    lodash.partial = partial;
    lodash.partialRight = partialRight;
    lodash.partition = partition;
    lodash.pick = pick;
    lodash.pickBy = pickBy;
    lodash.property = property;
    lodash.propertyOf = propertyOf;
    lodash.pull = pull;
    lodash.pullAll = pullAll;
    lodash.pullAllBy = pullAllBy;
    lodash.pullAllWith = pullAllWith;
    lodash.pullAt = pullAt;
    lodash.range = range;
    lodash.rangeRight = rangeRight;
    lodash.rearg = rearg;
    lodash.reject = reject;
    lodash.remove = remove;
    lodash.rest = rest;
    lodash.reverse = reverse;
    lodash.sampleSize = sampleSize;
    lodash.set = set;
    lodash.setWith = setWith;
    lodash.shuffle = shuffle;
    lodash.slice = slice;
    lodash.sortBy = sortBy;
    lodash.sortedUniq = sortedUniq;
    lodash.sortedUniqBy = sortedUniqBy;
    lodash.split = split;
    lodash.spread = spread;
    lodash.tail = tail;
    lodash.take = take;
    lodash.takeRight = takeRight;
    lodash.takeRightWhile = takeRightWhile;
    lodash.takeWhile = takeWhile;
    lodash.tap = tap;
    lodash.throttle = throttle;
    lodash.thru = thru;
    lodash.toArray = toArray;
    lodash.toPairs = toPairs;
    lodash.toPairsIn = toPairsIn;
    lodash.toPath = toPath;
    lodash.toPlainObject = toPlainObject;
    lodash.transform = transform;
    lodash.unary = unary;
    lodash.union = union;
    lodash.unionBy = unionBy;
    lodash.unionWith = unionWith;
    lodash.uniq = uniq;
    lodash.uniqBy = uniqBy;
    lodash.uniqWith = uniqWith;
    lodash.unset = unset;
    lodash.unzip = unzip;
    lodash.unzipWith = unzipWith;
    lodash.update = update;
    lodash.updateWith = updateWith;
    lodash.values = values;
    lodash.valuesIn = valuesIn;
    lodash.without = without;
    lodash.words = words;
    lodash.wrap = wrap;
    lodash.xor = xor;
    lodash.xorBy = xorBy;
    lodash.xorWith = xorWith;
    lodash.zip = zip;
    lodash.zipObject = zipObject;
    lodash.zipObjectDeep = zipObjectDeep;
    lodash.zipWith = zipWith;

    // Add aliases.
    lodash.entries = toPairs;
    lodash.entriesIn = toPairsIn;
    lodash.extend = assignIn;
    lodash.extendWith = assignInWith;

    // Add methods to `lodash.prototype`.
    mixin(lodash, lodash);

    /*------------------------------------------------------------------------*/

    // Add methods that return unwrapped values in chain sequences.
    lodash.add = add;
    lodash.attempt = attempt;
    lodash.camelCase = camelCase;
    lodash.capitalize = capitalize;
    lodash.ceil = ceil;
    lodash.clamp = clamp;
    lodash.clone = clone;
    lodash.cloneDeep = cloneDeep;
    lodash.cloneDeepWith = cloneDeepWith;
    lodash.cloneWith = cloneWith;
    lodash.conformsTo = conformsTo;
    lodash.deburr = deburr;
    lodash.defaultTo = defaultTo;
    lodash.divide = divide;
    lodash.endsWith = endsWith;
    lodash.eq = eq;
    lodash.escape = escape;
    lodash.escapeRegExp = escapeRegExp;
    lodash.every = every;
    lodash.find = find;
    lodash.findIndex = findIndex;
    lodash.findKey = findKey;
    lodash.findLast = findLast;
    lodash.findLastIndex = findLastIndex;
    lodash.findLastKey = findLastKey;
    lodash.floor = floor;
    lodash.forEach = forEach;
    lodash.forEachRight = forEachRight;
    lodash.forIn = forIn;
    lodash.forInRight = forInRight;
    lodash.forOwn = forOwn;
    lodash.forOwnRight = forOwnRight;
    lodash.get = get;
    lodash.gt = gt;
    lodash.gte = gte;
    lodash.has = has;
    lodash.hasIn = hasIn;
    lodash.head = head;
    lodash.identity = identity;
    lodash.includes = includes;
    lodash.indexOf = indexOf;
    lodash.inRange = inRange;
    lodash.invoke = invoke;
    lodash.isArguments = isArguments;
    lodash.isArray = isArray;
    lodash.isArrayBuffer = isArrayBuffer;
    lodash.isArrayLike = isArrayLike;
    lodash.isArrayLikeObject = isArrayLikeObject;
    lodash.isBoolean = isBoolean;
    lodash.isBuffer = isBuffer;
    lodash.isDate = isDate;
    lodash.isElement = isElement;
    lodash.isEmpty = isEmpty;
    lodash.isEqual = isEqual;
    lodash.isEqualWith = isEqualWith;
    lodash.isError = isError;
    lodash.isFinite = isFinite;
    lodash.isFunction = isFunction;
    lodash.isInteger = isInteger;
    lodash.isLength = isLength;
    lodash.isMap = isMap;
    lodash.isMatch = isMatch;
    lodash.isMatchWith = isMatchWith;
    lodash.isNaN = isNaN;
    lodash.isNative = isNative;
    lodash.isNil = isNil;
    lodash.isNull = isNull;
    lodash.isNumber = isNumber;
    lodash.isObject = isObject;
    lodash.isObjectLike = isObjectLike;
    lodash.isPlainObject = isPlainObject;
    lodash.isRegExp = isRegExp;
    lodash.isSafeInteger = isSafeInteger;
    lodash.isSet = isSet;
    lodash.isString = isString;
    lodash.isSymbol = isSymbol;
    lodash.isTypedArray = isTypedArray;
    lodash.isUndefined = isUndefined;
    lodash.isWeakMap = isWeakMap;
    lodash.isWeakSet = isWeakSet;
    lodash.join = join;
    lodash.kebabCase = kebabCase;
    lodash.last = last;
    lodash.lastIndexOf = lastIndexOf;
    lodash.lowerCase = lowerCase;
    lodash.lowerFirst = lowerFirst;
    lodash.lt = lt;
    lodash.lte = lte;
    lodash.max = max;
    lodash.maxBy = maxBy;
    lodash.mean = mean;
    lodash.meanBy = meanBy;
    lodash.min = min;
    lodash.minBy = minBy;
    lodash.stubArray = stubArray;
    lodash.stubFalse = stubFalse;
    lodash.stubObject = stubObject;
    lodash.stubString = stubString;
    lodash.stubTrue = stubTrue;
    lodash.multiply = multiply;
    lodash.nth = nth;
    lodash.noConflict = noConflict;
    lodash.noop = noop;
    lodash.now = now;
    lodash.pad = pad;
    lodash.padEnd = padEnd;
    lodash.padStart = padStart;
    lodash.parseInt = parseInt;
    lodash.random = random;
    lodash.reduce = reduce;
    lodash.reduceRight = reduceRight;
    lodash.repeat = repeat;
    lodash.replace = replace;
    lodash.result = result;
    lodash.round = round;
    lodash.runInContext = runInContext;
    lodash.sample = sample;
    lodash.size = size;
    lodash.snakeCase = snakeCase;
    lodash.some = some;
    lodash.sortedIndex = sortedIndex;
    lodash.sortedIndexBy = sortedIndexBy;
    lodash.sortedIndexOf = sortedIndexOf;
    lodash.sortedLastIndex = sortedLastIndex;
    lodash.sortedLastIndexBy = sortedLastIndexBy;
    lodash.sortedLastIndexOf = sortedLastIndexOf;
    lodash.startCase = startCase;
    lodash.startsWith = startsWith;
    lodash.subtract = subtract;
    lodash.sum = sum;
    lodash.sumBy = sumBy;
    lodash.template = template;
    lodash.times = times;
    lodash.toFinite = toFinite;
    lodash.toInteger = toInteger;
    lodash.toLength = toLength;
    lodash.toLower = toLower;
    lodash.toNumber = toNumber;
    lodash.toSafeInteger = toSafeInteger;
    lodash.toString = toString;
    lodash.toUpper = toUpper;
    lodash.trim = trim;
    lodash.trimEnd = trimEnd;
    lodash.trimStart = trimStart;
    lodash.truncate = truncate;
    lodash.unescape = unescape;
    lodash.uniqueId = uniqueId;
    lodash.upperCase = upperCase;
    lodash.upperFirst = upperFirst;

    // Add aliases.
    lodash.each = forEach;
    lodash.eachRight = forEachRight;
    lodash.first = head;

    mixin(lodash, (function() {
      var source = {};
      baseForOwn(lodash, function(func, methodName) {
        if (!hasOwnProperty.call(lodash.prototype, methodName)) {
          source[methodName] = func;
        }
      });
      return source;
    }()), { 'chain': false });

    /*------------------------------------------------------------------------*/

    /**
     * The semantic version number.
     *
     * @static
     * @memberOf _
     * @type {string}
     */
    lodash.VERSION = VERSION;

    // Assign default placeholders.
    arrayEach(['bind', 'bindKey', 'curry', 'curryRight', 'partial', 'partialRight'], function(methodName) {
      lodash[methodName].placeholder = lodash;
    });

    // Add `LazyWrapper` methods for `_.drop` and `_.take` variants.
    arrayEach(['drop', 'take'], function(methodName, index) {
      LazyWrapper.prototype[methodName] = function(n) {
        n = n === undefined ? 1 : nativeMax(toInteger(n), 0);

        var result = (this.__filtered__ && !index)
          ? new LazyWrapper(this)
          : this.clone();

        if (result.__filtered__) {
          result.__takeCount__ = nativeMin(n, result.__takeCount__);
        } else {
          result.__views__.push({
            'size': nativeMin(n, MAX_ARRAY_LENGTH),
            'type': methodName + (result.__dir__ < 0 ? 'Right' : '')
          });
        }
        return result;
      };

      LazyWrapper.prototype[methodName + 'Right'] = function(n) {
        return this.reverse()[methodName](n).reverse();
      };
    });

    // Add `LazyWrapper` methods that accept an `iteratee` value.
    arrayEach(['filter', 'map', 'takeWhile'], function(methodName, index) {
      var type = index + 1,
          isFilter = type == LAZY_FILTER_FLAG || type == LAZY_WHILE_FLAG;

      LazyWrapper.prototype[methodName] = function(iteratee) {
        var result = this.clone();
        result.__iteratees__.push({
          'iteratee': getIteratee(iteratee, 3),
          'type': type
        });
        result.__filtered__ = result.__filtered__ || isFilter;
        return result;
      };
    });

    // Add `LazyWrapper` methods for `_.head` and `_.last`.
    arrayEach(['head', 'last'], function(methodName, index) {
      var takeName = 'take' + (index ? 'Right' : '');

      LazyWrapper.prototype[methodName] = function() {
        return this[takeName](1).value()[0];
      };
    });

    // Add `LazyWrapper` methods for `_.initial` and `_.tail`.
    arrayEach(['initial', 'tail'], function(methodName, index) {
      var dropName = 'drop' + (index ? '' : 'Right');

      LazyWrapper.prototype[methodName] = function() {
        return this.__filtered__ ? new LazyWrapper(this) : this[dropName](1);
      };
    });

    LazyWrapper.prototype.compact = function() {
      return this.filter(identity);
    };

    LazyWrapper.prototype.find = function(predicate) {
      return this.filter(predicate).head();
    };

    LazyWrapper.prototype.findLast = function(predicate) {
      return this.reverse().find(predicate);
    };

    LazyWrapper.prototype.invokeMap = baseRest(function(path, args) {
      if (typeof path == 'function') {
        return new LazyWrapper(this);
      }
      return this.map(function(value) {
        return baseInvoke(value, path, args);
      });
    });

    LazyWrapper.prototype.reject = function(predicate) {
      return this.filter(negate(getIteratee(predicate)));
    };

    LazyWrapper.prototype.slice = function(start, end) {
      start = toInteger(start);

      var result = this;
      if (result.__filtered__ && (start > 0 || end < 0)) {
        return new LazyWrapper(result);
      }
      if (start < 0) {
        result = result.takeRight(-start);
      } else if (start) {
        result = result.drop(start);
      }
      if (end !== undefined) {
        end = toInteger(end);
        result = end < 0 ? result.dropRight(-end) : result.take(end - start);
      }
      return result;
    };

    LazyWrapper.prototype.takeRightWhile = function(predicate) {
      return this.reverse().takeWhile(predicate).reverse();
    };

    LazyWrapper.prototype.toArray = function() {
      return this.take(MAX_ARRAY_LENGTH);
    };

    // Add `LazyWrapper` methods to `lodash.prototype`.
    baseForOwn(LazyWrapper.prototype, function(func, methodName) {
      var checkIteratee = /^(?:filter|find|map|reject)|While$/.test(methodName),
          isTaker = /^(?:head|last)$/.test(methodName),
          lodashFunc = lodash[isTaker ? ('take' + (methodName == 'last' ? 'Right' : '')) : methodName],
          retUnwrapped = isTaker || /^find/.test(methodName);

      if (!lodashFunc) {
        return;
      }
      lodash.prototype[methodName] = function() {
        var value = this.__wrapped__,
            args = isTaker ? [1] : arguments,
            isLazy = value instanceof LazyWrapper,
            iteratee = args[0],
            useLazy = isLazy || isArray(value);

        var interceptor = function(value) {
          var result = lodashFunc.apply(lodash, arrayPush([value], args));
          return (isTaker && chainAll) ? result[0] : result;
        };

        if (useLazy && checkIteratee && typeof iteratee == 'function' && iteratee.length != 1) {
          // Avoid lazy use if the iteratee has a "length" value other than `1`.
          isLazy = useLazy = false;
        }
        var chainAll = this.__chain__,
            isHybrid = !!this.__actions__.length,
            isUnwrapped = retUnwrapped && !chainAll,
            onlyLazy = isLazy && !isHybrid;

        if (!retUnwrapped && useLazy) {
          value = onlyLazy ? value : new LazyWrapper(this);
          var result = func.apply(value, args);
          result.__actions__.push({ 'func': thru, 'args': [interceptor], 'thisArg': undefined });
          return new LodashWrapper(result, chainAll);
        }
        if (isUnwrapped && onlyLazy) {
          return func.apply(this, args);
        }
        result = this.thru(interceptor);
        return isUnwrapped ? (isTaker ? result.value()[0] : result.value()) : result;
      };
    });

    // Add `Array` methods to `lodash.prototype`.
    arrayEach(['pop', 'push', 'shift', 'sort', 'splice', 'unshift'], function(methodName) {
      var func = arrayProto[methodName],
          chainName = /^(?:push|sort|unshift)$/.test(methodName) ? 'tap' : 'thru',
          retUnwrapped = /^(?:pop|shift)$/.test(methodName);

      lodash.prototype[methodName] = function() {
        var args = arguments;
        if (retUnwrapped && !this.__chain__) {
          var value = this.value();
          return func.apply(isArray(value) ? value : [], args);
        }
        return this[chainName](function(value) {
          return func.apply(isArray(value) ? value : [], args);
        });
      };
    });

    // Map minified method names to their real names.
    baseForOwn(LazyWrapper.prototype, function(func, methodName) {
      var lodashFunc = lodash[methodName];
      if (lodashFunc) {
        var key = (lodashFunc.name + ''),
            names = realNames[key] || (realNames[key] = []);

        names.push({ 'name': methodName, 'func': lodashFunc });
      }
    });

    realNames[createHybrid(undefined, WRAP_BIND_KEY_FLAG).name] = [{
      'name': 'wrapper',
      'func': undefined
    }];

    // Add methods to `LazyWrapper`.
    LazyWrapper.prototype.clone = lazyClone;
    LazyWrapper.prototype.reverse = lazyReverse;
    LazyWrapper.prototype.value = lazyValue;

    // Add chain sequence methods to the `lodash` wrapper.
    lodash.prototype.at = wrapperAt;
    lodash.prototype.chain = wrapperChain;
    lodash.prototype.commit = wrapperCommit;
    lodash.prototype.next = wrapperNext;
    lodash.prototype.plant = wrapperPlant;
    lodash.prototype.reverse = wrapperReverse;
    lodash.prototype.toJSON = lodash.prototype.valueOf = lodash.prototype.value = wrapperValue;

    // Add lazy aliases.
    lodash.prototype.first = lodash.prototype.head;

    if (symIterator) {
      lodash.prototype[symIterator] = wrapperToIterator;
    }
    return lodash;
  });

  /*--------------------------------------------------------------------------*/

  // Export lodash.
  var _ = runInContext();

  // Some AMD build optimizers, like r.js, check for condition patterns like:
  if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) {
    // Expose Lodash on the global object to prevent errors when Lodash is
    // loaded by a script tag in the presence of an AMD loader.
    // See http://requirejs.org/docs/errors.html#mismatch for more details.
    // Use `_.noConflict` to remove Lodash from the global object.
    root._ = _;

    // Define as an anonymous module so, through path mapping, it can be
    // referenced as the "underscore" module.
    define(function() {
      return _;
    });
  }
  // Check for `exports` after `define` in case a build optimizer adds it.
  else if (freeModule) {
    // Export for Node.js.
    (freeModule.exports = _)._ = _;
    // Export for CommonJS support.
    freeExports._ = _;
  }
  else {
    // Export to the global object.
    root._ = _;
  }
}.call(this));
PK
!<�p���Lchrome/devtools/modules/devtools/client/shared/vendor/react-dom-factories.js'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;
});

PK
!<������Ichrome/devtools/modules/devtools/client/shared/vendor/react-dom-server.js/** @license React v16.2.0
 * react-dom-server.browser.production.min.js
 *
 * 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 (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.ReactDOMServer = factory(global.React));
}(this, (function (React) { 'use strict';

/**
 * 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 message = 'Minified React error #' + code + '; visit ' + 'http://facebook.github.io/react/docs/error-decoder.html?invariant=' + code;

  for (var argIdx = 0; argIdx < argCount; argIdx++) {
    message += '&args[]=' + encodeURIComponent(arguments[argIdx + 1]);
  }

  message += ' for the full message or use the non-minified dev environment' + ' for full errors and additional helpful warnings.';

  var error = new Error(message);
  error.name = 'Invariant Violation';
  error.framesToPop = 1; // we don't care about reactProdInvariant's own frame

  throw error;
}

/**
 * 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.
 *
 */

// These attributes should be all lowercase to allow for
// case insensitive checks
var RESERVED_PROPS = {
  children: true,
  dangerouslySetInnerHTML: true,
  defaultValue: true,
  defaultChecked: true,
  innerHTML: true,
  suppressContentEditableWarning: true,
  suppressHydrationWarning: true,
  style: true
};

function checkMask(value, bitmask) {
  return (value & bitmask) === bitmask;
}

var DOMPropertyInjection = {
  /**
   * Mapping from normalized, camelcased property names to a configuration that
   * specifies how the associated DOM property should be accessed or rendered.
   */
  MUST_USE_PROPERTY: 0x1,
  HAS_BOOLEAN_VALUE: 0x4,
  HAS_NUMERIC_VALUE: 0x8,
  HAS_POSITIVE_NUMERIC_VALUE: 0x10 | 0x8,
  HAS_OVERLOADED_BOOLEAN_VALUE: 0x20,
  HAS_STRING_BOOLEAN_VALUE: 0x40,

  /**
   * Inject some specialized knowledge about the DOM. This takes a config object
   * with the following properties:
   *
   * Properties: object mapping DOM property name to one of the
   * DOMPropertyInjection constants or null. If your attribute isn't in here,
   * it won't get written to the DOM.
   *
   * DOMAttributeNames: object mapping React attribute name to the DOM
   * attribute name. Attribute names not specified use the **lowercase**
   * normalized name.
   *
   * DOMAttributeNamespaces: object mapping React attribute name to the DOM
   * attribute namespace URL. (Attribute names not specified use no namespace.)
   *
   * DOMPropertyNames: similar to DOMAttributeNames but for DOM properties.
   * Property names not specified use the normalized name.
   *
   * DOMMutationMethods: Properties that require special mutation methods. If
   * `value` is undefined, the mutation method should unset the property.
   *
   * @param {object} domPropertyConfig the config as described above.
   */
  injectDOMPropertyConfig: function (domPropertyConfig) {
    var Injection = DOMPropertyInjection;
    var Properties = domPropertyConfig.Properties || {};
    var DOMAttributeNamespaces = domPropertyConfig.DOMAttributeNamespaces || {};
    var DOMAttributeNames = domPropertyConfig.DOMAttributeNames || {};
    var DOMMutationMethods = domPropertyConfig.DOMMutationMethods || {};

    for (var propName in Properties) {
      !!properties.hasOwnProperty(propName) ? reactProdInvariant("48", propName) : void 0;

      var lowerCased = propName.toLowerCase();
      var propConfig = Properties[propName];

      var propertyInfo = {
        attributeName: lowerCased,
        attributeNamespace: null,
        propertyName: propName,
        mutationMethod: null,

        mustUseProperty: checkMask(propConfig, Injection.MUST_USE_PROPERTY),
        hasBooleanValue: checkMask(propConfig, Injection.HAS_BOOLEAN_VALUE),
        hasNumericValue: checkMask(propConfig, Injection.HAS_NUMERIC_VALUE),
        hasPositiveNumericValue: checkMask(propConfig, Injection.HAS_POSITIVE_NUMERIC_VALUE),
        hasOverloadedBooleanValue: checkMask(propConfig, Injection.HAS_OVERLOADED_BOOLEAN_VALUE),
        hasStringBooleanValue: checkMask(propConfig, Injection.HAS_STRING_BOOLEAN_VALUE)
      };
      !(propertyInfo.hasBooleanValue + propertyInfo.hasNumericValue + propertyInfo.hasOverloadedBooleanValue <= 1) ? reactProdInvariant("50", propName) : void 0;

      if (DOMAttributeNames.hasOwnProperty(propName)) {
        var attributeName = DOMAttributeNames[propName];

        propertyInfo.attributeName = attributeName;
      }

      if (DOMAttributeNamespaces.hasOwnProperty(propName)) {
        propertyInfo.attributeNamespace = DOMAttributeNamespaces[propName];
      }

      if (DOMMutationMethods.hasOwnProperty(propName)) {
        propertyInfo.mutationMethod = DOMMutationMethods[propName];
      }

      // Downcase references to whitelist properties to check for membership
      // without case-sensitivity. This allows the whitelist to pick up
      // `allowfullscreen`, which should be written using the property configuration
      // for `allowFullscreen`
      properties[propName] = propertyInfo;
    }
  }
};

/* 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';

/**
 * Map from property "standard name" to an object with info about how to set
 * the property in the DOM. Each object contains:
 *
 * attributeName:
 *   Used when rendering markup or with `*Attribute()`.
 * attributeNamespace
 * propertyName:
 *   Used on DOM node instances. (This includes properties that mutate due to
 *   external factors.)
 * mutationMethod:
 *   If non-null, used instead of the property or `setAttribute()` after
 *   initial render.
 * mustUseProperty:
 *   Whether the property must be accessed and mutated as an object property.
 * hasBooleanValue:
 *   Whether the property should be removed when set to a falsey value.
 * hasNumericValue:
 *   Whether the property must be numeric or parse as a numeric and should be
 *   removed when set to a falsey value.
 * hasPositiveNumericValue:
 *   Whether the property must be positive numeric or parse as a positive
 *   numeric and should be removed when set to a falsey value.
 * hasOverloadedBooleanValue:
 *   Whether the property can be used as a flag as well as with a value.
 *   Removed when strictly equal to false; present without a value when
 *   strictly equal to true; present with a value otherwise.
 */
var properties = {};

/**
 * Checks whether a property name is a writeable attribute.
 * @method
 */
function shouldSetAttribute(name, value) {
  if (isReservedProp(name)) {
    return false;
  }
  if (name.length > 2 && (name[0] === 'o' || name[0] === 'O') && (name[1] === 'n' || name[1] === 'N')) {
    return false;
  }
  if (value === null) {
    return true;
  }
  switch (typeof value) {
    case 'boolean':
      return shouldAttributeAcceptBooleanValue(name);
    case 'undefined':
    case 'number':
    case 'string':
    case 'object':
      return true;
    default:
      // function, symbol
      return false;
  }
}

function getPropertyInfo(name) {
  return properties.hasOwnProperty(name) ? properties[name] : null;
}

function shouldAttributeAcceptBooleanValue(name) {
  if (isReservedProp(name)) {
    return true;
  }
  var propertyInfo = getPropertyInfo(name);
  if (propertyInfo) {
    return propertyInfo.hasBooleanValue || propertyInfo.hasStringBooleanValue || propertyInfo.hasOverloadedBooleanValue;
  }
  var prefix = name.toLowerCase().slice(0, 5);
  return prefix === 'data-' || prefix === 'aria-';
}

/**
 * Checks to see if a property name is within the list of properties
 * reserved for internal React operations. These properties should
 * not be set on an HTML element.
 *
 * @private
 * @param {string} name
 * @return {boolean} If the name is within reserved props
 */
function isReservedProp(name) {
  return RESERVED_PROPS.hasOwnProperty(name);
}

var injection = DOMPropertyInjection;

var MUST_USE_PROPERTY = injection.MUST_USE_PROPERTY;
var HAS_BOOLEAN_VALUE = injection.HAS_BOOLEAN_VALUE;
var HAS_NUMERIC_VALUE = injection.HAS_NUMERIC_VALUE;
var HAS_POSITIVE_NUMERIC_VALUE = injection.HAS_POSITIVE_NUMERIC_VALUE;
var HAS_OVERLOADED_BOOLEAN_VALUE = injection.HAS_OVERLOADED_BOOLEAN_VALUE;
var HAS_STRING_BOOLEAN_VALUE = injection.HAS_STRING_BOOLEAN_VALUE;

var HTMLDOMPropertyConfig = {
  // When adding attributes to this list, be sure to also add them to
  // the `possibleStandardNames` module to ensure casing and incorrect
  // name warnings.
  Properties: {
    allowFullScreen: HAS_BOOLEAN_VALUE,
    // specifies target context for links with `preload` type
    async: HAS_BOOLEAN_VALUE,
    // 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: HAS_BOOLEAN_VALUE,
    autoPlay: HAS_BOOLEAN_VALUE,
    capture: HAS_OVERLOADED_BOOLEAN_VALUE,
    checked: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
    cols: HAS_POSITIVE_NUMERIC_VALUE,
    contentEditable: HAS_STRING_BOOLEAN_VALUE,
    controls: HAS_BOOLEAN_VALUE,
    'default': HAS_BOOLEAN_VALUE,
    defer: HAS_BOOLEAN_VALUE,
    disabled: HAS_BOOLEAN_VALUE,
    download: HAS_OVERLOADED_BOOLEAN_VALUE,
    draggable: HAS_STRING_BOOLEAN_VALUE,
    formNoValidate: HAS_BOOLEAN_VALUE,
    hidden: HAS_BOOLEAN_VALUE,
    loop: HAS_BOOLEAN_VALUE,
    // Caution; `option.selected` is not updated if `select.multiple` is
    // disabled with `removeAttribute`.
    multiple: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
    muted: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
    noValidate: HAS_BOOLEAN_VALUE,
    open: HAS_BOOLEAN_VALUE,
    playsInline: HAS_BOOLEAN_VALUE,
    readOnly: HAS_BOOLEAN_VALUE,
    required: HAS_BOOLEAN_VALUE,
    reversed: HAS_BOOLEAN_VALUE,
    rows: HAS_POSITIVE_NUMERIC_VALUE,
    rowSpan: HAS_NUMERIC_VALUE,
    scoped: HAS_BOOLEAN_VALUE,
    seamless: HAS_BOOLEAN_VALUE,
    selected: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
    size: HAS_POSITIVE_NUMERIC_VALUE,
    start: HAS_NUMERIC_VALUE,
    // support for projecting regular DOM Elements via V1 named slots ( shadow dom )
    span: HAS_POSITIVE_NUMERIC_VALUE,
    spellCheck: HAS_STRING_BOOLEAN_VALUE,
    // Style must be explicitly set in the attribute list. React components
    // expect a style object
    style: 0,
    // Keep it in the whitelist because it is case-sensitive for SVG.
    tabIndex: 0,
    // itemScope is for for Microdata support.
    // See http://schema.org/docs/gs.html
    itemScope: HAS_BOOLEAN_VALUE,
    // These attributes must stay in the white-list because they have
    // different attribute names (see DOMAttributeNames below)
    acceptCharset: 0,
    className: 0,
    htmlFor: 0,
    httpEquiv: 0,
    // Attributes with mutation methods must be specified in the whitelist
    // Set the string boolean flag to allow the behavior
    value: HAS_STRING_BOOLEAN_VALUE
  },
  DOMAttributeNames: {
    acceptCharset: 'accept-charset',
    className: 'class',
    htmlFor: 'for',
    httpEquiv: 'http-equiv'
  },
  DOMMutationMethods: {
    value: function (node, value) {
      if (value == null) {
        return node.removeAttribute('value');
      }

      // Number inputs get special treatment due to some edge cases in
      // Chrome. Let everything else assign the value attribute as normal.
      // https://github.com/facebook/react/issues/7253#issuecomment-236074326
      if (node.type !== 'number' || node.hasAttribute('value') === false) {
        node.setAttribute('value', '' + value);
      } else if (node.validity && !node.validity.badInput && node.ownerDocument.activeElement !== node) {
        // Don't assign an attribute if validation reports bad
        // input. Chrome will clear the value. Additionally, don't
        // operate on inputs that have focus, otherwise Chrome might
        // strip off trailing decimal places and cause the user's
        // cursor position to jump to the beginning of the input.
        //
        // In ReactDOMInput, we have an onBlur event that will trigger
        // this function again when focus is lost.
        node.setAttribute('value', '' + value);
      }
    }
  }
};

var HAS_STRING_BOOLEAN_VALUE$1 = injection.HAS_STRING_BOOLEAN_VALUE;


var NS = {
  xlink: 'http://www.w3.org/1999/xlink',
  xml: 'http://www.w3.org/XML/1998/namespace'
};

/**
 * This is a list of all SVG attributes that need special casing,
 * namespacing, or boolean value assignment.
 *
 * When adding attributes to this list, be sure to also add them to
 * the `possibleStandardNames` module to ensure casing and incorrect
 * name warnings.
 *
 * SVG Attributes List:
 * https://www.w3.org/TR/SVG/attindex.html
 * SMIL Spec:
 * https://www.w3.org/TR/smil
 */
var ATTRS = ['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', 'x-height', 'xlink:actuate', 'xlink:arcrole', 'xlink:href', 'xlink:role', 'xlink:show', 'xlink:title', 'xlink:type', 'xml:base', 'xmlns:xlink', 'xml:lang', 'xml:space'];

var SVGDOMPropertyConfig = {
  Properties: {
    autoReverse: HAS_STRING_BOOLEAN_VALUE$1,
    externalResourcesRequired: HAS_STRING_BOOLEAN_VALUE$1,
    preserveAlpha: HAS_STRING_BOOLEAN_VALUE$1
  },
  DOMAttributeNames: {
    autoReverse: 'autoReverse',
    externalResourcesRequired: 'externalResourcesRequired',
    preserveAlpha: 'preserveAlpha'
  },
  DOMAttributeNamespaces: {
    xlinkActuate: NS.xlink,
    xlinkArcrole: NS.xlink,
    xlinkHref: NS.xlink,
    xlinkRole: NS.xlink,
    xlinkShow: NS.xlink,
    xlinkTitle: NS.xlink,
    xlinkType: NS.xlink,
    xmlBase: NS.xml,
    xmlLang: NS.xml,
    xmlSpace: NS.xml
  }
};

var CAMELIZE = /[\-\:]([a-z])/g;
var capitalize = function (token) {
  return token[1].toUpperCase();
};

ATTRS.forEach(function (original) {
  var reactName = original.replace(CAMELIZE, capitalize);

  SVGDOMPropertyConfig.Properties[reactName] = 0;
  SVGDOMPropertyConfig.DOMAttributeNames[reactName] = original;
});

injection.injectDOMPropertyConfig(HTMLDOMPropertyConfig);
injection.injectDOMPropertyConfig(SVGDOMPropertyConfig);

// TODO: this is special because it gets imported during build.

var ReactVersion = '16.2.0';

var ReactInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;

var _assign = ReactInternals.assign;

/**
 * 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.
 *
 */



var emptyObject = {};

var emptyObject_1 = emptyObject;

/**
 * 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.
 *
 * @typechecks
 */

var _uppercasePattern = /([A-Z])/g;

/**
 * Hyphenates a camelcased string, for example:
 *
 *   > hyphenate('backgroundColor')
 *   < "background-color"
 *
 * For CSS style names, use `hyphenateStyleName` instead which works properly
 * with all vendor prefixes, including `ms`.
 *
 * @param {string} string
 * @return {string}
 */
function hyphenate(string) {
  return string.replace(_uppercasePattern, '-$1').toLowerCase();
}

var hyphenate_1 = hyphenate;

/**
 * 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.
 *
 * @typechecks
 */





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-`.
 *
 * @param {string} string
 * @return {string}
 */
function hyphenateStyleName(string) {
  return hyphenate_1(string).replace(msPattern, '-ms-');
}

var hyphenateStyleName_1 = hyphenateStyleName;

/**
 * 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.
 *
 * 
 * @typechecks static-only
 */



/**
 * Memoizes the return value of a function that accepts one string argument.
 */

function memoizeStringOnly(callback) {
  var cache = {};
  return function (string) {
    if (!cache.hasOwnProperty(string)) {
      cache[string] = callback.call(this, string);
    }
    return cache[string];
  };
}

var memoizeStringOnly_1 = memoizeStringOnly;

/**
 * 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.
 *
 */

/**
 * 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 ReactInternals$1 = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;

var ReactCurrentOwner = ReactInternals$1.ReactCurrentOwner;

// 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_FRAGMENT_TYPE = hasSymbol ? Symbol['for']('react.fragment') : 0xeacb;

// 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;
  var html = '';
  var index = 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) + '"';
}

// isAttributeNameSafe() is currently duplicated in DOMPropertyOperations.
// TODO: Find a better place for this.
var VALID_ATTRIBUTE_NAME_REGEX = new RegExp('^[' + ATTRIBUTE_NAME_START_CHAR + '][' + ATTRIBUTE_NAME_CHAR + ']*$');
var illegalAttributeNameCache = {};
var validatedAttributeNameCache = {};
function isAttributeNameSafe(attributeName) {
  if (validatedAttributeNameCache.hasOwnProperty(attributeName)) {
    return true;
  }
  if (illegalAttributeNameCache.hasOwnProperty(attributeName)) {
    return false;
  }
  if (VALID_ATTRIBUTE_NAME_REGEX.test(attributeName)) {
    validatedAttributeNameCache[attributeName] = true;
    return true;
  }
  illegalAttributeNameCache[attributeName] = true;
  return false;
}

// shouldIgnoreValue() is currently duplicated in DOMPropertyOperations.
// TODO: Find a better place for this.
function shouldIgnoreValue(propertyInfo, value) {
  return value == null || propertyInfo.hasBooleanValue && !value || propertyInfo.hasNumericValue && isNaN(value) || propertyInfo.hasPositiveNumericValue && value < 1 || propertyInfo.hasOverloadedBooleanValue && value === false;
}

/**
 * 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 (propertyInfo) {
    if (shouldIgnoreValue(propertyInfo, value)) {
      return '';
    }
    var attributeName = propertyInfo.attributeName;
    if (propertyInfo.hasBooleanValue || propertyInfo.hasOverloadedBooleanValue && value === true) {
      return attributeName + '=""';
    } else if (typeof value !== 'boolean' || shouldAttributeAcceptBooleanValue(name)) {
      return attributeName + '=' + quoteAttributeValueForBrowser(value);
    }
  } else if (shouldSetAttribute(name, value)) {
    if (value == null) {
      return '';
    }
    return name + '=' + quoteAttributeValueForBrowser(value);
  }
  return null;
}

/**
 * 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);
}

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
};

// 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);

var HTML = '__html';

function assertValidProps(tag, props, getStack) {
  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, getStack()) : 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', getStack()) : 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,
  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();
}

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;
  }
}

/**
 * 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.
 *
 * @typechecks
 */

/**
 * 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 getStackAddendum = emptyFunction_1.thatReturns('');

var newlineEatingTags = {
  listing: true,
  pre: true,
  textarea: true
};

function getComponentName(type) {
  return typeof type === 'string' ? type : typeof type === 'function' ? type.displayName || type.name : null;
}

// 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 processStyleName = memoizeStringOnly_1(function (styleName) {
  return hyphenateStyleName_1(styleName);
});

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) {
  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;
    }
    if (typeof child === 'string' || typeof child === 'number') {
      content += child;
    } else {
      
    }
  });
  return content;
}

function maskContext(type, context) {
  var contextTypes = type.contextTypes;
  if (!contextTypes) {
    return emptyObject_1;
  }
  var maskedContext = {};
  for (var contextName in contextTypes) {
    maskedContext[contextName] = context[contextName];
  }
  return maskedContext;
}

function processContext(type, context) {
  var maskedContext = maskContext(type, context);
  return maskedContext;
}

var STYLE = 'style';
var RESERVED_PROPS$1 = {
  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 (!props.hasOwnProperty(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$1.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) {
  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;
    }
    var publicContext = processContext(Component, context);
    var inst;
    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, partialState) {
        if (queue === null) {
          return null;
        }
        queue.push(partialState);
      }
    };

    if (shouldConstruct(Component)) {
      inst = new Component(element.props, publicContext, updater);
    } else {
      inst = Component(element.props, publicContext, updater);
      if (inst == null || inst.render == null) {
        child = inst;
        validateRenderResult(child, Component);
        continue;
      }
    }

    inst.props = element.props;
    inst.context = publicContext;
    inst.updater = updater;

    var initialState = inst.state;
    if (initialState === undefined) {
      inst.state = initialState = null;
    }
    if (inst.componentWillMount) {
      inst.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) {
              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;
    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 () {
  function ReactDOMServerRenderer(children, makeStaticMarkup) {
    _classCallCheck(this, ReactDOMServerRenderer);

    var flatChildren = flattenTopLevelChildren(children);

    var topFrame = {
      // 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_1,
      footer: ''
    };
    this.stack = [topFrame];
    this.exhausted = false;
    this.currentSelectValue = null;
    this.previousWasTextNode = false;
    this.makeStaticMarkup = makeStaticMarkup;
  }
  // TODO: type this more strictly:


  ReactDOMServerRenderer.prototype.read = function read(bytes) {
    if (this.exhausted) {
      return null;
    }

    var out = '';
    while (out.length < bytes) {
      if (this.stack.length === 0) {
        this.exhausted = true;
        break;
      }
      var frame = this.stack[this.stack.length - 1];
      if (frame.childIndex >= frame.children.length) {
        var footer = frame.footer;
        out += footer;
        if (footer !== '') {
          this.previousWasTextNode = false;
        }
        this.stack.pop();
        if (frame.tag === 'select') {
          this.currentSelectValue = null;
        }
        continue;
      }
      var child = frame.children[frame.childIndex++];
      out += this.render(child, frame.context, frame.domNamespace);
      
    }
    return out;
  };

  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;

      var _resolve = resolve(child, context);

      nextChild = _resolve.child;
      context = _resolve.context;

      if (nextChild === null || nextChild === false) {
        return '';
      } else if (!React.isValidElement(nextChild)) {
        var nextChildren = toArray(nextChild);
        var frame = {
          domNamespace: parentNamespace,
          children: nextChildren,
          childIndex: 0,
          context: context,
          footer: ''
        };
        this.stack.push(frame);
        return '';
      } else if (nextChild.type === REACT_FRAGMENT_TYPE) {
        var _nextChildren = toArray(nextChild.props.children);
        var _frame = {
          domNamespace: parentNamespace,
          children: _nextChildren,
          childIndex: 0,
          context: context,
          footer: ''
        };
        this.stack.push(_frame);
        return '';
      } else {
        // Safe because we just checked it's an element.
        var nextElement = nextChild;
        return this.renderDOM(nextElement, context, parentNamespace);
      }
    }
  };

  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;
        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, getStackAddendum);

    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;
    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),
      tag: 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);
  var markup = renderer.read(Infinity);
  return markup;
}

/**
 * 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);
  var markup = renderer.read(Infinity);
  return markup;
}

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 = Object.freeze({
	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['default'] : ReactDOMServer;

return server_browser;

})));
PK
!<>������Mchrome/devtools/modules/devtools/client/shared/vendor/react-dom-test-utils.js/** @license React v16.2.0
 * react-dom-test-utils.production.min.js
 *
 * 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 (global, factory) {
	typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('devtools/client/shared/vendor/react'), require('devtools/client/shared/vendor/react-dom')) :
	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;

/**
 * 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 message = 'Minified React error #' + code + '; visit ' + 'http://facebook.github.io/react/docs/error-decoder.html?invariant=' + code;

  for (var argIdx = 0; argIdx < argCount; argIdx++) {
    message += '&args[]=' + encodeURIComponent(arguments[argIdx + 1]);
  }

  message += ' for the full message or use the non-minified dev environment' + ' for full errors and additional helpful warnings.';

  var error = new Error(message);
  error.name = 'Invariant Violation';
  error.framesToPop = 1; // we don't care about reactProdInvariant's own frame

  throw error;
}

/**
 * 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.
 *
 */

/**
 * 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) 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.
 *
 */

/**
 * `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 ReactInternals$1 = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;

var ReactCurrentOwner = ReactInternals$1.ReactCurrentOwner;

// Before we know whether it is functional or class
var FunctionalComponent = 1;
var ClassComponent = 2;
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:
var NoEffect = 0; //           0b00000000
 //      0b00000001

// You can change the rest (and add more).
var Placement = 2; //          0b00000010
 //             0b00000100
 // 0b00000110
 //           0b00001000
 //      0b00010000
 //          0b00100000
 //               0b01000000
 //              0b10000000

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;

var shouldBeReleasedProperties = ['dispatchConfig', '_targetInst', 'nativeEvent', 'isDefaultPrevented', 'isPropagationStopped', '_dispatchListeners', '_dispatchInstances'];

/**
 * @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: emptyFunction_1.thatReturnsNull,
  eventPhase: null,
  bubbles: null,
  cancelable: null,
  timeStamp: function (event) {
    return event.timeStamp || Date.now();
  },
  defaultPrevented: null,
  isTrusted: null
};

/**
 * 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 = emptyFunction_1.thatReturnsTrue;
  } else {
    this.isDefaultPrevented = emptyFunction_1.thatReturnsFalse;
  }
  this.isPropagationStopped = emptyFunction_1.thatReturnsFalse;
  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 = emptyFunction_1.thatReturnsTrue;
  },

  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 = emptyFunction_1.thatReturnsTrue;
  },

  /**
   * 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 = emptyFunction_1.thatReturnsTrue;
  },

  /**
   * Checks if this event should be released back into the pool.
   *
   * @return {boolean} True if this should not be released, false otherwise.
   */
  isPersistent: emptyFunction_1.thatReturnsFalse,

  /**
   * `PooledClass` looks for `destructor` on each instance it releases.
   */
  destructor: function () {
    var Interface = this.constructor.Interface;
    for (var propName in Interface) {
      {
        this[propName] = null;
      }
    }
    for (var i = 0; i < shouldBeReleasedProperties.length; i++) {
      this[shouldBeReleasedProperties[i]] = null;
    }
    
  }
});

SyntheticEvent.Interface = EventInterface;

/**
 * Helper to reduce boilerplate when creating subclasses.
 *
 * @param {function} Class
 * @param {?object} Interface
 */
SyntheticEvent.augmentClass = function (Class, Interface) {
  var Super = this;

  var E = function () {};
  E.prototype = Super.prototype;
  var prototype = new E();

  _assign(prototype, Class.prototype);
  Class.prototype = prototype;
  Class.prototype.constructor = Class;

  Class.Interface = _assign({}, Super.Interface, Interface);
  Class.augmentClass = Super.augmentClass;
  addEventPoolingTo(Class);
};

/** Proxying after everything set on SyntheticEvent
 * to resolve Proxy issue on some WebKit browsers
 * in which some Event properties are set to undefined (GH#10010)
 */
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('223') : 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;
}

/**
 * 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 canUseDOM = !!(typeof window !== 'undefined' && window.document && window.document.createElement);

/**
 * Simple, lightweight module assisting with the detection and context of
 * Worker. Helps avoid circular dependencies and allows code to reason about
 * whether or not they are in a Worker, even if they never include the main
 * `ReactWorker` dependency.
 */
var ExecutionEnvironment = {

  canUseDOM: canUseDOM,

  canUseWorkers: typeof Worker !== 'undefined',

  canUseEventListeners: canUseDOM && !!(window.addEventListener || window.attachEvent),

  canUseViewport: canUseDOM && !!window.screen,

  isInWorker: !canUseDOM // For now, this is true - might change in the future.

};

var ExecutionEnvironment_1 = ExecutionEnvironment;

/**
 * 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;
  prefixes['ms' + styleProp] = 'MS' + eventName;
  prefixes['O' + styleProp] = 'o' + eventName.toLowerCase();

  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 (ExecutionEnvironment_1.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 '';
}

/**
 * Types of raw signals from the browser caught at the top level.
 *
 * For events like 'submit' which don't consistently bubble (which we
 * trap at a lower node than `document`), binding at `document` would
 * cause duplicate events so we don't include them here.
 */
var topLevelTypes$1 = {
  topAbort: 'abort',
  topAnimationEnd: getVendorPrefixedEventName('animationend') || 'animationend',
  topAnimationIteration: getVendorPrefixedEventName('animationiteration') || 'animationiteration',
  topAnimationStart: getVendorPrefixedEventName('animationstart') || 'animationstart',
  topBlur: 'blur',
  topCancel: 'cancel',
  topCanPlay: 'canplay',
  topCanPlayThrough: 'canplaythrough',
  topChange: 'change',
  topClick: 'click',
  topClose: 'close',
  topCompositionEnd: 'compositionend',
  topCompositionStart: 'compositionstart',
  topCompositionUpdate: 'compositionupdate',
  topContextMenu: 'contextmenu',
  topCopy: 'copy',
  topCut: 'cut',
  topDoubleClick: 'dblclick',
  topDrag: 'drag',
  topDragEnd: 'dragend',
  topDragEnter: 'dragenter',
  topDragExit: 'dragexit',
  topDragLeave: 'dragleave',
  topDragOver: 'dragover',
  topDragStart: 'dragstart',
  topDrop: 'drop',
  topDurationChange: 'durationchange',
  topEmptied: 'emptied',
  topEncrypted: 'encrypted',
  topEnded: 'ended',
  topError: 'error',
  topFocus: 'focus',
  topInput: 'input',
  topKeyDown: 'keydown',
  topKeyPress: 'keypress',
  topKeyUp: 'keyup',
  topLoadedData: 'loadeddata',
  topLoad: 'load',
  topLoadedMetadata: 'loadedmetadata',
  topLoadStart: 'loadstart',
  topMouseDown: 'mousedown',
  topMouseMove: 'mousemove',
  topMouseOut: 'mouseout',
  topMouseOver: 'mouseover',
  topMouseUp: 'mouseup',
  topPaste: 'paste',
  topPause: 'pause',
  topPlay: 'play',
  topPlaying: 'playing',
  topProgress: 'progress',
  topRateChange: 'ratechange',
  topScroll: 'scroll',
  topSeeked: 'seeked',
  topSeeking: 'seeking',
  topSelectionChange: 'selectionchange',
  topStalled: 'stalled',
  topSuspend: 'suspend',
  topTextInput: 'textInput',
  topTimeUpdate: 'timeupdate',
  topToggle: 'toggle',
  topTouchCancel: 'touchcancel',
  topTouchEnd: 'touchend',
  topTouchMove: 'touchmove',
  topTouchStart: 'touchstart',
  topTransitionEnd: getVendorPrefixedEventName('transitionend') || 'transitionend',
  topVolumeChange: 'volumechange',
  topWaiting: 'waiting',
  topWheel: 'wheel'
};

var BrowserEventConstants = {
  topLevelTypes: topLevelTypes$1
};

var findDOMNode = ReactDOM.findDOMNode;
var _ReactDOM$__SECRET_IN = ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
var EventPluginHub = _ReactDOM$__SECRET_IN.EventPluginHub;
var EventPluginRegistry = _ReactDOM$__SECRET_IN.EventPluginRegistry;
var EventPropagators = _ReactDOM$__SECRET_IN.EventPropagators;
var ReactControlledComponent = _ReactDOM$__SECRET_IN.ReactControlledComponent;
var ReactDOMComponentTree = _ReactDOM$__SECRET_IN.ReactDOMComponentTree;
var ReactDOMEventListener = _ReactDOM$__SECRET_IN.ReactDOMEventListener;


var topLevelTypes = BrowserEventConstants.topLevelTypes;

function Event(suffix) {}

/**
 * @class ReactTestUtils
 */

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 === FunctionalComponent) {
      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;
  }
}

/**
 * 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 === 1 && 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) {
    if (!inst) {
      return [];
    }
    !ReactTestUtils.isCompositeComponent(inst) ? reactProdInvariant('10') : void 0;
    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) {
    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) {
    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) {
    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) {
    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) {
    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) {
    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;
  },

  /**
   * Simulates a top level event being dispatched from a raw event that occurred
   * on an `Element` node.
   * @param {Object} topLevelType A type from `BrowserEventConstants.topLevelTypes`
   * @param {!Element} node The dom to simulate an event occurring on.
   * @param {?Event} fakeNativeEvent Fake native event to use in SyntheticEvent.
   */
  simulateNativeEventOnNode: function (topLevelType, node, fakeNativeEvent) {
    fakeNativeEvent.target = node;
    ReactDOMEventListener.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.
   */
  simulateNativeEventOnDOMComponent: function (topLevelType, comp, fakeNativeEvent) {
    ReactTestUtils.simulateNativeEventOnNode(topLevelType, findDOMNode(comp), fakeNativeEvent);
  },

  nativeTouchData: function (x, y) {
    return {
      touches: [{ pageX: x, pageY: y }]
    };
  },

  Simulate: null,
  SimulateNative: {}
};

/**
 * 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 = EventPluginRegistry.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 = ReactDOMComponentTree.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) {
      EventPropagators.accumulateTwoPhaseDispatches(event);
    } else {
      EventPropagators.accumulateDirectDispatches(event);
    }

    ReactDOM.unstable_batchedUpdates(function () {
      // Normally extractEvent enqueues a state restore, but we'll just always
      // do that since we we're by-passing it here.
      ReactControlledComponent.enqueueStateRestore(domNode);

      EventPluginHub.enqueueEvents(event);
      EventPluginHub.processEventQueue(true);
    });
  };
}

function buildSimulators() {
  ReactTestUtils.Simulate = {};

  var eventType;
  for (eventType in EventPluginRegistry.eventNameDispatchConfigs) {
    /**
     * @param {!Element|ReactDOMComponent} domComponentOrNode
     * @param {?object} eventData Fake event data to use in SyntheticEvent.
     */
    ReactTestUtils.Simulate[eventType] = makeSimulator(eventType);
  }
}

// Rebuild ReactTestUtils.Simulate whenever event plugins are injected
var oldInjectEventPluginOrder = EventPluginHub.injection.injectEventPluginOrder;
EventPluginHub.injection.injectEventPluginOrder = function () {
  oldInjectEventPluginOrder.apply(this, arguments);
  buildSimulators();
};
var oldInjectEventPlugins = EventPluginHub.injection.injectEventPluginsByName;
EventPluginHub.injection.injectEventPluginsByName = function () {
  oldInjectEventPlugins.apply(this, arguments);
  buildSimulators();
};

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) {
  return function (domComponentOrNode, nativeEventData) {
    var fakeNativeEvent = new Event(eventType);
    _assign(fakeNativeEvent, nativeEventData);
    if (ReactTestUtils.isDOMComponent(domComponentOrNode)) {
      ReactTestUtils.simulateNativeEventOnDOMComponent(eventType, domComponentOrNode, fakeNativeEvent);
    } else if (domComponentOrNode.tagName) {
      // Will allow on actual dom nodes.
      ReactTestUtils.simulateNativeEventOnNode(eventType, domComponentOrNode, fakeNativeEvent);
    }
  };
}

Object.keys(topLevelTypes).forEach(function (eventType) {
  // Event type is stored as 'topClick' - we transform that to 'click'
  var convenienceName = eventType.indexOf('top') === 0 ? eventType.charAt(3).toLowerCase() + eventType.substr(4) : eventType;
  /**
   * @param {!Element|ReactDOMComponent} domComponentOrNode
   * @param {?Event} nativeEventData Fake native event to use in SyntheticEvent.
   */
  ReactTestUtils.SimulateNative[convenienceName] = makeNativeSimulator(eventType);
});



var ReactTestUtils$2 = Object.freeze({
	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['default'] : ReactTestUtils$3;

return testUtils;

})));
PK
!<j�����Bchrome/devtools/modules/devtools/client/shared/vendor/react-dom.js/** @license React v16.2.0
 * react-dom.production.min.js
 *
 * 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 (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';

/**
 * 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 message = 'Minified React error #' + code + '; visit ' + 'http://facebook.github.io/react/docs/error-decoder.html?invariant=' + code;

  for (var argIdx = 0; argIdx < argCount; argIdx++) {
    message += '&args[]=' + encodeURIComponent(arguments[argIdx + 1]);
  }

  message += ' for the full message or use the non-minified dev environment' + ' for full errors and additional helpful warnings.';

  var error = new Error(message);
  error.name = 'Invariant Violation';
  error.framesToPop = 1; // we don't care about reactProdInvariant's own frame

  throw error;
}

/**
 * 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.
 *
 */

!React ? reactProdInvariant('227') : void 0;

// These attributes should be all lowercase to allow for
// case insensitive checks
var RESERVED_PROPS = {
  children: true,
  dangerouslySetInnerHTML: true,
  defaultValue: true,
  defaultChecked: true,
  innerHTML: true,
  suppressContentEditableWarning: true,
  suppressHydrationWarning: true,
  style: true
};

function checkMask(value, bitmask) {
  return (value & bitmask) === bitmask;
}

var DOMPropertyInjection = {
  /**
   * Mapping from normalized, camelcased property names to a configuration that
   * specifies how the associated DOM property should be accessed or rendered.
   */
  MUST_USE_PROPERTY: 0x1,
  HAS_BOOLEAN_VALUE: 0x4,
  HAS_NUMERIC_VALUE: 0x8,
  HAS_POSITIVE_NUMERIC_VALUE: 0x10 | 0x8,
  HAS_OVERLOADED_BOOLEAN_VALUE: 0x20,
  HAS_STRING_BOOLEAN_VALUE: 0x40,

  /**
   * Inject some specialized knowledge about the DOM. This takes a config object
   * with the following properties:
   *
   * Properties: object mapping DOM property name to one of the
   * DOMPropertyInjection constants or null. If your attribute isn't in here,
   * it won't get written to the DOM.
   *
   * DOMAttributeNames: object mapping React attribute name to the DOM
   * attribute name. Attribute names not specified use the **lowercase**
   * normalized name.
   *
   * DOMAttributeNamespaces: object mapping React attribute name to the DOM
   * attribute namespace URL. (Attribute names not specified use no namespace.)
   *
   * DOMPropertyNames: similar to DOMAttributeNames but for DOM properties.
   * Property names not specified use the normalized name.
   *
   * DOMMutationMethods: Properties that require special mutation methods. If
   * `value` is undefined, the mutation method should unset the property.
   *
   * @param {object} domPropertyConfig the config as described above.
   */
  injectDOMPropertyConfig: function (domPropertyConfig) {
    var Injection = DOMPropertyInjection;
    var Properties = domPropertyConfig.Properties || {};
    var DOMAttributeNamespaces = domPropertyConfig.DOMAttributeNamespaces || {};
    var DOMAttributeNames = domPropertyConfig.DOMAttributeNames || {};
    var DOMMutationMethods = domPropertyConfig.DOMMutationMethods || {};

    for (var propName in Properties) {
      !!properties.hasOwnProperty(propName) ? reactProdInvariant("48", propName) : void 0;

      var lowerCased = propName.toLowerCase();
      var propConfig = Properties[propName];

      var propertyInfo = {
        attributeName: lowerCased,
        attributeNamespace: null,
        propertyName: propName,
        mutationMethod: null,

        mustUseProperty: checkMask(propConfig, Injection.MUST_USE_PROPERTY),
        hasBooleanValue: checkMask(propConfig, Injection.HAS_BOOLEAN_VALUE),
        hasNumericValue: checkMask(propConfig, Injection.HAS_NUMERIC_VALUE),
        hasPositiveNumericValue: checkMask(propConfig, Injection.HAS_POSITIVE_NUMERIC_VALUE),
        hasOverloadedBooleanValue: checkMask(propConfig, Injection.HAS_OVERLOADED_BOOLEAN_VALUE),
        hasStringBooleanValue: checkMask(propConfig, Injection.HAS_STRING_BOOLEAN_VALUE)
      };
      !(propertyInfo.hasBooleanValue + propertyInfo.hasNumericValue + propertyInfo.hasOverloadedBooleanValue <= 1) ? reactProdInvariant("50", propName) : void 0;

      if (DOMAttributeNames.hasOwnProperty(propName)) {
        var attributeName = DOMAttributeNames[propName];

        propertyInfo.attributeName = attributeName;
      }

      if (DOMAttributeNamespaces.hasOwnProperty(propName)) {
        propertyInfo.attributeNamespace = DOMAttributeNamespaces[propName];
      }

      if (DOMMutationMethods.hasOwnProperty(propName)) {
        propertyInfo.mutationMethod = DOMMutationMethods[propName];
      }

      // Downcase references to whitelist properties to check for membership
      // without case-sensitivity. This allows the whitelist to pick up
      // `allowfullscreen`, which should be written using the property configuration
      // for `allowFullscreen`
      properties[propName] = propertyInfo;
    }
  }
};

/* 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';

/**
 * Map from property "standard name" to an object with info about how to set
 * the property in the DOM. Each object contains:
 *
 * attributeName:
 *   Used when rendering markup or with `*Attribute()`.
 * attributeNamespace
 * propertyName:
 *   Used on DOM node instances. (This includes properties that mutate due to
 *   external factors.)
 * mutationMethod:
 *   If non-null, used instead of the property or `setAttribute()` after
 *   initial render.
 * mustUseProperty:
 *   Whether the property must be accessed and mutated as an object property.
 * hasBooleanValue:
 *   Whether the property should be removed when set to a falsey value.
 * hasNumericValue:
 *   Whether the property must be numeric or parse as a numeric and should be
 *   removed when set to a falsey value.
 * hasPositiveNumericValue:
 *   Whether the property must be positive numeric or parse as a positive
 *   numeric and should be removed when set to a falsey value.
 * hasOverloadedBooleanValue:
 *   Whether the property can be used as a flag as well as with a value.
 *   Removed when strictly equal to false; present without a value when
 *   strictly equal to true; present with a value otherwise.
 */
var properties = {};

/**
 * Checks whether a property name is a writeable attribute.
 * @method
 */
function shouldSetAttribute(name, value) {
  if (isReservedProp(name)) {
    return false;
  }
  if (name.length > 2 && (name[0] === 'o' || name[0] === 'O') && (name[1] === 'n' || name[1] === 'N')) {
    return false;
  }
  if (value === null) {
    return true;
  }
  switch (typeof value) {
    case 'boolean':
      return shouldAttributeAcceptBooleanValue(name);
    case 'undefined':
    case 'number':
    case 'string':
    case 'object':
      return true;
    default:
      // function, symbol
      return false;
  }
}

function getPropertyInfo(name) {
  return properties.hasOwnProperty(name) ? properties[name] : null;
}

function shouldAttributeAcceptBooleanValue(name) {
  if (isReservedProp(name)) {
    return true;
  }
  var propertyInfo = getPropertyInfo(name);
  if (propertyInfo) {
    return propertyInfo.hasBooleanValue || propertyInfo.hasStringBooleanValue || propertyInfo.hasOverloadedBooleanValue;
  }
  var prefix = name.toLowerCase().slice(0, 5);
  return prefix === 'data-' || prefix === 'aria-';
}

/**
 * Checks to see if a property name is within the list of properties
 * reserved for internal React operations. These properties should
 * not be set on an HTML element.
 *
 * @private
 * @param {string} name
 * @return {boolean} If the name is within reserved props
 */
function isReservedProp(name) {
  return RESERVED_PROPS.hasOwnProperty(name);
}

var injection = DOMPropertyInjection;

var MUST_USE_PROPERTY = injection.MUST_USE_PROPERTY;
var HAS_BOOLEAN_VALUE = injection.HAS_BOOLEAN_VALUE;
var HAS_NUMERIC_VALUE = injection.HAS_NUMERIC_VALUE;
var HAS_POSITIVE_NUMERIC_VALUE = injection.HAS_POSITIVE_NUMERIC_VALUE;
var HAS_OVERLOADED_BOOLEAN_VALUE = injection.HAS_OVERLOADED_BOOLEAN_VALUE;
var HAS_STRING_BOOLEAN_VALUE = injection.HAS_STRING_BOOLEAN_VALUE;

var HTMLDOMPropertyConfig = {
  // When adding attributes to this list, be sure to also add them to
  // the `possibleStandardNames` module to ensure casing and incorrect
  // name warnings.
  Properties: {
    allowFullScreen: HAS_BOOLEAN_VALUE,
    // specifies target context for links with `preload` type
    async: HAS_BOOLEAN_VALUE,
    // 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: HAS_BOOLEAN_VALUE,
    autoPlay: HAS_BOOLEAN_VALUE,
    capture: HAS_OVERLOADED_BOOLEAN_VALUE,
    checked: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
    cols: HAS_POSITIVE_NUMERIC_VALUE,
    contentEditable: HAS_STRING_BOOLEAN_VALUE,
    controls: HAS_BOOLEAN_VALUE,
    'default': HAS_BOOLEAN_VALUE,
    defer: HAS_BOOLEAN_VALUE,
    disabled: HAS_BOOLEAN_VALUE,
    download: HAS_OVERLOADED_BOOLEAN_VALUE,
    draggable: HAS_STRING_BOOLEAN_VALUE,
    formNoValidate: HAS_BOOLEAN_VALUE,
    hidden: HAS_BOOLEAN_VALUE,
    loop: HAS_BOOLEAN_VALUE,
    // Caution; `option.selected` is not updated if `select.multiple` is
    // disabled with `removeAttribute`.
    multiple: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
    muted: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
    noValidate: HAS_BOOLEAN_VALUE,
    open: HAS_BOOLEAN_VALUE,
    playsInline: HAS_BOOLEAN_VALUE,
    readOnly: HAS_BOOLEAN_VALUE,
    required: HAS_BOOLEAN_VALUE,
    reversed: HAS_BOOLEAN_VALUE,
    rows: HAS_POSITIVE_NUMERIC_VALUE,
    rowSpan: HAS_NUMERIC_VALUE,
    scoped: HAS_BOOLEAN_VALUE,
    seamless: HAS_BOOLEAN_VALUE,
    selected: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
    size: HAS_POSITIVE_NUMERIC_VALUE,
    start: HAS_NUMERIC_VALUE,
    // support for projecting regular DOM Elements via V1 named slots ( shadow dom )
    span: HAS_POSITIVE_NUMERIC_VALUE,
    spellCheck: HAS_STRING_BOOLEAN_VALUE,
    // Style must be explicitly set in the attribute list. React components
    // expect a style object
    style: 0,
    // Keep it in the whitelist because it is case-sensitive for SVG.
    tabIndex: 0,
    // itemScope is for for Microdata support.
    // See http://schema.org/docs/gs.html
    itemScope: HAS_BOOLEAN_VALUE,
    // These attributes must stay in the white-list because they have
    // different attribute names (see DOMAttributeNames below)
    acceptCharset: 0,
    className: 0,
    htmlFor: 0,
    httpEquiv: 0,
    // Attributes with mutation methods must be specified in the whitelist
    // Set the string boolean flag to allow the behavior
    value: HAS_STRING_BOOLEAN_VALUE
  },
  DOMAttributeNames: {
    acceptCharset: 'accept-charset',
    className: 'class',
    htmlFor: 'for',
    httpEquiv: 'http-equiv'
  },
  DOMMutationMethods: {
    value: function (node, value) {
      if (value == null) {
        return node.removeAttribute('value');
      }

      // Number inputs get special treatment due to some edge cases in
      // Chrome. Let everything else assign the value attribute as normal.
      // https://github.com/facebook/react/issues/7253#issuecomment-236074326
      if (node.type !== 'number' || node.hasAttribute('value') === false) {
        node.setAttribute('value', '' + value);
      } else if (node.validity && !node.validity.badInput && node.ownerDocument.activeElement !== node) {
        // Don't assign an attribute if validation reports bad
        // input. Chrome will clear the value. Additionally, don't
        // operate on inputs that have focus, otherwise Chrome might
        // strip off trailing decimal places and cause the user's
        // cursor position to jump to the beginning of the input.
        //
        // In ReactDOMInput, we have an onBlur event that will trigger
        // this function again when focus is lost.
        node.setAttribute('value', '' + value);
      }
    }
  }
};

var HAS_STRING_BOOLEAN_VALUE$1 = injection.HAS_STRING_BOOLEAN_VALUE;


var NS = {
  xlink: 'http://www.w3.org/1999/xlink',
  xml: 'http://www.w3.org/XML/1998/namespace'
};

/**
 * This is a list of all SVG attributes that need special casing,
 * namespacing, or boolean value assignment.
 *
 * When adding attributes to this list, be sure to also add them to
 * the `possibleStandardNames` module to ensure casing and incorrect
 * name warnings.
 *
 * SVG Attributes List:
 * https://www.w3.org/TR/SVG/attindex.html
 * SMIL Spec:
 * https://www.w3.org/TR/smil
 */
var ATTRS = ['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', 'x-height', 'xlink:actuate', 'xlink:arcrole', 'xlink:href', 'xlink:role', 'xlink:show', 'xlink:title', 'xlink:type', 'xml:base', 'xmlns:xlink', 'xml:lang', 'xml:space'];

var SVGDOMPropertyConfig = {
  Properties: {
    autoReverse: HAS_STRING_BOOLEAN_VALUE$1,
    externalResourcesRequired: HAS_STRING_BOOLEAN_VALUE$1,
    preserveAlpha: HAS_STRING_BOOLEAN_VALUE$1
  },
  DOMAttributeNames: {
    autoReverse: 'autoReverse',
    externalResourcesRequired: 'externalResourcesRequired',
    preserveAlpha: 'preserveAlpha'
  },
  DOMAttributeNamespaces: {
    xlinkActuate: NS.xlink,
    xlinkArcrole: NS.xlink,
    xlinkHref: NS.xlink,
    xlinkRole: NS.xlink,
    xlinkShow: NS.xlink,
    xlinkTitle: NS.xlink,
    xlinkType: NS.xlink,
    xmlBase: NS.xml,
    xmlLang: NS.xml,
    xmlSpace: NS.xml
  }
};

var CAMELIZE = /[\-\:]([a-z])/g;
var capitalize = function (token) {
  return token[1].toUpperCase();
};

ATTRS.forEach(function (original) {
  var reactName = original.replace(CAMELIZE, capitalize);

  SVGDOMPropertyConfig.Properties[reactName] = 0;
  SVGDOMPropertyConfig.DOMAttributeNames[reactName] = original;
});

injection.injectDOMPropertyConfig(HTMLDOMPropertyConfig);
injection.injectDOMPropertyConfig(SVGDOMPropertyConfig);

var ReactErrorUtils = {
  // Used by Fiber to simulate a try-catch.
  _caughtError: null,
  _hasCaughtError: false,

  // Used by event system to capture/rethrow the first error.
  _rethrowError: null,
  _hasRethrowError: false,

  injection: {
    injectErrorUtils: function (injectedErrorUtils) {
      !(typeof injectedErrorUtils.invokeGuardedCallback === 'function') ? reactProdInvariant('197') : void 0;
      invokeGuardedCallback = injectedErrorUtils.invokeGuardedCallback;
    }
  },

  /**
   * 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
   */
  invokeGuardedCallback: function (name, func, context, a, b, c, d, e, f) {
    invokeGuardedCallback.apply(ReactErrorUtils, 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
   */
  invokeGuardedCallbackAndCatchFirstError: function (name, func, context, a, b, c, d, e, f) {
    ReactErrorUtils.invokeGuardedCallback.apply(this, arguments);
    if (ReactErrorUtils.hasCaughtError()) {
      var error = ReactErrorUtils.clearCaughtError();
      if (!ReactErrorUtils._hasRethrowError) {
        ReactErrorUtils._hasRethrowError = true;
        ReactErrorUtils._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.
   */
  rethrowCaughtError: function () {
    return rethrowCaughtError.apply(ReactErrorUtils, arguments);
  },

  hasCaughtError: function () {
    return ReactErrorUtils._hasCaughtError;
  },

  clearCaughtError: function () {
    if (ReactErrorUtils._hasCaughtError) {
      var error = ReactErrorUtils._caughtError;
      ReactErrorUtils._caughtError = null;
      ReactErrorUtils._hasCaughtError = false;
      return error;
    } else {
      reactProdInvariant('198');
    }
  }
};

var invokeGuardedCallback = function (name, func, context, a, b, c, d, e, f) {
  ReactErrorUtils._hasCaughtError = false;
  ReactErrorUtils._caughtError = null;
  var funcArgs = Array.prototype.slice.call(arguments, 3);
  try {
    func.apply(context, funcArgs);
  } catch (error) {
    ReactErrorUtils._caughtError = error;
    ReactErrorUtils._hasCaughtError = true;
  }
};

var rethrowCaughtError = function () {
  if (ReactErrorUtils._hasRethrowError) {
    var error = ReactErrorUtils._rethrowError;
    ReactErrorUtils._rethrowError = null;
    ReactErrorUtils._hasRethrowError = false;
    throw error;
  }
};

/**
 * 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}
 */
var possibleRegistrationNames = null;
// 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();
  }
}

var EventPluginRegistry = Object.freeze({
	plugins: plugins,
	eventNameDispatchConfigs: eventNameDispatchConfigs,
	registrationNameModules: registrationNameModules,
	registrationNameDependencies: registrationNameDependencies,
	possibleRegistrationNames: possibleRegistrationNames,
	injectEventPluginOrder: injectEventPluginOrder,
	injectEventPluginsByName: injectEventPluginsByName
});

/**
 * 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) 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.
 *
 */

var getFiberCurrentPropsFromNode = null;
var getInstanceFromNode = null;
var getNodeFromInstance = null;

var injection$2 = {
  injectComponentTree: function (Injected) {
    getFiberCurrentPropsFromNode = Injected.getFiberCurrentPropsFromNode;
    getInstanceFromNode = Injected.getInstanceFromNode;
    getNodeFromInstance = Injected.getNodeFromInstance;

    
  }
};






/**
 * Dispatch the event to the listener.
 * @param {SyntheticEvent} event SyntheticEvent to handle
 * @param {boolean} simulated If the event is simulated (changes exn behavior)
 * @param {function} listener Application-level callback
 * @param {*} inst Internal component instance
 */
function executeDispatch(event, simulated, listener, inst) {
  var type = event.type || 'unknown-event';
  event.currentTarget = getNodeFromInstance(inst);
  ReactErrorUtils.invokeGuardedCallbackAndCatchFirstError(type, listener, undefined, event);
  event.currentTarget = null;
}

/**
 * Standard/simple iteration through an event's collected dispatches.
 */
function executeDispatchesInOrder(event, simulated) {
  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, simulated, dispatchListeners[i], dispatchInstances[i]);
    }
  } else if (dispatchListeners) {
    executeDispatch(event, simulated, 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.
 * @param {boolean} simulated If the event is simulated (changes exn behavior)
 * @private
 */
var executeDispatchesAndRelease = function (event, simulated) {
  if (event) {
    executeDispatchesInOrder(event, simulated);

    if (!event.isPersistent()) {
      event.constructor.release(event);
    }
  }
};
var executeDispatchesAndReleaseSimulated = function (e) {
  return executeDispatchesAndRelease(e, true);
};
var executeDispatchesAndReleaseTopLevel = function (e) {
  return executeDispatchesAndRelease(e, false);
};

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$1 = {
  /**
   * @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;

  // 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;
  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;
}

/**
 * Enqueues a synthetic event that should be dispatched when
 * `processEventQueue` is invoked.
 *
 * @param {*} events An accumulation of synthetic events.
 * @internal
 */
function enqueueEvents(events) {
  if (events) {
    eventQueue = accumulateInto(eventQueue, events);
  }
}

/**
 * Dispatches all synthetic events on the event queue.
 *
 * @internal
 */
function processEventQueue(simulated) {
  // 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;
  }

  if (simulated) {
    forEachAccumulated(processingEventQueue, executeDispatchesAndReleaseSimulated);
  } else {
    forEachAccumulated(processingEventQueue, executeDispatchesAndReleaseTopLevel);
  }
  !!eventQueue ? reactProdInvariant('95') : void 0;
  // This would be a good time to rethrow if any of the event handlers threw.
  ReactErrorUtils.rethrowCaughtError();
}

var EventPluginHub = Object.freeze({
	injection: injection$1,
	getListener: getListener,
	extractEvents: extractEvents,
	enqueueEvents: enqueueEvents,
	processEventQueue: processEventQueue
});

var IndeterminateComponent = 0; // Before we know whether it is functional or class
var FunctionalComponent = 1;
var ClassComponent = 2;
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 CallComponent = 7;
var CallHandlerPhase = 8;
var ReturnComponent = 9;
var Fragment = 10;

var randomKey = Math.random().toString(36).slice(2);
var internalInstanceKey = '__reactInternalInstance$' + randomKey;
var internalEventHandlersKey = '__reactEventHandlers$' + randomKey;

function precacheFiberNode$1(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];
  }

  // Walk up the tree until we find an ancestor whose instance we have cached.
  var parents = [];
  while (!node[internalInstanceKey]) {
    parents.push(node);
    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 closest = void 0;
  var inst = node[internalInstanceKey];
  if (inst.tag === HostComponent || inst.tag === HostText) {
    // In Fiber, this will always be the deepest root.
    return inst;
  }
  for (; node && (inst = node[internalInstanceKey]); node = parents.pop()) {
    closest = inst;
  }

  return closest;
}

/**
 * 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$1(node, props) {
  node[internalEventHandlersKey] = props;
}

var ReactDOMComponentTree = Object.freeze({
	precacheFiberNode: precacheFiberNode$1,
	getClosestInstanceFromNode: getClosestInstanceFromNode,
	getInstanceFromNode: getInstanceFromNode$1,
	getNodeFromInstance: getNodeFromInstance$1,
	getFiberCurrentPropsFromNode: getFiberCurrentPropsFromNode$1,
	updateFiberProps: updateFiberProps$1
});

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.
 */
function getParentInstance(inst) {
  return getParent(inst);
}

/**
 * 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;
  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);
  }
}

/**
 * Same as `accumulateTwoPhaseDispatchesSingle`, but skips over the targetID.
 */
function accumulateTwoPhaseDispatchesSingleSkipTarget(event) {
  if (event && event.dispatchConfig.phasedRegistrationNames) {
    var targetInst = event._targetInst;
    var parentInst = targetInst ? getParentInstance(targetInst) : null;
    traverseTwoPhase(parentInst, 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 accumulateTwoPhaseDispatchesSkipTarget(events) {
  forEachAccumulated(events, accumulateTwoPhaseDispatchesSingleSkipTarget);
}

function accumulateEnterLeaveDispatches(leave, enter, from, to) {
  traverseEnterLeave(from, to, accumulateDispatches, leave, enter);
}

function accumulateDirectDispatches(events) {
  forEachAccumulated(events, accumulateDirectDispatchesSingle);
}

var EventPropagators = Object.freeze({
	accumulateTwoPhaseDispatches: accumulateTwoPhaseDispatches,
	accumulateTwoPhaseDispatchesSkipTarget: accumulateTwoPhaseDispatchesSkipTarget,
	accumulateEnterLeaveDispatches: accumulateEnterLeaveDispatches,
	accumulateDirectDispatches: accumulateDirectDispatches
});

/**
 * 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 canUseDOM = !!(typeof window !== 'undefined' && window.document && window.document.createElement);

/**
 * Simple, lightweight module assisting with the detection and context of
 * Worker. Helps avoid circular dependencies and allows code to reason about
 * whether or not they are in a Worker, even if they never include the main
 * `ReactWorker` dependency.
 */
var ExecutionEnvironment = {

  canUseDOM: canUseDOM,

  canUseWorkers: typeof Worker !== 'undefined',

  canUseEventListeners: canUseDOM && !!(window.addEventListener || window.attachEvent),

  canUseViewport: canUseDOM && !!window.screen,

  isInWorker: !canUseDOM // For now, this is true - might change in the future.

};

var ExecutionEnvironment_1 = ExecutionEnvironment;

var contentKey = null;

/**
 * Gets the key used to access text content on a DOM node.
 *
 * @return {?string} Key used to access text content.
 * @internal
 */
function getTextContentAccessor() {
  if (!contentKey && ExecutionEnvironment_1.canUseDOM) {
    // Prefer textContent to innerText because many browsers support both but
    // SVG <text> elements don't support innerText even when <div> does.
    contentKey = 'textContent' in document.documentElement ? 'textContent' : 'innerText';
  }
  return contentKey;
}

/**
 * This helper object stores 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 compositionState = {
  _root: null,
  _startText: null,
  _fallbackText: null
};

function initialize(nativeEventTarget) {
  compositionState._root = nativeEventTarget;
  compositionState._startText = getText();
  return true;
}

function reset() {
  compositionState._root = null;
  compositionState._startText = null;
  compositionState._fallbackText = null;
}

function getData() {
  if (compositionState._fallbackText) {
    return compositionState._fallbackText;
  }

  var start;
  var startValue = compositionState._startText;
  var startLength = startValue.length;
  var end;
  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;
  compositionState._fallbackText = endValue.slice(start, sliceTail);
  return compositionState._fallbackText;
}

function getText() {
  if ('value' in compositionState._root) {
    return compositionState._root.value;
  }
  return compositionState._root[getTextContentAccessor()];
}

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;

var shouldBeReleasedProperties = ['dispatchConfig', '_targetInst', 'nativeEvent', 'isDefaultPrevented', 'isPropagationStopped', '_dispatchListeners', '_dispatchInstances'];

/**
 * @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: emptyFunction_1.thatReturnsNull,
  eventPhase: null,
  bubbles: null,
  cancelable: null,
  timeStamp: function (event) {
    return event.timeStamp || Date.now();
  },
  defaultPrevented: null,
  isTrusted: null
};

/**
 * 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 = emptyFunction_1.thatReturnsTrue;
  } else {
    this.isDefaultPrevented = emptyFunction_1.thatReturnsFalse;
  }
  this.isPropagationStopped = emptyFunction_1.thatReturnsFalse;
  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 = emptyFunction_1.thatReturnsTrue;
  },

  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 = emptyFunction_1.thatReturnsTrue;
  },

  /**
   * 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 = emptyFunction_1.thatReturnsTrue;
  },

  /**
   * Checks if this event should be released back into the pool.
   *
   * @return {boolean} True if this should not be released, false otherwise.
   */
  isPersistent: emptyFunction_1.thatReturnsFalse,

  /**
   * `PooledClass` looks for `destructor` on each instance it releases.
   */
  destructor: function () {
    var Interface = this.constructor.Interface;
    for (var propName in Interface) {
      {
        this[propName] = null;
      }
    }
    for (var i = 0; i < shouldBeReleasedProperties.length; i++) {
      this[shouldBeReleasedProperties[i]] = null;
    }
    
  }
});

SyntheticEvent.Interface = EventInterface;

/**
 * Helper to reduce boilerplate when creating subclasses.
 *
 * @param {function} Class
 * @param {?object} Interface
 */
SyntheticEvent.augmentClass = function (Class, Interface) {
  var Super = this;

  var E = function () {};
  E.prototype = Super.prototype;
  var prototype = new E();

  _assign(prototype, Class.prototype);
  Class.prototype = prototype;
  Class.prototype.constructor = Class;

  Class.Interface = _assign({}, Super.Interface, Interface);
  Class.augmentClass = Super.augmentClass;
  addEventPoolingTo(Class);
};

/** Proxying after everything set on SyntheticEvent
 * to resolve Proxy issue on some WebKit browsers
 * in which some Event properties are set to undefined (GH#10010)
 */
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('223') : 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 CompositionEventInterface = {
  data: null
};

/**
 * @param {object} dispatchConfig Configuration used to dispatch this event.
 * @param {string} dispatchMarker Marker identifying the event target.
 * @param {object} nativeEvent Native browser event.
 * @extends {SyntheticEvent}
 */
function SyntheticCompositionEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
  return SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
}

SyntheticEvent.augmentClass(SyntheticCompositionEvent, CompositionEventInterface);

/**
 * @interface Event
 * @see http://www.w3.org/TR/2013/WD-DOM-Level-3-Events-20131105
 *      /#events-inputevents
 */
var InputEventInterface = {
  data: null
};

/**
 * @param {object} dispatchConfig Configuration used to dispatch this event.
 * @param {string} dispatchMarker Marker identifying the event target.
 * @param {object} nativeEvent Native browser event.
 * @extends {SyntheticEvent}
 */
function SyntheticInputEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
  return SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
}

SyntheticEvent.augmentClass(SyntheticInputEvent, InputEventInterface);

var END_KEYCODES = [9, 13, 27, 32]; // Tab, Return, Esc, Space
var START_KEYCODE = 229;

var canUseCompositionEvent = ExecutionEnvironment_1.canUseDOM && 'CompositionEvent' in window;

var documentMode = null;
if (ExecutionEnvironment_1.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 = ExecutionEnvironment_1.canUseDOM && 'TextEvent' in window && !documentMode && !isPresto();

// 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 = ExecutionEnvironment_1.canUseDOM && (!canUseCompositionEvent || documentMode && documentMode > 8 && documentMode <= 11);

/**
 * Opera <= 12 includes TextEvent in window, but does not fire
 * text input events. Rely on keypress instead.
 */
function isPresto() {
  var opera = window.opera;
  return typeof opera === 'object' && typeof opera.version === 'function' && parseInt(opera.version(), 10) <= 12;
}

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: ['topCompositionEnd', 'topKeyPress', 'topTextInput', 'topPaste']
  },
  compositionEnd: {
    phasedRegistrationNames: {
      bubbled: 'onCompositionEnd',
      captured: 'onCompositionEndCapture'
    },
    dependencies: ['topBlur', 'topCompositionEnd', 'topKeyDown', 'topKeyPress', 'topKeyUp', 'topMouseDown']
  },
  compositionStart: {
    phasedRegistrationNames: {
      bubbled: 'onCompositionStart',
      captured: 'onCompositionStartCapture'
    },
    dependencies: ['topBlur', 'topCompositionStart', 'topKeyDown', 'topKeyPress', 'topKeyUp', 'topMouseDown']
  },
  compositionUpdate: {
    phasedRegistrationNames: {
      bubbled: 'onCompositionUpdate',
      captured: 'onCompositionUpdateCapture'
    },
    dependencies: ['topBlur', 'topCompositionUpdate', 'topKeyDown', 'topKeyPress', 'topKeyUp', 'topMouseDown']
  }
};

// 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 'topCompositionStart':
      return eventTypes.compositionStart;
    case 'topCompositionEnd':
      return eventTypes.compositionEnd;
    case 'topCompositionUpdate':
      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 === 'topKeyDown' && 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 'topKeyUp':
      // Command keys insert or clear IME input.
      return END_KEYCODES.indexOf(nativeEvent.keyCode) !== -1;
    case 'topKeyDown':
      // Expect IME keyCode on each keydown. If we get any other
      // code we must have exited earlier.
      return nativeEvent.keyCode !== START_KEYCODE;
    case 'topKeyPress':
    case 'topMouseDown':
    case 'topBlur':
      // 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;
}

// Track the current IME composition status, if any.
var isComposing = false;

/**
 * @return {?object} A SyntheticCompositionEvent.
 */
function extractCompositionEvent(topLevelType, targetInst, nativeEvent, nativeEventTarget) {
  var eventType;
  var fallbackData;

  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) {
    // 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 {TopLevelTypes} topLevelType Record from `BrowserEventConstants`.
 * @param {object} nativeEvent Native browser event.
 * @return {?string} The string corresponding to this `beforeInput` event.
 */
function getNativeBeforeInputChars(topLevelType, nativeEvent) {
  switch (topLevelType) {
    case 'topCompositionEnd':
      return getDataFromCustomEvent(nativeEvent);
    case 'topKeyPress':
      /**
       * 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 'topTextInput':
      // 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 blacklist 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 {string} topLevelType Record from `BrowserEventConstants`.
 * @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 === 'topCompositionEnd' || !canUseCompositionEvent && isFallbackCompositionEnd(topLevelType, nativeEvent)) {
      var chars = getData();
      reset();
      isComposing = false;
      return chars;
    }
    return null;
  }

  switch (topLevelType) {
    case 'topPaste':
      // If a paste event occurs after a keypress, throw out the input
      // chars. Paste events should not lead to BeforeInput events.
      return null;
    case 'topKeyPress':
      /**
       * 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 'topCompositionEnd':
      return useFallbackCompositionData ? 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;

  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) {
    return [extractCompositionEvent(topLevelType, targetInst, nativeEvent, nativeEventTarget), extractBeforeInputEvent(topLevelType, targetInst, nativeEvent, nativeEventTarget)];
  }
};

// Use to restore controlled state after a change event has fired.

var fiberHostComponent = null;

var ReactControlledComponentInjection = {
  injectFiberControlledHostComponent: function (hostComponentImpl) {
    // The fiber implementation doesn't use dynamic dispatch so we need to
    // inject the implementation.
    fiberHostComponent = hostComponentImpl;
  }
};

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;
  }
  !(fiberHostComponent && typeof fiberHostComponent.restoreControlledState === 'function') ? reactProdInvariant('194') : void 0;
  var props = getFiberCurrentPropsFromNode(internalInstance.stateNode);
  fiberHostComponent.restoreControlledState(internalInstance.stateNode, internalInstance.type, props);
}

var injection$3 = ReactControlledComponentInjection;

function enqueueStateRestore(target) {
  if (restoreTarget) {
    if (restoreQueue) {
      restoreQueue.push(target);
    } else {
      restoreQueue = [target];
    }
  } else {
    restoreTarget = target;
  }
}

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]);
    }
  }
}

var ReactControlledComponent = Object.freeze({
	injection: injection$3,
	enqueueStateRestore: enqueueStateRestore,
	restoreStateIfNeeded: restoreStateIfNeeded
});

// 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 fiberBatchedUpdates = function (fn, bookkeeping) {
  return fn(bookkeeping);
};

var isNestingBatched = false;
function batchedUpdates(fn, bookkeeping) {
  if (isNestingBatched) {
    // If we are currently inside another batch, we need to wait until it
    // fully completes before restoring state. Therefore, we add the target to
    // a queue of work.
    return fiberBatchedUpdates(fn, bookkeeping);
  }
  isNestingBatched = true;
  try {
    return fiberBatchedUpdates(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.
    isNestingBatched = false;
    restoreStateIfNeeded();
  }
}

var ReactGenericBatchingInjection = {
  injectFiberBatchedUpdates: function (_batchedUpdates) {
    fiberBatchedUpdates = _batchedUpdates;
  }
};

var injection$4 = ReactGenericBatchingInjection;

/**
 * @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) {
  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;
}

var useHasFeature;
if (ExecutionEnvironment_1.canUseDOM) {
  useHasFeature = document.implementation && document.implementation.hasFeature &&
  // always returns true in newer browsers as per the standard.
  // @see http://dom.spec.whatwg.org/#dom-domimplementation-hasfeature
  document.implementation.hasFeature('', '') !== true;
}

/**
 * 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".
 * @param {?boolean} capture Check if the capture phase is supported.
 * @return {boolean} True if the event is supported.
 * @internal
 * @license Modernizr 3.0.0pre (Custom Build) | MIT
 */
function isEventSupported(eventNameSuffix, capture) {
  if (!ExecutionEnvironment_1.canUseDOM || capture && !('addEventListener' in document)) {
    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';
  }

  if (!isSupported && useHasFeature && eventNameSuffix === 'wheel') {
    // This is the only way to test support for the `wheel` event in IE9+.
    isSupported = document.implementation.hasFeature('Events.wheel', '3.0');
  }

  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.get !== 'function' || typeof descriptor.set !== 'function') {
    return;
  }

  Object.defineProperty(node, valueField, {
    enumerable: descriptor.enumerable,
    configurable: true,
    get: function () {
      return descriptor.get.call(this);
    },
    set: function (value) {
      currentValue = '' + value;
      descriptor.set.call(this, value);
    }
  });

  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 eventTypes$1 = {
  change: {
    phasedRegistrationNames: {
      bubbled: 'onChange',
      captured: 'onChangeCapture'
    },
    dependencies: ['topBlur', 'topChange', 'topClick', 'topFocus', 'topInput', 'topKeyDown', 'topKeyUp', 'topSelectionChange']
  }
};

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) {
  enqueueEvents(event);
  processEventQueue(false);
}

function getInstIfValueChanged(targetInst) {
  var targetNode = getNodeFromInstance$1(targetInst);
  if (updateValueIfChanged(targetNode)) {
    return targetInst;
  }
}

function getTargetInstForChangeEvent(topLevelType, targetInst) {
  if (topLevelType === 'topChange') {
    return targetInst;
  }
}

/**
 * SECTION: handle `input` event
 */
var isInputEventSupported = false;
if (ExecutionEnvironment_1.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 === 'topFocus') {
    // 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 === 'topBlur') {
    stopWatchingForValueChange();
  }
}

// For IE8 and IE9.
function getTargetInstForInputEventPolyfill(topLevelType, targetInst) {
  if (topLevelType === 'topSelectionChange' || topLevelType === 'topKeyUp' || topLevelType === 'topKeyDown') {
    // 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 === 'topClick') {
    return getInstIfValueChanged(targetInst);
  }
}

function getTargetInstForInputOrChangeEvent(topLevelType, targetInst) {
  if (topLevelType === 'topInput' || topLevelType === 'topChange') {
    return getInstIfValueChanged(targetInst);
  }
}

function handleControlledInputBlur(inst, node) {
  // TODO: In IE, inst is occasionally null. Why?
  if (inst == null) {
    return;
  }

  // Fiber and ReactDOM keep wrapper state in separate places
  var state = inst._wrapperState || node._wrapperState;

  if (!state || !state.controlled || node.type !== 'number') {
    return;
  }

  // If controlled, assign the value attribute to the current value on blur
  var value = '' + node.value;
  if (node.getAttribute('value') !== value) {
    node.setAttribute('value', 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, handleEventFunc;
    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 === 'topBlur') {
      handleControlledInputBlur(targetInst, 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', 'TapEventPlugin', 'EnterLeaveEventPlugin', 'ChangeEventPlugin', 'SelectEventPlugin', 'BeforeInputEventPlugin'];

/**
 * @interface UIEvent
 * @see http://www.w3.org/TR/DOM-Level-3-Events/
 */
var UIEventInterface = {
  view: null,
  detail: null
};

/**
 * @param {object} dispatchConfig Configuration used to dispatch this event.
 * @param {string} dispatchMarker Marker identifying the event target.
 * @param {object} nativeEvent Native browser event.
 * @extends {SyntheticEvent}
 */
function SyntheticUIEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
  return SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
}

SyntheticEvent.augmentClass(SyntheticUIEvent, UIEventInterface);

/**
 * Translation from modifier key to the associated property in the event.
 * @see http://www.w3.org/TR/DOM-Level-3-Events/#keys-Modifiers
 */

var modifierKeyToProp = {
  Alt: 'altKey',
  Control: 'ctrlKey',
  Meta: 'metaKey',
  Shift: 'shiftKey'
};

// IE8 does not implement getModifierState so we simply map it to the only
// modifier keys exposed by the event itself, does not support Lock-keys.
// Currently, all major browsers except Chrome seems to support Lock-keys.
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;
}

/**
 * @interface MouseEvent
 * @see http://www.w3.org/TR/DOM-Level-3-Events/
 */
var MouseEventInterface = {
  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);
  }
};

/**
 * @param {object} dispatchConfig Configuration used to dispatch this event.
 * @param {string} dispatchMarker Marker identifying the event target.
 * @param {object} nativeEvent Native browser event.
 * @extends {SyntheticUIEvent}
 */
function SyntheticMouseEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
  return SyntheticUIEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
}

SyntheticUIEvent.augmentClass(SyntheticMouseEvent, MouseEventInterface);

var eventTypes$2 = {
  mouseEnter: {
    registrationName: 'onMouseEnter',
    dependencies: ['topMouseOut', 'topMouseOver']
  },
  mouseLeave: {
    registrationName: 'onMouseLeave',
    dependencies: ['topMouseOut', 'topMouseOver']
  }
};

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) {
    if (topLevelType === 'topMouseOver' && (nativeEvent.relatedTarget || nativeEvent.fromElement)) {
      return null;
    }
    if (topLevelType !== 'topMouseOut' && topLevelType !== 'topMouseOver') {
      // Must not be a mouse in or mouse out - ignoring.
      return null;
    }

    var win;
    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;
    var to;
    if (topLevelType === 'topMouseOut') {
      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 fromNode = from == null ? win : getNodeFromInstance$1(from);
    var toNode = to == null ? win : getNodeFromInstance$1(to);

    var leave = SyntheticMouseEvent.getPooled(eventTypes$2.mouseLeave, from, nativeEvent, nativeEventTarget);
    leave.type = 'mouseleave';
    leave.target = fromNode;
    leave.relatedTarget = toNode;

    var enter = SyntheticMouseEvent.getPooled(eventTypes$2.mouseEnter, to, nativeEvent, nativeEventTarget);
    enter.type = 'mouseenter';
    enter.target = toNode;
    enter.relatedTarget = fromNode;

    accumulateEnterLeaveDispatches(leave, enter, from, to);

    return [leave, enter];
  }
};

/**
 * `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;
}

var ReactInternals$1 = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;

var ReactCurrentOwner = ReactInternals$1.ReactCurrentOwner;

function getComponentName(fiber) {
  var type = fiber.type;

  if (typeof type === 'string') {
    return type;
  }
  if (typeof type === 'function') {
    return type.displayName || type.name;
  }
  return null;
}

// Don't change these two values:
var NoEffect = 0; //           0b00000000
var PerformedWork = 1; //      0b00000001

// You can change the rest (and add more).
var Placement = 2; //          0b00000010
var Update = 4; //             0b00000100
var PlacementAndUpdate = 6; // 0b00000110
var Deletion = 8; //           0b00001000
var ContentReset = 16; //      0b00010000
var Callback = 32; //          0b00100000
var Err = 64; //               0b01000000
var Ref = 128; //              0b10000000

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 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;
}

/**
 * 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.
 *
 * @typechecks
 */



/**
 * Upstream version of event listener. Does not take into account specific
 * nature of platform.
 */
var EventListener = {
  /**
   * Listen to DOM events during the bubble phase.
   *
   * @param {DOMEventTarget} target DOM element to register listener on.
   * @param {string} eventType Event type, e.g. 'click' or 'mouseover'.
   * @param {function} callback Callback function.
   * @return {object} Object with a `remove` method.
   */
  listen: function listen(target, eventType, callback) {
    if (target.addEventListener) {
      target.addEventListener(eventType, callback, false);
      return {
        remove: function remove() {
          target.removeEventListener(eventType, callback, false);
        }
      };
    } else if (target.attachEvent) {
      target.attachEvent('on' + eventType, callback);
      return {
        remove: function remove() {
          target.detachEvent('on' + eventType, callback);
        }
      };
    }
  },

  /**
   * Listen to DOM events during the capture phase.
   *
   * @param {DOMEventTarget} target DOM element to register listener on.
   * @param {string} eventType Event type, e.g. 'click' or 'mouseover'.
   * @param {function} callback Callback function.
   * @return {object} Object with a `remove` method.
   */
  capture: function capture(target, eventType, callback) {
    if (target.addEventListener) {
      target.addEventListener(eventType, callback, true);
      return {
        remove: function remove() {
          target.removeEventListener(eventType, callback, true);
        }
      };
    } else {
      return {
        remove: emptyFunction_1
      };
    }
  },

  registerDefault: function registerDefault() {}
};

var EventListener_1 = EventListener;

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 handleTopLevelImpl(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];
    _handleTopLevel(bookKeeping.topLevelType, targetInst, bookKeeping.nativeEvent, getEventTarget(bookKeeping.nativeEvent));
  }
}

// TODO: can we stop exporting these?
var _enabled = true;
var _handleTopLevel = void 0;

function setHandleTopLevel(handleTopLevel) {
  _handleTopLevel = handleTopLevel;
}

function setEnabled(enabled) {
  _enabled = !!enabled;
}

function isEnabled() {
  return _enabled;
}

/**
 * Traps top-level events by using event bubbling.
 *
 * @param {string} topLevelType Record from `BrowserEventConstants`.
 * @param {string} handlerBaseName Event name (e.g. "click").
 * @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, handlerBaseName, element) {
  if (!element) {
    return null;
  }
  return EventListener_1.listen(element, handlerBaseName, dispatchEvent.bind(null, topLevelType));
}

/**
 * Traps a top-level event by using event capturing.
 *
 * @param {string} topLevelType Record from `BrowserEventConstants`.
 * @param {string} handlerBaseName Event name (e.g. "click").
 * @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, handlerBaseName, element) {
  if (!element) {
    return null;
  }
  return EventListener_1.capture(element, handlerBaseName, dispatchEvent.bind(null, topLevelType));
}

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(handleTopLevelImpl, bookKeeping);
  } finally {
    releaseTopLevelCallbackBookKeeping(bookKeeping);
  }
}

var ReactDOMEventListener = Object.freeze({
	get _enabled () { return _enabled; },
	get _handleTopLevel () { return _handleTopLevel; },
	setHandleTopLevel: setHandleTopLevel,
	setEnabled: setEnabled,
	isEnabled: isEnabled,
	trapBubbledEvent: trapBubbledEvent,
	trapCapturedEvent: trapCapturedEvent,
	dispatchEvent: dispatchEvent
});

/**
 * 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;
  prefixes['ms' + styleProp] = 'MS' + eventName;
  prefixes['O' + styleProp] = 'o' + eventName.toLowerCase();

  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 (ExecutionEnvironment_1.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 '';
}

/**
 * Types of raw signals from the browser caught at the top level.
 *
 * For events like 'submit' which don't consistently bubble (which we
 * trap at a lower node than `document`), binding at `document` would
 * cause duplicate events so we don't include them here.
 */
var topLevelTypes$1 = {
  topAbort: 'abort',
  topAnimationEnd: getVendorPrefixedEventName('animationend') || 'animationend',
  topAnimationIteration: getVendorPrefixedEventName('animationiteration') || 'animationiteration',
  topAnimationStart: getVendorPrefixedEventName('animationstart') || 'animationstart',
  topBlur: 'blur',
  topCancel: 'cancel',
  topCanPlay: 'canplay',
  topCanPlayThrough: 'canplaythrough',
  topChange: 'change',
  topClick: 'click',
  topClose: 'close',
  topCompositionEnd: 'compositionend',
  topCompositionStart: 'compositionstart',
  topCompositionUpdate: 'compositionupdate',
  topContextMenu: 'contextmenu',
  topCopy: 'copy',
  topCut: 'cut',
  topDoubleClick: 'dblclick',
  topDrag: 'drag',
  topDragEnd: 'dragend',
  topDragEnter: 'dragenter',
  topDragExit: 'dragexit',
  topDragLeave: 'dragleave',
  topDragOver: 'dragover',
  topDragStart: 'dragstart',
  topDrop: 'drop',
  topDurationChange: 'durationchange',
  topEmptied: 'emptied',
  topEncrypted: 'encrypted',
  topEnded: 'ended',
  topError: 'error',
  topFocus: 'focus',
  topInput: 'input',
  topKeyDown: 'keydown',
  topKeyPress: 'keypress',
  topKeyUp: 'keyup',
  topLoadedData: 'loadeddata',
  topLoad: 'load',
  topLoadedMetadata: 'loadedmetadata',
  topLoadStart: 'loadstart',
  topMouseDown: 'mousedown',
  topMouseMove: 'mousemove',
  topMouseOut: 'mouseout',
  topMouseOver: 'mouseover',
  topMouseUp: 'mouseup',
  topPaste: 'paste',
  topPause: 'pause',
  topPlay: 'play',
  topPlaying: 'playing',
  topProgress: 'progress',
  topRateChange: 'ratechange',
  topScroll: 'scroll',
  topSeeked: 'seeked',
  topSeeking: 'seeking',
  topSelectionChange: 'selectionchange',
  topStalled: 'stalled',
  topSuspend: 'suspend',
  topTextInput: 'textInput',
  topTimeUpdate: 'timeupdate',
  topToggle: 'toggle',
  topTouchCancel: 'touchcancel',
  topTouchEnd: 'touchend',
  topTouchMove: 'touchmove',
  topTouchStart: 'touchstart',
  topTransitionEnd: getVendorPrefixedEventName('transitionend') || 'transitionend',
  topVolumeChange: 'volumechange',
  topWaiting: 'waiting',
  topWheel: 'wheel'
};

var BrowserEventConstants = {
  topLevelTypes: topLevelTypes$1
};

function runEventQueueInBatch(events) {
  enqueueEvents(events);
  processEventQueue(false);
}

/**
 * Streams a fired top-level event to `EventPluginHub` where plugins have the
 * opportunity to create `ReactEvent`s to be dispatched.
 */
function handleTopLevel(topLevelType, targetInst, nativeEvent, nativeEventTarget) {
  var events = extractEvents(topLevelType, targetInst, nativeEvent, nativeEventTarget);
  runEventQueueInBatch(events);
}

var topLevelTypes = BrowserEventConstants.topLevelTypes;

/**
 * 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} contentDocumentHandle Document which owns the container
 */
function listenTo(registrationName, contentDocumentHandle) {
  var mountAt = contentDocumentHandle;
  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])) {
      if (dependency === 'topScroll') {
        trapCapturedEvent('topScroll', 'scroll', mountAt);
      } else if (dependency === 'topFocus' || dependency === 'topBlur') {
        trapCapturedEvent('topFocus', 'focus', mountAt);
        trapCapturedEvent('topBlur', 'blur', mountAt);

        // to make sure blur and focus event listeners are only attached once
        isListening.topBlur = true;
        isListening.topFocus = true;
      } else if (dependency === 'topCancel') {
        if (isEventSupported('cancel', true)) {
          trapCapturedEvent('topCancel', 'cancel', mountAt);
        }
        isListening.topCancel = true;
      } else if (dependency === 'topClose') {
        if (isEventSupported('close', true)) {
          trapCapturedEvent('topClose', 'close', mountAt);
        }
        isListening.topClose = true;
      } else if (topLevelTypes.hasOwnProperty(dependency)) {
        trapBubbledEvent(dependency, topLevelTypes[dependency], mountAt);
      }

      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;
}

/**
 * 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.
 *
 * @typechecks
 */

/* eslint-disable fb-www/typeof-undefined */

/**
 * Same as document.activeElement but wraps in a try-catch block. In IE it is
 * not safe to call document.activeElement if there is nothing focused.
 *
 * The activeElement will be null only if the document or document body is not
 * yet defined.
 *
 * @param {?DOMDocument} doc Defaults to current document.
 * @return {?DOMElement}
 */
function getActiveElement(doc) /*?DOMElement*/{
  doc = doc || (typeof document !== 'undefined' ? document : undefined);
  if (typeof doc === 'undefined') {
    return null;
  }
  try {
    return doc.activeElement || doc.body;
  } catch (e) {
    return doc.body;
  }
}

var getActiveElement_1 = getActiveElement;

/**
 * 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.
 *
 * @typechecks
 * 
 */

/*eslint-disable no-self-compare */



var hasOwnProperty = Object.prototype.hasOwnProperty;

/**
 * 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) {
  // SameValue algorithm
  if (x === y) {
    // Steps 1-5, 7-10
    // Steps 6.b-6.e: +0 != -0
    // Added the nonzero y check to make Flow happy, but it is redundant
    return x !== 0 || y !== 0 || 1 / x === 1 / y;
  } else {
    // Step 6.a: NaN == NaN
    return x !== x && y !== y;
  }
}

/**
 * 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;
}

var shallowEqual_1 = shallowEqual;

/**
 * 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.
 *
 * @typechecks
 */

/**
 * @param {*} object The object to check.
 * @return {boolean} Whether or not the object is a DOM node.
 */
function isNode(object) {
  var doc = object ? object.ownerDocument || object : document;
  var defaultView = doc.defaultView || window;
  return !!(object && (typeof defaultView.Node === 'function' ? object instanceof defaultView.Node : typeof object === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string'));
}

var isNode_1 = isNode;

/**
 * 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.
 *
 * @typechecks
 */



/**
 * @param {*} object The object to check.
 * @return {boolean} Whether or not the object is a DOM text node.
 */
function isTextNode(object) {
  return isNode_1(object) && object.nodeType == 3;
}

var isTextNode_1 = isTextNode;

/**
 * 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.
 *
 * 
 */



/*eslint-disable no-bitwise */

/**
 * Checks if a given DOM node contains or is another DOM node.
 */
function containsNode(outerNode, innerNode) {
  if (!outerNode || !innerNode) {
    return false;
  } else if (outerNode === innerNode) {
    return true;
  } else if (isTextNode_1(outerNode)) {
    return false;
  } else if (isTextNode_1(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;
  }
}

var containsNode_1 = containsNode;

/**
 * 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.
 *
 */



/**
 * @param {DOMElement} node input/textarea to focus
 */

function focusNode(node) {
  // IE8 can throw "Can't move focus to the control because it is invisible,
  // not enabled, or of a type that does not accept the focus." for all kinds of
  // reasons that are too expensive and fragile to test.
  try {
    node.focus();
  } catch (e) {}
}

var focusNode_1 = focusNode;

/**
 * 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 selection = window.getSelection && window.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) {
  if (!window.getSelection) {
    return;
  }

  var selection = window.getSelection();
  var length = node[getTextContentAccessor()].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 = document.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 isInDocument(node) {
  return containsNode_1(document.documentElement, node);
}

/**
 * @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.
 */

function hasSelectionCapabilities(elem) {
  var nodeName = elem && elem.nodeName && elem.nodeName.toLowerCase();
  return nodeName && (nodeName === 'input' && elem.type === 'text' || nodeName === 'textarea' || elem.contentEditable === 'true');
}

function getSelectionInformation() {
  var focusedElem = getActiveElement_1();
  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 = getActiveElement_1();
  var priorFocusedElem = priorSelectionInformation.focusedElem;
  var priorSelectionRange = priorSelectionInformation.selectionRange;
  if (curFocusedElem !== priorFocusedElem && isInDocument(priorFocusedElem)) {
    if (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
        });
      }
    }

    focusNode_1(priorFocusedElem);

    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 = ExecutionEnvironment_1.canUseDOM && 'documentMode' in document && document.documentMode <= 11;

var eventTypes$3 = {
  select: {
    phasedRegistrationNames: {
      bubbled: 'onSelect',
      captured: 'onSelectCapture'
    },
    dependencies: ['topBlur', 'topContextMenu', 'topFocus', 'topKeyDown', 'topKeyUp', 'topMouseDown', 'topMouseUp', 'topSelectionChange']
  }
};

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 if (window.getSelection) {
    var selection = window.getSelection();
    return {
      anchorNode: selection.anchorNode,
      anchorOffset: selection.anchorOffset,
      focusNode: selection.focusNode,
      focusOffset: selection.focusOffset
    };
  }
}

/**
 * Poll selection to see whether it's changed.
 *
 * @param {object} nativeEvent
 * @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.
  if (mouseDown || activeElement$1 == null || activeElement$1 !== getActiveElement_1()) {
    return null;
  }

  // Only fire when selection has actually changed.
  var currentSelection = getSelection(activeElement$1);
  if (!lastSelection || !shallowEqual_1(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 = nativeEventTarget.window === nativeEventTarget ? nativeEventTarget.document : nativeEventTarget.nodeType === DOCUMENT_NODE ? nativeEventTarget : nativeEventTarget.ownerDocument;
    // 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 'topFocus':
        if (isTextInputElement(targetNode) || targetNode.contentEditable === 'true') {
          activeElement$1 = targetNode;
          activeElementInst$1 = targetInst;
          lastSelection = null;
        }
        break;
      case 'topBlur':
        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 'topMouseDown':
        mouseDown = true;
        break;
      case 'topContextMenu':
      case 'topMouseUp':
        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 'topSelectionChange':
        if (skipSelectionChangeEvent) {
          break;
        }
      // falls through
      case 'topKeyDown':
      case 'topKeyUp':
        return constructSelectEvent(nativeEvent, nativeEventTarget);
    }

    return null;
  }
};

/**
 * @interface Event
 * @see http://www.w3.org/TR/css3-animations/#AnimationEvent-interface
 * @see https://developer.mozilla.org/en-US/docs/Web/API/AnimationEvent
 */
var AnimationEventInterface = {
  animationName: null,
  elapsedTime: null,
  pseudoElement: null
};

/**
 * @param {object} dispatchConfig Configuration used to dispatch this event.
 * @param {string} dispatchMarker Marker identifying the event target.
 * @param {object} nativeEvent Native browser event.
 * @extends {SyntheticEvent}
 */
function SyntheticAnimationEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
  return SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
}

SyntheticEvent.augmentClass(SyntheticAnimationEvent, AnimationEventInterface);

/**
 * @interface Event
 * @see http://www.w3.org/TR/clipboard-apis/
 */
var ClipboardEventInterface = {
  clipboardData: function (event) {
    return 'clipboardData' in event ? event.clipboardData : window.clipboardData;
  }
};

/**
 * @param {object} dispatchConfig Configuration used to dispatch this event.
 * @param {string} dispatchMarker Marker identifying the event target.
 * @param {object} nativeEvent Native browser event.
 * @extends {SyntheticEvent}
 */
function SyntheticClipboardEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
  return SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
}

SyntheticEvent.augmentClass(SyntheticClipboardEvent, ClipboardEventInterface);

/**
 * @interface FocusEvent
 * @see http://www.w3.org/TR/DOM-Level-3-Events/
 */
var FocusEventInterface = {
  relatedTarget: null
};

/**
 * @param {object} dispatchConfig Configuration used to dispatch this event.
 * @param {string} dispatchMarker Marker identifying the event target.
 * @param {object} nativeEvent Native browser event.
 * @extends {SyntheticUIEvent}
 */
function SyntheticFocusEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
  return SyntheticUIEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
}

SyntheticUIEvent.augmentClass(SyntheticFocusEvent, FocusEventInterface);

/**
 * `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;
  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;
  }

  // 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 KeyboardEventInterface = {
  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;
  }
};

/**
 * @param {object} dispatchConfig Configuration used to dispatch this event.
 * @param {string} dispatchMarker Marker identifying the event target.
 * @param {object} nativeEvent Native browser event.
 * @extends {SyntheticUIEvent}
 */
function SyntheticKeyboardEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
  return SyntheticUIEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
}

SyntheticUIEvent.augmentClass(SyntheticKeyboardEvent, KeyboardEventInterface);

/**
 * @interface DragEvent
 * @see http://www.w3.org/TR/DOM-Level-3-Events/
 */
var DragEventInterface = {
  dataTransfer: null
};

/**
 * @param {object} dispatchConfig Configuration used to dispatch this event.
 * @param {string} dispatchMarker Marker identifying the event target.
 * @param {object} nativeEvent Native browser event.
 * @extends {SyntheticMouseEvent}
 */
function SyntheticDragEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
  return SyntheticMouseEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
}

SyntheticMouseEvent.augmentClass(SyntheticDragEvent, DragEventInterface);

/**
 * @interface TouchEvent
 * @see http://www.w3.org/TR/touch-events/
 */
var TouchEventInterface = {
  touches: null,
  targetTouches: null,
  changedTouches: null,
  altKey: null,
  metaKey: null,
  ctrlKey: null,
  shiftKey: null,
  getModifierState: getEventModifierState
};

/**
 * @param {object} dispatchConfig Configuration used to dispatch this event.
 * @param {string} dispatchMarker Marker identifying the event target.
 * @param {object} nativeEvent Native browser event.
 * @extends {SyntheticUIEvent}
 */
function SyntheticTouchEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
  return SyntheticUIEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
}

SyntheticUIEvent.augmentClass(SyntheticTouchEvent, TouchEventInterface);

/**
 * @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 TransitionEventInterface = {
  propertyName: null,
  elapsedTime: null,
  pseudoElement: null
};

/**
 * @param {object} dispatchConfig Configuration used to dispatch this event.
 * @param {string} dispatchMarker Marker identifying the event target.
 * @param {object} nativeEvent Native browser event.
 * @extends {SyntheticEvent}
 */
function SyntheticTransitionEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
  return SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
}

SyntheticEvent.augmentClass(SyntheticTransitionEvent, TransitionEventInterface);

/**
 * @interface WheelEvent
 * @see http://www.w3.org/TR/DOM-Level-3-Events/
 */
var WheelEventInterface = {
  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
};

/**
 * @param {object} dispatchConfig Configuration used to dispatch this event.
 * @param {string} dispatchMarker Marker identifying the event target.
 * @param {object} nativeEvent Native browser event.
 * @extends {SyntheticMouseEvent}
 */
function SyntheticWheelEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
  return SyntheticMouseEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
}

SyntheticMouseEvent.augmentClass(SyntheticWheelEvent, WheelEventInterface);

/**
 * Turns
 * ['abort', ...]
 * into
 * eventTypes = {
 *   'abort': {
 *     phasedRegistrationNames: {
 *       bubbled: 'onAbort',
 *       captured: 'onAbortCapture',
 *     },
 *     dependencies: ['topAbort'],
 *   },
 *   ...
 * };
 * topLevelEventsToDispatchConfig = {
 *   'topAbort': { sameConfig }
 * };
 */
var eventTypes$4 = {};
var topLevelEventsToDispatchConfig = {};
['abort', 'animationEnd', 'animationIteration', 'animationStart', 'blur', 'cancel', 'canPlay', 'canPlayThrough', 'click', 'close', 'contextMenu', 'copy', 'cut', 'doubleClick', 'drag', 'dragEnd', 'dragEnter', 'dragExit', 'dragLeave', 'dragOver', 'dragStart', 'drop', 'durationChange', 'emptied', 'encrypted', 'ended', 'error', 'focus', 'input', 'invalid', 'keyDown', 'keyPress', 'keyUp', 'load', 'loadedData', 'loadedMetadata', 'loadStart', 'mouseDown', 'mouseMove', 'mouseOut', 'mouseOver', 'mouseUp', 'paste', 'pause', 'play', 'playing', 'progress', 'rateChange', 'reset', 'scroll', 'seeked', 'seeking', 'stalled', 'submit', 'suspend', 'timeUpdate', 'toggle', 'touchCancel', 'touchEnd', 'touchMove', 'touchStart', 'transitionEnd', 'volumeChange', 'waiting', 'wheel'].forEach(function (event) {
  var capitalizedEvent = event[0].toUpperCase() + event.slice(1);
  var onEvent = 'on' + capitalizedEvent;
  var topEvent = 'top' + capitalizedEvent;

  var type = {
    phasedRegistrationNames: {
      bubbled: onEvent,
      captured: onEvent + 'Capture'
    },
    dependencies: [topEvent]
  };
  eventTypes$4[event] = type;
  topLevelEventsToDispatchConfig[topEvent] = type;
});

var SimpleEventPlugin = {
  eventTypes: eventTypes$4,

  extractEvents: function (topLevelType, targetInst, nativeEvent, nativeEventTarget) {
    var dispatchConfig = topLevelEventsToDispatchConfig[topLevelType];
    if (!dispatchConfig) {
      return null;
    }
    var EventConstructor;
    switch (topLevelType) {
      case 'topKeyPress':
        // 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 'topKeyDown':
      case 'topKeyUp':
        EventConstructor = SyntheticKeyboardEvent;
        break;
      case 'topBlur':
      case 'topFocus':
        EventConstructor = SyntheticFocusEvent;
        break;
      case 'topClick':
        // Firefox creates a click event on right mouse clicks. This removes the
        // unwanted click events.
        if (nativeEvent.button === 2) {
          return null;
        }
      /* falls through */
      case 'topDoubleClick':
      case 'topMouseDown':
      case 'topMouseMove':
      case 'topMouseUp':
      // TODO: Disabled elements should not respond to mouse events
      /* falls through */
      case 'topMouseOut':
      case 'topMouseOver':
      case 'topContextMenu':
        EventConstructor = SyntheticMouseEvent;
        break;
      case 'topDrag':
      case 'topDragEnd':
      case 'topDragEnter':
      case 'topDragExit':
      case 'topDragLeave':
      case 'topDragOver':
      case 'topDragStart':
      case 'topDrop':
        EventConstructor = SyntheticDragEvent;
        break;
      case 'topTouchCancel':
      case 'topTouchEnd':
      case 'topTouchMove':
      case 'topTouchStart':
        EventConstructor = SyntheticTouchEvent;
        break;
      case 'topAnimationEnd':
      case 'topAnimationIteration':
      case 'topAnimationStart':
        EventConstructor = SyntheticAnimationEvent;
        break;
      case 'topTransitionEnd':
        EventConstructor = SyntheticTransitionEvent;
        break;
      case 'topScroll':
        EventConstructor = SyntheticUIEvent;
        break;
      case 'topWheel':
        EventConstructor = SyntheticWheelEvent;
        break;
      case 'topCopy':
      case 'topCut':
      case 'topPaste':
        EventConstructor = SyntheticClipboardEvent;
        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;
  }
};

setHandleTopLevel(handleTopLevel);

/**
 * Inject modules for resolving DOM hierarchy and plugin ordering.
 */
injection$1.injectEventPluginOrder(DOMEventPluginOrder);
injection$2.injectComponentTree(ReactDOMComponentTree);

/**
 * Some important event plugins included by default (without having to require
 * them).
 */
injection$1.injectEventPluginsByName({
  SimpleEventPlugin: SimpleEventPlugin,
  EnterLeaveEventPlugin: EnterLeaveEventPlugin,
  ChangeEventPlugin: ChangeEventPlugin,
  SelectEventPlugin: SelectEventPlugin,
  BeforeInputEventPlugin: BeforeInputEventPlugin
});

var enableAsyncSubtreeAPI = true;
var enableAsyncSchedulingByDefaultInReactDOM = false;
// Exports ReactDOM.createRoot
var enableCreateRoot = false;
var enableUserTimingAPI = false;

// Mutating mode (React DOM, React ART, React Native):
var enableMutatingReconciler = true;
// Experimental noop mode (currently unused):
var enableNoopReconciler = false;
// Experimental persistent mode (CS):
var enablePersistentReconciler = false;

// Helps identify side effects in begin-phase lifecycle hooks and setState reducers:
var debugRenderPhaseSideEffects = false;

// Only used in www builds.

/**
 * 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 = {};

var emptyObject_1 = emptyObject;

/**
 * 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 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;
}

function reset$1() {
  while (index > -1) {
    valueStack[index] = null;

    index--;
  }
}

var describeComponentFrame = function (name, source, ownerName) {
  return '\n    in ' + (name || 'Unknown') + (source ? ' (at ' + source.fileName.replace(/^.*[\\\/]/, '') + ':' + source.lineNumber + ')' : ownerName ? ' (created by ' + ownerName + ')' : '');
};

function describeFiber(fiber) {
  switch (fiber.tag) {
    case IndeterminateComponent:
    case FunctionalComponent:
    case ClassComponent:
    case HostComponent:
      var owner = fiber._debugOwner;
      var source = fiber._debugSource;
      var name = getComponentName(fiber);
      var ownerName = null;
      if (owner) {
        ownerName = getComponentName(owner);
      }
      return describeComponentFrame(name, source, ownerName);
    default:
      return '';
  }
}

// This function can only be called with a work-in-progress fiber and
// only during begin or complete phase. Do not call it under any other
// circumstances.
function getStackAddendumByWorkInProgressFiber(workInProgress) {
  var info = '';
  var node = workInProgress;
  do {
    info += describeFiber(node);
    // Otherwise this return pointer might point to the wrong tree:
    node = node['return'];
  } while (node);
  return info;
}

// 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) || '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) || '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) || '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 ReturnComponent:
    case Fragment:
      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) {
  if (enableUserTimingAPI) {
    if (supportsUserTiming) {
      isWaitingForCallback = false;
      var warning = didExpire ? 'React was blocked by main thread' : null;
      endMark('(Waiting for async callback...)', '(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 = '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) {
  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) || 'Unknown';
        warning = 'An update to ' + componentName + ' interrupted the previous render';
      }
    } else if (commitCountInCurrentWorkLoop > 1) {
      warning = 'There were cascading updates';
    }
    commitCountInCurrentWorkLoop = 0;
    // Pause any measurements until the next loop.
    pauseTimers();
    endMark('(React Tree Reconciliation)', '(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 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);
  }
}

// A cursor to the current merged context object on the stack.
var contextStackCursor = createCursor(emptyObject_1);
// 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 = emptyObject_1;

function getUnmaskedContext(workInProgress) {
  var hasOwnContext = isContextProvider(workInProgress);
  if (hasOwnContext) {
    // If the fiber is a context provider itself, when we read its context
    // we 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 emptyObject_1;
  }

  // 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 isContextConsumer(fiber) {
  return fiber.tag === ClassComponent && fiber.type.contextTypes != null;
}

function isContextProvider(fiber) {
  return fiber.tag === ClassComponent && fiber.type.childContextTypes != null;
}

function popContextProvider(fiber) {
  if (!isContextProvider(fiber)) {
    return;
  }

  pop(didPerformWorkStackCursor, fiber);
  pop(contextStackCursor, fiber);
}

function popTopLevelContextObject(fiber) {
  pop(didPerformWorkStackCursor, fiber);
  pop(contextStackCursor, fiber);
}

function pushTopLevelContextObject(fiber, context, didChange) {
  !(contextStackCursor.cursor == null) ? reactProdInvariant('168') : void 0;

  push(contextStackCursor, context, fiber);
  push(didPerformWorkStackCursor, didChange, fiber);
}

function processChildContext(fiber, parentContext) {
  var instance = fiber.stateNode;
  var childContextTypes = fiber.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(fiber) || 'Unknown', contextKey) : void 0;
  }
  return _assign({}, parentContext, childContext);
}

function pushContextProvider(workInProgress) {
  if (!isContextProvider(workInProgress)) {
    return false;
  }

  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 || emptyObject_1;

  // 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, 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, 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 resetContext() {
  previousContext = emptyObject_1;
  contextStackCursor.current = emptyObject_1;
  didPerformWorkStackCursor.current = false;
}

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;
  while (node.tag !== HostRoot) {
    if (isContextProvider(node)) {
      return node.stateNode.__reactInternalMemoizedMergedChildContext;
    }
    var parent = node['return'];
    !parent ? reactProdInvariant('171') : void 0;
    node = parent;
  }
  return node.stateNode.context;
}

var NoWork = 0; // TODO: Use an opaque type once ESLint et al support the syntax

var Sync = 1;
var Never = 2147483647; // Max int32: Math.pow(2, 31) - 1

var UNIT_SIZE = 10;
var MAGIC_NUMBER_OFFSET = 2;

// 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 (ms / UNIT_SIZE | 0) + MAGIC_NUMBER_OFFSET;
}

function expirationTimeToMs(expirationTime) {
  return (expirationTime - MAGIC_NUMBER_OFFSET) * UNIT_SIZE;
}

function ceiling(num, precision) {
  return ((num / precision | 0) + 1) * precision;
}

function computeExpirationBucket(currentTime, expirationInMs, bucketSizeMs) {
  return ceiling(currentTime + expirationInMs / UNIT_SIZE, bucketSizeMs / UNIT_SIZE);
}

var NoContext = 0;
var AsyncUpdates = 1;

// A Fiber is work on a Component that needs to be done or was done. There can
// be more than one per component.


function FiberNode(tag, key, internalContextTag) {
  // Instance
  this.tag = tag;
  this.key = key;
  this.type = null;
  this.stateNode = null;

  // Fiber
  this['return'] = null;
  this.child = null;
  this.sibling = null;
  this.index = 0;

  this.ref = null;

  this.pendingProps = null;
  this.memoizedProps = null;
  this.updateQueue = null;
  this.memoizedState = null;

  this.internalContextTag = internalContextTag;

  // Effects
  this.effectTag = NoEffect;
  this.nextEffect = null;

  this.firstEffect = null;
  this.lastEffect = null;

  this.expirationTime = NoWork;

  this.alternate = null;

  
}

// 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, key, internalContextTag) {
  // $FlowFixMe: the shapes are exact here but Flow doesn't like constructors
  return new FiberNode(tag, key, internalContextTag);
};

function shouldConstruct(Component) {
  return !!(Component.prototype && Component.prototype.isReactComponent);
}

// 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, current.key, current.internalContextTag);
    workInProgress.type = current.type;
    workInProgress.stateNode = current.stateNode;

    workInProgress.alternate = current;
    current.alternate = workInProgress;
  } else {
    // 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;
  }

  workInProgress.expirationTime = expirationTime;
  workInProgress.pendingProps = pendingProps;

  workInProgress.child = current.child;
  workInProgress.memoizedProps = current.memoizedProps;
  workInProgress.memoizedState = current.memoizedState;
  workInProgress.updateQueue = current.updateQueue;

  // These will be overridden during the parent's reconciliation
  workInProgress.sibling = current.sibling;
  workInProgress.index = current.index;
  workInProgress.ref = current.ref;

  return workInProgress;
}

function createHostRootFiber() {
  var fiber = createFiber(HostRoot, null, NoContext);
  return fiber;
}

function createFiberFromElement(element, internalContextTag, expirationTime) {
  var fiber = void 0;
  var type = element.type,
      key = element.key;

  if (typeof type === 'function') {
    fiber = shouldConstruct(type) ? createFiber(ClassComponent, key, internalContextTag) : createFiber(IndeterminateComponent, key, internalContextTag);
    fiber.type = type;
    fiber.pendingProps = element.props;
  } else if (typeof type === 'string') {
    fiber = createFiber(HostComponent, key, internalContextTag);
    fiber.type = type;
    fiber.pendingProps = element.props;
  } else if (typeof type === 'object' && type !== null && typeof type.tag === 'number') {
    // Currently assumed to be a continuation and therefore is a fiber already.
    // TODO: The yield system is currently broken for updates in some cases.
    // The reified yield stores a fiber, but we don't know which fiber that is;
    // the current or a workInProgress? When the continuation gets rendered here
    // we don't know if we can reuse that fiber or if we need to clone it.
    // There is probably a clever way to restructure this.
    fiber = type;
    fiber.pendingProps = element.props;
  } else {
    var info = '';
    reactProdInvariant('130', type == null ? type : typeof type, info);
  }

  fiber.expirationTime = expirationTime;

  return fiber;
}

function createFiberFromFragment(elements, internalContextTag, expirationTime, key) {
  var fiber = createFiber(Fragment, key, internalContextTag);
  fiber.pendingProps = elements;
  fiber.expirationTime = expirationTime;
  return fiber;
}

function createFiberFromText(content, internalContextTag, expirationTime) {
  var fiber = createFiber(HostText, null, internalContextTag);
  fiber.pendingProps = content;
  fiber.expirationTime = expirationTime;
  return fiber;
}

function createFiberFromHostInstanceForDeletion() {
  var fiber = createFiber(HostComponent, null, NoContext);
  fiber.type = 'DELETED';
  return fiber;
}

function createFiberFromCall(call, internalContextTag, expirationTime) {
  var fiber = createFiber(CallComponent, call.key, internalContextTag);
  fiber.type = call.handler;
  fiber.pendingProps = call;
  fiber.expirationTime = expirationTime;
  return fiber;
}

function createFiberFromReturn(returnNode, internalContextTag, expirationTime) {
  var fiber = createFiber(ReturnComponent, null, internalContextTag);
  fiber.expirationTime = expirationTime;
  return fiber;
}

function createFiberFromPortal(portal, internalContextTag, expirationTime) {
  var fiber = createFiber(HostPortal, portal.key, internalContextTag);
  fiber.pendingProps = portal.children || [];
  fiber.expirationTime = expirationTime;
  fiber.stateNode = {
    containerInfo: portal.containerInfo,
    pendingChildren: null, // Used by persistent updates
    implementation: portal.implementation
  };
  return fiber;
}

function createFiberRoot(containerInfo, hydrate) {
  // Cyclic construction. This cheats the type system right now because
  // stateNode is any.
  var uninitializedFiber = createHostRootFiber();
  var root = {
    current: uninitializedFiber,
    containerInfo: containerInfo,
    pendingChildren: null,
    remainingExpirationTime: NoWork,
    isReadyForCommit: false,
    finishedWork: null,
    context: null,
    pendingContext: null,
    hydrate: hydrate,
    nextScheduledRoot: null
  };
  uninitializedFiber.stateNode = root;
  return root;
}

var onCommitFiberRoot = null;
var onCommitFiberUnmount = null;
function catchErrors(fn) {
  return function (arg) {
    try {
      return fn(arg);
    } catch (err) {
      
    }
  };
}

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);
  }
}

// Callbacks are not validated until invocation


// Singly linked-list of updates. When an update is scheduled, it is added to
// the queue of the current fiber and the work-in-progress fiber. The two queues
// are separate but they share a persistent structure.
//
// During reconciliation, updates are removed from the work-in-progress fiber,
// but they remain on the current fiber. That ensures that if a work-in-progress
// is aborted, the aborted updates are recovered by cloning from current.
//
// The work-in-progress queue is always a subset of the current queue.
//
// When the tree is committed, the work-in-progress becomes the current.


function createUpdateQueue(baseState) {
  var queue = {
    baseState: baseState,
    expirationTime: NoWork,
    first: null,
    last: null,
    callbackList: null,
    hasForceUpdate: false,
    isInitialized: false
  };
  return queue;
}

function insertUpdateIntoQueue(queue, update) {
  // Append the update to the end of the list.
  if (queue.last === null) {
    // Queue is empty
    queue.first = queue.last = update;
  } else {
    queue.last.next = update;
    queue.last = update;
  }
  if (queue.expirationTime === NoWork || queue.expirationTime > update.expirationTime) {
    queue.expirationTime = update.expirationTime;
  }
}

function insertUpdateIntoFiber(fiber, update) {
  // We'll have at least one and at most two distinct update queues.
  var alternateFiber = fiber.alternate;
  var queue1 = fiber.updateQueue;
  if (queue1 === null) {
    // TODO: We don't know what the base state will be until we begin work.
    // It depends on which fiber is the next current. Initialize with an empty
    // base state, then set to the memoizedState when rendering. Not super
    // happy with this approach.
    queue1 = fiber.updateQueue = createUpdateQueue(null);
  }

  var queue2 = void 0;
  if (alternateFiber !== null) {
    queue2 = alternateFiber.updateQueue;
    if (queue2 === null) {
      queue2 = alternateFiber.updateQueue = createUpdateQueue(null);
    }
  } else {
    queue2 = null;
  }
  queue2 = queue2 !== queue1 ? queue2 : null;

  // Warn if an update is scheduled from inside an updater function.
  if (queue2 === null) {
    insertUpdateIntoQueue(queue1, update);
    return;
  }

  // If either queue is empty, we need to add to both queues.
  if (queue1.last === null || queue2.last === null) {
    insertUpdateIntoQueue(queue1, update);
    insertUpdateIntoQueue(queue2, update);
    return;
  }

  // If both lists are not empty, the last update is the same for both lists
  // because of structural sharing. So, we should only append to one of
  // the lists.
  insertUpdateIntoQueue(queue1, update);
  // But we still need to update the `last` pointer of queue2.
  queue2.last = update;
}

function getUpdateExpirationTime(fiber) {
  if (fiber.tag !== ClassComponent && fiber.tag !== HostRoot) {
    return NoWork;
  }
  var updateQueue = fiber.updateQueue;
  if (updateQueue === null) {
    return NoWork;
  }
  return updateQueue.expirationTime;
}

function getStateFromUpdate(update, instance, prevState, props) {
  var partialState = update.partialState;
  if (typeof partialState === 'function') {
    var updateFn = partialState;

    // Invoke setState callback an extra time to help detect side-effects.
    if (debugRenderPhaseSideEffects) {
      updateFn.call(instance, prevState, props);
    }

    return updateFn.call(instance, prevState, props);
  } else {
    return partialState;
  }
}

function processUpdateQueue(current, workInProgress, queue, instance, props, renderExpirationTime) {
  if (current !== null && current.updateQueue === queue) {
    // We need to create a work-in-progress queue, by cloning the current queue.
    var currentQueue = queue;
    queue = workInProgress.updateQueue = {
      baseState: currentQueue.baseState,
      expirationTime: currentQueue.expirationTime,
      first: currentQueue.first,
      last: currentQueue.last,
      isInitialized: currentQueue.isInitialized,
      // These fields are no longer valid because they were already committed.
      // Reset them.
      callbackList: null,
      hasForceUpdate: false
    };
  }

  queue.expirationTime = NoWork;

  // TODO: We don't know what the base state will be until we begin work.
  // It depends on which fiber is the next current. Initialize with an empty
  // base state, then set to the memoizedState when rendering. Not super
  // happy with this approach.
  var state = void 0;
  if (queue.isInitialized) {
    state = queue.baseState;
  } else {
    state = queue.baseState = workInProgress.memoizedState;
    queue.isInitialized = true;
  }
  var dontMutatePrevState = true;
  var update = queue.first;
  var didSkip = false;
  while (update !== null) {
    var updateExpirationTime = update.expirationTime;
    if (updateExpirationTime > renderExpirationTime) {
      // This update does not have sufficient priority. Skip it.
      var remainingExpirationTime = queue.expirationTime;
      if (remainingExpirationTime === NoWork || remainingExpirationTime > updateExpirationTime) {
        // Update the remaining expiration time.
        queue.expirationTime = updateExpirationTime;
      }
      if (!didSkip) {
        didSkip = true;
        queue.baseState = state;
      }
      // Continue to the next update.
      update = update.next;
      continue;
    }

    // This update does have sufficient priority.

    // If no previous updates were skipped, drop this update from the queue by
    // advancing the head of the list.
    if (!didSkip) {
      queue.first = update.next;
      if (queue.first === null) {
        queue.last = null;
      }
    }

    // Process the update
    var _partialState = void 0;
    if (update.isReplace) {
      state = getStateFromUpdate(update, instance, state, props);
      dontMutatePrevState = true;
    } else {
      _partialState = getStateFromUpdate(update, instance, state, props);
      if (_partialState) {
        if (dontMutatePrevState) {
          // $FlowFixMe: Idk how to type this properly.
          state = _assign({}, state, _partialState);
        } else {
          state = _assign(state, _partialState);
        }
        dontMutatePrevState = false;
      }
    }
    if (update.isForced) {
      queue.hasForceUpdate = true;
    }
    if (update.callback !== null) {
      // Append to list of callbacks.
      var _callbackList = queue.callbackList;
      if (_callbackList === null) {
        _callbackList = queue.callbackList = [];
      }
      _callbackList.push(update);
    }
    update = update.next;
  }

  if (queue.callbackList !== null) {
    workInProgress.effectTag |= Callback;
  } else if (queue.first === null && !queue.hasForceUpdate) {
    // The queue is empty. We can reset it.
    workInProgress.updateQueue = null;
  }

  if (!didSkip) {
    didSkip = true;
    queue.baseState = state;
  }

  return state;
}

function commitCallbacks(queue, context) {
  var callbackList = queue.callbackList;
  if (callbackList === null) {
    return;
  }
  // Set the list to null to make sure they don't get called more than once.
  queue.callbackList = null;
  for (var i = 0; i < callbackList.length; i++) {
    var update = callbackList[i];
    var _callback = update.callback;
    // This update might be processed again. Clear the callback so it's only
    // called once.
    update.callback = null;
    !(typeof _callback === 'function') ? reactProdInvariant('191', _callback) : void 0;
    _callback.call(context);
  }
}

var ReactFiberClassComponent = function (scheduleWork, computeExpirationForFiber, memoizeProps, memoizeState) {
  // Class component state updater
  var updater = {
    isMounted: isMounted,
    enqueueSetState: function (instance, partialState, callback) {
      var fiber = get(instance);
      callback = callback === undefined ? null : callback;
      var expirationTime = computeExpirationForFiber(fiber);
      var update = {
        expirationTime: expirationTime,
        partialState: partialState,
        callback: callback,
        isReplace: false,
        isForced: false,
        nextCallback: null,
        next: null
      };
      insertUpdateIntoFiber(fiber, update);
      scheduleWork(fiber, expirationTime);
    },
    enqueueReplaceState: function (instance, state, callback) {
      var fiber = get(instance);
      callback = callback === undefined ? null : callback;
      var expirationTime = computeExpirationForFiber(fiber);
      var update = {
        expirationTime: expirationTime,
        partialState: state,
        callback: callback,
        isReplace: true,
        isForced: false,
        nextCallback: null,
        next: null
      };
      insertUpdateIntoFiber(fiber, update);
      scheduleWork(fiber, expirationTime);
    },
    enqueueForceUpdate: function (instance, callback) {
      var fiber = get(instance);
      callback = callback === undefined ? null : callback;
      var expirationTime = computeExpirationForFiber(fiber);
      var update = {
        expirationTime: expirationTime,
        partialState: null,
        callback: callback,
        isReplace: false,
        isForced: true,
        nextCallback: null,
        next: null
      };
      insertUpdateIntoFiber(fiber, update);
      scheduleWork(fiber, expirationTime);
    }
  };

  function checkShouldComponentUpdate(workInProgress, oldProps, newProps, oldState, newState, newContext) {
    if (oldProps === null || workInProgress.updateQueue !== null && workInProgress.updateQueue.hasForceUpdate) {
      // If the workInProgress already has an Update effect, return true
      return true;
    }

    var instance = workInProgress.stateNode;
    var type = workInProgress.type;
    if (typeof instance.shouldComponentUpdate === 'function') {
      startPhaseTimer(workInProgress, 'shouldComponentUpdate');
      var shouldUpdate = instance.shouldComponentUpdate(newProps, newState, newContext);
      stopPhaseTimer();

      // Simulate an async bailout/interruption by invoking lifecycle twice.
      if (debugRenderPhaseSideEffects) {
        instance.shouldComponentUpdate(newProps, newState, newContext);
      }

      return shouldUpdate;
    }

    if (type.prototype && type.prototype.isPureReactComponent) {
      return !shallowEqual_1(oldProps, newProps) || !shallowEqual_1(oldState, newState);
    }

    return true;
  }

  function resetInputPointers(workInProgress, instance) {
    instance.props = workInProgress.memoizedProps;
    instance.state = workInProgress.memoizedState;
  }

  function adoptClassInstance(workInProgress, instance) {
    instance.updater = updater;
    workInProgress.stateNode = instance;
    // The instance needs access to the fiber so that it can schedule updates
    set(instance, workInProgress);
    
  }

  function constructClassInstance(workInProgress, props) {
    var ctor = workInProgress.type;
    var unmaskedContext = getUnmaskedContext(workInProgress);
    var needsContext = isContextConsumer(workInProgress);
    var context = needsContext ? getMaskedContext(workInProgress, unmaskedContext) : emptyObject_1;
    var instance = new ctor(props, context);
    adoptClassInstance(workInProgress, instance);

    // 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 (needsContext) {
      cacheContext(workInProgress, unmaskedContext, context);
    }

    return instance;
  }

  function callComponentWillMount(workInProgress, instance) {
    startPhaseTimer(workInProgress, 'componentWillMount');
    var oldState = instance.state;
    instance.componentWillMount();
    stopPhaseTimer();

    // Simulate an async bailout/interruption by invoking lifecycle twice.
    if (debugRenderPhaseSideEffects) {
      instance.componentWillMount();
    }

    if (oldState !== instance.state) {
      updater.enqueueReplaceState(instance, instance.state, null);
    }
  }

  function callComponentWillReceiveProps(workInProgress, instance, newProps, newContext) {
    startPhaseTimer(workInProgress, 'componentWillReceiveProps');
    var oldState = instance.state;
    instance.componentWillReceiveProps(newProps, newContext);
    stopPhaseTimer();

    // Simulate an async bailout/interruption by invoking lifecycle twice.
    if (debugRenderPhaseSideEffects) {
      instance.componentWillReceiveProps(newProps, newContext);
    }

    if (instance.state !== oldState) {
      updater.enqueueReplaceState(instance, instance.state, null);
    }
  }

  // Invokes the mount life-cycles on a previously never rendered instance.
  function mountClassInstance(workInProgress, renderExpirationTime) {
    var current = workInProgress.alternate;

    var instance = workInProgress.stateNode;
    var state = instance.state || null;

    var props = workInProgress.pendingProps;
    !props ? reactProdInvariant('158') : void 0;

    var unmaskedContext = getUnmaskedContext(workInProgress);

    instance.props = props;
    instance.state = workInProgress.memoizedState = state;
    instance.refs = emptyObject_1;
    instance.context = getMaskedContext(workInProgress, unmaskedContext);

    if (enableAsyncSubtreeAPI && workInProgress.type != null && workInProgress.type.prototype != null && workInProgress.type.prototype.unstable_isAsyncReactComponent === true) {
      workInProgress.internalContextTag |= AsyncUpdates;
    }

    if (typeof instance.componentWillMount === 'function') {
      callComponentWillMount(workInProgress, instance);
      // If we had additional state updates during this life-cycle, let's
      // process them now.
      var updateQueue = workInProgress.updateQueue;
      if (updateQueue !== null) {
        instance.state = processUpdateQueue(current, workInProgress, updateQueue, instance, props, renderExpirationTime);
      }
    }
    if (typeof instance.componentDidMount === 'function') {
      workInProgress.effectTag |= Update;
    }
  }

  // Called on a preexisting class instance. Returns false if a resumed render
  // could be reused.
  // function resumeMountClassInstance(
  //   workInProgress: Fiber,
  //   priorityLevel: PriorityLevel,
  // ): boolean {
  //   const instance = workInProgress.stateNode;
  //   resetInputPointers(workInProgress, instance);

  //   let newState = workInProgress.memoizedState;
  //   let newProps = workInProgress.pendingProps;
  //   if (!newProps) {
  //     // If there isn't any new props, then we'll reuse the memoized props.
  //     // This could be from already completed work.
  //     newProps = workInProgress.memoizedProps;
  //     invariant(
  //       newProps != null,
  //       'There should always be pending or memoized props. This error is ' +
  //         'likely caused by a bug in React. Please file an issue.',
  //     );
  //   }
  //   const newUnmaskedContext = getUnmaskedContext(workInProgress);
  //   const newContext = getMaskedContext(workInProgress, newUnmaskedContext);

  //   const oldContext = instance.context;
  //   const oldProps = workInProgress.memoizedProps;

  //   if (
  //     typeof instance.componentWillReceiveProps === 'function' &&
  //     (oldProps !== newProps || oldContext !== newContext)
  //   ) {
  //     callComponentWillReceiveProps(
  //       workInProgress,
  //       instance,
  //       newProps,
  //       newContext,
  //     );
  //   }

  //   // Process the update queue before calling shouldComponentUpdate
  //   const updateQueue = workInProgress.updateQueue;
  //   if (updateQueue !== null) {
  //     newState = processUpdateQueue(
  //       workInProgress,
  //       updateQueue,
  //       instance,
  //       newState,
  //       newProps,
  //       priorityLevel,
  //     );
  //   }

  //   // TODO: Should we deal with a setState that happened after the last
  //   // componentWillMount and before this componentWillMount? Probably
  //   // unsupported anyway.

  //   if (
  //     !checkShouldComponentUpdate(
  //       workInProgress,
  //       workInProgress.memoizedProps,
  //       newProps,
  //       workInProgress.memoizedState,
  //       newState,
  //       newContext,
  //     )
  //   ) {
  //     // Update the existing instance's state, props, and context pointers even
  //     // though we're bailing out.
  //     instance.props = newProps;
  //     instance.state = newState;
  //     instance.context = newContext;
  //     return false;
  //   }

  //   // Update the input pointers now so that they are correct when we call
  //   // componentWillMount
  //   instance.props = newProps;
  //   instance.state = newState;
  //   instance.context = newContext;

  //   if (typeof instance.componentWillMount === 'function') {
  //     callComponentWillMount(workInProgress, instance);
  //     // componentWillMount may have called setState. Process the update queue.
  //     const newUpdateQueue = workInProgress.updateQueue;
  //     if (newUpdateQueue !== null) {
  //       newState = processUpdateQueue(
  //         workInProgress,
  //         newUpdateQueue,
  //         instance,
  //         newState,
  //         newProps,
  //         priorityLevel,
  //       );
  //     }
  //   }

  //   if (typeof instance.componentDidMount === 'function') {
  //     workInProgress.effectTag |= Update;
  //   }

  //   instance.state = newState;

  //   return true;
  // }

  // Invokes the update life-cycles and returns false if it shouldn't rerender.
  function updateClassInstance(current, workInProgress, renderExpirationTime) {
    var instance = workInProgress.stateNode;
    resetInputPointers(workInProgress, instance);

    var oldProps = workInProgress.memoizedProps;
    var newProps = workInProgress.pendingProps;
    if (!newProps) {
      // If there aren't any new props, then we'll reuse the memoized props.
      // This could be from already completed work.
      newProps = oldProps;
      !(newProps != null) ? reactProdInvariant('159') : void 0;
    }
    var oldContext = instance.context;
    var newUnmaskedContext = getUnmaskedContext(workInProgress);
    var newContext = getMaskedContext(workInProgress, newUnmaskedContext);

    // 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.

    if (typeof instance.componentWillReceiveProps === 'function' && (oldProps !== newProps || oldContext !== newContext)) {
      callComponentWillReceiveProps(workInProgress, instance, newProps, newContext);
    }

    // Compute the next state using the memoized state and the update queue.
    var oldState = workInProgress.memoizedState;
    // TODO: Previous state can be null.
    var newState = void 0;
    if (workInProgress.updateQueue !== null) {
      newState = processUpdateQueue(current, workInProgress, workInProgress.updateQueue, instance, newProps, renderExpirationTime);
    } else {
      newState = oldState;
    }

    if (oldProps === newProps && oldState === newState && !hasContextChanged() && !(workInProgress.updateQueue !== null && workInProgress.updateQueue.hasForceUpdate)) {
      // 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;
        }
      }
      return false;
    }

    var shouldUpdate = checkShouldComponentUpdate(workInProgress, oldProps, newProps, oldState, newState, newContext);

    if (shouldUpdate) {
      if (typeof instance.componentWillUpdate === 'function') {
        startPhaseTimer(workInProgress, 'componentWillUpdate');
        instance.componentWillUpdate(newProps, newState, newContext);
        stopPhaseTimer();

        // Simulate an async bailout/interruption by invoking lifecycle twice.
        if (debugRenderPhaseSideEffects) {
          instance.componentWillUpdate(newProps, newState, newContext);
        }
      }
      if (typeof instance.componentDidUpdate === '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.componentDidUpdate === 'function') {
        if (oldProps !== current.memoizedProps || oldState !== current.memoizedState) {
          workInProgress.effectTag |= Update;
        }
      }

      // If shouldComponentUpdate returned false, we should still update the
      // memoized props/state to indicate that this work can be reused.
      memoizeProps(workInProgress, newProps);
      memoizeState(workInProgress, newState);
    }

    // Update the existing instance's state, props, and context pointers even
    // if shouldComponentUpdate returns false.
    instance.props = newProps;
    instance.state = newState;
    instance.context = newContext;

    return shouldUpdate;
  }

  return {
    adoptClassInstance: adoptClassInstance,
    constructClassInstance: constructClassInstance,
    mountClassInstance: mountClassInstance,
    // resumeMountClassInstance,
    updateClassInstance: updateClassInstance
  };
};

// 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_CALL_TYPE = hasSymbol ? Symbol['for']('react.call') : 0xeac8;
var REACT_RETURN_TYPE = hasSymbol ? Symbol['for']('react.return') : 0xeac9;
var REACT_PORTAL_TYPE = hasSymbol ? Symbol['for']('react.portal') : 0xeaca;
var REACT_FRAGMENT_TYPE = hasSymbol ? Symbol['for']('react.fragment') : 0xeacb;

var MAYBE_ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator;
var FAUX_ITERATOR_SYMBOL = '@@iterator';

function getIteratorFn(maybeIterable) {
  if (maybeIterable === null || typeof maybeIterable === 'undefined') {
    return null;
  }
  var maybeIterator = MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL] || maybeIterable[FAUX_ITERATOR_SYMBOL];
  if (typeof maybeIterator === 'function') {
    return maybeIterator;
  }
  return null;
}

var isArray$1 = Array.isArray;

function coerceRef(current, element) {
  var mixedRef = element.ref;
  if (mixedRef !== null && typeof mixedRef !== 'function') {
    if (element._owner) {
      var owner = element._owner;
      var inst = void 0;
      if (owner) {
        var ownerFiber = owner;
        !(ownerFiber.tag === ClassComponent) ? reactProdInvariant('110') : 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 !== null && current.ref !== null && current.ref._stringRef === stringRef) {
        return current.ref;
      }
      var ref = function (value) {
        var refs = inst.refs === emptyObject_1 ? inst.refs = {} : inst.refs;
        if (value === null) {
          delete refs[stringRef];
        } else {
          refs[stringRef] = value;
        }
      };
      ref._stringRef = stringRef;
      return ref;
    } else {
      !(typeof mixedRef === 'string') ? reactProdInvariant('148') : void 0;
      !element._owner ? reactProdInvariant('149', 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 = newFiber.alternate;
    if (current !== null) {
      var oldIndex = current.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, textContent, expirationTime) {
    if (current === null || current.tag !== HostText) {
      // Insert
      var created = createFiberFromText(textContent, returnFiber.internalContextTag, expirationTime);
      created['return'] = returnFiber;
      return created;
    } else {
      // Update
      var existing = useFiber(current, textContent, expirationTime);
      existing['return'] = returnFiber;
      return existing;
    }
  }

  function updateElement(returnFiber, current, element, expirationTime) {
    if (current !== null && current.type === element.type) {
      // Move based on index
      var existing = useFiber(current, element.props, expirationTime);
      existing.ref = coerceRef(current, element);
      existing['return'] = returnFiber;
      return existing;
    } else {
      // Insert
      var created = createFiberFromElement(element, returnFiber.internalContextTag, expirationTime);
      created.ref = coerceRef(current, element);
      created['return'] = returnFiber;
      return created;
    }
  }

  function updateCall(returnFiber, current, call, expirationTime) {
    // TODO: Should this also compare handler to determine whether to reuse?
    if (current === null || current.tag !== CallComponent) {
      // Insert
      var created = createFiberFromCall(call, returnFiber.internalContextTag, expirationTime);
      created['return'] = returnFiber;
      return created;
    } else {
      // Move based on index
      var existing = useFiber(current, call, expirationTime);
      existing['return'] = returnFiber;
      return existing;
    }
  }

  function updateReturn(returnFiber, current, returnNode, expirationTime) {
    if (current === null || current.tag !== ReturnComponent) {
      // Insert
      var created = createFiberFromReturn(returnNode, returnFiber.internalContextTag, expirationTime);
      created.type = returnNode.value;
      created['return'] = returnFiber;
      return created;
    } else {
      // Move based on index
      var existing = useFiber(current, null, expirationTime);
      existing.type = returnNode.value;
      existing['return'] = returnFiber;
      return existing;
    }
  }

  function updatePortal(returnFiber, current, portal, expirationTime) {
    if (current === null || current.tag !== HostPortal || current.stateNode.containerInfo !== portal.containerInfo || current.stateNode.implementation !== portal.implementation) {
      // Insert
      var created = createFiberFromPortal(portal, returnFiber.internalContextTag, expirationTime);
      created['return'] = returnFiber;
      return created;
    } else {
      // Update
      var existing = useFiber(current, portal.children || [], expirationTime);
      existing['return'] = returnFiber;
      return existing;
    }
  }

  function updateFragment(returnFiber, current, fragment, expirationTime, key) {
    if (current === null || current.tag !== Fragment) {
      // Insert
      var created = createFiberFromFragment(fragment, returnFiber.internalContextTag, expirationTime, key);
      created['return'] = returnFiber;
      return created;
    } else {
      // Update
      var existing = useFiber(current, 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.internalContextTag, expirationTime);
      created['return'] = returnFiber;
      return created;
    }

    if (typeof newChild === 'object' && newChild !== null) {
      switch (newChild.$$typeof) {
        case REACT_ELEMENT_TYPE:
          {
            if (newChild.type === REACT_FRAGMENT_TYPE) {
              var _created = createFiberFromFragment(newChild.props.children, returnFiber.internalContextTag, expirationTime, newChild.key);
              _created['return'] = returnFiber;
              return _created;
            } else {
              var _created2 = createFiberFromElement(newChild, returnFiber.internalContextTag, expirationTime);
              _created2.ref = coerceRef(null, newChild);
              _created2['return'] = returnFiber;
              return _created2;
            }
          }

        case REACT_CALL_TYPE:
          {
            var _created3 = createFiberFromCall(newChild, returnFiber.internalContextTag, expirationTime);
            _created3['return'] = returnFiber;
            return _created3;
          }

        case REACT_RETURN_TYPE:
          {
            var _created4 = createFiberFromReturn(newChild, returnFiber.internalContextTag, expirationTime);
            _created4.type = newChild.value;
            _created4['return'] = returnFiber;
            return _created4;
          }

        case REACT_PORTAL_TYPE:
          {
            var _created5 = createFiberFromPortal(newChild, returnFiber.internalContextTag, expirationTime);
            _created5['return'] = returnFiber;
            return _created5;
          }
      }

      if (isArray$1(newChild) || getIteratorFn(newChild)) {
        var _created6 = createFiberFromFragment(newChild, returnFiber.internalContextTag, expirationTime, null);
        _created6['return'] = returnFiber;
        return _created6;
      }

      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_CALL_TYPE:
          {
            if (newChild.key === key) {
              return updateCall(returnFiber, oldFiber, newChild, expirationTime);
            } else {
              return null;
            }
          }

        case REACT_RETURN_TYPE:
          {
            // Returns 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
            // yield.
            if (key === null) {
              return updateReturn(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$1(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_CALL_TYPE:
          {
            var _matchedFiber2 = existingChildren.get(newChild.key === null ? newIdx : newChild.key) || null;
            return updateCall(returnFiber, _matchedFiber2, newChild, expirationTime);
          }

        case REACT_RETURN_TYPE:
          {
            // Returns don't have keys, so we neither have to check the old nor
            // new node for the key. If both are returns, they match.
            var _matchedFiber3 = existingChildren.get(newIdx) || null;
            return updateReturn(returnFiber, _matchedFiber3, newChild, expirationTime);
          }

        case REACT_PORTAL_TYPE:
          {
            var _matchedFiber4 = existingChildren.get(newChild.key === null ? newIdx : newChild.key) || null;
            return updatePortal(returnFiber, _matchedFiber4, newChild, expirationTime);
          }
      }

      if (isArray$1(newChild) || getIteratorFn(newChild)) {
        var _matchedFiber5 = existingChildren.get(newIdx) || null;
        return updateFragment(returnFiber, _matchedFiber5, 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 boths 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.internalContextTag, 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.type === element.type) {
          deleteRemainingChildren(returnFiber, child.sibling);
          var existing = useFiber(child, element.type === REACT_FRAGMENT_TYPE ? element.props.children : element.props, expirationTime);
          existing.ref = coerceRef(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.internalContextTag, expirationTime, element.key);
      created['return'] = returnFiber;
      return created;
    } else {
      var _created7 = createFiberFromElement(element, returnFiber.internalContextTag, expirationTime);
      _created7.ref = coerceRef(currentFirstChild, element);
      _created7['return'] = returnFiber;
      return _created7;
    }
  }

  function reconcileSingleCall(returnFiber, currentFirstChild, call, expirationTime) {
    var key = call.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 === CallComponent) {
          deleteRemainingChildren(returnFiber, child.sibling);
          var existing = useFiber(child, call, expirationTime);
          existing['return'] = returnFiber;
          return existing;
        } else {
          deleteRemainingChildren(returnFiber, child);
          break;
        }
      } else {
        deleteChild(returnFiber, child);
      }
      child = child.sibling;
    }

    var created = createFiberFromCall(call, returnFiber.internalContextTag, expirationTime);
    created['return'] = returnFiber;
    return created;
  }

  function reconcileSingleReturn(returnFiber, currentFirstChild, returnNode, expirationTime) {
    // There's no need to check for keys on yields since they're stateless.
    var child = currentFirstChild;
    if (child !== null) {
      if (child.tag === ReturnComponent) {
        deleteRemainingChildren(returnFiber, child.sibling);
        var existing = useFiber(child, null, expirationTime);
        existing.type = returnNode.value;
        existing['return'] = returnFiber;
        return existing;
      } else {
        deleteRemainingChildren(returnFiber, child);
      }
    }

    var created = createFiberFromReturn(returnNode, returnFiber.internalContextTag, expirationTime);
    created.type = returnNode.value;
    created['return'] = returnFiber;
    return created;
  }

  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.internalContextTag, 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.
    if (typeof newChild === 'object' && newChild !== null && newChild.type === REACT_FRAGMENT_TYPE && newChild.key === null) {
      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_CALL_TYPE:
          return placeSingleChild(reconcileSingleCall(returnFiber, currentFirstChild, newChild, expirationTime));
        case REACT_RETURN_TYPE:
          return placeSingleChild(reconcileSingleReturn(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$1(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') {
      // 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 FunctionalComponent:
          {
            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, workInProgress) {
  !(current === null || workInProgress.child === current.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 ReactFiberBeginWork = function (config, hostContext, hydrationContext, scheduleWork, computeExpirationForFiber) {
  var shouldSetTextContent = config.shouldSetTextContent,
      useSyncScheduling = config.useSyncScheduling,
      shouldDeprioritizeSubtree = config.shouldDeprioritizeSubtree;
  var pushHostContext = hostContext.pushHostContext,
      pushHostContainer = hostContext.pushHostContainer;
  var enterHydrationState = hydrationContext.enterHydrationState,
      resetHydrationState = hydrationContext.resetHydrationState,
      tryToClaimNextHydratableInstance = hydrationContext.tryToClaimNextHydratableInstance;

  var _ReactFiberClassCompo = ReactFiberClassComponent(scheduleWork, computeExpirationForFiber, memoizeProps, memoizeState),
      adoptClassInstance = _ReactFiberClassCompo.adoptClassInstance,
      constructClassInstance = _ReactFiberClassCompo.constructClassInstance,
      mountClassInstance = _ReactFiberClassCompo.mountClassInstance,
      updateClassInstance = _ReactFiberClassCompo.updateClassInstance;

  // TODO: Remove this and use reconcileChildrenAtExpirationTime directly.


  function reconcileChildren(current, workInProgress, nextChildren) {
    reconcileChildrenAtExpirationTime(current, workInProgress, nextChildren, workInProgress.expirationTime);
  }

  function reconcileChildrenAtExpirationTime(current, workInProgress, nextChildren, renderExpirationTime) {
    if (current === 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.child, nextChildren, renderExpirationTime);
    }
  }

  function updateFragment(current, workInProgress) {
    var nextChildren = workInProgress.pendingProps;
    if (hasContextChanged()) {
      // Normally we can bail out on props equality but if context has changed
      // we don't do the bailout and we have to reuse existing props instead.
      if (nextChildren === null) {
        nextChildren = workInProgress.memoizedProps;
      }
    } else if (nextChildren === null || workInProgress.memoizedProps === nextChildren) {
      return bailoutOnAlreadyFinishedWork(current, workInProgress);
    }
    reconcileChildren(current, workInProgress, nextChildren);
    memoizeProps(workInProgress, nextChildren);
    return workInProgress.child;
  }

  function markRef(current, workInProgress) {
    var ref = workInProgress.ref;
    if (ref !== null && (!current || current.ref !== ref)) {
      // Schedule a Ref effect
      workInProgress.effectTag |= Ref;
    }
  }

  function updateFunctionalComponent(current, workInProgress) {
    var fn = workInProgress.type;
    var nextProps = workInProgress.pendingProps;

    var memoizedProps = workInProgress.memoizedProps;
    if (hasContextChanged()) {
      // Normally we can bail out on props equality but if context has changed
      // we don't do the bailout and we have to reuse existing props instead.
      if (nextProps === null) {
        nextProps = memoizedProps;
      }
    } else {
      if (nextProps === null || memoizedProps === nextProps) {
        return bailoutOnAlreadyFinishedWork(current, workInProgress);
      }
      // TODO: consider bringing fn.shouldComponentUpdate() back.
      // It used to be here.
    }

    var unmaskedContext = getUnmaskedContext(workInProgress);
    var context = getMaskedContext(workInProgress, unmaskedContext);

    var nextChildren;

    {
      nextChildren = fn(nextProps, context);
    }
    // React DevTools reads this flag.
    workInProgress.effectTag |= PerformedWork;
    reconcileChildren(current, workInProgress, nextChildren);
    memoizeProps(workInProgress, nextProps);
    return workInProgress.child;
  }

  function updateClassComponent(current, workInProgress, renderExpirationTime) {
    // 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 = pushContextProvider(workInProgress);

    var shouldUpdate = void 0;
    if (current === null) {
      if (!workInProgress.stateNode) {
        // In the initial pass we might need to construct the instance.
        constructClassInstance(workInProgress, workInProgress.pendingProps);
        mountClassInstance(workInProgress, renderExpirationTime);
        shouldUpdate = true;
      } else {
        reactProdInvariant('153');
        // In a resume, we'll already have an instance we can reuse.
        // shouldUpdate = resumeMountClassInstance(workInProgress, renderExpirationTime);
      }
    } else {
      shouldUpdate = updateClassInstance(current, workInProgress, renderExpirationTime);
    }
    return finishClassComponent(current, workInProgress, shouldUpdate, hasContext);
  }

  function finishClassComponent(current, workInProgress, shouldUpdate, hasContext) {
    // Refs should update even if shouldComponentUpdate returns false
    markRef(current, workInProgress);

    if (!shouldUpdate) {
      // Context providers should defer to sCU for rendering
      if (hasContext) {
        invalidateContextProvider(workInProgress, false);
      }

      return bailoutOnAlreadyFinishedWork(current, workInProgress);
    }

    var instance = workInProgress.stateNode;

    // Rerender
    ReactCurrentOwner.current = workInProgress;
    var nextChildren = void 0;
    {
      if (debugRenderPhaseSideEffects) {
        instance.render();
      }
      nextChildren = instance.render();
    }
    // React DevTools reads this flag.
    workInProgress.effectTag |= PerformedWork;
    reconcileChildren(current, workInProgress, nextChildren);
    // Memoize props and state using the values we just used to render.
    // TODO: Restructure so we never read values from the instance.
    memoizeState(workInProgress, instance.state);
    memoizeProps(workInProgress, instance.props);

    // The context might have changed so we need to recalculate it.
    if (hasContext) {
      invalidateContextProvider(workInProgress, 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, workInProgress, renderExpirationTime) {
    pushHostRootContext(workInProgress);
    var updateQueue = workInProgress.updateQueue;
    if (updateQueue !== null) {
      var prevState = workInProgress.memoizedState;
      var state = processUpdateQueue(current, workInProgress, updateQueue, null, null, renderExpirationTime);
      if (prevState === state) {
        // 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, workInProgress);
      }
      var element = state.element;
      var root = workInProgress.stateNode;
      if ((current === null || current.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, element, renderExpirationTime);
      } else {
        // Otherwise reset hydration state in case we aborted and resumed another
        // root.
        resetHydrationState();
        reconcileChildren(current, workInProgress, element);
      }
      memoizeState(workInProgress, state);
      return workInProgress.child;
    }
    resetHydrationState();
    // If there is no update queue, that's a bailout because the root has no props.
    return bailoutOnAlreadyFinishedWork(current, workInProgress);
  }

  function updateHostComponent(current, workInProgress, renderExpirationTime) {
    pushHostContext(workInProgress);

    if (current === null) {
      tryToClaimNextHydratableInstance(workInProgress);
    }

    var type = workInProgress.type;
    var memoizedProps = workInProgress.memoizedProps;
    var nextProps = workInProgress.pendingProps;
    if (nextProps === null) {
      nextProps = memoizedProps;
      !(nextProps !== null) ? reactProdInvariant('154') : void 0;
    }
    var prevProps = current !== null ? current.memoizedProps : null;

    if (hasContextChanged()) {
      // Normally we can bail out on props equality but if context has changed
      // we don't do the bailout and we have to reuse existing props instead.
    } else if (nextProps === null || memoizedProps === nextProps) {
      return bailoutOnAlreadyFinishedWork(current, workInProgress);
    }

    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 && 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, workInProgress);

    // Check the host config to see if the children are offscreen/hidden.
    if (renderExpirationTime !== Never && !useSyncScheduling && shouldDeprioritizeSubtree(type, nextProps)) {
      // Down-prioritize the children.
      workInProgress.expirationTime = Never;
      // Bailout and come back to this fiber later.
      return null;
    }

    reconcileChildren(current, workInProgress, nextChildren);
    memoizeProps(workInProgress, nextProps);
    return workInProgress.child;
  }

  function updateHostText(current, workInProgress) {
    if (current === null) {
      tryToClaimNextHydratableInstance(workInProgress);
    }
    var nextProps = workInProgress.pendingProps;
    if (nextProps === null) {
      nextProps = workInProgress.memoizedProps;
    }
    memoizeProps(workInProgress, nextProps);
    // Nothing to do here. This is terminal. We'll do the completion step
    // immediately after.
    return null;
  }

  function mountIndeterminateComponent(current, workInProgress, renderExpirationTime) {
    !(current === null) ? reactProdInvariant('155') : void 0;
    var fn = workInProgress.type;
    var props = workInProgress.pendingProps;
    var unmaskedContext = getUnmaskedContext(workInProgress);
    var context = getMaskedContext(workInProgress, unmaskedContext);

    var value;

    {
      value = fn(props, context);
    }
    // React DevTools reads this flag.
    workInProgress.effectTag |= PerformedWork;

    if (typeof value === 'object' && value !== null && typeof value.render === 'function') {
      // Proceed under the assumption that this is a class instance
      workInProgress.tag = ClassComponent;

      // 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 = pushContextProvider(workInProgress);
      adoptClassInstance(workInProgress, value);
      mountClassInstance(workInProgress, renderExpirationTime);
      return finishClassComponent(current, workInProgress, true, hasContext);
    } else {
      // Proceed under the assumption that this is a functional component
      workInProgress.tag = FunctionalComponent;
      reconcileChildren(current, workInProgress, value);
      memoizeProps(workInProgress, props);
      return workInProgress.child;
    }
  }

  function updateCallComponent(current, workInProgress, renderExpirationTime) {
    var nextCall = workInProgress.pendingProps;
    if (hasContextChanged()) {
      // Normally we can bail out on props equality but if context has changed
      // we don't do the bailout and we have to reuse existing props instead.
      if (nextCall === null) {
        nextCall = current && current.memoizedProps;
        !(nextCall !== null) ? reactProdInvariant('154') : void 0;
      }
    } else if (nextCall === null || workInProgress.memoizedProps === nextCall) {
      nextCall = workInProgress.memoizedProps;
      // TODO: When bailing out, we might need to return the stateNode instead
      // of the child. To check it for work.
      // return bailoutOnAlreadyFinishedWork(current, workInProgress);
    }

    var nextChildren = nextCall.children;

    // The following is a fork of reconcileChildrenAtExpirationTime but using
    // stateNode to store the child.
    if (current === null) {
      workInProgress.stateNode = mountChildFibers(workInProgress, workInProgress.stateNode, nextChildren, renderExpirationTime);
    } else {
      workInProgress.stateNode = reconcileChildFibers(workInProgress, workInProgress.stateNode, nextChildren, renderExpirationTime);
    }

    memoizeProps(workInProgress, nextCall);
    // This doesn't take arbitrary time so we could synchronously just begin
    // eagerly do the work of workInProgress.child as an optimization.
    return workInProgress.stateNode;
  }

  function updatePortalComponent(current, workInProgress, renderExpirationTime) {
    pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);
    var nextChildren = workInProgress.pendingProps;
    if (hasContextChanged()) {
      // Normally we can bail out on props equality but if context has changed
      // we don't do the bailout and we have to reuse existing props instead.
      if (nextChildren === null) {
        nextChildren = current && current.memoizedProps;
        !(nextChildren != null) ? reactProdInvariant('154') : void 0;
      }
    } else if (nextChildren === null || workInProgress.memoizedProps === nextChildren) {
      return bailoutOnAlreadyFinishedWork(current, workInProgress);
    }

    if (current === 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);
      memoizeProps(workInProgress, nextChildren);
    } else {
      reconcileChildren(current, workInProgress, nextChildren);
      memoizeProps(workInProgress, nextChildren);
    }
    return workInProgress.child;
  }

  /*
  function reuseChildrenEffects(returnFiber : Fiber, firstChild : Fiber) {
    let child = firstChild;
    do {
      // Ensure that the first and last effect of the parent corresponds
      // to the children's first and last effect.
      if (!returnFiber.firstEffect) {
        returnFiber.firstEffect = child.firstEffect;
      }
      if (child.lastEffect) {
        if (returnFiber.lastEffect) {
          returnFiber.lastEffect.nextEffect = child.firstEffect;
        }
        returnFiber.lastEffect = child.lastEffect;
      }
    } while (child = child.sibling);
  }
  */

  function bailoutOnAlreadyFinishedWork(current, workInProgress) {
    cancelWorkTimer(workInProgress);

    // TODO: We should ideally be able to bail out early if the children have no
    // more work to do. However, since we don't have a separation of this
    // Fiber's priority and its children yet - we don't know without doing lots
    // of the same work we do anyway. Once we have that separation we can just
    // bail out here if the children has no more work at this priority level.
    // if (workInProgress.priorityOfChildren <= priorityLevel) {
    //   // If there are side-effects in these children that have not yet been
    //   // committed we need to ensure that they get properly transferred up.
    //   if (current && current.child !== workInProgress.child) {
    //     reuseChildrenEffects(workInProgress, child);
    //   }
    //   return null;
    // }

    cloneChildFibers(current, workInProgress);
    return workInProgress.child;
  }

  function bailoutOnLowPriority(current, workInProgress) {
    cancelWorkTimer(workInProgress);

    // TODO: Handle HostComponent tags here as well and call pushHostContext()?
    // See PR 8590 discussion for context
    switch (workInProgress.tag) {
      case HostRoot:
        pushHostRootContext(workInProgress);
        break;
      case ClassComponent:
        pushContextProvider(workInProgress);
        break;
      case HostPortal:
        pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);
        break;
    }
    // TODO: What if this is currently in progress?
    // How can that happen? How is this not being cloned?
    return null;
  }

  // TODO: Delete memoizeProps/State and move to reconcile/bailout instead
  function memoizeProps(workInProgress, nextProps) {
    workInProgress.memoizedProps = nextProps;
  }

  function memoizeState(workInProgress, nextState) {
    workInProgress.memoizedState = nextState;
    // Don't reset the updateQueue, in case there are pending updates. Resetting
    // is handled by processUpdateQueue.
  }

  function beginWork(current, workInProgress, renderExpirationTime) {
    if (workInProgress.expirationTime === NoWork || workInProgress.expirationTime > renderExpirationTime) {
      return bailoutOnLowPriority(current, workInProgress);
    }

    switch (workInProgress.tag) {
      case IndeterminateComponent:
        return mountIndeterminateComponent(current, workInProgress, renderExpirationTime);
      case FunctionalComponent:
        return updateFunctionalComponent(current, workInProgress);
      case ClassComponent:
        return updateClassComponent(current, workInProgress, renderExpirationTime);
      case HostRoot:
        return updateHostRoot(current, workInProgress, renderExpirationTime);
      case HostComponent:
        return updateHostComponent(current, workInProgress, renderExpirationTime);
      case HostText:
        return updateHostText(current, workInProgress);
      case CallHandlerPhase:
        // This is a restart. Reset the tag to the initial phase.
        workInProgress.tag = CallComponent;
      // Intentionally fall through since this is now the same.
      case CallComponent:
        return updateCallComponent(current, workInProgress, renderExpirationTime);
      case ReturnComponent:
        // A return component is just a placeholder, we can just run through the
        // next one immediately.
        return null;
      case HostPortal:
        return updatePortalComponent(current, workInProgress, renderExpirationTime);
      case Fragment:
        return updateFragment(current, workInProgress);
      default:
        reactProdInvariant('156');
    }
  }

  function beginFailedWork(current, workInProgress, renderExpirationTime) {
    // Push context providers here to avoid a push/pop context mismatch.
    switch (workInProgress.tag) {
      case ClassComponent:
        pushContextProvider(workInProgress);
        break;
      case HostRoot:
        pushHostRootContext(workInProgress);
        break;
      default:
        reactProdInvariant('157');
    }

    // Add an error effect so we can handle the error during the commit phase
    workInProgress.effectTag |= Err;

    // This is a weird case where we do "resume" work — work that failed on
    // our first attempt. Because we no longer have a notion of "progressed
    // deletions," reset the child to the current child to make sure we delete
    // it again. TODO: Find a better way to handle this, perhaps during a more
    // general overhaul of error handling.
    if (current === null) {
      workInProgress.child = null;
    } else if (workInProgress.child !== current.child) {
      workInProgress.child = current.child;
    }

    if (workInProgress.expirationTime === NoWork || workInProgress.expirationTime > renderExpirationTime) {
      return bailoutOnLowPriority(current, workInProgress);
    }

    // If we don't bail out, we're going be recomputing our children so we need
    // to drop our effect list.
    workInProgress.firstEffect = null;
    workInProgress.lastEffect = null;

    // Unmount the current children as if the component rendered null
    var nextChildren = null;
    reconcileChildrenAtExpirationTime(current, workInProgress, nextChildren, renderExpirationTime);

    if (workInProgress.tag === ClassComponent) {
      var instance = workInProgress.stateNode;
      workInProgress.memoizedProps = instance.props;
      workInProgress.memoizedState = instance.state;
    }

    return workInProgress.child;
  }

  return {
    beginWork: beginWork,
    beginFailedWork: beginFailedWork
  };
};

var ReactFiberCompleteWork = function (config, hostContext, hydrationContext) {
  var createInstance = config.createInstance,
      createTextInstance = config.createTextInstance,
      appendInitialChild = config.appendInitialChild,
      finalizeInitialChildren = config.finalizeInitialChildren,
      prepareUpdate = config.prepareUpdate,
      mutation = config.mutation,
      persistence = config.persistence;
  var getRootHostContainer = hostContext.getRootHostContainer,
      popHostContext = hostContext.popHostContext,
      getHostContext = hostContext.getHostContext,
      popHostContainer = hostContext.popHostContainer;
  var prepareToHydrateHostInstance = hydrationContext.prepareToHydrateHostInstance,
      prepareToHydrateHostTextInstance = hydrationContext.prepareToHydrateHostTextInstance,
      popHydrationState = hydrationContext.popHydrationState;


  function markUpdate(workInProgress) {
    // Tag the fiber with an update effect. This turns a Placement into
    // an UpdateAndPlacement.
    workInProgress.effectTag |= Update;
  }

  function markRef(workInProgress) {
    workInProgress.effectTag |= Ref;
  }

  function appendAllReturns(returns, workInProgress) {
    var node = workInProgress.stateNode;
    if (node) {
      node['return'] = workInProgress;
    }
    while (node !== null) {
      if (node.tag === HostComponent || node.tag === HostText || node.tag === HostPortal) {
        reactProdInvariant('247');
      } else if (node.tag === ReturnComponent) {
        returns.push(node.type);
      } else if (node.child !== null) {
        node.child['return'] = node;
        node = node.child;
        continue;
      }
      while (node.sibling === null) {
        if (node['return'] === null || node['return'] === workInProgress) {
          return;
        }
        node = node['return'];
      }
      node.sibling['return'] = node['return'];
      node = node.sibling;
    }
  }

  function moveCallToHandlerPhase(current, workInProgress, renderExpirationTime) {
    var call = workInProgress.memoizedProps;
    !call ? reactProdInvariant('165') : void 0;

    // First step of the call has completed. Now we need to do the second.
    // TODO: It would be nice to have a multi stage call represented by a
    // single component, or at least tail call optimize nested ones. Currently
    // that requires additional fields that we don't want to add to the fiber.
    // So this requires nested handlers.
    // Note: This doesn't mutate the alternate node. I don't think it needs to
    // since this stage is reset for every pass.
    workInProgress.tag = CallHandlerPhase;

    // Build up the returns.
    // TODO: Compare this to a generator or opaque helpers like Children.
    var returns = [];
    appendAllReturns(returns, workInProgress);
    var fn = call.handler;
    var props = call.props;
    var nextChildren = fn(props, returns);

    var currentFirstChild = current !== null ? current.child : null;
    workInProgress.child = reconcileChildFibers(workInProgress, currentFirstChild, nextChildren, renderExpirationTime);
    return workInProgress.child;
  }

  function appendAllChildren(parent, workInProgress) {
    // 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;
    }
  }

  var updateHostContainer = void 0;
  var updateHostComponent = void 0;
  var updateHostText = void 0;
  if (mutation) {
    if (enableMutatingReconciler) {
      // Mutation mode
      updateHostContainer = function (workInProgress) {
        // Noop
      };
      updateHostComponent = function (current, workInProgress, updatePayload, type, oldProps, newProps, rootContainerInstance) {
        // 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 = 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 {
      reactProdInvariant('237');
    }
  } else if (persistence) {
    if (enablePersistentReconciler) {
      // Persistent host tree mode
      var cloneInstance = persistence.cloneInstance,
          createContainerChildSet = persistence.createContainerChildSet,
          appendChildToContainerChildSet = persistence.appendChildToContainerChildSet,
          finalizeContainerChildren = persistence.finalizeContainerChildren;

      // An unfortunate fork of appendAllChildren because we have two different parent types.

      var appendAllChildrenToContainer = function (containerChildSet, workInProgress) {
        // 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) {
            appendChildToContainerChildSet(containerChildSet, 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) {
        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 (finalizeContainerChildren(container, newChildSet)) {
            markUpdate(workInProgress);
          }
          portalOrRoot.pendingChildren = newChildSet;
          // If children might have changed, we have to add them all to the set.
          appendAllChildrenToContainer(newChildSet, workInProgress);
          // Schedule an update on the container to swap out the container.
          markUpdate(workInProgress);
        }
      };
      updateHostComponent = function (current, workInProgress, updatePayload, type, oldProps, newProps, rootContainerInstance) {
        // 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;
        var currentInstance = current.stateNode;
        if (childrenUnchanged && updatePayload === null) {
          // No changes, just reuse the existing instance.
          // Note that this might release a previous clone.
          workInProgress.stateNode = currentInstance;
        } else {
          var recyclableInstance = workInProgress.stateNode;
          var newInstance = cloneInstance(currentInstance, updatePayload, type, oldProps, newProps, workInProgress, childrenUnchanged, recyclableInstance);
          if (finalizeInitialChildren(newInstance, type, newProps, rootContainerInstance)) {
            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);
          }
        }
      };
      updateHostText = 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 {
      reactProdInvariant('235');
    }
  } else {
    if (enableNoopReconciler) {
      // No host operations
      updateHostContainer = function (workInProgress) {
        // Noop
      };
      updateHostComponent = function (current, workInProgress, updatePayload, type, oldProps, newProps, rootContainerInstance) {
        // Noop
      };
      updateHostText = function (current, workInProgress, oldText, newText) {
        // Noop
      };
    } else {
      reactProdInvariant('236');
    }
  }

  function completeWork(current, workInProgress, renderExpirationTime) {
    // Get the latest props.
    var newProps = workInProgress.pendingProps;
    if (newProps === null) {
      newProps = workInProgress.memoizedProps;
    } else if (workInProgress.expirationTime !== Never || renderExpirationTime === Never) {
      // Reset the pending props, unless this was a down-prioritization.
      workInProgress.pendingProps = null;
    }

    switch (workInProgress.tag) {
      case FunctionalComponent:
        return null;
      case ClassComponent:
        {
          // We are leaving this subtree, so pop context if any.
          popContextProvider(workInProgress);
          return null;
        }
      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);
          return null;
        }
      case HostComponent:
        {
          popHostContext(workInProgress);
          var rootContainerInstance = getRootHostContainer();
          var type = workInProgress.type;
          if (current !== null && workInProgress.stateNode != null) {
            // 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 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();
            var updatePayload = prepareUpdate(instance, type, oldProps, newProps, rootContainerInstance, currentHostContext);

            updateHostComponent(current, workInProgress, updatePayload, type, oldProps, newProps, rootContainerInstance);

            if (current.ref !== workInProgress.ref) {
              markRef(workInProgress);
            }
          } else {
            if (!newProps) {
              !(workInProgress.stateNode !== null) ? reactProdInvariant('166') : void 0;
              // This can happen when we abort work.
              return null;
            }

            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);

              // 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)) {
                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(workInProgress);
            }
          }
          return null;
        }
      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(current, workInProgress, oldText, newText);
          } else {
            if (typeof newText !== 'string') {
              !(workInProgress.stateNode !== null) ? reactProdInvariant('166') : void 0;
              // This can happen when we abort work.
              return null;
            }
            var _rootContainerInstance = getRootHostContainer();
            var _currentHostContext2 = getHostContext();
            var _wasHydrated = popHydrationState(workInProgress);
            if (_wasHydrated) {
              if (prepareToHydrateHostTextInstance(workInProgress)) {
                markUpdate(workInProgress);
              }
            } else {
              workInProgress.stateNode = createTextInstance(newText, _rootContainerInstance, _currentHostContext2, workInProgress);
            }
          }
          return null;
        }
      case CallComponent:
        return moveCallToHandlerPhase(current, workInProgress, renderExpirationTime);
      case CallHandlerPhase:
        // Reset the tag to now be a first phase call.
        workInProgress.tag = CallComponent;
        return null;
      case ReturnComponent:
        // Does nothing.
        return null;
      case Fragment:
        return null;
      case HostPortal:
        popHostContainer(workInProgress);
        updateHostContainer(workInProgress);
        return null;
      // Error cases
      case IndeterminateComponent:
        reactProdInvariant('167');
      // eslint-disable-next-line no-fallthrough
      default:
        reactProdInvariant('156');
    }
  }

  return {
    completeWork: completeWork
  };
};

var ReactFiberCommitWork = function (config, captureError) {
  var getPublicInstance = config.getPublicInstance,
      mutation = config.mutation,
      persistence = config.persistence;


  var callComponentWillUnmountWithTimer = function (current, instance) {
    startPhaseTimer(current, 'componentWillUnmount');
    instance.props = current.memoizedProps;
    instance.state = current.memoizedState;
    instance.componentWillUnmount();
    stopPhaseTimer();
  };

  // Capture errors so they don't interrupt unmounting.
  function safelyCallComponentWillUnmount(current, instance) {
    {
      try {
        callComponentWillUnmountWithTimer(current, instance);
      } catch (unmountError) {
        captureError(current, unmountError);
      }
    }
  }

  function safelyDetachRef(current) {
    var ref = current.ref;
    if (ref !== null) {
      {
        try {
          ref(null);
        } catch (refError) {
          captureError(current, refError);
        }
      }
    }
  }

  function commitLifeCycles(current, finishedWork) {
    switch (finishedWork.tag) {
      case ClassComponent:
        {
          var instance = finishedWork.stateNode;
          if (finishedWork.effectTag & Update) {
            if (current === null) {
              startPhaseTimer(finishedWork, 'componentDidMount');
              instance.props = finishedWork.memoizedProps;
              instance.state = finishedWork.memoizedState;
              instance.componentDidMount();
              stopPhaseTimer();
            } else {
              var prevProps = current.memoizedProps;
              var prevState = current.memoizedState;
              startPhaseTimer(finishedWork, 'componentDidUpdate');
              instance.props = finishedWork.memoizedProps;
              instance.state = finishedWork.memoizedState;
              instance.componentDidUpdate(prevProps, prevState);
              stopPhaseTimer();
            }
          }
          var updateQueue = finishedWork.updateQueue;
          if (updateQueue !== null) {
            commitCallbacks(updateQueue, instance);
          }
          return;
        }
      case HostRoot:
        {
          var _updateQueue = finishedWork.updateQueue;
          if (_updateQueue !== null) {
            var _instance = finishedWork.child !== null ? finishedWork.child.stateNode : null;
            commitCallbacks(_updateQueue, _instance);
          }
          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 === 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;
        }
      default:
        {
          reactProdInvariant('163');
        }
    }
  }

  function commitAttachRef(finishedWork) {
    var ref = finishedWork.ref;
    if (ref !== null) {
      var instance = finishedWork.stateNode;
      switch (finishedWork.tag) {
        case HostComponent:
          ref(getPublicInstance(instance));
          break;
        default:
          ref(instance);
      }
    }
  }

  function commitDetachRef(current) {
    var currentRef = current.ref;
    if (currentRef !== null) {
      currentRef(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) {
    if (typeof onCommitUnmount === 'function') {
      onCommitUnmount(current);
    }

    switch (current.tag) {
      case ClassComponent:
        {
          safelyDetachRef(current);
          var instance = current.stateNode;
          if (typeof instance.componentWillUnmount === 'function') {
            safelyCallComponentWillUnmount(current, instance);
          }
          return;
        }
      case HostComponent:
        {
          safelyDetachRef(current);
          return;
        }
      case CallComponent:
        {
          commitNestedUnmounts(current.stateNode);
          return;
        }
      case HostPortal:
        {
          // TODO: this is recursive.
          // We are also not using this parent because
          // the portal will get pushed immediately.
          if (enableMutatingReconciler && mutation) {
            unmountHostComponents(current);
          } else if (enablePersistentReconciler && persistence) {
            emptyPortalContainer(current);
          }
          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.
      !mutation || 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) {
    // 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['return'] = null;
    current.child = null;
    if (current.alternate) {
      current.alternate.child = null;
      current.alternate['return'] = null;
    }
  }

  if (!mutation) {
    var commitContainer = void 0;
    if (persistence) {
      var replaceContainerChildren = persistence.replaceContainerChildren,
          createContainerChildSet = persistence.createContainerChildSet;

      var emptyPortalContainer = function (current) {
        var portal = current.stateNode;
        var containerInfo = portal.containerInfo;

        var emptyChildSet = createContainerChildSet(containerInfo);
        replaceContainerChildren(containerInfo, emptyChildSet);
      };
      commitContainer = function (finishedWork) {
        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');
            }
        }
      };
    } else {
      commitContainer = function (finishedWork) {
        // Noop
      };
    }
    if (enablePersistentReconciler || enableNoopReconciler) {
      return {
        commitResetTextContent: function (finishedWork) {},
        commitPlacement: function (finishedWork) {},
        commitDeletion: function (current) {
          // Detach refs and call componentWillUnmount() on the whole subtree.
          commitNestedUnmounts(current);
          detachFiber(current);
        },
        commitWork: function (current, finishedWork) {
          commitContainer(finishedWork);
        },

        commitLifeCycles: commitLifeCycles,
        commitAttachRef: commitAttachRef,
        commitDetachRef: commitDetachRef
      };
    } else if (persistence) {
      reactProdInvariant('235');
    } else {
      reactProdInvariant('236');
    }
  }
  var commitMount = mutation.commitMount,
      commitUpdate = mutation.commitUpdate,
      resetTextContent = mutation.resetTextContent,
      commitTextUpdate = mutation.commitTextUpdate,
      appendChild = mutation.appendChild,
      appendChildToContainer = mutation.appendChildToContainer,
      insertBefore = mutation.insertBefore,
      insertInContainerBefore = mutation.insertInContainerBefore,
      removeChild = mutation.removeChild,
      removeChildFromContainer = mutation.removeChildFromContainer;


  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) {
        // 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) {
    // Recursively insert all host nodes into the parent.
    var parentFiber = getHostParentFiber(finishedWork);
    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 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) {
    // We only have the top Fiber that was inserted but we need recurse down its
    var node = current;

    // Each iteration, currentParent is populated with node's host parent if not
    // currentParentIsValid.
    var currentParentIsValid = false;
    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 (node.tag === HostPortal) {
        // 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;
        // Visit children because portals might contain host components.
        if (node.child !== null) {
          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) {
        return;
      }
      while (node.sibling === null) {
        if (node['return'] === null || node['return'] === current) {
          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) {
    // Recursively delete all host nodes from the parent.
    // Detach refs and call componentWillUnmount() on the whole subtree.
    unmountHostComponents(current);
    detachFiber(current);
  }

  function commitWork(current, finishedWork) {
    switch (finishedWork.tag) {
      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 !== null ? current.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 !== null ? current.memoizedProps : newText;
          commitTextUpdate(textInstance, oldText, newText);
          return;
        }
      case HostRoot:
        {
          return;
        }
      default:
        {
          reactProdInvariant('163');
        }
    }
  }

  function commitResetTextContent(current) {
    resetTextContent(current.stateNode);
  }

  if (enableMutatingReconciler) {
    return {
      commitResetTextContent: commitResetTextContent,
      commitPlacement: commitPlacement,
      commitDeletion: commitDeletion,
      commitWork: commitWork,
      commitLifeCycles: commitLifeCycles,
      commitAttachRef: commitAttachRef,
      commitDetachRef: commitDetachRef
    };
  } else {
    reactProdInvariant('237');
  }
};

var NO_CONTEXT = {};

var ReactFiberHostContext = function (config) {
  var getChildHostContext = config.getChildHostContext,
      getRootHostContext = config.getRootHostContext;


  var contextStackCursor = 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);

    var nextRootContext = getRootHostContext(nextRootInstance);

    // 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, nextRootContext, fiber);
  }

  function popHostContainer(fiber) {
    pop(contextStackCursor, fiber);
    pop(contextFiberStackCursor, fiber);
    pop(rootInstanceStackCursor, fiber);
  }

  function getHostContext() {
    var context = requiredContext(contextStackCursor.current);
    return context;
  }

  function pushHostContext(fiber) {
    var rootInstance = requiredContext(rootInstanceStackCursor.current);
    var context = requiredContext(contextStackCursor.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, 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, fiber);
    pop(contextFiberStackCursor, fiber);
  }

  function resetHostContainer() {
    contextStackCursor.current = NO_CONTEXT;
    rootInstanceStackCursor.current = NO_CONTEXT;
  }

  return {
    getHostContext: getHostContext,
    getRootHostContainer: getRootHostContainer,
    popHostContainer: popHostContainer,
    popHostContext: popHostContext,
    pushHostContainer: pushHostContainer,
    pushHostContext: pushHostContext,
    resetHostContainer: resetHostContainer
  };
};

var ReactFiberHydrationContext = function (config) {
  var shouldSetTextContent = config.shouldSetTextContent,
      hydration = config.hydration;

  // If this doesn't have hydration mode.

  if (!hydration) {
    return {
      enterHydrationState: function () {
        return false;
      },
      resetHydrationState: function () {},
      tryToClaimNextHydratableInstance: function () {},
      prepareToHydrateHostInstance: function () {
        reactProdInvariant('175');
      },
      prepareToHydrateHostTextInstance: function () {
        reactProdInvariant('176');
      },
      popHydrationState: function (fiber) {
        return false;
      }
    };
  }

  var canHydrateInstance = hydration.canHydrateInstance,
      canHydrateTextInstance = hydration.canHydrateTextInstance,
      getNextHydratableSibling = hydration.getNextHydratableSibling,
      getFirstHydratableChild = hydration.getFirstHydratableChild,
      hydrateInstance = hydration.hydrateInstance,
      hydrateTextInstance = hydration.hydrateTextInstance,
      didNotMatchHydratedContainerTextInstance = hydration.didNotMatchHydratedContainerTextInstance,
      didNotMatchHydratedTextInstance = hydration.didNotMatchHydratedTextInstance,
      didNotHydrateContainerInstance = hydration.didNotHydrateContainerInstance,
      didNotHydrateInstance = hydration.didNotHydrateInstance,
      didNotFindHydratableContainerInstance = hydration.didNotFindHydratableContainerInstance,
      didNotFindHydratableContainerTextInstance = hydration.didNotFindHydratableContainerTextInstance,
      didNotFindHydratableInstance = hydration.didNotFindHydratableInstance,
      didNotFindHydratableTextInstance = hydration.didNotFindHydratableTextInstance;

  // 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) {
    var parentInstance = fiber.stateNode.containerInfo;
    nextHydratableInstance = getFirstHydratableChild(parentInstance);
    hydrationParentFiber = 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;
        }
      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;
    }
    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(nextInstance);
      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, nextHydratableInstance);
    }
    hydrationParentFiber = fiber;
    nextHydratableInstance = getFirstHydratableChild(nextInstance);
  }

  function prepareToHydrateHostInstance(fiber, rootContainerInstance, hostContext) {
    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) {
    var textInstance = fiber.stateNode;
    var textContent = fiber.memoizedProps;
    var shouldUpdate = hydrateTextInstance(textInstance, textContent, fiber);
    return shouldUpdate;
  }

  function popToNextHostParent(fiber) {
    var parent = fiber['return'];
    while (parent !== null && parent.tag !== HostComponent && parent.tag !== HostRoot) {
      parent = parent['return'];
    }
    hydrationParentFiber = parent;
  }

  function popHydrationState(fiber) {
    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() {
    hydrationParentFiber = null;
    nextHydratableInstance = null;
    isHydrating = false;
  }

  return {
    enterHydrationState: enterHydrationState,
    resetHydrationState: resetHydrationState,
    tryToClaimNextHydratableInstance: tryToClaimNextHydratableInstance,
    prepareToHydrateHostInstance: prepareToHydrateHostInstance,
    prepareToHydrateHostTextInstance: prepareToHydrateHostTextInstance,
    popHydrationState: popHydrationState
  };
};

// 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 defaultShowDialog = function (capturedError) {
  return true;
};

var showDialog = defaultShowDialog;

function logCapturedError(capturedError) {
  var logError = showDialog(capturedError);

  // Allow injected showDialog() 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 suppressLogging = error && error.suppressReactErrorLogging;
  if (suppressLogging) {
    return;
  }

  {
    // 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 ReactFiberScheduler = function (config) {
  var hostContext = ReactFiberHostContext(config);
  var hydrationContext = ReactFiberHydrationContext(config);
  var popHostContainer = hostContext.popHostContainer,
      popHostContext = hostContext.popHostContext,
      resetHostContainer = hostContext.resetHostContainer;

  var _ReactFiberBeginWork = ReactFiberBeginWork(config, hostContext, hydrationContext, scheduleWork, computeExpirationForFiber),
      beginWork = _ReactFiberBeginWork.beginWork,
      beginFailedWork = _ReactFiberBeginWork.beginFailedWork;

  var _ReactFiberCompleteWo = ReactFiberCompleteWork(config, hostContext, hydrationContext),
      completeWork = _ReactFiberCompleteWo.completeWork;

  var _ReactFiberCommitWork = ReactFiberCommitWork(config, captureError),
      commitResetTextContent = _ReactFiberCommitWork.commitResetTextContent,
      commitPlacement = _ReactFiberCommitWork.commitPlacement,
      commitDeletion = _ReactFiberCommitWork.commitDeletion,
      commitWork = _ReactFiberCommitWork.commitWork,
      commitLifeCycles = _ReactFiberCommitWork.commitLifeCycles,
      commitAttachRef = _ReactFiberCommitWork.commitAttachRef,
      commitDetachRef = _ReactFiberCommitWork.commitDetachRef;

  var now = config.now,
      scheduleDeferredCallback = config.scheduleDeferredCallback,
      cancelDeferredCallback = config.cancelDeferredCallback,
      useSyncScheduling = config.useSyncScheduling,
      prepareForCommit = config.prepareForCommit,
      resetAfterCommit = config.resetAfterCommit;

  // Represents the current time in ms.

  var startTime = now();
  var mostRecentCurrentTime = msToExpirationTime(0);

  // Represents the expiration time that incoming updates should use. (If this
  // is NoWork, use the default strategy: async updates in async mode, sync
  // updates in sync mode.)
  var expirationContext = NoWork;

  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;

  // The next fiber with an effect that we're currently committing.
  var nextEffect = null;

  // Keep track of which fibers have captured an error that need to be handled.
  // Work is removed from this collection after componentDidCatch is called.
  var capturedErrors = null;
  // Keep track of which fibers have failed during the current batch of work.
  // This is a different set than capturedErrors, because it is not reset until
  // the end of the batch. This is needed to propagate errors correctly if a
  // subtree fails more than once.
  var failedBoundaries = null;
  // Error boundaries that captured an error during the current commit.
  var commitPhaseBoundaries = null;
  var firstUncaughtError = null;
  var didFatal = false;

  var isCommitting = false;
  var isUnmounting = false;

  // Used for performance tracking.
  var interruptedBy = null;

  function resetContextStack() {
    // Reset the stack
    reset$1();
    // Reset the cursors
    resetContext();
    resetHostContainer();
  }

  function commitAllHostEffects() {
    while (nextEffect !== null) {
      recordEffect();

      var effectTag = nextEffect.effectTag;
      if (effectTag & ContentReset) {
        commitResetTextContent(nextEffect);
      }

      if (effectTag & Ref) {
        var current = nextEffect.alternate;
        if (current !== null) {
          commitDetachRef(current);
        }
      }

      // 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 & ~(Callback | Err | ContentReset | Ref | PerformedWork);
      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:
          {
            isUnmounting = true;
            commitDeletion(nextEffect);
            isUnmounting = false;
            break;
          }
      }
      nextEffect = nextEffect.nextEffect;
    }

    
  }

  function commitAllLifeCycles() {
    while (nextEffect !== null) {
      var effectTag = nextEffect.effectTag;

      if (effectTag & (Update | Callback)) {
        recordEffect();
        var current = nextEffect.alternate;
        commitLifeCycles(current, nextEffect);
      }

      if (effectTag & Ref) {
        recordEffect();
        commitAttachRef(nextEffect);
      }

      if (effectTag & Err) {
        recordEffect();
        commitErrorHandling(nextEffect);
      }

      var next = nextEffect.nextEffect;
      // Ensure that we clean these up so that we don't accidentally keep them.
      // I'm not actually sure this matters because we can't reset firstEffect
      // and lastEffect since they're on every node, not just the effectful
      // ones. So we have to clean everything as we reuse nodes anyway.
      nextEffect.nextEffect = null;
      // Ensure that we reset the effectTag here so that we can rely on effect
      // tags to reason about the current life-cycle.
      nextEffect = next;
    }
  }

  function commitRoot(finishedWork) {
    // We keep track of this so that captureError can collect any boundaries
    // that capture an error during the commit phase. The reason these aren't
    // local to this function is because errors that occur during cWU are
    // captured elsewhere, to prevent the unmount from being interrupted.
    isWorking = true;
    isCommitting = true;
    startCommitTimer();

    var root = finishedWork.stateNode;
    !(root.current !== finishedWork) ? reactProdInvariant('177') : void 0;
    root.isReadyForCommit = false;

    // Reset this to null before calling lifecycles
    ReactCurrentOwner.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();

    // 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;
        captureError(nextEffect, _error);
        // Clean-up
        if (nextEffect !== null) {
          nextEffect = nextEffect.nextEffect;
        }
      }
    }
    stopCommitHostEffectsTimer();

    resetAfterCommit();

    // 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 _didError = false;
      var _error2 = void 0;
      {
        try {
          commitAllLifeCycles();
        } catch (e) {
          _didError = true;
          _error2 = e;
        }
      }
      if (_didError) {
        !(nextEffect !== null) ? reactProdInvariant('178') : void 0;
        captureError(nextEffect, _error2);
        if (nextEffect !== null) {
          nextEffect = nextEffect.nextEffect;
        }
      }
    }

    isCommitting = false;
    isWorking = false;
    stopCommitLifeCyclesTimer();
    stopCommitTimer();
    if (typeof onCommitRoot === 'function') {
      onCommitRoot(finishedWork.stateNode);
    }
    if (commitPhaseBoundaries) {
      commitPhaseBoundaries.forEach(scheduleErrorRecovery);
      commitPhaseBoundaries = null;
    }

    if (firstUncaughtError !== null) {
      var _error3 = firstUncaughtError;
      firstUncaughtError = null;
      onUncaughtError(_error3);
    }

    var remainingTime = root.current.expirationTime;

    if (remainingTime === NoWork) {
      capturedErrors = null;
      failedBoundaries = null;
    }

    return remainingTime;
  }

  function resetExpirationTime(workInProgress, renderTime) {
    if (renderTime !== Never && workInProgress.expirationTime === Never) {
      // The children of this component are hidden. Don't bubble their
      // expiration times.
      return;
    }

    // Check for pending updates.
    var newExpirationTime = getUpdateExpirationTime(workInProgress);

    // TODO: Calls need to visit stateNode

    // Bubble up the earliest expiration time.
    var child = workInProgress.child;
    while (child !== null) {
      if (child.expirationTime !== NoWork && (newExpirationTime === NoWork || newExpirationTime > child.expirationTime)) {
        newExpirationTime = child.expirationTime;
      }
      child = child.sibling;
    }
    workInProgress.expirationTime = newExpirationTime;
  }

  function completeUnitOfWork(workInProgress) {
    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 = workInProgress.alternate;
      var next = completeWork(current, workInProgress, nextRenderExpirationTime);
      var returnFiber = workInProgress['return'];
      var siblingFiber = workInProgress.sibling;

      resetExpirationTime(workInProgress, nextRenderExpirationTime);

      if (next !== null) {
        stopWorkTimer(workInProgress);
        return next;
      }

      if (returnFiber !== null) {
        // 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;
        }
      }

      stopWorkTimer(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.
        var root = workInProgress.stateNode;
        root.isReadyForCommit = true;
        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 = workInProgress.alternate;

    // See if beginning this work spawns more work.
    startWorkTimer(workInProgress);
    var next = beginWork(current, workInProgress, nextRenderExpirationTime);
    if (next === null) {
      // If this doesn't spawn new work, complete the current work.
      next = completeUnitOfWork(workInProgress);
    }

    ReactCurrentOwner.current = null;

    return next;
  }

  function performFailedUnitOfWork(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 = workInProgress.alternate;

    // See if beginning this work spawns more work.
    startWorkTimer(workInProgress);
    var next = beginFailedWork(current, workInProgress, nextRenderExpirationTime);
    if (next === null) {
      // If this doesn't spawn new work, complete the current work.
      next = completeUnitOfWork(workInProgress);
    }

    ReactCurrentOwner.current = null;

    return next;
  }

  function workLoop(expirationTime) {
    if (capturedErrors !== null) {
      // If there are unhandled errors, switch to the slow work loop.
      // TODO: How to avoid this check in the fast path? Maybe the renderer
      // could keep track of which roots have unhandled errors and call a
      // forked version of renderRoot.
      slowWorkLoopThatChecksForFailedWork(expirationTime);
      return;
    }
    if (nextRenderExpirationTime === NoWork || nextRenderExpirationTime > expirationTime) {
      return;
    }

    if (nextRenderExpirationTime <= mostRecentCurrentTime) {
      // Flush all expired work.
      while (nextUnitOfWork !== null) {
        nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
      }
    } else {
      // Flush asynchronous work until the deadline runs out of time.
      while (nextUnitOfWork !== null && !shouldYield()) {
        nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
      }
    }
  }

  function slowWorkLoopThatChecksForFailedWork(expirationTime) {
    if (nextRenderExpirationTime === NoWork || nextRenderExpirationTime > expirationTime) {
      return;
    }

    if (nextRenderExpirationTime <= mostRecentCurrentTime) {
      // Flush all expired work.
      while (nextUnitOfWork !== null) {
        if (hasCapturedError(nextUnitOfWork)) {
          // Use a forked version of performUnitOfWork
          nextUnitOfWork = performFailedUnitOfWork(nextUnitOfWork);
        } else {
          nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
        }
      }
    } else {
      // Flush asynchronous work until the deadline runs out of time.
      while (nextUnitOfWork !== null && !shouldYield()) {
        if (hasCapturedError(nextUnitOfWork)) {
          // Use a forked version of performUnitOfWork
          nextUnitOfWork = performFailedUnitOfWork(nextUnitOfWork);
        } else {
          nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
        }
      }
    }
  }

  function renderRootCatchBlock(root, failedWork, boundary, expirationTime) {
    // We're going to restart the error boundary that captured the error.
    // Conceptually, we're unwinding the stack. We need to unwind the
    // context stack, too.
    unwindContexts(failedWork, boundary);

    // Restart the error boundary using a forked version of
    // performUnitOfWork that deletes the boundary's children. The entire
    // failed subree will be unmounted. During the commit phase, a special
    // lifecycle method is called on the error boundary, which triggers
    // a re-render.
    nextUnitOfWork = performFailedUnitOfWork(boundary);

    // Continue working.
    workLoop(expirationTime);
  }

  function renderRoot(root, expirationTime) {
    !!isWorking ? reactProdInvariant('243') : void 0;
    isWorking = true;

    // We're about to mutate the work-in-progress tree. If the root was pending
    // commit, it no longer is: we'll need to complete it again.
    root.isReadyForCommit = false;

    // Check if we're starting from a fresh stack, or if we're resuming from
    // previously yielded work.
    if (root !== nextRoot || expirationTime !== nextRenderExpirationTime || nextUnitOfWork === null) {
      // Reset the stack and start working from the root.
      resetContextStack();
      nextRoot = root;
      nextRenderExpirationTime = expirationTime;
      nextUnitOfWork = createWorkInProgress(nextRoot.current, null, expirationTime);
    }

    startWorkLoopTimer(nextUnitOfWork);

    var didError = false;
    var error = null;
    {
      try {
        workLoop(expirationTime);
      } catch (e) {
        didError = true;
        error = e;
      }
    }

    // An error was thrown during the render phase.
    while (didError) {
      if (didFatal) {
        // This was a fatal error. Don't attempt to recover from it.
        firstUncaughtError = error;
        break;
      }

      var failedWork = nextUnitOfWork;
      if (failedWork === null) {
        // An error was thrown but there's no current unit of work. This can
        // happen during the commit phase if there's a bug in the renderer.
        didFatal = true;
        continue;
      }

      // "Capture" the error by finding the nearest boundary. If there is no
      // error boundary, we use the root.
      var boundary = captureError(failedWork, error);
      !(boundary !== null) ? reactProdInvariant('183') : void 0;

      if (didFatal) {
        // The error we just captured was a fatal error. This happens
        // when the error propagates to the root more than once.
        continue;
      }

      didError = false;
      error = null;
      {
        try {
          renderRootCatchBlock(root, failedWork, boundary, expirationTime);
          error = null;
        } catch (e) {
          didError = true;
          error = e;
          continue;
        }
      }
      // We're finished working. Exit the error loop.
      break;
    }

    var uncaughtError = firstUncaughtError;

    // We're done performing work. Time to clean up.
    stopWorkLoopTimer(interruptedBy);
    interruptedBy = null;
    isWorking = false;
    didFatal = false;
    firstUncaughtError = null;

    if (uncaughtError !== null) {
      onUncaughtError(uncaughtError);
    }

    return root.isReadyForCommit ? root.current.alternate : null;
  }

  // Returns the boundary that captured the error, or null if the error is ignored
  function captureError(failedWork, error) {
    // It is no longer valid because we exited the user code.
    ReactCurrentOwner.current = null;
    var boundary = null;

    // Passed to logCapturedError()
    var errorBoundaryFound = false;
    var willRetry = false;
    var errorBoundaryName = null;

    // Host containers are a special case. If the failed work itself is a host
    // container, then it acts as its own boundary. In all other cases, we
    // ignore the work itself and only search through the parents.
    if (failedWork.tag === HostRoot) {
      boundary = failedWork;

      if (isFailedBoundary(failedWork)) {
        // If this root already failed, there must have been an error when
        // attempting to unmount it. This is a worst-case scenario and
        // should only be possible if there's a bug in the renderer.
        didFatal = true;
      }
    } else {
      var node = failedWork['return'];
      while (node !== null && boundary === null) {
        if (node.tag === ClassComponent) {
          var instance = node.stateNode;
          if (typeof instance.componentDidCatch === 'function') {
            errorBoundaryFound = true;
            errorBoundaryName = getComponentName(node);

            // Found an error boundary!
            boundary = node;
            willRetry = true;
          }
        } else if (node.tag === HostRoot) {
          // Treat the root like a no-op error boundary
          boundary = node;
        }

        if (isFailedBoundary(node)) {
          // This boundary is already in a failed state.

          // If we're currently unmounting, that means this error was
          // thrown while unmounting a failed subtree. We should ignore
          // the error.
          if (isUnmounting) {
            return null;
          }

          // If we're in the commit phase, we should check to see if
          // this boundary already captured an error during this commit.
          // This case exists because multiple errors can be thrown during
          // a single commit without interruption.
          if (commitPhaseBoundaries !== null && (commitPhaseBoundaries.has(node) || node.alternate !== null && commitPhaseBoundaries.has(node.alternate))) {
            // If so, we should ignore this error.
            return null;
          }

          // The error should propagate to the next boundary -— we keep looking.
          boundary = null;
          willRetry = false;
        }

        node = node['return'];
      }
    }

    if (boundary !== null) {
      // Add to the collection of failed boundaries. This lets us know that
      // subsequent errors in this subtree should propagate to the next boundary.
      if (failedBoundaries === null) {
        failedBoundaries = new Set();
      }
      failedBoundaries.add(boundary);

      // This method is unsafe outside of the begin and complete phases.
      // We might be in the commit phase when an error is captured.
      // The risk is that the return path from this Fiber may not be accurate.
      // That risk is acceptable given the benefit of providing users more context.
      var _componentStack = getStackAddendumByWorkInProgressFiber(failedWork);
      var _componentName = getComponentName(failedWork);

      // Add to the collection of captured errors. This is stored as a global
      // map of errors and their component stack location keyed by the boundaries
      // that capture them. We mostly use this Map as a Set; it's a Map only to
      // avoid adding a field to Fiber to store the error.
      if (capturedErrors === null) {
        capturedErrors = new Map();
      }

      var capturedError = {
        componentName: _componentName,
        componentStack: _componentStack,
        error: error,
        errorBoundary: errorBoundaryFound ? boundary.stateNode : null,
        errorBoundaryFound: errorBoundaryFound,
        errorBoundaryName: errorBoundaryName,
        willRetry: willRetry
      };

      capturedErrors.set(boundary, capturedError);

      try {
        logCapturedError(capturedError);
      } catch (e) {
        // Prevent cycle if logCapturedError() throws.
        // A cycle may still occur if logCapturedError renders a component that throws.
        var suppressLogging = e && e.suppressReactErrorLogging;
        if (!suppressLogging) {
          console.error(e);
        }
      }

      // If we're in the commit phase, defer scheduling an update on the
      // boundary until after the commit is complete
      if (isCommitting) {
        if (commitPhaseBoundaries === null) {
          commitPhaseBoundaries = new Set();
        }
        commitPhaseBoundaries.add(boundary);
      } else {
        // Otherwise, schedule an update now.
        // TODO: Is this actually necessary during the render phase? Is it
        // possible to unwind and continue rendering at the same priority,
        // without corrupting internal state?
        scheduleErrorRecovery(boundary);
      }
      return boundary;
    } else if (firstUncaughtError === null) {
      // If no boundary is found, we'll need to throw the error
      firstUncaughtError = error;
    }
    return null;
  }

  function hasCapturedError(fiber) {
    // TODO: capturedErrors should store the boundary instance, to avoid needing
    // to check the alternate.
    return capturedErrors !== null && (capturedErrors.has(fiber) || fiber.alternate !== null && capturedErrors.has(fiber.alternate));
  }

  function isFailedBoundary(fiber) {
    // TODO: failedBoundaries should store the boundary instance, to avoid
    // needing to check the alternate.
    return failedBoundaries !== null && (failedBoundaries.has(fiber) || fiber.alternate !== null && failedBoundaries.has(fiber.alternate));
  }

  function commitErrorHandling(effectfulFiber) {
    var capturedError = void 0;
    if (capturedErrors !== null) {
      capturedError = capturedErrors.get(effectfulFiber);
      capturedErrors['delete'](effectfulFiber);
      if (capturedError == null) {
        if (effectfulFiber.alternate !== null) {
          effectfulFiber = effectfulFiber.alternate;
          capturedError = capturedErrors.get(effectfulFiber);
          capturedErrors['delete'](effectfulFiber);
        }
      }
    }

    !(capturedError != null) ? reactProdInvariant('184') : void 0;

    switch (effectfulFiber.tag) {
      case ClassComponent:
        var instance = effectfulFiber.stateNode;

        var info = {
          componentStack: capturedError.componentStack
        };

        // Allow the boundary to handle the error, usually by scheduling
        // an update to itself
        instance.componentDidCatch(capturedError.error, info);
        return;
      case HostRoot:
        if (firstUncaughtError === null) {
          firstUncaughtError = capturedError.error;
        }
        return;
      default:
        reactProdInvariant('157');
    }
  }

  function unwindContexts(from, to) {
    var node = from;
    while (node !== null) {
      switch (node.tag) {
        case ClassComponent:
          popContextProvider(node);
          break;
        case HostComponent:
          popHostContext(node);
          break;
        case HostRoot:
          popHostContainer(node);
          break;
        case HostPortal:
          popHostContainer(node);
          break;
      }
      if (node === to || node.alternate === to) {
        stopFailedWorkTimer(node);
        break;
      } else {
        stopWorkTimer(node);
      }
      node = node['return'];
    }
  }

  function computeAsyncExpiration() {
    // Given the current clock time, returns an expiration time. We use rounding
    // to batch like updates together.
    // Should complete within ~1000ms. 1200ms max.
    var currentTime = recalculateCurrentTime();
    var expirationMs = 1000;
    var bucketSizeMs = 200;
    return computeExpirationBucket(currentTime, expirationMs, bucketSizeMs);
  }

  function computeExpirationForFiber(fiber) {
    var expirationTime = void 0;
    if (expirationContext !== NoWork) {
      // An explicit expiration context was set;
      expirationTime = expirationContext;
    } else if (isWorking) {
      if (isCommitting) {
        // Updates that occur during the commit phase should have sync priority
        // by default.
        expirationTime = Sync;
      } else {
        // Updates during the render phase should expire at the same time as
        // the work that is being rendered.
        expirationTime = nextRenderExpirationTime;
      }
    } else {
      // No explicit expiration context was set, and we're not currently
      // performing work. Calculate a new expiration time.
      if (useSyncScheduling && !(fiber.internalContextTag & AsyncUpdates)) {
        // This is a sync update
        expirationTime = Sync;
      } else {
        // This is an async update
        expirationTime = computeAsyncExpiration();
      }
    }
    return expirationTime;
  }

  function scheduleWork(fiber, expirationTime) {
    return scheduleWorkImpl(fiber, expirationTime, false);
  }

  function checkRootNeedsClearing(root, fiber, expirationTime) {
    if (!isWorking && root === nextRoot && expirationTime < nextRenderExpirationTime) {
      // Restart the root from the top.
      if (nextUnitOfWork !== null) {
        // This is an interruption. (Used for performance tracking.)
        interruptedBy = fiber;
      }
      nextRoot = null;
      nextUnitOfWork = null;
      nextRenderExpirationTime = NoWork;
    }
  }

  function scheduleWorkImpl(fiber, expirationTime, isErrorRecovery) {
    recordScheduleUpdate();

    var node = fiber;
    while (node !== null) {
      // Walk the parent path to the root and update each node's
      // expiration time.
      if (node.expirationTime === NoWork || node.expirationTime > expirationTime) {
        node.expirationTime = expirationTime;
      }
      if (node.alternate !== null) {
        if (node.alternate.expirationTime === NoWork || node.alternate.expirationTime > expirationTime) {
          node.alternate.expirationTime = expirationTime;
        }
      }
      if (node['return'] === null) {
        if (node.tag === HostRoot) {
          var root = node.stateNode;

          checkRootNeedsClearing(root, fiber, expirationTime);
          requestWork(root, expirationTime);
          checkRootNeedsClearing(root, fiber, expirationTime);
        } else {
          return;
        }
      }
      node = node['return'];
    }
  }

  function scheduleErrorRecovery(fiber) {
    scheduleWorkImpl(fiber, Sync, true);
  }

  function recalculateCurrentTime() {
    // Subtract initial time so it fits inside 32bits
    var ms = now() - startTime;
    mostRecentCurrentTime = msToExpirationTime(ms);
    return mostRecentCurrentTime;
  }

  function deferredUpdates(fn) {
    var previousExpirationContext = expirationContext;
    expirationContext = computeAsyncExpiration();
    try {
      return fn();
    } finally {
      expirationContext = previousExpirationContext;
    }
  }

  function syncUpdates(fn) {
    var previousExpirationContext = expirationContext;
    expirationContext = Sync;
    try {
      return fn();
    } finally {
      expirationContext = previousExpirationContext;
    }
  }

  // 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 = -1;
  var isRendering = false;
  var nextFlushedRoot = null;
  var nextFlushedExpirationTime = NoWork;
  var deadlineDidExpire = false;
  var hasUnhandledError = false;
  var unhandledError = null;
  var deadline = null;

  var isBatchingUpdates = false;
  var isUnbatchingUpdates = false;

  // Use these to prevent an infinite loop of nested updates
  var NESTED_UPDATE_LIMIT = 1000;
  var nestedUpdateCount = 0;

  var timeHeuristicForUnitOfWork = 1;

  function scheduleCallbackWithExpiration(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 {
        // Existing callback has insufficient timeout. Cancel and schedule a
        // new one.
        cancelDeferredCallback(callbackID);
      }
      // The request callback timer is already running. Don't start a new one.
    } else {
      startRequestCallbackTimer();
    }

    // Compute a timeout for the given expiration time.
    var currentMs = now() - startTime;
    var expirationMs = expirationTimeToMs(expirationTime);
    var timeout = expirationMs - currentMs;

    callbackExpirationTime = expirationTime;
    callbackID = scheduleDeferredCallback(performAsyncWork, { timeout: timeout });
  }

  // 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) {
    if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {
      reactProdInvariant('185');
    }

    // 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.remainingExpirationTime = 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.remainingExpirationTime;
      if (remainingExpirationTime === NoWork || expirationTime < remainingExpirationTime) {
        // Update the priority.
        root.remainingExpirationTime = 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(nextFlushedRoot, nextFlushedExpirationTime);
      }
      return;
    }

    // TODO: Get rid of Sync and use current time?
    if (expirationTime === Sync) {
      performWork(Sync, null);
    } else {
      scheduleCallbackWithExpiration(expirationTime);
    }
  }

  function findHighestPriorityRoot() {
    var highestPriorityWork = NoWork;
    var highestPriorityRoot = null;

    if (lastScheduledRoot !== null) {
      var previousScheduledRoot = lastScheduledRoot;
      var root = firstScheduledRoot;
      while (root !== null) {
        var remainingExpirationTime = root.remainingExpirationTime;
        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 (highestPriorityWork === NoWork || remainingExpirationTime < highestPriorityWork) {
            // Update the priority, if it's higher
            highestPriorityWork = remainingExpirationTime;
            highestPriorityRoot = root;
          }
          if (root === lastScheduledRoot) {
            break;
          }
          previousScheduledRoot = root;
          root = root.nextScheduledRoot;
        }
      }
    }

    // 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.
    var previousFlushedRoot = nextFlushedRoot;
    if (previousFlushedRoot !== null && previousFlushedRoot === highestPriorityRoot) {
      nestedUpdateCount++;
    } else {
      // Reset whenever we switch roots.
      nestedUpdateCount = 0;
    }
    nextFlushedRoot = highestPriorityRoot;
    nextFlushedExpirationTime = highestPriorityWork;
  }

  function performAsyncWork(dl) {
    performWork(NoWork, dl);
  }

  function performWork(minExpirationTime, dl) {
    deadline = dl;

    // Keep working on roots until there's no more work, or until the we reach
    // the deadline.
    findHighestPriorityRoot();

    if (enableUserTimingAPI && deadline !== null) {
      var didExpire = nextFlushedExpirationTime < recalculateCurrentTime();
      stopRequestCallbackTimer(didExpire);
    }

    while (nextFlushedRoot !== null && nextFlushedExpirationTime !== NoWork && (minExpirationTime === NoWork || nextFlushedExpirationTime <= minExpirationTime) && !deadlineDidExpire) {
      performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime);
      // Find the next highest priority work.
      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 (deadline !== null) {
      callbackExpirationTime = NoWork;
      callbackID = -1;
    }
    // If there's work left over, schedule a new callback.
    if (nextFlushedExpirationTime !== NoWork) {
      scheduleCallbackWithExpiration(nextFlushedExpirationTime);
    }

    // Clean-up.
    deadline = null;
    deadlineDidExpire = false;
    nestedUpdateCount = 0;

    if (hasUnhandledError) {
      var _error4 = unhandledError;
      unhandledError = null;
      hasUnhandledError = false;
      throw _error4;
    }
  }

  function performWorkOnRoot(root, expirationTime) {
    !!isRendering ? reactProdInvariant('245') : void 0;

    isRendering = true;

    // Check if this is async work or sync/expired work.
    // TODO: Pass current time as argument to renderRoot, commitRoot
    if (expirationTime <= recalculateCurrentTime()) {
      // Flush sync work.
      var finishedWork = root.finishedWork;
      if (finishedWork !== null) {
        // This root is already complete. We can commit it.
        root.finishedWork = null;
        root.remainingExpirationTime = commitRoot(finishedWork);
      } else {
        root.finishedWork = null;
        finishedWork = renderRoot(root, expirationTime);
        if (finishedWork !== null) {
          // We've completed the root. Commit it.
          root.remainingExpirationTime = commitRoot(finishedWork);
        }
      }
    } else {
      // Flush async work.
      var _finishedWork = root.finishedWork;
      if (_finishedWork !== null) {
        // This root is already complete. We can commit it.
        root.finishedWork = null;
        root.remainingExpirationTime = commitRoot(_finishedWork);
      } else {
        root.finishedWork = null;
        _finishedWork = renderRoot(root, expirationTime);
        if (_finishedWork !== null) {
          // We've completed the root. Check the deadline one more time
          // before committing.
          if (!shouldYield()) {
            // Still time left. Commit the root.
            root.remainingExpirationTime = commitRoot(_finishedWork);
          } else {
            // There's no time left. Mark this root as complete. We'll come
            // back and commit it later.
            root.finishedWork = _finishedWork;
          }
        }
      }
    }

    isRendering = false;
  }

  // When working on async work, the reconciler asks the renderer if it should
  // yield execution. For DOM, we implement this with requestIdleCallback.
  function shouldYield() {
    if (deadline === null) {
      return false;
    }
    if (deadline.timeRemaining() > timeHeuristicForUnitOfWork) {
      // Disregard deadline.didTimeout. Only expired work should be flushed
      // during a timeout. This path is only hit for non-expired work.
      return false;
    }
    deadlineDidExpire = true;
    return true;
  }

  // TODO: Not happy about this hook. Conceptually, renderRoot should return a
  // tuple of (isReadyForCommit, didError, error)
  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.remainingExpirationTime = 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) {
        performWork(Sync, null);
      }
    }
  }

  // TODO: Batching should be implemented at the renderer level, not inside
  // the reconciler.
  function unbatchedUpdates(fn) {
    if (isBatchingUpdates && !isUnbatchingUpdates) {
      isUnbatchingUpdates = true;
      try {
        return fn();
      } finally {
        isUnbatchingUpdates = false;
      }
    }
    return fn();
  }

  // TODO: Batching should be implemented at the renderer level, not within
  // the reconciler.
  function flushSync(fn) {
    var previousIsBatchingUpdates = isBatchingUpdates;
    isBatchingUpdates = true;
    try {
      return syncUpdates(fn);
    } finally {
      isBatchingUpdates = previousIsBatchingUpdates;
      !!isRendering ? reactProdInvariant('187') : void 0;
      performWork(Sync, null);
    }
  }

  return {
    computeAsyncExpiration: computeAsyncExpiration,
    computeExpirationForFiber: computeExpirationForFiber,
    scheduleWork: scheduleWork,
    batchedUpdates: batchedUpdates,
    unbatchedUpdates: unbatchedUpdates,
    flushSync: flushSync,
    deferredUpdates: deferredUpdates
  };
};

// 0 is PROD, 1 is DEV.
// Might add PROFILE later.


function getContextForSubtree(parentComponent) {
  if (!parentComponent) {
    return emptyObject_1;
  }

  var fiber = get(parentComponent);
  var parentContext = findCurrentUnmaskedContext(fiber);
  return isContextProvider(fiber) ? processChildContext(fiber, parentContext) : parentContext;
}

var ReactFiberReconciler$1 = function (config) {
  var getPublicInstance = config.getPublicInstance;

  var _ReactFiberScheduler = ReactFiberScheduler(config),
      computeAsyncExpiration = _ReactFiberScheduler.computeAsyncExpiration,
      computeExpirationForFiber = _ReactFiberScheduler.computeExpirationForFiber,
      scheduleWork = _ReactFiberScheduler.scheduleWork,
      batchedUpdates = _ReactFiberScheduler.batchedUpdates,
      unbatchedUpdates = _ReactFiberScheduler.unbatchedUpdates,
      flushSync = _ReactFiberScheduler.flushSync,
      deferredUpdates = _ReactFiberScheduler.deferredUpdates;

  function scheduleTopLevelUpdate(current, element, callback) {
    callback = callback === undefined ? null : callback;
    var expirationTime = void 0;
    // Check if the top-level element is an async wrapper component. If so,
    // treat updates to the root as async. This is a bit weird but lets us
    // avoid a separate `renderAsync` API.
    if (enableAsyncSubtreeAPI && element != null && element.type != null && element.type.prototype != null && element.type.prototype.unstable_isAsyncReactComponent === true) {
      expirationTime = computeAsyncExpiration();
    } else {
      expirationTime = computeExpirationForFiber(current);
    }

    var update = {
      expirationTime: expirationTime,
      partialState: { element: element },
      callback: callback,
      isReplace: false,
      isForced: false,
      nextCallback: null,
      next: null
    };
    insertUpdateIntoFiber(current, update);
    scheduleWork(current, expirationTime);
  }

  function findHostInstance(fiber) {
    var hostFiber = findCurrentHostFiber(fiber);
    if (hostFiber === null) {
      return null;
    }
    return hostFiber.stateNode;
  }

  return {
    createContainer: function (containerInfo, hydrate) {
      return createFiberRoot(containerInfo, hydrate);
    },
    updateContainer: function (element, container, parentComponent, callback) {
      // TODO: If this is a nested container, this won't be the root.
      var current = container.current;

      var context = getContextForSubtree(parentComponent);
      if (container.context === null) {
        container.context = context;
      } else {
        container.pendingContext = context;
      }

      scheduleTopLevelUpdate(current, element, callback);
    },


    batchedUpdates: batchedUpdates,

    unbatchedUpdates: unbatchedUpdates,

    deferredUpdates: deferredUpdates,

    flushSync: flushSync,

    getPublicRootInstance: function (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;
      }
    },


    findHostInstance: findHostInstance,

    findHostInstanceWithNoPortals: function (fiber) {
      var hostFiber = findCurrentHostFiberWithNoPortals(fiber);
      if (hostFiber === null) {
        return null;
      }
      return hostFiber.stateNode;
    },
    injectIntoDevTools: function (devToolsConfig) {
      var findFiberByHostInstance = devToolsConfig.findFiberByHostInstance;

      return injectInternals(_assign({}, devToolsConfig, {
        findHostInstanceByFiber: function (fiber) {
          return findHostInstance(fiber);
        },
        findFiberByHostInstance: function (instance) {
          if (!findFiberByHostInstance) {
            // Might not be implemented by the renderer.
            return null;
          }
          return findFiberByHostInstance(instance);
        }
      }));
    }
  };
};

var ReactFiberReconciler$2 = Object.freeze({
	default: ReactFiberReconciler$1
});

var ReactFiberReconciler$3 = ( ReactFiberReconciler$2 && ReactFiberReconciler$1 ) || ReactFiberReconciler$2;

// TODO: bundle Flow types with the package.



// TODO: decide on the top-level export form.
// This is hacky but makes it work with both Rollup and Jest.
var reactReconciler = ReactFiberReconciler$3['default'] ? ReactFiberReconciler$3['default'] : ReactFiberReconciler$3;

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.2.0';

// 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.

var hasNativePerformanceNow = typeof performance === 'object' && typeof performance.now === 'function';

var now = void 0;
if (hasNativePerformanceNow) {
  now = function () {
    return performance.now();
  };
} else {
  now = function () {
    return Date.now();
  };
}

// TODO: There's no way to cancel, because Fiber doesn't atm.
var rIC = void 0;
var cIC = void 0;

if (!ExecutionEnvironment_1.canUseDOM) {
  rIC = function (frameCallback) {
    return setTimeout(function () {
      frameCallback({
        timeRemaining: function () {
          return Infinity;
        }
      });
    });
  };
  cIC = function (timeoutID) {
    clearTimeout(timeoutID);
  };
} else if (typeof requestIdleCallback !== 'function' || typeof cancelIdleCallback !== 'function') {
  // Polyfill requestIdleCallback and cancelIdleCallback

  var scheduledRICCallback = null;
  var isIdleScheduled = false;
  var timeoutTime = -1;

  var isAnimationFrameScheduled = 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;

  var frameDeadlineObject;
  if (hasNativePerformanceNow) {
    frameDeadlineObject = {
      didTimeout: false,
      timeRemaining: function () {
        // We assume that if we have a performance timer that the rAF callback
        // gets a performance timer value. Not sure if this is always true.
        var remaining = frameDeadline - performance.now();
        return remaining > 0 ? remaining : 0;
      }
    };
  } else {
    frameDeadlineObject = {
      didTimeout: false,
      timeRemaining: function () {
        // Fallback to Date.now()
        var remaining = frameDeadline - Date.now();
        return remaining > 0 ? remaining : 0;
      }
    };
  }

  // We use the postMessage trick to defer idle work until after the repaint.
  var messageKey = '__reactIdleCallback$' + Math.random().toString(36).slice(2);
  var idleTick = function (event) {
    if (event.source !== window || event.data !== messageKey) {
      return;
    }

    isIdleScheduled = false;

    var currentTime = now();
    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 (timeoutTime !== -1 && timeoutTime <= currentTime) {
        // Exceeded the timeout. Invoke the callback even though there's no
        // time left.
        frameDeadlineObject.didTimeout = true;
      } else {
        // No timeout.
        if (!isAnimationFrameScheduled) {
          // Schedule another animation callback so we retry later.
          isAnimationFrameScheduled = true;
          requestAnimationFrame(animationTick);
        }
        // Exit without invoking the callback.
        return;
      }
    } else {
      // There's still time left in this idle period.
      frameDeadlineObject.didTimeout = false;
    }

    timeoutTime = -1;
    var callback = scheduledRICCallback;
    scheduledRICCallback = null;
    if (callback !== null) {
      callback(frameDeadlineObject);
    }
  };
  // Assumes that we have addEventListener in this environment. Might need
  // something better for old IE.
  window.addEventListener('message', idleTick, false);

  var animationTick = function (rafTime) {
    isAnimationFrameScheduled = false;
    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 we get lower than that, 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 (!isIdleScheduled) {
      isIdleScheduled = true;
      window.postMessage(messageKey, '*');
    }
  };

  rIC = function (callback, options) {
    // This assumes that we only schedule one callback at a time because that's
    // how Fiber uses it.
    scheduledRICCallback = callback;
    if (options != null && typeof options.timeout === 'number') {
      timeoutTime = now() + options.timeout;
    }
    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;
      requestAnimationFrame(animationTick);
    }
    return 0;
  };

  cIC = function () {
    scheduledRICCallback = null;
    isIdleScheduled = false;
    timeoutTime = -1;
  };
} else {
  rIC = window.requestIdleCallback;
  cIC = window.cancelIdleCallback;
}

/**
 * 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.
 */

// isAttributeNameSafe() is currently duplicated in DOMMarkupOperations.
// TODO: Find a better place for this.
var VALID_ATTRIBUTE_NAME_REGEX = new RegExp('^[' + ATTRIBUTE_NAME_START_CHAR + '][' + ATTRIBUTE_NAME_CHAR + ']*$');
var illegalAttributeNameCache = {};
var validatedAttributeNameCache = {};
function isAttributeNameSafe(attributeName) {
  if (validatedAttributeNameCache.hasOwnProperty(attributeName)) {
    return true;
  }
  if (illegalAttributeNameCache.hasOwnProperty(attributeName)) {
    return false;
  }
  if (VALID_ATTRIBUTE_NAME_REGEX.test(attributeName)) {
    validatedAttributeNameCache[attributeName] = true;
    return true;
  }
  illegalAttributeNameCache[attributeName] = true;
  return false;
}

// shouldIgnoreValue() is currently duplicated in DOMMarkupOperations.
// TODO: Find a better place for this.
function shouldIgnoreValue(propertyInfo, value) {
  return value == null || propertyInfo.hasBooleanValue && !value || propertyInfo.hasNumericValue && isNaN(value) || propertyInfo.hasPositiveNumericValue && value < 1 || propertyInfo.hasOverloadedBooleanValue && value === false;
}

/**
 * Operations for dealing with DOM properties.
 */





/**
 * 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) {
  var propertyInfo = getPropertyInfo(name);

  if (propertyInfo && shouldSetAttribute(name, value)) {
    var mutationMethod = propertyInfo.mutationMethod;
    if (mutationMethod) {
      mutationMethod(node, value);
    } else if (shouldIgnoreValue(propertyInfo, value)) {
      deleteValueForProperty(node, name);
      return;
    } else if (propertyInfo.mustUseProperty) {
      // Contrary to `setAttribute`, object properties are properly
      // `toString`ed by IE8/9.
      node[propertyInfo.propertyName] = value;
    } else {
      var attributeName = propertyInfo.attributeName;
      var namespace = propertyInfo.attributeNamespace;
      // `setAttribute` with objects becomes only `[object]` in IE8/9,
      // ('' + value) makes it output the correct toString()-value.
      if (namespace) {
        node.setAttributeNS(namespace, attributeName, '' + value);
      } else if (propertyInfo.hasBooleanValue || propertyInfo.hasOverloadedBooleanValue && value === true) {
        node.setAttribute(attributeName, '');
      } else {
        node.setAttribute(attributeName, '' + value);
      }
    }
  } else {
    setValueForAttribute(node, name, shouldSetAttribute(name, value) ? value : null);
    return;
  }

  
}

function setValueForAttribute(node, name, value) {
  if (!isAttributeNameSafe(name)) {
    return;
  }
  if (value == null) {
    node.removeAttribute(name);
  } else {
    node.setAttribute(name, '' + value);
  }

  
}

/**
 * Deletes an attributes from a node.
 *
 * @param {DOMElement} node
 * @param {string} name
 */
function deleteValueForAttribute(node, name) {
  node.removeAttribute(name);
}

/**
 * Deletes the value for a property on a node.
 *
 * @param {DOMElement} node
 * @param {string} name
 */
function deleteValueForProperty(node, name) {
  var propertyInfo = getPropertyInfo(name);
  if (propertyInfo) {
    var mutationMethod = propertyInfo.mutationMethod;
    if (mutationMethod) {
      mutationMethod(node, undefined);
    } else if (propertyInfo.mustUseProperty) {
      var propName = propertyInfo.propertyName;
      if (propertyInfo.hasBooleanValue) {
        node[propName] = false;
      } else {
        node[propName] = '';
      }
    } else {
      node.removeAttribute(propertyInfo.attributeName);
    }
  } else {
    node.removeAttribute(name);
  }
}

// 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 value = props.value;
  var checked = props.checked;

  var hostProps = _assign({
    // Make sure we set .type before any other properties (setting .value
    // before .type means .value is lost in IE11 and below)
    type: undefined,
    // Make sure we set .step before .value (setting .value before .step
    // means .value is rounded on mount, based upon step precision)
    step: undefined,
    // Make sure we set .min & .max before .value (to ensure proper order
    // in corner cases such as min or max deriving from value, e.g. Issue #7170)
    min: undefined,
    max: undefined
  }, props, {
    defaultChecked: undefined,
    defaultValue: undefined,
    value: value != null ? value : node._wrapperState.initialValue,
    checked: checked != null ? checked : node._wrapperState.initialChecked
  });

  return hostProps;
}

function initWrapperState(element, props) {
  var defaultValue = props.defaultValue;
  var node = element;
  node._wrapperState = {
    initialChecked: props.checked != null ? props.checked : props.defaultChecked,
    initialValue: 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);
  }
}

function updateWrapper(element, props) {
  var node = element;
  updateChecked(element, props);

  var value = props.value;
  if (value != null) {
    if (value === 0 && node.value === '') {
      node.value = '0';
      // Note: IE9 reports a number inputs as 'text', so check props instead.
    } else if (props.type === 'number') {
      // Simulate `input.valueAsNumber`. IE9 does not support it
      var valueAsNumber = parseFloat(node.value) || 0;

      if (
      // eslint-disable-next-line
      value != valueAsNumber ||
      // eslint-disable-next-line
      value == valueAsNumber && node.value != value) {
        // Cast `value` to a string to ensure the value is set correctly. While
        // browsers typically do this as necessary, jsdom doesn't.
        node.value = '' + value;
      }
    } else if (node.value !== '' + value) {
      // Cast `value` to a string to ensure the value is set correctly. While
      // browsers typically do this as necessary, jsdom doesn't.
      node.value = '' + value;
    }
  } else {
    if (props.value == null && props.defaultValue != null) {
      // 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
      if (node.defaultValue !== '' + props.defaultValue) {
        node.defaultValue = '' + props.defaultValue;
      }
    }
    if (props.checked == null && props.defaultChecked != null) {
      node.defaultChecked = !!props.defaultChecked;
    }
  }
}

function postMountWrapper(element, props) {
  var node = element;

  // Detach value from defaultValue. We won't do anything if we're working on
  // submit or reset inputs as those values & defaultValues are linked. They
  // are not resetable nodes so this operation doesn't matter and actually
  // removes browser-default values (eg "Submit Query") when no value is
  // provided.

  switch (props.type) {
    case 'submit':
    case 'reset':
      break;
    case 'color':
    case 'date':
    case 'datetime':
    case 'datetime-local':
    case 'month':
    case 'time':
    case 'week':
      // This fixes the no-show issue on iOS Safari and Android Chrome:
      // https://github.com/facebook/react/issues/7233
      node.value = '';
      node.value = node.defaultValue;
      break;
    default:
      node.value = node.value;
      break;
  }

  // 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 = '';
  }
  node.defaultChecked = !node.defaultChecked;
  node.defaultChecked = !node.defaultChecked;
  if (name !== '') {
    node.name = name;
  }
}

function restoreControlledState$1(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);
    }
  }
}

function flattenChildren(children) {
  var content = '';

  // Flatten children and warn if they aren't strings or numbers;
  // invalid types are ignored.
  // We can silently skip them because invalid DOM nesting warning
  // catches these cases in Fiber.
  React.Children.forEach(children, function (child) {
    if (child == null) {
      return;
    }
    if (typeof child === 'string' || typeof child === 'number') {
      content += child;
    }
  });

  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', 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 = '' + 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;
  var value = props.value;
  node._wrapperState = {
    initialValue: value != null ? value : props.defaultValue,
    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;
  // After the initial mount, we control selected-ness manually so don't pass
  // this value down
  node._wrapperState.initialValue = undefined;

  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);
  }
}

// TODO: direct imports like some-package/src/* are bad. Fix me.
/**
 * 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: '' + 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: '' + initialValue
  };
}

function updateWrapper$1(element, props) {
  var node = element;
  var value = props.value;
  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 = '' + 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;
    }
  }
  if (props.defaultValue != null) {
    node.defaultValue = props.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, ensuring that whitespace is preserved
 * even in IE8. innerText is a poor substitute for textContent and, among many
 * issues, inserts <br> instead of the literal newline chars. innerHTML behaves
 * as it should.
 *
 * @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;
};

/**
 * 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,
  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();
}

/**
 * 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.
 *
 * @typechecks
 */

/**
 * 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.
 *
 * @typechecks
 */

/**
 * 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, getStack) {
  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;
    }
  }
}

// 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
};

// 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);

var HTML$1 = '__html';

function assertValidProps(tag, props, getStack) {
  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, getStack()) : 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', getStack()) : 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 = 'style';
var HTML = '__html';

var HTML_NAMESPACE = Namespaces.html;


var getStack = emptyFunction_1.thatReturns('');

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;
}

// There are so many media events, it makes sense to just
// maintain a list rather than create a `trapBubbledEvent` for each
var mediaEvents = {
  topAbort: 'abort',
  topCanPlay: 'canplay',
  topCanPlayThrough: 'canplaythrough',
  topDurationChange: 'durationchange',
  topEmptied: 'emptied',
  topEncrypted: 'encrypted',
  topEnded: 'ended',
  topError: 'error',
  topLoadedData: 'loadeddata',
  topLoadedMetadata: 'loadedmetadata',
  topLoadStart: 'loadstart',
  topPause: 'pause',
  topPlay: 'play',
  topPlaying: 'playing',
  topProgress: 'progress',
  topRateChange: 'ratechange',
  topSeeked: 'seeked',
  topSeeking: 'seeking',
  topStalled: 'stalled',
  topSuspend: 'suspend',
  topTimeUpdate: 'timeupdate',
  topVolumeChange: 'volumechange',
  topWaiting: 'waiting'
};

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 = emptyFunction_1;
}

function setInitialDOMProperties(tag, domElement, rootContainerElement, nextProps, isCustomComponentTag) {
  for (var propKey in nextProps) {
    if (!nextProps.hasOwnProperty(propKey)) {
      continue;
    }
    var nextProp = nextProps[propKey];
    if (propKey === STYLE) {
      setValueForStyles(domElement, nextProp, getStack);
    } 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 blacklist it here rather than in the property list because we emit it in SSR.
    } else if (registrationNameModules.hasOwnProperty(propKey)) {
      if (nextProp != null) {
        ensureListeningTo(rootContainerElement, propKey);
      }
    } else if (isCustomComponentTag) {
      setValueForAttribute(domElement, propKey, nextProp);
    } else if (nextProp != null) {
      // If we're updating to null or undefined, we should remove the property
      // from the DOM node instead of inadvertently setting to a string. This
      // brings us in line with the same behavior we have on initial render.
      setValueForProperty(domElement, propKey, nextProp);
    }
  }
}

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) {
      setValueForStyles(domElement, propValue, getStack);
    } else if (propKey === DANGEROUSLY_SET_INNER_HTML) {
      setInnerHTML(domElement, propValue);
    } else if (propKey === CHILDREN) {
      setTextContent(domElement, propValue);
    } else if (isCustomComponentTag) {
      if (propValue != null) {
        setValueForAttribute(domElement, propKey, propValue);
      } else {
        deleteValueForAttribute(domElement, propKey);
      }
    } else if (propValue != null) {
      setValueForProperty(domElement, propKey, propValue);
    } else {
      // If we're updating to null or undefined, we should remove the property
      // from the DOM node instead of inadvertently setting to a string. This
      // brings us in line with the same behavior we have on initial render.
      deleteValueForProperty(domElement, propKey);
    }
  }
}

function createElement$1(type, props, rootContainerElement, parentNamespace) {
  // We create tags in the namespace of their parent container, except HTML
  var ownerDocument = getOwnerDocumentFromRootContainer(rootContainerElement);
  var domElement;
  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);
    }
  } else {
    domElement = ownerDocument.createElementNS(namespaceURI, type);
  }

  return domElement;
}

function createTextNode$1(text, rootContainerElement) {
  return getOwnerDocumentFromRootContainer(rootContainerElement).createTextNode(text);
}

function setInitialProperties$1(domElement, tag, rawProps, rootContainerElement) {
  var isCustomComponentTag = isCustomComponent(tag, rawProps);
  var props;
  switch (tag) {
    case 'iframe':
    case 'object':
      trapBubbledEvent('topLoad', 'load', domElement);
      props = rawProps;
      break;
    case 'video':
    case 'audio':
      // Create listener for each media event
      for (var event in mediaEvents) {
        if (mediaEvents.hasOwnProperty(event)) {
          trapBubbledEvent(event, mediaEvents[event], domElement);
        }
      }
      props = rawProps;
      break;
    case 'source':
      trapBubbledEvent('topError', 'error', domElement);
      props = rawProps;
      break;
    case 'img':
    case 'image':
      trapBubbledEvent('topError', 'error', domElement);
      trapBubbledEvent('topLoad', 'load', domElement);
      props = rawProps;
      break;
    case 'form':
      trapBubbledEvent('topReset', 'reset', domElement);
      trapBubbledEvent('topSubmit', 'submit', domElement);
      props = rawProps;
      break;
    case 'details':
      trapBubbledEvent('topToggle', 'toggle', domElement);
      props = rawProps;
      break;
    case 'input':
      initWrapperState(domElement, rawProps);
      props = getHostProps(domElement, rawProps);
      trapBubbledEvent('topInvalid', '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('topInvalid', '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('topInvalid', '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, getStack);

  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);
      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$1(domElement, tag, lastRawProps, nextRawProps, rootContainerElement) {
  var updatePayload = null;

  var lastProps;
  var nextProps;
  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, getStack);

  var propKey;
  var styleName;
  var styleUpdates = null;
  for (propKey in lastProps) {
    if (nextProps.hasOwnProperty(propKey) || !lastProps.hasOwnProperty(propKey) || lastProps[propKey] == null) {
      continue;
    }
    if (propKey === STYLE) {
      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) {
      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, styleUpdates);
  }
  return updatePayload;
}

// Apply the diff.
function updateProperties$1(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$1(domElement, tag, rawProps, parentNamespace, rootContainerElement) {
  switch (tag) {
    case 'iframe':
    case 'object':
      trapBubbledEvent('topLoad', 'load', domElement);
      break;
    case 'video':
    case 'audio':
      // Create listener for each media event
      for (var event in mediaEvents) {
        if (mediaEvents.hasOwnProperty(event)) {
          trapBubbledEvent(event, mediaEvents[event], domElement);
        }
      }
      break;
    case 'source':
      trapBubbledEvent('topError', 'error', domElement);
      break;
    case 'img':
    case 'image':
      trapBubbledEvent('topError', 'error', domElement);
      trapBubbledEvent('topLoad', 'load', domElement);
      break;
    case 'form':
      trapBubbledEvent('topReset', 'reset', domElement);
      trapBubbledEvent('topSubmit', 'submit', domElement);
      break;
    case 'details':
      trapBubbledEvent('topToggle', 'toggle', domElement);
      break;
    case 'input':
      initWrapperState(domElement, rawProps);
      trapBubbledEvent('topInvalid', '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('topInvalid', '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('topInvalid', '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, getStack);

  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);
      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$1(textNode, text) {
  var isDifferent = textNode.nodeValue !== text;
  return isDifferent;
}

function warnForUnmatchedText$1(textNode, text) {
  
}

function warnForDeletedHydratableElement$1(parentNode, child) {
  
}

function warnForDeletedHydratableText$1(parentNode, child) {
  
}

function warnForInsertedHydratedElement$1(parentNode, tag, props) {
  
}

function warnForInsertedHydratedText$1(parentNode, text) {
  
}

function restoreControlledState(domElement, tag, props) {
  switch (tag) {
    case 'input':
      restoreControlledState$1(domElement, props);
      return;
    case 'textarea':
      restoreControlledState$3(domElement, props);
      return;
    case 'select':
      restoreControlledState$2(domElement, props);
      return;
  }
}

var ReactDOMFiberComponent = Object.freeze({
	createElement: createElement$1,
	createTextNode: createTextNode$1,
	setInitialProperties: setInitialProperties$1,
	diffProperties: diffProperties$1,
	updateProperties: updateProperties$1,
	diffHydratedProperties: diffHydratedProperties$1,
	diffHydratedText: diffHydratedText$1,
	warnForUnmatchedText: warnForUnmatchedText$1,
	warnForDeletedHydratableElement: warnForDeletedHydratableElement$1,
	warnForDeletedHydratableText: warnForDeletedHydratableText$1,
	warnForInsertedHydratedElement: warnForInsertedHydratedElement$1,
	warnForInsertedHydratedText: warnForInsertedHydratedText$1,
	restoreControlledState: restoreControlledState
});

// TODO: direct imports like some-package/src/* are bad. Fix me.

// TODO: direct imports like some-package/src/* are bad. Fix me.
var createElement = createElement$1;
var createTextNode = createTextNode$1;
var setInitialProperties = setInitialProperties$1;
var diffProperties = diffProperties$1;
var updateProperties = updateProperties$1;
var diffHydratedProperties = diffHydratedProperties$1;
var diffHydratedText = diffHydratedText$1;
var precacheFiberNode = precacheFiberNode$1;
var updateFiberProps = updateFiberProps$1;


injection$3.injectFiberControlledHostComponent(ReactDOMFiberComponent);

var eventsEnabled = null;
var selectionInformation = null;

/**
 * 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));
}

function shouldAutoFocusHostComponent(type, props) {
  switch (type) {
    case 'button':
    case 'input':
    case 'select':
    case 'textarea':
      return !!props.autoFocus;
  }
  return false;
}

var DOMRenderer = reactReconciler({
  getRootHostContext: function (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;
  },
  getChildHostContext: function (parentHostContext, type) {
    var parentNamespace = parentHostContext;
    return getChildNamespace(parentNamespace, type);
  },
  getPublicInstance: function (instance) {
    return instance;
  },
  prepareForCommit: function () {
    eventsEnabled = isEnabled();
    selectionInformation = getSelectionInformation();
    setEnabled(false);
  },
  resetAfterCommit: function () {
    restoreSelection(selectionInformation);
    selectionInformation = null;
    setEnabled(eventsEnabled);
    eventsEnabled = null;
  },
  createInstance: function (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;
  },
  appendInitialChild: function (parentInstance, child) {
    parentInstance.appendChild(child);
  },
  finalizeInitialChildren: function (domElement, type, props, rootContainerInstance) {
    setInitialProperties(domElement, type, props, rootContainerInstance);
    return shouldAutoFocusHostComponent(type, props);
  },
  prepareUpdate: function (domElement, type, oldProps, newProps, rootContainerInstance, hostContext) {
    return diffProperties(domElement, type, oldProps, newProps, rootContainerInstance);
  },
  shouldSetTextContent: function (type, props) {
    return type === 'textarea' || typeof props.children === 'string' || typeof props.children === 'number' || typeof props.dangerouslySetInnerHTML === 'object' && props.dangerouslySetInnerHTML !== null && typeof props.dangerouslySetInnerHTML.__html === 'string';
  },
  shouldDeprioritizeSubtree: function (type, props) {
    return !!props.hidden;
  },
  createTextInstance: function (text, rootContainerInstance, hostContext, internalInstanceHandle) {
    var textNode = createTextNode(text, rootContainerInstance);
    precacheFiberNode(internalInstanceHandle, textNode);
    return textNode;
  },


  now: now,

  mutation: {
    commitMount: function (domElement, type, newProps, internalInstanceHandle) {
      domElement.focus();
    },
    commitUpdate: function (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);
    },
    resetTextContent: function (domElement) {
      domElement.textContent = '';
    },
    commitTextUpdate: function (textInstance, oldText, newText) {
      textInstance.nodeValue = newText;
    },
    appendChild: function (parentInstance, child) {
      parentInstance.appendChild(child);
    },
    appendChildToContainer: function (container, child) {
      if (container.nodeType === COMMENT_NODE) {
        container.parentNode.insertBefore(child, container);
      } else {
        container.appendChild(child);
      }
    },
    insertBefore: function (parentInstance, child, beforeChild) {
      parentInstance.insertBefore(child, beforeChild);
    },
    insertInContainerBefore: function (container, child, beforeChild) {
      if (container.nodeType === COMMENT_NODE) {
        container.parentNode.insertBefore(child, beforeChild);
      } else {
        container.insertBefore(child, beforeChild);
      }
    },
    removeChild: function (parentInstance, child) {
      parentInstance.removeChild(child);
    },
    removeChildFromContainer: function (container, child) {
      if (container.nodeType === COMMENT_NODE) {
        container.parentNode.removeChild(child);
      } else {
        container.removeChild(child);
      }
    }
  },

  hydration: {
    canHydrateInstance: function (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;
    },
    canHydrateTextInstance: function (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;
    },
    getNextHydratableSibling: function (instance) {
      var node = instance.nextSibling;
      // Skip non-hydratable nodes.
      while (node && node.nodeType !== ELEMENT_NODE && node.nodeType !== TEXT_NODE) {
        node = node.nextSibling;
      }
      return node;
    },
    getFirstHydratableChild: function (parentInstance) {
      var next = parentInstance.firstChild;
      // Skip non-hydratable nodes.
      while (next && next.nodeType !== ELEMENT_NODE && next.nodeType !== TEXT_NODE) {
        next = next.nextSibling;
      }
      return next;
    },
    hydrateInstance: function (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);
    },
    hydrateTextInstance: function (textInstance, text, internalInstanceHandle) {
      precacheFiberNode(internalInstanceHandle, textInstance);
      return diffHydratedText(textInstance, text);
    },
    didNotMatchHydratedContainerTextInstance: function (parentContainer, textInstance, text) {
      
    },
    didNotMatchHydratedTextInstance: function (parentType, parentProps, parentInstance, textInstance, text) {
      
    },
    didNotHydrateContainerInstance: function (parentContainer, instance) {
      
    },
    didNotHydrateInstance: function (parentType, parentProps, parentInstance, instance) {
      
    },
    didNotFindHydratableContainerInstance: function (parentContainer, type, props) {
      
    },
    didNotFindHydratableContainerTextInstance: function (parentContainer, text) {
      
    },
    didNotFindHydratableInstance: function (parentType, parentProps, parentInstance, type, props) {
      
    },
    didNotFindHydratableTextInstance: function (parentType, parentProps, parentInstance, text) {
      
    }
  },

  scheduleDeferredCallback: rIC,
  cancelDeferredCallback: cIC,

  useSyncScheduling: !enableAsyncSchedulingByDefaultInReactDOM
});

injection$4.injectFiberBatchedUpdates(DOMRenderer.batchedUpdates);

function renderSubtreeIntoContainer(parentComponent, children, container, forceHydrate, callback) {
  !isValidContainer(container) ? reactProdInvariant('200') : void 0;

  var root = container._reactRootContainer;
  if (!root) {
    var shouldHydrate = forceHydrate || shouldHydrateDueToLegacyHeuristic(container);
    // First clear any existing content.
    if (!shouldHydrate) {
      var rootSibling = void 0;
      while (rootSibling = container.lastChild) {
        container.removeChild(rootSibling);
      }
    }
    var newRoot = DOMRenderer.createContainer(container, shouldHydrate);
    root = container._reactRootContainer = newRoot;
    // Initial mount should not be batched.
    DOMRenderer.unbatchedUpdates(function () {
      DOMRenderer.updateContainer(children, newRoot, parentComponent, callback);
    });
  } else {
    DOMRenderer.updateContainer(children, root, parentComponent, callback);
  }
  return DOMRenderer.getPublicRootInstance(root);
}

function createPortal(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);
}

function ReactRoot(container, hydrate) {
  var root = DOMRenderer.createContainer(container, hydrate);
  this._reactRootContainer = root;
}
ReactRoot.prototype.render = function (children, callback) {
  var root = this._reactRootContainer;
  DOMRenderer.updateContainer(children, root, null, callback);
};
ReactRoot.prototype.unmount = function (callback) {
  var root = this._reactRootContainer;
  DOMRenderer.updateContainer(null, root, null, callback);
};

var ReactDOM = {
  createPortal: createPortal,

  findDOMNode: function (componentOrElement) {
    if (componentOrElement == null) {
      return null;
    }
    if (componentOrElement.nodeType === ELEMENT_NODE) {
      return componentOrElement;
    }

    var inst = get(componentOrElement);
    if (inst) {
      return DOMRenderer.findHostInstance(inst);
    }

    if (typeof componentOrElement.render === 'function') {
      reactProdInvariant('188');
    } else {
      reactProdInvariant('213', Object.keys(componentOrElement));
    }
  },
  hydrate: function (element, container, callback) {
    // TODO: throw or warn if we couldn't hydrate?
    return renderSubtreeIntoContainer(null, element, container, true, callback);
  },
  render: function (element, container, callback) {
    return renderSubtreeIntoContainer(null, element, container, false, callback);
  },
  unstable_renderSubtreeIntoContainer: function (parentComponent, element, containerNode, callback) {
    !(parentComponent != null && has(parentComponent)) ? reactProdInvariant('38') : void 0;
    return renderSubtreeIntoContainer(parentComponent, element, containerNode, false, callback);
  },
  unmountComponentAtNode: function (container) {
    !isValidContainer(container) ? reactProdInvariant('40') : void 0;

    if (container._reactRootContainer) {
      DOMRenderer.unbatchedUpdates(function () {
        renderSubtreeIntoContainer(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: createPortal,

  unstable_batchedUpdates: batchedUpdates,

  unstable_deferredUpdates: DOMRenderer.deferredUpdates,

  flushSync: DOMRenderer.flushSync,

  __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {
    // For TapEventPlugin which is popular in open source
    EventPluginHub: EventPluginHub,
    // Used by test-utils
    EventPluginRegistry: EventPluginRegistry,
    EventPropagators: EventPropagators,
    ReactControlledComponent: ReactControlledComponent,
    ReactDOMComponentTree: ReactDOMComponentTree,
    ReactDOMEventListener: ReactDOMEventListener
  }
};

if (enableCreateRoot) {
  ReactDOM.createRoot = function createRoot(container, options) {
    var hydrate = options != null && options.hydrate === true;
    return new ReactRoot(container, hydrate);
  };
}

var foundDevTools = DOMRenderer.injectIntoDevTools({
  findFiberByHostInstance: getClosestInstanceFromNode,
  bundleType: 0,
  version: ReactVersion,
  rendererPackageName: 'react-dom'
});

var ReactDOM$2 = Object.freeze({
  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['default'] : ReactDOM$3;

return reactDom;

})));
PK
!<7��5��Ichrome/devtools/modules/devtools/client/shared/vendor/react-prop-types.js /**
  * react-prop-types v15.6.0
  */
(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 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){
/**
 * 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';

if ("production" !== 'production') {
  var invariant = require('fbjs/lib/invariant');
  var warning = require('fbjs/lib/warning');
  var ReactPropTypesSecret = require('./lib/ReactPropTypesSecret');
  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) {
  if ("production" !== 'production') {
    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(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);
        } catch (ex) {
          error = ex;
        }
        warning(!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(false, 'Failed %s type: %s%s', location, error.message, stack != null ? stack : '');
        }
      }
    }
  }
}

module.exports = checkPropTypes;

},{"./lib/ReactPropTypesSecret":5,"fbjs/lib/invariant":7,"fbjs/lib/warning":8}],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 emptyFunction = require('fbjs/lib/emptyFunction');
var invariant = require('fbjs/lib/invariant');
var ReactPropTypesSecret = require('./lib/ReactPropTypesSecret');

module.exports = function() {
  function shim(props, propName, componentName, location, propFullName, secret) {
    if (secret === ReactPropTypesSecret) {
      // It is still safe when called from React.
      return;
    }
    invariant(
      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'
    );
  };
  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,
    instanceOf: getShim,
    node: shim,
    objectOf: getShim,
    oneOf: getShim,
    oneOfType: getShim,
    shape: getShim,
    exact: getShim
  };

  ReactPropTypes.checkPropTypes = emptyFunction;
  ReactPropTypes.PropTypes = ReactPropTypes;

  return ReactPropTypes;
};

},{"./lib/ReactPropTypesSecret":5,"fbjs/lib/emptyFunction":6,"fbjs/lib/invariant":7}],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 emptyFunction = require('fbjs/lib/emptyFunction');
var invariant = require('fbjs/lib/invariant');
var warning = require('fbjs/lib/warning');
var assign = require('object-assign');

var ReactPropTypesSecret = require('./lib/ReactPropTypesSecret');
var checkPropTypes = require('./checkPropTypes');

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(),
    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) {
    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
          invariant(
            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 ("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
          ) {
            warning(
              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.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);
        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)) {
      "production" !== 'production' ? warning(false, 'Invalid argument supplied to oneOf, expected an instance of array.') : void 0;
      return emptyFunction.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);
          if (error instanceof Error) {
            return error;
          }
        }
      }
      return null;
    }
    return createChainableTypeChecker(validate);
  }

  function createUnionTypeChecker(arrayOfTypeCheckers) {
    if (!Array.isArray(arrayOfTypeCheckers)) {
      "production" !== 'production' ? warning(false, 'Invalid argument supplied to oneOfType, expected an instance of array.') : void 0;
      return emptyFunction.thatReturnsNull;
    }

    for (var i = 0; i < arrayOfTypeCheckers.length; i++) {
      var checker = arrayOfTypeCheckers[i];
      if (typeof checker !== 'function') {
        warning(
          false,
          'Invalid argument supplied to oneOfType. Expected an array of check functions, but ' +
          'received %s at index %s.',
          getPostfixForTypeWarning(checker),
          i
        );
        return emptyFunction.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) == 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);
        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 (!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;
    }

    // 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.PropTypes = ReactPropTypes;

  return ReactPropTypes;
};

},{"./checkPropTypes":1,"./lib/ReactPropTypesSecret":5,"fbjs/lib/emptyFunction":6,"fbjs/lib/invariant":7,"fbjs/lib/warning":8,"object-assign":9}],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 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 = require('./factoryWithTypeCheckers')(isValidElement, 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}],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){
"use strict";

/**
 * 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;
};

module.exports = emptyFunction;
},{}],7:[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';

/**
 * 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) {};

if ("production" !== 'production') {
  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;
  }
}

module.exports = invariant;
},{}],8:[function(require,module,exports){
/**
 * 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.
 *
 */

'use strict';

var emptyFunction = require('./emptyFunction');

/**
 * 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;

if ("production" !== 'production') {
  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));
    }
  };
}

module.exports = warning;
},{"./emptyFunction":6}],9:[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;
};

},{}]},{},[4])(4)
});
PK
!<�[w-N N Dchrome/devtools/modules/devtools/client/shared/vendor/react-redux.js/**
 * react-redux v5.0.7
 */

(function (global, factory) {
	typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('devtools/client/shared/vendor/react'), require('devtools/client/shared/vendor/redux')) :
	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 || Function('return this')();

/** 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 });

})));
PK
!<���1�1Tchrome/devtools/modules/devtools/client/shared/vendor/react-test-renderer-shallow.js(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.ShallowRenderer = f()}})(function(){var define,module,exports;return (function(){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}return e})()({1:[function(require,module,exports){
/** @license React v16.2.0
 * react.development.js
 *
 * 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';

(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';

/*
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;
};

// TODO: this is special because it gets imported during build.

var ReactVersion = '16.2.0';

// 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_CALL_TYPE = hasSymbol ? Symbol['for']('react.call') : 0xeac8;
var REACT_RETURN_TYPE = hasSymbol ? Symbol['for']('react.return') : 0xeac9;
var REACT_PORTAL_TYPE = hasSymbol ? Symbol['for']('react.portal') : 0xeaca;
var REACT_FRAGMENT_TYPE = hasSymbol ? Symbol['for']('react.fragment') : 0xeacb;

var MAYBE_ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator;
var FAUX_ITERATOR_SYMBOL = '@@iterator';

function getIteratorFn(maybeIterable) {
  if (maybeIterable === null || typeof maybeIterable === 'undefined') {
    return null;
  }
  var maybeIterator = MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL] || maybeIterable[FAUX_ITERATOR_SYMBOL];
  if (typeof maybeIterator === 'function') {
    return maybeIterator;
  }
  return null;
}

/**
 * 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.
 */

/**
 * 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 = {};

{
  Object.freeze(emptyObject);
}

var emptyObject_1 = emptyObject;

/**
 * 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;

/**
 * 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('`warning(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;

/**
 * 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) 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.
 *
 */





/**
 * 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$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 = 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 = warning;

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;
    }
    warning_1(false, '%s(...): Can only update a mounted or mounting component. ' + 'This usually means you called %s() on an unmounted component. ' + 'This is a no-op.\n\nPlease check the code for the %s component.', callerName, 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');
  }
};

/**
 * Base class helpers for the updating state of a component.
 */
function Component(props, context, updater) {
  this.props = props;
  this.context = context;
  this.refs = emptyObject_1;
  // 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_1(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]);
    }
  }
}

/**
 * Base class helpers for the updating state of a component.
 */
function PureComponent(props, context, updater) {
  // Duplicated from Component.
  this.props = props;
  this.context = context;
  this.refs = emptyObject_1;
  // We initialize the default updater but the real one gets injected by the
  // renderer.
  this.updater = updater || ReactNoopUpdateQueue;
}

function ComponentDummy() {}
ComponentDummy.prototype = Component.prototype;
var pureComponentPrototype = PureComponent.prototype = new ComponentDummy();
pureComponentPrototype.constructor = PureComponent;
// Avoid an extra prototype jump for these methods.
objectAssign(pureComponentPrototype, Component.prototype);
pureComponentPrototype.isPureReactComponent = true;

function AsyncComponent(props, context, updater) {
  // Duplicated from Component.
  this.props = props;
  this.context = context;
  this.refs = emptyObject_1;
  // We initialize the default updater but the real one gets injected by the
  // renderer.
  this.updater = updater || ReactNoopUpdateQueue;
}

var asyncComponentPrototype = AsyncComponent.prototype = new ComponentDummy();
asyncComponentPrototype.constructor = AsyncComponent;
// Avoid an extra prototype jump for these methods.
objectAssign(asyncComponentPrototype, Component.prototype);
asyncComponentPrototype.unstable_isAsyncReactComponent = true;
asyncComponentPrototype.render = function () {
  return this.props.children;
};

/**
 * 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 hasOwnProperty$1 = Object.prototype.hasOwnProperty;

var RESERVED_PROPS = {
  key: true,
  ref: true,
  __self: true,
  __source: true
};

var specialPropKeyWarningShown;
var specialPropRefWarningShown;

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;
      warning_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;
      warning_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 allow 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;

  // 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) {
      if (typeof props.$$typeof === 'undefined' || props.$$typeof !== REACT_ELEMENT_TYPE) {
        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) {
  var propName;

  // 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;
    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 valid component.
 * @final
 */
function isValidElement(object) {
  return typeof object === 'object' && object !== null && object.$$typeof === REACT_ELEMENT_TYPE;
}

var ReactDebugCurrentFrame = {};

{
  // Component that is being worked on
  ReactDebugCurrentFrame.getCurrentStack = null;

  ReactDebugCurrentFrame.getStackAddendum = function () {
    var impl = ReactDebugCurrentFrame.getCurrentStack;
    if (impl) {
      return impl();
    }
    return null;
  };
}

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_CALL_TYPE:
          case REACT_RETURN_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;
  var nextName;
  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) {
          warning_1(didWarnAboutMaps, 'Using Maps as children is unsupported and will likely yield ' + 'unexpected results. Convert it to a sequence/iterable of keyed ' + 'ReactElements instead.%s', ReactDebugCurrentFrame.getStackAddendum());
          didWarnAboutMaps = true;
        }
      }

      var iterator = iteratorFn.call(children);
      var step;
      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_1(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#react.children.foreach
 *
 * 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, emptyFunction_1.thatReturnsArgument);
  } 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#react.children.map
 *
 * 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#react.children.count
 *
 * @param {?*} children Children tree container.
 * @return {number} The number of children.
 */
function countChildren(children, context) {
  return traverseAllChildren(children, emptyFunction_1.thatReturnsNull, 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#react.children.toarray
 */
function toArray(children) {
  var result = [];
  mapIntoWithKeyPrefixInternal(children, result, null, emptyFunction_1.thatReturnsArgument);
  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#react.children.only
 *
 * 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_1(false, 'React.Children.only expected to receive a single React element child.') : void 0;
  return children;
}

var describeComponentFrame = function (name, source, ownerName) {
  return '\n    in ' + (name || 'Unknown') + (source ? ' (at ' + source.fileName.replace(/^.*[\\\/]/, '') + ':' + source.lineNumber + ')' : ownerName ? ' (created by ' + ownerName + ')' : '');
};

function getComponentName(fiber) {
  var type = fiber.type;

  if (typeof type === 'string') {
    return type;
  }
  if (typeof type === 'function') {
    return type.displayName || type.name;
  }
  return null;
}

/**
 * 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 invariant$2 = invariant_1;
  var warning$2 = warning_1;
  var ReactPropTypesSecret = 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$2(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);
        } 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;

/**
 * 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 currentlyValidatingElement = null;

  var propTypesMisspellWarningShown = false;

  var getDisplayName = function (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 if (element.type === REACT_FRAGMENT_TYPE) {
      return 'React.Fragment';
    } else {
      return element.type.displayName || element.type.name || 'Unknown';
    }
  };

  var getStackAddendum = function () {
    var stack = '';
    if (currentlyValidatingElement) {
      var name = getDisplayName(currentlyValidatingElement);
      var owner = currentlyValidatingElement._owner;
      stack += describeComponentFrame(name, currentlyValidatingElement._source, owner && getComponentName(owner));
    }
    stack += ReactDebugCurrentFrame.getStackAddendum() || '';
    return stack;
  };

  var VALID_FRAGMENT_PROPS = new Map([['children', true], ['key', true]]);
}

function getDeclarationErrorAddendum() {
  if (ReactCurrentOwner.current) {
    var name = getComponentName(ReactCurrentOwner.current);
    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) + '.';
  }

  currentlyValidatingElement = element;
  {
    warning_1(false, 'Each child in an array or iterator should have a unique "key" prop.' + '%s%s See https://fb.me/react-warning-keys for more information.%s', currentComponentErrorInfo, childOwner, getStackAddendum());
  }
  currentlyValidatingElement = 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;
        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 componentClass = element.type;
  if (typeof componentClass !== 'function') {
    return;
  }
  var name = componentClass.displayName || componentClass.name;
  var propTypes = componentClass.propTypes;
  if (propTypes) {
    currentlyValidatingElement = element;
    checkPropTypes_1(propTypes, element.props, 'prop', name, getStackAddendum);
    currentlyValidatingElement = null;
  } else if (componentClass.PropTypes !== undefined && !propTypesMisspellWarningShown) {
    propTypesMisspellWarningShown = true;
    warning_1(false, 'Component %s declared `PropTypes` instead of `propTypes`. Did you misspell the property assignment?', name || 'Unknown');
  }
  if (typeof componentClass.getDefaultProps === 'function') {
    warning_1(componentClass.getDefaultProps.isReactClassApproved, 'getDefaultProps is only used on classic React.createClass ' + 'definitions. Use a static property named `defaultProps` instead.');
  }
}

/**
 * Given a fragment, validate that it can only be provided with fragment props
 * @param {ReactElement} fragment
 */
function validateFragmentProps(fragment) {
  currentlyValidatingElement = fragment;

  var _iteratorNormalCompletion = true;
  var _didIteratorError = false;
  var _iteratorError = undefined;

  try {
    for (var _iterator = Object.keys(fragment.props)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
      var key = _step.value;

      if (!VALID_FRAGMENT_PROPS.has(key)) {
        warning_1(false, 'Invalid prop `%s` supplied to `React.Fragment`. ' + 'React.Fragment can only have `key` and `children` props.%s', key, getStackAddendum());
        break;
      }
    }
  } catch (err) {
    _didIteratorError = true;
    _iteratorError = err;
  } finally {
    try {
      if (!_iteratorNormalCompletion && _iterator['return']) {
        _iterator['return']();
      }
    } finally {
      if (_didIteratorError) {
        throw _iteratorError;
      }
    }
  }

  if (fragment.ref !== null) {
    warning_1(false, 'Invalid attribute `ref` supplied to `React.Fragment`.%s', getStackAddendum());
  }

  currentlyValidatingElement = null;
}

function createElementWithValidation(type, props, children) {
  var validType = typeof type === 'string' || typeof type === 'function' || typeof type === 'symbol' || typeof type === 'number';
  // 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();
    }

    info += getStackAddendum() || '';

    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', type == null ? type : typeof type, 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 (typeof type === 'symbol' && type === REACT_FRAGMENT_TYPE) {
    validateFragmentProps(element);
  } else {
    validatePropTypes(element);
  }

  return element;
}

function createFactoryWithValidation(type) {
  var validatedFactory = createElementWithValidation.bind(null, type);
  // Legacy hook TODO: Warn if this is accessed
  validatedFactory.type = type;

  {
    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
  },

  Component: Component,
  PureComponent: PureComponent,
  unstable_AsyncComponent: AsyncComponent,

  Fragment: REACT_FRAGMENT_TYPE,

  createElement: createElementWithValidation,
  cloneElement: cloneElementWithValidation,
  createFactory: createFactoryWithValidation,
  isValidElement: isValidElement,

  version: ReactVersion,

  __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {
    ReactCurrentOwner: ReactCurrentOwner,
    // Used by renderers to avoid bundling object-assign twice in UMD bundles:
    assign: objectAssign
  }
};

{
  objectAssign(React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, {
    // 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: {}
  });
}



var React$2 = Object.freeze({
	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['default'] : React$3;

return react;

})));

},{}],2:[function(require,module,exports){
/** @license React v16.2.0
 * react-test-renderer-shallow.development.js
 *
 * 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';

  (function() {
'use strict';

var _assign = require('object-assign');
var React = require('../../../dist/react.development');
var emptyObject = require('fbjs/lib/emptyObject');
var invariant = require('fbjs/lib/invariant');
var shallowEqual = require('fbjs/lib/shallowEqual');
var checkPropTypes = require('prop-types/checkPropTypes');

/**
 * 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.
 */

var describeComponentFrame = function (name, source, ownerName) {
  return '\n    in ' + (name || 'Unknown') + (source ? ' (at ' + source.fileName.replace(/^.*[\\\/]/, '') + ':' + source.lineNumber + ')' : ownerName ? ' (created by ' + ownerName + ')' : '');
};

function getComponentName(fiber) {
  var type = fiber.type;

  if (typeof type === 'string') {
    return type;
  }
  if (typeof type === 'function') {
    return type.displayName || type.name;
  }
  return null;
}

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var ReactShallowRenderer = function () {
  function ReactShallowRenderer() {
    _classCallCheck(this, ReactShallowRenderer);

    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);
  }

  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) ? invariant(false, 'ReactShallowRenderer render(): Invalid component element.%s', typeof element === 'function' ? ' Instead of passing a component class, make sure to instantiate ' + 'it by passing it to React.createElement.' : '') : void 0;
    // Show a special message for host elements since it's a common case.
    !(typeof element.type !== 'string') ? invariant(false, 'ReactShallowRenderer render(): Shallow rendering works only with custom components, not primitives (%s). Instead of calling `.render(el)` and inspecting the rendered output, look at `el.props` directly instead.', element.type) : void 0;
    !(typeof element.type === 'function') ? invariant(false, 'ReactShallowRenderer render(): Shallow rendering works only with custom components, but the provided element type was `%s`.', Array.isArray(element.type) ? 'array' : element.type === null ? 'null' : typeof element.type) : void 0;

    if (this._rendering) {
      return;
    }

    this._rendering = true;
    this._element = element;
    this._context = context;

    if (this._instance) {
      this._updateClassComponent(element.type, element.props, context);
    } else {
      if (shouldConstruct(element.type)) {
        this._instance = new element.type(element.props, context, this._updater);

        if (element.type.hasOwnProperty('contextTypes')) {
          currentlyValidatingElement = element;

          checkPropTypes(element.type.contextTypes, context, 'context', getName(element.type, this._instance), getStackAddendum);

          currentlyValidatingElement = null;
        }

        this._mountClassComponent(element.props, context);
      } else {
        this._rendered = element.type(element.props, 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._context = null;
    this._element = null;
    this._newState = null;
    this._rendered = null;
    this._instance = null;
  };

  ReactShallowRenderer.prototype._mountClassComponent = function _mountClassComponent(props, context) {
    this._instance.context = context;
    this._instance.props = props;
    this._instance.state = this._instance.state || emptyObject;
    this._instance.updater = this._updater;

    if (typeof this._instance.componentWillMount === 'function') {
      var beforeState = this._newState;

      this._instance.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(type, props, context) {
    var oldState = this._instance.state || emptyObject;
    var oldProps = this._instance.props;

    if (oldProps !== props && typeof this._instance.componentWillReceiveProps === 'function') {
      this._instance.componentWillReceiveProps(props, context);
    }
    // Read state after cWRP in case it calls setState
    var state = this._newState || oldState;

    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 (type.prototype && type.prototype.isPureReactComponent) {
      shouldUpdate = !shallowEqual(oldProps, props) || !shallowEqual(oldState, state);
    }

    if (shouldUpdate) {
      if (typeof this._instance.componentWillUpdate === 'function') {
        this._instance.componentWillUpdate(props, state, context);
      }
    }

    this._instance.context = context;
    this._instance.props = props;
    this._instance.state = state;

    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 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(currentState, publicInstance.props);
    }

    this._renderer._newState = _assign({}, currentState, partialState);

    this._renderer.render(this._renderer._element, this._renderer._context);
  };

  return Updater;
}();

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 {
    return element.type.displayName || element.type.name || 'Unknown';
  }
}

function getStackAddendum() {
  var stack = '';
  if (currentlyValidatingElement) {
    var name = getDisplayName(currentlyValidatingElement);
    var owner = currentlyValidatingElement._owner;
    stack += describeComponentFrame(name, currentlyValidatingElement._source, owner && getComponentName(owner));
  }
  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);
}



var ReactShallowRenderer$2 = Object.freeze({
	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['default'] : ReactShallowRenderer$3;

module.exports = shallow;
  })();


},{"../../../dist/react.development":1,"fbjs/lib/emptyObject":5,"fbjs/lib/invariant":6,"fbjs/lib/shallowEqual":7,"object-assign":9,"prop-types/checkPropTypes":10}],3:[function(require,module,exports){
'use strict';

module.exports = require('./cjs/react-test-renderer-shallow.development.js');

},{"./cjs/react-test-renderer-shallow.development.js":2}],4:[function(require,module,exports){
"use strict";

/**
 * 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;
};

module.exports = emptyFunction;
},{}],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 emptyObject = {};

if ("development" !== 'production') {
  Object.freeze(emptyObject);
}

module.exports = emptyObject;
},{}],6:[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';

/**
 * 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) {};

if ("development" !== 'production') {
  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;
  }
}

module.exports = invariant;
},{}],7:[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.
 *
 * @typechecks
 * 
 */

/*eslint-disable no-self-compare */

'use strict';

var hasOwnProperty = Object.prototype.hasOwnProperty;

/**
 * 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) {
  // SameValue algorithm
  if (x === y) {
    // Steps 1-5, 7-10
    // Steps 6.b-6.e: +0 != -0
    // Added the nonzero y check to make Flow happy, but it is redundant
    return x !== 0 || y !== 0 || 1 / x === 1 / y;
  } else {
    // Step 6.a: NaN == NaN
    return x !== x && y !== y;
  }
}

/**
 * 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;
}

module.exports = shallowEqual;
},{}],8:[function(require,module,exports){
/**
 * 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.
 *
 */

'use strict';

var emptyFunction = require('./emptyFunction');

/**
 * 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;

if ("development" !== 'production') {
  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));
    }
  };
}

module.exports = warning;
},{"./emptyFunction":4}],9:[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;
};

},{}],10:[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';

if ("development" !== 'production') {
  var invariant = require('fbjs/lib/invariant');
  var warning = require('fbjs/lib/warning');
  var ReactPropTypesSecret = require('./lib/ReactPropTypesSecret');
  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) {
  if ("development" !== 'production') {
    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(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);
        } catch (ex) {
          error = ex;
        }
        warning(!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(false, 'Failed %s type: %s%s', location, error.message, stack != null ? stack : '');
        }
      }
    }
  }
}

module.exports = checkPropTypes;

},{"./lib/ReactPropTypesSecret":11,"fbjs/lib/invariant":6,"fbjs/lib/warning":8}],11:[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;

},{}]},{},[3])(3)
});PK
!<r��m2z2z>chrome/devtools/modules/devtools/client/shared/vendor/react.js/** @license React v16.2.0
 * react.production.min.js
 *
 * 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 (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';

/*
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;
};

// TODO: this is special because it gets imported during build.

var ReactVersion = '16.2.0';

// 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_CALL_TYPE = hasSymbol ? Symbol['for']('react.call') : 0xeac8;
var REACT_RETURN_TYPE = hasSymbol ? Symbol['for']('react.return') : 0xeac9;
var REACT_PORTAL_TYPE = hasSymbol ? Symbol['for']('react.portal') : 0xeaca;
var REACT_FRAGMENT_TYPE = hasSymbol ? Symbol['for']('react.fragment') : 0xeacb;

var MAYBE_ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator;
var FAUX_ITERATOR_SYMBOL = '@@iterator';

function getIteratorFn(maybeIterable) {
  if (maybeIterable === null || typeof maybeIterable === 'undefined') {
    return null;
  }
  var maybeIterator = MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL] || maybeIterable[FAUX_ITERATOR_SYMBOL];
  if (typeof maybeIterator === 'function') {
    return maybeIterator;
  }
  return null;
}

/**
 * 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 message = 'Minified React error #' + code + '; visit ' + 'http://facebook.github.io/react/docs/error-decoder.html?invariant=' + code;

  for (var argIdx = 0; argIdx < argCount; argIdx++) {
    message += '&args[]=' + encodeURIComponent(arguments[argIdx + 1]);
  }

  message += ' for the full message or use the non-minified dev environment' + ' for full errors and additional helpful warnings.';

  var error = new Error(message);
  error.name = 'Invariant Violation';
  error.framesToPop = 1; // we don't care about reactProdInvariant's own frame

  throw error;
}

/**
 * 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 = {};

var emptyObject_1 = emptyObject;

/**
 * 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.
 *
 */

/**
 * 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.
 */

/**
 * 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) 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.
 *
 */

/**
 * 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) {
    
  }
};

/**
 * Base class helpers for the updating state of a component.
 */
function Component(props, context, updater) {
  this.props = props;
  this.context = context;
  this.refs = emptyObject_1;
  // 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.
 */
/**
 * Base class helpers for the updating state of a component.
 */
function PureComponent(props, context, updater) {
  // Duplicated from Component.
  this.props = props;
  this.context = context;
  this.refs = emptyObject_1;
  // We initialize the default updater but the real one gets injected by the
  // renderer.
  this.updater = updater || ReactNoopUpdateQueue;
}

function ComponentDummy() {}
ComponentDummy.prototype = Component.prototype;
var pureComponentPrototype = PureComponent.prototype = new ComponentDummy();
pureComponentPrototype.constructor = PureComponent;
// Avoid an extra prototype jump for these methods.
objectAssign(pureComponentPrototype, Component.prototype);
pureComponentPrototype.isPureReactComponent = true;

function AsyncComponent(props, context, updater) {
  // Duplicated from Component.
  this.props = props;
  this.context = context;
  this.refs = emptyObject_1;
  // We initialize the default updater but the real one gets injected by the
  // renderer.
  this.updater = updater || ReactNoopUpdateQueue;
}

var asyncComponentPrototype = AsyncComponent.prototype = new ComponentDummy();
asyncComponentPrototype.constructor = AsyncComponent;
// Avoid an extra prototype jump for these methods.
objectAssign(asyncComponentPrototype, Component.prototype);
asyncComponentPrototype.unstable_isAsyncReactComponent = true;
asyncComponentPrototype.render = function () {
  return this.props.children;
};

/**
 * 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 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 allow 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;

  // 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 TODO: Warn if this is accessed
  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) {
  var propName;

  // 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;
    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 valid component.
 * @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_CALL_TYPE:
          case REACT_RETURN_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;
  var nextName;
  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;
      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#react.children.foreach
 *
 * 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, emptyFunction_1.thatReturnsArgument);
  } 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#react.children.map
 *
 * 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#react.children.count
 *
 * @param {?*} children Children tree container.
 * @return {number} The number of children.
 */
function countChildren(children, context) {
  return traverseAllChildren(children, emptyFunction_1.thatReturnsNull, 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#react.children.toarray
 */
function toArray(children) {
  var result = [];
  mapIntoWithKeyPrefixInternal(children, result, null, emptyFunction_1.thatReturnsArgument);
  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#react.children.only
 *
 * 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;
}

/**
 * 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
  },

  Component: Component,
  PureComponent: PureComponent,
  unstable_AsyncComponent: AsyncComponent,

  Fragment: REACT_FRAGMENT_TYPE,

  createElement: createElement,
  cloneElement: cloneElement,
  createFactory: createFactory,
  isValidElement: isValidElement,

  version: ReactVersion,

  __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {
    ReactCurrentOwner: ReactCurrentOwner,
    // Used by renderers to avoid bundling object-assign twice in UMD bundles:
    assign: objectAssign
  }
};



var React$2 = Object.freeze({
	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['default'] : React$3;

return react;

})));
PK
!<��E�zhzh>chrome/devtools/modules/devtools/client/shared/vendor/redux.js/**
 * react-redux v3.7.2
 */

(function (global, factory) {
	typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
	typeof define === 'function' && define.amd ? define(['exports'], factory) :
	(factory((global.Redux = global.Redux || {})));
}(this, (function (exports) { 'use strict';

/** 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')();

/** Built-in value references. */
var Symbol = root.Symbol;

/** Used for built-in method references. */
var objectProto$1 = Object.prototype;

/** Used to check objects for own properties. */
var hasOwnProperty$1 = objectProto$1.hasOwnProperty;

/**
 * Used to resolve the
 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
 * of values.
 */
var nativeObjectToString = objectProto$1.toString;

/** Built-in value references. */
var symToStringTag$1 = Symbol ? Symbol.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$1),
      tag = value[symToStringTag$1];

  try {
    value[symToStringTag$1] = undefined;
    var unmasked = true;
  } catch (e) {}

  var result = nativeObjectToString.call(value);
  if (unmasked) {
    if (isOwn) {
      value[symToStringTag$1] = tag;
    } else {
      delete value[symToStringTag$1];
    }
  }
  return result;
}

/** Used for built-in method references. */
var objectProto$2 = 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$2.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]';
var undefinedTag = '[object Undefined]';

/** Built-in value references. */
var symToStringTag = Symbol ? Symbol.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 && symToStringTag 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;
var objectProto = Object.prototype;

/** 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 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.call(proto, 'constructor') && proto.constructor;
  return typeof Ctor == 'function' && Ctor instanceof Ctor &&
    funcToString.call(Ctor) == objectCtorString;
}

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$2;

if (typeof self !== 'undefined') {
  root$2 = self;
} else if (typeof window !== 'undefined') {
  root$2 = window;
} else if (typeof global !== 'undefined') {
  root$2 = global;
} else if (typeof module !== 'undefined') {
  root$2 = module;
} else {
  root$2 = Function('return this')();
}

var result = symbolObservablePonyfill(root$2);

/**
 * 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 ActionTypes = {
  INIT: '@@redux/INIT'

  /**
   * 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 === '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;

  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() {
    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 listener to be a function.');
    }

    var isSubscribed = true;

    ensureCanMutateNextListeners();
    nextListeners.push(listener);

    return function unsubscribe() {
      if (!isSubscribed) {
        return;
      }

      isSubscribed = false;

      ensureCanMutateNextListeners();
      var index = nextListeners.indexOf(listener);
      nextListeners.splice(index, 1);
    };
  }

  /**
   * 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;
    dispatch({ type: ActionTypes.INIT });
  }

  /**
   * 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') {
          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);
    /* eslint-disable no-empty */
  } catch (e) {}
  /* eslint-enable no-empty */
}

function getUndefinedStateErrorMessage(key, action) {
  var actionType = action && action.type;
  var actionName = actionType && '"' + actionType.toString() + '"' || 'an action';

  return 'Given action ' + actionName + ', 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 (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.');
    }

    var type = '@@redux/PROBE_UNKNOWN_ACTION_' + Math.random().toString(36).substring(7).split('').join('.');
    if (typeof reducer(undefined, { type: type }) === '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);

  var unexpectedKeyCache = void 0;
  {
    unexpectedKeyCache = {};
  }

  var shapeAssertionError = void 0;
  try {
    assertReducerShape(finalReducers);
  } catch (e) {
    shapeAssertionError = e;
  }

  return function combination() {
    var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
    var action = arguments[1];

    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;
    }
    return hasChanged ? nextState : state;
  };
}

function bindActionCreator(actionCreator, dispatch) {
  return function () {
    return dispatch(actionCreator.apply(undefined, 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 a single function as the first argument,
 * and get a 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 keys = Object.keys(actionCreators);
  var boundActionCreators = {};
  for (var i = 0; i < keys.length; i++) {
    var key = keys[i];
    var actionCreator = actionCreators[key];
    if (typeof actionCreator === 'function') {
      boundActionCreators[key] = bindActionCreator(actionCreator, dispatch);
    }
  }
  return boundActionCreators;
}

/**
 * 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 = 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(undefined, arguments));
    };
  });
}

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; };

/**
 * 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 = Array(_len), _key = 0; _key < _len; _key++) {
    middlewares[_key] = arguments[_key];
  }

  return function (createStore) {
    return function (reducer, preloadedState, enhancer) {
      var store = createStore(reducer, preloadedState, enhancer);
      var _dispatch = store.dispatch;
      var chain = [];

      var middlewareAPI = {
        getState: store.getState,
        dispatch: function dispatch(action) {
          return _dispatch(action);
        }
      };
      chain = middlewares.map(function (middleware) {
        return middleware(middlewareAPI);
      });
      _dispatch = compose.apply(undefined, chain)(store.dispatch);

      return _extends({}, 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 ("development" !== 'production' && 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 DefinePlugin for webpack (http://stackoverflow.com/questions/30030031) ' + 'to ensure you have the correct code for your production build.');
}

exports.createStore = createStore;
exports.combineReducers = combineReducers;
exports.bindActionCreators = bindActionCreators;
exports.applyMiddleware = applyMiddleware;
exports.compose = compose;

Object.defineProperty(exports, '__esModule', { value: true });

})));
PK
!<�a7�SSAchrome/devtools/modules/devtools/client/shared/vendor/reselect.js(function (global, factory) {
  if (typeof define === "function" && define.amd) {
    define('Reselect', ['exports'], factory);
  } else if (typeof exports !== "undefined") {
    factory(exports);
  } else {
    var mod = {
      exports: {}
    };
    factory(mod.exports);
    global.Reselect = mod.exports;
  }
})(this, function (exports) {
  'use strict';

  exports.__esModule = true;
  exports.defaultMemoize = defaultMemoize;
  exports.createSelectorCreator = createSelectorCreator;
  exports.createStructuredSelector = createStructuredSelector;
  function defaultEqualityCheck(a, b) {
    return a === b;
  }

  function areArgumentsShallowlyEqual(equalityCheck, 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;
  }

  function defaultMemoize(func) {
    var equalityCheck = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : defaultEqualityCheck;

    var lastArgs = null;
    var lastResult = null;
    // we reference arguments instead of spreading them for performance reasons
    return function () {
      if (!areArgumentsShallowlyEqual(equalityCheck, lastArgs, arguments)) {
        // apply arguments instead of spreading for performance.
        lastResult = func.apply(null, arguments);
      }

      lastArgs = arguments;
      return lastResult;
    };
  }

  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;
      }).join(', ');
      throw new Error('Selector creators expect all input-selectors to be functions, ' + ('instead received the following types: [' + dependencyTypes + ']'));
    }

    return dependencies;
  }

  function createSelectorCreator(memoize) {
    for (var _len = arguments.length, memoizeOptions = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
      memoizeOptions[_key - 1] = arguments[_key];
    }

    return function () {
      for (var _len2 = arguments.length, funcs = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
        funcs[_key2] = arguments[_key2];
      }

      var recomputations = 0;
      var resultFunc = funcs.pop();
      var dependencies = getDependencies(funcs);

      var memoizedResultFunc = memoize.apply(undefined, [function () {
        recomputations++;
        // apply arguments instead of spreading for performance.
        return resultFunc.apply(null, arguments);
      }].concat(memoizeOptions));

      // If a selector is called with the exact same arguments we don't need to traverse our dependencies again.
      var selector = defaultMemoize(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.
          params.push(dependencies[i].apply(null, arguments));
        }

        // apply arguments instead of spreading for performance.
        return memoizedResultFunc.apply(null, params);
      });

      selector.resultFunc = resultFunc;
      selector.recomputations = function () {
        return recomputations;
      };
      selector.resetRecomputations = function () {
        return recomputations = 0;
      };
      return selector;
    };
  }

  var createSelector = exports.createSelector = createSelectorCreator(defaultMemoize);

  function createStructuredSelector(selectors) {
    var selectorCreator = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 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);
    return selectorCreator(objectKeys.map(function (key) {
      return selectors[key];
    }), function () {
      for (var _len3 = arguments.length, values = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
        values[_key3] = arguments[_key3];
      }

      return values.reduce(function (composition, value, index) {
        composition[objectKeys[index]] = value;
        return composition;
      }, {});
    });
  }
});
PK
!<�wM�1�1Kchrome/devtools/modules/devtools/client/shared/vendor/seamless-immutable.js(function(){
  "use strict";

  function addPropertyTo(target, methodName, value) {
    Object.defineProperty(target, methodName, {
      enumerable: false,
      configurable: false,
      writable: false,
      value: value
    });
  }

  function banProperty(target, methodName) {
    addPropertyTo(target, methodName, function() {
      throw new ImmutableError("The " + methodName +
        " method cannot be invoked on an Immutable data structure.");
    });
  }

  var immutabilityTag = "__immutable_invariants_hold";

  function addImmutabilityTag(target) {
    addPropertyTo(target, immutabilityTag, true);
  }

  function isImmutable(target) {
    if (typeof target === "object") {
      return target === null || target.hasOwnProperty(immutabilityTag);
    } else {
      // In JavaScript, only objects are even potentially mutable.
      // strings, numbers, null, and undefined are all naturally immutable.
      return true;
    }
  }

  function isMergableObject(target) {
    return target !== null && typeof target === "object" && !(target instanceof Array) && !(target instanceof Date);
  }

  var mutatingObjectMethods = [
    "setPrototypeOf"
  ];

  var nonMutatingObjectMethods = [
    "keys"
  ];

  var mutatingArrayMethods = mutatingObjectMethods.concat([
    "push", "pop", "sort", "splice", "shift", "unshift", "reverse"
  ]);

  var nonMutatingArrayMethods = nonMutatingObjectMethods.concat([
    "map", "filter", "slice", "concat", "reduce", "reduceRight"
  ]);

  function ImmutableError(message) {
    var err       = new Error(message);
    err.__proto__ = ImmutableError;

    return err;
  }
  ImmutableError.prototype = Error.prototype;

  function makeImmutable(obj, bannedMethods) {
    // Tag it so we can quickly tell it's immutable later.
    addImmutabilityTag(obj);

    if ("development" === "development") {
      // Make all mutating methods throw exceptions.
      for (var index in bannedMethods) {
        if (bannedMethods.hasOwnProperty(index)) {
          banProperty(obj, bannedMethods[index]);
        }
      }

      // Freeze it and return it.
      Object.freeze(obj);
    }

    return obj;
  }

  function makeMethodReturnImmutable(obj, methodName) {
    var currentMethod = obj[methodName];

    addPropertyTo(obj, methodName, function() {
      return Immutable(currentMethod.apply(obj, arguments));
    });
  }

  function makeImmutableArray(array) {
    // Don't change their implementations, but wrap these functions to make sure
    // they always return an immutable value.
    for (var index in nonMutatingArrayMethods) {
      if (nonMutatingArrayMethods.hasOwnProperty(index)) {
        var methodName = nonMutatingArrayMethods[index];
        makeMethodReturnImmutable(array, methodName);
      }
    }

    addPropertyTo(array, "flatMap",  flatMap);
    addPropertyTo(array, "asObject", asObject);
    addPropertyTo(array, "asMutable", asMutableArray);

    for(var i = 0, length = array.length; i < length; i++) {
      array[i] = Immutable(array[i]);
    }

    return makeImmutable(array, mutatingArrayMethods);
  }

  /**
   * Effectively performs a map() over the elements in the array, using the
   * provided iterator, except that whenever the iterator returns an array, that
   * array's elements are added to the final result instead of the array itself.
   *
   * @param {function} iterator - The iterator function that will be invoked on each element in the array. It will receive three arguments: the current value, the current index, and the current object.
   */
  function flatMap(iterator) {
    // Calling .flatMap() with no arguments is a no-op. Don't bother cloning.
    if (arguments.length === 0) {
      return this;
    }

    var result = [],
        length = this.length,
        index;

    for (index = 0; index < length; index++) {
      var iteratorResult = iterator(this[index], index, this);

      if (iteratorResult instanceof Array) {
        // Concatenate Array results into the return value we're building up.
        result.push.apply(result, iteratorResult);
      } else {
        // Handle non-Array results the same way map() does.
        result.push(iteratorResult);
      }
    }

    return makeImmutableArray(result);
  }

  /**
   * Returns an Immutable copy of the object without the given keys included.
   *
   * @param {array} keysToRemove - A list of strings representing the keys to exclude in the return value. Instead of providing a single array, this method can also be called by passing multiple strings as separate arguments.
   */
  function without(keysToRemove) {
    // Calling .without() with no arguments is a no-op. Don't bother cloning.
    if (arguments.length === 0) {
      return this;
    }

    // If we weren't given an array, use the arguments list.
    if (!(keysToRemove instanceof Array)) {
      keysToRemove = Array.prototype.slice.call(arguments);
    }

    var result = this.instantiateEmptyObject();

    for (var key in this) {
      if (this.hasOwnProperty(key) && (keysToRemove.indexOf(key) === -1)) {
        result[key] = this[key];
      }
    }

    return makeImmutableObject(result,
      {instantiateEmptyObject: this.instantiateEmptyObject});
  }

  function asMutableArray(opts) {
    var result = [], i, length;

    if(opts && opts.deep) {
      for(i = 0, length = this.length; i < length; i++) {
        result.push( asDeepMutable(this[i]) );
      }
    } else {
      for(i = 0, length = this.length; i < length; i++) {
        result.push(this[i]);
      }
    }

    return result;
  }

  /**
   * Effectively performs a [map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) over the elements in the array, expecting that the iterator function
   * will return an array of two elements - the first representing a key, the other
   * a value. Then returns an Immutable Object constructed of those keys and values.
   *
   * @param {function} iterator - A function which should return an array of two elements - the first representing the desired key, the other the desired value.
   */
  function asObject(iterator) {
    // If no iterator was provided, assume the identity function
    // (suggesting this array is already a list of key/value pairs.)
    if (typeof iterator !== "function") {
      iterator = function(value) { return value; };
    }

    var result = {},
        length = this.length,
        index;

    for (index = 0; index < length; index++) {
      var pair  = iterator(this[index], index, this),
          key   = pair[0],
          value = pair[1];

      result[key] = value;
    }

    return makeImmutableObject(result);
  }

  function asDeepMutable(obj) {
    if(!obj || !obj.hasOwnProperty(immutabilityTag) || obj instanceof Date) { return obj; }
    return obj.asMutable({deep: true});
  }

  function quickCopy(src, dest) {
    for (var key in src) {
      if (src.hasOwnProperty(key)) {
        dest[key] = src[key];
      }
    }

    return dest;
  }

  /**
   * Returns an Immutable Object containing the properties and values of both
   * this object and the provided object, prioritizing the provided object's
   * values whenever the same key is present in both objects.
   *
   * @param {object} other - The other object to merge. Multiple objects can be passed as an array. In such a case, the later an object appears in that list, the higher its priority.
   * @param {object} config - Optional config object that contains settings. Supported settings are: {deep: true} for deep merge and {merger: mergerFunc} where mergerFunc is a function
   *                          that takes a property from both objects. If anything is returned it overrides the normal merge behaviour.
   */
  function merge(other, config) {
    // Calling .merge() with no arguments is a no-op. Don't bother cloning.
    if (arguments.length === 0) {
      return this;
    }

    if (other === null || (typeof other !== "object")) {
      throw new TypeError("Immutable#merge can only be invoked with objects or arrays, not " + JSON.stringify(other));
    }

    var anyChanges    = false,
        result        = quickCopy(this, this.instantiateEmptyObject()), // A shallow clone of this object.
        receivedArray = (other instanceof Array),
        deep          = config && config.deep,
        merger        = config && config.merger,
        key;

    // Use the given key to extract a value from the given object, then place
    // that value in the result object under the same key. If that resulted
    // in a change from this object's value at that key, set anyChanges = true.
    function addToResult(currentObj, otherObj, key) {
      var immutableValue = Immutable(otherObj[key]);
      var mergerResult = merger && merger(currentObj[key], immutableValue, config);
      if (merger && mergerResult && mergerResult === currentObj[key]) return;

      anyChanges = anyChanges ||
        mergerResult !== undefined ||
        (!currentObj.hasOwnProperty(key) ||
        ((immutableValue !== currentObj[key]) &&
          // Avoid false positives due to (NaN !== NaN) evaluating to true
          (immutableValue === immutableValue)));

      if (mergerResult) {
        result[key] = mergerResult;
      } else if (deep && isMergableObject(currentObj[key]) && isMergableObject(immutableValue)) {
        result[key] = currentObj[key].merge(immutableValue, config);
      } else {
        result[key] = immutableValue;
      }
    }

    // Achieve prioritization by overriding previous values that get in the way.
    if (!receivedArray) {
      // The most common use case: just merge one object into the existing one.
      for (key in other) {
        if (other.hasOwnProperty(key)) {
          addToResult(this, other, key);
        }
      }
    } else {
      // We also accept an Array
      for (var index=0; index < other.length; index++) {
        var otherFromArray = other[index];

        for (key in otherFromArray) {
          if (otherFromArray.hasOwnProperty(key)) {
            addToResult(this, otherFromArray, key);
          }
        }
      }
    }

    if (anyChanges) {
      return makeImmutableObject(result,
        {instantiateEmptyObject: this.instantiateEmptyObject});
    } else {
      return this;
    }
  }

  function asMutableObject(opts) {
    var result = this.instantiateEmptyObject(), key;

    if(opts && opts.deep) {
      for (key in this) {
        if (this.hasOwnProperty(key)) {
          result[key] = asDeepMutable(this[key]);
        }
      }
    } else {
      for (key in this) {
        if (this.hasOwnProperty(key)) {
          result[key] = this[key];
        }
      }
    }

    return result;
  }

  // Creates plain object to be used for cloning
  function instantiatePlainObject() {
    return {};
  }

  // Finalizes an object with immutable methods, freezes it, and returns it.
  function makeImmutableObject(obj, options) {
    var instantiateEmptyObject =
      (options && options.instantiateEmptyObject) ?
        options.instantiateEmptyObject : instantiatePlainObject;

    addPropertyTo(obj, "merge", merge);
    addPropertyTo(obj, "without", without);
    addPropertyTo(obj, "asMutable", asMutableObject);
    addPropertyTo(obj, "instantiateEmptyObject", instantiateEmptyObject);

    return makeImmutable(obj, mutatingObjectMethods);
  }

  function Immutable(obj, options) {
    if (isImmutable(obj)) {
      return obj;
    } else if (obj instanceof Array) {
      return makeImmutableArray(obj.slice());
    } else if (obj instanceof Date) {
      return makeImmutable(new Date(obj.getTime()));
    } else {
      // Don't freeze the object we were given; make a clone and use that.
      var prototype = options && options.prototype;
      var instantiateEmptyObject =
        (!prototype || prototype === Object.prototype) ?
          instantiatePlainObject : (function() { return Object.create(prototype); });
      var clone = instantiateEmptyObject();

      for (var key in obj) {
        if (obj.hasOwnProperty(key)) {
          clone[key] = Immutable(obj[key]);
        }
      }

      return makeImmutableObject(clone,
        {instantiateEmptyObject: instantiateEmptyObject});
    }
  }

  // Export the library
  Immutable.isImmutable    = isImmutable;
  Immutable.ImmutableError = ImmutableError;

  Object.freeze(Immutable);

  /* istanbul ignore if */
  if (typeof module === "object") {
    module.exports = Immutable;
  } else if (typeof exports === "object") {
    exports.Immutable = Immutable;
  } else if (typeof window === "object") {
    window.Immutable = Immutable;
  } else if (typeof global === "object") {
    global.Immutable = Immutable;
  }
})();
PK
!<��|�22Tchrome/devtools/modules/devtools/client/shared/vendor/stringvalidator/util/assert.js// // based on node assert, original notice:
// // NB: The URL to the CommonJS spec is kept just for tradition.
// //     node-assert has evolved a lot since then, both in API and behavior.
//
// // http://wiki.commonjs.org/wiki/Unit_Testing/1.0
// //
// // THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8!
// //
// // Originally from narwhal.js (http://narwhaljs.org)
// // Copyright (c) 2009 Thomas Robinson <280north.com>
// //
// // 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 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.

"use strict";

var regex = /\s*function\s+([^\(\s]*)\s*/;

var functionsHaveNames = (function () {
  return function foo() {}.name === "foo";
}());

function assert(value, message) {
  if (!value) {
    fail(value, true, message, "==", assert.ok);
  }
}

assert.equal = function equal(actual, expected, message) {
  if (actual != expected) {
    fail(actual, expected, message, "==", assert.equal);
  }
};

assert.throws = function (block, error, message) {
  _throws(true, block, error, message);
};

function _throws(shouldThrow, block, expected, message) {
  var actual;

  if (typeof block !== "function") {
    throw new TypeError(`"block" argument must be a function`);
  }

  if (typeof expected === "string") {
    message = expected;
    expected = null;
  }

  actual = _tryBlock(block);

  message = (expected && expected.name ? " (" + expected.name + ")." : ".") +
            (message ? " " + message : ".");

  if (shouldThrow && !actual) {
    fail(actual, expected, "Missing expected exception" + message);
  }

  var userProvidedMessage = typeof message === "string";
  var isUnwantedException = !shouldThrow && isError(actual);
  var isUnexpectedException = !shouldThrow && actual && !expected;

  if ((isUnwantedException &&
      userProvidedMessage &&
      expectedException(actual, expected)) ||
      isUnexpectedException) {
    fail(actual, expected, "Got unwanted exception" + message);
  }

  if ((shouldThrow && actual && expected &&
      !expectedException(actual, expected)) || (!shouldThrow && actual)) {
    throw actual;
  }
}

function fail(actual, expected, message, operator, stackStartFunction) {
  throw new assert.AssertionError({
    message: message,
    actual: actual,
    expected: expected,
    operator: operator,
    stackStartFunction: stackStartFunction
  });
}

assert.fail = fail;

assert.AssertionError = function AssertionError(options) {
  this.name = "AssertionError";
  this.actual = options.actual;
  this.expected = options.expected;
  this.operator = options.operator;
  if (options.message) {
    this.message = options.message;
    this.generatedMessage = false;
  } else {
    this.message = getMessage(this);
    this.generatedMessage = true;
  }
  var stackStartFunction = options.stackStartFunction || fail;
  if (Error.captureStackTrace) {
    Error.captureStackTrace(this, stackStartFunction);
  } else {
    // non v8 browsers so we can have a stacktrace
    var err = new Error();
    if (err.stack) {
      var out = err.stack;

      // try to strip useless frames
      var fn_name = getName(stackStartFunction);
      var idx = out.indexOf("\n" + fn_name);
      if (idx >= 0) {
        // once we have located the function frame
        // we need to strip out everything before it (and its line)
        var next_line = out.indexOf("\n", idx + 1);
        out = out.substring(next_line + 1);
      }

      this.stack = out;
    }
  }
};

function expectedException(actual, expected) {
  if (!actual || !expected) {
    return false;
  }

  if (Object.prototype.toString.call(expected) == "[object RegExp]") {
    return expected.test(actual);
  }

  try {
    if (actual instanceof expected) {
      return true;
    }
  } catch (e) {
    // Ignore.  The instanceof check doesn"t work for arrow functions.
  }

  if (Error.isPrototypeOf(expected)) {
    return false;
  }

  return expected.call({}, actual) === true;
}

function _tryBlock(block) {
  var error;
  try {
    block();
  } catch (e) {
    error = e;
  }
  return error;
}

function isError(obj) {
  return obj instanceof Error;
}

function isFunction(value) {
  return typeof value === "function";
}

function getMessage(self) {
  return truncate(inspect(self.actual), 128) + " " +
         self.operator + " " +
         truncate(inspect(self.expected), 128);
}

function getName(func) {
  if (!isFunction(func)) {
    return null;
  }
  if (functionsHaveNames) {
    return func.name;
  }
  var str = func.toString();
  var match = str.match(regex);
  return match && match[1];
}

function truncate(s, n) {
  if (typeof s === "string") {
    return s.length < n ? s : s.slice(0, n);
  }
  return s;
}

function inspect(something) {
  if (functionsHaveNames || !isFunction(something)) {
    throw new Error(something);
  }
  var rawname = getName(something);
  var name = rawname ? ": " + rawname : "";
  return "[Function" + name + "]";
}

exports.assert = assert;
PK
!<.��p�p�Rchrome/devtools/modules/devtools/client/shared/vendor/stringvalidator/validator.js/*
 * Copyright (c) 2016 Chris O"Hara <[email protected]>
 *
 * 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.
 *
 * NOTE: This utility is derived from https://github.com/chriso/validator.js but it is
 *       **NOT** the same as the original. We have made the following changes:
 *         - Changed mocha tests to xpcshell based tests.
 *         - Merged the following pull requests:
 *           - [isMobileNumber] Added Lithuanian number pattern #667
 *           - Hongkong mobile number #665
 *           - Added option to validate any phone locale #663
 *           - Added validation for ISRC strings #660
 *         - Added isRFC5646 for rfc 5646 #572
 *         - Added isSemVer for version numbers.
 *         - Added isRGBColor for RGB colors.
 *
 * UPDATING: PLEASE FOLLOW THE INSTRUCTIONS INSIDE UPDATING.md
 */

"use strict";

(function (global, factory) {
      typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
      typeof define === 'function' && define.amd ? define(factory) :
      (global.validator = factory());
}(this, function () { 'use strict';

      function assertString(input) {
        if (typeof input !== 'string') {
          throw new TypeError('This library (validator.js) validates strings only');
        }
      }

      function toDate(date) {
        assertString(date);
        date = Date.parse(date);
        return !isNaN(date) ? new Date(date) : null;
      }

      function toFloat(str) {
        assertString(str);
        return parseFloat(str);
      }

      function toInt(str, radix) {
        assertString(str);
        return parseInt(str, radix || 10);
      }

      function toBoolean(str, strict) {
        assertString(str);
        if (strict) {
          return str === '1' || str === 'true';
        }
        return str !== '0' && str !== 'false' && str !== '';
      }

      function equals(str, comparison) {
        assertString(str);
        return str === comparison;
      }

      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;
      };

      var asyncGenerator = function () {
        function AwaitValue(value) {
          this.value = value;
        }

        function AsyncGenerator(gen) {
          var front, back;

          function send(key, arg) {
            return new Promise(function (resolve, reject) {
              var request = {
                key: key,
                arg: arg,
                resolve: resolve,
                reject: reject,
                next: null
              };

              if (back) {
                back = back.next = request;
              } else {
                front = back = request;
                resume(key, arg);
              }
            });
          }

          function resume(key, arg) {
            try {
              var result = gen[key](arg);
              var value = result.value;

              if (value instanceof AwaitValue) {
                Promise.resolve(value.value).then(function (arg) {
                  resume("next", arg);
                }, function (arg) {
                  resume("throw", arg);
                });
              } else {
                settle(result.done ? "return" : "normal", result.value);
              }
            } catch (err) {
              settle("throw", err);
            }
          }

          function settle(type, value) {
            switch (type) {
              case "return":
                front.resolve({
                  value: value,
                  done: true
                });
                break;

              case "throw":
                front.reject(value);
                break;

              default:
                front.resolve({
                  value: value,
                  done: false
                });
                break;
            }

            front = front.next;

            if (front) {
              resume(front.key, front.arg);
            } else {
              back = null;
            }
          }

          this._invoke = send;

          if (typeof gen.return !== "function") {
            this.return = undefined;
          }
        }

        if (typeof Symbol === "function" && Symbol.asyncIterator) {
          AsyncGenerator.prototype[Symbol.asyncIterator] = function () {
            return this;
          };
        }

        AsyncGenerator.prototype.next = function (arg) {
          return this._invoke("next", arg);
        };

        AsyncGenerator.prototype.throw = function (arg) {
          return this._invoke("throw", arg);
        };

        AsyncGenerator.prototype.return = function (arg) {
          return this._invoke("return", arg);
        };

        return {
          wrap: function (fn) {
            return function () {
              return new AsyncGenerator(fn.apply(this, arguments));
            };
          },
          await: function (value) {
            return new AwaitValue(value);
          }
        };
      }();

      function toString(input) {
        if ((typeof input === 'undefined' ? 'undefined' : _typeof(input)) === 'object' && input !== null) {
          if (typeof input.toString === 'function') {
            input = input.toString();
          } else {
            input = '[object Object]';
          }
        } else if (input === null || typeof input === 'undefined' || isNaN(input) && !input.length) {
          input = '';
        }
        return String(input);
      }

      function contains(str, elem) {
        assertString(str);
        return str.indexOf(toString(elem)) >= 0;
      }

      function matches(str, pattern, modifiers) {
        assertString(str);
        if (Object.prototype.toString.call(pattern) !== '[object RegExp]') {
          pattern = new RegExp(pattern, modifiers);
        }
        return pattern.test(str);
      }

      function merge() {
        var obj = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
        var defaults = arguments[1];

        for (var key in defaults) {
          if (typeof obj[key] === 'undefined') {
            obj[key] = defaults[key];
          }
        }
        return obj;
      }

      /* eslint-disable prefer-rest-params */
      function isByteLength(str, options) {
        assertString(str);
        var min = void 0;
        var max = void 0;
        if ((typeof options === 'undefined' ? 'undefined' : _typeof(options)) === 'object') {
          min = options.min || 0;
          max = options.max;
        } else {
          // backwards compatibility: isByteLength(str, min [, max])
          min = arguments[1];
          max = arguments[2];
        }
        var len = encodeURI(str).split(/%..|./).length - 1;
        return len >= min && (typeof max === 'undefined' || len <= max);
      }

      var default_fqdn_options = {
        require_tld: true,
        allow_underscores: false,
        allow_trailing_dot: false
      };

      function isFDQN(str, options) {
        assertString(str);
        options = merge(options, default_fqdn_options);

        /* Remove the optional trailing dot before checking validity */
        if (options.allow_trailing_dot && str[str.length - 1] === '.') {
          str = str.substring(0, str.length - 1);
        }
        var parts = str.split('.');
        if (options.require_tld) {
          var tld = parts.pop();
          if (!parts.length || !/^([a-z\u00a1-\uffff]{2,}|xn[a-z0-9-]{2,})$/i.test(tld)) {
            return false;
          }
        }
        for (var part, i = 0; i < parts.length; i++) {
          part = parts[i];
          if (options.allow_underscores) {
            part = part.replace(/_/g, '');
          }
          if (!/^[a-z\u00a1-\uffff0-9-]+$/i.test(part)) {
            return false;
          }
          if (/[\uff01-\uff5e]/.test(part)) {
            // disallow full-width chars
            return false;
          }
          if (part[0] === '-' || part[part.length - 1] === '-') {
            return false;
          }
        }
        return true;
      }

      var default_email_options = {
        allow_display_name: false,
        require_display_name: false,
        allow_utf8_local_part: true,
        require_tld: true
      };

      /* eslint-disable max-len */
      /* eslint-disable no-control-regex */
      var displayName = /^[a-z\d!#\$%&'\*\+\-\/=\?\^_`{\|}~\.\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+[a-z\d!#\$%&'\*\+\-\/=\?\^_`{\|}~\.\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF\s]*<(.+)>$/i;
      var emailUserPart = /^[a-z\d!#\$%&'\*\+\-\/=\?\^_`{\|}~]+$/i;
      var quotedEmailUser = /^([\s\x01-\x08\x0b\x0c\x0e-\x1f\x7f\x21\x23-\x5b\x5d-\x7e]|(\\[\x01-\x09\x0b\x0c\x0d-\x7f]))*$/i;
      var emailUserUtf8Part = /^[a-z\d!#\$%&'\*\+\-\/=\?\^_`{\|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+$/i;
      var quotedEmailUserUtf8 = /^([\s\x01-\x08\x0b\x0c\x0e-\x1f\x7f\x21\x23-\x5b\x5d-\x7e\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|(\\[\x01-\x09\x0b\x0c\x0d-\x7f\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))*$/i;
      /* eslint-enable max-len */
      /* eslint-enable no-control-regex */

      function isEmail(str, options) {
        assertString(str);
        options = merge(options, default_email_options);

        if (options.require_display_name || options.allow_display_name) {
          var display_email = str.match(displayName);
          if (display_email) {
            str = display_email[1];
          } else if (options.require_display_name) {
            return false;
          }
        }

        var parts = str.split('@');
        var domain = parts.pop();
        var user = parts.join('@');

        var lower_domain = domain.toLowerCase();
        if (lower_domain === 'gmail.com' || lower_domain === 'googlemail.com') {
          user = user.replace(/\./g, '').toLowerCase();
        }

        if (!isByteLength(user, { max: 64 }) || !isByteLength(domain, { max: 256 })) {
          return false;
        }

        if (!isFDQN(domain, { require_tld: options.require_tld })) {
          return false;
        }

        if (user[0] === '"') {
          user = user.slice(1, user.length - 1);
          return options.allow_utf8_local_part ? quotedEmailUserUtf8.test(user) : quotedEmailUser.test(user);
        }

        var pattern = options.allow_utf8_local_part ? emailUserUtf8Part : emailUserPart;

        var user_parts = user.split('.');
        for (var i = 0; i < user_parts.length; i++) {
          if (!pattern.test(user_parts[i])) {
            return false;
          }
        }

        return true;
      }

      var ipv4Maybe = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
      var ipv6Block = /^[0-9A-F]{1,4}$/i;

      function isIP(str) {
        var version = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';

        assertString(str);
        version = String(version);
        if (!version) {
          return isIP(str, 4) || isIP(str, 6);
        } else if (version === '4') {
          if (!ipv4Maybe.test(str)) {
            return false;
          }
          var parts = str.split('.').sort(function (a, b) {
            return a - b;
          });
          return parts[3] <= 255;
        } else if (version === '6') {
          var blocks = str.split(':');
          var foundOmissionBlock = false; // marker to indicate ::

          // At least some OS accept the last 32 bits of an IPv6 address
          // (i.e. 2 of the blocks) in IPv4 notation, and RFC 3493 says
          // that '::ffff:a.b.c.d' is valid for IPv4-mapped IPv6 addresses,
          // and '::a.b.c.d' is deprecated, but also valid.
          var foundIPv4TransitionBlock = isIP(blocks[blocks.length - 1], 4);
          var expectedNumberOfBlocks = foundIPv4TransitionBlock ? 7 : 8;

          if (blocks.length > expectedNumberOfBlocks) {
            return false;
          }
          // initial or final ::
          if (str === '::') {
            return true;
          } else if (str.substr(0, 2) === '::') {
            blocks.shift();
            blocks.shift();
            foundOmissionBlock = true;
          } else if (str.substr(str.length - 2) === '::') {
            blocks.pop();
            blocks.pop();
            foundOmissionBlock = true;
          }

          for (var i = 0; i < blocks.length; ++i) {
            // test for a :: which can not be at the string start/end
            // since those cases have been handled above
            if (blocks[i] === '' && i > 0 && i < blocks.length - 1) {
              if (foundOmissionBlock) {
                return false; // multiple :: in address
              }
              foundOmissionBlock = true;
            } else if (foundIPv4TransitionBlock && i === blocks.length - 1) {
              // it has been checked before that the last
              // block is a valid IPv4 address
            } else if (!ipv6Block.test(blocks[i])) {
              return false;
            }
          }
          if (foundOmissionBlock) {
            return blocks.length >= 1;
          }
          return blocks.length === expectedNumberOfBlocks;
        }
        return false;
      }

      var default_url_options = {
        protocols: ['http', 'https', 'ftp'],
        require_tld: true,
        require_protocol: false,
        require_host: true,
        require_valid_protocol: true,
        allow_underscores: false,
        allow_trailing_dot: false,
        allow_protocol_relative_urls: false
      };

      var wrapped_ipv6 = /^\[([^\]]+)\](?::([0-9]+))?$/;

      function isRegExp(obj) {
        return Object.prototype.toString.call(obj) === '[object RegExp]';
      }

      function checkHost(host, matches) {
        for (var i = 0; i < matches.length; i++) {
          var match = matches[i];
          if (host === match || isRegExp(match) && match.test(host)) {
            return true;
          }
        }
        return false;
      }

      function isURL(url, options) {
        assertString(url);
        if (!url || url.length >= 2083 || /[\s<>]/.test(url)) {
          return false;
        }
        if (url.indexOf('mailto:') === 0) {
          return false;
        }
        options = merge(options, default_url_options);
        var protocol = void 0,
            auth = void 0,
            host = void 0,
            hostname = void 0,
            port = void 0,
            port_str = void 0,
            split = void 0,
            ipv6 = void 0;

        split = url.split('#');
        url = split.shift();

        split = url.split('?');
        url = split.shift();

        split = url.split('://');
        if (split.length > 1) {
          protocol = split.shift();
          if (options.require_valid_protocol && options.protocols.indexOf(protocol) === -1) {
            return false;
          }
        } else if (options.require_protocol) {
          return false;
        } else if (options.allow_protocol_relative_urls && url.substr(0, 2) === '//') {
          split[0] = url.substr(2);
        }
        url = split.join('://');

        split = url.split('/');
        url = split.shift();

        if (url === '' && !options.require_host) {
          return true;
        }

        split = url.split('@');
        if (split.length > 1) {
          auth = split.shift();
          if (auth.indexOf(':') >= 0 && auth.split(':').length > 2) {
            return false;
          }
        }
        hostname = split.join('@');

        port_str = ipv6 = null;
        var ipv6_match = hostname.match(wrapped_ipv6);
        if (ipv6_match) {
          host = '';
          ipv6 = ipv6_match[1];
          port_str = ipv6_match[2] || null;
        } else {
          split = hostname.split(':');
          host = split.shift();
          if (split.length) {
            port_str = split.join(':');
          }
        }

        if (port_str !== null) {
          port = parseInt(port_str, 10);
          if (!/^[0-9]+$/.test(port_str) || port <= 0 || port > 65535) {
            return false;
          }
        }

        if (!isIP(host) && !isFDQN(host, options) && (!ipv6 || !isIP(ipv6, 6)) && host !== 'localhost') {
          return false;
        }

        host = host || ipv6;

        if (options.host_whitelist && !checkHost(host, options.host_whitelist)) {
          return false;
        }
        if (options.host_blacklist && checkHost(host, options.host_blacklist)) {
          return false;
        }

        return true;
      }

      var macAddress = /^([0-9a-fA-F][0-9a-fA-F]:){5}([0-9a-fA-F][0-9a-fA-F])$/;

      function isMACAddress(str) {
        assertString(str);
        return macAddress.test(str);
      }

      function isBoolean(str) {
        assertString(str);
        return ['true', 'false', '1', '0'].indexOf(str) >= 0;
      }

      var alpha = {
        'en-US': /^[A-Z]+$/i,
        'cs-CZ': /^[A-ZÁČĎÉĚÍŇÓŘŠŤÚŮÝŽ]+$/i,
        'da-DK': /^[A-ZÆØÅ]+$/i,
        'de-DE': /^[A-ZÄÖÜß]+$/i,
        'es-ES': /^[A-ZÁÉÍÑÓÚÜ]+$/i,
        'fr-FR': /^[A-ZÀÂÆÇÉÈÊËÏÎÔŒÙÛÜŸ]+$/i,
        'nl-NL': /^[A-ZÉËÏÓÖÜ]+$/i,
        'hu-HU': /^[A-ZÁÉÍÓÖŐÚÜŰ]+$/i,
        'pl-PL': /^[A-ZĄĆĘŚŁŃÓŻŹ]+$/i,
        'pt-PT': /^[A-ZÃÁÀÂÇÉÊÍÕÓÔÚÜ]+$/i,
        'ru-RU': /^[А-ЯЁ]+$/i,
        'sr-RS@latin': /^[A-ZČĆŽŠĐ]+$/i,
        'sr-RS': /^[А-ЯЂЈЉЊЋЏ]+$/i,
        'tr-TR': /^[A-ZÇĞİıÖŞÜ]+$/i,
        'uk-UA': /^[А-ЩЬЮЯЄIЇҐ]+$/i,
        ar: /^[ءآأؤإئابةتثجحخدذرزسشصضطظعغفقكلمنهوىيًٌٍَُِّْٰ]+$/
      };

      var alphanumeric = {
        'en-US': /^[0-9A-Z]+$/i,
        'cs-CZ': /^[0-9A-ZÁČĎÉĚÍŇÓŘŠŤÚŮÝŽ]+$/i,
        'da-DK': /^[0-9A-ZÆØÅ]$/i,
        'de-DE': /^[0-9A-ZÄÖÜß]+$/i,
        'es-ES': /^[0-9A-ZÁÉÍÑÓÚÜ]+$/i,
        'fr-FR': /^[0-9A-ZÀÂÆÇÉÈÊËÏÎÔŒÙÛÜŸ]+$/i,
        'hu-HU': /^[0-9A-ZÁÉÍÓÖŐÚÜŰ]+$/i,
        'nl-NL': /^[0-9A-ZÉËÏÓÖÜ]+$/i,
        'pl-PL': /^[0-9A-ZĄĆĘŚŁŃÓŻŹ]+$/i,
        'pt-PT': /^[0-9A-ZÃÁÀÂÇÉÊÍÕÓÔÚÜ]+$/i,
        'ru-RU': /^[0-9А-ЯЁ]+$/i,
        'sr-RS@latin': /^[0-9A-ZČĆŽŠĐ]+$/i,
        'sr-RS': /^[0-9А-ЯЂЈЉЊЋЏ]+$/i,
        'tr-TR': /^[0-9A-ZÇĞİıÖŞÜ]+$/i,
        'uk-UA': /^[0-9А-ЩЬЮЯЄIЇҐ]+$/i,
        ar: /^[٠١٢٣٤٥٦٧٨٩0-9ءآأؤإئابةتثجحخدذرزسشصضطظعغفقكلمنهوىيًٌٍَُِّْٰ]+$/
      };

      var englishLocales = ['AU', 'GB', 'HK', 'IN', 'NZ', 'ZA', 'ZM'];

      for (var locale, i = 0; i < englishLocales.length; i++) {
        locale = 'en-' + englishLocales[i];
        alpha[locale] = alpha['en-US'];
        alphanumeric[locale] = alphanumeric['en-US'];
      }

      alpha['pt-BR'] = alpha['pt-PT'];
      alphanumeric['pt-BR'] = alphanumeric['pt-PT'];

      // Source: http://www.localeplanet.com/java/
      var arabicLocales = ['AE', 'BH', 'DZ', 'EG', 'IQ', 'JO', 'KW', 'LB', 'LY', 'MA', 'QM', 'QA', 'SA', 'SD', 'SY', 'TN', 'YE'];

      for (var _locale, _i = 0; _i < arabicLocales.length; _i++) {
        _locale = 'ar-' + arabicLocales[_i];
        alpha[_locale] = alpha.ar;
        alphanumeric[_locale] = alphanumeric.ar;
      }

      function isAlpha(str) {
        var locale = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'en-US';

        assertString(str);
        if (locale in alpha) {
          return alpha[locale].test(str);
        }
        throw new Error('Invalid locale \'' + locale + '\'');
      }

      function isAlphanumeric(str) {
        var locale = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'en-US';

        assertString(str);
        if (locale in alphanumeric) {
          return alphanumeric[locale].test(str);
        }
        throw new Error('Invalid locale \'' + locale + '\'');
      }

      var numeric = /^[-+]?[0-9]+$/;

      function isNumeric(str) {
        assertString(str);
        return numeric.test(str);
      }

      function isLowercase(str) {
        assertString(str);
        return str === str.toLowerCase();
      }

      function isUppercase(str) {
        assertString(str);
        return str === str.toUpperCase();
      }

      /* eslint-disable no-control-regex */
      var ascii = /^[\x00-\x7F]+$/;
      /* eslint-enable no-control-regex */

      function isAscii(str) {
        assertString(str);
        return ascii.test(str);
      }

      var fullWidth = /[^\u0020-\u007E\uFF61-\uFF9F\uFFA0-\uFFDC\uFFE8-\uFFEE0-9a-zA-Z]/;

      function isFullWidth(str) {
        assertString(str);
        return fullWidth.test(str);
      }

      var halfWidth = /[\u0020-\u007E\uFF61-\uFF9F\uFFA0-\uFFDC\uFFE8-\uFFEE0-9a-zA-Z]/;

      function isHalfWidth(str) {
        assertString(str);
        return halfWidth.test(str);
      }

      function isVariableWidth(str) {
        assertString(str);
        return fullWidth.test(str) && halfWidth.test(str);
      }

      /* eslint-disable no-control-regex */
      var multibyte = /[^\x00-\x7F]/;
      /* eslint-enable no-control-regex */

      function isMultibyte(str) {
        assertString(str);
        return multibyte.test(str);
      }

      var surrogatePair = /[\uD800-\uDBFF][\uDC00-\uDFFF]/;

      function isSurrogatePair(str) {
        assertString(str);
        return surrogatePair.test(str);
      }

      var int = /^(?:[-+]?(?:0|[1-9][0-9]*))$/;
      var intLeadingZeroes = /^[-+]?[0-9]+$/;

      function isInt(str, options) {
        assertString(str);
        options = options || {};

        // Get the regex to use for testing, based on whether
        // leading zeroes are allowed or not.
        var regex = options.hasOwnProperty('allow_leading_zeroes') && !options.allow_leading_zeroes ? int : intLeadingZeroes;

        // Check min/max/lt/gt
        var minCheckPassed = !options.hasOwnProperty('min') || str >= options.min;
        var maxCheckPassed = !options.hasOwnProperty('max') || str <= options.max;
        var ltCheckPassed = !options.hasOwnProperty('lt') || str < options.lt;
        var gtCheckPassed = !options.hasOwnProperty('gt') || str > options.gt;

        return regex.test(str) && minCheckPassed && maxCheckPassed && ltCheckPassed && gtCheckPassed;
      }

      var float = /^(?:[-+])?(?:[0-9]+)?(?:\.[0-9]*)?(?:[eE][\+\-]?(?:[0-9]+))?$/;

      function isFloat(str, options) {
        assertString(str);
        options = options || {};
        if (str === '' || str === '.') {
          return false;
        }
        return float.test(str) && (!options.hasOwnProperty('min') || str >= options.min) && (!options.hasOwnProperty('max') || str <= options.max) && (!options.hasOwnProperty('lt') || str < options.lt) && (!options.hasOwnProperty('gt') || str > options.gt);
      }

      var decimal = /^[-+]?([0-9]+|\.[0-9]+|[0-9]+\.[0-9]+)$/;

      function isDecimal(str) {
        assertString(str);
        return str !== '' && decimal.test(str);
      }

      var hexadecimal = /^[0-9A-F]+$/i;

      function isHexadecimal(str) {
        assertString(str);
        return hexadecimal.test(str);
      }

      function isDivisibleBy(str, num) {
        assertString(str);
        return toFloat(str) % parseInt(num, 10) === 0;
      }

      var hexcolor = /^#?([0-9A-F]{3}|[0-9A-F]{6})$/i;

      function isHexColor(str) {
        assertString(str);
        return hexcolor.test(str);
      }

      var md5 = /^[a-f0-9]{32}$/;

      function isMD5(str) {
        assertString(str);
        return md5.test(str);
      }

      function isJSON(str) {
        assertString(str);
        try {
          var obj = JSON.parse(str);
          return !!obj && (typeof obj === 'undefined' ? 'undefined' : _typeof(obj)) === 'object';
        } catch (e) {/* ignore */}
        return false;
      }

      function isEmpty(str) {
        assertString(str);
        return str.length === 0;
      }

      /* eslint-disable prefer-rest-params */
      function isLength(str, options) {
        assertString(str);
        var min = void 0;
        var max = void 0;
        if ((typeof options === 'undefined' ? 'undefined' : _typeof(options)) === 'object') {
          min = options.min || 0;
          max = options.max;
        } else {
          // backwards compatibility: isLength(str, min [, max])
          min = arguments[1];
          max = arguments[2];
        }
        var surrogatePairs = str.match(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g) || [];
        var len = str.length - surrogatePairs.length;
        return len >= min && (typeof max === 'undefined' || len <= max);
      }

      var uuid = {
        3: /^[0-9A-F]{8}-[0-9A-F]{4}-3[0-9A-F]{3}-[0-9A-F]{4}-[0-9A-F]{12}$/i,
        4: /^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,
        5: /^[0-9A-F]{8}-[0-9A-F]{4}-5[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,
        all: /^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i
      };

      function isUUID(str) {
        var version = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'all';

        assertString(str);
        var pattern = uuid[version];
        return pattern && pattern.test(str);
      }

      function isMongoId(str) {
        assertString(str);
        return isHexadecimal(str) && str.length === 24;
      }

      function isAfter(str) {
        var date = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : String(new Date());

        assertString(str);
        var comparison = toDate(date);
        var original = toDate(str);
        return !!(original && comparison && original > comparison);
      }

      function isBefore(str) {
        var date = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : String(new Date());

        assertString(str);
        var comparison = toDate(date);
        var original = toDate(str);
        return !!(original && comparison && original < comparison);
      }

      function isIn(str, options) {
        assertString(str);
        var i = void 0;
        if (Object.prototype.toString.call(options) === '[object Array]') {
          var array = [];
          for (i in options) {
            if ({}.hasOwnProperty.call(options, i)) {
              array[i] = toString(options[i]);
            }
          }
          return array.indexOf(str) >= 0;
        } else if ((typeof options === 'undefined' ? 'undefined' : _typeof(options)) === 'object') {
          return options.hasOwnProperty(str);
        } else if (options && typeof options.indexOf === 'function') {
          return options.indexOf(str) >= 0;
        }
        return false;
      }

      /* eslint-disable max-len */
      var creditCard = /^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|(222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})|62[0-9]{14}$/;
      /* eslint-enable max-len */

      function isCreditCard(str) {
        assertString(str);
        var sanitized = str.replace(/[^0-9]+/g, '');
        if (!creditCard.test(sanitized)) {
          return false;
        }
        var sum = 0;
        var digit = void 0;
        var tmpNum = void 0;
        var shouldDouble = void 0;
        for (var i = sanitized.length - 1; i >= 0; i--) {
          digit = sanitized.substring(i, i + 1);
          tmpNum = parseInt(digit, 10);
          if (shouldDouble) {
            tmpNum *= 2;
            if (tmpNum >= 10) {
              sum += tmpNum % 10 + 1;
            } else {
              sum += tmpNum;
            }
          } else {
            sum += tmpNum;
          }
          shouldDouble = !shouldDouble;
        }
        return !!(sum % 10 === 0 ? sanitized : false);
      }

      var isin = /^[A-Z]{2}[0-9A-Z]{9}[0-9]$/;

      function isISIN(str) {
        assertString(str);
        if (!isin.test(str)) {
          return false;
        }

        var checksumStr = str.replace(/[A-Z]/g, function (character) {
          return parseInt(character, 36);
        });

        var sum = 0;
        var digit = void 0;
        var tmpNum = void 0;
        var shouldDouble = true;
        for (var i = checksumStr.length - 2; i >= 0; i--) {
          digit = checksumStr.substring(i, i + 1);
          tmpNum = parseInt(digit, 10);
          if (shouldDouble) {
            tmpNum *= 2;
            if (tmpNum >= 10) {
              sum += tmpNum + 1;
            } else {
              sum += tmpNum;
            }
          } else {
            sum += tmpNum;
          }
          shouldDouble = !shouldDouble;
        }

        return parseInt(str.substr(str.length - 1), 10) === (10000 - sum) % 10;
      }

      var isbn10Maybe = /^(?:[0-9]{9}X|[0-9]{10})$/;
      var isbn13Maybe = /^(?:[0-9]{13})$/;
      var factor = [1, 3];

      function isISBN(str) {
        var version = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';

        assertString(str);
        version = String(version);
        if (!version) {
          return isISBN(str, 10) || isISBN(str, 13);
        }
        var sanitized = str.replace(/[\s-]+/g, '');
        var checksum = 0;
        var i = void 0;
        if (version === '10') {
          if (!isbn10Maybe.test(sanitized)) {
            return false;
          }
          for (i = 0; i < 9; i++) {
            checksum += (i + 1) * sanitized.charAt(i);
          }
          if (sanitized.charAt(9) === 'X') {
            checksum += 10 * 10;
          } else {
            checksum += 10 * sanitized.charAt(9);
          }
          if (checksum % 11 === 0) {
            return !!sanitized;
          }
        } else if (version === '13') {
          if (!isbn13Maybe.test(sanitized)) {
            return false;
          }
          for (i = 0; i < 12; i++) {
            checksum += factor[i % 2] * sanitized.charAt(i);
          }
          if (sanitized.charAt(12) - (10 - checksum % 10) % 10 === 0) {
            return !!sanitized;
          }
        }
        return false;
      }

      var issn = '^\\d{4}-?\\d{3}[\\dX]$';

      function isISSN(str) {
        var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};

        assertString(str);
        var testIssn = issn;
        testIssn = options.require_hyphen ? testIssn.replace('?', '') : testIssn;
        testIssn = options.case_sensitive ? new RegExp(testIssn) : new RegExp(testIssn, 'i');
        if (!testIssn.test(str)) {
          return false;
        }
        var issnDigits = str.replace('-', '');
        var position = 8;
        var checksum = 0;
        var _iteratorNormalCompletion = true;
        var _didIteratorError = false;
        var _iteratorError = undefined;

        try {
          for (var _iterator = issnDigits[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
            var digit = _step.value;

            var digitValue = digit.toUpperCase() === 'X' ? 10 : +digit;
            checksum += digitValue * position;
            --position;
          }
        } catch (err) {
          _didIteratorError = true;
          _iteratorError = err;
        } finally {
          try {
            if (!_iteratorNormalCompletion && _iterator.return) {
              _iterator.return();
            }
          } finally {
            if (_didIteratorError) {
              throw _iteratorError;
            }
          }
        }

        return checksum % 11 === 0;
      }

      /* eslint-disable max-len */
      var phones = {
        'ar-DZ': /^(\+?213|0)(5|6|7)\d{8}$/,
        'ar-SY': /^(!?(\+?963)|0)?9\d{8}$/,
        'ar-SA': /^(!?(\+?966)|0)?5\d{8}$/,
        'en-US': /^(\+?1)?[2-9]\d{2}[2-9](?!11)\d{6}$/,
        'cs-CZ': /^(\+?420)? ?[1-9][0-9]{2} ?[0-9]{3} ?[0-9]{3}$/,
        'de-DE': /^(\+?49[ \.\-])?([\(]{1}[0-9]{1,6}[\)])?([0-9 \.\-\/]{3,20})((x|ext|extension)[ ]?[0-9]{1,4})?$/,
        'da-DK': /^(\+?45)?(\d{8})$/,
        'el-GR': /^(\+?30)?(69\d{8})$/,
        'en-AU': /^(\+?61|0)4\d{8}$/,
        'en-GB': /^(\+?44|0)7\d{9}$/,
        // According to http://www.ofca.gov.hk/filemanager/ofca/en/content_311/no_plan.pdf
        'en-HK': /^(\+?852-?)?((4(04[01]|06\d|09[3-9]|20\d|2[2-9]\d|3[3-9]\d|[467]\d{2}|5[1-9]\d|81\d|82[1-9]|8[69]\d|92[3-9]|95[2-9]|98\d)|5([1-79]\d{2})|6(0[1-9]\d|[1-9]\d{2})|7(0[1-9]\d|10[4-79]|11[458]|1[24578]\d|13[24-9]|16[0-8]|19[24579]|21[02-79]|2[456]\d|27[13-6]|3[456]\d|37[4578]|39[0146])|8(1[58]\d|2[45]\d|267|27[5-9]|2[89]\d|3[15-9]\d|32[5-8]|[46-9]\d{2}|5[013-9]\d)|9(0[1-9]\d|1[02-9]\d|[2-8]\d{2}))-?\d{4}|7130-?[0124-8]\d{3}|8167-?2\d{3})$/,
        'en-IN': /^(\+?91|0)?[789]\d{9}$/,
        'en-NG': /^(\+?234|0)?[789]\d{9}$/,
        'en-NZ': /^(\+?64|0)2\d{7,9}$/,
        'en-ZA': /^(\+?27|0)\d{9}$/,
        'en-ZM': /^(\+?26)?09[567]\d{7}$/,
        'es-ES': /^(\+?34)?(6\d{1}|7[1234])\d{7}$/,
        'fi-FI': /^(\+?358|0)\s?(4(0|1|2|4|5)?|50)\s?(\d\s?){4,8}\d$/,
        'fr-FR': /^(\+?33|0)[67]\d{8}$/,
        'he-IL': /^(\+972|0)([23489]|5[0248]|77)[1-9]\d{6}/,
        'hu-HU': /^(\+?36)(20|30|70)\d{7}$/,
        'lt-LT': /^(\+370|8)\d{8}$/,
        'id-ID': /^(\+?62|0[1-9])[\s|\d]+$/,
        'it-IT': /^(\+?39)?\s?3\d{2} ?\d{6,7}$/,
        'ko-KR': /^((\+?82)[ \-]?)?0?1([0|1|6|7|8|9]{1})[ \-]?\d{3,4}[ \-]?\d{4}$/,
        'ja-JP': /^(\+?81|0)\d{1,4}[ \-]?\d{1,4}[ \-]?\d{4}$/,
        'ms-MY': /^(\+?6?01){1}(([145]{1}(\-|\s)?\d{7,8})|([236789]{1}(\s|\-)?\d{7}))$/,
        'nb-NO': /^(\+?47)?[49]\d{7}$/,
        'nl-BE': /^(\+?32|0)4?\d{8}$/,
        'nn-NO': /^(\+?47)?[49]\d{7}$/,
        'pl-PL': /^(\+?48)? ?[5-8]\d ?\d{3} ?\d{2} ?\d{2}$/,
        'pt-BR': /^(\+?55|0)\-?[1-9]{2}\-?[2-9]{1}\d{3,4}\-?\d{4}$/,
        'pt-PT': /^(\+?351)?9[1236]\d{7}$/,
        'ro-RO': /^(\+?4?0)\s?7\d{2}(\/|\s|\.|\-)?\d{3}(\s|\.|\-)?\d{3}$/,
        'en-PK': /^((\+92)|(0092))-{0,1}\d{3}-{0,1}\d{7}$|^\d{11}$|^\d{4}-\d{7}$/,
        'ru-RU': /^(\+?7|8)?9\d{9}$/,
        'sr-RS': /^(\+3816|06)[- \d]{5,9}$/,
        'tr-TR': /^(\+?90|0)?5\d{9}$/,
        'vi-VN': /^(\+?84|0)?((1(2([0-9])|6([2-9])|88|99))|(9((?!5)[0-9])))([0-9]{7})$/,
        'zh-CN': /^(\+?0?86\-?)?1[345789]\d{9}$/,
        'zh-TW': /^(\+?886\-?|0)?9\d{8}$/
      };
      /* eslint-enable max-len */

      // aliases
      phones['en-CA'] = phones['en-US'];
      phones['fr-BE'] = phones['nl-BE'];
      phones['zh-HK'] = phones['en-HK'];

      function isMobilePhone(str, locale) {
        assertString(str);
        if (locale in phones) {
          return phones[locale].test(str);
        } else if (locale === 'any') {
          return !!Object.values(phones).find(phone => phone.test(str));
        }
        return false;
      }

      function currencyRegex(options) {
        var symbol = '(\\' + options.symbol.replace(/\./g, '\\.') + ')' + (options.require_symbol ? '' : '?'),
            negative = '-?',
            whole_dollar_amount_without_sep = '[1-9]\\d*',
            whole_dollar_amount_with_sep = '[1-9]\\d{0,2}(\\' + options.thousands_separator + '\\d{3})*',
            valid_whole_dollar_amounts = ['0', whole_dollar_amount_without_sep, whole_dollar_amount_with_sep],
            whole_dollar_amount = '(' + valid_whole_dollar_amounts.join('|') + ')?',
            decimal_amount = '(\\' + options.decimal_separator + '\\d{2})?';
        var pattern = whole_dollar_amount + decimal_amount;

        // default is negative sign before symbol, but there are two other options (besides parens)
        if (options.allow_negatives && !options.parens_for_negatives) {
          if (options.negative_sign_after_digits) {
            pattern += negative;
          } else if (options.negative_sign_before_digits) {
            pattern = negative + pattern;
          }
        }

        // South African Rand, for example, uses R 123 (space) and R-123 (no space)
        if (options.allow_negative_sign_placeholder) {
          pattern = '( (?!\\-))?' + pattern;
        } else if (options.allow_space_after_symbol) {
          pattern = ' ?' + pattern;
        } else if (options.allow_space_after_digits) {
          pattern += '( (?!$))?';
        }

        if (options.symbol_after_digits) {
          pattern += symbol;
        } else {
          pattern = symbol + pattern;
        }

        if (options.allow_negatives) {
          if (options.parens_for_negatives) {
            pattern = '(\\(' + pattern + '\\)|' + pattern + ')';
          } else if (!(options.negative_sign_before_digits || options.negative_sign_after_digits)) {
            pattern = negative + pattern;
          }
        }

        /* eslint-disable prefer-template */
        return new RegExp('^' +
        // ensure there's a dollar and/or decimal amount, and that
        // it doesn't start with a space or a negative sign followed by a space
        '(?!-? )(?=.*\\d)' + pattern + '$');
        /* eslint-enable prefer-template */
      }

      var default_currency_options = {
        symbol: '$',
        require_symbol: false,
        allow_space_after_symbol: false,
        symbol_after_digits: false,
        allow_negatives: true,
        parens_for_negatives: false,
        negative_sign_before_digits: false,
        negative_sign_after_digits: false,
        allow_negative_sign_placeholder: false,
        thousands_separator: ',',
        decimal_separator: '.',
        allow_space_after_digits: false
      };

      function isCurrency(str, options) {
        assertString(str);
        options = merge(options, default_currency_options);
        return currencyRegex(options).test(str);
      }

      /* eslint-disable max-len */
      // from http://goo.gl/0ejHHW
      var iso8601 = /^([\+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T\s]((([01]\d|2[0-3])((:?)[0-5]\d)?|24:?00)([\.,]\d+(?!:))?)?(\17[0-5]\d([\.,]\d+)?)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$/;
      /* eslint-enable max-len */

      function isISO8601 (str) {
        assertString(str);
        return iso8601.test(str);
      }

      function isBase64(str) {
        assertString(str);
        // Value length must be divisible by 4.
        var len = str.length;
        if (!len || len % 4 !== 0) {
          return false;
        }

        try {
          if (atob(str)) {
            return true;
          }
        } catch (e) {
          return false;
        }
      }

      var dataURI = /^\s*data:([a-z]+\/[a-z0-9\-\+]+(;[a-z\-]+=[a-z0-9\-]+)?)?(;base64)?,[a-z0-9!\$&',\(\)\*\+,;=\-\._~:@\/\?%\s]*\s*$/i; // eslint-disable-line max-len

      function isDataURI(str) {
        assertString(str);
        return dataURI.test(str);
      }

      function ltrim(str, chars) {
        assertString(str);
        var pattern = chars ? new RegExp('^[' + chars + ']+', 'g') : /^\s+/g;
        return str.replace(pattern, '');
      }

      function rtrim(str, chars) {
        assertString(str);
        var pattern = chars ? new RegExp('[' + chars + ']') : /\s/;

        var idx = str.length - 1;
        while (idx >= 0 && pattern.test(str[idx])) {
          idx--;
        }

        return idx < str.length ? str.substr(0, idx + 1) : str;
      }

      function trim(str, chars) {
        return rtrim(ltrim(str, chars), chars);
      }

      function escape(str) {
            assertString(str);
            return str.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\//g, '&#x2F;').replace(/\\/g, '&#x5C;').replace(/`/g, '&#96;');
      }

      function unescape(str) {
            assertString(str);
            return str.replace(/&amp;/g, '&').replace(/&quot;/g, '"').replace(/&#x27;/g, "'").replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&#x2F;/g, '/').replace(/&#96;/g, '`');
      }

      function blacklist(str, chars) {
        assertString(str);
        return str.replace(new RegExp('[' + chars + ']+', 'g'), '');
      }

      function stripLow(str, keep_new_lines) {
        assertString(str);
        var chars = keep_new_lines ? '\\x00-\\x09\\x0B\\x0C\\x0E-\\x1F\\x7F' : '\\x00-\\x1F\\x7F';
        return blacklist(str, chars);
      }

      function whitelist(str, chars) {
        assertString(str);
        return str.replace(new RegExp('[^' + chars + ']+', 'g'), '');
      }

      function isWhitelisted(str, chars) {
        assertString(str);
        for (var i = str.length - 1; i >= 0; i--) {
          if (chars.indexOf(str[i]) === -1) {
            return false;
          }
        }
        return true;
      }

      var default_normalize_email_options = {
        // The following options apply to all email addresses
        // Lowercases the local part of the email address.
        // Please note this may violate RFC 5321 as per http://stackoverflow.com/a/9808332/192024).
        // The domain is always lowercased, as per RFC 1035
        all_lowercase: true,

        // The following conversions are specific to GMail
        // Lowercases the local part of the GMail address (known to be case-insensitive)
        gmail_lowercase: true,
        // Removes dots from the local part of the email address, as that's ignored by GMail
        gmail_remove_dots: true,
        // Removes the subaddress (e.g. "+foo") from the email address
        gmail_remove_subaddress: true,
        // Conversts the googlemail.com domain to gmail.com
        gmail_convert_googlemaildotcom: true,

        // The following conversions are specific to Outlook.com / Windows Live / Hotmail
        // Lowercases the local part of the Outlook.com address (known to be case-insensitive)
        outlookdotcom_lowercase: true,
        // Removes the subaddress (e.g. "+foo") from the email address
        outlookdotcom_remove_subaddress: true,

        // The following conversions are specific to Yahoo
        // Lowercases the local part of the Yahoo address (known to be case-insensitive)
        yahoo_lowercase: true,
        // Removes the subaddress (e.g. "-foo") from the email address
        yahoo_remove_subaddress: true,

        // The following conversions are specific to iCloud
        // Lowercases the local part of the iCloud address (known to be case-insensitive)
        icloud_lowercase: true,
        // Removes the subaddress (e.g. "+foo") from the email address
        icloud_remove_subaddress: true
      };

      // List of domains used by iCloud
      var icloud_domains = ['icloud.com', 'me.com'];

      // List of domains used by Outlook.com and its predecessors
      // This list is likely incomplete.
      // Partial reference:
      // https://blogs.office.com/2013/04/17/outlook-com-gets-two-step-verification-sign-in-by-alias-and-new-international-domains/
      var outlookdotcom_domains = ['hotmail.at', 'hotmail.be', 'hotmail.ca', 'hotmail.cl', 'hotmail.co.il', 'hotmail.co.nz', 'hotmail.co.th', 'hotmail.co.uk', 'hotmail.com', 'hotmail.com.ar', 'hotmail.com.au', 'hotmail.com.br', 'hotmail.com.gr', 'hotmail.com.mx', 'hotmail.com.pe', 'hotmail.com.tr', 'hotmail.com.vn', 'hotmail.cz', 'hotmail.de', 'hotmail.dk', 'hotmail.es', 'hotmail.fr', 'hotmail.hu', 'hotmail.id', 'hotmail.ie', 'hotmail.in', 'hotmail.it', 'hotmail.jp', 'hotmail.kr', 'hotmail.lv', 'hotmail.my', 'hotmail.ph', 'hotmail.pt', 'hotmail.sa', 'hotmail.sg', 'hotmail.sk', 'live.be', 'live.co.uk', 'live.com', 'live.com.ar', 'live.com.mx', 'live.de', 'live.es', 'live.eu', 'live.fr', 'live.it', 'live.nl', 'msn.com', 'outlook.at', 'outlook.be', 'outlook.cl', 'outlook.co.il', 'outlook.co.nz', 'outlook.co.th', 'outlook.com', 'outlook.com.ar', 'outlook.com.au', 'outlook.com.br', 'outlook.com.gr', 'outlook.com.pe', 'outlook.com.tr', 'outlook.com.vn', 'outlook.cz', 'outlook.de', 'outlook.dk', 'outlook.es', 'outlook.fr', 'outlook.hu', 'outlook.id', 'outlook.ie', 'outlook.in', 'outlook.it', 'outlook.jp', 'outlook.kr', 'outlook.lv', 'outlook.my', 'outlook.ph', 'outlook.pt', 'outlook.sa', 'outlook.sg', 'outlook.sk', 'passport.com'];

      // List of domains used by Yahoo Mail
      // This list is likely incomplete
      var yahoo_domains = ['rocketmail.com', 'yahoo.ca', 'yahoo.co.uk', 'yahoo.com', 'yahoo.de', 'yahoo.fr', 'yahoo.in', 'yahoo.it', 'ymail.com'];

      function normalizeEmail(email, options) {
        options = merge(options, default_normalize_email_options);

        if (!isEmail(email)) {
          return false;
        }

        var raw_parts = email.split('@');
        var domain = raw_parts.pop();
        var user = raw_parts.join('@');
        var parts = [user, domain];

        // The domain is always lowercased, as it's case-insensitive per RFC 1035
        parts[1] = parts[1].toLowerCase();

        if (parts[1] === 'gmail.com' || parts[1] === 'googlemail.com') {
          // Address is GMail
          if (options.gmail_remove_subaddress) {
            parts[0] = parts[0].split('+')[0];
          }
          if (options.gmail_remove_dots) {
            parts[0] = parts[0].replace(/\./g, '');
          }
          if (!parts[0].length) {
            return false;
          }
          if (options.all_lowercase || options.gmail_lowercase) {
            parts[0] = parts[0].toLowerCase();
          }
          parts[1] = options.gmail_convert_googlemaildotcom ? 'gmail.com' : parts[1];
        } else if (~icloud_domains.indexOf(parts[1])) {
          // Address is iCloud
          if (options.icloud_remove_subaddress) {
            parts[0] = parts[0].split('+')[0];
          }
          if (!parts[0].length) {
            return false;
          }
          if (options.all_lowercase || options.icloud_lowercase) {
            parts[0] = parts[0].toLowerCase();
          }
        } else if (~outlookdotcom_domains.indexOf(parts[1])) {
          // Address is Outlook.com
          if (options.outlookdotcom_remove_subaddress) {
            parts[0] = parts[0].split('+')[0];
          }
          if (!parts[0].length) {
            return false;
          }
          if (options.all_lowercase || options.outlookdotcom_lowercase) {
            parts[0] = parts[0].toLowerCase();
          }
        } else if (~yahoo_domains.indexOf(parts[1])) {
          // Address is Yahoo
          if (options.yahoo_remove_subaddress) {
            var components = parts[0].split('-');
            parts[0] = components.length > 1 ? components.slice(0, -1).join('-') : components[0];
          }
          if (!parts[0].length) {
            return false;
          }
          if (options.all_lowercase || options.yahoo_lowercase) {
            parts[0] = parts[0].toLowerCase();
          }
        } else if (options.all_lowercase) {
          // Any other address
          parts[0] = parts[0].toLowerCase();
        }
        return parts.join('@');
      }

      // see http://isrc.ifpi.org/en/isrc-standard/code-syntax
      var isrc = /^[A-Z]{2}[0-9A-Z]{3}\d{2}\d{5}$/;

      function isISRC(str) {
        assertString(str);
        return isrc.test(str);
      }

      var cultureCodes = new Set(["ar", "bg", "ca", "zh-Hans", "cs", "da", "de",
      "el", "en", "es", "fi", "fr", "he", "hu", "is", "it", "ja", "ko", "nl", "no",
      "pl", "pt", "rm", "ro", "ru", "hr", "sk", "sq", "sv", "th", "tr", "ur", "id",
      "uk", "be", "sl", "et", "lv", "lt", "tg", "fa", "vi", "hy", "az", "eu", "hsb",
      "mk", "tn", "xh", "zu", "af", "ka", "fo", "hi", "mt", "se", "ga", "ms", "kk",
      "ky", "sw", "tk", "uz", "tt", "bn", "pa", "gu", "or", "ta", "te", "kn", "ml",
      "as", "mr", "sa", "mn", "bo", "cy", "km", "lo", "gl", "kok", "syr", "si", "iu",
      "am", "tzm", "ne", "fy", "ps", "fil", "dv", "ha", "yo", "quz", "nso", "ba", "lb",
      "kl", "ig", "ii", "arn", "moh", "br", "ug", "mi", "oc", "co", "gsw", "sah",
      "qut", "rw", "wo", "prs", "gd", "ar-SA", "bg-BG", "ca-ES", "zh-TW", "cs-CZ",
      "da-DK", "de-DE", "el-GR", "en-US", "fi-FI", "fr-FR", "he-IL", "hu-HU", "is-IS",
      "it-IT", "ja-JP", "ko-KR", "nl-NL", "nb-NO", "pl-PL", "pt-BR", "rm-CH", "ro-RO",
      "ru-RU", "hr-HR", "sk-SK", "sq-AL", "sv-SE", "th-TH", "tr-TR", "ur-PK", "id-ID",
      "uk-UA", "be-BY", "sl-SI", "et-EE", "lv-LV", "lt-LT", "tg-Cyrl-TJ", "fa-IR",
      "vi-VN", "hy-AM", "az-Latn-AZ", "eu-ES", "hsb-DE", "mk-MK", "tn-ZA", "xh-ZA",
      "zu-ZA", "af-ZA", "ka-GE", "fo-FO", "hi-IN", "mt-MT", "se-NO", "ms-MY", "kk-KZ",
      "ky-KG", "sw-KE", "tk-TM", "uz-Latn-UZ", "tt-RU", "bn-IN", "pa-IN", "gu-IN",
      "or-IN", "ta-IN", "te-IN", "kn-IN", "ml-IN", "as-IN", "mr-IN", "sa-IN", "mn-MN",
      "bo-CN", "cy-GB", "km-KH", "lo-LA", "gl-ES", "kok-IN", "syr-SY", "si-LK",
      "iu-Cans-CA", "am-ET", "ne-NP", "fy-NL", "ps-AF", "fil-PH", "dv-MV",
      "ha-Latn-NG", "yo-NG", "quz-BO", "nso-ZA", "ba-RU", "lb-LU", "kl-GL", "ig-NG",
      "ii-CN", "arn-CL", "moh-CA", "br-FR", "ug-CN", "mi-NZ", "oc-FR", "co-FR",
      "gsw-FR", "sah-RU", "qut-GT", "rw-RW", "wo-SN", "prs-AF", "gd-GB", "ar-IQ",
      "zh-CN", "de-CH", "en-GB", "es-MX", "fr-BE", "it-CH", "nl-BE", "nn-NO", "pt-PT",
      "sr-Latn-CS", "sv-FI", "az-Cyrl-AZ", "dsb-DE", "se-SE", "ga-IE", "ms-BN",
      "uz-Cyrl-UZ", "bn-BD", "mn-Mong-CN", "iu-Latn-CA", "tzm-Latn-DZ", "quz-EC",
      "ar-EG", "zh-HK", "de-AT", "en-AU", "es-ES", "fr-CA", "sr-Cyrl-CS", "se-FI",
      "quz-PE", "ar-LY", "zh-SG", "de-LU", "en-CA", "es-GT", "fr-CH", "hr-BA",
      "smj-NO", "ar-DZ", "zh-MO", "de-LI", "en-NZ", "es-CR", "fr-LU", "bs-Latn-BA",
      "smj-SE", "ar-MA", "en-IE", "es-PA", "fr-MC", "sr-Latn-BA", "sma-NO", "ar-TN",
      "en-ZA", "es-DO", "sr-Cyrl-BA", "sma-SE", "ar-OM", "en-JM", "es-VE",
      "bs-Cyrl-BA", "sms-FI", "ar-YE", "en-029", "es-CO", "sr-Latn-RS", "smn-FI",
      "ar-SY", "en-BZ", "es-PE", "sr-Cyrl-RS", "ar-JO", "en-TT", "es-AR", "sr-Latn-ME",
      "ar-LB", "en-ZW", "es-EC", "sr-Cyrl-ME", "ar-KW", "en-PH", "es-CL", "ar-AE",
      "es-UY", "ar-BH", "es-PY", "ar-QA", "en-IN", "es-BO", "en-MY", "es-SV", "en-SG",
      "es-HN", "es-NI", "es-PR", "es-US", "bs-Cyrl", "bs-Latn", "sr-Cyrl", "sr-Latn",
      "smn", "az-Cyrl", "sms", "zh", "nn", "bs", "az-Latn", "sma", "uz-Cyrl",
      "mn-Cyrl", "iu-Cans", "zh-Hant", "nb", "sr", "tg-Cyrl", "dsb", "smj", "uz-Latn",
      "mn-Mong", "iu-Latn", "tzm-Latn", "ha-Latn", "zh-CHS", "zh-CHT"]);

      function isRFC5646(str) {
        assertString(str);
        // According to the spec these codes are case sensitive so we can check the
        // string directly.
        return cultureCodes.has(str);
      }

      var semver =  /^v?(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(-(0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*)?(\+[0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*)?$/i

      function isSemVer(str) {
        assertString(str);
        return semver.test(str);
      }

      var rgbcolor =  /^rgb?\(\s*(0|[1-9]\d?|1\d\d?|2[0-4]\d|25[0-5])\s*,\s*(0|[1-9]\d?|1\d\d?|2[0-4]\d|25[0-5])\s*,\s*(0|[1-9]\d?|1\d\d?|2[0-4]\d|25[0-5])\s*\)$/i

      function isRGBColor(str) {
        assertString(str);
        return rgbcolor.test(str);
      }

      var version = '7.0.0';

      var validator = {
        blacklist: blacklist,
        contains: contains,
        equals: equals,
        escape: escape,
        isAfter: isAfter,
        isAlpha: isAlpha,
        isAlphanumeric: isAlphanumeric,
        isAscii: isAscii,
        isBase64: isBase64,
        isBefore: isBefore,
        isBoolean: isBoolean,
        isByteLength: isByteLength,
        isCreditCard: isCreditCard,
        isCurrency: isCurrency,
        isDataURI: isDataURI,
        isDecimal: isDecimal,
        isDivisibleBy: isDivisibleBy,
        isEmail: isEmail,
        isEmpty: isEmpty,
        isFloat: isFloat,
        isFQDN: isFDQN,
        isFullWidth: isFullWidth,
        isHalfWidth: isHalfWidth,
        isHexadecimal: isHexadecimal,
        isHexColor: isHexColor,
        isIn: isIn,
        isInt: isInt,
        isIP: isIP,
        isRFC5646: isRFC5646,
        isISBN: isISBN,
        isISIN: isISIN,
        isISO8601: isISO8601,
        isISRC: isISRC,
        isRGBColor: isRGBColor,
        isISSN: isISSN,
        isJSON: isJSON,
        isLength: isLength,
        isLowercase: isLowercase,
        isMACAddress: isMACAddress,
        isMD5: isMD5,
        isMobilePhone: isMobilePhone,
        isMongoId: isMongoId,
        isMultibyte: isMultibyte,
        isNumeric: isNumeric,
        isSemVer: isSemVer,
        isSurrogatePair: isSurrogatePair,
        isUppercase: isUppercase,
        isURL: isURL,
        isUUID: isUUID,
        isVariableWidth: isVariableWidth,
        isWhitelisted: isWhitelisted,
        ltrim: ltrim,
        matches: matches,
        normalizeEmail: normalizeEmail,
        rtrim: rtrim,
        stripLow: stripLow,
        toBoolean: toBoolean,
        toDate: toDate,
        toFloat: toFloat,
        toInt: toInt,
        toString: toString,
        trim: trim,
        unescape: unescape,
        version: version,
        whitelist: whitelist
      };

      return validator;
}));
PK
!<S���!!=chrome/devtools/modules/devtools/client/shared/view-source.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var { Task } = require("devtools/shared/task");

var Services = require("Services");
var { gDevTools } = require("devtools/client/framework/devtools");
var { getSourceText } = require("devtools/client/debugger/content/queries");

/**
 * 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} sourceURL
 * @param {number} sourceLine
 *
 * @return {Promise<boolean>}
 */
exports.viewSourceInStyleEditor = Task.async(function* (toolbox, sourceURL,
                                                        sourceLine) {
  let panel = yield toolbox.loadTool("styleeditor");

  try {
    yield panel.selectStyleSheet(sourceURL, sourceLine);
    yield toolbox.selectTool("styleeditor");
    return true;
  } catch (e) {
    exports.viewSource(toolbox, sourceURL, sourceLine);
    return false;
  }
});

/**
 * Tries to open a JavaScript file in the Debugger. 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 Debugger, as the built-in Firefox
 * View Source is the fallback.
 *
 * @param {Toolbox} toolbox
 * @param {string} sourceURL
 * @param {number} sourceLine
 *
 * @return {Promise<boolean>}
 */
exports.viewSourceInDebugger = Task.async(function* (toolbox, sourceURL, sourceLine) {
  // If the Debugger was already open, switch to it and try to show the
  // source immediately. Otherwise, initialize it and wait for the sources
  // to be added first.
  let debuggerAlreadyOpen = toolbox.getPanel("jsdebugger");
  let dbg = yield toolbox.loadTool("jsdebugger");

  // New debugger frontend
  if (Services.prefs.getBoolPref("devtools.debugger.new-debugger-frontend")) {
    const source = dbg.getSource(sourceURL);
    if (source) {
      yield toolbox.selectTool("jsdebugger");
      dbg.selectSource(sourceURL, sourceLine);
      return true;
    }

    exports.viewSource(toolbox, sourceURL, sourceLine);
    return false;
  }

  const win = dbg.panelWin;

  // Old debugger frontend
  if (!debuggerAlreadyOpen) {
    yield win.DebuggerController.waitForSourcesLoaded();
  }

  let { DebuggerView } = win;
  let { Sources } = DebuggerView;

  let item = Sources.getItemForAttachment(a => a.source.url === sourceURL);
  if (item) {
    yield toolbox.selectTool("jsdebugger");

    // Determine if the source has already finished loading. There's two cases
    // in which we need to wait for the source to be shown:
    // 1) The requested source is not yet selected and will be shown once it is
    //    selected and loaded
    // 2) The requested source is selected BUT the source text is still loading.
    const { actor } = item.attachment.source;
    const state = win.DebuggerController.getState();

    // (1) Is the source selected?
    const selected = state.sources.selectedSource;
    const isSelected = selected === actor;

    // (2) Has the source text finished loading?
    let isLoading = false;

    // Only check if the source is loading when the source is already selected.
    // If the source is not selected, we will select it below and the already
    // pending load will be cancelled and this check is useless.
    if (isSelected) {
      const sourceTextInfo = getSourceText(state, selected);
      isLoading = sourceTextInfo && sourceTextInfo.loading;
    }

    // Select the requested source
    DebuggerView.setEditorLocation(actor, sourceLine, { noDebug: true });

    // Wait for it to load
    if (!isSelected || isLoading) {
      yield win.DebuggerController.waitForSourceShown(sourceURL);
    }
    return true;
  }

  // If not found, still attempt to open in View Source
  exports.viewSource(toolbox, sourceURL, sourceLine);
  return false;
});

/**
 * Tries to open a JavaScript file in the corresponding Scratchpad.
 *
 * @param {string} sourceURL
 * @param {number} sourceLine
 *
 * @return {Promise}
 */
exports.viewSourceInScratchpad = Task.async(function* (sourceURL, sourceLine) {
  // Check for matching top level scratchpad window.
  let wins = Services.wm.getEnumerator("devtools:scratchpad");

  while (wins.hasMoreElements()) {
    let win = wins.getNext();

    if (!win.closed && win.Scratchpad.uniqueName === sourceURL) {
      win.focus();
      win.Scratchpad.editor.setCursor({ line: sourceLine, ch: 0 });
      return;
    }
  }

  // For scratchpads within toolbox
  for (let [, toolbox] of gDevTools) {
    let scratchpadPanel = toolbox.getPanel("scratchpad");
    if (scratchpadPanel) {
      let { scratchpad } = scratchpadPanel;
      if (scratchpad.uniqueName === sourceURL) {
        toolbox.selectTool("scratchpad");
        toolbox.raise();
        scratchpad.editor.focus();
        scratchpad.editor.setCursor({ line: sourceLine, ch: 0 });
        return;
      }
    }
  }
});

/**
 * Open a link in Firefox's View Source.
 *
 * @param {Toolbox} toolbox
 * @param {string} sourceURL
 * @param {number} sourceLine
 *
 * @return {Promise}
 */
exports.viewSource = Task.async(function* (toolbox, sourceURL, sourceLine) {
  let utils = toolbox.gViewSourceUtils;
  utils.viewSource({
    URL: sourceURL,
    lineNumber: sourceLine || 0,
  });
});
PK
!< �`�=chrome/devtools/modules/devtools/client/shared/webgl-utils.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Cc, Ci } = require("chrome");
const Services = require("Services");

const WEBGL_CONTEXT_NAME = "experimental-webgl";

function isWebGLForceEnabled() {
  return Services.prefs.getBoolPref("webgl.force-enabled");
}

function isWebGLSupportedByGFX() {
  let supported = false;

  try {
    let gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
    let angle = gfxInfo.FEATURE_WEBGL_ANGLE;
    let 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) {
  let supported =
    !isWebGLForceEnabled() &&
     isWebGLSupportedByGFX() &&
     create3DContext(createCanvas(doc));

  return supported;
}
exports.isWebGLSupported = isWebGLSupported;
PK
!<��c]��Wchrome/devtools/modules/devtools/client/shared/webpack/shims/platform-clipboard-stub.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Helpers for clipboard handling.

/* globals document */

"use strict";

function copyString(string) {
  let doCopy = function (e) {
    e.clipboardData.setData("text/plain", string);
    e.preventDefault();
  };

  document.addEventListener("copy", doCopy);
  document.execCommand("copy", false, null);
  document.removeEventListener("copy", doCopy);
}

function getText() {
  // See bug 1295692.
  return null;
}

exports.copyString = copyString;
exports.getText = getText;
PK
!<X�H���Schrome/devtools/modules/devtools/client/shared/webpack/shims/platform-stack-stub.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 few wrappers for stack-manipulation.  This version of the module
// is used in content code.  Note that this particular copy of the
// file can only be loaded via require(), because Cu.import doesn't
// exist in the content case.  So, we don't need the code to handle
// both require and import here.

"use strict";

/**
 * Looks like Cu.callFunctionWithAsyncStack, but just calls the callee.
 */
function callFunctionWithAsyncStack(callee, stack, id) {
  return callee();
}

/**
 * Return a description of the Nth caller, suitable for logging.
 *
 * @param {Number} n the caller to describe
 * @return {String} a description of the nth caller.
 */
function describeNthCaller(n) {
  if (isWorker) {
    return "";
  }

  let stack = new Error().stack.split("\n");
  // Add one here to skip this function.
  return stack[n + 1];
}

/**
 * Return a stack object that can be serialized and, when
 * deserialized, passed to callFunctionWithAsyncStack.
 */
function getStack() {
  // There's no reason for this to do anything fancy, since it's only
  // used to pass back into callFunctionWithAsyncStack, which we can't
  // implement.
  return null;
}

exports.callFunctionWithAsyncStack = callFunctionWithAsyncStack;
exports.describeNthCaller = describeNthCaller;
exports.getStack = getStack;
PK
!<���wHwHKchrome/devtools/modules/devtools/client/shared/widgets/AbstractTreeItem.jsm/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 { require, loader } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
const { ViewHelpers } = require("devtools/client/shared/widgets/view-helpers");
const { KeyCodes } = require("devtools/client/shared/keycodes");

loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");

this.EXPORTED_SYMBOLS = ["AbstractTreeItem"];

/**
 * A very generic and low-level tree view implementation. It is not intended
 * to be used alone, but as a base class that you can extend to build your
 * own custom implementation.
 *
 * Language:
 *   - An "item" is an instance of an AbstractTreeItem.
 *   - An "element" or "node" is an nsIDOMNode.
 *
 * The following events are emitted by this tree, always from the root item,
 * with the first argument pointing to the affected child item:
 *   - "expand": when an item is expanded in the tree
 *   - "collapse": when an item is collapsed in the tree
 *   - "focus": when an item is selected in the tree
 *
 * For example, you can extend this abstract class like this:
 *
 * function MyCustomTreeItem(dataSrc, properties) {
 *   AbstractTreeItem.call(this, properties);
 *   this.itemDataSrc = dataSrc;
 * }
 *
 * MyCustomTreeItem.prototype = extend(AbstractTreeItem.prototype, {
 *   _displaySelf: function(document, arrowNode) {
 *     let node = document.createElement("hbox");
 *     ...
 *     // Append the provided arrow node wherever you want.
 *     node.appendChild(arrowNode);
 *     ...
 *     // Use `this.itemDataSrc` to customize the tree item and
 *     // `this.level` to calculate the indentation.
 *     node.style.marginInlineStart = (this.level * 10) + "px";
 *     node.appendChild(document.createTextNode(this.itemDataSrc.label));
 *     ...
 *     return node;
 *   },
 *   _populateSelf: function(children) {
 *     ...
 *     // Use `this.itemDataSrc` to get the data source for the child items.
 *     let someChildDataSrc = this.itemDataSrc.children[0];
 *     ...
 *     children.push(new MyCustomTreeItem(someChildDataSrc, {
 *       parent: this,
 *       level: this.level + 1
 *     }));
 *     ...
 *   }
 * });
 *
 * And then you could use it like this:
 *
 * let dataSrc = {
 *   label: "root",
 *   children: [{
 *     label: "foo",
 *     children: []
 *   }, {
 *     label: "bar",
 *     children: [{
 *       label: "baz",
 *       children: []
 *     }]
 *   }]
 * };
 * let root = new MyCustomTreeItem(dataSrc, { parent: null });
 * root.attachTo(nsIDOMNode);
 * root.expand();
 *
 * The following tree view will be generated (after expanding all nodes):
 * ▼ root
 *   ▶ foo
 *   ▼ bar
 *     ▶ baz
 *
 * The way the data source is implemented is completely up to you. There's
 * no assumptions made and you can use it however you like inside the
 * `_displaySelf` and `populateSelf` methods. If you need to add children to a
 * node at a later date, you just need to modify the data source:
 *
 * dataSrc[...path-to-foo...].children.push({
 *   label: "lazily-added-node"
 *   children: []
 * });
 *
 * The existing tree view will be modified like so (after expanding `foo`):
 * ▼ root
 *   ▼ foo
 *     ▶ lazily-added-node
 *   ▼ bar
 *     ▶ baz
 *
 * Everything else is taken care of automagically!
 *
 * @param AbstractTreeItem parent
 *        The parent tree item. Should be null for root items.
 * @param number level
 *        The indentation level in the tree. The root item is at level 0.
 */
function AbstractTreeItem({ parent, level }) {
  this._rootItem = parent ? parent._rootItem : this;
  this._parentItem = parent;
  this._level = level || 0;
  this._childTreeItems = [];

  // Events are always propagated through the root item. Decorating every
  // tree item as an event emitter is a very costly operation.
  if (this == this._rootItem) {
    EventEmitter.decorate(this);
  }
}
this.AbstractTreeItem = AbstractTreeItem;

AbstractTreeItem.prototype = {
  _containerNode: null,
  _targetNode: null,
  _arrowNode: null,
  _constructed: false,
  _populated: false,
  _expanded: false,

  /**
   * Optionally, trees may be allowed to automatically expand a few levels deep
   * to avoid initially displaying a completely collapsed tree.
   */
  autoExpandDepth: 0,

  /**
   * Creates the view for this tree item. Implement this method in the
   * inheriting classes to create the child node displayed in the tree.
   * Use `this.level` and the provided `arrowNode` as you see fit.
   *
   * @param nsIDOMNode document
   * @param nsIDOMNode arrowNode
   * @return nsIDOMNode
   */
  _displaySelf: function (document, arrowNode) {
    throw new Error(
      "The `_displaySelf` method needs to be implemented by inheriting classes.");
  },

  /**
   * Populates this tree item with child items, whenever it's expanded.
   * Implement this method in the inheriting classes to fill the provided
   * `children` array with AbstractTreeItem instances, which will then be
   * magically handled by this tree item.
   *
   * @param array:AbstractTreeItem children
   */
  _populateSelf: function (children) {
    throw new Error(
      "The `_populateSelf` method needs to be implemented by inheriting classes.");
  },

  /**
   * Gets the this tree's owner document.
   * @return Document
   */
  get document() {
    return this._containerNode.ownerDocument;
  },

  /**
   * Gets the root item of this tree.
   * @return AbstractTreeItem
   */
  get root() {
    return this._rootItem;
  },

  /**
   * Gets the parent of this tree item.
   * @return AbstractTreeItem
   */
  get parent() {
    return this._parentItem;
  },

  /**
   * Gets the indentation level of this tree item.
   */
  get level() {
    return this._level;
  },

  /**
   * Gets the element displaying this tree item.
   */
  get target() {
    return this._targetNode;
  },

  /**
   * Gets the element containing all tree items.
   * @return nsIDOMNode
   */
  get container() {
    return this._containerNode;
  },

  /**
   * Returns whether or not this item is populated in the tree.
   * Collapsed items can still be populated.
   * @return boolean
   */
  get populated() {
    return this._populated;
  },

  /**
   * Returns whether or not this item is expanded in the tree.
   * Expanded items with no children aren't consudered `populated`.
   * @return boolean
   */
  get expanded() {
    return this._expanded;
  },

  /**
   * Gets the bounds for this tree's container without flushing.
   * @return object
   */
  get bounds() {
    let win = this.document.defaultView;
    let utils = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
    return utils.getBoundsWithoutFlushing(this._containerNode);
  },

  /**
   * Creates and appends this tree item to the specified parent element.
   *
   * @param nsIDOMNode containerNode
   *        The parent element for this tree item (and every other tree item).
   * @param nsIDOMNode fragmentNode [optional]
   *        An optional document fragment temporarily holding this tree item in
   *        the current batch. Defaults to the `containerNode`.
   * @param nsIDOMNode beforeNode [optional]
   *        An optional child element which should succeed this tree item.
   */
  attachTo: function (containerNode, fragmentNode = containerNode, beforeNode = null) {
    this._containerNode = containerNode;
    this._constructTargetNode();

    if (beforeNode) {
      fragmentNode.insertBefore(this._targetNode, beforeNode);
    } else {
      fragmentNode.appendChild(this._targetNode);
    }

    if (this._level < this.autoExpandDepth) {
      this.expand();
    }
  },

  /**
   * Permanently removes this tree item (and all subsequent children) from the
   * parent container.
   */
  remove: function () {
    this._targetNode.remove();
    this._hideChildren();
    this._childTreeItems.length = 0;
  },

  /**
   * Focuses this item in the tree.
   */
  focus: function () {
    this._targetNode.focus();
  },

  /**
   * Expands this item in the tree.
   */
  expand: function () {
    if (this._expanded) {
      return;
    }
    this._expanded = true;
    this._arrowNode.setAttribute("open", "");
    this._targetNode.setAttribute("expanded", "");
    this._toggleChildren(true);
    this._rootItem.emit("expand", this);
  },

  /**
   * Collapses this item in the tree.
   */
  collapse: function () {
    if (!this._expanded) {
      return;
    }
    this._expanded = false;
    this._arrowNode.removeAttribute("open");
    this._targetNode.removeAttribute("expanded", "");
    this._toggleChildren(false);
    this._rootItem.emit("collapse", this);
  },

  /**
   * Returns the child item at the specified index.
   *
   * @param number index
   * @return AbstractTreeItem
   */
  getChild: function (index = 0) {
    return this._childTreeItems[index];
  },

  /**
   * Calls the provided function on all the descendants of this item.
   * If this item was never expanded, then no descendents exist yet.
   * @param function cb
   */
  traverse: function (cb) {
    for (let child of this._childTreeItems) {
      cb(child);
      child.bfs();
    }
  },

  /**
   * Calls the provided function on all descendants of this item until
   * a truthy value is returned by the predicate.
   * @param function predicate
   * @return AbstractTreeItem
   */
  find: function (predicate) {
    for (let child of this._childTreeItems) {
      if (predicate(child) || child.find(predicate)) {
        return child;
      }
    }
    return null;
  },

  /**
   * Shows or hides all the children of this item in the tree. If neessary,
   * populates this item with children.
   *
   * @param boolean visible
   *        True if the children should be visible, false otherwise.
   */
  _toggleChildren: function (visible) {
    if (visible) {
      if (!this._populated) {
        this._populateSelf(this._childTreeItems);
        this._populated = this._childTreeItems.length > 0;
      }
      this._showChildren();
    } else {
      this._hideChildren();
    }
  },

  /**
   * Shows all children of this item in the tree.
   */
  _showChildren: function () {
    // If this is the root item and we're not expanding any child nodes,
    // it is safe to append everything at once.
    if (this == this._rootItem && this.autoExpandDepth == 0) {
      this._appendChildrenBatch();
    }
    // Otherwise, append the child items and their descendants successively;
    // if not, the tree will become garbled and nodes will intertwine,
    // since all the tree items are sharing a single container node.
    else {
      this._appendChildrenSuccessive();
    }
  },

  /**
   * Hides all children of this item in the tree.
   */
  _hideChildren: function () {
    for (let item of this._childTreeItems) {
      item._targetNode.remove();
      item._hideChildren();
    }
  },

  /**
   * Appends all children in a single batch.
   * This only works properly for root nodes when no child nodes will expand.
   */
  _appendChildrenBatch: function () {
    if (this._fragment === undefined) {
      this._fragment = this.document.createDocumentFragment();
    }

    let childTreeItems = this._childTreeItems;

    for (let i = 0, len = childTreeItems.length; i < len; i++) {
      childTreeItems[i].attachTo(this._containerNode, this._fragment);
    }

    this._containerNode.appendChild(this._fragment);
  },

  /**
   * Appends all children successively.
   */
  _appendChildrenSuccessive: function () {
    let childTreeItems = this._childTreeItems;
    let expandedChildTreeItems = childTreeItems.filter(e => e._expanded);
    let nextNode = this._getSiblingAtDelta(1);

    for (let i = 0, len = childTreeItems.length; i < len; i++) {
      childTreeItems[i].attachTo(this._containerNode, undefined, nextNode);
    }
    for (let i = 0, len = expandedChildTreeItems.length; i < len; i++) {
      expandedChildTreeItems[i]._showChildren();
    }
  },

  /**
   * Constructs and stores the target node displaying this tree item.
   */
  _constructTargetNode: function () {
    if (this._constructed) {
      return;
    }
    this._onArrowClick = this._onArrowClick.bind(this);
    this._onClick = this._onClick.bind(this);
    this._onDoubleClick = this._onDoubleClick.bind(this);
    this._onKeyDown = this._onKeyDown.bind(this);
    this._onFocus = this._onFocus.bind(this);
    this._onBlur = this._onBlur.bind(this);

    let document = this.document;

    let arrowNode = this._arrowNode = document.createElement("hbox");
    arrowNode.className = "arrow theme-twisty";
    arrowNode.addEventListener("mousedown", this._onArrowClick);

    let targetNode = this._targetNode = this._displaySelf(document, arrowNode);
    targetNode.style.MozUserFocus = "normal";

    targetNode.addEventListener("mousedown", this._onClick);
    targetNode.addEventListener("dblclick", this._onDoubleClick);
    targetNode.addEventListener("keydown", this._onKeyDown);
    targetNode.addEventListener("focus", this._onFocus);
    targetNode.addEventListener("blur", this._onBlur);

    this._constructed = true;
  },

  /**
   * Gets the element displaying an item in the tree at the specified offset
   * relative to this item.
   *
   * @param number delta
   *        The offset from this item to the target item.
   * @return nsIDOMNode
   *         The element displaying the target item at the specified offset.
   */
  _getSiblingAtDelta: function (delta) {
    let childNodes = this._containerNode.childNodes;
    let indexOfSelf = Array.indexOf(childNodes, this._targetNode);
    if (indexOfSelf + delta >= 0) {
      return childNodes[indexOfSelf + delta];
    }
    return undefined;
  },

  _getNodesPerPageSize: function() {
    let childNodes = this._containerNode.childNodes;
    let nodeHeight = this._getHeight(childNodes[childNodes.length - 1]);
    let containerHeight = this.bounds.height;
    return Math.ceil(containerHeight / nodeHeight);
  },

  _getHeight: function(elem) {
    let win = this.document.defaultView;
    let utils = win.QueryInterface(Ci.nsIInterfaceRequestor)
                   .getInterface(Ci.nsIDOMWindowUtils);
    return utils.getBoundsWithoutFlushing(elem).height;
  },

  /**
   * Focuses the first item in this tree.
   */
  _focusFirstNode: function () {
    let childNodes = this._containerNode.childNodes;
    // The root node of the tree may be hidden in practice, so uses for-loop
    // here to find the next visible node.
    for (let i = 0; i < childNodes.length; i++) {
      // The height will be 0 if an element is invisible.
      if (this._getHeight(childNodes[i])) {
        childNodes[i].focus();
        return;
      }
    }
  },

  /**
   * Focuses the last item in this tree.
   */
  _focusLastNode: function () {
    let childNodes = this._containerNode.childNodes;
    childNodes[childNodes.length - 1].focus();
  },

  /**
   * Focuses the next item in this tree.
   */
  _focusNextNode: function () {
    let nextElement = this._getSiblingAtDelta(1);
    if (nextElement) nextElement.focus(); // nsIDOMNode
  },

  /**
   * Focuses the previous item in this tree.
   */
  _focusPrevNode: function () {
    let prevElement = this._getSiblingAtDelta(-1);
    if (prevElement) prevElement.focus(); // nsIDOMNode
  },

  /**
   * Focuses the parent item in this tree.
   *
   * The parent item is not always the previous item, because any tree item
   * may have multiple children.
   */
  _focusParentNode: function () {
    let parentItem = this._parentItem;
    if (parentItem) parentItem.focus(); // AbstractTreeItem
  },

  /**
   * Handler for the "click" event on the arrow node of this tree item.
   */
  _onArrowClick: function (e) {
    if (!this._expanded) {
      this.expand();
    } else {
      this.collapse();
    }
  },

  /**
   * Handler for the "click" event on the element displaying this tree item.
   */
  _onClick: function (e) {
    e.stopPropagation();
    this.focus();
  },

  /**
   * Handler for the "dblclick" event on the element displaying this tree item.
   */
  _onDoubleClick: function (e) {
    // Ignore dblclick on the arrow as it has already recived and handled two
    // click events.
    if (!e.target.classList.contains("arrow")) {
      this._onArrowClick(e);
    }
    this.focus();
  },

  /**
   * Handler for the "keydown" event on the element displaying this tree item.
   */
  _onKeyDown: function (e) {
    // Prevent scrolling when pressing navigation keys.
    ViewHelpers.preventScrolling(e);

    switch (e.keyCode) {
      case KeyCodes.DOM_VK_UP:
        this._focusPrevNode();
        return;

      case KeyCodes.DOM_VK_DOWN:
        this._focusNextNode();
        return;

      case KeyCodes.DOM_VK_LEFT:
        if (this._expanded && this._populated) {
          this.collapse();
        } else {
          this._focusParentNode();
        }
        return;

      case KeyCodes.DOM_VK_RIGHT:
        if (!this._expanded) {
          this.expand();
        } else {
          this._focusNextNode();
        }
        return;

      case KeyCodes.DOM_VK_PAGE_UP:
        let pageUpElement =
          this._getSiblingAtDelta(-this._getNodesPerPageSize());
        // There's a chance that the root node is hidden. In this case, its
        // height will be 0.
        if (pageUpElement && this._getHeight(pageUpElement)) {
          pageUpElement.focus();
        } else {
          this._focusFirstNode();
        }
        return;

      case KeyCodes.DOM_VK_PAGE_DOWN:
        let pageDownElement =
          this._getSiblingAtDelta(this._getNodesPerPageSize());
        if (pageDownElement) {
          pageDownElement.focus();
        } else {
          this._focusLastNode();
        }
        return;

      case KeyCodes.DOM_VK_HOME:
        this._focusFirstNode();
        return;

      case KeyCodes.DOM_VK_END:
        this._focusLastNode();
        return;
    }
  },

  /**
   * Handler for the "focus" event on the element displaying this tree item.
   */
  _onFocus: function (e) {
    this._rootItem.emit("focus", this);
  },

  /**
   * Handler for the "blur" event on the element displaying this tree item.
   */
  _onBlur: function (e) {
    this._rootItem.emit("blur", this);
  }
};
PK
!< 8?l=l=Hchrome/devtools/modules/devtools/client/shared/widgets/BarGraphWidget.js"use strict";

const { extend } = require("devtools/shared/extend");
const { setNamedTimeout, clearNamedTimeout } = require("devtools/client/shared/widgets/view-helpers");
const { AbstractCanvasGraph, CanvasGraphUtils } = require("devtools/client/shared/widgets/Graphs");

const HTML_NS = "http://www.w3.org/1999/xhtml";

// Bar graph constants.

const GRAPH_DAMPEN_VALUES_FACTOR = 0.75;

// The following are in pixels
const GRAPH_BARS_MARGIN_TOP = 1;
const GRAPH_BARS_MARGIN_END = 1;
const GRAPH_MIN_BARS_WIDTH = 5;
const GRAPH_MIN_BLOCKS_HEIGHT = 1;

const GRAPH_BACKGROUND_GRADIENT_START = "rgba(0,136,204,0.0)";
const GRAPH_BACKGROUND_GRADIENT_END = "rgba(255,255,255,0.25)";

const GRAPH_CLIPHEAD_LINE_COLOR = "#666";
const GRAPH_SELECTION_LINE_COLOR = "#555";
const GRAPH_SELECTION_BACKGROUND_COLOR = "rgba(0,136,204,0.25)";
const GRAPH_SELECTION_STRIPES_COLOR = "rgba(255,255,255,0.1)";
const GRAPH_REGION_BACKGROUND_COLOR = "transparent";
const GRAPH_REGION_STRIPES_COLOR = "rgba(237,38,85,0.2)";

const GRAPH_HIGHLIGHTS_MASK_BACKGROUND = "rgba(255,255,255,0.75)";
const GRAPH_HIGHLIGHTS_MASK_STRIPES = "rgba(255,255,255,0.5)";

// in ms
const GRAPH_LEGEND_MOUSEOVER_DEBOUNCE = 50;

/**
 * A bar graph, plotting tuples of values as rectangles.
 *
 * @see AbstractCanvasGraph for emitted events and other options.
 *
 * Example usage:
 *   let graph = new BarGraphWidget(node);
 *   graph.format = ...;
 *   graph.once("ready", () => {
 *     graph.setData(src);
 *   });
 *
 * The `graph.format` traits are mandatory and will determine how the values
 * are styled as "blocks" in every "bar":
 *   [
 *     { color: "#f00", label: "Foo" },
 *     { color: "#0f0", label: "Bar" },
 *     ...
 *     { color: "#00f", label: "Baz" }
 *   ]
 *
 * Data source format:
 *   [
 *     { delta: x1, values: [y11, y12, ... y1n] },
 *     { delta: x2, values: [y21, y22, ... y2n] },
 *     ...
 *     { delta: xm, values: [ym1, ym2, ... ymn] }
 *   ]
 * where each item in the array represents a "bar", for which every value
 * represents a "block" inside that "bar", plotted at the "delta" position.
 *
 * @param nsIDOMNode parent
 *        The parent node holding the graph.
 */
this.BarGraphWidget = function (parent, ...args) {
  AbstractCanvasGraph.apply(this, [parent, "bar-graph", ...args]);

  this.once("ready", () => {
    this._onLegendMouseOver = this._onLegendMouseOver.bind(this);
    this._onLegendMouseOut = this._onLegendMouseOut.bind(this);
    this._onLegendMouseDown = this._onLegendMouseDown.bind(this);
    this._onLegendMouseUp = this._onLegendMouseUp.bind(this);
    this._createLegend();
  });
};

BarGraphWidget.prototype = extend(AbstractCanvasGraph.prototype, {
  clipheadLineColor: GRAPH_CLIPHEAD_LINE_COLOR,
  selectionLineColor: GRAPH_SELECTION_LINE_COLOR,
  selectionBackgroundColor: GRAPH_SELECTION_BACKGROUND_COLOR,
  selectionStripesColor: GRAPH_SELECTION_STRIPES_COLOR,
  regionBackgroundColor: GRAPH_REGION_BACKGROUND_COLOR,
  regionStripesColor: GRAPH_REGION_STRIPES_COLOR,

  /**
   * List of colors used to fill each block inside every bar, also
   * corresponding to labels displayed in this graph's legend.
   * @see constructor
   */
  format: null,

  /**
   * Optionally offsets the `delta` in the data source by this scalar.
   */
  dataOffsetX: 0,

  /**
   * Optionally uses this value instead of the last tick in the data source
   * to compute the horizontal scaling.
   */
  dataDuration: 0,

  /**
   * The scalar used to multiply the graph values to leave some headroom
   * on the top.
   */
  dampenValuesFactor: GRAPH_DAMPEN_VALUES_FACTOR,

  /**
   * Bars that are too close too each other in the graph will be combined.
   * This scalar specifies the required minimum width of each bar.
   */
  minBarsWidth: GRAPH_MIN_BARS_WIDTH,

  /**
   * Blocks in a bar that are too thin inside the bar will not be rendered.
   * This scalar specifies the required minimum height of each block.
   */
  minBlocksHeight: GRAPH_MIN_BLOCKS_HEIGHT,

  /**
   * Renders the graph's background.
   * @see AbstractCanvasGraph.prototype.buildBackgroundImage
   */
  buildBackgroundImage: function () {
    let { canvas, ctx } = this._getNamedCanvas("bar-graph-background");
    let width = this._width;
    let height = this._height;

    let gradient = ctx.createLinearGradient(0, 0, 0, height);
    gradient.addColorStop(0, GRAPH_BACKGROUND_GRADIENT_START);
    gradient.addColorStop(1, GRAPH_BACKGROUND_GRADIENT_END);
    ctx.fillStyle = gradient;
    ctx.fillRect(0, 0, width, height);

    return canvas;
  },

  /**
   * Renders the graph's data source.
   * @see AbstractCanvasGraph.prototype.buildGraphImage
   */
  buildGraphImage: function () {
    if (!this.format || !this.format.length) {
      throw new Error("The graph format traits are mandatory to style " +
                      "the data source.");
    }
    let { canvas, ctx } = this._getNamedCanvas("bar-graph-data");
    let width = this._width;
    let height = this._height;

    let totalTypes = this.format.length;
    let totalTicks = this._data.length;
    let lastTick = this._data[totalTicks - 1].delta;

    let minBarsWidth = this.minBarsWidth * this._pixelRatio;
    let minBlocksHeight = this.minBlocksHeight * this._pixelRatio;

    let duration = this.dataDuration || lastTick;
    let dataScaleX = this.dataScaleX = width / (duration - this.dataOffsetX);
    let dataScaleY = this.dataScaleY = height / this._calcMaxHeight({
      data: this._data,
      dataScaleX: dataScaleX,
      minBarsWidth: minBarsWidth
    }) * this.dampenValuesFactor;

    // Draw the graph.

    // Iterate over the blocks, then the bars, to draw all rectangles of
    // the same color in a single pass. See the @constructor for more
    // information about the data source, and how a "bar" contains "blocks".

    this._blocksBoundingRects = [];
    let prevHeight = [];
    let scaledMarginEnd = GRAPH_BARS_MARGIN_END * this._pixelRatio;
    let scaledMarginTop = GRAPH_BARS_MARGIN_TOP * this._pixelRatio;

    for (let type = 0; type < totalTypes; type++) {
      ctx.fillStyle = this.format[type].color || "#000";
      ctx.beginPath();

      let prevRight = 0;
      let skippedCount = 0;
      let skippedHeight = 0;

      for (let tick = 0; tick < totalTicks; tick++) {
        let delta = this._data[tick].delta;
        let value = this._data[tick].values[type] || 0;
        let blockRight = (delta - this.dataOffsetX) * dataScaleX;
        let blockHeight = value * dataScaleY;

        let blockWidth = blockRight - prevRight;
        if (blockWidth < minBarsWidth) {
          skippedCount++;
          skippedHeight += blockHeight;
          continue;
        }

        let averageHeight = (blockHeight + skippedHeight) / (skippedCount + 1);
        if (averageHeight >= minBlocksHeight) {
          let bottom = height - ~~prevHeight[tick];
          ctx.moveTo(prevRight, bottom);
          ctx.lineTo(prevRight, bottom - averageHeight);
          ctx.lineTo(blockRight, bottom - averageHeight);
          ctx.lineTo(blockRight, bottom);

          // Remember this block's type and location.
          this._blocksBoundingRects.push({
            type: type,
            start: prevRight,
            end: blockRight,
            top: bottom - averageHeight,
            bottom: bottom
          });

          if (prevHeight[tick] === undefined) {
            prevHeight[tick] = averageHeight + scaledMarginTop;
          } else {
            prevHeight[tick] += averageHeight + scaledMarginTop;
          }
        }

        prevRight += blockWidth + scaledMarginEnd;
        skippedHeight = 0;
        skippedCount = 0;
      }

      ctx.fill();
    }

    // The blocks bounding rects isn't guaranteed to be sorted ascending by
    // block location on the X axis. This should be the case, for better
    // cache cohesion and a faster `buildMaskImage`.
    this._blocksBoundingRects.sort((a, b) => a.start > b.start ? 1 : -1);

    // Update the legend.

    while (this._legendNode.hasChildNodes()) {
      this._legendNode.firstChild.remove();
    }
    for (let { color, label } of this.format) {
      this._createLegendItem(color, label);
    }

    return canvas;
  },

  /**
   * Renders the graph's mask.
   * Fades in only the parts of the graph that are inside the specified areas.
   *
   * @param array highlights
   *        A list of { start, end } values. Optionally, each object
   *        in the list may also specify { top, bottom } pixel values if the
   *        highlighting shouldn't span across the full height of the graph.
   * @param boolean inPixels
   *        Set this to true if the { start, end } values in the highlights
   *        list are pixel values, and not values from the data source.
   * @param function unpack [optional]
   *        @see AbstractCanvasGraph.prototype.getMappedSelection
   */
  buildMaskImage: function (highlights, inPixels = false,
                            unpack = e => e.delta) {
    // A null `highlights` array is used to clear the mask. An empty array
    // will mask the entire graph.
    if (!highlights) {
      return null;
    }

    // Get a render target for the highlights. It will be overlaid on top of
    // the existing graph, masking the areas that aren't highlighted.

    let { canvas, ctx } = this._getNamedCanvas("graph-highlights");
    let width = this._width;
    let height = this._height;

    // Draw the background mask.

    let pattern = AbstractCanvasGraph.getStripePattern({
      ownerDocument: this._document,
      backgroundColor: GRAPH_HIGHLIGHTS_MASK_BACKGROUND,
      stripesColor: GRAPH_HIGHLIGHTS_MASK_STRIPES
    });
    ctx.fillStyle = pattern;
    ctx.fillRect(0, 0, width, height);

    // Clear highlighted areas.

    let totalTicks = this._data.length;
    let firstTick = unpack(this._data[0]);
    let lastTick = unpack(this._data[totalTicks - 1]);

    for (let { start, end, top, bottom } of highlights) {
      if (!inPixels) {
        start = CanvasGraphUtils.map(start, firstTick, lastTick, 0, width);
        end = CanvasGraphUtils.map(end, firstTick, lastTick, 0, width);
      }
      let firstSnap = findFirst(this._blocksBoundingRects,
                                e => e.start >= start);
      let lastSnap = findLast(this._blocksBoundingRects,
                              e => e.start >= start && e.end <= end);

      let x1 = firstSnap ? firstSnap.start : start;
      let x2;
      if (lastSnap) {
        x2 = lastSnap.end;
      } else {
        x2 = firstSnap ? firstSnap.end : end;
      }

      let y1 = top || 0;
      let y2 = bottom || height;
      ctx.clearRect(x1, y1, x2 - x1, y2 - y1);
    }

    return canvas;
  },

  /**
   * A list storing the bounding rectangle for each drawn block in the graph.
   * Created whenever `buildGraphImage` is invoked.
   */
  _blocksBoundingRects: null,

  /**
   * Calculates the height of the tallest bar that would eventially be rendered
   * in this graph.
   *
   * Bars that are too close too each other in the graph will be combined.
   * @see `minBarsWidth`
   *
   * @return number
   *         The tallest bar height in this graph.
   */
  _calcMaxHeight: function ({ data, dataScaleX, minBarsWidth }) {
    let maxHeight = 0;
    let prevRight = 0;
    let skippedCount = 0;
    let skippedHeight = 0;
    let scaledMarginEnd = GRAPH_BARS_MARGIN_END * this._pixelRatio;

    for (let { delta, values } of data) {
      let barRight = (delta - this.dataOffsetX) * dataScaleX;
      let barHeight = values.reduce((a, b) => a + b, 0);

      let barWidth = barRight - prevRight;
      if (barWidth < minBarsWidth) {
        skippedCount++;
        skippedHeight += barHeight;
        continue;
      }

      let averageHeight = (barHeight + skippedHeight) / (skippedCount + 1);
      maxHeight = Math.max(averageHeight, maxHeight);

      prevRight += barWidth + scaledMarginEnd;
      skippedHeight = 0;
      skippedCount = 0;
    }

    return maxHeight;
  },

  /**
   * Creates the legend container when constructing this graph.
   */
  _createLegend: function () {
    let legendNode = this._legendNode = this._document.createElementNS(HTML_NS,
                                                                       "div");
    legendNode.className = "bar-graph-widget-legend";
    this._container.appendChild(legendNode);
  },

  /**
   * Creates a legend item when constructing this graph.
   */
  _createLegendItem: function (color, label) {
    let itemNode = this._document.createElementNS(HTML_NS, "div");
    itemNode.className = "bar-graph-widget-legend-item";

    let colorNode = this._document.createElementNS(HTML_NS, "span");
    colorNode.setAttribute("view", "color");
    colorNode.setAttribute("data-index", this._legendNode.childNodes.length);
    colorNode.style.backgroundColor = color;
    colorNode.addEventListener("mouseover", this._onLegendMouseOver);
    colorNode.addEventListener("mouseout", this._onLegendMouseOut);
    colorNode.addEventListener("mousedown", this._onLegendMouseDown);
    colorNode.addEventListener("mouseup", this._onLegendMouseUp);

    let labelNode = this._document.createElementNS(HTML_NS, "span");
    labelNode.setAttribute("view", "label");
    labelNode.textContent = label;

    itemNode.appendChild(colorNode);
    itemNode.appendChild(labelNode);
    this._legendNode.appendChild(itemNode);
  },

  /**
   * Invoked whenever a color node in the legend is hovered.
   */
  _onLegendMouseOver: function (ev) {
    setNamedTimeout(
      "bar-graph-debounce",
      GRAPH_LEGEND_MOUSEOVER_DEBOUNCE,
      () => {
        let type = ev.target.dataset.index;
        let rects = this._blocksBoundingRects.filter(e => e.type == type);

        this._originalHighlights = this._mask;
        this._hasCustomHighlights = true;
        this.setMask(rects, true);

        this.emit("legend-hover", [type, rects]);
      }
    );
  },

  /**
   * Invoked whenever a color node in the legend is unhovered.
   */
  _onLegendMouseOut: function () {
    clearNamedTimeout("bar-graph-debounce");

    if (this._hasCustomHighlights) {
      this.setMask(this._originalHighlights);
      this._hasCustomHighlights = false;
      this._originalHighlights = null;
    }

    this.emit("legend-unhover");
  },

  /**
   * Invoked whenever a color node in the legend is pressed.
   */
  _onLegendMouseDown: function (ev) {
    ev.preventDefault();
    ev.stopPropagation();

    let type = ev.target.dataset.index;
    let rects = this._blocksBoundingRects.filter(e => e.type == type);
    let leftmost = rects[0];
    let rightmost = rects[rects.length - 1];
    if (!leftmost || !rightmost) {
      this.dropSelection();
    } else {
      this.setSelection({ start: leftmost.start, end: rightmost.end });
    }

    this.emit("legend-selection", [leftmost, rightmost]);
  },

  /**
   * Invoked whenever a color node in the legend is released.
   */
  _onLegendMouseUp: function (e) {
    e.preventDefault();
    e.stopPropagation();
  }
});

/**
 * Finds the first element in an array that validates a predicate.
 * @param array
 * @param function predicate
 * @return number
 */
function findFirst(array, predicate) {
  for (let i = 0, len = array.length; i < len; i++) {
    let element = array[i];
    if (predicate(element)) {
      return element;
    }
  }
  return null;
}

/**
 * Finds the last element in an array that validates a predicate.
 * @param array
 * @param function predicate
 * @return number
 */
function findLast(array, predicate) {
  for (let i = array.length - 1; i >= 0; i--) {
    let element = array[i];
    if (predicate(element)) {
      return element;
    }
  }
  return null;
}

module.exports = BarGraphWidget;
PK
!<k��Lchrome/devtools/modules/devtools/client/shared/widgets/BreadcrumbsWidget.jsm/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 ENSURE_SELECTION_VISIBLE_DELAY = 50; // ms

const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
const { ViewHelpers, setNamedTimeout } = require("devtools/client/shared/widgets/view-helpers");
const EventEmitter = require("devtools/shared/event-emitter");

this.EXPORTED_SYMBOLS = ["BreadcrumbsWidget"];

/**
 * A breadcrumb-like list of items.
 *
 * Note: this widget should be used in tandem with the WidgetMethods in
 * view-helpers.js.
 *
 * @param nsIDOMNode aNode
 *        The element associated with the widget.
 * @param Object aOptions
 *        - smoothScroll: specifies if smooth scrolling on selection is enabled.
 */
this.BreadcrumbsWidget = function BreadcrumbsWidget(aNode, aOptions = {}) {
  this.document = aNode.ownerDocument;
  this.window = this.document.defaultView;
  this._parent = aNode;

  // Create an internal arrowscrollbox container.
  this._list = this.document.createElement("arrowscrollbox");
  this._list.className = "breadcrumbs-widget-container";
  this._list.setAttribute("flex", "1");
  this._list.setAttribute("orient", "horizontal");
  this._list.setAttribute("clicktoscroll", "true");
  this._list.setAttribute("smoothscroll", !!aOptions.smoothScroll);
  this._list.addEventListener("keydown", e => this.emit("keyDown", e));
  this._list.addEventListener("mousedown", e => this.emit("mousePress", e));
  this._parent.appendChild(this._list);

  // By default, hide the arrows. We let the arrowscrollbox show them
  // in case of overflow.
  this._list._scrollButtonUp.collapsed = true;
  this._list._scrollButtonDown.collapsed = true;
  this._list.addEventListener("underflow", this._onUnderflow.bind(this));
  this._list.addEventListener("overflow", this._onOverflow.bind(this));

  // This widget emits events that can be handled in a MenuContainer.
  EventEmitter.decorate(this);

  // Delegate some of the associated node's methods to satisfy the interface
  // required by MenuContainer instances.
  ViewHelpers.delegateWidgetAttributeMethods(this, aNode);
  ViewHelpers.delegateWidgetEventMethods(this, aNode);
};

BreadcrumbsWidget.prototype = {
  /**
   * Inserts an item in this container at the specified index.
   *
   * @param number aIndex
   *        The position in the container intended for this item.
   * @param nsIDOMNode aContents
   *        The node displayed in the container.
   * @return nsIDOMNode
   *         The element associated with the displayed item.
   */
  insertItemAt: function (aIndex, aContents) {
    let list = this._list;
    let breadcrumb = new Breadcrumb(this, aContents);
    return list.insertBefore(breadcrumb._target, list.childNodes[aIndex]);
  },

  /**
   * Returns the child node in this container situated at the specified index.
   *
   * @param number aIndex
   *        The position in the container intended for this item.
   * @return nsIDOMNode
   *         The element associated with the displayed item.
   */
  getItemAtIndex: function (aIndex) {
    return this._list.childNodes[aIndex];
  },

  /**
   * Removes the specified child node from this container.
   *
   * @param nsIDOMNode aChild
   *        The element associated with the displayed item.
   */
  removeChild: function (aChild) {
    this._list.removeChild(aChild);

    if (this._selectedItem == aChild) {
      this._selectedItem = null;
    }
  },

  /**
   * Removes all of the child nodes from this container.
   */
  removeAllItems: function () {
    let list = this._list;

    while (list.hasChildNodes()) {
      list.firstChild.remove();
    }

    this._selectedItem = null;
  },

  /**
   * Gets the currently selected child node in this container.
   * @return nsIDOMNode
   */
  get selectedItem() {
    return this._selectedItem;
  },

  /**
   * Sets the currently selected child node in this container.
   * @param nsIDOMNode aChild
   */
  set selectedItem(aChild) {
    let childNodes = this._list.childNodes;

    if (!aChild) {
      this._selectedItem = null;
    }
    for (let node of childNodes) {
      if (node == aChild) {
        node.setAttribute("checked", "");
        this._selectedItem = node;
      } else {
        node.removeAttribute("checked");
      }
    }
  },

  /**
   * Returns the value of the named attribute on this container.
   *
   * @param string aName
   *        The name of the attribute.
   * @return string
   *         The current attribute value.
   */
  getAttribute: function (aName) {
    if (aName == "scrollPosition") return this._list.scrollPosition;
    if (aName == "scrollWidth") return this._list.scrollWidth;
    return this._parent.getAttribute(aName);
  },

  /**
   * Ensures the specified element is visible.
   *
   * @param nsIDOMNode aElement
   *        The element to make visible.
   */
  ensureElementIsVisible: function (aElement) {
    if (!aElement) {
      return;
    }

    // Repeated calls to ensureElementIsVisible would interfere with each other
    // and may sometimes result in incorrect scroll positions.
    setNamedTimeout("breadcrumb-select", ENSURE_SELECTION_VISIBLE_DELAY, () => {
      if (this._list.ensureElementIsVisible) {
        this._list.ensureElementIsVisible(aElement);
      }
    });
  },

  /**
   * The underflow and overflow listener for the arrowscrollbox container.
   */
  _onUnderflow: function ({ target }) {
    if (target != this._list) {
      return;
    }
    target._scrollButtonUp.collapsed = true;
    target._scrollButtonDown.collapsed = true;
    target.removeAttribute("overflows");
  },

  /**
   * The underflow and overflow listener for the arrowscrollbox container.
   */
  _onOverflow: function ({ target }) {
    if (target != this._list) {
      return;
    }
    target._scrollButtonUp.collapsed = false;
    target._scrollButtonDown.collapsed = false;
    target.setAttribute("overflows", "");
  },

  window: null,
  document: null,
  _parent: null,
  _list: null,
  _selectedItem: null
};

/**
 * A Breadcrumb constructor for the BreadcrumbsWidget.
 *
 * @param BreadcrumbsWidget aWidget
 *        The widget to contain this breadcrumb.
 * @param nsIDOMNode aContents
 *        The node displayed in the container.
 */
function Breadcrumb(aWidget, aContents) {
  this.document = aWidget.document;
  this.window = aWidget.window;
  this.ownerView = aWidget;

  this._target = this.document.createElement("hbox");
  this._target.className = "breadcrumbs-widget-item";
  this._target.setAttribute("align", "center");
  this.contents = aContents;
}

Breadcrumb.prototype = {
  /**
   * Sets the contents displayed in this item's view.
   *
   * @param string | nsIDOMNode aContents
   *        The string or node displayed in the container.
   */
  set contents(aContents) {
    // If there are already some contents displayed, replace them.
    if (this._target.hasChildNodes()) {
      this._target.replaceChild(aContents, this._target.firstChild);
      return;
    }
    // These are the first contents ever displayed.
    this._target.appendChild(aContents);
  },

  window: null,
  document: null,
  ownerView: null,
  _target: null
};
PK
!<d)ҥ?�??chrome/devtools/modules/devtools/client/shared/widgets/Chart.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/shared/event-emitter");
const { LocalizationHelper } = require("devtools/shared/l10n");
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 nsIDOMNode displaying it.
 *
 * @param nsIDOMNode 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 nsIDOMNode displaying it.
 *
 * @param nsIDOMNode 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 nsIDOMNode 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 nsIDocument 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));
  }

  let pie = Chart.Pie(document, {
    width: diameter,
    data: data
  });

  let table = Chart.Table(document, {
    title: title,
    data: data,
    strings: strings,
    totals: totals,
    header: header,
  });

  let container = document.createElement("div");
  container.className = "pie-table-chart-container";
  container.appendChild(pie.node);
  container.appendChild(table.node);

  let 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 nsIDocument 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;

  // Filter out very small sizes, as they'll just render invisible slices.
  data = data ? data.filter(e => e.size > EPSILON) : null;

  // If there's no data available, display an empty placeholder.
  if (!data) {
    data = loadingPieChartData();
    isPlaceholder = true;
  }
  if (!data.length) {
    data = emptyPieChartData();
    isPlaceholder = true;
  }

  let container = document.createElementNS(SVG_NS, "svg");
  container.setAttribute("class", "generic-chart-container pie-chart-container");
  container.setAttribute("pack", "center");
  container.setAttribute("flex", "1");
  container.setAttribute("width", width);
  container.setAttribute("height", height);
  container.setAttribute("viewBox", "0 0 " + width + " " + height);
  container.setAttribute("slices", data.length);
  container.setAttribute("placeholder", isPlaceholder);

  let proxy = new PieChart(container);

  let total = data.reduce((acc, e) => acc + e.size, 0);
  let angles = data.map(e => e.size / total * (TAU - EPSILON));
  let largest = data.reduce((a, b) => a.size > b.size ? a : b);
  let smallest = data.reduce((a, b) => a.size < b.size ? a : b);

  let textDistance = radius / NAMED_SLICE_TEXT_DISTANCE_RATIO;
  let 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--) {
    let sliceInfo = data[i];
    let sliceAngle = angles[i];
    if (!sliceInfo.size || sliceAngle < EPSILON) {
      continue;
    }

    endAngle = startAngle - sliceAngle;
    midAngle = (startAngle + endAngle) / 2;

    let x1 = centerX + radius * Math.sin(startAngle);
    let y1 = centerY - radius * Math.cos(startAngle);
    let x2 = centerX + radius * Math.sin(endAngle);
    let y2 = centerY - radius * Math.cos(endAngle);
    let largeArcFlag = Math.abs(startAngle - endAngle) > PI ? 1 : 0;

    let pathNode = document.createElementNS(SVG_NS, "path");
    pathNode.setAttribute("class", "pie-chart-slice chart-colored-blob");
    pathNode.setAttribute("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", "");
    }

    let hoverX = translateDistance * Math.sin(midAngle);
    let hoverY = -translateDistance * Math.cos(midAngle);
    let hoverTransform = "transform: translate(" + hoverX + "px, " + hoverY + "px)";
    pathNode.setAttribute("style", data.length > 1 ? hoverTransform : "");

    proxy.slices.set(sliceInfo, pathNode);
    delegate(proxy, ["click", "mouseover", "mouseout"], pathNode, sliceInfo);
    container.appendChild(pathNode);

    if (sliceInfo.label && sliceAngle > NAMED_SLICE_MIN_ANGLE) {
      let textX = centerX + textDistance * Math.sin(midAngle);
      let textY = centerY - textDistance * Math.cos(midAngle);
      let label = document.createElementNS(SVG_NS, "text");
      label.appendChild(document.createTextNode(sliceInfo.label));
      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);
      container.appendChild(label);
    }

    startAngle = endAngle;
  }

  return proxy;
}

/**
 * Creates the DOM for a table chart based on the specified properties.
 *
 * @param nsIDocument 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;
  }

  let container = document.createElement("div");
  container.className = "generic-chart-container table-chart-container";
  container.setAttribute("pack", "center");
  container.setAttribute("flex", "1");
  container.setAttribute("rows", data.length);
  container.setAttribute("placeholder", isPlaceholder);
  container.setAttribute("style", "-moz-box-orient: vertical");

  let proxy = new TableChart(container);

  let titleNode = document.createElement("span");
  titleNode.className = "plain table-chart-title";
  titleNode.textContent = title;
  container.appendChild(titleNode);

  let tableNode = document.createElement("div");
  tableNode.className = "plain table-chart-grid";
  tableNode.setAttribute("style", "-moz-box-orient: vertical");
  container.appendChild(tableNode);

  const headerNode = document.createElement("div");
  headerNode.className = "table-chart-row";

  const headerBoxNode = document.createElement("div");
  headerBoxNode.className = "table-chart-row-box";
  headerNode.appendChild(headerBoxNode);

  for (let [key, value] of Object.entries(header)) {
    let headerLabelNode = document.createElement("span");
    headerLabelNode.className = "plain table-chart-row-label";
    headerLabelNode.setAttribute("name", key);
    headerLabelNode.textContent = value;

    headerNode.appendChild(headerLabelNode);
  }

  tableNode.appendChild(headerNode);

  for (let rowInfo of data) {
    let rowNode = document.createElement("div");
    rowNode.className = "table-chart-row";
    rowNode.setAttribute("align", "center");

    let boxNode = document.createElement("div");
    boxNode.className = "table-chart-row-box chart-colored-blob";
    boxNode.setAttribute("name", rowInfo.label);
    rowNode.appendChild(boxNode);

    for (let [key, value] of Object.entries(rowInfo)) {
      let index = data.indexOf(rowInfo);
      let stringified = strings[key] ? strings[key](value, index) : value;
      let labelNode = document.createElement("span");
      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);
    tableNode.appendChild(rowNode);
  }

  let totalsNode = document.createElement("div");
  totalsNode.className = "table-chart-totals";
  totalsNode.setAttribute("style", "-moz-box-orient: vertical");

  for (let [key, value] of Object.entries(totals)) {
    let total = data.reduce((acc, e) => acc + e[key], 0);
    let stringified = value ? value(total || 0) : total;
    let 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 an nsIDOMNode to an EventEmitter proxy.
 *
 * @param EventEmitter emitter
 *        The event emitter proxy instance.
 * @param array events
 *        An array of events, e.g. ["mouseover", "mouseout"].
 * @param nsIDOMNode 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 (let event of events) {
    node.addEventListener(event, emitter.emit.bind(emitter, event, args));
  }
}

exports.Chart = Chart;
PK
!<�Y\�V�VEchrome/devtools/modules/devtools/client/shared/widgets/ColorWidget.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 file is a new working copy of Spectrum.js for the purposes of refreshing the color
 * widget. It is hidden behind a pref("devtools.inspector.colorWidget.enabled").
 */

"use strict";

const {Task} = require("devtools/shared/task");
const EventEmitter = require("devtools/shared/event-emitter");
const {colorUtils} = require("devtools/shared/css/color");
const {LocalizationHelper} = require("devtools/shared/l10n");
const L10N = new LocalizationHelper("devtools/client/locales/inspector.properties");

const XHTML_NS = "http://www.w3.org/1999/xhtml";
const SAMPLE_TEXT = "Abc";

/**
 * ColorWidget creates a color picker widget in any container you give it.
 *
 * Simple usage example:
 *
 * const {ColorWidget} = require("devtools/client/shared/widgets/ColorWidget");
 * let s = new ColorWidget(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 the color widget to correctly initialize its various parts.
 *
 * Fires the following events:
 * - changed : When the user changes the current color
 */
function ColorWidget(parentEl, rgb) {
  EventEmitter.decorate(this);

  this.parentEl = parentEl;

  this.onAlphaSliderMove = this.onAlphaSliderMove.bind(this);
  this.onElementClick = this.onElementClick.bind(this);
  this.onDraggerMove = this.onDraggerMove.bind(this);
  this.onHexInputChange = this.onHexInputChange.bind(this);
  this.onHslaInputChange = this.onHslaInputChange.bind(this);
  this.onRgbaInputChange = this.onRgbaInputChange.bind(this);
  this.onSelectValueChange = this.onSelectValueChange.bind(this);
  this.onSliderMove = this.onSliderMove.bind(this);
  this.updateContrast = this.updateContrast.bind(this);

  this.initializeColorWidget();

  if (rgb) {
    this.rgb = rgb;
    this.updateUI();
  }
}

module.exports.ColorWidget = ColorWidget;

/**
 * Calculates the contrast grade and title for the given contrast
 * ratio and background color.
 * @param {Number} contrastRatio Contrast ratio to calculate grade.
 * @param {String} backgroundColor A string of the form `rgba(r, g, b, a)`
 * where r, g, b and a are floats.
 * @return {Object} An object of the form {grade, title}.
 * |grade| is a string containing the contrast grade.
 * |title| is a string containing the title of the colorwidget.
 */
ColorWidget.calculateGradeAndTitle = function (contrastRatio, backgroundColor) {
  let grade = "";
  let title = "";

  if (contrastRatio < 3.0) {
    grade = L10N.getStr("inspector.colorwidget.contrastRatio.failGrade");
    title = L10N.getStr("inspector.colorwidget.contrastRatio.failInfo");
  } else if (contrastRatio < 4.5) {
    grade = "AA*";
    title = L10N.getStr("inspector.colorwidget.contrastRatio.AABigInfo");
  } else if (contrastRatio < 7.0) {
    grade = "AAA*";
    title = L10N.getStr("inspector.colorwidget.contrastRatio.AAABigInfo");
  } else if (contrastRatio < 22.0) {
    grade = "AAA";
    title = L10N.getStr("inspector.colorwidget.contrastRatio.AAAInfo");
  }
  title += "\n";
  title += L10N.getStr("inspector.colorwidget.contrastRatio.info") + " ";
  title += backgroundColor;

  return { grade, title };
};

/**
 * Converts the contrastRatio to a string of length 4 by rounding
 * contrastRatio and padding the required number of 0s before or
 * after.
 * @param {Number} contrastRatio The contrast ratio to be formatted.
 * @return {String} The formatted ratio.
 */
ColorWidget.ratioToString = function (contrastRatio) {
  let formattedRatio = (contrastRatio < 10) ? "0" : "";
  formattedRatio += contrastRatio.toFixed(2);
  return formattedRatio;
};

ColorWidget.hsvToRgb = function (h, s, v, a) {
  let r, g, b;

  let i = Math.floor(h * 6);
  let f = h * 6 - i;
  let p = v * (1 - s);
  let q = v * (1 - f * s);
  let 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];
};

ColorWidget.rgbToHsv = function (r, g, b, a) {
  r = r / 255;
  g = g / 255;
  b = b / 255;

  let max = Math.max(r, g, b), min = Math.min(r, g, b);
  let h, s, v = max;

  let d = max - min;
  s = max == 0 ? 0 : d / max;

  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];
};

ColorWidget.hslToCssString = function (h, s, l, a) {
  return `hsla(${h}, ${s}%, ${l}%, ${a})`;
};

ColorWidget.draggable = function (element, onmove, onstart, onstop) {
  onmove = onmove || function () {};
  onstart = onstart || function () {};
  onstop = onstop || function () {};

  let doc = element.ownerDocument;
  let dragging = false;
  let offset = {};
  let maxHeight = 0;
  let maxWidth = 0;

  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;
      }
      let pageX = e.pageX;
      let pageY = e.pageY;

      let dragX = Math.max(0, Math.min(pageX - offset.left, maxWidth));
      let dragY = Math.max(0, Math.min(pageY - offset.top, maxHeight));

      onmove.apply(element, [dragX, dragY]);
    }
  }

  function start(e) {
    let rightclick = e.which === 3;

    if (!rightclick && !dragging) {
      if (onstart.apply(element, arguments) !== false) {
        dragging = true;
        maxHeight = element.offsetHeight;
        maxWidth = element.offsetWidth;

        offset = element.getBoundingClientRect();

        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);
      onstop.apply(element, arguments);
    }
    dragging = false;
  }

  element.addEventListener("mousedown", start);
};

ColorWidget.prototype = {
  set rgb(color) {
    this.hsv = ColorWidget.rgbToHsv(color[0], color[1], color[2], color[3]);

    let { h, s, l } = new colorUtils.CssColor(this.rgbCssString)._getHSLATuple();
    this.hsl = [h, s, l, color[3]];
  },

  get rgb() {
    let rgb = ColorWidget.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];
  },

  get rgbNoSatVal() {
    let rgb = ColorWidget.hsvToRgb(this.hsv[0], 1, 1);
    return [Math.round(rgb[0]), Math.round(rgb[1]), Math.round(rgb[2]), rgb[3]];
  },

  get rgbCssString() {
    let rgb = this.rgb;
    return "rgba(" + rgb[0] + ", " + rgb[1] + ", " + rgb[2] + ", " +
      rgb[3] + ")";
  },

  initializeColorWidget: function () {
    let colorNameLabel = L10N.getStr("inspector.colorwidget.colorNameLabel");
    this.parentEl.innerHTML = "";
    this.element = this.parentEl.ownerDocument.createElementNS(XHTML_NS, "div");

    this.element.className = "colorwidget-container";
    // eslint-disable-next-line no-unsanitized/property
    this.element.innerHTML = `
      <div class="colorwidget-top">
        <div class="colorwidget-fill"></div>
        <div class="colorwidget-top-inner">
          <div class="colorwidget-color colorwidget-box">
            <div class="colorwidget-sat">
              <div class="colorwidget-val">
                <div class="colorwidget-dragger"></div>
              </div>
            </div>
          </div>
          <div class="colorwidget-hue colorwidget-box">
            <div class="colorwidget-slider colorwidget-slider-control"></div>
          </div>
        </div>
      </div>
      <div class="colorwidget-alpha colorwidget-checker colorwidget-box">
        <div class="colorwidget-alpha-inner">
          <div class="colorwidget-alpha-handle colorwidget-slider-control"></div>
        </div>
      </div>
      <div class="colorwidget-value">
        <select class="colorwidget-select">
          <option value="hex">Hex</option>
          <option value="rgba">RGBA</option>
          <option value="hsla">HSLA</option>
        </select>
        <div class="colorwidget-hex">
          <input class="colorwidget-hex-input"/>
        </div>
        <div class="colorwidget-rgba colorwidget-hidden">
          <input class="colorwidget-rgba-r" data-id="r" />
          <input class="colorwidget-rgba-g" data-id="g" />
          <input class="colorwidget-rgba-b" data-id="b" />
          <input class="colorwidget-rgba-a" data-id="a" />
        </div>
        <div class="colorwidget-hsla colorwidget-hidden">
          <input class="colorwidget-hsla-h" data-id="h" />
          <input class="colorwidget-hsla-s" data-id="s" />
          <input class="colorwidget-hsla-l" data-id="l" />
          <input class="colorwidget-hsla-a" data-id="a" />
        </div>
      </div>
      <div class="colorwidget-colorname">
        <label class="colorwidget-colorname-label">${colorNameLabel}</label>
        <span class="colorwidget-colorname-value"></span>
      </div>
    <div class="colorwidget-contrast">
      <div class="colorwidget-contrast-info"></div>
      <div class="colorwidget-contrast-inner">
        <span class="colorwidget-colorswatch"></span>
        <span class="colorwidget-contrast-ratio"></span>
        <span class="colorwidget-contrast-grade"></span>
        <button class="colorwidget-contrast-help devtools-button"></button>
      </div>
    </div>
    `;

    this.element.addEventListener("click", this.onElementClick);

    this.parentEl.appendChild(this.element);

    this.closestBackgroundColor = "rgba(255, 255, 255, 1)";

    this.colorName = this.element.querySelector(".colorwidget-colorname-value");

    this.contrast = this.element.querySelector(".colorwidget-contrast");
    this.contrastInfo = this.element.querySelector(".colorwidget-contrast-info");
    this.contrastInfo.textContent = L10N.getStr(
      "inspector.colorwidget.contrastRatio.header"
    );

    this.contrastInner = this.element.querySelector(".colorwidget-contrast-inner");
    this.contrastSwatch = this.contrastInner.querySelector(".colorwidget-colorswatch");

    this.contrastSwatch.textContent = SAMPLE_TEXT;

    this.contrastRatio = this.contrastInner.querySelector(".colorwidget-contrast-ratio");
    this.contrastGrade = this.contrastInner.querySelector(".colorwidget-contrast-grade");
    this.contrastHelp = this.contrastInner.querySelector(".colorwidget-contrast-help");

    this.slider = this.element.querySelector(".colorwidget-hue");
    this.slideHelper = this.element.querySelector(".colorwidget-slider");
    ColorWidget.draggable(this.slider, this.onSliderMove);

    this.dragger = this.element.querySelector(".colorwidget-color");
    this.dragHelper = this.element.querySelector(".colorwidget-dragger");
    ColorWidget.draggable(this.dragger, this.onDraggerMove);

    this.alphaSlider = this.element.querySelector(".colorwidget-alpha");
    this.alphaSliderInner = this.element.querySelector(".colorwidget-alpha-inner");
    this.alphaSliderHelper = this.element.querySelector(".colorwidget-alpha-handle");
    ColorWidget.draggable(this.alphaSliderInner, this.onAlphaSliderMove);

    this.colorSelect = this.element.querySelector(".colorwidget-select");
    this.colorSelect.addEventListener("change", this.onSelectValueChange);

    this.hexValue = this.element.querySelector(".colorwidget-hex");
    this.hexValueInput = this.element.querySelector(".colorwidget-hex-input");
    this.hexValueInput.addEventListener("input", this.onHexInputChange);

    this.rgbaValue = this.element.querySelector(".colorwidget-rgba");
    this.rgbaValueInputs = {
      r: this.element.querySelector(".colorwidget-rgba-r"),
      g: this.element.querySelector(".colorwidget-rgba-g"),
      b: this.element.querySelector(".colorwidget-rgba-b"),
      a: this.element.querySelector(".colorwidget-rgba-a"),
    };
    this.rgbaValue.addEventListener("input", this.onRgbaInputChange);

    this.hslaValue = this.element.querySelector(".colorwidget-hsla");
    this.hslaValueInputs = {
      h: this.element.querySelector(".colorwidget-hsla-h"),
      s: this.element.querySelector(".colorwidget-hsla-s"),
      l: this.element.querySelector(".colorwidget-hsla-l"),
      a: this.element.querySelector(".colorwidget-hsla-a"),
    };
    this.hslaValue.addEventListener("input", this.onHslaInputChange);
  },

  show: Task.async(function* () {
    this.initializeColorWidget();
    this.element.classList.add("colorwidget-show");

    this.slideHeight = this.slider.offsetHeight;
    this.dragWidth = this.dragger.offsetWidth;
    this.dragHeight = this.dragger.offsetHeight;
    this.dragHelperHeight = this.dragHelper.offsetHeight;
    this.slideHelperHeight = this.slideHelper.offsetHeight;
    this.alphaSliderWidth = this.alphaSliderInner.offsetWidth;
    this.alphaSliderHelperWidth = this.alphaSliderHelper.offsetWidth;

    if (this.inspector && this.inspector.selection.nodeFront && this.contrastEnabled) {
      let node = this.inspector.selection.nodeFront;
      this.closestBackgroundColor = yield node.getClosestBackgroundColor();
    }
    this.updateContrast();

    this.updateUI();
  }),

  onElementClick: function (e) {
    e.stopPropagation();
  },

  onSliderMove: function (dragX, dragY) {
    this.hsv[0] = (dragY / this.slideHeight);
    this.hsl[0] = (dragY / this.slideHeight) * 360;
    this.updateUI();
    this.onChange();
  },

  onDraggerMove: function (dragX, dragY) {
    this.hsv[1] = dragX / this.dragWidth;
    this.hsv[2] = (this.dragHeight - dragY) / this.dragHeight;

    this.hsl[2] = ((2 - this.hsv[1]) * this.hsv[2] / 2);
    if (this.hsl[2] && this.hsl[2] < 1) {
      this.hsl[1] = this.hsv[1] * this.hsv[2] /
          (this.hsl[2] < 0.5 ? this.hsl[2] * 2 : 2 - this.hsl[2] * 2);
      this.hsl[1] = this.hsl[1] * 100;
    }
    this.hsl[2] = this.hsl[2] * 100;

    this.updateUI();
    this.onChange();
  },

  onAlphaSliderMove: function (dragX, dragY) {
    this.hsv[3] = dragX / this.alphaSliderWidth;
    this.hsl[3] = dragX / this.alphaSliderWidth;
    this.updateUI();
    this.onChange();
  },

  onSelectValueChange: function (event) {
    const selection = event.target.value;
    this.colorSelect.classList.remove("colorwidget-select-spacing");
    this.hexValue.classList.add("colorwidget-hidden");
    this.rgbaValue.classList.add("colorwidget-hidden");
    this.hslaValue.classList.add("colorwidget-hidden");

    switch (selection) {
      case "hex":
        this.hexValue.classList.remove("colorwidget-hidden");
        break;
      case "rgba":
        this.colorSelect.classList.add("colorwidget-select-spacing");
        this.rgbaValue.classList.remove("colorwidget-hidden");
        break;
      case "hsla":
        this.colorSelect.classList.add("colorwidget-select-spacing");
        this.hslaValue.classList.remove("colorwidget-hidden");
        break;
    }
  },

  onHexInputChange: function (event) {
    const hex = event.target.value;
    const color = new colorUtils.CssColor(hex, true);
    if (!color.rgba) {
      return;
    }

    const { r, g, b, a } = color.getRGBATuple();
    this.rgb = [r, g, b, a];
    this.updateUI();
    this.onChange();
  },

  onRgbaInputChange: function (event) {
    const field = event.target.dataset.id;
    const value = event.target.value.toString();
    if (!value || isNaN(value) || value.endsWith(".")) {
      return;
    }

    let rgb = this.rgb;

    switch (field) {
      case "r":
        rgb[0] = value;
        break;
      case "g":
        rgb[1] = value;
        break;
      case "b":
        rgb[2] = value;
        break;
      case "a":
        rgb[3] = Math.min(value, 1);
        break;
    }

    this.rgb = rgb;

    this.updateUI();
    this.onChange();
  },

  onHslaInputChange: function (event) {
    const field = event.target.dataset.id;
    let value = event.target.value.toString();
    if ((field === "s" || field === "l") && !value.endsWith("%")) {
      return;
    }

    if (value.endsWith("%")) {
      value = value.substring(0, value.length - 1);
    }

    if (!value || isNaN(value) || value.endsWith(".")) {
      return;
    }

    const hsl = this.hsl;

    switch (field) {
      case "h":
        hsl[0] = value;
        break;
      case "s":
        hsl[1] = value;
        break;
      case "l":
        hsl[2] = value;
        break;
      case "a":
        hsl[3] = Math.min(value, 1);
        break;
    }

    const cssString = ColorWidget.hslToCssString(hsl[0], hsl[1], hsl[2], hsl[3]);
    const { r, g, b, a } = new colorUtils.CssColor(cssString).getRGBATuple();

    this.rgb = [r, g, b, a];

    this.hsl = hsl;

    this.updateUI();
    this.onChange();
  },

  onChange: function () {
    this.updateContrast();
    this.emit("changed", this.rgb, this.rgbCssString);
  },

  updateContrast: function () {
    if (!this.contrastEnabled) {
      this.contrast.style.display = "none";
      return;
    }

    this.contrast.style.display = "initial";

    if (!colorUtils.isValidCSSColor(this.closestBackgroundColor)) {
      this.contrastRatio.textContent = L10N.getStr(
        "inspector.colorwidget.contrastRatio.invalidColor"
      );

      this.contrastGrade.textContent = "";
      this.contrastHelp.removeAttribute("title");
      return;
    }
    if (!this.rgbaColor) {
      this.rgbaColor = new colorUtils.CssColor(this.closestBackgroundColor);
    }
    this.rgbaColor.newColor(this.closestBackgroundColor);
    let rgba = this.rgbaColor.getRGBATuple();
    let backgroundColor = [rgba.r, rgba.g, rgba.b, rgba.a];

    let textColor = this.rgb;

    let ratio = colorUtils.calculateContrastRatio(backgroundColor, textColor);

    let contrastDetails = ColorWidget.calculateGradeAndTitle(ratio,
                        this.rgbaColor.toString());

    this.contrastRatio.textContent = ColorWidget.ratioToString(ratio);
    this.contrastGrade.textContent = contrastDetails.grade;

    this.contrastHelp.setAttribute("title", contrastDetails.title);

    this.contrastSwatch.style.backgroundColor = this.rgbaColor.toString();
    this.contrastSwatch.style.color = this.rgbCssString;
  },

  updateHelperLocations: function () {
    // If the UI hasn't been shown yet then none of the dimensions will be
    // correct
    if (!this.element.classList.contains("colorwidget-show")) {
      return;
    }

    let h = this.hsv[0];
    let s = this.hsv[1];
    let v = this.hsv[2];

    // Placing the color dragger
    let dragX = s * this.dragWidth;
    let dragY = this.dragHeight - (v * this.dragHeight);
    let 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
    let slideY = (h * this.slideHeight) - this.slideHelperHeight / 2;
    this.slideHelper.style.top = slideY + "px";

    // Placing the alpha slider
    let alphaSliderX = (this.hsv[3] * this.alphaSliderWidth) -
      (this.alphaSliderHelperWidth / 2);
    this.alphaSliderHelper.style.left = alphaSliderX + "px";

    const color = new colorUtils.CssColor(this.rgbCssString);

    // Updating the hex field
    this.hexValueInput.value = color.hex;

    // Updating the RGBA fields
    const rgb = this.rgb;
    this.rgbaValueInputs.r.value = rgb[0];
    this.rgbaValueInputs.g.value = rgb[1];
    this.rgbaValueInputs.b.value = rgb[2];
    this.rgbaValueInputs.a.value = parseFloat(rgb[3].toFixed(1));

    // Updating the HSLA fields
    this.hslaValueInputs.h.value = this.hsl[0];
    this.hslaValueInputs.s.value = this.hsl[1] + "%";
    this.hslaValueInputs.l.value = this.hsl[2] + "%";
    this.hslaValueInputs.a.value = parseFloat(this.hsl[3].toFixed(1));
  },

  updateUI: function () {
    this.updateHelperLocations();

    let rgb = this.rgb;
    let rgbNoSatVal = this.rgbNoSatVal;

    let flatColor = "rgb(" + rgbNoSatVal[0] + ", " + rgbNoSatVal[1] + ", " +
      rgbNoSatVal[2] + ")";

    this.dragger.style.backgroundColor = flatColor;

    let rgbNoAlpha = "rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ")";
    let rgbAlpha0 = "rgba(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ", 0)";
    let alphaGradient = "linear-gradient(to right, " + rgbAlpha0 + ", " +
      rgbNoAlpha + ")";
    this.alphaSliderInner.style.background = alphaGradient;

    let colorName = colorUtils.rgbToColorName(rgb[0], rgb[1], rgb[2]);
    this.colorName.textContent = colorName || "---";
  },

  destroy: function () {
    this.element.removeEventListener("click", this.onElementClick);

    this.parentEl.removeChild(this.element);

    this.slider = null;
    this.dragger = null;
    this.alphaSlider = this.alphaSliderInner = this.alphaSliderHelper = null;
    this.parentEl = null;
    this.element = null;
    this.colorName = null;
  }
};
PK
!<-
�$��Lchrome/devtools/modules/devtools/client/shared/widgets/CubicBezierPresets.js/**
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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;
PK
!<Ʒ�}g}gKchrome/devtools/modules/devtools/client/shared/widgets/CubicBezierWidget.js/**
 * 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("devtools/shared/event-emitter");
const {
  PREDEFINED,
  PRESETS,
  DEFAULT_PRESET_CATEGORY
} = require("devtools/client/shared/widgets/CubicBezierPresets");
const {getCSSLexer} = require("devtools/shared/css/lexer");
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--;) {
    let 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: function () {
    // Check first if current coords are one of css predefined functions
    let 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");
  let 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() {
    let 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: function (element) {
    let p = this.padding, w = this.canvas.width, h = this.canvas.height;

    // Convert padding percentage to actual padding
    p = p.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: function (settings = {}) {
    let xy = this.bezier.coordinates;

    let defaultSettings = {
      handleColor: "#666",
      handleThickness: .008,
      bezierColor: "#4C9ED9",
      bezierThickness: .015,
      drawHandles: true
    };

    for (let 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();

      let 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;
  let {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.30, 0]);
  this.bezierCanvas.plot();

  // Place the control points
  let 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);

  // Add preset preview menu
  this.presets = new CubicBezierPresetWidget(parent);

  // Add the timing function previewer
  this.timingPreview = new TimingFunctionPreviewWidget(parent);

  this._initEvents();
}

exports.CubicBezierWidget = CubicBezierWidget;

CubicBezierWidget.prototype = {
  _initMarkup: function () {
    let doc = this.parent.ownerDocument;

    let wrap = doc.createElementNS(XHTML_NS, "div");
    wrap.className = "display-wrap";

    let plane = doc.createElementNS(XHTML_NS, "div");
    plane.className = "coordinate-plane";

    let p1 = doc.createElementNS(XHTML_NS, "button");
    p1.className = "control-point";
    plane.appendChild(p1);

    let p2 = doc.createElementNS(XHTML_NS, "button");
    p2.className = "control-point";
    plane.appendChild(p2);

    let 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
    };
  },

  _removeMarkup: function () {
    this.parent.querySelector(".display-wrap").remove();
  },

  _initEvents: function () {
    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: function () {
    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: function (event) {
    // Updating the boundingbox in case it has changed
    this.curveBoundingBox = this.curve.getBoundingClientRect();

    let point = event.target;
    let doc = point.ownerDocument;
    let self = this;

    doc.onmousemove = function drag(e) {
      let x = e.pageX;
      let y = e.pageY;
      let left = self.curveBoundingBox.left;
      let 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: function (event) {
    let point = event.target;
    let code = event.keyCode;

    if (code >= 37 && code <= 40) {
      event.preventDefault();

      // Arrow keys pressed
      let left = parseInt(point.style.left, 10);
      let top = parseInt(point.style.top, 10);
      let 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: function (event) {
    this.curveBoundingBox = this.curve.getBoundingClientRect();

    let left = this.curveBoundingBox.left;
    let top = this.curveBoundingBox.top;
    let x = event.pageX - left;
    let y = event.pageY - top;

    // Find which point is closer
    let distP1 = distance(x, y,
      parseInt(this.p1.style.left, 10), parseInt(this.p1.style.top, 10));
    let distP2 = distance(x, y,
      parseInt(this.p2.style.left, 10), parseInt(this.p2.style.top, 10));

    let point = distP1 < distP2 ? this.p1 : this.p2;
    point.style.left = x + "px";
    point.style.top = y + "px";

    this._updateFromPoints();
  },

  _onNewCoordinates: function (coordinates) {
    this.coordinates = coordinates;
  },

  /**
   * Get the current point coordinates and redraw the curve to match
   */
  _updateFromPoints: function () {
    // 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: function (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);

    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
    let 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
    let coordinates = parseTimingFunction(value);

    this.presets.refreshMenu(coordinates);
    this.coordinates = coordinates;
  },

  destroy: function () {
    this._removeEvents();
    this._removeMarkup();

    this.timingPreview.destroy();
    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;

  let {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: function () {
    let doc = this.parent.ownerDocument;

    let presetPane = doc.createElementNS(XHTML_NS, "div");
    presetPane.className = "preset-pane";

    let categoryList = doc.createElementNS(XHTML_NS, "div");
    categoryList.id = "preset-categories";

    let presetContainer = doc.createElementNS(XHTML_NS, "div");
    presetContainer.id = "preset-container";

    Object.keys(PRESETS).forEach(categoryLabel => {
      let category = this._createCategory(categoryLabel);
      categoryList.appendChild(category);

      let presetList = this._createPresetList(categoryLabel);
      presetContainer.appendChild(presetList);
    });

    presetPane.appendChild(categoryList);
    presetPane.appendChild(presetContainer);

    this.parent.appendChild(presetPane);

    let allCategories = presetPane.querySelectorAll(".category");
    let allPresets = presetPane.querySelectorAll(".preset");

    return {
      presetPane: presetPane,
      presets: allPresets,
      categories: allCategories
    };
  },

  _createCategory: function (categoryLabel) {
    let doc = this.parent.ownerDocument;

    let category = doc.createElementNS(XHTML_NS, "div");
    category.id = categoryLabel;
    category.classList.add("category");

    let categoryDisplayLabel = this._normalizeCategoryLabel(categoryLabel);
    category.textContent = categoryDisplayLabel;
    category.setAttribute("title", categoryDisplayLabel);

    return category;
  },

  _normalizeCategoryLabel: function (categoryLabel) {
    return categoryLabel.replace("/-/g", " ");
  },

  _createPresetList: function (categoryLabel) {
    let doc = this.parent.ownerDocument;

    let presetList = doc.createElementNS(XHTML_NS, "div");
    presetList.id = "preset-category-" + categoryLabel;
    presetList.classList.add("preset-list");

    Object.keys(PRESETS[categoryLabel]).forEach(presetLabel => {
      let preset = this._createPreset(categoryLabel, presetLabel);
      presetList.appendChild(preset);
    });

    return presetList;
  },

  _createPreset: function (categoryLabel, presetLabel) {
    let doc = this.parent.ownerDocument;

    let preset = doc.createElementNS(XHTML_NS, "div");
    preset.classList.add("preset");
    preset.id = presetLabel;
    preset.coordinates = PRESETS[categoryLabel][presetLabel];
    // Create preset preview
    let curve = doc.createElementNS(XHTML_NS, "canvas");
    let 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
    let presetLabelElem = doc.createElementNS(XHTML_NS, "p");
    let presetDisplayLabel = this._normalizePresetLabel(categoryLabel,
                                                        presetLabel);
    presetLabelElem.textContent = presetDisplayLabel;
    preset.appendChild(presetLabelElem);
    preset.setAttribute("title", presetDisplayLabel);

    return preset;
  },

  _normalizePresetLabel: function (categoryLabel, presetLabel) {
    return presetLabel.replace(categoryLabel + "-", "").replace("/-/g", " ");
  },

  _initEvents: function () {
    for (let category of this.categories) {
      category.addEventListener("click", this._onCategoryClick);
    }

    for (let preset of this.presets) {
      preset.addEventListener("click", this._onPresetClick);
    }
  },

  _removeEvents: function () {
    for (let category of this.categories) {
      category.removeEventListener("click", this._onCategoryClick);
    }

    for (let preset of this.presets) {
      preset.removeEventListener("click", this._onPresetClick);
    }
  },

  _onPresetClick: function (event) {
    this.emit("new-coordinates", event.currentTarget.coordinates);
    this.activePreset = event.currentTarget;
  },

  _onCategoryClick: function (event) {
    this.activeCategory = event.target;
  },

  _setActivePresetList: function (presetListId) {
    let 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: function (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: function () {
    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: function () {
    let doc = this.parent.ownerDocument;

    let 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);

    let scale = doc.createElementNS(XHTML_NS, "div");
    scale.className = "scale";
    container.appendChild(scale);

    this.parent.appendChild(container);
  },

  destroy: function () {
    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: function (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: function (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: .5, offset: 0 },
      { left: "-7px", opacity: .5, offset: .19 },
      { left: "-7px", opacity: 1, offset: .2, easing: timingFunction },
      { left: "143px", opacity: 1, offset: .5 },
      { left: "143px", opacity: .5, offset: .51 },
      { left: "143px", opacity: .5, offset: .7 },
      { left: "143px", opacity: 1, offset: .71, easing: timingFunction },
      { left: "-7px", opacity: 1, offset: 1 }
    ], {
      duration: this.PREVIEW_DURATION * 2,
      iterations: Infinity
    });
  }
};

// Helpers

function getPadding(padding) {
  let 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];
  }

  let tokenStream = getCSSLexer(value);
  let getNextToken = () => {
    while (true) {
      let 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;
  }

  let 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);
}
PK
!<��9�Hchrome/devtools/modules/devtools/client/shared/widgets/FastListWidget.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 EventEmitter = require("devtools/shared/event-emitter");
const { ViewHelpers } = require("devtools/client/shared/widgets/view-helpers");

/**
 * A list menu widget that attempts to be very fast.
 *
 * Note: this widget should be used in tandem with the WidgetMethods in
 * view-helpers.js.
 *
 * @param nsIDOMNode aNode
 *        The element associated with the widget.
 */
const FastListWidget = module.exports = function FastListWidget(node) {
  this.document = node.ownerDocument;
  this.window = this.document.defaultView;
  this._parent = node;
  this._fragment = this.document.createDocumentFragment();

  // This is a prototype element that each item added to the list clones.
  this._templateElement = this.document.createElement("hbox");

  // Create an internal scrollbox container.
  this._list = this.document.createElement("scrollbox");
  this._list.className = "fast-list-widget-container theme-body";
  this._list.setAttribute("flex", "1");
  this._list.setAttribute("orient", "vertical");
  this._list.setAttribute("tabindex", "0");
  this._list.addEventListener("keydown", e => this.emit("keyDown", e));
  this._list.addEventListener("mousedown", e => this.emit("mousePress", e));
  this._parent.appendChild(this._list);

  this._orderedMenuElementsArray = [];
  this._itemsByElement = new Map();

  // This widget emits events that can be handled in a MenuContainer.
  EventEmitter.decorate(this);

  // Delegate some of the associated node's methods to satisfy the interface
  // required by MenuContainer instances.
  ViewHelpers.delegateWidgetAttributeMethods(this, node);
  ViewHelpers.delegateWidgetEventMethods(this, node);
};

FastListWidget.prototype = {
  /**
   * Inserts an item in this container at the specified index, optionally
   * grouping by name.
   *
   * @param number aIndex
   *        The position in the container intended for this item.
   * @param nsIDOMNode aContents
   *        The node to be displayed in the container.
   * @param Object aAttachment [optional]
   *        Extra data for the user.
   * @return nsIDOMNode
   *         The element associated with the displayed item.
   */
  insertItemAt: function (index, contents, attachment = {}) {
    let element = this._templateElement.cloneNode();
    element.appendChild(contents);

    if (index >= 0) {
      throw new Error("FastListWidget only supports appending items.");
    }

    this._fragment.appendChild(element);
    this._orderedMenuElementsArray.push(element);
    this._itemsByElement.set(element, this);

    return element;
  },

  /**
   * This is a non-standard widget implementation method. When appending items,
   * they are queued in a document fragment. This method appends the document
   * fragment to the dom.
   */
  flush: function () {
    this._list.appendChild(this._fragment);
  },

  /**
   * Removes all of the child nodes from this container.
   */
  removeAllItems: function () {
    let list = this._list;

    while (list.hasChildNodes()) {
      list.firstChild.remove();
    }

    this._selectedItem = null;

    this._orderedMenuElementsArray.length = 0;
    this._itemsByElement.clear();
  },

  /**
   * Remove the given item.
   */
  removeChild: function (child) {
    throw new Error("Not yet implemented");
  },

  /**
   * Gets the currently selected child node in this container.
   * @return nsIDOMNode
   */
  get selectedItem() {
    return this._selectedItem;
  },

  /**
   * Sets the currently selected child node in this container.
   * @param nsIDOMNode child
   */
  set selectedItem(child) {
    let menuArray = this._orderedMenuElementsArray;

    if (!child) {
      this._selectedItem = null;
    }
    for (let node of menuArray) {
      if (node == child) {
        node.classList.add("selected");
        this._selectedItem = node;
      } else {
        node.classList.remove("selected");
      }
    }

    this.ensureElementIsVisible(this.selectedItem);
  },

  /**
   * Returns the child node in this container situated at the specified index.
   *
   * @param number index
   *        The position in the container intended for this item.
   * @return nsIDOMNode
   *         The element associated with the displayed item.
   */
  getItemAtIndex: function (index) {
    return this._orderedMenuElementsArray[index];
  },

  /**
   * Adds a new attribute or changes an existing attribute on this container.
   *
   * @param string name
   *        The name of the attribute.
   * @param string value
   *        The desired attribute value.
   */
  setAttribute: function (name, value) {
    this._parent.setAttribute(name, value);

    if (name == "emptyText") {
      this._textWhenEmpty = value;
    }
  },

  /**
   * Removes an attribute on this container.
   *
   * @param string name
   *        The name of the attribute.
   */
  removeAttribute: function (name) {
    this._parent.removeAttribute(name);

    if (name == "emptyText") {
      this._removeEmptyText();
    }
  },

  /**
   * Ensures the specified element is visible.
   *
   * @param nsIDOMNode element
   *        The element to make visible.
   */
  ensureElementIsVisible: function (element) {
    if (!element) {
      return;
    }

    // Ensure the element is visible but not scrolled horizontally.
    let boxObject = this._list.boxObject;
    boxObject.ensureElementIsVisible(element);
    boxObject.scrollBy(-this._list.clientWidth, 0);
  },

  /**
   * Sets the text displayed in this container when empty.
   * @param string aValue
   */
  set _textWhenEmpty(value) {
    if (this._emptyTextNode) {
      this._emptyTextNode.setAttribute("value", value);
    }
    this._emptyTextValue = value;
    this._showEmptyText();
  },

  /**
   * Creates and appends a label signaling that this container is empty.
   */
  _showEmptyText: function () {
    if (this._emptyTextNode || !this._emptyTextValue) {
      return;
    }
    let label = this.document.createElement("label");
    label.className = "plain fast-list-widget-empty-text";
    label.setAttribute("value", this._emptyTextValue);

    this._parent.insertBefore(label, this._list);
    this._emptyTextNode = label;
  },

  /**
   * Removes the label signaling that this container is empty.
   */
  _removeEmptyText: function () {
    if (!this._emptyTextNode) {
      return;
    }
    this._parent.removeChild(this._emptyTextNode);
    this._emptyTextNode = null;
  },

  window: null,
  document: null,
  _parent: null,
  _list: null,
  _selectedItem: null,
  _orderedMenuElementsArray: null,
  _itemsByElement: null,
  _emptyTextNode: null,
  _emptyTextValue: ""
};
PK
!<	d��|�|Fchrome/devtools/modules/devtools/client/shared/widgets/FilterWidget.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/shared/event-emitter");
const XHTML_NS = "http://www.w3.org/1999/xhtml";

const { LocalizationHelper } = require("devtools/shared/l10n");
const STRINGS_URI = "devtools/client/locales/filterwidget.properties";
const L10N = new LocalizationHelper(STRINGS_URI);

const {cssTokenizer} = require("devtools/shared/css/parsing-utils");

const asyncStorage = require("devtools/shared/async-storage");

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 {nsIDOMNode} el
 *        The widget container.
 * @param {String} value
 *        CSS filter value
 * @param {Function} cssIsValid
 *        Test whether css name / value is valid.
 */
function CSSFilterEditorWidget(el, value = "", cssIsValid) {
  this.doc = el.ownerDocument;
  this.win = this.doc.defaultView;
  this.el = el;
  this._cssIsValid = cssIsValid;

  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: function () {
    // 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>
    let content = this.doc.createDocumentFragment();

    let 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);

    let 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);

    let filterListPlaceholder = this.doc.createElementNS(XHTML_NS, "option");
    filterListPlaceholder.setAttribute("value", "");
    filterListPlaceholder.textContent = L10N.getStr("filterListSelectPlaceholder");
    this.filterSelect.appendChild(filterListPlaceholder);

    let 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);

    let 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);

    let 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: function () {
    this._filterItemMarkup.remove();
    this.el.remove();
    this.el = this.filterList = this._filterItemMarkup = null;
    this.presetList = this.togglePresets = this.filterSelect = null;
    this.addPresetButton = null;
  },

  destroy: function () {
    this._removeEventListeners();
    this._destroyMarkup();
  },

  /**
    * Creates <option> elements for each filter definition
    * in filterList
    */
  _populateFilterSelect: function () {
    let select = this.filterSelect;
    filterList.forEach(filter => {
      let option = this.doc.createElementNS(XHTML_NS, "option");
      // eslint-disable-next-line no-unsanitized/property
      option.innerHTML = option.value = filter.name;
      select.appendChild(option);
    });
  },

  /**
    * Creates a template for filter elements which is cloned and used in render
    */
  _buildFilterItemMarkup: function () {
    let base = this.doc.createElementNS(XHTML_NS, "div");
    base.className = "filter";

    let name = this.doc.createElementNS(XHTML_NS, "div");
    name.className = "filter-name";

    let value = this.doc.createElementNS(XHTML_NS, "div");
    value.className = "filter-value";

    let drag = this.doc.createElementNS(XHTML_NS, "i");
    drag.title = L10N.getStr("dragHandleTooltipText");

    let label = this.doc.createElementNS(XHTML_NS, "label");

    name.appendChild(drag);
    name.appendChild(label);

    let unitPreview = this.doc.createElementNS(XHTML_NS, "span");
    let input = this.doc.createElementNS(XHTML_NS, "input");
    input.classList.add("devtools-textinput");

    value.appendChild(input);
    value.appendChild(unitPreview);

    let removeButton = this.doc.createElementNS(XHTML_NS, "button");
    removeButton.className = "remove-button";

    base.appendChild(name);
    base.appendChild(value);
    base.appendChild(removeButton);

    this._filterItemMarkup = base;
  },

  _buildPresetItemMarkup: function () {
    let base = this.doc.createElementNS(XHTML_NS, "div");
    base.classList.add("preset");

    let name = this.doc.createElementNS(XHTML_NS, "label");
    base.appendChild(name);

    let value = this.doc.createElementNS(XHTML_NS, "span");
    base.appendChild(value);

    let removeButton = this.doc.createElementNS(XHTML_NS, "button");
    removeButton.classList.add("remove-button");

    base.appendChild(removeButton);

    this._presetItemMarkup = base;
  },

  _addEventListeners: function () {
    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: function () {
    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: function (el) {
    return [...this.filterList.children].indexOf(el);
  },

  _keyDown: function (e) {
    if (e.target.tagName.toLowerCase() !== "input" ||
       (e.keyCode !== 40 && e.keyCode !== 38)) {
      return;
    }
    let 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) {
      let 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;
      let num = getNeighbourNumber(input.value, selectionStart);
      if (!num) {
        return;
      }

      let {start, end, value} = num;

      let split = input.value.split("");
      let computed = fixFloat(value + direction * multiplier);
      let 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: function (e) {
    let filterEl = e.target.closest(".filter");
    let index = this._getFilterElementIndex(filterEl);
    let filter = this.filters[index];
    let def = this._definition(filter.name);

    if (def.type !== "string") {
      e.target.value = fixFloat(e.target.value);
    }
    this.updateValueAt(index, e.target.value);
  },

  _mouseDown: function (e) {
    let 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")) {
      let label = e.target;
      let input = filterEl.querySelector("input");
      let index = this._getFilterElementIndex(filterEl);

      this._dragging = {
        index, label, input,
        startX: e.pageX
      };

      this.isDraggingLabel = true;
    }
  },

  _addButtonClick: function () {
    const select = this.filterSelect;
    if (!select.value) {
      return;
    }

    const key = select.value;
    this.add(key, null);

    this.render();
  },

  _removeButtonClick: function (e) {
    const isRemoveButton = e.target.classList.contains("remove-button");
    if (!isRemoveButton) {
      return;
    }

    let filterEl = e.target.closest(".filter");
    let index = this._getFilterElementIndex(filterEl);
    this.removeAt(index);
  },

  _mouseMove: function (e) {
    if (this.isReorderingFilter) {
      this._dragFilterElement(e);
    } else if (this.isDraggingLabel) {
      this._dragLabel(e);
    }
  },

  _dragFilterElement: function (e) {
    const rect = this.filterList.getBoundingClientRect();
    let top = e.pageY - LIST_PADDING;
    let 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: function (e) {
    let dragging = this._dragging;

    let 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: function () {
    // Label-dragging is disabled on mouseup
    this._dragging = null;
    this.isDraggingLabel = false;

    // Filter drag/drop needs more cleaning
    if (!this.isReorderingFilter) {
      return;
    }
    let 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: function (e) {
    let el = e.target;
    let preset = el.closest(".preset");
    if (!preset) {
      return;
    }

    let 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.
        let p = presets[id];

        this.setCssValue(p.value);
        this.addPresetInput.value = p.name;
      }
    }, console.error);
  },

  _togglePresets: function () {
    this.el.classList.toggle("show-presets");
    this.emit("render");
  },

  _savePreset: function (e) {
    e.preventDefault();

    let name = this.addPresetInput.value;
    let value = this.getCssValue();

    if (!name || !value || SPECIAL_VALUES.has(value)) {
      this.emit("preset-save-error");
      return;
    }

    this.getPresets().then(presets => {
      let 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: function () {
    this.filterSelect.ownerDocument.defaultView.focus();
  },

  /**
   * Clears the list and renders filters, binding required events.
   * There are some delegated events bound in _addEventListeners method
   */
  render: function () {
    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 = "";

    let base = this._filterItemMarkup;

    for (let filter of this.filters) {
      const def = this._definition(filter.name);

      let el = base.cloneNode(true);

      let [name, value] = el.children;
      let label = name.children[1];
      let [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);
    }

    let 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: function () {
    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;
      }
      let base = this._presetItemMarkup;

      this.presetList.innerHTML = "";

      for (let [index, preset] of presets.entries()) {
        let el = base.cloneNode(true);

        let [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: function (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: function (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: function (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: function (index) {
    let 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: function (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: function () {
    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: function (index, value) {
    let 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: function () {
    return asyncStorage.getItem("cssFilterPresets").then(presets => {
      if (!presets) {
        return [];
      }

      return presets;
    }, console.error);
  },

  setPresets: function (presets) {
    return asyncStorage.setItem("cssFilterPresets", presets)
      .catch(console.error);
  }
};

// Fixes JavaScript's float precision
function fixFloat(a, number) {
  let 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) {
  let filters = [];
  let depth = 0;

  if (SPECIAL_VALUES.has(css)) {
    return filters;
  }

  let state = "initial";
  let name;
  let contents;
  for (let 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.
          let originalText = css.substring(token.startOffset, token.endOffset);
          let [, quote] = /^url\([ \t\r\n\f]*(["']?)/i.exec(originalText);

          filters.push({name: "url", value: token.text.trim(), quote: 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: 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
  };
}
PK
!<ҌGd����Dchrome/devtools/modules/devtools/client/shared/widgets/FlameGraph.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Task } = require("devtools/shared/task");
const { ViewHelpers, setNamedTimeout } = require("devtools/client/shared/widgets/view-helpers");
const { ELLIPSIS } = require("devtools/shared/l10n");

loader.lazyRequireGetter(this, "defer", "devtools/shared/defer");
loader.lazyRequireGetter(this, "EventEmitter",
  "devtools/shared/event-emitter");

loader.lazyRequireGetter(this, "getColor",
  "devtools/client/shared/theme", true);

loader.lazyRequireGetter(this, "CATEGORY_MAPPINGS",
  "devtools/client/performance/modules/categories", true);
loader.lazyRequireGetter(this, "FrameUtils",
  "devtools/client/performance/modules/logic/frame-utils");
loader.lazyRequireGetter(this, "demangle",
  "devtools/client/shared/demangle");

loader.lazyRequireGetter(this, "AbstractCanvasGraph",
  "devtools/client/shared/widgets/Graphs", true);
loader.lazyRequireGetter(this, "GraphArea",
  "devtools/client/shared/widgets/Graphs", true);
loader.lazyRequireGetter(this, "GraphAreaDragger",
  "devtools/client/shared/widgets/Graphs", true);

const GRAPH_SRC = "chrome://devtools/content/shared/widgets/graphs-frame.xhtml";

const GRAPH_RESIZE_EVENTS_DRAIN = 100; // ms

const GRAPH_WHEEL_ZOOM_SENSITIVITY = 0.00035;
const GRAPH_WHEEL_SCROLL_SENSITIVITY = 0.5;
const GRAPH_KEYBOARD_ZOOM_SENSITIVITY = 20;
const GRAPH_KEYBOARD_PAN_SENSITIVITY = 20;
const GRAPH_KEYBOARD_ACCELERATION = 1.05;
const GRAPH_KEYBOARD_TRANSLATION_MAX = 150;

const GRAPH_MIN_SELECTION_WIDTH = 0.001; // ms

const GRAPH_HORIZONTAL_PAN_THRESHOLD = 10; // px
const GRAPH_VERTICAL_PAN_THRESHOLD = 30; // px

const FIND_OPTIMAL_TICK_INTERVAL_MAX_ITERS = 100;

const TIMELINE_TICKS_MULTIPLE = 5; // ms
const TIMELINE_TICKS_SPACING_MIN = 75; // px

const OVERVIEW_HEADER_HEIGHT = 16; // px
const OVERVIEW_HEADER_TEXT_FONT_SIZE = 9; // px
const OVERVIEW_HEADER_TEXT_FONT_FAMILY = "sans-serif";
const OVERVIEW_HEADER_TEXT_PADDING_LEFT = 6; // px
const OVERVIEW_HEADER_TEXT_PADDING_TOP = 5; // px
const OVERVIEW_HEADER_TIMELINE_STROKE_COLOR = "rgba(128, 128, 128, 0.5)";

const FLAME_GRAPH_BLOCK_HEIGHT = 15; // px
const FLAME_GRAPH_BLOCK_BORDER = 1; // px
const FLAME_GRAPH_BLOCK_TEXT_FONT_SIZE = 10; // px
const FLAME_GRAPH_BLOCK_TEXT_FONT_FAMILY = "message-box, Helvetica Neue," +
                                           "Helvetica, sans-serif";
const FLAME_GRAPH_BLOCK_TEXT_PADDING_TOP = 0; // px
const FLAME_GRAPH_BLOCK_TEXT_PADDING_LEFT = 3; // px
const FLAME_GRAPH_BLOCK_TEXT_PADDING_RIGHT = 3; // px

// Large enough number for a diverse pallette.
const PALLETTE_SIZE = 20;
const PALLETTE_HUE_OFFSET = Math.random() * 90;
const PALLETTE_HUE_RANGE = 270;
const PALLETTE_SATURATION = 100;
const PALLETTE_BRIGHTNESS = 55;
const PALLETTE_OPACITY = 0.35;

const COLOR_PALLETTE = Array.from(Array(PALLETTE_SIZE)).map((_, i) => "hsla" +
  "(" +
  ((PALLETTE_HUE_OFFSET + (i / PALLETTE_SIZE * PALLETTE_HUE_RANGE)) | 0 % 360) +
  "," + PALLETTE_SATURATION + "%" +
  "," + PALLETTE_BRIGHTNESS + "%" +
  "," + PALLETTE_OPACITY +
  ")"
);

/**
 * A flamegraph visualization. This implementation is responsable only with
 * drawing the graph, using a data source consisting of rectangles and
 * their corresponding widths.
 *
 * Example usage:
 *   let graph = new FlameGraph(node);
 *   graph.once("ready", () => {
 *     let data = FlameGraphUtils.createFlameGraphDataFromThread(thread);
 *     let bounds = { startTime, endTime };
 *     graph.setData({ data, bounds });
 *   });
 *
 * Data source format:
 *   [
 *     {
 *       color: "string",
 *       blocks: [
 *         {
 *           x: number,
 *           y: number,
 *           width: number,
 *           height: number,
 *           text: "string"
 *         },
 *         ...
 *       ]
 *     },
 *     {
 *       color: "string",
 *       blocks: [...]
 *     },
 *     ...
 *     {
 *       color: "string",
 *       blocks: [...]
 *     }
 *   ]
 *
 * Use `FlameGraphUtils` to convert profiler data (or any other data source)
 * into a drawable format.
 *
 * @param nsIDOMNode parent
 *        The parent node holding the graph.
 * @param number sharpness [optional]
 *        Defaults to the current device pixel ratio.
 */
function FlameGraph(parent, sharpness) {
  EventEmitter.decorate(this);

  this._parent = parent;
  this._ready = defer();

  this.setTheme();

  AbstractCanvasGraph.createIframe(GRAPH_SRC, parent, iframe => {
    this._iframe = iframe;
    this._window = iframe.contentWindow;
    this._document = iframe.contentDocument;
    this._pixelRatio = sharpness || this._window.devicePixelRatio;

    let container =
      this._container = this._document.getElementById("graph-container");
    container.className = "flame-graph-widget-container graph-widget-container";

    let canvas = this._canvas = this._document.getElementById("graph-canvas");
    canvas.className = "flame-graph-widget-canvas graph-widget-canvas";

    let bounds = parent.getBoundingClientRect();
    bounds.width = this.fixedWidth || bounds.width;
    bounds.height = this.fixedHeight || bounds.height;
    iframe.setAttribute("width", bounds.width);
    iframe.setAttribute("height", bounds.height);

    this._width = canvas.width = bounds.width * this._pixelRatio;
    this._height = canvas.height = bounds.height * this._pixelRatio;
    this._ctx = canvas.getContext("2d");

    this._bounds = new GraphArea();
    this._selection = new GraphArea();
    this._selectionDragger = new GraphAreaDragger();
    this._verticalOffset = 0;
    this._verticalOffsetDragger = new GraphAreaDragger(0);
    this._keyboardZoomAccelerationFactor = 1;
    this._keyboardPanAccelerationFactor = 1;

    this._userInputStack = 0;
    this._keysPressed = [];

    // Calculating text widths is necessary to trim the text inside the blocks
    // while the scaling changes (e.g. via scrolling). This is very expensive,
    // so maintain a cache of string contents to text widths.
    this._textWidthsCache = {};

    let fontSize = FLAME_GRAPH_BLOCK_TEXT_FONT_SIZE * this._pixelRatio;
    let fontFamily = FLAME_GRAPH_BLOCK_TEXT_FONT_FAMILY;
    this._ctx.font = fontSize + "px " + fontFamily;
    this._averageCharWidth = this._calcAverageCharWidth();
    this._overflowCharWidth = this._getTextWidth(this.overflowChar);

    this._onAnimationFrame = this._onAnimationFrame.bind(this);
    this._onKeyDown = this._onKeyDown.bind(this);
    this._onKeyUp = this._onKeyUp.bind(this);
    this._onMouseMove = this._onMouseMove.bind(this);
    this._onMouseDown = this._onMouseDown.bind(this);
    this._onMouseUp = this._onMouseUp.bind(this);
    this._onMouseWheel = this._onMouseWheel.bind(this);
    this._onResize = this._onResize.bind(this);
    this.refresh = this.refresh.bind(this);

    this._window.addEventListener("keydown", this._onKeyDown);
    this._window.addEventListener("keyup", this._onKeyUp);
    this._window.addEventListener("mousemove", this._onMouseMove);
    this._window.addEventListener("mousedown", this._onMouseDown);
    this._window.addEventListener("mouseup", this._onMouseUp);
    this._window.addEventListener("MozMousePixelScroll", this._onMouseWheel);

    let ownerWindow = this._parent.ownerDocument.defaultView;
    ownerWindow.addEventListener("resize", this._onResize);

    this._animationId =
      this._window.requestAnimationFrame(this._onAnimationFrame);

    this._ready.resolve(this);
    this.emit("ready", this);
  });
}

FlameGraph.prototype = {
  /**
   * Read-only width and height of the canvas.
   * @return number
   */
  get width() {
    return this._width;
  },
  get height() {
    return this._height;
  },

  /**
   * Returns a promise resolved once this graph is ready to receive data.
   */
  ready: function () {
    return this._ready.promise;
  },

  /**
   * Destroys this graph.
   */
  destroy: Task.async(function* () {
    yield this.ready();

    this._window.removeEventListener("keydown", this._onKeyDown);
    this._window.removeEventListener("keyup", this._onKeyUp);
    this._window.removeEventListener("mousemove", this._onMouseMove);
    this._window.removeEventListener("mousedown", this._onMouseDown);
    this._window.removeEventListener("mouseup", this._onMouseUp);
    this._window.removeEventListener("MozMousePixelScroll", this._onMouseWheel);

    let ownerWindow = this._parent.ownerDocument.defaultView;
    if (ownerWindow) {
      ownerWindow.removeEventListener("resize", this._onResize);
    }

    this._window.cancelAnimationFrame(this._animationId);
    this._iframe.remove();

    this._bounds = null;
    this._selection = null;
    this._selectionDragger = null;
    this._verticalOffset = null;
    this._verticalOffsetDragger = null;
    this._keyboardZoomAccelerationFactor = null;
    this._keyboardPanAccelerationFactor = null;
    this._textWidthsCache = null;

    this._data = null;

    this.emit("destroyed");
  }),

  /**
   * Makes sure the canvas graph is of the specified width or height, and
   * doesn't flex to fit all the available space.
   */
  fixedWidth: null,
  fixedHeight: null,

  /**
   * How much preliminar drag is necessary to determine the panning direction.
   */
  horizontalPanThreshold: GRAPH_HORIZONTAL_PAN_THRESHOLD,
  verticalPanThreshold: GRAPH_VERTICAL_PAN_THRESHOLD,

  /**
   * The units used in the overhead ticks. Could be "ms", for example.
   * Overwrite this with your own localized format.
   */
  timelineTickUnits: "",

  /**
   * Character used when a block's text is overflowing.
   * Defaults to an ellipsis.
   */
  overflowChar: ELLIPSIS,

  /**
   * Sets the data source for this graph.
   *
   * @param object data
   *        An object containing the following properties:
   *          - data: the data source; see the constructor for more info
   *          - bounds: the minimum/maximum { start, end }, in ms or px
   *          - visible: optional, the shown { start, end }, in ms or px
   */
  setData: function ({ data, bounds, visible }) {
    this._data = data;
    this.setOuterBounds(bounds);
    this.setViewRange(visible || bounds);
  },

  /**
   * Same as `setData`, but waits for this graph to finish initializing first.
   *
   * @param object data
   *        The data source. See the constructor for more information.
   * @return promise
   *         A promise resolved once the data is set.
   */
  setDataWhenReady: Task.async(function* (data) {
    yield this.ready();
    this.setData(data);
  }),

  /**
   * Gets whether or not this graph has a data source.
   * @return boolean
   */
  hasData: function () {
    return !!this._data;
  },

  /**
   * Sets the maximum selection (i.e. the 'graph bounds').
   * @param object { start, end }
   */
  setOuterBounds: function ({ startTime, endTime }) {
    this._bounds.start = startTime * this._pixelRatio;
    this._bounds.end = endTime * this._pixelRatio;
    this._shouldRedraw = true;
  },

  /**
   * Sets the selection and vertical offset (i.e. the 'view range').
   * @return number
   */
  setViewRange: function ({ startTime, endTime }, verticalOffset = 0) {
    this._selection.start = startTime * this._pixelRatio;
    this._selection.end = endTime * this._pixelRatio;
    this._verticalOffset = verticalOffset * this._pixelRatio;
    this._shouldRedraw = true;
  },

  /**
   * Gets the maximum selection (i.e. the 'graph bounds').
   * @return number
   */
  getOuterBounds: function () {
    return {
      startTime: this._bounds.start / this._pixelRatio,
      endTime: this._bounds.end / this._pixelRatio
    };
  },

  /**
   * Gets the current selection and vertical offset (i.e. the 'view range').
   * @return number
   */
  getViewRange: function () {
    return {
      startTime: this._selection.start / this._pixelRatio,
      endTime: this._selection.end / this._pixelRatio,
      verticalOffset: this._verticalOffset / this._pixelRatio
    };
  },

  /**
   * Focuses this graph's iframe window.
   */
  focus: function () {
    this._window.focus();
  },

  /**
   * Updates this graph to reflect the new dimensions of the parent node.
   *
   * @param boolean options.force
   *        Force redraw everything.
   */
  refresh: function (options = {}) {
    let bounds = this._parent.getBoundingClientRect();
    let newWidth = this.fixedWidth || bounds.width;
    let newHeight = this.fixedHeight || bounds.height;

    // Prevent redrawing everything if the graph's width & height won't change,
    // except if force=true.
    if (!options.force &&
        this._width == newWidth * this._pixelRatio &&
        this._height == newHeight * this._pixelRatio) {
      this.emit("refresh-cancelled");
      return;
    }

    bounds.width = newWidth;
    bounds.height = newHeight;
    this._iframe.setAttribute("width", bounds.width);
    this._iframe.setAttribute("height", bounds.height);
    this._width = this._canvas.width = bounds.width * this._pixelRatio;
    this._height = this._canvas.height = bounds.height * this._pixelRatio;

    this._shouldRedraw = true;
    this.emit("refresh");
  },

  /**
   * Sets the theme via `theme` to either "light" or "dark",
   * and updates the internal styling to match. Requires a redraw
   * to see the effects.
   */
  setTheme: function (theme) {
    theme = theme || "light";
    this.overviewHeaderBackgroundColor = getColor("body-background", theme);
    this.overviewHeaderTextColor = getColor("body-color", theme);
    // Hard to get a color that is readable across both themes for the text
    // on the flames
    this.blockTextColor = getColor(theme === "dark" ? "selection-color"
                                                    : "body-color", theme);
  },

  /**
   * The contents of this graph are redrawn only when something changed,
   * like the data source, or the selection bounds etc. This flag tracks
   * if the rendering is "dirty" and needs to be refreshed.
   */
  _shouldRedraw: false,

  /**
   * Animation frame callback, invoked on each tick of the refresh driver.
   */
  _onAnimationFrame: function () {
    this._animationId =
      this._window.requestAnimationFrame(this._onAnimationFrame);
    this._drawWidget();
  },

  /**
   * Redraws the widget when necessary. The actual graph is not refreshed
   * every time this function is called, only the cliphead, selection etc.
   */
  _drawWidget: function () {
    if (!this._shouldRedraw) {
      return;
    }

    // Unlike mouse events which are updated as needed in their own respective
    // handlers, keyboard events are granular and non-continuous (not even
    // "keydown", which is fired with a low frequency). Therefore, to maintain
    // animation smoothness, update anything that's controllable via the
    // keyboard here, in the animation loop, before any actual drawing.
    this._keyboardUpdateLoop();

    let ctx = this._ctx;
    let canvasWidth = this._width;
    let canvasHeight = this._height;
    ctx.clearRect(0, 0, canvasWidth, canvasHeight);

    let selection = this._selection;
    let selectionWidth = selection.end - selection.start;
    let selectionScale = canvasWidth / selectionWidth;
    this._drawTicks(selection.start, selectionScale);
    this._drawPyramid(this._data, this._verticalOffset,
                      selection.start, selectionScale);
    this._drawHeader(selection.start, selectionScale);

    // If the user isn't doing anything anymore, it's safe to stop drawing.
    // XXX: This doesn't handle cases where we should still be drawing even
    // if any input stops (e.g. smooth panning transitions after the user
    // finishes input). We don't care about that right now.
    if (this._userInputStack == 0) {
      this._shouldRedraw = false;
      return;
    }
    if (this._userInputStack < 0) {
      throw new Error("The user went back in time from a pyramid.");
    }
  },

  /**
   * Performs any necessary changes to the graph's state based on the
   * user's input on a keyboard.
   */
  _keyboardUpdateLoop: function () {
    const KEY_CODE_UP = 38;
    const KEY_CODE_DOWN = 40;
    const KEY_CODE_LEFT = 37;
    const KEY_CODE_RIGHT = 39;
    const KEY_CODE_W = 87;
    const KEY_CODE_A = 65;
    const KEY_CODE_S = 83;
    const KEY_CODE_D = 68;

    let canvasWidth = this._width;
    let pressed = this._keysPressed;

    let selection = this._selection;
    let selectionWidth = selection.end - selection.start;
    let selectionScale = canvasWidth / selectionWidth;

    let translation = [0, 0];
    let isZooming = false;
    let isPanning = false;

    if (pressed[KEY_CODE_UP] || pressed[KEY_CODE_W]) {
      translation[0] += GRAPH_KEYBOARD_ZOOM_SENSITIVITY / selectionScale;
      translation[1] -= GRAPH_KEYBOARD_ZOOM_SENSITIVITY / selectionScale;
      isZooming = true;
    }
    if (pressed[KEY_CODE_DOWN] || pressed[KEY_CODE_S]) {
      translation[0] -= GRAPH_KEYBOARD_ZOOM_SENSITIVITY / selectionScale;
      translation[1] += GRAPH_KEYBOARD_ZOOM_SENSITIVITY / selectionScale;
      isZooming = true;
    }
    if (pressed[KEY_CODE_LEFT] || pressed[KEY_CODE_A]) {
      translation[0] -= GRAPH_KEYBOARD_PAN_SENSITIVITY / selectionScale;
      translation[1] -= GRAPH_KEYBOARD_PAN_SENSITIVITY / selectionScale;
      isPanning = true;
    }
    if (pressed[KEY_CODE_RIGHT] || pressed[KEY_CODE_D]) {
      translation[0] += GRAPH_KEYBOARD_PAN_SENSITIVITY / selectionScale;
      translation[1] += GRAPH_KEYBOARD_PAN_SENSITIVITY / selectionScale;
      isPanning = true;
    }

    if (isPanning) {
      // Accelerate the left/right selection panning continuously
      // while the pan keys are pressed.
      this._keyboardPanAccelerationFactor *= GRAPH_KEYBOARD_ACCELERATION;
      translation[0] *= this._keyboardPanAccelerationFactor;
      translation[1] *= this._keyboardPanAccelerationFactor;
    } else {
      this._keyboardPanAccelerationFactor = 1;
    }

    if (isZooming) {
      // Accelerate the in/out selection zooming continuously
      // while the zoom keys are pressed.
      this._keyboardZoomAccelerationFactor *= GRAPH_KEYBOARD_ACCELERATION;
      translation[0] *= this._keyboardZoomAccelerationFactor;
      translation[1] *= this._keyboardZoomAccelerationFactor;
    } else {
      this._keyboardZoomAccelerationFactor = 1;
    }

    if (translation[0] != 0 || translation[1] != 0) {
      // Make sure the panning translation speed doesn't end up
      // being too high.
      let maxTranslation = GRAPH_KEYBOARD_TRANSLATION_MAX / selectionScale;
      if (Math.abs(translation[0]) > maxTranslation) {
        translation[0] = Math.sign(translation[0]) * maxTranslation;
      }
      if (Math.abs(translation[1]) > maxTranslation) {
        translation[1] = Math.sign(translation[1]) * maxTranslation;
      }
      this._selection.start += translation[0];
      this._selection.end += translation[1];
      this._normalizeSelectionBounds();
      this.emit("selecting");
    }
  },

  /**
   * Draws the overhead header, with time markers and ticks in this graph.
   *
   * @param number dataOffset, dataScale
   *        Offsets and scales the data source by the specified amount.
   *        This is used for scrolling the visualization.
   */
  _drawHeader: function (dataOffset, dataScale) {
    let ctx = this._ctx;
    let canvasWidth = this._width;
    let headerHeight = OVERVIEW_HEADER_HEIGHT * this._pixelRatio;

    ctx.fillStyle = this.overviewHeaderBackgroundColor;
    ctx.fillRect(0, 0, canvasWidth, headerHeight);

    this._drawTicks(dataOffset, dataScale, {
      from: 0,
      to: headerHeight,
      renderText: true
    });
  },

  /**
   * Draws the overhead ticks in this graph in the flame graph area.
   *
   * @param number dataOffset, dataScale, from, to, renderText
   *        Offsets and scales the data source by the specified amount.
   *        from and to determine the Y position of how far the stroke
   *        should be drawn.
   *        This is used when scrolling the visualization.
   */
  _drawTicks: function (dataOffset, dataScale, options) {
    let { from, to, renderText } = options || {};
    let ctx = this._ctx;
    let canvasWidth = this._width;
    let canvasHeight = this._height;
    let scaledOffset = dataOffset * dataScale;

    let fontSize = OVERVIEW_HEADER_TEXT_FONT_SIZE * this._pixelRatio;
    let fontFamily = OVERVIEW_HEADER_TEXT_FONT_FAMILY;
    let textPaddingLeft = OVERVIEW_HEADER_TEXT_PADDING_LEFT * this._pixelRatio;
    let textPaddingTop = OVERVIEW_HEADER_TEXT_PADDING_TOP * this._pixelRatio;
    let tickInterval = this._findOptimalTickInterval(dataScale);

    ctx.textBaseline = "top";
    ctx.font = fontSize + "px " + fontFamily;
    ctx.fillStyle = this.overviewHeaderTextColor;
    ctx.strokeStyle = OVERVIEW_HEADER_TIMELINE_STROKE_COLOR;
    ctx.beginPath();

    for (let x = -scaledOffset % tickInterval; x < canvasWidth;
         x += tickInterval) {
      let lineLeft = x;
      let textLeft = lineLeft + textPaddingLeft;
      let time = Math.round((x / dataScale + dataOffset) / this._pixelRatio);
      let label = time + " " + this.timelineTickUnits;
      if (renderText) {
        ctx.fillText(label, textLeft, textPaddingTop);
      }
      ctx.moveTo(lineLeft, from || 0);
      ctx.lineTo(lineLeft, to || canvasHeight);
    }

    ctx.stroke();
  },

  /**
   * Draws the blocks and text in this graph.
   *
   * @param object dataSource
   *        The data source. See the constructor for more information.
   * @param number verticalOffset
   *        Offsets the drawing vertically by the specified amount.
   * @param number dataOffset, dataScale
   *        Offsets and scales the data source by the specified amount.
   *        This is used for scrolling the visualization.
   */
  _drawPyramid: function (dataSource, verticalOffset, dataOffset, dataScale) {
    let ctx = this._ctx;

    let fontSize = FLAME_GRAPH_BLOCK_TEXT_FONT_SIZE * this._pixelRatio;
    let fontFamily = FLAME_GRAPH_BLOCK_TEXT_FONT_FAMILY;
    let visibleBlocksInfo = this._drawPyramidFill(dataSource, verticalOffset,
                                                  dataOffset, dataScale);

    ctx.textBaseline = "middle";
    ctx.font = fontSize + "px " + fontFamily;
    ctx.fillStyle = this.blockTextColor;

    this._drawPyramidText(visibleBlocksInfo, verticalOffset,
                          dataOffset, dataScale);
  },

  /**
   * Fills all block inside this graph's pyramid.
   * @see FlameGraph.prototype._drawPyramid
   */
  _drawPyramidFill: function (dataSource, verticalOffset, dataOffset,
                              dataScale) {
    let visibleBlocksInfoStore = [];
    let minVisibleBlockWidth = this._overflowCharWidth;

    for (let { color, blocks } of dataSource) {
      this._drawBlocksFill(
        color, blocks, verticalOffset, dataOffset, dataScale,
        visibleBlocksInfoStore, minVisibleBlockWidth);
    }

    return visibleBlocksInfoStore;
  },

  /**
   * Adds the text for all block inside this graph's pyramid.
   * @see FlameGraph.prototype._drawPyramid
   */
  _drawPyramidText: function (blocksInfo, verticalOffset, dataOffset,
                              dataScale) {
    for (let { block, rect } of blocksInfo) {
      this._drawBlockText(block, rect, verticalOffset, dataOffset, dataScale);
    }
  },

  /**
   * Fills a group of blocks sharing the same style.
   *
   * @param string color
   *        The color used as the block's background.
   * @param array blocks
   *        A list of { x, y, width, height } objects visually representing
   *        all the blocks sharing this particular style.
   * @param number verticalOffset
   *        Offsets the drawing vertically by the specified amount.
   * @param number dataOffset, dataScale
   *        Offsets and scales the data source by the specified amount.
   *        This is used for scrolling the visualization.
   * @param array visibleBlocksInfoStore
   *        An array to store all the visible blocks into, along with the
   *        final baked coordinates and dimensions, after drawing them.
   *        The provided array will be populated.
   * @param number minVisibleBlockWidth
   *        The minimum width of the blocks that will be added into
   *        the `visibleBlocksInfoStore`.
   */
  _drawBlocksFill: function (
    color, blocks, verticalOffset, dataOffset, dataScale,
    visibleBlocksInfoStore, minVisibleBlockWidth) {
    let ctx = this._ctx;
    let canvasWidth = this._width;
    let canvasHeight = this._height;
    let scaledOffset = dataOffset * dataScale;

    ctx.fillStyle = color;
    ctx.beginPath();

    for (let block of blocks) {
      let { x, y, width, height } = block;
      let rectLeft = x * this._pixelRatio * dataScale - scaledOffset;
      let rectTop = (y - verticalOffset + OVERVIEW_HEADER_HEIGHT)
                    * this._pixelRatio;
      let rectWidth = width * this._pixelRatio * dataScale;
      let rectHeight = height * this._pixelRatio;

      // Too far respectively right/left/bottom/top
      if (rectLeft > canvasWidth ||
          rectLeft < -rectWidth ||
          rectTop > canvasHeight ||
          rectTop < -rectHeight) {
        continue;
      }

      // Clamp the blocks position to start at 0. Avoid negative X coords,
      // to properly place the text inside the blocks.
      if (rectLeft < 0) {
        rectWidth += rectLeft;
        rectLeft = 0;
      }

      // Avoid drawing blocks that are too narrow.
      if (rectWidth <= FLAME_GRAPH_BLOCK_BORDER ||
          rectHeight <= FLAME_GRAPH_BLOCK_BORDER) {
        continue;
      }

      ctx.rect(
        rectLeft, rectTop,
        rectWidth - FLAME_GRAPH_BLOCK_BORDER,
        rectHeight - FLAME_GRAPH_BLOCK_BORDER);

      // Populate the visible blocks store with this block if the width
      // is longer than a given threshold.
      if (rectWidth > minVisibleBlockWidth) {
        visibleBlocksInfoStore.push({
          block: block,
          rect: { rectLeft, rectTop, rectWidth, rectHeight }
        });
      }
    }

    ctx.fill();
  },

  /**
   * Adds text for a single block.
   *
   * @param object block
   *        A single { x, y, width, height, text } object visually representing
   *        the block containing the text.
   * @param object rect
   *        A single { rectLeft, rectTop, rectWidth, rectHeight } object
   *        representing the final baked coordinates of the drawn rectangle.
   *        Think of them as screen-space values, vs. object-space values. These
   *        differ from the scalars in `block` when the graph is scaled/panned.
   * @param number verticalOffset
   *        Offsets the drawing vertically by the specified amount.
   * @param number dataOffset, dataScale
   *        Offsets and scales the data source by the specified amount.
   *        This is used for scrolling the visualization.
   */
  _drawBlockText: function (block, rect, verticalOffset, dataOffset,
                            dataScale) {
    let ctx = this._ctx;

    let { text } = block;
    let { rectLeft, rectTop, rectWidth, rectHeight } = rect;

    let paddingTop = FLAME_GRAPH_BLOCK_TEXT_PADDING_TOP * this._pixelRatio;
    let paddingLeft = FLAME_GRAPH_BLOCK_TEXT_PADDING_LEFT * this._pixelRatio;
    let paddingRight = FLAME_GRAPH_BLOCK_TEXT_PADDING_RIGHT * this._pixelRatio;
    let totalHorizontalPadding = paddingLeft + paddingRight;

    // Clamp the blocks position to start at 0. Avoid negative X coords,
    // to properly place the text inside the blocks.
    if (rectLeft < 0) {
      rectWidth += rectLeft;
      rectLeft = 0;
    }

    let textLeft = rectLeft + paddingLeft;
    let textTop = rectTop + rectHeight / 2 + paddingTop;
    let textAvailableWidth = rectWidth - totalHorizontalPadding;

    // Massage the text to fit inside a given width. This clamps the string
    // at the end to avoid overflowing.
    let fittedText = this._getFittedText(text, textAvailableWidth);
    if (fittedText.length < 1) {
      return;
    }

    ctx.fillText(fittedText, textLeft, textTop);
  },

  /**
   * Calculating text widths is necessary to trim the text inside the blocks
   * while the scaling changes (e.g. via scrolling). This is very expensive,
   * so maintain a cache of string contents to text widths.
   */
  _textWidthsCache: null,
  _overflowCharWidth: null,
  _averageCharWidth: null,

  /**
   * Gets the width of the specified text, for the current context state
   * (font size, family etc.).
   *
   * @param string text
   *        The text to analyze.
   * @return number
   *         The text width.
   */
  _getTextWidth: function (text) {
    let cachedWidth = this._textWidthsCache[text];
    if (cachedWidth) {
      return cachedWidth;
    }
    let metrics = this._ctx.measureText(text);
    return (this._textWidthsCache[text] = metrics.width);
  },

  /**
   * Gets an approximate width of the specified text. This is much faster
   * than `_getTextWidth`, but inexact.
   *
   * @param string text
   *        The text to analyze.
   * @return number
   *         The approximate text width.
   */
  _getTextWidthApprox: function (text) {
    return text.length * this._averageCharWidth;
  },

  /**
   * Gets the average letter width in the English alphabet, for the current
   * context state (font size, family etc.). This provides a close enough
   * value to use in `_getTextWidthApprox`.
   *
   * @return number
   *         The average letter width.
   */
  _calcAverageCharWidth: function () {
    let letterWidthsSum = 0;
    // space
    let start = 32;
    // "z"
    let end = 123;

    for (let i = start; i < end; i++) {
      let char = String.fromCharCode(i);
      letterWidthsSum += this._getTextWidth(char);
    }

    return letterWidthsSum / (end - start);
  },

  /**
   * Massage a text to fit inside a given width. This clamps the string
   * at the end to avoid overflowing.
   *
   * @param string text
   *        The text to fit inside the given width.
   * @param number maxWidth
   *        The available width for the given text.
   * @return string
   *         The fitted text.
   */
  _getFittedText: function (text, maxWidth) {
    let textWidth = this._getTextWidth(text);
    if (textWidth < maxWidth) {
      return text;
    }
    if (this._overflowCharWidth > maxWidth) {
      return "";
    }
    for (let i = 1, len = text.length; i <= len; i++) {
      let trimmedText = text.substring(0, len - i);
      let trimmedWidth = this._getTextWidthApprox(trimmedText)
                         + this._overflowCharWidth;
      if (trimmedWidth < maxWidth) {
        return trimmedText + this.overflowChar;
      }
    }
    return "";
  },

  /**
   * Listener for the "keydown" event on the graph's container.
   */
  _onKeyDown: function (e) {
    ViewHelpers.preventScrolling(e);

    const hasModifier = e.ctrlKey || e.shiftKey || e.altKey || e.metaKey;

    if (!hasModifier && !this._keysPressed[e.keyCode]) {
      this._keysPressed[e.keyCode] = true;
      this._userInputStack++;
      this._shouldRedraw = true;
    }
  },

  /**
   * Listener for the "keyup" event on the graph's container.
   */
  _onKeyUp: function (e) {
    ViewHelpers.preventScrolling(e);

    if (this._keysPressed[e.keyCode]) {
      this._keysPressed[e.keyCode] = false;
      this._userInputStack--;
      this._shouldRedraw = true;
    }
  },

  /**
   * Listener for the "mousemove" event on the graph's container.
   */
  _onMouseMove: function (e) {
    let {mouseX, mouseY} = this._getRelativeEventCoordinates(e);

    let canvasWidth = this._width;

    let selection = this._selection;
    let selectionWidth = selection.end - selection.start;
    let selectionScale = canvasWidth / selectionWidth;

    let horizDrag = this._selectionDragger;
    let vertDrag = this._verticalOffsetDragger;

    // Avoid dragging both horizontally and vertically at the same time,
    // as this doesn't feel natural. Based on a minimum distance, enable either
    // one, and remember the drag direction to offset the mouse coords later.
    if (!this._horizontalDragEnabled && !this._verticalDragEnabled) {
      let horizDiff = Math.abs(horizDrag.origin - mouseX);
      if (horizDiff > this.horizontalPanThreshold) {
        this._horizontalDragDirection = Math.sign(horizDrag.origin - mouseX);
        this._horizontalDragEnabled = true;
      }
      let vertDiff = Math.abs(vertDrag.origin - mouseY);
      if (vertDiff > this.verticalPanThreshold) {
        this._verticalDragDirection = Math.sign(vertDrag.origin - mouseY);
        this._verticalDragEnabled = true;
      }
    }

    if (horizDrag.origin != null && this._horizontalDragEnabled) {
      let relativeX = mouseX + this._horizontalDragDirection *
                               this.horizontalPanThreshold;
      selection.start = horizDrag.anchor.start +
                        (horizDrag.origin - relativeX) / selectionScale;
      selection.end = horizDrag.anchor.end +
                      (horizDrag.origin - relativeX) / selectionScale;
      this._normalizeSelectionBounds();
      this._shouldRedraw = true;
      this.emit("selecting");
    }

    if (vertDrag.origin != null && this._verticalDragEnabled) {
      let relativeY = mouseY +
                      this._verticalDragDirection * this.verticalPanThreshold;
      this._verticalOffset = vertDrag.anchor +
                             (vertDrag.origin - relativeY) / this._pixelRatio;
      this._normalizeVerticalOffset();
      this._shouldRedraw = true;
      this.emit("panning-vertically");
    }
  },

  /**
   * Listener for the "mousedown" event on the graph's container.
   */
  _onMouseDown: function (e) {
    let {mouseX, mouseY} = this._getRelativeEventCoordinates(e);

    this._selectionDragger.origin = mouseX;
    this._selectionDragger.anchor.start = this._selection.start;
    this._selectionDragger.anchor.end = this._selection.end;

    this._verticalOffsetDragger.origin = mouseY;
    this._verticalOffsetDragger.anchor = this._verticalOffset;

    this._horizontalDragEnabled = false;
    this._verticalDragEnabled = false;

    this._canvas.setAttribute("input", "adjusting-view-area");
  },

  /**
   * Listener for the "mouseup" event on the graph's container.
   */
  _onMouseUp: function () {
    this._selectionDragger.origin = null;
    this._verticalOffsetDragger.origin = null;
    this._horizontalDragEnabled = false;
    this._horizontalDragDirection = 0;
    this._verticalDragEnabled = false;
    this._verticalDragDirection = 0;
    this._canvas.removeAttribute("input");
  },

  /**
   * Listener for the "wheel" event on the graph's container.
   */
  _onMouseWheel: function (e) {
    let {mouseX} = this._getRelativeEventCoordinates(e);

    let canvasWidth = this._width;

    let selection = this._selection;
    let selectionWidth = selection.end - selection.start;
    let selectionScale = canvasWidth / selectionWidth;

    switch (e.axis) {
      case e.VERTICAL_AXIS: {
        let distFromStart = mouseX;
        let distFromEnd = canvasWidth - mouseX;
        let vector = e.detail * GRAPH_WHEEL_ZOOM_SENSITIVITY / selectionScale;
        selection.start -= distFromStart * vector;
        selection.end += distFromEnd * vector;
        break;
      }
      case e.HORIZONTAL_AXIS: {
        let vector = e.detail * GRAPH_WHEEL_SCROLL_SENSITIVITY / selectionScale;
        selection.start += vector;
        selection.end += vector;
        break;
      }
    }

    this._normalizeSelectionBounds();
    this._shouldRedraw = true;
    this.emit("selecting");
  },

  /**
   * Makes sure the start and end points of the current selection
   * are withing the graph's visible bounds, and that they form a selection
   * wider than the allowed minimum width.
   */
  _normalizeSelectionBounds: function () {
    let boundsStart = this._bounds.start;
    let boundsEnd = this._bounds.end;
    let selectionStart = this._selection.start;
    let selectionEnd = this._selection.end;

    if (selectionStart < boundsStart) {
      selectionStart = boundsStart;
    }
    if (selectionEnd < boundsStart) {
      selectionStart = boundsStart;
      selectionEnd = GRAPH_MIN_SELECTION_WIDTH;
    }
    if (selectionEnd > boundsEnd) {
      selectionEnd = boundsEnd;
    }
    if (selectionStart > boundsEnd) {
      selectionEnd = boundsEnd;
      selectionStart = boundsEnd - GRAPH_MIN_SELECTION_WIDTH;
    }
    if (selectionEnd - selectionStart < GRAPH_MIN_SELECTION_WIDTH) {
      let midPoint = (selectionStart + selectionEnd) / 2;
      selectionStart = midPoint - GRAPH_MIN_SELECTION_WIDTH / 2;
      selectionEnd = midPoint + GRAPH_MIN_SELECTION_WIDTH / 2;
    }

    this._selection.start = selectionStart;
    this._selection.end = selectionEnd;
  },

  /**
   * Makes sure that the current vertical offset is within the allowed
   * panning range.
   */
  _normalizeVerticalOffset: function () {
    this._verticalOffset = Math.max(this._verticalOffset, 0);
  },

  /**
   *
   * Finds the optimal tick interval between time markers in this graph.
   *
   * @param number dataScale
   * @return number
   */
  _findOptimalTickInterval: function (dataScale) {
    let timingStep = TIMELINE_TICKS_MULTIPLE;
    let spacingMin = TIMELINE_TICKS_SPACING_MIN * this._pixelRatio;
    let maxIters = FIND_OPTIMAL_TICK_INTERVAL_MAX_ITERS;
    let numIters = 0;

    if (dataScale > spacingMin) {
      return dataScale;
    }

    while (true) {
      let scaledStep = dataScale * timingStep;
      if (++numIters > maxIters) {
        return scaledStep;
      }
      if (scaledStep < spacingMin) {
        timingStep <<= 1;
        continue;
      }
      return scaledStep;
    }
  },

  /**
   * Gets the offset of this graph's container relative to the owner window.
   *
   * @return object
   *         The { left, top } offset.
   */
  _getContainerOffset: function () {
    let node = this._canvas;
    let x = 0;
    let y = 0;

    while ((node = node.offsetParent)) {
      x += node.offsetLeft;
      y += node.offsetTop;
    }

    return { left: x, top: y };
  },

  /**
   * Given a MouseEvent, make it relative to this._canvas.
   * @return object {mouseX,mouseY}
   */
  _getRelativeEventCoordinates: function (e) {
    // For ease of testing, testX and testY can be passed in as the event
    // object.
    if ("testX" in e && "testY" in e) {
      return {
        mouseX: e.testX * this._pixelRatio,
        mouseY: e.testY * this._pixelRatio
      };
    }

    let offset = this._getContainerOffset();
    let mouseX = (e.clientX - offset.left) * this._pixelRatio;
    let mouseY = (e.clientY - offset.top) * this._pixelRatio;

    return {mouseX, mouseY};
  },

  /**
   * Listener for the "resize" event on the graph's parent node.
   */
  _onResize: function () {
    if (this.hasData()) {
      setNamedTimeout(this._uid, GRAPH_RESIZE_EVENTS_DRAIN, this.refresh);
    }
  }
};

/**
 * A collection of utility functions converting various data sources
 * into a format drawable by the FlameGraph.
 */
var FlameGraphUtils = {
  _cache: new WeakMap(),

  /**
   * Create data suitable for use with FlameGraph from a profile's samples.
   * Iterate the profile's samples and keep a moving window of stack traces.
   *
   * @param object thread
   *               The raw thread object received from the backend.
   * @param object options
   *               Additional supported options,
   *                 - boolean contentOnly [optional]
   *                 - boolean invertTree [optional]
   *                 - boolean flattenRecursion [optional]
   *                 - string showIdleBlocks [optional]
   * @return object
   *         Data source usable by FlameGraph.
   */
  createFlameGraphDataFromThread: function (thread, options = {}, out = []) {
    let cached = this._cache.get(thread);
    if (cached) {
      return cached;
    }

    // 1. Create a map of colors to arrays, representing buckets of
    // blocks inside the flame graph pyramid sharing the same style.

    let buckets = Array.from({ length: PALLETTE_SIZE }, () => []);

    // 2. Populate the buckets by iterating over every frame in every sample.

    let { samples, stackTable, frameTable, stringTable } = thread;

    const SAMPLE_STACK_SLOT = samples.schema.stack;
    const SAMPLE_TIME_SLOT = samples.schema.time;

    const STACK_PREFIX_SLOT = stackTable.schema.prefix;
    const STACK_FRAME_SLOT = stackTable.schema.frame;

    const getOrAddInflatedFrame = FrameUtils.getOrAddInflatedFrame;

    let inflatedFrameCache = FrameUtils.getInflatedFrameCache(frameTable);
    let labelCache = Object.create(null);

    let samplesData = samples.data;
    let stacksData = stackTable.data;

    let flattenRecursion = options.flattenRecursion;

    // Reused objects.
    let mutableFrameKeyOptions = {
      contentOnly: options.contentOnly,
      isRoot: false,
      isLeaf: false,
      isMetaCategoryOut: false
    };

    // Take the timestamp of the first sample as prevTime. 0 is incorrect due
    // to circular buffer wraparound. If wraparound happens, then the first
    // sample will have an incorrect, large duration.
    let prevTime = samplesData.length > 0 ? samplesData[0][SAMPLE_TIME_SLOT]
                                          : 0;
    let prevFrames = [];
    let sampleFrames = [];
    let sampleFrameKeys = [];

    for (let i = 1; i < samplesData.length; i++) {
      let sample = samplesData[i];
      let time = sample[SAMPLE_TIME_SLOT];

      let stackIndex = sample[SAMPLE_STACK_SLOT];
      let prevFrameKey;

      let stackDepth = 0;

      // Inflate the stack and keep a moving window of call stacks.
      //
      // For reference, see the similar block comment in
      // ThreadNode.prototype._buildInverted.
      //
      // In a similar fashion to _buildInverted, frames are inflated on the
      // fly while stackwalking the stackTable trie. The exact same frame key
      // is computed in both _buildInverted and here.
      //
      // Unlike _buildInverted, which builds a call tree directly, the flame
      // graph inflates the stack into an array, as it maintains a moving
      // window of stacks over time.
      //
      // Like _buildInverted, the various filtering functions are also inlined
      // into stack inflation loop.
      while (stackIndex !== null) {
        let stackEntry = stacksData[stackIndex];
        let frameIndex = stackEntry[STACK_FRAME_SLOT];

        // Fetch the stack prefix (i.e. older frames) index.
        stackIndex = stackEntry[STACK_PREFIX_SLOT];

        // Inflate the frame.
        let inflatedFrame = getOrAddInflatedFrame(inflatedFrameCache,
                                                  frameIndex, frameTable,
                                                  stringTable);

        mutableFrameKeyOptions.isRoot = stackIndex === null;
        mutableFrameKeyOptions.isLeaf = stackDepth === 0;
        let frameKey = inflatedFrame.getFrameKey(mutableFrameKeyOptions);

        // If not skipping the frame, add it to the current level. The (root)
        // node isn't useful for flame graphs.
        if (frameKey !== "" && frameKey !== "(root)") {
          // If the frame is a meta category, use the category label.
          if (mutableFrameKeyOptions.isMetaCategoryOut) {
            frameKey = CATEGORY_MAPPINGS[frameKey].label;
          }

          sampleFrames[stackDepth] = inflatedFrame;
          sampleFrameKeys[stackDepth] = frameKey;

          // If we shouldn't flatten the current frame into the previous one,
          // increment the stack depth.
          if (!flattenRecursion || frameKey !== prevFrameKey) {
            stackDepth++;
          }

          prevFrameKey = frameKey;
        }
      }

      // Uninvert frames in place if needed.
      if (!options.invertTree) {
        sampleFrames.length = stackDepth;
        sampleFrames.reverse();
        sampleFrameKeys.length = stackDepth;
        sampleFrameKeys.reverse();
      }

      // If no frames are available, add a pseudo "idle" block in between.
      let isIdleFrame = false;
      if (options.showIdleBlocks && stackDepth === 0) {
        sampleFrames[0] = null;
        sampleFrameKeys[0] = options.showIdleBlocks;
        stackDepth = 1;
        isIdleFrame = true;
      }

      // Put each frame in a bucket.
      for (let frameIndex = 0; frameIndex < stackDepth; frameIndex++) {
        let key = sampleFrameKeys[frameIndex];
        let prevFrame = prevFrames[frameIndex];

        // Frames at the same location and the same depth will be reused.
        // If there is a block already created, change its width.
        if (prevFrame && prevFrame.frameKey === key) {
          prevFrame.width = (time - prevFrame.startTime);
        } else {
          // Otherwise, create a new block for this frame at this depth,
          // using a simple location based salt for picking a color.
          let hash = this._getStringHash(key);
          let bucket = buckets[hash % PALLETTE_SIZE];

          let label;
          if (isIdleFrame) {
            label = key;
          } else {
            label = labelCache[key];
            if (!label) {
              label = labelCache[key] =
                this._formatLabel(key, sampleFrames[frameIndex]);
            }
          }

          bucket.push(prevFrames[frameIndex] = {
            startTime: prevTime,
            frameKey: key,
            x: prevTime,
            y: frameIndex * FLAME_GRAPH_BLOCK_HEIGHT,
            width: time - prevTime,
            height: FLAME_GRAPH_BLOCK_HEIGHT,
            text: label
          });
        }
      }

      // Previous frames at stack depths greater than the current sample's
      // maximum need to be nullified. It's nonsensical to reuse them.
      prevFrames.length = stackDepth;
      prevTime = time;
    }

    // 3. Convert the buckets into a data source usable by the FlameGraph.
    // This is a simple conversion from a Map to an Array.

    for (let i = 0; i < buckets.length; i++) {
      out.push({ color: COLOR_PALLETTE[i], blocks: buckets[i] });
    }

    this._cache.set(thread, out);
    return out;
  },

  /**
   * Clears the cached flame graph data created for the given source.
   * @param any source
   */
  removeFromCache: function (source) {
    this._cache.delete(source);
  },

  /**
   * Very dumb hashing of a string. Used to pick colors from a pallette.
   *
   * @param string input
   * @return number
   */
  _getStringHash: function (input) {
    const STRING_HASH_PRIME1 = 7;
    const STRING_HASH_PRIME2 = 31;

    let hash = STRING_HASH_PRIME1;

    for (let i = 0, len = input.length; i < len; i++) {
      hash *= STRING_HASH_PRIME2;
      hash += input.charCodeAt(i);

      if (hash > Number.MAX_SAFE_INTEGER / STRING_HASH_PRIME2) {
        return hash;
      }
    }

    return hash;
  },

  /**
   * Takes a frame key and a frame, and returns a string that should be
   * displayed in its flame block.
   *
   * @param string key
   * @param object frame
   * @return string
   */
  _formatLabel: function (key, frame) {
    let { functionName, fileName, line } =
      FrameUtils.parseLocation(key, frame.line);
    let label = FrameUtils.shouldDemangle(functionName) ? demangle(functionName)
                                                        : functionName;

    if (fileName) {
      label += ` (${fileName}${line != null ? (":" + line) : ""})`;
    }

    return label;
  }
};

exports.FlameGraph = FlameGraph;
exports.FlameGraphUtils = FlameGraphUtils;
exports.PALLETTE_SIZE = PALLETTE_SIZE;
exports.FLAME_GRAPH_BLOCK_HEIGHT = FLAME_GRAPH_BLOCK_HEIGHT;
exports.FLAME_GRAPH_BLOCK_TEXT_FONT_SIZE = FLAME_GRAPH_BLOCK_TEXT_FONT_SIZE;
exports.FLAME_GRAPH_BLOCK_TEXT_FONT_FAMILY = FLAME_GRAPH_BLOCK_TEXT_FONT_FAMILY;
PK
!<ւ�A����@chrome/devtools/modules/devtools/client/shared/widgets/Graphs.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Task } = require("devtools/shared/task");
const { setNamedTimeout } = require("devtools/client/shared/widgets/view-helpers");
const { getCurrentZoom } = require("devtools/shared/layout/utils");

loader.lazyRequireGetter(this, "defer", "devtools/shared/defer");
loader.lazyRequireGetter(this, "EventEmitter",
  "devtools/shared/event-emitter");

loader.lazyImporter(this, "DevToolsWorker",
  "resource://devtools/shared/worker/worker.js");

const HTML_NS = "http://www.w3.org/1999/xhtml";
const GRAPH_SRC = "chrome://devtools/content/shared/widgets/graphs-frame.xhtml";
const WORKER_URL =
  "resource://devtools/client/shared/widgets/GraphsWorker.js";

// Generic constants.

const GRAPH_RESIZE_EVENTS_DRAIN = 100; // ms
const GRAPH_WHEEL_ZOOM_SENSITIVITY = 0.00075;
const GRAPH_WHEEL_SCROLL_SENSITIVITY = 0.1;
const GRAPH_WHEEL_MIN_SELECTION_WIDTH = 10; // px

const GRAPH_SELECTION_BOUNDARY_HOVER_LINE_WIDTH = 4; // px
const GRAPH_SELECTION_BOUNDARY_HOVER_THRESHOLD = 10; // px
const GRAPH_MAX_SELECTION_LEFT_PADDING = 1; // px
const GRAPH_MAX_SELECTION_RIGHT_PADDING = 1; // px

const GRAPH_REGION_LINE_WIDTH = 1; // px
const GRAPH_REGION_LINE_COLOR = "rgba(237,38,85,0.8)";

const GRAPH_STRIPE_PATTERN_WIDTH = 16; // px
const GRAPH_STRIPE_PATTERN_HEIGHT = 16; // px
const GRAPH_STRIPE_PATTERN_LINE_WIDTH = 2; // px
const GRAPH_STRIPE_PATTERN_LINE_SPACING = 4; // px

/**
 * Small data primitives for all graphs.
 */
this.GraphCursor = function () {
  this.x = null;
  this.y = null;
};

this.GraphArea = function () {
  this.start = null;
  this.end = null;
};

this.GraphAreaDragger = function (anchor = new GraphArea()) {
  this.origin = null;
  this.anchor = anchor;
};

this.GraphAreaResizer = function () {
  this.margin = null;
};

/**
 * Base class for all graphs using a canvas to render the data source. Handles
 * frame creation, data source, selection bounds, cursor position, etc.
 *
 * Language:
 *   - The "data" represents the values used when building the graph.
 *     Its specific format is defined by the inheriting classes.
 *
 *   - A "cursor" is the cliphead position across the X axis of the graph.
 *
 *   - A "selection" is defined by a "start" and an "end" value and
 *     represents the selected bounds in the graph.
 *
 *   - A "region" is a highlighted area in the graph, also defined by a
 *     "start" and an "end" value, but distinct from the "selection". It is
 *     simply used to highlight important regions in the data.
 *
 * Instances of this class are EventEmitters with the following events:
 *   - "ready": when the container iframe and canvas are created.
 *   - "selecting": when the selection is set or changed.
 *   - "deselecting": when the selection is dropped.
 *
 * @param nsIDOMNode parent
 *        The parent node holding the graph.
 * @param string name
 *        The graph type, used for setting the correct class names.
 *        Currently supported: "line-graph" only.
 * @param number sharpness [optional]
 *        Defaults to the current device pixel ratio.
 */
this.AbstractCanvasGraph = function (parent, name, sharpness) {
  EventEmitter.decorate(this);

  this._parent = parent;
  this._ready = defer();

  this._uid = "canvas-graph-" + Date.now();
  this._renderTargets = new Map();

  AbstractCanvasGraph.createIframe(GRAPH_SRC, parent, iframe => {
    this._iframe = iframe;
    this._window = iframe.contentWindow;
    this._topWindow = this._window.top;
    this._document = iframe.contentDocument;
    this._pixelRatio = sharpness || this._window.devicePixelRatio;

    let container =
      this._container = this._document.getElementById("graph-container");
    container.className = name + "-widget-container graph-widget-container";

    let canvas = this._canvas = this._document.getElementById("graph-canvas");
    canvas.className = name + "-widget-canvas graph-widget-canvas";

    let bounds = parent.getBoundingClientRect();
    bounds.width = this.fixedWidth || bounds.width;
    bounds.height = this.fixedHeight || bounds.height;
    iframe.setAttribute("width", bounds.width);
    iframe.setAttribute("height", bounds.height);

    this._width = canvas.width = bounds.width * this._pixelRatio;
    this._height = canvas.height = bounds.height * this._pixelRatio;
    this._ctx = canvas.getContext("2d");
    this._ctx.imageSmoothingEnabled = false;

    this._cursor = new GraphCursor();
    this._selection = new GraphArea();
    this._selectionDragger = new GraphAreaDragger();
    this._selectionResizer = new GraphAreaResizer();
    this._isMouseActive = false;

    this._onAnimationFrame = this._onAnimationFrame.bind(this);
    this._onMouseMove = this._onMouseMove.bind(this);
    this._onMouseDown = this._onMouseDown.bind(this);
    this._onMouseUp = this._onMouseUp.bind(this);
    this._onMouseWheel = this._onMouseWheel.bind(this);
    this._onMouseOut = this._onMouseOut.bind(this);
    this._onResize = this._onResize.bind(this);
    this.refresh = this.refresh.bind(this);

    this._window.addEventListener("mousemove", this._onMouseMove);
    this._window.addEventListener("mousedown", this._onMouseDown);
    this._window.addEventListener("MozMousePixelScroll", this._onMouseWheel);
    this._window.addEventListener("mouseout", this._onMouseOut);

    let ownerWindow = this._parent.ownerDocument.defaultView;
    ownerWindow.addEventListener("resize", this._onResize);

    this._animationId =
      this._window.requestAnimationFrame(this._onAnimationFrame);

    this._ready.resolve(this);
    this.emit("ready", this);
  });
};

AbstractCanvasGraph.prototype = {
  /**
   * Read-only width and height of the canvas.
   * @return number
   */
  get width() {
    return this._width;
  },
  get height() {
    return this._height;
  },

  /**
   * Return true if the mouse is actively messing with the selection, false
   * otherwise.
   */
  get isMouseActive() {
    return this._isMouseActive;
  },

  /**
   * Returns a promise resolved once this graph is ready to receive data.
   */
  ready: function () {
    return this._ready.promise;
  },

  /**
   * Destroys this graph.
   */
  destroy: Task.async(function* () {
    yield this.ready();

    this._topWindow.removeEventListener("mousemove", this._onMouseMove);
    this._topWindow.removeEventListener("mouseup", this._onMouseUp);
    this._window.removeEventListener("mousemove", this._onMouseMove);
    this._window.removeEventListener("mousedown", this._onMouseDown);
    this._window.removeEventListener("MozMousePixelScroll", this._onMouseWheel);
    this._window.removeEventListener("mouseout", this._onMouseOut);

    let ownerWindow = this._parent.ownerDocument.defaultView;
    if (ownerWindow) {
      ownerWindow.removeEventListener("resize", this._onResize);
    }

    this._window.cancelAnimationFrame(this._animationId);
    this._iframe.remove();

    this._cursor = null;
    this._selection = null;
    this._selectionDragger = null;
    this._selectionResizer = null;

    this._data = null;
    this._mask = null;
    this._maskArgs = null;
    this._regions = null;

    this._cachedBackgroundImage = null;
    this._cachedGraphImage = null;
    this._cachedMaskImage = null;
    this._renderTargets.clear();
    gCachedStripePattern.clear();

    this.emit("destroyed");
  }),

  /**
   * Rendering options. Subclasses should override these.
   */
  clipheadLineWidth: 1,
  clipheadLineColor: "transparent",
  selectionLineWidth: 1,
  selectionLineColor: "transparent",
  selectionBackgroundColor: "transparent",
  selectionStripesColor: "transparent",
  regionBackgroundColor: "transparent",
  regionStripesColor: "transparent",

  /**
   * Makes sure the canvas graph is of the specified width or height, and
   * doesn't flex to fit all the available space.
   */
  fixedWidth: null,
  fixedHeight: null,

  /**
   * Optionally builds and caches a background image for this graph.
   * Inheriting classes may override this method.
   */
  buildBackgroundImage: function () {
    return null;
  },

  /**
   * Builds and caches a graph image, based on the data source supplied
   * in `setData`. The graph image is not rebuilt on each frame, but
   * only when the data source changes.
   */
  buildGraphImage: function () {
    let error = "This method needs to be implemented by inheriting classes.";
    throw new Error(error);
  },

  /**
   * Optionally builds and caches a mask image for this graph, composited
   * over the data image created via `buildGraphImage`. Inheriting classes
   * may override this method.
   */
  buildMaskImage: function () {
    return null;
  },

  /**
   * When setting the data source, the coordinates and values may be
   * stretched or squeezed on the X/Y axis, to fit into the available space.
   */
  dataScaleX: 1,
  dataScaleY: 1,

  /**
   * Sets the data source for this graph.
   *
   * @param object data
   *        The data source. The actual format is specified by subclasses.
   */
  setData: function (data) {
    this._data = data;
    this._cachedBackgroundImage = this.buildBackgroundImage();
    this._cachedGraphImage = this.buildGraphImage();
    this._shouldRedraw = true;
  },

  /**
   * Same as `setData`, but waits for this graph to finish initializing first.
   *
   * @param object data
   *        The data source. The actual format is specified by subclasses.
   * @return promise
   *         A promise resolved once the data is set.
   */
  setDataWhenReady: Task.async(function* (data) {
    yield this.ready();
    this.setData(data);
  }),

  /**
   * Adds a mask to this graph.
   *
   * @param any mask, options
   *        See `buildMaskImage` in inheriting classes for the required args.
   */
  setMask: function (mask, ...options) {
    this._mask = mask;
    this._maskArgs = [mask, ...options];
    this._cachedMaskImage = this.buildMaskImage.apply(this, this._maskArgs);
    this._shouldRedraw = true;
  },

  /**
   * Adds regions to this graph.
   *
   * See the "Language" section in the constructor documentation
   * for details about what "regions" represent.
   *
   * @param array regions
   *        A list of { start, end } values.
   */
  setRegions: function (regions) {
    if (!this._cachedGraphImage) {
      throw new Error("Can't highlight regions on a graph with " +
                      "no data displayed.");
    }
    if (this._regions) {
      throw new Error("Regions were already highlighted on the graph.");
    }
    this._regions = regions.map(e => ({
      start: e.start * this.dataScaleX,
      end: e.end * this.dataScaleX
    }));
    this._bakeRegions(this._regions, this._cachedGraphImage);
    this._shouldRedraw = true;
  },

  /**
   * Gets whether or not this graph has a data source.
   * @return boolean
   */
  hasData: function () {
    return !!this._data;
  },

  /**
   * Gets whether or not this graph has any mask applied.
   * @return boolean
   */
  hasMask: function () {
    return !!this._mask;
  },

  /**
   * Gets whether or not this graph has any regions.
   * @return boolean
   */
  hasRegions: function () {
    return !!this._regions;
  },

  /**
   * Sets the selection bounds.
   * Use `dropSelection` to remove the selection.
   *
   * If the bounds aren't different, no "selection" event is emitted.
   *
   * See the "Language" section in the constructor documentation
   * for details about what a "selection" represents.
   *
   * @param object selection
   *        The selection's { start, end } values.
   */
  setSelection: function (selection) {
    if (!selection || selection.start == null || selection.end == null) {
      throw new Error("Invalid selection coordinates");
    }
    if (!this.isSelectionDifferent(selection)) {
      return;
    }
    this._selection.start = selection.start;
    this._selection.end = selection.end;
    this._shouldRedraw = true;
    this.emit("selecting");
  },

  /**
   * Gets the selection bounds.
   * If there's no selection, the bounds have null values.
   *
   * @return object
   *         The selection's { start, end } values.
   */
  getSelection: function () {
    if (this.hasSelection()) {
      return { start: this._selection.start, end: this._selection.end };
    }
    if (this.hasSelectionInProgress()) {
      return { start: this._selection.start, end: this._cursor.x };
    }
    return { start: null, end: null };
  },

  /**
   * Sets the selection bounds, scaled to correlate with the data source ranges,
   * such that a [0, max width] selection maps to [first value, last value].
   *
   * @param object selection
   *        The selection's { start, end } values.
   * @param object { mapStart, mapEnd } mapping [optional]
   *        Invoked when retrieving the numbers in the data source representing
   *        the first and last values, on the X axis.
   */
  setMappedSelection: function (selection, mapping = {}) {
    if (!this.hasData()) {
      throw new Error("A data source is necessary for retrieving " +
                      "a mapped selection.");
    }
    if (!selection || selection.start == null || selection.end == null) {
      throw new Error("Invalid selection coordinates");
    }

    let { mapStart, mapEnd } = mapping;
    let startTime = (mapStart || (e => e.delta))(this._data[0]);
    let endTime = (mapEnd || (e => e.delta))(this._data[this._data.length - 1]);

    // The selection's start and end values are not guaranteed to be ascending.
    // Also make sure that the selection bounds fit inside the data bounds.
    let min = Math.max(Math.min(selection.start, selection.end), startTime);
    let max = Math.min(Math.max(selection.start, selection.end), endTime);
    min = map(min, startTime, endTime, 0, this._width);
    max = map(max, startTime, endTime, 0, this._width);

    this.setSelection({ start: min, end: max });
  },

  /**
   * Gets the selection bounds, scaled to correlate with the data source ranges,
   * such that a [0, max width] selection maps to [first value, last value].
   *
   * @param object { mapStart, mapEnd } mapping [optional]
   *        Invoked when retrieving the numbers in the data source representing
   *        the first and last values, on the X axis.
   * @return object
   *         The mapped selection's { min, max } values.
   */
  getMappedSelection: function (mapping = {}) {
    if (!this.hasData()) {
      throw new Error("A data source is necessary for retrieving a " +
                      "mapped selection.");
    }
    if (!this.hasSelection() && !this.hasSelectionInProgress()) {
      return { min: null, max: null };
    }

    let { mapStart, mapEnd } = mapping;
    let startTime = (mapStart || (e => e.delta))(this._data[0]);
    let endTime = (mapEnd || (e => e.delta))(this._data[this._data.length - 1]);

    // The selection's start and end values are not guaranteed to be ascending.
    // This can happen, for example, when click & dragging from right to left.
    // Also make sure that the selection bounds fit inside the canvas bounds.
    let selection = this.getSelection();
    let min = Math.max(Math.min(selection.start, selection.end), 0);
    let max = Math.min(Math.max(selection.start, selection.end), this._width);
    min = map(min, 0, this._width, startTime, endTime);
    max = map(max, 0, this._width, startTime, endTime);

    return { min: min, max: max };
  },

  /**
   * Removes the selection.
   */
  dropSelection: function () {
    if (!this.hasSelection() && !this.hasSelectionInProgress()) {
      return;
    }
    this._selection.start = null;
    this._selection.end = null;
    this._shouldRedraw = true;
    this.emit("deselecting");
  },

  /**
   * Gets whether or not this graph has a selection.
   * @return boolean
   */
  hasSelection: function () {
    return this._selection &&
      this._selection.start != null && this._selection.end != null;
  },

  /**
   * Gets whether or not a selection is currently being made, for example
   * via a click+drag operation.
   * @return boolean
   */
  hasSelectionInProgress: function () {
    return this._selection &&
      this._selection.start != null && this._selection.end == null;
  },

  /**
   * Specifies whether or not mouse selection is allowed.
   * @type boolean
   */
  selectionEnabled: true,

  /**
   * Sets the selection bounds.
   * Use `dropCursor` to hide the cursor.
   *
   * @param object cursor
   *        The cursor's { x, y } position.
   */
  setCursor: function (cursor) {
    if (!cursor || cursor.x == null || cursor.y == null) {
      throw new Error("Invalid cursor coordinates");
    }
    if (!this.isCursorDifferent(cursor)) {
      return;
    }
    this._cursor.x = cursor.x;
    this._cursor.y = cursor.y;
    this._shouldRedraw = true;
  },

  /**
   * Gets the cursor position.
   * If there's no cursor, the position has null values.
   *
   * @return object
   *         The cursor's { x, y } values.
   */
  getCursor: function () {
    return { x: this._cursor.x, y: this._cursor.y };
  },

  /**
   * Hides the cursor.
   */
  dropCursor: function () {
    if (!this.hasCursor()) {
      return;
    }
    this._cursor.x = null;
    this._cursor.y = null;
    this._shouldRedraw = true;
  },

  /**
   * Gets whether or not this graph has a visible cursor.
   * @return boolean
   */
  hasCursor: function () {
    return this._cursor && this._cursor.x != null;
  },

  /**
   * Specifies if this graph's selection is different from another one.
   *
   * @param object other
   *        The other graph's selection, as { start, end } values.
   */
  isSelectionDifferent: function (other) {
    if (!other) {
      return true;
    }
    let current = this.getSelection();
    return current.start != other.start || current.end != other.end;
  },

  /**
   * Specifies if this graph's cursor is different from another one.
   *
   * @param object other
   *        The other graph's position, as { x, y } values.
   */
  isCursorDifferent: function (other) {
    if (!other) {
      return true;
    }
    let current = this.getCursor();
    return current.x != other.x || current.y != other.y;
  },

  /**
   * Gets the width of the current selection.
   * If no selection is available, 0 is returned.
   *
   * @return number
   *         The selection width.
   */
  getSelectionWidth: function () {
    let selection = this.getSelection();
    return Math.abs(selection.start - selection.end);
  },

  /**
   * Gets the currently hovered region, if any.
   * If no region is currently hovered, null is returned.
   *
   * @return object
   *         The hovered region, as { start, end } values.
   */
  getHoveredRegion: function () {
    if (!this.hasRegions() || !this.hasCursor()) {
      return null;
    }
    let { x } = this._cursor;
    return this._regions.find(({ start, end }) =>
      (start < end && start < x && end > x) ||
      (start > end && end < x && start > x));
  },

  /**
   * Updates this graph to reflect the new dimensions of the parent node.
   *
   * @param boolean options.force
   *        Force redrawing everything
   */
  refresh: function (options = {}) {
    let bounds = this._parent.getBoundingClientRect();
    let newWidth = this.fixedWidth || bounds.width;
    let newHeight = this.fixedHeight || bounds.height;

    // Prevent redrawing everything if the graph's width & height won't change,
    // except if force=true.
    if (!options.force &&
        this._width == newWidth * this._pixelRatio &&
        this._height == newHeight * this._pixelRatio) {
      this.emit("refresh-cancelled");
      return;
    }

    // Handle a changed size by mapping the old selection to the new width
    if (this._width && newWidth && this.hasSelection()) {
      let ratio = this._width / (newWidth * this._pixelRatio);
      this._selection.start = Math.round(this._selection.start / ratio);
      this._selection.end = Math.round(this._selection.end / ratio);
    }

    bounds.width = newWidth;
    bounds.height = newHeight;
    this._iframe.setAttribute("width", bounds.width);
    this._iframe.setAttribute("height", bounds.height);
    this._width = this._canvas.width = bounds.width * this._pixelRatio;
    this._height = this._canvas.height = bounds.height * this._pixelRatio;

    if (this.hasData()) {
      this._cachedBackgroundImage = this.buildBackgroundImage();
      this._cachedGraphImage = this.buildGraphImage();
    }
    if (this.hasMask()) {
      this._cachedMaskImage = this.buildMaskImage.apply(this, this._maskArgs);
    }
    if (this.hasRegions()) {
      this._bakeRegions(this._regions, this._cachedGraphImage);
    }

    this._shouldRedraw = true;
    this.emit("refresh");
  },

  /**
   * Gets a canvas with the specified name, for this graph.
   *
   * If it doesn't exist yet, it will be created, otherwise the cached instance
   * will be cleared and returned.
   *
   * @param string name
   *        The canvas name.
   * @param number width, height [optional]
   *        A custom width and height for the canvas. Defaults to this graph's
   *        container canvas width and height.
   */
  _getNamedCanvas: function (name, width = this._width, height = this._height) {
    let cachedRenderTarget = this._renderTargets.get(name);
    if (cachedRenderTarget) {
      let { canvas, ctx } = cachedRenderTarget;
      canvas.width = width;
      canvas.height = height;
      ctx.clearRect(0, 0, width, height);
      return cachedRenderTarget;
    }

    let canvas = this._document.createElementNS(HTML_NS, "canvas");
    let ctx = canvas.getContext("2d");
    canvas.width = width;
    canvas.height = height;

    let renderTarget = { canvas: canvas, ctx: ctx };
    this._renderTargets.set(name, renderTarget);
    return renderTarget;
  },

  /**
   * The contents of this graph are redrawn only when something changed,
   * like the data source, or the selection bounds etc. This flag tracks
   * if the rendering is "dirty" and needs to be refreshed.
   */
  _shouldRedraw: false,

  /**
   * Animation frame callback, invoked on each tick of the refresh driver.
   */
  _onAnimationFrame: function () {
    this._animationId =
      this._window.requestAnimationFrame(this._onAnimationFrame);
    this._drawWidget();
  },

  /**
   * Redraws the widget when necessary. The actual graph is not refreshed
   * every time this function is called, only the cliphead, selection etc.
   */
  _drawWidget: function () {
    if (!this._shouldRedraw) {
      return;
    }
    let ctx = this._ctx;
    ctx.clearRect(0, 0, this._width, this._height);

    if (this._cachedGraphImage) {
      ctx.drawImage(this._cachedGraphImage, 0, 0, this._width, this._height);
    }
    if (this._cachedMaskImage) {
      ctx.globalCompositeOperation = "destination-out";
      ctx.drawImage(this._cachedMaskImage, 0, 0, this._width, this._height);
    }
    if (this._cachedBackgroundImage) {
      ctx.globalCompositeOperation = "destination-over";
      ctx.drawImage(this._cachedBackgroundImage, 0, 0,
                    this._width, this._height);
    }

    // Revert to the original global composition operation.
    if (this._cachedMaskImage || this._cachedBackgroundImage) {
      ctx.globalCompositeOperation = "source-over";
    }

    if (this.hasCursor()) {
      this._drawCliphead();
    }
    if (this.hasSelection() || this.hasSelectionInProgress()) {
      this._drawSelection();
    }

    this._shouldRedraw = false;
  },

  /**
   * Draws the cliphead, if available and necessary.
   */
  _drawCliphead: function () {
    if (this._isHoveringSelectionContentsOrBoundaries() ||
        this._isHoveringRegion()) {
      return;
    }

    let ctx = this._ctx;
    ctx.lineWidth = this.clipheadLineWidth;
    ctx.strokeStyle = this.clipheadLineColor;
    ctx.beginPath();
    ctx.moveTo(this._cursor.x, 0);
    ctx.lineTo(this._cursor.x, this._height);
    ctx.stroke();
  },

  /**
   * Draws the selection, if available and necessary.
   */
  _drawSelection: function () {
    let { start, end } = this.getSelection();
    let input = this._canvas.getAttribute("input");

    let ctx = this._ctx;
    ctx.strokeStyle = this.selectionLineColor;

    // Fill selection.

    let pattern = AbstractCanvasGraph.getStripePattern({
      ownerDocument: this._document,
      backgroundColor: this.selectionBackgroundColor,
      stripesColor: this.selectionStripesColor
    });
    ctx.fillStyle = pattern;
    let rectStart = Math.min(this._width, Math.max(0, start));
    let rectEnd = Math.min(this._width, Math.max(0, end));
    ctx.fillRect(rectStart, 0, rectEnd - rectStart, this._height);

    // Draw left boundary.

    if (input == "hovering-selection-start-boundary") {
      ctx.lineWidth = GRAPH_SELECTION_BOUNDARY_HOVER_LINE_WIDTH;
    } else {
      ctx.lineWidth = this.clipheadLineWidth;
    }
    ctx.beginPath();
    ctx.moveTo(start, 0);
    ctx.lineTo(start, this._height);
    ctx.stroke();

    // Draw right boundary.

    if (input == "hovering-selection-end-boundary") {
      ctx.lineWidth = GRAPH_SELECTION_BOUNDARY_HOVER_LINE_WIDTH;
    } else {
      ctx.lineWidth = this.clipheadLineWidth;
    }
    ctx.beginPath();
    ctx.moveTo(end, this._height);
    ctx.lineTo(end, 0);
    ctx.stroke();
  },

  /**
   * Draws regions into the cached graph image, created via `buildGraphImage`.
   * Called when new regions are set.
   */
  _bakeRegions: function (regions, destination) {
    let ctx = destination.getContext("2d");

    let pattern = AbstractCanvasGraph.getStripePattern({
      ownerDocument: this._document,
      backgroundColor: this.regionBackgroundColor,
      stripesColor: this.regionStripesColor
    });
    ctx.fillStyle = pattern;
    ctx.strokeStyle = GRAPH_REGION_LINE_COLOR;
    ctx.lineWidth = GRAPH_REGION_LINE_WIDTH;

    let y = -GRAPH_REGION_LINE_WIDTH;
    let height = this._height + GRAPH_REGION_LINE_WIDTH;

    for (let { start, end } of regions) {
      let x = start;
      let width = end - start;
      ctx.fillRect(x, y, width, height);
      ctx.strokeRect(x, y, width, height);
    }
  },

  /**
   * Checks whether the start handle of the selection is hovered.
   * @return boolean
   */
  _isHoveringStartBoundary: function () {
    if (!this.hasSelection() || !this.hasCursor()) {
      return false;
    }
    let { x } = this._cursor;
    let { start } = this._selection;
    let threshold = GRAPH_SELECTION_BOUNDARY_HOVER_THRESHOLD * this._pixelRatio;
    return Math.abs(start - x) < threshold;
  },

  /**
   * Checks whether the end handle of the selection is hovered.
   * @return boolean
   */
  _isHoveringEndBoundary: function () {
    if (!this.hasSelection() || !this.hasCursor()) {
      return false;
    }
    let { x } = this._cursor;
    let { end } = this._selection;
    let threshold = GRAPH_SELECTION_BOUNDARY_HOVER_THRESHOLD * this._pixelRatio;
    return Math.abs(end - x) < threshold;
  },

  /**
   * Checks whether the selection is hovered.
   * @return boolean
   */
  _isHoveringSelectionContents: function () {
    if (!this.hasSelection() || !this.hasCursor()) {
      return false;
    }
    let { x } = this._cursor;
    let { start, end } = this._selection;
    return (start < end && start < x && end > x) ||
           (start > end && end < x && start > x);
  },

  /**
   * Checks whether the selection or its handles are hovered.
   * @return boolean
   */
  _isHoveringSelectionContentsOrBoundaries: function () {
    return this._isHoveringSelectionContents() ||
           this._isHoveringStartBoundary() ||
           this._isHoveringEndBoundary();
  },

  /**
   * Checks whether a region is hovered.
   * @return boolean
   */
  _isHoveringRegion: function () {
    return !!this.getHoveredRegion();
  },

  /**
   * Given a MouseEvent, make it relative to this._canvas.
   * @return object {mouseX,mouseY}
   */
  _getRelativeEventCoordinates: function (e) {
    // For ease of testing, testX and testY can be passed in as the event
    // object.  If so, just return this.
    if ("testX" in e && "testY" in e) {
      return {
        mouseX: e.testX * this._pixelRatio,
        mouseY: e.testY * this._pixelRatio
      };
    }

    // This method is concerned with converting mouse event coordinates from
    // "screen space" to "local space" (in other words, relative to this
    // canvas's position, thus (0,0) would correspond to the upper left corner).
    // We can't simply use `clientX` and `clientY` because the given MouseEvent
    // object may be generated from events coming from other DOM nodes.
    // Therefore, we need to get a bounding box relative to the top document and
    // do some simple math to convert screen coords into local coords.
    // However, `getBoxQuads` may be a very costly operation depending on the
    // complexity of the "outside world" DOM, so cache the results until we
    // suspect they might change (e.g. on a resize).
    // It'd sure be nice if we could use `getBoundsWithoutFlushing`, but it's
    // not taking the document zoom factor into consideration consistently.
    if (!this._boundingBox || this._maybeDirtyBoundingBox) {
      let topDocument = this._topWindow.document;
      let boxQuad = this._canvas.getBoxQuads({ relativeTo: topDocument })[0];
      this._boundingBox = boxQuad;
      this._maybeDirtyBoundingBox = false;
    }

    let bb = this._boundingBox;
    let x = (e.screenX - this._topWindow.screenX) - bb.p1.x;
    let y = (e.screenY - this._topWindow.screenY) - bb.p1.y;

    // Don't allow the event coordinates to be bigger than the canvas
    // or less than 0.
    let maxX = bb.p2.x - bb.p1.x;
    let maxY = bb.p3.y - bb.p1.y;
    let mouseX = Math.max(0, Math.min(x, maxX)) * this._pixelRatio;
    let mouseY = Math.max(0, Math.min(y, maxY)) * this._pixelRatio;

    // The coordinates need to be modified with the current zoom level
    // to prevent them from being wrong.
    let zoom = getCurrentZoom(this._canvas);
    mouseX /= zoom;
    mouseY /= zoom;

    return {mouseX, mouseY};
  },

  /**
   * Listener for the "mousemove" event on the graph's container.
   */
  _onMouseMove: function (e) {
    let resizer = this._selectionResizer;
    let dragger = this._selectionDragger;

    // Need to stop propagation here, since this function can be bound
    // to both this._window and this._topWindow.  It's only attached to
    // this._topWindow during a drag event.  Null check here since tests
    // don't pass this method into the event object.
    if (e.stopPropagation && this._isMouseActive) {
      e.stopPropagation();
    }

    // If a mouseup happened outside the window and the current operation
    // is causing the selection to change, then end it.
    if (e.buttons == 0 && (this.hasSelectionInProgress() ||
                           resizer.margin != null ||
                           dragger.origin != null)) {
      this._onMouseUp();
      return;
    }

    let {mouseX, mouseY} = this._getRelativeEventCoordinates(e);
    this._cursor.x = mouseX;
    this._cursor.y = mouseY;

    if (resizer.margin != null) {
      this._selection[resizer.margin] = mouseX;
      this._shouldRedraw = true;
      this.emit("selecting");
      return;
    }

    if (dragger.origin != null) {
      this._selection.start = dragger.anchor.start - dragger.origin + mouseX;
      this._selection.end = dragger.anchor.end - dragger.origin + mouseX;
      this._shouldRedraw = true;
      this.emit("selecting");
      return;
    }

    if (this.hasSelectionInProgress()) {
      this._shouldRedraw = true;
      this.emit("selecting");
      return;
    }

    if (this.hasSelection()) {
      if (this._isHoveringStartBoundary()) {
        this._canvas.setAttribute("input", "hovering-selection-start-boundary");
        this._shouldRedraw = true;
        return;
      }
      if (this._isHoveringEndBoundary()) {
        this._canvas.setAttribute("input", "hovering-selection-end-boundary");
        this._shouldRedraw = true;
        return;
      }
      if (this._isHoveringSelectionContents()) {
        this._canvas.setAttribute("input", "hovering-selection-contents");
        this._shouldRedraw = true;
        return;
      }
    }

    let region = this.getHoveredRegion();
    if (region) {
      this._canvas.setAttribute("input", "hovering-region");
    } else {
      this._canvas.setAttribute("input", "hovering-background");
    }

    this._shouldRedraw = true;
  },

  /**
   * Listener for the "mousedown" event on the graph's container.
   */
  _onMouseDown: function (e) {
    this._isMouseActive = true;
    let {mouseX} = this._getRelativeEventCoordinates(e);

    switch (this._canvas.getAttribute("input")) {
      case "hovering-background":
      case "hovering-region":
        if (!this.selectionEnabled) {
          break;
        }
        this._selection.start = mouseX;
        this._selection.end = null;
        this.emit("selecting");
        break;

      case "hovering-selection-start-boundary":
        this._selectionResizer.margin = "start";
        break;

      case "hovering-selection-end-boundary":
        this._selectionResizer.margin = "end";
        break;

      case "hovering-selection-contents":
        this._selectionDragger.origin = mouseX;
        this._selectionDragger.anchor.start = this._selection.start;
        this._selectionDragger.anchor.end = this._selection.end;
        this._canvas.setAttribute("input", "dragging-selection-contents");
        break;
    }

    // During a drag, bind to the top level window so that mouse movement
    // outside of this frame will still work.
    this._topWindow.addEventListener("mousemove", this._onMouseMove);
    this._topWindow.addEventListener("mouseup", this._onMouseUp);

    this._shouldRedraw = true;
    this.emit("mousedown");
  },

  /**
   * Listener for the "mouseup" event on the graph's container.
   */
  _onMouseUp: function () {
    this._isMouseActive = false;
    switch (this._canvas.getAttribute("input")) {
      case "hovering-background":
      case "hovering-region":
        if (!this.selectionEnabled) {
          break;
        }
        if (this.getSelectionWidth() < 1) {
          let region = this.getHoveredRegion();
          if (region) {
            this._selection.start = region.start;
            this._selection.end = region.end;
            this.emit("selecting");
          } else {
            this._selection.start = null;
            this._selection.end = null;
            this.emit("deselecting");
          }
        } else {
          this._selection.end = this._cursor.x;
          this.emit("selecting");
        }
        break;

      case "hovering-selection-start-boundary":
      case "hovering-selection-end-boundary":
        this._selectionResizer.margin = null;
        break;

      case "dragging-selection-contents":
        this._selectionDragger.origin = null;
        this._canvas.setAttribute("input", "hovering-selection-contents");
        break;
    }

    // No longer dragging, no need to bind to the top level window.
    this._topWindow.removeEventListener("mousemove", this._onMouseMove);
    this._topWindow.removeEventListener("mouseup", this._onMouseUp);

    this._shouldRedraw = true;
    this.emit("mouseup");
  },

  /**
   * Listener for the "wheel" event on the graph's container.
   */
  _onMouseWheel: function (e) {
    if (!this.hasSelection()) {
      return;
    }

    let {mouseX} = this._getRelativeEventCoordinates(e);
    let focusX = mouseX;

    let selection = this._selection;
    let vector = 0;

    // If the selection is hovered, "zoom" towards or away the cursor,
    // by shrinking or growing the selection.
    if (this._isHoveringSelectionContentsOrBoundaries()) {
      let distStart = selection.start - focusX;
      let distEnd = selection.end - focusX;
      vector = e.detail * GRAPH_WHEEL_ZOOM_SENSITIVITY;
      selection.start = selection.start + distStart * vector;
      selection.end = selection.end + distEnd * vector;
    } else {
      // Otherwise, simply pan the selection towards the left or right.
      let direction = 0;
      if (focusX > selection.end) {
        direction = Math.sign(focusX - selection.end);
      } else if (focusX < selection.start) {
        direction = Math.sign(focusX - selection.start);
      }
      vector = direction * e.detail * GRAPH_WHEEL_SCROLL_SENSITIVITY;
      selection.start -= vector;
      selection.end -= vector;
    }

    // Make sure the selection bounds are still comfortably inside the
    // graph's bounds when zooming out, to keep the margin handles accessible.

    let minStart = GRAPH_MAX_SELECTION_LEFT_PADDING;
    let maxEnd = this._width - GRAPH_MAX_SELECTION_RIGHT_PADDING;
    if (selection.start < minStart) {
      selection.start = minStart;
    }
    if (selection.start > maxEnd) {
      selection.start = maxEnd;
    }
    if (selection.end < minStart) {
      selection.end = minStart;
    }
    if (selection.end > maxEnd) {
      selection.end = maxEnd;
    }

    // Make sure the selection doesn't get too narrow when zooming in.

    let thickness = Math.abs(selection.start - selection.end);
    if (thickness < GRAPH_WHEEL_MIN_SELECTION_WIDTH) {
      let midPoint = (selection.start + selection.end) / 2;
      selection.start = midPoint - GRAPH_WHEEL_MIN_SELECTION_WIDTH / 2;
      selection.end = midPoint + GRAPH_WHEEL_MIN_SELECTION_WIDTH / 2;
    }

    this._shouldRedraw = true;
    this.emit("selecting");
    this.emit("scroll");
  },

  /**
   * Listener for the "mouseout" event on the graph's container.
   * Clear any active cursors if a drag isn't happening.
   */
  _onMouseOut: function (e) {
    if (!this._isMouseActive) {
      this._cursor.x = null;
      this._cursor.y = null;
      this._canvas.removeAttribute("input");
      this._shouldRedraw = true;
    }
  },

  /**
   * Listener for the "resize" event on the graph's parent node.
   */
  _onResize: function () {
    if (this.hasData()) {
      // The assumption is that resize events may change the outside world
      // layout in a way that affects this graph's bounding box location
      // relative to the top window's document. Graphs aren't currently
      // (or ever) expected to move around on their own.
      this._maybeDirtyBoundingBox = true;
      setNamedTimeout(this._uid, GRAPH_RESIZE_EVENTS_DRAIN, this.refresh);
    }
  }
};

// Helper functions.

/**
 * Creates an iframe element with the provided source URL, appends it to
 * the specified node and invokes the callback once the content is loaded.
 *
 * @param string url
 *        The desired source URL for the iframe.
 * @param nsIDOMNode parent
 *        The desired parent node for the iframe.
 * @param function callback
 *        Invoked once the content is loaded, with the iframe as an argument.
 */
AbstractCanvasGraph.createIframe = function (url, parent, callback) {
  let iframe = parent.ownerDocument.createElementNS(HTML_NS, "iframe");

  iframe.addEventListener("DOMContentLoaded", function () {
    callback(iframe);
  }, {once: true});

  // Setting 100% width on the frame and flex on the parent allows the graph
  // to properly shrink when the window is resized to be smaller.
  iframe.setAttribute("frameborder", "0");
  iframe.style.width = "100%";
  iframe.style.minWidth = "50px";
  iframe.src = url;

  parent.style.display = "flex";
  parent.appendChild(iframe);
};

/**
 * Gets a striped pattern used as a background in selections and regions.
 *
 * @param object data
 *        The following properties are required:
 *          - ownerDocument: the nsIDocumentElement owning the canvas
 *          - backgroundColor: a string representing the fill style
 *          - stripesColor: a string representing the stroke style
 * @return nsIDOMCanvasPattern
 *         The custom striped pattern.
 */
AbstractCanvasGraph.getStripePattern = function (data) {
  let { ownerDocument, backgroundColor, stripesColor } = data;
  let id = [backgroundColor, stripesColor].join(",");

  if (gCachedStripePattern.has(id)) {
    return gCachedStripePattern.get(id);
  }

  let canvas = ownerDocument.createElementNS(HTML_NS, "canvas");
  let ctx = canvas.getContext("2d");
  let width = canvas.width = GRAPH_STRIPE_PATTERN_WIDTH;
  let height = canvas.height = GRAPH_STRIPE_PATTERN_HEIGHT;

  ctx.fillStyle = backgroundColor;
  ctx.fillRect(0, 0, width, height);

  let pixelRatio = ownerDocument.defaultView.devicePixelRatio;
  let scaledLineWidth = GRAPH_STRIPE_PATTERN_LINE_WIDTH * pixelRatio;
  let scaledLineSpacing = GRAPH_STRIPE_PATTERN_LINE_SPACING * pixelRatio;

  ctx.strokeStyle = stripesColor;
  ctx.lineWidth = scaledLineWidth;
  ctx.lineCap = "square";
  ctx.beginPath();

  for (let i = -height; i <= height; i += scaledLineSpacing) {
    ctx.moveTo(width, i);
    ctx.lineTo(0, i + height);
  }

  ctx.stroke();

  let pattern = ctx.createPattern(canvas, "repeat");
  gCachedStripePattern.set(id, pattern);
  return pattern;
};

/**
 * Cache used by `AbstractCanvasGraph.getStripePattern`.
 */
const gCachedStripePattern = new Map();

/**
 * Utility functions for graph canvases.
 */
this.CanvasGraphUtils = {
  _graphUtilsWorker: null,
  _graphUtilsTaskId: 0,

  /**
   * Merges the animation loop of two graphs.
   */
  linkAnimation: Task.async(function* (graph1, graph2) {
    if (!graph1 || !graph2) {
      return;
    }
    yield graph1.ready();
    yield graph2.ready();

    let window = graph1._window;
    window.cancelAnimationFrame(graph1._animationId);
    window.cancelAnimationFrame(graph2._animationId);

    let loop = () => {
      window.requestAnimationFrame(loop);
      graph1._drawWidget();
      graph2._drawWidget();
    };

    window.requestAnimationFrame(loop);
  }),

  /**
   * Makes sure selections in one graph are reflected in another.
   */
  linkSelection: function (graph1, graph2) {
    if (!graph1 || !graph2) {
      return;
    }

    if (graph1.hasSelection()) {
      graph2.setSelection(graph1.getSelection());
    } else {
      graph2.dropSelection();
    }

    graph1.on("selecting", () => {
      graph2.setSelection(graph1.getSelection());
    });
    graph2.on("selecting", () => {
      graph1.setSelection(graph2.getSelection());
    });
    graph1.on("deselecting", () => {
      graph2.dropSelection();
    });
    graph2.on("deselecting", () => {
      graph1.dropSelection();
    });
  },

  /**
   * Performs the given task in a chrome worker, assuming it exists.
   *
   * @param string task
   *        The task name. Currently supported: "plotTimestampsGraph".
   * @param any data
   *        Extra arguments to pass to the worker.
   * @return object
   *         A promise that is resolved once the worker finishes the task.
   */
  _performTaskInWorker: function (task, data) {
    let worker = this._graphUtilsWorker || new DevToolsWorker(WORKER_URL);
    return worker.performTask(task, data);
  }
};

/**
 * Maps a value from one range to another.
 * @param number value, istart, istop, ostart, ostop
 * @return number
 */
function map(value, istart, istop, ostart, ostop) {
  let ratio = istop - istart;
  if (ratio == 0) {
    return value;
  }
  return ostart + (ostop - ostart) * ((value - istart) / ratio);
}

/**
 * Constrains a value to a range.
 * @param number value, min, max
 * @return number
 */
function clamp(value, min, max) {
  if (value < min) {
    return min;
  }
  if (value > max) {
    return max;
  }
  return value;
}

exports.GraphCursor = GraphCursor;
exports.GraphArea = GraphArea;
exports.GraphAreaDragger = GraphAreaDragger;
exports.GraphAreaResizer = GraphAreaResizer;
exports.AbstractCanvasGraph = AbstractCanvasGraph;
exports.CanvasGraphUtils = CanvasGraphUtils;
exports.CanvasGraphUtils.map = map;
exports.CanvasGraphUtils.clamp = clamp;
PK
!<c |��Fchrome/devtools/modules/devtools/client/shared/widgets/GraphsWorker.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";

/* eslint-env worker */

/**
 * Import `createTask` to communicate with `devtools/shared/worker`.
 */
importScripts("resource://gre/modules/workers/require.js");
const { createTask } = require("resource://devtools/shared/worker/helper.js");

/**
 * @see LineGraphWidget.prototype.setDataFromTimestamps in Graphs.js
 * @param number id
 * @param array timestamps
 * @param number interval
 * @param number duration
 */
createTask(self, "plotTimestampsGraph", function ({ timestamps,
                                                    interval, duration }) {
  let plottedData = plotTimestamps(timestamps, interval);
  let plottedMinMaxSum = getMinMaxAvg(plottedData, timestamps, duration);

  return { plottedData, plottedMinMaxSum };
});

/**
 * Gets the min, max and average of the values in an array.
 * @param array source
 * @param array timestamps
 * @param number duration
 * @return object
 */
function getMinMaxAvg(source, timestamps, duration) {
  let totalFrames = timestamps.length;
  let maxValue = Number.MIN_SAFE_INTEGER;
  let minValue = Number.MAX_SAFE_INTEGER;
  // Calculate the average by counting how many frames occurred
  // in the duration of the recording, rather than average the frame points
  // we have, as that weights higher FPS, as there'll be more timestamps for
  // those values
  let avgValue = totalFrames / (duration / 1000);

  for (let { value } of source) {
    maxValue = Math.max(value, maxValue);
    minValue = Math.min(value, minValue);
  }

  return { minValue, maxValue, avgValue };
}

/**
 * Takes a list of numbers and plots them on a line graph representing
 * the rate of occurences in a specified interval.
 *
 * @param array timestamps
 *        A list of numbers representing time, ordered ascending. For example,
 *        this can be the raw data received from the framerate actor, which
 *        represents the elapsed time on each refresh driver tick.
 * @param number interval
 *        The maximum amount of time to wait between calculations.
 * @param number clamp
 *        The maximum allowed value.
 * @return array
 *         A collection of { delta, value } objects representing the
 *         plotted value at every delta time.
 */
function plotTimestamps(timestamps, interval = 100, clamp = 60) {
  let timeline = [];
  let totalTicks = timestamps.length;

  // If the refresh driver didn't get a chance to tick before the
  // recording was stopped, assume rate was 0.
  if (totalTicks == 0) {
    timeline.push({ delta: 0, value: 0 });
    timeline.push({ delta: interval, value: 0 });
    return timeline;
  }

  let frameCount = 0;
  let prevTime = +timestamps[0];

  for (let i = 1; i < totalTicks; i++) {
    let currTime = +timestamps[i];
    frameCount++;

    let elapsedTime = currTime - prevTime;
    if (elapsedTime < interval) {
      continue;
    }

    let rate = Math.min(1000 / (elapsedTime / frameCount), clamp);
    timeline.push({ delta: prevTime, value: rate });
    timeline.push({ delta: currTime, value: rate });

    frameCount = 0;
    prevTime = currTime;
  }

  return timeline;
}
PK
!<8��77Ichrome/devtools/modules/devtools/client/shared/widgets/LineGraphWidget.js"use strict";

const { Task } = require("devtools/shared/task");
const { extend } = require("devtools/shared/extend");
const { AbstractCanvasGraph, CanvasGraphUtils } = require("devtools/client/shared/widgets/Graphs");
const { LocalizationHelper } = require("devtools/shared/l10n");

const HTML_NS = "http://www.w3.org/1999/xhtml";
const L10N = new LocalizationHelper("devtools/client/locales/graphs.properties");

// Line graph constants.

const GRAPH_DAMPEN_VALUES_FACTOR = 0.85;
const GRAPH_TOOLTIP_SAFE_BOUNDS = 8; // px
const GRAPH_MIN_MAX_TOOLTIP_DISTANCE = 14; // px

const GRAPH_BACKGROUND_COLOR = "#0088cc";
const GRAPH_STROKE_WIDTH = 1; // px
const GRAPH_STROKE_COLOR = "rgba(255,255,255,0.9)";
const GRAPH_HELPER_LINES_DASH = [5]; // px
const GRAPH_HELPER_LINES_WIDTH = 1; // px
const GRAPH_MAXIMUM_LINE_COLOR = "rgba(255,255,255,0.4)";
const GRAPH_AVERAGE_LINE_COLOR = "rgba(255,255,255,0.7)";
const GRAPH_MINIMUM_LINE_COLOR = "rgba(255,255,255,0.9)";
const GRAPH_BACKGROUND_GRADIENT_START = "rgba(255,255,255,0.25)";
const GRAPH_BACKGROUND_GRADIENT_END = "rgba(255,255,255,0.0)";

const GRAPH_CLIPHEAD_LINE_COLOR = "#fff";
const GRAPH_SELECTION_LINE_COLOR = "#fff";
const GRAPH_SELECTION_BACKGROUND_COLOR = "rgba(44,187,15,0.25)";
const GRAPH_SELECTION_STRIPES_COLOR = "rgba(255,255,255,0.1)";
const GRAPH_REGION_BACKGROUND_COLOR = "transparent";
const GRAPH_REGION_STRIPES_COLOR = "rgba(237,38,85,0.2)";

/**
 * A basic line graph, plotting values on a curve and adding helper lines
 * and tooltips for maximum, average and minimum values.
 *
 * @see AbstractCanvasGraph for emitted events and other options.
 *
 * Example usage:
 *   let graph = new LineGraphWidget(node, "units");
 *   graph.once("ready", () => {
 *     graph.setData(src);
 *   });
 *
 * Data source format:
 *   [
 *     { delta: x1, value: y1 },
 *     { delta: x2, value: y2 },
 *     ...
 *     { delta: xn, value: yn }
 *   ]
 * where each item in the array represents a point in the graph.
 *
 * @param nsIDOMNode parent
 *        The parent node holding the graph.
 * @param object options [optional]
 *  `metric`: The metric displayed in the graph, e.g. "fps" or "bananas".
 *  `min`: Boolean whether to show the min tooltip/gutter/line (default: true)
 *  `max`: Boolean whether to show the max tooltip/gutter/line (default: true)
 *  `avg`: Boolean whether to show the avg tooltip/gutter/line (default: true)
 */
this.LineGraphWidget = function (parent, options = {}, ...args) {
  let { metric, min, max, avg } = options;

  this._showMin = min !== false;
  this._showMax = max !== false;
  this._showAvg = avg !== false;

  AbstractCanvasGraph.apply(this, [parent, "line-graph", ...args]);

  this.once("ready", () => {
    // Create all gutters and tooltips incase the showing of min/max/avg
    // are changed later
    this._gutter = this._createGutter();
    this._maxGutterLine = this._createGutterLine("maximum");
    this._maxTooltip = this._createTooltip(
      "maximum", "start", L10N.getStr("graphs.label.maximum"), metric
    );
    this._minGutterLine = this._createGutterLine("minimum");
    this._minTooltip = this._createTooltip(
      "minimum", "start", L10N.getStr("graphs.label.minimum"), metric
    );
    this._avgGutterLine = this._createGutterLine("average");
    this._avgTooltip = this._createTooltip(
      "average", "end", L10N.getStr("graphs.label.average"), metric
    );
  });
};

LineGraphWidget.prototype = extend(AbstractCanvasGraph.prototype, {
  backgroundColor: GRAPH_BACKGROUND_COLOR,
  backgroundGradientStart: GRAPH_BACKGROUND_GRADIENT_START,
  backgroundGradientEnd: GRAPH_BACKGROUND_GRADIENT_END,
  strokeColor: GRAPH_STROKE_COLOR,
  strokeWidth: GRAPH_STROKE_WIDTH,
  maximumLineColor: GRAPH_MAXIMUM_LINE_COLOR,
  averageLineColor: GRAPH_AVERAGE_LINE_COLOR,
  minimumLineColor: GRAPH_MINIMUM_LINE_COLOR,
  clipheadLineColor: GRAPH_CLIPHEAD_LINE_COLOR,
  selectionLineColor: GRAPH_SELECTION_LINE_COLOR,
  selectionBackgroundColor: GRAPH_SELECTION_BACKGROUND_COLOR,
  selectionStripesColor: GRAPH_SELECTION_STRIPES_COLOR,
  regionBackgroundColor: GRAPH_REGION_BACKGROUND_COLOR,
  regionStripesColor: GRAPH_REGION_STRIPES_COLOR,

  /**
   * Optionally offsets the `delta` in the data source by this scalar.
   */
  dataOffsetX: 0,

  /**
   * Optionally uses this value instead of the last tick in the data source
   * to compute the horizontal scaling.
   */
  dataDuration: 0,

  /**
   * The scalar used to multiply the graph values to leave some headroom.
   */
  dampenValuesFactor: GRAPH_DAMPEN_VALUES_FACTOR,

  /**
   * Specifies if min/max/avg tooltips have arrow handlers on their sides.
   */
  withTooltipArrows: true,

  /**
   * Specifies if min/max/avg tooltips are positioned based on the actual
   * values, or just placed next to the graph corners.
   */
  withFixedTooltipPositions: false,

  /**
   * Takes a list of numbers and plots them on a line graph representing
   * the rate of occurences in a specified interval. Useful for drawing
   * framerate, for example, from a sequence of timestamps.
   *
   * @param array timestamps
   *        A list of numbers representing time, ordered ascending. For example,
   *        this can be the raw data received from the framerate actor, which
   *        represents the elapsed time on each refresh driver tick.
   * @param number interval
   *        The maximum amount of time to wait between calculations.
   * @param number duration
   *        The duration of the recording in milliseconds.
   */
  setDataFromTimestamps: Task.async(function* (timestamps, interval, duration) {
    let {
      plottedData,
      plottedMinMaxSum
    } = yield CanvasGraphUtils._performTaskInWorker("plotTimestampsGraph", {
      timestamps, interval, duration
    });

    this._tempMinMaxSum = plottedMinMaxSum;
    this.setData(plottedData);
  }),

  /**
   * Renders the graph's data source.
   * @see AbstractCanvasGraph.prototype.buildGraphImage
   */
  buildGraphImage: function () {
    let { canvas, ctx } = this._getNamedCanvas("line-graph-data");
    let width = this._width;
    let height = this._height;

    let totalTicks = this._data.length;
    let firstTick = totalTicks ? this._data[0].delta : 0;
    let lastTick = totalTicks ? this._data[totalTicks - 1].delta : 0;
    let maxValue = Number.MIN_SAFE_INTEGER;
    let minValue = Number.MAX_SAFE_INTEGER;
    let avgValue = 0;

    if (this._tempMinMaxSum) {
      maxValue = this._tempMinMaxSum.maxValue;
      minValue = this._tempMinMaxSum.minValue;
      avgValue = this._tempMinMaxSum.avgValue;
    } else {
      let sumValues = 0;
      for (let { value } of this._data) {
        maxValue = Math.max(value, maxValue);
        minValue = Math.min(value, minValue);
        sumValues += value;
      }
      avgValue = sumValues / totalTicks;
    }

    let duration = this.dataDuration || lastTick;
    let dataScaleX = this.dataScaleX = width / (duration - this.dataOffsetX);
    let dataScaleY =
      this.dataScaleY = height / maxValue * this.dampenValuesFactor;

    // Draw the background.

    ctx.fillStyle = this.backgroundColor;
    ctx.fillRect(0, 0, width, height);

    // Draw the graph.

    let gradient = ctx.createLinearGradient(0, height / 2, 0, height);
    gradient.addColorStop(0, this.backgroundGradientStart);
    gradient.addColorStop(1, this.backgroundGradientEnd);
    ctx.fillStyle = gradient;
    ctx.strokeStyle = this.strokeColor;
    ctx.lineWidth = this.strokeWidth * this._pixelRatio;
    ctx.beginPath();

    for (let { delta, value } of this._data) {
      let currX = (delta - this.dataOffsetX) * dataScaleX;
      let currY = height - value * dataScaleY;

      if (delta == firstTick) {
        ctx.moveTo(-GRAPH_STROKE_WIDTH, height);
        ctx.lineTo(-GRAPH_STROKE_WIDTH, currY);
      }

      ctx.lineTo(currX, currY);

      if (delta == lastTick) {
        ctx.lineTo(width + GRAPH_STROKE_WIDTH, currY);
        ctx.lineTo(width + GRAPH_STROKE_WIDTH, height);
      }
    }

    ctx.fill();
    ctx.stroke();

    this._drawOverlays(ctx, minValue, maxValue, avgValue, dataScaleY);

    return canvas;
  },

  /**
   * Draws the min, max and average horizontal lines, along with their
   * repsective tooltips.
   *
   * @param CanvasRenderingContext2D ctx
   * @param number minValue
   * @param number maxValue
   * @param number avgValue
   * @param number dataScaleY
   */
  _drawOverlays: function (ctx, minValue, maxValue, avgValue, dataScaleY) {
    let width = this._width;
    let height = this._height;
    let totalTicks = this._data.length;

    // Draw the maximum value horizontal line.
    if (this._showMax) {
      ctx.strokeStyle = this.maximumLineColor;
      ctx.lineWidth = GRAPH_HELPER_LINES_WIDTH;
      ctx.setLineDash(GRAPH_HELPER_LINES_DASH);
      ctx.beginPath();
      let maximumY = height - maxValue * dataScaleY;
      ctx.moveTo(0, maximumY);
      ctx.lineTo(width, maximumY);
      ctx.stroke();
    }

    // Draw the average value horizontal line.
    if (this._showAvg) {
      ctx.strokeStyle = this.averageLineColor;
      ctx.lineWidth = GRAPH_HELPER_LINES_WIDTH;
      ctx.setLineDash(GRAPH_HELPER_LINES_DASH);
      ctx.beginPath();
      let averageY = height - avgValue * dataScaleY;
      ctx.moveTo(0, averageY);
      ctx.lineTo(width, averageY);
      ctx.stroke();
    }

    // Draw the minimum value horizontal line.
    if (this._showMin) {
      ctx.strokeStyle = this.minimumLineColor;
      ctx.lineWidth = GRAPH_HELPER_LINES_WIDTH;
      ctx.setLineDash(GRAPH_HELPER_LINES_DASH);
      ctx.beginPath();
      let minimumY = height - minValue * dataScaleY;
      ctx.moveTo(0, minimumY);
      ctx.lineTo(width, minimumY);
      ctx.stroke();
    }

    // Update the tooltips text and gutter lines.

    this._maxTooltip.querySelector("[text=value]").textContent =
      L10N.numberWithDecimals(maxValue, 2);
    this._avgTooltip.querySelector("[text=value]").textContent =
      L10N.numberWithDecimals(avgValue, 2);
    this._minTooltip.querySelector("[text=value]").textContent =
      L10N.numberWithDecimals(minValue, 2);

    let bottom = height / this._pixelRatio;
    let maxPosY = CanvasGraphUtils.map(maxValue * this.dampenValuesFactor, 0,
                                       maxValue, bottom, 0);
    let avgPosY = CanvasGraphUtils.map(avgValue * this.dampenValuesFactor, 0,
                                       maxValue, bottom, 0);
    let minPosY = CanvasGraphUtils.map(minValue * this.dampenValuesFactor, 0,
                                       maxValue, bottom, 0);

    let safeTop = GRAPH_TOOLTIP_SAFE_BOUNDS;
    let safeBottom = bottom - GRAPH_TOOLTIP_SAFE_BOUNDS;

    let maxTooltipTop = (this.withFixedTooltipPositions
      ? safeTop : CanvasGraphUtils.clamp(maxPosY, safeTop, safeBottom));
    let avgTooltipTop = (this.withFixedTooltipPositions
      ? safeTop : CanvasGraphUtils.clamp(avgPosY, safeTop, safeBottom));
    let minTooltipTop = (this.withFixedTooltipPositions
      ? safeBottom : CanvasGraphUtils.clamp(minPosY, safeTop, safeBottom));

    this._maxTooltip.style.top = maxTooltipTop + "px";
    this._avgTooltip.style.top = avgTooltipTop + "px";
    this._minTooltip.style.top = minTooltipTop + "px";

    this._maxGutterLine.style.top = maxPosY + "px";
    this._avgGutterLine.style.top = avgPosY + "px";
    this._minGutterLine.style.top = minPosY + "px";

    this._maxTooltip.setAttribute("with-arrows", this.withTooltipArrows);
    this._avgTooltip.setAttribute("with-arrows", this.withTooltipArrows);
    this._minTooltip.setAttribute("with-arrows", this.withTooltipArrows);

    let distanceMinMax = Math.abs(maxTooltipTop - minTooltipTop);
    this._maxTooltip.hidden = this._showMax === false
                            || !totalTicks
                            || distanceMinMax < GRAPH_MIN_MAX_TOOLTIP_DISTANCE;
    this._avgTooltip.hidden = this._showAvg === false || !totalTicks;
    this._minTooltip.hidden = this._showMin === false || !totalTicks;
    this._gutter.hidden = (this._showMin === false &&
                           this._showAvg === false &&
                           this._showMax === false) || !totalTicks;

    this._maxGutterLine.hidden = this._showMax === false;
    this._avgGutterLine.hidden = this._showAvg === false;
    this._minGutterLine.hidden = this._showMin === false;
  },

  /**
   * Creates the gutter node when constructing this graph.
   * @return nsIDOMNode
   */
  _createGutter: function () {
    let gutter = this._document.createElementNS(HTML_NS, "div");
    gutter.className = "line-graph-widget-gutter";
    gutter.setAttribute("hidden", true);
    this._container.appendChild(gutter);

    return gutter;
  },

  /**
   * Creates the gutter line nodes when constructing this graph.
   * @return nsIDOMNode
   */
  _createGutterLine: function (type) {
    let line = this._document.createElementNS(HTML_NS, "div");
    line.className = "line-graph-widget-gutter-line";
    line.setAttribute("type", type);
    this._gutter.appendChild(line);

    return line;
  },

  /**
   * Creates the tooltip nodes when constructing this graph.
   * @return nsIDOMNode
   */
  _createTooltip: function (type, arrow, info, metric) {
    let tooltip = this._document.createElementNS(HTML_NS, "div");
    tooltip.className = "line-graph-widget-tooltip";
    tooltip.setAttribute("type", type);
    tooltip.setAttribute("arrow", arrow);
    tooltip.setAttribute("hidden", true);

    let infoNode = this._document.createElementNS(HTML_NS, "span");
    infoNode.textContent = info;
    infoNode.setAttribute("text", "info");

    let valueNode = this._document.createElementNS(HTML_NS, "span");
    valueNode.textContent = 0;
    valueNode.setAttribute("text", "value");

    let metricNode = this._document.createElementNS(HTML_NS, "span");
    metricNode.textContent = metric;
    metricNode.setAttribute("text", "metric");

    tooltip.appendChild(infoNode);
    tooltip.appendChild(valueNode);
    tooltip.appendChild(metricNode);
    this._container.appendChild(tooltip);

    return tooltip;
  }
});

module.exports = LineGraphWidget;
PK
!<\���Mchrome/devtools/modules/devtools/client/shared/widgets/MountainGraphWidget.js"use strict";

const { extend } = require("devtools/shared/extend");
const { AbstractCanvasGraph } = require("devtools/client/shared/widgets/Graphs");

// Bar graph constants.

const GRAPH_DAMPEN_VALUES_FACTOR = 0.9;

const GRAPH_BACKGROUND_COLOR = "#ddd";
const GRAPH_STROKE_WIDTH = 1; // px
const GRAPH_STROKE_COLOR = "rgba(255,255,255,0.9)";
const GRAPH_HELPER_LINES_DASH = [5]; // px
const GRAPH_HELPER_LINES_WIDTH = 1; // px

const GRAPH_CLIPHEAD_LINE_COLOR = "#fff";
const GRAPH_SELECTION_LINE_COLOR = "#fff";
const GRAPH_SELECTION_BACKGROUND_COLOR = "rgba(44,187,15,0.25)";
const GRAPH_SELECTION_STRIPES_COLOR = "rgba(255,255,255,0.1)";
const GRAPH_REGION_BACKGROUND_COLOR = "transparent";
const GRAPH_REGION_STRIPES_COLOR = "rgba(237,38,85,0.2)";

/**
 * A mountain graph, plotting sets of values as line graphs.
 *
 * @see AbstractCanvasGraph for emitted events and other options.
 *
 * Example usage:
 *   let graph = new MountainGraphWidget(node);
 *   graph.format = ...;
 *   graph.once("ready", () => {
 *     graph.setData(src);
 *   });
 *
 * The `graph.format` traits are mandatory and will determine how each
 * section of the moutain will be styled:
 *   [
 *     { color: "#f00", ... },
 *     { color: "#0f0", ... },
 *     ...
 *     { color: "#00f", ... }
 *   ]
 *
 * Data source format:
 *   [
 *     { delta: x1, values: [y11, y12, ... y1n] },
 *     { delta: x2, values: [y21, y22, ... y2n] },
 *     ...
 *     { delta: xm, values: [ym1, ym2, ... ymn] }
 *   ]
 * where the [ymn] values is assumed to aready be normalized from [0..1].
 *
 * @param nsIDOMNode parent
 *        The parent node holding the graph.
 */
this.MountainGraphWidget = function (parent, ...args) {
  AbstractCanvasGraph.apply(this, [parent, "mountain-graph", ...args]);
};

MountainGraphWidget.prototype = extend(AbstractCanvasGraph.prototype, {
  backgroundColor: GRAPH_BACKGROUND_COLOR,
  strokeColor: GRAPH_STROKE_COLOR,
  strokeWidth: GRAPH_STROKE_WIDTH,
  clipheadLineColor: GRAPH_CLIPHEAD_LINE_COLOR,
  selectionLineColor: GRAPH_SELECTION_LINE_COLOR,
  selectionBackgroundColor: GRAPH_SELECTION_BACKGROUND_COLOR,
  selectionStripesColor: GRAPH_SELECTION_STRIPES_COLOR,
  regionBackgroundColor: GRAPH_REGION_BACKGROUND_COLOR,
  regionStripesColor: GRAPH_REGION_STRIPES_COLOR,

  /**
   * List of rules used to style each section of the mountain.
   * @see constructor
   * @type array
   */
  format: null,

  /**
   * Optionally offsets the `delta` in the data source by this scalar.
   */
  dataOffsetX: 0,

  /**
   * Optionally uses this value instead of the last tick in the data source
   * to compute the horizontal scaling.
   */
  dataDuration: 0,

  /**
   * The scalar used to multiply the graph values to leave some headroom
   * on the top.
   */
  dampenValuesFactor: GRAPH_DAMPEN_VALUES_FACTOR,

  /**
   * Renders the graph's background.
   * @see AbstractCanvasGraph.prototype.buildBackgroundImage
   */
  buildBackgroundImage: function () {
    let { canvas, ctx } = this._getNamedCanvas("mountain-graph-background");
    let width = this._width;
    let height = this._height;

    ctx.fillStyle = this.backgroundColor;
    ctx.fillRect(0, 0, width, height);

    return canvas;
  },

  /**
   * Renders the graph's data source.
   * @see AbstractCanvasGraph.prototype.buildGraphImage
   */
  buildGraphImage: function () {
    if (!this.format || !this.format.length) {
      throw new Error("The graph format traits are mandatory to style " +
                      "the data source.");
    }
    let { canvas, ctx } = this._getNamedCanvas("mountain-graph-data");
    let width = this._width;
    let height = this._height;

    let totalSections = this.format.length;
    let totalTicks = this._data.length;
    let firstTick = totalTicks ? this._data[0].delta : 0;
    let lastTick = totalTicks ? this._data[totalTicks - 1].delta : 0;

    let duration = this.dataDuration || lastTick;
    let dataScaleX = this.dataScaleX = width / (duration - this.dataOffsetX);
    let dataScaleY = this.dataScaleY = height * this.dampenValuesFactor;

    // Draw the graph.

    let prevHeights = Array.from({ length: totalTicks }).fill(0);

    ctx.globalCompositeOperation = "destination-over";
    ctx.strokeStyle = this.strokeColor;
    ctx.lineWidth = this.strokeWidth * this._pixelRatio;

    for (let section = 0; section < totalSections; section++) {
      ctx.fillStyle = this.format[section].color || "#000";
      ctx.beginPath();

      for (let tick = 0; tick < totalTicks; tick++) {
        let { delta, values } = this._data[tick];
        let currX = (delta - this.dataOffsetX) * dataScaleX;
        let currY = values[section] * dataScaleY;
        let prevY = prevHeights[tick];

        if (delta == firstTick) {
          ctx.moveTo(-GRAPH_STROKE_WIDTH, height);
          ctx.lineTo(-GRAPH_STROKE_WIDTH, height - currY - prevY);
        }

        ctx.lineTo(currX, height - currY - prevY);

        if (delta == lastTick) {
          ctx.lineTo(width + GRAPH_STROKE_WIDTH, height - currY - prevY);
          ctx.lineTo(width + GRAPH_STROKE_WIDTH, height);
        }

        prevHeights[tick] += currY;
      }

      ctx.fill();
      ctx.stroke();
    }

    ctx.globalCompositeOperation = "source-over";
    ctx.lineWidth = GRAPH_HELPER_LINES_WIDTH;
    ctx.setLineDash(GRAPH_HELPER_LINES_DASH);

    // Draw the maximum value horizontal line.

    ctx.beginPath();
    let maximumY = height * this.dampenValuesFactor;
    ctx.moveTo(0, maximumY);
    ctx.lineTo(width, maximumY);
    ctx.stroke();

    // Draw the average value horizontal line.

    ctx.beginPath();
    let averageY = height / 2 * this.dampenValuesFactor;
    ctx.moveTo(0, averageY);
    ctx.lineTo(width, averageY);
    ctx.stroke();

    return canvas;
  }
});

module.exports = MountainGraphWidget;
PK
!<ۼܗ�W�WIchrome/devtools/modules/devtools/client/shared/widgets/SideMenuWidget.jsm/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 SHARED_STRINGS_URI = "devtools/client/locales/shared.properties";

const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
const EventEmitter = require("devtools/shared/event-emitter");
const { LocalizationHelper } = require("devtools/shared/l10n");
const { ViewHelpers } = require("devtools/client/shared/widgets/view-helpers");

this.EXPORTED_SYMBOLS = ["SideMenuWidget"];

/**
 * Localization convenience methods.
 */
var L10N = new LocalizationHelper(SHARED_STRINGS_URI);

/**
 * A simple side menu, with the ability of grouping menu items.
 *
 * Note: this widget should be used in tandem with the WidgetMethods in
 * view-helpers.js.
 *
 * @param nsIDOMNode aNode
 *        The element associated with the widget.
 * @param Object aOptions
 *        - contextMenu: optional element or element ID that serves as a context menu.
 *        - showArrows: specifies if items should display horizontal arrows.
 *        - showItemCheckboxes: specifies if items should display checkboxes.
 *        - showGroupCheckboxes: specifies if groups should display checkboxes.
 */
this.SideMenuWidget = function SideMenuWidget(aNode, aOptions = {}) {
  this.document = aNode.ownerDocument;
  this.window = this.document.defaultView;
  this._parent = aNode;

  let { contextMenu, showArrows, showItemCheckboxes, showGroupCheckboxes } = aOptions;
  this._contextMenu = contextMenu || null;
  this._showArrows = showArrows || false;
  this._showItemCheckboxes = showItemCheckboxes || false;
  this._showGroupCheckboxes = showGroupCheckboxes || false;

  // Create an internal scrollbox container.
  this._list = this.document.createElement("scrollbox");
  this._list.className = "side-menu-widget-container theme-sidebar";
  this._list.setAttribute("flex", "1");
  this._list.setAttribute("orient", "vertical");
  this._list.setAttribute("with-arrows", this._showArrows);
  this._list.setAttribute("with-item-checkboxes", this._showItemCheckboxes);
  this._list.setAttribute("with-group-checkboxes", this._showGroupCheckboxes);
  this._list.setAttribute("tabindex", "0");
  this._list.addEventListener("contextmenu", e => this._showContextMenu(e));
  this._list.addEventListener("keydown", e => this.emit("keyDown", e));
  this._list.addEventListener("mousedown", e => this.emit("mousePress", e));
  this._parent.appendChild(this._list);

  // Menu items can optionally be grouped.
  this._groupsByName = new Map(); // Can't use a WeakMap because keys are strings.
  this._orderedGroupElementsArray = [];
  this._orderedMenuElementsArray = [];
  this._itemsByElement = new Map();

  // This widget emits events that can be handled in a MenuContainer.
  EventEmitter.decorate(this);

  // Delegate some of the associated node's methods to satisfy the interface
  // required by MenuContainer instances.
  ViewHelpers.delegateWidgetAttributeMethods(this, aNode);
  ViewHelpers.delegateWidgetEventMethods(this, aNode);
};

SideMenuWidget.prototype = {
  /**
   * Specifies if groups in this container should be sorted.
   */
  sortedGroups: true,

  /**
   * The comparator used to sort groups.
   */
  groupSortPredicate: (a, b) => a.localeCompare(b),

  /**
   * Inserts an item in this container at the specified index, optionally
   * grouping by name.
   *
   * @param number aIndex
   *        The position in the container intended for this item.
   * @param nsIDOMNode aContents
   *        The node displayed in the container.
   * @param object aAttachment [optional]
   *        Some attached primitive/object. Custom options supported:
   *          - group: a string specifying the group to place this item into
   *          - checkboxState: the checked state of the checkbox, if shown
   *          - checkboxTooltip: the tooltip text for the checkbox, if shown
   * @return nsIDOMNode
   *         The element associated with the displayed item.
   */
  insertItemAt: function (aIndex, aContents, aAttachment = {}) {
    let group = this._getMenuGroupForName(aAttachment.group);
    let item = this._getMenuItemForGroup(group, aContents, aAttachment);
    let element = item.insertSelfAt(aIndex);

    return element;
  },

  /**
   * Checks to see if the list is scrolled all the way to the bottom.
   * Uses getBoundsWithoutFlushing to limit the performance impact
   * of this function.
   *
   * @return bool
   */
  isScrolledToBottom: function () {
    if (this._list.lastElementChild) {
      let utils = this.window.QueryInterface(Ci.nsIInterfaceRequestor)
                             .getInterface(Ci.nsIDOMWindowUtils);
      let childRect = utils.getBoundsWithoutFlushing(this._list.lastElementChild);
      let listRect = utils.getBoundsWithoutFlushing(this._list);

      // Cheap way to check if it's scrolled all the way to the bottom.
      return (childRect.height + childRect.top) <= listRect.bottom;
    }

    return false;
  },

  /**
   * Scroll the list to the bottom after a timeout.
   * If the user scrolls in the meantime, cancel this operation.
   */
  scrollToBottom: function () {
    this._list.scrollTop = this._list.scrollHeight;
    this.emit("scroll-to-bottom");
  },

  /**
   * Returns the child node in this container situated at the specified index.
   *
   * @param number aIndex
   *        The position in the container intended for this item.
   * @return nsIDOMNode
   *         The element associated with the displayed item.
   */
  getItemAtIndex: function (aIndex) {
    return this._orderedMenuElementsArray[aIndex];
  },

  /**
   * Removes the specified child node from this container.
   *
   * @param nsIDOMNode aChild
   *        The element associated with the displayed item.
   */
  removeChild: function (aChild) {
    this._getNodeForContents(aChild).remove();

    this._orderedMenuElementsArray.splice(
      this._orderedMenuElementsArray.indexOf(aChild), 1);

    this._itemsByElement.delete(aChild);

    if (this._selectedItem == aChild) {
      this._selectedItem = null;
    }
  },

  /**
   * Removes all of the child nodes from this container.
   */
  removeAllItems: function () {
    let parent = this._parent;
    let list = this._list;

    while (list.hasChildNodes()) {
      list.firstChild.remove();
    }

    this._selectedItem = null;

    this._groupsByName.clear();
    this._orderedGroupElementsArray.length = 0;
    this._orderedMenuElementsArray.length = 0;
    this._itemsByElement.clear();
  },

  /**
   * Gets the currently selected child node in this container.
   * @return nsIDOMNode
   */
  get selectedItem() {
    return this._selectedItem;
  },

  /**
   * Sets the currently selected child node in this container.
   * @param nsIDOMNode aChild
   */
  set selectedItem(aChild) {
    let menuArray = this._orderedMenuElementsArray;

    if (!aChild) {
      this._selectedItem = null;
    }
    for (let node of menuArray) {
      if (node == aChild) {
        this._getNodeForContents(node).classList.add("selected");
        this._selectedItem = node;
      } else {
        this._getNodeForContents(node).classList.remove("selected");
      }
    }
  },

  /**
   * Ensures the specified element is visible.
   *
   * @param nsIDOMNode aElement
   *        The element to make visible.
   */
  ensureElementIsVisible: function (aElement) {
    if (!aElement) {
      return;
    }

    // Ensure the element is visible but not scrolled horizontally.
    let boxObject = this._list.boxObject;
    boxObject.ensureElementIsVisible(aElement);
    boxObject.scrollBy(-this._list.clientWidth, 0);
  },

  /**
   * Shows all the groups, even the ones with no visible children.
   */
  showEmptyGroups: function () {
    for (let group of this._orderedGroupElementsArray) {
      group.hidden = false;
    }
  },

  /**
   * Hides all the groups which have no visible children.
   */
  hideEmptyGroups: function () {
    let visibleChildNodes = ".side-menu-widget-item-contents:not([hidden=true])";

    for (let group of this._orderedGroupElementsArray) {
      group.hidden = group.querySelectorAll(visibleChildNodes).length == 0;
    }
    for (let menuItem of this._orderedMenuElementsArray) {
      menuItem.parentNode.hidden = menuItem.hidden;
    }
  },

  /**
   * Adds a new attribute or changes an existing attribute on this container.
   *
   * @param string aName
   *        The name of the attribute.
   * @param string aValue
   *        The desired attribute value.
   */
  setAttribute: function (aName, aValue) {
    this._parent.setAttribute(aName, aValue);

    if (aName == "emptyText") {
      this._textWhenEmpty = aValue;
    }
  },

  /**
   * Removes an attribute on this container.
   *
   * @param string aName
   *        The name of the attribute.
   */
  removeAttribute: function (aName) {
    this._parent.removeAttribute(aName);

    if (aName == "emptyText") {
      this._removeEmptyText();
    }
  },

  /**
   * Set the checkbox state for the item associated with the given node.
   *
   * @param nsIDOMNode aNode
   *        The dom node for an item we want to check.
   * @param boolean aCheckState
   *        True to check, false to uncheck.
   */
  checkItem: function (aNode, aCheckState) {
    const widgetItem = this._itemsByElement.get(aNode);
    if (!widgetItem) {
      throw new Error("No item for " + aNode);
    }
    widgetItem.check(aCheckState);
  },

  /**
   * Sets the text displayed in this container when empty.
   * @param string aValue
   */
  set _textWhenEmpty(aValue) {
    if (this._emptyTextNode) {
      this._emptyTextNode.setAttribute("value", aValue);
    }
    this._emptyTextValue = aValue;
    this._showEmptyText();
  },

  /**
   * Creates and appends a label signaling that this container is empty.
   */
  _showEmptyText: function () {
    if (this._emptyTextNode || !this._emptyTextValue) {
      return;
    }
    let label = this.document.createElement("label");
    label.className = "plain side-menu-widget-empty-text";
    label.setAttribute("value", this._emptyTextValue);

    this._parent.insertBefore(label, this._list);
    this._emptyTextNode = label;
  },

  /**
   * Removes the label representing a notice in this container.
   */
  _removeEmptyText: function () {
    if (!this._emptyTextNode) {
      return;
    }

    this._parent.removeChild(this._emptyTextNode);
    this._emptyTextNode = null;
  },

  /**
   * Gets a container representing a group for menu items. If the container
   * is not available yet, it is immediately created.
   *
   * @param string aName
   *        The required group name.
   * @return SideMenuGroup
   *         The newly created group.
   */
  _getMenuGroupForName: function (aName) {
    let cachedGroup = this._groupsByName.get(aName);
    if (cachedGroup) {
      return cachedGroup;
    }

    let group = new SideMenuGroup(this, aName, {
      showCheckbox: this._showGroupCheckboxes
    });

    this._groupsByName.set(aName, group);
    group.insertSelfAt(this.sortedGroups ? group.findExpectedIndexForSelf(this.groupSortPredicate) : -1);

    return group;
  },

  /**
   * Gets a menu item to be displayed inside a group.
   * @see SideMenuWidget.prototype._getMenuGroupForName
   *
   * @param SideMenuGroup aGroup
   *        The group to contain the menu item.
   * @param nsIDOMNode aContents
   *        The node displayed in the container.
   * @param object aAttachment [optional]
   *        Some attached primitive/object.
   */
  _getMenuItemForGroup: function (aGroup, aContents, aAttachment) {
    return new SideMenuItem(aGroup, aContents, aAttachment, {
      showArrow: this._showArrows,
      showCheckbox: this._showItemCheckboxes
    });
  },

  /**
   * Returns the .side-menu-widget-item node corresponding to a SideMenuItem.
   * To optimize the markup, some redundant elemenst are skipped when creating
   * these child items, in which case we need to be careful on which nodes
   * .selected class names are added, or which nodes are removed.
   *
   * @param nsIDOMNode aChild
   *        An element which is the target node of a SideMenuItem.
   * @return nsIDOMNode
   *         The wrapper node if there is one, or the same child otherwise.
   */
  _getNodeForContents: function (aChild) {
    if (aChild.hasAttribute("merged-item-contents")) {
      return aChild;
    } else {
      return aChild.parentNode;
    }
  },

  /**
   * Shows the contextMenu element.
   */
  _showContextMenu: function (e) {
    if (!this._contextMenu) {
      return;
    }

    // Don't show the menu if a descendant node is going to be visible also.
    let node = e.originalTarget;
    while (node && node !== this._list) {
      if (node.hasAttribute("contextmenu")) {
        return;
      }
      node = node.parentNode;
    }

    this._contextMenu.openPopupAtScreen(e.screenX, e.screenY, true);
  },

  window: null,
  document: null,
  _showArrows: false,
  _showItemCheckboxes: false,
  _showGroupCheckboxes: false,
  _parent: null,
  _list: null,
  _selectedItem: null,
  _groupsByName: null,
  _orderedGroupElementsArray: null,
  _orderedMenuElementsArray: null,
  _itemsByElement: null,
  _emptyTextNode: null,
  _emptyTextValue: ""
};

/**
 * A SideMenuGroup constructor for the BreadcrumbsWidget.
 * Represents a group which should contain SideMenuItems.
 *
 * @param SideMenuWidget aWidget
 *        The widget to contain this menu item.
 * @param string aName
 *        The string displayed in the container.
 * @param object aOptions [optional]
 *        An object containing the following properties:
 *          - showCheckbox: specifies if a checkbox should be displayed.
 */
function SideMenuGroup(aWidget, aName, aOptions = {}) {
  this.document = aWidget.document;
  this.window = aWidget.window;
  this.ownerView = aWidget;
  this.identifier = aName;

  // Create an internal title and list container.
  if (aName) {
    let target = this._target = this.document.createElement("vbox");
    target.className = "side-menu-widget-group";
    target.setAttribute("name", aName);

    let list = this._list = this.document.createElement("vbox");
    list.className = "side-menu-widget-group-list";

    let title = this._title = this.document.createElement("hbox");
    title.className = "side-menu-widget-group-title";

    let name = this._name = this.document.createElement("label");
    name.className = "plain name";
    name.setAttribute("value", aName);
    name.setAttribute("crop", "end");
    name.setAttribute("flex", "1");

    // Show a checkbox before the content.
    if (aOptions.showCheckbox) {
      let checkbox = this._checkbox = makeCheckbox(title, {
        description: aName,
        checkboxTooltip: L10N.getStr("sideMenu.groupCheckbox.tooltip")
      });
      checkbox.className = "side-menu-widget-group-checkbox";
    }

    title.appendChild(name);
    target.appendChild(title);
    target.appendChild(list);
  }
  // Skip a few redundant nodes when no title is shown.
  else {
    let target = this._target = this._list = this.document.createElement("vbox");
    target.className = "side-menu-widget-group side-menu-widget-group-list";
    target.setAttribute("merged-group-contents", "");
  }
}

SideMenuGroup.prototype = {
  get _orderedGroupElementsArray() {
    return this.ownerView._orderedGroupElementsArray;
  },
  get _orderedMenuElementsArray() {
    return this.ownerView._orderedMenuElementsArray;
  },
  get _itemsByElement() { return this.ownerView._itemsByElement; },

  /**
   * Inserts this group in the parent container at the specified index.
   *
   * @param number aIndex
   *        The position in the container intended for this group.
   */
  insertSelfAt: function (aIndex) {
    let ownerList = this.ownerView._list;
    let groupsArray = this._orderedGroupElementsArray;

    if (aIndex >= 0) {
      ownerList.insertBefore(this._target, groupsArray[aIndex]);
      groupsArray.splice(aIndex, 0, this._target);
    } else {
      ownerList.appendChild(this._target);
      groupsArray.push(this._target);
    }
  },

  /**
   * Finds the expected index of this group based on its name.
   *
   * @return number
   *         The expected index.
   */
  findExpectedIndexForSelf: function (sortPredicate) {
    let identifier = this.identifier;
    let groupsArray = this._orderedGroupElementsArray;

    for (let group of groupsArray) {
      let name = group.getAttribute("name");
      if (sortPredicate(name, identifier) > 0 && // Insertion sort at its best :)
          !name.includes(identifier)) { // Least significant group should be last.
        return groupsArray.indexOf(group);
      }
    }
    return -1;
  },

  window: null,
  document: null,
  ownerView: null,
  identifier: "",
  _target: null,
  _checkbox: null,
  _title: null,
  _name: null,
  _list: null
};

/**
 * A SideMenuItem constructor for the BreadcrumbsWidget.
 *
 * @param SideMenuGroup aGroup
 *        The group to contain this menu item.
 * @param nsIDOMNode aContents
 *        The node displayed in the container.
 * @param object aAttachment [optional]
 *        The attachment object.
 * @param object aOptions [optional]
 *        An object containing the following properties:
 *          - showArrow: specifies if a horizontal arrow should be displayed.
 *          - showCheckbox: specifies if a checkbox should be displayed.
 */
function SideMenuItem(aGroup, aContents, aAttachment = {}, aOptions = {}) {
  this.document = aGroup.document;
  this.window = aGroup.window;
  this.ownerView = aGroup;

  if (aOptions.showArrow || aOptions.showCheckbox) {
    let container = this._container = this.document.createElement("hbox");
    container.className = "side-menu-widget-item";

    let target = this._target = this.document.createElement("vbox");
    target.className = "side-menu-widget-item-contents";

    // Show a checkbox before the content.
    if (aOptions.showCheckbox) {
      let checkbox = this._checkbox = makeCheckbox(container, aAttachment);
      checkbox.className = "side-menu-widget-item-checkbox";
    }

    container.appendChild(target);

    // Show a horizontal arrow towards the content.
    if (aOptions.showArrow) {
      let arrow = this._arrow = this.document.createElement("hbox");
      arrow.className = "side-menu-widget-item-arrow";
      container.appendChild(arrow);
    }
  }
  // Skip a few redundant nodes when no horizontal arrow or checkbox is shown.
  else {
    let target = this._target = this._container = this.document.createElement("hbox");
    target.className = "side-menu-widget-item side-menu-widget-item-contents";
    target.setAttribute("merged-item-contents", "");
  }

  this._target.setAttribute("flex", "1");
  this.contents = aContents;
}

SideMenuItem.prototype = {
  get _orderedGroupElementsArray() {
    return this.ownerView._orderedGroupElementsArray;
  },
  get _orderedMenuElementsArray() {
    return this.ownerView._orderedMenuElementsArray;
  },
  get _itemsByElement() { return this.ownerView._itemsByElement; },

  /**
   * Inserts this item in the parent group at the specified index.
   *
   * @param number aIndex
   *        The position in the container intended for this item.
   * @return nsIDOMNode
   *         The element associated with the displayed item.
   */
  insertSelfAt: function (aIndex) {
    let ownerList = this.ownerView._list;
    let menuArray = this._orderedMenuElementsArray;

    if (aIndex >= 0) {
      ownerList.insertBefore(this._container, ownerList.childNodes[aIndex]);
      menuArray.splice(aIndex, 0, this._target);
    } else {
      ownerList.appendChild(this._container);
      menuArray.push(this._target);
    }
    this._itemsByElement.set(this._target, this);

    return this._target;
  },

  /**
   * Check or uncheck the checkbox associated with this item.
   *
   * @param boolean aCheckState
   *        True to check, false to uncheck.
   */
  check: function (aCheckState) {
    if (!this._checkbox) {
      throw new Error("Cannot check items that do not have checkboxes.");
    }
    // Don't set or remove the "checked" attribute, assign the property instead.
    // Otherwise, the "CheckboxStateChange" event will not be fired. XUL!!
    this._checkbox.checked = !!aCheckState;
  },

  /**
   * Sets the contents displayed in this item's view.
   *
   * @param string | nsIDOMNode aContents
   *        The string or node displayed in the container.
   */
  set contents(aContents) {
    // If there are already some contents displayed, replace them.
    if (this._target.hasChildNodes()) {
      this._target.replaceChild(aContents, this._target.firstChild);
      return;
    }
    // These are the first contents ever displayed.
    this._target.appendChild(aContents);
  },

  window: null,
  document: null,
  ownerView: null,
  _target: null,
  _container: null,
  _checkbox: null,
  _arrow: null
};

/**
 * Creates a checkbox to a specified parent node. Emits a "check" event
 * whenever the checkbox is checked or unchecked by the user.
 *
 * @param nsIDOMNode aParentNode
 *        The parent node to contain this checkbox.
 * @param object aOptions
 *        An object containing some or all of the following properties:
 *          - description: defaults to "item" if unspecified
 *          - checkboxState: true for checked, false for unchecked
 *          - checkboxTooltip: the tooltip text of the checkbox
 */
function makeCheckbox(aParentNode, aOptions) {
  let checkbox = aParentNode.ownerDocument.createElement("checkbox");

  checkbox.setAttribute("tooltiptext", aOptions.checkboxTooltip || "");

  if (aOptions.checkboxState) {
    checkbox.setAttribute("checked", true);
  } else {
    checkbox.removeAttribute("checked");
  }

  // Stop the toggling of the checkbox from selecting the list item.
  checkbox.addEventListener("mousedown", e => {
    e.stopPropagation();
  });

  // Emit an event from the checkbox when it is toggled. Don't listen for the
  // "command" event! It won't fire for programmatic changes. XUL!!
  checkbox.addEventListener("CheckboxStateChange", e => {
    ViewHelpers.dispatchEvent(checkbox, "check", {
      description: aOptions.description || "item",
      checked: checkbox.checked
    });
  });

  aParentNode.appendChild(checkbox);
  return checkbox;
}
PK
!<~�qa��Kchrome/devtools/modules/devtools/client/shared/widgets/SimpleListWidget.jsm/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
const { ViewHelpers } = require("devtools/client/shared/widgets/view-helpers");

this.EXPORTED_SYMBOLS = ["SimpleListWidget"];

/**
 * A very simple vertical list view.
 *
 * Note: this widget should be used in tandem with the WidgetMethods in
 * view-helpers.js.
 *
 * @param nsIDOMNode aNode
 *        The element associated with the widget.
 */
function SimpleListWidget(aNode) {
  this.document = aNode.ownerDocument;
  this.window = this.document.defaultView;
  this._parent = aNode;

  // Create an internal list container.
  this._list = this.document.createElement("scrollbox");
  this._list.className = "simple-list-widget-container theme-body";
  this._list.setAttribute("flex", "1");
  this._list.setAttribute("orient", "vertical");
  this._parent.appendChild(this._list);

  // Delegate some of the associated node's methods to satisfy the interface
  // required by WidgetMethods instances.
  ViewHelpers.delegateWidgetAttributeMethods(this, aNode);
  ViewHelpers.delegateWidgetEventMethods(this, aNode);
}
this.SimpleListWidget = SimpleListWidget;

SimpleListWidget.prototype = {
  /**
   * Inserts an item in this container at the specified index.
   *
   * @param number aIndex
   *        The position in the container intended for this item.
   * @param nsIDOMNode aContents
   *        The node displayed in the container.
   * @return nsIDOMNode
   *         The element associated with the displayed item.
   */
  insertItemAt: function (aIndex, aContents) {
    aContents.classList.add("simple-list-widget-item");

    let list = this._list;
    return list.insertBefore(aContents, list.childNodes[aIndex]);
  },

  /**
   * Returns the child node in this container situated at the specified index.
   *
   * @param number aIndex
   *        The position in the container intended for this item.
   * @return nsIDOMNode
   *         The element associated with the displayed item.
   */
  getItemAtIndex: function (aIndex) {
    return this._list.childNodes[aIndex];
  },

  /**
   * Immediately removes the specified child node from this container.
   *
   * @param nsIDOMNode aChild
   *        The element associated with the displayed item.
   */
  removeChild: function (aChild) {
    this._list.removeChild(aChild);

    if (this._selectedItem == aChild) {
      this._selectedItem = null;
    }
  },

  /**
   * Removes all of the child nodes from this container.
   */
  removeAllItems: function () {
    let list = this._list;
    let parent = this._parent;

    while (list.hasChildNodes()) {
      list.firstChild.remove();
    }

    parent.scrollTop = 0;
    parent.scrollLeft = 0;
    this._selectedItem = null;
  },

  /**
   * Gets the currently selected child node in this container.
   * @return nsIDOMNode
   */
  get selectedItem() {
    return this._selectedItem;
  },

  /**
   * Sets the currently selected child node in this container.
   * @param nsIDOMNode aChild
   */
  set selectedItem(aChild) {
    let childNodes = this._list.childNodes;

    if (!aChild) {
      this._selectedItem = null;
    }
    for (let node of childNodes) {
      if (node == aChild) {
        node.classList.add("selected");
        this._selectedItem = node;
      } else {
        node.classList.remove("selected");
      }
    }
  },

  /**
   * Adds a new attribute or changes an existing attribute on this container.
   *
   * @param string aName
   *        The name of the attribute.
   * @param string aValue
   *        The desired attribute value.
   */
  setAttribute: function (aName, aValue) {
    this._parent.setAttribute(aName, aValue);

    if (aName == "emptyText") {
      this._textWhenEmpty = aValue;
    } else if (aName == "headerText") {
      this._textAsHeader = aValue;
    }
  },

  /**
   * Removes an attribute on this container.
   *
   * @param string aName
   *        The name of the attribute.
   */
  removeAttribute: function (aName) {
    this._parent.removeAttribute(aName);

    if (aName == "emptyText") {
      this._removeEmptyText();
    }
  },

  /**
   * Ensures the specified element is visible.
   *
   * @param nsIDOMNode aElement
   *        The element to make visible.
   */
  ensureElementIsVisible: function (aElement) {
    if (!aElement) {
      return;
    }

    // Ensure the element is visible but not scrolled horizontally.
    let boxObject = this._list.boxObject;
    boxObject.ensureElementIsVisible(aElement);
    boxObject.scrollBy(-this._list.clientWidth, 0);
  },

  /**
   * Sets the text displayed permanently in this container as a header.
   * @param string aValue
   */
  set _textAsHeader(aValue) {
    if (this._headerTextNode) {
      this._headerTextNode.setAttribute("value", aValue);
    }
    this._headerTextValue = aValue;
    this._showHeaderText();
  },

  /**
   * Sets the text displayed in this container when empty.
   * @param string aValue
   */
  set _textWhenEmpty(aValue) {
    if (this._emptyTextNode) {
      this._emptyTextNode.setAttribute("value", aValue);
    }
    this._emptyTextValue = aValue;
    this._showEmptyText();
  },

  /**
   * Creates and appends a label displayed as this container's header.
   */
  _showHeaderText: function () {
    if (this._headerTextNode || !this._headerTextValue) {
      return;
    }
    let label = this.document.createElement("label");
    label.className = "plain simple-list-widget-perma-text";
    label.setAttribute("value", this._headerTextValue);

    this._parent.insertBefore(label, this._list);
    this._headerTextNode = label;
  },

  /**
   * Creates and appends a label signaling that this container is empty.
   */
  _showEmptyText: function () {
    if (this._emptyTextNode || !this._emptyTextValue) {
      return;
    }
    let label = this.document.createElement("label");
    label.className = "plain simple-list-widget-empty-text";
    label.setAttribute("value", this._emptyTextValue);

    this._parent.appendChild(label);
    this._emptyTextNode = label;
  },

  /**
   * Removes the label signaling that this container is empty.
   */
  _removeEmptyText: function () {
    if (!this._emptyTextNode) {
      return;
    }
    this._parent.removeChild(this._emptyTextNode);
    this._emptyTextNode = null;
  },

  window: null,
  document: null,
  _parent: null,
  _list: null,
  _selectedItem: null,
  _headerTextNode: null,
  _headerTextValue: "",
  _emptyTextNode: null,
  _emptyTextValue: ""
};
PK
!<ͼ�3&&Bchrome/devtools/modules/devtools/client/shared/widgets/Spectrum.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/shared/event-emitter");
const XHTML_NS = "http://www.w3.org/1999/xhtml";

/**
 * 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
 */
function Spectrum(parentEl, rgb) {
  EventEmitter.decorate(this);

  this.element = parentEl.ownerDocument.createElementNS(XHTML_NS, "div");
  this.parentEl = parentEl;

  this.element.className = "spectrum-container";
  this.element.innerHTML = `
    <div class="spectrum-top">
      <div class="spectrum-fill"></div>
      <div class="spectrum-top-inner">
        <div class="spectrum-color spectrum-box">
          <div class="spectrum-sat">
            <div class="spectrum-val">
              <div class="spectrum-dragger"></div>
            </div>
          </div>
        </div>
        <div class="spectrum-hue spectrum-box">
          <div class="spectrum-slider spectrum-slider-control"></div>
        </div>
      </div>
    </div>
    <div class="spectrum-alpha spectrum-checker spectrum-box">
      <div class="spectrum-alpha-inner">
        <div class="spectrum-alpha-handle spectrum-slider-control"></div>
      </div>
    </div>
  `;

  this.onElementClick = this.onElementClick.bind(this);
  this.element.addEventListener("click", this.onElementClick);

  this.parentEl.appendChild(this.element);

  this.slider = this.element.querySelector(".spectrum-hue");
  this.slideHelper = this.element.querySelector(".spectrum-slider");
  Spectrum.draggable(this.slider, this.onSliderMove.bind(this));

  this.dragger = this.element.querySelector(".spectrum-color");
  this.dragHelper = this.element.querySelector(".spectrum-dragger");
  Spectrum.draggable(this.dragger, this.onDraggerMove.bind(this));

  this.alphaSlider = this.element.querySelector(".spectrum-alpha");
  this.alphaSliderInner = this.element.querySelector(".spectrum-alpha-inner");
  this.alphaSliderHelper = this.element.querySelector(".spectrum-alpha-handle");
  Spectrum.draggable(this.alphaSliderInner, this.onAlphaSliderMove.bind(this));

  if (rgb) {
    this.rgb = rgb;
    this.updateUI();
  }
}

module.exports.Spectrum = Spectrum;

Spectrum.hsvToRgb = function (h, s, v, a) {
  let r, g, b;

  let i = Math.floor(h * 6);
  let f = h * 6 - i;
  let p = v * (1 - s);
  let q = v * (1 - f * s);
  let 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];
};

Spectrum.rgbToHsv = function (r, g, b, a) {
  r = r / 255;
  g = g / 255;
  b = b / 255;

  let max = Math.max(r, g, b), min = Math.min(r, g, b);
  let h, s, v = max;

  let d = max - min;
  s = max == 0 ? 0 : d / max;

  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];
};

Spectrum.draggable = function (element, onmove, onstart, onstop) {
  onmove = onmove || function () {};
  onstart = onstart || function () {};
  onstop = onstop || function () {};

  let doc = element.ownerDocument;
  let dragging = false;
  let offset = {};
  let maxHeight = 0;
  let maxWidth = 0;

  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;
      }
      let pageX = e.pageX;
      let pageY = e.pageY;

      let dragX = Math.max(0, Math.min(pageX - offset.left, maxWidth));
      let dragY = Math.max(0, Math.min(pageY - offset.top, maxHeight));

      onmove.apply(element, [dragX, dragY]);
    }
  }

  function start(e) {
    let rightclick = e.which === 3;

    if (!rightclick && !dragging) {
      if (onstart.apply(element, arguments) !== false) {
        dragging = true;
        maxHeight = element.offsetHeight;
        maxWidth = element.offsetWidth;

        offset = element.getBoundingClientRect();

        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);
      onstop.apply(element, arguments);
    }
    dragging = false;
  }

  element.addEventListener("mousedown", start);
};

Spectrum.prototype = {
  set rgb(color) {
    this.hsv = Spectrum.rgbToHsv(color[0], color[1], color[2], color[3]);
  },

  get rgb() {
    let rgb = Spectrum.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];
  },

  get rgbNoSatVal() {
    let rgb = Spectrum.hsvToRgb(this.hsv[0], 1, 1);
    return [Math.round(rgb[0]), Math.round(rgb[1]), Math.round(rgb[2]), rgb[3]];
  },

  get rgbCssString() {
    let rgb = this.rgb;
    return "rgba(" + rgb[0] + ", " + rgb[1] + ", " + rgb[2] + ", " +
      rgb[3] + ")";
  },

  show: function () {
    this.element.classList.add("spectrum-show");

    this.slideHeight = this.slider.offsetHeight;
    this.dragWidth = this.dragger.offsetWidth;
    this.dragHeight = this.dragger.offsetHeight;
    this.dragHelperHeight = this.dragHelper.offsetHeight;
    this.slideHelperHeight = this.slideHelper.offsetHeight;
    this.alphaSliderWidth = this.alphaSliderInner.offsetWidth;
    this.alphaSliderHelperWidth = this.alphaSliderHelper.offsetWidth;

    this.updateUI();
  },

  onElementClick: function (e) {
    e.stopPropagation();
  },

  onSliderMove: function (dragX, dragY) {
    this.hsv[0] = (dragY / this.slideHeight);
    this.updateUI();
    this.onChange();
  },

  onDraggerMove: function (dragX, dragY) {
    this.hsv[1] = dragX / this.dragWidth;
    this.hsv[2] = (this.dragHeight - dragY) / this.dragHeight;
    this.updateUI();
    this.onChange();
  },

  onAlphaSliderMove: function (dragX, dragY) {
    this.hsv[3] = dragX / this.alphaSliderWidth;
    this.updateUI();
    this.onChange();
  },

  onChange: function () {
    this.emit("changed", this.rgb, this.rgbCssString);
  },

  updateHelperLocations: function () {
    // If the UI hasn't been shown yet then none of the dimensions will be
    // correct
    if (!this.element.classList.contains("spectrum-show")) {
      return;
    }

    let h = this.hsv[0];
    let s = this.hsv[1];
    let v = this.hsv[2];

    // Placing the color dragger
    let dragX = s * this.dragWidth;
    let dragY = this.dragHeight - (v * this.dragHeight);
    let 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
    let slideY = (h * this.slideHeight) - this.slideHelperHeight / 2;
    this.slideHelper.style.top = slideY + "px";

    // Placing the alpha slider
    let alphaSliderX = (this.hsv[3] * this.alphaSliderWidth) -
      (this.alphaSliderHelperWidth / 2);
    this.alphaSliderHelper.style.left = alphaSliderX + "px";
  },

  updateUI: function () {
    this.updateHelperLocations();

    let rgb = this.rgb;
    let rgbNoSatVal = this.rgbNoSatVal;

    let flatColor = "rgb(" + rgbNoSatVal[0] + ", " + rgbNoSatVal[1] + ", " +
      rgbNoSatVal[2] + ")";

    this.dragger.style.backgroundColor = flatColor;

    let rgbNoAlpha = "rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ")";
    let rgbAlpha0 = "rgba(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ", 0)";
    let alphaGradient = "linear-gradient(to right, " + rgbAlpha0 + ", " +
      rgbNoAlpha + ")";
    this.alphaSliderInner.style.background = alphaGradient;
  },

  destroy: function () {
    this.element.removeEventListener("click", this.onElementClick);

    this.parentEl.removeChild(this.element);

    this.slider = null;
    this.dragger = null;
    this.alphaSlider = this.alphaSliderInner = this.alphaSliderHelper = null;
    this.parentEl = null;
    this.element = null;
  }
};
PK
!<Gi�0!�!�Echrome/devtools/modules/devtools/client/shared/widgets/TableWidget.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/shared/event-emitter");
loader.lazyRequireGetter(this, "setNamedTimeout",
  "devtools/client/shared/widgets/view-helpers", true);
loader.lazyRequireGetter(this, "clearNamedTimeout",
  "devtools/client/shared/widgets/view-helpers", true);
loader.lazyRequireGetter(this, "naturalSortCaseInsensitive",
  "devtools/client/shared/natural-sort", true);
const {KeyCodes} = require("devtools/client/shared/keycodes");

const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
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 {nsIDOMNode} 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: text to display when 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;

  let {initialColumns, emptyText, uniqueId, highlightUpdated, removableColumns,
       firstColumn, wrapTextInElements, cellContextMenuId} = 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.tbody = this.document.createElementNS(XUL_NS, "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));

  this.placeholder = this.document.createElementNS(XUL_NS, "label");
  this.placeholder.className = "plain table-widget-empty-text";
  this.placeholder.setAttribute("flex", "1");
  this._parent.appendChild(this.placeholder);

  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);
  } else if (this.emptyText) {
    this.setPlaceholderText(this.emptyText);
  }

  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,

  /**
   * 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 (let column of this.columns.values()) {
      if (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 (let 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() {
    let column = this.firstVisibleColumn;
    let 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 (let column of this.columns.values()) {
      if (column._private) {
        continue;
      }

      if (column.column.clientHeight > 0) {
        return column;
      }
    }

    return null;
  },

  /**
   * returns all editable columns.
   */
  get editableColumns() {
    let filter = columns => {
      columns = [...columns].filter(col => {
        if (col.clientWidth === 0) {
          return false;
        }

        let cell = col.querySelector(".table-widget-cell");

        for (let selector of this._editableFieldsEngine.selectors) {
          if (cell.matches(selector)) {
            return true;
          }
        }

        return false;
      });

      return columns;
    };

    let columns = this._parent.querySelectorAll(".table-widget-column");
    return filter(columns);
  },

  /**
   * Emit all cell edit events.
   */
  onChange: function (data) {
    let changedField = data.change.field;
    let colName = changedField.parentNode.id;
    let column = this.columns.get(colName);
    let uniqueId = column.table.uniqueId;
    let itemIndex = column.cellNodes.indexOf(changedField);
    let items = {};

    for (let [name, col] of this.columns) {
      items[name] = col.cellNodes[itemIndex].value;
    }

    let change = {
      host: this.host,
      key: uniqueId,
      field: colName,
      oldValue: data.change.oldValue,
      newValue: data.change.newValue,
      items: 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: function () {
    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: function (event) {
    let textbox = event.target;
    let editor = this._editableFieldsEngine;

    if (textbox.id !== editor.INPUT_ID) {
      return;
    }

    let 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 cell;
        let cells;
        let columnObj;
        let cols = this.editableColumns;
        let rowIndex = this.visibleSelectedIndex;
        let 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) {
          let id = cols[0].id;
          columnObj = this.columns.get(id);
          let maxRowIndex = columnObj.visibleCellNodes.length - 1;
          if (rowIndex === maxRowIndex) {
            return;
          }
          newIndex = 0;
        } else {
          newIndex = colIndex + 1;
        }

        let newcol = cols[newIndex];
        columnObj = this.columns.get(newcol.id);

        // Select the correct row even if it has moved due to sorting.
        let 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.
        cells = columnObj.visibleCellNodes;
        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.
    let cell = this.getEditedCellOnTab(event, column);
    editor.edit(cell);
  },

  /**
   * 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: function (event, column) {
    let cell = null;
    let cols = this.editableColumns;
    let rowIndex = this.visibleSelectedIndex;
    let colIndex = cols.indexOf(column);
    let maxCol = cols.length - 1;
    let 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];
        let cells = this.columns.get(column.id).visibleCellNodes;
        cell = cells[rowIndex - 1];

        let rowId = cell.getAttribute("data-id");
        this.emit(EVENTS.ROW_SELECTED, rowId);
      } else {
        column = cols[colIndex - 1];
        let 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];
      let cells = this.columns.get(column.id).visibleCellNodes;
      cell = cells[rowIndex + 1];

      let rowId = cell.getAttribute("data-id");
      this.emit(EVENTS.ROW_SELECTED, rowId);
    } else {
      // Navigate forwards on tab.
      column = cols[colIndex + 1];
      let 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: function (row) {
    if (!this._editableFieldsEngine || !this._editableFieldsEngine.isEditing) {
      return;
    }

    let removedKey = row[this.uniqueId];
    let 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: function () {
    let editor = this._editableFieldsEngine;

    if (!editor || !editor.isEditing) {
      return;
    }

    editor.cancelEdit();
  },

  /**
   * Keydown event handler for the table. Used for keyboard navigation amongst
   * rows.
   */
  onKeydown: function (event) {
    // If we are in edit mode bail out.
    if (this._editableFieldsEngine && this._editableFieldsEngine.isEditing) {
      return;
    }

    let selectedCell = this.tbody.querySelector(".theme-selected");
    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: function ({target}) {
    let nodeName = target.nodeName;

    if (nodeName === "textbox" || !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: function (editableColumns) {
    let selectors = [];

    if (typeof editableColumns === "string") {
      editableColumns = [editableColumns];
    }

    for (let id of editableColumns) {
      selectors.push("#" + id + " .table-widget-cell");
    }

    for (let [name, column] of this.columns) {
      if (!editableColumns.includes(name)) {
        column.column.setAttribute("readonly", "");
      }
    }

    if (this._editableFieldsEngine) {
      this._editableFieldsEngine.selectors = selectors;
    } else {
      this._editableFieldsEngine = new EditableFieldsEngine({
        root: this.tbody,
        onTab: this.onEditorTab,
        onTriggerEvent: "dblclick",
        selectors: selectors
      });

      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: function () {
    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 text to be shown when the table is empty.
   */
  setPlaceholderText: function (text) {
    this.placeholder.setAttribute("value", text);
  },

  /**
   * 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: function () {
    let popupset = this.document.getElementsByTagName("popupset")[0];
    if (!popupset) {
      popupset = this.document.createElementNS(XUL_NS, "popupset");
      this.document.documentElement.appendChild(popupset);
    }

    this.menupopup = this.document.createElementNS(XUL_NS, "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: function (privateColumns = []) {
    if (!this.menupopup) {
      return;
    }

    while (this.menupopup.firstChild) {
      this.menupopup.firstChild.remove();
    }

    for (let column of this.columns.values()) {
      if (privateColumns.includes(column.id)) {
        continue;
      }

      let menuitem = this.document.createElementNS(XUL_NS, "menuitem");
      menuitem.setAttribute("label", column.header.getAttribute("value"));
      menuitem.setAttribute("data-id", column.id);
      menuitem.setAttribute("type", "checkbox");
      menuitem.setAttribute("checked", !column.wrapper.getAttribute("hidden"));
      if (column.id == this.uniqueId) {
        menuitem.setAttribute("disabled", "true");
      }
      this.menupopup.appendChild(menuitem);
    }
    let 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: function (event) {
    let item = event.originalTarget;
    let checked = !!item.getAttribute("checked");
    let id = item.getAttribute("data-id");
    this.emit(EVENTS.HEADER_CONTEXT_MENU, id, checked);
    checked = this.menupopup.querySelectorAll("menuitem[checked]");
    let 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: function (columns, sortOn = this.sortedOn, hiddenColumns = [],
                        privateColumns = []) {
    for (let 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 (let 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: function (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: function (id) {
    this.selectedRow = id;
  },

  /**
   * Selects the next row. Cycles over to the first row if last row is selected
   */
  selectNextRow: function () {
    for (let column of this.columns.values()) {
      column.selectNextRow();
    }
  },

  /**
   * Selects the previous row. Cycles over to the last row if first row is
   * selected.
   */
  selectPreviousRow: function () {
    for (let column of this.columns.values()) {
      column.selectPreviousRow();
    }
  },

  /**
   * Clears any selected row.
   */
  clearSelection: function () {
    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: function (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];
    }

    let index = this.columns.get(this.sortedOn).push(item);
    for (let [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: function (item) {
    if (typeof item != "object") {
      item = this.items.get(item);
    }
    if (!item) {
      return;
    }
    let removed = this.items.delete(item[this.uniqueId]);

    if (!removed) {
      return;
    }
    for (let 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: function (item) {
    let oldItem = this.items.get(item[this.uniqueId]);
    if (!oldItem) {
      return;
    }
    this.items.set(item[this.uniqueId], item);

    let changed = false;
    for (let 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: function () {
    this.items.clear();
    for (let column of this.columns.values()) {
      column.clear();
    }
    this.tbody.setAttribute("empty", "empty");
    this.setPlaceholderText(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: function (column) {
    this.emit(EVENTS.COLUMN_SORTED, column);
    this.sortedOn = column;

    if (!this.items.size) {
      return;
    }

    let sortedItems = this.columns.get(column).sort([...this.items.values()]);
    for (let [id, col] of this.columns) {
      if (id != col) {
        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();

    let itemsToHide = [...this.items.keys()];
    // Loop through all items and hide unmatched items
    for (let [id, val] of this.items) {
      for (let prop in val) {
        let column = this.columns.get(prop);
        if (ignoreProps.includes(prop) || column.hidden) {
          continue;
        }

        let 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: function () {
    clearNamedTimeout("table-scroll");
    setNamedTimeout("table-scroll", AFTER_SCROLL_DELAY, this.afterScroll);
  },

  /**
   * Emits the "scroll-end" event when the whole table is scrolled
   */
  afterScroll: function () {
    let scrollHeight = this.tbody.getBoundingClientRect().height -
        this.tbody.querySelector(".table-widget-column-header").clientHeight;

    // Emit scroll-end event when 9/10 of the table is scrolled
    if (this.tbody.scrollTop >= 0.9 * scrollHeight) {
      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 wrapping element is required solely so that position:sticky works on
  // the headers of the columns.
  this.wrapper = this.document.createElementNS(XUL_NS, "vbox");
  this.wrapper.className = "table-widget-wrapper";
  this.wrapper.setAttribute("flex", "1");
  this.wrapper.setAttribute("tabindex", "0");
  this.tbody.appendChild(this.wrapper);

  this.splitter = this.document.createElementNS(XUL_NS, "splitter");
  this.splitter.className = "devtools-side-splitter";
  this.tbody.appendChild(this.splitter);

  this.column = this.document.createElementNS(HTML_NS, "div");
  this.column.id = id;
  this.column.className = "table-widget-column";
  this.wrapper.appendChild(this.column);

  this.header = this.document.createElementNS(XUL_NS, "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.wrapper.hasAttribute("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() {
    let editor = this.table._editableFieldsEngine;
    let 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} event
   *        The event name of the event. i.e. EVENTS.COLUMN_SORTED
   * @param {string} column
   *        The id of the column being sorted by.
   */
  onColumnSorted: function (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: function (itemsToHide) {
    this._updateItems();
    if (!this.cells) {
      return;
    }
    for (let cell of this.cells) {
      cell.hidden = false;
    }
    for (let 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: function (id) {
    this._updateItems();

    if (this.highlightUpdated && this.items[id] != null) {
      if (this.table.scrollIntoViewOnUpdate) {
        let 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: function () {
    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.parentNode.remove();
    this.cells = null;
    this.items = null;
    this.selectedRow = null;
  },

  /**
   * Selects the row at the `index` index
   */
  selectRowAt: function (index) {
    if (this.selectedRow != null) {
      this.cells[this.items[this.selectedRow]].classList.remove("theme-selected");
    }

    let 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: function (id) {
    this._updateItems();
    this.selectRowAt(this.items[id]);
  },

  /**
   * Selects the next row. Cycles to first if last row is selected.
   */
  selectNextRow: function () {
    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: function () {
    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: function (item) {
    let value = item[this.id];

    if (this.sorted) {
      let index;
      if (this.sorted == 1) {
        index = this.cells.findIndex(element => {
          return naturalSortCaseInsensitive(value, element.value) === -1;
        });
      } else {
        index = this.cells.findIndex(element => {
          return naturalSortCaseInsensitive(value, element.value) === 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: function (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: function (id, checked) {
    if (arguments.length == 0) {
      // Act like a toggling method when called with no params
      id = this.id;
      checked = this.wrapper.hasAttribute("hidden");
    }
    if (id != this.id) {
      return;
    }
    if (checked) {
      this.wrapper.removeAttribute("hidden");
    } else {
      this.wrapper.setAttribute("hidden", "true");
    }
  },

  /**
   * Removes the corresponding item from the column.
   */
  remove: function (item) {
    this._updateItems();
    let 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: function (item) {
    this._updateItems();

    let 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: function () {
    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: function () {
    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: function (items) {
    // Only sort the array if we are sorting based on this column
    if (this.sorted == 1) {
      items.sort((a, b) => {
        let val1 = (a[this.id] instanceof Node) ?
            a[this.id].textContent : a[this.id];
        let val2 = (b[this.id] instanceof Node) ?
            b[this.id].textContent : b[this.id];
        return naturalSortCaseInsensitive(val1, val2);
      });
    } else if (this.sorted > 1) {
      items.sort((a, b) => {
        let val1 = (a[this.id] instanceof Node) ?
            a[this.id].textContent : a[this.id];
        let val2 = (b[this.id] instanceof Node) ?
            b[this.id].textContent : b[this.id];
        return naturalSortCaseInsensitive(val2, val1);
      });
    }

    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.
    items.forEach((item, i) => {
      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 (let cell of this.cells) {
      if (!cell.hidden) {
        i++;
      }

      let even = !(i % 2);
      cell.classList.toggle("even", even);
    }
  },

  /**
   * Click event handler for the column. Used to detect click on header for
   * for sorting.
   */
  onClick: function (event) {
    let 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: function (event) {
    let target = event.originalTarget;

    if (target.nodeType !== target.ELEMENT_NODE ||
        target == this.column ||
        target == this.header) {
      return;
    }
    if (event.button == 0) {
      let closest = target.closest("[data-id]");
      if (!closest) {
        return;
      }

      let 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) {
  let document = column.document;

  this.wrapTextInElements = column.wrapTextInElements;
  this.label = document.createElementNS(XUL_NS, "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.hasAttribute("hidden");
  },

  set hidden(value) {
    if (value) {
      this.label.setAttribute("hidden", "hidden");
    } else {
      this.label.removeAttribute("hidden");
    }
  },

  set value(value) {
    this._value = value;
    if (value == null) {
      this.label.setAttribute("value", "");
      return;
    }

    if (this.wrapTextInElements && !(value instanceof Node)) {
      let span = this.label.ownerDocument.createElementNS(HTML_NS, "span");
      span.textContent = value;
      value = span;
    }

    if (value instanceof Node) {
      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: function () {
    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
    let onAnimEnd = () => {
      this.label.classList.remove("flash-out");
      this.label.removeEventListener("animationend", onAnimEnd);
    };
    this.label.addEventListener("animationend", onAnimEnd);
    this.label.classList.add("flash-out");
  },

  focus: function () {
    this.label.focus();
  },

  scrollIntoView: function () {
    this.label.scrollIntoView(false);
  },

  destroy: function () {
    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.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) {
      let doc = this.root.ownerDocument;
      this._textbox = doc.createElementNS(XUL_NS, "textbox");
      this._textbox.id = this.INPUT_ID;

      this._textbox.setAttribute("flex", "1");

      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: function ({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: function (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: function (target) {
    if (!target) {
      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: function () {
    if (!this.isEditing) {
      return;
    }

    let oldValue = this.currentValue;
    let newValue = this.textbox.value;
    let changed = oldValue !== newValue;

    this.textbox.hidden = true;

    if (!this.currentTarget) {
      return;
    }

    this.currentTarget.hidden = false;
    if (changed) {
      this.currentTarget.value = newValue;

      let data = {
        change: {
          field: this.currentTarget,
          oldValue: oldValue,
          newValue: newValue
        }
      };

      this.emit("change", data);
    }
  },

  /**
   * Cancel an edit.
   */
  cancelEdit: function () {
    if (!this.isEditing) {
      return;
    }
    if (this.currentTarget) {
      this.currentTarget.hidden = false;
    }

    this.textbox.hidden = true;
  },

  /**
   * Stop edit mode and apply changes.
   */
  blur: function () {
    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: function (source, destination) {
    let style = source.ownerDocument.defaultView.getComputedStyle(source);
    let props = [
      "borderTopWidth",
      "borderRightWidth",
      "borderBottomWidth",
      "borderLeftWidth",
      "fontFamily",
      "fontSize",
      "fontWeight",
      "height",
      "marginTop",
      "marginRight",
      "marginBottom",
      "marginLeft",
      "marginInlineStart",
      "marginInlineEnd"
    ];

    for (let 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: function () {
    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");
  },
};
PK
!<�UyEyEDchrome/devtools/modules/devtools/client/shared/widgets/TreeWidget.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 HTML_NS = "http://www.w3.org/1999/xhtml";

const EventEmitter = require("devtools/shared/event-emitter");
const {KeyCodes} = require("devtools/client/shared/keycodes");

/**
 * A tree widget with keyboard navigation and collapsable structure.
 *
 * @param {nsIDOMNode} 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");
    }
    let 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: function (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: function () {
    this.root.remove();
    this.root = null;
  },

  /**
   * Sets up the root container of the TreeWidget.
   */
  setupRoot: function () {
    this.root = new TreeItem(this.document);
    if (this.contextMenuId) {
      this.root.children.addEventListener("contextmenu", (event) => {
        let 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
   */
  setPlaceholderText: function (text) {
    this.placeholder.textContent = text;
  },

  /**
   * Select any node in the tree.
   *
   * @param {array} id
   *        An array of ids leading upto the selected item
   */
  selectItem: function (id) {
    this.selectedItem = id;
  },

  /**
   * Selects the next visible item in the tree.
   */
  selectNextItem: function () {
    let next = this.getNextVisibleItem();
    if (next) {
      this.selectedItem = next;
    }
  },

  /**
   * Selects the previos visible item in the tree
   */
  selectPreviousItem: function () {
    let prev = this.getPreviousVisibleItem();
    if (prev) {
      this.selectedItem = prev;
    }
  },

  /**
   * Returns the next visible item in the tree
   */
  getNextVisibleItem: function () {
    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 && 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: function () {
    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: function () {
    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: function (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("");
  },

  /**
   * 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: function (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: function () {
    this.root.remove();
    this.setupRoot();
    this.attachments.clear();
    if (this.emptyText) {
      this.setPlaceholderText(this.emptyText);
    }
  },

  /**
   * Expands the tree completely
   */
  expandAll: function () {
    this.root.expandAll();
  },

  /**
   * Collapses the tree completely
   */
  collapseAll: function () {
    this.root.collapseAll();
  },

  /**
   * Click handler for the tree. Used to select, open and close the tree nodes.
   */
  onClick: function (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) {
      let 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: function (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: function () {
    let {top, bottom} = this._selectedLabel.getBoundingClientRect();
    let 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: function (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.
    let 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];
    let 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;
    }
    let 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
      let 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: function (items = []) {
    let id = items.shift();
    if (id && this.items.has(id)) {
      let 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: function (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])) {
      let 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: function () {
    if (this.label) {
      this.label.removeAttribute("expanded");
    }
    for (let child of this.items.values()) {
      child.collapseAll();
    }
  },

  /**
   * Expands this item and all of its sub tree items
   */
  expandAll: function () {
    if (this.label) {
      this.label.setAttribute("expanded", "true");
    }
    for (let child of this.items.values()) {
      child.expandAll();
    }
  },

  destroy: function () {
    this.children.remove();
    this.node.remove();
    this.label = null;
    this.items = null;
    this.children = null;
  }
};
PK
!<�ڜ?Y�Y�Hchrome/devtools/modules/devtools/client/shared/widgets/VariablesView.jsm/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 DBG_STRINGS_URI = "devtools/client/locales/debugger.properties";
const LAZY_EMPTY_DELAY = 150; // ms
const SCROLL_PAGE_SIZE_DEFAULT = 0;
const PAGE_SIZE_SCROLL_HEIGHT_RATIO = 100;
const PAGE_SIZE_MAX_JUMPS = 30;
const SEARCH_ACTION_MAX_DELAY = 300; // ms
const ITEM_FLASH_DURATION = 300; // ms

const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
const EventEmitter = require("devtools/shared/event-emitter");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const Services = require("Services");
const { getSourceNames } = require("devtools/client/shared/source-utils");
const promise = require("promise");
const defer = require("devtools/shared/defer");
const { extend } = require("devtools/shared/extend");
const { ViewHelpers, setNamedTimeout } =
  require("devtools/client/shared/widgets/view-helpers");
const { Task } = require("devtools/shared/task");
const nodeConstants = require("devtools/shared/dom-node-constants");
const {KeyCodes} = require("devtools/client/shared/keycodes");
const {PluralForm} = require("devtools/shared/plural-form");
const {LocalizationHelper, ELLIPSIS} = require("devtools/shared/l10n");
const L10N = new LocalizationHelper(DBG_STRINGS_URI);

XPCOMUtils.defineLazyServiceGetter(this, "clipboardHelper",
  "@mozilla.org/widget/clipboardhelper;1",
  "nsIClipboardHelper");

Object.defineProperty(this, "WebConsoleUtils", {
  get: function () {
    return require("devtools/client/webconsole/utils").Utils;
  },
  configurable: true,
  enumerable: true
});

Object.defineProperty(this, "NetworkHelper", {
  get: function () {
    return require("devtools/shared/webconsole/network-helper");
  },
  configurable: true,
  enumerable: true
});

this.EXPORTED_SYMBOLS = ["VariablesView", "escapeHTML"];

/**
 * A tree view for inspecting scopes, objects and properties.
 * Iterable via "for (let [id, scope] of instance) { }".
 * Requires the devtools common.css and debugger.css skin stylesheets.
 *
 * To allow replacing variable or property values in this view, provide an
 * "eval" function property. To allow replacing variable or property names,
 * provide a "switch" function. To handle deleting variables or properties,
 * provide a "delete" function.
 *
 * @param nsIDOMNode aParentNode
 *        The parent node to hold this view.
 * @param object aFlags [optional]
 *        An object contaning initialization options for this view.
 *        e.g. { lazyEmpty: true, searchEnabled: true ... }
 */
this.VariablesView = function VariablesView(aParentNode, aFlags = {}) {
  this._store = []; // Can't use a Map because Scope names needn't be unique.
  this._itemsByElement = new WeakMap();
  this._prevHierarchy = new Map();
  this._currHierarchy = new Map();

  this._parent = aParentNode;
  this._parent.classList.add("variables-view-container");
  this._parent.classList.add("theme-body");
  this._appendEmptyNotice();

  this._onSearchboxInput = this._onSearchboxInput.bind(this);
  this._onSearchboxKeyDown = this._onSearchboxKeyDown.bind(this);
  this._onViewKeyDown = this._onViewKeyDown.bind(this);

  // Create an internal scrollbox container.
  this._list = this.document.createElement("scrollbox");
  this._list.setAttribute("orient", "vertical");
  this._list.addEventListener("keydown", this._onViewKeyDown);
  this._parent.appendChild(this._list);

  for (let name in aFlags) {
    this[name] = aFlags[name];
  }

  EventEmitter.decorate(this);
};

VariablesView.prototype = {
  /**
   * Helper setter for populating this container with a raw object.
   *
   * @param object aObject
   *        The raw object to display. You can only provide this object
   *        if you want the variables view to work in sync mode.
   */
  set rawObject(aObject) {
    this.empty();
    this.addScope()
        .addItem(undefined, { enumerable: true })
        .populate(aObject, { sorted: true });
  },

  /**
   * Adds a scope to contain any inspected variables.
   *
   * This new scope will be considered the parent of any other scope
   * added afterwards.
   *
   * @param string aName
   *        The scope's name (e.g. "Local", "Global" etc.).
   * @param string aCustomClass
   *        An additional class name for the containing element.
   * @return Scope
   *         The newly created Scope instance.
   */
  addScope: function (aName = "", aCustomClass = "") {
    this._removeEmptyNotice();
    this._toggleSearchVisibility(true);

    let scope = new Scope(this, aName, { customClass: aCustomClass });
    this._store.push(scope);
    this._itemsByElement.set(scope._target, scope);
    this._currHierarchy.set(aName, scope);
    scope.header = !!aName;

    return scope;
  },

  /**
   * Removes all items from this container.
   *
   * @param number aTimeout [optional]
   *        The number of milliseconds to delay the operation if
   *        lazy emptying of this container is enabled.
   */
  empty: function (aTimeout = this.lazyEmptyDelay) {
    // If there are no items in this container, emptying is useless.
    if (!this._store.length) {
      return;
    }

    this._store.length = 0;
    this._itemsByElement = new WeakMap();
    this._prevHierarchy = this._currHierarchy;
    this._currHierarchy = new Map(); // Don't clear, this is just simple swapping.

    // Check if this empty operation may be executed lazily.
    if (this.lazyEmpty && aTimeout > 0) {
      this._emptySoon(aTimeout);
      return;
    }

    while (this._list.hasChildNodes()) {
      this._list.firstChild.remove();
    }

    this._appendEmptyNotice();
    this._toggleSearchVisibility(false);
  },

  /**
   * Emptying this container and rebuilding it immediately afterwards would
   * result in a brief redraw flicker, because the previously expanded nodes
   * may get asynchronously re-expanded, after fetching the prototype and
   * properties from a server.
   *
   * To avoid such behaviour, a normal container list is rebuild, but not
   * immediately attached to the parent container. The old container list
   * is kept around for a short period of time, hopefully accounting for the
   * data fetching delay. In the meantime, any operations can be executed
   * normally.
   *
   * @see VariablesView.empty
   * @see VariablesView.commitHierarchy
   */
  _emptySoon: function (aTimeout) {
    let prevList = this._list;
    let currList = this._list = this.document.createElement("scrollbox");

    this.window.setTimeout(() => {
      prevList.removeEventListener("keydown", this._onViewKeyDown);
      currList.addEventListener("keydown", this._onViewKeyDown);
      currList.setAttribute("orient", "vertical");

      this._parent.removeChild(prevList);
      this._parent.appendChild(currList);

      if (!this._store.length) {
        this._appendEmptyNotice();
        this._toggleSearchVisibility(false);
      }
    }, aTimeout);
  },

  /**
   * Optional DevTools toolbox containing this VariablesView. Used to
   * communicate with the inspector and highlighter.
   */
  toolbox: null,

  /**
   * The controller for this VariablesView, if it has one.
   */
  controller: null,

  /**
   * The amount of time (in milliseconds) it takes to empty this view lazily.
   */
  lazyEmptyDelay: LAZY_EMPTY_DELAY,

  /**
   * Specifies if this view may be emptied lazily.
   * @see VariablesView.prototype.empty
   */
  lazyEmpty: false,

  /**
   * Specifies if nodes in this view may be searched lazily.
   */
  lazySearch: true,

  /**
   * The number of elements in this container to jump when Page Up or Page Down
   * keys are pressed. If falsy, then the page size will be based on the
   * container height.
   */
  scrollPageSize: SCROLL_PAGE_SIZE_DEFAULT,

  /**
   * Function called each time a variable or property's value is changed via
   * user interaction. If null, then value changes are disabled.
   *
   * This property is applied recursively onto each scope in this view and
   * affects only the child nodes when they're created.
   */
  eval: null,

  /**
   * Function called each time a variable or property's name is changed via
   * user interaction. If null, then name changes are disabled.
   *
   * This property is applied recursively onto each scope in this view and
   * affects only the child nodes when they're created.
   */
  switch: null,

  /**
   * Function called each time a variable or property is deleted via
   * user interaction. If null, then deletions are disabled.
   *
   * This property is applied recursively onto each scope in this view and
   * affects only the child nodes when they're created.
   */
  delete: null,

  /**
   * Function called each time a property is added via user interaction. If
   * null, then property additions are disabled.
   *
   * This property is applied recursively onto each scope in this view and
   * affects only the child nodes when they're created.
   */
  new: null,

  /**
   * Specifies if after an eval or switch operation, the variable or property
   * which has been edited should be disabled.
   */
  preventDisableOnChange: false,

  /**
   * Specifies if, whenever a variable or property descriptor is available,
   * configurable, enumerable, writable, frozen, sealed and extensible
   * attributes should not affect presentation.
   *
   * This flag is applied recursively onto each scope in this view and
   * affects only the child nodes when they're created.
   */
  preventDescriptorModifiers: false,

  /**
   * The tooltip text shown on a variable or property's value if an |eval|
   * function is provided, in order to change the variable or property's value.
   *
   * This flag is applied recursively onto each scope in this view and
   * affects only the child nodes when they're created.
   */
  editableValueTooltip: L10N.getStr("variablesEditableValueTooltip"),

  /**
   * The tooltip text shown on a variable or property's name if a |switch|
   * function is provided, in order to change the variable or property's name.
   *
   * This flag is applied recursively onto each scope in this view and
   * affects only the child nodes when they're created.
   */
  editableNameTooltip: L10N.getStr("variablesEditableNameTooltip"),

  /**
   * The tooltip text shown on a variable or property's edit button if an
   * |eval| function is provided and a getter/setter descriptor is present,
   * in order to change the variable or property to a plain value.
   *
   * This flag is applied recursively onto each scope in this view and
   * affects only the child nodes when they're created.
   */
  editButtonTooltip: L10N.getStr("variablesEditButtonTooltip"),

  /**
   * The tooltip text shown on a variable or property's value if that value is
   * a DOMNode that can be highlighted and selected in the inspector.
   *
   * This flag is applied recursively onto each scope in this view and
   * affects only the child nodes when they're created.
   */
  domNodeValueTooltip: L10N.getStr("variablesDomNodeValueTooltip"),

  /**
   * The tooltip text shown on a variable or property's delete button if a
   * |delete| function is provided, in order to delete the variable or property.
   *
   * This flag is applied recursively onto each scope in this view and
   * affects only the child nodes when they're created.
   */
  deleteButtonTooltip: L10N.getStr("variablesCloseButtonTooltip"),

  /**
   * Specifies the context menu attribute set on variables and properties.
   *
   * This flag is applied recursively onto each scope in this view and
   * affects only the child nodes when they're created.
   */
  contextMenuId: "",

  /**
   * The separator label between the variables or properties name and value.
   *
   * This flag is applied recursively onto each scope in this view and
   * affects only the child nodes when they're created.
   */
  separatorStr: L10N.getStr("variablesSeparatorLabel"),

  /**
   * Specifies if enumerable properties and variables should be displayed.
   * These variables and properties are visible by default.
   * @param boolean aFlag
   */
  set enumVisible(aFlag) {
    this._enumVisible = aFlag;

    for (let scope of this._store) {
      scope._enumVisible = aFlag;
    }
  },

  /**
   * Specifies if non-enumerable properties and variables should be displayed.
   * These variables and properties are visible by default.
   * @param boolean aFlag
   */
  set nonEnumVisible(aFlag) {
    this._nonEnumVisible = aFlag;

    for (let scope of this._store) {
      scope._nonEnumVisible = aFlag;
    }
  },

  /**
   * Specifies if only enumerable properties and variables should be displayed.
   * Both types of these variables and properties are visible by default.
   * @param boolean aFlag
   */
  set onlyEnumVisible(aFlag) {
    if (aFlag) {
      this.enumVisible = true;
      this.nonEnumVisible = false;
    } else {
      this.enumVisible = true;
      this.nonEnumVisible = true;
    }
  },

  /**
   * Sets if the variable and property searching is enabled.
   * @param boolean aFlag
   */
  set searchEnabled(aFlag) {
    aFlag ? this._enableSearch() : this._disableSearch();
  },

  /**
   * Gets if the variable and property searching is enabled.
   * @return boolean
   */
  get searchEnabled() {
    return !!this._searchboxContainer;
  },

  /**
   * Sets the text displayed for the searchbox in this container.
   * @param string aValue
   */
  set searchPlaceholder(aValue) {
    if (this._searchboxNode) {
      this._searchboxNode.setAttribute("placeholder", aValue);
    }
    this._searchboxPlaceholder = aValue;
  },

  /**
   * Gets the text displayed for the searchbox in this container.
   * @return string
   */
  get searchPlaceholder() {
    return this._searchboxPlaceholder;
  },

  /**
   * Enables variable and property searching in this view.
   * Use the "searchEnabled" setter to enable searching.
   */
  _enableSearch: function () {
    // If searching was already enabled, no need to re-enable it again.
    if (this._searchboxContainer) {
      return;
    }
    let document = this.document;
    let ownerNode = this._parent.parentNode;

    let container = this._searchboxContainer = document.createElement("hbox");
    container.className = "devtools-toolbar";

    // Hide the variables searchbox container if there are no variables or
    // properties to display.
    container.hidden = !this._store.length;

    let searchbox = this._searchboxNode = document.createElement("textbox");
    searchbox.className = "variables-view-searchinput devtools-filterinput";
    searchbox.setAttribute("placeholder", this._searchboxPlaceholder);
    searchbox.setAttribute("type", "search");
    searchbox.setAttribute("flex", "1");
    searchbox.addEventListener("command", this._onSearchboxInput);
    searchbox.addEventListener("keydown", this._onSearchboxKeyDown);

    container.appendChild(searchbox);
    ownerNode.insertBefore(container, this._parent);
  },

  /**
   * Disables variable and property searching in this view.
   * Use the "searchEnabled" setter to disable searching.
   */
  _disableSearch: function () {
    // If searching was already disabled, no need to re-disable it again.
    if (!this._searchboxContainer) {
      return;
    }
    this._searchboxContainer.remove();
    this._searchboxNode.removeEventListener("command", this._onSearchboxInput);
    this._searchboxNode.removeEventListener("keydown", this._onSearchboxKeyDown);

    this._searchboxContainer = null;
    this._searchboxNode = null;
  },

  /**
   * Sets the variables searchbox container hidden or visible.
   * It's hidden by default.
   *
   * @param boolean aVisibleFlag
   *        Specifies the intended visibility.
   */
  _toggleSearchVisibility: function (aVisibleFlag) {
    // If searching was already disabled, there's no need to hide it.
    if (!this._searchboxContainer) {
      return;
    }
    this._searchboxContainer.hidden = !aVisibleFlag;
  },

  /**
   * Listener handling the searchbox input event.
   */
  _onSearchboxInput: function () {
    this.scheduleSearch(this._searchboxNode.value);
  },

  /**
   * Listener handling the searchbox keydown event.
   */
  _onSearchboxKeyDown: function (e) {
    switch (e.keyCode) {
      case KeyCodes.DOM_VK_RETURN:
        this._onSearchboxInput();
        return;
      case KeyCodes.DOM_VK_ESCAPE:
        this._searchboxNode.value = "";
        this._onSearchboxInput();
        return;
    }
  },

  /**
   * Schedules searching for variables or properties matching the query.
   *
   * @param string aToken
   *        The variable or property to search for.
   * @param number aWait
   *        The amount of milliseconds to wait until draining.
   */
  scheduleSearch: function (aToken, aWait) {
    // Check if this search operation may not be executed lazily.
    if (!this.lazySearch) {
      this._doSearch(aToken);
      return;
    }

    // The amount of time to wait for the requests to settle.
    let maxDelay = SEARCH_ACTION_MAX_DELAY;
    let delay = aWait === undefined ? maxDelay / aToken.length : aWait;

    // Allow requests to settle down first.
    setNamedTimeout("vview-search", delay, () => this._doSearch(aToken));
  },

  /**
   * Performs a case insensitive search for variables or properties matching
   * the query, and hides non-matched items.
   *
   * If aToken is falsy, then all the scopes are unhidden and expanded,
   * while the available variables and properties inside those scopes are
   * just unhidden.
   *
   * @param string aToken
   *        The variable or property to search for.
   */
  _doSearch: function (aToken) {
    if (this.controller && this.controller.supportsSearch()) {
      // Retrieve the main Scope in which we add attributes
      let scope = this._store[0]._store.get(undefined);
      if (!aToken) {
        // Prune the view from old previous content
        // so that we delete the intermediate search results
        // we created in previous searches
        for (let property of scope._store.values()) {
          property.remove();
        }
      }
      // Retrieve new attributes eventually hidden in splits
      this.controller.performSearch(scope, aToken);
      // Filter already displayed attributes
      if (aToken) {
        scope._performSearch(aToken.toLowerCase());
      }
      return;
    }
    for (let scope of this._store) {
      switch (aToken) {
        case "":
        case null:
        case undefined:
          scope.expand();
          scope._performSearch("");
          break;
        default:
          scope._performSearch(aToken.toLowerCase());
          break;
      }
    }
  },

  /**
   * Find the first item in the tree of visible items in this container that
   * matches the predicate. Searches in visual order (the order seen by the
   * user). Descends into each scope to check the scope and its children.
   *
   * @param function aPredicate
   *        A function that returns true when a match is found.
   * @return Scope | Variable | Property
   *         The first visible scope, variable or property, or null if nothing
   *         is found.
   */
  _findInVisibleItems: function (aPredicate) {
    for (let scope of this._store) {
      let result = scope._findInVisibleItems(aPredicate);
      if (result) {
        return result;
      }
    }
    return null;
  },

  /**
   * Find the last item in the tree of visible items in this container that
   * matches the predicate. Searches in reverse visual order (opposite of the
   * order seen by the user). Descends into each scope to check the scope and
   * its children.
   *
   * @param function aPredicate
   *        A function that returns true when a match is found.
   * @return Scope | Variable | Property
   *         The last visible scope, variable or property, or null if nothing
   *         is found.
   */
  _findInVisibleItemsReverse: function (aPredicate) {
    for (let i = this._store.length - 1; i >= 0; i--) {
      let scope = this._store[i];
      let result = scope._findInVisibleItemsReverse(aPredicate);
      if (result) {
        return result;
      }
    }
    return null;
  },

  /**
   * Gets the scope at the specified index.
   *
   * @param number aIndex
   *        The scope's index.
   * @return Scope
   *         The scope if found, undefined if not.
   */
  getScopeAtIndex: function (aIndex) {
    return this._store[aIndex];
  },

  /**
   * Recursively searches this container for the scope, variable or property
   * displayed by the specified node.
   *
   * @param nsIDOMNode aNode
   *        The node to search for.
   * @return Scope | Variable | Property
   *         The matched scope, variable or property, or null if nothing is found.
   */
  getItemForNode: function (aNode) {
    return this._itemsByElement.get(aNode);
  },

  /**
   * Gets the scope owning a Variable or Property.
   *
   * @param Variable | Property
   *        The variable or property to retrieven the owner scope for.
   * @return Scope
   *         The owner scope.
   */
  getOwnerScopeForVariableOrProperty: function (aItem) {
    if (!aItem) {
      return null;
    }
    // If this is a Scope, return it.
    if (!(aItem instanceof Variable)) {
      return aItem;
    }
    // If this is a Variable or Property, find its owner scope.
    if (aItem instanceof Variable && aItem.ownerView) {
      return this.getOwnerScopeForVariableOrProperty(aItem.ownerView);
    }
    return null;
  },

  /**
   * Gets the parent scopes for a specified Variable or Property.
   * The returned list will not include the owner scope.
   *
   * @param Variable | Property
   *        The variable or property for which to find the parent scopes.
   * @return array
   *         A list of parent Scopes.
   */
  getParentScopesForVariableOrProperty: function (aItem) {
    let scope = this.getOwnerScopeForVariableOrProperty(aItem);
    return this._store.slice(0, Math.max(this._store.indexOf(scope), 0));
  },

  /**
   * Gets the currently focused scope, variable or property in this view.
   *
   * @return Scope | Variable | Property
   *         The focused scope, variable or property, or null if nothing is found.
   */
  getFocusedItem: function () {
    let focused = this.document.commandDispatcher.focusedElement;
    return this.getItemForNode(focused);
  },

  /**
   * Focuses the first visible scope, variable, or property in this container.
   */
  focusFirstVisibleItem: function () {
    let focusableItem = this._findInVisibleItems(item => item.focusable);
    if (focusableItem) {
      this._focusItem(focusableItem);
    }
    this._parent.scrollTop = 0;
    this._parent.scrollLeft = 0;
  },

  /**
   * Focuses the last visible scope, variable, or property in this container.
   */
  focusLastVisibleItem: function () {
    let focusableItem = this._findInVisibleItemsReverse(item => item.focusable);
    if (focusableItem) {
      this._focusItem(focusableItem);
    }
    this._parent.scrollTop = this._parent.scrollHeight;
    this._parent.scrollLeft = 0;
  },

  /**
   * Focuses the next scope, variable or property in this view.
   */
  focusNextItem: function () {
    this.focusItemAtDelta(+1);
  },

  /**
   * Focuses the previous scope, variable or property in this view.
   */
  focusPrevItem: function () {
    this.focusItemAtDelta(-1);
  },

  /**
   * Focuses another scope, variable or property in this view, based on
   * the index distance from the currently focused item.
   *
   * @param number aDelta
   *        A scalar specifying by how many items should the selection change.
   */
  focusItemAtDelta: function (aDelta) {
    let direction = aDelta > 0 ? "advanceFocus" : "rewindFocus";
    let distance = Math.abs(Math[aDelta > 0 ? "ceil" : "floor"](aDelta));
    while (distance--) {
      if (!this._focusChange(direction)) {
        break; // Out of bounds.
      }
    }
  },

  /**
   * Focuses the next or previous scope, variable or property in this view.
   *
   * @param string aDirection
   *        Either "advanceFocus" or "rewindFocus".
   * @return boolean
   *         False if the focus went out of bounds and the first or last element
   *         in this view was focused instead.
   */
  _focusChange: function (aDirection) {
    let commandDispatcher = this.document.commandDispatcher;
    let prevFocusedElement = commandDispatcher.focusedElement;
    let currFocusedItem = null;

    do {
      commandDispatcher.suppressFocusScroll = true;
      commandDispatcher[aDirection]();

      // Make sure the newly focused item is a part of this view.
      // If the focus goes out of bounds, revert the previously focused item.
      if (!(currFocusedItem = this.getFocusedItem())) {
        prevFocusedElement.focus();
        return false;
      }
    } while (!currFocusedItem.focusable);

    // Focus remained within bounds.
    return true;
  },

  /**
   * Focuses a scope, variable or property and makes sure it's visible.
   *
   * @param aItem Scope | Variable | Property
   *        The item to focus.
   * @param boolean aCollapseFlag
   *        True if the focused item should also be collapsed.
   * @return boolean
   *         True if the item was successfully focused.
   */
  _focusItem: function (aItem, aCollapseFlag) {
    if (!aItem.focusable) {
      return false;
    }
    if (aCollapseFlag) {
      aItem.collapse();
    }
    aItem._target.focus();
    this.boxObject.ensureElementIsVisible(aItem._arrow);
    return true;
  },

  /**
   * Listener handling a key down event on the view.
   */
  _onViewKeyDown: function (e) {
    let item = this.getFocusedItem();

    // Prevent scrolling when pressing navigation keys.
    ViewHelpers.preventScrolling(e);

    switch (e.keyCode) {
      case KeyCodes.DOM_VK_C:
        // Copy current selection to clipboard.
        if (e.ctrlKey || e.metaKey) {
          let item = this.getFocusedItem();
          clipboardHelper.copyString(
            item._nameString + item.separatorStr + item._valueString
          );
        }
        return;

      case KeyCodes.DOM_VK_UP:
        // Always rewind focus.
        this.focusPrevItem(true);
        return;

      case KeyCodes.DOM_VK_DOWN:
        // Always advance focus.
        this.focusNextItem(true);
        return;

      case KeyCodes.DOM_VK_LEFT:
        // Collapse scopes, variables and properties before rewinding focus.
        if (item._isExpanded && item._isArrowVisible) {
          item.collapse();
        } else {
          this._focusItem(item.ownerView);
        }
        return;

      case KeyCodes.DOM_VK_RIGHT:
        // Nothing to do here if this item never expands.
        if (!item._isArrowVisible) {
          return;
        }
        // Expand scopes, variables and properties before advancing focus.
        if (!item._isExpanded) {
          item.expand();
        } else {
          this.focusNextItem(true);
        }
        return;

      case KeyCodes.DOM_VK_PAGE_UP:
        // Rewind a certain number of elements based on the container height.
        this.focusItemAtDelta(-(this.scrollPageSize || Math.min(Math.floor(this._list.scrollHeight /
          PAGE_SIZE_SCROLL_HEIGHT_RATIO),
          PAGE_SIZE_MAX_JUMPS)));
        return;

      case KeyCodes.DOM_VK_PAGE_DOWN:
        // Advance a certain number of elements based on the container height.
        this.focusItemAtDelta(+(this.scrollPageSize || Math.min(Math.floor(this._list.scrollHeight /
          PAGE_SIZE_SCROLL_HEIGHT_RATIO),
          PAGE_SIZE_MAX_JUMPS)));
        return;

      case KeyCodes.DOM_VK_HOME:
        this.focusFirstVisibleItem();
        return;

      case KeyCodes.DOM_VK_END:
        this.focusLastVisibleItem();
        return;

      case KeyCodes.DOM_VK_RETURN:
        // Start editing the value or name of the Variable or Property.
        if (item instanceof Variable) {
          if (e.metaKey || e.altKey || e.shiftKey) {
            item._activateNameInput();
          } else {
            item._activateValueInput();
          }
        }
        return;

      case KeyCodes.DOM_VK_DELETE:
      case KeyCodes.DOM_VK_BACK_SPACE:
        // Delete the Variable or Property if allowed.
        if (item instanceof Variable) {
          item._onDelete(e);
        }
        return;

      case KeyCodes.DOM_VK_INSERT:
        item._onAddProperty(e);
        return;
    }
  },

  /**
   * Sets the text displayed in this container when there are no available items.
   * @param string aValue
   */
  set emptyText(aValue) {
    if (this._emptyTextNode) {
      this._emptyTextNode.setAttribute("value", aValue);
    }
    this._emptyTextValue = aValue;
    this._appendEmptyNotice();
  },

  /**
   * Creates and appends a label signaling that this container is empty.
   */
  _appendEmptyNotice: function () {
    if (this._emptyTextNode || !this._emptyTextValue) {
      return;
    }

    let label = this.document.createElement("label");
    label.className = "variables-view-empty-notice";
    label.setAttribute("value", this._emptyTextValue);

    this._parent.appendChild(label);
    this._emptyTextNode = label;
  },

  /**
   * Removes the label signaling that this container is empty.
   */
  _removeEmptyNotice: function () {
    if (!this._emptyTextNode) {
      return;
    }

    this._parent.removeChild(this._emptyTextNode);
    this._emptyTextNode = null;
  },

  /**
   * Gets if all values should be aligned together.
   * @return boolean
   */
  get alignedValues() {
    return this._alignedValues;
  },

  /**
   * Sets if all values should be aligned together.
   * @param boolean aFlag
   */
  set alignedValues(aFlag) {
    this._alignedValues = aFlag;
    if (aFlag) {
      this._parent.setAttribute("aligned-values", "");
    } else {
      this._parent.removeAttribute("aligned-values");
    }
  },

  /**
   * Gets if action buttons (like delete) should be placed at the beginning or
   * end of a line.
   * @return boolean
   */
  get actionsFirst() {
    return this._actionsFirst;
  },

  /**
   * Sets if action buttons (like delete) should be placed at the beginning or
   * end of a line.
   * @param boolean aFlag
   */
  set actionsFirst(aFlag) {
    this._actionsFirst = aFlag;
    if (aFlag) {
      this._parent.setAttribute("actions-first", "");
    } else {
      this._parent.removeAttribute("actions-first");
    }
  },

  /**
   * Gets the parent node holding this view.
   * @return nsIDOMNode
   */
  get boxObject() {
    return this._list.boxObject;
  },

  /**
   * Gets the parent node holding this view.
   * @return nsIDOMNode
   */
  get parentNode() {
    return this._parent;
  },

  /**
   * Gets the owner document holding this view.
   * @return nsIHTMLDocument
   */
  get document() {
    return this._document || (this._document = this._parent.ownerDocument);
  },

  /**
   * Gets the default window holding this view.
   * @return nsIDOMWindow
   */
  get window() {
    return this._window || (this._window = this.document.defaultView);
  },

  _document: null,
  _window: null,

  _store: null,
  _itemsByElement: null,
  _prevHierarchy: null,
  _currHierarchy: null,

  _enumVisible: true,
  _nonEnumVisible: true,
  _alignedValues: false,
  _actionsFirst: false,

  _parent: null,
  _list: null,
  _searchboxNode: null,
  _searchboxContainer: null,
  _searchboxPlaceholder: "",
  _emptyTextNode: null,
  _emptyTextValue: ""
};

VariablesView.NON_SORTABLE_CLASSES = [
  "Array",
  "Int8Array",
  "Uint8Array",
  "Uint8ClampedArray",
  "Int16Array",
  "Uint16Array",
  "Int32Array",
  "Uint32Array",
  "Float32Array",
  "Float64Array",
  "NodeList"
];

/**
 * Determine whether an object's properties should be sorted based on its class.
 *
 * @param string aClassName
 *        The class of the object.
 */
VariablesView.isSortable = function (aClassName) {
  return !VariablesView.NON_SORTABLE_CLASSES.includes(aClassName);
};

/**
 * Generates the string evaluated when performing simple value changes.
 *
 * @param Variable | Property aItem
 *        The current variable or property.
 * @param string aCurrentString
 *        The trimmed user inputted string.
 * @param string aPrefix [optional]
 *        Prefix for the symbolic name.
 * @return string
 *         The string to be evaluated.
 */
VariablesView.simpleValueEvalMacro = function (aItem, aCurrentString, aPrefix = "") {
  return aPrefix + aItem.symbolicName + "=" + aCurrentString;
};

/**
 * Generates the string evaluated when overriding getters and setters with
 * plain values.
 *
 * @param Property aItem
 *        The current getter or setter property.
 * @param string aCurrentString
 *        The trimmed user inputted string.
 * @param string aPrefix [optional]
 *        Prefix for the symbolic name.
 * @return string
 *         The string to be evaluated.
 */
VariablesView.overrideValueEvalMacro = function (aItem, aCurrentString, aPrefix = "") {
  let property = escapeString(aItem._nameString);
  let parent = aPrefix + aItem.ownerView.symbolicName || "this";

  return "Object.defineProperty(" + parent + "," + property + "," +
    "{ value: " + aCurrentString +
    ", enumerable: " + parent + ".propertyIsEnumerable(" + property + ")" +
    ", configurable: true" +
    ", writable: true" +
    "})";
};

/**
 * Generates the string evaluated when performing getters and setters changes.
 *
 * @param Property aItem
 *        The current getter or setter property.
 * @param string aCurrentString
 *        The trimmed user inputted string.
 * @param string aPrefix [optional]
 *        Prefix for the symbolic name.
 * @return string
 *         The string to be evaluated.
 */
VariablesView.getterOrSetterEvalMacro = function (aItem, aCurrentString, aPrefix = "") {
  let type = aItem._nameString;
  let propertyObject = aItem.ownerView;
  let parentObject = propertyObject.ownerView;
  let property = escapeString(propertyObject._nameString);
  let parent = aPrefix + parentObject.symbolicName || "this";

  switch (aCurrentString) {
    case "":
    case "null":
    case "undefined":
      let mirrorType = type == "get" ? "set" : "get";
      let mirrorLookup = type == "get" ? "__lookupSetter__" : "__lookupGetter__";

      // If the parent object will end up without any getter or setter,
      // morph it into a plain value.
      if ((type == "set" && propertyObject.getter.type == "undefined") ||
          (type == "get" && propertyObject.setter.type == "undefined")) {
        // Make sure the right getter/setter to value override macro is applied
        // to the target object.
        return propertyObject.evaluationMacro(propertyObject, "undefined", aPrefix);
      }

      // Construct and return the getter/setter removal evaluation string.
      // e.g: Object.defineProperty(foo, "bar", {
      //   get: foo.__lookupGetter__("bar"),
      //   set: undefined,
      //   enumerable: true,
      //   configurable: true
      // })
      return "Object.defineProperty(" + parent + "," + property + "," +
        "{" + mirrorType + ":" + parent + "." + mirrorLookup + "(" + property + ")" +
        "," + type + ":" + undefined +
        ", enumerable: " + parent + ".propertyIsEnumerable(" + property + ")" +
        ", configurable: true" +
        "})";

    default:
      // Wrap statements inside a function declaration if not already wrapped.
      if (!aCurrentString.startsWith("function")) {
        let header = "function(" + (type == "set" ? "value" : "") + ")";
        let body = "";
        // If there's a return statement explicitly written, always use the
        // standard function definition syntax
        if (aCurrentString.includes("return ")) {
          body = "{" + aCurrentString + "}";
        }
        // If block syntax is used, use the whole string as the function body.
        else if (aCurrentString.startsWith("{")) {
          body = aCurrentString;
        }
        // Prefer an expression closure.
        else {
          body = "(" + aCurrentString + ")";
        }
        aCurrentString = header + body;
      }

      // Determine if a new getter or setter should be defined.
      let defineType = type == "get" ? "__defineGetter__" : "__defineSetter__";

      // Make sure all quotes are escaped in the expression's syntax,
      let defineFunc = "eval(\"(" + aCurrentString.replace(/"/g, "\\$&") + ")\")";

      // Construct and return the getter/setter evaluation string.
      // e.g: foo.__defineGetter__("bar", eval("(function() { return 42; })"))
      return parent + "." + defineType + "(" + property + "," + defineFunc + ")";
  }
};

/**
 * Function invoked when a getter or setter is deleted.
 *
 * @param Property aItem
 *        The current getter or setter property.
 */
VariablesView.getterOrSetterDeleteCallback = function (aItem) {
  aItem._disable();

  // Make sure the right getter/setter to value override macro is applied
  // to the target object.
  aItem.ownerView.eval(aItem, "");

  return true; // Don't hide the element.
};


/**
 * A Scope is an object holding Variable instances.
 * Iterable via "for (let [name, variable] of instance) { }".
 *
 * @param VariablesView aView
 *        The view to contain this scope.
 * @param string aName
 *        The scope's name.
 * @param object aFlags [optional]
 *        Additional options or flags for this scope.
 */
function Scope(aView, aName, aFlags = {}) {
  this.ownerView = aView;

  this._onClick = this._onClick.bind(this);
  this._openEnum = this._openEnum.bind(this);
  this._openNonEnum = this._openNonEnum.bind(this);

  // Inherit properties and flags from the parent view. You can override
  // each of these directly onto any scope, variable or property instance.
  this.scrollPageSize = aView.scrollPageSize;
  this.eval = aView.eval;
  this.switch = aView.switch;
  this.delete = aView.delete;
  this.new = aView.new;
  this.preventDisableOnChange = aView.preventDisableOnChange;
  this.preventDescriptorModifiers = aView.preventDescriptorModifiers;
  this.editableNameTooltip = aView.editableNameTooltip;
  this.editableValueTooltip = aView.editableValueTooltip;
  this.editButtonTooltip = aView.editButtonTooltip;
  this.deleteButtonTooltip = aView.deleteButtonTooltip;
  this.domNodeValueTooltip = aView.domNodeValueTooltip;
  this.contextMenuId = aView.contextMenuId;
  this.separatorStr = aView.separatorStr;

  this._init(aName, aFlags);
}

Scope.prototype = {
  /**
   * Whether this Scope should be prefetched when it is remoted.
   */
  shouldPrefetch: true,

  /**
   * Whether this Scope should paginate its contents.
   */
  allowPaginate: false,

  /**
   * The class name applied to this scope's target element.
   */
  targetClassName: "variables-view-scope",

  /**
   * Create a new Variable that is a child of this Scope.
   *
   * @param string aName
   *        The name of the new Property.
   * @param object aDescriptor
   *        The variable's descriptor.
   * @param object aOptions
   *        Options of the form accepted by addItem.
   * @return Variable
   *         The newly created child Variable.
   */
  _createChild: function (aName, aDescriptor, aOptions) {
    return new Variable(this, aName, aDescriptor, aOptions);
  },

  /**
   * Adds a child to contain any inspected properties.
   *
   * @param string aName
   *        The child's name.
   * @param object aDescriptor
   *        Specifies the value and/or type & class of the child,
   *        or 'get' & 'set' accessor properties. If the type is implicit,
   *        it will be inferred from the value. If this parameter is omitted,
   *        a property without a value will be added (useful for branch nodes).
   *        e.g. - { value: 42 }
   *             - { value: true }
   *             - { value: "nasu" }
   *             - { value: { type: "undefined" } }
   *             - { value: { type: "null" } }
   *             - { value: { type: "object", class: "Object" } }
   *             - { get: { type: "object", class: "Function" },
   *                 set: { type: "undefined" } }
   * @param object aOptions
   *        Specifies some options affecting the new variable.
   *        Recognized properties are
   *        * boolean relaxed  true if name duplicates should be allowed.
   *                           You probably shouldn't do it. Use this
   *                           with caution.
   *        * boolean internalItem  true if the item is internally generated.
   *                           This is used for special variables
   *                           like <return> or <exception> and distinguishes
   *                           them from ordinary properties that happen
   *                           to have the same name
   * @return Variable
   *         The newly created Variable instance, null if it already exists.
   */
  addItem: function (aName, aDescriptor = {}, aOptions = {}) {
    let {relaxed} = aOptions;
    if (this._store.has(aName) && !relaxed) {
      return this._store.get(aName);
    }

    let child = this._createChild(aName, aDescriptor, aOptions);
    this._store.set(aName, child);
    this._variablesView._itemsByElement.set(child._target, child);
    this._variablesView._currHierarchy.set(child.absoluteName, child);
    child.header = aName !== undefined;

    return child;
  },

  /**
   * Adds items for this variable.
   *
   * @param object aItems
   *        An object containing some { name: descriptor } data properties,
   *        specifying the value and/or type & class of the variable,
   *        or 'get' & 'set' accessor properties. If the type is implicit,
   *        it will be inferred from the value.
   *        e.g. - { someProp0: { value: 42 },
   *                 someProp1: { value: true },
   *                 someProp2: { value: "nasu" },
   *                 someProp3: { value: { type: "undefined" } },
   *                 someProp4: { value: { type: "null" } },
   *                 someProp5: { value: { type: "object", class: "Object" } },
   *                 someProp6: { get: { type: "object", class: "Function" },
   *                              set: { type: "undefined" } } }
   * @param object aOptions [optional]
   *        Additional options for adding the properties. Supported options:
   *        - sorted: true to sort all the properties before adding them
   *        - callback: function invoked after each item is added
   */
  addItems: function (aItems, aOptions = {}) {
    let names = Object.keys(aItems);

    // Sort all of the properties before adding them, if preferred.
    if (aOptions.sorted) {
      names.sort(this._naturalSort);
    }

    // Add the properties to the current scope.
    for (let name of names) {
      let descriptor = aItems[name];
      let item = this.addItem(name, descriptor);

      if (aOptions.callback) {
        aOptions.callback(item, descriptor && descriptor.value);
      }
    }
  },

  /**
   * Remove this Scope from its parent and remove all children recursively.
   */
  remove: function () {
    let view = this._variablesView;
    view._store.splice(view._store.indexOf(this), 1);
    view._itemsByElement.delete(this._target);
    view._currHierarchy.delete(this._nameString);

    this._target.remove();

    for (let variable of this._store.values()) {
      variable.remove();
    }
  },

  /**
   * Gets the variable in this container having the specified name.
   *
   * @param string aName
   *        The name of the variable to get.
   * @return Variable
   *         The matched variable, or null if nothing is found.
   */
  get: function (aName) {
    return this._store.get(aName);
  },

  /**
   * Recursively searches for the variable or property in this container
   * displayed by the specified node.
   *
   * @param nsIDOMNode aNode
   *        The node to search for.
   * @return Variable | Property
   *         The matched variable or property, or null if nothing is found.
   */
  find: function (aNode) {
    for (let [, variable] of this._store) {
      let match;
      if (variable._target == aNode) {
        match = variable;
      } else {
        match = variable.find(aNode);
      }
      if (match) {
        return match;
      }
    }
    return null;
  },

  /**
   * Determines if this scope is a direct child of a parent variables view,
   * scope, variable or property.
   *
   * @param VariablesView | Scope | Variable | Property
   *        The parent to check.
   * @return boolean
   *         True if the specified item is a direct child, false otherwise.
   */
  isChildOf: function (aParent) {
    return this.ownerView == aParent;
  },

  /**
   * Determines if this scope is a descendant of a parent variables view,
   * scope, variable or property.
   *
   * @param VariablesView | Scope | Variable | Property
   *        The parent to check.
   * @return boolean
   *         True if the specified item is a descendant, false otherwise.
   */
  isDescendantOf: function (aParent) {
    if (this.isChildOf(aParent)) {
      return true;
    }

    // Recurse to parent if it is a Scope, Variable, or Property.
    if (this.ownerView instanceof Scope) {
      return this.ownerView.isDescendantOf(aParent);
    }

    return false;
  },

  /**
   * Shows the scope.
   */
  show: function () {
    this._target.hidden = false;
    this._isContentVisible = true;

    if (this.onshow) {
      this.onshow(this);
    }
  },

  /**
   * Hides the scope.
   */
  hide: function () {
    this._target.hidden = true;
    this._isContentVisible = false;

    if (this.onhide) {
      this.onhide(this);
    }
  },

  /**
   * Expands the scope, showing all the added details.
   */
  expand: function () {
    if (this._isExpanded || this._isLocked) {
      return;
    }
    if (this._variablesView._enumVisible) {
      this._openEnum();
    }
    if (this._variablesView._nonEnumVisible) {
      Services.tm.dispatchToMainThread({ run: this._openNonEnum });
    }
    this._isExpanded = true;

    if (this.onexpand) {
      // We return onexpand as it sometimes returns a promise
      // (up to the user of VariableView to do it)
      // that can indicate when the view is done expanding
      // and attributes are available. (Mostly used for tests)
      return this.onexpand(this);
    }
  },

  /**
   * Collapses the scope, hiding all the added details.
   */
  collapse: function () {
    if (!this._isExpanded || this._isLocked) {
      return;
    }
    this._arrow.removeAttribute("open");
    this._enum.removeAttribute("open");
    this._nonenum.removeAttribute("open");
    this._isExpanded = false;

    if (this.oncollapse) {
      this.oncollapse(this);
    }
  },

  /**
   * Toggles between the scope's collapsed and expanded state.
   */
  toggle: function (e) {
    if (e && e.button != 0) {
      // Only allow left-click to trigger this event.
      return;
    }
    this.expanded ^= 1;

    // Make sure the scope and its contents are visibile.
    for (let [, variable] of this._store) {
      variable.header = true;
      variable._matched = true;
    }
    if (this.ontoggle) {
      this.ontoggle(this);
    }
  },

  /**
   * Shows the scope's title header.
   */
  showHeader: function () {
    if (this._isHeaderVisible || !this._nameString) {
      return;
    }
    this._target.removeAttribute("untitled");
    this._isHeaderVisible = true;
  },

  /**
   * Hides the scope's title header.
   * This action will automatically expand the scope.
   */
  hideHeader: function () {
    if (!this._isHeaderVisible) {
      return;
    }
    this.expand();
    this._target.setAttribute("untitled", "");
    this._isHeaderVisible = false;
  },

  /**
   * Sort in ascending order
   * This only needs to compare non-numbers since it is dealing with an array
   * which numeric-based indices are placed in order.
   *
   * @param string a
   * @param string b
   * @return number
   *         -1 if a is less than b, 0 if no change in order, +1 if a is greater than 0
   */
  _naturalSort: function (a, b) {
    if (isNaN(parseFloat(a)) && isNaN(parseFloat(b))) {
      return a < b ? -1 : 1;
    }
  },

  /**
   * Shows the scope's expand/collapse arrow.
   */
  showArrow: function () {
    if (this._isArrowVisible) {
      return;
    }
    this._arrow.removeAttribute("invisible");
    this._isArrowVisible = true;
  },

  /**
   * Hides the scope's expand/collapse arrow.
   */
  hideArrow: function () {
    if (!this._isArrowVisible) {
      return;
    }
    this._arrow.setAttribute("invisible", "");
    this._isArrowVisible = false;
  },

  /**
   * Gets the visibility state.
   * @return boolean
   */
  get visible() {
    return this._isContentVisible;
  },

  /**
   * Gets the expanded state.
   * @return boolean
   */
  get expanded() {
    return this._isExpanded;
  },

  /**
   * Gets the header visibility state.
   * @return boolean
   */
  get header() {
    return this._isHeaderVisible;
  },

  /**
   * Gets the twisty visibility state.
   * @return boolean
   */
  get twisty() {
    return this._isArrowVisible;
  },

  /**
   * Gets the expand lock state.
   * @return boolean
   */
  get locked() {
    return this._isLocked;
  },

  /**
   * Sets the visibility state.
   * @param boolean aFlag
   */
  set visible(aFlag) {
    aFlag ? this.show() : this.hide();
  },

  /**
   * Sets the expanded state.
   * @param boolean aFlag
   */
  set expanded(aFlag) {
    aFlag ? this.expand() : this.collapse();
  },

  /**
   * Sets the header visibility state.
   * @param boolean aFlag
   */
  set header(aFlag) {
    aFlag ? this.showHeader() : this.hideHeader();
  },

  /**
   * Sets the twisty visibility state.
   * @param boolean aFlag
   */
  set twisty(aFlag) {
    aFlag ? this.showArrow() : this.hideArrow();
  },

  /**
   * Sets the expand lock state.
   * @param boolean aFlag
   */
  set locked(aFlag) {
    this._isLocked = aFlag;
  },

  /**
   * Specifies if this target node may be focused.
   * @return boolean
   */
  get focusable() {
    // Check if this target node is actually visibile.
    if (!this._nameString ||
        !this._isContentVisible ||
        !this._isHeaderVisible ||
        !this._isMatch) {
      return false;
    }
    // Check if all parent objects are expanded.
    let item = this;

    // Recurse while parent is a Scope, Variable, or Property
    while ((item = item.ownerView) && item instanceof Scope) {
      if (!item._isExpanded) {
        return false;
      }
    }
    return true;
  },

  /**
   * Focus this scope.
   */
  focus: function () {
    this._variablesView._focusItem(this);
  },

  /**
   * Adds an event listener for a certain event on this scope's title.
   * @param string aName
   * @param function aCallback
   * @param boolean aCapture
   */
  addEventListener: function (aName, aCallback, aCapture) {
    this._title.addEventListener(aName, aCallback, aCapture);
  },

  /**
   * Removes an event listener for a certain event on this scope's title.
   * @param string aName
   * @param function aCallback
   * @param boolean aCapture
   */
  removeEventListener: function (aName, aCallback, aCapture) {
    this._title.removeEventListener(aName, aCallback, aCapture);
  },

  /**
   * Gets the id associated with this item.
   * @return string
   */
  get id() {
    return this._idString;
  },

  /**
   * Gets the name associated with this item.
   * @return string
   */
  get name() {
    return this._nameString;
  },

  /**
   * Gets the displayed value for this item.
   * @return string
   */
  get displayValue() {
    return this._valueString;
  },

  /**
   * Gets the class names used for the displayed value.
   * @return string
   */
  get displayValueClassName() {
    return this._valueClassName;
  },

  /**
   * Gets the element associated with this item.
   * @return nsIDOMNode
   */
  get target() {
    return this._target;
  },

  /**
   * Initializes this scope's id, view and binds event listeners.
   *
   * @param string aName
   *        The scope's name.
   * @param object aFlags [optional]
   *        Additional options or flags for this scope.
   */
  _init: function (aName, aFlags) {
    this._idString = generateId(this._nameString = aName);
    this._displayScope(aName, `${this.targetClassName} ${aFlags.customClass}`,
                       "devtools-toolbar");
    this._addEventListeners();
    this.parentNode.appendChild(this._target);
  },

  /**
   * Creates the necessary nodes for this scope.
   *
   * @param string aName
   *        The scope's name.
   * @param string aTargetClassName
   *        A custom class name for this scope's target element.
   * @param string aTitleClassName [optional]
   *        A custom class name for this scope's title element.
   */
  _displayScope: function (aName = "", aTargetClassName, aTitleClassName = "") {
    let document = this.document;

    let element = this._target = document.createElement("vbox");
    element.id = this._idString;
    element.className = aTargetClassName;

    let arrow = this._arrow = document.createElement("hbox");
    arrow.className = "arrow theme-twisty";

    let name = this._name = document.createElement("label");
    name.className = "plain name";
    name.setAttribute("value", aName.trim());
    name.setAttribute("crop", "end");

    let title = this._title = document.createElement("hbox");
    title.className = "title " + aTitleClassName;
    title.setAttribute("align", "center");

    let enumerable = this._enum = document.createElement("vbox");
    let nonenum = this._nonenum = document.createElement("vbox");
    enumerable.className = "variables-view-element-details enum";
    nonenum.className = "variables-view-element-details nonenum";

    title.appendChild(arrow);
    title.appendChild(name);

    element.appendChild(title);
    element.appendChild(enumerable);
    element.appendChild(nonenum);
  },

  /**
   * Adds the necessary event listeners for this scope.
   */
  _addEventListeners: function () {
    this._title.addEventListener("mousedown", this._onClick);
  },

  /**
   * The click listener for this scope's title.
   */
  _onClick: function (e) {
    if (this.editing ||
        e.button != 0 ||
        e.target == this._editNode ||
        e.target == this._deleteNode ||
        e.target == this._addPropertyNode) {
      return;
    }
    this.toggle();
    this.focus();
  },

  /**
   * Opens the enumerable items container.
   */
  _openEnum: function () {
    this._arrow.setAttribute("open", "");
    this._enum.setAttribute("open", "");
  },

  /**
   * Opens the non-enumerable items container.
   */
  _openNonEnum: function () {
    this._nonenum.setAttribute("open", "");
  },

  /**
   * Specifies if enumerable properties and variables should be displayed.
   * @param boolean aFlag
   */
  set _enumVisible(aFlag) {
    for (let [, variable] of this._store) {
      variable._enumVisible = aFlag;

      if (!this._isExpanded) {
        continue;
      }
      if (aFlag) {
        this._enum.setAttribute("open", "");
      } else {
        this._enum.removeAttribute("open");
      }
    }
  },

  /**
   * Specifies if non-enumerable properties and variables should be displayed.
   * @param boolean aFlag
   */
  set _nonEnumVisible(aFlag) {
    for (let [, variable] of this._store) {
      variable._nonEnumVisible = aFlag;

      if (!this._isExpanded) {
        continue;
      }
      if (aFlag) {
        this._nonenum.setAttribute("open", "");
      } else {
        this._nonenum.removeAttribute("open");
      }
    }
  },

  /**
   * Performs a case insensitive search for variables or properties matching
   * the query, and hides non-matched items.
   *
   * @param string aLowerCaseQuery
   *        The lowercased name of the variable or property to search for.
   */
  _performSearch: function (aLowerCaseQuery) {
    for (let [, variable] of this._store) {
      let currentObject = variable;
      let lowerCaseName = variable._nameString.toLowerCase();
      let lowerCaseValue = variable._valueString.toLowerCase();

      // Non-matched variables or properties require a corresponding attribute.
      if (!lowerCaseName.includes(aLowerCaseQuery) &&
          !lowerCaseValue.includes(aLowerCaseQuery)) {
        variable._matched = false;
      }
      // Variable or property is matched.
      else {
        variable._matched = true;

        // If the variable was ever expanded, there's a possibility it may
        // contain some matched properties, so make sure they're visible
        // ("expand downwards").
        if (variable._store.size) {
          variable.expand();
        }

        // If the variable is contained in another Scope, Variable, or Property,
        // the parent may not be a match, thus hidden. It should be visible
        // ("expand upwards").
        while ((variable = variable.ownerView) && variable instanceof Scope) {
          variable._matched = true;
          variable.expand();
        }
      }

      // Proceed with the search recursively inside this variable or property.
      if (currentObject._store.size || currentObject.getter || currentObject.setter) {
        currentObject._performSearch(aLowerCaseQuery);
      }
    }
  },

  /**
   * Sets if this object instance is a matched or non-matched item.
   * @param boolean aStatus
   */
  set _matched(aStatus) {
    if (this._isMatch == aStatus) {
      return;
    }
    if (aStatus) {
      this._isMatch = true;
      this.target.removeAttribute("unmatched");
    } else {
      this._isMatch = false;
      this.target.setAttribute("unmatched", "");
    }
  },

  /**
   * Find the first item in the tree of visible items in this item that matches
   * the predicate. Searches in visual order (the order seen by the user).
   * Tests itself, then descends into first the enumerable children and then
   * the non-enumerable children (since they are presented in separate groups).
   *
   * @param function aPredicate
   *        A function that returns true when a match is found.
   * @return Scope | Variable | Property
   *         The first visible scope, variable or property, or null if nothing
   *         is found.
   */
  _findInVisibleItems: function (aPredicate) {
    if (aPredicate(this)) {
      return this;
    }

    if (this._isExpanded) {
      if (this._variablesView._enumVisible) {
        for (let item of this._enumItems) {
          let result = item._findInVisibleItems(aPredicate);
          if (result) {
            return result;
          }
        }
      }

      if (this._variablesView._nonEnumVisible) {
        for (let item of this._nonEnumItems) {
          let result = item._findInVisibleItems(aPredicate);
          if (result) {
            return result;
          }
        }
      }
    }

    return null;
  },

  /**
   * Find the last item in the tree of visible items in this item that matches
   * the predicate. Searches in reverse visual order (opposite of the order
   * seen by the user). Descends into first the non-enumerable children, then
   * the enumerable children (since they are presented in separate groups), and
   * finally tests itself.
   *
   * @param function aPredicate
   *        A function that returns true when a match is found.
   * @return Scope | Variable | Property
   *         The last visible scope, variable or property, or null if nothing
   *         is found.
   */
  _findInVisibleItemsReverse: function (aPredicate) {
    if (this._isExpanded) {
      if (this._variablesView._nonEnumVisible) {
        for (let i = this._nonEnumItems.length - 1; i >= 0; i--) {
          let item = this._nonEnumItems[i];
          let result = item._findInVisibleItemsReverse(aPredicate);
          if (result) {
            return result;
          }
        }
      }

      if (this._variablesView._enumVisible) {
        for (let i = this._enumItems.length - 1; i >= 0; i--) {
          let item = this._enumItems[i];
          let result = item._findInVisibleItemsReverse(aPredicate);
          if (result) {
            return result;
          }
        }
      }
    }

    if (aPredicate(this)) {
      return this;
    }

    return null;
  },

  /**
   * Gets top level variables view instance.
   * @return VariablesView
   */
  get _variablesView() {
    return this._topView || (this._topView = (() => {
      let parentView = this.ownerView;
      let topView;

      while ((topView = parentView.ownerView)) {
        parentView = topView;
      }
      return parentView;
    })());
  },

  /**
   * Gets the parent node holding this scope.
   * @return nsIDOMNode
   */
  get parentNode() {
    return this.ownerView._list;
  },

  /**
   * Gets the owner document holding this scope.
   * @return nsIHTMLDocument
   */
  get document() {
    return this._document || (this._document = this.ownerView.document);
  },

  /**
   * Gets the default window holding this scope.
   * @return nsIDOMWindow
   */
  get window() {
    return this._window || (this._window = this.ownerView.window);
  },

  _topView: null,
  _document: null,
  _window: null,

  ownerView: null,
  eval: null,
  switch: null,
  delete: null,
  new: null,
  preventDisableOnChange: false,
  preventDescriptorModifiers: false,
  editing: false,
  editableNameTooltip: "",
  editableValueTooltip: "",
  editButtonTooltip: "",
  deleteButtonTooltip: "",
  domNodeValueTooltip: "",
  contextMenuId: "",
  separatorStr: "",

  _store: null,
  _enumItems: null,
  _nonEnumItems: null,
  _fetched: false,
  _committed: false,
  _isLocked: false,
  _isExpanded: false,
  _isContentVisible: true,
  _isHeaderVisible: true,
  _isArrowVisible: true,
  _isMatch: true,
  _idString: "",
  _nameString: "",
  _target: null,
  _arrow: null,
  _name: null,
  _title: null,
  _enum: null,
  _nonenum: null,
};

// Creating maps and arrays thousands of times for variables or properties
// with a large number of children fills up a lot of memory. Make sure
// these are instantiated only if needed.
DevToolsUtils.defineLazyPrototypeGetter(Scope.prototype, "_store", () => new Map());
DevToolsUtils.defineLazyPrototypeGetter(Scope.prototype, "_enumItems", Array);
DevToolsUtils.defineLazyPrototypeGetter(Scope.prototype, "_nonEnumItems", Array);

/**
 * A Variable is a Scope holding Property instances.
 * Iterable via "for (let [name, property] of instance) { }".
 *
 * @param Scope aScope
 *        The scope to contain this variable.
 * @param string aName
 *        The variable's name.
 * @param object aDescriptor
 *        The variable's descriptor.
 * @param object aOptions
 *        Options of the form accepted by Scope.addItem
 */
function Variable(aScope, aName, aDescriptor, aOptions) {
  this._setTooltips = this._setTooltips.bind(this);
  this._activateNameInput = this._activateNameInput.bind(this);
  this._activateValueInput = this._activateValueInput.bind(this);
  this.openNodeInInspector = this.openNodeInInspector.bind(this);
  this.highlightDomNode = this.highlightDomNode.bind(this);
  this.unhighlightDomNode = this.unhighlightDomNode.bind(this);
  this._internalItem = aOptions.internalItem;

  // Treat safe getter descriptors as descriptors with a value.
  if ("getterValue" in aDescriptor) {
    aDescriptor.value = aDescriptor.getterValue;
    delete aDescriptor.get;
    delete aDescriptor.set;
  }

  Scope.call(this, aScope, aName, this._initialDescriptor = aDescriptor);
  this.setGrip(aDescriptor.value);
}

Variable.prototype = extend(Scope.prototype, {
  /**
   * Whether this Variable should be prefetched when it is remoted.
   */
  get shouldPrefetch() {
    return this.name == "window" || this.name == "this";
  },

  /**
   * Whether this Variable should paginate its contents.
   */
  get allowPaginate() {
    return this.name != "window" && this.name != "this";
  },

  /**
   * The class name applied to this variable's target element.
   */
  targetClassName: "variables-view-variable variable-or-property",

  /**
   * Create a new Property that is a child of Variable.
   *
   * @param string aName
   *        The name of the new Property.
   * @param object aDescriptor
   *        The property's descriptor.
   * @param object aOptions
   *        Options of the form accepted by Scope.addItem
   * @return Property
   *         The newly created child Property.
   */
  _createChild: function (aName, aDescriptor, aOptions) {
    return new Property(this, aName, aDescriptor, aOptions);
  },

  /**
   * Remove this Variable from its parent and remove all children recursively.
   */
  remove: function () {
    if (this._linkedToInspector) {
      this.unhighlightDomNode();
      this._valueLabel.removeEventListener("mouseover", this.highlightDomNode);
      this._valueLabel.removeEventListener("mouseout", this.unhighlightDomNode);
      this._openInspectorNode.removeEventListener("mousedown", this.openNodeInInspector);
    }

    this.ownerView._store.delete(this._nameString);
    this._variablesView._itemsByElement.delete(this._target);
    this._variablesView._currHierarchy.delete(this.absoluteName);

    this._target.remove();

    for (let property of this._store.values()) {
      property.remove();
    }
  },

  /**
   * Populates this variable to contain all the properties of an object.
   *
   * @param object aObject
   *        The raw object you want to display.
   * @param object aOptions [optional]
   *        Additional options for adding the properties. Supported options:
   *        - sorted: true to sort all the properties before adding them
   *        - expanded: true to expand all the properties after adding them
   */
  populate: function (aObject, aOptions = {}) {
    // Retrieve the properties only once.
    if (this._fetched) {
      return;
    }
    this._fetched = true;

    let propertyNames = Object.getOwnPropertyNames(aObject);
    let prototype = Object.getPrototypeOf(aObject);

    // Sort all of the properties before adding them, if preferred.
    if (aOptions.sorted) {
      propertyNames.sort(this._naturalSort);
    }

    // Add all the variable properties.
    for (let name of propertyNames) {
      let descriptor = Object.getOwnPropertyDescriptor(aObject, name);
      if (descriptor.get || descriptor.set) {
        let prop = this._addRawNonValueProperty(name, descriptor);
        if (aOptions.expanded) {
          prop.expanded = true;
        }
      } else {
        let prop = this._addRawValueProperty(name, descriptor, aObject[name]);
        if (aOptions.expanded) {
          prop.expanded = true;
        }
      }
    }
    // Add the variable's __proto__.
    if (prototype) {
      this._addRawValueProperty("__proto__", {}, prototype);
    }
  },

  /**
   * Populates a specific variable or property instance to contain all the
   * properties of an object
   *
   * @param Variable | Property aVar
   *        The target variable to populate.
   * @param object aObject [optional]
   *        The raw object you want to display. If unspecified, the object is
   *        assumed to be defined in a _sourceValue property on the target.
   */
  _populateTarget: function (aVar, aObject = aVar._sourceValue) {
    aVar.populate(aObject);
  },

  /**
   * Adds a property for this variable based on a raw value descriptor.
   *
   * @param string aName
   *        The property's name.
   * @param object aDescriptor
   *        Specifies the exact property descriptor as returned by a call to
   *        Object.getOwnPropertyDescriptor.
   * @param object aValue
   *        The raw property value you want to display.
   * @return Property
   *         The newly added property instance.
   */
  _addRawValueProperty: function (aName, aDescriptor, aValue) {
    let descriptor = Object.create(aDescriptor);
    descriptor.value = VariablesView.getGrip(aValue);

    let propertyItem = this.addItem(aName, descriptor);
    propertyItem._sourceValue = aValue;

    // Add an 'onexpand' callback for the property, lazily handling
    // the addition of new child properties.
    if (!VariablesView.isPrimitive(descriptor)) {
      propertyItem.onexpand = this._populateTarget;
    }
    return propertyItem;
  },

  /**
   * Adds a property for this variable based on a getter/setter descriptor.
   *
   * @param string aName
   *        The property's name.
   * @param object aDescriptor
   *        Specifies the exact property descriptor as returned by a call to
   *        Object.getOwnPropertyDescriptor.
   * @return Property
   *         The newly added property instance.
   */
  _addRawNonValueProperty: function (aName, aDescriptor) {
    let descriptor = Object.create(aDescriptor);
    descriptor.get = VariablesView.getGrip(aDescriptor.get);
    descriptor.set = VariablesView.getGrip(aDescriptor.set);

    return this.addItem(aName, descriptor);
  },

  /**
   * Gets this variable's path to the topmost scope in the form of a string
   * meant for use via eval() or a similar approach.
   * For example, a symbolic name may look like "arguments['0']['foo']['bar']".
   * @return string
   */
  get symbolicName() {
    return this._nameString || "";
  },

  /**
   * Gets full path to this variable, including name of the scope.
   * @return string
   */
  get absoluteName() {
    if (this._absoluteName) {
      return this._absoluteName;
    }

    this._absoluteName = this.ownerView._nameString + "[" + escapeString(this._nameString) + "]";
    return this._absoluteName;
  },

  /**
   * Gets this variable's symbolic path to the topmost scope.
   * @return array
   * @see Variable._buildSymbolicPath
   */
  get symbolicPath() {
    if (this._symbolicPath) {
      return this._symbolicPath;
    }
    this._symbolicPath = this._buildSymbolicPath();
    return this._symbolicPath;
  },

  /**
   * Build this variable's path to the topmost scope in form of an array of
   * strings, one for each segment of the path.
   * For example, a symbolic path may look like ["0", "foo", "bar"].
   * @return array
   */
  _buildSymbolicPath: function (path = []) {
    if (this.name) {
      path.unshift(this.name);
      if (this.ownerView instanceof Variable) {
        return this.ownerView._buildSymbolicPath(path);
      }
    }
    return path;
  },

  /**
   * Returns this variable's value from the descriptor if available.
   * @return any
   */
  get value() {
    return this._initialDescriptor.value;
  },

  /**
   * Returns this variable's getter from the descriptor if available.
   * @return object
   */
  get getter() {
    return this._initialDescriptor.get;
  },

  /**
   * Returns this variable's getter from the descriptor if available.
   * @return object
   */
  get setter() {
    return this._initialDescriptor.set;
  },

  /**
   * Sets the specific grip for this variable (applies the text content and
   * class name to the value label).
   *
   * The grip should contain the value or the type & class, as defined in the
   * remote debugger protocol. For convenience, undefined and null are
   * both considered types.
   *
   * @param any aGrip
   *        Specifies the value and/or type & class of the variable.
   *        e.g. - 42
   *             - true
   *             - "nasu"
   *             - { type: "undefined" }
   *             - { type: "null" }
   *             - { type: "object", class: "Object" }
   */
  setGrip: function (aGrip) {
    // Don't allow displaying grip information if there's no name available
    // or the grip is malformed.
    if (this._nameString === undefined || aGrip === undefined || aGrip === null) {
      return;
    }
    // Getters and setters should display grip information in sub-properties.
    if (this.getter || this.setter) {
      return;
    }

    let prevGrip = this._valueGrip;
    if (prevGrip) {
      this._valueLabel.classList.remove(VariablesView.getClass(prevGrip));
    }
    this._valueGrip = aGrip;

    if (aGrip && (aGrip.optimizedOut || aGrip.uninitialized || aGrip.missingArguments)) {
      if (aGrip.optimizedOut) {
        this._valueString = L10N.getStr("variablesViewOptimizedOut");
      }
      else if (aGrip.uninitialized) {
        this._valueString = L10N.getStr("variablesViewUninitialized");
      }
      else if (aGrip.missingArguments) {
        this._valueString = L10N.getStr("variablesViewMissingArgs");
      }
      this.eval = null;
    }
    else {
      this._valueString = VariablesView.getString(aGrip, {
        concise: true,
        noEllipsis: true,
      });
      this.eval = this.ownerView.eval;
    }

    this._valueClassName = VariablesView.getClass(aGrip);

    this._valueLabel.classList.add(this._valueClassName);
    this._valueLabel.setAttribute("value", this._valueString);
    this._separatorLabel.hidden = false;

    // DOMNodes get special treatment since they can be linked to the inspector
    if (this._valueGrip.preview && this._valueGrip.preview.kind === "DOMNode") {
      this._linkToInspector();
    }
  },

  /**
   * Marks this variable as overridden.
   *
   * @param boolean aFlag
   *        Whether this variable is overridden or not.
   */
  setOverridden: function (aFlag) {
    if (aFlag) {
      this._target.setAttribute("overridden", "");
    } else {
      this._target.removeAttribute("overridden");
    }
  },

  /**
   * Briefly flashes this variable.
   *
   * @param number aDuration [optional]
   *        An optional flash animation duration.
   */
  flash: function (aDuration = ITEM_FLASH_DURATION) {
    let fadeInDelay = this._variablesView.lazyEmptyDelay + 1;
    let fadeOutDelay = fadeInDelay + aDuration;

    setNamedTimeout("vview-flash-in" + this.absoluteName,
      fadeInDelay, () => this._target.setAttribute("changed", ""));

    setNamedTimeout("vview-flash-out" + this.absoluteName,
      fadeOutDelay, () => this._target.removeAttribute("changed"));
  },

  /**
   * Initializes this variable's id, view and binds event listeners.
   *
   * @param string aName
   *        The variable's name.
   * @param object aDescriptor
   *        The variable's descriptor.
   */
  _init: function (aName, aDescriptor) {
    this._idString = generateId(this._nameString = aName);
    this._displayScope(aName, this.targetClassName);
    this._displayVariable();
    this._customizeVariable();
    this._prepareTooltips();
    this._setAttributes();
    this._addEventListeners();

    if (this._initialDescriptor.enumerable ||
        this._nameString == "this" ||
        this._internalItem) {
      this.ownerView._enum.appendChild(this._target);
      this.ownerView._enumItems.push(this);
    } else {
      this.ownerView._nonenum.appendChild(this._target);
      this.ownerView._nonEnumItems.push(this);
    }
  },

  /**
   * Creates the necessary nodes for this variable.
   */
  _displayVariable: function () {
    let document = this.document;
    let descriptor = this._initialDescriptor;

    let separatorLabel = this._separatorLabel = document.createElement("label");
    separatorLabel.className = "plain separator";
    separatorLabel.setAttribute("value", this.separatorStr + " ");

    let valueLabel = this._valueLabel = document.createElement("label");
    valueLabel.className = "plain value";
    valueLabel.setAttribute("flex", "1");
    valueLabel.setAttribute("crop", "center");

    this._title.appendChild(separatorLabel);
    this._title.appendChild(valueLabel);

    if (VariablesView.isPrimitive(descriptor)) {
      this.hideArrow();
    }

    // If no value will be displayed, we don't need the separator.
    if (!descriptor.get && !descriptor.set && !("value" in descriptor)) {
      separatorLabel.hidden = true;
    }

    // If this is a getter/setter property, create two child pseudo-properties
    // called "get" and "set" that display the corresponding functions.
    if (descriptor.get || descriptor.set) {
      separatorLabel.hidden = true;
      valueLabel.hidden = true;

      // Changing getter/setter names is never allowed.
      this.switch = null;

      // Getter/setter properties require special handling when it comes to
      // evaluation and deletion.
      if (this.ownerView.eval) {
        this.delete = VariablesView.getterOrSetterDeleteCallback;
        this.evaluationMacro = VariablesView.overrideValueEvalMacro;
      }
      // Deleting getters and setters individually is not allowed if no
      // evaluation method is provided.
      else {
        this.delete = null;
        this.evaluationMacro = null;
      }

      let getter = this.addItem("get", { value: descriptor.get });
      let setter = this.addItem("set", { value: descriptor.set });
      getter.evaluationMacro = VariablesView.getterOrSetterEvalMacro;
      setter.evaluationMacro = VariablesView.getterOrSetterEvalMacro;

      getter.hideArrow();
      setter.hideArrow();
      this.expand();
    }
  },

  /**
   * Adds specific nodes for this variable based on custom flags.
   */
  _customizeVariable: function () {
    let ownerView = this.ownerView;
    let descriptor = this._initialDescriptor;

    if (ownerView.eval && this.getter || this.setter) {
      let editNode = this._editNode = this.document.createElement("toolbarbutton");
      editNode.className = "plain variables-view-edit";
      editNode.addEventListener("mousedown", this._onEdit.bind(this));
      this._title.insertBefore(editNode, this._spacer);
    }

    if (ownerView.delete) {
      let deleteNode = this._deleteNode = this.document.createElement("toolbarbutton");
      deleteNode.className = "plain variables-view-delete";
      deleteNode.addEventListener("click", this._onDelete.bind(this));
      this._title.appendChild(deleteNode);
    }

    if (ownerView.new) {
      let addPropertyNode = this._addPropertyNode = this.document.createElement("toolbarbutton");
      addPropertyNode.className = "plain variables-view-add-property";
      addPropertyNode.addEventListener("mousedown", this._onAddProperty.bind(this));
      this._title.appendChild(addPropertyNode);

      // Can't add properties to primitive values, hide the node in those cases.
      if (VariablesView.isPrimitive(descriptor)) {
        addPropertyNode.setAttribute("invisible", "");
      }
    }

    if (ownerView.contextMenuId) {
      this._title.setAttribute("context", ownerView.contextMenuId);
    }

    if (ownerView.preventDescriptorModifiers) {
      return;
    }

    if (!descriptor.writable && !ownerView.getter && !ownerView.setter) {
      let nonWritableIcon = this.document.createElement("hbox");
      nonWritableIcon.className = "plain variable-or-property-non-writable-icon";
      nonWritableIcon.setAttribute("optional-visibility", "");
      this._title.appendChild(nonWritableIcon);
    }
    if (descriptor.value && typeof descriptor.value == "object") {
      if (descriptor.value.frozen) {
        let frozenLabel = this.document.createElement("label");
        frozenLabel.className = "plain variable-or-property-frozen-label";
        frozenLabel.setAttribute("optional-visibility", "");
        frozenLabel.setAttribute("value", "F");
        this._title.appendChild(frozenLabel);
      }
      if (descriptor.value.sealed) {
        let sealedLabel = this.document.createElement("label");
        sealedLabel.className = "plain variable-or-property-sealed-label";
        sealedLabel.setAttribute("optional-visibility", "");
        sealedLabel.setAttribute("value", "S");
        this._title.appendChild(sealedLabel);
      }
      if (!descriptor.value.extensible) {
        let nonExtensibleLabel = this.document.createElement("label");
        nonExtensibleLabel.className = "plain variable-or-property-non-extensible-label";
        nonExtensibleLabel.setAttribute("optional-visibility", "");
        nonExtensibleLabel.setAttribute("value", "N");
        this._title.appendChild(nonExtensibleLabel);
      }
    }
  },

  /**
   * Prepares all tooltips for this variable.
   */
  _prepareTooltips: function () {
    this._target.addEventListener("mouseover", this._setTooltips);
  },

  /**
   * Sets all tooltips for this variable.
   */
  _setTooltips: function () {
    this._target.removeEventListener("mouseover", this._setTooltips);

    let ownerView = this.ownerView;
    if (ownerView.preventDescriptorModifiers) {
      return;
    }

    let tooltip = this.document.createElement("tooltip");
    tooltip.id = "tooltip-" + this._idString;
    tooltip.setAttribute("orient", "horizontal");

    let labels = [
      "configurable", "enumerable", "writable",
      "frozen", "sealed", "extensible", "overridden", "WebIDL"];

    for (let type of labels) {
      let labelElement = this.document.createElement("label");
      labelElement.className = type;
      labelElement.setAttribute("value", L10N.getStr(type + "Tooltip"));
      tooltip.appendChild(labelElement);
    }

    this._target.appendChild(tooltip);
    this._target.setAttribute("tooltip", tooltip.id);

    if (this._editNode && ownerView.eval) {
      this._editNode.setAttribute("tooltiptext", ownerView.editButtonTooltip);
    }
    if (this._openInspectorNode && this._linkedToInspector) {
      this._openInspectorNode.setAttribute("tooltiptext", this.ownerView.domNodeValueTooltip);
    }
    if (this._valueLabel && ownerView.eval) {
      this._valueLabel.setAttribute("tooltiptext", ownerView.editableValueTooltip);
    }
    if (this._name && ownerView.switch) {
      this._name.setAttribute("tooltiptext", ownerView.editableNameTooltip);
    }
    if (this._deleteNode && ownerView.delete) {
      this._deleteNode.setAttribute("tooltiptext", ownerView.deleteButtonTooltip);
    }
  },

  /**
   * Get the parent variablesview toolbox, if any.
   */
  get toolbox() {
    return this._variablesView.toolbox;
  },

  /**
   * Checks if this variable is a DOMNode and is part of a variablesview that
   * has been linked to the toolbox, so that highlighting and jumping to the
   * inspector can be done.
   */
  _isLinkableToInspector: function () {
    let isDomNode = this._valueGrip && this._valueGrip.preview.kind === "DOMNode";
    let hasBeenLinked = this._linkedToInspector;
    let hasToolbox = !!this.toolbox;

    return isDomNode && !hasBeenLinked && hasToolbox;
  },

  /**
   * If the variable is a DOMNode, and if a toolbox is set, then link it to the
   * inspector (highlight on hover, and jump to markup-view on click)
   */
  _linkToInspector: function () {
    if (!this._isLinkableToInspector()) {
      return;
    }

    // Listen to value mouseover/click events to highlight and jump
    this._valueLabel.addEventListener("mouseover", this.highlightDomNode);
    this._valueLabel.addEventListener("mouseout", this.unhighlightDomNode);

    // Add a button to open the node in the inspector
    this._openInspectorNode = this.document.createElement("toolbarbutton");
    this._openInspectorNode.className = "plain variables-view-open-inspector";
    this._openInspectorNode.addEventListener("mousedown", this.openNodeInInspector);
    this._title.appendChild(this._openInspectorNode);

    this._linkedToInspector = true;
  },

  /**
   * In case this variable is a DOMNode and part of a variablesview that has been
   * linked to the toolbox's inspector, then select the corresponding node in
   * the inspector, and switch the inspector tool in the toolbox
   * @return a promise that resolves when the node is selected and the inspector
   * has been switched to and is ready
   */
  openNodeInInspector: function (event) {
    if (!this.toolbox) {
      return promise.reject(new Error("Toolbox not available"));
    }

    event && event.stopPropagation();

    return Task.spawn(function* () {
      yield this.toolbox.initInspector();

      let nodeFront = this._nodeFront;
      if (!nodeFront) {
        nodeFront = yield this.toolbox.walker.getNodeActorFromObjectActor(this._valueGrip.actor);
      }

      if (nodeFront) {
        yield this.toolbox.selectTool("inspector");

        let inspectorReady = defer();
        this.toolbox.getPanel("inspector").once("inspector-updated", inspectorReady.resolve);
        yield this.toolbox.selection.setNodeFront(nodeFront, "variables-view");
        yield inspectorReady.promise;
      }
    }.bind(this));
  },

  /**
   * In case this variable is a DOMNode and part of a variablesview that has been
   * linked to the toolbox's inspector, then highlight the corresponding node
   */
  highlightDomNode: function () {
    if (this.toolbox) {
      if (this._nodeFront) {
        // If the nodeFront has been retrieved before, no need to ask the server
        // again for it
        this.toolbox.highlighterUtils.highlightNodeFront(this._nodeFront);
        return;
      }

      this.toolbox.highlighterUtils.highlightDomValueGrip(this._valueGrip).then(front => {
        this._nodeFront = front;
      });
    }
  },

  /**
   * Unhighlight a previously highlit node
   * @see highlightDomNode
   */
  unhighlightDomNode: function () {
    if (this.toolbox) {
      this.toolbox.highlighterUtils.unhighlight();
    }
  },

  /**
   * Sets a variable's configurable, enumerable and writable attributes,
   * and specifies if it's a 'this', '<exception>', '<return>' or '__proto__'
   * reference.
   */
  _setAttributes: function () {
    let ownerView = this.ownerView;
    if (ownerView.preventDescriptorModifiers) {
      return;
    }

    let descriptor = this._initialDescriptor;
    let target = this._target;
    let name = this._nameString;

    if (ownerView.eval) {
      target.setAttribute("editable", "");
    }

    if (!descriptor.configurable) {
      target.setAttribute("non-configurable", "");
    }
    if (!descriptor.enumerable) {
      target.setAttribute("non-enumerable", "");
    }
    if (!descriptor.writable && !ownerView.getter && !ownerView.setter) {
      target.setAttribute("non-writable", "");
    }

    if (descriptor.value && typeof descriptor.value == "object") {
      if (descriptor.value.frozen) {
        target.setAttribute("frozen", "");
      }
      if (descriptor.value.sealed) {
        target.setAttribute("sealed", "");
      }
      if (!descriptor.value.extensible) {
        target.setAttribute("non-extensible", "");
      }
    }

    if (descriptor && "getterValue" in descriptor) {
      target.setAttribute("safe-getter", "");
    }

    if (name == "this") {
      target.setAttribute("self", "");
    }
    else if (this._internalItem && name == "<exception>") {
      target.setAttribute("exception", "");
      target.setAttribute("pseudo-item", "");
    }
    else if (this._internalItem && name == "<return>") {
      target.setAttribute("return", "");
      target.setAttribute("pseudo-item", "");
    }
    else if (name == "__proto__") {
      target.setAttribute("proto", "");
      target.setAttribute("pseudo-item", "");
    }

    if (Object.keys(descriptor).length == 0) {
      target.setAttribute("pseudo-item", "");
    }
  },

  /**
   * Adds the necessary event listeners for this variable.
   */
  _addEventListeners: function () {
    this._name.addEventListener("dblclick", this._activateNameInput);
    this._valueLabel.addEventListener("mousedown", this._activateValueInput);
    this._title.addEventListener("mousedown", this._onClick);
  },

  /**
   * Makes this variable's name editable.
   */
  _activateNameInput: function (e) {
    if (!this._variablesView.alignedValues) {
      this._separatorLabel.hidden = true;
      this._valueLabel.hidden = true;
    }

    EditableName.create(this, {
      onSave: aKey => {
        if (!this._variablesView.preventDisableOnChange) {
          this._disable();
        }
        this.ownerView.switch(this, aKey);
      },
      onCleanup: () => {
        if (!this._variablesView.alignedValues) {
          this._separatorLabel.hidden = false;
          this._valueLabel.hidden = false;
        }
      }
    }, e);
  },

  /**
   * Makes this variable's value editable.
   */
  _activateValueInput: function (e) {
    EditableValue.create(this, {
      onSave: aString => {
        if (this._linkedToInspector) {
          this.unhighlightDomNode();
        }
        if (!this._variablesView.preventDisableOnChange) {
          this._disable();
        }
        this.ownerView.eval(this, aString);
      }
    }, e);
  },

  /**
   * Disables this variable prior to a new name switch or value evaluation.
   */
  _disable: function () {
    // Prevent the variable from being collapsed or expanded.
    this.hideArrow();

    // Hide any nodes that may offer information about the variable.
    for (let node of this._title.childNodes) {
      node.hidden = node != this._arrow && node != this._name;
    }
    this._enum.hidden = true;
    this._nonenum.hidden = true;
  },

  /**
   * The current macro used to generate the string evaluated when performing
   * a variable or property value change.
   */
  evaluationMacro: VariablesView.simpleValueEvalMacro,

  /**
   * The click listener for the edit button.
   */
  _onEdit: function (e) {
    if (e.button != 0) {
      return;
    }

    e.preventDefault();
    e.stopPropagation();
    this._activateValueInput();
  },

  /**
   * The click listener for the delete button.
   */
  _onDelete: function (e) {
    if ("button" in e && e.button != 0) {
      return;
    }

    e.preventDefault();
    e.stopPropagation();

    if (this.ownerView.delete) {
      if (!this.ownerView.delete(this)) {
        this.hide();
      }
    }
  },

  /**
   * The click listener for the add property button.
   */
  _onAddProperty: function (e) {
    if ("button" in e && e.button != 0) {
      return;
    }

    e.preventDefault();
    e.stopPropagation();

    this.expanded = true;

    let item = this.addItem(" ", {
      value: undefined,
      configurable: true,
      enumerable: true,
      writable: true
    }, {relaxed: true});

    // Force showing the separator.
    item._separatorLabel.hidden = false;

    EditableNameAndValue.create(item, {
      onSave: ([aKey, aValue]) => {
        if (!this._variablesView.preventDisableOnChange) {
          this._disable();
        }
        this.ownerView.new(this, aKey, aValue);
      }
    }, e);
  },

  _symbolicName: null,
  _symbolicPath: null,
  _absoluteName: null,
  _initialDescriptor: null,
  _separatorLabel: null,
  _valueLabel: null,
  _spacer: null,
  _editNode: null,
  _deleteNode: null,
  _addPropertyNode: null,
  _tooltip: null,
  _valueGrip: null,
  _valueString: "",
  _valueClassName: "",
  _prevExpandable: false,
  _prevExpanded: false
});

/**
 * A Property is a Variable holding additional child Property instances.
 * Iterable via "for (let [name, property] of instance) { }".
 *
 * @param Variable aVar
 *        The variable to contain this property.
 * @param string aName
 *        The property's name.
 * @param object aDescriptor
 *        The property's descriptor.
 * @param object aOptions
 *        Options of the form accepted by Scope.addItem
 */
function Property(aVar, aName, aDescriptor, aOptions) {
  Variable.call(this, aVar, aName, aDescriptor, aOptions);
}

Property.prototype = extend(Variable.prototype, {
  /**
   * The class name applied to this property's target element.
   */
  targetClassName: "variables-view-property variable-or-property",

  /**
   * @see Variable.symbolicName
   * @return string
   */
  get symbolicName() {
    if (this._symbolicName) {
      return this._symbolicName;
    }

    this._symbolicName = this.ownerView.symbolicName + "[" + escapeString(this._nameString) + "]";
    return this._symbolicName;
  },

  /**
   * @see Variable.absoluteName
   * @return string
   */
  get absoluteName() {
    if (this._absoluteName) {
      return this._absoluteName;
    }

    this._absoluteName = this.ownerView.absoluteName + "[" + escapeString(this._nameString) + "]";
    return this._absoluteName;
  }
});

/**
 * A generator-iterator over the VariablesView, Scopes, Variables and Properties.
 */
VariablesView.prototype[Symbol.iterator] =
Scope.prototype[Symbol.iterator] =
Variable.prototype[Symbol.iterator] =
Property.prototype[Symbol.iterator] = function* () {
  yield* this._store;
};

/**
 * Forget everything recorded about added scopes, variables or properties.
 * @see VariablesView.commitHierarchy
 */
VariablesView.prototype.clearHierarchy = function () {
  this._prevHierarchy.clear();
  this._currHierarchy.clear();
};

/**
 * Perform operations on all the VariablesView Scopes, Variables and Properties
 * after you've added all the items you wanted.
 *
 * Calling this method is optional, and does the following:
 *   - styles the items overridden by other items in parent scopes
 *   - reopens the items which were previously expanded
 *   - flashes the items whose values changed
 */
VariablesView.prototype.commitHierarchy = function () {
  for (let [, currItem] of this._currHierarchy) {
    // Avoid performing expensive operations.
    if (this.commitHierarchyIgnoredItems[currItem._nameString]) {
      continue;
    }
    let overridden = this.isOverridden(currItem);
    if (overridden) {
      currItem.setOverridden(true);
    }
    let expanded = !currItem._committed && this.wasExpanded(currItem);
    if (expanded) {
      currItem.expand();
    }
    let changed = !currItem._committed && this.hasChanged(currItem);
    if (changed) {
      currItem.flash();
    }
    currItem._committed = true;
  }
  if (this.oncommit) {
    this.oncommit(this);
  }
};

// Some variables are likely to contain a very large number of properties.
// It would be a bad idea to re-expand them or perform expensive operations.
VariablesView.prototype.commitHierarchyIgnoredItems = extend(null, {
  "window": true,
  "this": true
});

/**
 * Checks if the an item was previously expanded, if it existed in a
 * previous hierarchy.
 *
 * @param Scope | Variable | Property aItem
 *        The item to verify.
 * @return boolean
 *         Whether the item was expanded.
 */
VariablesView.prototype.wasExpanded = function (aItem) {
  if (!(aItem instanceof Scope)) {
    return false;
  }
  let prevItem = this._prevHierarchy.get(aItem.absoluteName || aItem._nameString);
  return prevItem ? prevItem._isExpanded : false;
};

/**
 * Checks if the an item's displayed value (a representation of the grip)
 * has changed, if it existed in a previous hierarchy.
 *
 * @param Variable | Property aItem
 *        The item to verify.
 * @return boolean
 *         Whether the item has changed.
 */
VariablesView.prototype.hasChanged = function (aItem) {
  // Only analyze Variables and Properties for displayed value changes.
  // Scopes are just collections of Variables and Properties and
  // don't have a "value", so they can't change.
  if (!(aItem instanceof Variable)) {
    return false;
  }
  let prevItem = this._prevHierarchy.get(aItem.absoluteName);
  return prevItem ? prevItem._valueString != aItem._valueString : false;
};

/**
 * Checks if the an item was previously expanded, if it existed in a
 * previous hierarchy.
 *
 * @param Scope | Variable | Property aItem
 *        The item to verify.
 * @return boolean
 *         Whether the item was expanded.
 */
VariablesView.prototype.isOverridden = function (aItem) {
  // Only analyze Variables for being overridden in different Scopes.
  if (!(aItem instanceof Variable) || aItem instanceof Property) {
    return false;
  }
  let currVariableName = aItem._nameString;
  let parentScopes = this.getParentScopesForVariableOrProperty(aItem);

  for (let otherScope of parentScopes) {
    for (let [otherVariableName] of otherScope) {
      if (otherVariableName == currVariableName) {
        return true;
      }
    }
  }
  return false;
};

/**
 * Returns true if the descriptor represents an undefined, null or
 * primitive value.
 *
 * @param object aDescriptor
 *        The variable's descriptor.
 */
VariablesView.isPrimitive = function (aDescriptor) {
  // For accessor property descriptors, the getter and setter need to be
  // contained in 'get' and 'set' properties.
  let getter = aDescriptor.get;
  let setter = aDescriptor.set;
  if (getter || setter) {
    return false;
  }

  // As described in the remote debugger protocol, the value grip
  // must be contained in a 'value' property.
  let grip = aDescriptor.value;
  if (typeof grip != "object") {
    return true;
  }

  // For convenience, undefined, null, Infinity, -Infinity, NaN, -0, and long
  // strings are considered types.
  let type = grip.type;
  if (type == "undefined" ||
      type == "null" ||
      type == "Infinity" ||
      type == "-Infinity" ||
      type == "NaN" ||
      type == "-0" ||
      type == "symbol" ||
      type == "longString") {
    return true;
  }

  return false;
};

/**
 * Returns true if the descriptor represents an undefined value.
 *
 * @param object aDescriptor
 *        The variable's descriptor.
 */
VariablesView.isUndefined = function (aDescriptor) {
  // For accessor property descriptors, the getter and setter need to be
  // contained in 'get' and 'set' properties.
  let getter = aDescriptor.get;
  let setter = aDescriptor.set;
  if (typeof getter == "object" && getter.type == "undefined" &&
      typeof setter == "object" && setter.type == "undefined") {
    return true;
  }

  // As described in the remote debugger protocol, the value grip
  // must be contained in a 'value' property.
  let grip = aDescriptor.value;
  if (typeof grip == "object" && grip.type == "undefined") {
    return true;
  }

  return false;
};

/**
 * Returns true if the descriptor represents a falsy value.
 *
 * @param object aDescriptor
 *        The variable's descriptor.
 */
VariablesView.isFalsy = function (aDescriptor) {
  // As described in the remote debugger protocol, the value grip
  // must be contained in a 'value' property.
  let grip = aDescriptor.value;
  if (typeof grip != "object") {
    return !grip;
  }

  // For convenience, undefined, null, NaN, and -0 are all considered types.
  let type = grip.type;
  if (type == "undefined" ||
      type == "null" ||
      type == "NaN" ||
      type == "-0") {
    return true;
  }

  return false;
};

/**
 * Returns true if the value is an instance of Variable or Property.
 *
 * @param any aValue
 *        The value to test.
 */
VariablesView.isVariable = function (aValue) {
  return aValue instanceof Variable;
};

/**
 * Returns a standard grip for a value.
 *
 * @param any aValue
 *        The raw value to get a grip for.
 * @return any
 *         The value's grip.
 */
VariablesView.getGrip = function (aValue) {
  switch (typeof aValue) {
    case "boolean":
    case "string":
      return aValue;
    case "number":
      if (aValue === Infinity) {
        return { type: "Infinity" };
      } else if (aValue === -Infinity) {
        return { type: "-Infinity" };
      } else if (Number.isNaN(aValue)) {
        return { type: "NaN" };
      } else if (1 / aValue === -Infinity) {
        return { type: "-0" };
      }
      return aValue;
    case "undefined":
      // document.all is also "undefined"
      if (aValue === undefined) {
        return { type: "undefined" };
      }
    case "object":
      if (aValue === null) {
        return { type: "null" };
      }
    case "function":
      return { type: "object",
               class: WebConsoleUtils.getObjectClassName(aValue) };
    default:
      console.error("Failed to provide a grip for value of " + typeof value +
                    ": " + aValue);
      return null;
  }
};

/**
 * Returns a custom formatted property string for a grip.
 *
 * @param any aGrip
 *        @see Variable.setGrip
 * @param object aOptions
 *        Options:
 *        - concise: boolean that tells you want a concisely formatted string.
 *        - noStringQuotes: boolean that tells to not quote strings.
 *        - noEllipsis: boolean that tells to not add an ellipsis after the
 *        initial text of a longString.
 * @return string
 *         The formatted property string.
 */
VariablesView.getString = function (aGrip, aOptions = {}) {
  if (aGrip && typeof aGrip == "object") {
    switch (aGrip.type) {
      case "undefined":
      case "null":
      case "NaN":
      case "Infinity":
      case "-Infinity":
      case "-0":
        return aGrip.type;
      default:
        let stringifier = VariablesView.stringifiers.byType[aGrip.type];
        if (stringifier) {
          let result = stringifier(aGrip, aOptions);
          if (result != null) {
            return result;
          }
        }

        if (aGrip.displayString) {
          return VariablesView.getString(aGrip.displayString, aOptions);
        }

        if (aGrip.type == "object" && aOptions.concise) {
          return aGrip.class;
        }

        return "[" + aGrip.type + " " + aGrip.class + "]";
    }
  }

  switch (typeof aGrip) {
    case "string":
      return VariablesView.stringifiers.byType.string(aGrip, aOptions);
    case "boolean":
      return aGrip ? "true" : "false";
    case "number":
      if (!aGrip && 1 / aGrip === -Infinity) {
        return "-0";
      }
    default:
      return aGrip + "";
  }
};

/**
 * The VariablesView stringifiers are used by VariablesView.getString(). These
 * are organized by object type, object class and by object actor preview kind.
 * Some objects share identical ways for previews, for example Arrays, Sets and
 * NodeLists.
 *
 * Any stringifier function must return a string. If null is returned, * then
 * the default stringifier will be used. When invoked, the stringifier is
 * given the same two arguments as those given to VariablesView.getString().
 */
VariablesView.stringifiers = {};

VariablesView.stringifiers.byType = {
  string: function (aGrip, {noStringQuotes}) {
    if (noStringQuotes) {
      return aGrip;
    }
    return '"' + aGrip + '"';
  },

  longString: function ({initial}, {noStringQuotes, noEllipsis}) {
    let ellipsis = noEllipsis ? "" : ELLIPSIS;
    if (noStringQuotes) {
      return initial + ellipsis;
    }
    let result = '"' + initial + '"';
    if (!ellipsis) {
      return result;
    }
    return result.substr(0, result.length - 1) + ellipsis + '"';
  },

  object: function (aGrip, aOptions) {
    let {preview} = aGrip;
    let stringifier;
    if (aGrip.class) {
      stringifier = VariablesView.stringifiers.byObjectClass[aGrip.class];
    }
    if (!stringifier && preview && preview.kind) {
      stringifier = VariablesView.stringifiers.byObjectKind[preview.kind];
    }
    if (stringifier) {
      return stringifier(aGrip, aOptions);
    }
    return null;
  },

  symbol: function (aGrip, aOptions) {
    const name = aGrip.name || "";
    return "Symbol(" + name + ")";
  },

  mapEntry: function (aGrip, {concise}) {
    let { preview: { key, value }} = aGrip;

    let keyString = VariablesView.getString(key, {
      concise: true,
      noStringQuotes: true,
    });
    let valueString = VariablesView.getString(value, { concise: true });

    return keyString + " \u2192 " + valueString;
  },

}; // VariablesView.stringifiers.byType

VariablesView.stringifiers.byObjectClass = {
  Function: function (aGrip, {concise}) {
    // TODO: Bug 948484 - support arrow functions and ES6 generators

    let name = aGrip.userDisplayName || aGrip.displayName || aGrip.name || "";
    name = VariablesView.getString(name, { noStringQuotes: true });

    // TODO: Bug 948489 - Support functions with destructured parameters and
    // rest parameters
    let params = aGrip.parameterNames || "";
    if (!concise) {
      return "function " + name + "(" + params + ")";
    }
    return (name || "function ") + "(" + params + ")";
  },

  RegExp: function ({displayString}) {
    return VariablesView.getString(displayString, { noStringQuotes: true });
  },

  Date: function ({preview}) {
    if (!preview || !("timestamp" in preview)) {
      return null;
    }

    if (typeof preview.timestamp != "number") {
      return new Date(preview.timestamp).toString(); // invalid date
    }

    return "Date " + new Date(preview.timestamp).toISOString();
  },

  Number: function (aGrip) {
    let {preview} = aGrip;
    if (preview === undefined) {
      return null;
    }
    return aGrip.class + " { " + VariablesView.getString(preview.wrappedValue) +
      " }";
  },
}; // VariablesView.stringifiers.byObjectClass

VariablesView.stringifiers.byObjectClass.Boolean =
  VariablesView.stringifiers.byObjectClass.Number;

VariablesView.stringifiers.byObjectKind = {
  ArrayLike: function (aGrip, {concise}) {
    let {preview} = aGrip;
    if (concise) {
      return aGrip.class + "[" + preview.length + "]";
    }

    if (!preview.items) {
      return null;
    }

    let shown = 0, result = [], lastHole = null;
    for (let item of preview.items) {
      if (item === null) {
        if (lastHole !== null) {
          result[lastHole] += ",";
        } else {
          result.push("");
        }
        lastHole = result.length - 1;
      } else {
        lastHole = null;
        result.push(VariablesView.getString(item, { concise: true }));
      }
      shown++;
    }

    if (shown < preview.length) {
      let n = preview.length - shown;
      result.push(VariablesView.stringifiers._getNMoreString(n));
    } else if (lastHole !== null) {
      // make sure we have the right number of commas...
      result[lastHole] += ",";
    }

    let prefix = aGrip.class == "Array" ? "" : aGrip.class + " ";
    return prefix + "[" + result.join(", ") + "]";
  },

  MapLike: function (aGrip, {concise}) {
    let {preview} = aGrip;
    if (concise || !preview.entries) {
      let size = typeof preview.size == "number" ?
                   "[" + preview.size + "]" : "";
      return aGrip.class + size;
    }

    let entries = [];
    for (let [key, value] of preview.entries) {
      let keyString = VariablesView.getString(key, {
        concise: true,
        noStringQuotes: true,
      });
      let valueString = VariablesView.getString(value, { concise: true });
      entries.push(keyString + ": " + valueString);
    }

    if (typeof preview.size == "number" && preview.size > entries.length) {
      let n = preview.size - entries.length;
      entries.push(VariablesView.stringifiers._getNMoreString(n));
    }

    return aGrip.class + " {" + entries.join(", ") + "}";
  },

  ObjectWithText: function (aGrip, {concise}) {
    if (concise) {
      return aGrip.class;
    }

    return aGrip.class + " " + VariablesView.getString(aGrip.preview.text);
  },

  ObjectWithURL: function (aGrip, {concise}) {
    let result = aGrip.class;
    let url = aGrip.preview.url;
    if (!VariablesView.isFalsy({ value: url })) {
      result += ` \u2192 ${getSourceNames(url)[concise ? "short" : "long"]}`;
    }
    return result;
  },

  // Stringifier for any kind of object.
  Object: function (aGrip, {concise}) {
    if (concise) {
      return aGrip.class;
    }

    let {preview} = aGrip;
    let props = [];

    if (aGrip.class == "Promise" && aGrip.promiseState) {
      let { state, value, reason } = aGrip.promiseState;
      props.push("<state>: " + VariablesView.getString(state));
      if (state == "fulfilled") {
        props.push("<value>: " + VariablesView.getString(value, { concise: true }));
      } else if (state == "rejected") {
        props.push("<reason>: " + VariablesView.getString(reason, { concise: true }));
      }
    }

    for (let key of Object.keys(preview.ownProperties || {})) {
      let value = preview.ownProperties[key];
      let valueString = "";
      if (value.get) {
        valueString = "Getter";
      } else if (value.set) {
        valueString = "Setter";
      } else {
        valueString = VariablesView.getString(value.value, { concise: true });
      }
      props.push(key + ": " + valueString);
    }

    for (let key of Object.keys(preview.safeGetterValues || {})) {
      let value = preview.safeGetterValues[key];
      let valueString = VariablesView.getString(value.getterValue,
                                                { concise: true });
      props.push(key + ": " + valueString);
    }

    if (!props.length) {
      return null;
    }

    if (preview.ownPropertiesLength) {
      let previewLength = Object.keys(preview.ownProperties).length;
      let diff = preview.ownPropertiesLength - previewLength;
      if (diff > 0) {
        props.push(VariablesView.stringifiers._getNMoreString(diff));
      }
    }

    let prefix = aGrip.class != "Object" ? aGrip.class + " " : "";
    return prefix + "{" + props.join(", ") + "}";
  }, // Object

  Error: function (aGrip, {concise}) {
    let {preview} = aGrip;
    let name = VariablesView.getString(preview.name, { noStringQuotes: true });
    if (concise) {
      return name || aGrip.class;
    }

    let msg = name + ": " +
              VariablesView.getString(preview.message, { noStringQuotes: true });

    if (!VariablesView.isFalsy({ value: preview.stack })) {
      msg += "\n" + L10N.getStr("variablesViewErrorStacktrace") +
             "\n" + preview.stack;
    }

    return msg;
  },

  DOMException: function (aGrip, {concise}) {
    let {preview} = aGrip;
    if (concise) {
      return preview.name || aGrip.class;
    }

    let msg = aGrip.class + " [" + preview.name + ": " +
              VariablesView.getString(preview.message) + "\n" +
              "code: " + preview.code + "\n" +
              "nsresult: 0x" + (+preview.result).toString(16);

    if (preview.filename) {
      msg += "\nlocation: " + preview.filename;
      if (preview.lineNumber) {
        msg += ":" + preview.lineNumber;
      }
    }

    return msg + "]";
  },

  DOMEvent: function (aGrip, {concise}) {
    let {preview} = aGrip;
    if (!preview.type) {
      return null;
    }

    if (concise) {
      return aGrip.class + " " + preview.type;
    }

    let result = preview.type;

    if (preview.eventKind == "key" && preview.modifiers &&
        preview.modifiers.length) {
      result += " " + preview.modifiers.join("-");
    }

    let props = [];
    if (preview.target) {
      let target = VariablesView.getString(preview.target, { concise: true });
      props.push("target: " + target);
    }

    for (let prop in preview.properties) {
      let value = preview.properties[prop];
      props.push(prop + ": " + VariablesView.getString(value, { concise: true }));
    }

    return result + " {" + props.join(", ") + "}";
  }, // DOMEvent

  DOMNode: function (aGrip, {concise}) {
    let {preview} = aGrip;

    switch (preview.nodeType) {
      case nodeConstants.DOCUMENT_NODE: {
        let result = aGrip.class;
        if (preview.location) {
          result += ` \u2192 ${getSourceNames(preview.location)[concise ? "short" : "long"]}`;
        }

        return result;
      }

      case nodeConstants.ATTRIBUTE_NODE: {
        let value = VariablesView.getString(preview.value, { noStringQuotes: true });
        return preview.nodeName + '="' + escapeHTML(value) + '"';
      }

      case nodeConstants.TEXT_NODE:
        return preview.nodeName + " " +
               VariablesView.getString(preview.textContent);

      case nodeConstants.COMMENT_NODE: {
        let comment = VariablesView.getString(preview.textContent,
                                              { noStringQuotes: true });
        return "<!--" + comment + "-->";
      }

      case nodeConstants.DOCUMENT_FRAGMENT_NODE: {
        if (concise || !preview.childNodes) {
          return aGrip.class + "[" + preview.childNodesLength + "]";
        }
        let nodes = [];
        for (let node of preview.childNodes) {
          nodes.push(VariablesView.getString(node));
        }
        if (nodes.length < preview.childNodesLength) {
          let n = preview.childNodesLength - nodes.length;
          nodes.push(VariablesView.stringifiers._getNMoreString(n));
        }
        return aGrip.class + " [" + nodes.join(", ") + "]";
      }

      case nodeConstants.ELEMENT_NODE: {
        let attrs = preview.attributes;
        if (!concise) {
          let n = 0, result = "<" + preview.nodeName;
          for (let name in attrs) {
            let value = VariablesView.getString(attrs[name],
                                                { noStringQuotes: true });
            result += " " + name + '="' + escapeHTML(value) + '"';
            n++;
          }
          if (preview.attributesLength > n) {
            result += " " + ELLIPSIS;
          }
          return result + ">";
        }

        let result = "<" + preview.nodeName;
        if (attrs.id) {
          result += "#" + attrs.id;
        }

        if (attrs.class) {
          result += "." + attrs.class.trim().replace(/\s+/, ".");
        }
        return result + ">";
      }

      default:
        return null;
    }
  }, // DOMNode
}; // VariablesView.stringifiers.byObjectKind


/**
 * Get the "N more…" formatted string, given an N. This is used for displaying
 * how many elements are not displayed in an object preview (eg. an array).
 *
 * @private
 * @param number aNumber
 * @return string
 */
VariablesView.stringifiers._getNMoreString = function (aNumber) {
  let str = L10N.getStr("variablesViewMoreObjects");
  return PluralForm.get(aNumber, str).replace("#1", aNumber);
};

/**
 * Returns a custom class style for a grip.
 *
 * @param any aGrip
 *        @see Variable.setGrip
 * @return string
 *         The custom class style.
 */
VariablesView.getClass = function (aGrip) {
  if (aGrip && typeof aGrip == "object") {
    if (aGrip.preview) {
      switch (aGrip.preview.kind) {
        case "DOMNode":
          return "token-domnode";
      }
    }

    switch (aGrip.type) {
      case "undefined":
        return "token-undefined";
      case "null":
        return "token-null";
      case "Infinity":
      case "-Infinity":
      case "NaN":
      case "-0":
        return "token-number";
      case "longString":
        return "token-string";
    }
  }
  switch (typeof aGrip) {
    case "string":
      return "token-string";
    case "boolean":
      return "token-boolean";
    case "number":
      return "token-number";
    default:
      return "token-other";
  }
};

/**
 * A monotonically-increasing counter, that guarantees the uniqueness of scope,
 * variables and properties ids.
 *
 * @param string aName
 *        An optional string to prefix the id with.
 * @return number
 *         A unique id.
 */
var generateId = (function () {
  let count = 0;
  return function (aName = "") {
    return aName.toLowerCase().trim().replace(/\s+/g, "-") + (++count);
  };
})();

/**
 * Quote and escape a string. The result will be another string containing an
 * ECMAScript StringLiteral which will produce the original one when evaluated
 * by `eval` or similar.
 *
 * @param string aString
 *       An optional string to be escaped. If no string is passed, the function
 *       returns an empty string.
 * @return string
 */
function escapeString(aString) {
  if (typeof aString !== "string") {
    return "";
  }
  // U+2028 and U+2029 are allowed in JSON but not in ECMAScript string literals.
  return JSON.stringify(aString).replace(/\u2028/g, '\\u2028')
                                .replace(/\u2029/g, '\\u2029');
}

/**
 * Escape some HTML special characters. We do not need full HTML serialization
 * here, we just want to make strings safe to display in HTML attributes, for
 * the stringifiers.
 *
 * @param string aString
 * @return string
 */
function escapeHTML(aString) {
  return aString.replace(/&/g, "&amp;")
                .replace(/"/g, "&quot;")
                .replace(/</g, "&lt;")
                .replace(/>/g, "&gt;");
}


/**
 * An Editable encapsulates the UI of an edit box that overlays a label,
 * allowing the user to edit the value.
 *
 * @param Variable aVariable
 *        The Variable or Property to make editable.
 * @param object aOptions
 *        - onSave
 *          The callback to call with the value when editing is complete.
 *        - onCleanup
 *          The callback to call when the editable is removed for any reason.
 */
function Editable(aVariable, aOptions) {
  this._variable = aVariable;
  this._onSave = aOptions.onSave;
  this._onCleanup = aOptions.onCleanup;
}

Editable.create = function (aVariable, aOptions, aEvent) {
  let editable = new this(aVariable, aOptions);
  editable.activate(aEvent);
  return editable;
};

Editable.prototype = {
  /**
   * The class name for targeting this Editable type's label element. Overridden
   * by inheriting classes.
   */
  className: null,

  /**
   * Boolean indicating whether this Editable should activate. Overridden by
   * inheriting classes.
   */
  shouldActivate: null,

  /**
   * The label element for this Editable. Overridden by inheriting classes.
   */
  label: null,

  /**
   * Activate this editable by replacing the input box it overlays and
   * initialize the handlers.
   *
   * @param Event e [optional]
   *        Optionally, the Event object that was used to activate the Editable.
   */
  activate: function (e) {
    if (!this.shouldActivate) {
      this._onCleanup && this._onCleanup();
      return;
    }

    let { label } = this;
    let initialString = label.getAttribute("value");

    if (e) {
      e.preventDefault();
      e.stopPropagation();
    }

    // Create a texbox input element which will be shown in the current
    // element's specified label location.
    let input = this._input = this._variable.document.createElement("textbox");
    input.className = "plain " + this.className;
    input.setAttribute("value", initialString);
    input.setAttribute("flex", "1");

    // Replace the specified label with a textbox input element.
    label.parentNode.replaceChild(input, label);
    this._variable._variablesView.boxObject.ensureElementIsVisible(input);
    input.select();

    // When the value is a string (displayed as "value"), then we probably want
    // to change it to another string in the textbox, so to avoid typing the ""
    // again, tackle with the selection bounds just a bit.
    if (initialString.match(/^".+"$/)) {
      input.selectionEnd--;
      input.selectionStart++;
    }

    this._onKeydown = this._onKeydown.bind(this);
    this._onBlur = this._onBlur.bind(this);
    input.addEventListener("keydown", this._onKeydown);
    input.addEventListener("blur", this._onBlur);

    this._prevExpandable = this._variable.twisty;
    this._prevExpanded = this._variable.expanded;
    this._variable.collapse();
    this._variable.hideArrow();
    this._variable.locked = true;
    this._variable.editing = true;
  },

  /**
   * Remove the input box and restore the Variable or Property to its previous
   * state.
   */
  deactivate: function () {
    this._input.removeEventListener("keydown", this._onKeydown);
    this._input.removeEventListener("blur", this.deactivate);
    this._input.parentNode.replaceChild(this.label, this._input);
    this._input = null;

    let { boxObject } = this._variable._variablesView;
    boxObject.scrollBy(-this._variable._target, 0);
    this._variable.locked = false;
    this._variable.twisty = this._prevExpandable;
    this._variable.expanded = this._prevExpanded;
    this._variable.editing = false;
    this._onCleanup && this._onCleanup();
  },

  /**
   * Save the current value and deactivate the Editable.
   */
  _save: function () {
    let initial = this.label.getAttribute("value");
    let current = this._input.value.trim();
    this.deactivate();
    if (initial != current) {
      this._onSave(current);
    }
  },

  /**
   * Called when tab is pressed, allowing subclasses to link different
   * behavior to tabbing if desired.
   */
  _next: function () {
    this._save();
  },

  /**
   * Called when escape is pressed, indicating a cancelling of editing without
   * saving.
   */
  _reset: function () {
    this.deactivate();
    this._variable.focus();
  },

  /**
   * Event handler for when the input loses focus.
   */
  _onBlur: function () {
    this.deactivate();
  },

  /**
   * Event handler for when the input receives a key press.
   */
  _onKeydown: function (e) {
    e.stopPropagation();

    switch (e.keyCode) {
      case KeyCodes.DOM_VK_TAB:
        this._next();
        break;
      case KeyCodes.DOM_VK_RETURN:
        this._save();
        break;
      case KeyCodes.DOM_VK_ESCAPE:
        this._reset();
        break;
    }
  },
};


/**
 * An Editable specific to editing the name of a Variable or Property.
 */
function EditableName(aVariable, aOptions) {
  Editable.call(this, aVariable, aOptions);
}

EditableName.create = Editable.create;

EditableName.prototype = extend(Editable.prototype, {
  className: "element-name-input",

  get label() {
    return this._variable._name;
  },

  get shouldActivate() {
    return !!this._variable.ownerView.switch;
  },
});


/**
 * An Editable specific to editing the value of a Variable or Property.
 */
function EditableValue(aVariable, aOptions) {
  Editable.call(this, aVariable, aOptions);
}

EditableValue.create = Editable.create;

EditableValue.prototype = extend(Editable.prototype, {
  className: "element-value-input",

  get label() {
    return this._variable._valueLabel;
  },

  get shouldActivate() {
    return !!this._variable.ownerView.eval;
  },
});


/**
 * An Editable specific to editing the key and value of a new property.
 */
function EditableNameAndValue(aVariable, aOptions) {
  EditableName.call(this, aVariable, aOptions);
}

EditableNameAndValue.create = Editable.create;

EditableNameAndValue.prototype = extend(EditableName.prototype, {
  _reset: function (e) {
    // Hide the Variable or Property if the user presses escape.
    this._variable.remove();
    this.deactivate();
  },

  _next: function (e) {
    // Override _next so as to set both key and value at the same time.
    let key = this._input.value;
    this.label.setAttribute("value", key);

    let valueEditable = EditableValue.create(this._variable, {
      onSave: aValue => {
        this._onSave([key, aValue]);
      }
    });
    valueEditable._reset = () => {
      this._variable.remove();
      valueEditable.deactivate();
    };
  },

  _save: function (e) {
    // Both _save and _next activate the value edit box.
    this._next(e);
  }
});
PK
!<Ws@�HrHrRchrome/devtools/modules/devtools/client/shared/widgets/VariablesViewController.jsm/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

var {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
var {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
var {VariablesView} = require("resource://devtools/client/shared/widgets/VariablesView.jsm");
var Services = require("Services");
var promise = require("promise");
var defer = require("devtools/shared/defer");
var {LocalizationHelper, ELLIPSIS} = require("devtools/shared/l10n");

Object.defineProperty(this, "WebConsoleUtils", {
  get: function () {
    return require("devtools/client/webconsole/utils").Utils;
  },
  configurable: true,
  enumerable: true
});

XPCOMUtils.defineLazyGetter(this, "VARIABLES_SORTING_ENABLED", () =>
  Services.prefs.getBoolPref("devtools.debugger.ui.variables-sorting-enabled")
);

const MAX_LONG_STRING_LENGTH = 200000;
const MAX_PROPERTY_ITEMS = 2000;
const DBG_STRINGS_URI = "devtools/client/locales/debugger.properties";

this.EXPORTED_SYMBOLS = ["VariablesViewController", "StackFrameUtils"];

/**
 * Localization convenience methods.
 */
var L10N = new LocalizationHelper(DBG_STRINGS_URI);

/**
 * Controller for a VariablesView that handles interfacing with the debugger
 * protocol. Is able to populate scopes and variables via the protocol as well
 * as manage actor lifespans.
 *
 * @param VariablesView aView
 *        The view to attach to.
 * @param object aOptions [optional]
 *        Options for configuring the controller. Supported options:
 *        - getObjectClient: @see this._setClientGetters
 *        - getLongStringClient: @see this._setClientGetters
 *        - getEnvironmentClient: @see this._setClientGetters
 *        - releaseActor: @see this._setClientGetters
 *        - overrideValueEvalMacro: @see _setEvaluationMacros
 *        - getterOrSetterEvalMacro: @see _setEvaluationMacros
 *        - simpleValueEvalMacro: @see _setEvaluationMacros
 */
function VariablesViewController(aView, aOptions = {}) {
  this.addExpander = this.addExpander.bind(this);

  this._setClientGetters(aOptions);
  this._setEvaluationMacros(aOptions);

  this._actors = new Set();
  this.view = aView;
  this.view.controller = this;
}
this.VariablesViewController = VariablesViewController;

VariablesViewController.prototype = {
  /**
   * The default getter/setter evaluation macro.
   */
  _getterOrSetterEvalMacro: VariablesView.getterOrSetterEvalMacro,

  /**
   * The default override value evaluation macro.
   */
  _overrideValueEvalMacro: VariablesView.overrideValueEvalMacro,

  /**
   * The default simple value evaluation macro.
   */
  _simpleValueEvalMacro: VariablesView.simpleValueEvalMacro,

  /**
   * Set the functions used to retrieve debugger client grips.
   *
   * @param object aOptions
   *        Options for getting the client grips. Supported options:
   *        - getObjectClient: callback for creating an object grip client
   *        - getLongStringClient: callback for creating a long string grip client
   *        - getEnvironmentClient: callback for creating an environment client
   *        - releaseActor: callback for releasing an actor when it's no longer needed
   */
  _setClientGetters: function (aOptions) {
    if (aOptions.getObjectClient) {
      this._getObjectClient = aOptions.getObjectClient;
    }
    if (aOptions.getLongStringClient) {
      this._getLongStringClient = aOptions.getLongStringClient;
    }
    if (aOptions.getEnvironmentClient) {
      this._getEnvironmentClient = aOptions.getEnvironmentClient;
    }
    if (aOptions.releaseActor) {
      this._releaseActor = aOptions.releaseActor;
    }
  },

  /**
   * Sets the functions used when evaluating strings in the variables view.
   *
   * @param object aOptions
   *        Options for configuring the macros. Supported options:
   *        - overrideValueEvalMacro: callback for creating an overriding eval macro
   *        - getterOrSetterEvalMacro: callback for creating a getter/setter eval macro
   *        - simpleValueEvalMacro: callback for creating a simple value eval macro
   */
  _setEvaluationMacros: function (aOptions) {
    if (aOptions.overrideValueEvalMacro) {
      this._overrideValueEvalMacro = aOptions.overrideValueEvalMacro;
    }
    if (aOptions.getterOrSetterEvalMacro) {
      this._getterOrSetterEvalMacro = aOptions.getterOrSetterEvalMacro;
    }
    if (aOptions.simpleValueEvalMacro) {
      this._simpleValueEvalMacro = aOptions.simpleValueEvalMacro;
    }
  },

  /**
   * Populate a long string into a target using a grip.
   *
   * @param Variable aTarget
   *        The target Variable/Property to put the retrieved string into.
   * @param LongStringActor aGrip
   *        The long string grip that use to retrieve the full string.
   * @return Promise
   *         The promise that will be resolved when the string is retrieved.
   */
  _populateFromLongString: function (aTarget, aGrip) {
    let deferred = defer();

    let from = aGrip.initial.length;
    let to = Math.min(aGrip.length, MAX_LONG_STRING_LENGTH);

    this._getLongStringClient(aGrip).substring(from, to, aResponse => {
      // Stop tracking the actor because it's no longer needed.
      this.releaseActor(aGrip);

      // Replace the preview with the full string and make it non-expandable.
      aTarget.onexpand = null;
      aTarget.setGrip(aGrip.initial + aResponse.substring);
      aTarget.hideArrow();

      deferred.resolve();
    });

    return deferred.promise;
  },

  /**
   * Adds pseudo items in case there is too many properties to display.
   * Each item can expand into property slices.
   *
   * @param Scope aTarget
   *        The Scope where the properties will be placed into.
   * @param object aGrip
   *        The property iterator grip.
   */
  _populatePropertySlices: function (aTarget, aGrip) {
    if (aGrip.count < MAX_PROPERTY_ITEMS) {
      return this._populateFromPropertyIterator(aTarget, aGrip);
    }

    // Divide the keys into quarters.
    let items = Math.ceil(aGrip.count / 4);
    let iterator = aGrip.propertyIterator;
    let promises = [];
    for (let i = 0; i < 4; i++) {
      let start = aGrip.start + i * items;
      let count = i != 3 ? items : aGrip.count - i * items;

      // Create a new kind of grip, with additional fields to define the slice
      let sliceGrip = {
        type: "property-iterator",
        propertyIterator: iterator,
        start: start,
        count: count
      };

      // Query the name of the first and last items for this slice
      let deferred = defer();
      iterator.names([start, start + count - 1], ({ names }) => {
          let label = "[" + names[0] + ELLIPSIS + names[1] + "]";
        let item = aTarget.addItem(label, {}, { internalItem: true });
        item.showArrow();
        this.addExpander(item, sliceGrip);
        deferred.resolve();
      });
      promises.push(deferred.promise);
    }

    return promise.all(promises);
  },

  /**
   * Adds a property slice for a Variable in the view using the already
   * property iterator
   *
   * @param Scope aTarget
   *        The Scope where the properties will be placed into.
   * @param object aGrip
   *        The property iterator grip.
   */
  _populateFromPropertyIterator: function (aTarget, aGrip) {
    if (aGrip.count >= MAX_PROPERTY_ITEMS) {
      // We already started to split, but there is still too many properties, split again.
      return this._populatePropertySlices(aTarget, aGrip);
    }
    // We started slicing properties, and the slice is now small enough to be displayed
    let deferred = defer();
    aGrip.propertyIterator.slice(aGrip.start, aGrip.count,
      ({ ownProperties }) => {
        // Add all the variable properties.
        if (Object.keys(ownProperties).length > 0) {
          aTarget.addItems(ownProperties, {
            sorted: true,
            // Expansion handlers must be set after the properties are added.
            callback: this.addExpander
          });
        }
        deferred.resolve();
      });
    return deferred.promise;
  },

  /**
   * Adds the properties for a Variable in the view using a new feature in FF40+
   * that allows iteration over properties in slices.
   *
   * @param Scope aTarget
   *        The Scope where the properties will be placed into.
   * @param object aGrip
   *        The grip to use to populate the target.
   * @param string aQuery [optional]
   *        The query string used to fetch only a subset of properties
   */
  _populateFromObjectWithIterator: function (aTarget, aGrip, aQuery) {
    // FF40+ starts exposing `ownPropertyLength` on ObjectActor's grip,
    // as well as `enumProperties` request.
    let deferred = defer();
    let objectClient = this._getObjectClient(aGrip);
    let isArray = aGrip.preview && aGrip.preview.kind === "ArrayLike";
    if (isArray) {
      // First enumerate array items, e.g. properties from `0` to `array.length`.
      let options = {
        ignoreNonIndexedProperties: true,
        query: aQuery
      };
      objectClient.enumProperties(options, ({ iterator }) => {
        let sliceGrip = {
          type: "property-iterator",
          propertyIterator: iterator,
          start: 0,
          count: iterator.count
        };
        this._populatePropertySlices(aTarget, sliceGrip)
            .then(() => {
          // Then enumerate the rest of the properties, like length, buffer, etc.
              let options = {
                ignoreIndexedProperties: true,
                sort: true,
                query: aQuery
              };
              objectClient.enumProperties(options, ({ iterator }) => {
                let sliceGrip = {
                  type: "property-iterator",
                  propertyIterator: iterator,
                  start: 0,
                  count: iterator.count
                };
                deferred.resolve(this._populatePropertySlices(aTarget, sliceGrip));
              });
            });
      });
    } else {
      // For objects, we just enumerate all the properties sorted by name.
      objectClient.enumProperties({ sort: true, query: aQuery }, ({ iterator }) => {
        let sliceGrip = {
          type: "property-iterator",
          propertyIterator: iterator,
          start: 0,
          count: iterator.count
        };
        deferred.resolve(this._populatePropertySlices(aTarget, sliceGrip));
      });

    }
    return deferred.promise;
  },

  /**
   * Adds the given prototype in the view.
   *
   * @param Scope aTarget
   *        The Scope where the properties will be placed into.
   * @param object aProtype
   *        The prototype grip.
   */
  _populateObjectPrototype: function (aTarget, aPrototype) {
    // Add the variable's __proto__.
    if (aPrototype && aPrototype.type != "null") {
      let proto = aTarget.addItem("__proto__", { value: aPrototype });
      this.addExpander(proto, aPrototype);
    }
  },

  /**
   * Adds properties to a Scope, Variable, or Property in the view. Triggered
   * when a scope is expanded or certain variables are hovered.
   *
   * @param Scope aTarget
   *        The Scope where the properties will be placed into.
   * @param object aGrip
   *        The grip to use to populate the target.
   */
  _populateFromObject: function (aTarget, aGrip) {
    if (aGrip.class === "Proxy") {
      this.addExpander(
        aTarget.addItem("<target>", { value: aGrip.proxyTarget }, { internalItem: true }),
        aGrip.proxyTarget);
      this.addExpander(
        aTarget.addItem("<handler>", { value: aGrip.proxyHandler }, { internalItem: true }),
        aGrip.proxyHandler);

      // Refuse to play the proxy's stupid game and return immediately
      let deferred = defer();
      deferred.resolve();
      return deferred.promise;
    }

    if (aGrip.class === "Promise" && aGrip.promiseState) {
      const { state, value, reason } = aGrip.promiseState;
      aTarget.addItem("<state>", { value: state }, { internalItem: true });
      if (state === "fulfilled") {
        this.addExpander(
          aTarget.addItem("<value>", { value }, { internalItem: true }),
          value);
      } else if (state === "rejected") {
        this.addExpander(
          aTarget.addItem("<reason>", { value: reason }, { internalItem: true }),
          reason);
      }
    } else if (["Map", "WeakMap", "Set", "WeakSet"].includes(aGrip.class)) {
      let entriesList = aTarget.addItem("<entries>", {}, { internalItem: true });
      entriesList.showArrow();
      this.addExpander(entriesList, {
        type: "entries-list",
        obj: aGrip
      });
    }

    // Fetch properties by slices if there is too many in order to prevent UI freeze.
    if ("ownPropertyLength" in aGrip && aGrip.ownPropertyLength >= MAX_PROPERTY_ITEMS) {
      return this._populateFromObjectWithIterator(aTarget, aGrip)
                 .then(() => {
                   let deferred = defer();
                   let objectClient = this._getObjectClient(aGrip);
                   objectClient.getPrototype(({ prototype }) => {
                     this._populateObjectPrototype(aTarget, prototype);
                     deferred.resolve();
                   });
                   return deferred.promise;
                 });
    }

    return this._populateProperties(aTarget, aGrip);
  },

  _populateProperties: function (aTarget, aGrip, aOptions) {
    let deferred = defer();

    let objectClient = this._getObjectClient(aGrip);
    objectClient.getPrototypeAndProperties(aResponse => {
      let ownProperties = aResponse.ownProperties || {};
      let prototype = aResponse.prototype || null;
      // 'safeGetterValues' is new and isn't necessary defined on old actors.
      let safeGetterValues = aResponse.safeGetterValues || {};
      let sortable = VariablesView.isSortable(aGrip.class);

      // Merge the safe getter values into one object such that we can use it
      // in VariablesView.
      for (let name of Object.keys(safeGetterValues)) {
        if (name in ownProperties) {
          let { getterValue, getterPrototypeLevel } = safeGetterValues[name];
          ownProperties[name].getterValue = getterValue;
          ownProperties[name].getterPrototypeLevel = getterPrototypeLevel;
        } else {
          ownProperties[name] = safeGetterValues[name];
        }
      }

      // Add all the variable properties.
      aTarget.addItems(ownProperties, {
        // Not all variables need to force sorted properties.
        sorted: sortable,
        // Expansion handlers must be set after the properties are added.
        callback: this.addExpander
      });

      // Add the variable's __proto__.
      this._populateObjectPrototype(aTarget, prototype);

      // If the object is a function we need to fetch its scope chain
      // to show them as closures for the respective function.
      if (aGrip.class == "Function") {
        objectClient.getScope(aResponse => {
          if (aResponse.error) {
            // This function is bound to a built-in object or it's not present
            // in the current scope chain. Not necessarily an actual error,
            // it just means that there's no closure for the function.
            console.warn(aResponse.error + ": " + aResponse.message);
            return void deferred.resolve();
          }
          this._populateWithClosure(aTarget, aResponse.scope).then(deferred.resolve);
        });
      } else {
        deferred.resolve();
      }
    });

    return deferred.promise;
  },

  /**
   * Adds the scope chain elements (closures) of a function variable.
   *
   * @param Variable aTarget
   *        The variable where the properties will be placed into.
   * @param Scope aScope
   *        The lexical environment form as specified in the protocol.
   */
  _populateWithClosure: function (aTarget, aScope) {
    let objectScopes = [];
    let environment = aScope;
    let funcScope = aTarget.addItem("<Closure>");
    funcScope.target.setAttribute("scope", "");
    funcScope.showArrow();

    do {
      // Create a scope to contain all the inspected variables.
      let label = StackFrameUtils.getScopeLabel(environment);

      // Block scopes may have the same label, so make addItem allow duplicates.
      let closure = funcScope.addItem(label, undefined, {relaxed: true});
      closure.target.setAttribute("scope", "");
      closure.showArrow();

      // Add nodes for every argument and every other variable in scope.
      if (environment.bindings) {
        this._populateWithEnvironmentBindings(closure, environment.bindings);
      } else {
        let deferred = defer();
        objectScopes.push(deferred.promise);
        this._getEnvironmentClient(environment).getBindings(response => {
          this._populateWithEnvironmentBindings(closure, response.bindings);
          deferred.resolve();
        });
      }
    } while ((environment = environment.parent));

    return promise.all(objectScopes).then(() => {
      // Signal that scopes have been fetched.
      this.view.emit("fetched", "scopes", funcScope);
    });
  },

  /**
   * Adds nodes for every specified binding to the closure node.
   *
   * @param Variable aTarget
   *        The variable where the bindings will be placed into.
   * @param object aBindings
   *        The bindings form as specified in the protocol.
   */
  _populateWithEnvironmentBindings: function (aTarget, aBindings) {
    // Add nodes for every argument in the scope.
    aTarget.addItems(aBindings.arguments.reduce((accumulator, arg) => {
      let name = Object.getOwnPropertyNames(arg)[0];
      let descriptor = arg[name];
      accumulator[name] = descriptor;
      return accumulator;
    }, {}), {
      // Arguments aren't sorted.
      sorted: false,
      // Expansion handlers must be set after the properties are added.
      callback: this.addExpander
    });

    // Add nodes for every other variable in the scope.
    aTarget.addItems(aBindings.variables, {
      // Not all variables need to force sorted properties.
      sorted: VARIABLES_SORTING_ENABLED,
      // Expansion handlers must be set after the properties are added.
      callback: this.addExpander
    });
  },

  _populateFromEntries: function (target, grip) {
    let objGrip = grip.obj;
    let objectClient = this._getObjectClient(objGrip);

    return new promise((resolve, reject) => {
      objectClient.enumEntries((response) => {
        if (response.error) {
          // Older server might not support the enumEntries method
          console.warn(response.error + ": " + response.message);
          resolve();
        } else {
          let sliceGrip = {
            type: "property-iterator",
            propertyIterator: response.iterator,
            start: 0,
            count: response.iterator.count
          };

          resolve(this._populatePropertySlices(target, sliceGrip));
        }
      });
    });
  },

  /**
   * Adds an 'onexpand' callback for a variable, lazily handling
   * the addition of new properties.
   *
   * @param Variable aTarget
   *        The variable where the properties will be placed into.
   * @param any aSource
   *        The source to use to populate the target.
   */
  addExpander: function (aTarget, aSource) {
    // Attach evaluation macros as necessary.
    if (aTarget.getter || aTarget.setter) {
      aTarget.evaluationMacro = this._overrideValueEvalMacro;
      let getter = aTarget.get("get");
      if (getter) {
        getter.evaluationMacro = this._getterOrSetterEvalMacro;
      }
      let setter = aTarget.get("set");
      if (setter) {
        setter.evaluationMacro = this._getterOrSetterEvalMacro;
      }
    } else {
      aTarget.evaluationMacro = this._simpleValueEvalMacro;
    }

    // If the source is primitive then an expander is not needed.
    if (VariablesView.isPrimitive({ value: aSource })) {
      return;
    }

    // If the source is a long string then show the arrow.
    if (WebConsoleUtils.isActorGrip(aSource) && aSource.type == "longString") {
      aTarget.showArrow();
    }

    // Make sure that properties are always available on expansion.
    aTarget.onexpand = () => this.populate(aTarget, aSource);

    // Some variables are likely to contain a very large number of properties.
    // It's a good idea to be prepared in case of an expansion.
    if (aTarget.shouldPrefetch) {
      aTarget.addEventListener("mouseover", aTarget.onexpand);
    }

    // Register all the actors that this controller now depends on.
    for (let grip of [aTarget.value, aTarget.getter, aTarget.setter]) {
      if (WebConsoleUtils.isActorGrip(grip)) {
        this._actors.add(grip.actor);
      }
    }
  },

  /**
   * Adds properties to a Scope, Variable, or Property in the view. Triggered
   * when a scope is expanded or certain variables are hovered.
   *
   * This does not expand the target, it only populates it.
   *
   * @param Scope aTarget
   *        The Scope to be expanded.
   * @param object aSource
   *        The source to use to populate the target.
   * @return Promise
   *         The promise that is resolved once the target has been expanded.
   */
  populate: function (aTarget, aSource) {
    // Fetch the variables only once.
    if (aTarget._fetched) {
      return aTarget._fetched;
    }
    // Make sure the source grip is available.
    if (!aSource) {
      return promise.reject(new Error("No actor grip was given for the variable."));
    }

    let deferred = defer();
    aTarget._fetched = deferred.promise;

    if (aSource.type === "property-iterator") {
      return this._populateFromPropertyIterator(aTarget, aSource);
    }

    if (aSource.type === "entries-list") {
      return this._populateFromEntries(aTarget, aSource);
    }

    if (aSource.type === "mapEntry") {
      aTarget.addItems({
        key: { value: aSource.preview.key },
        value: { value: aSource.preview.value }
      }, {
        callback: this.addExpander
      });

      return promise.resolve();
    }

    // If the target is a Variable or Property then we're fetching properties.
    if (VariablesView.isVariable(aTarget)) {
      this._populateFromObject(aTarget, aSource).then(() => {
        // Signal that properties have been fetched.
        this.view.emit("fetched", "properties", aTarget);
        // Commit the hierarchy because new items were added.
        this.view.commitHierarchy();
        deferred.resolve();
      });
      return deferred.promise;
    }

    switch (aSource.type) {
      case "longString":
        this._populateFromLongString(aTarget, aSource).then(() => {
          // Signal that a long string has been fetched.
          this.view.emit("fetched", "longString", aTarget);
          deferred.resolve();
        });
        break;
      case "with":
      case "object":
        this._populateFromObject(aTarget, aSource.object).then(() => {
          // Signal that variables have been fetched.
          this.view.emit("fetched", "variables", aTarget);
          // Commit the hierarchy because new items were added.
          this.view.commitHierarchy();
          deferred.resolve();
        });
        break;
      case "block":
      case "function":
        this._populateWithEnvironmentBindings(aTarget, aSource.bindings);
        // No need to signal that variables have been fetched, since
        // the scope arguments and variables are already attached to the
        // environment bindings, so pausing the active thread is unnecessary.
        // Commit the hierarchy because new items were added.
        this.view.commitHierarchy();
        deferred.resolve();
        break;
      default:
        let error = "Unknown Debugger.Environment type: " + aSource.type;
        console.error(error);
        deferred.reject(error);
    }

    return deferred.promise;
  },

  /**
   * Indicates to the view if the targeted actor supports properties search
   *
   * @return boolean True, if the actor supports enumProperty request
   */
  supportsSearch: function () {
    // FF40+ starts exposing ownPropertyLength on object actor's grip
    // as well as enumProperty which allows to query a subset of properties.
    return this.objectActor && ("ownPropertyLength" in this.objectActor);
  },

  /**
   * Try to use the actor to perform an attribute search.
   *
   * @param Scope aScope
   *        The Scope instance to populate with properties
   * @param string aToken
   *        The query string
   */
  performSearch: function (aScope, aToken) {
    this._populateFromObjectWithIterator(aScope, this.objectActor, aToken)
        .then(() => {
          this.view.emit("fetched", "search", aScope);
        });
  },

  /**
   * Release an actor from the controller.
   *
   * @param object aActor
   *        The actor to release.
   */
  releaseActor: function (aActor) {
    if (this._releaseActor) {
      this._releaseActor(aActor);
    }
    this._actors.delete(aActor);
  },

  /**
   * Release all the actors referenced by the controller, optionally filtered.
   *
   * @param function aFilter [optional]
   *        Callback to filter which actors are released.
   */
  releaseActors: function (aFilter) {
    for (let actor of this._actors) {
      if (!aFilter || aFilter(actor)) {
        this.releaseActor(actor);
      }
    }
  },

  /**
   * Helper function for setting up a single Scope with a single Variable
   * contained within it.
   *
   * This function will empty the variables view.
   *
   * @param object options
   *        Options for the contents of the view:
   *        - objectActor: the grip of the new ObjectActor to show.
   *        - rawObject: the raw object to show.
   *        - label: the label for the inspected object.
   * @param object configuration
   *        Additional options for the controller:
   *        - overrideValueEvalMacro: @see _setEvaluationMacros
   *        - getterOrSetterEvalMacro: @see _setEvaluationMacros
   *        - simpleValueEvalMacro: @see _setEvaluationMacros
   * @return Object
   *         - variable: the created Variable.
   *         - expanded: the Promise that resolves when the variable expands.
   */
  setSingleVariable: function (options, configuration = {}) {
    this._setEvaluationMacros(configuration);
    this.view.empty();

    let scope = this.view.addScope(options.label);
    scope.expanded = true; // Expand the scope by default.
    scope.locked = true; // Prevent collapsing the scope.

    let variable = scope.addItem(undefined, { enumerable: true });
    let populated;

    if (options.objectActor) {
      // Save objectActor for properties filtering
      this.objectActor = options.objectActor;
      if (VariablesView.isPrimitive({ value: this.objectActor })) {
        populated = promise.resolve();
      } else {
        populated = this.populate(variable, options.objectActor);
        variable.expand();
      }
    } else if (options.rawObject) {
      variable.populate(options.rawObject, { expanded: true });
      populated = promise.resolve();
    }

    return { variable: variable, expanded: populated };
  },
};


/**
 * Attaches a VariablesViewController to a VariablesView if it doesn't already
 * have one.
 *
 * @param VariablesView aView
 *        The view to attach to.
 * @param object aOptions
 *        The options to use in creating the controller.
 * @return VariablesViewController
 */
VariablesViewController.attach = function (aView, aOptions) {
  if (aView.controller) {
    return aView.controller;
  }
  return new VariablesViewController(aView, aOptions);
};

/**
 * Utility functions for handling stackframes.
 */
var StackFrameUtils = this.StackFrameUtils = {
  /**
   * Create a textual representation for the specified stack frame
   * to display in the stackframes container.
   *
   * @param object aFrame
   *        The stack frame to label.
   */
  getFrameTitle: function (aFrame) {
    if (aFrame.type == "call") {
      let c = aFrame.callee;
      return (c.name || c.userDisplayName || c.displayName || "(anonymous)");
    }
    return "(" + aFrame.type + ")";
  },

  /**
   * Constructs a scope label based on its environment.
   *
   * @param object aEnv
   *        The scope's environment.
   * @return string
   *         The scope's label.
   */
  getScopeLabel: function (aEnv) {
    let name = "";

    // Name the outermost scope Global.
    if (!aEnv.parent) {
      name = L10N.getStr("globalScopeLabel");
    }
    // Otherwise construct the scope name.
    else {
      name = aEnv.type.charAt(0).toUpperCase() + aEnv.type.slice(1);
    }

    let label = L10N.getFormatStr("scopeLabel", name);
    switch (aEnv.type) {
      case "with":
      case "object":
        label += " [" + aEnv.object.class + "]";
        break;
      case "function":
        let f = aEnv.function;
        label += " [" +
          (f.name || f.userDisplayName || f.displayName || "(anonymous)") +
        "]";
        break;
    }
    return label;
  }
};
PK
!< 7��n+n+Tchrome/devtools/modules/devtools/client/shared/widgets/tooltip/EventTooltipHelper.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 {LocalizationHelper} = require("devtools/shared/l10n");
const L10N = new LocalizationHelper("devtools/client/locales/inspector.properties");

const Editor = require("devtools/client/sourceeditor/editor");
const beautify = require("devtools/shared/jsbeautify/beautify");

const XHTML_NS = "http://www.w3.org/1999/xhtml";
const CONTAINER_WIDTH = 500;

/**
 * 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
 */
function setEventTooltip(tooltip, eventListenerInfos, toolbox) {
  let eventTooltip = new EventTooltip(tooltip, eventListenerInfos, toolbox);
  eventTooltip.init();
}

function EventTooltip(tooltip, eventListenerInfos, toolbox) {
  this._tooltip = tooltip;
  this._eventListenerInfos = eventListenerInfos;
  this._toolbox = toolbox;
  this._eventEditors = new WeakMap();

  // Used in tests: add a reference to the EventTooltip instance on the HTMLTooltip.
  this._tooltip.eventTooltip = this;

  this._headerClicked = this._headerClicked.bind(this);
  this._debugClicked = this._debugClicked.bind(this);
  this.destroy = this.destroy.bind(this);
  this._subscriptions = [];
}

EventTooltip.prototype = {
  init: function () {
    let config = {
      mode: Editor.modes.js,
      lineNumbers: false,
      lineWrapping: true,
      readOnly: true,
      styleActiveLine: true,
      extraKeys: {},
      theme: "mozilla markup-view"
    };

    let doc = this._tooltip.doc;
    this.container = doc.createElementNS(XHTML_NS, "div");
    this.container.className = "devtools-tooltip-events-container";

    const sourceMapService = this._toolbox.sourceMapURLService;

    const Bubbling = L10N.getStr("eventsTooltip.Bubbling");
    const Capturing = L10N.getStr("eventsTooltip.Capturing");
    for (let listener of this._eventListenerInfos) {
      let phase = listener.capturing ? Capturing : Bubbling;
      let level = listener.DOM0 ? "DOM0" : "DOM2";

      // Create this early so we can refer to it from a closure, below.
      let content = doc.createElementNS(XHTML_NS, "div");

      // Header
      let header = doc.createElementNS(XHTML_NS, "div");
      header.className = "event-header devtools-toolbar";
      this.container.appendChild(header);

      if (!listener.hide.type) {
        let eventTypeLabel = doc.createElementNS(XHTML_NS, "span");
        eventTypeLabel.className = "event-tooltip-event-type";
        eventTypeLabel.textContent = listener.type;
        eventTypeLabel.setAttribute("title", listener.type);
        header.appendChild(eventTypeLabel);
      }

      let filename = doc.createElementNS(XHTML_NS, "span");
      filename.className = "event-tooltip-filename devtools-monospace";

      let text = listener.origin;
      let title = text;
      if (listener.hide.filename) {
        text = L10N.getStr("eventsTooltip.unknownLocation");
        title = L10N.getStr("eventsTooltip.unknownLocationExplanation");
      } else {
        const location = this._parseLocation(text);
        if (location) {
          let callback = (enabled, url, line, column) => {
            // Do nothing if the tooltip was destroyed while we were
            // waiting for a response.
            if (this._tooltip) {
              const newUrl = enabled ? url : location.url;
              const newLine = enabled ? line : location.line;

              let newURI = newUrl + ":" + newLine;
              filename.textContent = newURI;
              filename.setAttribute("title", newURI);
              let eventEditor = this._eventEditors.get(content);
              eventEditor.uri = newURI;

              // This is emitted for testing.
              this._tooltip.emit("event-tooltip-source-map-ready");
            }
          };

          sourceMapService.subscribe(location.url, location.line, null, callback);
          this._subscriptions.push({
            url: location.url,
            line: location.line,
            callback
          });
        }
      }

      filename.textContent = text;
      filename.setAttribute("title", title);
      header.appendChild(filename);

      if (!listener.hide.debugger) {
        let debuggerIcon = doc.createElementNS(XHTML_NS, "img");
        debuggerIcon.className = "event-tooltip-debugger-icon";
        debuggerIcon.setAttribute("src",
          "chrome://devtools/skin/images/jump-definition.svg");
        let openInDebugger = L10N.getStr("eventsTooltip.openInDebugger");
        debuggerIcon.setAttribute("title", openInDebugger);
        header.appendChild(debuggerIcon);
      }

      let attributesContainer = doc.createElementNS(XHTML_NS, "div");
      attributesContainer.className = "event-tooltip-attributes-container";
      header.appendChild(attributesContainer);

      if (!listener.hide.capturing) {
        let attributesBox = doc.createElementNS(XHTML_NS, "div");
        attributesBox.className = "event-tooltip-attributes-box";
        attributesContainer.appendChild(attributesBox);

        let capturing = doc.createElementNS(XHTML_NS, "span");
        capturing.className = "event-tooltip-attributes";
        capturing.textContent = phase;
        capturing.setAttribute("title", phase);
        attributesBox.appendChild(capturing);
      }

      if (listener.tags) {
        for (let tag of listener.tags.split(",")) {
          let attributesBox = doc.createElementNS(XHTML_NS, "div");
          attributesBox.className = "event-tooltip-attributes-box";
          attributesContainer.appendChild(attributesBox);

          let tagBox = doc.createElementNS(XHTML_NS, "span");
          tagBox.className = "event-tooltip-attributes";
          tagBox.textContent = tag;
          tagBox.setAttribute("title", tag);
          attributesBox.appendChild(tagBox);
        }
      }

      if (!listener.hide.dom0) {
        let attributesBox = doc.createElementNS(XHTML_NS, "div");
        attributesBox.className = "event-tooltip-attributes-box";
        attributesContainer.appendChild(attributesBox);

        let dom0 = doc.createElementNS(XHTML_NS, "span");
        dom0.className = "event-tooltip-attributes";
        dom0.textContent = level;
        attributesBox.appendChild(dom0);
      }

      // Content
      let editor = new Editor(config);
      this._eventEditors.set(content, {
        editor: editor,
        handler: listener.handler,
        uri: listener.origin,
        dom0: listener.DOM0,
        native: listener.native,
        appended: false,
      });

      content.className = "event-tooltip-content-box";
      this.container.appendChild(content);

      this._addContentListeners(header);
    }

    this._tooltip.setContent(this.container, {width: CONTAINER_WIDTH});
    this._tooltip.on("hidden", this.destroy);
  },

  _addContentListeners: function (header) {
    header.addEventListener("click", this._headerClicked);
  },

  _headerClicked: function (event) {
    if (event.target.classList.contains("event-tooltip-debugger-icon")) {
      this._debugClicked(event);
      event.stopPropagation();
      return;
    }

    let doc = this._tooltip.doc;
    let header = event.currentTarget;
    let content = header.nextElementSibling;
    header.classList.toggle("content-expanded");

    if (content.hasAttribute("open")) {
      content.removeAttribute("open");
    } else {
      let contentNodes = doc.querySelectorAll(".event-tooltip-content-box");

      for (let node of contentNodes) {
        if (node !== content) {
          node.removeAttribute("open");
        }
      }

      content.setAttribute("open", "");

      let eventEditor = this._eventEditors.get(content);

      if (eventEditor.appended) {
        return;
      }

      let {editor, handler} = eventEditor;

      let iframe = doc.createElementNS(XHTML_NS, "iframe");
      iframe.setAttribute("style", "width: 100%; height: 100%; border-style: none;");

      editor.appendTo(content, iframe).then(() => {
        let tidied = beautify.js(handler, { "indent_size": 2 });
        editor.setText(tidied);

        eventEditor.appended = true;

        let 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.emit("event-tooltip-ready");
      });
    }
  },

  _debugClicked: function (event) {
    let header = event.currentTarget;
    let content = header.nextElementSibling;

    let {uri} = this._eventEditors.get(content);

    let location = this._parseLocation(uri);
    if (location) {
      // Save a copy of toolbox as it will be set to null when we hide the tooltip.
      let toolbox = this._toolbox;

      this._tooltip.hide();

      toolbox.viewSourceInDebugger(location.url, location.line);
    }
  },

  /**
   * Parse URI and return {url, line}; or return null if it can't be parsed.
   */
  _parseLocation: function (uri) {
    if (uri && uri !== "?") {
      uri = uri.replace(/"/g, "");

      let matches = uri.match(/(.*):(\d+$)/);

      if (matches) {
        return {
          url: matches[1],
          line: parseInt(matches[2], 10),
        };
      }
      return {url: uri, line: 1};
    }
    return null;
  },

  destroy: function () {
    if (this._tooltip) {
      this._tooltip.off("hidden", this.destroy);

      let boxes = this.container.querySelectorAll(".event-tooltip-content-box");

      for (let box of boxes) {
        let {editor} = this._eventEditors.get(box);
        editor.destroy();
      }

      this._eventEditors = null;
      this._tooltip.eventTooltip = null;
    }

    let headerNodes = this.container.querySelectorAll(".event-header");

    for (let node of headerNodes) {
      node.removeEventListener("click", this._headerClicked);
    }

    let sourceNodes = this.container.querySelectorAll(".event-tooltip-debugger-icon");
    for (let node of sourceNodes) {
      node.removeEventListener("click", this._debugClicked);
    }

    const sourceMapService = this._toolbox.sourceMapURLService;
    for (let subscription of this._subscriptions) {
      sourceMapService.unsubscribe(subscription.url, subscription.line, null,
                                   subscription.callback);
    }

    this._eventListenerInfos = this._toolbox = this._tooltip = null;
  }
};

module.exports.setEventTooltip = setEventTooltip;
PK
!<F.��BVBVMchrome/devtools/modules/devtools/client/shared/widgets/tooltip/HTMLTooltip.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 EventEmitter = require("devtools/shared/event-emitter");
const {TooltipToggle} = require("devtools/client/shared/widgets/tooltip/TooltipToggle");
const {listenOnce} = require("devtools/shared/async-utils");
const {Task} = require("devtools/shared/task");

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",
};

module.exports.TYPE = TYPE;

const ARROW_WIDTH = 32;

// Default offset between the tooltip's left edge and the tooltip arrow.
const ARROW_OFFSET = 20;

const EXTRA_HEIGHT = {
  "normal": 0,
  // The arrow is 16px tall, but merges on 3px with the panel border
  "arrow": 13,
};

const EXTRA_BORDER = {
  "normal": 0,
  "arrow": 3,
};

/**
 * 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".
 * @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 =
function (anchorRect, viewportRect, height, pos, offset) {
  let {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.
  let availableTop = anchorTop;
  let 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.
  let availableHeight = pos === TOP ? availableTop : availableBottom;
  height = Math.min(height, availableHeight - offset);
  height = Math.floor(height);

  // 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, height, computedPosition: pos};
};

/**
 * 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} width
 *        Preferred width for the tooltip.
 * @param {String} type
 *        The tooltip type (e.g. "arrow").
 * @param {Number} offset
 *        Horizontal offset in pixels.
 * @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 =
function (anchorRect, viewportRect, width, type, offset, isRtl) {
  let anchorWidth = anchorRect.width;
  let anchorStart = isRtl ? anchorRect.right : anchorRect.left;

  // Translate to the available viewport space before calculating dimensions and position.
  anchorStart -= viewportRect.left;

  // Calculate WIDTH.
  width = Math.min(width, viewportRect.width);

  // Calculate LEFT.
  // By default the tooltip is aligned with the anchor left edge. Unless this
  // makes it overflow the viewport, in which case is shifts to the left.
  let left = anchorStart + offset - (isRtl ? width : 0);
  left = Math.min(left, viewportRect.width - width);
  left = Math.max(0, left);

  // Calculate ARROW LEFT (tooltip's LEFT might be updated)
  let arrowLeft;
  // Arrow style tooltips may need to be shifted to the left
  if (type === TYPE.ARROW) {
    let arrowCenter = left + ARROW_OFFSET + ARROW_WIDTH / 2;
    let anchorCenter = anchorStart + anchorWidth / 2;
    // If the anchor is too narrow, align the arrow and the anchor center.
    if (arrowCenter > anchorCenter) {
      left = Math.max(0, left - (arrowCenter - anchorCenter));
    }
    // Arrow's left offset relative to the anchor.
    arrowLeft = Math.min(ARROW_OFFSET, (anchorWidth - ARROW_WIDTH) / 2) | 0;
    // Translate the coordinate to tooltip container
    arrowLeft += anchorStart - left;
    // Make sure the arrow remains in the tooltip container.
    arrowLeft = Math.min(arrowLeft, width - ARROW_WIDTH);
    arrowLeft = Math.max(arrowLeft, 0);
  }

  // Translate back to absolute coordinates by re-including viewport left margin.
  left += viewportRect.left;

  return {left, width, 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) {
    let {top, left, width, height} = node.getBoundingClientRect();
    let right = left + width;
    let bottom = top + height;
    return {top, right, bottom, left, width, height};
  }

  // Width and Height can be taken from the rect.
  let {width, height} = node.getBoundingClientRect();

  let quads = node.getBoxQuads({relativeTo});
  let top = quads[0].bounds.top;
  let left = quads[0].bounds.left;

  // Compute right and bottom coordinates using the rest of the data.
  let right = left + width;
  let 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} type
 *          Display type of the tooltip. Possible values: "normal", "arrow"
 *        - {Boolean} autofocus
 *          Defaults to false. Should the tooltip be focused when opening it.
 *        - {Boolean} consumeOutsideClicks
 *          Defaults to true. The tooltip is closed when clicking outside.
 *          Should this event be stopped and consumed or not.
 *        - {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.
 */
function HTMLTooltip(toolboxDoc, {
    type = "normal",
    autofocus = false,
    consumeOutsideClicks = true,
    useXulWrapper = false,
  } = {}) {
  EventEmitter.decorate(this);

  this.doc = toolboxDoc;
  this.type = type;
  this.autofocus = autofocus;
  this.consumeOutsideClicks = consumeOutsideClicks;
  this.useXulWrapper = this._isXUL() && useXulWrapper;

  // 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._onXulPanelHidden = this._onXulPanelHidden.bind(this);

  this._toggle = new TooltipToggle(this);
  this.startTogglingOnHover = this._toggle.start.bind(this._toggle);
  this.stopTogglingOnHover = this._toggle.stop.bind(this._toggle);

  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> <!-- div wrapper used to isolate the tooltip container -->
    //     <div> <! the actual tooltip.container element -->
    this.xulPanelWrapper = this._createXulPanelWrapper();
    let inner = this.doc.createElementNS(XHTML_NS, "div");
    inner.classList.add("tooltip-xul-wrapper-inner");

    this.doc.documentElement.appendChild(this.xulPanelWrapper);
    this.xulPanelWrapper.appendChild(inner);
    inner.appendChild(this.container);
  } else if (this._isXUL()) {
    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 provided in
   * setContent().
   */
  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;
  },

  /**
   * Set the tooltip content element. The preferred width/height should also be
   * specified here.
   *
   * @param {Element} content
   *        The tooltip content, should be a HTML element.
   * @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 preferred width.
   *        - {Number} height: optional, preferred height for the tooltip container. If
   *          not specified, the tooltip will be able to use all the height available.
   */
  setContent: function (content, {width = "auto", height = Infinity} = {}) {
    this.preferredWidth = width;
    this.preferredHeight = height;

    this.panel.innerHTML = "";
    this.panel.appendChild(content);
  },

  /**
   * Show the tooltip next to the provided anchor element. 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}
   *        - {String} position: optional, possible values: top|bottom
   *          If layout permits, the tooltip will be displayed on top/bottom
   *          of the anchor. If ommitted, the tooltip will be displayed where
   *          more space is available.
   *        - {Number} x: optional, horizontal offset between the anchor and the tooltip
   *        - {Number} y: optional, vertical offset between the anchor and the tooltip
   */
  show: Task.async(function* (anchor, {position, x = 0, y = 0} = {}) {
    // Get anchor geometry
    let anchorRect = getRelativeRect(anchor, this.doc);
    if (this.useXulWrapper) {
      anchorRect = this._convertToScreenRect(anchorRect);
    }

    // Get viewport size
    let viewportRect = this._getViewportRect();

    let themeHeight = EXTRA_HEIGHT[this.type] + 2 * EXTRA_BORDER[this.type];
    let preferredHeight = this.preferredHeight + themeHeight;

    let {top, height, computedPosition} =
      calculateVerticalPosition(anchorRect, viewportRect, preferredHeight, position, y);

    this._position = computedPosition;
    // Apply height before measuring the content width (if width="auto").
    let 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";

    let preferredWidth;
    if (this.preferredWidth === "auto") {
      preferredWidth = this._measureContainerWidth();
    } else {
      let themeWidth = 2 * EXTRA_BORDER[this.type];
      preferredWidth = this.preferredWidth + themeWidth;
    }

    let anchorWin = anchor.ownerDocument.defaultView;
    let isRtl = anchorWin.getComputedStyle(anchor).direction === "rtl";
    let {left, width, arrowLeft} = calculateHorizontalPosition(
      anchorRect, viewportRect, preferredWidth, this.type, x, isRtl);

    this.container.style.width = width + "px";

    if (this.type === TYPE.ARROW) {
      this.arrow.style.left = arrowLeft + "px";
    }

    if (this.useXulWrapper) {
      yield this._showXulWrapperAt(left, top);
    } else {
      this.container.style.left = left + "px";
      this.container.style.top = top + "px";
    }

    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;

    this.doc.defaultView.clearTimeout(this.attachEventsTimer);
    this.attachEventsTimer = this.doc.defaultView.setTimeout(() => {
      this._maybeFocusTooltip();
      // Updated the top window reference each time in case the host changes.
      this.topWindow = this._getTopWindow();
      this.topWindow.addEventListener("click", this._onClick, true);
      this.emit("shown");
    }, 0);
  }),

  /**
   * Calculate the rect of the viewport that limits the tooltip dimensions. When using a
   * XUL panel wrapper, the viewport will be able to use the whole screen (excluding space
   * reserved by the OS for toolbars etc.). Otherwise, the viewport is limited to the
   * tooltip's document.
   *
   * @return {Object} DOMRect-like object with the Number properties: top, right, bottom,
   *         left, width, height
   */
  _getViewportRect: function () {
    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
      let {availLeft, availTop, availHeight, availWidth} = this.doc.defaultView.screen;
      return {
        top: availTop,
        right: availLeft + availWidth,
        bottom: availTop + availHeight,
        left: availLeft,
        width: availWidth,
        height: availHeight,
      };
    }

    return this.doc.documentElement.getBoundingClientRect();
  },

  _measureContainerWidth: function () {
    let 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");
    this.container.style.width = "auto";
    let width = this.container.getBoundingClientRect().width;
    this.container.classList.remove("tooltip-hidden");

    if (this.useXulWrapper && !this.isVisible()) {
      xulParent.appendChild(this.container);
    }

    return width;
  },

  /**
   * Hide the current tooltip. The event "hidden" will be fired when the tooltip
   * is hidden.
   */
  hide: Task.async(function* () {
    this.doc.defaultView.clearTimeout(this.attachEventsTimer);
    if (!this.isVisible()) {
      this.emit("hidden");
      return;
    }

    this.topWindow.removeEventListener("click", this._onClick, true);
    this.container.classList.remove("tooltip-visible");
    if (this.useXulWrapper) {
      yield this._hideXulWrapper();
    }

    this.emit("hidden");

    let tooltipHasFocus = this.container.contains(this.doc.activeElement);
    if (tooltipHasFocus && this._focusedElement) {
      this._focusedElement.focus();
      this._focusedElement = null;
    }
  }),

  /**
   * Check if the tooltip is currently displayed.
   * @return {Boolean} true if the tooltip is visible
   */
  isVisible: function () {
    return this.container.classList.contains("tooltip-visible");
  },

  /**
   * Destroy the tooltip instance. Hide the tooltip if displayed, remove the
   * tooltip container from the document.
   */
  destroy: function () {
    this.hide();
    this.container.remove();
    if (this.xulPanelWrapper) {
      this.xulPanelWrapper.remove();
    }
    this._toggle.destroy();
  },

  _createContainer: function () {
    let container = this.doc.createElementNS(XHTML_NS, "div");
    container.setAttribute("type", this.type);
    container.classList.add("tooltip-container");

    let html = '<div class="tooltip-filler"></div>';
    html += '<div class="tooltip-panel"></div>';

    if (this.type === TYPE.ARROW) {
      html += '<div class="tooltip-arrow"></div>';
    }
    // eslint-disable-next-line no-unsanitized/property
    container.innerHTML = html;
    return container;
  },

  _onClick: function (e) {
    if (this._isInTooltipContainer(e.target)) {
      return;
    }

    this.hide();
    if (this.consumeOutsideClicks && e.button === 0) {
      // Consume only left click events (button === 0).
      e.preventDefault();
      e.stopPropagation();
    }
  },

  _isInTooltipContainer: function (node) {
    // Check if the target is the tooltip arrow.
    if (this.arrow && this.arrow === node) {
      return true;
    }

    let 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: function () {
    if (this.isVisible()) {
      this.hide();
    }
  },

  /**
   * If the tootlip is configured to autofocus and a focusable element can be found,
   * focus it.
   */
  _maybeFocusTooltip: function () {
    // Simplied selector targetting elements that can receive the focus, full version at
    // http://stackoverflow.com/questions/1599660/which-html-elements-can-receive-focus .
    let focusableSelector = "a, button, iframe, input, select, textarea";
    let focusableElement = this.panel.querySelector(focusableSelector);
    if (this.autofocus && focusableElement) {
      focusableElement.focus();
    }
  },

  _getTopWindow: function () {
    return this.doc.defaultView.top;
  },

  /**
   * Check if the tooltip's owner document is a XUL document.
   */
  _isXUL: function () {
    return this.doc.documentElement.namespaceURI === XUL_NS;
  },

  _createXulPanelWrapper: function () {
    let panel = this.doc.createElementNS(XUL_NS, "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("noautofocus", true);
    panel.setAttribute("ignorekeys", true);
    panel.setAttribute("tooltip", "aHTMLTooltip");

    // Use type="arrow" to prevent side effects (see Bug 1285206)
    panel.setAttribute("type", "arrow");

    panel.setAttribute("level", "top");
    panel.setAttribute("class", "tooltip-xul-wrapper");

    return panel;
  },

  _showXulWrapperAt: function (left, top) {
    this.xulPanelWrapper.addEventListener("popuphidden", this._onXulPanelHidden);
    let onPanelShown = listenOnce(this.xulPanelWrapper, "popupshown");
    this.xulPanelWrapper.openPopupAtScreen(left, top, false);
    return onPanelShown;
  },

  _hideXulWrapper: function () {
    this.xulPanelWrapper.removeEventListener("popuphidden", this._onXulPanelHidden);

    if (this.xulPanelWrapper.state === "closed") {
      // XUL panel is already closed, resolve immediately.
      return Promise.resolve();
    }

    let 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: function ({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};
  },
};
PK
!<�P�wOOTchrome/devtools/modules/devtools/client/shared/widgets/tooltip/ImageTooltipHelper.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 {LocalizationHelper} = require("devtools/shared/l10n");
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;
const LABEL_HEIGHT = 20;
const IMAGE_PADDING = 4;

/**
 * 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 => {
    let imgObj = new doc.defaultView.Image();
    imgObj.onload = () => {
      imgObj.onload = null;
      let { 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) {
    let scale = maxDim / Math.max(imgHeight, imgWidth);
    // Only allow integer values to avoid rounding errors.
    imgHeight = Math.floor(scale * naturalHeight);
    imgWidth = Math.ceil(scale * naturalWidth);
  }

  let imageClass = "";
  if (!hideCheckeredBackground) {
    imageClass = "devtools-tooltip-tiles";
  }

  // Create tooltip content
  let div = doc.createElementNS(XHTML_NS, "div");
  div.style.cssText = `
    height: 100%;
    min-width: 100px;
    display: flex;
    flex-direction: column;
    text-align: center;`;
  let html = `
    <div style="flex: 1;
                display: flex;
                padding: ${IMAGE_PADDING}px;
                align-items: center;
                justify-content: center;
                min-height: 1px;">
      <img class="${imageClass}"
           style="height: ${imgHeight}px; max-height: 100%;"
           src="${encodeURI(imageUrl)}"/>
    </div>`;

  if (!hideDimensionLabel) {
    let label = naturalWidth + " \u00D7 " + naturalHeight;
    html += `
      <div style="height: ${LABEL_HEIGHT}px;
                  text-align: center;">
        <span class="theme-comment devtools-tooltip-caption">${label}</span>
      </div>`;
  }
  // eslint-disable-next-line no-unsanitized/property
  div.innerHTML = html;

  // Calculate tooltip dimensions
  let height = imgHeight + 2 * IMAGE_PADDING;
  if (!hideDimensionLabel) {
    height += LABEL_HEIGHT;
  }
  let width = Math.max(CONTAINER_MIN_WIDTH, imgWidth + 2 * IMAGE_PADDING);

  tooltip.setContent(div, {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) {
  let div = doc.createElementNS(XHTML_NS, "div");
  div.className = "theme-comment devtools-tooltip-image-broken";
  let message = L10N.getStr("previewTooltip.image.brokenImage");
  div.textContent = message;
  tooltip.setContent(div);
}

module.exports.getImageDimensions = getImageDimensions;
module.exports.setImageTooltip = setImageTooltip;
module.exports.setBrokenImageTooltip = setBrokenImageTooltip;
PK
!<쥔�..Ochrome/devtools/modules/devtools/client/shared/widgets/tooltip/InlineTooltip.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/shared/event-emitter");

/**
 * The InlineTooltip can display widgets for the CSS Rules view in an
 * inline container.
 *
 * @param {Document} doc
 *        The toolbox document to attach the InlineTooltip container.
 */
function InlineTooltip(doc) {
  EventEmitter.decorate(this);

  this.doc = doc;

  this.panel = this.doc.createElement("div");

  this.topWindow = this._getTopWindow();
}

InlineTooltip.prototype = {
  /**
   * Show the tooltip. It might be wise to append some content first if you
   * don't want the tooltip to be empty.
   *
   * @param {Node} anchor
   *        Which node below which the tooltip should be shown.
   */
  show(anchor) {
    anchor.parentNode.insertBefore(this.panel, anchor.nextSibling);

    this.emit("shown");
  },

  /**
   * Hide the current tooltip.
   */
  hide() {
    if (!this.isVisible()) {
      return;
    }

    this.panel.parentNode.remove(this.panel);

    this.emit("hidden");
  },

  /**
   * Check if the tooltip is currently displayed.
   *
   * @return {Boolean} true if the tooltip is visible
   */
  isVisible() {
    return typeof this.panel.parentNode !== "undefined" && this.panel.parentNode !== null;
  },

  /**
   * Clears the HTML content of the tooltip panel
   */
  clear() {
    this.panel.innerHTML = "";
  },

  /**
   * Set the content of this tooltip. Will first clear the tooltip and then
   * append the new content element.
   *
   * @param {DOMNode} content
   *        A node that can be appended in the tooltip
   */
  setContent(content) {
    this.clear();

    this.panel.appendChild(content);
  },

  get content() {
    return this.panel.firstChild;
  },

  _getTopWindow: function () {
    return this.doc.defaultView;
  },

  destroy() {
    this.hide();
    this.doc = null;
    this.panel = null;
  },
};

module.exports = InlineTooltip;
PK
!<����ccZchrome/devtools/modules/devtools/client/shared/widgets/tooltip/SwatchBasedEditorTooltip.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/shared/event-emitter");
const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
const InlineTooltip = require("devtools/client/shared/widgets/tooltip/InlineTooltip");

const INLINE_TOOLTIP_CLASS = "inline-tooltip-container";

/**
 * Base class for all (color, gradient, ...)-swatch based value editors inside
 * tooltips
 *
 * @param {Document} document
 *        The document to attach the SwatchBasedEditorTooltip. 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 {Boolean} useInline
 *        A boolean flag representing whether or not the InlineTooltip should be used.
 */

class SwatchBasedEditorTooltip {
  constructor(document, useInline) {
    EventEmitter.decorate(this);

    this.useInline = useInline;

    // Creating a tooltip instance
    if (useInline) {
      this.tooltip = new InlineTooltip(document);
    } else {
      // 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.topWindow
    });
    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);
  }

 /**
   * 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() {
    let tooltipAnchor = this.useInline ?
      this.activeSwatch.closest(`.${INLINE_TOOLTIP_CLASS}`) :
      this.activeSwatch;

    if (tooltipAnchor) {
      let onShown = this.tooltip.once("shown");

      this.tooltip.show(tooltipAnchor, "topcenter bottomleft");
      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() {
    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: callbacks
    });
    swatchEl.addEventListener("click", this._onSwatchClick);
  }

  removeSwatch(swatchEl) {
    if (this.swatches.has(swatchEl)) {
      if (this.activeSwatch === swatchEl) {
        this.hide();
        this.activeSwatch = null;
      }
      swatchEl.removeEventListener("click", this._onSwatchClick);
      this.swatches.delete(swatchEl);
    }
  }

  _onSwatchClick(event) {
    let swatch = this.swatches.get(event.target);

    if (event.shiftKey) {
      event.stopPropagation();
      return;
    }
    if (swatch) {
      this.activeSwatch = event.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) {
      let 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;
      let 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) {
      let swatch = this.swatches.get(this.activeSwatch);
      swatch.callbacks.onCommit();
    }
  }

  destroy() {
    this.swatches.clear();
    this.activeSwatch = null;
    this.tooltip.off("keydown", this._onTooltipKeydown);
    this.tooltip.destroy();
    this.shortcuts.destroy();
  }
}

module.exports = SwatchBasedEditorTooltip;
PK
!<�͓99Zchrome/devtools/modules/devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Services = require("Services");
const {colorUtils} = require("devtools/shared/css/color");
const {ColorWidget} = require("devtools/client/shared/widgets/ColorWidget");
const {Spectrum} = require("devtools/client/shared/widgets/Spectrum");
const SwatchBasedEditorTooltip = require("devtools/client/shared/widgets/tooltip/SwatchBasedEditorTooltip");
const {LocalizationHelper} = require("devtools/shared/l10n");
const L10N = new LocalizationHelper("devtools/client/locales/inspector.properties");

const colorWidgetPref = "devtools.inspector.colorWidget.enabled";
const NEW_COLOR_WIDGET = Services.prefs.getBoolPref(colorWidgetPref);
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.
 *
 * @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.
 * @param {Function} supportsCssColor4ColorFunction
 *        A function for checking the supporting of css-color-4 color function.
 */

class SwatchColorPickerTooltip extends SwatchBasedEditorTooltip {
  constructor(document, inspector, {supportsCssColor4ColorFunction}) {
    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.cssColor4 = supportsCssColor4ColorFunction();
  }

  /**
   * 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) {
    let { doc } = this.tooltip;

    let container = doc.createElementNS(XHTML_NS, "div");
    container.id = "spectrum-tooltip";

    let widget;
    let node = doc.createElementNS(XHTML_NS, "div");

    if (NEW_COLOR_WIDGET) {
      node.id = "colorwidget";
      container.appendChild(node);
      widget = new ColorWidget(node, color);
      this.tooltip.setContent(container, { width: 218, height: 320 });
    } else {
      node.id = "spectrum";
      container.appendChild(node);
      widget = new Spectrum(node, color);
      this.tooltip.setContent(container, { width: 218, height: 224 });
    }
    widget.inspector = this.inspector;

    let eyedropper = doc.createElementNS(XHTML_NS, "button");
    eyedropper.id = "eyedropper-button";
    eyedropper.className = "devtools-button";
    /* pointerEvents for eyedropper has to be set auto to display tooltip when
     * eyedropper is disabled in non-HTML documents.
     */
    eyedropper.style.pointerEvents = "auto";
    container.appendChild(eyedropper);

    // 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
    let name = this.activeSwatch.dataset.propertyName;

    if (this.isContrastCompatible === undefined) {
      let target = this.inspector.target;
      this.isContrastCompatible = await target.actorHasMethod(
        "domnode",
        "getClosestBackgroundColor"
      );
    }

    // only enable contrast if it is compatible and if the type of property is color.
    this.spectrum.contrastEnabled = (name === "color") && this.isContrastCompatible;

    // Call then parent class' show function
    await super.show();

    // Then set spectrum's color and listen to color changes to preview them
    if (this.activeSwatch) {
      this.currentSwatchColor = this.activeSwatch.nextSibling;
      this._originalColor = this.currentSwatchColor.textContent;
      let 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();
    }

    let eyeButton = this.tooltip.container.querySelector("#eyedropper-button");
    let 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");
    }
    this.emit("ready");
  }

  _onSpectrumColorChange(rgba, cssColor) {
    this._selectColor(cssColor);
  }

  _selectColor(color) {
    if (this.activeSwatch) {
      this.activeSwatch.style.backgroundColor = color;
      this.activeSwatch.parentNode.dataset.color = color;

      color = this._toDefaultType(color);
      this.currentSwatchColor.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();
  }

  _openEyeDropper() {
    let {inspector, toolbox, telemetry} = this.inspector;
    telemetry.toolOpened("pickereyedropper");

    // cancelling picker(if it is already selected) on opening eye-dropper
    toolbox.highlighterUtils.cancelPicker();

    // 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;

    inspector.pickColorFromPage(toolbox, {copyOnSelect: false}).then(() => {
      // close the colorpicker tooltip so that only the eyedropper is open.
      this.hide();

      this.tooltip.emit("eyedropper-opened");
    }, console.error);

    inspector.once("color-picked", color => {
      toolbox.win.focus();
      this._selectColor(color);
      this._onEyeDropperDone();
    });

    inspector.once("color-pick-canceled", () => {
      this._onEyeDropperDone();
    });
  }

  _onEyeDropperDone() {
    this.eyedropperOpen = false;
    this.activeSwatch = null;
  }

  _colorToRgba(color) {
    color = new colorUtils.CssColor(color, this.cssColor4);
    let rgba = color.getRGBATuple();
    return [rgba.r, rgba.g, rgba.b, rgba.a];
  }

  _toDefaultType(color) {
    let colorObj = new colorUtils.CssColor(color);
    colorObj.setAuthoredUnitFromColor(this._originalColor, this.cssColor4);
    return colorObj.toString();
  }

  /**
   * Overriding the SwatchBasedEditorTooltip.isEditing function to consider the
   * eyedropper.
   */
  isEditing() {
    return this.tooltip.isVisible() || this.eyedropperOpen;
  }

  destroy() {
    super.destroy();
    this.inspector = null;
    this.currentSwatchColor = null;
    this.spectrum.off("changed", this._onSpectrumColorChange);
    this.spectrum.destroy();
  }
}

module.exports = SwatchColorPickerTooltip;
PK
!<�8�ggZchrome/devtools/modules/devtools/client/shared/widgets/tooltip/SwatchCubicBezierTooltip.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 defer = require("devtools/shared/defer");
const {CubicBezierWidget} = require("devtools/client/shared/widgets/CubicBezierWidget");
const SwatchBasedEditorTooltip = require("devtools/client/shared/widgets/tooltip/SwatchBasedEditorTooltip");

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
   */

  setCubicBezierContent(bezier) {
    let { doc } = this.tooltip;

    let container = doc.createElementNS(XHTML_NS, "div");
    container.className = "cubic-bezier-container";

    this.tooltip.setContent(container, { width: 510, height: 370 });

    let def = defer();

    // Wait for the tooltip to be shown before calling instanciating the widget
    // as it expect its DOM elements to be visible.
    this.tooltip.once("shown", () => {
      let widget = new CubicBezierWidget(container, bezier);
      def.resolve(widget);
    });

    return def.promise;
  }

  /**
   * 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;
PK
!<쮺S��Uchrome/devtools/modules/devtools/client/shared/widgets/tooltip/SwatchFilterTooltip.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/widgets/FilterWidget");
const SwatchBasedEditorTooltip = require("devtools/client/shared/widgets/tooltip/SwatchBasedEditorTooltip");

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.
 * @param {function} cssIsValid
 *        A function to check that css declaration's name and values are valid together.
 *        This can be obtained from "shared/fronts/css-properties.js".
 */

class SwatchFilterTooltip extends SwatchBasedEditorTooltip {
  constructor(document, cssIsValid) {
    super(document);
    this._cssIsValid = cssIsValid;

    // 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) {
    let { doc } = this.tooltip;

    let container = doc.createElementNS(XHTML_NS, "div");
    container.id = "filter-container";

    this.tooltip.setContent(container, { width: 510, height: 200 });

    return new CSSFilterEditorWidget(container, filter, this._cssIsValid);
  }

  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();
    }
    let 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;
PK
!<��厳0�0Ichrome/devtools/modules/devtools/client/shared/widgets/tooltip/Tooltip.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 defer = require("devtools/shared/defer");
const EventEmitter = require("devtools/shared/event-emitter");
const {KeyCodes} = require("devtools/client/shared/keycodes");
const {TooltipToggle} = require("devtools/client/shared/widgets/tooltip/TooltipToggle");

const XHTML_NS = "http://www.w3.org/1999/xhtml";
const ESCAPE_KEYCODE = KeyCodes.DOM_VK_ESCAPE;
const POPUP_EVENTS = ["shown", "hidden", "showing", "hiding"];

/**
 * Tooltip widget.
 *
 * This widget is intended at any tool that may need to show rich content in the
 * form of floating panels.
 * A common use case is image previewing in the CSS rule view, but more complex
 * use cases may include color pickers, object inspection, etc...
 *
 * Tooltips are based on XUL (namely XUL arrow-type <panel>s), and therefore
 * need a XUL Document to live in.
 * This is pretty much the only requirement they have on their environment.
 *
 * The way to use a tooltip is simply by instantiating a tooltip yourself and
 * attaching some content in it, or using one of the ready-made content types.
 *
 * A convenient `startTogglingOnHover` method may avoid having to register event
 * handlers yourself if the tooltip has to be shown when hovering over a
 * specific element or group of elements (which is usually the most common case)
 */

/**
 * Tooltip class.
 *
 * Basic usage:
 *   let t = new Tooltip(xulDoc);
 *   t.content = someXulContent;
 *   t.show();
 *   t.hide();
 *   t.destroy();
 *
 * Better usage:
 *   let t = new Tooltip(xulDoc);
 *   t.startTogglingOnHover(container, target => {
 *     if (<condition based on target>) {
 *       t.content = el;
 *       return true;
 *     }
 *   });
 *   t.destroy();
 *
 * @param {XULDocument} doc
 *        The XUL document hosting this tooltip
 * @param {Object} options
 *        Optional options that give options to consumers:
 *        - consumeOutsideClick {Boolean} Wether the first click outside of the
 *        tooltip should close the tooltip and be consumed or not.
 *        Defaults to false.
 *        - closeOnKeys {Array} An array of key codes that should close the
 *        tooltip. Defaults to [27] (escape key).
 *        - closeOnEvents [{emitter: {Object}, event: {String},
 *                          useCapture: {Boolean}}]
 *        Provide an optional list of emitter objects and event names here to
 *        trigger the closing of the tooltip when these events are fired by the
 *        emitters. The emitter objects should either implement
 *        on/off(event, cb) or addEventListener/removeEventListener(event, cb).
 *        Defaults to [].
 *        For instance, the following would close the tooltip whenever the
 *        toolbox selects a new tool and when a DOM node gets scrolled:
 *        new Tooltip(doc, {
 *          closeOnEvents: [
 *            {emitter: toolbox, event: "select"},
 *            {emitter: myContainer, event: "scroll", useCapture: true}
 *          ]
 *        });
 *        - noAutoFocus {Boolean} Should the focus automatically go to the panel
 *        when it opens. Defaults to true.
 *
 * Fires these events:
 * - showing : just before the tooltip shows
 * - shown : when the tooltip is shown
 * - hiding : just before the tooltip closes
 * - hidden : when the tooltip gets hidden
 * - keydown : when any key gets pressed, with keyCode
 */

class Tooltip {
  constructor(doc, {
  consumeOutsideClick = false,
  closeOnKeys = [ESCAPE_KEYCODE],
  noAutoFocus = true,
  closeOnEvents = [],
  } = {}) {
    EventEmitter.decorate(this);

    this.defaultPosition = "before_start";
    // px
    this.defaultOffsetX = 0;
    // px
    this.defaultOffsetY = 0;
    // px

    this.doc = doc;
    this.consumeOutsideClick = consumeOutsideClick;
    this.closeOnKeys = closeOnKeys;
    this.noAutoFocus = noAutoFocus;
    this.closeOnEvents = closeOnEvents;

    this.panel = this._createPanel();

    // Create tooltip toggle helper and decorate the Tooltip instance with
    // shortcut methods.
    this._toggle = new TooltipToggle(this);
    this.startTogglingOnHover = this._toggle.start.bind(this._toggle);
    this.stopTogglingOnHover = this._toggle.stop.bind(this._toggle);

  // Emit show/hide events when the panel does.
    for (let eventName of POPUP_EVENTS) {
      this["_onPopup" + eventName] = (name => {
        return e => {
          if (e.target === this.panel) {
            this.emit(name);
          }
        };
      })(eventName);
      this.panel.addEventListener("popup" + eventName,
        this["_onPopup" + eventName]);
    }

  // Listen to keydown events to close the tooltip if configured to do so
    let win = this.doc.querySelector("window");
    this._onKeyDown = event => {
      if (this.panel.hidden) {
        return;
      }

      this.emit("keydown", event.keyCode);
      if (this.closeOnKeys.includes(event.keyCode) &&
          this.isShown()) {
        event.stopPropagation();
        this.hide();
      }
    };
    win.addEventListener("keydown", this._onKeyDown);

  // Listen to custom emitters' events to close the tooltip
    this.hide = this.hide.bind(this);
    for (let {emitter, event, useCapture} of this.closeOnEvents) {
      for (let add of ["addEventListener", "on"]) {
        if (add in emitter) {
          emitter[add](event, this.hide, useCapture);
          break;
        }
      }
    }
  }

  /**
   * Show the tooltip. It might be wise to append some content first if you
   * don't want the tooltip to be empty. You may access the content of the
   * tooltip by setting a XUL node to t.content.
   * @param {node} anchor
   *        Which node should the tooltip be shown on
   * @param {string} position [optional]
   *        Optional tooltip position. Defaults to before_start
   *        https://developer.mozilla.org/en-US/docs/XUL/PopupGuide/Positioning
   * @param {number} x, y [optional]
   *        The left and top offset coordinates, in pixels.
   */
  show(anchor,
    position = this.defaultPosition,
    x = this.defaultOffsetX,
    y = this.defaultOffsetY) {
    this.panel.hidden = false;
    this.panel.openPopup(anchor, position, x, y);
  }

  /**
   * Hide the tooltip
   */
  hide() {
    this.panel.hidden = true;
    this.panel.hidePopup();
  }

  isShown() {
    return this.panel &&
           this.panel.state !== "closed" &&
           this.panel.state !== "hiding";
  }

  setSize(width, height) {
    this.panel.sizeTo(width, height);
  }

  /**
   * Empty the tooltip's content
   */
  empty() {
    while (this.panel.hasChildNodes()) {
      this.panel.firstChild.remove();
    }
  }

  /**
   * Gets this panel's visibility state.
   * @return boolean
   */
  isHidden() {
    return this.panel.state == "closed" || this.panel.state == "hiding";
  }

  /**
   * Gets if this panel has any child nodes.
   * @return boolean
   */
  isEmpty() {
    return !this.panel.hasChildNodes();
  }

  /**
   * Get rid of references and event listeners
   */
  destroy() {
    this.hide();

    for (let eventName of POPUP_EVENTS) {
      this.panel.removeEventListener("popup" + eventName,
        this["_onPopup" + eventName]);
    }

    let win = this.doc.querySelector("window");
    win.removeEventListener("keydown", this._onKeyDown);

    for (let {emitter, event, useCapture} of this.closeOnEvents) {
      for (let remove of ["removeEventListener", "off"]) {
        if (remove in emitter) {
          emitter[remove](event, this.hide, useCapture);
          break;
        }
      }
    }

    this.content = null;

    this._toggle.destroy();

    this.doc = null;

    this.panel.remove();
    this.panel = null;
  }

  /**
   * Returns the outer container node (that includes the arrow etc.). Happens
   * to be identical to this.panel here, can be different element in other
   * Tooltip implementations.
   */
  get container() {
    return this.panel;
  }

  /**
   * Set the content of this tooltip. Will first empty the tooltip and then
   * append the new content element.
   * Consider using one of the set<type>Content() functions instead.
   * @param {node} content
   *        A node that can be appended in the tooltip XUL element
   */
  set content(content) {
    if (this.content == content) {
      return;
    }

    this.empty();
    this.panel.removeAttribute("clamped-dimensions");
    this.panel.removeAttribute("clamped-dimensions-no-min-height");
    this.panel.removeAttribute("clamped-dimensions-no-max-or-min-height");
    this.panel.removeAttribute("wide");

    if (content) {
      this.panel.appendChild(content);
    }
  }

  get content() {
    return this.panel.firstChild;
  }

  /**
   * Sets some text as the content of this tooltip.
   *
   * @param {array} messages
   *        A list of text messages.
   * @param {string} messagesClass [optional]
   *        A style class for the text messages.
   * @param {string} containerClass [optional]
   *        A style class for the text messages container.
   */
  setTextContent(
    {
      messages,
      messagesClass,
      containerClass
    },
    extraButtons = []) {
    messagesClass = messagesClass || "default-tooltip-simple-text-colors";
    containerClass = containerClass || "default-tooltip-simple-text-colors";

    let vbox = this.doc.createElement("vbox");
    vbox.className = "devtools-tooltip-simple-text-container " + containerClass;
    vbox.setAttribute("flex", "1");

    for (let text of messages) {
      let description = this.doc.createElement("description");
      description.setAttribute("flex", "1");
      description.className = "devtools-tooltip-simple-text " + messagesClass;
      description.textContent = text;
      vbox.appendChild(description);
    }

    for (let { label, className, command } of extraButtons) {
      let button = this.doc.createElement("button");
      button.className = className;
      button.setAttribute("label", label);
      button.addEventListener("command", command);
      vbox.appendChild(button);
    }

    this.content = vbox;
  }

  /**
   * Load a document into an iframe, and set the iframe
   * to be the tooltip's content.
   *
   * Used by tooltips that want to load their interface
   * into an iframe from a URL.
   *
   * @param {string} width
   *        Width of the iframe.
   * @param {string} height
   *        Height of the iframe.
   * @param {string} url
   *        URL of the document to load into the iframe.
   *
   * @return {promise} A promise which is resolved with
   * the iframe.
   *
   * This function creates an iframe, loads the specified document
   * into it, sets the tooltip's content to the iframe, and returns
   * a promise.
   *
   * When the document is loaded, the function gets the content window
   * and resolves the promise with the content window.
   */
  setIFrameContent({width, height}, url) {
    let def = defer();

    // Create an iframe
    let iframe = this.doc.createElementNS(XHTML_NS, "iframe");
    iframe.setAttribute("transparent", true);
    iframe.setAttribute("width", width);
    iframe.setAttribute("height", height);
    iframe.setAttribute("flex", "1");
    iframe.setAttribute("tooltip", "aHTMLTooltip");
    iframe.setAttribute("class", "devtools-tooltip-iframe");

    // Wait for the load to initialize the widget
    function onLoad() {
      iframe.removeEventListener("load", onLoad, true);
      def.resolve(iframe);
    }
    iframe.addEventListener("load", onLoad, true);

    // load the document from url into the iframe
    iframe.setAttribute("src", url);

    // Put the iframe in the tooltip
    this.content = iframe;

    return def.promise;
  }

  /**
   * Create the tooltip panel
   */
  _createPanel() {
    let panel = this.doc.createElement("panel");
    panel.setAttribute("hidden", true);
    panel.setAttribute("ignorekeys", true);
    panel.setAttribute("animate", false);

    panel.setAttribute("consumeoutsideclicks",
                       this.consumeOutsideClick);
    panel.setAttribute("noautofocus", this.noAutoFocus);
    panel.setAttribute("type", "arrow");
    panel.setAttribute("level", "top");

    panel.setAttribute("class", "devtools-tooltip theme-tooltip-panel");
    this.doc.querySelector("window").appendChild(panel);

    return panel;
  }
}

module.exports = Tooltip;
PK
!<��׳��Ochrome/devtools/modules/devtools/client/shared/widgets/tooltip/TooltipToggle.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 {Task} = require("devtools/shared/task");

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: function (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);

    if (this._interactive) {
      this.tooltip.container.addEventListener("mouseover", this._onTooltipMouseOver);
      this.tooltip.container.addEventListener("mouseout", this._onTooltipMouseOut);
    }
  },

  /**
   * 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: function () {
    this.win.clearTimeout(this.toggleTimer);

    if (!this._baseNode) {
      return;
    }

    this._baseNode.removeEventListener("mousemove", this._onMouseMove);
    this._baseNode.removeEventListener("mouseout", this._onMouseOut);

    if (this._interactive) {
      this.tooltip.container.removeEventListener("mouseover", this._onTooltipMouseOver);
      this.tooltip.container.removeEventListener("mouseout", this._onTooltipMouseOut);
    }

    this._baseNode = null;
    this._targetNodeCb = null;
    this._lastHovered = null;
  },

  _onMouseMove: function (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.
   */
  isValidHoverTarget: Task.async(function* (target) {
    let res = yield this._targetNodeCb(target, this.tooltip);
    if (res) {
      return res.nodeName ? res : target;
    }

    return null;
  }),

  _onMouseOut: function (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: function () {
    this.stop();
  }
};
PK
!<�l柙
�
Wchrome/devtools/modules/devtools/client/shared/widgets/tooltip/VariableContentHelper.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 ChromeUtils = require("ChromeUtils");

ChromeUtils.defineModuleGetter(this, "VariablesView",
  "resource://devtools/client/shared/widgets/VariablesView.jsm");
ChromeUtils.defineModuleGetter(this, "VariablesViewController",
  "resource://devtools/client/shared/widgets/VariablesViewController.jsm");

/**
 * Fill the tooltip with a variables view, inspecting an object via its
 * corresponding object actor, as specified in the remote debugging protocol.
 *
 * @param {Tooltip} tooltip
 *        The tooltip to use
 * @param {object} objectActor
 *        The value grip for the object actor.
 * @param {object} viewOptions [optional]
 *        Options for the variables view visualization.
 * @param {object} controllerOptions [optional]
 *        Options for the variables view controller.
 * @param {object} relayEvents [optional]
 *        A collection of events to listen on the variables view widget.
 *        For example, { fetched: () => ... }
 * @param {array} extraButtons [optional]
 *        An array of extra buttons to add.  Each element of the array
 *        should be of the form {label, className, command}.
 * @param {Toolbox} toolbox [optional]
 *        Pass the instance of the current toolbox if you want the variables
 *        view widget to allow highlighting and selection of DOM nodes
 */

function setTooltipVariableContent(tooltip, objectActor,
                                   viewOptions = {}, controllerOptions = {},
                                   relayEvents = {}, extraButtons = [],
                                   toolbox = null) {
  let doc = tooltip.doc;
  let vbox = doc.createElement("vbox");
  vbox.className = "devtools-tooltip-variables-view-box";
  vbox.setAttribute("flex", "1");

  let innerbox = doc.createElement("vbox");
  innerbox.className = "devtools-tooltip-variables-view-innerbox";
  innerbox.setAttribute("flex", "1");
  vbox.appendChild(innerbox);

  for (let { label, className, command } of extraButtons) {
    let button = doc.createElement("button");
    button.className = className;
    button.setAttribute("label", label);
    button.addEventListener("command", command);
    vbox.appendChild(button);
  }

  let widget = new VariablesView(innerbox, viewOptions);

  // If a toolbox was provided, link it to the vview
  if (toolbox) {
    widget.toolbox = toolbox;
  }

  // Analyzing state history isn't useful with transient object inspectors.
  widget.commitHierarchy = () => {};

  for (let e in relayEvents) {
    widget.on(e, relayEvents[e]);
  }
  VariablesViewController.attach(widget, controllerOptions);

  // Some of the view options are allowed to change between uses.
  widget.searchPlaceholder = viewOptions.searchPlaceholder;
  widget.searchEnabled = viewOptions.searchEnabled;

  // Use the object actor's grip to display it as a variable in the widget.
  // The controller options are allowed to change between uses.
  widget.controller.setSingleVariable(
    { objectActor: objectActor }, controllerOptions);

  tooltip.content = vbox;
  tooltip.panel.setAttribute("clamped-dimensions", "");
}

exports.setTooltipVariableContent = setTooltipVariableContent;
PK
!<h7d��Wchrome/devtools/modules/devtools/client/shared/widgets/tooltip/VariableTooltipHelper.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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
  let div = doc.createElementNS(XHTML_NS, "div");
  div.classList.add("devtools-monospace", "devtools-tooltip-css-variable");
  div.textContent = text;

  tooltip.setContent(div);
}

module.exports.setVariableTooltip = setVariableTooltip;
PK
!<�s+����Fchrome/devtools/modules/devtools/client/shared/widgets/view-helpers.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 {KeyCodes} = require("devtools/client/shared/keycodes");

const PANE_APPEARANCE_DELAY = 50;
const PAGE_SIZE_ITEM_COUNT_RATIO = 5;
const WIDGET_FOCUSABLE_NODES = new Set(["vbox", "hbox"]);

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;

/**
 * Same as `setNamedTimeout`, but invokes the callback only if the provided
 * predicate function returns true. Otherwise, the timeout is re-triggered.
 *
 * @param string id
 *        A string identifier for the conditional timeout.
 * @param number wait
 *        The amount of milliseconds to wait after no more events are fired.
 * @param function predicate
 *        The predicate function used to determine whether the timeout restarts.
 * @param function callback
 *        Invoked when no more events are fired after the specified time, and
 *        the provided predicate function returns true.
 */
const setConditionalTimeout = function setConditionalTimeout(id, wait,
                                                             predicate,
                                                             callback) {
  setNamedTimeout(id, wait, function maybeCallback() {
    if (predicate()) {
      callback();
      return;
    }
    setConditionalTimeout(id, wait, predicate, callback);
  });
};
exports.setConditionalTimeout = setConditionalTimeout;

/**
 * Clears a conditional timeout.
 * @see setConditionalTimeout
 *
 * @param string id
 *        A string identifier for the conditional timeout.
 */
const clearConditionalTimeout = function clearConditionalTimeout(id) {
  clearNamedTimeout(id);
};
exports.clearConditionalTimeout = clearConditionalTimeout;

/**
 * Helpers for creating and messaging between UI components.
 */
const ViewHelpers = exports.ViewHelpers = {
  /**
   * Convenience method, dispatching a custom event.
   *
   * @param nsIDOMNode 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: function (target, type, detail) {
    if (!(target instanceof Node)) {
      // Event cancelled.
      return true;
    }
    let document = target.ownerDocument || target;
    let dispatcher = target.ownerDocument ? target : document.documentElement;

    let 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 nsIDOMNode node
   *        A node to delegate the methods to.
   */
  delegateWidgetAttributeMethods: function (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 nsIDOMNode node
   *        A node to delegate the methods to.
   */
  delegateWidgetEventMethods: function (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: function (object) {
    return object && 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: function (object) {
    return object instanceof Node ||
           object instanceof Element ||
           object instanceof DocumentFragment;
  },

  /**
   * Prevents event propagation when navigation keys are pressed.
   *
   * @param Event e
   *        The event to be prevented.
   */
  preventScrolling: function (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: function (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 nsIDOMNode pane
   *        The element representing the pane to toggle.
   */
  togglePane: function (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.
    let 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 {
        let width = Math.floor(pane.getAttribute("width")) + 1;
        let 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) {
        let 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 an nsIDOMNode, 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 nsIDOMNode 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 nsIDOMNode element
   *        An nsIDOMNode 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: function (element, options = {}) {
    let 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: function (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 nsIDOMNode element
   *        The element displaying the item.
   */
  _entangleItem: function (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: function (item) {
    if (item.finalize) {
      item.finalize(item);
    }
    for (let 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: function (item) {
    this._itemsByElement.delete(item._target);
  },

  /**
   * Returns a string representing the object.
   * Avoid using `toString` to avoid accidental JSONification.
   * @return string
   */
  stringify: function () {
    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
};

/**
 * Some generic Widget methods handling Item instances.
 * Iterable via "for (let childItem of wrappedView) { }".
 *
 * Usage:
 *   function MyView() {
 *     this.widget = new MyWidget(document.querySelector(".my-node"));
 *   }
 *
 *   MyView.prototype = extend(WidgetMethods, {
 *     myMethod: function() {},
 *     ...
 *   });
 *
 * See https://gist.github.com/victorporof/5749386 for more details.
 * The devtools/shared/widgets/SimpleListWidget.jsm is an implementation
 * example.
 *
 * Language:
 *   - An "item" is an instance of an Item.
 *   - An "element" or "node" is a nsIDOMNode.
 *
 * The supplied widget can be any object implementing the following
 * methods:
 *   - function:nsIDOMNode insertItemAt(aIndex:number, aNode:nsIDOMNode,
 *                                      aValue:string)
 *   - function:nsIDOMNode getItemAtIndex(aIndex:number)
 *   - function removeChild(aChild:nsIDOMNode)
 *   - function removeAllItems()
 *   - get:nsIDOMNode selectedItem()
 *   - set selectedItem(aChild:nsIDOMNode)
 *   - function getAttribute(aName:string)
 *   - function setAttribute(aName:string, aValue:string)
 *   - function removeAttribute(aName:string)
 *   - function addEventListener(aName:string, aCallback:function,
 *                               aBubbleFlag:boolean)
 *   - function removeEventListener(aName:string, aCallback:function,
 *                                  aBubbleFlag:boolean)
 *
 * Optional methods that can be implemented by the widget:
 *   - function ensureElementIsVisible(aChild:nsIDOMNode)
 *
 * Optional attributes that may be handled (when calling
 * get/set/removeAttribute):
 *   - "emptyText": label temporarily added when there are no items present
 *   - "headerText": label permanently added as a header
 *
 * For automagical keyboard and mouse accessibility, the widget should be an
 * event emitter with the following events:
 *   - "keyDown" -> (aName:string, aEvent:KeyboardEvent)
 *   - "mousePress" -> (aName:string, aEvent:MouseEvent)
 */
const WidgetMethods = exports.WidgetMethods = {
  /**
   * Sets the element node or widget associated with this container.
   * @param nsIDOMNode | object widget
   */
  set widget(widget) {
    this._widget = widget;

    // Can't use a WeakMap for _itemsByValue because keys are strings, and
    // can't use one for _itemsByElement either, since it needs to be iterable.
    this._itemsByValue = new Map();
    this._itemsByElement = new Map();
    this._stagedItems = [];

    // Handle internal events emitted by the widget if necessary.
    if (ViewHelpers.isEventEmitter(widget)) {
      widget.on("keyDown", this._onWidgetKeyDown.bind(this));
      widget.on("mousePress", this._onWidgetMousePress.bind(this));
    }
  },

  /**
   * Gets the element node or widget associated with this container.
   * @return nsIDOMNode | object
   */
  get widget() {
    return this._widget;
  },

  /**
   * Prepares an item to be added to this container. This allows, for example,
   * for a large number of items to be batched up before being sorted & added.
   *
   * If the "staged" flag is *not* set to true, the item will be immediately
   * inserted at the correct position in this container, so that all the items
   * still remain sorted. This can (possibly) be much slower than batching up
   * multiple items.
   *
   * By default, this container assumes that all the items should be displayed
   * sorted by their value. This can be overridden with the "index" flag,
   * specifying on which position should an item be appended. The "staged" and
   * "index" flags are mutually exclusive, meaning that all staged items
   * will always be appended.
   *
   * @param nsIDOMNode element
   *        A prebuilt node to be wrapped.
   * @param string value
   *        A string identifying the node.
   * @param object options [optional]
   *        Additional options or flags supported by this operation:
   *          - attachment: some attached primitive/object for the item
   *          - staged: true to stage the item to be appended later
   *          - index: specifies on which position should the item be appended
   *          - attributes: a batch of attributes set to the displayed element
   *          - finalize: function invoked when the item is removed
   * @return Item
   *         The item associated with the displayed element if an unstaged push,
   *         undefined if the item was staged for a later commit.
   */
  push: function ([element, value], options = {}) {
    let item = new Item(this, element, value, options.attachment);

    // Batch the item to be added later.
    if (options.staged) {
      // An ulterior commit operation will ignore any specified index, so
      // no reason to keep it around.
      options.index = undefined;
      return void this._stagedItems.push({ item: item, options: options });
    }
    // Find the target position in this container and insert the item there.
    if (!("index" in options)) {
      return this._insertItemAt(this._findExpectedIndexFor(item), item,
                                options);
    }
    // Insert the item at the specified index. If negative or out of bounds,
    // the item will be simply appended.
    return this._insertItemAt(options.index, item, options);
  },

  /**
   * Flushes all the prepared items into this container.
   * Any specified index on the items will be ignored. Everything is appended.
   *
   * @param object options [optional]
   *        Additional options or flags supported by this operation:
   *          - sorted: true to sort all the items before adding them
   */
  commit: function (options = {}) {
    let stagedItems = this._stagedItems;

    // Sort the items before adding them to this container, if preferred.
    if (options.sorted) {
      stagedItems.sort((a, b) => this._currentSortPredicate(a.item, b.item));
    }
    // Append the prepared items to this container.
    for (let { item, opt } of stagedItems) {
      this._insertItemAt(-1, item, opt);
    }
    // Recreate the temporary items list for ulterior pushes.
    this._stagedItems.length = 0;
  },

  /**
   * Immediately removes the specified item from this container.
   *
   * @param Item item
   *        The item associated with the element to remove.
   */
  remove: function (item) {
    if (!item) {
      return;
    }
    this._widget.removeChild(item._target);
    this._untangleItem(item);

    if (!this._itemsByElement.size) {
      this._preferredValue = this.selectedValue;
      this._widget.selectedItem = null;
      this._widget.setAttribute("emptyText", this._emptyText);
    }
  },

  /**
   * Removes the item at the specified index from this container.
   *
   * @param number index
   *        The index of the item to remove.
   */
  removeAt: function (index) {
    this.remove(this.getItemAtIndex(index));
  },

  /**
   * Removes the items in this container based on a predicate.
   */
  removeForPredicate: function (predicate) {
    let item;
    while ((item = this.getItemForPredicate(predicate))) {
      this.remove(item);
    }
  },

  /**
   * Removes all items from this container.
   */
  empty: function () {
    this._preferredValue = this.selectedValue;
    this._widget.selectedItem = null;
    this._widget.removeAllItems();
    this._widget.setAttribute("emptyText", this._emptyText);

    for (let [, item] of this._itemsByElement) {
      this._untangleItem(item);
    }

    this._itemsByValue.clear();
    this._itemsByElement.clear();
    this._stagedItems.length = 0;
  },

  /**
   * Ensures the specified item is visible in this container.
   *
   * @param Item item
   *        The item to bring into view.
   */
  ensureItemIsVisible: function (item) {
    this._widget.ensureElementIsVisible(item._target);
  },

  /**
   * Ensures the item at the specified index is visible in this container.
   *
   * @param number index
   *        The index of the item to bring into view.
   */
  ensureIndexIsVisible: function (index) {
    this.ensureItemIsVisible(this.getItemAtIndex(index));
  },

  /**
   * Sugar for ensuring the selected item is visible in this container.
   */
  ensureSelectedItemIsVisible: function () {
    this.ensureItemIsVisible(this.selectedItem);
  },

  /**
   * If supported by the widget, the label string temporarily added to this
   * container when there are no child items present.
   */
  set emptyText(value) {
    this._emptyText = value;

    // Apply the emptyText attribute right now if there are no child items.
    if (!this._itemsByElement.size) {
      this._widget.setAttribute("emptyText", value);
    }
  },

  /**
   * If supported by the widget, the label string permanently added to this
   * container as a header.
   * @param string value
   */
  set headerText(value) {
    this._headerText = value;
    this._widget.setAttribute("headerText", value);
  },

  /**
   * Toggles all the items in this container hidden or visible.
   *
   * This does not change the default filtering predicate, so newly inserted
   * items will always be visible. Use WidgetMethods.filterContents if you care.
   *
   * @param boolean visibleFlag
   *        Specifies the intended visibility.
   */
  toggleContents: function (visibleFlag) {
    for (let [element] of this._itemsByElement) {
      element.hidden = !visibleFlag;
    }
  },

  /**
   * Toggles all items in this container hidden or visible based on a predicate.
   *
   * @param function predicate [optional]
   *        Items are toggled according to the return value of this function,
   *        which will become the new default filtering predicate in this
   *        container.
   *        If unspecified, all items will be toggled visible.
   */
  filterContents: function (predicate = this._currentFilterPredicate) {
    this._currentFilterPredicate = predicate;

    for (let [element, item] of this._itemsByElement) {
      element.hidden = !predicate(item);
    }
  },

  /**
   * Sorts all the items in this container based on a predicate.
   *
   * @param function predicate [optional]
   *        Items are sorted according to the return value of the function,
   *        which will become the new default sorting predicate in this
   *        container. If unspecified, all items will be sorted by their value.
   */
  sortContents: function (predicate = this._currentSortPredicate) {
    let sortedItems = this.items.sort(this._currentSortPredicate = predicate);

    for (let i = 0, len = sortedItems.length; i < len; i++) {
      this.swapItems(this.getItemAtIndex(i), sortedItems[i]);
    }
  },

  /**
   * Visually swaps two items in this container.
   *
   * @param Item first
   *        The first item to be swapped.
   * @param Item second
   *        The second item to be swapped.
   */
  swapItems: function (first, second) {
    if (first == second) {
      // We're just dandy, thank you.
      return;
    }
    let { _prebuiltNode: firstPrebuiltTarget, _target: firstTarget } = first;
    let { _prebuiltNode: secondPrebuiltTarget, _target: secondTarget } = second;

    // If the two items were constructed with prebuilt nodes as
    // DocumentFragments, then those DocumentFragments are now
    // empty and need to be reassembled.
    if (firstPrebuiltTarget instanceof DocumentFragment) {
      for (let node of firstTarget.childNodes) {
        firstPrebuiltTarget.appendChild(node.cloneNode(true));
      }
    }
    if (secondPrebuiltTarget instanceof DocumentFragment) {
      for (let node of secondTarget.childNodes) {
        secondPrebuiltTarget.appendChild(node.cloneNode(true));
      }
    }

    // 1. Get the indices of the two items to swap.
    let i = this._indexOfElement(firstTarget);
    let j = this._indexOfElement(secondTarget);

    // 2. Remeber the selection index, to reselect an item, if necessary.
    let selectedTarget = this._widget.selectedItem;
    let selectedIndex = -1;
    if (selectedTarget == firstTarget) {
      selectedIndex = i;
    } else if (selectedTarget == secondTarget) {
      selectedIndex = j;
    }

    // 3. Silently nuke both items, nobody needs to know about this.
    this._widget.removeChild(firstTarget);
    this._widget.removeChild(secondTarget);
    this._unlinkItem(first);
    this._unlinkItem(second);

    // 4. Add the items again, but reversing their indices.
    this._insertItemAt.apply(this, i < j ? [i, second] : [j, first]);
    this._insertItemAt.apply(this, i < j ? [j, first] : [i, second]);

    // 5. Restore the previous selection, if necessary.
    if (selectedIndex == i) {
      this._widget.selectedItem = first._target;
    } else if (selectedIndex == j) {
      this._widget.selectedItem = second._target;
    }

    // 6. Let the outside world know that these two items were swapped.
    ViewHelpers.dispatchEvent(first.target, "swap", [second, first]);
  },

  /**
   * Visually swaps two items in this container at specific indices.
   *
   * @param number first
   *        The index of the first item to be swapped.
   * @param number second
   *        The index of the second item to be swapped.
   */
  swapItemsAtIndices: function (first, second) {
    this.swapItems(this.getItemAtIndex(first), this.getItemAtIndex(second));
  },

  /**
   * Checks whether an item with the specified value is among the elements
   * shown in this container.
   *
   * @param string value
   *        The item's value.
   * @return boolean
   *         True if the value is known, false otherwise.
   */
  containsValue: function (value) {
    return this._itemsByValue.has(value) ||
           this._stagedItems.some(({ item }) => item._value == value);
  },

  /**
   * Gets the "preferred value". This is the latest selected item's value,
   * remembered just before emptying this container.
   * @return string
   */
  get preferredValue() {
    return this._preferredValue;
  },

  /**
   * Retrieves the item associated with the selected element.
   * @return Item | null
   */
  get selectedItem() {
    let selectedElement = this._widget.selectedItem;
    if (selectedElement) {
      return this._itemsByElement.get(selectedElement);
    }
    return null;
  },

  /**
   * Retrieves the selected element's index in this container.
   * @return number
   */
  get selectedIndex() {
    let selectedElement = this._widget.selectedItem;
    if (selectedElement) {
      return this._indexOfElement(selectedElement);
    }
    return -1;
  },

  /**
   * Retrieves the value of the selected element.
   * @return string
   */
  get selectedValue() {
    let selectedElement = this._widget.selectedItem;
    if (selectedElement) {
      return this._itemsByElement.get(selectedElement)._value;
    }
    return "";
  },

  /**
   * Retrieves the attachment of the selected element.
   * @return object | null
   */
  get selectedAttachment() {
    let selectedElement = this._widget.selectedItem;
    if (selectedElement) {
      return this._itemsByElement.get(selectedElement).attachment;
    }
    return null;
  },

  _selectItem: function (item) {
    // A falsy item is allowed to invalidate the current selection.
    let targetElement = item ? item._target : null;
    let prevElement = this._widget.selectedItem;

    // Make sure the selected item's target element is focused and visible.
    if (this.autoFocusOnSelection && targetElement) {
      targetElement.focus();
    }

    if (targetElement != prevElement) {
      this._widget.selectedItem = targetElement;
    }
  },

  /**
   * Selects the element with the entangled item in this container.
   * @param Item | function item
   */
  set selectedItem(item) {
    // A predicate is allowed to select a specific item.
    // If no item is matched, then the current selection is removed.
    if (typeof item == "function") {
      item = this.getItemForPredicate(item);
    }

    let targetElement = item ? item._target : null;
    let prevElement = this._widget.selectedItem;

    if (this.maintainSelectionVisible && targetElement) {
      // Some methods are optional. See the WidgetMethods object documentation
      // for a comprehensive list.
      if ("ensureElementIsVisible" in this._widget) {
        this._widget.ensureElementIsVisible(targetElement);
      }
    }

    this._selectItem(item);

    // Prevent selecting the same item again and avoid dispatching
    // a redundant selection event, so return early.
    if (targetElement != prevElement) {
      let dispTarget = targetElement || prevElement;
      let dispName = this.suppressSelectionEvents ? "suppressed-select"
                                                  : "select";
      ViewHelpers.dispatchEvent(dispTarget, dispName, item);
    }
  },

  /**
   * Selects the element at the specified index in this container.
   * @param number index
   */
  set selectedIndex(index) {
    let targetElement = this._widget.getItemAtIndex(index);
    if (targetElement) {
      this.selectedItem = this._itemsByElement.get(targetElement);
      return;
    }
    this.selectedItem = null;
  },

  /**
   * Selects the element with the specified value in this container.
   * @param string value
   */
  set selectedValue(value) {
    this.selectedItem = this._itemsByValue.get(value);
  },

  /**
   * Deselects and re-selects an item in this container.
   *
   * Useful when you want a "select" event to be emitted, even though
   * the specified item was already selected.
   *
   * @param Item | function item
   * @see `set selectedItem`
   */
  forceSelect: function (item) {
    this.selectedItem = null;
    this.selectedItem = item;
  },

  /**
   * Specifies if this container should try to keep the selected item visible.
   * (For example, when new items are added the selection is brought into view).
   */
  maintainSelectionVisible: true,

  /**
   * Specifies if "select" events dispatched from the elements in this container
   * when their respective items are selected should be suppressed or not.
   *
   * If this flag is set to true, then consumers of this container won't
   * be normally notified when items are selected.
   */
  suppressSelectionEvents: false,

  /**
   * Focus this container the first time an element is inserted?
   *
   * If this flag is set to true, then when the first item is inserted in
   * this container (and thus it's the only item available), its corresponding
   * target element is focused as well.
   */
  autoFocusOnFirstItem: true,

  /**
   * Focus on selection?
   *
   * If this flag is set to true, then whenever an item is selected in
   * this container (e.g. via the selectedIndex or selectedItem setters),
   * its corresponding target element is focused as well.
   *
   * You can disable this flag, for example, to maintain a certain node
   * focused but visually indicate a different selection in this container.
   */
  autoFocusOnSelection: true,

  /**
   * Focus on input (e.g. mouse click)?
   *
   * If this flag is set to true, then whenever an item receives user input in
   * this container, its corresponding target element is focused as well.
   */
  autoFocusOnInput: true,

  /**
   * When focusing on input, allow right clicks?
   * @see WidgetMethods.autoFocusOnInput
   */
  allowFocusOnRightClick: false,

  /**
   * The number of elements in this container to jump when Page Up or Page Down
   * keys are pressed. If falsy, then the page size will be based on the
   * number of visible items in the container.
   */
  pageSize: 0,

  /**
   * Focuses the first visible item in this container.
   */
  focusFirstVisibleItem: function () {
    this.focusItemAtDelta(-this.itemCount);
  },

  /**
   * Focuses the last visible item in this container.
   */
  focusLastVisibleItem: function () {
    this.focusItemAtDelta(+this.itemCount);
  },

  /**
   * Focuses the next item in this container.
   */
  focusNextItem: function () {
    this.focusItemAtDelta(+1);
  },

  /**
   * Focuses the previous item in this container.
   */
  focusPrevItem: function () {
    this.focusItemAtDelta(-1);
  },

  /**
   * Focuses another item in this container based on the index distance
   * from the currently focused item.
   *
   * @param number delta
   *        A scalar specifying by how many items should the selection change.
   */
  focusItemAtDelta: function (delta) {
    // Make sure the currently selected item is also focused, so that the
    // command dispatcher mechanism has a relative node to work with.
    // If there's no selection, just select an item at a corresponding index
    // (e.g. the first item in this container if delta <= 1).
    let selectedElement = this._widget.selectedItem;
    if (selectedElement) {
      selectedElement.focus();
    } else {
      this.selectedIndex = Math.max(0, delta - 1);
      return;
    }

    let direction = delta > 0 ? "advanceFocus" : "rewindFocus";
    let distance = Math.abs(Math[delta > 0 ? "ceil" : "floor"](delta));
    while (distance--) {
      if (!this._focusChange(direction)) {
        // Out of bounds.
        break;
      }
    }

    // Synchronize the selected item as being the currently focused element.
    this.selectedItem = this.getItemForElement(this._focusedElement);
  },

  /**
   * Focuses the next or previous item in this container.
   *
   * @param string direction
   *        Either "advanceFocus" or "rewindFocus".
   * @return boolean
   *         False if the focus went out of bounds and the first or last item
   *         in this container was focused instead.
   */
  _focusChange: function (direction) {
    let commandDispatcher = this._commandDispatcher;
    let prevFocusedElement = commandDispatcher.focusedElement;
    let currFocusedElement;

    do {
      commandDispatcher.suppressFocusScroll = true;
      commandDispatcher[direction]();
      currFocusedElement = commandDispatcher.focusedElement;

      // Make sure the newly focused item is a part of this container. If the
      // focus goes out of bounds, revert the previously focused item.
      if (!this.getItemForElement(currFocusedElement)) {
        prevFocusedElement.focus();
        return false;
      }
    } while (!WIDGET_FOCUSABLE_NODES.has(currFocusedElement.tagName));

    // Focus remained within bounds.
    return true;
  },

  /**
   * Gets the command dispatcher instance associated with this container's DOM.
   * If there are no items displayed in this container, null is returned.
   * @return nsIDOMXULCommandDispatcher | null
   */
  get _commandDispatcher() {
    if (this._cachedCommandDispatcher) {
      return this._cachedCommandDispatcher;
    }
    let someElement = this._widget.getItemAtIndex(0);
    if (someElement) {
      let commandDispatcher = someElement.ownerDocument.commandDispatcher;
      this._cachedCommandDispatcher = commandDispatcher;
      return commandDispatcher;
    }
    return null;
  },

  /**
   * Gets the currently focused element in this container.
   *
   * @return nsIDOMNode
   *         The focused element, or null if nothing is found.
   */
  get _focusedElement() {
    let commandDispatcher = this._commandDispatcher;
    if (commandDispatcher) {
      return commandDispatcher.focusedElement;
    }
    return null;
  },

  /**
   * Gets the item in the container having the specified index.
   *
   * @param number index
   *        The index used to identify the element.
   * @return Item
   *         The matched item, or null if nothing is found.
   */
  getItemAtIndex: function (index) {
    return this.getItemForElement(this._widget.getItemAtIndex(index));
  },

  /**
   * Gets the item in the container having the specified value.
   *
   * @param string value
   *        The value used to identify the element.
   * @return Item
   *         The matched item, or null if nothing is found.
   */
  getItemByValue: function (value) {
    return this._itemsByValue.get(value);
  },

  /**
   * Gets the item in the container associated with the specified element.
   *
   * @param nsIDOMNode element
   *        The element used to identify the item.
   * @param object flags [optional]
   *        Additional options for showing the source. Supported options:
   *          - noSiblings: if siblings shouldn't be taken into consideration
   *                        when searching for the associated item.
   * @return Item
   *         The matched item, or null if nothing is found.
   */
  getItemForElement: function (element, flags = {}) {
    while (element) {
      let item = this._itemsByElement.get(element);

      // Also search the siblings if allowed.
      if (!flags.noSiblings) {
        item = item ||
          this._itemsByElement.get(element.nextElementSibling) ||
          this._itemsByElement.get(element.previousElementSibling);
      }
      if (item) {
        return item;
      }
      element = element.parentNode;
    }
    return null;
  },

  /**
   * Gets a visible item in this container validating a specified predicate.
   *
   * @param function predicate
   *        The first item which validates this predicate is returned
   * @return Item
   *         The matched item, or null if nothing is found.
   */
  getItemForPredicate: function (predicate, owner = this) {
    // Recursively check the items in this widget for a predicate match.
    for (let [element, item] of owner._itemsByElement) {
      let match;
      if (predicate(item) && !element.hidden) {
        match = item;
      } else {
        match = this.getItemForPredicate(predicate, item);
      }
      if (match) {
        return match;
      }
    }
    // Also check the staged items. No need to do this recursively since
    // they're not even appended to the view yet.
    for (let { item } of this._stagedItems) {
      if (predicate(item)) {
        return item;
      }
    }
    return null;
  },

  /**
   * Shortcut function for getItemForPredicate which works on item attachments.
   * @see getItemForPredicate
   */
  getItemForAttachment: function (predicate, owner = this) {
    return this.getItemForPredicate(e => predicate(e.attachment));
  },

  /**
   * Finds the index of an item in the container.
   *
   * @param Item item
   *        The item get the index for.
   * @return number
   *         The index of the matched item, or -1 if nothing is found.
   */
  indexOfItem: function (item) {
    return this._indexOfElement(item._target);
  },

  /**
   * Finds the index of an element in the container.
   *
   * @param nsIDOMNode element
   *        The element get the index for.
   * @return number
   *         The index of the matched element, or -1 if nothing is found.
   */
  _indexOfElement: function (element) {
    for (let i = 0; i < this._itemsByElement.size; i++) {
      if (this._widget.getItemAtIndex(i) == element) {
        return i;
      }
    }
    return -1;
  },

  /**
   * Gets the total number of items in this container.
   * @return number
   */
  get itemCount() {
    return this._itemsByElement.size;
  },

  /**
   * Returns a list of items in this container, in the displayed order.
   * @return array
   */
  get items() {
    let store = [];
    let itemCount = this.itemCount;
    for (let i = 0; i < itemCount; i++) {
      store.push(this.getItemAtIndex(i));
    }
    return store;
  },

  /**
   * Returns a list of values in this container, in the displayed order.
   * @return array
   */
  get values() {
    return this.items.map(e => e._value);
  },

  /**
   * Returns a list of attachments in this container, in the displayed order.
   * @return array
   */
  get attachments() {
    return this.items.map(e => e.attachment);
  },

  /**
   * Returns a list of all the visible (non-hidden) items in this container,
   * in the displayed order
   * @return array
   */
  get visibleItems() {
    return this.items.filter(e => !e._target.hidden);
  },

  /**
   * Checks if an item is unique in this container. If an item's value is an
   * empty string, "undefined" or "null", it is considered unique.
   *
   * @param Item item
   *        The item for which to verify uniqueness.
   * @return boolean
   *         True if the item is unique, false otherwise.
   */
  isUnique: function (item) {
    let value = item._value;
    if (value == "" || value == "undefined" || value == "null") {
      return true;
    }
    return !this._itemsByValue.has(value);
  },

  /**
   * Checks if an item is eligible for this container. By default, this checks
   * whether an item is unique and has a prebuilt target node.
   *
   * @param Item item
   *        The item for which to verify eligibility.
   * @return boolean
   *         True if the item is eligible, false otherwise.
   */
  isEligible: function (item) {
    return this.isUnique(item) && item._prebuiltNode;
  },

  /**
   * Finds the expected item index in this container based on the default
   * sort predicate.
   *
   * @param Item item
   *        The item for which to get the expected index.
   * @return number
   *         The expected item index.
   */
  _findExpectedIndexFor: function (item) {
    let itemCount = this.itemCount;
    for (let i = 0; i < itemCount; i++) {
      if (this._currentSortPredicate(this.getItemAtIndex(i), item) > 0) {
        return i;
      }
    }
    return itemCount;
  },

  /**
   * Immediately inserts an item in this container at the specified index.
   *
   * @param number index
   *        The position in the container intended for this item.
   * @param Item item
   *        The item describing a target element.
   * @param object options [optional]
   *        Additional options or flags supported by this operation:
   *          - attributes: a batch of attributes set to the displayed element
   *          - finalize: function when the item is untangled (removed)
   * @return Item
   *         The item associated with the displayed element, null if rejected.
   */
  _insertItemAt: function (index, item, options = {}) {
    if (!this.isEligible(item)) {
      return null;
    }

    // Entangle the item with the newly inserted node.
    // Make sure this is done with the value returned by insertItemAt(),
    // to avoid storing a potential DocumentFragment.
    let node = item._prebuiltNode;
    let attachment = item.attachment;
    this._entangleItem(item,
                       this._widget.insertItemAt(index, node, attachment));

    // Handle any additional options after entangling the item.
    if (!this._currentFilterPredicate(item)) {
      item._target.hidden = true;
    }
    if (this.autoFocusOnFirstItem && this._itemsByElement.size == 1) {
      item._target.focus();
    }
    if (options.attributes) {
      options.attributes.forEach(e => item._target.setAttribute(e[0], e[1]));
    }
    if (options.finalize) {
      item.finalize = options.finalize;
    }

    // Hide the empty text if the selection wasn't lost.
    this._widget.removeAttribute("emptyText");

    // Return the item associated with the displayed element.
    return item;
  },

  /**
   * Entangles an item (model) with a displayed node element (view).
   *
   * @param Item item
   *        The item describing a target element.
   * @param nsIDOMNode element
   *        The element displaying the item.
   */
  _entangleItem: function (item, element) {
    this._itemsByValue.set(item._value, item);
    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: function (item) {
    if (item.finalize) {
      item.finalize(item);
    }
    for (let 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: function (item) {
    this._itemsByValue.delete(item._value);
    this._itemsByElement.delete(item._target);
  },

  /**
   * The keyDown event listener for this container.
   * @param string name
   * @param KeyboardEvent event
   */
  _onWidgetKeyDown: function (event) {
    // Prevent scrolling when pressing navigation keys.
    ViewHelpers.preventScrolling(event);

    switch (event.keyCode) {
      case KeyCodes.DOM_VK_UP:
      case KeyCodes.DOM_VK_LEFT:
        this.focusPrevItem();
        break;
      case KeyCodes.DOM_VK_DOWN:
      case KeyCodes.DOM_VK_RIGHT:
        this.focusNextItem();
        break;
      case KeyCodes.DOM_VK_PAGE_UP:
        this.focusItemAtDelta(-(this.pageSize ||
                               (this.itemCount / PAGE_SIZE_ITEM_COUNT_RATIO)));
        break;
      case KeyCodes.DOM_VK_PAGE_DOWN:
        this.focusItemAtDelta(+(this.pageSize ||
                               (this.itemCount / PAGE_SIZE_ITEM_COUNT_RATIO)));
        break;
      case KeyCodes.DOM_VK_HOME:
        this.focusFirstVisibleItem();
        break;
      case KeyCodes.DOM_VK_END:
        this.focusLastVisibleItem();
        break;
    }
  },

  /**
   * The mousePress event listener for this container.
   * @param string name
   * @param MouseEvent event
   */
  _onWidgetMousePress: function (event) {
    if (event.button != 0 && !this.allowFocusOnRightClick) {
      // Only allow left-click to trigger this event.
      return;
    }

    let item = this.getItemForElement(event.target);
    if (item) {
      // The container is not empty and we clicked on an actual item.
      this.selectedItem = item;
      // Make sure the current event's target element is also focused.
      this.autoFocusOnInput && item._target.focus();
    }
  },

  /**
   * The predicate used when filtering items. By default, all items in this
   * view are visible.
   *
   * @param Item item
   *        The item passing through the filter.
   * @return boolean
   *         True if the item should be visible, false otherwise.
   */
  _currentFilterPredicate: function (item) {
    return true;
  },

  /**
   * The predicate used when sorting items. By default, items in this view
   * are sorted by their label.
   *
   * @param Item first
   *        The first item used in the comparison.
   * @param Item second
   *        The second item used in the comparison.
   * @return number
   *         -1 to sort first to a lower index than second
   *          0 to leave first and second unchanged with respect to each other
   *          1 to sort second to a lower index than first
   */
  _currentSortPredicate: function (first, second) {
    return +(first._value.toLowerCase() > second._value.toLowerCase());
  },

  /**
   * Call a method on this widget named `methodName`. Any further arguments are
   * passed on to the method. Returns the result of the method call.
   *
   * @param String methodName
   *        The name of the method you want to call.
   * @param args
   *        Optional. Any arguments you want to pass through to the method.
   */
  callMethod: function (methodName, ...args) {
    return this._widget[methodName].apply(this._widget, args);
  },

  _widget: null,
  _emptyText: "",
  _headerText: "",
  _preferredValue: "",
  _cachedCommandDispatcher: null
};

/**
 * A generator-iterator over all the items in this container.
 */
Item.prototype[Symbol.iterator] =
WidgetMethods[Symbol.iterator] = function* () {
  yield* this._itemsByElement.values();
};
PK
!<p�8ַ	�	;chrome/devtools/modules/devtools/client/shared/zoom-keys.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Ci } = require("chrome");
const Services = require("Services");
const KeyShortcuts = require("devtools/client/shared/key-shortcuts");

const ZOOM_PREF = "devtools.toolbox.zoomValue";
const MIN_ZOOM = 0.5;
const MAX_ZOOM = 2;

const {LocalizationHelper} = require("devtools/shared/l10n");
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.
 */
exports.register = function (window) {
  let shortcuts = new KeyShortcuts({
    window
  });
  let docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
    .getInterface(Ci.nsIWebNavigation)
    .QueryInterface(Ci.nsIDocShell);
  let contViewer = docShell.contentViewer;
  let zoomValue = parseFloat(Services.prefs.getCharPref(ZOOM_PREF));
  let zoomIn = function (event) {
    setZoom(zoomValue + 0.1);
    event.preventDefault();
  };

  let zoomOut = function (event) {
    setZoom(zoomValue - 0.1);
    event.preventDefault();
  };

  let zoomReset = function (event) {
    setZoom(1);
    event.preventDefault();
  };

  let setZoom = function (newValue) {
    // cap zoom value
    zoomValue = Math.max(newValue, MIN_ZOOM);
    zoomValue = Math.min(zoomValue, MAX_ZOOM);

    contViewer.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);
  let zoomIn2 = L10N.getStr("toolbox.zoomIn2.key");
  if (zoomIn2) {
    shortcuts.on(zoomIn2, zoomIn);
  }
  let zoomIn3 = L10N.getStr("toolbox.zoomIn2.key");
  if (zoomIn3) {
    shortcuts.on(zoomIn3, zoomIn);
  }

  shortcuts.on(L10N.getStr("toolbox.zoomOut.key"),
               zoomOut);
  let zoomOut2 = L10N.getStr("toolbox.zoomOut2.key");
  if (zoomOut2) {
    shortcuts.on(zoomOut2, zoomOut);
  }

  shortcuts.on(L10N.getStr("toolbox.zoomReset.key"),
               zoomReset);
  let zoomReset2 = L10N.getStr("toolbox.zoomReset2.key");
  if (zoomReset2) {
    shortcuts.on(zoomReset2, zoomReset);
  }
};
PK
!<�4���0�0Dchrome/devtools/modules/devtools/client/sourceeditor/autocomplete.js/* vim:set ts=2 sw=2 sts=2 et tw=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 AutocompletePopup = require("devtools/client/shared/autocomplete-popup");
const CSSCompleter = require("devtools/client/sourceeditor/css-autocompleter");
const {KeyCodes} = require("devtools/client/shared/keycodes");

const CM_TERN_SCRIPTS = [
  "chrome://devtools/content/sourceeditor/codemirror/addon/tern/tern.js",
  "chrome://devtools/content/sourceeditor/codemirror/addon/hint/show-hint.js"
];

const autocompleteMap = new WeakMap();

/**
 * Prepares an editor instance for autocompletion.
 */
function initializeAutoCompletion(ctx, options = {}) {
  let { cm, ed, Editor } = ctx;
  if (autocompleteMap.has(ed)) {
    return;
  }

  let win = ed.container.contentWindow.wrappedJSObject;
  let { CodeMirror, document } = win;

  let completer = null;
  let autocompleteKey = "Ctrl-" +
                        Editor.keyFor("autocompletion", { noaccel: true });
  if (ed.config.mode == Editor.modes.js) {
    let defs = [
      require("./tern/browser"),
      require("./tern/ecma5"),
    ];

    CM_TERN_SCRIPTS.forEach(ed.loadScript, ed);
    win.tern = require("./tern/tern");
    cm.tern = new CodeMirror.TernServer({
      defs: defs,
      typeTip: function (data) {
        let tip = document.createElement("span");
        tip.className = "CodeMirror-Tern-information";
        let tipType = document.createElement("strong");
        let tipText = document.createTextNode(data.type ||
          cm.l10n("autocompletion.notFound"));
        tipType.appendChild(tipText);
        tip.appendChild(tipType);

        if (data.doc) {
          tip.appendChild(document.createTextNode(" — " + data.doc));
        }

        if (data.url) {
          tip.appendChild(document.createTextNode(" "));
          let docLink = document.createElement("a");
          docLink.textContent = "[" + cm.l10n("autocompletion.docsLink") + "]";
          docLink.href = data.url;
          docLink.className = "theme-link";
          docLink.setAttribute("target", "_blank");
          tip.appendChild(docLink);
        }

        return tip;
      }
    });

    let keyMap = {};
    let updateArgHintsCallback = cm.tern.updateArgHints.bind(cm.tern, cm);
    cm.on("cursorActivity", updateArgHintsCallback);

    keyMap[autocompleteKey] = cmArg => {
      cmArg.tern.getHint(cmArg, data => {
        CodeMirror.on(data, "shown", () => ed.emit("before-suggest"));
        CodeMirror.on(data, "close", () => ed.emit("after-suggest"));
        CodeMirror.on(data, "select", () => ed.emit("suggestion-entered"));
        CodeMirror.showHint(cmArg, (cmIgnore, cb) => cb(data), { async: true });
      });
    };

    keyMap[Editor.keyFor("showInformation2", { noaccel: true })] = cmArg => {
      cmArg.tern.showType(cmArg, null, () => {
        ed.emit("show-information");
      });
    };
    cm.addKeyMap(keyMap);

    let destroyTern = function () {
      ed.off("destroy", destroyTern);
      cm.off("cursorActivity", updateArgHintsCallback);
      cm.removeKeyMap(keyMap);
      win.tern = cm.tern = null;
      autocompleteMap.delete(ed);
    };

    ed.on("destroy", destroyTern);

    autocompleteMap.set(ed, {
      destroy: destroyTern
    });

    // TODO: Integrate tern autocompletion with this autocomplete API.
    return;
  } else if (ed.config.mode == Editor.modes.css) {
    completer = new CSSCompleter({walker: options.walker,
                                  cssProperties: options.cssProperties});
  }

  function insertSelectedPopupItem() {
    let 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",
    theme: "auto",
    autoSelect: true,
    onClick: insertSelectedPopupItem
  });

  let cycle = reverse => {
    if (popup && 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": () => {
      let wasHandled = insertSelectedPopupItem();
      return wasHandled ? true : CodeMirror.Pass;
    }
  };

  let autoCompleteCallback = autoComplete.bind(null, ctx);
  let 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: popup,
    completer: completer,
    keyMap: keyMap,
    destroy: destroy,
    insertingSuggestion: false,
    suggestionInsertedOnce: false
  });
}

/**
 * Destroy autocompletion on an editor instance.
 */
function destroyAutoCompletion(ctx) {
  let { ed } = ctx;
  if (!autocompleteMap.has(ed)) {
    return;
  }

  let {destroy} = autocompleteMap.get(ed);
  destroy();
}

/**
 * Provides suggestions to autocomplete the current token/word being typed.
 */
function autoComplete({ ed, cm }) {
  let autocompleteOpts = autocompleteMap.get(ed);
  let { completer, popup } = autocompleteOpts;
  if (!completer || autocompleteOpts.insertingSuggestion ||
      autocompleteOpts.doNotAutocomplete) {
    autocompleteOpts.insertingSuggestion = false;
    return;
  }
  let 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). 4 comes from the popup's left padding.

    let cursorElement = cm.display.cursorDiv.querySelector(".CodeMirror-cursor");
    let left = suggestions[0].preLabel.length * cm.defaultCharWidth() + 4;
    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) {
  let {preLabel, text} = popupItem;
  let cur = ed.getCursor();
  let textBeforeCursor = ed.getText(cur.line).substring(0, cur.ch);
  let backwardsTextBeforeCursor = textBeforeCursor.split("").reverse().join("");
  let 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) {
  let autocompleteOpts = autocompleteMap.get(ed);
  let { popup } = autocompleteOpts;
  let 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 {
    let 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) {
  let 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) {
        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)) {
    let completer = autocompleteMap.get(ed).completer;
    if (completer && 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;
PK
!<������Ichrome/devtools/modules/devtools/client/sourceeditor/css-autocompleter.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

/* eslint-disable complexity */
const {cssTokenizer, cssTokenizerWithLineColumn} = require("devtools/shared/css/parsing-utils");
const {getClientCssProperties} = require("devtools/shared/fronts/css-properties");

/**
 * 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;
  // If no css properties database is passed in, default to the client list.
  this.cssProperties = options.cssProperties || getClientCssProperties();

  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: function (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.
   */
  resolveState: function (source, {line, ch}) {
    // Function to return the last element of an array
    let 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
    let matchedStateIndex = this.findNearestNullState(line);
    if (matchedStateIndex > -1) {
      let 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 = [];
    }
    let tokens = cssTokenizerWithLineColumn(source);
    let 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))) {
                  let 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))) {
                  let 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 == 0) {
          this.nullStates.push([token.loc.end.line, token.loc.end.column,
                                [...scopeStack]]);
          continue;
        }
        let tokenLine = token.loc.end.line;
        let 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: function () {
    let 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: function (result) {
    if (this._currentQuery != result.query) {
      return [];
    }

    result = result.suggestions;
    let query = this.selector;
    let 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;
      }

      let 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: function (startProp) {
    let finalList = [];
    if (!startProp) {
      return Promise.resolve(finalList);
    }

    let length = this.propertyNames.length;
    let i = 0, count = 0;
    for (; i < length && count < this.maxEntries; i++) {
      if (this.propertyNames[i].startsWith(startProp)) {
        count++;
        let 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: function (propName, startValue) {
    let finalList = [];
    let 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);
    }

    let length = list.length;
    let i = 0, count = 0;
    for (; i < length && count < this.maxEntries; i++) {
      if (list[i].startsWith(startValue)) {
        count++;
        let 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: function (line) {
    let 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: function (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: function (source, caret) {
    // Limits the input source till the {line, ch} caret position
    function limit(sourceArg, {line, ch}) {
      line++;
      let 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
    let state = this.resolveState(limit(source, caret), caret);
    let propertyName = this.propertyName;
    let {line, ch} = caret;
    let 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.
     */
    let 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;
        let tokens = cssTokenizer(lineText);
        let found = false;
        let 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;
          }

          let forwState = this.resolveState(limitedSource, {
            line: line,
            ch: token.endOffset + ech
          });
          if (check(forwState)) {
            if (prevToken && prevToken.tokenType == "whitespace") {
              token = prevToken;
            }
            location = {
              line: 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.
     */
    let 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);
        }

        let 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 {
            let length = token.endOffset - token.startOffset;
            limitedSource = limitedSource.slice(0, -1 * length);
          }

          // Whitespace cannot change state.
          if (token.tokenType == "whitespace") {
            continue;
          }

          let backState = this.resolveState(limitedSource, {
            line: line,
            ch: token.startOffset
          });
          if (check(backState)) {
            if (tokens[i + 1] && tokens[i + 1].tokenType == "whitespace") {
              token = tokens[i + 1];
            }
            location = {
              line: 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.
      let 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.
      let 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: state,
        selector: selector,
        loc: {
          start: start,
          end: end
        }
      };
    } else if (state == CSS_STATES.property) {
      // A property can only be a single word and thus very easy to calculate.
      let tokens = cssTokenizer(sourceArray[line]);
      for (let 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: state,
            propertyName: token.text,
            selectors: this.selectors,
            loc: {
              start: {
                line: line,
                ch: token.startOffset
              },
              end: {
                line: 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
      let start = traverseBackwards(backState => backState != CSS_STATES.value, true);

      line = caret.line;
      limitedSource = limit(source, caret);
      let 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: state,
        propertyName: propertyName,
        selectors: this.selectors,
        value: value,
        loc: {
          start: start,
          end: end
        }
      };
    }
    return null;
  }
};

module.exports = CSSCompleter;
PK
!<l�O� � @chrome/devtools/modules/devtools/client/sourceeditor/debugger.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 DevToolsUtils = require("devtools/shared/DevToolsUtils");
const dbginfo = new WeakMap();

// These functions implement search within the debugger. Since
// search in the debugger is different from other components,
// we can't use search.js CodeMirror addon. This is a slightly
// modified version of that addon. Depends on searchcursor.js.

function SearchState() {
  this.posFrom = this.posTo = this.query = null;
}

function getSearchState(cm) {
  return cm.state.search || (cm.state.search = new SearchState());
}

function getSearchCursor(cm, query, pos) {
  // If the query string is all lowercase, do a case insensitive search.
  return cm.getSearchCursor(query, pos,
    typeof query == "string" && query == query.toLowerCase());
}

/**
 * If there's a saved search, selects the next results.
 * Otherwise, creates a new search and selects the first
 * result.
 */
function doSearch(ctx, rev, query) {
  let { cm } = ctx;
  let state = getSearchState(cm);

  if (state.query) {
    searchNext(ctx, rev);
    return;
  }

  cm.operation(function () {
    if (state.query) {
      return;
    }

    state.query = query;
    state.posFrom = state.posTo = { line: 0, ch: 0 };
    searchNext(ctx, rev);
  });
}

/**
 * Selects the next result of a saved search.
 */
function searchNext(ctx, rev) {
  let { cm, ed } = ctx;
  cm.operation(function () {
    let state = getSearchState(cm);
    let cursor = getSearchCursor(cm, state.query,
                                 rev ? state.posFrom : state.posTo);

    if (!cursor.find(rev)) {
      cursor = getSearchCursor(cm, state.query, rev ?
        { line: cm.lastLine(), ch: null } : { line: cm.firstLine(), ch: 0 });
      if (!cursor.find(rev)) {
        return;
      }
    }

    ed.alignLine(cursor.from().line, "center");
    cm.setSelection(cursor.from(), cursor.to());
    state.posFrom = cursor.from();
    state.posTo = cursor.to();
  });
}

/**
 * Clears the currently saved search.
 */
function clearSearch(cm) {
  let state = getSearchState(cm);

  if (!state.query) {
    return;
  }

  state.query = null;
}

// Exported functions

/**
 * This function is called whenever Editor is extended with functions
 * from this module. See Editor.extend for more info.
 */
function initialize(ctx) {
  let { ed } = ctx;

  dbginfo.set(ed, {
    breakpoints: {},
    debugLocation: null
  });
}

/**
 * True if editor has a visual breakpoint at that line, false
 * otherwise.
 */
function hasBreakpoint(ctx, line) {
  let { ed } = ctx;
  // In some rare occasions CodeMirror might not be properly initialized yet, so
  // return an exceptional value in that case.
  if (ed.lineInfo(line) === null) {
    return null;
  }
  let markers = ed.lineInfo(line).wrapClass;

  return markers != null &&
         markers.includes("breakpoint");
}

/**
 * Adds a visual breakpoint for a specified line. Third
 * parameter 'cond' can hold any object.
 *
 * After adding a breakpoint, this function makes Editor to
 * emit a breakpointAdded event.
 */
function addBreakpoint(ctx, line, cond) {
  if (hasBreakpoint(ctx, line)) {
    return null;
  }

  return new Promise(resolve => {
    function _addBreakpoint() {
      let { ed } = ctx;
      let meta = dbginfo.get(ed);
      let info = ed.lineInfo(line);

      // The line does not exist in the editor. This is harmless, the
      // architecture calling this assumes the editor will handle this
      // gracefully, and make sure breakpoints exist when they need to.
      if (!info) {
        return;
      }

      ed.addLineClass(line, "breakpoint");
      meta.breakpoints[line] = { condition: cond };

      // TODO(jwl): why is `info` null when breaking on page reload?
      info.handle.on("delete", function onDelete() {
        info.handle.off("delete", onDelete);
        meta.breakpoints[info.line] = null;
      });

      if (cond) {
        setBreakpointCondition(ctx, line);
      }
      ed.emit("breakpointAdded", line);
      resolve();
    }

    // If lineInfo() returns null, wait a tick to give the editor a chance to
    // initialize properly.
    if (ctx.ed.lineInfo(line) === null) {
      DevToolsUtils.executeSoon(() => _addBreakpoint());
    } else {
      _addBreakpoint();
    }
  });
}

/**
 * Helps reset the debugger's breakpoint state
 * - removes the breakpoints in the editor
 * - cleares the debugger's breakpoint state
 *
 * Note, does not *actually* remove a source's breakpoints.
 * The canonical state is kept in the app state.
 *
 */
function removeBreakpoints(ctx) {
  let { ed, cm } = ctx;

  let meta = dbginfo.get(ed);
  if (meta.breakpoints != null) {
    meta.breakpoints = {};
  }

  cm.doc.iter((line) => {
    // The hasBreakpoint is a slow operation: checks the line type, whether cm
    // is initialized and creates several new objects. Inlining the line's
    // wrapClass property check directly.
    if (line.wrapClass == null || !line.wrapClass.includes("breakpoint")) {
      return;
    }
    removeBreakpoint(ctx, line);
  });
}

/**
 * Removes a visual breakpoint from a specified line and
 * makes Editor emit a breakpointRemoved event.
 */
function removeBreakpoint(ctx, line) {
  if (!hasBreakpoint(ctx, line)) {
    return;
  }

  let { ed } = ctx;
  let meta = dbginfo.get(ed);
  let info = ed.lineInfo(line);
  let lineOrOffset = ed.getLineOrOffset(info.line);

  meta.breakpoints[lineOrOffset] = null;
  ed.removeLineClass(lineOrOffset, "breakpoint");
  ed.removeLineClass(lineOrOffset, "conditional");
  ed.emit("breakpointRemoved", line);
}

function moveBreakpoint(ctx, fromLine, toLine) {
  let { ed } = ctx;

  ed.removeBreakpoint(fromLine);
  ed.addBreakpoint(toLine);
}

function setBreakpointCondition(ctx, line) {
  let { ed } = ctx;
  let info = ed.lineInfo(line);

  // The line does not exist in the editor. This is harmless, the
  // architecture calling this assumes the editor will handle this
  // gracefully, and make sure breakpoints exist when they need to.
  if (!info) {
    return;
  }

  ed.addLineClass(line, "conditional");
}

function removeBreakpointCondition(ctx, line) {
  let { ed } = ctx;

  ed.removeLineClass(line, "conditional");
}

/**
 * Returns a list of all breakpoints in the current Editor.
 */
function getBreakpoints(ctx) {
  let { ed } = ctx;
  let meta = dbginfo.get(ed);

  return Object.keys(meta.breakpoints).reduce((acc, line) => {
    if (meta.breakpoints[line] != null) {
      acc.push({ line: line, condition: meta.breakpoints[line].condition });
    }
    return acc;
  }, []);
}

/**
 * Saves a debug location information and adds a visual anchor to
 * the breakpoints gutter. This is used by the debugger UI to
 * display the line on which the Debugger is currently paused.
 */
function setDebugLocation(ctx, lineOrOffset) {
  let { ed } = ctx;
  let meta = dbginfo.get(ed);
  let line = ed.getLineOrOffset(lineOrOffset);

  clearDebugLocation(ctx);

  meta.debugLocation = line;
  ed.addLineClass(line, "debug-line");
}

/**
 * Returns a line number that corresponds to the current debug
 * location.
 */
function getDebugLocation(ctx) {
  let { ed } = ctx;
  let meta = dbginfo.get(ed);

  return meta.debugLocation;
}

/**
 * Clears the debug location. Clearing the debug location
 * also removes a visual anchor from the breakpoints gutter.
 */
function clearDebugLocation(ctx) {
  let { ed } = ctx;
  let meta = dbginfo.get(ed);

  if (meta.debugLocation != null) {
    ed.removeLineClass(meta.debugLocation, "debug-line");
    meta.debugLocation = null;
  }
}

/**
 * Starts a new search.
 */
function find(ctx, query) {
  clearSearch(ctx.cm);
  doSearch(ctx, false, query);
}

/**
 * Finds the next item based on the currently saved search.
 */
function findNext(ctx, query) {
  doSearch(ctx, false, query);
}

/**
 * Finds the previous item based on the currently saved search.
 */
function findPrev(ctx, query) {
  doSearch(ctx, true, query);
}

// Export functions

[
  initialize, hasBreakpoint, addBreakpoint, removeBreakpoint, moveBreakpoint,
  setBreakpointCondition, removeBreakpointCondition, getBreakpoints, removeBreakpoints,
  setDebugLocation, getDebugLocation, clearDebugLocation, find, findNext,
  findPrev
].forEach(func => {
  module.exports[func.name] = func;
});
PK
!<�fyz	z	Rchrome/devtools/modules/devtools/client/sourceeditor/editor-commands-controller.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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: function (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: function (cmd) {
      let 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: function (cmd) {
      let cm = ed.codeMirror;

      let 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: function () {}
  };
}

/**
 * Create and insert a commands controller for the provided SourceEditor instance.
 */
function insertCommandsController(sourceEditor) {
  let input = sourceEditor.codeMirror.getInputField();
  let controller = createController(sourceEditor);
  input.controllers.insertControllerAt(0, controller);
}

module.exports = { insertCommandsController };
PK
!<, [AS�S�>chrome/devtools/modules/devtools/client/sourceeditor/editor.js/* -*- indent-tabs-mode: nil; js-indent-level: 2; fill-column: 80 -*- */
/* vim:set ts=2 sw=2 sts=2 et tw=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 {
  EXPAND_TAB,
  TAB_SIZE,
  DETECT_INDENT,
  getIndentationFromIteration
} = require("devtools/shared/indentation");

const ENABLE_CODE_FOLDING = "devtools.editor.enableCodeFolding";
const KEYMAP = "devtools.editor.keymap";
const AUTO_CLOSE = "devtools.editor.autoclosebrackets";
const AUTOCOMPLETE = "devtools.editor.autocomplete";
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const VALID_KEYMAPS = new Set(["emacs", "vim", "sublime"]);

// 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;

// Match @Scratchpad/N:LINE[:COLUMN] or (LINE[:COLUMN]) anywhere at an end of
// line in text selection.
const RE_SCRATCHPAD_ERROR = /(?:@Scratchpad\/\d+:|\()(\d+):?(\d+)?(?:\)|\n)/;
const RE_JUMP_TO_LINE = /^(\d+):?(\d+)?/;

const Services = require("Services");
const events = require("devtools/shared/old-event-emitter");
const { PrefObserver } = require("devtools/client/shared/prefs");
const { getClientCssProperties } = require("devtools/shared/fronts/css-properties");
const KeyShortcuts = require("devtools/client/shared/key-shortcuts");

const {LocalizationHelper} = require("devtools/shared/l10n");
const L10N = new LocalizationHelper("devtools/client/locales/sourceeditor.properties");

const {
  getWasmText,
  getWasmLineNumberFormatter,
  isWasm,
  lineToWasmOffset,
  wasmOffsetToLine,
} = require("./wasm");

const { OS } = Services.appinfo;

// CM_SCRIPTS and CM_IFRAME represent the HTML and JavaScript that is
// injected into an iframe in order to initialize a CodeMirror instance.

const CM_SCRIPTS = [
  "chrome://devtools/content/sourceeditor/codemirror/codemirror.bundle.js",
];

const CM_IFRAME = "chrome://devtools/content/sourceeditor/codemirror/cmiframe.html";

const CM_MAPPING = [
  "focus",
  "hasFocus",
  "lineCount",
  "somethingSelected",
  "getCursor",
  "setSelection",
  "getSelection",
  "replaceSelection",
  "extendSelection",
  "undo",
  "redo",
  "clearHistory",
  "openDialog",
  "refresh",
  "getScrollInfo",
  "getViewport"
];

const editors = new WeakMap();

Editor.modes = {
  text: { name: "text" },
  html: { name: "htmlmixed" },
  css: { name: "css" },
  wasm: { name: "wasm" },
  js: { name: "javascript" },
  vs: { name: "x-shader/x-vertex" },
  fs: { name: "x-shader/x-fragment" }
};

/**
 * 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
 */
function Editor(config) {
  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 = {
    value: "",
    mode: Editor.modes.text,
    indentUnit: tabSize,
    tabSize: tabSize,
    contextMenu: null,
    matchBrackets: true,
    extraKeys: {},
    indentWithTabs: useTabs,
    inputStyle: "textarea",
    styleActiveLine: true,
    autoCloseBrackets: "()[]{}''\"\"``",
    autoCloseEnabled: useAutoClose,
    theme: "mozilla",
    themeSwitching: true,
    autocomplete: false,
    autocompleteOpts: {}
  };

  // 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;

  // 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 (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");
  };

  // Allow add-ons to inject scripts for their editor instances
  if (!this.config.externalScripts) {
    this.config.externalScripts = [];
  }

  if (this.config.cssProperties) {
    // Ensure that autocompletion has cssProperties if it's passed in via the options.
    this.config.autocompleteOpts.cssProperties = this.config.cssProperties;
  } else {
    // Use a static client-side database of CSS values if none is provided.
    this.config.cssProperties = getClientCssProperties();
  }

  events.decorate(this);
}

Editor.prototype = {
  container: null,
  version: null,
  config: null,
  Doc: null,

  /**
   * 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);
  },

  /**
   * Appends the current Editor instance to the element specified by
   * 'el'. You can also provide your won 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: function (el, env) {
    return new Promise(resolve => {
      let cm = editors.get(this);

      if (!env) {
        env = el.ownerDocument.createElementNS(el.namespaceURI, "iframe");

        if (el.namespaceURI === XUL_NS) {
          env.flex = 1;
        }
      }

      if (cm) {
        throw new Error("You can append an editor only once.");
      }

      let onLoad = () => {
        let win = env.contentWindow.wrappedJSObject;

        if (!this.config.themeSwitching) {
          win.document.documentElement.setAttribute("force-theme", "light");
        }

        Services.scriptloader.loadSubScript(
          "chrome://devtools/content/shared/theme-switching.js",
          win, "utf8"
        );
        this.container = env;
        this._setup(win.document.body, el.ownerDocument);
        env.removeEventListener("load", onLoad, true);

        resolve();
      };

      env.addEventListener("load", onLoad, true);
      env.setAttribute("src", CM_IFRAME);
      el.appendChild(env);

      this.once("destroy", () => el.removeChild(env));
    });
  },

  appendToLocalElement: function (el) {
    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: function (el, doc) {
    doc = doc || el.ownerDocument;
    let win = el.ownerDocument.defaultView;

    let scriptsToInject = CM_SCRIPTS.concat(this.config.externalScripts);
    scriptsToInject.forEach(url => {
      if (url.startsWith("chrome://")) {
        Services.scriptloader.loadSubScript(url, win, "utf8");
      }
    });

    // 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);

    let cssSpec = win.CodeMirror.resolveMode("text/css");
    cssSpec.propertyKeywords = propertyKeywords;
    cssSpec.colorKeywords = colorKeywords;
    cssSpec.valueKeywords = valueKeywords;
    win.CodeMirror.defineMIME("text/css", cssSpec);

    let 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).

    let 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 = doc.getElementById(this.config.contextMenu);
      }

      this.emit("popupOpen", ev, popup);
      popup.openPopupAtScreen(ev.screenX, ev.screenY, true);
    });

    cm.on("focus", () => this.emit("focus"));
    cm.on("scroll", () => this.emit("scroll"));
    cm.on("change", () => {
      this.emit("change");
      if (!this._lastDirty) {
        this._lastDirty = true;
        this.emit("dirty-change");
      }
    });
    cm.on("cursorActivity", () => this.emit("cursorActivity"));

    cm.on("gutterClick", (cmArg, line, gutter, ev) => {
      let lineOrOffset = !this.isWasm ? line : this.lineToWasmOffset(line);
      let head = { line: line, ch: 0 };
      let tail = { line: line, ch: this.getText(lineOrOffset).length };

      // Shift-click on a gutter selects the whole line.
      if (ev.shiftKey) {
        cmArg.setSelection(head, tail);
        return;
      }

      this.emit("gutterClick", lineOrOffset, ev.button);
    });

    win.CodeMirror.defineExtension("l10n", (name) => {
      return L10N.getStr(name);
    });

    this._initShortcuts(win);

    editors.set(this, cm);

    this.reloadPreferences = this.reloadPreferences.bind(this);
    this._prefObserver = new PrefObserver("devtools.editor.");
    this._prefObserver.on(TAB_SIZE, this.reloadPreferences);
    this._prefObserver.on(EXPAND_TAB, this.reloadPreferences);
    this._prefObserver.on(KEYMAP, 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();

    win.editor = this;
    let editorReadyEvent = new win.CustomEvent("editorReady");
    win.dispatchEvent(editorReadyEvent);
  },

  /**
   * Returns a boolean indicating whether the editor is ready to
   * use. Use appendTo(el).then(() => {}) for most cases
   */
  isAppended: function () {
    return editors.has(this);
  },

  /**
   * Returns the currently active highlighting mode.
   * See Editor.modes for the list of all suppoert modes.
   */
  getMode: function () {
    return this.getOption("mode");
  },

  /**
   * Loads a script into editor's containing window.
   */
  loadScript: function (url) {
    if (!this.container) {
      throw new Error("Can't load a script until the editor is loaded.");
    }
    let win = this.container.contentWindow.wrappedJSObject;
    Services.scriptloader.loadSubScript(url, win, "utf8");
  },

  /**
   * Creates a CodeMirror Document
   * @returns CodeMirror.Doc
   */
  createDocument: function () {
    return new this.Doc("");
  },

  /**
   * Replaces the current document with a new source document
   */
  replaceDocument: function (doc) {
    let cm = editors.get(this);
    cm.swapDoc(doc);
    if (!Services.prefs.getBoolPref("devtools.debugger.new-debugger-frontend")) {
      this._updateLineNumberFormat();
    }
  },

  /**
   * Changes the value of a currently used highlighting mode.
   * See Editor.modes for the list of all supported modes.
   */
  setMode: function (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 scratchpad and styleeditor.
   */
  insertCommandsController: function () {
    const { insertCommandsController } =
      require("devtools/client/sourceeditor/editor-commands-controller");
    insertCommandsController(this);
  },

  /**
   * Returns text from the text area. If line argument is provided
   * the method returns only that line.
   */
  getText: function (line) {
    let cm = editors.get(this);

    if (line == null) {
      return cm.getValue();
    }

    let info = this.lineInfo(line);
    return info ? info.text : "";
  },

  getDoc: function () {
    let cm = editors.get(this);
    return cm.getDoc();
  },

  get isWasm() {
    return isWasm(this.getDoc());
  },

  wasmOffsetToLine: function (offset) {
    return wasmOffsetToLine(this.getDoc(), offset);
  },

  lineToWasmOffset: function (number) {
    return lineToWasmOffset(this.getDoc(), number);
  },

  toLineIfWasmOffset: function (maybeOffset) {
    if (typeof maybeOffset !== "number" || !this.isWasm) {
      return maybeOffset;
    }
    return this.wasmOffsetToLine(maybeOffset);
  },

  lineInfo: function (lineOrOffset) {
    let line = this.toLineIfWasmOffset(lineOrOffset);
    if (line == undefined) {
      return null;
    }
    let cm = editors.get(this);
    return cm.lineInfo(line);
  },

  getLineOrOffset: function (line) {
    return this.isWasm ? this.lineToWasmOffset(line) : line;
  },

  _updateLineNumberFormat: function () {
    let cm = editors.get(this);
    if (this.isWasm) {
      let formatter = getWasmLineNumberFormatter(this.getDoc());
      cm.setOption("lineNumberFormatter", formatter);
    } else {
      cm.setOption("lineNumberFormatter", (number) => number);
    }
  },

  /**
   * Replaces whatever is in the text area with the contents of
   * the 'value' argument.
   */
  setText: function (value) {
    let cm = editors.get(this);

    if (typeof value !== "string" && "binary" in value) { // wasm?
      // binary does not survive as Uint8Array, converting from string
      let binary = value.binary;
      let data = new Uint8Array(binary.length);
      for (let i = 0; i < data.length; i++) {
        data[i] = binary.charCodeAt(i);
      }
      let { lines, done } = 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 (!Services.prefs.getBoolPref("devtools.debugger.new-debugger-frontend")) {
      this._updateLineNumberFormat();
    }

    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: function () {
    // Restore the saved autoCloseBrackets value if it is preffed on.
    let useAutoClose = Services.prefs.getBoolPref(AUTO_CLOSE);
    this.setOption("autoCloseBrackets",
      useAutoClose ? this.config.autoCloseBracketsSaved : false);

    // If alternative keymap is provided, use it.
    const keyMap = Services.prefs.getCharPref(KEYMAP);
    if (VALID_KEYMAPS.has(keyMap)) {
      this.setOption("keyMap", keyMap);
    } else {
      this.setOption("keyMap", "default");
    }
    this.updateCodeFoldingGutter();

    this.resetIndentUnit();
    this.setupAutoCompletion();
  },

  /**
   * Sets the editor's indentation based on the current prefs and
   * re-detect indentation if we should.
   */
  resetIndentUnit: function () {
    let cm = editors.get(this);

    let iterFn = function (start, end, callback) {
      cm.eachLine(start, end, (line) => {
        return callback(line.text);
      });
    };

    let {indentUnit, indentWithTabs} = getIndentationFromIteration(iterFn);

    cm.setOption("tabSize", indentUnit);
    cm.setOption("indentUnit", indentUnit);
    cm.setOption("indentWithTabs", indentWithTabs);
  },

  /**
   * 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: function (value, from, to) {
    let cm = editors.get(this);

    if (!from) {
      this.setText(value);
      return;
    }

    if (!to) {
      let 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: function (value, at) {
    let cm = editors.get(this);
    cm.replaceRange(value, at, at);
  },

  /**
   * Deselects contents of the text area.
   */
  dropSelection: function () {
    if (!this.somethingSelected()) {
      return;
    }

    this.setCursor(this.getCursor());
  },

  /**
   * Returns true if there is more than one selection in the editor.
   */
  hasMultipleSelections: function () {
    let cm = editors.get(this);
    return cm.listSelections().length > 1;
  },

  /**
   * Gets the first visible line number in the editor.
   */
  getFirstVisibleLine: function () {
    let cm = editors.get(this);
    return cm.lineAtHeight(0, "local");
  },

  /**
   * Scrolls the view such that the given line number is the first visible line.
   */
  setFirstVisibleLine: function (line) {
    let cm = editors.get(this);
    let { top } = cm.charCoords({line: 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: function ({line, ch}, align) {
    let cm = editors.get(this);
    this.alignLine(line, align);
    cm.setCursor({line: line, ch: 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: function (line, align) {
    let cm = editors.get(this);
    let from = cm.lineAtHeight(0, "page");
    let to = cm.lineAtHeight(cm.getWrapperElement().clientHeight, "page");
    let linesVisible = to - from;
    let 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.
    let 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: function (line, gutterName, markerClass) {
    let 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: function (line, gutterName, markerClass) {
    let cm = editors.get(this);
    let info = this.lineInfo(line);
    if (!info) {
      return;
    }

    let 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: function (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: function (line, gutterName, markerClass, content) {
    let cm = editors.get(this);
    let info = this.lineInfo(line);
    if (!info) {
      return;
    }

    let 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: function (line, gutterName) {
    let cm = editors.get(this);
    let info = this.lineInfo(line);
    if (!info) {
      return;
    }

    cm.setGutterMarker(info.line, gutterName, null);
  },

  getMarker: function (line, gutterName) {
    let info = this.lineInfo(line);
    if (!info) {
      return null;
    }

    let gutterMarkers = info.gutterMarkers;
    if (!gutterMarkers) {
      return null;
    }

    return gutterMarkers[gutterName];
  },

  /**
   * Removes all gutter markers in the gutter with the given name.
   */
  removeAllMarkers: function (gutterName) {
    let 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: function (line, gutterName, markerClass, eventsArg, data) {
    if (!this.hasMarker(line, gutterName, markerClass)) {
      return;
    }

    let cm = editors.get(this);
    let marker = cm.lineInfo(line).gutterMarkers[gutterName];

    for (let name in eventsArg) {
      let listener = eventsArg[name].bind(this, line, marker, data);
      marker.addEventListener(name, listener);
    }
  },

  /**
   * Returns whether a line is decorated using the specified class name.
   */
  hasLineClass: function (line, className) {
    let 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: function (lineOrOffset, className) {
    let cm = editors.get(this);
    let line = this.toLineIfWasmOffset(lineOrOffset);
    cm.addLineClass(line, "wrap", className);
  },

  /**
   * The reverse of addLineClass.
   */
  removeLineClass: function (lineOrOffset, className) {
    let cm = editors.get(this);
    let 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: function (from, to, className = "marked-text") {
    let cm = editors.get(this);
    let text = cm.getRange(from, to);
    let span = cm.getWrapperElement().ownerDocument.createElement("span");
    span.className = className;
    span.textContent = text;

    let 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: function (...args) {
    let cm = editors.get(this);
    let 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: function (...args) {
    let cm = editors.get(this);
    let 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: function ({left, top}) {
    let cm = editors.get(this);
    return cm.coordsChar({ left: left, top: top });
  },

  /**
   * The reverse of getPositionFromCoords. Similarly, returns a {left, top}
   * object that corresponds to the specified line and character number.
   */
  getCoordsFromPosition: function ({line, ch}) {
    let cm = editors.get(this);
    return cm.charCoords({ line: ~~line, ch: ~~ch });
  },

  /**
   * Returns true if there's something to undo and false otherwise.
   */
  canUndo: function () {
    let cm = editors.get(this);
    return cm.historySize().undo > 0;
  },

  /**
   * Returns true if there's something to redo and false otherwise.
   */
  canRedo: function () {
    let cm = editors.get(this);
    return cm.historySize().redo > 0;
  },

  /**
   * Marks the contents as clean and returns the current
   * version number.
   */
  setClean: function () {
    let 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: function () {
    let 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: function () {
    let doc = editors.get(this).getWrapperElement().ownerDocument;
    let div = doc.createElement("div");
    let inp = doc.createElement("input");
    let txt = doc.createTextNode(L10N.getStr("gotoLineCmd.promptTitle"));

    inp.type = "text";
    inp.style.width = "10em";
    inp.style.marginInlineStart = "1em";

    div.appendChild(txt);
    div.appendChild(inp);

    if (!this.hasMultipleSelections()) {
      let cm = editors.get(this);
      let sel = cm.getSelection();
      // Scratchpad inserts and selects a comment after an error happens:
      // "@Scratchpad/1:10:2". Parse this to get the line and column.
      // In the string above this is line 10, column 2.
      let match = sel.match(RE_SCRATCHPAD_ERROR);
      if (match) {
        let [, line, column ] = match;
        inp.value = column ? line + ":" + column : line;
        inp.selectionStart = inp.selectionEnd = inp.value.length;
      }
    }

    this.openDialog(div, (line) => {
      // Handle LINE:COLUMN as well as LINE
      let match = line.toString().match(RE_JUMP_TO_LINE);
      if (match) {
        let [, 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: function () {
    let cm = editors.get(this);
    let start = cm.getCursor("start");
    let 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: function () {
    let cm = editors.get(this);
    let start = cm.getCursor("start");
    let 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: function (node, isReplaceAll) {
    let cm = editors.get(this);
    let isInput = node.tagName === "INPUT";
    let isSearchInput = isInput && node.type === "search";
    // replace box is a different input instance than search, and it is
    // located in a code mirror dialog
    let 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: function (node, isFindPrev) {
    let cm = editors.get(this);
    let isInput = node.tagName === "INPUT";
    let isSearchInput = isInput && node.type === "search";
    if (!isSearchInput) {
      return;
    }
    let 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: function () {
    let cm = editors.get(this);
    let el = cm.getWrapperElement();
    let win = el.ownerDocument.defaultView;

    return parseInt(win.getComputedStyle(el).getPropertyValue("font-size"), 10);
  },

  /**
   * Sets font size for the editor area.
   */
  setFontSize: function (size) {
    let 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: function (o, v) {
    let 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: function (o) {
    let 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: function () {
    // The autocomplete module will overwrite this.initializeAutoCompletion
    // with a mode specific autocompletion handler.
    if (!this.initializeAutoCompletion) {
      this.extend(require("./autocomplete"));
    }

    if (this.config.autocomplete && Services.prefs.getBoolPref(AUTOCOMPLETE)) {
      this.initializeAutoCompletion(this.config.autocompleteOpts);
    } else {
      this.destroyAutoCompletion();
    }
  },

  /**
   * 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: function (funcs) {
    Object.keys(funcs).forEach(name => {
      let cm = editors.get(this);
      let ctx = { ed: this, cm: cm, Editor: Editor};

      if (name === "initialize") {
        funcs[name](ctx);
        return;
      }

      this[name] = funcs[name].bind(null, ctx);
    });
  },

  destroy: function () {
    this.container = null;
    this.config = null;
    this.version = null;

    if (this._prefObserver) {
      this._prefObserver.off(TAB_SIZE, this.reloadPreferences);
      this._prefObserver.off(EXPAND_TAB, this.reloadPreferences);
      this._prefObserver.off(KEYMAP, 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.
    let cm = editors.get(this);
    if (cm && cm.doc) {
      cm.doc.cm = null;
    }

    this.emit("destroy");
  },

  updateCodeFoldingGutter: function () {
    let shouldFoldGutter = this.config.enableCodeFolding;
    let foldGutterIndex = this.config.gutters.indexOf("CodeMirror-foldgutter");
    let 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) {
        let 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) {
        let gutters = this.config.gutters.slice();
        gutters.splice(foldGutterIndex, 1);
        this.setOption("gutters", gutters);
      }

      this.setOption("foldGutter", false);
    }
  },

  /**
   * Register all key shortcuts.
   */
  _initShortcuts: function (win) {
    let shortcuts = new KeyShortcuts({
      window: win
    });
    this._onShortcut = this._onShortcut.bind(this);
    let 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 => {
      let key = L10N.getStr(name);
      shortcuts.on(key, event => this._onShortcut(name, event));
    });
  },
    /**
   * Key shortcut listener.
   */
  _onShortcut: function (name, event) {
    if (!this._isInputOrTextarea(event.target)) {
      return;
    }
    let 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: function (element) {
    let 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) {
    let cm = editors.get(this);
    return cm[name].apply(cm, args);
  };
});

// 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
 */
Editor.accel = function (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.
 */
Editor.keyFor = function (cmd, opts = { noaccel: false }) {
  let key = L10N.getStr(cmd + ".commandkey");
  return opts.noaccel ? key : Editor.accel(key);
};

/**
 * 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) {
    let keys = {};
    for (let i = 0; i < array.length; ++i) {
      keys[array[i]] = true;
    }
    return keys;
  }

  let propertyKeywords = cssProperties.getNames();
  let colorKeywords = {};
  let 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: colorKeywords,
    valueKeywords: valueKeywords
  };
}

module.exports = Editor;
PK
!<y��̲G�GDchrome/devtools/modules/devtools/client/sourceeditor/tern/browser.jsmodule.exports = {
  "!name": "browser",
  "location": {
    "assign": {
      "!type": "fn(url: string)",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.location",
      "!doc": "Load the document at the provided URL."
    },
    "replace": {
      "!type": "fn(url: string)",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.location",
      "!doc": "Replace the current document with the one at the provided URL. The difference from the assign() method is that after using replace() the current page will not be saved in session history, meaning the user won't be able to use the Back button to navigate to it."
    },
    "reload": {
      "!type": "fn()",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.location",
      "!doc": "Reload the document from the current URL. forceget is a boolean, which, when it is true, causes the page to always be reloaded from the server. If it is false or not specified, the browser may reload the page from its cache."
    },
    "origin": {
      "!type": "string",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.location",
      "!doc": "The origin of the URL."
    },
    "hash": {
      "!type": "string",
      "!url": "https://developer.mthat follows the # symbol, including the # symbol."
    },
    "search": {
      "!type": "string",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.location",
      "!doc": "The part of the URL that follows the ? symbol, including the ? symbol."
    },
    "pathname": {
      "!type": "string",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.location",
      "!doc": "The path (relative to the host)."
    },
    "port": {
      "!type": "string",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.location",
      "!doc": "The port number of the URL."
    },
    "hostname": {
      "!type": "string",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.location",
      "!doc": "The host name (without the port number or square brackets)."
    },
    "host": {
      "!type": "string",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.location",
      "!doc": "The host name and port number."
    },
    "protocol": {
      "!type": "string",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.location",
      "!doc": "The protocol of the URL."
    },
    "href": {
      "!type": "string",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.location",
      "!doc": "The entire URL."
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.location",
    "!doc": "Returns a location object with information about the current location of the document. Assigning to the location property changes the current page to the new address."
  },
  "Node": {
    "!type": "fn()",
    "prototype": {
      "parentElement": {
        "!type": "+Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.parentElement",
        "!doc": "Returns the DOM node's parent Element, or null if the node either has no parent, or its parent isn't a DOM Element."
      },
      "textContent": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.textContent",
        "!doc": "Gets or sets the text content of a node and its descendants."
      },
      "baseURI": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.baseURI",
        "!doc": "The absolute base URI of a node or null if unable to obtain an absolute URI."
      },
      "localName": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.localName",
        "!doc": "Returns the local part of the qualified name of this node."
      },
      "prefix": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.prefix",
        "!doc": "Returns the namespace prefix of the specified node, or null if no prefix is specified. This property is read only."
      },
      "namespaceURI": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.namespaceURI",
        "!doc": "The namespace URI of the node, or null if the node is not in a namespace (read-only). When the node is a document, it returns the XML namespace for the current document."
      },
      "ownerDocument": {
        "!type": "+Document",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.ownerDocument",
        "!doc": "The ownerDocument property returns the top-level document object for this node."
      },
      "attributes": {
        "!type": "+NamedNodeMap",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.attributes",
        "!doc": "A collection of all attribute nodes registered to the specified node. It is a NamedNodeMap,not an Array, so it has no Array methods and the Attr nodes' indexes may differ among browsers."
      },
      "nextSibling": {
        "!type": "+Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.nextSibling",
        "!doc": "Returns the node immediately following the specified one in its parent's childNodes list, or null if the specified node is the last node in that list."
      },
      "previousSibling": {
        "!type": "+Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.previousSibling",
        "!doc": "Returns the node immediately preceding the specified one in its parent's childNodes list, null if the specified node is the first in that list."
      },
      "lastChild": {
        "!type": "+Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.lastChild",
        "!doc": "Returns the last child of a node."
      },
      "firstChild": {
        "!type": "+Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.firstChild",
        "!doc": "Returns the node's first child in the tree, or null if the node is childless. If the node is a Document, it returns the first node in the list of its direct children."
      },
      "childNodes": {
        "!type": "+NodeList",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.childNodes",
        "!doc": "Returns a collection of child nodes of the given element."
      },
      "parentNode": {
        "!type": "+Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.parentNode",
        "!doc": "Returns the parent of the specified node in the DOM tree."
      },
      "nodeType": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.nodeType",
        "!doc": "Returns an integer code representing the type of the node."
      },
      "nodeValue": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.nodeValue",
        "!doc": "Returns or sets the value of the current node."
      },
      "nodeName": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.nodeName",
        "!doc": "Returns the name of the current node as a string."
      },
      "tagName": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.nodeName",
        "!doc": "Returns the name of the current node as a string."
      },
      "insertBefore": {
        "!type": "fn(newElt: +Element, before: +Element) -> +Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.insertBefore",
        "!doc": "Inserts the specified node before a reference element as a child of the current node."
      },
      "replaceChild": {
        "!type": "fn(newElt: +Element, oldElt: +Element) -> +Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.replaceChild",
        "!doc": "Replaces one child node of the specified element with another."
      },
      "removeChild": {
        "!type": "fn(oldElt: +Element) -> +Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.removeChild",
        "!doc": "Removes a child node from the DOM. Returns removed node."
      },
      "appendChild": {
        "!type": "fn(newElt: +Element) -> +Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.appendChild",
        "!doc": "Adds a node to the end of the list of children of a specified parent node. If the node already exists it is removed from current parent node, then added to new parent node."
      },
      "hasChildNodes": {
        "!type": "fn() -> bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.hasChildNodes",
        "!doc": "Returns a Boolean value indicating whether the current Node has child nodes or not."
      },
      "cloneNode": {
        "!type": "fn(deep: bool) -> +Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.cloneNode",
        "!doc": "Returns a duplicate of the node on which this method was called."
      },
      "normalize": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.normalize",
        "!doc": "Puts the specified node and all of its subtree into a \"normalized\" form. In a normalized subtree, no text nodes in the subtree are empty and there are no adjacent text nodes."
      },
      "isSupported": {
        "!type": "fn(features: string, version: number) -> bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.isSupported",
        "!doc": "Tests whether the DOM implementation implements a specific feature and that feature is supported by this node."
      },
      "hasAttributes": {
        "!type": "fn() -> bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.hasAttributes",
        "!doc": "Returns a boolean value of true or false, indicating if the current element has any attributes or not."
      },
      "lookupPrefix": {
        "!type": "fn(uri: string) -> string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.lookupPrefix",
        "!doc": "Returns the prefix for a given namespaceURI if present, and null if not. When multiple prefixes are possible, the result is implementation-dependent."
      },
      "isDefaultNamespace": {
        "!type": "fn(uri: string) -> bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.isDefaultNamespace",
        "!doc": "Accepts a namespace URI as an argument and returns true if the namespace is the default namespace on the given node or false if not."
      },
      "lookupNamespaceURI": {
        "!type": "fn(uri: string) -> string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.lookupNamespaceURI",
        "!doc": "Takes a prefix and returns the namespaceURI associated with it on the given node if found (and null if not). Supplying null for the prefix will return the default namespace."
      },
      "addEventListener": {
        "!type": "fn(type: string, listener: fn(e: +Event), capture: bool)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/EventTarget.addEventListener",
        "!doc": "Registers a single event listener on a single target. The event target may be a single element in a document, the document itself, a window, or an XMLHttpRequest."
      },
      "removeEventListener": {
        "!type": "fn(type: string, listener: fn(), capture: bool)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/EventTarget.removeEventListener",
        "!doc": "Allows the removal of event listeners from the event target."
      },
      "isSameNode": {
        "!type": "fn(other: +Node) -> bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.isSameNode",
        "!doc": "Tests whether two nodes are the same, that is they reference the same object."
      },
      "isEqualNode": {
        "!type": "fn(other: +Node) -> bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.isEqualNode",
        "!doc": "Tests whether two nodes are equal."
      },
      "compareDocumentPosition": {
        "!type": "fn(other: +Node) -> number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.compareDocumentPosition",
        "!doc": "Compares the position of the current node against another node in any other document."
      },
      "contains": {
        "!type": "fn(other: +Node) -> bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Node.contains",
        "!doc": "Indicates whether a node is a descendent of a given node."
      },
      "dispatchEvent": {
        "!type": "fn(event: +Event) -> bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/EventTarget.dispatchEvent",
        "!doc": "Dispatches an event into the event system. The event is subject to the same capturing and bubbling behavior as directly dispatched events."
      },
      "ELEMENT_NODE": "number",
      "ATTRIBUTE_NODE": "number",
      "TEXT_NODE": "number",
      "CDATA_SECTION_NODE": "number",
      "ENTITY_REFERENCE_NODE": "number",
      "ENTITY_NODE": "number",
      "PROCESSING_INSTRUCTION_NODE": "number",
      "COMMENT_NODE": "number",
      "DOCUMENT_NODE": "number",
      "DOCUMENT_TYPE_NODE": "number",
      "DOCUMENT_FRAGMENT_NODE": "number",
      "NOTATION_NODE": "number",
      "DOCUMENT_POSITION_DISCONNECTED": "number",
      "DOCUMENT_POSITION_PRECEDING": "number",
      "DOCUMENT_POSITION_FOLLOWING": "number",
      "DOCUMENT_POSITION_CONTAINS": "number",
      "DOCUMENT_POSITION_CONTAINED_BY": "number",
      "DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC": "number"
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/Node",
    "!doc": "A Node is an interface from which a number of DOM types inherit, and allows these various types to be treated (or tested) similarly."
  },
  "Element": {
    "!type": "fn()",
    "prototype": {
      "!proto": "Node.prototype",
      "getAttribute": {
        "!type": "fn(name: string) -> string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.getAttribute",
        "!doc": "Returns the value of the named attribute on the specified element. If the named attribute does not exist, the value returned will either be null or \"\" (the empty string)."
      },
      "setAttribute": {
        "!type": "fn(name: string, value: string)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.setAttribute",
        "!doc": "Adds a new attribute or changes the value of an existing attribute on the specified element."
      },
      "removeAttribute": {
        "!type": "fn(name: string)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.removeAttribute",
        "!doc": "Removes an attribute from the specified element."
      },
      "getAttributeNode": {
        "!type": "fn(name: string) -> +Attr",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.getAttributeNode",
        "!doc": "Returns the specified attribute of the specified element, as an Attr node."
      },
      "getElementsByTagName": {
        "!type": "fn(tagName: string) -> +NodeList",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.getElementsByTagName",
        "!doc": "Returns a list of elements with the given tag name. The subtree underneath the specified element is searched, excluding the element itself. The returned list is live, meaning that it updates itself with the DOM tree automatically. Consequently, there is no need to call several times element.getElementsByTagName with the same element and arguments."
      },
      "getElementsByTagNameNS": {
        "!type": "fn(ns: string, tagName: string) -> +NodeList",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.getElementsByTagNameNS",
        "!doc": "Returns a list of elements with the given tag name belonging to the given namespace."
      },
      "getAttributeNS": {
        "!type": "fn(ns: string, name: string) -> string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.getAttributeNS",
        "!doc": "Returns the string value of the attribute with the specified namespace and name. If the named attribute does not exist, the value returned will either be null or \"\" (the empty string)."
      },
      "setAttributeNS": {
        "!type": "fn(ns: string, name: string, value: string)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.setAttributeNS",
        "!doc": "Adds a new attribute or changes the value of an attribute with the given namespace and name."
      },
      "removeAttributeNS": {
        "!type": "fn(ns: string, name: string)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.removeAttributeNS",
        "!doc": "removeAttributeNS removes the specified attribute from an element."
      },
      "getAttributeNodeNS": {
        "!type": "fn(ns: string, name: string) -> +Attr",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.getAttributeNodeNS",
        "!doc": "Returns the Attr node for the attribute with the given namespace and name."
      },
      "hasAttribute": {
        "!type": "fn(name: string) -> bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.hasAttribute",
        "!doc": "hasAttribute returns a boolean value indicating whether the specified element has the specified attribute or not."
      },
      "hasAttributeNS": {
        "!type": "fn(ns: string, name: string) -> bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.hasAttributeNS",
        "!doc": "hasAttributeNS returns a boolean value indicating whether the current element has the specified attribute."
      },
      "focus": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.focus",
        "!doc": "Sets focus on the specified element, if it can be focused."
      },
      "blur": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.blur",
        "!doc": "The blur method removes keyboard focus from the current element."
      },
      "scrollIntoView": {
        "!type": "fn(top: bool)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.scrollIntoView",
        "!doc": "The scrollIntoView() method scrolls the element into view."
      },
      "scrollByLines": {
        "!type": "fn(lines: number)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/window.scrollByLines",
        "!doc": "Scrolls the document by the given number of lines."
      },
      "scrollByPages": {
        "!type": "fn(pages: number)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/window.scrollByPages",
        "!doc": "Scrolls the current document by the specified number of pages."
      },
      "getElementsByClassName": {
        "!type": "fn(name: string) -> +NodeList",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.getElementsByClassName",
        "!doc": "Returns a set of elements which have all the given class names. When called on the document object, the complete document is searched, including the root node. You may also call getElementsByClassName on any element; it will return only elements which are descendants of the specified root element with the given class names."
      },
      "querySelector": {
        "!type": "fn(selectors: string) -> +Node",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Element.querySelector",
        "!doc": "Returns the first element that is a descendent of the element on which it is invoked that matches the specified group of selectors."
      },
      "querySelectorAll": {
        "!type": "fn(selectors: string) -> +NodeList",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Element.querySelectorAll",
        "!doc": "Returns a non-live NodeList of all elements descended from the element on which it is invoked that match the specified group of CSS selectors."
      },
      "getClientRects": {
        "!type": "fn() -> [+ClientRect]",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.getClientRects",
        "!doc": "Returns a collection of rectangles that indicate the bounding rectangles for each box in a client."
      },
      "getBoundingClientRect": {
        "!type": "fn() -> +ClientRect",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.getBoundingClientRect",
        "!doc": "Returns a text rectangle object that encloses a group of text rectangles."
      },
      "setAttributeNode": {
        "!type": "fn(attr: +Attr) -> +Attr",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.setAttributeNode",
        "!doc": "Adds a new Attr node to the specified element."
      },
      "removeAttributeNode": {
        "!type": "fn(attr: +Attr) -> +Attr",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.removeAttributeNode",
        "!doc": "Removes the specified attribute from the current element."
      },
      "setAttributeNodeNS": {
        "!type": "fn(attr: +Attr) -> +Attr",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.setAttributeNodeNS",
        "!doc": "Adds a new namespaced attribute node to an element."
      },
      "insertAdjacentHTML": {
        "!type": "fn(position: string, text: string)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.insertAdjacentHTML",
        "!doc": "Parses the specified text as HTML or XML and inserts the resulting nodes into the DOM tree at a specified position. It does not reparse the element it is being used on and thus it does not corrupt the existing elements inside the element. This, and avoiding the extra step of serialization make it much faster than direct innerHTML manipulation."
      },
      "children": {
        "!type": "+HTMLCollection",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Element.children",
        "!doc": "Returns a collection of child elements of the given element."
      },
      "childElementCount": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Element.childElementCount",
        "!doc": "Returns the number of child elements of the given element."
      },
      "className": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.className",
        "!doc": "Gets and sets the value of the class attribute of the specified element."
      },
      "style": {
        "cssText": "string",
        "alignmentBaseline": "string",
        "background": "string",
        "backgroundAttachment": "string",
        "backgroundClip": "string",
        "backgroundColor": "string",
        "backgroundImage": "string",
        "backgroundOrigin": "string",
        "backgroundPosition": "string",
        "backgroundPositionX": "string",
        "backgroundPositionY": "string",
        "backgroundRepeat": "string",
        "backgroundRepeatX": "string",
        "backgroundRepeatY": "string",
        "backgroundSize": "string",
        "baselineShift": "string",
        "border": "string",
        "borderBottom": "string",
        "borderBottomColor": "string",
        "borderBottomLeftRadius": "string",
        "borderBottomRightRadius": "string",
        "borderBottomStyle": "string",
        "borderBottomWidth": "string",
        "borderCollapse": "string",
        "borderColor": "string",
        "borderImage": "string",
        "borderImageOutset": "string",
        "borderImageRepeat": "string",
        "borderImageSlice": "string",
        "borderImageSource": "string",
        "borderImageWidth": "string",
        "borderLeft": "string",
        "borderLeftColor": "string",
        "borderLeftStyle": "string",
        "borderLeftWidth": "string",
        "borderRadius": "string",
        "borderRight": "string",
        "borderRightColor": "string",
        "borderRightStyle": "string",
        "borderRightWidth": "string",
        "borderSpacing": "string",
        "borderStyle": "string",
        "borderTop": "string",
        "borderTopColor": "string",
        "borderTopLeftRadius": "string",
        "borderTopRightRadius": "string",
        "borderTopStyle": "string",
        "borderTopWidth": "string",
        "borderWidth": "string",
        "bottom": "string",
        "boxShadow": "string",
        "boxSizing": "string",
        "captionSide": "string",
        "clear": "string",
        "clip": "string",
        "clipPath": "string",
        "clipRule": "string",
        "color": "string",
        "colorInterpolation": "string",
        "colorInterpolationFilters": "string",
        "colorProfile": "string",
        "colorRendering": "string",
        "content": "string",
        "counterIncrement": "string",
        "counterReset": "string",
        "cursor": "string",
        "direction": "string",
        "display": "string",
        "dominantBaseline": "string",
        "emptyCells": "string",
        "enableBackground": "string",
        "fill": "string",
        "fillOpacity": "string",
        "fillRule": "string",
        "filter": "string",
        "float": "string",
        "floodColor": "string",
        "floodOpacity": "string",
        "font": "string",
        "fontFamily": "string",
        "fontSize": "string",
        "fontStretch": "string",
        "fontStyle": "string",
        "fontVariant": "string",
        "fontWeight": "string",
        "glyphOrientationHorizontal": "string",
        "glyphOrientationVertical": "string",
        "height": "string",
        "imageRendering": "string",
        "kerning": "string",
        "left": "string",
        "letterSpacing": "string",
        "lightingColor": "string",
        "lineHeight": "string",
        "listStyle": "string",
        "listStyleImage": "string",
        "listStylePosition": "string",
        "listStyleType": "string",
        "margin": "string",
        "marginBottom": "string",
        "marginLeft": "string",
        "marginRight": "string",
        "marginTop": "string",
        "marker": "string",
        "markerEnd": "string",
        "markerMid": "string",
        "markerStart": "string",
        "mask": "string",
        "maxHeight": "string",
        "maxWidth": "string",
        "minHeight": "string",
        "minWidth": "string",
        "opacity": "string",
        "orphans": "string",
        "outline": "string",
        "outlineColor": "string",
        "outlineOffset": "string",
        "outlineStyle": "string",
        "outlineWidth": "string",
        "overflow": "string",
        "overflowWrap": "string",
        "overflowX": "string",
        "overflowY": "string",
        "padding": "string",
        "paddingBottom": "string",
        "paddingLeft": "string",
        "paddingRight": "string",
        "paddingTop": "string",
        "page": "string",
        "pageBreakAfter": "string",
        "pageBreakBefore": "string",
        "pageBreakInside": "string",
        "pointerEvents": "string",
        "position": "string",
        "quotes": "string",
        "resize": "string",
        "right": "string",
        "shapeRendering": "string",
        "size": "string",
        "speak": "string",
        "src": "string",
        "stopColor": "string",
        "stopOpacity": "string",
        "stroke": "string",
        "strokeDasharray": "string",
        "strokeDashoffset": "string",
        "strokeLinecap": "string",
        "strokeLinejoin": "string",
        "strokeMiterlimit": "string",
        "strokeOpacity": "string",
        "strokeWidth": "string",
        "tabSize": "string",
        "tableLayout": "string",
        "textAlign": "string",
        "textAnchor": "string",
        "textDecoration": "string",
        "textIndent": "string",
        "textLineThrough": "string",
        "textLineThroughColor": "string",
        "textLineThroughMode": "string",
        "textLineThroughStyle": "string",
        "textLineThroughWidth": "string",
        "textOverflow": "string",
        "textOverline": "string",
        "textOverlineColor": "string",
        "textOverlineMode": "string",
        "textOverlineStyle": "string",
        "textOverlineWidth": "string",
        "textRendering": "string",
        "textShadow": "string",
        "textTransform": "string",
        "textUnderline": "string",
        "textUnderlineColor": "string",
        "textUnderlineMode": "string",
        "textUnderlineStyle": "string",
        "textUnderlineWidth": "string",
        "top": "string",
        "unicodeBidi": "string",
        "unicodeRange": "string",
        "vectorEffect": "string",
        "verticalAlign": "string",
        "visibility": "string",
        "whiteSpace": "string",
        "width": "string",
        "wordBreak": "string",
        "wordSpacing": "string",
        "wordWrap": "string",
        "writingMode": "string",
        "zIndex": "string",
        "zoom": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.style",
        "!doc": "Returns an object that represents the element's style attribute."
      },
      "classList": {
        "!type": "+DOMTokenList",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.classList",
        "!doc": "Returns a token list of the class attribute of the element."
      },
      "contentEditable": {
        "!type": "bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Element.contentEditable",
        "!doc": "Indicates whether or not the element is editable."
      },
      "firstElementChild": {
        "!type": "+Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Element.firstElementChild",
        "!doc": "Returns the element's first child element or null if there are no child elements."
      },
      "lastElementChild": {
        "!type": "+Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Element.lastElementChild",
        "!doc": "Returns the element's last child element or null if there are no child elements."
      },
      "nextElementSibling": {
        "!type": "+Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Element.nextElementSibling",
        "!doc": "Returns the element immediately following the specified one in its parent's children list, or null if the specified element is the last one in the list."
      },
      "previousElementSibling": {
        "!type": "+Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Element.previousElementSibling",
        "!doc": "Returns the element immediately prior to the specified one in its parent's children list, or null if the specified element is the first one in the list."
      },
      "tabIndex": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.tabIndex",
        "!doc": "Gets/sets the tab order of the current element."
      },
      "title": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.title",
        "!doc": "Establishes the text to be displayed in a 'tool tip' popup when the mouse is over the displayed node."
      },
      "width": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.offsetWidth",
        "!doc": "Returns the layout width of an element."
      },
      "height": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.offsetHeight",
        "!doc": "Height of an element relative to the element's offsetParent."
      },
      "getContext": {
        "!type": "fn(id: string) -> CanvasRenderingContext2D",
        "!url": "https://developer.mozilla.org/en/docs/DOM/HTMLCanvasElement",
        "!doc": "DOM canvas elements expose the HTMLCanvasElement interface, which provides properties and methods for manipulating the layout and presentation of canvas elements. The HTMLCanvasElement interface inherits the properties and methods of the element object interface."
      },
      "supportsContext": "fn(id: string) -> bool",
      "oncopy": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.oncopy",
        "!doc": "The oncopy property returns the onCopy event handler code on the current element."
      },
      "oncut": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.oncut",
        "!doc": "The oncut property returns the onCut event handler code on the current element."
      },
      "onpaste": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.onpaste",
        "!doc": "The onpaste property returns the onPaste event handler code on the current element."
      },
      "onbeforeunload": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/HTML/Element/body",
        "!doc": "The HTML <body> element represents the main content of an HTML document. There is only one <body> element in a document."
      },
      "onfocus": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.onfocus",
        "!doc": "The onfocus property returns the onFocus event handler code on the current element."
      },
      "onblur": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.onblur",
        "!doc": "The onblur property returns the onBlur event handler code, if any, that exists on the current element."
      },
      "onchange": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.onchange",
        "!doc": "The onchange property sets and returns the onChange event handler code for the current element."
      },
      "onclick": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.onclick",
        "!doc": "The onclick property returns the onClick event handler code on the current element."
      },
      "ondblclick": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.ondblclick",
        "!doc": "The ondblclick property returns the onDblClick event handler code on the current element."
      },
      "onmousedown": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.onmousedown",
        "!doc": "The onmousedown property returns the onMouseDown event handler code on the current element."
      },
      "onmouseup": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.onmouseup",
        "!doc": "The onmouseup property returns the onMouseUp event handler code on the current element."
      },
      "onmousewheel": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Mozilla_event_reference/wheel",
        "!doc": "The wheel event is fired when a wheel button of a pointing device (usually a mouse) is rotated. This event deprecates the legacy mousewheel event."
      },
      "onmouseover": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.onmouseover",
        "!doc": "The onmouseover property returns the onMouseOver event handler code on the current element."
      },
      "onmouseout": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.onmouseout",
        "!doc": "The onmouseout property returns the onMouseOut event handler code on the current element."
      },
      "onmousemove": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.onmousemove",
        "!doc": "The onmousemove property returns the mousemove event handler code on the current element."
      },
      "oncontextmenu": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/window.oncontextmenu",
        "!doc": "An event handler property for right-click events on the window. Unless the default behavior is prevented, the browser context menu will activate. Note that this event will occur with any non-disabled right-click event and does not depend on an element possessing the \"contextmenu\" attribute."
      },
      "onkeydown": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.onkeydown",
        "!doc": "The onkeydown property returns the onKeyDown event handler code on the current element."
      },
      "onkeyup": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.onkeyup",
        "!doc": "The onkeyup property returns the onKeyUp event handler code for the current element."
      },
      "onkeypress": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.onkeypress",
        "!doc": "The onkeypress property sets and returns the onKeyPress event handler code for the current element."
      },
      "onresize": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.onresize",
        "!doc": "onresize returns the element's onresize event handler code. It can also be used to set the code to be executed when the resize event occurs."
      },
      "onscroll": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.onscroll",
        "!doc": "The onscroll property returns the onScroll event handler code on the current element."
      },
      "ondragstart": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DragDrop/Drag_Operations",
        "!doc": "The following describes the steps that occur during a drag and drop operation."
      },
      "ondragover": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Mozilla_event_reference/dragover",
        "!doc": "The dragover event is fired when an element or text selection is being dragged over a valid drop target (every few hundred milliseconds)."
      },
      "ondragleave": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Mozilla_event_reference/dragleave",
        "!doc": "The dragleave event is fired when a dragged element or text selection leaves a valid drop target."
      },
      "ondragenter": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Mozilla_event_reference/dragenter",
        "!doc": "The dragenter event is fired when a dragged element or text selection enters a valid drop target."
      },
      "ondragend": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Mozilla_event_reference/dragend",
        "!doc": "The dragend event is fired when a drag operation is being ended (by releasing a mouse button or hitting the escape key)."
      },
      "ondrag": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Mozilla_event_reference/drag",
        "!doc": "The drag event is fired when an element or text selection is being dragged (every few hundred milliseconds)."
      },
      "offsetTop": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.offsetTop",
        "!doc": "Returns the distance of the current element relative to the top of the offsetParent node."
      },
      "offsetLeft": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.offsetLeft",
        "!doc": "Returns the number of pixels that the upper left corner of the current element is offset to the left within the offsetParent node."
      },
      "offsetHeight": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.offsetHeight",
        "!doc": "Height of an element relative to the element's offsetParent."
      },
      "offsetWidth": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.offsetWidth",
        "!doc": "Returns the layout width of an element."
      },
      "scrollTop": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.scrollTop",
        "!doc": "Gets or sets the number of pixels that the content of an element is scrolled upward."
      },
      "scrollLeft": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.scrollLeft",
        "!doc": "Gets or sets the number of pixels that an element's content is scrolled to the left."
      },
      "scrollHeight": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.scrollHeight",
        "!doc": "Height of the scroll view of an element; it includes the element padding but not its margin."
      },
      "scrollWidth": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.scrollWidth",
        "!doc": "Read-only property that returns either the width in pixels of the content of an element or the width of the element itself, whichever is greater."
      },
      "clientTop": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.clientTop",
        "!doc": "The width of the top border of an element in pixels. It does not include the top margin or padding. clientTop is read-only."
      },
      "clientLeft": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.clientLeft",
        "!doc": "The width of the left border of an element in pixels. It includes the width of the vertical scrollbar if the text direction of the element is right-to-left and if there is an overflow causing a left vertical scrollbar to be rendered. clientLeft does not include the left margin or the left padding. clientLeft is read-only."
      },
      "clientHeight": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.clientHeight",
        "!doc": "Returns the inner height of an element in pixels, including padding but not the horizontal scrollbar height, border, or margin."
      },
      "clientWidth": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.clientWidth",
        "!doc": "The inner width of an element in pixels. It includes padding but not the vertical scrollbar (if present, if rendered), border or margin."
      },
      "innerHTML": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.innerHTML",
        "!doc": "Sets or gets the HTML syntax describing the element's descendants."
      }
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/Element",
    "!doc": "Represents an element in an HTML or XML document."
  },
  "Text": {
    "!type": "fn()",
    "prototype": {
      "!proto": "Node.prototype",
      "wholeText": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Text.wholeText",
        "!doc": "Returns all text of all Text nodes logically adjacent to the node.  The text is concatenated in document order.  This allows you to specify any text node and obtain all adjacent text as a single string."
      },
      "splitText": {
        "!type": "fn(offset: number) -> +Text",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Text.splitText",
        "!doc": "Breaks the Text node into two nodes at the specified offset, keeping both nodes in the tree as siblings."
      }
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/Text",
    "!doc": "In the DOM, the Text interface represents the textual content of an Element or Attr.  If an element has no markup within its content, it has a single child implementing Text that contains the element's text.  However, if the element contains markup, it is parsed into information items and Text nodes that form its children."
  },
  "Document": {
    "!type": "fn()",
    "prototype": {
      "!proto": "Node.prototype",
      "activeElement": {
        "!type": "+Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.activeElement",
        "!doc": "Returns the currently focused element, that is, the element that will get keystroke events if the user types any. This attribute is read only."
      },
      "compatMode": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.compatMode",
        "!doc": "Indicates whether the document is rendered in Quirks mode or Strict mode."
      },
      "designMode": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.designMode",
        "!doc": "Can be used to make any document editable, for example in a <iframe />:"
      },
      "dir": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Document.dir",
        "!doc": "This property should indicate and allow the setting of the directionality of the text of the document, whether left to right (default) or right to left."
      },
      "height": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.height",
        "!doc": "Returns the height of the <body> element of the current document."
      },
      "width": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.width",
        "!doc": "Returns the width of the <body> element of the current document in pixels."
      },
      "characterSet": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.characterSet",
        "!doc": "Returns the character encoding of the current document."
      },
      "readyState": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.readyState",
        "!doc": "Returns \"loading\" while the document is loading, \"interactive\" once it is finished parsing but still loading sub-resources, and \"complete\" once it has loaded."
      },
      "location": {
        "!type": "location",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.location",
        "!doc": "Returns a Location object, which contains information about the URL of the document and provides methods for changing that URL."
      },
      "lastModified": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.lastModified",
        "!doc": "Returns a string containing the date and time on which the current document was last modified."
      },
      "head": {
        "!type": "+Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.head",
        "!doc": "Returns the <head> element of the current document. If there are more than one <head> elements, the first one is returned."
      },
      "body": {
        "!type": "+Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.body",
        "!doc": "Returns the <body> or <frameset> node of the current document."
      },
      "cookie": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.cookie",
        "!doc": "Get and set the cookies associated with the current document."
      },
      "URL": "string",
      "domain": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.domain",
        "!doc": "Gets/sets the domain portion of the origin of the current document, as used by the same origin policy."
      },
      "referrer": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.referrer",
        "!doc": "Returns the URI of the page that linked to this page."
      },
      "title": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.title",
        "!doc": "Gets or sets the title of the document."
      },
      "defaultView": {
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.defaultView",
        "!doc": "In browsers returns the window object associated with the document or null if none available."
      },
      "documentURI": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.documentURI",
        "!doc": "Returns the document location as string. It is read-only per DOM4 specification."
      },
      "xmlStandalone": "bool",
      "xmlVersion": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.xmlVersion",
        "!doc": "Returns the version number as specified in the XML declaration (e.g., <?xml version=\"1.0\"?>) or \"1.0\" if the declaration is absent."
      },
      "xmlEncoding": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Document.xmlEncoding",
        "!doc": "Returns the encoding as determined by the XML declaration. Should be null if unspecified or unknown."
      },
      "inputEncoding": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.inputEncoding",
        "!doc": "Returns a string representing the encoding under which the document was parsed (e.g. ISO-8859-1)."
      },
      "documentElement": {
        "!type": "+Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.documentElement",
        "!doc": "Read-only"
      },
      "implementation": {
        "hasFeature": "fn(feature: string, version: number) -> bool",
        "createDocumentType": {
          "!type": "fn(qualifiedName: string, publicId: string, systemId: string) -> +Node",
          "!url": "https://developer.mozilla.org/en/docs/DOM/DOMImplementation.createDocumentType",
          "!doc": "Returns a DocumentType object which can either be used with DOMImplementation.createDocument upon document creation or they can be put into the document via Node.insertBefore() or Node.replaceChild(): http://www.w3.org/TR/DOM-Level-3-Cor...l#ID-B63ED1A31 (less ideal due to features not likely being as accessible: http://www.w3.org/TR/DOM-Level-3-Cor...createDocument ). In any case, entity declarations and notations will not be available: http://www.w3.org/TR/DOM-Level-3-Cor...-createDocType   "
        },
        "createHTMLDocument": {
          "!type": "fn(title: string) -> +Document",
          "!url": "https://developer.mozilla.org/en/docs/DOM/DOMImplementation.createHTMLDocument",
          "!doc": "This method (available from document.implementation) creates a new HTML document."
        },
        "createDocument": {
          "!type": "fn(namespaceURI: string, qualifiedName: string, type: +Node) -> +Document",
          "!url": "https://developer.mozilla.org/en-US/docs/DOM/DOMImplementation.createHTMLDocument",
          "!doc": "This method creates a new HTML document."
        },
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.implementation",
        "!doc": "Returns a DOMImplementation object associated with the current document."
      },
      "doctype": {
        "!type": "+Node",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.doctype",
        "!doc": "Returns the Document Type Declaration (DTD) associated with current document. The returned object implements the DocumentType interface. Use DOMImplementation.createDocumentType() to create a DocumentType."
      },
      "open": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.open",
        "!doc": "The document.open() method opens a document for writing."
      },
      "close": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.close",
        "!doc": "The document.close() method finishes writing to a document, opened with document.open()."
      },
      "write": {
        "!type": "fn(html: string)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.write",
        "!doc": "Writes a string of text to a document stream opened by document.open()."
      },
      "writeln": {
        "!type": "fn(html: string)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.writeln",
        "!doc": "Writes a string of text followed by a newline character to a document."
      },
      "clear": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.clear",
        "!doc": "In recent versions of Mozilla-based applications as well as in Internet Explorer and Netscape 4 this method does nothing."
      },
      "hasFocus": {
        "!type": "fn() -> bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.hasFocus",
        "!doc": "Returns a Boolean value indicating whether the document or any element inside the document has focus. This method can be used to determine whether the active element in a document has focus."
      },
      "createElement": {
        "!type": "fn(tagName: string) -> +Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.createElement",
        "!doc": "Creates the specified element."
      },
      "createElementNS": {
        "!type": "fn(ns: string, tagName: string) -> +Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.createElementNS",
        "!doc": "Creates an element with the specified namespace URI and qualified name."
      },
      "createDocumentFragment": {
        "!type": "fn() -> +DocumentFragment",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.createDocumentFragment",
        "!doc": "Creates a new empty DocumentFragment."
      },
      "createTextNode": {
        "!type": "fn(content: string) -> +Text",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.createTextNode",
        "!doc": "Creates a new Text node."
      },
      "createComment": {
        "!type": "fn(content: string) -> +Node",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.createComment",
        "!doc": "Creates a new comment node, and returns it."
      },
      "createCDATASection": {
        "!type": "fn(content: string) -> +Node",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.createCDATASection",
        "!doc": "Creates a new CDATA section node, and returns it. "
      },
      "createProcessingInstruction": {
        "!type": "fn(content: string) -> +Node",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.createProcessingInstruction",
        "!doc": "Creates a new processing instruction node, and returns it."
      },
      "createAttribute": {
        "!type": "fn(name: string) -> +Attr",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.createAttribute",
        "!doc": "Creates a new attribute node, and returns it."
      },
      "createAttributeNS": {
        "!type": "fn(ns: string, name: string) -> +Attr",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Attr",
        "!doc": "This type represents a DOM element's attribute as an object. In most DOM methods, you will probably directly retrieve the attribute as a string (e.g., Element.getAttribute(), but certain functions (e.g., Element.getAttributeNode()) or means of iterating give Attr types."
      },
      "importNode": {
        "!type": "fn(node: +Node, deep: bool) -> +Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.importNode",
        "!doc": "Creates a copy of a node from an external document that can be inserted into the current document."
      },
      "getElementById": {
        "!type": "fn(id: string) -> +Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.getElementById",
        "!doc": "Returns a reference to the element by its ID."
      },
      "getElementsByTagName": {
        "!type": "fn(tagName: string) -> +NodeList",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.getElementsByTagName",
        "!doc": "Returns a NodeList of elements with the given tag name. The complete document is searched, including the root node. The returned NodeList is live, meaning that it updates itself automatically to stay in sync with the DOM tree without having to call document.getElementsByTagName again."
      },
      "getElementsByTagNameNS": {
        "!type": "fn(ns: string, tagName: string) -> +NodeList",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.getElementsByTagNameNS",
        "!doc": "Returns a list of elements with the given tag name belonging to the given namespace. The complete document is searched, including the root node."
      },
      "createEvent": {
        "!type": "fn(type: string) -> +Event",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.createEvent",
        "!doc": "Creates an event of the type specified. The returned object should be first initialized and can then be passed to element.dispatchEvent."
      },
      "createRange": {
        "!type": "fn() -> +Range",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.createRange",
        "!doc": "Returns a new Range object."
      },
      "evaluate": {
        "!type": "fn(expr: ?) -> +XPathResult",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.evaluate",
        "!doc": "Returns an XPathResult based on an XPath expression and other given parameters."
      },
      "execCommand": {
        "!type": "fn(cmd: string)",
        "!url": "https://developer.mozilla.org/en-US/docs/Rich-Text_Editing_in_Mozilla#Executing_Commands",
        "!doc": "Run command to manipulate the contents of an editable region."
      },
      "queryCommandEnabled": {
        "!type": "fn(cmd: string) -> bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document",
        "!doc": "Returns true if the Midas command can be executed on the current range."
      },
      "queryCommandIndeterm": {
        "!type": "fn(cmd: string) -> bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document",
        "!doc": "Returns true if the Midas command is in a indeterminate state on the current range."
      },
      "queryCommandState": {
        "!type": "fn(cmd: string) -> bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document",
        "!doc": "Returns true if the Midas command has been executed on the current range."
      },
      "queryCommandSupported": {
        "!type": "fn(cmd: string) -> bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.queryCommandSupported",
        "!doc": "Reports whether or not the specified editor query command is supported by the browser."
      },
      "queryCommandValue": {
        "!type": "fn(cmd: string) -> string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document",
        "!doc": "Returns the current value of the current range for Midas command."
      },
      "getElementsByName": {
        "!type": "fn(name: string) -> +HTMLCollection",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.getElementsByName",
        "!doc": "Returns a list of elements with a given name in the HTML document."
      },
      "elementFromPoint": {
        "!type": "fn(x: number, y: number) -> +Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.elementFromPoint",
        "!doc": "Returns the element from the document whose elementFromPoint method is being called which is the topmost element which lies under the given point.  To get an element, specify the point via coordinates, in CSS pixels, relative to the upper-left-most point in the window or frame containing the document."
      },
      "getSelection": {
        "!type": "fn() -> +Selection",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.getSelection",
        "!doc": "The DOM getSelection() method is available on the Window and Document interfaces."
      },
      "adoptNode": {
        "!type": "fn(node: +Node) -> +Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.adoptNode",
        "!doc": "Adopts a node from an external document. The node and its subtree is removed from the document it's in (if any), and its ownerDocument is changed to the current document. The node can then be inserted into the current document."
      },
      "createTreeWalker": {
        "!type": "fn(root: +Node, mask: number) -> ?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.createTreeWalker",
        "!doc": "Returns a new TreeWalker object."
      },
      "createExpression": {
        "!type": "fn(text: string) -> ?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.createExpression",
        "!doc": "This method compiles an XPathExpression which can then be used for (repeated) evaluations."
      },
      "createNSResolver": {
        "!type": "fn(node: +Node)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.createNSResolver",
        "!doc": "Creates an XPathNSResolver which resolves namespaces with respect to the definitions in scope for a specified node."
      },
      "scripts": {
        "!type": "+HTMLCollection",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Document.scripts",
        "!doc": "Returns a list of the <script> elements in the document. The returned object is an HTMLCollection."
      },
      "plugins": {
        "!type": "+HTMLCollection",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.plugins",
        "!doc": "Returns an HTMLCollection object containing one or more HTMLEmbedElements or null which represent the <embed> elements in the current document."
      },
      "embeds": {
        "!type": "+HTMLCollection",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.embeds",
        "!doc": "Returns a list of the embedded OBJECTS within the current document."
      },
      "anchors": {
        "!type": "+HTMLCollection",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.anchors",
        "!doc": "Returns a list of all of the anchors in the document."
      },
      "links": {
        "!type": "+HTMLCollection",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.links",
        "!doc": "The links property returns a collection of all AREA elements and anchor elements in a document with a value for the href attribute. "
      },
      "forms": {
        "!type": "+HTMLCollection",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.forms",
        "!doc": "Returns a collection (an HTMLCollection) of the form elements within the current document."
      },
      "styleSheets": {
        "!type": "+HTMLCollection",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.styleSheets",
        "!doc": "Returns a list of stylesheet objects for stylesheets explicitly linked into or embedded in a document."
      },
      "querySelector": "Element.prototype.querySelector",
      "querySelectorAll": "Element.prototype.querySelectorAll"
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/document",
    "!doc": "Each web page loaded in the browser has its own document object. This object serves as an entry point to the web page's content (the DOM tree, including elements such as <body> and <table>) and provides functionality global to the document (such as obtaining the page's URL and creating new elements in the document)."
  },
  "document": {
    "!type": "+Document",
    "!url": "https://developer.mozilla.org/en/docs/DOM/document",
    "!doc": "Each web page loaded in the browser has its own document object. This object serves as an entry point to the web page's content (the DOM tree, including elements such as <body> and <table>) and provides functionality global to the document (such as obtaining the page's URL and creating new elements in the document)."
  },
  "XMLDocument": {
    "!type": "fn()",
    "prototype": "Document.prototype",
    "!url": "https://developer.mozilla.org/en/docs/Parsing_and_serializing_XML",
    "!doc": "The Web platform provides the following objects for parsing and serializing XML:"
  },
  "Attr": {
    "!type": "fn()",
    "prototype": {
      "isId": {
        "!type": "bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Attr",
        "!doc": "This type represents a DOM element's attribute as an object. In most DOM methods, you will probably directly retrieve the attribute as a string (e.g., Element.getAttribute(), but certain functions (e.g., Element.getAttributeNode()) or means of iterating give Attr types."
      },
      "name": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Attr",
        "!doc": "This type represents a DOM element's attribute as an object. In most DOM methods, you will probably directly retrieve the attribute as a string (e.g., Element.getAttribute(), but certain functions (e.g., Element.getAttributeNode()) or means of iterating give Attr types."
      },
      "value": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Attr",
        "!doc": "This type represents a DOM element's attribute as an object. In most DOM methods, you will probably directly retrieve the attribute as a string (e.g., Element.getAttribute(), but certain functions (e.g., Element.getAttributeNode()) or means of iterating give Attr types."
      }
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/Attr",
    "!doc": "This type represents a DOM element's attribute as an object. In most DOM methods, you will probably directly retrieve the attribute as a string (e.g., Element.getAttribute(), but certain functions (e.g., Element.getAttributeNode()) or means of iterating give Attr types."
  },
  "NodeList": {
    "!type": "fn()",
    "prototype": {
      "length": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.length",
        "!doc": "Returns the number of items in a NodeList."
      },
      "item": {
        "!type": "fn(i: number) -> +Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/NodeList.item",
        "!doc": "Returns a node from a NodeList by index."
      },
      "<i>": "+Element"
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/NodeList",
    "!doc": "NodeList objects are collections of nodes returned by getElementsByTagName, getElementsByTagNameNS, Node.childNodes, querySelectorAll, getElementsByClassName, etc."
  },
  "HTMLCollection": {
    "!type": "fn()",
    "prototype": {
      "length": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/HTMLCollection",
        "!doc": "The number of items in the collection."
      },
      "item": {
        "!type": "fn(i: number) -> +Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/HTMLCollection",
        "!doc": "Returns the specific node at the given zero-based index into the list. Returns null if the index is out of range."
      },
      "namedItem": {
        "!type": "fn(name: string) -> +Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/HTMLCollection",
        "!doc": "Returns the specific node whose ID or, as a fallback, name matches the string specified by name. Matching by name is only done as a last resort, only in HTML, and only if the referenced element supports the name attribute. Returns null if no node exists by the given name."
      },
      "<i>": "+Element"
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/HTMLCollection",
    "!doc": "HTMLCollection is an interface representing a generic collection of elements (in document order) and offers methods and properties for traversing the list."
  },
  "NamedNodeMap": {
    "!type": "fn()",
    "prototype": {
      "length": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/NamedNodeMap",
        "!doc": "The number of items in the map."
      },
      "getNamedItem": {
        "!type": "fn(name: string) -> +Node",
        "!url": "https://developer.mozilla.org/en/docs/DOM/NamedNodeMap",
        "!doc": "Gets a node by name."
      },
      "setNamedItem": {
        "!type": "fn(node: +Node) -> +Node",
        "!url": "https://developer.mozilla.org/en/docs/DOM/NamedNodeMap",
        "!doc": "Adds (or replaces) a node by its nodeName."
      },
      "removeNamedItem": {
        "!type": "fn(name: string) -> +Node",
        "!url": "https://developer.mozilla.org/en/docs/DOM/NamedNodeMap",
        "!doc": "Removes a node (or if an attribute, may reveal a default if present)."
      },
      "item": {
        "!type": "fn(i: number) -> +Node",
        "!url": "https://developer.mozilla.org/en/docs/DOM/NamedNodeMap",
        "!doc": "Returns the item at the given index (or null if the index is higher or equal to the number of nodes)."
      },
      "getNamedItemNS": {
        "!type": "fn(ns: string, name: string) -> +Node",
        "!url": "https://developer.mozilla.org/en/docs/DOM/NamedNodeMap",
        "!doc": "Gets a node by namespace and localName."
      },
      "setNamedItemNS": {
        "!type": "fn(node: +Node) -> +Node",
        "!url": "https://developer.mozilla.org/en/docs/DOM/NamedNodeMap",
        "!doc": "Adds (or replaces) a node by its localName and namespaceURI."
      },
      "removeNamedItemNS": {
        "!type": "fn(ns: string, name: string) -> +Node",
        "!url": "https://developer.mozilla.org/en/docs/DOM/NamedNodeMap",
        "!doc": "Removes a node (or if an attribute, may reveal a default if present)."
      },
      "<i>": "+Node"
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/NamedNodeMap",
    "!doc": "A collection of nodes returned by Element.attributes (also potentially for DocumentType.entities, DocumentType.notations). NamedNodeMaps are not in any particular order (unlike NodeList), although they may be accessed by an index as in an array (they may also be accessed with the item() method). A NamedNodeMap object are live and will thus be auto-updated if changes are made to their contents internally or elsewhere."
  },
  "DocumentFragment": {
    "!type": "fn()",
    "prototype": {
      "!proto": "Node.prototype"
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/document.createDocumentFragment",
    "!doc": "Creates a new empty DocumentFragment."
  },
  "DOMTokenList": {
    "!type": "fn()",
    "prototype": {
      "length": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/DOMTokenList",
        "!doc": "The amount of items in the list."
      },
      "item": {
        "!type": "fn(i: number) -> string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/DOMTokenList",
        "!doc": "Returns an item in the list by its index."
      },
      "contains": {
        "!type": "fn(token: string) -> bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/DOMTokenList",
        "!doc": "Return true if the underlying string contains token, otherwise false."
      },
      "add": {
        "!type": "fn(token: string)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/DOMTokenList",
        "!doc": "Adds token to the underlying string."
      },
      "remove": {
        "!type": "fn(token: string)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/DOMTokenList",
        "!doc": "Remove token from the underlying string."
      },
      "toggle": {
        "!type": "fn(token: string) -> bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/DOMTokenList",
        "!doc": "Removes token from string and returns false. If token doesn't exist it's added and the function returns true."
      },
      "<i>": "string"
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/DOMTokenList",
    "!doc": "This type represents a set of space-separated tokens. Commonly returned by HTMLElement.classList, HTMLLinkElement.relList, HTMLAnchorElement.relList or HTMLAreaElement.relList. It is indexed beginning with 0 as with JavaScript arrays. DOMTokenList is always case-sensitive."
  },
  "XPathResult": {
    "!type": "fn()",
    "prototype": {
      "boolValue": "bool",
      "invalidIteratorState": {
        "!type": "bool",
        "!url": "https://developer.mozilla.org/en/docs/Introduction_to_using_XPath_in_JavaScript",
        "!doc": "This document describes the interface for using XPath in JavaScript internally, in extensions, and from websites. Mozilla implements a fair amount of the DOM 3 XPath. Which means that XPath expressions can be run against both HTML and XML documents."
      },
      "numberValue": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/XPathResult",
        "!doc": "Refer to XPathResult for more detail."
      },
      "resultType": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/document.evaluate",
        "!doc": "Returns an XPathResult based on an XPath expression and other given parameters."
      },
      "singleNodeValue": {
        "!type": "+Element",
        "!url": "https://developer.mozilla.org/en/docs/Introduction_to_using_XPath_in_JavaScript",
        "!doc": "This document describes the interface for using XPath in JavaScript internally, in extensions, and from websites. Mozilla implements a fair amount of the DOM 3 XPath. Which means that XPath expressions can be run against both HTML and XML documents."
      },
      "snapshotLength": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/XPathResult",
        "!doc": "Refer to XPathResult for more detail."
      },
      "stringValue": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/Introduction_to_using_XPath_in_JavaScript",
        "!doc": "This document describes the interface for using XPath in JavaScript internally, in extensions, and from websites. Mozilla implements a fair amount of the DOM 3 XPath. Which means that XPath expressions can be run against both HTML and XML documents."
      },
      "iterateNext": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/Introduction_to_using_XPath_in_JavaScript",
        "!doc": "This document describes the interface for using XPath in JavaScript internally, in extensions, and from websites. Mozilla implements a fair amount of the DOM 3 XPath. Which means that XPath expressions can be run against both HTML and XML documents."
      },
      "snapshotItem": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en-US/docs/XPathResult#snapshotItem()"
      },
      "ANY_TYPE": "number",
      "NUMBER_TYPE": "number",
      "STRING_TYPE": "number",
      "BOOL_TYPE": "number",
      "UNORDERED_NODE_ITERATOR_TYPE": "number",
      "ORDERED_NODE_ITERATOR_TYPE": "number",
      "UNORDERED_NODE_SNAPSHOT_TYPE": "number",
      "ORDERED_NODE_SNAPSHOT_TYPE": "number",
      "ANY_UNORDERED_NODE_TYPE": "number",
      "FIRST_ORDERED_NODE_TYPE": "number"
    },
    "!url": "https://developer.mozilla.org/en/docs/XPathResult",
    "!doc": "Refer to XPathResult for more detail."
  },
  "ClientRect": {
    "!type": "fn()",
    "prototype": {
      "top": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.getClientRects",
        "!doc": "Top of the box, in pixels, relative to the viewport."
      },
      "left": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.getClientRects",
        "!doc": "Left of the box, in pixels, relative to the viewport."
      },
      "bottom": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.getClientRects",
        "!doc": "Bottom of the box, in pixels, relative to the viewport."
      },
      "right": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/element.getClientRects",
        "!doc": "Right of the box, in pixels, relative to the viewport."
      }
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/element.getClientRects",
    "!doc": "Returns a collection of rectangles that indicate the bounding rectangles for each box in a client."
  },
  "Event": {
    "!type": "fn()",
    "prototype": {
      "stopPropagation": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/DOM/event.stopPropagation",
        "!doc": "Prevents further propagation of the current event."
      },
      "preventDefault": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/DOM/event.preventDefault",
        "!doc": "Cancels the event if it is cancelable, without stopping further propagation of the event."
      },
      "initEvent": {
        "!type": "fn(type: string, bubbles: bool, cancelable: bool)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/event.initEvent",
        "!doc": "The initEvent method is used to initialize the value of an event created using document.createEvent."
      },
      "stopImmediatePropagation": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/DOM/event.stopImmediatePropagation",
        "!doc": "Prevents other listeners of the same event to be called."
      },
      "NONE": "number",
      "CAPTURING_PHASE": "number",
      "AT_TARGET": "number",
      "BUBBLING_PHASE": "number",
      "MOUSEDOWN": "number",
      "MOUSEUP": "number",
      "MOUSEOVER": "number",
      "MOUSEOUT": "number",
      "MOUSEMOVE": "number",
      "MOUSEDRAG": "number",
      "CLICK": "number",
      "DBLCLICK": "number",
      "KEYDOWN": "number",
      "KEYUP": "number",
      "KEYPRESS": "number",
      "DRAGDROP": "number",
      "FOCUS": "number",
      "BLUR": "number",
      "SELECT": "number",
      "CHANGE": "number",
      "target": {
        "!type": "+Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/EventTarget",
        "!doc": "An EventTarget is a DOM interface implemented by objects that can receive DOM events and have listeners for them. The most common EventTargets are DOM elements, although other objects can be EventTargets too, for example document, window, XMLHttpRequest, and others."
      },
      "relatedTarget": {
        "!type": "+Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/event.relatedTarget",
        "!doc": "Identifies a secondary target for the event."
      },
      "pageX": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/event.pageX",
        "!doc": "Returns the horizontal coordinate of the event relative to whole document."
      },
      "pageY": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/event.pageY",
        "!doc": "Returns the vertical coordinate of the event relative to the whole document."
      },
      "clientX": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/event.clientX",
        "!doc": "Returns the horizontal coordinate within the application's client area at which the event occurred (as opposed to the coordinates within the page). For example, clicking in the top-left corner of the client area will always result in a mouse event with a clientX value of 0, regardless of whether the page is scrolled horizontally."
      },
      "clientY": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/event.clientY",
        "!doc": "Returns the vertical coordinate within the application's client area at which the event occurred (as opposed to the coordinates within the page). For example, clicking in the top-left corner of the client area will always result in a mouse event with a clientY value of 0, regardless of whether the page is scrolled vertically."
      },
      "keyCode": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/event.keyCode",
        "!doc": "Returns the Unicode value of a non-character key in a keypress event or any key in any other type of keyboard event."
      },
      "charCode": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/event.charCode",
        "!doc": "Returns the Unicode value of a character key pressed during a keypress event."
      },
      "which": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/event.which",
        "!doc": "Returns the numeric keyCode of the key pressed, or the character code (charCode) for an alphanumeric key pressed."
      },
      "button": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/event.button",
        "!doc": "Indicates which mouse button caused the event."
      },
      "shiftKey": {
        "!type": "bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/event.shiftKey",
        "!doc": "Indicates whether the SHIFT key was pressed when the event fired."
      },
      "ctrlKey": {
        "!type": "bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/event.ctrlKey",
        "!doc": "Indicates whether the CTRL key was pressed when the event fired."
      },
      "altKey": {
        "!type": "bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/event.altKey",
        "!doc": "Indicates whether the ALT key was pressed when the event fired."
      },
      "metaKey": {
        "!type": "bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/event.metaKey",
        "!doc": "Indicates whether the META key was pressed when the event fired."
      },
      "returnValue": {
        "!type": "bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/window.onbeforeunload",
        "!doc": "An event that fires when a window is about to unload its resources. The document is still visible and the event is still cancelable."
      },
      "cancelBubble": {
        "!type": "bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/event.cancelBubble",
        "!doc": "bool is the boolean value of true or false."
      },
      "dataTransfer": {
        "dropEffect": {
          "!type": "string",
          "!url": "https://developer.mozilla.org/en/docs/DragDrop/DataTransfer",
          "!doc": "The actual effect that will be used, and should always be one of the possible values of effectAllowed."
        },
        "effectAllowed": {
          "!type": "string",
          "!url": "https://developer.mozilla.org/en/docs/DragDrop/Drag_Operations",
          "!doc": "Specifies the effects that are allowed for this drag."
        },
        "files": {
          "!type": "+FileList",
          "!url": "https://developer.mozilla.org/en/docs/DragDrop/DataTransfer",
          "!doc": "Contains a list of all the local files available on the data transfer."
        },
        "types": {
          "!type": "[string]",
          "!url": "https://developer.mozilla.org/en-US/docs/DragDrop/DataTransfer",
          "!doc": "Holds a list of the format types of the data that is stored for the first item, in the same order the data was added. An empty list will be returned if no data was added."
        },
        "addElement": {
          "!type": "fn(element: +Element)",
          "!url": "https://developer.mozilla.org/en/docs/DragDrop/DataTransfer",
          "!doc": "Set the drag source."
        },
        "clearData": {
          "!type": "fn(type?: string)",
          "!url": "https://developer.mozilla.org/en/docs/DragDrop/Drag_Operations",
          "!doc": "Remove the data associated with a given type."
        },
        "getData": {
          "!type": "fn(type: string) -> string",
          "!url": "https://developer.mozilla.org/en/docs/DragDrop/Drag_Operations",
          "!doc": "Retrieves the data for a given type, or an empty string if data for that type does not exist or the data transfer contains no data."
        },
        "setData": {
          "!type": "fn(type: string, data: string)",
          "!url": "https://developer.mozilla.org/en/docs/DragDrop/Drag_Operations",
          "!doc": "Set the data for a given type."
        },
        "setDragImage": {
          "!type": "fn(image: +Element)",
          "!url": "https://developer.mozilla.org/en/docs/DragDrop/Drag_Operations",
          "!doc": "Set the image to be used for dragging if a custom one is desired."
        },
        "!url": "https://developer.mozilla.org/en/docs/DragDrop/DataTransfer",
        "!doc": "This object is available from the dataTransfer property of all drag events. It cannot be created separately."
      }
    },
    "!url": "https://developer.mozilla.org/en-US/docs/DOM/event",
    "!doc": "The DOM Event interface is accessible from within the handler function, via the event object passed as the first argument."
  },
  "TouchEvent": {
    "!type": "fn()",
    "prototype": "Event.prototype",
    "!url": "https://developer.mozilla.org/en/docs/DOM/Touch_events",
    "!doc": "In order to provide quality support for touch-based user interfaces, touch events offer the ability to interpret finger activity on touch screens or trackpads."
  },
  "WheelEvent": {
    "!type": "fn()",
    "prototype": "Event.prototype",
    "!url": "https://developer.mozilla.org/en/docs/DOM/WheelEvent",
    "!doc": "The DOM WheelEvent represents events that occur due to the user moving a mouse wheel or similar input device."
  },
  "MouseEvent": {
    "!type": "fn()",
    "prototype": "Event.prototype",
    "!url": "https://developer.mozilla.org/en/docs/DOM/MouseEvent",
    "!doc": "The DOM MouseEvent represents events that occur due to the user interacting with a pointing device (such as a mouse). It's represented by the nsINSDOMMouseEvent interface, which extends the nsIDOMMouseEvent interface."
  },
  "KeyboardEvent": {
    "!type": "fn()",
    "prototype": "Event.prototype",
    "!url": "https://developer.mozilla.org/en/docs/DOM/KeyboardEvent",
    "!doc": "KeyboardEvent objects describe a user interaction with the keyboard. Each event describes a key; the event type (keydown, keypress, or keyup) identifies what kind of activity was performed."
  },
  "HashChangeEvent": {
    "!type": "fn()",
    "prototype": "Event.prototype",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.onhashchange",
    "!doc": "The hashchange event fires when a window's hash changes."
  },
  "ErrorEvent": {
    "!type": "fn()",
    "prototype": "Event.prototype",
    "!url": "https://developer.mozilla.org/en/docs/DOM/DOM_event_reference/error",
    "!doc": "The error event is fired whenever a resource fails to load."
  },
  "CustomEvent": {
    "!type": "fn()",
    "prototype": "Event.prototype",
    "!url": "https://developer.mozilla.org/en/docs/DOM/Event/CustomEvent",
    "!doc": "The DOM CustomEvent are events initialized by an application for any purpose."
  },
  "BeforeLoadEvent": {
    "!type": "fn()",
    "prototype": "Event.prototype",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window",
    "!doc": "This section provides a brief reference for all of the methods, properties, and events available through the DOM window object. The window object implements the Window interface, which in turn inherits from the AbstractView interface. Some additional global functions, namespaces objects, and constructors, not typically associated with the window, but available on it, are listed in the JavaScript Reference."
  },
  "WebSocket": {
    "!type": "fn(url: string)",
    "prototype": {
      "close": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/WebSockets/WebSockets_reference/CloseEvent",
        "!doc": "A CloseEvent is sent to clients using WebSockets when the connection is closed. This is delivered to the listener indicated by the WebSocket object's onclose attribute."
      },
      "send": {
        "!type": "fn(data: string)",
        "!url": "https://developer.mozilla.org/en/docs/WebSockets/WebSockets_reference/WebSocket",
        "!doc": "The WebSocket object provides the API for creating and managing a WebSocket connection to a server, as well as for sending and receiving data on the connection."
      },
      "binaryType": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/WebSockets/WebSockets_reference/WebSocket",
        "!doc": "The WebSocket object provides the API for creating and managing a WebSocket connection to a server, as well as for sending and receiving data on the connection."
      },
      "bufferedAmount": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/WebSockets/Writing_WebSocket_client_applications",
        "!doc": "WebSockets is a technology that makes it possible to open an interactive communication session between the user's browser and a server. Using a WebSocket connection, Web applications can perform real-time communication instead of having to poll for changes back and forth."
      },
      "extensions": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/WebSockets/WebSockets_reference/WebSocket",
        "!doc": "The WebSocket object provides the API for creating and managing a WebSocket connection to a server, as well as for sending and receiving data on the connection."
      },
      "onclose": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/WebSockets/WebSockets_reference/CloseEvent",
        "!doc": "A CloseEvent is sent to clients using WebSockets when the connection is closed. This is delivered to the listener indicated by the WebSocket object's onclose attribute."
      },
      "onerror": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/WebSockets/Writing_WebSocket_client_applications",
        "!doc": "WebSockets is a technology that makes it possible to open an interactive communication session between the user's browser and a server. Using a WebSocket connection, Web applications can perform real-time communication instead of having to poll for changes back and forth."
      },
      "onmessage": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/WebSockets/WebSockets_reference/WebSocket",
        "!doc": "The WebSocket object provides the API for creating and managing a WebSocket connection to a server, as well as for sending and receiving data on the connection."
      },
      "onopen": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/WebSockets/WebSockets_reference/WebSocket",
        "!doc": "The WebSocket object provides the API for creating and managing a WebSocket connection to a server, as well as for sending and receiving data on the connection."
      },
      "protocol": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/WebSockets",
        "!doc": "WebSockets is an advanced technology that makes it possible to open an interactive communication session between the user's browser and a server. With this API, you can send messages to a server and receive event-driven responses without having to poll the server for a reply."
      },
      "url": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/WebSockets/Writing_WebSocket_client_applications",
        "!doc": "WebSockets is a technology that makes it possible to open an interactive communication session between the user's browser and a server. Using a WebSocket connection, Web applications can perform real-time communication instead of having to poll for changes back and forth."
      },
      "CONNECTING": "number",
      "OPEN": "number",
      "CLOSING": "number",
      "CLOSED": "number"
    },
    "!url": "https://developer.mozilla.org/en/docs/WebSockets",
    "!doc": "WebSockets is an advanced technology that makes it possible to open an interactive communication session between the user's browser and a server. With this API, you can send messages to a server and receive event-driven responses without having to poll the server for a reply."
  },
  "Worker": {
    "!type": "fn(scriptURL: string)",
    "prototype": {
      "postMessage": {
        "!type": "fn(message: ?)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Worker",
        "!doc": "Sends a message to the worker's inner scope. This accepts a single parameter, which is the data to send to the worker. The data may be any value or JavaScript object handled by the structured clone algorithm, which includes cyclical references."
      },
      "terminate": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Worker",
        "!doc": "Immediately terminates the worker. This does not offer the worker an opportunity to finish its operations; it is simply stopped at once."
      },
      "onmessage": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Worker",
        "!doc": "An event listener that is called whenever a MessageEvent with type message bubbles through the worker. The message is stored in the event's data member."
      },
      "onerror": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Worker",
        "!doc": "An event listener that is called whenever an ErrorEvent with type error bubbles through the worker."
      }
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/Worker",
    "!doc": "Workers are background tasks that can be easily created and can send messages back to their creators. Creating a worker is as simple as calling the Worker() constructor, specifying a script to be run in the worker thread."
  },
  "localStorage": {
    "setItem": {
      "!type": "fn(name: string, value: string)",
      "!url": "https://developer.mozilla.org/en/docs/DOM/Storage",
      "!doc": "Store an item in storage."
    },
    "getItem": {
      "!type": "fn(name: string) -> string",
      "!url": "https://developer.mozilla.org/en/docs/DOM/Storage",
      "!doc": "Retrieve an item from storage."
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/Storage",
    "!doc": "The DOM Storage mechanism is a means through which string key/value pairs can be securely stored and later retrieved for use."
  },
  "sessionStorage": {
    "setItem": {
      "!type": "fn(name: string, value: string)",
      "!url": "https://developer.mozilla.org/en/docs/DOM/Storage",
      "!doc": "Store an item in storage."
    },
    "getItem": {
      "!type": "fn(name: string) -> string",
      "!url": "https://developer.mozilla.org/en/docs/DOM/Storage",
      "!doc": "Retrieve an item from storage."
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/Storage",
    "!doc": "This is a global object (sessionStorage) that maintains a storage area that's available for the duration of the page session. A page session lasts for as long as the browser is open and survives over page reloads and restores. Opening a page in a new tab or window will cause a new session to be initiated."
  },
  "FileList": {
    "!type": "fn()",
    "prototype": {
      "length": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/FileList",
        "!doc": "A read-only value indicating the number of files in the list."
      },
      "item": {
        "!type": "fn(i: number) -> +File",
        "!url": "https://developer.mozilla.org/en/docs/DOM/FileList",
        "!doc": "Returns a File object representing the file at the specified index in the file list."
      },
      "<i>": "+File"
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/FileList",
    "!doc": "An object of this type is returned by the files property of the HTML input element; this lets you access the list of files selected with the <input type=\"file\"> element. It's also used for a list of files dropped into web content when using the drag and drop API."
  },
  "File": {
    "!type": "fn()",
    "prototype": {
      "!proto": "Blob.prototype",
      "fileName": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/File.fileName",
        "!doc": "Returns the name of the file. For security reasons the path is excluded from this property."
      },
      "fileSize": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/File.fileSize",
        "!doc": "Returns the size of a file in bytes."
      },
      "lastModifiedDate": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/File.lastModifiedDate",
        "!doc": "Returns the last modified date of the file. Files without a known last modified date use the current date instead."
      },
      "name": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/File.name",
        "!doc": "Returns the name of the file. For security reasons, the path is excluded from this property."
      }
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/File",
    "!doc": "The File object provides information about -- and access to the contents of -- files. These are generally retrieved from a FileList object returned as a result of a user selecting files using the input element, or from a drag and drop operation's DataTransfer object."
  },
  "Blob": {
    "!type": "fn(parts: [?], properties?: ?)",
    "prototype": {
      "size": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Blob",
        "!doc": "The size, in bytes, of the data contained in the Blob object. Read only."
      },
      "type": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Blob",
        "!doc": "An ASCII-encoded string, in all lower case, indicating the MIME type of the data contained in the Blob. If the type is unknown, this string is empty. Read only."
      },
      "slice": {
        "!type": "fn(start: number, end?: number, type?: string) -> +Blob",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Blob",
        "!doc": "Returns a new Blob object containing the data in the specified range of bytes of the source Blob."
      }
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/Blob",
    "!doc": "A Blob object represents a file-like object of immutable, raw data. Blobs represent data that isn't necessarily in a JavaScript-native format. The File interface is based on Blob, inheriting blob functionality and expanding it to support files on the user's system."
  },
  "FileReader": {
    "!type": "fn()",
    "prototype": {
      "abort": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
        "!doc": "Aborts the read operation. Upon return, the readyState will be DONE."
      },
      "readAsArrayBuffer": {
        "!type": "fn(blob: +Blob)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
        "!doc": "Starts reading the contents of the specified Blob, producing an ArrayBuffer."
      },
      "readAsBinaryString": {
        "!type": "fn(blob: +Blob)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
        "!doc": "Starts reading the contents of the specified Blob, producing raw binary data."
      },
      "readAsDataURL": {
        "!type": "fn(blob: +Blob)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
        "!doc": "Starts reading the contents of the specified Blob, producing a data: url."
      },
      "readAsText": {
        "!type": "fn(blob: +Blob, encoding?: string)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
        "!doc": "Starts reading the contents of the specified Blob, producing a string."
      },
      "EMPTY": "number",
      "LOADING": "number",
      "DONE": "number",
      "error": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
        "!doc": "The error that occurred while reading the file. Read only."
      },
      "readyState": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
        "!doc": "Indicates the state of the FileReader. This will be one of the State constants. Read only."
      },
      "result": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
        "!doc": "The file's contents. This property is only valid after the read operation is complete, and the format of the data depends on which of the methods was used to initiate the read operation. Read only."
      },
      "onabort": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
        "!doc": "Called when the read operation is aborted."
      },
      "onerror": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
        "!doc": "Called when an error occurs."
      },
      "onload": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
        "!doc": "Called when the read operation is successfully completed."
      },
      "onloadend": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
        "!doc": "Called when the read is completed, whether successful or not. This is called after either onload or onerror."
      },
      "onloadstart": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
        "!doc": "Called when reading the data is about to begin."
      },
      "onprogress": {
        "!type": "?",
        "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
        "!doc": "Called periodically while the data is being read."
      }
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
    "!doc": "The FileReader object lets web applications asynchronously read the contents of files (or raw data buffers) stored on the user's computer, using File or Blob objects to specify the file or data to read. File objects may be obtained from a FileList object returned as a result of a user selecting files using the <input> element, from a drag and drop operation's DataTransfer object, or from the mozGetAsFile() API on an HTMLCanvasElement."
  },
  "Range": {
    "!type": "fn()",
    "prototype": {
      "collapsed": {
        "!type": "bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.collapsed",
        "!doc": "Returns a boolean indicating whether the range's start and end points are at the same position."
      },
      "commonAncestorContainer": {
        "!type": "+Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.commonAncestorContainer",
        "!doc": "Returns the deepest Node that contains the  startContainer and  endContainer Nodes."
      },
      "endContainer": {
        "!type": "+Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.endContainer",
        "!doc": "Returns the Node within which the Range ends."
      },
      "endOffset": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.endOffset",
        "!doc": "Returns a number representing where in the  endContainer the Range ends."
      },
      "startContainer": {
        "!type": "+Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.startContainer",
        "!doc": "Returns the Node within which the Range starts."
      },
      "startOffset": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.startOffset",
        "!doc": "Returns a number representing where in the startContainer the Range starts."
      },
      "setStart": {
        "!type": "fn(node: +Element, offset: number)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.setStart",
        "!doc": "Sets the start position of a Range."
      },
      "setEnd": {
        "!type": "fn(node: +Element, offset: number)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.setEnd",
        "!doc": "Sets the end position of a Range."
      },
      "setStartBefore": {
        "!type": "fn(node: +Element)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.setStartBefore",
        "!doc": "Sets the start position of a Range relative to another Node."
      },
      "setStartAfter": {
        "!type": "fn(node: +Element)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.setStartAfter",
        "!doc": "Sets the start position of a Range relative to a Node."
      },
      "setEndBefore": {
        "!type": "fn(node: +Element)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.setEndBefore",
        "!doc": "Sets the end position of a Range relative to another Node."
      },
      "setEndAfter": {
        "!type": "fn(node: +Element)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.setEndAfter",
        "!doc": "Sets the end position of a Range relative to another Node."
      },
      "selectNode": {
        "!type": "fn(node: +Element)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.selectNode",
        "!doc": "Sets the Range to contain the Node and its contents."
      },
      "selectNodeContents": {
        "!type": "fn(node: +Element)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.selectNodeContents",
        "!doc": "Sets the Range to contain the contents of a Node."
      },
      "collapse": {
        "!type": "fn(toStart: bool)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.collapse",
        "!doc": "Collapses the Range to one of its boundary points."
      },
      "cloneContents": {
        "!type": "fn() -> +DocumentFragment",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.cloneContents",
        "!doc": "Returns a DocumentFragment copying the Nodes of a Range."
      },
      "deleteContents": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.deleteContents",
        "!doc": "Removes the contents of a Range from the Document."
      },
      "extractContents": {
        "!type": "fn() -> +DocumentFragment",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.extractContents",
        "!doc": "Moves contents of a Range from the document tree into a DocumentFragment."
      },
      "insertNode": {
        "!type": "fn(node: +Element)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.insertNode",
        "!doc": "Insert a node at the start of a Range."
      },
      "surroundContents": {
        "!type": "fn(node: +Element)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.surroundContents",
        "!doc": "Moves content of a Range into a new node, placing the new node at the start of the specified range."
      },
      "compareBoundaryPoints": {
        "!type": "fn(how: number, other: +Range) -> number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.compareBoundaryPoints",
        "!doc": "Compares the boundary points of two Ranges."
      },
      "cloneRange": {
        "!type": "fn() -> +Range",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.cloneRange",
        "!doc": "Returns a Range object with boundary points identical to the cloned Range."
      },
      "detach": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/DOM/range.detach",
        "!doc": "Releases a Range from use to improve performance. This lets the browser choose to release resources associated with this Range. Subsequent attempts to use the detached range will result in a DOMException being thrown with an error code of INVALID_STATE_ERR."
      },
      "END_TO_END": "number",
      "END_TO_START": "number",
      "START_TO_END": "number",
      "START_TO_START": "number"
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/range.detach",
    "!doc": "Releases a Range from use to improve performance. This lets the browser choose to release resources associated with this Range. Subsequent attempts to use the detached range will result in a DOMException being thrown with an error code of INVALID_STATE_ERR."
  },
  "XMLHttpRequest": {
    "!type": "fn()",
    "prototype": {
      "abort": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest",
        "!doc": "Aborts the request if it has already been sent."
      },
      "getAllResponseHeaders": {
        "!type": "fn() -> string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest",
        "!doc": "Returns all the response headers as a string, or null if no response has been received. Note: For multipart requests, this returns the headers from the current part of the request, not from the original channel."
      },
      "getResponseHeader": {
        "!type": "fn(header: string) -> string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest",
        "!doc": "Returns the string containing the text of the specified header, or null if either the response has not yet been received or the header doesn't exist in the response."
      },
      "open": {
        "!type": "fn(method: string, url: string, async?: bool, user?: string, password?: string)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest",
        "!doc": "Initializes a request."
      },
      "overrideMimeType": {
        "!type": "fn(type: string)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest",
        "!doc": "Overrides the MIME type returned by the server."
      },
      "send": {
        "!type": "fn(data?: string)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest",
        "!doc": "Sends the request. If the request is asynchronous (which is the default), this method returns as soon as the request is sent. If the request is synchronous, this method doesn't return until the response has arrived."
      },
      "setRequestHeader": {
        "!type": "fn(header: string, value: string)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest",
        "!doc": "Sets the value of an HTTP request header.You must call setRequestHeader() after open(), but before send()."
      },
      "onreadystatechange": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest",
        "!doc": "A JavaScript function object that is called whenever the readyState attribute changes."
      },
      "readyState": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest",
        "!doc": "The state of the request. (0=unsent, 1=opened, 2=headers_received, 3=loading, 4=done)"
      },
      "response": {
        "!type": "+Document",
        "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest",
        "!doc": "The response entity body according to responseType, as an ArrayBuffer, Blob, Document, JavaScript object (for \"json\"), or string. This is null if the request is not complete or was not successful."
      },
      "responseText": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest",
        "!doc": "The response to the request as text, or null if the request was unsuccessful or has not yet been sent."
      },
      "responseType": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest",
        "!doc": "Can be set to change the response type."
      },
      "responseXML": {
        "!type": "+Document",
        "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest",
        "!doc": "The response to the request as a DOM Document object, or null if the request was unsuccessful, has not yet been sent, or cannot be parsed as XML or HTML."
      },
      "status": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest",
        "!doc": "The status of the response to the request. This is the HTTP result code"
      },
      "statusText": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest",
        "!doc": "The response string returned by the HTTP server. Unlike status, this includes the entire text of the response message (\"200 OK\", for example)."
      },
      "timeout": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest/Synchronous_and_Asynchronous_Requests",
        "!doc": "The number of milliseconds a request can take before automatically being terminated. A value of 0 (which is the default) means there is no timeout."
      },
      "UNSENT": "number",
      "OPENED": "number",
      "HEADERS_RECEIVED": "number",
      "LOADING": "number",
      "DONE": "number"
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest",
    "!doc": "XMLHttpRequest is a JavaScript object that was designed by Microsoft and adopted by Mozilla, Apple, and Google. It's now being standardized in the W3C. It provides an easy way to retrieve data at a URL. Despite its name, XMLHttpRequest can be used to retrieve any type of data, not just XML, and it supports protocols other than HTTP (including file and ftp)."
  },
  "DOMParser": {
    "!type": "fn()",
    "prototype": {
      "parseFromString": {
        "!type": "fn(data: string, mime: string) -> +Document",
        "!url": "https://developer.mozilla.org/en/docs/DOM/DOMParser",
        "!doc": "DOMParser can parse XML or HTML source stored in a string into a DOM Document. DOMParser is specified in DOM Parsing and Serialization."
      }
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/DOMParser",
    "!doc": "DOMParser can parse XML or HTML source stored in a string into a DOM Document. DOMParser is specified in DOM Parsing and Serialization."
  },
  "Selection": {
    "!type": "fn()",
    "prototype": {
      "anchorNode": {
        "!type": "+Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/anchorNode",
        "!doc": "Returns the node in which the selection begins."
      },
      "anchorOffset": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/anchorOffset",
        "!doc": "Returns the number of characters that the selection's anchor is offset within the anchorNode."
      },
      "focusNode": {
        "!type": "+Element",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/focusNode",
        "!doc": "Returns the node in which the selection ends."
      },
      "focusOffset": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/focusOffset",
        "!doc": "Returns the number of characters that the selection's focus is offset within the focusNode. "
      },
      "isCollapsed": {
        "!type": "bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/isCollapsed",
        "!doc": "Returns a boolean indicating whether the selection's start and end points are at the same position."
      },
      "rangeCount": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/rangeCount",
        "!doc": "Returns the number of ranges in the selection."
      },
      "getRangeAt": {
        "!type": "fn(i: number) -> +Range",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/getRangeAt",
        "!doc": "Returns a range object representing one of the ranges currently selected."
      },
      "collapse": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/collapse",
        "!doc": "Collapses the current selection to a single point. The document is not modified. If the content is focused and editable, the caret will blink there."
      },
      "extend": {
        "!type": "fn(node: +Element, offset: number)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/extend",
        "!doc": "Moves the focus of the selection to a specified point. The anchor of the selection does not move. The selection will be from the anchor to the new focus regardless of direction."
      },
      "collapseToStart": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/collapseToStart",
        "!doc": "Collapses the selection to the start of the first range in the selection.  If the content of the selection is focused and editable, the caret will blink there."
      },
      "collapseToEnd": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/collapseToEnd",
        "!doc": "Collapses the selection to the end of the last range in the selection.  If the content the selection is in is focused and editable, the caret will blink there."
      },
      "selectAllChildren": {
        "!type": "fn(node: +Element)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/selectAllChildren",
        "!doc": "Adds all the children of the specified node to the selection. Previous selection is lost."
      },
      "addRange": {
        "!type": "fn(range: +Range)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/addRange",
        "!doc": "Adds a Range to a Selection."
      },
      "removeRange": {
        "!type": "fn(range: +Range)",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/removeRange",
        "!doc": "Removes a range from the selection."
      },
      "removeAllRanges": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/removeAllRanges",
        "!doc": "Removes all ranges from the selection, leaving the anchorNode and focusNode properties equal to null and leaving nothing selected. "
      },
      "deleteFromDocument": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/deleteFromDocument",
        "!doc": "Deletes the actual text being represented by a selection object from the document's DOM."
      },
      "containsNode": {
        "!type": "fn(node: +Element) -> bool",
        "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/containsNode",
        "!doc": "Indicates if the node is part of the selection."
      }
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/Selection",
    "!doc": "Selection is the class of the object returned by window.getSelection() and other methods. It represents the text selection in the greater page, possibly spanning multiple elements, when the user drags over static text and other parts of the page. For information about text selection in an individual text editing element."
  },
  "console": {
    "error": {
      "!type": "fn(text: string)",
      "!url": "https://developer.mozilla.org/en/docs/DOM/console.error",
      "!doc": "Outputs an error message to the Web Console."
    },
    "info": {
      "!type": "fn(text: string)",
      "!url": "https://developer.mozilla.org/en/docs/DOM/console.info",
      "!doc": "Outputs an informational message to the Web Console."
    },
    "log": {
      "!type": "fn(text: string)",
      "!url": "https://developer.mozilla.org/en/docs/DOM/console.log",
      "!doc": "Outputs a message to the Web Console."
    },
    "warn": {
      "!type": "fn(text: string)",
      "!url": "https://developer.mozilla.org/en/docs/DOM/console.warn",
      "!doc": "Outputs a warning message to the Web Console."
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/console",
    "!doc": "The console object provides access to the browser's debugging console. The specifics of how it works vary from browser to browser, but there is a de facto set of features that are typically provided."
  },
  "top": {
    "!type": "<top>",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.top",
    "!doc": "Returns a reference to the topmost window in the window hierarchy."
  },
  "parent": {
    "!type": "<top>",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.parent",
    "!doc": "A reference to the parent of the current window or subframe."
  },
  "window": {
    "!type": "<top>",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window",
    "!doc": "This section provides a brief reference for all of the methods, properties, and events available through the DOM window object. The window object implements the Window interface, which in turn inherits from the AbstractView interface. Some additional global functions, namespaces objects, and constructors, not typically associated with the window, but available on it, are listed in the JavaScript Reference."
  },
  "opener": {
    "!type": "<top>",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.opener",
    "!doc": "Returns a reference to the window that opened this current window."
  },
  "self": {
    "!type": "<top>",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.self",
    "!doc": "Returns an object reference to the window object. "
  },
  "devicePixelRatio": "number",
  "name": {
    "!type": "string",
    "!url": "https://developer.mozilla.org/en/docs/JavaScript/Reference/Global_Objects/Function/name",
    "!doc": "The name of the function."
  },
  "closed": {
    "!type": "bool",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.closed",
    "!doc": "This property indicates whether the referenced window is closed or not."
  },
  "pageYOffset": {
    "!type": "number",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.scrollY",
    "!doc": "Returns the number of pixels that the document has already been scrolled vertically."
  },
  "pageXOffset": {
    "!type": "number",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.scrollX",
    "!doc": "Returns the number of pixels that the document has already been scrolled vertically."
  },
  "scrollY": {
    "!type": "number",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.scrollY",
    "!doc": "Returns the number of pixels that the document has already been scrolled vertically."
  },
  "scrollX": {
    "!type": "number",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.scrollX",
    "!doc": "Returns the number of pixels that the document has already been scrolled vertically."
  },
  "screenTop": {
    "!type": "number",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.screen.top",
    "!doc": "Returns the distance in pixels from the top side of the current screen."
  },
  "screenLeft": {
    "!type": "number",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.screen.left",
    "!doc": "Returns the distance in pixels from the left side of the main screen to the left side of the current screen."
  },
  "screenY": {
    "!type": "number",
    "!url": "https://developer.mozilla.org/en/docs/DOM/event.screenY",
    "!doc": "Returns the vertical coordinate of the event within the screen as a whole."
  },
  "screenX": {
    "!type": "number",
    "!url": "https://developer.mozilla.org/en/docs/DOM/event.screenX",
    "!doc": "Returns the horizontal coordinate of the event within the screen as a whole."
  },
  "innerWidth": {
    "!type": "number",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.innerWidth",
    "!doc": "Width (in pixels) of the browser window viewport including, if rendered, the vertical scrollbar."
  },
  "innerHeight": {
    "!type": "number",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.innerHeight",
    "!doc": "Height (in pixels) of the browser window viewport including, if rendered, the horizontal scrollbar."
  },
  "outerWidth": {
    "!type": "number",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.outerWidth",
    "!doc": "window.outerWidth gets the width of the outside of the browser window. It represents the width of the whole browser window including sidebar (if expanded), window chrome and window resizing borders/handles."
  },
  "outerHeight": {
    "!type": "number",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.outerHeight",
    "!doc": "window.outerHeight gets the height in pixels of the whole browser window."
  },
  "frameElement": {
    "!type": "+Element",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.frameElement",
    "!doc": "Returns the element (such as <iframe> or <object>) in which the window is embedded, or null if the window is top-level."
  },
  "crypto": {
    "getRandomValues": {
      "!type": "fn([number])",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.crypto.getRandomValues",
      "!doc": "This methods lets you get cryptographically random values."
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.crypto.getRandomValues",
    "!doc": "This methods lets you get cryptographically random values."
  },
  "navigator": {
    "appName": {
      "!type": "string",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.navigator.appName",
      "!doc": "Returns the name of the browser. The HTML5 specification also allows any browser to return \"Netscape\" here, for compatibility reasons."
    },
    "appVersion": {
      "!type": "string",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.navigator.appVersion",
      "!doc": "Returns the version of the browser as a string. It may be either a plain version number, like \"5.0\", or a version number followed by more detailed information. The HTML5 specification also allows any browser to return \"4.0\" here, for compatibility reasons."
    },
    "language": {
      "!type": "string",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.navigator.language",
      "!doc": "Returns a string representing the language version of the browser."
    },
    "platform": {
      "!type": "string",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.navigator.platform",
      "!doc": "Returns a string representing the platform of the browser."
    },
    "plugins": {
      "!type": "[?]",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.navigator.plugins",
      "!doc": "Returns a PluginArray object, listing the plugins installed in the application."
    },
    "userAgent": {
      "!type": "string",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.navigator.userAgent",
      "!doc": "Returns the user agent string for the current browser."
    },
    "vendor": {
      "!type": "string",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.navigator.vendor",
      "!doc": "Returns the name of the browser vendor for the current browser."
    },
    "javaEnabled": {
      "!type": "bool",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.navigator.javaEnabled",
      "!doc": "This method indicates whether the current browser is Java-enabled or not."
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.navigator",
    "!doc": "Returns a reference to the navigator object, which can be queried for information about the application running the script."
  },
  "history": {
    "state": {
      "!type": "?",
      "!url": "https://developer.mozilla.org/en/docs/DOM/Manipulating_the_browser_history",
      "!doc": "The DOM window object provides access to the browser's history through the history object. It exposes useful methods and properties that let you move back and forth through the user's history, as well as -- starting with HTML5 -- manipulate the contents of the history stack."
    },
    "length": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en/docs/DOM/Manipulating_the_browser_history",
      "!doc": "The DOM window object provides access to the browser's history through the history object. It exposes useful methods and properties that let you move back and forth through the user's history, as well as -- starting with HTML5 -- manipulate the contents of the history stack."
    },
    "go": {
      "!type": "fn(delta: number)",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.history",
      "!doc": "Returns a reference to the History object, which provides an interface for manipulating the browser session history (pages visited in the tab or frame that the current page is loaded in)."
    },
    "forward": {
      "!type": "fn()",
      "!url": "https://developer.mozilla.org/en/docs/DOM/Manipulating_the_browser_history",
      "!doc": "The DOM window object provides access to the browser's history through the history object. It exposes useful methods and properties that let you move back and forth through the user's history, as well as -- starting with HTML5 -- manipulate the contents of the history stack."
    },
    "back": {
      "!type": "fn()",
      "!url": "https://developer.mozilla.org/en/docs/DOM/Manipulating_the_browser_history",
      "!doc": "The DOM window object provides access to the browser's history through the history object. It exposes useful methods and properties that let you move back and forth through the user's history, as well as -- starting with HTML5 -- manipulate the contents of the history stack."
    },
    "pushState": {
      "!type": "fn(data: ?, title: string, url?: string)",
      "!url": "https://developer.mozilla.org/en/docs/DOM/Manipulating_the_browser_history",
      "!doc": "The DOM window object provides access to the browser's history through the history object. It exposes useful methods and properties that let you move back and forth through the user's history, as well as -- starting with HTML5 -- manipulate the contents of the history stack."
    },
    "replaceState": {
      "!type": "fn(data: ?, title: string, url?: string)",
      "!url": "https://developer.mozilla.org/en/docs/DOM/Manipulating_the_browser_history",
      "!doc": "The DOM window object provides access to the browser's history through the history object. It exposes useful methods and properties that let you move back and forth through the user's history, as well as -- starting with HTML5 -- manipulate the contents of the history stack."
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/Manipulating_the_browser_history",
    "!doc": "The DOM window object provides access to the browser's history through the history object. It exposes useful methods and properties that let you move back and forth through the user's history, as well as -- starting with HTML5 -- manipulate the contents of the history stack."
  },
  "screen": {
    "availWidth": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.screen.availWidth",
      "!doc": "Returns the amount of horizontal space in pixels available to the window."
    },
    "availHeight": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.screen.availHeight",
      "!doc": "Returns the amount of vertical space available to the window on the screen."
    },
    "availTop": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.screen.availTop",
      "!doc": "Specifies the y-coordinate of the first pixel that is not allocated to permanent or semipermanent user interface features."
    },
    "availLeft": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.screen.availLeft",
      "!doc": "Returns the first available pixel available from the left side of the screen."
    },
    "pixelDepth": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.screen.pixelDepth",
      "!doc": "Returns the bit depth of the screen."
    },
    "colorDepth": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.screen.colorDepth",
      "!doc": "Returns the color depth of the screen."
    },
    "width": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.screen.width",
      "!doc": "Returns the width of the screen."
    },
    "height": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en/docs/DOM/window.screen.height",
      "!doc": "Returns the height of the screen in pixels."
    },
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.screen",
    "!doc": "Returns a reference to the screen object associated with the window."
  },
  "postMessage": {
    "!type": "fn(message: string, targetOrigin: string)",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.postMessage",
    "!doc": "window.postMessage, when called, causes a MessageEvent to be dispatched at the target window when any pending script that must be executed completes (e.g. remaining event handlers if window.postMessage is called from an event handler, previously-set pending timeouts, etc.). The MessageEvent has the type message, a data property which is set to the value of the first argument provided to window.postMessage, an origin property corresponding to the origin of the main document in the window calling window.postMessage at the time window.postMessage was called, and a source property which is the window from which window.postMessage is called. (Other standard properties of events are present with their expected values.)"
  },
  "close": {
    "!type": "fn()",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.close",
    "!doc": "Closes the current window, or a referenced window."
  },
  "blur": {
    "!type": "fn()",
    "!url": "https://developer.mozilla.org/en/docs/DOM/element.blur",
    "!doc": "The blur method removes keyboard focus from the current element."
  },
  "focus": {
    "!type": "fn()",
    "!url": "https://developer.mozilla.org/en/docs/DOM/element.focus",
    "!doc": "Sets focus on the specified element, if it can be focused."
  },
  "onload": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.onload",
    "!doc": "An event handler for the load event of a window."
  },
  "onunload": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.onunload",
    "!doc": "The unload event is raised when the window is unloading its content and resources. The resources removal is processed after the unload event occurs."
  },
  "onscroll": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.onscroll",
    "!doc": "Specifies the function to be called when the window is scrolled."
  },
  "onresize": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.onresize",
    "!doc": "An event handler for the resize event on the window."
  },
  "ononline": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/document.ononline",
    "!doc": ",fgh s dgkljgsdfl dfjg sdlgj sdlg sdlfj dlg jkdfkj dfjgdfkglsdfjsdlfkgj hdflkg hdlkfjgh dfkjgh"
  },
  "onoffline": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/Online_and_offline_events",
    "!doc": "Some browsers implement Online/Offline events from the WHATWG Web Applications 1.0 specification."
  },
  "onmousewheel": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/DOM_event_reference/mousewheel",
    "!doc": "The DOM mousewheel event is fired asynchronously when mouse wheel or similar device is operated. It's represented by the MouseWheelEvent interface."
  },
  "onmouseup": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.onmouseup",
    "!doc": "An event handler for the mouseup event on the window."
  },
  "onmouseover": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/element.onmouseover",
    "!doc": "The onmouseover property returns the onMouseOver event handler code on the current element."
  },
  "onmouseout": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/element.onmouseout",
    "!doc": "The onmouseout property returns the onMouseOut event handler code on the current element."
  },
  "onmousemove": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/element.onmousemove",
    "!doc": "The onmousemove property returns the mousemove event handler code on the current element."
  },
  "onmousedown": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.onmousedown",
    "!doc": "An event handler for the mousedown event on the window."
  },
  "onclick": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/element.onclick",
    "!doc": "The onclick property returns the onClick event handler code on the current element."
  },
  "ondblclick": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/element.ondblclick",
    "!doc": "The ondblclick property returns the onDblClick event handler code on the current element."
  },
  "onmessage": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/Worker",
    "!doc": "Dedicated Web Workers provide a simple means for web content to run scripts in background threads.  Once created, a worker can send messages to the spawning task by posting messages to an event handler specified by the creator."
  },
  "onkeyup": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/element.onkeyup",
    "!doc": "The onkeyup property returns the onKeyUp event handler code for the current element."
  },
  "onkeypress": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/element.onkeypress",
    "!doc": "The onkeypress property sets and returns the onKeyPress event handler code for the current element."
  },
  "onkeydown": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.onkeydown",
    "!doc": "An event handler for the keydown event on the window."
  },
  "oninput": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/DOM_event_reference/input",
    "!doc": "The DOM input event is fired synchronously when the value of an <input> or <textarea> element is changed. Additionally, it's also fired on contenteditable editors when its contents are changed. In this case, the event target is the editing host element. If there are two or more elements which have contenteditable as true, \"editing host\" is the nearest ancestor element whose parent isn't editable. Similarly, it's also fired on root element of designMode editors."
  },
  "onpopstate": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.onpopstate",
    "!doc": "An event handler for the popstate event on the window."
  },
  "onhashchange": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.onhashchange",
    "!doc": "The hashchange event fires when a window's hash changes."
  },
  "onfocus": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/element.onfocus",
    "!doc": "The onfocus property returns the onFocus event handler code on the current element."
  },
  "onblur": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/element.onblur",
    "!doc": "The onblur property returns the onBlur event handler code, if any, that exists on the current element."
  },
  "onerror": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.onerror",
    "!doc": "An event handler for runtime script errors."
  },
  "ondrop": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en-US/docs/DOM/Mozilla_event_reference/drop",
    "!doc": "The drop event is fired when an element or text selection is dropped on a valid drop target."
  },
  "ondragstart": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en-US/docs/DOM/Mozilla_event_reference/dragstart",
    "!doc": "The dragstart event is fired when the user starts dragging an element or text selection."
  },
  "ondragover": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en-US/docs/DOM/Mozilla_event_reference/dragover",
    "!doc": "The dragover event is fired when an element or text selection is being dragged over a valid drop target (every few hundred milliseconds)."
  },
  "ondragleave": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en-US/docs/DOM/Mozilla_event_reference/dragleave",
    "!doc": "The dragleave event is fired when a dragged element or text selection leaves a valid drop target."
  },
  "ondragenter": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en-US/docs/DOM/Mozilla_event_reference/dragenter",
    "!doc": "The dragenter event is fired when a dragged element or text selection enters a valid drop target."
  },
  "ondragend": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en-US/docs/DOM/Mozilla_event_reference/dragend",
    "!doc": "The dragend event is fired when a drag operation is being ended (by releasing a mouse button or hitting the escape key)."
  },
  "ondrag": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en-US/docs/DOM/Mozilla_event_reference/drag",
    "!doc": "The drag event is fired when an element or text selection is being dragged (every few hundred milliseconds)."
  },
  "oncontextmenu": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.oncontextmenu",
    "!doc": "An event handler property for right-click events on the window. Unless the default behavior is prevented, the browser context menu will activate (though IE8 has a bug with this and will not activate the context menu if a contextmenu event handler is defined). Note that this event will occur with any non-disabled right-click event and does not depend on an element possessing the \"contextmenu\" attribute."
  },
  "onchange": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/element.onchange",
    "!doc": "The onchange property sets and returns the onChange event handler code for the current element."
  },
  "onbeforeunload": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.onbeforeunload",
    "!doc": "An event that fires when a window is about to unload its resources. The document is still visible and the event is still cancelable."
  },
  "onabort": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.onabort",
    "!doc": "An event handler for abort events sent to the window."
  },
  "getSelection": {
    "!type": "fn() -> +Selection",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.getSelection",
    "!doc": "Returns a selection object representing the range of text selected by the user. "
  },
  "alert": {
    "!type": "fn(message: string)",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.alert",
    "!doc": "Display an alert dialog with the specified content and an OK button."
  },
  "confirm": {
    "!type": "fn(message: string) -> bool",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.confirm",
    "!doc": "Displays a modal dialog with a message and two buttons, OK and Cancel."
  },
  "prompt": {
    "!type": "fn(message: string, value: string) -> string",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.prompt",
    "!doc": "Displays a dialog with a message prompting the user to input some text."
  },
  "scrollBy": {
    "!type": "fn(x: number, y: number)",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.scrollBy",
    "!doc": "Scrolls the document in the window by the given amount."
  },
  "scrollTo": {
    "!type": "fn(x: number, y: number)",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.scrollTo",
    "!doc": "Scrolls to a particular set of coordinates in the document."
  },
  "scroll": {
    "!type": "fn(x: number, y: number)",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.scroll",
    "!doc": "Scrolls the window to a particular place in the document."
  },
  "setTimeout": {
    "!type": "fn(f: fn(), ms: number) -> number",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.setTimeout",
    "!doc": "Calls a function or executes a code snippet after specified delay."
  },
  "clearTimeout": {
    "!type": "fn(timeout: number)",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.clearTimeout",
    "!doc": "Clears the delay set by window.setTimeout()."
  },
  "setInterval": {
    "!type": "fn(f: fn(), ms: number) -> number",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.setInterval",
    "!doc": "Calls a function or executes a code snippet repeatedly, with a fixed time delay between each call to that function."
  },
  "clearInterval": {
    "!type": "fn(interval: number)",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.clearInterval",
    "!doc": "Cancels repeated action which was set up using setInterval."
  },
  "atob": {
    "!type": "fn(encoded: string) -> string",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.atob",
    "!doc": "Decodes a string of data which has been encoded using base-64 encoding."
  },
  "btoa": {
    "!type": "fn(data: string) -> string",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.btoa",
    "!doc": "Creates a base-64 encoded ASCII string from a string of binary data."
  },
  "addEventListener": {
    "!type": "fn(type: string, listener: fn(e: +Event), capture: bool)",
    "!url": "https://developer.mozilla.org/en/docs/DOM/EventTarget.addEventListener",
    "!doc": "Registers a single event listener on a single target. The event target may be a single element in a document, the document itself, a window, or an XMLHttpRequest."
  },
  "removeEventListener": {
    "!type": "fn(type: string, listener: fn(), capture: bool)",
    "!url": "https://developer.mozilla.org/en/docs/DOM/EventTarget.removeEventListener",
    "!doc": "Allows the removal of event listeners from the event target."
  },
  "dispatchEvent": {
    "!type": "fn(event: +Event) -> bool",
    "!url": "https://developer.mozilla.org/en/docs/DOM/EventTarget.dispatchEvent",
    "!doc": "Dispatches an event into the event system. The event is subject to the same capturing and bubbling behavior as directly dispatched events."
  },
  "getComputedStyle": {
    "!type": "fn(node: +Element, pseudo?: string) -> Element.prototype.style",
    "!url": "https://developer.mozilla.org/en/docs/DOM/window.getComputedStyle",
    "!doc": "Gives the final used values of all the CSS properties of an element."
  },
  "CanvasRenderingContext2D": {
    "canvas": "+Element",
    "width": "number",
    "height": "number",
    "commit": "fn()",
    "save": "fn()",
    "restore": "fn()",
    "currentTransform": "?",
    "scale": "fn(x: number, y: number)",
    "rotate": "fn(angle: number)",
    "translate": "fn(x: number, y: number)",
    "transform": "fn(a: number, b: number, c: number, d: number, e: number, f: number)",
    "setTransform": "fn(a: number, b: number, c: number, d: number, e: number, f: number)",
    "resetTransform": "fn()",
    "globalAlpha": "number",
    "globalCompositeOperation": "string",
    "imageSmoothingEnabled": "bool",
    "strokeStyle": "string",
    "fillStyle": "string",
    "createLinearGradient": "fn(x0: number, y0: number, x1: number, y1: number) -> ?",
    "createPattern": "fn(image: ?, repetition: string) -> ?",
    "shadowOffsetX": "number",
    "shadowOffsetY": "number",
    "shadowBlur": "number",
    "shadowColor": "string",
    "clearRect": "fn(x: number, y: number, w: number, h: number)",
    "fillRect": "fn(x: number, y: number, w: number, h: number)",
    "strokeRect": "fn(x: number, y: number, w: number, h: number)",
    "fillRule": "string",
    "fill": "fn()",
    "beginPath": "fn()",
    "stroke": "fn()",
    "clip": "fn()",
    "resetClip": "fn()",
    "measureText": "fn(text: string) -> ?",
    "drawImage": "fn(image: ?, dx: number, dy: number)",
    "createImageData": "fn(sw: number, sh: number) -> ?",
    "getImageData": "fn(sx: number, sy: number, sw: number, sh: number) -> ?",
    "putImageData": "fn(imagedata: ?, dx: number, dy: number)",
    "lineWidth": "number",
    "lineCap": "string",
    "lineJoin": "string",
    "miterLimit": "number",
    "setLineDash": "fn(segments: [number])",
    "getLineDash": "fn() -> [number]",
    "lineDashOffset": "number",
    "font": "string",
    "textAlign": "string",
    "textBaseline": "string",
    "direction": "string",
    "closePath": "fn()",
    "moveTo": "fn(x: number, y: number)",
    "lineTo": "fn(x: number, y: number)",
    "quadraticCurveTo": "fn(cpx: number, cpy: number, x: number, y: number)",
    "bezierCurveTo": "fn(cp1x: number, cp1y: number, cp2x: number, cp2y: number, x: number, y: number)",
    "arcTo": "fn(x1: number, y1: number, x2: number, y2: number, radius: number)",
    "rect": "fn(x: number, y: number, w: number, h: number)",
    "arc": "fn(x: number, y: number, radius: number, startAngle: number, endAngle: number, anticlockwise?: bool)",
    "ellipse": "fn(x: number, y: number, radiusX: number, radiusY: number, rotation: number, startAngle: number, endAngle: number, anticlockwise: bool)"
  }
}
PK
!<���KKDchrome/devtools/modules/devtools/client/sourceeditor/tern/comment.js(function(mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    return mod(exports);
  if (typeof define == "function" && define.amd) // AMD
    return define(["exports"], mod);
  mod(tern.comment || (tern.comment = {}));
})(function(exports) {
  function isSpace(ch) {
    return (ch < 14 && ch > 8) || ch === 32 || ch === 160;
  }

  function onOwnLine(text, pos) {
    for (; pos > 0; --pos) {
      var ch = text.charCodeAt(pos - 1);
      if (ch == 10) break;
      if (!isSpace(ch)) return false;
    }
    return true;
  }

  // Gather comments directly before a function
  exports.commentsBefore = function(text, pos) {
    var found = null, emptyLines = 0, topIsLineComment;
    out: while (pos > 0) {
      var prev = text.charCodeAt(pos - 1);
      if (prev == 10) {
        for (var scan = --pos, sawNonWS = false; scan > 0; --scan) {
          prev = text.charCodeAt(scan - 1);
          if (prev == 47 && text.charCodeAt(scan - 2) == 47) {
            if (!onOwnLine(text, scan - 2)) break out;
            var content = text.slice(scan, pos);
            if (!emptyLines && topIsLineComment) found[0] = content + "\n" + found[0];
            else (found || (found = [])).unshift(content);
            topIsLineComment = true;
            emptyLines = 0;
            pos = scan - 2;
            break;
          } else if (prev == 10) {
            if (!sawNonWS && ++emptyLines > 1) break out;
            break;
          } else if (!sawNonWS && !isSpace(prev)) {
            sawNonWS = true;
          }
        }
      } else if (prev == 47 && text.charCodeAt(pos - 2) == 42) {
        for (var scan = pos - 2; scan > 1; --scan) {
          if (text.charCodeAt(scan - 1) == 42 && text.charCodeAt(scan - 2) == 47) {
            if (!onOwnLine(text, scan - 2)) break out;
            (found || (found = [])).unshift(text.slice(scan, pos - 2));
            topIsLineComment = false;
            emptyLines = 0;
            break;
          }
        }
        pos = scan - 2;
      } else if (isSpace(prev)) {
        --pos;
      } else {
        break;
      }
    }
    return found;
  };

  exports.commentAfter = function(text, pos) {
    while (pos < text.length) {
      var next = text.charCodeAt(pos);
      if (next == 47) {
        var after = text.charCodeAt(pos + 1), end;
        if (after == 47) // line comment
          end = text.indexOf("\n", pos + 2);
        else if (after == 42) // block comment
          end = text.indexOf("*/", pos + 2);
        else
          return;
        return text.slice(pos + 2, end < 0 ? text.length : end);
      } else if (isSpace(next)) {
        ++pos;
      }
    }
  };

  exports.ensureCommentsBefore = function(text, node) {
    if (node.hasOwnProperty("commentsBefore")) return node.commentsBefore;
    return node.commentsBefore = exports.commentsBefore(text, node.start);
  };
});
PK
!<�y�(�(Echrome/devtools/modules/devtools/client/sourceeditor/tern/condense.js// Condensing an inferred set of types to a JSON description document.

// This code can be used to, after a library has been analyzed,
// extract the types defined in that library and dump them as a JSON
// structure (as parsed by def.js).

// The idea being that big libraries can be analyzed once, dumped, and
// then cheaply included in later analysis.

(function(root, mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    return mod(exports, require("./infer"));
  if (typeof define == "function" && define.amd) // AMD
    return define(["exports", "./infer"], mod);
  mod(root.tern || (root.tern = {}), tern); // Plain browser env
})(this, function(exports, infer) {
  "use strict";

  exports.condense = function(origins, name, options) {
    if (typeof origins == "string") origins = [origins];
    var state = new State(origins, name || origins[0], options || {});

    state.server.signal("preCondenseReach", state)

    state.cx.topScope.path = "<top>";
    state.cx.topScope.reached("", state);
    for (var path in state.roots)
      reach(state.roots[path], null, path, state);
    for (var i = 0; i < state.patchUp.length; ++i)
      patchUpSimpleInstance(state.patchUp[i], state);

    state.server.signal("postCondenseReach", state)

    for (var path in state.types)
      store(createPath(path.split("."), state), state.types[path], state);
    for (var path in state.altPaths)
      storeAlt(path, state.altPaths[path], state);
    var hasDef = false;
    for (var _def in state.output["!define"]) { hasDef = true; break; }
    if (!hasDef) delete state.output["!define"];

    state.server.signal("postCondense", state)

    return simplify(state.output, state.options.sortOutput);
  };

  function State(origins, name, options) {
    this.origins = origins;
    this.cx = infer.cx();
    this.server = options.server || this.cx.parent || {signal: function() {}}
    this.maxOrigin = -Infinity;
    for (var i = 0; i < origins.length; ++i)
      this.maxOrigin = Math.max(this.maxOrigin, this.cx.origins.indexOf(origins[i]));
    this.output = {"!name": name, "!define": {}};
    this.options = options;
    this.types = Object.create(null);
    this.altPaths = Object.create(null);
    this.patchUp = [];
    this.roots = Object.create(null);
  }

  State.prototype.isTarget = function(origin) {
    return this.origins.indexOf(origin) > -1;
  };

  State.prototype.getSpan = function(node) {
    if (this.options.spans == false || !this.isTarget(node.origin)) return null;
    if (node.span) return node.span;
    var srv = this.cx.parent, file;
    if (!srv || !node.originNode || !(file = srv.findFile(node.origin))) return null;
    var start = node.originNode.start, end = node.originNode.end;
    var pStart = file.asLineChar(start), pEnd = file.asLineChar(end);
    return start + "[" + pStart.line + ":" + pStart.ch + "]-" +
      end + "[" + pEnd.line + ":" + pEnd.ch + "]";
  };

  function pathLen(path) {
    var len = 1, pos = 0, dot;
    while ((dot = path.indexOf(".", pos)) != -1) {
      pos = dot + 1;
      len += path.charAt(pos) == "!" ? 10 : 1;
    }
    return len;
  }

  function hop(obj, prop) {
    return Object.prototype.hasOwnProperty.call(obj, prop);
  }

  function isSimpleInstance(o) {
    return o.proto && !(o instanceof infer.Fn) && o.proto != infer.cx().protos.Object &&
      o.proto.hasCtor && !o.hasCtor;
  }

  function reach(type, path, id, state, byName) {
    var actual = type.getType(false);
    if (!actual) return;
    var orig = type.origin || actual.origin, relevant = false;
    if (orig) {
      var origPos = state.cx.origins.indexOf(orig);
      // This is a path that is newer than the code we are interested in.
      if (origPos > state.maxOrigin) return;
      relevant = state.isTarget(orig);
    }
    var newPath = path ? path + "." + id : id, oldPath = actual.path;
    var shorter = !oldPath || pathLen(oldPath) > pathLen(newPath);
    if (shorter) {
      if (!(actual instanceof infer.Prim)) actual.path = newPath;
      if (actual.reached(newPath, state, !relevant) && relevant) {
        var data = state.types[oldPath];
        if (data) {
          delete state.types[oldPath];
          state.altPaths[oldPath] = actual;
        } else data = {type: actual};
        data.span = state.getSpan(type) || (actual != type && state.isTarget(actual.origin) && state.getSpan(actual)) || data.span;
        data.doc = type.doc || (actual != type && state.isTarget(actual.origin) && actual.doc) || data.doc;
        data.data = actual.metaData;
        data.byName = data.byName == null ? !!byName : data.byName && byName;
        state.types[newPath] = data;
      }
    } else {
      if (relevant) state.altPaths[newPath] = actual;
    }
  }
  function reachByName(aval, path, id, state) {
    var type = aval.getType();
    if (type) reach(type, path, id, state, true);
  }

  infer.Prim.prototype.reached = function() {return true;};

  infer.Arr.prototype.reached = function(path, state, concrete) {
    if (concrete) return true
    if (this.tuple) {
      for (var i = 0; i < this.tuple; i++)
        reachByName(this.getProp(String(i)), path, String(i), state)
    } else {
      reachByName(this.getProp("<i>"), path, "<i>", state)
    }
    return true;
  };

  infer.Fn.prototype.reached = function(path, state, concrete) {
    infer.Obj.prototype.reached.call(this, path, state, concrete);
    if (!concrete) {
      for (var i = 0; i < this.args.length; ++i)
        reachByName(this.args[i], path, "!" + i, state);
      reachByName(this.retval, path, "!ret", state);
    }
    return true;
  };

  infer.Obj.prototype.reached = function(path, state, concrete) {
    if (isSimpleInstance(this) && !this.condenseForceInclude) {
      if (!state.patchUp.includes(this)) state.patchUp.push(this);
      return true;
    } else if (this.proto && !concrete) {
      reach(this.proto, path, "!proto", state);
    }
    var hasProps = false;
    for (var prop in this.props) {
      reach(this.props[prop], path, prop, state);
      hasProps = true;
    }
    if (!hasProps && !this.condenseForceInclude && !(this instanceof infer.Fn)) {
      this.nameOverride = "?";
      return false;
    }
    return true;
  };

  function patchUpSimpleInstance(obj, state) {
    var path = obj.proto.hasCtor.path;
    if (path) {
      obj.nameOverride = "+" + path;
    } else {
      path = obj.path;
    }
    for (var prop in obj.props)
      reach(obj.props[prop], path, prop, state);
  }

  function createPath(parts, state) {
    var base = state.output, defs = state.output["!define"];
    for (var i = 0, path; i < parts.length; ++i) {
      var part = parts[i];
      path = path ? path + "." + part : part;
      var me = state.types[path];
      if (part.charAt(0) == "!" || me && me.byName) {
        if (hop(defs, path)) base = defs[path];
        else defs[path] = base = {};
      } else {
        if (hop(base, parts[i])) base = base[part];
        else base = base[part] = {};
      }
    }
    return base;
  }

  function store(out, info, state) {
    var name = typeName(info.type);
    if (name != info.type.path && name != "?") {
      out["!type"] = name;
    } else if (info.type.proto && info.type.proto != state.cx.protos.Object) {
      var protoName = typeName(info.type.proto);
      if (protoName != "?") out["!proto"] = protoName;
    }
    if (info.span) out["!span"] = info.span;
    if (info.doc) out["!doc"] = info.doc;
    if (info.data) out["!data"] = info.data;
  }

  function storeAlt(path, type, state) {
    var parts = path.split("."), last = parts.pop();
    if (last[0] == "!") return;
    var known = state.types[parts.join(".")];
    var base = createPath(parts, state);
    if (known && known.type.constructor != infer.Obj) return;
    if (!hop(base, last)) base[last] = type.nameOverride || type.path;
  }

  var typeNameStack = [];
  function typeName(value) {
    var isType = value instanceof infer.Type;
    if (isType) {
      if (typeNameStack.indexOf(value) > -1)
        return value.path || "?";
      typeNameStack.push(value);
    }
    var name = value.typeName();
    if (isType) typeNameStack.pop();
    return name;
  }

  infer.AVal.prototype.typeName = function() {
    if (this.types.length == 0) return "?";
    if (this.types.length == 1) return typeName(this.types[0]);
    var simplified = infer.simplifyTypes(this.types);
    if (simplified.length > 2) return "?";
    for (var strs = [], i = 0; i < simplified.length; i++)
      strs.push(typeName(simplified[i]));
    return strs.join("|");
  };

  infer.ANull.typeName = function() { return "?"; };

  infer.Prim.prototype.typeName = function() { return this.name; };

  infer.Sym.prototype.typeName = function() { return this.asPropName }

  infer.Arr.prototype.typeName = function() {
    if (!this.tuple) return "[" + typeName(this.getProp("<i>")) + "]"
    var content = []
    for (var i = 0; i < this.tuple; i++)
      content.push(typeName(this.getProp(String(i))))
    return "[" + content.join(", ") + "]"
  };

  infer.Fn.prototype.typeName = function() {
    var out = this.generator ? "fn*(" : "fn(";
    for (var i = 0; i < this.args.length; ++i) {
      if (i) out += ", ";
      var name = this.argNames[i];
      if (name && name != "?") out += name + ": ";
      out += typeName(this.args[i]);
    }
    out += ")";
    if (this.computeRetSource)
      out += " -> " + this.computeRetSource;
    else if (!this.retval.isEmpty())
      out += " -> " + typeName(this.retval);
    return out;
  };

  infer.Obj.prototype.typeName = function() {
    if (this.nameOverride) return this.nameOverride;
    if (!this.path) return "?";
    return this.path;
  };

  function simplify(data, sort) {
    if (typeof data != "object") return data;
    var sawType = false, sawOther = false;
    for (var prop in data) {
      if (prop == "!type") sawType = true;
      else sawOther = true;
      if (prop != "!data")
        data[prop] = simplify(data[prop], sort);
    }
    if (sawType && !sawOther) return data["!type"];
    return sort ? sortObject(data) : data;
  }

  function sortObject(obj) {
    var props = [], out = {};
    for (var prop in obj) props.push(prop);
    props.sort();
    for (var i = 0; i < props.length; ++i) {
      var prop = props[i];
      out[prop] = obj[prop];
    }
    return out;
  }
});
PK
!<�P�2Z2Z@chrome/devtools/modules/devtools/client/sourceeditor/tern/def.js// Type description parser
//
// Type description JSON files (such as ecma5.json and browser.json)
// are used to
//
// A) describe types that come from native code
//
// B) to cheaply load the types for big libraries, or libraries that
//    can't be inferred well

(function(mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    return exports.init = mod;
  if (typeof define == "function" && define.amd) // AMD
    return define({init: mod});
  tern.def = {init: mod};
})(function(exports, infer) {
  "use strict";

  function hop(obj, prop) {
    return Object.prototype.hasOwnProperty.call(obj, prop);
  }

  var TypeParser = exports.TypeParser = function(spec, start, base, forceNew) {
    this.pos = start || 0;
    this.spec = spec;
    this.base = base;
    this.forceNew = forceNew;
  };

  function unwrapType(type, self, args) {
    return type.call ? type(self, args) : type;
  }

  function extractProp(type, prop) {
    if (prop == "!ret") {
      if (type.retval) return type.retval;
      var rv = new infer.AVal;
      type.propagate(new infer.IsCallee(infer.ANull, [], null, rv));
      return rv;
    } else {
      return type.getProp(prop);
    }
  }

  function computedFunc(name, args, retType, generator) {
    return function(self, cArgs) {
      var realArgs = [];
      for (var i = 0; i < args.length; i++) realArgs.push(unwrapType(args[i], self, cArgs));
      return new infer.Fn(name, infer.ANull, realArgs, unwrapType(retType, self, cArgs), generator);
    };
  }
  function computedUnion(types) {
    return function(self, args) {
      var union = new infer.AVal;
      for (var i = 0; i < types.length; i++) unwrapType(types[i], self, args).propagate(union);
      union.maxWeight = 1e5;
      return union;
    };
  }
  function computedArray(inner) {
    return function(self, args) {
      return new infer.Arr(inner(self, args));
    };
  }
  function computedTuple(types) {
    return function(self, args) {
      return new infer.Arr(types.map(function(tp) { return unwrapType(tp, self, args) }))
    }
  }

  TypeParser.prototype = {
    eat: function(str) {
      if (str.length == 1 ? this.spec.charAt(this.pos) == str : this.spec.indexOf(str, this.pos) == this.pos) {
        this.pos += str.length;
        return true;
      }
    },
    word: function(re) {
      var word = "", ch, re = re || /[\w$]/;
      while ((ch = this.spec.charAt(this.pos)) && re.test(ch)) { word += ch; ++this.pos; }
      return word;
    },
    error: function() {
      throw new Error("Unrecognized type spec: " + this.spec + " (at " + this.pos + ")");
    },
    parseFnType: function(comp, name, top, generator) {
      var args = [], names = [], computed = false;
      if (!this.eat(")")) for (var i = 0; ; ++i) {
        var colon = this.spec.indexOf(": ", this.pos), argname;
        if (colon != -1) {
          argname = this.spec.slice(this.pos, colon);
          if (/^[$\w?]+$/.test(argname))
            this.pos = colon + 2;
          else
            argname = null;
        }
        names.push(argname);
        var argType = this.parseType(comp);
        if (argType.call) computed = true;
        args.push(argType);
        if (!this.eat(", ")) {
          this.eat(")") || this.error();
          break;
        }
      }
      var retType, computeRet, computeRetStart, fn;
      if (this.eat(" -> ")) {
        var retStart = this.pos;
        retType = this.parseType(true);
        if (retType.call && !computed) {
          computeRet = retType;
          retType = infer.ANull;
          computeRetStart = retStart;
        }
      } else {
        retType = infer.ANull;
      }
      if (computed) return computedFunc(name, args, retType, generator);

      if (top && (fn = this.base))
        infer.Fn.call(this.base, name, infer.ANull, args, names, retType, generator);
      else
        fn = new infer.Fn(name, infer.ANull, args, names, retType, generator);
      if (computeRet) fn.computeRet = computeRet;
      if (computeRetStart != null) fn.computeRetSource = this.spec.slice(computeRetStart, this.pos);
      return fn;
    },
    parseType: function(comp, name, top) {
      var main = this.parseTypeMaybeProp(comp, name, top);
      if (!this.eat("|")) return main;
      var types = [main], computed = main.call;
      for (;;) {
        var next = this.parseTypeMaybeProp(comp, name, top);
        types.push(next);
        if (next.call) computed = true;
        if (!this.eat("|")) break;
      }
      if (computed) return computedUnion(types);
      var union = new infer.AVal;
      for (var i = 0; i < types.length; i++) types[i].propagate(union);
      union.maxWeight = 1e5;
      return union;
    },
    parseTypeMaybeProp: function(comp, name, top) {
      var result = this.parseTypeInner(comp, name, top);
      while (comp && this.eat(".")) result = this.extendWithProp(result);
      return result;
    },
    extendWithProp: function(base) {
      var propName = this.word(/[\w<>$!:]/) || this.error();
      if (base.apply) return function(self, args) {
        return extractProp(base(self, args), propName);
      };
      return extractProp(base, propName);
    },
    parseTypeInner: function(comp, name, top) {
      var gen
      if (this.eat("fn(") || (gen = this.eat("fn*("))) {
        return this.parseFnType(comp, name, top, gen);
      } else if (this.eat("[")) {
        var inner = this.parseType(comp), types, computed = inner.call
        while (this.eat(", ")) {
          if (!types) types = [inner]
          var next = this.parseType(comp)
          types.push(next)
          computed = computed || next.call
        }
        this.eat("]") || this.error()
        if (computed) return types ? computedTuple(types) : computedArray(inner)
        if (top && this.base) {
          infer.Arr.call(this.base, types || inner)
          return this.base
        }
        return new infer.Arr(types || inner)
      } else if (this.eat("+")) {
        var path = this.word(/[\w$<>\.:!]/)
        var base = infer.cx().localDefs[path + ".prototype"]
        if (!base) {
          var base = parsePath(path);
          if (!(base instanceof infer.Obj)) return base;
          var proto = descendProps(base, ["prototype"])
          if (proto && (proto = proto.getObjType()))
            base = proto
        }
        if (comp && this.eat("[")) return this.parsePoly(base);
        if (top && this.forceNew) return new infer.Obj(base);
        return infer.getInstance(base);
      } else if (this.eat(":")) {
        var name = this.word(/[\w$\.]/)
        return infer.getSymbol(name)
      } else if (comp && this.eat("!")) {
        var arg = this.word(/\d/);
        if (arg) {
          arg = Number(arg);
          return function(_self, args) {return args[arg] || infer.ANull;};
        } else if (this.eat("this")) {
          return function(self) {return self;};
        } else if (this.eat("custom:")) {
          var fname = this.word(/[\w$]/);
          return customFunctions[fname] || function() { return infer.ANull; };
        } else {
          return this.fromWord("!" + this.word(/[\w$<>\.!:]/));
        }
      } else if (this.eat("?")) {
        return infer.ANull;
      } else {
        return this.fromWord(this.word(/[\w$<>\.!:`]/));
      }
    },
    fromWord: function(spec) {
      var cx = infer.cx();
      switch (spec) {
      case "number": return cx.num;
      case "string": return cx.str;
      case "bool": return cx.bool;
      case "<top>": return cx.topScope;
      }
      if (cx.localDefs && spec in cx.localDefs) return cx.localDefs[spec];
      return parsePath(spec);
    },
    parsePoly: function(base) {
      var propName = "<i>", match;
      if (match = this.spec.slice(this.pos).match(/^\s*([\w$:]+)\s*=\s*/)) {
        propName = match[1];
        this.pos += match[0].length;
      }
      var value = this.parseType(true);
      if (!this.eat("]")) this.error();
      if (value.call) return function(self, args) {
        var instance = new infer.Obj(base);
        value(self, args).propagate(instance.defProp(propName));
        return instance;
      };
      var instance = new infer.Obj(base);
      value.propagate(instance.defProp(propName));
      return instance;
    }
  };

  function parseType(spec, name, base, forceNew) {
    var type = new TypeParser(spec, null, base, forceNew).parseType(false, name, true);
    if (/^fn\(/.test(spec)) for (var i = 0; i < type.args.length; ++i) (function(i) {
      var arg = type.args[i];
      if (arg instanceof infer.Fn && arg.args && arg.args.length) addEffect(type, function(_self, fArgs) {
        var fArg = fArgs[i];
        if (fArg) fArg.propagate(new infer.IsCallee(infer.cx().topScope, arg.args, null, infer.ANull));
      });
    })(i);
    return type;
  }

  function addEffect(fn, handler, replaceRet) {
    var oldCmp = fn.computeRet, rv = fn.retval;
    fn.computeRet = function(self, args, argNodes) {
      var handled = handler(self, args, argNodes);
      var old = oldCmp ? oldCmp(self, args, argNodes) : rv;
      return replaceRet ? handled : old;
    };
  }

  var parseEffect = exports.parseEffect = function(effect, fn) {
    var m;
    if (effect.indexOf("propagate ") == 0) {
      var p = new TypeParser(effect, 10);
      var origin = p.parseType(true);
      if (!p.eat(" ")) p.error();
      var target = p.parseType(true);
      addEffect(fn, function(self, args) {
        unwrapType(origin, self, args).propagate(unwrapType(target, self, args));
      });
    } else if (effect.indexOf("call ") == 0) {
      var andRet = effect.indexOf("and return ", 5) == 5;
      var p = new TypeParser(effect, andRet ? 16 : 5);
      var getCallee = p.parseType(true), getSelf = null, getArgs = [];
      if (p.eat(" this=")) getSelf = p.parseType(true);
      while (p.eat(" ")) getArgs.push(p.parseType(true));
      addEffect(fn, function(self, args) {
        var callee = unwrapType(getCallee, self, args);
        var slf = getSelf ? unwrapType(getSelf, self, args) : infer.ANull, as = [];
        for (var i = 0; i < getArgs.length; ++i) as.push(unwrapType(getArgs[i], self, args));
        var result = andRet ? new infer.AVal : infer.ANull;
        callee.propagate(new infer.IsCallee(slf, as, null, result));
        return result;
      }, andRet);
    } else if (m = effect.match(/^custom (\S+)\s*(.*)/)) {
      var customFunc = customFunctions[m[1]];
      if (customFunc) addEffect(fn, m[2] ? customFunc(m[2]) : customFunc);
    } else if (effect.indexOf("copy ") == 0) {
      var p = new TypeParser(effect, 5);
      var getFrom = p.parseType(true);
      p.eat(" ");
      var getTo = p.parseType(true);
      addEffect(fn, function(self, args) {
        var from = unwrapType(getFrom, self, args), to = unwrapType(getTo, self, args);
        from.forAllProps(function(prop, val, local) {
          if (local && prop != "<i>")
            to.propagate(new infer.DefProp(prop, val));
        });
      });
    } else {
      throw new Error("Unknown effect type: " + effect);
    }
  };

  var currentTopScope;

  var parsePath = exports.parsePath = function(path, scope) {
    var cx = infer.cx(), cached = cx.paths[path], origPath = path;
    if (cached != null) return cached;
    cx.paths[path] = infer.ANull;

    var base = scope || currentTopScope || cx.topScope;

    if (cx.localDefs) for (var name in cx.localDefs) {
      if (path.indexOf(name) == 0) {
        if (path == name) return cx.paths[path] = cx.localDefs[path];
        if (path.charAt(name.length) == ".") {
          base = cx.localDefs[name];
          path = path.slice(name.length + 1);
          break;
        }
      }
    }

    var result = descendProps(base, path.split("."))
    // Uncomment this to get feedback on your poorly written .json files
    // if (result == infer.ANull) console.error("bad path: " + origPath + " (" + cx.curOrigin + ")")
    cx.paths[origPath] = result == infer.ANull ? null : result
    return result
  }

  function descendProps(base, parts) {
    for (var i = 0; i < parts.length && base != infer.ANull; ++i) {
      var prop = parts[i];
      if (prop.charAt(0) == "!") {
        if (prop == "!proto") {
          base = (base instanceof infer.Obj && base.proto) || infer.ANull;
        } else {
          var fn = base.getFunctionType();
          if (!fn) {
            base = infer.ANull;
          } else if (prop == "!ret") {
            base = fn.retval && fn.retval.getType(false) || infer.ANull;
          } else {
            var arg = fn.args && fn.args[Number(prop.slice(1))];
            base = (arg && arg.getType(false)) || infer.ANull;
          }
        }
      } else if (base instanceof infer.Obj) {
        var propVal = (prop == "prototype" && base instanceof infer.Fn) ? base.getProp(prop) : base.props[prop];
        if (!propVal || propVal.isEmpty())
          base = infer.ANull;
        else
          base = propVal.types[0];
      }
    }
    return base;
  }

  function emptyObj(ctor) {
    var empty = Object.create(ctor.prototype);
    empty.props = Object.create(null);
    empty.isShell = true;
    return empty;
  }

  function isSimpleAnnotation(spec) {
    if (!spec["!type"] || /^(fn\(|\[)/.test(spec["!type"])) return false;
    for (var prop in spec)
      if (prop != "!type" && prop != "!doc" && prop != "!url" && prop != "!span" && prop != "!data")
        return false;
    return true;
  }

  function passOne(base, spec, path) {
    if (!base) {
      var tp = spec["!type"];
      if (tp) {
        if (/^fn\(/.test(tp)) base = emptyObj(infer.Fn);
        else if (tp.charAt(0) == "[") base = emptyObj(infer.Arr);
        else throw new Error("Invalid !type spec: " + tp);
      } else if (spec["!stdProto"]) {
        base = infer.cx().protos[spec["!stdProto"]];
      } else {
        base = emptyObj(infer.Obj);
      }
      base.name = path;
    }

    for (var name in spec) if (hop(spec, name) && name.charCodeAt(0) != 33) {
      var inner = spec[name];
      if (typeof inner == "string" || isSimpleAnnotation(inner)) continue;
      var prop = base.defProp(name);
      passOne(prop.getObjType(), inner, path ? path + "." + name : name).propagate(prop);
    }
    return base;
  }

  function passTwo(base, spec, path) {
    if (base.isShell) {
      delete base.isShell;
      var tp = spec["!type"];
      if (tp) {
        parseType(tp, path, base);
      } else {
        var proto = spec["!proto"] && parseType(spec["!proto"]);
        infer.Obj.call(base, proto instanceof infer.Obj ? proto : true, path);
      }
    }

    var effects = spec["!effects"];
    if (effects && base instanceof infer.Fn) for (var i = 0; i < effects.length; ++i)
      parseEffect(effects[i], base);
    copyInfo(spec, base);

    for (var name in spec) if (hop(spec, name) && name.charCodeAt(0) != 33) {
      var inner = spec[name], known = base.defProp(name), innerPath = path ? path + "." + name : name;
      if (typeof inner == "string") {
        if (known.isEmpty()) parseType(inner, innerPath).propagate(known);
      } else {
        if (!isSimpleAnnotation(inner))
          passTwo(known.getObjType(), inner, innerPath);
        else if (known.isEmpty())
          parseType(inner["!type"], innerPath, null, true).propagate(known);
        else
          continue;
        if (inner["!doc"]) known.doc = inner["!doc"];
        if (inner["!url"]) known.url = inner["!url"];
        if (inner["!span"]) known.span = inner["!span"];
      }
    }
    return base;
  }

  function copyInfo(spec, type) {
    if (spec["!doc"]) type.doc = spec["!doc"];
    if (spec["!url"]) type.url = spec["!url"];
    if (spec["!span"]) type.span = spec["!span"];
    if (spec["!data"]) type.metaData = spec["!data"];
  }

  function doLoadEnvironment(data, scope) {
    var cx = infer.cx(), server = cx.parent

    infer.addOrigin(cx.curOrigin = data["!name"] || "env#" + cx.origins.length);
    cx.localDefs = cx.definitions[cx.curOrigin] = Object.create(null);

    if (server) server.signal("preLoadDef", data)

    passOne(scope, data);

    var def = data["!define"];
    if (def) {
      for (var name in def) {
        var spec = def[name];
        cx.localDefs[name] = typeof spec == "string" ? parsePath(spec) : passOne(null, spec, name);
      }
      for (var name in def) {
        var spec = def[name];
        if (typeof spec != "string") passTwo(cx.localDefs[name], def[name], name);
      }
    }

    passTwo(scope, data);

    if (server) server.signal("postLoadDef", data)

    cx.curOrigin = cx.localDefs = null;
  }

  exports.load = function(data, scope) {
    if (!scope) scope = infer.cx().topScope;
    var oldScope = currentTopScope;
    currentTopScope = scope;
    try {
      doLoadEnvironment(data, scope);
    } finally {
      currentTopScope = oldScope;
    }
  };

  exports.parse = function(data, origin, path) {
    var cx = infer.cx();
    if (origin) {
      cx.origin = origin;
      cx.localDefs = cx.definitions[origin];
    }

    try {
      if (typeof data == "string")
        return parseType(data, path);
      else
        return passTwo(passOne(null, data, path), data, path);
    } finally {
      if (origin) cx.origin = cx.localDefs = null;
    }
  };

  // Used to register custom logic for more involved effect or type
  // computation.
  var customFunctions = Object.create(null);
  infer.registerFunction = function(name, f) { customFunctions[name] = f; };

  var IsCreated = infer.constraint({
    construct: function(created, target, spec) {
      this.created = created;
      this.target = target;
      this.spec = spec;
    },
    addType: function(tp) {
      if (tp instanceof infer.Obj && this.created++ < 5) {
        var derived = new infer.Obj(tp), spec = this.spec;
        if (spec instanceof infer.AVal) spec = spec.getObjType(false);
        if (spec instanceof infer.Obj) for (var prop in spec.props) {
          var cur = spec.props[prop].types[0];
          var p = derived.defProp(prop);
          if (cur && cur instanceof infer.Obj && cur.props.value) {
            var vtp = cur.props.value.getType(false);
            if (vtp) p.addType(vtp);
          }
        }
        this.target.addType(derived);
      }
    }
  });

  infer.registerFunction("Object_create", function(_self, args, argNodes) {
    if (argNodes && argNodes.length && argNodes[0].type == "Literal" && argNodes[0].value == null)
      return new infer.Obj();

    var result = new infer.AVal;
    if (args[0]) args[0].propagate(new IsCreated(0, result, args[1]));
    return result;
  });

  var PropSpec = infer.constraint({
    construct: function(target) { this.target = target; },
    addType: function(tp) {
      if (!(tp instanceof infer.Obj)) return;
      if (tp.hasProp("value"))
        tp.getProp("value").propagate(this.target);
      else if (tp.hasProp("get"))
        tp.getProp("get").propagate(new infer.IsCallee(infer.ANull, [], null, this.target));
    }
  });

  infer.registerFunction("Object_defineProperty", function(_self, args, argNodes) {
    if (argNodes && argNodes.length >= 3 && argNodes[1].type == "Literal" &&
        typeof argNodes[1].value == "string") {
      var obj = args[0], connect = new infer.AVal;
      obj.propagate(new infer.DefProp(argNodes[1].value, connect, argNodes[1]));
      args[2].propagate(new PropSpec(connect));
    }
    return infer.ANull;
  });

  infer.registerFunction("Object_defineProperties", function(_self, args, argNodes) {
    if (args.length >= 2) {
      var obj = args[0];
      args[1].forAllProps(function(prop, val, local) {
        if (!local) return;
        var connect = new infer.AVal;
        obj.propagate(new infer.DefProp(prop, connect, argNodes && argNodes[1]));
        val.propagate(new PropSpec(connect));
      });
    }
    return infer.ANull;
  });

  var IsBound = infer.constraint({
    construct: function(self, args, target) {
      this.self = self; this.args = args; this.target = target;
    },
    addType: function(tp) {
      if (!(tp instanceof infer.Fn)) return;
      this.target.addType(new infer.Fn(tp.name, infer.ANull, tp.args.slice(this.args.length),
                                       tp.argNames.slice(this.args.length), tp.retval, tp.generator));
      this.self.propagate(tp.self);
      for (var i = 0; i < Math.min(tp.args.length, this.args.length); ++i)
        this.args[i].propagate(tp.args[i]);
    }
  });

  infer.registerFunction("Function_bind", function(self, args) {
    if (!args.length) return infer.ANull;
    var result = new infer.AVal;
    self.propagate(new IsBound(args[0], args.slice(1), result));
    return result;
  });

  infer.registerFunction("Array_ctor", function(_self, args) {
    var arr = new infer.Arr;
    if (args.length != 1 || !args[0].hasType(infer.cx().num)) {
      var content = arr.getProp("<i>");
      for (var i = 0; i < args.length; ++i) args[i].propagate(content);
    }
    return arr;
  });

  infer.registerFunction("Promise_ctor", function(_self, args, argNodes) {
    var defs6 = infer.cx().definitions.ecma6
    if (!defs6 || args.length < 1) return infer.ANull;
    var self = new infer.Obj(defs6["Promise.prototype"]);
    var valProp = self.defProp(":t", argNodes && argNodes[0]);
    var valArg = new infer.AVal;
    valArg.propagate(valProp);
    var exec = new infer.Fn("execute", infer.ANull, [valArg], ["value"], infer.ANull);
    var reject = defs6.Promise_reject;
    args[0].propagate(new infer.IsCallee(infer.ANull, [exec, reject], null, infer.ANull));
    return self;
  });

  var PromiseResolvesTo = infer.constraint({
    construct: function(output) { this.output = output; },
    addType: function(tp) {
      if (tp.constructor == infer.Obj && tp.name == "Promise" && tp.hasProp(":t"))
        tp.getProp(":t").propagate(this.output);
      else
        tp.propagate(this.output);
    }
  });

  var WG_PROMISE_KEEP_VALUE = 50;

  infer.registerFunction("Promise_then", function(self, args, argNodes) {
    var fn = args.length && args[0].getFunctionType();
    var defs6 = infer.cx().definitions.ecma6
    if (!fn || !defs6) return self;

    var result = new infer.Obj(defs6["Promise.prototype"]);
    var value = result.defProp(":t", argNodes && argNodes[0]), ty;
    if (fn.retval.isEmpty() && (ty = self.getType()) instanceof infer.Obj && ty.hasProp(":t"))
      ty.getProp(":t").propagate(value, WG_PROMISE_KEEP_VALUE);
    fn.retval.propagate(new PromiseResolvesTo(value));
    return result;
  });

  infer.registerFunction("getOwnPropertySymbols", function(_self, args) {
    if (!args.length) return infer.ANull
    var result = new infer.AVal
    args[0].forAllProps(function(prop, _val, local) {
      if (local && prop.charAt(0) == ":") result.addType(infer.getSymbol(prop.slice(1)))
    })
    return result
  })

  infer.registerFunction("getSymbol", function(_self, _args, argNodes) {
    if (argNodes.length && argNodes[0].type == "Literal" && typeof argNodes[0].value == "string")
      return infer.getSymbol(argNodes[0].value)
    else
      return infer.ANull
  })

  return exports;
});
PK
!<�c�9�9�Bchrome/devtools/modules/devtools/client/sourceeditor/tern/ecma5.jsmodule.exports = {
  "!name": "ecma5",
  "!define": {"Error.prototype": "Error.prototype"},
  "Infinity": {
    "!type": "number",
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Infinity",
    "!doc": "A numeric value representing infinity."
  },
  "undefined": {
    "!type": "?",
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/undefined",
    "!doc": "The value undefined."
  },
  "NaN": {
    "!type": "number",
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/NaN",
    "!doc": "A value representing Not-A-Number."
  },
  "Object": {
    "!type": "fn()",
    "getPrototypeOf": {
      "!type": "fn(obj: ?) -> ?",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/getPrototypeOf",
      "!doc": "Returns the prototype (i.e. the internal prototype) of the specified object."
    },
    "create": {
      "!type": "fn(proto: ?) -> !custom:Object_create",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/create",
      "!doc": "Creates a new object with the specified prototype object and properties."
    },
    "defineProperty": {
      "!type": "fn(obj: ?, prop: string, desc: ?)",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/defineProperty",
      "!doc": "Defines a new property directly on an object, or modifies an existing property on an object, and returns the object. If you want to see how to use the Object.defineProperty method with a binary-flags-like syntax, see this article."
    },
    "defineProperties": {
      "!type": "fn(obj: ?, props: ?)",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/defineProperty",
      "!doc": "Defines a new property directly on an object, or modifies an existing property on an object, and returns the object. If you want to see how to use the Object.defineProperty method with a binary-flags-like syntax, see this article."
    },
    "getOwnPropertyDescriptor": {
      "!type": "fn(obj: ?, prop: string) -> ?",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor",
      "!doc": "Returns a property descriptor for an own property (that is, one directly present on an object, not present by dint of being along an object's prototype chain) of a given object."
    },
    "keys": {
      "!type": "fn(obj: ?) -> [string]",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/keys",
      "!doc": "Returns an array of a given object's own enumerable properties, in the same order as that provided by a for-in loop (the difference being that a for-in loop enumerates properties in the prototype chain as well)."
    },
    "getOwnPropertyNames": {
      "!type": "fn(obj: ?) -> [string]",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames",
      "!doc": "Returns an array of all properties (enumerable or not) found directly upon a given object."
    },
    "seal": {
      "!type": "fn(obj: ?)",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/seal",
      "!doc": "Seals an object, preventing new properties from being added to it and marking all existing properties as non-configurable. Values of present properties can still be changed as long as they are writable."
    },
    "isSealed": {
      "!type": "fn(obj: ?) -> bool",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/isSealed",
      "!doc": "Determine if an object is sealed."
    },
    "freeze": {
      "!type": "fn(obj: ?)",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/freeze",
      "!doc": "Freezes an object: that is, prevents new properties from being added to it; prevents existing properties from being removed; and prevents existing properties, or their enumerability, configurability, or writability, from being changed. In essence the object is made effectively immutable. The method returns the object being frozen."
    },
    "isFrozen": {
      "!type": "fn(obj: ?) -> bool",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/isFrozen",
      "!doc": "Determine if an object is frozen."
    },
    "prototype": {
      "!stdProto": "Object",
      "toString": {
        "!type": "fn() -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/toString",
        "!doc": "Returns a string representing the object."
      },
      "toLocaleString": {
        "!type": "fn() -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/toLocaleString",
        "!doc": "Returns a string representing the object. This method is meant to be overriden by derived objects for locale-specific purposes."
      },
      "valueOf": {
        "!type": "fn() -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/valueOf",
        "!doc": "Returns the primitive value of the specified object"
      },
      "hasOwnProperty": {
        "!type": "fn(prop: string) -> bool",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/hasOwnProperty",
        "!doc": "Returns a boolean indicating whether the object has the specified property."
      },
      "propertyIsEnumerable": {
        "!type": "fn(prop: string) -> bool",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/propertyIsEnumerable",
        "!doc": "Returns a Boolean indicating whether the specified property is enumerable."
      }
    },
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object",
    "!doc": "Creates an object wrapper."
  },
  "Function": {
    "!type": "fn(body: string) -> fn()",
    "prototype": {
      "!stdProto": "Function",
      "apply": {
        "!type": "fn(this: ?, args: [?])",
        "!effects": [
          "call and return !this this=!0 !1.<i> !1.<i> !1.<i>"
        ],
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/apply",
        "!doc": "Calls a function with a given this value and arguments provided as an array (or an array like object)."
      },
      "call": {
        "!type": "fn(this: ?, args?: ?) -> !this.!ret",
        "!effects": [
          "call and return !this this=!0 !1 !2 !3 !4"
        ],
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/call",
        "!doc": "Calls a function with a given this value and arguments provided individually."
      },
      "bind": {
        "!type": "fn(this: ?, args?: ?) -> !custom:Function_bind",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind",
        "!doc": "Creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function was called."
      },
      "prototype": "?"
    },
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function",
    "!doc": "Every function in JavaScript is actually a Function object."
  },
  "Array": {
    "!type": "fn(size: number) -> !custom:Array_ctor",
    "isArray": {
      "!type": "fn(value: ?) -> bool",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/isArray",
      "!doc": "Returns true if an object is an array, false if it is not."
    },
    "prototype": {
      "!stdProto": "Array",
      "length": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/length",
        "!doc": "An unsigned, 32-bit integer that specifies the number of elements in an array."
      },
      "concat": {
        "!type": "fn(other: [?]) -> !this",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/concat",
        "!doc": "Returns a new array comprised of this array joined with other array(s) and/or value(s)."
      },
      "join": {
        "!type": "fn(separator?: string) -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/join",
        "!doc": "Joins all elements of an array into a string."
      },
      "splice": {
        "!type": "fn(pos: number, amount: number)",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/splice",
        "!doc": "Changes the content of an array, adding new elements while removing old elements."
      },
      "pop": {
        "!type": "fn() -> !this.<i>",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/pop",
        "!doc": "Removes the last element from an array and returns that element."
      },
      "push": {
        "!type": "fn(newelt: ?) -> number",
        "!effects": [
          "propagate !0 !this.<i>"
        ],
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/push",
        "!doc": "Mutates an array by appending the given elements and returning the new length of the array."
      },
      "shift": {
        "!type": "fn() -> !this.<i>",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/shift",
        "!doc": "Removes the first element from an array and returns that element. This method changes the length of the array."
      },
      "unshift": {
        "!type": "fn(newelt: ?) -> number",
        "!effects": [
          "propagate !0 !this.<i>"
        ],
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/unshift",
        "!doc": "Adds one or more elements to the beginning of an array and returns the new length of the array."
      },
      "slice": {
        "!type": "fn(from: number, to?: number) -> !this",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/slice",
        "!doc": "Returns a shallow copy of a portion of an array."
      },
      "reverse": {
        "!type": "fn()",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/reverse",
        "!doc": "Reverses an array in place.  The first array element becomes the last and the last becomes the first."
      },
      "sort": {
        "!type": "fn(compare?: fn(a: ?, b: ?) -> number)",
        "!effects": [
          "call !0 !this.<i> !this.<i>"
        ],
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/sort",
        "!doc": "Sorts the elements of an array in place and returns the array."
      },
      "indexOf": {
        "!type": "fn(elt: ?, from?: number) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/indexOf",
        "!doc": "Returns the first index at which a given element can be found in the array, or -1 if it is not present."
      },
      "lastIndexOf": {
        "!type": "fn(elt: ?, from?: number) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/lastIndexOf",
        "!doc": "Returns the last index at which a given element can be found in the array, or -1 if it is not present. The array is searched backwards, starting at fromIndex."
      },
      "every": {
        "!type": "fn(test: fn(elt: ?, i: number) -> bool, context?: ?) -> bool",
        "!effects": [
          "call !0 this=!1 !this.<i> number"
        ],
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/every",
        "!doc": "Tests whether all elements in the array pass the test implemented by the provided function."
      },
      "some": {
        "!type": "fn(test: fn(elt: ?, i: number) -> bool, context?: ?) -> bool",
        "!effects": [
          "call !0 this=!1 !this.<i> number"
        ],
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/some",
        "!doc": "Tests whether some element in the array passes the test implemented by the provided function."
      },
      "filter": {
        "!type": "fn(test: fn(elt: ?, i: number) -> bool, context?: ?) -> !this",
        "!effects": [
          "call !0 this=!1 !this.<i> number"
        ],
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/filter",
        "!doc": "Creates a new array with all elements that pass the test implemented by the provided function."
      },
      "forEach": {
        "!type": "fn(f: fn(elt: ?, i: number), context?: ?)",
        "!effects": [
          "call !0 this=!1 !this.<i> number"
        ],
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/forEach",
        "!doc": "Executes a provided function once per array element."
      },
      "map": {
        "!type": "fn(f: fn(elt: ?, i: number) -> ?, context?: ?) -> [!0.!ret]",
        "!effects": [
          "call !0 this=!1 !this.<i> number"
        ],
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/map",
        "!doc": "Creates a new array with the results of calling a provided function on every element in this array."
      },
      "reduce": {
        "!type": "fn(combine: fn(sum: ?, elt: ?, i: number) -> ?, init?: ?) -> !0.!ret",
        "!effects": [
          "call !0 !1 !this.<i> number"
        ],
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/Reduce",
        "!doc": "Apply a function against an accumulator and each value of the array (from left-to-right) as to reduce it to a single value."
      },
      "reduceRight": {
        "!type": "fn(combine: fn(sum: ?, elt: ?, i: number) -> ?, init?: ?) -> !0.!ret",
        "!effects": [
          "call !0 !1 !this.<i> number"
        ],
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/ReduceRight",
        "!doc": "Apply a function simultaneously against two values of the array (from right-to-left) as to reduce it to a single value."
      }
    },
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array",
    "!doc": "The JavaScript Array global object is a constructor for arrays, which are high-level, list-like objects."
  },
  "String": {
    "!type": "fn(value: ?) -> string",
    "fromCharCode": {
      "!type": "fn(code: number) -> string",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/fromCharCode",
      "!doc": "Returns a string created by using the specified sequence of Unicode values."
    },
    "prototype": {
      "!stdProto": "String",
      "length": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en/docs/JavaScript/Reference/Global_Objects/String/length",
        "!doc": "Represents the length of a string."
      },
      "<i>": "string",
      "charAt": {
        "!type": "fn(i: number) -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/charAt",
        "!doc": "Returns the specified character from a string."
      },
      "charCodeAt": {
        "!type": "fn(i: number) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/charCodeAt",
        "!doc": "Returns the numeric Unicode value of the character at the given index (except for unicode codepoints > 0x10000)."
      },
      "indexOf": {
        "!type": "fn(char: string, from?: number) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/indexOf",
        "!doc": "Returns the index within the calling String object of the first occurrence of the specified value, starting the search at fromIndex,\nreturns -1 if the value is not found."
      },
      "lastIndexOf": {
        "!type": "fn(char: string, from?: number) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/lastIndexOf",
        "!doc": "Returns the index within the calling String object of the last occurrence of the specified value, or -1 if not found. The calling string is searched backward, starting at fromIndex."
      },
      "substring": {
        "!type": "fn(from: number, to?: number) -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/substring",
        "!doc": "Returns a subset of a string between one index and another, or through the end of the string."
      },
      "substr": {
        "!type": "fn(from: number, length?: number) -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/substr",
        "!doc": "Returns the characters in a string beginning at the specified location through the specified number of characters."
      },
      "slice": {
        "!type": "fn(from: number, to?: number) -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/slice",
        "!doc": "Extracts a section of a string and returns a new string."
      },
      "trim": {
        "!type": "fn() -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/Trim",
        "!doc": "Removes whitespace from both ends of the string."
      },
      "trimLeft": {
        "!type": "fn() -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/TrimLeft",
        "!doc": "Removes whitespace from the left end of the string."
      },
      "trimRight": {
        "!type": "fn() -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/TrimRight",
        "!doc": "Removes whitespace from the right end of the string."
      },
      "toUpperCase": {
        "!type": "fn() -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/toUpperCase",
        "!doc": "Returns the calling string value converted to uppercase."
      },
      "toLowerCase": {
        "!type": "fn() -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/toLowerCase",
        "!doc": "Returns the calling string value converted to lowercase."
      },
      "toLocaleUpperCase": {
        "!type": "fn() -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/toLocaleUpperCase",
        "!doc": "Returns the calling string value converted to upper case, according to any locale-specific case mappings."
      },
      "toLocaleLowerCase": {
        "!type": "fn() -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/toLocaleLowerCase",
        "!doc": "Returns the calling string value converted to lower case, according to any locale-specific case mappings."
      },
      "split": {
        "!type": "fn(pattern: string) -> [string]",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/split",
        "!doc": "Splits a String object into an array of strings by separating the string into substrings."
      },
      "concat": {
        "!type": "fn(other: string) -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/concat",
        "!doc": "Combines the text of two or more strings and returns a new string."
      },
      "localeCompare": {
        "!type": "fn(other: string) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/localeCompare",
        "!doc": "Returns a number indicating whether a reference string comes before or after or is the same as the given string in sort order."
      },
      "match": {
        "!type": "fn(pattern: +RegExp) -> [string]",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/match",
        "!doc": "Used to retrieve the matches when matching a string against a regular expression."
      },
      "replace": {
        "!type": "fn(pattern: +RegExp, replacement: string) -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/replace",
        "!doc": "Returns a new string with some or all matches of a pattern replaced by a replacement.  The pattern can be a string or a RegExp, and the replacement can be a string or a function to be called for each match."
      },
      "search": {
        "!type": "fn(pattern: +RegExp) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/search",
        "!doc": "Executes the search for a match between a regular expression and this String object."
      }
    },
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String",
    "!doc": "The String global object is a constructor for strings, or a sequence of characters."
  },
  "Number": {
    "!type": "fn(value: ?) -> number",
    "MAX_VALUE": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number/MAX_VALUE",
      "!doc": "The maximum numeric value representable in JavaScript."
    },
    "MIN_VALUE": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number/MIN_VALUE",
      "!doc": "The smallest positive numeric value representable in JavaScript."
    },
    "POSITIVE_INFINITY": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number/POSITIVE_INFINITY",
      "!doc": "A value representing the positive Infinity value."
    },
    "NEGATIVE_INFINITY": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number/NEGATIVE_INFINITY",
      "!doc": "A value representing the negative Infinity value."
    },
    "prototype": {
      "!stdProto": "Number",
      "toString": {
        "!type": "fn(radix?: number) -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number/toString",
        "!doc": "Returns a string representing the specified Number object"
      },
      "toFixed": {
        "!type": "fn(digits: number) -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number/toFixed",
        "!doc": "Formats a number using fixed-point notation"
      },
      "toExponential": {
        "!type": "fn(digits: number) -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number/toExponential",
        "!doc": "Returns a string representing the Number object in exponential notation"
      }
    },
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number",
    "!doc": "The Number JavaScript object is a wrapper object allowing you to work with numerical values. A Number object is created using the Number() constructor."
  },
  "Boolean": {
    "!type": "fn(value: ?) -> bool",
    "prototype": {
      "!stdProto": "Boolean"
    },
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Boolean",
    "!doc": "The Boolean object is an object wrapper for a boolean value."
  },
  "RegExp": {
    "!type": "fn(source: string, flags?: string)",
    "prototype": {
      "!stdProto": "RegExp",
      "exec": {
        "!type": "fn(input: string) -> [string]",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp/exec",
        "!doc": "Executes a search for a match in a specified string. Returns a result array, or null."
      },
      "compile": {
        "!type": "fn(source: string, flags?: string)",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp",
        "!doc": "Creates a regular expression object for matching text with a pattern."
      },
      "test": {
        "!type": "fn(input: string) -> bool",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp/test",
        "!doc": "Executes the search for a match between a regular expression and a specified string. Returns true or false."
      },
      "global": {
        "!type": "bool",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp",
        "!doc": "Creates a regular expression object for matching text with a pattern."
      },
      "ignoreCase": {
        "!type": "bool",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp",
        "!doc": "Creates a regular expression object for matching text with a pattern."
      },
      "multiline": {
        "!type": "bool",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp/multiline",
        "!doc": "Reflects whether or not to search in strings across multiple lines.\n"
      },
      "source": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp/source",
        "!doc": "A read-only property that contains the text of the pattern, excluding the forward slashes.\n"
      },
      "lastIndex": {
        "!type": "number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp/lastIndex",
        "!doc": "A read/write integer property that specifies the index at which to start the next match."
      }
    },
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp",
    "!doc": "Creates a regular expression object for matching text with a pattern."
  },
  "Date": {
    "!type": "fn(ms: number)",
    "parse": {
      "!type": "fn(source: string) -> +Date",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/parse",
      "!doc": "Parses a string representation of a date, and returns the number of milliseconds since January 1, 1970, 00:00:00 UTC."
    },
    "UTC": {
      "!type": "fn(year: number, month: number, date: number, hour?: number, min?: number, sec?: number, ms?: number) -> number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/UTC",
      "!doc": "Accepts the same parameters as the longest form of the constructor, and returns the number of milliseconds in a Date object since January 1, 1970, 00:00:00, universal time."
    },
    "now": {
      "!type": "fn() -> number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/now",
      "!doc": "Returns the number of milliseconds elapsed since 1 January 1970 00:00:00 UTC."
    },
    "prototype": {
      "toUTCString": {
        "!type": "fn() -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/toUTCString",
        "!doc": "Converts a date to a string, using the universal time convention."
      },
      "toISOString": {
        "!type": "fn() -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/toISOString",
        "!doc": "JavaScript provides a direct way to convert a date object into a string in ISO format, the ISO 8601 Extended Format."
      },
      "toDateString": {
        "!type": "fn() -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/toDateString",
        "!doc": "Returns the date portion of a Date object in human readable form in American English."
      },
      "toTimeString": {
        "!type": "fn() -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/toTimeString",
        "!doc": "Returns the time portion of a Date object in human readable form in American English."
      },
      "toLocaleDateString": {
        "!type": "fn() -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/toLocaleDateString",
        "!doc": "Converts a date to a string, returning the \"date\" portion using the operating system's locale's conventions.\n"
      },
      "toLocaleTimeString": {
        "!type": "fn() -> string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/toLocaleTimeString",
        "!doc": "Converts a date to a string, returning the \"time\" portion using the current locale's conventions."
      },
      "getTime": {
        "!type": "fn() -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getTime",
        "!doc": "Returns the numeric value corresponding to the time for the specified date according to universal time."
      },
      "getFullYear": {
        "!type": "fn() -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getFullYear",
        "!doc": "Returns the year of the specified date according to local time."
      },
      "getYear": {
        "!type": "fn() -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getYear",
        "!doc": "Returns the year in the specified date according to local time."
      },
      "getMonth": {
        "!type": "fn() -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getMonth",
        "!doc": "Returns the month in the specified date according to local time."
      },
      "getUTCMonth": {
        "!type": "fn() -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getUTCMonth",
        "!doc": "Returns the month of the specified date according to universal time.\n"
      },
      "getDate": {
        "!type": "fn() -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getDate",
        "!doc": "Returns the day of the month for the specified date according to local time."
      },
      "getUTCDate": {
        "!type": "fn() -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getUTCDate",
        "!doc": "Returns the day (date) of the month in the specified date according to universal time.\n"
      },
      "getDay": {
        "!type": "fn() -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getDay",
        "!doc": "Returns the day of the week for the specified date according to local time."
      },
      "getUTCDay": {
        "!type": "fn() -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getUTCDay",
        "!doc": "Returns the day of the week in the specified date according to universal time.\n"
      },
      "getHours": {
        "!type": "fn() -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getHours",
        "!doc": "Returns the hour for the specified date according to local time."
      },
      "getUTCHours": {
        "!type": "fn() -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getUTCHours",
        "!doc": "Returns the hours in the specified date according to universal time.\n"
      },
      "getMinutes": {
        "!type": "fn() -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getMinutes",
        "!doc": "Returns the minutes in the specified date according to local time."
      },
      "getUTCMinutes": {
        "!type": "fn() -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date",
        "!doc": "Creates JavaScript Date instances which let you work with dates and times."
      },
      "getSeconds": {
        "!type": "fn() -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getSeconds",
        "!doc": "Returns the seconds in the specified date according to local time."
      },
      "getUTCSeconds": {
        "!type": "fn() -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getUTCSeconds",
        "!doc": "Returns the seconds in the specified date according to universal time.\n"
      },
      "getMilliseconds": {
        "!type": "fn() -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getMilliseconds",
        "!doc": "Returns the milliseconds in the specified date according to local time."
      },
      "getUTCMilliseconds": {
        "!type": "fn() -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getUTCMilliseconds",
        "!doc": "Returns the milliseconds in the specified date according to universal time.\n"
      },
      "getTimezoneOffset": {
        "!type": "fn() -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset",
        "!doc": "Returns the time-zone offset from UTC, in minutes, for the current locale."
      },
      "setTime": {
        "!type": "fn(date: +Date) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setTime",
        "!doc": "Sets the Date object to the time represented by a number of milliseconds since January 1, 1970, 00:00:00 UTC.\n"
      },
      "setFullYear": {
        "!type": "fn(year: number) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setFullYear",
        "!doc": "Sets the full year for a specified date according to local time.\n"
      },
      "setUTCFullYear": {
        "!type": "fn(year: number) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setUTCFullYear",
        "!doc": "Sets the full year for a specified date according to universal time.\n"
      },
      "setMonth": {
        "!type": "fn(month: number) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setMonth",
        "!doc": "Set the month for a specified date according to local time."
      },
      "setUTCMonth": {
        "!type": "fn(month: number) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setUTCMonth",
        "!doc": "Sets the month for a specified date according to universal time.\n"
      },
      "setDate": {
        "!type": "fn(day: number) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setDate",
        "!doc": "Sets the day of the month for a specified date according to local time."
      },
      "setUTCDate": {
        "!type": "fn(day: number) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setUTCDate",
        "!doc": "Sets the day of the month for a specified date according to universal time.\n"
      },
      "setHours": {
        "!type": "fn(hour: number) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setHours",
        "!doc": "Sets the hours for a specified date according to local time, and returns the number of milliseconds since 1 January 1970 00:00:00 UTC until the time represented by the updated Date instance."
      },
      "setUTCHours": {
        "!type": "fn(hour: number) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setUTCHours",
        "!doc": "Sets the hour for a specified date according to universal time.\n"
      },
      "setMinutes": {
        "!type": "fn(min: number) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setMinutes",
        "!doc": "Sets the minutes for a specified date according to local time."
      },
      "setUTCMinutes": {
        "!type": "fn(min: number) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setUTCMinutes",
        "!doc": "Sets the minutes for a specified date according to universal time.\n"
      },
      "setSeconds": {
        "!type": "fn(sec: number) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setSeconds",
        "!doc": "Sets the seconds for a specified date according to local time."
      },
      "setUTCSeconds": {
        "!type": "fn(sec: number) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setUTCSeconds",
        "!doc": "Sets the seconds for a specified date according to universal time.\n"
      },
      "setMilliseconds": {
        "!type": "fn(ms: number) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setMilliseconds",
        "!doc": "Sets the milliseconds for a specified date according to local time.\n"
      },
      "setUTCMilliseconds": {
        "!type": "fn(ms: number) -> number",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setUTCMilliseconds",
        "!doc": "Sets the milliseconds for a specified date according to universal time.\n"
      }
    },
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date",
    "!doc": "Creates JavaScript Date instances which let you work with dates and times."
  },
  "Error": {
    "!type": "fn(message: string)",
    "prototype": {
      "name": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Error/name",
        "!doc": "A name for the type of error."
      },
      "message": {
        "!type": "string",
        "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Error/message",
        "!doc": "A human-readable description of the error."
      }
    },
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Error",
    "!doc": "Creates an error object."
  },
  "SyntaxError": {
    "!type": "fn(message: string)",
    "prototype": "Error.prototype",
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/SyntaxError",
    "!doc": "Represents an error when trying to interpret syntactically invalid code."
  },
  "ReferenceError": {
    "!type": "fn(message: string)",
    "prototype": "Error.prototype",
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/ReferenceError",
    "!doc": "Represents an error when a non-existent variable is referenced."
  },
  "URIError": {
    "!type": "fn(message: string)",
    "prototype": "Error.prototype",
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/URIError",
    "!doc": "Represents an error when a malformed URI is encountered."
  },
  "EvalError": {
    "!type": "fn(message: string)",
    "prototype": "Error.prototype",
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/EvalError",
    "!doc": "Represents an error regarding the eval function."
  },
  "RangeError": {
    "!type": "fn(message: string)",
    "prototype": "Error.prototype",
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RangeError",
    "!doc": "Represents an error when a number is not within the correct range allowed."
  },
  "parseInt": {
    "!type": "fn(string: string, radix?: number) -> number",
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/parseInt",
    "!doc": "Parses a string argument and returns an integer of the specified radix or base."
  },
  "parseFloat": {
    "!type": "fn(string: string) -> number",
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/parseFloat",
    "!doc": "Parses a string argument and returns a floating point number."
  },
  "isNaN": {
    "!type": "fn(value: number) -> bool",
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/isNaN",
    "!doc": "Determines whether a value is NaN or not. Be careful, this function is broken. You may be interested in ECMAScript 6 Number.isNaN."
  },
  "eval": {
    "!type": "fn(code: string) -> ?",
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/eval",
    "!doc": "Evaluates JavaScript code represented as a string."
  },
  "encodeURI": {
    "!type": "fn(uri: string) -> string",
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/encodeURI",
    "!doc": "Encodes a Uniform Resource Identifier (URI) by replacing each instance of certain characters by one, two, three, or four escape sequences representing the UTF-8 encoding of the character (will only be four escape sequences for characters composed of two \"surrogate\" characters)."
  },
  "encodeURIComponent": {
    "!type": "fn(uri: string) -> string",
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/encodeURIComponent",
    "!doc": "Encodes a Uniform Resource Identifier (URI) component by replacing each instance of certain characters by one, two, three, or four escape sequences representing the UTF-8 encoding of the character (will only be four escape sequences for characters composed of two \"surrogate\" characters)."
  },
  "decodeURI": {
    "!type": "fn(uri: string) -> string",
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/decodeURI",
    "!doc": "Decodes a Uniform Resource Identifier (URI) previously created by encodeURI or by a similar routine."
  },
  "decodeURIComponent": {
    "!type": "fn(uri: string) -> string",
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/decodeURIComponent",
    "!doc": "Decodes a Uniform Resource Identifier (URI) component previously created by encodeURIComponent or by a similar routine."
  },
  "Math": {
    "E": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/E",
      "!doc": "The base of natural logarithms, e, approximately 2.718."
    },
    "LN2": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/LN2",
      "!doc": "The natural logarithm of 2, approximately 0.693."
    },
    "LN10": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/LN10",
      "!doc": "The natural logarithm of 10, approximately 2.302."
    },
    "LOG2E": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/LOG2E",
      "!doc": "The base 2 logarithm of E (approximately 1.442)."
    },
    "LOG10E": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/LOG10E",
      "!doc": "The base 10 logarithm of E (approximately 0.434)."
    },
    "SQRT1_2": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/SQRT1_2",
      "!doc": "The square root of 1/2; equivalently, 1 over the square root of 2, approximately 0.707."
    },
    "SQRT2": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/SQRT2",
      "!doc": "The square root of 2, approximately 1.414."
    },
    "PI": {
      "!type": "number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/PI",
      "!doc": "The ratio of the circumference of a circle to its diameter, approximately 3.14159."
    },
    "abs": {
      "!type": "fn(number) -> number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/abs",
      "!doc": "Returns the absolute value of a number."
    },
    "cos": {
      "!type": "fn(number) -> number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/cos",
      "!doc": "Returns the cosine of a number."
    },
    "sin": {
      "!type": "fn(number) -> number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/sin",
      "!doc": "Returns the sine of a number."
    },
    "tan": {
      "!type": "fn(number) -> number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/tan",
      "!doc": "Returns the tangent of a number."
    },
    "acos": {
      "!type": "fn(number) -> number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/acos",
      "!doc": "Returns the arccosine (in radians) of a number."
    },
    "asin": {
      "!type": "fn(number) -> number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/asin",
      "!doc": "Returns the arcsine (in radians) of a number."
    },
    "atan": {
      "!type": "fn(number) -> number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/atan",
      "!doc": "Returns the arctangent (in radians) of a number."
    },
    "atan2": {
      "!type": "fn(number, number) -> number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/atan2",
      "!doc": "Returns the arctangent of the quotient of its arguments."
    },
    "ceil": {
      "!type": "fn(number) -> number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/ceil",
      "!doc": "Returns the smallest integer greater than or equal to a number."
    },
    "floor": {
      "!type": "fn(number) -> number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/floor",
      "!doc": "Returns the largest integer less than or equal to a number."
    },
    "round": {
      "!type": "fn(number) -> number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/round",
      "!doc": "Returns the value of a number rounded to the nearest integer."
    },
    "exp": {
      "!type": "fn(number) -> number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/exp",
      "!doc": "Returns Ex, where x is the argument, and E is Euler's constant, the base of the natural logarithms."
    },
    "log": {
      "!type": "fn(number) -> number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/log",
      "!doc": "Returns the natural logarithm (base E) of a number."
    },
    "sqrt": {
      "!type": "fn(number) -> number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/sqrt",
      "!doc": "Returns the square root of a number."
    },
    "pow": {
      "!type": "fn(number, number) -> number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/pow",
      "!doc": "Returns base to the exponent power, that is, baseexponent."
    },
    "max": {
      "!type": "fn(number, number) -> number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/max",
      "!doc": "Returns the largest of zero or more numbers."
    },
    "min": {
      "!type": "fn(number, number) -> number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/min",
      "!doc": "Returns the smallest of zero or more numbers."
    },
    "random": {
      "!type": "fn() -> number",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/random",
      "!doc": "Returns a floating-point, pseudo-random number in the range [0, 1) that is, from 0 (inclusive) up to but not including 1 (exclusive), which you can then scale to your desired range."
    },
    "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math",
    "!doc": "A built-in object that has properties and methods for mathematical constants and functions."
  },
  "JSON": {
    "parse": {
      "!type": "fn(json: string) -> ?",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/JSON/parse",
      "!doc": "Parse a string as JSON, optionally transforming the value produced by parsing."
    },
    "stringify": {
      "!type": "fn(value: ?) -> string",
      "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/JSON/stringify",
      "!doc": "Convert a value to JSON, optionally replacing values if a replacer function is specified, or optionally including only the specified properties if a replacer array is specified."
    },
    "!url": "https://developer.mozilla.org/en-US/docs/JSON",
    "!doc": "JSON (JavaScript Object Notation) is a data-interchange format.  It closely resembles a subset of JavaScript syntax, although it is not a strict subset. (See JSON in the JavaScript Reference for full details.)  It is useful when writing any kind of JavaScript-based application, including websites and browser extensions.  For example, you might store user information in JSON format in a cookie, or you might store extension preferences in JSON in a string-valued browser preference."
  }
}
PK
!<�?��V V Bchrome/devtools/modules/devtools/client/sourceeditor/tern/infer.js// Main type inference engine

// Walks an AST, building up a graph of abstract values and constraints
// that cause types to flow from one node to another. Also defines a
// number of utilities for accessing ASTs and scopes.

// Analysis is done in a context, which is tracked by the dynamically
// bound cx variable. Use withContext to set the current context.

// For memory-saving reasons, individual types export an interface
// similar to abstract values (which can hold multiple types), and can
// thus be used in place abstract values that only ever contain a
// single type.

(function(root, mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    return mod(exports, require("acorn/acorn"), require("acorn/acorn_loose"), require("acorn/walk"),
               require("./def"), require("./signal"));
  if (typeof define == "function" && define.amd) // AMD
    return define(["exports", "acorn/acorn", "acorn/acorn_loose", "acorn/walk", "./def", "./signal"], mod);
  mod(root.tern || (root.tern = {}), acorn, acorn, acorn.walk, tern.def, tern.signal); // Plain browser env
})(this, function(exports, acorn, acorn_loose, walk, def, signal) {
  "use strict";

  var toString = exports.toString = function(type, maxDepth, parent) {
    if (!type || type == parent || maxDepth && maxDepth < -3) return "?";
    return type.toString(maxDepth, parent);
  };

  // A variant of AVal used for unknown, dead-end values. Also serves
  // as prototype for AVals, Types, and Constraints because it
  // implements 'empty' versions of all the methods that the code
  // expects.
  var ANull = exports.ANull = signal.mixin({
    addType: function() {},
    propagate: function() {},
    getProp: function() { return ANull; },
    forAllProps: function() {},
    hasType: function() { return false; },
    isEmpty: function() { return true; },
    getFunctionType: function() {},
    getObjType: function() {},
    getSymbolType: function() {},
    getType: function() {},
    gatherProperties: function() {},
    propagatesTo: function() {},
    typeHint: function() {},
    propHint: function() {},
    toString: function() { return "?"; }
  });

  function extend(proto, props) {
    var obj = Object.create(proto);
    if (props) for (var prop in props) obj[prop] = props[prop];
    return obj;
  }

  // ABSTRACT VALUES

  var WG_DEFAULT = 100, WG_NEW_INSTANCE = 90, WG_MADEUP_PROTO = 10,
      WG_MULTI_MEMBER = 6, WG_CATCH_ERROR = 6,
      WG_PHANTOM_OBJ = 1,
      WG_GLOBAL_THIS = 90, WG_SPECULATIVE_THIS = 2, WG_SPECULATIVE_PROTO_THIS = 4;

  var AVal = exports.AVal = function() {
    this.types = [];
    this.forward = null;
    this.maxWeight = 0;
  };
  AVal.prototype = extend(ANull, {
    addType: function(type, weight) {
      weight = weight || WG_DEFAULT;
      if (this.maxWeight < weight) {
        this.maxWeight = weight;
        if (this.types.length == 1 && this.types[0] == type) return;
        this.types.length = 0;
      } else if (this.maxWeight > weight || this.types.indexOf(type) > -1) {
        return;
      }

      this.signal("addType", type);
      this.types.push(type);
      var forward = this.forward;
      if (forward) withWorklist(function(add) {
        for (var i = 0; i < forward.length; ++i) add(type, forward[i], weight);
      });
    },

    propagate: function(target, weight) {
      if (target == ANull || (target instanceof Type && this.forward && this.forward.length > 2)) return;
      if (weight && weight != WG_DEFAULT) target = new Muffle(target, weight);
      (this.forward || (this.forward = [])).push(target);
      var types = this.types;
      if (types.length) withWorklist(function(add) {
        for (var i = 0; i < types.length; ++i) add(types[i], target, weight);
      });
    },

    getProp: function(prop) {
      if (prop == "__proto__" || prop == "✖") return ANull;
      var found = (this.props || (this.props = Object.create(null)))[prop];
      if (!found) {
        found = this.props[prop] = new AVal;
        this.propagate(new GetProp(prop, found));
      }
      return found;
    },

    forAllProps: function(c) {
      this.propagate(new ForAllProps(c));
    },

    hasType: function(type) {
      return this.types.indexOf(type) > -1;
    },
    isEmpty: function() { return this.types.length === 0; },
    getFunctionType: function() {
      for (var i = this.types.length - 1; i >= 0; --i)
        if (this.types[i] instanceof Fn) return this.types[i];
    },
    getObjType: function() {
      var seen = null;
      for (var i = this.types.length - 1; i >= 0; --i) {
        var type = this.types[i];
        if (!(type instanceof Obj)) continue;
        if (type.name) return type;
        if (!seen) seen = type;
      }
      return seen;
    },

    getSymbolType: function() {
      for (var i = this.types.length - 1; i >= 0; --i)
        if (this.types[i] instanceof Sym) return this.types[i]
    },

    getType: function(guess) {
      if (this.types.length === 0 && guess !== false) return this.makeupType();
      if (this.types.length === 1) return this.types[0];
      return canonicalType(this.types);
    },

    toString: function(maxDepth, parent) {
      if (this.types.length == 0) return toString(this.makeupType(), maxDepth, parent);
      if (this.types.length == 1) return toString(this.types[0], maxDepth, parent);
      var simplified = simplifyTypes(this.types);
      if (simplified.length > 2) return "?";
      return simplified.map(function(tp) { return toString(tp, maxDepth, parent); }).join("|");
    },

    makeupPropType: function(obj) {
      var propName = this.propertyName;

      var protoProp = obj.proto && obj.proto.hasProp(propName);
      if (protoProp) {
        var fromProto = protoProp.getType();
        if (fromProto) return fromProto;
      }

      if (propName != "<i>") {
        var computedProp = obj.hasProp("<i>");
        if (computedProp) return computedProp.getType();
      } else if (obj.props["<i>"] != this) {
        for (var prop in obj.props) {
          var val = obj.props[prop];
          if (!val.isEmpty()) return val.getType();
        }
      }
    },

    makeupType: function() {
      var computed = this.propertyOf && this.makeupPropType(this.propertyOf);
      if (computed) return computed;

      if (!this.forward) return null;
      for (var i = this.forward.length - 1; i >= 0; --i) {
        var hint = this.forward[i].typeHint();
        if (hint && !hint.isEmpty()) {guessing = true; return hint;}
      }

      var props = Object.create(null), foundProp = null;
      for (var i = 0; i < this.forward.length; ++i) {
        var prop = this.forward[i].propHint();
        if (prop && prop != "length" && prop != "<i>" && prop != "✖" && prop != cx.completingProperty) {
          props[prop] = true;
          foundProp = prop;
        }
      }
      if (!foundProp) return null;

      var objs = objsWithProp(foundProp);
      if (objs) {
        var matches = [];
        search: for (var i = 0; i < objs.length; ++i) {
          var obj = objs[i];
          for (var prop in props) if (!obj.hasProp(prop)) continue search;
          if (obj.hasCtor) obj = getInstance(obj);
          matches.push(obj);
        }
        var canon = canonicalType(matches);
        if (canon) {guessing = true; return canon;}
      }
    },

    typeHint: function() { return this.types.length ? this.getType() : null; },
    propagatesTo: function() { return this; },

    gatherProperties: function(f, depth) {
      for (var i = 0; i < this.types.length; ++i)
        this.types[i].gatherProperties(f, depth);
    },

    guessProperties: function(f) {
      if (this.forward) for (var i = 0; i < this.forward.length; ++i) {
        var prop = this.forward[i].propHint();
        if (prop) f(prop, null, 0);
      }
      var guessed = this.makeupType();
      if (guessed) guessed.gatherProperties(f);
    }
  });

  function similarAVal(a, b, depth) {
    var typeA = a.getType(false), typeB = b.getType(false);
    if (!typeA || !typeB) return true;
    return similarType(typeA, typeB, depth);
  }

  function similarType(a, b, depth) {
    if (!a || depth >= 5) return b;
    if (!a || a == b) return a;
    if (!b) return a;
    if (a.constructor != b.constructor) return false;
    if (a.constructor == Arr) {
      var innerA = a.getProp("<i>").getType(false);
      if (!innerA) return b;
      var innerB = b.getProp("<i>").getType(false);
      if (!innerB || similarType(innerA, innerB, depth + 1)) return b;
    } else if (a.constructor == Obj) {
      var propsA = 0, propsB = 0, same = 0;
      for (var prop in a.props) {
        propsA++;
        if (prop in b.props && similarAVal(a.props[prop], b.props[prop], depth + 1))
          same++;
      }
      for (var prop in b.props) propsB++;
      if (propsA && propsB && same < Math.max(propsA, propsB) / 2) return false;
      return propsA > propsB ? a : b;
    } else if (a.constructor == Fn) {
      if (a.args.length != b.args.length ||
          !a.args.every(function(tp, i) { return similarAVal(tp, b.args[i], depth + 1); }) ||
          !similarAVal(a.retval, b.retval, depth + 1) || !similarAVal(a.self, b.self, depth + 1))
        return false;
      return a;
    } else {
      return false;
    }
  }

  var simplifyTypes = exports.simplifyTypes = function(types) {
    var found = [];
    outer: for (var i = 0; i < types.length; ++i) {
      var tp = types[i];
      for (var j = 0; j < found.length; j++) {
        var similar = similarType(tp, found[j], 0);
        if (similar) {
          found[j] = similar;
          continue outer;
        }
      }
      found.push(tp);
    }
    return found;
  };

  function canonicalType(types) {
    var arrays = 0, fns = 0, objs = 0, prim = null;
    for (var i = 0; i < types.length; ++i) {
      var tp = types[i];
      if (tp instanceof Arr) ++arrays;
      else if (tp instanceof Fn) ++fns;
      else if (tp instanceof Obj) ++objs;
      else if (tp instanceof Prim) {
        if (prim && tp.name != prim.name) return null;
        prim = tp;
      }
    }
    var kinds = (arrays && 1) + (fns && 1) + (objs && 1) + (prim && 1);
    if (kinds > 1) return null;
    if (prim) return prim;

    var maxScore = 0, maxTp = null;
    for (var i = 0; i < types.length; ++i) {
      var tp = types[i], score = 0;
      if (arrays) {
        score = tp.getProp("<i>").isEmpty() ? 1 : 2;
      } else if (fns) {
        score = 1;
        for (var j = 0; j < tp.args.length; ++j) if (!tp.args[j].isEmpty()) ++score;
        if (!tp.retval.isEmpty()) ++score;
      } else if (objs) {
        score = tp.name ? 100 : 2;
      }
      if (score >= maxScore) { maxScore = score; maxTp = tp; }
    }
    return maxTp;
  }

  // PROPAGATION STRATEGIES

  var constraint = exports.constraint = function(methods) {
    var ctor = function() {
      this.origin = cx.curOrigin;
      this.construct.apply(this, arguments);
    };
    ctor.prototype = Object.create(ANull);
    for (var m in methods) if (methods.hasOwnProperty(m)) ctor.prototype[m] = methods[m];
    return ctor;
  };

  var GetProp = constraint({
    construct: function(prop, target) {
      this.prop = prop; this.target = target;
    },
    addType: function(type, weight) {
      if (type.getProp)
        type.getProp(this.prop).propagate(this.target, weight);
    },
    propHint: function() { return this.prop; },
    propagatesTo: function() {
      if (this.prop == "<i>" || !/[^\w_]/.test(this.prop))
        return {target: this.target, pathExt: "." + this.prop};
    }
  });

  var DefProp = exports.PropHasSubset = exports.DefProp = constraint({
    construct: function(prop, type, originNode) {
      this.prop = prop; this.type = type; this.originNode = originNode;
    },
    addType: function(type, weight) {
      if (!(type instanceof Obj)) return;
      var prop = type.defProp(this.prop, this.originNode);
      if (!prop.origin) prop.origin = this.origin;
      this.type.propagate(prop, weight);
    },
    propHint: function() { return this.prop; }
  });

  var ForAllProps = constraint({
    construct: function(c) { this.c = c; },
    addType: function(type) {
      if (!(type instanceof Obj)) return;
      type.forAllProps(this.c);
    }
  });

  function withDisabledComputing(fn, body) {
    cx.disabledComputing = {fn: fn, prev: cx.disabledComputing};
    var result = body();
    cx.disabledComputing = cx.disabledComputing.prev;
    return result;
  }
  var IsCallee = exports.IsCallee = constraint({
    construct: function(self, args, argNodes, retval) {
      this.self = self; this.args = args; this.argNodes = argNodes; this.retval = retval;
      this.disabled = cx.disabledComputing;
    },
    addType: function(fn, weight) {
      if (!(fn instanceof Fn)) return;
      for (var i = 0; i < this.args.length; ++i) {
        if (i < fn.args.length) this.args[i].propagate(fn.args[i], weight);
        if (fn.arguments) this.args[i].propagate(fn.arguments, weight);
      }
      this.self.propagate(fn.self, this.self == cx.topScope ? WG_GLOBAL_THIS : weight);
      var compute = fn.computeRet, result = fn.retval
      if (compute) for (var d = this.disabled; d; d = d.prev)
        if (d.fn == fn || fn.originNode && d.fn.originNode == fn.originNode) compute = null;
      if (compute) {
        var old = cx.disabledComputing;
        cx.disabledComputing = this.disabled;
        result = compute(this.self, this.args, this.argNodes)
        cx.disabledComputing = old;
      }
      maybeIterator(fn, result).propagate(this.retval, weight)
    },
    typeHint: function() {
      var names = [];
      for (var i = 0; i < this.args.length; ++i) names.push("?");
      return new Fn(null, this.self, this.args, names, ANull);
    },
    propagatesTo: function() {
      return {target: this.retval, pathExt: ".!ret"};
    }
  });

  var HasMethodCall = constraint({
    construct: function(propName, args, argNodes, retval) {
      this.propName = propName; this.args = args; this.argNodes = argNodes; this.retval = retval;
      this.disabled = cx.disabledComputing;
    },
    addType: function(obj, weight) {
      var callee = new IsCallee(obj, this.args, this.argNodes, this.retval);
      callee.disabled = this.disabled;
      obj.getProp(this.propName).propagate(callee, weight);
    },
    propHint: function() { return this.propName; }
  });

  var IsCtor = exports.IsCtor = constraint({
    construct: function(target, noReuse) {
      this.target = target; this.noReuse = noReuse;
    },
    addType: function(f, weight) {
      if (!(f instanceof Fn)) return;
      if (cx.parent && !cx.parent.options.reuseInstances) this.noReuse = true;
      f.getProp("prototype").propagate(new IsProto(this.noReuse ? false : f, this.target), weight);
    }
  });

  var getInstance = exports.getInstance = function(obj, ctor) {
    if (ctor === false) return new Obj(obj);

    if (!ctor) ctor = obj.hasCtor;
    if (!obj.instances) obj.instances = [];
    for (var i = 0; i < obj.instances.length; ++i) {
      var cur = obj.instances[i];
      if (cur.ctor == ctor) return cur.instance;
    }
    var instance = new Obj(obj, ctor && ctor.name);
    instance.origin = obj.origin;
    obj.instances.push({ctor: ctor, instance: instance});
    return instance;
  };

  var IsProto = exports.IsProto = constraint({
    construct: function(ctor, target) {
      this.ctor = ctor; this.target = target;
    },
    addType: function(o, _weight) {
      if (!(o instanceof Obj)) return;
      if ((this.count = (this.count || 0) + 1) > 8) return;
      if (o == cx.protos.Array)
        this.target.addType(new Arr);
      else
        this.target.addType(getInstance(o, this.ctor));
    }
  });

  var FnPrototype = constraint({
    construct: function(fn) { this.fn = fn; },
    addType: function(o, _weight) {
      if (o instanceof Obj && !o.hasCtor) {
        o.hasCtor = this.fn;
        var adder = new SpeculativeThis(o, this.fn);
        adder.addType(this.fn);
        o.forAllProps(function(_prop, val, local) {
          if (local) val.propagate(adder);
        });
      }
    }
  });

  var IsAdded = constraint({
    construct: function(other, target) {
      this.other = other; this.target = target;
    },
    addType: function(type, weight) {
      if (type == cx.str)
        this.target.addType(cx.str, weight);
      else if (type == cx.num && this.other.hasType(cx.num))
        this.target.addType(cx.num, weight);
    },
    typeHint: function() { return this.other; }
  });

  var IfObj = exports.IfObj = constraint({
    construct: function(target) { this.target = target; },
    addType: function(t, weight) {
      if (t instanceof Obj) this.target.addType(t, weight);
    },
    propagatesTo: function() { return this.target; }
  });

  var SpeculativeThis = constraint({
    construct: function(obj, ctor) { this.obj = obj; this.ctor = ctor; },
    addType: function(tp) {
      if (tp instanceof Fn && tp.self)
        tp.self.addType(getInstance(this.obj, this.ctor), WG_SPECULATIVE_PROTO_THIS);
    }
  });

  var HasProto = constraint({
    construct: function(obj) { this.obj = obj },
    addType: function(tp) {
      if (tp instanceof Obj && this.obj.proto == cx.protos.Object)
        this.obj.replaceProto(tp)
    }
  });

  var Muffle = constraint({
    construct: function(inner, weight) {
      this.inner = inner; this.weight = weight;
    },
    addType: function(tp, weight) {
      this.inner.addType(tp, Math.min(weight, this.weight));
    },
    propagatesTo: function() { return this.inner.propagatesTo(); },
    typeHint: function() { return this.inner.typeHint(); },
    propHint: function() { return this.inner.propHint(); }
  });

  // TYPE OBJECTS

  var Type = exports.Type = function() {};
  Type.prototype = extend(ANull, {
    constructor: Type,
    propagate: function(c, w) { c.addType(this, w); },
    hasType: function(other) { return other == this; },
    isEmpty: function() { return false; },
    typeHint: function() { return this; },
    getType: function() { return this; }
  });

  var Prim = exports.Prim = function(proto, name) { this.name = name; this.proto = proto; };
  Prim.prototype = extend(Type.prototype, {
    constructor: Prim,
    toString: function() { return this.name; },
    getProp: function(prop) {return this.proto.hasProp(prop) || ANull;},
    gatherProperties: function(f, depth) {
      if (this.proto) this.proto.gatherProperties(f, depth);
    }
  });

  function isInteger(str) {
    var c0 = str.charCodeAt(0)
    if (c0 >= 48 && c0 <= 57) return !/\D/.test(str)
    else return false
  }

  var Obj = exports.Obj = function(proto, name) {
    if (!this.props) this.props = Object.create(null);
    this.proto = proto === true ? cx.protos.Object : proto;
    if (proto && !name && proto.name && !(this instanceof Fn)) {
      var match = /^(.*)\.prototype$/.exec(this.proto.name);
      if (match) name = match[1];
    }
    this.name = name;
    this.maybeProps = null;
    this.origin = cx.curOrigin;
  };
  Obj.prototype = extend(Type.prototype, {
    constructor: Obj,
    toString: function(maxDepth) {
      if (maxDepth == null) maxDepth = 0;
      if (maxDepth <= 0 && this.name) return this.name;
      var props = [], etc = false;
      for (var prop in this.props) if (prop != "<i>") {
        if (props.length > 5) { etc = true; break; }
        if (maxDepth)
          props.push(prop + ": " + toString(this.props[prop], maxDepth - 1, this));
        else
          props.push(prop);
      }
      props.sort();
      if (etc) props.push("...");
      return "{" + props.join(", ") + "}";
    },
    hasProp: function(prop, searchProto) {
      if (isInteger(prop)) prop = this.normalizeIntegerProp(prop)
      var found = this.props[prop];
      if (searchProto !== false)
        for (var p = this.proto; p && !found; p = p.proto) found = p.props[prop];
      return found;
    },
    defProp: function(prop, originNode) {
      var found = this.hasProp(prop, false);
      if (found) {
        if (originNode && !found.originNode) found.originNode = originNode;
        return found;
      }
      if (prop == "__proto__" || prop == "✖") return ANull;
      if (isInteger(prop)) prop = this.normalizeIntegerProp(prop)

      var av = this.maybeProps && this.maybeProps[prop];
      if (av) {
        delete this.maybeProps[prop];
        this.maybeUnregProtoPropHandler();
      } else {
        av = new AVal;
        av.propertyOf = this;
        av.propertyName = prop;
      }

      this.props[prop] = av;
      av.originNode = originNode;
      av.origin = cx.curOrigin;
      this.broadcastProp(prop, av, true);
      return av;
    },
    getProp: function(prop) {
      var found = this.hasProp(prop, true) || (this.maybeProps && this.maybeProps[prop]);
      if (found) return found;
      if (prop == "__proto__" || prop == "✖") return ANull;
      if (isInteger(prop)) prop = this.normalizeIntegerProp(prop)
      var av = this.ensureMaybeProps()[prop] = new AVal;
      av.propertyOf = this;
      av.propertyName = prop;
      return av;
    },
    normalizeIntegerProp: function(_) { return "<i>" },
    broadcastProp: function(prop, val, local) {
      if (local) {
        this.signal("addProp", prop, val);
        // If this is a scope, it shouldn't be registered
        if (!(this instanceof Scope)) registerProp(prop, this);
      }

      if (this.onNewProp) for (var i = 0; i < this.onNewProp.length; ++i) {
        var h = this.onNewProp[i];
        h.onProtoProp ? h.onProtoProp(prop, val, local) : h(prop, val, local);
      }
    },
    onProtoProp: function(prop, val, _local) {
      var maybe = this.maybeProps && this.maybeProps[prop];
      if (maybe) {
        delete this.maybeProps[prop];
        this.maybeUnregProtoPropHandler();
        this.proto.getProp(prop).propagate(maybe);
      }
      this.broadcastProp(prop, val, false);
    },
    replaceProto: function(proto) {
      if (this.proto && this.maybeProps)
        this.proto.unregPropHandler(this)
      this.proto = proto
      if (this.maybeProps)
        this.proto.forAllProps(this)
    },
    ensureMaybeProps: function() {
      if (!this.maybeProps) {
        if (this.proto) this.proto.forAllProps(this);
        this.maybeProps = Object.create(null);
      }
      return this.maybeProps;
    },
    removeProp: function(prop) {
      var av = this.props[prop];
      delete this.props[prop];
      this.ensureMaybeProps()[prop] = av;
      av.types.length = 0;
    },
    forAllProps: function(c) {
      if (!this.onNewProp) {
        this.onNewProp = [];
        if (this.proto) this.proto.forAllProps(this);
      }
      this.onNewProp.push(c);
      for (var o = this; o; o = o.proto) for (var prop in o.props) {
        if (c.onProtoProp)
          c.onProtoProp(prop, o.props[prop], o == this);
        else
          c(prop, o.props[prop], o == this);
      }
    },
    maybeUnregProtoPropHandler: function() {
      if (this.maybeProps) {
        for (var _n in this.maybeProps) return;
        this.maybeProps = null;
      }
      if (!this.proto || this.onNewProp && this.onNewProp.length) return;
      this.proto.unregPropHandler(this);
    },
    unregPropHandler: function(handler) {
      for (var i = 0; i < this.onNewProp.length; ++i)
        if (this.onNewProp[i] == handler) { this.onNewProp.splice(i, 1); break; }
      this.maybeUnregProtoPropHandler();
    },
    gatherProperties: function(f, depth) {
      for (var prop in this.props) if (prop != "<i>" && prop.charAt(0) != ":")
        f(prop, this, depth);
      if (this.proto) this.proto.gatherProperties(f, depth + 1);
    },
    getObjType: function() { return this; }
  });

  var Fn = exports.Fn = function(name, self, args, argNames, retval, generator) {
    Obj.call(this, cx.protos.Function, name);
    this.self = self;
    this.args = args;
    this.argNames = argNames;
    this.retval = retval;
    this.generator = generator
  };
  Fn.prototype = extend(Obj.prototype, {
    constructor: Fn,
    toString: function(maxDepth) {
      if (maxDepth == null) maxDepth = 0;
      var str = this.generator ? "fn*(" : "fn(";
      for (var i = 0; i < this.args.length; ++i) {
        if (i) str += ", ";
        var name = this.argNames[i];
        if (name && name != "?") str += name + ": ";
        str += maxDepth > -3 ? toString(this.args[i], maxDepth - 1, this) : "?";
      }
      str += ")";
      if (!this.retval.isEmpty())
        str += " -> " + (maxDepth > -3 ? toString(this.retval, maxDepth - 1, this) : "?");
      return str;
    },
    getProp: function(prop) {
      if (prop == "prototype") {
        var known = this.hasProp(prop, false);
        if (!known) {
          known = this.defProp(prop);
          var proto = new Obj(true, this.name && this.name + ".prototype");
          proto.origin = this.origin;
          known.addType(proto, WG_MADEUP_PROTO);
        }
        return known;
      }
      return Obj.prototype.getProp.call(this, prop);
    },
    defProp: function(prop, originNode) {
      if (prop == "prototype") {
        var found = this.hasProp(prop, false);
        if (found) return found;
        found = Obj.prototype.defProp.call(this, prop, originNode);
        found.origin = this.origin;
        found.propagate(new FnPrototype(this));
        return found;
      }
      return Obj.prototype.defProp.call(this, prop, originNode);
    },
    getFunctionType: function() { return this; }
  });

  var Arr = exports.Arr = function(contentType) {
    Obj.call(this, cx.protos.Array)
    var content = this.defProp("<i>")
    if (Array.isArray(contentType)) {
      this.tuple = contentType.length
      for (var i = 0; i < contentType.length; i++) {
        var prop = this.defProp(String(i))
        contentType[i].propagate(prop)
        prop.propagate(content)
      }
    } else if (contentType) {
      this.tuple = 0
      contentType.propagate(content)
    }
  };
  Arr.prototype = extend(Obj.prototype, {
    constructor: Arr,
    toString: function(maxDepth) {
      if (maxDepth == null) maxDepth = 0
      if (maxDepth <= -3) return "[?]"
      var content = ""
      if (this.tuple) {
        var similar
        for (var i = 0; i in this.props; i++) {
          var type = toString(this.getProp(String(i)), maxDepth - 1, this)
          if (similar == null)
            similar = type
          else if (similar != type)
            similar = false
          else
            similar = type
          content += (content ? ", " : "") + type
        }
        if (similar) content = similar
      } else {
        content = toString(this.getProp("<i>"), maxDepth - 1, this)
      }
      return "[" + content + "]"
    },
    normalizeIntegerProp: function(prop) {
      if (+prop < this.tuple) return prop
      else return "<i>"
    }
  });

  var Sym = exports.Sym = function(name, originNode) {
    Prim.call(this, cx.protos.Symbol, "Symbol")
    this.symName = name
    this.originNode = originNode
  }
  Sym.prototype = extend(Prim.prototype, {
    constructor: Sym,
    asPropName: function() { return ":" + this.symName },
    getSymbolType: function() { return this }
  })

  exports.getSymbol = function(name, originNode) {
    var cleanName = name.replace(/[^\w$\.]/g, "_")
    var known = cx.symbols[cleanName]
    if (known) {
      if (originNode && !known.originNode) known.originNode = originNode
      return known
    }
    return cx.symbols[cleanName] = new Sym(cleanName, originNode)
  }

  // THE PROPERTY REGISTRY

  function registerProp(prop, obj) {
    var data = cx.props[prop] || (cx.props[prop] = []);
    data.push(obj);
  }

  function objsWithProp(prop) {
    return cx.props[prop];
  }

  // INFERENCE CONTEXT

  exports.Context = function(defs, parent) {
    this.parent = parent;
    this.props = Object.create(null);
    this.protos = Object.create(null);
    this.origins = [];
    this.curOrigin = "ecma5";
    this.paths = Object.create(null);
    this.definitions = Object.create(null);
    this.purgeGen = 0;
    this.workList = null;
    this.disabledComputing = null;
    this.curSuperCtor = this.curSuper = null;
    this.symbols = Object.create(null)

    exports.withContext(this, function() {
      cx.protos.Object = new Obj(null, "Object.prototype");
      cx.topScope = new Scope();
      cx.topScope.name = "<top>";
      cx.protos.Array = new Obj(true, "Array.prototype");
      cx.protos.Function = new Fn("Function.prototype", ANull, [], [], ANull);
      cx.protos.Function.proto = cx.protos.Object;
      cx.protos.RegExp = new Obj(true, "RegExp.prototype");
      cx.protos.String = new Obj(true, "String.prototype");
      cx.protos.Number = new Obj(true, "Number.prototype");
      cx.protos.Boolean = new Obj(true, "Boolean.prototype");
      cx.protos.Symbol = new Obj(true, "Symbol.prototype");
      cx.str = new Prim(cx.protos.String, "string");
      cx.bool = new Prim(cx.protos.Boolean, "bool");
      cx.num = new Prim(cx.protos.Number, "number");
      cx.curOrigin = null;

      if (defs) for (var i = 0; i < defs.length; ++i)
        def.load(defs[i]);
    });
  };

  exports.Context.prototype.startAnalysis = function() {
    this.disabledComputing = this.workList = this.curSuperCtor = this.curSuper = null;
  };

  var cx = null;
  exports.cx = function() { return cx; };

  exports.withContext = function(context, f) {
    var old = cx;
    cx = context;
    try { return f(); }
    finally { cx = old; }
  };

  exports.TimedOut = function() {
    this.message = "Timed out";
    this.stack = (new Error()).stack;
  };
  exports.TimedOut.prototype = Object.create(Error.prototype);
  exports.TimedOut.prototype.name = "infer.TimedOut";

  var timeout;
  exports.withTimeout = function(ms, f) {
    var end = +new Date + ms;
    var oldEnd = timeout;
    if (oldEnd && oldEnd < end) return f();
    timeout = end;
    try { return f(); }
    finally { timeout = oldEnd; }
  };

  exports.addOrigin = function(origin) {
    if (!cx.origins.includes(origin)) cx.origins.push(origin);
  };

  var baseMaxWorkDepth = 20, reduceMaxWorkDepth = 0.0001;
  function withWorklist(f) {
    if (cx.workList) return f(cx.workList);

    var list = [], depth = 0;
    var add = cx.workList = function(type, target, weight) {
      if (depth < baseMaxWorkDepth - reduceMaxWorkDepth * list.length)
        list.push(type, target, weight, depth);
    };
    var ret = f(add);
    for (var i = 0; i < list.length; i += 4) {
      if (timeout && +new Date >= timeout)
        throw new exports.TimedOut();
      depth = list[i + 3] + 1;
      list[i + 1].addType(list[i], list[i + 2]);
    }
    cx.workList = null;
    return ret;
  }

  function withSuper(ctor, obj, f) {
    var oldCtor = cx.curSuperCtor, oldObj = cx.curSuper
    cx.curSuperCtor = ctor; cx.curSuper = obj
    var result = f()
    cx.curSuperCtor = oldCtor; cx.curSuper = oldObj
    return result
  }

  // SCOPES

  var Scope = exports.Scope = function(prev, originNode, isBlock) {
    Obj.call(this, prev || true);
    this.prev = prev;
    this.originNode = originNode
    this.isBlock = !!isBlock
  };
  Scope.prototype = extend(Obj.prototype, {
    constructor: Scope,
    defVar: function(name, originNode) {
      for (var s = this; ; s = s.proto) {
        var found = s.props[name];
        if (found) return found;
        if (!s.prev) return s.defProp(name, originNode);
      }
    }
  });

  function functionScope(scope) {
    while (scope.isBlock) scope = scope.prev
    return scope
  }


  // RETVAL COMPUTATION HEURISTICS

  function maybeInstantiate(scope, score) {
    var fn = functionScope(scope).fnType
    if (fn) fn.instantiateScore = (fn.instantiateScore || 0) + score;
  }

  var NotSmaller = {};
  function nodeSmallerThan(node, n) {
    try {
      walk.simple(node, {Expression: function() { if (--n <= 0) throw NotSmaller; }});
      return true;
    } catch(e) {
      if (e == NotSmaller) return false;
      throw e;
    }
  }

  function maybeTagAsInstantiated(node, fn) {
    var score = fn.instantiateScore;
    if (!cx.disabledComputing && score && fn.args.length && nodeSmallerThan(node, score * 5)) {
      maybeInstantiate(functionScope(fn.originNode.scope.prev), score / 2);
      setFunctionInstantiated(node, fn);
      return true;
    } else {
      fn.instantiateScore = null;
    }
  }

  function setFunctionInstantiated(node, fn) {
    // Disconnect the arg avals, so that we can add info to them without side effects
    for (var i = 0; i < fn.args.length; ++i) fn.args[i] = new AVal;
    fn.self = new AVal;
    fn.computeRet = function(self, args) {
      // Prevent recursion
      return withDisabledComputing(fn, function() {
        var oldOrigin = cx.curOrigin;
        cx.curOrigin = fn.origin;
        var scope = node.scope
        var scopeCopy = new Scope(scope.prev, scope.originNode);
        for (var v in scope.props) {
          var local = scopeCopy.defProp(v, scope.props[v].originNode);
          for (var i = 0; i < args.length; ++i) if (fn.argNames[i] == v && i < args.length)
            args[i].propagate(local);
        }
        var argNames = fn.argNames.length != args.length ? fn.argNames.slice(0, args.length) : fn.argNames;
        while (argNames.length < args.length) argNames.push("?");
        scopeCopy.fnType = new Fn(fn.name, self, args, argNames, ANull, fn.generator);
        scopeCopy.fnType.originNode = fn.originNode;
        if (fn.arguments) {
          var argset = scopeCopy.fnType.arguments = new AVal;
          scopeCopy.defProp("arguments").addType(new Arr(argset));
          for (var i = 0; i < args.length; ++i) args[i].propagate(argset);
        }
        node.scope = scopeCopy;
        walk.recursive(node.body, scopeCopy, null, scopeGatherer);
        walk.recursive(node.body, scopeCopy, null, inferWrapper);
        cx.curOrigin = oldOrigin;
        return scopeCopy.fnType.retval;
      });
    };
  }

  function maybeTagAsGeneric(fn) {
    var target = fn.retval;
    if (target == ANull) return;
    var targetInner, asArray;
    if (!target.isEmpty() && (targetInner = target.getType()) instanceof Arr)
      target = asArray = targetInner.getProp("<i>");

    function explore(aval, path, depth) {
      if (depth > 3 || !aval.forward) return;
      for (var i = 0; i < aval.forward.length; ++i) {
        var prop = aval.forward[i].propagatesTo();
        if (!prop) continue;
        var newPath = path, dest;
        if (prop instanceof AVal) {
          dest = prop;
        } else if (prop.target instanceof AVal) {
          newPath += prop.pathExt;
          dest = prop.target;
        } else continue;
        if (dest == target) return newPath;
        var found = explore(dest, newPath, depth + 1);
        if (found) return found;
      }
    }

    var foundPath = explore(fn.self, "!this", 0);
    for (var i = 0; !foundPath && i < fn.args.length; ++i)
      foundPath = explore(fn.args[i], "!" + i, 0);

    if (foundPath) {
      if (asArray) foundPath = "[" + foundPath + "]";
      var p = new def.TypeParser(foundPath);
      var parsed = p.parseType(true);
      fn.computeRet = parsed.apply ? parsed : function() { return parsed; };
      fn.computeRetSource = foundPath;
      return true;
    }
  }

  // SCOPE GATHERING PASS

  function addVar(scope, nameNode) {
    return scope.defProp(nameNode.name, nameNode);
  }
  function patternName(node) {
    if (node.type == "Identifier") return node.name
    if (node.type == "AssignmentPattern") return patternName(node.left)
    if (node.type == "ObjectPattern") return "{" + node.properties.map(function(e) { return patternName(e.value) }).join(", ") + "}"
    if (node.type == "ArrayPattern") return "[" + node.elements.map(patternName).join(", ") + "]"
    if (node.type == "RestElement") return "..." + patternName(node.argument)
    return "_"
  }

  function isBlockScopedDecl(node) {
    return node.type == "VariableDeclaration" && node.kind != "var" ||
      node.type == "FunctionDeclaration" ||
      node.type == "ClassDeclaration";
  }

  function patternScopes(inner, outer) {
    return {inner: inner, outer: outer || inner}
  }

  var scopeGatherer = exports.scopeGatherer = walk.make({
    VariablePattern: function(node, scopes) {
      if (scopes.inner) addVar(scopes.inner, node)
    },
    AssignmentPattern: function(node, scopes, c) {
      c(node.left, scopes, "Pattern")
      c(node.right, scopes.outer, "Expression")
    },
    AssignmentExpression: function(node, scope, c) {
      if (node.left.type == "MemberExpression")
        c(node.left, scope, "Expression")
      else
        c(node.left, patternScopes(false, scope), "Pattern")
      c(node.right, scope, "Expression")
    },
    Function: function(node, scope, c) {
      var inner = node.scope = new Scope(scope, node)
      var argVals = [], argNames = []
      for (var i = 0; i < node.params.length; ++i) {
        var param = node.params[i]
        argNames.push(patternName(param))
        if (param.type == "Identifier") {
          argVals.push(addVar(inner, param))
        } else {
          var arg = new AVal
          argVals.push(arg)
          arg.originNode = param
          c(param, patternScopes(inner), "Pattern")
        }
      }
      inner.fnType = new Fn(node.id && node.id.name, new AVal, argVals, argNames, ANull, node.generator)
      inner.fnType.originNode = node;
      if (node.id) {
        var decl = node.type == "FunctionDeclaration";
        addVar(decl ? scope : inner, node.id);
      }
      c(node.body, inner, node.expression ? "Expression" : "Statement");
    },
    BlockStatement: function(node, scope, c) {
      if (!node.scope && node.body.some(isBlockScopedDecl))
        scope = node.scope = new Scope(scope, node, true)
      walk.base.BlockStatement(node, scope, c)
    },
    TryStatement: function(node, scope, c) {
      c(node.block, scope, "Statement");
      if (node.handler) {
        if (node.handler.param.type == "Identifier") {
          var v = addVar(scope, node.handler.param);
          c(node.handler.body, scope, "Statement");
          var e5 = cx.definitions.ecma5;
          if (e5 && v.isEmpty()) getInstance(e5["Error.prototype"]).propagate(v, WG_CATCH_ERROR);
        } else {
          c(node.handler.param, patternScopes(scope), "Pattern")
        }
      }
      if (node.finalizer) c(node.finalizer, scope, "Statement");
    },
    VariableDeclaration: function(node, scope, c) {
      var targetScope = node.kind == "var" ? functionScope(scope) : scope
      for (var i = 0; i < node.declarations.length; ++i) {
        var decl = node.declarations[i];
        c(decl.id, patternScopes(targetScope, scope), "Pattern")
        if (decl.init) c(decl.init, scope, "Expression");
      }
    },
    ClassDeclaration: function(node, scope, c) {
      addVar(scope, node.id)
      if (node.superClass) c(node.superClass, scope, "Expression")
      for (var i = 0; i < node.body.body.length; i++)
        c(node.body.body[i], scope)
    },
    ForInStatement: function(node, scope, c) {
      if (!node.scope && isBlockScopedDecl(node.left))
        scope = node.scope = new Scope(scope, node, true)
      walk.base.ForInStatement(node, scope, c)
    },
    ForStatement: function(node, scope, c) {
      if (!node.scope && node.init && isBlockScopedDecl(node.init))
        scope = node.scope = new Scope(scope, node, true)
      walk.base.ForStatement(node, scope, c)
    },
    ImportDeclaration: function(node, scope) {
      for (var i = 0; i < node.specifiers.length; i++)
        addVar(scope, node.specifiers[i].local)
    }
  });
  scopeGatherer.ForOfStatement = scopeGatherer.ForInStatement

  // CONSTRAINT GATHERING PASS

  var propName = exports.propName = function(node, inferInScope) {
    var key = node.property || node.key;
    if (!node.computed && key.type == "Identifier") return key.name;
    if (key.type == "Literal") {
      if (typeof key.value == "string") return key.value
      if (typeof key.value == "number") return String(key.value)
    }
    if (inferInScope) {
      var symName = symbolName(infer(key, inferInScope))
      if (symName) return node.propName = symName
    } else if (node.propName) {
      return node.propName
    }
    return "<i>";
  }
  function symbolName(val) {
    var sym = val.getSymbolType()
    if (sym) return sym.asPropName()
  }

  function unopResultType(op) {
    switch (op) {
    case "+": case "-": case "~": return cx.num;
    case "!": return cx.bool;
    case "typeof": return cx.str;
    case "void": case "delete": return ANull;
    }
  }
  function binopIsBoolean(op) {
    switch (op) {
    case "==": case "!=": case "===": case "!==": case "<": case ">": case ">=": case "<=":
    case "in": case "instanceof": return true;
    }
  }
  function literalType(node) {
    if (node.regex) return getInstance(cx.protos.RegExp);
    switch (typeof node.value) {
    case "boolean": return cx.bool;
    case "number": return cx.num;
    case "string": return cx.str;
    case "object":
    case "function":
      if (!node.value) return ANull;
      return getInstance(cx.protos.RegExp);
    }
  }

  function join(a, b) {
    if (a == b || b == ANull) return a
    if (a == ANull) return b
    var joined = new AVal
    a.propagate(joined)
    b.propagate(joined)
    return joined
  }

  function connectParams(node, scope) {
    for (var i = 0; i < node.params.length; i++) {
      var param = node.params[i]
      if (param.type == "Identifier") continue
      connectPattern(param, scope, node.scope.fnType.args[i])
    }
  }

  function ensureVar(node, scope) {
    return scope.hasProp(node.name) || cx.topScope.defProp(node.name, node)
  }

  var inferPatternVisitor = exports.inferPatternVisitor = {
    Identifier: function(node, scope, source) {
      source.propagate(ensureVar(node, scope))
    },
    MemberExpression: function(node, scope, source) {
      var obj = infer(node.object, scope)
      var pName = propName(node, scope)
      obj.propagate(new DefProp(pName, source, node.property))
    },
    RestElement: function(node, scope, source) {
      connectPattern(node.argument, scope, new Arr(source))
    },
    ObjectPattern: function(node, scope, source) {
      for (var i = 0; i < node.properties.length; ++i) {
        var prop = node.properties[i]
        connectPattern(prop.value, scope, source.getProp(prop.key.name))
      }
    },
    ArrayPattern: function(node, scope, source) {
      for (var i = 0; i < node.elements.length; i++)
        if (node.elements[i])
          connectPattern(node.elements[i], scope, source.getProp(String(i)))
    },
    AssignmentPattern: function(node, scope, source) {
      connectPattern(node.left, scope, join(source, infer(node.right, scope)))
    }
  }

  function connectPattern(node, scope, source) {
    var connecter = inferPatternVisitor[node.type]
    if (connecter) connecter(node, scope, source)
  }

  function getThis(scope) {
    var fnScope = functionScope(scope)
    return fnScope.fnType ? fnScope.fnType.self : fnScope
  }

  function maybeAddPhantomObj(obj) {
    if (!obj.isEmpty() || !obj.propertyOf) return
    obj.propertyOf.getProp(obj.propertyName).addType(new Obj, WG_PHANTOM_OBJ)
    maybeAddPhantomObj(obj.propertyOf)
  }

  function inferClass(node, scope, name) {
    if (!name && node.id) name = node.id.name

    var sup = cx.protos.Object, supCtor, delayed
    if (node.superClass) {
      if (node.superClass.type == "Literal" && node.superClass.value == null) {
        sup = null
      } else {
        var supVal = infer(node.superClass, scope), supProto
        supCtor = supVal.getFunctionType()
        if (supCtor && (supProto = supCtor.getProp("prototype").getObjType())) {
          sup = supProto
        } else {
          supCtor = supVal
          delayed = supVal.getProp("prototype")
        }
      }
    }
    var proto = new Obj(sup, name && name + ".prototype")
    if (delayed) delayed.propagate(new HasProto(proto))

    return withSuper(supCtor, delayed || sup, function() {
      var ctor, body = node.body.body
      for (var i = 0; i < body.length; i++)
        if (body[i].kind == "constructor") ctor = body[i].value
      var fn = node.objType = ctor ? infer(ctor, scope) : new Fn(name, ANull, [], null, ANull)
      fn.originNode = node.id || ctor || node

      var inst = getInstance(proto, fn)
      fn.self.addType(inst)
      fn.defProp("prototype", node).addType(proto)
      for (var i = 0; i < body.length; i++) {
        var method = body[i], target
        if (method.kind == "constructor") continue
        var pName = propName(method, scope)
        if (pName == "<i>" || method.kind == "set") {
          target = ANull
        } else {
          target = (method.static ? fn : proto).defProp(pName, method.key)
          target.initializer = true
          if (method.kind == "get") target = new IsCallee(inst, [], null, target)
        }
        infer(method.value, scope, target)
        var methodFn = target.getFunctionType()
        if (methodFn) methodFn.self.addType(inst)
      }
      return fn
    })
  }

  function arrayLiteralType(elements, scope, inner) {
    var tuple = elements.length > 1 && elements.length < 6
    if (tuple) {
      var homogenous = true, litType
      for (var i = 0; i < elements.length; i++) {
        var elt = elements[i]
        if (!elt)
          tuple = false
        else if (elt.type != "Literal" || (litType && litType != typeof elt.value))
          homogenous = false
        else
          litType = typeof elt.value
      }
      if (homogenous) tuple = false
    }

    if (tuple) {
      var types = []
      for (var i = 0; i < elements.length; ++i)
        types.push(inner(elements[i], scope))
      return new Arr(types)
    } else if (elements.length < 2) {
      return new Arr(elements[0] && inner(elements[0], scope))
    } else {
      var eltVal = new AVal
      for (var i = 0; i < elements.length; i++)
        if (elements[i]) inner(elements[i], scope).propagate(eltVal)
      return new Arr(eltVal)
    }
  }

  function ret(f) {
    return function(node, scope, out, name) {
      var r = f(node, scope, name);
      if (out) r.propagate(out);
      return r;
    };
  }
  function fill(f) {
    return function(node, scope, out, name) {
      if (!out) out = new AVal;
      f(node, scope, out, name);
      return out;
    };
  }

  var inferExprVisitor = exports.inferExprVisitor = {
    ArrayExpression: ret(function(node, scope) {
      return arrayLiteralType(node.elements, scope, infer)
    }),
    ObjectExpression: ret(function(node, scope, name) {
      var proto = true, waitForProto
      for (var i = 0; i < node.properties.length; ++i) {
        var prop = node.properties[i]
        if (prop.key.name == "__proto__") {
          if (prop.value.type == "Literal" && prop.value.value == null) {
            proto = null
          } else {
            var protoVal = infer(prop.value, scope), known = protoVal.getObjType()
            if (known) proto = known
            else waitForProto = protoVal
          }
        }
      }

      var obj = node.objType = new Obj(proto, name);
      if (waitForProto) waitForProto.propagate(new HasProto(obj))
      obj.originNode = node;

      withSuper(null, waitForProto || proto, function() {
        for (var i = 0; i < node.properties.length; ++i) {
          var prop = node.properties[i], key = prop.key;
          if (prop.value.name == "✖" || prop.key.name == "__proto__") continue;

          var name = propName(prop, scope), target
          if (name == "<i>" || prop.kind == "set") {
            target = ANull;
          } else {
            var val = target = obj.defProp(name, key);
            val.initializer = true;
            if (prop.kind == "get")
              target = new IsCallee(obj, [], null, val);
          }
          infer(prop.value, scope, target, name);
          if (prop.value.type == "FunctionExpression")
            prop.value.scope.fnType.self.addType(obj, WG_SPECULATIVE_THIS);
        }
      })
      return obj;
    }),
    FunctionExpression: ret(function(node, scope, name) {
      var inner = node.scope, fn = inner.fnType;
      if (name && !fn.name) fn.name = name;
      connectParams(node, inner)
      if (node.expression)
        infer(node.body, inner, inner.fnType.retval = new AVal)
      else
        walk.recursive(node.body, inner, null, inferWrapper, "Statement")
      if (node.type == "ArrowFunctionExpression") {
        getThis(scope).propagate(fn.self)
        fn.self = ANull
      }
      maybeTagAsInstantiated(node, fn) || maybeTagAsGeneric(fn);
      if (node.id) inner.getProp(node.id.name).addType(fn);
      return fn;
    }),
    ClassExpression: ret(inferClass),
    SequenceExpression: ret(function(node, scope) {
      for (var i = 0, l = node.expressions.length - 1; i < l; ++i)
        infer(node.expressions[i], scope, ANull);
      return infer(node.expressions[l], scope);
    }),
    UnaryExpression: ret(function(node, scope) {
      infer(node.argument, scope, ANull);
      return unopResultType(node.operator);
    }),
    UpdateExpression: ret(function(node, scope) {
      infer(node.argument, scope, ANull);
      return cx.num;
    }),
    BinaryExpression: ret(function(node, scope) {
      if (node.operator == "+") {
        var lhs = infer(node.left, scope);
        var rhs = infer(node.right, scope);
        if (lhs.hasType(cx.str) || rhs.hasType(cx.str)) return cx.str;
        if (lhs.hasType(cx.num) && rhs.hasType(cx.num)) return cx.num;
        var result = new AVal;
        lhs.propagate(new IsAdded(rhs, result));
        rhs.propagate(new IsAdded(lhs, result));
        return result;
      } else {
        infer(node.left, scope, ANull);
        infer(node.right, scope, ANull);
        return binopIsBoolean(node.operator) ? cx.bool : cx.num;
      }
    }),
    AssignmentExpression: ret(function(node, scope, name) {
      var rhs, pName;
      if (node.left.type == "MemberExpression") {
        pName = propName(node.left, scope)
        if (!name)
          name = node.left.object.type == "Identifier" ? node.left.object.name + "." + pName : pName
      } else if (!name && node.left.type == "Identifier") {
        name = node.left.name
      }

      if (node.operator && node.operator != "=" && node.operator != "+=") {
        infer(node.right, scope, ANull);
        rhs = cx.num;
      } else {
        rhs = infer(node.right, scope, null, name);
      }

      if (node.left.type == "MemberExpression") {
        var obj = infer(node.left.object, scope);
        if (pName == "prototype") maybeInstantiate(scope, 20);
        if (pName == "<i>") {
          // This is a hack to recognize for/in loops that copy
          // properties, and do the copying ourselves, insofar as we
          // manage, because such loops tend to be relevant for type
          // information.
          var v = node.left.property.name, local = scope.props[v], over = local && local.iteratesOver;
          if (over) {
            maybeInstantiate(scope, 20);
            var fromRight = node.right.type == "MemberExpression" && node.right.computed && node.right.property.name == v;
            over.forAllProps(function(prop, val, local) {
              if (local && prop != "prototype" && prop != "<i>")
                obj.propagate(new DefProp(prop, fromRight ? val : ANull));
            });
            return rhs;
          }
        }

        obj.propagate(new DefProp(pName, rhs, node.left.property));
        maybeAddPhantomObj(obj)
        if (node.right.type == "FunctionExpression")
          obj.propagate(node.right.scope.fnType.self, WG_SPECULATIVE_THIS);
      } else {
        connectPattern(node.left, scope, rhs)
      }
      return rhs;
    }),
    LogicalExpression: fill(function(node, scope, out) {
      infer(node.left, scope, out);
      infer(node.right, scope, out);
    }),
    ConditionalExpression: fill(function(node, scope, out) {
      infer(node.test, scope, ANull);
      infer(node.consequent, scope, out);
      infer(node.alternate, scope, out);
    }),
    NewExpression: fill(function(node, scope, out, name) {
      if (node.callee.type == "Identifier" && node.callee.name in scope.props)
        maybeInstantiate(scope, 20);

      for (var i = 0, args = []; i < node.arguments.length; ++i)
        args.push(infer(node.arguments[i], scope));
      var callee = infer(node.callee, scope);
      var self = new AVal;
      callee.propagate(new IsCtor(self, name && /\.prototype$/.test(name)));
      self.propagate(out, WG_NEW_INSTANCE);
      callee.propagate(new IsCallee(self, args, node.arguments, new IfObj(out)));
    }),
    CallExpression: fill(function(node, scope, out) {
      for (var i = 0, args = []; i < node.arguments.length; ++i)
        args.push(infer(node.arguments[i], scope));
      var outerFn = functionScope(scope).fnType
      if (node.callee.type == "MemberExpression") {
        var self = infer(node.callee.object, scope);
        var pName = propName(node.callee, scope)
        if (outerFn && (pName == "call" || pName == "apply") &&
            outerFn.args.indexOf(self) > -1)
          maybeInstantiate(scope, 30);
        self.propagate(new HasMethodCall(pName, args, node.arguments, out));
      } else if (node.callee.type == "Super" && cx.curSuperCtor) {
        cx.curSuperCtor.propagate(new IsCallee(getThis(scope), args, node.arguments, out))
      } else {
        var callee = infer(node.callee, scope);
        if (outerFn && outerFn.args.indexOf(callee) > -1)
          maybeInstantiate(scope, 30);
        var knownFn = callee.getFunctionType();
        if (knownFn && knownFn.instantiateScore && outerFn)
          maybeInstantiate(scope, knownFn.instantiateScore / 5);
        callee.propagate(new IsCallee(cx.topScope, args, node.arguments, out));
      }
    }),
    MemberExpression: fill(function(node, scope, out) {
      var name = propName(node), wg;
      if (name == "<i>") {
        var propType = infer(node.property, scope)
        var symName = symbolName(propType)
        if (symName)
          name = node.propName = symName
        else if (!propType.hasType(cx.num))
          wg = WG_MULTI_MEMBER
      }
      infer(node.object, scope).getProp(name).propagate(out, wg)
    }),
    Identifier: ret(function(node, scope) {
      if (node.name == "arguments") {
        var fnScope = functionScope(scope)
        if (fnScope.fnType && !(node.name in fnScope.props))
          scope.defProp(node.name, fnScope.fnType.originNode)
            .addType(new Arr(fnScope.fnType.arguments = new AVal));
      }
      return scope.getProp(node.name);
    }),
    ThisExpression: ret(function(_node, scope) {
      return getThis(scope)
    }),
    Super: ret(function(node) {
      return node.superType = cx.curSuper || ANull
    }),
    Literal: ret(function(node) {
      return literalType(node);
    }),
    TemplateLiteral: ret(function(node, scope) {
      for (var i = 0; i < node.expressions.length; ++i)
        infer(node.expressions[i], scope, ANull)
      return cx.str
    }),
    TaggedTemplateExpression: fill(function(node, scope, out) {
      var args = [new Arr(cx.str)]
      for (var i = 0; i < node.quasi.expressions.length; ++i)
        args.push(infer(node.quasi.expressions[i], scope))
      infer(node.tag, scope, new IsCallee(cx.topScope, args, node.quasi.expressions, out))
    }),
    YieldExpression: ret(function(node, scope) {
      var output = ANull, fn = functionScope(scope).fnType
      if (fn) {
        if (fn.retval == ANull) fn.retval = new AVal
        if (!fn.yieldval) fn.yieldval = new AVal
        output = fn.retval
      }
      if (node.argument) {
        if (node.delegate) {
          infer(node.argument, scope, new HasMethodCall("next", [], null,
                                                        new GetProp("value", output)))
        } else {
          infer(node.argument, scope, output)
        }
      }
      return fn ? fn.yieldval : ANull
    })
  };
  inferExprVisitor.ArrowFunctionExpression = inferExprVisitor.FunctionExpression

  function infer(node, scope, out, name) {
    var handler = inferExprVisitor[node.type];
    return handler ? handler(node, scope, out, name) : ANull;
  }

  function loopPattern(init) {
    return init.type == "VariableDeclaration" ? init.declarations[0].id : init
  }

  var inferWrapper = exports.inferWrapper = walk.make({
    Expression: function(node, scope) {
      infer(node, node.scope || scope, ANull);
    },

    FunctionDeclaration: function(node, scope, c) {
      var inner = node.scope, fn = inner.fnType;
      connectParams(node, inner)
      c(node.body, inner, "Statement");
      maybeTagAsInstantiated(node, fn) || maybeTagAsGeneric(fn);
      scope.getProp(node.id.name).addType(fn)
    },

    Statement: function(node, scope, c) {
      c(node, node.scope || scope)
    },

    VariableDeclaration: function(node, scope) {
      for (var i = 0; i < node.declarations.length; ++i) {
        var decl = node.declarations[i];
        if (decl.id.type == "Identifier") {
          var prop = scope.getProp(decl.id.name);
          if (decl.init)
            infer(decl.init, scope, prop, decl.id.name);
        } else if (decl.init) {
          connectPattern(decl.id, scope, infer(decl.init, scope))
        }
      }
    },

    ClassDeclaration: function(node, scope) {
      scope.getProp(node.id.name).addType(inferClass(node, scope, node.id.name))
    },

    ReturnStatement: function(node, scope) {
      if (!node.argument) return;
      var output = ANull, fn = functionScope(scope).fnType
      if (fn) {
        if (fn.retval == ANull) fn.retval = new AVal;
        output = fn.retval;
      }
      infer(node.argument, scope, output);
    },

    ForInStatement: function(node, scope, c) {
      var source = infer(node.right, scope);
      if ((node.right.type == "Identifier" && node.right.name in scope.props) ||
          (node.right.type == "MemberExpression" && node.right.property.name == "prototype")) {
        maybeInstantiate(scope, 5);
        var pattern = loopPattern(node.left)
        if (pattern.type == "Identifier") {
          if (pattern.name in scope.props)
            scope.getProp(pattern.name).iteratesOver = source
          source.getProp("<i>").propagate(ensureVar(pattern, scope))
        } else {
          connectPattern(pattern, scope, source.getProp("<i>"))
        }
      }
      c(node.body, scope, "Statement");
    },

    ForOfStatement: function(node, scope, c) {
      var pattern = loopPattern(node.left), target
      if (pattern.type == "Identifier")
        target = ensureVar(pattern, scope)
      else
        connectPattern(pattern, scope, target = new AVal)
      infer(node.right, scope, new HasMethodCall(":Symbol.iterator", [], null,
                                                 new HasMethodCall("next", [], null,
                                                                   new GetProp("value", target))))
      c(node.body, scope, "Statement")
    }
  });

  // PARSING

  var parse = exports.parse = function(text, options, thirdArg) {
    if (!options || Array.isArray(options)) options = thirdArg
    var ast;
    try { ast = acorn.parse(text, options); }
    catch(e) { ast = acorn_loose.parse_dammit(text, options); }
    return ast;
  };

  // ANALYSIS INTERFACE

  exports.analyze = function(ast, name, scope) {
    if (typeof ast == "string") ast = parse(ast);

    if (!name) name = "file#" + cx.origins.length;
    exports.addOrigin(cx.curOrigin = name);

    if (!scope) scope = cx.topScope;
    cx.startAnalysis();

    walk.recursive(ast, scope, null, scopeGatherer);
    if (cx.parent) cx.parent.signal("preInfer", ast, scope)
    walk.recursive(ast, scope, null, inferWrapper);
    if (cx.parent) cx.parent.signal("postInfer", ast, scope)

    cx.curOrigin = null;
  };

  // PURGING

  exports.purge = function(origins, start, end) {
    var test = makePredicate(origins, start, end);
    ++cx.purgeGen;
    cx.topScope.purge(test);
    for (var prop in cx.props) {
      var list = cx.props[prop];
      for (var i = 0; i < list.length; ++i) {
        var obj = list[i], av = obj.props[prop];
        if (!av || test(av, av.originNode)) list.splice(i--, 1);
      }
      if (!list.length) delete cx.props[prop];
    }
  };

  function makePredicate(origins, start, end) {
    var arr = Array.isArray(origins);
    if (arr && origins.length == 1) { origins = origins[0]; arr = false; }
    if (arr) {
      if (end == null) return function(n) { return origins.indexOf(n.origin) > -1; };
      return function(n, pos) { return pos && pos.start >= start && pos.end <= end && origins.indexOf(n.origin) > -1; };
    } else {
      if (end == null) return function(n) { return n.origin == origins; };
      return function(n, pos) { return pos && pos.start >= start && pos.end <= end && n.origin == origins; };
    }
  }

  AVal.prototype.purge = function(test) {
    if (this.purgeGen == cx.purgeGen) return;
    this.purgeGen = cx.purgeGen;
    for (var i = 0; i < this.types.length; ++i) {
      var type = this.types[i];
      if (test(type, type.originNode))
        this.types.splice(i--, 1);
      else
        type.purge(test);
    }
    if (!this.types.length) this.maxWeight = 0;

    if (this.forward) for (var i = 0; i < this.forward.length; ++i) {
      var f = this.forward[i];
      if (test(f)) {
        this.forward.splice(i--, 1);
        if (this.props) this.props = null;
      } else if (f.purge) {
        f.purge(test);
      }
    }
  };
  ANull.purge = function() {};
  Obj.prototype.purge = function(test) {
    if (this.purgeGen == cx.purgeGen) return true;
    this.purgeGen = cx.purgeGen;
    for (var p in this.props) {
      var av = this.props[p];
      if (test(av, av.originNode))
        this.removeProp(p);
      av.purge(test);
    }
  };
  Fn.prototype.purge = function(test) {
    if (Obj.prototype.purge.call(this, test)) return;
    this.self.purge(test);
    this.retval.purge(test);
    for (var i = 0; i < this.args.length; ++i) this.args[i].purge(test);
  };

  // EXPRESSION TYPE DETERMINATION

  function findByPropertyName(name) {
    guessing = true;
    var found = objsWithProp(name);
    if (found) for (var i = 0; i < found.length; ++i) {
      var val = found[i].getProp(name);
      if (!val.isEmpty()) return val;
    }
    return ANull;
  }

  function generatorResult(input, output) {
    var retObj = new Obj(true)
    retObj.defProp("done").addType(cx.bool)
    output.propagate(retObj.defProp("value"))
    var method = new Fn(null, ANull, input ? [input] : [], input ? ["?"] : [], retObj)
    var result = new Obj(cx.definitions.ecma6 && cx.definitions.ecma6.generator_prototype || true)
    result.defProp("next").addType(method)
    return result
  }

  function maybeIterator(fn, output) {
    if (!fn.generator) return output
    if (!fn.computeRet) { // Reuse iterator objects for non-computed return types
      if (fn.generator === true) fn.generator = generatorResult(fn.yieldval, output)
      return fn.generator
    }
    return generatorResult(fn.yieldval, output)
  }

  function computeReturnType(funcNode, argNodes, scope) {
    var fn = findType(funcNode, scope).getFunctionType()
    if (!fn) return ANull
    var result = fn.retval
    if (fn.computeRet) {
      for (var i = 0, args = []; i < argNodes.length; ++i)
        args.push(findType(argNodes[i], scope))
      var self = ANull
      if (funcNode.type == "MemberExpression")
        self = findType(funcNode.object, scope)
      result = fn.computeRet(self, args, argNodes);
    }
    return maybeIterator(fn, result)
  }

  var typeFinder = exports.typeFinder = {
    ArrayExpression: function(node, scope) {
      return arrayLiteralType(node.elements, scope, findType)
    },
    ObjectExpression: function(node) {
      return node.objType;
    },
    ClassExpression: function(node) {
      return node.objType;
    },
    FunctionExpression: function(node) {
      return node.scope.fnType;
    },
    ArrowFunctionExpression: function(node) {
      return node.scope.fnType;
    },
    SequenceExpression: function(node, scope) {
      return findType(node.expressions[node.expressions.length-1], scope);
    },
    UnaryExpression: function(node) {
      return unopResultType(node.operator);
    },
    UpdateExpression: function() {
      return cx.num;
    },
    BinaryExpression: function(node, scope) {
      if (binopIsBoolean(node.operator)) return cx.bool;
      if (node.operator == "+") {
        var lhs = findType(node.left, scope);
        var rhs = findType(node.right, scope);
        if (lhs.hasType(cx.str) || rhs.hasType(cx.str)) return cx.str;
      }
      return cx.num;
    },
    AssignmentExpression: function(node, scope) {
      return findType(node.right, scope);
    },
    LogicalExpression: function(node, scope) {
      var lhs = findType(node.left, scope);
      return lhs.isEmpty() ? findType(node.right, scope) : lhs;
    },
    ConditionalExpression: function(node, scope) {
      var lhs = findType(node.consequent, scope);
      return lhs.isEmpty() ? findType(node.alternate, scope) : lhs;
    },
    NewExpression: function(node, scope) {
      var f = findType(node.callee, scope).getFunctionType();
      var proto = f && f.getProp("prototype").getObjType();
      if (!proto) return ANull;
      return getInstance(proto, f);
    },
    CallExpression: function(node, scope) {
      return computeReturnType(node.callee, node.arguments, scope)
    },
    MemberExpression: function(node, scope) {
      var propN = propName(node), obj = findType(node.object, scope).getType();
      if (obj) return obj.getProp(propN);
      if (propN == "<i>") return ANull;
      return findByPropertyName(propN);
    },
    Identifier: function(node, scope) {
      return scope.hasProp(node.name) || ANull;
    },
    ThisExpression: function(_node, scope) {
      return getThis(scope)
    },
    Literal: function(node) {
      return literalType(node);
    },
    Super: ret(function(node) {
      return node.superType
    }),
    TemplateLiteral: function() {
      return cx.str
    },
    TaggedTemplateExpression: function(node, scope) {
      return computeReturnType(node.tag, node.quasi.expressions, scope)
    },
    YieldExpression: function(_node, scope) {
      var fn = functionScope(scope).fnType
      return fn ? fn.yieldval : ANull
    }
  };

  function findType(node, scope) {
    var finder = typeFinder[node.type];
    return finder ? finder(node, scope) : ANull;
  }

  var searchVisitor = exports.searchVisitor = walk.make({
    Function: function(node, _st, c) {
      walk.base.Function(node, node.scope, c)
    },
    Property: function(node, st, c) {
      if (node.computed) c(node.key, st, "Expression");
      if (node.key != node.value) c(node.value, st, "Expression");
    },
    Statement: function(node, st, c) {
      c(node, node.scope || st)
    },
    ImportSpecifier: function(node, st, c) {
      c(node.local, st)
    },
    ImportDefaultSpecifier: function(node, st, c) {
      c(node.local, st)
    },
    ImportNamespaceSpecifier: function(node, st, c) {
      c(node.local, st)
    }
  });
  exports.fullVisitor = walk.make({
    MemberExpression: function(node, st, c) {
      c(node.object, st, "Expression");
      c(node.property, st, node.computed ? "Expression" : null);
    },
    ObjectExpression: function(node, st, c) {
      for (var i = 0; i < node.properties.length; ++i) {
        c(node.properties[i].value, st, "Expression");
        c(node.properties[i].key, st);
      }
    }
  }, searchVisitor);

  exports.findExpressionAt = function(ast, start, end, defaultScope, filter) {
    var test = filter || function(_t, node) {
      if (node.type == "Identifier" && node.name == "✖") return false;
      return typeFinder.hasOwnProperty(node.type);
    };
    return walk.findNodeAt(ast, start, end, test, searchVisitor, defaultScope || cx.topScope);
  };

  exports.findExpressionAround = function(ast, start, end, defaultScope, filter) {
    var test = filter || function(_t, node) {
      if (start != null && node.start > start) return false;
      if (node.type == "Identifier" && node.name == "✖") return false;
      return typeFinder.hasOwnProperty(node.type);
    };
    return walk.findNodeAround(ast, end, test, searchVisitor, defaultScope || cx.topScope);
  };

  exports.expressionType = function(found) {
    return findType(found.node, found.state);
  };

  // Finding the expected type of something, from context

  exports.parentNode = function(child, ast) {
    var stack = [];
    function c(node, st, override) {
      if (node.start <= child.start && node.end >= child.end) {
        var top = stack[stack.length - 1];
        if (node == child) throw {found: top};
        if (top != node) stack.push(node);
        walk.base[override || node.type](node, st, c);
        if (top != node) stack.pop();
      }
    }
    try {
      c(ast, null);
    } catch (e) {
      if (e.found) return e.found;
      throw e;
    }
  };

  var findTypeFromContext = exports.findTypeFromContext = {
    ArrayExpression: function(parent, _, get) { return get(parent, true).getProp("<i>"); },
    ObjectExpression: function(parent, node, get) {
      for (var i = 0; i < parent.properties.length; ++i) {
        var prop = node.properties[i];
        if (prop.value == node)
          return get(parent, true).getProp(prop.key.name);
      }
    },
    UnaryExpression: function(parent) { return unopResultType(parent.operator); },
    UpdateExpression: function() { return cx.num; },
    BinaryExpression: function(parent) { return binopIsBoolean(parent.operator) ? cx.bool : cx.num; },
    AssignmentExpression: function(parent, _, get) { return get(parent.left); },
    LogicalExpression: function(parent, _, get) { return get(parent, true); },
    ConditionalExpression: function(parent, node, get) {
      if (parent.consequent == node || parent.alternate == node) return get(parent, true);
    },
    CallExpression: function(parent, node, get) {
      for (var i = 0; i < parent.arguments.length; i++) {
        var arg = parent.arguments[i];
        if (arg == node) {
          var calleeType = get(parent.callee).getFunctionType();
          if (calleeType instanceof Fn)
            return calleeType.args[i];
          break;
        }
      }
    },
    ReturnStatement: function(_parent, node, get) {
      var fnNode = walk.findNodeAround(node.sourceFile.ast, node.start, "Function");
      if (fnNode) {
        var fnType = fnNode.node.type != "FunctionDeclaration"
          ? get(fnNode.node, true).getFunctionType()
          : fnNode.node.scope.fnType;
        if (fnType) return fnType.retval.getType();
      }
    },
    VariableDeclarator: function(parent, node, get) {
      if (parent.init == node) return get(parent.id)
    }
  };
  findTypeFromContext.NewExpression = findTypeFromContext.CallExpression

  exports.typeFromContext = function(ast, found) {
    var parent = exports.parentNode(found.node, ast);
    var type = null;
    if (findTypeFromContext.hasOwnProperty(parent.type)) {
      var finder = findTypeFromContext[parent.type];
      type = finder && finder(parent, found.node, function(node, fromContext) {
        var obj = {node: node, state: found.state};
        var tp = fromContext ? exports.typeFromContext(ast, obj) : exports.expressionType(obj);
        return tp || ANull;
      });
    }
    return type || exports.expressionType(found);
  };

  // Flag used to indicate that some wild guessing was used to produce
  // a type or set of completions.
  var guessing = false;

  exports.resetGuessing = function(val) { guessing = val; };
  exports.didGuess = function() { return guessing; };

  exports.forAllPropertiesOf = function(type, f) {
    type.gatherProperties(f, 0);
  };

  var refFindWalker = walk.make({}, searchVisitor);

  exports.findRefs = function(ast, baseScope, name, refScope, f) {
    refFindWalker.Identifier = refFindWalker.VariablePattern = function(node, scope) {
      if (node.name != name) return;
      for (var s = scope; s; s = s.prev) {
        if (s == refScope) f(node, scope);
        if (name in s.props) return;
      }
    };
    walk.recursive(ast, baseScope, null, refFindWalker);
  };

  var simpleWalker = walk.make({
    Function: function(node, _scope, c) {
      c(node.body, node.scope, node.expression ? "Expression" : "Statement")
    },
    Statement: function(node, scope, c) {
      c(node, node.scope || scope)
    }
  });

  exports.findPropRefs = function(ast, scope, objType, propName, f) {
    walk.simple(ast, {
      MemberExpression: function(node, scope) {
        if (node.computed || node.property.name != propName) return;
        if (findType(node.object, scope).getType() == objType) f(node.property);
      },
      ObjectExpression: function(node, scope) {
        if (findType(node, scope).getType() != objType) return;
        for (var i = 0; i < node.properties.length; ++i)
          if (node.properties[i].key.name == propName) f(node.properties[i].key);
      }
    }, simpleWalker, scope);
  };

  // LOCAL-VARIABLE QUERIES

  var scopeAt = exports.scopeAt = function(ast, pos, defaultScope) {
    var found = walk.findNodeAround(ast, pos, function(_, node) {
      return node.scope;
    });
    if (found) return found.node.scope;
    else return defaultScope || cx.topScope;
  };

  exports.forAllLocalsAt = function(ast, pos, defaultScope, f) {
    var scope = scopeAt(ast, pos, defaultScope);
    scope.gatherProperties(f, 0);
  };

  // INIT DEF MODULE

  // Delayed initialization because of cyclic dependencies.
  def = exports.def = def.init({}, exports);
});
PK
!<hp Cchrome/devtools/modules/devtools/client/sourceeditor/tern/signal.js(function(root, mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    return mod(exports);
  if (typeof define == "function" && define.amd) // AMD
    return define(["exports"], mod);
  mod((root.tern || (root.tern = {})).signal = {}); // Plain browser env
})(this, function(exports) {

  function on(type, f) {
    var handlers = this._handlers || (this._handlers = Object.create(null));
    (handlers[type] || (handlers[type] = [])).push(f);
  }

  function off(type, f) {
    var arr = this._handlers && this._handlers[type];
    if (arr) for (var i = 0; i < arr.length; ++i)
      if (arr[i] == f) { arr.splice(i, 1); break; }
  }

  var noHandlers = []
  function getHandlers(emitter, type) {
    var arr = emitter._handlers && emitter._handlers[type];
    return arr && arr.length ? arr.slice() : noHandlers
  }

  function signal(type, a1, a2, a3, a4) {
    var arr = getHandlers(this, type)
    for (var i = 0; i < arr.length; ++i) arr[i].call(this, a1, a2, a3, a4)
  }

  function signalReturnFirst(type, a1, a2, a3, a4) {
    var arr = getHandlers(this, type)
    for (var i = 0; i < arr.length; ++i) {
      var result = arr[i].call(this, a1, a2, a3, a4)
      if (result) return result
    }
  }

  function hasHandler(type) {
    var arr = this._handlers && this._handlers[type]
    return arr && arr.length > 0 && arr
  }

  exports.mixin = function(obj) {
    obj.on = on; obj.off = off;
    obj.signal = signal;
    obj.signalReturnFirst = signalReturnFirst;
    obj.hasHandler = hasHandler;
    return obj;
  };
});
PK
!<�$�|����Achrome/devtools/modules/devtools/client/sourceeditor/tern/tern.js// The Tern server object

// A server is a stateful object that manages the analysis for a
// project, and defines an interface for querying the code in the
// project.

(function(root, mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    return mod(exports, require("./infer"), require("./signal"),
               require("acorn/acorn"), require("acorn/walk"));
  if (typeof define == "function" && define.amd) // AMD
    return define(["exports", "./infer", "./signal", "acorn/acorn", "acorn/walk"], mod);
  mod(root.tern || (root.tern = {}), tern, tern.signal, acorn, acorn.walk); // Plain browser env
})(this, function(exports, infer, signal, acorn, walk) {
  "use strict";

  var plugins = Object.create(null);
  exports.registerPlugin = function(name, init) { plugins[name] = init; };

  var defaultOptions = exports.defaultOptions = {
    debug: false,
    async: false,
    getFile: function(_f, c) { if (this.async) c(null, null); },
    normalizeFilename: function(name) { return name },
    defs: [],
    plugins: {},
    fetchTimeout: 1000,
    dependencyBudget: 20000,
    reuseInstances: true,
    stripCRs: false,
    ecmaVersion: 6,
    projectDir: "/"
  };

  var queryTypes = {
    completions: {
      takesFile: true,
      run: findCompletions
    },
    properties: {
      run: findProperties
    },
    type: {
      takesFile: true,
      run: findTypeAt
    },
    documentation: {
      takesFile: true,
      run: findDocs
    },
    definition: {
      takesFile: true,
      run: findDef
    },
    refs: {
      takesFile: true,
      fullFile: true,
      run: findRefs
    },
    rename: {
      takesFile: true,
      fullFile: true,
      run: buildRename
    },
    files: {
      run: listFiles
    }
  };

  exports.defineQueryType = function(name, desc) { queryTypes[name] = desc; };

  function File(name, parent) {
    this.name = name;
    this.parent = parent;
    this.scope = this.text = this.ast = this.lineOffsets = null;
  }
  File.prototype.asLineChar = function(pos) { return asLineChar(this, pos); };

  function parseFile(srv, file) {
    var options = {
      directSourceFile: file,
      allowReturnOutsideFunction: true,
      allowImportExportEverywhere: true,
      ecmaVersion: srv.options.ecmaVersion
    }
    var text = srv.signalReturnFirst("preParse", file.text, options) || file.text
    var ast = infer.parse(text, options)
    srv.signal("postParse", ast, text)
    return ast
  }

  function updateText(file, text, srv) {
    file.text = srv.options.stripCRs ? text.replace(/\r\n/g, "\n") : text;
    infer.withContext(srv.cx, function() {
      file.ast = parseFile(srv, file)
    });
    file.lineOffsets = null;
  }

  var Server = exports.Server = function(options) {
    this.cx = null;
    this.options = options || {};
    for (var o in defaultOptions) if (!options.hasOwnProperty(o))
      options[o] = defaultOptions[o];

    this.projectDir = options.projectDir.replace(/\\/g, "/")
    if (!/\/$/.test(this.projectDir)) this.projectDir += "/"

    this.handlers = Object.create(null);
    this.files = [];
    this.fileMap = Object.create(null);
    this.needsPurge = [];
    this.budgets = Object.create(null);
    this.uses = 0;
    this.pending = 0;
    this.asyncError = null;
    this.mod = {}

    this.defs = options.defs.slice(0)
    this.plugins = Object.create(null)
    for (var plugin in options.plugins) if (options.plugins.hasOwnProperty(plugin))
      this.loadPlugin(plugin, options.plugins[plugin])

    this.reset();
  };
  Server.prototype = signal.mixin({
    addFile: function(name, /*optional*/ text, parent) {
      // Don't crash when sloppy plugins pass non-existent parent ids
      if (parent && !(parent in this.fileMap)) parent = null;
      if (!(name in this.fileMap))
        name = this.normalizeFilename(name)
      ensureFile(this, name, parent, text);
    },
    delFile: function(name) {
      var file = this.findFile(name);
      if (file) {
        this.needsPurge.push(file.name);
        this.files.splice(this.files.indexOf(file), 1);
        delete this.fileMap[name];
      }
    },
    reset: function() {
      this.signal("reset");
      this.cx = new infer.Context(this.defs, this);
      this.uses = 0;
      this.budgets = Object.create(null);
      for (var i = 0; i < this.files.length; ++i) {
        var file = this.files[i];
        file.scope = null;
      }
      this.signal("postReset");
    },

    request: function(doc, c) {
      var inv = invalidDoc(doc);
      if (inv) return c(inv);

      var self = this;
      doRequest(this, doc, function(err, data) {
        c(err, data);
        if (self.uses > 40) {
          self.reset();
          analyzeAll(self, null, function(){});
        }
      });
    },

    findFile: function(name) {
      return this.fileMap[name];
    },

    flush: function(c) {
      var cx = this.cx;
      analyzeAll(this, null, function(err) {
        if (err) return c(err);
        infer.withContext(cx, c);
      });
    },

    startAsyncAction: function() {
      ++this.pending;
    },
    finishAsyncAction: function(err) {
      if (err) this.asyncError = err;
      if (--this.pending === 0) this.signal("everythingFetched");
    },

    addDefs: function(defs, toFront) {
      if (toFront) this.defs.unshift(defs)
      else this.defs.push(defs)

      if (this.cx) this.reset()
    },

    loadPlugin: function(name, options) {
      if (arguments.length == 1) options = this.options.plugins[name] || true
      if (name in this.plugins || !(name in plugins) || !options) return
      this.plugins[name] = true
      var init = plugins[name](this, options)

      // This is for backwards-compatibilty. Don't rely on it -- use addDef and on directly
      if (!init) return
      if (init.defs) this.addDefs(init.defs, init.loadFirst)
      if (init.passes) for (var type in init.passes) if (init.passes.hasOwnProperty(type))
        this.on(type, init.passes[type])
    },

    normalizeFilename: function(name) {
      var norm = this.options.normalizeFilename(name).replace(/\\/g, "/")
      if (norm.indexOf(this.projectDir) == 0) norm = norm.slice(this.projectDir.length)
      return norm
    }
  });

  function doRequest(srv, doc, c) {
    if (doc.query && !queryTypes.hasOwnProperty(doc.query.type))
      return c("No query type '" + doc.query.type + "' defined");

    var query = doc.query;
    // Respond as soon as possible when this just uploads files
    if (!query) c(null, {});

    var files = doc.files || [];
    if (files.length) ++srv.uses;
    for (var i = 0; i < files.length; ++i) {
      var file = files[i];
      if (file.type == "delete")
        srv.delFile(file.name);
      else
        ensureFile(srv, file.name, null, file.type == "full" ? file.text : null);
    }

    var timeBudget = typeof doc.timeout == "number" ? [doc.timeout] : null;
    if (!query) {
      analyzeAll(srv, timeBudget, function(){});
      return;
    }

    var queryType = queryTypes[query.type];
    if (queryType.takesFile) {
      if (typeof query.file != "string") return c(".query.file must be a string");
      if (!/^#/.test(query.file)) ensureFile(srv, query.file, null);
    }

    analyzeAll(srv, timeBudget, function(err) {
      if (err) return c(err);
      var file = queryType.takesFile && resolveFile(srv, files, query.file);
      if (queryType.fullFile && file.type == "part")
        return c("Can't run a " + query.type + " query on a file fragment");

      function run() {
        var result;
        try {
          result = queryType.run(srv, query, file);
        } catch (e) {
          if (srv.options.debug && e.name != "TernError") console.error(e.stack);
          return c(e);
        }
        c(null, result);
      }
      infer.resetGuessing()
      infer.withContext(srv.cx, timeBudget ? function() { infer.withTimeout(timeBudget[0], run); } : run);
    });
  }

  function analyzeFile(srv, file) {
    infer.withContext(srv.cx, function() {
      file.scope = srv.cx.topScope;
      srv.signal("beforeLoad", file);
      infer.analyze(file.ast, file.name, file.scope);
      srv.signal("afterLoad", file);
    });
    return file;
  }

  function ensureFile(srv, name, parent, text) {
    var known = srv.findFile(name);
    if (known) {
      if (text != null) {
        if (known.scope) {
          srv.needsPurge.push(name);
          known.scope = null;
        }
        updateText(known, text, srv);
      }
      if (parentDepth(srv, known.parent) > parentDepth(srv, parent)) {
        known.parent = parent;
        if (known.excluded) known.excluded = null;
      }
      return;
    }

    var file = new File(name, parent);
    srv.files.push(file);
    srv.fileMap[name] = file;
    if (text != null) {
      updateText(file, text, srv);
    } else if (srv.options.async) {
      srv.startAsyncAction();
      srv.options.getFile(name, function(err, text) {
        updateText(file, text || "", srv);
        srv.finishAsyncAction(err);
      });
    } else {
      updateText(file, srv.options.getFile(name) || "", srv);
    }
  }

  function fetchAll(srv, c) {
    var done = true, returned = false;
    srv.files.forEach(function(file) {
      if (file.text != null) return;
      if (srv.options.async) {
        done = false;
        srv.options.getFile(file.name, function(err, text) {
          if (err && !returned) { returned = true; return c(err); }
          updateText(file, text || "", srv);
          fetchAll(srv, c);
        });
      } else {
        try {
          updateText(file, srv.options.getFile(file.name) || "", srv);
        } catch (e) { return c(e); }
      }
    });
    if (done) c();
  }

  function waitOnFetch(srv, timeBudget, c) {
    var done = function() {
      srv.off("everythingFetched", done);
      clearTimeout(timeout);
      analyzeAll(srv, timeBudget, c);
    };
    srv.on("everythingFetched", done);
    var timeout = setTimeout(done, srv.options.fetchTimeout);
  }

  function analyzeAll(srv, timeBudget, c) {
    if (srv.pending) return waitOnFetch(srv, timeBudget, c);

    var e = srv.fetchError;
    if (e) { srv.fetchError = null; return c(e); }

    if (srv.needsPurge.length > 0) infer.withContext(srv.cx, function() {
      infer.purge(srv.needsPurge);
      srv.needsPurge.length = 0;
    });

    var done = true;
    // The second inner loop might add new files. The outer loop keeps
    // repeating both inner loops until all files have been looked at.
    for (var i = 0; i < srv.files.length;) {
      var toAnalyze = [];
      for (; i < srv.files.length; ++i) {
        var file = srv.files[i];
        if (file.text == null) done = false;
        else if (file.scope == null && !file.excluded) toAnalyze.push(file);
      }
      toAnalyze.sort(function(a, b) {
        return parentDepth(srv, a.parent) - parentDepth(srv, b.parent);
      });
      for (var j = 0; j < toAnalyze.length; j++) {
        var file = toAnalyze[j];
        if (file.parent && !chargeOnBudget(srv, file)) {
          file.excluded = true;
        } else if (timeBudget) {
          var startTime = +new Date;
          infer.withTimeout(timeBudget[0], function() { analyzeFile(srv, file); });
          timeBudget[0] -= +new Date - startTime;
        } else {
          analyzeFile(srv, file);
        }
      }
    }
    if (done) c();
    else waitOnFetch(srv, timeBudget, c);
  }

  function firstLine(str) {
    var end = str.indexOf("\n");
    if (end < 0) return str;
    return str.slice(0, end);
  }

  function findMatchingPosition(line, file, near) {
    var pos = Math.max(0, near - 500), closest = null;
    if (!/^\s*$/.test(line)) for (;;) {
      var found = file.indexOf(line, pos);
      if (found < 0 || found > near + 500) break;
      if (closest == null || Math.abs(closest - near) > Math.abs(found - near))
        closest = found;
      pos = found + line.length;
    }
    return closest;
  }

  function scopeDepth(s) {
    for (var i = 0; s; ++i, s = s.prev) {}
    return i;
  }

  function ternError(msg) {
    var err = new Error(msg);
    err.name = "TernError";
    return err;
  }

  function resolveFile(srv, localFiles, name) {
    var isRef = name.match(/^#(\d+)$/);
    if (!isRef) return srv.findFile(name);

    var file = localFiles[isRef[1]];
    if (!file || file.type == "delete") throw ternError("Reference to unknown file " + name);
    if (file.type == "full") return srv.findFile(file.name);

    // This is a partial file

    var realFile = file.backing = srv.findFile(file.name);
    var offset = file.offset;
    if (file.offsetLines) offset = {line: file.offsetLines, ch: 0};
    file.offset = offset = resolvePos(realFile, file.offsetLines == null ? file.offset : {line: file.offsetLines, ch: 0}, true);
    var line = firstLine(file.text);
    var foundPos = findMatchingPosition(line, realFile.text, offset);
    var pos = foundPos == null ? Math.max(0, realFile.text.lastIndexOf("\n", offset)) : foundPos;
    var inObject, atFunction;

    infer.withContext(srv.cx, function() {
      infer.purge(file.name, pos, pos + file.text.length);

      var text = file.text, m;
      if (m = text.match(/(?:"([^"]*)"|([\w$]+))\s*:\s*function\b/)) {
        var objNode = walk.findNodeAround(file.backing.ast, pos, "ObjectExpression");
        if (objNode && objNode.node.objType)
          inObject = {type: objNode.node.objType, prop: m[2] || m[1]};
      }
      if (foundPos && (m = line.match(/^(.*?)\bfunction\b/))) {
        var cut = m[1].length, white = "";
        for (var i = 0; i < cut; ++i) white += " ";
        file.text = white + text.slice(cut);
        atFunction = true;
      }

      var scopeStart = infer.scopeAt(realFile.ast, pos, realFile.scope);
      var scopeEnd = infer.scopeAt(realFile.ast, pos + text.length, realFile.scope);
      var scope = file.scope = scopeDepth(scopeStart) < scopeDepth(scopeEnd) ? scopeEnd : scopeStart;
      file.ast = parseFile(srv, file)
      infer.analyze(file.ast, file.name, scope);

      // This is a kludge to tie together the function types (if any)
      // outside and inside of the fragment, so that arguments and
      // return values have some information known about them.
      tieTogether: if (inObject || atFunction) {
        var newInner = infer.scopeAt(file.ast, line.length, scopeStart);
        if (!newInner.fnType) break tieTogether;
        if (inObject) {
          var prop = inObject.type.getProp(inObject.prop);
          prop.addType(newInner.fnType);
        } else if (atFunction) {
          var inner = infer.scopeAt(realFile.ast, pos + line.length, realFile.scope);
          if (inner == scopeStart || !inner.fnType) break tieTogether;
          var fOld = inner.fnType, fNew = newInner.fnType;
          if (!fNew || (fNew.name != fOld.name && fOld.name)) break tieTogether;
          for (var i = 0, e = Math.min(fOld.args.length, fNew.args.length); i < e; ++i)
            fOld.args[i].propagate(fNew.args[i]);
          fOld.self.propagate(fNew.self);
          fNew.retval.propagate(fOld.retval);
        }
      }
    });
    return file;
  }

  // Budget management

  function astSize(node) {
    var size = 0;
    walk.simple(node, {Expression: function() { ++size; }});
    return size;
  }

  function parentDepth(srv, parent) {
    var depth = 0;
    while (parent) {
      parent = srv.findFile(parent).parent;
      ++depth;
    }
    return depth;
  }

  function budgetName(srv, file) {
    for (;;) {
      var parent = srv.findFile(file.parent);
      if (!parent.parent) break;
      file = parent;
    }
    return file.name;
  }

  function chargeOnBudget(srv, file) {
    var bName = budgetName(srv, file);
    var size = astSize(file.ast);
    var known = srv.budgets[bName];
    if (known == null)
      known = srv.budgets[bName] = srv.options.dependencyBudget;
    if (known < size) return false;
    srv.budgets[bName] = known - size;
    return true;
  }

  // Query helpers

  function isPosition(val) {
    return typeof val == "number" || typeof val == "object" &&
      typeof val.line == "number" && typeof val.ch == "number";
  }

  // Baseline query document validation
  function invalidDoc(doc) {
    if (doc.query) {
      if (typeof doc.query.type != "string") return ".query.type must be a string";
      if (doc.query.start && !isPosition(doc.query.start)) return ".query.start must be a position";
      if (doc.query.end && !isPosition(doc.query.end)) return ".query.end must be a position";
    }
    if (doc.files) {
      if (!Array.isArray(doc.files)) return "Files property must be an array";
      for (var i = 0; i < doc.files.length; ++i) {
        var file = doc.files[i];
        if (typeof file != "object") return ".files[n] must be objects";
        else if (typeof file.name != "string") return ".files[n].name must be a string";
        else if (file.type == "delete") continue;
        else if (typeof file.text != "string") return ".files[n].text must be a string";
        else if (file.type == "part") {
          if (!isPosition(file.offset) && typeof file.offsetLines != "number")
            return ".files[n].offset must be a position";
        } else if (file.type != "full") return ".files[n].type must be \"full\" or \"part\"";
      }
    }
  }

  var offsetSkipLines = 25;

  function findLineStart(file, line) {
    var text = file.text, offsets = file.lineOffsets || (file.lineOffsets = [0]);
    var pos = 0, curLine = 0;
    var storePos = Math.min(Math.floor(line / offsetSkipLines), offsets.length - 1);
    var pos = offsets[storePos], curLine = storePos * offsetSkipLines;

    while (curLine < line) {
      ++curLine;
      pos = text.indexOf("\n", pos) + 1;
      if (pos === 0) return null;
      if (curLine % offsetSkipLines === 0) offsets.push(pos);
    }
    return pos;
  }

  var resolvePos = exports.resolvePos = function(file, pos, tolerant) {
    if (typeof pos != "number") {
      var lineStart = findLineStart(file, pos.line);
      if (lineStart == null) {
        if (tolerant) pos = file.text.length;
        else throw ternError("File doesn't contain a line " + pos.line);
      } else {
        pos = lineStart + pos.ch;
      }
    }
    if (pos > file.text.length) {
      if (tolerant) pos = file.text.length;
      else throw ternError("Position " + pos + " is outside of file.");
    }
    return pos;
  };

  function asLineChar(file, pos) {
    if (!file) return {line: 0, ch: 0};
    var offsets = file.lineOffsets || (file.lineOffsets = [0]);
    var text = file.text, line, lineStart;
    for (var i = offsets.length - 1; i >= 0; --i) if (offsets[i] <= pos) {
      line = i * offsetSkipLines;
      lineStart = offsets[i];
    }
    for (;;) {
      var eol = text.indexOf("\n", lineStart);
      if (eol >= pos || eol < 0) break;
      lineStart = eol + 1;
      ++line;
    }
    return {line: line, ch: pos - lineStart};
  }

  var outputPos = exports.outputPos = function(query, file, pos) {
    if (query.lineCharPositions) {
      var out = asLineChar(file, pos);
      if (file.type == "part")
        out.line += file.offsetLines != null ? file.offsetLines : asLineChar(file.backing, file.offset).line;
      return out;
    } else {
      return pos + (file.type == "part" ? file.offset : 0);
    }
  };

  // Delete empty fields from result objects
  function clean(obj) {
    for (var prop in obj) if (obj[prop] == null) delete obj[prop];
    return obj;
  }
  function maybeSet(obj, prop, val) {
    if (val != null) obj[prop] = val;
  }

  // Built-in query types

  function compareCompletions(a, b) {
    if (typeof a != "string") { a = a.name; b = b.name; }
    var aUp = /^[A-Z]/.test(a), bUp = /^[A-Z]/.test(b);
    if (aUp == bUp) return a < b ? -1 : a == b ? 0 : 1;
    else return aUp ? 1 : -1;
  }

  function isStringAround(node, start, end) {
    return node.type == "Literal" && typeof node.value == "string" &&
      node.start == start - 1 && node.end <= end + 1;
  }

  function pointInProp(objNode, point) {
    for (var i = 0; i < objNode.properties.length; i++) {
      var curProp = objNode.properties[i];
      if (curProp.key.start <= point && curProp.key.end >= point)
        return curProp;
    }
  }

  var jsKeywords = ("break do instanceof typeof case else new var " +
    "catch finally return void continue for switch while debugger " +
    "function this with default if throw delete in try").split(" ");

  var addCompletion = exports.addCompletion = function(query, completions, name, aval, depth) {
    var typeInfo = query.types || query.docs || query.urls || query.origins;
    var wrapAsObjs = typeInfo || query.depths;

    for (var i = 0; i < completions.length; ++i) {
      var c = completions[i];
      if ((wrapAsObjs ? c.name : c) == name) return;
    }
    var rec = wrapAsObjs ? {name: name} : name;
    completions.push(rec);

    if (aval && typeInfo) {
      infer.resetGuessing();
      var type = aval.getType();
      rec.guess = infer.didGuess();
      if (query.types)
        rec.type = infer.toString(aval);
      if (query.docs)
        maybeSet(rec, "doc", parseDoc(query, aval.doc || type && type.doc));
      if (query.urls)
        maybeSet(rec, "url", aval.url || type && type.url);
      if (query.origins)
        maybeSet(rec, "origin", aval.origin || type && type.origin);
    }
    if (query.depths) rec.depth = depth || 0;
    return rec;
  };

  function findCompletions(srv, query, file) {
    if (query.end == null) throw ternError("missing .query.end field");
    var fromPlugin = srv.signalReturnFirst("completion", file, query)
    if (fromPlugin) return fromPlugin

    var wordStart = resolvePos(file, query.end), wordEnd = wordStart, text = file.text;
    while (wordStart && acorn.isIdentifierChar(text.charCodeAt(wordStart - 1))) --wordStart;
    if (query.expandWordForward !== false)
      while (wordEnd < text.length && acorn.isIdentifierChar(text.charCodeAt(wordEnd))) ++wordEnd;
    var word = text.slice(wordStart, wordEnd), completions = [], ignoreObj;
    if (query.caseInsensitive) word = word.toLowerCase();

    function gather(prop, obj, depth, addInfo) {
      // 'hasOwnProperty' and such are usually just noise, leave them
      // out when no prefix is provided.
      if ((objLit || query.omitObjectPrototype !== false) && obj == srv.cx.protos.Object && !word) return;
      if (query.filter !== false && word &&
          (query.caseInsensitive ? prop.toLowerCase() : prop).indexOf(word) !== 0) return;
      if (ignoreObj && ignoreObj.props[prop]) return;
      var result = addCompletion(query, completions, prop, obj && obj.props[prop], depth);
      if (addInfo && result && typeof result != "string") addInfo(result);
    }

    var hookname, prop, objType, isKey;

    var exprAt = infer.findExpressionAround(file.ast, null, wordStart, file.scope);
    var memberExpr, objLit;
    // Decide whether this is an object property, either in a member
    // expression or an object literal.
    if (exprAt) {
      var exprNode = exprAt.node;
      if (exprNode.type == "MemberExpression" && exprNode.object.end < wordStart) {
        memberExpr = exprAt;
      } else if (isStringAround(exprNode, wordStart, wordEnd)) {
        var parent = infer.parentNode(exprNode, file.ast);
        if (parent.type == "MemberExpression" && parent.property == exprNode)
          memberExpr = {node: parent, state: exprAt.state};
      } else if (exprNode.type == "ObjectExpression") {
        var objProp = pointInProp(exprNode, wordEnd);
        if (objProp) {
          objLit = exprAt;
          prop = isKey = objProp.key.name;
        } else if (!word && !/:\s*$/.test(file.text.slice(0, wordStart))) {
          objLit = exprAt;
          prop = isKey = true;
        }
      }
    }

    if (objLit) {
      // Since we can't use the type of the literal itself to complete
      // its properties (it doesn't contain the information we need),
      // we have to try asking the surrounding expression for type info.
      objType = infer.typeFromContext(file.ast, objLit);
      ignoreObj = objLit.node.objType;
    } else if (memberExpr) {
      prop = memberExpr.node.property;
      prop = prop.type == "Literal" ? prop.value.slice(1) : prop.name;
      memberExpr.node = memberExpr.node.object;
      objType = infer.expressionType(memberExpr);
    } else if (text.charAt(wordStart - 1) == ".") {
      var pathStart = wordStart - 1;
      while (pathStart && (text.charAt(pathStart - 1) == "." || acorn.isIdentifierChar(text.charCodeAt(pathStart - 1)))) pathStart--;
      var path = text.slice(pathStart, wordStart - 1);
      if (path) {
        objType = infer.def.parsePath(path, file.scope).getObjType();
        prop = word;
      }
    }

    if (prop != null) {
      srv.cx.completingProperty = prop;

      if (objType) infer.forAllPropertiesOf(objType, gather);

      if (!completions.length && query.guess !== false && objType && objType.guessProperties)
        objType.guessProperties(function(p, o, d) {if (p != prop && p != "✖") gather(p, o, d);});
      if (!completions.length && word.length >= 2 && query.guess !== false)
        for (var prop in srv.cx.props) gather(prop, srv.cx.props[prop][0], 0);
      hookname = "memberCompletion";
    } else {
      infer.forAllLocalsAt(file.ast, wordStart, file.scope, gather);
      if (query.includeKeywords) jsKeywords.forEach(function(kw) {
        gather(kw, null, 0, function(rec) { rec.isKeyword = true; });
      });
      hookname = "variableCompletion";
    }
    srv.signal(hookname, file, wordStart, wordEnd, gather)

    if (query.sort !== false) completions.sort(compareCompletions);
    srv.cx.completingProperty = null;

    return {start: outputPos(query, file, wordStart),
            end: outputPos(query, file, wordEnd),
            isProperty: !!prop,
            isObjectKey: !!isKey,
            completions: completions};
  }

  function findProperties(srv, query) {
    var prefix = query.prefix, found = [];
    for (var prop in srv.cx.props)
      if (prop != "<i>" && (!prefix || prop.indexOf(prefix) === 0)) found.push(prop);
    if (query.sort !== false) found.sort(compareCompletions);
    return {completions: found};
  }

  var findExpr = exports.findQueryExpr = function(file, query, wide) {
    if (query.end == null) throw ternError("missing .query.end field");

    if (query.variable) {
      var scope = infer.scopeAt(file.ast, resolvePos(file, query.end), file.scope);
      return {node: {type: "Identifier", name: query.variable, start: query.end, end: query.end + 1},
              state: scope};
    } else {
      var start = query.start && resolvePos(file, query.start), end = resolvePos(file, query.end);
      var expr = infer.findExpressionAt(file.ast, start, end, file.scope);
      if (expr) return expr;
      expr = infer.findExpressionAround(file.ast, start, end, file.scope);
      if (expr && (expr.node.type == "ObjectExpression" || wide ||
                   (start == null ? end : start) - expr.node.start < 20 || expr.node.end - end < 20))
        return expr;
      return null;
    }
  };

  function findExprOrThrow(file, query, wide) {
    var expr = findExpr(file, query, wide);
    if (expr) return expr;
    throw ternError("No expression at the given position.");
  }

  function ensureObj(tp) {
    if (!tp || !(tp = tp.getType()) || !(tp instanceof infer.Obj)) return null;
    return tp;
  }

  function findExprType(srv, query, file, expr) {
    var type;
    if (expr) {
      infer.resetGuessing();
      type = infer.expressionType(expr);
    }
    var typeHandlers = srv.hasHandler("typeAt")
    if (typeHandlers) {
      var pos = resolvePos(file, query.end)
      for (var i = 0; i < typeHandlers.length; i++)
        type = typeHandlers[i](file, pos, expr, type)
    }
    if (!type) throw ternError("No type found at the given position.");

    var objProp;
    if (expr.node.type == "ObjectExpression" && query.end != null &&
        (objProp = pointInProp(expr.node, resolvePos(file, query.end)))) {
      var name = objProp.key.name;
      var fromCx = ensureObj(infer.typeFromContext(file.ast, expr));
      if (fromCx && fromCx.hasProp(name)) {
        type = fromCx.hasProp(name);
      } else {
        var fromLocal = ensureObj(type);
        if (fromLocal && fromLocal.hasProp(name))
          type = fromLocal.hasProp(name);
      }
    }
    return type;
  };

  function findTypeAt(srv, query, file) {
    var expr = findExpr(file, query), exprName;
    var type = findExprType(srv, query, file, expr), exprType = type;
    if (query.preferFunction)
      type = type.getFunctionType() || type.getType();
    else
      type = type.getType();

    if (expr) {
      if (expr.node.type == "Identifier")
        exprName = expr.node.name;
      else if (expr.node.type == "MemberExpression" && !expr.node.computed)
        exprName = expr.node.property.name;
    }

    if (query.depth != null && typeof query.depth != "number")
      throw ternError(".query.depth must be a number");

    var result = {guess: infer.didGuess(),
                  type: infer.toString(exprType, query.depth),
                  name: type && type.name,
                  exprName: exprName,
                  doc: exprType.doc,
                  url: exprType.url};
    if (type) storeTypeDocs(query, type, result);

    return clean(result);
  }

  function parseDoc(query, doc) {
    if (!doc) return null;
    if (query.docFormat == "full") return doc;
    var parabreak = /.\n[\s@\n]/.exec(doc);
    if (parabreak) doc = doc.slice(0, parabreak.index + 1);
    doc = doc.replace(/\n\s*/g, " ");
    if (doc.length < 100) return doc;
    var sentenceEnd = /[\.!?] [A-Z]/g;
    sentenceEnd.lastIndex = 80;
    var found = sentenceEnd.exec(doc);
    if (found) doc = doc.slice(0, found.index + 1);
    return doc;
  }

  function findDocs(srv, query, file) {
    var expr = findExpr(file, query);
    var type = findExprType(srv, query, file, expr);
    var result = {url: type.url, doc: parseDoc(query, type.doc), type: infer.toString(type)};
    var inner = type.getType();
    if (inner) storeTypeDocs(query, inner, result);
    return clean(result);
  }

  function storeTypeDocs(query, type, out) {
    if (!out.url) out.url = type.url;
    if (!out.doc) out.doc = parseDoc(query, type.doc);
    if (!out.origin) out.origin = type.origin;
    var ctor, boring = infer.cx().protos;
    if (!out.url && !out.doc && type.proto && (ctor = type.proto.hasCtor) &&
        type.proto != boring.Object && type.proto != boring.Function && type.proto != boring.Array) {
      out.url = ctor.url;
      out.doc = parseDoc(query, ctor.doc);
    }
  }

  var getSpan = exports.getSpan = function(obj) {
    if (!obj.origin) return;
    if (obj.originNode) {
      var node = obj.originNode;
      if (/^Function/.test(node.type) && node.id) node = node.id;
      return {origin: obj.origin, node: node};
    }
    if (obj.span) return {origin: obj.origin, span: obj.span};
  };

  var storeSpan = exports.storeSpan = function(srv, query, span, target) {
    target.origin = span.origin;
    if (span.span) {
      var m = /^(\d+)\[(\d+):(\d+)\]-(\d+)\[(\d+):(\d+)\]$/.exec(span.span);
      target.start = query.lineCharPositions ? {line: Number(m[2]), ch: Number(m[3])} : Number(m[1]);
      target.end = query.lineCharPositions ? {line: Number(m[5]), ch: Number(m[6])} : Number(m[4]);
    } else {
      var file = srv.findFile(span.origin);
      target.start = outputPos(query, file, span.node.start);
      target.end = outputPos(query, file, span.node.end);
    }
  };

  function findDef(srv, query, file) {
    var expr = findExpr(file, query);
    var type = findExprType(srv, query, file, expr);
    if (infer.didGuess()) return {};

    var span = getSpan(type);
    var result = {url: type.url, doc: parseDoc(query, type.doc), origin: type.origin};

    if (type.types) for (var i = type.types.length - 1; i >= 0; --i) {
      var tp = type.types[i];
      storeTypeDocs(query, tp, result);
      if (!span) span = getSpan(tp);
    }

    if (span && span.node) { // refers to a loaded file
      var spanFile = span.node.sourceFile || srv.findFile(span.origin);
      var start = outputPos(query, spanFile, span.node.start), end = outputPos(query, spanFile, span.node.end);
      result.start = start; result.end = end;
      result.file = span.origin;
      var cxStart = Math.max(0, span.node.start - 50);
      result.contextOffset = span.node.start - cxStart;
      result.context = spanFile.text.slice(cxStart, cxStart + 50);
    } else if (span) { // external
      result.file = span.origin;
      storeSpan(srv, query, span, result);
    }
    return clean(result);
  }

  function findRefsToVariable(srv, query, file, expr, checkShadowing) {
    var name = expr.node.name;

    for (var scope = expr.state; scope && !(name in scope.props); scope = scope.prev) {}
    if (!scope) throw ternError("Could not find a definition for " + name);

    var type, refs = [];
    function storeRef(file) {
      return function(node, scopeHere) {
        if (checkShadowing) for (var s = scopeHere; s != scope; s = s.prev) {
          var exists = s.hasProp(checkShadowing);
          if (exists)
            throw ternError("Renaming `" + name + "` to `" + checkShadowing + "` would make a variable at line " +
                            (asLineChar(file, node.start).line + 1) + " point to the definition at line " +
                            (asLineChar(file, exists.name.start).line + 1));
        }
        refs.push({file: file.name,
                   start: outputPos(query, file, node.start),
                   end: outputPos(query, file, node.end)});
      };
    }

    if (scope.originNode) {
      type = "local";
      if (checkShadowing) {
        for (var prev = scope.prev; prev; prev = prev.prev)
          if (checkShadowing in prev.props) break;
        if (prev) infer.findRefs(scope.originNode, scope, checkShadowing, prev, function(node) {
          throw ternError("Renaming `" + name + "` to `" + checkShadowing + "` would shadow the definition used at line " +
                          (asLineChar(file, node.start).line + 1));
        });
      }
      infer.findRefs(scope.originNode, scope, name, scope, storeRef(file));
    } else {
      type = "global";
      for (var i = 0; i < srv.files.length; ++i) {
        var cur = srv.files[i];
        infer.findRefs(cur.ast, cur.scope, name, scope, storeRef(cur));
      }
    }

    return {refs: refs, type: type, name: name};
  }

  function findRefsToProperty(srv, query, expr, prop) {
    var objType = infer.expressionType(expr).getObjType();
    if (!objType) throw ternError("Couldn't determine type of base object.");

    var refs = [];
    function storeRef(file) {
      return function(node) {
        refs.push({file: file.name,
                   start: outputPos(query, file, node.start),
                   end: outputPos(query, file, node.end)});
      };
    }
    for (var i = 0; i < srv.files.length; ++i) {
      var cur = srv.files[i];
      infer.findPropRefs(cur.ast, cur.scope, objType, prop.name, storeRef(cur));
    }

    return {refs: refs, name: prop.name};
  }

  function findRefs(srv, query, file) {
    var expr = findExprOrThrow(file, query, true);
    if (expr && expr.node.type == "Identifier") {
      return findRefsToVariable(srv, query, file, expr);
    } else if (expr && expr.node.type == "MemberExpression" && !expr.node.computed) {
      var p = expr.node.property;
      expr.node = expr.node.object;
      return findRefsToProperty(srv, query, expr, p);
    } else if (expr && expr.node.type == "ObjectExpression") {
      var pos = resolvePos(file, query.end);
      for (var i = 0; i < expr.node.properties.length; ++i) {
        var k = expr.node.properties[i].key;
        if (k.start <= pos && k.end >= pos)
          return findRefsToProperty(srv, query, expr, k);
      }
    }
    throw ternError("Not at a variable or property name.");
  }

  function buildRename(srv, query, file) {
    if (typeof query.newName != "string") throw ternError(".query.newName should be a string");
    var expr = findExprOrThrow(file, query);
    if (!expr || expr.node.type != "Identifier") throw ternError("Not at a variable.");

    var data = findRefsToVariable(srv, query, file, expr, query.newName), refs = data.refs;
    delete data.refs;
    data.files = srv.files.map(function(f){return f.name;});

    var changes = data.changes = [];
    for (var i = 0; i < refs.length; ++i) {
      var use = refs[i];
      use.text = query.newName;
      changes.push(use);
    }

    return data;
  }

  function listFiles(srv) {
    return {files: srv.files.map(function(f){return f.name;})};
  }

  exports.version = "0.16.0";
});
PK
!<?	��Q	Q	<chrome/devtools/modules/devtools/client/sourceeditor/wasm.js/* -*- indent-tabs-mode: nil; js-indent-level: 2; fill-column: 80 -*- */
/* vim:set ts=2 sw=2 sts=2 et tw=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 wasmparser = require("devtools/client/shared/vendor/WasmParser");
const wasmdis = require("devtools/client/shared/vendor/WasmDis");

const wasmStates = new WeakMap();

function getWasmText(subject, data) {
  let parser = new wasmparser.BinaryReader();
  parser.setData(data.buffer, 0, data.length);
  let dis = new wasmdis.WasmDisassembler();
  dis.addOffsets = true;
  let done = dis.disassembleChunk(parser);
  let result = dis.getResult();
  if (result.lines.length === 0) {
    result = { lines: ["No luck with wast conversion"], offsets: [0], done, };
  }

  let 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;
  let buffer =
    [codeOf0, codeOf0, codeOf0, codeOf0, codeOf0, codeOf0, codeOf0, codeOf0];
  let last0 = 7;
  return function (number) {
    let offset = lineToWasmOffset(subject, number - 1);
    if (offset === undefined) {
      return "";
    }
    let i = 7;
    for (let n = offset | 0; n !== 0 && i >= 0; n >>= 4, i--) {
      let 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) {
  let 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) {
  let wasmState = wasmStates.get(subject);
  return wasmState.lines[offset];
}

module.exports = {
  getWasmText,
  getWasmLineNumberFormatter,
  isWasm,
  lineToWasmOffset,
  wasmOffsetToLine,
};
PK
!<�np^��8chrome/devtools/modules/devtools/client/storage/panel.js/* -*- Mode: Javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 EventEmitter = require("devtools/shared/old-event-emitter");

loader.lazyRequireGetter(this, "StorageFront",
                         "devtools/shared/fronts/storage", true);
loader.lazyRequireGetter(this, "StorageUI",
                         "devtools/client/storage/ui", true);

class StoragePanel {
  constructor(panelWin, toolbox) {
    EventEmitter.decorate(this);

    this._toolbox = toolbox;
    this._target = toolbox.target;
    this._panelWin = panelWin;

    this.destroy = this.destroy.bind(this);
  }

  get target() {
    return this._toolbox.target;
  }

  get panelWindow() {
    return this._panelWin;
  }

  /**
   * open is effectively an asynchronous constructor
   */
  open() {
    let targetPromise;
    // We always interact with the target as if it were remote
    if (!this.target.isRemote) {
      targetPromise = this.target.makeRemote();
    } else {
      targetPromise = Promise.resolve(this.target);
    }

    return targetPromise.then(() => {
      this.target.on("close", this.destroy);
      this._front = new StorageFront(this.target.client, this.target.form);

      this.UI = new StorageUI(this._front, this._target,
                              this._panelWin, this._toolbox);
      this.isReady = true;
      this.emit("ready");

      return this;
    }).catch(e => {
      console.log("error while opening storage panel", e);
      this.destroy();
    });
  }

  /**
   * Destroy the storage inspector.
   */
  destroy() {
    if (!this._destroyed) {
      this.UI.destroy();
      this.UI = null;

      // Destroy front to ensure packet handler is removed from client
      this._front.destroy();
      this._front = null;
      this._destroyed = true;

      this._target.off("close", this.destroy);
      this._target = null;
      this._toolbox = null;
      this._panelWin = null;
    }

    return Promise.resolve(null);
  }
}

exports.StoragePanel = StoragePanel;
PK
!<�*���5chrome/devtools/modules/devtools/client/storage/ui.js/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/shared/old-event-emitter");
const {LocalizationHelper, ELLIPSIS} = require("devtools/shared/l10n");
const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
const JSOL = require("devtools/client/shared/vendor/jsol");
const {KeyCodes} = require("devtools/client/shared/keycodes");

// GUID to be used as a separator in compound keys. This must match the same
// constant in devtools/server/actors/storage.js,
// devtools/client/storage/test/head.js and
// devtools/server/tests/browser/head.js
const SEPARATOR_GUID = "{9d414cc5-8319-0a04-0586-c0a6ae01670a}";

loader.lazyRequireGetter(this, "TreeWidget",
                         "devtools/client/shared/widgets/TreeWidget", true);
loader.lazyRequireGetter(this, "TableWidget",
                         "devtools/client/shared/widgets/TableWidget", true);
loader.lazyRequireGetter(this, "ViewHelpers",
                         "devtools/client/shared/widgets/view-helpers");
loader.lazyImporter(this, "VariablesView",
  "resource://devtools/client/shared/widgets/VariablesView.jsm");

/**
 * Localization convenience methods.
 */
const STORAGE_STRINGS = "devtools/client/locales/storage.properties";
const L10N = new LocalizationHelper(STORAGE_STRINGS);

const GENERIC_VARIABLES_VIEW_SETTINGS = {
  lazyEmpty: true,
   // ms
  lazyEmptyDelay: 10,
  searchEnabled: true,
  searchPlaceholder: L10N.getStr("storage.search.placeholder"),
  preventDescriptorModifiers: true
};

const REASON = {
  NEW_ROW: "new-row",
  NEXT_50_ITEMS: "next-50-items",
  POPULATE: "populate",
  UPDATE: "update"
};

const COOKIE_KEY_MAP = {
  path: "Path",
  host: "Domain",
  expires: "Expires",
  isSecure: "Secure",
  isHttpOnly: "HttpOnly",
  isDomain: "HostOnly",
  creationTime: "CreationTime",
  lastAccessed: "LastAccessed"
};

// Maximum length of item name to show in context menu label - will be
// trimmed with ellipsis if it's longer.
const ITEM_NAME_MAX_LENGTH = 32;

/**
 * StorageUI is controls and builds the UI of the Storage Inspector.
 *
 * @param {Front} front
 *        Front for the storage actor
 * @param {Target} target
 *        Interface for the page we're debugging
 * @param {Window} panelWin
 *        Window of the toolbox panel to populate UI in.
 */
class StorageUI {
  constructor(front, target, panelWin, toolbox) {
    EventEmitter.decorate(this);

    this._target = target;
    this._window = panelWin;
    this._panelDoc = panelWin.document;
    this._toolbox = toolbox;
    this.front = front;
    this.storageTypes = null;
    this.sidebarToggledOpen = null;
    this.shouldLoadMoreItems = true;

    let treeNode = this._panelDoc.getElementById("storage-tree");
    this.tree = new TreeWidget(treeNode, {
      defaultType: "dir",
      contextMenuId: "storage-tree-popup"
    });
    this.onHostSelect = this.onHostSelect.bind(this);
    this.tree.on("select", this.onHostSelect);

    let tableNode = this._panelDoc.getElementById("storage-table");
    this.table = new TableWidget(tableNode, {
      emptyText: L10N.getStr("table.emptyText"),
      highlightUpdated: true,
      cellContextMenuId: "storage-table-popup"
    });

    this.updateObjectSidebar = this.updateObjectSidebar.bind(this);
    this.table.on(TableWidget.EVENTS.ROW_SELECTED, this.updateObjectSidebar);

    this.handleScrollEnd = this.handleScrollEnd.bind(this);
    this.table.on(TableWidget.EVENTS.SCROLL_END, this.handleScrollEnd);

    this.editItem = this.editItem.bind(this);
    this.table.on(TableWidget.EVENTS.CELL_EDIT, this.editItem);

    this.sidebar = this._panelDoc.getElementById("storage-sidebar");
    this.sidebar.setAttribute("width", "300");
    this.view = new VariablesView(this.sidebar.firstChild,
                                  GENERIC_VARIABLES_VIEW_SETTINGS);

    this.filterItems = this.filterItems.bind(this);
    this.onPaneToggleButtonClicked = this.onPaneToggleButtonClicked.bind(this);
    this.setupToolbar();

    let shortcuts = new KeyShortcuts({
      window: this._panelDoc.defaultView,
    });
    let key = L10N.getStr("storage.filter.key");
    shortcuts.on(key, event => {
      event.preventDefault();
      this.searchBox.focus();
    });

    this.front.listStores().then(storageTypes => {
      this.populateStorageTree(storageTypes);
    }).catch(e => {
      if (!this._toolbox || this._toolbox._destroyer) {
        // The toolbox is in the process of being destroyed... in this case throwing here
        // is expected and normal so let's ignore the error.
        return;
      }

      // The toolbox is open so the error is unexpected and real so let's log it.
      console.error(e);
    });

    this.onUpdate = this.onUpdate.bind(this);
    this.front.on("stores-update", this.onUpdate);
    this.onCleared = this.onCleared.bind(this);
    this.front.on("stores-cleared", this.onCleared);

    this.handleKeypress = this.handleKeypress.bind(this);
    this._panelDoc.addEventListener("keypress", this.handleKeypress);

    this.onTreePopupShowing = this.onTreePopupShowing.bind(this);
    this._treePopup = this._panelDoc.getElementById("storage-tree-popup");
    this._treePopup.addEventListener("popupshowing", this.onTreePopupShowing);

    this.onTablePopupShowing = this.onTablePopupShowing.bind(this);
    this._tablePopup = this._panelDoc.getElementById("storage-table-popup");
    this._tablePopup.addEventListener("popupshowing", this.onTablePopupShowing);

    this.onRefreshTable = this.onRefreshTable.bind(this);
    this.onAddItem = this.onAddItem.bind(this);
    this.onRemoveItem = this.onRemoveItem.bind(this);
    this.onRemoveAllFrom = this.onRemoveAllFrom.bind(this);
    this.onRemoveAll = this.onRemoveAll.bind(this);
    this.onRemoveAllSessionCookies = this.onRemoveAllSessionCookies.bind(this);
    this.onRemoveTreeItem = this.onRemoveTreeItem.bind(this);

    this._refreshButton = this._panelDoc.getElementById("refresh-button");
    this._refreshButton.addEventListener("command", this.onRefreshTable);

    this._addButton = this._panelDoc.getElementById("add-button");
    this._addButton.addEventListener("command", this.onAddItem);

    this._tablePopupAddItem = this._panelDoc.getElementById(
      "storage-table-popup-add");
    this._tablePopupAddItem.addEventListener("command", this.onAddItem);

    this._tablePopupDelete = this._panelDoc.getElementById(
      "storage-table-popup-delete");
    this._tablePopupDelete.addEventListener("command", this.onRemoveItem);

    this._tablePopupDeleteAllFrom = this._panelDoc.getElementById(
      "storage-table-popup-delete-all-from");
    this._tablePopupDeleteAllFrom.addEventListener("command",
      this.onRemoveAllFrom);

    this._tablePopupDeleteAll = this._panelDoc.getElementById(
      "storage-table-popup-delete-all");
    this._tablePopupDeleteAll.addEventListener("command", this.onRemoveAll);

    this._tablePopupDeleteAllSessionCookies = this._panelDoc.getElementById(
      "storage-table-popup-delete-all-session-cookies");
    this._tablePopupDeleteAllSessionCookies.addEventListener("command",
      this.onRemoveAllSessionCookies);

    this._treePopupDeleteAll = this._panelDoc.getElementById(
      "storage-tree-popup-delete-all");
    this._treePopupDeleteAll.addEventListener("command", this.onRemoveAll);

    this._treePopupDeleteAllSessionCookies = this._panelDoc.getElementById(
      "storage-tree-popup-delete-all-session-cookies");
    this._treePopupDeleteAllSessionCookies.addEventListener("command",
      this.onRemoveAllSessionCookies);

    this._treePopupDelete = this._panelDoc.getElementById("storage-tree-popup-delete");
    this._treePopupDelete.addEventListener("command", this.onRemoveTreeItem);
  }

  set animationsEnabled(value) {
    this._panelDoc.documentElement.classList.toggle("no-animate", !value);
  }

  destroy() {
    this.table.off(TableWidget.EVENTS.ROW_SELECTED, this.updateObjectSidebar);
    this.table.off(TableWidget.EVENTS.SCROLL_END, this.handleScrollEnd);
    this.table.off(TableWidget.EVENTS.CELL_EDIT, this.editItem);
    this.table.destroy();

    this.front.off("stores-update", this.onUpdate);
    this.front.off("stores-cleared", this.onCleared);
    this._panelDoc.removeEventListener("keypress", this.handleKeypress);
    this.searchBox.removeEventListener("input", this.filterItems);
    this.searchBox = null;

    this.sidebarToggleBtn.removeEventListener("click", this.onPaneToggleButtonClicked);
    this.sidebarToggleBtn = null;

    this._treePopup.removeEventListener("popupshowing", this.onTreePopupShowing);
    this._refreshButton.removeEventListener("command", this.onRefreshTable);
    this._addButton.removeEventListener("command", this.onAddItem);
    this._tablePopupAddItem.removeEventListener("command", this.onAddItem);
    this._treePopupDeleteAll.removeEventListener("command", this.onRemoveAll);
    this._treePopupDeleteAllSessionCookies.removeEventListener("command",
      this.onRemoveAllSessionCookies);
    this._treePopupDelete.removeEventListener("command", this.onRemoveTreeItem);

    this._tablePopup.removeEventListener("popupshowing", this.onTablePopupShowing);
    this._tablePopupDelete.removeEventListener("command", this.onRemoveItem);
    this._tablePopupDeleteAllFrom.removeEventListener("command", this.onRemoveAllFrom);
    this._tablePopupDeleteAll.removeEventListener("command", this.onRemoveAll);
    this._tablePopupDeleteAllSessionCookies.removeEventListener("command",
      this.onRemoveAllSessionCookies);
  }

  setupToolbar() {
    this.searchBox = this._panelDoc.getElementById("storage-searchbox");
    this.searchBox.addEventListener("command", this.filterItems);

    // Setup the sidebar toggle button.
    this.sidebarToggleBtn = this._panelDoc.querySelector(".sidebar-toggle");
    this.updateSidebarToggleButton();

    this.sidebarToggleBtn.addEventListener("click", this.onPaneToggleButtonClicked);
  }

  onPaneToggleButtonClicked() {
    if (this.sidebar.hidden && this.table.selectedRow) {
      this.sidebar.hidden = false;
      this.sidebarToggledOpen = true;
      this.updateSidebarToggleButton();
    } else {
      this.sidebarToggledOpen = false;
      this.hideSidebar();
    }
  }

  updateSidebarToggleButton() {
    let title;
    this.sidebarToggleBtn.hidden = !this.table.hasSelectedRow;

    if (this.sidebar.hidden) {
      this.sidebarToggleBtn.classList.add("pane-collapsed");
      title = L10N.getStr("storage.expandPane");
    } else {
      this.sidebarToggleBtn.classList.remove("pane-collapsed");
      title = L10N.getStr("storage.collapsePane");
    }

    this.sidebarToggleBtn.setAttribute("tooltiptext", title);
  }

  /**
   * Hide the object viewer sidebar
   */
  hideSidebar() {
    this.sidebar.hidden = true;
    this.updateSidebarToggleButton();
  }

  getCurrentFront() {
    let type = this.table.datatype;

    return this.storageTypes[type];
  }

  /**
   *  Make column fields editable
   *
   *  @param {Array} editableFields
   *         An array of keys of columns to be made editable
   */
  makeFieldsEditable(editableFields) {
    if (editableFields && editableFields.length > 0) {
      this.table.makeFieldsEditable(editableFields);
    } else if (this.table._editableFieldsEngine) {
      this.table._editableFieldsEngine.destroy();
    }
  }

  editItem(data) {
    let front = this.getCurrentFront();

    front.editItem(data);
  }

  /**
   * Removes the given item from the storage table. Reselects the next item in
   * the table and repopulates the sidebar with that item's data if the item
   * being removed was selected.
   */
  async removeItemFromTable(name) {
    if (this.table.isSelected(name) && this.table.items.size > 1) {
      if (this.table.selectedIndex == 0) {
        this.table.selectNextRow();
      } else {
        this.table.selectPreviousRow();
      }
    }

    this.table.remove(name);
    await this.updateObjectSidebar();
  }

  /**
   * Event handler for "stores-cleared" event coming from the storage actor.
   *
   * @param {object} response
   *        An object containing which storage types were cleared
   */
  onCleared(response) {
    function* enumPaths() {
      for (let type in response) {
        if (Array.isArray(response[type])) {
          // Handle the legacy response with array of hosts
          for (let host of response[type]) {
            yield [type, host];
          }
        } else {
          // Handle the new format that supports clearing sub-stores in a host
          for (let host in response[type]) {
            let paths = response[type][host];

            if (!paths.length) {
              yield [type, host];
            } else {
              for (let path of paths) {
                try {
                  path = JSON.parse(path);
                  yield [type, host, ...path];
                } catch (ex) {
                  // ignore
                }
              }
            }
          }
        }
      }
    }

    for (let path of enumPaths()) {
      // Find if the path is selected (there is max one) and clear it
      if (this.tree.isSelected(path)) {
        this.table.clear();
        this.hideSidebar();
        this.emit("store-objects-cleared");
        break;
      }
    }
  }

  /**
   * Event handler for "stores-update" event coming from the storage actor.
   *
   * @param {object} argument0
   *        An object containing the details of the added, changed and deleted
   *        storage objects.
   *        Each of these 3 objects are of the following format:
   *        {
   *          <store_type1>: {
   *            <host1>: [<store_names1>, <store_name2>...],
   *            <host2>: [<store_names34>...], ...
   *          },
   *          <store_type2>: {
   *            <host1>: [<store_names1>, <store_name2>...],
   *            <host2>: [<store_names34>...], ...
   *          }, ...
   *        }
   *        Where store_type1 and store_type2 is one of cookies, indexedDB,
   *        sessionStorage and localStorage; host1, host2 are the host in which
   *        this change happened; and [<store_namesX] is an array of the names
   *        of the changed store objects. This array is empty for deleted object
   *        if the host was completely removed.
   */
  async onUpdate({ changed, added, deleted }) {
    if (added) {
      await this.handleAddedItems(added);
    }

    if (changed) {
      await this.handleChangedItems(changed);
    }

    // We are dealing with batches of changes here. Deleted **MUST** come last in case it
    // is in the same batch as added and changed events e.g.
    //   - An item is changed then deleted in the same batch: deleted then changed will
    //     display an item that has been deleted.
    //   - An item is added then deleted in the same batch: deleted then added will
    //     display an item that has been deleted.
    if (deleted) {
      await this.handleDeletedItems(deleted);
    }

    if (added || deleted || changed) {
      this.emit("store-objects-updated");
    }
  }

  /**
   * Handle added items received by onUpdate
   *
   * @param {object} See onUpdate docs
   */
  async handleAddedItems(added) {
    for (let type in added) {
      for (let host in added[type]) {
        this.tree.add([type, {id: host, type: "url"}]);
        for (let name of added[type][host]) {
          try {
            name = JSON.parse(name);
            if (name.length == 3) {
              name.splice(2, 1);
            }
            this.tree.add([type, host, ...name]);
            if (!this.tree.selectedItem) {
              this.tree.selectedItem = [type, host, name[0], name[1]];
              await this.fetchStorageObjects(type, host, [JSON.stringify(name)],
                                              REASON.NEW_ROW);
            }
          } catch (ex) {
            // Do nothing
          }
        }

        if (this.tree.isSelected([type, host])) {
          await this.fetchStorageObjects(type, host, added[type][host],
                                          REASON.NEW_ROW);
        }
      }
    }
  }

  /**
   * Handle deleted items received by onUpdate
   *
   * @param {object} See onUpdate docs
   */
  async handleDeletedItems(deleted) {
    for (let type in deleted) {
      for (let host in deleted[type]) {
        if (!deleted[type][host].length) {
          // This means that the whole host is deleted, thus the item should
          // be removed from the storage tree
          if (this.tree.isSelected([type, host])) {
            this.table.clear();
            this.hideSidebar();
            this.tree.selectPreviousItem();
          }

          this.tree.remove([type, host]);
        } else {
          for (let name of deleted[type][host]) {
            try {
              // trying to parse names in case of indexedDB or cache
              let names = JSON.parse(name);
              // Is a whole cache, database or objectstore deleted?
              // Then remove it from the tree.
              if (names.length < 3) {
                if (this.tree.isSelected([type, host, ...names])) {
                  this.table.clear();
                  this.hideSidebar();
                  this.tree.selectPreviousItem();
                }
                this.tree.remove([type, host, ...names]);
              }

              // Remove the item from table if currently displayed.
              if (names.length > 0) {
                let tableItemName = names.pop();
                if (this.tree.isSelected([type, host, ...names])) {
                  await this.removeItemFromTable(tableItemName);
                }
              }
            } catch (ex) {
              if (this.tree.isSelected([type, host])) {
                await this.removeItemFromTable(name);
              }
            }
          }
        }
      }
    }
  }

  /**
   * Handle changed items received by onUpdate
   *
   * @param {object} See onUpdate docs
   */
  async handleChangedItems(changed) {
    let [type, host, db, objectStore] = this.tree.selectedItem;
    if (!changed[type] || !changed[type][host] ||
        changed[type][host].length == 0) {
      return;
    }
    try {
      let toUpdate = [];
      for (let name of changed[type][host]) {
        let names = JSON.parse(name);
        if (names[0] == db && names[1] == objectStore && names[2]) {
          toUpdate.push(name);
        }
      }
      await this.fetchStorageObjects(type, host, toUpdate, REASON.UPDATE);
    } catch (ex) {
      await this.fetchStorageObjects(type, host, changed[type][host], REASON.UPDATE);
    }
  }

  /**
   * Fetches the storage objects from the storage actor and populates the
   * storage table with the returned data.
   *
   * @param {string} type
   *        The type of storage. Ex. "cookies"
   * @param {string} host
   *        Hostname
   * @param {array} names
   *        Names of particular store objects. Empty if all are requested
   * @param {Constant} reason
   *        See REASON constant at top of file.
   */
  async fetchStorageObjects(type, host, names, reason) {
    let fetchOpts = reason === REASON.NEXT_50_ITEMS ? {offset: this.itemOffset}
                                                    : {};
    let storageType = this.storageTypes[type];

    this.sidebarToggledOpen = null;

    if (reason !== REASON.NEXT_50_ITEMS &&
        reason !== REASON.UPDATE &&
        reason !== REASON.NEW_ROW &&
        reason !== REASON.POPULATE) {
      throw new Error("Invalid reason specified");
    }

    try {
      if (reason === REASON.POPULATE) {
        let subType = null;
        // The indexedDB type could have sub-type data to fetch.
        // If having names specified, then it means
        // we are fetching details of specific database or of object store.
        if (type === "indexedDB" && names) {
          let [ dbName, objectStoreName ] = JSON.parse(names[0]);
          if (dbName) {
            subType = "database";
          }
          if (objectStoreName) {
            subType = "object store";
          }
        }

        this.actorSupportsAddItem = await this._target.actorHasMethod(type, "addItem");
        this.actorSupportsRemoveItem =
          await this._target.actorHasMethod(type, "removeItem");
        this.actorSupportsRemoveAll =
          await this._target.actorHasMethod(type, "removeAll");
        this.actorSupportsRemoveAllSessionCookies =
          await this._target.actorHasMethod(type, "removeAllSessionCookies");

        await this.resetColumns(type, host, subType);
      }

      let {data} = await storageType.getStoreObjects(host, names, fetchOpts);
      if (data.length) {
        await this.populateTable(data, reason);
      }
      this.updateToolbar();
      this.emit("store-objects-updated");
    } catch (ex) {
      console.error(ex);
    }
  }

  /**
   * Updates the toolbar hiding and showing buttons as appropriate.
   */
  updateToolbar() {
    let item = this.tree.selectedItem;
    let howManyNodesIn = item ? item.length : 0;

    // The first node is just a title e.g. "Cookies" so we need to be at least
    // 2 nodes in to show the add button.
    let canAdd = this.actorSupportsAddItem && howManyNodesIn > 1;

    if (canAdd) {
      this._addButton.hidden = false;
      this._addButton.setAttribute("tooltiptext",
        L10N.getFormatStr("storage.popupMenu.addItemLabel"));
    } else {
      this._addButton.hidden = true;
      this._addButton.removeAttribute("tooltiptext");
    }
  }

  /**
   * Populates the storage tree which displays the list of storages present for
   * the page.
   *
   * @param {object} storageTypes
   *        List of storages and their corresponding hosts returned by the
   *        StorageFront.listStores call.
   */
  populateStorageTree(storageTypes) {
    this.storageTypes = {};
    for (let type in storageTypes) {
      // Ignore `from` field, which is just a protocol.js implementation
      // artifact.
      if (type === "from") {
        continue;
      }
      let typeLabel = type;
      try {
        typeLabel = L10N.getStr("tree.labels." + type);
      } catch (e) {
        console.error("Unable to localize tree label type:" + type);
      }
      this.tree.add([{id: type, label: typeLabel, type: "store"}]);
      if (!storageTypes[type].hosts) {
        continue;
      }
      this.storageTypes[type] = storageTypes[type];
      for (let host in storageTypes[type].hosts) {
        this.tree.add([type, {id: host, type: "url"}]);
        for (let name of storageTypes[type].hosts[host]) {
          try {
            let names = JSON.parse(name);
            this.tree.add([type, host, ...names]);
            if (!this.tree.selectedItem) {
              this.tree.selectedItem = [type, host, names[0], names[1]];
            }
          } catch (ex) {
            // Do Nothing
          }
        }
        if (!this.tree.selectedItem) {
          this.tree.selectedItem = [type, host];
        }
      }
    }
  }

  /**
   * Populates the selected entry from the table in the sidebar for a more
   * detailed view.
   */
  async updateObjectSidebar() {
    let item = this.table.selectedRow;
    let value;

    // Get the string value (async action) and the update the UI synchronously.
    if (item && item.name && item.valueActor) {
      value = await item.valueActor.string();
    }

    // Bail if the selectedRow is no longer selected, the item doesn't exist or the state
    // changed in another way during the above yield.
    if (this.table.items.size === 0 ||
        !item ||
        !this.table.selectedRow ||
        item.uniqueKey !== this.table.selectedRow.uniqueKey) {
      this.hideSidebar();
      return;
    }

    // Start updating the UI. Everything is sync beyond this point.
    if (this.sidebarToggledOpen === null || this.sidebarToggledOpen === true) {
      this.sidebar.hidden = false;
    }

    this.updateSidebarToggleButton();
    this.view.empty();
    let mainScope = this.view.addScope(L10N.getStr("storage.data.label"));
    mainScope.expanded = true;

    if (value) {
      let itemVar = mainScope.addItem(item.name + "", {}, {relaxed: true});

      // The main area where the value will be displayed
      itemVar.setGrip(value);

      // May be the item value is a json or a key value pair itself
      this.parseItemValue(item.name, value);

      // By default the item name and value are shown. If this is the only
      // information available, then nothing else is to be displayed.
      let itemProps = Object.keys(item);
      if (itemProps.length > 3) {
        // Display any other information other than the item name and value
        // which may be available.
        let rawObject = Object.create(null);
        let otherProps = itemProps.filter(
          e => !["name", "value", "valueActor"].includes(e));
        for (let prop of otherProps) {
          let column = this.table.columns.get(prop);
          if (column && column.private) {
            continue;
          }

          let cookieProp = COOKIE_KEY_MAP[prop] || prop;
          // The pseduo property of HostOnly refers to converse of isDomain property
          rawObject[cookieProp] = (prop === "isDomain") ? !item[prop] : item[prop];
        }
        itemVar.populate(rawObject, {sorted: true});
        itemVar.twisty = true;
        itemVar.expanded = true;
      }
    } else {
      // Case when displaying IndexedDB db/object store properties.
      for (let key in item) {
        let column = this.table.columns.get(key);
        if (column && column.private) {
          continue;
        }

        mainScope.addItem(key, {}, true).setGrip(item[key]);
        this.parseItemValue(key, item[key]);
      }
    }

    this.emit("sidebar-updated");
  }

  /**
   * Tries to parse a string value into either a json or a key-value separated
   * object and populates the sidebar with the parsed value. The value can also
   * be a key separated array.
   *
   * @param {string} name
   *        The key corresponding to the `value` string in the object
   * @param {string} value
   *        The string to be parsed into an object
   */
  parseItemValue(name, originalValue) {
    // Find if value is URLEncoded ie
    let decodedValue = "";
    try {
      decodedValue = decodeURIComponent(originalValue);
    } catch (e) {
      // Unable to decode, nothing to do
    }
    let value = (decodedValue && decodedValue !== originalValue)
      ? decodedValue : originalValue;

    let json = null;
    try {
      json = JSOL.parse(value);
    } catch (ex) {
      json = null;
    }

    if (!json && value) {
      json = this._extractKeyValPairs(value);
    }

    // return if json is null, or same as value, or just a string.
    if (!json || json == value || typeof json == "string") {
      return;
    }

    // One special case is a url which gets separated as key value pair on :
    if ((json.length == 2 || Object.keys(json).length == 1) &&
        ((json[0] || Object.keys(json)[0]) + "").match(/^(http|file|ftp)/)) {
      return;
    }

    let jsonObject = Object.create(null);
    let view = this.view;
    jsonObject[name] = json;
    let valueScope = view.getScopeAtIndex(1) ||
                      view.addScope(L10N.getStr("storage.parsedValue.label"));
    valueScope.expanded = true;
    let jsonVar = valueScope.addItem("", Object.create(null), {relaxed: true});
    jsonVar.expanded = true;
    jsonVar.twisty = true;
    jsonVar.populate(jsonObject, {expanded: true});
  }

  /**
   * Tries to parse a string into an object on the basis of key-value pairs,
   * separated by various separators. If failed, tries to parse for single
   * separator separated values to form an array.
   *
   * @param {string} value
   *        The string to be parsed into an object or array
   */
  _extractKeyValPairs(value) {
    let makeObject = (keySep, pairSep) => {
      let object = {};
      for (let pair of value.split(pairSep)) {
        let [key, val] = pair.split(keySep);
        object[key] = val;
      }
      return object;
    };

    // Possible separators.
    const separators = ["=", ":", "~", "#", "&", "\\*", ",", "\\."];
    // Testing for object
    for (let i = 0; i < separators.length; i++) {
      let kv = separators[i];
      for (let j = 0; j < separators.length; j++) {
        if (i == j) {
          continue;
        }
        let p = separators[j];
        let word = `[^${kv}${p}]*`;
        let keyValue = `${word}${kv}${word}`;
        let keyValueList = `${keyValue}(${p}${keyValue})*`;
        let regex = new RegExp(`^${keyValueList}$`);
        if (value.match && value.match(regex) && value.includes(kv) &&
            (value.includes(p) || value.split(kv).length == 2)) {
          return makeObject(kv, p);
        }
      }
    }
    // Testing for array
    for (let p of separators) {
      let word = `[^${p}]*`;
      let wordList = `(${word}${p})+${word}`;
      let regex = new RegExp(`^${wordList}$`);
      if (value.match && value.match(regex)) {
        return value.split(p.replace(/\\*/g, ""));
      }
    }
    return null;
  }

  /**
   * Select handler for the storage tree. Fetches details of the selected item
   * from the storage details and populates the storage tree.
   *
   * @param {string} event
   *        The name of the event fired
   * @param {array} item
   *        An array of ids which represent the location of the selected item in
   *        the storage tree
   */
  async onHostSelect(item) {
    this.table.clear();
    this.hideSidebar();
    this.searchBox.value = "";

    let [type, host] = item;
    this.table.host = host;
    this.table.datatype = type;

    this.updateToolbar();

    let names = null;
    if (!host) {
      return;
    }
    if (item.length > 2) {
      names = [JSON.stringify(item.slice(2))];
    }
    await this.fetchStorageObjects(type, host, names, REASON.POPULATE);
    this.itemOffset = 0;
  }

  /**
   * Resets the column headers in the storage table with the pased object `data`
   *
   * @param {string} type
   *        The type of storage corresponding to the after-reset columns in the
   *        table.
   * @param {string} host
   *        The host name corresponding to the table after reset.
   *
   * @param {string} [subType]
   *        The sub type under the given type.
   */
  async resetColumns(type, host, subtype) {
    this.table.host = host;
    this.table.datatype = type;

    let uniqueKey = null;
    let columns = {};
    let editableFields = [];
    let hiddenFields = [];
    let privateFields = [];
    let fields = await this.getCurrentFront().getFields(subtype);

    fields.forEach(f => {
      if (!uniqueKey) {
        this.table.uniqueId = uniqueKey = f.name;
      }

      if (f.editable) {
        editableFields.push(f.name);
      }

      if (f.hidden) {
        hiddenFields.push(f.name);
      }

      if (f.private) {
        privateFields.push(f.name);
      }

      columns[f.name] = f.name;
      let columnName;
      try {
        // Path key names for l10n in the case of a string change.
        let name = f.name === "keyPath" ? "keyPath2" : f.name;

        columnName = L10N.getStr("table.headers." + type + "." + name);
      } catch (e) {
        columnName = COOKIE_KEY_MAP[f.name];
      }

      if (!columnName) {
        console.error("Unable to localize table header type:" + type + " key:" + f.name);
      } else {
        columns[f.name] = columnName;
      }
    });

    this.table.setColumns(columns, null, hiddenFields, privateFields);
    this.hideSidebar();

    this.makeFieldsEditable(editableFields);
  }

  /**
   * Populates or updates the rows in the storage table.
   *
   * @param {array[object]} data
   *        Array of objects to be populated in the storage table
   * @param {Constant} reason
   *        See REASON constant at top of file.
   */
  async populateTable(data, reason) {
    for (let item of data) {
      if (item.value) {
        item.valueActor = item.value;
        item.value = item.value.initial || "";
      }
      if (item.expires != null) {
        item.expires = item.expires
          ? new Date(item.expires).toUTCString()
          : L10N.getStr("label.expires.session");
      }
      if (item.creationTime != null) {
        item.creationTime = new Date(item.creationTime).toUTCString();
      }
      if (item.lastAccessed != null) {
        item.lastAccessed = new Date(item.lastAccessed).toUTCString();
      }

      switch (reason) {
        case REASON.POPULATE:
          // Update without flashing the row.
          this.table.push(item, true);
          break;
        case REASON.NEW_ROW:
        case REASON.NEXT_50_ITEMS:
          // Update and flash the row.
          this.table.push(item, false);
          break;
        case REASON.UPDATE:
          this.table.update(item);
          if (item == this.table.selectedRow && !this.sidebar.hidden) {
            await this.updateObjectSidebar();
          }
          break;
      }

      this.shouldLoadMoreItems = true;
    }
  }

  /**
   * Handles keypress event on the body table to close the sidebar when open
   *
   * @param {DOMEvent} event
   *        The event passed by the keypress event.
   */
  handleKeypress(event) {
    if (event.keyCode == KeyCodes.DOM_VK_ESCAPE && !this.sidebar.hidden) {
      // Stop Propagation to prevent opening up of split console
      this.hideSidebar();
      this.sidebarToggledOpen = false;
      event.stopPropagation();
      event.preventDefault();
    }
  }

  /**
   * Handles filtering the table
   */
  filterItems() {
    let value = this.searchBox.value;
    this.table.filterItems(value, ["valueActor"]);
    this._panelDoc.documentElement.classList.toggle("filtering", !!value);
  }

  /**
   * Handles endless scrolling for the table
   */
  async handleScrollEnd() {
    if (!this.shouldLoadMoreItems) {
      return;
    }
    this.shouldLoadMoreItems = false;
    this.itemOffset += 50;

    let item = this.tree.selectedItem;
    let [type, host] = item;
    let names = null;
    if (item.length > 2) {
      names = [JSON.stringify(item.slice(2))];
    }
    await this.fetchStorageObjects(type, host, names, REASON.NEXT_50_ITEMS);
  }

  /**
   * Fires before a cell context menu with the "Add" or "Delete" action is
   * shown. If the currently selected storage object doesn't support adding or
   * removing items, prevent showing the menu.
   */
  onTablePopupShowing(event) {
    let selectedItem = this.tree.selectedItem;
    let type = selectedItem[0];

    // IndexedDB only supports removing items from object stores (level 4 of the tree)
    if ((!this.actorSupportsAddItem && !this.actorSupportsRemoveItem &&
          type !== "cookies") ||
        (type === "indexedDB" && selectedItem.length !== 4)) {
      event.preventDefault();
      return;
    }

    let rowId = this.table.contextMenuRowId;
    let data = this.table.items.get(rowId);

    if (this.actorSupportsRemoveItem) {
      let name = data[this.table.uniqueId];
      let separatorRegex = new RegExp(SEPARATOR_GUID, "g");
      let label = addEllipsis((name + "").replace(separatorRegex, "-"));

      this._tablePopupDelete.hidden = false;
      this._tablePopupDelete.setAttribute("label",
        L10N.getFormatStr("storage.popupMenu.deleteLabel", label));
    } else {
      this._tablePopupDelete.hidden = true;
    }

    if (this.actorSupportsAddItem) {
      this._tablePopupAddItem.hidden = false;
      this._tablePopupAddItem.setAttribute("label",
        L10N.getFormatStr("storage.popupMenu.addItemLabel"));
    } else {
      this._tablePopupAddItem.hidden = true;
    }

    let showDeleteAllSessionCookies = false;
    if (this.actorSupportsRemoveAllSessionCookies) {
      if (type === "cookies" && selectedItem.length === 2) {
        showDeleteAllSessionCookies = true;
      }
    }

    this._tablePopupDeleteAllSessionCookies.hidden = !showDeleteAllSessionCookies;

    if (type === "cookies") {
      let host = addEllipsis(data.host);

      this._tablePopupDeleteAllFrom.hidden = false;
      this._tablePopupDeleteAllFrom.setAttribute("label",
        L10N.getFormatStr("storage.popupMenu.deleteAllFromLabel", host));
    } else {
      this._tablePopupDeleteAllFrom.hidden = true;
    }
  }

  onTreePopupShowing(event) {
    let showMenu = false;
    let selectedItem = this.tree.selectedItem;

    if (selectedItem) {
      let type = selectedItem[0];

      // The delete all (aka clear) action is displayed for IndexedDB object stores
      // (level 4 of tree), for Cache objects (level 3) and for the whole host (level 2)
      // for other storage types (cookies, localStorage, ...).
      let showDeleteAll = false;
      if (this.actorSupportsRemoveAll) {
        let level;
        if (type == "indexedDB") {
          level = 4;
        } else if (type == "Cache") {
          level = 3;
        } else {
          level = 2;
        }

        if (selectedItem.length == level) {
          showDeleteAll = true;
        }
      }

      this._treePopupDeleteAll.hidden = !showDeleteAll;

      // The delete all session cookies action is displayed for cookie object stores
      // (level 2 of tree)
      let showDeleteAllSessionCookies = false;
      if (this.actorSupportsRemoveAllSessionCookies) {
        if (type === "cookies" && selectedItem.length === 2) {
          showDeleteAllSessionCookies = true;
        }
      }

      this._treePopupDeleteAllSessionCookies.hidden = !showDeleteAllSessionCookies;

      // The delete action is displayed for:
      // - IndexedDB databases (level 3 of the tree)
      // - Cache objects (level 3 of the tree)
      let showDelete = (type == "indexedDB" || type == "Cache") &&
                        selectedItem.length == 3;
      this._treePopupDelete.hidden = !showDelete;
      if (showDelete) {
        let itemName = addEllipsis(selectedItem[selectedItem.length - 1]);
        this._treePopupDelete.setAttribute("label",
          L10N.getFormatStr("storage.popupMenu.deleteLabel", itemName));
      }

      showMenu = showDeleteAll || showDelete;
    }

    if (!showMenu) {
      event.preventDefault();
    }
  }

  /**
   * Handles refreshing the selected storage
   */
  async onRefreshTable() {
    await this.onHostSelect(this.tree.selectedItem);
  }

  /**
   * Handles adding an item from the storage
   */
  onAddItem() {
    let front = this.getCurrentFront();
    let [, host] = this.tree.selectedItem;

    // Prepare to scroll into view.
    this.table.scrollIntoViewOnUpdate = true;
    this.table.editBookmark = createGUID();
    front.addItem(this.table.editBookmark, host);
  }

  /**
   * Handles removing an item from the storage
   */
  onRemoveItem() {
    let [, host, ...path] = this.tree.selectedItem;
    let front = this.getCurrentFront();
    let rowId = this.table.contextMenuRowId;
    let data = this.table.items.get(rowId);
    let name = data[this.table.uniqueId];
    if (path.length > 0) {
      name = JSON.stringify([...path, name]);
    }
    front.removeItem(host, name);
  }

  /**
   * Handles removing all items from the storage
   */
  onRemoveAll() {
    // Cannot use this.currentActor() if the handler is called from the
    // tree context menu: it returns correct value only after the table
    // data from server are successfully fetched (and that's async).
    let [, host, ...path] = this.tree.selectedItem;
    let front = this.getCurrentFront();
    let name = path.length > 0 ? JSON.stringify(path) : undefined;
    front.removeAll(host, name);
  }

  /**
   * Handles removing all session cookies from the storage
   */
  onRemoveAllSessionCookies() {
    // Cannot use this.currentActor() if the handler is called from the
    // tree context menu: it returns the correct value only after the
    // table data from server is successfully fetched (and that's async).
    let [, host, ...path] = this.tree.selectedItem;
    let front = this.getCurrentFront();
    let name = path.length > 0 ? JSON.stringify(path) : undefined;
    front.removeAllSessionCookies(host, name);
  }

  /**
   * Handles removing all cookies with exactly the same domain as the
   * cookie in the selected row.
   */
  onRemoveAllFrom() {
    let [, host] = this.tree.selectedItem;
    let front = this.getCurrentFront();
    let rowId = this.table.contextMenuRowId;
    let data = this.table.items.get(rowId);

    front.removeAll(host, data.host);
  }

  onRemoveTreeItem() {
    let [type, host, ...path] = this.tree.selectedItem;

    if (type == "indexedDB" && path.length == 1) {
      this.removeDatabase(host, path[0]);
    } else if (type == "Cache" && path.length == 1) {
      this.removeCache(host, path[0]);
    }
  }

  removeDatabase(host, dbName) {
    let front = this.getCurrentFront();

    front.removeDatabase(host, dbName).then(result => {
      if (result.blocked) {
        let notificationBox = this._toolbox.getNotificationBox();
        notificationBox.appendNotification(
          L10N.getFormatStr("storage.idb.deleteBlocked", dbName),
          "storage-idb-delete-blocked",
          null,
          notificationBox.PRIORITY_WARNING_LOW);
      }
    }).catch(error => {
      let notificationBox = this._toolbox.getNotificationBox();
      notificationBox.appendNotification(
        L10N.getFormatStr("storage.idb.deleteError", dbName),
        "storage-idb-delete-error",
        null,
        notificationBox.PRIORITY_CRITICAL_LOW);
    });
  }

  removeCache(host, cacheName) {
    let front = this.getCurrentFront();

    front.removeItem(host, JSON.stringify([ cacheName ]));
  }
}

exports.StorageUI = StorageUI;

// Helper Functions

function createGUID() {
  return "{cccccccc-cccc-4ccc-yccc-cccccccccccc}".replace(/[cy]/g, c => {
    let r = Math.random() * 16 | 0, v = c == "c" ? r : (r & 0x3 | 0x8);
    return v.toString(16);
  });
}

function addEllipsis(name) {
  if (name.length > ITEM_NAME_MAX_LENGTH) {
    if (/^https?:/.test(name)) {
      // For URLs, add ellipsis in the middle
      const halfLen = ITEM_NAME_MAX_LENGTH / 2;
      return name.slice(0, halfLen) + ELLIPSIS + name.slice(-halfLen);
    }

    // For other strings, add ellipsis at the end
    return name.substr(0, ITEM_NAME_MAX_LENGTH) + ELLIPSIS;
  }

  return name;
}
PK
!<(��@�@�Echrome/devtools/modules/devtools/client/styleeditor/StyleEditorUI.jsm/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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.EXPORTED_SYMBOLS = ["StyleEditorUI"];

const {loader, require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
const Services = require("Services");
const {NetUtil} = require("resource://gre/modules/NetUtil.jsm");
const {OS} = require("resource://gre/modules/osfile.jsm");
const {Task} = require("devtools/shared/task");
const EventEmitter = require("devtools/shared/old-event-emitter");
const {gDevTools} = require("devtools/client/framework/devtools");
const {
  getString,
  text,
  wire,
  showFilePicker,
} = require("resource://devtools/client/styleeditor/StyleEditorUtil.jsm");
const {SplitView} = require("resource://devtools/client/shared/SplitView.jsm");
const {StyleSheetEditor} = require("resource://devtools/client/styleeditor/StyleSheetEditor.jsm");
const {PluralForm} = require("devtools/shared/plural-form");
const {PrefObserver} = require("devtools/client/shared/prefs");
const csscoverage = require("devtools/shared/fronts/csscoverage");
const {KeyCodes} = require("devtools/client/shared/keycodes");
const {OriginalSource} = require("devtools/client/styleeditor/original-source");

loader.lazyRequireGetter(this, "ResponsiveUIManager", "devtools/client/responsive.html/manager", true);

const LOAD_ERROR = "error-load";
const STYLE_EDITOR_TEMPLATE = "stylesheet";
const SELECTOR_HIGHLIGHTER_TYPE = "SelectorHighlighter";
const PREF_MEDIA_SIDEBAR = "devtools.styleeditor.showMediaSidebar";
const PREF_SIDEBAR_WIDTH = "devtools.styleeditor.mediaSidebarWidth";
const PREF_NAV_WIDTH = "devtools.styleeditor.navSidebarWidth";
const PREF_ORIG_SOURCES = "devtools.source-map.client-service.enabled";

/**
 * StyleEditorUI is controls and builds the UI of the Style Editor, including
 * maintaining a list of editors for each stylesheet on a debuggee.
 *
 * Emits events:
 *   'editor-added': A new editor was added to the UI
 *   'editor-selected': An editor was selected
 *   'error': An error occured
 *
 * @param {StyleEditorFront} debuggee
 *        Client-side front for interacting with the page's stylesheets
 * @param {Target} target
 *        Interface for the page we're debugging
 * @param {Document} panelDoc
 *        Document of the toolbox panel to populate UI in.
 * @param {CssProperties} A css properties database.
 */
function StyleEditorUI(debuggee, target, panelDoc, cssProperties) {
  EventEmitter.decorate(this);

  this._debuggee = debuggee;
  this._target = target;
  this._panelDoc = panelDoc;
  this._cssProperties = cssProperties;
  this._window = this._panelDoc.defaultView;
  this._root = this._panelDoc.getElementById("style-editor-chrome");

  this.editors = [];
  this.selectedEditor = null;
  this.savedLocations = {};
  this._seenSheets = new Map();

  // Don't add any style sheets that might arrive via events, until
  // the call to initialize.  Style sheets can arrive from the server
  // at any time, for example if a new style sheet was added, or if
  // the style sheet actor was just created and is walking the style
  // sheets for the first time.  In any case, in |initialize| we're
  // going to fetch the list of sheets anyway.
  this._suppressAdd = true;

  this._onOptionsPopupShowing = this._onOptionsPopupShowing.bind(this);
  this._onOptionsPopupHiding = this._onOptionsPopupHiding.bind(this);
  this._onNewDocument = this._onNewDocument.bind(this);
  this._onMediaPrefChanged = this._onMediaPrefChanged.bind(this);
  this._updateMediaList = this._updateMediaList.bind(this);
  this._clear = this._clear.bind(this);
  this._onError = this._onError.bind(this);
  this._updateOpenLinkItem = this._updateOpenLinkItem.bind(this);
  this._openLinkNewTab = this._openLinkNewTab.bind(this);
  this._addStyleSheet = this._addStyleSheet.bind(this);

  this._prefObserver = new PrefObserver("devtools.styleeditor.");
  this._prefObserver.on(PREF_MEDIA_SIDEBAR, this._onMediaPrefChanged);
  this._sourceMapPrefObserver = new PrefObserver("devtools.source-map.client-service.");
  this._sourceMapPrefObserver.on(PREF_ORIG_SOURCES, this._onNewDocument);

  this._debuggee.on("stylesheet-added", this._addStyleSheet);
}
this.StyleEditorUI = StyleEditorUI;

StyleEditorUI.prototype = {
  /*
   * Index of selected stylesheet in document.styleSheets
   */
  get selectedStyleSheetIndex() {
    return this.selectedEditor ?
           this.selectedEditor.styleSheet.styleSheetIndex : -1;
  },

  /**
   * Initiates the style editor ui creation, the inspector front to get
   * reference to the walker and the selector highlighter if available
   */
  initialize: Task.async(function* () {
    yield this.initializeHighlighter();

    this.createUI();

    let styleSheets = yield this._debuggee.getStyleSheets();
    yield this._resetStyleSheetList(styleSheets);

    this._target.on("will-navigate", this._clear);
    this._target.on("navigate", this._onNewDocument);
  }),

  initializeHighlighter: Task.async(function* () {
    let toolbox = gDevTools.getToolbox(this._target);
    yield toolbox.initInspector();
    this._walker = toolbox.walker;

    let hUtils = toolbox.highlighterUtils;
    if (hUtils.supportsCustomHighlighters()) {
      try {
        this._highlighter =
          yield hUtils.getHighlighterByType(SELECTOR_HIGHLIGHTER_TYPE);
      } catch (e) {
        // The selectorHighlighter can't always be instantiated, for example
        // it doesn't work with XUL windows (until bug 1094959 gets fixed);
        // or the selectorHighlighter doesn't exist on the backend.
        console.warn("The selectorHighlighter couldn't be instantiated, " +
          "elements matching hovered selectors will not be highlighted");
      }
    }
  }),

  /**
   * Build the initial UI and wire buttons with event handlers.
   */
  createUI: function () {
    let viewRoot = this._root.parentNode.querySelector(".splitview-root");

    this._view = new SplitView(viewRoot);

    wire(this._view.rootElement, ".style-editor-newButton", () =>{
      this._debuggee.addStyleSheet(null);
    });

    wire(this._view.rootElement, ".style-editor-importButton", ()=> {
      this._importFromFile(this._mockImportFile || null, this._window);
    });

    this._optionsButton = this._panelDoc.getElementById("style-editor-options");
    this._panelDoc.addEventListener("contextmenu", () => {
      this._contextMenuStyleSheet = null;
    }, true);

    this._contextMenu = this._panelDoc.getElementById("sidebar-context");
    this._contextMenu.addEventListener("popupshowing",
                                       this._updateOpenLinkItem);

    this._optionsMenu =
      this._panelDoc.getElementById("style-editor-options-popup");
    this._optionsMenu.addEventListener("popupshowing",
                                       this._onOptionsPopupShowing);
    this._optionsMenu.addEventListener("popuphiding",
                                       this._onOptionsPopupHiding);

    this._sourcesItem = this._panelDoc.getElementById("options-origsources");
    this._sourcesItem.addEventListener("command",
                                       this._toggleOrigSources);

    this._mediaItem = this._panelDoc.getElementById("options-show-media");
    this._mediaItem.addEventListener("command",
                                     this._toggleMediaSidebar);

    this._openLinkNewTabItem =
      this._panelDoc.getElementById("context-openlinknewtab");
    this._openLinkNewTabItem.addEventListener("command",
                                              this._openLinkNewTab);

    let nav = this._panelDoc.querySelector(".splitview-controller");
    nav.setAttribute("width", Services.prefs.getIntPref(PREF_NAV_WIDTH));
  },

  /**
   * Listener handling the 'gear menu' popup showing event.
   * Update options menu items to reflect current preference settings.
   */
  _onOptionsPopupShowing: function () {
    this._optionsButton.setAttribute("open", "true");
    this._sourcesItem.setAttribute("checked",
      Services.prefs.getBoolPref(PREF_ORIG_SOURCES));
    this._mediaItem.setAttribute("checked",
      Services.prefs.getBoolPref(PREF_MEDIA_SIDEBAR));
  },

  /**
   * Listener handling the 'gear menu' popup hiding event.
   */
  _onOptionsPopupHiding: function () {
    this._optionsButton.removeAttribute("open");
  },

  /**
   * Refresh editors to reflect the stylesheets in the document.
   *
   * @param {string} event
   *        Event name
   * @param {StyleSheet} styleSheet
   *        StyleSheet object for new sheet
   */
  _onNewDocument: function () {
    this._suppressAdd = true;
    this._debuggee.getStyleSheets().then((styleSheets) => {
      return this._resetStyleSheetList(styleSheets);
    }).catch(console.error);
  },

  /**
   * Add editors for all the given stylesheets to the UI.
   *
   * @param  {array} styleSheets
   *         Array of StyleSheetFront
   */
  _resetStyleSheetList: Task.async(function* (styleSheets) {
    this._clear();
    this._suppressAdd = false;

    for (let sheet of styleSheets) {
      try {
        yield this._addStyleSheet(sheet);
      } catch (e) {
        console.error(e);
        this.emit("error", { key: LOAD_ERROR, level: "warning" });
      }
    }

    this._root.classList.remove("loading");

    this.emit("stylesheets-reset");
  }),

  /**
   * Remove all editors and add loading indicator.
   */
  _clear: function () {
    // remember selected sheet and line number for next load
    if (this.selectedEditor && this.selectedEditor.sourceEditor) {
      let href = this.selectedEditor.styleSheet.href;
      let {line, ch} = this.selectedEditor.sourceEditor.getCursor();

      this._styleSheetToSelect = {
        stylesheet: href,
        line: line,
        col: ch
      };
    }

    // remember saved file locations
    for (let editor of this.editors) {
      if (editor.savedFile) {
        let identifier = this.getStyleSheetIdentifier(editor.styleSheet);
        this.savedLocations[identifier] = editor.savedFile;
      }
    }

    this._clearStyleSheetEditors();
    this._view.removeAll();

    this.selectedEditor = null;
    // Here the keys are style sheet actors, and the values are
    // promises that resolve to the sheet's editor.  See |_addStyleSheet|.
    this._seenSheets = new Map();
    this._suppressAdd = true;

    this._root.classList.add("loading");
  },

  /**
   * Add an editor for this stylesheet. Add editors for its original sources
   * instead (e.g. Sass sources), if applicable.
   *
   * @param  {StyleSheetFront} styleSheet
   *         Style sheet to add to style editor
   * @param {Boolean} isNew
   *        True if this style sheet was created by a call to the
   *        style sheets actor's @see addStyleSheet method.
   * @return {Promise}
   *         A promise that resolves to the style sheet's editor when the style sheet has
   *         been fully loaded.  If the style sheet has a source map, and source mapping
   *         is enabled, then the promise resolves to null.
   */
  _addStyleSheet: function (styleSheet, isNew) {
    if (this._suppressAdd) {
      return null;
    }

    if (!this._seenSheets.has(styleSheet)) {
      let promise = (async () => {
        let editor = await this._addStyleSheetEditor(styleSheet, isNew);

        let toolbox = gDevTools.getToolbox(this._target);
        let sourceMapService = toolbox.sourceMapService;
        if (!sourceMapService) {
          return editor;
        }

        let {href, nodeHref, actorID: id, sourceMapURL} = styleSheet;
        let url = href || nodeHref;
        let sources = await sourceMapService.getOriginalURLs({
          id,
          url,
          sourceMapURL,
        });
        // A single generated sheet might map to multiple original
        // sheets, so make editors for each of them.
        if (sources && sources.length) {
          let parentEditorName = editor.friendlyName;
          this._removeStyleSheetEditor(editor);
          editor = null;

          for (let source of sources) {
            let generatedId = sourceMapService.generatedToOriginalId(id, source);
            let original = new OriginalSource(source, generatedId, sourceMapService);

            // set so the first sheet will be selected, even if it's a source
            original.styleSheetIndex = styleSheet.styleSheetIndex;
            original.relatedStyleSheet = styleSheet;
            original.relatedEditorName = parentEditorName;
            await this._addStyleSheetEditor(original);
          }
        }

        return editor;
      })();
      this._seenSheets.set(styleSheet, promise);
    }
    return this._seenSheets.get(styleSheet);
  },

  /**
   * Add a new editor to the UI for a source.
   *
   * @param {StyleSheet}  styleSheet
   *        Object representing stylesheet
   * @param {Boolean} isNew
   *         Optional if stylesheet is a new sheet created by user
   * @return {Promise} that is resolved with the created StyleSheetEditor when
   *                   the editor is fully initialized or rejected on error.
   */
  _addStyleSheetEditor: Task.async(function* (styleSheet, isNew) {
    // recall location of saved file for this sheet after page reload
    let file = null;
    let identifier = this.getStyleSheetIdentifier(styleSheet);
    let savedFile = this.savedLocations[identifier];
    if (savedFile) {
      file = savedFile;
    }

    let editor = new StyleSheetEditor(styleSheet, this._window, file, isNew,
                                      this._walker, this._highlighter);

    editor.on("property-change", this._summaryChange.bind(this, editor));
    editor.on("media-rules-changed", this._updateMediaList.bind(this, editor));
    editor.on("linked-css-file", this._summaryChange.bind(this, editor));
    editor.on("linked-css-file-error", this._summaryChange.bind(this, editor));
    editor.on("error", this._onError);

    this.editors.push(editor);

    yield editor.fetchSource();
    this._sourceLoaded(editor);

    return editor;
  }),

  /**
   * Import a style sheet from file and asynchronously create a
   * new stylesheet on the debuggee for it.
   *
   * @param {mixed} file
   *        Optional nsIFile or filename string.
   *        If not set a file picker will be shown.
   * @param {nsIWindow} parentWindow
   *        Optional parent window for the file picker.
   */
  _importFromFile: function (file, parentWindow) {
    let onFileSelected = (selectedFile) => {
      if (!selectedFile) {
        // nothing selected
        return;
      }
      NetUtil.asyncFetch({
        uri: NetUtil.newURI(selectedFile),
        loadingNode: this._window.document,
        securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS,
        contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER
      }, (stream, status) => {
        if (!Components.isSuccessCode(status)) {
          this.emit("error", { key: LOAD_ERROR, level: "warning" });
          return;
        }
        let source =
            NetUtil.readInputStreamToString(stream, stream.available());
        stream.close();

        this._suppressAdd = true;
        this._debuggee.addStyleSheet(source).then((styleSheet) => {
          this._suppressAdd = false;
          this._addStyleSheet(styleSheet, true).then(editor => {
            if (editor) {
              editor.savedFile = selectedFile;
            }
            // Just for testing purposes.
            this.emit("test:editor-updated", editor);
          });
        });
      });
    };

    showFilePicker(file, false, parentWindow, onFileSelected);
  },

  /**
   * Forward any error from a stylesheet.
   *
   * @param  {string} event
   *         Event name
   * @param  {data} data
   *         The event data
   */
  _onError: function (event, data) {
    this.emit("error", data);
  },

  /**
   * Toggle the original sources pref.
   */
  _toggleOrigSources: function () {
    let isEnabled = Services.prefs.getBoolPref(PREF_ORIG_SOURCES);
    Services.prefs.setBoolPref(PREF_ORIG_SOURCES, !isEnabled);
  },

  /**
   * Toggle the pref for showing a @media rules sidebar in each editor.
   */
  _toggleMediaSidebar: function () {
    let isEnabled = Services.prefs.getBoolPref(PREF_MEDIA_SIDEBAR);
    Services.prefs.setBoolPref(PREF_MEDIA_SIDEBAR, !isEnabled);
  },

  /**
   * Toggle the @media sidebar in each editor depending on the setting.
   */
  _onMediaPrefChanged: function () {
    this.editors.forEach(this._updateMediaList);
  },

  /**
   * This method handles the following cases related to the context
   * menu item "_openLinkNewTabItem":
   *
   * 1) There was a stylesheet clicked on and it is external: show and
   * enable the context menu item
   * 2) There was a stylesheet clicked on and it is inline: show and
   * disable the context menu item
   * 3) There was no stylesheet clicked on (the right click happened
   * below the list): hide the context menu
   */
  _updateOpenLinkItem: function () {
    this._openLinkNewTabItem.setAttribute("hidden",
                                          !this._contextMenuStyleSheet);
    if (this._contextMenuStyleSheet) {
      this._openLinkNewTabItem.setAttribute("disabled",
                                            !this._contextMenuStyleSheet.href);
    }
  },

  /**
   * Open a particular stylesheet in a new tab.
   */
  _openLinkNewTab: function () {
    if (this._contextMenuStyleSheet) {
      this._window.openUILinkIn(this._contextMenuStyleSheet.href, "tab");
    }
  },

  /**
   * Remove a particular stylesheet editor from the UI
   *
   * @param {StyleSheetEditor}  editor
   *        The editor to remove.
   */
  _removeStyleSheetEditor: function (editor) {
    if (editor.summary) {
      this._view.removeItem(editor.summary);
    } else {
      let self = this;
      this.on("editor-added", function onAdd(event, added) {
        if (editor == added) {
          self.off("editor-added", onAdd);
          self._view.removeItem(editor.summary);
        }
      });
    }

    editor.destroy();
    this.editors.splice(this.editors.indexOf(editor), 1);
  },

  /**
   * Clear all the editors from the UI.
   */
  _clearStyleSheetEditors: function () {
    for (let editor of this.editors) {
      editor.destroy();
    }
    this.editors = [];
  },

  /**
   * Called when a StyleSheetEditor's source has been fetched. Create a
   * summary UI for the editor.
   *
   * @param  {StyleSheetEditor} editor
   *         Editor to create UI for.
   */
  _sourceLoaded: function (editor) {
    let ordinal = editor.styleSheet.styleSheetIndex;
    ordinal = ordinal == -1 ? Number.MAX_SAFE_INTEGER : ordinal;
    // add new sidebar item and editor to the UI
    this._view.appendTemplatedItem(STYLE_EDITOR_TEMPLATE, {
      data: {
        editor: editor
      },
      disableAnimations: this._alwaysDisableAnimations,
      ordinal: ordinal,
      onCreate: (summary, details, data) => {
        let createdEditor = data.editor;
        createdEditor.summary = summary;
        createdEditor.details = details;

        wire(summary, ".stylesheet-enabled", function onToggleDisabled(event) {
          event.stopPropagation();
          event.target.blur();

          createdEditor.toggleDisabled();
        });

        wire(summary, ".stylesheet-name", {
          events: {
            "keypress": (event) => {
              if (event.keyCode == KeyCodes.DOM_VK_RETURN) {
                this._view.activeSummary = summary;
              }
            }
          }
        });

        wire(summary, ".stylesheet-saveButton", function onSaveButton(event) {
          event.stopPropagation();
          event.target.blur();

          createdEditor.saveToFile(createdEditor.savedFile);
        });

        this._updateSummaryForEditor(createdEditor, summary);

        summary.addEventListener("contextmenu", () => {
          this._contextMenuStyleSheet = createdEditor.styleSheet;
        });

        summary.addEventListener("focus", function onSummaryFocus(event) {
          if (event.target == summary) {
            // autofocus the stylesheet name
            summary.querySelector(".stylesheet-name").focus();
          }
        });

        let sidebar = details.querySelector(".stylesheet-sidebar");
        sidebar.setAttribute("width",
            Services.prefs.getIntPref(PREF_SIDEBAR_WIDTH));

        let splitter = details.querySelector(".devtools-side-splitter");
        splitter.addEventListener("mousemove", () => {
          let sidebarWidth = sidebar.getAttribute("width");
          Services.prefs.setIntPref(PREF_SIDEBAR_WIDTH, sidebarWidth);

          // update all @media sidebars for consistency
          let sidebars =
              [...this._panelDoc.querySelectorAll(".stylesheet-sidebar")];
          for (let mediaSidebar of sidebars) {
            mediaSidebar.setAttribute("width", sidebarWidth);
          }
        });

        // autofocus if it's a new user-created stylesheet
        if (createdEditor.isNew) {
          this._selectEditor(createdEditor);
        }

        if (this._isEditorToSelect(createdEditor)) {
          this.switchToSelectedSheet();
        }

        // If this is the first stylesheet and there is no pending request to
        // select a particular style sheet, select this sheet.
        if (!this.selectedEditor && !this._styleSheetBoundToSelect
            && createdEditor.styleSheet.styleSheetIndex == 0) {
          this._selectEditor(createdEditor);
        }
        this.emit("editor-added", createdEditor);
      },

      onShow: (summary, details, data) => {
        let showEditor = data.editor;
        this.selectedEditor = showEditor;

        Task.spawn(function* () {
          if (!showEditor.sourceEditor) {
            // only initialize source editor when we switch to this view
            let inputElement =
                details.querySelector(".stylesheet-editor-input");
            yield showEditor.load(inputElement, this._cssProperties);
          }

          showEditor.onShow();

          this.emit("editor-selected", showEditor);

          // Is there any CSS coverage markup to include?
          let usage = yield csscoverage.getUsage(this._target);
          if (usage == null) {
            return;
          }

          let sheet = showEditor.styleSheet;
          let {reports} = yield usage.createEditorReportForSheet(sheet);

          showEditor.removeAllUnusedRegions();

          if (reports.length > 0) {
            // Only apply if this file isn't compressed. We detect a
            // compressed file if there are more rules than lines.
            let editorText = showEditor.sourceEditor.getText();
            let lineCount = editorText.split("\n").length;
            let ruleCount = showEditor.styleSheet.ruleCount;
            if (lineCount >= ruleCount) {
              showEditor.addUnusedRegions(reports);
            } else {
              this.emit("error", { key: "error-compressed", level: "info" });
            }
          }
        }.bind(this)).catch(console.error);
      }
    });
  },

  /**
   * Switch to the editor that has been marked to be selected.
   *
   * @return {Promise}
   *         Promise that will resolve when the editor is selected.
   */
  switchToSelectedSheet: function () {
    let toSelect = this._styleSheetToSelect;

    for (let editor of this.editors) {
      if (this._isEditorToSelect(editor)) {
        // The _styleSheetBoundToSelect will always hold the latest pending
        // requested style sheet (with line and column) which is not yet
        // selected by the source editor. Only after we select that particular
        // editor and go the required line and column, it will become null.
        this._styleSheetBoundToSelect = this._styleSheetToSelect;
        this._styleSheetToSelect = null;
        return this._selectEditor(editor, toSelect.line, toSelect.col);
      }
    }

    return Promise.resolve();
  },

  /**
   * Returns whether a given editor is the current editor to be selected. Tests
   * based on href or underlying stylesheet.
   *
   * @param {StyleSheetEditor} editor
   *        The editor to test.
   */
  _isEditorToSelect: function (editor) {
    let toSelect = this._styleSheetToSelect;
    if (!toSelect) {
      return false;
    }
    let isHref = toSelect.stylesheet === null ||
                 typeof toSelect.stylesheet == "string";

    return (isHref && editor.styleSheet.href == toSelect.stylesheet) ||
           (toSelect.stylesheet == editor.styleSheet);
  },

  /**
   * Select an editor in the UI.
   *
   * @param  {StyleSheetEditor} editor
   *         Editor to switch to.
   * @param  {number} line
   *         Line number to jump to
   * @param  {number} col
   *         Column number to jump to
   * @return {Promise}
   *         Promise that will resolve when the editor is selected and ready
   *         to be used.
   */
  _selectEditor: function (editor, line, col) {
    line = line || 0;
    col = col || 0;

    let editorPromise = editor.getSourceEditor().then(() => {
      editor.sourceEditor.setCursor({line: line, ch: col});
      this._styleSheetBoundToSelect = null;
    });

    let summaryPromise = this.getEditorSummary(editor).then((summary) => {
      this._view.activeSummary = summary;
    });

    return Promise.all([editorPromise, summaryPromise]);
  },

  getEditorSummary: function (editor) {
    let self = this;

    if (editor.summary) {
      return Promise.resolve(editor.summary);
    }

    return new Promise(resolve => {
      this.on("editor-added", function onAdd(e, selected) {
        if (selected == editor) {
          self.off("editor-added", onAdd);
          resolve(editor.summary);
        }
      });
    });
  },

  getEditorDetails: function (editor) {
    let self = this;

    if (editor.details) {
      return Promise.resolve(editor.details);
    }

    return new Promise(resolve => {
      this.on("editor-added", function onAdd(e, selected) {
        if (selected == editor) {
          self.off("editor-added", onAdd);
          resolve(editor.details);
        }
      });
    });
  },

  /**
   * Returns an identifier for the given style sheet.
   *
   * @param {StyleSheet} styleSheet
   *        The style sheet to be identified.
   */
  getStyleSheetIdentifier: function (styleSheet) {
    // Identify inline style sheets by their host page URI and index
    // at the page.
    return styleSheet.href ? styleSheet.href :
      "inline-" + styleSheet.styleSheetIndex + "-at-" + styleSheet.nodeHref;
  },

  /**
   * selects a stylesheet and optionally moves the cursor to a selected line
   *
   * @param {StyleSheetFront} [stylesheet]
   *        Stylesheet to select or href of stylesheet to select
   * @param {Number} [line]
   *        Line to which the caret should be moved (zero-indexed).
   * @param {Number} [col]
   *        Column to which the caret should be moved (zero-indexed).
   * @return {Promise}
   *         Promise that will resolve when the editor is selected and ready
   *         to be used.
   */
  selectStyleSheet: function (stylesheet, line, col) {
    this._styleSheetToSelect = {
      stylesheet: stylesheet,
      line: line,
      col: col,
    };

    /* Switch to the editor for this sheet, if it exists yet.
       Otherwise each editor will be checked when it's created. */
    return this.switchToSelectedSheet();
  },

  /**
   * Handler for an editor's 'property-changed' event.
   * Update the summary in the UI.
   *
   * @param  {StyleSheetEditor} editor
   *         Editor for which a property has changed
   */
  _summaryChange: function (editor) {
    this._updateSummaryForEditor(editor);
  },

  /**
   * Update split view summary of given StyleEditor instance.
   *
   * @param {StyleSheetEditor} editor
   * @param {DOMElement} summary
   *        Optional item's summary element to update. If none, item
   *        corresponding to passed editor is used.
   */
  _updateSummaryForEditor: function (editor, summary) {
    summary = summary || editor.summary;
    if (!summary) {
      return;
    }

    let ruleCount = editor.styleSheet.ruleCount;
    if (editor.styleSheet.relatedStyleSheet) {
      ruleCount = editor.styleSheet.relatedStyleSheet.ruleCount;
    }
    if (ruleCount === undefined) {
      ruleCount = "-";
    }

    let flags = [];
    if (editor.styleSheet.disabled) {
      flags.push("disabled");
    }
    if (editor.unsaved) {
      flags.push("unsaved");
    }
    if (editor.linkedCSSFileError) {
      flags.push("linked-file-error");
    }
    this._view.setItemClassName(summary, flags.join(" "));

    let label = summary.querySelector(".stylesheet-name > label");
    label.setAttribute("value", editor.friendlyName);
    if (editor.styleSheet.href) {
      label.setAttribute("tooltiptext", editor.styleSheet.href);
    }

    let linkedCSSSource = "";
    if (editor.linkedCSSFile) {
      linkedCSSSource = OS.Path.basename(editor.linkedCSSFile);
    } else if (editor.styleSheet.relatedEditorName) {
      linkedCSSSource = editor.styleSheet.relatedEditorName;
    }
    text(summary, ".stylesheet-linked-file", linkedCSSSource);
    text(summary, ".stylesheet-title", editor.styleSheet.title || "");
    text(summary, ".stylesheet-rule-count",
      PluralForm.get(ruleCount,
                     getString("ruleCount.label")).replace("#1", ruleCount));
  },

  /**
   * Update the @media rules sidebar for an editor. Hide if there are no rules
   * Display a list of the @media rules in the editor's associated style sheet.
   * Emits a 'media-list-changed' event after updating the UI.
   *
   * @param  {StyleSheetEditor} editor
   *         Editor to update @media sidebar of
   */
  _updateMediaList: function (editor) {
    Task.spawn(function* () {
      let details = yield this.getEditorDetails(editor);
      let list = details.querySelector(".stylesheet-media-list");

      while (list.firstChild) {
        list.firstChild.remove();
      }

      let rules = editor.mediaRules;
      let showSidebar = Services.prefs.getBoolPref(PREF_MEDIA_SIDEBAR);
      let sidebar = details.querySelector(".stylesheet-sidebar");

      let inSource = false;

      for (let rule of rules) {
        let {line, column, parentStyleSheet} = rule;

        let location = {
          line: line,
          column: column,
          source: editor.styleSheet.href,
          styleSheet: parentStyleSheet
        };
        if (editor.styleSheet.isOriginalSource) {
          let styleSheet = editor.cssSheet;
          location = yield editor.styleSheet.getOriginalLocation(styleSheet, line,
                                                                 column);
        }

        // this @media rule is from a different original source
        if (location.source != editor.styleSheet.href) {
          continue;
        }
        inSource = true;

        let div = this._panelDoc.createElement("div");
        div.className = "media-rule-label";
        div.addEventListener("click",
                             this._jumpToLocation.bind(this, location));

        let cond = this._panelDoc.createElement("div");
        cond.className = "media-rule-condition";
        if (!rule.matches) {
          cond.classList.add("media-condition-unmatched");
        }
        if (this._target.isLocalTab) {
          this._setConditionContents(cond, rule.conditionText);
        } else {
          cond.textContent = rule.conditionText;
        }
        div.appendChild(cond);

        let link = this._panelDoc.createElement("div");
        link.className = "media-rule-line theme-link";
        if (location.line != -1) {
          link.textContent = ":" + location.line;
        }
        div.appendChild(link);

        list.appendChild(div);
      }

      sidebar.hidden = !showSidebar || !inSource;

      this.emit("media-list-changed", editor);
    }.bind(this)).catch(console.error);
  },

  /**
   * Used to safely inject media query links
   *
   * @param {HTMLElement} element
   *        The element corresponding to the media sidebar condition
   * @param {String} rawText
   *        The raw condition text to parse
   */
  _setConditionContents(element, rawText) {
    const minMaxPattern = /(min\-|max\-)(width|height):\s\d+(px)/ig;

    let match = minMaxPattern.exec(rawText);
    let lastParsed = 0;
    while (match && match.index != minMaxPattern.lastIndex) {
      let matchEnd = match.index + match[0].length;
      let node = this._panelDoc.createTextNode(
        rawText.substring(lastParsed, match.index)
      );
      element.appendChild(node);

      let link = this._panelDoc.createElement("a");
      link.href = "#";
      link.className = "media-responsive-mode-toggle";
      link.textContent = rawText.substring(match.index, matchEnd);
      link.addEventListener("click", this._onMediaConditionClick.bind(this));
      element.appendChild(link);

      match = minMaxPattern.exec(rawText);
      lastParsed = matchEnd;
    }

    let node = this._panelDoc.createTextNode(
      rawText.substring(lastParsed, rawText.length)
    );
    element.appendChild(node);
  },

  /**
   * Called when a media condition is clicked
   * If a responsive mode link is clicked, it will launch it.
   *
   * @param {object} e
   *        Event object
   */
  _onMediaConditionClick: function (e) {
    let conditionText = e.target.textContent;
    let isWidthCond = conditionText.toLowerCase().indexOf("width") > -1;
    let mediaVal = parseInt(/\d+/.exec(conditionText), 10);

    let options = isWidthCond ? {width: mediaVal} : {height: mediaVal};
    this._launchResponsiveMode(options);
    e.preventDefault();
    e.stopPropagation();
  },

  /**
   * Launches the responsive mode with a specific width or height
   *
   * @param  {object} options
   *         Object with width or/and height properties.
   */
  _launchResponsiveMode: Task.async(function* (options = {}) {
    let tab = this._target.tab;
    let win = this._target.tab.ownerDocument.defaultView;

    yield ResponsiveUIManager.openIfNeeded(win, tab);
    ResponsiveUIManager.getResponsiveUIForTab(tab).setViewportSize(options);
  }),

  /**
   * Jump cursor to the editor for a stylesheet and line number for a rule.
   *
   * @param  {object} location
   *         Location object with 'line', 'column', and 'source' properties.
   */
  _jumpToLocation: function (location) {
    let source = location.styleSheet || location.source;
    this.selectStyleSheet(source, location.line - 1, location.column - 1);
  },

  destroy: function () {
    if (this._highlighter) {
      this._highlighter.finalize();
      this._highlighter = null;
    }

    this._clearStyleSheetEditors();

    this._seenSheets = null;
    this._suppressAdd = false;

    let sidebar = this._panelDoc.querySelector(".splitview-controller");
    let sidebarWidth = sidebar.getAttribute("width");
    Services.prefs.setIntPref(PREF_NAV_WIDTH, sidebarWidth);

    this._optionsMenu.removeEventListener("popupshowing",
                                          this._onOptionsPopupShowing);
    this._optionsMenu.removeEventListener("popuphiding",
                                          this._onOptionsPopupHiding);

    this._sourceMapPrefObserver.off(PREF_ORIG_SOURCES, this._onNewDocument);
    this._sourceMapPrefObserver.destroy();
    this._prefObserver.off(PREF_MEDIA_SIDEBAR, this._onMediaPrefChanged);
    this._prefObserver.destroy();

    this._debuggee.off("stylesheet-added", this._addStyleSheet);
  }
};
PK
!<u�!$$Gchrome/devtools/modules/devtools/client/styleeditor/StyleEditorUtil.jsm/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* All top-level definitions here are exports.  */
/* eslint no-unused-vars: [2, {"vars": "local"}] */

"use strict";

this.EXPORTED_SYMBOLS = [
  "getString",
  "assert",
  "log",
  "text",
  "wire",
  "showFilePicker"
];

const PROPERTIES_URL = "chrome://devtools/locale/styleeditor.properties";

const {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
const Services = require("Services");
const gStringBundle = Services.strings.createBundle(PROPERTIES_URL);

/**
 * Returns a localized string with the given key name from the string bundle.
 *
 * @param name
 * @param ...rest
 *        Optional arguments to format in the string.
 * @return string
 */
function getString(name) {
  try {
    if (arguments.length == 1) {
      return gStringBundle.GetStringFromName(name);
    }
    let rest = Array.prototype.slice.call(arguments, 1);
    return gStringBundle.formatStringFromName(name, rest, rest.length);
  } catch (ex) {
    console.error(ex);
    throw new Error("L10N error. '" + name + "' is missing from " +
                    PROPERTIES_URL);
  }
}

/**
 * Assert an expression is true or throw if false.
 *
 * @param expression
 * @param message
 *        Optional message.
 * @return expression
 */
function assert(expression, message) {
  if (!expression) {
    let msg = message ? "ASSERTION FAILURE:" + message : "ASSERTION FAILURE";
    log(msg);
    throw new Error(msg);
  }
  return expression;
}

/**
 * Retrieve or set the text content of an element.
 *
 * @param DOMElement root
 *        The element to use for querySelector.
 * @param string selector
 *        Selector string for the element to get/set the text content.
 * @param string textContent
 *        Optional text to set.
 * @return string
 *         Text content of matching element or null if there were no element
 *         matching selector.
 */
function text(root, selector, textContent) {
  let element = root.querySelector(selector);
  if (!element) {
    return null;
  }

  if (textContent === undefined) {
    return element.textContent;
  }
  element.textContent = textContent;
  return textContent;
}

/**
 * Iterates _own_ properties of an object.
 *
 * @param object
 *        The object to iterate.
 * @param function callback(aKey, aValue)
 */
function forEach(object, callback) {
  for (let key in object) {
    if (object.hasOwnProperty(key)) {
      callback(key, object[key]);
    }
  }
}

/**
 * Log a message to the console.
 *
 * @param ...rest
 *        One or multiple arguments to log.
 *        If multiple arguments are given, they will be joined by " "
 *        in the log.
 */
function log() {
  console.logStringMessage(Array.prototype.slice.call(arguments).join(" "));
}

/**
 * Wire up element(s) matching selector with attributes, event listeners, etc.
 *
 * @param DOMElement root
 *        The element to use for querySelectorAll.
 *        Can be null if selector is a DOMElement.
 * @param string|DOMElement selectorOrElement
 *        Selector string or DOMElement for the element(s) to wire up.
 * @param object descriptor
 *        An object describing how to wire matching selector,
 *        supported properties are "events" and "attributes" taking
 *        objects themselves.
 *        Each key of properties above represents the name of the event or
 *        attribute, with the value being a function used as an event handler or
 *        string to use as attribute value.
 *        If descriptor is a function, the argument is equivalent to :
 *        {events: {'click': descriptor}}
 */
function wire(root, selectorOrElement, descriptor) {
  let matches;
  if (typeof selectorOrElement == "string") {
    // selector
    matches = root.querySelectorAll(selectorOrElement);
    if (!matches.length) {
      return;
    }
  } else {
    // element
    matches = [selectorOrElement];
  }

  if (typeof descriptor == "function") {
    descriptor = {events: {click: descriptor}};
  }

  for (let i = 0; i < matches.length; i++) {
    let element = matches[i];
    forEach(descriptor.events, function (name, handler) {
      element.addEventListener(name, handler);
    });
    forEach(descriptor.attributes, element.setAttribute);
  }
}

/**
 * Show file picker and return the file user selected.
 *
 * @param mixed file
 *        Optional nsIFile or string representing the filename to auto-select.
 * @param boolean toSave
 *        If true, the user is selecting a filename to save.
 * @param nsIWindow parentWindow
 *        Optional parent window. If null the parent window of the file picker
 *        will be the window of the attached input element.
 * @param callback
 *        The callback method, which will be called passing in the selected
 *        file or null if the user did not pick one.
 * @param AString suggestedFilename
 *        The suggested filename when toSave is true.
 */
function showFilePicker(path, toSave, parentWindow, callback,
                        suggestedFilename) {
  if (typeof path == "string") {
    try {
      if (Services.io.extractScheme(path) == "file") {
        let uri = Services.io.newURI(path);
        let file = uri.QueryInterface(Ci.nsIFileURL).file;
        callback(file);
        return;
      }
    } catch (ex) {
      callback(null);
      return;
    }
    try {
      let file =
          Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
      file.initWithPath(path);
      callback(file);
      return;
    } catch (ex) {
      callback(null);
      return;
    }
  }
  if (path) {
    // "path" is an nsIFile
    callback(path);
    return;
  }

  let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
  let mode = toSave ? fp.modeSave : fp.modeOpen;
  let key = toSave ? "saveStyleSheet" : "importStyleSheet";
  let fpCallback = function (result) {
    if (result == Ci.nsIFilePicker.returnCancel) {
      callback(null);
    } else {
      callback(fp.file);
    }
  };

  if (toSave && suggestedFilename) {
    fp.defaultString = suggestedFilename;
  }

  fp.init(parentWindow, getString(key + ".title"), mode);
  fp.appendFilter(getString(key + ".filter"), "*.css");
  fp.appendFilters(fp.filterAll);
  fp.open(fpCallback);
}
PK
!<MSHKgKgHchrome/devtools/modules/devtools/client/styleeditor/StyleSheetEditor.jsm/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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.EXPORTED_SYMBOLS = ["StyleSheetEditor"];

const {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
const Editor = require("devtools/client/sourceeditor/editor");
const promise = require("promise");
const {shortSource, prettifyCSS} = require("devtools/shared/inspector/css-logic");
const Services = require("Services");
const EventEmitter = require("devtools/shared/old-event-emitter");
const {Task} = require("devtools/shared/task");
const {FileUtils} = require("resource://gre/modules/FileUtils.jsm");
const {NetUtil} = require("resource://gre/modules/NetUtil.jsm");
const {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm", {});
const {
  getString,
  showFilePicker,
} = require("resource://devtools/client/styleeditor/StyleEditorUtil.jsm");

const LOAD_ERROR = "error-load";
const SAVE_ERROR = "error-save";

// max update frequency in ms (avoid potential typing lag and/or flicker)
// @see StyleEditor.updateStylesheet
const UPDATE_STYLESHEET_DELAY = 500;

// Pref which decides if CSS autocompletion is enabled in Style Editor or not.
const AUTOCOMPLETION_PREF = "devtools.styleeditor.autocompletion-enabled";

// Pref which decides whether updates to the stylesheet use transitions
const TRANSITION_PREF = "devtools.styleeditor.transitions";

// How long to wait to update linked CSS file after original source was saved
// to disk. Time in ms.
const CHECK_LINKED_SHEET_DELAY = 500;

// How many times to check for linked file changes
const MAX_CHECK_COUNT = 10;

// The classname used to show a line that is not used
const UNUSED_CLASS = "cm-unused-line";

// How much time should the mouse be still before the selector at that position
// gets highlighted?
const SELECTOR_HIGHLIGHT_TIMEOUT = 500;

/**
 * StyleSheetEditor controls the editor linked to a particular StyleSheet
 * object.
 *
 * Emits events:
 *   'property-change': A property on the underlying stylesheet has changed
 *   'source-editor-load': The source editor for this editor has been loaded
 *   'error': An error has occured
 *
 * @param {StyleSheet|OriginalSource}  styleSheet
 *        Stylesheet or original source to show
 * @param {DOMWindow}  win
 *        panel window for style editor
 * @param {nsIFile}  file
 *        Optional file that the sheet was imported from
 * @param {boolean} isNew
 *        Optional whether the sheet was created by the user
 * @param {Walker} walker
 *        Optional walker used for selectors autocompletion
 * @param {CustomHighlighterFront} highlighter
 *        Optional highlighter front for the SelectorHighligher used to
 *        highlight selectors
 */
function StyleSheetEditor(styleSheet, win, file, isNew, walker, highlighter) {
  EventEmitter.decorate(this);

  this.styleSheet = styleSheet;
  this._inputElement = null;
  this.sourceEditor = null;
  this._window = win;
  this._isNew = isNew;
  this.walker = walker;
  this.highlighter = highlighter;

  // True when we've called update() on the style sheet.
  this._isUpdating = false;
  // True when we've just set the editor text based on a style-applied
  // event from the StyleSheetActor.
  this._justSetText = false;

  // state to use when inputElement attaches
  this._state = {
    text: "",
    selection: {
      start: {line: 0, ch: 0},
      end: {line: 0, ch: 0}
    }
  };

  this._styleSheetFilePath = null;
  if (styleSheet.href &&
      Services.io.extractScheme(this.styleSheet.href) == "file") {
    this._styleSheetFilePath = this.styleSheet.href;
  }

  this._onPropertyChange = this._onPropertyChange.bind(this);
  this._onError = this._onError.bind(this);
  this._onMediaRuleMatchesChange = this._onMediaRuleMatchesChange.bind(this);
  this._onMediaRulesChanged = this._onMediaRulesChanged.bind(this);
  this._onStyleApplied = this._onStyleApplied.bind(this);
  this.checkLinkedFileForChanges = this.checkLinkedFileForChanges.bind(this);
  this.markLinkedFileBroken = this.markLinkedFileBroken.bind(this);
  this.saveToFile = this.saveToFile.bind(this);
  this.updateStyleSheet = this.updateStyleSheet.bind(this);
  this._updateStyleSheet = this._updateStyleSheet.bind(this);
  this._onMouseMove = this._onMouseMove.bind(this);

  this._focusOnSourceEditorReady = false;
  this.cssSheet.on("property-change", this._onPropertyChange);
  this.styleSheet.on("error", this._onError);
  this.mediaRules = [];
  if (this.cssSheet.getMediaRules) {
    this.cssSheet.getMediaRules().then(this._onMediaRulesChanged,
                                       console.error);
  }
  this.cssSheet.on("media-rules-changed", this._onMediaRulesChanged);
  this.cssSheet.on("style-applied", this._onStyleApplied);
  this.savedFile = file;
  this.linkCSSFile();
}
this.StyleSheetEditor = StyleSheetEditor;

StyleSheetEditor.prototype = {
  /**
   * Whether there are unsaved changes in the editor
   */
  get unsaved() {
    return this.sourceEditor && !this.sourceEditor.isClean();
  },

  /**
   * Whether the editor is for a stylesheet created by the user
   * through the style editor UI.
   */
  get isNew() {
    return this._isNew;
  },

  /**
   * The style sheet or the generated style sheet for this source if it's an
   * original source.
   */
  get cssSheet() {
    if (this.styleSheet.isOriginalSource) {
      return this.styleSheet.relatedStyleSheet;
    }
    return this.styleSheet;
  },

  get savedFile() {
    return this._savedFile;
  },

  set savedFile(name) {
    this._savedFile = name;

    this.linkCSSFile();
  },

  /**
   * Get a user-friendly name for the style sheet.
   *
   * @return string
   */
  get friendlyName() {
    if (this.savedFile) {
      return this.savedFile.leafName;
    }

    if (this._isNew) {
      let index = this.styleSheet.styleSheetIndex + 1;
      return getString("newStyleSheet", index);
    }

    if (!this.styleSheet.href) {
      let index = this.styleSheet.styleSheetIndex + 1;
      return getString("inlineStyleSheet", index);
    }

    if (!this._friendlyName) {
      let sheetURI = this.styleSheet.href;
      this._friendlyName = shortSource({ href: sheetURI });
      try {
        this._friendlyName = decodeURI(this._friendlyName);
      } catch (ex) {
        // Ignore.
      }
    }
    return this._friendlyName;
  },

  /**
   * Check if transitions are enabled for style changes.
   *
   * @return Boolean
   */
  get transitionsEnabled() {
    return Services.prefs.getBoolPref(TRANSITION_PREF);
  },

  /**
   * If this is an original source, get the path of the CSS file it generated.
   */
  linkCSSFile: function () {
    if (!this.styleSheet.isOriginalSource) {
      return;
    }

    let relatedSheet = this.styleSheet.relatedStyleSheet;
    if (!relatedSheet || !relatedSheet.href) {
      return;
    }

    let path;
    let href = removeQuery(relatedSheet.href);
    let uri = NetUtil.newURI(href);

    if (uri.scheme == "file") {
      let file = uri.QueryInterface(Ci.nsIFileURL).file;
      path = file.path;
    } else if (this.savedFile) {
      let origHref = removeQuery(this.styleSheet.href);
      let origUri = NetUtil.newURI(origHref);
      path = findLinkedFilePath(uri, origUri, this.savedFile);
    } else {
      // we can't determine path to generated file on disk
      return;
    }

    if (this.linkedCSSFile == path) {
      return;
    }

    this.linkedCSSFile = path;

    this.linkedCSSFileError = null;

    // save last file change time so we can compare when we check for changes.
    OS.File.stat(path).then((info) => {
      this._fileModDate = info.lastModificationDate.getTime();
    }, this.markLinkedFileBroken);

    this.emit("linked-css-file");
  },

  /**
   * A helper function that fetches the source text from the style
   * sheet.  The text is possibly prettified using prettifyCSS.  This
   * also sets |this._state.text| to the new text.
   *
   * @return {Promise} a promise that resolves to the new text
   */
  _getSourceTextAndPrettify: function () {
    return this.styleSheet.getText().then((longStr) => {
      return longStr.string();
    }).then((source) => {
      let ruleCount = this.styleSheet.ruleCount;
      if (!this.styleSheet.isOriginalSource) {
        source = prettifyCSS(source, ruleCount);
      }
      this._state.text = source;
      return source;
    });
  },

  /**
   * Start fetching the full text source for this editor's sheet.
   *
   * @return {Promise}
   *         A promise that'll resolve with the source text once the source
   *         has been loaded or reject on unexpected error.
   */
  fetchSource: function () {
    return this._getSourceTextAndPrettify().then((source) => {
      this.sourceLoaded = true;
      return source;
    }).catch(e => {
      if (this._isDestroyed) {
        console.warn("Could not fetch the source for " +
                     this.styleSheet.href +
                     ", the editor was destroyed");
        console.error(e);
      } else {
        console.error(e);
        this.emit("error", { key: LOAD_ERROR, append: this.styleSheet.href,
                             level: "warning" });
        throw e;
      }
    });
  },

  /**
   * Add markup to a region. UNUSED_CLASS is added to specified lines
   * @param region An object shaped like
   *   {
   *     start: { line: L1, column: C1 },
   *     end: { line: L2, column: C2 }    // optional
   *   }
   */
  addUnusedRegion: function (region) {
    this.sourceEditor.addLineClass(region.start.line - 1, UNUSED_CLASS);
    if (region.end) {
      for (let i = region.start.line; i <= region.end.line; i++) {
        this.sourceEditor.addLineClass(i - 1, UNUSED_CLASS);
      }
    }
  },

  /**
   * As addUnusedRegion except that it takes an array of regions
   */
  addUnusedRegions: function (regions) {
    for (let region of regions) {
      this.addUnusedRegion(region);
    }
  },

  /**
   * Remove all the unused markup regions added by addUnusedRegion
   */
  removeAllUnusedRegions: function () {
    for (let i = 0; i < this.sourceEditor.lineCount(); i++) {
      this.sourceEditor.removeLineClass(i, UNUSED_CLASS);
    }
  },

  /**
   * Forward property-change event from stylesheet.
   *
   * @param  {string} event
   *         Event type
   * @param  {string} property
   *         Property that has changed on sheet
   */
  _onPropertyChange: function (property, value) {
    this.emit("property-change", property, value);
  },

  /**
   * Called when the stylesheet text changes.
   */
  _onStyleApplied: function () {
    if (this._isUpdating) {
      // We just applied an edit in the editor, so we can drop this
      // notification.
      this._isUpdating = false;
      this.emit("style-applied");
    } else if (this.sourceEditor) {
      this._getSourceTextAndPrettify().then((newText) => {
        this._justSetText = true;
        let firstLine = this.sourceEditor.getFirstVisibleLine();
        let pos = this.sourceEditor.getCursor();
        this.sourceEditor.setText(newText);
        this.sourceEditor.setFirstVisibleLine(firstLine);
        this.sourceEditor.setCursor(pos);
        this.emit("style-applied");
      });
    }
  },

  /**
   * Handles changes to the list of @media rules in the stylesheet.
   * Emits 'media-rules-changed' if the list has changed.
   *
   * @param  {array} rules
   *         Array of MediaRuleFronts for new media rules of sheet.
   */
  _onMediaRulesChanged: function (rules) {
    if (!rules.length && !this.mediaRules.length) {
      return;
    }
    for (let rule of this.mediaRules) {
      rule.off("matches-change", this._onMediaRuleMatchesChange);
      rule.destroy();
    }
    this.mediaRules = rules;

    for (let rule of rules) {
      rule.on("matches-change", this._onMediaRuleMatchesChange);
    }
    this.emit("media-rules-changed", rules);
  },

  /**
   * Forward media-rules-changed event from stylesheet.
   */
  _onMediaRuleMatchesChange: function () {
    this.emit("media-rules-changed", this.mediaRules);
  },

  /**
   * Forward error event from stylesheet.
   *
   * @param  {string} event
   *         Event type
   * @param  {string} errorCode
   */
  _onError: function (event, data) {
    this.emit("error", data);
  },

  /**
   * Create source editor and load state into it.
   * @param  {DOMElement} inputElement
   *         Element to load source editor in
   * @param  {CssProperties} cssProperties
   *         A css properties database.
   *
   * @return {Promise}
   *         Promise that will resolve when the style editor is loaded.
   */
  load: function (inputElement, cssProperties) {
    if (this._isDestroyed) {
      return promise.reject("Won't load source editor as the style sheet has " +
                            "already been removed from Style Editor.");
    }

    this._inputElement = inputElement;

    let config = {
      value: this._state.text,
      lineNumbers: true,
      mode: Editor.modes.css,
      readOnly: false,
      autoCloseBrackets: "{}()",
      extraKeys: this._getKeyBindings(),
      contextMenu: "sourceEditorContextMenu",
      autocomplete: Services.prefs.getBoolPref(AUTOCOMPLETION_PREF),
      autocompleteOpts: { walker: this.walker, cssProperties },
      cssProperties
    };
    let sourceEditor = this._sourceEditor = new Editor(config);

    sourceEditor.on("dirty-change", this._onPropertyChange);

    return sourceEditor.appendTo(inputElement).then(() => {
      sourceEditor.on("saveRequested", this.saveToFile);

      if (this.styleSheet.update) {
        sourceEditor.on("change", this.updateStyleSheet);
      }

      this.sourceEditor = sourceEditor;

      if (this._focusOnSourceEditorReady) {
        this._focusOnSourceEditorReady = false;
        sourceEditor.focus();
      }

      sourceEditor.setSelection(this._state.selection.start,
                                this._state.selection.end);

      if (this.highlighter && this.walker) {
        sourceEditor.container.addEventListener("mousemove", this._onMouseMove);
      }

      // Add the commands controller for the source-editor.
      sourceEditor.insertCommandsController();

      this.emit("source-editor-load");
    });
  },

  /**
   * Get the source editor for this editor.
   *
   * @return {Promise}
   *         Promise that will resolve with the editor.
   */
  getSourceEditor: function () {
    let self = this;

    if (this.sourceEditor) {
      return Promise.resolve(this);
    }

    return new Promise(resolve => {
      this.on("source-editor-load", () => {
        resolve(self);
      });
    });
  },

  /**
   * Focus the Style Editor input.
   */
  focus: function () {
    if (this.sourceEditor) {
      this.sourceEditor.focus();
    } else {
      this._focusOnSourceEditorReady = true;
    }
  },

  /**
   * Event handler for when the editor is shown.
   */
  onShow: function () {
    if (this.sourceEditor) {
      // CodeMirror needs refresh to restore scroll position after hiding and
      // showing the editor.
      this.sourceEditor.refresh();
    }
    this.focus();
  },

  /**
   * Toggled the disabled state of the underlying stylesheet.
   */
  toggleDisabled: function () {
    this.styleSheet.toggleDisabled().catch(console.error);
  },

  /**
   * Queue a throttled task to update the live style sheet.
   */
  updateStyleSheet: function () {
    if (this._updateTask) {
      // cancel previous queued task not executed within throttle delay
      this._window.clearTimeout(this._updateTask);
    }

    this._updateTask = this._window.setTimeout(this._updateStyleSheet,
                                               UPDATE_STYLESHEET_DELAY);
  },

  /**
   * Update live style sheet according to modifications.
   */
  _updateStyleSheet: function () {
    if (this.styleSheet.disabled) {
      // TODO: do we want to do this?
      return;
    }

    if (this._justSetText) {
      this._justSetText = false;
      return;
    }

    // reset only if we actually perform an update
    // (stylesheet is enabled) so that 'missed' updates
    // while the stylesheet is disabled can be performed
    // when it is enabled back. @see enableStylesheet
    this._updateTask = null;

    if (this.sourceEditor) {
      this._state.text = this.sourceEditor.getText();
    }

    this._isUpdating = true;
    this.styleSheet.update(this._state.text, this.transitionsEnabled)
      .catch(console.error);
  },

  /**
   * Handle mousemove events, calling _highlightSelectorAt after a delay only
   * and reseting the delay everytime.
   */
  _onMouseMove: function (e) {
    this.highlighter.hide();

    if (this.mouseMoveTimeout) {
      this._window.clearTimeout(this.mouseMoveTimeout);
      this.mouseMoveTimeout = null;
    }

    this.mouseMoveTimeout = this._window.setTimeout(() => {
      this._highlightSelectorAt(e.clientX, e.clientY);
    }, SELECTOR_HIGHLIGHT_TIMEOUT);
  },

  /**
   * Highlight nodes matching the selector found at coordinates x,y in the
   * editor, if any.
   *
   * @param {Number} x
   * @param {Number} y
   */
  _highlightSelectorAt: Task.async(function* (x, y) {
    let pos = this.sourceEditor.getPositionFromCoords({left: x, top: y});
    let info = this.sourceEditor.getInfoAt(pos);
    if (!info || info.state !== "selector") {
      return;
    }

    let node =
        yield this.walker.getStyleSheetOwnerNode(this.styleSheet.actorID);
    yield this.highlighter.show(node, {
      selector: info.selector,
      hideInfoBar: true,
      showOnly: "border",
      region: "border"
    });

    this.emit("node-highlighted");
  }),

  /**
   * Save the editor contents into a file and set savedFile property.
   * A file picker UI will open if file is not set and editor is not headless.
   *
   * @param mixed file
   *        Optional nsIFile or string representing the filename to save in the
   *        background, no UI will be displayed.
   *        If not specified, the original style sheet URI is used.
   *        To implement 'Save' instead of 'Save as', you can pass
   *        savedFile here.
   * @param function(nsIFile aFile) callback
   *        Optional callback called when the operation has finished.
   *        aFile has the nsIFile object for saved file or null if the operation
   *        has failed or has been canceled by the user.
   * @see savedFile
   */
  saveToFile: function (file, callback) {
    let onFile = (returnFile) => {
      if (!returnFile) {
        if (callback) {
          callback(null);
        }
        return;
      }

      if (this.sourceEditor) {
        this._state.text = this.sourceEditor.getText();
      }

      let ostream = FileUtils.openSafeFileOutputStream(returnFile);
      let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
                        .createInstance(Ci.nsIScriptableUnicodeConverter);
      converter.charset = "UTF-8";
      let istream = converter.convertToInputStream(this._state.text);

      NetUtil.asyncCopy(istream, ostream, (status) => {
        if (!Components.isSuccessCode(status)) {
          if (callback) {
            callback(null);
          }
          this.emit("error", { key: SAVE_ERROR });
          return;
        }
        FileUtils.closeSafeFileOutputStream(ostream);

        this.onFileSaved(returnFile);

        if (callback) {
          callback(returnFile);
        }
      });
    };

    let defaultName;
    if (this._friendlyName) {
      defaultName = OS.Path.basename(this._friendlyName);
    }
    showFilePicker(file || this._styleSheetFilePath, true, this._window,
                   onFile, defaultName);
  },

  /**
   * Called when this source has been successfully saved to disk.
   */
  onFileSaved: function (returnFile) {
    this._friendlyName = null;
    this.savedFile = returnFile;

    if (this.sourceEditor) {
      this.sourceEditor.setClean();
    }

    this.emit("property-change");

    // TODO: replace with file watching
    this._modCheckCount = 0;
    this._window.clearTimeout(this._timeout);

    if (this.linkedCSSFile && !this.linkedCSSFileError) {
      this._timeout = this._window.setTimeout(this.checkLinkedFileForChanges,
                                              CHECK_LINKED_SHEET_DELAY);
    }
  },

  /**
   * Check to see if our linked CSS file has changed on disk, and
   * if so, update the live style sheet.
   */
  checkLinkedFileForChanges: function () {
    OS.File.stat(this.linkedCSSFile).then((info) => {
      let lastChange = info.lastModificationDate.getTime();

      if (this._fileModDate && lastChange != this._fileModDate) {
        this._fileModDate = lastChange;
        this._modCheckCount = 0;

        this.updateLinkedStyleSheet();
        return;
      }

      if (++this._modCheckCount > MAX_CHECK_COUNT) {
        this.updateLinkedStyleSheet();
        return;
      }

      // try again in a bit
      this._timeout = this._window.setTimeout(this.checkLinkedFileForChanges,
                                              CHECK_LINKED_SHEET_DELAY);
    }, this.markLinkedFileBroken);
  },

  /**
   * Notify that the linked CSS file (if this is an original source)
   * doesn't exist on disk in the place we think it does.
   *
   * @param string error
   *        The error we got when trying to access the file.
   */
  markLinkedFileBroken: function (error) {
    this.linkedCSSFileError = error || true;
    this.emit("linked-css-file-error");

    error += " querying " + this.linkedCSSFile +
             " original source location: " + this.savedFile.path;
    console.error(error);
  },

  /**
   * For original sources (e.g. Sass files). Fetch contents of linked CSS
   * file from disk and live update the stylesheet object with the contents.
   */
  updateLinkedStyleSheet: function () {
    OS.File.read(this.linkedCSSFile).then((array) => {
      let decoder = new TextDecoder();
      let text = decoder.decode(array);

      // Ensure we don't re-fetch the text from the original source
      // actor when we're notified that the style sheet changed.
      this._isUpdating = true;
      let relatedSheet = this.styleSheet.relatedStyleSheet;
      relatedSheet.update(text, this.transitionsEnabled);
    }, this.markLinkedFileBroken);
  },

  /**
    * Retrieve custom key bindings objects as expected by Editor.
    * Editor action names are not displayed to the user.
    *
    * @return {array} key binding objects for the source editor
    */
  _getKeyBindings: function () {
    let bindings = {};
    let keybind = Editor.accel(getString("saveStyleSheet.commandkey"));

    bindings[keybind] = () => {
      this.saveToFile(this.savedFile);
    };

    bindings["Shift-" + keybind] = () => {
      this.saveToFile();
    };

    bindings.Esc = false;

    return bindings;
  },

  /**
   * Clean up for this editor.
   */
  destroy: function () {
    if (this._sourceEditor) {
      this._sourceEditor.off("dirty-change", this._onPropertyChange);
      this._sourceEditor.off("saveRequested", this.saveToFile);
      this._sourceEditor.off("change", this.updateStyleSheet);
      if (this.highlighter && this.walker && this._sourceEditor.container) {
        this._sourceEditor.container.removeEventListener("mousemove",
          this._onMouseMove);
      }
      this._sourceEditor.destroy();
    }
    this.cssSheet.off("property-change", this._onPropertyChange);
    this.cssSheet.off("media-rules-changed", this._onMediaRulesChanged);
    this.cssSheet.off("style-applied", this._onStyleApplied);
    this.styleSheet.off("error", this._onError);
    this._isDestroyed = true;
  }
};

/**
 * Find a path on disk for a file given it's hosted uri, the uri of the
 * original resource that generated it (e.g. Sass file), and the location of the
 * local file for that source.
 *
 * @param {nsIURI} uri
 *        The uri of the resource
 * @param {nsIURI} origUri
 *        The uri of the original source for the resource
 * @param {nsIFile} file
 *        The local file for the resource on disk
 *
 * @return {string}
 *         The path of original file on disk
 */
function findLinkedFilePath(uri, origUri, file) {
  let { origBranch, branch } = findUnsharedBranches(origUri, uri);
  let project = findProjectPath(file, origBranch);

  let parts = project.concat(branch);
  let path = OS.Path.join.apply(this, parts);

  return path;
}

/**
 * Find the path of a project given a file in the project and its branch
 * off the root. e.g.:
 * /Users/moz/proj/src/a.css" and "src/a.css"
 * would yield ["Users", "moz", "proj"]
 *
 * @param {nsIFile} file
 *        file for that resource on disk
 * @param {array} branch
 *        path parts for branch to chop off file path.
 * @return {array}
 *        array of path parts
 */
function findProjectPath(file, branch) {
  let path = OS.Path.split(file.path).components;

  for (let i = 2; i <= branch.length; i++) {
    // work backwards until we find a differing directory name
    if (path[path.length - i] != branch[branch.length - i]) {
      return path.slice(0, path.length - i + 1);
    }
  }

  // if we don't find a differing directory, just chop off the branch
  return path.slice(0, path.length - branch.length);
}

/**
 * Find the parts of a uri past the root it shares with another uri. e.g:
 * "http://localhost/built/a.scss" and "http://localhost/src/a.css"
 * would yield ["built", "a.scss"] and ["src", "a.css"]
 *
 * @param {nsIURI} origUri
 *        uri to find unshared branch of. Usually is uri for original source.
 * @param {nsIURI} uri
 *        uri to compare against to get a shared root
 * @return {object}
 *         object with 'branch' and 'origBranch' array of path parts for branch
 */
function findUnsharedBranches(origUri, uri) {
  origUri = OS.Path.split(origUri.pathQueryRef).components;
  uri = OS.Path.split(uri.pathQueryRef).components;

  for (let i = 0; i < uri.length - 1; i++) {
    if (uri[i] != origUri[i]) {
      return {
        branch: uri.slice(i),
        origBranch: origUri.slice(i)
      };
    }
  }
  return {
    branch: uri,
    origBranch: origUri
  };
}

/**
 * Remove the query string from a url.
 *
 * @param  {string} href
 *         Url to remove query string from
 * @return {string}
 *         Url without query string
 */
function removeQuery(href) {
  return href.replace(/\?.*/, "");
}
PK
!<�3ă�Fchrome/devtools/modules/devtools/client/styleeditor/original-source.js/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

/**
 * An object of this type represents an original source for the style
 * editor.  An "original" source is one that is mentioned in a source
 * map.
 *
 * @param {String} url
 *        The URL of the original source.
 * @param {String} sourceID
 *        The source ID of the original source, as used by the source
 *        map service.
 * @param {SourceMapService} sourceMapService
 *        The source map service; @see Toolbox.sourceMapService
 */
function OriginalSource(url, sourceId, sourceMapService) {
  this.isOriginalSource = true;

  this._url = url;
  this._sourceId = sourceId;
  this._sourceMapService = sourceMapService;
}

OriginalSource.prototype = {
  /** Get the original source's URL.  */
  get url() {
    return this._url;
  },

  /** Get the original source's URL.  */
  get href() {
    return this._url;
  },

  /**
   * Return a promise that will resolve to the original source's full
   * text.  The return result is actually an object with a single
   * `string` method; this method will return the source text as a
   * string.  This is done because the style editor elsewhere expects
   * a long string actor.
   */
  getText: function () {
    if (!this._sourcePromise) {
      this._sourcePromise = this._sourceMapService.getOriginalSourceText({
        id: this._sourceId,
        url: this._url,
      }).then(contents => {
        // Make it look like a long string actor.
        return {
          string: () => contents.text,
        };
      });
    }
    return this._sourcePromise;
  },

  /**
   * Given a source-mapped, generated style sheet, a line, and a
   * column, return the corresponding original location in this style
   * sheet.
   *
   * @param {StyleSheetFront} relatedSheet
   *        The generated style sheet's actor
   * @param {Number} line
   *        Line number.
   * @param {Number} column
   *        Column number.
   * @return {Location}
   *        The original location, an object with at least
   *        `sourceUrl`, `source`, `styleSheet`, `line`, and `column`
   *        properties.
   */
  getOriginalLocation: function (relatedSheet, line, column) {
    let {href, nodeHref, actorID: sourceId} = relatedSheet;
    let sourceUrl = href || nodeHref;
    return this._sourceMapService.getOriginalLocation({
      sourceId,
      line,
      column,
      sourceUrl,
    }).then(location => {
      // Add some properties for the style editor.
      location.source = location.sourceUrl;
      location.styleSheet = relatedSheet;
      return location;
    });
  },

  // Dummy implementations, as we never emit an event.
  on: function () { },
  off: function () { },
};

exports.OriginalSource = OriginalSource;
PK
!<��]�Kchrome/devtools/modules/devtools/client/styleeditor/styleeditor-commands.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 gDevTools */

"use strict";

const l10n = require("gcli/l10n");
loader.lazyRequireGetter(this, "gDevTools",
                         "devtools/client/framework/devtools", true);

/**
 * The `edit` command opens the toolbox to the style editor, with a given
 * stylesheet open.
 *
 * This command is tricky. The 'edit' command uses the toolbox, so it's
 * clearly runAt:client, but it uses the 'resource' type which accesses the
 * DOM, so it must also be runAt:server.
 *
 * Our solution is to have the command technically be runAt:server, but to not
 * actually do anything other than basically `return args;`, and have the
 * converter (all converters are runAt:client) do the actual work of opening
 * a toolbox.
 *
 * For alternative solutions that we considered, see the comment on commit
 * 2645af7.
 */
exports.items = [{
  item: "command",
  runAt: "server",
  name: "edit",
  description: l10n.lookup("editDesc"),
  manual: l10n.lookup("editManual2"),
  params: [
    {
      name: "resource",
      type: {
        name: "resource",
        include: "text/css"
      },
      description: l10n.lookup("editResourceDesc")
    },
    {
      name: "line",
      defaultValue: 1,
      type: {
        name: "number",
        min: 1,
        step: 10
      },
      description: l10n.lookup("editLineToJumpToDesc")
    }
  ],
  returnType: "editArgs",
  exec: args => {
    return { href: args.resource.name, line: args.line };
  }
}, {
  item: "converter",
  from: "editArgs",
  to: "dom",
  exec: function (args, context) {
    let target = context.environment.target;
    let toolboxOpened = gDevTools.showToolbox(target, "styleeditor");
    return toolboxOpened.then(function (toolbox) {
      let styleEditor = toolbox.getCurrentPanel();
      styleEditor.selectStyleSheet(args.href, args.line);
      return null;
    });
  }
}];
PK
!<�J-���Hchrome/devtools/modules/devtools/client/styleeditor/styleeditor-panel.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var Services = require("Services");
var promise = require("promise");
var {Task} = require("devtools/shared/task");
var {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
var EventEmitter = require("devtools/shared/old-event-emitter");

var {StyleEditorUI} = require("resource://devtools/client/styleeditor/StyleEditorUI.jsm");
var {getString} = require("resource://devtools/client/styleeditor/StyleEditorUtil.jsm");
var {initCssProperties} = require("devtools/shared/fronts/css-properties");

var StyleEditorPanel = function StyleEditorPanel(panelWin, toolbox) {
  EventEmitter.decorate(this);

  this._toolbox = toolbox;
  this._target = toolbox.target;
  this._panelWin = panelWin;
  this._panelDoc = panelWin.document;

  this.destroy = this.destroy.bind(this);
  this._showError = this._showError.bind(this);
};

exports.StyleEditorPanel = StyleEditorPanel;

StyleEditorPanel.prototype = {
  get target() {
    return this._toolbox.target;
  },

  get panelWindow() {
    return this._panelWin;
  },

  /**
   * open is effectively an asynchronous constructor
   */
  open: Task.async(function* () {
    // We always interact with the target as if it were remote
    if (!this.target.isRemote) {
      yield this.target.makeRemote();
    }

    this.target.on("close", this.destroy);

    this._debuggee = this._toolbox.initStyleSheetsFront();

    // Initialize the CSS properties database.
    const {cssProperties} = yield initCssProperties(this._toolbox);

    // Initialize the UI
    this.UI = new StyleEditorUI(this._debuggee, this.target, this._panelDoc,
                                cssProperties);
    this.UI.on("error", this._showError);
    yield this.UI.initialize();

    this.isReady = true;

    return this;
  }),

  /**
   * Show an error message from the style editor in the toolbox
   * notification box.
   *
   * @param  {string} event
   *         Type of event
   * @param  {string} data
   *         The parameters to customize the error message
   */
  _showError: function (event, data) {
    if (!this._toolbox) {
      // could get an async error after we've been destroyed
      return;
    }

    let errorMessage = getString(data.key);
    if (data.append) {
      errorMessage += " " + data.append;
    }

    let notificationBox = this._toolbox.getNotificationBox();
    let notification =
        notificationBox.getNotificationWithValue("styleeditor-error");

    let level = notificationBox.PRIORITY_CRITICAL_LOW;
    if (data.level === "info") {
      level = notificationBox.PRIORITY_INFO_LOW;
    } else if (data.level === "warning") {
      level = notificationBox.PRIORITY_WARNING_LOW;
    }

    if (!notification) {
      notificationBox.appendNotification(errorMessage, "styleeditor-error",
                                         "", level);
    }
  },

  /**
   * Select a stylesheet.
   *
   * @param {string} href
   *        Url of stylesheet to find and select in editor
   * @param {number} line
   *        Line number to jump to after selecting. One-indexed
   * @param {number} col
   *        Column number to jump to after selecting. One-indexed
   * @return {Promise}
   *         Promise that will resolve when the editor is selected and ready
   *         to be used.
   */
  selectStyleSheet: function (href, line, col) {
    if (!this._debuggee || !this.UI) {
      return null;
    }
    return this.UI.selectStyleSheet(href, line - 1, col ? col - 1 : 0);
  },

  /**
   * Destroy the style editor.
   */
  destroy: function () {
    if (!this._destroyed) {
      this._destroyed = true;

      this._target.off("close", this.destroy);
      this._target = null;
      this._toolbox = null;
      this._panelWin = null;
      this._panelDoc = null;
      this._debuggee.destroy();
      this._debuggee = null;

      this.UI.destroy();
      this.UI = null;
    }

    return promise.resolve(null);
  },
};

XPCOMUtils.defineLazyGetter(StyleEditorPanel.prototype, "strings",
  function () {
    return Services.strings.createBundle(
            "chrome://devtools/locale/styleeditor.properties");
  });
PK
!<օ��d�d@chrome/devtools/modules/devtools/client/themes/audio/shutter.wavRIFF�dWAVEfmt D��XdataI��	���������������������������$�������#	�����QS*����������/D)����
��D0������=M9
����%12�����3<$ *������<!���������PQ����.RckM�����DYA�������������\��9��������\b1��������'>Vxn-�k�;�����AT:�������
���������56���������~�$����v��y��7������������B������g��������>jw�����9�u`Tk����n�����������=��i�s�{�� �V78�y�t��F����������.M������K"�EW��W�����z||&�/���2�b=Xl�;�u��:DT6����Tr����a�:��1���������P����4�����P��IU�mL��s��b�o�0,������`��������I20���T����T
,w�P��z��ji�V����b�u��]���s������O������#�2�X�]P�}�>������V2��P �������Y�������I��V�L�;9��������$����:
��)�L���'����^�w������a]�U�8����q�����M�x�M��S������\'���������X�,�3��P������&����6�e���<���C��Z1�A�������H�V��������gc�9[����
i�X���>	B���L1��=����4<����w������Q�B���������KB����.��D�t����<�G�aqp��5����)�����l
F�����(�)w5�31��,�M��3�
�`;uvfG
{���ʎ��294&X��*	)�Y���{�����&�5%-x+K"�֗�8j�3����ڮ��X��0k,p)��x���m#AC�3I�������^����s
��%�j��G��Ҟ���� �^����z�������^>-t#T	��y�9���A�M��������X�	�
�	���
��
�B�e�w
[�������p�1!x���f�����p���!�b�*�����5b�=g3&�����ۈ�2/#����������c
b%�0���������)��) g.��n�����������:��8
��ܑ�U �������|��4�"�}x
_��kۜ�9!@: @� ��f���9��u-	G�)E���3��Q�;	��$��Z�_�xޡ��� �?���E���"�j��t���r�ZAz
>z���������F��P�-:��>�� �H�k��eC	���~���~���D}���4���k�Z��CM;t#�����i?��H���:�u������_�w��	����������y�j�s�
��O
�����J�}�)�_e\�^��������3��?����^�o������C�B�E�w�	@	z�0����������=����
����������	�
n��z���B���Yzo�%������x��j�
e+����2�����W���^���l�<��v��/?��N���W���0�=�t	��������7��E[I����l����
��������3��?o2���F���������r�G�O���{h��qu��r���N
�~6�����i�)����#X�����!�������h��-~���y�p�E���j:y�8���&����.�
�d����n�v��7�����R�������%��Rh��v���
�j2������Ax�������
{KSVMi�����Q����o����������+!�H�5B���O������0�����k���jn�j[�v�0����������|�4���j�z}�������I�zj�0��,��M����V�n����J�]h�]����}�c�+�>��������*��x�R�c��b�����g0�&�{�q�v�����w������y������nK�~W�����������W�vn�������������Z��
8�5E�0��d
���y��v?�=���I�z���l,s������2���h��=`���������������;��[X��[���8�@�����
�|���7�����b�l������E�.��R�������8����*����B�}�����f��b���u�����p�;B�m���"���>"�}�i�c�T']8r�@��������N�c�9�������S������8���?u��7�4������n2�(�\��'���q{<����������y�����yf.��p���miJ��1���P� ��x��!�0�}�p�Nz��O^�X�7�`��*�Og����0�z���c�
C�I���H�N�D������{���X�$�
;E�%���x��[�������A-�g�"������r���
�em"V+��R�/�Y�����b���P�����x������a��'������G��D���G�S�.���q�23�������k�����������'M�&e�`��$�@�����WC�m��w�����k�vq����v%"���������Q��T�N��!�A����H���d�L���z�o����	mT������@1�{��9�Z����"������x���#������������?�?V�V���x��O�0=������tI������
��Tf����IC
J�m�K��z��r$j
|���
T�	d
N�o�?��d�������9�k!���U������
�	����u
'!]�z�u�C�&�R�	!	����z�l	p���A����r�x�	��Ss�������LY���m�����$����d�$����q	��g�C���}��b�������t�=�K�xP����_����Z��������0�>l������L!5�����*�Sdd��b�����A��m����&���+3��K��z�L��O����~�
}�x�����
mxS�`�\���)���R���������3����\���G���L5�����l���[����v�,z����O����x��6���6B3i���?������%6�#.U��5���$��T�e�'����F��{�������b�4�����^�j���^�	�F�W�Q7���V����>�n�r7����G����|����������o�Q��T�������B-]�g��e���L���L�M)aQ��O�t�T��%�6�`k�������!���I�����Gv����B%���� b���R������������1��.f��������k���C8�i��������
�O��V����p��7G���"Ec���F�� ���M�/���7�_;���D��������Zb�Z���*���X>�W}�~���$����7 ��B�I�����t����C���B�E���G�j���2����h��x�_E���v����#�����9��@���4���u���/U���6�y��X�������]�g����&���=F#>C���(O^(��I�a�[\�n�0|�E���'��������t�z�[�8���	��n��-_h���	����������@5�����:L4����"�����������$����
,,����
����������%]a=��������7NP8������� 8=;6������#3I�r�����i�O��n�T�`r&��s�Z6�+�|��I�b7W]�����1���]+����O�k���K ���;�9�0���-c��6����Q��	�\���Y=g�By�Q�V�E����#������F������#���������Q�m�x���(���7o������k�r�i���i�|����-��,�����)�-�
���AZ��L���;@KuD���������?H�F�<����G��H?������`���S}��8��V�r���LL��T������� e�k�^������z��~��P�*�A�<�n�~�O���O��x����'������d���W%���e�b�	���\��Z��-��M�9��w�-�J<�]S.�������[����|���vi+������I��:���������b�����e����<0�����9����������M���+"�����U�]��;�K���Yep�$�����/Y��U�����a��
k�'E�r����e,G3���7��	8�?��!��������?����/�����To��M�B��|�����?�����n���9
�i���<���i�sU��p�19�u���L������5�n�2�-�����2�b��!�j�u�s��r�>0p��y���a���R�3��02���5�����������
,^�������b�R������h6�����k�_������sTWc`�W�<�=��Gt���I��������X�w��� U)qV�������������g�����d4�Cx[��3����5�U�G�u�j�����z�5�����j�����������6w����k��!�V�!������������`�<��������{��r�e�3�����������;�D^��5�������'L���Z�(_�m����%��^�g�'������h��������O�����ol���j�����[��p.�=��?�
�X�������������IXN����\����!�������L�	k��|���m�����:��(}����g�<����=��v���$w�����5�;�e��f���P���t��%����v��l����-���'�"�f�����<�W���j/~n�O�����.���S
�t�Y����E�0����JA��]�����i��������i�	�Y�%��u����9�%�(������(��������6��fS��h�L����G�r����y�N���Ob����V����J�%�,����q_���U�k��<��.��h�C�v�0(~�|���]�i��n�k��m����*���v
����
����?H|�����#��z����)�����u������t�������U/J��<�&�%��������-P�F�/"��V���q���`�i�y��������:a�D�}�G(T������'�<u�����y���=�a����KN����j:�i��H���5|�|������/Z��(���b���G����9��<�r3�0����C��/3������/�A�P��5����P��.O�c��=�~��������p���y�i���H��8���Z���$�BE�k��=������F�m�D�w���w��q�k2�9�����d'r5���R�\�^���2��)���5�)�]���.��������u������3��K��3`[M���a�����'���/����&��h���������u�h��./���p���������J�|�����(�.]��I���%��'�'�"���������&�(�����q��c��D���>���yE����o�7P�h�8�`���d���u�p����x�;o���bw��`�n���g���rq���@�L����;�����z������h�D
��K�������z��q�S������+q��Y��T�
�;���rJ��v�������������i�j���:����� �_�S�E�������C���.�,�6���!MT�P�����)��������������-��9Y�������������y,�����>����4���f��� ?��o>����*���'�3k�s3�����������r>�����s��������������E\� �D���c�]�������4��e���~�Ss�)��l�v��%U�C���������'d��>����������Y���N��Y�9|���D�W��b�q�S����dy�L��)���D,d��d���X����*���E����Y���U<��;�#K�s���"*��3�q������"4��2��p����u����0�Y�����>���gj�}�7��\u���2����B���-�"�B�US�G�����
���md��s�-�3�����"���Z��l�3���8j\t���.��d��;�������A��}4z��{��t���R�4���L�����8���������a�7��!�7[[��>�B�������h�J�J�N�����Tp�������H��7�$\<�"��F����K�������s�B�Z��B�vp����*�dk���{�����=����/�����}���R�X�����Y�i�����x������=�������y�LD���|��[���d� �����8�?�H�����,���l�Y���CB�yf�2���������d�������e7�L��[�n����/:il�!�;���j���o+���3�����?�}���������\�
���0��������kf��0���� ���aj������#(E���������F@G��V��N��Ae��0q�C����c���	�����|�����U��5#���,���%�h�k��h�<�S3�������R�#�
@��U�f���/�����L��"������BGD���l�F��DA_���3�u8%0���x>�(���+�`�^�j���i��x@�����|5'R��_�I����Y�`Q������!��#�u���O�:���������\�~�����4�	�����B:,��
�V� �^��x�Z��������Q������������U�p�N������A}���~����3�8/�N���4�7�3���������������o������IZ��_^�������S��5����e��s����X�
"���M���g����U�J��m�Q�g��^�a���;"�����O�n9��������+=A�l+�e�5O��#��9����-Mc�n�>���Le.i�b����������%�q��j��G���w�=�h���*�������f\���.�����K��hE�������>j����^�Bo���(�6����4�+������S�J�����l��<����	� ��H�%�Β�I���,�>����&̇��
35h��L�l����7��d��������	W��
:�5�1�r�����e������x�4��	~�$��y�	��V���m�5�2�"[���Q��2���]�R��	�s���������-�����w�'�����4f
I��?���>���;���h�������T�>�<��V���U�������*�"��v�~�N���e6�5���+�	0+t�����u�����~���6NU���j���Q������?
�	~>���-�l�$�F���Ok�z0�b��$����@���L�������k�����dy�����h��g�0a����!�
���\�M����w������U�8�^�������V��l�q��	�N������3nDy����13���`fV��������S���L1�������e��>������������t��b��2������'�����V�����R���=�&�t��$J�����Y������U)3	�	������}��o���1n�^�A���p:Z���������RI�����,�j�P��5������J3+�����W����=�(���8��5������k��nB�?�<����g�p���D���������~�U��+��qJ�)�6���Z�q������������n�!��Z��8��w�^�t��|�f[�g���������+�����-�W~�WT�P������+����m�)���j7�5������0����<���O���_`��x�Qi����7�����'����8����Y�������`1"���
�P����u8��8����+p��D�v�m���I���=���������G��K����5�n����.
�5���k�{��r�� LS?��>����r�=�k" ����12������Hm��<���������~���������E�����K��drp�;����V����.Z_�D�,��M��[�����������B�����b���XF���7>��:����16��>����N�c��m���B�i�[���yba��������D�&�������SM��aT�>�a�y�(C������D�bB���q�]�`��3��.9q��[�x���b��l��e�/��3[�9���A����{�j������
y���4���{�����n�!� ��a�G�C�h��My��2X�$�b���!���&��$�v����,���/Ij�J�u�z��Y�waN���
J9��5u5����U�����f��c9���Q��c��C�T���������������K�I��K���L(��f�@�\���Ii���,3TZ&����!����~���Z����C�=���!�������7���,����������m���U�yG���+���V�Iv���%�z�����������Yvy-��\�����6sij��I�q������������������\����@i)�eQ��������=-���������l��������9������D@��b���SFE��
)a3����{�q��\i#�sz9�����#va�z���%YW=.!A"�t�J�X���4��P�ix1����/O2�������u���6���t�m��������()�������!/�����3%��y�8����P�V)#����*U\PB@3����U�K����^~G�{�/�z�D�,�������0��������	����0+�����?K,���������$D9������������u�S�Y���-��=�����AbZ����b|I�����7.���������� �����������9])���������� B:������Wd#�~�����Mk/	;[D/������4VK-$/DL2����
������'1%�����	<T;����������
���)#����������& 
������
)?2������&I63 ������#F@Cd`���	 �����
;)�����0S/	��������������LxL
�������C��<�����$>G��s�����m�f�������GW3���
2(���������*�������'�����������0�������������������4(���"$����@T&���������5+
����������������,�������!>4�������-A16��������%"��BE*	��������/:6#
����3=!�����2-
�������������.5�������������������%������� ;; ������
'����'<?:&���$��#  #	��������� ��������������-6������2@$�����������'"$,	������ �������������	����	����������������������
���  ������	��������21������"&���������"
������)5(	�����	!$�������2- ��������''
��������%& ������
���������	��	
����������������������������������
�������
�����
���������
���������������������������������		��������������������!0��������
"	��	������
	������������
������
��������#)%�����''����#4-�������!����������������������������

��
�����������	��

��

���������������������������������������
����	������������������
	������������������������
������
��������������	����	����
��������
�����������������������		���������
����������
	��
	����������
����
�������������	�����������	�����������������
����������

����������������

������������������������������������������	������������������	����
	������������������
�����������������������������	����
����������������������
��������������������������������������������������������������������������������
����������������������������������������������������
�����������������������������������
������������	�������������������������������������������������������������	������	������	����������������������������������������	����������
	��������
������������	

������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������	������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������AFAn�streamtyped���@���NSMutableDictionary��NSDictionary��NSObject��i����NSString��+Date�����NSDate��d�)�� lϸA�����TablesAreBigEndian�����NSNumber��NSValue��*��c������Channels�����NSMutableArray��NSArray����������Max�������f��f?�����
AFRMSWaveform�����NSData���L�[332c]��28p9�V:>�0;�v(<ʹ<b<�f>$)%>J�%= e<�QU</�=�=���<rm>Ǐ�<}�&<I"<L��;��;�І;��*;�X;���:)�:U�:��;��<�
�<�>�<F8�< ZZ<��l<��<���<�Ɩ<���<R��<�+�=���<_qN<Բ�;ޯ�;��;;��;;�:}�:̨�:��:.b:��[:5�S:�}3:1N&:��':���9��9�P�9�ΰ9o�~9;K*9���8��8̚�8$�D8Q�?8++#88�888�+	84�8���7�S�7qĜ7�`�7j�s7>�q7:bO7�57�����Clipped�������������RMS����������=�����AFMeanWaveform������L��7A9 �9`;I<��;n6<hx>e�=@��<��8<�+<�r=@��<��V<��=@@�<<:�;$�;�U;"];p;��:�:��:H�:x�;��;��<k�<�e<��-<�23<@��<@z�<��n<Zy<�T<0V�=��<n!<�;_;8;�:X�:��:Ċ:�b:�2: ,:�%:(:0:�:�9��9��9��9�K9@9��8@�8�T8�8�88�7�7�7�7�7<7\7@7(7�6�6�6�6�����
AFMaxWaveform������L� 9:;�<�<��<�=f?�3?()>�B==��>@>@^=\�>��=��<��<�<O<�+<�;�;�;z;p;�<��<��=��=�6=�'=�b=�=0�=�j=��=@a=|�>P> =@�<8<<�;�;V;t;8;$;V;;;�:;�:�:�:�:H::�9�9@9 99�8�89�8�8�8�8�88�88888����_PMXp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.5-c014 79.151805, 2013/04/09-12:08:21        ">
   <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
      <rdf:Description rdf:about=""
            xmlns:xmpDM="http://ns.adobe.com/xmp/1.0/DynamicMedia/"
            xmlns:xmp="http://ns.adobe.com/xap/1.0/"
            xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/"
            xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#"
            xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#"
            xmlns:dc="http://purl.org/dc/elements/1.1/">
         <xmpDM:Tracks>
            <rdf:Bag>
               <rdf:li rdf:parseType="Resource">
                  <xmpDM:trackName>CuePoint Markers</xmpDM:trackName>
                  <xmpDM:trackType>Cue</xmpDM:trackType>
                  <xmpDM:frameRate>f44100</xmpDM:frameRate>
               </rdf:li>
               <rdf:li rdf:parseType="Resource">
                  <xmpDM:trackName>CD Track Markers</xmpDM:trackName>
                  <xmpDM:trackType>Track</xmpDM:trackType>
                  <xmpDM:frameRate>f44100</xmpDM:frameRate>
               </rdf:li>
               <rdf:li rdf:parseType="Resource">
                  <xmpDM:trackName>Subclip Markers</xmpDM:trackName>
                  <xmpDM:trackType>InOut</xmpDM:trackType>
                  <xmpDM:frameRate>f44100</xmpDM:frameRate>
               </rdf:li>
            </rdf:Bag>
         </xmpDM:Tracks>
         <xmp:MetadataDate>2014-08-28T12:05:33-07:00</xmp:MetadataDate>
         <xmp:ModifyDate>2014-08-28T12:05:33-07:00</xmp:ModifyDate>
         <xmpMM:InstanceID>xmp.iid:3d8da4c4-93d1-4856-aa0a-31a56f6b9cb5</xmpMM:InstanceID>
         <xmpMM:DocumentID>xmp.did:3d8da4c4-93d1-4856-aa0a-31a56f6b9cb5</xmpMM:DocumentID>
         <xmpMM:OriginalDocumentID>xmp.did:a79c9b4b-de8a-45a2-9b1c-13956d97bb47</xmpMM:OriginalDocumentID>
         <xmpMM:History>
            <rdf:Seq>
               <rdf:li rdf:parseType="Resource">
                  <stEvt:action>saved</stEvt:action>
                  <stEvt:instanceID>xmp.iid:a79c9b4b-de8a-45a2-9b1c-13956d97bb47</stEvt:instanceID>
                  <stEvt:when>2014-08-28T12:05:33-07:00</stEvt:when>
                  <stEvt:softwareAgent>Adobe Audition CC (Macintosh)</stEvt:softwareAgent>
                  <stEvt:changed>/metadata</stEvt:changed>
               </rdf:li>
               <rdf:li rdf:parseType="Resource">
                  <stEvt:action>saved</stEvt:action>
                  <stEvt:instanceID>xmp.iid:3d8da4c4-93d1-4856-aa0a-31a56f6b9cb5</stEvt:instanceID>
                  <stEvt:when>2014-08-28T12:05:33-07:00</stEvt:when>
                  <stEvt:softwareAgent>Adobe Audition CC (Macintosh)</stEvt:softwareAgent>
                  <stEvt:changed>/</stEvt:changed>
               </rdf:li>
            </rdf:Seq>
         </xmpMM:History>
         <xmpMM:DerivedFrom rdf:parseType="Resource">
            <stRef:instanceID>xmp.iid:a79c9b4b-de8a-45a2-9b1c-13956d97bb47</stRef:instanceID>
            <stRef:documentID>xmp.did:a79c9b4b-de8a-45a2-9b1c-13956d97bb47</stRef:documentID>
            <stRef:originalDocumentID>xmp.did:a79c9b4b-de8a-45a2-9b1c-13956d97bb47</stRef:originalDocumentID>
         </xmpMM:DerivedFrom>
         <dc:format>audio/x-wav</dc:format>
      </rdf:Description>
   </rdf:RDF>
</x:xmpmeta>
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                           
<?xpacket end="w"?>LGWVR�$�-%���v�G����!�!������������5����������������PK
!<��h���Fchrome/devtools/modules/devtools/client/themes/commandline-browser.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* Developer toolbar */

@namespace html url("http://www.w3.org/1999/xhtml");

/* NOTE: THESE NEED TO STAY IN SYNC WITH LIGHT-THEME.CSS AND DARK-THEME.CSS.
   We are copy/pasting variables from light-theme and dark-theme,
   since they aren't loaded in this context (within browser.css). */
#browser-bottombox[devtoolstheme="light"] #developer-toolbar {
  --gcli-background-color: #fcfcfc; /* --theme-tab-toolbar-background */
  --gcli-input-background: #fcfcfc; /* --theme-toolbar-background */
  --gcli-input-focused-background: #ffffff; /* --theme-sidebar-background */
  --gcli-input-color: #393f4c; /* --theme-body-color */
  --gcli-border-color: #e0e0e1; /* --theme-splitter-color */
  --selection-background: #4c9ed9; /* --theme-selection-background */
  --selection-color: #f5f7fa; /* --theme-selection-color */
  --command-line-image: url(chrome://devtools/skin/images/commandline-icon.svg#light-theme); /* --theme-command-line-image */
  --command-line-image-focus: url(chrome://devtools/skin/images/commandline-icon.svg#light-theme-focus); /* --theme-command-line-image-focus */
}

#browser-bottombox[devtoolstheme="dark"] #developer-toolbar {
  --gcli-background-color: #272b35; /* --theme-toolbar-background */
  --gcli-input-background: #272b35; /* --theme-tab-toolbar-background */
  --gcli-input-focused-background: #272b35; /* --theme-tab-toolbar-background */
  --gcli-input-color: #b6babf; /* --theme-body-color-alt */
  --gcli-border-color: #3c3c3d; /* --theme-splitter-color */
  --selection-background: #5675b9; /* --theme-selection-background */
  --selection-color: #f5f7fa; /* --theme-selection-color */
  --command-line-image: url(chrome://devtools/skin/images/commandline-icon.svg#dark-theme); /* --theme-command-line-image */
  --command-line-image-focus: url(chrome://devtools/skin/images/commandline-icon.svg#dark-theme-focus); /* --theme-command-line-image-focus */
}

#developer-toolbar {
  -moz-appearance: none;
  padding: 0;
  min-height: 32px;
  background-color: var(--gcli-background-color);
  color: var(--gcli-input-color);
  border-top: 1px solid var(--gcli-border-color);
}

#developer-toolbar > toolbarbutton {
  -moz-appearance: none;
  border: none;
  background-color: transparent;
  margin: 0;
  padding: 0 10px;
  width: 32px;
}

.developer-toolbar-button > image {
  margin: auto 10px;
}

#browser-bottombox[devtoolstheme="light"] #developer-toolbar > .developer-toolbar-button:not([checked=true]) > image,
#browser-bottombox[devtoolstheme="light"] .gclitoolbar-input-node:not([focused=true])::before  {
  filter: invert(1);
}

.developer-toolbar-button > .toolbarbutton-icon {
  width: 16px;
  height: 16px;
}

#developer-toolbar-toolbox-button {
  list-style-image: url("chrome://devtools/skin/images/toggle-tools.png");
  -moz-image-region: rect(0px, 16px, 16px, 0px);
}

#developer-toolbar-toolbox-button > label {
  display: none;
}

#developer-toolbar-toolbox-button:hover {
  -moz-image-region: rect(0px, 32px, 16px, 16px);
}

#developer-toolbar-toolbox-button:hover:active {
  -moz-image-region: rect(0px, 48px, 16px, 32px);
}

#developer-toolbar-toolbox-button[checked=true] {
  -moz-image-region: rect(0px, 64px, 16px, 48px);
}

@media (min-resolution: 1.1dppx) {
  #developer-toolbar-toolbox-button {
    list-style-image: url("chrome://devtools/skin/images/[email protected]");
    -moz-image-region: rect(0px, 32px, 32px, 0px);
  }

  #developer-toolbar-toolbox-button:hover {
    -moz-image-region: rect(0px, 64px, 32px, 32px);
  }

  #developer-toolbar-toolbox-button:hover:active {
    -moz-image-region: rect(0px, 96px, 32px, 64px);
  }

  #developer-toolbar-toolbox-button[checked=true] {
    -moz-image-region: rect(0px, 128px, 32px, 96px);
  }
}

/* GCLI */

html|*#gcli-tooltip-frame,
html|*#gcli-output-frame {
  padding: 0;
  border-width: 0;
  background-color: transparent;
}

#gcli-output,
#gcli-tooltip {
  border-width: 0;
  background-color: transparent;
  -moz-appearance: none;

  /* We always wanted this to be a percentage of the viewport size but width: x% does not
   * work. vw is the obvious solution.
   */
  width: 80vw;
}

.gclitoolbar-input-node,
.gclitoolbar-complete-node {
  margin: 0;
  -moz-box-align: center;
  padding-top: 0;
  padding-bottom: 0;
  padding-right: 8px;
  text-shadow: none;
  box-shadow: none;
  border-width: 0;
  background-color: transparent;
  border-radius: 0;
}

.gclitoolbar-input-node {
  -moz-appearance: none;
  color: var(--gcli-input-color);
  background-color: var(--gcli-input-background);
  background-repeat: no-repeat;
  background-position: 4px center;
  box-shadow: 1px 0 0 var(--gcli-border-color) inset,
              -1px 0 0 var(--gcli-border-color) inset;

  line-height: 32px;
  outline-style: none;
  padding: 0;
}

.gclitoolbar-input-node[focused="true"] {
  background-color: var(--gcli-input-focused-background);
}

.gclitoolbar-input-node::before {
  content: "";
  display: inline-block;
  -moz-box-ordinal-group: 0;
  width: 16px;
  height: 16px;
  margin: 0 2px;
  background-image: var(--command-line-image);
}

.gclitoolbar-input-node[focused="true"]::before {
  background-image: var(--command-line-image-focus);
}

.gclitoolbar-input-node > .textbox-input-box > html|*.textbox-input::-moz-selection {
  background-color: var(--selection-background);
  color: var(--selection-color);
  text-shadow: none;
}

.gclitoolbar-complete-node {
  padding-left: 21px;
  background-color: transparent;
  color: transparent;
  z-index: 100;
  pointer-events: none;
}

.gcli-in-incomplete,
.gcli-in-error,
.gcli-in-ontab,
.gcli-in-todo,
.gcli-in-closebrace,
.gcli-in-param,
.gcli-in-valid {
  margin: 0;
  padding: 0;
}

.gcli-in-incomplete {
  border-bottom: 2px dotted #999;
}

.gcli-in-error {
  border-bottom: 2px dotted #F00;
}

.gcli-in-ontab {
  color: hsl(210,0%,35%);
}

.gcli-in-todo {
  color: hsl(210,50%,35%);
}

.gcli-in-closebrace {
  color: hsl(0,0%,80%);
}
PK
!<a�68II9chrome/devtools/modules/devtools/client/themes/common.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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("resource://devtools/client/themes/splitters.css");
@namespace html url("http://www.w3.org/1999/xhtml");

:root {
  font: message-box;

  --tab-line-selected-color: var(--blue-50);
}

:root.theme-light {
  --tab-line-hover-color: rgba(0,0,0,.2);
}

:root.theme-dark {
  --tab-line-hover-color: rgba(255,255,255,.2);
}

:root[platform="mac"] {
  --monospace-font-family: Menlo, monospace;
}

:root[platform="win"] {
  --monospace-font-family: Consolas, monospace;
}

:root[platform="linux"],
:root.theme-firebug {
  --monospace-font-family: monospace;
}

:root.theme-firebug {
  --proportional-font-family: Lucida Grande, Tahoma, sans-serif;
}

.devtools-monospace {
  font-family: var(--monospace-font-family);
}

:root[platform="linux"] .devtools-monospace {
  font-size: 11px;
}

/* Override wrong system font from forms.css */
html|button, html|select {
  font: message-box;
}

/* Autocomplete Popup */

.devtools-autocomplete-popup {
  box-shadow: 0 1px 0 hsla(209,29%,72%,.25) inset;
  background-color: transparent;
  border-radius: 3px;
  overflow-x: hidden;
  max-height: 20rem;

  /* Devtools autocompletes display technical english keywords and should be displayed
     using LTR direction. */
  direction: ltr !important;
}

/* Reset list styles. */
.devtools-autocomplete-popup ul {
  list-style: none;
}

.devtools-autocomplete-popup ul,
.devtools-autocomplete-popup li {
  margin: 0;
}

:root[platform="linux"] .devtools-autocomplete-popup {
  /* Root font size is bigger on Linux, adjust rem-based values. */
  max-height: 16rem;
}

.devtools-autocomplete-listbox {
  -moz-appearance: none !important;
  background-color: transparent;
  border-width: 0px !important;
  margin: 0;
  padding: 2px;
}

.devtools-autocomplete-listbox .autocomplete-item {
  width: 100%;
  background-color: transparent;
  border-radius: 4px;
  padding: 1px 0;
  cursor: default;
  text-overflow: ellipsis;
  white-space: pre;
  overflow: hidden;
}

.devtools-autocomplete-listbox .autocomplete-item > .initial-value,
.devtools-autocomplete-listbox .autocomplete-item > .autocomplete-value {
  margin: 0;
  padding: 0;
}

.devtools-autocomplete-listbox .autocomplete-item > .autocomplete-count {
  text-align: end;
}

/* Rest of the dark and light theme */

.devtools-autocomplete-popup,
.CodeMirror-hints,
.CodeMirror-Tern-tooltip {
  border: 1px solid hsl(210,24%,90%);
  background-image: linear-gradient(to bottom, hsla(209,18%,100%,0.9), hsl(210,24%,95%));
}

.theme-dark .devtools-autocomplete-popup,
.theme-dark .CodeMirror-hints,
.theme-dark .CodeMirror-Tern-tooltip {
  border: 1px solid hsl(210,11%,10%);
  background-image: linear-gradient(to bottom, hsla(209,18%,18%,0.9), hsl(210,11%,16%));
}

.theme-firebug .devtools-autocomplete-popup {
  border-color: var(--theme-splitter-color);
  border-radius: 5px;
  font-size: var(--theme-autompletion-font-size);
  background: var(--theme-body-background);
}

.devtools-autocomplete-listbox .autocomplete-selected,
.devtools-autocomplete-listbox .autocomplete-item:hover {
  background-color: rgba(128,128,128,0.3);
}

.theme-dark .devtools-autocomplete-listbox .autocomplete-selected,
.theme-dark .devtools-autocomplete-listbox .autocomplete-item:hover {
  background-color: rgba(0,0,0,0.5);
}

.devtools-autocomplete-listbox .autocomplete-selected > .autocomplete-value,
.devtools-autocomplete-listbox:focus .autocomplete-selected > .initial-value {
  color: #222;
}

.theme-dark .devtools-autocomplete-listbox .autocomplete-selected > .autocomplete-value,
.theme-dark .devtools-autocomplete-listbox:focus .autocomplete-selected > .initial-value {
  color: hsl(208,100%,60%);
}

.devtools-autocomplete-listbox .autocomplete-item > span {
  color: #666;
}

.theme-dark .devtools-autocomplete-listbox .autocomplete-item > span {
  color: #ccc;
}

.theme-dark .devtools-autocomplete-listbox .autocomplete-selected > span {
  color: #eee;
}

/* Autocomplete list clone used for accessibility. */

.devtools-autocomplete-list-aria-clone {
  /* Cannot use display:none or visibility:hidden : screen readers ignore the element. */
  position: fixed;
  overflow: hidden;
  margin: 0;
  width: 0;
  height: 0;
}

.devtools-autocomplete-list-aria-clone li {
  /* Prevent screen readers from prefacing every item with 'bullet'. */
  list-style-type: none;
}

/* links to source code, like displaying `myfile.js:45` */

.devtools-source-link {
  font-family: var(--monospace-font-family);
  color: var(--theme-highlight-blue);
  cursor: pointer;
  white-space: nowrap;
  display: flex;
  text-decoration: none;
  font-size: 11px;
  width: 12em; /* probably should be changed for each tool */
}

.devtools-source-link:hover {
  text-decoration: underline;
}

.devtools-source-link > .filename {
  text-overflow: ellipsis;
  text-align: end;
  overflow: hidden;
  margin: 2px 0px;
  cursor: pointer;
}

.devtools-source-link > .line-number {
  flex: none;
  margin: 2px 0px;
  cursor: pointer;
}

/* Keyboard focus highlight styles */

:-moz-focusring {
  outline: var(--theme-focus-outline);
  outline-offset: -1px;
}

textbox[focused="true"] {
  border-color: var(--theme-focus-border-color-textbox);
  box-shadow: var(--theme-focus-box-shadow-textbox);
  transition: all 0.2s ease-in-out
}

textbox :-moz-focusring {
  box-shadow: none;
  outline: none;
}

/* Form fields should already have box-shadow hightlight */
select:-moz-focusring,
input[type="radio"]:-moz-focusring,
input[type="checkbox"]:-moz-focusring,
checkbox:-moz-focusring {
  outline: none;
}

/* Toolbar buttons */
.devtools-menulist,
.devtools-toolbarbutton,
.devtools-button {
  -moz-appearance: none;
  background: transparent;
  border: 1px solid var(--toolbarbutton-border-color);
  border-radius: 2px;
  color: var(--theme-body-color);
  transition: background 0.05s ease-in-out;
  -moz-box-align: center;
  text-shadow: none;
  padding: 1px;
  margin: 1px;

  /* Button text should not wrap on multiple lines */
  white-space: nowrap;
}

.devtools-toolbarbutton:not([label]) > .toolbarbutton-icon,
.devtools-button:empty::before {
  width: 16px;
  height: 16px;
  margin: 0 3px;
  transition: opacity 0.05s ease-in-out;

  color: var(--theme-toolbar-color);
  direction: ltr;
  font-size: 11px;
}

.devtools-button:empty::before {
  content: "";
  display: inline-block;
  background-size: cover;
  background-repeat: no-repeat;
  vertical-align: middle;
}

.devtools-button.checked:empty::before {
  color: var(--theme-toolbar-checked-color);
}

/* Standalone buttons */
.devtools-button[standalone],
.devtools-button[data-standalone],
.devtools-toolbarbutton[standalone],
.devtools-toolbarbutton[data-standalone] {
  border-color: rgba(138,161,180,0.2) !important;
  min-height: 30px;
}

.devtools-toolbarbutton[standalone], .devtools-toolbarbutton[data-standalone] {
  margin-inline-end: 5px;
}

/* Icon button styles */
.devtools-toolbarbutton:not([label]) > .toolbarbutton-text {
  display: none;
}

/* Icon-only buttons */
.devtools-button:hover:empty:not(:disabled):before,
.devtools-button.checked:empty::before,
.devtools-toolbarbutton:not([label]):not([disabled=true]):hover > image,
.devtools-toolbarbutton:not([label])[checked=true] > image,
.devtools-toolbarbutton:not([label])[open=true] > image {
  opacity: 1;
}

.devtools-button:disabled,
.devtools-toolbarbutton[disabled] {
  opacity: 0.5 !important;
}

.devtools-button.checked::before,
.devtools-toolbarbutton:not([label])[checked=true] > image,
.devtools-toolbarbutton:not([label])[open=true] > image {
  fill: var(--theme-toolbar-checked-color);
}

/* Button states */
.devtools-toolbarbutton[label]:not([type=menu-button]),
.devtools-toolbarbutton[standalone],
.devtools-button[data-standalone],
.devtools-button:not(:empty) {
  background: var(--toolbarbutton-background);
  padding: 0 5px;
}

.devtools-toolbarbutton:not([label]):hover,
.devtools-button:empty:not(:disabled):hover {
  background: var(--toolbarbutton-background);
}

.devtools-button:not(:empty):not(:disabled):not(.checked):hover,
.devtools-toolbarbutton[label]:not(:-moz-any([checked=true],[disabled])):hover,
.devtools-button:empty:not(:disabled):-moz-any(:hover:active,.checked),
.devtools-toolbarbutton:not([label]):-moz-any([checked],[open],:hover:active) {
  background: var(--toolbarbutton-hover-background);
  border-color: var(--toolbarbutton-hover-border-color);
}

.devtools-button:not(:empty):not(.checked):not(:disabled):hover:active,
.devtools-toolbarbutton:not(:-moz-any([checked=true],[disabled]))[label]:hover:active {
  background-color: var(--theme-selection-background-hover);
}

.devtools-toolbarbutton:not([disabled])[label][checked=true],
.devtools-toolbarbutton:not([disabled])[label][open],
.devtools-button:not(:empty).checked,
.theme-firebug .devtools-toolbarbutton[checked],
.theme-firebug .devtools-toolbarbutton[open],
.theme-firebug .devtools-button.checked:empty {
  background: var(--toolbarbutton-checked-background);
  border-color: var(--toolbarbutton-checked-border-color);
  color: var(--toolbarbutton-checked-color);
}

/* Icons */
:root {
  --clear-icon-url: url("chrome://devtools/skin/images/clear.svg");
}

.devtools-button.devtools-clear-icon::before {
  background-image: var(--clear-icon-url);
}

.devtools-button.devtools-filter-icon::before {
  background-image: var(--filter-image);
}

.devtools-toolbarbutton.devtools-clear-icon {
  list-style-image: var(--clear-icon-url);
}

.devtools-option-toolbarbutton {
  list-style-image: var(--tool-options-image);
}

.devtools-toolbarbutton-group > .devtools-toolbarbutton:last-child {
  margin-inline-end: 0;
}

.devtools-toolbarbutton-group + .devtools-toolbarbutton {
  margin-inline-start: 3px;
}

/* Text input */

.devtools-textinput,
.devtools-searchinput,
.devtools-filterinput {
  -moz-appearance: none;
  margin: 1px 3px;
  border: 1px solid;
  border-radius: 2px;
  padding: 4px 6px;
  border-color: var(--theme-splitter-color);
  font: message-box;
}

:root[platform="mac"] .devtools-textinput,
:root[platform="mac"] .devtools-searchinput,
:root[platform="mac"] .devtools-filterinput {
  border-radius: 20px;
}

.devtools-searchinput,
.devtools-filterinput {
  padding: 0;
  padding-inline-start: 22px;
  padding-inline-end: 4px;
  background-position: 8px center;
  background-size: 11px 11px;
  background-repeat: no-repeat;
  font-size: inherit;
}

/*
 * @TODO : has-clear-btn class was added for bug 1296187 and we should remove it
 *  once we have a standardized search and filter input across the toolboxes.
 */
.has-clear-btn > .devtools-searchinput,
.has-clear-btn > .devtools-filterinput {
  padding-inline-end: 23px;
}

.devtools-searchinput {
  background-image: var(--magnifying-glass-image);
}

.devtools-filterinput {
  background-image: url(chrome://devtools/skin/images/filter.svg#filterinput);
}

.devtools-searchinput:-moz-locale-dir(rtl),
.devtools-searchinput:dir(rtl),
.devtools-filterinput:-moz-locale-dir(rtl),
.devtools-filterinput:dir(rtl) {
  background-position: calc(100% - 8px) center;
}

/* This is needed to remove the default search icon on windows. See bug 1400615. */
.devtools-filterinput > .textbox-input-box > .textbox-search-sign {
  display: none;
}

.devtools-searchinput > .textbox-input-box > .textbox-search-icons > .textbox-search-icon,
.devtools-filterinput > .textbox-input-box > .textbox-search-icons > .textbox-search-icon {
  visibility: hidden;
}

.devtools-searchinput .textbox-input::placeholder,
.devtools-filterinput .textbox-input::placeholder {
  font-style: normal;
}

.devtools-plaininput {
  border-color: transparent;
  background-color: transparent;
}

.theme-dark .devtools-plaininput {
  color: var(--theme-highlight-gray);
}

/* Searchbox is a div container element for a search input element */
.devtools-searchbox {
  display: inline-flex;
  flex: 1;
  height: 23px;
  position: relative;
  padding: 0 3px;
}

/* The spacing is accomplished with a padding on the searchbox */
.devtools-searchbox > .devtools-textinput,
.devtools-searchbox > .devtools-searchinput,
.devtools-searchbox > .devtools-filterinput {
  margin-left: 0;
  margin-right: 0;
  width: 100%;
}

.devtools-textinput:focus,
.devtools-searchinput:focus,
.devtools-filterinput:focus {
  border-color: var(--theme-focus-border-color-textbox);
  box-shadow: var(--theme-focus-box-shadow-textbox);
  transition: all 0.2s ease-in-out;
  outline: none;
}

.devtools-searchbox .devtools-autocomplete-popup {
  position: absolute;
  top: 100%;
  width: 100%;
  line-height: initial !important;
  /* See bug - 1414609. z-index is greater than 1000 so that searchbox's z-index
  is more than z-index of requests-list-headers-wrapper in netmonitor */
  z-index: 1001;
}

/* Don't add 'double spacing' for inputs that are at beginning / end
   of a toolbar (since the toolbar has it's own spacing). */
.devtools-toolbar > .devtools-textinput:first-child,
.devtools-toolbar > .devtools-searchinput:first-child,
.devtools-toolbar > .devtools-filterinput:first-child {
  margin-inline-start: 0;
}
.devtools-toolbar > .devtools-textinput:last-child,
.devtools-toolbar > .devtools-searchinput:last-child,
.devtools-toolbar > .devtools-filterinput:last-child {
  margin-inline-end: 0;
}
.devtools-toolbar > .devtools-searchbox:first-child {
  padding-inline-start: 0;
}
.devtools-toolbar > .devtools-searchbox:last-child {
  padding-inline-end: 0;
}

.devtools-rule-searchbox {
  -moz-box-flex: 1;
  width: 100%;
}

.devtools-filterinput:-moz-any([filled],.filled) {
  background-color: var(--searchbox-background-color);
  border-color: var(--searchbox-border-color);
}

.devtools-style-searchbox-no-match {
  background-color: var(--searcbox-no-match-background-color) !important;
  border-color: var(--searcbox-no-match-border-color) !important;
}

.devtools-searchinput-clear {
  position: absolute;
  top: 3.5px;
  offset-inline-end: 7px;
  padding: 0;
  border: 0;
  width: 16px;
  height: 16px;
  background-position: 0 0;
  background-repeat: no-repeat;
  background-color: transparent;
}

.devtools-searchinput-clear:dir(rtl) {
  right: unset;
  left: 7px;
}

.theme-dark .devtools-searchinput-clear {
  background-image: url("chrome://devtools/skin/images/search-clear-dark.svg");
}

.theme-light .devtools-searchinput-clear {
  background-image: url("chrome://devtools/skin/images/search-clear-light.svg");
}

.devtools-style-searchbox-no-match + .devtools-searchinput-clear {
  background-image: url("chrome://devtools/skin/images/search-clear-failed.svg") !important;
}

.devtools-searchinput-clear:hover {
  background-position: -16px 0;
}

.theme-dark .devtools-searchinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear,
.theme-dark .devtools-filterinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear {
  list-style-image: url("chrome://devtools/skin/images/search-clear-dark.svg");
  -moz-image-region: rect(0, 16px, 16px, 0);
}

.theme-light .devtools-searchinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear,
.theme-light .devtools-filterinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear {
  list-style-image: url("chrome://devtools/skin/images/search-clear-light.svg");
  -moz-image-region: rect(0, 16px, 16px, 0);
}

.devtools-searchinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear,
.devtools-filterinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear {
  margin-bottom: 0;
}

.devtools-searchinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear:hover,
.devtools-filterinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear:hover {
  -moz-image-region: rect(0, 32px, 16px, 16px);
}

/* Twisty and checkbox controls */
.theme-twisty, .theme-checkbox {
  width: 14px;
  height: 14px;
  background-repeat: no-repeat;
  background-image: url("chrome://devtools/skin/images/controls.png");
  background-size: 56px 28px;
}

.theme-twisty {
  cursor: pointer;
  background-position: 0 -14px;
}

.theme-selected ~ .theme-twisty,
.theme-dark .theme-twisty {
  background-position: -28px -14px;
}

.theme-twisty:-moz-focusring {
  outline-style: none;
}

.theme-twisty[open], .theme-twisty.open {
  background-position: -14px -14px;
}

.theme-selected ~ .theme-twisty[open],
.theme-dark .theme-twisty[open], .theme-dark .theme-twisty.open  {
  background-position: -42px -14px;
}

.theme-twisty[invisible] {
  visibility: hidden;
}

/* Mirror the twisty for rtl direction */
.theme-twisty:dir(rtl),
.theme-twisty:-moz-locale-dir(rtl) {
  transform: scaleX(-1);
}

.theme-checkbox {
  display: inline-block;
  border: 0;
  padding: 0;
  outline: none;
  background-position: 0 0;
}

.theme-dark .theme-checkbox {
  background-position: -28px 0;
}

.theme-checkbox[checked] {
  background-position: -14px 0;
}

.theme-dark .theme-checkbox[checked] {
  background-position: -42px 0;
}

@media (min-resolution: 1.1dppx) {
  .theme-twisty, .theme-checkbox {
    background-image: url("chrome://devtools/skin/images/[email protected]");
  }
}

/* Throbbers */
.devtools-throbber::before {
  content: "";
  display: inline-block;
  vertical-align: bottom;
  margin-inline-end: 0.5em;
  width: 1em;
  height: 1em;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: 1.1s linear throbber-spin infinite;
}

@keyframes throbber-spin {
  from {
    transform: none;
  }
  to {
    transform: rotate(360deg);
  }
}

/* Common tabs styles */

.all-tabs-menu {
  position: absolute;

  top: 0;
  offset-inline-end: 0;
  width: 15px;
  height: 100%;

  border-inline-start: 1px solid var(--theme-splitter-color);

  background: var(--theme-tab-toolbar-background);
  background-image: url("chrome://devtools/skin/images/dropmarker.svg");
  background-repeat: no-repeat;
  background-position: center;
}

.devtools-tab-line {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 2px;
  background: transparent;
}

.devtools-tab:hover .devtools-tab-line,
.tabs-menu-item:hover .devtools-tab-line {
  background: var(--tab-line-hover-color);
}

.devtools-tab.selected .devtools-tab-line,
.tabs-menu-item.is-active .devtools-tab-line {
  background: var(--tab-line-selected-color);
}

/* Hide the tab line in the firebug theme */
.theme-firebug .devtools-tab-line {
  background: transparent !important;
}

/* No result message styles */

.devtools-sidepanel-no-result {
  font-style: italic;
  text-align: center;
  padding: 0.5em;
  -moz-user-select: none;
  font-size: 12px;
}
PK
!<��o��	�	<chrome/devtools/modules/devtools/client/themes/splitters.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 file is loaded by both browser.xul and toolbox.xul. Therefore, rules
   defined here can not rely on toolbox.xul variables. */

/* Splitters */

:root {
  /* Define the widths of the draggable areas on each side of a splitter. top
     and bottom widths are used for horizontal splitters, inline-start and
     inline-end for side splitters.*/

  --devtools-splitter-top-width: 2px;
  --devtools-splitter-bottom-width: 2px;

  /* Small draggable area on inline-start to avoid overlaps on scrollbars.*/
  --devtools-splitter-inline-start-width: 1px;
  --devtools-splitter-inline-end-width: 4px;
}

#appcontent[devtoolstheme="light"] {
  /* These variables are used in browser.xul but inside the toolbox they are overridden by --theme-splitter-color */
  --devtools-splitter-color: #dde1e4;
}

#appcontent[devtoolstheme="dark"] {
  --devtools-splitter-color: #42484f;
}

.devtools-horizontal-splitter,
.devtools-side-splitter {
  -moz-appearance: none;
  background-image: none;
  border: 0;
  border-style: solid;
  border-color: transparent;
  background-color: var(--devtools-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;
}

.devtools-horizontal-splitter {
  min-height: calc(var(--devtools-splitter-top-width) +
    var(--devtools-splitter-bottom-width) + 1px);

  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;
}

.devtools-side-splitter {
  min-width: calc(var(--devtools-splitter-inline-start-width) +
    var(--devtools-splitter-inline-end-width) + 1px);

  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;
}

.devtools-horizontal-splitter.disabled,
.devtools-side-splitter.disabled {
  pointer-events: none;
}
PK
!<+%����;chrome/devtools/modules/devtools/client/themes/toolbars.css/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* CSS Variables specific to the devtools toolbar that aren't defined by the themes */
.theme-light {
  --searchbox-background-color: var(--theme-highlight-yellow);
  --searchbox-border-color: #ffbf00;
  --searcbox-no-match-background-color: #ffe5e5;
  --searcbox-no-match-border-color: #e52e2e;

  --magnifying-glass-image: url(chrome://devtools/skin/images/search.svg);
  --filter-image: url(chrome://devtools/skin/images/filter.svg);
  --tool-options-image: url(chrome://devtools/skin/images/tool-options.svg);

  --separator-border-image: linear-gradient(transparent 4px, rgba(0,0,0,.1) 4px, rgba(0,0,0,.1) calc(100% - 4px), transparent calc(100% - 4px));
}

.theme-dark {
  --searchbox-background-color: #4d4222;
  --searchbox-border-color: #d99f2b;
  --searcbox-no-match-background-color: #402325;
  --searcbox-no-match-border-color: #cc3d3d;

  --magnifying-glass-image: url(chrome://devtools/skin/images/search.svg);
  --filter-image: url(chrome://devtools/skin/images/filter.svg);
  --tool-options-image: url(chrome://devtools/skin/images/tool-options.svg);

  --separator-border-image: linear-gradient(transparent 4px, rgba(100%,100%,100%,.2) 4px, rgba(100%,100%,100%,.2) calc(100% - 4px), transparent calc(100% - 4px));
}

.theme-firebug {
  --magnifying-glass-image: url(chrome://devtools/skin/images/search.svg);
  --tool-options-image: url(chrome://devtools/skin/images/firebug/tool-options.svg);
}


/* Toolbars */
.devtools-toolbar,
.devtools-sidebar-tabs tabs {
  -moz-appearance: none;
  padding: 0;
  border-width: 0;
  border-bottom-width: 1px;
  border-style: solid;
  height: 24px;
  line-height: 24px;
  box-sizing: border-box;
}

.devtools-toolbar {
  padding: 0 3px;
}

.devtools-toolbar checkbox {
  margin: 0 2px;
  padding: 0;
  line-height: -moz-block-height;
}

.devtools-toolbar checkbox .checkbox-check {
  margin: 0;
  padding: 0;
  vertical-align: bottom;
}

.devtools-toolbar checkbox .checkbox-label-box {
  border: none !important; /* overrides .checkbox-label-box from checkbox.css */
}

.devtools-toolbar checkbox .checkbox-label-box .checkbox-label {
  margin: 0 6px !important; /* overrides .checkbox-label from checkbox.css */
  padding: 0;
}

.devtools-toolbar-bottom {
  border-top-width: 1px;
  border-bottom: none;
}

.devtools-separator {
  border-inline-start: 1px solid;
  border-image: var(--separator-border-image) 1 1;
  height: inherit;
}

/* In-tools sidebar */
.devtools-sidebar-tabs {
  -moz-appearance: none;
  margin: 0;
  height: 100%;
}

.devtools-sidebar-tabs > tabpanels {
  -moz-appearance: none;
  background: transparent;
  padding: 0;
  border: 0;
}

.theme-light .devtools-sidebar-tabs > tabpanels {
  background: var(--theme-sidebar-background);
  color: var(--theme-body-color);
}

.devtools-sidebar-tabs tabs {
  position: static;
  font: inherit;
  margin-bottom: 0;
  overflow: hidden;
}

.devtools-sidebar-alltabs {
  -moz-appearance: none;
  height: 24px;
  line-height: 24px;
  padding: 0 4px;
  margin: 0;
  border-width: 0 0 1px 0;
  border-inline-start-width: 1px;
  border-style: solid;
}

.devtools-sidebar-alltabs .toolbarbutton-icon {
  display: none;
}

.devtools-sidebar-tabs tabs > .tabs-right,
.devtools-sidebar-tabs tabs > .tabs-left {
  display: none;
}

.devtools-sidebar-tabs tabs > tab {
  -moz-appearance: none;
  /* We want to match the height of a toolbar with a toolbarbutton
   * First, we need to replicated the padding of toolbar (4px),
   * then we need to take the border of the buttons into account (1px).
   */
  padding: 0 3px;
  margin: 0;
  min-width: 78px;
  text-align: center;
  background-color: transparent;
  color: inherit;
  -moz-box-flex: 1;
  border-width: 0;
  border-inline-start-width: 1px;
  border-style: solid;
  border-radius: 0;
  position: static;
  text-shadow: none;
}

.devtools-sidebar-tabs tabs > tab {
  border-image: linear-gradient(transparent 15%, var(--theme-splitter-color) 15%, var(--theme-splitter-color) 85%, transparent 85%) 1 1;
}

.devtools-sidebar-tabs tabs > tab[selected],
.devtools-sidebar-tabs tabs > tab[selected] + tab {
  border-image: linear-gradient(var(--theme-splitter-color), var(--theme-splitter-color)) 1 1;
}

.devtools-sidebar-tabs tabs > tab:first-child {
  border-inline-start-width: 0;
}

.devtools-sidebar-tabs tabs > tab:hover {
  background: rgba(0, 0, 0, 0.12);
}

.devtools-sidebar-tabs tabs > tab:hover:active {
  background: rgba(0, 0, 0, 0.2);
}

.devtools-sidebar-tabs tabs > tab[selected],
.devtools-sidebar-tabs tabs > tab[selected]:hover:active {
  color: var(--theme-selection-color);
  background: var(--theme-selection-background);
}

/*
 * Apply the default fill color to toolbar icons
 */
.devtools-toolbarbutton > image,
.devtools-button::before,
.scrollbutton-up > .toolbarbutton-icon,
.scrollbutton-down > .toolbarbutton-icon,
#black-boxed-message-button .button-icon,
#canvas-debugging-empty-notice-button .button-icon,
#toggle-breakpoints[checked] > image,
.event-tooltip-debugger-icon {
  -moz-context-properties: fill;
  fill: var(--theme-toolbar-color);
}

.hidden-labels-box:not(.visible) > label,
.hidden-labels-box.visible ~ .hidden-labels-box > label:last-child {
  display: none;
}

.devtools-invisible-splitter {
  border-color: transparent;
  background-color: transparent;
}

.devtools-horizontal-splitter,
.devtools-side-splitter {
  background-color: var(--theme-splitter-color);
}
PK
!<xQ�--<chrome/devtools/modules/devtools/client/themes/variables.css/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* Variable declarations for light and dark devtools themes.
 * Colors are taken from:
 * https://developer.mozilla.org/en-US/docs/Tools/DevToolsColors.
 */

/*
 * IMPORTANT NOTE
 * 1. Changes should be kept in sync with commandline.css and commandline-browser.css.
 *
 * 2. This file is parsed in js (see client/shared/theme.js)
 * so the formatting should be consistent (i.e. no '}' inside a rule).
 */

:root.theme-light {
  --theme-body-background: white;
  --theme-sidebar-background: white;
  --theme-contrast-background: #e6b064;

  /* Toolbar */
  --theme-tab-toolbar-background: var(--grey-10);
  --theme-toolbar-background: var(--grey-10);
  --theme-toolbar-color: var(--grey-90);
  --theme-toolbar-selected-color: var(--blue-60);
  --theme-toolbar-checked-color: var(--blue-60);
  --theme-toolbar-highlighted-color: var(--green-60);
  --theme-toolbar-background-hover: rgba(221, 225, 228, 0.66);
  --theme-toolbar-background-alt: #f5f5f5;
  --theme-toolbar-hover: var(--grey-20);
  --theme-toolbar-hover-active: var(--grey-20);

  /* Selection */
  --theme-selection-background: var(--blue-55);
  --theme-selection-background-hover: #F0F9FE;
  --theme-selection-color: #ffffff;

  /* Border color that splits the toolbars/panels/headers.
   * This needs to be sync with commandline.css and commandline-browser.css. */
  --theme-splitter-color: #e0e0e1;

  --theme-comment: var(--grey-50);
  --theme-comment-alt: #ccd1d5;

  --theme-body-color: var(--grey-60);
  --theme-body-color-alt: var(--grey-40);
  --theme-body-color-inactive: #999797;
  --theme-content-color1: #292e33;
  --theme-content-color2: #8fa1b2;
  --theme-content-color3: #667380;

  --theme-highlight-green: var(--green-70);
  --theme-highlight-blue: var(--blue-55);
  --theme-highlight-purple: var(--blue-70);
  --theme-highlight-red: var(--magenta-65);
  --theme-highlight-yellow: #FFF89E;

  /* These theme-highlight color variables have not been photonized. */
  --theme-highlight-bluegrey: #0072ab;
  --theme-highlight-lightorange: #d97e00;
  --theme-highlight-orange: #f13c00;
  --theme-highlight-pink: #b82ee5;
  --theme-highlight-gray: #dde1e4;

  /* For accessibility purposes we want to enhance the focus styling. This
   * should improve keyboard navigation usability. */
  --theme-focus-outline-color: #000000;

  /* Colors used in Graphs, like performance tools. Similar colors to Chrome's timeline. */
  --theme-graphs-green: #85d175;
  --theme-graphs-blue: #83b7f6;
  --theme-graphs-bluegrey: #0072ab;
  --theme-graphs-purple: #b693eb;
  --theme-graphs-yellow: #efc052;
  --theme-graphs-orange: #d97e00;
  --theme-graphs-red: #e57180;
  --theme-graphs-grey: #cccccc;
  --theme-graphs-full-red: #f00;
  --theme-graphs-full-blue: #00f;

  /* Images */
  --theme-pane-collapse-image: url(chrome://devtools/skin/images/pane-collapse.svg);
  --theme-pane-expand-image: url(chrome://devtools/skin/images/pane-expand.svg);

  /* Icon filters */
  --theme-icon-checked-filter: url(chrome://devtools/skin/images/filters.svg#icon-checked-light);

  /* Tooltips */
  --theme-tooltip-border: #d9e1e8;
  --theme-tooltip-background: rgba(255, 255, 255, .9);
  --theme-tooltip-shadow: rgba(155, 155, 155, 0.26);

  /* Command line */
  --theme-command-line-image: url(chrome://devtools/skin/images/commandline-icon.svg#light-theme);
  --theme-command-line-image-focus: url(chrome://devtools/skin/images/commandline-icon.svg#light-theme-focus);

  --theme-codemirror-gutter-background: #f4f4f4;
  --theme-messageCloseButtonFilter: invert(0);
}

:root.theme-dark {
  --theme-body-background: var(--grey-80);
  --theme-sidebar-background: #1B1B1D;
  --theme-contrast-background: #ffb35b;

  /* Toolbar */
  --theme-tab-toolbar-background: var(--grey-90);
  --theme-toolbar-background: #1B1B1D;
  --theme-toolbar-color: var(--grey-40);
  --theme-toolbar-selected-color: white;
  --theme-toolbar-checked-color: #75BFFF;
  --theme-toolbar-highlighted-color: var(--green-50);
  --theme-toolbar-background-hover: #20232B;
  --theme-toolbar-background-alt: #1B1B1D;
  --theme-toolbar-hover: #252526;
  --theme-toolbar-hover-active: #252526;

  /* Selection */
  --theme-selection-background: #204E8A;
  --theme-selection-background-hover: #353B48;
  --theme-selection-color: #ffffff;

  /* Border color that splits the toolbars/panels/headers.
   * This needs to be sync with commandline.css and commandline-browser.css. */
  --theme-splitter-color: #3c3c3d;

  --theme-comment: #939393;
  --theme-comment-alt: #939393;

  --theme-body-color: #909090;
  --theme-body-color-alt: var(--grey-50);
  --theme-body-color-inactive: var(--grey-40);
  --theme-content-color1: var(--grey-30);
  --theme-content-color2: var(--grey-40);
  --theme-content-color3: #58575c;

  --theme-highlight-green: #86DE74;
  --theme-highlight-blue: #75BFFF;
  --theme-highlight-purple: #B98EFF;
  --theme-highlight-red: #FF7DE9;
  --theme-highlight-yellow: #FFF89E;

  /* These theme-highlight color variables have not been photonized. */
  --theme-highlight-bluegrey: #5e88b0;
  --theme-highlight-lightorange: #d99b28;
  --theme-highlight-orange: #d96629;
  --theme-highlight-pink: #df80ff;
  --theme-highlight-gray: #e9f4fe;

  /* For accessibility purposes we want to enhance the focus styling. This
   * should improve keyboard navigation usability. */
  --theme-focus-outline-color: #ced3d9;

  /* Colors used in Graphs, like performance tools. Mostly similar to some "highlight-*" colors. */
  --theme-graphs-green: #70bf53;
  --theme-graphs-blue: #46afe3;
  --theme-graphs-bluegrey: #5e88b0;
  --theme-graphs-purple: #df80ff;
  --theme-graphs-yellow: #d99b28;
  --theme-graphs-orange: #d96629;
  --theme-graphs-red: #eb5368;
  --theme-graphs-grey: #757873;
  --theme-graphs-full-red: #f00;
  --theme-graphs-full-blue: #00f;

  /* Images */
  --theme-pane-collapse-image: url(chrome://devtools/skin/images/pane-collapse.svg);
  --theme-pane-expand-image: url(chrome://devtools/skin/images/pane-expand.svg);

  /* Icon filters */
  --theme-icon-checked-filter: url(chrome://devtools/skin/images/filters.svg#icon-checked-dark);

  /* Tooltips */
  --theme-tooltip-border: #434850;
  --theme-tooltip-background: rgba(19, 28, 38, .9);
  --theme-tooltip-shadow: rgba(25, 25, 25, 0.76);

  /* Command line */
  --theme-command-line-image: url(chrome://devtools/skin/images/commandline-icon.svg#dark-theme);
  --theme-command-line-image-focus: url(chrome://devtools/skin/images/commandline-icon.svg#dark-theme-focus);

  --theme-codemirror-gutter-background: #262b37;
  --theme-messageCloseButtonFilter: invert(1);
}

:root.theme-firebug {
  --theme-body-background: #fff;
  --theme-sidebar-background: #fcfcfc;
  --theme-contrast-background: #e6b064;

  --theme-tab-toolbar-background: rgb(240, 240, 240) linear-gradient(rgba(255, 255, 255, 0.8), transparent);
  --theme-toolbar-background: rgb(240, 240, 240) linear-gradient(rgba(255, 255, 255, 0.8), transparent);
  --theme-toolbar-tab-selected-background: rgb(253, 253, 253);
  --theme-selection-background: #3399ff;
  --theme-selection-background-hover: rgba(128,128,128,0.2);
  --theme-selection-color: white;
  --theme-splitter-color: #bfbfbf;
  --theme-comment: darkgreen;

  --theme-body-color: #252525;
  --theme-body-color-alt: #585959;
  --theme-content-color1: #292e33;
  --theme-content-color2: #8fa1b2;
  --theme-content-color3: #667380;

  --theme-highlight-green: #2cbb0f;
  --theme-highlight-blue: #3455db;
  --theme-highlight-bluegrey: #0072ab;
  --theme-highlight-purple: #000080;
  --theme-highlight-lightorange: #d97e00;
  --theme-highlight-orange: #f13c00;
  --theme-highlight-red: #f00;
  --theme-highlight-pink: #b82ee5;
  --theme-highlight-gray: #dde1e4;

  /* Colors used in Graphs, like performance tools. Similar colors to Chrome's timeline. */
  --theme-graphs-green: #85d175;
  --theme-graphs-blue: #83b7f6;
  --theme-graphs-bluegrey: #0072ab;
  --theme-graphs-purple: #b693eb;
  --theme-graphs-yellow: #efc052;
  --theme-graphs-orange: #d97e00;
  --theme-graphs-red: #e57180;
  --theme-graphs-grey: #cccccc;
  --theme-graphs-full-red: #f00;
  --theme-graphs-full-blue: #00f;

  /* Images */
  --theme-pane-collapse-image: url(chrome://devtools/skin/images/firebug/pane-collapse.svg);
  --theme-pane-expand-image: url(chrome://devtools/skin/images/firebug/pane-expand.svg);

  /* Icon filters */
  --theme-icon-checked-filter: url(chrome://devtools/skin/images/filters.svg#icon-checked-light);

  /* Font size */
  --theme-toolbar-font-size: 12px;

  /* Header */
  --theme-header-background: #F0F0F0 linear-gradient(to top,
                                                     rgba(0, 0, 0, 0.1),
                                                     transparent) repeat-x;

  /* Command line */
  --theme-command-line-image: url(chrome://devtools/skin/images/firebug/commandline-icon.svg);
  --theme-command-line-image-focus: url(chrome://devtools/skin/images/firebug/commandline-icon.svg#focus);

  /* Toolbar buttons */
  --toolbarbutton-background: transparent linear-gradient(rgba(255, 255, 255, 0.4), rgba(255, 255, 255, 0.2)) no-repeat;
  --toolbarbutton-hover-background: transparent;
  --toolbarbutton-hover-border-color: var(--theme-splitter-color);
  --toolbarbutton-checked-background: linear-gradient(rgba(0, 0, 0, 0.1), transparent);
  --toolbarbutton-checked-color: var(--theme-body-color);
  --toolbarbutton-checked-border-color: var(--toolbarbutton-hover-border-color);

  --theme-codemirror-gutter-background: #ebeced;
}

:root.theme-firebug[platform="win"] {
  --theme-tab-toolbar-background: #d8eaf9 linear-gradient(rgba(253, 253, 253, 0.2), rgba(253, 253, 253, 0));
  --theme-toolbar-background: #d8eaf9 linear-gradient(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0.2));
  --theme-toolbar-tab-selected-background: rgb(247, 251, 254);
  --theme-splitter-color: #aabccf;
}

:root {
  --theme-focus-border-color-textbox: #0675d3;
  --theme-textbox-box-shadow: rgba(97,181,255,.75);

  /* For accessibility purposes we want to enhance the focus styling. This
   * should improve keyboard navigation usability. */
  --theme-focus-outline: 1px dotted var(--theme-focus-outline-color);
  --theme-focus-box-shadow-textbox: 0 0 0 1px var(--theme-textbox-box-shadow);

  --toolbarbutton-background: var(--theme-toolbar-hover);
  --toolbarbutton-border-color: transparent;
  --toolbarbutton-hover-background: rgba(110,120,130,0.2);
  --toolbarbutton-hover-border-color: var(--toolbarbutton-border-color);
  --toolbarbutton-checked-background: var(--theme-selection-background);
  --toolbarbutton-checked-color: var(--theme-selection-color);
  --toolbarbutton-checked-border-color: var(--toolbarbutton-border-color);

  /* The photon animation curve */
  --animation-curve: cubic-bezier(.07,.95,0,1);

  /* Firefox Colors CSS Variables v1.0.3
   * Colors are taken from: https://github.com/FirefoxUX/design-tokens */
  --magenta-65: #dd00a9;

  --purple-60: #8000d7;

  --blue-50: #0a84ff;
  --blue-55: #0074e8;
  --blue-60: #0060df;
  --blue-70: #003eaa;

  --red-70: #a4000f;

  --green-50: #30e60b;
  --green-60: #12bc00;
  --green-70: #058b00;

  --yellow-80: #715100;

  --grey-10: #f9f9fa;
  --grey-20: #ededf0;
  --grey-30: #d7d7db;
  --grey-40: #b1b1b3;
  --grey-50: #737373;
  --grey-60: #4a4a4f;
  --grey-70: #38383d;
  --grey-80: #2a2a2e;
  --grey-90: #0c0c0d;
}
PK
!<�<]�?chrome/devtools/modules/devtools/client/webaudioeditor/panel.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 { Cc, Ci, Cu, Cr } = require("chrome");
const EventEmitter = require("devtools/shared/event-emitter");
const { WebAudioFront } = require("devtools/shared/fronts/webaudio");

function WebAudioEditorPanel(iframeWindow, toolbox) {
  this.panelWin = iframeWindow;
  this._toolbox = toolbox;
  this._destroyer = null;

  EventEmitter.decorate(this);
}

exports.WebAudioEditorPanel = WebAudioEditorPanel;

WebAudioEditorPanel.prototype = {
  open: function () {
    let targetPromise;

    // Local debugging needs to make the target remote.
    if (!this.target.isRemote) {
      targetPromise = this.target.makeRemote();
    } else {
      targetPromise = Promise.resolve(this.target);
    }

    return targetPromise
      .then(() => {
        this.panelWin.gToolbox = this._toolbox;
        this.panelWin.gTarget = this.target;

        this.panelWin.gFront = new WebAudioFront(this.target.client, this.target.form);
        return this.panelWin.startupWebAudioEditor();
      })
      .then(() => {
        this.isReady = true;
        this.emit("ready");
        return this;
      })
      .catch(function onError(aReason) {
        console.error("WebAudioEditorPanel open failed. " +
                      aReason.error + ": " + aReason.message);
      });
  },

  // DevToolPanel API

  get target() {
    return this._toolbox.target;
  },

  destroy: function () {
    // Make sure this panel is not already destroyed.
    if (this._destroyer) {
      return this._destroyer;
    }

    return this._destroyer = this.panelWin.shutdownWebAudioEditor().then(() => {
      // Destroy front to ensure packet handler is removed from client
      this.panelWin.gFront.destroy();
      this.emit("destroyed");
    });
  }
};
PK
!<��`�Fchrome/devtools/modules/devtools/client/webconsole/console-commands.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft= javascript ts=2 et sw=2 tw=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 l10n = require("gcli/l10n");
loader.lazyRequireGetter(this, "gDevTools",
                         "devtools/client/framework/devtools", true);

exports.items = [
  {
    item: "command",
    runAt: "client",
    name: "splitconsole",
    hidden: true,
    buttonId: "command-button-splitconsole",
    buttonClass: "command-button",
    tooltipText: l10n.lookupFormat("splitconsoleTooltip2", ["Esc"]),
    isRemoteSafe: true,
    state: {
      isChecked: function (target) {
        let toolbox = gDevTools.getToolbox(target);
        return !!(toolbox && toolbox.splitConsole);
      },
      onChange: function (target, changeHandler) {
        // Register handlers for when a change event should be fired
        // (which resets the checked state of the button).
        let toolbox = gDevTools.getToolbox(target);
        let callback = changeHandler.bind(null, "changed", { target: target });

        if (!toolbox) {
          return;
        }

        toolbox.on("split-console", callback);
        toolbox.once("destroyed", () => {
          toolbox.off("split-console", callback);
        });
      }
    },
    exec: function (args, context) {
      let target = context.environment.target;
      let toolbox = gDevTools.getToolbox(target);

      if (!toolbox) {
        return gDevTools.showToolbox(target, "inspector").then((newToolbox) => {
          newToolbox.toggleSplitConsole();
        });
      }
      return toolbox.toggleSplitConsole();
    }
  },
  {
    name: "console",
    description: l10n.lookup("consoleDesc"),
    manual: l10n.lookup("consoleManual")
  },
  {
    item: "command",
    runAt: "client",
    name: "console clear",
    description: l10n.lookup("consoleclearDesc"),
    exec: function (args, context) {
      let toolbox = gDevTools.getToolbox(context.environment.target);
      if (toolbox == null) {
        return null;
      }

      let panel = toolbox.getPanel("webconsole");
      if (panel == null) {
        return null;
      }

      let onceMessagesCleared = panel.hud.jsterm.once("messages-cleared");
      panel.hud.jsterm.clearOutput();
      return onceMessagesCleared;
    }
  },
  {
    item: "command",
    runAt: "client",
    name: "console close",
    description: l10n.lookup("consolecloseDesc"),
    exec: function (args, context) {
      // Don't return a value to GCLI
      return gDevTools.closeToolbox(context.environment.target).then(() => {});
    }
  },
  {
    item: "command",
    runAt: "client",
    name: "console open",
    description: l10n.lookup("consoleopenDesc"),
    exec: function (args, context) {
      const target = context.environment.target;
      // Don't return a value to GCLI
      return gDevTools.showToolbox(target, "webconsole").then(() => {});
    }
  }
];
PK
!<���@�@�Dchrome/devtools/modules/devtools/client/webconsole/console-output.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 {Ci} = require("chrome");

loader.lazyImporter(this, "VariablesView", "resource://devtools/client/shared/widgets/VariablesView.jsm");
loader.lazyImporter(this, "escapeHTML", "resource://devtools/client/shared/widgets/VariablesView.jsm");

loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
loader.lazyRequireGetter(this, "TableWidget", "devtools/client/shared/widgets/TableWidget", true);
loader.lazyRequireGetter(this, "ObjectClient", "devtools/shared/client/object-client");

const XHTML_NS = "http://www.w3.org/1999/xhtml";

const WebConsoleUtils = require("devtools/client/webconsole/utils").Utils;
const { getSourceNames } = require("devtools/client/shared/source-utils");
const {Task} = require("devtools/shared/task");
const l10n = require("devtools/client/webconsole/webconsole-l10n");
const nodeConstants = require("devtools/shared/dom-node-constants");
const {PluralForm} = require("devtools/shared/plural-form");
const {extend} = require("devtools/shared/extend");
const defer = require("devtools/shared/defer");

const MAX_STRING_GRIP_LENGTH = 36;
const {ELLIPSIS} = require("devtools/shared/l10n");

const validProtocols = /^(http|https|ftp|resource|chrome):/i;

// Constants for compatibility with the Web Console output implementation before
// bug 778766.
// TODO: remove these once bug 778766 is fixed.
const COMPAT = {
  // The various categories of messages.
  CATEGORIES: {
    NETWORK: 0,
    CSS: 1,
    JS: 2,
    WEBDEV: 3,
    INPUT: 4,
    OUTPUT: 5,
    SECURITY: 6,
    SERVER: 7,
  },

  // The possible message severities.
  SEVERITIES: {
    ERROR: 0,
    WARNING: 1,
    INFO: 2,
    LOG: 3,
  },

  // The preference keys to use for each category/severity combination, indexed
  // first by category (rows) and then by severity (columns).
  //
  // Most of these rather idiosyncratic names are historical and predate the
  // division of message type into "category" and "severity".
  /* eslint-disable no-multi-spaces */
  /* eslint-disable max-len */
  PREFERENCE_KEYS: [
    // Error         Warning       Info          Log
    [ "network",     "netwarn",    null,         "networkinfo", ],  // Network
    [ "csserror",    "cssparser",  null,         null,          ],  // CSS
    [ "exception",   "jswarn",     null,         "jslog",       ],  // JS
    [ "error",       "warn",       "info",       "log",         ],  // Web Developer
    [ null,          null,         null,         null,          ],  // Input
    [ null,          null,         null,         null,          ],  // Output
    [ "secerror",    "secwarn",    null,         null,          ],  // Security
    [ "servererror", "serverwarn", "serverinfo", "serverlog",   ],  // Server Logging
  ],
  /* eslint-enable max-len */
  /* eslint-enable no-multi-spaces */

  // The fragment of a CSS class name that identifies each category.
  CATEGORY_CLASS_FRAGMENTS: [ "network", "cssparser", "exception", "console",
                              "input", "output", "security", "server" ],

  // The fragment of a CSS class name that identifies each severity.
  SEVERITY_CLASS_FRAGMENTS: [ "error", "warn", "info", "log" ],

  // The indent of a console group in pixels.
  GROUP_INDENT: 12,
};

// A map from the console API call levels to the Web Console severities.
const CONSOLE_API_LEVELS_TO_SEVERITIES = {
  error: "error",
  exception: "error",
  assert: "error",
  warn: "warning",
  info: "info",
  log: "log",
  clear: "log",
  trace: "log",
  table: "log",
  debug: "log",
  dir: "log",
  dirxml: "log",
  group: "log",
  groupCollapsed: "log",
  groupEnd: "log",
  time: "log",
  timeEnd: "log",
  count: "log"
};

// Array of known message source URLs we need to hide from output.
const IGNORED_SOURCE_URLS = ["debugger eval code"];

// The maximum length of strings to be displayed by the Web Console.
const MAX_LONG_STRING_LENGTH = 200000;

// Regular expression that matches the allowed CSS property names when using
// the `window.console` API.
const RE_ALLOWED_STYLES = new RegExp(["^(?:-moz-)?(?:background|border|box|clear|" +
                                      "color|cursor|display|float|font|line|margin|" +
                                      "padding|text|transition|outline|white-space|" +
                                      "word|writing|(?:min-|max-)?width|" +
                                      "(?:min-|max-)?height)"]);

// Regular expressions to search and replace with 'notallowed' in the styles
// given to the `window.console` API methods.
const RE_CLEANUP_STYLES = [
  // url(), -moz-element()
  /\b(?:url|(?:-moz-)?element)[\s('"]+/gi,

  // various URL protocols
  /['"(]*(?:chrome|resource|about|app|data|https?|ftp|file):+\/*/gi,
];

// Maximum number of rows to display in console.table().
const TABLE_ROW_MAX_ITEMS = 1000;

// Maximum number of columns to display in console.table().
const TABLE_COLUMN_MAX_ITEMS = 10;

/**
 * The ConsoleOutput object is used to manage output of messages in the Web
 * Console.
 *
 * @constructor
 * @param object owner
 *        The console output owner. This usually the WebConsoleFrame instance.
 *        Any other object can be used, as long as it has the following
 *        properties and methods:
 *          - window
 *          - document
 *          - outputMessage(category, methodOrNode[, methodArguments])
 *            TODO: this is needed temporarily, until bug 778766 is fixed.
 */
function ConsoleOutput(owner) {
  this.owner = owner;
  this._onFlushOutputMessage = this._onFlushOutputMessage.bind(this);
}

ConsoleOutput.prototype = {
  _dummyElement: null,

  /**
   * The output container.
   * @type DOMElement
   */
  get element() {
    return this.owner.outputNode;
  },

  /**
   * The document that holds the output.
   * @type DOMDocument
   */
  get document() {
    return this.owner ? this.owner.document : null;
  },

  /**
   * The DOM window that holds the output.
   * @type Window
   */
  get window() {
    return this.owner.window;
  },

  /**
   * Getter for the debugger WebConsoleClient.
   * @type object
   */
  get webConsoleClient() {
    return this.owner.webConsoleClient;
  },

  /**
   * Getter for the current toolbox debuggee target.
   * @type Target
   */
  get toolboxTarget() {
    return this.owner.owner.target;
  },

  /**
   * Release an actor.
   *
   * @private
   * @param string actorId
   *        The actor ID you want to release.
   */
  _releaseObject: function (actorId) {
    this.owner._releaseObject(actorId);
  },

  /**
   * Add a message to output.
   *
   * @param object ...args
   *        Any number of Message objects.
   * @return this
   */
  addMessage: function (...args) {
    for (let msg of args) {
      msg.init(this);
      this.owner.outputMessage(msg._categoryCompat, this._onFlushOutputMessage,
                               [msg]);
    }
    return this;
  },

  /**
   * Message renderer used for compatibility with the current Web Console output
   * implementation. This method is invoked for every message object that is
   * flushed to output. The message object is initialized and rendered, then it
   * is displayed.
   *
   * TODO: remove this method once bug 778766 is fixed.
   *
   * @private
   * @param object message
   *        The message object to render.
   * @return DOMElement
   *         The message DOM element that can be added to the console output.
   */
  _onFlushOutputMessage: function (message) {
    return message.render().element;
  },

  /**
   * Get an array of selected messages. This list is based on the text selection
   * start and end points.
   *
   * @param number [limit]
   *        Optional limit of selected messages you want. If no value is given,
   *        all of the selected messages are returned.
   * @return array
   *         Array of DOM elements for each message that is currently selected.
   */
  getSelectedMessages: function (limit) {
    let selection = this.window.getSelection();
    if (selection.isCollapsed) {
      return [];
    }

    if (selection.containsNode(this.element, true)) {
      return Array.slice(this.element.children);
    }

    let anchor = this.getMessageForElement(selection.anchorNode);
    let focus = this.getMessageForElement(selection.focusNode);
    if (!anchor || !focus) {
      return [];
    }

    let start, end;
    if (anchor.timestamp > focus.timestamp) {
      start = focus;
      end = anchor;
    } else {
      start = anchor;
      end = focus;
    }

    let result = [];
    let current = start;
    while (current) {
      result.push(current);
      if (current == end || (limit && result.length == limit)) {
        break;
      }
      current = current.nextSibling;
    }
    return result;
  },

  /**
   * Find the DOM element of a message for any given descendant.
   *
   * @param DOMElement elem
   *        The element to start the search from.
   * @return DOMElement|null
   *         The DOM element of the message, if any.
   */
  getMessageForElement: function (elem) {
    while (elem && elem.parentNode) {
      if (elem.classList && elem.classList.contains("message")) {
        return elem;
      }
      elem = elem.parentNode;
    }
    return null;
  },

  /**
   * Select all messages.
   */
  selectAllMessages: function () {
    let selection = this.window.getSelection();
    selection.removeAllRanges();
    let range = this.document.createRange();
    range.selectNodeContents(this.element);
    selection.addRange(range);
  },

  /**
   * Add a message to the selection.
   *
   * @param DOMElement elem
   *        The message element to select.
   */
  selectMessage: function (elem) {
    let selection = this.window.getSelection();
    selection.removeAllRanges();
    let range = this.document.createRange();
    range.selectNodeContents(elem);
    selection.addRange(range);
  },

  /**
   * Open an URL in a new tab.
   * @see WebConsole.openLink() in hudservice.js
   */
  openLink: function () {
    this.owner.owner.openLink.apply(this.owner.owner, arguments);
  },

  openLocationInDebugger: function ({url, line}) {
    return this.owner.owner.viewSourceInDebugger(url, line);
  },

  /**
   * Open the variables view to inspect an object actor.
   * @see JSTerm.openVariablesView() in webconsole.js
   */
  openVariablesView: function () {
    this.owner.jsterm.openVariablesView.apply(this.owner.jsterm, arguments);
  },

  /**
   * Destroy this ConsoleOutput instance.
   */
  destroy: function () {
    this._dummyElement = null;
    this.owner = null;
  },
}; // ConsoleOutput.prototype

/**
 * Message objects container.
 * @type object
 */
var Messages = {};

/**
 * The BaseMessage object is used for all types of messages. Every kind of
 * message should use this object as its base.
 *
 * @constructor
 */
Messages.BaseMessage = function () {
  this.widgets = new Set();
  this._onClickAnchor = this._onClickAnchor.bind(this);
  this._repeatID = { uid: gSequenceId() };
  this.textContent = "";
};

Messages.BaseMessage.prototype = {
  /**
   * Reference to the ConsoleOutput owner.
   *
   * @type object|null
   *       This is |null| if the message is not yet initialized.
   */
  output: null,

  /**
   * Reference to the parent message object, if this message is in a group or if
   * it is otherwise owned by another message.
   *
   * @type object|null
   */
  parent: null,

  /**
   * Message DOM element.
   *
   * @type DOMElement|null
   *       This is |null| if the message is not yet rendered.
   */
  element: null,

  /**
   * Tells if this message is visible or not.
   * @type boolean
   */
  get visible() {
    return this.element && this.element.parentNode;
  },

  /**
   * The owner DOM document.
   * @type DOMElement
   */
  get document() {
    return this.output.document;
  },

  /**
   * Holds the text-only representation of the message.
   * @type string
   */
  textContent: null,

  /**
   * Set of widgets included in this message.
   * @type Set
   */
  widgets: null,

  // Properties that allow compatibility with the current Web Console output
  // implementation.
  _categoryCompat: null,
  _severityCompat: null,
  _categoryNameCompat: null,
  _severityNameCompat: null,
  _filterKeyCompat: null,

  /**
   * Object that is JSON-ified and used as a non-unique ID for tracking
   * duplicate messages.
   * @private
   * @type object
   */
  _repeatID: null,

  /**
   * Initialize the message.
   *
   * @param object output
   *        The ConsoleOutput owner.
   * @param object [parent=null]
   *        Optional: a different message object that owns this instance.
   * @return this
   */
  init: function (output, parent = null) {
    this.output = output;
    this.parent = parent;
    return this;
  },

  /**
   * Non-unique ID for this message object used for tracking duplicate messages.
   * Different message kinds can identify themselves based their own criteria.
   *
   * @return string
   */
  getRepeatID: function () {
    return JSON.stringify(this._repeatID);
  },

  /**
   * Render the message. After this method is invoked the |element| property
   * will point to the DOM element of this message.
   * @return this
   */
  render: function () {
    if (!this.element) {
      this.element = this._renderCompat();
    }
    return this;
  },

  /**
   * Prepare the message container for the Web Console, such that it is
   * compatible with the current implementation.
   * TODO: remove this once bug 778766 is fixed.
   *
   * @private
   * @return Element
   *         The DOM element that wraps the message.
   */
  _renderCompat: function () {
    let doc = this.output.document;
    let container = doc.createElementNS(XHTML_NS, "div");
    container.id = "console-msg-" + gSequenceId();
    container.className = "message";
    if (this.category == "input") {
      // Assistive technology tools shouldn't echo input to the user,
      // as the user knows what they've just typed.
      container.setAttribute("aria-live", "off");
    }
    container.category = this._categoryCompat;
    container.severity = this._severityCompat;
    container.setAttribute("category", this._categoryNameCompat);
    container.setAttribute("severity", this._severityNameCompat);
    container.setAttribute("filter", this._filterKeyCompat);
    container.clipboardText = this.textContent;
    container.timestamp = this.timestamp;
    container._messageObject = this;

    return container;
  },

  /**
   * Add a click callback to a given DOM element.
   *
   * @private
   * @param Element element
   *        The DOM element to which you want to add a click event handler.
   * @param function [callback=this._onClickAnchor]
   *        Optional click event handler. The default event handler is
   *        |this._onClickAnchor|.
   */
  _addLinkCallback: function (element, callback = this._onClickAnchor) {
    // This is going into the WebConsoleFrame object instance that owns
    // the ConsoleOutput object. The WebConsoleFrame owner is the WebConsole
    // object instance from hudservice.js.
    // TODO: move _addMessageLinkCallback() into ConsoleOutput once bug 778766
    // is fixed.
    this.output.owner._addMessageLinkCallback(element, callback);
  },

  /**
   * The default |click| event handler for links in the output. This function
   * opens the anchor's link in a new tab.
   *
   * @private
   * @param Event event
   *        The DOM event that invoked this function.
   */
  _onClickAnchor: function (event) {
    this.output.openLink(event.target.href);
  },

  destroy: function () {
    // Destroy all widgets that have registered themselves in this.widgets
    for (let widget of this.widgets) {
      widget.destroy();
    }
    this.widgets.clear();
  }
};

/**
 * The NavigationMarker is used to show a page load event.
 *
 * @constructor
 * @extends Messages.BaseMessage
 * @param object response
 *        The response received from the back end.
 * @param number timestamp
 *        The message date and time, milliseconds elapsed since 1 January 1970
 *        00:00:00 UTC.
 */
Messages.NavigationMarker = function (response, timestamp) {
  Messages.BaseMessage.call(this);

  // Store the response packet received from the server. It might
  // be useful for extensions customizing the console output.
  this.response = response;
  this._url = response.url;
  this.textContent = "------ " + this._url;
  this.timestamp = timestamp;
};

Messages.NavigationMarker.prototype = extend(Messages.BaseMessage.prototype, {
  /**
   * The address of the loading page.
   * @private
   * @type string
   */
  _url: null,

  /**
   * Message timestamp.
   *
   * @type number
   *       Milliseconds elapsed since 1 January 1970 00:00:00 UTC.
   */
  timestamp: 0,

  _categoryCompat: COMPAT.CATEGORIES.NETWORK,
  _severityCompat: COMPAT.SEVERITIES.LOG,
  _categoryNameCompat: "network",
  _severityNameCompat: "info",
  _filterKeyCompat: "networkinfo",

  /**
   * Prepare the DOM element for this message.
   * @return this
   */
  render: function () {
    if (this.element) {
      return this;
    }

    let url = this._url;
    let pos = url.indexOf("?");
    if (pos > -1) {
      url = url.substr(0, pos);
    }

    let doc = this.output.document;
    let urlnode = doc.createElementNS(XHTML_NS, "a");
    urlnode.className = "url";
    urlnode.textContent = url;
    urlnode.title = this._url;
    urlnode.href = this._url;
    urlnode.draggable = false;
    this._addLinkCallback(urlnode);

    let render = Messages.BaseMessage.prototype.render.bind(this);
    render().element.appendChild(urlnode);
    this.element.classList.add("navigation-marker");
    this.element.url = this._url;
    this.element.appendChild(doc.createTextNode("\n"));

    return this;
  },
});

/**
 * The Simple message is used to show any basic message in the Web Console.
 *
 * @constructor
 * @extends Messages.BaseMessage
 * @param string|Node|function message
 *        The message to display.
 * @param object [options]
 *        Options for this message:
 *        - category: (string) category that this message belongs to. Defaults
 *        to no category.
 *        - severity: (string) severity of the message. Defaults to no severity.
 *        - timestamp: (number) date and time when the message was recorded.
 *        Defaults to |Date.now()|.
 *        - link: (string) if provided, the message will be wrapped in an anchor
 *        pointing to the given URL here.
 *        - linkCallback: (function) if provided, the message will be wrapped in
 *        an anchor. The |linkCallback| function will be added as click event
 *        handler.
 *        - location: object that tells the message source: url, line, column
 *        and lineText.
 *        - stack: array that tells the message source stack.
 *        - className: (string) additional element class names for styling
 *        purposes.
 *        - private: (boolean) mark this as a private message.
 *        - filterDuplicates: (boolean) true if you do want this message to be
 *        filtered as a potential duplicate message, false otherwise.
 */
Messages.Simple = function (message, options = {}) {
  Messages.BaseMessage.call(this);

  this.category = options.category;
  this.severity = options.severity;
  this.location = options.location;
  this.stack = options.stack;
  this.timestamp = options.timestamp || Date.now();
  this.prefix = options.prefix;
  this.private = !!options.private;

  this._message = message;
  this._className = options.className;
  this._link = options.link;
  this._linkCallback = options.linkCallback;
  this._filterDuplicates = options.filterDuplicates;

  this._onClickCollapsible = this._onClickCollapsible.bind(this);
};

Messages.Simple.prototype = extend(Messages.BaseMessage.prototype, {
  /**
   * Message category.
   * @type string
   */
  category: null,

  /**
   * Message severity.
   * @type string
   */
  severity: null,

  /**
   * Message source location. Properties: url, line, column, lineText.
   * @type object
   */
  location: null,

  /**
   * Holds the stackframes received from the server.
   *
   * @private
   * @type array
   */
  stack: null,

  /**
   * Message prefix
   * @type string|null
   */
  prefix: null,

  /**
   * Tells if this message comes from a private browsing context.
   * @type boolean
   */
  private: false,

  /**
   * Custom class name for the DOM element of the message.
   * @private
   * @type string
   */
  _className: null,

  /**
   * Message link - if this message is clicked then this URL opens in a new tab.
   * @private
   * @type string
   */
  _link: null,

  /**
   * Message click event handler.
   * @private
   * @type function
   */
  _linkCallback: null,

  /**
   * Tells if this message should be checked if it is a duplicate of another
   * message or not.
   */
  _filterDuplicates: false,

  /**
   * The raw message displayed by this Message object. This can be a function,
   * DOM node or a string.
   *
   * @private
   * @type mixed
   */
  _message: null,

  /**
   * The message's "attachment" element to be displayed under the message.
   * Used for things like stack traces or tables in console.table().
   *
   * @private
   * @type DOMElement|null
   */
  _attachment: null,

  _objectActors: null,
  _groupDepthCompat: 0,

  /**
   * Message timestamp.
   *
   * @type number
   *       Milliseconds elapsed since 1 January 1970 00:00:00 UTC.
   */
  timestamp: 0,

  get _categoryCompat() {
    return this.category ?
           COMPAT.CATEGORIES[this.category.toUpperCase()] : null;
  },
  get _severityCompat() {
    return this.severity ?
           COMPAT.SEVERITIES[this.severity.toUpperCase()] : null;
  },
  get _categoryNameCompat() {
    return this.category ?
           COMPAT.CATEGORY_CLASS_FRAGMENTS[this._categoryCompat] : null;
  },
  get _severityNameCompat() {
    return this.severity ?
           COMPAT.SEVERITY_CLASS_FRAGMENTS[this._severityCompat] : null;
  },

  get _filterKeyCompat() {
    return this._categoryCompat !== null && this._severityCompat !== null ?
           COMPAT.PREFERENCE_KEYS[this._categoryCompat][this._severityCompat] :
           null;
  },

  init: function () {
    Messages.BaseMessage.prototype.init.apply(this, arguments);
    this._groupDepthCompat = this.output.owner.groupDepth;
    this._initRepeatID();
    return this;
  },

  /**
   * Tells if the message can be expanded/collapsed.
   * @type boolean
   */
  collapsible: false,

  /**
   * Getter that tells if this message is collapsed - no details are shown.
   * @type boolean
   */
  get collapsed() {
    return this.collapsible && this.element && !this.element.hasAttribute("open");
  },

  _initRepeatID: function () {
    if (!this._filterDuplicates) {
      return;
    }

    // Add the properties we care about for identifying duplicate messages.
    let rid = this._repeatID;
    delete rid.uid;

    rid.category = this.category;
    rid.severity = this.severity;
    rid.prefix = this.prefix;
    rid.private = this.private;
    rid.location = this.location;
    rid.link = this._link;
    rid.linkCallback = this._linkCallback + "";
    rid.className = this._className;
    rid.groupDepth = this._groupDepthCompat;
    rid.textContent = "";
  },

  getRepeatID: function () {
    // No point in returning a string that includes other properties when there
    // is a unique ID.
    if (this._repeatID.uid) {
      return JSON.stringify({ uid: this._repeatID.uid });
    }

    return JSON.stringify(this._repeatID);
  },

  render: function () {
    if (this.element) {
      return this;
    }

    let timestamp = new Widgets.MessageTimestamp(this, this.timestamp).render();

    let icon = this.document.createElementNS(XHTML_NS, "span");
    icon.className = "icon";
    icon.title = l10n.getStr("severity." + this._severityNameCompat);
    if (this.stack) {
      icon.addEventListener("click", this._onClickCollapsible);
    }

    let prefixNode;
    if (this.prefix) {
      prefixNode = this.document.createElementNS(XHTML_NS, "span");
      prefixNode.className = "prefix devtools-monospace";
      prefixNode.textContent = this.prefix + ":";
    }

    // Apply the current group by indenting appropriately.
    // TODO: remove this once bug 778766 is fixed.
    let indent = this._groupDepthCompat * COMPAT.GROUP_INDENT;
    let indentNode = this.document.createElementNS(XHTML_NS, "span");
    indentNode.className = "indent";
    indentNode.style.width = indent + "px";

    let body = this._renderBody();

    Messages.BaseMessage.prototype.render.call(this);
    if (this._className) {
      this.element.className += " " + this._className;
    }

    this.element.appendChild(timestamp.element);
    this.element.appendChild(indentNode);
    this.element.appendChild(icon);
    if (prefixNode) {
      this.element.appendChild(prefixNode);
    }

    if (this.stack) {
      let twisty = this.document.createElementNS(XHTML_NS, "a");
      twisty.className = "theme-twisty";
      twisty.href = "#";
      twisty.title = l10n.getStr("messageToggleDetails");
      twisty.addEventListener("click", this._onClickCollapsible);
      this.element.appendChild(twisty);
      this.collapsible = true;
      this.element.setAttribute("collapsible", true);
    }

    this.element.appendChild(body);

    this.element.clipboardText = this.element.textContent;

    if (this.private) {
      this.element.setAttribute("private", true);
    }

    // TODO: handle object releasing in a more elegant way once all console
    // messages use the new API - bug 778766.
    this.element._objectActors = this._objectActors;
    this._objectActors = null;

    return this;
  },

  /**
   * Render the message body DOM element.
   * @private
   * @return Element
   */
  _renderBody: function () {
    let bodyWrapper = this.document.createElementNS(XHTML_NS, "span");
    bodyWrapper.className = "message-body-wrapper";

    let bodyFlex = this.document.createElementNS(XHTML_NS, "span");
    bodyFlex.className = "message-flex-body";
    bodyWrapper.appendChild(bodyFlex);

    let body = this.document.createElementNS(XHTML_NS, "span");
    body.className = "message-body devtools-monospace";
    bodyFlex.appendChild(body);

    let anchor, container = body;
    if (this._link || this._linkCallback) {
      container = anchor = this.document.createElementNS(XHTML_NS, "a");
      anchor.href = this._link || "#";
      anchor.draggable = false;
      this._addLinkCallback(anchor, this._linkCallback);
      body.appendChild(anchor);
    }

    if (typeof this._message == "function") {
      container.appendChild(this._message(this));
    } else if (this._message instanceof Ci.nsIDOMNode) {
      container.appendChild(this._message);
    } else {
      container.textContent = this._message;
    }

    // do this before repeatNode is rendered - it has no effect afterwards
    this._repeatID.textContent += "|" + container.textContent;

    let repeatNode = this._renderRepeatNode();
    let location = this._renderLocation();

    if (repeatNode) {
      bodyFlex.appendChild(this.document.createTextNode(" "));
      bodyFlex.appendChild(repeatNode);
    }
    if (location) {
      bodyFlex.appendChild(this.document.createTextNode(" "));
      bodyFlex.appendChild(location);
    }

    bodyFlex.appendChild(this.document.createTextNode("\n"));

    if (this.stack) {
      this._attachment = new Widgets.Stacktrace(this, this.stack).render().element;
    }

    if (this._attachment) {
      bodyWrapper.appendChild(this._attachment);
    }

    return bodyWrapper;
  },

  /**
   * Render the repeat bubble DOM element part of the message.
   * @private
   * @return Element
   */
  _renderRepeatNode: function () {
    if (!this._filterDuplicates) {
      return null;
    }

    let repeatNode = this.document.createElementNS(XHTML_NS, "span");
    repeatNode.setAttribute("value", "1");
    repeatNode.className = "message-repeats";
    repeatNode.textContent = 1;
    repeatNode._uid = this.getRepeatID();
    return repeatNode;
  },

  /**
   * Render the message source location DOM element.
   * @private
   * @return Element
   */
  _renderLocation: function () {
    if (!this.location) {
      return null;
    }

    let {url, line, column} = this.location;
    if (IGNORED_SOURCE_URLS.includes(url)) {
      return null;
    }

    // The ConsoleOutput owner is a WebConsoleFrame instance from webconsole.js.
    // TODO: move createLocationNode() into this file when bug 778766 is fixed.
    return this.output.owner.createLocationNode({url, line, column });
  },

  /**
   * The click event handler for the message expander arrow element. This method
   * toggles the display of message details.
   *
   * @private
   * @param nsIDOMEvent ev
   *        The DOM event object.
   * @see this.toggleDetails()
   */
  _onClickCollapsible: function (ev) {
    ev.preventDefault();
    this.toggleDetails();
  },

  /**
   * Expand/collapse message details.
   */
  toggleDetails: function () {
    let twisty = this.element.querySelector(".theme-twisty");
    if (this.element.hasAttribute("open")) {
      this.element.removeAttribute("open");
      twisty.removeAttribute("open");
    } else {
      this.element.setAttribute("open", true);
      twisty.setAttribute("open", true);
    }
  },
}); // Messages.Simple.prototype

/**
 * The Extended message.
 *
 * @constructor
 * @extends Messages.Simple
 * @param array messagePieces
 *        The message to display given as an array of elements. Each array
 *        element can be a DOM node, function, ObjectActor, LongString or
 *        a string.
 * @param object [options]
 *        Options for rendering this message:
 *        - quoteStrings: boolean that tells if you want strings to be wrapped
 *        in quotes or not.
 */
Messages.Extended = function (messagePieces, options = {}) {
  Messages.Simple.call(this, null, options);

  this._messagePieces = messagePieces;

  if ("quoteStrings" in options) {
    this._quoteStrings = options.quoteStrings;
  }

  this._repeatID.quoteStrings = this._quoteStrings;
  this._repeatID.messagePieces = JSON.stringify(messagePieces);
  this._repeatID.actors = new Set(); // using a set to avoid duplicates
};

Messages.Extended.prototype = extend(Messages.Simple.prototype, {
  /**
   * The message pieces displayed by this message instance.
   * @private
   * @type array
   */
  _messagePieces: null,

  /**
   * Boolean that tells if the strings displayed in this message are wrapped.
   * @private
   * @type boolean
   */
  _quoteStrings: true,

  getRepeatID: function () {
    if (this._repeatID.uid) {
      return JSON.stringify({ uid: this._repeatID.uid });
    }

    // Sets are not stringified correctly. Temporarily switching to an array.
    let actors = this._repeatID.actors;
    this._repeatID.actors = [...actors];
    let result = JSON.stringify(this._repeatID);
    this._repeatID.actors = actors;
    return result;
  },

  render: function () {
    let result = this.document.createDocumentFragment();

    for (let i = 0; i < this._messagePieces.length; i++) {
      let separator = i > 0 ? this._renderBodyPieceSeparator() : null;
      if (separator) {
        result.appendChild(separator);
      }

      let piece = this._messagePieces[i];
      result.appendChild(this._renderBodyPiece(piece));
    }

    this._message = result;
    this._messagePieces = null;
    return Messages.Simple.prototype.render.call(this);
  },

  /**
   * Render the separator between the pieces of the message.
   *
   * @private
   * @return Element
   */
  _renderBodyPieceSeparator: function () {
    return null;
  },

  /**
   * Render one piece/element of the message array.
   *
   * @private
   * @param mixed piece
   *        Message element to display - this can be a LongString, ObjectActor,
   *        DOM node or a function to invoke.
   * @return Element
   */
  _renderBodyPiece: function (piece, options = {}) {
    if (piece instanceof Ci.nsIDOMNode) {
      return piece;
    }
    if (typeof piece == "function") {
      return piece(this);
    }

    return this._renderValueGrip(piece, options);
  },

  /**
   * Render a grip that represents a value received from the server. This method
   * picks the appropriate widget to render the value with.
   *
   * @private
   * @param object grip
   *        The value grip received from the server.
   * @param object options
   *        Options for displaying the value. Available options:
   *        - noStringQuotes - boolean that tells the renderer to not use quotes
   *        around strings.
   *        - concise - boolean that tells the renderer to compactly display the
   *        grip. This is typically set to true when the object needs to be
   *        displayed in an array preview, or as a property value in object
   *        previews, etc.
   *        - shorten - boolean that tells the renderer to display a truncated
   *        grip.
   * @return DOMElement
   *         The DOM element that displays the given grip.
   */
  _renderValueGrip: function (grip, options = {}) {
    let isPrimitive = VariablesView.isPrimitive({ value: grip });
    let isActorGrip = WebConsoleUtils.isActorGrip(grip);
    let noStringQuotes = !this._quoteStrings;
    if ("noStringQuotes" in options) {
      noStringQuotes = options.noStringQuotes;
    }

    if (isActorGrip) {
      this._repeatID.actors.add(grip.actor);

      if (!isPrimitive) {
        return this._renderObjectActor(grip, options);
      }
      if (grip.type == "longString") {
        let widget = new Widgets.LongString(this, grip, options).render();
        return widget.element;
      }
    }

    let unshortenedGrip = grip;
    if (options.shorten) {
      grip = this.shortenValueGrip(grip);
    }

    let result = this.document.createElementNS(XHTML_NS, "span");
    if (isPrimitive) {
      if (Widgets.URLString.prototype.containsURL(grip)) {
        let widget = new Widgets.URLString(this, grip, unshortenedGrip).render();
        return widget.element;
      }

      let className = this.getClassNameForValueGrip(grip);
      if (className) {
        result.className = className;
      }

      result.textContent = VariablesView.getString(grip, {
        noStringQuotes: noStringQuotes,
        concise: options.concise,
      });
    } else {
      result.textContent = grip;
    }

    return result;
  },

  /**
   * Shorten grips of the type string, leaves other grips unmodified.
   *
   * @param object grip
   *        Value grip from the server.
   * @return object
   *        Possible values of object:
   *        - A shortened string, if original grip was of string type.
   *        - The unmodified input grip, if it wasn't of string type.
   */
  shortenValueGrip: function (grip) {
    let shortVal = grip;
    if (typeof (grip) == "string") {
      shortVal = grip.replace(/(\r\n|\n|\r)/gm, " ");
      if (shortVal.length > MAX_STRING_GRIP_LENGTH) {
        shortVal = shortVal.substring(0, MAX_STRING_GRIP_LENGTH - 1) + ELLIPSIS;
      }
    }

    return shortVal;
  },

  /**
   * Get a CodeMirror-compatible class name for a given value grip.
   *
   * @param object grip
   *        Value grip from the server.
   * @return string
   *         The class name for the grip.
   */
  getClassNameForValueGrip: function (grip) {
    let map = {
      "number": "cm-number",
      "longstring": "console-string",
      "string": "console-string",
      "regexp": "cm-string-2",
      "boolean": "cm-atom",
      "-infinity": "cm-atom",
      "infinity": "cm-atom",
      "null": "cm-atom",
      "undefined": "cm-comment",
      "symbol": "cm-atom"
    };

    let className = map[typeof grip];
    if (!className && grip && grip.type) {
      className = map[grip.type.toLowerCase()];
    }
    if (!className && grip && grip.class) {
      className = map[grip.class.toLowerCase()];
    }

    return className;
  },

  /**
   * Display an object actor with the appropriate renderer.
   *
   * @private
   * @param object objectActor
   *        The ObjectActor to display.
   * @param object options
   *        Options to use for displaying the ObjectActor.
   * @see this._renderValueGrip for the available options.
   * @return DOMElement
   *         The DOM element that displays the object actor.
   */
  _renderObjectActor: function (objectActor, options = {}) {
    let Widget = Widgets.ObjectRenderers.byClass[objectActor.class];

    let { preview } = objectActor;
    if ((!Widget || (Widget.canRender && !Widget.canRender(objectActor)))
        && preview
        && preview.kind) {
      Widget = Widgets.ObjectRenderers.byKind[preview.kind];
    }

    if (!Widget || (Widget.canRender && !Widget.canRender(objectActor))) {
      Widget = Widgets.JSObject;
    }

    let instance = new Widget(this, objectActor, options).render();
    return instance.element;
  },
}); // Messages.Extended.prototype

/**
 * The JavaScriptEvalOutput message.
 *
 * @constructor
 * @extends Messages.Extended
 * @param object evalResponse
 *        The evaluation response packet received from the server.
 * @param string [errorMessage]
 *        Optional error message to display.
 * @param string [errorDocLink]
 * Optional error doc URL to link to.
 */
Messages.JavaScriptEvalOutput = function (evalResponse, errorMessage, errorDocLink) {
  let severity = "log", msg, quoteStrings = true;

  // Store also the response packet from the back end. It might
  // be useful to extensions customizing the console output.
  this.response = evalResponse;

  if (typeof (errorMessage) !== "undefined") {
    severity = "error";
    msg = errorMessage;
    quoteStrings = false;
  } else {
    msg = evalResponse.result;
  }

  let options = {
    className: "cm-s-mozilla",
    timestamp: evalResponse.timestamp,
    category: "output",
    severity: severity,
    quoteStrings: quoteStrings,
  };

  let messages = [msg];
  if (errorDocLink) {
    messages.push(errorDocLink);
  }

  Messages.Extended.call(this, messages, options);
};

Messages.JavaScriptEvalOutput.prototype = Messages.Extended.prototype;

/**
 * The ConsoleGeneric message is used for console API calls.
 *
 * @constructor
 * @extends Messages.Extended
 * @param object packet
 *        The Console API call packet received from the server.
 */
Messages.ConsoleGeneric = function (packet) {
  let options = {
    className: "cm-s-mozilla",
    timestamp: packet.timeStamp,
    category: packet.category || "webdev",
    severity: CONSOLE_API_LEVELS_TO_SEVERITIES[packet.level],
    prefix: packet.prefix,
    private: packet.private,
    filterDuplicates: true,
    location: {
      url: packet.filename,
      line: packet.lineNumber,
      column: packet.columnNumber
    },
  };

  switch (packet.level) {
    case "count": {
      let counter = packet.counter, label = counter.label;
      if (!label) {
        label = l10n.getStr("noCounterLabel");
      }
      Messages.Extended.call(this, [label + ": " + counter.count], options);
      break;
    }
    default:
      Messages.Extended.call(this, packet.arguments, options);
      break;
  }

  this._repeatID.consoleApiLevel = packet.level;
  this._repeatID.styles = packet.styles;
  this.stack = this._repeatID.stacktrace = packet.stacktrace;
  this._styles = packet.styles || [];
};

Messages.ConsoleGeneric.prototype = extend(Messages.Extended.prototype, {
  _styles: null,

  _renderBodyPieceSeparator: function () {
    return this.document.createTextNode(" ");
  },

  render: function () {
    let result = this.document.createDocumentFragment();
    this._renderBodyPieces(result);

    this._message = result;
    this._stacktrace = null;

    Messages.Simple.prototype.render.call(this);

    return this;
  },

  _renderBodyPieces: function (container) {
    let lastStyle = null;
    let stylePieces = this._styles.length > 0 ? this._styles.length : 1;

    for (let i = 0; i < this._messagePieces.length; i++) {
      // Pieces with an associated style definition come from "%c" formatting.
      // For body pieces beyond that, add a separator before each one.
      if (i >= stylePieces) {
        container.appendChild(this._renderBodyPieceSeparator());
      }

      let piece = this._messagePieces[i];
      let style = this._styles[i];

      // No long string support.
      lastStyle = (style && typeof style == "string") ?
                  this.cleanupStyle(style) : null;

      container.appendChild(this._renderBodyPiece(piece, lastStyle));
    }

    this._messagePieces = null;
    this._styles = null;
  },

  _renderBodyPiece: function (piece, style) {
    // Skip quotes for top-level strings.
    let options = { noStringQuotes: true };
    let elem = Messages.Extended.prototype._renderBodyPiece.call(this, piece, options);
    let result = elem;

    if (style) {
      if (elem.nodeType == nodeConstants.ELEMENT_NODE) {
        elem.style = style;
      } else {
        let span = this.document.createElementNS(XHTML_NS, "span");
        span.style = style;
        span.appendChild(elem);
        result = span;
      }
    }

    return result;
  },

  /**
   * Given a style attribute value, return a cleaned up version of the string
   * such that:
   *
   * - no external URL is allowed to load. See RE_CLEANUP_STYLES.
   * - only some of the properties are allowed, based on a whitelist. See
   *   RE_ALLOWED_STYLES.
   *
   * @param string style
   *        The style string to cleanup.
   * @return string
   *         The style value after cleanup.
   */
  cleanupStyle: function (style) {
    for (let r of RE_CLEANUP_STYLES) {
      style = style.replace(r, "notallowed");
    }

    let dummy = this.output._dummyElement;
    if (!dummy) {
      dummy = this.output._dummyElement =
        this.document.createElementNS(XHTML_NS, "div");
    }
    dummy.style = style;

    let toRemove = [];
    for (let i = 0; i < dummy.style.length; i++) {
      let prop = dummy.style[i];
      if (!RE_ALLOWED_STYLES.test(prop)) {
        toRemove.push(prop);
      }
    }

    for (let prop of toRemove) {
      dummy.style.removeProperty(prop);
    }

    style = dummy.style.cssText;

    dummy.style = "";

    return style;
  },
}); // Messages.ConsoleGeneric.prototype

/**
 * The ConsoleTrace message is used for console.trace() calls.
 *
 * @constructor
 * @extends Messages.Simple
 * @param object packet
 *        The Console API call packet received from the server.
 */
Messages.ConsoleTrace = function (packet) {
  let options = {
    className: "cm-s-mozilla",
    timestamp: packet.timeStamp,
    category: packet.category || "webdev",
    severity: CONSOLE_API_LEVELS_TO_SEVERITIES[packet.level],
    private: packet.private,
    filterDuplicates: true,
    location: {
      url: packet.filename,
      line: packet.lineNumber,
    },
  };

  Messages.Simple.call(this, null, options);

  this._repeatID.consoleApiLevel = packet.level;
  this._stacktrace = this._repeatID.stacktrace = packet.stacktrace;
  this._arguments = packet.arguments;
};

Messages.ConsoleTrace.prototype = extend(Messages.Simple.prototype, {
  /**
   * Holds the stackframes received from the server.
   *
   * @private
   * @type array
   */
  _stacktrace: null,

  /**
   * Holds the arguments the content script passed to the console.trace()
   * method. This array is cleared when the message is initialized, and
   * associated actors are released.
   *
   * @private
   * @type array
   */
  _arguments: null,

  init: function () {
    let result = Messages.Simple.prototype.init.apply(this, arguments);

    // We ignore console.trace() arguments. Release object actors.
    if (Array.isArray(this._arguments)) {
      for (let arg of this._arguments) {
        if (WebConsoleUtils.isActorGrip(arg)) {
          this.output._releaseObject(arg.actor);
        }
      }
    }
    this._arguments = null;

    return result;
  },

  render: function () {
    this._message = this._renderMessage();
    this._attachment = this._renderStack();

    Messages.Simple.prototype.render.apply(this, arguments);
    this.element.setAttribute("open", true);
    return this;
  },

  /**
   * Render the console messageNode
   */
  _renderMessage: function () {
    let cmvar = this.document.createElementNS(XHTML_NS, "span");
    cmvar.className = "cm-variable";
    cmvar.textContent = "console";

    let cmprop = this.document.createElementNS(XHTML_NS, "span");
    cmprop.className = "cm-property";
    cmprop.textContent = "trace";

    let frag = this.document.createDocumentFragment();
    frag.appendChild(cmvar);
    frag.appendChild(this.document.createTextNode("."));
    frag.appendChild(cmprop);
    frag.appendChild(this.document.createTextNode("():"));

    return frag;
  },

  /**
   * Render the stack frames.
   *
   * @private
   * @return DOMElement
   */
  _renderStack: function () {
    return new Widgets.Stacktrace(this, this._stacktrace).render().element;
  },
}); // Messages.ConsoleTrace.prototype

/**
 * The ConsoleTable message is used for console.table() calls.
 *
 * @constructor
 * @extends Messages.Extended
 * @param object packet
 *        The Console API call packet received from the server.
 */
Messages.ConsoleTable = function (packet) {
  let options = {
    className: "cm-s-mozilla",
    timestamp: packet.timeStamp,
    category: packet.category || "webdev",
    severity: CONSOLE_API_LEVELS_TO_SEVERITIES[packet.level],
    private: packet.private,
    filterDuplicates: false,
    location: {
      url: packet.filename,
      line: packet.lineNumber,
    },
  };

  this._populateTableData = this._populateTableData.bind(this);
  this._renderMessage = this._renderMessage.bind(this);
  Messages.Extended.call(this, [this._renderMessage], options);

  this._repeatID.consoleApiLevel = packet.level;
  this._arguments = packet.arguments;
};

Messages.ConsoleTable.prototype = extend(Messages.Extended.prototype, {
  /**
   * Holds the arguments the content script passed to the console.table()
   * method.
   *
   * @private
   * @type array
   */
  _arguments: null,

  /**
   * Array of objects that holds the data to log in the table.
   *
   * @private
   * @type array
   */
  _data: null,

  /**
   * Key value pair of the id and display name for the columns in the table.
   * Refer to the TableWidget API.
   *
   * @private
   * @type object
   */
  _columns: null,

  /**
   * A promise that resolves when the table data is ready or null if invalid
   * arguments are provided.
   *
   * @private
   * @type promise|null
   */
  _populatePromise: null,

  init: function () {
    let result = Messages.Extended.prototype.init.apply(this, arguments);
    this._data = [];
    this._columns = {};

    this._populatePromise = this._populateTableData();

    return result;
  },

  /**
   * Sets the key value pair of the id and display name for the columns in the
   * table.
   *
   * @private
   * @param array|string columns
   *        Either a string or array containing the names for the columns in
   *        the output table.
   */
  _setColumns: function (columns) {
    if (columns.class == "Array") {
      let items = columns.preview.items;

      for (let item of items) {
        if (typeof item == "string") {
          this._columns[item] = item;
        }
      }
    } else if (typeof columns == "string" && columns) {
      this._columns[columns] = columns;
    }
  },

  /**
   * Retrieves the table data and columns from the arguments received from the
   * server.
   *
   * @return Promise|null
   *         Returns a promise that resolves when the table data is ready or
   *         null if the arguments are invalid.
   */
  _populateTableData: function () {
    let deferred = defer();

    if (this._arguments.length <= 0) {
      return deferred.reject();
    }

    let data = this._arguments[0];
    if (data.class != "Array" && data.class != "Object" &&
        data.class != "Map" && data.class != "Set" &&
        data.class != "WeakMap" && data.class != "WeakSet") {
      return deferred.reject();
    }

    let hasColumnsArg = false;
    if (this._arguments.length > 1) {
      if (data.class == "Object" || data.class == "Array") {
        this._columns._index = l10n.getStr("table.index");
      } else {
        this._columns._index = l10n.getStr("table.iterationIndex");
      }

      this._setColumns(this._arguments[1]);
      hasColumnsArg = true;
    }

    if (data.class == "Object" || data.class == "Array") {
      // Get the object properties, and parse the key and value properties into
      // the table data and columns.
      this.client = new ObjectClient(this.output.owner.jsterm.hud.proxy.client,
          data);
      this.client.getPrototypeAndProperties(response => {
        let {ownProperties} = response;
        let rowCount = 0;
        let columnCount = 0;

        for (let index of Object.keys(ownProperties || {})) {
          // Avoid outputting the length property if the data argument provided
          // is an array
          if (data.class == "Array" && index == "length") {
            continue;
          }

          if (!hasColumnsArg) {
            this._columns._index = l10n.getStr("table.index");
          }

          if (data.class == "Array") {
            if (index == parseInt(index, 10)) {
              index = parseInt(index, 10);
            }
          }

          let property = ownProperties[index].value;
          let item = { _index: index };

          if (property.class == "Object" || property.class == "Array") {
            let {preview} = property;
            let entries = property.class == "Object" ?
                preview.ownProperties : preview.items;

            for (let key of Object.keys(entries)) {
              let value = property.class == "Object" ?
                  preview.ownProperties[key].value : preview.items[key];

              item[key] = this._renderValueGrip(value, { concise: true });

              if (!hasColumnsArg && !(key in this._columns) &&
                  (++columnCount <= TABLE_COLUMN_MAX_ITEMS)) {
                this._columns[key] = key;
              }
            }
          } else {
            // Display the value for any non-object data input.
            item._value = this._renderValueGrip(property, { concise: true });

            if (!hasColumnsArg && !("_value" in this._columns)) {
              this._columns._value = l10n.getStr("table.value");
            }
          }

          this._data.push(item);

          if (++rowCount == TABLE_ROW_MAX_ITEMS) {
            break;
          }
        }

        deferred.resolve();
      });
    } else if (data.class == "Map" || data.class == "WeakMap") {
      let entries = data.preview.entries;

      if (!hasColumnsArg) {
        this._columns._index = l10n.getStr("table.iterationIndex");
        this._columns._key = l10n.getStr("table.key");
        this._columns._value = l10n.getStr("table.value");
      }

      let rowCount = 0;
      for (let [key, value] of entries) {
        let item = {
          _index: rowCount,
          _key: this._renderValueGrip(key, { concise: true }),
          _value: this._renderValueGrip(value, { concise: true })
        };

        this._data.push(item);

        if (++rowCount == TABLE_ROW_MAX_ITEMS) {
          break;
        }
      }

      deferred.resolve();
    } else if (data.class == "Set" || data.class == "WeakSet") {
      let entries = data.preview.items;

      if (!hasColumnsArg) {
        this._columns._index = l10n.getStr("table.iterationIndex");
        this._columns._value = l10n.getStr("table.value");
      }

      let rowCount = 0;
      for (let entry of entries) {
        let item = {
          _index: rowCount,
          _value: this._renderValueGrip(entry, { concise: true })
        };

        this._data.push(item);

        if (++rowCount == TABLE_ROW_MAX_ITEMS) {
          break;
        }
      }

      deferred.resolve();
    }

    return deferred.promise;
  },

  render: function () {
    this._attachment = this._renderTable();
    Messages.Extended.prototype.render.apply(this, arguments);
    this.element.setAttribute("open", true);
    return this;
  },

  _renderMessage: function () {
    let cmvar = this.document.createElementNS(XHTML_NS, "span");
    cmvar.className = "cm-variable";
    cmvar.textContent = "console";

    let cmprop = this.document.createElementNS(XHTML_NS, "span");
    cmprop.className = "cm-property";
    cmprop.textContent = "table";

    let frag = this.document.createDocumentFragment();
    frag.appendChild(cmvar);
    frag.appendChild(this.document.createTextNode("."));
    frag.appendChild(cmprop);
    frag.appendChild(this.document.createTextNode("():"));

    return frag;
  },

  /**
   * Render the table.
   *
   * @private
   * @return DOMElement
   */
  _renderTable: function () {
    let result = this.document.createElementNS(XHTML_NS, "div");

    if (this._populatePromise) {
      this._populatePromise.then(() => {
        if (this._data.length > 0) {
          let widget = new Widgets.Table(this, this._data, this._columns).render();
          result.appendChild(widget.element);
        }

        result.scrollIntoView();
        this.output.owner.emit("messages-table-rendered");

        // Release object actors
        if (Array.isArray(this._arguments)) {
          for (let arg of this._arguments) {
            if (WebConsoleUtils.isActorGrip(arg)) {
              this.output._releaseObject(arg.actor);
            }
          }
        }
        this._arguments = null;
      });
    }

    return result;
  },
}); // Messages.ConsoleTable.prototype

var Widgets = {};

/**
 * The base widget class.
 *
 * @constructor
 * @param object message
 *        The owning message.
 */
Widgets.BaseWidget = function (message) {
  this.message = message;
};

Widgets.BaseWidget.prototype = {
  /**
   * The owning message object.
   * @type object
   */
  message: null,

  /**
   * The DOM element of the rendered widget.
   * @type Element
   */
  element: null,

  /**
   * Getter for the DOM document that holds the output.
   * @type Document
   */
  get document() {
    return this.message.document;
  },

  /**
   * The ConsoleOutput instance that owns this widget instance.
   */
  get output() {
    return this.message.output;
  },

  /**
   * Render the widget DOM element.
   * @return this
   */
  render: function () { },

  /**
   * Destroy this widget instance.
   */
  destroy: function () { },

  /**
   * Helper for creating DOM elements for widgets.
   *
   * Usage:
   *   this.el("tag#id.class.names"); // create element "tag" with ID "id" and
   *   two class names, .class and .names.
   *
   *   this.el("span", { attr1: "value1", ... }) // second argument can be an
   *   object that holds element attributes and values for the new DOM element.
   *
   *   this.el("p", { attr1: "value1", ... }, "text content"); // the third
   *   argument can include the default .textContent of the new DOM element.
   *
   *   this.el("p", "text content"); // if the second argument is not an object,
   *   it will be used as .textContent for the new DOM element.
   *
   * @param string tagNameIdAndClasses
   *        Tag name for the new element, optionally followed by an ID and/or
   *        class names. Examples: "span", "div#fooId", "div.class.names",
   *        "p#id.class".
   * @param string|object [attributesOrTextContent]
   *        If this argument is an object it will be used to set the attributes
   *        of the new DOM element. Otherwise, the value becomes the
   *        .textContent of the new DOM element.
   * @param string [textContent]
   *        If this argument is provided the value is used as the textContent of
   *        the new DOM element.
   * @return DOMElement
   *         The new DOM element.
   */
  el: function (tagNameIdAndClasses) {
    let attrs, text;
    if (typeof arguments[1] == "object") {
      attrs = arguments[1];
      text = arguments[2];
    } else {
      text = arguments[1];
    }

    let tagName = tagNameIdAndClasses.split(/#|\./)[0];

    let elem = this.document.createElementNS(XHTML_NS, tagName);
    for (let name of Object.keys(attrs || {})) {
      elem.setAttribute(name, attrs[name]);
    }
    if (text !== undefined && text !== null) {
      elem.textContent = text;
    }

    let idAndClasses = tagNameIdAndClasses.match(/([#.][^#.]+)/g);
    for (let idOrClass of (idAndClasses || [])) {
      if (idOrClass.charAt(0) == "#") {
        elem.id = idOrClass.substr(1);
      } else {
        elem.classList.add(idOrClass.substr(1));
      }
    }

    return elem;
  },
};

/**
 * The timestamp widget.
 *
 * @constructor
 * @param object message
 *        The owning message.
 * @param number timestamp
 *        The UNIX timestamp to display.
 */
Widgets.MessageTimestamp = function (message, timestamp) {
  Widgets.BaseWidget.call(this, message);
  this.timestamp = timestamp;
};

Widgets.MessageTimestamp.prototype = extend(Widgets.BaseWidget.prototype, {
  /**
   * The UNIX timestamp.
   * @type number
   */
  timestamp: 0,

  render: function () {
    if (this.element) {
      return this;
    }

    this.element = this.document.createElementNS(XHTML_NS, "span");
    this.element.className = "timestamp devtools-monospace";
    this.element.textContent = l10n.timestampString(this.timestamp) + " ";

    return this;
  },
}); // Widgets.MessageTimestamp.prototype

/**
 * The URLString widget, for rendering strings where at least one token is a
 * URL.
 *
 * @constructor
 * @param object message
 *        The owning message.
 * @param string str
 *        The string, which contains at least one valid URL.
 * @param string unshortenedStr
 *        The unshortened form of the string, if it was shortened.
 */
Widgets.URLString = function (message, str, unshortenedStr) {
  Widgets.BaseWidget.call(this, message);
  this.str = str;
  this.unshortenedStr = unshortenedStr;
};

Widgets.URLString.prototype = extend(Widgets.BaseWidget.prototype, {
  /**
   * The string to format, which contains at least one valid URL.
   * @type string
   */
  str: "",

  render: function () {
    if (this.element) {
      return this;
    }

    // The rendered URLString will be a <span> containing a number of text
    // <spans> for non-URL tokens and <a>'s for URL tokens.
    this.element = this.el("span", {
      class: "console-string"
    });
    this.element.appendChild(this._renderText("\""));

    // As we walk through the tokens of the source string, we make sure to preserve
    // the original whitespace that separated the tokens.
    let tokens = this.str.split(/\s+/);
    let textStart = 0;
    let tokenStart;
    for (let i = 0; i < tokens.length; i++) {
      let token = tokens[i];
      let unshortenedToken;
      tokenStart = this.str.indexOf(token, textStart);
      if (this._isURL(token)) {
        // The last URL in the string might be shortened.  If so, get the
        // real URL so the rendered link can point to it.
        if (i === tokens.length - 1 && this.unshortenedStr) {
          unshortenedToken = this.unshortenedStr.slice(tokenStart).split(/\s+/, 1)[0];
        }
        this.element.appendChild(this._renderText(this.str.slice(textStart, tokenStart)));
        textStart = tokenStart + token.length;
        this.element.appendChild(this._renderURL(token, unshortenedToken));
      }
    }

    // Clean up any non-URL text at the end of the source string.
    const rendered = this._renderText(this.str.slice(textStart, this.str.length));
    this.element.appendChild(rendered);
    this.element.appendChild(this._renderText("\""));

    return this;
  },

  /**
   * 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.
   */
  containsURL: function (grip) {
    if (typeof grip != "string") {
      return false;
    }

    let tokens = grip.split(/\s+/);
    return tokens.some(this._isURL);
  },

  /**
   * Determines whether a string token is a valid URL.
   *
   * @param string token
   *        The token.
   * @return boolean
   *         Whenther the token is a URL.
   */
  _isURL: function (token) {
    try {
      if (!validProtocols.test(token)) {
        return false;
      }
      new URL(token);
      return true;
    } catch (e) {
      return false;
    }
  },

  /**
   * Renders a string as a URL.
   *
   * @param string url
   *        The string to be rendered as a url.
   * @param string fullUrl
   *        The unshortened form of the URL, if it was shortened.
   * @return DOMElement
   *         An element containing the rendered string.
   */
  _renderURL: function (url, fullUrl) {
    let unshortened = fullUrl || url;
    let result = this.el("a", {
      class: "url",
      title: unshortened,
      href: unshortened,
      draggable: false
    }, url);
    this.message._addLinkCallback(result);
    return result;
  },

  _renderText: function (text) {
    return this.el("span", text);
  },
}); // Widgets.URLString.prototype

/**
 * Widget used for displaying ObjectActors that have no specialised renderers.
 *
 * @constructor
 * @param object message
 *        The owning message.
 * @param object objectActor
 *        The ObjectActor to display.
 * @param object [options]
 *        Options for displaying the given ObjectActor. See
 *        Messages.Extended.prototype._renderValueGrip for the available
 *        options.
 */
Widgets.JSObject = function (message, objectActor, options = {}) {
  Widgets.BaseWidget.call(this, message);
  this.objectActor = objectActor;
  this.options = options;
  this._onClick = this._onClick.bind(this);
};

Widgets.JSObject.prototype = extend(Widgets.BaseWidget.prototype, {
  /**
   * The ObjectActor displayed by the widget.
   * @type object
   */
  objectActor: null,

  render: function () {
    if (!this.element) {
      this._render();
    }

    return this;
  },

  _render: function () {
    let str = VariablesView.getString(this.objectActor, this.options);
    let className = this.message.getClassNameForValueGrip(this.objectActor);
    if (!className && this.objectActor.class == "Object") {
      className = "cm-variable";
    }

    this.element = this._anchor(str, { className: className });
  },

  /**
   * Render a concise representation of an object.
   */
  _renderConciseObject: function () {
    this.element = this._anchor(this.objectActor.class,
                                { className: "cm-variable" });
  },

  /**
   * Render the `<class> { ` prefix of an object.
   */
  _renderObjectPrefix: function () {
    let { kind } = this.objectActor.preview;
    this.element = this.el("span.kind-" + kind);
    this._anchor(this.objectActor.class, { className: "cm-variable" });
    this._text(" { ");
  },

  /**
   * Render the ` }` suffix of an object.
   */
  _renderObjectSuffix: function () {
    this._text(" }");
  },

  /**
   * Render an object property.
   *
   * @param String key
   *        The property name.
   * @param Object value
   *        The property value, as an RDP grip.
   * @param nsIDOMNode container
   *        The container node to render to.
   * @param Boolean needsComma
   *        True if there was another property before this one and we need to
   *        separate them with a comma.
   * @param Boolean valueIsText
   *        Add the value as is, don't treat it as a grip and pass it to
   *        `_renderValueGrip`.
   */
  _renderObjectProperty: function (
    key,
    value,
    container,
    needsComma,
    valueIsText = false
  ) {
    if (needsComma) {
      this._text(", ");
    }

    container.appendChild(this.el("span.cm-property", key));
    this._text(": ");

    if (valueIsText) {
      this._text(value);
    } else {
      let valueElem = this.message._renderValueGrip(value, {
        concise: true,
        shorten: true
      });
      container.appendChild(valueElem);
    }
  },

  /**
   * Render this object's properties.
   *
   * @param nsIDOMNode container
   *        The container node to render to.
   * @param Boolean needsComma
   *        True if there was another property before this one and we need to
   *        separate them with a comma.
   */
  _renderObjectProperties: function (container, needsComma) {
    let { preview } = this.objectActor;
    let { ownProperties, safeGetterValues } = preview;

    let shown = 0;

    let getValue = desc => {
      if (desc.get) {
        return "Getter";
      } else if (desc.set) {
        return "Setter";
      }
      return desc.value;
    };

    for (let key of Object.keys(ownProperties || {})) {
      this._renderObjectProperty(key, getValue(ownProperties[key]), container,
                                 shown > 0 || needsComma,
                                 ownProperties[key].get || ownProperties[key].set);
      shown++;
    }

    let ownPropertiesShown = shown;

    for (let key of Object.keys(safeGetterValues || {})) {
      this._renderObjectProperty(key, safeGetterValues[key].getterValue,
                                 container, shown > 0 || needsComma);
      shown++;
    }

    if (typeof preview.ownPropertiesLength == "number" &&
        ownPropertiesShown < preview.ownPropertiesLength) {
      this._text(", ");

      let n = preview.ownPropertiesLength - ownPropertiesShown;
      let str = VariablesView.stringifiers._getNMoreString(n);
      this._anchor(str);
    }
  },

  /**
   * Render an anchor with a given text content and link.
   *
   * @private
   * @param string text
   *        Text to show in the anchor.
   * @param object [options]
   *        Available options:
   *        - onClick (function): "click" event handler.By default a click on
   *        the anchor opens the variables view for the current object actor
   *        (this.objectActor).
   *        - href (string): if given the string is used as a link, and clicks
   *        on the anchor open the link in a new tab.
   *        - appendTo (DOMElement): append the element to the given DOM
   *        element. If not provided, the anchor is appended to |this.element|
   *        if it is available. If |appendTo| is provided and if it is a falsy
   *        value, the anchor is not appended to any element.
   * @return DOMElement
   *         The DOM element of the new anchor.
   */
  _anchor: function (text, options = {}) {
    if (!options.onClick) {
      // If the anchor has an URL, open it in a new tab. If not, show the
      // current object actor.
      options.onClick = options.href ? this._onClickAnchor : this._onClick;
    }

    options.onContextMenu = options.onContextMenu || this._onContextMenu;

    let anchor = this.el("a", {
      class: options.className,
      draggable: false,
      href: options.href || "#",
    }, text);

    this.message._addLinkCallback(anchor, options.onClick);

    anchor.addEventListener("contextmenu", options.onContextMenu.bind(this));

    if (options.appendTo) {
      options.appendTo.appendChild(anchor);
    } else if (!("appendTo" in options) && this.element) {
      this.element.appendChild(anchor);
    }

    return anchor;
  },

  openObjectInVariablesView: function () {
    this.output.openVariablesView({
      label: VariablesView.getString(this.objectActor, { concise: true }),
      objectActor: this.objectActor,
      autofocus: true,
    });
  },

  storeObjectInWindow: function () {
    let evalString = `{ let i = 0;
      while (this.hasOwnProperty("temp" + i) && i < 1000) {
        i++;
      }
      this["temp" + i] = _self;
      "temp" + i;
    }`;
    let options = {
      selectedObjectActor: this.objectActor.actor,
    };

    this.output.owner.jsterm.requestEvaluation(evalString, options).then((res) => {
      this.output.owner.jsterm.focus();
      this.output.owner.jsterm.setInputValue(res.result);
    });
  },

  /**
   * The click event handler for objects shown inline.
   * @private
   */
  _onClick: function () {
    this.openObjectInVariablesView();
  },

  _onContextMenu: function (ev) {
    // TODO offer a nice API for the context menu.
    // Probably worth to take a look at Firebug's way
    // https://github.com/firebug/firebug/blob/master/extension/content/firebug/chrome/menu.js
    let doc = ev.target.ownerDocument;
    let cmPopup = doc.getElementById("output-contextmenu");

    let openInVarViewCmd = doc.getElementById("menu_openInVarView");
    let openVarView = this.openObjectInVariablesView.bind(this);
    openInVarViewCmd.addEventListener("command", openVarView);
    openInVarViewCmd.removeAttribute("disabled");
    cmPopup.addEventListener("popuphiding", function () {
      openInVarViewCmd.removeEventListener("command", openVarView);
      openInVarViewCmd.setAttribute("disabled", "true");
    }, {once: true});

    // 'Store as global variable' command isn't supported on pre-44 servers,
    // so remove it from the menu in that case.
    let storeInGlobalCmd = doc.getElementById("menu_storeAsGlobal");
    if (!this.output.webConsoleClient.traits.selectedObjectActor) {
      storeInGlobalCmd.remove();
    } else if (storeInGlobalCmd) {
      let storeObjectInWindow = this.storeObjectInWindow.bind(this);
      storeInGlobalCmd.addEventListener("command", storeObjectInWindow);
      storeInGlobalCmd.removeAttribute("disabled");
      cmPopup.addEventListener("popuphiding", function () {
        storeInGlobalCmd.removeEventListener("command", storeObjectInWindow);
        storeInGlobalCmd.setAttribute("disabled", "true");
      }, {once: true});
    }
  },

  /**
   * Add a string to the message.
   *
   * @private
   * @param string str
   *        String to add.
   * @param DOMElement [target = this.element]
   *        Optional DOM element to append the string to. The default is
   *        this.element.
   */
  _text: function (str, target = this.element) {
    target.appendChild(this.document.createTextNode(str));
  },
}); // Widgets.JSObject.prototype

Widgets.ObjectRenderers = {};
Widgets.ObjectRenderers.byKind = {};
Widgets.ObjectRenderers.byClass = {};

/**
 * Add an object renderer.
 *
 * @param object obj
 *        An object that represents the renderer. Properties:
 *        - byClass (string, optional): this renderer will be used for the given
 *        object class.
 *        - byKind (string, optional): this renderer will be used for the given
 *        object kind.
 *        One of byClass or byKind must be provided.
 *        - extends (object, optional): the renderer object extends the given
 *        object. Default: Widgets.JSObject.
 *        - canRender (function, optional): this method is invoked when
 *        a candidate object needs to be displayed. The method is invoked as
 *        a static method, as such, none of the properties of the renderer
 *        object will be available. You get one argument: the object actor grip
 *        received from the server. If the method returns true, then this
 *        renderer is used for displaying the object, otherwise not.
 *        - initialize (function, optional): the constructor of the renderer
 *        widget. This function is invoked with the following arguments: the
 *        owner message object instance, the object actor grip to display, and
 *        an options object. See Messages.Extended.prototype._renderValueGrip()
 *        for details about the options object.
 *        - render (function, required): the method that displays the given
 *        object actor.
 */
Widgets.ObjectRenderers.add = function (obj) {
  let extendObj = obj.extends || Widgets.JSObject;

  let constructor = function () {
    if (obj.initialize) {
      obj.initialize.apply(this, arguments);
    } else {
      extendObj.apply(this, arguments);
    }
  };

  let proto = WebConsoleUtils.cloneObject(obj, false, function (key) {
    if (key == "initialize" || key == "canRender" ||
        (key == "render" && extendObj === Widgets.JSObject)) {
      return false;
    }
    return true;
  });

  if (extendObj === Widgets.JSObject) {
    proto._render = obj.render;
  }

  constructor.canRender = obj.canRender;
  constructor.prototype = extend(extendObj.prototype, proto);

  if (obj.byClass) {
    Widgets.ObjectRenderers.byClass[obj.byClass] = constructor;
  } else if (obj.byKind) {
    Widgets.ObjectRenderers.byKind[obj.byKind] = constructor;
  } else {
    throw new Error("You are adding an object renderer without any byClass or " +
                    "byKind property.");
  }
};

/**
 * The widget used for displaying Date objects.
 */
Widgets.ObjectRenderers.add({
  byClass: "Date",

  render: function () {
    let {preview} = this.objectActor;
    this.element = this.el("span.class-" + this.objectActor.class);

    let anchorText = this.objectActor.class;
    let anchorClass = "cm-variable";
    if (preview && "timestamp" in preview && typeof preview.timestamp != "number") {
      anchorText = new Date(preview.timestamp).toString(); // invalid date
      anchorClass = "";
    }

    this._anchor(anchorText, { className: anchorClass });

    if (!preview || !("timestamp" in preview) || typeof preview.timestamp != "number") {
      return;
    }

    this._text(" ");

    let elem = this.el("span.cm-string-2", new Date(preview.timestamp).toISOString());
    this.element.appendChild(elem);
  },
});

/**
 * The widget used for displaying Function objects.
 */
Widgets.ObjectRenderers.add({
  byClass: "Function",

  render: function () {
    let grip = this.objectActor;
    this.element = this.el("span.class-" + this.objectActor.class);

    // TODO: Bug 948484 - support arrow functions and ES6 generators
    let name = grip.userDisplayName || grip.displayName || grip.name || "";
    name = VariablesView.getString(name, { noStringQuotes: true });

    if (this.options.concise) {
      this._anchor(name || "function", {
        className: name ? "cm-variable" : "cm-keyword",
      });
      if (!name) {
        this._text(" ");
      }
    } else if (name) {
      this.element.appendChild(this.el("span.cm-keyword", "function"));
      this._text(" ");
      this._anchor(name, { className: "cm-variable" });
    } else {
      this._anchor("function", { className: "cm-keyword" });
      this._text(" ");
    }

    this._text("(");

    // TODO: Bug 948489 - Support functions with destructured parameters and
    // rest parameters
    let params = grip.parameterNames || [];
    let shown = 0;
    for (let param of params) {
      if (shown > 0) {
        this._text(", ");
      }
      this.element.appendChild(this.el("span.cm-def", param));
      shown++;
    }

    this._text(")");
  },

  _onClick: function () {
    let location = this.objectActor.location;
    let url = location && location.url;
    if (url && !IGNORED_SOURCE_URLS.includes(url)) {
      this.output.openLocationInDebugger(location);
    } else {
      this.openObjectInVariablesView();
    }
  }
}); // Widgets.ObjectRenderers.byClass.Function

/**
 * The widget used for displaying ArrayLike objects.
 */
Widgets.ObjectRenderers.add({
  byKind: "ArrayLike",

  render: function () {
    let {preview} = this.objectActor;
    let {items} = preview;
    this.element = this.el("span.kind-" + preview.kind);

    this._anchor(this.objectActor.class, { className: "cm-variable" });

    if (!items || this.options.concise) {
      this._text("[");
      this.element.appendChild(this.el("span.cm-number", preview.length));
      this._text("]");
      return this;
    }

    this._text(" [ ");

    let isFirst = true;
    let emptySlots = 0;
    // A helper that renders a comma between items if isFirst == false.
    let renderSeparator = () => !isFirst && this._text(", ");

    for (let item of items) {
      if (item === null) {
        emptySlots++;
      } else {
        renderSeparator();
        isFirst = false;

        if (emptySlots) {
          this._renderEmptySlots(emptySlots);
          emptySlots = 0;
        }

        let elem = this.message._renderValueGrip(item, { concise: true, shorten: true });
        this.element.appendChild(elem);
      }
    }

    if (emptySlots) {
      renderSeparator();
      this._renderEmptySlots(emptySlots, false);
    }

    let shown = items.length;
    if (shown < preview.length) {
      this._text(", ");

      let n = preview.length - shown;
      let str = VariablesView.stringifiers._getNMoreString(n);
      this._anchor(str);
    }

    this._text(" ]");

    return this;
  },

  _renderEmptySlots: function (numSlots, appendComma = true) {
    let slotLabel = l10n.getStr("emptySlotLabel");
    let slotText = PluralForm.get(numSlots, slotLabel);
    this._text("<" + slotText.replace("#1", numSlots) + ">");
    if (appendComma) {
      this._text(", ");
    }
  },

}); // Widgets.ObjectRenderers.byKind.ArrayLike

/**
 * The widget used for displaying MapLike objects.
 */
Widgets.ObjectRenderers.add({
  byKind: "MapLike",

  render: function () {
    let {preview} = this.objectActor;
    let {entries} = preview;

    let container = this.element = this.el("span.kind-" + preview.kind);
    this._anchor(this.objectActor.class, { className: "cm-variable" });

    if (!entries || this.options.concise) {
      if (typeof preview.size == "number") {
        this._text("[");
        container.appendChild(this.el("span.cm-number", preview.size));
        this._text("]");
      }
      return;
    }

    this._text(" { ");

    let shown = 0;
    for (let [key, value] of entries) {
      if (shown > 0) {
        this._text(", ");
      }

      let keyElem = this.message._renderValueGrip(key, {
        concise: true,
        noStringQuotes: true,
      });

      // Strings are property names.
      if (keyElem.classList && keyElem.classList.contains("console-string")) {
        keyElem.classList.remove("console-string");
        keyElem.classList.add("cm-property");
      }

      container.appendChild(keyElem);

      this._text(": ");

      let valueElem = this.message._renderValueGrip(value, { concise: true });
      container.appendChild(valueElem);

      shown++;
    }

    if (typeof preview.size == "number" && shown < preview.size) {
      this._text(", ");

      let n = preview.size - shown;
      let str = VariablesView.stringifiers._getNMoreString(n);
      this._anchor(str);
    }

    this._text(" }");
  },
}); // Widgets.ObjectRenderers.byKind.MapLike

/**
 * The widget used for displaying objects with a URL.
 */
Widgets.ObjectRenderers.add({
  byKind: "ObjectWithURL",

  render: function () {
    this.element = this._renderElement(this.objectActor,
                                       this.objectActor.preview.url);
  },

  _renderElement: function (objectActor, url) {
    let container = this.el("span.kind-" + objectActor.preview.kind);

    this._anchor(objectActor.class, {
      className: "cm-variable",
      appendTo: container,
    });

    if (!VariablesView.isFalsy({ value: url })) {
      this._text(" \u2192 ", container);
      let shortUrl = getSourceNames(url)[this.options.concise ? "short" : "long"];
      this._anchor(shortUrl, { href: url, appendTo: container });
    }

    return container;
  },
}); // Widgets.ObjectRenderers.byKind.ObjectWithURL

/**
 * The widget used for displaying objects with a string next to them.
 */
Widgets.ObjectRenderers.add({
  byKind: "ObjectWithText",

  render: function () {
    let {preview} = this.objectActor;
    this.element = this.el("span.kind-" + preview.kind);

    this._anchor(this.objectActor.class, { className: "cm-variable" });

    if (!this.options.concise) {
      this._text(" ");
      this.element.appendChild(this.el("span.theme-fg-color6",
                                       VariablesView.getString(preview.text)));
    }
  },
});

/**
 * The widget used for displaying DOM event previews.
 */
Widgets.ObjectRenderers.add({
  byKind: "DOMEvent",

  render: function () {
    let {preview} = this.objectActor;

    let container = this.element = this.el("span.kind-" + preview.kind);

    this._anchor(preview.type || this.objectActor.class,
                 { className: "cm-variable" });

    if (this.options.concise) {
      return;
    }

    if (preview.eventKind == "key" && preview.modifiers &&
        preview.modifiers.length) {
      this._text(" ");

      let mods = 0;
      for (let mod of preview.modifiers) {
        if (mods > 0) {
          this._text("-");
        }
        container.appendChild(this.el("span.cm-keyword", mod));
        mods++;
      }
    }

    this._text(" { ");

    let shown = 0;
    if (preview.target) {
      container.appendChild(this.el("span.cm-property", "target"));
      this._text(": ");
      let target = this.message._renderValueGrip(preview.target, { concise: true });
      container.appendChild(target);
      shown++;
    }

    for (let key of Object.keys(preview.properties || {})) {
      if (shown > 0) {
        this._text(", ");
      }

      container.appendChild(this.el("span.cm-property", key));
      this._text(": ");

      let value = preview.properties[key];
      let valueElem = this.message._renderValueGrip(value, { concise: true });
      container.appendChild(valueElem);

      shown++;
    }

    this._text(" }");
  },
}); // Widgets.ObjectRenderers.byKind.DOMEvent

/**
 * The widget used for displaying DOM node previews.
 */
Widgets.ObjectRenderers.add({
  byKind: "DOMNode",

  canRender: function (objectActor) {
    let {preview} = objectActor;
    if (!preview) {
      return false;
    }

    switch (preview.nodeType) {
      case nodeConstants.DOCUMENT_NODE:
      case nodeConstants.ATTRIBUTE_NODE:
      case nodeConstants.TEXT_NODE:
      case nodeConstants.COMMENT_NODE:
      case nodeConstants.DOCUMENT_FRAGMENT_NODE:
      case nodeConstants.ELEMENT_NODE:
        return true;
      default:
        return false;
    }
  },

  render: function () {
    const {preview} = this.objectActor;

    switch (preview.nodeType) {
      case nodeConstants.DOCUMENT_NODE:
        this._renderDocumentNode();
        break;
      case nodeConstants.ATTRIBUTE_NODE: {
        this.element = this.el("span.attributeNode.kind-" + preview.kind);
        let attr = this._renderAttributeNode(preview.nodeName, preview.value, true);
        this.element.appendChild(attr);
        break;
      }
      case nodeConstants.TEXT_NODE:
        this._renderTextNode();
        break;
      case nodeConstants.COMMENT_NODE:
        this._renderCommentNode();
        break;
      case nodeConstants.DOCUMENT_FRAGMENT_NODE:
        this._renderDocumentFragmentNode();
        break;
      case nodeConstants.ELEMENT_NODE:
        this._renderElementNode();
        break;
      default:
        throw new Error("Unsupported nodeType: " + preview.nodeType);
    }
  },

  _renderDocumentNode: function () {
    let fn =
      Widgets.ObjectRenderers.byKind.ObjectWithURL.prototype._renderElement;
    this.element = fn.call(this, this.objectActor,
                           this.objectActor.preview.location);
    this.element.classList.add("documentNode");
  },

  _renderAttributeNode: function (nodeName, nodeValue, addLink) {
    let value = VariablesView.getString(nodeValue, { noStringQuotes: true });

    let fragment = this.document.createDocumentFragment();
    if (addLink) {
      this._anchor(nodeName, { className: "cm-attribute", appendTo: fragment });
    } else {
      fragment.appendChild(this.el("span.cm-attribute", nodeName));
    }

    this._text("=\"", fragment);
    fragment.appendChild(this.el("span.theme-fg-color6", escapeHTML(value)));
    this._text("\"", fragment);

    return fragment;
  },

  _renderTextNode: function () {
    let {preview} = this.objectActor;
    this.element = this.el("span.textNode.kind-" + preview.kind);

    this._anchor(preview.nodeName, { className: "cm-variable" });
    this._text(" ");

    let text = VariablesView.getString(preview.textContent);
    this.element.appendChild(this.el("span.console-string", text));
  },

  _renderCommentNode: function () {
    let {preview} = this.objectActor;
    let comment = "<!-- " + VariablesView.getString(preview.textContent, {
      noStringQuotes: true,
    }) + " -->";

    this.element = this._anchor(comment, {
      className: "kind-" + preview.kind + " commentNode cm-comment",
    });
  },

  _renderDocumentFragmentNode: function () {
    let {preview} = this.objectActor;
    let {childNodes} = preview;
    let container = this.element = this.el("span.documentFragmentNode.kind-" +
                                           preview.kind);

    this._anchor(this.objectActor.class, { className: "cm-variable" });

    if (!childNodes || this.options.concise) {
      this._text("[");
      container.appendChild(this.el("span.cm-number", preview.childNodesLength));
      this._text("]");
      return;
    }

    this._text(" [ ");

    let shown = 0;
    for (let item of childNodes) {
      if (shown > 0) {
        this._text(", ");
      }

      let elem = this.message._renderValueGrip(item, { concise: true });
      container.appendChild(elem);
      shown++;
    }

    if (shown < preview.childNodesLength) {
      this._text(", ");

      let n = preview.childNodesLength - shown;
      let str = VariablesView.stringifiers._getNMoreString(n);
      this._anchor(str);
    }

    this._text(" ]");
  },

  _renderElementNode: function () {
    let {attributes, nodeName} = this.objectActor.preview;

    this.element = this.el("span." + "kind-" + this.objectActor.preview.kind +
                           ".elementNode");

    this._text("<");
    let openTag = this.el("span.cm-tag");
    this.element.appendChild(openTag);

    let tagName = this._anchor(nodeName, {
      className: "cm-tag",
      appendTo: openTag
    });

    if (this.options.concise) {
      if (attributes.id) {
        tagName.appendChild(this.el("span.cm-attribute", "#" + attributes.id));
      }
      if (attributes.class) {
        const joinedClasses = "." + attributes.class.split(/\s+/g).join(".");
        tagName.appendChild(this.el("span.cm-attribute", joinedClasses));
      }
    } else {
      for (let name of Object.keys(attributes)) {
        let attr = this._renderAttributeNode(" " + name, attributes[name]);
        this.element.appendChild(attr);
      }
    }

    this._text(">");

    // Register this widget in the owner message so that it gets destroyed when
    // the message is destroyed.
    this.message.widgets.add(this);

    this.linkToInspector().catch(console.error);
  },

  /**
   * If the DOMNode being rendered can be highlit in the page, this function
   * will attach mouseover/out event listeners to do so, and the inspector icon
   * to open the node in the inspector.
   * @return a promise that resolves when the node has been linked to the
   * inspector, or rejects if it wasn't (either if no toolbox could be found to
   * access the inspector, or if the node isn't present in the inspector, i.e.
   * if the node is in a DocumentFragment or not part of the tree, or not of
   * type nodeConstants.ELEMENT_NODE).
   */
  linkToInspector: Task.async(function* () {
    if (this._linkedToInspector) {
      return;
    }

    // Checking the node type
    if (this.objectActor.preview.nodeType !== nodeConstants.ELEMENT_NODE) {
      throw new Error("The object cannot be linked to the inspector as it " +
        "isn't an element node");
    }

    // Checking the presence of a toolbox
    let target = this.message.output.toolboxTarget;
    this.toolbox = gDevTools.getToolbox(target);
    if (!this.toolbox) {
      // In cases like the browser console, there is no toolbox.
      return;
    }

    // Checking that the inspector supports the node
    yield this.toolbox.initInspector();
    this._nodeFront = yield this.toolbox.walker.getNodeActorFromObjectActor(
      this.objectActor.actor);
    if (!this._nodeFront) {
      throw new Error("The object cannot be linked to the inspector, the " +
        "corresponding nodeFront could not be found");
    }

    // At this stage, the message may have been cleared already
    if (!this.document) {
      throw new Error("The object cannot be linked to the inspector, the " +
        "message was got cleared away");
    }

    // Check it again as this method is async!
    if (this._linkedToInspector) {
      return;
    }
    this._linkedToInspector = true;

    this.highlightDomNode = this.highlightDomNode.bind(this);
    this.element.addEventListener("mouseover", this.highlightDomNode);
    this.unhighlightDomNode = this.unhighlightDomNode.bind(this);
    this.element.addEventListener("mouseout", this.unhighlightDomNode);

    this._openInspectorNode = this._anchor("", {
      className: "open-inspector",
      onClick: this.openNodeInInspector.bind(this)
    });
    this._openInspectorNode.title = l10n.getStr("openNodeInInspector");
  }),

  /**
   * Highlight the DOMNode corresponding to the ObjectActor in the page.
   * @return a promise that resolves when the node has been highlighted, or
   * rejects if the node cannot be highlighted (detached from the DOM)
   */
  highlightDomNode: Task.async(function* () {
    yield this.linkToInspector();
    let isAttached = yield this.toolbox.walker.isInDOMTree(this._nodeFront);
    if (isAttached) {
      yield this.toolbox.highlighterUtils.highlightNodeFront(this._nodeFront);
    } else {
      throw new Error("Node is not attached.");
    }
  }),

  /**
   * Unhighlight a previously highlit node
   * @see highlightDomNode
   * @return a promise that resolves when the highlighter has been hidden
   */
  unhighlightDomNode: function () {
    return this.linkToInspector().then(() => {
      return this.toolbox.highlighterUtils.unhighlight();
    }).catch(console.error);
  },

  /**
   * Open the DOMNode corresponding to the ObjectActor in the inspector panel
   * @return a promise that resolves when the inspector has been switched to
   * and the node has been selected, or rejects if the node cannot be selected
   * (detached from the DOM). Note that in any case, the inspector panel will
   * be switched to.
   */
  openNodeInInspector: Task.async(function* () {
    yield this.linkToInspector();
    yield this.toolbox.selectTool("inspector");

    let isAttached = yield this.toolbox.walker.isInDOMTree(this._nodeFront);
    if (isAttached) {
      let onReady = defer();
      this.toolbox.inspector.once("inspector-updated", onReady.resolve);
      yield this.toolbox.selection.setNodeFront(this._nodeFront, "console");
      yield onReady.promise;
    } else {
      throw new Error("Node is not attached.");
    }
  }),

  destroy: function () {
    if (this.toolbox && this._nodeFront) {
      this.element.removeEventListener("mouseover", this.highlightDomNode);
      this.element.removeEventListener("mouseout", this.unhighlightDomNode);
      this._openInspectorNode.removeEventListener("mousedown",
                                                  this.openNodeInInspector,
                                                  true);

      if (this._linkedToInspector) {
        this.unhighlightDomNode().then(() => {
          this.toolbox = null;
          this._nodeFront = null;
        });
      } else {
        this.toolbox = null;
        this._nodeFront = null;
      }
    }
  },
}); // Widgets.ObjectRenderers.byKind.DOMNode

/**
 * The widget user for displaying Promise objects.
 */
Widgets.ObjectRenderers.add({
  byClass: "Promise",

  render: function () {
    let { ownProperties, safeGetterValues } = this.objectActor.preview || {};
    if ((!ownProperties && !safeGetterValues) || this.options.concise) {
      this._renderConciseObject();
      return;
    }

    this._renderObjectPrefix();
    let container = this.element;
    let addedPromiseInternalProps = false;

    if (this.objectActor.promiseState) {
      const { state, value, reason } = this.objectActor.promiseState;

      this._renderObjectProperty("<state>", state, container, false);
      addedPromiseInternalProps = true;

      if (state == "fulfilled") {
        this._renderObjectProperty("<value>", value, container, true);
      } else if (state == "rejected") {
        this._renderObjectProperty("<reason>", reason, container, true);
      }
    }

    this._renderObjectProperties(container, addedPromiseInternalProps);
    this._renderObjectSuffix();
  }
}); // Widgets.ObjectRenderers.byClass.Promise

/*
 * A renderer used for wrapped primitive objects.
 */

function WrappedPrimitiveRenderer() {
  let { ownProperties, safeGetterValues } = this.objectActor.preview || {};
  if ((!ownProperties && !safeGetterValues) || this.options.concise) {
    this._renderConciseObject();
    return;
  }

  this._renderObjectPrefix();

  let elem =
      this.message._renderValueGrip(this.objectActor.preview.wrappedValue);
  this.element.appendChild(elem);

  this._renderObjectProperties(this.element, true);
  this._renderObjectSuffix();
}

/**
 * The widget used for displaying Boolean previews.
 */
Widgets.ObjectRenderers.add({
  byClass: "Boolean",

  render: WrappedPrimitiveRenderer,
});

/**
 * The widget used for displaying Number previews.
 */
Widgets.ObjectRenderers.add({
  byClass: "Number",

  render: WrappedPrimitiveRenderer,
});

/**
 * The widget used for displaying String previews.
 */
Widgets.ObjectRenderers.add({
  byClass: "String",

  render: WrappedPrimitiveRenderer,
});

/**
 * The widget used for displaying generic JS object previews.
 */
Widgets.ObjectRenderers.add({
  byKind: "Object",

  render: function () {
    let { ownProperties, safeGetterValues } = this.objectActor.preview || {};
    if ((!ownProperties && !safeGetterValues) || this.options.concise) {
      this._renderConciseObject();
      return;
    }

    this._renderObjectPrefix();
    this._renderObjectProperties(this.element, false);
    this._renderObjectSuffix();
  },
}); // Widgets.ObjectRenderers.byKind.Object

/**
 * The long string widget.
 *
 * @constructor
 * @param object message
 *        The owning message.
 * @param object longStringActor
 *        The LongStringActor to display.
 * @param object options
 *        Options, such as noStringQuotes
 */
Widgets.LongString = function (message, longStringActor, options) {
  Widgets.BaseWidget.call(this, message);
  this.longStringActor = longStringActor;
  this.noStringQuotes = (options && "noStringQuotes" in options) ?
    options.noStringQuotes : !this.message._quoteStrings;

  this._onClick = this._onClick.bind(this);
  this._onSubstring = this._onSubstring.bind(this);
};

Widgets.LongString.prototype = extend(Widgets.BaseWidget.prototype, {
  /**
   * The LongStringActor displayed by the widget.
   * @type object
   */
  longStringActor: null,

  render: function () {
    if (this.element) {
      return this;
    }

    let result = this.element = this.document.createElementNS(XHTML_NS, "span");
    result.className = "longString console-string";
    this._renderString(this.longStringActor.initial);
    result.appendChild(this._renderEllipsis());

    return this;
  },

  /**
   * Render the long string in the widget element.
   * @private
   * @param string str
   *        The string to display.
   */
  _renderString: function (str) {
    this.element.textContent = VariablesView.getString(str, {
      noStringQuotes: this.noStringQuotes,
      noEllipsis: true,
    });
  },

  /**
   * Render the anchor ellipsis that allows the user to expand the long string.
   *
   * @private
   * @return Element
   */
  _renderEllipsis: function () {
    let ellipsis = this.document.createElementNS(XHTML_NS, "a");
    ellipsis.className = "longStringEllipsis";
    ellipsis.textContent = l10n.getStr("longStringEllipsis");
    ellipsis.href = "#";
    ellipsis.draggable = false;
    this.message._addLinkCallback(ellipsis, this._onClick);

    return ellipsis;
  },

  /**
   * The click event handler for the ellipsis shown after the short string. This
   * function expands the element to show the full string.
   * @private
   */
  _onClick: function () {
    let longString = this.output.webConsoleClient.longString(this.longStringActor);
    let toIndex = Math.min(longString.length, MAX_LONG_STRING_LENGTH);

    longString.substring(longString.initial.length, toIndex, this._onSubstring);
  },

  /**
   * The longString substring response callback.
   *
   * @private
   * @param object response
   *        Response packet.
   */
  _onSubstring: function (response) {
    if (response.error) {
      console.error("LongString substring failure: " + response.error);
      return;
    }

    this.element.lastChild.remove();
    this.element.classList.remove("longString");

    this._renderString(this.longStringActor.initial + response.substring);

    this.output.owner.emit("new-messages", new Set([{
      update: true,
      node: this.message.element,
      response: response,
    }]));

    let toIndex = Math.min(this.longStringActor.length, MAX_LONG_STRING_LENGTH);
    if (toIndex != this.longStringActor.length) {
      this._logWarningAboutStringTooLong();
    }
  },

  /**
   * Inform user that the string he tries to view is too long.
   * @private
   */
  _logWarningAboutStringTooLong: function () {
    let msg = new Messages.Simple(l10n.getStr("longStringTooLong"), {
      category: "output",
      severity: "warning",
    });
    this.output.addMessage(msg);
  },
}); // Widgets.LongString.prototype

/**
 * The stacktrace widget.
 *
 * @constructor
 * @extends Widgets.BaseWidget
 * @param object message
 *        The owning message.
 * @param array stacktrace
 *        The stacktrace to display, array of frames as supplied by the server,
 *        over the remote protocol.
 */
Widgets.Stacktrace = function (message, stacktrace) {
  Widgets.BaseWidget.call(this, message);
  this.stacktrace = stacktrace;
};

Widgets.Stacktrace.prototype = extend(Widgets.BaseWidget.prototype, {
  /**
   * The stackframes received from the server.
   * @type array
   */
  stacktrace: null,

  render() {
    if (this.element) {
      return this;
    }

    let result = this.element = this.document.createElementNS(XHTML_NS, "div");
    result.className = "stacktrace devtools-monospace";

    if (this.stacktrace) {
      const target = this.message.output.toolboxTarget;
      const toolbox = gDevTools.getToolbox(target);
      this.output.owner.ReactDOM.render(this.output.owner.StackTraceView({
        stacktrace: this.stacktrace,
        onViewSourceInDebugger: frame => this.output.openLocationInDebugger(frame),
        sourceMapService: toolbox ? toolbox.sourceMapURLService : null,
      }), result);
    }

    return this;
  }
});

/**
 * The table widget.
 *
 * @constructor
 * @extends Widgets.BaseWidget
 * @param object message
 *        The owning message.
 * @param array data
 *        Array of objects that holds the data to log in the table.
 * @param object columns
 *        Object containing the key value pair of the id and display name for
 *        the columns in the table.
 */
Widgets.Table = function (message, data, columns) {
  Widgets.BaseWidget.call(this, message);
  this.data = data;
  this.columns = columns;
};

Widgets.Table.prototype = extend(Widgets.BaseWidget.prototype, {
  /**
   * Array of objects that holds the data to output in the table.
   * @type array
   */
  data: null,

  /**
   * Object containing the key value pair of the id and display name for
   * the columns in the table.
   * @type object
   */
  columns: null,

  render: function () {
    if (this.element) {
      return this;
    }

    let result = this.element = this.document.createElementNS(XHTML_NS, "div");
    result.className = "consoletable devtools-monospace";

    this.table = new TableWidget(result, {
      wrapTextInElements: true,
      initialColumns: this.columns,
      uniqueId: "_index",
      firstColumn: "_index"
    });

    for (let row of this.data) {
      this.table.push(row);
    }

    return this;
  }
}); // Widgets.Table.prototype

function gSequenceId() {
  return gSequenceId.n++;
}
gSequenceId.n = 0;

exports.ConsoleOutput = ConsoleOutput;
exports.Messages = Messages;
exports.Widgets = Widgets;
PK
!<.�s�PPPP@chrome/devtools/modules/devtools/client/webconsole/hudservice.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var Services = require("Services");
loader.lazyRequireGetter(this, "Utils", "devtools/client/webconsole/utils", true);
loader.lazyRequireGetter(this, "extend", "devtools/shared/extend", true);
loader.lazyRequireGetter(this, "TargetFactory", "devtools/client/framework/target", true);
loader.lazyRequireGetter(this, "gDevToolsBrowser", "devtools/client/framework/devtools-browser", true);
loader.lazyRequireGetter(this, "Tools", "devtools/client/definitions", true);
loader.lazyRequireGetter(this, "Telemetry", "devtools/client/shared/telemetry");
loader.lazyRequireGetter(this, "WebConsoleFrame", "devtools/client/webconsole/webconsole", true);
loader.lazyRequireGetter(this, "NewWebConsoleFrame", "devtools/client/webconsole/new-webconsole", true);
loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true);
loader.lazyRequireGetter(this, "DebuggerClient", "devtools/shared/client/debugger-client", true);
loader.lazyRequireGetter(this, "showDoorhanger", "devtools/client/shared/doorhanger", true);
loader.lazyRequireGetter(this, "viewSource", "devtools/client/shared/view-source");
loader.lazyRequireGetter(this, "l10n", "devtools/client/webconsole/webconsole-l10n");
const BC_WINDOW_FEATURES = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";

// The preference prefix for all of the Browser Console filters.
const BC_FILTER_PREFS_PREFIX = "devtools.browserconsole.filter.";

var gHudId = 0;

function HUD_SERVICE() {
  this.consoles = new Map();
  this.lastFinishedRequest = { callback: null };
}

HUD_SERVICE.prototype =
{
  _browserConsoleID: null,
  _browserConsoleInitializing: null,

  /**
   * Keeps a reference for each Web Console / Browser Console that is created.
   * @type Map
   */
  consoles: null,

  _browerConsoleSessionState: false,
  storeBrowserConsoleSessionState() {
    this._browerConsoleSessionState = !!this.getBrowserConsole();
  },
  getBrowserConsoleSessionState() {
    return this._browerConsoleSessionState;
  },

  /**
   * Assign a function to this property to listen for every request that
   * completes. Used by unit tests. The callback takes one argument: the HTTP
   * activity object as received from the remote Web Console.
   *
   * @type object
   *       Includes a property named |callback|. Assign the function to the
   *       |callback| property of this object.
   */
  lastFinishedRequest: null,

  /**
   * Get the current context, which is the main application window.
   *
   * @returns nsIDOMWindow
   */
  currentContext() {
    return Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
  },

  /**
   * Open a Web Console for the given target.
   *
   * @see devtools/framework/target.js for details about targets.
   *
   * @param object target
   *        The target that the web console will connect to.
   * @param nsIDOMWindow iframeWindow
   *        The window where the web console UI is already loaded.
   * @param nsIDOMWindow chromeWindow
   *        The window of the web console owner.
   * @return object
   *         A promise object for the opening of the new WebConsole instance.
   */
  openWebConsole(target, iframeWindow, chromeWindow) {
    let hud = new WebConsole(target, iframeWindow, chromeWindow);
    this.consoles.set(hud.hudId, hud);
    return hud.init();
  },

  /**
   * Open a Browser Console for the given target.
   *
   * @see devtools/framework/target.js for details about targets.
   *
   * @param object target
   *        The target that the browser console will connect to.
   * @param nsIDOMWindow iframeWindow
   *        The window where the browser console UI is already loaded.
   * @param nsIDOMWindow chromeWindow
   *        The window of the browser console owner.
   * @return object
   *         A promise object for the opening of the new BrowserConsole instance.
   */
  openBrowserConsole(target, iframeWindow, chromeWindow) {
    let hud = new BrowserConsole(target, iframeWindow, chromeWindow);
    this._browserConsoleID = hud.hudId;
    this.consoles.set(hud.hudId, hud);
    return hud.init();
  },

  /**
   * Returns the Web Console object associated to a content window.
   *
   * @param nsIDOMWindow contentWindow
   * @returns object
   */
  getHudByWindow(contentWindow) {
    for (let [, hud] of this.consoles) {
      let target = hud.target;
      if (target && target.tab && target.window === contentWindow) {
        return hud;
      }
    }
    return null;
  },

  /**
   * Returns the console instance for a given id.
   *
   * @param string id
   * @returns Object
   */
  getHudReferenceById(id) {
    return this.consoles.get(id);
  },

  /**
   * Find if there is a Web Console open for the current tab and return the
   * instance.
   * @return object|null
   *         The WebConsole object or null if the active tab has no open Web
   *         Console.
   */
  getOpenWebConsole() {
    let tab = this.currentContext().gBrowser.selectedTab;
    if (!tab || !TargetFactory.isKnownTab(tab)) {
      return null;
    }
    let target = TargetFactory.forTab(tab);
    let toolbox = gDevTools.getToolbox(target);
    let panel = toolbox ? toolbox.getPanel("webconsole") : null;
    return panel ? panel.hud : null;
  },

  /**
   * Toggle the Browser Console.
   */
  async toggleBrowserConsole() {
    if (this._browserConsoleID) {
      let hud = this.getHudReferenceById(this._browserConsoleID);
      return hud.destroy();
    }

    if (this._browserConsoleInitializing) {
      return this._browserConsoleInitializing;
    }

    async function connect() {
      // Ensure that the root actor and the tab actors have been registered on the
      // DebuggerServer, so that the Browser Console can retrieve the console actors.
      // (See Bug 1416105 for rationale).
      DebuggerServer.init();
      DebuggerServer.registerActors({ root: true, tab: true });

      DebuggerServer.allowChromeProcess = true;

      let client = new DebuggerClient(DebuggerServer.connectPipe());
      await client.connect();
      let response = await client.getProcess();
      return { form: response.form, client, chrome: true, isTabActor: true };
    }

    async function openWindow(t) {
      let browserConsoleURL = Tools.webConsole.browserConsoleURL;
      let win = Services.ww.openWindow(null, browserConsoleURL, "_blank",
                                       BC_WINDOW_FEATURES, null);
      await new Promise(resolve => {
        win.addEventListener("DOMContentLoaded", resolve, {once: true});
      });

      win.document.title = l10n.getStr("browserConsole.title");

      if (browserConsoleURL === Tools.webConsole.oldWebConsoleURL) {
        return {iframeWindow: win, chromeWindow: win};
      }

      let iframe = win.document.querySelector("iframe");
      await new Promise(resolve => {
        iframe.addEventListener("DOMContentLoaded", resolve, {once: true});
      });

      return {iframeWindow: iframe.contentWindow, chromeWindow: win};
    }

    // Temporarily cache the async startup sequence so that if toggleBrowserConsole
    // gets called again we can return this console instead of opening another one.
    this._browserConsoleInitializing = (async () => {
      let connection = await connect();
      let target = await TargetFactory.forRemoteTab(connection);
      let {iframeWindow, chromeWindow} = await openWindow(target);
      let browserConsole =
        await this.openBrowserConsole(target, iframeWindow, chromeWindow);
      return browserConsole;
    })();

    let browserConsole = await this._browserConsoleInitializing;
    this._browserConsoleInitializing = null;
    return browserConsole;
  },

  /**
   * Opens or focuses the Browser Console.
   */
  openBrowserConsoleOrFocus() {
    let hud = this.getBrowserConsole();
    if (hud) {
      hud.iframeWindow.focus();
      return Promise.resolve(hud);
    }

    return this.toggleBrowserConsole();
  },

  /**
   * Get the Browser Console instance, if open.
   *
   * @return object|null
   *         A BrowserConsole instance or null if the Browser Console is not
   *         open.
   */
  getBrowserConsole() {
    return this.getHudReferenceById(this._browserConsoleID);
  },
};

/**
 * A WebConsole instance is an interactive console initialized *per target*
 * that displays console log data as well as provides an interactive terminal to
 * manipulate the target's document content.
 *
 * This object only wraps the iframe that holds the Web Console UI. This is
 * meant to be an integration point between the Firefox UI and the Web Console
 * UI and features.
 *
 * @constructor
 * @param object target
 *        The target that the web console will connect to.
 * @param nsIDOMWindow iframeWindow
 *        The window where the web console UI is already loaded.
 * @param nsIDOMWindow chromeWindow
 *        The window of the web console owner.
 */
function WebConsole(target, iframeWindow, chromeWindow) {
  this.iframeWindow = iframeWindow;
  this.chromeWindow = chromeWindow;
  this.hudId = "hud_" + ++gHudId;
  this.target = target;
  this.browserWindow = this.chromeWindow.top;
  let element = this.browserWindow.document.documentElement;
  if (element.getAttribute("windowtype") != gDevTools.chromeWindowType) {
    this.browserWindow = HUDService.currentContext();
  }
  if (iframeWindow.location.href === Tools.webConsole.newWebConsoleURL) {
    this.ui = new NewWebConsoleFrame(this);
  } else {
    this.ui = new WebConsoleFrame(this);
  }
}
WebConsole.prototype = {
  iframeWindow: null,
  chromeWindow: null,
  browserWindow: null,
  hudId: null,
  target: null,
  ui: null,
  _browserConsole: false,
  _destroyer: null,

  /**
   * Getter for a function to to listen for every request that completes. Used
   * by unit tests. The callback takes one argument: the HTTP activity object as
   * received from the remote Web Console.
   *
   * @type function
   */
  get lastFinishedRequestCallback() {
    return HUDService.lastFinishedRequest.callback;
  },

  /**
   * Getter for the window that can provide various utilities that the web
   * console makes use of, like opening links, managing popups, etc.  In
   * most cases, this will be |this.browserWindow|, but in some uses (such as
   * the Browser Toolbox), there is no browser window, so an alternative window
   * hosts the utilities there.
   * @type nsIDOMWindow
   */
  get chromeUtilsWindow() {
    if (this.browserWindow) {
      return this.browserWindow;
    }
    return this.chromeWindow.top;
  },

  /**
   * Getter for the xul:popupset that holds any popups we open.
   * @type nsIDOMElement
   */
  get mainPopupSet() {
    return this.chromeUtilsWindow.document.getElementById("mainPopupSet");
  },

  /**
   * Getter for the output element that holds messages we display.
   * @type nsIDOMElement
   */
  get outputNode() {
    return this.ui ? this.ui.outputNode : null;
  },

  get gViewSourceUtils() {
    return this.chromeUtilsWindow.gViewSourceUtils;
  },

  /**
   * Initialize the Web Console instance.
   *
   * @return object
   *         A promise for the initialization.
   */
  init() {
    return this.ui.init().then(() => this);
  },

  /**
   * The JSTerm object that manages the console's input.
   * @see webconsole.js::JSTerm
   * @type object
   */
  get jsterm() {
    return this.ui ? this.ui.jsterm : null;
  },

  /**
   * The clear output button handler.
   * @private
   */
  _onClearButton() {
    if (this.target.isLocalTab) {
      gDevToolsBrowser.getDeveloperToolbar(this.browserWindow)
        .resetErrorsCount(this.target.tab);
    }
  },

  /**
   * Alias for the WebConsoleFrame.setFilterState() method.
   * @see webconsole.js::WebConsoleFrame.setFilterState()
   */
  setFilterState() {
    this.ui && this.ui.setFilterState.apply(this.ui, arguments);
  },

  /**
   * Open a link in a new tab.
   *
   * @param string link
   *        The URL you want to open in a new tab.
   */
  openLink(link, e) {
    let isOSX = Services.appinfo.OS == "Darwin";
    if (e && (e.button === 1 || (e.button === 0 && (isOSX ? e.metaKey : e.ctrlKey)))) {
      this.chromeUtilsWindow.openUILinkIn(link, "tabshifted");
    } else {
      this.chromeUtilsWindow.openUILinkIn(link, "tab");
    }
  },

  /**
   * Open a link in Firefox's view source.
   *
   * @param string sourceURL
   *        The URL of the file.
   * @param integer sourceLine
   *        The line number which should be highlighted.
   */
  viewSource(sourceURL, sourceLine) {
    this.gViewSourceUtils.viewSource({ URL: sourceURL, lineNumber: sourceLine || 0 });
  },

  /**
   * Tries to open a Stylesheet file related to the web page for the web console
   * instance in the Style Editor. If the file is not found, it is opened in
   * source view instead.
   *
   * Manually handle the case where toolbox does not exist (Browser Console).
   *
   * @param string sourceURL
   *        The URL of the file.
   * @param integer sourceLine
   *        The line number which you want to place the caret.
   */
  viewSourceInStyleEditor(sourceURL, sourceLine) {
    let toolbox = gDevTools.getToolbox(this.target);
    if (!toolbox) {
      this.viewSource(sourceURL, sourceLine);
      return;
    }
    toolbox.viewSourceInStyleEditor(sourceURL, sourceLine);
  },

  /**
   * Tries to open a JavaScript file related to the web page for the web console
   * instance in the Script Debugger. If the file is not found, it is opened in
   * source view instead.
   *
   * Manually handle the case where toolbox does not exist (Browser Console).
   *
   * @param string sourceURL
   *        The URL of the file.
   * @param integer sourceLine
   *        The line number which you want to place the caret.
   */
  viewSourceInDebugger(sourceURL, sourceLine) {
    let toolbox = gDevTools.getToolbox(this.target);
    if (!toolbox) {
      this.viewSource(sourceURL, sourceLine);
      return;
    }
    toolbox.viewSourceInDebugger(sourceURL, sourceLine).then(() => {
      this.ui.emit("source-in-debugger-opened");
    });
  },

  /**
   * Tries to open a JavaScript file related to the web page for the web console
   * instance in the corresponding Scratchpad.
   *
   * @param string sourceURL
   *        The URL of the file which corresponds to a Scratchpad id.
   */
  viewSourceInScratchpad(sourceURL, sourceLine) {
    viewSource.viewSourceInScratchpad(sourceURL, sourceLine);
  },

  /**
   * Retrieve information about the JavaScript debugger's stackframes list. This
   * is used to allow the Web Console to evaluate code in the selected
   * stackframe.
   *
   * @return object|null
   *         An object which holds:
   *         - frames: the active ThreadClient.cachedFrames array.
   *         - selected: depth/index of the selected stackframe in the debugger
   *         UI.
   *         If the debugger is not open or if it's not paused, then |null| is
   *         returned.
   */
  getDebuggerFrames() {
    let toolbox = gDevTools.getToolbox(this.target);
    if (!toolbox) {
      return null;
    }
    let panel = toolbox.getPanel("jsdebugger");

    if (!panel) {
      return null;
    }

    return panel.getFrames();
  },

  /**
   * Retrieves the current selection from the Inspector, if such a selection
   * exists. This is used to pass the ID of the selected actor to the Web
   * Console server for the $0 helper.
   *
   * @return object|null
   *         A Selection referring to the currently selected node in the
   *         Inspector.
   *         If the inspector was never opened, or no node was ever selected,
   *         then |null| is returned.
   */
  getInspectorSelection() {
    let toolbox = gDevTools.getToolbox(this.target);
    if (!toolbox) {
      return null;
    }
    let panel = toolbox.getPanel("inspector");
    if (!panel || !panel.selection) {
      return null;
    }
    return panel.selection;
  },

  /**
   * Destroy the object. Call this method to avoid memory leaks when the Web
   * Console is closed.
   *
   * @return object
   *         A promise object that is resolved once the Web Console is closed.
   */
  async destroy() {
    if (this._destroyer) {
      return this._destroyer;
    }

    this._destroyer = (async () => {
      HUDService.consoles.delete(this.hudId);

      // The document may already be removed
      if (this.chromeUtilsWindow && this.mainPopupSet) {
        let popupset = this.mainPopupSet;
        let panels = popupset.querySelectorAll("panel[hudId=" + this.hudId + "]");
        for (let panel of panels) {
          panel.hidePopup();
        }
      }

      if (this.ui) {
        await this.ui.destroy();
      }

      if (!this._browserConsole) {
        try {
          await this.target.activeTab.focus();
        } catch (ex) {
          // Tab focus can fail if the tab or target is closed.
        }
      }

      let id = Utils.supportsString(this.hudId);
      Services.obs.notifyObservers(id, "web-console-destroyed");
    })();

    return this._destroyer;
  },
};

/**
 * A BrowserConsole instance is an interactive console initialized *per target*
 * that displays console log data as well as provides an interactive terminal to
 * manipulate the target's document content.
 *
 * This object only wraps the iframe that holds the Browser Console UI. This is
 * meant to be an integration point between the Firefox UI and the Browser Console
 * UI and features.
 *
 * @constructor
 * @param object target
 *        The target that the browser console will connect to.
 * @param nsIDOMWindow iframeWindow
 *        The window where the browser console UI is already loaded.
 * @param nsIDOMWindow chromeWindow
 *        The window of the browser console owner.
 */
function BrowserConsole() {
  WebConsole.apply(this, arguments);
  this._telemetry = new Telemetry();
}

BrowserConsole.prototype = extend(WebConsole.prototype, {
  _browserConsole: true,
  _bcInit: null,
  _bcDestroyer: null,

  $init: WebConsole.prototype.init,

  /**
   * Initialize the Browser Console instance.
   *
   * @return object
   *         A promise for the initialization.
   */
  init() {
    if (this._bcInit) {
      return this._bcInit;
    }

    // Only add the shutdown observer if we've opened a Browser Console window.
    ShutdownObserver.init();

    this.ui._filterPrefsPrefix = BC_FILTER_PREFS_PREFIX;

    let window = this.iframeWindow;

    // Make sure that the closing of the Browser Console window destroys this
    // instance.
    window.addEventListener("unload", () => {
      window.removeEventListener("focus", onFocus);
      this.destroy();
    }, {once: true});

    this._telemetry.toolOpened("browserconsole");

    // Create an onFocus handler just to display the dev edition promo.
    // This is to prevent race conditions in some environments.
    // Hook to display promotional Developer Edition doorhanger. Only displayed once.
    let onFocus = () => showDoorhanger({ window, type: "deveditionpromo" });
    window.addEventListener("focus", onFocus);

    this._bcInit = this.$init();
    return this._bcInit;
  },

  $destroy: WebConsole.prototype.destroy,

  /**
   * Destroy the object.
   *
   * @return object
   *         A promise object that is resolved once the Browser Console is closed.
   */
  destroy() {
    if (this._bcDestroyer) {
      return this._bcDestroyer;
    }

    this._bcDestroyer = (async () => {
      this._telemetry.toolClosed("browserconsole");
      await this.$destroy();
      await this.target.client.close();
      HUDService._browserConsoleID = null;
      this.chromeWindow.close();
    })();

    return this._bcDestroyer;
  },
});

const HUDService = new HUD_SERVICE();
exports.HUDService = HUDService;

/**
 * The ShutdownObserver listens for app shutdown and saves the current state
 * of the Browser Console for session restore.
 */
var ShutdownObserver = {
  _initialized: false,
  init() {
    if (this._initialized) {
      return;
    }

    Services.obs.addObserver(this, "quit-application-granted");

    this._initialized = true;
  },

  observe(message, topic) {
    if (topic == "quit-application-granted") {
      HUDService.storeBrowserConsoleSessionState();
      this.uninit();
    }
  },

  uninit() {
    Services.obs.removeObserver(this, "quit-application-granted");
  }
};
PK
!<ڈ�!r�r�<chrome/devtools/modules/devtools/client/webconsole/jsterm.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 {Utils: WebConsoleUtils} =
  require("devtools/client/webconsole/utils");
const defer = require("devtools/shared/defer");
const Debugger = require("Debugger");
const Services = require("Services");
const {KeyCodes} = require("devtools/client/shared/keycodes");

loader.lazyServiceGetter(this, "clipboardHelper",
                         "@mozilla.org/widget/clipboardhelper;1",
                         "nsIClipboardHelper");
loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
loader.lazyRequireGetter(this, "AutocompletePopup", "devtools/client/shared/autocomplete-popup");
loader.lazyRequireGetter(this, "ToolSidebar", "devtools/client/framework/sidebar", true);
loader.lazyRequireGetter(this, "Messages", "devtools/client/webconsole/console-output", true);
loader.lazyRequireGetter(this, "asyncStorage", "devtools/shared/async-storage");
loader.lazyRequireGetter(this, "EnvironmentClient", "devtools/shared/client/environment-client");
loader.lazyRequireGetter(this, "ObjectClient", "devtools/shared/client/object-client");
loader.lazyImporter(this, "VariablesView", "resource://devtools/client/shared/widgets/VariablesView.jsm");
loader.lazyImporter(this, "VariablesViewController", "resource://devtools/client/shared/widgets/VariablesViewController.jsm");
loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
loader.lazyRequireGetter(this, "NotificationBox", "devtools/client/shared/components/NotificationBox", true);
loader.lazyRequireGetter(this, "PriorityLevels", "devtools/client/shared/components/NotificationBox", true);

const l10n = require("devtools/client/webconsole/webconsole-l10n");

// Constants used for defining the direction of JSTerm input history navigation.
const HISTORY_BACK = -1;
const HISTORY_FORWARD = 1;

const XHTML_NS = "http://www.w3.org/1999/xhtml";

const HELP_URL = "https://developer.mozilla.org/docs/Tools/Web_Console/Helpers";

const VARIABLES_VIEW_URL = "chrome://devtools/content/shared/widgets/VariablesView.xul";

const PREF_INPUT_HISTORY_COUNT = "devtools.webconsole.inputHistoryCount";
const PREF_AUTO_MULTILINE = "devtools.webconsole.autoMultiline";

/**
 * Create a JSTerminal (a JavaScript command line). This is attached to an
 * existing HeadsUpDisplay (a Web Console instance). This code is responsible
 * with handling command line input, code evaluation and result output.
 *
 * @constructor
 * @param object webConsoleFrame
 *        The WebConsoleFrame object that owns this JSTerm instance.
 */
function JSTerm(webConsoleFrame) {
  this.hud = webConsoleFrame;
  this.hudId = this.hud.hudId;
  this.inputHistoryCount = Services.prefs.getIntPref(PREF_INPUT_HISTORY_COUNT);

  this.lastCompletion = { value: null };
  this._loadHistory();

  this._objectActorsInVariablesViews = new Map();

  this._keyPress = this._keyPress.bind(this);
  this._inputEventHandler = this._inputEventHandler.bind(this);
  this._focusEventHandler = this._focusEventHandler.bind(this);
  this._onKeypressInVariablesView = this._onKeypressInVariablesView.bind(this);
  this._blurEventHandler = this._blurEventHandler.bind(this);

  EventEmitter.decorate(this);
}

JSTerm.prototype = {
  SELECTED_FRAME: -1,

  /**
   * Load the console history from previous sessions.
   * @private
   */
  _loadHistory: function () {
    this.history = [];
    this.historyIndex = this.historyPlaceHolder = 0;

    this.historyLoaded = asyncStorage.getItem("webConsoleHistory")
      .then(value => {
        if (Array.isArray(value)) {
          // Since it was gotten asynchronously, there could be items already in
          // the history.  It's not likely but stick them onto the end anyway.
          this.history = value.concat(this.history);

          // Holds the number of entries in history. This value is incremented
          // in this.execute().
          this.historyIndex = this.history.length;

          // Holds the index of the history entry that the user is currently
          // viewing. This is reset to this.history.length when this.execute()
          // is invoked.
          this.historyPlaceHolder = this.history.length;
        }
      }, console.error);
  },

  /**
   * Clear the console history altogether.  Note that this will not affect
   * other consoles that are already opened (since they have their own copy),
   * but it will reset the array for all newly-opened consoles.
   * @returns Promise
   *          Resolves once the changes have been persisted.
   */
  clearHistory: function () {
    this.history = [];
    this.historyIndex = this.historyPlaceHolder = 0;
    return this.storeHistory();
  },

  /**
   * Stores the console history for future console instances.
   * @returns Promise
   *          Resolves once the changes have been persisted.
   */
  storeHistory: function () {
    return asyncStorage.setItem("webConsoleHistory", this.history);
  },

  /**
   * Stores the data for the last completion.
   * @type object
   */
  lastCompletion: null,

  /**
   * Array that caches the user input suggestions received from the server.
   * @private
   * @type array
   */
  _autocompleteCache: null,

  /**
   * The input that caused the last request to the server, whose response is
   * cached in the _autocompleteCache array.
   * @private
   * @type string
   */
  _autocompleteQuery: null,

  /**
   * The frameActorId used in the last autocomplete query. Whenever this changes
   * the autocomplete cache must be invalidated.
   * @private
   * @type string
   */
  _lastFrameActorId: null,

  /**
   * The Web Console sidebar.
   * @see this._createSidebar()
   * @see Sidebar.jsm
   */
  sidebar: null,

  /**
   * The Variables View instance shown in the sidebar.
   * @private
   * @type object
   */
  _variablesView: null,

  /**
   * Tells if you want the variables view UI updates to be lazy or not. Tests
   * disable lazy updates.
   *
   * @private
   * @type boolean
   */
  _lazyVariablesView: true,

  /**
   * Holds a map between VariablesView instances and sets of ObjectActor IDs
   * that have been retrieved from the server. This allows us to release the
   * objects when needed.
   *
   * @private
   * @type Map
   */
  _objectActorsInVariablesViews: null,

  /**
   * Last input value.
   * @type string
   */
  lastInputValue: "",

  /**
   * Tells if the input node changed since the last focus.
   *
   * @private
   * @type boolean
   */
  _inputChanged: false,

  /**
   * Tells if the autocomplete popup was navigated since the last open.
   *
   * @private
   * @type boolean
   */
  _autocompletePopupNavigated: false,

  /**
   * History of code that was executed.
   * @type array
   */
  history: null,
  autocompletePopup: null,
  inputNode: null,
  completeNode: null,

  /**
   * Getter for the element that holds the messages we display.
   * @type nsIDOMElement
   */
  get outputNode() {
    return this.hud.outputNode;
  },

  /**
   * Getter for the debugger WebConsoleClient.
   * @type object
   */
  get webConsoleClient() {
    return this.hud.webConsoleClient;
  },

  COMPLETE_FORWARD: 0,
  COMPLETE_BACKWARD: 1,
  COMPLETE_HINT_ONLY: 2,
  COMPLETE_PAGEUP: 3,
  COMPLETE_PAGEDOWN: 4,

  /**
   * Initialize the JSTerminal UI.
   */
  init: function () {
    let autocompleteOptions = {
      onSelect: this.onAutocompleteSelect.bind(this),
      onClick: this.acceptProposedCompletion.bind(this),
      listId: "webConsole_autocompletePopupListBox",
      position: "top",
      theme: "auto",
      autoSelect: true
    };

    let doc = this.hud.document;
    let toolbox = gDevTools.getToolbox(this.hud.owner.target);
    let tooltipDoc = toolbox ? toolbox.doc : doc;
    // The popup will be attached to the toolbox document or HUD document in the case
    // such as the browser console which doesn't have a toolbox.
    this.autocompletePopup = new AutocompletePopup(tooltipDoc, autocompleteOptions);
    let inputContainer = doc.querySelector(".jsterm-input-container");
    this.completeNode = doc.querySelector(".jsterm-complete-node");
    this.inputNode = doc.querySelector(".jsterm-input-node");
    // Update the character width and height needed for the popup offset
    // calculations.
    this._updateCharSize();

    if (this.hud.isBrowserConsole &&
        !Services.prefs.getBoolPref("devtools.chrome.enabled")) {
      inputContainer.style.display = "none";
    } else {
      this.inputNode.addEventListener("keypress", this._keyPress);
      this.inputNode.addEventListener("input", this._inputEventHandler);
      this.inputNode.addEventListener("keyup", this._inputEventHandler);
      this.inputNode.addEventListener("focus", this._focusEventHandler);
    }

    if (!this.hud.isBrowserConsole) {
      let okstring = l10n.getStr("selfxss.okstring");
      let msg = l10n.getFormatStr("selfxss.msg", [okstring]);
      this._onPaste = WebConsoleUtils.pasteHandlerGen(this.inputNode,
          this.getNotificationBox(), msg, okstring);
      this.inputNode.addEventListener("paste", this._onPaste);
      this.inputNode.addEventListener("drop", this._onPaste);
    }

    this.hud.window.addEventListener("blur", this._blurEventHandler);
    this.lastInputValue && this.setInputValue(this.lastInputValue);
  },

  focus: function () {
    if (!this.inputNode.getAttribute("focused")) {
      this.inputNode.focus();
    }
  },

  /**
   * The JavaScript evaluation response handler.
   *
   * @private
   * @param function [callback]
   *        Optional function to invoke when the evaluation result is added to
   *        the output.
   * @param object response
   *        The message received from the server.
   */
  _executeResultCallback: function (callback, response) {
    if (!this.hud) {
      return;
    }
    if (response.error) {
      console.error("Evaluation error " + response.error + ": " +
                    response.message);
      return;
    }
    let errorMessage = response.exceptionMessage;
    let errorDocURL = response.exceptionDocURL;

    let errorDocLink;
    if (errorDocURL) {
      errorMessage += " ";
      errorDocLink = this.hud.document.createElementNS(XHTML_NS, "a");
      errorDocLink.className = "learn-more-link webconsole-learn-more-link";
      errorDocLink.textContent = `[${l10n.getStr("webConsoleMoreInfoLabel")}]`;
      errorDocLink.title = errorDocURL.split("?")[0];
      errorDocLink.href = "#";
      errorDocLink.draggable = false;
      errorDocLink.addEventListener("click", () => {
        this.hud.owner.openLink(errorDocURL);
      });
    }

    // Wrap thrown strings in Error objects, so `throw "foo"` outputs
    // "Error: foo"
    if (typeof response.exception === "string") {
      errorMessage = new Error(errorMessage).toString();
    }
    let result = response.result;
    let helperResult = response.helperResult;
    let helperHasRawOutput = !!(helperResult || {}).rawOutput;

    if (helperResult && helperResult.type) {
      switch (helperResult.type) {
        case "clearOutput":
          this.clearOutput();
          break;
        case "clearHistory":
          this.clearHistory();
          break;
        case "inspectObject":
          this.inspectObjectActor(helperResult.object);
          break;
        case "error":
          try {
            errorMessage = l10n.getStr(helperResult.message);
          } catch (ex) {
            errorMessage = helperResult.message;
          }
          break;
        case "help":
          this.hud.owner.openLink(HELP_URL);
          break;
        case "copyValueToClipboard":
          clipboardHelper.copyString(helperResult.value);
          break;
      }
    }

    // Hide undefined results coming from JSTerm helper functions.
    if (!errorMessage && result && typeof result == "object" &&
      result.type == "undefined" &&
      helperResult && !helperHasRawOutput) {
      callback && callback();
      return;
    }

    if (this.hud.NEW_CONSOLE_OUTPUT_ENABLED) {
      this.hud.newConsoleOutput.dispatchMessageAdd(response, true).then(callback);
      return;
    }
    let msg = new Messages.JavaScriptEvalOutput(response,
                                                errorMessage, errorDocLink);
    this.hud.output.addMessage(msg);

    if (callback) {
      let oldFlushCallback = this.hud._flushCallback;
      this.hud._flushCallback = () => {
        callback(msg.element);
        if (oldFlushCallback) {
          oldFlushCallback();
          this.hud._flushCallback = oldFlushCallback;
          return true;
        }

        return false;
      };
    }

    msg._objectActors = new Set();

    if (WebConsoleUtils.isActorGrip(response.exception)) {
      msg._objectActors.add(response.exception.actor);
    }

    if (WebConsoleUtils.isActorGrip(result)) {
      msg._objectActors.add(result.actor);
    }
  },

  inspectObjectActor: function (objectActor) {
    if (this.hud.NEW_CONSOLE_OUTPUT_ENABLED) {
      this.hud.newConsoleOutput.dispatchMessageAdd({
        helperResult: {
          type: "inspectObject",
          object: objectActor
        }
      }, true);
      return this.hud.newConsoleOutput;
    }

    return this.openVariablesView({
      objectActor,
      label: VariablesView.getString(objectActor, {concise: true}),
    });
  },

  /**
   * Execute a string. Execution happens asynchronously in the content process.
   *
   * @param string [executeString]
   *        The string you want to execute. If this is not provided, the current
   *        user input is used - taken from |this.getInputValue()|.
   * @param function [callback]
   *        Optional function to invoke when the result is displayed.
   *        This is deprecated - please use the promise return value instead.
   * @returns Promise
   *          Resolves with the message once the result is displayed.
   */
  execute: function (executeString, callback) {
    let deferred = defer();
    let resultCallback;
    if (this.hud.NEW_CONSOLE_OUTPUT_ENABLED) {
      resultCallback = (msg) => deferred.resolve(msg);
    } else {
      resultCallback = (msg) => {
        deferred.resolve(msg);
        if (callback) {
          callback(msg);
        }
      };
    }

    // attempt to execute the content of the inputNode
    executeString = executeString || this.getInputValue();
    if (!executeString) {
      return null;
    }

    let selectedNodeActor = null;
    let inspectorSelection = this.hud.owner.getInspectorSelection();
    if (inspectorSelection && inspectorSelection.nodeFront) {
      selectedNodeActor = inspectorSelection.nodeFront.actorID;
    }

    if (this.hud.NEW_CONSOLE_OUTPUT_ENABLED) {
      const { ConsoleCommand } = require("devtools/client/webconsole/new-console-output/types");
      let message = new ConsoleCommand({
        messageText: executeString,
      });
      this.hud.proxy.dispatchMessageAdd(message);
    } else {
      let message = new Messages.Simple(executeString, {
        category: "input",
        severity: "log",
      });
      this.hud.output.addMessage(message);
    }
    let onResult = this._executeResultCallback.bind(this, resultCallback);

    let options = {
      frame: this.SELECTED_FRAME,
      selectedNodeActor: selectedNodeActor,
    };

    this.requestEvaluation(executeString, options).then(onResult, onResult);

    // Append a new value in the history of executed code, or overwrite the most
    // recent entry. The most recent entry may contain the last edited input
    // value that was not evaluated yet.
    this.history[this.historyIndex++] = executeString;
    this.historyPlaceHolder = this.history.length;

    if (this.history.length > this.inputHistoryCount) {
      this.history.splice(0, this.history.length - this.inputHistoryCount);
      this.historyIndex = this.historyPlaceHolder = this.history.length;
    }
    this.storeHistory();
    WebConsoleUtils.usageCount++;
    this.setInputValue("");
    this.clearCompletion();
    return deferred.promise;
  },

  /**
   * Request a JavaScript string evaluation from the server.
   *
   * @param string str
   *        String to execute.
   * @param object [options]
   *        Options for evaluation:
   *        - bindObjectActor: tells the ObjectActor ID for which you want to do
   *        the evaluation. The Debugger.Object of the OA will be bound to
   *        |_self| during evaluation, such that it's usable in the string you
   *        execute.
   *        - frame: tells the stackframe depth to evaluate the string in. If
   *        the jsdebugger is paused, you can pick the stackframe to be used for
   *        evaluation. Use |this.SELECTED_FRAME| to always pick the
   *        user-selected stackframe.
   *        If you do not provide a |frame| the string will be evaluated in the
   *        global content window.
   *        - selectedNodeActor: tells the NodeActor ID of the current selection
   *        in the Inspector, if such a selection exists. This is used by
   *        helper functions that can evaluate on the current selection.
   * @return object
   *         A promise object that is resolved when the server response is
   *         received.
   */
  requestEvaluation: function (str, options = {}) {
    let deferred = defer();

    function onResult(response) {
      if (!response.error) {
        deferred.resolve(response);
      } else {
        deferred.reject(response);
      }
    }

    let frameActor = null;
    if ("frame" in options) {
      frameActor = this.getFrameActor(options.frame);
    }

    let evalOptions = {
      bindObjectActor: options.bindObjectActor,
      frameActor: frameActor,
      selectedNodeActor: options.selectedNodeActor,
      selectedObjectActor: options.selectedObjectActor,
    };

    this.webConsoleClient.evaluateJSAsync(str, onResult, evalOptions);
    return deferred.promise;
  },

  /**
   * Copy the object/variable by invoking the server
   * which invokes the `copy(variable)` command and makes it
   * available in the clipboard
   * @param evalString - string which has the evaluation string to be copied
   * @param options - object - Options for evaluation
   * @return object
   *         A promise object that is resolved when the server response is
   *         received.
   */
  copyObject: function (evalString, evalOptions) {
    return this.webConsoleClient.evaluateJSAsync(`copy(${evalString})`,
      null, evalOptions);
  },

  /**
   * Retrieve the FrameActor ID given a frame depth.
   *
   * @param number frame
   *        Frame depth.
   * @return string|null
   *         The FrameActor ID for the given frame depth.
   */
  getFrameActor: function (frame) {
    let state = this.hud.owner.getDebuggerFrames();
    if (!state) {
      return null;
    }

    let grip;
    if (frame == this.SELECTED_FRAME) {
      grip = state.frames[state.selected];
    } else {
      grip = state.frames[frame];
    }

    return grip ? grip.actor : null;
  },

  /**
   * Opens a new variables view that allows the inspection of the given object.
   *
   * @param object options
   *        Options for the variables view:
   *        - objectActor: grip of the ObjectActor you want to show in the
   *        variables view.
   *        - rawObject: the raw object you want to show in the variables view.
   *        - label: label to display in the variables view for inspected
   *        object.
   *        - hideFilterInput: optional boolean, |true| if you want to hide the
   *        variables view filter input.
   *        - targetElement: optional nsIDOMElement to append the variables view
   *        to. An iframe element is used as a container for the view. If this
   *        option is not used, then the variables view opens in the sidebar.
   *        - autofocus: optional boolean, |true| if you want to give focus to
   *        the variables view window after open, |false| otherwise.
   * @return object
   *         A promise object that is resolved when the variables view has
   *         opened. The new variables view instance is given to the callbacks.
   */
  openVariablesView: function (options) {
    // Bail out if the side bar doesn't exist.
    if (!this.hud.document.querySelector("#webconsole-sidebar")) {
      return Promise.resolve(null);
    }

    let onContainerReady = (window) => {
      let container = window.document.querySelector("#variables");
      let view = this._variablesView;
      if (!view || options.targetElement) {
        let viewOptions = {
          container: container,
          hideFilterInput: options.hideFilterInput,
        };
        view = this._createVariablesView(viewOptions);
        if (!options.targetElement) {
          this._variablesView = view;
          window.addEventListener("keypress", this._onKeypressInVariablesView);
        }
      }
      options.view = view;
      this._updateVariablesView(options);

      if (!options.targetElement && options.autofocus) {
        window.focus();
      }

      this.emit("variablesview-open", {view, options});
      return view;
    };

    let openPromise;
    if (options.targetElement) {
      let deferred = defer();
      openPromise = deferred.promise;
      let document = options.targetElement.ownerDocument;
      let iframe = document.createElementNS(XHTML_NS, "iframe");

      iframe.addEventListener("load", function () {
        iframe.style.visibility = "visible";
        deferred.resolve(iframe.contentWindow);
      }, {capture: true, once: true});

      iframe.flex = 1;
      iframe.style.visibility = "hidden";
      iframe.setAttribute("src", VARIABLES_VIEW_URL);
      options.targetElement.appendChild(iframe);
    } else {
      if (!this.sidebar) {
        this._createSidebar();
      }
      openPromise = this._addVariablesViewSidebarTab();
    }

    return openPromise.then(onContainerReady);
  },

  /**
   * Create the Web Console sidebar.
   *
   * @see devtools/framework/sidebar.js
   * @private
   */
  _createSidebar: function () {
    let tabbox = this.hud.document.querySelector("#webconsole-sidebar");
    this.sidebar = new ToolSidebar(tabbox, this, "webconsole");
    this.sidebar.show();
    this.emit("sidebar-opened");
  },

  /**
   * Add the variables view tab to the sidebar.
   *
   * @private
   * @return object
   *         A promise object for the adding of the new tab.
   */
  _addVariablesViewSidebarTab: function () {
    let deferred = defer();

    let onTabReady = () => {
      let window = this.sidebar.getWindowForTab("variablesview");
      deferred.resolve(window);
    };

    let tabPanel = this.sidebar.getTabPanel("variablesview");
    if (tabPanel) {
      if (this.sidebar.getCurrentTabID() == "variablesview") {
        onTabReady();
      } else {
        this.sidebar.once("variablesview-selected", onTabReady);
        this.sidebar.select("variablesview");
      }
    } else {
      this.sidebar.once("variablesview-ready", onTabReady);
      this.sidebar.addTab("variablesview", VARIABLES_VIEW_URL, {selected: true});
    }

    return deferred.promise;
  },

  /**
   * The keypress event handler for the Variables View sidebar. Currently this
   * is used for removing the sidebar when Escape is pressed.
   *
   * @private
   * @param nsIDOMEvent event
   *        The keypress DOM event object.
   */
  _onKeypressInVariablesView: function (event) {
    let tag = event.target.nodeName;
    if (event.keyCode != KeyCodes.DOM_VK_ESCAPE || event.shiftKey ||
        event.altKey || event.ctrlKey || event.metaKey ||
        ["input", "textarea", "select", "textbox"].indexOf(tag) > -1) {
      return;
    }

    this._sidebarDestroy();
    this.focus();
    event.stopPropagation();
  },

  /**
   * Create a variables view instance.
   *
   * @private
   * @param object options
   *        Options for the new Variables View instance:
   *        - container: the DOM element where the variables view is inserted.
   *        - hideFilterInput: boolean, if true the variables filter input is
   *        hidden.
   * @return object
   *         The new Variables View instance.
   */
  _createVariablesView: function (options) {
    let view = new VariablesView(options.container);
    view.toolbox = gDevTools.getToolbox(this.hud.owner.target);
    view.searchPlaceholder = l10n.getStr("propertiesFilterPlaceholder");
    view.emptyText = l10n.getStr("emptyPropertiesList");
    view.searchEnabled = !options.hideFilterInput;
    view.lazyEmpty = this._lazyVariablesView;

    VariablesViewController.attach(view, {
      getEnvironmentClient: grip => {
        return new EnvironmentClient(this.hud.proxy.client, grip);
      },
      getObjectClient: grip => {
        return new ObjectClient(this.hud.proxy.client, grip);
      },
      getLongStringClient: grip => {
        return this.webConsoleClient.longString(grip);
      },
      releaseActor: actor => {
        this.hud._releaseObject(actor);
      },
      simpleValueEvalMacro: simpleValueEvalMacro,
      overrideValueEvalMacro: overrideValueEvalMacro,
      getterOrSetterEvalMacro: getterOrSetterEvalMacro,
    });

    // Relay events from the VariablesView.
    view.on("fetched", (type, variableObject) => {
      this.emit("variablesview-fetched", variableObject);
    });

    return view;
  },

  /**
   * Update the variables view.
   *
   * @private
   * @param object options
   *        Options for updating the variables view:
   *        - view: the view you want to update.
   *        - objectActor: the grip of the new ObjectActor you want to show in
   *        the view.
   *        - rawObject: the new raw object you want to show.
   *        - label: the new label for the inspected object.
   */
  _updateVariablesView: function (options) {
    let view = options.view;
    view.empty();

    // We need to avoid pruning the object inspection starting point.
    // That one is pruned when the console message is removed.
    view.controller.releaseActors(actor => {
      return view._consoleLastObjectActor != actor;
    });

    if (options.objectActor &&
        (!this.hud.isBrowserConsole ||
         Services.prefs.getBoolPref("devtools.chrome.enabled"))) {
      // Make sure eval works in the correct context.
      view.eval = this._variablesViewEvaluate.bind(this, options);
      view.switch = this._variablesViewSwitch.bind(this, options);
      view.delete = this._variablesViewDelete.bind(this, options);
    } else {
      view.eval = null;
      view.switch = null;
      view.delete = null;
    }

    let { variable, expanded } = view.controller.setSingleVariable(options);
    variable.evaluationMacro = simpleValueEvalMacro;

    if (options.objectActor) {
      view._consoleLastObjectActor = options.objectActor.actor;
    } else if (options.rawObject) {
      view._consoleLastObjectActor = null;
    } else {
      throw new Error(
        "Variables View cannot open without giving it an object display.");
    }

    expanded.then(() => {
      this.emit("variablesview-updated", view, options);
    });
  },

  /**
   * The evaluation function used by the variables view when editing a property
   * value.
   *
   * @private
   * @param object options
   *        The options used for |this._updateVariablesView()|.
   * @param object variableObject
   *        The Variable object instance for the edited property.
   * @param string value
   *        The value the edited property was changed to.
   */
  _variablesViewEvaluate: function (options, variableObject, value) {
    let updater = this._updateVariablesView.bind(this, options);
    let onEval = this._silentEvalCallback.bind(this, updater);
    let string = variableObject.evaluationMacro(variableObject, value);

    let evalOptions = {
      frame: this.SELECTED_FRAME,
      bindObjectActor: options.objectActor.actor,
    };

    this.requestEvaluation(string, evalOptions).then(onEval, onEval);
  },

  /**
   * The property deletion function used by the variables view when a property
   * is deleted.
   *
   * @private
   * @param object options
   *        The options used for |this._updateVariablesView()|.
   * @param object variableObject
   *        The Variable object instance for the deleted property.
   */
  _variablesViewDelete: function (options, variableObject) {
    let onEval = this._silentEvalCallback.bind(this, null);

    let evalOptions = {
      frame: this.SELECTED_FRAME,
      bindObjectActor: options.objectActor.actor,
    };

    this.requestEvaluation("delete _self" +
      variableObject.symbolicName, evalOptions).then(onEval, onEval);
  },

  /**
   * The property rename function used by the variables view when a property
   * is renamed.
   *
   * @private
   * @param object options
   *        The options used for |this._updateVariablesView()|.
   * @param object variableObject
   *        The Variable object instance for the renamed property.
   * @param string newName
   *        The new name for the property.
   */
  _variablesViewSwitch: function (options, variableObject, newName) {
    let updater = this._updateVariablesView.bind(this, options);
    let onEval = this._silentEvalCallback.bind(this, updater);

    let evalOptions = {
      frame: this.SELECTED_FRAME,
      bindObjectActor: options.objectActor.actor,
    };

    let newSymbolicName =
      variableObject.ownerView.symbolicName + '["' + newName + '"]';
    if (newSymbolicName == variableObject.symbolicName) {
      return;
    }

    let code = "_self" + newSymbolicName + " = _self" +
      variableObject.symbolicName + ";" + "delete _self" +
      variableObject.symbolicName;

    this.requestEvaluation(code, evalOptions).then(onEval, onEval);
  },

  /**
   * A noop callback for JavaScript evaluation. This method releases any
   * result ObjectActors that come from the server for evaluation requests. This
   * is used for editing, renaming and deleting properties in the variables
   * view.
   *
   * Exceptions are displayed in the output.
   *
   * @private
   * @param function callback
   *        Function to invoke once the response is received.
   * @param object response
   *        The response packet received from the server.
   */
  _silentEvalCallback: function (callback, response) {
    if (response.error) {
      console.error("Web Console evaluation failed. " + response.error + ":" +
                    response.message);

      callback && callback(response);
      return;
    }

    if (response.exceptionMessage) {
      let message = new Messages.Simple(response.exceptionMessage, {
        category: "output",
        severity: "error",
        timestamp: response.timestamp,
      });
      this.hud.output.addMessage(message);
      message._objectActors = new Set();
      if (WebConsoleUtils.isActorGrip(response.exception)) {
        message._objectActors.add(response.exception.actor);
      }
    }

    let helper = response.helperResult || { type: null };
    let helperGrip = null;
    if (helper.type == "inspectObject") {
      helperGrip = helper.object;
    }

    let grips = [response.result, helperGrip];
    for (let grip of grips) {
      if (WebConsoleUtils.isActorGrip(grip)) {
        this.hud._releaseObject(grip.actor);
      }
    }

    callback && callback(response);
  },

  /**
   * Clear the Web Console output.
   *
   * This method emits the "messages-cleared" notification.
   *
   * @param boolean clearStorage
   *        True if you want to clear the console messages storage associated to
   *        this Web Console.
   */
  clearOutput: function (clearStorage) {
    let hud = this.hud;

    if (hud.NEW_CONSOLE_OUTPUT_ENABLED) {
      hud.newConsoleOutput.dispatchMessagesClear();
    } else {
      let outputNode = hud.outputNode;
      let node;
      while ((node = outputNode.firstChild)) {
        hud.removeOutputMessage(node);
      }

      hud.groupDepth = 0;
      hud._outputQueue.forEach(hud._destroyItem, hud);
      hud._outputQueue = [];
      hud._repeatNodes = {};
    }
    this.webConsoleClient.clearNetworkRequests();
    if (clearStorage) {
      this.webConsoleClient.clearMessagesCache();
    }
    this._sidebarDestroy();
    this.focus();
    this.emit("messages-cleared");
  },

  /**
   * Remove all of the private messages from the Web Console output.
   *
   * This method emits the "private-messages-cleared" notification.
   */
  clearPrivateMessages: function () {
    if (this.hud.NEW_CONSOLE_OUTPUT_ENABLED) {
      this.hud.newConsoleOutput.dispatchPrivateMessagesClear();
    } else {
      let nodes = this.hud.outputNode.querySelectorAll(".message[private]");
      for (let node of nodes) {
        this.hud.removeOutputMessage(node);
      }
    }
    this.emit("private-messages-cleared");
  },

  /**
   * Updates the size of the input field (command line) to fit its contents.
   *
   * @returns void
   */
  resizeInput: function () {
    let inputNode = this.inputNode;

    // Reset the height so that scrollHeight will reflect the natural height of
    // the contents of the input field.
    inputNode.style.height = "auto";

    // Now resize the input field to fit its contents.
    // TODO: remove `inputNode.inputField.scrollHeight` when the old
    // console UI is removed. See bug 1381834
    let scrollHeight = inputNode.inputField ?
      inputNode.inputField.scrollHeight : inputNode.scrollHeight;

    if (scrollHeight > 0) {
      inputNode.style.height = scrollHeight + "px";
    }
  },

  /**
   * Sets the value of the input field (command line), and resizes the field to
   * fit its contents. This method is preferred over setting "inputNode.value"
   * directly, because it correctly resizes the field.
   *
   * @param string newValue
   *        The new value to set.
   * @returns void
   */
  setInputValue: function (newValue) {
    this.inputNode.value = newValue;
    this.lastInputValue = newValue;
    this.completeNode.value = "";
    this.resizeInput();
    this._inputChanged = true;
    this.emit("set-input-value");
  },

  /**
   * Gets the value from the input field
   * @returns string
   */
  getInputValue: function () {
    return this.inputNode.value || "";
  },

  /**
   * The inputNode "input" and "keyup" event handler.
   * @private
   */
  _inputEventHandler: function () {
    if (this.lastInputValue != this.getInputValue()) {
      this.resizeInput();
      this.complete(this.COMPLETE_HINT_ONLY);
      this.lastInputValue = this.getInputValue();
      this._inputChanged = true;
    }
  },

  /**
   * The window "blur" event handler.
   * @private
   */
  _blurEventHandler: function () {
    if (this.autocompletePopup) {
      this.clearCompletion();
    }
  },

  /* eslint-disable complexity */
  /**
   * The inputNode "keypress" event handler.
   *
   * @private
   * @param nsIDOMEvent event
   */
  _keyPress: function (event) {
    let inputNode = this.inputNode;
    let inputValue = this.getInputValue();
    let inputUpdated = false;

    if (event.ctrlKey) {
      switch (event.charCode) {
        case 101:
          // control-e
          if (Services.appinfo.OS == "WINNT") {
            break;
          }
          let lineEndPos = inputValue.length;
          if (this.hasMultilineInput()) {
            // find index of closest newline >= cursor
            for (let i = inputNode.selectionEnd; i < lineEndPos; i++) {
              if (inputValue.charAt(i) == "\r" ||
                  inputValue.charAt(i) == "\n") {
                lineEndPos = i;
                break;
              }
            }
          }
          inputNode.setSelectionRange(lineEndPos, lineEndPos);
          event.preventDefault();
          this.clearCompletion();
          break;

        case 110:
          // Control-N differs from down arrow: it ignores autocomplete state.
          // Note that we preserve the default 'down' navigation within
          // multiline text.
          if (Services.appinfo.OS == "Darwin" &&
              this.canCaretGoNext() &&
              this.historyPeruse(HISTORY_FORWARD)) {
            event.preventDefault();
            // Ctrl-N is also used to focus the Network category button on
            // MacOSX. The preventDefault() call doesn't prevent the focus
            // from moving away from the input.
            this.focus();
          }
          this.clearCompletion();
          break;

        case 112:
          // Control-P differs from up arrow: it ignores autocomplete state.
          // Note that we preserve the default 'up' navigation within
          // multiline text.
          if (Services.appinfo.OS == "Darwin" &&
              this.canCaretGoPrevious() &&
              this.historyPeruse(HISTORY_BACK)) {
            event.preventDefault();
            // Ctrl-P may also be used to focus some category button on MacOSX.
            // The preventDefault() call doesn't prevent the focus from moving
            // away from the input.
            this.focus();
          }
          this.clearCompletion();
          break;
        default:
          break;
      }
      return;
    } else if (event.keyCode == KeyCodes.DOM_VK_RETURN) {
      let autoMultiline = Services.prefs.getBoolPref(PREF_AUTO_MULTILINE);
      if (event.shiftKey ||
          (!Debugger.isCompilableUnit(inputNode.value) && autoMultiline)) {
        // shift return or incomplete statement
        return;
      }
    }

    switch (event.keyCode) {
      case KeyCodes.DOM_VK_ESCAPE:
        if (this.autocompletePopup.isOpen) {
          this.clearCompletion();
          event.preventDefault();
          event.stopPropagation();
        } else if (this.sidebar) {
          this._sidebarDestroy();
          event.preventDefault();
          event.stopPropagation();
        }
        break;

      case KeyCodes.DOM_VK_RETURN:
        if (this._autocompletePopupNavigated &&
            this.autocompletePopup.isOpen &&
            this.autocompletePopup.selectedIndex > -1) {
          this.acceptProposedCompletion();
        } else {
          this.execute();
          this._inputChanged = false;
        }
        event.preventDefault();
        break;

      case KeyCodes.DOM_VK_UP:
        if (this.autocompletePopup.isOpen) {
          inputUpdated = this.complete(this.COMPLETE_BACKWARD);
          if (inputUpdated) {
            this._autocompletePopupNavigated = true;
          }
        } else if (this.canCaretGoPrevious()) {
          inputUpdated = this.historyPeruse(HISTORY_BACK);
        }
        if (inputUpdated) {
          event.preventDefault();
        }
        break;

      case KeyCodes.DOM_VK_DOWN:
        if (this.autocompletePopup.isOpen) {
          inputUpdated = this.complete(this.COMPLETE_FORWARD);
          if (inputUpdated) {
            this._autocompletePopupNavigated = true;
          }
        } else if (this.canCaretGoNext()) {
          inputUpdated = this.historyPeruse(HISTORY_FORWARD);
        }
        if (inputUpdated) {
          event.preventDefault();
        }
        break;

      case KeyCodes.DOM_VK_PAGE_UP:
        if (this.autocompletePopup.isOpen) {
          inputUpdated = this.complete(this.COMPLETE_PAGEUP);
          if (inputUpdated) {
            this._autocompletePopupNavigated = true;
          }
        } else {
          this.hud.outputScroller.scrollTop =
            Math.max(0,
              this.hud.outputScroller.scrollTop -
              this.hud.outputScroller.clientHeight
            );
        }
        event.preventDefault();
        break;

      case KeyCodes.DOM_VK_PAGE_DOWN:
        if (this.autocompletePopup.isOpen) {
          inputUpdated = this.complete(this.COMPLETE_PAGEDOWN);
          if (inputUpdated) {
            this._autocompletePopupNavigated = true;
          }
        } else {
          this.hud.outputScroller.scrollTop =
            Math.min(this.hud.outputScroller.scrollHeight,
              this.hud.outputScroller.scrollTop +
              this.hud.outputScroller.clientHeight
            );
        }
        event.preventDefault();
        break;

      case KeyCodes.DOM_VK_HOME:
        if (this.autocompletePopup.isOpen) {
          this.autocompletePopup.selectedIndex = 0;
          event.preventDefault();
        } else if (inputValue.length <= 0) {
          this.hud.outputScroller.scrollTop = 0;
          event.preventDefault();
        }
        break;

      case KeyCodes.DOM_VK_END:
        if (this.autocompletePopup.isOpen) {
          this.autocompletePopup.selectedIndex =
            this.autocompletePopup.itemCount - 1;
          event.preventDefault();
        } else if (inputValue.length <= 0) {
          this.hud.outputScroller.scrollTop =
            this.hud.outputScroller.scrollHeight;
          event.preventDefault();
        }
        break;

      case KeyCodes.DOM_VK_LEFT:
        if (this.autocompletePopup.isOpen || this.lastCompletion.value) {
          this.clearCompletion();
        }
        break;

      case KeyCodes.DOM_VK_RIGHT:
        let cursorAtTheEnd = this.inputNode.selectionStart ==
                             this.inputNode.selectionEnd &&
                             this.inputNode.selectionStart ==
                             inputValue.length;
        let haveSuggestion = this.autocompletePopup.isOpen ||
                             this.lastCompletion.value;
        let useCompletion = cursorAtTheEnd || this._autocompletePopupNavigated;
        if (haveSuggestion && useCompletion &&
            this.complete(this.COMPLETE_HINT_ONLY) &&
            this.lastCompletion.value &&
            this.acceptProposedCompletion()) {
          event.preventDefault();
        }
        if (this.autocompletePopup.isOpen) {
          this.clearCompletion();
        }
        break;

      case KeyCodes.DOM_VK_TAB:
        // Generate a completion and accept the first proposed value.
        if (this.complete(this.COMPLETE_HINT_ONLY) &&
            this.lastCompletion &&
            this.acceptProposedCompletion()) {
          event.preventDefault();
        } else if (this._inputChanged) {
          this.updateCompleteNode(l10n.getStr("Autocomplete.blank"));
          event.preventDefault();
        }
        break;
      default:
        break;
    }
  },
  /* eslint-enable complexity */

  /**
   * The inputNode "focus" event handler.
   * @private
   */
  _focusEventHandler: function () {
    this._inputChanged = false;
  },

  /**
   * Go up/down the history stack of input values.
   *
   * @param number direction
   *        History navigation direction: HISTORY_BACK or HISTORY_FORWARD.
   *
   * @returns boolean
   *          True if the input value changed, false otherwise.
   */
  historyPeruse: function (direction) {
    if (!this.history.length) {
      return false;
    }

    // Up Arrow key
    if (direction == HISTORY_BACK) {
      if (this.historyPlaceHolder <= 0) {
        return false;
      }
      let inputVal = this.history[--this.historyPlaceHolder];

      // Save the current input value as the latest entry in history, only if
      // the user is already at the last entry.
      // Note: this code does not store changes to items that are already in
      // history.
      if (this.historyPlaceHolder + 1 == this.historyIndex) {
        this.history[this.historyIndex] = this.getInputValue() || "";
      }

      this.setInputValue(inputVal);
    } else if (direction == HISTORY_FORWARD) {
      // Down Arrow key
      if (this.historyPlaceHolder >= (this.history.length - 1)) {
        return false;
      }

      let inputVal = this.history[++this.historyPlaceHolder];
      this.setInputValue(inputVal);
    } else {
      throw new Error("Invalid argument 0");
    }

    return true;
  },

  /**
   * Test for multiline input.
   *
   * @return boolean
   *         True if CR or LF found in node value; else false.
   */
  hasMultilineInput: function () {
    return /[\r\n]/.test(this.getInputValue());
  },

  /**
   * Check if the caret is at a location that allows selecting the previous item
   * in history when the user presses the Up arrow key.
   *
   * @return boolean
   *         True if the caret is at a location that allows selecting the
   *         previous item in history when the user presses the Up arrow key,
   *         otherwise false.
   */
  canCaretGoPrevious: function () {
    let node = this.inputNode;
    if (node.selectionStart != node.selectionEnd) {
      return false;
    }

    let multiline = /[\r\n]/.test(node.value);
    return node.selectionStart == 0 ? true :
           node.selectionStart == node.value.length && !multiline;
  },

  /**
   * Check if the caret is at a location that allows selecting the next item in
   * history when the user presses the Down arrow key.
   *
   * @return boolean
   *         True if the caret is at a location that allows selecting the next
   *         item in history when the user presses the Down arrow key, otherwise
   *         false.
   */
  canCaretGoNext: function () {
    let node = this.inputNode;
    if (node.selectionStart != node.selectionEnd) {
      return false;
    }

    let multiline = /[\r\n]/.test(node.value);
    return node.selectionStart == node.value.length ? true :
           node.selectionStart == 0 && !multiline;
  },

  /**
   * Completes the current typed text in the inputNode. Completion is performed
   * only if the selection/cursor is at the end of the string. If no completion
   * is found, the current inputNode value and cursor/selection stay.
   *
   * @param int type possible values are
   *    - this.COMPLETE_FORWARD: If there is more than one possible completion
   *          and the input value stayed the same compared to the last time this
   *          function was called, then the next completion of all possible
   *          completions is used. If the value changed, then the first possible
   *          completion is used and the selection is set from the current
   *          cursor position to the end of the completed text.
   *          If there is only one possible completion, then this completion
   *          value is used and the cursor is put at the end of the completion.
   *    - this.COMPLETE_BACKWARD: Same as this.COMPLETE_FORWARD but if the
   *          value stayed the same as the last time the function was called,
   *          then the previous completion of all possible completions is used.
   *    - this.COMPLETE_PAGEUP: Scroll up one page if available or select the
   *          first item.
   *    - this.COMPLETE_PAGEDOWN: Scroll down one page if available or select
   *          the last item.
   *    - this.COMPLETE_HINT_ONLY: If there is more than one possible
   *          completion and the input value stayed the same compared to the
   *          last time this function was called, then the same completion is
   *          used again. If there is only one possible completion, then
   *          the this.getInputValue() is set to this value and the selection
   *          is set from the current cursor position to the end of the
   *          completed text.
   * @param function callback
   *        Optional function invoked when the autocomplete properties are
   *        updated.
   * @returns boolean true if there existed a completion for the current input,
   *          or false otherwise.
   */
  complete: function (type, callback) {
    let inputNode = this.inputNode;
    let inputValue = this.getInputValue();
    let frameActor = this.getFrameActor(this.SELECTED_FRAME);

    // If the inputNode has no value, then don't try to complete on it.
    if (!inputValue) {
      this.clearCompletion();
      callback && callback(this);
      this.emit("autocomplete-updated");
      return false;
    }

    // Only complete if the selection is empty.
    if (inputNode.selectionStart != inputNode.selectionEnd) {
      this.clearCompletion();
      callback && callback(this);
      this.emit("autocomplete-updated");
      return false;
    }

    // Update the completion results.
    if (this.lastCompletion.value != inputValue ||
        frameActor != this._lastFrameActorId) {
      this._updateCompletionResult(type, callback);
      return false;
    }

    let popup = this.autocompletePopup;
    let accepted = false;

    if (type != this.COMPLETE_HINT_ONLY && popup.itemCount == 1) {
      this.acceptProposedCompletion();
      accepted = true;
    } else if (type == this.COMPLETE_BACKWARD) {
      popup.selectPreviousItem();
    } else if (type == this.COMPLETE_FORWARD) {
      popup.selectNextItem();
    } else if (type == this.COMPLETE_PAGEUP) {
      popup.selectPreviousPageItem();
    } else if (type == this.COMPLETE_PAGEDOWN) {
      popup.selectNextPageItem();
    }

    callback && callback(this);
    this.emit("autocomplete-updated");
    return accepted || popup.itemCount > 0;
  },

  /**
   * Update the completion result. This operation is performed asynchronously by
   * fetching updated results from the content process.
   *
   * @private
   * @param int type
   *        Completion type. See this.complete() for details.
   * @param function [callback]
   *        Optional, function to invoke when completion results are received.
   */
  _updateCompletionResult: function (type, callback) {
    let frameActor = this.getFrameActor(this.SELECTED_FRAME);
    if (this.lastCompletion.value == this.getInputValue() &&
        frameActor == this._lastFrameActorId) {
      return;
    }

    let requestId = gSequenceId();
    let cursor = this.inputNode.selectionStart;
    let input = this.getInputValue().substring(0, cursor);
    let cache = this._autocompleteCache;

    // If the current input starts with the previous input, then we already
    // have a list of suggestions and we just need to filter the cached
    // suggestions. When the current input ends with a non-alphanumeric
    // character we ask the server again for suggestions.

    // Check if last character is non-alphanumeric
    if (!/[a-zA-Z0-9]$/.test(input) || frameActor != this._lastFrameActorId) {
      this._autocompleteQuery = null;
      this._autocompleteCache = null;
    }

    if (this._autocompleteQuery && input.startsWith(this._autocompleteQuery)) {
      let filterBy = input;
      // Find the last non-alphanumeric other than _ or $ if it exists.
      let lastNonAlpha = input.match(/[^a-zA-Z0-9_$][a-zA-Z0-9_$]*$/);
      // If input contains non-alphanumerics, use the part after the last one
      // to filter the cache
      if (lastNonAlpha) {
        filterBy = input.substring(input.lastIndexOf(lastNonAlpha) + 1);
      }

      let newList = cache.sort().filter(function (l) {
        return l.startsWith(filterBy);
      });

      this.lastCompletion = {
        requestId: null,
        completionType: type,
        value: null,
      };

      let response = { matches: newList, matchProp: filterBy };
      this._receiveAutocompleteProperties(null, callback, response);
      return;
    }

    this._lastFrameActorId = frameActor;

    this.lastCompletion = {
      requestId: requestId,
      completionType: type,
      value: null,
    };

    let autocompleteCallback =
      this._receiveAutocompleteProperties.bind(this, requestId, callback);

    this.webConsoleClient.autocomplete(
      input, cursor, autocompleteCallback, frameActor);
  },

  /**
   * Handler for the autocompletion results. This method takes
   * the completion result received from the server and updates the UI
   * accordingly.
   *
   * @param number requestId
   *        Request ID.
   * @param function [callback=null]
   *        Optional, function to invoke when the completion result is received.
   * @param object message
   *        The JSON message which holds the completion results received from
   *        the content process.
   */
  _receiveAutocompleteProperties: function (requestId, callback, message) {
    let inputNode = this.inputNode;
    let inputValue = this.getInputValue();
    if (this.lastCompletion.value == inputValue ||
        requestId != this.lastCompletion.requestId) {
      return;
    }
    // Cache whatever came from the server if the last char is
    // alphanumeric or '.'
    let cursor = inputNode.selectionStart;
    let inputUntilCursor = inputValue.substring(0, cursor);

    if (requestId != null && /[a-zA-Z0-9.]$/.test(inputUntilCursor)) {
      this._autocompleteCache = message.matches;
      this._autocompleteQuery = inputUntilCursor;
    }

    let matches = message.matches;
    let lastPart = message.matchProp;
    if (!matches.length) {
      this.clearCompletion();
      callback && callback(this);
      this.emit("autocomplete-updated");
      return;
    }

    let items = matches.reverse().map(function (match) {
      return { preLabel: lastPart, label: match };
    });

    let popup = this.autocompletePopup;
    popup.setItems(items);

    let completionType = this.lastCompletion.completionType;
    this.lastCompletion = {
      value: inputValue,
      matchProp: lastPart,
    };
    if (items.length > 1 && !popup.isOpen) {
      let str = this.getInputValue().substr(0, this.inputNode.selectionStart);
      let offset = str.length - (str.lastIndexOf("\n") + 1) - lastPart.length;
      let x = offset * this._inputCharWidth;
      popup.openPopup(inputNode, x + this._chevronWidth);
      this._autocompletePopupNavigated = false;
    } else if (items.length < 2 && popup.isOpen) {
      popup.hidePopup();
      this._autocompletePopupNavigated = false;
    }
    if (items.length == 1) {
      popup.selectedIndex = 0;
    }

    this.onAutocompleteSelect();

    if (completionType != this.COMPLETE_HINT_ONLY && popup.itemCount == 1) {
      this.acceptProposedCompletion();
    } else if (completionType == this.COMPLETE_BACKWARD) {
      popup.selectPreviousItem();
    } else if (completionType == this.COMPLETE_FORWARD) {
      popup.selectNextItem();
    }

    callback && callback(this);
    this.emit("autocomplete-updated");
  },

  onAutocompleteSelect: function () {
    // Render the suggestion only if the cursor is at the end of the input.
    if (this.inputNode.selectionStart != this.getInputValue().length) {
      return;
    }

    let currentItem = this.autocompletePopup.selectedItem;
    if (currentItem && this.lastCompletion.value) {
      let suffix =
        currentItem.label.substring(this.lastCompletion.matchProp.length);
      this.updateCompleteNode(suffix);
    } else {
      this.updateCompleteNode("");
    }
  },

  /**
   * Clear the current completion information and close the autocomplete popup,
   * if needed.
   */
  clearCompletion: function () {
    this.autocompletePopup.clearItems();
    this.lastCompletion = { value: null };
    this.updateCompleteNode("");
    if (this.autocompletePopup.isOpen) {
      // Trigger a blur/focus of the JSTerm input to force screen readers to read the
      // value again.
      this.inputNode.blur();
      this.autocompletePopup.once("popup-closed", () => {
        this.inputNode.focus();
      });
      this.autocompletePopup.hidePopup();
      this._autocompletePopupNavigated = false;
    }
  },

  /**
   * Accept the proposed input completion.
   *
   * @return boolean
   *         True if there was a selected completion item and the input value
   *         was updated, false otherwise.
   */
  acceptProposedCompletion: function () {
    let updated = false;

    let currentItem = this.autocompletePopup.selectedItem;
    if (currentItem && this.lastCompletion.value) {
      let suffix =
        currentItem.label.substring(this.lastCompletion.matchProp.length);
      let cursor = this.inputNode.selectionStart;
      let value = this.getInputValue();
      this.setInputValue(value.substr(0, cursor) +
        suffix + value.substr(cursor));
      let newCursor = cursor + suffix.length;
      this.inputNode.selectionStart = this.inputNode.selectionEnd = newCursor;
      updated = true;
    }

    this.clearCompletion();

    return updated;
  },

  /**
   * Update the node that displays the currently selected autocomplete proposal.
   *
   * @param string suffix
   *        The proposed suffix for the inputNode value.
   */
  updateCompleteNode: function (suffix) {
    // completion prefix = input, with non-control chars replaced by spaces
    let prefix = suffix ? this.getInputValue().replace(/[\S]/g, " ") : "";
    this.completeNode.value = prefix + suffix;
  },
  /**
   * Calculates the width and height of a single character of the input box.
   * This will be used in opening the popup at the correct offset.
   *
   * @private
   */
  _updateCharSize: function () {
    let doc = this.hud.document;
    let tempLabel = doc.createElementNS(XHTML_NS, "span");
    let style = tempLabel.style;
    style.position = "fixed";
    style.padding = "0";
    style.margin = "0";
    style.width = "auto";
    style.color = "transparent";
    WebConsoleUtils.copyTextStyles(this.inputNode, tempLabel);
    tempLabel.textContent = "x";
    doc.documentElement.appendChild(tempLabel);
    this._inputCharWidth = tempLabel.offsetWidth;
    tempLabel.remove();
    // Calculate the width of the chevron placed at the beginning of the input
    // box. Remove 4 more pixels to accomodate the padding of the popup.
    this._chevronWidth = +doc.defaultView.getComputedStyle(this.inputNode)
                             .paddingLeft.replace(/[^0-9.]/g, "") - 4;
  },

  /**
   * Build the notification box as soon as needed.
   */
  getNotificationBox: function () {
    if (this._notificationBox) {
      return this._notificationBox;
    }

    let box = this.hud.document.getElementById("webconsole-notificationbox");
    if (box.tagName === "notificationbox") {
      // Here we are in the old console frontend (e.g. browser console), so we
      // can directly return the notificationbox.
      return box;
    }

    let toolbox = gDevTools.getToolbox(this.hud.owner.target);

    // Render NotificationBox and assign priority levels to it.
    this._notificationBox = Object.assign(
      toolbox.ReactDOM.render(toolbox.React.createElement(NotificationBox), box),
      PriorityLevels);
    return this._notificationBox;
  },

  /**
   * Destroy the sidebar.
   * @private
   */
  _sidebarDestroy: function () {
    if (this._variablesView) {
      this._variablesView.controller.releaseActors();
      this._variablesView = null;
    }

    if (this.sidebar) {
      this.sidebar.hide();
      this.sidebar.destroy();
      this.sidebar = null;
    }

    this.emit("sidebar-closed");
  },

  /**
   * Destroy the JSTerm object. Call this method to avoid memory leaks.
   */
  destroy: function () {
    this._sidebarDestroy();

    this.clearCompletion();

    if (this.hud.NEW_CONSOLE_OUTPUT_ENABLED) {
      this.webConsoleClient.clearNetworkRequests();
      this.hud.outputNode.innerHTML = "";
    } else {
      this.clearOutput();
    }

    this.autocompletePopup.destroy();
    this.autocompletePopup = null;

    if (this._onPaste) {
      this.inputNode.removeEventListener("paste", this._onPaste);
      this.inputNode.removeEventListener("drop", this._onPaste);
      this._onPaste = null;
    }

    this.inputNode.removeEventListener("keypress", this._keyPress);
    this.inputNode.removeEventListener("input", this._inputEventHandler);
    this.inputNode.removeEventListener("keyup", this._inputEventHandler);
    this.inputNode.removeEventListener("focus", this._focusEventHandler);
    this.hud.window.removeEventListener("blur", this._blurEventHandler);

    this.hud = null;
  },
};

function gSequenceId() {
  return gSequenceId.n++;
}
gSequenceId.n = 0;
exports.gSequenceId = gSequenceId;

/**
 * @see VariablesView.simpleValueEvalMacro
 */
function simpleValueEvalMacro(item, currentString) {
  return VariablesView.simpleValueEvalMacro(item, currentString, "_self");
}

/**
 * @see VariablesView.overrideValueEvalMacro
 */
function overrideValueEvalMacro(item, currentString) {
  return VariablesView.overrideValueEvalMacro(item, currentString, "_self");
}

/**
 * @see VariablesView.getterOrSetterEvalMacro
 */
function getterOrSetterEvalMacro(item, currentString) {
  return VariablesView.getterOrSetterEvalMacro(item, currentString, "_self");
}

exports.JSTerm = JSTerm;
PK
!<͛}WWPchrome/devtools/modules/devtools/client/webconsole/net/components/cookies-tab.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const NetInfoGroupList = createFactory(require("./net-info-group-list"));

/**
 * This template represents 'Cookies' tab displayed when the user
 * expands network log in the Console panel. It's responsible for rendering
 * sent and received cookies.
 */
class CookiesTab extends Component {
  static get propTypes() {
    return {
      actions: PropTypes.shape({
        requestData: PropTypes.func.isRequired
      }),
      data: PropTypes.object.isRequired,
    };
  }

  componentDidMount() {
    let { actions, data } = this.props;
    let requestCookies = data.request.cookies;
    let responseCookies = data.response.cookies;

    // TODO: use async action objects as soon as Redux is in place
    if (!requestCookies || !requestCookies.length) {
      actions.requestData("requestCookies");
    }

    if (!responseCookies || !responseCookies.length) {
      actions.requestData("responseCookies");
    }
  }

  render() {
    let { data: file } = this.props;
    let requestCookies = file.request.cookies;
    let responseCookies = file.response.cookies;

    // The cookie panel displays two groups of cookies:
    // 1) Response Cookies
    // 2) Request Cookies
    let groups = [{
      key: "responseCookies",
      name: Locale.$STR("responseCookies"),
      params: responseCookies
    }, {
      key: "requestCookies",
      name: Locale.$STR("requestCookies"),
      params: requestCookies
    }];

    return (
      dom.div({className: "cookiesTabBox"},
        dom.div({className: "panelContent"},
          NetInfoGroupList({
            groups: groups
          })
        )
      )
    );
  }
}

// Exports from this module
module.exports = CookiesTab;
PK
!<�Un�		Pchrome/devtools/modules/devtools/client/webconsole/net/components/headers-tab.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const NetInfoGroupList = createFactory(require("./net-info-group-list"));
const Spinner = createFactory(require("./spinner"));

/**
 * This template represents 'Headers' tab displayed when the user
 * expands network log in the Console panel. It's responsible for rendering
 * request and response HTTP headers.
 */
class HeadersTab extends Component {
  static get propTypes() {
    return {
      actions: PropTypes.shape({
        requestData: PropTypes.func.isRequired
      }),
      data: PropTypes.object.isRequired,
    };
  }

  componentDidMount() {
    let { actions, data } = this.props;
    let requestHeaders = data.request.headers;
    let responseHeaders = data.response.headers;

    // Request headers if they are not available yet.
    // TODO: use async action objects as soon as Redux is in place
    if (!requestHeaders) {
      actions.requestData("requestHeaders");
    }

    if (!responseHeaders) {
      actions.requestData("responseHeaders");
    }
  }

  render() {
    let { data } = this.props;
    let requestHeaders = data.request.headers;
    let responseHeaders = data.response.headers;

    // TODO: Another groups to implement:
    // 1) Cached Headers
    // 2) Headers from upload stream
    let groups = [{
      key: "responseHeaders",
      name: Locale.$STR("responseHeaders"),
      params: responseHeaders
    }, {
      key: "requestHeaders",
      name: Locale.$STR("requestHeaders"),
      params: requestHeaders
    }];

    // If response headers are not available yet, display a spinner
    if (!responseHeaders || !responseHeaders.length) {
      groups[0].content = Spinner();
    }

    return (
      dom.div({className: "headersTabBox"},
        dom.div({className: "panelContent"},
          NetInfoGroupList({groups: groups})
        )
      )
    );
  }
}

// Exports from this module
module.exports = HeadersTab;
PK
!<v��ddSchrome/devtools/modules/devtools/client/webconsole/net/components/net-info-body.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/******************************************************************************/
/* Network Info Body */

.netInfoBody {
  margin: 10px 0 0 0;
  width: 100%;
  cursor: default;
  display: block;
}

.netInfoBody *:focus {
  outline: 0 !important;
}

.netInfoBody .panelContent {
  word-break: break-all;
}

/******************************************************************************/
/* Network Info Body Tabs */

.netInfoBody > .tabs {
  background-color: transparent;
  background-image: none;
  height: 100%;
}

.netInfoBody > .tabs .tabs-navigation {
  border-bottom-color: var(--net-border);
  background-color: transparent;
  text-decoration: none;
  padding-top: 3px;
  padding-left: 7px;
  padding-bottom: 1px;
  border-bottom: 1px solid var(--net-border);
}

.netInfoBody > .tabs .tabs-menu {
  display: table;
  list-style: none;
  padding: 0;
  margin: 0;
}

/* This is the trick that makes the tab bottom border invisible */
.netInfoBody > .tabs .tabs-menu-item {
  position: relative;
  bottom: -2px;
  float: left;
}

.netInfoBody > .tabs .tabs-menu-item a {
  display: block;
  border: 1px solid transparent;
  text-decoration: none;
  padding: 5px 8px 4px 8px;;
  font-weight: bold;
  color: var(--theme-body-color);
  border-radius: 4px 4px 0 0;
}

.netInfoBody > .tabs .tab-panel {
  background-color: var(--theme-body-background);
  border: 1px solid transparent;
  border-top: none;
  padding: 10px;
  overflow: auto;
  height: calc(100% - 31px); /* minus the height of the tab bar */
}

.netInfoBody > .tabs .tab-panel > div,
.netInfoBody > .tabs .tab-panel > div > div {
  height: 100%;
}

.netInfoBody > .tabs .tabs-menu-item.is-active a,
.netInfoBody > .tabs .tabs-menu-item.is-active a:focus,
.netInfoBody > .tabs .tabs-menu-item.is-active:hover a {
  background-color: var(--theme-body-background);
  border: 1px solid transparent;
  border-bottom-color: var(--theme-highlight-bluegrey);
  color: var(--theme-highlight-bluegrey);
}

.netInfoBody > .tabs .tabs-menu-item:hover a {
  border: 1px solid transparent;
  border-bottom: 1px solid var(--net-border);
  background-color: var(--theme-body-background);
}


/******************************************************************************/
/* Themes */

.theme-firebug .netInfoBody > .tabs .tab-panel {
  border-color: var(--net-border);
}

.theme-firebug .netInfoBody > .tabs .tabs-menu-item.is-active a,
.theme-firebug .netInfoBody > .tabs .tabs-menu-item.is-active:hover a,
.theme-firebug .netInfoBody > .tabs .tabs-menu-item.is-active a:focus {
  border: 1px solid var(--net-border);
  border-bottom-color: transparent;
}

.theme-firebug .netInfoBody > .tabs .tabs-menu-item:hover a {
  border-bottom-color: transparent;
}
PK
!<�@>99Rchrome/devtools/modules/devtools/client/webconsole/net/components/net-info-body.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/vendor/react");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const { createFactories } = require("devtools/client/shared/react-utils");
const { Tabs, TabPanel } = createFactories(require("devtools/client/shared/components/tabs/Tabs"));

// Network
const HeadersTab = createFactory(require("./headers-tab"));
const ResponseTab = createFactory(require("./response-tab"));
const ParamsTab = createFactory(require("./params-tab"));
const CookiesTab = createFactory(require("./cookies-tab"));
const PostTab = createFactory(require("./post-tab"));
const StackTraceTab = createFactory(require("./stacktrace-tab"));
const NetUtils = require("../utils/net");


/**
 * This template renders the basic Network log info body. It's not
 * visible by default, the user needs to expand the network log
 * to see it.
 *
 * This is the set of tabs displaying details about network events:
 * 1) Headers - request and response headers
 * 2) Params - URL parameters
 * 3) Response - response body
 * 4) Cookies - request and response cookies
 * 5) Post - posted data
 */
class NetInfoBody extends Component {
  static get propTypes() {
    return {
      tabActive: PropTypes.number.isRequired,
      actions: PropTypes.object.isRequired,
      data: PropTypes.shape({
        request: PropTypes.object.isRequired,
        response: PropTypes.object.isRequired
      }),
      // Service to enable the source map feature.
      sourceMapService: PropTypes.object,
    };
  }

  static get defaultProps() {
    return {
      tabActive: 0
    };
  }

  constructor(props) {
    super(props);

    this.state = {
      data: {
        request: {},
        response: {}
      },
      tabActive: props.tabActive,
    };

    this.onTabChanged = this.onTabChanged.bind(this);
    this.hasCookies = this.hasCookies.bind(this);
    this.hasStackTrace = this.hasStackTrace.bind(this);
    this.getTabPanels = this.getTabPanels.bind(this);
  }

  onTabChanged(index) {
    this.setState({tabActive: index});
  }

  hasCookies() {
    let {request, response} = this.state.data;
    return this.state.hasCookies ||
      NetUtils.getHeaderValue(request.headers, "Cookie") ||
      NetUtils.getHeaderValue(response.headers, "Set-Cookie");
  }

  hasStackTrace() {
    let {cause} = this.state.data;
    return cause && cause.stacktrace && cause.stacktrace.length > 0;
  }

  getTabPanels() {
    let { actions, sourceMapService } = this.props;
    let data = this.state.data;
    let {request} = data;

    // Flags for optional tabs. Some tabs are visible only if there
    // are data to display.
    let hasParams = request.queryString && request.queryString.length;
    let hasPostData = request.bodySize > 0;

    let panels = [];

    // Headers tab
    panels.push(
      TabPanel({
        id: "headers",
        className: "headers",
        key: "headers",
        title: Locale.$STR("netRequest.headers")},
        HeadersTab({data: data, actions: actions})
      )
    );

    // URL parameters tab
    if (hasParams) {
      panels.push(
        TabPanel({
          id: "params",
          className: "params",
          key: "params",
          title: Locale.$STR("netRequest.params")},
          ParamsTab({data: data, actions: actions})
        )
      );
    }

    // Posted data tab
    if (hasPostData) {
      panels.push(
        TabPanel({
          id: "post",
          className: "post",
          key: "post",
          title: Locale.$STR("netRequest.post")},
          PostTab({data: data, actions: actions})
        )
      );
    }

    // Response tab
    panels.push(
      TabPanel({
        id: "response",
        className: "response",
        key: "response",
        title: Locale.$STR("netRequest.response")},
        ResponseTab({data: data, actions: actions})
      )
    );

    // Cookies tab
    if (this.hasCookies()) {
      panels.push(
        TabPanel({
          id: "cookies",
          className: "cookies",
          key: "cookies",
          title: Locale.$STR("netRequest.cookies")},
          CookiesTab({
            data: data,
            actions: actions
          })
        )
      );
    }

    // Stacktrace tab
    if (this.hasStackTrace()) {
      panels.push(
        TabPanel({
          id: "stacktrace-tab",
          className: "stacktrace-tab",
          key: "stacktrace",
          title: Locale.$STR("netRequest.callstack")},
          StackTraceTab({
            data: data,
            actions: actions,
            sourceMapService: sourceMapService,
          })
        )
      );
    }

    return panels;
  }

  render() {
    let tabActive = this.state.tabActive;
    let tabPanels = this.getTabPanels();
    return (
      Tabs({
        tabActive: tabActive,
        onAfterChange: this.onTabChanged},
        tabPanels
      )
    );
  }
}

// Exports from this module
module.exports = NetInfoBody;
PK
!<?m�%%Xchrome/devtools/modules/devtools/client/webconsole/net/components/net-info-group-list.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const NetInfoGroup = createFactory(require("./net-info-group"));

/**
 * This template is responsible for rendering sections/groups inside tabs.
 * It's used e.g to display Response and Request headers as separate groups.
 */
class NetInfoGroupList extends Component {
  static get propTypes() {
    return {
      groups: PropTypes.array.isRequired,
    };
  }

  render() {
    let groups = this.props.groups;

    // Filter out empty groups.
    groups = groups.filter(group => {
      return group && ((group.params && group.params.length) || group.content);
    });

    // Render groups
    groups = groups.map(group => {
      group.type = group.key;
      return NetInfoGroup(group);
    });

    return (
      dom.div({className: "netInfoGroupList"},
        groups
      )
    );
  }
}

// Exports from this module
module.exports = NetInfoGroupList;
PK
!<#���??Tchrome/devtools/modules/devtools/client/webconsole/net/components/net-info-group.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/******************************************************************************/
/* Net Info Group */

.netInfoBody .netInfoGroup {
  padding-bottom: 6px;
}

/* Last group doesn't need bottom padding */
.netInfoBody .netInfoGroup:last-child {
  padding-bottom: 0;
}

.netInfoBody .netInfoGroup:last-child .netInfoGroupContent {
  padding-bottom: 0;
}

.netInfoBody .netInfoGroupTitle {
  cursor: pointer;
  font-weight: bold;
  -moz-user-select: none;
  cursor: pointer;
  padding-left: 3px;
}

.netInfoBody .netInfoGroupTwisty {
  background-image: url("chrome://devtools/skin/images/controls.png");
  background-size: 56px 28px;
  background-position: 0 -14px;
  background-repeat: no-repeat;
  width: 14px;
  height: 14px;
  cursor: pointer;
  display: inline-block;
  vertical-align: middle;
}

.netInfoBody .netInfoGroup.opened .netInfoGroupTwisty {
  background-position: -14px -14px;
}

/* Group content is expandable/collapsible by clicking on the title */
.netInfoBody .netInfoGroupContent {
  padding-top: 7px;
  margin-top: 3px;
  padding-bottom: 14px;
  border-top: 1px solid var(--net-border);
  display: none;
}

/* Toggle group visibility */
.netInfoBody .netInfoGroup.opened .netInfoGroupContent {
  display: block;
}

/******************************************************************************/
/* Themes */

.theme-dark .netInfoBody .netInfoGroup {
  color: var(--theme-body-color);
}

.theme-dark .netInfoBody .netInfoGroup .netInfoGroupTwisty {
  filter: invert(1);
}

/* Twisties */
.theme-firebug .netInfoBody .netInfoGroup .netInfoGroupTwisty {
  background-image: url("chrome://devtools/skin/images/firebug/twisty-closed-firebug.svg");
  background-position: 0 2px;
  background-size: 11px 11px;
  width: 15px;
}

.theme-firebug .netInfoBody .netInfoGroup.opened .netInfoGroupTwisty {
  background-image: url("chrome://devtools/skin/images/firebug/twisty-open-firebug.svg");
}
PK
!<�Schrome/devtools/modules/devtools/client/webconsole/net/components/net-info-group.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const NetInfoParams = createFactory(require("./net-info-params"));

/**
 * This template represents a group of data within a tab. For example,
 * Headers tab has two groups 'Request Headers' and 'Response Headers'
 * The Response tab can also have two groups 'Raw Data' and 'JSON'
 */
class NetInfoGroup extends Component {
  static get propTypes() {
    return {
      type: PropTypes.string.isRequired,
      name: PropTypes.string.isRequired,
      params: PropTypes.array,
      content: PropTypes.element,
      open: PropTypes.bool
    };
  }

  static get defaultProps() {
    return {
      open: true,
    };
  }

  constructor(props) {
    super(props);

    this.state = {
      open: props.open,
    };

    this.onToggle = this.onToggle.bind(this);
  }

  onToggle(event) {
    this.setState({
      open: !this.state.open
    });
  }

  render() {
    let content = this.props.content;

    if (!content && this.props.params) {
      content = NetInfoParams({
        params: this.props.params
      });
    }

    let open = this.state.open;
    let className = open ? "opened" : "";

    return (
      dom.div({className: "netInfoGroup" + " " + className + " " +
        this.props.type},
        dom.span({
          className: "netInfoGroupTwisty",
          onClick: this.onToggle
        }),
        dom.span({
          className: "netInfoGroupTitle",
          onClick: this.onToggle},
          this.props.name
        ),
        dom.div({className: "netInfoGroupContent"},
          content
        )
      )
    );
  }
}

// Exports from this module
module.exports = NetInfoGroup;
PK
!<�J^xnnUchrome/devtools/modules/devtools/client/webconsole/net/components/net-info-params.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/******************************************************************************/
/* Net Info Params */

.netInfoBody .netInfoParamName {
  padding: 0 10px 0 0;
  font-weight: bold;
  vertical-align: top;
  text-align: right;
  white-space: nowrap;
}

.netInfoBody .netInfoParamValue {
  width: 100%;
  word-wrap: break-word;
}

.netInfoBody .netInfoParamValue > code {
  font-family: var(--monospace-font-family);
}
PK
!<������Tchrome/devtools/modules/devtools/client/webconsole/net/components/net-info-params.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
/**
 * This template renders list of parameters within a group.
 * It's essentially a list of name + value pairs.
 */
class NetInfoParams extends Component {
  static get propTypes() {
    return {
      params: PropTypes.arrayOf(PropTypes.shape({
        name: PropTypes.string.isRequired,
        value: PropTypes.string.isRequired
      })).isRequired,
    };
  }

  render() {
    let params = this.props.params || [];

    params.sort(function (a, b) {
      return a.name > b.name ? 1 : -1;
    });

    let rows = [];
    params.forEach((param, index) => {
      rows.push(
        dom.tr({key: index},
          dom.td({className: "netInfoParamName"},
            dom.span({title: param.name}, param.name)
          ),
          dom.td({className: "netInfoParamValue"},
            dom.code({}, param.value)
          )
        )
      );
    });

    return (
      dom.table({cellPadding: 0, cellSpacing: 0},
        dom.tbody({},
          rows
        )
      )
    );
  }
}

// Exports from this module
module.exports = NetInfoParams;
PK
!<ު�@��Ochrome/devtools/modules/devtools/client/webconsole/net/components/params-tab.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const NetInfoParams = createFactory(require("./net-info-params"));

/**
 * This template represents 'Params' tab displayed when the user
 * expands network log in the Console panel. It's responsible for
 * displaying URL parameters (query string).
 */
class ParamsTab extends Component {
  static get propTypes() {
    return {
      data: PropTypes.shape({
        request: PropTypes.object.isRequired
      })
    };
  }

  render() {
    let data = this.props.data;

    return (
      dom.div({className: "paramsTabBox"},
        dom.div({className: "panelContent"},
          NetInfoParams({params: data.request.queryString})
        )
      )
    );
  }
}

// Exports from this module
module.exports = ParamsTab;
PK
!<b^�+Mchrome/devtools/modules/devtools/client/webconsole/net/components/post-tab.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");

const TreeView =
  createFactory(require("devtools/client/shared/components/tree/TreeView"));

const { REPS, MODE, parseURLEncodedText } =
  require("devtools/client/shared/components/reps/reps");
const { Rep } = REPS;

// Network
const NetInfoParams = createFactory(require("./net-info-params"));
const NetInfoGroupList = createFactory(require("./net-info-group-list"));
const Spinner = createFactory(require("./spinner"));
const SizeLimit = createFactory(require("./size-limit"));
const NetUtils = require("../utils/net");
const Json = require("../utils/json");

/**
 * This template represents 'Post' tab displayed when the user
 * expands network log in the Console panel. It's responsible for
 * displaying posted data (HTTP post body).
 */
class PostTab extends Component {
  static get propTypes() {
    return {
      data: PropTypes.shape({
        request: PropTypes.object.isRequired
      }),
      actions: PropTypes.object.isRequired
    };
  }

  constructor(props) {
    super(props);
    this.isJson = this.isJson.bind(this);
    this.parseJson = this.parseJson.bind(this);
    this.renderJson = this.renderJson.bind(this);
    this.parseXml = this.parseXml.bind(this);
    this.isXml = this.isXml.bind(this);
    this.renderXml = this.renderXml.bind(this);
    this.renderMultiPart = this.renderMultiPart.bind(this);
    this.renderUrlEncoded = this.renderUrlEncoded.bind(this);
    this.renderRawData = this.renderRawData.bind(this);
  }

  componentDidMount() {
    let { actions, data: file } = this.props;

    if (!file.request.postData) {
      // TODO: use async action objects as soon as Redux is in place
      actions.requestData("requestPostData");
    }
  }

  isJson(file) {
    let text = file.request.postData.text;
    let value = NetUtils.getHeaderValue(file.request.headers, "content-type");
    return Json.isJSON(value, text);
  }

  parseJson(file) {
    let postData = file.request.postData;
    if (!postData) {
      return null;
    }

    let jsonString = new String(postData.text);
    return Json.parseJSONString(jsonString);
  }

  /**
   * Render JSON post data as an expandable tree.
   */
  renderJson(file) {
    let text = file.request.postData.text;
    if (!text || isLongString(text)) {
      return null;
    }

    if (!this.isJson(file)) {
      return null;
    }

    let json = this.parseJson(file);
    if (!json) {
      return null;
    }

    return {
      key: "json",
      content: TreeView({
        columns: [{id: "value"}],
        object: json,
        mode: MODE.TINY,
        renderValue: props => Rep(Object.assign({}, props, {
          cropLimit: 50,
        })),
      }),
      name: Locale.$STR("jsonScopeName")
    };
  }

  parseXml(file) {
    let text = file.request.postData.text;
    if (isLongString(text)) {
      return null;
    }

    return NetUtils.parseXml({
      mimeType: NetUtils.getHeaderValue(file.request.headers, "content-type"),
      text: text,
    });
  }

  isXml(file) {
    if (isLongString(file.request.postData.text)) {
      return false;
    }

    let value = NetUtils.getHeaderValue(file.request.headers, "content-type");
    if (!value) {
      return false;
    }

    return NetUtils.isHTML(value);
  }

  renderXml(file) {
    let text = file.request.postData.text;
    if (!text || isLongString(text)) {
      return null;
    }

    if (!this.isXml(file)) {
      return null;
    }

    let doc = this.parseXml(file);
    if (!doc) {
      return null;
    }

    // Proper component for rendering XML should be used (see bug 1247392)
    return null;
  }

  /**
   * Multipart post data are parsed and nicely rendered
   * as an expandable tree of individual parts.
   */
  renderMultiPart(file) {
    let text = file.request.postData.text;
    if (!text || isLongString(text)) {
      return;
    }

    if (NetUtils.isMultiPartRequest(file)) {
      // TODO: render multi part request (bug: 1247423)
    }
  }

  /**
   * URL encoded post data are nicely rendered as a list
   * of parameters.
   */
  renderUrlEncoded(file) {
    let text = file.request.postData.text;
    if (!text || isLongString(text)) {
      return null;
    }

    if (!NetUtils.isURLEncodedRequest(file)) {
      return null;
    }

    let lines = text.split("\n");
    let params = parseURLEncodedText(lines[lines.length - 1]);

    return {
      key: "url-encoded",
      content: NetInfoParams({params: params}),
      name: Locale.$STR("netRequest.params")
    };
  }

  renderRawData(file) {
    let text = file.request.postData.text;

    let group;

    // The post body might reached the limit, so check if we are
    // dealing with a long string.
    if (typeof text == "object") {
      group = {
        key: "raw-longstring",
        name: Locale.$STR("netRequest.rawData"),
        content: dom.div({className: "netInfoResponseContent"},
          sanitize(text.initial),
          SizeLimit({
            actions: this.props.actions,
            data: file.request.postData,
            message: Locale.$STR("netRequest.sizeLimitMessage"),
            link: Locale.$STR("netRequest.sizeLimitMessageLink")
          })
        )
      };
    } else {
      group = {
        key: "raw",
        name: Locale.$STR("netRequest.rawData"),
        content: dom.div({className: "netInfoResponseContent"},
          sanitize(text)
        )
      };
    }

    return group;
  }

  render() {
    let { data: file } = this.props;

    if (file.discardRequestBody) {
      return dom.span({className: "netInfoBodiesDiscarded"},
        Locale.$STR("netRequest.requestBodyDiscarded")
      );
    }

    if (!file.request.postData) {
      return (
        Spinner()
      );
    }

    // Render post body data. The right representation of the data
    // is picked according to the content type.
    let groups = [];
    groups.push(this.renderUrlEncoded(file));
    // TODO: render multi part request (bug: 1247423)
    // groups.push(this.renderMultiPart(file));
    groups.push(this.renderJson(file));
    groups.push(this.renderXml(file));
    groups.push(this.renderRawData(file));

    // Filter out empty groups.
    groups = groups.filter(group => group);

    // The raw response is collapsed by default if a nice formatted
    // version is available.
    if (groups.length > 1) {
      groups[groups.length - 1].open = false;
    }

    return (
      dom.div({className: "postTabBox"},
        dom.div({className: "panelContent"},
          NetInfoGroupList({
            groups: groups
          })
        )
      )
    );
  }
}

// Helpers

/**
 * Workaround for a "not well-formed" error that react
 * reports when there's multipart data passed to render.
 */
function sanitize(text) {
  text = JSON.stringify(text);
  text = text.replace(/\\r\\n/g, "\r\n").replace(/\\"/g, "\"");
  return text.slice(1, text.length - 1);
}

function isLongString(text) {
  return typeof text == "object";
}

// Exports from this module
module.exports = PostTab;
PK
!<X��@@Rchrome/devtools/modules/devtools/client/webconsole/net/components/response-tab.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/******************************************************************************/
/* Response Tab */

.netInfoBody .netInfoBodiesDiscarded {
  font-style: italic;
  color: gray;
}

.netInfoBody .netInfoResponseContent {
  font-family: var(--monospace-font-family);
  word-wrap: break-word;
}

.netInfoBody .responseTabBox img {
  max-width: 300px;
  max-height: 300px;
}
PK
!<4��>��Qchrome/devtools/modules/devtools/client/webconsole/net/components/response-tab.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");

// Reps
const TreeView = createFactory(require("devtools/client/shared/components/tree/TreeView"));
const { REPS, MODE } = require("devtools/client/shared/components/reps/reps");
const { Rep } = REPS;

// Network
const SizeLimit = createFactory(require("./size-limit"));
const NetInfoGroupList = createFactory(require("./net-info-group-list"));
const Spinner = createFactory(require("./spinner"));
const Json = require("../utils/json");
const NetUtils = require("../utils/net");

/**
 * This template represents 'Response' tab displayed when the user
 * expands network log in the Console panel. It's responsible for
 * rendering HTTP response body.
 *
 * In case of supported response mime-type (e.g. application/json,
 * text/xml, etc.), the response is parsed using appropriate parser
 * and rendered accordingly.
 */
class ResponseTab extends Component {
  static get propTypes() {
    return {
      data: PropTypes.shape({
        request: PropTypes.object.isRequired,
        response: PropTypes.object.isRequired
      }),
      actions: PropTypes.object.isRequired
    };
  }

  constructor(props) {
    super(props);
    this.isJson = this.isJson.bind(this);
    this.parseJson = this.parseJson.bind(this);
    this.isImage = this.isImage.bind(this);
    this.isXml = this.isXml.bind(this);
    this.parseXml = this.parseXml.bind(this);
    this.renderJson = this.renderJson.bind(this);
    this.renderImage = this.renderImage.bind(this);
    this.renderXml = this.renderXml.bind(this);
    this.renderFormattedResponse = this.renderFormattedResponse.bind(this);
    this.renderRawResponse = this.renderRawResponse.bind(this);
  }

  componentDidMount() {
    let { actions, data: file } = this.props;
    let content = file.response.content;

    if (!content || typeof (content.text) == "undefined") {
      // TODO: use async action objects as soon as Redux is in place
      actions.requestData("responseContent");
    }
  }

  // Response Types

  isJson(content) {
    if (isLongString(content.text)) {
      return false;
    }

    return Json.isJSON(content.mimeType, content.text);
  }

  parseJson(file) {
    let content = file.response.content;
    if (isLongString(content.text)) {
      return null;
    }

    let jsonString = new String(content.text);
    return Json.parseJSONString(jsonString);
  }

  isImage(content) {
    if (isLongString(content.text)) {
      return false;
    }

    return NetUtils.isImage(content.mimeType);
  }

  isXml(content) {
    if (isLongString(content.text)) {
      return false;
    }

    return NetUtils.isHTML(content.mimeType);
  }

  parseXml(file) {
    let content = file.response.content;
    if (isLongString(content.text)) {
      return null;
    }

    return NetUtils.parseXml(content);
  }

  // Rendering

  renderJson(file) {
    let content = file.response.content;
    if (!this.isJson(content)) {
      return null;
    }

    let json = this.parseJson(file);
    if (!json) {
      return null;
    }

    return {
      key: "json",
      content: TreeView({
        columns: [{id: "value"}],
        object: json,
        mode: MODE.TINY,
        renderValue: props => Rep(Object.assign({}, props, {
          cropLimit: 50,
        })),
      }),
      name: Locale.$STR("jsonScopeName")
    };
  }

  renderImage(file) {
    let content = file.response.content;
    if (!this.isImage(content)) {
      return null;
    }

    let dataUri = "data:" + content.mimeType + ";base64," + content.text;
    return {
      key: "image",
      content: dom.img({src: dataUri}),
      name: Locale.$STR("netRequest.image")
    };
  }

  renderXml(file) {
    let content = file.response.content;
    if (!this.isXml(content)) {
      return null;
    }

    let doc = this.parseXml(file);
    if (!doc) {
      return null;
    }

    // Proper component for rendering XML should be used (see bug 1247392)
    return null;
  }

  /**
   * If full response text is available, let's try to parse and
   * present nicely according to the underlying format.
   */
  renderFormattedResponse(file) {
    let content = file.response.content;
    if (typeof content.text == "object") {
      return null;
    }

    let group = this.renderJson(file);
    if (group) {
      return group;
    }

    group = this.renderImage(file);
    if (group) {
      return group;
    }

    group = this.renderXml(file);
    if (group) {
      return group;
    }

    return null;
  }

  renderRawResponse(file) {
    let group;
    let content = file.response.content;

    // The response might reached the limit, so check if we are
    // dealing with a long string.
    if (typeof content.text == "object") {
      group = {
        key: "raw-longstring",
        name: Locale.$STR("netRequest.rawData"),
        content: dom.div({className: "netInfoResponseContent"},
          content.text.initial,
          SizeLimit({
            actions: this.props.actions,
            data: content,
            message: Locale.$STR("netRequest.sizeLimitMessage"),
            link: Locale.$STR("netRequest.sizeLimitMessageLink")
          })
        )
      };
    } else {
      group = {
        key: "raw",
        name: Locale.$STR("netRequest.rawData"),
        content: dom.div({className: "netInfoResponseContent"},
          content.text
        )
      };
    }

    return group;
  }

  /**
   * The response panel displays two groups:
   *
   * 1) Formatted response (in case of supported format, e.g. JSON, XML, etc.)
   * 2) Raw response data (always displayed if not discarded)
   */
  render() {
    let { data: file } = this.props;

    // If response bodies are discarded (not collected) let's just
    // display a info message indicating what to do to collect even
    // response bodies.
    if (file.discardResponseBody) {
      return dom.span({className: "netInfoBodiesDiscarded"},
        Locale.$STR("netRequest.responseBodyDiscarded")
      );
    }

    // Request for the response content is done only if the response
    // is not fetched yet - i.e. the `content.text` is undefined.
    // Empty content.text` can also be a valid response either
    // empty or not available yet.
    let content = file.response.content;
    if (!content || typeof (content.text) == "undefined") {
      return (
        Spinner()
      );
    }

    // Render response body data. The right representation of the data
    // is picked according to the content type.
    let groups = [];
    groups.push(this.renderFormattedResponse(file));
    groups.push(this.renderRawResponse(file));

    // Filter out empty groups.
    groups = groups.filter(group => group);

    // The raw response is collapsed by default if a nice formatted
    // version is available.
    if (groups.length > 1) {
      groups[1].open = false;
    }

    return (
      dom.div({className: "responseTabBox"},
        dom.div({className: "panelContent"},
          NetInfoGroupList({
            groups: groups
          })
        )
      )
    );
  }
}

// Helpers

function isLongString(text) {
  return typeof text == "object";
}

// Exports from this module
module.exports = ResponseTab;
PK
!<��x���Pchrome/devtools/modules/devtools/client/webconsole/net/components/size-limit.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/******************************************************************************/
/* Response Size Limit */

.netInfoBody .netInfoSizeLimit {
  font-weight: bold;
  padding-top: 10px;
}

.netInfoBody .netInfoSizeLimit .objectLink {
  color: var(--theme-highlight-blue);
}
PK
!<�!|2��Ochrome/devtools/modules/devtools/client/webconsole/net/components/size-limit.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");

/**
 * This template represents a size limit notification message
 * used e.g. in the Response tab when response body exceeds
 * size limit. The message contains a link allowing the user
 * to fetch the rest of the data from the backend (debugger server).
 */
class SizeLimit extends Component {
  static get propTypes() {
    return {
      data: PropTypes.object.isRequired,
      message: PropTypes.string.isRequired,
      link: PropTypes.string.isRequired,
      actions: PropTypes.shape({
        resolveString: PropTypes.func.isRequired
      }),
    };
  }

  constructor(props) {
    super(props);
    this.onClickLimit = this.onClickLimit.bind(this);
  }

  // Event Handlers

  onClickLimit(event) {
    let actions = this.props.actions;
    let content = this.props.data;

    actions.resolveString(content, "text");
  }

  // Rendering

  render() {
    let message = this.props.message;
    let link = this.props.link;
    let reLink = /^(.*)\{\{link\}\}(.*$)/;
    let m = message.match(reLink);

    return (
        dom.div({className: "netInfoSizeLimit"},
          dom.span({}, m[1]),
          dom.a({
            className: "objectLink",
            onClick: this.onClickLimit},
              link
          ),
          dom.span({}, m[2])
        )
    );
  }
}

// Exports from this module
module.exports = SizeLimit;
PK
!<ב�=��Lchrome/devtools/modules/devtools/client/webconsole/net/components/spinner.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/vendor/react");
const dom = require("devtools/client/shared/vendor/react-dom-factories");

/**
 * This template represents a throbber displayed when the UI
 * is waiting for data coming from the backend (debugging server).
 */
class Spinner extends Component {
  render() {
    return (
      dom.div({className: "devtools-throbber"})
    );
  }
}

// Exports from this module
module.exports = Spinner;
PK
!<p���xxSchrome/devtools/modules/devtools/client/webconsole/net/components/stacktrace-tab.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/vendor/react");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const StackTrace =
  createFactory(require("devtools/client/shared/components/StackTrace"));

class StackTraceTab extends Component {
  static get propTypes() {
    return {
      data: PropTypes.object.isRequired,
      actions: PropTypes.shape({
        onViewSourceInDebugger: PropTypes.func.isRequired
      }),
      // Service to enable the source map feature.
      sourceMapService: PropTypes.object,
    };
  }

  render() {
    let { stacktrace } = this.props.data.cause;
    let { actions, sourceMapService } = this.props;
    let onViewSourceInDebugger = actions.onViewSourceInDebugger.bind(actions);

    return StackTrace({ stacktrace, onViewSourceInDebugger, sourceMapService });
  }
}

// Exports from this module
module.exports = StackTraceTab;
PK
!<��H�!!Gchrome/devtools/modules/devtools/client/webconsole/net/data-provider.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 promise = require("promise");
const defer = require("devtools/shared/defer");

/**
 * Map of pending requests. Used mainly by tests to wait
 * till things are ready.
 */
var promises = new Map();

/**
 * This object is used to fetch network data from the backend.
 * Communication with the chrome scope is based on message
 * exchange.
 */
var DataProvider = {
  hasPendingRequests: function () {
    return promises.size > 0;
  },

  requestData: function (client, actor, method) {
    let key = actor + ":" + method;
    let p = promises.get(key);
    if (p) {
      return p;
    }

    let deferred = defer();
    let realMethodName = "get" + method.charAt(0).toUpperCase() +
      method.slice(1);

    if (!client[realMethodName]) {
      return null;
    }

    client[realMethodName](actor, response => {
      promises.delete(key);
      deferred.resolve(response);
    });

    promises.set(key, deferred.promise);
    return deferred.promise;
  },

  resolveString: function (client, stringGrip) {
    let key = stringGrip.actor + ":getString";
    let p = promises.get(key);
    if (p) {
      return p;
    }

    p = client.getString(stringGrip).then(result => {
      promises.delete(key);
      return result;
    });

    promises.set(key, p);
    return p;
  },
};

// Exports from this module
module.exports = DataProvider;
PK
!<<݁ґ�>chrome/devtools/modules/devtools/client/webconsole/net/main.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 BrowserLoader */

// Initialize module loader and load all modules of the new inline
// preview feature. The entire code-base doesn't need any extra
// privileges and runs entirely in content scope.
const rootUrl = "resource://devtools/client/webconsole/net/";
const require = BrowserLoader({
  baseURI: rootUrl,
  window}).require;

const NetRequest = require("./net-request");
const { loadSheet } = require("devtools/shared/layout/utils");

// Localization
const {LocalizationHelper} = require("devtools/shared/l10n");
const L10N = new LocalizationHelper("devtools/client/locales/netmonitor.properties");

// Stylesheets
var styleSheets = [
  "resource://devtools/client/jsonview/css/toolbar.css",
  "resource://devtools/client/shared/components/tree/TreeView.css",
  "resource://devtools/client/shared/components/reps.css",
  "resource://devtools/client/webconsole/net/net-request.css",
  "resource://devtools/client/webconsole/net/components/size-limit.css",
  "resource://devtools/client/webconsole/net/components/net-info-body.css",
  "resource://devtools/client/webconsole/net/components/net-info-group.css",
  "resource://devtools/client/webconsole/net/components/net-info-params.css",
  "resource://devtools/client/webconsole/net/components/response-tab.css"
];

// Load theme stylesheets into the Console frame. This should be
// done automatically by UI Components as soon as we have consensus
// on the right CSS strategy FIXME.
// It would also be nice to include them using @import.
styleSheets.forEach(url => {
  loadSheet(window, url, "author");
});

// Localization API used by React components
// accessing strings from *.properties file.
// Example:
//   let localizedString = Locale.$STR('string-key');
//
// Resources:
// http://l20n.org/
// https://github.com/yahoo/react-intl
this.Locale = {
  $STR: key => {
    try {
      return L10N.getStr(key);
    } catch (err) {
      console.error(key + ": " + err);
    }
    return key;
  }
};

// List of NetRequest instances represents the state.
// As soon as Redux is in place it should be maintained using a reducer.
var netRequests = new Map();

/**
 * This function handles network events received from the backend. It's
 * executed from within the webconsole.js
 */
function onNetworkEvent(log) {
  // The 'from' field is set only in case of a 'networkEventUpdate' packet.
  // The initial 'networkEvent' packet uses 'actor'.
  // Check if NetRequest object is already created for this event actor and
  // if there is none make sure to create one.
  let response = log.response;
  let netRequest = response.from ? netRequests.get(response.from) : null;
  if (!netRequest && !log.update) {
    netRequest = new NetRequest(log);
    netRequests.set(response.actor, netRequest);
  }

  if (!netRequest) {
    return;
  }

  if (log.update) {
    netRequest.updateBody(response);
  }
}

// Make the 'onNetworkEvent' accessible from chrome (see webconsole.js)
this.NetRequest = {
  onNetworkEvent: onNetworkEvent
};
PK
!<],e��Fchrome/devtools/modules/devtools/client/webconsole/net/net-request.css  /* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/******************************************************************************/
/* General */

:root {
  --net-border: #d7d7d7;
}

:root.theme-dark {
  --net-border: #5f7387;
}

/******************************************************************************/
/* Network log */

/* No background if a Net log is opened */
.netRequest.message.opened,
.netRequest.message.opened:hover {
  background: transparent !important;
}

/******************************************************************************/
/* Themes */

.theme-dark .netRequest.opened:hover,
.theme-dark .netRequest.opened {
  background: transparent;
}

.theme-firebug .netRequest.message.opened:hover {
  background-image: linear-gradient(rgba(214, 233, 246, 0.8), rgba(255, 255, 255, 1.6)) !important;
}
PK
!<���&&Echrome/devtools/modules/devtools/client/webconsole/net/net-request.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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
const React = require("devtools/client/shared/vendor/react");
const ReactDOM = require("devtools/client/shared/vendor/react-dom");

// Reps
const { parseURLParams } = require("devtools/client/shared/components/reps/reps");

// Network
const { cancelEvent, isLeftClick } = require("./utils/events");
const NetInfoBody = React.createFactory(require("./components/net-info-body"));
const DataProvider = require("./data-provider");

// Constants
const XHTML_NS = "http://www.w3.org/1999/xhtml";

/**
 * This object represents a network log in the Console panel (and in the
 * Network panel in the future).
 * It's associated with an existing log and so, also with an existing
 * element in the DOM.
 *
 * The object neither render no request for more data by default. It only
 * reqisters a click listener to the associated log entry (a network event)
 * and changes the class attribute of the log entry, so a twisty icon
 * appears to indicates that there are more details displayed if the
 * log entry is expanded.
 *
 * When the user expands the log, data are requested from the backend
 * and rendered directly within the Console iframe.
 */
function NetRequest(log) {
  this.initialize(log);
}

NetRequest.prototype = {
  initialize: function (log) {
    this.client = log.consoleFrame.webConsoleClient;
    this.owner = log.consoleFrame.owner;

    // 'this.file' field is following HAR spec.
    // http://www.softwareishard.com/blog/har-12-spec/
    this.file = log.response;
    this.parentNode = log.node;
    this.file.request.queryString = parseURLParams(this.file.request.url);
    this.hasCookies = false;

    // Map of fetched responses (to avoid unnecessary RDP round trip).
    this.cachedResponses = new Map();

    let doc = this.parentNode.ownerDocument;
    let twisty = doc.createElementNS(XHTML_NS, "a");
    twisty.className = "theme-twisty";
    twisty.href = "#";

    let messageBody = this.parentNode.querySelector(".message-body-wrapper");
    this.parentNode.insertBefore(twisty, messageBody);
    this.parentNode.setAttribute("collapsible", true);

    this.parentNode.classList.add("netRequest");

    // Register a click listener.
    this.addClickListener();
  },

  addClickListener: function () {
    // Add an event listener to toggle the expanded state when clicked.
    // The event bubbling is canceled if the user clicks on the log
    // itself (not on the expanded body), so opening of the default
    // modal dialog is avoided.
    this.parentNode.addEventListener("click", (event) => {
      if (!isLeftClick(event)) {
        return;
      }

      // Clicking on the toggle button or the method expands/collapses
      // the body with HTTP details.
      let classList = event.originalTarget.classList;
      if (!(classList.contains("theme-twisty") ||
        classList.contains("method"))) {
        return;
      }

      // Alright, the user is clicking fine, let's open HTTP details!
      this.onToggleBody(event);

      // Avoid the default modal dialog
      cancelEvent(event);
    }, true);
  },

  onToggleBody: function (event) {
    let target = event.currentTarget;
    let logRow = target.closest(".netRequest");
    logRow.classList.toggle("opened");

    let twisty = this.parentNode.querySelector(".theme-twisty");
    if (logRow.classList.contains("opened")) {
      twisty.setAttribute("open", true);
    } else {
      twisty.removeAttribute("open");
    }

    let isOpen = logRow.classList.contains("opened");
    if (isOpen) {
      this.renderBody();
    } else {
      this.closeBody();
    }
  },

  updateCookies: function(method, response) {
    // TODO: This code will be part of a reducer.
    let result;
    if (response.cookies > 0 &&
        ["requestCookies", "responseCookies"].includes(method)) {
      this.hasCookies = true;
      this.refresh();
    }
  },

  /**
   * Executed when 'networkEventUpdate' is received from the backend.
   */
  updateBody: function (response) {
    // 'networkEventUpdate' event indicates that there are new data
    // available on the backend. The following logic checks the response
    // cache and if this data has been already requested before they
    // need to be updated now (re-requested).
    let method = response.updateType;
    this.updateCookies(method, response);
    if (this.cachedResponses.get(method)) {
      this.cachedResponses.delete(method);
      this.requestData(method);
    }
  },

  /**
   * Close network inline preview body.
   */
  closeBody: function () {
    this.netInfoBodyBox.remove();
  },

  /**
   * Render network inline preview body.
   */
  renderBody: function () {
    let messageBody = this.parentNode.querySelector(".message-body-wrapper");

    // Create box for all markup rendered by ReactJS. Since we are
    // rendering within webconsole.xul (i.e. XUL document) we need
    // to explicitly specify XHTML namespace.
    let doc = messageBody.ownerDocument;
    this.netInfoBodyBox = doc.createElementNS(XHTML_NS, "div");
    this.netInfoBodyBox.classList.add("netInfoBody");
    messageBody.appendChild(this.netInfoBodyBox);

    // As soon as Redux is in place state and actions will come from
    // separate modules.
    let body = NetInfoBody({
      actions: this,
      sourceMapService: this.owner.sourceMapURLService,
    });

    // Render net info body!
    this.body = ReactDOM.render(body, this.netInfoBodyBox);

    this.refresh();
  },

  /**
   * Render top level ReactJS component.
   */
  refresh: function () {
    if (!this.netInfoBodyBox) {
      return;
    }

    // TODO: As soon as Redux is in place there will be reducer
    // computing a new state.
    let newState = Object.assign({}, this.body.state, {
      data: this.file,
      hasCookies: this.hasCookies
    });

    this.body.setState(newState);
  },

  // Communication with the backend

  requestData: function (method) {
    // If the response has already been received bail out.
    let response = this.cachedResponses.get(method);
    if (response) {
      return;
    }

    // Set an attribute indicating that this net log is waiting for
    // data coming from the backend. Intended mainly for tests.
    this.parentNode.setAttribute("loading", "true");

    let actor = this.file.actor;
    DataProvider.requestData(this.client, actor, method).then(args => {
      this.cachedResponses.set(method, args);
      this.onRequestData(method, args);

      if (!DataProvider.hasPendingRequests()) {
        this.parentNode.removeAttribute("loading");

        // Fire an event indicating that all pending requests for
        // data from the backend has finished. Intended for tests.
        // Do it asynchronously so, it's done after all handlers
        // for the current promise are executed.
        setTimeout(() => {
          let event = document.createEvent("Event");
          event.initEvent("netlog-no-pending-requests", true, true);
          this.parentNode.dispatchEvent(event);
        });
      }
    });
  },

  onRequestData: function (method, response) {
    // TODO: This code will be part of a reducer.
    let result;
    switch (method) {
      case "requestHeaders":
        result = this.onRequestHeaders(response);
        break;
      case "responseHeaders":
        result = this.onResponseHeaders(response);
        break;
      case "requestCookies":
        result = this.onRequestCookies(response);
        break;
      case "responseCookies":
        result = this.onResponseCookies(response);
        break;
      case "responseContent":
        result = this.onResponseContent(response);
        break;
      case "requestPostData":
        result = this.onRequestPostData(response);
        break;
    }

    result.then(() => {
      this.refresh();
    });
  },

  onRequestHeaders: function (response) {
    this.file.request.headers = response.headers;

    return this.resolveHeaders(this.file.request.headers);
  },

  onResponseHeaders: function (response) {
    this.file.response.headers = response.headers;

    return this.resolveHeaders(this.file.response.headers);
  },

  onResponseContent: function (response) {
    let content = response.content;

    for (let p in content) {
      this.file.response.content[p] = content[p];
    }

    return Promise.resolve();
  },

  onRequestPostData: function (response) {
    this.file.request.postData = response.postData;
    return Promise.resolve();
  },

  onRequestCookies: function (response) {
    this.file.request.cookies = response.cookies;
    return this.resolveHeaders(this.file.request.cookies);
  },

  onResponseCookies: function (response) {
    this.file.response.cookies = response.cookies;
    return this.resolveHeaders(this.file.response.cookies);
  },

  onViewSourceInDebugger: function (frame) {
    this.owner.viewSourceInDebugger(frame.source, frame.line);
  },

  resolveHeaders: function (headers) {
    let promises = [];

    for (let header of headers) {
      if (typeof header.value == "object") {
        promises.push(this.resolveString(header.value).then(value => {
          header.value = value;
        }));
      }
    }

    return Promise.all(promises);
  },

  resolveString: function (object, propName) {
    let stringGrip = object[propName];
    if (typeof stringGrip == "object") {
      DataProvider.resolveString(this.client, stringGrip).then(args => {
        object[propName] = args;
        this.refresh();
      });
    }
  }
};

// Exports from this module
module.exports = NetRequest;
PK
!<�� ���Fchrome/devtools/modules/devtools/client/webconsole/net/utils/events.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 isLeftClick(event, allowKeyModifiers) {
  return event.button === 0 && (allowKeyModifiers || noKeyModifiers(event));
}

function noKeyModifiers(event) {
  return !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey;
}

function cancelEvent(event) {
  event.stopPropagation();
  event.preventDefault();
}

// Exports from this module
exports.isLeftClick = isLeftClick;
exports.cancelEvent = cancelEvent;
PK
!<�ȹw��Dchrome/devtools/modules/devtools/client/webconsole/net/utils/json.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";

// List of JSON content types.
const contentTypes = {
  "text/plain": 1,
  "text/javascript": 1,
  "text/x-javascript": 1,
  "text/json": 1,
  "text/x-json": 1,
  "application/json": 1,
  "application/x-json": 1,
  "application/javascript": 1,
  "application/x-javascript": 1,
  "application/json-rpc": 1
};

// Implementation
var Json = {};

/**
 * Parsing JSON
 */
Json.parseJSONString = function (jsonString) {
  if (!jsonString.length) {
    return null;
  }

  let regex, matches;

  let first = firstNonWs(jsonString);
  if (first !== "[" && first !== "{") {
    // This (probably) isn't pure JSON. Let's try to strip various sorts
    // of XSSI protection/wrapping and see if that works better.

    // Prototype-style secure requests
    regex = /^\s*\/\*-secure-([\s\S]*)\*\/\s*$/;
    matches = regex.exec(jsonString);
    if (matches) {
      jsonString = matches[1];

      if (jsonString[0] === "\\" && jsonString[1] === "n") {
        jsonString = jsonString.substr(2);
      }

      if (jsonString[jsonString.length - 2] === "\\" &&
        jsonString[jsonString.length - 1] === "n") {
        jsonString = jsonString.substr(0, jsonString.length - 2);
      }
    }

    // Google-style (?) delimiters
    if (jsonString.includes("&&&START&&&")) {
      regex = /&&&START&&&([\s\S]*)&&&END&&&/;
      matches = regex.exec(jsonString);
      if (matches) {
        jsonString = matches[1];
      }
    }

    // while(1);, for(;;);, and )]}'
    regex = /^\s*(\)\]\}[^\n]*\n|while\s*\(1\);|for\s*\(;;\);)([\s\S]*)/;
    matches = regex.exec(jsonString);
    if (matches) {
      jsonString = matches[2];
    }

    // JSONP
    regex = /^\s*([A-Za-z0-9_$.]+\s*(?:\[.*\]|))\s*\(([\s\S]*)\)/;
    matches = regex.exec(jsonString);
    if (matches) {
      jsonString = matches[2];
    }
  }

  try {
    return JSON.parse(jsonString);
  } catch (err) {
    // eslint-disable-line no-empty
  }

  // Give up if we don't have valid start, to avoid some unnecessary overhead.
  first = firstNonWs(jsonString);
  if (first !== "[" && first !== "{" && isNaN(first) && first !== '"') {
    return null;
  }

  // Remove JavaScript comments, quote non-quoted identifiers, and merge
  // multi-line structures like |{"a": 1} \n {"b": 2}| into a single JSON
  // object [{"a": 1}, {"b": 2}].
  jsonString = pseudoJsonToJson(jsonString);

  try {
    return JSON.parse(jsonString);
  } catch (err) {
    // eslint-disable-line no-empty
  }

  return null;
};

function firstNonWs(str) {
  for (let i = 0, len = str.length; i < len; i++) {
    let ch = str[i];
    if (ch !== " " && ch !== "\n" && ch !== "\t" && ch !== "\r") {
      return ch;
    }
  }
  return "";
}

function pseudoJsonToJson(json) {
  let ret = "";
  let at = 0, lasti = 0, lastch = "", hasMultipleParts = false;
  for (let i = 0, len = json.length; i < len; ++i) {
    let ch = json[i];
    if (/\s/.test(ch)) {
      continue;
    }

    if (ch === '"') {
      // Consume a string.
      ++i;
      while (i < len) {
        if (json[i] === "\\") {
          ++i;
        } else if (json[i] === '"') {
          break;
        }
        ++i;
      }
    } else if (ch === "'") {
      // Convert an invalid string into a valid one.
      ret += json.slice(at, i) + "\"";
      at = i + 1;
      ++i;

      while (i < len) {
        if (json[i] === "\\") {
          ++i;
        } else if (json[i] === "'") {
          break;
        }
        ++i;
      }

      if (i < len) {
        ret += json.slice(at, i) + "\"";
        at = i + 1;
      }
    } else if ((ch === "[" || ch === "{") &&
        (lastch === "]" || lastch === "}")) {
      // Multiple JSON messages in one... Make it into a single array by
      // inserting a comma and setting the "multiple parts" flag.
      ret += json.slice(at, i) + ",";
      hasMultipleParts = true;
      at = i;
    } else if (lastch === "," && (ch === "]" || ch === "}")) {
      // Trailing commas in arrays/objects.
      ret += json.slice(at, lasti);
      at = i;
    } else if (lastch === "/" && lasti === i - 1) {
      // Some kind of comment; remove it.
      if (ch === "/") {
        ret += json.slice(at, i - 1);
        at = i + json.slice(i).search(/\n|\r|$/);
        i = at - 1;
      } else if (ch === "*") {
        ret += json.slice(at, i - 1);
        at = json.indexOf("*/", i + 1) + 2;
        if (at === 1) {
          at = len;
        }
        i = at - 1;
      }
      ch = "\0";
    } else if (/[a-zA-Z$_]/.test(ch) && lastch !== ":") {
      // Non-quoted identifier. Quote it.
      ret += json.slice(at, i) + "\"";
      at = i;
      i = i + json.slice(i).search(/[^a-zA-Z0-9$_]|$/);
      ret += json.slice(at, i) + "\"";
      at = i;
    }

    lastch = ch;
    lasti = i;
  }

  ret += json.slice(at);
  if (hasMultipleParts) {
    ret = "[" + ret + "]";
  }

  return ret;
}

Json.isJSON = function (contentType, data) {
  // Workaround for JSON responses without proper content type
  // Let's consider all responses starting with "{" as JSON. In the worst
  // case there will be an exception when parsing. This means that no-JSON
  // responses (and post data) (with "{") can be parsed unnecessarily,
  // which represents a little overhead, but this happens only if the request
  // is actually expanded by the user in the UI (Net & Console panels).
  // Do a manual string search instead of checking (data.strip()[0] === "{")
  // to improve performance/memory usage.
  let len = data ? data.length : 0;
  for (let i = 0; i < len; i++) {
    let ch = data.charAt(i);
    if (ch === "{") {
      return true;
    }

    if (ch === " " || ch === "\t" || ch === "\n" || ch === "\r") {
      continue;
    }

    break;
  }

  if (!contentType) {
    return false;
  }

  contentType = contentType.split(";")[0];
  contentType = contentType.trim();
  return !!contentTypes[contentType];
};

// Exports from this module
module.exports = Json;

PK
!<�&)00Cchrome/devtools/modules/devtools/client/webconsole/net/utils/net.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 mimeCategoryMap = {
  "text/plain": "txt",
  "application/octet-stream": "bin",
  "text/html": "html",
  "text/xml": "html",
  "application/xml": "html",
  "application/rss+xml": "html",
  "application/atom+xml": "html",
  "application/xhtml+xml": "html",
  "application/mathml+xml": "html",
  "application/rdf+xml": "html",
  "text/css": "css",
  "application/x-javascript": "js",
  "text/javascript": "js",
  "application/javascript": "js",
  "text/ecmascript": "js",
  "application/ecmascript": "js",
  "image/jpeg": "image",
  "image/jpg": "image",
  "image/gif": "image",
  "image/png": "image",
  "image/bmp": "image",
  "application/x-shockwave-flash": "plugin",
  "application/x-silverlight-app": "plugin",
  "video/x-flv": "media",
  "audio/mpeg3": "media",
  "audio/x-mpeg-3": "media",
  "video/mpeg": "media",
  "video/x-mpeg": "media",
  "video/webm": "media",
  "video/mp4": "media",
  "video/ogg": "media",
  "audio/ogg": "media",
  "application/ogg": "media",
  "application/x-ogg": "media",
  "application/x-midi": "media",
  "audio/midi": "media",
  "audio/x-mid": "media",
  "audio/x-midi": "media",
  "music/crescendo": "media",
  "audio/wav": "media",
  "audio/x-wav": "media",
  "application/x-woff": "font",
  "application/font-woff": "font",
  "application/x-font-woff": "font",
  "application/x-ttf": "font",
  "application/x-font-ttf": "font",
  "font/ttf": "font",
  "font/woff": "font",
  "application/x-otf": "font",
  "application/x-font-otf": "font"
};

var NetUtils = {};

NetUtils.isImage = function (contentType) {
  if (!contentType) {
    return false;
  }

  contentType = contentType.split(";")[0];
  contentType = contentType.trim();
  return mimeCategoryMap[contentType] == "image";
};

NetUtils.isHTML = function (contentType) {
  if (!contentType) {
    return false;
  }

  contentType = contentType.split(";")[0];
  contentType = contentType.trim();
  return mimeCategoryMap[contentType] == "html";
};

NetUtils.getHeaderValue = function (headers, name) {
  if (!headers) {
    return null;
  }

  name = name.toLowerCase();
  for (let i = 0; i < headers.length; ++i) {
    let headerName = headers[i].name.toLowerCase();
    if (headerName == name) {
      return headers[i].value;
    }
  }
};

NetUtils.parseXml = function (content) {
  let contentType = content.mimeType.split(";")[0];
  contentType = contentType.trim();

  let parser = new DOMParser();
  let doc = parser.parseFromString(content.text, contentType);
  let root = doc.documentElement;

  // Error handling
  let nsURI = "http://www.mozilla.org/newlayout/xml/parsererror.xml";
  if (root.namespaceURI == nsURI && root.nodeName == "parsererror") {
    return null;
  }

  return doc;
};

NetUtils.isURLEncodedRequest = function (file) {
  let mimeType = "application/x-www-form-urlencoded";

  let postData = file.request.postData;
  if (postData && postData.text) {
    let text = postData.text.toLowerCase();
    if (text.startsWith("content-type: " + mimeType)) {
      return true;
    }
  }

  let value = NetUtils.getHeaderValue(file.request.headers, "content-type");
  return value && value.startsWith(mimeType);
};

NetUtils.isMultiPartRequest = function (file) {
  let mimeType = "multipart/form-data";
  let value = NetUtils.getHeaderValue(file.request.headers, "content-type");
  return value && value.startsWith(mimeType);
};

// Exports from this module
module.exports = NetUtils;
PK
!<�t��UUXchrome/devtools/modules/devtools/client/webconsole/new-console-output/actions/filters.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 { getAllFilters } = require("devtools/client/webconsole/new-console-output/selectors/filters");

const {
  FILTER_TEXT_SET,
  FILTER_TOGGLE,
  FILTERS_CLEAR,
  DEFAULT_FILTERS_RESET,
  PREFS,
  FILTERS,
  DEFAULT_FILTERS,
} = require("devtools/client/webconsole/new-console-output/constants");

function filterTextSet(text) {
  return {
    type: FILTER_TEXT_SET,
    text
  };
}

function filterToggle(filter) {
  return (dispatch, getState, {prefsService}) => {
    dispatch({
      type: FILTER_TOGGLE,
      filter,
    });
    const filterState = getAllFilters(getState());
    prefsService.setBoolPref(PREFS.FILTER[filter.toUpperCase()], filterState[filter]);
  };
}

function filtersClear() {
  return (dispatch, getState, {prefsService}) => {
    dispatch({
      type: FILTERS_CLEAR,
    });

    const filterState = getAllFilters(getState());
    for (let filter in filterState) {
      if (filter !== FILTERS.TEXT) {
        prefsService.clearUserPref(PREFS.FILTER[filter.toUpperCase()]);
      }
    }
  };
}

/**
 * Set the default filters to their original values.
 * This is different than filtersClear where we reset
 * all the filters to their original values. Here we want
 * to keep non-default filters the user might have set.
 */
function defaultFiltersReset() {
  return (dispatch, getState, {prefsService}) => {
    // Get the state before dispatching so the action does not alter prefs reset.
    const filterState = getAllFilters(getState());

    dispatch({
      type: DEFAULT_FILTERS_RESET,
    });

    DEFAULT_FILTERS.forEach(filter => {
      if (filterState[filter] === false) {
        prefsService.clearUserPref(PREFS.FILTER[filter.toUpperCase()]);
      }
    });
  };
}

module.exports = {
  filterTextSet,
  filterToggle,
  filtersClear,
  defaultFiltersReset,
};
PK
!<����Vchrome/devtools/modules/devtools/client/webconsole/new-console-output/actions/index.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 actionModules = [
  require("./filters"),
  require("./messages"),
  require("./ui"),
];

const actions = Object.assign({}, ...actionModules);

module.exports = actions;
PK
!<���d��Ychrome/devtools/modules/devtools/client/webconsole/new-console-output/actions/messages.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 {
  prepareMessage
} = require("devtools/client/webconsole/new-console-output/utils/messages");
const { IdGenerator } = require("devtools/client/webconsole/new-console-output/utils/id-generator");
const { batchActions } = require("devtools/client/shared/redux/middleware/debounce");

const {
  MESSAGES_ADD,
  NETWORK_MESSAGE_UPDATE,
  NETWORK_UPDATE_REQUEST,
  MESSAGES_CLEAR,
  MESSAGE_OPEN,
  MESSAGE_CLOSE,
  MESSAGE_TYPE,
  MESSAGE_TABLE_RECEIVE,
  PRIVATE_MESSAGES_CLEAR,
} = require("../constants");

const defaultIdGenerator = new IdGenerator();

function messagesAdd(packets, idGenerator = null) {
  if (idGenerator == null) {
    idGenerator = defaultIdGenerator;
  }
  let messages = packets.map(packet => prepareMessage(packet, idGenerator));
  for (let i = messages.length - 1; i >= 0; i--) {
    if (messages[i].type === MESSAGE_TYPE.CLEAR) {
      return batchActions([
        messagesClear(),
        {
          type: MESSAGES_ADD,
          messages: messages.slice(i),
        }
      ]);
    }
  }

  // When this is used for non-cached messages then handle clear message and
  // split up into batches
  return {
    type: MESSAGES_ADD,
    messages
  };
}

function messagesClear() {
  return {
    type: MESSAGES_CLEAR
  };
}

function privateMessagesClear() {
  return {
    type: PRIVATE_MESSAGES_CLEAR
  };
}

function messageOpen(id) {
  return {
    type: MESSAGE_OPEN,
    id
  };
}

function messageClose(id) {
  return {
    type: MESSAGE_CLOSE,
    id
  };
}

function messageTableDataGet(id, client, dataType) {
  return (dispatch) => {
    let fetchObjectActorData;
    if (["Map", "WeakMap", "Set", "WeakSet"].includes(dataType)) {
      fetchObjectActorData = (cb) => client.enumEntries(cb);
    } else {
      fetchObjectActorData = (cb) => client.enumProperties({
        ignoreNonIndexedProperties: dataType === "Array"
      }, cb);
    }

    fetchObjectActorData(enumResponse => {
      const {iterator} = enumResponse;
      iterator.slice(0, iterator.count, sliceResponse => {
        let {ownProperties} = sliceResponse;
        dispatch(messageTableDataReceive(id, ownProperties));
      });
    });
  };
}

function messageTableDataReceive(id, data) {
  return {
    type: MESSAGE_TABLE_RECEIVE,
    id,
    data
  };
}

function networkMessageUpdate(packet, idGenerator = null, response) {
  if (idGenerator == null) {
    idGenerator = defaultIdGenerator;
  }

  let message = prepareMessage(packet, idGenerator);

  return {
    type: NETWORK_MESSAGE_UPDATE,
    message,
    response,
  };
}

function networkUpdateRequest(id, data) {
  return {
    type: NETWORK_UPDATE_REQUEST,
    id,
    data,
  };
}

module.exports = {
  messagesAdd,
  messagesClear,
  messageOpen,
  messageClose,
  messageTableDataGet,
  networkMessageUpdate,
  networkUpdateRequest,
  privateMessagesClear,
  // for test purpose only.
  messageTableDataReceive,
};
PK
!<z˕�Schrome/devtools/modules/devtools/client/webconsole/new-console-output/actions/ui.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 { getAllUi } = require("devtools/client/webconsole/new-console-output/selectors/ui");
const { getMessage } = require("devtools/client/webconsole/new-console-output/selectors/messages");

const {
  FILTER_BAR_TOGGLE,
  INITIALIZE,
  PERSIST_TOGGLE,
  PREFS,
  SELECT_NETWORK_MESSAGE_TAB,
  SIDEBAR_CLOSE,
  SHOW_OBJECT_IN_SIDEBAR,
  TIMESTAMPS_TOGGLE,
} = require("devtools/client/webconsole/new-console-output/constants");

function filterBarToggle(show) {
  return (dispatch, getState, {prefsService}) => {
    dispatch({
      type: FILTER_BAR_TOGGLE,
    });
    const {filterBarVisible} = getAllUi(getState());
    prefsService.setBoolPref(PREFS.UI.FILTER_BAR, filterBarVisible);
  };
}

function persistToggle(show) {
  return (dispatch, getState, {prefsService}) => {
    dispatch({
      type: PERSIST_TOGGLE,
    });
    const uiState = getAllUi(getState());
    prefsService.setBoolPref(PREFS.UI.PERSIST, uiState.persistLogs);
  };
}

function timestampsToggle(visible) {
  return {
    type: TIMESTAMPS_TOGGLE,
    visible,
  };
}

function selectNetworkMessageTab(id) {
  return {
    type: SELECT_NETWORK_MESSAGE_TAB,
    id,
  };
}

function initialize() {
  return {
    type: INITIALIZE
  };
}

function sidebarClose(show) {
  return {
    type: SIDEBAR_CLOSE,
  };
}

function showObjectInSidebar(actorId, messageId) {
  return (dispatch, getState) => {
    let { parameters } = getMessage(getState(), messageId);
    if (Array.isArray(parameters)) {
      for (let parameter of parameters) {
        if (parameter.actor === actorId) {
          dispatch({
            type: SHOW_OBJECT_IN_SIDEBAR,
            grip: parameter
          });
          return;
        }
      }
    }
  };
}

module.exports = {
  filterBarToggle,
  initialize,
  persistToggle,
  selectNetworkMessageTab,
  sidebarClose,
  showObjectInSidebar,
  timestampsToggle,
};
PK
!<�ޥ$llbchrome/devtools/modules/devtools/client/webconsole/new-console-output/components/CollapseButton.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 dom = require("devtools/client/shared/vendor/react-dom-factories");

const { l10n } = require("devtools/client/webconsole/new-console-output/utils/messages");
const messageToggleDetails = l10n.getStr("messageToggleDetails");

function CollapseButton(props) {
  const {
    open,
    onClick,
    title = messageToggleDetails,
  } = props;

  let classes = ["theme-twisty"];

  if (open) {
    classes.push("open");
  }

  return dom.a({
    className: classes.join(" "),
    onClick,
    title: title,
  });
}

module.exports = CollapseButton;
PK
!<
{J_��achrome/devtools/modules/devtools/client/webconsole/new-console-output/components/ConsoleOutput.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const { connect } = require("devtools/client/shared/redux/visibility-handler-connect");
const {initialize} = require("devtools/client/webconsole/new-console-output/actions/ui");

const {
  getAllMessagesById,
  getAllMessagesUiById,
  getAllMessagesTableDataById,
  getAllNetworkMessagesUpdateById,
  getVisibleMessages,
  getAllRepeatById,
} = require("devtools/client/webconsole/new-console-output/selectors/messages");
const MessageContainer = createFactory(require("devtools/client/webconsole/new-console-output/components/MessageContainer").MessageContainer);
const {
  MESSAGE_TYPE,
} = require("devtools/client/webconsole/new-console-output/constants");
const {
  getInitialMessageCountForViewport
} = require("devtools/client/webconsole/new-console-output/utils/messages.js");

class ConsoleOutput extends Component {
  static get propTypes() {
    return {
      initialized: PropTypes.bool.isRequired,
      messages: PropTypes.object.isRequired,
      messagesUi: PropTypes.array.isRequired,
      serviceContainer: PropTypes.shape({
        attachRefToHud: PropTypes.func.isRequired,
        openContextMenu: PropTypes.func.isRequired,
        sourceMapService: PropTypes.object,
      }),
      dispatch: PropTypes.func.isRequired,
      timestampsVisible: PropTypes.bool,
      messagesTableData: PropTypes.object.isRequired,
      messagesRepeat: PropTypes.object.isRequired,
      networkMessagesUpdate: PropTypes.object.isRequired,
      visibleMessages: PropTypes.array.isRequired,
      networkMessageActiveTabId: PropTypes.string.isRequired,
      onFirstMeaningfulPaint: PropTypes.func.isRequired,
    };
  }

  constructor(props) {
    super(props);
    this.onContextMenu = this.onContextMenu.bind(this);
  }

  componentDidMount() {
    scrollToBottom(this.outputNode);
    this.props.serviceContainer.attachRefToHud("outputScroller", this.outputNode);

    // Waiting for the next paint.
    new Promise(res => requestAnimationFrame(res))
      .then(() => {
        if (this.props.onFirstMeaningfulPaint) {
          this.props.onFirstMeaningfulPaint();
        }

        // Dispatching on next tick so we don't block on action execution.
        setTimeout(() => {
          this.props.dispatch(initialize());
        }, 0);
      });
  }

  componentWillUpdate(nextProps, nextState) {
    const outputNode = this.outputNode;
    if (!outputNode || !outputNode.lastChild) {
      // Force a scroll to bottom when messages are added to an empty console.
      // This makes the console stay pinned to the bottom if a batch of messages
      // are added after a page refresh (Bug 1402237).
      this.shouldScrollBottom = true;
      return;
    }

    const lastChild = outputNode.lastChild;
    const visibleMessagesDelta =
      nextProps.visibleMessages.length - this.props.visibleMessages.length;
    const messagesDelta =
      nextProps.messages.size - this.props.messages.size;

    // We need to scroll to the bottom if:
    // - we are reacting to the "initialize" action,
    //   and we are already scrolled to the bottom
    // - the number of messages displayed changed
    //   and we are already scrolled to the bottom
    // - the number of messages in the store changed
    //   and the new message is an evaluation result.
    const isNewMessageEvaluationResult = messagesDelta > 0 &&
      [...nextProps.messages.values()][nextProps.messages.size - 1].type
        === MESSAGE_TYPE.RESULT;

    this.shouldScrollBottom =
      (
        !this.props.initialized &&
        nextProps.initialized &&
        isScrolledToBottom(lastChild, outputNode)
      ) ||
      (isNewMessageEvaluationResult) ||
      (visibleMessagesDelta > 0 && isScrolledToBottom(lastChild, outputNode));
  }

  componentDidUpdate() {
    if (this.shouldScrollBottom) {
      scrollToBottom(this.outputNode);
    }
  }

  onContextMenu(e) {
    this.props.serviceContainer.openContextMenu(e);
    e.stopPropagation();
    e.preventDefault();
  }

  render() {
    let {
      dispatch,
      visibleMessages,
      messages,
      messagesUi,
      messagesTableData,
      messagesRepeat,
      networkMessagesUpdate,
      networkMessageActiveTabId,
      serviceContainer,
      timestampsVisible,
      initialized,
    } = this.props;

    if (!initialized) {
      const numberMessagesFitViewport = getInitialMessageCountForViewport(window);
      if (numberMessagesFitViewport < visibleMessages.length) {
        visibleMessages = visibleMessages.slice(
          visibleMessages.length - numberMessagesFitViewport);
      }
    }

    let messageNodes = visibleMessages.map((messageId) => MessageContainer({
      dispatch,
      key: messageId,
      messageId,
      serviceContainer,
      open: messagesUi.includes(messageId),
      tableData: messagesTableData.get(messageId),
      timestampsVisible,
      repeat: messagesRepeat[messageId],
      networkMessageUpdate: networkMessagesUpdate[messageId],
      networkMessageActiveTabId,
      getMessage: () => messages.get(messageId),
    }));

    return (
      dom.div({
        className: "webconsole-output",
        onContextMenu: this.onContextMenu,
        ref: node => {
          this.outputNode = node;
        },
      }, messageNodes
      )
    );
  }
}

function scrollToBottom(node) {
  node.scrollTop = node.scrollHeight;
}

function isScrolledToBottom(outputNode, scrollNode) {
  let lastNodeHeight = outputNode.lastChild ?
                       outputNode.lastChild.clientHeight : 0;
  return scrollNode.scrollTop + scrollNode.clientHeight >=
         scrollNode.scrollHeight - lastNodeHeight / 2;
}

function mapStateToProps(state, props) {
  return {
    initialized: state.ui.initialized,
    messages: getAllMessagesById(state),
    visibleMessages: getVisibleMessages(state),
    messagesUi: getAllMessagesUiById(state),
    messagesTableData: getAllMessagesTableDataById(state),
    messagesRepeat: getAllRepeatById(state),
    networkMessagesUpdate: getAllNetworkMessagesUpdateById(state),
    timestampsVisible: state.ui.timestampsVisible,
    networkMessageActiveTabId: state.ui.networkMessageActiveTabId,
  };
}

module.exports = connect(mapStateToProps)(ConsoleOutput);
PK
!<�}�*��`chrome/devtools/modules/devtools/client/webconsole/new-console-output/components/ConsoleTable.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const ObjectClient = require("devtools/shared/client/object-client");
const actions = require("devtools/client/webconsole/new-console-output/actions/messages");
const { l10n } = require("devtools/client/webconsole/new-console-output/utils/messages");
const { MODE } = require("devtools/client/shared/components/reps/reps");
const GripMessageBody = createFactory(require("devtools/client/webconsole/new-console-output/components/GripMessageBody"));

const TABLE_ROW_MAX_ITEMS = 1000;
const TABLE_COLUMN_MAX_ITEMS = 10;

class ConsoleTable extends Component {
  static get propTypes() {
    return {
      dispatch: PropTypes.func.isRequired,
      parameters: PropTypes.array.isRequired,
      serviceContainer: PropTypes.shape({
        hudProxy: PropTypes.object.isRequired,
      }),
      id: PropTypes.string.isRequired,
      tableData: PropTypes.object,
    };
  }

  constructor(props) {
    super(props);
    this.getHeaders = this.getHeaders.bind(this);
    this.getRows = this.getRows.bind(this);
  }

  componentWillMount() {
    const {id, dispatch, serviceContainer, parameters} = this.props;

    if (!Array.isArray(parameters) || parameters.length === 0) {
      return;
    }

    const client = new ObjectClient(serviceContainer.hudProxy.client, parameters[0]);
    let dataType = getParametersDataType(parameters);

    // Get all the object properties.
    dispatch(actions.messageTableDataGet(id, client, dataType));
  }

  getHeaders(columns) {
    let headerItems = [];
    columns.forEach((value, key) => headerItems.push(dom.div({
      className: "new-consoletable-header",
      role: "columnheader"
    }, value))
  );
    return headerItems;
  }

  getRows(columns, items) {
    const {
      dispatch,
      serviceContainer,
    } = this.props;

    return items.map((item, index) => {
      let cells = [];
      columns.forEach((value, key) => {
        cells.push(
          dom.div(
            {
              role: "gridcell",
              className: (index % 2) ? "odd" : "even"
            },
            GripMessageBody({
              grip: item[key],
              mode: MODE.SHORT,
              useQuotes: false,
              serviceContainer,
              dispatch,
            })
          )
        );
      });
      return cells;
    });
  }

  render() {
    const {parameters, tableData} = this.props;
    const headersGrip = parameters[1];
    const headers = headersGrip && headersGrip.preview ? headersGrip.preview.items : null;

    // if tableData is nullable, we don't show anything.
    if (!tableData) {
      return null;
    }

    const {columns, items} = getTableItems(
      tableData,
      getParametersDataType(parameters),
      headers
    );

    return (
      dom.div({
        className: "new-consoletable",
        role: "grid",
        style: {
          gridTemplateColumns: `repeat(${columns.size}, auto)`
        }
      },
        this.getHeaders(columns),
        this.getRows(columns, items)
      )
    );
  }
}

function getParametersDataType(parameters = null) {
  if (!Array.isArray(parameters) || parameters.length === 0) {
    return null;
  }
  return parameters[0].class;
}

function getTableItems(data = {}, type, headers = null) {
  const INDEX_NAME = "_index";
  const VALUE_NAME = "_value";
  const namedIndexes = {
    [INDEX_NAME]: (
      ["Object", "Array"].includes(type) ?
        l10n.getStr("table.index") : l10n.getStr("table.iterationIndex")
    ),
    [VALUE_NAME]: l10n.getStr("table.value"),
    key: l10n.getStr("table.key")
  };

  let columns = new Map();
  let items = [];

  let addItem = function (item) {
    items.push(item);
    Object.keys(item).forEach(key => addColumn(key));
  };

  let addColumn = function (columnIndex) {
    let columnExists = columns.has(columnIndex);
    let hasMaxColumns = columns.size == TABLE_COLUMN_MAX_ITEMS;
    let hasCustomHeaders = Array.isArray(headers);

    if (
      !columnExists &&
      !hasMaxColumns && (
        !hasCustomHeaders ||
        headers.includes(columnIndex) ||
        columnIndex === INDEX_NAME
      )
    ) {
      columns.set(columnIndex, namedIndexes[columnIndex] || columnIndex);
    }
  };

  for (let index of Object.keys(data)) {
    if (type !== "Object" && index == parseInt(index, 10)) {
      index = parseInt(index, 10);
    }

    let item = {
      [INDEX_NAME]: index
    };

    let property = data[index].value;

    if (property.preview) {
      let {preview} = property;
      let entries = preview.ownProperties || preview.items;
      if (entries) {
        for (let key of Object.keys(entries)) {
          let entry = entries[key];
          item[key] = Object.prototype.hasOwnProperty.call(entry, "value")
            ? entry.value
            : entry;
        }
      } else {
        if (preview.key) {
          item.key = preview.key;
        }

        item[VALUE_NAME] = Object.prototype.hasOwnProperty.call(preview, "value")
          ? preview.value
          : property;
      }
    } else {
      item[VALUE_NAME] = property;
    }

    addItem(item);

    if (items.length === TABLE_ROW_MAX_ITEMS) {
      break;
    }
  }

  // Some headers might not be present in the items, so we make sure to
  // return all the headers set by the user.
  if (Array.isArray(headers)) {
    headers.forEach(header => addColumn(header));
  }

  // We want to always have the index column first
  if (columns.has(INDEX_NAME)) {
    let index = columns.get(INDEX_NAME);
    columns.delete(INDEX_NAME);
    columns = new Map([[INDEX_NAME, index], ...columns.entries()]);
  }

  // We want to always have the values column last
  if (columns.has(VALUE_NAME)) {
    let index = columns.get(VALUE_NAME);
    columns.delete(VALUE_NAME);
    columns.set(VALUE_NAME, index);
  }

  return {
    columns,
    items
  };
}

module.exports = ConsoleTable;
PK
!<"�#[Y#Y#]chrome/devtools/modules/devtools/client/webconsole/new-console-output/components/FilterBar.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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 { connect } = require("devtools/client/shared/vendor/react-redux");
const { getAllFilters } = require("devtools/client/webconsole/new-console-output/selectors/filters");
const { getFilteredMessagesCount } = require("devtools/client/webconsole/new-console-output/selectors/messages");
const { getAllUi } = require("devtools/client/webconsole/new-console-output/selectors/ui");
const actions = require("devtools/client/webconsole/new-console-output/actions/index");
const { l10n } = require("devtools/client/webconsole/new-console-output/utils/messages");
const { PluralForm } = require("devtools/shared/plural-form");
const {
  DEFAULT_FILTERS,
  FILTERS,
} = require("../constants");

const FilterButton = require("devtools/client/webconsole/new-console-output/components/FilterButton");
const FilterCheckbox = require("devtools/client/webconsole/new-console-output/components/FilterCheckbox");

class FilterBar extends Component {
  static get propTypes() {
    return {
      dispatch: PropTypes.func.isRequired,
      filter: PropTypes.object.isRequired,
      serviceContainer: PropTypes.shape({
        attachRefToHud: PropTypes.func.isRequired,
      }).isRequired,
      filterBarVisible: PropTypes.bool.isRequired,
      persistLogs: PropTypes.bool.isRequired,
      hidePersistLogsCheckbox: PropTypes.bool.isRequired,
      filteredMessagesCount: PropTypes.object.isRequired,
    };
  }

  static get defaultProps() {
    return {
      hidePersistLogsCheckbox: false,
    };
  }

  constructor(props) {
    super(props);
    this.onClickMessagesClear = this.onClickMessagesClear.bind(this);
    this.onClickFilterBarToggle = this.onClickFilterBarToggle.bind(this);
    this.onClickRemoveAllFilters = this.onClickRemoveAllFilters.bind(this);
    this.onClickRemoveTextFilter = this.onClickRemoveTextFilter.bind(this);
    this.onSearchInput = this.onSearchInput.bind(this);
    this.onChangePersistToggle = this.onChangePersistToggle.bind(this);
    this.renderFiltersConfigBar = this.renderFiltersConfigBar.bind(this);
    this.renderFilteredMessagesBar = this.renderFilteredMessagesBar.bind(this);
  }

  componentDidMount() {
    this.props.serviceContainer.attachRefToHud(
      "filterBox",
      this.wrapperNode.querySelector(".text-filter")
    );
  }

  shouldComponentUpdate(nextProps, nextState) {
    if (nextProps.filter !== this.props.filter) {
      return true;
    }

    if (nextProps.filterBarVisible !== this.props.filterBarVisible) {
      return true;
    }

    if (nextProps.persistLogs !== this.props.persistLogs) {
      return true;
    }

    if (
      JSON.stringify(nextProps.filteredMessagesCount)
      !== JSON.stringify(this.props.filteredMessagesCount)
    ) {
      return true;
    }

    return false;
  }

  onClickMessagesClear() {
    this.props.dispatch(actions.messagesClear());
  }

  onClickFilterBarToggle() {
    this.props.dispatch(actions.filterBarToggle());
  }

  onClickRemoveAllFilters() {
    this.props.dispatch(actions.defaultFiltersReset());
  }

  onClickRemoveTextFilter() {
    this.props.dispatch(actions.filterTextSet(""));
  }

  onSearchInput(e) {
    this.props.dispatch(actions.filterTextSet(e.target.value));
  }

  onChangePersistToggle() {
    this.props.dispatch(actions.persistToggle());
  }

  renderFiltersConfigBar() {
    const {
      dispatch,
      filter,
      filteredMessagesCount,
    } = this.props;

    const getLabel = (baseLabel, filterKey) => {
      const count = filteredMessagesCount[filterKey];
      if (filter[filterKey] || count === 0) {
        return baseLabel;
      }
      return `${baseLabel} (${count})`;
    };

    return dom.div({
      className: "devtools-toolbar webconsole-filterbar-secondary",
      key: "config-bar",
    },
      FilterButton({
        active: filter[FILTERS.ERROR],
        label: getLabel(
          l10n.getStr("webconsole.errorsFilterButton.label"),
          FILTERS.ERROR
        ),
        filterKey: FILTERS.ERROR,
        dispatch
      }),
      FilterButton({
        active: filter[FILTERS.WARN],
        label: getLabel(
          l10n.getStr("webconsole.warningsFilterButton.label"),
          FILTERS.WARN
        ),
        filterKey: FILTERS.WARN,
        dispatch
      }),
      FilterButton({
        active: filter[FILTERS.LOG],
        label: getLabel(l10n.getStr("webconsole.logsFilterButton.label"), FILTERS.LOG),
        filterKey: FILTERS.LOG,
        dispatch
      }),
      FilterButton({
        active: filter[FILTERS.INFO],
        label: getLabel(l10n.getStr("webconsole.infoFilterButton.label"), FILTERS.INFO),
        filterKey: FILTERS.INFO,
        dispatch
      }),
      FilterButton({
        active: filter[FILTERS.DEBUG],
        label: getLabel(l10n.getStr("webconsole.debugFilterButton.label"), FILTERS.DEBUG),
        filterKey: FILTERS.DEBUG,
        dispatch
      }),
      dom.div({
        className: "devtools-separator",
      }),
      FilterButton({
        active: filter[FILTERS.CSS],
        label: l10n.getStr("webconsole.cssFilterButton.label"),
        filterKey: FILTERS.CSS,
        dispatch
      }),
      FilterButton({
        active: filter[FILTERS.NETXHR],
        label: l10n.getStr("webconsole.xhrFilterButton.label"),
        filterKey: FILTERS.NETXHR,
        dispatch
      }),
      FilterButton({
        active: filter[FILTERS.NET],
        label: l10n.getStr("webconsole.requestsFilterButton.label"),
        filterKey: FILTERS.NET,
        dispatch
      }),
    );
  }

  renderFilteredMessagesBar() {
    const {
      filteredMessagesCount
    } = this.props;
    const {
      global,
    } = filteredMessagesCount;

    let label = l10n.getStr("webconsole.filteredMessages.label");
    label = PluralForm.get(global, label).replace("#1", global);

    // Include all default filters that are hiding messages.
    let title = DEFAULT_FILTERS.reduce((res, filter) => {
      if (filteredMessagesCount[filter] > 0) {
        return res.concat(`${filter}: ${filteredMessagesCount[filter]}`);
      }
      return res;
    }, []).join(", ");

    return dom.div({
      className: "devtools-toolbar webconsole-filterbar-filtered-messages",
      key: "filtered-messages-bar",
    },
      dom.span({
        className: "filter-message-text",
        title,
      }, label),
      dom.button({
        className: "devtools-button reset-filters-button",
        onClick: this.onClickRemoveAllFilters
      }, l10n.getStr("webconsole.resetFiltersButton.label"))
    );
  }

  render() {
    const {
      filter,
      filterBarVisible,
      persistLogs,
      filteredMessagesCount,
      hidePersistLogsCheckbox,
    } = this.props;

    let children = [
      dom.div({
        className: "devtools-toolbar webconsole-filterbar-primary",
        key: "primary-bar",
      },
        dom.button({
          className: "devtools-button devtools-clear-icon",
          title: l10n.getStr("webconsole.clearButton.tooltip"),
          onClick: this.onClickMessagesClear
        }),
        dom.div({
          className: "devtools-separator",
        }),
        dom.button({
          className: "devtools-button devtools-filter-icon" + (
            filterBarVisible ? " checked" : ""),
          title: l10n.getStr("webconsole.toggleFilterButton.tooltip"),
          onClick: this.onClickFilterBarToggle
        }),
        dom.input({
          className: "devtools-plaininput text-filter",
          type: "search",
          value: filter.text,
          placeholder: l10n.getStr("webconsole.filterInput.placeholder"),
          onInput: this.onSearchInput
        }),
        !hidePersistLogsCheckbox && FilterCheckbox({
          label: l10n.getStr("webconsole.enablePersistentLogs.label"),
          title: l10n.getStr("webconsole.enablePersistentLogs.tooltip"),
          onChange: this.onChangePersistToggle,
          checked: persistLogs,
        }),
      )
    ];

    if (filteredMessagesCount.global > 0) {
      children.push(this.renderFilteredMessagesBar());
    }

    if (filterBarVisible) {
      children.push(this.renderFiltersConfigBar());
    }

    return (
      dom.div({
        className: "webconsole-filteringbar-wrapper",
        ref: node => {
          this.wrapperNode = node;
        }
      }, ...children
      )
    );
  }
}

function mapStateToProps(state) {
  let uiState = getAllUi(state);
  return {
    filter: getAllFilters(state),
    filterBarVisible: uiState.filterBarVisible,
    persistLogs: uiState.persistLogs,
    filteredMessagesCount: getFilteredMessagesCount(state),
  };
}

module.exports = connect(mapStateToProps)(FilterBar);
PK
!<sYDVV`chrome/devtools/modules/devtools/client/webconsole/new-console-output/components/FilterButton.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/vendor/react-prop-types");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const actions = require("devtools/client/webconsole/new-console-output/actions/index");

FilterButton.displayName = "FilterButton";

FilterButton.propTypes = {
  label: PropTypes.string.isRequired,
  filterKey: PropTypes.string.isRequired,
  active: PropTypes.bool.isRequired,
  dispatch: PropTypes.func.isRequired,
};

function FilterButton(props) {
  const {active, label, filterKey, dispatch} = props;
  let classList = [
    "devtools-button",
    filterKey,
  ];
  if (active) {
    classList.push("checked");
  }

  return dom.button({
    "aria-pressed": active === true,
    className: classList.join(" "),
    onClick: () => {
      dispatch(actions.filterToggle(filterKey));
    },
  }, label);
}

module.exports = FilterButton;
PK
!<�ORRbchrome/devtools/modules/devtools/client/webconsole/new-console-output/components/FilterCheckbox.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/client/shared/vendor/react-prop-types");
const dom = require("devtools/client/shared/vendor/react-dom-factories");

FilterCheckbox.displayName = "FilterCheckbox";

FilterCheckbox.propTypes = {
  label: PropTypes.string.isRequired,
  title: PropTypes.string,
  checked: PropTypes.bool.isRequired,
  onChange: PropTypes.func.isRequired,
};

function FilterCheckbox(props) {
  const {checked, label, title, onChange} = props;
  return dom.label({ title, className: "filter-checkbox" }, dom.input({
    type: "checkbox",
    checked,
    onChange,
  }), label);
}

module.exports = FilterCheckbox;
PK
!<J�В�
�
cchrome/devtools/modules/devtools/client/webconsole/new-console-output/components/GripMessageBody.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

// React
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const {
  MESSAGE_TYPE,
  JSTERM_COMMANDS,
} = require("../constants");
const { getObjectInspector } = require("devtools/client/webconsole/new-console-output/utils/object-inspector");

const reps = require("devtools/client/shared/components/reps/reps");
const { MODE } = reps;

GripMessageBody.displayName = "GripMessageBody";

GripMessageBody.propTypes = {
  grip: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.number,
    PropTypes.object,
  ]).isRequired,
  serviceContainer: PropTypes.shape({
    createElement: PropTypes.func.isRequired,
    hudProxy: PropTypes.object.isRequired,
    onViewSourceInDebugger: PropTypes.func.isRequired,
  }),
  userProvidedStyle: PropTypes.string,
  useQuotes: PropTypes.bool,
  escapeWhitespace: PropTypes.bool,
  type: PropTypes.string,
  helperType: PropTypes.string,
};

GripMessageBody.defaultProps = {
  mode: MODE.LONG,
};

function GripMessageBody(props) {
  const {
    grip,
    userProvidedStyle,
    serviceContainer,
    useQuotes,
    escapeWhitespace,
    mode = MODE.LONG,
  } = props;

  let styleObject;
  if (userProvidedStyle && userProvidedStyle !== "") {
    styleObject = cleanupStyle(userProvidedStyle, serviceContainer.createElement);
  }

  let objectInspectorProps = {
    autoExpandDepth: shouldAutoExpandObjectInspector(props) ? 1 : 0,
    mode,
  };

  if (typeof grip === "string" || (grip && grip.type === "longString")) {
    Object.assign(objectInspectorProps, {
      useQuotes,
      escapeWhitespace,
      style: styleObject
    });
  }

  return getObjectInspector(grip, serviceContainer, objectInspectorProps);
}

// Regular expression that matches the allowed CSS property names.
const allowedStylesRegex = new RegExp(
  "^(?:-moz-)?(?:background|border|box|clear|color|cursor|display|float|font|line|" +
  "margin|padding|text|transition|outline|white-space|word|writing|" +
  "(?:min-|max-)?width|(?:min-|max-)?height)"
);

// Regular expression that matches the forbidden CSS property values.
const forbiddenValuesRegexs = [
  // url(), -moz-element()
  /\b(?:url|(?:-moz-)?element)[\s('"]+/gi,

  // various URL protocols
  /['"(]*(?:chrome|resource|about|app|data|https?|ftp|file):+\/*/gi,
];

function cleanupStyle(userProvidedStyle, createElement) {
  // Use a dummy element to parse the style string.
  let 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 => {
      return allowedStylesRegex.test(name)
        && !forbiddenValuesRegexs.some(regex => regex.test(dummy.style[name]));
    })
    .reduce((object, name) => {
      return Object.assign({
        [name]: dummy.style[name]
      }, object);
    }, {});
}

function shouldAutoExpandObjectInspector(props) {
  const {
    helperType,
    type,
  } = props;

  return (
    type === MESSAGE_TYPE.DIR
    || helperType === JSTERM_COMMANDS.INSPECT
  );
}

module.exports = GripMessageBody;
PK
!<��\��#�#[chrome/devtools/modules/devtools/client/webconsole/new-console-output/components/Message.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

// React & Redux
const { Component, createFactory } = 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");
const { l10n } =
  require("devtools/client/webconsole/new-console-output/utils/messages");
const actions =
  require("devtools/client/webconsole/new-console-output/actions/index");
const { MESSAGE_SOURCE, MESSAGE_TYPE } =
  require("devtools/client/webconsole/new-console-output/constants");
const CollapseButton =
  require("devtools/client/webconsole/new-console-output/components/CollapseButton");
const { MessageIndent } =
  require("devtools/client/webconsole/new-console-output/components/MessageIndent");
const MessageIcon =
  require("devtools/client/webconsole/new-console-output/components/MessageIcon");
const MessageRepeat =
  require("devtools/client/webconsole/new-console-output/components/MessageRepeat");
const FrameView = createFactory(require("devtools/client/shared/components/Frame"));
const StackTrace = createFactory(require("devtools/client/shared/components/StackTrace"));

class Message extends Component {
  static get propTypes() {
    return {
      open: PropTypes.bool,
      collapsible: PropTypes.bool,
      collapseTitle: PropTypes.string,
      source: PropTypes.string.isRequired,
      type: PropTypes.string.isRequired,
      level: PropTypes.string.isRequired,
      indent: PropTypes.number.isRequired,
      topLevelClasses: PropTypes.array.isRequired,
      messageBody: PropTypes.any.isRequired,
      repeat: PropTypes.any,
      frame: PropTypes.any,
      attachment: PropTypes.any,
      stacktrace: PropTypes.any,
      messageId: PropTypes.string,
      scrollToMessage: PropTypes.bool,
      exceptionDocURL: PropTypes.string,
      request: PropTypes.object,
      dispatch: PropTypes.func,
      timeStamp: PropTypes.number,
      timestampsVisible: PropTypes.bool.isRequired,
      serviceContainer: PropTypes.shape({
        emitNewMessage: PropTypes.func.isRequired,
        onViewSourceInDebugger: PropTypes.func,
        onViewSourceInScratchpad: PropTypes.func,
        onViewSourceInStyleEditor: PropTypes.func,
        openContextMenu: PropTypes.func.isRequired,
        openLink: PropTypes.func.isRequired,
        sourceMapService: PropTypes.any,
      }),
      notes: PropTypes.arrayOf(PropTypes.shape({
        messageBody: PropTypes.string.isRequired,
        frame: PropTypes.any,
      })),
    };
  }

  static get defaultProps() {
    return {
      indent: 0
    };
  }

  constructor(props) {
    super(props);
    this.onLearnMoreClick = this.onLearnMoreClick.bind(this);
    this.toggleMessage = this.toggleMessage.bind(this);
    this.onContextMenu = this.onContextMenu.bind(this);
  }

  componentDidMount() {
    if (this.messageNode) {
      if (this.props.scrollToMessage) {
        this.messageNode.scrollIntoView();
      }
      // Event used in tests. Some message types don't pass it in because existing tests
      // did not emit for them.
      if (this.props.serviceContainer) {
        this.props.serviceContainer.emitNewMessage(
          this.messageNode, this.props.messageId, this.props.timeStamp);
      }
    }
  }

  onLearnMoreClick(e) {
    let {exceptionDocURL} = this.props;
    this.props.serviceContainer.openLink(exceptionDocURL, e);
  }

  toggleMessage(e) {
    let { open, dispatch, messageId } = this.props;
    if (open) {
      dispatch(actions.messageClose(messageId));
    } else {
      dispatch(actions.messageOpen(messageId));
    }
  }

  onContextMenu(e) {
    let { serviceContainer, source, request, messageId } = this.props;
    let messageInfo = {
      source,
      request,
      messageId,
    };
    serviceContainer.openContextMenu(e, messageInfo);
    e.stopPropagation();
    e.preventDefault();
  }

  render() {
    const {
      open,
      collapsible,
      collapseTitle,
      source,
      type,
      level,
      indent,
      topLevelClasses,
      messageBody,
      frame,
      stacktrace,
      serviceContainer,
      exceptionDocURL,
      timeStamp = Date.now(),
      timestampsVisible,
      notes,
    } = this.props;

    topLevelClasses.push("message", source, type, level);
    if (open) {
      topLevelClasses.push("open");
    }

    let timestampEl;
    if (timestampsVisible === true) {
      timestampEl = dom.span({
        className: "timestamp devtools-monospace"
      }, l10n.timestampString(timeStamp));
    }

    const icon = MessageIcon({level});

    // Figure out if there is an expandable part to the message.
    let attachment = null;
    if (this.props.attachment) {
      attachment = this.props.attachment;
    } else if (stacktrace && open) {
      attachment = dom.div(
        {
          className: "stacktrace devtools-monospace"
        },
        StackTrace({
          stacktrace: stacktrace,
          onViewSourceInDebugger: serviceContainer.onViewSourceInDebugger
            || serviceContainer.onViewSource,
          onViewSourceInScratchpad: serviceContainer.onViewSourceInScratchpad
            || serviceContainer.onViewSource,
          sourceMapService: serviceContainer.sourceMapService,
        })
      );
    }

    // If there is an expandable part, make it collapsible.
    let collapse = null;
    if (collapsible) {
      collapse = CollapseButton({
        open,
        title: collapseTitle,
        onClick: this.toggleMessage
      });
    }

    let notesNodes;
    if (notes) {
      notesNodes = notes.map(note => dom.span(
        { className: "message-flex-body error-note" },
        dom.span({ className: "message-body devtools-monospace" },
          "note: " + note.messageBody
        ),
        dom.span({ className: "message-location devtools-monospace" },
          note.frame ? FrameView({
            frame: note.frame,
            onClick: serviceContainer
              ? serviceContainer.onViewSourceInDebugger || serviceContainer.onViewSource
              : undefined,
            showEmptyPathAsHost: true,
            sourceMapService: serviceContainer
              ? serviceContainer.sourceMapService
              : undefined
          }) : null
        )));
    } else {
      notesNodes = [];
    }

    const repeat = this.props.repeat && this.props.repeat > 1 ?
      MessageRepeat({repeat: this.props.repeat}) : null;

    let onFrameClick;
    if (serviceContainer && frame) {
      if (source === MESSAGE_SOURCE.CSS) {
        onFrameClick = serviceContainer.onViewSourceInStyleEditor
          || serviceContainer.onViewSource;
      } else if (/^Scratchpad\/\d+$/.test(frame.source)) {
        onFrameClick = serviceContainer.onViewSourceInScratchpad
          || serviceContainer.onViewSource;
      } else {
        // Point everything else to debugger, if source not available,
        // it will fall back to view-source.
        onFrameClick = serviceContainer.onViewSourceInDebugger
          || serviceContainer.onViewSource;
      }
    }

    // Configure the location.
    const location = dom.span({ className: "message-location devtools-monospace" },
      frame ? FrameView({
        frame,
        onClick: onFrameClick,
        showEmptyPathAsHost: true,
        sourceMapService: serviceContainer ? serviceContainer.sourceMapService : undefined
      }) : null
    );

    let learnMore;
    if (exceptionDocURL) {
      learnMore = dom.a({
        className: "learn-more-link webconsole-learn-more-link",
        title: exceptionDocURL.split("?")[0],
        onClick: this.onLearnMoreClick,
      }, `[${l10n.getStr("webConsoleMoreInfoLabel")}]`);
    }

    const bodyElements = Array.isArray(messageBody) ? messageBody : [messageBody];

    return dom.div({
      className: topLevelClasses.join(" "),
      onContextMenu: this.onContextMenu,
      ref: node => {
        this.messageNode = node;
      },
      "aria-live": type === MESSAGE_TYPE.COMMAND ? "off" : "polite"
    },
      timestampEl,
      MessageIndent({indent}),
      icon,
      collapse,
      dom.span({ className: "message-body-wrapper" },
        dom.span({
          className: "message-flex-body",
          onClick: collapsible ? this.toggleMessage : undefined,
        },
          // Add whitespaces for formatting when copying to the clipboard.
          timestampEl ? " " : null,
          dom.span({ className: "message-body devtools-monospace" },
            ...bodyElements,
            learnMore
          ),
          repeat ? " " : null,
          repeat,
          " ", location
        ),
        // Add a newline for formatting when copying to the clipboard.
        "\n",
        // If an attachment is displayed, the final newline is handled by the attachment.
        attachment,
        ...notesNodes
      )
    );
  }
}

module.exports = Message;
PK
!<%E�A�
�
dchrome/devtools/modules/devtools/client/webconsole/new-console-output/components/MessageContainer.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

// React & Redux
const { Component } = require("devtools/client/shared/vendor/react");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");

const {
  MESSAGE_SOURCE,
  MESSAGE_TYPE
} = require("devtools/client/webconsole/new-console-output/constants");

const componentMap = new Map([
  ["ConsoleApiCall", require("./message-types/ConsoleApiCall")],
  ["ConsoleCommand", require("./message-types/ConsoleCommand")],
  ["DefaultRenderer", require("./message-types/DefaultRenderer")],
  ["EvaluationResult", require("./message-types/EvaluationResult")],
  ["NetworkEventMessage", require("./message-types/NetworkEventMessage")],
  ["PageError", require("./message-types/PageError")]
]);

class MessageContainer extends Component {
  static get propTypes() {
    return {
      messageId: PropTypes.string.isRequired,
      open: PropTypes.bool.isRequired,
      serviceContainer: PropTypes.object.isRequired,
      tableData: PropTypes.object,
      timestampsVisible: PropTypes.bool.isRequired,
      repeat: PropTypes.number,
      networkMessageUpdate: PropTypes.object,
      getMessage: PropTypes.func.isRequired,
    };
  }

  static get defaultProps() {
    return {
      open: false,
    };
  }

  shouldComponentUpdate(nextProps, nextState) {
    const repeatChanged = this.props.repeat !== nextProps.repeat;
    const openChanged = this.props.open !== nextProps.open;
    const tableDataChanged = this.props.tableData !== nextProps.tableData;
    const timestampVisibleChanged =
      this.props.timestampsVisible !== nextProps.timestampsVisible;
    const networkMessageUpdateChanged =
      this.props.networkMessageUpdate !== nextProps.networkMessageUpdate;

    return repeatChanged
      || openChanged
      || tableDataChanged
      || timestampVisibleChanged
      || networkMessageUpdateChanged;
  }

  render() {
    const message = this.props.getMessage();

    let MessageComponent = getMessageComponent(message);
    return MessageComponent(Object.assign({message}, this.props));
  }
}

function getMessageComponent(message) {
  if (!message) {
    return componentMap.get("DefaultRenderer");
  }

  switch (message.source) {
    case MESSAGE_SOURCE.CONSOLE_API:
      return componentMap.get("ConsoleApiCall");
    case MESSAGE_SOURCE.NETWORK:
      return componentMap.get("NetworkEventMessage");
    case MESSAGE_SOURCE.CSS:
    case MESSAGE_SOURCE.JAVASCRIPT:
      switch (message.type) {
        case MESSAGE_TYPE.COMMAND:
          return componentMap.get("ConsoleCommand");
        case MESSAGE_TYPE.RESULT:
          return componentMap.get("EvaluationResult");
        // @TODO this is probably not the right behavior, but works for now.
        // Chrome doesn't distinguish between page errors and log messages. We
        // may want to remove the PageError component and just handle errors
        // with ConsoleApiCall.
        case MESSAGE_TYPE.LOG:
          return componentMap.get("PageError");
        default:
          return componentMap.get("DefaultRenderer");
      }
  }

  return componentMap.get("DefaultRenderer");
}

module.exports.MessageContainer = MessageContainer;

// Exported so we can test it with unit tests.
module.exports.getMessageComponent = getMessageComponent;
PK
!<���V_chrome/devtools/modules/devtools/client/webconsole/new-console-output/components/MessageIcon.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const {l10n} = require("devtools/client/webconsole/new-console-output/utils/messages");

// Store common icons so they can be used without recreating the element
// during render.
const CONSTANT_ICONS = {
  "error": getIconElement("level.error"),
  "warn": getIconElement("level.warn"),
  "info": getIconElement("level.info"),
  "log": getIconElement("level.log"),
  "debug": getIconElement("level.debug"),
};

function getIconElement(level) {
  return dom.span({
    className: "icon",
    title: l10n.getStr(level),
    "aria-live": "off",
  });
}

MessageIcon.displayName = "MessageIcon";
MessageIcon.propTypes = {
  level: PropTypes.string.isRequired,
};
function MessageIcon(props) {
  const { level } = props;
  return CONSTANT_ICONS[level] || getIconElement(level);
}

module.exports = MessageIcon;
PK
!<+z�achrome/devtools/modules/devtools/client/webconsole/new-console-output/components/MessageIndent.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 dom = require("devtools/client/shared/vendor/react-dom-factories");

const INDENT_WIDTH = 12;

// Store common indents so they can be used without recreating the element
// during render.
const CONSTANT_INDENTS = [getIndentElement(0), getIndentElement(1)];

function getIndentElement(indent) {
  return dom.span({
    "data-indent": indent,
    className: "indent",
    style: {
      "width": indent * INDENT_WIDTH
    }
  });
}

function MessageIndent(props) {
  const { indent } = props;
  return CONSTANT_INDENTS[indent] || getIndentElement(indent);
}

module.exports.MessageIndent = MessageIndent;

// Exported so we can test it with unit tests.
module.exports.INDENT_WIDTH = INDENT_WIDTH;
PK
!<M�$$achrome/devtools/modules/devtools/client/webconsole/new-console-output/components/MessageRepeat.js
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const { PluralForm } = require("devtools/shared/plural-form");
const { l10n } = require("devtools/client/webconsole/new-console-output/utils/messages");
const messageRepeatsTooltip = l10n.getStr("messageRepeats.tooltip2");

MessageRepeat.displayName = "MessageRepeat";

MessageRepeat.propTypes = {
  repeat: PropTypes.number.isRequired
};

function MessageRepeat(props) {
  const { repeat } = props;
  return dom.span({
    className: "message-repeats",
    title: PluralForm.get(repeat, messageRepeatsTooltip).replace("#1", repeat)
  }, repeat);
}

module.exports = MessageRepeat;
PK
!<)���	�	[chrome/devtools/modules/devtools/client/webconsole/new-console-output/components/SideBar.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("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");
const { connect } = require("devtools/client/shared/vendor/react-redux");
const { getObjectInspector } = require("devtools/client/webconsole/new-console-output/utils/object-inspector");
const actions = require("devtools/client/webconsole/new-console-output/actions/index");
const SplitBox = createFactory(require("devtools/client/shared/components/splitter/SplitBox"));

const reps = require("devtools/client/shared/components/reps/reps");
const { MODE } = reps;

class SideBar extends Component {
  static get propTypes() {
    return {
      serviceContainer: PropTypes.object,
      dispatch: PropTypes.func.isRequired,
      sidebarVisible: PropTypes.bool,
      grip: PropTypes.object,
    };
  }

  constructor(props) {
    super(props);
    this.onClickSidebarClose = this.onClickSidebarClose.bind(this);
  }

  onClickSidebarClose() {
    this.props.dispatch(actions.sidebarClose());
  }

  render() {
    let {
      sidebarVisible,
      grip,
      serviceContainer,
    } = this.props;

    let objectInspector = getObjectInspector(grip, serviceContainer, {
      autoExpandDepth: 1,
      mode: MODE.SHORT,
    });

    let endPanel = dom.aside({
      className: "sidebar-wrapper"
    },
      dom.header({
        className: "devtools-toolbar webconsole-sidebar-toolbar"
      },
        dom.button({
          className: "devtools-button sidebar-close-button",
          onClick: this.onClickSidebarClose
        })
      ),
      dom.aside({
        className: "sidebar-contents"
      }, objectInspector)
    );

    return (
      sidebarVisible ?
        SplitBox({
          className: "sidebar",
          endPanel,
          endPanelControl: true,
          initialSize: "200px",
          minSize: "100px",
          vert: true,
        })
        : null
    );
  }
}

function mapStateToProps(state, props) {
  return {
    sidebarVisible: state.ui.sidebarVisible,
    grip: state.ui.gripInSidebar,
  };
}

module.exports = connect(mapStateToProps)(SideBar);
PK
!<��Wo��pchrome/devtools/modules/devtools/client/webconsole/new-console-output/components/message-types/ConsoleApiCall.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

// React & Redux
const { createFactory } = 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 GripMessageBody = require("devtools/client/webconsole/new-console-output/components/GripMessageBody");
const ConsoleTable = createFactory(require("devtools/client/webconsole/new-console-output/components/ConsoleTable"));
const {isGroupType, l10n} = require("devtools/client/webconsole/new-console-output/utils/messages");

const Message = createFactory(require("devtools/client/webconsole/new-console-output/components/Message"));

ConsoleApiCall.displayName = "ConsoleApiCall";

ConsoleApiCall.propTypes = {
  dispatch: PropTypes.func.isRequired,
  message: PropTypes.object.isRequired,
  open: PropTypes.bool,
  serviceContainer: PropTypes.object.isRequired,
  timestampsVisible: PropTypes.bool.isRequired,
};

ConsoleApiCall.defaultProps = {
  open: false,
};

function ConsoleApiCall(props) {
  const {
    dispatch,
    message,
    open,
    tableData,
    serviceContainer,
    timestampsVisible,
    repeat,
  } = props;
  const {
    id: messageId,
    indent,
    source,
    type,
    level,
    stacktrace,
    frame,
    timeStamp,
    parameters,
    messageText,
    prefix,
    userProvidedStyles,
  } = message;

  let messageBody;
  const messageBodyConfig = {
    dispatch,
    messageId,
    parameters,
    userProvidedStyles,
    serviceContainer,
    type,
  };

  if (type === "trace") {
    messageBody = dom.span({className: "cm-variable"}, "console.trace()");
  } else if (type === "assert") {
    let reps = formatReps(messageBodyConfig);
    messageBody = dom.span({ className: "cm-variable" }, "Assertion failed: ", reps);
  } else if (type === "table") {
    // TODO: Chrome does not output anything, see if we want to keep this
    messageBody = dom.span({className: "cm-variable"}, "console.table()");
  } else if (parameters) {
    messageBody = formatReps(messageBodyConfig);
    if (prefix) {
      messageBody.unshift(dom.span({
        className: "console-message-prefix"
      }, `${prefix}: `));
    }
  } else {
    messageBody = messageText;
  }

  let attachment = null;
  if (type === "table") {
    attachment = ConsoleTable({
      dispatch,
      id: message.id,
      serviceContainer,
      parameters: message.parameters,
      tableData,
    });
  }

  let collapseTitle = null;
  if (isGroupType(type)) {
    collapseTitle = l10n.getStr("groupToggle");
  }

  const collapsible = isGroupType(type)
    || (type === "error" && Array.isArray(stacktrace));
  const topLevelClasses = ["cm-s-mozilla"];

  return Message({
    messageId,
    open,
    collapsible,
    collapseTitle,
    source,
    type,
    level,
    topLevelClasses,
    messageBody,
    repeat,
    frame,
    stacktrace,
    attachment,
    serviceContainer,
    dispatch,
    indent,
    timeStamp,
    timestampsVisible,
    parameters,
  });
}

function formatReps(options = {}) {
  const {
    dispatch,
    loadedObjectProperties,
    loadedObjectEntries,
    messageId,
    parameters,
    serviceContainer,
    userProvidedStyles,
    type,
  } = options;

  return (
    parameters
      // Get all the grips.
      .map((grip, key) => GripMessageBody({
        dispatch,
        messageId,
        grip,
        key,
        userProvidedStyle: userProvidedStyles ? userProvidedStyles[key] : null,
        serviceContainer,
        useQuotes: false,
        loadedObjectProperties,
        loadedObjectEntries,
        type,
      }))
      // Interleave spaces.
      .reduce((arr, v, i) => {
        // We need to interleave a space if we are not on the last element AND
        // if we are not between 2 messages with user provided style.
        const needSpace = i + 1 < parameters.length && (
          !userProvidedStyles
          || userProvidedStyles[i] === undefined
          || userProvidedStyles[i + 1] === undefined
        );

        return needSpace
          ? arr.concat(v, " ")
          : arr.concat(v);
      }, [])
  );
}

module.exports = ConsoleApiCall;

PK
!<*���pchrome/devtools/modules/devtools/client/webconsole/new-console-output/components/message-types/ConsoleCommand.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

// React & Redux
const { createFactory } = require("devtools/client/shared/vendor/react");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const Message = createFactory(require("devtools/client/webconsole/new-console-output/components/Message"));

ConsoleCommand.displayName = "ConsoleCommand";

ConsoleCommand.propTypes = {
  message: PropTypes.object.isRequired,
  timestampsVisible: PropTypes.bool.isRequired,
  serviceContainer: PropTypes.object,
};

/**
 * Displays input from the console.
 */
function ConsoleCommand(props) {
  const {
    message,
    timestampsVisible,
    serviceContainer,
  } = props;

  const {
    indent,
    source,
    type,
    level,
    messageText: messageBody,
  } = message;

  return Message({
    source,
    type,
    level,
    topLevelClasses: [],
    messageBody,
    serviceContainer,
    indent,
    timestampsVisible,
  });
}

module.exports = ConsoleCommand;
PK
!<\��vRRqchrome/devtools/modules/devtools/client/webconsole/new-console-output/components/message-types/DefaultRenderer.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 dom = require("devtools/client/shared/vendor/react-dom-factories");

DefaultRenderer.displayName = "DefaultRenderer";

function DefaultRenderer(props) {
  return dom.div({},
    "This message type is not supported yet."
  );
}

module.exports = DefaultRenderer;
PK
!<�X����rchrome/devtools/modules/devtools/client/webconsole/new-console-output/components/message-types/EvaluationResult.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

// React & Redux
const { createFactory } = require("devtools/client/shared/vendor/react");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const Message = createFactory(require("devtools/client/webconsole/new-console-output/components/Message"));
const GripMessageBody = require("devtools/client/webconsole/new-console-output/components/GripMessageBody");

EvaluationResult.displayName = "EvaluationResult";

EvaluationResult.propTypes = {
  dispatch: PropTypes.func.isRequired,
  message: PropTypes.object.isRequired,
  timestampsVisible: PropTypes.bool.isRequired,
  serviceContainer: PropTypes.object,
};

function EvaluationResult(props) {
  const {
    dispatch,
    message,
    serviceContainer,
    timestampsVisible,
  } = props;

  const {
    source,
    type,
    helperType,
    level,
    id: messageId,
    indent,
    exceptionDocURL,
    frame,
    timeStamp,
    parameters,
    notes,
  } = message;

  let messageBody;
  if (typeof message.messageText !== "undefined" && message.messageText !== null) {
    if (typeof message.messageText === "string") {
      messageBody = message.messageText;
    } else if (
      typeof message.messageText === "object"
      && message.messageText.type === "longString"
    ) {
      messageBody = `${message.messageText.initial}…`;
    }
  } else {
    messageBody = GripMessageBody({
      dispatch,
      messageId,
      grip: parameters[0],
      serviceContainer,
      useQuotes: true,
      escapeWhitespace: false,
      type,
      helperType,
    });
  }

  const topLevelClasses = ["cm-s-mozilla"];

  return Message({
    source,
    type,
    level,
    indent,
    topLevelClasses,
    messageBody,
    messageId,
    serviceContainer,
    exceptionDocURL,
    frame,
    timeStamp,
    parameters,
    notes,
    timestampsVisible,
  });
}

module.exports = EvaluationResult;
PK
!<�)�{{uchrome/devtools/modules/devtools/client/webconsole/new-console-output/components/message-types/NetworkEventMessage.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

// React & Redux
const { createFactory } = 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 Message = createFactory(require("devtools/client/webconsole/new-console-output/components/Message"));
const actions = require("devtools/client/webconsole/new-console-output/actions/index");
const { l10n } = require("devtools/client/webconsole/new-console-output/utils/messages");
const TabboxPanel = createFactory(require("devtools/client/netmonitor/src/components/TabboxPanel"));
const { getHTTPStatusCodeURL } = require("devtools/client/netmonitor/src/utils/mdn-utils");
const LEARN_MORE = l10n.getStr("webConsoleMoreInfoLabel");

NetworkEventMessage.displayName = "NetworkEventMessage";

NetworkEventMessage.propTypes = {
  message: PropTypes.object.isRequired,
  serviceContainer: PropTypes.shape({
    openNetworkPanel: PropTypes.func.isRequired,
  }),
  timestampsVisible: PropTypes.bool.isRequired,
  networkMessageUpdate: PropTypes.object.isRequired,
};

/**
 * This component is responsible for rendering network messages
 * in the Console panel.
 *
 * Network logs are expandable and the user can inspect it inline
 * within the Console panel (no need to switch to the Network panel).
 *
 * HTTP details are rendered using `TabboxPanel` component used to
 * render contents of the side bar in the Network panel.
 *
 * All HTTP details data are fetched from the backend on-demand
 * when the user is expanding network log for the first time.
 */
function NetworkEventMessage({
  message = {},
  serviceContainer,
  timestampsVisible,
  networkMessageUpdate = {},
  networkMessageActiveTabId,
  dispatch,
  open,
}) {
  const {
    id,
    indent,
    source,
    type,
    level,
    request,
    isXHR,
    timeStamp,
  } = message;

  const {
    response = {},
    totalTime,
  } = networkMessageUpdate;

  const {
    httpVersion,
    status,
    statusText,
  } = response;

  const topLevelClasses = [ "cm-s-mozilla" ];
  let statusCode, statusInfo;

  if (httpVersion && status && statusText !== undefined && totalTime !== undefined) {
    let statusCodeDocURL = getHTTPStatusCodeURL(status.toString(), "webconsole");
    statusCode = dom.span({
      className: "status-code",
      "data-code": status,
      title: LEARN_MORE,
      onClick: (e) => {
        e.stopPropagation();
        e.preventDefault();
        serviceContainer.openLink(statusCodeDocURL, e);
      }
    }, status);
    statusInfo = dom.span(
      {className: "status-info"},
      `[${httpVersion} `, statusCode, ` ${statusText} ${totalTime}ms]`
    );
  }

  const toggle = () => {
    if (open) {
      dispatch(actions.messageClose(id));
    } else {
      dispatch(actions.messageOpen(id));
    }
  };

  // Message body components.
  const method = dom.span({className: "method" }, request.method);
  const xhr = isXHR
    ? dom.span({ className: "xhr" }, l10n.getStr("webConsoleXhrIndicator"))
    : null;
  const requestUrl = dom.a({ className: "url", title: request.url, onClick: toggle },
    request.url);
  const statusBody = statusInfo
    ? dom.a({ className: "status", onClick: toggle }, statusInfo)
    : null;

  const messageBody = [xhr, method, requestUrl, statusBody];

  // API consumed by Net monitor UI components. Most of the method
  // are not needed in context of the Console panel (atm) and thus
  // let's just provide empty implementation.
  // Individual methods might be implemented step by step as needed.
  let connector = {
    viewSourceInDebugger: (url, line) => {
      serviceContainer.onViewSourceInDebugger({url, line});
    },
    getLongString: (grip) => {
      return serviceContainer.getLongString(grip);
    },
    getTabTarget: () => {},
    getNetworkRequest: () => {},
    sendHTTPRequest: () => {},
    setPreferences: () => {},
    triggerActivity: () => {},
    requestData: (requestId, dataType) => {
      return serviceContainer.requestData(requestId, dataType);
    },
  };

  // Only render the attachment if the network-event is
  // actually opened (performance optimization).
  const attachment = open && dom.div({
    className: "network-info network-monitor devtools-monospace"},
    TabboxPanel({
      connector,
      activeTabId: networkMessageActiveTabId,
      request: networkMessageUpdate,
      sourceMapService: serviceContainer.sourceMapService,
      openLink: serviceContainer.openLink,
      selectTab: (tabId) => {
        dispatch(actions.selectNetworkMessageTab(tabId));
      },
    })
  );

  return Message({
    dispatch,
    messageId: id,
    source,
    type,
    level,
    indent,
    collapsible: true,
    open,
    attachment,
    topLevelClasses,
    timeStamp,
    messageBody,
    serviceContainer,
    request,
    timestampsVisible,
  });
}

module.exports = NetworkEventMessage;
PK
!<�z	��kchrome/devtools/modules/devtools/client/webconsole/new-console-output/components/message-types/PageError.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

// React & Redux
const { createFactory } = require("devtools/client/shared/vendor/react");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const Message = createFactory(require("devtools/client/webconsole/new-console-output/components/Message"));

PageError.displayName = "PageError";

PageError.propTypes = {
  message: PropTypes.object.isRequired,
  open: PropTypes.bool,
  timestampsVisible: PropTypes.bool.isRequired,
  serviceContainer: PropTypes.object,
};

PageError.defaultProps = {
  open: false,
};

function PageError(props) {
  const {
    dispatch,
    message,
    open,
    repeat,
    serviceContainer,
    timestampsVisible,
  } = props;
  const {
    id: messageId,
    indent,
    source,
    type,
    level,
    messageText,
    stacktrace,
    frame,
    exceptionDocURL,
    timeStamp,
    notes,
  } = message;

  let messageBody;
  if (typeof messageText === "string") {
    messageBody = messageText;
  } else if (typeof messageText === "object" && messageText.type === "longString") {
    messageBody = `${message.messageText.initial}…`;
  }

  return Message({
    dispatch,
    messageId,
    open,
    collapsible: Array.isArray(stacktrace),
    source,
    type,
    level,
    topLevelClasses: [],
    indent,
    messageBody,
    repeat,
    frame,
    stacktrace,
    serviceContainer,
    exceptionDocURL,
    timeStamp,
    notes,
    timestampsVisible,
  });
}

module.exports = PageError;
PK
!<k D��Rchrome/devtools/modules/devtools/client/webconsole/new-console-output/constants.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 actionTypes = {
  BATCH_ACTIONS: "BATCH_ACTIONS",
  DEFAULT_FILTERS_RESET: "DEFAULT_FILTERS_RESET",
  FILTER_BAR_TOGGLE: "FILTER_BAR_TOGGLE",
  FILTER_TEXT_SET: "FILTER_TEXT_SET",
  FILTER_TOGGLE: "FILTER_TOGGLE",
  FILTERS_CLEAR: "FILTERS_CLEAR",
  INITIALIZE: "INITIALIZE",
  MESSAGE_CLOSE: "MESSAGE_CLOSE",
  MESSAGE_OPEN: "MESSAGE_OPEN",
  MESSAGE_TABLE_RECEIVE: "MESSAGE_TABLE_RECEIVE",
  MESSAGES_ADD: "MESSAGES_ADD",
  MESSAGES_CLEAR: "MESSAGES_CLEAR",
  NETWORK_MESSAGE_UPDATE: "NETWORK_MESSAGE_UPDATE",
  NETWORK_UPDATE_REQUEST: "NETWORK_UPDATE_REQUEST",
  PERSIST_TOGGLE: "PERSIST_TOGGLE",
  PRIVATE_MESSAGES_CLEAR: "PRIVATE_MESSAGES_CLEAR",
  REMOVED_ACTORS_CLEAR: "REMOVED_ACTORS_CLEAR",
  SELECT_NETWORK_MESSAGE_TAB: "SELECT_NETWORK_MESSAGE_TAB",
  SIDEBAR_CLOSE: "SIDEBAR_CLOSE",
  SHOW_OBJECT_IN_SIDEBAR: "SHOW_OBJECT_IN_SIDEBAR",
  TIMESTAMPS_TOGGLE: "TIMESTAMPS_TOGGLE",
};

const prefs = {
  PREFS: {
    // Filter preferences only have the suffix since they can be used either for the
    // webconsole or the browser console.
    FILTER: {
      ERROR: "filter.error",
      WARN: "filter.warn",
      INFO: "filter.info",
      LOG: "filter.log",
      DEBUG: "filter.debug",
      CSS: "filter.css",
      NET: "filter.net",
      NETXHR: "filter.netxhr",
    },
    UI: {
      // Filter bar UI preference only have the suffix since it can be used either for
      // the webconsole or the browser console.
      FILTER_BAR: "ui.filterbar",
      // Persist is only used by the webconsole.
      PERSIST: "devtools.webconsole.persistlog",
      // We use the same pref to enable the sidebar on webconsole and browser console.
      SIDEBAR_TOGGLE: "devtools.webconsole.sidebarToggle",
    }
  }
};

const FILTERS = {
  CSS: "css",
  DEBUG: "debug",
  ERROR: "error",
  INFO: "info",
  LOG: "log",
  NET: "net",
  NETXHR: "netxhr",
  TEXT: "text",
  WARN: "warn",
};

const DEFAULT_FILTERS_VALUES = {
  [FILTERS.TEXT]: "",
  [FILTERS.ERROR]: true,
  [FILTERS.WARN]: true,
  [FILTERS.LOG]: true,
  [FILTERS.INFO]: true,
  [FILTERS.DEBUG]: true,
  [FILTERS.CSS]: false,
  [FILTERS.NET]: false,
  [FILTERS.NETXHR]: false,
};

const DEFAULT_FILTERS = Object.keys(DEFAULT_FILTERS_VALUES)
  .filter(filter => DEFAULT_FILTERS_VALUES[filter] !== false);

const chromeRDPEnums = {
  MESSAGE_SOURCE: {
    XML: "xml",
    CSS: "css",
    JAVASCRIPT: "javascript",
    NETWORK: "network",
    CONSOLE_API: "console-api",
    STORAGE: "storage",
    APPCACHE: "appcache",
    RENDERING: "rendering",
    SECURITY: "security",
    OTHER: "other",
    DEPRECATION: "deprecation"
  },
  MESSAGE_TYPE: {
    LOG: "log",
    DIR: "dir",
    TABLE: "table",
    TRACE: "trace",
    CLEAR: "clear",
    START_GROUP: "startGroup",
    START_GROUP_COLLAPSED: "startGroupCollapsed",
    END_GROUP: "endGroup",
    ASSERT: "assert",
    DEBUG: "debug",
    PROFILE: "profile",
    PROFILE_END: "profileEnd",
    // Undocumented in Chrome RDP, but is used for evaluation results.
    RESULT: "result",
    // Undocumented in Chrome RDP, but is used for input.
    COMMAND: "command",
    // Undocumented in Chrome RDP, but is used for messages that should not
    // output anything (e.g. `console.time()` calls).
    NULL_MESSAGE: "nullMessage",
  },
  MESSAGE_LEVEL: {
    LOG: "log",
    ERROR: "error",
    WARN: "warn",
    DEBUG: "debug",
    INFO: "info"
  }
};

const jstermCommands = {
  JSTERM_COMMANDS: {
    INSPECT: "inspectObject"
  }
};

// Combine into a single constants object
module.exports = Object.assign({
  FILTERS,
  DEFAULT_FILTERS,
  DEFAULT_FILTERS_VALUES,
},
  actionTypes,
  chromeRDPEnums,
  jstermCommands,
  prefs,
);
PK
!<�}f���Mchrome/devtools/modules/devtools/client/webconsole/new-console-output/main.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 BrowserLoader */

"use strict";

const { BrowserLoader } = ChromeUtils.import("resource://devtools/client/shared/browser-loader.js", {});

this.NewConsoleOutput = function (parentNode, jsterm, toolbox, owner, serviceContainer) {
  // Initialize module loader and load all modules of the new inline
  // preview feature. The entire code-base doesn't need any extra
  // privileges and runs entirely in content scope.
  let NewConsoleOutputWrapper = BrowserLoader({
    baseURI: "resource://devtools/client/webconsole/new-console-output/",
    window
  }).require("./new-console-output-wrapper");

  return new NewConsoleOutputWrapper(
    parentNode, jsterm, toolbox, owner, serviceContainer);
};
PK
!<Y�L�C;C;cchrome/devtools/modules/devtools/client/webconsole/new-console-output/new-console-output-wrapper.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { createElement, createFactory } = require("devtools/client/shared/vendor/react");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const ReactDOM = require("devtools/client/shared/vendor/react-dom");
const { Provider } = require("devtools/client/shared/vendor/react-redux");

const actions = require("devtools/client/webconsole/new-console-output/actions/index");
const { createContextMenu } = require("devtools/client/webconsole/new-console-output/utils/context-menu");
const { configureStore } = require("devtools/client/webconsole/new-console-output/store");
const { isPacketPrivate } = require("devtools/client/webconsole/new-console-output/utils/messages");
const { getAllMessagesById, getMessage } = require("devtools/client/webconsole/new-console-output/selectors/messages");

const EventEmitter = require("devtools/shared/event-emitter");
const ConsoleOutput = createFactory(require("devtools/client/webconsole/new-console-output/components/ConsoleOutput"));
const FilterBar = createFactory(require("devtools/client/webconsole/new-console-output/components/FilterBar"));
const SideBar = createFactory(require("devtools/client/webconsole/new-console-output/components/SideBar"));

let store = null;

function NewConsoleOutputWrapper(parentNode, jsterm, toolbox, owner, document) {
  EventEmitter.decorate(this);

  this.parentNode = parentNode;
  this.jsterm = jsterm;
  this.toolbox = toolbox;
  this.owner = owner;
  this.document = document;

  this.init = this.init.bind(this);

  this.queuedMessageAdds = [];
  this.queuedMessageUpdates = [];
  this.queuedRequestUpdates = [];
  this.throttledDispatchPromise = null;

  store = configureStore(this.jsterm.hud);
}
NewConsoleOutputWrapper.prototype = {
  init: function () {
    return new Promise((resolve) => {
      const attachRefToHud = (id, node) => {
        this.jsterm.hud[id] = node;
      };
      // Focus the input line whenever the output area is clicked.
      this.parentNode.addEventListener("click", (event) => {
        // Do not focus on middle/right-click or 2+ clicks.
        if (event.detail !== 1 || event.button !== 0) {
          return;
        }

        // Do not focus if a link was clicked
        let target = event.originalTarget || event.target;
        if (target.closest("a")) {
          return;
        }

        // Do not focus if an input field was clicked
        if (target.closest("input")) {
          return;
        }

        // Do not focus if something other than the output region was clicked
        // (including e.g. the clear messages button in toolbar)
        if (!target.closest(".webconsole-output-wrapper")) {
          return;
        }

        // Do not focus if something is selected
        let selection = this.document.defaultView.getSelection();
        if (selection && !selection.isCollapsed) {
          return;
        }

        this.jsterm.focus();
      });

      let { hud } = this.jsterm;

      const serviceContainer = {
        attachRefToHud,
        emitNewMessage: (node, messageId, timeStamp) => {
          this.jsterm.hud.emit("new-messages", new Set([{
            node,
            messageId,
            timeStamp,
          }]));
        },
        hudProxy: hud.proxy,
        openLink: (url, e) => {
          hud.owner.openLink(url, e);
        },
        createElement: nodename => {
          return this.document.createElement(nodename);
        },
        getLongString: (grip) => {
          return hud.proxy.webConsoleClient.getString(grip);
        },
        requestData(id, type) {
          return hud.proxy.networkDataProvider.requestData(id, type);
        },
        onViewSource(frame) {
          if (hud && hud.owner && hud.owner.viewSource) {
            hud.owner.viewSource(frame.url, frame.line);
          }
        }
      };

      // Set `openContextMenu` this way so, `serviceContainer` variable
      // is available in the current scope and we can pass it into
      // `createContextMenu` method.
      serviceContainer.openContextMenu = (e, message) => {
        let { screenX, screenY, target } = e;

        let messageEl = target.closest(".message");
        let clipboardText = messageEl ? messageEl.textContent : null;

        let messageVariable = target.closest(".objectBox");
        // Ensure that console.group and console.groupCollapsed commands are not captured
        let variableText = (messageVariable
          && !(messageEl.classList.contains("startGroup"))
          && !(messageEl.classList.contains("startGroupCollapsed")))
            ? messageVariable.textContent : null;

        // Retrieve closes actor id from the DOM.
        let actorEl = target.closest("[data-link-actor-id]") ||
                      target.querySelector("[data-link-actor-id]");
        let actor = actorEl ? actorEl.dataset.linkActorId : null;

        let rootObjectInspector = target.closest(".object-inspector");
        let rootActor = rootObjectInspector ?
                        rootObjectInspector.querySelector("[data-link-actor-id]") : null;
        let rootActorId = rootActor ? rootActor.dataset.linkActorId : null;

        let sidebarTogglePref = store.getState().prefs.sidebarToggle;
        let openSidebar = sidebarTogglePref ? (messageId) => {
          store.dispatch(actions.showObjectInSidebar(rootActorId, messageId));
        } : null;

        let menu = createContextMenu(this.jsterm, this.parentNode,
          { actor, clipboardText, variableText, message,
            serviceContainer, openSidebar, rootActorId });

        // Emit the "menu-open" event for testing.
        menu.once("open", () => this.emit("menu-open"));
        menu.popup(screenX, screenY, { doc: this.owner.chromeWindow.document });

        return menu;
      };

      if (this.toolbox) {
        Object.assign(serviceContainer, {
          onViewSourceInDebugger: frame => {
            this.toolbox.viewSourceInDebugger(frame.url, frame.line).then(() =>
              this.jsterm.hud.emit("source-in-debugger-opened")
            );
          },
          onViewSourceInScratchpad: frame => this.toolbox.viewSourceInScratchpad(
            frame.url,
            frame.line
          ),
          onViewSourceInStyleEditor: frame => this.toolbox.viewSourceInStyleEditor(
            frame.url,
            frame.line
          ),
          openNetworkPanel: (requestId) => {
            return this.toolbox.selectTool("netmonitor").then((panel) => {
              return panel.panelWin.Netmonitor.inspectRequest(requestId);
            });
          },
          sourceMapService: this.toolbox ? this.toolbox.sourceMapURLService : null,
          highlightDomElement: (grip, options = {}) => {
            return this.toolbox.highlighterUtils
              ? this.toolbox.highlighterUtils.highlightDomValueGrip(grip, options)
              : null;
          },
          unHighlightDomElement: (forceHide = false) => {
            return this.toolbox.highlighterUtils
              ? this.toolbox.highlighterUtils.unhighlight(forceHide)
              : null;
          },
          openNodeInInspector: async (grip) => {
            let onSelectInspector = this.toolbox.selectTool("inspector");
            let onGripNodeToFront = this.toolbox.highlighterUtils.gripToNodeFront(grip);
            let [
              front,
              inspector
            ] = await Promise.all([onGripNodeToFront, onSelectInspector]);

            let onInspectorUpdated = inspector.once("inspector-updated");
            let onNodeFrontSet = this.toolbox.selection.setNodeFront(front, "console");

            return Promise.all([onNodeFrontSet, onInspectorUpdated]);
          }
        });
      }

      let consoleOutput = ConsoleOutput({
        serviceContainer,
        onFirstMeaningfulPaint: resolve
      });

      let filterBar = FilterBar({
        hidePersistLogsCheckbox: this.jsterm.hud.isBrowserConsole,
        serviceContainer: {
          attachRefToHud
        }
      });

      let sideBar = SideBar({
        serviceContainer,
      });

      let provider = createElement(
        Provider,
        { store },
        dom.div(
          {className: "webconsole-output-wrapper"},
          filterBar,
          consoleOutput,
          sideBar
        ));
      this.body = ReactDOM.render(provider, this.parentNode);

      this.jsterm.focus();
    });
  },

  dispatchMessageAdd: function (packet, waitForResponse) {
    // Wait for the message to render to resolve with the DOM node.
    // This is just for backwards compatibility with old tests, and should
    // be removed once it's not needed anymore.
    // Can only wait for response if the action contains a valid message.
    let promise;
    // Also, do not expect any update while the panel is in background.
    if (waitForResponse && document.visibilityState === "visible") {
      const timeStampToMatch = packet.message
        ? packet.message.timeStamp
        : packet.timestamp;

      promise = new Promise(resolve => {
        let jsterm = this.jsterm;
        jsterm.hud.on("new-messages", function onThisMessage(messages) {
          for (let m of messages) {
            if (m.timeStamp === timeStampToMatch) {
              resolve(m.node);
              jsterm.hud.off("new-messages", onThisMessage);
              return;
            }
          }
        });
      });
    } else {
      promise = Promise.resolve();
    }

    this.batchedMessagesAdd(packet);
    return promise;
  },

  dispatchMessagesAdd: function (messages) {
    store.dispatch(actions.messagesAdd(messages));
  },

  dispatchMessagesClear: function () {
    // We might still have pending message additions and updates when the clear action is
    // triggered, so we need to flush them to make sure we don't have unexpected behavior
    // in the ConsoleOutput.
    this.queuedMessageAdds = [];
    this.queuedMessageUpdates = [];
    this.queuedRequestUpdates = [];
    store.dispatch(actions.messagesClear());
  },

  dispatchPrivateMessagesClear: function () {
    // We might still have pending private message additions when the private messages
    // clear action is triggered. We need to remove any private-window-issued packets from
    // the queue so they won't appear in the output.

    // For (network) message updates, we need to check both messages queue and the state
    // since we can receive updates even if the message isn't rendered yet.
    const messages = [...getAllMessagesById(store.getState()).values()];
    this.queuedMessageUpdates = this.queuedMessageUpdates.filter(({networkInfo}) => {
      const { actor } = networkInfo;

      const queuedNetworkMessage = this.queuedMessageAdds.find(p => p.actor === actor);
      if (queuedNetworkMessage && isPacketPrivate(queuedNetworkMessage)) {
        return false;
      }

      const requestMessage = messages.find(message => actor === message.actor);
      if (requestMessage && requestMessage.private === true) {
        return false;
      }

      return true;
    });

    // For (network) requests updates, we can check only the state, since there must be a
    // user interaction to get an update (i.e. the network message is displayed and thus
    // in the state).
    this.queuedRequestUpdates = this.queuedRequestUpdates.filter(({id}) => {
      const requestMessage = getMessage(store.getState(), id);
      if (requestMessage && requestMessage.private === true) {
        return false;
      }

      return true;
    });

    // Finally we clear the messages queue. This needs to be done here since we use it to
    // clean the other queues.
    this.queuedMessageAdds = this.queuedMessageAdds.filter(p => !isPacketPrivate(p));

    store.dispatch(actions.privateMessagesClear());
  },

  dispatchTimestampsToggle: function (enabled) {
    store.dispatch(actions.timestampsToggle(enabled));
  },

  dispatchMessageUpdate: function (message, res) {
    // network-message-updated will emit when all the update message arrives.
    // Since we can't ensure the order of the network update, we check
    // that networkInfo.updates has all we need.
    // Note that 'requestPostData' is sent only for POST requests, so we need
    // to count with that.
    const NUMBER_OF_NETWORK_UPDATE = 8;
    let expectedLength = NUMBER_OF_NETWORK_UPDATE;
    if (res.networkInfo.updates.includes("requestPostData")) {
      expectedLength++;
    }

    if (res.networkInfo.updates.length === expectedLength) {
      this.batchedMessageUpdates({ res, message });
    }
  },

  dispatchRequestUpdate: function (id, data) {
    this.batchedRequestUpdates({ id, data });
  },

  dispatchSidebarClose: function () {
    store.dispatch(actions.sidebarClose());
  },

  batchedMessageUpdates: function (info) {
    this.queuedMessageUpdates.push(info);
    this.setTimeoutIfNeeded();
  },

  batchedRequestUpdates: function (message) {
    this.queuedRequestUpdates.push(message);
    this.setTimeoutIfNeeded();
  },

  batchedMessagesAdd: function (message) {
    this.queuedMessageAdds.push(message);
    this.setTimeoutIfNeeded();
  },

  /**
   * Returns a Promise that resolves once any async dispatch is finally dispatched.
   */
  waitAsyncDispatches: function () {
    if (!this.throttledDispatchPromise) {
      return Promise.resolve();
    }
    return this.throttledDispatchPromise;
  },

  setTimeoutIfNeeded: function () {
    if (this.throttledDispatchPromise) {
      return;
    }

    this.throttledDispatchPromise = new Promise(done => {
      setTimeout(() => {
        this.throttledDispatchPromise = null;

        store.dispatch(actions.messagesAdd(this.queuedMessageAdds));
        this.queuedMessageAdds = [];

        if (this.queuedMessageUpdates.length > 0) {
          this.queuedMessageUpdates.forEach(({ message, res }) => {
            store.dispatch(actions.networkMessageUpdate(message, null, res));
            this.jsterm.hud.emit("network-message-updated", res);
          });
          this.queuedMessageUpdates = [];
        }
        if (this.queuedRequestUpdates.length > 0) {
          this.queuedRequestUpdates.forEach(({ id, data}) => {
            store.dispatch(actions.networkUpdateRequest(id, data));
          });
          this.queuedRequestUpdates = [];

          // Fire an event indicating that all data fetched from
          // the backend has been received. This is based on
          // 'FirefoxDataProvider.isQueuePayloadReady', see more
          // comments in that method.
          // (netmonitor/src/connector/firefox-data-provider).
          // This event might be utilized in tests to find the right
          // time when to finish.
          this.jsterm.hud.emit("network-request-payload-ready");
        }
        done();
      }, 50);
    });
  },

  // Should be used for test purpose only.
  getStore: function () {
    return store;
  }
};

// Exports from this module
module.exports = NewConsoleOutputWrapper;
PK
!<���G[[Ychrome/devtools/modules/devtools/client/webconsole/new-console-output/reducers/filters.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 constants = require("devtools/client/webconsole/new-console-output/constants");

const FilterState = (overrides) => Object.freeze(
  cloneState(constants.DEFAULT_FILTERS_VALUES, overrides)
);

function filters(state = FilterState(), action) {
  switch (action.type) {
    case constants.FILTER_TOGGLE:
      const {filter} = action;
      const active = !state[filter];
      return cloneState(state, {[filter]: active});
    case constants.FILTERS_CLEAR:
      return FilterState();
    case constants.DEFAULT_FILTERS_RESET:
      const newState = cloneState(state);
      constants.DEFAULT_FILTERS.forEach(filterName => {
        newState[filterName] = constants.DEFAULT_FILTERS_VALUES[filterName];
      });
      return newState;
    case constants.FILTER_TEXT_SET:
      const {text} = action;
      return cloneState(state, {[constants.FILTERS.TEXT]: text});
  }

  return state;
}

function cloneState(state, overrides) {
  return Object.assign({}, state, overrides);
}

exports.FilterState = FilterState;
exports.filters = filters;
PK
!<ڊ�a  Wchrome/devtools/modules/devtools/client/webconsole/new-console-output/reducers/index.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 { filters } = require("./filters");
const { messages } = require("./messages");
const { prefs } = require("./prefs");
const { ui } = require("./ui");

exports.reducers = {
  filters,
  messages,
  prefs,
  ui,
};
PK
!<Mڥ��f�fZchrome/devtools/modules/devtools/client/webconsole/new-console-output/reducers/messages.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 {
  isGroupType,
  l10n,
} = require("devtools/client/webconsole/new-console-output/utils/messages");

const constants = require("devtools/client/webconsole/new-console-output/constants");
const {
  DEFAULT_FILTERS,
  FILTERS,
  MESSAGE_TYPE,
  MESSAGE_SOURCE,
} = constants;
const { getGripPreviewItems } = require("devtools/client/shared/components/reps/reps");
const { getSourceNames } = require("devtools/client/shared/source-utils");

const {
  UPDATE_REQUEST,
} = require("devtools/client/netmonitor/src/constants");

const {
  processNetworkUpdates,
} = require("devtools/client/netmonitor/src/utils/request-utils");

const MessageState = overrides => Object.freeze(Object.assign({
  // List of all the messages added to the console.
  messagesById: new Map(),
  // Array of the visible messages.
  visibleMessages: [],
  // Object for the filtered messages.
  filteredMessagesCount: getDefaultFiltersCounter(),
  // List of the message ids which are opened.
  messagesUiById: [],
  // Map of the form {messageId : tableData}, which represent the data passed
  // as an argument in console.table calls.
  messagesTableDataById: new Map(),
  // Map of the form {groupMessageId : groupArray},
  // where groupArray is the list of of all the parent groups' ids of the groupMessageId.
  groupsById: new Map(),
  // Message id of the current group (no corresponding console.groupEnd yet).
  currentGroup: null,
  // Array of removed actors (i.e. actors logged in removed messages) we keep track of
  // in order to properly release them.
  // This array is not supposed to be consumed by any UI component.
  removedActors: [],
  // Map of the form {messageId : numberOfRepeat}
  repeatById: {},
  // Map of the form {messageId : networkInformation}
  // `networkInformation` holds request, response, totalTime, ...
  networkMessagesUpdateById: {},
}, overrides));

function cloneState(state) {
  return {
    messagesById: new Map(state.messagesById),
    visibleMessages: [...state.visibleMessages],
    filteredMessagesCount: {...state.filteredMessagesCount},
    messagesUiById: [...state.messagesUiById],
    messagesTableDataById: new Map(state.messagesTableDataById),
    groupsById: new Map(state.groupsById),
    currentGroup: state.currentGroup,
    removedActors: [...state.removedActors],
    repeatById: {...state.repeatById},
    networkMessagesUpdateById: {...state.networkMessagesUpdateById},
  };
}

function addMessage(state, filtersState, prefsState, newMessage) {
  const {
    messagesById,
    groupsById,
    currentGroup,
    repeatById,
  } = state;

  if (newMessage.type === constants.MESSAGE_TYPE.NULL_MESSAGE) {
    // When the message has a NULL type, we don't add it.
    return state;
  }

  if (newMessage.type === constants.MESSAGE_TYPE.END_GROUP) {
    // Compute the new current group.
    state.currentGroup = getNewCurrentGroup(currentGroup, groupsById);
    return state;
  }

  if (newMessage.allowRepeating && messagesById.size > 0) {
    let lastMessage = [...messagesById.values()][messagesById.size - 1];

    if (
      lastMessage.repeatId === newMessage.repeatId
      && lastMessage.groupId === currentGroup
    ) {
      state.repeatById[lastMessage.id] = (repeatById[lastMessage.id] || 1) + 1;
      return state;
    }
  }

  // Add the new message with a reference to the parent group.
  let parentGroups = getParentGroups(currentGroup, groupsById);
  newMessage.groupId = currentGroup;
  newMessage.indent = parentGroups.length;

  const addedMessage = Object.freeze(newMessage);
  state.messagesById.set(newMessage.id, addedMessage);

  if (newMessage.type === "trace") {
    // We want the stacktrace to be open by default.
    state.messagesUiById.push(newMessage.id);
  } else if (isGroupType(newMessage.type)) {
    state.currentGroup = newMessage.id;
    state.groupsById.set(newMessage.id, parentGroups);

    if (newMessage.type === constants.MESSAGE_TYPE.START_GROUP) {
      // We want the group to be open by default.
      state.messagesUiById.push(newMessage.id);
    }
  }

  const {
    visible,
    cause
  } = getMessageVisibility(addedMessage, state, filtersState);

  if (visible) {
    state.visibleMessages.push(newMessage.id);
  } else if (DEFAULT_FILTERS.includes(cause)) {
    state.filteredMessagesCount.global++;
    state.filteredMessagesCount[cause]++;
  }

  // Append received network-data also into networkMessagesUpdateById
  // that is responsible for collecting (lazy loaded) HTTP payload data.
  if (newMessage.source == "network") {
    state.networkMessagesUpdateById[newMessage.actor] = newMessage;
  }

  return state;
}

function messages(state = MessageState(), action, filtersState, prefsState) {
  const {
    messagesById,
    messagesUiById,
    messagesTableDataById,
    networkMessagesUpdateById,
    groupsById,
    visibleMessages,
  } = state;

  const {logLimit} = prefsState;

  let newState;
  switch (action.type) {
    case constants.MESSAGES_ADD:
      // Preemptively remove messages that will never be rendered
      let list = [];
      let prunableCount = 0;
      let lastMessageRepeatId = -1;
      for (let i = action.messages.length - 1; i >= 0; i--) {
        let message = action.messages[i];
        if (
          !message.groupId && !isGroupType(message.type) &&
          message.type !== MESSAGE_TYPE.END_GROUP
        ) {
          if (message.repeatId !== lastMessageRepeatId) {
            prunableCount++;
          }
          // Once we've added the max number of messages that can be added, stop.
          // Except for repeated messages, where we keep adding over the limit.
          if (prunableCount <= logLimit || message.repeatId == lastMessageRepeatId) {
            list.unshift(action.messages[i]);
          } else {
            break;
          }
        } else {
          list.unshift(message);
        }
        lastMessageRepeatId = message.repeatId;
      }

      newState = cloneState(state);
      list.forEach(message => {
        newState = addMessage(newState, filtersState, prefsState, message);
      });

      return limitTopLevelMessageCount(newState, logLimit);

    case constants.MESSAGES_CLEAR:
      return MessageState({
        // Store all actors from removed messages. This array is used by
        // `releaseActorsEnhancer` to release all of those backend actors.
        removedActors: [...state.messagesById.values()].reduce((res, msg) => {
          res.push(...getAllActorsInMessage(msg));
          return res;
        }, [])
      });

    case constants.PRIVATE_MESSAGES_CLEAR:
      const removedIds = [];
      for (const [id, message] of messagesById) {
        if (message.private === true) {
          removedIds.push(id);
        }
      }

      // If there's no private messages, there's no need to change the state.
      if (removedIds.length === 0) {
        return state;
      }

      return removeMessagesFromState({
        ...state,
      }, removedIds);

    case constants.MESSAGE_OPEN:
      const openState = {...state};
      openState.messagesUiById = [...messagesUiById, action.id];
      let currMessage = messagesById.get(action.id);

      // If the message is a group
      if (isGroupType(currMessage.type)) {
        // We want to make its children visible
        const messagesToShow = [...messagesById].reduce((res, [id, message]) => {
          if (
            !visibleMessages.includes(message.id)
            && getParentGroups(message.groupId, groupsById).includes(action.id)
            && getMessageVisibility(
              message,
              openState,
              filtersState,
              // We want to check if the message is in an open group
              // only if it is not a direct child of the group we're opening.
              message.groupId !== action.id
            ).visible
          ) {
            res.push(id);
          }
          return res;
        }, []);

        // We can then insert the messages ids right after the one of the group.
        const insertIndex = visibleMessages.indexOf(action.id) + 1;
        openState.visibleMessages = [
          ...visibleMessages.slice(0, insertIndex),
          ...messagesToShow,
          ...visibleMessages.slice(insertIndex),
        ];
      }

      // If the current message is a network event, mark it as opened-once,
      // so HTTP details are not fetched again the next time the user
      // opens the log.
      if (currMessage.source == "network") {
        openState.messagesById = (new Map(messagesById)).set(
          action.id, {
            ...currMessage,
            openedOnce: true
          });
      }
      return openState;

    case constants.MESSAGE_CLOSE:
      const closeState = {...state};
      let messageId = action.id;
      let index = closeState.messagesUiById.indexOf(messageId);
      closeState.messagesUiById.splice(index, 1);
      closeState.messagesUiById = [...closeState.messagesUiById];

      // If the message is a group
      if (isGroupType(messagesById.get(messageId).type)) {
        // Hide all its children
        closeState.visibleMessages = visibleMessages.filter(id =>
          getParentGroups(messagesById.get(id).groupId, groupsById)
            .includes(messageId) === false
        );
      }
      return closeState;

    case constants.MESSAGE_TABLE_RECEIVE:
      const {id, data} = action;

      return {
        ...state,
        messagesTableDataById: (new Map(messagesTableDataById)).set(id, data)
      };

    case constants.NETWORK_MESSAGE_UPDATE:
      return {
        ...state,
        networkMessagesUpdateById: {
          ...networkMessagesUpdateById,
          [action.message.id]: action.message
        }
      };

    case UPDATE_REQUEST:
    case constants.NETWORK_UPDATE_REQUEST: {
      let request = networkMessagesUpdateById[action.id];
      if (!request) {
        return state;
      }

      return {
        ...state,
        networkMessagesUpdateById: {
          ...networkMessagesUpdateById,
          [action.id]: {
            ...request,
            ...processNetworkUpdates(action.data),
          }
        }
      };
    }

    case constants.REMOVED_ACTORS_CLEAR:
      return {
        ...state,
        removedActors: [],
      };

    case constants.FILTER_TOGGLE:
    case constants.FILTER_TEXT_SET:
    case constants.FILTERS_CLEAR:
    case constants.DEFAULT_FILTERS_RESET:
      const messagesToShow = [];
      const filtered = getDefaultFiltersCounter();

      messagesById.forEach((message, msgId) => {
        const {
          visible,
          cause
        } = getMessageVisibility(message, state, filtersState);
        if (visible) {
          messagesToShow.push(msgId);
        } else if (DEFAULT_FILTERS.includes(cause)) {
          filtered.global = filtered.global + 1;
          filtered[cause] = filtered[cause] + 1;
        }
      });

      return {
        ...state,
        visibleMessages: messagesToShow,
        filteredMessagesCount: filtered,
      };
  }

  return state;
}

/**
 * Returns the new current group id given the previous current group and the groupsById
 * state property.
 *
 * @param {String} currentGroup: id of the current group
 * @param {Map} groupsById
 * @param {Array} ignoredIds: An array of ids which can't be the new current group.
 * @returns {String|null} The new current group id, or null if there isn't one.
 */
function getNewCurrentGroup(currentGroup, groupsById, ignoredIds = []) {
  if (!currentGroup) {
    return null;
  }

  // Retrieve the parent groups of the current group.
  let parents = groupsById.get(currentGroup);

  // If there's at least one parent, make the first one the new currentGroup.
  if (Array.isArray(parents) && parents.length > 0) {
    // If the found group must be ignored, let's search for its parent.
    if (ignoredIds.includes(parents[0])) {
      return getNewCurrentGroup(parents[0], groupsById, ignoredIds);
    }

    return parents[0];
  }

  return null;
}

function getParentGroups(currentGroup, groupsById) {
  let groups = [];
  if (currentGroup) {
    // If there is a current group, we add it as a parent
    groups = [currentGroup];

    // As well as all its parents, if it has some.
    let parentGroups = groupsById.get(currentGroup);
    if (Array.isArray(parentGroups) && parentGroups.length > 0) {
      groups = groups.concat(parentGroups);
    }
  }

  return groups;
}

/**
 * Remove all top level messages that exceeds message limit.
 * Also populate an array of all backend actors associated with these
 * messages so they can be released.
 */
function limitTopLevelMessageCount(newState, logLimit) {
  let topLevelCount = newState.groupsById.size === 0
    ? newState.messagesById.size
    : getToplevelMessageCount(newState);

  if (topLevelCount <= logLimit) {
    return newState;
  }

  const removedMessagesId = [];

  let cleaningGroup = false;
  for (let [id, message] of newState.messagesById) {
    // If we were cleaning a group and the current message does not have
    // a groupId, we're done cleaning.
    if (cleaningGroup === true && !message.groupId) {
      cleaningGroup = false;
    }

    // If we're not cleaning a group and the message count is below the logLimit,
    // we exit the loop.
    if (cleaningGroup === false && topLevelCount <= logLimit) {
      break;
    }

    // If we're not currently cleaning a group, and the current message is identified
    // as a group, set the cleaning flag to true.
    if (cleaningGroup === false && newState.groupsById.has(id)) {
      cleaningGroup = true;
    }

    if (!message.groupId) {
      topLevelCount--;
    }

    removedMessagesId.push(id);
  }

  return removeMessagesFromState(newState, removedMessagesId);
}

/**
 * Clean the properties for a given state object and an array of removed messages ids.
 * Be aware that this function MUTATE the `state` argument.
 *
 * @param {MessageState} state
 * @param {Array} removedMessagesIds
 * @returns {MessageState}
 */
function removeMessagesFromState(state, removedMessagesIds) {
  if (!Array.isArray(removedMessagesIds) || removedMessagesIds.length === 0) {
    return state;
  }

  const removedActors = [];
  const visibleMessages = [...state.visibleMessages];
  removedMessagesIds.forEach(id => {
    const index = visibleMessages.indexOf(id);
    if (index > -1) {
      visibleMessages.splice(index, 1);
    }

    removedActors.push(...getAllActorsInMessage(state.messagesById.get(id)));
  });

  if (state.visibleMessages.length > visibleMessages.length) {
    state.visibleMessages = visibleMessages;
  }

  if (removedActors.length > 0) {
    state.removedActors =  state.removedActors.concat(removedActors);
  }

  const isInRemovedId = id => removedMessagesIds.includes(id);
  const mapHasRemovedIdKey = map => removedMessagesIds.some(id => map.has(id));
  const objectHasRemovedIdKey = obj => Object.keys(obj).findIndex(isInRemovedId) !== -1;

  const cleanUpMap = map => {
    const clonedMap = new Map(map);
    removedMessagesIds.forEach(id => clonedMap.delete(id));
    return clonedMap;
  };
  const cleanUpObject = object => [...Object.entries(object)]
    .reduce((res, [id, value]) => {
      if (!isInRemovedId(id)) {
        res[id] = value;
      }
      return res;
    }, {});

  state.messagesById = cleanUpMap(state.messagesById);

  if (state.messagesUiById.find(isInRemovedId)) {
    state.messagesUiById = state.messagesUiById.filter(id => !isInRemovedId(id));
  }

  if (isInRemovedId(state.currentGroup)) {
    state.currentGroup =
      getNewCurrentGroup(state.currentGroup, state.groupsById, removedMessagesIds);
  }

  if (mapHasRemovedIdKey(state.messagesTableDataById)) {
    state.messagesTableDataById = cleanUpMap(state.messagesTableDataById);
  }
  if (mapHasRemovedIdKey(state.groupsById)) {
    state.groupsById = cleanUpMap(state.groupsById);
  }
  if (mapHasRemovedIdKey(state.groupsById)) {
    state.groupsById = cleanUpMap(state.groupsById);
  }

  if (objectHasRemovedIdKey(state.repeatById)) {
    state.repeatById = cleanUpObject(state.repeatById);
  }

  if (objectHasRemovedIdKey(state.networkMessagesUpdateById)) {
    state.networkMessagesUpdateById = cleanUpObject(state.networkMessagesUpdateById);
  }

  return state;
}

/**
 * Get an array of all the actors logged in a specific message.
 *
 * @param {Message} message: The message to get actors from.
 * @return {Array} An array containing all the actors logged in a message.
 */
function getAllActorsInMessage(message) {
  const {
    parameters,
    messageText,
  } = message;

  let actors = [];
  if (Array.isArray(parameters)) {
    message.parameters.forEach(parameter => {
      if (parameter.actor) {
        actors.push(parameter.actor);
      }
    });
  }

  if (messageText && messageText.actor) {
    actors.push(messageText.actor);
  }

  return actors;
}

/**
 * Returns total count of top level messages (those which are not
 * within a group).
 */
function getToplevelMessageCount(state) {
  let count = 0;
  state.messagesById.forEach(message => {
    if (!message.groupId) {
      count++;
    }
  });
  return count;
}

/**
 * Check if a message should be visible in the console output, and if not, what
 * causes it to be hidden.
 *
 * @return {Object} An object of the following form:
 *         - visible {Boolean}: true if the message should be visible
 *         - cause {String}: if visible is false, what causes the message to be hidden.
 */
function getMessageVisibility(message, messagesState, filtersState, checkGroup = true) {
  // Do not display the message if it's in closed group.
  if (
    checkGroup
    && !isInOpenedGroup(message, messagesState.groupsById, messagesState.messagesUiById)
  ) {
    return {
      visible: false,
      cause: "closedGroup"
    };
  }

  // Some messages can't be filtered out (e.g. groups).
  // So, always return visible: true for those.
  if (isUnfilterable(message)) {
    return {
      visible: true
    };
  }

  if (!passSearchFilters(message, filtersState)) {
    return {
      visible: false,
      cause: FILTERS.TEXT
    };
  }

  // Let's check all level filters (error, warn, log, …) and return visible: false
  // and the message level as a cause if the function returns false.
  if (!passLevelFilters(message, filtersState)) {
    return {
      visible: false,
      cause: message.level
    };
  }

  if (!passCssFilters(message, filtersState)) {
    return {
      visible: false,
      cause: FILTERS.CSS
    };
  }

  if (!passNetworkFilter(message, filtersState)) {
    return {
      visible: false,
      cause: FILTERS.NET
    };
  }

  if (!passXhrFilter(message, filtersState)) {
    return {
      visible: false,
      cause: FILTERS.NETXHR
    };
  }

  return {
    visible: true
  };
}

function isUnfilterable(message) {
  return [
    MESSAGE_TYPE.COMMAND,
    MESSAGE_TYPE.RESULT,
    MESSAGE_TYPE.START_GROUP,
    MESSAGE_TYPE.START_GROUP_COLLAPSED,
  ].includes(message.type);
}

function isInOpenedGroup(message, groupsById, messagesUI) {
  return !message.groupId
    || (
      !isGroupClosed(message.groupId, messagesUI)
      && !hasClosedParentGroup(groupsById.get(message.groupId), messagesUI)
    );
}

function hasClosedParentGroup(group, messagesUI) {
  return group.some(groupId => isGroupClosed(groupId, messagesUI));
}

function isGroupClosed(groupId, messagesUI) {
  return messagesUI.includes(groupId) === false;
}

/**
 * Returns true if the message shouldn't be hidden because of the network filter state.
 *
 * @param {Object} message - The message to check the filter against.
 * @param {FilterState} filters - redux "filters" state.
 * @returns {Boolean}
 */
function passNetworkFilter(message, filters) {
  // The message passes the filter if it is not a network message,
  // or if it is an xhr one,
  // or if the network filter is on.
  return (
    message.source !== MESSAGE_SOURCE.NETWORK ||
    message.isXHR === true ||
    filters[FILTERS.NET] === true
  );
}

/**
 * Returns true if the message shouldn't be hidden because of the xhr filter state.
 *
 * @param {Object} message - The message to check the filter against.
 * @param {FilterState} filters - redux "filters" state.
 * @returns {Boolean}
 */
function passXhrFilter(message, filters) {
  // The message passes the filter if it is not a network message,
  // or if it is a non-xhr one,
  // or if the xhr filter is on.
  return (
    message.source !== MESSAGE_SOURCE.NETWORK ||
    message.isXHR === false ||
    filters[FILTERS.NETXHR] === true
  );
}

/**
 * Returns true if the message shouldn't be hidden because of levels filter state.
 *
 * @param {Object} message - The message to check the filter against.
 * @param {FilterState} filters - redux "filters" state.
 * @returns {Boolean}
 */
function passLevelFilters(message, filters) {
  // The message passes the filter if it is not a console call,
  // or if its level matches the state of the corresponding filter.
  return (
    (message.source !== MESSAGE_SOURCE.CONSOLE_API &&
    message.source !== MESSAGE_SOURCE.JAVASCRIPT) ||
    filters[message.level] === true
  );
}

/**
 * Returns true if the message shouldn't be hidden because of the CSS filter state.
 *
 * @param {Object} message - The message to check the filter against.
 * @param {FilterState} filters - redux "filters" state.
 * @returns {Boolean}
 */
function passCssFilters(message, filters) {
  // The message passes the filter if it is not a CSS message,
  // or if the CSS filter is on.
  return (
    message.source !== MESSAGE_SOURCE.CSS ||
    filters.css === true
  );
}

/**
 * Returns true if the message shouldn't be hidden because of search filter state.
 *
 * @param {Object} message - The message to check the filter against.
 * @param {FilterState} filters - redux "filters" state.
 * @returns {Boolean}
 */
function passSearchFilters(message, filters) {
  let text = (filters.text || "").trim();

  // If there is no search, the message passes the filter.
  if (!text) {
    return true;
  }

  return (
    // Look for a match in parameters.
    isTextInParameters(text, message.parameters)
    // Look for a match in location.
    || isTextInFrame(text, message.frame)
    // Look for a match in net events.
    || isTextInNetEvent(text, message.request)
    // Look for a match in stack-trace.
    || isTextInStackTrace(text, message.stacktrace)
    // Look for a match in messageText.
    || isTextInMessageText(text, message.messageText)
    // Look for a match in notes.
    || isTextInNotes(text, message.notes)
    // Look for a match in prefix.
    || isTextInPrefix(text, message.prefix)
  );
}

/**
* Returns true if given text is included in provided stack frame.
*/
function isTextInFrame(text, frame) {
  if (!frame) {
    return false;
  }

  const {
    functionName,
    line,
    column,
    source
  } = frame;
  const { short } = getSourceNames(source);

  return `${functionName ? functionName + " " : ""}${short}:${line}:${column}`
    .toLocaleLowerCase()
    .includes(text.toLocaleLowerCase());
}

/**
* Returns true if given text is included in provided parameters.
*/
function isTextInParameters(text, parameters) {
  if (!parameters) {
    return false;
  }

  text = text.toLocaleLowerCase();
  return getAllProps(parameters).some(prop =>
    (prop + "").toLocaleLowerCase().includes(text)
  );
}

/**
* Returns true if given text is included in provided net event grip.
*/
function isTextInNetEvent(text, request) {
  if (!request) {
    return false;
  }

  text = text.toLocaleLowerCase();

  let method = request.method.toLocaleLowerCase();
  let url = request.url.toLocaleLowerCase();
  return method.includes(text) || url.includes(text);
}

/**
* Returns true if given text is included in provided stack trace.
*/
function isTextInStackTrace(text, stacktrace) {
  if (!Array.isArray(stacktrace)) {
    return false;
  }

  // isTextInFrame expect the properties of the frame object to be in the same
  // order they are rendered in the Frame component.
  return stacktrace.some(frame => isTextInFrame(text, {
    functionName: frame.functionName || l10n.getStr("stacktrace.anonymousFunction"),
    source: frame.filename,
    lineNumber: frame.lineNumber,
    columnNumber: frame.columnNumber
  }));
}

/**
* Returns true if given text is included in `messageText` field.
*/
function isTextInMessageText(text, messageText) {
  if (!messageText) {
    return false;
  }

  return messageText.toLocaleLowerCase().includes(text.toLocaleLowerCase());
}

/**
* Returns true if given text is included in notes.
*/
function isTextInNotes(text, notes) {
  if (!Array.isArray(notes)) {
    return false;
  }

  return notes.some(note =>
    // Look for a match in location.
    isTextInFrame(text, note.frame) ||
    // Look for a match in messageBody.
    (
      note.messageBody &&
      note.messageBody.toLocaleLowerCase().includes(text.toLocaleLowerCase())
    )
  );
}

/**
* Returns true if given text is included in prefix.
*/
function isTextInPrefix(text, prefix) {
  if (!prefix) {
    return false;
  }

  return `${prefix}: `.toLocaleLowerCase().includes(text.toLocaleLowerCase());
}

/**
 * Get a flat array of all the grips and their properties.
 *
 * @param {Array} Grips
 * @return {Array} Flat array of the grips and their properties.
 */
function getAllProps(grips) {
  let result = grips.reduce((res, grip) => {
    let previewItems = getGripPreviewItems(grip);
    let allProps = previewItems.length > 0 ? getAllProps(previewItems) : [];
    return [...res, grip, grip.class, ...allProps];
  }, []);

  // We are interested only in primitive props (to search for)
  // not in objects and undefined previews.
  result = result.filter(grip =>
    typeof grip != "object" &&
    typeof grip != "undefined"
  );

  return [...new Set(result)];
}

function getDefaultFiltersCounter() {
  const count = DEFAULT_FILTERS.reduce((res, filter) => {
    res[filter] = 0;
    return res;
  }, {});
  count.global = 0;
  return count;
}

exports.messages = messages;
PK
!<tZq�55Wchrome/devtools/modules/devtools/client/webconsole/new-console-output/reducers/prefs.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 PrefState = (overrides) => Object.freeze(Object.assign({
  logLimit: 1000,
  sidebarToggle: false
}, overrides));

function prefs(state = PrefState(), action) {
  return state;
}

exports.PrefState = PrefState;
exports.prefs = prefs;
PK
!<B�Z��Tchrome/devtools/modules/devtools/client/webconsole/new-console-output/reducers/ui.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 {
  FILTER_BAR_TOGGLE,
  INITIALIZE,
  PERSIST_TOGGLE,
  SELECT_NETWORK_MESSAGE_TAB,
  SIDEBAR_CLOSE,
  SHOW_OBJECT_IN_SIDEBAR,
  TIMESTAMPS_TOGGLE,
  MESSAGES_CLEAR,
} = require("devtools/client/webconsole/new-console-output/constants");

const {
  PANELS,
} = require("devtools/client/netmonitor/src/constants");

const UiState = (overrides) => Object.freeze(Object.assign({
  filterBarVisible: false,
  initialized: false,
  networkMessageActiveTabId: PANELS.HEADERS,
  persistLogs: false,
  sidebarVisible: false,
  timestampsVisible: true,
  gripInSidebar: null
}, overrides));

function ui(state = UiState(), action) {
  switch (action.type) {
    case FILTER_BAR_TOGGLE:
      return Object.assign({}, state, {filterBarVisible: !state.filterBarVisible});
    case PERSIST_TOGGLE:
      return Object.assign({}, state, {persistLogs: !state.persistLogs});
    case TIMESTAMPS_TOGGLE:
      return Object.assign({}, state, {timestampsVisible: action.visible});
    case SELECT_NETWORK_MESSAGE_TAB:
      return Object.assign({}, state, {networkMessageActiveTabId: action.id});
    case SIDEBAR_CLOSE:
      return Object.assign({}, state, {
        sidebarVisible: false,
        gripInSidebar: null
      });
    case INITIALIZE:
      return Object.assign({}, state, {initialized: true});
    case MESSAGES_CLEAR:
      return Object.assign({}, state, {sidebarVisible: false, gripInSidebar: null});
    case SHOW_OBJECT_IN_SIDEBAR:
      if (action.grip === state.gripInSidebar) {
        return state;
      }
      return Object.assign({}, state, {sidebarVisible: true, gripInSidebar: action.grip});
  }

  return state;
}

module.exports = {
  UiState,
  ui,
};
PK
!<Bz�J��Zchrome/devtools/modules/devtools/client/webconsole/new-console-output/selectors/filters.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

function getAllFilters(state) {
  return state.filters;
}

exports.getAllFilters = getAllFilters;
PK
!<�a
���[chrome/devtools/modules/devtools/client/webconsole/new-console-output/selectors/messages.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

function getAllMessagesById(state) {
  return state.messages.messagesById;
}

function getMessage(state, id) {
  return getAllMessagesById(state).get(id);
}

function getAllMessagesUiById(state) {
  return state.messages.messagesUiById;
}

function getAllMessagesTableDataById(state) {
  return state.messages.messagesTableDataById;
}

function getAllGroupsById(state) {
  return state.messages.groupsById;
}

function getCurrentGroup(state) {
  return state.messages.currentGroup;
}

function getVisibleMessages(state) {
  return state.messages.visibleMessages;
}

function getFilteredMessagesCount(state) {
  return state.messages.filteredMessagesCount;
}

function getAllRepeatById(state) {
  return state.messages.repeatById;
}

function getAllNetworkMessagesUpdateById(state) {
  return state.messages.networkMessagesUpdateById;
}

function getGroupsById(state) {
  return state.messages.groupsById;
}

module.exports = {
  getAllGroupsById,
  getAllMessagesById,
  getAllMessagesTableDataById,
  getAllMessagesUiById,
  getAllNetworkMessagesUpdateById,
  getAllRepeatById,
  getCurrentGroup,
  getFilteredMessagesCount,
  getGroupsById,
  getMessage,
  getVisibleMessages,
};
PK
!<����Xchrome/devtools/modules/devtools/client/webconsole/new-console-output/selectors/prefs.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

function getLogLimit(state) {
  return state.prefs.logLimit;
}

exports.getLogLimit = getLogLimit;
PK
!<����Uchrome/devtools/modules/devtools/client/webconsole/new-console-output/selectors/ui.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

function getAllUi(state) {
  return state.ui;
}

module.exports = {
  getAllUi,
};
PK
!<g�3U�!�!Nchrome/devtools/modules/devtools/client/webconsole/new-console-output/store.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {FilterState} = require("devtools/client/webconsole/new-console-output/reducers/filters");
const {PrefState} = require("devtools/client/webconsole/new-console-output/reducers/prefs");
const {UiState} = require("devtools/client/webconsole/new-console-output/reducers/ui");
const {
  applyMiddleware,
  compose,
  createStore
} = require("devtools/client/shared/vendor/redux");
const {
  BATCH_ACTIONS
} = require("devtools/client/shared/redux/middleware/debounce");
const {
  MESSAGE_OPEN,
  MESSAGES_ADD,
  MESSAGES_CLEAR,
  PRIVATE_MESSAGES_CLEAR,
  REMOVED_ACTORS_CLEAR,
  NETWORK_MESSAGE_UPDATE,
  PREFS,
} = require("devtools/client/webconsole/new-console-output/constants");
const { reducers } = require("./reducers/index");
const {
  getMessage,
  getAllMessagesUiById,
} = require("devtools/client/webconsole/new-console-output/selectors/messages");
const DataProvider = require("devtools/client/netmonitor/src/connector/firefox-data-provider");
const {
  getAllNetworkMessagesUpdateById,
} = require("devtools/client/webconsole/new-console-output/selectors/messages");
const {getPrefsService} = require("devtools/client/webconsole/new-console-output/utils/prefs");

/**
 * Create and configure store for the Console panel. This is the place
 * where various enhancers and middleware can be registered.
 */
function configureStore(hud, options = {}) {
  const prefsService = getPrefsService(hud);
  const {
    getBoolPref,
    getIntPref,
  } = prefsService;

  const logLimit = options.logLimit
    || Math.max(getIntPref("devtools.hud.loglimit"), 1);
  const sidebarToggle = getBoolPref(PREFS.UI.SIDEBAR_TOGGLE);

  const initialState = {
    prefs: PrefState({ logLimit, sidebarToggle }),
    filters: FilterState({
      error: getBoolPref(PREFS.FILTER.ERROR),
      warn: getBoolPref(PREFS.FILTER.WARN),
      info: getBoolPref(PREFS.FILTER.INFO),
      debug: getBoolPref(PREFS.FILTER.DEBUG),
      log: getBoolPref(PREFS.FILTER.LOG),
      css: getBoolPref(PREFS.FILTER.CSS),
      net: getBoolPref(PREFS.FILTER.NET),
      netxhr: getBoolPref(PREFS.FILTER.NETXHR),
    }),
    ui: UiState({
      filterBarVisible: getBoolPref(PREFS.UI.FILTER_BAR),
      networkMessageActiveTabId: "headers",
      persistLogs: getBoolPref(PREFS.UI.PERSIST),
    })
  };

  return createStore(
    createRootReducer(),
    initialState,
    compose(
      applyMiddleware(thunk.bind(null, {prefsService})),
      enableActorReleaser(hud),
      enableBatching(),
      enableNetProvider(hud)
    )
  );
}

function thunk(options = {}, { dispatch, getState }) {
  return next => action => {
    return (typeof action === "function")
      ? action(dispatch, getState, options)
      : next(action);
  };
}

function createRootReducer() {
  return function rootReducer(state, action) {
    // We want to compute the new state for all properties except "messages".
    const newState = [...Object.entries(reducers)].reduce((res, [key, reducer]) => {
      if (key !== "messages") {
        res[key] = reducer(state[key], action);
      }
      return res;
    }, {});

    return Object.assign(newState, {
      // specifically pass the updated filters and prefs state as additional arguments.
      messages: reducers.messages(
        state.messages,
        action,
        newState.filters,
        newState.prefs,
      ),
    });
  };
}

/**
 * A enhancer for the store to handle batched actions.
 */
function enableBatching() {
  return next => (reducer, initialState, enhancer) => {
    function batchingReducer(state, action) {
      switch (action.type) {
        case BATCH_ACTIONS:
          return action.actions.reduce(batchingReducer, state);
        default:
          return reducer(state, action);
      }
    }

    if (typeof initialState === "function" && typeof enhancer === "undefined") {
      enhancer = initialState;
      initialState = undefined;
    }

    return next(batchingReducer, initialState, enhancer);
  };
}

/**
 * This enhancer is responsible for releasing actors on the backend.
 * When messages with arguments are removed from the store we should also
 * clean up the backend.
 */
function enableActorReleaser(hud) {
  return next => (reducer, initialState, enhancer) => {
    function releaseActorsEnhancer(state, action) {
      state = reducer(state, action);

      let type = action.type;
      let proxy = hud ? hud.proxy : null;
      if (
        proxy &&
        ([MESSAGES_ADD, MESSAGES_CLEAR, PRIVATE_MESSAGES_CLEAR].includes(type))
      ) {
        releaseActors(state.messages.removedActors, proxy);

        // Reset `removedActors` in message reducer.
        state = reducer(state, {
          type: REMOVED_ACTORS_CLEAR
        });
      }

      return state;
    }

    return next(releaseActorsEnhancer, initialState, enhancer);
  };
}

/**
 * This enhancer is responsible for fetching HTTP details data
 * collected by the backend. The fetch happens on-demand
 * when the user expands network log in order to inspect it.
 *
 * This way we don't slow down the Console logging by fetching.
 * unnecessary data over RDP.
 */
function enableNetProvider(hud) {
  let dataProvider;
  return next => (reducer, initialState, enhancer) => {
    function netProviderEnhancer(state, action) {
      let proxy = hud ? hud.proxy : null;
      if (!proxy) {
        return reducer(state, action);
      }

      let actions = {
        updateRequest: (id, data, batch) => {
          proxy.dispatchRequestUpdate(id, data);
        }
      };

      // Data provider implements async logic for fetching
      // data from the backend. It's created the first
      // time it's needed.
      if (!dataProvider) {
        dataProvider = new DataProvider({
          actions,
          webConsoleClient: proxy.webConsoleClient
        });

        // /!\ This is terrible, but it allows ResponsePanel to be able to call
        // `dataProvider.requestData` to fetch response content lazily.
        // `proxy.networkDataProvider` is put by NewConsoleOutputWrapper on
        // `serviceContainer` which allow NetworkEventMessage to expose requestData on
        // the fake `connector` object it hands over to ResponsePanel.
        proxy.networkDataProvider = dataProvider;
      }

      let type = action.type;
      let newState = reducer(state, action);

      // If network message has been opened, fetch all HTTP details
      // from the backend. It can happen (especially in test) that
      // the message is opened before all network event updates are
      // received. The rest of updates will be handled below, see:
      // NETWORK_MESSAGE_UPDATE action handler.
      if (type == MESSAGE_OPEN) {
        let updates = getAllNetworkMessagesUpdateById(newState);
        let message = updates[action.id];
        if (message && !message.openedOnce && message.source == "network") {
          dataProvider.onNetworkEvent(null, message);
          message.updates.forEach(updateType => {
            dataProvider.onNetworkEventUpdate(null, {
              packet: { updateType: updateType },
              networkInfo: message,
            });
          });
        }
      }

      // Process all incoming HTTP details packets. Note that
      // Network event update packets are sent in batches from:
      // `NewConsoleOutputWrapper.dispatchMessageUpdate` using
      // NETWORK_MESSAGE_UPDATE action.
      // Make sure to call `dataProvider.onNetworkEventUpdate`
      // to fetch data from the backend.
      if (type == NETWORK_MESSAGE_UPDATE) {
        let actor = action.response.networkInfo.actor;
        let open = getAllMessagesUiById(state).includes(actor);
        if (open) {
          let message = getMessage(state, actor);
          message.updates.forEach(updateType => {
            dataProvider.onNetworkEventUpdate(null, {
              packet: { updateType },
              networkInfo: message,
            });
          });
        }
      }

      return newState;
    }

    return next(netProviderEnhancer, initialState, enhancer);
  };
}
/**
 * Helper function for releasing backend actors.
 */
function releaseActors(removedActors, proxy) {
  if (!proxy) {
    return;
  }

  removedActors.forEach(actor => proxy.releaseActor(actor));
}

// Provide the store factory for test code so that each test is working with
// its own instance.
module.exports.configureStore = configureStore;

PK
!<�5�ZZtchrome/devtools/modules/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/stub-snippets.js/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

// Console API

const consoleApiCommands = [
  "console.log('foobar', 'test')",
  "console.log(undefined)",
  "console.warn('danger, will robinson!')",
  "console.log(NaN)",
  "console.log(null)",
  "console.log('\u9f2c')",
  "console.clear()",
  "console.count('bar')",
  "console.assert(false, {message: 'foobar'})",
  "console.log('hello \\nfrom \\rthe \\\"string world!')",
  "console.log('\xFA\u1E47\u0129\xE7\xF6d\xEA \u021B\u0115\u0219\u0165')",
  "console.dirxml(window)",
  "console.log('myarray', ['red', 'green', 'blue'])",
  "console.log('myregex', /a.b.c/)",
  "console.table(['red', 'green', 'blue']);",
  "console.log('myobject', {red: 'redValue', green: 'greenValue', blue: 'blueValue'});",
  "console.debug('debug message');",
  "console.info('info message');",
  "console.error('error message');",
];

let consoleApi = new Map(consoleApiCommands.map(
  cmd => [cmd, {keys: [cmd], code: cmd}]));

consoleApi.set("console.log('mymap')", {
  keys: ["console.log('mymap')"],
  code: `
var map = new Map();
map.set("key1", "value1");
map.set("key2", "value2");
console.log('mymap', map);
`});

consoleApi.set("console.log('myset')", {
  keys: ["console.log('myset')"],
  code: `
console.log('myset', new Set(["a", "b"]));
`});

consoleApi.set("console.trace()", {
  keys: ["console.trace()"],
  code: `
function testStacktraceFiltering() {
  console.trace()
}
function foo() {
  testStacktraceFiltering()
}

foo()
`});

consoleApi.set("console.time('bar')", {
  keys: ["console.time('bar')", "timerAlreadyExists",
         "console.timeEnd('bar')", "timerDoesntExist"],
  code: `
console.time("bar");
console.time("bar");
console.timeEnd("bar");
console.timeEnd("bar");
`});

consoleApi.set("console.table('bar')", {
  keys: ["console.table('bar')"],
  code: `
console.table('bar');
`});

consoleApi.set("console.table(['a', 'b', 'c'])", {
  keys: ["console.table(['a', 'b', 'c'])"],
  code: `
console.table(['a', 'b', 'c']);
`});

consoleApi.set("console.group('bar')", {
  keys: ["console.group('bar')", "console.groupEnd('bar')"],
  code: `
console.group("bar");
console.groupEnd();
`});

consoleApi.set("console.groupCollapsed('foo')", {
  keys: ["console.groupCollapsed('foo')", "console.groupEnd('foo')"],
  code: `
console.groupCollapsed("foo");
console.groupEnd();
`});

consoleApi.set("console.group()", {
  keys: ["console.group()", "console.groupEnd()"],
  code: `
console.group();
console.groupEnd();
`});

consoleApi.set("console.log(%cfoobar)", {
  keys: ["console.log(%cfoobar)"],
  code: `
console.log(
  "%cfoo%cbar",
  "color:blue; font-size:1.3em; background:url('http://example.com/test'); position:absolute; top:10px; ",
  "color:red; line-height: 1.5; background:\\165rl('http://example.com/test')"
);
`});

consoleApi.set('console.log("%cHello%c|%cWorld")', {
  keys: ['console.log("%cHello%c|%cWorld")'],
  code: `
  console.log(
    "%cHello%c|%cWorld",
    "color:red",
    "",
    "color: blue"
  );
`});

consoleApi.set("console.group(%cfoo%cbar)", {
  keys: ["console.group(%cfoo%cbar)", "console.groupEnd(%cfoo%cbar)"],
  code: `
console.group(
  "%cfoo%cbar",
  "color:blue;font-size:1.3em;background:url('http://example.com/test');position:absolute;top:10px",
  "color:red;background:\\165rl('http://example.com/test')");
console.groupEnd();
`});

consoleApi.set("console.groupCollapsed(%cfoo%cbaz)", {
  keys: ["console.groupCollapsed(%cfoo%cbaz)", "console.groupEnd(%cfoo%cbaz)"],
  code: `
console.groupCollapsed(
  "%cfoo%cbaz",
  "color:blue;font-size:1.3em;background:url('http://example.com/test');position:absolute;top:10px",
  "color:red;background:\\165rl('http://example.com/test')");
console.groupEnd();
`});

consoleApi.set("console.dir({C, M, Y, K})", {
  keys: ["console.dir({C, M, Y, K})"],
  code: "console.dir({cyan: 'C', magenta: 'M', yellow: 'Y', black: 'K'});"
});

consoleApi.set("console.count", {
  keys: [
    "console.count | default: 1",
    "console.count | default: 2",
    "console.count | test counter: 1",
    "console.count | test counter: 2",
    "console.count | default: 3",
    "console.count | clear",
    "console.count | default: 4",
    "console.count | test counter: 3",
  ],
  code: `
    console.count();
    console.count();
    console.count("test counter");
    console.count("test counter");
    console.count();
    console.clear();
    console.count();
    console.count("test counter");
`});

// CSS messages
const cssMessage = new Map();

cssMessage.set("Unknown property", `
p {
  such-unknown-property: wow;
}
`);

cssMessage.set("Invalid property value", `
p {
  padding-top: invalid value;
}
`);

// Evaluation Result
const evaluationResultCommands = [
  "new Date(0)",
  "asdf()",
  "1 + @",
  "inspect({a: 1})",
  "cd(document)",
  "undefined"
];

let evaluationResult = new Map(evaluationResultCommands.map(cmd => [cmd, cmd]));
evaluationResult.set("longString message Error",
  `throw new Error("Long error ".repeat(10000))`);

evaluationResult.set(`eval throw ""`, `throw ""`);
evaluationResult.set(`eval throw "tomato"`, `throw "tomato"`);

// Network Event

let networkEvent = new Map();

networkEvent.set("GET request", {
  keys: ["GET request"],
  code: `
let i = document.createElement("img");
i.src = "inexistent.html";
`});

networkEvent.set("XHR GET request", {
  keys: ["XHR GET request"],
  code: `
const xhr = new XMLHttpRequest();
xhr.open("GET", "inexistent.html");
xhr.send();
`});

networkEvent.set("XHR POST request", {
  keys: ["XHR POST request"],
  code: `
const xhr = new XMLHttpRequest();
xhr.open("POST", "inexistent.html");
xhr.send();
`});

// Page Error

let pageError = new Map();

pageError.set("ReferenceError: asdf is not defined", `
  function bar() {
    asdf()
  }
  function foo() {
    bar()
  }

  foo()
`);

pageError.set("SyntaxError: redeclaration of let a", `
  let a, a;
`);

pageError.set("TypeError longString message",
  `throw new Error("Long error ".repeat(10000))`);

pageError.set(`throw ""`, `throw ""`);
pageError.set(`throw "tomato"`, `throw "tomato"`);

module.exports = {
  consoleApi,
  cssMessage,
  evaluationResult,
  networkEvent,
  pageError,
};
PK
!<o]J<z�z�gchrome/devtools/modules/devtools/client/webconsole/new-console-output/test/fixtures/stubs/consoleApi.js/* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable max-len */

"use strict";

/*
 * THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND. RUN TESTS IN FIXTURES/ TO UPDATE.
 */

const { ConsoleMessage } =
  require("devtools/client/webconsole/new-console-output/types");

let stubPreparedMessages = new Map();
let stubPackets = new Map();
stubPreparedMessages.set(`console.log('foobar', 'test')`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1502884924471,
  "type": "log",
  "helperType": null,
  "level": "log",
  "messageText": null,
  "parameters": [
    "foobar",
    "test"
  ],
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"foobar\",\"test\"],\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 1,
    "column": 27
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.log(undefined)`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1502884924479,
  "type": "log",
  "helperType": null,
  "level": "log",
  "messageText": null,
  "parameters": [
    {
      "type": "undefined"
    }
  ],
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"undefined\"}],\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 1,
    "column": 27
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.warn('danger, will robinson!')`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1502884924487,
  "type": "warn",
  "helperType": null,
  "level": "warn",
  "messageText": null,
  "parameters": [
    "danger, will robinson!"
  ],
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"indent\":0,\"level\":\"warn\",\"messageText\":null,\"parameters\":[\"danger, will robinson!\"],\"source\":\"console-api\",\"type\":\"warn\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 1,
    "column": 27
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.log(NaN)`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1502884924495,
  "type": "log",
  "helperType": null,
  "level": "log",
  "messageText": null,
  "parameters": [
    {
      "type": "NaN"
    }
  ],
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"NaN\"}],\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 1,
    "column": 27
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.log(null)`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1502884924501,
  "type": "log",
  "helperType": null,
  "level": "log",
  "messageText": null,
  "parameters": [
    {
      "type": "null"
    }
  ],
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"null\"}],\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 1,
    "column": 27
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.log('鼬')`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1502884924506,
  "type": "log",
  "helperType": null,
  "level": "log",
  "messageText": null,
  "parameters": [
    "鼬"
  ],
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"鼬\"],\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 1,
    "column": 27
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.clear()`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1502884924512,
  "type": "clear",
  "helperType": null,
  "level": "log",
  "messageText": null,
  "parameters": [
    "Console was cleared."
  ],
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"Console was cleared.\"],\"source\":\"console-api\",\"type\":\"clear\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 1,
    "column": 27
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.count('bar')`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1502884924515,
  "type": "log",
  "helperType": null,
  "level": "log",
  "messageText": "bar: 1",
  "parameters": null,
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":\"bar: 1\",\"parameters\":null,\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 1,
    "column": 27
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.assert(false, {message: 'foobar'})`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1502884924521,
  "type": "assert",
  "helperType": null,
  "level": "error",
  "messageText": null,
  "parameters": [
    {
      "type": "object",
      "actor": "server1.conn0.child1/obj30",
      "class": "Object",
      "extensible": true,
      "frozen": false,
      "sealed": false,
      "ownPropertyLength": 1,
      "preview": {
        "kind": "Object",
        "ownProperties": {
          "message": {
            "configurable": true,
            "enumerable": true,
            "writable": true,
            "value": "foobar"
          }
        },
        "ownSymbols": [],
        "ownPropertiesLength": 1,
        "ownSymbolsLength": 0,
        "safeGetterValues": {}
      }
    }
  ],
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"indent\":0,\"level\":\"error\",\"messageText\":null,\"parameters\":[{\"type\":\"object\",\"actor\":\"server1.conn0.child1/obj30\",\"class\":\"Object\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":1,\"preview\":{\"kind\":\"Object\",\"ownProperties\":{\"message\":{\"configurable\":true,\"enumerable\":true,\"writable\":true,\"value\":\"foobar\"}},\"ownSymbols\":[],\"ownPropertiesLength\":1,\"ownSymbolsLength\":0,\"safeGetterValues\":{}}}],\"source\":\"console-api\",\"type\":\"assert\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": [
    {
      "columnNumber": 27,
      "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
      "functionName": "triggerPacket",
      "lineNumber": 1
    }
  ],
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 1,
    "column": 27
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.log('hello \nfrom \rthe \"string world!')`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1502884924528,
  "type": "log",
  "helperType": null,
  "level": "log",
  "messageText": null,
  "parameters": [
    "hello \nfrom \rthe \"string world!"
  ],
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"hello \\nfrom \\rthe \\\"string world!\"],\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 1,
    "column": 27
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.log('úṇĩçödê țĕșť')`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1502884924586,
  "type": "log",
  "helperType": null,
  "level": "log",
  "messageText": null,
  "parameters": [
    "úṇĩçödê țĕșť"
  ],
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"úṇĩçödê țĕșť\"],\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 1,
    "column": 27
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.dirxml(window)`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1502884924596,
  "type": "log",
  "helperType": null,
  "level": "log",
  "messageText": null,
  "parameters": [
    {
      "type": "object",
      "actor": "server1.conn0.child1/obj31",
      "class": "Window",
      "extensible": true,
      "frozen": false,
      "sealed": false,
      "ownPropertyLength": 830,
      "preview": {
        "kind": "ObjectWithURL",
        "url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html"
      }
    }
  ],
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"object\",\"actor\":\"server1.conn0.child1/obj31\",\"class\":\"Window\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":830,\"preview\":{\"kind\":\"ObjectWithURL\",\"url\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\"}}],\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 1,
    "column": 27
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.log('myarray', ['red', 'green', 'blue'])`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1502884924604,
  "type": "log",
  "helperType": null,
  "level": "log",
  "messageText": null,
  "parameters": [
    "myarray",
    {
      "type": "object",
      "actor": "server1.conn0.child1/obj32",
      "class": "Array",
      "extensible": true,
      "frozen": false,
      "sealed": false,
      "ownPropertyLength": 4,
      "preview": {
        "kind": "ArrayLike",
        "length": 3,
        "items": [
          "red",
          "green",
          "blue"
        ]
      }
    }
  ],
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"myarray\",{\"type\":\"object\",\"actor\":\"server1.conn0.child1/obj32\",\"class\":\"Array\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":4,\"preview\":{\"kind\":\"ArrayLike\",\"length\":3,\"items\":[\"red\",\"green\",\"blue\"]}}],\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 1,
    "column": 27
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.log('myregex', /a.b.c/)`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1502884924610,
  "type": "log",
  "helperType": null,
  "level": "log",
  "messageText": null,
  "parameters": [
    "myregex",
    {
      "type": "object",
      "actor": "server1.conn0.child1/obj33",
      "class": "RegExp",
      "extensible": true,
      "frozen": false,
      "sealed": false,
      "ownPropertyLength": 1,
      "displayString": "/a.b.c/"
    }
  ],
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"myregex\",{\"type\":\"object\",\"actor\":\"server1.conn0.child1/obj33\",\"class\":\"RegExp\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":1,\"displayString\":\"/a.b.c/\"}],\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 1,
    "column": 27
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.table(['red', 'green', 'blue']);`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1502884924612,
  "type": "table",
  "helperType": null,
  "level": "log",
  "messageText": null,
  "parameters": [
    {
      "type": "object",
      "actor": "server1.conn0.child1/obj34",
      "class": "Array",
      "extensible": true,
      "frozen": false,
      "sealed": false,
      "ownPropertyLength": 4,
      "preview": {
        "kind": "ArrayLike",
        "length": 3,
        "items": [
          "red",
          "green",
          "blue"
        ]
      }
    }
  ],
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"object\",\"actor\":\"server1.conn0.child1/obj34\",\"class\":\"Array\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":4,\"preview\":{\"kind\":\"ArrayLike\",\"length\":3,\"items\":[\"red\",\"green\",\"blue\"]}}],\"source\":\"console-api\",\"type\":\"table\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 1,
    "column": 27
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.log('myobject', {red: 'redValue', green: 'greenValue', blue: 'blueValue'});`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1502884924614,
  "type": "log",
  "helperType": null,
  "level": "log",
  "messageText": null,
  "parameters": [
    "myobject",
    {
      "type": "object",
      "actor": "server1.conn0.child1/obj35",
      "class": "Object",
      "extensible": true,
      "frozen": false,
      "sealed": false,
      "ownPropertyLength": 3,
      "preview": {
        "kind": "Object",
        "ownProperties": {
          "red": {
            "configurable": true,
            "enumerable": true,
            "writable": true,
            "value": "redValue"
          },
          "green": {
            "configurable": true,
            "enumerable": true,
            "writable": true,
            "value": "greenValue"
          },
          "blue": {
            "configurable": true,
            "enumerable": true,
            "writable": true,
            "value": "blueValue"
          }
        },
        "ownSymbols": [],
        "ownPropertiesLength": 3,
        "ownSymbolsLength": 0,
        "safeGetterValues": {}
      }
    }
  ],
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"myobject\",{\"type\":\"object\",\"actor\":\"server1.conn0.child1/obj35\",\"class\":\"Object\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":3,\"preview\":{\"kind\":\"Object\",\"ownProperties\":{\"red\":{\"configurable\":true,\"enumerable\":true,\"writable\":true,\"value\":\"redValue\"},\"green\":{\"configurable\":true,\"enumerable\":true,\"writable\":true,\"value\":\"greenValue\"},\"blue\":{\"configurable\":true,\"enumerable\":true,\"writable\":true,\"value\":\"blueValue\"}},\"ownSymbols\":[],\"ownPropertiesLength\":3,\"ownSymbolsLength\":0,\"safeGetterValues\":{}}}],\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 1,
    "column": 27
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.debug('debug message');`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1502884924621,
  "type": "debug",
  "helperType": null,
  "level": "debug",
  "messageText": null,
  "parameters": [
    "debug message"
  ],
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"indent\":0,\"level\":\"debug\",\"messageText\":null,\"parameters\":[\"debug message\"],\"source\":\"console-api\",\"type\":\"debug\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 1,
    "column": 27
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.info('info message');`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1502884924625,
  "type": "info",
  "helperType": null,
  "level": "info",
  "messageText": null,
  "parameters": [
    "info message"
  ],
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"indent\":0,\"level\":\"info\",\"messageText\":null,\"parameters\":[\"info message\"],\"source\":\"console-api\",\"type\":\"info\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 1,
    "column": 27
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.error('error message');`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1502884924628,
  "type": "error",
  "helperType": null,
  "level": "error",
  "messageText": null,
  "parameters": [
    "error message"
  ],
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"indent\":0,\"level\":\"error\",\"messageText\":null,\"parameters\":[\"error message\"],\"source\":\"console-api\",\"type\":\"error\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": [
    {
      "columnNumber": 27,
      "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
      "functionName": "triggerPacket",
      "lineNumber": 1
    }
  ],
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 1,
    "column": 27
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.log('mymap')`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1502884924631,
  "type": "log",
  "helperType": null,
  "level": "log",
  "messageText": null,
  "parameters": [
    "mymap",
    {
      "type": "object",
      "actor": "server1.conn0.child1/obj36",
      "class": "Map",
      "extensible": true,
      "frozen": false,
      "sealed": false,
      "ownPropertyLength": 0,
      "preview": {
        "kind": "MapLike",
        "size": 2,
        "entries": [
          [
            "key1",
            "value1"
          ],
          [
            "key2",
            "value2"
          ]
        ]
      }
    }
  ],
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":5,\"column\":1},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"mymap\",{\"type\":\"object\",\"actor\":\"server1.conn0.child1/obj36\",\"class\":\"Map\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":0,\"preview\":{\"kind\":\"MapLike\",\"size\":2,\"entries\":[[\"key1\",\"value1\"],[\"key2\",\"value2\"]]}}],\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 5,
    "column": 1
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.log('myset')`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1502884924746,
  "type": "log",
  "helperType": null,
  "level": "log",
  "messageText": null,
  "parameters": [
    "myset",
    {
      "type": "object",
      "actor": "server1.conn0.child1/obj37",
      "class": "Set",
      "extensible": true,
      "frozen": false,
      "sealed": false,
      "ownPropertyLength": 0,
      "preview": {
        "kind": "ArrayLike",
        "length": 2,
        "items": [
          "a",
          "b"
        ]
      }
    }
  ],
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":1},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"myset\",{\"type\":\"object\",\"actor\":\"server1.conn0.child1/obj37\",\"class\":\"Set\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":0,\"preview\":{\"kind\":\"ArrayLike\",\"length\":2,\"items\":[\"a\",\"b\"]}}],\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 2,
    "column": 1
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.trace()`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1502884924752,
  "type": "trace",
  "helperType": null,
  "level": "log",
  "messageText": null,
  "parameters": [],
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":3,\"column\":3},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[],\"source\":\"console-api\",\"type\":\"trace\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": [
    {
      "columnNumber": 3,
      "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
      "functionName": "testStacktraceFiltering",
      "lineNumber": 3
    },
    {
      "columnNumber": 3,
      "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
      "functionName": "foo",
      "lineNumber": 6
    },
    {
      "columnNumber": 1,
      "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
      "functionName": "triggerPacket",
      "lineNumber": 9
    }
  ],
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 3,
    "column": 3
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.time('bar')`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1502884924757,
  "type": "nullMessage",
  "helperType": null,
  "level": "log",
  "messageText": null,
  "parameters": null,
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":1},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":null,\"source\":\"console-api\",\"type\":\"nullMessage\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 2,
    "column": 1
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`timerAlreadyExists`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1502884924758,
  "type": "time",
  "helperType": null,
  "level": "warn",
  "messageText": "Timer “bar” already exists.",
  "parameters": null,
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":3,\"column\":1},\"groupId\":null,\"indent\":0,\"level\":\"warn\",\"messageText\":\"Timer “bar” already exists.\",\"parameters\":null,\"source\":\"console-api\",\"type\":\"time\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 3,
    "column": 1
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.timeEnd('bar')`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1502884924759,
  "type": "timeEnd",
  "helperType": null,
  "level": "log",
  "messageText": "bar: 1.21ms",
  "parameters": null,
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":4,\"column\":1},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":\"bar: 1.21ms\",\"parameters\":null,\"source\":\"console-api\",\"type\":\"timeEnd\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 4,
    "column": 1
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`timerDoesntExist`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1502884924759,
  "type": "timeEnd",
  "helperType": null,
  "level": "warn",
  "messageText": "Timer “bar” doesn’t exist.",
  "parameters": null,
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":5,\"column\":1},\"groupId\":null,\"indent\":0,\"level\":\"warn\",\"messageText\":\"Timer “bar” doesn’t exist.\",\"parameters\":null,\"source\":\"console-api\",\"type\":\"timeEnd\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 5,
    "column": 1
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.table('bar')`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1502884924801,
  "type": "log",
  "helperType": null,
  "level": "log",
  "messageText": null,
  "parameters": [
    "bar"
  ],
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":1},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"bar\"],\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 2,
    "column": 1
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.table(['a', 'b', 'c'])`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1502884924859,
  "type": "table",
  "helperType": null,
  "level": "log",
  "messageText": null,
  "parameters": [
    {
      "type": "object",
      "actor": "server1.conn0.child1/obj39",
      "class": "Array",
      "extensible": true,
      "frozen": false,
      "sealed": false,
      "ownPropertyLength": 4,
      "preview": {
        "kind": "ArrayLike",
        "length": 3,
        "items": [
          "a",
          "b",
          "c"
        ]
      }
    }
  ],
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":1},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"object\",\"actor\":\"server1.conn0.child1/obj39\",\"class\":\"Array\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":4,\"preview\":{\"kind\":\"ArrayLike\",\"length\":3,\"items\":[\"a\",\"b\",\"c\"]}}],\"source\":\"console-api\",\"type\":\"table\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 2,
    "column": 1
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.group('bar')`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1502884924863,
  "type": "startGroup",
  "helperType": null,
  "level": "log",
  "messageText": null,
  "parameters": [
    "bar"
  ],
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":1},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"bar\"],\"source\":\"console-api\",\"type\":\"startGroup\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 2,
    "column": 1
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.groupEnd('bar')`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1502884924864,
  "type": "endGroup",
  "helperType": null,
  "level": "log",
  "messageText": null,
  "parameters": null,
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":3,\"column\":1},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":null,\"source\":\"console-api\",\"type\":\"endGroup\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 3,
    "column": 1
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.groupCollapsed('foo')`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1502884924870,
  "type": "startGroupCollapsed",
  "helperType": null,
  "level": "log",
  "messageText": null,
  "parameters": [
    "foo"
  ],
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":1},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"foo\"],\"source\":\"console-api\",\"type\":\"startGroupCollapsed\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 2,
    "column": 1
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.groupEnd('foo')`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1502884924871,
  "type": "endGroup",
  "helperType": null,
  "level": "log",
  "messageText": null,
  "parameters": null,
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":3,\"column\":1},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":null,\"source\":\"console-api\",\"type\":\"endGroup\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 3,
    "column": 1
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.group()`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1502884924878,
  "type": "startGroup",
  "helperType": null,
  "level": "log",
  "messageText": null,
  "parameters": [
    "<no group label>"
  ],
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":1},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"<no group label>\"],\"source\":\"console-api\",\"type\":\"startGroup\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 2,
    "column": 1
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.groupEnd()`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1502884924879,
  "type": "endGroup",
  "helperType": null,
  "level": "log",
  "messageText": null,
  "parameters": null,
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":3,\"column\":1},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":null,\"source\":\"console-api\",\"type\":\"endGroup\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 3,
    "column": 1
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.log(%cfoobar)`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1502884924883,
  "type": "log",
  "helperType": null,
  "level": "log",
  "messageText": null,
  "parameters": [
    "foo",
    "bar"
  ],
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":1},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"foo\",\"bar\"],\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[\"color:blue; font-size:1.3em; background:url('http://example.com/test'); position:absolute; top:10px; \",\"color:red; line-height: 1.5; background:url('http://example.com/test')\"],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 2,
    "column": 1
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [
    "color:blue; font-size:1.3em; background:url('http://example.com/test'); position:absolute; top:10px; ",
    "color:red; line-height: 1.5; background:url('http://example.com/test')"
  ],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.log("%cHello%c|%cWorld")`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1518681614352,
  "type": "log",
  "helperType": null,
  "level": "log",
  "messageText": null,
  "parameters": [
    "Hello",
    "|",
    "World"
  ],
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":3},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"Hello\",\"|\",\"World\"],\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[\"color:red\",\"\",\"color: blue\"],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 2,
    "column": 3
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [
    "color:red",
    "",
    "color: blue"
  ],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.group(%cfoo%cbar)`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1502884924887,
  "type": "startGroup",
  "helperType": null,
  "level": "log",
  "messageText": null,
  "parameters": [
    "foo",
    "bar"
  ],
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":1},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"foo\",\"bar\"],\"source\":\"console-api\",\"type\":\"startGroup\",\"userProvidedStyles\":[\"color:blue;font-size:1.3em;background:url('http://example.com/test');position:absolute;top:10px\",\"color:red;background:url('http://example.com/test')\"],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 2,
    "column": 1
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [
    "color:blue;font-size:1.3em;background:url('http://example.com/test');position:absolute;top:10px",
    "color:red;background:url('http://example.com/test')"
  ],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.groupEnd(%cfoo%cbar)`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1502884924887,
  "type": "endGroup",
  "helperType": null,
  "level": "log",
  "messageText": null,
  "parameters": null,
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":6,\"column\":1},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":null,\"source\":\"console-api\",\"type\":\"endGroup\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 6,
    "column": 1
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.groupCollapsed(%cfoo%cbaz)`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1502884924892,
  "type": "startGroupCollapsed",
  "helperType": null,
  "level": "log",
  "messageText": null,
  "parameters": [
    "foo",
    "baz"
  ],
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":1},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"foo\",\"baz\"],\"source\":\"console-api\",\"type\":\"startGroupCollapsed\",\"userProvidedStyles\":[\"color:blue;font-size:1.3em;background:url('http://example.com/test');position:absolute;top:10px\",\"color:red;background:url('http://example.com/test')\"],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 2,
    "column": 1
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [
    "color:blue;font-size:1.3em;background:url('http://example.com/test');position:absolute;top:10px",
    "color:red;background:url('http://example.com/test')"
  ],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.groupEnd(%cfoo%cbaz)`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1502884924893,
  "type": "endGroup",
  "helperType": null,
  "level": "log",
  "messageText": null,
  "parameters": null,
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":6,\"column\":1},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":null,\"source\":\"console-api\",\"type\":\"endGroup\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 6,
    "column": 1
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.dir({C, M, Y, K})`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1502884924899,
  "type": "dir",
  "helperType": null,
  "level": "log",
  "messageText": null,
  "parameters": [
    {
      "type": "object",
      "actor": "server1.conn0.child1/obj36",
      "class": "Object",
      "extensible": true,
      "frozen": false,
      "sealed": false,
      "ownPropertyLength": 4,
      "preview": {
        "kind": "Object",
        "ownProperties": {
          "cyan": {
            "configurable": true,
            "enumerable": true,
            "writable": true,
            "value": "C"
          },
          "magenta": {
            "configurable": true,
            "enumerable": true,
            "writable": true,
            "value": "M"
          },
          "yellow": {
            "configurable": true,
            "enumerable": true,
            "writable": true,
            "value": "Y"
          },
          "black": {
            "configurable": true,
            "enumerable": true,
            "writable": true,
            "value": "K"
          }
        },
        "ownSymbols": [],
        "ownPropertiesLength": 4,
        "ownSymbolsLength": 0,
        "safeGetterValues": {}
      }
    }
  ],
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"object\",\"actor\":\"server1.conn0.child1/obj36\",\"class\":\"Object\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":4,\"preview\":{\"kind\":\"Object\",\"ownProperties\":{\"cyan\":{\"configurable\":true,\"enumerable\":true,\"writable\":true,\"value\":\"C\"},\"magenta\":{\"configurable\":true,\"enumerable\":true,\"writable\":true,\"value\":\"M\"},\"yellow\":{\"configurable\":true,\"enumerable\":true,\"writable\":true,\"value\":\"Y\"},\"black\":{\"configurable\":true,\"enumerable\":true,\"writable\":true,\"value\":\"K\"}},\"ownSymbols\":[],\"ownPropertiesLength\":4,\"ownSymbolsLength\":0,\"safeGetterValues\":{}}}],\"source\":\"console-api\",\"type\":\"dir\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 1,
    "column": 27
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.count | default: 1`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1511365913333,
  "type": "log",
  "helperType": null,
  "level": "log",
  "messageText": "default: 1",
  "parameters": null,
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":5},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":\"default: 1\",\"parameters\":null,\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 2,
    "column": 5
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.count | default: 2`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1511365913334,
  "type": "log",
  "helperType": null,
  "level": "log",
  "messageText": "default: 2",
  "parameters": null,
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":3,\"column\":5},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":\"default: 2\",\"parameters\":null,\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 3,
    "column": 5
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.count | test counter: 1`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1511365913334,
  "type": "log",
  "helperType": null,
  "level": "log",
  "messageText": "test counter: 1",
  "parameters": null,
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":4,\"column\":5},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":\"test counter: 1\",\"parameters\":null,\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 4,
    "column": 5
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.count | test counter: 2`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1511365913334,
  "type": "log",
  "helperType": null,
  "level": "log",
  "messageText": "test counter: 2",
  "parameters": null,
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":5,\"column\":5},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":\"test counter: 2\",\"parameters\":null,\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 5,
    "column": 5
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.count | default: 3`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1511365913334,
  "type": "log",
  "helperType": null,
  "level": "log",
  "messageText": "default: 3",
  "parameters": null,
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":6,\"column\":5},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":\"default: 3\",\"parameters\":null,\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 6,
    "column": 5
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.count | clear`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1511365913334,
  "type": "clear",
  "helperType": null,
  "level": "log",
  "messageText": null,
  "parameters": [
    "Console was cleared."
  ],
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":7,\"column\":5},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"Console was cleared.\"],\"source\":\"console-api\",\"type\":\"clear\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 7,
    "column": 5
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.count | default: 4`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1511365913335,
  "type": "log",
  "helperType": null,
  "level": "log",
  "messageText": "default: 4",
  "parameters": null,
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":8,\"column\":5},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":\"default: 4\",\"parameters\":null,\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 8,
    "column": 5
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`console.count | test counter: 3`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "console-api",
  "timeStamp": 1511365913335,
  "type": "log",
  "helperType": null,
  "level": "log",
  "messageText": "test counter: 3",
  "parameters": null,
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":9,\"column\":5},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":\"test counter: 3\",\"parameters\":null,\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[],\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 9,
    "column": 5
  },
  "groupId": null,
  "exceptionDocURL": null,
  "userProvidedStyles": [],
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPackets.set(`console.log('foobar', 'test')`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [
      "foobar",
      "test"
    ],
    "columnNumber": 27,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "",
    "level": "log",
    "lineNumber": 1,
    "prefix": "",
    "private": false,
    "styles": [],
    "timeStamp": 1502884924471,
    "timer": null,
    "workerType": "none",
    "category": "webdev"
  }
});

stubPackets.set(`console.log(undefined)`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [
      {
        "type": "undefined"
      }
    ],
    "columnNumber": 27,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "",
    "level": "log",
    "lineNumber": 1,
    "prefix": "",
    "private": false,
    "styles": [],
    "timeStamp": 1502884924479,
    "timer": null,
    "workerType": "none",
    "category": "webdev"
  }
});

stubPackets.set(`console.warn('danger, will robinson!')`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [
      "danger, will robinson!"
    ],
    "columnNumber": 27,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "",
    "level": "warn",
    "lineNumber": 1,
    "prefix": "",
    "private": false,
    "styles": [],
    "timeStamp": 1502884924487,
    "timer": null,
    "workerType": "none",
    "category": "webdev"
  }
});

stubPackets.set(`console.log(NaN)`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [
      {
        "type": "NaN"
      }
    ],
    "columnNumber": 27,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "",
    "level": "log",
    "lineNumber": 1,
    "prefix": "",
    "private": false,
    "styles": [],
    "timeStamp": 1502884924495,
    "timer": null,
    "workerType": "none",
    "category": "webdev"
  }
});

stubPackets.set(`console.log(null)`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [
      {
        "type": "null"
      }
    ],
    "columnNumber": 27,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "",
    "level": "log",
    "lineNumber": 1,
    "prefix": "",
    "private": false,
    "styles": [],
    "timeStamp": 1502884924501,
    "timer": null,
    "workerType": "none",
    "category": "webdev"
  }
});

stubPackets.set(`console.log('鼬')`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [
      "鼬"
    ],
    "columnNumber": 27,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "",
    "level": "log",
    "lineNumber": 1,
    "prefix": "",
    "private": false,
    "styles": [],
    "timeStamp": 1502884924506,
    "timer": null,
    "workerType": "none",
    "category": "webdev"
  }
});

stubPackets.set(`console.clear()`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [],
    "columnNumber": 27,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "",
    "level": "clear",
    "lineNumber": 1,
    "prefix": "",
    "private": false,
    "timeStamp": 1502884924512,
    "timer": null,
    "workerType": "none",
    "styles": [],
    "category": "webdev"
  }
});

stubPackets.set(`console.count('bar')`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [
      "bar"
    ],
    "columnNumber": 27,
    "counter": {
      "count": 1,
      "label": "bar"
    },
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "",
    "level": "count",
    "lineNumber": 1,
    "prefix": "",
    "private": false,
    "timeStamp": 1502884924515,
    "timer": null,
    "workerType": "none",
    "styles": [],
    "category": "webdev"
  }
});

stubPackets.set(`console.assert(false, {message: 'foobar'})`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [
      {
        "type": "object",
        "actor": "server1.conn0.child1/obj30",
        "class": "Object",
        "extensible": true,
        "frozen": false,
        "sealed": false,
        "ownPropertyLength": 1,
        "preview": {
          "kind": "Object",
          "ownProperties": {
            "message": {
              "configurable": true,
              "enumerable": true,
              "writable": true,
              "value": "foobar"
            }
          },
          "ownSymbols": [],
          "ownPropertiesLength": 1,
          "ownSymbolsLength": 0,
          "safeGetterValues": {}
        }
      }
    ],
    "columnNumber": 27,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "",
    "level": "assert",
    "lineNumber": 1,
    "prefix": "",
    "private": false,
    "styles": [],
    "timeStamp": 1502884924521,
    "timer": null,
    "stacktrace": [
      {
        "columnNumber": 27,
        "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
        "functionName": "triggerPacket",
        "lineNumber": 1
      }
    ],
    "workerType": "none",
    "category": "webdev"
  }
});

stubPackets.set(`console.log('hello \nfrom \rthe \"string world!')`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [
      "hello \nfrom \rthe \"string world!"
    ],
    "columnNumber": 27,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "",
    "level": "log",
    "lineNumber": 1,
    "prefix": "",
    "private": false,
    "styles": [],
    "timeStamp": 1502884924528,
    "timer": null,
    "workerType": "none",
    "category": "webdev"
  }
});

stubPackets.set(`console.log('úṇĩçödê țĕșť')`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [
      "úṇĩçödê țĕșť"
    ],
    "columnNumber": 27,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "",
    "level": "log",
    "lineNumber": 1,
    "prefix": "",
    "private": false,
    "styles": [],
    "timeStamp": 1502884924586,
    "timer": null,
    "workerType": "none",
    "category": "webdev"
  }
});

stubPackets.set(`console.dirxml(window)`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [
      {
        "type": "object",
        "actor": "server1.conn0.child1/obj31",
        "class": "Window",
        "extensible": true,
        "frozen": false,
        "sealed": false,
        "ownPropertyLength": 830,
        "preview": {
          "kind": "ObjectWithURL",
          "url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html"
        }
      }
    ],
    "columnNumber": 27,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "",
    "level": "dirxml",
    "lineNumber": 1,
    "prefix": "",
    "private": false,
    "timeStamp": 1502884924596,
    "timer": null,
    "workerType": "none",
    "styles": [],
    "category": "webdev"
  }
});

stubPackets.set(`console.log('myarray', ['red', 'green', 'blue'])`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [
      "myarray",
      {
        "type": "object",
        "actor": "server1.conn0.child1/obj32",
        "class": "Array",
        "extensible": true,
        "frozen": false,
        "sealed": false,
        "ownPropertyLength": 4,
        "preview": {
          "kind": "ArrayLike",
          "length": 3,
          "items": [
            "red",
            "green",
            "blue"
          ]
        }
      }
    ],
    "columnNumber": 27,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "",
    "level": "log",
    "lineNumber": 1,
    "prefix": "",
    "private": false,
    "styles": [],
    "timeStamp": 1502884924604,
    "timer": null,
    "workerType": "none",
    "category": "webdev"
  }
});

stubPackets.set(`console.log('myregex', /a.b.c/)`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [
      "myregex",
      {
        "type": "object",
        "actor": "server1.conn0.child1/obj33",
        "class": "RegExp",
        "extensible": true,
        "frozen": false,
        "sealed": false,
        "ownPropertyLength": 1,
        "displayString": "/a.b.c/"
      }
    ],
    "columnNumber": 27,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "",
    "level": "log",
    "lineNumber": 1,
    "prefix": "",
    "private": false,
    "styles": [],
    "timeStamp": 1502884924610,
    "timer": null,
    "workerType": "none",
    "category": "webdev"
  }
});

stubPackets.set(`console.table(['red', 'green', 'blue']);`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [
      {
        "type": "object",
        "actor": "server1.conn0.child1/obj34",
        "class": "Array",
        "extensible": true,
        "frozen": false,
        "sealed": false,
        "ownPropertyLength": 4,
        "preview": {
          "kind": "ArrayLike",
          "length": 3,
          "items": [
            "red",
            "green",
            "blue"
          ]
        }
      }
    ],
    "columnNumber": 27,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "",
    "level": "table",
    "lineNumber": 1,
    "prefix": "",
    "private": false,
    "timeStamp": 1502884924612,
    "timer": null,
    "workerType": "none",
    "styles": [],
    "category": "webdev"
  }
});

stubPackets.set(`console.log('myobject', {red: 'redValue', green: 'greenValue', blue: 'blueValue'});`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [
      "myobject",
      {
        "type": "object",
        "actor": "server1.conn0.child1/obj35",
        "class": "Object",
        "extensible": true,
        "frozen": false,
        "sealed": false,
        "ownPropertyLength": 3,
        "preview": {
          "kind": "Object",
          "ownProperties": {
            "red": {
              "configurable": true,
              "enumerable": true,
              "writable": true,
              "value": "redValue"
            },
            "green": {
              "configurable": true,
              "enumerable": true,
              "writable": true,
              "value": "greenValue"
            },
            "blue": {
              "configurable": true,
              "enumerable": true,
              "writable": true,
              "value": "blueValue"
            }
          },
          "ownSymbols": [],
          "ownPropertiesLength": 3,
          "ownSymbolsLength": 0,
          "safeGetterValues": {}
        }
      }
    ],
    "columnNumber": 27,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "",
    "level": "log",
    "lineNumber": 1,
    "prefix": "",
    "private": false,
    "styles": [],
    "timeStamp": 1502884924614,
    "timer": null,
    "workerType": "none",
    "category": "webdev"
  }
});

stubPackets.set(`console.debug('debug message');`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [
      "debug message"
    ],
    "columnNumber": 27,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "",
    "level": "debug",
    "lineNumber": 1,
    "prefix": "",
    "private": false,
    "styles": [],
    "timeStamp": 1502884924621,
    "timer": null,
    "workerType": "none",
    "category": "webdev"
  }
});

stubPackets.set(`console.info('info message');`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [
      "info message"
    ],
    "columnNumber": 27,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "",
    "level": "info",
    "lineNumber": 1,
    "prefix": "",
    "private": false,
    "styles": [],
    "timeStamp": 1502884924625,
    "timer": null,
    "workerType": "none",
    "category": "webdev"
  }
});

stubPackets.set(`console.error('error message');`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [
      "error message"
    ],
    "columnNumber": 27,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "",
    "level": "error",
    "lineNumber": 1,
    "prefix": "",
    "private": false,
    "styles": [],
    "timeStamp": 1502884924628,
    "timer": null,
    "stacktrace": [
      {
        "columnNumber": 27,
        "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
        "functionName": "triggerPacket",
        "lineNumber": 1
      }
    ],
    "workerType": "none",
    "category": "webdev"
  }
});

stubPackets.set(`console.log('mymap')`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [
      "mymap",
      {
        "type": "object",
        "actor": "server1.conn0.child1/obj36",
        "class": "Map",
        "extensible": true,
        "frozen": false,
        "sealed": false,
        "ownPropertyLength": 0,
        "preview": {
          "kind": "MapLike",
          "size": 2,
          "entries": [
            [
              "key1",
              "value1"
            ],
            [
              "key2",
              "value2"
            ]
          ]
        }
      }
    ],
    "columnNumber": 1,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "",
    "level": "log",
    "lineNumber": 5,
    "prefix": "",
    "private": false,
    "styles": [],
    "timeStamp": 1502884924631,
    "timer": null,
    "workerType": "none",
    "category": "webdev"
  }
});

stubPackets.set(`console.log('myset')`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [
      "myset",
      {
        "type": "object",
        "actor": "server1.conn0.child1/obj37",
        "class": "Set",
        "extensible": true,
        "frozen": false,
        "sealed": false,
        "ownPropertyLength": 0,
        "preview": {
          "kind": "ArrayLike",
          "length": 2,
          "items": [
            "a",
            "b"
          ]
        }
      }
    ],
    "columnNumber": 1,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "",
    "level": "log",
    "lineNumber": 2,
    "prefix": "",
    "private": false,
    "styles": [],
    "timeStamp": 1502884924746,
    "timer": null,
    "workerType": "none",
    "category": "webdev"
  }
});

stubPackets.set(`console.trace()`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [],
    "columnNumber": 3,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "testStacktraceFiltering",
    "groupName": "",
    "level": "trace",
    "lineNumber": 3,
    "prefix": "",
    "private": false,
    "timeStamp": 1502884924752,
    "timer": null,
    "stacktrace": [
      {
        "columnNumber": 3,
        "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
        "functionName": "testStacktraceFiltering",
        "lineNumber": 3
      },
      {
        "columnNumber": 3,
        "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
        "functionName": "foo",
        "lineNumber": 6
      },
      {
        "columnNumber": 1,
        "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
        "functionName": "triggerPacket",
        "lineNumber": 9
      }
    ],
    "workerType": "none",
    "styles": [],
    "category": "webdev"
  }
});

stubPackets.set(`console.time('bar')`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [
      "bar"
    ],
    "columnNumber": 1,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "",
    "level": "time",
    "lineNumber": 2,
    "prefix": "",
    "private": false,
    "timeStamp": 1502884924757,
    "timer": {
      "name": "bar"
    },
    "workerType": "none",
    "styles": [],
    "category": "webdev"
  }
});

stubPackets.set(`timerAlreadyExists`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [
      "bar"
    ],
    "columnNumber": 1,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "",
    "level": "time",
    "lineNumber": 3,
    "prefix": "",
    "private": false,
    "timeStamp": 1502884924758,
    "timer": {
      "error": "timerAlreadyExists",
      "name": "bar"
    },
    "workerType": "none",
    "styles": [],
    "category": "webdev"
  }
});

stubPackets.set(`console.timeEnd('bar')`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [
      "bar"
    ],
    "columnNumber": 1,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "",
    "level": "timeEnd",
    "lineNumber": 4,
    "prefix": "",
    "private": false,
    "timeStamp": 1502884924759,
    "timer": {
      "duration": 1.2149999999999181,
      "name": "bar"
    },
    "workerType": "none",
    "styles": [],
    "category": "webdev"
  }
});

stubPackets.set(`timerDoesntExist`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [
      "bar"
    ],
    "columnNumber": 1,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "",
    "level": "timeEnd",
    "lineNumber": 5,
    "prefix": "",
    "private": false,
    "timeStamp": 1502884924759,
    "timer": {
      "error": "timerDoesntExist",
      "name": "bar"
    },
    "workerType": "none",
    "styles": [],
    "category": "webdev"
  }
});

stubPackets.set(`console.table('bar')`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [
      "bar"
    ],
    "columnNumber": 1,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "",
    "level": "table",
    "lineNumber": 2,
    "prefix": "",
    "private": false,
    "timeStamp": 1502884924801,
    "timer": null,
    "workerType": "none",
    "styles": [],
    "category": "webdev"
  }
});

stubPackets.set(`console.table(['a', 'b', 'c'])`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [
      {
        "type": "object",
        "actor": "server1.conn0.child1/obj39",
        "class": "Array",
        "extensible": true,
        "frozen": false,
        "sealed": false,
        "ownPropertyLength": 4,
        "preview": {
          "kind": "ArrayLike",
          "length": 3,
          "items": [
            "a",
            "b",
            "c"
          ]
        }
      }
    ],
    "columnNumber": 1,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "",
    "level": "table",
    "lineNumber": 2,
    "prefix": "",
    "private": false,
    "timeStamp": 1502884924859,
    "timer": null,
    "workerType": "none",
    "styles": [],
    "category": "webdev"
  }
});

stubPackets.set(`console.group('bar')`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [
      "bar"
    ],
    "columnNumber": 1,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "bar",
    "level": "group",
    "lineNumber": 2,
    "prefix": "",
    "private": false,
    "styles": [],
    "timeStamp": 1502884924863,
    "timer": null,
    "workerType": "none",
    "category": "webdev"
  }
});

stubPackets.set(`console.groupEnd('bar')`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [],
    "columnNumber": 1,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "bar",
    "level": "groupEnd",
    "lineNumber": 3,
    "prefix": "",
    "private": false,
    "timeStamp": 1502884924864,
    "timer": null,
    "workerType": "none",
    "styles": [],
    "category": "webdev"
  }
});

stubPackets.set(`console.groupCollapsed('foo')`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [
      "foo"
    ],
    "columnNumber": 1,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "foo",
    "level": "groupCollapsed",
    "lineNumber": 2,
    "prefix": "",
    "private": false,
    "styles": [],
    "timeStamp": 1502884924870,
    "timer": null,
    "workerType": "none",
    "category": "webdev"
  }
});

stubPackets.set(`console.groupEnd('foo')`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [],
    "columnNumber": 1,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "foo",
    "level": "groupEnd",
    "lineNumber": 3,
    "prefix": "",
    "private": false,
    "timeStamp": 1502884924871,
    "timer": null,
    "workerType": "none",
    "styles": [],
    "category": "webdev"
  }
});

stubPackets.set(`console.group()`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [],
    "columnNumber": 1,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "",
    "level": "group",
    "lineNumber": 2,
    "prefix": "",
    "private": false,
    "styles": [],
    "timeStamp": 1502884924878,
    "timer": null,
    "workerType": "none",
    "category": "webdev"
  }
});

stubPackets.set(`console.groupEnd()`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [],
    "columnNumber": 1,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "",
    "level": "groupEnd",
    "lineNumber": 3,
    "prefix": "",
    "private": false,
    "timeStamp": 1502884924879,
    "timer": null,
    "workerType": "none",
    "styles": [],
    "category": "webdev"
  }
});

stubPackets.set(`console.log(%cfoobar)`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [
      "foo",
      "bar"
    ],
    "columnNumber": 1,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "",
    "level": "log",
    "lineNumber": 2,
    "prefix": "",
    "private": false,
    "styles": [
      "color:blue; font-size:1.3em; background:url('http://example.com/test'); position:absolute; top:10px; ",
      "color:red; line-height: 1.5; background:url('http://example.com/test')"
    ],
    "timeStamp": 1502884924883,
    "timer": null,
    "workerType": "none",
    "category": "webdev"
  }
});

stubPackets.set(`console.log("%cHello%c|%cWorld")`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [
      "Hello",
      "|",
      "World"
    ],
    "columnNumber": 3,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "",
    "level": "log",
    "lineNumber": 2,
    "prefix": "",
    "private": false,
    "styles": [
      "color:red",
      "",
      "color: blue"
    ],
    "timeStamp": 1518681614352,
    "timer": null,
    "workerType": "none",
    "category": "webdev"
  }
});

stubPackets.set(`console.group(%cfoo%cbar)`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [
      "foo",
      "bar"
    ],
    "columnNumber": 1,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "foo bar",
    "level": "group",
    "lineNumber": 2,
    "prefix": "",
    "private": false,
    "styles": [
      "color:blue;font-size:1.3em;background:url('http://example.com/test');position:absolute;top:10px",
      "color:red;background:url('http://example.com/test')"
    ],
    "timeStamp": 1502884924887,
    "timer": null,
    "workerType": "none",
    "category": "webdev"
  }
});

stubPackets.set(`console.groupEnd(%cfoo%cbar)`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [],
    "columnNumber": 1,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "foo bar",
    "level": "groupEnd",
    "lineNumber": 6,
    "prefix": "",
    "private": false,
    "timeStamp": 1502884924887,
    "timer": null,
    "workerType": "none",
    "styles": [],
    "category": "webdev"
  }
});

stubPackets.set(`console.groupCollapsed(%cfoo%cbaz)`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [
      "foo",
      "baz"
    ],
    "columnNumber": 1,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "foo baz",
    "level": "groupCollapsed",
    "lineNumber": 2,
    "prefix": "",
    "private": false,
    "styles": [
      "color:blue;font-size:1.3em;background:url('http://example.com/test');position:absolute;top:10px",
      "color:red;background:url('http://example.com/test')"
    ],
    "timeStamp": 1502884924892,
    "timer": null,
    "workerType": "none",
    "category": "webdev"
  }
});

stubPackets.set(`console.groupEnd(%cfoo%cbaz)`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [],
    "columnNumber": 1,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "foo baz",
    "level": "groupEnd",
    "lineNumber": 6,
    "prefix": "",
    "private": false,
    "timeStamp": 1502884924893,
    "timer": null,
    "workerType": "none",
    "styles": [],
    "category": "webdev"
  }
});

stubPackets.set(`console.dir({C, M, Y, K})`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [
      {
        "type": "object",
        "actor": "server1.conn0.child1/obj36",
        "class": "Object",
        "extensible": true,
        "frozen": false,
        "sealed": false,
        "ownPropertyLength": 4,
        "preview": {
          "kind": "Object",
          "ownProperties": {
            "cyan": {
              "configurable": true,
              "enumerable": true,
              "writable": true,
              "value": "C"
            },
            "magenta": {
              "configurable": true,
              "enumerable": true,
              "writable": true,
              "value": "M"
            },
            "yellow": {
              "configurable": true,
              "enumerable": true,
              "writable": true,
              "value": "Y"
            },
            "black": {
              "configurable": true,
              "enumerable": true,
              "writable": true,
              "value": "K"
            }
          },
          "ownSymbols": [],
          "ownPropertiesLength": 4,
          "ownSymbolsLength": 0,
          "safeGetterValues": {}
        }
      }
    ],
    "columnNumber": 27,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "",
    "level": "dir",
    "lineNumber": 1,
    "prefix": "",
    "private": false,
    "timeStamp": 1502884924899,
    "timer": null,
    "workerType": "none",
    "styles": [],
    "category": "webdev"
  }
});

stubPackets.set(`console.count | default: 1`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [
      "default"
    ],
    "columnNumber": 5,
    "counter": {
      "count": 1,
      "label": "default"
    },
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "",
    "level": "count",
    "lineNumber": 2,
    "prefix": "",
    "private": false,
    "timeStamp": 1511365913333,
    "timer": null,
    "workerType": "none",
    "styles": [],
    "category": "webdev"
  }
});

stubPackets.set(`console.count | default: 2`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [
      "default"
    ],
    "columnNumber": 5,
    "counter": {
      "count": 2,
      "label": "default"
    },
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "",
    "level": "count",
    "lineNumber": 3,
    "prefix": "",
    "private": false,
    "timeStamp": 1511365913334,
    "timer": null,
    "workerType": "none",
    "styles": [],
    "category": "webdev"
  }
});

stubPackets.set(`console.count | test counter: 1`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [
      "test counter"
    ],
    "columnNumber": 5,
    "counter": {
      "count": 1,
      "label": "test counter"
    },
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "",
    "level": "count",
    "lineNumber": 4,
    "prefix": "",
    "private": false,
    "timeStamp": 1511365913334,
    "timer": null,
    "workerType": "none",
    "styles": [],
    "category": "webdev"
  }
});

stubPackets.set(`console.count | test counter: 2`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [
      "test counter"
    ],
    "columnNumber": 5,
    "counter": {
      "count": 2,
      "label": "test counter"
    },
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "",
    "level": "count",
    "lineNumber": 5,
    "prefix": "",
    "private": false,
    "timeStamp": 1511365913334,
    "timer": null,
    "workerType": "none",
    "styles": [],
    "category": "webdev"
  }
});

stubPackets.set(`console.count | default: 3`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [
      "default"
    ],
    "columnNumber": 5,
    "counter": {
      "count": 3,
      "label": "default"
    },
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "",
    "level": "count",
    "lineNumber": 6,
    "prefix": "",
    "private": false,
    "timeStamp": 1511365913334,
    "timer": null,
    "workerType": "none",
    "styles": [],
    "category": "webdev"
  }
});

stubPackets.set(`console.count | clear`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [],
    "columnNumber": 5,
    "counter": null,
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "",
    "level": "clear",
    "lineNumber": 7,
    "prefix": "",
    "private": false,
    "timeStamp": 1511365913334,
    "timer": null,
    "workerType": "none",
    "styles": [],
    "category": "webdev"
  }
});

stubPackets.set(`console.count | default: 4`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [
      "default"
    ],
    "columnNumber": 5,
    "counter": {
      "count": 4,
      "label": "default"
    },
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "",
    "level": "count",
    "lineNumber": 8,
    "prefix": "",
    "private": false,
    "timeStamp": 1511365913335,
    "timer": null,
    "workerType": "none",
    "styles": [],
    "category": "webdev"
  }
});

stubPackets.set(`console.count | test counter: 3`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "consoleAPICall",
  "message": {
    "addonId": "",
    "arguments": [
      "test counter"
    ],
    "columnNumber": 5,
    "counter": {
      "count": 3,
      "label": "test counter"
    },
    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "functionName": "triggerPacket",
    "groupName": "",
    "level": "count",
    "lineNumber": 9,
    "prefix": "",
    "private": false,
    "timeStamp": 1511365913335,
    "timer": null,
    "workerType": "none",
    "styles": [],
    "category": "webdev"
  }
});

module.exports = {
  stubPreparedMessages,
  stubPackets,
};
PK
!<<�tB��gchrome/devtools/modules/devtools/client/webconsole/new-console-output/test/fixtures/stubs/cssMessage.js/* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable max-len */

"use strict";

/*
 * THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND. RUN TESTS IN FIXTURES/ TO UPDATE.
 */

const { ConsoleMessage } =
  require("devtools/client/webconsole/new-console-output/types");

let stubPreparedMessages = new Map();
let stubPackets = new Map();
stubPreparedMessages.set(`Unknown property ‘such-unknown-property’.  Declaration dropped.`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "css",
  "timeStamp": 1479159920406,
  "type": "log",
  "helperType": null,
  "level": "warn",
  "messageText": "Unknown property ‘such-unknown-property’.  Declaration dropped.",
  "parameters": null,
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-css-message.html\",\"line\":3,\"column\":25},\"groupId\":null,\"indent\":0,\"level\":\"warn\",\"messageText\":\"Unknown property ‘such-unknown-property’.  Declaration dropped.\",\"parameters\":null,\"source\":\"css\",\"type\":\"log\",\"userProvidedStyles\":null,\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-css-message.html",
    "line": 3,
    "column": 25
  },
  "groupId": null,
  "userProvidedStyles": null,
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`Error in parsing value for ‘padding-top’.  Declaration dropped.`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "css",
  "timeStamp": 1479159920465,
  "type": "log",
  "helperType": null,
  "level": "warn",
  "messageText": "Error in parsing value for ‘padding-top’.  Declaration dropped.",
  "parameters": null,
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-css-message.html\",\"line\":3,\"column\":16},\"groupId\":null,\"indent\":0,\"level\":\"warn\",\"messageText\":\"Error in parsing value for ‘padding-top’.  Declaration dropped.\",\"parameters\":null,\"source\":\"css\",\"type\":\"log\",\"userProvidedStyles\":null,\"private\":false}",
  "stacktrace": null,
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-css-message.html",
    "line": 3,
    "column": 16
  },
  "groupId": null,
  "userProvidedStyles": null,
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPackets.set(`Unknown property ‘such-unknown-property’.  Declaration dropped.`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "pageError",
  "pageError": {
    "errorMessage": "Unknown property ‘such-unknown-property’.  Declaration dropped.",
    "errorMessageName": "",
    "sourceName": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-css-message.html",
    "lineText": "",
    "lineNumber": 3,
    "columnNumber": 25,
    "category": "CSS Parser",
    "timeStamp": 1479159920406,
    "warning": true,
    "error": false,
    "exception": false,
    "strict": false,
    "info": false,
    "private": false,
    "stacktrace": null,
    "notes": null
  }
});

stubPackets.set(`Error in parsing value for ‘padding-top’.  Declaration dropped.`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "pageError",
  "pageError": {
    "errorMessage": "Error in parsing value for ‘padding-top’.  Declaration dropped.",
    "errorMessageName": "",
    "sourceName": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-css-message.html",
    "lineText": "",
    "lineNumber": 3,
    "columnNumber": 16,
    "category": "CSS Parser",
    "timeStamp": 1479159920465,
    "warning": true,
    "error": false,
    "exception": false,
    "strict": false,
    "info": false,
    "private": false,
    "stacktrace": null,
    "notes": null
  }
});

module.exports = {
  stubPreparedMessages,
  stubPackets,
};
PK
!<�z�GGmchrome/devtools/modules/devtools/client/webconsole/new-console-output/test/fixtures/stubs/evaluationResult.js/* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable max-len */

"use strict";

/*
 * THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND. RUN TESTS IN FIXTURES/ TO UPDATE.
 */

const { ConsoleMessage } =
  require("devtools/client/webconsole/new-console-output/types");

let stubPreparedMessages = new Map();
let stubPackets = new Map();
stubPreparedMessages.set(`new Date(0)`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "javascript",
  "timeStamp": 1479159921364,
  "type": "result",
  "helperType": null,
  "level": "log",
  "parameters": [
    {
      "type": "object",
      "actor": "server1.conn0.child1/obj30",
      "class": "Date",
      "extensible": true,
      "frozen": false,
      "sealed": false,
      "ownPropertyLength": 0,
      "preview": {
        "timestamp": 0
      }
    }
  ],
  "repeatId": "{\"frame\":null,\"groupId\":null,\"indent\":0,\"level\":\"log\",\"parameters\":[{\"type\":\"object\",\"actor\":\"server1.conn0.child1/obj30\",\"class\":\"Date\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":0,\"preview\":{\"timestamp\":0}}],\"source\":\"javascript\",\"type\":\"result\",\"userProvidedStyles\":null}",
  "stacktrace": null,
  "frame": null,
  "groupId": null,
  "userProvidedStyles": null,
  "notes": null,
  "indent": 0,
  "prefix": ""
}));

stubPreparedMessages.set(`asdf()`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "javascript",
  "timeStamp": 1479159921377,
  "type": "result",
  "helperType": null,
  "level": "error",
  "messageText": "ReferenceError: asdf is not defined",
  "parameters": [
    {
      "type": "undefined"
    }
  ],
  "repeatId": "{\"frame\":{\"source\":\"debugger eval code\",\"line\":1,\"column\":1},\"groupId\":null,\"indent\":0,\"level\":\"error\",\"messageText\":\"ReferenceError: asdf is not defined\",\"parameters\":[{\"type\":\"undefined\"}],\"source\":\"javascript\",\"type\":\"result\",\"userProvidedStyles\":null}",
  "stacktrace": null,
  "frame": {
    "source": "debugger eval code",
    "line": 1,
    "column": 1
  },
  "groupId": null,
  "exceptionDocURL": "https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Not_defined?utm_source=mozilla&utm_medium=firefox-console-errors&utm_campaign=default",
  "userProvidedStyles": null,
  "notes": null,
  "indent": 0,
  "prefix": ""
}));

stubPreparedMessages.set(`1 + @`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "javascript",
  "timeStamp": 1479159921399,
  "type": "result",
  "helperType": null,
  "level": "error",
  "messageText": "SyntaxError: illegal character",
  "parameters": [
    {
      "type": "undefined"
    }
  ],
  "repeatId": "{\"frame\":{\"source\":\"debugger eval code\",\"line\":1,\"column\":4},\"groupId\":null,\"indent\":0,\"level\":\"error\",\"messageText\":\"SyntaxError: illegal character\",\"parameters\":[{\"type\":\"undefined\"}],\"source\":\"javascript\",\"type\":\"result\",\"userProvidedStyles\":null}",
  "stacktrace": null,
  "frame": {
    "source": "debugger eval code",
    "line": 1,
    "column": 4
  },
  "groupId": null,
  "exceptionDocURL": "https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Illegal_character?utm_source=mozilla&utm_medium=firefox-console-errors&utm_campaign=default",
  "userProvidedStyles": null,
  "notes": null,
  "indent": 0,
  "prefix": ""
}));

stubPreparedMessages.set(`inspect({a: 1})`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "javascript",
  "timeStamp": 1499776070751,
  "type": "result",
  "helperType": "inspectObject",
  "level": "log",
  "parameters": [
    {
      "type": "object",
      "actor": "server1.conn0.child1/obj35",
      "class": "Object",
      "extensible": true,
      "frozen": false,
      "sealed": false,
      "ownPropertyLength": 1,
      "preview": {
        "kind": "Object",
        "ownProperties": {
          "a": {
            "configurable": true,
            "enumerable": true,
            "writable": true,
            "value": 1
          }
        },
        "ownSymbols": [],
        "ownPropertiesLength": 1,
        "ownSymbolsLength": 0,
        "safeGetterValues": {}
      }
    }
  ],
  "repeatId": "{\"frame\":null,\"groupId\":null,\"indent\":0,\"level\":\"log\",\"parameters\":[{\"type\":\"object\",\"actor\":\"server1.conn0.child1/obj35\",\"class\":\"Object\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":1,\"preview\":{\"kind\":\"Object\",\"ownProperties\":{\"a\":{\"configurable\":true,\"enumerable\":true,\"writable\":true,\"value\":1}},\"ownSymbols\":[],\"ownPropertiesLength\":1,\"ownSymbolsLength\":0,\"safeGetterValues\":{}}}],\"source\":\"javascript\",\"type\":\"result\",\"userProvidedStyles\":null}",
  "stacktrace": null,
  "frame": null,
  "groupId": null,
  "userProvidedStyles": null,
  "notes": null,
  "indent": 0,
  "prefix": ""
}));

stubPreparedMessages.set(`cd(document)`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "javascript",
  "timeStamp": 1510650094657,
  "type": "result",
  "helperType": "error",
  "level": "error",
  "messageText": "Cannot cd() to the given window. Invalid argument.",
  "parameters": [
    {
      "type": "undefined"
    }
  ],
  "repeatId": "{\"frame\":null,\"groupId\":null,\"indent\":0,\"level\":\"error\",\"messageText\":\"Cannot cd() to the given window. Invalid argument.\",\"parameters\":[{\"type\":\"undefined\"}],\"source\":\"javascript\",\"type\":\"result\",\"userProvidedStyles\":null}",
  "stacktrace": null,
  "frame": null,
  "groupId": null,
  "userProvidedStyles": null,
  "notes": null,
  "indent": 0,
  "prefix": ""
}));

stubPreparedMessages.set(`undefined`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "javascript",
  "timeStamp": 1518606917356,
  "type": "result",
  "helperType": null,
  "level": "log",
  "parameters": [
    {
      "type": "undefined"
    }
  ],
  "repeatId": "{\"frame\":null,\"groupId\":null,\"indent\":0,\"level\":\"log\",\"parameters\":[{\"type\":\"undefined\"}],\"source\":\"javascript\",\"type\":\"result\",\"userProvidedStyles\":null}",
  "stacktrace": null,
  "frame": null,
  "groupId": null,
  "userProvidedStyles": null,
  "notes": null,
  "indent": 0,
  "prefix": ""
}));

stubPreparedMessages.set(`longString message Error`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "javascript",
  "timeStamp": 1493108241073,
  "type": "result",
  "helperType": null,
  "level": "error",
  "messageText": {
    "type": "longString",
    "initial": "Error: Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Lon",
    "length": 110007,
    "actor": "server1.conn0.child1/longString37"
  },
  "parameters": [
    {
      "type": "undefined"
    }
  ],
  "repeatId": "{\"frame\":null,\"groupId\":null,\"indent\":0,\"level\":\"error\",\"messageText\":{\"type\":\"longString\",\"initial\":\"Error: Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Lon\",\"length\":110007,\"actor\":\"server1.conn0.child1/longString37\"},\"parameters\":[{\"type\":\"undefined\"}],\"source\":\"javascript\",\"type\":\"result\",\"userProvidedStyles\":null}",
  "stacktrace": null,
  "frame": null,
  "groupId": null,
  "userProvidedStyles": null,
  "notes": null,
  "indent": 0,
  "prefix": ""
}));

stubPreparedMessages.set(`eval throw ""`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "javascript",
  "timeStamp": 1517990289517,
  "type": "result",
  "helperType": null,
  "level": "error",
  "messageText": "Error",
  "parameters": [
    {
      "type": "undefined"
    }
  ],
  "repeatId": "{\"frame\":null,\"groupId\":null,\"indent\":0,\"level\":\"error\",\"messageText\":\"Error\",\"parameters\":[{\"type\":\"undefined\"}],\"source\":\"javascript\",\"type\":\"result\",\"userProvidedStyles\":null}",
  "stacktrace": null,
  "frame": null,
  "groupId": null,
  "userProvidedStyles": null,
  "notes": null,
  "indent": 0,
  "prefix": ""
}));

stubPreparedMessages.set(`eval throw "tomato"`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "javascript",
  "timeStamp": 1517990289520,
  "type": "result",
  "helperType": null,
  "level": "error",
  "messageText": "Error: tomato",
  "parameters": [
    {
      "type": "undefined"
    }
  ],
  "repeatId": "{\"frame\":null,\"groupId\":null,\"indent\":0,\"level\":\"error\",\"messageText\":\"Error: tomato\",\"parameters\":[{\"type\":\"undefined\"}],\"source\":\"javascript\",\"type\":\"result\",\"userProvidedStyles\":null}",
  "stacktrace": null,
  "frame": null,
  "groupId": null,
  "userProvidedStyles": null,
  "notes": null,
  "indent": 0,
  "prefix": ""
}));

stubPackets.set(`new Date(0)`, {
  "from": "server1.conn0.child1/consoleActor2",
  "input": "new Date(0)",
  "result": {
    "type": "object",
    "actor": "server1.conn0.child1/obj30",
    "class": "Date",
    "extensible": true,
    "frozen": false,
    "sealed": false,
    "ownPropertyLength": 0,
    "preview": {
      "timestamp": 0
    }
  },
  "timestamp": 1479159921364,
  "exception": null,
  "frame": null,
  "helperResult": null,
  "notes": null
});

stubPackets.set(`asdf()`, {
  "from": "server1.conn0.child1/consoleActor2",
  "input": "asdf()",
  "result": {
    "type": "undefined"
  },
  "timestamp": 1479159921377,
  "exception": {
    "type": "object",
    "actor": "server1.conn0.child1/obj32",
    "class": "Error",
    "extensible": true,
    "frozen": false,
    "sealed": false,
    "ownPropertyLength": 4,
    "preview": {
      "kind": "Error",
      "name": "ReferenceError",
      "message": "asdf is not defined",
      "stack": "@debugger eval code:1:1\n",
      "fileName": "debugger eval code",
      "lineNumber": 1,
      "columnNumber": 1
    }
  },
  "exceptionMessage": "ReferenceError: asdf is not defined",
  "exceptionDocURL": "https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Not_defined?utm_source=mozilla&utm_medium=firefox-console-errors&utm_campaign=default",
  "frame": {
    "source": "debugger eval code",
    "line": 1,
    "column": 1
  },
  "helperResult": null,
  "notes": null
});

stubPackets.set(`1 + @`, {
  "from": "server1.conn0.child1/consoleActor2",
  "input": "1 + @",
  "result": {
    "type": "undefined"
  },
  "timestamp": 1479159921399,
  "exception": {
    "type": "object",
    "actor": "server1.conn0.child1/obj33",
    "class": "Error",
    "extensible": true,
    "frozen": false,
    "sealed": false,
    "ownPropertyLength": 4,
    "preview": {
      "kind": "Error",
      "name": "SyntaxError",
      "message": "illegal character",
      "stack": "",
      "fileName": "debugger eval code",
      "lineNumber": 1,
      "columnNumber": 4
    }
  },
  "exceptionMessage": "SyntaxError: illegal character",
  "exceptionDocURL": "https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Illegal_character?utm_source=mozilla&utm_medium=firefox-console-errors&utm_campaign=default",
  "frame": {
    "source": "debugger eval code",
    "line": 1,
    "column": 4
  },
  "helperResult": null,
  "notes": null
});

stubPackets.set(`inspect({a: 1})`, {
  "from": "server1.conn0.child1/consoleActor2",
  "input": "inspect({a: 1})",
  "result": {
    "type": "undefined"
  },
  "timestamp": 1499776070751,
  "exception": null,
  "frame": null,
  "helperResult": {
    "type": "inspectObject",
    "input": "inspect({a: 1})",
    "object": {
      "type": "object",
      "actor": "server1.conn0.child1/obj35",
      "class": "Object",
      "extensible": true,
      "frozen": false,
      "sealed": false,
      "ownPropertyLength": 1,
      "preview": {
        "kind": "Object",
        "ownProperties": {
          "a": {
            "configurable": true,
            "enumerable": true,
            "writable": true,
            "value": 1
          }
        },
        "ownSymbols": [],
        "ownPropertiesLength": 1,
        "ownSymbolsLength": 0,
        "safeGetterValues": {}
      }
    }
  },
  "notes": null
});

stubPackets.set(`cd(document)`, {
  "from": "server1.conn0.child1/consoleActor2",
  "input": "cd(document)",
  "result": {
    "type": "undefined"
  },
  "timestamp": 1510650094657,
  "exception": null,
  "frame": null,
  "helperResult": {
    "type": "error",
    "message": "cdFunctionInvalidArgument"
  },
  "notes": null
});

stubPackets.set(`undefined`, {
  "from": "server1.conn0.child1/consoleActor2",
  "input": "undefined",
  "result": {
    "type": "undefined"
  },
  "timestamp": 1518606917356,
  "exception": null,
  "frame": null,
  "helperResult": null,
  "notes": null
});

stubPackets.set(`longString message Error`, {
  "from": "server1.conn0.child1/consoleActor2",
  "input": "throw new Error(\"Long error \".repeat(10000))",
  "result": {
    "type": "undefined"
  },
  "timestamp": 1493108241073,
  "exception": {
    "type": "object",
    "actor": "server1.conn0.child1/obj35",
    "class": "Error",
    "extensible": true,
    "frozen": false,
    "sealed": false,
    "ownPropertyLength": 4,
    "preview": {
      "kind": "Error",
      "name": "Error",
      "message": {
        "type": "longString",
        "initial": "Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error",
        "length": 110000,
        "actor": "server1.conn0.child1/longString36"
      },
      "stack": "@debugger eval code:1:7\n",
      "fileName": "debugger eval code",
      "lineNumber": 1,
      "columnNumber": 7
    }
  },
  "exceptionMessage": {
    "type": "longString",
    "initial": "Error: Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Lon",
    "length": 110007,
    "actor": "server1.conn0.child1/longString37"
  },
  "frame": null,
  "helperResult": null,
  "notes": null
});

stubPackets.set(`eval throw ""`, {
  "from": "server1.conn0.child1/consoleActor2",
  "input": "throw \"\"",
  "result": {
    "type": "undefined"
  },
  "timestamp": 1517990289517,
  "exception": "",
  "exceptionMessage": "",
  "frame": null,
  "helperResult": null,
  "notes": null
});

stubPackets.set(`eval throw "tomato"`, {
  "from": "server1.conn0.child1/consoleActor2",
  "input": "throw \"tomato\"",
  "result": {
    "type": "undefined"
  },
  "timestamp": 1517990289520,
  "exception": "tomato",
  "exceptionMessage": "tomato",
  "frame": null,
  "helperResult": null,
  "notes": null
});

module.exports = {
  stubPreparedMessages,
  stubPackets,
};
PK
!<'WWbchrome/devtools/modules/devtools/client/webconsole/new-console-output/test/fixtures/stubs/index.js/* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

let maps = [];

[
  "consoleApi",
  "cssMessage",
  "evaluationResult",
  "networkEvent",
  "pageError",
].forEach((filename) => {
  maps[filename] = require(`./${filename}`);
});

// Combine all the maps into a single map.
module.exports = {
  stubPreparedMessages: new Map([
    ...maps.consoleApi.stubPreparedMessages,
    ...maps.cssMessage.stubPreparedMessages,
    ...maps.evaluationResult.stubPreparedMessages,
    ...maps.networkEvent.stubPreparedMessages,
    ...maps.pageError.stubPreparedMessages, ]),
  stubPackets: new Map([
    ...maps.consoleApi.stubPackets,
    ...maps.cssMessage.stubPackets,
    ...maps.evaluationResult.stubPackets,
    ...maps.networkEvent.stubPackets,
    ...maps.pageError.stubPackets, ]),
};
PK
!<���1�1ichrome/devtools/modules/devtools/client/webconsole/new-console-output/test/fixtures/stubs/networkEvent.js/* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable max-len */

"use strict";

/*
 * THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND. RUN TESTS IN FIXTURES/ TO UPDATE.
 */

const { NetworkEventMessage } =
  require("devtools/client/webconsole/new-console-output/types");

let stubPreparedMessages = new Map();
let stubPackets = new Map();
stubPreparedMessages.set("GET request", new NetworkEventMessage({
  "id": "1",
  "actor": "server1.conn0.child1/netEvent30",
  "level": "log",
  "isXHR": false,
  "request": {
    "url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
    "method": "GET"
  },
  "response": {},
  "source": "network",
  "type": "log",
  "groupId": null,
  "timeStamp": 1487022056850,
  "indent": 0,
  "updates": [],
  "openedOnce": false,
  "securityState": null,
  "securityInfo": null,
  "requestHeadersFromUploadStream": null,
  "private": false,
  "url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
  "urlDetails": {
    "baseNameWithQuery": "inexistent.html",
    "host": "example.com",
    "scheme": "http",
    "unicodeUrl": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
    "isLocal": null
  },
  "method": "GET",
  "cause": {
    "type": "img",
    "loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
    "stacktraceAvailable": true
  }
}));

stubPreparedMessages.set("GET request update", new NetworkEventMessage({
  "id": "1",
  "actor": "server1.conn0.child1/netEvent30",
  "level": "log",
  "request": {
    "url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
    "method": "GET",
    "headersSize": 489
  },
  "response": {
    "httpVersion": "HTTP/1.1",
    "status": "404",
    "statusText": "Not Found",
    "headersSize": 160,
    "remoteAddress": "127.0.0.1",
    "remotePort": 8888,
    "content": {
      "mimeType": "text/html; charset=utf-8"
    },
    "bodySize": 904,
    "transferredSize": 904
  },
  "source": "network",
  "type": "log",
  "groupId": null,
  "totalTime": 16,
  "indent": 0,
  "openedOnce": false,
  "securityState": null,
  "securityInfo": null,
  "requestHeadersFromUploadStream": null,
  "url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
  "urlDetails": {
    "baseNameWithQuery": "inexistent.html",
    "host": "example.com",
    "scheme": "http",
    "unicodeUrl": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
    "isLocal": null
  },
  "method": "GET"
}));

stubPreparedMessages.set("XHR GET request", new NetworkEventMessage({
  "id": "1",
  "actor": "server1.conn1.child1/netEvent30",
  "level": "log",
  "isXHR": true,
  "request": {
    "url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
    "method": "GET"
  },
  "response": {},
  "source": "network",
  "type": "log",
  "groupId": null,
  "timeStamp": 1487022057746,
  "indent": 0,
  "updates": [],
  "openedOnce": false,
  "securityState": null,
  "securityInfo": null,
  "requestHeadersFromUploadStream": null,
  "private": false,
  "url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
  "urlDetails": {
    "baseNameWithQuery": "inexistent.html",
    "host": "example.com",
    "scheme": "http",
    "unicodeUrl": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
    "isLocal": null
  },
  "method": "GET",
  "cause": {
    "type": "xhr",
    "loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
    "stacktraceAvailable": true
  }
}));

stubPreparedMessages.set("XHR GET request update", new NetworkEventMessage({
  "id": "1",
  "actor": "server1.conn0.child1/netEvent31",
  "level": "log",
  "request": {
    "url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
    "method": "GET",
    "headersSize": 489
  },
  "response": {
    "httpVersion": "HTTP/1.1",
    "status": "404",
    "statusText": "Not Found",
    "headersSize": 160,
    "remoteAddress": "127.0.0.1",
    "remotePort": 8888,
    "content": {
      "mimeType": "text/html; charset=utf-8"
    },
    "bodySize": 904,
    "transferredSize": 904
  },
  "source": "network",
  "type": "log",
  "groupId": null,
  "totalTime": 16,
  "indent": 0,
  "openedOnce": false,
  "securityState": null,
  "securityInfo": null,
  "requestHeadersFromUploadStream": null,
  "url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
  "urlDetails": {
    "baseNameWithQuery": "inexistent.html",
    "host": "example.com",
    "scheme": "http",
    "unicodeUrl": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
    "isLocal": null
  },
  "method": "GET"
}));

stubPreparedMessages.set("XHR POST request", new NetworkEventMessage({
  "id": "1",
  "actor": "server1.conn2.child1/netEvent30",
  "level": "log",
  "isXHR": true,
  "request": {
    "url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
    "method": "POST"
  },
  "response": {},
  "source": "network",
  "type": "log",
  "groupId": null,
  "timeStamp": 1487022058414,
  "indent": 0,
  "updates": [],
  "openedOnce": false,
  "securityState": null,
  "securityInfo": null,
  "requestHeadersFromUploadStream": null,
  "private": false,
  "url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
  "urlDetails": {
    "baseNameWithQuery": "inexistent.html",
    "host": "example.com",
    "scheme": "http",
    "unicodeUrl": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
    "isLocal": null
  },
  "method": "POST",
  "cause": {
    "type": "xhr",
    "loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
    "stacktraceAvailable": true
  }
}));

stubPreparedMessages.set("XHR POST request update", new NetworkEventMessage({
  "id": "1",
  "actor": "server1.conn0.child1/netEvent32",
  "level": "log",
  "request": {
    "url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
    "method": "POST",
    "headersSize": 509
  },
  "response": {
    "httpVersion": "HTTP/1.1",
    "status": "404",
    "statusText": "Not Found",
    "headersSize": 160,
    "remoteAddress": "127.0.0.1",
    "remotePort": 8888,
    "content": {
      "mimeType": "text/html; charset=utf-8"
    },
    "bodySize": 904,
    "transferredSize": 904
  },
  "source": "network",
  "type": "log",
  "groupId": null,
  "totalTime": 10,
  "indent": 0,
  "openedOnce": false,
  "securityState": null,
  "securityInfo": null,
  "requestHeadersFromUploadStream": null,
  "url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
  "urlDetails": {
    "baseNameWithQuery": "inexistent.html",
    "host": "example.com",
    "scheme": "http",
    "unicodeUrl": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
    "isLocal": null
  },
  "method": "POST"
}));

stubPackets.set(`GET request`, {
  "_type": "NetworkEvent",
  "timeStamp": 1487022056850,
  "node": null,
  "actor": "server1.conn0.child1/netEvent30",
  "discardRequestBody": true,
  "discardResponseBody": true,
  "startedDateTime": "2017-02-13T21:40:56.850Z",
  "request": {
    "url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
    "method": "GET"
  },
  "isXHR": false,
  "cause": {
    "type": "img",
    "loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
    "stacktraceAvailable": true
  },
  "response": {},
  "timings": {},
  "updates": [],
  "private": false,
  "from": "server1.conn0.child1/consoleActor2"
});

stubPackets.set(`GET request update`, {
  "networkInfo": {
    "_type": "NetworkEvent",
    "actor": "server1.conn0.child1/netEvent30",
    "request": {
      "url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
      "method": "GET",
      "headersSize": 489
    },
    "response": {
      "httpVersion": "HTTP/1.1",
      "status": "404",
      "statusText": "Not Found",
      "headersSize": 160,
      "remoteAddress": "127.0.0.1",
      "remotePort": 8888,
      "content": {
        "mimeType": "text/html; charset=utf-8"
      },
      "bodySize": 904,
      "transferredSize": 904
    },
    "totalTime": 16
  }
});

stubPackets.set(`XHR GET request`, {
  "_type": "NetworkEvent",
  "timeStamp": 1487022057746,
  "node": null,
  "actor": "server1.conn1.child1/netEvent30",
  "discardRequestBody": true,
  "discardResponseBody": true,
  "startedDateTime": "2017-02-13T21:40:57.746Z",
  "request": {
    "url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
    "method": "GET"
  },
  "isXHR": true,
  "cause": {
    "type": "xhr",
    "loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
    "stacktraceAvailable": true
  },
  "response": {},
  "timings": {},
  "updates": [],
  "private": false,
  "from": "server1.conn1.child1/consoleActor2"
});

stubPackets.set(`XHR GET request update`, {
  "networkInfo": {
    "_type": "NetworkEvent",
    "actor": "server1.conn0.child1/netEvent31",
    "request": {
      "url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
      "method": "GET",
      "headersSize": 489
    },
    "response": {
      "httpVersion": "HTTP/1.1",
      "status": "404",
      "statusText": "Not Found",
      "headersSize": 160,
      "remoteAddress": "127.0.0.1",
      "remotePort": 8888,
      "content": {
        "mimeType": "text/html; charset=utf-8"
      },
      "bodySize": 904,
      "transferredSize": 904
    },
    "totalTime": 16
  }
});

stubPackets.set(`XHR POST request`, {
  "_type": "NetworkEvent",
  "timeStamp": 1487022058414,
  "node": null,
  "actor": "server1.conn2.child1/netEvent30",
  "discardRequestBody": true,
  "discardResponseBody": true,
  "startedDateTime": "2017-02-13T21:40:58.414Z",
  "request": {
    "url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
    "method": "POST"
  },
  "isXHR": true,
  "cause": {
    "type": "xhr",
    "loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
    "stacktraceAvailable": true
  },
  "response": {},
  "timings": {},
  "updates": [],
  "private": false,
  "from": "server1.conn2.child1/consoleActor2"
});

stubPackets.set(`XHR POST request update`, {
  "networkInfo": {
    "_type": "NetworkEvent",
    "actor": "server1.conn0.child1/netEvent32",
    "request": {
      "url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
      "method": "POST",
      "headersSize": 509
    },
    "response": {
      "httpVersion": "HTTP/1.1",
      "status": "404",
      "statusText": "Not Found",
      "headersSize": 160,
      "remoteAddress": "127.0.0.1",
      "remotePort": 8888,
      "content": {
        "mimeType": "text/html; charset=utf-8"
      },
      "bodySize": 904,
      "transferredSize": 904
    },
    "totalTime": 10
  }
});

module.exports = {
  stubPreparedMessages,
  stubPackets,
};
PK
!<���?�?fchrome/devtools/modules/devtools/client/webconsole/new-console-output/test/fixtures/stubs/pageError.js/* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable max-len */

"use strict";

/*
 * THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND. RUN TESTS IN FIXTURES/ TO UPDATE.
 */

const { ConsoleMessage } =
  require("devtools/client/webconsole/new-console-output/types");

let stubPreparedMessages = new Map();
let stubPackets = new Map();
stubPreparedMessages.set(`ReferenceError: asdf is not defined`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "javascript",
  "timeStamp": 1476573167137,
  "type": "log",
  "helperType": null,
  "level": "error",
  "messageText": "ReferenceError: asdf is not defined",
  "parameters": null,
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":3,\"column\":5},\"groupId\":null,\"indent\":0,\"level\":\"error\",\"messageText\":\"ReferenceError: asdf is not defined\",\"parameters\":null,\"source\":\"javascript\",\"type\":\"log\",\"userProvidedStyles\":null,\"private\":false}",
  "stacktrace": [
    {
      "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
      "lineNumber": 3,
      "columnNumber": 5,
      "functionName": "bar"
    },
    {
      "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
      "lineNumber": 6,
      "columnNumber": 5,
      "functionName": "foo"
    },
    {
      "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
      "lineNumber": 9,
      "columnNumber": 3,
      "functionName": null
    },
    {
      "filename": "resource://testing-common/content-task.js line 50 > eval",
      "lineNumber": 7,
      "columnNumber": 9,
      "functionName": null
    },
    {
      "filename": "resource://testing-common/content-task.js",
      "lineNumber": 51,
      "columnNumber": 20,
      "functionName": null
    }
  ],
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 3,
    "column": 5
  },
  "groupId": null,
  "exceptionDocURL": "https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Not_defined?utm_source=mozilla&utm_medium=firefox-console-errors&utm_campaign=default",
  "userProvidedStyles": null,
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`SyntaxError: redeclaration of let a`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "javascript",
  "timeStamp": 1487992945524,
  "type": "log",
  "helperType": null,
  "level": "error",
  "messageText": "SyntaxError: redeclaration of let a",
  "parameters": null,
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":9},\"groupId\":null,\"indent\":0,\"level\":\"error\",\"messageText\":\"SyntaxError: redeclaration of let a\",\"parameters\":null,\"source\":\"javascript\",\"type\":\"log\",\"userProvidedStyles\":null,\"private\":false}",
  "stacktrace": [
    {
      "filename": "resource://testing-common/content-task.js line 50 > eval",
      "lineNumber": 7,
      "columnNumber": 9,
      "functionName": null
    },
    {
      "filename": "resource://testing-common/content-task.js",
      "lineNumber": 51,
      "columnNumber": 20,
      "functionName": null
    }
  ],
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 2,
    "column": 9
  },
  "groupId": null,
  "userProvidedStyles": null,
  "notes": [
    {
      "messageBody": "Previously declared at line 2, column 6",
      "frame": {
        "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
        "line": 2,
        "column": 6
      }
    }
  ],
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`TypeError longString message`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "javascript",
  "timeStamp": 1493109507061,
  "type": "log",
  "helperType": null,
  "level": "error",
  "messageText": {
    "type": "longString",
    "initial": "Error: Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Lon",
    "length": 110007,
    "actor": "server1.conn0.child1/longString30"
  },
  "parameters": null,
  "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":7},\"groupId\":null,\"indent\":0,\"level\":\"error\",\"messageText\":{\"type\":\"longString\",\"initial\":\"Error: Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Lon\",\"length\":110007,\"actor\":\"server1.conn0.child1/longString30\"},\"parameters\":null,\"source\":\"javascript\",\"type\":\"log\",\"userProvidedStyles\":null,\"private\":false}",
  "stacktrace": [
    {
      "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
      "lineNumber": 1,
      "columnNumber": 7,
      "functionName": null
    },
    {
      "filename": "resource://testing-common/content-task.js line 50 > eval",
      "lineNumber": 7,
      "columnNumber": 9,
      "functionName": null
    },
    {
      "filename": "resource://testing-common/content-task.js",
      "lineNumber": 51,
      "columnNumber": 20,
      "functionName": null
    }
  ],
  "frame": {
    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "line": 1,
    "column": 7
  },
  "groupId": null,
  "userProvidedStyles": null,
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`throw ""`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "javascript",
  "timeStamp": 1517942398629,
  "type": "log",
  "helperType": null,
  "level": "error",
  "messageText": "uncaught exception: ",
  "parameters": null,
  "repeatId": "{\"frame\":null,\"groupId\":null,\"indent\":0,\"level\":\"error\",\"messageText\":\"uncaught exception: \",\"parameters\":null,\"source\":\"javascript\",\"type\":\"log\",\"userProvidedStyles\":null,\"private\":false}",
  "stacktrace": null,
  "frame": null,
  "groupId": null,
  "userProvidedStyles": null,
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPreparedMessages.set(`throw "tomato"`, new ConsoleMessage({
  "id": "1",
  "allowRepeating": true,
  "source": "javascript",
  "timeStamp": 1517942398637,
  "type": "log",
  "helperType": null,
  "level": "error",
  "messageText": "uncaught exception: tomato",
  "parameters": null,
  "repeatId": "{\"frame\":null,\"groupId\":null,\"indent\":0,\"level\":\"error\",\"messageText\":\"uncaught exception: tomato\",\"parameters\":null,\"source\":\"javascript\",\"type\":\"log\",\"userProvidedStyles\":null,\"private\":false}",
  "stacktrace": null,
  "frame": null,
  "groupId": null,
  "userProvidedStyles": null,
  "notes": null,
  "indent": 0,
  "prefix": "",
  "private": false
}));

stubPackets.set(`ReferenceError: asdf is not defined`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "pageError",
  "pageError": {
    "errorMessage": "ReferenceError: asdf is not defined",
    "errorMessageName": "JSMSG_NOT_DEFINED",
    "exceptionDocURL": "https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Not_defined?utm_source=mozilla&utm_medium=firefox-console-errors&utm_campaign=default",
    "sourceName": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "lineText": "",
    "lineNumber": 3,
    "columnNumber": 5,
    "category": "content javascript",
    "timeStamp": 1476573167137,
    "warning": false,
    "error": false,
    "exception": true,
    "strict": false,
    "info": false,
    "private": false,
    "stacktrace": [
      {
        "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
        "lineNumber": 3,
        "columnNumber": 5,
        "functionName": "bar"
      },
      {
        "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
        "lineNumber": 6,
        "columnNumber": 5,
        "functionName": "foo"
      },
      {
        "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
        "lineNumber": 9,
        "columnNumber": 3,
        "functionName": null
      },
      {
        "filename": "resource://testing-common/content-task.js line 50 > eval",
        "lineNumber": 7,
        "columnNumber": 9,
        "functionName": null
      },
      {
        "filename": "resource://testing-common/content-task.js",
        "lineNumber": 51,
        "columnNumber": 20,
        "functionName": null
      }
    ],
    "notes": null
  }
});

stubPackets.set(`SyntaxError: redeclaration of let a`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "pageError",
  "pageError": {
    "errorMessage": "SyntaxError: redeclaration of let a",
    "errorMessageName": "JSMSG_REDECLARED_VAR",
    "sourceName": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "lineText": "  let a, a;\n",
    "lineNumber": 2,
    "columnNumber": 9,
    "category": "content javascript",
    "timeStamp": 1487992945524,
    "warning": false,
    "error": false,
    "exception": true,
    "strict": false,
    "info": false,
    "private": false,
    "stacktrace": [
      {
        "filename": "resource://testing-common/content-task.js line 50 > eval",
        "lineNumber": 7,
        "columnNumber": 9,
        "functionName": null
      },
      {
        "filename": "resource://testing-common/content-task.js",
        "lineNumber": 51,
        "columnNumber": 20,
        "functionName": null
      }
    ],
    "notes": [
      {
        "messageBody": "Previously declared at line 2, column 6",
        "frame": {
          "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
          "line": 2,
          "column": 6
        }
      }
    ]
  }
});

stubPackets.set(`TypeError longString message`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "pageError",
  "pageError": {
    "errorMessage": {
      "type": "longString",
      "initial": "Error: Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Lon",
      "length": 110007,
      "actor": "server1.conn0.child1/longString30"
    },
    "errorMessageName": "",
    "sourceName": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
    "lineText": "",
    "lineNumber": 1,
    "columnNumber": 7,
    "category": "content javascript",
    "timeStamp": 1493109507061,
    "warning": false,
    "error": false,
    "exception": true,
    "strict": false,
    "info": false,
    "private": false,
    "stacktrace": [
      {
        "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
        "lineNumber": 1,
        "columnNumber": 7,
        "functionName": null
      },
      {
        "filename": "resource://testing-common/content-task.js line 50 > eval",
        "lineNumber": 7,
        "columnNumber": 9,
        "functionName": null
      },
      {
        "filename": "resource://testing-common/content-task.js",
        "lineNumber": 51,
        "columnNumber": 20,
        "functionName": null
      }
    ],
    "notes": null
  }
});

stubPackets.set(`throw ""`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "pageError",
  "pageError": {
    "errorMessage": "uncaught exception: ",
    "errorMessageName": "JSMSG_UNCAUGHT_EXCEPTION",
    "sourceName": "",
    "lineText": "",
    "lineNumber": 0,
    "columnNumber": 0,
    "category": "content javascript",
    "timeStamp": 1517942398629,
    "warning": false,
    "error": false,
    "exception": false,
    "strict": false,
    "info": false,
    "private": false,
    "stacktrace": null,
    "notes": null
  }
});

stubPackets.set(`throw "tomato"`, {
  "from": "server1.conn0.child1/consoleActor2",
  "type": "pageError",
  "pageError": {
    "errorMessage": "uncaught exception: tomato",
    "errorMessageName": "JSMSG_UNCAUGHT_EXCEPTION",
    "sourceName": "",
    "lineText": "",
    "lineNumber": 0,
    "columnNumber": 0,
    "category": "content javascript",
    "timeStamp": 1517942398637,
    "warning": false,
    "error": false,
    "exception": false,
    "strict": false,
    "info": false,
    "private": false,
    "stacktrace": null,
    "notes": null
  }
});

module.exports = {
  stubPreparedMessages,
  stubPackets,
};
PK
!<�ƞ���Nchrome/devtools/modules/devtools/client/webconsole/new-console-output/types.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 {
  MESSAGE_SOURCE,
  MESSAGE_TYPE,
  MESSAGE_LEVEL
} = require("devtools/client/webconsole/new-console-output/constants");

exports.ConsoleCommand = function (props) {
  return Object.assign({
    id: null,
    allowRepeating: false,
    messageText: null,
    source: MESSAGE_SOURCE.JAVASCRIPT,
    type: MESSAGE_TYPE.COMMAND,
    level: MESSAGE_LEVEL.LOG,
    groupId: null,
    indent: 0,
    private: false,
  }, props);
};

exports.ConsoleMessage = function (props) {
  return Object.assign({
    id: null,
    allowRepeating: true,
    source: null,
    timeStamp: null,
    type: null,
    helperType: null,
    level: null,
    messageText: null,
    parameters: null,
    repeatId: null,
    stacktrace: null,
    frame: null,
    groupId: null,
    exceptionDocURL: null,
    userProvidedStyles: null,
    notes: null,
    indent: 0,
    prefix: "",
    private: false,
  }, props);
};

exports.NetworkEventMessage = function (props) {
  return Object.assign({
    id: null,
    actor: null,
    level: MESSAGE_LEVEL.LOG,
    isXHR: false,
    request: null,
    response: null,
    source: MESSAGE_SOURCE.NETWORK,
    type: MESSAGE_TYPE.LOG,
    groupId: null,
    timeStamp: null,
    totalTime: null,
    indent: 0,
    updates: null,
    openedOnce: false,
    securityState: null,
    securityInfo: null,
    requestHeadersFromUploadStream: null,
    private: false,
  }, props);
};
PK
!<�i�Ƙ�[chrome/devtools/modules/devtools/client/webconsole/new-console-output/utils/context-menu.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 Services = require("Services");
const {gDevTools} = require("devtools/client/framework/devtools");

const Menu = require("devtools/client/framework/menu");
const MenuItem = require("devtools/client/framework/menu-item");

const { MESSAGE_SOURCE } = require("devtools/client/webconsole/new-console-output/constants");

const clipboardHelper = require("devtools/shared/platform/clipboard");
const { l10n } = require("devtools/client/webconsole/new-console-output/utils/messages");

/**
 * Create a Menu instance for the webconsole.
 *
 * @param {Object} jsterm
 *        The JSTerm instance used by the webconsole.
 * @param {Element} parentNode
 *        The container of the new console frontend output wrapper.
 * @param {Object} options
 *        - {String} actor (optional) actor id to use for context menu actions
 *        - {String} clipboardText (optional) text to "Copy" if no selection is available
 *        - {String} variableText (optional) which is the textual frontend
 *            representation of the variable
 *        - {Object} message (optional) message object containing metadata such as:
 *          - {String} source
 *          - {String} request
 *        - {Function} openSidebar (optional) function that will open the object
 *            inspector sidebar
 *        - {String} rootActorId (optional) actor id for the root object being clicked on
 */
function createContextMenu(jsterm, parentNode, {
  actor,
  clipboardText,
  variableText,
  message,
  serviceContainer,
  openSidebar,
  rootActorId,
}) {
  let win = parentNode.ownerDocument.defaultView;
  let selection = win.getSelection();

  let { source, request } = message || {};

  let menu = new Menu({
    id: "webconsole-menu"
  });

  // Copy URL for a network request.
  menu.append(new MenuItem({
    id: "console-menu-copy-url",
    label: l10n.getStr("webconsole.menu.copyURL.label"),
    accesskey: l10n.getStr("webconsole.menu.copyURL.accesskey"),
    visible: source === MESSAGE_SOURCE.NETWORK,
    click: () => {
      if (!request) {
        return;
      }
      clipboardHelper.copyString(request.url);
    },
  }));

  // Open Network message in the Network panel.
  if (serviceContainer.openNetworkPanel && request) {
    menu.append(new MenuItem({
      id: "console-menu-open-in-network-panel",
      label: l10n.getStr("webconsole.menu.openInNetworkPanel.label"),
      accesskey: l10n.getStr("webconsole.menu.openInNetworkPanel.accesskey"),
      visible: source === MESSAGE_SOURCE.NETWORK,
      click: () => serviceContainer.openNetworkPanel(message.messageId)
    }));
  }

  // Open URL in a new tab for a network request.
  menu.append(new MenuItem({
    id: "console-menu-open-url",
    label: l10n.getStr("webconsole.menu.openURL.label"),
    accesskey: l10n.getStr("webconsole.menu.openURL.accesskey"),
    visible: source === MESSAGE_SOURCE.NETWORK,
    click: () => {
      if (!request) {
        return;
      }
      let mainWindow = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
      mainWindow.openUILinkIn(request.url, "tab");
    },
  }));

  // Store as global variable.
  menu.append(new MenuItem({
    id: "console-menu-store",
    label: l10n.getStr("webconsole.menu.storeAsGlobalVar.label"),
    accesskey: l10n.getStr("webconsole.menu.storeAsGlobalVar.accesskey"),
    disabled: !actor,
    click: () => {
      let evalString = `{ let i = 0;
        while (this.hasOwnProperty("temp" + i) && i < 1000) {
          i++;
        }
        this["temp" + i] = _self;
        "temp" + i;
      }`;
      let options = {
        selectedObjectActor: actor,
      };

      jsterm.requestEvaluation(evalString, options).then((res) => {
        jsterm.focus();
        jsterm.setInputValue(res.result);
      });
    },
  }));

  // Copy message or grip.
  menu.append(new MenuItem({
    id: "console-menu-copy",
    label: l10n.getStr("webconsole.menu.copyMessage.label"),
    accesskey: l10n.getStr("webconsole.menu.copyMessage.accesskey"),
    // Disabled if there is no selection and no message element available to copy.
    disabled: selection.isCollapsed && !clipboardText,
    click: () => {
      if (selection.isCollapsed) {
        // If the selection is empty/collapsed, copy the text content of the
        // message for which the context menu was opened.
        clipboardHelper.copyString(clipboardText);
      } else {
        clipboardHelper.copyString(selection.toString());
      }
    },
  }));

  // Copy message object.
  menu.append(new MenuItem({
    id: "console-menu-copy-object",
    label: l10n.getStr("webconsole.menu.copyObject.label"),
    accesskey: l10n.getStr("webconsole.menu.copyObject.accesskey"),
    // Disabled if there is no actor and no variable text associated.
    disabled: (!actor && !variableText),
    click: () => {
      if (actor) {
        // The Debugger.Object of the OA will be bound to |_self| during evaluation,
        jsterm.copyObject(`_self`, { selectedObjectActor: actor }).then((res) => {
          clipboardHelper.copyString(res.helperResult.value);
        });
      } else {
        clipboardHelper.copyString(variableText);
      }
    },
  }));

  // Select all.
  menu.append(new MenuItem({
    id: "console-menu-select",
    label: l10n.getStr("webconsole.menu.selectAll.label"),
    accesskey: l10n.getStr("webconsole.menu.selectAll.accesskey"),
    disabled: false,
    click: () => {
      let webconsoleOutput = parentNode.querySelector(".webconsole-output");
      selection.selectAllChildren(webconsoleOutput);
    },
  }));

  // Open object in sidebar.
  if (openSidebar) {
    menu.append(new MenuItem({
      id: "console-menu-open-sidebar",
      label: l10n.getStr("webconsole.menu.openInSidebar.label"),
      accesskey: l10n.getStr("webconsole.menu.openInSidebar.accesskey"),
      disabled: !rootActorId,
      click: () => openSidebar(message.messageId),
    }));
  }

  return menu;
}

exports.createContextMenu = createContextMenu;
PK
!<l3�`[chrome/devtools/modules/devtools/client/webconsole/new-console-output/utils/id-generator.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

exports.IdGenerator = class IdGenerator {
  constructor() {
    this.messageId = 1;
  }

  getNextId(packet) {
    return (packet && packet.actor) ? packet.actor : "" + this.messageId++;
  }
};
PK
!<�՝L,L,Wchrome/devtools/modules/devtools/client/webconsole/new-console-output/utils/messages.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 l10n = require("devtools/client/webconsole/webconsole-l10n");
const { getUrlDetails } = require("devtools/client/netmonitor/src/utils/request-utils");

const {
  MESSAGE_SOURCE,
  MESSAGE_TYPE,
  MESSAGE_LEVEL,
} = require("../constants");
const {
  ConsoleMessage,
  NetworkEventMessage,
} = require("../types");

function prepareMessage(packet, idGenerator) {
  if (!packet.source) {
    packet = transformPacket(packet);
  }

  if (packet.allowRepeating) {
    packet.repeatId = getRepeatId(packet);
  }
  packet.id = idGenerator.getNextId(packet);
  return packet;
}

/**
 * Transforms a packet from Firefox RDP structure to Chrome RDP structure.
 */
function transformPacket(packet) {
  if (packet._type) {
    packet = convertCachedPacket(packet);
  }

  switch (packet.type) {
    case "consoleAPICall": {
      return transformConsoleAPICallPacket(packet);
    }

    case "navigationMessage": {
      return transformNavigationMessagePacket(packet);
    }

    case "logMessage": {
      return transformLogMessagePacket(packet);
    }

    case "pageError": {
      return transformPageErrorPacket(packet);
    }

    case "networkEvent": {
      return transformNetworkEventPacket(packet);
    }

    case "evaluationResult":
    default: {
      return transformEvaluationResultPacket(packet);
    }
  }
}

function transformConsoleAPICallPacket(packet) {
  let { message } = packet;

  let parameters = message.arguments;
  let type = message.level;
  let level = getLevelFromType(type);
  let messageText = null;
  const timer = message.timer;

  // Special per-type conversion.
  switch (type) {
    case "clear":
      // We show a message to users when calls console.clear() is called.
      parameters = [l10n.getStr("consoleCleared")];
      break;
    case "count":
      // Chrome RDP doesn't have a special type for count.
      type = MESSAGE_TYPE.LOG;
      let {counter} = message;

      if (!counter) {
        // We don't show anything if we don't have counter data.
        type = MESSAGE_TYPE.NULL_MESSAGE;
      } else {
        let label = counter.label ? counter.label : l10n.getStr("noCounterLabel");
        messageText = `${label}: ${counter.count}`;
        parameters = null;
      }
      break;
    case "time":
      parameters = null;
      if (timer && timer.error) {
        messageText = l10n.getFormatStr(timer.error, [timer.name]);
        level = MESSAGE_LEVEL.WARN;
      } else {
        // We don't show anything for console.time calls to match Chrome's behaviour.
        type = MESSAGE_TYPE.NULL_MESSAGE;
      }
      break;
    case "timeEnd":
      parameters = null;
      if (timer && timer.error) {
        messageText = l10n.getFormatStr(timer.error, [timer.name]);
        level = MESSAGE_LEVEL.WARN;
      } else if (timer) {
        // We show the duration to users when calls console.timeEnd() is called,
        // if corresponding console.time() was called before.
        let duration = Math.round(timer.duration * 100) / 100;
        messageText = l10n.getFormatStr("timeEnd", [timer.name, duration]);
      } else {
        // If the `timer` property does not exists, we don't output anything.
        type = MESSAGE_TYPE.NULL_MESSAGE;
      }
      break;
    case "table":
      const supportedClasses = [
        "Array", "Object", "Map", "Set", "WeakMap", "WeakSet"];
      if (
        !Array.isArray(parameters) ||
        parameters.length === 0 ||
        !supportedClasses.includes(parameters[0].class)
      ) {
        // If the class of the first parameter is not supported,
        // we handle the call as a simple console.log
        type = "log";
      }
      break;
    case "group":
      type = MESSAGE_TYPE.START_GROUP;
      if (parameters.length === 0) {
        parameters = [l10n.getStr("noGroupLabel")];
      }
      break;
    case "groupCollapsed":
      type = MESSAGE_TYPE.START_GROUP_COLLAPSED;
      if (parameters.length === 0) {
        parameters = [l10n.getStr("noGroupLabel")];
      }
      break;
    case "groupEnd":
      type = MESSAGE_TYPE.END_GROUP;
      parameters = null;
      break;
    case "dirxml":
      // Handle console.dirxml calls as simple console.log
      type = "log";
      break;
  }

  const frame = message.filename ? {
    source: message.filename,
    line: message.lineNumber,
    column: message.columnNumber,
  } : null;

  return new ConsoleMessage({
    source: MESSAGE_SOURCE.CONSOLE_API,
    type,
    level,
    parameters,
    messageText,
    stacktrace: message.stacktrace ? message.stacktrace : null,
    frame,
    timeStamp: message.timeStamp,
    userProvidedStyles: message.styles,
    prefix: message.prefix,
    private: message.private,
  });
}

function transformNavigationMessagePacket(packet) {
  let { message } = packet;
  return new ConsoleMessage({
    source: MESSAGE_SOURCE.CONSOLE_API,
    type: MESSAGE_TYPE.LOG,
    level: MESSAGE_LEVEL.LOG,
    messageText: l10n.getFormatStr("webconsole.navigated", [message.url]),
    timeStamp: message.timeStamp,
    private: message.private,
  });
}

function transformLogMessagePacket(packet) {
  let {
    message,
    timeStamp,
  } = packet;

  return new ConsoleMessage({
    source: MESSAGE_SOURCE.CONSOLE_API,
    type: MESSAGE_TYPE.LOG,
    level: MESSAGE_LEVEL.LOG,
    messageText: message,
    timeStamp,
    private: message.private,
  });
}

function transformPageErrorPacket(packet) {
  let { pageError } = packet;
  let level = MESSAGE_LEVEL.ERROR;
  if (pageError.warning || pageError.strict) {
    level = MESSAGE_LEVEL.WARN;
  } else if (pageError.info) {
    level = MESSAGE_LEVEL.INFO;
  }

  const frame = pageError.sourceName ? {
    source: pageError.sourceName,
    line: pageError.lineNumber,
    column: pageError.columnNumber
  } : null;

  let matchesCSS = /^(?:CSS|Layout)\b/.test(pageError.category);
  let messageSource = matchesCSS ? MESSAGE_SOURCE.CSS
                                  : MESSAGE_SOURCE.JAVASCRIPT;
  return new ConsoleMessage({
    source: messageSource,
    type: MESSAGE_TYPE.LOG,
    level,
    messageText: pageError.errorMessage,
    stacktrace: pageError.stacktrace ? pageError.stacktrace : null,
    frame,
    exceptionDocURL: pageError.exceptionDocURL,
    timeStamp: pageError.timeStamp,
    notes: pageError.notes,
    private: pageError.private,
  });
}

function transformNetworkEventPacket(packet) {
  let { networkEvent } = packet;

  return new NetworkEventMessage({
    actor: networkEvent.actor,
    isXHR: networkEvent.isXHR,
    request: networkEvent.request,
    response: networkEvent.response,
    timeStamp: networkEvent.timeStamp,
    totalTime: networkEvent.totalTime,
    url: networkEvent.request.url,
    urlDetails: getUrlDetails(networkEvent.request.url),
    method: networkEvent.request.method,
    updates: networkEvent.updates,
    cause: networkEvent.cause,
    private: networkEvent.private,
  });
}

function transformEvaluationResultPacket(packet) {
  let {
    exceptionMessage,
    exceptionDocURL,
    exception,
    frame,
    result,
    helperResult,
    timestamp: timeStamp,
    notes,
  } = packet;

  const parameter = helperResult && helperResult.object
    ? helperResult.object
    : result;

  if (helperResult && helperResult.type === "error") {
    try {
      exceptionMessage = l10n.getStr(helperResult.message);
    } catch (ex) {
      exceptionMessage = helperResult.message;
    }
  } else if (typeof exception === "string") {
    // Wrap thrown strings in Error objects, so `throw "foo"` outputs "Error: foo"
    exceptionMessage = new Error(exceptionMessage).toString();
  }

  const level = typeof exceptionMessage !== "undefined" && exceptionMessage !== null
    ? MESSAGE_LEVEL.ERROR
    : MESSAGE_LEVEL.LOG;

  return new ConsoleMessage({
    source: MESSAGE_SOURCE.JAVASCRIPT,
    type: MESSAGE_TYPE.RESULT,
    helperType: helperResult ? helperResult.type : null,
    level,
    messageText: exceptionMessage,
    parameters: [parameter],
    exceptionDocURL,
    frame,
    timeStamp,
    notes,
    private: packet.private,
  });
}

// Helpers
function getRepeatId(message) {
  return JSON.stringify({
    frame: message.frame,
    groupId: message.groupId,
    indent: message.indent,
    level: message.level,
    messageText: message.messageText,
    parameters: message.parameters,
    source: message.source,
    type: message.type,
    userProvidedStyles: message.userProvidedStyles,
    private: message.private,
  });
}

function convertCachedPacket(packet) {
  // The devtools server provides cached message packets in a different shape, so we
  // transform them here.
  let convertPacket = {};
  if (packet._type === "ConsoleAPI") {
    convertPacket.message = packet;
    convertPacket.type = "consoleAPICall";
  } else if (packet._type === "PageError") {
    convertPacket.pageError = packet;
    convertPacket.type = "pageError";
  } else if ("_navPayload" in packet) {
    convertPacket.type = "navigationMessage";
    convertPacket.message = packet;
  } else if (packet._type === "NetworkEvent") {
    convertPacket.networkEvent = packet;
    convertPacket.type = "networkEvent";
  } else if (packet._type === "LogMessage") {
    convertPacket = {
      ...packet,
      type: "logMessage"
    };
  } else {
    throw new Error("Unexpected packet type: " + packet._type);
  }
  return convertPacket;
}

/**
 * Maps a Firefox RDP type to its corresponding level.
 */
function getLevelFromType(type) {
  const levels = {
    LEVEL_ERROR: "error",
    LEVEL_WARNING: "warn",
    LEVEL_INFO: "info",
    LEVEL_LOG: "log",
    LEVEL_DEBUG: "debug",
  };

  // A mapping from the console API log event levels to the Web Console levels.
  const levelMap = {
    error: levels.LEVEL_ERROR,
    exception: levels.LEVEL_ERROR,
    assert: levels.LEVEL_ERROR,
    warn: levels.LEVEL_WARNING,
    info: levels.LEVEL_INFO,
    log: levels.LEVEL_LOG,
    clear: levels.LEVEL_LOG,
    trace: levels.LEVEL_LOG,
    table: levels.LEVEL_LOG,
    debug: levels.LEVEL_DEBUG,
    dir: levels.LEVEL_LOG,
    dirxml: levels.LEVEL_LOG,
    group: levels.LEVEL_LOG,
    groupCollapsed: levels.LEVEL_LOG,
    groupEnd: levels.LEVEL_LOG,
    time: levels.LEVEL_LOG,
    timeEnd: levels.LEVEL_LOG,
    count: levels.LEVEL_LOG,
  };

  return levelMap[type] || MESSAGE_TYPE.LOG;
}

function isGroupType(type) {
  return [
    MESSAGE_TYPE.START_GROUP,
    MESSAGE_TYPE.START_GROUP_COLLAPSED
  ].includes(type);
}

function getInitialMessageCountForViewport(win) {
  const minMessageHeight = 20;
  return Math.ceil(win.innerHeight / minMessageHeight);
}

function isPacketPrivate(packet) {
  return (
    packet.private === true ||
    (packet.message && packet.message.private === true) ||
    (packet.pageError && packet.pageError.private === true) ||
    (packet.networkEvent && packet.networkEvent.private === true)
  );
}

module.exports = {
  getInitialMessageCountForViewport,
  isGroupType,
  isPacketPrivate,
  l10n,
  prepareMessage,
  // Export for use in testing.
  getRepeatId,
};
PK
!<�S`�
�
_chrome/devtools/modules/devtools/client/webconsole/new-console-output/utils/object-inspector.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 } = require("devtools/client/shared/vendor/react");
const ObjectClient = require("devtools/shared/client/object-client");

const reps = require("devtools/client/shared/components/reps/reps");
const { REPS, MODE } = reps;
const ObjectInspector = createFactory(reps.ObjectInspector);
const { Grip } = REPS;

/**
 * Create and return an ObjectInspector for the given grip.
 *
 * @param {Object} grip
 *        The object grip to create an ObjectInspector for.
 * @param {Object} serviceContainer
 *        Object containing various utility functions
 * @param {Object} override
 *        Object containing props that should override the default props passed to
 *        ObjectInspector.
 * @returns {ObjectInspector}
 *        An ObjectInspector for the given grip.
 */
function getObjectInspector(grip, serviceContainer, override) {
  let onDOMNodeMouseOver;
  let onDOMNodeMouseOut;
  let onInspectIconClick;
  if (serviceContainer) {
    onDOMNodeMouseOver = serviceContainer.highlightDomElement
      ? (object) => serviceContainer.highlightDomElement(object)
      : null;
    onDOMNodeMouseOut = serviceContainer.unHighlightDomElement;
    onInspectIconClick = serviceContainer.openNodeInInspector
      ? (object, e) => {
        // Stop the event propagation so we don't trigger ObjectInspector expand/collapse.
        e.stopPropagation();
        serviceContainer.openNodeInInspector(object);
      }
      : null;
  }

  const objectInspectorProps = {
    autoExpandDepth: 0,
    mode: MODE.LONG,
    // TODO: we disable focus since it's not currently working well in ObjectInspector.
    // Let's remove the property below when problem are fixed in OI.
    disabledFocus: true,
    roots: [{
      path: (grip && grip.actor) || JSON.stringify(grip),
      contents: {
        value: grip
      }
    }],
    createObjectClient: object =>
      new ObjectClient(serviceContainer.hudProxy.client, object),
    releaseActor: actor => {
      if (!actor || !serviceContainer.hudProxy.releaseActor) {
        return;
      }
      serviceContainer.hudProxy.releaseActor(actor);
    },
    onViewSourceInDebugger: serviceContainer.onViewSourceInDebugger,
    openLink: serviceContainer.openLink,
  };

  if (!(typeof grip === "string" || (grip && grip.type === "longString"))) {
    Object.assign(objectInspectorProps, {
      onDOMNodeMouseOver,
      onDOMNodeMouseOut,
      onInspectIconClick,
      defaultRep: Grip,
    });
  }

  return ObjectInspector({...objectInspectorProps, ...override});
}

exports.getObjectInspector = getObjectInspector;
PK
!<��}���Tchrome/devtools/modules/devtools/client/webconsole/new-console-output/utils/prefs.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Services = require("Services");

function getPreferenceName(hud, suffix) {
  if (!suffix) {
    console.error("Suffix shouldn't be falsy", {suffix});
    return null;
  }

  if (!hud) {
    console.error("hud shouldn't be falsy", {hud});
    return null;
  }

  if (suffix.startsWith("devtools.")) {
    // We don't have a suffix but a full pref name. Let's return it.
    return suffix;
  }

  const component = hud.isBrowserConsole
    ? "browserconsole"
    : "webconsole";
  return `devtools.${component}.${suffix}`;
}

function getPrefsService(hud) {
  const getPrefName = pref => getPreferenceName(hud, pref);

  return {
    getBoolPref: (pref, deflt) => Services.prefs.getBoolPref(getPrefName(pref), deflt),
    getIntPref: (pref, deflt) => Services.prefs.getIntPref(getPrefName(pref), deflt),
    setBoolPref: (pref, value) => Services.prefs.setBoolPref(getPrefName(pref), value),
    clearUserPref: pref => Services.prefs.clearUserPref(getPrefName(pref)),
    getPrefName,
  };
}

module.exports = {
  getPrefsService,
};
PK
!<���0((Dchrome/devtools/modules/devtools/client/webconsole/new-webconsole.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 {Utils: WebConsoleUtils} = require("devtools/client/webconsole/utils");
const EventEmitter = require("devtools/shared/event-emitter");
const promise = require("promise");
const defer = require("devtools/shared/defer");
const Services = require("Services");
const { gDevTools } = require("devtools/client/framework/devtools");
const { JSTerm } = require("devtools/client/webconsole/jsterm");
const { WebConsoleConnectionProxy } = require("devtools/client/webconsole/webconsole-connection-proxy");
const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
const { l10n } = require("devtools/client/webconsole/new-console-output/utils/messages");
const system = require("devtools/shared/system");
const ZoomKeys = require("devtools/client/shared/zoom-keys");

const PREF_MESSAGE_TIMESTAMP = "devtools.webconsole.timestampMessages";
const PREF_PERSISTLOG = "devtools.webconsole.persistlog";
const PREF_SIDEBAR_ENABLED = "devtools.webconsole.sidebarToggle";

// XXX: This file is incomplete (see bug 1326937).
// It's used when loading the webconsole with devtools-launchpad, but will ultimately be
// the entry point for the new frontend

/**
 * A WebConsoleFrame instance is an interactive console initialized *per target*
 * that displays console log data as well as provides an interactive terminal to
 * manipulate the target's document content.
 *
 * The WebConsoleFrame is responsible for the actual Web Console UI
 * implementation.
 *
 * @constructor
 * @param object webConsoleOwner
 *        The WebConsole owner object.
 */
function NewWebConsoleFrame(webConsoleOwner) {
  this.owner = webConsoleOwner;
  this.hudId = this.owner.hudId;
  this.isBrowserConsole = this.owner._browserConsole;
  this.NEW_CONSOLE_OUTPUT_ENABLED = true;
  this.window = this.owner.iframeWindow;

  this._onToolboxPrefChanged = this._onToolboxPrefChanged.bind(this);

  EventEmitter.decorate(this);
}
NewWebConsoleFrame.prototype = {
  /**
   * Getter for the debugger WebConsoleClient.
   * @type object
   */
  get webConsoleClient() {
    return this.proxy ? this.proxy.webConsoleClient : null;
  },

  /**
   * Getter for the persistent logging preference.
   * @type boolean
   */
  get persistLog() {
    // For the browser console, we receive tab navigation
    // when the original top level window we attached to is closed,
    // but we don't want to reset console history and just switch to
    // the next available window.
    return this.isBrowserConsole ||
           Services.prefs.getBoolPref(PREF_PERSISTLOG);
  },

  /**
   * Initialize the WebConsoleFrame instance.
   * @return object
   *         A promise object that resolves once the frame is ready to use.
   */
  async init() {
    this._initUI();
    let connectionInited = this._initConnection();
    // Don't reject if the history fails to load for some reason.
    // This would be fine, the panel will just start with empty history.
    let onJsTermHistoryLoaded = this.jsterm.historyLoaded
      .catch(() => {});

    await Promise.all([connectionInited, onJsTermHistoryLoaded]);
    await this.newConsoleOutput.init();

    let id = WebConsoleUtils.supportsString(this.hudId);
    if (Services.obs) {
      Services.obs.notifyObservers(id, "web-console-created");
    }
  },
  destroy() {
    if (this._destroyer) {
      return this._destroyer.promise;
    }
    this._destroyer = defer();
    Services.prefs.removeObserver(PREF_MESSAGE_TIMESTAMP, this._onToolboxPrefChanged);
    this.React = this.ReactDOM = this.FrameView = null;
    if (this.jsterm) {
      this.jsterm.off("sidebar-opened", this.resize);
      this.jsterm.off("sidebar-closed", this.resize);
      this.jsterm.destroy();
      this.jsterm = null;
    }

    let toolbox = gDevTools.getToolbox(this.owner.target);
    if (toolbox) {
      toolbox.off("webconsole-selected", this._onPanelSelected);
    }

    this.window = this.owner = this.newConsoleOutput = null;

    let onDestroy = () => {
      this._destroyer.resolve(null);
    };
    if (this.proxy) {
      this.proxy.disconnect().then(onDestroy);
      this.proxy = null;
    } else {
      onDestroy();
    }

    return this._destroyer.promise;
  },

  _onUpdateListeners() {

  },

  logWarningAboutReplacedAPI() {
    this.owner.target.logWarningInPage(l10n.getStr("ConsoleAPIDisabled"),
      "ConsoleAPIDisabled");
  },

  handleNetworkEventUpdate() {

  },

  /**
   * Setter for saving of network request and response bodies.
   *
   * @param boolean value
   *        The new value you want to set.
   */
  setSaveRequestAndResponseBodies(value) {
    if (!this.webConsoleClient) {
      // Don't continue if the webconsole disconnected.
      return promise.resolve(null);
    }

    let deferred = defer();
    let newValue = !!value;
    let toSet = {
      "NetworkMonitor.saveRequestAndResponseBodies": newValue,
    };

    // Make sure the web console client connection is established first.
    this.webConsoleClient.setPreferences(toSet, response => {
      if (!response.error) {
        this._saveRequestAndResponseBodies = newValue;
        deferred.resolve(response);
      } else {
        deferred.reject(response.error);
      }
    });

    return deferred.promise;
  },

  /**
   * Connect to the server using the remote debugging protocol.
   *
   * @private
   * @return object
   *         A promise object that is resolved/reject based on the connection
   *         result.
   */
  _initConnection: function () {
    if (this._initDefer) {
      return this._initDefer.promise;
    }

    this._initDefer = defer();
    this.proxy = new WebConsoleConnectionProxy(this, this.owner.target);

    this.proxy.connect().then(() => {
      // on success
      this._initDefer.resolve(this);
    }, (reason) => {
      // on failure
      // TODO Print a message to console
      this._initDefer.reject(reason);
    });

    return this._initDefer.promise;
  },

  _initUI: function () {
    this.document = this.window.document;
    this.rootElement = this.document.documentElement;

    this.outputNode = this.document.getElementById("output-container");
    this.completeNode = this.document.querySelector(".jsterm-complete-node");
    this.inputNode = this.document.querySelector(".jsterm-input-node");

    this.jsterm = new JSTerm(this);
    this.jsterm.init();

    let toolbox = gDevTools.getToolbox(this.owner.target);

    // @TODO Remove this once JSTerm is handled with React/Redux.
    this.window.jsterm = this.jsterm;
    // @TODO Once the toolbox has been converted to React, see if passing
    // in JSTerm is still necessary.

    // Handle both launchpad and toolbox loading
    let Wrapper = this.owner.NewConsoleOutputWrapper || this.window.NewConsoleOutput;
    this.newConsoleOutput = new Wrapper(
      this.outputNode, this.jsterm, toolbox, this.owner, this.document);
    // Toggle the timestamp on preference change
    Services.prefs.addObserver(PREF_MESSAGE_TIMESTAMP, this._onToolboxPrefChanged);
    this._onToolboxPrefChanged();

    this._initShortcuts();
  },

  _initShortcuts: function () {
    let shortcuts = new KeyShortcuts({
      window: this.window
    });

    shortcuts.on(l10n.getStr("webconsole.find.key"),
                 event => {
                   this.filterBox.focus();
                   event.preventDefault();
                 });

    let clearShortcut;
    if (system.constants.platform === "macosx") {
      clearShortcut = l10n.getStr("webconsole.clear.keyOSX");
    } else {
      clearShortcut = l10n.getStr("webconsole.clear.key");
    }

    shortcuts.on(clearShortcut, () => this.jsterm.clearOutput(true));

    if (this.isBrowserConsole) {
      shortcuts.on(l10n.getStr("webconsole.close.key"),
                   this.window.top.close.bind(this.window.top));

      ZoomKeys.register(this.window);
    } else if (Services.prefs.getBoolPref(PREF_SIDEBAR_ENABLED)) {
      shortcuts.on("Esc", event => {
        if (!this.jsterm.autocompletePopup || !this.jsterm.autocompletePopup.isOpen) {
          this.newConsoleOutput.dispatchSidebarClose();
        }
      });
    }
  },
  /**
   * Handler for page location changes.
   *
   * @param string uri
   *        New page location.
   * @param string title
   *        New page title.
   */
  onLocationChange: function (uri, title) {
    this.contentLocation = uri;
    if (this.owner.onLocationChange) {
      this.owner.onLocationChange(uri, title);
    }
  },

  /**
   * Release an actor.
   *
   * @private
   * @param string actor
   *        The actor ID you want to release.
   */
  _releaseObject: function (actor) {
    if (this.proxy) {
      this.proxy.releaseActor(actor);
    }
  },

  /**
   * Called when the message timestamp pref changes.
   */
  _onToolboxPrefChanged: function () {
    let newValue = Services.prefs.getBoolPref(PREF_MESSAGE_TIMESTAMP);
    this.newConsoleOutput.dispatchTimestampsToggle(newValue);
  },

  /**
   * Handler for the tabNavigated notification.
   *
   * @param string event
   *        Event name.
   * @param object packet
   *        Notification packet received from the server.
   */
  handleTabNavigated: async function (event, packet) {
    if (event == "will-navigate") {
      if (this.persistLog) {
        // Add a _type to hit convertCachedPacket.
        packet._type = true;
        this.newConsoleOutput.dispatchMessageAdd(packet);
      } else {
        this.jsterm.clearOutput(false);
      }
    }

    if (packet.url) {
      this.onLocationChange(packet.url, packet.title);
    }

    if (event == "navigate" && !packet.nativeConsoleAPI) {
      this.logWarningAboutReplacedAPI();
    }

    if (event == "navigate") {
      // Wait for completion of any async dispatch before notifying that the console
      // is fully updated after a page reload
      await this.newConsoleOutput.waitAsyncDispatches();
      this.emit("reloaded");
    }
  },
};

exports.NewWebConsoleFrame = NewWebConsoleFrame;
PK
!<��2�
�
;chrome/devtools/modules/devtools/client/webconsole/panel.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft= javascript ts=2 et sw=2 tw=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 promise = require("promise");
const defer = require("devtools/shared/defer");

loader.lazyRequireGetter(this, "HUDService", "devtools/client/webconsole/hudservice", true);
loader.lazyGetter(this, "EventEmitter", () => require("devtools/shared/event-emitter"));

/**
 * A DevToolPanel that controls the Web Console.
 */
function WebConsolePanel(iframeWindow, toolbox) {
  this._frameWindow = iframeWindow;
  this._toolbox = toolbox;
  EventEmitter.decorate(this);
}

exports.WebConsolePanel = WebConsolePanel;

WebConsolePanel.prototype = {
  hud: null,

  /**
   * Called by the WebConsole's onkey command handler.
   * If the WebConsole is opened, check if the JSTerm's input line has focus.
   * If not, focus it.
   */
  focusInput: function () {
    this.hud.jsterm.focus();
  },

  /**
   * Open is effectively an asynchronous constructor.
   *
   * @return object
   *         A promise that is resolved when the Web Console completes opening.
   */
  open: function () {
    let parentDoc = this._toolbox.doc;
    let iframe = parentDoc.getElementById("toolbox-panel-iframe-webconsole");

    // Make sure the iframe content window is ready.
    let deferredIframe = defer();
    let win, doc;
    if ((win = iframe.contentWindow) &&
        (doc = win.document) &&
        doc.readyState == "complete") {
      deferredIframe.resolve(null);
    } else {
      iframe.addEventListener("load", function () {
        deferredIframe.resolve(null);
      }, {capture: true, once: true});
    }

    // Local debugging needs to make the target remote.
    let promiseTarget;
    if (!this.target.isRemote) {
      promiseTarget = this.target.makeRemote();
    } else {
      promiseTarget = promise.resolve(this.target);
    }

    // 1. Wait for the iframe to load.
    // 2. Wait for the remote target.
    // 3. Open the Web Console.
    return deferredIframe.promise
      .then(() => promiseTarget)
      .then((target) => {
        this._frameWindow._remoteTarget = target;

        let webConsoleUIWindow = iframe.contentWindow.wrappedJSObject;
        let chromeWindow = iframe.ownerDocument.defaultView;
        return HUDService.openWebConsole(this.target, webConsoleUIWindow,
                                         chromeWindow);
      })
      .then((webConsole) => {
        this.hud = webConsole;
        // Pipe 'reloaded' event from NewWebConsoleFrame to WebConsolePanel.
        // These events are listened by the Toolbox.
        this.hud.ui.on("reloaded", () => {
          this.emit("reloaded");
        });
        this._isReady = true;
        this.emit("ready");
        return this;
      }, (reason) => {
        let msg = "WebConsolePanel open failed. " +
                  reason.error + ": " + reason.message;
        dump(msg + "\n");
        console.error(msg, reason);
      });
  },

  get target() {
    return this._toolbox.target;
  },

  _isReady: false,
  get isReady() {
    return this._isReady;
  },

  destroy: function () {
    if (this._destroyer) {
      return this._destroyer;
    }

    this._destroyer = this.hud.destroy();
    this._destroyer.then(() => {
      this._frameWindow = null;
      this._toolbox = null;
      this.emit("destroyed");
    });

    return this._destroyer;
  },
};
PK
!<ߚ���;chrome/devtools/modules/devtools/client/webconsole/utils.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft= javascript ts=2 et sw=2 tw=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 {Cc, Ci} = require("chrome");
const Services = require("Services");

// Match the function name from the result of toString() or toSource().
//
// Examples:
// (function foobar(a, b) { ...
// function foobar2(a) { ...
// function() { ...
const REGEX_MATCH_FUNCTION_NAME = /^\(?function\s+([^(\s]+)\s*\(/;

// Number of terminal entries for the self-xss prevention to go away
const CONSOLE_ENTRY_THRESHOLD = 5;

exports.CONSOLE_WORKER_IDS = [
  "SharedWorker",
  "ServiceWorker",
  "Worker"
];

var WebConsoleUtils = {

  /**
   * Wrap a string in an nsISupportsString object.
   *
   * @param string string
   * @return nsISupportsString
   */
  supportsString: function (string) {
    let str = Cc["@mozilla.org/supports-string;1"]
              .createInstance(Ci.nsISupportsString);
    str.data = string;
    return str;
  },

  /**
   * Clone an object.
   *
   * @param object object
   *        The object you want cloned.
   * @param boolean recursive
   *        Tells if you want to dig deeper into the object, to clone
   *        recursively.
   * @param function [filter]
   *        Optional, filter function, called for every property. Three
   *        arguments are passed: key, value and object. Return true if the
   *        property should be added to the cloned object. Return false to skip
   *        the property.
   * @return object
   *         The cloned object.
   */
  cloneObject: function (object, recursive, filter) {
    if (typeof object != "object") {
      return object;
    }

    let temp;

    if (Array.isArray(object)) {
      temp = [];
      Array.forEach(object, function (value, index) {
        if (!filter || filter(index, value, object)) {
          temp.push(recursive ? WebConsoleUtils.cloneObject(value) : value);
        }
      });
    } else {
      temp = {};
      for (let key in object) {
        let value = object[key];
        if (object.hasOwnProperty(key) &&
            (!filter || filter(key, value, object))) {
          temp[key] = recursive ? WebConsoleUtils.cloneObject(value) : value;
        }
      }
    }

    return temp;
  },

  /**
   * Copies certain style attributes from one element to another.
   *
   * @param nsIDOMNode from
   *        The target node.
   * @param nsIDOMNode to
   *        The destination node.
   */
  copyTextStyles: function (from, to) {
    let win = from.ownerDocument.defaultView;
    let style = win.getComputedStyle(from);
    to.style.fontFamily = style.fontFamily;
    to.style.fontSize = style.fontSize;
    to.style.fontWeight = style.fontWeight;
    to.style.fontStyle = style.fontStyle;
  },

  /**
   * Determine if the given request mixes HTTP with HTTPS content.
   *
   * @param string request
   *        Location of the requested content.
   * @param string location
   *        Location of the current page.
   * @return boolean
   *         True if the content is mixed, false if not.
   */
  isMixedHTTPSRequest: function (request, location) {
    try {
      let requestURI = Services.io.newURI(request);
      let contentURI = Services.io.newURI(location);
      return (contentURI.scheme == "https" && requestURI.scheme != "https");
    } catch (ex) {
      return false;
    }
  },

  /**
   * Helper function to deduce the name of the provided function.
   *
   * @param funtion function
   *        The function whose name will be returned.
   * @return string
   *         Function name.
   */
  getFunctionName: function (func) {
    let name = null;
    if (func.name) {
      name = func.name;
    } else {
      let desc;
      try {
        desc = func.getOwnPropertyDescriptor("displayName");
      } catch (ex) {
        // Ignore.
      }
      if (desc && typeof desc.value == "string") {
        name = desc.value;
      }
    }
    if (!name) {
      try {
        let str = (func.toString() || func.toSource()) + "";
        name = (str.match(REGEX_MATCH_FUNCTION_NAME) || [])[1];
      } catch (ex) {
        // Ignore.
      }
    }
    return name;
  },

  /**
   * Get the object class name. For example, the |window| object has the Window
   * class name (based on [object Window]).
   *
   * @param object object
   *        The object you want to get the class name for.
   * @return string
   *         The object class name.
   */
  getObjectClassName: function (object) {
    if (object === null) {
      return "null";
    }
    if (object === undefined) {
      return "undefined";
    }

    let type = typeof object;
    if (type != "object") {
      // Grip class names should start with an uppercase letter.
      return type.charAt(0).toUpperCase() + type.substr(1);
    }

    let className;

    try {
      className = ((object + "").match(/^\[object (\S+)\]$/) || [])[1];
      if (!className) {
        className = ((object.constructor + "")
                     .match(/^\[object (\S+)\]$/) || [])[1];
      }
      if (!className && typeof object.constructor == "function") {
        className = this.getFunctionName(object.constructor);
      }
    } catch (ex) {
      // Ignore.
    }

    return className;
  },

  /**
   * Check if the given value is a grip with an actor.
   *
   * @param mixed grip
   *        Value you want to check if it is a grip with an actor.
   * @return boolean
   *         True if the given value is a grip with an actor.
   */
  isActorGrip: function (grip) {
    return grip && typeof (grip) == "object" && grip.actor;
  },

  /**
   * Value of devtools.selfxss.count preference
   *
   * @type number
   * @private
   */
  _usageCount: 0,
  get usageCount() {
    if (WebConsoleUtils._usageCount < CONSOLE_ENTRY_THRESHOLD) {
      WebConsoleUtils._usageCount =
        Services.prefs.getIntPref("devtools.selfxss.count");
      if (Services.prefs.getBoolPref("devtools.chrome.enabled")) {
        WebConsoleUtils.usageCount = CONSOLE_ENTRY_THRESHOLD;
      }
    }
    return WebConsoleUtils._usageCount;
  },
  set usageCount(newUC) {
    if (newUC <= CONSOLE_ENTRY_THRESHOLD) {
      WebConsoleUtils._usageCount = newUC;
      Services.prefs.setIntPref("devtools.selfxss.count", newUC);
    }
  },
  /**
   * The inputNode "paste" event handler generator. Helps prevent
   * self-xss attacks
   *
   * @param nsIDOMElement inputField
   * @param nsIDOMElement notificationBox
   * @returns A function to be added as a handler to 'paste' and
   *'drop' events on the input field
   */
  pasteHandlerGen: function (inputField, notificationBox, msg, okstring) {
    let handler = function (event) {
      if (WebConsoleUtils.usageCount >= CONSOLE_ENTRY_THRESHOLD) {
        inputField.removeEventListener("paste", handler);
        inputField.removeEventListener("drop", handler);
        return true;
      }
      if (notificationBox.getNotificationWithValue("selfxss-notification")) {
        event.preventDefault();
        event.stopPropagation();
        return false;
      }

      let notification = notificationBox.appendNotification(msg,
        "selfxss-notification", null,
        notificationBox.PRIORITY_WARNING_HIGH, null,
        function (eventType) {
          // Cleanup function if notification is dismissed
          if (eventType == "removed") {
            inputField.removeEventListener("keyup", pasteKeyUpHandler);
          }
        });

      function pasteKeyUpHandler(event2) {
        let value = inputField.value || inputField.textContent;
        if (value.includes(okstring)) {
          notificationBox.removeNotification(notification);
          inputField.removeEventListener("keyup", pasteKeyUpHandler);
          WebConsoleUtils.usageCount = CONSOLE_ENTRY_THRESHOLD;
        }
      }
      inputField.addEventListener("keyup", pasteKeyUpHandler);

      event.preventDefault();
      event.stopPropagation();
      return false;
    };
    return handler;
  },
};

exports.Utils = WebConsoleUtils;

PK
!<��2�*=*=Qchrome/devtools/modules/devtools/client/webconsole/webconsole-connection-proxy.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 {Utils: WebConsoleUtils} = require("devtools/client/webconsole/utils");
const defer = require("devtools/shared/defer");
const Services = require("Services");

const l10n = require("devtools/client/webconsole/webconsole-l10n");

const PREF_CONNECTION_TIMEOUT = "devtools.debugger.remote-timeout";
// Web Console connection proxy

/**
 * The WebConsoleConnectionProxy handles the connection between the Web Console
 * and the application we connect to through the remote debug protocol.
 *
 * @constructor
 * @param object webConsoleFrame
 *        The WebConsoleFrame object that owns this connection proxy.
 * @param RemoteTarget target
 *        The target that the console will connect to.
 */
function WebConsoleConnectionProxy(webConsoleFrame, target) {
  this.webConsoleFrame = webConsoleFrame;
  this.target = target;

  this._onPageError = this._onPageError.bind(this);
  this._onLogMessage = this._onLogMessage.bind(this);
  this._onConsoleAPICall = this._onConsoleAPICall.bind(this);
  this._onNetworkEvent = this._onNetworkEvent.bind(this);
  this._onNetworkEventUpdate = this._onNetworkEventUpdate.bind(this);
  this._onFileActivity = this._onFileActivity.bind(this);
  this._onReflowActivity = this._onReflowActivity.bind(this);
  this._onServerLogCall = this._onServerLogCall.bind(this);
  this._onTabNavigated = this._onTabNavigated.bind(this);
  this._onAttachConsole = this._onAttachConsole.bind(this);
  this._onCachedMessages = this._onCachedMessages.bind(this);
  this._connectionTimeout = this._connectionTimeout.bind(this);
  this._onLastPrivateContextExited =
    this._onLastPrivateContextExited.bind(this);
}

WebConsoleConnectionProxy.prototype = {
  /**
   * The owning Web Console Frame instance.
   *
   * @see WebConsoleFrame
   * @type object
   */
  webConsoleFrame: null,

  /**
   * The target that the console connects to.
   * @type RemoteTarget
   */
  target: null,

  /**
   * The DebuggerClient object.
   *
   * @see DebuggerClient
   * @type object
   */
  client: null,

  /**
   * The WebConsoleClient object.
   *
   * @see WebConsoleClient
   * @type object
   */
  webConsoleClient: null,

  /**
   * Tells if the connection is established.
   * @type boolean
   */
  connected: false,

  /**
   * Timer used for the connection.
   * @private
   * @type object
   */
  _connectTimer: null,

  _connectDefer: null,
  _disconnecter: null,

  /**
   * The WebConsoleActor ID.
   *
   * @private
   * @type string
   */
  _consoleActor: null,

  /**
   * Tells if the window.console object of the remote web page is the native
   * object or not.
   * @private
   * @type boolean
   */
  _hasNativeConsoleAPI: false,

  /**
   * Initialize a debugger client and connect it to the debugger server.
   *
   * @return object
   *         A promise object that is resolved/rejected based on the success of
   *         the connection initialization.
   */
  connect: function () {
    if (this._connectDefer) {
      return this._connectDefer.promise;
    }

    this._connectDefer = defer();

    let timeout = Services.prefs.getIntPref(PREF_CONNECTION_TIMEOUT);
    this._connectTimer = setTimeout(this._connectionTimeout, timeout);

    let connPromise = this._connectDefer.promise;
    connPromise.then(() => {
      clearTimeout(this._connectTimer);
      this._connectTimer = null;
    }, () => {
      clearTimeout(this._connectTimer);
      this._connectTimer = null;
    });

    let client = this.client = this.target.client;

    client.addListener("logMessage", this._onLogMessage);
    client.addListener("pageError", this._onPageError);
    client.addListener("consoleAPICall", this._onConsoleAPICall);
    client.addListener("fileActivity", this._onFileActivity);
    client.addListener("reflowActivity", this._onReflowActivity);
    client.addListener("serverLogCall", this._onServerLogCall);
    client.addListener("lastPrivateContextExited",
                       this._onLastPrivateContextExited);

    this.target.on("will-navigate", this._onTabNavigated);
    this.target.on("navigate", this._onTabNavigated);

    this._consoleActor = this.target.form.consoleActor;
    if (this.target.isTabActor) {
      let tab = this.target.form;
      this.webConsoleFrame.onLocationChange(tab.url, tab.title);
    }
    this._attachConsole();

    return connPromise;
  },

  /**
   * Connection timeout handler.
   * @private
   */
  _connectionTimeout: function () {
    let error = {
      error: "timeout",
      message: l10n.getStr("connectionTimeout"),
    };

    this._connectDefer.reject(error);
  },

  /**
   * Attach to the Web Console actor.
   * @private
   */
  _attachConsole: function () {
    let listeners = ["PageError", "ConsoleAPI", "NetworkActivity",
                     "FileActivity"];
    // Enable the forwarding of console messages to the parent process
    // when we open the Browser Console or Toolbox.
    if (this.target.chrome && !this.target.isAddon) {
      listeners.push("ContentProcessMessages");
    }
    this.client.attachConsole(this._consoleActor, listeners,
                              this._onAttachConsole);
  },

  /**
   * The "attachConsole" response handler.
   *
   * @private
   * @param object response
   *        The JSON response object received from the server.
   * @param object webConsoleClient
   *        The WebConsoleClient instance for the attached console, for the
   *        specific tab we work with.
   */
  _onAttachConsole: function (response, webConsoleClient) {
    if (response.error) {
      console.error("attachConsole failed: " + response.error + " " +
                    response.message);
      this._connectDefer.reject(response);
      return;
    }

    this.webConsoleClient = webConsoleClient;
    this._hasNativeConsoleAPI = response.nativeConsoleAPI;

    // There is no way to view response bodies from the Browser Console, so do
    // not waste the memory.
    let saveBodies = !this.webConsoleFrame.isBrowserConsole;
    this.webConsoleFrame.setSaveRequestAndResponseBodies(saveBodies);

    this.webConsoleClient.on("networkEvent", this._onNetworkEvent);
    this.webConsoleClient.on("networkEventUpdate", this._onNetworkEventUpdate);

    let msgs = ["PageError", "ConsoleAPI"];
    this.webConsoleClient.getCachedMessages(msgs, this._onCachedMessages);

    this.webConsoleFrame._onUpdateListeners();
  },

  /**
   * Dispatch a message add on the new frontend and emit an event for tests.
   */
  dispatchMessageAdd: function (packet) {
    this.webConsoleFrame.newConsoleOutput.dispatchMessageAdd(packet);
  },

  /**
   * Batched dispatch of messages.
   */
  dispatchMessagesAdd: function (packets) {
    this.webConsoleFrame.newConsoleOutput.dispatchMessagesAdd(packets);
  },

  /**
   * Dispatch a message event on the new frontend and emit an event for tests.
   */
  dispatchMessageUpdate: function (networkInfo, response) {
    this.webConsoleFrame.newConsoleOutput.dispatchMessageUpdate(networkInfo, response);
  },

  dispatchRequestUpdate: function (id, data) {
    this.webConsoleFrame.newConsoleOutput.dispatchRequestUpdate(id, data);
  },

  /**
   * The "cachedMessages" response handler.
   *
   * @private
   * @param object response
   *        The JSON response object received from the server.
   */
  _onCachedMessages: function (response) {
    if (response.error) {
      console.error("Web Console getCachedMessages error: " + response.error +
                    " " + response.message);
      this._connectDefer.reject(response);
      return;
    }

    if (!this._connectTimer) {
      // This happens if the promise is rejected (eg. a timeout), but the
      // connection attempt is successful, nonetheless.
      console.error("Web Console getCachedMessages error: invalid state.");
    }

    let messages =
      response.messages.concat(...this.webConsoleClient.getNetworkEvents());
    messages.sort((a, b) => a.timeStamp - b.timeStamp);

    if (this.webConsoleFrame.NEW_CONSOLE_OUTPUT_ENABLED) {
      this.dispatchMessagesAdd(messages);
    } else {
      this.webConsoleFrame.displayCachedMessages(messages);
    }
    if (!this._hasNativeConsoleAPI) {
      this.webConsoleFrame.logWarningAboutReplacedAPI();
    }

    this.connected = true;
    this._connectDefer.resolve(this);
  },

  /**
   * The "pageError" message type handler. We redirect any page errors to the UI
   * for displaying.
   *
   * @private
   * @param string type
   *        Message type.
   * @param object packet
   *        The message received from the server.
   */
  _onPageError: function (type, packet) {
    if (!this.webConsoleFrame || packet.from != this._consoleActor) {
      return;
    }
    if (this.webConsoleFrame.NEW_CONSOLE_OUTPUT_ENABLED) {
      this.dispatchMessageAdd(packet);
    } else {
      this.webConsoleFrame.handlePageError(packet.pageError);
    }
  },
  /**
   * The "logMessage" message type handler. We redirect any message to the UI
   * for displaying.
   *
   * @private
   * @param string type
   *        Message type.
   * @param object packet
   *        The message received from the server.
   */
  _onLogMessage: function (type, packet) {
    if (!this.webConsoleFrame || packet.from != this._consoleActor) {
      return;
    }
    if (this.webConsoleFrame.NEW_CONSOLE_OUTPUT_ENABLED) {
      this.dispatchMessageAdd(packet);
    } else {
      this.webConsoleFrame.handleLogMessage(packet);
    }
  },
  /**
   * The "consoleAPICall" message type handler. We redirect any message to
   * the UI for displaying.
   *
   * @private
   * @param string type
   *        Message type.
   * @param object packet
   *        The message received from the server.
   */
  _onConsoleAPICall: function (type, packet) {
    if (!this.webConsoleFrame || packet.from != this._consoleActor) {
      return;
    }
    if (this.webConsoleFrame.NEW_CONSOLE_OUTPUT_ENABLED) {
      this.dispatchMessageAdd(packet);
    } else {
      this.webConsoleFrame.handleConsoleAPICall(packet.message);
    }
  },
  /**
   * The "networkEvent" message type handler. We redirect any message to
   * the UI for displaying.
   *
   * @private
   * @param string type
   *        Message type.
   * @param object networkInfo
   *        The network request information.
   */
  _onNetworkEvent: function (type, networkInfo) {
    if (!this.webConsoleFrame) {
      return;
    }
    if (this.webConsoleFrame.NEW_CONSOLE_OUTPUT_ENABLED) {
      this.dispatchMessageAdd(networkInfo);
    } else {
      this.webConsoleFrame.handleNetworkEvent(networkInfo);
    }
  },
  /**
   * The "networkEventUpdate" message type handler. We redirect any message to
   * the UI for displaying.
   *
   * @private
   * @param string type
   *        Message type.
   * @param object response
   *        The update response received from the server.
   */
  _onNetworkEventUpdate: function (type, response) {
    if (!this.webConsoleFrame) {
      return;
    }
    let { packet, networkInfo } = response;
    if (this.webConsoleFrame.NEW_CONSOLE_OUTPUT_ENABLED) {
      this.dispatchMessageUpdate(networkInfo, response);
    } else {
      this.webConsoleFrame.handleNetworkEventUpdate(networkInfo, packet);
    }
  },
  /**
   * The "fileActivity" message type handler. We redirect any message to
   * the UI for displaying.
   *
   * @private
   * @param string type
   *        Message type.
   * @param object packet
   *        The message received from the server.
   */
  _onFileActivity: function (type, packet) {
    if (!this.webConsoleFrame || packet.from != this._consoleActor) {
      return;
    }
    if (this.webConsoleFrame.NEW_CONSOLE_OUTPUT_ENABLED) {
      // TODO: Implement for new console
    } else {
      this.webConsoleFrame.handleFileActivity(packet.uri);
    }
  },
  _onReflowActivity: function (type, packet) {
    if (!this.webConsoleFrame || packet.from != this._consoleActor) {
      return;
    }
    if (this.webConsoleFrame.NEW_CONSOLE_OUTPUT_ENABLED) {
      // TODO: Implement for new console
    } else {
      this.webConsoleFrame.handleReflowActivity(packet);
    }
  },
  /**
   * The "serverLogCall" message type handler. We redirect any message to
   * the UI for displaying.
   *
   * @private
   * @param string type
   *        Message type.
   * @param object packet
   *        The message received from the server.
   */
  _onServerLogCall: function (type, packet) {
    if (!this.webConsoleFrame || packet.from != this._consoleActor) {
      return;
    }
    if (this.webConsoleFrame.NEW_CONSOLE_OUTPUT_ENABLED) {
      // TODO: Implement for new console
    } else {
      this.webConsoleFrame.handleConsoleAPICall(packet.message);
    }
  },
  /**
   * The "lastPrivateContextExited" message type handler. When this message is
   * received the Web Console UI is cleared.
   *
   * @private
   * @param string type
   *        Message type.
   * @param object packet
   *        The message received from the server.
   */
  _onLastPrivateContextExited: function (type, packet) {
    if (this.webConsoleFrame && packet.from == this._consoleActor) {
      this.webConsoleFrame.jsterm.clearPrivateMessages();
    }
  },

  /**
   * The "will-navigate" and "navigate" event handlers. We redirect any message
   * to the UI for displaying.
   *
   * @private
   * @param string event
   *        Event type.
   * @param object packet
   *        The message received from the server.
   */
  _onTabNavigated: function (event, packet) {
    if (!this.webConsoleFrame) {
      return;
    }

    this.webConsoleFrame.handleTabNavigated(event, packet);
  },

  /**
   * Release an object actor.
   *
   * @param string actor
   *        The actor ID to send the request to.
   */
  releaseActor: function (actor) {
    if (this.client) {
      this.client.release(actor);
    }
  },

  /**
   * Disconnect the Web Console from the remote server.
   *
   * @return object
   *         A promise object that is resolved when disconnect completes.
   */
  disconnect: function () {
    if (this._disconnecter) {
      return this._disconnecter.promise;
    }

    this._disconnecter = defer();

    if (!this.client) {
      this._disconnecter.resolve(null);
      return this._disconnecter.promise;
    }

    this.client.removeListener("logMessage", this._onLogMessage);
    this.client.removeListener("pageError", this._onPageError);
    this.client.removeListener("consoleAPICall", this._onConsoleAPICall);
    this.client.removeListener("fileActivity", this._onFileActivity);
    this.client.removeListener("reflowActivity", this._onReflowActivity);
    this.client.removeListener("serverLogCall", this._onServerLogCall);
    this.client.removeListener("lastPrivateContextExited",
                               this._onLastPrivateContextExited);
    this.webConsoleClient.off("networkEvent", this._onNetworkEvent);
    this.webConsoleClient.off("networkEventUpdate", this._onNetworkEventUpdate);
    this.target.off("will-navigate", this._onTabNavigated);
    this.target.off("navigate", this._onTabNavigated);

    this.client = null;
    this.webConsoleClient = null;
    this.target = null;
    this.connected = false;
    this.webConsoleFrame = null;
    this._disconnecter.resolve(null);

    return this._disconnecter.promise;
  },
};

exports.WebConsoleConnectionProxy = WebConsoleConnectionProxy;
PK
!<e�e�NNEchrome/devtools/modules/devtools/client/webconsole/webconsole-l10n.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 {LocalizationHelper} = require("devtools/shared/l10n");
const helper = new LocalizationHelper("devtools/client/locales/webconsole.properties");

const l10n = {

  /**
   * Generates a formatted timestamp string for displaying in console messages.
   *
   * @param integer [milliseconds]
   *        Optional, allows you to specify the timestamp in milliseconds since
   *        the UNIX epoch.
   * @return string
   *         The timestamp formatted for display.
   */
  timestampString: function (milliseconds) {
    let d = new Date(milliseconds ? milliseconds : null);
    let hours = d.getHours(), minutes = d.getMinutes();
    let seconds = d.getSeconds();
    milliseconds = d.getMilliseconds();
    let parameters = [hours, minutes, seconds, milliseconds];
    return l10n.getFormatStr("timestampFormat", parameters);
  },

  /**
   * Retrieve a localized string.
   *
   * @param string name
   *        The string name you want from the Web Console string bundle.
   * @return string
   *         The localized string.
   */
  getStr: function (name) {
    try {
      return helper.getStr(name);
    } catch (ex) {
      console.error("Failed to get string: " + name);
      throw ex;
    }
  },

  /**
   * Retrieve a localized string formatted with values coming from the given
   * array.
   *
   * @param string name
   *        The string name you want from the Web Console string bundle.
   * @param array array
   *        The array of values you want in the formatted string.
   * @return string
   *         The formatted local string.
   */
  getFormatStr: function (name, array) {
    try {
      return helper.getFormatStr(name, ...array);
    } catch (ex) {
      console.error("Failed to format string: " + name);
      throw ex;
    }
  },
};

module.exports = l10n;
PK
!<�L�?{?{@chrome/devtools/modules/devtools/client/webconsole/webconsole.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 {Cc, Ci, Cu} = require("chrome");

const {Utils: WebConsoleUtils, CONSOLE_WORKER_IDS} =
  require("devtools/client/webconsole/utils");
const { getSourceNames } = require("devtools/client/shared/source-utils");
const ChromeUtils = require("ChromeUtils");
// browser-loader.js is a JSM without .jsm file extension, so it has to be loaded
// via ChromeUtils.import and not require() which would consider it as a CommonJS module
const { BrowserLoader } = ChromeUtils.import("resource://devtools/client/shared/browser-loader.js", {});

const promise = require("promise");
const defer = require("devtools/shared/defer");
const Services = require("Services");
const Telemetry = require("devtools/client/shared/telemetry");
const {PrefObserver} = require("devtools/client/shared/prefs");

loader.lazyServiceGetter(this, "clipboardHelper",
                         "@mozilla.org/widget/clipboardhelper;1",
                         "nsIClipboardHelper");
loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
loader.lazyRequireGetter(this, "ConsoleOutput", "devtools/client/webconsole/console-output", true);
loader.lazyRequireGetter(this, "Messages", "devtools/client/webconsole/console-output", true);
loader.lazyRequireGetter(this, "EnvironmentClient", "devtools/shared/client/environment-client");
loader.lazyRequireGetter(this, "ObjectClient", "devtools/shared/client/object-client");
loader.lazyRequireGetter(this, "system", "devtools/shared/system");
loader.lazyRequireGetter(this, "JSTerm", "devtools/client/webconsole/jsterm", true);
loader.lazyRequireGetter(this, "gSequenceId", "devtools/client/webconsole/jsterm", true);
loader.lazyImporter(this, "VariablesView", "resource://devtools/client/shared/widgets/VariablesView.jsm");
loader.lazyImporter(this, "VariablesViewController", "resource://devtools/client/shared/widgets/VariablesViewController.jsm");
loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
loader.lazyRequireGetter(this, "KeyShortcuts", "devtools/client/shared/key-shortcuts");
loader.lazyRequireGetter(this, "ZoomKeys", "devtools/client/shared/zoom-keys");
loader.lazyRequireGetter(this, "WebConsoleConnectionProxy", "devtools/client/webconsole/webconsole-connection-proxy", true);

const {PluralForm} = require("devtools/shared/plural-form");
const l10n = require("devtools/client/webconsole/webconsole-l10n");

const XHTML_NS = "http://www.w3.org/1999/xhtml";

const MIXED_CONTENT_LEARN_MORE = "https://developer.mozilla.org/docs/Web/Security/Mixed_content";

const IGNORED_SOURCE_URLS = ["debugger eval code"];

// The amount of time in milliseconds that we wait before performing a live
// search.
const SEARCH_DELAY = 200;

// The number of lines that are displayed in the console output by default, for
// each category. The user can change this number by adjusting the hidden
// "devtools.hud.loglimit.{network,cssparser,exception,console}" preferences.
const DEFAULT_LOG_LIMIT = 1000;

// The various categories of messages. We start numbering at zero so we can
// use these as indexes into the MESSAGE_PREFERENCE_KEYS matrix below.
const CATEGORY_NETWORK = 0;
const CATEGORY_CSS = 1;
const CATEGORY_JS = 2;
const CATEGORY_WEBDEV = 3;
// always on
const CATEGORY_INPUT = 4;
// always on
const CATEGORY_OUTPUT = 5;
const CATEGORY_SECURITY = 6;
const CATEGORY_SERVER = 7;

// The possible message severities. As before, we start at zero so we can use
// these as indexes into MESSAGE_PREFERENCE_KEYS.
const SEVERITY_ERROR = 0;
const SEVERITY_WARNING = 1;
const SEVERITY_INFO = 2;
const SEVERITY_LOG = 3;

// The fragment of a CSS class name that identifies each category.
const CATEGORY_CLASS_FRAGMENTS = [
  "network",
  "cssparser",
  "exception",
  "console",
  "input",
  "output",
  "security",
  "server",
];

// The fragment of a CSS class name that identifies each severity.
const SEVERITY_CLASS_FRAGMENTS = [
  "error",
  "warn",
  "info",
  "log",
];

// The preference keys to use for each category/severity combination, indexed
// first by category (rows) and then by severity (columns) in the following
// order:
//
// [ Error, Warning, Info, Log ]
//
// Most of these rather idiosyncratic names are historical and predate the
// division of message type into "category" and "severity".
const MESSAGE_PREFERENCE_KEYS = [
  // Network
  [ "network", "netwarn", "netxhr", "networkinfo", ],
  // CSS
  [ "csserror", "cssparser", null, "csslog", ],
  // JS
  [ "exception", "jswarn", null, "jslog", ],
  // Web Developer
  [ "error", "warn", "info", "log", ],
  // Input
  [ null, null, null, null, ],
  // Output
  [ null, null, null, null, ],
  // Security
  [ "secerror", "secwarn", null, null, ],
  // Server Logging
  [ "servererror", "serverwarn", "serverinfo", "serverlog", ],
];

// A mapping from the console API log event levels to the Web Console
// severities.
const LEVELS = {
  error: SEVERITY_ERROR,
  exception: SEVERITY_ERROR,
  assert: SEVERITY_ERROR,
  warn: SEVERITY_WARNING,
  info: SEVERITY_INFO,
  log: SEVERITY_LOG,
  clear: SEVERITY_LOG,
  trace: SEVERITY_LOG,
  table: SEVERITY_LOG,
  debug: SEVERITY_LOG,
  dir: SEVERITY_LOG,
  dirxml: SEVERITY_LOG,
  group: SEVERITY_LOG,
  groupCollapsed: SEVERITY_LOG,
  groupEnd: SEVERITY_LOG,
  time: SEVERITY_LOG,
  timeEnd: SEVERITY_LOG,
  count: SEVERITY_LOG
};

// This array contains the prefKey for the workers and it must keep them in the
// same order as CONSOLE_WORKER_IDS
const WORKERTYPES_PREFKEYS =
  [ "sharedworkers", "serviceworkers", "windowlessworkers" ];

// The lowest HTTP response code (inclusive) that is considered an error.
const MIN_HTTP_ERROR_CODE = 400;
// The highest HTTP response code (inclusive) that is considered an error.
const MAX_HTTP_ERROR_CODE = 599;

// The indent of a console group in pixels.
const GROUP_INDENT = 12;

// The number of messages to display in a single display update. If we display
// too many messages at once we slow down the Firefox UI too much.
const MESSAGES_IN_INTERVAL = DEFAULT_LOG_LIMIT;

// The delay (in milliseconds) between display updates - tells how often we
// should *try* to push new messages to screen. This value is optimistic,
// updates won't always happen. Keep this low so the Web Console output feels
// live.
const OUTPUT_INTERVAL = 20;

// The maximum amount of time (in milliseconds) that can be spent doing cleanup
// inside of the flush output callback.  If things don't get cleaned up in this
// time, then it will start again the next time it is called.
const MAX_CLEANUP_TIME = 10;

// When the output queue has more than MESSAGES_IN_INTERVAL items we throttle
// output updates to this number of milliseconds. So during a lot of output we
// update every N milliseconds given here.
const THROTTLE_UPDATES = 1000;

// The preference prefix for all of the Web Console filters.
const FILTER_PREFS_PREFIX = "devtools.webconsole.filter.";

const PREF_PERSISTLOG = "devtools.webconsole.persistlog";
const PREF_MESSAGE_TIMESTAMP = "devtools.webconsole.timestampMessages";
const PREF_NEW_FRONTEND_ENABLED = "devtools.webconsole.new-frontend-enabled";

/**
 * A WebConsoleFrame instance is an interactive console initialized *per target*
 * that displays console log data as well as provides an interactive terminal to
 * manipulate the target's document content.
 *
 * The WebConsoleFrame is responsible for the actual Web Console UI
 * implementation.
 *
 * @constructor
 * @param object webConsoleOwner
 *        The WebConsole owner object.
 */
function WebConsoleFrame(webConsoleOwner) {
  this.owner = webConsoleOwner;
  this.hudId = this.owner.hudId;
  this.isBrowserConsole = this.owner._browserConsole;

  this.window = this.owner.iframeWindow;

  this._repeatNodes = {};
  this._outputQueue = [];
  this._itemDestroyQueue = [];
  this._pruneCategoriesQueue = {};
  this.filterPrefs = {};

  this.output = new ConsoleOutput(this);

  this.unmountMessage = this.unmountMessage.bind(this);
  this._toggleFilter = this._toggleFilter.bind(this);
  this.resize = this.resize.bind(this);
  this._onPanelSelected = this._onPanelSelected.bind(this);
  this._flushMessageQueue = this._flushMessageQueue.bind(this);
  this._onToolboxPrefChanged = this._onToolboxPrefChanged.bind(this);
  this._onUpdateListeners = this._onUpdateListeners.bind(this);

  this._outputTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
  this._outputTimerInitialized = false;

  let toolbox = gDevTools.getToolbox(this.owner.target);
  let {require} = BrowserLoader({
    window: this.window,
    useOnlyShared: true,
    // The toolbox isn't available for the browser console.
    commonLibRequire: toolbox ? toolbox.browserRequire : null,
  });

  this.React = require("devtools/client/shared/vendor/react");
  this.ReactDOM = require("devtools/client/shared/vendor/react-dom");
  this.FrameView = this.React.createFactory(require("devtools/client/shared/components/Frame"));
  this.StackTraceView = this.React.createFactory(require("devtools/client/shared/components/StackTrace"));

  this._telemetry = new Telemetry();

  EventEmitter.decorate(this);
}
exports.WebConsoleFrame = WebConsoleFrame;

WebConsoleFrame.prototype = {
  /**
   * The WebConsole instance that owns this frame.
   * @see hudservice.js::WebConsole
   * @type object
   */
  owner: null,

  /**
   * Proxy between the Web Console and the remote Web Console instance. This
   * object holds methods used for connecting, listening and disconnecting from
   * the remote server, using the remote debugging protocol.
   *
   * @see WebConsoleConnectionProxy
   * @type object
   */
  proxy: null,

  /**
   * Getter for the xul:popupset that holds any popups we open.
   * @type nsIDOMElement
   */
  get popupset() {
    return this.owner.mainPopupSet;
  },

  /**
   * Holds the initialization promise object.
   * @private
   * @type object
   */
  _initDefer: null,

  /**
   * Last time when we displayed any message in the output.
   *
   * @private
   * @type number
   *       Timestamp in milliseconds since the Unix epoch.
   */
  _lastOutputFlush: 0,

  /**
   * Message nodes are stored here in a queue for later display.
   *
   * @private
   * @type array
   */
  _outputQueue: null,

  /**
   * Keep track of the categories we need to prune from time to time.
   *
   * @private
   * @type array
   */
  _pruneCategoriesQueue: null,

  /**
   * Function invoked whenever the output queue is emptied. This is used by some
   * tests.
   *
   * @private
   * @type function
   */
  _flushCallback: null,

  /**
   * Timer used for flushing the messages output queue.
   *
   * @private
   * @type nsITimer
   */
  _outputTimer: null,
  _outputTimerInitialized: null,

  /**
   * Store for tracking repeated nodes.
   * @private
   * @type object
   */
  _repeatNodes: null,

  /**
   * Preferences for filtering messages by type.
   * @see this._initDefaultFilterPrefs()
   * @type object
   */
  filterPrefs: null,

  /**
   * Prefix used for filter preferences.
   * @private
   * @type string
   */
  _filterPrefsPrefix: FILTER_PREFS_PREFIX,

  /**
   * The nesting depth of the currently active console group.
   */
  groupDepth: 0,

  /**
   * The current target location.
   * @type string
   */
  contentLocation: "",

  /**
   * The JSTerm object that manage the console's input.
   * @see JSTerm
   * @type object
   */
  jsterm: null,

  /**
   * The element that holds all of the messages we display.
   * @type nsIDOMElement
   */
  outputNode: null,

  /**
   * The ConsoleOutput instance that manages all output.
   * @type object
   */
  output: null,

  /**
   * The input element that allows the user to filter messages by string.
   * @type nsIDOMElement
   */
  filterBox: null,

  /**
   * Getter for the debugger WebConsoleClient.
   * @type object
   */
  get webConsoleClient() {
    return this.proxy ? this.proxy.webConsoleClient : null;
  },

  _destroyer: null,

  _saveRequestAndResponseBodies: true,
  _throttleData: null,

  // Chevron width at the starting of Web Console's input box.
  _chevronWidth: 0,
  // Width of the monospace characters in Web Console's input box.
  _inputCharWidth: 0,

  /**
   * Setter for saving of network request and response bodies.
   *
   * @param boolean value
   *        The new value you want to set.
   */
  setSaveRequestAndResponseBodies: function (value) {
    if (!this.webConsoleClient) {
      // Don't continue if the webconsole disconnected.
      return promise.resolve(null);
    }

    let deferred = defer();
    let newValue = !!value;
    let toSet = {
      "NetworkMonitor.saveRequestAndResponseBodies": newValue,
    };

    // Make sure the web console client connection is established first.
    this.webConsoleClient.setPreferences(toSet, response => {
      if (!response.error) {
        this._saveRequestAndResponseBodies = newValue;
        deferred.resolve(response);
      } else {
        deferred.reject(response.error);
      }
    });

    return deferred.promise;
  },

  /**
   * Setter for throttling data.
   *
   * @param boolean value
   *        The new value you want to set; @see NetworkThrottleManager.
   */
  setThrottleData: function(value) {
    if (!this.webConsoleClient) {
      // Don't continue if the webconsole disconnected.
      return promise.resolve(null);
    }

    let deferred = defer();
    let toSet = {
      "NetworkMonitor.throttleData": value,
    };

    // Make sure the web console client connection is established first.
    this.webConsoleClient.setPreferences(toSet, response => {
      if (!response.error) {
        this._throttleData = value;
        deferred.resolve(response);
      } else {
        deferred.reject(response.error);
      }
    });

    return deferred.promise;
  },

  /**
   * Getter for the persistent logging preference.
   * @type boolean
   */
  get persistLog() {
    // For the browser console, we receive tab navigation
    // when the original top level window we attached to is closed,
    // but we don't want to reset console history and just switch to
    // the next available window.
    return this.isBrowserConsole ||
           Services.prefs.getBoolPref(PREF_PERSISTLOG);
  },

  /**
   * Initialize the WebConsoleFrame instance.
   * @return object
   *         A promise object that resolves once the frame is ready to use.
   */
  init: function () {
    this._initUI();
    let connectionInited = this._initConnection();

    // Don't reject if the history fails to load for some reason.
    // This would be fine, the panel will just start with empty history.
    let allReady = this.jsterm.historyLoaded.catch(() => {}).then(() => {
      return connectionInited;
    });

    // This notification is only used in tests. Don't chain it onto
    // the returned promise because the console panel needs to be attached
    // to the toolbox before the web-console-created event is receieved.
    let notifyObservers = () => {
      let id = WebConsoleUtils.supportsString(this.hudId);
      Services.obs.notifyObservers(id, "web-console-created");
    };
    allReady.then(notifyObservers, notifyObservers);
    return allReady;
  },
  /**
   * Connect to the server using the remote debugging protocol.
   *
   * @private
   * @return object
   *         A promise object that is resolved/reject based on the connection
   *         result.
   */
  _initConnection: function () {
    if (this._initDefer) {
      return this._initDefer.promise;
    }

    this._initDefer = defer();
    this.proxy = new WebConsoleConnectionProxy(this, this.owner.target);

    this.proxy.connect().then(() => {
      // on success
      this._initDefer.resolve(this);
    }, (reason) => {
      // on failure
      let node = this.createMessageNode(CATEGORY_JS, SEVERITY_ERROR,
                                        reason.error + ": " + reason.message);
      this.outputMessage(CATEGORY_JS, node, [reason]);
      this._initDefer.reject(reason);
    });

    return this._initDefer.promise;
  },
  /**
   * Find the Web Console UI elements and setup event listeners as needed.
   * @private
   */
  _initUI: function () {
    this.document = this.window.document;
    this.rootElement = this.document.documentElement;
    this.outputNode = this.document.getElementById("output-container");
    this.outputWrapper = this.document.getElementById("output-wrapper");
    this.completeNode = this.document.querySelector(".jsterm-complete-node");
    this.inputNode = this.document.querySelector(".jsterm-input-node");
    // In the old frontend, the area that scrolls is outputWrapper, but in the new
    // frontend this will be reassigned.
    this.outputScroller = this.outputWrapper;
    this.jsterm = new JSTerm(this);
    this.jsterm.init();
    let toolbox = gDevTools.getToolbox(this.owner.target);
    // Register the controller to handle "select all" properly.
    this._commandController = new CommandController(this);
    this.window.controllers.insertControllerAt(0, this._commandController);

    this._contextMenuHandler = new ConsoleContextMenu(this);

    this._initDefaultFilterPrefs();
    this.filterBox = this.document.querySelector(".hud-filter-box");
    this._setFilterTextBoxEvents();
    this._initFilterButtons();

    let clearButton =
      this.document.getElementsByClassName("webconsole-clear-console-button")[0];
    clearButton.addEventListener("command", () => {
      this.owner._onClearButton();
      this.jsterm.clearOutput(true);
    });
    this.resize();
    this.window.addEventListener("resize", this.resize, true);
    this.jsterm.on("sidebar-opened", this.resize);
    this.jsterm.on("sidebar-closed", this.resize);
    if (toolbox) {
      toolbox.on("webconsole-selected", this._onPanelSelected);
    }

    /*
     * Focus the input line whenever the output area is clicked.
     */
    this.outputWrapper.addEventListener("click", (event) => {
      // Do not focus on middle/right-click or 2+ clicks.
      if (event.detail !== 1 || event.button !== 0) {
        return;
      }

      // Do not focus if something is selected
      let selection = this.window.getSelection();
      if (selection && !selection.isCollapsed) {
        return;
      }
      // Do not focus if a link was clicked
      if (event.target.nodeName.toLowerCase() === "a" ||
          event.target.parentNode.nodeName.toLowerCase() === "a") {
        return;
      }
      this.jsterm.focus();
    });
    // Toggle the timestamp on preference change
    this._prefObserver = new PrefObserver("");
    this._prefObserver.on(PREF_MESSAGE_TIMESTAMP, this._onToolboxPrefChanged);
    this._onToolboxPrefChanged();
    this._initShortcuts();

    // focus input node
    this.jsterm.focus();
  },
  /**
   * Resizes the output node to fit the output wrapped.
   * We need this because it makes the layout a lot faster than
   * using -moz-box-flex and 100% width.  See Bug 1237368.
   */
  resize: function () {
    this.outputNode.style.width = this.outputWrapper.clientWidth + "px";
  },
  /**
   * Sets the focus to JavaScript input field when the web console tab is
   * selected or when there is a split console present.
   * @private
   */
  _onPanelSelected: function () {
    this.jsterm.focus();
  },

  /**
   * Initialize the default filter preferences.
   * @private
   */
  _initDefaultFilterPrefs: function () {
    let prefs = ["network", "networkinfo", "csserror", "cssparser", "csslog",
                 "exception", "jswarn", "jslog", "error", "info", "warn", "log",
                 "secerror", "secwarn", "netwarn", "netxhr", "sharedworkers",
                 "serviceworkers", "windowlessworkers", "servererror",
                 "serverwarn", "serverinfo", "serverlog"];

    for (let pref of prefs) {
      this.filterPrefs[pref] = Services.prefs.getBoolPref(
        this._filterPrefsPrefix + pref);
    }
  },

  _initShortcuts: function() {
    var shortcuts = new KeyShortcuts({
      window: this.window
    });

    shortcuts.on(l10n.getStr("webconsole.find.key"),
                 event => {
                   this.filterBox.focus();
                   event.preventDefault();
                 });

    let clearShortcut;
    if (system.constants.platform === "macosx") {
      clearShortcut = l10n.getStr("webconsole.clear.keyOSX");
    } else {
      clearShortcut = l10n.getStr("webconsole.clear.key");
    }
    shortcuts.on(clearShortcut,
                 () => this.jsterm.clearOutput(true));

    if (this.isBrowserConsole) {
      shortcuts.on(l10n.getStr("webconsole.close.key"),
                   this.window.close.bind(this.window));

      ZoomKeys.register(this.window);
    }
  },

  /**
   * Attach / detach reflow listeners depending on the checked status
   * of the `CSS > Log` menuitem.
   *
   * @param function [callback=null]
   *        Optional function to invoke when the listener has been
   *        added/removed.
   */
  _updateReflowActivityListener: function (callback) {
    if (this.webConsoleClient) {
      let pref = this._filterPrefsPrefix + "csslog";
      if (Services.prefs.getBoolPref(pref)) {
        this.webConsoleClient.startListeners(["ReflowActivity"], callback);
      } else {
        this.webConsoleClient.stopListeners(["ReflowActivity"], callback);
      }
    }
  },

  /**
   * Attach / detach server logging listener depending on the filter
   * preferences. If the user isn't interested in the server logs at
   * all the listener is not registered.
   *
   * @param function [callback=null]
   *        Optional function to invoke when the listener has been
   *        added/removed.
   */
  _updateServerLoggingListener: function (callback) {
    if (!this.webConsoleClient) {
      return null;
    }

    let startListener = false;
    let prefs = ["servererror", "serverwarn", "serverinfo", "serverlog"];
    for (let i = 0; i < prefs.length; i++) {
      if (this.filterPrefs[prefs[i]]) {
        startListener = true;
        break;
      }
    }

    if (startListener) {
      this.webConsoleClient.startListeners(["ServerLogging"], callback);
    } else {
      this.webConsoleClient.stopListeners(["ServerLogging"], callback);
    }
  },

  /**
   * Sets the events for the filter input field.
   * @private
   */
  _setFilterTextBoxEvents: function () {
    let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
    let timerEvent = this.adjustVisibilityOnSearchStringChange.bind(this);

    let onChange = function _onChange() {
      // To improve responsiveness, we let the user finish typing before we
      // perform the search.
      timer.cancel();
      timer.initWithCallback(timerEvent, SEARCH_DELAY,
                             Ci.nsITimer.TYPE_ONE_SHOT);
    };

    this.filterBox.addEventListener("command", onChange);
    this.filterBox.addEventListener("input", onChange);
  },

  /**
   * Creates one of the filter buttons on the toolbar.
   *
   * @private
   * @param nsIDOMNode aParent
   *        The node to which the filter button should be appended.
   * @param object aDescriptor
   *        A descriptor that contains info about the button. Contains "name",
   *        "category", and "prefKey" properties, and optionally a "severities"
   *        property.
   */
  _initFilterButtons: function () {
    let categories = this.document
                     .querySelectorAll(".webconsole-filter-button[category]");
    Array.forEach(categories, function (button) {
      button.addEventListener("contextmenu", () => {
        button.open = true;
      });
      button.addEventListener("click", this._toggleFilter);

      let someChecked = false;
      let severities = button.querySelectorAll("menuitem[prefKey]");
      Array.forEach(severities, function (menuItem) {
        menuItem.addEventListener("command", this._toggleFilter);

        let prefKey = menuItem.getAttribute("prefKey");
        let checked = this.filterPrefs[prefKey];
        menuItem.setAttribute("checked", checked);
        someChecked = someChecked || checked;
      }, this);

      button.setAttribute("checked", someChecked);
      button.setAttribute("aria-pressed", someChecked);
    }, this);

    if (!this.isBrowserConsole) {
      // The Browser Console displays nsIConsoleMessages which are messages that
      // end up in the JS category, but they are not errors or warnings, they
      // are just log messages. The Web Console does not show such messages.
      let jslog = this.document.querySelector("menuitem[prefKey=jslog]");
      jslog.hidden = true;
    }

    if (Services.appinfo.OS == "Darwin") {
      let net = this.document.querySelector("toolbarbutton[category=net]");
      let accesskey = net.getAttribute("accesskeyMacOSX");
      net.setAttribute("accesskey", accesskey);

      let logging =
        this.document.querySelector("toolbarbutton[category=logging]");
      logging.removeAttribute("accesskey");
      let serverLogging =
        this.document.querySelector("toolbarbutton[category=server]");
      serverLogging.removeAttribute("accesskey");
    }
  },
  /**
   * The event handler that is called whenever a user switches a filter on or
   * off.
   *
   * @private
   * @param nsIDOMEvent event
   *        The event that triggered the filter change.
   */
  _toggleFilter: function (event) {
    let target = event.target;
    let tagName = target.tagName;
    // Prevent toggle if generated from a contextmenu event (right click)
    let isRightClick = (event.button === 2);
    if (tagName != event.currentTarget.tagName || isRightClick) {
      return;
    }

    switch (tagName) {
      case "toolbarbutton": {
        let originalTarget = event.originalTarget;
        let classes = originalTarget.classList;

        if (originalTarget.localName !== "toolbarbutton") {
          // Oddly enough, the click event is sent to the menu button when
          // selecting a menu item with the mouse. Detect this case and bail
          // out.
          break;
        }

        if (!classes.contains("toolbarbutton-menubutton-button") &&
            originalTarget.getAttribute("type") === "menu-button") {
          // This is a filter button with a drop-down. The user clicked the
          // drop-down, so do nothing. (The menu will automatically appear
          // without our intervention.)
          break;
        }

        // Toggle on the targeted filter button, and if the user alt clicked,
        // toggle off all other filter buttons and their associated filters.
        let state = target.getAttribute("checked") !== "true";
        if (event.getModifierState("Alt")) {
          let buttons = this.document
                        .querySelectorAll(".webconsole-filter-button");
          Array.forEach(buttons, (button) => {
            if (button !== target) {
              button.setAttribute("checked", false);
              button.setAttribute("aria-pressed", false);
              this._setMenuState(button, false);
            }
          });
          state = true;
        }
        target.setAttribute("checked", state);
        target.setAttribute("aria-pressed", state);

        // This is a filter button with a drop-down, and the user clicked the
        // main part of the button. Go through all the severities and toggle
        // their associated filters.
        this._setMenuState(target, state);

        // CSS reflow logging can decrease web page performance.
        // Make sure the option is always unchecked when the CSS filter button
        // is selected. See bug 971798.
        if (target.getAttribute("category") == "css" && state) {
          let csslogMenuItem = target.querySelector("menuitem[prefKey=csslog]");
          csslogMenuItem.setAttribute("checked", false);
          this.setFilterState("csslog", false);
        }

        break;
      }

      case "menuitem": {
        let state = target.getAttribute("checked") !== "true";
        target.setAttribute("checked", state);

        let prefKey = target.getAttribute("prefKey");
        this.setFilterState(prefKey, state);

        // Adjust the state of the button appropriately.
        let menuPopup = target.parentNode;

        let someChecked = false;
        let menuItem = menuPopup.firstChild;
        while (menuItem) {
          if (menuItem.hasAttribute("prefKey") &&
              menuItem.getAttribute("checked") === "true") {
            someChecked = true;
            break;
          }
          menuItem = menuItem.nextSibling;
        }
        let toolbarButton = menuPopup.parentNode;
        toolbarButton.setAttribute("checked", someChecked);
        toolbarButton.setAttribute("aria-pressed", someChecked);
        break;
      }
    }
  },

  /**
   * Set the menu attributes for a specific toggle button.
   *
   * @private
   * @param XULElement target
   *        Button with drop down items to be toggled.
   * @param boolean state
   *        True if the menu item is being toggled on, and false otherwise.
   */
  _setMenuState: function (target, state) {
    let menuItems = target.querySelectorAll("menuitem");
    Array.forEach(menuItems, (item) => {
      item.setAttribute("checked", state);
      let prefKey = item.getAttribute("prefKey");
      this.setFilterState(prefKey, state);
    });
  },

  /**
   * Set the filter state for a specific toggle button.
   *
   * @param string toggleType
   * @param boolean state
   * @returns void
   */
  setFilterState: function (toggleType, state) {
    this.filterPrefs[toggleType] = state;
    this.adjustVisibilityForMessageType(toggleType, state);

    Services.prefs.setBoolPref(this._filterPrefsPrefix + toggleType, state);

    if (this._updateListenersTimeout) {
      clearTimeout(this._updateListenersTimeout);
    }

    this._updateListenersTimeout = setTimeout(
      this._onUpdateListeners, 200);
  },

  /**
   * Get the filter state for a specific toggle button.
   *
   * @param string toggleType
   * @returns boolean
   */
  getFilterState: function (toggleType) {
    return this.filterPrefs[toggleType];
  },

  /**
   * Called when a logging filter changes. Allows to stop/start
   * listeners according to the current filter state.
   */
  _onUpdateListeners: function () {
    this._updateReflowActivityListener();
    this._updateServerLoggingListener();
  },

  /**
   * Check that the passed string matches the filter arguments.
   *
   * @param String str
   *        to search for filter words in.
   * @param String filter
   *        is a string containing all of the words to filter on.
   * @returns boolean
   */
  stringMatchesFilters: function (str, filter) {
    if (!filter || !str) {
      return true;
    }

    let searchStr = str.toLowerCase();
    let filterStrings = filter.toLowerCase().split(/\s+/);
    return !filterStrings.some(function (f) {
      return !searchStr.includes(f);
    });
  },

  /**
   * Turns the display of log nodes on and off appropriately to reflect the
   * adjustment of the message type filter named by @prefKey.
   *
   * @param string prefKey
   *        The preference key for the message type being filtered: one of the
   *        values in the MESSAGE_PREFERENCE_KEYS table.
   * @param boolean state
   *        True if the filter named by @messageType is being turned on; false
   *        otherwise.
   * @returns void
   */
  adjustVisibilityForMessageType: function (prefKey, state) {
    let outputNode = this.outputNode;
    let doc = this.document;

    // Look for message nodes (".message") with the given preference key
    // (filter="error", filter="cssparser", etc.) and add or remove the
    // "filtered-by-type" class, which turns on or off the display.

    let attribute = !WORKERTYPES_PREFKEYS.includes(prefKey)
                      ? "filter" : "workerType";

    let selector = "[" + attribute + "='" + prefKey + "'].message";
    let result = outputNode.querySelectorAll(selector);
    for (let node of result) {
      if (state) {
        node.classList.remove("filtered-by-type");
      } else {
        node.classList.add("filtered-by-type");
      }
    }
  },

  /**
   * Turns the display of log nodes on and off appropriately to reflect the
   * adjustment of the search string.
   */
  adjustVisibilityOnSearchStringChange: function () {
    let nodes = this.outputNode.getElementsByClassName("message");
    let searchString = this.filterBox.value;

    for (let i = 0, n = nodes.length; i < n; ++i) {
      let node = nodes[i];

      // hide nodes that match the strings
      let text = node.textContent;

      // if the text matches the words in aSearchString...
      if (this.stringMatchesFilters(text, searchString)) {
        node.classList.remove("filtered-by-string");
      } else {
        node.classList.add("filtered-by-string");
      }
    }

    this.resize();
  },

  /**
   * Applies the user's filters to a newly-created message node via CSS
   * classes.
   *
   * @param nsIDOMNode node
   *        The newly-created message node.
   * @return boolean
   *         True if the message was filtered or false otherwise.
   */
  filterMessageNode: function (node) {
    let isFiltered = false;

    // Filter by the message type.
    let prefKey = MESSAGE_PREFERENCE_KEYS[node.category][node.severity];
    if (prefKey && !this.getFilterState(prefKey)) {
      // The node is filtered by type.
      node.classList.add("filtered-by-type");
      isFiltered = true;
    }

    // Filter by worker type
    if ("workerType" in node && !this.getFilterState(node.workerType)) {
      node.classList.add("filtered-by-type");
      isFiltered = true;
    }

    // Filter on the search string.
    let search = this.filterBox.value;
    let text = node.clipboardText;

    // if string matches the filter text
    if (!this.stringMatchesFilters(text, search)) {
      node.classList.add("filtered-by-string");
      isFiltered = true;
    }

    if (isFiltered && node.classList.contains("inlined-variables-view")) {
      node.classList.add("hidden-message");
    }

    return isFiltered;
  },

  /**
   * Merge the attributes of repeated nodes.
   *
   * @param nsIDOMNode original
   *        The Original Node. The one being merged into.
   */
  mergeFilteredMessageNode: function (original) {
    let repeatNode = original.getElementsByClassName("message-repeats")[0];
    if (!repeatNode) {
      // no repeat node, return early.
      return;
    }

    let occurrences = parseInt(repeatNode.getAttribute("value"), 10) + 1;
    repeatNode.setAttribute("value", occurrences);
    repeatNode.textContent = occurrences;
    let str = l10n.getStr("messageRepeats.tooltip2");
    repeatNode.title = PluralForm.get(occurrences, str)
                       .replace("#1", occurrences);
  },

  /**
   * Filter the message node from the output if it is a repeat.
   *
   * @private
   * @param nsIDOMNode node
   *        The message node to be filtered or not.
   * @returns nsIDOMNode|null
   *          Returns the duplicate node if the message was filtered, null
   *          otherwise.
   */
  _filterRepeatedMessage: function (node) {
    let repeatNode = node.getElementsByClassName("message-repeats")[0];
    if (!repeatNode) {
      return null;
    }

    let uid = repeatNode._uid;
    let dupeNode = null;

    if (node.category == CATEGORY_CSS ||
        node.category == CATEGORY_SECURITY) {
      dupeNode = this._repeatNodes[uid];
      if (!dupeNode) {
        this._repeatNodes[uid] = node;
      }
    } else if ((node.category == CATEGORY_WEBDEV ||
                node.category == CATEGORY_JS) &&
               node.category != CATEGORY_NETWORK &&
               !node.classList.contains("inlined-variables-view")) {
      let lastMessage = this.outputNode.lastChild;
      if (!lastMessage) {
        return null;
      }

      let lastRepeatNode =
        lastMessage.getElementsByClassName("message-repeats")[0];
      if (lastRepeatNode && lastRepeatNode._uid == uid) {
        dupeNode = lastMessage;
      }
    }

    if (dupeNode) {
      this.mergeFilteredMessageNode(dupeNode);
      // Even though this node was never rendered, we create the location
      // nodes before rendering, so we still have to clean up any
      // React components
      this.unmountMessage(node);
      return dupeNode;
    }

    return null;
  },

  /**
   * Display cached messages that may have been collected before the UI is
   * displayed.
   *
   * @param array remoteMessages
   *        Array of cached messages coming from the remote Web Console
   *        content instance.
   */
  displayCachedMessages: function (remoteMessages) {
    if (!remoteMessages.length) {
      return;
    }

    remoteMessages.forEach(function (message) {
      switch (message._type) {
        case "PageError": {
          let category = Utils.categoryForScriptError(message);
          this.outputMessage(category, this.reportPageError,
                             [category, message]);
          break;
        }
        case "LogMessage":
          this.handleLogMessage(message);
          break;
        case "ConsoleAPI":
          this.outputMessage(CATEGORY_WEBDEV, this.logConsoleAPIMessage,
                             [message]);
          break;
        case "NetworkEvent":
          this.outputMessage(CATEGORY_NETWORK, this.logNetEvent, [message]);
          break;
      }
    }, this);
  },

  /**
   * Logs a message to the Web Console that originates from the Web Console
   * server.
   *
   * @param object message
   *        The message received from the server.
   * @return nsIDOMElement|null
   *         The message element to display in the Web Console output.
   */
  logConsoleAPIMessage: function (message) {
    let body = null;
    let clipboardText = null;
    let sourceURL = message.filename;
    let sourceLine = message.lineNumber;
    let level = message.level;
    let args = message.arguments;
    let objectActors = new Set();
    let node = null;

    // Gather the actor IDs.
    args.forEach((value) => {
      if (WebConsoleUtils.isActorGrip(value)) {
        objectActors.add(value.actor);
      }
    });

    switch (level) {
      case "log":
      case "info":
      case "warn":
      case "error":
      case "exception":
      case "assert":
      case "debug": {
        let msg = new Messages.ConsoleGeneric(message);
        node = msg.init(this.output).render().element;
        break;
      }
      case "table": {
        let msg = new Messages.ConsoleTable(message);
        node = msg.init(this.output).render().element;
        break;
      }
      case "trace": {
        let msg = new Messages.ConsoleTrace(message);
        node = msg.init(this.output).render().element;
        break;
      }
      case "clear": {
        body = l10n.getStr("consoleCleared");
        clipboardText = body;
        break;
      }
      case "dir": {
        body = { arguments: args };
        let clipboardArray = [];
        args.forEach((value) => {
          clipboardArray.push(VariablesView.getString(value));
        });
        clipboardText = clipboardArray.join(" ");
        break;
      }
      case "dirxml": {
        // We just alias console.dirxml() with console.log().
        message.level = "log";
        return this.logConsoleAPIMessage(message);
      }
      case "group":
      case "groupCollapsed":
        clipboardText = body = message.groupName;
        this.groupDepth++;
        break;

      case "groupEnd":
        if (this.groupDepth > 0) {
          this.groupDepth--;
        }
        break;

      case "time": {
        let timer = message.timer;
        if (!timer) {
          return null;
        }
        if (timer.error) {
          console.error(new Error(l10n.getFormatStr(timer.error,
                                                    [timer.name])));
          return null;
        }
        body = l10n.getFormatStr("timerStarted", [timer.name]);
        clipboardText = body;
        break;
      }

      case "timeEnd": {
        let timer = message.timer;
        if (!timer) {
          return null;
        }
        if (timer.error) {
          console.error(new Error(l10n.getFormatStr(timer.error,
                                                    [timer.name])));
          return null;
        }
        let duration = Math.round(timer.duration * 100) / 100;
        body = l10n.getFormatStr("timeEnd", [timer.name, duration]);
        clipboardText = body;
        break;
      }

      case "count": {
        let counter = message.counter;
        if (!counter) {
          return null;
        }
        if (counter.error) {
          console.error(l10n.getStr(counter.error));
          return null;
        }
        let msg = new Messages.ConsoleGeneric(message);
        node = msg.init(this.output).render().element;
        break;
      }

      case "timeStamp": {
        // console.timeStamp() doesn't need to display anything.
        return null;
      }

      default:
        console.error(new Error("Unknown Console API log level: " + level));
        return null;
    }

    // Release object actors for arguments coming from console API methods that
    // we ignore their arguments.
    switch (level) {
      case "group":
      case "groupCollapsed":
      case "groupEnd":
      case "time":
      case "timeEnd":
      case "count":
        for (let actor of objectActors) {
          this._releaseObject(actor);
        }
        objectActors.clear();
    }

    if (level == "groupEnd") {
      // no need to continue
      return null;
    }

    if (!node) {
      node = this.createMessageNode(CATEGORY_WEBDEV, LEVELS[level], body,
                                    sourceURL, sourceLine, clipboardText,
                                    level, message.timeStamp);
      if (message.private) {
        node.setAttribute("private", true);
      }
    }

    if (objectActors.size > 0) {
      node._objectActors = objectActors;

      if (!node._messageObject) {
        let repeatNode = node.getElementsByClassName("message-repeats")[0];
        repeatNode._uid += [...objectActors].join("-");
      }
    }

    let workerTypeID = CONSOLE_WORKER_IDS.indexOf(message.workerType);
    if (workerTypeID != -1) {
      node.workerType = WORKERTYPES_PREFKEYS[workerTypeID];
      node.setAttribute("workerType", WORKERTYPES_PREFKEYS[workerTypeID]);
    }

    return node;
  },

  /**
   * Handle ConsoleAPICall objects received from the server. This method outputs
   * the window.console API call.
   *
   * @param object message
   *        The console API message received from the server.
   */
  handleConsoleAPICall: function (message) {
    this.outputMessage(CATEGORY_WEBDEV, this.logConsoleAPIMessage, [message]);
  },

  /**
   * Reports an error in the page source, either JavaScript or CSS.
   *
   * @param nsIScriptError scriptError
   *        The error message to report.
   * @return nsIDOMElement|undefined
   *         The message element to display in the Web Console output.
   */
  reportPageError: function (category, scriptError) {
    // Warnings and legacy strict errors become warnings; other types become
    // errors.
    let severity = "error";
    if (scriptError.warning || scriptError.strict) {
      severity = "warning";
    } else if (scriptError.info) {
      severity = "log";
    }

    switch (category) {
      case CATEGORY_CSS:
        category = "css";
        break;
      case CATEGORY_SECURITY:
        category = "security";
        break;
      default:
        category = "js";
        break;
    }

    let objectActors = new Set();

    // Gather the actor IDs.
    for (let prop of ["errorMessage", "lineText"]) {
      let grip = scriptError[prop];
      if (WebConsoleUtils.isActorGrip(grip)) {
        objectActors.add(grip.actor);
      }
    }

    let errorMessage = scriptError.errorMessage;
    if (errorMessage.type && errorMessage.type == "longString") {
      errorMessage = errorMessage.initial;
    }

    let displayOrigin = scriptError.sourceName;

    // TLS errors are related to the connection and not the resource; therefore
    // it makes sense to only display the protcol, host and port (prePath).
    // This also means messages are grouped for a single origin.
    if (scriptError.category && scriptError.category == "SHA-1 Signature") {
      let sourceURI = Services.io.newURI(scriptError.sourceName)
                                 .QueryInterface(Ci.nsIURL);
      displayOrigin = sourceURI.prePath;
    }

    // Create a new message
    let msg = new Messages.Simple(errorMessage, {
      location: {
        url: displayOrigin,
        line: scriptError.lineNumber,
        column: scriptError.columnNumber
      },
      stack: scriptError.stacktrace,
      category: category,
      severity: severity,
      timestamp: scriptError.timeStamp,
      private: scriptError.private,
      filterDuplicates: true
    });

    let node = msg.init(this.output).render().element;

    // Select the body of the message node that is displayed in the console
    let msgBody = node.getElementsByClassName("message-body")[0];

    // Add the more info link node to messages that belong to certain categories
    if (scriptError.exceptionDocURL) {
      this.addLearnMoreWarningNode(msgBody, scriptError.exceptionDocURL);
    }

    // Collect telemetry data regarding JavaScript errors
    this._telemetry.logKeyed("DEVTOOLS_JAVASCRIPT_ERROR_DISPLAYED",
                             scriptError.errorMessageName || "Unknown",
                             true);

    if (objectActors.size > 0) {
      node._objectActors = objectActors;
    }

    return node;
  },

  /**
   * Handle PageError objects received from the server. This method outputs the
   * given error.
   *
   * @param nsIScriptError pageError
   *        The error received from the server.
   */
  handlePageError: function (pageError) {
    let category = Utils.categoryForScriptError(pageError);
    this.outputMessage(category, this.reportPageError, [category, pageError]);
  },

  /**
   * Handle log messages received from the server. This method outputs the given
   * message.
   *
   * @param object packet
   *        The message packet received from the server.
   */
  handleLogMessage: function (packet) {
    if (packet.message) {
      this.outputMessage(CATEGORY_JS, this._reportLogMessage, [packet]);
    }
  },

  /**
   * Display log messages received from the server.
   *
   * @private
   * @param object packet
   *        The message packet received from the server.
   * @return nsIDOMElement
   *         The message element to render for the given log message.
   */
  _reportLogMessage: function (packet) {
    let msg = packet.message;
    if (msg.type && msg.type == "longString") {
      msg = msg.initial;
    }
    let node = this.createMessageNode(CATEGORY_JS, SEVERITY_LOG, msg, null,
                                      null, null, null, packet.timeStamp);
    if (WebConsoleUtils.isActorGrip(packet.message)) {
      node._objectActors = new Set([packet.message.actor]);
    }
    return node;
  },

  /**
   * Log network event.
   *
   * @param object networkInfo
   *        The network request information to log.
   * @return nsIDOMElement|null
   *         The message element to display in the Web Console output.
   */
  logNetEvent: function (networkInfo) {
    let actorId = networkInfo.actor;
    let request = networkInfo.request;
    let clipboardText = request.method + " " + request.url;
    let severity = SEVERITY_LOG;
    if (networkInfo.isXHR) {
      clipboardText = request.method + " XHR " + request.url;
      severity = SEVERITY_INFO;
    }
    let mixedRequest =
      WebConsoleUtils.isMixedHTTPSRequest(request.url, this.contentLocation);
    if (mixedRequest) {
      severity = SEVERITY_WARNING;
    }

    let methodNode = this.document.createElementNS(XHTML_NS, "span");
    methodNode.className = "method";
    methodNode.textContent = request.method + " ";

    let messageNode = this.createMessageNode(CATEGORY_NETWORK, severity,
                                             methodNode, null, null,
                                             clipboardText, null,
                                             networkInfo.timeStamp);
    if (networkInfo.private) {
      messageNode.setAttribute("private", true);
    }
    messageNode._connectionId = actorId;
    messageNode.url = request.url;

    let body = methodNode.parentNode;
    body.setAttribute("aria-haspopup", true);

    if (networkInfo.isXHR) {
      let xhrNode = this.document.createElementNS(XHTML_NS, "span");
      xhrNode.className = "xhr";
      xhrNode.textContent = l10n.getStr("webConsoleXhrIndicator");
      body.appendChild(xhrNode);
      body.appendChild(this.document.createTextNode(" "));
    }

    let displayUrl = request.url;
    let pos = displayUrl.indexOf("?");
    if (pos > -1) {
      displayUrl = displayUrl.substr(0, pos);
    }

    let urlNode = this.document.createElementNS(XHTML_NS, "a");
    urlNode.className = "url";
    urlNode.setAttribute("title", request.url);
    urlNode.href = request.url;
    urlNode.textContent = displayUrl;
    urlNode.draggable = false;
    body.appendChild(urlNode);
    body.appendChild(this.document.createTextNode(" "));

    if (mixedRequest) {
      messageNode.classList.add("mixed-content");
      this.makeMixedContentNode(body);
    }

    let statusNode = this.document.createElementNS(XHTML_NS, "a");
    statusNode.className = "status";
    body.appendChild(statusNode);

    let onClick = () => this.openNetworkPanel(networkInfo.actor);

    this._addMessageLinkCallback(urlNode, onClick);
    this._addMessageLinkCallback(statusNode, onClick);

    networkInfo.node = messageNode;

    this._updateNetMessage(actorId);

    if (this.window.NetRequest) {
      this.window.NetRequest.onNetworkEvent({
        consoleFrame: this,
        response: networkInfo,
        node: messageNode,
        update: false
      });
    }

    return messageNode;
  },

  /**
   * Create a mixed content warning Node.
   *
   * @param linkNode
   *        Parent to the requested urlNode.
   */
  makeMixedContentNode: function (linkNode) {
    let mixedContentWarning =
      "[" + l10n.getStr("webConsoleMixedContentWarning") + "]";

    // Mixed content warning message links to a Learn More page
    let mixedContentWarningNode = this.document.createElementNS(XHTML_NS, "a");
    mixedContentWarningNode.title = MIXED_CONTENT_LEARN_MORE;
    mixedContentWarningNode.href = MIXED_CONTENT_LEARN_MORE;
    mixedContentWarningNode.className = "learn-more-link";
    mixedContentWarningNode.textContent = mixedContentWarning;
    mixedContentWarningNode.draggable = false;

    linkNode.appendChild(mixedContentWarningNode);

    this._addMessageLinkCallback(mixedContentWarningNode, (event) => {
      event.stopPropagation();
      this.owner.openLink(MIXED_CONTENT_LEARN_MORE);
    });
  },

  /*
   * Appends a clickable warning node to the node passed
   * as a parameter to the function. When a user clicks on the appended
   * warning node, the browser navigates to the provided url.
   *
   * @param node
   *        The node to which we will be adding a clickable warning node.
   * @param url
   *        The url which points to the page where the user can learn more
   *        about security issues associated with the specific message that's
   *        being logged.
   */
  addLearnMoreWarningNode: function (node, url) {
    let moreInfoLabel = "[" + l10n.getStr("webConsoleMoreInfoLabel") + "]";

    let warningNode = this.document.createElementNS(XHTML_NS, "a");
    warningNode.title = url.split("?")[0];
    warningNode.href = url;
    warningNode.draggable = false;
    warningNode.textContent = moreInfoLabel;
    warningNode.className = "learn-more-link";

    this._addMessageLinkCallback(warningNode, (event) => {
      event.stopPropagation();
      this.owner.openLink(url);
    });

    node.appendChild(warningNode);
  },

  /**
   * Log file activity.
   *
   * @param string fileURI
   *        The file URI that was loaded.
   * @return nsIDOMElement|undefined
   *         The message element to display in the Web Console output.
   */
  logFileActivity: function (fileURI) {
    let urlNode = this.document.createElementNS(XHTML_NS, "a");
    urlNode.setAttribute("title", fileURI);
    urlNode.className = "url";
    urlNode.textContent = fileURI;
    urlNode.draggable = false;
    urlNode.href = fileURI;

    let outputNode = this.createMessageNode(CATEGORY_NETWORK, SEVERITY_LOG,
                                            urlNode, null, null, fileURI);

    this._addMessageLinkCallback(urlNode, () => {
      this.owner.viewSource(fileURI);
    });

    return outputNode;
  },

  /**
   * Handle the file activity messages coming from the remote Web Console.
   *
   * @param string fileURI
   *        The file URI that was requested.
   */
  handleFileActivity: function (fileURI) {
    this.outputMessage(CATEGORY_NETWORK, this.logFileActivity, [fileURI]);
  },

  /**
   * Handle the reflow activity messages coming from the remote Web Console.
   *
   * @param object msg
   *        An object holding information about a reflow batch.
   */
  logReflowActivity: function (message) {
    let {start, end, sourceURL, sourceLine} = message;
    let duration = Math.round((end - start) * 100) / 100;
    let node = this.document.createElementNS(XHTML_NS, "span");
    if (sourceURL) {
      node.textContent =
        l10n.getFormatStr("reflow.messageWithLink", [duration]);
      let a = this.document.createElementNS(XHTML_NS, "a");
      a.href = "#";
      a.draggable = "false";
      let filename = getSourceNames(sourceURL).short;
      let functionName = message.functionName ||
                         l10n.getStr("stacktrace.anonymousFunction");
      a.textContent = l10n.getFormatStr("reflow.messageLinkText",
                         [functionName, filename, sourceLine]);
      this._addMessageLinkCallback(a, () => {
        this.owner.viewSourceInDebugger(sourceURL, sourceLine);
      });
      node.appendChild(a);
    } else {
      node.textContent =
        l10n.getFormatStr("reflow.messageWithNoLink", [duration]);
    }
    return this.createMessageNode(CATEGORY_CSS, SEVERITY_LOG, node);
  },

  handleReflowActivity: function (message) {
    this.outputMessage(CATEGORY_CSS, this.logReflowActivity, [message]);
  },

  /**
   * Inform user that the window.console API has been replaced by a script
   * in a content page.
   */
  logWarningAboutReplacedAPI: function () {
    let node = this.createMessageNode(CATEGORY_JS, SEVERITY_WARNING,
                                      l10n.getStr("ConsoleAPIDisabled"));
    this.outputMessage(CATEGORY_JS, node);
  },

  /**
   * Handle the network events coming from the remote Web Console.
   *
   * @param object networkInfo
   *        The network request information.
   */
  handleNetworkEvent: function (networkInfo) {
    this.outputMessage(CATEGORY_NETWORK, this.logNetEvent, [networkInfo]);
  },

  /**
   * Handle network event updates coming from the server.
   *
   * @param object networkInfo
   *        The network request information.
   * @param object packet
   *        Update details.
   */
  handleNetworkEventUpdate: function (networkInfo, packet) {
    if (networkInfo.node && this._updateNetMessage(packet.from)) {
      if (this.window.NetRequest) {
        this.window.NetRequest.onNetworkEvent({
          client: this.webConsoleClient,
          response: packet,
          node: networkInfo.node,
          update: true
        });
      }

      this.emit("new-messages", new Set([{
        update: true,
        node: networkInfo.node,
        response: packet,
      }]));
    }

    // For unit tests we pass the HTTP activity object to the test callback,
    // once requests complete.
    if (this.owner.lastFinishedRequestCallback &&
        networkInfo.updates.indexOf("responseContent") > -1 &&
        networkInfo.updates.indexOf("eventTimings") > -1) {
      this.owner.lastFinishedRequestCallback(networkInfo, this);
    }
  },

  /**
   * Update an output message to reflect the latest state of a network request,
   * given a network event actor ID.
   *
   * @private
   * @param string actorId
   *        The network event actor ID for which you want to update the message.
   * @return boolean
   *         |true| if the message node was updated, or |false| otherwise.
   */
  _updateNetMessage: function (actorId) {
    let networkInfo = this.webConsoleClient.getNetworkRequest(actorId);
    if (!networkInfo || !networkInfo.node) {
      return false;
    }

    let messageNode = networkInfo.node;
    let updates = networkInfo.updates;
    let hasEventTimings = updates.indexOf("eventTimings") > -1;
    let hasResponseStart = updates.indexOf("responseStart") > -1;
    let request = networkInfo.request;
    let methodText = (networkInfo.isXHR) ?
                     request.method + " XHR" : request.method;
    let response = networkInfo.response;
    let updated = false;

    if (hasEventTimings || hasResponseStart) {
      let status = [];
      if (response.httpVersion && response.status) {
        status = [response.httpVersion, response.status, response.statusText];
      }
      if (hasEventTimings) {
        status.push(l10n.getFormatStr("NetworkPanel.durationMS",
                                      [networkInfo.totalTime]));
      }
      let statusText = "[" + status.join(" ") + "]";

      let statusNode = messageNode.getElementsByClassName("status")[0];
      statusNode.textContent = statusText;

      messageNode.clipboardText = [methodText, request.url, statusText]
                                  .join(" ");

      if (hasResponseStart && response.status >= MIN_HTTP_ERROR_CODE &&
          response.status <= MAX_HTTP_ERROR_CODE) {
        this.setMessageType(messageNode, CATEGORY_NETWORK, SEVERITY_ERROR);
      }

      updated = true;
    }

    if (messageNode._netPanel) {
      messageNode._netPanel.update();
    }

    return updated;
  },

  /**
   * Opens the network monitor and highlights the specified request.
   *
   * @param string requestId
   *        The actor ID of the network request.
   */
  openNetworkPanel: function (requestId) {
    let toolbox = gDevTools.getToolbox(this.owner.target);
    // The browser console doesn't have a toolbox.
    if (toolbox) {
      return toolbox.selectTool("netmonitor").then(panel => {
        return panel.panelWin.Netmonitor.inspectRequest(requestId);
      });
    }
  },

  /**
   * Handler for page location changes.
   *
   * @param string uri
   *        New page location.
   * @param string title
   *        New page title.
   */
  onLocationChange: function (uri, title) {
    this.contentLocation = uri;
    if (this.owner.onLocationChange) {
      this.owner.onLocationChange(uri, title);
    }
  },

  /**
   * Handler for the tabNavigated notification.
   *
   * @param string event
   *        Event name.
   * @param object packet
   *        Notification packet received from the server.
   */
  handleTabNavigated: function (event, packet) {
    if (event == "will-navigate") {
      if (this.persistLog) {
        let marker = new Messages.NavigationMarker(packet, Date.now());
        this.output.addMessage(marker);
      } else {
        this.jsterm.clearOutput();
      }
    }
    if (packet.url) {
      this.onLocationChange(packet.url, packet.title);
    }

    if (event == "navigate" && !packet.nativeConsoleAPI) {
      this.logWarningAboutReplacedAPI();
    }
  },

  /**
   * Output a message node. This filters a node appropriately, then sends it to
   * the output, regrouping and pruning output as necessary.
   *
   * Note: this call is async - the given message node may not be displayed when
   * you call this method.
   *
   * @param integer category
   *        The category of the message you want to output. See the CATEGORY_*
   *        constants.
   * @param function|nsIDOMElement methodOrNode
   *        The method that creates the message element to send to the output or
   *        the actual element. If a method is given it will be bound to the HUD
   *        object and the arguments will be |args|.
   * @param array [args]
   *        If a method is given to output the message element then the method
   *        will be invoked with the list of arguments given here. The last
   *        object in this array should be the packet received from the
   *        back end.
   */
  outputMessage: function (category, methodOrNode, args) {
    if (!this._outputQueue.length) {
      // If the queue is empty we consider that now was the last output flush.
      // This avoid an immediate output flush when the timer executes.
      this._lastOutputFlush = Date.now();
    }

    this._outputQueue.push([category, methodOrNode, args]);

    this._initOutputTimer();
  },

  /**
   * Try to flush the output message queue. This takes the messages in the
   * output queue and displays them. Outputting stops at MESSAGES_IN_INTERVAL.
   * Further output is queued to happen later - see OUTPUT_INTERVAL.
   *
   * @private
   */
  _flushMessageQueue: function () {
    this._outputTimerInitialized = false;
    if (!this._outputTimer) {
      return;
    }

    let startTime = Date.now();
    let timeSinceFlush = startTime - this._lastOutputFlush;
    let shouldThrottle = this._outputQueue.length > MESSAGES_IN_INTERVAL &&
        timeSinceFlush < THROTTLE_UPDATES;

    // Determine how many messages we can display now.
    let toDisplay = Math.min(this._outputQueue.length, MESSAGES_IN_INTERVAL);

    // If there aren't any messages to display (because of throttling or an
    // empty queue), then take care of some cleanup. Destroy items that were
    // pruned from the outputQueue before being displayed.
    if (shouldThrottle || toDisplay < 1) {
      while (this._itemDestroyQueue.length) {
        if ((Date.now() - startTime) > MAX_CLEANUP_TIME) {
          break;
        }
        this._destroyItem(this._itemDestroyQueue.pop());
      }

      this._initOutputTimer();
      return;
    }

    // Try to prune the message queue.
    let shouldPrune = false;
    if (this._outputQueue.length > toDisplay && this._pruneOutputQueue()) {
      toDisplay = Math.min(this._outputQueue.length, toDisplay);
      shouldPrune = true;
    }

    let batch = this._outputQueue.splice(0, toDisplay);
    let outputNode = this.outputNode;
    let lastVisibleNode = null;
    let scrollNode = this.outputWrapper;
    let hudIdSupportsString = WebConsoleUtils.supportsString(this.hudId);

    // We won't bother to try to restore scroll position if this is showing
    // a lot of messages at once (and there are still items in the queue).
    // It is going to purge whatever you were looking at anyway.
    let scrolledToBottom =
      shouldPrune || Utils.isOutputScrolledToBottom(outputNode, scrollNode);

    // Output the current batch of messages.
    let messages = new Set();
    for (let i = 0; i < batch.length; i++) {
      let item = batch[i];
      let result = this._outputMessageFromQueue(hudIdSupportsString, item);
      if (result) {
        messages.add({
          node: result.isRepeated ? result.isRepeated : result.node,
          response: result.message,
          update: !!result.isRepeated,
        });

        if (result.visible && result.node == this.outputNode.lastChild) {
          lastVisibleNode = result.node;
        }
      }
    }

    let oldScrollHeight = 0;
    let removedNodes = 0;

    // Prune messages from the DOM, but only if needed.
    if (shouldPrune || !this._outputQueue.length) {
      // Only bother measuring the scrollHeight if not scrolled to bottom,
      // since the oldScrollHeight will not be used if it is.
      if (!scrolledToBottom) {
        oldScrollHeight = scrollNode.scrollHeight;
      }

      let categories = Object.keys(this._pruneCategoriesQueue);
      categories.forEach(function _pruneOutput(category) {
        removedNodes += this.pruneOutputIfNecessary(category);
      }, this);
      this._pruneCategoriesQueue = {};
    }

    let isInputOutput = lastVisibleNode &&
                        (lastVisibleNode.category == CATEGORY_INPUT ||
                         lastVisibleNode.category == CATEGORY_OUTPUT);

    // Scroll to the new node if it is not filtered, and if the output node is
    // scrolled at the bottom or if the new node is a jsterm input/output
    // message.
    if (lastVisibleNode && (scrolledToBottom || isInputOutput)) {
      Utils.scrollToVisible(lastVisibleNode);
    } else if (!scrolledToBottom && removedNodes > 0 &&
               oldScrollHeight != scrollNode.scrollHeight) {
      // If there were pruned messages and if scroll is not at the bottom, then
      // we need to adjust the scroll location.
      scrollNode.scrollTop -= oldScrollHeight - scrollNode.scrollHeight;
    }

    if (messages.size) {
      this.emit("new-messages", messages);
    }

    // If the output queue is empty, then run _flushCallback.
    if (this._outputQueue.length === 0 && this._flushCallback) {
      if (this._flushCallback() === false) {
        this._flushCallback = null;
      }
    }

    this._initOutputTimer();

    // Resize the output area in case a vertical scrollbar has been added
    this.resize();

    this._lastOutputFlush = Date.now();
  },

  /**
   * Initialize the output timer.
   * @private
   */
  _initOutputTimer: function () {
    let panelIsDestroyed = !this._outputTimer;
    let alreadyScheduled = this._outputTimerInitialized;
    let nothingToDo = !this._itemDestroyQueue.length &&
                      !this._outputQueue.length;

    // Don't schedule a callback in the following cases:
    if (panelIsDestroyed || alreadyScheduled || nothingToDo) {
      return;
    }

    this._outputTimerInitialized = true;
    this._outputTimer.initWithCallback(this._flushMessageQueue,
                                       OUTPUT_INTERVAL,
                                       Ci.nsITimer.TYPE_ONE_SHOT);
  },

  /**
   * Output a message from the queue.
   *
   * @private
   * @param nsISupportsString hudIdSupportsString
   *        The HUD ID as an nsISupportsString.
   * @param array item
   *        An item from the output queue - this item represents a message.
   * @return object
   *         An object that holds the following properties:
   *         - node: the DOM element of the message.
   *         - isRepeated: the DOM element of the original message, if this is
   *         a repeated message, otherwise null.
   *         - visible: boolean that tells if the message is visible.
   */
  _outputMessageFromQueue: function (hudIdSupportsString, item) {
    let [, methodOrNode, args] = item;

    // The last object in the args array should be message
    // object or response packet received from the server.
    let message = (args && args.length) ? args[args.length - 1] : null;

    let node = typeof methodOrNode == "function" ?
               methodOrNode.apply(this, args || []) :
               methodOrNode;
    if (!node) {
      return null;
    }

    let isFiltered = this.filterMessageNode(node);

    let isRepeated = this._filterRepeatedMessage(node);

    // If a clear message is processed while the webconsole is opened, the UI
    // should be cleared.
    // Do not clear the output if the current frame is owned by a Browser Console.
    if (message && message.level == "clear" && !this.isBrowserConsole) {
      // Do not clear the consoleStorage here as it has been cleared already
      // by the clear method, only clear the UI.
      this.jsterm.clearOutput(false);
    }

    let visible = !isRepeated && !isFiltered;
    if (!isRepeated) {
      this.outputNode.appendChild(node);
      this._pruneCategoriesQueue[node.category] = true;

      let nodeID = node.getAttribute("id");
      Services.obs.notifyObservers(hudIdSupportsString,
                                   "web-console-message-created", nodeID);
    }

    if (node._onOutput) {
      node._onOutput();
      delete node._onOutput;
    }

    return {
      visible: visible,
      node: node,
      isRepeated: isRepeated,
      message: message
    };
  },

  /**
   * Prune the queue of messages to display. This avoids displaying messages
   * that will be removed at the end of the queue anyway.
   * @private
   */
  _pruneOutputQueue: function () {
    let nodes = {};

    // Group the messages per category.
    this._outputQueue.forEach(function (item, index) {
      let [category] = item;
      if (!(category in nodes)) {
        nodes[category] = [];
      }
      nodes[category].push(index);
    }, this);

    let pruned = 0;

    // Loop through the categories we found and prune if needed.
    for (let category in nodes) {
      let limit = Utils.logLimitForCategory(category);
      let indexes = nodes[category];
      if (indexes.length > limit) {
        let n = Math.max(0, indexes.length - limit);
        pruned += n;
        for (let i = n - 1; i >= 0; i--) {
          this._itemDestroyQueue.push(this._outputQueue[indexes[i]]);
          this._outputQueue.splice(indexes[i], 1);
        }
      }
    }

    return pruned;
  },

  /**
   * Destroy an item that was once in the outputQueue but isn't needed
   * after all.
   *
   * @private
   * @param array item
   *        The item you want to destroy.  Does not remove it from the output
   *        queue.
   */
  _destroyItem: function (item) {
    // TODO: handle object releasing in a more elegant way once all console
    // messages use the new API - bug 778766.
    let [category, methodOrNode, args] = item;
    if (typeof methodOrNode != "function" && methodOrNode._objectActors) {
      for (let actor of methodOrNode._objectActors) {
        this._releaseObject(actor);
      }
      methodOrNode._objectActors.clear();
    }

    if (methodOrNode == this.output._flushMessageQueue &&
        args[0]._objectActors) {
      for (let arg of args) {
        if (!arg._objectActors) {
          continue;
        }
        for (let actor of arg._objectActors) {
          this._releaseObject(actor);
        }
        arg._objectActors.clear();
      }
    }

    if (category == CATEGORY_NETWORK) {
      let connectionId = null;
      if (methodOrNode == this.logNetEvent) {
        connectionId = args[0].actor;
      } else if (typeof methodOrNode != "function") {
        connectionId = methodOrNode._connectionId;
      }
      if (connectionId &&
          this.webConsoleClient.hasNetworkRequest(connectionId)) {
        this.webConsoleClient.removeNetworkRequest(connectionId);
        this._releaseObject(connectionId);
      }
    } else if (category == CATEGORY_WEBDEV &&
               methodOrNode == this.logConsoleAPIMessage) {
      args[0].arguments.forEach((value) => {
        if (WebConsoleUtils.isActorGrip(value)) {
          this._releaseObject(value.actor);
        }
      });
    } else if (category == CATEGORY_JS &&
               methodOrNode == this.reportPageError) {
      let pageError = args[1];
      for (let prop of ["errorMessage", "lineText"]) {
        let grip = pageError[prop];
        if (WebConsoleUtils.isActorGrip(grip)) {
          this._releaseObject(grip.actor);
        }
      }
    } else if (category == CATEGORY_JS &&
               methodOrNode == this._reportLogMessage) {
      if (WebConsoleUtils.isActorGrip(args[0].message)) {
        this._releaseObject(args[0].message.actor);
      }
    }
  },

  /**
   * Cleans up a message via a node that may or may not
   * have actually been rendered in the DOM. Currently, only
   * cleans up React components.
   *
   * @param nsIDOMNode node
   *        The message node you want to clean up.
   */
  unmountMessage(node) {
    // Unmount the Frame component with the message location
    let locationNode = node.querySelector(".message-location");
    if (locationNode) {
      this.ReactDOM.unmountComponentAtNode(locationNode);
    }

    // Unmount the StackTrace component if present in the message
    let stacktraceNode = node.querySelector(".stacktrace");
    if (stacktraceNode) {
      this.ReactDOM.unmountComponentAtNode(stacktraceNode);
    }
  },

  /**
   * Ensures that the number of message nodes of type category don't exceed that
   * category's line limit by removing old messages as needed.
   *
   * @param integer category
   *        The category of message nodes to prune if needed.
   * @return number
   *         The number of removed nodes.
   */
  pruneOutputIfNecessary: function (category) {
    let logLimit = Utils.logLimitForCategory(category);
    let messageNodes = this.outputNode.querySelectorAll(".message[category=" +
                       CATEGORY_CLASS_FRAGMENTS[category] + "]");
    let n = Math.max(0, messageNodes.length - logLimit);
    [...messageNodes].slice(0, n).forEach(this.removeOutputMessage, this);
    return n;
  },

  /**
   * Remove a given message from the output.
   *
   * @param nsIDOMNode node
   *        The message node you want to remove.
   */
  removeOutputMessage: function (node) {
    if (node._messageObject) {
      node._messageObject.destroy();
    }

    if (node._objectActors) {
      for (let actor of node._objectActors) {
        this._releaseObject(actor);
      }
      node._objectActors.clear();
    }

    if (node.category == CATEGORY_CSS ||
        node.category == CATEGORY_SECURITY) {
      let repeatNode = node.getElementsByClassName("message-repeats")[0];
      if (repeatNode && repeatNode._uid) {
        delete this._repeatNodes[repeatNode._uid];
      }
    } else if (node._connectionId &&
               node.category == CATEGORY_NETWORK) {
      this.webConsoleClient.removeNetworkRequest(node._connectionId);
      this._releaseObject(node._connectionId);
    } else if (node.classList.contains("inlined-variables-view")) {
      let view = node._variablesView;
      if (view) {
        view.controller.releaseActors();
      }
      node._variablesView = null;
    }

    this.unmountMessage(node);

    node.remove();
  },

  /**
   * Given a category and message body, creates a DOM node to represent an
   * incoming message. The timestamp is automatically added.
   *
   * @param number category
   *        The category of the message: one of the CATEGORY_* constants.
   * @param number severity
   *        The severity of the message: one of the SEVERITY_* constants;
   * @param string|nsIDOMNode body
   *        The body of the message, either a simple string or a DOM node.
   * @param string sourceURL [optional]
   *        The URL of the source file that emitted the error.
   * @param number sourceLine [optional]
   *        The line number on which the error occurred. If zero or omitted,
   *        there is no line number associated with this message.
   * @param string clipboardText [optional]
   *        The text that should be copied to the clipboard when this node is
   *        copied. If omitted, defaults to the body text. If `body` is not
   *        a string, then the clipboard text must be supplied.
   * @param number level [optional]
   *        The level of the console API message.
   * @param number timestamp [optional]
   *        The timestamp to use for this message node. If omitted, the current
   *        date and time is used.
   * @return nsIDOMNode
   *         The message node: a DIV ready to be inserted into the Web Console
   *         output node.
   */
  createMessageNode: function (category, severity, body, sourceURL, sourceLine,
                              clipboardText, level, timestamp) {
    if (typeof body != "string" && clipboardText == null && body.innerText) {
      clipboardText = body.innerText;
    }

    let indentNode = this.document.createElementNS(XHTML_NS, "span");
    indentNode.className = "indent";

    // Apply the current group by indenting appropriately.
    let indent = this.groupDepth * GROUP_INDENT;
    indentNode.style.width = indent + "px";

    // Make the icon container, which is a vertical box. Its purpose is to
    // ensure that the icon stays anchored at the top of the message even for
    // long multi-line messages.
    let iconContainer = this.document.createElementNS(XHTML_NS, "span");
    iconContainer.className = "icon";

    // Create the message body, which contains the actual text of the message.
    let bodyNode = this.document.createElementNS(XHTML_NS, "span");
    bodyNode.className = "message-body-wrapper message-body devtools-monospace";

    // Store the body text, since it is needed later for the variables view.
    let storedBody = body;
    // If a string was supplied for the body, turn it into a DOM node and an
    // associated clipboard string now.
    clipboardText = clipboardText ||
                     (body + (sourceURL ? " @ " + sourceURL : "") +
                              (sourceLine ? ":" + sourceLine : ""));

    timestamp = timestamp || Date.now();

    // Create the containing node and append all its elements to it.
    let node = this.document.createElementNS(XHTML_NS, "div");
    node.id = "console-msg-" + gSequenceId();
    node.className = "message";
    node.clipboardText = clipboardText;
    node.timestamp = timestamp;
    this.setMessageType(node, category, severity);

    if (body instanceof Ci.nsIDOMNode) {
      bodyNode.appendChild(body);
    } else {
      let str = undefined;
      if (level == "dir") {
        str = VariablesView.getString(body.arguments[0]);
      } else {
        str = body;
      }

      if (str !== undefined) {
        body = this.document.createTextNode(str);
        bodyNode.appendChild(body);
      }
    }

    // Add the message repeats node only when needed.
    let repeatNode = null;
    if (category != CATEGORY_INPUT &&
        category != CATEGORY_OUTPUT &&
        category != CATEGORY_NETWORK &&
        !(category == CATEGORY_CSS && severity == SEVERITY_LOG)) {
      repeatNode = this.document.createElementNS(XHTML_NS, "span");
      repeatNode.setAttribute("value", "1");
      repeatNode.className = "message-repeats";
      repeatNode.textContent = 1;
      repeatNode._uid = [bodyNode.textContent, category, severity, level,
                         sourceURL, sourceLine].join(":");
    }

    // Create the timestamp.
    let timestampNode = this.document.createElementNS(XHTML_NS, "span");
    timestampNode.className = "timestamp devtools-monospace";

    let timestampString = l10n.timestampString(timestamp);
    timestampNode.textContent = timestampString + " ";

    // Create the source location (e.g. www.example.com:6) that sits on the
    // right side of the message, if applicable.
    let locationNode;
    if (sourceURL && !IGNORED_SOURCE_URLS.includes(sourceURL)) {
      locationNode = this.createLocationNode({url: sourceURL,
                                              line: sourceLine});
    }

    node.appendChild(timestampNode);
    node.appendChild(indentNode);
    node.appendChild(iconContainer);

    // Display the variables view after the message node.
    if (level == "dir") {
      let options = {
        objectActor: storedBody.arguments[0],
        targetElement: bodyNode,
        hideFilterInput: true,
      };
      this.jsterm.openVariablesView(options).then((view) => {
        node._variablesView = view;
        if (node.classList.contains("hidden-message")) {
          node.classList.remove("hidden-message");
        }
      });

      node.classList.add("inlined-variables-view");
    }

    node.appendChild(bodyNode);
    if (repeatNode) {
      node.appendChild(repeatNode);
    }
    if (locationNode) {
      node.appendChild(locationNode);
    }
    node.appendChild(this.document.createTextNode("\n"));

    return node;
  },

  /**
   * Creates the anchor that displays the textual location of an incoming
   * message.
   *
   * @param {Object} location
   *        An object containing url, line and column number of the message source.
   * @return {Element}
   *         The new anchor element, ready to be added to the message node.
   */
  createLocationNode: function (location) {
    let locationNode = this.document.createElementNS(XHTML_NS, "div");
    locationNode.className = "message-location devtools-monospace";

    // Make the location clickable.
    let onClick = ({ url, line }) => {
      let category = locationNode.closest(".message").category;
      let target = null;

      if (/^Scratchpad\/\d+$/.test(url)) {
        target = "scratchpad";
      } else if (category === CATEGORY_CSS) {
        target = "styleeditor";
      } else if (category === CATEGORY_JS || category === CATEGORY_WEBDEV) {
        target = "jsdebugger";
      } else if (/\.js$/.test(url)) {
        // If it ends in .js, let's attempt to open in debugger
        // anyway, as this falls back to normal view-source.
        target = "jsdebugger";
      } else {
        // Point everything else to debugger, if source not available,
        // it will fall back to view-source.
        target = "jsdebugger";
      }

      switch (target) {
        case "scratchpad":
          this.owner.viewSourceInScratchpad(url, line);
          return;
        case "jsdebugger":
          this.owner.viewSourceInDebugger(url, line);
          return;
        case "styleeditor":
          this.owner.viewSourceInStyleEditor(url, line);
          return;
      }
      // No matching tool found; use old school view-source
      this.owner.viewSource(url, line);
    };

    const toolbox = gDevTools.getToolbox(this.owner.target);

    let { url, line, column } = location;
    let source = url ? url.split(" -> ").pop() : "";

    this.ReactDOM.render(this.FrameView({
      frame: { source, line, column },
      showEmptyPathAsHost: true,
      onClick,
      sourceMapService: toolbox ? toolbox.sourceMapURLService : null,
    }), locationNode);

    return locationNode;
  },

  /**
   * Adjusts the category and severity of the given message.
   *
   * @param nsIDOMNode messageNode
   *        The message node to alter.
   * @param number category
   *        The category for the message; one of the CATEGORY_ constants.
   * @param number severity
   *        The severity for the message; one of the SEVERITY_ constants.
   * @return void
   */
  setMessageType: function (messageNode, category, severity) {
    messageNode.category = category;
    messageNode.severity = severity;
    messageNode.setAttribute("category", CATEGORY_CLASS_FRAGMENTS[category]);
    messageNode.setAttribute("severity", SEVERITY_CLASS_FRAGMENTS[severity]);
    messageNode.setAttribute("filter",
      MESSAGE_PREFERENCE_KEYS[category][severity]);
  },

  /**
   * Add the mouse event handlers needed to make a link.
   *
   * @private
   * @param nsIDOMNode node
   *        The node for which you want to add the event handlers.
   * @param function callback
   *        The function you want to invoke on click.
   */
  _addMessageLinkCallback: function (node, callback) {
    node.addEventListener("mousedown", (event) => {
      this._mousedown = true;
      this._startX = event.clientX;
      this._startY = event.clientY;
    });

    node.addEventListener("click", (event) => {
      let mousedown = this._mousedown;
      this._mousedown = false;

      event.preventDefault();

      // Do not allow middle/right-click or 2+ clicks.
      if (event.detail != 1 || event.button != 0) {
        return;
      }

      // If this event started with a mousedown event and it ends at a different
      // location, we consider this text selection.
      if (mousedown &&
          (this._startX != event.clientX) &&
          (this._startY != event.clientY)) {
        this._startX = this._startY = undefined;
        return;
      }

      this._startX = this._startY = undefined;

      callback.call(this, event);
    });
  },
  /**
   * Called when the message timestamp pref changes.
   */
  _onToolboxPrefChanged: function () {
    let newValue = Services.prefs.getBoolPref(PREF_MESSAGE_TIMESTAMP);
    if (newValue) {
      this.outputNode.classList.remove("hideTimestamps");
    } else {
      this.outputNode.classList.add("hideTimestamps");
    }
  },
  /**
   * Copies the selected items to the system clipboard.
   *
   * @param object options
   *        - linkOnly:
   *        An optional flag to copy only URL without other meta-information.
   *        Default is false.
   *        - contextmenu:
   *        An optional flag to copy the last clicked item which brought
   *        up the context menu if nothing is selected. Default is false.
   */
  copySelectedItems: function (options) {
    options = options || { linkOnly: false, contextmenu: false };

    // Gather up the selected items and concatenate their clipboard text.
    let strings = [];

    let children = this.output.getSelectedMessages();
    if (!children.length && options.contextmenu) {
      children = [this._contextMenuHandler.lastClickedMessage];
    }

    for (let item of children) {
      // Ensure the selected item hasn't been filtered by type or string.
      if (!item.classList.contains("filtered-by-type") &&
          !item.classList.contains("filtered-by-string")) {
        if (options.linkOnly) {
          strings.push(item.url);
        } else {
          strings.push(item.clipboardText);
        }
      }
    }

    clipboardHelper.copyString(strings.join("\n"));
  },

  /**
   * Object properties provider. This function gives you the properties of the
   * remote object you want.
   *
   * @param string actor
   *        The object actor ID from which you want the properties.
   * @param function callback
   *        Function you want invoked once the properties are received.
   */
  objectPropertiesProvider: function (actor, callback) {
    this.webConsoleClient.inspectObjectProperties(actor,
      function (response) {
        if (response.error) {
          console.error("Failed to retrieve the object properties from the " +
                        "server. Error: " + response.error);
          return;
        }
        callback(response.properties);
      });
  },

  /**
   * Release an actor.
   *
   * @private
   * @param string actor
   *        The actor ID you want to release.
   */
  _releaseObject: function (actor) {
    if (this.proxy) {
      this.proxy.releaseActor(actor);
    }
  },

  /**
   * Open the selected item's URL in a new tab.
   */
  openSelectedItemInTab: function () {
    let item = this.output.getSelectedMessages(1)[0] ||
               this._contextMenuHandler.lastClickedMessage;

    if (!item || !item.url) {
      return;
    }

    this.owner.openLink(item.url);
  },

  /**
   * Destroy the WebConsoleFrame object. Call this method to avoid memory leaks
   * when the Web Console is closed.
   *
   * @return object
   *         A promise that is resolved when the WebConsoleFrame instance is
   *         destroyed.
   */
  destroy: function () {
    if (this._destroyer) {
      return this._destroyer.promise;
    }

    this._destroyer = defer();

    let toolbox = gDevTools.getToolbox(this.owner.target);
    if (toolbox) {
      toolbox.off("webconsole-selected", this._onPanelSelected);
    }

    this._prefObserver.off(PREF_MESSAGE_TIMESTAMP, this._onToolboxPrefChanged);
    this._prefObserver.destroy();
    this.window.removeEventListener("resize", this.resize, true);

    this._repeatNodes = {};
    this._outputQueue.forEach(this._destroyItem, this);
    this._outputQueue = [];
    this._itemDestroyQueue.forEach(this._destroyItem, this);
    this._itemDestroyQueue = [];
    this._pruneCategoriesQueue = {};
    this.webConsoleClient.clearNetworkRequests();

    // Unmount any currently living frame components in DOM, since
    // currently we only clean up messages in `this.removeOutputMessage`,
    // via `this.pruneOutputIfNecessary`.
    let liveMessages = this.outputNode.querySelectorAll(".message");
    Array.prototype.forEach.call(liveMessages, this.unmountMessage);

    if (this._outputTimerInitialized) {
      this._outputTimerInitialized = false;
      this._outputTimer.cancel();
    }
    this._outputTimer = null;
    if (this.jsterm) {
      this.jsterm.off("sidebar-opened", this.resize);
      this.jsterm.off("sidebar-closed", this.resize);
      this.jsterm.destroy();
      this.jsterm = null;
    }
    this.output.destroy();
    this.output = null;

    this.React = this.ReactDOM = this.FrameView = null;

    if (this._contextMenuHandler) {
      this._contextMenuHandler.destroy();
      this._contextMenuHandler = null;
    }

    this._commandController = null;

    let onDestroy = () => {
      this._destroyer.resolve(null);
    };

    if (this.proxy) {
      this.proxy.disconnect().then(onDestroy);
      this.proxy = null;
    } else {
      onDestroy();
    }

    return this._destroyer.promise;
  },
};

/**
 * Utils: a collection of globally used functions.
 */
var Utils = {
  /**
   * Scrolls a node so that it's visible in its containing element.
   *
   * @param nsIDOMNode node
   *        The node to make visible.
   * @returns void
   */
  scrollToVisible: function (node) {
    node.scrollIntoView(false);
  },

  /**
   * Check if the given output node is scrolled to the bottom.
   *
   * @param nsIDOMNode outputNode
   * @param nsIDOMNode scrollNode
   * @return boolean
   *         True if the output node is scrolled to the bottom, or false
   *         otherwise.
   */
  isOutputScrolledToBottom: function (outputNode, scrollNode) {
    let lastNodeHeight = outputNode.lastChild ?
                         outputNode.lastChild.clientHeight : 0;
    return scrollNode.scrollTop + scrollNode.clientHeight >=
           scrollNode.scrollHeight - lastNodeHeight / 2;
  },

  /**
   * Determine the category of a given nsIScriptError.
   *
   * @param nsIScriptError scriptError
   *        The script error you want to determine the category for.
   * @return CATEGORY_JS|CATEGORY_CSS|CATEGORY_SECURITY
   *         Depending on the script error CATEGORY_JS, CATEGORY_CSS, or
   *         CATEGORY_SECURITY can be returned.
   */
  categoryForScriptError: function (scriptError) {
    let category = scriptError.category;

    if (/^(?:CSS|Layout)\b/.test(category)) {
      return CATEGORY_CSS;
    }

    switch (category) {
      case "Mixed Content Blocker":
      case "Mixed Content Message":
      case "CSP":
      case "Invalid HSTS Headers":
      case "Invalid HPKP Headers":
      case "SHA-1 Signature":
      case "Insecure Password Field":
      case "SSL":
      case "CORS":
      case "Iframe Sandbox":
      case "Tracking Protection":
      case "Sub-resource Integrity":
        return CATEGORY_SECURITY;

      default:
        return CATEGORY_JS;
    }
  },

  /**
   * Retrieve the limit of messages for a specific category.
   *
   * @param number category
   *        The category of messages you want to retrieve the limit for. See the
   *        CATEGORY_* constants.
   * @return number
   *         The number of messages allowed for the specific category.
   */
  logLimitForCategory: function (category) {
    let logLimit = DEFAULT_LOG_LIMIT;

    try {
      let prefName = CATEGORY_CLASS_FRAGMENTS[category];
      logLimit = Services.prefs.getIntPref("devtools.hud.loglimit." + prefName);
      logLimit = Math.max(logLimit, 1);
    } catch (e) {
      // Ignore any exceptions
    }

    return logLimit;
  },
};

// CommandController

/**
 * A controller (an instance of nsIController) that makes editing actions
 * behave appropriately in the context of the Web Console.
 */
function CommandController(webConsole) {
  this.owner = webConsole;
}

CommandController.prototype = {
  /**
   * Selects all the text in the HUD output.
   */
  selectAll: function () {
    this.owner.output.selectAllMessages();
  },

  /**
   * Open the URL of the selected message in a new tab.
   */
  openURL: function () {
    this.owner.openSelectedItemInTab();
  },

  copyURL: function () {
    this.owner.copySelectedItems({ linkOnly: true, contextmenu: true });
  },

  /**
   * Copies the last clicked message.
   */
  copyLastClicked: function () {
    this.owner.copySelectedItems({ linkOnly: false, contextmenu: true });
  },

  supportsCommand: function (command) {
    if (!this.owner || !this.owner.output) {
      return false;
    }
    return this.isCommandEnabled(command);
  },

  isCommandEnabled: function (command) {
    switch (command) {
      case "consoleCmd_openURL":
      case "consoleCmd_copyURL": {
        // Only enable URL-related actions if node is Net Activity.
        let selectedItem = this.owner.output.getSelectedMessages(1)[0] ||
                           this.owner._contextMenuHandler.lastClickedMessage;
        return selectedItem && "url" in selectedItem;
      }
      case "cmd_copy": {
        // Only copy if we right-clicked the console and there's no selected
        // text. With text selected, we want to fall back onto the default
        // copy behavior.
        return this.owner._contextMenuHandler.lastClickedMessage &&
              !this.owner.output.getSelectedMessages(1)[0];
      }
      case "cmd_selectAll":
        return true;
    }
    return false;
  },

  doCommand: function (command) {
    switch (command) {
      case "consoleCmd_openURL":
        this.openURL();
        break;
      case "consoleCmd_copyURL":
        this.copyURL();
        break;
      case "cmd_copy":
        this.copyLastClicked();
        break;
      case "cmd_selectAll":
        this.selectAll();
        break;
    }
  }
};

// Context Menu

/*
 * ConsoleContextMenu this used to handle the visibility of context menu items.
 *
 * @constructor
 * @param object owner
 *        The WebConsoleFrame instance that owns this object.
 */
function ConsoleContextMenu(owner) {
  this.owner = owner;
  this.popup = this.owner.document.getElementById("output-contextmenu");
  this.build = this.build.bind(this);
  this.popup.addEventListener("popupshowing", this.build);
}

ConsoleContextMenu.prototype = {
  lastClickedMessage: null,

  /*
   * Handle to show/hide context menu item.
   */
  build: function (event) {
    let metadata = this.getSelectionMetadata(event.rangeParent);
    for (let element of this.popup.children) {
      element.hidden = this.shouldHideMenuItem(element, metadata);
    }
  },

  /*
   * Get selection information from the view.
   *
   * @param nsIDOMElement clickElement
   *        The DOM element the user clicked on.
   * @return object
   *         Selection metadata.
   */
  getSelectionMetadata: function (clickElement) {
    let metadata = {
      selectionType: "",
      selection: new Set(),
    };
    let selectedItems = this.owner.output.getSelectedMessages();
    if (!selectedItems.length) {
      let clickedItem = this.owner.output.getMessageForElement(clickElement);
      if (clickedItem) {
        this.lastClickedMessage = clickedItem;
        selectedItems = [clickedItem];
      }
    }

    metadata.selectionType = selectedItems.length > 1 ? "multiple" : "single";

    let selection = metadata.selection;
    for (let item of selectedItems) {
      switch (item.category) {
        case CATEGORY_NETWORK:
          selection.add("network");
          break;
        case CATEGORY_CSS:
          selection.add("css");
          break;
        case CATEGORY_JS:
          selection.add("js");
          break;
        case CATEGORY_WEBDEV:
          selection.add("webdev");
          break;
        case CATEGORY_SERVER:
          selection.add("server");
          break;
      }
    }

    return metadata;
  },

  /*
   * Determine if an item should be hidden.
   *
   * @param nsIDOMElement menuItem
   * @param object metadata
   * @return boolean
   *         Whether the given item should be hidden or not.
   */
  shouldHideMenuItem: function (menuItem, metadata) {
    let selectionType = menuItem.getAttribute("selectiontype");
    if (selectionType && !metadata.selectionType == selectionType) {
      return true;
    }

    let selection = menuItem.getAttribute("selection");
    if (!selection) {
      return false;
    }

    let shouldHide = true;
    let itemData = selection.split("|");
    for (let type of metadata.selection) {
      // check whether this menu item should show or not.
      if (itemData.includes(type)) {
        shouldHide = false;
        break;
      }
    }

    return shouldHide;
  },

  /**
   * Destroy the ConsoleContextMenu object instance.
   */
  destroy: function () {
    this.popup.removeEventListener("popupshowing", this.build);
    this.popup = null;
    this.owner = null;
    this.lastClickedMessage = null;
  },
};
PK
!<F)PP@chrome/devtools/modules/devtools/client/webide/modules/addons.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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} = require("resource://gre/modules/AddonManager.jsm");
const Services = require("Services");
const {getJSON} = require("devtools/client/shared/getjson");
const EventEmitter = require("devtools/shared/event-emitter");

var ADB_LINK = Services.prefs.getCharPref("devtools.webide.adbAddonURL");
var ADB_ADDON_ID = Services.prefs.getCharPref("devtools.webide.adbAddonID");

var platform = Services.appShell.hiddenDOMWindow.navigator.platform;
var 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 = "linux32";
  }
}

var addonsListener = {};
addonsListener.onEnabled =
addonsListener.onDisabled =
addonsListener.onInstalled =
addonsListener.onUninstalled = (updatedAddon) => {
  let addons = GetAvailableAddons();
  addons.adb.updateInstallStatus();
};
AddonManager.addAddonListener(addonsListener);

var AvailableAddons = null;
var GetAvailableAddons = exports.GetAvailableAddons = function () {
  if (!AvailableAddons) {
    AvailableAddons = {
      adb: new ADBAddon()
    };
  }
  return AvailableAddons;
};

exports.ForgetAddonsList = function () {
  AvailableAddons = null;
};

function Addon() {}
Addon.prototype = {
  _status: "unknown",
  set status(value) {
    if (this._status != value) {
      this._status = value;
      this.emit("update");
    }
  },
  get status() {
    return this._status;
  },

  updateInstallStatus: function () {
    AddonManager.getAddonByID(this.addonID, (addon) => {
      if (addon && !addon.userDisabled) {
        this.status = "installed";
      } else {
        this.status = "uninstalled";
      }
    });
  },

  install: function () {
    AddonManager.getAddonByID(this.addonID, (addon) => {
      if (addon && !addon.userDisabled) {
        this.status = "installed";
        return;
      }
      this.status = "preparing";
      if (addon && addon.userDisabled) {
        addon.userDisabled = false;
      } else {
        AddonManager.getInstallForURL(this.xpiLink, (install) => {
          install.addListener(this);
          install.install();
        }, "application/x-xpinstall");
      }
    });
  },

  uninstall: function () {
    AddonManager.getAddonByID(this.addonID, (addon) => {
      addon.uninstall();
    });
  },

  installFailureHandler: function (install, message) {
    this.status = "uninstalled";
    this.emit("failure", message);
  },

  onDownloadStarted: function () {
    this.status = "downloading";
  },

  onInstallStarted: function () {
    this.status = "installing";
  },

  onDownloadProgress: function (install) {
    if (install.maxProgress == -1) {
      this.emit("progress", -1);
    } else {
      this.emit("progress", install.progress / install.maxProgress);
    }
  },

  onInstallEnded: function ({addon}) {
    addon.userDisabled = false;
  },

  onDownloadCancelled: function (install) {
    this.installFailureHandler(install, "Download cancelled");
  },
  onDownloadFailed: function (install) {
    this.installFailureHandler(install, "Download failed");
  },
  onInstallCancelled: function (install) {
    this.installFailureHandler(install, "Install cancelled");
  },
  onInstallFailed: function (install) {
    this.installFailureHandler(install, "Install failed");
  },
};

function ADBAddon() {
  EventEmitter.decorate(this);
  // This addon uses the string "linux" for "linux32"
  let fixedOS = OS == "linux32" ? "linux" : OS;
  this.xpiLink = ADB_LINK.replace(/#OS#/g, fixedOS);
  this.addonID = ADB_ADDON_ID;
  this.updateInstallStatus();
}
ADBAddon.prototype = Object.create(Addon.prototype);
PK
!<�����d�dEchrome/devtools/modules/devtools/client/webide/modules/app-manager.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 {Cu} = require("chrome");

const {TargetFactory} = require("devtools/client/framework/target");
const Services = require("Services");
const {FileUtils} = require("resource://gre/modules/FileUtils.jsm");
const EventEmitter = require("devtools/shared/event-emitter");
const {OS} = require("resource://gre/modules/osfile.jsm");
const {AppProjects} = require("devtools/client/webide/modules/app-projects");
const TabStore = require("devtools/client/webide/modules/tab-store");
const {AppValidator} = require("devtools/client/webide/modules/app-validator");
const {ConnectionManager, Connection} = require("devtools/shared/client/connection-manager");
const {getDeviceFront} = require("devtools/shared/fronts/device");
const {getPreferenceFront} = require("devtools/shared/fronts/preference");
const {Task} = require("devtools/shared/task");
const {RuntimeScanners, RuntimeTypes} = require("devtools/client/webide/modules/runtimes");
const {NetUtil} = require("resource://gre/modules/NetUtil.jsm");
const Telemetry = require("devtools/client/shared/telemetry");

const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties");

var AppManager = exports.AppManager = {

  DEFAULT_PROJECT_ICON: "chrome://webide/skin/default-app-icon.png",
  DEFAULT_PROJECT_NAME: "--",

  _initialized: false,

  init: function () {
    if (this._initialized) {
      return;
    }
    this._initialized = true;

    let port = Services.prefs.getIntPref("devtools.debugger.remote-port");
    this.connection = ConnectionManager.createConnection("localhost", port);
    this.onConnectionChanged = this.onConnectionChanged.bind(this);
    this.connection.on(Connection.Events.STATUS_CHANGED, this.onConnectionChanged);

    this.tabStore = new TabStore(this.connection);
    this.onTabList = this.onTabList.bind(this);
    this.onTabNavigate = this.onTabNavigate.bind(this);
    this.onTabClosed = this.onTabClosed.bind(this);
    this.tabStore.on("tab-list", this.onTabList);
    this.tabStore.on("navigate", this.onTabNavigate);
    this.tabStore.on("closed", this.onTabClosed);

    this._clearRuntimeList();
    this._rebuildRuntimeList = this._rebuildRuntimeList.bind(this);
    RuntimeScanners.on("runtime-list-updated", this._rebuildRuntimeList);
    RuntimeScanners.enable();
    this._rebuildRuntimeList();

    this._telemetry = new Telemetry();
  },

  destroy: function () {
    if (!this._initialized) {
      return;
    }
    this._initialized = false;

    this.selectedProject = null;
    this.selectedRuntime = null;
    RuntimeScanners.off("runtime-list-updated", this._rebuildRuntimeList);
    RuntimeScanners.disable();
    this.runtimeList = null;
    this.tabStore.off("tab-list", this.onTabList);
    this.tabStore.off("navigate", this.onTabNavigate);
    this.tabStore.off("closed", this.onTabClosed);
    this.tabStore.destroy();
    this.tabStore = null;
    this.connection.off(Connection.Events.STATUS_CHANGED, this.onConnectionChanged);
    this._listTabsResponse = null;
    this.connection.disconnect();
    this.connection = null;
  },

  /**
   * This module emits various events when state changes occur.  The basic event
   * naming scheme is that event "X" means "X has changed" or "X is available".
   * Some names are more detailed to clarify their precise meaning.
   *
   * The events this module may emit include:
   *   before-project:
   *     The selected project is about to change.  The event includes a special
   *     |cancel| callback that will abort the project change if desired.
   *   connection:
   *     The connection status has changed (connected, disconnected, etc.)
   *   project:
   *     The selected project has changed.
   *   project-started:
   *     The selected project started running on the connected runtime.
   *   project-stopped:
   *     The selected project stopped running on the connected runtime.
   *   project-removed:
   *     The selected project was removed from the project list.
   *   project-validated:
   *     The selected project just completed validation.  As part of validation,
   *     many pieces of metadata about the project are refreshed, including its
   *     name, manifest details, etc.
   *   runtime:
   *     The selected runtime has changed.
   *   runtime-global-actors:
   *     The list of global actors for the entire runtime (but not actors for a
   *     specific tab or app) are now available, so we can test for features
   *     like preferences and settings.
   *   runtime-details:
   *     The selected runtime's details have changed, such as its user-visible
   *     name.
   *   runtime-list:
   *     The list of available runtimes has changed, or any of the user-visible
   *     details (like names) for the non-selected runtimes has changed.
   *   runtime-telemetry:
   *     Detailed runtime telemetry has been recorded.  Used by tests.
   *   runtime-targets:
   *     The list of remote runtime targets available from the currently
   *     connected runtime (such as tabs or apps) has changed, or any of the
   *     user-visible details (like names) for the non-selected runtime targets
   *     has changed.  This event includes |type| in the details, to distinguish
   *     "apps" and "tabs".
   */
  update: function (what, details) {
    // Anything we want to forward to the UI
    this.emit("app-manager-update", what, details);
  },

  reportError: function (l10nProperty, ...l10nArgs) {
    let win = Services.wm.getMostRecentWindow("devtools:webide");
    if (win) {
      win.UI.reportError(l10nProperty, ...l10nArgs);
    } else {
      let text;
      if (l10nArgs.length > 0) {
        text = Strings.formatStringFromName(l10nProperty, l10nArgs, l10nArgs.length);
      } else {
        text = Strings.GetStringFromName(l10nProperty);
      }
      console.error(text);
    }
  },

  onConnectionChanged: function () {
    console.log("Connection status changed: " + this.connection.status);

    if (this.connection.status == Connection.Status.DISCONNECTED) {
      this.selectedRuntime = null;
    }

    if (!this.connected) {
      this._listTabsResponse = null;
    } else {
      this.connection.client.listTabs().then((response) => {
        this._listTabsResponse = response;
        this._recordRuntimeInfo();
        this.update("runtime-global-actors");
      });
    }

    this.update("connection");
  },

  get connected() {
    return this.connection &&
           this.connection.status == Connection.Status.CONNECTED;
  },

  get apps() {
    if (this._appsFront) {
      return this._appsFront.apps;
    } else {
      return new Map();
    }
  },

  isProjectRunning: function () {
    if (this.selectedProject.type == "mainProcess" ||
        this.selectedProject.type == "tab") {
      return true;
    }

    let app = this._getProjectFront(this.selectedProject);
    return app && app.running;
  },

  checkIfProjectIsRunning: function () {
    if (this.selectedProject) {
      if (this.isProjectRunning()) {
        this.update("project-started");
      } else {
        this.update("project-stopped");
      }
    }
  },

  listTabs: function () {
    return this.tabStore.listTabs();
  },

  onTabList: function () {
    this.update("runtime-targets", { type: "tabs" });
  },

  // TODO: Merge this into TabProject as part of project-agnostic work
  onTabNavigate: function () {
    this.update("runtime-targets", { type: "tabs" });
    if (this.selectedProject.type !== "tab") {
      return;
    }
    let tab = this.selectedProject.app = this.tabStore.selectedTab;
    let uri = NetUtil.newURI(tab.url);
    // Wanted to use nsIFaviconService here, but it only works for visited
    // tabs, so that's no help for any remote tabs.  Maybe some favicon wizard
    // knows how to get high-res favicons easily, or we could offer actor
    // support for this (bug 1061654).
    tab.favicon = uri.prePath + "/favicon.ico";
    tab.name = tab.title || Strings.GetStringFromName("project_tab_loading");
    if (uri.scheme.startsWith("http")) {
      tab.name = uri.host + ": " + tab.name;
    }
    this.selectedProject.location = tab.url;
    this.selectedProject.name = tab.name;
    this.selectedProject.icon = tab.favicon;
    this.update("project-validated");
  },

  onTabClosed: function () {
    if (this.selectedProject.type !== "tab") {
      return;
    }
    this.selectedProject = null;
  },

  reloadTab: function () {
    if (this.selectedProject && this.selectedProject.type != "tab") {
      return Promise.reject("tried to reload non-tab project");
    }
    return this.getTarget().then(target => {
      target.activeTab.reload();
    }, console.error.bind(console));
  },

  getTarget: function () {
    if (this.selectedProject.type == "mainProcess") {
      // Fx >=39 exposes a ChromeActor to debug the main process
      if (this.connection.client.mainRoot.traits.allowChromeProcess) {
        return this.connection.client.getProcess()
                   .then(aResponse => {
                     return TargetFactory.forRemoteTab({
                       form: aResponse.form,
                       client: this.connection.client,
                       chrome: true
                     });
                   });
      } else {
        // Fx <39 exposes tab actors on the root actor
        return TargetFactory.forRemoteTab({
          form: this._listTabsResponse,
          client: this.connection.client,
          chrome: true,
          isTabActor: false
        });
      }
    }

    if (this.selectedProject.type == "tab") {
      return this.tabStore.getTargetForTab();
    }

    let app = this._getProjectFront(this.selectedProject);
    if (!app) {
      return Promise.reject("Can't find app front for selected project");
    }

    return Task.spawn(function* () {
      // Once we asked the app to launch, the app isn't necessary completely loaded.
      // launch request only ask the app to launch and immediatly returns.
      // We have to keep trying to get app tab actors required to create its target.

      for (let i = 0; i < 10; i++) {
        try {
          return yield app.getTarget();
        } catch (e) {}
        return new Promise(resolve => {
          setTimeout(resolve, 500);
        });
      }

      AppManager.reportError("error_cantConnectToApp", app.manifest.manifestURL);
      throw new Error("can't connect to app");
    });
  },

  getProjectManifestURL: function (project) {
    let manifest = null;
    if (project.type == "runtimeApp") {
      manifest = project.app.manifestURL;
    }

    if (project.type == "hosted") {
      manifest = project.location;
    }

    if (project.type == "packaged" && project.packagedAppOrigin) {
      manifest = "app://" + project.packagedAppOrigin + "/manifest.webapp";
    }

    return manifest;
  },

  _getProjectFront: function (project) {
    let manifest = this.getProjectManifestURL(project);
    if (manifest && this._appsFront) {
      return this._appsFront.apps.get(manifest);
    }
    return null;
  },

  _selectedProject: null,
  set selectedProject(project) {
    // A regular comparison doesn't work as we recreate a new object every time
    let prev = this._selectedProject;
    if (!prev && !project) {
      return;
    } else if (prev && project && prev.type === project.type) {
      let type = project.type;
      if (type === "runtimeApp") {
        if (prev.app.manifestURL === project.app.manifestURL) {
          return;
        }
      } else if (type === "tab") {
        if (prev.app.actor === project.app.actor) {
          return;
        }
      } else if (type === "packaged" || type === "hosted") {
        if (prev.location === project.location) {
          return;
        }
      } else if (type === "mainProcess") {
        return;
      } else {
        throw new Error("Unsupported project type: " + type);
      }
    }

    let cancelled = false;
    this.update("before-project", { cancel: () => { cancelled = true; } });
    if (cancelled) {
      return;
    }

    this._selectedProject = project;

    // Clear out tab store's selected state, if any
    this.tabStore.selectedTab = null;

    if (project) {
      if (project.type == "packaged" ||
          project.type == "hosted") {
        this.validateAndUpdateProject(project);
      }
      if (project.type == "tab") {
        this.tabStore.selectedTab = project.app;
      }
    }

    this.update("project");
    this.checkIfProjectIsRunning();
  },
  get selectedProject() {
    return this._selectedProject;
  },

  removeSelectedProject: Task.async(function* () {
    let location = this.selectedProject.location;
    AppManager.selectedProject = null;
    // If the user cancels the removeProject operation, don't remove the project
    if (AppManager.selectedProject != null) {
      return;
    }

    yield AppProjects.remove(location);
    AppManager.update("project-removed");
  }),

  _selectedRuntime: null,
  set selectedRuntime(value) {
    this._selectedRuntime = value;
    if (!value && this.selectedProject &&
        (this.selectedProject.type == "mainProcess" ||
         this.selectedProject.type == "runtimeApp" ||
         this.selectedProject.type == "tab")) {
      this.selectedProject = null;
    }
    this.update("runtime");
  },

  get selectedRuntime() {
    return this._selectedRuntime;
  },

  connectToRuntime: function (runtime) {

    if (this.connected && this.selectedRuntime === runtime) {
      // Already connected
      return Promise.resolve();
    }

    let deferred = new Promise((resolve, reject) => {
      this.disconnectRuntime().then(() => {
        this.selectedRuntime = runtime;

        let onConnectedOrDisconnected = () => {
          this.connection.off(Connection.Events.CONNECTED, onConnectedOrDisconnected);
          this.connection.off(Connection.Events.DISCONNECTED, onConnectedOrDisconnected);
          if (this.connected) {
            resolve();
          } else {
            reject();
          }
        };
        this.connection.on(Connection.Events.CONNECTED, onConnectedOrDisconnected);
        this.connection.on(Connection.Events.DISCONNECTED, onConnectedOrDisconnected);
        try {
          // Reset the connection's state to defaults
          this.connection.resetOptions();
          // Only watch for errors here.  Final resolution occurs above, once
          // we've reached the CONNECTED state.
          this.selectedRuntime.connect(this.connection)
                              .catch(e => reject(e));
        } catch (e) {
          reject(e);
        }
      }, reject);
    });

    // Record connection result in telemetry
    let logResult = result => {
      this._telemetry.log("DEVTOOLS_WEBIDE_CONNECTION_RESULT", result);
      if (runtime.type) {
        this._telemetry.log("DEVTOOLS_WEBIDE_" + runtime.type +
                            "_CONNECTION_RESULT", result);
      }
    };
    deferred.then(() => logResult(true), () => logResult(false));

    // If successful, record connection time in telemetry
    deferred.then(() => {
      const timerId = "DEVTOOLS_WEBIDE_CONNECTION_TIME_SECONDS";
      this._telemetry.startTimer(timerId);
      this.connection.once(Connection.Events.STATUS_CHANGED, () => {
        this._telemetry.stopTimer(timerId);
      });
    }).catch(() => {
      // Empty rejection handler to silence uncaught rejection warnings
      // |connectToRuntime| caller should listen for rejections.
      // Bug 1121100 may find a better way to silence these.
    });

    return deferred;
  },

  _recordRuntimeInfo: Task.async(function* () {
    if (!this.connected) {
      return;
    }
    let runtime = this.selectedRuntime;
    this._telemetry.logKeyed("DEVTOOLS_WEBIDE_CONNECTED_RUNTIME_TYPE",
                             runtime.type || "UNKNOWN", true);
    this._telemetry.logKeyed("DEVTOOLS_WEBIDE_CONNECTED_RUNTIME_ID",
                             runtime.id || "unknown", true);
    if (!this.deviceFront) {
      this.update("runtime-telemetry");
      return;
    }
    let d = yield this.deviceFront.getDescription();
    this._telemetry.logKeyed("DEVTOOLS_WEBIDE_CONNECTED_RUNTIME_PROCESSOR",
                             d.processor, true);
    this._telemetry.logKeyed("DEVTOOLS_WEBIDE_CONNECTED_RUNTIME_OS",
                             d.os, true);
    this._telemetry.logKeyed("DEVTOOLS_WEBIDE_CONNECTED_RUNTIME_PLATFORM_VERSION",
                             d.platformversion, true);
    this._telemetry.logKeyed("DEVTOOLS_WEBIDE_CONNECTED_RUNTIME_APP_TYPE",
                             d.apptype, true);
    this._telemetry.logKeyed("DEVTOOLS_WEBIDE_CONNECTED_RUNTIME_VERSION",
                             d.version, true);
    this.update("runtime-telemetry");
  }),

  isMainProcessDebuggable: function () {
    // Fx <39 exposes chrome tab actors on RootActor
    // Fx >=39 exposes a dedicated actor via getProcess request
    return this.connection.client &&
           this.connection.client.mainRoot &&
           this.connection.client.mainRoot.traits.allowChromeProcess ||
           (this._listTabsResponse &&
            this._listTabsResponse.consoleActor);
  },

  get listTabsForm() {
    return this._listTabsResponse;
  },

  get deviceFront() {
    if (!this._listTabsResponse) {
      return null;
    }
    return getDeviceFront(this.connection.client, this._listTabsResponse);
  },

  get preferenceFront() {
    if (!this._listTabsResponse) {
      return null;
    }
    return getPreferenceFront(this.connection.client, this._listTabsResponse);
  },

  disconnectRuntime: function () {
    if (!this.connected) {
      return Promise.resolve();
    }

    return new Promise(resolve => {
      this.connection.once(Connection.Events.DISCONNECTED, () => resolve());
      this.connection.disconnect();
    });
  },

  launchRuntimeApp: function () {
    if (this.selectedProject && this.selectedProject.type != "runtimeApp") {
      return Promise.reject("attempting to launch a non-runtime app");
    }
    let app = this._getProjectFront(this.selectedProject);
    return app.launch();
  },

  launchOrReloadRuntimeApp: function () {
    if (this.selectedProject && this.selectedProject.type != "runtimeApp") {
      return Promise.reject("attempting to launch / reload a non-runtime app");
    }
    let app = this._getProjectFront(this.selectedProject);
    if (!app.running) {
      return app.launch();
    } else {
      return app.reload();
    }
  },

  runtimeCanHandleApps: function () {
    return !!this._appsFront;
  },

  installAndRunProject: function () {
    let project = this.selectedProject;

    if (!project || (project.type != "packaged" && project.type != "hosted")) {
      console.error("Can't install project. Unknown type of project.");
      return Promise.reject("Can't install");
    }

    if (!this._listTabsResponse) {
      this.reportError("error_cantInstallNotFullyConnected");
      return Promise.reject("Can't install");
    }

    if (!this._appsFront) {
      console.error("Runtime doesn't have a webappsActor");
      return Promise.reject("Can't install");
    }

    return Task.spawn(function* () {
      let self = AppManager;

      // Validate project
      yield self.validateAndUpdateProject(project);

      if (project.errorsCount > 0) {
        self.reportError("error_cantInstallValidationErrors");
        return;
      }

      let installPromise;

      if (project.type != "packaged" && project.type != "hosted") {
        return Promise.reject("Don't know how to install project");
      }

      let response;
      if (project.type == "packaged") {
        let packageDir = project.location;
        console.log("Installing app from " + packageDir);

        response = yield self._appsFront.installPackaged(packageDir,
                                                         project.packagedAppOrigin);

        // If the packaged app specified a custom origin override,
        // we need to update the local project origin
        project.packagedAppOrigin = response.appId;
        // And ensure the indexed db on disk is also updated
        AppProjects.update(project);
      }

      if (project.type == "hosted") {
        let manifestURLObject = Services.io.newURI(project.location);
        let origin = Services.io.newURI(manifestURLObject.prePath);
        let appId = origin.host;
        let metadata = {
          origin: origin.spec,
          manifestURL: project.location
        };
        response = yield self._appsFront.installHosted(appId,
                                            metadata,
                                            project.manifest);
      }

      // Addons don't have any document to load (yet?)
      // So that there is no need to run them, installing is enough
      if (project.manifest.manifest_version || project.manifest.role === "addon") {
        return;
      }

      let {app} = response;
      if (!app.running) {
        let deferred = new Promise(resolve => {
          self.on("app-manager-update", function onUpdate(what) {
            if (what == "project-started") {
              self.off("app-manager-update", onUpdate);
              resolve();
            }
          });
        });
        yield app.launch();
        yield deferred;
      } else {
        yield app.reload();
      }
    });
  },

  stopRunningApp: function () {
    let app = this._getProjectFront(this.selectedProject);
    return app.close();
  },

  /* PROJECT VALIDATION */

  validateAndUpdateProject: function (project) {
    if (!project) {
      return Promise.reject();
    }

    return Task.spawn(function* () {

      let packageDir = project.location;
      let validation = new AppValidator({
        type: project.type,
        // Build process may place the manifest in a non-root directory
        location: packageDir
      });

      yield validation.validate();

      if (validation.manifest) {
        let manifest = validation.manifest;
        let iconPath;
        if (manifest.icons) {
          let size = Object.keys(manifest.icons).sort((a, b) => b - a)[0];
          if (size) {
            iconPath = manifest.icons[size];
          }
        }
        if (!iconPath) {
          project.icon = AppManager.DEFAULT_PROJECT_ICON;
        } else {
          if (project.type == "hosted") {
            let manifestURL = Services.io.newURI(project.location);
            let origin = Services.io.newURI(manifestURL.prePath);
            project.icon = Services.io.newURI(iconPath, null, origin).spec;
          } else if (project.type == "packaged") {
            let projectFolder = FileUtils.File(packageDir);
            let folderURI = Services.io.newFileURI(projectFolder).spec;
            project.icon = folderURI + iconPath.replace(/^\/|\\/, "");
          }
        }
        project.manifest = validation.manifest;

        if ("name" in project.manifest) {
          project.name = project.manifest.name;
        } else {
          project.name = AppManager.DEFAULT_PROJECT_NAME;
        }
      } else {
        project.manifest = null;
        project.icon = AppManager.DEFAULT_PROJECT_ICON;
        project.name = AppManager.DEFAULT_PROJECT_NAME;
      }

      project.validationStatus = "valid";

      if (validation.warnings.length > 0) {
        project.warningsCount = validation.warnings.length;
        project.warnings = validation.warnings;
        project.validationStatus = "warning";
      } else {
        project.warnings = "";
        project.warningsCount = 0;
      }

      if (validation.errors.length > 0) {
        project.errorsCount = validation.errors.length;
        project.errors = validation.errors;
        project.validationStatus = "error";
      } else {
        project.errors = "";
        project.errorsCount = 0;
      }

      if (project.warningsCount && project.errorsCount) {
        project.validationStatus = "error warning";
      }

      if (project.type === "hosted" && project.location !== validation.manifestURL) {
        yield AppProjects.updateLocation(project, validation.manifestURL);
      } else if (AppProjects.get(project.location)) {
        yield AppProjects.update(project);
      }

      if (AppManager.selectedProject === project) {
        AppManager.update("project-validated");
      }
    });
  },

  /* RUNTIME LIST */

  _clearRuntimeList: function () {
    this.runtimeList = {
      usb: [],
      wifi: [],
      other: []
    };
  },

  _rebuildRuntimeList: function () {
    let runtimes = RuntimeScanners.listRuntimes();
    this._clearRuntimeList();

    // Reorganize runtimes by type
    for (let runtime of runtimes) {
      switch (runtime.type) {
        case RuntimeTypes.USB:
          this.runtimeList.usb.push(runtime);
          break;
        case RuntimeTypes.WIFI:
          this.runtimeList.wifi.push(runtime);
          break;
        default:
          this.runtimeList.other.push(runtime);
      }
    }

    this.update("runtime-details");
    this.update("runtime-list");
  },

  /* MANIFEST UTILS */

  writeManifest: function (project) {
    if (project.type != "packaged") {
      return Promise.reject("Not a packaged app");
    }

    if (!project.manifest) {
      project.manifest = {};
    }

    let folder = project.location;
    let manifestPath = OS.Path.join(folder, "manifest.webapp");
    let text = JSON.stringify(project.manifest, null, 2);
    let encoder = new TextEncoder();
    let array = encoder.encode(text);
    return OS.File.writeAtomic(manifestPath, array, {tmpPath: manifestPath + ".tmp"});
  },
};

EventEmitter.decorate(AppManager);
PK
!<QV���Fchrome/devtools/modules/devtools/client/webide/modules/app-projects.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 {Cc, Ci, Cu, Cr} = require("chrome");

const EventEmitter = require("devtools/shared/event-emitter");
const {generateUUID} = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
const {FileUtils} = require("resource://gre/modules/FileUtils.jsm");

/**
 * IndexedDB wrapper that just save project objects
 *
 * The only constraint is that project objects have to have
 * a unique `location` object.
 */

const IDB = {
  _db: null,
  databaseName: "AppProjects",

  open: function () {
    return new Promise((resolve, reject) => {
      let request = indexedDB.open(IDB.databaseName, 5);
      request.onerror = function (event) {
        reject("Unable to open AppProjects indexedDB: " +
                        this.error.name + " - " + this.error.message);
      };
      request.onupgradeneeded = function (event) {
        let db = event.target.result;
        db.createObjectStore("projects", { keyPath: "location" });
      };

      request.onsuccess = function () {
        let db = IDB._db = request.result;
        let objectStore = db.transaction("projects").objectStore("projects");
        let projects = [];
        let toRemove = [];
        objectStore.openCursor().onsuccess = function (event) {
          let cursor = event.target.result;
          if (cursor) {
            if (cursor.value.location) {

              // We need to make sure this object has a `.location` property.
              // The UI depends on this property.
              // This should not be needed as we make sure to register valid
              // projects, but in the past (before bug 924568), we might have
              // registered invalid objects.


              // We also want to make sure the location is valid.
              // If the location doesn't exist, we remove the project.

              try {
                let file = FileUtils.File(cursor.value.location);
                if (file.exists()) {
                  projects.push(cursor.value);
                } else {
                  toRemove.push(cursor.value.location);
                }
              } catch (e) {
                if (e.result == Cr.NS_ERROR_FILE_UNRECOGNIZED_PATH) {
                  // A URL
                  projects.push(cursor.value);
                }
              }
            }
            cursor.continue();
          } else {
            let removePromises = [];
            for (let location of toRemove) {
              removePromises.push(IDB.remove(location));
            }
            Promise.all(removePromises).then(() => {
              resolve(projects);
            });
          }
        };
      };
    });
  },

  add: function (project) {
    return new Promise((resolve, reject) => {
      if (!project.location) {
        // We need to make sure this object has a `.location` property.
        reject("Missing location property on project object.");
      } else {
        let transaction = IDB._db.transaction(["projects"], "readwrite");
        let objectStore = transaction.objectStore("projects");
        let request = objectStore.add(project);
        request.onerror = function (event) {
          reject("Unable to add project to the AppProjects indexedDB: " +
                 this.error.name + " - " + this.error.message);
        };
        request.onsuccess = function () {
          resolve();
        };
      }
    });
  },

  update: function (project) {
    return new Promise((resolve, reject) => {
      var transaction = IDB._db.transaction(["projects"], "readwrite");
      var objectStore = transaction.objectStore("projects");
      var request = objectStore.put(project);
      request.onerror = function (event) {
        reject("Unable to update project to the AppProjects indexedDB: " +
               this.error.name + " - " + this.error.message);
      };
      request.onsuccess = function () {
        resolve();
      };
    });
  },

  remove: function (location) {
    return new Promise((resolve, reject) => {
      let request = IDB._db.transaction(["projects"], "readwrite")
                    .objectStore("projects")
                    .delete(location);
      request.onsuccess = function (event) {
        resolve();
      };
      request.onerror = function () {
        reject("Unable to delete project to the AppProjects indexedDB: " +
               this.error.name + " - " + this.error.message);
      };
    });
  }
};

var loadDeferred = IDB.open().then(function (projects) {
  AppProjects.projects = projects;
  AppProjects.emit("ready", projects);
});

const AppProjects = {
  load: function () {
    return loadDeferred;
  },

  addPackaged: function (folder) {
    let file = FileUtils.File(folder.path);
    if (!file.exists()) {
      return Promise.reject("path doesn't exist");
    }
    let existingProject = this.get(folder.path);
    if (existingProject) {
      return Promise.reject("Already added");
    }
    let project = {
      type: "packaged",
      location: folder.path,
      // We need a unique id, that is the app origin,
      // in order to identify the app when being installed on the device.
      // The packaged app local path is a valid id, but only on the client.
      // This origin will be used to generate the true id of an app:
      // its manifest URL.
      // If the app ends up specifying an explicit origin in its manifest,
      // we will override this random UUID on app install.
      packagedAppOrigin: generateUUID().toString().slice(1, -1)
    };
    return IDB.add(project).then(() => {
      this.projects.push(project);
      return project;
    });
  },

  addHosted: function (manifestURL) {
    let existingProject = this.get(manifestURL);
    if (existingProject) {
      return Promise.reject("Already added");
    }
    let project = {
      type: "hosted",
      location: manifestURL
    };
    return IDB.add(project).then(() => {
      this.projects.push(project);
      return project;
    });
  },

  update: function (project) {
    return IDB.update(project);
  },

  updateLocation: function (project, newLocation) {
    return IDB.remove(project.location)
              .then(() => {
                project.location = newLocation;
                return IDB.add(project);
              });
  },

  remove: function (location) {
    return IDB.remove(location).then(() => {
      for (let i = 0; i < this.projects.length; i++) {
        if (this.projects[i].location == location) {
          this.projects.splice(i, 1);
          return;
        }
      }
      throw new Error("Unable to find project in AppProjects store");
    });
  },

  get: function (location) {
    for (let i = 0; i < this.projects.length; i++) {
      if (this.projects[i].location == location) {
        return this.projects[i];
      }
    }
    return null;
  },

  projects: []
};

EventEmitter.decorate(AppProjects);

exports.AppProjects = AppProjects;
PK
!<J"l��#�#Gchrome/devtools/modules/devtools/client/webide/modules/app-validator.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";

var {Ci, Cu} = require("chrome");

const {FileUtils} = require("resource://gre/modules/FileUtils.jsm");
const Services = require("Services");
const {Task} = require("devtools/shared/task");
var strings = Services.strings.createBundle("chrome://devtools/locale/app-manager.properties");

function AppValidator({ type, location }) {
  this.type = type;
  this.location = location;
  this.errors = [];
  this.warnings = [];
}

AppValidator.prototype.error = function (message) {
  this.errors.push(message);
};

AppValidator.prototype.warning = function (message) {
  this.warnings.push(message);
};

AppValidator.prototype._getPackagedManifestFile = function () {
  let manifestFile = FileUtils.File(this.location);
  if (!manifestFile.exists()) {
    this.error(strings.GetStringFromName("validator.nonExistingFolder"));
    return null;
  }
  if (!manifestFile.isDirectory()) {
    this.error(strings.GetStringFromName("validator.expectProjectFolder"));
    return null;
  }

  let appManifestFile = manifestFile.clone();
  appManifestFile.append("manifest.webapp");

  let jsonManifestFile = manifestFile.clone();
  jsonManifestFile.append("manifest.json");

  let hasAppManifest = appManifestFile.exists() && appManifestFile.isFile();
  let hasJsonManifest = jsonManifestFile.exists() && jsonManifestFile.isFile();

  if (!hasAppManifest && !hasJsonManifest) {
    this.error(strings.GetStringFromName("validator.noManifestFile"));
    return null;
  }

  return hasAppManifest ? appManifestFile : jsonManifestFile;
};

AppValidator.prototype._getPackagedManifestURL = function () {
  let manifestFile = this._getPackagedManifestFile();
  if (!manifestFile) {
    return null;
  }
  return Services.io.newFileURI(manifestFile).spec;
};

AppValidator.checkManifest = function (manifestURL) {
  return new Promise((resolve, reject) => {
    let error;

    let req = new XMLHttpRequest();
    req.overrideMimeType("text/plain");

    try {
      req.open("GET", manifestURL, true);
      req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE | Ci.nsIRequest.INHIBIT_CACHING;
    } catch (e) {
      error = strings.formatStringFromName("validator.invalidManifestURL", [manifestURL], 1);
      return reject(error);
    }

    req.onload = function () {
      let manifest = null;
      try {
        manifest = JSON.parse(req.responseText);
      } catch (e) {
        error = strings.formatStringFromName("validator.invalidManifestJSON", [e, manifestURL], 2);
        reject(error);
      }

      resolve({manifest, manifestURL});
    };

    req.onerror = function () {
      error = strings.formatStringFromName("validator.noAccessManifestURL", [req.statusText, manifestURL], 2);
      reject(error);
    };

    try {
      req.send(null);
    } catch (e) {
      error = strings.formatStringFromName("validator.noAccessManifestURL", [e, manifestURL], 2);
      reject(error);
    }
  });
};

AppValidator.findManifestAtOrigin = function (manifestURL) {
  let fixedManifest = Services.io.newURI(manifestURL).prePath + "/manifest.webapp";
  return AppValidator.checkManifest(fixedManifest);
};

AppValidator.findManifestPath = function (manifestURL) {
  return new Promise((resolve, reject) => {
    if (manifestURL.endsWith("manifest.webapp")) {
      reject();
    } else {
      let fixedManifest = manifestURL + "/manifest.webapp";
      resolve(AppValidator.checkManifest(fixedManifest));
    }
  });
};

AppValidator.checkAlternateManifest = function (manifestURL) {
  return Task.spawn(function* () {
    let result;
    try {
      result = yield AppValidator.findManifestPath(manifestURL);
    } catch (e) {
      result = yield AppValidator.findManifestAtOrigin(manifestURL);
    }

    return result;
  });
};

AppValidator.prototype._fetchManifest = function (manifestURL) {
  return new Promise(resolve => {
    this.manifestURL = manifestURL;

    AppValidator.checkManifest(manifestURL)
                .then(({manifest, manifestURL}) => {
                  resolve(manifest);
                }, error => {
                  AppValidator.checkAlternateManifest(manifestURL)
                              .then(({manifest, manifestURL}) => {
                                this.manifestURL = manifestURL;
                                resolve(manifest);
                              }, () => {
                                this.error(error);
                                resolve(null);
                              });
                });
  });
};

AppValidator.prototype._getManifest = function () {
  let manifestURL;
  if (this.type == "packaged") {
    manifestURL = this._getPackagedManifestURL();
    if (!manifestURL)
      return Promise.resolve(null);
  } else if (this.type == "hosted") {
    manifestURL = this.location;
    try {
      Services.io.newURI(manifestURL);
    } catch (e) {
      this.error(strings.formatStringFromName("validator.invalidHostedManifestURL", [manifestURL, e.message], 2));
      return Promise.resolve(null);
    }
  } else {
    this.error(strings.formatStringFromName("validator.invalidProjectType", [this.type], 1));
    return Promise.resolve(null);
  }
  return this._fetchManifest(manifestURL);
};

AppValidator.prototype.validateManifest = function (manifest) {
  if (!manifest.name) {
    this.error(strings.GetStringFromName("validator.missNameManifestProperty"));
  }

  if (!manifest.icons || Object.keys(manifest.icons).length === 0) {
    this.warning(strings.GetStringFromName("validator.missIconsManifestProperty"));
  } else if (!manifest.icons["128"]) {
    this.warning(strings.GetStringFromName("validator.missIconMarketplace2"));
  }
};

AppValidator.prototype._getOriginURL = function () {
  if (this.type == "packaged") {
    let manifestURL = Services.io.newURI(this.manifestURL);
    return Services.io.newURI(".", null, manifestURL).spec;
  } else if (this.type == "hosted") {
    return Services.io.newURI(this.location).prePath;
  }
};

AppValidator.prototype.validateLaunchPath = function (manifest) {
  return new Promise(resolve => {
    // The launch_path field has to start with a `/`
    if (manifest.launch_path && manifest.launch_path[0] !== "/") {
      this.error(strings.formatStringFromName("validator.nonAbsoluteLaunchPath", [manifest.launch_path], 1));
      resolve();
    }
    let origin = this._getOriginURL();
    let path;
    if (this.type == "packaged") {
      path = "." + (manifest.launch_path || "/index.html");
    } else if (this.type == "hosted") {
      path = manifest.launch_path || "/";
    }
    let indexURL;
    try {
      indexURL = Services.io.newURI(path, null, Services.io.newURI(origin)).spec;
    } catch (e) {
      this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [origin + path], 1));
      return resolve();
    }

    let req = new XMLHttpRequest();
    req.overrideMimeType("text/plain");
    try {
      req.open("HEAD", indexURL, true);
      req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE | Ci.nsIRequest.INHIBIT_CACHING;
    } catch (e) {
      this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [indexURL], 1));
      return resolve();
    }
    req.onload = () => {
      if (req.status >= 400)
        this.error(strings.formatStringFromName("validator.accessFailedLaunchPathBadHttpCode", [indexURL, req.status], 2));
      resolve();
    };
    req.onerror = () => {
      this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [indexURL], 1));
      resolve();
    };

    try {
      req.send(null);
    } catch (e) {
      this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [indexURL], 1));
      resolve();
    }
  });
};

AppValidator.prototype.validateType = function (manifest) {
  let appType = manifest.type || "web";
  if (!["web", "privileged", "certified"].includes(appType)) {
    this.error(strings.formatStringFromName("validator.invalidAppType", [appType], 1));
  } else if (this.type == "hosted" &&
             ["certified", "privileged"].includes(appType)) {
    this.error(strings.formatStringFromName("validator.invalidHostedPriviledges", [appType], 1));
  }

  // certified app are not fully supported on the simulator
  if (appType === "certified") {
    this.warning(strings.GetStringFromName("validator.noCertifiedSupport"));
  }
};

AppValidator.prototype.validate = function () {
  this.errors = [];
  this.warnings = [];
  return this._getManifest().
    then((manifest) => {
      if (manifest) {
        this.manifest = manifest;

        // Skip validations for add-ons
        if (manifest.role === "addon" || manifest.manifest_version) {
          return Promise.resolve();
        }

        this.validateManifest(manifest);
        this.validateType(manifest);
        return this.validateLaunchPath(manifest);
      }
    });
};

exports.AppValidator = AppValidator;
PK
!<9��^''Echrome/devtools/modules/devtools/client/webide/modules/config-view.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 {Cu} = require("chrome");

const EventEmitter = require("devtools/shared/event-emitter");
const Services = require("Services");
const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties");

var ConfigView;

module.exports = ConfigView = function (window) {
  EventEmitter.decorate(this);
  this._doc = window.document;
  this._keys = [];
  return this;
};

ConfigView.prototype = {
  _renderByType: function (input, name, value, customType) {
    value = customType || typeof value;

    switch (value) {
      case "boolean":
        input.setAttribute("data-type", "boolean");
        input.setAttribute("type", "checkbox");
        break;
      case "number":
        input.setAttribute("data-type", "number");
        input.setAttribute("type", "number");
        break;
      case "object":
        input.setAttribute("data-type", "object");
        input.setAttribute("type", "text");
        break;
      default:
        input.setAttribute("data-type", "string");
        input.setAttribute("type", "text");
        break;
    }
    return input;
  },

  set front(front) {
    this._front = front;
  },

  set keys(keys) {
    this._keys = keys;
  },

  get keys() {
    return this._keys;
  },

  set kind(kind) {
    this._kind = kind;
  },

  set includeTypeName(include) {
    this._includeTypeName = include;
  },

  search: function (event) {
    if (event.target.value.length) {
      let stringMatch = new RegExp(event.target.value, "i");

      for (let i = 0; i < this._keys.length; i++) {
        let key = this._keys[i];
        let row = this._doc.getElementById("row-" + key);
        if (key.match(stringMatch)) {
          row.classList.remove("hide");
        } else if (row) {
          row.classList.add("hide");
        }
      }
    } else {
      var trs = this._doc.getElementById("device-fields").querySelectorAll("tr");

      for (let i = 0; i < trs.length; i++) {
        trs[i].classList.remove("hide");
      }
    }
  },

  generateDisplay: function (json) {
    let deviceItems = Object.keys(json);
    deviceItems.sort();
    this.keys = deviceItems;
    for (let i = 0; i < this.keys.length; i++) {
      let key = this.keys[i];
      this.generateField(key, json[key].value, json[key].hasUserValue);
    }
  },

  generateField: function (name, value, hasUserValue, customType, newRow) {
    let table = this._doc.querySelector("table");
    let sResetDefault = Strings.GetStringFromName("device_reset_default");

    if (!this._keys.includes(name)) {
      this._keys.push(name);
    }

    let input = this._doc.createElement("input");
    let tr = this._doc.createElement("tr");
    tr.setAttribute("id", "row-" + name);
    tr.classList.add("edit-row");
    let td = this._doc.createElement("td");
    td.classList.add("field-name");
    td.textContent = name;
    tr.appendChild(td);
    td = this._doc.createElement("td");
    input.classList.add("editable");
    input.setAttribute("id", name);
    input = this._renderByType(input, name, value, customType);

    if (customType === "boolean" || input.type === "checkbox") {
      input.checked = value;
    } else {
      if (typeof value === "object") {
        value = JSON.stringify(value);
      }
      input.value = value;
    }

    if (!(this._includeTypeName || isNaN(parseInt(value, 10)))) {
      input.type = "number";
    }

    td.appendChild(input);
    tr.appendChild(td);
    td = this._doc.createElement("td");
    td.setAttribute("id", "td-" + name);

    let button = this._doc.createElement("button");
    button.setAttribute("data-id", name);
    button.setAttribute("id", "btn-" + name);
    button.classList.add("reset");
    button.textContent = sResetDefault;
    td.appendChild(button);

    if (!hasUserValue) {
      button.classList.add("hide");
    }

    tr.appendChild(td);

    // If this is a new field, add it to the top of the table.
    if (newRow) {
      let existing = table.querySelector("#" + name);

      if (!existing) {
        table.insertBefore(tr, newRow);
      } else {
        existing.value = value;
      }
    } else {
      table.appendChild(tr);
    }
  },

  resetTable: function () {
    let table = this._doc.querySelector("table");
    let trs = table.querySelectorAll("tr:not(#add-custom-field)");

    for (var i = 0; i < trs.length; i++) {
      table.removeChild(trs[i]);
    }

    return table;
  },

  _getCallType: function (type, name) {
    let frontName = "get";

    if (this._includeTypeName) {
      frontName += type;
    }

    return this._front[frontName + this._kind](name);
  },

  _setCallType: function (type, name, value) {
    let frontName = "set";

    if (this._includeTypeName) {
      frontName += type;
    }

    return this._front[frontName + this._kind](name, value);
  },

  _saveByType: function (options) {
    let fieldName = options.id;
    let inputType = options.type;
    let value = options.value;
    let input = this._doc.getElementById(fieldName);

    switch (inputType) {
      case "boolean":
        this._setCallType("Bool", fieldName, input.checked);
        break;
      case "number":
        this._setCallType("Int", fieldName, value);
        break;
      case "object":
        try {
          value = JSON.parse(value);
        } catch (e) {}
        this._setCallType("Object", fieldName, value);
        break;
      default:
        this._setCallType("Char", fieldName, value);
        break;
    }
  },

  updateField: function (event) {
    if (event.target) {
      let inputType = event.target.getAttribute("data-type");
      let inputValue = event.target.checked || event.target.value;

      if (event.target.nodeName == "input" &&
          event.target.validity.valid &&
          event.target.classList.contains("editable")) {
        let id = event.target.id;
        if (inputType === "boolean") {
          if (event.target.checked) {
            inputValue = true;
          } else {
            inputValue = false;
          }
        }

        this._saveByType({
          id: id,
          type: inputType,
          value: inputValue
        });
        this._doc.getElementById("btn-" + id).classList.remove("hide");
      }
    }
  },

  _resetToDefault: function (name, input, button) {
    this._front["clearUser" + this._kind](name);
    let dataType = input.getAttribute("data-type");
    let tr = this._doc.getElementById("row-" + name);

    switch (dataType) {
      case "boolean":
        this._defaultField = this._getCallType("Bool", name);
        this._defaultField.then(boolean => {
          input.checked = boolean;
        }, () => {
          input.checked = false;
          tr.remove();
        });
        break;
      case "number":
        this._defaultField = this._getCallType("Int", name);
        this._defaultField.then(number => {
          input.value = number;
        }, () => {
          tr.remove();
        });
        break;
      case "object":
        this._defaultField = this._getCallType("Object", name);
        this._defaultField.then(object => {
          input.value = JSON.stringify(object);
        }, () => {
          tr.remove();
        });
        break;
      default:
        this._defaultField = this._getCallType("Char", name);
        this._defaultField.then(string => {
          input.value = string;
        }, () => {
          tr.remove();
        });
        break;
    }

    button.classList.add("hide");
  },

  checkReset: function (event) {
    if (event.target.classList.contains("reset")) {
      let btnId = event.target.getAttribute("data-id");
      let input = this._doc.getElementById(btnId);
      this._resetToDefault(btnId, input, event.target);
    }
  },

  updateFieldType: function () {
    let table = this._doc.querySelector("table");
    let customValueType = table.querySelector("#custom-value-type").value;
    let customTextEl = table.querySelector("#custom-value-text");
    let customText = customTextEl.value;

    if (customValueType.length === 0) {
      return false;
    }

    switch (customValueType) {
      case "boolean":
        customTextEl.type = "checkbox";
        customText = customTextEl.checked;
        break;
      case "number":
        customText = parseInt(customText, 10) || 0;
        customTextEl.type = "number";
        break;
      default:
        customTextEl.type = "text";
        break;
    }

    return customValueType;
  },

  clearNewFields: function () {
    let table = this._doc.querySelector("table");
    let customTextEl = table.querySelector("#custom-value-text");
    if (customTextEl.checked) {
      customTextEl.checked = false;
    } else {
      customTextEl.value = "";
    }

    this.updateFieldType();
  },

  updateNewField: function () {
    let table = this._doc.querySelector("table");
    let customValueType = this.updateFieldType();

    if (!customValueType) {
      return;
    }

    let customRow = table.querySelector("tr:nth-of-type(2)");
    let customTextEl = table.querySelector("#custom-value-text");
    let customTextNameEl = table.querySelector("#custom-value-name");

    if (customTextEl.validity.valid) {
      let customText = customTextEl.value;

      if (customValueType === "boolean") {
        customText = customTextEl.checked;
      }

      let customTextName = customTextNameEl.value.replace(/[^A-Za-z0-9\.\-_]/gi, "");
      this.generateField(customTextName, customText, true, customValueType, customRow);
      this._saveByType({
        id: customTextName,
        type: customValueType,
        value: customText
      });
      customTextNameEl.value = "";
      this.clearNewFields();
    }
  },

  checkNewFieldSubmit: function (event) {
    if (event.keyCode === 13) {
      this._doc.getElementById("custom-value").click();
    }
  }
};
PK
!<��Y�k.k.Fchrome/devtools/modules/devtools/client/webide/modules/project-list.js/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.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 {Cu} = require("chrome");

const Services = require("Services");
const {AppProjects} = require("devtools/client/webide/modules/app-projects");
const {AppManager} = require("devtools/client/webide/modules/app-manager");
const EventEmitter = require("devtools/shared/event-emitter");
const {Task} = require("devtools/shared/task");
const utils = require("devtools/client/webide/modules/utils");
const Telemetry = require("devtools/client/shared/telemetry");

const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties");

var ProjectList;

module.exports = ProjectList = function (win, parentWindow) {
  EventEmitter.decorate(this);
  this._doc = win.document;
  this._UI = parentWindow.UI;
  this._parentWindow = parentWindow;
  this._telemetry = new Telemetry();
  this._panelNodeEl = "div";

  this.onWebIDEUpdate = this.onWebIDEUpdate.bind(this);
  this._UI.on("webide-update", this.onWebIDEUpdate);

  AppManager.init();
  this.appManagerUpdate = this.appManagerUpdate.bind(this);
  AppManager.on("app-manager-update", this.appManagerUpdate);
};

ProjectList.prototype = {
  get doc() {
    return this._doc;
  },

  appManagerUpdate: function (what, details) {
    // Got a message from app-manager.js
    // See AppManager.update() for descriptions of what these events mean.
    switch (what) {
      case "project-removed":
      case "runtime-targets":
      case "connection":
        this.update(details);
        break;
      case "project":
        this.updateCommands();
        this.update(details);
        break;
    }
  },

  onWebIDEUpdate: function (what, details) {
    if (what == "busy" || what == "unbusy") {
      this.updateCommands();
    }
  },

  /**
   * testOptions: {       chrome mochitest support
   *   folder: nsIFile,   where to store the app
   *   index: Number,     index of the app in the template list
   *   name: String       name of the app
   * }
   */
  newApp: function (testOptions) {
    let parentWindow = this._parentWindow;
    let self = this;
    return this._UI.busyUntil(Task.spawn(function* () {
      // Open newapp.xul, which will feed ret.location
      let ret = {location: null, testOptions: testOptions};
      parentWindow.openDialog("chrome://webide/content/newapp.xul", "newapp", "chrome,modal", ret);
      if (!ret.location)
        return;

      // Retrieve added project
      let project = AppProjects.get(ret.location);

      // Select project
      AppManager.selectedProject = project;

      self._telemetry.actionOccurred("webideNewProject");
    }), "creating new app");
  },

  importPackagedApp: function (location) {
    let parentWindow = this._parentWindow;
    let UI = this._UI;
    return UI.busyUntil(Task.spawn(function* () {
      let directory = yield utils.getPackagedDirectory(parentWindow, location);

      if (!directory) {
        // User cancelled directory selection
        return;
      }

      yield UI.importAndSelectApp(directory);
    }), "importing packaged app");
  },

  importHostedApp: function (location) {
    let parentWindow = this._parentWindow;
    let UI = this._UI;
    return UI.busyUntil(Task.spawn(function* () {
      let url = utils.getHostedURL(parentWindow, location);

      if (!url) {
        return;
      }

      yield UI.importAndSelectApp(url);
    }), "importing hosted app");
  },

  /**
   * opts: {
   *   panel: Object,     currenl project panel node
   *   name: String,      name of the project
   *   icon: String       path of the project icon
   * }
   */
  _renderProjectItem: function (opts) {
    let span = opts.panel.querySelector("span") || this._doc.createElement("span");
    span.textContent = opts.name;
    let icon = opts.panel.querySelector("img") || this._doc.createElement("img");
    icon.className = "project-image";
    icon.setAttribute("src", opts.icon);
    opts.panel.appendChild(icon);
    opts.panel.appendChild(span);
    opts.panel.setAttribute("title", opts.name);
  },

  refreshTabs: function () {
    if (AppManager.connected) {
      return AppManager.listTabs().then(() => {
        this.updateTabs();
      }).catch(console.error);
    }
  },

  updateTabs: function () {
    let tabsHeaderNode = this._doc.querySelector("#panel-header-tabs");
    let tabsNode = this._doc.querySelector("#project-panel-tabs");

    while (tabsNode.hasChildNodes()) {
      tabsNode.firstChild.remove();
    }

    if (!AppManager.connected) {
      tabsHeaderNode.setAttribute("hidden", "true");
      return;
    }

    let tabs = AppManager.tabStore.tabs;

    tabsHeaderNode.removeAttribute("hidden");

    for (let i = 0; i < tabs.length; i++) {
      let tab = tabs[i];
      let URL = this._parentWindow.URL;
      let url;
      try {
        url = new URL(tab.url);
      } catch (e) {
        // Don't try to handle invalid URLs
        continue;
      }
      // Wanted to use nsIFaviconService here, but it only works for visited
      // tabs, so that's no help for any remote tabs.  Maybe some favicon wizard
      // knows how to get high-res favicons easily, or we could offer actor
      // support for this (bug 1061654).
      if (url.origin) {
        tab.favicon = url.origin + "/favicon.ico";
      }
      tab.name = tab.title || Strings.GetStringFromName("project_tab_loading");
      if (url.protocol.startsWith("http")) {
        tab.name = url.hostname + ": " + tab.name;
      }
      let panelItemNode = this._doc.createElement(this._panelNodeEl);
      panelItemNode.className = "panel-item";
      tabsNode.appendChild(panelItemNode);
      this._renderProjectItem({
        panel: panelItemNode,
        name: tab.name,
        icon: tab.favicon || AppManager.DEFAULT_PROJECT_ICON
      });
      panelItemNode.addEventListener("click", () => {
        AppManager.selectedProject = {
          type: "tab",
          app: tab,
          icon: tab.favicon || AppManager.DEFAULT_PROJECT_ICON,
          location: tab.url,
          name: tab.name
        };
      }, true);
    }

    return Promise.resolve();
  },

  updateApps: function () {
    let doc = this._doc;
    let runtimeappsHeaderNode = doc.querySelector("#panel-header-runtimeapps");
    let sortedApps = [];
    for (let [manifestURL, app] of AppManager.apps) {
      sortedApps.push(app);
    }
    sortedApps = sortedApps.sort((a, b) => {
      return a.manifest.name > b.manifest.name;
    });
    let mainProcess = AppManager.isMainProcessDebuggable();
    if (AppManager.connected && (sortedApps.length > 0 || mainProcess)) {
      runtimeappsHeaderNode.removeAttribute("hidden");
    } else {
      runtimeappsHeaderNode.setAttribute("hidden", "true");
    }

    let runtimeAppsNode = doc.querySelector("#project-panel-runtimeapps");
    while (runtimeAppsNode.hasChildNodes()) {
      runtimeAppsNode.firstChild.remove();
    }

    if (mainProcess) {
      let panelItemNode = doc.createElement(this._panelNodeEl);
      panelItemNode.className = "panel-item";
      this._renderProjectItem({
        panel: panelItemNode,
        name: Strings.GetStringFromName("mainProcess_label"),
        icon: AppManager.DEFAULT_PROJECT_ICON
      });
      runtimeAppsNode.appendChild(panelItemNode);
      panelItemNode.addEventListener("click", () => {
        AppManager.selectedProject = {
          type: "mainProcess",
          name: Strings.GetStringFromName("mainProcess_label"),
          icon: AppManager.DEFAULT_PROJECT_ICON
        };
      }, true);
    }

    for (let i = 0; i < sortedApps.length; i++) {
      let app = sortedApps[i];
      let panelItemNode = doc.createElement(this._panelNodeEl);
      panelItemNode.className = "panel-item";
      this._renderProjectItem({
        panel: panelItemNode,
        name: app.manifest.name,
        icon: app.iconURL || AppManager.DEFAULT_PROJECT_ICON
      });
      runtimeAppsNode.appendChild(panelItemNode);
      panelItemNode.addEventListener("click", () => {
        AppManager.selectedProject = {
          type: "runtimeApp",
          app: app.manifest,
          icon: app.iconURL || AppManager.DEFAULT_PROJECT_ICON,
          name: app.manifest.name
        };
      }, true);
    }

    return Promise.resolve();
  },

  updateCommands: function () {
    let doc = this._doc;
    let newAppCmd;
    let packagedAppCmd;
    let hostedAppCmd;

    newAppCmd = doc.querySelector("#new-app");
    packagedAppCmd = doc.querySelector("#packaged-app");
    hostedAppCmd = doc.querySelector("#hosted-app");

    if (!newAppCmd || !packagedAppCmd || !hostedAppCmd) {
      return;
    }

    if (this._parentWindow.document.querySelector("window").classList.contains("busy")) {
      newAppCmd.setAttribute("disabled", "true");
      packagedAppCmd.setAttribute("disabled", "true");
      hostedAppCmd.setAttribute("disabled", "true");
      return;
    }

    newAppCmd.removeAttribute("disabled");
    packagedAppCmd.removeAttribute("disabled");
    hostedAppCmd.removeAttribute("disabled");
  },

  /**
   * Trigger an update of the project and remote runtime list.
   * @param options object (optional)
   *        An |options| object containing a type of |apps| or |tabs| will limit
   *        what is updated to only those sections.
   */
  update: function (options) {
    if (options && options.type === "apps") {
      return this.updateApps();
    } else if (options && options.type === "tabs") {
      return this.updateTabs();
    }

    return new Promise((resolve, reject) => {
      let doc = this._doc;
      let projectsNode = doc.querySelector("#project-panel-projects");

      while (projectsNode.hasChildNodes()) {
        projectsNode.firstChild.remove();
      }

      AppProjects.load().then(() => {
        let projects = AppProjects.projects;
        for (let i = 0; i < projects.length; i++) {
          let project = projects[i];
          let panelItemNode = doc.createElement(this._panelNodeEl);
          panelItemNode.className = "panel-item";
          projectsNode.appendChild(panelItemNode);
          if (!project.validationStatus) {
            // The result of the validation process (storing names, icons, …) is not stored in
            // the IndexedDB database when App Manager v1 is used.
            // We need to run the validation again and update the name and icon of the app.
            AppManager.validateAndUpdateProject(project).then(() => {
              this._renderProjectItem({
                panel: panelItemNode,
                name: project.name,
                icon: project.icon
              });
            });
          } else {
            this._renderProjectItem({
              panel: panelItemNode,
              name: project.name || AppManager.DEFAULT_PROJECT_NAME,
              icon: project.icon || AppManager.DEFAULT_PROJECT_ICON
            });
          }
          panelItemNode.addEventListener("click", () => {
            AppManager.selectedProject = project;
          }, true);
        }

        resolve();
      }, reject);

      // List remote apps and the main process, if they exist
      this.updateApps();

      // Build the tab list right now, so it's fast...
      this.updateTabs();

      // But re-list them and rebuild, in case any tabs navigated since the last
      // time they were listed.
      if (AppManager.connected) {
        AppManager.listTabs().then(() => {
          this.updateTabs();
        }).catch(console.error);
      }
    });
  },

  destroy: function () {
    this._doc = null;
    AppManager.off("app-manager-update", this.appManagerUpdate);
    this._UI.off("webide-update", this.onWebIDEUpdate);
    this._UI = null;
    this._parentWindow = null;
    this._panelNodeEl = null;
  }
};
PK
!<e�(Fchrome/devtools/modules/devtools/client/webide/modules/runtime-list.js/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If 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 Services = require("Services");
const {AppManager} = require("devtools/client/webide/modules/app-manager");
const EventEmitter = require("devtools/shared/event-emitter");
const {RuntimeScanners, WiFiScanner} = require("devtools/client/webide/modules/runtimes");
const {Devices} = require("resource://devtools/shared/apps/Devices.jsm");
const {Task} = require("devtools/shared/task");
const utils = require("devtools/client/webide/modules/utils");

const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties");

var RuntimeList;

module.exports = RuntimeList = function (window, parentWindow) {
  EventEmitter.decorate(this);
  this._doc = window.document;
  this._UI = parentWindow.UI;
  this._Cmds = parentWindow.Cmds;
  this._parentWindow = parentWindow;
  this._panelNodeEl = "button";
  this._panelBoxEl = "div";

  this.onWebIDEUpdate = this.onWebIDEUpdate.bind(this);
  this._UI.on("webide-update", this.onWebIDEUpdate);

  AppManager.init();
  this.appManagerUpdate = this.appManagerUpdate.bind(this);
  AppManager.on("app-manager-update", this.appManagerUpdate);
};

RuntimeList.prototype = {
  get doc() {
    return this._doc;
  },

  appManagerUpdate: function (what, details) {
    // Got a message from app-manager.js
    // See AppManager.update() for descriptions of what these events mean.
    switch (what) {
      case "runtime-list":
        this.update();
        break;
      case "connection":
      case "runtime-global-actors":
        this.updateCommands();
        break;
    }
  },

  onWebIDEUpdate: function (what, details) {
    if (what == "busy" || what == "unbusy") {
      this.updateCommands();
    }
  },

  takeScreenshot: function () {
    this._Cmds.takeScreenshot();
  },

  showRuntimeDetails: function () {
    this._Cmds.showRuntimeDetails();
  },

  showDevicePreferences: function () {
    this._Cmds.showDevicePrefs();
  },

  showSettings: function () {
    this._Cmds.showSettings();
  },

  showTroubleShooting: function () {
    this._Cmds.showTroubleShooting();
  },

  showAddons: function () {
    this._Cmds.showAddons();
  },

  refreshScanners: function () {
    RuntimeScanners.scan();
  },

  updateCommands: function () {
    let doc = this._doc;

    // Runtime commands
    let screenshotCmd = doc.querySelector("#runtime-screenshot");
    let detailsCmd = doc.querySelector("#runtime-details");
    let disconnectCmd = doc.querySelector("#runtime-disconnect");
    let devicePrefsCmd = doc.querySelector("#runtime-preferences");
    let settingsCmd = doc.querySelector("#runtime-settings");

    if (AppManager.connected) {
      if (AppManager.deviceFront) {
        detailsCmd.removeAttribute("disabled");
        screenshotCmd.removeAttribute("disabled");
      }
      if (AppManager.preferenceFront) {
        devicePrefsCmd.removeAttribute("disabled");
      }
      disconnectCmd.removeAttribute("disabled");
    } else {
      detailsCmd.setAttribute("disabled", "true");
      screenshotCmd.setAttribute("disabled", "true");
      disconnectCmd.setAttribute("disabled", "true");
      devicePrefsCmd.setAttribute("disabled", "true");
      settingsCmd.setAttribute("disabled", "true");
    }
  },

  update: function () {
    let doc = this._doc;
    let wifiHeaderNode = doc.querySelector("#runtime-header-wifi");

    if (WiFiScanner.allowed) {
      wifiHeaderNode.removeAttribute("hidden");
    } else {
      wifiHeaderNode.setAttribute("hidden", "true");
    }

    let usbListNode = doc.querySelector("#runtime-panel-usb");
    let wifiListNode = doc.querySelector("#runtime-panel-wifi");
    let otherListNode = doc.querySelector("#runtime-panel-other");
    let noHelperNode = doc.querySelector("#runtime-panel-noadbhelper");
    let noUSBNode = doc.querySelector("#runtime-panel-nousbdevice");

    if (Devices.helperAddonInstalled) {
      noHelperNode.setAttribute("hidden", "true");
    } else {
      noHelperNode.removeAttribute("hidden");
    }

    let runtimeList = AppManager.runtimeList;

    if (!runtimeList) {
      return;
    }

    if (runtimeList.usb.length === 0 && Devices.helperAddonInstalled) {
      noUSBNode.removeAttribute("hidden");
    } else {
      noUSBNode.setAttribute("hidden", "true");
    }

    for (let [type, parent] of [
      ["usb", usbListNode],
      ["wifi", wifiListNode],
      ["other", otherListNode],
    ]) {
      while (parent.hasChildNodes()) {
        parent.firstChild.remove();
      }
      for (let runtime of runtimeList[type]) {
        let r = runtime;
        let panelItemNode = doc.createElement(this._panelBoxEl);
        panelItemNode.className = "panel-item-complex";

        let connectButton = doc.createElement(this._panelNodeEl);
        connectButton.className = "panel-item runtime-panel-item-" + type;
        connectButton.textContent = r.name;

        connectButton.addEventListener("click", () => {
          this._UI.dismissErrorNotification();
          this._UI.connectToRuntime(r);
        }, true);
        panelItemNode.appendChild(connectButton);

        if (r.configure) {
          let configButton = doc.createElement(this._panelNodeEl);
          configButton.className = "configure-button";
          configButton.addEventListener("click", r.configure.bind(r), true);
          panelItemNode.appendChild(configButton);
        }

        parent.appendChild(panelItemNode);
      }
    }
  },

  destroy: function () {
    this._doc = null;
    AppManager.off("app-manager-update", this.appManagerUpdate);
    this._UI.off("webide-update", this.onWebIDEUpdate);
    this._UI = null;
    this._Cmds = null;
    this._parentWindow = null;
    this._panelNodeEl = null;
  }
};
PK
!<n0��7�7Bchrome/devtools/modules/devtools/client/webide/modules/runtimes.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {Ci} = require("chrome");
const Services = require("Services");
const {Devices} = require("resource://devtools/shared/apps/Devices.jsm");
const {Connection} = require("devtools/shared/client/connection-manager");
const {DebuggerServer} = require("devtools/server/main");
const discovery = require("devtools/shared/discovery/discovery");
const EventEmitter = require("devtools/shared/event-emitter");
const promise = require("promise");
loader.lazyRequireGetter(this, "AuthenticationResult",
  "devtools/shared/security/auth", true);
loader.lazyRequireGetter(this, "DevToolsUtils",
  "devtools/shared/DevToolsUtils");

const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties");

/**
 * Runtime and Scanner API
 *
 * |RuntimeScanners| maintains a set of |Scanner| objects that produce one or
 * more |Runtime|s to connect to.  Add-ons can extend the set of known runtimes
 * by registering additional |Scanner|s that emit them.
 *
 * Each |Scanner| must support the following API:
 *
 * enable()
 *   Bind any event handlers and start any background work the scanner needs to
 *   maintain an updated set of |Runtime|s.
 *   Called when the first consumer (such as WebIDE) actively interested in
 *   maintaining the |Runtime| list enables the registry.
 * disable()
 *   Unbind any event handlers and stop any background work the scanner needs to
 *   maintain an updated set of |Runtime|s.
 *   Called when the last consumer (such as WebIDE) actively interested in
 *   maintaining the |Runtime| list disables the registry.
 * emits "runtime-list-updated"
 *   If the set of runtimes a |Scanner| manages has changed, it must emit this
 *   event to notify consumers of changes.
 * scan()
 *   Actively refreshes the list of runtimes the scanner knows about.  If your
 *   scanner uses an active scanning approach (as opposed to listening for
 *   events when changes occur), the bulk of the work would be done here.
 *   @return Promise
 *           Should be resolved when scanning is complete.  If scanning has no
 *           well-defined end point, you can resolve immediately, as long as
 *           update event is emitted later when changes are noticed.
 * listRuntimes()
 *   Return the current list of runtimes known to the |Scanner| instance.
 *   @return Iterable
 *
 * Each |Runtime| must support the following API:
 *
 * |type| field
 *   The |type| must be one of the values from the |RuntimeTypes| object.  This
 *   is used for Telemetry and to support displaying sets of |Runtime|s
 *   categorized by type.
 * |id| field
 *   An identifier that is unique in the set of all runtimes with the same
 *   |type|.  WebIDE tries to save the last used runtime via type + id, and
 *   tries to locate it again in the next session, so this value should attempt
 *   to be stable across Firefox sessions.
 * |name| field
 *   A user-visible label to identify the runtime that will be displayed in a
 *   runtime list.
 * |prolongedConnection| field
 *   A boolean value which should be |true| if the connection process is
 *   expected to take a unknown or large amount of time.  A UI may use this as a
 *   hint to skip timeouts or other time-based code paths.
 * connect()
 *   Configure the passed |connection| object with any settings need to
 *   successfully connect to the runtime, and call the |connection|'s connect()
 *   method.
 *   @param  Connection connection
 *           A |Connection| object from the DevTools |ConnectionManager|.
 *   @return Promise
 *           Resolved once you've called the |connection|'s connect() method.
 * configure() OPTIONAL
 *   Show a configuration screen if the runtime is configurable.
 */

/* SCANNER REGISTRY */

var RuntimeScanners = {

  _enabledCount: 0,
  _scanners: new Set(),

  get enabled() {
    return !!this._enabledCount;
  },

  add(scanner) {
    if (this.enabled) {
      // Enable any scanner added while globally enabled
      this._enableScanner(scanner);
    }
    this._scanners.add(scanner);
    this._emitUpdated();
  },

  remove(scanner) {
    this._scanners.delete(scanner);
    if (this.enabled) {
      // Disable any scanner removed while globally enabled
      this._disableScanner(scanner);
    }
    this._emitUpdated();
  },

  has(scanner) {
    return this._scanners.has(scanner);
  },

  scan() {
    if (!this.enabled) {
      return promise.resolve();
    }

    if (this._scanPromise) {
      return this._scanPromise;
    }

    let promises = [];

    for (let scanner of this._scanners) {
      promises.push(scanner.scan());
    }

    this._scanPromise = promise.all(promises);

    // Reset pending promise
    this._scanPromise.then(() => {
      this._scanPromise = null;
    }, () => {
      this._scanPromise = null;
    });

    return this._scanPromise;
  },

  listRuntimes: function* () {
    for (let scanner of this._scanners) {
      for (let runtime of scanner.listRuntimes()) {
        yield runtime;
      }
    }
  },

  _emitUpdated() {
    this.emit("runtime-list-updated");
  },

  enable() {
    if (this._enabledCount++ !== 0) {
      // Already enabled scanners during a previous call
      return;
    }
    this._emitUpdated = this._emitUpdated.bind(this);
    for (let scanner of this._scanners) {
      this._enableScanner(scanner);
    }
  },

  _enableScanner(scanner) {
    scanner.enable();
    scanner.on("runtime-list-updated", this._emitUpdated);
  },

  disable() {
    if (--this._enabledCount !== 0) {
      // Already disabled scanners during a previous call
      return;
    }
    for (let scanner of this._scanners) {
      this._disableScanner(scanner);
    }
  },

  _disableScanner(scanner) {
    scanner.off("runtime-list-updated", this._emitUpdated);
    scanner.disable();
  },

};

EventEmitter.decorate(RuntimeScanners);

exports.RuntimeScanners = RuntimeScanners;

/* SCANNERS */

/**
 * This is a lazy ADB scanner shim which only tells the ADB Helper to start and
 * stop as needed.  The real scanner that lists devices lives in ADB Helper.
 * ADB Helper 0.8.0 and later wait until these signals are received before
 * starting ADB polling.  For earlier versions, they have no effect.
 */
var LazyAdbScanner = {

  enable() {
    Devices.emit("adb-start-polling");
  },

  disable() {
    Devices.emit("adb-stop-polling");
  },

  scan() {
    return promise.resolve();
  },

  listRuntimes: function () {
    return [];
  }

};

EventEmitter.decorate(LazyAdbScanner);
RuntimeScanners.add(LazyAdbScanner);

var WiFiScanner = {

  _runtimes: [],

  init() {
    this.updateRegistration();
    Services.prefs.addObserver(this.ALLOWED_PREF, this);
  },

  enable() {
    this._updateRuntimes = this._updateRuntimes.bind(this);
    discovery.on("devtools-device-added", this._updateRuntimes);
    discovery.on("devtools-device-updated", this._updateRuntimes);
    discovery.on("devtools-device-removed", this._updateRuntimes);
    this._updateRuntimes();
  },

  disable() {
    discovery.off("devtools-device-added", this._updateRuntimes);
    discovery.off("devtools-device-updated", this._updateRuntimes);
    discovery.off("devtools-device-removed", this._updateRuntimes);
  },

  _emitUpdated() {
    this.emit("runtime-list-updated");
  },

  _updateRuntimes() {
    this._runtimes = [];
    for (let device of discovery.getRemoteDevicesWithService("devtools")) {
      this._runtimes.push(new WiFiRuntime(device));
    }
    this._emitUpdated();
  },

  scan() {
    discovery.scan();
    return promise.resolve();
  },

  listRuntimes: function () {
    return this._runtimes;
  },

  ALLOWED_PREF: "devtools.remote.wifi.scan",

  get allowed() {
    return Services.prefs.getBoolPref(this.ALLOWED_PREF);
  },

  updateRegistration() {
    if (this.allowed) {
      RuntimeScanners.add(WiFiScanner);
    } else {
      RuntimeScanners.remove(WiFiScanner);
    }
    this._emitUpdated();
  },

  observe(subject, topic, data) {
    if (data !== WiFiScanner.ALLOWED_PREF) {
      return;
    }
    WiFiScanner.updateRegistration();
  }

};

EventEmitter.decorate(WiFiScanner);
WiFiScanner.init();

exports.WiFiScanner = WiFiScanner;

var StaticScanner = {
  enable() {},
  disable() {},
  scan() { return promise.resolve(); },
  listRuntimes() {
    let runtimes = [gRemoteRuntime];
    if (Services.prefs.getBoolPref("devtools.webide.enableLocalRuntime")) {
      runtimes.push(gLocalRuntime);
    }
    return runtimes;
  }
};

EventEmitter.decorate(StaticScanner);
RuntimeScanners.add(StaticScanner);

/* RUNTIMES */

// These type strings are used for logging events to Telemetry.
// You must update Histograms.json if new types are added.
var RuntimeTypes = exports.RuntimeTypes = {
  USB: "USB",
  WIFI: "WIFI",
  REMOTE: "REMOTE",
  LOCAL: "LOCAL",
  OTHER: "OTHER"
};

function WiFiRuntime(deviceName) {
  this.deviceName = deviceName;
}

WiFiRuntime.prototype = {
  type: RuntimeTypes.WIFI,
  // Mark runtime as taking a long time to connect
  prolongedConnection: true,
  connect: function (connection) {
    let service = discovery.getRemoteService("devtools", this.deviceName);
    if (!service) {
      return promise.reject(new Error("Can't find device: " + this.name));
    }
    connection.advertisement = service;
    connection.authenticator.sendOOB = this.sendOOB;
    // Disable the default connection timeout, since QR scanning can take an
    // unknown amount of time.  This prevents spurious errors (even after
    // eventual success) from being shown.
    connection.timeoutDelay = 0;
    connection.connect();
    return promise.resolve();
  },
  get id() {
    return this.deviceName;
  },
  get name() {
    return this.deviceName;
  },

  /**
   * During OOB_CERT authentication, a notification dialog like this is used to
   * to display a token which the user must transfer through some mechanism to the
   * server to authenticate the devices.
   *
   * This implementation presents the token as text for the user to transfer
   * manually.  For a mobile device, you should override this implementation with
   * something more convenient, such as displaying a QR code.
   *
   * This method receives an object containing:
   * @param host string
   *        The host name or IP address of the debugger server.
   * @param port number
   *        The port number of the debugger server.
   * @param cert object (optional)
   *        The server's cert details.
   * @param authResult AuthenticationResult
   *        Authentication result sent from the server.
   * @param oob object (optional)
   *        The token data to be transferred during OOB_CERT step 8:
   *        * sha256: hash(ClientCert)
   *        * k     : K(random 128-bit number)
   * @return object containing:
   *         * close: Function to hide the notification
   */
  sendOOB(session) {
    const WINDOW_ID = "devtools:wifi-auth";
    let { authResult } = session;
    // Only show in the PENDING state
    if (authResult != AuthenticationResult.PENDING) {
      throw new Error("Expected PENDING result, got " + authResult);
    }

    // Listen for the window our prompt opens, so we can close it programatically
    let promptWindow;
    let windowListener = {
      onOpenWindow(xulWindow) {
        let win = xulWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                           .getInterface(Ci.nsIDOMWindow);
        win.addEventListener("load", function () {
          if (win.document.documentElement.getAttribute("id") != WINDOW_ID) {
            return;
          }
          // Found the window
          promptWindow = win;
          Services.wm.removeListener(windowListener);
        }, {once: true});
      },
      onCloseWindow() {},
    };
    Services.wm.addListener(windowListener);

    // |openDialog| is typically a blocking API, so |executeSoon| to get around this
    DevToolsUtils.executeSoon(() => {
      // Height determines the size of the QR code.  Force a minimum size to
      // improve scanability.
      const MIN_HEIGHT = 600;
      let win = Services.wm.getMostRecentWindow("devtools:webide");
      let width = win.outerWidth * 0.8;
      let height = Math.max(win.outerHeight * 0.5, MIN_HEIGHT);
      win.openDialog("chrome://webide/content/wifi-auth.xhtml",
                     WINDOW_ID,
                     "modal=yes,width=" + width + ",height=" + height, session);
    });

    return {
      close() {
        if (!promptWindow) {
          return;
        }
        promptWindow.close();
        promptWindow = null;
      }
    };
  }
};

// For testing use only
exports._WiFiRuntime = WiFiRuntime;

var gLocalRuntime = {
  type: RuntimeTypes.LOCAL,
  connect: function (connection) {
    DebuggerServer.init();
    DebuggerServer.registerAllActors();
    DebuggerServer.allowChromeProcess = true;
    connection.host = null; // Force Pipe transport
    connection.port = null;
    connection.connect();
    return promise.resolve();
  },
  get id() {
    return "local";
  },
  get name() {
    return Strings.GetStringFromName("local_runtime");
  },
};

// For testing use only
exports._gLocalRuntime = gLocalRuntime;

var gRemoteRuntime = {
  type: RuntimeTypes.REMOTE,
  connect: function (connection) {
    let win = Services.wm.getMostRecentWindow("devtools:webide");
    if (!win) {
      return promise.reject(new Error("No WebIDE window found"));
    }
    let ret = {value: connection.host + ":" + connection.port};
    let title = Strings.GetStringFromName("remote_runtime_promptTitle");
    let message = Strings.GetStringFromName("remote_runtime_promptMessage");
    let ok = Services.prompt.prompt(win, title, message, ret, null, {});
    let [host, port] = ret.value.split(":");
    if (!ok) {
      return promise.reject({canceled: true});
    }
    if (!host || !port) {
      return promise.reject(new Error("Invalid host or port"));
    }
    connection.host = host;
    connection.port = port;
    connection.connect();
    return promise.resolve();
  },
  get name() {
    return Strings.GetStringFromName("remote_runtime");
  },
};

// For testing use only
exports._gRemoteRuntime = gRemoteRuntime;
PK
!<�%9'))Cchrome/devtools/modules/devtools/client/webide/modules/tab-store.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 { Cu } = require("chrome");

const { TargetFactory } = require("devtools/client/framework/target");
const EventEmitter = require("devtools/shared/event-emitter");
const { Connection } = require("devtools/shared/client/connection-manager");
const { Task } = require("devtools/shared/task");

const _knownTabStores = new WeakMap();

var TabStore;

module.exports = TabStore = function (connection) {
  // If we already know about this connection,
  // let's re-use the existing store.
  if (_knownTabStores.has(connection)) {
    return _knownTabStores.get(connection);
  }

  _knownTabStores.set(connection, this);

  EventEmitter.decorate(this);

  this._resetStore();

  this.destroy = this.destroy.bind(this);
  this._onStatusChanged = this._onStatusChanged.bind(this);

  this._connection = connection;
  this._connection.once(Connection.Events.DESTROYED, this.destroy);
  this._connection.on(Connection.Events.STATUS_CHANGED, this._onStatusChanged);
  this._onTabListChanged = this._onTabListChanged.bind(this);
  this._onTabNavigated = this._onTabNavigated.bind(this);
  this._onStatusChanged();
  return this;
};

TabStore.prototype = {

  destroy: function () {
    if (this._connection) {
      // While this.destroy is bound using .once() above, that event may not
      // have occurred when the TabStore client calls destroy, so we
      // manually remove it here.
      this._connection.off(Connection.Events.DESTROYED, this.destroy);
      this._connection.off(Connection.Events.STATUS_CHANGED, this._onStatusChanged);
      _knownTabStores.delete(this._connection);
      this._connection = null;
    }
  },

  _resetStore: function () {
    this.response = null;
    this.tabs = [];
    this._selectedTab = null;
    this._selectedTabTargetPromise = null;
  },

  _onStatusChanged: function () {
    if (this._connection.status == Connection.Status.CONNECTED) {
      // Watch for changes to remote browser tabs
      this._connection.client.addListener("tabListChanged",
                                          this._onTabListChanged);
      this._connection.client.addListener("tabNavigated",
                                          this._onTabNavigated);
      this.listTabs();
    } else {
      if (this._connection.client) {
        this._connection.client.removeListener("tabListChanged",
                                               this._onTabListChanged);
        this._connection.client.removeListener("tabNavigated",
                                               this._onTabNavigated);
      }
      this._resetStore();
    }
  },

  _onTabListChanged: function () {
    this.listTabs().then(() => this.emit("tab-list"))
                   .catch(console.error);
  },

  _onTabNavigated: function (e, { from, title, url }) {
    if (!this._selectedTab || from !== this._selectedTab.actor) {
      return;
    }
    this._selectedTab.url = url;
    this._selectedTab.title = title;
    this.emit("navigate");
  },

  listTabs: function () {
    if (!this._connection || !this._connection.client) {
      return Promise.reject(new Error("Can't listTabs, not connected."));
    }

    return new Promise((resolve, reject) => {
      this._connection.client.listTabs().then(response => {
        if (response.error) {
          this._connection.disconnect();
          reject(response.error);
          return;
        }
        let tabsChanged = JSON.stringify(this.tabs) !== JSON.stringify(response.tabs);
        this.response = response;
        this.tabs = response.tabs;
        this._checkSelectedTab();
        if (tabsChanged) {
          this.emit("tab-list");
        }
        resolve(response);
      });
    });
  },

  // TODO: Tab "selection" should really take place by creating a TabProject
  // which is the selected project.  This should be done as part of the
  // project-agnostic work.
  _selectedTab: null,
  _selectedTabTargetPromise: null,
  get selectedTab() {
    return this._selectedTab;
  },
  set selectedTab(tab) {
    if (this._selectedTab === tab) {
      return;
    }
    this._selectedTab = tab;
    this._selectedTabTargetPromise = null;
    // Attach to the tab to follow navigation events
    if (this._selectedTab) {
      this.getTargetForTab();
    }
  },

  _checkSelectedTab: function () {
    if (!this._selectedTab) {
      return;
    }
    let alive = this.tabs.some(tab => {
      return tab.actor === this._selectedTab.actor;
    });
    if (!alive) {
      this._selectedTab = null;
      this._selectedTabTargetPromise = null;
      this.emit("closed");
    }
  },

  getTargetForTab: function () {
    if (this._selectedTabTargetPromise) {
      return this._selectedTabTargetPromise;
    }
    let store = this;
    this._selectedTabTargetPromise = Task.spawn(function* () {
      // If you connect to a tab, then detach from it, the root actor may have
      // de-listed the actors that belong to the tab.  This breaks the toolbox
      // if you try to connect to the same tab again.  To work around this
      // issue, we force a "listTabs" request before connecting to a tab.
      yield store.listTabs();
      return TargetFactory.forRemoteTab({
        form: store._selectedTab,
        client: store._connection.client,
        chrome: false
      });
    });
    this._selectedTabTargetPromise.then(target => {
      target.once("close", () => {
        this._selectedTabTargetPromise = null;
      });
    });
    return this._selectedTabTargetPromise;
  },

};
PK
!<�T����?chrome/devtools/modules/devtools/client/webide/modules/utils.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 { Cc, Cu, Ci } = require("chrome");
const { FileUtils } = require("resource://gre/modules/FileUtils.jsm");
const Services = require("Services");
const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties");

function doesFileExist(location) {
  let file = new FileUtils.File(location);
  return file.exists();
}
exports.doesFileExist = doesFileExist;

function _getFile(location, ...pickerParams) {
  if (location) {
    return Promise.resolve(new FileUtils.File(location));
  }
  let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
  fp.init(...pickerParams);

  return new Promise(resolve => {
    fp.open(res => {
      if (res == Ci.nsIFilePicker.returnCancel) {
        resolve(null);
      } else {
        resolve(fp.file);
      }
    });
  });
}

function getCustomBinary(window, location) {
  return _getFile(location, window, Strings.GetStringFromName("selectCustomBinary_title"), Ci.nsIFilePicker.modeOpen);
}
exports.getCustomBinary = getCustomBinary;

function getCustomProfile(window, location) {
  return _getFile(location, window, Strings.GetStringFromName("selectCustomProfile_title"), Ci.nsIFilePicker.modeGetFolder);
}
exports.getCustomProfile = getCustomProfile;

function getPackagedDirectory(window, location) {
  return _getFile(location, window, Strings.GetStringFromName("importPackagedApp_title"), Ci.nsIFilePicker.modeGetFolder);
}
exports.getPackagedDirectory = getPackagedDirectory;

function getHostedURL(window, location) {
  let ret = { value: null };

  if (!location) {
    Services.prompt.prompt(window,
        Strings.GetStringFromName("importHostedApp_title"),
        Strings.GetStringFromName("importHostedApp_header"),
        ret, null, {});
    location = ret.value;
  }

  if (!location) {
    return null;
  }

  // Clean location string and add "http://" if missing
  location = location.trim();
  try { // Will fail if no scheme
    Services.io.extractScheme(location);
  } catch (e) {
    location = "http://" + location;
  }
  return location;
}
exports.getHostedURL = getHostedURL;
PK
!<U&((��Fchrome/devtools/modules/devtools/server/actors/accessibility-parent.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Cc, Ci } = require("chrome");
const Services = require("Services");
const PREF_ACCESSIBILITY_FORCE_DISABLED = "accessibility.force_disabled";

/**
 * A helper class that does all the work related to accessibility service
 * lifecycle (initialization, shutdown, consumer changes, etc) in parent
 * parent process. It is not guaranteed that the AccessibilityActor starts in
 * parent process and thus triggering these lifecycle functions directly is
 * extremely unreliable.
 */
class AccessibilityParent {
  constructor(mm, prefix) {
    this._msgName = `debug:${prefix}accessibility`;
    this.onAccessibilityMessage = this.onAccessibilityMessage.bind(this);
    this.setMessageManager(mm);

    this.userPref = Services.prefs.getIntPref(PREF_ACCESSIBILITY_FORCE_DISABLED);
    Services.obs.addObserver(this, "a11y-consumers-changed");
    Services.prefs.addObserver(PREF_ACCESSIBILITY_FORCE_DISABLED, this);

    if (this.enabled && !this.accService) {
      // Set a local reference to an accessibility service if accessibility was
      // started elsewhere to ensure that parent process a11y service does not
      // get GC'ed away.
      this.accService = Cc["@mozilla.org/accessibilityService;1"].getService(
        Ci.nsIAccessibilityService);
    }

    this.messageManager.sendAsyncMessage(`${this._msgName}:event`, {
      topic: "initialized",
      data: {
        canBeDisabled: this.canBeDisabled,
        canBeEnabled: this.canBeEnabled
      }
    });
  }

  /**
   * Set up message manager listener to listen for messages coming from the
   * AccessibilityActor when it is instantiated in the child process.
   *
   * @param {Object} mm
   *        Message manager that corresponds to the current content tab.
   */
  setMessageManager(mm) {
    if (this.messageManager === mm) {
      return;
    }

    if (this.messageManager) {
      // If the browser was swapped we need to reset the message manager.
      let oldMM = this.messageManager;
      oldMM.removeMessageListener(this._msgName, this.onAccessibilityMessage);
    }

    this.messageManager = mm;
    if (mm) {
      mm.addMessageListener(this._msgName, this.onAccessibilityMessage);
    }
  }

  /**
   * Content AccessibilityActor message listener.
   *
   * @param  {String} msg
   *         Name of the action to perform.
   */
  onAccessibilityMessage(msg) {
    let { action } = msg.json;
    switch (action) {
      case "enable":
        this.enable();
        break;

      case "disable":
        this.disable();
        break;

      case "disconnect":
        this.destroy();
        break;

      default:
        break;
    }
  }

  observe(subject, topic, data) {
    if (topic === "a11y-consumers-changed") {
      // This event is fired when accessibility service consumers change. Since
      // this observer lives in parent process there are 2 possible consumers of
      // a11y service: XPCOM and PlatformAPI (e.g. screen readers). We only care
      // about PlatformAPI consumer changes because when set, we can no longer
      // disable accessibility service.
      let { PlatformAPI } = JSON.parse(data);
      this.messageManager.sendAsyncMessage(`${this._msgName}:event`, {
        topic: "can-be-disabled-change",
        data: !PlatformAPI
      });
    } else if (!this.disabling && topic === "nsPref:changed" &&
               data === PREF_ACCESSIBILITY_FORCE_DISABLED) {
      // PREF_ACCESSIBILITY_FORCE_DISABLED preference change event. When set to
      // >=1, it means that the user wants to disable accessibility service and
      // prevent it from starting in the future. Note: we also check
      // this.disabling state when handling this pref change because this is how
      // we disable the accessibility inspector itself.
      this.messageManager.sendAsyncMessage(`${this._msgName}:event`, {
        topic: "can-be-enabled-change",
        data: Services.prefs.getIntPref(PREF_ACCESSIBILITY_FORCE_DISABLED) < 1
      });
    }
  }

  /**
   * A getter that indicates if accessibility service is enabled.
   *
   * @return {Boolean}
   *         True if accessibility service is on.
   */
  get enabled() {
    return Services.appinfo.accessibilityEnabled;
  }

  /**
   * A getter that indicates if the accessibility service can be disabled.
   *
   * @return {Boolean}
   *         True if accessibility service can be disabled.
   */
  get canBeDisabled() {
    if (this.enabled) {
      let a11yService = Cc["@mozilla.org/accessibilityService;1"].getService(
        Ci.nsIAccessibilityService);
      let { PlatformAPI } = JSON.parse(a11yService.getConsumers());
      return !PlatformAPI;
    }

    return true;
  }

  /**
   * A getter that indicates if the accessibility service can be enabled.
   *
   * @return {Boolean}
   *         True if accessibility service can be enabled.
   */
  get canBeEnabled() {
    return Services.prefs.getIntPref(PREF_ACCESSIBILITY_FORCE_DISABLED) < 1;
  }

  /**
   * Enable accessibility service (via XPCOM service).
   */
  enable() {
    if (this.enabled || !this.canBeEnabled) {
      return;
    }

    this.accService = Cc["@mozilla.org/accessibilityService;1"].getService(
      Ci.nsIAccessibilityService);
  }

  /**
   * Force disable accessibility service. This method removes the reference to
   * the XPCOM a11y service object and flips the
   * PREF_ACCESSIBILITY_FORCE_DISABLED preference on and off to shutdown a11y
   * service.
   */
  disable() {
    if (!this.enabled || !this.canBeDisabled) {
      return;
    }

    this.disabling = true;
    this.accService = null;
    // Set PREF_ACCESSIBILITY_FORCE_DISABLED to 1 to force disable
    // accessibility service. This is the only way to guarantee an immediate
    // accessibility service shutdown in all processes. This also prevents
    // accessibility service from starting up in the future.
    Services.prefs.setIntPref(PREF_ACCESSIBILITY_FORCE_DISABLED, 1);
    // Set PREF_ACCESSIBILITY_FORCE_DISABLED back to previous default or user
    // set value. This will not start accessibility service until the user
    // activates it again. It simply ensures that accessibility service can
    // start again (when value is below 1).
    Services.prefs.setIntPref(PREF_ACCESSIBILITY_FORCE_DISABLED, this.userPref);
    delete this.disabling;
  }

  /**
   * Destroy thie helper class, remove all listeners and if possible disable
   * accessibility service in the parent process.
   */
  destroy() {
    Services.obs.removeObserver(this, "a11y-consumers-changed");
    Services.prefs.removeObserver(PREF_ACCESSIBILITY_FORCE_DISABLED, this);
    this.setMessageManager(null);
    this.accService = null;
  }
}

/**
 * Setup function that runs in parent process and setups AccessibleActor bits
 * that must always run in parent process.
 *
 * @param  {Object} options.mm
 *         Message manager that corresponds to the current content tab.
 * @param  {String} options.prefix
 *         Unique prefix for message manager messages.
 * @return {Object}
 *         Defines event listeners for when client disconnects or browser gets
 *         swapped.
 */
function setupParentProcess({ mm, prefix }) {
  let accessibility = new AccessibilityParent(mm, prefix);

  return {
    onBrowserSwap: newMM => accessibility.setMessageManager(newMM),
    onDisconnected: () => {
      accessibility.destroy();
      accessibility = null;
    }
  };
}

exports.setupParentProcess = setupParentProcess;
PK
!<z"�%����?chrome/devtools/modules/devtools/server/actors/accessibility.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Cc, Ci } = require("chrome");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const { DebuggerServer } = require("devtools/server/main");
const Services = require("Services");
const { Actor, ActorClassWithSpec } = require("devtools/shared/protocol");
const defer = require("devtools/shared/defer");
const events = require("devtools/shared/event-emitter");
const {
  accessibleSpec,
  accessibleWalkerSpec,
  accessibilitySpec
} = require("devtools/shared/specs/accessibility");

const { isXUL } = require("devtools/server/actors/highlighters/utils/markup");
const { isWindowIncluded } = require("devtools/shared/layout/utils");
const { CustomHighlighterActor, register } =
  require("devtools/server/actors/highlighters");
const PREF_ACCESSIBILITY_FORCE_DISABLED = "accessibility.force_disabled";

const nsIAccessibleEvent = Ci.nsIAccessibleEvent;
const nsIAccessibleStateChangeEvent = Ci.nsIAccessibleStateChangeEvent;
const nsIPropertyElement = Ci.nsIPropertyElement;
const nsIAccessibleRole = Ci.nsIAccessibleRole;

const {
  EVENT_TEXT_CHANGED,
  EVENT_TEXT_INSERTED,
  EVENT_TEXT_REMOVED,
  EVENT_ACCELERATOR_CHANGE,
  EVENT_ACTION_CHANGE,
  EVENT_DEFACTION_CHANGE,
  EVENT_DESCRIPTION_CHANGE,
  EVENT_DOCUMENT_ATTRIBUTES_CHANGED,
  EVENT_HELP_CHANGE,
  EVENT_HIDE,
  EVENT_NAME_CHANGE,
  EVENT_OBJECT_ATTRIBUTE_CHANGED,
  EVENT_REORDER,
  EVENT_STATE_CHANGE,
  EVENT_TEXT_ATTRIBUTE_CHANGED,
  EVENT_VALUE_CHANGE
} = nsIAccessibleEvent;

// TODO: We do not need this once bug 1422913 is fixed. We also would not need
// to fire a name change event for an accessible that has an updated subtree and
// that has its name calculated from the said subtree.
const NAME_FROM_SUBTREE_RULE_ROLES = new Set([
  nsIAccessibleRole.ROLE_BUTTONDROPDOWN,
  nsIAccessibleRole.ROLE_BUTTONDROPDOWNGRID,
  nsIAccessibleRole.ROLE_BUTTONMENU,
  nsIAccessibleRole.ROLE_CELL,
  nsIAccessibleRole.ROLE_CHECKBUTTON,
  nsIAccessibleRole.ROLE_CHECK_MENU_ITEM,
  nsIAccessibleRole.ROLE_CHECK_RICH_OPTION,
  nsIAccessibleRole.ROLE_COLUMN,
  nsIAccessibleRole.ROLE_COLUMNHEADER,
  nsIAccessibleRole.ROLE_COMBOBOX_OPTION,
  nsIAccessibleRole.ROLE_DEFINITION,
  nsIAccessibleRole.ROLE_GRID_CELL,
  nsIAccessibleRole.ROLE_HEADING,
  nsIAccessibleRole.ROLE_HELPBALLOON,
  nsIAccessibleRole.ROLE_HTML_CONTAINER,
  nsIAccessibleRole.ROLE_KEY,
  nsIAccessibleRole.ROLE_LABEL,
  nsIAccessibleRole.ROLE_LINK,
  nsIAccessibleRole.ROLE_LISTITEM,
  nsIAccessibleRole.ROLE_MATHML_IDENTIFIER,
  nsIAccessibleRole.ROLE_MATHML_NUMBER,
  nsIAccessibleRole.ROLE_MATHML_OPERATOR,
  nsIAccessibleRole.ROLE_MATHML_TEXT,
  nsIAccessibleRole.ROLE_MATHML_STRING_LITERAL,
  nsIAccessibleRole.ROLE_MATHML_GLYPH,
  nsIAccessibleRole.ROLE_MENUITEM,
  nsIAccessibleRole.ROLE_OPTION,
  nsIAccessibleRole.ROLE_OUTLINEITEM,
  nsIAccessibleRole.ROLE_PAGETAB,
  nsIAccessibleRole.ROLE_PARENT_MENUITEM,
  nsIAccessibleRole.ROLE_PUSHBUTTON,
  nsIAccessibleRole.ROLE_RADIOBUTTON,
  nsIAccessibleRole.ROLE_RADIO_MENU_ITEM,
  nsIAccessibleRole.ROLE_RICH_OPTION,
  nsIAccessibleRole.ROLE_ROW,
  nsIAccessibleRole.ROLE_ROWHEADER,
  nsIAccessibleRole.ROLE_SUMMARY,
  nsIAccessibleRole.ROLE_SWITCH,
  nsIAccessibleRole.ROLE_TABLE_COLUMN_HEADER,
  nsIAccessibleRole.ROLE_TABLE_ROW_HEADER,
  nsIAccessibleRole.ROLE_TEAR_OFF_MENU_ITEM,
  nsIAccessibleRole.ROLE_TERM,
  nsIAccessibleRole.ROLE_TOGGLE_BUTTON,
  nsIAccessibleRole.ROLE_TOOLTIP
]);

const IS_OSX = Services.appinfo.OS === "Darwin";

register("AccessibleHighlighter", "accessible");
register("XULWindowAccessibleHighlighter", "xul-accessible");

/**
 * Helper function that determines if nsIAccessible object is in defunct state.
 *
 * @param  {nsIAccessible}  accessible
 *         object to be tested.
 * @return {Boolean}
 *         True if accessible object is defunct, false otherwise.
 */
function isDefunct(accessible) {
  let defunct = false;

  try {
    let extState = {};
    accessible.getState({}, extState);
    // extState.value is a bitmask. We are applying bitwise AND to mask out
    // irrelevant states.
    defunct = !!(extState.value & Ci.nsIAccessibleStates.EXT_STATE_DEFUNCT);
  } catch (e) {
    defunct = true;
  }

  return defunct;
}

/**
 * Set of actors that expose accessibility tree information to the
 * devtools protocol clients.
 *
 * The |Accessibility| actor is the main entry point. It is used to request
 * an AccessibleWalker actor that caches the tree of Accessible actors.
 *
 * The |AccessibleWalker| actor is used to cache all seen Accessible actors as
 * well as observe all relevant accessible events.
 *
 * The |Accessible| actor provides information about a particular accessible
 * object, its properties, , attributes, states, relations, etc.
 */

/**
 * The AccessibleActor provides information about a given accessible object: its
 * role, name, states, etc.
 */
const AccessibleActor = ActorClassWithSpec(accessibleSpec, {
  initialize(walker, rawAccessible) {
    Actor.prototype.initialize.call(this, walker.conn);
    this.walker = walker;
    this.rawAccessible = rawAccessible;

    /**
     * Indicates if the raw accessible is no longer alive.
     *
     * @return Boolean
     */
    Object.defineProperty(this, "isDefunct", {
      get() {
        let defunct = isDefunct(this.rawAccessible);
        if (defunct) {
          delete this.isDefunct;
          this.isDefunct = true;
          return this.isDefunct;
        }

        return defunct;
      },
      configurable: true
    });
  },

  /**
   * Items returned by this actor should belong to the parent walker.
   */
  marshallPool() {
    return this.walker;
  },

  destroy() {
    Actor.prototype.destroy.call(this);
    this.walker = null;
    this.rawAccessible = null;
  },

  get role() {
    if (this.isDefunct) {
      return null;
    }
    return this.walker.a11yService.getStringRole(this.rawAccessible.role);
  },

  get name() {
    if (this.isDefunct) {
      return null;
    }
    return this.rawAccessible.name;
  },

  get value() {
    if (this.isDefunct) {
      return null;
    }
    return this.rawAccessible.value;
  },

  get description() {
    if (this.isDefunct) {
      return null;
    }
    return this.rawAccessible.description;
  },

  get help() {
    if (this.isDefunct) {
      return null;
    }
    return this.rawAccessible.help;
  },

  get keyboardShortcut() {
    if (this.isDefunct) {
      return null;
    }
    return this.rawAccessible.keyboardShortcut;
  },

  get childCount() {
    if (this.isDefunct) {
      return 0;
    }
    return this.rawAccessible.childCount;
  },

  get domNodeType() {
    if (this.isDefunct) {
      return 0;
    }
    return this.rawAccessible.DOMNode ? this.rawAccessible.DOMNode.nodeType : 0;
  },

  get parentAcc() {
    if (this.isDefunct) {
      return null;
    }
    return this.walker.addRef(this.rawAccessible.parent);
  },

  children() {
    let children = [];
    if (this.isDefunct) {
      return children;
    }

    for (let child = this.rawAccessible.firstChild; child; child = child.nextSibling) {
      children.push(this.walker.addRef(child));
    }
    return children;
  },

  get indexInParent() {
    if (this.isDefunct) {
      return -1;
    }

    try {
      return this.rawAccessible.indexInParent;
    } catch (e) {
      // Accessible is dead.
      return -1;
    }
  },

  get actions() {
    let actions = [];
    if (this.isDefunct) {
      return actions;
    }

    for (let i = 0; i < this.rawAccessible.actionCount; i++) {
      actions.push(this.rawAccessible.getActionDescription(i));
    }
    return actions;
  },

  get states() {
    if (this.isDefunct) {
      return [];
    }

    let state = {};
    let extState = {};
    this.rawAccessible.getState(state, extState);
    return [
      ...this.walker.a11yService.getStringStates(state.value, extState.value)
    ];
  },

  get attributes() {
    if (this.isDefunct || !this.rawAccessible.attributes) {
      return {};
    }

    let attributes = {};
    let attrsEnum = this.rawAccessible.attributes.enumerate();
    while (attrsEnum.hasMoreElements()) {
      let { key, value } = attrsEnum.getNext().QueryInterface(
        nsIPropertyElement);
      attributes[key] = value;
    }

    return attributes;
  },

  get bounds() {
    if (this.isDefunct) {
      return null;
    }

    let x = {}, y = {}, w = {}, h = {};
    try {
      this.rawAccessible.getBounds(x, y, w, h);
      x = x.value;
      y = y.value;
      w = w.value;
      h = h.value;
    } catch (e) {
      return null;
    }

    return { x, y, w, h };
  },

  form() {
    return {
      actor: this.actorID,
      role: this.role,
      name: this.name,
      value: this.value,
      description: this.description,
      help: this.help,
      keyboardShortcut: this.keyboardShortcut,
      childCount: this.childCount,
      domNodeType: this.domNodeType,
      indexInParent: this.indexInParent,
      states: this.states,
      actions: this.actions,
      attributes: this.attributes
    };
  }
});

/**
 * The AccessibleWalkerActor stores a cache of AccessibleActors that represent
 * accessible objects in a given document.
 *
 * It is also responsible for implicitely initializing and shutting down
 * accessibility engine by storing a reference to the XPCOM accessibility
 * service.
 */
const AccessibleWalkerActor = ActorClassWithSpec(accessibleWalkerSpec, {
  initialize(conn, tabActor) {
    Actor.prototype.initialize.call(this, conn);
    this.tabActor = tabActor;
    this.refMap = new Map();
    this.setA11yServiceGetter();
    this.onPick = this.onPick.bind(this);
    this.onHovered = this.onHovered.bind(this);
    this.onKey = this.onKey.bind(this);

    this.highlighter = CustomHighlighterActor(this, isXUL(this.rootWin) ?
      "XULWindowAccessibleHighlighter" : "AccessibleHighlighter");
  },

  setA11yServiceGetter() {
    DevToolsUtils.defineLazyGetter(this, "a11yService", () => {
      Services.obs.addObserver(this, "accessible-event");
      return Cc["@mozilla.org/accessibilityService;1"].getService(
        Ci.nsIAccessibilityService);
    });
  },

  get rootWin() {
    return this.tabActor && this.tabActor.window;
  },

  get rootDoc() {
    return this.tabActor && this.tabActor.window.document;
  },

  reset() {
    try {
      Services.obs.removeObserver(this, "accessible-event");
    } catch (e) {
      // Accessible event observer might not have been initialized if a11y
      // service was never used.
    }

    this.cancelPick();

    // Clean up accessible actors cache.
    if (this.refMap.size > 0) {
      try {
        if (this.rootDoc) {
          this.purgeSubtree(this.a11yService.getAccessibleFor(this.rootDoc),
                            this.rootDoc);
        }
      } catch (e) {
        // Accessibility service might be already destroyed.
      }
    }

    delete this.a11yService;
    this.setA11yServiceGetter();
  },

  destroy() {
    Actor.prototype.destroy.call(this);

    this.reset();

    this.highlighter.destroy();
    this.highlighter = null;

    this.tabActor = null;
    this.refMap = null;
  },

  getRef(rawAccessible) {
    return this.refMap.get(rawAccessible);
  },

  addRef(rawAccessible) {
    let actor = this.refMap.get(rawAccessible);
    if (actor) {
      return actor;
    }

    actor = new AccessibleActor(this, rawAccessible);
    this.manage(actor);
    this.refMap.set(rawAccessible, actor);

    return actor;
  },

  /**
   * Clean up accessible actors cache for a given accessible's subtree.
   *
   * @param  {nsIAccessible} rawAccessible
   * @param  {null|Object}   rawNode
   */
  purgeSubtree(rawAccessible, rawNode) {
    let actor = this.getRef(rawAccessible);
    if (actor && rawAccessible && !actor.isDefunct) {
      for (let child = rawAccessible.firstChild; child; child = child.nextSibling) {
        this.purgeSubtree(child);
      }
    }

    this.refMap.delete(rawAccessible);

    if (actor) {
      events.emit(this, "accessible-destroy", actor);
      actor.destroy();
    }

    // If corresponding DOMNode is a top level document, clear entire cache.
    if (rawNode && rawNode === this.rootDoc) {
      this.refMap.clear();
    }
  },

  /**
   * A helper method. Accessibility walker is assumed to have only 1 child which
   * is the top level document.
   */
  children() {
    return Promise.all([this.getDocument()]);
  },

  /**
   * A promise for a root document accessible actor that only resolves when its
   * corresponding document accessible object is fully loaded.
   *
   * @return {Promise}
   */
  getDocument() {
    if (!this.rootDoc || !this.rootDoc.documentElement) {
      return this.once("document-ready").then(docAcc => this.addRef(docAcc));
    }

    if (isXUL(this.rootWin)) {
      let doc = this.addRef(this.a11yService.getAccessibleFor(this.rootDoc));
      return Promise.resolve(doc);
    }

    let doc = this.a11yService.getAccessibleFor(this.rootDoc);
    let state = {};
    doc.getState(state, {});
    if (state.value & Ci.nsIAccessibleStates.STATE_BUSY) {
      return this.once("document-ready").then(docAcc => this.addRef(docAcc));
    }

    return Promise.resolve(this.addRef(doc));
  },

  getAccessibleFor(domNode) {
    // We need to make sure that the document is loaded processed by a11y first.
    return this.getDocument().then(() =>
      this.addRef(this.a11yService.getAccessibleFor(domNode.rawNode)));
  },

  async getAncestry(accessible) {
    if (accessible.indexInParent === -1) {
      return [];
    }
    const doc = await this.getDocument();
    let ancestry = [];
    try {
      let parent = accessible;
      while (parent && (parent = parent.parentAcc) && parent != doc) {
        ancestry.push(parent);
      }
      ancestry.push(doc);
    } catch (error) {
      throw new Error(`Failed to get ancestor for ${accessible}: ${error}`);
    }

    return ancestry.map(parent => (
      { accessible: parent, children: parent.children() }));
  },

  /**
   * Accessible event observer function.
   *
   * @param {nsIAccessibleEvent} subject
   *                                      accessible event object.
   */
  observe(subject) {
    let event = subject.QueryInterface(nsIAccessibleEvent);
    let rawAccessible = event.accessible;
    let accessible = this.getRef(rawAccessible);

    switch (event.eventType) {
      case EVENT_STATE_CHANGE:
        let { state, isEnabled } = event.QueryInterface(nsIAccessibleStateChangeEvent);
        let isBusy = state & Ci.nsIAccessibleStates.STATE_BUSY;
        // Accessible document is recreated.
        if (isBusy && !isEnabled && rawAccessible instanceof Ci.nsIAccessibleDocument) {
          // Remove its existing cache from tree.
          this.purgeSubtree(rawAccessible, event.DOMNode);
          // If it's a top level document notify listeners about the document
          // being ready.
          if (event.DOMNode == this.rootDoc) {
            events.emit(this, "document-ready", rawAccessible);
          }
        }

        if (accessible) {
          // Only propagate state change events for active accessibles.
          if (isBusy && isEnabled) {
            if (rawAccessible instanceof Ci.nsIAccessibleDocument) {
              // Remove its existing cache from tree.
              this.purgeSubtree(rawAccessible, event.DOMNode);
            }
            return;
          }
          events.emit(accessible, "states-change", accessible.states);
        }

        break;
      case EVENT_NAME_CHANGE:
        if (accessible) {
          events.emit(accessible, "name-change", rawAccessible.name,
            event.DOMNode == this.rootDoc ?
              undefined : this.getRef(rawAccessible.parent), this);
        }
        break;
      case EVENT_VALUE_CHANGE:
        if (accessible) {
          events.emit(accessible, "value-change", rawAccessible.value);
        }
        break;
      case EVENT_DESCRIPTION_CHANGE:
        if (accessible) {
          events.emit(accessible, "description-change", rawAccessible.description);
        }
        break;
      case EVENT_HELP_CHANGE:
        if (accessible) {
          events.emit(accessible, "help-change", rawAccessible.help);
        }
        break;
      case EVENT_REORDER:
        if (accessible) {
          accessible.children().forEach(child =>
            events.emit(child, "index-in-parent-change", child.indexInParent));
          events.emit(accessible, "reorder", rawAccessible.childCount, this);
        }
        break;
      case EVENT_HIDE:
        this.purgeSubtree(rawAccessible);
        break;
      case EVENT_DEFACTION_CHANGE:
      case EVENT_ACTION_CHANGE:
        if (accessible) {
          events.emit(accessible, "actions-change", accessible.actions);
        }
        break;
      case EVENT_TEXT_CHANGED:
      case EVENT_TEXT_INSERTED:
      case EVENT_TEXT_REMOVED:
        if (accessible) {
          events.emit(accessible, "text-change", this);
          if (NAME_FROM_SUBTREE_RULE_ROLES.has(rawAccessible.role)) {
            events.emit(accessible, "name-change", rawAccessible.name,
              event.DOMNode == this.rootDoc ?
                undefined : this.getRef(rawAccessible.parent), this);
          }
        }
        break;
      case EVENT_DOCUMENT_ATTRIBUTES_CHANGED:
      case EVENT_OBJECT_ATTRIBUTE_CHANGED:
      case EVENT_TEXT_ATTRIBUTE_CHANGED:
        if (accessible) {
          events.emit(accessible, "attributes-change", accessible.attributes);
        }
        break;
      case EVENT_ACCELERATOR_CHANGE:
        if (accessible) {
          events.emit(accessible, "shortcut-change", rawAccessible.keyboardShortcut);
        }
        break;
      default:
        break;
    }
  },

  /**
   * Public method used to show an accessible object highlighter on the client
   * side.
   *
   * @param  {Object} accessible
   *         AccessibleActor to be highlighted.
   * @param  {Object} options
   *         Object used for passing options. Available options:
   *         - duration {Number}
   *                    Duration of time that the highlighter should be shown.
   * @return {Boolean}
   *         True if highlighter shows the accessible object.
   */
  highlightAccessible(accessible, options = {}) {
    let bounds = accessible.bounds;
    if (!bounds) {
      return false;
    }

    return this.highlighter.show({ rawNode: accessible.rawAccessible.DOMNode },
                                 { ...options, ...bounds });
  },

  /**
   * Public method used to hide an accessible object highlighter on the client
   * side.
   */
  unhighlight() {
    this.highlighter.hide();
  },

  /**
   * Picking state that indicates if picking is currently enabled and, if so,
   * what the current and hovered accessible objects are.
   */
  _isPicking: false,
  _currentAccessible: null,

  /**
   * Check is event handling is allowed.
   */
  _isEventAllowed: function ({ view }) {
    return this.rootWin instanceof Ci.nsIDOMChromeWindow ||
           isWindowIncluded(this.rootWin, view);
  },

  _preventContentEvent(event) {
    event.stopPropagation();
    event.preventDefault();
  },

  /**
   * Click event handler for when picking is enabled.
   *
   * @param  {Object} event
   *         Current click event.
   */
  async onPick(event) {
    if (!this._isPicking) {
      return;
    }

    this._preventContentEvent(event);
    if (!this._isEventAllowed(event)) {
      return;
    }

    // If shift is pressed, this is only a preview click, send the event to
    // the client, but don't stop picking.
    if (event.shiftKey) {
      if (!this._currentAccessible) {
        this._currentAccessible = await this._findAndAttachAccessible(event);
      }
      events.emit(this, "picker-accessible-previewed", this._currentAccessible);
      return;
    }

    this._stopPickerListeners();
    this._isPicking = false;
    if (!this._currentAccessible) {
      this._currentAccessible = await this._findAndAttachAccessible(event);
    }
    events.emit(this, "picker-accessible-picked", this._currentAccessible);
  },

  /**
   * Hover event handler for when picking is enabled.
   *
   * @param  {Object} event
   *         Current hover event.
   */
  async onHovered(event) {
    if (!this._isPicking) {
      return;
    }

    this._preventContentEvent(event);
    if (!this._isEventAllowed(event)) {
      return;
    }

    let accessible = await this._findAndAttachAccessible(event);
    if (!accessible) {
      return;
    }

    if (this._currentAccessible !== accessible) {
      let { bounds } = accessible;
      if (bounds) {
        this.highlighter.show({ rawNode: event.originalTarget || event.target }, bounds);
      }

      events.emit(this, "picker-accessible-hovered", accessible);
      this._currentAccessible = accessible;
    }
  },

  /**
   * Keyboard event handler for when picking is enabled.
   *
   * @param  {Object} event
   *         Current keyboard event.
   */
  onKey(event) {
    if (!this._currentAccessible || !this._isPicking) {
      return;
    }

    this._preventContentEvent(event);
    if (!this._isEventAllowed(event)) {
      return;
    }

    /**
     * KEY: Action/scope
     * ENTER/CARRIAGE_RETURN: Picks current accessible
     * ESC/CTRL+SHIFT+C: Cancels picker
     */
    switch (event.keyCode) {
      // Select the element.
      case event.DOM_VK_RETURN:
        this._onPick(event);
        break;
      // Cancel pick mode.
      case event.DOM_VK_ESCAPE:
        this.cancelPick();
        events.emit(this, "picker-accessible-canceled");
        break;
      case event.DOM_VK_C:
        if ((IS_OSX && event.metaKey && event.altKey) ||
          (!IS_OSX && event.ctrlKey && event.shiftKey)) {
          this.cancelPick();
          events.emit(this, "picker-accessible-canceled");
        }
        break;
      default:
        break;
    }
  },

  /**
   * Picker method that starts picker content listeners.
   */
  pick: function () {
    if (!this._isPicking) {
      this._isPicking = true;
      this._startPickerListeners();
    }
  },

  /**
   * This pick method also focuses the highlighter's target window.
   */
  pickAndFocus: function () {
    this.pick();
    this.rootWin.focus();
  },

  /**
   * Find accessible object that corresponds to a DOMNode and attach (lookup its
   * ancestry to the root doc) to the AccessibilityWalker tree.
   *
   * @param  {Object} event
   *         Correspoinding content event.
   * @return {null|Object}
   *         Accessible object, if available, that corresponds to a DOM node.
   */
  async _findAndAttachAccessible(event) {
    let target = event.originalTarget || event.target;
    let rawAccessible = this.a11yService.getAccessibleFor(target);
    // If raw accessible object is defunct or detached, no need to cache it and
    // its ancestry.
    if (!rawAccessible || isDefunct(rawAccessible) || rawAccessible.indexInParent < 0) {
      return {};
    }

    const doc = await this.getDocument();
    let accessible = this.addRef(rawAccessible);
    // There is a chance that ancestry lookup can fail if the accessible is in
    // the detached subtree. At that point the root accessible object would be
    // defunct and accessing it via parent property will throw.
    try {
      let parent = accessible;
      while (parent && parent != doc) {
        parent = parent.parentAcc;
      }
    } catch (error) {
      throw new Error(`Failed to get ancestor for ${accessible}: ${error}`);
    }

    return accessible;
  },

  /**
   * Start picker content listeners.
   */
  _startPickerListeners: function () {
    let target = this.tabActor.chromeEventHandler;
    target.addEventListener("mousemove", this.onHovered, true);
    target.addEventListener("click", this.onPick, true);
    target.addEventListener("mousedown", this._preventContentEvent, true);
    target.addEventListener("mouseup", this._preventContentEvent, true);
    target.addEventListener("dblclick", this._preventContentEvent, true);
    target.addEventListener("keydown", this.onKey, true);
    target.addEventListener("keyup", this._preventContentEvent, true);
  },

  /**
   * If content is still alive, stop picker content listeners.
   */
  _stopPickerListeners: function () {
    let target = this.tabActor.chromeEventHandler;

    if (!target) {
      return;
    }

    target.removeEventListener("mousemove", this.onHovered, true);
    target.removeEventListener("click", this.onPick, true);
    target.removeEventListener("mousedown", this._preventContentEvent, true);
    target.removeEventListener("mouseup", this._preventContentEvent, true);
    target.removeEventListener("dblclick", this._preventContentEvent, true);
    target.removeEventListener("keydown", this.onKey, true);
    target.removeEventListener("keyup", this._preventContentEvent, true);
  },

  /**
   * Cacncel picker pick. Remvoe all content listeners and hide the highlighter.
   */
  cancelPick: function () {
    this.highlighter.hide();

    if (this._isPicking) {
      this._stopPickerListeners();
      this._isPicking = false;
      this._currentAccessible = null;
    }
  }
});

/**
 * The AccessibilityActor is a top level container actor that initializes
 * accessible walker and is the top-most point of interaction for accessibility
 * tools UI.
 */
const AccessibilityActor = ActorClassWithSpec(accessibilitySpec, {
  initialize(conn, tabActor) {
    Actor.prototype.initialize.call(this, conn);

    this.initializedDeferred = defer();

    if (DebuggerServer.isInChildProcess) {
      this._msgName = `debug:${this.conn.prefix}accessibility`;
      this.conn.setupInParent({
        module: "devtools/server/actors/accessibility-parent",
        setupParent: "setupParentProcess"
      });

      this.onMessage = this.onMessage.bind(this);
      this.messageManager.addMessageListener(`${this._msgName}:event`, this.onMessage);
    } else {
      this.userPref = Services.prefs.getIntPref(PREF_ACCESSIBILITY_FORCE_DISABLED);
      Services.obs.addObserver(this, "a11y-consumers-changed");
      Services.prefs.addObserver(PREF_ACCESSIBILITY_FORCE_DISABLED, this);
      this.initializedDeferred.resolve();
    }

    Services.obs.addObserver(this, "a11y-init-or-shutdown");
    this.tabActor = tabActor;
  },

  bootstrap() {
    return this.initializedDeferred.promise.then(() => ({
      enabled: this.enabled,
      canBeEnabled: this.canBeEnabled,
      canBeDisabled: this.canBeDisabled
    }));
  },

  get enabled() {
    return Services.appinfo.accessibilityEnabled;
  },

  get canBeEnabled() {
    if (DebuggerServer.isInChildProcess) {
      return this._canBeEnabled;
    }

    return Services.prefs.getIntPref(PREF_ACCESSIBILITY_FORCE_DISABLED) < 1;
  },

  get canBeDisabled() {
    if (DebuggerServer.isInChildProcess) {
      return this._canBeDisabled;
    } else if (!this.enabled) {
      return true;
    }

    let { PlatformAPI } = JSON.parse(this.walker.a11yService.getConsumers());
    return !PlatformAPI;
  },

  /**
   * Getter for a message manager that corresponds to a current tab. It is onyl
   * used if the AccessibilityActor runs in the child process.
   *
   * @return {Object}
   *         Message manager that corresponds to the current content tab.
   */
  get messageManager() {
    if (!DebuggerServer.isInChildProcess) {
      throw new Error(
        "Message manager should only be used when actor is in child process.");
    }

    return this.conn.parentMessageManager;
  },

  onMessage(msg) {
    let { topic, data } = msg.data;

    switch (topic) {
      case "initialized":
        this._canBeEnabled = data.canBeEnabled;
        this._canBeDisabled = data.canBeDisabled;
        this.initializedDeferred.resolve();
        break;
      case "can-be-disabled-change":
        this._canBeDisabled = data;
        events.emit(this, "can-be-disabled-change", this.canBeDisabled);
        break;

      case "can-be-enabled-change":
        this._canBeEnabled = data;
        events.emit(this, "can-be-enabled-change", this.canBeEnabled);
        break;

      default:
        break;
    }
  },

  /**
   * Enable acessibility service in the given process.
   */
  async enable() {
    if (this.enabled || !this.canBeEnabled) {
      return;
    }

    let initPromise = this.once("init");

    if (DebuggerServer.isInChildProcess) {
      this.messageManager.sendAsyncMessage(this._msgName, { action: "enable" });
    } else {
      // This executes accessibility service lazy getter and adds accessible
      // events observer.
      this.walker.a11yService;
    }

    await initPromise;
  },

  /**
   * Disable acessibility service in the given process.
   */
  async disable() {
    if (!this.enabled || !this.canBeDisabled) {
      return;
    }

    this.disabling = true;
    let shutdownPromise = this.once("shutdown");
    if (DebuggerServer.isInChildProcess) {
      this.messageManager.sendAsyncMessage(this._msgName, { action: "disable" });
    } else {
      // Set PREF_ACCESSIBILITY_FORCE_DISABLED to 1 to force disable
      // accessibility service. This is the only way to guarantee an immediate
      // accessibility service shutdown in all processes. This also prevents
      // accessibility service from starting up in the future.
      //
      // TODO: Introduce a shutdown method that is exposed via XPCOM on
      // accessibility service.
      Services.prefs.setIntPref(PREF_ACCESSIBILITY_FORCE_DISABLED, 1);
      // Set PREF_ACCESSIBILITY_FORCE_DISABLED back to previous default or user
      // set value. This will not start accessibility service until the user
      // activates it again. It simply ensures that accessibility service can
      // start again (when value is below 1).
      Services.prefs.setIntPref(PREF_ACCESSIBILITY_FORCE_DISABLED, this.userPref);
    }

    await shutdownPromise;
    delete this.disabling;
  },

  /**
   * Observe Accessibility service init and shutdown events. It relays these
   * events to AccessibilityFront iff the event is fired for the a11y service
   * that lives in the same process.
   *
   * @param  {null} subject
   *         Not used.
   * @param  {String} topic
   *         Name of the a11y service event: "a11y-init-or-shutdown".
   * @param  {String} data
   *         "0" corresponds to shutdown and "1" to init.
   */
  observe(subject, topic, data) {
    if (topic === "a11y-init-or-shutdown") {
      // This event is fired when accessibility service is initialized or shut
      // down. "init" and "shutdown" events are only relayed when the enabled
      // state matches the event (e.g. the event came from the same process as
      // the actor).
      const enabled = data === "1";
      if (enabled && this.enabled) {
        events.emit(this, "init");
      } else if (!enabled && !this.enabled) {
        if (this.walker) {
          this.walker.reset();
        }

        events.emit(this, "shutdown");
      }
    } else if (topic === "a11y-consumers-changed") {
      // This event is fired when accessibility service consumers change. There
      // are 3 possible consumers of a11y service: XPCOM, PlatformAPI (e.g.
      // screen readers) and MainProcess. PlatformAPI consumer can only be set
      // in parent process, and MainProcess consumer can only be set in child
      // process. We only care about PlatformAPI consumer changes because when
      // set, we can no longer disable accessibility service.
      let { PlatformAPI } = JSON.parse(data);
      events.emit(this, "can-be-disabled-change", !PlatformAPI);
    } else if (!this.disabling && topic === "nsPref:changed" &&
               data === PREF_ACCESSIBILITY_FORCE_DISABLED) {
      // PREF_ACCESSIBILITY_FORCE_DISABLED preference change event. When set to
      // >=1, it means that the user wants to disable accessibility service and
      // prevent it from starting in the future. Note: we also check
      // this.disabling state when handling this pref change because this is how
      // we disable the accessibility inspector itself.
      events.emit(this, "can-be-enabled-change", this.canBeEnabled);
    }
  },

  /**
   * Get or create AccessibilityWalker actor, similar to WalkerActor.
   *
   * @return {Object}
   *         AccessibleWalkerActor for the current tab.
   */
  getWalker() {
    if (!this.walker) {
      this.walker = new AccessibleWalkerActor(this.conn, this.tabActor);
    }
    return this.walker;
  },

  /**
   * Destroy accessibility service actor. This method also shutsdown
   * accessibility service if possible.
   */
  async destroy() {
    if (this.destroyed) {
      await this.destroyed;
      return;
    }

    let resolver;
    this.destroyed = new Promise(resolve => {
      resolver = resolve;
    });

    if (this.walker) {
      this.walker.reset();
    }

    Services.obs.removeObserver(this, "a11y-init-or-shutdown");
    if (DebuggerServer.isInChildProcess) {
      this.messageManager.removeMessageListener(`${this._msgName}:event`,
                                                this.onMessage);
    } else {
      Services.obs.removeObserver(this, "a11y-consumers-changed");
      Services.prefs.removeObserver(PREF_ACCESSIBILITY_FORCE_DISABLED, this);
    }

    Actor.prototype.destroy.call(this);
    this.walker = null;
    this.tabActor = null;
    resolver();
  }
});

exports.AccessibleActor = AccessibleActor;
exports.AccessibleWalkerActor = AccessibleWalkerActor;
exports.AccessibilityActor = AccessibilityActor;
PK
!<�e����@chrome/devtools/modules/devtools/server/actors/actor-registry.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 protocol = require("devtools/shared/protocol");

const { registerActor, unregisterActor } = require("devtools/server/actors/utils/actor-registry-utils");
const { actorActorSpec, actorRegistrySpec } = require("devtools/shared/specs/actor-registry");

/**
 * The ActorActor gives you a handle to an actor you've dynamically
 * registered and allows you to unregister it.
 */
const ActorActor = protocol.ActorClassWithSpec(actorActorSpec, {
  initialize: function (conn, options) {
    protocol.Actor.prototype.initialize.call(this, conn);

    this.options = options;
  },

  unregister: function () {
    unregisterActor(this.options);
  }
});

/*
 * The ActorRegistryActor allows clients to define new actors on the
 * server. This is particularly useful for addons.
 */
const ActorRegistryActor = protocol.ActorClassWithSpec(actorRegistrySpec, {
  initialize: function (conn) {
    protocol.Actor.prototype.initialize.call(this, conn);
  },

  registerActor: function (sourceText, fileName, options) {
    return registerActor(sourceText, fileName, options).then(() => {
      let { constructor, type } = options;

      return ActorActor(this.conn, {
        name: constructor,
        tab: type.tab,
        global: type.global
      });
    });
  }
});

exports.ActorRegistryActor = ActorRegistryActor;
PK
!<�KU�&�&7chrome/devtools/modules/devtools/server/actors/addon.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var { Ci, Cu } = require("chrome");
var Services = require("Services");
var { ActorPool } = require("devtools/server/actors/common");
var { TabSources } = require("./utils/TabSources");
var makeDebugger = require("./utils/make-debugger");
var { ConsoleAPIListener } = require("devtools/server/actors/webconsole/listeners");
var DevToolsUtils = require("devtools/shared/DevToolsUtils");
var { assert, update } = DevToolsUtils;

loader.lazyRequireGetter(this, "AddonThreadActor", "devtools/server/actors/thread", true);
loader.lazyRequireGetter(this, "unwrapDebuggerObjectGlobal", "devtools/server/actors/thread", true);
loader.lazyRequireGetter(this, "mapURIToAddonID", "devtools/server/actors/utils/map-uri-to-addon-id");
loader.lazyRequireGetter(this, "WebConsoleActor", "devtools/server/actors/webconsole", true);

loader.lazyImporter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm");

function BrowserAddonActor(connection, addon) {
  this.conn = connection;
  this._addon = addon;
  this._contextPool = new ActorPool(this.conn);
  this.conn.addActorPool(this._contextPool);
  this.threadActor = null;
  this._global = null;

  this._shouldAddNewGlobalAsDebuggee = this._shouldAddNewGlobalAsDebuggee.bind(this);

  this.makeDebugger = makeDebugger.bind(null, {
    findDebuggees: this._findDebuggees.bind(this),
    shouldAddNewGlobalAsDebuggee: this._shouldAddNewGlobalAsDebuggee
  });

  AddonManager.addAddonListener(this);
}
exports.BrowserAddonActor = BrowserAddonActor;

BrowserAddonActor.prototype = {
  actorPrefix: "addon",

  get exited() {
    return !this._addon;
  },

  get id() {
    return this._addon.id;
  },

  get url() {
    return this._addon.sourceURI ? this._addon.sourceURI.spec : undefined;
  },

  get attached() {
    return this.threadActor;
  },

  get global() {
    return this._global;
  },

  get sources() {
    if (!this._sources) {
      assert(this.threadActor, "threadActor should exist when creating sources.");
      this._sources = new TabSources(this.threadActor, this._allowSource);
    }
    return this._sources;
  },

  form: function BAAForm() {
    assert(this.actorID, "addon should have an actorID.");
    if (!this._consoleActor) {
      this._consoleActor = new AddonConsoleActor(this._addon, this.conn, this);
      this._contextPool.addActor(this._consoleActor);
    }

    return {
      actor: this.actorID,
      id: this.id,
      name: this._addon.name,
      url: this.url,
      iconURL: this._addon.iconURL,
      debuggable: this._addon.isDebuggable,
      temporarilyInstalled: this._addon.temporarilyInstalled,
      type: this._addon.type,
      isWebExtension: this._addon.isWebExtension,
      isAPIExtension: this._addon.isAPIExtension,
      consoleActor: this._consoleActor.actorID,

      traits: {
        highlightable: false,
        networkMonitor: false,
      },
    };
  },

  destroy() {
    this.conn.removeActorPool(this._contextPool);
    this._contextPool = null;
    this._consoleActor = null;
    this._addon = null;
    this._global = null;
    AddonManager.removeAddonListener(this);
  },

  setOptions: function BAASetOptions(options) {
    if ("global" in options) {
      this._global = options.global;
    }
  },

  onInstalled: function BAAUpdateAddonWrapper(addon) {
    if (addon.id != this._addon.id) {
      return;
    }

    // Update the AddonManager's addon object on reload/update.
    this._addon = addon;
  },

  onDisabled: function BAAOnDisabled(addon) {
    if (addon != this._addon) {
      return;
    }

    this._global = null;
  },

  onUninstalled: function BAAOnUninstalled(addon) {
    if (addon != this._addon) {
      return;
    }

    if (this.attached) {
      this.onDetach();

      // The BrowserAddonActor is not a TabActor and it has to send
      // "tabDetached" directly to close the devtools toolbox window.
      this.conn.send({ from: this.actorID, type: "tabDetached" });
    }

    this.destroy();
  },

  onAttach: function BAAOnAttach() {
    if (this.exited) {
      return { type: "exited" };
    }

    if (!this.attached) {
      this.threadActor = new AddonThreadActor(this.conn, this);
      this._contextPool.addActor(this.threadActor);
    }

    return { type: "tabAttached", threadActor: this.threadActor.actorID };
  },

  onDetach: function BAAOnDetach() {
    if (!this.attached) {
      return { error: "wrongState" };
    }

    this._contextPool.removeActor(this.threadActor);

    this.threadActor = null;
    this._sources = null;

    return { type: "detached" };
  },

  onReload: function BAAOnReload() {
    return this._addon.reload()
      .then(() => {
        // send an empty response
        return {};
      });
  },

  preNest: function () {
    let e = Services.wm.getEnumerator(null);
    while (e.hasMoreElements()) {
      let win = e.getNext();
      let windowUtils = win.QueryInterface(Ci.nsIInterfaceRequestor)
                           .getInterface(Ci.nsIDOMWindowUtils);
      windowUtils.suppressEventHandling(true);
      windowUtils.suspendTimeouts();
    }
  },

  postNest: function () {
    let e = Services.wm.getEnumerator(null);
    while (e.hasMoreElements()) {
      let win = e.getNext();
      let windowUtils = win.QueryInterface(Ci.nsIInterfaceRequestor)
                           .getInterface(Ci.nsIDOMWindowUtils);
      windowUtils.resumeTimeouts();
      windowUtils.suppressEventHandling(false);
    }
  },

  /**
   * Return true if the given global is associated with this addon and should be
   * added as a debuggee, false otherwise.
   */
  _shouldAddNewGlobalAsDebuggee: function (givenGlobal) {
    const global = unwrapDebuggerObjectGlobal(givenGlobal);
    try {
      // This will fail for non-Sandbox objects, hence the try-catch block.
      let metadata = Cu.getSandboxMetadata(global);
      if (metadata) {
        return metadata.addonID === this.id;
      }
    } catch (e) {
      // ignore
    }

    if (global instanceof Ci.nsIDOMWindow) {
      return mapURIToAddonID(global.document.documentURIObject) == this.id;
    }

    // Check the global for a __URI__ property and then try to map that to an
    // add-on
    let uridescriptor = givenGlobal.getOwnPropertyDescriptor("__URI__");
    if (uridescriptor && "value" in uridescriptor && uridescriptor.value) {
      let uri;
      try {
        uri = Services.io.newURI(uridescriptor.value);
      } catch (e) {
        DevToolsUtils.reportException(
          "BrowserAddonActor.prototype._shouldAddNewGlobalAsDebuggee",
          new Error("Invalid URI: " + uridescriptor.value)
        );
        return false;
      }

      if (mapURIToAddonID(uri) == this.id) {
        return true;
      }
    }

    return false;
  },

  /**
   * Override the eligibility check for scripts and sources to make
   * sure every script and source with a URL is stored when debugging
   * add-ons.
   */
  _allowSource: function (source) {
    // XPIProvider.jsm evals some code in every add-on's bootstrap.js. Hide it.
    if (source.url === "resource://gre/modules/addons/XPIProvider.jsm") {
      return false;
    }

    return true;
  },

  /**
   * Yield the current set of globals associated with this addon that should be
   * added as debuggees.
   */
  _findDebuggees: function (dbg) {
    return dbg.findAllGlobals().filter(this._shouldAddNewGlobalAsDebuggee);
  }
};

BrowserAddonActor.prototype.requestTypes = {
  "attach": BrowserAddonActor.prototype.onAttach,
  "detach": BrowserAddonActor.prototype.onDetach,
  "reload": BrowserAddonActor.prototype.onReload
};

/**
 * The AddonConsoleActor implements capabilities needed for the add-on web
 * console feature.
 *
 * @constructor
 * @param object addon
 *        The add-on that this console watches.
 * @param object connection
 *        The connection to the client, DebuggerServerConnection.
 * @param object parentActor
 *        The parent BrowserAddonActor actor.
 */
function AddonConsoleActor(addon, connection, parentActor) {
  this.addon = addon;
  WebConsoleActor.call(this, connection, parentActor);
}

AddonConsoleActor.prototype = Object.create(WebConsoleActor.prototype);

update(AddonConsoleActor.prototype, {
  constructor: AddonConsoleActor,

  actorPrefix: "addonConsole",

  /**
   * The add-on that this console watches.
   */
  addon: null,

  /**
   * The main add-on JS global
   */
  get window() {
    return this.parentActor.global;
  },

  /**
   * Destroy the current AddonConsoleActor instance.
   */
  destroy() {
    WebConsoleActor.prototype.destroy.call(this);
    this.addon = null;
  },

  /**
   * Handler for the "startListeners" request.
   *
   * @param object request
   *        The JSON request object received from the Web Console client.
   * @return object
   *         The response object which holds the startedListeners array.
   */
  onStartListeners: function ACAOnStartListeners(request) {
    let startedListeners = [];

    while (request.listeners.length > 0) {
      let listener = request.listeners.shift();
      switch (listener) {
        case "ConsoleAPI":
          if (!this.consoleAPIListener) {
            this.consoleAPIListener =
              new ConsoleAPIListener(null, this, { addonId: this.addon.id });
            this.consoleAPIListener.init();
          }
          startedListeners.push(listener);
          break;
      }
    }
    return {
      startedListeners: startedListeners,
      nativeConsoleAPI: true,
      traits: this.traits,
    };
  },
});

AddonConsoleActor.prototype.requestTypes = Object.create(
  WebConsoleActor.prototype.requestTypes
);
AddonConsoleActor.prototype.requestTypes.startListeners =
  AddonConsoleActor.prototype.onStartListeners;
PK
!<�wf�nn8chrome/devtools/modules/devtools/server/actors/addons.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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} = require("resource://gre/modules/AddonManager.jsm");
const protocol = require("devtools/shared/protocol");
const {FileUtils} = require("resource://gre/modules/FileUtils.jsm");
const {addonsSpec} = require("devtools/shared/specs/addons");

const AddonsActor = protocol.ActorClassWithSpec(addonsSpec, {

  initialize: function (conn) {
    protocol.Actor.prototype.initialize.call(this, conn);
  },

  async installTemporaryAddon(addonPath) {
    let addonFile;
    let addon;
    try {
      addonFile = new FileUtils.File(addonPath);
      addon = await AddonManager.installTemporaryAddon(addonFile);
    } catch (error) {
      throw new Error(`Could not install add-on at '${addonPath}': ${error}`);
    }

    // TODO: once the add-on actor has been refactored to use
    // protocol.js, we could return it directly.
    // return new BrowserAddonActor(this.conn, addon);

    // Return a pseudo add-on object that a calling client can work
    // with. Provide a flag that the client can use to detect when it
    // gets upgraded to a real actor object.
    return { id: addon.id, actor: false };
  },
});

exports.AddonsActor = AddonsActor;
PK
!<�g�u�u;chrome/devtools/modules/devtools/server/actors/animation.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

/**
 * Set of actors that expose the Web Animations API to devtools protocol
 * clients.
 *
 * The |Animations| actor is the main entry point. It is used to discover
 * animation players on given nodes.
 * There should only be one instance per debugger server.
 *
 * The |AnimationPlayer| actor provides attributes and methods to inspect an
 * animation as well as pause/resume/seek it.
 *
 * The Web Animation spec implementation is ongoing in Gecko, and so this set
 * of actors should evolve when the implementation progresses.
 *
 * References:
 * - WebAnimation spec:
 *   http://drafts.csswg.org/web-animations/
 * - WebAnimation WebIDL files:
 *   /dom/webidl/Animation*.webidl
 */

const {Cu, Ci} = require("chrome");
const protocol = require("devtools/shared/protocol");
const {Actor} = protocol;
const {animationPlayerSpec, animationsSpec} = require("devtools/shared/specs/animation");

// Types of animations.
const ANIMATION_TYPES = {
  CSS_ANIMATION: "cssanimation",
  CSS_TRANSITION: "csstransition",
  SCRIPT_ANIMATION: "scriptanimation",
  UNKNOWN: "unknown"
};
exports.ANIMATION_TYPES = ANIMATION_TYPES;

/**
 * The AnimationPlayerActor provides information about a given animation: its
 * startTime, currentTime, current state, etc.
 *
 * Since the state of a player changes as the animation progresses it is often
 * useful to call getCurrentState at regular intervals to get the current state.
 *
 * This actor also allows playing, pausing and seeking the animation.
 */
var AnimationPlayerActor = protocol.ActorClassWithSpec(animationPlayerSpec, {
  /**
   * @param {AnimationsActor} The main AnimationsActor instance
   * @param {AnimationPlayer} The player object returned by getAnimationPlayers
   */
  initialize: function (animationsActor, player) {
    Actor.prototype.initialize.call(this, animationsActor.conn);

    this.onAnimationMutation = this.onAnimationMutation.bind(this);

    this.walker = animationsActor.walker;
    this.player = player;

    // Listen to animation mutations on the node to alert the front when the
    // current animation changes.
    // If the node is a pseudo-element, then we listen on its parent with
    // subtree:true (there's no risk of getting too many notifications in
    // onAnimationMutation since we filter out events that aren't for the
    // current animation).
    this.observer = new this.window.MutationObserver(this.onAnimationMutation);
    if (this.isPseudoElement) {
      this.observer.observe(this.node.parentElement,
                            {animations: true, subtree: true});
    } else {
      this.observer.observe(this.node, {animations: true});
    }
  },

  destroy: function () {
    // Only try to disconnect the observer if it's not already dead (i.e. if the
    // container view hasn't navigated since).
    if (this.observer && !Cu.isDeadWrapper(this.observer)) {
      this.observer.disconnect();
    }
    this.player = this.observer = this.walker = null;

    Actor.prototype.destroy.call(this);
  },

  get isPseudoElement() {
    return !this.player.effect.target.ownerDocument;
  },

  get node() {
    if (this._node) {
      return this._node;
    }

    let node = this.player.effect.target;

    if (this.isPseudoElement) {
      // The target is a CSSPseudoElement object which just has a property that
      // points to its parent element and a string type (::before or ::after).
      let treeWalker = this.walker.getDocumentWalker(node.parentElement);
      while (treeWalker.nextNode()) {
        let currentNode = treeWalker.currentNode;
        if ((currentNode.nodeName === "_moz_generated_content_before" &&
             node.type === "::before") ||
            (currentNode.nodeName === "_moz_generated_content_after" &&
             node.type === "::after")) {
          this._node = currentNode;
        }
      }
    } else {
      // The target is a DOM node.
      this._node = node;
    }

    return this._node;
  },

  get window() {
    // ownerGlobal doesn't exist in content privileged windows.
    // eslint-disable-next-line mozilla/use-ownerGlobal
    return this.node.ownerDocument.defaultView;
  },

  /**
   * Release the actor, when it isn't needed anymore.
   * Protocol.js uses this release method to call the destroy method.
   */
  release: function () {},

  form: function (detail) {
    if (detail === "actorid") {
      return this.actorID;
    }

    let data = this.getCurrentState();
    data.actor = this.actorID;

    // If we know the WalkerActor, and if the animated node is known by it, then
    // return its corresponding NodeActor ID too.
    if (this.walker && this.walker.hasNode(this.node)) {
      data.animationTargetNodeActorID = this.walker.getNode(this.node).actorID;
    }

    return data;
  },

  isCssAnimation: function (player = this.player) {
    return player instanceof this.window.CSSAnimation;
  },

  isCssTransition: function (player = this.player) {
    return player instanceof this.window.CSSTransition;
  },

  isScriptAnimation: function (player = this.player) {
    return player instanceof this.window.Animation && !(
      player instanceof this.window.CSSAnimation ||
      player instanceof this.window.CSSTransition
    );
  },

  getType: function () {
    if (this.isCssAnimation()) {
      return ANIMATION_TYPES.CSS_ANIMATION;
    } else if (this.isCssTransition()) {
      return ANIMATION_TYPES.CSS_TRANSITION;
    } else if (this.isScriptAnimation()) {
      return ANIMATION_TYPES.SCRIPT_ANIMATION;
    }

    return ANIMATION_TYPES.UNKNOWN;
  },

  /**
   * Get the name of this animation. This can be either the animation.id
   * property if it was set, or the keyframe rule name or the transition
   * property.
   * @return {String}
   */
  getName: function () {
    if (this.player.id) {
      return this.player.id;
    } else if (this.isCssAnimation()) {
      return this.player.animationName;
    } else if (this.isCssTransition()) {
      return this.player.transitionProperty;
    }

    return "";
  },

  /**
   * Get the animation duration from this player, in milliseconds.
   * @return {Number}
   */
  getDuration: function () {
    return this.player.effect.getComputedTiming().duration;
  },

  /**
   * Get the animation delay from this player, in milliseconds.
   * @return {Number}
   */
  getDelay: function () {
    return this.player.effect.getComputedTiming().delay;
  },

  /**
   * Get the animation endDelay from this player, in milliseconds.
   * @return {Number}
   */
  getEndDelay: function () {
    return this.player.effect.getComputedTiming().endDelay;
  },

  /**
   * Get the animation iteration count for this player. That is, how many times
   * is the animation scheduled to run.
   * @return {Number} The number of iterations, or null if the animation repeats
   * infinitely.
   */
  getIterationCount: function () {
    let iterations = this.player.effect.getComputedTiming().iterations;
    return iterations === "Infinity" ? null : iterations;
  },

  /**
   * Get the animation iterationStart from this player, in ratio.
   * That is offset of starting position of the animation.
   * @return {Number}
   */
  getIterationStart: function () {
    return this.player.effect.getComputedTiming().iterationStart;
  },

  /**
   * Get the animation easing from this player.
   * @return {String}
   */
  getEasing: function () {
    return this.player.effect.timing.easing;
  },

  /**
   * Get the animation fill mode from this player.
   * @return {String}
   */
  getFill: function () {
    return this.player.effect.getComputedTiming().fill;
  },

  /**
   * Get the animation direction from this player.
   * @return {String}
   */
  getDirection: function () {
    return this.player.effect.getComputedTiming().direction;
  },

  /**
   * Get animation-timing-function from animated element if CSS Animations.
   * @return {String}
   */
  getAnimationTimingFunction: function () {
    if (!this.isCssAnimation()) {
      return null;
    }

    let pseudo = null;
    let target = this.player.effect.target;
    if (target.type) {
      // Animated element is a pseudo element.
      pseudo = target.type;
      target = target.parentElement;
    }
    return this.window.getComputedStyle(target, pseudo).animationTimingFunction;
  },

  getPropertiesCompositorStatus: function () {
    let properties = this.player.effect.getProperties();
    return properties.map(prop => {
      return {
        property: prop.property,
        runningOnCompositor: prop.runningOnCompositor,
        warning: prop.warning
      };
    });
  },

  /**
   * Return the current start of the Animation.
   * @return {Object}
   */
  getState: function () {
    // Remember the startTime each time getState is called, it may be useful
    // when animations get paused. As in, when an animation gets paused, its
    // startTime goes back to null, but the front-end might still be interested
    // in knowing what the previous startTime was. So everytime it is set,
    // remember it and send it along with the newState.
    if (this.player.startTime) {
      this.previousStartTime = this.player.startTime;
    }

    // Note that if you add a new property to the state object, make sure you
    // add the corresponding property in the AnimationPlayerFront' initialState
    // getter.
    return {
      type: this.getType(),
      // startTime is null whenever the animation is paused or waiting to start.
      startTime: this.player.startTime,
      previousStartTime: this.previousStartTime,
      currentTime: this.player.currentTime,
      playState: this.player.playState,
      playbackRate: this.player.playbackRate,
      name: this.getName(),
      duration: this.getDuration(),
      delay: this.getDelay(),
      endDelay: this.getEndDelay(),
      iterationCount: this.getIterationCount(),
      iterationStart: this.getIterationStart(),
      fill: this.getFill(),
      easing: this.getEasing(),
      direction: this.getDirection(),
      animationTimingFunction: this.getAnimationTimingFunction(),
      // animation is hitting the fast path or not. Returns false whenever the
      // animation is paused as it is taken off the compositor then.
      isRunningOnCompositor:
        this.getPropertiesCompositorStatus()
            .some(propState => propState.runningOnCompositor),
      propertyState: this.getPropertiesCompositorStatus(),
      // The document timeline's currentTime is being sent along too. This is
      // not strictly related to the node's animationPlayer, but is useful to
      // know the current time of the animation with respect to the document's.
      documentCurrentTime: this.node.ownerDocument.timeline.currentTime
    };
  },

  /**
   * Get the current state of the AnimationPlayer (currentTime, playState, ...).
   * Note that the initial state is returned as the form of this actor when it
   * is initialized.
   * This protocol method only returns a trimed down version of this state in
   * case some properties haven't changed since last time (since the front can
   * reconstruct those). If you want the full state, use the getState method.
   * @return {Object}
   */
  getCurrentState: function () {
    let newState = this.getState();

    // If we've saved a state before, compare and only send what has changed.
    // It's expected of the front to also save old states to re-construct the
    // full state when an incomplete one is received.
    // This is to minimize protocol traffic.
    let sentState = {};
    if (this.currentState) {
      for (let key in newState) {
        if (typeof this.currentState[key] === "undefined" ||
            this.currentState[key] !== newState[key]) {
          sentState[key] = newState[key];
        }
      }
    } else {
      sentState = newState;
    }
    this.currentState = newState;

    return sentState;
  },

  /**
   * Executed when the current animation changes, used to emit the new state
   * the the front.
   */
  onAnimationMutation: function (mutations) {
    let isCurrentAnimation = animation => animation === this.player;
    let hasCurrentAnimation = animations => animations.some(isCurrentAnimation);
    let hasChanged = false;

    for (let {removedAnimations, changedAnimations} of mutations) {
      if (hasCurrentAnimation(removedAnimations)) {
        // Reset the local copy of the state on removal, since the animation can
        // be kept on the client and re-added, its state needs to be sent in
        // full.
        this.currentState = null;
      }

      if (hasCurrentAnimation(changedAnimations)) {
        // Only consider the state has having changed if any of delay, duration,
        // iterationCount, iterationStart, or playbackRate has changed (for now
        // at least).
        let newState = this.getState();
        let oldState = this.currentState;
        hasChanged = newState.delay !== oldState.delay ||
                     newState.iterationCount !== oldState.iterationCount ||
                     newState.iterationStart !== oldState.iterationStart ||
                     newState.duration !== oldState.duration ||
                     newState.endDelay !== oldState.endDelay ||
                     newState.playbackRate !== oldState.playbackRate;
        break;
      }
    }

    if (hasChanged) {
      this.emit("changed", this.getCurrentState());
    }
  },

  /**
   * Pause the player.
   */
  pause: function () {
    this.player.pause();
    return this.player.ready;
  },

  /**
   * Play the player.
   * This method only returns when the animation has left its pending state.
   */
  play: function () {
    this.player.play();
    return this.player.ready;
  },

  /**
   * Simply exposes the player ready promise.
   *
   * When an animation is created/paused then played, there's a short time
   * during which its playState is pending, before being set to running.
   *
   * If you either created a new animation using the Web Animations API or
   * paused/played an existing one, and then want to access the playState, you
   * might be interested to call this method.
   * This is especially important for tests.
   */
  ready: function () {
    return this.player.ready;
  },

  /**
   * Set the current time of the animation player.
   */
  setCurrentTime: function (currentTime) {
    // The spec is that the progress of animation is changed
    // if the time of setCurrentTime is during the endDelay.
    // We should prevent the time
    // to make the same animation behavior as the original.
    // Likewise, in case the time is less than 0.
    const timing = this.player.effect.getComputedTiming();
    if (timing.delay < 0) {
      currentTime += timing.delay;
    }
    if (currentTime < 0) {
      currentTime = 0;
    } else if (currentTime * this.player.playbackRate > timing.endTime) {
      currentTime = timing.endTime;
    }
    this.player.currentTime = currentTime * this.player.playbackRate;
  },

  /**
   * Set the playback rate of the animation player.
   */
  setPlaybackRate: function (playbackRate) {
    this.player.updatePlaybackRate(playbackRate);
    return this.player.ready;
  },

  /**
   * Get data about the keyframes of this animation player.
   * @return {Object} Returns a list of frames, each frame containing the list
   * animated properties as well as the frame's offset.
   */
  getFrames: function () {
    return this.player.effect.getKeyframes();
  },

  /**
   * Get data about the animated properties of this animation player.
   * @return {Array} Returns a list of animated properties.
   * Each property contains a list of values, their offsets and distances.
   */
  getProperties: function () {
    const properties = this.player.effect.getProperties().map(property => {
      return {name: property.property, values: property.values};
    });

    const DOMWindowUtils =
      this.window.QueryInterface(Ci.nsIInterfaceRequestor)
          .getInterface(Ci.nsIDOMWindowUtils);

    // Fill missing keyframe with computed value.
    for (let property of properties) {
      let underlyingValue = null;
      // Check only 0% and 100% keyframes.
      [0, property.values.length - 1].forEach(index => {
        const values = property.values[index];
        if (values.value !== undefined) {
          return;
        }
        if (!underlyingValue) {
          let pseudo = null;
          let target = this.player.effect.target;
          if (target.type) {
            // This target is a pseudo element.
            pseudo = target.type;
            target = target.parentElement;
          }
          const value =
            DOMWindowUtils.getUnanimatedComputedStyle(target,
                                                      pseudo,
                                                      property.name,
                                                      DOMWindowUtils.FLUSH_NONE);
          const animationType = DOMWindowUtils.getAnimationTypeForLonghand(property.name);
          underlyingValue = animationType === "float" ? parseFloat(value, 10) : value;
        }
        values.value = underlyingValue;
      });
    }

    // Calculate the distance.
    for (let property of properties) {
      const propertyName = property.name;
      const maxObject = { distance: -1 };
      for (let i = 0; i < property.values.length - 1; i++) {
        const value1 = property.values[i].value;
        for (let j = i + 1; j < property.values.length; j++) {
          const value2 = property.values[j].value;
          const distance = this.getDistance(this.player.effect.target, propertyName,
                                            value1, value2, DOMWindowUtils);
          if (maxObject.distance >= distance) {
            continue;
          }
          maxObject.distance = distance;
          maxObject.value1 = value1;
          maxObject.value2 = value2;
        }
      }
      if (maxObject.distance === 0) {
        // Distance is zero means that no values change or can't calculate the distance.
        // In this case, we use the keyframe offset as the distance.
        property.values.reduce((previous, current) => {
          // If the current value is same as previous value, use previous distance.
          current.distance =
            current.value === previous.value ? previous.distance : current.offset;
          return current;
        }, property.values[0]);
        continue;
      }
      const baseValue =
        maxObject.value1 < maxObject.value2 ? maxObject.value1 : maxObject.value2;
      for (let values of property.values) {
        const value = values.value;
        const distance = this.getDistance(this.player.effect.target, propertyName,
                                          baseValue, value, DOMWindowUtils);
        values.distance = distance / maxObject.distance;
      }
    }
    return properties;
  },

  /**
   * Get the animation types for a given list of CSS property names.
   * @param {Array} propertyNames - CSS property names (e.g. background-color)
   * @return {Object} Returns animation types (e.g. {"background-color": "rgb(0, 0, 0)"}.
   */
  getAnimationTypes: function (propertyNames) {
    const DOMWindowUtils = this.window.QueryInterface(Ci.nsIInterfaceRequestor)
                               .getInterface(Ci.nsIDOMWindowUtils);
    const animationTypes = {};
    for (let propertyName of propertyNames) {
      animationTypes[propertyName] =
        DOMWindowUtils.getAnimationTypeForLonghand(propertyName);
    }
    return animationTypes;
  },

  /**
   * Returns the distance of between value1, value2.
   * @param {Object} target - dom element
   * @param {String} propertyName - e.g. transform
   * @param {String} value1 - e.g. translate(0px)
   * @param {String} value2 - e.g. translate(10px)
   * @param {Object} DOMWindowUtils
   * @param {float} distance
   */
  getDistance: function (target, propertyName, value1, value2, DOMWindowUtils) {
    if (value1 === value2) {
      return 0;
    }
    try {
      const distance =
        DOMWindowUtils.computeAnimationDistance(target, propertyName, value1, value2);
      return distance;
    } catch (e) {
      // We can't compute the distance such the 'discrete' animation,
      // 'auto' keyword and so on.
      return 0;
    }
  }
});

exports.AnimationPlayerActor = AnimationPlayerActor;

/**
 * The Animations actor lists animation players for a given node.
 */
exports.AnimationsActor = protocol.ActorClassWithSpec(animationsSpec, {
  initialize: function (conn, tabActor) {
    Actor.prototype.initialize.call(this, conn);
    this.tabActor = tabActor;

    this.onWillNavigate = this.onWillNavigate.bind(this);
    this.onNavigate = this.onNavigate.bind(this);
    this.onAnimationMutation = this.onAnimationMutation.bind(this);

    this.allAnimationsPaused = false;
    this.tabActor.on("will-navigate", this.onWillNavigate);
    this.tabActor.on("navigate", this.onNavigate);
  },

  destroy: function () {
    Actor.prototype.destroy.call(this);
    this.tabActor.off("will-navigate", this.onWillNavigate);
    this.tabActor.off("navigate", this.onNavigate);

    this.stopAnimationPlayerUpdates();
    this.tabActor = this.observer = this.actors = this.walker = null;
  },

  /**
   * Clients can optionally call this with a reference to their WalkerActor.
   * If they do, then AnimationPlayerActor's forms are going to also include
   * NodeActor IDs when the corresponding NodeActors do exist.
   * This, in turns, is helpful for clients to avoid having to go back once more
   * to the server to get a NodeActor for a particular animation.
   * @param {WalkerActor} walker
   */
  setWalkerActor: function (walker) {
    this.walker = walker;
  },

  /**
   * Retrieve the list of AnimationPlayerActor actors for currently running
   * animations on a node and its descendants.
   * Note that calling this method a second time will destroy all previously
   * retrieved AnimationPlayerActors. Indeed, the lifecycle of these actors
   * is managed here on the server and tied to getAnimationPlayersForNode
   * being called.
   * @param {NodeActor} nodeActor The NodeActor as defined in
   * /devtools/server/actors/inspector
   */
  getAnimationPlayersForNode: function (nodeActor) {
    let animations = nodeActor.rawNode.getAnimations({subtree: true});

    // Destroy previously stored actors
    if (this.actors) {
      this.actors.forEach(actor => actor.destroy());
    }
    this.actors = [];

    for (let i = 0; i < animations.length; i++) {
      let actor = AnimationPlayerActor(this, animations[i]);
      this.actors.push(actor);
    }

    // When a front requests the list of players for a node, start listening
    // for animation mutations on this node to send updates to the front, until
    // either getAnimationPlayersForNode is called again or
    // stopAnimationPlayerUpdates is called.
    this.stopAnimationPlayerUpdates();
    // ownerGlobal doesn't exist in content privileged windows.
    // eslint-disable-next-line mozilla/use-ownerGlobal
    let win = nodeActor.rawNode.ownerDocument.defaultView;
    this.observer = new win.MutationObserver(this.onAnimationMutation);
    this.observer.observe(nodeActor.rawNode, {
      animations: true,
      subtree: true
    });

    return this.actors;
  },

  onAnimationMutation: function (mutations) {
    let eventData = [];
    let readyPromises = [];

    for (let {addedAnimations, removedAnimations} of mutations) {
      for (let player of removedAnimations) {
        // Note that animations are reported as removed either when they are
        // actually removed from the node (e.g. css class removed) or when they
        // are finished and don't have forwards animation-fill-mode.
        // In the latter case, we don't send an event, because the corresponding
        // animation can still be seeked/resumed, so we want the client to keep
        // its reference to the AnimationPlayerActor.
        if (player.playState !== "idle") {
          continue;
        }

        let index = this.actors.findIndex(a => a.player === player);
        if (index !== -1) {
          eventData.push({
            type: "removed",
            player: this.actors[index]
          });
          this.actors.splice(index, 1);
        }
      }

      for (let player of addedAnimations) {
        // If the added player already exists, it means we previously filtered
        // it out when it was reported as removed. So filter it out here too.
        if (this.actors.find(a => a.player === player)) {
          continue;
        }

        // If the added player has the same name and target node as a player we
        // already have, it means it's a transition that's re-starting. So send
        // a "removed" event for the one we already have.
        let index = this.actors.findIndex(a => {
          let isSameType = a.player.constructor === player.constructor;
          let isSameName = (a.isCssAnimation() &&
                            a.player.animationName === player.animationName) ||
                           (a.isCssTransition() &&
                            a.player.transitionProperty === player.transitionProperty);
          let isSameNode = a.player.effect.target === player.effect.target;

          return isSameType && isSameNode && isSameName;
        });
        if (index !== -1) {
          eventData.push({
            type: "removed",
            player: this.actors[index]
          });
          this.actors.splice(index, 1);
        }

        let actor = AnimationPlayerActor(this, player);
        this.actors.push(actor);
        eventData.push({
          type: "added",
          player: actor
        });
        readyPromises.push(player.ready);
      }
    }

    if (eventData.length) {
      // Let's wait for all added animations to be ready before telling the
      // front-end.
      Promise.all(readyPromises).then(() => {
        this.emit("mutations", eventData);
      });
    }
  },

  /**
   * After the client has called getAnimationPlayersForNode for a given DOM
   * node, the actor starts sending animation mutations for this node. If the
   * client doesn't want this to happen anymore, it should call this method.
   */
  stopAnimationPlayerUpdates: function () {
    if (this.observer && !Cu.isDeadWrapper(this.observer)) {
      this.observer.disconnect();
    }
  },

  /**
   * Iterates through all nodes below a given rootNode (optionally also in
   * nested frames) and finds all existing animation players.
   * @param {DOMNode} rootNode The root node to start iterating at. Animation
   * players will *not* be reported for this node.
   * @param {Boolean} traverseFrames Whether we should iterate through nested
   * frames too.
   * @return {Array} An array of AnimationPlayer objects.
   */
  getAllAnimations: function (rootNode, traverseFrames) {
    if (!traverseFrames) {
      return rootNode.getAnimations({subtree: true});
    }

    let animations = [];
    for (let {document} of this.tabActor.windows) {
      animations = [...animations, ...document.getAnimations({subtree: true})];
    }
    return animations;
  },

  onWillNavigate: function ({isTopLevel}) {
    if (isTopLevel) {
      this.stopAnimationPlayerUpdates();
    }
  },

  onNavigate: function ({isTopLevel}) {
    if (isTopLevel) {
      this.allAnimationsPaused = false;
    }
  },

  /**
   * Pause all animations in the current tabActor's frames.
   */
  pauseAll: function () {
    let readyPromises = [];
    // Until the WebAnimations API provides a way to play/pause via the document
    // timeline, we have to iterate through the whole DOM to find all players.
    for (let player of
         this.getAllAnimations(this.tabActor.window.document, true)) {
      player.pause();
      readyPromises.push(player.ready);
    }
    this.allAnimationsPaused = true;
    return Promise.all(readyPromises);
  },

  /**
   * Play all animations in the current tabActor's frames.
   * This method only returns when animations have left their pending states.
   */
  playAll: function () {
    let readyPromises = [];
    // Until the WebAnimations API provides a way to play/pause via the document
    // timeline, we have to iterate through the whole DOM to find all players.
    for (let player of
         this.getAllAnimations(this.tabActor.window.document, true)) {
      player.play();
      readyPromises.push(player.ready);
    }
    this.allAnimationsPaused = false;
    return Promise.all(readyPromises);
  },

  toggleAll: function () {
    if (this.allAnimationsPaused) {
      return this.playAll();
    }
    return this.pauseAll();
  },

  /**
   * Toggle (play/pause) several animations at the same time.
   * @param {Array} players A list of AnimationPlayerActor objects.
   * @param {Boolean} shouldPause If set to true, the players will be paused,
   * otherwise they will be played.
   */
  toggleSeveral: function (players, shouldPause) {
    return Promise.all(players.map(player => {
      return shouldPause ? player.pause() : player.play();
    }));
  },

  /**
   * Set the current time of several animations at the same time.
   * @param {Array} players A list of AnimationPlayerActor.
   * @param {Number} time The new currentTime.
   * @param {Boolean} shouldPause Should the players be paused too.
   */
  setCurrentTimes: function (players, time, shouldPause) {
    return Promise.all(players.map(player => {
      let pause = shouldPause ? player.pause() : Promise.resolve();
      return pause.then(() => player.setCurrentTime(time));
    }));
  },

  /**
   * Set the playback rate of several animations at the same time.
   * @param {Array} players A list of AnimationPlayerActor.
   * @param {Number} rate The new rate.
   */
  setPlaybackRates: function (players, rate) {
    return Promise.all(
      players.map(player => player.setPlaybackRate(rate))
    );
  }
});
PK
!<X�p<chrome/devtools/modules/devtools/server/actors/breakpoint.js/* -*- indent-tabs-mode: nil; js-indent-level: 2; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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/. */

/* global assert */

"use strict";

const { ActorClassWithSpec } = require("devtools/shared/protocol");
const { breakpointSpec } = require("devtools/shared/specs/breakpoint");

/**
 * Set breakpoints on all the given entry points with the given
 * BreakpointActor as the handler.
 *
 * @param BreakpointActor actor
 *        The actor handling the breakpoint hits.
 * @param Array entryPoints
 *        An array of objects of the form `{ script, offsets }`.
 */
function setBreakpointAtEntryPoints(actor, entryPoints) {
  for (let { script, offsets } of entryPoints) {
    actor.addScript(script);
    for (let offset of offsets) {
      script.setBreakpoint(offset, actor);
    }
  }
}

exports.setBreakpointAtEntryPoints = setBreakpointAtEntryPoints;

/**
 * BreakpointActors exist for the lifetime of their containing thread and are
 * responsible for deleting breakpoints, handling breakpoint hits and
 * associating breakpoints with scripts.
 */
let BreakpointActor = ActorClassWithSpec(breakpointSpec, {
  /**
   * Create a Breakpoint actor.
   *
   * @param ThreadActor threadActor
   *        The parent thread actor that contains this breakpoint.
   * @param OriginalLocation originalLocation
   *        The original location of the breakpoint.
   */
  initialize: function (threadActor, originalLocation) {
    // The set of Debugger.Script instances that this breakpoint has been set
    // upon.
    this.scripts = new Set();

    this.threadActor = threadActor;
    this.originalLocation = originalLocation;
    this.condition = null;
    this.isPending = true;
  },

  destroy: function () {
    this.removeScripts();
  },

  hasScript: function (script) {
    return this.scripts.has(script);
  },

  /**
   * Called when this same breakpoint is added to another Debugger.Script
   * instance.
   *
   * @param script Debugger.Script
   *        The new source script on which the breakpoint has been set.
   */
  addScript: function (script) {
    this.scripts.add(script);
    this.isPending = false;
  },

  /**
   * Remove the breakpoints from associated scripts and clear the script cache.
   */
  removeScripts: function () {
    for (let script of this.scripts) {
      script.clearBreakpoint(this);
    }
    this.scripts.clear();
  },

  /**
   * Check if this breakpoint has a condition that doesn't error and
   * evaluates to true in frame.
   *
   * @param frame Debugger.Frame
   *        The frame to evaluate the condition in
   * @returns Object
   *          - result: boolean|undefined
   *            True when the conditional breakpoint should trigger a pause,
   *            false otherwise. If the condition evaluation failed/killed,
   *            `result` will be `undefined`.
   *          - message: string
   *            If the condition throws, this is the thrown message.
   */
  checkCondition: function (frame) {
    let completion = frame.eval(this.condition);
    if (completion) {
      if (completion.throw) {
        // The evaluation failed and threw
        let message = "Unknown exception";
        try {
          if (completion.throw.getOwnPropertyDescriptor) {
            message = completion.throw.getOwnPropertyDescriptor("message")
                      .value;
          } else if (completion.toString) {
            message = completion.toString();
          }
        } catch (ex) {
          // ignore
        }
        return {
          result: true,
          message: message
        };
      } else if (completion.yield) {
        assert(false, "Shouldn't ever get yield completions from an eval");
      } else {
        return { result: !!completion.return };
      }
    }
    // The evaluation was killed (possibly by the slow script dialog)
    return { result: undefined };
  },

  /**
   * A function that the engine calls when a breakpoint has been hit.
   *
   * @param frame Debugger.Frame
   *        The stack frame that contained the breakpoint.
   */
  hit: function (frame) {
    // Don't pause if we are currently stepping (in or over) or the frame is
    // black-boxed.
    let generatedLocation = this.threadActor.sources.getFrameLocation(frame);
    let {
      originalSourceActor,
      originalLine,
      originalColumn
    } = this.threadActor.unsafeSynchronize(
      this.threadActor.sources.getOriginalLocation(generatedLocation));
    let url = originalSourceActor.url;

    if (this.threadActor.sources.isBlackBoxed(url)
        || frame.onStep) {
      return undefined;
    }

    // If we're trying to pop this frame, and we see a breakpoint at
    // the spot at which popping started, ignore it.  See bug 970469.
    const locationAtFinish = frame.onPop && frame.onPop.originalLocation;
    if (locationAtFinish &&
        locationAtFinish.originalLine === originalLine &&
        locationAtFinish.originalColumn === originalColumn) {
      return undefined;
    }

    let reason = {};

    if (this.threadActor._hiddenBreakpoints.has(this.actorID)) {
      reason.type = "pauseOnDOMEvents";
    } else if (!this.condition) {
      reason.type = "breakpoint";
      // TODO: add the rest of the breakpoints on that line (bug 676602).
      reason.actors = [ this.actorID ];
    } else {
      let { result, message } = this.checkCondition(frame);

      if (result) {
        if (!message) {
          reason.type = "breakpoint";
        } else {
          reason.type = "breakpointConditionThrown";
          reason.message = message;
        }
        reason.actors = [ this.actorID ];
      } else {
        return undefined;
      }
    }
    return this.threadActor._pauseAndRespond(frame, reason);
  },

  /**
   * Handle a protocol request to remove this breakpoint.
   */
  delete: function () {
    // Remove from the breakpoint store.
    if (this.originalLocation) {
      this.threadActor.breakpointActorMap.deleteActor(this.originalLocation);
    }
    this.threadActor.threadLifetimePool.removeActor(this);
    // Remove the actual breakpoint from the associated scripts.
    this.removeScripts();
  }
});

exports.BreakpointActor = BreakpointActor;
PK
!<��Q�Q>chrome/devtools/modules/devtools/server/actors/call-watcher.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 XPCNativeWrapper */

const {Ci, Cu} = require("chrome");
const protocol = require("devtools/shared/protocol");
const ChromeUtils = require("ChromeUtils");
// base-loader.js is a JSM without .jsm file extension, so it has to be loaded
// via ChromeUtils.import and not require() which would consider it as a CommonJS module
const {serializeStack, parseStack} = ChromeUtils.import("resource://devtools/shared/base-loader.js", {});

const { functionCallSpec, callWatcherSpec } = require("devtools/shared/specs/call-watcher");
const { CallWatcherFront } = require("devtools/shared/fronts/call-watcher");

/**
 * This actor contains information about a function call, like the function
 * type, name, stack, arguments, returned value etc.
 */
var FunctionCallActor = protocol.ActorClassWithSpec(functionCallSpec, {
  /**
   * Creates the function call actor.
   *
   * @param DebuggerServerConnection conn
   *        The server connection.
   * @param DOMWindow window
   *        The content window.
   * @param string global
   *        The name of the global object owning this function, like
   *        "CanvasRenderingContext2D" or "WebGLRenderingContext".
   * @param object caller
   *        The object owning the function when it was called.
   *        For example, in `foo.bar()`, the caller is `foo`.
   * @param number type
   *        Either METHOD_FUNCTION, METHOD_GETTER or METHOD_SETTER.
   * @param string name
   *        The called function's name.
   * @param array stack
   *        The called function's stack, as a list of { name, file, line } objects.
   * @param number timestamp
   *        The performance.now() timestamp when the function was called.
   * @param array args
   *        The called function's arguments.
   * @param any result
   *        The value returned by the function call.
   * @param boolean holdWeak
   *        Determines whether or not FunctionCallActor stores a weak reference
   *        to the underlying objects.
   */
  initialize: function (
    conn,
    [window, global, caller, type, name, stack, timestamp, args, result],
    holdWeak
  ) {
    protocol.Actor.prototype.initialize.call(this, conn);

    this.details = {
      global: global,
      type: type,
      name: name,
      stack: stack,
      timestamp: timestamp
    };

    // Store a weak reference to all objects so we don't
    // prevent natural GC if `holdWeak` was passed into
    // setup as truthy.
    if (holdWeak) {
      let weakRefs = {
        window: Cu.getWeakReference(window),
        caller: Cu.getWeakReference(caller),
        args: Cu.getWeakReference(args),
        result: Cu.getWeakReference(result),
      };

      Object.defineProperties(this.details, {
        window: { get: () => weakRefs.window.get() },
        caller: { get: () => weakRefs.caller.get() },
        args: { get: () => weakRefs.args.get() },
        result: { get: () => weakRefs.result.get() },
      });
    } else {
      // Otherwise, hold strong references to the objects.
      this.details.window = window;
      this.details.caller = caller;
      this.details.args = args;
      this.details.result = result;
    }

    // The caller, args and results are string names for now. It would
    // certainly be nicer if they were Object actors. Make this smarter, so
    // that the frontend can inspect each argument, be it object or primitive.
    // Bug 978960.
    this.details.previews = {
      caller: this._generateStringPreview(caller),
      args: this._generateArgsPreview(args),
      result: this._generateStringPreview(result)
    };
  },

  /**
   * Customize the marshalling of this actor to provide some generic information
   * directly on the Front instance.
   */
  form: function () {
    return {
      actor: this.actorID,
      type: this.details.type,
      name: this.details.name,
      file: this.details.stack[0].file,
      line: this.details.stack[0].line,
      timestamp: this.details.timestamp,
      callerPreview: this.details.previews.caller,
      argsPreview: this.details.previews.args,
      resultPreview: this.details.previews.result
    };
  },

  /**
   * Gets more information about this function call, which is not necessarily
   * available on the Front instance.
   */
  getDetails: function () {
    let { type, name, stack, timestamp } = this.details;

    // Since not all calls on the stack have corresponding owner files (e.g.
    // callbacks of a requestAnimationFrame etc.), there's no benefit in
    // returning them, as the user can't jump to the Debugger from them.
    for (let i = stack.length - 1; ;) {
      if (stack[i].file) {
        break;
      }
      stack.pop();
      i--;
    }

    // XXX: Use grips for objects and serialize them properly, in order
    // to add the function's caller, arguments and return value. Bug 978957.
    return {
      type: type,
      name: name,
      stack: stack,
      timestamp: timestamp
    };
  },

  /**
   * Serializes the arguments so that they can be easily be transferred
   * as a string, but still be useful when displayed in a potential UI.
   *
   * @param array args
   *        The source arguments.
   * @return string
   *         The arguments as a string.
   */
  _generateArgsPreview: function (args) {
    let { global, name, caller } = this.details;

    // Get method signature to determine if there are any enums
    // used in this method.
    let methodSignatureEnums;

    let knownGlobal = CallWatcherFront.KNOWN_METHODS[global];
    if (knownGlobal) {
      let knownMethod = knownGlobal[name];
      if (knownMethod) {
        let isOverloaded = typeof knownMethod.enums === "function";
        if (isOverloaded) {
          methodSignatureEnums = methodSignatureEnums(args);
        } else {
          methodSignatureEnums = knownMethod.enums;
        }
      }
    }

    let serializeArgs = () => args.map((arg, i) => {
      // XXX: Bug 978960.
      if (arg === undefined) {
        return "undefined";
      }
      if (arg === null) {
        return "null";
      }
      if (typeof arg == "function") {
        return "Function";
      }
      if (typeof arg == "object") {
        return "Object";
      }
      // If this argument matches the method's signature
      // and is an enum, change it to its constant name.
      if (methodSignatureEnums && methodSignatureEnums.has(i)) {
        return getBitToEnumValue(global, caller, arg);
      }
      return arg + "";
    });

    return serializeArgs().join(", ");
  },

  /**
   * Serializes the data so that it can be easily be transferred
   * as a string, but still be useful when displayed in a potential UI.
   *
   * @param object data
   *        The source data.
   * @return string
   *         The arguments as a string.
   */
  _generateStringPreview: function (data) {
    // XXX: Bug 978960.
    if (data === undefined) {
      return "undefined";
    }
    if (data === null) {
      return "null";
    }
    if (typeof data == "function") {
      return "Function";
    }
    if (typeof data == "object") {
      return "Object";
    }
    return data + "";
  }
});

/**
 * This actor observes function calls on certain objects or globals.
 */
exports.CallWatcherActor = protocol.ActorClassWithSpec(callWatcherSpec, {
  initialize: function (conn, tabActor) {
    protocol.Actor.prototype.initialize.call(this, conn);
    this.tabActor = tabActor;
    this._onGlobalCreated = this._onGlobalCreated.bind(this);
    this._onGlobalDestroyed = this._onGlobalDestroyed.bind(this);
    this._onContentFunctionCall = this._onContentFunctionCall.bind(this);
    this.tabActor.on("window-ready", this._onGlobalCreated);
    this.tabActor.on("window-destroyed", this._onGlobalDestroyed);
  },
  destroy: function (conn) {
    protocol.Actor.prototype.destroy.call(this, conn);
    this.tabActor.off("window-ready", this._onGlobalCreated);
    this.tabActor.off("window-destroyed", this._onGlobalDestroyed);
    this.finalize();
  },

  /**
   * Lightweight listener invoked whenever an instrumented function is called
   * while recording. We're doing this to avoid the event emitter overhead,
   * since this is expected to be a very hot function.
   */
  onCall: null,

  /**
   * Starts waiting for the current tab actor's document global to be
   * created, in order to instrument the specified objects and become
   * aware of everything the content does with them.
   */
  setup: function ({
    tracedGlobals, tracedFunctions, startRecording, performReload, holdWeak, storeCalls
  }) {
    if (this._initialized) {
      return;
    }
    this._initialized = true;
    this._timestampEpoch = 0;

    this._functionCalls = [];
    this._tracedGlobals = tracedGlobals || [];
    this._tracedFunctions = tracedFunctions || [];
    this._holdWeak = !!holdWeak;
    this._storeCalls = !!storeCalls;

    if (startRecording) {
      this.resumeRecording();
    }
    if (performReload) {
      this.tabActor.window.location.reload();
    }
  },

  /**
   * Stops listening for document global changes and puts this actor
   * to hibernation. This method is called automatically just before the
   * actor is destroyed.
   */
  finalize: function () {
    if (!this._initialized) {
      return;
    }
    this._initialized = false;
    this._finalized = true;

    this._tracedGlobals = null;
    this._tracedFunctions = null;
  },

  /**
   * Returns whether the instrumented function calls are currently recorded.
   */
  isRecording: function () {
    return this._recording;
  },

  /**
   * Initialize the timestamp epoch used to offset function call timestamps.
   */
  initTimestampEpoch: function () {
    this._timestampEpoch = this.tabActor.window.performance.now();
  },

  /**
   * Starts recording function calls.
   */
  resumeRecording: function () {
    this._recording = true;
  },

  /**
   * Stops recording function calls.
   */
  pauseRecording: function () {
    this._recording = false;
    return this._functionCalls;
  },

  /**
   * Erases all the recorded function calls.
   * Calling `resumeRecording` or `pauseRecording` does not erase history.
   */
  eraseRecording: function () {
    this._functionCalls = [];
  },

  /**
   * Invoked whenever the current tab actor's document global is created.
   */
  _onGlobalCreated: function ({window, id, isTopLevel}) {
    if (!this._initialized) {
      return;
    }

    // TODO: bug 981748, support more than just the top-level documents.
    if (!isTopLevel) {
      return;
    }

    let self = this;
    this._tracedWindowId = id;

    let unwrappedWindow = XPCNativeWrapper.unwrap(window);
    let callback = this._onContentFunctionCall;

    for (let global of this._tracedGlobals) {
      let prototype = unwrappedWindow[global].prototype;
      let properties = Object.keys(prototype);
      properties.forEach(name => overrideSymbol(global, prototype, name, callback));
    }

    for (let name of this._tracedFunctions) {
      overrideSymbol("window", unwrappedWindow, name, callback);
    }

    /**
     * Instruments a method, getter or setter on the specified target object to
     * invoke a callback whenever it is called.
     */
    function overrideSymbol(global, target, name, subcallback) {
      let propertyDescriptor = Object.getOwnPropertyDescriptor(target, name);

      if (propertyDescriptor.get || propertyDescriptor.set) {
        overrideAccessor(global, target, name, propertyDescriptor, subcallback);
        return;
      }
      if (propertyDescriptor.writable && typeof propertyDescriptor.value == "function") {
        overrideFunction(global, target, name, propertyDescriptor, subcallback);
      }
    }

    /**
     * Instruments a function on the specified target object.
     */
    function overrideFunction(global, target, name, descriptor, subcallback) {
      // Invoking .apply on an unxrayed content function doesn't work, because
      // the arguments array is inaccessible to it. Get Xrays back.
      let originalFunc = Cu.unwaiveXrays(target[name]);

      Cu.exportFunction(function (...args) {
        let result;
        try {
          result = Cu.waiveXrays(originalFunc.apply(this, args));
        } catch (e) {
          throw createContentError(e, unwrappedWindow);
        }

        if (self._recording) {
          let type = CallWatcherFront.METHOD_FUNCTION;
          let stack = getStack(name);
          let timestamp = self.tabActor.window.performance.now() - self._timestampEpoch;
          subcallback(unwrappedWindow, global, this, type, name, stack, timestamp,
            args, result);
        }
        return result;
      }, target, { defineAs: name });

      Object.defineProperty(target, name, {
        configurable: descriptor.configurable,
        enumerable: descriptor.enumerable,
        writable: true
      });
    }

    /**
     * Instruments a getter or setter on the specified target object.
     */
    function overrideAccessor(global, target, name, descriptor, subcallback) {
      // Invoking .apply on an unxrayed content function doesn't work, because
      // the arguments array is inaccessible to it. Get Xrays back.
      let originalGetter = Cu.unwaiveXrays(target.__lookupGetter__(name));
      let originalSetter = Cu.unwaiveXrays(target.__lookupSetter__(name));

      Object.defineProperty(target, name, {
        get: function (...args) {
          if (!originalGetter) {
            return undefined;
          }
          let result = Cu.waiveXrays(originalGetter.apply(this, args));

          if (self._recording) {
            let type = CallWatcherFront.GETTER_FUNCTION;
            let stack = getStack(name);
            let timestamp = self.tabActor.window.performance.now() - self._timestampEpoch;
            subcallback(unwrappedWindow, global, this, type, name, stack, timestamp,
              args, result);
          }
          return result;
        },
        set: function (...args) {
          if (!originalSetter) {
            return;
          }
          originalSetter.apply(this, args);

          if (self._recording) {
            let type = CallWatcherFront.SETTER_FUNCTION;
            let stack = getStack(name);
            let timestamp = self.tabActor.window.performance.now() - self._timestampEpoch;
            subcallback(unwrappedWindow, global, this, type, name, stack, timestamp,
              args, undefined);
          }
        },
        configurable: descriptor.configurable,
        enumerable: descriptor.enumerable
      });
    }

    /**
     * Stores the relevant information about calls on the stack when
     * a function is called.
     */
    function getStack(caller) {
      let stack;
      try {
        // Using Components.stack wouldn't be a better idea, since it's
        // much slower because it attempts to retrieve the C++ stack as well.
        throw new Error();
      } catch (e) {
        stack = e.stack;
      }

      // Of course, using a simple regex like /(.*?)@(.*):(\d*):\d*/ would be
      // much prettier, but this is a very hot function, so let's sqeeze
      // every drop of performance out of it.
      let calls = [];
      let callIndex = 0;
      let currNewLinePivot = stack.indexOf("\n") + 1;
      let nextNewLinePivot = stack.indexOf("\n", currNewLinePivot);

      while (nextNewLinePivot > 0) {
        let nameDelimiterIndex = stack.indexOf("@", currNewLinePivot);
        let columnDelimiterIndex = stack.lastIndexOf(":", nextNewLinePivot - 1);
        let lineDelimiterIndex = stack.lastIndexOf(":", columnDelimiterIndex - 1);

        if (!calls[callIndex]) {
          calls[callIndex] = { name: "", file: "", line: 0 };
        }
        if (!calls[callIndex + 1]) {
          calls[callIndex + 1] = { name: "", file: "", line: 0 };
        }

        if (callIndex > 0) {
          let file = stack.substring(nameDelimiterIndex + 1, lineDelimiterIndex);
          let line = stack.substring(lineDelimiterIndex + 1, columnDelimiterIndex);
          let name = stack.substring(currNewLinePivot, nameDelimiterIndex);
          calls[callIndex].name = name;
          calls[callIndex - 1].file = file;
          calls[callIndex - 1].line = line;
        } else {
          // Since the topmost stack frame is actually our overwritten function,
          // it will not have the expected name.
          calls[0].name = caller;
        }

        currNewLinePivot = nextNewLinePivot + 1;
        nextNewLinePivot = stack.indexOf("\n", currNewLinePivot);
        callIndex++;
      }

      return calls;
    }
  },

  /**
   * Invoked whenever the current tab actor's inner window is destroyed.
   */
  _onGlobalDestroyed: function ({window, id, isTopLevel}) {
    if (this._tracedWindowId == id) {
      this.pauseRecording();
      this.eraseRecording();
      this._timestampEpoch = 0;
    }
  },

  /**
   * Invoked whenever an instrumented function is called.
   */
  _onContentFunctionCall: function (...details) {
    // If the consuming tool has finalized call-watcher, ignore the
    // still-instrumented calls.
    if (this._finalized) {
      return;
    }

    let functionCall = new FunctionCallActor(this.conn, details, this._holdWeak);

    if (this._storeCalls) {
      this._functionCalls.push(functionCall);
    }

    if (this.onCall) {
      this.onCall(functionCall);
    } else {
      this.emit("call", functionCall);
    }
  }
});

/**
 * A lookup table for cross-referencing flags or properties with their name
 * assuming they look LIKE_THIS most of the time.
 *
 * For example, when gl.clear(gl.COLOR_BUFFER_BIT) is called, the actual passed
 * argument's value is 16384, which we want identified as "COLOR_BUFFER_BIT".
 */
var gEnumRegex = /^[A-Z][A-Z0-9_]+$/;
var gEnumsLookupTable = {};

// These values are returned from errors, or empty values,
// and need to be ignored when checking arguments due to the bitwise math.
var INVALID_ENUMS = [
  "INVALID_ENUM", "NO_ERROR", "INVALID_VALUE", "OUT_OF_MEMORY", "NONE"
];

function getBitToEnumValue(type, object, arg) {
  let table = gEnumsLookupTable[type];

  // If mapping not yet created, do it on the first run.
  if (!table) {
    table = gEnumsLookupTable[type] = {};

    for (let key in object) {
      if (key.match(gEnumRegex)) {
        // Maps `16384` to `"COLOR_BUFFER_BIT"`, etc.
        table[object[key]] = key;
      }
    }
  }

  // If a single bit value, just return it.
  if (table[arg]) {
    return table[arg];
  }

  // Otherwise, attempt to reduce it to the original bit flags:
  // `16640` -> "COLOR_BUFFER_BIT | DEPTH_BUFFER_BIT"
  let flags = [];
  for (let flag in table) {
    if (INVALID_ENUMS.includes(table[flag])) {
      continue;
    }

    // Cast to integer as all values are stored as strings
    // in `table`
    flag = flag | 0;
    if (flag && (arg & flag) === flag) {
      flags.push(table[flag]);
    }
  }

  // Cache the combined bitmask value
  table[arg] = flags.join(" | ") || arg;
  return table[arg];
}

/**
 * Creates a new error from an error that originated from content but was called
 * from a wrapped overridden method. This is so we can make our own error
 * that does not look like it originated from the call watcher.
 *
 * We use toolkit/loader's parseStack and serializeStack rather than the
 * parsing done in the local `getStack` function, because it does not expose
 * column number, would have to change the protocol models `call-stack-items`
 * and `call-details` which hurts backwards compatibility, and the local `getStack`
 * is an optimized, hot function.
 */
function createContentError(e, win) {
  let { message, name, stack } = e;
  let parsedStack = parseStack(stack);
  let { fileName, lineNumber, columnNumber } = parsedStack[parsedStack.length - 1];
  let error;

  let isDOMException = e instanceof Ci.nsIDOMDOMException;
  let constructor = isDOMException ? win.DOMException : (win[e.name] || win.Error);

  if (isDOMException) {
    error = new constructor(message, name);
    Object.defineProperties(error, {
      code: { value: e.code },
      // columnNumber is always 0 for DOMExceptions?
      columnNumber: { value: 0 },
      // note the lowercase `filename`
      filename: { value: fileName },
      lineNumber: { value: lineNumber },
      result: { value: e.result },
      stack: { value: serializeStack(parsedStack) }
    });
  } else {
    // Constructing an error here retains all the stack information,
    // and we can add message, fileName and lineNumber via constructor, though
    // need to manually add columnNumber.
    error = new constructor(message, fileName, lineNumber);
    Object.defineProperty(error, "columnNumber", {
      value: columnNumber
    });
  }
  return error;
}
PK
!<����i�i8chrome/devtools/modules/devtools/server/actors/canvas.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 XPCNativeWrapper */

const defer = require("devtools/shared/defer");
const protocol = require("devtools/shared/protocol");
const {CallWatcherActor} = require("devtools/server/actors/call-watcher");
const {CallWatcherFront} = require("devtools/shared/fronts/call-watcher");
const {WebGLPrimitiveCounter} = require("devtools/server/primitive");
const {
  frameSnapshotSpec,
  canvasSpec,
  CANVAS_CONTEXTS,
  ANIMATION_GENERATORS,
  LOOP_GENERATORS
} = require("devtools/shared/specs/canvas");
const {CanvasFront} = require("devtools/shared/fronts/canvas");

/**
 * This actor represents a recorded animation frame snapshot, along with
 * all the corresponding canvas' context methods invoked in that frame,
 * thumbnails for each draw call and a screenshot of the end result.
 */
var FrameSnapshotActor = protocol.ActorClassWithSpec(frameSnapshotSpec, {
  /**
   * Creates the frame snapshot call actor.
   *
   * @param DebuggerServerConnection conn
   *        The server connection.
   * @param HTMLCanvasElement canvas
   *        A reference to the content canvas.
   * @param array calls
   *        An array of "function-call" actor instances.
   * @param object screenshot
   *        A single "snapshot-image" type instance.
   */
  initialize: function (conn, { canvas, calls, screenshot, primitive }) {
    protocol.Actor.prototype.initialize.call(this, conn);
    this._contentCanvas = canvas;
    this._functionCalls = calls;
    this._animationFrameEndScreenshot = screenshot;
    this._primitive = primitive;
  },

  /**
   * Gets as much data about this snapshot without computing anything costly.
   */
  getOverview: function () {
    return {
      calls: this._functionCalls,
      thumbnails: this._functionCalls.map(e => e._thumbnail).filter(e => !!e),
      screenshot: this._animationFrameEndScreenshot,
      primitive: {
        tris: this._primitive.tris,
        vertices: this._primitive.vertices,
        points: this._primitive.points,
        lines: this._primitive.lines
      }
    };
  },

  /**
   * Gets a screenshot of the canvas's contents after the specified
   * function was called.
   */
  generateScreenshotFor: function (functionCall) {
    let global = functionCall.details.global;

    let canvas = this._contentCanvas;
    let calls = this._functionCalls;
    let index = calls.indexOf(functionCall);

    // To get a screenshot, replay all the steps necessary to render the frame,
    // by invoking the context calls up to and including the specified one.
    // This will be done in a custom framebuffer in case of a WebGL context.
    let replayData = ContextUtils.replayAnimationFrame({
      contextType: global,
      canvas: canvas,
      calls: calls,
      first: 0,
      last: index
    });

    let {
      replayContext,
      replayContextScaling,
      lastDrawCallIndex,
      doCleanup
    } = replayData;
    let [left, top, width, height] = replayData.replayViewport;
    let screenshot;

    // Depending on the canvas' context, generating a screenshot is done
    // in different ways.
    if (global == "WebGLRenderingContext") {
      screenshot = ContextUtils.getPixelsForWebGL(replayContext, left, top,
        width, height);
      screenshot.flipped = true;
    } else if (global == "CanvasRenderingContext2D") {
      screenshot = ContextUtils.getPixelsFor2D(replayContext, left, top, width, height);
      screenshot.flipped = false;
    }

    // In case of the WebGL context, we also need to reset the framebuffer
    // binding to the original value, after generating the screenshot.
    doCleanup();

    screenshot.scaling = replayContextScaling;
    screenshot.index = lastDrawCallIndex;
    return screenshot;
  }
});

/**
 * This Canvas Actor handles simple instrumentation of all the methods
 * of a 2D or WebGL context, to provide information regarding all the calls
 * made when drawing frame inside an animation loop.
 */
exports.CanvasActor = protocol.ActorClassWithSpec(canvasSpec, {
  // Reset for each recording, boolean indicating whether or not
  // any draw calls were called for a recording.
  _animationContainsDrawCall: false,

  initialize: function (conn, tabActor) {
    protocol.Actor.prototype.initialize.call(this, conn);
    this.tabActor = tabActor;
    this._webGLPrimitiveCounter = new WebGLPrimitiveCounter(tabActor);
    this._onContentFunctionCall = this._onContentFunctionCall.bind(this);
  },
  destroy: function (conn) {
    protocol.Actor.prototype.destroy.call(this, conn);
    this._webGLPrimitiveCounter.destroy();
    this.finalize();
  },

  /**
   * Starts listening for function calls.
   */
  setup: function ({ reload }) {
    if (this._initialized) {
      if (reload) {
        this.tabActor.window.location.reload();
      }
      return;
    }
    this._initialized = true;

    this._callWatcher = new CallWatcherActor(this.conn, this.tabActor);
    this._callWatcher.onCall = this._onContentFunctionCall;
    this._callWatcher.setup({
      tracedGlobals: CANVAS_CONTEXTS,
      tracedFunctions: [...ANIMATION_GENERATORS, ...LOOP_GENERATORS],
      performReload: reload,
      storeCalls: true
    });
  },

  /**
   * Stops listening for function calls.
   */
  finalize: function () {
    if (!this._initialized) {
      return;
    }
    this._initialized = false;

    this._callWatcher.finalize();
    this._callWatcher = null;
  },

  /**
   * Returns whether this actor has been set up.
   */
  isInitialized: function () {
    return !!this._initialized;
  },

  /**
   * Returns whether or not the CanvasActor is recording an animation.
   * Used in tests.
   */
  isRecording: function () {
    return !!this._callWatcher.isRecording();
  },

  /**
   * Records a snapshot of all the calls made during the next animation frame.
   * The animation should be implemented via the de-facto requestAnimationFrame
   * utility, or inside recursive `setTimeout`s. `setInterval` at this time are
   * not supported.
   */
  recordAnimationFrame: function () {
    if (this._callWatcher.isRecording()) {
      return this._currentAnimationFrameSnapshot.promise;
    }

    this._recordingContainsDrawCall = false;
    this._callWatcher.eraseRecording();
    this._callWatcher.initTimestampEpoch();
    this._webGLPrimitiveCounter.resetCounts();
    this._callWatcher.resumeRecording();

    let deferred = this._currentAnimationFrameSnapshot = defer();
    return deferred.promise;
  },

  /**
   * Cease attempts to record an animation frame.
   */
  stopRecordingAnimationFrame: function () {
    if (!this._callWatcher.isRecording()) {
      return;
    }
    this._animationStarted = false;
    this._callWatcher.pauseRecording();
    this._callWatcher.eraseRecording();
    this._currentAnimationFrameSnapshot.resolve(null);
    this._currentAnimationFrameSnapshot = null;
  },

  /**
   * Invoked whenever an instrumented function is called, be it on a
   * 2d or WebGL context, or an animation generator like requestAnimationFrame.
   */
  _onContentFunctionCall: function (functionCall) {
    let { window, name, args } = functionCall.details;

    // The function call arguments are required to replay animation frames,
    // in order to generate screenshots. However, simply storing references to
    // every kind of object is a bad idea, since their properties may change.
    // Consider transformation matrices for example, which are typically
    // Float32Arrays whose values can easily change across context calls.
    // They need to be cloned.
    inplaceShallowCloneArrays(args, window);

    // Handle animations generated using requestAnimationFrame
    if (CanvasFront.ANIMATION_GENERATORS.has(name)) {
      this._handleAnimationFrame(functionCall);
      return;
    }
    // Handle animations generated using setTimeout. While using
    // those timers is considered extremely poor practice, they're still widely
    // used on the web, especially for old demos; it's nice to support them as well.
    if (CanvasFront.LOOP_GENERATORS.has(name)) {
      this._handleAnimationFrame(functionCall);
      return;
    }
    if (CanvasFront.DRAW_CALLS.has(name) && this._animationStarted) {
      this._handleDrawCall(functionCall);
      this._webGLPrimitiveCounter.handleDrawPrimitive(functionCall);
    }
  },

  /**
   * Handle animations generated using requestAnimationFrame.
   */
  _handleAnimationFrame: function (functionCall) {
    if (!this._animationStarted) {
      this._handleAnimationFrameBegin();
    } else if (this._animationContainsDrawCall) {
      // Check to see if draw calls occurred yet, as it could be future frames,
      // like in the scenario where requestAnimationFrame is called to trigger
      // an animation, and rAF is at the beginning of the animate loop.
      this._handleAnimationFrameEnd(functionCall);
    }
  },

  /**
   * Called whenever an animation frame rendering begins.
   */
  _handleAnimationFrameBegin: function () {
    this._callWatcher.eraseRecording();
    this._animationStarted = true;
  },

  /**
   * Called whenever an animation frame rendering ends.
   */
  _handleAnimationFrameEnd: function () {
    // Get a hold of all the function calls made during this animation frame.
    // Since only one snapshot can be recorded at a time, erase all the
    // previously recorded calls.
    let functionCalls = this._callWatcher.pauseRecording();
    this._callWatcher.eraseRecording();
    this._animationContainsDrawCall = false;

    // Since the animation frame finished, get a hold of the (already retrieved)
    // canvas pixels to conveniently create a screenshot of the final rendering.
    let index = this._lastDrawCallIndex;
    let width = this._lastContentCanvasWidth;
    let height = this._lastContentCanvasHeight;
    // undefined -> false
    let flipped = !!this._lastThumbnailFlipped;
    let pixels = ContextUtils.getPixelStorage()["8bit"];
    let primitiveResult = this._webGLPrimitiveCounter.getCounts();
    let animationFrameEndScreenshot = {
      index: index,
      width: width,
      height: height,
      scaling: 1,
      flipped: flipped,
      pixels: pixels.subarray(0, width * height * 4)
    };

    // Wrap the function calls and screenshot in a FrameSnapshotActor instance,
    // which will resolve the promise returned by `recordAnimationFrame`.
    let frameSnapshot = new FrameSnapshotActor(this.conn, {
      canvas: this._lastDrawCallCanvas,
      calls: functionCalls,
      screenshot: animationFrameEndScreenshot,
      primitive: {
        tris: primitiveResult.tris,
        vertices: primitiveResult.vertices,
        points: primitiveResult.points,
        lines: primitiveResult.lines
      }
    });

    this._currentAnimationFrameSnapshot.resolve(frameSnapshot);
    this._currentAnimationFrameSnapshot = null;
    this._animationStarted = false;
  },

  /**
   * Invoked whenever a draw call is detected in the animation frame which is
   * currently being recorded.
   */
  _handleDrawCall: function (functionCall) {
    let functionCalls = this._callWatcher.pauseRecording();
    let caller = functionCall.details.caller;
    let global = functionCall.details.global;

    let contentCanvas = this._lastDrawCallCanvas = caller.canvas;
    let index = this._lastDrawCallIndex = functionCalls.indexOf(functionCall);
    let w = this._lastContentCanvasWidth = contentCanvas.width;
    let h = this._lastContentCanvasHeight = contentCanvas.height;

    // To keep things fast, generate images of small and fixed dimensions.
    let dimensions = CanvasFront.THUMBNAIL_SIZE;
    let thumbnail;

    this._animationContainsDrawCall = true;

    // Create a thumbnail on every draw call on the canvas context, to augment
    // the respective function call actor with this additional data.
    if (global == "WebGLRenderingContext") {
      // Check if drawing to a custom framebuffer (when rendering to texture).
      // Don't create a thumbnail in this particular case.
      let framebufferBinding = caller.getParameter(caller.FRAMEBUFFER_BINDING);
      if (framebufferBinding == null) {
        thumbnail = ContextUtils.getPixelsForWebGL(caller, 0, 0, w, h, dimensions);
        thumbnail.flipped = this._lastThumbnailFlipped = true;
        thumbnail.index = index;
      }
    } else if (global == "CanvasRenderingContext2D") {
      thumbnail = ContextUtils.getPixelsFor2D(caller, 0, 0, w, h, dimensions);
      thumbnail.flipped = this._lastThumbnailFlipped = false;
      thumbnail.index = index;
    }

    functionCall._thumbnail = thumbnail;
    this._callWatcher.resumeRecording();
  }
});

/**
 * A collection of methods for manipulating canvas contexts.
 */
var ContextUtils = {
  /**
   * WebGL contexts are sensitive to how they're queried. Use this function
   * to make sure the right context is always retrieved, if available.
   *
   * @param HTMLCanvasElement canvas
   *        The canvas element for which to get a WebGL context.
   * @param WebGLRenderingContext gl
   *        The queried WebGL context, or null if unavailable.
   */
  getWebGLContext: function (canvas) {
    return canvas.getContext("webgl") ||
           canvas.getContext("experimental-webgl");
  },

  /**
   * Gets a hold of the rendered pixels in the most efficient way possible for
   * a canvas with a WebGL context.
   *
   * @param WebGLRenderingContext gl
   *        The WebGL context to get a screenshot from.
   * @param number srcX [optional]
   *        The first left pixel that is read from the framebuffer.
   * @param number srcY [optional]
   *        The first top pixel that is read from the framebuffer.
   * @param number srcWidth [optional]
   *        The number of pixels to read on the X axis.
   * @param number srcHeight [optional]
   *        The number of pixels to read on the Y axis.
   * @param number dstHeight [optional]
   *        The desired generated screenshot height.
   * @return object
   *         An objet containing the screenshot's width, height and pixel data,
   *         represented as an 8-bit array buffer of r, g, b, a values.
   */
  getPixelsForWebGL: function (gl,
    srcX = 0, srcY = 0,
    srcWidth = gl.canvas.width,
    srcHeight = gl.canvas.height,
    dstHeight = srcHeight
  ) {
    let contentPixels = ContextUtils.getPixelStorage(srcWidth, srcHeight);
    let { "8bit": charView, "32bit": intView } = contentPixels;
    gl.readPixels(srcX, srcY, srcWidth, srcHeight, gl.RGBA, gl.UNSIGNED_BYTE, charView);
    return this.resizePixels(intView, srcWidth, srcHeight, dstHeight);
  },

  /**
   * Gets a hold of the rendered pixels in the most efficient way possible for
   * a canvas with a 2D context.
   *
   * @param CanvasRenderingContext2D ctx
   *        The 2D context to get a screenshot from.
   * @param number srcX [optional]
   *        The first left pixel that is read from the canvas.
   * @param number srcY [optional]
   *        The first top pixel that is read from the canvas.
   * @param number srcWidth [optional]
   *        The number of pixels to read on the X axis.
   * @param number srcHeight [optional]
   *        The number of pixels to read on the Y axis.
   * @param number dstHeight [optional]
   *        The desired generated screenshot height.
   * @return object
   *         An objet containing the screenshot's width, height and pixel data,
   *         represented as an 8-bit array buffer of r, g, b, a values.
   */
  getPixelsFor2D: function (ctx,
    srcX = 0, srcY = 0,
    srcWidth = ctx.canvas.width,
    srcHeight = ctx.canvas.height,
    dstHeight = srcHeight
  ) {
    let { data } = ctx.getImageData(srcX, srcY, srcWidth, srcHeight);
    let { "32bit": intView } = ContextUtils.usePixelStorage(data.buffer);
    return this.resizePixels(intView, srcWidth, srcHeight, dstHeight);
  },

  /**
   * Resizes the provided pixels to fit inside a rectangle with the specified
   * height and the same aspect ratio as the source.
   *
   * @param Uint32Array srcPixels
   *        The source pixel data, assuming 32bit/pixel and 4 color components.
   * @param number srcWidth
   *        The source pixel data width.
   * @param number srcHeight
   *        The source pixel data height.
   * @param number dstHeight [optional]
   *        The desired resized pixel data height.
   * @return object
   *         An objet containing the resized pixels width, height and data,
   *         represented as an 8-bit array buffer of r, g, b, a values.
   */
  resizePixels: function (srcPixels, srcWidth, srcHeight, dstHeight) {
    let screenshotRatio = dstHeight / srcHeight;
    let dstWidth = (srcWidth * screenshotRatio) | 0;
    let dstPixels = new Uint32Array(dstWidth * dstHeight);

    // If the resized image ends up being completely transparent, returning
    // an empty array will skip some redundant serialization cycles.
    let isTransparent = true;

    for (let dstX = 0; dstX < dstWidth; dstX++) {
      for (let dstY = 0; dstY < dstHeight; dstY++) {
        let srcX = (dstX / screenshotRatio) | 0;
        let srcY = (dstY / screenshotRatio) | 0;
        let cPos = srcX + srcWidth * srcY;
        let dPos = dstX + dstWidth * dstY;
        let color = dstPixels[dPos] = srcPixels[cPos];
        if (color) {
          isTransparent = false;
        }
      }
    }

    return {
      width: dstWidth,
      height: dstHeight,
      pixels: isTransparent ? [] : new Uint8Array(dstPixels.buffer)
    };
  },

  /**
   * Invokes a series of canvas context calls, to "replay" an animation frame
   * and generate a screenshot.
   *
   * In case of a WebGL context, an offscreen framebuffer is created for
   * the respective canvas, and the rendering will be performed into it.
   * This is necessary because some state (like shaders, textures etc.) can't
   * be shared between two different WebGL contexts.
   *   - Hopefully, once SharedResources are a thing this won't be necessary:
   *     http://www.khronos.org/webgl/wiki/SharedResouces
   *   - Alternatively, we could pursue the idea of using the same context
   *     for multiple canvases, instead of trying to share resources:
   *     https://www.khronos.org/webgl/public-mailing-list/archives/1210/msg00058.html
   *
   * In case of a 2D context, a new canvas is created, since there's no
   * intrinsic state that can't be easily duplicated.
   *
   * @param number contexType
   *        The type of context to use. See the CallWatcherFront scope types.
   * @param HTMLCanvasElement canvas
   *        The canvas element which is the source of all context calls.
   * @param array calls
   *        An array of function call actors.
   * @param number first
   *        The first function call to start from.
   * @param number last
   *        The last (inclusive) function call to end at.
   * @return object
   *         The context on which the specified calls were invoked, the
   *         last registered draw call's index and a cleanup function, which
   *         needs to be called whenever any potential followup work is finished.
   */
  replayAnimationFrame: function ({ contextType, canvas, calls, first, last }) {
    let w = canvas.width;
    let h = canvas.height;

    let replayContext;
    let replayContextScaling;
    let customViewport;
    let customFramebuffer;
    let lastDrawCallIndex = -1;
    let doCleanup = () => {};

    // In case of WebGL contexts, rendering will be done offscreen, in a
    // custom framebuffer, but using the same provided context. This is
    // necessary because it's very memory-unfriendly to rebuild all the
    // required GL state (like recompiling shaders, setting global flags, etc.)
    // in an entirely new canvas. However, special care is needed to not
    // permanently affect the existing GL state in the process.
    if (contextType == "WebGLRenderingContext") {
      // To keep things fast, replay the context calls on a framebuffer
      // of smaller dimensions than the actual canvas (maximum 256x256 pixels).
      let scaling = Math.min(CanvasFront.WEBGL_SCREENSHOT_MAX_HEIGHT, h) / h;
      replayContextScaling = scaling;
      w = (w * scaling) | 0;
      h = (h * scaling) | 0;

      // Fetch the same WebGL context and bind a new framebuffer.
      let gl = replayContext = this.getWebGLContext(canvas);
      let { newFramebuffer, oldFramebuffer } = this.createBoundFramebuffer(gl, w, h);
      customFramebuffer = newFramebuffer;

      // Set the viewport to match the new framebuffer's dimensions.
      let { newViewport, oldViewport } = this.setCustomViewport(gl, w, h);
      customViewport = newViewport;

      // Revert the framebuffer and viewport to the original values.
      doCleanup = () => {
        gl.bindFramebuffer(gl.FRAMEBUFFER, oldFramebuffer);
        gl.viewport.apply(gl, oldViewport);
      };
    } else if (contextType == "CanvasRenderingContext2D") {
      // In case of 2D contexts, draw everything on a separate canvas context.
      let contentDocument = canvas.ownerDocument;
      let replayCanvas = contentDocument.createElement("canvas");
      replayCanvas.width = w;
      replayCanvas.height = h;
      replayContext = replayCanvas.getContext("2d");
      replayContextScaling = 1;
      customViewport = [0, 0, w, h];
    }

    // Replay all the context calls up to and including the specified one.
    for (let i = first; i <= last; i++) {
      let { type, name, args } = calls[i].details;

      // Prevent WebGL context calls that try to reset the framebuffer binding
      // to the default value, since we want to perform the rendering offscreen.
      if (name == "bindFramebuffer" && args[1] == null) {
        replayContext.bindFramebuffer(replayContext.FRAMEBUFFER, customFramebuffer);
        continue;
      }
      // Also prevent WebGL context calls that try to change the viewport
      // while our custom framebuffer is bound.
      if (name == "viewport") {
        let framebufferBinding = replayContext.getParameter(
          replayContext.FRAMEBUFFER_BINDING);
        if (framebufferBinding == customFramebuffer) {
          replayContext.viewport.apply(replayContext, customViewport);
          continue;
        }
      }
      if (type == CallWatcherFront.METHOD_FUNCTION) {
        replayContext[name].apply(replayContext, args);
      } else if (type == CallWatcherFront.SETTER_FUNCTION) {
        replayContext[name] = args;
      }
      if (CanvasFront.DRAW_CALLS.has(name)) {
        lastDrawCallIndex = i;
      }
    }

    return {
      replayContext: replayContext,
      replayContextScaling: replayContextScaling,
      replayViewport: customViewport,
      lastDrawCallIndex: lastDrawCallIndex,
      doCleanup: doCleanup
    };
  },

  /**
   * Gets an object containing a buffer large enough to hold width * height
   * pixels, assuming 32bit/pixel and 4 color components.
   *
   * This method avoids allocating memory and tries to reuse a common buffer
   * as much as possible.
   *
   * @param number w
   *        The desired pixel array storage width.
   * @param number h
   *        The desired pixel array storage height.
   * @return object
   *         The requested pixel array buffer.
   */
  getPixelStorage: function (w = 0, h = 0) {
    let storage = this._currentPixelStorage;
    if (storage && storage["32bit"].length >= w * h) {
      return storage;
    }
    return this.usePixelStorage(new ArrayBuffer(w * h * 4));
  },

  /**
   * Creates and saves the array buffer views used by `getPixelStorage`.
   *
   * @param ArrayBuffer buffer
   *        The raw buffer used as storage for various array buffer views.
   */
  usePixelStorage: function (buffer) {
    let array8bit = new Uint8Array(buffer);
    let array32bit = new Uint32Array(buffer);
    this._currentPixelStorage = {
      "8bit": array8bit,
      "32bit": array32bit
    };
    return this._currentPixelStorage;
  },

  /**
   * Creates a framebuffer of the specified dimensions for a WebGL context,
   * assuming a RGBA color buffer, a depth buffer and no stencil buffer.
   *
   * @param WebGLRenderingContext gl
   *        The WebGL context to create and bind a framebuffer for.
   * @param number width
   *        The desired width of the renderbuffers.
   * @param number height
   *        The desired height of the renderbuffers.
   * @return WebGLFramebuffer
   *         The generated framebuffer object.
   */
  createBoundFramebuffer: function (gl, width, height) {
    let oldFramebuffer = gl.getParameter(gl.FRAMEBUFFER_BINDING);
    let oldRenderbufferBinding = gl.getParameter(gl.RENDERBUFFER_BINDING);
    let oldTextureBinding = gl.getParameter(gl.TEXTURE_BINDING_2D);

    let newFramebuffer = gl.createFramebuffer();
    gl.bindFramebuffer(gl.FRAMEBUFFER, newFramebuffer);

    // Use a texture as the color renderbuffer attachment, since consumers of
    // this function will most likely want to read the rendered pixels back.
    let colorBuffer = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, colorBuffer);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA,
      gl.UNSIGNED_BYTE, null);

    let depthBuffer = gl.createRenderbuffer();
    gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
    gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, width, height);

    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D,
      colorBuffer, 0);
    gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER,
      depthBuffer);

    gl.bindTexture(gl.TEXTURE_2D, oldTextureBinding);
    gl.bindRenderbuffer(gl.RENDERBUFFER, oldRenderbufferBinding);

    return { oldFramebuffer, newFramebuffer };
  },

  /**
   * Sets the viewport of the drawing buffer for a WebGL context.
   * @param WebGLRenderingContext gl
   * @param number width
   * @param number height
   */
  setCustomViewport: function (gl, width, height) {
    let oldViewport = XPCNativeWrapper.unwrap(gl.getParameter(gl.VIEWPORT));
    let newViewport = [0, 0, width, height];
    gl.viewport.apply(gl, newViewport);

    return { oldViewport, newViewport };
  }
};

/**
 * Goes through all the arguments and creates a one-level shallow copy
 * of all arrays and array buffers.
 */
function inplaceShallowCloneArrays(functionArguments, contentWindow) {
  let { Object, Array, ArrayBuffer } = contentWindow;

  functionArguments.forEach((arg, index, store) => {
    if (arg instanceof Array) {
      store[index] = arg.slice();
    }
    if (arg instanceof Object && arg.buffer instanceof ArrayBuffer) {
      store[index] = new arg.constructor(arg);
    }
  });
}
PK
!<���?��?chrome/devtools/modules/devtools/server/actors/child-process.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Cc, Ci, Cu } = require("chrome");
const Services = require("Services");

const { ChromeDebuggerActor } = require("devtools/server/actors/thread");
const { WebConsoleActor } = require("devtools/server/actors/webconsole");
const makeDebugger = require("devtools/server/actors/utils/make-debugger");
const { ActorPool } = require("devtools/server/main");
const { assert } = require("devtools/shared/DevToolsUtils");
const { TabSources } = require("./utils/TabSources");

loader.lazyRequireGetter(this, "WorkerActorList", "devtools/server/actors/worker-list", true);

function ChildProcessActor(connection) {
  this.conn = connection;
  this._contextPool = new ActorPool(this.conn);
  this.conn.addActorPool(this._contextPool);
  this.threadActor = null;

  // Use a see-everything debugger
  this.makeDebugger = makeDebugger.bind(null, {
    findDebuggees: dbg => dbg.findAllGlobals(),
    shouldAddNewGlobalAsDebuggee: global => true
  });

  let sandboxPrototype = {
    get tabs() {
      let tabs = [];
      let windowEnumerator = Services.ww.getWindowEnumerator();
      while (windowEnumerator.hasMoreElements()) {
        let window = windowEnumerator.getNext().QueryInterface(Ci.nsIDOMWindow);
        let tabChildGlobal = window.QueryInterface(Ci.nsIInterfaceRequestor)
                                   .getInterface(Ci.nsIDocShell)
                                   .sameTypeRootTreeItem
                                   .QueryInterface(Ci.nsIInterfaceRequestor)
                                   .getInterface(Ci.nsIContentFrameMessageManager);
        tabs.push(tabChildGlobal);
      }
      return tabs;
    },
  };

  // Scope into which the webconsole executes:
  // A sandbox with chrome privileges with a `tabs` getter.
  let systemPrincipal = Cc["@mozilla.org/systemprincipal;1"]
    .createInstance(Ci.nsIPrincipal);
  let sandbox = Cu.Sandbox(systemPrincipal, {
    sandboxPrototype,
  });
  this._consoleScope = sandbox;

  this._workerList = null;
  this._workerActorPool = null;
  this._onWorkerListChanged = this._onWorkerListChanged.bind(this);
}
exports.ChildProcessActor = ChildProcessActor;

ChildProcessActor.prototype = {
  actorPrefix: "process",

  get isRootActor() {
    return true;
  },

  get exited() {
    return !this._contextPool;
  },

  get url() {
    return undefined;
  },

  get window() {
    return this._consoleScope;
  },

  get sources() {
    if (!this._sources) {
      assert(this.threadActor, "threadActor should exist when creating sources.");
      this._sources = new TabSources(this.threadActor);
    }
    return this._sources;
  },

  form: function () {
    if (!this._consoleActor) {
      this._consoleActor = new WebConsoleActor(this.conn, this);
      this._contextPool.addActor(this._consoleActor);
    }

    if (!this.threadActor) {
      this.threadActor = new ChromeDebuggerActor(this.conn, this);
      this._contextPool.addActor(this.threadActor);
    }

    return {
      actor: this.actorID,
      name: "Content process",

      consoleActor: this._consoleActor.actorID,
      chromeDebugger: this.threadActor.actorID,

      traits: {
        highlightable: false,
        networkMonitor: false,
      },
    };
  },

  onListWorkers: function () {
    if (!this._workerList) {
      this._workerList = new WorkerActorList(this.conn, {});
    }
    return this._workerList.getList().then(actors => {
      let pool = new ActorPool(this.conn);
      for (let actor of actors) {
        pool.addActor(actor);
      }

      this.conn.removeActorPool(this._workerActorPool);
      this._workerActorPool = pool;
      this.conn.addActorPool(this._workerActorPool);

      this._workerList.onListChanged = this._onWorkerListChanged;

      return {
        "from": this.actorID,
        "workers": actors.map(actor => actor.form())
      };
    });
  },

  _onWorkerListChanged: function () {
    this.conn.send({ from: this.actorID, type: "workerListChanged" });
    this._workerList.onListChanged = null;
  },

  destroy: function () {
    this.conn.removeActorPool(this._contextPool);
    this._contextPool = null;

    // Tell the live lists we aren't watching any more.
    if (this._workerList) {
      this._workerList.onListChanged = null;
    }
  },

  preNest: function () {
    // TODO: freeze windows
    // window mediator doesn't work in child.
    // it doesn't throw, but doesn't return any window
  },

  postNest: function () {
  },
};

ChildProcessActor.prototype.requestTypes = {
  "listWorkers": ChildProcessActor.prototype.onListWorkers,
};
PK
!<��,t��8chrome/devtools/modules/devtools/server/actors/chrome.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Ci } = require("chrome");
const Services = require("Services");
const { DebuggerServer } = require("../main");
const { getChildDocShells, TabActor } = require("./tab");
const makeDebugger = require("./utils/make-debugger");

/**
 * Creates a TabActor for debugging all the chrome content in the
 * current process. Most of the implementation is inherited from TabActor.
 * ChromeActor is a child of RootActor, it can be instanciated via
 * RootActor.getProcess request.
 * ChromeActor exposes all tab actors via its form() request, like TabActor.
 *
 * History lecture:
 * All tab actors used to also be registered as global actors,
 * so that the root actor was also exposing tab actors for the main process.
 * Tab actors ended up having RootActor as parent actor,
 * but more and more features of the tab actors were relying on TabActor.
 * So we are now exposing a process actor that offers the same API as TabActor
 * by inheriting its functionality.
 * Global actors are now only the actors that are meant to be global,
 * and are no longer related to any specific scope/document.
 *
 * @param connection DebuggerServerConnection
 *        The connection to the client.
 */
function ChromeActor(connection) {
  TabActor.call(this, connection);

  // This creates a Debugger instance for chrome debugging all globals.
  this.makeDebugger = makeDebugger.bind(null, {
    findDebuggees: dbg => dbg.findAllGlobals(),
    shouldAddNewGlobalAsDebuggee: () => true
  });

  // Ensure catching the creation of any new content docshell
  this.listenForNewDocShells = true;

  // Defines the default docshell selected for the tab actor
  let window = Services.wm.getMostRecentWindow(DebuggerServer.chromeWindowType);

  // Default to any available top level window if there is no expected window
  // (for example when we open firefox with -webide argument)
  if (!window) {
    window = Services.wm.getMostRecentWindow(null);
  }

  // We really want _some_ window at least, so fallback to the hidden window if
  // there's nothing else (such as during early startup).
  if (!window) {
    try {
      window = Services.appShell.hiddenDOMWindow;
    } catch (e) {
      // On XPCShell, the above line will throw.
    }
  }

  // On XPCShell, there is no window/docshell
  let docShell = window ? window.QueryInterface(Ci.nsIInterfaceRequestor)
                                .getInterface(Ci.nsIDocShell)
                        : null;
  Object.defineProperty(this, "docShell", {
    value: docShell,
    configurable: true
  });
}
exports.ChromeActor = ChromeActor;

ChromeActor.prototype = Object.create(TabActor.prototype);

ChromeActor.prototype.constructor = ChromeActor;

ChromeActor.prototype.isRootActor = true;

/**
 * Getter for the list of all docshells in this tabActor
 * @return {Array}
 */
Object.defineProperty(ChromeActor.prototype, "docShells", {
  get: function () {
    // Iterate over all top-level windows and all their docshells.
    let docShells = [];
    let e = Services.ww.getWindowEnumerator();
    while (e.hasMoreElements()) {
      let window = e.getNext();
      let docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
                           .getInterface(Ci.nsIWebNavigation)
                           .QueryInterface(Ci.nsIDocShell);
      docShells = docShells.concat(getChildDocShells(docShell));
    }

    return docShells;
  }
});

ChromeActor.prototype.observe = function (subject, topic, data) {
  TabActor.prototype.observe.call(this, subject, topic, data);
  if (!this.attached) {
    return;
  }

  subject.QueryInterface(Ci.nsIDocShell);

  if (topic == "chrome-webnavigation-create") {
    this._onDocShellCreated(subject);
  } else if (topic == "chrome-webnavigation-destroy") {
    this._onDocShellDestroy(subject);
  }
};

ChromeActor.prototype._attach = function () {
  if (this.attached) {
    return false;
  }

  TabActor.prototype._attach.call(this);

  // Listen for any new/destroyed chrome docshell
  Services.obs.addObserver(this, "chrome-webnavigation-create");
  Services.obs.addObserver(this, "chrome-webnavigation-destroy");

  // Iterate over all top-level windows.
  let e = Services.ww.getWindowEnumerator();
  while (e.hasMoreElements()) {
    let window = e.getNext();
    let docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsIWebNavigation)
                         .QueryInterface(Ci.nsIDocShell);
    if (docShell == this.docShell) {
      continue;
    }
    this._progressListener.watch(docShell);
  }
  return undefined;
};

ChromeActor.prototype._detach = function () {
  if (!this.attached) {
    return false;
  }

  Services.obs.removeObserver(this, "chrome-webnavigation-create");
  Services.obs.removeObserver(this, "chrome-webnavigation-destroy");

  // Iterate over all top-level windows.
  let e = Services.ww.getWindowEnumerator();
  while (e.hasMoreElements()) {
    let window = e.getNext();
    let docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsIWebNavigation)
                         .QueryInterface(Ci.nsIDocShell);
    if (docShell == this.docShell) {
      continue;
    }
    this._progressListener.unwatch(docShell);
  }

  TabActor.prototype._detach.call(this);
  return undefined;
};

/* ThreadActor hooks. */

/**
 * Prepare to enter a nested event loop by disabling debuggee events.
 */
ChromeActor.prototype.preNest = function () {
  // Disable events in all open windows.
  let e = Services.wm.getEnumerator(null);
  while (e.hasMoreElements()) {
    let win = e.getNext();
    let windowUtils = win.QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsIDOMWindowUtils);
    windowUtils.suppressEventHandling(true);
    windowUtils.suspendTimeouts();
  }
};

/**
 * Prepare to exit a nested event loop by enabling debuggee events.
 */
ChromeActor.prototype.postNest = function (nestData) {
  // Enable events in all open windows.
  let e = Services.wm.getEnumerator(null);
  while (e.hasMoreElements()) {
    let win = e.getNext();
    let windowUtils = win.QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsIDOMWindowUtils);
    windowUtils.resumeTimeouts();
    windowUtils.suppressEventHandling(false);
  }
};
PK
!<�Aw�A�A8chrome/devtools/modules/devtools/server/actors/common.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 { method } = require("devtools/shared/protocol");

/**
 * Creates "registered" actors factory meant for creating another kind of
 * factories, ObservedActorFactory, during the call to listTabs.
 * These factories live in DebuggerServer.{tab|global}ActorFactories.
 *
 * These actors only exposes:
 * - `name` string attribute used to match actors by constructor name
 *   in DebuggerServer.remove{Global,Tab}Actor.
 * - `createObservedActorFactory` function to create "observed" actors factory
 *
 * @param options object, function
 *        Either an object or a function.
 *        If given an object:
 *
 *        If given a function (deprecated):
 *          Constructor function of an actor.
 *          The constructor function for this actor type.
 *          This expects to be called as a constructor (i.e. with 'new'),
 *          and passed two arguments: the DebuggerServerConnection, and
 *          the BrowserTabActor with which it will be associated.
 *          Only used for deprecated eagerly loaded actors.
 *
 */
function RegisteredActorFactory(options, prefix) {
  // By default the actor name will also be used for the actorID prefix.
  this._prefix = prefix;
  if (typeof (options) != "function") {
    // actors definition registered by actorRegistryActor
    if (options.constructorFun) {
      this._getConstructor = () => options.constructorFun;
    } else {
      // Lazy actor definition, where options contains all the information
      // required to load the actor lazily.
      this._getConstructor = function () {
        // Load the module
        let mod;
        try {
          mod = require(options.id);
        } catch (e) {
          throw new Error("Unable to load actor module '" + options.id + "'.\n" +
                          e.message + "\n" + e.stack + "\n");
        }
        // Fetch the actor constructor
        let c = mod[options.constructorName];
        if (!c) {
          throw new Error("Unable to find actor constructor named '" +
                          options.constructorName + "'. (Is it exported?)");
        }
        return c;
      };
    }
    // Exposes `name` attribute in order to allow removeXXXActor to match
    // the actor by its actor constructor name.
    this.name = options.constructorName;
  } else {
    // Old actor case, where options is a function that is the actor constructor.
    this._getConstructor = () => options;
    // Exposes `name` attribute in order to allow removeXXXActor to match
    // the actor by its actor constructor name.
    this.name = options.name;

    // For old actors, we allow the use of a different prefix for actorID
    // than for listTabs actor names, by fetching a prefix on the actor prototype.
    // (Used by ChromeDebuggerActor)
    if (options.prototype && options.prototype.actorPrefix) {
      this._prefix = options.prototype.actorPrefix;
    }
  }
}
RegisteredActorFactory.prototype.createObservedActorFactory = function (conn,
  parentActor) {
  return new ObservedActorFactory(this._getConstructor, this._prefix, conn, parentActor);
};
exports.RegisteredActorFactory = RegisteredActorFactory;

/**
 * Creates "observed" actors factory meant for creating real actor instances.
 * These factories lives in actor pools and fake various actor attributes.
 * They will be replaced in actor pools by final actor instances during
 * the first request for the same actorID from DebuggerServer._getOrCreateActor.
 *
 * ObservedActorFactory fakes the following actors attributes:
 *   actorPrefix (string) Used by ActorPool.addActor to compute the actor id
 *   actorID (string) Set by ActorPool.addActor just after being instantiated
 *   registeredPool (object) Set by ActorPool.addActor just after being
 *                           instantiated
 * And exposes the following method:
 *   createActor (function) Instantiate an actor that is going to replace
 *                          this factory in the actor pool.
 */
function ObservedActorFactory(getConstructor, prefix, conn, parentActor) {
  this._getConstructor = getConstructor;
  this._conn = conn;
  this._parentActor = parentActor;

  this.actorPrefix = prefix;

  this.actorID = null;
  this.registeredPool = null;
}
ObservedActorFactory.prototype.createActor = function () {
  // Fetch the actor constructor
  let C = this._getConstructor();
  // Instantiate a new actor instance
  let instance = new C(this._conn, this._parentActor);
  instance.conn = this._conn;
  instance.parentID = this._parentActor.actorID;
  // We want the newly-constructed actor to completely replace the factory
  // actor. Reusing the existing actor ID will make sure ActorPool.addActor
  // does the right thing.
  instance.actorID = this.actorID;
  this.registeredPool.addActor(instance);
  return instance;
};
exports.ObservedActorFactory = ObservedActorFactory;

/**
 * Methods shared between RootActor and BrowserTabActor.
 */

/**
 * Populate |this._extraActors| as specified by |factories|, reusing whatever
 * actors are already there. Add all actors in the final extra actors table to
 * |pool|.
 *
 * The root actor and the tab actor use this to instantiate actors that other
 * parts of the browser have specified with DebuggerServer.addTabActor and
 * DebuggerServer.addGlobalActor.
 *
 * @param factories
 *     An object whose own property names are the names of properties to add to
 *     some reply packet (say, a tab actor grip or the "listTabs" response
 *     form), and whose own property values are actor constructor functions, as
 *     documented for addTabActor and addGlobalActor.
 *
 * @param this
 *     The BrowserRootActor or BrowserTabActor with which the new actors will
 *     be associated. It should support whatever API the |factories|
 *     constructor functions might be interested in, as it is passed to them.
 *     For the sake of CommonCreateExtraActors itself, it should have at least
 *     the following properties:
 *
 *     - _extraActors
 *        An object whose own property names are factory table (and packet)
 *        property names, and whose values are no-argument actor constructors,
 *        of the sort that one can add to an ActorPool.
 *
 *     - conn
 *        The DebuggerServerConnection in which the new actors will participate.
 *
 *     - actorID
 *        The actor's name, for use as the new actors' parentID.
 */
exports.createExtraActors = function createExtraActors(factories, pool) {
  // Walk over global actors added by extensions.
  for (let name in factories) {
    let actor = this._extraActors[name];
    if (!actor) {
      // Register another factory, but this time specific to this connection.
      // It creates a fake actor that looks like an regular actor in the pool,
      // but without actually instantiating the actor.
      // It will only be instantiated on the first request made to the actor.
      actor = factories[name].createObservedActorFactory(this.conn, this);
      this._extraActors[name] = actor;
    }

    // If the actor already exists in the pool, it may have been instantiated,
    // so make sure not to overwrite it by a non-instantiated version.
    if (!pool.has(actor.actorID)) {
      pool.addActor(actor);
    }
  }
};

/**
 * Append the extra actors in |this._extraActors|, constructed by a prior call
 * to CommonCreateExtraActors, to |object|.
 *
 * @param object
 *     The object to which the extra actors should be added, under the
 *     property names given in the |factories| table passed to
 *     CommonCreateExtraActors.
 *
 * @param this
 *     The BrowserRootActor or BrowserTabActor whose |_extraActors| table we
 *     should use; see above.
 */
exports.appendExtraActors = function appendExtraActors(object) {
  for (let name in this._extraActors) {
    let actor = this._extraActors[name];
    object[name] = actor.actorID;
  }
};

/**
 * Construct an ActorPool.
 *
 * ActorPools are actorID -> actor mapping and storage.  These are
 * used to accumulate and quickly dispose of groups of actors that
 * share a lifetime.
 */
function ActorPool(connection) {
  this.conn = connection;
  this._actors = {};
}

ActorPool.prototype = {
  /**
   * Destroy the pool. This will remove all actors from the pool.
   */
  destroy: function APDestroy() {
    for (let id in this._actors) {
      this.removeActor(this._actors[id]);
    }
  },

  /**
   * Add an actor to the pool. If the actor doesn't have an ID, allocate one
   * from the connection.
   *
   * @param Object actor
   *        The actor to be added to the pool.
   */
  addActor: function APAddActor(actor) {
    actor.conn = this.conn;
    if (!actor.actorID) {
      // Older style actors use actorPrefix, while protocol.js-based actors use typeName
      let prefix = actor.actorPrefix || actor.typeName;
      if (!prefix && typeof actor == "function") {
        prefix = actor.prototype.actorPrefix || actor.prototype.typeName;
      }
      actor.actorID = this.conn.allocID(prefix || undefined);
    }

    // If the actor is already in a pool, remove it without destroying it.
    if (actor.registeredPool) {
      delete actor.registeredPool._actors[actor.actorID];
    }
    actor.registeredPool = this;

    this._actors[actor.actorID] = actor;
  },

  /**
   * Remove an actor from the pool. If the actor has a destroy method, call it.
   */
  removeActor(actor) {
    delete this._actors[actor.actorID];
    if (actor.destroy) {
      actor.destroy();
      return;
    }
    // Obsolete destruction method name (might still be used by custom actors)
    if (actor.disconnect) {
      actor.disconnect();
    }
  },

  get: function APGet(actorID) {
    return this._actors[actorID] || undefined;
  },

  has: function APHas(actorID) {
    return actorID in this._actors;
  },

  /**
   * Returns true if the pool is empty.
   */
  isEmpty: function APIsEmpty() {
    return Object.keys(this._actors).length == 0;
  },

  /**
   * Match the api expected by the protocol library.
   */
  unmanage: function (actor) {
    return this.removeActor(actor);
  },

  forEach: function (callback) {
    for (let name in this._actors) {
      callback(this._actors[name]);
    }
  },
};

exports.ActorPool = ActorPool;

/**
 * An OriginalLocation represents a location in an original source.
 *
 * @param SourceActor actor
 *        A SourceActor representing an original source.
 * @param Number line
 *        A line within the given source.
 * @param Number column
 *        A column within the given line.
 * @param String name
 *        The name of the symbol corresponding to this OriginalLocation.
 */
function OriginalLocation(actor, line, column, name) {
  this._connection = actor ? actor.conn : null;
  this._actorID = actor ? actor.actorID : undefined;
  this._line = line;
  this._column = column;
  this._name = name;
}

OriginalLocation.fromGeneratedLocation = function (generatedLocation) {
  return new OriginalLocation(
    generatedLocation.generatedSourceActor,
    generatedLocation.generatedLine,
    generatedLocation.generatedColumn
  );
};

OriginalLocation.prototype = {
  get originalSourceActor() {
    return this._connection ? this._connection.getActor(this._actorID) : null;
  },

  get originalUrl() {
    let actor = this.originalSourceActor;
    let source = actor.source;
    return source ? source.url : actor._originalUrl;
  },

  get originalLine() {
    return this._line;
  },

  get originalColumn() {
    return this._column;
  },

  get originalName() {
    return this._name;
  },

  get generatedSourceActor() {
    throw new Error("Shouldn't  access generatedSourceActor from an OriginalLocation");
  },

  get generatedLine() {
    throw new Error("Shouldn't access generatedLine from an OriginalLocation");
  },

  get generatedColumn() {
    throw new Error("Shouldn't access generatedColumn from an Originallocation");
  },

  equals: function (other) {
    return this.originalSourceActor.url == other.originalSourceActor.url &&
           this.originalLine === other.originalLine &&
           (this.originalColumn === undefined ||
            other.originalColumn === undefined ||
            this.originalColumn === other.originalColumn);
  },

  toJSON: function () {
    return {
      source: this.originalSourceActor.form(),
      line: this.originalLine,
      column: this.originalColumn
    };
  }
};

exports.OriginalLocation = OriginalLocation;

/**
 * A GeneratedLocation represents a location in a generated source.
 *
 * @param SourceActor actor
 *        A SourceActor representing a generated source.
 * @param Number line
 *        A line within the given source.
 * @param Number column
 *        A column within the given line.
 */
function GeneratedLocation(actor, line, column, lastColumn) {
  this._connection = actor ? actor.conn : null;
  this._actorID = actor ? actor.actorID : undefined;
  this._line = line;
  this._column = column;
  this._lastColumn = (lastColumn !== undefined) ? lastColumn : column + 1;
}

GeneratedLocation.fromOriginalLocation = function (originalLocation) {
  return new GeneratedLocation(
    originalLocation.originalSourceActor,
    originalLocation.originalLine,
    originalLocation.originalColumn
  );
};

GeneratedLocation.prototype = {
  get originalSourceActor() {
    throw new Error();
  },

  get originalUrl() {
    throw new Error("Shouldn't access originalUrl from a GeneratedLocation");
  },

  get originalLine() {
    throw new Error("Shouldn't access originalLine from a GeneratedLocation");
  },

  get originalColumn() {
    throw new Error("Shouldn't access originalColumn from a GeneratedLocation");
  },

  get originalName() {
    throw new Error("Shouldn't access originalName from a GeneratedLocation");
  },

  get generatedSourceActor() {
    return this._connection ? this._connection.getActor(this._actorID) : null;
  },

  get generatedLine() {
    return this._line;
  },

  get generatedColumn() {
    return this._column;
  },

  get generatedLastColumn() {
    return this._lastColumn;
  },

  equals: function (other) {
    return this.generatedSourceActor.url == other.generatedSourceActor.url &&
           this.generatedLine === other.generatedLine &&
           (this.generatedColumn === undefined ||
            other.generatedColumn === undefined ||
            this.generatedColumn === other.generatedColumn);
  },

  toJSON: function () {
    return {
      source: this.generatedSourceActor.form(),
      line: this.generatedLine,
      column: this.generatedColumn,
      lastColumn: this.generatedLastColumn
    };
  }
};

exports.GeneratedLocation = GeneratedLocation;

/**
 * A method decorator that ensures the actor is in the expected state before
 * proceeding. If the actor is not in the expected state, the decorated method
 * returns a rejected promise.
 *
 * The actor's state must be at this.state property.
 *
 * @param String expectedState
 *        The expected state.
 * @param String activity
 *        Additional info about what's going on.
 * @param Function methodFunc
 *        The actor method to proceed with when the actor is in the expected
 *        state.
 *
 * @returns Function
 *          The decorated method.
 */
function expectState(expectedState, methodFunc, activity) {
  return function (...args) {
    if (this.state !== expectedState) {
      const msg = `Wrong state while ${activity}:` +
                  `Expected '${expectedState}', ` +
                  `but current state is '${this.state}'.`;
      return Promise.reject(new Error(msg));
    }

    return methodFunc.apply(this, args);
  };
}

exports.expectState = expectState;

/**
 * Proxies a call from an actor to an underlying module, stored
 * as `bridge` on the actor. This allows a module to be defined in one
 * place, usable by other modules/actors on the server, but a separate
 * module defining the actor/RDP definition.
 *
 * @see Framerate implementation: devtools/server/performance/framerate.js
 * @see Framerate actor definition: devtools/server/actors/framerate.js
 */
function actorBridge(methodName, definition = {}) {
  return method(function () {
    return this.bridge[methodName].apply(this.bridge, arguments);
  }, definition);
}
exports.actorBridge = actorBridge;

/**
 * Like `actorBridge`, but without a spec definition, for when the actor is
 * created with `ActorClassWithSpec` rather than vanilla `ActorClass`.
 */
function actorBridgeWithSpec(methodName) {
  return method(function () {
    return this.bridge[methodName].apply(this.bridge, arguments);
  });
}
exports.actorBridgeWithSpec = actorBridgeWithSpec;
PK
!<0��0
0
9chrome/devtools/modules/devtools/server/actors/content.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var { Cr } = require("chrome");
var { TabActor } = require("devtools/server/actors/tab");

/**
 * Tab actor for documents living in a child process.
 *
 * Depends on TabActor, defined in tab.js.
 */

/**
 * Creates a tab actor for handling requests to the single tab, like
 * attaching and detaching. ContentActor respects the actor factories
 * registered with DebuggerServer.addTabActor.
 *
 * @param connection DebuggerServerConnection
 *        The conection to the client.
 * @param chromeGlobal
 *        The content script global holding |content| and |docShell| properties for a tab.
 * @param prefix
 *        the prefix used in protocol to create IDs for each actor.
 *        Used as ID identifying this particular TabActor from the parent process.
 */
function ContentActor(connection, chromeGlobal, prefix) {
  this._chromeGlobal = chromeGlobal;
  this._prefix = prefix;
  TabActor.call(this, connection, chromeGlobal);
  this.traits.reconfigure = false;
  this._sendForm = this._sendForm.bind(this);
  this._chromeGlobal.addMessageListener("debug:form", this._sendForm);

  Object.defineProperty(this, "docShell", {
    value: this._chromeGlobal.docShell,
    configurable: true
  });
}

ContentActor.prototype = Object.create(TabActor.prototype);

ContentActor.prototype.constructor = ContentActor;

Object.defineProperty(ContentActor.prototype, "title", {
  get: function () {
    return this.window.document.title;
  },
  enumerable: true,
  configurable: true
});

ContentActor.prototype.exit = function () {
  if (this._sendForm) {
    try {
      this._chromeGlobal.removeMessageListener("debug:form", this._sendForm);
    } catch (e) {
      if (e.result != Cr.NS_ERROR_NULL_POINTER) {
        throw e;
      }
      // In some cases, especially when using messageManagers in non-e10s mode, we reach
      // this point with a dead messageManager which only throws errors but does not
      // seem to indicate in any other way that it is dead.
    }
    this._sendForm = null;
  }

  TabActor.prototype.exit.call(this);

  this._chromeGlobal = null;
};

/**
 * On navigation events, our URL and/or title may change, so we update our
 * counterpart in the parent process that participates in the tab list.
 */
ContentActor.prototype._sendForm = function () {
  this._chromeGlobal.sendAsyncMessage("debug:form", this.form());
};

exports.ContentActor = ContentActor;
PK
!<��|
|
@chrome/devtools/modules/devtools/server/actors/css-properties.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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, "CSS_TYPES",
  "devtools/shared/css/properties-db", true);

const protocol = require("devtools/shared/protocol");
const { ActorClassWithSpec, Actor } = protocol;
const { cssPropertiesSpec } = require("devtools/shared/specs/css-properties");
const { cssColors } = require("devtools/shared/css/color-db");
const InspectorUtils = require("InspectorUtils");

exports.CssPropertiesActor = ActorClassWithSpec(cssPropertiesSpec, {
  typeName: "cssProperties",

  initialize(conn) {
    Actor.prototype.initialize.call(this, conn);
  },

  destroy() {
    Actor.prototype.destroy.call(this);
  },

  getCSSDatabase() {
    const properties = generateCssProperties();
    const pseudoElements = InspectorUtils.getCSSPseudoElementNames();
    const supportedFeature = {
      // checking for css-color-4 color function support.
      "css-color-4-color-function": InspectorUtils.isValidCSSColor("rgb(1 1 1 / 100%)"),
    };

    return { properties, pseudoElements, supportedFeature };
  }
});

/**
 * Generate the CSS properties object. Every key is the property name, while
 * the values are objects that contain information about that property.
 *
 * @return {Object}
 */
function generateCssProperties() {
  const properties = {};
  const propertyNames = InspectorUtils.getCSSPropertyNames({ includeAliases: true });
  const colors = Object.keys(cssColors);

  propertyNames.forEach(name => {
    // Get the list of CSS types this property supports.
    let supports = [];
    for (let type in CSS_TYPES) {
      if (safeCssPropertySupportsType(name, InspectorUtils["TYPE_" + type])) {
        supports.push(CSS_TYPES[type]);
      }
    }

    // Don't send colors over RDP, these will be re-attached by the front.
    let values = InspectorUtils.getCSSValuesForProperty(name);
    if (values.includes("aliceblue")) {
      values = values.filter(x => !colors.includes(x));
      values.unshift("COLOR");
    }

    let subproperties = InspectorUtils.getSubpropertiesForCSSProperty(name);

    properties[name] = {
      isInherited: InspectorUtils.isInheritedProperty(name),
      values,
      supports,
      subproperties,
    };
  });

  return properties;
}
exports.generateCssProperties = generateCssProperties;

/**
 * Test if a CSS is property is known using server-code.
 *
 * @param {string} name
 * @return {Boolean}
 */
function isCssPropertyKnown(name) {
  try {
    // If the property name is unknown, the cssPropertyIsShorthand
    // will throw an exception.  But if it is known, no exception will
    // be thrown; so we just ignore the return value.
    InspectorUtils.cssPropertyIsShorthand(name);
    return true;
  } catch (e) {
    return false;
  }
}

exports.isCssPropertyKnown = isCssPropertyKnown;

/**
 * A wrapper for InspectorUtils.cssPropertySupportsType that ignores invalid
 * properties.
 *
 * @param {String} name The property name.
 * @param {number} type The type tested for support.
 * @return {Boolean} Whether the property supports the type.
 *        If the property is unknown, false is returned.
 */
function safeCssPropertySupportsType(name, type) {
  try {
    return InspectorUtils.cssPropertySupportsType(name, type);
  } catch (e) {
    return false;
  }
}
PK
!<�.�ļU�U=chrome/devtools/modules/devtools/server/actors/csscoverage.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Ci } = require("chrome");

const InspectorUtils = require("InspectorUtils");
const Services = require("Services");
const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");

const protocol = require("devtools/shared/protocol");
const { cssUsageSpec } = require("devtools/shared/specs/csscoverage");

loader.lazyRequireGetter(this, "stylesheets", "devtools/server/actors/stylesheets");
loader.lazyRequireGetter(this, "prettifyCSS", "devtools/shared/inspector/css-logic", true);

const MAX_UNUSED_RULES = 10000;

/**
 * Allow: let foo = l10n.lookup("csscoverageFoo");
 */
const l10n = exports.l10n = {
  _URI: "chrome://devtools-shared/locale/csscoverage.properties",
  lookup: function (msg) {
    if (this._stringBundle == null) {
      this._stringBundle = Services.strings.createBundle(this._URI);
    }
    return this._stringBundle.GetStringFromName(msg);
  }
};

/**
 * CSSUsage manages the collection of CSS usage data.
 * The core of a CSSUsage is a JSON-able data structure called _knownRules
 * which looks like this:
 * This records the CSSStyleRules and their usage.
 * The format is:
 *     Map({
 *       <CSS-URL>|<START-LINE>|<START-COLUMN>: {
 *         selectorText: <CSSStyleRule.selectorText>,
 *         test: <simplify(CSSStyleRule.selectorText)>,
 *         cssText: <CSSStyleRule.cssText>,
 *         isUsed: <TRUE|FALSE>,
 *         presentOn: Set([ <HTML-URL>, ... ]),
 *         preLoadOn: Set([ <HTML-URL>, ... ]),
 *         isError: <TRUE|FALSE>,
 *       }
 *     })
 *
 * For example:
 *     this._knownRules = Map({
 *       "http://eg.com/styles1.css|15|0": {
 *         selectorText: "p.quote:hover",
 *         test: "p.quote",
 *         cssText: "p.quote { color: red; }",
 *         isUsed: true,
 *         presentOn: Set([ "http://eg.com/page1.html", ... ]),
 *         preLoadOn: Set([ "http://eg.com/page1.html" ]),
 *         isError: false,
 *       }, ...
 *     });
 */
var CSSUsageActor = protocol.ActorClassWithSpec(cssUsageSpec, {
  initialize: function (conn, tabActor) {
    protocol.Actor.prototype.initialize.call(this, conn);

    this._tabActor = tabActor;
    this._running = false;

    this._onTabLoad = this._onTabLoad.bind(this);
    this._onChange = this._onChange.bind(this);

    this._notifyOn = Ci.nsIWebProgress.NOTIFY_STATE_ALL;
  },

  destroy: function () {
    this._tabActor = undefined;

    delete this._onTabLoad;
    delete this._onChange;

    protocol.Actor.prototype.destroy.call(this);
  },

  /**
   * Begin recording usage data
   * @param noreload It's best if we start by reloading the current page
   * because that starts the test at a known point, but there could be reasons
   * why we don't want to do that (e.g. the page contains state that will be
   * lost across a reload)
   */
  start: function (noreload) {
    if (this._running) {
      throw new Error(l10n.lookup("csscoverageRunningError"));
    }

    this._isOneShot = false;
    this._visitedPages = new Set();
    this._knownRules = new Map();
    this._running = true;
    this._tooManyUnused = false;

    this._progressListener = {
      QueryInterface: XPCOMUtils.generateQI([ Ci.nsIWebProgressListener,
                                              Ci.nsISupportsWeakReference ]),

      onStateChange: (progress, request, flags, status) => {
        let isStop = flags & Ci.nsIWebProgressListener.STATE_STOP;
        let isWindow = flags & Ci.nsIWebProgressListener.STATE_IS_WINDOW;

        if (isStop && isWindow) {
          this._onTabLoad(progress.DOMWindow.document);
        }
      },

      destroy: () => {}
    };

    this._progress = this._tabActor.docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                                            .getInterface(Ci.nsIWebProgress);
    this._progress.addProgressListener(this._progressListener, this._notifyOn);

    if (noreload) {
      // If we're not starting by reloading the page, then pretend that onload
      // has just happened.
      this._onTabLoad(this._tabActor.window.document);
    } else {
      this._tabActor.window.location.reload();
    }

    this.emit("state-change", { isRunning: true });
  },

  /**
   * Cease recording usage data
   */
  stop: function () {
    if (!this._running) {
      throw new Error(l10n.lookup("csscoverageNotRunningError"));
    }

    this._progress.removeProgressListener(this._progressListener, this._notifyOn);
    this._progress = undefined;

    this._running = false;
    this.emit("state-change", { isRunning: false });
  },

  /**
   * Start/stop recording usage data depending on what we're currently doing.
   */
  toggle: function () {
    return this._running ? this.stop() : this.start();
  },

  /**
   * Running start() quickly followed by stop() does a bunch of unnecessary
   * work, so this cuts all that out
   */
  oneshot: function () {
    if (this._running) {
      throw new Error(l10n.lookup("csscoverageRunningError"));
    }

    this._isOneShot = true;
    this._visitedPages = new Set();
    this._knownRules = new Map();

    this._populateKnownRules(this._tabActor.window.document);
    this._updateUsage(this._tabActor.window.document, false);
  },

  /**
   * Called by the ProgressListener to simulate a "load" event
   */
  _onTabLoad: function (document) {
    this._populateKnownRules(document);
    this._updateUsage(document, true);

    this._observeMutations(document);
  },

  /**
   * Setup a MutationObserver on the current document
   */
  _observeMutations: function (document) {
    let MutationObserver = document.defaultView.MutationObserver;
    let observer = new MutationObserver(mutations => {
      // It's possible that one of the mutations in this list adds a 'use' of
      // a CSS rule, and another takes it away. See Bug 1010189
      this._onChange(document);
    });

    observer.observe(document, {
      attributes: true,
      childList: true,
      characterData: false,
      subtree: true
    });
  },

  /**
   * Event handler for whenever we think the page has changed in a way that
   * means the CSS usage might have changed.
   */
  _onChange: function (document) {
    // Ignore changes pre 'load'
    if (!this._visitedPages.has(getURL(document))) {
      return;
    }
    this._updateUsage(document, false);
  },

  /**
   * Called whenever we think the list of stylesheets might have changed so
   * we can update the list of rules that we should be checking
   */
  _populateKnownRules: function (document) {
    let url = getURL(document);
    this._visitedPages.add(url);
    // Go through all the rules in the current sheets adding them to knownRules
    // if needed and adding the current url to the list of pages they're on
    for (let rule of getAllSelectorRules(document)) {
      let ruleId = ruleToId(rule);
      let ruleData = this._knownRules.get(ruleId);
      if (ruleData == null) {
        ruleData = {
          selectorText: rule.selectorText,
          cssText: rule.cssText,
          test: getTestSelector(rule.selectorText),
          isUsed: false,
          presentOn: new Set(),
          preLoadOn: new Set(),
          isError: false
        };
        this._knownRules.set(ruleId, ruleData);
      }

      ruleData.presentOn.add(url);
    }
  },

  /**
   * Update knownRules with usage information from the current page
   */
  _updateUsage: function (document, isLoad) {
    let qsaCount = 0;

    // Update this._data with matches to say 'used at load time' by sheet X
    let url = getURL(document);

    for (let [ , ruleData ] of this._knownRules) {
      // If it broke before, don't try again selectors don't change
      if (ruleData.isError) {
        continue;
      }

      // If it's used somewhere already, don't bother checking again unless
      // this is a load event in which case we need to add preLoadOn
      if (!isLoad && ruleData.isUsed) {
        continue;
      }

      // Ignore rules that are not present on this page
      if (!ruleData.presentOn.has(url)) {
        continue;
      }

      qsaCount++;
      if (qsaCount > MAX_UNUSED_RULES) {
        console.error("Too many unused rules on " + url + " ");
        this._tooManyUnused = true;
        continue;
      }

      try {
        let match = document.querySelector(ruleData.test);
        if (match != null) {
          ruleData.isUsed = true;
          if (isLoad) {
            ruleData.preLoadOn.add(url);
          }
        }
      } catch (ex) {
        ruleData.isError = true;
      }
    }
  },

  /**
   * Returns a JSONable structure designed to help marking up the style editor,
   * which describes the CSS selector usage.
   * Example:
   *   [
   *     {
   *       selectorText: "p#content",
   *       usage: "unused|used",
   *       start: { line: 3, column: 0 },
   *     },
   *     ...
   *   ]
   */
  createEditorReport: function (url) {
    if (this._knownRules == null) {
      return { reports: [] };
    }

    let reports = [];
    for (let [ruleId, ruleData] of this._knownRules) {
      let { url: ruleUrl, line, column } = deconstructRuleId(ruleId);
      if (ruleUrl !== url || ruleData.isUsed) {
        continue;
      }

      let ruleReport = {
        selectorText: ruleData.selectorText,
        start: { line: line, column: column }
      };

      if (ruleData.end) {
        ruleReport.end = ruleData.end;
      }

      reports.push(ruleReport);
    }

    return { reports: reports };
  },

  /**
   * Compute the stylesheet URL and delegate the report creation to createEditorReport.
   * See createEditorReport documentation.
   *
   * @param {StyleSheetActor} stylesheetActor
   *        the stylesheet actor for which the coverage report should be generated.
   */
  createEditorReportForSheet: function (stylesheetActor) {
    let url = sheetToUrl(stylesheetActor.rawSheet);
    return this.createEditorReport(url);
  },

  /**
   * Returns a JSONable structure designed for the page report which shows
   * the recommended changes to a page.
   *
   * "preload" means that a rule is used before the load event happens, which
   * means that the page could by optimized by placing it in a <style> element
   * at the top of the page, moving the <link> elements to the bottom.
   *
   * Example:
   *   {
   *     preload: [
   *       {
   *         url: "http://example.org/page1.html",
   *         shortUrl: "page1.html",
   *         rules: [
   *           {
   *             url: "http://example.org/style1.css",
   *             shortUrl: "style1.css",
   *             start: { line: 3, column: 4 },
   *             selectorText: "p#content",
   *             formattedCssText: "p#content {\n  color: red;\n }\n"
   *          },
   *          ...
   *         ]
   *       }
   *     ],
   *     unused: [
   *       {
   *         url: "http://example.org/style1.css",
   *         shortUrl: "style1.css",
   *         rules: [ ... ]
   *       }
   *     ]
   *   }
   */
  createPageReport: function () {
    if (this._running) {
      throw new Error(l10n.lookup("csscoverageRunningError"));
    }

    if (this._visitedPages == null) {
      throw new Error(l10n.lookup("csscoverageNotRunError"));
    }

    if (this._isOneShot) {
      throw new Error(l10n.lookup("csscoverageOneShotReportError"));
    }

    // Helper function to create a JSONable data structure representing a rule
    const ruleToRuleReport = function (rule, ruleData) {
      return {
        url: rule.url,
        shortUrl: rule.url.split("/").slice(-1)[0],
        start: { line: rule.line, column: rule.column },
        selectorText: ruleData.selectorText,
        formattedCssText: prettifyCSS(ruleData.cssText)
      };
    };

    // A count of each type of rule for the bar chart
    let summary = { used: 0, unused: 0, preload: 0 };

    // Create the set of the unused rules
    let unusedMap = new Map();
    for (let [ruleId, ruleData] of this._knownRules) {
      let rule = deconstructRuleId(ruleId);
      let rules = unusedMap.get(rule.url);
      if (rules == null) {
        rules = [];
        unusedMap.set(rule.url, rules);
      }
      if (!ruleData.isUsed) {
        let ruleReport = ruleToRuleReport(rule, ruleData);
        rules.push(ruleReport);
      } else {
        summary.unused++;
      }
    }
    let unused = [];
    for (let [url, rules] of unusedMap) {
      unused.push({
        url: url,
        shortUrl: url.split("/").slice(-1),
        rules: rules
      });
    }

    // Create the set of rules that could be pre-loaded
    let preload = [];
    for (let url of this._visitedPages) {
      let page = {
        url: url,
        shortUrl: url.split("/").slice(-1),
        rules: []
      };

      for (let [ruleId, ruleData] of this._knownRules) {
        if (ruleData.preLoadOn.has(url)) {
          let rule = deconstructRuleId(ruleId);
          let ruleReport = ruleToRuleReport(rule, ruleData);
          page.rules.push(ruleReport);
          summary.preload++;
        } else {
          summary.used++;
        }
      }

      if (page.rules.length > 0) {
        preload.push(page);
      }
    }

    return {
      summary: summary,
      preload: preload,
      unused: unused
    };
  },

  /**
   * For testing only. What pages did we visit.
   */
  _testOnlyVisitedPages: function () {
    return [...this._visitedPages];
  },
});

exports.CSSUsageActor = CSSUsageActor;

/**
 * Generator that filters the CSSRules out of _getAllRules so it only
 * iterates over the CSSStyleRules
 */
function* getAllSelectorRules(document) {
  for (let rule of getAllRules(document)) {
    if (rule.type === CSSRule.STYLE_RULE && rule.selectorText !== "") {
      yield rule;
    }
  }
}

/**
 * Generator to iterate over the CSSRules in all the stylesheets the
 * current document (i.e. it includes import rules, media rules, etc)
 */
function* getAllRules(document) {
  // sheets is an array of the <link> and <style> element in this document
  let sheets = getAllSheets(document);
  for (let i = 0; i < sheets.length; i++) {
    for (let j = 0; j < sheets[i].cssRules.length; j++) {
      yield sheets[i].cssRules[j];
    }
  }
}

/**
 * Get an array of all the stylesheets that affect this document. That means
 * the <link> and <style> based sheets, and the @imported sheets (recursively)
 * but not the sheets in nested frames.
 */
function getAllSheets(document) {
  // sheets is an array of the <link> and <style> element in this document
  let sheets = Array.slice(document.styleSheets);
  // Add @imported sheets
  for (let i = 0; i < sheets.length; i++) {
    let subSheets = getImportedSheets(sheets[i]);
    sheets = sheets.concat(...subSheets);
  }
  return sheets;
}

/**
 * Recursively find @import rules in the given stylesheet.
 * We're relying on the browser giving rule.styleSheet == null to resolve
 * @import loops
 */
function getImportedSheets(stylesheet) {
  let sheets = [];
  for (let i = 0; i < stylesheet.cssRules.length; i++) {
    let rule = stylesheet.cssRules[i];
    // rule.styleSheet == null with duplicate @imports for the same URL.
    if (rule.type === CSSRule.IMPORT_RULE && rule.styleSheet != null) {
      sheets.push(rule.styleSheet);
      let subSheets = getImportedSheets(rule.styleSheet);
      sheets = sheets.concat(...subSheets);
    }
  }
  return sheets;
}

/**
 * Get a unique identifier for a rule. This is currently the string
 * <CSS-URL>|<START-LINE>|<START-COLUMN>
 * @see deconstructRuleId(ruleId)
 */
function ruleToId(rule) {
  let line = InspectorUtils.getRelativeRuleLine(rule);
  let column = InspectorUtils.getRuleColumn(rule);
  return sheetToUrl(rule.parentStyleSheet) + "|" + line + "|" + column;
}

/**
 * Convert a ruleId to an object with { url, line, column } properties
 * @see ruleToId(rule)
 */
const deconstructRuleId = exports.deconstructRuleId = function (ruleId) {
  let split = ruleId.split("|");
  if (split.length > 3) {
    let replace = split.slice(0, split.length - 3 + 1).join("|");
    split.splice(0, split.length - 3 + 1, replace);
  }
  let [ url, line, column ] = split;
  return {
    url: url,
    line: parseInt(line, 10),
    column: parseInt(column, 10)
  };
};

/**
 * We're only interested in the origin and pathname, because changes to the
 * username, password, hash, or query string probably don't significantly
 * change the CSS usage properties of a page.
 * @param document
 */
const getURL = exports.getURL = function (document) {
  let url = new document.defaultView.URL(document.documentURI);
  return url == "about:blank" ? "" : "" + url.origin + url.pathname;
};

/**
 * Pseudo class handling constants:
 * We split pseudo-classes into a number of categories so we can decide how we
 * should match them. See getTestSelector for how we use these constants.
 *
 * @see http://dev.w3.org/csswg/selectors4/#overview
 * @see https://developer.mozilla.org/en-US/docs/tag/CSS%20Pseudo-class
 * @see https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-elements
 */

/**
 * Category 1: Pseudo-classes that depend on external browser/OS state
 * This includes things like the time, locale, position of mouse/caret/window,
 * contents of browser history, etc. These can be hard to mimic.
 * Action: Remove from selectors
 */
const SEL_EXTERNAL = [
  "active", "active-drop", "current", "dir", "focus", "future", "hover",
  "invalid-drop", "lang", "past", "placeholder-shown", "target", "valid-drop",
  "visited"
];

/**
 * Category 2: Pseudo-classes that depend on user-input state
 * These are pseudo-classes that arguably *should* be covered by unit tests but
 * which probably aren't and which are unlikely to be covered by manual tests.
 * We're currently stripping them out,
 * Action: Remove from selectors (but consider future command line flag to
 * enable them in the future. e.g. 'csscoverage start --strict')
 */
const SEL_FORM = [
  "checked", "default", "disabled", "enabled", "fullscreen", "in-range",
  "indeterminate", "invalid", "optional", "out-of-range", "required", "valid"
];

/**
 * Category 3: Pseudo-elements
 * querySelectorAll doesn't return matches with pseudo-elements because there
 * is no element to match (they're pseudo) so we have to remove them all.
 * (See http://codepen.io/joewalker/pen/sanDw for a demo)
 * Action: Remove from selectors (including deprecated single colon versions)
 */
const SEL_ELEMENT = [
  "after", "before", "first-letter", "first-line", "selection"
];

/**
 * Category 4: Structural pseudo-classes
 * This is a category defined by the spec (also called tree-structural and
 * grid-structural) for selection based on relative position in the document
 * tree that cannot be represented by other simple selectors or combinators.
 * Action: Require a page-match
 */
const SEL_STRUCTURAL = [
  "empty", "first-child", "first-of-type", "last-child", "last-of-type",
  "nth-column", "nth-last-column", "nth-child", "nth-last-child",
  "nth-last-of-type", "nth-of-type", "only-child", "only-of-type", "root"
];

/**
 * Category 4a: Semi-structural pseudo-classes
 * These are not structural according to the spec, but act nevertheless on
 * information in the document tree.
 * Action: Require a page-match
 */
const SEL_SEMI = [ "any-link", "link", "read-only", "read-write", "scope" ];

/**
 * Category 5: Combining pseudo-classes
 * has(), not() etc join selectors together in various ways. We take care when
 * removing pseudo-classes to convert "not(:hover)" into "not(*)" and so on.
 * With these changes the combining pseudo-classes should probably stand on
 * their own.
 * Action: Require a page-match
 */
const SEL_COMBINING = [ "not", "has", "matches" ];

/**
 * Category 6: Media pseudo-classes
 * Pseudo-classes that should be ignored because they're only relevant to
 * media queries
 * Action: Don't need removing from selectors as they appear in media queries
 */
const SEL_MEDIA = [ "blank", "first", "left", "right" ];

/**
 * A test selector is a reduced form of a selector that we actually test
 * against. This code strips out pseudo-elements and some pseudo-classes that
 * we think should not have to match in order for the selector to be relevant.
 */
function getTestSelector(selector) {
  let replacement = selector;
  let replaceSelector = pseudo => {
    replacement = replacement.replace(" :" + pseudo, " *")
                             .replace("(:" + pseudo, "(*")
                             .replace(":" + pseudo, "");
  };

  SEL_EXTERNAL.forEach(replaceSelector);
  SEL_FORM.forEach(replaceSelector);
  SEL_ELEMENT.forEach(replaceSelector);

  // Pseudo elements work in : and :: forms
  SEL_ELEMENT.forEach(pseudo => {
    replacement = replacement.replace("::" + pseudo, "");
  });

  return replacement;
}

/**
 * I've documented all known pseudo-classes above for 2 reasons: To allow
 * checking logic and what might be missing, but also to allow a unit test
 * that fetches the list of supported pseudo-classes and pseudo-elements from
 * the platform and check that they were all represented here.
 */
exports.SEL_ALL = [
  SEL_EXTERNAL, SEL_FORM, SEL_ELEMENT, SEL_STRUCTURAL, SEL_SEMI,
  SEL_COMBINING, SEL_MEDIA
].reduce(function (prev, curr) {
  return prev.concat(curr);
}, []);

/**
 * Find a URL for a given stylesheet
 * @param {StyleSheet} stylesheet raw stylesheet
 */
const sheetToUrl = function (stylesheet) {
  // For <link> elements
  if (stylesheet.href) {
    return stylesheet.href;
  }

  // For <style> elements
  if (stylesheet.ownerNode) {
    let document = stylesheet.ownerNode.ownerDocument;
    let sheets = [...document.querySelectorAll("style")];
    let index = sheets.indexOf(stylesheet.ownerNode);
    return getURL(document) + " → <style> index " + index;
  }

  throw new Error("Unknown sheet source");
};
PK
!<����8chrome/devtools/modules/devtools/server/actors/device.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Services = require("Services");
const protocol = require("devtools/shared/protocol");
const defer = require("devtools/shared/defer");
const {LongStringActor} = require("devtools/server/actors/string");
const {DebuggerServer} = require("devtools/server/main");
const {getSystemInfo, getSetting} = require("devtools/shared/system");
const {deviceSpec} = require("devtools/shared/specs/device");
const FileReader = require("FileReader");

exports.DeviceActor = protocol.ActorClassWithSpec(deviceSpec, {
  _desc: null,

  getDescription: function () {
    return getSystemInfo();
  },

  getWallpaper: function () {
    let deferred = defer();
    getSetting("wallpaper.image").then((blob) => {
      let reader = new FileReader();
      let conn = this.conn;
      reader.addEventListener("load", function () {
        let str = new LongStringActor(conn, reader.result);
        deferred.resolve(str);
      });
      reader.addEventListener("error", function () {
        deferred.reject(reader.error);
      });
      reader.readAsDataURL(blob);
    });
    return deferred.promise;
  },

  screenshotToDataURL: function () {
    let window = Services.wm.getMostRecentWindow(DebuggerServer.chromeWindowType);
    let { devicePixelRatio } = window;
    let canvas = window.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
    let width = window.innerWidth;
    let height = window.innerHeight;
    canvas.setAttribute("width", Math.round(width * devicePixelRatio));
    canvas.setAttribute("height", Math.round(height * devicePixelRatio));
    let context = canvas.getContext("2d");
    let flags =
          context.DRAWWINDOW_DRAW_CARET |
          context.DRAWWINDOW_DRAW_VIEW |
          context.DRAWWINDOW_USE_WIDGET_LAYERS;
    context.scale(devicePixelRatio, devicePixelRatio);
    context.drawWindow(window, 0, 0, width, height, "rgb(255,255,255)", flags);
    let dataURL = canvas.toDataURL("image/png");
    return new LongStringActor(this.conn, dataURL);
  }
});
PK
!<�)�O�,�,Kchrome/devtools/modules/devtools/server/actors/emulation/touch-simulator.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 XPCNativeWrapper */

"use strict";

const { Ci, Cu } = require("chrome");
const { Services } = require("resource://gre/modules/Services.jsm");

var systemAppOrigin = (function () {
  let systemOrigin = "_";
  try {
    systemOrigin =
      Services.io.newURI(Services.prefs.getCharPref("b2g.system_manifest_url"))
                 .prePath;
  } catch (e) {
    // Fall back to default value
  }
  return systemOrigin;
})();

var threshold = Services.prefs.getIntPref("ui.dragThresholdX", 25);
var delay = Services.prefs.getIntPref("ui.click_hold_context_menus.delay", 500);

function TouchSimulator(simulatorTarget) {
  this.simulatorTarget = simulatorTarget;
}

/**
 * Simulate touch events for platforms where they aren't generally available.
 */
TouchSimulator.prototype = {
  events: [
    "mousedown",
    "mousemove",
    "mouseup",
    "touchstart",
    "touchend",
    "mouseenter",
    "mouseover",
    "mouseout",
    "mouseleave"
  ],

  contextMenuTimeout: null,

  simulatorTarget: null,

  enabled: false,

  start() {
    if (this.enabled) {
      // Simulator is already started
      return;
    }
    this.events.forEach(evt => {
      // Only listen trusted events to prevent messing with
      // event dispatched manually within content documents
      this.simulatorTarget.addEventListener(evt, this, true, false);
    });
    this.enabled = true;
  },

  stop() {
    if (!this.enabled) {
      // Simulator isn't running
      return;
    }
    this.events.forEach(evt => {
      this.simulatorTarget.removeEventListener(evt, this, true);
    });
    this.enabled = false;
  },

  handleEvent(evt) {
    // The gaia system window use an hybrid system even on the device which is
    // a mix of mouse/touch events. So let's not cancel *all* mouse events
    // if it is the current target.
    let content = this.getContent(evt.target);
    if (!content) {
      return;
    }
    let isSystemWindow = content.location.toString()
                                .startsWith(systemAppOrigin);

    // App touchstart & touchend should also be dispatched on the system app
    // to match on-device behavior.
    if (evt.type.startsWith("touch") && !isSystemWindow) {
      let sysFrame = content.realFrameElement;
      if (!sysFrame) {
        return;
      }
      let sysDocument = sysFrame.ownerDocument;
      let sysWindow = sysDocument.defaultView;

      let touchEvent = sysDocument.createEvent("touchevent");
      let touch = evt.touches[0] || evt.changedTouches[0];
      let point = sysDocument.createTouch(sysWindow, sysFrame, 0,
                                          touch.pageX, touch.pageY,
                                          touch.screenX, touch.screenY,
                                          touch.clientX, touch.clientY,
                                          1, 1, 0, 0);

      let touches = sysDocument.createTouchList(point);
      let targetTouches = touches;
      let changedTouches = touches;
      touchEvent.initTouchEvent(evt.type, true, true, sysWindow, 0,
                                false, false, false, false,
                                touches, targetTouches, changedTouches);
      sysFrame.dispatchEvent(touchEvent);
      return;
    }

    // Ignore all but real mouse event coming from physical mouse
    // (especially ignore mouse event being dispatched from a touch event)
    if (evt.button ||
        evt.mozInputSource != Ci.nsIDOMMouseEvent.MOZ_SOURCE_MOUSE ||
        evt.isSynthesized) {
      return;
    }

    let eventTarget = this.target;
    let type = "";
    switch (evt.type) {
      case "mouseenter":
      case "mouseover":
      case "mouseout":
      case "mouseleave":
        // Don't propagate events which are not related to touch events
        evt.stopPropagation();
        break;

      case "mousedown":
        this.target = evt.target;

        this.contextMenuTimeout = this.sendContextMenu(evt);

        this.cancelClick = false;
        this.startX = evt.pageX;
        this.startY = evt.pageY;

        // Capture events so if a different window show up the events
        // won't be dispatched to something else.
        evt.target.setCapture(false);

        type = "touchstart";
        break;

      case "mousemove":
        if (!eventTarget) {
          // Don't propagate mousemove event when touchstart event isn't fired
          evt.stopPropagation();
          return;
        }

        if (!this.cancelClick) {
          if (Math.abs(this.startX - evt.pageX) > threshold ||
              Math.abs(this.startY - evt.pageY) > threshold) {
            this.cancelClick = true;
            content.clearTimeout(this.contextMenuTimeout);
          }
        }

        type = "touchmove";
        break;

      case "mouseup":
        if (!eventTarget) {
          return;
        }
        this.target = null;

        content.clearTimeout(this.contextMenuTimeout);
        type = "touchend";

        // Only register click listener after mouseup to ensure
        // catching only real user click. (Especially ignore click
        // being dispatched on form submit)
        if (evt.detail == 1) {
          this.simulatorTarget.addEventListener("click", this, true, false);
        }
        break;

      case "click":
        // Mouse events has been cancelled so dispatch a sequence
        // of events to where touchend has been fired
        evt.preventDefault();
        evt.stopImmediatePropagation();

        this.simulatorTarget.removeEventListener("click", this, true, false);

        if (this.cancelClick) {
          return;
        }

        content.setTimeout(function dispatchMouseEvents(self) {
          try {
            self.fireMouseEvent("mousedown", evt);
            self.fireMouseEvent("mousemove", evt);
            self.fireMouseEvent("mouseup", evt);
          } catch (e) {
            console.error("Exception in touch event helper: " + e);
          }
        }, this.getDelayBeforeMouseEvent(evt), this);
        return;
    }

    let target = eventTarget || this.target;
    if (target && type) {
      this.sendTouchEvent(evt, target, type);
    }

    if (!isSystemWindow) {
      evt.preventDefault();
      evt.stopImmediatePropagation();
    }
  },

  fireMouseEvent(type, evt) {
    let content = this.getContent(evt.target);
    let utils = content.QueryInterface(Ci.nsIInterfaceRequestor)
                       .getInterface(Ci.nsIDOMWindowUtils);
    utils.sendMouseEvent(type, evt.clientX, evt.clientY, 0, 1, 0, true, 0,
                         Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH);
  },

  sendContextMenu({ target, clientX, clientY, screenX, screenY }) {
    let view = target.ownerGlobal;
    let { MouseEvent } = view;
    let evt = new MouseEvent("contextmenu", {
      bubbles: true,
      cancelable: true,
      view,
      screenX,
      screenY,
      clientX,
      clientY,
    });
    let content = this.getContent(target);
    let timeout = content.setTimeout(() => {
      target.dispatchEvent(evt);
      this.cancelClick = true;
    }, delay);

    return timeout;
  },

  sendTouchEvent(evt, target, name) {
    function clone(obj) {
      return Cu.cloneInto(obj, target);
    }
    // When running OOP b2g desktop, we need to send the touch events
    // using the mozbrowser api on the unwrapped frame.
    if (target.localName == "iframe" && target.mozbrowser === true) {
      if (name == "touchstart") {
        this.touchstartTime = Date.now();
      } else if (name == "touchend") {
        // If we have a "fast" tap, don't send a click as both will be turned
        // into a click and that breaks eg. checkboxes.
        if (Date.now() - this.touchstartTime < delay) {
          this.cancelClick = true;
        }
      }
      let unwrapped = XPCNativeWrapper.unwrap(target);
      /* eslint-disable no-multi-spaces */
      unwrapped.sendTouchEvent(name, clone([0]),       // event type, id
                               clone([evt.clientX]),   // x
                               clone([evt.clientY]),   // y
                               clone([1]), clone([1]), // rx, ry
                               clone([0]), clone([0]), // rotation, force
                               1);                     // count
      /* eslint-enable no-multi-spaces */
      return;
    }
    let document = target.ownerDocument;
    let content = this.getContent(target);
    if (!content) {
      return;
    }

    let touchEvent = document.createEvent("touchevent");
    let point = document.createTouch(content, target, 0,
                                     evt.pageX, evt.pageY,
                                     evt.screenX, evt.screenY,
                                     evt.clientX, evt.clientY,
                                     1, 1, 0, 0);

    let touches = document.createTouchList(point);
    let targetTouches = touches;
    let changedTouches = touches;
    if (name === "touchend" || name === "touchcancel") {
      // "touchend" and "touchcancel" events should not have the removed touch
      // neither in touches nor in targetTouches
      touches = targetTouches = document.createTouchList();
    }

    touchEvent.initTouchEvent(name, true, true, content, 0,
                              false, false, false, false,
                              touches, targetTouches, changedTouches);
    target.dispatchEvent(touchEvent);
  },

  getContent(target) {
    let win = (target && target.ownerDocument)
      ? target.ownerGlobal
      : null;
    return win;
  },

  getDelayBeforeMouseEvent(evt) {
    // On mobile platforms, Firefox inserts a 300ms delay between
    // touch events and accompanying mouse events, except if the
    // content window is not zoomable and the content window is
    // auto-zoomed to device-width.

    // If the preference dom.meta-viewport.enabled is set to false,
    // we couldn't read viewport's information from getViewportInfo().
    // So we always simulate 300ms delay when the
    // dom.meta-viewport.enabled is false.
    let savedMetaViewportEnabled =
      Services.prefs.getBoolPref("dom.meta-viewport.enabled");
    if (!savedMetaViewportEnabled) {
      return 300;
    }

    let content = this.getContent(evt.target);
    if (!content) {
      return 0;
    }

    let utils = content.QueryInterface(Ci.nsIInterfaceRequestor)
                       .getInterface(Ci.nsIDOMWindowUtils);

    let allowZoom = {};
    let minZoom = {};
    let maxZoom = {};
    let autoSize = {};

    utils.getViewportInfo(content.innerWidth, content.innerHeight, {},
                          allowZoom, minZoom, maxZoom, {}, {}, autoSize);

    // FIXME: On Safari and Chrome mobile platform, if the css property
    // touch-action set to none or manipulation would also suppress 300ms
    // delay. But Firefox didn't support this property now, we can't get
    // this value from utils.getVisitedDependentComputedStyle() to check
    // if we should suppress 300ms delay.
    if (!allowZoom.value || // user-scalable = no
        minZoom.value === maxZoom.value || // minimum-scale = maximum-scale
        autoSize.value // width = device-width
    ) {
      return 0;
    }
    return 300;
  }
};

exports.TouchSimulator = TouchSimulator;
PK
!<�*�bb;chrome/devtools/modules/devtools/server/actors/emulation.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Ci } = require("chrome");
const protocol = require("devtools/shared/protocol");
const { emulationSpec } = require("devtools/shared/specs/emulation");
const { TouchSimulator } = require("devtools/server/actors/emulation/touch-simulator");

/**
 * This actor overrides various browser features to simulate different environments to
 * test how pages perform under various conditions.
 *
 * The design below, which saves the previous value of each property before setting, is
 * needed because it's possible to have multiple copies of this actor for a single page.
 * When some instance of this actor changes a property, we want it to be able to restore
 * that property to the way it was found before the change.
 *
 * A subtle aspect of the code below is that all get* methods must return non-undefined
 * values, so that the absence of a previous value can be distinguished from the value for
 * "no override" for each of the properties.
 */
let EmulationActor = protocol.ActorClassWithSpec(emulationSpec, {

  initialize(conn, tabActor) {
    protocol.Actor.prototype.initialize.call(this, conn);
    this.tabActor = tabActor;
    this.docShell = tabActor.docShell;
    this.touchSimulator = new TouchSimulator(tabActor.chromeEventHandler);
  },

  destroy() {
    this.clearDPPXOverride();
    this.clearNetworkThrottling();
    this.clearTouchEventsOverride();
    this.clearUserAgentOverride();
    this.tabActor = null;
    this.docShell = null;
    this.touchSimulator = null;
    protocol.Actor.prototype.destroy.call(this);
  },

  /**
   * Retrieve the console actor for this tab.  This allows us to expose network throttling
   * as part of emulation settings, even though it's internally connected to the network
   * monitor, which for historical reasons is part of the console actor.
   */
  get _consoleActor() {
    if (this.tabActor.exited) {
      return null;
    }
    let form = this.tabActor.form();
    return this.conn._getOrCreateActor(form.consoleActor);
  },

  /* DPPX override */

  _previousDPPXOverride: undefined,

  setDPPXOverride(dppx) {
    if (this.getDPPXOverride() === dppx) {
      return false;
    }

    if (this._previousDPPXOverride === undefined) {
      this._previousDPPXOverride = this.getDPPXOverride();
    }

    this.docShell.contentViewer.overrideDPPX = dppx;

    return true;
  },

  getDPPXOverride() {
    return this.docShell.contentViewer.overrideDPPX;
  },

  clearDPPXOverride() {
    if (this._previousDPPXOverride !== undefined) {
      return this.setDPPXOverride(this._previousDPPXOverride);
    }

    return false;
  },

  /* Network Throttling */

  _previousNetworkThrottling: undefined,

  /**
   * Transform the RDP format into the internal format and then set network throttling.
   */
  setNetworkThrottling({ downloadThroughput, uploadThroughput, latency }) {
    let throttleData = {
      latencyMean: latency,
      latencyMax: latency,
      downloadBPSMean: downloadThroughput,
      downloadBPSMax: downloadThroughput,
      uploadBPSMean: uploadThroughput,
      uploadBPSMax: uploadThroughput,
    };
    return this._setNetworkThrottling(throttleData);
  },

  _setNetworkThrottling(throttleData) {
    let current = this._getNetworkThrottling();
    // Check if they are both objects or both null
    let match = throttleData == current;
    // If both objects, check all entries
    if (match && current && throttleData) {
      match = Object.entries(current).every(([ k, v ]) => {
        return throttleData[k] === v;
      });
    }
    if (match) {
      return false;
    }

    if (this._previousNetworkThrottling === undefined) {
      this._previousNetworkThrottling = current;
    }

    let consoleActor = this._consoleActor;
    if (!consoleActor) {
      return false;
    }
    consoleActor.onStartListeners({
      listeners: [ "NetworkActivity" ],
    });
    consoleActor.onSetPreferences({
      preferences: {
        "NetworkMonitor.throttleData": throttleData,
      }
    });
    return true;
  },

  /**
   * Get network throttling and then transform the internal format into the RDP format.
   */
  getNetworkThrottling() {
    let throttleData = this._getNetworkThrottling();
    if (!throttleData) {
      return null;
    }
    let { downloadBPSMax, uploadBPSMax, latencyMax } = throttleData;
    return {
      downloadThroughput: downloadBPSMax,
      uploadThroughput: uploadBPSMax,
      latency: latencyMax,
    };
  },

  _getNetworkThrottling() {
    let consoleActor = this._consoleActor;
    if (!consoleActor) {
      return null;
    }
    let prefs = consoleActor.onGetPreferences({
      preferences: [ "NetworkMonitor.throttleData" ],
    });
    return prefs.preferences["NetworkMonitor.throttleData"] || null;
  },

  clearNetworkThrottling() {
    if (this._previousNetworkThrottling !== undefined) {
      return this._setNetworkThrottling(this._previousNetworkThrottling);
    }

    return false;
  },

  /* Touch events override */

  _previousTouchEventsOverride: undefined,

  setTouchEventsOverride(flag) {
    if (this.getTouchEventsOverride() == flag) {
      return false;
    }
    if (this._previousTouchEventsOverride === undefined) {
      this._previousTouchEventsOverride = this.getTouchEventsOverride();
    }

    // Start or stop the touch simulator depending on the override flag
    if (flag == Ci.nsIDocShell.TOUCHEVENTS_OVERRIDE_ENABLED) {
      this.touchSimulator.start();
    } else {
      this.touchSimulator.stop();
    }

    this.docShell.touchEventsOverride = flag;
    return true;
  },

  getTouchEventsOverride() {
    return this.docShell.touchEventsOverride;
  },

  clearTouchEventsOverride() {
    if (this._previousTouchEventsOverride !== undefined) {
      return this.setTouchEventsOverride(this._previousTouchEventsOverride);
    }
    return false;
  },

  /* User agent override */

  _previousUserAgentOverride: undefined,

  setUserAgentOverride(userAgent) {
    if (this.getUserAgentOverride() == userAgent) {
      return false;
    }
    if (this._previousUserAgentOverride === undefined) {
      this._previousUserAgentOverride = this.getUserAgentOverride();
    }
    this.docShell.customUserAgent = userAgent;
    return true;
  },

  getUserAgentOverride() {
    return this.docShell.customUserAgent;
  },

  clearUserAgentOverride() {
    if (this._previousUserAgentOverride !== undefined) {
      return this.setUserAgentOverride(this._previousUserAgentOverride);
    }
    return false;
  },

});

exports.EmulationActor = EmulationActor;
PK
!<$�.;;=chrome/devtools/modules/devtools/server/actors/environment.js/* -*- indent-tabs-mode: nil; js-indent-level: 2; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

/* global Debugger */

const { ActorClassWithSpec } = require("devtools/shared/protocol");
const { createValueGrip } = require("devtools/server/actors/object");
const { environmentSpec } = require("devtools/shared/specs/environment");

/**
 * Creates an EnvironmentActor. EnvironmentActors are responsible for listing
 * the bindings introduced by a lexical environment and assigning new values to
 * those identifier bindings.
 *
 * @param Debugger.Environment aEnvironment
 *        The lexical environment that will be used to create the actor.
 * @param ThreadActor aThreadActor
 *        The parent thread actor that contains this environment.
 */
let EnvironmentActor = ActorClassWithSpec(environmentSpec, {
  initialize: function (environment, threadActor) {
    this.obj = environment;
    this.threadActor = threadActor;
  },

  /**
   * When the Environment Actor is destroyed it removes the
   * Debugger.Environment.actor field so that environment does not
   * reference a destroyed actor.
   */
  destroy: function () {
    this.obj.actor = null;
  },

  /**
   * Return an environment form for use in a protocol message.
   */
  form: function () {
    let form = { actor: this.actorID };

    // What is this environment's type?
    if (this.obj.type == "declarative") {
      form.type = this.obj.callee ? "function" : "block";
    } else {
      form.type = this.obj.type;
    }

    // Does this environment have a parent?
    if (this.obj.parent) {
      form.parent = (this.threadActor
                     .createEnvironmentActor(this.obj.parent,
                                             this.registeredPool)
                     .form());
    }

    // Does this environment reflect the properties of an object as variables?
    if (this.obj.type == "object" || this.obj.type == "with") {
      form.object = createValueGrip(this.obj.object,
        this.registeredPool, this.threadActor.objectGrip);
    }

    // Is this the environment created for a function call?
    if (this.obj.callee) {
      form.function = createValueGrip(this.obj.callee,
        this.registeredPool, this.threadActor.objectGrip);
    }

    // Shall we list this environment's bindings?
    if (this.obj.type == "declarative") {
      form.bindings = this.bindings();
    }

    return form;
  },

  /**
   * Handle a protocol request to change the value of a variable bound in this
   * lexical environment.
   *
   * @param string name
   *        The name of the variable to be changed.
   * @param any value
   *        The value to be assigned.
   */
  assign: function (name, value) {
    // TODO: enable the commented-out part when getVariableDescriptor lands
    // (bug 725815).
    /* let desc = this.obj.getVariableDescriptor(name);

    if (!desc.writable) {
      return { error: "immutableBinding",
               message: "Changing the value of an immutable binding is not " +
                        "allowed" };
    }*/

    try {
      this.obj.setVariable(name, value);
    } catch (e) {
      if (e instanceof Debugger.DebuggeeWouldRun) {
        const errorObject = {
          error: "threadWouldRun",
          message: "Assigning a value would cause the debuggee to run"
        };
        throw errorObject;
      } else {
        throw e;
      }
    }
    return { from: this.actorID };
  },

  /**
   * Handle a protocol request to fully enumerate the bindings introduced by the
   * lexical environment.
   */
  bindings: function () {
    let bindings = { arguments: [], variables: {} };

    // TODO: this part should be removed in favor of the commented-out part
    // below when getVariableDescriptor lands (bug 725815).
    if (typeof this.obj.getVariable != "function") {
    // if (typeof this.obj.getVariableDescriptor != "function") {
      return bindings;
    }

    let parameterNames;
    if (this.obj.callee) {
      parameterNames = this.obj.callee.parameterNames;
    } else {
      parameterNames = [];
    }
    for (let name of parameterNames) {
      let arg = {};
      let value = this.obj.getVariable(name);

      // TODO: this part should be removed in favor of the commented-out part
      // below when getVariableDescriptor lands (bug 725815).
      let desc = {
        value: value,
        configurable: false,
        writable: !(value && value.optimizedOut),
        enumerable: true
      };

      // let desc = this.obj.getVariableDescriptor(name);
      let descForm = {
        enumerable: true,
        configurable: desc.configurable
      };
      if ("value" in desc) {
        descForm.value = createValueGrip(desc.value,
          this.registeredPool, this.threadActor.objectGrip);
        descForm.writable = desc.writable;
      } else {
        descForm.get = createValueGrip(desc.get, this.registeredPool,
          this.threadActor.objectGrip);
        descForm.set = createValueGrip(desc.set, this.registeredPool,
          this.threadActor.objectGrip);
      }
      arg[name] = descForm;
      bindings.arguments.push(arg);
    }

    for (let name of this.obj.names()) {
      if (bindings.arguments.some(function exists(element) {
        return !!element[name];
      })) {
        continue;
      }

      let value = this.obj.getVariable(name);

      // TODO: this part should be removed in favor of the commented-out part
      // below when getVariableDescriptor lands.
      let desc = {
        value: value,
        configurable: false,
        writable: !(value &&
                    (value.optimizedOut ||
                     value.uninitialized ||
                     value.missingArguments)),
        enumerable: true
      };

      // let desc = this.obj.getVariableDescriptor(name);
      let descForm = {
        enumerable: true,
        configurable: desc.configurable
      };
      if ("value" in desc) {
        descForm.value = createValueGrip(desc.value,
          this.registeredPool, this.threadActor.objectGrip);
        descForm.writable = desc.writable;
      } else {
        descForm.get = createValueGrip(desc.get || undefined,
          this.registeredPool, this.threadActor.objectGrip);
        descForm.set = createValueGrip(desc.set || undefined,
          this.registeredPool, this.threadActor.objectGrip);
      }
      bindings.variables[name] = descForm;
    }

    return bindings;
  }
});

exports.EnvironmentActor = EnvironmentActor;
PK
!<_�r�ll;chrome/devtools/modules/devtools/server/actors/errordocs.js/* this source code form is subject to the terms of the mozilla public
 * License, v. 2.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 mapping of error message names to external documentation. Any error message
 * included here will be displayed alongside its link in the web console.
 */

"use strict";

const baseURL = "https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/";
const params =
  "?utm_source=mozilla&utm_medium=firefox-console-errors&utm_campaign=default";
const ErrorDocs = {
  JSMSG_READ_ONLY: "Read-only",
  JSMSG_BAD_ARRAY_LENGTH: "Invalid_array_length",
  JSMSG_NEGATIVE_REPETITION_COUNT: "Negative_repetition_count",
  JSMSG_RESULTING_STRING_TOO_LARGE: "Resulting_string_too_large",
  JSMSG_BAD_RADIX: "Bad_radix",
  JSMSG_PRECISION_RANGE: "Precision_range",
  JSMSG_STMT_AFTER_RETURN: "Stmt_after_return",
  JSMSG_NOT_A_CODEPOINT: "Not_a_codepoint",
  JSMSG_BAD_SORT_ARG: "Array_sort_argument",
  JSMSG_UNEXPECTED_TYPE: "Unexpected_type",
  JSMSG_NOT_DEFINED: "Not_defined",
  JSMSG_NOT_FUNCTION: "Not_a_function",
  JSMSG_EQUAL_AS_ASSIGN: "Equal_as_assign",
  JSMSG_UNDEFINED_PROP: "Undefined_prop",
  JSMSG_DEPRECATED_PRAGMA: "Deprecated_source_map_pragma",
  JSMSG_DEPRECATED_USAGE: "Deprecated_caller_or_arguments_usage",
  JSMSG_CANT_DELETE: "Cant_delete",
  JSMSG_VAR_HIDES_ARG: "Var_hides_argument",
  JSMSG_JSON_BAD_PARSE: "JSON_bad_parse",
  JSMSG_UNDECLARED_VAR: "Undeclared_var",
  JSMSG_UNEXPECTED_TOKEN: "Unexpected_token",
  JSMSG_BAD_OCTAL: "Bad_octal",
  JSMSG_PROPERTY_ACCESS_DENIED: "Property_access_denied",
  JSMSG_NO_PROPERTIES: "No_properties",
  JSMSG_ALREADY_HAS_PRAGMA: "Already_has_pragma",
  JSMSG_BAD_RETURN_OR_YIELD: "Bad_return_or_yield",
  JSMSG_UNEXPECTED_TOKEN_NO_EXPECT: "Missing_semicolon_before_statement",
  JSMSG_OVER_RECURSED: "Too_much_recursion",
  JSMSG_BRACKET_AFTER_LIST: "Missing_bracket_after_list",
  JSMSG_PAREN_AFTER_ARGS: "Missing_parenthesis_after_argument_list",
  JSMSG_MORE_ARGS_NEEDED: "More_arguments_needed",
  JSMSG_BAD_LEFTSIDE_OF_ASS: "Invalid_assignment_left-hand_side",
  JSMSG_UNTERMINATED_STRING: "Unterminated_string_literal",
  JSMSG_NOT_CONSTRUCTOR: "Not_a_constructor",
  JSMSG_CURLY_AFTER_LIST: "Missing_curly_after_property_list",
  JSMSG_DEPRECATED_FOR_EACH: "For-each-in_loops_are_deprecated",
  JSMSG_STRICT_NON_SIMPLE_PARAMS: "Strict_Non_Simple_Params",
  JSMSG_DEAD_OBJECT: "Dead_object",
  JSMSG_NOT_NONNULL_OBJECT: "No_non-null_object",
  JSMSG_IDSTART_AFTER_NUMBER: "Identifier_after_number",
  JSMSG_DEPRECATED_EXPR_CLOSURE: "Deprecated_expression_closures",
  JSMSG_ILLEGAL_CHARACTER: "Illegal_character",
  JSMSG_BAD_REGEXP_FLAG: "Bad_regexp_flag",
  JSMSG_INVALID_FOR_IN_DECL_WITH_INIT: "Invalid_for-in_initializer",
  JSMSG_CANT_REDEFINE_PROP: "Cant_redefine_property",
  JSMSG_COLON_AFTER_ID: "Missing_colon_after_property_id",
  JSMSG_IN_NOT_OBJECT: "in_operator_no_object",
  JSMSG_CURLY_AFTER_BODY: "Missing_curly_after_function_body",
  JSMSG_NAME_AFTER_DOT: "Missing_name_after_dot_operator",
  JSMSG_DEPRECATED_OCTAL: "Deprecated_octal",
  JSMSG_PAREN_AFTER_COND: "Missing_parenthesis_after_condition",
  JSMSG_JSON_CYCLIC_VALUE: "Cyclic_object_value",
  JSMSG_NO_VARIABLE_NAME: "No_variable_name",
  JSMSG_UNNAMED_FUNCTION_STMT: "Unnamed_function_statement",
  JSMSG_CANT_DEFINE_PROP_OBJECT_NOT_EXTENSIBLE:
    "Cant_define_property_object_not_extensible",
  JSMSG_TYPED_ARRAY_BAD_ARGS: "Typed_array_invalid_arguments",
  JSMSG_GETTER_ONLY: "Getter_only",
  JSMSG_INVALID_DATE: "Invalid_date",
  JSMSG_DEPRECATED_STRING_METHOD: "Deprecated_String_generics",
  JSMSG_RESERVED_ID: "Reserved_identifier",
  JSMSG_BAD_CONST_ASSIGN: "Invalid_const_assignment",
  JSMSG_BAD_CONST_DECL: "Missing_initializer_in_const",
  JSMSG_OF_AFTER_FOR_LOOP_DECL: "Invalid_for-of_initializer",
  JSMSG_BAD_URI: "Malformed_URI",
  JSMSG_DEPRECATED_DELETE_OPERAND: "Delete_in_strict_mode",
  JSMSG_MISSING_FORMAL: "Missing_formal_parameter",
  JSMSG_CANT_TRUNCATE_ARRAY: "Non_configurable_array_element",
  JSMSG_INCOMPATIBLE_PROTO: "Called_on_incompatible_type",
  JSMSG_INCOMPATIBLE_METHOD: "Called_on_incompatible_type",
  JSMSG_BAD_INSTANCEOF_RHS: "invalid_right_hand_side_instanceof_operand",
};

const MIXED_CONTENT_LEARN_MORE = "https://developer.mozilla.org/docs/Web/Security/Mixed_content";
const TRACKING_PROTECTION_LEARN_MORE = "https://developer.mozilla.org/Firefox/Privacy/Tracking_Protection";
const INSECURE_PASSWORDS_LEARN_MORE = "https://developer.mozilla.org/docs/Web/Security/Insecure_passwords";
const PUBLIC_KEY_PINS_LEARN_MORE = "https://developer.mozilla.org/docs/Web/HTTP/Public_Key_Pinning";
const STRICT_TRANSPORT_SECURITY_LEARN_MORE = "https://developer.mozilla.org/docs/Web/HTTP/Headers/Strict-Transport-Security";
const WEAK_SIGNATURE_ALGORITHM_LEARN_MORE = "https://developer.mozilla.org/docs/Web/Security/Weak_Signature_Algorithm";
const MIME_TYPE_MISMATCH_LEARN_MORE = "https://developer.mozilla.org/docs/Web/HTTP/Headers/X-Content-Type-Options";
const SOURCE_MAP_LEARN_MORE = "https://developer.mozilla.org/en-US/docs/Tools/Debugger/Source_map_errors";
const ErrorCategories = {
  "Insecure Password Field": INSECURE_PASSWORDS_LEARN_MORE,
  "Mixed Content Message": MIXED_CONTENT_LEARN_MORE,
  "Mixed Content Blocker": MIXED_CONTENT_LEARN_MORE,
  "Invalid HPKP Headers": PUBLIC_KEY_PINS_LEARN_MORE,
  "Invalid HSTS Headers": STRICT_TRANSPORT_SECURITY_LEARN_MORE,
  "SHA-1 Signature": WEAK_SIGNATURE_ALGORITHM_LEARN_MORE,
  "Tracking Protection": TRACKING_PROTECTION_LEARN_MORE,
  "MIMEMISMATCH": MIME_TYPE_MISMATCH_LEARN_MORE,
  "source map": SOURCE_MAP_LEARN_MORE,
};

exports.GetURL = (error) => {
  if (!error) {
    return undefined;
  }

  let doc = ErrorDocs[error.errorMessageName];
  if (doc) {
    return baseURL + doc + params;
  }

  let categoryURL = ErrorCategories[error.category];
  if (categoryURL) {
    return categoryURL + params;
  }
  return undefined;
};
PK
!<왷��>chrome/devtools/modules/devtools/server/actors/eventlooplag.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 eventLoopLag actor emits "event-loop-lag" events when the event
 * loop gets unresponsive. The event comes with a "time" property (the
 * duration of the lag in milliseconds).
 */

const {Ci} = require("chrome");
const Services = require("Services");
const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
const {Actor, ActorClassWithSpec} = require("devtools/shared/protocol");
const {eventLoopLagSpec} = require("devtools/shared/specs/eventlooplag");

exports.EventLoopLagActor = ActorClassWithSpec(eventLoopLagSpec, {
  _observerAdded: false,

  /**
   * Start tracking the event loop lags.
   */
  start: function () {
    if (!this._observerAdded) {
      Services.obs.addObserver(this, "event-loop-lag");
      this._observerAdded = true;
    }
    return Services.appShell.startEventLoopLagTracking();
  },

  /**
   * Stop tracking the event loop lags.
   */
  stop: function () {
    if (this._observerAdded) {
      Services.obs.removeObserver(this, "event-loop-lag");
      this._observerAdded = false;
    }
    Services.appShell.stopEventLoopLagTracking();
  },

  destroy: function () {
    this.stop();
    Actor.prototype.destroy.call(this);
  },

  // nsIObserver

  observe: function (subject, topic, data) {
    if (topic == "event-loop-lag") {
      // Forward event loop lag event
      this.emit("event-loop-lag", data);
    }
  },

  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
});
PK
!<�#$��7chrome/devtools/modules/devtools/server/actors/frame.js/* -*- indent-tabs-mode: nil; js-indent-level: 2; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 { ActorPool } = require("devtools/server/actors/common");
const { createValueGrip } = require("devtools/server/actors/object");
const { ActorClassWithSpec } = require("devtools/shared/protocol");
const { frameSpec } = require("devtools/shared/specs/frame");

/**
 * An actor for a specified stack frame.
 */
let FrameActor = ActorClassWithSpec(frameSpec, {
  /**
   * Creates the Frame actor.
   *
   * @param frame Debugger.Frame
   *        The debuggee frame.
   * @param threadActor ThreadActor
   *        The parent thread actor for this frame.
   */
  initialize: function (frame, threadActor) {
    this.frame = frame;
    this.threadActor = threadActor;
  },

  /**
   * A pool that contains frame-lifetime objects, like the environment.
   */
  _frameLifetimePool: null,
  get frameLifetimePool() {
    if (!this._frameLifetimePool) {
      this._frameLifetimePool = new ActorPool(this.conn);
      this.conn.addActorPool(this._frameLifetimePool);
    }
    return this._frameLifetimePool;
  },

  /**
   * Finalization handler that is called when the actor is being evicted from
   * the pool.
   */
  destroy: function () {
    this.conn.removeActorPool(this._frameLifetimePool);
    this._frameLifetimePool = null;
  },

  getEnvironment: function () {
    if (!this.frame.environment) {
      return {};
    }

    let envActor = this.threadActor.createEnvironmentActor(
      this.frame.environment,
      this.frameLifetimePool
    );

    return envActor.form();
  },

  /**
   * Returns a frame form for use in a protocol message.
   */
  form: function () {
    let threadActor = this.threadActor;
    let form = { actor: this.actorID,
                 type: this.frame.type };
    if (this.frame.type === "call") {
      form.callee = createValueGrip(this.frame.callee, threadActor._pausePool,
        threadActor.objectGrip);
    }

    // NOTE: ignoreFrameEnvironment lets the client explicitly avoid
    // populating form environments on pause.
    if (
      !this.threadActor._options.ignoreFrameEnvironment &&
      this.frame.environment
    ) {
      form.environment = this.getEnvironment();
    }

    if (this.frame.type != "wasmcall") {
      form.this = createValueGrip(this.frame.this, threadActor._pausePool,
        threadActor.objectGrip);
    }

    form.arguments = this._args();
    if (this.frame.script) {
      let generatedLocation = this.threadActor.sources.getFrameLocation(this.frame);
      form.where = {
        source: generatedLocation.generatedSourceActor.form(),
        line: generatedLocation.generatedLine,
        column: generatedLocation.generatedColumn
      };
    }

    if (!this.frame.older) {
      form.oldest = true;
    }

    return form;
  },

  _args: function () {
    if (!this.frame.arguments) {
      return [];
    }

    return this.frame.arguments.map(arg => createValueGrip(arg,
      this.threadActor._pausePool, this.threadActor.objectGrip));
  }
});

exports.FrameActor = FrameActor;
PK
!<�#�;chrome/devtools/modules/devtools/server/actors/framerate.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Actor, ActorClassWithSpec } = require("devtools/shared/protocol");
const { actorBridgeWithSpec } = require("devtools/server/actors/common");
const { Framerate } = require("devtools/server/performance/framerate");
const { framerateSpec } = require("devtools/shared/specs/framerate");

/**
 * An actor wrapper around Framerate. Uses exposed
 * methods via bridge and provides RDP definitions.
 *
 * @see devtools/server/performance/framerate.js for documentation.
 */
exports.FramerateActor = ActorClassWithSpec(framerateSpec, {
  initialize: function (conn, tabActor) {
    Actor.prototype.initialize.call(this, conn);
    this.bridge = new Framerate(tabActor);
  },
  destroy: function (conn) {
    Actor.prototype.destroy.call(this, conn);
    this.bridge.destroy();
  },

  startRecording: actorBridgeWithSpec("startRecording"),
  stopRecording: actorBridgeWithSpec("stopRecording"),
  cancelRecording: actorBridgeWithSpec("cancelRecording"),
  isRecording: actorBridgeWithSpec("isRecording"),
  getPendingTicks: actorBridgeWithSpec("getPendingTicks"),
});
PK
!<�s??��6chrome/devtools/modules/devtools/server/actors/gcli.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Actor, ActorClassWithSpec } = require("devtools/shared/protocol");
const { gcliSpec } = require("devtools/shared/specs/gcli");
const { createSystem } = require("gcli/system");

/**
 * Manage remote connections that want to talk to GCLI
 */
const GcliActor = ActorClassWithSpec(gcliSpec, {
  initialize: function (conn, tabActor) {
    Actor.prototype.initialize.call(this, conn);

    this._commandsChanged = this._commandsChanged.bind(this);

    this._tabActor = tabActor;
    // see _getRequisition()
    this._requisitionPromise = undefined;
  },

  destroy: function () {
    Actor.prototype.destroy.call(this);

    // If _getRequisition has not been called, just bail quickly
    if (this._requisitionPromise == null) {
      this._commandsChanged = undefined;
      this._tabActor = undefined;
      return Promise.resolve();
    }

    return this._getRequisition().then(requisition => {
      requisition.destroy();

      this._system.commands.onCommandsChange.remove(this._commandsChanged);
      this._system.destroy();
      this._system = undefined;

      this._requisitionPromise = undefined;
      this._tabActor = undefined;

      this._commandsChanged = undefined;
    });
  },

  /**
   * Load a module into the requisition
   */
  _testOnlyAddItemsByModule: function (names) {
    return this._getRequisition().then(requisition => {
      return requisition.system.addItemsByModule(names);
    });
  },

  /**
   * Unload a module from the requisition
   */
  _testOnlyRemoveItemsByModule: function (names) {
    return this._getRequisition().then(requisition => {
      return requisition.system.removeItemsByModule(names);
    });
  },

  /**
   * Retrieve a list of the remotely executable commands
   * @param customProps Array of strings containing additional properties which,
   * if specified in the command spec, will be included in the JSON. Normally we
   * transfer only the properties required for GCLI to function.
   */
  specs: function (customProps) {
    return this._getRequisition().then(requisition => {
      return requisition.system.commands.getCommandSpecs(customProps);
    });
  },

  /**
   * Execute a GCLI command
   * @return a promise of an object with the following properties:
   * - data: The output of the command
   * - type: The type of the data to allow selection of a converter
   * - error: True if the output was considered an error
   */
  execute: function (typed) {
    return this._getRequisition().then(requisition => {
      return requisition.updateExec(typed).then(output => output.toJson());
    });
  },

  /**
   * Get the state of an input string. i.e. requisition.getStateData()
   */
  state: function (typed, start, rank) {
    return this._getRequisition().then(requisition => {
      return requisition.update(typed).then(() => {
        return requisition.getStateData(start, rank);
      });
    });
  },

  /**
   * Call type.parse to check validity. Used by the remote type
   * @return a promise of an object with the following properties:
   * - status: Of of the following strings: VALID|INCOMPLETE|ERROR
   * - message: The message to display to the user
   * - predictions: An array of suggested values for the given parameter
   */
  parseType: function (typed, paramName) {
    return this._getRequisition().then(requisition => {
      return requisition.update(typed).then(() => {
        let assignment = requisition.getAssignment(paramName);
        return Promise.resolve(assignment.predictions).then(predictions => {
          return {
            status: assignment.getStatus().toString(),
            message: assignment.message,
            predictions: predictions
          };
        });
      });
    });
  },

  /**
   * Get the incremented/decremented value of some type
   * @return a promise of a string containing the new argument text
   */
  nudgeType: function (typed, by, paramName) {
    return this.requisition.update(typed).then(() => {
      const assignment = this.requisition.getAssignment(paramName);
      return this.requisition.nudge(assignment, by).then(() => {
        return assignment.arg == null ? undefined : assignment.arg.text;
      });
    });
  },

  /**
   * Perform a lookup on a selection type to get the allowed values
   */
  getSelectionLookup: function (commandName, paramName) {
    return this._getRequisition().then(requisition => {
      const command = requisition.system.commands.get(commandName);
      if (command == null) {
        throw new Error("No command called '" + commandName + "'");
      }

      let type;
      command.params.forEach(param => {
        if (param.name === paramName) {
          type = param.type;
        }
      });

      if (type == null) {
        throw new Error("No parameter called '" + paramName + "' in '" +
                        commandName + "'");
      }

      const reply = type.getLookup(requisition.executionContext);
      return Promise.resolve(reply).then(lookup => {
        // lookup returns an array of objects with name/value properties and
        // the values might not be JSONable, so remove them
        return lookup.map(info => ({ name: info.name }));
      });
    });
  },

  /**
   * Lazy init for a Requisition
   */
  _getRequisition: function () {
    if (this._tabActor == null) {
      throw new Error("GcliActor used post-destroy");
    }

    if (this._requisitionPromise != null) {
      return this._requisitionPromise;
    }

    const Requisition = require("gcli/cli").Requisition;
    const tabActor = this._tabActor;

    this._system = createSystem({ location: "server" });
    this._system.commands.onCommandsChange.add(this._commandsChanged);

    const gcliInit = require("devtools/shared/gcli/commands/index");
    gcliInit.addAllItemsByModule(this._system);

    // this._requisitionPromise should be created synchronously with the call
    // to _getRequisition so that destroy can tell whether there is an async
    // init in progress
    this._requisitionPromise = this._system.load().then(() => {
      const environment = {
        get chromeWindow() {
          throw new Error("environment.chromeWindow is not available in runAt:server" +
            " commands");
        },

        get chromeDocument() {
          throw new Error("environment.chromeDocument is not available in runAt:server" +
            " commands");
        },

        get window() {
          return tabActor.window;
        },

        get document() {
          return tabActor.window && tabActor.window.document;
        }
      };

      return new Requisition(this._system, { environment: environment });
    });

    return this._requisitionPromise;
  },

  /**
   * Pass events from requisition.system.commands.onCommandsChange upwards
   */
  _commandsChanged: function () {
    this.emit("commands-changed");
  },
});

exports.GcliActor = GcliActor;
PK
!<��J/,	,	Dchrome/devtools/modules/devtools/server/actors/heap-snapshot-file.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 protocol = require("devtools/shared/protocol");
const Services = require("Services");

const { heapSnapshotFileSpec } = require("devtools/shared/specs/heap-snapshot-file");

loader.lazyRequireGetter(this, "DevToolsUtils",
                         "devtools/shared/DevToolsUtils");
loader.lazyRequireGetter(this, "OS", "resource://gre/modules/osfile.jsm", true);
loader.lazyRequireGetter(this, "HeapSnapshotFileUtils",
                         "devtools/shared/heapsnapshot/HeapSnapshotFileUtils");

/**
 * The HeapSnapshotFileActor handles transferring heap snapshot files from the
 * server to the client. This has to be a global actor in the parent process
 * because child processes are sandboxed and do not have access to the file
 * system.
 */
exports.HeapSnapshotFileActor = protocol.ActorClassWithSpec(heapSnapshotFileSpec, {
  initialize: function (conn, parent) {
    if (Services.appinfo.processType !== Services.appinfo.PROCESS_TYPE_DEFAULT) {
      const err = new Error("Attempt to create a HeapSnapshotFileActor in a " +
                            "child process! The HeapSnapshotFileActor *MUST* " +
                            "be in the parent process!");
      DevToolsUtils.reportException(
        "HeapSnapshotFileActor.prototype.initialize", err);
      return;
    }

    protocol.Actor.prototype.initialize.call(this, conn, parent);
  },

  /**
   * @see MemoryFront.prototype.transferHeapSnapshot
   */
  async transferHeapSnapshot(snapshotId) {
    const snapshotFilePath =
          HeapSnapshotFileUtils.getHeapSnapshotTempFilePath(snapshotId);
    if (!snapshotFilePath) {
      throw new Error(`No heap snapshot with id: ${snapshotId}`);
    }

    const streamPromise = DevToolsUtils.openFileStream(snapshotFilePath);

    const { size } = await OS.File.stat(snapshotFilePath);
    const bulkPromise = this.conn.startBulkSend({
      actor: this.actorID,
      type: "heap-snapshot",
      length: size
    });

    const [bulk, stream] = await Promise.all([bulkPromise, streamPromise]);

    try {
      await bulk.copyFrom(stream);
    } finally {
      stream.close();
    }
  },

});
PK
!<1�z
��Ichrome/devtools/modules/devtools/server/actors/highlighters/accessible.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { AutoRefreshHighlighter } = require("./auto-refresh");
const { getBounds } = require("./utils/accessibility");

const {
  CanvasFrameAnonymousContentHelper,
  createNode,
  createSVGNode
} = require("./utils/markup");

const { setIgnoreLayoutChanges } = require("devtools/shared/layout/utils");

/**
 * The AccessibleHighlighter draws the bounds of an accessible object.
 *
 * Usage example:
 *
 * let h = new AccessibleHighlighter(env);
 * h.show(node, { x, y, w, h, [duration] });
 * h.hide();
 * h.destroy();
 *
 * Available options:
 *         - {Number} x
 *           x coordinate of the top left corner of the accessible object
 *         - {Number} y
 *           y coordinate of the top left corner of the accessible object
 *         - {Number} w
 *           width of the the accessible object
 *         - {Number} h
 *           height of the the accessible object
 *         - {Number} duration
 *           Duration of time that the highlighter should be shown.
 *
 * Structure:
 * <div class="highlighter-container">
 *   <div class="accessible-root">
 *     <svg class="accessible-elements" hidden="true">
 *       <path class="accessible-bounds" points="..." />
 *     </svg>
 *   </div>
 * </div>
 */
class AccessibleHighlighter extends AutoRefreshHighlighter {
  constructor(highlighterEnv) {
    super(highlighterEnv);

    this.ID_CLASS_PREFIX = "accessible-";

    this.markup = new CanvasFrameAnonymousContentHelper(this.highlighterEnv,
      this._buildMarkup.bind(this));

    this.onPageHide = this.onPageHide.bind(this);
    this.onWillNavigate = this.onWillNavigate.bind(this);

    this.highlighterEnv.on("will-navigate", this.onWillNavigate);

    this.pageListenerTarget = highlighterEnv.pageListenerTarget;
    this.pageListenerTarget.addEventListener("pagehide", this.onPageHide);
  }

  /**
   * Build highlighter markup.
   *
   * @return {Object} Container element for the highlighter markup.
   */
  _buildMarkup() {
    let container = createNode(this.win, {
      attributes: {
        "class": "highlighter-container",
        "role": "presentation"
      }
    });

    let root = createNode(this.win, {
      parent: container,
      attributes: {
        "id": "root",
        "class": "root",
        "role": "presentation"
      },
      prefix: this.ID_CLASS_PREFIX
    });

    // Build the SVG element.
    let svg = createSVGNode(this.win, {
      nodeType: "svg",
      parent: root,
      attributes: {
        "id": "elements",
        "width": "100%",
        "height": "100%",
        "hidden": "true",
        "role": "presentation"
      },
      prefix: this.ID_CLASS_PREFIX
    });

    createSVGNode(this.win, {
      nodeType: "path",
      parent: svg,
      attributes: {
        "class": "bounds",
        "id": "bounds",
        "role": "presentation"
      },
      prefix: this.ID_CLASS_PREFIX
    });

    return container;
  }

  /**
   * Destroy the nodes. Remove listeners.
   */
  destroy() {
    if (this._highlightTimer) {
      clearTimeout(this._highlightTimer);
      this._highlightTimer = null;
    }

    this.highlighterEnv.off("will-navigate", this.onWillNavigate);
    this.pageListenerTarget.removeEventListener("pagehide", this.onPageHide);
    this.pageListenerTarget = null;

    this.markup.destroy();
    AutoRefreshHighlighter.prototype.destroy.call(this);
  }

  /**
   * Find an element in highlighter markup.
   *
   * @param  {String} id
   *         Highlighter markup elemet id attribute.
   * @return {DOMNode} Element in the highlighter markup.
   */
  getElement(id) {
    return this.markup.getElement(this.ID_CLASS_PREFIX + id);
  }

  /**
   * Show the highlighter on a given accessible.
   *
   * @return {Boolean} True if accessible is highlighted, false otherwise.
   */
  _show() {
    if (this._highlightTimer) {
      clearTimeout(this._highlightTimer);
      this._highlightTimer = null;
    }

    let { duration } = this.options;
    let shown = this._update();
    if (shown && duration) {
      this._highlightTimer = setTimeout(() => {
        this.hide();
      }, duration);
    }
    return shown;
  }

  /**
   * Update and show accessible bounds for a current accessible.
   *
   * @return {Boolean} True if accessible is highlighted, false otherwise.
   */
  _update() {
    let shown = false;
    setIgnoreLayoutChanges(true);

    if (this._updateAccessibleBounds()) {
      this._showAccessibleBounds();
      shown = true;
    } else {
      // Nothing to highlight (0px rectangle like a <script> tag for instance)
      this.hide();
    }

    setIgnoreLayoutChanges(false,
                           this.highlighterEnv.window.document.documentElement);

    return shown;
  }

  /**
   * Hide the highlighter.
   */
  _hide() {
    setIgnoreLayoutChanges(true);
    this._hideAccessibleBounds();
    setIgnoreLayoutChanges(false,
                           this.highlighterEnv.window.document.documentElement);
  }

  /**
   * Hide the accessible bounds container.
   */
  _hideAccessibleBounds() {
    this.getElement("elements").setAttribute("hidden", "true");
  }

  /**
   * Showthe accessible bounds container.
   */
  _showAccessibleBounds() {
    this.getElement("elements").removeAttribute("hidden");
  }

  /**
   * Get current accessible bounds.
   *
   * @return {Object|null} Returns, if available, positioning and bounds
   *                       information for the accessible object.
   */
  get _bounds() {
    return getBounds(this.win, this.options);
  }

  /**
   * Update accessible bounds for a current accessible. Re-draw highlighter
   * markup.
   *
   * @return {Boolean} True if accessible is highlighted, false otherwise.
   */
  _updateAccessibleBounds() {
    let bounds = this._bounds;
    if (!bounds) {
      this._hideAccessibleBounds();
      return false;
    }

    let boundsEl = this.getElement("bounds");
    let { left, right, top, bottom } = bounds;
    let path = `M${left},${top} L${right},${top} L${right},${bottom} L${left},${bottom}`;
    boundsEl.setAttribute("d", path);

    // Un-zoom the root wrapper if the page was zoomed.
    let rootId = this.ID_CLASS_PREFIX + "elements";
    this.markup.scaleRootElement(this.currentNode, rootId);

    return true;
  }

  /**
   * Hide highlighter on page hide.
   */
  onPageHide({ target }) {
    // If a pagehide event is triggered for current window's highlighter, hide
    // the highlighter.
    if (target.defaultView === this.win) {
      this.hide();
    }
  }

  /**
   * Hide highlighter on navigation.
   */
  onWillNavigate({ isTopLevel }) {
    if (isTopLevel) {
      this.hide();
    }
  }
}

exports.AccessibleHighlighter = AccessibleHighlighter;
PK
!<h��4 4 Kchrome/devtools/modules/devtools/server/actors/highlighters/auto-refresh.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Cu } = require("chrome");
const EventEmitter = require("devtools/shared/event-emitter");
const { isNodeValid } = require("./utils/markup");
const { getAdjustedQuads, getWindowDimensions } = require("devtools/shared/layout/utils");

// Note that the order of items in this array is important because it is used
// for drawing the BoxModelHighlighter's path elements correctly.
const BOX_MODEL_REGIONS = ["margin", "border", "padding", "content"];
const QUADS_PROPS = ["p1", "p2", "p3", "p4", "bounds"];

function areValuesDifferent(oldValue, newValue) {
  let delta = Math.abs(oldValue.toFixed(4) - newValue.toFixed(4));
  return delta >= .5;
}

function areQuadsDifferent(oldQuads, newQuads) {
  for (let region of BOX_MODEL_REGIONS) {
    if (oldQuads[region].length !== newQuads[region].length) {
      return true;
    }

    for (let i = 0; i < oldQuads[region].length; i++) {
      for (let prop of QUADS_PROPS) {
        let oldProp = oldQuads[region][i][prop];
        let newProp = newQuads[region][i][prop];

        for (let key of Object.keys(oldProp)) {
          if (areValuesDifferent(oldProp[key], newProp[key])) {
            return true;
          }
        }
      }
    }
  }

  return false;
}

/**
 * Base class for auto-refresh-on-change highlighters. Sub classes will have a
 * chance to update whenever the current node's geometry changes.
 *
 * Sub classes must implement the following methods:
 * _show: called when the highlighter should be shown,
 * _hide: called when the highlighter should be hidden,
 * _update: called while the highlighter is shown and the geometry of the
 *          current node changes.
 *
 * Sub classes will have access to the following properties:
 * - this.currentNode: the node to be shown
 * - this.currentQuads: all of the node's box model region quads
 * - this.win: the current window
 *
 * Emits the following events:
 * - shown
 * - hidden
 * - updated
 */
function AutoRefreshHighlighter(highlighterEnv) {
  EventEmitter.decorate(this);

  this.highlighterEnv = highlighterEnv;

  this.currentNode = null;
  this.currentQuads = {};

  this._winDimensions = getWindowDimensions(this.win);
  this._scroll = { x: this.win.pageXOffset, y: this.win.pageYOffset };

  this.update = this.update.bind(this);
}

AutoRefreshHighlighter.prototype = {
  /**
   * Window corresponding to the current highlighterEnv
   */
  get win() {
    if (!this.highlighterEnv) {
      return null;
    }
    return this.highlighterEnv.window;
  },

  /**
   * Show the highlighter on a given node
   * @param {DOMNode} node
   * @param {Object} options
   *        Object used for passing options
   */
  show: function (node, options = {}) {
    let isSameNode = node === this.currentNode;
    let isSameOptions = this._isSameOptions(options);

    if (!this._isNodeValid(node) || (isSameNode && isSameOptions)) {
      return false;
    }

    this.options = options;

    this._stopRefreshLoop();
    this.currentNode = node;
    this._updateAdjustedQuads();
    this._startRefreshLoop();

    let shown = this._show();
    if (shown) {
      this.emit("shown");
    }
    return shown;
  },

  /**
   * Hide the highlighter
   */
  hide: function () {
    if (!this.currentNode || !this.highlighterEnv.window) {
      return;
    }

    this._hide();
    this._stopRefreshLoop();
    this.currentNode = null;
    this.currentQuads = {};
    this.options = null;

    this.emit("hidden");
  },

  /**
   * Whether the current node is valid for this highlighter type.
   * This is implemented by default to check if the node is an element node. Highlighter
   * sub-classes should override this method if they want to highlight other node types.
   * @param {DOMNode} node
   * @return {Boolean}
   */
  _isNodeValid: function (node) {
    return isNodeValid(node);
  },

  /**
   * Are the provided options the same as the currently stored options?
   * Returns false if there are no options stored currently.
   */
  _isSameOptions: function (options) {
    if (!this.options) {
      return false;
    }

    let keys = Object.keys(options);

    if (keys.length !== Object.keys(this.options).length) {
      return false;
    }

    for (let key of keys) {
      if (this.options[key] !== options[key]) {
        return false;
      }
    }

    return true;
  },

  /**
   * Update the stored box quads by reading the current node's box quads.
   */
  _updateAdjustedQuads: function () {
    this.currentQuads = {};

    for (let region of BOX_MODEL_REGIONS) {
      this.currentQuads[region] = getAdjustedQuads(
        this.win,
        this.currentNode, region);
    }
  },

  /**
   * Update the knowledge we have of the current node's boxquads and return true
   * if any of the points x/y or bounds have change since.
   * @return {Boolean}
   */
  _hasMoved: function () {
    let oldQuads = this.currentQuads;
    this._updateAdjustedQuads();

    return areQuadsDifferent(oldQuads, this.currentQuads);
  },

  /**
   * Update the knowledge we have of the current window's scrolling offset, both
   * horizontal and vertical, and return `true` if they have changed since.
   * @return {Boolean}
   */
  _hasWindowScrolled: function () {
    let { pageXOffset, pageYOffset } = this.win;
    let hasChanged = this._scroll.x !== pageXOffset ||
                     this._scroll.y !== pageYOffset;

    this._scroll = { x: pageXOffset, y: pageYOffset };

    return hasChanged;
  },

  /**
   * Update the knowledge we have of the current window's dimensions and return `true`
   * if they have changed since.
   * @return {Boolean}
   */
  _haveWindowDimensionsChanged: function () {
    let { width, height } = getWindowDimensions(this.win);
    let haveChanged = (this._winDimensions.width !== width ||
                      this._winDimensions.height !== height);

    this._winDimensions = { width, height };
    return haveChanged;
  },

  /**
   * Update the highlighter if the node has moved since the last update.
   */
  update: function () {
    if (!this._isNodeValid(this.currentNode) ||
       (!this._hasMoved() && !this._haveWindowDimensionsChanged())) {
      // At this point we're not calling the `_update` method. However, if the window has
      // scrolled, we want to invoke `_scrollUpdate`.
      if (this._hasWindowScrolled()) {
        this._scrollUpdate();
      }

      return;
    }

    this._update();
    this.emit("updated");
  },

  _show: function () {
    // To be implemented by sub classes
    // When called, sub classes should actually show the highlighter for
    // this.currentNode, potentially using options in this.options
    throw new Error("Custom highlighter class had to implement _show method");
  },

  _update: function () {
    // To be implemented by sub classes
    // When called, sub classes should update the highlighter shown for
    // this.currentNode
    // This is called as a result of a page zoom or repaint
    throw new Error("Custom highlighter class had to implement _update method");
  },

  _scrollUpdate: function () {
    // Can be implemented by sub classes
    // When called, sub classes can upate the highlighter shown for
    // this.currentNode
    // This is called as a result of a page scroll
  },

  _hide: function () {
    // To be implemented by sub classes
    // When called, sub classes should actually hide the highlighter
    throw new Error("Custom highlighter class had to implement _hide method");
  },

  _startRefreshLoop: function () {
    let win = this.currentNode.ownerGlobal;
    this.rafID = win.requestAnimationFrame(this._startRefreshLoop.bind(this));
    this.rafWin = win;
    this.update();
  },

  _stopRefreshLoop: function () {
    if (this.rafID && !Cu.isDeadWrapper(this.rafWin)) {
      this.rafWin.cancelAnimationFrame(this.rafID);
    }
    this.rafID = this.rafWin = null;
  },

  destroy: function () {
    this.hide();

    this.highlighterEnv = null;
    this.currentNode = null;
  }
};
exports.AutoRefreshHighlighter = AutoRefreshHighlighter;
PK
!<Dҵ�XVXVHchrome/devtools/modules/devtools/server/actors/highlighters/box-model.js /* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { AutoRefreshHighlighter } = require("./auto-refresh");
const {
  CanvasFrameAnonymousContentHelper,
  createNode,
  createSVGNode,
  getBindingElementAndPseudo,
  hasPseudoClassLock,
  isNodeValid,
  moveInfobar,
} = require("./utils/markup");
const {
  setIgnoreLayoutChanges,
  getCurrentZoom,
 } = require("devtools/shared/layout/utils");
const { getNodeDisplayName } = require("devtools/server/actors/inspector/utils");
const nodeConstants = require("devtools/shared/dom-node-constants");

// Note that the order of items in this array is important because it is used
// for drawing the BoxModelHighlighter's path elements correctly.
const BOX_MODEL_REGIONS = ["margin", "border", "padding", "content"];
const BOX_MODEL_SIDES = ["top", "right", "bottom", "left"];
// Width of boxmodelhighlighter guides
const GUIDE_STROKE_WIDTH = 1;
// FIXME: add ":visited" and ":link" after bug 713106 is fixed
const PSEUDO_CLASSES = [":hover", ":active", ":focus"];

/**
 * The BoxModelHighlighter draws the box model regions on top of a node.
 * If the node is a block box, then each region will be displayed as 1 polygon.
 * If the node is an inline box though, each region may be represented by 1 or
 * more polygons, depending on how many line boxes the inline element has.
 *
 * Usage example:
 *
 * let h = new BoxModelHighlighter(env);
 * h.show(node, options);
 * h.hide();
 * h.destroy();
 *
 * Available options:
 * - region {String}
 *   "content", "padding", "border" or "margin"
 *   This specifies the region that the guides should outline.
 *   Defaults to "content"
 * - hideGuides {Boolean}
 *   Defaults to false
 * - hideInfoBar {Boolean}
 *   Defaults to false
 * - showOnly {String}
 *   "content", "padding", "border" or "margin"
 *   If set, only this region will be highlighted. Use with onlyRegionArea to
 *   only highlight the area of the region.
 * - onlyRegionArea {Boolean}
 *   This can be set to true to make each region's box only highlight the area
 *   of the corresponding region rather than the area of nested regions too.
 *   This is useful when used with showOnly.
 *
 * Structure:
 * <div class="highlighter-container">
 *   <div class="box-model-root">
 *     <svg class="box-model-elements" hidden="true">
 *       <g class="box-model-regions">
 *         <path class="box-model-margin" points="..." />
 *         <path class="box-model-border" points="..." />
 *         <path class="box-model-padding" points="..." />
 *         <path class="box-model-content" points="..." />
 *       </g>
 *       <line class="box-model-guide-top" x1="..." y1="..." x2="..." y2="..." />
 *       <line class="box-model-guide-right" x1="..." y1="..." x2="..." y2="..." />
 *       <line class="box-model-guide-bottom" x1="..." y1="..." x2="..." y2="..." />
 *       <line class="box-model-guide-left" x1="..." y1="..." x2="..." y2="..." />
 *     </svg>
 *     <div class="box-model-infobar-container">
 *       <div class="box-model-infobar-arrow highlighter-infobar-arrow-top" />
 *       <div class="box-model-infobar">
 *         <div class="box-model-infobar-text" align="center">
 *           <span class="box-model-infobar-tagname">Node name</span>
 *           <span class="box-model-infobar-id">Node id</span>
 *           <span class="box-model-infobar-classes">.someClass</span>
 *           <span class="box-model-infobar-pseudo-classes">:hover</span>
 *         </div>
 *       </div>
 *       <div class="box-model-infobar-arrow box-model-infobar-arrow-bottom"/>
 *     </div>
 *   </div>
 * </div>
 */
class BoxModelHighlighter extends AutoRefreshHighlighter {
  constructor(highlighterEnv) {
    super(highlighterEnv);

    this.ID_CLASS_PREFIX = "box-model-";

    this.markup = new CanvasFrameAnonymousContentHelper(this.highlighterEnv,
      this._buildMarkup.bind(this));

    /**
     * Optionally customize each region's fill color by adding an entry to the
     * regionFill property: `highlighter.regionFill.margin = "red";
     */
    this.regionFill = {};

    this.onPageHide = this.onPageHide.bind(this);
    this.onWillNavigate = this.onWillNavigate.bind(this);

    this.highlighterEnv.on("will-navigate", this.onWillNavigate);

    let { pageListenerTarget } = highlighterEnv;
    pageListenerTarget.addEventListener("pagehide", this.onPageHide);
  }

  _buildMarkup() {
    let doc = this.win.document;

    let highlighterContainer = doc.createElement("div");
    highlighterContainer.setAttribute("role", "presentation");
    highlighterContainer.className = "highlighter-container box-model";

    // Build the root wrapper, used to adapt to the page zoom.
    let rootWrapper = createNode(this.win, {
      parent: highlighterContainer,
      attributes: {
        "id": "root",
        "class": "root",
        "role": "presentation"
      },
      prefix: this.ID_CLASS_PREFIX
    });

    // Building the SVG element with its polygons and lines

    let svg = createSVGNode(this.win, {
      nodeType: "svg",
      parent: rootWrapper,
      attributes: {
        "id": "elements",
        "width": "100%",
        "height": "100%",
        "hidden": "true",
        "role": "presentation"
      },
      prefix: this.ID_CLASS_PREFIX
    });

    let regions = createSVGNode(this.win, {
      nodeType: "g",
      parent: svg,
      attributes: {
        "class": "regions",
        "role": "presentation"
      },
      prefix: this.ID_CLASS_PREFIX
    });

    for (let region of BOX_MODEL_REGIONS) {
      createSVGNode(this.win, {
        nodeType: "path",
        parent: regions,
        attributes: {
          "class": region,
          "id": region,
          "role": "presentation"
        },
        prefix: this.ID_CLASS_PREFIX
      });
    }

    for (let side of BOX_MODEL_SIDES) {
      createSVGNode(this.win, {
        nodeType: "line",
        parent: svg,
        attributes: {
          "class": "guide-" + side,
          "id": "guide-" + side,
          "stroke-width": GUIDE_STROKE_WIDTH,
          "role": "presentation"
        },
        prefix: this.ID_CLASS_PREFIX
      });
    }

    // Building the nodeinfo bar markup

    let infobarContainer = createNode(this.win, {
      parent: rootWrapper,
      attributes: {
        "class": "infobar-container",
        "id": "infobar-container",
        "position": "top",
        "hidden": "true"
      },
      prefix: this.ID_CLASS_PREFIX
    });

    let infobar = createNode(this.win, {
      parent: infobarContainer,
      attributes: {
        "class": "infobar"
      },
      prefix: this.ID_CLASS_PREFIX
    });

    let texthbox = createNode(this.win, {
      parent: infobar,
      attributes: {
        "class": "infobar-text"
      },
      prefix: this.ID_CLASS_PREFIX
    });
    createNode(this.win, {
      nodeType: "span",
      parent: texthbox,
      attributes: {
        "class": "infobar-tagname",
        "id": "infobar-tagname"
      },
      prefix: this.ID_CLASS_PREFIX
    });
    createNode(this.win, {
      nodeType: "span",
      parent: texthbox,
      attributes: {
        "class": "infobar-id",
        "id": "infobar-id"
      },
      prefix: this.ID_CLASS_PREFIX
    });
    createNode(this.win, {
      nodeType: "span",
      parent: texthbox,
      attributes: {
        "class": "infobar-classes",
        "id": "infobar-classes"
      },
      prefix: this.ID_CLASS_PREFIX
    });
    createNode(this.win, {
      nodeType: "span",
      parent: texthbox,
      attributes: {
        "class": "infobar-pseudo-classes",
        "id": "infobar-pseudo-classes"
      },
      prefix: this.ID_CLASS_PREFIX
    });
    createNode(this.win, {
      nodeType: "span",
      parent: texthbox,
      attributes: {
        "class": "infobar-dimensions",
        "id": "infobar-dimensions"
      },
      prefix: this.ID_CLASS_PREFIX
    });

    return highlighterContainer;
  }

  /**
   * Destroy the nodes. Remove listeners.
   */
  destroy() {
    this.highlighterEnv.off("will-navigate", this.onWillNavigate);

    let { pageListenerTarget } = this.highlighterEnv;
    if (pageListenerTarget) {
      pageListenerTarget.removeEventListener("pagehide", this.onPageHide);
    }

    this.markup.destroy();
    AutoRefreshHighlighter.prototype.destroy.call(this);
  }

  getElement(id) {
    return this.markup.getElement(this.ID_CLASS_PREFIX + id);
  }

  /**
   * Override the AutoRefreshHighlighter's _isNodeValid method to also return true for
   * text nodes since these can also be highlighted.
   * @param {DOMNode} node
   * @return {Boolean}
   */
  _isNodeValid(node) {
    return node && (isNodeValid(node) || isNodeValid(node, nodeConstants.TEXT_NODE));
  }

  /**
   * Show the highlighter on a given node
   */
  _show() {
    if (!BOX_MODEL_REGIONS.includes(this.options.region)) {
      this.options.region = "content";
    }

    let shown = this._update();
    this._trackMutations();
    this.emit("ready");
    return shown;
  }

  /**
   * Track the current node markup mutations so that the node info bar can be
   * updated to reflects the node's attributes
   */
  _trackMutations() {
    if (isNodeValid(this.currentNode)) {
      let win = this.currentNode.ownerGlobal;
      this.currentNodeObserver = new win.MutationObserver(this.update);
      this.currentNodeObserver.observe(this.currentNode, {attributes: true});
    }
  }

  _untrackMutations() {
    if (isNodeValid(this.currentNode) && this.currentNodeObserver) {
      this.currentNodeObserver.disconnect();
      this.currentNodeObserver = null;
    }
  }

  /**
   * Update the highlighter on the current highlighted node (the one that was
   * passed as an argument to show(node)).
   * Should be called whenever node size or attributes change
   */
  _update() {
    let shown = false;
    setIgnoreLayoutChanges(true);

    if (this._updateBoxModel()) {
      // Show the infobar only if configured to do so and the node is an element or a text
      // node.
      if (!this.options.hideInfoBar && (
          this.currentNode.nodeType === this.currentNode.ELEMENT_NODE ||
          this.currentNode.nodeType === this.currentNode.TEXT_NODE)) {
        this._showInfobar();
      } else {
        this._hideInfobar();
      }
      this._showBoxModel();
      shown = true;
    } else {
      // Nothing to highlight (0px rectangle like a <script> tag for instance)
      this._hide();
    }

    setIgnoreLayoutChanges(false, this.highlighterEnv.window.document.documentElement);

    return shown;
  }

  _scrollUpdate() {
    this._moveInfobar();
  }

  /**
   * Hide the highlighter, the outline and the infobar.
   */
  _hide() {
    setIgnoreLayoutChanges(true);

    this._untrackMutations();
    this._hideBoxModel();
    this._hideInfobar();

    setIgnoreLayoutChanges(false, this.highlighterEnv.window.document.documentElement);
  }

  /**
   * Hide the infobar
   */
  _hideInfobar() {
    this.getElement("infobar-container").setAttribute("hidden", "true");
  }

  /**
   * Show the infobar
   */
  _showInfobar() {
    this.getElement("infobar-container").removeAttribute("hidden");
    this._updateInfobar();
  }

  /**
   * Hide the box model
   */
  _hideBoxModel() {
    this.getElement("elements").setAttribute("hidden", "true");
  }

  /**
   * Show the box model
   */
  _showBoxModel() {
    this.getElement("elements").removeAttribute("hidden");
  }

  /**
   * Calculate an outer quad based on the quads returned by getAdjustedQuads.
   * The BoxModelHighlighter may highlight more than one boxes, so in this case
   * create a new quad that "contains" all of these quads.
   * This is useful to position the guides and infobar.
   * This may happen if the BoxModelHighlighter is used to highlight an inline
   * element that spans line breaks.
   * @param {String} region The box-model region to get the outer quad for.
   * @return {Object} A quad-like object {p1,p2,p3,p4,bounds}
   */
  _getOuterQuad(region) {
    let quads = this.currentQuads[region];
    if (!quads.length) {
      return null;
    }

    let quad = {
      p1: {x: Infinity, y: Infinity},
      p2: {x: -Infinity, y: Infinity},
      p3: {x: -Infinity, y: -Infinity},
      p4: {x: Infinity, y: -Infinity},
      bounds: {
        bottom: -Infinity,
        height: 0,
        left: Infinity,
        right: -Infinity,
        top: Infinity,
        width: 0,
        x: 0,
        y: 0,
      }
    };

    for (let q of quads) {
      quad.p1.x = Math.min(quad.p1.x, q.p1.x);
      quad.p1.y = Math.min(quad.p1.y, q.p1.y);
      quad.p2.x = Math.max(quad.p2.x, q.p2.x);
      quad.p2.y = Math.min(quad.p2.y, q.p2.y);
      quad.p3.x = Math.max(quad.p3.x, q.p3.x);
      quad.p3.y = Math.max(quad.p3.y, q.p3.y);
      quad.p4.x = Math.min(quad.p4.x, q.p4.x);
      quad.p4.y = Math.max(quad.p4.y, q.p4.y);

      quad.bounds.bottom = Math.max(quad.bounds.bottom, q.bounds.bottom);
      quad.bounds.top = Math.min(quad.bounds.top, q.bounds.top);
      quad.bounds.left = Math.min(quad.bounds.left, q.bounds.left);
      quad.bounds.right = Math.max(quad.bounds.right, q.bounds.right);
    }
    quad.bounds.x = quad.bounds.left;
    quad.bounds.y = quad.bounds.top;
    quad.bounds.width = quad.bounds.right - quad.bounds.left;
    quad.bounds.height = quad.bounds.bottom - quad.bounds.top;

    return quad;
  }

  /**
   * Update the box model as per the current node.
   *
   * @return {boolean}
   *         True if the current node has a box model to be highlighted
   */
  _updateBoxModel() {
    let options = this.options;
    options.region = options.region || "content";

    if (!this._nodeNeedsHighlighting()) {
      this._hideBoxModel();
      return false;
    }

    for (let i = 0; i < BOX_MODEL_REGIONS.length; i++) {
      let boxType = BOX_MODEL_REGIONS[i];
      let nextBoxType = BOX_MODEL_REGIONS[i + 1];
      let box = this.getElement(boxType);

      if (this.regionFill[boxType]) {
        box.setAttribute("style", "fill:" + this.regionFill[boxType]);
      } else {
        box.setAttribute("style", "");
      }

      // Highlight all quads for this region by setting the "d" attribute of the
      // corresponding <path>.
      let path = [];
      for (let j = 0; j < this.currentQuads[boxType].length; j++) {
        let boxQuad = this.currentQuads[boxType][j];
        let nextBoxQuad = this.currentQuads[nextBoxType]
                          ? this.currentQuads[nextBoxType][j]
                          : null;
        path.push(this._getBoxPathCoordinates(boxQuad, nextBoxQuad));
      }

      box.setAttribute("d", path.join(" "));
      box.removeAttribute("faded");

      // If showOnly is defined, either hide the other regions, or fade them out
      // if onlyRegionArea is set too.
      if (options.showOnly && options.showOnly !== boxType) {
        if (options.onlyRegionArea) {
          box.setAttribute("faded", "true");
        } else {
          box.removeAttribute("d");
        }
      }

      if (boxType === options.region && !options.hideGuides) {
        this._showGuides(boxType);
      } else if (options.hideGuides) {
        this._hideGuides();
      }
    }

    // Un-zoom the root wrapper if the page was zoomed.
    let rootId = this.ID_CLASS_PREFIX + "elements";
    this.markup.scaleRootElement(this.currentNode, rootId);

    return true;
  }

  _getBoxPathCoordinates(boxQuad, nextBoxQuad) {
    let {p1, p2, p3, p4} = boxQuad;

    let path;
    if (!nextBoxQuad || !this.options.onlyRegionArea) {
      // If this is the content box (inner-most box) or if we're not being asked
      // to highlight only region areas, then draw a simple rectangle.
      path = "M" + p1.x + "," + p1.y + " " +
             "L" + p2.x + "," + p2.y + " " +
             "L" + p3.x + "," + p3.y + " " +
             "L" + p4.x + "," + p4.y;
    } else {
      // Otherwise, just draw the region itself, not a filled rectangle.
      let {p1: np1, p2: np2, p3: np3, p4: np4} = nextBoxQuad;
      path = "M" + p1.x + "," + p1.y + " " +
             "L" + p2.x + "," + p2.y + " " +
             "L" + p3.x + "," + p3.y + " " +
             "L" + p4.x + "," + p4.y + " " +
             "L" + p1.x + "," + p1.y + " " +
             "L" + np1.x + "," + np1.y + " " +
             "L" + np4.x + "," + np4.y + " " +
             "L" + np3.x + "," + np3.y + " " +
             "L" + np2.x + "," + np2.y + " " +
             "L" + np1.x + "," + np1.y;
    }

    return path;
  }

  /**
   * Can the current node be highlighted? Does it have quads.
   * @return {Boolean}
   */
  _nodeNeedsHighlighting() {
    return this.currentQuads.margin.length ||
           this.currentQuads.border.length ||
           this.currentQuads.padding.length ||
           this.currentQuads.content.length;
  }

  _getOuterBounds() {
    for (let region of ["margin", "border", "padding", "content"]) {
      let quad = this._getOuterQuad(region);

      if (!quad) {
        // Invisible element such as a script tag.
        break;
      }

      let {bottom, height, left, right, top, width, x, y} = quad.bounds;

      if (width > 0 || height > 0) {
        return {bottom, height, left, right, top, width, x, y};
      }
    }

    return {
      bottom: 0,
      height: 0,
      left: 0,
      right: 0,
      top: 0,
      width: 0,
      x: 0,
      y: 0
    };
  }

  /**
   * We only want to show guides for horizontal and vertical edges as this helps
   * to line them up. This method finds these edges and displays a guide there.
   * @param {String} region The region around which the guides should be shown.
   */
  _showGuides(region) {
    let {p1, p2, p3, p4} = this._getOuterQuad(region);

    let allX = [p1.x, p2.x, p3.x, p4.x].sort((a, b) => a - b);
    let allY = [p1.y, p2.y, p3.y, p4.y].sort((a, b) => a - b);
    let toShowX = [];
    let toShowY = [];

    for (let arr of [allX, allY]) {
      for (let i = 0; i < arr.length; i++) {
        let val = arr[i];

        if (i !== arr.lastIndexOf(val)) {
          if (arr === allX) {
            toShowX.push(val);
          } else {
            toShowY.push(val);
          }
          arr.splice(arr.lastIndexOf(val), 1);
        }
      }
    }

    // Move guide into place or hide it if no valid co-ordinate was found.
    this._updateGuide("top", Math.round(toShowY[0]));
    this._updateGuide("right", Math.round(toShowX[1]) - 1);
    this._updateGuide("bottom", Math.round(toShowY[1] - 1));
    this._updateGuide("left", Math.round(toShowX[0]));
  }

  _hideGuides() {
    for (let side of BOX_MODEL_SIDES) {
      this.getElement("guide-" + side).setAttribute("hidden", "true");
    }
  }

  /**
   * Move a guide to the appropriate position and display it. If no point is
   * passed then the guide is hidden.
   *
   * @param  {String} side
   *         The guide to update
   * @param  {Integer} point
   *         x or y co-ordinate. If this is undefined we hide the guide.
   */
  _updateGuide(side, point = -1) {
    let guide = this.getElement("guide-" + side);

    if (point <= 0) {
      guide.setAttribute("hidden", "true");
      return false;
    }

    if (side === "top" || side === "bottom") {
      guide.setAttribute("x1", "0");
      guide.setAttribute("y1", point + "");
      guide.setAttribute("x2", "100%");
      guide.setAttribute("y2", point + "");
    } else {
      guide.setAttribute("x1", point + "");
      guide.setAttribute("y1", "0");
      guide.setAttribute("x2", point + "");
      guide.setAttribute("y2", "100%");
    }

    guide.removeAttribute("hidden");

    return true;
  }

  /**
   * Update node information (displayName#id.class)
   */
  _updateInfobar() {
    if (!this.currentNode) {
      return;
    }

    let {bindingElement: node, pseudo} =
        getBindingElementAndPseudo(this.currentNode);

    // Update the tag, id, classes, pseudo-classes and dimensions
    let displayName = getNodeDisplayName(node);

    let id = node.id ? "#" + node.id : "";

    let classList = (node.classList || []).length
                    ? "." + [...node.classList].join(".")
                    : "";

    let pseudos = this._getPseudoClasses(node).join("");
    if (pseudo) {
      // Display :after as ::after
      pseudos += ":" + pseudo;
    }

    // We want to display the original `width` and `height`, instead of the ones affected
    // by any zoom. Since the infobar can be displayed also for text nodes, we can't
    // access the computed style for that, and this is why we recalculate them here.
    let zoom = getCurrentZoom(this.win);
    let { width, height } = this._getOuterQuad("border").bounds;
    let dim = parseFloat((width / zoom).toPrecision(6)) +
              " \u00D7 " +
              parseFloat((height / zoom).toPrecision(6));

    this.getElement("infobar-tagname").setTextContent(displayName);
    this.getElement("infobar-id").setTextContent(id);
    this.getElement("infobar-classes").setTextContent(classList);
    this.getElement("infobar-pseudo-classes").setTextContent(pseudos);
    this.getElement("infobar-dimensions").setTextContent(dim);

    this._moveInfobar();
  }

  _getPseudoClasses(node) {
    if (node.nodeType !== nodeConstants.ELEMENT_NODE) {
      // hasPseudoClassLock can only be used on Elements.
      return [];
    }

    return PSEUDO_CLASSES.filter(pseudo => hasPseudoClassLock(node, pseudo));
  }

  /**
   * Move the Infobar to the right place in the highlighter.
   */
  _moveInfobar() {
    let bounds = this._getOuterBounds();
    let container = this.getElement("infobar-container");

    moveInfobar(container, bounds, this.win);
  }

  onPageHide({ target }) {
    // If a pagehide event is triggered for current window's highlighter, hide the
    // highlighter.
    if (target.defaultView === this.win) {
      this.hide();
    }
  }

  onWillNavigate({ isTopLevel }) {
    if (isTopLevel) {
      this.hide();
    }
  }
}

exports.BoxModelHighlighter = BoxModelHighlighter;
PK
!<7��/�/�Gchrome/devtools/modules/devtools/server/actors/highlighters/css-grid.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { AutoRefreshHighlighter } = require("./auto-refresh");
const {
  CANVAS_SIZE,
  DEFAULT_COLOR,
  drawBubbleRect,
  drawLine,
  drawRect,
  drawRoundedRect,
  getBoundsFromPoints,
  getCurrentMatrix,
  getPathDescriptionFromPoints,
  getPointsFromDiagonal,
  updateCanvasElement,
  updateCanvasPosition,
} = require("./utils/canvas");
const {
  CanvasFrameAnonymousContentHelper,
  createNode,
  createSVGNode,
  getComputedStyle,
  moveInfobar,
} = require("./utils/markup");
const { apply } = require("devtools/shared/layout/dom-matrix-2d");
const {
  getCurrentZoom,
  getDisplayPixelRatio,
  setIgnoreLayoutChanges,
} = require("devtools/shared/layout/utils");
const { stringifyGridFragments } = require("devtools/server/actors/utils/css-grid-utils");
const { LocalizationHelper } = require("devtools/shared/l10n");

const LAYOUT_STRINGS_URI = "devtools/client/locales/layout.properties";
const LAYOUT_L10N = new LocalizationHelper(LAYOUT_STRINGS_URI);

const COLUMNS = "cols";
const ROWS = "rows";

const GRID_FONT_SIZE = 10;
const GRID_FONT_FAMILY = "sans-serif";
const GRID_AREA_NAME_FONT_SIZE = "20";

const GRID_LINES_PROPERTIES = {
  "edge": {
    lineDash: [0, 0],
    alpha: 1,
  },
  "explicit": {
    lineDash: [5, 3],
    alpha: 0.75,
  },
  "implicit": {
    lineDash: [2, 2],
    alpha: 0.5,
  },
  "areaEdge": {
    lineDash: [0, 0],
    alpha: 1,
    lineWidth: 3,
  }
};

const GRID_GAP_PATTERN_WIDTH = 14; // px
const GRID_GAP_PATTERN_HEIGHT = 14; // px
const GRID_GAP_PATTERN_LINE_DASH = [5, 3]; // px
const GRID_GAP_ALPHA = 0.5;

// 25 is a good margin distance between the document grid container edge without cutting
// off parts of the arrow box container.
const OFFSET_FROM_EDGE = 25;

/**
 * Given an `edge` of a box, return the name of the edge one move to the right.
 */
function rotateEdgeRight(edge) {
  switch (edge) {
    case "top": return "right";
    case "right": return "bottom";
    case "bottom": return "left";
    case "left": return "top";
    default: return edge;
  }
}

/**
 * Given an `edge` of a box, return the name of the edge one move to the left.
 */
function rotateEdgeLeft(edge) {
  switch (edge) {
    case "top": return "left";
    case "right": return "top";
    case "bottom": return "right";
    case "left": return "bottom";
    default: return edge;
  }
}

/**
 * Given an `edge` of a box, return the name of the opposite edge.
 */
function reflectEdge(edge) {
  switch (edge) {
    case "top": return "bottom";
    case "right": return "left";
    case "bottom": return "top";
    case "left": return "right";
    default: return edge;
  }
}

/**
 * Cached used by `CssGridHighlighter.getGridGapPattern`.
 */
const gCachedGridPattern = new Map();

/**
 * The CssGridHighlighter is the class that overlays a visual grid on top of
 * display:[inline-]grid elements.
 *
 * Usage example:
 * let h = new CssGridHighlighter(env);
 * h.show(node, options);
 * h.hide();
 * h.destroy();
 *
 * Available Options:
 * - color(colorValue)
 *     @param  {String} colorValue
 *     The color that should be used to draw the highlighter for this grid.
 * - showAllGridAreas(isShown)
 *     @param  {Boolean} isShown
 *     Shows all the grid area highlights for the current grid if isShown is true.
 * - showGridArea(areaName)
 *     @param  {String} areaName
 *     Shows the grid area highlight for the given area name.
 * - showGridAreasOverlay(isShown)
 *     @param  {Boolean} isShown
 *     Displays an overlay of all the grid areas for the current grid container if
 *     isShown is true.
 * - showGridCell({ gridFragmentIndex: Number, rowNumber: Number, columnNumber: Number })
 *     @param  {Object} { gridFragmentIndex: Number, rowNumber: Number,
 *                        columnNumber: Number }
 *     An object containing the grid fragment index, row and column numbers to the
 *     corresponding grid cell to highlight for the current grid.
 * - showGridLineNames({ gridFragmentIndex: Number, lineNumber: Number,
 *                       type: String })
 *     @param  {Object} { gridFragmentIndex: Number, lineNumber: Number }
 *     An object containing the grid fragment index and line number to the
 *     corresponding grid line to highlight for the current grid.
 * - showGridLineNumbers(isShown)
 *     @param  {Boolean} isShown
 *     Displays the grid line numbers on the grid lines if isShown is true.
 * - showInfiniteLines(isShown)
 *     @param  {Boolean} isShown
 *     Displays an infinite line to represent the grid lines if isShown is true.
 *
 * Structure:
 * <div class="highlighter-container">
 *   <canvas id="css-grid-canvas" class="css-grid-canvas">
 *   <svg class="css-grid-elements" hidden="true">
 *     <g class="css-grid-regions">
 *       <path class="css-grid-areas" points="..." />
 *       <path class="css-grid-cells" points="..." />
 *     </g>
 *   </svg>
 *   <div class="css-grid-area-infobar-container">
 *     <div class="css-grid-infobar">
 *       <div class="css-grid-infobar-text">
 *         <span class="css-grid-area-infobar-name">Grid Area Name</span>
 *         <span class="css-grid-area-infobar-dimensions">Grid Area Dimensions></span>
 *       </div>
 *     </div>
 *   </div>
 *   <div class="css-grid-cell-infobar-container">
 *     <div class="css-grid-infobar">
 *       <div class="css-grid-infobar-text">
 *         <span class="css-grid-cell-infobar-position">Grid Cell Position</span>
 *         <span class="css-grid-cell-infobar-dimensions">Grid Cell Dimensions></span>
 *       </div>
 *     </div>
 *   <div class="css-grid-line-infobar-container">
 *     <div class="css-grid-infobar">
 *       <div class="css-grid-infobar-text">
 *         <span class="css-grid-line-infobar-number">Grid Line Number</span>
 *         <span class="css-grid-line-infobar-names">Grid Line Names></span>
 *       </div>
 *     </div>
 *   </div>
 * </div>
 */
class CssGridHighlighter extends AutoRefreshHighlighter {
  constructor(highlighterEnv) {
    super(highlighterEnv);

    this.ID_CLASS_PREFIX = "css-grid-";

    this.markup = new CanvasFrameAnonymousContentHelper(this.highlighterEnv,
      this._buildMarkup.bind(this));

    this.onPageHide = this.onPageHide.bind(this);
    this.onWillNavigate = this.onWillNavigate.bind(this);

    this.highlighterEnv.on("will-navigate", this.onWillNavigate);

    let { pageListenerTarget } = highlighterEnv;
    pageListenerTarget.addEventListener("pagehide", this.onPageHide);

    // Initialize the <canvas> position to the top left corner of the page.
    this._canvasPosition = {
      x: 0,
      y: 0
    };

    // Calling `updateCanvasPosition` anyway since the highlighter could be initialized
    // on a page that has scrolled already.
    updateCanvasPosition(this._canvasPosition, this._scroll, this.win,
      this._winDimensions);
  }

  _buildMarkup() {
    let container = createNode(this.win, {
      attributes: {
        "class": "highlighter-container"
      }
    });

    let root = createNode(this.win, {
      parent: container,
      attributes: {
        "id": "root",
        "class": "root"
      },
      prefix: this.ID_CLASS_PREFIX
    });

    // We use a <canvas> element so that we can draw an arbitrary number of lines
    // which wouldn't be possible with HTML or SVG without having to insert and remove
    // the whole markup on every update.
    createNode(this.win, {
      parent: root,
      nodeType: "canvas",
      attributes: {
        "id": "canvas",
        "class": "canvas",
        "hidden": "true",
        "width": CANVAS_SIZE,
        "height": CANVAS_SIZE
      },
      prefix: this.ID_CLASS_PREFIX
    });

    // Build the SVG element.
    let svg = createSVGNode(this.win, {
      nodeType: "svg",
      parent: root,
      attributes: {
        "id": "elements",
        "width": "100%",
        "height": "100%",
        "hidden": "true"
      },
      prefix: this.ID_CLASS_PREFIX
    });

    let regions = createSVGNode(this.win, {
      nodeType: "g",
      parent: svg,
      attributes: {
        "class": "regions"
      },
      prefix: this.ID_CLASS_PREFIX
    });

    createSVGNode(this.win, {
      nodeType: "path",
      parent: regions,
      attributes: {
        "class": "areas",
        "id": "areas"
      },
      prefix: this.ID_CLASS_PREFIX
    });

    createSVGNode(this.win, {
      nodeType: "path",
      parent: regions,
      attributes: {
        "class": "cells",
        "id": "cells"
      },
      prefix: this.ID_CLASS_PREFIX
    });

    // Build the grid area infobar markup.
    let areaInfobarContainer = createNode(this.win, {
      parent: container,
      attributes: {
        "class": "area-infobar-container",
        "id": "area-infobar-container",
        "position": "top",
        "hidden": "true"
      },
      prefix: this.ID_CLASS_PREFIX
    });

    let areaInfobar = createNode(this.win, {
      parent: areaInfobarContainer,
      attributes: {
        "class": "infobar"
      },
      prefix: this.ID_CLASS_PREFIX
    });

    let areaTextbox = createNode(this.win, {
      parent: areaInfobar,
      attributes: {
        "class": "infobar-text"
      },
      prefix: this.ID_CLASS_PREFIX
    });
    createNode(this.win, {
      nodeType: "span",
      parent: areaTextbox,
      attributes: {
        "class": "area-infobar-name",
        "id": "area-infobar-name"
      },
      prefix: this.ID_CLASS_PREFIX
    });
    createNode(this.win, {
      nodeType: "span",
      parent: areaTextbox,
      attributes: {
        "class": "area-infobar-dimensions",
        "id": "area-infobar-dimensions"
      },
      prefix: this.ID_CLASS_PREFIX
    });

    // Build the grid cell infobar markup.
    let cellInfobarContainer = createNode(this.win, {
      parent: container,
      attributes: {
        "class": "cell-infobar-container",
        "id": "cell-infobar-container",
        "position": "top",
        "hidden": "true"
      },
      prefix: this.ID_CLASS_PREFIX
    });

    let cellInfobar = createNode(this.win, {
      parent: cellInfobarContainer,
      attributes: {
        "class": "infobar"
      },
      prefix: this.ID_CLASS_PREFIX
    });

    let cellTextbox = createNode(this.win, {
      parent: cellInfobar,
      attributes: {
        "class": "infobar-text"
      },
      prefix: this.ID_CLASS_PREFIX
    });
    createNode(this.win, {
      nodeType: "span",
      parent: cellTextbox,
      attributes: {
        "class": "cell-infobar-position",
        "id": "cell-infobar-position"
      },
      prefix: this.ID_CLASS_PREFIX
    });
    createNode(this.win, {
      nodeType: "span",
      parent: cellTextbox,
      attributes: {
        "class": "cell-infobar-dimensions",
        "id": "cell-infobar-dimensions"
      },
      prefix: this.ID_CLASS_PREFIX
    });

    // Build the grid line infobar markup.
    let lineInfobarContainer = createNode(this.win, {
      parent: container,
      attributes: {
        "class": "line-infobar-container",
        "id": "line-infobar-container",
        "position": "top",
        "hidden": "true"
      },
      prefix: this.ID_CLASS_PREFIX
    });

    let lineInfobar = createNode(this.win, {
      parent: lineInfobarContainer,
      attributes: {
        "class": "infobar"
      },
      prefix: this.ID_CLASS_PREFIX
    });

    let lineTextbox = createNode(this.win, {
      parent: lineInfobar,
      attributes: {
        "class": "infobar-text"
      },
      prefix: this.ID_CLASS_PREFIX
    });
    createNode(this.win, {
      nodeType: "span",
      parent: lineTextbox,
      attributes: {
        "class": "line-infobar-number",
        "id": "line-infobar-number"
      },
      prefix: this.ID_CLASS_PREFIX
    });
    createNode(this.win, {
      nodeType: "span",
      parent: lineTextbox,
      attributes: {
        "class": "line-infobar-names",
        "id": "line-infobar-names"
      },
      prefix: this.ID_CLASS_PREFIX
    });

    return container;
  }

  clearCache() {
    gCachedGridPattern.clear();
  }

  /**
   * Clear the grid area highlights.
   */
  clearGridAreas() {
    let areas = this.getElement("areas");
    areas.setAttribute("d", "");
  }

  /**
   * Clear the grid cell highlights.
   */
  clearGridCell() {
    let cells = this.getElement("cells");
    cells.setAttribute("d", "");
  }

  destroy() {
    let { highlighterEnv } = this;
    highlighterEnv.off("will-navigate", this.onWillNavigate);

    let { pageListenerTarget } = highlighterEnv;
    if (pageListenerTarget) {
      pageListenerTarget.removeEventListener("pagehide", this.onPageHide);
    }

    this.markup.destroy();

    // Clear the pattern cache to avoid dead object exceptions (Bug 1342051).
    this.clearCache();
    AutoRefreshHighlighter.prototype.destroy.call(this);
  }

  get canvas() {
    return this.getElement("canvas");
  }

  get color() {
    return this.options.color || DEFAULT_COLOR;
  }

  get ctx() {
    return this.canvas.getCanvasContext("2d");
  }

  getElement(id) {
    return this.markup.getElement(this.ID_CLASS_PREFIX + id);
  }

  getFirstColLinePos(fragment) {
    return fragment.cols.lines[0].start;
  }

  getFirstRowLinePos(fragment) {
    return fragment.rows.lines[0].start;
  }

  /**
   * Gets the grid gap pattern used to render the gap regions based on the device
   * pixel ratio given.
   *
   * @param  {Number} devicePixelRatio
   *         The device pixel ratio we want the pattern for.
   * @param  {Object} dimension
   *         Refers to the Map key for the grid dimension type which is either the
   *         constant COLUMNS or ROWS.
   * @return {CanvasPattern} grid gap pattern.
   */
  getGridGapPattern(devicePixelRatio, dimension) {
    let gridPatternMap = null;

    if (gCachedGridPattern.has(devicePixelRatio)) {
      gridPatternMap = gCachedGridPattern.get(devicePixelRatio);
    } else {
      gridPatternMap = new Map();
    }

    if (gridPatternMap.has(dimension)) {
      return gridPatternMap.get(dimension);
    }

    // Create the diagonal lines pattern for the rendering the grid gaps.
    let canvas = createNode(this.win, { nodeType: "canvas" });
    let width = canvas.width = GRID_GAP_PATTERN_WIDTH * devicePixelRatio;
    let height = canvas.height = GRID_GAP_PATTERN_HEIGHT * devicePixelRatio;

    let ctx = canvas.getContext("2d");
    ctx.save();
    ctx.setLineDash(GRID_GAP_PATTERN_LINE_DASH);
    ctx.beginPath();
    ctx.translate(.5, .5);

    if (dimension === COLUMNS) {
      ctx.moveTo(0, 0);
      ctx.lineTo(width, height);
    } else {
      ctx.moveTo(width, 0);
      ctx.lineTo(0, height);
    }

    ctx.strokeStyle = this.color;
    ctx.globalAlpha = GRID_GAP_ALPHA;
    ctx.stroke();
    ctx.restore();

    let pattern = ctx.createPattern(canvas, "repeat");

    gridPatternMap.set(dimension, pattern);
    gCachedGridPattern.set(devicePixelRatio, gridPatternMap);

    return pattern;
  }

  getLastColLinePos(fragment) {
    return fragment.cols.lines[fragment.cols.lines.length - 1].start;
  }

  /**
   * Get the GridLine index of the last edge of the explicit grid for a grid dimension.
   *
   * @param  {GridTracks} tracks
   *         The grid track of a given grid dimension.
   * @return {Number} index of the last edge of the explicit grid for a grid dimension.
   */
  getLastEdgeLineIndex(tracks) {
    let trackIndex = tracks.length - 1;

    // Traverse the grid track backwards until we find an explicit track.
    while (trackIndex >= 0 && tracks[trackIndex].type != "explicit") {
      trackIndex--;
    }

    // The grid line index is the grid track index + 1.
    return trackIndex + 1;
  }

  getLastRowLinePos(fragment) {
    return fragment.rows.lines[fragment.rows.lines.length - 1].start;
  }

  /**
   * The AutoRefreshHighlighter's _hasMoved method returns true only if the
   * element's quads have changed. Override it so it also returns true if the
   * element's grid has changed (which can happen when you change the
   * grid-template-* CSS properties with the highlighter displayed).
   */
  _hasMoved() {
    let hasMoved = AutoRefreshHighlighter.prototype._hasMoved.call(this);

    let oldGridData = stringifyGridFragments(this.gridData);
    this.gridData = this.currentNode.getGridFragments();
    let newGridData = stringifyGridFragments(this.gridData);

    return hasMoved || oldGridData !== newGridData;
  }

  /**
   * Hide the highlighter, the canvas and the infobars.
   */
  _hide() {
    setIgnoreLayoutChanges(true);
    this._hideGrid();
    this._hideGridElements();
    this._hideGridAreaInfoBar();
    this._hideGridCellInfoBar();
    this._hideGridLineInfoBar();
    setIgnoreLayoutChanges(false, this.highlighterEnv.document.documentElement);
  }

  _hideGrid() {
    this.getElement("canvas").setAttribute("hidden", "true");
  }

  _hideGridAreaInfoBar() {
    this.getElement("area-infobar-container").setAttribute("hidden", "true");
  }

  _hideGridCellInfoBar() {
    this.getElement("cell-infobar-container").setAttribute("hidden", "true");
  }

  _hideGridElements() {
    this.getElement("elements").setAttribute("hidden", "true");
  }

  _hideGridLineInfoBar() {
    this.getElement("line-infobar-container").setAttribute("hidden", "true");
  }

  /**
   * Checks if the current node has a CSS Grid layout.
   *
   * @return {Boolean} true if the current node has a CSS grid layout, false otherwise.
   */
  isGrid() {
    return this.currentNode.getGridFragments().length > 0;
  }

  /**
   * Is a given grid fragment valid? i.e. does it actually have tracks? In some cases, we
   * may have a fragment that defines column tracks but doesn't have any rows (or vice
   * versa). In which case we do not want to draw anything for that fragment.
   *
   * @param  {Object} fragment
   * @return {Boolean}
   */
  isValidFragment(fragment) {
    return fragment.cols.tracks.length && fragment.rows.tracks.length;
  }

  /**
   * The <canvas>'s position needs to be updated if the page scrolls too much, in order
   * to give the illusion that it always covers the viewport.
   */
  _scrollUpdate() {
    let hasUpdated = updateCanvasPosition(this._canvasPosition, this._scroll, this.win,
      this._winDimensions);

    if (hasUpdated) {
      this._update();
    }
  }

  _show() {
    if (!this.isGrid()) {
      this.hide();
      return false;
    }

    // The grid pattern cache should be cleared in case the color changed.
    this.clearCache();

    // Hide the canvas, grid element highlights and infobar.
    this._hide();

    return this._update();
  }

  _showGrid() {
    this.getElement("canvas").removeAttribute("hidden");
  }

  _showGridAreaInfoBar() {
    this.getElement("area-infobar-container").removeAttribute("hidden");
  }

  _showGridCellInfoBar() {
    this.getElement("cell-infobar-container").removeAttribute("hidden");
  }

  _showGridElements() {
    this.getElement("elements").removeAttribute("hidden");
  }

  _showGridLineInfoBar() {
    this.getElement("line-infobar-container").removeAttribute("hidden");
  }

  /**
   * Shows all the grid area highlights for the current grid.
   */
  showAllGridAreas() {
    this.renderGridArea();
  }

  /**
   * Shows the grid area highlight for the given area name.
   *
   * @param  {String} areaName
   *         Grid area name.
   */
  showGridArea(areaName) {
    this.renderGridArea(areaName);
  }

  /**
   * Shows the grid cell highlight for the given grid cell options.
   *
   * @param  {Number} options.gridFragmentIndex
   *         Index of the grid fragment to render the grid cell highlight.
   * @param  {Number} options.rowNumber
   *         Row number of the grid cell to highlight.
   * @param  {Number} options.columnNumber
   *         Column number of the grid cell to highlight.
   */
  showGridCell({ gridFragmentIndex, rowNumber, columnNumber }) {
    this.renderGridCell(gridFragmentIndex, rowNumber, columnNumber);
  }

  /**
   * Shows the grid line highlight for the given grid line options.
   *
   * @param  {Number} options.gridFragmentIndex
   *         Index of the grid fragment to render the grid line highlight.
   * @param  {Number} options.lineNumber
   *         Line number of the grid line to highlight.
   * @param  {String} options.type
   *         The dimension type of the grid line.
   */
  showGridLineNames({ gridFragmentIndex, lineNumber, type }) {
    this.renderGridLineNames(gridFragmentIndex, lineNumber, type);
  }

  /**
   * If a page hide event is triggered for current window's highlighter, hide the
   * highlighter.
   */
  onPageHide({ target }) {
    if (target.defaultView === this.win) {
      this.hide();
    }
  }

  /**
   * Called when the page will-navigate. Used to hide the grid highlighter and clear
   * the cached gap patterns and avoid using DeadWrapper obejcts as gap patterns the
   * next time.
   */
  onWillNavigate({ isTopLevel }) {
    this.clearCache();

    if (isTopLevel) {
      this.hide();
    }
  }

  renderFragment(fragment) {
    if (!this.isValidFragment(fragment)) {
      return;
    }

    this.renderLines(fragment.cols, COLUMNS, this.getFirstRowLinePos(fragment),
      this.getLastRowLinePos(fragment));
    this.renderLines(fragment.rows, ROWS, this.getFirstColLinePos(fragment),
      this.getLastColLinePos(fragment));

    if (this.options.showGridAreasOverlay) {
      this.renderGridAreaOverlay();
    }

    // Line numbers are rendered in a 2nd step to avoid overlapping with existing lines.
    if (this.options.showGridLineNumbers) {
      this.renderLineNumbers(fragment.cols, COLUMNS, this.getFirstRowLinePos(fragment));
      this.renderLineNumbers(fragment.rows, ROWS, this.getFirstColLinePos(fragment));
      this.renderNegativeLineNumbers(fragment.cols, COLUMNS,
        this.getLastRowLinePos(fragment));
      this.renderNegativeLineNumbers(fragment.rows, ROWS,
        this.getLastColLinePos(fragment));
    }
  }

  /**
   * Render the grid area highlight for the given area name or for all the grid areas.
   *
   * @param  {String} areaName
   *         Name of the grid area to be highlighted. If no area name is provided, all
   *         the grid areas should be highlighted.
   */
  renderGridArea(areaName) {
    let { devicePixelRatio } = this.win;
    let displayPixelRatio = getDisplayPixelRatio(this.win);
    let paths = [];

    for (let i = 0; i < this.gridData.length; i++) {
      let fragment = this.gridData[i];

      for (let area of fragment.areas) {
        if (areaName && areaName != area.name) {
          continue;
        }

        let rowStart = fragment.rows.lines[area.rowStart - 1];
        let rowEnd = fragment.rows.lines[area.rowEnd - 1];
        let columnStart = fragment.cols.lines[area.columnStart - 1];
        let columnEnd = fragment.cols.lines[area.columnEnd - 1];

        let x1 = columnStart.start + columnStart.breadth;
        let y1 = rowStart.start + rowStart.breadth;
        let x2 = columnEnd.start;
        let y2 = rowEnd.start;

        let points = getPointsFromDiagonal(x1, y1, x2, y2, this.currentMatrix);

        // Scale down by `devicePixelRatio` since SVG element already take them into
        // account.
        let svgPoints = points.map(point => ({
          x: Math.round(point.x / devicePixelRatio),
          y: Math.round(point.y / devicePixelRatio)
        }));

        // Scale down by `displayPixelRatio` since infobar's HTML elements already take it
        // into account; and the zoom scaling is handled by `moveInfobar`.
        let bounds = getBoundsFromPoints(points.map(point => ({
          x: Math.round(point.x / displayPixelRatio),
          y: Math.round(point.y / displayPixelRatio)
        })));

        paths.push(getPathDescriptionFromPoints(svgPoints));

        // Update and show the info bar when only displaying a single grid area.
        if (areaName) {
          this._showGridAreaInfoBar();
          this._updateGridAreaInfobar(area, bounds);
        }
      }
    }

    let areas = this.getElement("areas");
    areas.setAttribute("d", paths.join(" "));
  }

  /**
   * Render grid area name on the containing grid area cell.
   *
   * @param  {Object} fragment
   *         The grid fragment of the grid container.
   * @param  {Object} area
   *         The area overlay to render on the CSS highlighter canvas.
   */
  renderGridAreaName(fragment, area) {
    let { rowStart, rowEnd, columnStart, columnEnd } = area;
    let { devicePixelRatio } = this.win;
    let displayPixelRatio = getDisplayPixelRatio(this.win);
    let offset = (displayPixelRatio / 2) % 1;
    let fontSize = GRID_AREA_NAME_FONT_SIZE * displayPixelRatio;
    let canvasX = Math.round(this._canvasPosition.x * devicePixelRatio);
    let canvasY = Math.round(this._canvasPosition.y * devicePixelRatio);

    this.ctx.save();
    this.ctx.translate(offset - canvasX, offset - canvasY);
    this.ctx.font = fontSize + "px " + GRID_FONT_FAMILY;
    this.ctx.strokeStyle = this.color;
    this.ctx.textAlign = "center";
    this.ctx.textBaseline = "middle";

    // Draw the text for the grid area name.
    for (let rowNumber = rowStart; rowNumber < rowEnd; rowNumber++) {
      for (let columnNumber = columnStart; columnNumber < columnEnd; columnNumber++) {
        let row = fragment.rows.tracks[rowNumber - 1];
        let column = fragment.cols.tracks[columnNumber - 1];

        // If the font size exceeds the bounds of the containing grid cell, size it its
        // row or column dimension, whichever is smallest.
        if (fontSize > (column.breadth * displayPixelRatio) ||
            fontSize > (row.breadth * displayPixelRatio)) {
          fontSize = Math.min([column.breadth, row.breadth]);
          this.ctx.font = fontSize + "px " + GRID_FONT_FAMILY;
        }

        let textWidth = this.ctx.measureText(area.name).width;
        // The width of the character 'm' approximates the height of the text.
        let textHeight = this.ctx.measureText("m").width;
        // Padding in pixels for the line number text inside of the line number container.
        let padding = 3 * displayPixelRatio;

        let boxWidth = textWidth + 2 * padding;
        let boxHeight = textHeight + 2 * padding;

        let x = column.start + column.breadth / 2;
        let y = row.start + row.breadth / 2;

        [x, y] = apply(this.currentMatrix, [x, y]);

        let rectXPos = x - boxWidth / 2;
        let rectYPos = y - boxHeight / 2;

        // Draw a rounded rectangle with a border width of 1 pixel,
        // a border color matching the grid color, and a white background.
        this.ctx.lineWidth = 1 * displayPixelRatio;
        this.ctx.strokeStyle = this.color;
        this.ctx.fillStyle = "white";
        let radius = 2 * displayPixelRatio;
        drawRoundedRect(this.ctx, rectXPos, rectYPos, boxWidth, boxHeight, radius);

        this.ctx.fillStyle = this.color;
        this.ctx.fillText(area.name, x, y + padding);
      }
    }

    this.ctx.restore();
  }

  /**
   * Renders the grid area overlay on the css grid highlighter canvas.
   */
  renderGridAreaOverlay() {
    let padding = 1;

    for (let i = 0; i < this.gridData.length; i++) {
      let fragment = this.gridData[i];

      for (let area of fragment.areas) {
        let { rowStart, rowEnd, columnStart, columnEnd, type } = area;

        if (type === "implicit") {
          continue;
        }

        // Draw the line edges for the grid area.
        const areaColStart = fragment.cols.lines[columnStart - 1];
        const areaColEnd = fragment.cols.lines[columnEnd - 1];

        const areaRowStart = fragment.rows.lines[rowStart - 1];
        const areaRowEnd = fragment.rows.lines[rowEnd - 1];

        const areaColStartLinePos = areaColStart.start + areaColStart.breadth;
        const areaRowStartLinePos = areaRowStart.start + areaRowStart.breadth;

        this.renderLine(areaColStartLinePos + padding, areaRowStartLinePos,
          areaRowEnd.start, COLUMNS, "areaEdge");
        this.renderLine(areaColEnd.start - padding, areaRowStartLinePos,
          areaRowEnd.start, COLUMNS, "areaEdge");

        this.renderLine(areaRowStartLinePos + padding, areaColStartLinePos,
          areaColEnd.start, ROWS, "areaEdge");
        this.renderLine(areaRowEnd.start - padding, areaColStartLinePos, areaColEnd.start,
          ROWS, "areaEdge");

        this.renderGridAreaName(fragment, area);
      }
    }
  }

  /**
   * Render the grid cell highlight for the given grid fragment index, row and column
   * number.
   *
   * @param  {Number} gridFragmentIndex
   *         Index of the grid fragment to render the grid cell highlight.
   * @param  {Number} rowNumber
   *         Row number of the grid cell to highlight.
   * @param  {Number} columnNumber
   *         Column number of the grid cell to highlight.
   */
  renderGridCell(gridFragmentIndex, rowNumber, columnNumber) {
    let fragment = this.gridData[gridFragmentIndex];

    if (!fragment) {
      return;
    }

    let row = fragment.rows.tracks[rowNumber - 1];
    let column = fragment.cols.tracks[columnNumber - 1];

    if (!row || !column) {
      return;
    }

    let x1 = column.start;
    let y1 = row.start;
    let x2 = column.start + column.breadth;
    let y2 = row.start + row.breadth;

    let { devicePixelRatio } = this.win;
    let displayPixelRatio = getDisplayPixelRatio(this.win);
    let points = getPointsFromDiagonal(x1, y1, x2, y2, this.currentMatrix);

    // Scale down by `devicePixelRatio` since SVG element already take them into account.
    let svgPoints = points.map(point => ({
      x: Math.round(point.x / devicePixelRatio),
      y: Math.round(point.y / devicePixelRatio)
    }));

    // Scale down by `displayPixelRatio` since infobar's HTML elements already take it
    // into account, and the zoom scaling is handled by `moveInfobar`.
    let bounds = getBoundsFromPoints(points.map(point => ({
      x: Math.round(point.x / displayPixelRatio),
      y: Math.round(point.y / displayPixelRatio)
    })));

    let cells = this.getElement("cells");
    cells.setAttribute("d", getPathDescriptionFromPoints(svgPoints));

    this._showGridCellInfoBar();
    this._updateGridCellInfobar(rowNumber, columnNumber, bounds);
  }

  /**
   * Render the grid gap area on the css grid highlighter canvas.
   *
   * @param  {Number} linePos
   *         The line position along the x-axis for a column grid line and
   *         y-axis for a row grid line.
   * @param  {Number} startPos
   *         The start position of the cross side of the grid line.
   * @param  {Number} endPos
   *         The end position of the cross side of the grid line.
   * @param  {Number} breadth
   *         The grid line breadth value.
   * @param  {String} dimensionType
   *         The grid dimension type which is either the constant COLUMNS or ROWS.
   */
  renderGridGap(linePos, startPos, endPos, breadth, dimensionType) {
    let { devicePixelRatio } = this.win;
    let displayPixelRatio = getDisplayPixelRatio(this.win);
    let offset = (displayPixelRatio / 2) % 1;
    let canvasX = Math.round(this._canvasPosition.x * devicePixelRatio);
    let canvasY = Math.round(this._canvasPosition.y * devicePixelRatio);

    linePos = Math.round(linePos);
    startPos = Math.round(startPos);
    breadth = Math.round(breadth);

    this.ctx.save();
    this.ctx.fillStyle = this.getGridGapPattern(devicePixelRatio, dimensionType);
    this.ctx.translate(offset - canvasX, offset - canvasY);

    if (dimensionType === COLUMNS) {
      if (isFinite(endPos)) {
        endPos = Math.round(endPos);
      } else {
        endPos = this._winDimensions.height;
        startPos = -endPos;
      }
      drawRect(this.ctx, linePos, startPos, linePos + breadth, endPos,
        this.currentMatrix);
    } else {
      if (isFinite(endPos)) {
        endPos = Math.round(endPos);
      } else {
        endPos = this._winDimensions.width;
        startPos = -endPos;
      }
      drawRect(this.ctx, startPos, linePos, endPos, linePos + breadth,
        this.currentMatrix);
    }

    this.ctx.fill();
    this.ctx.restore();
  }

  /**
   * Render the grid line name highlight for the given grid fragment index, lineNumber,
   * and dimensionType.
   *
   * @param  {Number} gridFragmentIndex
   *         Index of the grid fragment to render the grid line highlight.
   * @param  {Number} lineNumber
   *         Line number of the grid line to highlight.
   * @param  {String} dimensionType
   *         The dimension type of the grid line.
   */
  renderGridLineNames(gridFragmentIndex, lineNumber, dimensionType) {
    let fragment = this.gridData[gridFragmentIndex];

    if (!fragment || !lineNumber || !dimensionType) {
      return;
    }

    const { names } = fragment[dimensionType].lines[lineNumber - 1];
    let linePos;

    if (dimensionType === ROWS) {
      linePos = fragment.rows.lines[lineNumber - 1];
    } else if (dimensionType === COLUMNS) {
      linePos = fragment.cols.lines[lineNumber - 1];
    }

    if (!linePos) {
      return;
    }

    let currentZoom = getCurrentZoom(this.win);
    let { bounds } = this.currentQuads.content[gridFragmentIndex];

    const rowYPosition = fragment.rows.lines[0];
    const colXPosition = fragment.rows.lines[0];

    let x = dimensionType === COLUMNS
      ? linePos.start + (bounds.left / currentZoom)
      : colXPosition.start + (bounds.left / currentZoom);

    let y = dimensionType === ROWS
      ? linePos.start + (bounds.top / currentZoom)
      : rowYPosition.start + (bounds.top / currentZoom);

    this._showGridLineInfoBar();
    this._updateGridLineInfobar(names.join(", "), lineNumber, x, y);
  }

  /**
   * Render the grid line number on the css grid highlighter canvas.
   *
   * @param  {Number} lineNumber
   *         The grid line number.
   * @param  {Number} linePos
   *         The line position along the x-axis for a column grid line and
   *         y-axis for a row grid line.
   * @param  {Number} startPos
   *         The start position of the cross side of the grid line.
   * @param  {Number} breadth
   *         The grid line breadth value.
   * @param  {String} dimensionType
   *         The grid dimension type which is either the constant COLUMNS or ROWS.
   * @param  {Number||undefined} stackedLineIndex
   *         The line index position of the stacked line.
   */
  renderGridLineNumber(lineNumber, linePos, startPos, breadth, dimensionType,
    stackedLineIndex) {
    let displayPixelRatio = getDisplayPixelRatio(this.win);
    let { devicePixelRatio } = this.win;
    let offset = (displayPixelRatio / 2) % 1;
    let fontSize = GRID_FONT_SIZE * displayPixelRatio;
    let canvasX = Math.round(this._canvasPosition.x * devicePixelRatio);
    let canvasY = Math.round(this._canvasPosition.y * devicePixelRatio);

    linePos = Math.round(linePos);
    startPos = Math.round(startPos);
    breadth = Math.round(breadth);

    if (linePos + breadth < 0) {
      // Don't render the line number since the line is not visible on screen.
      return;
    }

    this.ctx.save();
    this.ctx.translate(offset - canvasX, offset - canvasY);
    this.ctx.font = fontSize + "px " + GRID_FONT_FAMILY;

    // For a general grid box, the height of the character "m" will be its minimum width
    // and height. If line number's text width is greater, then use the grid box's text
    // width instead.
    let textHeight = this.ctx.measureText("m").width;
    let textWidth = Math.max(textHeight, this.ctx.measureText(lineNumber).width);

    // Padding in pixels for the line number text inside of the line number container.
    let padding = 3 * displayPixelRatio;
    let offsetFromEdge = 2 * displayPixelRatio;

    let boxWidth = textWidth + 2 * padding;
    let boxHeight = textHeight + 2 * padding;

    // Calculate the x & y coordinates for the line number container, so that its arrow
    // tip is centered on the line (or the gap if there is one), and is offset by the
    // calculated padding value from the grid container edge.
    let x, y;

    if (dimensionType === COLUMNS) {
      x = linePos + breadth / 2;
      y = startPos;

      if (lineNumber > 0) {
        y -= offsetFromEdge;
      } else {
        y += offsetFromEdge;
      }
    } else if (dimensionType === ROWS) {
      x = startPos;
      y = linePos + breadth / 2;

      if (lineNumber > 0) {
        x -= offsetFromEdge;
      } else {
        x += offsetFromEdge;
      }
    }

    [x, y] = apply(this.currentMatrix, [x, y]);

    if (stackedLineIndex) {
      // Offset the stacked line number by half of the box's width/height.
      const xOffset = boxWidth / 4;
      const yOffset = boxHeight / 4;

      if (lineNumber > 0) {
        x -= xOffset;
        y -= yOffset;
      } else {
        x += xOffset;
        y += yOffset;
      }
    }

    // Draw a bubble rectangular arrow with a border width of 2 pixels, a border color
    // matching the grid color and a white background (the line number will be written in
    // black).
    this.ctx.lineWidth = 2 * displayPixelRatio;
    this.ctx.strokeStyle = this.color;
    this.ctx.fillStyle = "white";

    // See param definitions of drawBubbleRect.
    let radius = 2 * displayPixelRatio;
    let margin = 2 * displayPixelRatio;
    let arrowSize = 8 * displayPixelRatio;

    let minBoxSize = arrowSize * 2 + padding;
    boxWidth = Math.max(boxWidth, minBoxSize);
    boxHeight = Math.max(boxHeight, minBoxSize);

    // Determine default box edge to aim the line number arrow at.
    let boxEdge;
    if (dimensionType === COLUMNS) {
      if (lineNumber > 0) {
        boxEdge = "top";
      } else {
        boxEdge = "bottom";
      }
    }
    if (dimensionType === ROWS) {
      if (lineNumber > 0) {
        boxEdge = "left";
      } else {
        boxEdge = "right";
      }
    }

    // Rotate box edge as needed for writing mode and text direction.
    let { direction, writingMode } = getComputedStyle(this.currentNode);

    switch (writingMode) {
      case "horizontal-tb":
        // This is the initial value.  No further adjustment needed.
        break;
      case "vertical-rl":
        boxEdge = rotateEdgeRight(boxEdge);
        break;
      case "vertical-lr":
        if (dimensionType === COLUMNS) {
          boxEdge = rotateEdgeLeft(boxEdge);
        } else {
          boxEdge = rotateEdgeRight(boxEdge);
        }
        break;
      case "sideways-rl":
        boxEdge = rotateEdgeRight(boxEdge);
        break;
      case "sideways-lr":
        boxEdge = rotateEdgeLeft(boxEdge);
        break;
      default:
        console.error(`Unexpected writing-mode: ${writingMode}`);
    }

    switch (direction) {
      case "ltr":
        // This is the initial value.  No further adjustment needed.
        break;
      case "rtl":
        if (dimensionType === ROWS) {
          boxEdge = reflectEdge(boxEdge);
        }
        break;
      default:
        console.error(`Unexpected direction: ${direction}`);
    }

    // Default to drawing outside the edge, but move inside when close to viewport.
    let minOffsetFromEdge = OFFSET_FROM_EDGE * displayPixelRatio;
    let { width, height } = this._winDimensions;
    width *= displayPixelRatio;
    height *= displayPixelRatio;

    // Check if the x or y position of the line number's arrow is too close to the edge
    // of the window.  If it is too close, flip the arrow around (by reflecting the box
    // edge we're thinking of) and adjust the position by 2 x padding since we're now
    // going the opposite direction.
    switch (boxEdge) {
      case "left":
        if (x < minOffsetFromEdge) {
          boxEdge = reflectEdge(boxEdge);
          x += 2 * padding;
        }
        break;
      case "right":
        if ((width - x) < minOffsetFromEdge) {
          boxEdge = reflectEdge(boxEdge);
          x -= 2 * padding;
        }
        break;
      case "top":
        if (y < minOffsetFromEdge) {
          boxEdge = reflectEdge(boxEdge);
          y += 2 * padding;
        }
        break;
      case "bottom":
        if ((height - y) < minOffsetFromEdge) {
          boxEdge = reflectEdge(boxEdge);
          y -= 2 * padding;
        }
        break;
    }

    // Draw the bubble rect to show the arrow.
    drawBubbleRect(this.ctx, x, y, boxWidth, boxHeight, radius, margin, arrowSize,
                   boxEdge);

    // Adjust position based on the edge.
    switch (boxEdge) {
      case "left":
        x -= (boxWidth + arrowSize + radius) - boxWidth / 2;
        break;
      case "right":
        x += (boxWidth + arrowSize + radius) - boxWidth / 2;
        break;
      case "top":
        y -= (boxHeight + arrowSize + radius) - boxHeight / 2;
        break;
      case "bottom":
        y += (boxHeight + arrowSize + radius) - boxHeight / 2;
        break;
    }

    // Write the line number inside of the rectangle.
    this.ctx.textAlign = "center";
    this.ctx.textBaseline = "middle";
    this.ctx.fillStyle = "black";
    const numberText = stackedLineIndex ? "" : lineNumber;
    this.ctx.fillText(numberText, x, y);
    this.ctx.restore();
  }

  /**
   * Render the grid line on the css grid highlighter canvas.
   *
   * @param  {Number} linePos
   *         The line position along the x-axis for a column grid line and
   *         y-axis for a row grid line.
   * @param  {Number} startPos
   *         The start position of the cross side of the grid line.
   * @param  {Number} endPos
   *         The end position of the cross side of the grid line.
   * @param  {String} dimensionType
   *         The grid dimension type which is either the constant COLUMNS or ROWS.
   * @param  {String} lineType
   *         The grid line type - "edge", "explicit", or "implicit".
   */
  renderLine(linePos, startPos, endPos, dimensionType, lineType) {
    let { devicePixelRatio } = this.win;
    let lineWidth = getDisplayPixelRatio(this.win);
    let offset = (lineWidth / 2) % 1;
    let canvasX = Math.round(this._canvasPosition.x * devicePixelRatio);
    let canvasY = Math.round(this._canvasPosition.y * devicePixelRatio);

    linePos = Math.round(linePos);
    startPos = Math.round(startPos);
    endPos = Math.round(endPos);

    this.ctx.save();
    this.ctx.setLineDash(GRID_LINES_PROPERTIES[lineType].lineDash);
    this.ctx.translate(offset - canvasX, offset - canvasY);

    let lineOptions = {
      matrix: this.currentMatrix
    };

    if (this.options.showInfiniteLines) {
      lineOptions.extendToBoundaries = [canvasX, canvasY, canvasX + CANVAS_SIZE,
                                        canvasY + CANVAS_SIZE];
    }

    if (dimensionType === COLUMNS) {
      drawLine(this.ctx, linePos, startPos, linePos, endPos, lineOptions);
    } else {
      drawLine(this.ctx, startPos, linePos, endPos, linePos, lineOptions);
    }

    this.ctx.strokeStyle = this.color;
    this.ctx.globalAlpha = GRID_LINES_PROPERTIES[lineType].alpha;

    if (GRID_LINES_PROPERTIES[lineType].lineWidth) {
      this.ctx.lineWidth = GRID_LINES_PROPERTIES[lineType].lineWidth * devicePixelRatio;
    } else {
      this.ctx.lineWidth = lineWidth;
    }

    this.ctx.stroke();
    this.ctx.restore();
  }

  /**
   * Render the grid lines given the grid dimension information of the
   * column or row lines.
   *
   * @param  {GridDimension} gridDimension
   *         Column or row grid dimension object.
   * @param  {Object} quad.bounds
   *         The content bounds of the box model region quads.
   * @param  {String} dimensionType
   *         The grid dimension type which is either the constant COLUMNS or ROWS.
   * @param  {Number} startPos
   *         The start position of the cross side ("left" for ROWS and "top" for COLUMNS)
   *         of the grid dimension.
   * @param  {Number} endPos
   *         The end position of the cross side ("left" for ROWS and "top" for COLUMNS)
   *         of the grid dimension.
   */
  renderLines(gridDimension, dimensionType, startPos, endPos) {
    const { lines, tracks } = gridDimension;
    const lastEdgeLineIndex = this.getLastEdgeLineIndex(tracks);

    for (let i = 0; i < lines.length; i++) {
      let line = lines[i];
      let linePos = line.start;

      if (i == 0 || i == lastEdgeLineIndex) {
        this.renderLine(linePos, startPos, endPos, dimensionType, "edge");
      } else {
        this.renderLine(linePos, startPos, endPos, dimensionType, tracks[i - 1].type);
      }

      // Render a second line to illustrate the gutter for non-zero breadth.
      if (line.breadth > 0) {
        this.renderGridGap(linePos, startPos, endPos, line.breadth, dimensionType);
        this.renderLine(linePos + line.breadth, startPos, endPos, dimensionType,
          tracks[i].type);
      }
    }
  }

  /**
   * Render the grid lines given the grid dimension information of the
   * column or row lines.
   *
   * @param  {GridDimension} gridDimension
   *         Column or row grid dimension object.
   * @param  {String} dimensionType
   *         The grid dimension type which is either the constant COLUMNS or ROWS.
   * @param  {Number} startPos
   *         The start position of the cross side ("left" for ROWS and "top" for COLUMNS)
   *         of the grid dimension.
   */
  renderLineNumbers(gridDimension, dimensionType, startPos) {
    const { lines, tracks } = gridDimension;
    // Keep track of the number of collapsed lines per line position.
    let stackedLines = [];

    for (let i = 0, line; (line = lines[i++]);) {
      // If you place something using negative numbers, you can trigger some implicit
      // grid creation above and to the left of the explicit grid (assuming a
      // horizontal-tb writing mode).
      //
      // The first explicit grid line gets the number of 1, and any implicit grid lines
      // before 1 get negative numbers. Since here we're rendering only the positive line
      // numbers, we have to skip any implicit grid lines before the first one that is
      // explicit. The API returns a 0 as the line's number for these implicit lines that
      // occurs before the first explicit line.
      if (line.number === 0) {
        continue;
      }

      // Check for overlapping lines. We render a second box beneath the last overlapping
      // line number to indicate there are lines beneath it.
      const gridLine = tracks[line.number - 1];

      if (gridLine) {
        const { breadth }  = gridLine;

        if (breadth === 0) {
          stackedLines.push(lines[i].number);

          if (stackedLines.length > 0) {
            this.renderGridLineNumber(line.number, line.start, startPos, line.breadth,
              dimensionType, 1);
          }

          continue;
        }
      }

      this.renderGridLineNumber(line.number, line.start, startPos, line.breadth,
        dimensionType);
    }
  }

  /**
   * Render the negative grid lines given the grid dimension information of the
   * column or row lines.
   *
   * @param  {GridDimension} gridDimension
   *         Column or row grid dimension object.
   * @param  {String} dimensionType
   *         The grid dimension type which is either the constant COLUMNS or ROWS.
   * @param  {Number} startPos
   *         The start position of the cross side ("left" for ROWS and "top" for COLUMNS)
   *         of the grid dimension.
   */
  renderNegativeLineNumbers(gridDimension, dimensionType, startPos) {
    const { lines, tracks } = gridDimension;
    // Keep track of the number of collapsed lines per line position.
    let stackedLines = [];

    for (let i = 0, line; (line = lines[i++]);) {
      let linePos = line.start;
      let negativeLineNumber = line.negativeNumber;

      // Don't render any negative line number greater than -1.
      if (negativeLineNumber == 0) {
        break;
      }

      // Check for overlapping lines. We render a second box beneath the last overlapping
      // line number to indicate there are lines beneath it.
      const gridLine = tracks[line.number - 1];

      if (gridLine) {
        const { breadth }  = gridLine;

        if (breadth === 0) {
          stackedLines.push(negativeLineNumber);

          if (stackedLines.length > 0) {
            this.renderGridLineNumber(negativeLineNumber, linePos, startPos,
              line.breadth, dimensionType, 1);
          }

          continue;
        }
      }

      // For negative line numbers, we want to display the smallest
      // value at the front of the stack.
      if (stackedLines.length) {
        negativeLineNumber = stackedLines[0];
        stackedLines = [];
      }

      this.renderGridLineNumber(negativeLineNumber, linePos, startPos, line.breadth,
        dimensionType);
    }
  }

  /**
   * Update the highlighter on the current highlighted node (the one that was
   * passed as an argument to show(node)). Should be called whenever node's geometry
   * or grid changes.
   */
  _update() {
    setIgnoreLayoutChanges(true);

    let root = this.getElement("root");
    let cells = this.getElement("cells");
    let areas = this.getElement("areas");

    // Hide the root element and force the reflow in order to get the proper window's
    // dimensions without increasing them.
    root.setAttribute("style", "display: none");
    this.win.document.documentElement.offsetWidth;

    // Set the grid cells and areas fill to the current grid colour.
    cells.setAttribute("style", `fill: ${this.color}`);
    areas.setAttribute("style", `fill: ${this.color}`);

    let { width, height } = this._winDimensions;

    // Updates the <canvas> element's position and size.
    // It also clear the <canvas>'s drawing context.
    updateCanvasElement(this.canvas, this._canvasPosition, this.win.devicePixelRatio);

    // Clear the grid area highlights.
    this.clearGridAreas();
    this.clearGridCell();

    // Update the current matrix used in our canvas' rendering.
    let { currentMatrix, hasNodeTransformations } =
      getCurrentMatrix(this.currentNode, this.win);
    this.currentMatrix = currentMatrix;
    this.hasNodeTransformations = hasNodeTransformations;

    // Start drawing the grid fragments.
    for (let i = 0; i < this.gridData.length; i++) {
      this.renderFragment(this.gridData[i]);
    }

    // Display the grid area highlights if needed.
    if (this.options.showAllGridAreas) {
      this.showAllGridAreas();
    } else if (this.options.showGridArea) {
      this.showGridArea(this.options.showGridArea);
    }

    // Display the grid cell highlights if needed.
    if (this.options.showGridCell) {
      this.showGridCell(this.options.showGridCell);
    }

    // Display the grid line names if needed.
    if (this.options.showGridLineNames) {
      this.showGridLineNames(this.options.showGridLineNames);
    }

    this._showGrid();
    this._showGridElements();

    root.setAttribute("style",
      `position: absolute; width: ${width}px; height: ${height}px; overflow: hidden`);

    setIgnoreLayoutChanges(false, this.highlighterEnv.document.documentElement);
    return true;
  }

  /**
   * Update the grid information displayed in the grid area info bar.
   *
   * @param  {GridArea} area
   *         The grid area object.
   * @param  {Object} bounds
   *         A DOMRect-like object represent the grid area rectangle.
   */
  _updateGridAreaInfobar(area, bounds) {
    let { width, height } = bounds;
    let dim = parseFloat(width.toPrecision(6)) +
              " \u00D7 " +
              parseFloat(height.toPrecision(6));

    this.getElement("area-infobar-name").setTextContent(area.name);
    this.getElement("area-infobar-dimensions").setTextContent(dim);

    let container = this.getElement("area-infobar-container");
    moveInfobar(container, bounds, this.win, {
      position: "bottom",
      hideIfOffscreen: true
    });
  }

  /**
   * Update the grid information displayed in the grid cell info bar.
   *
   * @param  {Number} rowNumber
   *         The grid cell's row number.
   * @param  {Number} columnNumber
   *         The grid cell's column number.
   * @param  {Object} bounds
   *         A DOMRect-like object represent the grid cell rectangle.
   */
  _updateGridCellInfobar(rowNumber, columnNumber, bounds) {
    let { width, height } = bounds;
    let dim = parseFloat(width.toPrecision(6)) +
              " \u00D7 " +
              parseFloat(height.toPrecision(6));
    let position = LAYOUT_L10N.getFormatStr("layout.rowColumnPositions", rowNumber,
      columnNumber);

    this.getElement("cell-infobar-position").setTextContent(position);
    this.getElement("cell-infobar-dimensions").setTextContent(dim);

    let container = this.getElement("cell-infobar-container");
    moveInfobar(container, bounds, this.win, {
      position: "top",
      hideIfOffscreen: true
    });
  }

  /**
   * Update the grid information displayed in the grid line info bar.
   *
   * @param  {String} gridLineNames
   *         Comma-separated string of names for the grid line.
   * @param  {Number} gridLineNumber
   *         The grid line number.
   * @param  {Number} x
   *         The x-coordinate of the grid line.
   * @param  {Number} y
   *         The y-coordinate of the grid line.
   */
  _updateGridLineInfobar(gridLineNames, gridLineNumber, x, y) {
    this.getElement("line-infobar-number").setTextContent(gridLineNumber);
    this.getElement("line-infobar-names").setTextContent(gridLineNames);

    let container = this.getElement("line-infobar-container");
    moveInfobar(container, getBoundsFromPoints([{x, y}, {x, y}, {x, y}, {x, y}]),
      this.win);
  }
}

exports.CssGridHighlighter = CssGridHighlighter;
PK
!<��##Lchrome/devtools/modules/devtools/server/actors/highlighters/css-transform.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { AutoRefreshHighlighter } = require("./auto-refresh");
const {
  CanvasFrameAnonymousContentHelper, getComputedStyle,
  createSVGNode, createNode } = require("./utils/markup");
const { setIgnoreLayoutChanges,
  getNodeBounds } = require("devtools/shared/layout/utils");

// The minimum distance a line should be before it has an arrow marker-end
const ARROW_LINE_MIN_DISTANCE = 10;

var MARKER_COUNTER = 1;

/**
 * The CssTransformHighlighter is the class that draws an outline around a
 * transformed element and an outline around where it would be if untransformed
 * as well as arrows connecting the 2 outlines' corners.
 */
class CssTransformHighlighter extends AutoRefreshHighlighter {
  constructor(highlighterEnv) {
    super(highlighterEnv);

    this.ID_CLASS_PREFIX = "css-transform-";

    this.markup = new CanvasFrameAnonymousContentHelper(this.highlighterEnv,
      this._buildMarkup.bind(this));
  }

  _buildMarkup() {
    let container = createNode(this.win, {
      attributes: {
        "class": "highlighter-container"
      }
    });

    // The root wrapper is used to unzoom the highlighter when needed.
    let rootWrapper = createNode(this.win, {
      parent: container,
      attributes: {
        "id": "root",
        "class": "root"
      },
      prefix: this.ID_CLASS_PREFIX
    });

    let svg = createSVGNode(this.win, {
      nodeType: "svg",
      parent: rootWrapper,
      attributes: {
        "id": "elements",
        "hidden": "true",
        "width": "100%",
        "height": "100%"
      },
      prefix: this.ID_CLASS_PREFIX
    });

    // Add a marker tag to the svg root for the arrow tip
    this.markerId = "arrow-marker-" + MARKER_COUNTER;
    MARKER_COUNTER++;
    let marker = createSVGNode(this.win, {
      nodeType: "marker",
      parent: svg,
      attributes: {
        "id": this.markerId,
        "markerWidth": "10",
        "markerHeight": "5",
        "orient": "auto",
        "markerUnits": "strokeWidth",
        "refX": "10",
        "refY": "5",
        "viewBox": "0 0 10 10"
      },
      prefix: this.ID_CLASS_PREFIX
    });
    createSVGNode(this.win, {
      nodeType: "path",
      parent: marker,
      attributes: {
        "d": "M 0 0 L 10 5 L 0 10 z",
        "fill": "#08C"
      }
    });

    let shapesGroup = createSVGNode(this.win, {
      nodeType: "g",
      parent: svg
    });

    // Create the 2 polygons (transformed and untransformed)
    createSVGNode(this.win, {
      nodeType: "polygon",
      parent: shapesGroup,
      attributes: {
        "id": "untransformed",
        "class": "untransformed"
      },
      prefix: this.ID_CLASS_PREFIX
    });
    createSVGNode(this.win, {
      nodeType: "polygon",
      parent: shapesGroup,
      attributes: {
        "id": "transformed",
        "class": "transformed"
      },
      prefix: this.ID_CLASS_PREFIX
    });

    // Create the arrows
    for (let nb of ["1", "2", "3", "4"]) {
      createSVGNode(this.win, {
        nodeType: "line",
        parent: shapesGroup,
        attributes: {
          "id": "line" + nb,
          "class": "line",
          "marker-end": "url(#" + this.markerId + ")"
        },
        prefix: this.ID_CLASS_PREFIX
      });
    }

    return container;
  }

  /**
   * Destroy the nodes. Remove listeners.
   */
  destroy() {
    AutoRefreshHighlighter.prototype.destroy.call(this);
    this.markup.destroy();
  }

  getElement(id) {
    return this.markup.getElement(this.ID_CLASS_PREFIX + id);
  }

  /**
   * Show the highlighter on a given node
   */
  _show() {
    if (!this._isTransformed(this.currentNode)) {
      this.hide();
      return false;
    }

    return this._update();
  }

  /**
   * Checks if the supplied node is transformed and not inline
   */
  _isTransformed(node) {
    let style = getComputedStyle(node);
    return style && (style.transform !== "none" && style.display !== "inline");
  }

  _setPolygonPoints(quad, id) {
    let points = [];
    for (let point of ["p1", "p2", "p3", "p4"]) {
      points.push(quad[point].x + "," + quad[point].y);
    }
    this.getElement(id).setAttribute("points", points.join(" "));
  }

  _setLinePoints(p1, p2, id) {
    let line = this.getElement(id);
    line.setAttribute("x1", p1.x);
    line.setAttribute("y1", p1.y);
    line.setAttribute("x2", p2.x);
    line.setAttribute("y2", p2.y);

    let dist = Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
    if (dist < ARROW_LINE_MIN_DISTANCE) {
      line.removeAttribute("marker-end");
    } else {
      line.setAttribute("marker-end", "url(#" + this.markerId + ")");
    }
  }

  /**
   * Update the highlighter on the current highlighted node (the one that was
   * passed as an argument to show(node)).
   * Should be called whenever node size or attributes change
   */
  _update() {
    setIgnoreLayoutChanges(true);

    // Getting the points for the transformed shape
    let quads = this.currentQuads.border;
    if (!quads.length ||
        quads[0].bounds.width <= 0 || quads[0].bounds.height <= 0) {
      this._hideShapes();
      return false;
    }

    let [quad] = quads;

    // Getting the points for the untransformed shape
    let untransformedQuad = getNodeBounds(this.win, this.currentNode);

    this._setPolygonPoints(quad, "transformed");
    this._setPolygonPoints(untransformedQuad, "untransformed");
    for (let nb of ["1", "2", "3", "4"]) {
      this._setLinePoints(untransformedQuad["p" + nb], quad["p" + nb], "line" + nb);
    }

    // Adapt to the current zoom
    this.markup.scaleRootElement(this.currentNode, this.ID_CLASS_PREFIX + "root");

    this._showShapes();

    setIgnoreLayoutChanges(false, this.highlighterEnv.window.document.documentElement);
    return true;
  }

  /**
   * Hide the highlighter, the outline and the infobar.
   */
  _hide() {
    setIgnoreLayoutChanges(true);
    this._hideShapes();
    setIgnoreLayoutChanges(false, this.highlighterEnv.window.document.documentElement);
  }

  _hideShapes() {
    this.getElement("elements").setAttribute("hidden", "true");
  }

  _showShapes() {
    this.getElement("elements").removeAttribute("hidden");
  }
}

exports.CssTransformHighlighter = CssTransformHighlighter;
PK
!<R��1@1@Jchrome/devtools/modules/devtools/server/actors/highlighters/eye-dropper.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";

// Eye-dropper tool. This is implemented as a highlighter so it can be displayed in the
// content page.
// It basically displays a magnifier that tracks mouse moves and shows a magnified version
// of the page. On click, it samples the color at the pixel being hovered.

const {Ci, Cc} = require("chrome");
const {CanvasFrameAnonymousContentHelper, createNode} = require("./utils/markup");
const Services = require("Services");
const EventEmitter = require("devtools/shared/event-emitter");
const {rgbToHsl, rgbToColorName} = require("devtools/shared/css/color").colorUtils;
const {getCurrentZoom, getFrameOffsets} = require("devtools/shared/layout/utils");

loader.lazyGetter(this, "clipboardHelper",
  () => Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper));
loader.lazyGetter(this, "l10n",
  () => Services.strings.createBundle("chrome://devtools-shared/locale/eyedropper.properties"));

const ZOOM_LEVEL_PREF = "devtools.eyedropper.zoom";
const FORMAT_PREF = "devtools.defaultColorUnit";
// Width of the canvas.
const MAGNIFIER_WIDTH = 96;
// Height of the canvas.
const MAGNIFIER_HEIGHT = 96;
// Start position, when the tool is first shown. This should match the top/left position
// defined in CSS.
const DEFAULT_START_POS_X = 100;
const DEFAULT_START_POS_Y = 100;
// How long to wait before closing after copy.
const CLOSE_DELAY = 750;

/**
 * The EyeDropper is the class that draws the gradient line and
 * color stops as an overlay on top of a linear-gradient background-image.
 */
function EyeDropper(highlighterEnv) {
  EventEmitter.decorate(this);

  this.highlighterEnv = highlighterEnv;
  this.markup = new CanvasFrameAnonymousContentHelper(this.highlighterEnv,
                                                      this._buildMarkup.bind(this));

  // Get a couple of settings from prefs.
  this.format = Services.prefs.getCharPref(FORMAT_PREF);
  this.eyeDropperZoomLevel = Services.prefs.getIntPref(ZOOM_LEVEL_PREF);
}

EyeDropper.prototype = {
  typeName: "EyeDropper",

  ID_CLASS_PREFIX: "eye-dropper-",

  get win() {
    return this.highlighterEnv.window;
  },

  _buildMarkup() {
    // Highlighter main container.
    let container = createNode(this.win, {
      attributes: {"class": "highlighter-container"}
    });

    // Wrapper element.
    let wrapper = createNode(this.win, {
      parent: container,
      attributes: {
        "id": "root",
        "class": "root",
        "hidden": "true"
      },
      prefix: this.ID_CLASS_PREFIX
    });

    // The magnifier canvas element.
    createNode(this.win, {
      parent: wrapper,
      nodeType: "canvas",
      attributes: {
        "id": "canvas",
        "class": "canvas",
        "width": MAGNIFIER_WIDTH,
        "height": MAGNIFIER_HEIGHT
      },
      prefix: this.ID_CLASS_PREFIX
    });

    // The color label element.
    let colorLabelContainer = createNode(this.win, {
      parent: wrapper,
      attributes: {"class": "color-container"},
      prefix: this.ID_CLASS_PREFIX
    });
    createNode(this.win, {
      nodeType: "div",
      parent: colorLabelContainer,
      attributes: {"id": "color-preview", "class": "color-preview"},
      prefix: this.ID_CLASS_PREFIX
    });
    createNode(this.win, {
      nodeType: "div",
      parent: colorLabelContainer,
      attributes: {"id": "color-value", "class": "color-value"},
      prefix: this.ID_CLASS_PREFIX
    });

    return container;
  },

  destroy() {
    this.hide();
    this.markup.destroy();
  },

  getElement(id) {
    return this.markup.getElement(this.ID_CLASS_PREFIX + id);
  },

  /**
   * Show the eye-dropper highlighter.
   * @param {DOMNode} node The node which document the highlighter should be inserted in.
   * @param {Object} options The options object may contain the following properties:
   * - {Boolean} copyOnSelect Whether selecting a color should copy it to the clipboard.
   */
  show(node, options = {}) {
    if (this.highlighterEnv.isXUL) {
      return false;
    }

    this.options = options;

    // Get the page's current zoom level.
    this.pageZoom = getCurrentZoom(this.win);

    // Take a screenshot of the viewport. This needs to be done first otherwise the
    // eyedropper UI will appear in the screenshot itself (since the UI is injected as
    // native anonymous content in the page).
    // Once the screenshot is ready, the magnified area will be drawn.
    this.prepareImageCapture();

    // Start listening for user events.
    let {pageListenerTarget} = this.highlighterEnv;
    pageListenerTarget.addEventListener("mousemove", this);
    pageListenerTarget.addEventListener("click", this, true);
    pageListenerTarget.addEventListener("keydown", this);
    pageListenerTarget.addEventListener("DOMMouseScroll", this);
    pageListenerTarget.addEventListener("FullZoomChange", this);

    // Show the eye-dropper.
    this.getElement("root").removeAttribute("hidden");

    // Prepare the canvas context on which we're drawing the magnified page portion.
    this.ctx = this.getElement("canvas").getCanvasContext();
    this.ctx.imageSmoothingEnabled = false;

    this.magnifiedArea = {width: MAGNIFIER_WIDTH, height: MAGNIFIER_HEIGHT,
                          x: DEFAULT_START_POS_X, y: DEFAULT_START_POS_Y};

    this.moveTo(DEFAULT_START_POS_X, DEFAULT_START_POS_Y);

    // Focus the content so the keyboard can be used.
    this.win.focus();

    return true;
  },

  /**
   * Hide the eye-dropper highlighter.
   */
  hide() {
    if (this.highlighterEnv.isXUL) {
      return;
    }

    this.pageImage = null;

    let {pageListenerTarget} = this.highlighterEnv;

    if (pageListenerTarget) {
      pageListenerTarget.removeEventListener("mousemove", this);
      pageListenerTarget.removeEventListener("click", this, true);
      pageListenerTarget.removeEventListener("keydown", this);
      pageListenerTarget.removeEventListener("DOMMouseScroll", this);
      pageListenerTarget.removeEventListener("FullZoomChange", this);
    }

    this.getElement("root").setAttribute("hidden", "true");
    this.getElement("root").removeAttribute("drawn");

    this.emit("hidden");
  },

  prepareImageCapture() {
    // Get the image data from the content window.
    let imageData = getWindowAsImageData(this.win);

    // We need to transform imageData to something drawWindow will consume. An ImageBitmap
    // works well. We could have used an Image, but doing so results in errors if the page
    // defines CSP headers.
    this.win.createImageBitmap(imageData).then(image => {
      this.pageImage = image;
      // We likely haven't drawn anything yet (no mousemove events yet), so start now.
      this.draw();

      // Set an attribute on the root element to be able to run tests after the first draw
      // was done.
      this.getElement("root").setAttribute("drawn", "true");
    });
  },

  /**
   * Get the number of cells (blown-up pixels) per direction in the grid.
   */
  get cellsWide() {
    // Canvas will render whole "pixels" (cells) only, and an even number at that. Round
    // up to the nearest even number of pixels.
    let cellsWide = Math.ceil(this.magnifiedArea.width / this.eyeDropperZoomLevel);
    cellsWide += cellsWide % 2;

    return cellsWide;
  },

  /**
   * Get the size of each cell (blown-up pixel) in the grid.
   */
  get cellSize() {
    return this.magnifiedArea.width / this.cellsWide;
  },

  /**
   * Get index of cell in the center of the grid.
   */
  get centerCell() {
    return Math.floor(this.cellsWide / 2);
  },

  /**
   * Get color of center cell in the grid.
   */
  get centerColor() {
    let pos = (this.centerCell * this.cellSize) + (this.cellSize / 2);
    let rgb = this.ctx.getImageData(pos, pos, 1, 1).data;
    return rgb;
  },

  draw() {
    // If the image of the page isn't ready yet, bail out, we'll draw later on mousemove.
    if (!this.pageImage) {
      return;
    }

    let {width, height, x, y} = this.magnifiedArea;

    let zoomedWidth = width / this.eyeDropperZoomLevel;
    let zoomedHeight = height / this.eyeDropperZoomLevel;

    let sx = x - (zoomedWidth / 2);
    let sy = y - (zoomedHeight / 2);
    let sw = zoomedWidth;
    let sh = zoomedHeight;

    this.ctx.drawImage(this.pageImage, sx, sy, sw, sh, 0, 0, width, height);

    // Draw the grid on top, but only at 3x or more, otherwise it's too busy.
    if (this.eyeDropperZoomLevel > 2) {
      this.drawGrid();
    }

    this.drawCrosshair();

    // Update the color preview and value.
    let rgb = this.centerColor;
    this.getElement("color-preview").setAttribute("style",
      `background-color:${toColorString(rgb, "rgb")};`);
    this.getElement("color-value").setTextContent(toColorString(rgb, this.format));
  },

  /**
   * Draw a grid on the canvas representing pixel boundaries.
   */
  drawGrid() {
    let {width, height} = this.magnifiedArea;

    this.ctx.lineWidth = 1;
    this.ctx.strokeStyle = "rgba(143, 143, 143, 0.2)";

    for (let i = 0; i < width; i += this.cellSize) {
      this.ctx.beginPath();
      this.ctx.moveTo(i - .5, 0);
      this.ctx.lineTo(i - .5, height);
      this.ctx.stroke();

      this.ctx.beginPath();
      this.ctx.moveTo(0, i - .5);
      this.ctx.lineTo(width, i - .5);
      this.ctx.stroke();
    }
  },

  /**
   * Draw a box on the canvas to highlight the center cell.
   */
  drawCrosshair() {
    let pos = this.centerCell * this.cellSize;

    this.ctx.lineWidth = 1;
    this.ctx.lineJoin = "miter";
    this.ctx.strokeStyle = "rgba(0, 0, 0, 1)";
    this.ctx.strokeRect(pos - 1.5, pos - 1.5, this.cellSize + 2, this.cellSize + 2);

    this.ctx.strokeStyle = "rgba(255, 255, 255, 1)";
    this.ctx.strokeRect(pos - 0.5, pos - 0.5, this.cellSize, this.cellSize);
  },

  handleEvent(e) {
    switch (e.type) {
      case "mousemove":
        // We might be getting an event from a child frame, so account for the offset.
        let [xOffset, yOffset] = getFrameOffsets(this.win, e.target);
        let x = xOffset + e.pageX - this.win.scrollX;
        let y = yOffset + e.pageY - this.win.scrollY;
        // Update the zoom area.
        this.magnifiedArea.x = x * this.pageZoom;
        this.magnifiedArea.y = y * this.pageZoom;
        // Redraw the portion of the screenshot that is now under the mouse.
        this.draw();
        // And move the eye-dropper's UI so it follows the mouse.
        this.moveTo(x, y);
        break;
      case "click":
        this.selectColor();
        break;
      case "keydown":
        this.handleKeyDown(e);
        break;
      case "DOMMouseScroll":
        // Prevent scrolling. That's because we only took a screenshot of the viewport, so
        // scrolling out of the viewport wouldn't draw the expected things. In the future
        // we can take the screenshot again on scroll, but for now it doesn't seem
        // important.
        e.preventDefault();
        break;
      case "FullZoomChange":
        this.hide();
        this.show();
        break;
    }
  },

  moveTo(x, y) {
    let root = this.getElement("root");
    root.setAttribute("style", `top:${y}px;left:${x}px;`);

    // Move the label container to the top if the magnifier is close to the bottom edge.
    if (y >= this.win.innerHeight - MAGNIFIER_HEIGHT) {
      root.setAttribute("top", "");
    } else {
      root.removeAttribute("top");
    }

    // Also offset the label container to the right or left if the magnifier is close to
    // the edge.
    root.removeAttribute("left");
    root.removeAttribute("right");
    if (x <= MAGNIFIER_WIDTH) {
      root.setAttribute("right", "");
    } else if (x >= this.win.innerWidth - MAGNIFIER_WIDTH) {
      root.setAttribute("left", "");
    }
  },

  /**
   * Select the current color that's being previewed. Depending on the current options,
   * selecting might mean copying to the clipboard and closing the
   */
  selectColor() {
    let onColorSelected = Promise.resolve();
    if (this.options.copyOnSelect) {
      onColorSelected = this.copyColor();
    }

    this.emit("selected", toColorString(this.centerColor, this.format));
    onColorSelected.then(() => this.hide(), console.error);
  },

  /**
   * Handler for the keydown event. Either select the color or move the panel in a
   * direction depending on the key pressed.
   */
  handleKeyDown(e) {
    // Bail out early if any unsupported modifier is used, so that we let
    // keyboard shortcuts through.
    if (e.metaKey || e.ctrlKey || e.altKey) {
      return;
    }

    if (e.keyCode === e.DOM_VK_RETURN) {
      this.selectColor();
      e.preventDefault();
      return;
    }

    if (e.keyCode === e.DOM_VK_ESCAPE) {
      this.emit("canceled");
      this.hide();
      e.preventDefault();
      return;
    }

    let offsetX = 0;
    let offsetY = 0;
    let modifier = 1;

    if (e.keyCode === e.DOM_VK_LEFT) {
      offsetX = -1;
    } else if (e.keyCode === e.DOM_VK_RIGHT) {
      offsetX = 1;
    } else if (e.keyCode === e.DOM_VK_UP) {
      offsetY = -1;
    } else if (e.keyCode === e.DOM_VK_DOWN) {
      offsetY = 1;
    }

    if (e.shiftKey) {
      modifier = 10;
    }

    offsetY *= modifier;
    offsetX *= modifier;

    if (offsetX !== 0 || offsetY !== 0) {
      this.magnifiedArea.x = cap(this.magnifiedArea.x + offsetX,
                                 0, this.win.innerWidth * this.pageZoom);
      this.magnifiedArea.y = cap(this.magnifiedArea.y + offsetY, 0,
                                 this.win.innerHeight * this.pageZoom);

      this.draw();

      this.moveTo(this.magnifiedArea.x / this.pageZoom,
                  this.magnifiedArea.y / this.pageZoom);

      e.preventDefault();
    }
  },

  /**
   * Copy the currently inspected color to the clipboard.
   * @return {Promise} Resolves when the copy has been done (after a delay that is used to
   * let users know that something was copied).
   */
  copyColor() {
    // Copy to the clipboard.
    let color = toColorString(this.centerColor, this.format);
    clipboardHelper.copyString(color);

    // Provide some feedback.
    this.getElement("color-value").setTextContent(
      "✓ " + l10n.GetStringFromName("colorValue.copied"));

    // Hide the tool after a delay.
    clearTimeout(this._copyTimeout);
    return new Promise(resolve => {
      this._copyTimeout = setTimeout(resolve, CLOSE_DELAY);
    });
  }
};

exports.EyeDropper = EyeDropper;

/**
 * Draw the visible portion of the window on a canvas and get the resulting ImageData.
 * @param {Window} win
 * @return {ImageData} The image data for the window.
 */
function getWindowAsImageData(win) {
  let canvas = win.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
  let scale = getCurrentZoom(win);
  let width = win.innerWidth;
  let height = win.innerHeight;
  canvas.width = width * scale;
  canvas.height = height * scale;
  canvas.mozOpaque = true;

  let ctx = canvas.getContext("2d");

  ctx.scale(scale, scale);
  ctx.drawWindow(win, win.scrollX, win.scrollY, width, height, "#fff");

  return ctx.getImageData(0, 0, canvas.width, canvas.height);
}

/**
 * Get a formatted CSS color string from a color value.
 * @param {array} rgb Rgb values of a color to format.
 * @param {string} format Format of string. One of "hex", "rgb", "hsl", "name".
 * @return {string} Formatted color value, e.g. "#FFF" or "hsl(20, 10%, 10%)".
 */
function toColorString(rgb, format) {
  let [r, g, b] = rgb;

  switch (format) {
    case "hex":
      return hexString(rgb);
    case "rgb":
      return "rgb(" + r + ", " + g + ", " + b + ")";
    case "hsl":
      let [h, s, l] = rgbToHsl(rgb);
      return "hsl(" + h + ", " + s + "%, " + l + "%)";
    case "name":
      let str = rgbToColorName(r, g, b) || hexString(rgb);
      return str;
    default:
      return hexString(rgb);
  }
}

/**
 * Produce a hex-formatted color string from rgb values.
 * @param {array} rgb Rgb values of color to stringify.
 * @return {string} Hex formatted string for color, e.g. "#FFEE00".
 */
function hexString([r, g, b]) {
  let val = (1 << 24) + (r << 16) + (g << 8) + (b << 0);
  return "#" + val.toString(16).substr(-6);
}

function cap(value, min, max) {
  return Math.max(min, Math.min(value, max));
}
PK
!<��5HHFchrome/devtools/modules/devtools/server/actors/highlighters/flexbox.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { AutoRefreshHighlighter } = require("./auto-refresh");
const {
  CANVAS_SIZE,
  DEFAULT_COLOR,
  clearRect,
  drawLine,
  drawRect,
  getCurrentMatrix,
  updateCanvasElement,
  updateCanvasPosition,
} = require("./utils/canvas");
const {
  CanvasFrameAnonymousContentHelper,
  createNode,
  getComputedStyle,
} = require("./utils/markup");
const {
  getAdjustedQuads,
  getDisplayPixelRatio,
  setIgnoreLayoutChanges,
} = require("devtools/shared/layout/utils");

const FLEXBOX_LINES_PROPERTIES = {
  "edge": {
    lineDash: [12, 10]
  },
  "item": {
    lineDash: [0, 0]
  }
};

const FLEXBOX_CONTAINER_PATTERN_WIDTH = 14; // px
const FLEXBOX_CONTAINER_PATTERN_HEIGHT = 14; // px
const FLEXBOX_JUSTIFY_CONTENT_PATTERN_WIDTH = 7; // px
const FLEXBOX_JUSTIFY_CONTENT_PATTERN_HEIGHT = 7; // px
const FLEXBOX_CONTAINER_PATTERN_LINE_DISH = [5, 3]; // px
const BASIS_FILL_COLOR = "rgb(109, 184, 255, 0.4)";

/**
 * Cached used by `FlexboxHighlighter.getFlexContainerPattern`.
 */
const gCachedFlexboxPattern = new Map();

const FLEXBOX = "flexbox";
const JUSTIFY_CONTENT = "justify-content";

class FlexboxHighlighter extends AutoRefreshHighlighter {
  constructor(highlighterEnv) {
    super(highlighterEnv);

    this.ID_CLASS_PREFIX = "flexbox-";

    this.markup = new CanvasFrameAnonymousContentHelper(this.highlighterEnv,
      this._buildMarkup.bind(this));

    this.onPageHide = this.onPageHide.bind(this);
    this.onWillNavigate = this.onWillNavigate.bind(this);

    this.highlighterEnv.on("will-navigate", this.onWillNavigate);

    let { pageListenerTarget } = highlighterEnv;
    pageListenerTarget.addEventListener("pagehide", this.onPageHide);

    // Initialize the <canvas> position to the top left corner of the page
    this._canvasPosition = {
      x: 0,
      y: 0
    };

    // Calling `updateCanvasPosition` anyway since the highlighter could be initialized
    // on a page that has scrolled already.
    updateCanvasPosition(this._canvasPosition, this._scroll, this.win,
      this._winDimensions);
  }

  _buildMarkup() {
    let container = createNode(this.win, {
      attributes: {
        "class": "highlighter-container"
      }
    });

    let root = createNode(this.win, {
      parent: container,
      attributes: {
        "id": "root",
        "class": "root"
      },
      prefix: this.ID_CLASS_PREFIX
    });

    // We use a <canvas> element because there is an arbitrary number of items and texts
    // to draw which wouldn't be possible with HTML or SVG without having to insert and
    // remove the whole markup on every update.
    createNode(this.win, {
      parent: root,
      nodeType: "canvas",
      attributes: {
        "id": "canvas",
        "class": "canvas",
        "hidden": "true",
        "width": CANVAS_SIZE,
        "height": CANVAS_SIZE
      },
      prefix: this.ID_CLASS_PREFIX
    });

    return container;
  }

  clearCache() {
    gCachedFlexboxPattern.clear();
  }

  destroy() {
    let { highlighterEnv } = this;
    highlighterEnv.off("will-navigate", this.onWillNavigate);

    let { pageListenerTarget } = highlighterEnv;

    if (pageListenerTarget) {
      pageListenerTarget.removeEventListener("pagehide", this.onPageHide);
    }

    this.markup.destroy();

    // Clear the pattern cache to avoid dead object exceptions (Bug 1342051).
    this.clearCache();
    AutoRefreshHighlighter.prototype.destroy.call(this);
  }

  /**
   * Draw the justify content for a given flex item (left, top, right, bottom) position.
   */
  drawJustifyContent(left, top, right, bottom) {
    let { devicePixelRatio } = this.win;
    this.ctx.fillStyle = this.getJustifyContentPattern(devicePixelRatio);
    drawRect(this.ctx, left, top, right, bottom, this.currentMatrix);
    this.ctx.fill();
  }

  get canvas() {
    return this.getElement("canvas");
  }

  get ctx() {
    return this.canvas.getCanvasContext("2d");
  }

  getElement(id) {
    return this.markup.getElement(this.ID_CLASS_PREFIX + id);
  }

  /**
  * Gets the flexbox container pattern used to render the container regions.
  *
  * @param  {Number} devicePixelRatio
  *         The device pixel ratio we want the pattern for.
  * @return {CanvasPattern} flex container pattern.
  */
  getFlexContainerPattern(devicePixelRatio) {
    let flexboxPatternMap = null;

    if (gCachedFlexboxPattern.has(devicePixelRatio)) {
      flexboxPatternMap = gCachedFlexboxPattern.get(devicePixelRatio);
    } else {
      flexboxPatternMap = new Map();
    }

    if (gCachedFlexboxPattern.has(FLEXBOX)) {
      return gCachedFlexboxPattern.get(FLEXBOX);
    }

    // Create the diagonal lines pattern for the rendering the flexbox gaps.
    let canvas = createNode(this.win, { nodeType: "canvas" });
    let width = canvas.width = FLEXBOX_CONTAINER_PATTERN_WIDTH * devicePixelRatio;
    let height = canvas.height = FLEXBOX_CONTAINER_PATTERN_HEIGHT * devicePixelRatio;

    let ctx = canvas.getContext("2d");
    ctx.save();
    ctx.setLineDash(FLEXBOX_CONTAINER_PATTERN_LINE_DISH);
    ctx.beginPath();
    ctx.translate(.5, .5);

    ctx.moveTo(0, 0);
    ctx.lineTo(width, height);

    ctx.strokeStyle = DEFAULT_COLOR;
    ctx.stroke();
    ctx.restore();

    let pattern = ctx.createPattern(canvas, "repeat");
    flexboxPatternMap.set(FLEXBOX, pattern);
    gCachedFlexboxPattern.set(devicePixelRatio, flexboxPatternMap);

    return pattern;
  }

  /**
  * Gets the flexbox justify content pattern used to render the justify content regions.
  *
  * @param  {Number} devicePixelRatio
  *         The device pixel ratio we want the pattern for.
  * @return {CanvasPattern} flex justify content pattern.
  */
  getJustifyContentPattern(devicePixelRatio) {
    let flexboxPatternMap = null;

    if (gCachedFlexboxPattern.has(devicePixelRatio)) {
      flexboxPatternMap = gCachedFlexboxPattern.get(devicePixelRatio);
    } else {
      flexboxPatternMap = new Map();
    }

    if (gCachedFlexboxPattern.has(JUSTIFY_CONTENT)) {
      return gCachedFlexboxPattern.get(JUSTIFY_CONTENT);
    }

    // Create the inversed diagonal lines pattern
    // for the rendering the justify content gaps.
    let canvas = createNode(this.win, { nodeType: "canvas" });
    let width = canvas.width = FLEXBOX_JUSTIFY_CONTENT_PATTERN_WIDTH * devicePixelRatio;
    let height = canvas.height = FLEXBOX_JUSTIFY_CONTENT_PATTERN_HEIGHT *
      devicePixelRatio;

    let ctx = canvas.getContext("2d");
    ctx.save();
    ctx.setLineDash(FLEXBOX_CONTAINER_PATTERN_LINE_DISH);
    ctx.beginPath();
    ctx.translate(.5, .5);

    ctx.moveTo(0, height);
    ctx.lineTo(width, 0);

    ctx.strokeStyle = DEFAULT_COLOR;
    ctx.stroke();
    ctx.restore();

    let pattern = ctx.createPattern(canvas, "repeat");
    flexboxPatternMap.set(JUSTIFY_CONTENT, pattern);
    gCachedFlexboxPattern.set(devicePixelRatio, flexboxPatternMap);

    return pattern;
  }

  /**
   * The AutoRefreshHighlighter's _hasMoved method returns true only if the
   * element's quads have changed. Override it so it also returns true if the
   * element and its flex items have changed.
   */
  _hasMoved() {
    let hasMoved = AutoRefreshHighlighter.prototype._hasMoved.call(this);

    // TODO: Implement a check of old and new flex container and flex items to react
    // to any alignment and size changes. This is blocked on Bug 1414920 that implements
    // a platform API to retrieve the flex container and flex item information.

    return hasMoved;
  }

  _hide() {
    setIgnoreLayoutChanges(true);
    this._hideFlexbox();
    setIgnoreLayoutChanges(false, this.highlighterEnv.document.documentElement);
  }

  _hideFlexbox() {
    this.getElement("canvas").setAttribute("hidden", "true");
  }

  /**
   * The <canvas>'s position needs to be updated if the page scrolls too much, in order
   * to give the illusion that it always covers the viewport.
   */
  _scrollUpdate() {
    let hasUpdated = updateCanvasPosition(this._canvasPosition, this._scroll, this.win,
      this._winDimensions);

    if (hasUpdated) {
      this._update();
    }
  }

  _show() {
    this._hide();
    return this._update();
  }

  _showFlexbox() {
    this.getElement("canvas").removeAttribute("hidden");
  }

  /**
   * If a page hide event is triggered for current window's highlighter, hide the
   * highlighter.
   */
  onPageHide({ target }) {
    if (target.defaultView === this.win) {
      this.hide();
    }
  }

  /**
   * Called when the page will-navigate. Used to hide the flexbox highlighter and clear
   * the cached gap patterns and avoid using DeadWrapper obejcts as gap patterns the
   * next time.
   */
  onWillNavigate({ isTopLevel }) {
    this.clearCache();

    if (isTopLevel) {
      this.hide();
    }
  }

  renderFlexContainerBorder() {
    if (!this.currentQuads.content || !this.currentQuads.content[0]) {
      return;
    }

    let { devicePixelRatio } = this.win;
    let lineWidth = getDisplayPixelRatio(this.win) * 2;
    let offset = (lineWidth / 2) % 1;
    let canvasX = Math.round(this._canvasPosition.x * devicePixelRatio);
    let canvasY = Math.round(this._canvasPosition.y * devicePixelRatio);

    this.ctx.save();
    this.ctx.translate(offset - canvasX, offset - canvasY);
    this.ctx.setLineDash(FLEXBOX_LINES_PROPERTIES.edge.lineDash);
    this.ctx.lineWidth = lineWidth;
    this.ctx.strokeStyle = DEFAULT_COLOR;

    let { bounds } = this.currentQuads.content[0];
    drawRect(this.ctx, 0, 0, bounds.width, bounds.height, this.currentMatrix);

    this.ctx.stroke();
    this.ctx.restore();
  }

  renderFlexContainerFill() {
    if (!this.currentQuads.content || !this.currentQuads.content[0]) {
      return;
    }

    let { devicePixelRatio } = this.win;
    let lineWidth = getDisplayPixelRatio(this.win);
    let offset = (lineWidth / 2) % 1;
    let canvasX = Math.round(this._canvasPosition.x * devicePixelRatio);
    let canvasY = Math.round(this._canvasPosition.y * devicePixelRatio);

    this.ctx.save();
    this.ctx.translate(offset - canvasX, offset - canvasY);
    this.ctx.setLineDash(FLEXBOX_LINES_PROPERTIES.edge.lineDash);
    this.ctx.lineWidth = 0;
    this.ctx.strokeStyle = DEFAULT_COLOR;
    this.ctx.fillStyle = this.getFlexContainerPattern(devicePixelRatio);

    let { bounds } = this.currentQuads.content[0];
    drawRect(this.ctx, 0, 0, bounds.width, bounds.height, this.currentMatrix);

    this.ctx.fill();
    this.ctx.stroke();
    this.ctx.restore();
  }

  /**
   * Renders the flex basis for a given flex item.
   */
  renderFlexItemBasis(flexItem, left, top, right, bottom, boundsWidth) {
    let computedStyle = getComputedStyle(flexItem);

    if (!computedStyle) {
      return;
    }

    let basis = computedStyle.getPropertyValue("flex-basis");

    if (basis.endsWith("px")) {
      right = Math.round(left + parseFloat(basis));
    } else if (basis.endsWith("%")) {
      basis = parseFloat(basis) / 100 * boundsWidth;
      right = Math.round(left + basis);
    }

    this.ctx.fillStyle = BASIS_FILL_COLOR;
    drawRect(this.ctx, left, top, right, bottom, this.currentMatrix);
    this.ctx.fill();
  }

  renderFlexItems() {
    if (!this.currentQuads.content || !this.currentQuads.content[0]) {
      return;
    }

    let { devicePixelRatio } = this.win;
    let lineWidth = getDisplayPixelRatio(this.win);
    let offset = (lineWidth / 2) % 1;
    let canvasX = Math.round(this._canvasPosition.x * devicePixelRatio);
    let canvasY = Math.round(this._canvasPosition.y * devicePixelRatio);

    this.ctx.save();
    this.ctx.translate(offset - canvasX, offset - canvasY);
    this.ctx.setLineDash(FLEXBOX_LINES_PROPERTIES.item.lineDash);
    this.ctx.lineWidth = lineWidth;
    this.ctx.strokeStyle = DEFAULT_COLOR;

    let { bounds } = this.currentQuads.content[0];
    let flexLines = this.currentNode.getAsFlexContainer().getLines();

    for (let flexLine of flexLines) {
      let flexItems = flexLine.getItems();

      for (let flexItem of flexItems) {
        let { node } = flexItem;
        let quads = getAdjustedQuads(this.win, node, "border");

        if (!quads.length) {
          continue;
        }

        // Adjust the flex item bounds relative to the current quads.
        let { bounds: flexItemBounds } = quads[0];
        let left = Math.round(flexItemBounds.left - bounds.left);
        let top = Math.round(flexItemBounds.top - bounds.top);
        let right = Math.round(flexItemBounds.right - bounds.left);
        let bottom = Math.round(flexItemBounds.bottom - bounds.top);

        clearRect(this.ctx, left, top, right, bottom, this.currentMatrix);
        drawRect(this.ctx, left, top, right, bottom, this.currentMatrix);
        this.ctx.stroke();

        this.renderFlexItemBasis(node, left, top, right, bottom, bounds.width);
      }
    }

    this.ctx.restore();
  }

  renderFlexLines() {
    if (!this.currentQuads.content || !this.currentQuads.content[0]) {
      return;
    }

    let { devicePixelRatio } = this.win;
    let lineWidth = getDisplayPixelRatio(this.win);
    let offset = (lineWidth / 2) % 1;
    let canvasX = Math.round(this._canvasPosition.x * devicePixelRatio);
    let canvasY = Math.round(this._canvasPosition.y * devicePixelRatio);

    this.ctx.save();
    this.ctx.translate(offset - canvasX, offset - canvasY);
    this.ctx.lineWidth = lineWidth;
    this.ctx.strokeStyle = DEFAULT_COLOR;

    let { bounds } = this.currentQuads.content[0];
    let flexLines = this.currentNode.getAsFlexContainer().getLines();
    let computedStyle = getComputedStyle(this.currentNode);
    let isColumn = computedStyle.getPropertyValue("flex-direction").startsWith("column");
    let options = { matrix: this.currentMatrix };

    for (let flexLine of flexLines) {
      let { crossStart, crossSize } = flexLine;

      if (isColumn) {
        clearRect(this.ctx, crossStart, 0, crossStart + crossSize, bounds.height,
          this.currentMatrix);

        // Avoid drawing the start flex line when they overlap with the flex container.
        if (crossStart != 0) {
          drawLine(this.ctx, crossStart, 0, crossStart, bounds.height, options);
          this.ctx.stroke();
        }

        // Avoid drawing the end flex line when they overlap with the flex container.
        if (bounds.width - crossStart - crossSize >= lineWidth) {
          drawLine(this.ctx, crossStart + crossSize, 0, crossStart + crossSize,
            bounds.height, options);
          this.ctx.stroke();
        }
      } else {
        clearRect(this.ctx, 0, crossStart, bounds.width, crossStart + crossSize,
          this.currentMatrix);

        // Avoid drawing the start flex line when they overlap with the flex container.
        if (crossStart != 0) {
          drawLine(this.ctx, 0, crossStart, bounds.width, crossStart, options);
          this.ctx.stroke();
        }

        // Avoid drawing the end flex line when they overlap with the flex container.
        if (bounds.height - crossStart - crossSize >= lineWidth) {
          drawLine(this.ctx, 0, crossStart + crossSize, bounds.width,
            crossStart + crossSize, options);
          this.ctx.stroke();
        }
      }
    }

    this.ctx.restore();
  }

  renderJustifyContent() {
    if (!this.currentQuads.content || !this.currentQuads.content[0]) {
      return;
    }

    let { bounds } = this.currentQuads.content[0];
    let flexLines = this.currentNode.getAsFlexContainer().getLines();
    let computedStyle = getComputedStyle(this.currentNode);
    let isColumn = computedStyle.getPropertyValue("flex-direction").startsWith("column");

    for (let flexLine of flexLines) {
      let { crossStart, crossSize } = flexLine;
      let flexItems = flexLine.getItems();
      let mainStart = 0;

      for (let flexItem of flexItems) {
        let { node } = flexItem;
        let quads = getAdjustedQuads(this.win, node, "margin");

        if (!quads.length) {
          continue;
        }

        // Adjust the flex item bounds relative to the current quads.
        let { bounds: flexItemBounds } = quads[0];
        let left = Math.round(flexItemBounds.left - bounds.left);
        let top = Math.round(flexItemBounds.top - bounds.top);
        let right = Math.round(flexItemBounds.right - bounds.left);
        let bottom = Math.round(flexItemBounds.bottom - bounds.top);

        if (isColumn) {
          this.drawJustifyContent(crossStart, mainStart, crossStart + crossSize, top);
          mainStart = bottom;
        } else {
          this.drawJustifyContent(mainStart, crossStart, left, crossStart + crossSize);
          mainStart = right;
        }
      }

      // Draw the last justify-content area after the last flex item.
      if (isColumn) {
        this.drawJustifyContent(crossStart, mainStart, crossStart + crossSize,
          bounds.height);
      } else {
        this.drawJustifyContent(mainStart, crossStart, bounds.width,
          crossStart + crossSize);
      }
    }
  }

  _update() {
    setIgnoreLayoutChanges(true);

    let root = this.getElement("root");

    // Hide the root element and force the reflow in order to get the proper window's
    // dimensions without increasing them.
    root.setAttribute("style", "display: none");
    this.win.document.documentElement.offsetWidth;

    let { width, height } = this._winDimensions;

    // Updates the <canvas> element's position and size.
    // It also clear the <canvas>'s drawing context.
    updateCanvasElement(this.canvas, this._canvasPosition, this.win.devicePixelRatio);

    // Update the current matrix used in our canvas' rendering
    let { currentMatrix, hasNodeTransformations } = getCurrentMatrix(this.currentNode,
      this.win);
    this.currentMatrix = currentMatrix;
    this.hasNodeTransformations = hasNodeTransformations;

    this.renderFlexContainerFill();
    this.renderFlexLines();
    this.renderJustifyContent();
    this.renderFlexItems();
    this.renderFlexContainerBorder();

    this._showFlexbox();

    root.setAttribute("style",
      `position: absolute; width: ${width}px; height: ${height}px; overflow: hidden`);

    setIgnoreLayoutChanges(false, this.highlighterEnv.document.documentElement);
    return true;
  }
}

exports.FlexboxHighlighter = FlexboxHighlighter;
PK
!<�ݑw�X�XNchrome/devtools/modules/devtools/server/actors/highlighters/geometry-editor.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { AutoRefreshHighlighter } = require("./auto-refresh");
const { CanvasFrameAnonymousContentHelper, getComputedStyle,
        createSVGNode, createNode } = require("./utils/markup");
const { setIgnoreLayoutChanges, getAdjustedQuads } = require("devtools/shared/layout/utils");
const { getCSSStyleRules } = require("devtools/shared/inspector/css-logic");

const GEOMETRY_LABEL_SIZE = 6;

// List of all DOM Events subscribed directly to the document from the
// Geometry Editor highlighter
const DOM_EVENTS = ["mousemove", "mouseup", "pagehide"];

const _dragging = Symbol("geometry/dragging");

/**
 * Element geometry properties helper that gives names of position and size
 * properties.
 */
var GeoProp = {
  SIDES: ["top", "right", "bottom", "left"],
  SIZES: ["width", "height"],

  allProps: function () {
    return [...this.SIDES, ...this.SIZES];
  },

  isSide: function (name) {
    return this.SIDES.includes(name);
  },

  isSize: function (name) {
    return this.SIZES.includes(name);
  },

  containsSide: function (names) {
    return names.some(name => this.SIDES.includes(name));
  },

  containsSize: function (names) {
    return names.some(name => this.SIZES.includes(name));
  },

  isHorizontal: function (name) {
    return name === "left" || name === "right" || name === "width";
  },

  isInverted: function (name) {
    return name === "right" || name === "bottom";
  },

  mainAxisStart: function (name) {
    return this.isHorizontal(name) ? "left" : "top";
  },

  crossAxisStart: function (name) {
    return this.isHorizontal(name) ? "top" : "left";
  },

  mainAxisSize: function (name) {
    return this.isHorizontal(name) ? "width" : "height";
  },

  crossAxisSize: function (name) {
    return this.isHorizontal(name) ? "height" : "width";
  },

  axis: function (name) {
    return this.isHorizontal(name) ? "x" : "y";
  },

  crossAxis: function (name) {
    return this.isHorizontal(name) ? "y" : "x";
  }
};

/**
 * Get the provided node's offsetParent dimensions.
 * Returns an object with the {parent, dimension} properties.
 * Note that the returned parent will be null if the offsetParent is the
 * default, non-positioned, body or html node.
 *
 * node.offsetParent returns the nearest positioned ancestor but if it is
 * non-positioned itself, we just return null to let consumers know the node is
 * actually positioned relative to the viewport.
 *
 * @return {Object}
 */
function getOffsetParent(node) {
  let win = node.ownerGlobal;

  let offsetParent = node.offsetParent;
  if (offsetParent &&
      getComputedStyle(offsetParent).position === "static") {
    offsetParent = null;
  }

  let width, height;
  if (!offsetParent) {
    height = win.innerHeight;
    width = win.innerWidth;
  } else {
    height = offsetParent.offsetHeight;
    width = offsetParent.offsetWidth;
  }

  return {
    element: offsetParent,
    dimension: {width, height}
  };
}

/**
 * Get the list of geometry properties that are actually set on the provided
 * node.
 *
 * @param {nsIDOMNode} node The node to analyze.
 * @return {Map} A map indexed by property name and where the value is an
 * object having the cssRule property.
 */
function getDefinedGeometryProperties(node) {
  let props = new Map();
  if (!node) {
    return props;
  }

  // Get the list of css rules applying to the current node.
  let cssRules = getCSSStyleRules(node);
  for (let i = 0; i < cssRules.length; i++) {
    let rule = cssRules[i];
    for (let name of GeoProp.allProps()) {
      let value = rule.style.getPropertyValue(name);
      if (value && value !== "auto") {
        // getCSSStyleRules returns rules ordered from least to most specific
        // so just override any previous properties we have set.
        props.set(name, {
          cssRule: rule
        });
      }
    }
  }

  // Go through the inline styles last, only if the node supports inline style
  // (e.g. pseudo elements don't have a style property)
  if (node.style) {
    for (let name of GeoProp.allProps()) {
      let value = node.style.getPropertyValue(name);
      if (value && value !== "auto") {
        props.set(name, {
          // There's no cssRule to store here, so store the node instead since
          // node.style exists.
          cssRule: node
        });
      }
    }
  }

  // Post-process the list for invalid properties. This is done after the fact
  // because of cases like relative positioning with both top and bottom where
  // only top will actually be used, but both exists in css rules and computed
  // styles.
  let { position } = getComputedStyle(node);
  for (let [name] of props) {
    // Top/left/bottom/right on static positioned elements have no effect.
    if (position === "static" && GeoProp.SIDES.includes(name)) {
      props.delete(name);
    }

    // Bottom/right on relative positioned elements are only used if top/left
    // are not defined.
    let hasRightAndLeft = name === "right" && props.has("left");
    let hasBottomAndTop = name === "bottom" && props.has("top");
    if (position === "relative" && (hasRightAndLeft || hasBottomAndTop)) {
      props.delete(name);
    }
  }

  return props;
}
exports.getDefinedGeometryProperties = getDefinedGeometryProperties;

/**
 * The GeometryEditor highlights an elements's top, left, bottom, right, width
 * and height dimensions, when they are set.
 *
 * To determine if an element has a set size and position, the highlighter lists
 * the CSS rules that apply to the element and checks for the top, left, bottom,
 * right, width and height properties.
 * The highlighter won't be shown if the element doesn't have any of these
 * properties set, but will be shown when at least 1 property is defined.
 *
 * The highlighter displays lines and labels for each of the defined properties
 * in and around the element (relative to the offset parent when one exists).
 * The highlighter also highlights the element itself and its offset parent if
 * there is one.
 *
 * Note that the class name contains the word Editor because the aim is for the
 * handles to be draggable in content to make the geometry editable.
 */
class GeometryEditorHighlighter extends AutoRefreshHighlighter {
  constructor(highlighterEnv) {
    super(highlighterEnv);

    this.ID_CLASS_PREFIX = "geometry-editor-";

    // The list of element geometry properties that can be set.
    this.definedProperties = new Map();

    this.markup = new CanvasFrameAnonymousContentHelper(highlighterEnv,
      this._buildMarkup.bind(this));

    let { pageListenerTarget } = this.highlighterEnv;

    // Register the geometry editor instance to all events we're interested in.
    DOM_EVENTS.forEach(type => pageListenerTarget.addEventListener(type, this));

    // Register the mousedown event for each Geometry Editor's handler.
    // Those events are automatically removed when the markup is destroyed.
    let onMouseDown = this.handleEvent.bind(this);

    for (let side of GeoProp.SIDES) {
      this.getElement("handler-" + side)
        .addEventListener("mousedown", onMouseDown);
    }

    this.onWillNavigate = this.onWillNavigate.bind(this);

    this.highlighterEnv.on("will-navigate", this.onWillNavigate);
  }

  _buildMarkup() {
    let container = createNode(this.win, {
      attributes: {"class": "highlighter-container"}
    });

    let root = createNode(this.win, {
      parent: container,
      attributes: {
        "id": "root",
        "class": "root",
        "hidden": "true"
      },
      prefix: this.ID_CLASS_PREFIX
    });

    let svg = createSVGNode(this.win, {
      nodeType: "svg",
      parent: root,
      attributes: {
        "id": "elements",
        "width": "100%",
        "height": "100%"
      },
      prefix: this.ID_CLASS_PREFIX
    });

    // Offset parent node highlighter.
    createSVGNode(this.win, {
      nodeType: "polygon",
      parent: svg,
      attributes: {
        "class": "offset-parent",
        "id": "offset-parent",
        "hidden": "true"
      },
      prefix: this.ID_CLASS_PREFIX
    });

    // Current node highlighter (margin box).
    createSVGNode(this.win, {
      nodeType: "polygon",
      parent: svg,
      attributes: {
        "class": "current-node",
        "id": "current-node",
        "hidden": "true"
      },
      prefix: this.ID_CLASS_PREFIX
    });

    // Build the 4 side arrows, handlers and labels.
    for (let name of GeoProp.SIDES) {
      createSVGNode(this.win, {
        nodeType: "line",
        parent: svg,
        attributes: {
          "class": "arrow " + name,
          "id": "arrow-" + name,
          "hidden": "true"
        },
        prefix: this.ID_CLASS_PREFIX
      });

      createSVGNode(this.win, {
        nodeType: "circle",
        parent: svg,
        attributes: {
          "class": "handler-" + name,
          "id": "handler-" + name,
          "r": "4",
          "data-side": name,
          "hidden": "true"
        },
        prefix: this.ID_CLASS_PREFIX
      });

      // Labels are positioned by using a translated <g>. This group contains
      // a path and text that are themselves positioned using another translated
      // <g>. This is so that the label arrow points at the 0,0 coordinates of
      // parent <g>.
      let labelG = createSVGNode(this.win, {
        nodeType: "g",
        parent: svg,
        attributes: {
          "id": "label-" + name,
          "hidden": "true"
        },
        prefix: this.ID_CLASS_PREFIX
      });

      let subG = createSVGNode(this.win, {
        nodeType: "g",
        parent: labelG,
        attributes: {
          "transform": GeoProp.isHorizontal(name)
                       ? "translate(-30 -30)"
                       : "translate(5 -10)"
        }
      });

      createSVGNode(this.win, {
        nodeType: "path",
        parent: subG,
        attributes: {
          "class": "label-bubble",
          "d": GeoProp.isHorizontal(name)
               ? "M0 0 L60 0 L60 20 L35 20 L30 25 L25 20 L0 20z"
               : "M5 0 L65 0 L65 20 L5 20 L5 15 L0 10 L5 5z"
        },
        prefix: this.ID_CLASS_PREFIX
      });

      createSVGNode(this.win, {
        nodeType: "text",
        parent: subG,
        attributes: {
          "class": "label-text",
          "id": "label-text-" + name,
          "x": GeoProp.isHorizontal(name) ? "30" : "35",
          "y": "10"
        },
        prefix: this.ID_CLASS_PREFIX
      });
    }

    return container;
  }

  destroy() {
    // Avoiding exceptions if `destroy` is called multiple times; and / or the
    // highlighter environment was already destroyed.
    if (!this.highlighterEnv) {
      return;
    }

    let { pageListenerTarget } = this.highlighterEnv;

    if (pageListenerTarget) {
      DOM_EVENTS.forEach(type =>
        pageListenerTarget.removeEventListener(type, this));
    }

    AutoRefreshHighlighter.prototype.destroy.call(this);

    this.markup.destroy();
    this.definedProperties.clear();
    this.definedProperties = null;
    this.offsetParent = null;
  }

  handleEvent(event, id) {
    // No event handling if the highlighter is hidden
    if (this.getElement("root").hasAttribute("hidden")) {
      return;
    }

    const { target, type, pageX, pageY } = event;

    switch (type) {
      case "pagehide":
        // If a page hide event is triggered for current window's highlighter, hide the
        // highlighter.
        if (target.defaultView === this.win) {
          this.destroy();
        }

        break;
      case "mousedown":
        // The mousedown event is intended only for the handler
        if (!id) {
          return;
        }

        let handlerSide = this.markup.getElement(id).getAttribute("data-side");

        if (handlerSide) {
          let side = handlerSide;
          let sideProp = this.definedProperties.get(side);

          if (!sideProp) {
            return;
          }

          let value = sideProp.cssRule.style.getPropertyValue(side);
          let computedValue = this.computedStyle.getPropertyValue(side);

          let [unit] = value.match(/[^\d]+$/) || [""];

          value = parseFloat(value);

          let ratio = (value / parseFloat(computedValue)) || 1;
          let dir = GeoProp.isInverted(side) ? -1 : 1;

          // Store all the initial values needed for drag & drop
          this[_dragging] = {
            side,
            value,
            unit,
            x: pageX,
            y: pageY,
            inc: ratio * dir
          };

          this.getElement("handler-" + side).classList.add("dragging");
        }

        this.getElement("root").setAttribute("dragging", "true");
        break;
      case "mouseup":
        // If we're dragging, drop it.
        if (this[_dragging]) {
          let { side } = this[_dragging];
          this.getElement("root").removeAttribute("dragging");
          this.getElement("handler-" + side).classList.remove("dragging");
          this[_dragging] = null;
        }
        break;
      case "mousemove":
        if (!this[_dragging]) {
          return;
        }

        let { side, x, y, value, unit, inc } = this[_dragging];
        let sideProps = this.definedProperties.get(side);

        if (!sideProps) {
          return;
        }

        let delta = (GeoProp.isHorizontal(side) ? pageX - x : pageY - y) * inc;

        // The inline style has usually the priority over any other CSS rule
        // set in stylesheets. However, if a rule has `!important` keyword,
        // it will override the inline style too. To ensure Geometry Editor
        // will always update the element, we have to add `!important` as
        // well.
        this.currentNode.style.setProperty(
          side, (value + delta) + unit, "important");

        break;
    }
  }

  getElement(id) {
    return this.markup.getElement(this.ID_CLASS_PREFIX + id);
  }

  _show() {
    this.computedStyle = getComputedStyle(this.currentNode);
    let pos = this.computedStyle.position;
    // XXX: sticky positioning is ignored for now. To be implemented next.
    if (pos === "sticky") {
      this.hide();
      return false;
    }

    let hasUpdated = this._update();
    if (!hasUpdated) {
      this.hide();
      return false;
    }

    this.getElement("root").removeAttribute("hidden");

    return true;
  }

  _update() {
    // At each update, the position or/and size may have changed, so get the
    // list of defined properties, and re-position the arrows and highlighters.
    this.definedProperties = getDefinedGeometryProperties(this.currentNode);

    if (!this.definedProperties.size) {
      console.warn("The element does not have editable geometry properties");
      return false;
    }

    setIgnoreLayoutChanges(true);

    // Update the highlighters and arrows.
    this.updateOffsetParent();
    this.updateCurrentNode();
    this.updateArrows();

    // Avoid zooming the arrows when content is zoomed.
    let node = this.currentNode;
    this.markup.scaleRootElement(node, this.ID_CLASS_PREFIX + "root");

    setIgnoreLayoutChanges(false, this.highlighterEnv.document.documentElement);
    return true;
  }

  /**
   * Update the offset parent rectangle.
   * There are 3 different cases covered here:
   * - the node is absolutely/fixed positioned, and an offsetParent is defined
   *   (i.e. it's not just positioned in the viewport): the offsetParent node
   *   is highlighted (i.e. the rectangle is shown),
   * - the node is relatively positioned: the rectangle is shown where the node
   *   would originally have been (because that's where the relative positioning
   *   is calculated from),
   * - the node has no offset parent at all: the offsetParent rectangle is
   *   hidden.
   */
  updateOffsetParent() {
    // Get the offsetParent, if any.
    this.offsetParent = getOffsetParent(this.currentNode);
    // And the offsetParent quads.
    this.parentQuads = getAdjustedQuads(
        this.win, this.offsetParent.element, "padding");

    let el = this.getElement("offset-parent");

    let isPositioned = this.computedStyle.position === "absolute" ||
                       this.computedStyle.position === "fixed";
    let isRelative = this.computedStyle.position === "relative";
    let isHighlighted = false;

    if (this.offsetParent.element && isPositioned) {
      let {p1, p2, p3, p4} = this.parentQuads[0];
      let points = p1.x + "," + p1.y + " " +
                   p2.x + "," + p2.y + " " +
                   p3.x + "," + p3.y + " " +
                   p4.x + "," + p4.y;
      el.setAttribute("points", points);
      isHighlighted = true;
    } else if (isRelative) {
      let xDelta = parseFloat(this.computedStyle.left);
      let yDelta = parseFloat(this.computedStyle.top);
      if (xDelta || yDelta) {
        let {p1, p2, p3, p4} = this.currentQuads.margin[0];
        let points = (p1.x - xDelta) + "," + (p1.y - yDelta) + " " +
                     (p2.x - xDelta) + "," + (p2.y - yDelta) + " " +
                     (p3.x - xDelta) + "," + (p3.y - yDelta) + " " +
                     (p4.x - xDelta) + "," + (p4.y - yDelta);
        el.setAttribute("points", points);
        isHighlighted = true;
      }
    }

    if (isHighlighted) {
      el.removeAttribute("hidden");
    } else {
      el.setAttribute("hidden", "true");
    }
  }

  updateCurrentNode() {
    let box = this.getElement("current-node");
    let {p1, p2, p3, p4} = this.currentQuads.margin[0];
    let attr = p1.x + "," + p1.y + " " +
               p2.x + "," + p2.y + " " +
               p3.x + "," + p3.y + " " +
               p4.x + "," + p4.y;
    box.setAttribute("points", attr);
    box.removeAttribute("hidden");
  }

  _hide() {
    setIgnoreLayoutChanges(true);

    this.getElement("root").setAttribute("hidden", "true");
    this.getElement("current-node").setAttribute("hidden", "true");
    this.getElement("offset-parent").setAttribute("hidden", "true");
    this.hideArrows();

    this.definedProperties.clear();

    setIgnoreLayoutChanges(false, this.highlighterEnv.document.documentElement);
  }

  hideArrows() {
    for (let side of GeoProp.SIDES) {
      this.getElement("arrow-" + side).setAttribute("hidden", "true");
      this.getElement("label-" + side).setAttribute("hidden", "true");
      this.getElement("handler-" + side).setAttribute("hidden", "true");
    }
  }

  updateArrows() {
    this.hideArrows();

    // Position arrows always end at the node's margin box.
    let marginBox = this.currentQuads.margin[0].bounds;

    // Position the side arrows which need to be visible.
    // Arrows always start at the offsetParent edge, and end at the middle
    // position of the node's margin edge.
    // Note that for relative positioning, the offsetParent is considered to be
    // the node itself, where it would have been originally.
    // +------------------+----------------+
    // | offsetparent     | top            |
    // | or viewport      |                |
    // |         +--------+--------+       |
    // |         | node            |       |
    // +---------+                 +-------+
    // | left    |                 | right |
    // |         +--------+--------+       |
    // |                  | bottom         |
    // +------------------+----------------+
    let getSideArrowStartPos = side => {
      // In case an offsetParent exists and is highlighted.
      if (this.parentQuads && this.parentQuads.length) {
        return this.parentQuads[0].bounds[side];
      }

      // In case of relative positioning.
      if (this.computedStyle.position === "relative") {
        if (GeoProp.isInverted(side)) {
          return marginBox[side] + parseFloat(this.computedStyle[side]);
        }
        return marginBox[side] - parseFloat(this.computedStyle[side]);
      }

      // In case the element is positioned in the viewport.
      if (GeoProp.isInverted(side)) {
        return this.offsetParent.dimension[GeoProp.mainAxisSize(side)];
      }
      return -1 * this.currentNode.ownerGlobal["scroll" +
                                              GeoProp.axis(side).toUpperCase()];
    };

    for (let side of GeoProp.SIDES) {
      let sideProp = this.definedProperties.get(side);
      if (!sideProp) {
        continue;
      }

      let mainAxisStartPos = getSideArrowStartPos(side);
      let mainAxisEndPos = marginBox[side];
      let crossAxisPos = marginBox[GeoProp.crossAxisStart(side)] +
                         marginBox[GeoProp.crossAxisSize(side)] / 2;

      this.updateArrow(side, mainAxisStartPos, mainAxisEndPos, crossAxisPos,
                       sideProp.cssRule.style.getPropertyValue(side));
    }
  }

  updateArrow(side, mainStart, mainEnd, crossPos, labelValue) {
    let arrowEl = this.getElement("arrow-" + side);
    let labelEl = this.getElement("label-" + side);
    let labelTextEl = this.getElement("label-text-" + side);
    let handlerEl = this.getElement("handler-" + side);

    // Position the arrow <line>.
    arrowEl.setAttribute(GeoProp.axis(side) + "1", mainStart);
    arrowEl.setAttribute(GeoProp.crossAxis(side) + "1", crossPos);
    arrowEl.setAttribute(GeoProp.axis(side) + "2", mainEnd);
    arrowEl.setAttribute(GeoProp.crossAxis(side) + "2", crossPos);
    arrowEl.removeAttribute("hidden");

    handlerEl.setAttribute("c" + GeoProp.axis(side), mainEnd);
    handlerEl.setAttribute("c" + GeoProp.crossAxis(side), crossPos);
    handlerEl.removeAttribute("hidden");

    // Position the label <text> in the middle of the arrow (making sure it's
    // not hidden below the fold).
    let capitalize = str => str[0].toUpperCase() + str.substring(1);
    let winMain = this.win["inner" + capitalize(GeoProp.mainAxisSize(side))];
    let labelMain = mainStart + (mainEnd - mainStart) / 2;
    if ((mainStart > 0 && mainStart < winMain) ||
        (mainEnd > 0 && mainEnd < winMain)) {
      if (labelMain < GEOMETRY_LABEL_SIZE) {
        labelMain = GEOMETRY_LABEL_SIZE;
      } else if (labelMain > winMain - GEOMETRY_LABEL_SIZE) {
        labelMain = winMain - GEOMETRY_LABEL_SIZE;
      }
    }
    let labelCross = crossPos;
    labelEl.setAttribute("transform", GeoProp.isHorizontal(side)
                         ? "translate(" + labelMain + " " + labelCross + ")"
                         : "translate(" + labelCross + " " + labelMain + ")");
    labelEl.removeAttribute("hidden");
    labelTextEl.setTextContent(labelValue);
  }

  onWillNavigate({ isTopLevel }) {
    if (isTopLevel) {
      this.hide();
    }
  }
}

exports.GeometryEditorHighlighter = GeometryEditorHighlighter;
PK
!<1�E��7�7Mchrome/devtools/modules/devtools/server/actors/highlighters/measuring-tool.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/shared/event-emitter");
const { getCurrentZoom, getWindowDimensions,
  setIgnoreLayoutChanges } = require("devtools/shared/layout/utils");
const {
  CanvasFrameAnonymousContentHelper,
  createSVGNode, createNode } = require("./utils/markup");

// Hard coded value about the size of measuring tool label, in order to
// position and flip it when is needed.
const LABEL_SIZE_MARGIN = 8;
const LABEL_SIZE_WIDTH = 80;
const LABEL_SIZE_HEIGHT = 52;
const LABEL_POS_MARGIN = 4;
const LABEL_POS_WIDTH = 40;
const LABEL_POS_HEIGHT = 34;

const SIDES = ["top", "right", "bottom", "left"];

/**
 * The MeasuringToolHighlighter is used to measure distances in a content page.
 * It allows users to click and drag with their mouse to draw an area whose
 * dimensions will be displayed in a tooltip next to it.
 * This allows users to measure distances between elements on a page.
 */
function MeasuringToolHighlighter(highlighterEnv) {
  this.env = highlighterEnv;
  this.markup = new CanvasFrameAnonymousContentHelper(highlighterEnv,
    this._buildMarkup.bind(this));

  this.coords = {
    x: 0,
    y: 0
  };

  let { pageListenerTarget } = highlighterEnv;

  pageListenerTarget.addEventListener("mousedown", this);
  pageListenerTarget.addEventListener("mousemove", this);
  pageListenerTarget.addEventListener("mouseleave", this);
  pageListenerTarget.addEventListener("scroll", this);
  pageListenerTarget.addEventListener("pagehide", this);
}

MeasuringToolHighlighter.prototype = {
  typeName: "MeasuringToolHighlighter",

  ID_CLASS_PREFIX: "measuring-tool-highlighter-",

  _buildMarkup() {
    let prefix = this.ID_CLASS_PREFIX;
    let { window } = this.env;

    let container = createNode(window, {
      attributes: {"class": "highlighter-container"}
    });

    let root = createNode(window, {
      parent: container,
      attributes: {
        "id": "root",
        "class": "root",
        "hidden": "true",
      },
      prefix
    });

    let svg = createSVGNode(window, {
      nodeType: "svg",
      parent: root,
      attributes: {
        id: "elements",
        "class": "elements",
        width: "100%",
        height: "100%",
      },
      prefix
    });

    createNode(window, {
      nodeType: "label",
      attributes: {
        id: "label-size",
        "class": "label-size",
        "hidden": "true"
      },
      parent: root,
      prefix
    });

    createNode(window, {
      nodeType: "label",
      attributes: {
        id: "label-position",
        "class": "label-position",
        "hidden": "true"
      },
      parent: root,
      prefix
    });

    // Creating a <g> element in order to group all the paths below, that
    // together represent the measuring tool; so that would be easier move them
    // around
    let g = createSVGNode(window, {
      nodeType: "g",
      attributes: {
        id: "tool",
      },
      parent: svg,
      prefix
    });

    createSVGNode(window, {
      nodeType: "path",
      attributes: {
        id: "box-path"
      },
      parent: g,
      prefix
    });

    createSVGNode(window, {
      nodeType: "path",
      attributes: {
        id: "diagonal-path"
      },
      parent: g,
      prefix
    });

    for (let side of SIDES) {
      createSVGNode(window, {
        nodeType: "line",
        parent: svg,
        attributes: {
          "class": `guide-${side}`,
          id: `guide-${side}`,
          hidden: "true"
        },
        prefix
      });
    }

    return container;
  },

  _update() {
    let { window } = this.env;

    setIgnoreLayoutChanges(true);

    let zoom = getCurrentZoom(window);

    let { width, height } = getWindowDimensions(window);

    let { coords } = this;

    let isZoomChanged = zoom !== coords.zoom;

    if (isZoomChanged) {
      coords.zoom = zoom;
      this.updateLabel();
    }

    let isDocumentSizeChanged = width !== coords.documentWidth ||
                                height !== coords.documentHeight;

    if (isDocumentSizeChanged) {
      coords.documentWidth = width;
      coords.documentHeight = height;
    }

    // If either the document's size or the zoom is changed since the last
    // repaint, we update the tool's size as well.
    if (isZoomChanged || isDocumentSizeChanged) {
      this.updateViewport();
    }

    setIgnoreLayoutChanges(false, window.document.documentElement);

    this._rafID = window.requestAnimationFrame(() => this._update());
  },

  _cancelUpdate() {
    if (this._rafID) {
      this.env.window.cancelAnimationFrame(this._rafID);
      this._rafID = 0;
    }
  },

  destroy() {
    this.hide();

    this._cancelUpdate();

    let { pageListenerTarget } = this.env;

    if (pageListenerTarget) {
      pageListenerTarget.removeEventListener("mousedown", this);
      pageListenerTarget.removeEventListener("mousemove", this);
      pageListenerTarget.removeEventListener("mouseup", this);
      pageListenerTarget.removeEventListener("scroll", this);
      pageListenerTarget.removeEventListener("pagehide", this);
      pageListenerTarget.removeEventListener("mouseleave", this);
    }

    this.markup.destroy();

    EventEmitter.emit(this, "destroy");
  },

  show() {
    setIgnoreLayoutChanges(true);

    this.getElement("root").removeAttribute("hidden");

    this._update();

    setIgnoreLayoutChanges(false, this.env.window.document.documentElement);
  },

  hide() {
    setIgnoreLayoutChanges(true);

    this.hideLabel("size");
    this.hideLabel("position");

    this.getElement("root").setAttribute("hidden", "true");

    this._cancelUpdate();

    setIgnoreLayoutChanges(false, this.env.window.document.documentElement);
  },

  getElement(id) {
    return this.markup.getElement(this.ID_CLASS_PREFIX + id);
  },

  setSize(w, h) {
    this.setCoords(undefined, undefined, w, h);
  },

  setCoords(x, y, w, h) {
    let { coords } = this;

    if (typeof x !== "undefined") {
      coords.x = x;
    }

    if (typeof y !== "undefined") {
      coords.y = y;
    }

    if (typeof w !== "undefined") {
      coords.w = w;
    }

    if (typeof h !== "undefined") {
      coords.h = h;
    }

    setIgnoreLayoutChanges(true);

    if (this._isDragging) {
      this.updatePaths();
    }

    this.updateLabel();

    setIgnoreLayoutChanges(false, this.env.window.document.documentElement);
  },

  updatePaths() {
    let { x, y, w, h } = this.coords;
    let dir = `M0 0 L${w} 0 L${w} ${h} L0 ${h}z`;

    // Adding correction to the line path, otherwise some pixels are drawn
    // outside the main rectangle area.
    let x1 = w > 0 ? 0.5 : 0;
    let y1 = w < 0 && h < 0 ? -0.5 : 0;
    let w1 = w + (h < 0 && w < 0 ? 0.5 : 0);
    let h1 = h + (h > 0 && w > 0 ? -0.5 : 0);

    let linedir = `M${x1} ${y1} L${w1} ${h1}`;

    this.getElement("box-path").setAttribute("d", dir);
    this.getElement("diagonal-path").setAttribute("d", linedir);
    this.getElement("tool").setAttribute("transform", `translate(${x},${y})`);
  },

  updateLabel(type) {
    type = type || this._isDragging ? "size" : "position";

    let isSizeLabel = type === "size";

    let label = this.getElement(`label-${type}`);

    let origin = "top left";

    let { innerWidth, innerHeight, scrollX, scrollY } = this.env.window;
    let { x, y, w, h, zoom } = this.coords;
    let scale = 1 / zoom;

    w = w || 0;
    h = h || 0;
    x = (x || 0) + w;
    y = (y || 0) + h;

    let labelMargin, labelHeight, labelWidth;

    if (isSizeLabel) {
      labelMargin = LABEL_SIZE_MARGIN;
      labelWidth = LABEL_SIZE_WIDTH;
      labelHeight = LABEL_SIZE_HEIGHT;

      let d = Math.hypot(w, h).toFixed(2);

      label.setTextContent(`W: ${Math.abs(w)} px
                            H: ${Math.abs(h)} px
                            ↘: ${d}px`);
    } else {
      labelMargin = LABEL_POS_MARGIN;
      labelWidth = LABEL_POS_WIDTH;
      labelHeight = LABEL_POS_HEIGHT;

      label.setTextContent(`${x}
                            ${y}`);
    }

    // Size used to position properly the label
    let labelBoxWidth = (labelWidth + labelMargin) * scale;
    let labelBoxHeight = (labelHeight + labelMargin) * scale;

    let isGoingLeft = w < scrollX;
    let isSizeGoingLeft = isSizeLabel && isGoingLeft;
    let isExceedingLeftMargin = x - labelBoxWidth < scrollX;
    let isExceedingRightMargin = x + labelBoxWidth > innerWidth + scrollX;
    let isExceedingTopMargin = y - labelBoxHeight < scrollY;
    let isExceedingBottomMargin = y + labelBoxHeight > innerHeight + scrollY;

    if ((isSizeGoingLeft && !isExceedingLeftMargin) || isExceedingRightMargin) {
      x -= labelBoxWidth;
      origin = "top right";
    } else {
      x += labelMargin * scale;
    }

    if (isSizeLabel) {
      y += isExceedingTopMargin ? labelMargin * scale : -labelBoxHeight;
    } else {
      y += isExceedingBottomMargin ? -labelBoxHeight : labelMargin * scale;
    }

    label.setAttribute("style", `
      width: ${labelWidth}px;
      height: ${labelHeight}px;
      transform-origin: ${origin};
      transform: translate(${x}px,${y}px) scale(${scale})
    `);

    if (!isSizeLabel) {
      let labelSize = this.getElement("label-size");
      let style = labelSize.getAttribute("style");

      if (style) {
        labelSize.setAttribute("style",
          style.replace(/scale[^)]+\)/, `scale(${scale})`));
      }
    }
  },

  updateViewport() {
    let { devicePixelRatio } = this.env.window;
    let { documentWidth, documentHeight, zoom } = this.coords;

    // Because `devicePixelRatio` is affected by zoom (see bug 809788),
    // in order to get the "real" device pixel ratio, we need divide by `zoom`
    let pixelRatio = devicePixelRatio / zoom;

    // The "real" device pixel ratio is used to calculate the max stroke
    // width we can actually assign: on retina, for instance, it would be 0.5,
    // where on non high dpi monitor would be 1.
    let minWidth = 1 / pixelRatio;
    let strokeWidth = minWidth / zoom;

    this.getElement("root").setAttribute("style",
      `stroke-width:${strokeWidth};
       width:${documentWidth}px;
       height:${documentHeight}px;`);
  },

  updateGuides() {
    let { x, y, w, h } = this.coords;

    let guide = this.getElement("guide-top");

    guide.setAttribute("x1", "0");
    guide.setAttribute("y1", y);
    guide.setAttribute("x2", "100%");
    guide.setAttribute("y2", y);

    guide = this.getElement("guide-right");

    guide.setAttribute("x1", x + w);
    guide.setAttribute("y1", 0);
    guide.setAttribute("x2", x + w);
    guide.setAttribute("y2", "100%");

    guide = this.getElement("guide-bottom");

    guide.setAttribute("x1", "0");
    guide.setAttribute("y1", y + h);
    guide.setAttribute("x2", "100%");
    guide.setAttribute("y2", y + h);

    guide = this.getElement("guide-left");

    guide.setAttribute("x1", x);
    guide.setAttribute("y1", 0);
    guide.setAttribute("x2", x);
    guide.setAttribute("y2", "100%");
  },

  showLabel(type) {
    setIgnoreLayoutChanges(true);

    this.getElement(`label-${type}`).removeAttribute("hidden");

    setIgnoreLayoutChanges(false, this.env.window.document.documentElement);
  },

  hideLabel(type) {
    setIgnoreLayoutChanges(true);

    this.getElement(`label-${type}`).setAttribute("hidden", "true");

    setIgnoreLayoutChanges(false, this.env.window.document.documentElement);
  },

  showGuides() {
    let prefix = this.ID_CLASS_PREFIX + "guide-";

    for (let side of SIDES) {
      this.markup.removeAttributeForElement(`${prefix + side}`, "hidden");
    }
  },

  hideGuides() {
    let prefix = this.ID_CLASS_PREFIX + "guide-";

    for (let side of SIDES) {
      this.markup.setAttributeForElement(`${prefix + side}`, "hidden", "true");
    }
  },

  handleEvent(event) {
    let scrollX, scrollY, innerWidth, innerHeight;
    let x, y;

    let { pageListenerTarget } = this.env;

    switch (event.type) {
      case "mousedown":
        if (event.button) {
          return;
        }

        this._isDragging = true;

        let { window } = this.env;

        ({ scrollX, scrollY } = window);
        x = event.clientX + scrollX;
        y = event.clientY + scrollY;

        pageListenerTarget.addEventListener("mouseup", this);

        setIgnoreLayoutChanges(true);

        this.getElement("tool").setAttribute("class", "dragging");

        this.hideLabel("size");
        this.hideLabel("position");

        this.hideGuides();
        this.setCoords(x, y, 0, 0);

        setIgnoreLayoutChanges(false, window.document.documentElement);

        break;
      case "mouseup":
        this._isDragging = false;

        pageListenerTarget.removeEventListener("mouseup", this);

        setIgnoreLayoutChanges(true);

        this.getElement("tool").removeAttribute("class", "");

        // Shows the guides only if an actual area is selected
        if (this.coords.w !== 0 && this.coords.h !== 0) {
          this.updateGuides();
          this.showGuides();
        }

        setIgnoreLayoutChanges(false, this.env.window.document.documentElement);

        break;
      case "mousemove":
        ({ scrollX, scrollY, innerWidth, innerHeight } = this.env.window);
        x = event.clientX + scrollX;
        y = event.clientY + scrollY;

        let { coords } = this;

        x = Math.min(innerWidth + scrollX, Math.max(scrollX, x));
        y = Math.min(innerHeight + scrollY, Math.max(scrollY, y));

        this.setSize(x - coords.x, y - coords.y);

        let type = this._isDragging ? "size" : "position";

        this.showLabel(type);
        break;
      case "mouseleave":
        if (!this._isDragging) {
          this.hideLabel("position");
        }
        break;
      case "scroll":
        this.hideLabel("position");
        break;
      case "pagehide":
        // If a page hide event is triggered for current window's highlighter, hide the
        // highlighter.
        if (event.target.defaultView === this.env.window) {
          this.destroy();
        }
        break;
    }
  }
};
exports.MeasuringToolHighlighter = MeasuringToolHighlighter;
PK
!<�-��AANchrome/devtools/modules/devtools/server/actors/highlighters/paused-debugger.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { CanvasFrameAnonymousContentHelper, createNode } = require("./utils/markup");

/**
 * The PausedDebuggerOverlay is a class that displays a semi-transparent mask on top of
 * the whole page and a toolbar at the top of the page.
 * This is used to signal to users that script execution is current paused.
 * The toolbar is used to display the reason for the pause in script execution as well as
 * buttons to resume or step through the program.
 */
function PausedDebuggerOverlay(highlighterEnv) {
  this.env = highlighterEnv;
  this.markup = new CanvasFrameAnonymousContentHelper(highlighterEnv,
    this._buildMarkup.bind(this));
}

PausedDebuggerOverlay.prototype = {
  typeName: "PausedDebuggerOverlay",

  ID_CLASS_PREFIX: "paused-dbg-",

  _buildMarkup() {
    let { window } = this.env;
    let prefix = this.ID_CLASS_PREFIX;

    let container = createNode(window, {
      attributes: {"class": "highlighter-container"}
    });

    // Wrapper element.
    let wrapper = createNode(window, {
      parent: container,
      attributes: {
        "id": "root",
        "class": "root",
        "hidden": "true",
        "overlay": "true"
      },
      prefix
    });

    let toolbar = createNode(window, {
      parent: wrapper,
      attributes: {
        "id": "toolbar",
        "class": "toolbar"
      },
      prefix
    });

    createNode(window, {
      nodeType: "span",
      parent: toolbar,
      attributes: {
        "id": "reason",
        "class": "reason"
      },
      prefix
    });

    return container;
  },

  destroy() {
    this.hide();
    this.markup.destroy();
    this.env = null;
  },

  getElement(id) {
    return this.markup.getElement(this.ID_CLASS_PREFIX + id);
  },

  show(node, options = {}) {
    if (this.env.isXUL) {
      return false;
    }

    // Show the highlighter's root element.
    let root = this.getElement("root");
    root.removeAttribute("hidden");

    // The page overlay is only shown upon request. Sometimes we just want the toolbar.
    if (options.onlyToolbar) {
      root.removeAttribute("overlay");
    } else {
      root.setAttribute("overlay", "true");
    }

    // Set the text to appear in the toolbar.
    let toolbar = this.getElement("toolbar");
    if (options.reason) {
      this.getElement("reason").setTextContent(options.reason);
      toolbar.removeAttribute("hidden");
    } else {
      toolbar.setAttribute("hidden", "true");
    }

    return true;
  },

  hide() {
    if (this.env.isXUL) {
      return;
    }

    // Hide the overlay.
    this.getElement("root").setAttribute("hidden", "true");
  }
};
exports.PausedDebuggerOverlay = PausedDebuggerOverlay;
PK
!<a�=p�!�!Echrome/devtools/modules/devtools/server/actors/highlighters/rulers.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/shared/event-emitter");
const { getCurrentZoom,
  setIgnoreLayoutChanges } = require("devtools/shared/layout/utils");
const {
  CanvasFrameAnonymousContentHelper,
  createSVGNode, createNode } = require("./utils/markup");

// Maximum size, in pixel, for the horizontal ruler and vertical ruler
// used by RulersHighlighter
const RULERS_MAX_X_AXIS = 10000;
const RULERS_MAX_Y_AXIS = 15000;
// Number of steps after we add a graduation, marker and text in
// RulersHighliter; currently the unit is in pixel.
const RULERS_GRADUATION_STEP = 5;
const RULERS_MARKER_STEP = 50;
const RULERS_TEXT_STEP = 100;

/**
 * The RulersHighlighter is a class that displays both horizontal and
 * vertical rules on the page, along the top and left edges, with pixel
 * graduations, useful for users to quickly check distances
 */
function RulersHighlighter(highlighterEnv) {
  this.env = highlighterEnv;
  this.markup = new CanvasFrameAnonymousContentHelper(highlighterEnv,
    this._buildMarkup.bind(this));

  let { pageListenerTarget } = highlighterEnv;
  pageListenerTarget.addEventListener("scroll", this);
  pageListenerTarget.addEventListener("pagehide", this);
}

RulersHighlighter.prototype = {
  typeName: "RulersHighlighter",

  ID_CLASS_PREFIX: "rulers-highlighter-",

  _buildMarkup: function () {
    let { window } = this.env;
    let prefix = this.ID_CLASS_PREFIX;

    function createRuler(axis, size) {
      let width, height;
      let isHorizontal = true;

      if (axis === "x") {
        width = size;
        height = 16;
      } else if (axis === "y") {
        width = 16;
        height = size;
        isHorizontal = false;
      } else {
        throw new Error(
          `Invalid type of axis given; expected "x" or "y" but got "${axis}"`);
      }

      let g = createSVGNode(window, {
        nodeType: "g",
        attributes: {
          id: `${axis}-axis`
        },
        parent: svg,
        prefix
      });

      createSVGNode(window, {
        nodeType: "rect",
        attributes: {
          y: isHorizontal ? 0 : 16,
          width,
          height
        },
        parent: g
      });

      let gRule = createSVGNode(window, {
        nodeType: "g",
        attributes: {
          id: `${axis}-axis-ruler`
        },
        parent: g,
        prefix
      });

      let pathGraduations = createSVGNode(window, {
        nodeType: "path",
        attributes: {
          "class": "ruler-graduations",
          width,
          height
        },
        parent: gRule,
        prefix
      });

      let pathMarkers = createSVGNode(window, {
        nodeType: "path",
        attributes: {
          "class": "ruler-markers",
          width,
          height
        },
        parent: gRule,
        prefix
      });

      let gText = createSVGNode(window, {
        nodeType: "g",
        attributes: {
          id: `${axis}-axis-text`,
          "class": (isHorizontal ? "horizontal" : "vertical") + "-labels"
        },
        parent: g,
        prefix
      });

      let dGraduations = "";
      let dMarkers = "";
      let graduationLength;

      for (let i = 0; i < size; i += RULERS_GRADUATION_STEP) {
        if (i === 0) {
          continue;
        }

        graduationLength = (i % 2 === 0) ? 6 : 4;

        if (i % RULERS_TEXT_STEP === 0) {
          graduationLength = 8;
          createSVGNode(window, {
            nodeType: "text",
            parent: gText,
            attributes: {
              x: isHorizontal ? 2 + i : -i - 1,
              y: 5
            }
          }).textContent = i;
        }

        if (isHorizontal) {
          if (i % RULERS_MARKER_STEP === 0) {
            dMarkers += `M${i} 0 L${i} ${graduationLength}`;
          } else {
            dGraduations += `M${i} 0 L${i} ${graduationLength} `;
          }
        } else if (i % 50 === 0) {
          dMarkers += `M0 ${i} L${graduationLength} ${i}`;
        } else {
          dGraduations += `M0 ${i} L${graduationLength} ${i}`;
        }
      }

      pathGraduations.setAttribute("d", dGraduations);
      pathMarkers.setAttribute("d", dMarkers);

      return g;
    }

    let container = createNode(window, {
      attributes: {"class": "highlighter-container"}
    });

    let root = createNode(window, {
      parent: container,
      attributes: {
        "id": "root",
        "class": "root"
      },
      prefix
    });

    let svg = createSVGNode(window, {
      nodeType: "svg",
      parent: root,
      attributes: {
        id: "elements",
        "class": "elements",
        width: "100%",
        height: "100%",
        hidden: "true"
      },
      prefix
    });

    createRuler("x", RULERS_MAX_X_AXIS);
    createRuler("y", RULERS_MAX_Y_AXIS);

    createNode(window, {
      parent: container,
      attributes: {
        "class": "viewport-infobar-container",
        "id": "viewport-infobar-container",
        "position": "top"
      },
      prefix
    });

    return container;
  },

  handleEvent: function (event) {
    switch (event.type) {
      case "scroll":
        this._onScroll(event);
        break;
      case "pagehide":
        // If a page hide event is triggered for current window's highlighter, hide the
        // highlighter.
        if (event.target.defaultView === this.env.window) {
          this.destroy();
        }
        break;
    }
  },

  _onScroll: function (event) {
    let prefix = this.ID_CLASS_PREFIX;
    let { scrollX, scrollY } = event.view;

    this.markup.getElement(`${prefix}x-axis-ruler`)
                        .setAttribute("transform", `translate(${-scrollX})`);
    this.markup.getElement(`${prefix}x-axis-text`)
                        .setAttribute("transform", `translate(${-scrollX})`);
    this.markup.getElement(`${prefix}y-axis-ruler`)
                        .setAttribute("transform", `translate(0, ${-scrollY})`);
    this.markup.getElement(`${prefix}y-axis-text`)
                        .setAttribute("transform", `translate(0, ${-scrollY})`);
  },

  _update: function () {
    let { window } = this.env;

    setIgnoreLayoutChanges(true);

    let zoom = getCurrentZoom(window);
    let isZoomChanged = zoom !== this._zoom;

    if (isZoomChanged) {
      this._zoom = zoom;
      this.updateViewport();
    }

    this.updateViewportInfobar();

    setIgnoreLayoutChanges(false, window.document.documentElement);

    this._rafID = window.requestAnimationFrame(() => this._update());
  },

  _cancelUpdate: function () {
    if (this._rafID) {
      this.env.window.cancelAnimationFrame(this._rafID);
      this._rafID = 0;
    }
  },
  updateViewport: function () {
    let { devicePixelRatio } = this.env.window;

    // Because `devicePixelRatio` is affected by zoom (see bug 809788),
    // in order to get the "real" device pixel ratio, we need divide by `zoom`
    let pixelRatio = devicePixelRatio / this._zoom;

    // The "real" device pixel ratio is used to calculate the max stroke
    // width we can actually assign: on retina, for instance, it would be 0.5,
    // where on non high dpi monitor would be 1.
    let minWidth = 1 / pixelRatio;
    let strokeWidth = Math.min(minWidth, minWidth / this._zoom);

    this.markup.getElement(this.ID_CLASS_PREFIX + "root").setAttribute("style",
      `stroke-width:${strokeWidth};`);
  },

  updateViewportInfobar: function () {
    let { window } = this.env;
    let { innerHeight, innerWidth } = window;
    let infobarId = this.ID_CLASS_PREFIX + "viewport-infobar-container";
    let textContent = innerHeight + "px \u00D7 " + innerWidth + "px";
    this.markup.getElement(infobarId).setTextContent(textContent);
  },

  destroy: function () {
    this.hide();

    let { pageListenerTarget } = this.env;

    if (pageListenerTarget) {
      pageListenerTarget.removeEventListener("scroll", this);
      pageListenerTarget.removeEventListener("pagehide", this);
    }

    this.markup.destroy();

    EventEmitter.emit(this, "destroy");
  },

  show: function () {
    this.markup.removeAttributeForElement(this.ID_CLASS_PREFIX + "elements",
      "hidden");

    this._update();

    return true;
  },

  hide: function () {
    this.markup.setAttributeForElement(this.ID_CLASS_PREFIX + "elements",
      "hidden", "true");

    this._cancelUpdate();
  }
};
exports.RulersHighlighter = RulersHighlighter;
PK
!<�[A�{	{	Gchrome/devtools/modules/devtools/server/actors/highlighters/selector.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { isNodeValid } = require("./utils/markup");
const { BoxModelHighlighter } = require("./box-model");

// How many maximum nodes can be highlighted at the same time by the
// SelectorHighlighter
const MAX_HIGHLIGHTED_ELEMENTS = 100;

/**
 * The SelectorHighlighter runs a given selector through querySelectorAll on the
 * document of the provided context node and then uses the BoxModelHighlighter
 * to highlight the matching nodes
 */
function SelectorHighlighter(highlighterEnv) {
  this.highlighterEnv = highlighterEnv;
  this._highlighters = [];
}

SelectorHighlighter.prototype = {
  typeName: "SelectorHighlighter",

  /**
   * Show BoxModelHighlighter on each node that matches that provided selector.
   * @param {DOMNode} node A context node that is used to get the document on
   * which querySelectorAll should be executed. This node will NOT be
   * highlighted.
   * @param {Object} options Should at least contain the 'selector' option, a
   * string that will be used in querySelectorAll. On top of this, all of the
   * valid options to BoxModelHighlighter.show are also valid here.
   */
  show: function (node, options = {}) {
    this.hide();

    if (!isNodeValid(node) || !options.selector) {
      return false;
    }

    let nodes = [];
    try {
      nodes = [...node.ownerDocument.querySelectorAll(options.selector)];
    } catch (e) {
      // It's fine if the provided selector is invalid, nodes will be an empty
      // array.
    }

    delete options.selector;

    let i = 0;
    for (let matchingNode of nodes) {
      if (i >= MAX_HIGHLIGHTED_ELEMENTS) {
        break;
      }

      let highlighter = new BoxModelHighlighter(this.highlighterEnv);
      if (options.fill) {
        highlighter.regionFill[options.region || "border"] = options.fill;
      }
      highlighter.show(matchingNode, options);
      this._highlighters.push(highlighter);
      i++;
    }

    return true;
  },

  hide: function () {
    for (let highlighter of this._highlighters) {
      highlighter.destroy();
    }
    this._highlighters = [];
  },

  destroy: function () {
    this.hide();
    this.highlighterEnv = null;
  }
};
exports.SelectorHighlighter = SelectorHighlighter;
PK
!<Q~������Echrome/devtools/modules/devtools/server/actors/highlighters/shapes.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { CanvasFrameAnonymousContentHelper,
        createSVGNode, createNode, getComputedStyle } = require("./utils/markup");
const { setIgnoreLayoutChanges, getCurrentZoom,
        getAdjustedQuads, getFrameOffsets } = require("devtools/shared/layout/utils");
const { AutoRefreshHighlighter } = require("./auto-refresh");
const {
  getDistance,
  clickedOnEllipseEdge,
  distanceToLine,
  projection,
  clickedOnPoint
} = require("devtools/server/actors/utils/shapes-utils");
const {
  identity,
  apply,
  translate,
  multiply,
  scale,
  rotate,
  changeMatrixBase,
  getBasis
} = require("devtools/shared/layout/dom-matrix-2d");
const EventEmitter = require("devtools/shared/event-emitter");
const { getCSSStyleRules } = require("devtools/shared/inspector/css-logic");

const BASE_MARKER_SIZE = 5;
// the width of the area around highlighter lines that can be clicked, in px
const LINE_CLICK_WIDTH = 5;
const ROTATE_LINE_LENGTH = 50;
const DOM_EVENTS = ["mousedown", "mousemove", "mouseup", "dblclick"];
const _dragging = Symbol("shapes/dragging");

/**
 * The ShapesHighlighter draws an outline shapes in the page.
 * The idea is to have something that is able to wrap complex shapes for css properties
 * such as shape-outside/inside, clip-path but also SVG elements.
 *
 * Notes on shape transformation:
 *
 * When using transform mode to translate, scale, and rotate shapes, a transformation
 * matrix keeps track of the transformations done to the original shape. When the
 * highlighter is toggled on/off or between transform mode and point editing mode,
 * the transformations applied to the shape become permanent.
 *
 * While transformations are being performed on a shape, there is an "original" and
 * a "transformed" coordinate system. This is used when scaling or rotating a rotated
 * shape.
 *
 * The "original" coordinate system is the one where (0,0) is at the top left corner
 * of the page, the x axis is horizontal, and the y axis is vertical.
 *
 * The "transformed" coordinate system is the one where (0,0) is at the top left
 * corner of the current shape. The x axis follows the north edge of the shape
 * (from the northwest corner to the northeast corner) and the y axis follows
 * the west edge of the shape (from the northwest corner to the southwest corner).
 *
 * Because of rotation, the "north" and "west" edges might not actually be at the
 * top and left of the transformed shape. Imagine that the compass directions are
 * also rotated along with the shape.
 *
 * A refresher for coordinates and change of basis that may be helpful:
 * https://www.math.ubc.ca/~behrend/math221/Coords.pdf
 */
class ShapesHighlighter extends AutoRefreshHighlighter {
  constructor(highlighterEnv) {
    super(highlighterEnv);
    EventEmitter.decorate(this);

    this.ID_CLASS_PREFIX = "shapes-";

    this.referenceBox = "border";
    this.useStrokeBox = false;
    this.geometryBox = "";
    this.hoveredPoint = null;
    this.fillRule = "";
    this.numInsetPoints = 0;
    this.transformMode = false;

    this.markup = new CanvasFrameAnonymousContentHelper(this.highlighterEnv,
      this._buildMarkup.bind(this));
    this.onPageHide = this.onPageHide.bind(this);

    let { pageListenerTarget } = this.highlighterEnv;
    DOM_EVENTS.forEach(event => pageListenerTarget.addEventListener(event, this));
    pageListenerTarget.addEventListener("pagehide", this.onPageHide);
  }

  _buildMarkup() {
    let container = createNode(this.win, {
      attributes: {
        "class": "highlighter-container"
      }
    });

    // The root wrapper is used to unzoom the highlighter when needed.
    let rootWrapper = createNode(this.win, {
      parent: container,
      attributes: {
        "id": "root",
        "class": "root"
      },
      prefix: this.ID_CLASS_PREFIX
    });

    let mainSvg = createSVGNode(this.win, {
      nodeType: "svg",
      parent: rootWrapper,
      attributes: {
        "id": "shape-container",
        "class": "shape-container",
        "viewBox": "0 0 100 100",
        "preserveAspectRatio": "none"
      },
      prefix: this.ID_CLASS_PREFIX
    });

    // This clipPath and its children make sure the element quad outline
    // is only shown when the shape extends past the element quads.
    let clipSvg = createSVGNode(this.win, {
      nodeType: "clipPath",
      parent: mainSvg,
      attributes: {
        "id": "clip-path",
        "class": "clip-path",
      },
      prefix: this.ID_CLASS_PREFIX
    });

    createSVGNode(this.win, {
      nodeType: "polygon",
      parent: clipSvg,
      attributes: {
        "id": "clip-polygon",
        "class": "clip-polygon",
        "hidden": "true"
      },
      prefix: this.ID_CLASS_PREFIX
    });

    createSVGNode(this.win, {
      nodeType: "ellipse",
      parent: clipSvg,
      attributes: {
        "id": "clip-ellipse",
        "class": "clip-ellipse",
        "hidden": true
      },
      prefix: this.ID_CLASS_PREFIX
    });

    createSVGNode(this.win, {
      nodeType: "rect",
      parent: clipSvg,
      attributes: {
        "id": "clip-rect",
        "class": "clip-rect",
        "hidden": true
      },
      prefix: this.ID_CLASS_PREFIX
    });

    // Rectangle that displays the element quads. Only shown for shape-outside.
    // Only the parts of the rectangle's outline that overlap with the shape is shown.
    createSVGNode(this.win, {
      nodeType: "rect",
      parent: mainSvg,
      attributes: {
        "id": "quad",
        "class": "quad",
        "hidden": "true",
        "clip-path": "url(#shapes-clip-path)",
        "x": 0,
        "y": 0,
        "width": 100,
        "height": 100
      },
      prefix: this.ID_CLASS_PREFIX
    });

    // clipPath that corresponds to the element's quads. Only applied for shape-outside.
    // This ensures only the parts of the shape that are within the element's quads are
    // outlined by a solid line.
    let shapeClipSvg = createSVGNode(this.win, {
      nodeType: "clipPath",
      parent: mainSvg,
      attributes: {
        "id": "quad-clip-path",
        "class": "quad-clip-path",
      },
      prefix: this.ID_CLASS_PREFIX
    });

    createSVGNode(this.win, {
      nodeType: "rect",
      parent: shapeClipSvg,
      attributes: {
        "id": "quad-clip",
        "class": "quad-clip",
        "x": -1,
        "y": -1,
        "width": 102,
        "height": 102
      },
      prefix: this.ID_CLASS_PREFIX
    });

    let mainGroup = createSVGNode(this.win, {
      nodeType: "g",
      parent: mainSvg,
      attributes: {
        "id": "group",
      },
      prefix: this.ID_CLASS_PREFIX
    });

    // Append a polygon for polygon shapes.
    createSVGNode(this.win, {
      nodeType: "polygon",
      parent: mainGroup,
      attributes: {
        "id": "polygon",
        "class": "polygon",
        "hidden": "true"
      },
      prefix: this.ID_CLASS_PREFIX
    });

    // Append an ellipse for circle/ellipse shapes.
    createSVGNode(this.win, {
      nodeType: "ellipse",
      parent: mainGroup,
      attributes: {
        "id": "ellipse",
        "class": "ellipse",
        "hidden": true
      },
      prefix: this.ID_CLASS_PREFIX
    });

    // Append a rect for inset().
    createSVGNode(this.win, {
      nodeType: "rect",
      parent: mainGroup,
      attributes: {
        "id": "rect",
        "class": "rect",
        "hidden": true
      },
      prefix: this.ID_CLASS_PREFIX
    });

    // Dashed versions of each shape. Only shown for the parts of the shape
    // that extends past the element's quads.
    createSVGNode(this.win, {
      nodeType: "polygon",
      parent: mainGroup,
      attributes: {
        "id": "dashed-polygon",
        "class": "polygon",
        "hidden": "true",
        "stroke-dasharray": "5, 5",
      },
      prefix: this.ID_CLASS_PREFIX
    });

    createSVGNode(this.win, {
      nodeType: "ellipse",
      parent: mainGroup,
      attributes: {
        "id": "dashed-ellipse",
        "class": "ellipse",
        "hidden": "true",
        "stroke-dasharray": "5, 5",
      },
      prefix: this.ID_CLASS_PREFIX
    });

    createSVGNode(this.win, {
      nodeType: "rect",
      parent: mainGroup,
      attributes: {
        "id": "dashed-rect",
        "class": "rect",
        "hidden": "true",
        "stroke-dasharray": "5, 5",
      },
      prefix: this.ID_CLASS_PREFIX
    });

    createSVGNode(this.win, {
      nodeType: "path",
      parent: mainGroup,
      attributes: {
        "id": "bounding-box",
        "class": "bounding-box",
        "stroke-dasharray": "5, 5",
        "hidden": true
      },
      prefix: this.ID_CLASS_PREFIX
    });

    createSVGNode(this.win, {
      nodeType: "path",
      parent: mainGroup,
      attributes: {
        "id": "rotate-line",
        "class": "rotate-line",
      },
      prefix: this.ID_CLASS_PREFIX
    });

    // Append a path to display the markers for the shape.
    createSVGNode(this.win, {
      nodeType: "path",
      parent: mainGroup,
      attributes: {
        "id": "markers-outline",
        "class": "markers-outline",
      },
      prefix: this.ID_CLASS_PREFIX
    });

    createSVGNode(this.win, {
      nodeType: "path",
      parent: mainGroup,
      attributes: {
        "id": "markers",
        "class": "markers",
      },
      prefix: this.ID_CLASS_PREFIX
    });

    createSVGNode(this.win, {
      nodeType: "path",
      parent: mainGroup,
      attributes: {
        "id": "marker-hover",
        "class": "marker-hover",
        "hidden": true
      },
      prefix: this.ID_CLASS_PREFIX
    });

    return container;
  }

  get currentDimensions() {
    let dims = this.currentQuads[this.referenceBox][0].bounds;
    let zoom = getCurrentZoom(this.win);

    // If an SVG element has a stroke, currentQuads will return the stroke bounding box.
    // However, clip-path always uses the object bounding box unless "stroke-box" is
    // specified. So, we must calculate the object bounding box if there is a stroke
    // and "stroke-box" is not specified. stroke only applies to SVG elements, so use
    // getBBox, which only exists for SVG, to check if currentNode is an SVG element.
    if (this.currentNode.getBBox &&
        getComputedStyle(this.currentNode).stroke !== "none" && !this.useStrokeBox) {
      dims = getObjectBoundingBox(dims.top, dims.left,
        dims.width, dims.height, this.currentNode);
    }

    return {
      top: dims.top / zoom,
      left: dims.left / zoom,
      width: dims.width / zoom,
      height: dims.height / zoom
    };
  }

  get frameDimensions() {
    // In an iframe, we get the node's quads relative to the frame, instead of the parent
    // document.
    let dims = this.highlighterEnv.window.document === this.currentNode.ownerDocument ?
               this.currentQuads[this.referenceBox][0].bounds :
               getAdjustedQuads(this.currentNode.ownerGlobal,
                                this.currentNode, this.referenceBox)[0].bounds;
    let zoom = getCurrentZoom(this.win);

    // If an SVG element has a stroke, currentQuads will return the stroke bounding box.
    // However, clip-path always uses the object bounding box unless "stroke-box" is
    // specified. So, we must calculate the object bounding box if there is a stroke
    // and "stroke-box" is not specified. stroke only applies to SVG elements, so use
    // getBBox, which only exists for SVG, to check if currentNode is an SVG element.
    if (this.currentNode.getBBox &&
        getComputedStyle(this.currentNode).stroke !== "none" && !this.useStrokeBox) {
      dims = getObjectBoundingBox(dims.top, dims.left,
        dims.width, dims.height, this.currentNode);
    }

    return {
      top: dims.top / zoom,
      left: dims.left / zoom,
      width: dims.width / zoom,
      height: dims.height / zoom
    };
  }

  /**
   * Changes the appearance of the mouse cursor on the highlighter.
   *
   * Because we can't attach event handlers to individual elements in the
   * highlighter, we determine if the mouse is hovering over a point by seeing if
   * it's within 5 pixels of it. This creates a square hitbox that doesn't match
   * perfectly with the circular markers. So if we were to use the :hover
   * pseudo-class to apply changes to the mouse cursor, the cursor change would not
   * always accurately reflect whether you can interact with the point. This is
   * also the reason we have the hidden marker-hover element instead of using CSS
   * to fill in the marker.
   *
   * In addition, the cursor CSS property is applied to .shapes-root because if
   * it were attached to .shapes-marker, the cursor change no longer applies if
   * you are for example resizing the shape and your mouse goes off the point.
   * Also, if you are dragging a polygon point, the marker plays catch up to your
   * mouse position, resulting in an undesirable visual effect where the cursor
   * rapidly flickers between "grab" and "auto".
   *
   * @param {String} cursorType the name of the cursor to display
   */
  setCursor(cursorType) {
    let container = this.getElement("root");
    let style = container.getAttribute("style");
    // remove existing cursor definitions in the style
    style = style.replace(/cursor:.*?;/g, "");
    style = style.replace(/pointer-events:.*?;/g, "");
    let pointerEvents = cursorType === "auto" ? "none" : "auto";
    container.setAttribute("style",
      `${style}pointer-events:${pointerEvents};cursor:${cursorType};`);
  }

  handleEvent(event, id) {
    // No event handling if the highlighter is hidden
    if (this.areShapesHidden()) {
      return;
    }

    let { target, type, pageX, pageY } = event;

    // For events on highlighted nodes in an iframe, when the event takes place
    // outside the iframe. Check if event target belongs to the iframe. If it doesn't,
    // adjust pageX/pageY to be relative to the iframe rather than the parent.
    let nodeDocument = this.currentNode.ownerDocument;
    if (target !== nodeDocument && target.ownerDocument !== nodeDocument) {
      let [xOffset, yOffset] = getFrameOffsets(target.ownerGlobal, this.currentNode);
      let zoom = getCurrentZoom(this.win);
      // xOffset/yOffset are relative to the viewport, so first find the top/left
      // edges of the viewport relative to the page.
      let viewportLeft = pageX - event.clientX;
      let viewportTop = pageY - event.clientY;
      // Also adjust for scrolling in the iframe.
      let { scrollTop, scrollLeft } = nodeDocument.documentElement;
      pageX -= viewportLeft + xOffset / zoom - scrollLeft;
      pageY -= viewportTop + yOffset / zoom - scrollTop;
    }

    switch (type) {
      case "pagehide":
        // If a page hide event is triggered for current window's highlighter, hide the
        // highlighter.
        if (target.defaultView === this.win) {
          this.destroy();
        }

        break;
      case "mousedown":
        if (this.transformMode) {
          this._handleTransformClick(pageX, pageY);
        } else if (this.shapeType === "polygon") {
          this._handlePolygonClick(pageX, pageY);
        } else if (this.shapeType === "circle") {
          this._handleCircleClick(pageX, pageY);
        } else if (this.shapeType === "ellipse") {
          this._handleEllipseClick(pageX, pageY);
        } else if (this.shapeType === "inset") {
          this._handleInsetClick(pageX, pageY);
        }
        event.stopPropagation();
        event.preventDefault();
        break;
      case "mouseup":
        if (this[_dragging]) {
          this[_dragging] = null;
          this._handleMarkerHover(this.hoveredPoint);
        }
        break;
      case "mousemove":
        if (!this[_dragging]) {
          this._handleMouseMoveNotDragging(pageX, pageY);
          return;
        }
        event.stopPropagation();
        event.preventDefault();

        let { point } = this[_dragging];
        if (this.transformMode) {
          this._handleTransformMove(pageX, pageY);
        } else if (this.shapeType === "polygon") {
          this._handlePolygonMove(pageX, pageY);
        } else if (this.shapeType === "circle") {
          this._handleCircleMove(point, pageX, pageY);
        } else if (this.shapeType === "ellipse") {
          this._handleEllipseMove(point, pageX, pageY);
        } else if (this.shapeType === "inset") {
          this._handleInsetMove(point, pageX, pageY);
        }
        break;
      case "dblclick":
        if (this.shapeType === "polygon" && !this.transformMode) {
          let { percentX, percentY } = this.convertPageCoordsToPercent(pageX, pageY);
          let index = this.getPolygonPointAt(percentX, percentY);
          if (index === -1) {
            this.getPolygonClickedLine(percentX, percentY);
            return;
          }

          this._deletePolygonPoint(index);
        }
        break;
    }
  }

  /**
   * Handle a mouse click in transform mode.
   * @param {Number} pageX the x coordinate of the mouse
   * @param {Number} pageY the y coordinate of the mouse
   */
  _handleTransformClick(pageX, pageY) {
    let { percentX, percentY } = this.convertPageCoordsToPercent(pageX, pageY);
    let type = this.getTransformPointAt(percentX, percentY);
    if (!type) {
      return;
    }

    if (this.shapeType === "polygon") {
      this._handlePolygonTransformClick(pageX, pageY, type);
    } else if (this.shapeType === "circle") {
      this._handleCircleTransformClick(pageX, pageY, type);
    } else if (this.shapeType === "ellipse") {
      this._handleEllipseTransformClick(pageX, pageY, type);
    } else if (this.shapeType === "inset") {
      this._handleInsetTransformClick(pageX, pageY, type);
    }
  }

  /**
   * Handle a click in transform mode while highlighting a polygon.
   * @param {Number} pageX the x coordinate of the mouse.
   * @param {Number} pageY the y coordinate of the mouse.
   * @param {String} type the type of transform handle that was clicked.
   */
  _handlePolygonTransformClick(pageX, pageY, type) {
    let { width, height } = this.currentDimensions;
    let pointsInfo = this.origCoordUnits.map(([x, y], i) => {
      let xComputed = this.origCoordinates[i][0] / 100 * width;
      let yComputed = this.origCoordinates[i][1] / 100 * height;
      let unitX = getUnit(x);
      let unitY = getUnit(y);
      let valueX = (isUnitless(x)) ? xComputed : parseFloat(x);
      let valueY = (isUnitless(y)) ? yComputed : parseFloat(y);

      let ratioX = (valueX / xComputed) || 1;
      let ratioY = (valueY / yComputed) || 1;
      return { unitX, unitY, valueX, valueY, ratioX, ratioY };
    });
    this[_dragging] = { type, pointsInfo, x: pageX, y: pageY, bb: this.boundingBox,
                        matrix: this.transformMatrix,
                        transformedBB: this.transformedBoundingBox };
  }

  /**
   * Handle a click in transform mode while highlighting a circle.
   * @param {Number} pageX the x coordinate of the mouse.
   * @param {Number} pageY the y coordinate of the mouse.
   * @param {String} type the type of transform handle that was clicked.
   */
  _handleCircleTransformClick(pageX, pageY, type) {
    let { width, height } = this.currentDimensions;
    let { cx, cy } = this.origCoordUnits;
    let cxComputed = this.origCoordinates.cx / 100 * width;
    let cyComputed = this.origCoordinates.cy / 100 * height;
    let unitX = getUnit(cx);
    let unitY = getUnit(cy);
    let valueX = (isUnitless(cx)) ? cxComputed : parseFloat(cx);
    let valueY = (isUnitless(cy)) ? cyComputed : parseFloat(cy);

    let ratioX = (valueX / cxComputed) || 1;
    let ratioY = (valueY / cyComputed) || 1;

    let { radius } = this.origCoordinates;
    let computedSize = Math.sqrt((width ** 2) + (height ** 2)) / Math.sqrt(2);
    radius = radius / 100 * computedSize;
    let valueRad = this.origCoordUnits.radius;
    let unitRad = getUnit(valueRad);
    valueRad = (isUnitless(valueRad)) ? radius : parseFloat(valueRad);
    let ratioRad = (valueRad / radius) || 1;

    this[_dragging] = { type, unitX, unitY, unitRad, valueX, valueY,
                        ratioX, ratioY, ratioRad, x: pageX, y: pageY,
                        bb: this.boundingBox, matrix: this.transformMatrix,
                        transformedBB: this.transformedBoundingBox };
  }

  /**
   * Handle a click in transform mode while highlighting an ellipse.
   * @param {Number} pageX the x coordinate of the mouse.
   * @param {Number} pageY the y coordinate of the mouse.
   * @param {String} type the type of transform handle that was clicked.
   */
  _handleEllipseTransformClick(pageX, pageY, type) {
    let { width, height } = this.currentDimensions;
    let { cx, cy } = this.origCoordUnits;
    let cxComputed = this.origCoordinates.cx / 100 * width;
    let cyComputed = this.origCoordinates.cy / 100 * height;
    let unitX = getUnit(cx);
    let unitY = getUnit(cy);
    let valueX = (isUnitless(cx)) ? cxComputed : parseFloat(cx);
    let valueY = (isUnitless(cy)) ? cyComputed : parseFloat(cy);

    let ratioX = (valueX / cxComputed) || 1;
    let ratioY = (valueY / cyComputed) || 1;

    let { rx, ry } = this.origCoordinates;
    rx = rx / 100 * width;
    let valueRX = this.origCoordUnits.rx;
    let unitRX = getUnit(valueRX);
    valueRX = (isUnitless(valueRX)) ? rx : parseFloat(valueRX);
    let ratioRX = (valueRX / rx) || 1;
    ry = ry / 100 * height;
    let valueRY = this.origCoordUnits.ry;
    let unitRY = getUnit(valueRY);
    valueRY = (isUnitless(valueRY)) ? ry : parseFloat(valueRY);
    let ratioRY = (valueRY / ry) || 1;

    this[_dragging] = { type, unitX, unitY, unitRX, unitRY,
                        valueX, valueY, ratioX, ratioY, ratioRX, ratioRY,
                        x: pageX, y: pageY, bb: this.boundingBox,
                        matrix: this.transformMatrix,
                        transformedBB: this.transformedBoundingBox };
  }

  /**
   * Handle a click in transform mode while highlighting an inset.
   * @param {Number} pageX the x coordinate of the mouse.
   * @param {Number} pageY the y coordinate of the mouse.
   * @param {String} type the type of transform handle that was clicked.
   */
  _handleInsetTransformClick(pageX, pageY, type) {
    let { width, height } = this.currentDimensions;
    let pointsInfo = {};
    ["top", "right", "bottom", "left"].forEach(point => {
      let value = this.origCoordUnits[point];
      let size = (point === "left" || point === "right") ? width : height;
      let computedValue = this.origCoordinates[point] / 100 * size;
      let unit = getUnit(value);
      value = (isUnitless(value)) ? computedValue : parseFloat(value);
      let ratio = (value / computedValue) || 1;

      pointsInfo[point] = { value, unit, ratio };
    });
    this[_dragging] = { type, pointsInfo, x: pageX, y: pageY, bb: this.boundingBox,
                        matrix: this.transformMatrix,
                        transformedBB: this.transformedBoundingBox };
  }

  /**
   * Handle mouse movement after a click on a handle in transform mode.
   * @param {Number} pageX the x coordinate of the mouse
   * @param {Number} pageY the y coordinate of the mouse
   */
  _handleTransformMove(pageX, pageY) {
    let { type } = this[_dragging];
    if (type === "translate") {
      this._translateShape(pageX, pageY);
    } else if (type.includes("scale")) {
      this._scaleShape(pageX, pageY);
    } else if (type === "rotate" && this.shapeType === "polygon") {
      this._rotateShape(pageX, pageY);
    }

    this.transformedBoundingBox = this.calculateTransformedBoundingBox();
  }

  /**
   * Translates a shape based on the current mouse position.
   * @param {Number} pageX the x coordinate of the mouse.
   * @param {Number} pageY the y coordinate of the mouse.
   */
  _translateShape(pageX, pageY) {
    let { x, y, matrix } = this[_dragging];
    let deltaX = pageX - x;
    let deltaY = pageY - y;
    this.transformMatrix = multiply(translate(deltaX, deltaY), matrix);

    if (this.shapeType === "polygon") {
      this._transformPolygon();
    } else if (this.shapeType === "circle") {
      this._transformCircle();
    } else if (this.shapeType === "ellipse") {
      this._transformEllipse();
    } else if (this.shapeType === "inset") {
      this._transformInset();
    }
  }

  /**
   * Scales a shape according to the current mouse position.
   * @param {Number} pageX the x coordinate of the mouse.
   * @param {Number} pageY the y coordinate of the mouse.
   */
  _scaleShape(pageX, pageY) {
    /**
     * To scale a shape:
     * 1) Get the change of basis matrix corresponding to the current transformation
     *    matrix of the shape.
     * 2) Convert the mouse x/y deltas to the "transformed" coordinate system, using
     *    the change of base matrix.
     * 3) Calculate the proportion to which the shape should be scaled to, using the
     *    mouse x/y deltas and the width/height of the transformed shape.
     * 4) Translate the shape such that the anchor (the point opposite to the one
     *    being dragged) is at the top left of the element.
     * 5) Scale each point by multiplying by the scaling proportion.
     * 6) Translate the shape back such that the anchor is in its original position.
     */
    let { type, x, y, matrix } = this[_dragging];
    let { width, height } = this.currentDimensions;
    // The point opposite to the one being dragged
    let anchor = getAnchorPoint(type);

    let { ne, nw, sw } = this[_dragging].transformedBB;
    // u/v are the basis vectors of the transformed coordinate system.
    let u = [(ne[0] - nw[0]) / 100 * width, (ne[1] - nw[1]) / 100 * height];
    let v = [(sw[0] - nw[0]) / 100 * width, (sw[1] - nw[1]) / 100 * height];
    // uLength/vLength represent the width/height of the shape in the
    // transformed coordinate system.
    let { basis, invertedBasis, uLength, vLength } = getBasis(u, v);

    // How much points on each axis should be translated before scaling
    let transX = this[_dragging].transformedBB[anchor][0] / 100 * width;
    let transY = this[_dragging].transformedBB[anchor][1] / 100 * height;

    // Distance from original click to current mouse position
    let distanceX = pageX - x;
    let distanceY = pageY - y;
    // Convert from original coordinate system to transformed coordinate system
    let tDistanceX = invertedBasis[0] * distanceX + invertedBasis[1] * distanceY;
    let tDistanceY = invertedBasis[3] * distanceX + invertedBasis[4] * distanceY;

    // Proportion of distance to bounding box width/height of shape
    let proportionX = tDistanceX / uLength;
    let proportionY = tDistanceY / vLength;
    // proportionX is positive for size reductions dragging on w/nw/sw,
    // negative for e/ne/se.
    let scaleX = (type.includes("w")) ? 1 - proportionX : 1 + proportionX;
    // proportionT is positive for size reductions dragging on n/nw/ne,
    // negative for s/sw/se.
    let scaleY = (type.includes("n")) ? 1 - proportionY : 1 + proportionY;
    // Take the average of scaleX/scaleY for scaling on two axes
    let scaleXY = (scaleX + scaleY) / 2;

    let translateMatrix = translate(-transX, -transY);
    let scaleMatrix = identity();
    // The scale matrices are in the transformed coordinate system. We must convert
    // them to the original coordinate system before applying it to the transformation
    // matrix.
    if (type === "scale-e" || type === "scale-w") {
      scaleMatrix = changeMatrixBase(scale(scaleX, 1), invertedBasis, basis);
    } else if (type === "scale-n" || type === "scale-s") {
      scaleMatrix = changeMatrixBase(scale(1, scaleY), invertedBasis, basis);
    } else {
      scaleMatrix = changeMatrixBase(scale(scaleXY, scaleXY), invertedBasis, basis);
    }
    let translateBackMatrix = translate(transX, transY);
    this.transformMatrix = multiply(translateBackMatrix,
                              multiply(scaleMatrix,
                              multiply(translateMatrix, matrix)));

    if (this.shapeType === "polygon") {
      this._transformPolygon();
    } else if (this.shapeType === "circle") {
      this._transformCircle(transX);
    } else if (this.shapeType === "ellipse") {
      this._transformEllipse(transX, transY);
    } else if (this.shapeType === "inset") {
      this._transformInset();
    }
  }

  /**
   * Rotates a polygon based on the current mouse position.
   * @param {Number} pageX the x coordinate of the mouse.
   * @param {Number} pageY the y coordinate of the mouse.
   */
  _rotateShape(pageX, pageY) {
    let { matrix } = this[_dragging];
    let { center, ne, nw, sw } = this[_dragging].transformedBB;
    let { width, height } = this.currentDimensions;
    let centerX = center[0] / 100 * width;
    let centerY = center[1] / 100 * height;
    let { x: pageCenterX, y: pageCenterY } = this.convertPercentToPageCoords(...center);

    let dx = pageCenterX - pageX;
    let dy = pageCenterY - pageY;

    let u = [(ne[0] - nw[0]) / 100 * width, (ne[1] - nw[1]) / 100 * height];
    let v = [(sw[0] - nw[0]) / 100 * width, (sw[1] - nw[1]) / 100 * height];
    let { invertedBasis } = getBasis(u, v);

    let tdx = invertedBasis[0] * dx + invertedBasis[1] * dy;
    let tdy = invertedBasis[3] * dx + invertedBasis[4] * dy;
    let angle = Math.atan2(tdx, tdy);
    let translateMatrix = translate(-centerX, -centerY);
    let rotateMatrix = rotate(angle);
    let translateBackMatrix = translate(centerX, centerY);
    this.transformMatrix = multiply(translateBackMatrix,
                           multiply(rotateMatrix,
                           multiply(translateMatrix, matrix)));

    this._transformPolygon();
  }

  /**
   * Transform a polygon depending on the current transformation matrix.
   */
  _transformPolygon() {
    let { pointsInfo } = this[_dragging];

    let polygonDef = (this.fillRule) ? `${this.fillRule}, ` : "";
    polygonDef += pointsInfo.map(point => {
      let { unitX, unitY, valueX, valueY, ratioX, ratioY } = point;
      let vector = [valueX / ratioX, valueY / ratioY];
      let [newX, newY] = apply(this.transformMatrix, vector);
      let precisionX = getDecimalPrecision(unitX);
      let precisionY = getDecimalPrecision(unitY);
      newX = (newX * ratioX).toFixed(precisionX);
      newY = (newY * ratioY).toFixed(precisionY);

      return `${newX}${unitX} ${newY}${unitY}`;
    }).join(", ");
    polygonDef = (this.geometryBox) ? `polygon(${polygonDef}) ${this.geometryBox}` :
                                      `polygon(${polygonDef})`;

    this.currentNode.style.setProperty(this.property, polygonDef, "important");
  }

  /**
   * Transform a circle depending on the current transformation matrix.
   * @param {Number} transX the number of pixels the shape is translated on the x axis
   *                 before scaling
   */
  _transformCircle(transX = null) {
    let { unitX, unitY, unitRad, valueX, valueY,
          ratioX, ratioY, ratioRad } = this[_dragging];
    let { radius } = this.coordUnits;

    let [newCx, newCy] = apply(this.transformMatrix, [valueX / ratioX, valueY / ratioY]);
    if (transX !== null) {
      // As part of scaling, the shape is translated to be tangent to the line y=0.
      // To get the new radius, we translate the new cx back to that point and get
      // the distance to the line y=0.
      radius = `${Math.abs((newCx - transX) * ratioRad)}${unitRad}`;
    }

    let circleDef = (this.geometryBox) ?
      `circle(${radius} at ${newCx * ratioX}${unitX} ` +
        `${newCy * ratioY}${unitY} ${this.geometryBox}` :
      `circle(${radius} at ${newCx * ratioX}${unitX} ${newCy * ratioY}${unitY}`;
    this.currentNode.style.setProperty(this.property, circleDef, "important");
  }

  /**
   * Transform an ellipse depending on the current transformation matrix.
   * @param {Number} transX the number of pixels the shape is translated on the x axis
   *                 before scaling
   * @param {Number} transY the number of pixels the shape is translated on the y axis
   *                 before scaling
   */
  _transformEllipse(transX = null, transY = null) {
    let { unitX, unitY, unitRX, unitRY, valueX, valueY,
          ratioX, ratioY, ratioRX, ratioRY } = this[_dragging];
    let { rx, ry } = this.coordUnits;

    let [newCx, newCy] = apply(this.transformMatrix, [valueX / ratioX, valueY / ratioY]);
    if (transX !== null && transY !== null) {
      // As part of scaling, the shape is translated to be tangent to the lines y=0 & x=0.
      // To get the new radii, we translate the new center back to that point and get the
      // distances to the line x=0 and y=0.
      rx = `${Math.abs((newCx - transX) * ratioRX)}${unitRX}`;
      ry = `${Math.abs((newCy - transY) * ratioRY)}${unitRY}`;
    }

    let ellipseDef = (this.geometryBox) ?
        `ellipse(${rx} ${ry} at ${newCx * ratioX}${unitX} ` +
          `${newCy * ratioY}${unitY}) ${this.geometryBox}` :
        `ellipse(${rx} ${ry} at ${newCx * ratioX}${unitX} ` +
          `${newCy * ratioY}${unitY})`;
    this.currentNode.style.setProperty(this.property, ellipseDef, "important");
  }

  /**
   * Transform an inset depending on the current transformation matrix.
   */
  _transformInset() {
    let { top, left, right, bottom } = this[_dragging].pointsInfo;
    let { width, height } = this.currentDimensions;

    let topLeft = [ left.value / left.ratio, top.value / top.ratio ];
    let [newLeft, newTop] = apply(this.transformMatrix, topLeft);
    newLeft = `${newLeft * left.ratio}${left.unit}`;
    newTop = `${newTop * top.ratio}${top.unit}`;

    // Right and bottom values are relative to the right and bottom edges of the
    // element, so convert to the value relative to the left/top edges before scaling
    // and convert back.
    let bottomRight = [ width - right.value / right.ratio,
                        height - bottom.value / bottom.ratio ];
    let [newRight, newBottom] = apply(this.transformMatrix, bottomRight);
    newRight = `${(width - newRight) * right.ratio}${right.unit}`;
    newBottom = `${(height - newBottom) * bottom.ratio}${bottom.unit}`;

    let round = this.insetRound;
    let insetDef = (round) ?
          `inset(${newTop} ${newRight} ${newBottom} ${newLeft} round ${round})` :
          `inset(${newTop} ${newRight} ${newBottom} ${newLeft})`;
    insetDef += (this.geometryBox) ? this.geometryBox : "";

    this.currentNode.style.setProperty(this.property, insetDef, "important");
  }

  /**
   * Handle a click when highlighting a polygon.
   * @param {Number} pageX the x coordinate of the click
   * @param {Number} pageY the y coordinate of the click
   */
  _handlePolygonClick(pageX, pageY) {
    let { width, height } = this.currentDimensions;
    let { percentX, percentY } = this.convertPageCoordsToPercent(pageX, pageY);
    let point = this.getPolygonPointAt(percentX, percentY);
    if (point === -1) {
      return;
    }

    let [x, y] = this.coordUnits[point];
    let xComputed = this.coordinates[point][0] / 100 * width;
    let yComputed = this.coordinates[point][1] / 100 * height;
    let unitX = getUnit(x);
    let unitY = getUnit(y);
    let valueX = (isUnitless(x)) ? xComputed : parseFloat(x);
    let valueY = (isUnitless(y)) ? yComputed : parseFloat(y);

    let ratioX = (valueX / xComputed) || 1;
    let ratioY = (valueY / yComputed) || 1;

    this.setCursor("grabbing");
    this[_dragging] = { point, unitX, unitY, valueX, valueY,
                        ratioX, ratioY, x: pageX, y: pageY };
  }

  /**
   * Set the inline style of the polygon, replacing the given point with the given x/y
   * coords.
   * @param {Number} pageX the new x coordinate of the point
   * @param {Number} pageY the new y coordinate of the point
   */
  _handlePolygonMove(pageX, pageY) {
    let { point, unitX, unitY, valueX, valueY, ratioX, ratioY, x, y } = this[_dragging];
    let deltaX = (pageX - x) * ratioX;
    let deltaY = (pageY - y) * ratioY;
    let precisionX = getDecimalPrecision(unitX);
    let precisionY = getDecimalPrecision(unitY);
    let newX = (valueX + deltaX).toFixed(precisionX);
    let newY = (valueY + deltaY).toFixed(precisionY);

    let polygonDef = (this.fillRule) ? `${this.fillRule}, ` : "";
    polygonDef += this.coordUnits.map((coords, i) => {
      return (i === point) ?
        `${newX}${unitX} ${newY}${unitY}` : `${coords[0]} ${coords[1]}`;
    }).join(", ");
    polygonDef = (this.geometryBox) ? `polygon(${polygonDef}) ${this.geometryBox}` :
                                      `polygon(${polygonDef})`;

    this.currentNode.style.setProperty(this.property, polygonDef, "important");
  }

  /**
   * Set the inline style of the polygon, adding a new point.
   * TODO: Bug 1436054 - Do not default to percentage unit when inserting new point.
   * https://bugzilla.mozilla.org/show_bug.cgi?id=1436054
   *
   * @param {Number} after the index of the point that the new point should be added after
   * @param {Number} x the x coordinate of the new point
   * @param {Number} y the y coordinate of the new point
   */
  _addPolygonPoint(after, x, y) {
    let polygonDef = (this.fillRule) ? `${this.fillRule}, ` : "";
    polygonDef += this.coordUnits.map((coords, i) => {
      return (i === after) ? `${coords[0]} ${coords[1]}, ${x}% ${y}%` :
                             `${coords[0]} ${coords[1]}`;
    }).join(", ");
    polygonDef = (this.geometryBox) ? `polygon(${polygonDef}) ${this.geometryBox}` :
                                      `polygon(${polygonDef})`;

    this.hoveredPoint = after + 1;
    this._emitHoverEvent(this.hoveredPoint);
    this.currentNode.style.setProperty(this.property, polygonDef, "important");
  }

  /**
   * Set the inline style of the polygon, deleting the given point.
   * @param {Number} point the index of the point to delete
   */
  _deletePolygonPoint(point) {
    let coordinates = this.coordUnits.slice();
    coordinates.splice(point, 1);
    let polygonDef = (this.fillRule) ? `${this.fillRule}, ` : "";
    polygonDef += coordinates.map((coords, i) => {
      return `${coords[0]} ${coords[1]}`;
    }).join(", ");
    polygonDef = (this.geometryBox) ? `polygon(${polygonDef}) ${this.geometryBox}` :
                                      `polygon(${polygonDef})`;

    this.hoveredPoint = null;
    this._emitHoverEvent(this.hoveredPoint);
    this.currentNode.style.setProperty(this.property, polygonDef, "important");
  }
  /**
   * Handle a click when highlighting a circle.
   * @param {Number} pageX the x coordinate of the click
   * @param {Number} pageY the y coordinate of the click
   */
  _handleCircleClick(pageX, pageY) {
    let { width, height } = this.currentDimensions;
    let { percentX, percentY } = this.convertPageCoordsToPercent(pageX, pageY);
    let point = this.getCirclePointAt(percentX, percentY);
    if (!point) {
      return;
    }

    this.setCursor("grabbing");
    if (point === "center") {
      let { cx, cy } = this.coordUnits;
      let cxComputed = this.coordinates.cx / 100 * width;
      let cyComputed = this.coordinates.cy / 100 * height;
      let unitX = getUnit(cx);
      let unitY = getUnit(cy);
      let valueX = (isUnitless(cx)) ? cxComputed : parseFloat(cx);
      let valueY = (isUnitless(cy)) ? cyComputed : parseFloat(cy);

      let ratioX = (valueX / cxComputed) || 1;
      let ratioY = (valueY / cyComputed) || 1;

      this[_dragging] = { point, unitX, unitY, valueX, valueY,
                          ratioX, ratioY, x: pageX, y: pageY };
    } else if (point === "radius") {
      let { radius } = this.coordinates;
      let computedSize = Math.sqrt((width ** 2) + (height ** 2)) / Math.sqrt(2);
      radius = radius / 100 * computedSize;
      let value = this.coordUnits.radius;
      let unit = getUnit(value);
      value = (isUnitless(value)) ? radius : parseFloat(value);
      let ratio = (value / radius) || 1;

      this[_dragging] = { point, value, origRadius: radius, unit, ratio };
    }
  }

  /**
   * Set the inline style of the circle, setting the center/radius according to the
   * mouse position.
   * @param {String} point either "center" or "radius"
   * @param {Number} pageX the x coordinate of the mouse position, in terms of %
   *        relative to the element
   * @param {Number} pageY the y coordinate of the mouse position, in terms of %
   *        relative to the element
   */
  _handleCircleMove(point, pageX, pageY) {
    let { radius, cx, cy } = this.coordUnits;

    if (point === "center") {
      let { unitX, unitY, valueX, valueY, ratioX, ratioY, x, y} = this[_dragging];
      let deltaX = (pageX - x) * ratioX;
      let deltaY = (pageY - y) * ratioY;
      let newCx = `${valueX + deltaX}${unitX}`;
      let newCy = `${valueY + deltaY}${unitY}`;
      let circleDef = (this.geometryBox) ?
            `circle(${radius} at ${newCx} ${newCy}) ${this.geometryBox}` :
            `circle(${radius} at ${newCx} ${newCy})`;

      this.currentNode.style.setProperty(this.property, circleDef, "important");
    } else if (point === "radius") {
      let { value, unit, origRadius, ratio } = this[_dragging];
      // convert center point to px, then get distance between center and mouse.
      let { x: pageCx, y: pageCy } = this.convertPercentToPageCoords(this.coordinates.cx,
                                                                     this.coordinates.cy);
      let newRadiusPx = getDistance(pageCx, pageCy, pageX, pageY);

      let delta = (newRadiusPx - origRadius) * ratio;
      let newRadius = `${value + delta}${unit}`;

      let circleDef = (this.geometryBox) ?
                      `circle(${newRadius} at ${cx} ${cy} ${this.geometryBox}` :
                      `circle(${newRadius} at ${cx} ${cy}`;

      this.currentNode.style.setProperty(this.property, circleDef, "important");
    }
  }

  /**
   * Handle a click when highlighting an ellipse.
   * @param {Number} pageX the x coordinate of the click
   * @param {Number} pageY the y coordinate of the click
   */
  _handleEllipseClick(pageX, pageY) {
    let { width, height } = this.currentDimensions;
    let { percentX, percentY } = this.convertPageCoordsToPercent(pageX, pageY);
    let point = this.getEllipsePointAt(percentX, percentY);
    if (!point) {
      return;
    }

    this.setCursor("grabbing");
    if (point === "center") {
      let { cx, cy } = this.coordUnits;
      let cxComputed = this.coordinates.cx / 100 * width;
      let cyComputed = this.coordinates.cy / 100 * height;
      let unitX = getUnit(cx);
      let unitY = getUnit(cy);
      let valueX = (isUnitless(cx)) ? cxComputed : parseFloat(cx);
      let valueY = (isUnitless(cy)) ? cyComputed : parseFloat(cy);

      let ratioX = (valueX / cxComputed) || 1;
      let ratioY = (valueY / cyComputed) || 1;

      this[_dragging] = { point, unitX, unitY, valueX, valueY,
                          ratioX, ratioY, x: pageX, y: pageY };
    } else if (point === "rx") {
      let { rx } = this.coordinates;
      rx = rx / 100 * width;
      let value = this.coordUnits.rx;
      let unit = getUnit(value);
      value = (isUnitless(value)) ? rx : parseFloat(value);
      let ratio = (value / rx) || 1;

      this[_dragging] = { point, value, origRadius: rx, unit, ratio };
    } else if (point === "ry") {
      let { ry } = this.coordinates;
      ry = ry / 100 * height;
      let value = this.coordUnits.ry;
      let unit = getUnit(value);
      value = (isUnitless(value)) ? ry : parseFloat(value);
      let ratio = (value / ry) || 1;

      this[_dragging] = { point, value, origRadius: ry, unit, ratio };
    }
  }

  /**
   * Set the inline style of the ellipse, setting the center/rx/ry according to the
   * mouse position.
   * @param {String} point "center", "rx", or "ry"
   * @param {Number} pageX the x coordinate of the mouse position, in terms of %
   *        relative to the element
   * @param {Number} pageY the y coordinate of the mouse position, in terms of %
   *        relative to the element
   */
  _handleEllipseMove(point, pageX, pageY) {
    let { percentX, percentY } = this.convertPageCoordsToPercent(pageX, pageY);
    let { rx, ry, cx, cy } = this.coordUnits;

    if (point === "center") {
      let { unitX, unitY, valueX, valueY, ratioX, ratioY, x, y} = this[_dragging];
      let deltaX = (pageX - x) * ratioX;
      let deltaY = (pageY - y) * ratioY;
      let newCx = `${valueX + deltaX}${unitX}`;
      let newCy = `${valueY + deltaY}${unitY}`;
      let ellipseDef = (this.geometryBox) ?
        `ellipse(${rx} ${ry} at ${newCx} ${newCy}) ${this.geometryBox}` :
        `ellipse(${rx} ${ry} at ${newCx} ${newCy})`;

      this.currentNode.style.setProperty(this.property, ellipseDef, "important");
    } else if (point === "rx") {
      let { value, unit, origRadius, ratio } = this[_dragging];
      let newRadiusPercent = Math.abs(percentX - this.coordinates.cx);
      let { width } = this.currentDimensions;
      let delta = ((newRadiusPercent / 100 * width) - origRadius) * ratio;
      let newRadius = `${value + delta}${unit}`;

      let ellipseDef = (this.geometryBox) ?
        `ellipse(${newRadius} ${ry} at ${cx} ${cy}) ${this.geometryBox}` :
        `ellipse(${newRadius} ${ry} at ${cx} ${cy})`;

      this.currentNode.style.setProperty(this.property, ellipseDef, "important");
    } else if (point === "ry") {
      let { value, unit, origRadius, ratio } = this[_dragging];
      let newRadiusPercent = Math.abs(percentY - this.coordinates.cy);
      let { height } = this.currentDimensions;
      let delta = ((newRadiusPercent / 100 * height) - origRadius) * ratio;
      let newRadius = `${value + delta}${unit}`;

      let ellipseDef = (this.geometryBox) ?
        `ellipse(${rx} ${newRadius} at ${cx} ${cy}) ${this.geometryBox}` :
        `ellipse(${rx} ${newRadius} at ${cx} ${cy})`;

      this.currentNode.style.setProperty(this.property, ellipseDef, "important");
    }
  }

  /**
   * Handle a click when highlighting an inset.
   * @param {Number} pageX the x coordinate of the click
   * @param {Number} pageY the y coordinate of the click
   */
  _handleInsetClick(pageX, pageY) {
    let { width, height } = this.currentDimensions;
    let { percentX, percentY } = this.convertPageCoordsToPercent(pageX, pageY);
    let point = this.getInsetPointAt(percentX, percentY);
    if (!point) {
      return;
    }

    this.setCursor("grabbing");
    let value = this.coordUnits[point];
    let size = (point === "left" || point === "right") ? width : height;
    let computedValue = this.coordinates[point] / 100 * size;
    let unit = getUnit(value);
    value = (isUnitless(value)) ? computedValue : parseFloat(value);
    let ratio = (value / computedValue) || 1;
    let origValue = (point === "left" || point === "right") ? pageX : pageY;

    this[_dragging] = { point, value, origValue, unit, ratio };
  }

  /**
   * Set the inline style of the inset, setting top/left/right/bottom according to the
   * mouse position.
   * @param {String} point "top", "left", "right", or "bottom"
   * @param {Number} pageX the x coordinate of the mouse position, in terms of %
   *        relative to the element
   * @param {Number} pageY the y coordinate of the mouse position, in terms of %
   *        relative to the element
   * @memberof ShapesHighlighter
   */
  _handleInsetMove(point, pageX, pageY) {
    let { top, left, right, bottom } = this.coordUnits;
    let round = this.insetRound;
    let { value, origValue, unit, ratio } = this[_dragging];

    if (point === "left") {
      let delta = (pageX - origValue) * ratio;
      left = `${value + delta}${unit}`;
    } else if (point === "right") {
      let delta = (pageX - origValue) * ratio;
      right = `${value - delta}${unit}`;
    } else if (point === "top") {
      let delta = (pageY - origValue) * ratio;
      top = `${value + delta}${unit}`;
    } else if (point === "bottom") {
      let delta = (pageY - origValue) * ratio;
      bottom = `${value - delta}${unit}`;
    }
    let insetDef = (round) ?
      `inset(${top} ${right} ${bottom} ${left} round ${round})` :
      `inset(${top} ${right} ${bottom} ${left})`;

    insetDef += (this.geometryBox) ? this.geometryBox : "";

    this.currentNode.style.setProperty(this.property, insetDef, "important");
  }

  _handleMouseMoveNotDragging(pageX, pageY) {
    let { percentX, percentY } = this.convertPageCoordsToPercent(pageX, pageY);
    if (this.transformMode) {
      let point = this.getTransformPointAt(percentX, percentY);
      this.hoveredPoint = point;
      this._handleMarkerHover(point);
    } else if (this.shapeType === "polygon") {
      let point = this.getPolygonPointAt(percentX, percentY);
      let oldHoveredPoint = this.hoveredPoint;
      this.hoveredPoint = (point !== -1) ? point : null;
      if (this.hoveredPoint !== oldHoveredPoint) {
        this._emitHoverEvent(this.hoveredPoint);
      }
      this._handleMarkerHover(point);
    } else if (this.shapeType === "circle") {
      let point = this.getCirclePointAt(percentX, percentY);
      let oldHoveredPoint = this.hoveredPoint;
      this.hoveredPoint = point ? point : null;
      if (this.hoveredPoint !== oldHoveredPoint) {
        this._emitHoverEvent(this.hoveredPoint);
      }
      this._handleMarkerHover(point);
    } else if (this.shapeType === "ellipse") {
      let point = this.getEllipsePointAt(percentX, percentY);
      let oldHoveredPoint = this.hoveredPoint;
      this.hoveredPoint = point ? point : null;
      if (this.hoveredPoint !== oldHoveredPoint) {
        this._emitHoverEvent(this.hoveredPoint);
      }
      this._handleMarkerHover(point);
    } else if (this.shapeType === "inset") {
      let point = this.getInsetPointAt(percentX, percentY);
      let oldHoveredPoint = this.hoveredPoint;
      this.hoveredPoint = point ? point : null;
      if (this.hoveredPoint !== oldHoveredPoint) {
        this._emitHoverEvent(this.hoveredPoint);
      }
      this._handleMarkerHover(point);
    }
  }

  /**
   * Change the appearance of the given marker when the mouse hovers over it.
   * @param {String|Number} point if the shape is a polygon, the integer index of the
   *        point being hovered. Otherwise, a string identifying the point being hovered.
   *        Integers < 0 and falsey values excluding 0 indicate no point is being hovered.
   */
  _handleMarkerHover(point) {
    // Hide hover marker for now, will be shown if point is a valid hover target
    this.getElement("marker-hover").setAttribute("hidden", true);
    // Catch all falsey values except when point === 0, as that's a valid point
    if (!point && point !== 0) {
      this.setCursor("auto");
      return;
    }
    let hoverCursor = (this[_dragging]) ? "grabbing" : "grab";

    if (this.transformMode) {
      if (!point) {
        this.setCursor("auto");
        return;
      }
      let { nw, ne, sw, se, n, w, s, e,
            rotatePoint, center } = this.transformedBoundingBox;

      const points = [
        { pointName: "translate", x: center[0], y: center[1], cursor: "move" },
        { pointName: "scale-se", x: se[0], y: se[1], anchor: "nw" },
        { pointName: "scale-ne", x: ne[0], y: ne[1], anchor: "sw" },
        { pointName: "scale-sw", x: sw[0], y: sw[1], anchor: "ne" },
        { pointName: "scale-nw", x: nw[0], y: nw[1], anchor: "se" },
        { pointName: "scale-n", x: n[0], y: n[1], anchor: "s" },
        { pointName: "scale-s", x: s[0], y: s[1], anchor: "n" },
        { pointName: "scale-e", x: e[0], y: e[1], anchor: "w" },
        { pointName: "scale-w", x: w[0], y: w[1], anchor: "e" },
        { pointName: "rotate", x: rotatePoint[0], y: rotatePoint[1], cursor: "grab" },
      ];

      for (let { pointName, x, y, cursor, anchor } of points) {
        if (point === pointName) {
          this._drawHoverMarker([[x, y]]);

          // If the point is a scale handle, we will need to determine the direction
          // of the resize cursor based on the position of the handle relative to its
          // "anchor" (the handle opposite to it).
          if (pointName.includes("scale")) {
            let direction = this.getRoughDirection(pointName, anchor);
            this.setCursor(`${direction}-resize`);
          } else {
            this.setCursor(cursor);
          }
        }
      }
    } else if (this.shapeType === "polygon") {
      if (point === -1) {
        this.setCursor("auto");
        return;
      }
      this.setCursor(hoverCursor);
      this._drawHoverMarker([this.coordinates[point]]);
    } else if (this.shapeType === "circle") {
      this.setCursor(hoverCursor);

      let { cx, cy, rx } = this.coordinates;
      if (point === "radius") {
        this._drawHoverMarker([[cx + rx, cy]]);
      } else if (point === "center") {
        this._drawHoverMarker([[cx, cy]]);
      }
    } else if (this.shapeType === "ellipse") {
      this.setCursor(hoverCursor);

      if (point === "center") {
        let { cx, cy } = this.coordinates;
        this._drawHoverMarker([[cx, cy]]);
      } else if (point === "rx") {
        let { cx, cy, rx } = this.coordinates;
        this._drawHoverMarker([[cx + rx, cy]]);
      } else if (point === "ry") {
        let { cx, cy, ry } = this.coordinates;
        this._drawHoverMarker([[cx, cy + ry]]);
      }
    } else if (this.shapeType === "inset") {
      this.setCursor(hoverCursor);

      let { top, right, bottom, left } = this.coordinates;
      let centerX = (left + (100 - right)) / 2;
      let centerY = (top + (100 - bottom)) / 2;
      let points = point.split(",");
      let coords = points.map(side => {
        if (side === "top") {
          return [centerX, top];
        } else if (side === "right") {
          return [100 - right, centerY];
        } else if (side === "bottom") {
          return [centerX, 100 - bottom];
        } else if (side === "left") {
          return [left, centerY];
        }
        return null;
      });

      this._drawHoverMarker(coords);
    }
  }

  _drawHoverMarker(points) {
    let { width, height } = this.currentDimensions;
    let zoom = getCurrentZoom(this.win);
    let path = points.map(([x, y]) => {
      return getCirclePath(BASE_MARKER_SIZE, x, y, width, height, zoom);
    }).join(" ");

    let markerHover = this.getElement("marker-hover");
    markerHover.setAttribute("d", path);
    markerHover.removeAttribute("hidden");
  }

  _emitHoverEvent(point) {
    if (point === null || point === undefined) {
      this.emit("highlighter-event", {
        type: "shape-hover-off"
      });
    } else {
      this.emit("highlighter-event", {
        type: "shape-hover-on",
        point: point.toString()
      });
    }
  }

  /**
   * Convert the given coordinates on the page to percentages relative to the current
   * element.
   * @param {Number} pageX the x coordinate on the page
   * @param {Number} pageY the y coordinate on the page
   * @returns {Object} object of form {percentX, percentY}, which are the x/y coords
   *          in percentages relative to the element.
   */
  convertPageCoordsToPercent(pageX, pageY) {
    // If the current node is in an iframe, we get dimensions relative to the frame.
    let dims = this.frameDimensions;
    let { top, left, width, height } = dims;
    pageX -= left;
    pageY -= top;
    let percentX = pageX * 100 / width;
    let percentY = pageY * 100 / height;
    return { percentX, percentY };
  }

  /**
   * Convert the given x/y coordinates, in percentages relative to the current element,
   * to pixel coordinates relative to the page
   * @param {Number} x the x coordinate
   * @param {Number} y the y coordinate
   * @returns {Object} object of form {x, y}, which are the x/y coords in pixels
   *          relative to the page
   *
   * @memberof ShapesHighlighter
   */
  convertPercentToPageCoords(x, y) {
    let dims = this.frameDimensions;
    let { top, left, width, height } = dims;
    x = x * width / 100;
    y = y * height / 100;
    x += left;
    y += top;
    return { x, y };
  }

  /**
   * Get which transformation should be applied based on the mouse position.
   * @param {Number} pageX the x coordinate of the mouse.
   * @param {Number} pageY the y coordinate of the mouse.
   * @returns {String} a string describing the transformation that should be applied
   *          to the shape.
   */
  getTransformPointAt(pageX, pageY) {
    let { nw, ne, sw, se, n, w, s, e, rotatePoint, center } = this.transformedBoundingBox;
    let { width, height } = this.currentDimensions;
    let zoom = getCurrentZoom(this.win);
    let clickRadiusX = BASE_MARKER_SIZE / zoom * 100 / width;
    let clickRadiusY = BASE_MARKER_SIZE / zoom * 100 / height;

    let points = [
      { pointName: "translate", x: center[0], y: center[1] },
      { pointName: "scale-se", x: se[0], y: se[1] },
      { pointName: "scale-ne", x: ne[0], y: ne[1] },
      { pointName: "scale-sw", x: sw[0], y: sw[1] },
      { pointName: "scale-nw", x: nw[0], y: nw[1] },
    ];

    if (this.shapeType === "polygon" || this.shapeType === "ellipse") {
      points.push({ pointName: "scale-n", x: n[0], y: n[1] },
                  { pointName: "scale-s", x: s[0], y: s[1] },
                  { pointName: "scale-e", x: e[0], y: e[1] },
                  { pointName: "scale-w", x: w[0], y: w[1] });
    }

    if (this.shapeType === "polygon") {
      let x = rotatePoint[0];
      let y = rotatePoint[1];
      if (pageX >= x - clickRadiusX && pageX <= x + clickRadiusX &&
          pageY >= y - clickRadiusY && pageY <= y + clickRadiusY) {
        return "rotate";
      }
    }

    for (let { pointName, x, y } of points) {
      if (pageX >= x - clickRadiusX && pageX <= x + clickRadiusX &&
          pageY >= y - clickRadiusY && pageY <= y + clickRadiusY) {
        return pointName;
      }
    }

    return "";
  }

  /**
   * Get the id of the point on the polygon highlighter at the given coordinate.
   * @param {Number} pageX the x coordinate on the page, in % relative to the element
   * @param {Number} pageY the y coordinate on the page, in % relative to the element
   * @returns {Number} the index of the point that was clicked on in this.coordinates,
   *          or -1 if none of the points were clicked on.
   */
  getPolygonPointAt(pageX, pageY) {
    let { coordinates } = this;
    let { width, height } = this.currentDimensions;
    let zoom = getCurrentZoom(this.win);
    let clickRadiusX = BASE_MARKER_SIZE / zoom * 100 / width;
    let clickRadiusY = BASE_MARKER_SIZE / zoom * 100 / height;

    for (let [index, coord] of coordinates.entries()) {
      let [x, y] = coord;
      if (pageX >= x - clickRadiusX && pageX <= x + clickRadiusX &&
          pageY >= y - clickRadiusY && pageY <= y + clickRadiusY) {
        return index;
      }
    }

    return -1;
  }

  /**
   * Check if the mouse clicked on a line of the polygon, and if so, add a point near
   * the click.
   * @param {Number} pageX the x coordinate on the page, in % relative to the element
   * @param {Number} pageY the y coordinate on the page, in % relative to the element
   */
  getPolygonClickedLine(pageX, pageY) {
    let { coordinates } = this;
    let { width } = this.currentDimensions;
    let clickWidth = LINE_CLICK_WIDTH * 100 / width;

    for (let i = 0; i < coordinates.length; i++) {
      let [x1, y1] = coordinates[i];
      let [x2, y2] = (i === coordinates.length - 1) ? coordinates[0] : coordinates[i + 1];
      // Get the distance between clicked point and line drawn between points 1 and 2
      // to check if the click was on the line between those two points.
      let distance = distanceToLine(x1, y1, x2, y2, pageX, pageY);
      if (distance <= clickWidth &&
          Math.min(x1, x2) - clickWidth <= pageX &&
          pageX <= Math.max(x1, x2) + clickWidth &&
          Math.min(y1, y2) - clickWidth <= pageY &&
          pageY <= Math.max(y1, y2) + clickWidth) {
        // Get the point on the line closest to the clicked point.
        let [newX, newY] = projection(x1, y1, x2, y2, pageX, pageY);
        // Default unit for new points is percentages
        let precision = getDecimalPrecision("%");
        this._addPolygonPoint(i, newX.toFixed(precision), newY.toFixed(precision));
        return;
      }
    }
  }

  /**
   * Check if the center point or radius of the circle highlighter is at given coords
   * @param {Number} pageX the x coordinate on the page, in % relative to the element
   * @param {Number} pageY the y coordinate on the page, in % relative to the element
   * @returns {String} "center" if the center point was clicked, "radius" if the radius
   *          was clicked, "" if neither was clicked.
   */
  getCirclePointAt(pageX, pageY) {
    let { cx, cy, rx, ry } = this.coordinates;
    let { width, height } = this.currentDimensions;
    let zoom = getCurrentZoom(this.win);
    let clickRadiusX = BASE_MARKER_SIZE / zoom * 100 / width;
    let clickRadiusY = BASE_MARKER_SIZE / zoom * 100 / height;

    if (clickedOnPoint(pageX, pageY, cx, cy, clickRadiusX, clickRadiusY)) {
      return "center";
    }

    let clickWidthX = LINE_CLICK_WIDTH * 100 / width;
    let clickWidthY = LINE_CLICK_WIDTH * 100 / height;
    if (clickedOnEllipseEdge(pageX, pageY, cx, cy, rx, ry, clickWidthX, clickWidthY) ||
        clickedOnPoint(pageX, pageY, cx + rx, cy, clickRadiusX, clickRadiusY)) {
      return "radius";
    }

    return "";
  }

  /**
   * Check if the center or rx/ry points of the ellipse highlighter is at given point
   * @param {Number} pageX the x coordinate on the page, in % relative to the element
   * @param {Number} pageY the y coordinate on the page, in % relative to the element
   * @returns {String} "center" if the center point was clicked, "rx" if the x-radius
   *          point was clicked, "ry" if the y-radius point was clicked,
   *          "" if none was clicked.
   */
  getEllipsePointAt(pageX, pageY) {
    let { cx, cy, rx, ry } = this.coordinates;
    let { width, height } = this.currentDimensions;
    let zoom = getCurrentZoom(this.win);
    let clickRadiusX = BASE_MARKER_SIZE / zoom * 100 / width;
    let clickRadiusY = BASE_MARKER_SIZE / zoom * 100 / height;

    if (clickedOnPoint(pageX, pageY, cx, cy, clickRadiusX, clickRadiusY)) {
      return "center";
    }

    if (clickedOnPoint(pageX, pageY, cx + rx, cy, clickRadiusX, clickRadiusY)) {
      return "rx";
    }

    if (clickedOnPoint(pageX, pageY, cx, cy + ry, clickRadiusX, clickRadiusY)) {
      return "ry";
    }

    return "";
  }

  /**
   * Check if the edges of the inset highlighter is at given coords
   * @param {Number} pageX the x coordinate on the page, in % relative to the element
   * @param {Number} pageY the y coordinate on the page, in % relative to the element
   * @returns {String} "top", "left", "right", or "bottom" if any of those edges were
   *          clicked. "" if none were clicked.
   */
  getInsetPointAt(pageX, pageY) {
    let { top, left, right, bottom } = this.coordinates;
    let zoom = getCurrentZoom(this.win);
    let { width, height } = this.currentDimensions;
    let clickWidthX = LINE_CLICK_WIDTH * 100 / width;
    let clickWidthY = LINE_CLICK_WIDTH * 100 / height;
    let clickRadiusX = BASE_MARKER_SIZE / zoom * 100 / width;
    let clickRadiusY = BASE_MARKER_SIZE / zoom * 100 / height;
    let centerX = (left + (100 - right)) / 2;
    let centerY = (top + (100 - bottom)) / 2;

    if ((pageX >= left - clickWidthX && pageX <= left + clickWidthX &&
        pageY >= top && pageY <= 100 - bottom) ||
        clickedOnPoint(pageX, pageY, left, centerY, clickRadiusX, clickRadiusY)) {
      return "left";
    }

    if ((pageX >= 100 - right - clickWidthX && pageX <= 100 - right + clickWidthX &&
        pageY >= top && pageY <= 100 - bottom) ||
        clickedOnPoint(pageX, pageY, 100 - right, centerY, clickRadiusX, clickRadiusY)) {
      return "right";
    }

    if ((pageY >= top - clickWidthY && pageY <= top + clickWidthY &&
        pageX >= left && pageX <= 100 - right) ||
        clickedOnPoint(pageX, pageY, centerX, top, clickRadiusX, clickRadiusY)) {
      return "top";
    }

    if ((pageY >= 100 - bottom - clickWidthY && pageY <= 100 - bottom + clickWidthY &&
        pageX >= left && pageX <= 100 - right) ||
        clickedOnPoint(pageX, pageY, centerX, 100 - bottom, clickRadiusX, clickRadiusY)) {
      return "bottom";
    }

    return "";
  }

  /**
   * Parses the CSS definition given and returns the shape type associated
   * with the definition and the coordinates necessary to draw the shape.
   * @param {String} definition the input CSS definition
   * @returns {Object} null if the definition is not of a known shape type,
   *          or an object of the type { shapeType, coordinates }, where
   *          shapeType is the name of the shape and coordinates are an array
   *          or object of the coordinates needed to draw the shape.
   */
  _parseCSSShapeValue(definition) {
    const shapeTypes = [{
      name: "polygon",
      prefix: "polygon(",
      coordParser: this.polygonPoints.bind(this)
    }, {
      name: "circle",
      prefix: "circle(",
      coordParser: this.circlePoints.bind(this)
    }, {
      name: "ellipse",
      prefix: "ellipse(",
      coordParser: this.ellipsePoints.bind(this)
    }, {
      name: "inset",
      prefix: "inset(",
      coordParser: this.insetPoints.bind(this)
    }];
    const geometryTypes = ["margin", "border", "padding", "content"];

    // default to border for clip-path, and margin for shape-outside
    let referenceBox = this.property === "clip-path" ? "border" : "margin";
    for (let geometry of geometryTypes) {
      if (definition.includes(geometry)) {
        referenceBox = geometry;
      }
    }
    this.referenceBox = referenceBox;

    this.useStrokeBox = definition.includes("stroke-box");
    this.geometryBox = definition.substring(definition.lastIndexOf(")") + 1).trim();

    for (let { name, prefix, coordParser } of shapeTypes) {
      if (definition.includes(prefix)) {
        // the closing paren of the shape function is always the last one in definition.
        definition = definition.substring(prefix.length, definition.lastIndexOf(")"));
        return {
          shapeType: name,
          coordinates: coordParser(definition)
        };
      }
    }

    return null;
  }

  /**
   * Parses the definition of the CSS polygon() function and returns its points,
   * converted to percentages.
   * @param {String} definition the arguments of the polygon() function
   * @returns {Array} an array of the points of the polygon, with all values
   *          evaluated and converted to percentages
   */
  polygonPoints(definition) {
    this.coordUnits = this.polygonRawPoints();
    if (!this.origCoordUnits) {
      this.origCoordUnits = this.coordUnits;
    }
    let splitDef = definition.split(", ");
    if (splitDef[0] === "evenodd" || splitDef[0] === "nonzero") {
      splitDef.shift();
    }
    let minX = Number.MAX_SAFE_INTEGER;
    let minY = Number.MAX_SAFE_INTEGER;
    let maxX = Number.MIN_SAFE_INTEGER;
    let maxY = Number.MIN_SAFE_INTEGER;
    let coordinates = splitDef.map(coords => {
      let [x, y] = splitCoords(coords).map(this.convertCoordsToPercent.bind(this));
      if (x < minX) {
        minX = x;
      }
      if (y < minY) {
        minY = y;
      }
      if (x > maxX) {
        maxX = x;
      }
      if (y > maxY) {
        maxY = y;
      }
      return [x, y];
    });
    this.boundingBox = { minX, minY, maxX, maxY };
    if (!this.origBoundingBox) {
      this.origBoundingBox = this.boundingBox;
    }
    return coordinates;
  }

  /**
   * Parse the raw (non-computed) definition of the CSS polygon.
   * @returns {Array} an array of the points of the polygon, with units preserved.
   */
  polygonRawPoints() {
    let definition = getDefinedShapeProperties(this.currentNode, this.property);
    if (definition === this.rawDefinition && this.coordUnits) {
      return this.coordUnits;
    }
    this.rawDefinition = definition;
    definition = definition.substring(8, definition.lastIndexOf(")"));
    let splitDef = definition.split(", ");
    if (splitDef[0].includes("evenodd") || splitDef[0].includes("nonzero")) {
      this.fillRule = splitDef[0].trim();
      splitDef.shift();
    } else {
      this.fillRule = "";
    }
    return splitDef.map(coords => {
      return splitCoords(coords).map(coord => {
        // Undo the insertion of &nbsp; that was done in splitCoords.
        return coord.replace(/\u00a0/g, " ");
      });
    });
  }

  /**
   * Parses the definition of the CSS circle() function and returns the x/y radiuses and
   * center coordinates, converted to percentages.
   * @param {String} definition the arguments of the circle() function
   * @returns {Object} an object of the form { rx, ry, cx, cy }, where rx and ry are the
   *          radiuses for the x and y axes, and cx and cy are the x/y coordinates for the
   *          center of the circle. All values are evaluated and converted to percentages.
   */
  circlePoints(definition) {
    this.coordUnits = this.circleRawPoints();
    if (!this.origCoordUnits) {
      this.origCoordUnits = this.coordUnits;
    }
    // The computed value of circle() always has the keyword "at".
    let values = definition.split(" at ");
    let radius = values[0];
    let { width, height } = this.currentDimensions;
    let center = splitCoords(values[1]).map(this.convertCoordsToPercent.bind(this));

    // Percentage values for circle() are resolved from the
    // used width and height of the reference box as sqrt(width^2+height^2)/sqrt(2).
    let computedSize = Math.sqrt((width ** 2) + (height ** 2)) / Math.sqrt(2);

    if (radius === "closest-side") {
      // radius is the distance from center to closest side of reference box
      radius = Math.min(center[0], center[1], 100 - center[0], 100 - center[1]);
    } else if (radius === "farthest-side") {
      // radius is the distance from center to farthest side of reference box
      radius = Math.max(center[0], center[1], 100 - center[0], 100 - center[1]);
    } else if (radius.includes("calc(")) {
      radius = evalCalcExpression(radius.substring(5, radius.length - 1), computedSize);
    } else {
      radius = coordToPercent(radius, computedSize);
    }

    // Scale both radiusX and radiusY to match the radius computed
    // using the above equation.
    let ratioX = width / computedSize;
    let ratioY = height / computedSize;
    let radiusX = radius / ratioX;
    let radiusY = radius / ratioY;

    this.boundingBox = { minX: center[0] - radiusX, maxX: center[0] + radiusX,
                         minY: center[1] - radiusY, maxY: center[1] + radiusY };
    if (!this.origBoundingBox) {
      this.origBoundingBox = this.boundingBox;
    }
    return { radius, rx: radiusX, ry: radiusY, cx: center[0], cy: center[1] };
  }

  /**
   * Parse the raw (non-computed) definition of the CSS circle.
   * @returns {Object} an object of the points of the circle (cx, cy, radius),
   *          with units preserved.
   */
  circleRawPoints() {
    let definition = getDefinedShapeProperties(this.currentNode, this.property);
    if (definition === this.rawDefinition && this.coordUnits) {
      return this.coordUnits;
    }
    this.rawDefinition = definition;
    definition = definition.substring(7, definition.lastIndexOf(")"));

    let values = definition.split("at");
    let [cx = "", cy = ""] = (values[1]) ? splitCoords(values[1]).map(coord => {
      // Undo the insertion of &nbsp; that was done in splitCoords.
      return coord.replace(/\u00a0/g, " ");
    }) : [];
    let radius = (values[0]) ? values[0].trim() : "closest-side";
    return { cx, cy, radius };
  }

  /**
   * Parses the definition of the CSS ellipse() function and returns the x/y radiuses and
   * center coordinates, converted to percentages.
   * @param {String} definition the arguments of the ellipse() function
   * @returns {Object} an object of the form { rx, ry, cx, cy }, where rx and ry are the
   *          radiuses for the x and y axes, and cx and cy are the x/y coordinates for the
   *          center of the ellipse. All values are evaluated and converted to percentages
   */
  ellipsePoints(definition) {
    this.coordUnits = this.ellipseRawPoints();
    if (!this.origCoordUnits) {
      this.origCoordUnits = this.coordUnits;
    }
    let values = definition.split(" at ");
    let center = splitCoords(values[1]).map(this.convertCoordsToPercent.bind(this));

    let radii = splitCoords(values[0]).map((radius, i) => {
      if (radius === "closest-side") {
        // radius is the distance from center to closest x/y side of reference box
        return i % 2 === 0 ? Math.min(center[0], 100 - center[0])
                           : Math.min(center[1], 100 - center[1]);
      } else if (radius === "farthest-side") {
        // radius is the distance from center to farthest x/y side of reference box
        return i % 2 === 0 ? Math.max(center[0], 100 - center[0])
                           : Math.max(center[1], 100 - center[1]);
      }
      return this.convertCoordsToPercent(radius, i);
    });

    this.boundingBox = { minX: center[0] - radii[0], maxX: center[0] + radii[0],
                         minY: center[1] - radii[1], maxY: center[1] + radii[1] };
    if (!this.origBoundingBox) {
      this.origBoundingBox = this.boundingBox;
    }
    return { rx: radii[0], ry: radii[1], cx: center[0], cy: center[1] };
  }

  /**
   * Parse the raw (non-computed) definition of the CSS ellipse.
   * @returns {Object} an object of the points of the ellipse (cx, cy, rx, ry),
   *          with units preserved.
   */
  ellipseRawPoints() {
    let definition = getDefinedShapeProperties(this.currentNode, this.property);
    if (definition === this.rawDefinition && this.coordUnits) {
      return this.coordUnits;
    }
    this.rawDefinition = definition;
    definition = definition.substring(8, definition.lastIndexOf(")"));

    let values = definition.split("at");
    let [rx = "closest-side", ry = "closest-side"] = (values[0]) ?
      splitCoords(values[0]).map(coord => {
        // Undo the insertion of &nbsp; that was done in splitCoords.
        return coord.replace(/\u00a0/g, " ");
      }) : [];
    let [cx = "", cy = ""] = (values[1]) ? splitCoords(values[1]).map(coord => {
      return coord.replace(/\u00a0/g, " ");
    }) : [];
    return { rx, ry, cx, cy };
  }

  /**
   * Parses the definition of the CSS inset() function and returns the x/y offsets and
   * width/height of the shape, converted to percentages. Border radiuses (given after
   * "round" in the definition) are currently ignored.
   * @param {String} definition the arguments of the inset() function
   * @returns {Object} an object of the form { x, y, width, height }, which are the top/
   *          left positions and width/height of the shape.
   */
  insetPoints(definition) {
    this.coordUnits = this.insetRawPoints();
    if (!this.origCoordUnits) {
      this.origCoordUnits = this.coordUnits;
    }
    let values = definition.split(" round ");
    let offsets = splitCoords(values[0]).map(this.convertCoordsToPercent.bind(this));

    let top, left, right, bottom;
    // The offsets, like margin/padding/border, are in order: top, right, bottom, left.
    if (offsets.length === 1) {
      top = left = right = bottom = offsets[0];
    } else if (offsets.length === 2) {
      top = bottom = offsets[0];
      left = right = offsets[1];
    } else if (offsets.length === 3) {
      top = offsets[0];
      left = right = offsets[1];
      bottom = offsets[2];
    } else if (offsets.length === 4) {
      top = offsets[0];
      right = offsets[1];
      bottom = offsets[2];
      left = offsets[3];
    }

    // maxX/maxY are found by subtracting the right/bottom edges from 100
    // (the width/height of the element in %)
    this.boundingBox = { minX: left, maxX: 100 - right, minY: top, maxY: 100 - bottom};
    if (!this.origBoundingBox) {
      this.origBoundingBox = this.boundingBox;
    }
    return { top, left, right, bottom };
  }

  /**
   * Parse the raw (non-computed) definition of the CSS inset.
   * @returns {Object} an object of the points of the inset (top, right, bottom, left),
   *          with units preserved.
   */
  insetRawPoints() {
    let definition = getDefinedShapeProperties(this.currentNode, this.property);
    if (definition === this.rawDefinition && this.coordUnits) {
      return this.coordUnits;
    }
    this.rawDefinition = definition;
    definition = definition.substring(6, definition.lastIndexOf(")"));

    let values = definition.split(" round ");
    this.insetRound = values[1];
    let offsets = splitCoords(values[0]).map(coord => {
      // Undo the insertion of &nbsp; that was done in splitCoords.
      return coord.replace(/\u00a0/g, " ");
    });

    let top, left, right, bottom = 0;

    if (offsets.length === 1) {
      top = left = right = bottom = offsets[0];
    } else if (offsets.length === 2) {
      top = bottom = offsets[0];
      left = right = offsets[1];
    } else if (offsets.length === 3) {
      top = offsets[0];
      left = right = offsets[1];
      bottom = offsets[2];
    } else if (offsets.length === 4) {
      top = offsets[0];
      right = offsets[1];
      bottom = offsets[2];
      left = offsets[3];
    }

    return { top, left, right, bottom };
  }

  convertCoordsToPercent(coord, i) {
    let { width, height } = this.currentDimensions;
    let size = i % 2 === 0 ? width : height;
    if (coord.includes("calc(")) {
      return evalCalcExpression(coord.substring(5, coord.length - 1), size);
    }
    return coordToPercent(coord, size);
  }

  /**
   * Destroy the nodes. Remove listeners.
   */
  destroy() {
    let { pageListenerTarget } = this.highlighterEnv;
    if (pageListenerTarget) {
      DOM_EVENTS.forEach(type => pageListenerTarget.removeEventListener(type, this));
    }
    super.destroy(this);
    this.markup.destroy();
  }

  /**
   * Get the element in the highlighter markup with the given id
   * @param {String} id
   * @returns {Object} the element with the given id
   */
  getElement(id) {
    return this.markup.getElement(this.ID_CLASS_PREFIX + id);
  }

  /**
   * Return whether all the elements used to draw shapes are hidden.
   * @returns {Boolean}
   */
  areShapesHidden() {
    return this.getElement("ellipse").hasAttribute("hidden") &&
           this.getElement("polygon").hasAttribute("hidden") &&
           this.getElement("rect").hasAttribute("hidden") &&
           this.getElement("bounding-box").hasAttribute("hidden");
  }

  /**
   * Show the highlighter on a given node
   */
  _show() {
    this.hoveredPoint = this.options.hoverPoint;
    this.transformMode = this.options.transformMode;
    this.coordinates = null;
    this.coordUnits = null;
    this.origBoundingBox = null;
    this.origCoordUnits = null;
    this.origCoordinates = null;
    this.transformedBoundingBox = null;
    if (this.transformMode) {
      this.transformMatrix = identity();
    }
    if (this._hasMoved() && this.transformMode) {
      this.transformedBoundingBox = this.calculateTransformedBoundingBox();
    }
    return this._update();
  }

  /**
   * The AutoRefreshHighlighter's _hasMoved method returns true only if the element's
   * quads have changed. Override it so it also returns true if the element's shape has
   * changed (which can happen when you change a CSS properties for instance).
   */
  _hasMoved() {
    let hasMoved = AutoRefreshHighlighter.prototype._hasMoved.call(this);

    if (hasMoved) {
      this.origBoundingBox = null;
      this.origCoordUnits = null;
      this.origCoordinates = null;
      if (this.transformMode) {
        this.transformMatrix = identity();
      }
    }

    let oldShapeCoordinates = JSON.stringify(this.coordinates);

    // TODO: need other modes too.
    if (this.options.mode.startsWith("css")) {
      let property = shapeModeToCssPropertyName(this.options.mode);
      // change camelCase to kebab-case
      this.property = property.replace(/([a-z][A-Z])/g, g => {
        return g[0] + "-" + g[1].toLowerCase();
      });
      let style = getComputedStyle(this.currentNode)[property];

      if (!style || style === "none") {
        this.coordinates = [];
        this.shapeType = "none";
      } else {
        let { coordinates, shapeType } = this._parseCSSShapeValue(style);
        this.coordinates = coordinates;
        if (!this.origCoordinates) {
          this.origCoordinates = coordinates;
        }
        this.shapeType = shapeType;
      }
    }

    let newShapeCoordinates = JSON.stringify(this.coordinates);
    hasMoved = hasMoved || oldShapeCoordinates !== newShapeCoordinates;
    if (this.transformMode && hasMoved) {
      this.transformedBoundingBox = this.calculateTransformedBoundingBox();
    }

    return hasMoved;
  }

  /**
   * Hide all elements used to highlight CSS different shapes.
   */
  _hideShapes() {
    this.getElement("ellipse").setAttribute("hidden", true);
    this.getElement("polygon").setAttribute("hidden", true);
    this.getElement("rect").setAttribute("hidden", true);
    this.getElement("bounding-box").setAttribute("hidden", true);
    this.getElement("markers").setAttribute("d", "");
    this.getElement("markers-outline").setAttribute("d", "");
    this.getElement("rotate-line").setAttribute("d", "");
    this.getElement("quad").setAttribute("hidden", true);
    this.getElement("clip-ellipse").setAttribute("hidden", true);
    this.getElement("clip-polygon").setAttribute("hidden", true);
    this.getElement("clip-rect").setAttribute("hidden", true);
    this.getElement("dashed-polygon").setAttribute("hidden", true);
    this.getElement("dashed-ellipse").setAttribute("hidden", true);
    this.getElement("dashed-rect").setAttribute("hidden", true);
  }

  /**
   * Update the highlighter for the current node. Called whenever the element's quads
   * or CSS shape has changed.
   * @returns {Boolean} whether the highlighter was successfully updated
   */
  _update() {
    setIgnoreLayoutChanges(true);
    this.getElement("group").setAttribute("transform", "");
    let root = this.getElement("root");
    root.setAttribute("hidden", true);

    let { top, left, width, height } = this.currentDimensions;
    let zoom = getCurrentZoom(this.win);

    // Size the SVG like the current node.
    this.getElement("shape-container").setAttribute("style",
      `top:${top}px;left:${left}px;width:${width}px;height:${height}px;`);

    this._hideShapes();

    if (this.transformMode && this.shapeType !== "none") {
      this._updateTransformMode(width, height, zoom);
    } else if (this.shapeType === "polygon") {
      this._updatePolygonShape(width, height, zoom);
    } else if (this.shapeType === "circle") {
      this._updateCircleShape(width, height, zoom);
    } else if (this.shapeType === "ellipse") {
      this._updateEllipseShape(width, height, zoom);
    } else if (this.shapeType === "inset") {
      this._updateInsetShape(width, height, zoom);
    }

    if (this.property === "shape-outside") {
      // For shape-outside, the element's quads are displayed for the parts that overlap
      // with the shape, and the parts of the shape that extend past the element's quads
      // are shown with a dashed line.
      let quadRect = this.getElement("quad");
      quadRect.removeAttribute("hidden");

      this.getElement("polygon").setAttribute("clip-path", "url(#shapes-quad-clip-path)");
      this.getElement("ellipse").setAttribute("clip-path", "url(#shapes-quad-clip-path)");
      this.getElement("rect").setAttribute("clip-path", "url(#shapes-quad-clip-path)");
    } else {
      this.getElement("polygon").removeAttribute("clip-path");
      this.getElement("ellipse").removeAttribute("clip-path");
      this.getElement("rect").removeAttribute("clip-path");
    }

    let { width: winWidth, height: winHeight } = this._winDimensions;
    root.removeAttribute("hidden");
    root.setAttribute("style",
      `position:absolute; width:${winWidth}px;height:${winHeight}px; overflow:hidden;`);

    this._handleMarkerHover(this.hoveredPoint);

    setIgnoreLayoutChanges(false, this.highlighterEnv.window.document.documentElement);

    return true;
  }

  /**
   * Update the SVGs for transform mode to fit the new shape.
   * @param {Number} width the width of the element quads
   * @param {Number} height the height of the element quads
   * @param {Number} zoom the zoom level of the window
   */
  _updateTransformMode(width, height, zoom) {
    let { nw, ne, sw, se, n, w, s, e, rotatePoint, center } = this.transformedBoundingBox;
    let boundingBox = this.getElement("bounding-box");
    let path = `M${nw.join(" ")} L${ne.join(" ")} L${se.join(" ")} L${sw.join(" ")} Z`;
    boundingBox.setAttribute("d", path);
    boundingBox.removeAttribute("hidden");

    let markerPoints = [center, nw, ne, se, sw];
    if (this.shapeType === "polygon" || this.shapeType === "ellipse") {
      markerPoints.push(n, s, w, e);
    }
    this._drawMarkers(markerPoints, width, height, zoom);

    if (this.shapeType === "polygon") {
      let points = this.coordinates.map(point => point.join(",")).join(" ");

      let polygonEl = this.getElement("polygon");
      polygonEl.setAttribute("points", points);
      polygonEl.removeAttribute("hidden");

      let clipPolygon = this.getElement("clip-polygon");
      clipPolygon.setAttribute("points", points);
      clipPolygon.removeAttribute("hidden");

      let dashedPolygon = this.getElement("dashed-polygon");
      dashedPolygon.setAttribute("points", points);
      dashedPolygon.removeAttribute("hidden");

      markerPoints.push(rotatePoint);
      let rotateLine = `M ${center.join(" ")} L ${rotatePoint.join(" ")}`;
      this.getElement("rotate-line").setAttribute("d", rotateLine);
    } else if (this.shapeType === "circle" || this.shapeType === "ellipse") {
      let { rx, ry, cx, cy } = this.coordinates;
      let ellipseEl = this.getElement("ellipse");
      ellipseEl.setAttribute("rx", rx);
      ellipseEl.setAttribute("ry", ry);
      ellipseEl.setAttribute("cx", cx);
      ellipseEl.setAttribute("cy", cy);
      ellipseEl.removeAttribute("hidden");

      let clipEllipse = this.getElement("clip-ellipse");
      clipEllipse.setAttribute("rx", rx);
      clipEllipse.setAttribute("ry", ry);
      clipEllipse.setAttribute("cx", cx);
      clipEllipse.setAttribute("cy", cy);
      clipEllipse.removeAttribute("hidden");

      let dashedEllipse = this.getElement("dashed-ellipse");
      dashedEllipse.setAttribute("rx", rx);
      dashedEllipse.setAttribute("ry", ry);
      dashedEllipse.setAttribute("cx", cx);
      dashedEllipse.setAttribute("cy", cy);
      dashedEllipse.removeAttribute("hidden");
    }
    this._drawMarkers(markerPoints, width, height, zoom);
  }

  /**
   * Update the SVG polygon to fit the CSS polygon.
   * @param {Number} width the width of the element quads
   * @param {Number} height the height of the element quads
   * @param {Number} zoom the zoom level of the window
   */
  _updatePolygonShape(width, height, zoom) {
    // Draw and show the polygon.
    let points = this.coordinates.map(point => point.join(",")).join(" ");

    let polygonEl = this.getElement("polygon");
    polygonEl.setAttribute("points", points);
    polygonEl.removeAttribute("hidden");

    let clipPolygon = this.getElement("clip-polygon");
    clipPolygon.setAttribute("points", points);
    clipPolygon.removeAttribute("hidden");

    let dashedPolygon = this.getElement("dashed-polygon");
    dashedPolygon.setAttribute("points", points);
    dashedPolygon.removeAttribute("hidden");
    this._drawMarkers(this.coordinates, width, height, zoom);
  }

  /**
   * Update the SVG ellipse to fit the CSS circle.
   * @param {Number} width the width of the element quads
   * @param {Number} height the height of the element quads
   * @param {Number} zoom the zoom level of the window
   */
  _updateCircleShape(width, height, zoom) {
    let { rx, ry, cx, cy } = this.coordinates;
    let ellipseEl = this.getElement("ellipse");
    ellipseEl.setAttribute("rx", rx);
    ellipseEl.setAttribute("ry", ry);
    ellipseEl.setAttribute("cx", cx);
    ellipseEl.setAttribute("cy", cy);
    ellipseEl.removeAttribute("hidden");

    let clipEllipse = this.getElement("clip-ellipse");
    clipEllipse.setAttribute("rx", rx);
    clipEllipse.setAttribute("ry", ry);
    clipEllipse.setAttribute("cx", cx);
    clipEllipse.setAttribute("cy", cy);
    clipEllipse.removeAttribute("hidden");

    let dashedEllipse = this.getElement("dashed-ellipse");
    dashedEllipse.setAttribute("rx", rx);
    dashedEllipse.setAttribute("ry", ry);
    dashedEllipse.setAttribute("cx", cx);
    dashedEllipse.setAttribute("cy", cy);
    dashedEllipse.removeAttribute("hidden");

    this._drawMarkers([[cx, cy], [cx + rx, cy]], width, height, zoom);
  }

  /**
   * Update the SVG ellipse to fit the CSS ellipse.
   * @param {Number} width the width of the element quads
   * @param {Number} height the height of the element quads
   * @param {Number} zoom the zoom level of the window
   */
  _updateEllipseShape(width, height, zoom) {
    let { rx, ry, cx, cy } = this.coordinates;
    let ellipseEl = this.getElement("ellipse");
    ellipseEl.setAttribute("rx", rx);
    ellipseEl.setAttribute("ry", ry);
    ellipseEl.setAttribute("cx", cx);
    ellipseEl.setAttribute("cy", cy);
    ellipseEl.removeAttribute("hidden");

    let clipEllipse = this.getElement("clip-ellipse");
    clipEllipse.setAttribute("rx", rx);
    clipEllipse.setAttribute("ry", ry);
    clipEllipse.setAttribute("cx", cx);
    clipEllipse.setAttribute("cy", cy);
    clipEllipse.removeAttribute("hidden");

    let dashedEllipse = this.getElement("dashed-ellipse");
    dashedEllipse.setAttribute("rx", rx);
    dashedEllipse.setAttribute("ry", ry);
    dashedEllipse.setAttribute("cx", cx);
    dashedEllipse.setAttribute("cy", cy);
    dashedEllipse.removeAttribute("hidden");
    let markerCoords = [ [cx, cy], [cx + rx, cy], [cx, cy + ry] ];
    this._drawMarkers(markerCoords, width, height, zoom);
  }

  /**
   * Update the SVG rect to fit the CSS inset.
   * @param {Number} width the width of the element quads
   * @param {Number} height the height of the element quads
   * @param {Number} zoom the zoom level of the window
   */
  _updateInsetShape(width, height, zoom) {
    let { top, left, right, bottom } = this.coordinates;
    let rectEl = this.getElement("rect");
    rectEl.setAttribute("x", left);
    rectEl.setAttribute("y", top);
    rectEl.setAttribute("width", 100 - left - right);
    rectEl.setAttribute("height", 100 - top - bottom);
    rectEl.removeAttribute("hidden");

    let clipRect = this.getElement("clip-rect");
    clipRect.setAttribute("x", left);
    clipRect.setAttribute("y", top);
    clipRect.setAttribute("width", 100 - left - right);
    clipRect.setAttribute("height", 100 - top - bottom);
    clipRect.removeAttribute("hidden");

    let dashedRect = this.getElement("dashed-rect");
    dashedRect.setAttribute("x", left);
    dashedRect.setAttribute("y", top);
    dashedRect.setAttribute("width", 100 - left - right);
    dashedRect.setAttribute("height", 100 - top - bottom);
    dashedRect.removeAttribute("hidden");

    let centerX = (left + (100 - right)) / 2;
    let centerY = (top + (100 - bottom)) / 2;
    let markerCoords = [[centerX, top], [100 - right, centerY],
                        [centerX, 100 - bottom], [left, centerY]];
    this._drawMarkers(markerCoords, width, height, zoom);
  }

  /**
   * Draw markers for the given coordinates.
   * @param {Array} coords an array of coordinate arrays, of form [[x, y] ...]
   * @param {Number} width the width of the element markers are being drawn for
   * @param {Number} height the height of the element markers are being drawn for
   * @param {Number} zoom the zoom level of the window
   */
  _drawMarkers(coords, width, height, zoom) {
    let markers = coords.map(([x, y]) => {
      return getCirclePath(BASE_MARKER_SIZE, x, y, width, height, zoom);
    }).join(" ");
    let outline = coords.map(([x, y]) => {
      return getCirclePath(BASE_MARKER_SIZE + 2, x, y, width, height, zoom);
    }).join(" ");

    this.getElement("markers").setAttribute("d", markers);
    this.getElement("markers-outline").setAttribute("d", outline);
  }

  /**
   * Calculate the bounding box of the shape after it is transformed according to
   * the transformation matrix.
   * @returns {Object} of form { nw, ne, sw, se, n, s, w, e, rotatePoint, center }.
   *          Each element in the object is an array of form [x,y], denoting the x/y
   *          coordinates of the given point.
   */
  calculateTransformedBoundingBox() {
    let { minX, minY, maxX, maxY } = this.origBoundingBox;
    let { width, height } = this.currentDimensions;
    let toPixel = scale(width / 100, height / 100);
    let toPercent = scale(100 / width, 100 / height);
    let matrix = multiply(toPercent, multiply(this.transformMatrix, toPixel));
    let centerX = (minX + maxX) / 2;
    let centerY = (minY + maxY) / 2;
    let nw = apply(matrix, [minX, minY]);
    let ne = apply(matrix, [maxX, minY]);
    let sw = apply(matrix, [minX, maxY]);
    let se = apply(matrix, [maxX, maxY]);
    let n = apply(matrix, [centerX, minY]);
    let s = apply(matrix, [centerX, maxY]);
    let w = apply(matrix, [minX, centerY]);
    let e = apply(matrix, [maxX, centerY]);
    let center = apply(matrix, [centerX, centerY]);

    let u = [(ne[0] - nw[0]) / 100 * width, (ne[1] - nw[1]) / 100 * height];
    let v = [(sw[0] - nw[0]) / 100 * width, (sw[1] - nw[1]) / 100 * height];
    let { basis, invertedBasis } = getBasis(u, v);
    let rotatePointMatrix = changeMatrixBase(translate(0, -ROTATE_LINE_LENGTH),
                                             invertedBasis, basis);
    rotatePointMatrix = multiply(toPercent,
                        multiply(rotatePointMatrix,
                        multiply(this.transformMatrix, toPixel)));
    let rotatePoint = apply(rotatePointMatrix, [centerX, centerY]);
    return { nw, ne, sw, se, n, s, w, e, rotatePoint, center };
  }

  /**
   * Hide the highlighter, the outline and the infobar.
   */
  _hide() {
    setIgnoreLayoutChanges(true);

    this._hideShapes();
    this.getElement("markers").setAttribute("d", "");
    this.getElement("root").setAttribute("style", "");

    setIgnoreLayoutChanges(false, this.highlighterEnv.window.document.documentElement);
  }

  onPageHide({ target }) {
    // If a page hide event is triggered for current window's highlighter, hide the
    // highlighter.
    if (target.defaultView === this.win) {
      this.hide();
    }
  }

  /**
   * Get the rough direction of the point relative to the anchor.
   * If the handle is roughly horizontal relative to the anchor, return "ew".
   * If the handle is roughly vertical relative to the anchor, return "ns"
   * If the handle is roughly above/right or below/left, return "nesw"
   * If the handle is roughly above/left or below/right, return "nwse"
   * @param {String} pointName the name of the point being hovered
   * @param {String} anchor the name of the anchor point
   * @returns {String} The rough direction of the point relative to the anchor
   */
  getRoughDirection(pointName, anchor) {
    let scalePoint = pointName.split("-")[1];
    let anchorPos = this.transformedBoundingBox[anchor];
    let scalePos = this.transformedBoundingBox[scalePoint];
    let { minX, minY, maxX, maxY } = this.boundingBox;
    let width = maxX - minX;
    let height = maxY - minY;
    let dx = (scalePos[0] - anchorPos[0]) / width;
    let dy = (scalePos[1] - anchorPos[1]) / height;
    if (dx >= -0.33 && dx <= 0.33) {
      return "ns";
    } else if (dy >= -0.33 && dy <= 0.33) {
      return "ew";
    } else if ((dx > 0.33 && dy < -0.33) || (dx < -0.33 && dy > 0.33)) {
      return "nesw";
    }
    return "nwse";
  }
}

/**
 * Get the "raw" (i.e. non-computed) shape definition on the given node.
 * @param {nsIDOMNode} node the node to analyze
 * @param {String} property the CSS property for which a value should be retrieved.
 * @returns {String} the value of the given CSS property on the given node.
 */
function getDefinedShapeProperties(node, property) {
  let prop = "";
  if (!node) {
    return prop;
  }

  let cssRules = getCSSStyleRules(node);
  for (let i = 0; i < cssRules.length; i++) {
    let rule = cssRules[i];
    let value = rule.style.getPropertyValue(property);
    if (value && value !== "auto") {
      prop = value;
    }
  }

  if (node.style) {
    let value = node.style.getPropertyValue(property);
    if (value && value !== "auto") {
      prop = value;
    }
  }

  return prop.trim();
}

/**
 * Split coordinate pairs separated by a space and return an array.
 * @param {String} coords the coordinate pair, where each coord is separated by a space.
 * @returns {Array} a 2 element array containing the coordinates.
 */
function splitCoords(coords) {
  // All coordinate pairs are of the form "x y" where x and y are values or
  // calc() expressions. calc() expressions have spaces around operators, so
  // replace those spaces with \u00a0 (non-breaking space) so they will not be
  // split later.
  return coords.trim().replace(/ [\+\-\*\/] /g, match => {
    return `\u00a0${match.trim()}\u00a0`;
  }).split(" ");
}
exports.splitCoords = splitCoords;

/**
 * Convert a coordinate to a percentage value.
 * @param {String} coord a single coordinate
 * @param {Number} size the size of the element (width or height) that the percentages
 *        are relative to
 * @returns {Number} the coordinate as a percentage value
 */
function coordToPercent(coord, size) {
  if (coord.includes("%")) {
    // Just remove the % sign, nothing else to do, we're in a viewBox that's 100%
    // worth.
    return parseFloat(coord.replace("%", ""));
  } else if (coord.includes("px")) {
    // Convert the px value to a % value.
    let px = parseFloat(coord.replace("px", ""));
    return px * 100 / size;
  }

  // Unit-less value, so 0.
  return 0;
}
exports.coordToPercent = coordToPercent;

/**
 * Evaluates a CSS calc() expression (only handles addition)
 * @param {String} expression the arguments to the calc() function
 * @param {Number} size the size of the element (width or height) that percentage values
 *        are relative to
 * @returns {Number} the result of the expression as a percentage value
 */
function evalCalcExpression(expression, size) {
  // the calc() values returned by getComputedStyle only have addition, as it
  // computes calc() expressions as much as possible without resolving percentages,
  // leaving only addition.
  let values = expression.split("+").map(v => v.trim());

  return values.reduce((prev, curr) => {
    return prev + coordToPercent(curr, size);
  }, 0);
}
exports.evalCalcExpression = evalCalcExpression;

/**
 * Converts a shape mode to the proper CSS property name.
 * @param {String} mode the mode of the CSS shape
 * @returns the equivalent CSS property name
 */
const shapeModeToCssPropertyName = mode => {
  let property = mode.substring(3);
  return property.substring(0, 1).toLowerCase() + property.substring(1);
};
exports.shapeModeToCssPropertyName = shapeModeToCssPropertyName;

/**
 * Get the SVG path definition for a circle with given attributes.
 * @param {Number} size the radius of the circle in pixels
 * @param {Number} cx the x coordinate of the centre of the circle
 * @param {Number} cy the y coordinate of the centre of the circle
 * @param {Number} width the width of the element the circle is being drawn for
 * @param {Number} height the height of the element the circle is being drawn for
 * @param {Number} zoom the zoom level of the window the circle is drawn in
 * @returns {String} the definition of the circle in SVG path description format.
 */
const getCirclePath = (size, cx, cy, width, height, zoom) => {
  // We use a viewBox of 100x100 for shape-container so it's easy to position things
  // based on their percentage, but this makes it more difficult to create circles.
  // Therefor, 100px is the base size of shape-container. In order to make the markers'
  // size scale properly, we must adjust the radius based on zoom and the width/height of
  // the element being highlighted, then calculate a radius for both x/y axes based
  // on the aspect ratio of the element.
  let radius = size * (100 / Math.max(width, height)) / zoom;
  let ratio = width / height;
  let rx = (ratio > 1) ? radius : radius / ratio;
  let ry = (ratio > 1) ? radius * ratio : radius;
  // a circle is drawn as two arc lines, starting at the leftmost point of the circle.
  return `M${cx - rx},${cy}a${rx},${ry} 0 1,0 ${rx * 2},0` +
         `a${rx},${ry} 0 1,0 ${rx * -2},0`;
};
exports.getCirclePath = getCirclePath;

/**
 * Calculates the object bounding box for a node given its stroke bounding box.
 * @param {Number} top the y coord of the top edge of the stroke bounding box
 * @param {Number} left the x coord of the left edge of the stroke bounding box
 * @param {Number} width the width of the stroke bounding box
 * @param {Number} height the height of the stroke bounding box
 * @param {Object} node the node object
 * @returns {Object} an object of the form { top, left, width, height }, which
 *          are the top/left/width/height of the object bounding box for the node.
 */
const getObjectBoundingBox = (top, left, width, height, node) => {
  // See https://drafts.fxtf.org/css-masking-1/#stroke-bounding-box for details
  // on this algorithm. Note that we intentionally do not check "stroke-linecap".
  let strokeWidth = parseFloat(getComputedStyle(node).strokeWidth);
  let delta = strokeWidth / 2;
  let tagName = node.tagName;

  if (tagName !== "rect" && tagName !== "ellipse"
      && tagName !== "circle" && tagName !== "image") {
    if (getComputedStyle(node).strokeLinejoin === "miter") {
      let miter = getComputedStyle(node).strokeMiterlimit;
      if (miter < Math.SQRT2) {
        delta *= Math.SQRT2;
      } else {
        delta *= miter;
      }
    } else {
      delta *= Math.SQRT2;
    }
  }

  return {
    top: top + delta,
    left: left + delta,
    width: width - 2 * delta,
    height: height - 2 * delta
  };
};

/**
 * Get the unit (e.g. px, %, em) for the given point value.
 * @param {any} point a point value for which a unit should be retrieved.
 * @returns {String} the unit.
 */
const getUnit = (point) => {
  // If the point has no unit, default to px.
  if (isUnitless(point)) {
    return "px";
  }
  let [unit] = point.match(/[^\d]+$/) || ["px"];
  return unit;
};
exports.getUnit = getUnit;

/**
 * Check if the given point value has a unit.
 * @param {any} point a point value.
 * @returns {Boolean} whether the given value has a unit.
 */
const isUnitless = (point) => {
  // We treat all values that evaluate to 0 as unitless, regardless of whether
  // they originally had a unit.
  return !point ||
         !point.match(/[^\d]+$/) ||
         parseFloat(point) === 0 ||
         point.includes("(") ||
         point === "closest-side" ||
         point === "farthest-side";
};

/**
 * Return the anchor corresponding to the given scale type.
 * @param {String} type a scale type, of form "scale-[direction]"
 * @returns {String} a string describing the anchor, one of the 8 cardinal directions.
 */
const getAnchorPoint = (type) => {
  let anchor = type.split("-")[1];
  if (anchor.includes("n")) {
    anchor = anchor.replace("n", "s");
  } else if (anchor.includes("s")) {
    anchor = anchor.replace("s", "n");
  }
  if (anchor.includes("w")) {
    anchor = anchor.replace("w", "e");
  } else if (anchor.includes("e")) {
    anchor = anchor.replace("e", "w");
  }

  if (anchor === "e" || anchor === "w") {
    anchor = "n" + anchor;
  } else if (anchor === "n" || anchor === "s") {
    anchor = anchor + "w";
  }

  return anchor;
};

/**
* Get the decimal point precision for values depending on unit type.
* Used as argument for `toFixed()` on coordinate values when:
* - transforming shapes
* - inserting new points on a polygon.
* Only handle pixels and falsy values for now. Round them to the nearest integer value.
* All other unit types round to two decimal points.
*
* @param {String|undefined} unitType any one of the accepted CSS unit types for position.
* @return {Number} decimal precision when rounding a value
*/
function getDecimalPrecision(unitType) {
  switch (unitType) {
    case "px":
    case "":
    case undefined:
      return 0;
    default:
      return 2;
  }
}
exports.getDecimalPrecision = getDecimalPrecision;

exports.ShapesHighlighter = ShapesHighlighter;
PK
!<ƞ��Mchrome/devtools/modules/devtools/server/actors/highlighters/simple-outline.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  isNodeValid,
  addPseudoClassLock,
  removePseudoClassLock
} = require("./utils/markup");

const { loadSheet } = require("devtools/shared/layout/utils");

// SimpleOutlineHighlighter's stylesheet
const HIGHLIGHTED_PSEUDO_CLASS = ":-moz-devtools-highlighted";
const SIMPLE_OUTLINE_SHEET = "data:text/css;charset=utf-8," + encodeURIComponent(`
  .__fx-devtools-hide-shortcut__ {
    visibility: hidden !important
  }
  ${HIGHLIGHTED_PSEUDO_CLASS} {
    outline: 2px dashed #F06!important;
    outline-offset: -2px!important
  }`);

/**
 * The SimpleOutlineHighlighter is a class that has the same API than the
 * BoxModelHighlighter, but adds a pseudo-class on the target element itself
 * to draw a simple css outline around the element.
 * It is used by the HighlighterActor when canvasframe-based highlighters can't
 * be used. This is the case for XUL windows.
 */
function SimpleOutlineHighlighter(highlighterEnv) {
  this.chromeDoc = highlighterEnv.document;
}

SimpleOutlineHighlighter.prototype = {
  /**
   * Destroy the nodes. Remove listeners.
   */
  destroy: function () {
    this.hide();
    this.chromeDoc = null;
  },

  /**
   * Show the highlighter on a given node
   * @param {DOMNode} node
   */
  show: function (node) {
    if (isNodeValid(node) && (!this.currentNode || node !== this.currentNode)) {
      this.hide();
      this.currentNode = node;
      loadSheet(node.ownerGlobal, SIMPLE_OUTLINE_SHEET);
      addPseudoClassLock(node, HIGHLIGHTED_PSEUDO_CLASS);
    }
    return true;
  },

  /**
   * Hide the highlighter, the outline and the infobar.
   */
  hide: function () {
    if (this.currentNode) {
      removePseudoClassLock(this.currentNode, HIGHLIGHTED_PSEUDO_CLASS);
      this.currentNode = null;
    }
  }
};
exports.SimpleOutlineHighlighter = SimpleOutlineHighlighter;
PK
!<�#�L��Rchrome/devtools/modules/devtools/server/actors/highlighters/utils/accessibility.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { getCurrentZoom } = require("devtools/shared/layout/utils");

/**
 * A helper function that calculate accessible object bounds and positioning to
 * be used for highlighting.
 *
 * @param  {Object} win
 *         window that contains accessible object.
 * @param  {Object} options
 *         Object used for passing options:
 *         - {Number} x
 *           x coordinate of the top left corner of the accessible object
 *         - {Number} y
 *           y coordinate of the top left corner of the accessible object
 *         - {Number} w
 *           width of the the accessible object
 *         - {Number} h
 *           height of the the accessible object
 * @return {Object|null} Returns, if available, positioning and bounds information for
 *                 the accessible object.
 */
function getBounds(win, { x, y, w, h }) {
  let { devicePixelRatio, mozInnerScreenX, mozInnerScreenY, scrollX, scrollY } = win;
  let zoom = getCurrentZoom(win);
  let left = x, right = x + w, top = y, bottom = y + h;

  left -= (mozInnerScreenX - scrollX) * devicePixelRatio;
  right -= (mozInnerScreenX - scrollX) * devicePixelRatio;
  top -= (mozInnerScreenY - scrollY) * devicePixelRatio;
  bottom -= (mozInnerScreenY - scrollY) * devicePixelRatio;

  left *= zoom / devicePixelRatio;
  right *= zoom / devicePixelRatio;
  top *= zoom / devicePixelRatio;
  bottom *= zoom / devicePixelRatio;

  let width = right - left;
  let height = bottom - top;

  return { left, right, top, bottom, width, height };
}

exports.getBounds = getBounds;
PK
!<)�G�E�EKchrome/devtools/modules/devtools/server/actors/highlighters/utils/canvas.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  apply,
  getNodeTransformationMatrix,
  getWritingModeMatrix,
  identity,
  isIdentity,
  multiply,
  scale,
  translate,
} = require("devtools/shared/layout/dom-matrix-2d");
const { getViewportDimensions } = require("devtools/shared/layout/utils");
const { getComputedStyle } = require("./markup");

// A set of utility functions for highlighters that render their content to a <canvas>
// element.

// We create a <canvas> element that has always 4096x4096 physical pixels, to displays
// our grid's overlay.
// Then, we move the element around when needed, to give the perception that it always
// covers the screen (See bug 1345434).
//
// This canvas size value is the safest we can use because most GPUs can handle it.
// It's also far from the maximum canvas memory allocation limit (4096x4096x4 is
// 67.108.864 bytes, where the limit is 500.000.000 bytes, see:
// http://searchfox.org/mozilla-central/source/gfx/thebes/gfxPrefs.h#401).
//
// Note:
// Once bug 1232491 lands, we could try to refactor this code to use the values from
// the displayport API instead.
//
// Using a fixed value should also solve bug 1348293.
const CANVAS_SIZE = 4096;

// The default color used for the canvas' font, fill and stroke colors.
const DEFAULT_COLOR = "#9400FF";

/**
 * Draws a rect to the context given and applies a transformation matrix if passed.
 * The coordinates are the start and end points of the rectangle's diagonal.
 *
 * @param  {CanvasRenderingContext2D} ctx
 *         The 2D canvas context.
 * @param  {Number} x1
 *         The x-axis coordinate of the rectangle's diagonal start point.
 * @param  {Number} y1
 *         The y-axis coordinate of the rectangle's diagonal start point.
 * @param  {Number} x2
 *         The x-axis coordinate of the rectangle's diagonal end point.
 * @param  {Number} y2
 *         The y-axis coordinate of the rectangle's diagonal end point.
 * @param  {Array} [matrix=identity()]
 *         The transformation matrix to apply.
 */
function clearRect(ctx, x1, y1, x2, y2, matrix = identity()) {
  let p = getPointsFromDiagonal(x1, y1, x2, y2, matrix);
  ctx.clearRect(p[0].x, p[0].y, p[1].x - p[0].x, p[3].y - p[0].y);
}

/**
 * Draws an arrow-bubble rectangle in the provided canvas context.
 *
 * @param  {CanvasRenderingContext2D} ctx
 *         The 2D canvas context.
 * @param  {Number} x
 *         The x-axis origin of the rectangle.
 * @param  {Number} y
 *         The y-axis origin of the rectangle.
 * @param  {Number} width
 *         The width of the rectangle.
 * @param  {Number} height
 *         The height of the rectangle.
 * @param  {Number} radius
 *         The radius of the rounding.
 * @param  {Number} margin
 *         The distance of the origin point from the pointer.
 * @param  {Number} arrowSize
 *         The size of the arrow.
 * @param  {String} alignment
 *         The alignment of the rectangle in relation to its position to the grid.
 */
function drawBubbleRect(ctx, x, y, width, height, radius, margin, arrowSize, alignment) {
  let angle = 0;

  if (alignment === "bottom") {
    angle = 180;
  } else if (alignment === "right") {
    angle = 90;
    [width, height] = [height, width];
  } else if (alignment === "left") {
    [width, height] = [height, width];
    angle = 270;
  }

  let originX = x;
  let originY = y;

  ctx.save();
  ctx.translate(originX, originY);
  ctx.rotate(angle * (Math.PI / 180));
  ctx.translate(-originX, -originY);
  ctx.translate(-width / 2, -height - arrowSize - margin);

  ctx.beginPath();
  ctx.moveTo(x, y + radius);
  ctx.lineTo(x, y + height - radius);
  ctx.arcTo(x, y + height, x + radius, y + height, radius);
  ctx.lineTo(x + width / 2 - arrowSize, y + height);
  ctx.lineTo(x + width / 2, y + height + arrowSize);
  ctx.lineTo(x + width / 2 + arrowSize, y + height);
  ctx.arcTo(x + width, y + height, x + width, y + height - radius, radius);
  ctx.lineTo(x + width, y + radius);
  ctx.arcTo(x + width, y, x + width - radius, y, radius);
  ctx.lineTo(x + radius, y);
  ctx.arcTo(x, y, x, y + radius, radius);

  ctx.stroke();
  ctx.fill();

  ctx.restore();
}

/**
 * Draws a line to the context given and applies a transformation matrix if passed.
 *
 * @param  {CanvasRenderingContext2D} ctx
 *         The 2D canvas context.
 * @param  {Number} x1
 *         The x-axis of the coordinate for the begin of the line.
 * @param  {Number} y1
 *         The y-axis of the coordinate for the begin of the line.
 * @param  {Number} x2
 *         The x-axis of the coordinate for the end of the line.
 * @param  {Number} y2
 *         The y-axis of the coordinate for the end of the line.
 * @param  {Object} [options]
 *         The options object.
 * @param  {Array} [options.matrix=identity()]
 *         The transformation matrix to apply.
 * @param  {Array} [options.extendToBoundaries]
 *         If set, the line will be extended to reach the boundaries specified.
 */
function drawLine(ctx, x1, y1, x2, y2, options) {
  let matrix = options.matrix || identity();

  let p1 = apply(matrix, [x1, y1]);
  let p2 = apply(matrix, [x2, y2]);

  x1 = p1[0];
  y1 = p1[1];
  x2 = p2[0];
  y2 = p2[1];

  if (options.extendToBoundaries) {
    if (p1[1] === p2[1]) {
      x1 = options.extendToBoundaries[0];
      x2 = options.extendToBoundaries[2];
    } else {
      y1 = options.extendToBoundaries[1];
      x1 = (p2[0] - p1[0]) * (y1 - p1[1]) / (p2[1] - p1[1]) + p1[0];
      y2 = options.extendToBoundaries[3];
      x2 = (p2[0] - p1[0]) * (y2 - p1[1]) / (p2[1] - p1[1]) + p1[0];
    }
  }

  ctx.beginPath();
  ctx.moveTo(Math.round(x1), Math.round(y1));
  ctx.lineTo(Math.round(x2), Math.round(y2));
}

/**
 * Draws a rect to the context given and applies a transformation matrix if passed.
 * The coordinates are the start and end points of the rectangle's diagonal.
 *
 * @param  {CanvasRenderingContext2D} ctx
 *         The 2D canvas context.
 * @param  {Number} x1
 *         The x-axis coordinate of the rectangle's diagonal start point.
 * @param  {Number} y1
 *         The y-axis coordinate of the rectangle's diagonal start point.
 * @param  {Number} x2
 *         The x-axis coordinate of the rectangle's diagonal end point.
 * @param  {Number} y2
 *         The y-axis coordinate of the rectangle's diagonal end point.
 * @param  {Array} [matrix=identity()]
 *         The transformation matrix to apply.
 */
function drawRect(ctx, x1, y1, x2, y2, matrix = identity()) {
  let p = getPointsFromDiagonal(x1, y1, x2, y2, matrix);

  ctx.beginPath();
  ctx.moveTo(Math.round(p[0].x), Math.round(p[0].y));
  ctx.lineTo(Math.round(p[1].x), Math.round(p[1].y));
  ctx.lineTo(Math.round(p[2].x), Math.round(p[2].y));
  ctx.lineTo(Math.round(p[3].x), Math.round(p[3].y));
  ctx.closePath();
}

/**
 * Draws a rounded rectangle in the provided canvas context.
 *
 * @param  {CanvasRenderingContext2D} ctx
 *         The 2D canvas context.
 * @param  {Number} x
 *         The x-axis origin of the rectangle.
 * @param  {Number} y
 *         The y-axis origin of the rectangle.
 * @param  {Number} width
 *         The width of the rectangle.
 * @param  {Number} height
 *         The height of the rectangle.
 * @param  {Number} radius
 *         The radius of the rounding.
 */
function drawRoundedRect(ctx, x, y, width, height, radius) {
  ctx.beginPath();
  ctx.moveTo(x, y + radius);
  ctx.lineTo(x, y + height - radius);
  ctx.arcTo(x, y + height, x + radius, y + height, radius);
  ctx.lineTo(x + width - radius, y + height);
  ctx.arcTo(x + width, y + height, x + width, y + height - radius, radius);
  ctx.lineTo(x + width, y + radius);
  ctx.arcTo(x + width, y, x + width - radius, y, radius);
  ctx.lineTo(x + radius, y);
  ctx.arcTo(x, y, x, y + radius, radius);
  ctx.stroke();
  ctx.fill();
}

/**
 * Given an array of four points and returns a DOMRect-like object representing the
 * boundaries defined by the four points.
 *
 * @param  {Array} points
 *         An array with 4 pointer objects {x, y} representing the box quads.
 * @return {Object} DOMRect-like object of the 4 points.
 */
function getBoundsFromPoints(points) {
  let bounds = {};

  bounds.left = Math.min(points[0].x, points[1].x, points[2].x, points[3].x);
  bounds.right = Math.max(points[0].x, points[1].x, points[2].x, points[3].x);
  bounds.top = Math.min(points[0].y, points[1].y, points[2].y, points[3].y);
  bounds.bottom = Math.max(points[0].y, points[1].y, points[2].y, points[3].y);

  bounds.x = bounds.left;
  bounds.y = bounds.top;
  bounds.width = bounds.right - bounds.left;
  bounds.height = bounds.bottom - bounds.top;

  return bounds;
}

/**
 * Returns the current matrices for both canvas drawing and SVG taking into account the
 * following transformations, in this order:
 *   1. The scale given by the display pixel ratio.
 *   2. The translation to the top left corner of the element.
 *   3. The scale given by the current zoom.
 *   4. The translation given by the top and left padding of the element.
 *   5. Any CSS transformation applied directly to the element (only 2D
 *      transformation; the 3D transformation are flattened, see `dom-matrix-2d` module
 *      for further details.)
 *   6. Rotate, translate, and reflect as needed to match the writing mode and text
 *      direction of the element.
 *
 *  The transformations of the element's ancestors are not currently computed (see
 *  bug 1355675).
 *
 * @param  {Element} element
 *         The current element.
 * @param  {Window} window
 *         The window object.
 * @return {Object} An object with the following properties:
 *         - {Array} currentMatrix
 *           The current matrix.
 *         - {Boolean} hasNodeTransformations
 *           true if the node has transformed and false otherwise.
 */
function getCurrentMatrix(element, window) {
  let computedStyle = getComputedStyle(element);

  let paddingTop = parseFloat(computedStyle.paddingTop);
  let paddingLeft = parseFloat(computedStyle.paddingLeft);
  let borderTop = parseFloat(computedStyle.borderTopWidth);
  let borderLeft = parseFloat(computedStyle.borderLeftWidth);

  let nodeMatrix = getNodeTransformationMatrix(element, window.document.documentElement);

  let currentMatrix = identity();
  let hasNodeTransformations = false;

  // Scale based on the device pixel ratio.
  currentMatrix = multiply(currentMatrix, scale(window.devicePixelRatio));

  // Apply the current node's transformation matrix, relative to the inspected window's
  // root element, but only if it's not a identity matrix.
  if (isIdentity(nodeMatrix)) {
    hasNodeTransformations = false;
  } else {
    currentMatrix = multiply(currentMatrix, nodeMatrix);
    hasNodeTransformations = true;
  }

  // Translate the origin based on the node's padding and border values.
  currentMatrix = multiply(currentMatrix,
    translate(paddingLeft + borderLeft, paddingTop + borderTop));

  // Adjust as needed to match the writing mode and text direction of the element.
  let size = {
    width: element.offsetWidth,
    height: element.offsetHeight,
  };
  let writingModeMatrix = getWritingModeMatrix(size, computedStyle);
  if (!isIdentity(writingModeMatrix)) {
    currentMatrix = multiply(currentMatrix, writingModeMatrix);
  }

  return { currentMatrix, hasNodeTransformations };
}

/**
 * Given an array of four points, returns a string represent a path description.
 *
 * @param  {Array} points
 *         An array with 4 pointer objects {x, y} representing the box quads.
 * @return {String} a Path Description that can be used in svg's <path> element.
 */
function getPathDescriptionFromPoints(points) {
  return "M" + points[0].x + "," + points[0].y + " " +
         "L" + points[1].x + "," + points[1].y + " " +
         "L" + points[2].x + "," + points[2].y + " " +
         "L" + points[3].x + "," + points[3].y;
}

/**
 * Given the rectangle's diagonal start and end coordinates, returns an array containing
 * the four coordinates of a rectangle. If a matrix is provided, applies the matrix
 * function to each of the coordinates' value.
 *
 * @param  {Number} x1
 *         The x-axis coordinate of the rectangle's diagonal start point.
 * @param  {Number} y1
 *         The y-axis coordinate of the rectangle's diagonal start point.
 * @param  {Number} x2
 *         The x-axis coordinate of the rectangle's diagonal end point.
 * @param  {Number} y2
 *         The y-axis coordinate of the rectangle's diagonal end point.
 * @param  {Array} [matrix=identity()]
 *         A transformation matrix to apply.
 * @return {Array} the four coordinate points of the given rectangle transformed by the
 * matrix given.
 */
function getPointsFromDiagonal(x1, y1, x2, y2, matrix = identity()) {
  return [
    [x1, y1],
    [x2, y1],
    [x2, y2],
    [x1, y2]
  ].map(point => {
    let transformedPoint = apply(matrix, point);

    return { x: transformedPoint[0], y: transformedPoint[1] };
  });
}

/**
 * Updates the <canvas> element's style in accordance with the current window's
 * device pixel ratio, and the position calculated in `getCanvasPosition`. It also
 * clears the drawing context. This is called on canvas update after a scroll event where
 * `getCanvasPosition` updates the new canvasPosition.
 *
 * @param  {Canvas} canvas
 *         The <canvas> element.
 * @param  {Object} canvasPosition
 *         A pointer object {x, y} representing the <canvas> position to the top left
 *         corner of the page.
 * @param  {Number} devicePixelRatio
 *         The device pixel ratio.
 */
function updateCanvasElement(canvas, canvasPosition, devicePixelRatio) {
  let { x, y } = canvasPosition;
  let size = CANVAS_SIZE / devicePixelRatio;

  // Resize the canvas taking the dpr into account so as to have crisp lines, and
  // translating it to give the perception that it always covers the viewport.
  canvas.setAttribute("style",
    `width: ${size}px; height: ${size}px; transform: translate(${x}px, ${y}px);`);
  canvas.getCanvasContext("2d").clearRect(0, 0, CANVAS_SIZE, CANVAS_SIZE);
}

/**
 * Calculates and returns the <canvas>'s position in accordance with the page's scroll,
 * document's size, canvas size, and viewport's size. This is called when a page's scroll
 * is detected.
 *
 * @param  {Object} canvasPosition
 *         A pointer object {x, y} representing the <canvas> position to the top left
 *         corner of the page.
 * @param  {Object} scrollPosition
 *         A pointer object {x, y} representing the window's pageXOffset and pageYOffset.
 * @param  {Window} window
 *         The window object.
 * @param  {Object} windowDimensions
 *         An object {width, height} representing the window's dimensions for the
 *         `window` given.
 * @return {Boolean} true if the <canvas> position was updated and false otherwise.
 */
function updateCanvasPosition(canvasPosition, scrollPosition, window, windowDimensions) {
  let { x: canvasX, y: canvasY } = canvasPosition;
  let { x: scrollX, y: scrollY } = scrollPosition;
  let cssCanvasSize = CANVAS_SIZE / window.devicePixelRatio;
  let viewportSize = getViewportDimensions(window);
  let { height, width } = windowDimensions;
  let canvasWidth = cssCanvasSize;
  let canvasHeight = cssCanvasSize;
  let hasUpdated = false;

  // Those values indicates the relative horizontal and vertical space the page can
  // scroll before we have to reposition the <canvas>; they're 1/4 of the delta between
  // the canvas' size and the viewport's size: that's because we want to consider both
  // sides (top/bottom, left/right; so 1/2 for each side) and also we don't want to
  // shown the edges of the canvas in case of fast scrolling (to avoid showing undraw
  // areas, therefore another 1/2 here).
  let bufferSizeX = (canvasWidth - viewportSize.width) >> 2;
  let bufferSizeY = (canvasHeight - viewportSize.height) >> 2;

  // Defines the boundaries for the canvas.
  let leftBoundary = 0;
  let rightBoundary = width - canvasWidth;
  let topBoundary = 0;
  let bottomBoundary = height - canvasHeight;

  // Defines the thresholds that triggers the canvas' position to be updated.
  let leftThreshold = scrollX - bufferSizeX;
  let rightThreshold = scrollX - canvasWidth + viewportSize.width + bufferSizeX;
  let topThreshold = scrollY - bufferSizeY;
  let bottomThreshold = scrollY - canvasHeight + viewportSize.height + bufferSizeY;

  if (canvasX < rightBoundary && canvasX < rightThreshold) {
    canvasX = Math.min(leftThreshold, rightBoundary);
    hasUpdated = true;
  } else if (canvasX > leftBoundary && canvasX > leftThreshold) {
    canvasX = Math.max(rightThreshold, leftBoundary);
    hasUpdated = true;
  }

  if (canvasY < bottomBoundary && canvasY < bottomThreshold) {
    canvasY = Math.min(topThreshold, bottomBoundary);
    hasUpdated = true;
  } else if (canvasY > topBoundary && canvasY > topThreshold) {
    canvasY = Math.max(bottomThreshold, topBoundary);
    hasUpdated = true;
  }

  // Update the canvas position with the calculated canvasX and canvasY positions.
  canvasPosition.x = canvasX;
  canvasPosition.y = canvasY;

  return hasUpdated;
}

exports.CANVAS_SIZE = CANVAS_SIZE;
exports.DEFAULT_COLOR = DEFAULT_COLOR;
exports.clearRect = clearRect;
exports.drawBubbleRect = drawBubbleRect;
exports.drawLine = drawLine;
exports.drawRect = drawRect;
exports.drawRoundedRect = drawRoundedRect;
exports.getBoundsFromPoints = getBoundsFromPoints;
exports.getCurrentMatrix = getCurrentMatrix;
exports.getPathDescriptionFromPoints = getPathDescriptionFromPoints;
exports.getPointsFromDiagonal = getPointsFromDiagonal;
exports.updateCanvasElement = updateCanvasElement;
exports.updateCanvasPosition = updateCanvasPosition;
PK
!<\��"4]4]Kchrome/devtools/modules/devtools/server/actors/highlighters/utils/markup.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Ci, Cu, Cr } = require("chrome");
const { getCurrentZoom, getWindowDimensions, getViewportDimensions,
  getRootBindingParent, loadSheet } = require("devtools/shared/layout/utils");
const EventEmitter = require("devtools/shared/event-emitter");
const InspectorUtils = require("InspectorUtils");

const lazyContainer = {};

loader.lazyRequireGetter(lazyContainer, "CssLogic",
  "devtools/server/css-logic", true);
exports.getComputedStyle = (node) =>
  lazyContainer.CssLogic.getComputedStyle(node);

exports.getBindingElementAndPseudo = (node) =>
  lazyContainer.CssLogic.getBindingElementAndPseudo(node);

exports.hasPseudoClassLock = (...args) =>
  InspectorUtils.hasPseudoClassLock(...args);

exports.addPseudoClassLock = (...args) =>
  InspectorUtils.addPseudoClassLock(...args);

exports.removePseudoClassLock = (...args) =>
  InspectorUtils.removePseudoClassLock(...args);

const SVG_NS = "http://www.w3.org/2000/svg";
const XHTML_NS = "http://www.w3.org/1999/xhtml";
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const STYLESHEET_URI = "resource://devtools/server/actors/" +
                       "highlighters.css";

const _tokens = Symbol("classList/tokens");

/**
 * Shims the element's `classList` for anonymous content elements; used
 * internally by `CanvasFrameAnonymousContentHelper.getElement()` method.
 */
function ClassList(className) {
  let trimmed = (className || "").trim();
  this[_tokens] = trimmed ? trimmed.split(/\s+/) : [];
}

ClassList.prototype = {
  item(index) {
    return this[_tokens][index];
  },
  contains(token) {
    return this[_tokens].includes(token);
  },
  add(token) {
    if (!this.contains(token)) {
      this[_tokens].push(token);
    }
    EventEmitter.emit(this, "update");
  },
  remove(token) {
    let index = this[_tokens].indexOf(token);

    if (index > -1) {
      this[_tokens].splice(index, 1);
    }
    EventEmitter.emit(this, "update");
  },
  toggle(token) {
    if (this.contains(token)) {
      this.remove(token);
    } else {
      this.add(token);
    }
  },
  get length() {
    return this[_tokens].length;
  },
  [Symbol.iterator]: function* () {
    for (let i = 0; i < this.tokens.length; i++) {
      yield this[_tokens][i];
    }
  },
  toString() {
    return this[_tokens].join(" ");
  }
};

/**
 * Is this content window a XUL window?
 * @param {Window} window
 * @return {Boolean}
 */
function isXUL(window) {
  return window.document.documentElement.namespaceURI === XUL_NS;
}
exports.isXUL = isXUL;

/**
 * Returns true if a DOM node is "valid", where "valid" means that the node isn't a dead
 * object wrapper, is still attached to a document, and is of a given type.
 * @param {DOMNode} node
 * @param {Number} nodeType Optional, defaults to ELEMENT_NODE
 * @return {Boolean}
 */
function isNodeValid(node, nodeType = Ci.nsIDOMNode.ELEMENT_NODE) {
  // Is it still alive?
  if (!node || Cu.isDeadWrapper(node)) {
    return false;
  }

  // Is it of the right type?
  if (node.nodeType !== nodeType) {
    return false;
  }

  // Is its document accessible?
  let doc = node.ownerDocument;
  if (!doc || !doc.defaultView) {
    return false;
  }

  // Is the node connected to the document? Using getBindingParent adds
  // support for anonymous elements generated by a node in the document.
  let bindingParent = getRootBindingParent(node);
  if (!doc.documentElement.contains(bindingParent)) {
    return false;
  }

  return true;
}
exports.isNodeValid = isNodeValid;

/**
 * Helper function that creates SVG DOM nodes.
 * @param {Window} This window's document will be used to create the element
 * @param {Object} Options for the node include:
 * - nodeType: the type of node, defaults to "box".
 * - attributes: a {name:value} object to be used as attributes for the node.
 * - prefix: a string that will be used to prefix the values of the id and class
 *   attributes.
 * - parent: if provided, the newly created element will be appended to this
 *   node.
 */
function createSVGNode(win, options) {
  if (!options.nodeType) {
    options.nodeType = "box";
  }
  options.namespace = SVG_NS;
  return createNode(win, options);
}
exports.createSVGNode = createSVGNode;

/**
 * Helper function that creates DOM nodes.
 * @param {Window} This window's document will be used to create the element
 * @param {Object} Options for the node include:
 * - nodeType: the type of node, defaults to "div".
 * - namespace: the namespace to use to create the node, defaults to XHTML namespace.
 * - attributes: a {name:value} object to be used as attributes for the node.
 * - prefix: a string that will be used to prefix the values of the id and class
 *   attributes.
 * - parent: if provided, the newly created element will be appended to this
 *   node.
 */
function createNode(win, options) {
  let type = options.nodeType || "div";
  let namespace = options.namespace || XHTML_NS;

  let node = win.document.createElementNS(namespace, type);

  for (let name in options.attributes || {}) {
    let value = options.attributes[name];
    if (options.prefix && (name === "class" || name === "id")) {
      value = options.prefix + value;
    }
    node.setAttribute(name, value);
  }

  if (options.parent) {
    options.parent.appendChild(node);
  }

  return node;
}
exports.createNode = createNode;

/**
 * Every highlighters should insert their markup content into the document's
 * canvasFrame anonymous content container (see dom/webidl/Document.webidl).
 *
 * Since this container gets cleared when the document navigates, highlighters
 * should use this helper to have their markup content automatically re-inserted
 * in the new document.
 *
 * Since the markup content is inserted in the canvasFrame using
 * insertAnonymousContent, this means that it can be modified using the API
 * described in AnonymousContent.webidl.
 * To retrieve the AnonymousContent instance, use the content getter.
 *
 * @param {HighlighterEnv} highlighterEnv
 *        The environemnt which windows will be used to insert the node.
 * @param {Function} nodeBuilder
 *        A function that, when executed, returns a DOM node to be inserted into
 *        the canvasFrame.
 */
function CanvasFrameAnonymousContentHelper(highlighterEnv, nodeBuilder) {
  this.highlighterEnv = highlighterEnv;
  this.nodeBuilder = nodeBuilder;
  this.anonymousContentDocument = this.highlighterEnv.document;
  // XXX the next line is a wallpaper for bug 1123362.
  this.anonymousContentGlobal = Cu.getGlobalForObject(
                                this.anonymousContentDocument);

  // Only try to create the highlighter when the document is loaded,
  // otherwise, wait for the window-ready event to fire.
  let doc = this.highlighterEnv.document;
  if (doc.documentElement && doc.readyState != "uninitialized") {
    this._insert();
  }

  this._onWindowReady = this._onWindowReady.bind(this);
  this.highlighterEnv.on("window-ready", this._onWindowReady);

  this.listeners = new Map();
  this.elements = new Map();
}

CanvasFrameAnonymousContentHelper.prototype = {
  destroy() {
    this._remove();
    this.highlighterEnv.off("window-ready", this._onWindowReady);
    this.highlighterEnv = this.nodeBuilder = this._content = null;
    this.anonymousContentDocument = null;
    this.anonymousContentGlobal = null;

    this._removeAllListeners();
    this.elements.clear();
  },

  _insert() {
    let doc = this.highlighterEnv.document;
    // Wait for DOMContentLoaded before injecting the anonymous content.
    if (doc.readyState != "interactive" && doc.readyState != "complete") {
      doc.addEventListener("DOMContentLoaded", this._insert.bind(this),
                           { once: true });
      return;
    }
    // Reject XUL documents. Check that after DOMContentLoaded as we query
    // documentElement which is only available after this event.
    if (isXUL(this.highlighterEnv.window)) {
      return;
    }

    // For now highlighters.css is injected in content as a ua sheet because
    // we no longer support scoped style sheets (see bug 1345702).
    // If it did, highlighters.css would be injected as an anonymous content
    // node using CanvasFrameAnonymousContentHelper instead.
    loadSheet(this.highlighterEnv.window, STYLESHEET_URI);

    let node = this.nodeBuilder();

    // It was stated that hidden documents don't accept
    // `insertAnonymousContent` calls yet. That doesn't seems the case anymore,
    // at least on desktop. Therefore, removing the code that was dealing with
    // that scenario, fixes when we're adding anonymous content in a tab that
    // is not the active one (see bug 1260043 and bug 1260044)
    try {
      this._content = doc.insertAnonymousContent(node);
    } catch (e) {
      // If the `insertAnonymousContent` fails throwing a `NS_ERROR_UNEXPECTED`, it means
      // we don't have access to a `CustomContentContainer` yet (see bug 1365075).
      // At this point, it could only happen on document's interactive state, and we
      // need to wait until the `complete` state before inserting the anonymous content
      // again.
      if (e.result === Cr.NS_ERROR_UNEXPECTED && doc.readyState === "interactive") {
        // The next state change will be "complete" since the current is "interactive"
        doc.addEventListener("readystatechange", () => {
          this._content = doc.insertAnonymousContent(node);
        }, { once: true });
      } else {
        throw e;
      }
    }
  },

  _remove() {
    try {
      let doc = this.anonymousContentDocument;
      doc.removeAnonymousContent(this._content);
    } catch (e) {
      // If the current window isn't the one the content was inserted into, this
      // will fail, but that's fine.
    }
  },

  /**
   * The "window-ready" event can be triggered when:
   *   - a new window is created
   *   - a window is unfrozen from bfcache
   *   - when first attaching to a page
   *   - when swapping frame loaders (moving tabs, toggling RDM)
   */
  _onWindowReady({isTopLevel}) {
    if (isTopLevel) {
      this._removeAllListeners();
      this.elements.clear();
      this._insert();
      this.anonymousContentDocument = this.highlighterEnv.document;
    }
  },

  getComputedStylePropertyValue(id, property) {
    return this.content && this.content.getComputedStylePropertyValue(id, property);
  },

  getTextContentForElement(id) {
    return this.content && this.content.getTextContentForElement(id);
  },

  setTextContentForElement(id, text) {
    if (this.content) {
      this.content.setTextContentForElement(id, text);
    }
  },

  setAttributeForElement(id, name, value) {
    if (this.content) {
      this.content.setAttributeForElement(id, name, value);
    }
  },

  getAttributeForElement(id, name) {
    return this.content && this.content.getAttributeForElement(id, name);
  },

  removeAttributeForElement(id, name) {
    if (this.content) {
      this.content.removeAttributeForElement(id, name);
    }
  },

  hasAttributeForElement(id, name) {
    return typeof this.getAttributeForElement(id, name) === "string";
  },

  getCanvasContext(id, type = "2d") {
    return this.content && this.content.getCanvasContext(id, type);
  },

  /**
   * Add an event listener to one of the elements inserted in the canvasFrame
   * native anonymous container.
   * Like other methods in this helper, this requires the ID of the element to
   * be passed in.
   *
   * Note that if the content page navigates, the event listeners won't be
   * added again.
   *
   * Also note that unlike traditional DOM events, the events handled by
   * listeners added here will propagate through the document only through
   * bubbling phase, so the useCapture parameter isn't supported.
   * It is possible however to call e.stopPropagation() to stop the bubbling.
   *
   * IMPORTANT: the chrome-only canvasFrame insertion API takes great care of
   * not leaking references to inserted elements to chrome JS code. That's
   * because otherwise, chrome JS code could freely modify native anon elements
   * inside the canvasFrame and probably change things that are assumed not to
   * change by the C++ code managing this frame.
   * See https://wiki.mozilla.org/DevTools/Highlighter#The_AnonymousContent_API
   * Unfortunately, the inserted nodes are still available via
   * event.originalTarget, and that's what the event handler here uses to check
   * that the event actually occured on the right element, but that also means
   * consumers of this code would be able to access the inserted elements.
   * Therefore, the originalTarget property will be nullified before the event
   * is passed to your handler.
   *
   * IMPL DETAIL: A single event listener is added per event types only, at
   * browser level and if the event originalTarget is found to have the provided
   * ID, the callback is executed (and then IDs of parent nodes of the
   * originalTarget are checked too).
   *
   * @param {String} id
   * @param {String} type
   * @param {Function} handler
   */
  addEventListenerForElement(id, type, handler) {
    if (typeof id !== "string") {
      throw new Error("Expected a string ID in addEventListenerForElement but" +
        " got: " + id);
    }

    // If no one is listening for this type of event yet, add one listener.
    if (!this.listeners.has(type)) {
      let target = this.highlighterEnv.pageListenerTarget;
      target.addEventListener(type, this, true);
      // Each type entry in the map is a map of ids:handlers.
      this.listeners.set(type, new Map());
    }

    let listeners = this.listeners.get(type);
    listeners.set(id, handler);
  },

  /**
   * Remove an event listener from one of the elements inserted in the
   * canvasFrame native anonymous container.
   * @param {String} id
   * @param {String} type
   */
  removeEventListenerForElement(id, type) {
    let listeners = this.listeners.get(type);
    if (!listeners) {
      return;
    }
    listeners.delete(id);

    // If no one is listening for event type anymore, remove the listener.
    if (!this.listeners.has(type)) {
      let target = this.highlighterEnv.pageListenerTarget;
      target.removeEventListener(type, this, true);
    }
  },

  handleEvent(event) {
    let listeners = this.listeners.get(event.type);
    if (!listeners) {
      return;
    }

    // Hide the originalTarget property to avoid exposing references to native
    // anonymous elements. See addEventListenerForElement's comment.
    let isPropagationStopped = false;
    let eventProxy = new Proxy(event, {
      get: (obj, name) => {
        if (name === "originalTarget") {
          return null;
        } else if (name === "stopPropagation") {
          return () => {
            isPropagationStopped = true;
          };
        }
        return obj[name];
      }
    });

    // Start at originalTarget, bubble through ancestors and call handlers when
    // needed.
    let node = event.originalTarget;
    while (node) {
      let handler = listeners.get(node.id);
      if (handler) {
        handler(eventProxy, node.id);
        if (isPropagationStopped) {
          break;
        }
      }
      node = node.parentNode;
    }
  },

  _removeAllListeners() {
    if (this.highlighterEnv && this.highlighterEnv.pageListenerTarget) {
      let target = this.highlighterEnv.pageListenerTarget;
      for (let [type] of this.listeners) {
        target.removeEventListener(type, this, true);
      }
    }
    this.listeners.clear();
  },

  getElement(id) {
    if (this.elements.has(id)) {
      return this.elements.get(id);
    }

    let classList = new ClassList(this.getAttributeForElement(id, "class"));

    EventEmitter.on(classList, "update", () => {
      this.setAttributeForElement(id, "class", classList.toString());
    });

    let element = {
      getTextContent: () => this.getTextContentForElement(id),
      setTextContent: text => this.setTextContentForElement(id, text),
      setAttribute: (name, val) => this.setAttributeForElement(id, name, val),
      getAttribute: name => this.getAttributeForElement(id, name),
      removeAttribute: name => this.removeAttributeForElement(id, name),
      hasAttribute: name => this.hasAttributeForElement(id, name),
      getCanvasContext: type => this.getCanvasContext(id, type),
      addEventListener: (type, handler) => {
        return this.addEventListenerForElement(id, type, handler);
      },
      removeEventListener: (type, handler) => {
        return this.removeEventListenerForElement(id, type, handler);
      },
      computedStyle: {
        getPropertyValue: property => this.getComputedStylePropertyValue(id, property)
      },
      classList
    };

    this.elements.set(id, element);

    return element;
  },

  get content() {
    if (!this._content || Cu.isDeadWrapper(this._content)) {
      return null;
    }
    return this._content;
  },

  /**
   * The canvasFrame anonymous content container gets zoomed in/out with the
   * page. If this is unwanted, i.e. if you want the inserted element to remain
   * unzoomed, then this method can be used.
   *
   * Consumers of the CanvasFrameAnonymousContentHelper should call this method,
   * it isn't executed automatically. Typically, AutoRefreshHighlighter can call
   * it when _update is executed.
   *
   * The matching element will be scaled down or up by 1/zoomLevel (using css
   * transform) to cancel the current zoom. The element's width and height
   * styles will also be set according to the scale. Finally, the element's
   * position will be set as absolute.
   *
   * Note that if the matching element already has an inline style attribute, it
   * *won't* be preserved.
   *
   * @param {DOMNode} node This node is used to determine which container window
   * should be used to read the current zoom value.
   * @param {String} id The ID of the root element inserted with this API.
   */
  scaleRootElement(node, id) {
    let boundaryWindow = this.highlighterEnv.window;
    let zoom = getCurrentZoom(node);
    // Hide the root element and force the reflow in order to get the proper window's
    // dimensions without increasing them.
    this.setAttributeForElement(id, "style", "display: none");
    node.offsetWidth;

    let { width, height } = getWindowDimensions(boundaryWindow);
    let value = "";

    if (zoom !== 1) {
      value = `transform-origin:top left; transform:scale(${1 / zoom}); `;
      width *= zoom;
      height *= zoom;
    }

    value += `position:absolute; width:${width}px;height:${height}px; overflow:hidden`;

    this.setAttributeForElement(id, "style", value);
  }
};
exports.CanvasFrameAnonymousContentHelper = CanvasFrameAnonymousContentHelper;

/**
 * Move the infobar to the right place in the highlighter. This helper method is utilized
 * in both css-grid.js and box-model.js to help position the infobar in an appropriate
 * space over the highlighted node element or grid area. The infobar is used to display
 * relevant information about the highlighted item (ex, node or grid name and dimensions).
 *
 * This method will first try to position the infobar to top or bottom of the container
 * such that it has enough space for the height of the infobar. Afterwards, it will try
 * to horizontally center align with the container element if possible.
 *
 * @param  {DOMNode} container
 *         The container element which will be used to position the infobar.
 * @param  {Object} bounds
 *         The content bounds of the container element.
 * @param  {Window} win
 *         The window object.
 * @param  {Object} [options={}]
 *         Advanced options for the infobar.
 * @param  {String} options.position
 *         Force the infobar to be displayed either on "top" or "bottom". Any other value
 *         will be ingnored.
 * @param  {Boolean} options.hideIfOffscreen
 *         If set to `true`, hides the infobar if it's offscreen, instead of automatically
 *         reposition it.
 */
function moveInfobar(container, bounds, win, options = {}) {
  let zoom = getCurrentZoom(win);
  let viewport = getViewportDimensions(win);

  let { computedStyle } = container;

  let margin = 2;
  let arrowSize = parseFloat(computedStyle
                              .getPropertyValue("--highlighter-bubble-arrow-size"));
  let containerHeight = parseFloat(computedStyle.getPropertyValue("height"));
  let containerWidth = parseFloat(computedStyle.getPropertyValue("width"));
  let containerHalfWidth = containerWidth / 2;

  let viewportWidth = viewport.width * zoom;
  let viewportHeight = viewport.height * zoom;
  let { pageXOffset, pageYOffset } = win;

  pageYOffset *= zoom;
  pageXOffset *= zoom;

  // Defines the boundaries for the infobar.
  let topBoundary = margin;
  let bottomBoundary = viewportHeight - containerHeight - margin - 1;
  let leftBoundary = containerHalfWidth + margin;
  let rightBoundary = viewportWidth - containerHalfWidth - margin;

  // Set the default values.
  let top = bounds.y - containerHeight - arrowSize;
  let bottom = bounds.bottom + margin + arrowSize;
  let left = bounds.x + bounds.width / 2;
  let isOverlapTheNode = false;
  let positionAttribute = "top";
  let position = "absolute";

  // Here we start the math.
  // We basically want to position absolutely the infobar, except when is pointing to a
  // node that is offscreen or partially offscreen, in a way that the infobar can't
  // be placed neither on top nor on bottom.
  // In such cases, the infobar will overlap the node, and to limit the latency given
  // by APZ (See Bug 1312103) it will be positioned as "fixed".
  // It's a sort of "position: sticky" (but positioned as absolute instead of relative).
  let canBePlacedOnTop = top >= pageYOffset;
  let canBePlacedOnBottom = bottomBoundary + pageYOffset - bottom > 0;
  let forcedOnTop = options.position === "top";
  let forcedOnBottom = options.position === "bottom";

  if ((!canBePlacedOnTop && canBePlacedOnBottom && !forcedOnTop) || forcedOnBottom) {
    top = bottom;
    positionAttribute = "bottom";
  }

  let isOffscreenOnTop = top < topBoundary + pageYOffset;
  let isOffscreenOnBottom = top > bottomBoundary + pageYOffset;
  let isOffscreenOnLeft = left < leftBoundary + pageXOffset;
  let isOffscreenOnRight = left > rightBoundary + pageXOffset;

  if (isOffscreenOnTop) {
    top = topBoundary;
    isOverlapTheNode = true;
  } else if (isOffscreenOnBottom) {
    top = bottomBoundary;
    isOverlapTheNode = true;
  } else if (isOffscreenOnLeft || isOffscreenOnRight) {
    isOverlapTheNode = true;
    top -= pageYOffset;
  }

  if (isOverlapTheNode && options.hideIfOffscreen) {
    container.setAttribute("hidden", "true");
    return;
  } else if (isOverlapTheNode) {
    left = Math.min(Math.max(leftBoundary, left - pageXOffset), rightBoundary);

    position = "fixed";
    container.setAttribute("hide-arrow", "true");
  } else {
    position = "absolute";
    container.removeAttribute("hide-arrow");
  }

  // We need to scale the infobar Independently from the highlighter's container;
  // otherwise the `position: fixed` won't work, since "any value other than `none` for
  // the transform, results in the creation of both a stacking context and a containing
  // block. The object acts as a containing block for fixed positioned descendants."
  // (See https://www.w3.org/TR/css-transforms-1/#transform-rendering)
  container.setAttribute("style", `
    position:${position};
    transform-origin: 0 0;
    transform: scale(${1 / zoom}) translate(${left}px, ${top}px)`);

  container.setAttribute("position", positionAttribute);
}
exports.moveInfobar = moveInfobar;
PK
!<E�J�

Mchrome/devtools/modules/devtools/server/actors/highlighters/xul-accessible.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { getBounds } = require("./utils/accessibility");
const { createNode, isNodeValid } = require("./utils/markup");
const { loadSheet } = require("devtools/shared/layout/utils");

/**
 * Stylesheet used for highlighter styling of accessible objects in chrome. It
 * is consistent with the styling of an in-content accessible highlighter.
 */
const ACCESSIBLE_BOUNDS_SHEET = "data:text/css;charset=utf-8," + encodeURIComponent(`
  .accessible-bounds {
    position: fixed;
    pointer-events: none;
    z-index: 10;
    display: block;
    background-color: #6a5acd!important;
    opacity: 0.6;
  }`);

/**
 * The XULWindowAccessibleHighlighter is a class that has the same API as the
 * AccessibleHighlighter, and by extension other highlighters that implement
 * auto-refresh highlighter, but instead of drawing in canvas frame anonymous
 * content (that is not available for chrome accessible highlighting) it adds a
 * transparrent inactionable element with the same position and bounds as the
 * accessible object highlighted. Unlike SimpleOutlineHighlighter, we can't use
 * element (that corresponds to accessible object) itself because the accessible
 * position and bounds are calculated differently.
 *
 * It is used when canvasframe-based AccessibleHighlighter can't be used. This
 * is the case for XUL windows.
 */
class XULWindowAccessibleHighlighter {
  constructor(highlighterEnv) {
    this.highlighterEnv = highlighterEnv;
    this.win = highlighterEnv.window;
  }

  /**
   * Static getter that indicates that XULWindowAccessibleHighlighter supports
   * highlighting in XUL windows.
   */
  static get XULSupported() {
    return true;
  }

  /**
   * Build highlighter markup.
   */
  _buildMarkup() {
    let doc = this.win.document;
    loadSheet(doc.ownerGlobal, ACCESSIBLE_BOUNDS_SHEET);

    this.container = createNode(this.win, {
      parent: doc.body || doc.documentElement,
      attributes: {
        "class": "highlighter-container",
        "role": "presentation"
      }
    });

    this.bounds = createNode(this.win, {
      parent: this.container,
      attributes: {
        "class": "accessible-bounds",
        "role": "presentation"
      }
    });
  }

  /**
   * Get current accessible bounds.
   *
   * @return {Object|null} Returns, if available, positioning and bounds
   *                       information for the accessible object.
   */
  get _bounds() {
    return getBounds(this.win, this.options);
  }

  /**
   * Show the highlighter on a given accessible.
   *
   * @param {DOMNode} node
   *        A dom node that corresponds to the accessible object.
   * @param {Object} options
   *        Object used for passing options. Available options:
   *         - {Number} x
   *           x coordinate of the top left corner of the accessible object
   *         - {Number} y
   *           y coordinate of the top left corner of the accessible object
   *         - {Number} w
   *           width of the the accessible object
   *         - {Number} h
   *           height of the the accessible object
   *         - duration {Number}
   *                    Duration of time that the highlighter should be shown.
   * @return {Boolean} True if accessible is highlighted, false otherwise.
   */
  show(node, options = {}) {
    let isSameNode = node === this.currentNode;
    let hasBounds = options && typeof options.x == "number" &&
                               typeof options.y == "number" &&
                               typeof options.w == "number" &&
                               typeof options.h == "number";
    if (!hasBounds || !isNodeValid(node) || isSameNode) {
      return false;
    }

    this.options = options;
    this.currentNode = node;

    return this._show();
  }

  /**
   * Internal show method that updates bounds and tracks duration based
   * highlighting.
   *
   * @return {Boolean} True if accessible is highlighted, false otherwise.
   */
  _show() {
    if (this._highlightTimer) {
      clearTimeout(this._highlightTimer);
      this._highlightTimer = null;
    }

    let shown = this._update();
    let { duration } = this.options;
    if (shown && duration) {
      this._highlightTimer = setTimeout(() => {
        this._hideAccessibleBounds();
      }, duration);
    }
    return shown;
  }

  /**
   * Update accessible bounds for a current accessible. Re-draw highlighter
   * markup.
   *
   * @return {Boolean} True if accessible is highlighted, false otherwise.
   */
  _update() {
    this._hideAccessibleBounds();
    let bounds = this._bounds;
    if (!bounds) {
      return false;
    }

    let boundsEl = this.bounds;
    if (!boundsEl) {
      this._buildMarkup();
      boundsEl = this.bounds;
    }

    let { left, top, width, height } = bounds;
    boundsEl.style.top = `${top}px`;
    boundsEl.style.left = `${left}px`;
    boundsEl.style.width = `${width}px`;
    boundsEl.style.height = `${height}px`;
    this._showAccessibleBounds();

    return true;
  }

  /**
   * Hide the highlighter
   */
  hide() {
    if (!this.currentNode || !this.highlighterEnv.window) {
      return;
    }

    this._hideAccessibleBounds();
    this.currentNode = null;
    this.options = null;
  }

  /**
   * Show accessible bounds highlighter.
   */
  _showAccessibleBounds() {
    if (this.container) {
      this.container.removeAttribute("hidden");
    }
  }

  /**
   * Hide accessible bounds highlighter.
   */
  _hideAccessibleBounds() {
    if (this.container) {
      this.container.setAttribute("hidden", "true");
    }
  }

  /**
   * Hide accessible highlighter, clean up and remove the markup.
   */
  destroy() {
    if (this._highlightTimer) {
      clearTimeout(this._highlightTimer);
      this._highlightTimer = null;
    }

    this.hide();
    if (this.container) {
      this.container.remove();
    }

    this.win = null;
  }
}

exports.XULWindowAccessibleHighlighter = XULWindowAccessibleHighlighter;
PK
!<=J��F�F?chrome/devtools/modules/devtools/server/actors/highlighters.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/*
  The :-moz-native-anonymous selector prefix prevents the styles defined here
  from impacting web content. Indeed, this pseudo-class is only available to chrome code.
  This stylesheet is loaded as a ua stylesheet via the addon sdk, so having this
  pseudo-class is important.

  A specific selector should still be specified to avoid impacting non-devtools
  chrome content.
*/

:-moz-native-anonymous .highlighter-container {
  /*
  Content CSS applying to the html element impact the highlighters.
  To avoid that, possible cases have been set to initial.
  */
  text-transform: initial;
  text-indent: initial;
  letter-spacing: initial;
  word-spacing: initial;
  color: initial;
  direction: initial;
  writing-mode: initial;
}

:-moz-native-anonymous .highlighter-container {
  --highlighter-guide-color: #08c;
  --highlighter-content-color: #87ceeb;
  --highlighter-bubble-text-color: hsl(216, 33%, 97%);
  --highlighter-bubble-background-color: hsl(214, 13%, 24%);
  --highlighter-bubble-border-color: rgba(255, 255, 255, 0.2);
  --highlighter-bubble-arrow-size: 8px;
  --highlighter-font-family: message-box;
  --highlighter-font-size: 11px;
  --highlighter-marker-color: #000;
}

/**
 * Highlighters are asbolute positioned in the page by default.
 * A single highlighter can have fixed position in its css class if needed (see below the
 * eye dropper or rulers highlighter, for example); but if it has to handle the
 * document's scrolling (as rulers does), it would lag a bit behind due the APZ (Async
 * Pan/Zoom module), that performs asynchronously panning and zooming on the compositor
 * thread rather than the main thread.
 */
:-moz-native-anonymous .highlighter-container {
  position: absolute;
  width: 100%;
  height: 100%;
  /* The container for all highlighters doesn't react to pointer-events by
     default. This is because most highlighters cover the whole viewport but
     don't contain UIs that need to be accessed.
     If your highlighter has UI that needs to be interacted with, add
     'pointer-events:auto;' on its container element. */
  pointer-events: none;
}

:-moz-native-anonymous .highlighter-container.box-model {
  /* Make the box-model container have a z-index other than auto so it always sits above
     other highlighters. */
  z-index: 1;
}

:-moz-native-anonymous .highlighter-container [hidden] {
  display: none;
}

:-moz-native-anonymous .highlighter-container [dragging] {
  cursor: grabbing;
}

/* Box Model Highlighter */

:-moz-native-anonymous .box-model-regions {
  opacity: 0.6;
}

/* Box model regions can be faded (see the onlyRegionArea option in
   highlighters.js) in order to only display certain regions. */
:-moz-native-anonymous .box-model-regions [faded] {
  display: none;
}

:-moz-native-anonymous .box-model-content {
  fill: var(--highlighter-content-color);
}

:-moz-native-anonymous .box-model-padding {
  fill: #6a5acd;
}

:-moz-native-anonymous .box-model-border {
  fill: #444444;
}

:-moz-native-anonymous .box-model-margin {
  fill: #edff64;
}

:-moz-native-anonymous .box-model-content,
:-moz-native-anonymous .box-model-padding,
:-moz-native-anonymous .box-model-border,
:-moz-native-anonymous .box-model-margin {
  stroke: none;
}

:-moz-native-anonymous .box-model-guide-top,
:-moz-native-anonymous .box-model-guide-right,
:-moz-native-anonymous .box-model-guide-bottom,
:-moz-native-anonymous .box-model-guide-left {
  stroke: var(--highlighter-guide-color);
  stroke-dasharray: 5 3;
  shape-rendering: crispEdges;
}

/* Highlighter - Infobar */

:-moz-native-anonymous [class$=infobar-container] {
  position: absolute;
  max-width: 95%;

  font: var(--highlighter-font-family);
  font-size: var(--highlighter-font-size);
}

:-moz-native-anonymous [class$=infobar] {
  position: relative;

  /* Centering the infobar in the container */
  left: -50%;

  padding: 5px;
  min-width: 75px;

  border-radius: 3px;
  background: var(--highlighter-bubble-background-color) no-repeat padding-box;

  color: var(--highlighter-bubble-text-color);
  text-shadow: none;

  border: 1px solid var(--highlighter-bubble-border-color);
}

/* Arrows */

:-moz-native-anonymous [class$=infobar-container] > [class$=infobar]:before {
  left: calc(50% - var(--highlighter-bubble-arrow-size));
  border: var(--highlighter-bubble-arrow-size) solid var(--highlighter-bubble-border-color);
}

:-moz-native-anonymous [class$=infobar-container] > [class$=infobar]:after {
  left: calc(50% - 7px);
  border: 7px solid var(--highlighter-bubble-background-color);
}

:-moz-native-anonymous [class$=infobar-container] > [class$=infobar]:before,
:-moz-native-anonymous [class$=infobar-container] > [class$=infobar]:after {
  content: "";
  display: none;
  position: absolute;
  height: 0;
  width: 0;
  border-left-color: transparent;
  border-right-color: transparent;
}

:-moz-native-anonymous [class$=infobar-container][position="top"]:not([hide-arrow]) > [class$=infobar]:before,
:-moz-native-anonymous [class$=infobar-container][position="top"]:not([hide-arrow]) > [class$=infobar]:after {
  border-bottom: 0;
  top: 100%;
  display: block;
}

:-moz-native-anonymous [class$=infobar-container][position="bottom"]:not([hide-arrow]) > [class$=infobar]:before,
:-moz-native-anonymous [class$=infobar-container][position="bottom"]:not([hide-arrow]) > [class$=infobar]:after {
  border-top: 0;
  bottom: 100%;
  display: block;
}

/* Text Container */

:-moz-native-anonymous [class$=infobar-text] {
  overflow: hidden;
  white-space: nowrap;
  direction: ltr;
  padding-bottom: 1px;
  display: flex;
  justify-content: center;
}

:-moz-native-anonymous .box-model-infobar-tagname {
  color: hsl(285, 100%, 75%);
}

:-moz-native-anonymous .box-model-infobar-id {
  color: hsl(103, 46%, 54%);
  overflow: hidden;
  text-overflow: ellipsis;
}

:-moz-native-anonymous .box-model-infobar-classes,
:-moz-native-anonymous .box-model-infobar-pseudo-classes {
  color: hsl(200, 74%, 57%);
  overflow: hidden;
  text-overflow: ellipsis;
}

:-moz-native-anonymous [class$=infobar-dimensions] {
  color: hsl(210, 30%, 85%);
  border-inline-start: 1px solid #5a6169;
  margin-inline-start: 6px;
  padding-inline-start: 6px;
}

/* CSS Grid Highlighter */

:-moz-native-anonymous .css-grid-canvas {
  position: absolute;
  pointer-events: none;
  top: 0;
  left: 0;
  image-rendering: -moz-crisp-edges;
}

:-moz-native-anonymous .css-grid-regions {
  opacity: 0.6;
}

:-moz-native-anonymous .css-grid-areas,
:-moz-native-anonymous .css-grid-cells {
  opacity: 0.5;
  stroke: none;
}

:-moz-native-anonymous .css-grid-area-infobar-name,
:-moz-native-anonymous .css-grid-cell-infobar-position,
:-moz-native-anonymous .css-grid-line-infobar-number {
  color: hsl(285, 100%, 75%);
}

:-moz-native-anonymous .css-grid-line-infobar-names:not(:empty) {
  color: hsl(210, 30%, 85%);
  border-inline-start: 1px solid #5a6169;
  margin-inline-start: 6px;
  padding-inline-start: 6px;
}

/* CSS Transform Highlighter */

:-moz-native-anonymous .css-transform-transformed {
  fill: var(--highlighter-content-color);
  opacity: 0.8;
}

:-moz-native-anonymous .css-transform-untransformed {
  fill: #66cc52;
  opacity: 0.8;
}

:-moz-native-anonymous .css-transform-transformed,
:-moz-native-anonymous .css-transform-untransformed,
:-moz-native-anonymous .css-transform-line {
  stroke: var(--highlighter-guide-color);
  stroke-dasharray: 5 3;
  stroke-width: 2;
}

/* Element Geometry Highlighter */

:-moz-native-anonymous .geometry-editor-root {
  /* The geometry editor can be interacted with, so it needs to react to
     pointer events */
  pointer-events: auto;
  -moz-user-select: none;
}

:-moz-native-anonymous .geometry-editor-offset-parent {
  stroke: var(--highlighter-guide-color);
  shape-rendering: crispEdges;
  stroke-dasharray: 5 3;
  fill: transparent;
}

:-moz-native-anonymous .geometry-editor-current-node {
  stroke: var(--highlighter-guide-color);
  fill: var(--highlighter-content-color);
  shape-rendering: crispEdges;
  opacity: 0.6;
}

:-moz-native-anonymous .geometry-editor-arrow {
  stroke: var(--highlighter-guide-color);
  shape-rendering: crispEdges;
}

:-moz-native-anonymous .geometry-editor-root circle {
  stroke: var(--highlighter-guide-color);
  fill: var(--highlighter-content-color);
}

:-moz-native-anonymous .geometry-editor-handler-top,
:-moz-native-anonymous .geometry-editor-handler-bottom {
  cursor: ns-resize;
}

:-moz-native-anonymous .geometry-editor-handler-right,
:-moz-native-anonymous .geometry-editor-handler-left {
  cursor: ew-resize;
}

:-moz-native-anonymous [dragging] .geometry-editor-handler-top,
:-moz-native-anonymous [dragging] .geometry-editor-handler-right,
:-moz-native-anonymous [dragging] .geometry-editor-handler-bottom,
:-moz-native-anonymous [dragging] .geometry-editor-handler-left {
  cursor: grabbing;
}

:-moz-native-anonymous .geometry-editor-handler-top.dragging,
:-moz-native-anonymous .geometry-editor-handler-right.dragging,
:-moz-native-anonymous .geometry-editor-handler-bottom.dragging,
:-moz-native-anonymous .geometry-editor-handler-left.dragging {
  fill: var(--highlighter-guide-color);
}

:-moz-native-anonymous .geometry-editor-label-bubble {
  fill: var(--highlighter-bubble-background-color);
  shape-rendering: crispEdges;
}

:-moz-native-anonymous .geometry-editor-label-text {
  fill: var(--highlighter-bubble-text-color);
  font: var(--highlighter-font-family);
  font-size: 10px;
  text-anchor: middle;
  dominant-baseline: middle;
}

/* Rulers Highlighter */

:-moz-native-anonymous .rulers-highlighter-elements {
  shape-rendering: crispEdges;
  pointer-events: none;
  position: fixed;
  top: 0;
  left: 0;
}

:-moz-native-anonymous .rulers-highlighter-elements > g {
  opacity: 0.8;
}

:-moz-native-anonymous .rulers-highlighter-elements > g > rect {
  fill: #fff;
}

:-moz-native-anonymous .rulers-highlighter-ruler-graduations {
  stroke: #bebebe;
}

:-moz-native-anonymous .rulers-highlighter-ruler-markers {
  stroke: #202020;
}

:-moz-native-anonymous .rulers-highlighter-horizontal-labels > text,
:-moz-native-anonymous .rulers-highlighter-vertical-labels > text {
  stroke: none;
  fill: #202020;
  font: var(--highlighter-font-family);
  font-size: 9px;
  dominant-baseline: hanging;
}

:-moz-native-anonymous .rulers-highlighter-horizontal-labels > text {
  text-anchor: start;
}

:-moz-native-anonymous .rulers-highlighter-vertical-labels > text {
  transform: rotate(-90deg);
  text-anchor: end;
}

:-moz-native-anonymous .rulers-highlighter-viewport-infobar-container {
  shape-rendering: crispEdges;
  background-color: rgba(255, 255, 255, 0.7);
  font: var(--highlighter-font-family);
  position: fixed;
  top: 30px;
  right: 0px;
  font-size: 12px;
  padding: 4px;
}

/* Measuring Tool Highlighter */

:-moz-native-anonymous .measuring-tool-highlighter-root {
  position: absolute;
  top: 0;
  left: 0;
  pointer-events: auto;
  cursor: crosshair;
}

:-moz-native-anonymous .measuring-tool-highlighter-elements {
  position: absolute;
}

:-moz-native-anonymous .measuring-tool-highlighter-root path {
  shape-rendering: crispEdges;
  fill: rgba(135, 206, 235, 0.6);
  stroke: var(--highlighter-guide-color);
  pointer-events: none;
}

:-moz-native-anonymous .dragging path {
  fill: rgba(135, 206, 235, 0.6);
  stroke: var(--highlighter-guide-color);
  opacity: 0.45;
}

:-moz-native-anonymous .measuring-tool-highlighter-label-size,
:-moz-native-anonymous .measuring-tool-highlighter-label-position {
  position: absolute;
  top: 0;
  left: 0;
  display: inline-block;
  border-radius: 4px;
  padding: 4px;
  white-space: pre-line;
  font: var(--highlighter-font-family);
  font-size: 10px;
  pointer-events: none;
  -moz-user-select: none;
  box-sizing: border-box;
}

:-moz-native-anonymous .measuring-tool-highlighter-label-position {
  color: #fff;
  background: hsla(214, 13%, 24%, 0.8);
}

:-moz-native-anonymous .measuring-tool-highlighter-label-size {
  color: var(--highlighter-bubble-text-color);
  background: var(--highlighter-bubble-background-color);
  border: 1px solid var(--highlighter-bubble-border-color);
  line-height: 1.5em;
}

:-moz-native-anonymous .measuring-tool-highlighter-guide-top,
:-moz-native-anonymous .measuring-tool-highlighter-guide-right,
:-moz-native-anonymous .measuring-tool-highlighter-guide-bottom,
:-moz-native-anonymous .measuring-tool-highlighter-guide-left {
  stroke: var(--highlighter-guide-color);
  stroke-dasharray: 5 3;
  shape-rendering: crispEdges;
}

/* Eye Dropper */

:-moz-native-anonymous .eye-dropper-root {
  --magnifier-width: 96px;
  --magnifier-height: 96px;
  /* Width accounts for all color formats (hsl being the longest) */
  --label-width: 160px;
  --label-height: 23px;
  --color: #e0e0e0;

  position: fixed;
  /* Tool start position. This should match the X/Y defines in JS */
  top: 100px;
  left: 100px;

  /* Prevent interacting with the page when hovering and clicking */
  pointer-events: auto;

  /* Offset the UI so it is centered around the pointer */
  transform: translate(
    calc(var(--magnifier-width) / -2), calc(var(--magnifier-height) / -2));

  filter: drop-shadow(0 0 1px rgba(0,0,0,.4));

  /* We don't need the UI to be reversed in RTL locales, otherwise the # would appear
     to the right of the hex code. Force LTR */
  direction: ltr;
}

:-moz-native-anonymous .eye-dropper-canvas {
  image-rendering: -moz-crisp-edges;
  cursor: none;
  width: var(--magnifier-width);
  height: var(--magnifier-height);
  border-radius: 50%;
  box-shadow: 0 0 0 3px var(--color);
  display: block;
}

:-moz-native-anonymous .eye-dropper-color-container {
  background-color: var(--color);
  border-radius: 2px;
  width: var(--label-width);
  height: var(--label-height);
  position: relative;

  --label-horizontal-center:
    translateX(calc((var(--magnifier-width) - var(--label-width)) / 2));
  --label-horizontal-left:
    translateX(calc((-1 * var(--label-width) + var(--magnifier-width) / 2)));
  --label-horizontal-right:
    translateX(calc(var(--magnifier-width) / 2));
  --label-vertical-top:
    translateY(calc((-1 * var(--magnifier-height)) - var(--label-height)));

  /* By default the color label container sits below the canvas.
     Here we just center it horizontally */
  transform: var(--label-horizontal-center);
  transition: transform .1s ease-in-out;
}

/* If there isn't enough space below the canvas, we move the label container to the top */
:-moz-native-anonymous .eye-dropper-root[top] .eye-dropper-color-container {
  transform: var(--label-horizontal-center) var(--label-vertical-top);
}

/* If there isn't enough space right of the canvas to horizontally center the label
   container, offset it to the left */
:-moz-native-anonymous .eye-dropper-root[left] .eye-dropper-color-container {
  transform: var(--label-horizontal-left);
}
:-moz-native-anonymous .eye-dropper-root[left][top] .eye-dropper-color-container {
  transform: var(--label-horizontal-left) var(--label-vertical-top);
}

/* If there isn't enough space left of the canvas to horizontally center the label
   container, offset it to the right */
:-moz-native-anonymous .eye-dropper-root[right] .eye-dropper-color-container {
  transform: var(--label-horizontal-right);
}
:-moz-native-anonymous .eye-dropper-root[right][top] .eye-dropper-color-container {
  transform: var(--label-horizontal-right) var(--label-vertical-top);
}

:-moz-native-anonymous .eye-dropper-color-preview {
  width: 16px;
  height: 16px;
  position: absolute;
  offset-inline-start: 3px;
  offset-block-start: 3px;
  box-shadow: 0px 0px 0px black;
  border: solid 1px #fff;
}

:-moz-native-anonymous .eye-dropper-color-value {
  text-shadow: 1px 1px 1px #fff;
  font: var(--highlighter-font-family);
  font-size: var(--highlighter-font-size);
  text-align: center;
  padding: 4px 0;
}

/* Paused Debugger Overlay */

:-moz-native-anonymous .paused-dbg-root {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;

  display: flex;
  align-items: center;
  flex-direction: column;

  /* We don't have access to DevTools themes here, but some of these colors come from the
     themes. Theme variable names are given in comments. */
  --text-color: #585959; /* --theme-body-color-alt */
  --toolbar-background: #fcfcfc; /* --theme-toolbar-background */;
  --toolbar-border: #dde1e4; /* --theme-splitter-color */
  --toolbar-box-shadow: 0 4px 4px 0 rgba(155, 155, 155, 0.26); /* --rdm-box-shadow */
  --overlay-background: #dde1e4a8;
}

:-moz-native-anonymous .paused-dbg-root[overlay] {
  background-color: var(--overlay-background);
  pointer-events: auto;
}

:-moz-native-anonymous .paused-dbg-toolbar {
  margin-top: 15px;
  padding: 4px 5px;
  display: inline-flex;
  -moz-user-select: none;
  pointer-events: auto;

  color: var(--text-color);
  border-radius: 2px;
  box-shadow: var(--toolbar-box-shadow);
  background-color: var(--toolbar-background);
  border: 1px solid var(--toolbar-border);

  font: var(--highlighter-font-family);
  font-size: var(--highlighter-font-size);
}

/* Shapes highlighter */

:-moz-native-anonymous .shapes-root {
  pointer-events: none;
}

:-moz-native-anonymous .shapes-shape-container {
  position: absolute;
  overflow: visible;
}

:-moz-native-anonymous .shapes-polygon,
:-moz-native-anonymous .shapes-ellipse,
:-moz-native-anonymous .shapes-rect,
:-moz-native-anonymous .shapes-bounding-box,
:-moz-native-anonymous .shapes-rotate-line,
:-moz-native-anonymous .shapes-quad {
  fill: transparent;
  stroke: var(--highlighter-guide-color);
  shape-rendering: geometricPrecision;
  vector-effect: non-scaling-stroke;
}

:-moz-native-anonymous .shapes-markers {
  fill: #fff;
}

:-moz-native-anonymous .shapes-markers-outline {
  fill: var(--highlighter-guide-color);
}

:-moz-native-anonymous .shapes-marker-hover {
  fill: var(--highlighter-guide-color);
}

/* Accessible highlighter */

:-moz-native-anonymous .accessible-bounds {
  opacity: 0.6;
  fill: #6a5acd;
}
PK
!<U)d�\�\>chrome/devtools/modules/devtools/server/actors/highlighters.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Ci, Cu } = require("chrome");

const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
const EventEmitter = require("devtools/shared/event-emitter");
const protocol = require("devtools/shared/protocol");
const Services = require("Services");
const { highlighterSpec, customHighlighterSpec } = require("devtools/shared/specs/highlighters");

loader.lazyRequireGetter(this, "isWindowIncluded", "devtools/shared/layout/utils", true);
loader.lazyRequireGetter(this, "isXUL", "devtools/server/actors/highlighters/utils/markup", true);
loader.lazyRequireGetter(this, "SimpleOutlineHighlighter", "devtools/server/actors/highlighters/simple-outline", true);
loader.lazyRequireGetter(this, "BoxModelHighlighter", "devtools/server/actors/highlighters/box-model", true);

const HIGHLIGHTER_PICKED_TIMER = 1000;
const IS_OSX = Services.appinfo.OS === "Darwin";

/**
 * The registration mechanism for highlighters provide a quick way to
 * have modular highlighters, instead of a hard coded list.
 * It allow us to split highlighers in sub modules, and add them dynamically
 * using add-on (useful for 3rd party developers, or prototyping)
 *
 * Note that currently, highlighters added using add-ons, can only work on
 * Firefox desktop, or Fennec if the same add-on is installed in both.
 */
const highlighterTypes = new Map();

/**
 * Returns `true` if a highlighter for the given `typeName` is registered,
 * `false` otherwise.
 */
const isTypeRegistered = (typeName) => highlighterTypes.has(typeName);
exports.isTypeRegistered = isTypeRegistered;

/**
 * Registers a given constructor as highlighter, for the `typeName` given.
 * If no `typeName` is provided, the `typeName` property on the constructor's prototype
 * is used, if one is found, otherwise the name of the constructor function is used.
 */
const register = (typeName, modulePath) => {
  if (highlighterTypes.has(typeName)) {
    throw Error(`${typeName} is already registered.`);
  }

  highlighterTypes.set(typeName, modulePath);
};
exports.register = register;

/**
 * The Highlighter is the server-side entry points for any tool that wishes to
 * highlight elements in some way in the content document.
 *
 * A little bit of vocabulary:
 * - <something>HighlighterActor classes are the actors that can be used from
 *   the client. They do very little else than instantiate a given
 *   <something>Highlighter and use it to highlight elements.
 * - <something>Highlighter classes aren't actors, they're just JS classes that
 *   know how to create and attach the actual highlighter elements on top of the
 *   content
 *
 * The most used highlighter actor is the HighlighterActor which can be
 * conveniently retrieved via the InspectorActor's 'getHighlighter' method.
 * The InspectorActor will always return the same instance of
 * HighlighterActor if asked several times and this instance is used in the
 * toolbox to highlighter elements's box-model from the markup-view,
 * box model view, console, debugger, ... as well as select elements with the
 * pointer (pick).
 *
 * Other types of highlighter actors exist and can be accessed via the
 * InspectorActor's 'getHighlighterByType' method.
 */

/**
 * The HighlighterActor class
 */
exports.HighlighterActor = protocol.ActorClassWithSpec(highlighterSpec, {
  initialize: function (inspector, autohide) {
    protocol.Actor.prototype.initialize.call(this, null);

    this._autohide = autohide;
    this._inspector = inspector;
    this._walker = this._inspector.walker;
    this._tabActor = this._inspector.tabActor;
    this._highlighterEnv = new HighlighterEnvironment();
    this._highlighterEnv.initFromTabActor(this._tabActor);

    this._highlighterReady = this._highlighterReady.bind(this);
    this._highlighterHidden = this._highlighterHidden.bind(this);
    this._onNavigate = this._onNavigate.bind(this);

    let doc = this._tabActor.window.document;
    // Only try to create the highlighter when the document is loaded,
    // otherwise, wait for the navigate event to fire.
    if (doc.documentElement && doc.readyState != "uninitialized") {
      this._createHighlighter();
    }

    // Listen to navigation events to switch from the BoxModelHighlighter to the
    // SimpleOutlineHighlighter, and back, if the top level window changes.
    this._tabActor.on("navigate", this._onNavigate);
  },

  get conn() {
    return this._inspector && this._inspector.conn;
  },

  form: function () {
    return {
      actor: this.actorID,
      traits: {
        autoHideOnDestroy: true
      }
    };
  },

  _createHighlighter: function () {
    this._isPreviousWindowXUL = isXUL(this._tabActor.window);

    if (!this._isPreviousWindowXUL) {
      this._highlighter = new BoxModelHighlighter(this._highlighterEnv,
                                                  this._inspector);
      this._highlighter.on("ready", this._highlighterReady);
      this._highlighter.on("hide", this._highlighterHidden);
    } else {
      this._highlighter = new SimpleOutlineHighlighter(this._highlighterEnv);
    }
  },

  _destroyHighlighter: function () {
    if (this._highlighter) {
      if (!this._isPreviousWindowXUL) {
        this._highlighter.off("ready", this._highlighterReady);
        this._highlighter.off("hide", this._highlighterHidden);
      }
      this._highlighter.destroy();
      this._highlighter = null;
    }
  },

  _onNavigate: function ({isTopLevel}) {
    // Skip navigation events for non top-level windows, or if the document
    // doesn't exist anymore.
    if (!isTopLevel || !this._tabActor.window.document.documentElement) {
      return;
    }

    // Only rebuild the highlighter if the window type changed.
    if (isXUL(this._tabActor.window) !== this._isPreviousWindowXUL) {
      this._destroyHighlighter();
      this._createHighlighter();
    }
  },

  destroy: function () {
    protocol.Actor.prototype.destroy.call(this);

    this.hideBoxModel();
    this._destroyHighlighter();
    this._tabActor.off("navigate", this._onNavigate);

    this._highlighterEnv.destroy();
    this._highlighterEnv = null;

    this._autohide = null;
    this._inspector = null;
    this._walker = null;
    this._tabActor = null;
  },

  /**
   * Display the box model highlighting on a given NodeActor.
   * There is only one instance of the box model highlighter, so calling this
   * method several times won't display several highlighters, it will just move
   * the highlighter instance to these nodes.
   *
   * @param NodeActor The node to be highlighted
   * @param Options See the request part for existing options. Note that not
   * all options may be supported by all types of highlighters.
   */
  showBoxModel: function (node, options = {}) {
    if (!node || !this._highlighter.show(node.rawNode, options)) {
      this._highlighter.hide();
    }
  },

  /**
   * Hide the box model highlighting if it was shown before
   */
  hideBoxModel: function () {
    if (this._highlighter) {
      this._highlighter.hide();
    }
  },

  /**
   * Returns `true` if the event was dispatched from a window included in
   * the current highlighter environment; or if the highlighter environment has
   * chrome privileges
   *
   * The method is specifically useful on B2G, where we do not want that events
   * from app or main process are processed if we're inspecting the content.
   *
   * @param {Event} event
   *          The event to allow
   * @return {Boolean}
   */
  _isEventAllowed: function ({view}) {
    let { window } = this._highlighterEnv;

    return window instanceof Ci.nsIDOMChromeWindow ||
          isWindowIncluded(window, view);
  },

  /**
   * Pick a node on click, and highlight hovered nodes in the process.
   *
   * This method doesn't respond anything interesting, however, it starts
   * mousemove, and click listeners on the content document to fire
   * events and let connected clients know when nodes are hovered over or
   * clicked.
   *
   * Once a node is picked, events will cease, and listeners will be removed.
   */
  _isPicking: false,
  _hoveredNode: null,
  _currentNode: null,

  pick: function () {
    if (this._isPicking) {
      return null;
    }
    this._isPicking = true;

    this._preventContentEvent = event => {
      event.stopPropagation();
      event.preventDefault();
    };

    this._onPick = event => {
      this._preventContentEvent(event);

      if (!this._isEventAllowed(event)) {
        return;
      }

      // If shift is pressed, this is only a preview click, send the event to
      // the client, but don't stop picking.
      if (event.shiftKey) {
        this._walker.emit("picker-node-previewed",
          this._findAndAttachElement(event));
        return;
      }

      this._stopPickerListeners();
      this._isPicking = false;
      if (this._autohide) {
        this._tabActor.window.setTimeout(() => {
          this._highlighter.hide();
        }, HIGHLIGHTER_PICKED_TIMER);
      }
      if (!this._currentNode) {
        this._currentNode = this._findAndAttachElement(event);
      }
      this._walker.emit("picker-node-picked", this._currentNode);
    };

    this._onHovered = event => {
      this._preventContentEvent(event);

      if (!this._isEventAllowed(event)) {
        return;
      }

      this._currentNode = this._findAndAttachElement(event);
      if (this._hoveredNode !== this._currentNode.node) {
        this._highlighter.show(this._currentNode.node.rawNode);
        this._walker.emit("picker-node-hovered", this._currentNode);
        this._hoveredNode = this._currentNode.node;
      }
    };

    this._onKey = event => {
      if (!this._currentNode || !this._isPicking) {
        return;
      }

      this._preventContentEvent(event);

      if (!this._isEventAllowed(event)) {
        return;
      }

      let currentNode = this._currentNode.node.rawNode;

      /**
       * KEY: Action/scope
       * LEFT_KEY: wider or parent
       * RIGHT_KEY: narrower or child
       * ENTER/CARRIAGE_RETURN: Picks currentNode
       * ESC/CTRL+SHIFT+C: Cancels picker, picks currentNode
       */
      switch (event.keyCode) {
        // Wider.
        case event.DOM_VK_LEFT:
          if (!currentNode.parentElement) {
            return;
          }
          currentNode = currentNode.parentElement;
          break;

        // Narrower.
        case event.DOM_VK_RIGHT:
          if (!currentNode.children.length) {
            return;
          }

          // Set firstElementChild by default
          let child = currentNode.firstElementChild;
          // If currentNode is parent of hoveredNode, then
          // previously selected childNode is set
          let hoveredNode = this._hoveredNode.rawNode;
          for (let sibling of currentNode.children) {
            if (sibling.contains(hoveredNode) || sibling === hoveredNode) {
              child = sibling;
            }
          }

          currentNode = child;
          break;

        // Select the element.
        case event.DOM_VK_RETURN:
          this._onPick(event);
          return;

        // Cancel pick mode.
        case event.DOM_VK_ESCAPE:
          this.cancelPick();
          this._walker.emit("picker-node-canceled");
          return;
        case event.DOM_VK_C:
          if ((IS_OSX && event.metaKey && event.altKey) ||
            (!IS_OSX && event.ctrlKey && event.shiftKey)) {
            this.cancelPick();
            this._walker.emit("picker-node-canceled");
          }
          return;
        default: return;
      }

      // Store currently attached element
      this._currentNode = this._walker.attachElement(currentNode);
      this._highlighter.show(this._currentNode.node.rawNode);
      this._walker.emit("picker-node-hovered", this._currentNode);
    };

    this._startPickerListeners();

    return null;
  },

  /**
   * This pick method also focuses the highlighter's target window.
   */
  pickAndFocus: function () {
    // Go ahead and pass on the results to help future-proof this method.
    let pickResults = this.pick();
    this._highlighterEnv.window.focus();
    return pickResults;
  },

  _findAndAttachElement: function (event) {
    // originalTarget allows access to the "real" element before any retargeting
    // is applied, such as in the case of XBL anonymous elements.  See also
    // https://developer.mozilla.org/docs/XBL/XBL_1.0_Reference/Anonymous_Content#Event_Flow_and_Targeting
    let node = event.originalTarget || event.target;
    return this._walker.attachElement(node);
  },

  _startPickerListeners: function () {
    let target = this._highlighterEnv.pageListenerTarget;
    target.addEventListener("mousemove", this._onHovered, true);
    target.addEventListener("click", this._onPick, true);
    target.addEventListener("mousedown", this._preventContentEvent, true);
    target.addEventListener("mouseup", this._preventContentEvent, true);
    target.addEventListener("dblclick", this._preventContentEvent, true);
    target.addEventListener("keydown", this._onKey, true);
    target.addEventListener("keyup", this._preventContentEvent, true);
  },

  _stopPickerListeners: function () {
    let target = this._highlighterEnv.pageListenerTarget;

    if (!target) {
      return;
    }

    target.removeEventListener("mousemove", this._onHovered, true);
    target.removeEventListener("click", this._onPick, true);
    target.removeEventListener("mousedown", this._preventContentEvent, true);
    target.removeEventListener("mouseup", this._preventContentEvent, true);
    target.removeEventListener("dblclick", this._preventContentEvent, true);
    target.removeEventListener("keydown", this._onKey, true);
    target.removeEventListener("keyup", this._preventContentEvent, true);
  },

  _highlighterReady: function () {
    this._inspector.walker.emit("highlighter-ready");
  },

  _highlighterHidden: function () {
    this._inspector.walker.emit("highlighter-hide");
  },

  cancelPick: function () {
    if (this._isPicking) {
      this._highlighter.hide();
      this._stopPickerListeners();
      this._isPicking = false;
      this._hoveredNode = null;
    }
  }
});

/**
 * A generic highlighter actor class that instantiate a highlighter given its
 * type name and allows to show/hide it.
 */
exports.CustomHighlighterActor = protocol.ActorClassWithSpec(customHighlighterSpec, {
  /**
   * Create a highlighter instance given its typename
   * The typename must be one of HIGHLIGHTER_CLASSES and the class must
   * implement constructor(tabActor), show(node), hide(), destroy()
   */
  initialize: function (parent, typeName) {
    protocol.Actor.prototype.initialize.call(this, null);

    this._parent = parent;

    let modulePath = highlighterTypes.get(typeName);
    if (!modulePath) {
      let list = [...highlighterTypes.keys()];

      throw new Error(`${typeName} isn't a valid highlighter class (${list})`);
    }

    let constructor = require("./highlighters/" + modulePath)[typeName];
    // The assumption is that custom highlighters either need the canvasframe
    // container to append their elements and thus a non-XUL window or they have
    // to define a static XULSupported flag that indicates that the highlighter
    // supports XUL windows. Otherwise, bail out.
    if (!isXUL(this._parent.tabActor.window) || constructor.XULSupported) {
      this._highlighterEnv = new HighlighterEnvironment();
      this._highlighterEnv.initFromTabActor(parent.tabActor);
      this._highlighter = new constructor(this._highlighterEnv);
      if (this._highlighter.on) {
        this._highlighter.on("highlighter-event", this._onHighlighterEvent.bind(this));
      }
    } else {
      throw new Error("Custom " + typeName +
        "highlighter cannot be created in a XUL window");
    }
  },

  get conn() {
    return this._parent && this._parent.conn;
  },

  destroy: function () {
    protocol.Actor.prototype.destroy.call(this);
    this.finalize();
    this._parent = null;
  },

  release: function () {},

  /**
   * Show the highlighter.
   * This calls through to the highlighter instance's |show(node, options)|
   * method.
   *
   * Most custom highlighters are made to highlight DOM nodes, hence the first
   * NodeActor argument (NodeActor as in
   * devtools/server/actor/inspector).
   * Note however that some highlighters use this argument merely as a context
   * node: The SelectHighlighter for instance uses it as a base node to run the
   * provided CSS selector on.
   *
   * @param {NodeActor} The node to be highlighted
   * @param {Object} Options for the custom highlighter
   * @return {Boolean} True, if the highlighter has been successfully shown
   * (FF41+)
   */
  show: function (node, options) {
    if (!node || !this._highlighter) {
      return false;
    }

    return this._highlighter.show(node.rawNode, options);
  },

  /**
   * Hide the highlighter if it was shown before
   */
  hide: function () {
    if (this._highlighter) {
      this._highlighter.hide();
    }
  },

  /**
   * Upon receiving an event from the highlighter, forward it to the client.
   */
  _onHighlighterEvent: function (data) {
    this.emit("highlighter-event", data);
  },

  /**
   * Kill this actor. This method is called automatically just before the actor
   * is destroyed.
   */
  finalize: function () {
    if (this._highlighter) {
      if (this._highlighter.off) {
        this._highlighter.off("highlighter-event", this._onHighlighterEvent.bind(this));
      }
      this._highlighter.destroy();
      this._highlighter = null;
    }

    if (this._highlighterEnv) {
      this._highlighterEnv.destroy();
      this._highlighterEnv = null;
    }
  }
});

/**
 * The HighlighterEnvironment is an object that holds all the required data for
 * highlighters to work: the window, docShell, event listener target, ...
 * It also emits "will-navigate", "navigate" and "window-ready" events,
 * similarly to the TabActor.
 *
 * It can be initialized either from a TabActor (which is the most frequent way
 * of using it, since highlighters are usually initialized by the
 * HighlighterActor or CustomHighlighterActor, which have a tabActor reference).
 * It can also be initialized just with a window object (which is useful for
 * when a highlighter is used outside of the debugger server context, for
 * instance from a gcli command).
 */
function HighlighterEnvironment() {
  this.relayTabActorWindowReady = this.relayTabActorWindowReady.bind(this);
  this.relayTabActorNavigate = this.relayTabActorNavigate.bind(this);
  this.relayTabActorWillNavigate = this.relayTabActorWillNavigate.bind(this);

  EventEmitter.decorate(this);
}

exports.HighlighterEnvironment = HighlighterEnvironment;

HighlighterEnvironment.prototype = {
  initFromTabActor: function (tabActor) {
    this._tabActor = tabActor;
    this._tabActor.on("window-ready", this.relayTabActorWindowReady);
    this._tabActor.on("navigate", this.relayTabActorNavigate);
    this._tabActor.on("will-navigate", this.relayTabActorWillNavigate);
  },

  initFromWindow: function (win) {
    this._win = win;

    // We need a progress listener to know when the window will navigate/has
    // navigated.
    let self = this;
    this.listener = {
      QueryInterface: XPCOMUtils.generateQI([
        Ci.nsIWebProgressListener,
        Ci.nsISupportsWeakReference,
        Ci.nsISupports
      ]),

      onStateChange: function (progress, request, flag) {
        let isStart = flag & Ci.nsIWebProgressListener.STATE_START;
        let isStop = flag & Ci.nsIWebProgressListener.STATE_STOP;
        let isWindow = flag & Ci.nsIWebProgressListener.STATE_IS_WINDOW;
        let isDocument = flag & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT;

        if (progress.DOMWindow !== win) {
          return;
        }

        if (isDocument && isStart) {
          // One of the earliest events that tells us a new URI is being loaded
          // in this window.
          self.emit("will-navigate", {
            window: win,
            isTopLevel: true
          });
        }
        if (isWindow && isStop) {
          self.emit("navigate", {
            window: win,
            isTopLevel: true
          });
        }
      }
    };

    this.webProgress.addProgressListener(this.listener,
      Ci.nsIWebProgress.NOTIFY_STATE_WINDOW |
      Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT);
  },

  get isInitialized() {
    return this._win || this._tabActor;
  },

  get isXUL() {
    return isXUL(this.window);
  },

  get window() {
    if (!this.isInitialized) {
      throw new Error("Initialize HighlighterEnvironment with a tabActor " +
        "or window first");
    }
    let win = this._tabActor ? this._tabActor.window : this._win;

    return Cu.isDeadWrapper(win) ? null : win;
  },

  get document() {
    return this.window && this.window.document;
  },

  get docShell() {
    return this.window &&
           this.window.QueryInterface(Ci.nsIInterfaceRequestor)
                      .getInterface(Ci.nsIWebNavigation)
                      .QueryInterface(Ci.nsIDocShell);
  },

  get webProgress() {
    return this.docShell &&
           this.docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                        .getInterface(Ci.nsIWebProgress);
  },

  /**
   * Get the right target for listening to events on the page.
   * - If the environment was initialized from a TabActor *and* if we're in the
   *   Browser Toolbox (to inspect firefox desktop): the tabActor is the
   *   RootActor, in which case, the window property can be used to listen to
   *   events.
   * - With firefox desktop, that tabActor is a BrowserTabActor, and with B2G,
   *   a ContentActor (which overrides BrowserTabActor). In both cases we use
   *   the chromeEventHandler which gives us a target we can use to listen to
   *   events, even from nested iframes.
   * - If the environment was initialized from a window, we also use the
   *   chromeEventHandler.
   */
  get pageListenerTarget() {
    if (this._tabActor && this._tabActor.isRootActor) {
      return this.window;
    }
    return this.docShell && this.docShell.chromeEventHandler;
  },

  relayTabActorWindowReady: function (data) {
    this.emit("window-ready", data);
  },

  relayTabActorNavigate: function (data) {
    this.emit("navigate", data);
  },

  relayTabActorWillNavigate: function (data) {
    this.emit("will-navigate", data);
  },

  destroy: function () {
    if (this._tabActor) {
      this._tabActor.off("window-ready", this.relayTabActorWindowReady);
      this._tabActor.off("navigate", this.relayTabActorNavigate);
      this._tabActor.off("will-navigate", this.relayTabActorWillNavigate);
    }

    // In case the environment was initialized from a window, we need to remove
    // the progress listener.
    if (this._win) {
      try {
        this.webProgress.removeProgressListener(this.listener);
      } catch (e) {
        // Which may fail in case the window was already destroyed.
      }
    }

    this._tabActor = null;
    this._win = null;
  }
};

register("BoxModelHighlighter", "box-model");
register("CssGridHighlighter", "css-grid");
register("CssTransformHighlighter", "css-transform");
register("EyeDropper", "eye-dropper");
register("FlexboxHighlighter", "flexbox");
register("GeometryEditorHighlighter", "geometry-editor");
register("MeasuringToolHighlighter", "measuring-tool");
register("PausedDebuggerOverlay", "paused-debugger");
register("RulersHighlighter", "rulers");
register("SelectorHighlighter", "selector");
register("ShapesHighlighter", "shapes");
PK
!<�dc��Kchrome/devtools/modules/devtools/server/actors/inspector/document-walker.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {Cc, Ci, Cu} = require("chrome");

loader.lazyRequireGetter(this, "nodeFilterConstants", "devtools/shared/dom-node-filter-constants");
loader.lazyRequireGetter(this, "standardTreeWalkerFilter", "devtools/server/actors/inspector/utils", true);

// SKIP_TO_* arguments are used with the DocumentWalker, driving the strategy to use if
// the starting node is incompatible with the filter function of the walker.
const SKIP_TO_PARENT = "SKIP_TO_PARENT";
const SKIP_TO_SIBLING = "SKIP_TO_SIBLING";

/**
 * Wrapper for inDeepTreeWalker.  Adds filtering to the traversal methods.
 * See inDeepTreeWalker for more information about the methods.
 *
 * @param {DOMNode} node
 * @param {Window} rootWin
 * @param {Number} whatToShow
 *        See nodeFilterConstants / inIDeepTreeWalker for options.
 * @param {Function} filter
 *        A custom filter function Taking in a DOMNode and returning an Int. See
 *        WalkerActor.nodeFilter for an example.
 * @param {String} skipTo
 *        Either SKIP_TO_PARENT or SKIP_TO_SIBLING. If the provided node is not compatible
 *        with the filter function for this walker, try to find a compatible one either
 *        in the parents or in the siblings of the node.
 */
function DocumentWalker(node, rootWin,
  whatToShow = nodeFilterConstants.SHOW_ALL,
  filter = standardTreeWalkerFilter,
  skipTo = SKIP_TO_PARENT) {
  if (Cu.isDeadWrapper(rootWin) || !rootWin.location) {
    throw new Error("Got an invalid root window in DocumentWalker");
  }

  this.walker = Cc["@mozilla.org/inspector/deep-tree-walker;1"]
    .createInstance(Ci.inIDeepTreeWalker);
  this.walker.showAnonymousContent = true;
  this.walker.showSubDocuments = true;
  this.walker.showDocumentsAsNodes = true;
  this.walker.init(rootWin.document, whatToShow);
  this.filter = filter;

  // Make sure that the walker knows about the initial node (which could
  // be skipped due to a filter).
  this.walker.currentNode = this.getStartingNode(node, skipTo);
}

DocumentWalker.prototype = {

  get whatToShow() {
    return this.walker.whatToShow;
  },
  get currentNode() {
    return this.walker.currentNode;
  },
  set currentNode(val) {
    this.walker.currentNode = val;
  },

  parentNode: function () {
    return this.walker.parentNode();
  },

  nextNode: function () {
    let node = this.walker.currentNode;
    if (!node) {
      return null;
    }

    let nextNode = this.walker.nextNode();
    while (nextNode && this.isSkippedNode(nextNode)) {
      nextNode = this.walker.nextNode();
    }

    return nextNode;
  },

  firstChild: function () {
    let node = this.walker.currentNode;
    if (!node) {
      return null;
    }

    let firstChild = this.walker.firstChild();
    while (firstChild && this.isSkippedNode(firstChild)) {
      firstChild = this.walker.nextSibling();
    }

    return firstChild;
  },

  lastChild: function () {
    let node = this.walker.currentNode;
    if (!node) {
      return null;
    }

    let lastChild = this.walker.lastChild();
    while (lastChild && this.isSkippedNode(lastChild)) {
      lastChild = this.walker.previousSibling();
    }

    return lastChild;
  },

  previousSibling: function () {
    let node = this.walker.previousSibling();
    while (node && this.isSkippedNode(node)) {
      node = this.walker.previousSibling();
    }
    return node;
  },

  nextSibling: function () {
    let node = this.walker.nextSibling();
    while (node && this.isSkippedNode(node)) {
      node = this.walker.nextSibling();
    }
    return node;
  },

  getStartingNode: function (node, skipTo) {
    // Keep a reference on the starting node in case we can't find a node compatible with
    // the filter.
    let startingNode = node;

    if (skipTo === SKIP_TO_PARENT) {
      while (node && this.isSkippedNode(node)) {
        node = node.parentNode;
      }
    } else if (skipTo === SKIP_TO_SIBLING) {
      node = this.getClosestAcceptedSibling(node);
    }

    return node || startingNode;
  },

  /**
   * Loop on all of the provided node siblings until finding one that is compliant with
   * the filter function.
   */
  getClosestAcceptedSibling: function (node) {
    if (this.filter(node) === nodeFilterConstants.FILTER_ACCEPT) {
      // node is already valid, return immediately.
      return node;
    }

    // Loop on starting node siblings.
    let previous = node;
    let next = node;
    while (previous || next) {
      previous = previous && previous.previousSibling;
      next = next && next.nextSibling;

      if (previous && this.filter(previous) === nodeFilterConstants.FILTER_ACCEPT) {
        // A valid node was found in the previous siblings of the node.
        return previous;
      }

      if (next && this.filter(next) === nodeFilterConstants.FILTER_ACCEPT) {
        // A valid node was found in the next siblings of the node.
        return next;
      }
    }

    return null;
  },

  isSkippedNode: function (node) {
    return this.filter(node) === nodeFilterConstants.FILTER_SKIP;
  },
};

exports.DocumentWalker = DocumentWalker;
exports.SKIP_TO_PARENT = SKIP_TO_PARENT;
exports.SKIP_TO_SIBLING = SKIP_TO_SIBLING;
PK
!<R�
�-�-Echrome/devtools/modules/devtools/server/actors/inspector/inspector.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

/**
 * Here's the server side of the remote inspector.
 *
 * The WalkerActor is the client's view of the debuggee's DOM.  It's gives
 * the client a tree of NodeActor objects.
 *
 * The walker presents the DOM tree mostly unmodified from the source DOM
 * tree, but with a few key differences:
 *
 *  - Empty text nodes are ignored.  This is pretty typical of developer
 *    tools, but maybe we should reconsider that on the server side.
 *  - iframes with documents loaded have the loaded document as the child,
 *    the walker provides one big tree for the whole document tree.
 *
 * There are a few ways to get references to NodeActors:
 *
 *   - When you first get a WalkerActor reference, it comes with a free
 *     reference to the root document's node.
 *   - Given a node, you can ask for children, siblings, and parents.
 *   - You can issue querySelector and querySelectorAll requests to find
 *     other elements.
 *   - Requests that return arbitrary nodes from the tree (like querySelector
 *     and querySelectorAll) will also return any nodes the client hasn't
 *     seen in order to have a complete set of parents.
 *
 * Once you have a NodeFront, you should be able to answer a few questions
 * without further round trips, like the node's name, namespace/tagName,
 * attributes, etc.  Other questions (like a text node's full nodeValue)
 * might require another round trip.
 *
 * The protocol guarantees that the client will always know the parent of
 * any node that is returned by the server.  This means that some requests
 * (like querySelector) will include the extra nodes needed to satisfy this
 * requirement.  The client keeps track of this parent relationship, so the
 * node fronts form a tree that is a subset of the actual DOM tree.
 *
 *
 * We maintain this guarantee to support the ability to release subtrees on
 * the client - when a node is disconnected from the DOM tree we want to be
 * able to free the client objects for all the children nodes.
 *
 * So to be able to answer "all the children of a given node that we have
 * seen on the client side", we guarantee that every time we've seen a node,
 * we connect it up through its parents.
 */

const Services = require("Services");
const protocol = require("devtools/shared/protocol");
const {LongStringActor} = require("devtools/server/actors/string");
const defer = require("devtools/shared/defer");

const {inspectorSpec} = require("devtools/shared/specs/inspector");

loader.lazyRequireGetter(this, "InspectorActorUtils", "devtools/server/actors/inspector/utils");
loader.lazyRequireGetter(this, "WalkerActor", "devtools/server/actors/inspector/walker", true);
loader.lazyRequireGetter(this, "EyeDropper", "devtools/server/actors/highlighters/eye-dropper", true);
loader.lazyRequireGetter(this, "PageStyleActor", "devtools/server/actors/styles", true);
loader.lazyRequireGetter(this, "HighlighterActor", "devtools/server/actors/highlighters", true);
loader.lazyRequireGetter(this, "CustomHighlighterActor", "devtools/server/actors/highlighters", true);
loader.lazyRequireGetter(this, "isTypeRegistered", "devtools/server/actors/highlighters", true);
loader.lazyRequireGetter(this, "HighlighterEnvironment", "devtools/server/actors/highlighters", true);

const SVG_NS = "http://www.w3.org/2000/svg";
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";

/**
 * Server side of the inspector actor, which is used to create
 * inspector-related actors, including the walker.
 */
exports.InspectorActor = protocol.ActorClassWithSpec(inspectorSpec, {
  initialize: function (conn, tabActor) {
    protocol.Actor.prototype.initialize.call(this, conn);
    this.tabActor = tabActor;

    this._onColorPicked = this._onColorPicked.bind(this);
    this._onColorPickCanceled = this._onColorPickCanceled.bind(this);
    this.destroyEyeDropper = this.destroyEyeDropper.bind(this);
  },

  destroy: function () {
    protocol.Actor.prototype.destroy.call(this);

    this.destroyEyeDropper();

    this._highlighterPromise = null;
    this._pageStylePromise = null;
    this._walkerPromise = null;
    this.walker = null;
    this.tabActor = null;
  },

  get window() {
    return this.tabActor.window;
  },

  getWalker: function (options = {}) {
    if (this._walkerPromise) {
      return this._walkerPromise;
    }

    let deferred = defer();
    this._walkerPromise = deferred.promise;

    let window = this.window;
    let domReady = () => {
      let tabActor = this.tabActor;
      window.removeEventListener("DOMContentLoaded", domReady, true);
      this.walker = WalkerActor(this.conn, tabActor, options);
      this.manage(this.walker);
      this.walker.once("destroyed", () => {
        this._walkerPromise = null;
        this._pageStylePromise = null;
      });
      deferred.resolve(this.walker);
    };

    if (window.document.readyState === "loading") {
      window.addEventListener("DOMContentLoaded", domReady, true);
    } else {
      domReady();
    }

    return this._walkerPromise;
  },

  getPageStyle: function () {
    if (this._pageStylePromise) {
      return this._pageStylePromise;
    }

    this._pageStylePromise = this.getWalker().then(walker => {
      let pageStyle = PageStyleActor(this);
      this.manage(pageStyle);
      return pageStyle;
    });
    return this._pageStylePromise;
  },

  /**
   * The most used highlighter actor is the HighlighterActor which can be
   * conveniently retrieved by this method.
   * The same instance will always be returned by this method when called
   * several times.
   * The highlighter actor returned here is used to highlighter elements's
   * box-models from the markup-view, box model, console, debugger, ... as
   * well as select elements with the pointer (pick).
   *
   * @param {Boolean} autohide Optionally autohide the highlighter after an
   * element has been picked
   * @return {HighlighterActor}
   */
  getHighlighter: function (autohide) {
    if (this._highlighterPromise) {
      return this._highlighterPromise;
    }

    this._highlighterPromise = this.getWalker().then(walker => {
      let highlighter = HighlighterActor(this, autohide);
      this.manage(highlighter);
      return highlighter;
    });
    return this._highlighterPromise;
  },

  /**
   * If consumers need to display several highlighters at the same time or
   * different types of highlighters, then this method should be used, passing
   * the type name of the highlighter needed as argument.
   * A new instance will be created everytime the method is called, so it's up
   * to the consumer to release it when it is not needed anymore
   *
   * @param {String} type The type of highlighter to create
   * @return {Highlighter} The highlighter actor instance or null if the
   * typeName passed doesn't match any available highlighter
   */
  getHighlighterByType: function (typeName) {
    if (isTypeRegistered(typeName)) {
      return CustomHighlighterActor(this, typeName);
    }
    return null;
  },

  /**
   * Get the node's image data if any (for canvas and img nodes).
   * Returns an imageData object with the actual data being a LongStringActor
   * and a size json object.
   * The image data is transmitted as a base64 encoded png data-uri.
   * The method rejects if the node isn't an image or if the image is missing
   *
   * Accepts a maxDim request parameter to resize images that are larger. This
   * is important as the resizing occurs server-side so that image-data being
   * transfered in the longstring back to the client will be that much smaller
   */
  getImageDataFromURL: function (url, maxDim) {
    let img = new this.window.Image();
    img.src = url;

    // imageToImageData waits for the image to load.
    return InspectorActorUtils.imageToImageData(img, maxDim).then(imageData => {
      return {
        data: LongStringActor(this.conn, imageData.data),
        size: imageData.size
      };
    });
  },

  /**
   * Resolve a URL to its absolute form, in the scope of a given content window.
   * @param {String} url.
   * @param {NodeActor} node If provided, the owner window of this node will be
   * used to resolve the URL. Otherwise, the top-level content window will be
   * used instead.
   * @return {String} url.
   */
  resolveRelativeURL: function (url, node) {
    let document = InspectorActorUtils.isNodeDead(node)
                   ? this.window.document
                   : InspectorActorUtils.nodeDocument(node.rawNode);

    if (!document) {
      return url;
    }

    let baseURI = Services.io.newURI(document.location.href);
    return Services.io.newURI(url, null, baseURI).spec;
  },

  /**
   * Create an instance of the eye-dropper highlighter and store it on this._eyeDropper.
   * Note that for now, a new instance is created every time to deal with page navigation.
   */
  createEyeDropper: function () {
    this.destroyEyeDropper();
    this._highlighterEnv = new HighlighterEnvironment();
    this._highlighterEnv.initFromTabActor(this.tabActor);
    this._eyeDropper = new EyeDropper(this._highlighterEnv);
  },

  /**
   * Destroy the current eye-dropper highlighter instance.
   */
  destroyEyeDropper: function () {
    if (this._eyeDropper) {
      this.cancelPickColorFromPage();
      this._eyeDropper.destroy();
      this._eyeDropper = null;
      this._highlighterEnv.destroy();
      this._highlighterEnv = null;
    }
  },

  /**
   * Pick a color from the page using the eye-dropper. This method doesn't return anything
   * but will cause events to be sent to the front when a color is picked or when the user
   * cancels the picker.
   * @param {Object} options
   */
  pickColorFromPage: function (options) {
    this.createEyeDropper();
    this._eyeDropper.show(this.window.document.documentElement, options);
    this._eyeDropper.once("selected", this._onColorPicked);
    this._eyeDropper.once("canceled", this._onColorPickCanceled);
    this.tabActor.once("will-navigate", this.destroyEyeDropper);
  },

  /**
   * After the pickColorFromPage method is called, the only way to dismiss the eye-dropper
   * highlighter is for the user to click in the page and select a color. If you need to
   * dismiss the eye-dropper programatically instead, use this method.
   */
  cancelPickColorFromPage: function () {
    if (this._eyeDropper) {
      this._eyeDropper.hide();
      this._eyeDropper.off("selected", this._onColorPicked);
      this._eyeDropper.off("canceled", this._onColorPickCanceled);
      this.tabActor.off("will-navigate", this.destroyEyeDropper);
    }
  },

  /**
   * Check if the current document supports highlighters using a canvasFrame anonymous
   * content container (ie all highlighters except the SimpleOutlineHighlighter).
   * It is impossible to detect the feature programmatically as some document types simply
   * don't render the canvasFrame without throwing any error.
   */
  supportsHighlighters: function () {
    let doc = this.tabActor.window.document;
    let ns = doc.documentElement.namespaceURI;

    // XUL documents do not support insertAnonymousContent().
    if (ns === XUL_NS) {
      return false;
    }

    // SVG documents do not render the canvasFrame (see Bug 1157592).
    if (ns === SVG_NS) {
      return false;
    }

    return true;
  },

  _onColorPicked: function (color) {
    this.emit("color-picked", color);
  },

  _onColorPickCanceled: function () {
    this.emit("color-pick-canceled");
  }
});
PK
!<���8X8X@chrome/devtools/modules/devtools/server/actors/inspector/node.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {Ci, Cu} = require("chrome");

const protocol = require("devtools/shared/protocol");
const {nodeSpec, nodeListSpec} = require("devtools/shared/specs/node");

const InspectorUtils = require("InspectorUtils");

loader.lazyRequireGetter(this, "colorUtils", "devtools/shared/css/color", true);

loader.lazyRequireGetter(this, "getCssPath", "devtools/shared/inspector/css-logic", true);
loader.lazyRequireGetter(this, "getXPath", "devtools/shared/inspector/css-logic", true);
loader.lazyRequireGetter(this, "findCssSelector", "devtools/shared/inspector/css-logic", true);

loader.lazyRequireGetter(this, "isNativeAnonymous", "devtools/shared/layout/utils", true);
loader.lazyRequireGetter(this, "isXBLAnonymous", "devtools/shared/layout/utils", true);
loader.lazyRequireGetter(this, "isShadowAnonymous", "devtools/shared/layout/utils", true);
loader.lazyRequireGetter(this, "isAnonymous", "devtools/shared/layout/utils", true);

loader.lazyRequireGetter(this, "InspectorActorUtils", "devtools/server/actors/inspector/utils");
loader.lazyRequireGetter(this, "LongStringActor", "devtools/server/actors/string", true);
loader.lazyRequireGetter(this, "getFontPreviewData", "devtools/server/actors/styles", true);
loader.lazyRequireGetter(this, "CssLogic", "devtools/server/css-logic", true);
loader.lazyRequireGetter(this, "EventParsers", "devtools/server/event-parsers", true);

const EventEmitter = require("devtools/shared/event-emitter");

const PSEUDO_CLASSES = [":hover", ":active", ":focus"];
const FONT_FAMILY_PREVIEW_TEXT = "The quick brown fox jumps over the lazy dog";
const FONT_FAMILY_PREVIEW_TEXT_SIZE = 20;

/**
 * Server side of the node actor.
 */
const NodeActor = protocol.ActorClassWithSpec(nodeSpec, {
  initialize: function (walker, node) {
    protocol.Actor.prototype.initialize.call(this, null);
    this.walker = walker;
    this.rawNode = node;
    this._eventParsers = new EventParsers().parsers;

    // Store the original display type and whether or not the node is displayed to
    // track changes when reflows occur.
    this.currentDisplayType = this.displayType;
    this.wasDisplayed = this.isDisplayed;
  },

  toString: function () {
    return "[NodeActor " + this.actorID + " for " +
      this.rawNode.toString() + "]";
  },

  /**
   * Instead of storing a connection object, the NodeActor gets its connection
   * from its associated walker.
   */
  get conn() {
    return this.walker.conn;
  },

  isDocumentElement: function () {
    return this.rawNode.ownerDocument &&
           this.rawNode.ownerDocument.documentElement === this.rawNode;
  },

  destroy: function () {
    protocol.Actor.prototype.destroy.call(this);

    if (this.mutationObserver) {
      if (!Cu.isDeadWrapper(this.mutationObserver)) {
        this.mutationObserver.disconnect();
      }
      this.mutationObserver = null;
    }
    this.rawNode = null;
    this.walker = null;
  },

  // Returns the JSON representation of this object over the wire.
  form: function (detail) {
    if (detail === "actorid") {
      return this.actorID;
    }

    let parentNode = this.walker.parentNode(this);
    let inlineTextChild = this.walker.inlineTextChild(this);

    let form = {
      actor: this.actorID,
      baseURI: this.rawNode.baseURI,
      parent: parentNode ? parentNode.actorID : undefined,
      nodeType: this.rawNode.nodeType,
      namespaceURI: this.rawNode.namespaceURI,
      nodeName: this.rawNode.nodeName,
      nodeValue: this.rawNode.nodeValue,
      displayName: InspectorActorUtils.getNodeDisplayName(this.rawNode),
      numChildren: this.numChildren,
      inlineTextChild: inlineTextChild ? inlineTextChild.form() : undefined,
      displayType: this.displayType,

      // doctype attributes
      name: this.rawNode.name,
      publicId: this.rawNode.publicId,
      systemId: this.rawNode.systemId,

      attrs: this.writeAttrs(),
      isBeforePseudoElement: this.isBeforePseudoElement,
      isAfterPseudoElement: this.isAfterPseudoElement,
      isAnonymous: isAnonymous(this.rawNode),
      isNativeAnonymous: isNativeAnonymous(this.rawNode),
      isXBLAnonymous: isXBLAnonymous(this.rawNode),
      isShadowAnonymous: isShadowAnonymous(this.rawNode),
      pseudoClassLocks: this.writePseudoClassLocks(),

      isDisplayed: this.isDisplayed,
      isInHTMLDocument: this.rawNode.ownerDocument &&
        this.rawNode.ownerDocument.contentType === "text/html",
      hasEventListeners: this._hasEventListeners,
    };

    if (this.isDocumentElement()) {
      form.isDocumentElement = true;
    }

    // Add an extra API for custom properties added by other
    // modules/extensions.
    form.setFormProperty = (name, value) => {
      if (!form.props) {
        form.props = {};
      }
      form.props[name] = value;
    };

    // Fire an event so, other modules can create its own properties
    // that should be passed to the client (within the form.props field).
    EventEmitter.emit(NodeActor, "form", {
      target: this,
      data: form
    });

    return form;
  },

  /**
   * Watch the given document node for mutations using the DOM observer
   * API.
   */
  watchDocument: function (callback) {
    let node = this.rawNode;
    // Create the observer on the node's actor.  The node will make sure
    // the observer is cleaned up when the actor is released.
    let observer = new node.defaultView.MutationObserver(callback);
    observer.mergeAttributeRecords = true;
    observer.observe(node, {
      nativeAnonymousChildList: true,
      attributes: true,
      characterData: true,
      characterDataOldValue: true,
      childList: true,
      subtree: true
    });
    this.mutationObserver = observer;
  },

  get isBeforePseudoElement() {
    return this.rawNode.nodeName === "_moz_generated_content_before";
  },

  get isAfterPseudoElement() {
    return this.rawNode.nodeName === "_moz_generated_content_after";
  },

  // Estimate the number of children that the walker will return without making
  // a call to children() if possible.
  get numChildren() {
    // For pseudo elements, childNodes.length returns 1, but the walker
    // will return 0.
    if (this.isBeforePseudoElement || this.isAfterPseudoElement) {
      return 0;
    }

    let rawNode = this.rawNode;
    let numChildren = rawNode.childNodes.length;
    let hasAnonChildren = rawNode.nodeType === Ci.nsIDOMNode.ELEMENT_NODE &&
                          rawNode.ownerDocument.getAnonymousNodes(rawNode);

    let hasContentDocument = rawNode.contentDocument;
    let hasSVGDocument = rawNode.getSVGDocument && rawNode.getSVGDocument();
    if (numChildren === 0 && (hasContentDocument || hasSVGDocument)) {
      // This might be an iframe with virtual children.
      numChildren = 1;
    }

    // Normal counting misses ::before/::after.  Also, some anonymous children
    // may ultimately be skipped, so we have to consult with the walker.
    if (numChildren === 0 || hasAnonChildren) {
      numChildren = this.walker.children(this).nodes.length;
    }

    return numChildren;
  },

  get computedStyle() {
    if (!this._computedStyle) {
      this._computedStyle = CssLogic.getComputedStyle(this.rawNode);
    }
    return this._computedStyle;
  },

  /**
   * Returns the computed display style property value of the node.
   */
  get displayType() {
    // Consider all non-element nodes as displayed.
    if (InspectorActorUtils.isNodeDead(this) ||
        this.rawNode.nodeType !== Ci.nsIDOMNode.ELEMENT_NODE ||
        this.isAfterPseudoElement ||
        this.isBeforePseudoElement) {
      return null;
    }

    let style = this.computedStyle;
    if (!style) {
      return null;
    }

    return style.display;
  },

  /**
   * Is the node's display computed style value other than "none"
   */
  get isDisplayed() {
    // Consider all non-element nodes as displayed.
    if (InspectorActorUtils.isNodeDead(this) ||
        this.rawNode.nodeType !== Ci.nsIDOMNode.ELEMENT_NODE) {
      return true;
    }

    let style = this.computedStyle;
    if (!style) {
      return true;
    }

    return style.display !== "none";
  },

  /**
   * Are there event listeners that are listening on this node? This method
   * uses all parsers registered via event-parsers.js.registerEventParser() to
   * check if there are any event listeners.
   */
  get _hasEventListeners() {
    let parsers = this._eventParsers;
    for (let [, {hasListeners}] of parsers) {
      try {
        if (hasListeners && hasListeners(this.rawNode)) {
          return true;
        }
      } catch (e) {
        // An object attached to the node looked like a listener but wasn't...
        // do nothing.
      }
    }
    return false;
  },

  writeAttrs: function () {
    if (!this.rawNode.attributes) {
      return undefined;
    }

    return [...this.rawNode.attributes].map(attr => {
      return {namespace: attr.namespace, name: attr.name, value: attr.value };
    });
  },

  writePseudoClassLocks: function () {
    if (this.rawNode.nodeType !== Ci.nsIDOMNode.ELEMENT_NODE) {
      return undefined;
    }
    let ret = undefined;
    for (let pseudo of PSEUDO_CLASSES) {
      if (InspectorUtils.hasPseudoClassLock(this.rawNode, pseudo)) {
        ret = ret || [];
        ret.push(pseudo);
      }
    }
    return ret;
  },

  /**
   * Gets event listeners and adds their information to the events array.
   *
   * @param  {Node} node
   *         Node for which we are to get listeners.
   */
  getEventListeners: function (node) {
    let parsers = this._eventParsers;
    let dbg = this.parent().tabActor.makeDebugger();
    let listenerArray = [];

    for (let [, {getListeners, normalizeListener}] of parsers) {
      try {
        let listeners = getListeners(node);

        if (!listeners) {
          continue;
        }

        for (let listener of listeners) {
          if (normalizeListener) {
            listener.normalizeListener = normalizeListener;
          }

          this.processHandlerForEvent(node, listenerArray, dbg, listener);
        }
      } catch (e) {
        // An object attached to the node looked like a listener but wasn't...
        // do nothing.
      }
    }

    listenerArray.sort((a, b) => {
      return a.type.localeCompare(b.type);
    });

    return listenerArray;
  },

  /**
   * Process a handler
   *
   * @param  {Node} node
   *         The node for which we want information.
   * @param  {Array} listenerArray
   *         listenerArray contains all event objects that we have gathered
   *         so far.
   * @param  {Debugger} dbg
   *         JSDebugger instance.
   * @param  {Object} eventInfo
   *         See event-parsers.js.registerEventParser() for a description of the
   *         eventInfo object.
   *
   * @return {Array}
   *         An array of objects where a typical object looks like this:
   *           {
   *             type: "click",
   *             handler: function() { doSomething() },
   *             origin: "http://www.mozilla.com",
   *             searchString: 'onclick="doSomething()"',
   *             tags: tags,
   *             DOM0: true,
   *             capturing: true,
   *             hide: {
   *               DOM0: true
   *             },
   *             native: false
   *           }
   */
  processHandlerForEvent: function (node, listenerArray, dbg, listener) {
    let { handler } = listener;
    let global = Cu.getGlobalForObject(handler);
    let globalDO = dbg.addDebuggee(global);
    let listenerDO = globalDO.makeDebuggeeValue(handler);

    let { normalizeListener } = listener;

    if (normalizeListener) {
      listenerDO = normalizeListener(listenerDO, listener);
    }

    let { capturing } = listener;
    let dom0 = false;
    let functionSource = handler.toString();
    let hide = listener.hide || {};
    let line = 0;
    let native = false;
    let override = listener.override || {};
    let tags = listener.tags || "";
    let type = listener.type || "";
    let url = "";

    // If the listener is an object with a 'handleEvent' method, use that.
    if (listenerDO.class === "Object" || listenerDO.class === "XULElement") {
      let desc;

      while (!desc && listenerDO) {
        desc = listenerDO.getOwnPropertyDescriptor("handleEvent");
        listenerDO = listenerDO.proto;
      }

      if (desc && desc.value) {
        listenerDO = desc.value;
      }
    }

    // If the listener is bound to a different context then we need to switch
    // to the bound function.
    if (listenerDO.isBoundFunction) {
      listenerDO = listenerDO.boundTargetFunction;
    }

    let { isArrowFunction, name, script, parameterNames } = listenerDO;

    if (script) {
      let scriptSource = script.source.text;

      // Scripts are provided via script tags. If it wasn't provided by a
      // script tag it must be a DOM0 event.
      if (script.source.element) {
        dom0 = script.source.element.class !== "HTMLScriptElement";
      } else {
        dom0 = false;
      }

      line = script.startLine;
      url = script.url;

      // Checking for the string "[native code]" is the only way at this point
      // to check for native code. Even if this provides a false positive then
      // grabbing the source code a second time is harmless.
      if (functionSource === "[object Object]" ||
          functionSource === "[object XULElement]" ||
          functionSource.includes("[native code]")) {
        functionSource =
          scriptSource.substr(script.sourceStart, script.sourceLength);

        // At this point the script looks like this:
        // () { ... }
        // We prefix this with "function" if it is not a fat arrow function.
        if (!isArrowFunction) {
          functionSource = "function " + functionSource;
        }
      }
    } else {
      // If the listener is a native one (provided by C++ code) then we have no
      // access to the script. We use the native flag to prevent showing the
      // debugger button because the script is not available.
      native = true;
    }

    // Fat arrow function text always contains the parameters. Function
    // parameters are often missing e.g. if Array.sort is used as a handler.
    // If they are missing we provide the parameters ourselves.
    if (parameterNames && parameterNames.length > 0) {
      let prefix = "function " + name + "()";
      let paramString = parameterNames.join(", ");

      if (functionSource.startsWith(prefix)) {
        functionSource = functionSource.substr(prefix.length);

        functionSource = `function ${name} (${paramString})${functionSource}`;
      }
    }

    // If the listener is native code we display the filename "[native code]."
    // This is the official string and should *not* be translated.
    let origin;
    if (native) {
      origin = "[native code]";
    } else {
      origin = url + ((dom0 || line === 0) ? "" : ":" + line);
    }

    let eventObj = {
      type: override.type || type,
      handler: override.handler || functionSource.trim(),
      origin: override.origin || origin,
      tags: override.tags || tags,
      DOM0: typeof override.dom0 !== "undefined" ? override.dom0 : dom0,
      capturing: typeof override.capturing !== "undefined" ?
                 override.capturing : capturing,
      hide: typeof override.hide !== "undefined" ? override.hide : hide,
      native
    };

    // Hide the debugger icon for DOM0 and native listeners. DOM0 listeners are
    // generated dynamically from e.g. an onclick="" attribute so the script
    // doesn't actually exist.
    if (native || dom0) {
      eventObj.hide.debugger = true;
    }

    listenerArray.push(eventObj);

    dbg.removeDebuggee(globalDO);
  },

  /**
   * Returns a LongStringActor with the node's value.
   */
  getNodeValue: function () {
    return new LongStringActor(this.conn, this.rawNode.nodeValue || "");
  },

  /**
   * Set the node's value to a given string.
   */
  setNodeValue: function (value) {
    this.rawNode.nodeValue = value;
  },

  /**
   * Get a unique selector string for this node.
   */
  getUniqueSelector: function () {
    if (Cu.isDeadWrapper(this.rawNode)) {
      return "";
    }
    return findCssSelector(this.rawNode);
  },

  /**
   * Get the full CSS path for this node.
   *
   * @return {String} A CSS selector with a part for the node and each of its ancestors.
   */
  getCssPath: function () {
    if (Cu.isDeadWrapper(this.rawNode)) {
      return "";
    }
    return getCssPath(this.rawNode);
  },

  /**
   * Get the XPath for this node.
   *
   * @return {String} The XPath for finding this node on the page.
   */
  getXPath: function () {
    if (Cu.isDeadWrapper(this.rawNode)) {
      return "";
    }
    return getXPath(this.rawNode);
  },

  /**
   * Scroll the selected node into view.
   */
  scrollIntoView: function () {
    this.rawNode.scrollIntoView(true);
  },

  /**
   * Get the node's image data if any (for canvas and img nodes).
   * Returns an imageData object with the actual data being a LongStringActor
   * and a size json object.
   * The image data is transmitted as a base64 encoded png data-uri.
   * The method rejects if the node isn't an image or if the image is missing
   *
   * Accepts a maxDim request parameter to resize images that are larger. This
   * is important as the resizing occurs server-side so that image-data being
   * transfered in the longstring back to the client will be that much smaller
   */
  getImageData: function (maxDim) {
    return InspectorActorUtils.imageToImageData(this.rawNode, maxDim).then(imageData => {
      return {
        data: LongStringActor(this.conn, imageData.data),
        size: imageData.size
      };
    });
  },

  /**
   * Get all event listeners that are listening on this node.
   */
  getEventListenerInfo: function () {
    let node = this.rawNode;

    if (this.rawNode.nodeName.toLowerCase() === "html") {
      let winListeners = this.getEventListeners(node.ownerGlobal) || [];
      let docElementListeners = this.getEventListeners(node) || [];
      let docListeners = this.getEventListeners(node.parentNode) || [];

      return [...winListeners, ...docElementListeners, ...docListeners];
    }
    return this.getEventListeners(node);
  },

  /**
   * Modify a node's attributes.  Passed an array of modifications
   * similar in format to "attributes" mutations.
   * {
   *   attributeName: <string>
   *   attributeNamespace: <optional string>
   *   newValue: <optional string> - If null or undefined, the attribute
   *     will be removed.
   * }
   *
   * Returns when the modifications have been made.  Mutations will
   * be queued for any changes made.
   */
  modifyAttributes: function (modifications) {
    let rawNode = this.rawNode;
    for (let change of modifications) {
      if (change.newValue == null) {
        if (change.attributeNamespace) {
          rawNode.removeAttributeNS(change.attributeNamespace,
                                    change.attributeName);
        } else {
          rawNode.removeAttribute(change.attributeName);
        }
      } else if (change.attributeNamespace) {
        rawNode.setAttributeNS(change.attributeNamespace, change.attributeName,
                               change.newValue);
      } else {
        rawNode.setAttribute(change.attributeName, change.newValue);
      }
    }
  },

  /**
   * Given the font and fill style, get the image data of a canvas with the
   * preview text and font.
   * Returns an imageData object with the actual data being a LongStringActor
   * and the width of the text as a string.
   * The image data is transmitted as a base64 encoded png data-uri.
   */
  getFontFamilyDataURL: function (font, fillStyle = "black") {
    let doc = this.rawNode.ownerDocument;
    let options = {
      previewText: FONT_FAMILY_PREVIEW_TEXT,
      previewFontSize: FONT_FAMILY_PREVIEW_TEXT_SIZE,
      fillStyle: fillStyle
    };
    let { dataURL, size } = getFontPreviewData(font, doc, options);

    return { data: LongStringActor(this.conn, dataURL), size: size };
  },

  /**
   * Finds the computed background color of the closest parent with
   * a set background color.
   * Returns a string with the background color of the form
   * rgba(r, g, b, a). Defaults to rgba(255, 255, 255, 1) if no
   * background color is found.
   */
  getClosestBackgroundColor: function () {
    let current = this.rawNode;
    while (current) {
      let computedStyle = CssLogic.getComputedStyle(current);
      let currentStyle = computedStyle.getPropertyValue("background-color");
      if (colorUtils.isValidCSSColor(currentStyle)) {
        let currentCssColor = new colorUtils.CssColor(currentStyle);
        if (!currentCssColor.isTransparent()) {
          return currentCssColor.rgba;
        }
      }
      current = current.parentNode;
    }
    return "rgba(255, 255, 255, 1)";
  }
});

/**
 * Server side of a node list as returned by querySelectorAll()
 */
const NodeListActor = protocol.ActorClassWithSpec(nodeListSpec, {
  typeName: "domnodelist",

  initialize: function (walker, nodeList) {
    protocol.Actor.prototype.initialize.call(this);
    this.walker = walker;
    this.nodeList = nodeList || [];
  },

  destroy: function () {
    protocol.Actor.prototype.destroy.call(this);
  },

  /**
   * Instead of storing a connection object, the NodeActor gets its connection
   * from its associated walker.
   */
  get conn() {
    return this.walker.conn;
  },

  /**
   * Items returned by this actor should belong to the parent walker.
   */
  marshallPool: function () {
    return this.walker;
  },

  // Returns the JSON representation of this object over the wire.
  form: function () {
    return {
      actor: this.actorID,
      length: this.nodeList ? this.nodeList.length : 0
    };
  },

  /**
   * Get a single node from the node list.
   */
  item: function (index) {
    return this.walker.attachElement(this.nodeList[index]);
  },

  /**
   * Get a range of the items from the node list.
   */
  items: function (start = 0, end = this.nodeList.length) {
    let items = Array.prototype.slice.call(this.nodeList, start, end)
      .map(item => this.walker._ref(item));
    return this.walker.attachElements(items);
  },

  release: function () {}
});

exports.NodeActor = NodeActor;
exports.NodeListActor = NodeListActor;
PK
!<��P!!Achrome/devtools/modules/devtools/server/actors/inspector/utils.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {Ci, Cu} = require("chrome");

loader.lazyRequireGetter(this, "AsyncUtils", "devtools/shared/async-utils");
loader.lazyRequireGetter(this, "flags", "devtools/shared/flags");
loader.lazyRequireGetter(this, "DevToolsUtils", "devtools/shared/DevToolsUtils");
loader.lazyRequireGetter(this, "nodeFilterConstants", "devtools/shared/dom-node-filter-constants");

loader.lazyRequireGetter(this, "isNativeAnonymous", "devtools/shared/layout/utils", true);
loader.lazyRequireGetter(this, "isXBLAnonymous", "devtools/shared/layout/utils", true);

const XHTML_NS = "http://www.w3.org/1999/xhtml";
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const IMAGE_FETCHING_TIMEOUT = 500;

/**
 * Returns the properly cased version of the node's tag name, which can be
 * used when displaying said name in the UI.
 *
 * @param  {Node} rawNode
 *         Node for which we want the display name
 * @return {String}
 *         Properly cased version of the node tag name
 */
const getNodeDisplayName = function (rawNode) {
  if (rawNode.nodeName && !rawNode.localName) {
    // The localName & prefix APIs have been moved from the Node interface to the Element
    // interface. Use Node.nodeName as a fallback.
    return rawNode.nodeName;
  }
  return (rawNode.prefix ? rawNode.prefix + ":" : "") + rawNode.localName;
};

function nodeDocument(node) {
  if (Cu.isDeadWrapper(node)) {
    return null;
  }
  return node.ownerDocument ||
         (node.nodeType == Ci.nsIDOMNode.DOCUMENT_NODE ? node : null);
}

function isNodeDead(node) {
  return !node || !node.rawNode || Cu.isDeadWrapper(node.rawNode);
}

function isInXULDocument(el) {
  let doc = nodeDocument(el);
  return doc &&
         doc.documentElement &&
         doc.documentElement.namespaceURI === XUL_NS;
}

/**
 * This DeepTreeWalker filter skips whitespace text nodes and anonymous
 * content with the exception of ::before and ::after and anonymous content
 * in XUL document (needed to show all elements in the browser toolbox).
 */
function standardTreeWalkerFilter(node) {
  // ::before and ::after are native anonymous content, but we always
  // want to show them
  if (node.nodeName === "_moz_generated_content_before" ||
      node.nodeName === "_moz_generated_content_after") {
    return nodeFilterConstants.FILTER_ACCEPT;
  }

  // Ignore empty whitespace text nodes that do not impact the layout.
  if (isWhitespaceTextNode(node)) {
    return nodeHasSize(node)
           ? nodeFilterConstants.FILTER_ACCEPT
           : nodeFilterConstants.FILTER_SKIP;
  }

  // Ignore all native and XBL anonymous content inside a non-XUL document.
  // We need to do this to skip things like form controls, scrollbars,
  // video controls, etc (see bug 1187482).
  if (!isInXULDocument(node) && (isXBLAnonymous(node) ||
                                  isNativeAnonymous(node))) {
    return nodeFilterConstants.FILTER_SKIP;
  }

  return nodeFilterConstants.FILTER_ACCEPT;
}

/**
 * This DeepTreeWalker filter is like standardTreeWalkerFilter except that
 * it also includes all anonymous content (like internal form controls).
 */
function allAnonymousContentTreeWalkerFilter(node) {
  // Ignore empty whitespace text nodes that do not impact the layout.
  if (isWhitespaceTextNode(node)) {
    return nodeHasSize(node)
           ? nodeFilterConstants.FILTER_ACCEPT
           : nodeFilterConstants.FILTER_SKIP;
  }
  return nodeFilterConstants.FILTER_ACCEPT;
}

/**
 * Is the given node a text node composed of whitespace only?
 * @param {DOMNode} node
 * @return {Boolean}
 */
function isWhitespaceTextNode(node) {
  return node.nodeType == Ci.nsIDOMNode.TEXT_NODE && !/[^\s]/.exec(node.nodeValue);
}

/**
 * Does the given node have non-0 width and height?
 * @param {DOMNode} node
 * @return {Boolean}
 */
function nodeHasSize(node) {
  if (!node.getBoxQuads) {
    return false;
  }

  let quads = node.getBoxQuads();
  return quads.length && quads.some(quad => quad.bounds.width && quad.bounds.height);
}

/**
 * Returns a promise that is settled once the given HTMLImageElement has
 * finished loading.
 *
 * @param {HTMLImageElement} image - The image element.
 * @param {Number} timeout - Maximum amount of time the image is allowed to load
 * before the waiting is aborted. Ignored if flags.testing is set.
 *
 * @return {Promise} that is fulfilled once the image has loaded. If the image
 * fails to load or the load takes too long, the promise is rejected.
 */
function ensureImageLoaded(image, timeout) {
  let { HTMLImageElement } = image.ownerGlobal;
  if (!(image instanceof HTMLImageElement)) {
    return Promise.reject("image must be an HTMLImageELement");
  }

  if (image.complete) {
    // The image has already finished loading.
    return Promise.resolve();
  }

  // This image is still loading.
  let onLoad = AsyncUtils.listenOnce(image, "load");

  // Reject if loading fails.
  let onError = AsyncUtils.listenOnce(image, "error").then(() => {
    return Promise.reject("Image '" + image.src + "' failed to load.");
  });

  // Don't timeout when testing. This is never settled.
  let onAbort = new Promise(() => {});

  if (!flags.testing) {
    // Tests are not running. Reject the promise after given timeout.
    onAbort = DevToolsUtils.waitForTime(timeout).then(() => {
      return Promise.reject("Image '" + image.src + "' took too long to load.");
    });
  }

  // See which happens first.
  return Promise.race([onLoad, onError, onAbort]);
}

/**
 * Given an <img> or <canvas> element, return the image data-uri. If @param node
 * is an <img> element, the method waits a while for the image to load before
 * the data is generated. If the image does not finish loading in a reasonable
 * time (IMAGE_FETCHING_TIMEOUT milliseconds) the process aborts.
 *
 * @param {HTMLImageElement|HTMLCanvasElement} node - The <img> or <canvas>
 * element, or Image() object. Other types cause the method to reject.
 * @param {Number} maxDim - Optionally pass a maximum size you want the longest
 * side of the image to be resized to before getting the image data.

 * @return {Promise} A promise that is fulfilled with an object containing the
 * data-uri and size-related information:
 * { data: "...",
 *   size: {
 *     naturalWidth: 400,
 *     naturalHeight: 300,
 *     resized: true }
 *  }.
 *
 * If something goes wrong, the promise is rejected.
 */
const imageToImageData = async function (node, maxDim) {
  let { HTMLCanvasElement, HTMLImageElement } = node.ownerGlobal;

  let isImg = node instanceof HTMLImageElement;
  let isCanvas = node instanceof HTMLCanvasElement;

  if (!isImg && !isCanvas) {
    throw new Error("node is not a <canvas> or <img> element.");
  }

  if (isImg) {
    // Ensure that the image is ready.
    await ensureImageLoaded(node, IMAGE_FETCHING_TIMEOUT);
  }

  // Get the image resize ratio if a maxDim was provided
  let resizeRatio = 1;
  let imgWidth = node.naturalWidth || node.width;
  let imgHeight = node.naturalHeight || node.height;
  let imgMax = Math.max(imgWidth, imgHeight);
  if (maxDim && imgMax > maxDim) {
    resizeRatio = maxDim / imgMax;
  }

  // Extract the image data
  let imageData;
  // The image may already be a data-uri, in which case, save ourselves the
  // trouble of converting via the canvas.drawImage.toDataURL method, but only
  // if the image doesn't need resizing
  if (isImg && node.src.startsWith("data:") && resizeRatio === 1) {
    imageData = node.src;
  } else {
    // Create a canvas to copy the rawNode into and get the imageData from
    let canvas = node.ownerDocument.createElementNS(XHTML_NS, "canvas");
    canvas.width = imgWidth * resizeRatio;
    canvas.height = imgHeight * resizeRatio;
    let ctx = canvas.getContext("2d");

    // Copy the rawNode image or canvas in the new canvas and extract data
    ctx.drawImage(node, 0, 0, canvas.width, canvas.height);
    imageData = canvas.toDataURL("image/png");
  }

  return {
    data: imageData,
    size: {
      naturalWidth: imgWidth,
      naturalHeight: imgHeight,
      resized: resizeRatio !== 1
    }
  };
};

module.exports = {
  allAnonymousContentTreeWalkerFilter,
  getNodeDisplayName,
  imageToImageData,
  isNodeDead,
  nodeDocument,
  standardTreeWalkerFilter,
};
PK
!<�qTh_�_�Bchrome/devtools/modules/devtools/server/actors/inspector/walker.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {Ci, Cu} = require("chrome");

const Services = require("Services");
const protocol = require("devtools/shared/protocol");
const {walkerSpec} = require("devtools/shared/specs/inspector");
const {LongStringActor} = require("devtools/server/actors/string");
const InspectorUtils = require("InspectorUtils");

loader.lazyRequireGetter(this, "getFrameElement", "devtools/shared/layout/utils", true);
loader.lazyRequireGetter(this, "isAnonymous", "devtools/shared/layout/utils", true);
loader.lazyRequireGetter(this, "loadSheet", "devtools/shared/layout/utils", true);
loader.lazyRequireGetter(this, "throttle", "devtools/shared/throttle", true);

loader.lazyRequireGetter(this, "allAnonymousContentTreeWalkerFilter", "devtools/server/actors/inspector/utils", true);
loader.lazyRequireGetter(this, "isNodeDead", "devtools/server/actors/inspector/utils", true);
loader.lazyRequireGetter(this, "nodeDocument", "devtools/server/actors/inspector/utils", true);
loader.lazyRequireGetter(this, "standardTreeWalkerFilter", "devtools/server/actors/inspector/utils", true);

loader.lazyRequireGetter(this, "DocumentWalker", "devtools/server/actors/inspector/document-walker", true);
loader.lazyRequireGetter(this, "SKIP_TO_SIBLING", "devtools/server/actors/inspector/document-walker", true);
loader.lazyRequireGetter(this, "NodeActor", "devtools/server/actors/inspector/node", true);
loader.lazyRequireGetter(this, "NodeListActor", "devtools/server/actors/inspector/node", true);
loader.lazyRequireGetter(this, "LayoutActor", "devtools/server/actors/layout", true);
loader.lazyRequireGetter(this, "getLayoutChangesObserver", "devtools/server/actors/reflow", true);
loader.lazyRequireGetter(this, "releaseLayoutChangesObserver", "devtools/server/actors/reflow", true);
loader.lazyRequireGetter(this, "WalkerSearch", "devtools/server/actors/utils/walker-search", true);

loader.lazyServiceGetter(this, "eventListenerService",
  "@mozilla.org/eventlistenerservice;1", "nsIEventListenerService");

loader.lazyServiceGetter(this, "DOMParser",
  "@mozilla.org/xmlextras/domparser;1", "nsIDOMParser");

// Minimum delay between two "new-mutations" events.
const MUTATIONS_THROTTLING_DELAY = 100;
// List of mutation types that should -not- be throttled.
const IMMEDIATE_MUTATIONS = [
  "documentUnload",
  "frameLoad",
  "newRoot",
  "pseudoClassLock",
];

const HIDDEN_CLASS = "__fx-devtools-hide-shortcut__";

// The possible completions to a ':' with added score to give certain values
// some preference.
const PSEUDO_SELECTORS = [
  [":active", 1],
  [":hover", 1],
  [":focus", 1],
  [":visited", 0],
  [":link", 0],
  [":first-letter", 0],
  [":first-child", 2],
  [":before", 2],
  [":after", 2],
  [":lang(", 0],
  [":not(", 3],
  [":first-of-type", 0],
  [":last-of-type", 0],
  [":only-of-type", 0],
  [":only-child", 2],
  [":nth-child(", 3],
  [":nth-last-child(", 0],
  [":nth-of-type(", 0],
  [":nth-last-of-type(", 0],
  [":last-child", 2],
  [":root", 0],
  [":empty", 0],
  [":target", 0],
  [":enabled", 0],
  [":disabled", 0],
  [":checked", 1],
  ["::selection", 0]
];

const HELPER_SHEET = "data:text/css;charset=utf-8," + encodeURIComponent(`
  .__fx-devtools-hide-shortcut__ {
    visibility: hidden !important;
  }

  :-moz-devtools-highlighted {
    outline: 2px dashed #F06!important;
    outline-offset: -2px !important;
  }
`);

/**
 * We only send nodeValue up to a certain size by default.  This stuff
 * controls that size.
 */
exports.DEFAULT_VALUE_SUMMARY_LENGTH = 50;
var gValueSummaryLength = exports.DEFAULT_VALUE_SUMMARY_LENGTH;

exports.getValueSummaryLength = function () {
  return gValueSummaryLength;
};

exports.setValueSummaryLength = function (val) {
  gValueSummaryLength = val;
};

/**
 * Server side of the DOM walker.
 */
var WalkerActor = protocol.ActorClassWithSpec(walkerSpec, {
  /**
   * Create the WalkerActor
   * @param DebuggerServerConnection conn
   *    The server connection.
   */
  initialize: function (conn, tabActor, options) {
    protocol.Actor.prototype.initialize.call(this, conn);
    this.tabActor = tabActor;
    this.rootWin = tabActor.window;
    this.rootDoc = this.rootWin.document;
    this._refMap = new Map();
    this._pendingMutations = [];
    this._activePseudoClassLocks = new Set();
    this.showAllAnonymousContent = options.showAllAnonymousContent;

    this.walkerSearch = new WalkerSearch(this);

    // Nodes which have been removed from the client's known
    // ownership tree are considered "orphaned", and stored in
    // this set.
    this._orphaned = new Set();

    // The client can tell the walker that it is interested in a node
    // even when it is orphaned with the `retainNode` method.  This
    // list contains orphaned nodes that were so retained.
    this._retainedOrphans = new Set();

    this.onMutations = this.onMutations.bind(this);
    this.onFrameLoad = this.onFrameLoad.bind(this);
    this.onFrameUnload = this.onFrameUnload.bind(this);
    this._throttledEmitNewMutations = throttle(this._emitNewMutations.bind(this),
      MUTATIONS_THROTTLING_DELAY);

    tabActor.on("will-navigate", this.onFrameUnload);
    tabActor.on("window-ready", this.onFrameLoad);

    // Ensure that the root document node actor is ready and
    // managed.
    this.rootNode = this.document();

    this.layoutChangeObserver = getLayoutChangesObserver(this.tabActor);
    this._onReflows = this._onReflows.bind(this);
    this.layoutChangeObserver.on("reflows", this._onReflows);
    this._onResize = this._onResize.bind(this);
    this.layoutChangeObserver.on("resize", this._onResize);

    this._onEventListenerChange = this._onEventListenerChange.bind(this);
    eventListenerService.addListenerChangeListener(this._onEventListenerChange);
  },

  /**
   * Callback for eventListenerService.addListenerChangeListener
   * @param nsISimpleEnumerator changesEnum
   *    enumerator of nsIEventListenerChange
   */
  _onEventListenerChange: function (changesEnum) {
    let changes = changesEnum.enumerate();
    while (changes.hasMoreElements()) {
      let current = changes.getNext().QueryInterface(Ci.nsIEventListenerChange);
      let target = current.target;

      if (this._refMap.has(target)) {
        let actor = this.getNode(target);
        let mutation = {
          type: "events",
          target: actor.actorID,
          hasEventListeners: actor._hasEventListeners
        };
        this.queueMutation(mutation);
      }
    }
  },

  // Returns the JSON representation of this object over the wire.
  form: function () {
    return {
      actor: this.actorID,
      root: this.rootNode.form(),
      traits: {}
    };
  },

  toString: function () {
    return "[WalkerActor " + this.actorID + "]";
  },

  getDocumentWalker: function (node, whatToShow, skipTo) {
    // Allow native anon content (like <video> controls) if preffed on
    let nodeFilter = this.showAllAnonymousContent
                    ? allAnonymousContentTreeWalkerFilter
                    : standardTreeWalkerFilter;
    return new DocumentWalker(node, this.rootWin, whatToShow, nodeFilter, skipTo);
  },

  destroy: function () {
    if (this._destroyed) {
      return;
    }
    this._destroyed = true;
    protocol.Actor.prototype.destroy.call(this);
    try {
      this.clearPseudoClassLocks();
      this._activePseudoClassLocks = null;

      this._hoveredNode = null;
      this.rootWin = null;
      this.rootDoc = null;
      this.rootNode = null;
      this.layoutHelpers = null;
      this._orphaned = null;
      this._retainedOrphans = null;
      this._refMap = null;

      this.tabActor.off("will-navigate", this.onFrameUnload);
      this.tabActor.off("window-ready", this.onFrameLoad);

      this.onFrameLoad = null;
      this.onFrameUnload = null;

      this.walkerSearch.destroy();

      this.layoutChangeObserver.off("reflows", this._onReflows);
      this.layoutChangeObserver.off("resize", this._onResize);
      this.layoutChangeObserver = null;
      releaseLayoutChangesObserver(this.tabActor);

      eventListenerService.removeListenerChangeListener(
        this._onEventListenerChange);

      this.onMutations = null;

      this.layoutActor = null;
      this.tabActor = null;

      this.emit("destroyed");
    } catch (e) {
      console.error(e);
    }
  },

  release: function () {},

  unmanage: function (actor) {
    if (actor instanceof NodeActor) {
      if (this._activePseudoClassLocks &&
          this._activePseudoClassLocks.has(actor)) {
        this.clearPseudoClassLocks(actor);
      }
      this._refMap.delete(actor.rawNode);
    }
    protocol.Actor.prototype.unmanage.call(this, actor);
  },

  /**
   * Determine if the walker has come across this DOM node before.
   * @param {DOMNode} rawNode
   * @return {Boolean}
   */
  hasNode: function (rawNode) {
    return this._refMap.has(rawNode);
  },

  /**
   * If the walker has come across this DOM node before, then get the
   * corresponding node actor.
   * @param {DOMNode} rawNode
   * @return {NodeActor}
   */
  getNode: function (rawNode) {
    return this._refMap.get(rawNode);
  },

  _ref: function (node) {
    let actor = this.getNode(node);
    if (actor) {
      return actor;
    }

    actor = new NodeActor(this, node);

    // Add the node actor as a child of this walker actor, assigning
    // it an actorID.
    this.manage(actor);
    this._refMap.set(node, actor);

    if (node.nodeType === Ci.nsIDOMNode.DOCUMENT_NODE) {
      actor.watchDocument(this.onMutations);
    }
    return actor;
  },

  _onReflows: function (reflows) {
    // Going through the nodes the walker knows about, see which ones have
    // had their display changed and send a display-change event if any
    let changes = [];
    for (let [node, actor] of this._refMap) {
      if (Cu.isDeadWrapper(node)) {
        continue;
      }

      let displayType = actor.displayType;
      let isDisplayed = actor.isDisplayed;

      if (displayType !== actor.currentDisplayType ||
          isDisplayed !== actor.wasDisplayed) {
        changes.push(actor);

        // Updating the original value
        actor.currentDisplayType = displayType;
        actor.wasDisplayed = isDisplayed;
      }
    }

    if (changes.length) {
      this.emit("display-change", changes);
    }
  },

  /**
   * When the browser window gets resized, relay the event to the front.
   */
  _onResize: function () {
    this.emit("resize");
  },

  /**
   * Ensures that the node is attached and it can be accessed from the root.
   *
   * @param {(Node|NodeActor)} nodes The nodes
   * @return {Object} An object compatible with the disconnectedNode type.
   */
  attachElement: function (node) {
    let { nodes, newParents } = this.attachElements([node]);
    return {
      node: nodes[0],
      newParents: newParents
    };
  },

  /**
   * Ensures that the nodes are attached and they can be accessed from the root.
   *
   * @param {(Node[]|NodeActor[])} nodes The nodes
   * @return {Object} An object compatible with the disconnectedNodeArray type.
   */
  attachElements: function (nodes) {
    let nodeActors = [];
    let newParents = new Set();
    for (let node of nodes) {
      if (!(node instanceof NodeActor)) {
        // If an anonymous node was passed in and we aren't supposed to know
        // about it, then consult with the document walker as the source of
        // truth about which elements exist.
        if (!this.showAllAnonymousContent && isAnonymous(node)) {
          node = this.getDocumentWalker(node).currentNode;
        }

        node = this._ref(node);
      }

      this.ensurePathToRoot(node, newParents);
      // If nodes may be an array of raw nodes, we're sure to only have
      // NodeActors with the following array.
      nodeActors.push(node);
    }

    return {
      nodes: nodeActors,
      newParents: [...newParents]
    };
  },

  /**
   * Return the document node that contains the given node,
   * or the root node if no node is specified.
   * @param NodeActor node
   *        The node whose document is needed, or null to
   *        return the root.
   */
  document: function (node) {
    let doc = isNodeDead(node) ? this.rootDoc : nodeDocument(node.rawNode);
    return this._ref(doc);
  },

  /**
   * Return the documentElement for the document containing the
   * given node.
   * @param NodeActor node
   *        The node whose documentElement is requested, or null
   *        to use the root document.
   */
  documentElement: function (node) {
    let elt = isNodeDead(node)
              ? this.rootDoc.documentElement
              : nodeDocument(node.rawNode).documentElement;
    return this._ref(elt);
  },

  /**
   * Return all parents of the given node, ordered from immediate parent
   * to root.
   * @param NodeActor node
   *    The node whose parents are requested.
   * @param object options
   *    Named options, including:
   *    `sameDocument`: If true, parents will be restricted to the same
   *      document as the node.
   *    `sameTypeRootTreeItem`: If true, this will not traverse across
   *     different types of docshells.
   */
  parents: function (node, options = {}) {
    if (isNodeDead(node)) {
      return [];
    }

    let walker = this.getDocumentWalker(node.rawNode);
    let parents = [];
    let cur;
    while ((cur = walker.parentNode())) {
      if (options.sameDocument &&
          nodeDocument(cur) != nodeDocument(node.rawNode)) {
        break;
      }

      if (options.sameTypeRootTreeItem &&
          nodeDocshell(cur).sameTypeRootTreeItem !=
          nodeDocshell(node.rawNode).sameTypeRootTreeItem) {
        break;
      }

      parents.push(this._ref(cur));
    }
    return parents;
  },

  parentNode: function (node) {
    let walker = this.getDocumentWalker(node.rawNode);
    let parent = walker.parentNode();
    if (parent) {
      return this._ref(parent);
    }
    return null;
  },

  /**
   * If the given NodeActor only has a single text node as a child with a text
   * content small enough to be inlined, return that child's NodeActor.
   *
   * @param NodeActor node
   */
  inlineTextChild: function (node) {
    // Quick checks to prevent creating a new walker if possible.
    if (node.isBeforePseudoElement ||
        node.isAfterPseudoElement ||
        node.rawNode.nodeType != Ci.nsIDOMNode.ELEMENT_NODE ||
        node.rawNode.children.length > 0) {
      return undefined;
    }

    let docWalker = this.getDocumentWalker(node.rawNode);
    let firstChild = docWalker.firstChild();

    // Bail out if:
    // - more than one child
    // - unique child is not a text node
    // - unique child is a text node, but is too long to be inlined
    if (!firstChild ||
        docWalker.nextSibling() ||
        firstChild.nodeType !== Ci.nsIDOMNode.TEXT_NODE ||
        firstChild.nodeValue.length > gValueSummaryLength
        ) {
      return undefined;
    }

    return this._ref(firstChild);
  },

  /**
   * Mark a node as 'retained'.
   *
   * A retained node is not released when `releaseNode` is called on its
   * parent, or when a parent is released with the `cleanup` option to
   * `getMutations`.
   *
   * When a retained node's parent is released, a retained mode is added to
   * the walker's "retained orphans" list.
   *
   * Retained nodes can be deleted by providing the `force` option to
   * `releaseNode`.  They will also be released when their document
   * has been destroyed.
   *
   * Retaining a node makes no promise about its children;  They can
   * still be removed by normal means.
   */
  retainNode: function (node) {
    node.retained = true;
  },

  /**
   * Remove the 'retained' mark from a node.  If the node was a
   * retained orphan, release it.
   */
  unretainNode: function (node) {
    node.retained = false;
    if (this._retainedOrphans.has(node)) {
      this._retainedOrphans.delete(node);
      this.releaseNode(node);
    }
  },

  /**
   * Release actors for a node and all child nodes.
   */
  releaseNode: function (node, options = {}) {
    if (isNodeDead(node)) {
      return;
    }

    if (node.retained && !options.force) {
      this._retainedOrphans.add(node);
      return;
    }

    if (node.retained) {
      // Forcing a retained node to go away.
      this._retainedOrphans.delete(node);
    }

    let walker = this.getDocumentWalker(node.rawNode);

    let child = walker.firstChild();
    while (child) {
      let childActor = this.getNode(child);
      if (childActor) {
        this.releaseNode(childActor, options);
      }
      child = walker.nextSibling();
    }

    node.destroy();
  },

  /**
   * Add any nodes between `node` and the walker's root node that have not
   * yet been seen by the client.
   */
  ensurePathToRoot: function (node, newParents = new Set()) {
    if (!node) {
      return newParents;
    }
    let walker = this.getDocumentWalker(node.rawNode);
    let cur;
    while ((cur = walker.parentNode())) {
      let parent = this.getNode(cur);
      if (!parent) {
        // This parent didn't exist, so hasn't been seen by the client yet.
        newParents.add(this._ref(cur));
      } else {
        // This parent did exist, so the client knows about it.
        return newParents;
      }
    }
    return newParents;
  },

  /**
   * Return children of the given node.  By default this method will return
   * all children of the node, but there are options that can restrict this
   * to a more manageable subset.
   *
   * @param NodeActor node
   *    The node whose children you're curious about.
   * @param object options
   *    Named options:
   *    `maxNodes`: The set of nodes returned by the method will be no longer
   *       than maxNodes.
   *    `start`: If a node is specified, the list of nodes will start
   *       with the given child.  Mutally exclusive with `center`.
   *    `center`: If a node is specified, the given node will be as centered
   *       as possible in the list, given how close to the ends of the child
   *       list it is.  Mutually exclusive with `start`.
   *    `whatToShow`: A bitmask of node types that should be included.  See
   *       https://developer.mozilla.org/en-US/docs/Web/API/NodeFilter.
   *
   * @returns an object with three items:
   *    hasFirst: true if the first child of the node is included in the list.
   *    hasLast: true if the last child of the node is included in the list.
   *    nodes: Child nodes returned by the request.
   */
  children: function (node, options = {}) {
    if (isNodeDead(node)) {
      return { hasFirst: true, hasLast: true, nodes: [] };
    }

    if (options.center && options.start) {
      throw Error("Can't specify both 'center' and 'start' options.");
    }
    let maxNodes = options.maxNodes || -1;
    if (maxNodes == -1) {
      maxNodes = Number.MAX_VALUE;
    }

    // We're going to create a few document walkers with the same filter,
    // make it easier.
    let getFilteredWalker = documentWalkerNode => {
      let { whatToShow } = options;
      // Use SKIP_TO_SIBLING to force the walker to use a sibling of the provided node
      // in case this one is incompatible with the walker's filter function.
      return this.getDocumentWalker(documentWalkerNode, whatToShow, SKIP_TO_SIBLING);
    };

    // Need to know the first and last child.
    let rawNode = node.rawNode;
    let firstChild = getFilteredWalker(rawNode).firstChild();
    let lastChild = getFilteredWalker(rawNode).lastChild();

    if (!firstChild) {
      // No children, we're done.
      return { hasFirst: true, hasLast: true, nodes: [] };
    }

    let start;
    if (options.center) {
      start = options.center.rawNode;
    } else if (options.start) {
      start = options.start.rawNode;
    } else {
      start = firstChild;
    }

    let nodes = [];

    // Start by reading backward from the starting point if we're centering...
    let backwardWalker = getFilteredWalker(start);
    if (backwardWalker.currentNode != firstChild && options.center) {
      backwardWalker.previousSibling();
      let backwardCount = Math.floor(maxNodes / 2);
      let backwardNodes = this._readBackward(backwardWalker, backwardCount);
      nodes = backwardNodes;
    }

    // Then read forward by any slack left in the max children...
    let forwardWalker = getFilteredWalker(start);
    let forwardCount = maxNodes - nodes.length;
    nodes = nodes.concat(this._readForward(forwardWalker, forwardCount));

    // If there's any room left, it means we've run all the way to the end.
    // If we're centering, check if there are more items to read at the front.
    let remaining = maxNodes - nodes.length;
    if (options.center && remaining > 0 && nodes[0].rawNode != firstChild) {
      let firstNodes = this._readBackward(backwardWalker, remaining);

      // Then put it all back together.
      nodes = firstNodes.concat(nodes);
    }

    return {
      hasFirst: nodes[0].rawNode == firstChild,
      hasLast: nodes[nodes.length - 1].rawNode == lastChild,
      nodes: nodes
    };
  },

  /**
   * Return siblings of the given node.  By default this method will return
   * all siblings of the node, but there are options that can restrict this
   * to a more manageable subset.
   *
   * If `start` or `center` are not specified, this method will center on the
   * node whose siblings are requested.
   *
   * @param NodeActor node
   *    The node whose children you're curious about.
   * @param object options
   *    Named options:
   *    `maxNodes`: The set of nodes returned by the method will be no longer
   *       than maxNodes.
   *    `start`: If a node is specified, the list of nodes will start
   *       with the given child.  Mutally exclusive with `center`.
   *    `center`: If a node is specified, the given node will be as centered
   *       as possible in the list, given how close to the ends of the child
   *       list it is.  Mutually exclusive with `start`.
   *    `whatToShow`: A bitmask of node types that should be included.  See
   *       https://developer.mozilla.org/en-US/docs/Web/API/NodeFilter.
   *
   * @returns an object with three items:
   *    hasFirst: true if the first child of the node is included in the list.
   *    hasLast: true if the last child of the node is included in the list.
   *    nodes: Child nodes returned by the request.
   */
  siblings: function (node, options = {}) {
    if (isNodeDead(node)) {
      return { hasFirst: true, hasLast: true, nodes: [] };
    }

    let parentNode = this.getDocumentWalker(node.rawNode, options.whatToShow)
                         .parentNode();
    if (!parentNode) {
      return {
        hasFirst: true,
        hasLast: true,
        nodes: [node]
      };
    }

    if (!(options.start || options.center)) {
      options.center = node;
    }

    return this.children(this._ref(parentNode), options);
  },

  /**
   * Get the next sibling of a given node.  Getting nodes one at a time
   * might be inefficient, be careful.
   *
   * @param object options
   *    Named options:
   *    `whatToShow`: A bitmask of node types that should be included.  See
   *       https://developer.mozilla.org/en-US/docs/Web/API/NodeFilter.
   */
  nextSibling: function (node, options = {}) {
    if (isNodeDead(node)) {
      return null;
    }

    let walker = this.getDocumentWalker(node.rawNode, options.whatToShow);
    let sibling = walker.nextSibling();
    return sibling ? this._ref(sibling) : null;
  },

  /**
   * Get the previous sibling of a given node.  Getting nodes one at a time
   * might be inefficient, be careful.
   *
   * @param object options
   *    Named options:
   *    `whatToShow`: A bitmask of node types that should be included.  See
   *       https://developer.mozilla.org/en-US/docs/Web/API/NodeFilter.
   */
  previousSibling: function (node, options = {}) {
    if (isNodeDead(node)) {
      return null;
    }

    let walker = this.getDocumentWalker(node.rawNode, options.whatToShow);
    let sibling = walker.previousSibling();
    return sibling ? this._ref(sibling) : null;
  },

  /**
   * Helper function for the `children` method: Read forward in the sibling
   * list into an array with `count` items, including the current node.
   */
  _readForward: function (walker, count) {
    let ret = [];

    let node = walker.currentNode;
    do {
      if (!walker.isSkippedNode(node)) {
        // The walker can be on a node that would be filtered out if it didn't find any
        // other node to fallback to.
        ret.push(this._ref(node));
      }
      node = walker.nextSibling();
    } while (node && --count);
    return ret;
  },

  /**
   * Helper function for the `children` method: Read backward in the sibling
   * list into an array with `count` items, including the current node.
   */
  _readBackward: function (walker, count) {
    let ret = [];

    let node = walker.currentNode;
    do {
      if (!walker.isSkippedNode(node)) {
        // The walker can be on a node that would be filtered out if it didn't find any
        // other node to fallback to.
        ret.push(this._ref(node));
      }
      node = walker.previousSibling();
    } while (node && --count);
    ret.reverse();
    return ret;
  },

  /**
   * Return the first node in the document that matches the given selector.
   * See https://developer.mozilla.org/en-US/docs/Web/API/Element.querySelector
   *
   * @param NodeActor baseNode
   * @param string selector
   */
  querySelector: function (baseNode, selector) {
    if (isNodeDead(baseNode)) {
      return {};
    }

    let node = baseNode.rawNode.querySelector(selector);
    if (!node) {
      return {};
    }

    return this.attachElement(node);
  },

  /**
   * Return a NodeListActor with all nodes that match the given selector.
   * See https://developer.mozilla.org/en-US/docs/Web/API/Element.querySelectorAll
   *
   * @param NodeActor baseNode
   * @param string selector
   */
  querySelectorAll: function (baseNode, selector) {
    let nodeList = null;

    try {
      nodeList = baseNode.rawNode.querySelectorAll(selector);
    } catch (e) {
      // Bad selector. Do nothing as the selector can come from a searchbox.
    }

    return new NodeListActor(this, nodeList);
  },

  /**
   * Get a list of nodes that match the given selector in all known frames of
   * the current content page.
   * @param {String} selector.
   * @return {Array}
   */
  _multiFrameQuerySelectorAll: function (selector) {
    let nodes = [];

    for (let {document} of this.tabActor.windows) {
      try {
        nodes = [...nodes, ...document.querySelectorAll(selector)];
      } catch (e) {
        // Bad selector. Do nothing as the selector can come from a searchbox.
      }
    }

    return nodes;
  },

  /**
   * Return a NodeListActor with all nodes that match the given selector in all
   * frames of the current content page.
   * @param {String} selector
   */
  multiFrameQuerySelectorAll: function (selector) {
    return new NodeListActor(this, this._multiFrameQuerySelectorAll(selector));
  },

  /**
   * Search the document for a given string.
   * Results will be searched with the walker-search module (searches through
   * tag names, attribute names and values, and text contents).
   *
   * @returns {searchresult}
   *            - {NodeList} list
   *            - {Array<Object>} metadata. Extra information with indices that
   *                              match up with node list.
   */
  search: function (query) {
    let results = this.walkerSearch.search(query);
    let nodeList = new NodeListActor(this, results.map(r => r.node));

    return {
      list: nodeList,
      metadata: []
    };
  },

  /**
   * Returns a list of matching results for CSS selector autocompletion.
   *
   * @param string query
   *        The selector query being completed
   * @param string completing
   *        The exact token being completed out of the query
   * @param string selectorState
   *        One of "pseudo", "id", "tag", "class", "null"
   */
  getSuggestionsForQuery: function (query, completing, selectorState) {
    let sugs = {
      classes: new Map(),
      tags: new Map(),
      ids: new Map()
    };
    let result = [];
    let nodes = null;
    // Filtering and sorting the results so that protocol transfer is miminal.
    switch (selectorState) {
      case "pseudo":
        result = PSEUDO_SELECTORS.filter(item => {
          return item[0].startsWith(":" + completing);
        });
        break;

      case "class":
        if (!query) {
          nodes = this._multiFrameQuerySelectorAll("[class]");
        } else {
          nodes = this._multiFrameQuerySelectorAll(query);
        }
        for (let node of nodes) {
          for (let className of node.classList) {
            sugs.classes.set(className, (sugs.classes.get(className)|0) + 1);
          }
        }
        sugs.classes.delete("");
        sugs.classes.delete(HIDDEN_CLASS);
        for (let [className, count] of sugs.classes) {
          if (className.startsWith(completing)) {
            result.push(["." + CSS.escape(className), count, selectorState]);
          }
        }
        break;

      case "id":
        if (!query) {
          nodes = this._multiFrameQuerySelectorAll("[id]");
        } else {
          nodes = this._multiFrameQuerySelectorAll(query);
        }
        for (let node of nodes) {
          sugs.ids.set(node.id, (sugs.ids.get(node.id)|0) + 1);
        }
        for (let [id, count] of sugs.ids) {
          if (id.startsWith(completing) && id !== "") {
            result.push(["#" + CSS.escape(id), count, selectorState]);
          }
        }
        break;

      case "tag":
        if (!query) {
          nodes = this._multiFrameQuerySelectorAll("*");
        } else {
          nodes = this._multiFrameQuerySelectorAll(query);
        }
        for (let node of nodes) {
          let tag = node.localName;
          sugs.tags.set(tag, (sugs.tags.get(tag)|0) + 1);
        }
        for (let [tag, count] of sugs.tags) {
          if ((new RegExp("^" + completing + ".*", "i")).test(tag)) {
            result.push([tag, count, selectorState]);
          }
        }

        // For state 'tag' (no preceding # or .) and when there's no query (i.e.
        // only one word) then search for the matching classes and ids
        if (!query) {
          result = [
            ...result,
            ...this.getSuggestionsForQuery(null, completing, "class")
                   .suggestions,
            ...this.getSuggestionsForQuery(null, completing, "id")
                   .suggestions
          ];
        }

        break;

      case "null":
        nodes = this._multiFrameQuerySelectorAll(query);
        for (let node of nodes) {
          sugs.ids.set(node.id, (sugs.ids.get(node.id)|0) + 1);
          let tag = node.localName;
          sugs.tags.set(tag, (sugs.tags.get(tag)|0) + 1);
          for (let className of node.classList) {
            sugs.classes.set(className, (sugs.classes.get(className)|0) + 1);
          }
        }
        for (let [tag, count] of sugs.tags) {
          tag && result.push([tag, count]);
        }
        for (let [id, count] of sugs.ids) {
          id && result.push(["#" + id, count]);
        }
        sugs.classes.delete("");
        sugs.classes.delete(HIDDEN_CLASS);
        for (let [className, count] of sugs.classes) {
          className && result.push(["." + className, count]);
        }
    }

    // Sort by count (desc) and name (asc)
    result = result.sort((a, b) => {
      // Computed a sortable string with first the inverted count, then the name
      let sortA = (10000 - a[1]) + a[0];
      let sortB = (10000 - b[1]) + b[0];

      // Prefixing ids, classes and tags, to group results
      let firstA = a[0].substring(0, 1);
      let firstB = b[0].substring(0, 1);

      if (firstA === "#") {
        sortA = "2" + sortA;
      } else if (firstA === ".") {
        sortA = "1" + sortA;
      } else {
        sortA = "0" + sortA;
      }

      if (firstB === "#") {
        sortB = "2" + sortB;
      } else if (firstB === ".") {
        sortB = "1" + sortB;
      } else {
        sortB = "0" + sortB;
      }

      // String compare
      return sortA.localeCompare(sortB);
    });

    result.slice(0, 25);

    return {
      query: query,
      suggestions: result
    };
  },

  /**
   * Add a pseudo-class lock to a node.
   *
   * @param NodeActor node
   * @param string pseudo
   *    A pseudoclass: ':hover', ':active', ':focus'
   * @param options
   *    Options object:
   *    `parents`: True if the pseudo-class should be added
   *      to parent nodes.
   *    `enabled`: False if the pseudo-class should be locked
   *      to 'off'. Defaults to true.
   *
   * @returns An empty packet.  A "pseudoClassLock" mutation will
   *    be queued for any changed nodes.
   */
  addPseudoClassLock: function (node, pseudo, options = {}) {
    if (isNodeDead(node)) {
      return;
    }

    // There can be only one node locked per pseudo, so dismiss all existing
    // ones
    for (let locked of this._activePseudoClassLocks) {
      if (InspectorUtils.hasPseudoClassLock(locked.rawNode, pseudo)) {
        this._removePseudoClassLock(locked, pseudo);
      }
    }

    let enabled = options.enabled === undefined ||
                  options.enabled;
    this._addPseudoClassLock(node, pseudo, enabled);

    if (!options.parents) {
      return;
    }

    let walker = this.getDocumentWalker(node.rawNode);
    let cur;
    while ((cur = walker.parentNode())) {
      let curNode = this._ref(cur);
      this._addPseudoClassLock(curNode, pseudo, enabled);
    }
  },

  _queuePseudoClassMutation: function (node) {
    this.queueMutation({
      target: node.actorID,
      type: "pseudoClassLock",
      pseudoClassLocks: node.writePseudoClassLocks()
    });
  },

  _addPseudoClassLock: function (node, pseudo, enabled) {
    if (node.rawNode.nodeType !== Ci.nsIDOMNode.ELEMENT_NODE) {
      return false;
    }
    InspectorUtils.addPseudoClassLock(node.rawNode, pseudo, enabled);
    this._activePseudoClassLocks.add(node);
    this._queuePseudoClassMutation(node);
    return true;
  },

  hideNode: function (node) {
    if (isNodeDead(node)) {
      return;
    }

    loadSheet(node.rawNode.ownerGlobal, HELPER_SHEET);
    node.rawNode.classList.add(HIDDEN_CLASS);
  },

  unhideNode: function (node) {
    if (isNodeDead(node)) {
      return;
    }

    node.rawNode.classList.remove(HIDDEN_CLASS);
  },

  /**
   * Remove a pseudo-class lock from a node.
   *
   * @param NodeActor node
   * @param string pseudo
   *    A pseudoclass: ':hover', ':active', ':focus'
   * @param options
   *    Options object:
   *    `parents`: True if the pseudo-class should be removed
   *      from parent nodes.
   *
   * @returns An empty response.  "pseudoClassLock" mutations
   *    will be emitted for any changed nodes.
   */
  removePseudoClassLock: function (node, pseudo, options = {}) {
    if (isNodeDead(node)) {
      return;
    }

    this._removePseudoClassLock(node, pseudo);

    // Remove pseudo class for children as we don't want to allow
    // turning it on for some childs without setting it on some parents
    for (let locked of this._activePseudoClassLocks) {
      if (node.rawNode.contains(locked.rawNode) &&
          InspectorUtils.hasPseudoClassLock(locked.rawNode, pseudo)) {
        this._removePseudoClassLock(locked, pseudo);
      }
    }

    if (!options.parents) {
      return;
    }

    let walker = this.getDocumentWalker(node.rawNode);
    let cur;
    while ((cur = walker.parentNode())) {
      let curNode = this._ref(cur);
      this._removePseudoClassLock(curNode, pseudo);
    }
  },

  _removePseudoClassLock: function (node, pseudo) {
    if (node.rawNode.nodeType != Ci.nsIDOMNode.ELEMENT_NODE) {
      return false;
    }
    InspectorUtils.removePseudoClassLock(node.rawNode, pseudo);
    if (!node.writePseudoClassLocks()) {
      this._activePseudoClassLocks.delete(node);
    }

    this._queuePseudoClassMutation(node);
    return true;
  },

  /**
   * Clear all the pseudo-classes on a given node or all nodes.
   * @param {NodeActor} node Optional node to clear pseudo-classes on
   */
  clearPseudoClassLocks: function (node) {
    if (node && isNodeDead(node)) {
      return;
    }

    if (node) {
      InspectorUtils.clearPseudoClassLocks(node.rawNode);
      this._activePseudoClassLocks.delete(node);
      this._queuePseudoClassMutation(node);
    } else {
      for (let locked of this._activePseudoClassLocks) {
        InspectorUtils.clearPseudoClassLocks(locked.rawNode);
        this._activePseudoClassLocks.delete(locked);
        this._queuePseudoClassMutation(locked);
      }
    }
  },

  /**
   * Get a node's innerHTML property.
   */
  innerHTML: function (node) {
    let html = "";
    if (!isNodeDead(node)) {
      html = node.rawNode.innerHTML;
    }
    return LongStringActor(this.conn, html);
  },

  /**
   * Set a node's innerHTML property.
   *
   * @param {NodeActor} node The node.
   * @param {string} value The piece of HTML content.
   */
  setInnerHTML: function (node, value) {
    if (isNodeDead(node)) {
      return;
    }

    let rawNode = node.rawNode;
    if (rawNode.nodeType !== rawNode.ownerDocument.ELEMENT_NODE) {
      throw new Error("Can only change innerHTML to element nodes");
    }
    // eslint-disable-next-line no-unsanitized/property
    rawNode.innerHTML = value;
  },

  /**
   * Get a node's outerHTML property.
   *
   * @param {NodeActor} node The node.
   */
  outerHTML: function (node) {
    let outerHTML = "";
    if (!isNodeDead(node)) {
      outerHTML = node.rawNode.outerHTML;
    }
    return LongStringActor(this.conn, outerHTML);
  },

  /**
   * Set a node's outerHTML property.
   *
   * @param {NodeActor} node The node.
   * @param {string} value The piece of HTML content.
   */
  setOuterHTML: function (node, value) {
    if (isNodeDead(node)) {
      return;
    }

    let parsedDOM = DOMParser.parseFromString(value, "text/html");
    let rawNode = node.rawNode;
    let parentNode = rawNode.parentNode;

    // Special case for head and body.  Setting document.body.outerHTML
    // creates an extra <head> tag, and document.head.outerHTML creates
    // an extra <body>.  So instead we will call replaceChild with the
    // parsed DOM, assuming that they aren't trying to set both tags at once.
    if (rawNode.tagName === "BODY") {
      if (parsedDOM.head.innerHTML === "") {
        parentNode.replaceChild(parsedDOM.body, rawNode);
      } else {
      // eslint-disable-next-line no-unsanitized/property
        rawNode.outerHTML = value;
      }
    } else if (rawNode.tagName === "HEAD") {
      if (parsedDOM.body.innerHTML === "") {
        parentNode.replaceChild(parsedDOM.head, rawNode);
      } else {
        // eslint-disable-next-line no-unsanitized/property
        rawNode.outerHTML = value;
      }
    } else if (node.isDocumentElement()) {
      // Unable to set outerHTML on the document element.  Fall back by
      // setting attributes manually, then replace the body and head elements.
      let finalAttributeModifications = [];
      let attributeModifications = {};
      for (let attribute of rawNode.attributes) {
        attributeModifications[attribute.name] = null;
      }
      for (let attribute of parsedDOM.documentElement.attributes) {
        attributeModifications[attribute.name] = attribute.value;
      }
      for (let key in attributeModifications) {
        finalAttributeModifications.push({
          attributeName: key,
          newValue: attributeModifications[key]
        });
      }
      node.modifyAttributes(finalAttributeModifications);
      rawNode.replaceChild(parsedDOM.head, rawNode.querySelector("head"));
      rawNode.replaceChild(parsedDOM.body, rawNode.querySelector("body"));
    } else {
      // eslint-disable-next-line no-unsanitized/property
      rawNode.outerHTML = value;
    }
  },

  /**
   * Insert adjacent HTML to a node.
   *
   * @param {Node} node
   * @param {string} position One of "beforeBegin", "afterBegin", "beforeEnd",
   *                          "afterEnd" (see Element.insertAdjacentHTML).
   * @param {string} value The HTML content.
   */
  insertAdjacentHTML: function (node, position, value) {
    if (isNodeDead(node)) {
      return {node: [], newParents: []};
    }

    let rawNode = node.rawNode;
    let isInsertAsSibling = position === "beforeBegin" ||
      position === "afterEnd";

    // Don't insert anything adjacent to the document element.
    if (isInsertAsSibling && node.isDocumentElement()) {
      throw new Error("Can't insert adjacent element to the root.");
    }

    let rawParentNode = rawNode.parentNode;
    if (!rawParentNode && isInsertAsSibling) {
      throw new Error("Can't insert as sibling without parent node.");
    }

    // We can't use insertAdjacentHTML, because we want to return the nodes
    // being created (so the front can remove them if the user undoes
    // the change). So instead, use Range.createContextualFragment().
    let range = rawNode.ownerDocument.createRange();
    if (position === "beforeBegin" || position === "afterEnd") {
      range.selectNode(rawNode);
    } else {
      range.selectNodeContents(rawNode);
    }
    let docFrag = range.createContextualFragment(value);
    let newRawNodes = Array.from(docFrag.childNodes);
    switch (position) {
      case "beforeBegin":
        rawParentNode.insertBefore(docFrag, rawNode);
        break;
      case "afterEnd":
        // Note: if the second argument is null, rawParentNode.insertBefore
        // behaves like rawParentNode.appendChild.
        rawParentNode.insertBefore(docFrag, rawNode.nextSibling);
        break;
      case "afterBegin":
        rawNode.insertBefore(docFrag, rawNode.firstChild);
        break;
      case "beforeEnd":
        rawNode.appendChild(docFrag);
        break;
      default:
        throw new Error("Invalid position value. Must be either " +
          "'beforeBegin', 'beforeEnd', 'afterBegin' or 'afterEnd'.");
    }

    return this.attachElements(newRawNodes);
  },

  /**
   * Duplicate a specified node
   *
   * @param {NodeActor} node The node to duplicate.
   */
  duplicateNode: function ({rawNode}) {
    let clonedNode = rawNode.cloneNode(true);
    rawNode.parentNode.insertBefore(clonedNode, rawNode.nextSibling);
  },

  /**
   * Test whether a node is a document or a document element.
   *
   * @param {NodeActor} node The node to remove.
   * @return {boolean} True if the node is a document or a document element.
   */
  isDocumentOrDocumentElementNode: function (node) {
    return ((node.rawNode.ownerDocument &&
      node.rawNode.ownerDocument.documentElement === this.rawNode) ||
      node.rawNode.nodeType === Ci.nsIDOMNode.DOCUMENT_NODE);
  },

  /**
   * Removes a node from its parent node.
   *
   * @param {NodeActor} node The node to remove.
   * @returns The node's nextSibling before it was removed.
   */
  removeNode: function (node) {
    if (isNodeDead(node) || this.isDocumentOrDocumentElementNode(node)) {
      throw Error("Cannot remove document, document elements or dead nodes.");
    }

    let nextSibling = this.nextSibling(node);
    node.rawNode.remove();
    // Mutation events will take care of the rest.
    return nextSibling;
  },

  /**
   * Removes an array of nodes from their parent node.
   *
   * @param {NodeActor[]} nodes The nodes to remove.
   */
  removeNodes: function (nodes) {
    // Check that all nodes are valid before processing the removals.
    for (let node of nodes) {
      if (isNodeDead(node) || this.isDocumentOrDocumentElementNode(node)) {
        throw Error("Cannot remove document, document elements or dead nodes");
      }
    }

    for (let node of nodes) {
      node.rawNode.remove();
      // Mutation events will take care of the rest.
    }
  },

  /**
   * Insert a node into the DOM.
   */
  insertBefore: function (node, parent, sibling) {
    if (isNodeDead(node) ||
        isNodeDead(parent) ||
        (sibling && isNodeDead(sibling))) {
      return;
    }

    let rawNode = node.rawNode;
    let rawParent = parent.rawNode;
    let rawSibling = sibling ? sibling.rawNode : null;

    // Don't bother inserting a node if the document position isn't going
    // to change. This prevents needless iframes reloading and mutations.
    if (rawNode.parentNode === rawParent) {
      let currentNextSibling = this.nextSibling(node);
      currentNextSibling = currentNextSibling ? currentNextSibling.rawNode :
                                                null;

      if (rawNode === rawSibling || currentNextSibling === rawSibling) {
        return;
      }
    }

    rawParent.insertBefore(rawNode, rawSibling);
  },

  /**
   * Editing a node's tagname actually means creating a new node with the same
   * attributes, removing the node and inserting the new one instead.
   * This method does not return anything as mutation events are taking care of
   * informing the consumers about changes.
   */
  editTagName: function (node, tagName) {
    if (isNodeDead(node)) {
      return null;
    }

    let oldNode = node.rawNode;

    // Create a new element with the same attributes as the current element and
    // prepare to replace the current node with it.
    let newNode;
    try {
      newNode = nodeDocument(oldNode).createElement(tagName);
    } catch (x) {
      // Failed to create a new element with that tag name, ignore the change,
      // and signal the error to the front.
      return Promise.reject(new Error("Could not change node's tagName to " + tagName));
    }

    let attrs = oldNode.attributes;
    for (let i = 0; i < attrs.length; i++) {
      newNode.setAttribute(attrs[i].name, attrs[i].value);
    }

    // Insert the new node, and transfer the old node's children.
    oldNode.parentNode.insertBefore(newNode, oldNode);
    while (oldNode.firstChild) {
      newNode.appendChild(oldNode.firstChild);
    }

    oldNode.remove();
    return null;
  },

  /**
   * Get any pending mutation records.  Must be called by the client after
   * the `new-mutations` notification is received.  Returns an array of
   * mutation records.
   *
   * Mutation records have a basic structure:
   *
   * {
   *   type: attributes|characterData|childList,
   *   target: <domnode actor ID>,
   * }
   *
   * And additional attributes based on the mutation type:
   *
   * `attributes` type:
   *   attributeName: <string> - the attribute that changed
   *   attributeNamespace: <string> - the attribute's namespace URI, if any.
   *   newValue: <string> - The new value of the attribute, if any.
   *
   * `characterData` type:
   *   newValue: <string> - the new nodeValue for the node
   *
   * `childList` type is returned when the set of children for a node
   * has changed.  Includes extra data, which can be used by the client to
   * maintain its ownership subtree.
   *
   *   added: array of <domnode actor ID> - The list of actors *previously
   *     seen by the client* that were added to the target node.
   *   removed: array of <domnode actor ID> The list of actors *previously
   *     seen by the client* that were removed from the target node.
   *   inlineTextChild: If the node now has a single text child, it will
   *     be sent here.
   *
   * Actors that are included in a MutationRecord's `removed` but
   * not in an `added` have been removed from the client's ownership
   * tree (either by being moved under a node the client has seen yet
   * or by being removed from the tree entirely), and is considered
   * 'orphaned'.
   *
   * Keep in mind that if a node that the client hasn't seen is moved
   * into or out of the target node, it will not be included in the
   * removedNodes and addedNodes list, so if the client is interested
   * in the new set of children it needs to issue a `children` request.
   */
  getMutations: function (options = {}) {
    let pending = this._pendingMutations || [];
    this._pendingMutations = [];
    this._waitingForGetMutations = false;

    if (options.cleanup) {
      for (let node of this._orphaned) {
        // Release the orphaned node.  Nodes or children that have been
        // retained will be moved to this._retainedOrphans.
        this.releaseNode(node);
      }
      this._orphaned = new Set();
    }

    return pending;
  },

  queueMutation: function (mutation) {
    if (!this.actorID || this._destroyed) {
      // We've been destroyed, don't bother queueing this mutation.
      return;
    }

    // Add the mutation to the list of mutations to be retrieved next.
    this._pendingMutations.push(mutation);

    // Bail out if we already emitted a new-mutations event and are waiting for a client
    // to retrieve them.
    if (this._waitingForGetMutations) {
      return;
    }

    if (IMMEDIATE_MUTATIONS.includes(mutation.type)) {
      this._emitNewMutations();
    } else {
      /**
       * If many mutations are fired at the same time, clients might sequentially request
       * children/siblings for updated nodes, which can be costly. By throttling the calls
       * to getMutations, duplicated mutations will be ignored.
       */
      this._throttledEmitNewMutations();
    }
  },

  _emitNewMutations: function () {
    if (!this.actorID || this._destroyed) {
      // Bail out if the actor was destroyed after throttling this call.
      return;
    }

    if (this._waitingForGetMutations || this._pendingMutations.length == 0) {
      // Bail out if we already fired the new-mutation event or if no mutations are
      // waiting to be retrieved.
      return;
    }

    this._waitingForGetMutations = true;
    this.emit("new-mutations");
  },

  /**
   * Handles mutations from the DOM mutation observer API.
   *
   * @param array[MutationRecord] mutations
   *    See https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver#MutationRecord
   */
  onMutations: function (mutations) {
    // Notify any observers that want *all* mutations (even on nodes that aren't
    // referenced).  This is not sent over the protocol so can only be used by
    // scripts running in the server process.
    this.emit("any-mutation");

    for (let change of mutations) {
      let targetActor = this.getNode(change.target);
      if (!targetActor) {
        continue;
      }
      let targetNode = change.target;
      let type = change.type;
      let mutation = {
        type: type,
        target: targetActor.actorID,
      };

      if (type === "attributes") {
        mutation.attributeName = change.attributeName;
        mutation.attributeNamespace = change.attributeNamespace || undefined;
        mutation.newValue = targetNode.hasAttribute(mutation.attributeName) ?
                            targetNode.getAttribute(mutation.attributeName)
                            : null;
      } else if (type === "characterData") {
        mutation.newValue = targetNode.nodeValue;
        this._maybeQueueInlineTextChildMutation(change, targetNode);
      } else if (type === "childList" || type === "nativeAnonymousChildList") {
        // Get the list of removed and added actors that the client has seen
        // so that it can keep its ownership tree up to date.
        let removedActors = [];
        let addedActors = [];
        for (let removed of change.removedNodes) {
          let removedActor = this.getNode(removed);
          if (!removedActor) {
            // If the client never encountered this actor we don't need to
            // mention that it was removed.
            continue;
          }
          // While removed from the tree, nodes are saved as orphaned.
          this._orphaned.add(removedActor);
          removedActors.push(removedActor.actorID);
        }
        for (let added of change.addedNodes) {
          let addedActor = this.getNode(added);
          if (!addedActor) {
            // If the client never encounted this actor we don't need to tell
            // it about its addition for ownership tree purposes - if the
            // client wants to see the new nodes it can ask for children.
            continue;
          }
          // The actor is reconnected to the ownership tree, unorphan
          // it and let the client know so that its ownership tree is up
          // to date.
          this._orphaned.delete(addedActor);
          addedActors.push(addedActor.actorID);
        }

        mutation.numChildren = targetActor.numChildren;
        mutation.removed = removedActors;
        mutation.added = addedActors;

        let inlineTextChild = this.inlineTextChild(targetActor);
        if (inlineTextChild) {
          mutation.inlineTextChild = inlineTextChild.form();
        }
      }
      this.queueMutation(mutation);
    }
  },

  /**
   * Check if the provided mutation could change the way the target element is
   * inlined with its parent node. If it might, a custom mutation of type
   * "inlineTextChild" will be queued.
   *
   * @param {MutationRecord} mutation
   *        A characterData type mutation
   */
  _maybeQueueInlineTextChildMutation: function (mutation) {
    let {oldValue, target} = mutation;
    let newValue = target.nodeValue;
    let limit = gValueSummaryLength;

    if ((oldValue.length <= limit && newValue.length <= limit) ||
        (oldValue.length > limit && newValue.length > limit)) {
      // Bail out if the new & old values are both below/above the size limit.
      return;
    }

    let parentActor = this.getNode(target.parentNode);
    if (!parentActor || parentActor.rawNode.children.length > 0) {
      // If the parent node has other children, a character data mutation will
      // not change anything regarding inlining text nodes.
      return;
    }

    let inlineTextChild = this.inlineTextChild(parentActor);
    this.queueMutation({
      type: "inlineTextChild",
      target: parentActor.actorID,
      inlineTextChild:
        inlineTextChild ? inlineTextChild.form() : undefined
    });
  },

  onFrameLoad: function ({ window, isTopLevel }) {
    let { readyState } = window.document;
    if (readyState != "interactive" && readyState != "complete") {
      window.addEventListener("DOMContentLoaded",
        this.onFrameLoad.bind(this, { window, isTopLevel }),
        { once: true });
      return;
    }
    if (isTopLevel) {
      // If we initialize the inspector while the document is loading,
      // we may already have a root document set in the constructor.
      if (this.rootDoc && !Cu.isDeadWrapper(this.rootDoc) &&
          this.rootDoc.defaultView) {
        this.onFrameUnload({ window: this.rootDoc.defaultView });
      }
      // Update all DOM objects references to target the new document.
      this.rootWin = window;
      this.rootDoc = window.document;
      this.rootNode = this.document();
      this.queueMutation({
        type: "newRoot",
        target: this.rootNode.form()
      });
      return;
    }
    let frame = getFrameElement(window);
    let frameActor = this.getNode(frame);
    if (!frameActor) {
      return;
    }

    this.queueMutation({
      type: "frameLoad",
      target: frameActor.actorID,
    });

    // Send a childList mutation on the frame.
    this.queueMutation({
      type: "childList",
      target: frameActor.actorID,
      added: [],
      removed: []
    });
  },

  // Returns true if domNode is in window or a subframe.
  _childOfWindow: function (window, domNode) {
    let win = nodeDocument(domNode).defaultView;
    while (win) {
      if (win === window) {
        return true;
      }
      win = getFrameElement(win);
    }
    return false;
  },

  onFrameUnload: function ({ window }) {
    // Any retained orphans that belong to this document
    // or its children need to be released, and a mutation sent
    // to notify of that.
    let releasedOrphans = [];

    for (let retained of this._retainedOrphans) {
      if (Cu.isDeadWrapper(retained.rawNode) ||
          this._childOfWindow(window, retained.rawNode)) {
        this._retainedOrphans.delete(retained);
        releasedOrphans.push(retained.actorID);
        this.releaseNode(retained, { force: true });
      }
    }

    if (releasedOrphans.length > 0) {
      this.queueMutation({
        target: this.rootNode.actorID,
        type: "unretained",
        nodes: releasedOrphans
      });
    }

    let doc = window.document;
    let documentActor = this.getNode(doc);
    if (!documentActor) {
      return;
    }

    if (this.rootDoc === doc) {
      this.rootDoc = null;
      this.rootNode = null;
    }

    this.queueMutation({
      type: "documentUnload",
      target: documentActor.actorID
    });

    let walker = this.getDocumentWalker(doc);
    let parentNode = walker.parentNode();
    if (parentNode) {
      // Send a childList mutation on the frame so that clients know
      // they should reread the children list.
      this.queueMutation({
        type: "childList",
        target: this.getNode(parentNode).actorID,
        added: [],
        removed: []
      });
    }

    // Need to force a release of this node, because those nodes can't
    // be accessed anymore.
    this.releaseNode(documentActor, { force: true });
  },

  /**
   * Check if a node is attached to the DOM tree of the current page.
   * @param {nsIDomNode} rawNode
   * @return {Boolean} false if the node is removed from the tree or within a
   * document fragment
   */
  _isInDOMTree: function (rawNode) {
    let walker = this.getDocumentWalker(rawNode);
    let current = walker.currentNode;

    // Reaching the top of tree
    while (walker.parentNode()) {
      current = walker.currentNode;
    }

    // The top of the tree is a fragment or is not rootDoc, hence rawNode isn't
    // attached
    if (current.nodeType === Ci.nsIDOMNode.DOCUMENT_FRAGMENT_NODE ||
        current !== this.rootDoc) {
      return false;
    }

    // Otherwise the top of the tree is rootDoc, hence rawNode is in rootDoc
    return true;
  },

  /**
   * @see _isInDomTree
   */
  isInDOMTree: function (node) {
    if (isNodeDead(node)) {
      return false;
    }
    return this._isInDOMTree(node.rawNode);
  },

  /**
   * Given an ObjectActor (identified by its ID), commonly used in the debugger,
   * webconsole and variablesView, return the corresponding inspector's
   * NodeActor
   */
  getNodeActorFromObjectActor: function (objectActorID) {
    let actor = this.conn.getActor(objectActorID);
    if (!actor) {
      return null;
    }

    let debuggerObject = this.conn.getActor(objectActorID).obj;
    let rawNode = debuggerObject.unsafeDereference();

    if (!this._isInDOMTree(rawNode)) {
      return null;
    }

    // This is a special case for the document object whereby it is considered
    // as document.documentElement (the <html> node)
    if (rawNode.defaultView && rawNode === rawNode.defaultView.document) {
      rawNode = rawNode.documentElement;
    }

    return this.attachElement(rawNode);
  },

  /**
   * Given a windowID return the NodeActor for the corresponding frameElement,
   * unless it's the root window
   */
  getNodeActorFromWindowID: function (windowID) {
    let win;

    try {
      win = Services.wm.getOuterWindowWithId(windowID);
    } catch (e) {
      // ignore
    }

    if (!win) {
      return { error: "noWindow",
               message: "The related docshell is destroyed or not found" };
    } else if (!win.frameElement) {
      // the frame element of the root document is privileged & thus
      // inaccessible, so return the document body/element instead
      return this.attachElement(win.document.body || win.document.documentElement);
    }

    return this.attachElement(win.frameElement);
  },

  /**
   * Given a StyleSheetActor (identified by its ID), commonly used in the
   * style-editor, get its ownerNode and return the corresponding walker's
   * NodeActor.
   * Note that getNodeFromActor was added later and can now be used instead.
   */
  getStyleSheetOwnerNode: function (styleSheetActorID) {
    return this.getNodeFromActor(styleSheetActorID, ["ownerNode"]);
  },

  /**
   * This method can be used to retrieve NodeActor for DOM nodes from other
   * actors in a way that they can later be highlighted in the page, or
   * selected in the inspector.
   * If an actor has a reference to a DOM node, and the UI needs to know about
   * this DOM node (and possibly select it in the inspector), the UI should
   * first retrieve a reference to the walkerFront:
   *
   * // Make sure the inspector/walker have been initialized first.
   * toolbox.initInspector().then(() => {
   *  // Retrieve the walker.
   *  let walker = toolbox.walker;
   * });
   *
   * And then call this method:
   *
   * // Get the nodeFront from my actor, passing the ID and properties path.
   * walker.getNodeFromActor(myActorID, ["element"]).then(nodeFront => {
   *   // Use the nodeFront, e.g. select the node in the inspector.
   *   toolbox.getPanel("inspector").selection.setNodeFront(nodeFront);
   * });
   *
   * @param {String} actorID The ID for the actor that has a reference to the
   * DOM node.
   * @param {Array} path Where, on the actor, is the DOM node stored. If in the
   * scope of the actor, the node is available as `this.data.node`, then this
   * should be ["data", "node"].
   * @return {NodeActor} The attached NodeActor, or null if it couldn't be
   * found.
   */
  getNodeFromActor: function (actorID, path) {
    let actor = this.conn.getActor(actorID);
    if (!actor) {
      return null;
    }

    let obj = actor;
    for (let name of path) {
      if (!(name in obj)) {
        return null;
      }
      obj = obj[name];
    }

    return this.attachElement(obj);
  },

  /**
   * Returns an instance of the LayoutActor that is used to retrieve CSS layout-related
   * information.
   *
   * @return {LayoutActor}
   */
  getLayoutInspector: function () {
    if (!this.layoutActor) {
      this.layoutActor = new LayoutActor(this.conn, this.tabActor, this);
    }

    return this.layoutActor;
  },

  /**
   * Returns the offset parent DOMNode of the given node if it exists, otherwise, it
   * returns null.
   */
  getOffsetParent: function (node) {
    if (isNodeDead(node)) {
      return null;
    }

    let offsetParent = node.rawNode.offsetParent;

    if (!offsetParent) {
      return null;
    }

    return this._ref(offsetParent);
  },
});

function nodeDocshell(node) {
  let doc = node ? nodeDocument(node) : null;
  let win = doc ? doc.defaultView : null;
  if (win) {
    return win.QueryInterface(Ci.nsIInterfaceRequestor)
              .getInterface(Ci.nsIDocShell);
  }
  return null;
}

exports.WalkerActor = WalkerActor;
PK
!<�=0���8chrome/devtools/modules/devtools/server/actors/layout.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Cu } = require("chrome");
const { Actor, ActorClassWithSpec } = require("devtools/shared/protocol");
const { flexboxSpec, gridSpec, layoutSpec } = require("devtools/shared/specs/layout");
const nodeFilterConstants = require("devtools/shared/dom-node-filter-constants");
const { getStringifiableFragments } =
  require("devtools/server/actors/utils/css-grid-utils");

loader.lazyRequireGetter(this, "nodeConstants", "devtools/shared/dom-node-constants");
loader.lazyRequireGetter(this, "CssLogic", "devtools/server/css-logic", true);

/**
 * Set of actors the expose the CSS layout information to the devtools protocol clients.
 *
 * The |Layout| actor is the main entry point. It is used to get various CSS
 * layout-related information from the document.
 *
 * The |Flexbox| actor provides the container node information to inspect the flexbox
 * container.
 *
 * The |Grid| actor provides the grid fragment information to inspect the grid container.
 */

const FlexboxActor = ActorClassWithSpec(flexboxSpec, {
  /**
   * @param  {LayoutActor} layoutActor
   *         The LayoutActor instance.
   * @param  {DOMNode} containerEl
   *         The flex container element.
   */
  initialize(layoutActor, containerEl) {
    Actor.prototype.initialize.call(this, layoutActor.conn);

    this.containerEl = containerEl;
    this.walker = layoutActor.walker;
  },

  destroy() {
    Actor.prototype.destroy.call(this);

    this.containerEl = null;
    this.walker = null;
  },

  form(detail) {
    if (detail === "actorid") {
      return this.actorID;
    }

    let form = {
      actor: this.actorID,
    };

    // If the WalkerActor already knows the container element, then also return its
    // ActorID so we avoid the client from doing another round trip to get it in many
    // cases.
    if (this.walker.hasNode(this.containerEl)) {
      form.containerNodeActorID = this.walker.getNode(this.containerEl).actorID;
    }

    return form;
  },
});

/**
 * The GridActor provides information about a given grid's fragment data.
 */
const GridActor = ActorClassWithSpec(gridSpec, {
  /**
   * @param  {LayoutActor} layoutActor
   *         The LayoutActor instance.
   * @param  {DOMNode} containerEl
   *         The grid container element.
   */
  initialize(layoutActor, containerEl) {
    Actor.prototype.initialize.call(this, layoutActor.conn);

    this.containerEl = containerEl;
    this.walker = layoutActor.walker;
  },

  destroy() {
    Actor.prototype.destroy.call(this);

    this.containerEl = null;
    this.gridFragments = null;
    this.walker = null;
  },

  form(detail) {
    if (detail === "actorid") {
      return this.actorID;
    }

    // Seralize the grid fragment data into JSON so protocol.js knows how to write
    // and read the data.
    let gridFragments = this.containerEl.getGridFragments();
    this.gridFragments = getStringifiableFragments(gridFragments);

    // Record writing mode and text direction for use by the grid outline.
    let { direction, writingMode } = CssLogic.getComputedStyle(this.containerEl);

    let form = {
      actor: this.actorID,
      direction,
      gridFragments: this.gridFragments,
      writingMode,
    };

    // If the WalkerActor already knows the container element, then also return its
    // ActorID so we avoid the client from doing another round trip to get it in many
    // cases.
    if (this.walker.hasNode(this.containerEl)) {
      form.containerNodeActorID = this.walker.getNode(this.containerEl).actorID;
    }

    return form;
  },
});

/**
 * The CSS layout actor provides layout information for the given document.
 */
const LayoutActor = ActorClassWithSpec(layoutSpec, {
  initialize(conn, tabActor, walker) {
    Actor.prototype.initialize.call(this, conn);

    this.tabActor = tabActor;
    this.walker = walker;
  },

  destroy() {
    Actor.prototype.destroy.call(this);

    this.tabActor = null;
    this.walker = null;
  },

  /**
   * Returns the flex container found by iterating on the given selected node. The current
   * node can be a flex container or flex item. If it is a flex item, returns the parent
   * flex container. Otherwise, return null if the current or parent node is not a flex
   * container.
   *
   * @param  {Node|NodeActor} node
   *         The node to start iterating at.
   * @return {FlexboxActor|Null} The FlexboxActor of the flex container of the give node.
   * Otherwise, returns null.
   */
  getCurrentFlexbox(node) {
    if (isNodeDead(node)) {
      return null;
    }

    // Given node can either be a Node or a NodeActor.
    if (node.rawNode) {
      node = node.rawNode;
    }

    let treeWalker = this.walker.getDocumentWalker(node,
      nodeFilterConstants.SHOW_ELEMENT);
    let currentNode = treeWalker.currentNode;
    let displayType = this.walker.getNode(currentNode).displayType;

    if (!displayType) {
      return null;
    }

    // Check if the current node is a flex container.
    if (displayType == "inline-flex" || displayType == "flex") {
      return new FlexboxActor(this, treeWalker.currentNode);
    }

    // Otherwise, check if this is a flex item or the parent node is a flex container.
    while ((currentNode = treeWalker.parentNode())) {
      if (!currentNode) {
        break;
      }

      displayType = this.walker.getNode(currentNode).displayType;

      switch (displayType) {
        case "inline-flex":
        case "flex":
          return new FlexboxActor(this, currentNode);
        case "contents":
          // Continue walking up the tree since the parent node is a content element.
          continue;
      }

      break;
    }

    return null;
  },

  /**
   * Returns an array of GridActor objects for all the grid elements contained in the
   * given root node.
   *
   * @param  {Node|NodeActor} node
   *         The root node for grid elements
   * @return {Array} An array of GridActor objects.
   */
  getGrids(node) {
    if (isNodeDead(node)) {
      return [];
    }

    // Root node can either be a Node or a NodeActor.
    if (node.rawNode) {
      node = node.rawNode;
    }

    // Root node can be a #document object, which does not support getElementsWithGrid.
    if (node.nodeType === nodeConstants.DOCUMENT_NODE) {
      node = node.documentElement;
    }

    let gridElements = node.getElementsWithGrid();
    let gridActors = gridElements.map(n => new GridActor(this, n));

    let frames = node.querySelectorAll("iframe, frame");
    for (let frame of frames) {
      gridActors = gridActors.concat(this.getGrids(frame.contentDocument));
    }

    return gridActors;
  },
});

function isNodeDead(node) {
  return !node || (node.rawNode && Cu.isDeadWrapper(node.rawNode));
}

exports.FlexboxActor = FlexboxActor;
exports.GridActor = GridActor;
exports.LayoutActor = LayoutActor;
PK
!<�I"�MM8chrome/devtools/modules/devtools/server/actors/memory.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 protocol = require("devtools/shared/protocol");
const { Memory } = require("devtools/server/performance/memory");
const { actorBridgeWithSpec } = require("devtools/server/actors/common");
const { memorySpec } = require("devtools/shared/specs/memory");
loader.lazyRequireGetter(this, "StackFrameCache",
                         "devtools/server/actors/utils/stack", true);

/**
 * An actor that returns memory usage data for its parent actor's window.
 * A tab-scoped instance of this actor will measure the memory footprint of its
 * parent tab. A global-scoped instance however, will measure the memory
 * footprint of the chrome window referenced by the root actor.
 *
 * This actor wraps the Memory module at devtools/server/performance/memory.js
 * and provides RDP definitions.
 *
 * @see devtools/server/performance/memory.js for documentation.
 */
exports.MemoryActor = protocol.ActorClassWithSpec(memorySpec, {
  initialize: function (conn, parent, frameCache = new StackFrameCache()) {
    protocol.Actor.prototype.initialize.call(this, conn);

    this._onGarbageCollection = this._onGarbageCollection.bind(this);
    this._onAllocations = this._onAllocations.bind(this);
    this.bridge = new Memory(parent, frameCache);
    this.bridge.on("garbage-collection", this._onGarbageCollection);
    this.bridge.on("allocations", this._onAllocations);
  },

  destroy: function () {
    this.bridge.off("garbage-collection", this._onGarbageCollection);
    this.bridge.off("allocations", this._onAllocations);
    this.bridge.destroy();
    protocol.Actor.prototype.destroy.call(this);
  },

  attach: actorBridgeWithSpec("attach"),

  detach: actorBridgeWithSpec("detach"),

  getState: actorBridgeWithSpec("getState"),

  saveHeapSnapshot: function (boundaries) {
    return this.bridge.saveHeapSnapshot(boundaries);
  },

  takeCensus: actorBridgeWithSpec("takeCensus"),

  startRecordingAllocations: actorBridgeWithSpec("startRecordingAllocations"),

  stopRecordingAllocations: actorBridgeWithSpec("stopRecordingAllocations"),

  getAllocationsSettings: actorBridgeWithSpec("getAllocationsSettings"),

  getAllocations: actorBridgeWithSpec("getAllocations"),

  forceGarbageCollection: actorBridgeWithSpec("forceGarbageCollection"),

  forceCycleCollection: actorBridgeWithSpec("forceCycleCollection"),

  measure: actorBridgeWithSpec("measure"),

  residentUnique: actorBridgeWithSpec("residentUnique"),

  _onGarbageCollection: function (data) {
    if (this.conn.transport) {
      this.emit("garbage-collection", data);
    }
  },

  _onAllocations: function (data) {
    if (this.conn.transport) {
      this.emit("allocations", data);
    }
  },
});
PK
!<���֮1�18chrome/devtools/modules/devtools/server/actors/object.js/* -*- indent-tabs-mode: nil; js-indent-level: 2; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 { Cu, Ci } = require("chrome");
const { GeneratedLocation } = require("devtools/server/actors/common");
const { DebuggerServer } = require("devtools/server/main");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const { assert } = DevToolsUtils;

loader.lazyRequireGetter(this, "ChromeUtils");

// Number of items to preview in objects, arrays, maps, sets, lists,
// collections, etc.
const OBJECT_PREVIEW_MAX_ITEMS = 10;

/**
 * Creates an actor for the specified object.
 *
 * @param obj Debugger.Object
 *        The debuggee object.
 * @param hooks Object
 *        A collection of abstract methods that are implemented by the caller.
 *        ObjectActor requires the following functions to be implemented by
 *        the caller:
 *          - createValueGrip
 *              Creates a value grip for the given object
 *          - sources
 *              TabSources getter that manages the sources of a thread
 *          - createEnvironmentActor
 *              Creates and return an environment actor
 *          - getGripDepth
 *              An actor's grip depth getter
 *          - incrementGripDepth
 *              Increment the actor's grip depth
 *          - decrementGripDepth
 *              Decrement the actor's grip depth
 *          - globalDebugObject
 *              The Debuggee Global Object as given by the ThreadActor
 */
function ObjectActor(obj, {
  createValueGrip: createValueGripHook,
  sources,
  createEnvironmentActor,
  getGripDepth,
  incrementGripDepth,
  decrementGripDepth,
  getGlobalDebugObject
}) {
  assert(!obj.optimizedOut,
         "Should not create object actors for optimized out values!");
  this.obj = obj;
  this.hooks = {
    createValueGrip: createValueGripHook,
    sources,
    createEnvironmentActor,
    getGripDepth,
    incrementGripDepth,
    decrementGripDepth,
    getGlobalDebugObject
  };
  this.iterators = new Set();
}

ObjectActor.prototype = {
  actorPrefix: "obj",

  rawValue: function () {
    return this.obj.unsafeDereference();
  },

  /**
   * Returns a grip for this actor for returning in a protocol message.
   */
  grip: function () {
    let g = {
      "type": "object",
      "actor": this.actorID,
      "class": this.obj.class,
    };

    let unwrapped = DevToolsUtils.unwrap(this.obj);

    // Unsafe objects must be treated carefully.
    if (!DevToolsUtils.isSafeDebuggerObject(this.obj)) {
      if (DevToolsUtils.isCPOW(this.obj)) {
        // Cross-process object wrappers can't be accessed.
        g.class = "CPOW: " + g.class;
      } else if (unwrapped === undefined) {
        // Objects belonging to an invisible-to-debugger compartment might be proxies,
        // so just in case they shouldn't be accessed.
        g.class = "InvisibleToDebugger: " + g.class;
      } else if (unwrapped.isProxy) {
        // Proxy objects can run traps when accessed, so just create a preview with
        // the target and the handler.
        g.class = "Proxy";
        this.hooks.incrementGripDepth();
        DebuggerServer.ObjectActorPreviewers.Proxy[0](this, g, null);
        this.hooks.decrementGripDepth();
      }
      return g;
    }

    // If the debuggee does not subsume the object's compartment, most properties won't
    // be accessible. Cross-orgin Window and Location objects might expose some, though.
    // Change the displayed class, but when creating the preview use the original one.
    if (unwrapped === null) {
      g.class = "Restricted";
    }

    this.hooks.incrementGripDepth();

    g.extensible = this.obj.isExtensible();
    g.frozen = this.obj.isFrozen();
    g.sealed = this.obj.isSealed();

    if (g.class == "Promise") {
      g.promiseState = this._createPromiseState();
    }

    // FF40+: Allow to know how many properties an object has to lazily display them
    // when there is a bunch.
    if (isTypedArray(g)) {
      // Bug 1348761: getOwnPropertyNames is unnecessary slow on TypedArrays
      g.ownPropertyLength = getArrayLength(this.obj);
    } else {
      try {
        g.ownPropertyLength = this.obj.getOwnPropertyNames().length;
      } catch (err) {
        // The above can throw when the debuggee does not subsume the object's
        // compartment, or for some WrappedNatives like Cu.Sandbox.
      }
    }

    let raw = this.obj.unsafeDereference();

    // If Cu is not defined, we are running on a worker thread, where xrays
    // don't exist.
    if (Cu) {
      raw = Cu.unwaiveXrays(raw);
    }

    if (!DevToolsUtils.isSafeJSObject(raw)) {
      raw = null;
    }

    let previewers = DebuggerServer.ObjectActorPreviewers[this.obj.class] ||
                     DebuggerServer.ObjectActorPreviewers.Object;
    for (let fn of previewers) {
      try {
        if (fn(this, g, raw)) {
          break;
        }
      } catch (e) {
        let msg = "ObjectActor.prototype.grip previewer function";
        DevToolsUtils.reportException(msg, e);
      }
    }

    this.hooks.decrementGripDepth();
    return g;
  },

  /**
   * Returns an object exposing the internal Promise state.
   */
  _createPromiseState: function () {
    const { state, value, reason } = getPromiseState(this.obj);
    let promiseState = { state };

    if (state == "fulfilled") {
      promiseState.value = this.hooks.createValueGrip(value);
    } else if (state == "rejected") {
      promiseState.reason = this.hooks.createValueGrip(reason);
    }

    promiseState.creationTimestamp = Date.now() - this.obj.promiseLifetime;

    // Only add the timeToSettle property if the Promise isn't pending.
    if (state !== "pending") {
      promiseState.timeToSettle = this.obj.promiseTimeToResolution;
    }

    return promiseState;
  },

  /**
   * Releases this actor from the pool.
   */
  release: function () {
    if (this.registeredPool.objectActors) {
      this.registeredPool.objectActors.delete(this.obj);
    }
    this.iterators.forEach(actor => this.registeredPool.removeActor(actor));
    this.iterators.clear();
    this.registeredPool.removeActor(this);
  },

  /**
   * Handle a protocol request to provide the definition site of this function
   * object.
   */
  onDefinitionSite: function () {
    if (this.obj.class != "Function") {
      return {
        from: this.actorID,
        error: "objectNotFunction",
        message: this.actorID + " is not a function."
      };
    }

    if (!this.obj.script) {
      return {
        from: this.actorID,
        error: "noScript",
        message: this.actorID + " has no Debugger.Script"
      };
    }

    return this.hooks.sources().getOriginalLocation(new GeneratedLocation(
      this.hooks.sources().createNonSourceMappedActor(this.obj.script.source),
      this.obj.script.startLine,
      0 // TODO bug 901138: use Debugger.Script.prototype.startColumn
    )).then((originalLocation) => {
      return {
        source: originalLocation.originalSourceActor.form(),
        line: originalLocation.originalLine,
        column: originalLocation.originalColumn
      };
    });
  },

  /**
   * Handle a protocol request to provide the names of the properties defined on
   * the object and not its prototype.
   */
  onOwnPropertyNames: function () {
    let props = [];
    if (DevToolsUtils.isSafeDebuggerObject(this.obj)) {
      try {
        props = this.obj.getOwnPropertyNames();
      } catch (err) {
        // The above can throw when the debuggee does not subsume the object's
        // compartment, or for some WrappedNatives like Cu.Sandbox.
      }
    }
    return { from: this.actorID, ownPropertyNames: props };
  },

  /**
   * Creates an actor to iterate over an object property names and values.
   * See PropertyIteratorActor constructor for more info about options param.
   *
   * @param request object
   *        The protocol request object.
   */
  onEnumProperties: function (request) {
    let actor = new PropertyIteratorActor(this, request.options);
    this.registeredPool.addActor(actor);
    this.iterators.add(actor);
    return { iterator: actor.grip() };
  },

  /**
   * Creates an actor to iterate over entries of a Map/Set-like object.
   */
  onEnumEntries: function () {
    let actor = new PropertyIteratorActor(this, { enumEntries: true });
    this.registeredPool.addActor(actor);
    this.iterators.add(actor);
    return { iterator: actor.grip() };
  },

  /**
   * Creates an actor to iterate over an object symbols properties.
   */
  onEnumSymbols: function () {
    let actor = new SymbolIteratorActor(this);
    this.registeredPool.addActor(actor);
    this.iterators.add(actor);
    return { iterator: actor.grip() };
  },

  /**
   * Handle a protocol request to provide the prototype and own properties of
   * the object.
   *
   * @returns {Object} An object containing the data of this.obj, of the following form:
   *          - {string} from: this.obj's actorID.
   *          - {Object} prototype: The descriptor of this.obj's prototype.
   *          - {Object} ownProperties: an object where the keys are the names of the
   *                     this.obj's ownProperties, and the values the descriptors of
   *                     the properties.
   *          - {Array} ownSymbols: An array containing all descriptors of this.obj's
   *                    ownSymbols. Here we have an array, and not an object like for
   *                    ownProperties, because we can have multiple symbols with the same
   *                    name in this.obj, e.g. `{[Symbol()]: "a", [Symbol()]: "b"}`.
   *          - {Object} safeGetterValues: an object that maps this.obj's property names
   *                     with safe getters descriptors.
   */
  onPrototypeAndProperties: function () {
    let proto = null;
    let names = [];
    let symbols = [];
    if (DevToolsUtils.isSafeDebuggerObject(this.obj)) {
      try {
        proto = this.obj.proto;
        names = this.obj.getOwnPropertyNames();
        symbols = this.obj.getOwnPropertySymbols();
      } catch (err) {
        // The above can throw when the debuggee does not subsume the object's
        // compartment, or for some WrappedNatives like Cu.Sandbox.
      }
    }

    let ownProperties = Object.create(null);
    let ownSymbols = [];

    for (let name of names) {
      ownProperties[name] = this._propertyDescriptor(name);
    }

    for (let sym of symbols) {
      ownSymbols.push({
        name: sym.toString(),
        descriptor: this._propertyDescriptor(sym)
      });
    }

    return { from: this.actorID,
             prototype: this.hooks.createValueGrip(proto),
             ownProperties,
             ownSymbols,
             safeGetterValues: this._findSafeGetterValues(names) };
  },

  /**
   * Find the safe getter values for the current Debugger.Object, |this.obj|.
   *
   * @private
   * @param array ownProperties
   *        The array that holds the list of known ownProperties names for
   *        |this.obj|.
   * @param number [limit=0]
   *        Optional limit of getter values to find.
   * @return object
   *         An object that maps property names to safe getter descriptors as
   *         defined by the remote debugging protocol.
   */
  _findSafeGetterValues: function (ownProperties, limit = 0) {
    let safeGetterValues = Object.create(null);
    let obj = this.obj;
    let level = 0, i = 0;

    // Do not search safe getters in unsafe objects.
    if (!DevToolsUtils.isSafeDebuggerObject(obj)) {
      return safeGetterValues;
    }

    // Most objects don't have any safe getters but inherit some from their
    // prototype. Avoid calling getOwnPropertyNames on objects that may have
    // many properties like Array, strings or js objects. That to avoid
    // freezing firefox when doing so.
    if (isArray(this.obj) || ["Object", "String"].includes(this.obj.class)) {
      obj = obj.proto;
      level++;
    }

    while (obj && DevToolsUtils.isSafeDebuggerObject(obj)) {
      let getters = this._findSafeGetters(obj);
      for (let name of getters) {
        // Avoid overwriting properties from prototypes closer to this.obj. Also
        // avoid providing safeGetterValues from prototypes if property |name|
        // is already defined as an own property.
        if (name in safeGetterValues ||
            (obj != this.obj && ownProperties.includes(name))) {
          continue;
        }

        // Ignore __proto__ on Object.prototye.
        if (!obj.proto && name == "__proto__") {
          continue;
        }

        let desc = null, getter = null;
        try {
          desc = obj.getOwnPropertyDescriptor(name);
          getter = desc.get;
        } catch (ex) {
          // The above can throw if the cache becomes stale.
        }
        if (!getter) {
          obj._safeGetters = null;
          continue;
        }

        let result = getter.call(this.obj);
        if (result && !("throw" in result)) {
          let getterValue = undefined;
          if ("return" in result) {
            getterValue = result.return;
          } else if ("yield" in result) {
            getterValue = result.yield;
          }
          // WebIDL attributes specified with the LenientThis extended attribute
          // return undefined and should be ignored.
          if (getterValue !== undefined) {
            safeGetterValues[name] = {
              getterValue: this.hooks.createValueGrip(getterValue),
              getterPrototypeLevel: level,
              enumerable: desc.enumerable,
              writable: level == 0 ? desc.writable : true,
            };
            if (limit && ++i == limit) {
              break;
            }
          }
        }
      }
      if (limit && i == limit) {
        break;
      }

      obj = obj.proto;
      level++;
    }

    return safeGetterValues;
  },

  /**
   * Find the safe getters for a given Debugger.Object. Safe getters are native
   * getters which are safe to execute.
   *
   * @private
   * @param Debugger.Object object
   *        The Debugger.Object where you want to find safe getters.
   * @return Set
   *         A Set of names of safe getters. This result is cached for each
   *         Debugger.Object.
   */
  _findSafeGetters: function (object) {
    if (object._safeGetters) {
      return object._safeGetters;
    }

    let getters = new Set();

    if (!DevToolsUtils.isSafeDebuggerObject(object)) {
      object._safeGetters = getters;
      return getters;
    }

    let names = [];
    try {
      names = object.getOwnPropertyNames();
    } catch (ex) {
      // Calling getOwnPropertyNames() on some wrapped native prototypes is not
      // allowed: "cannot modify properties of a WrappedNative". See bug 952093.
    }

    for (let name of names) {
      let desc = null;
      try {
        desc = object.getOwnPropertyDescriptor(name);
      } catch (e) {
        // Calling getOwnPropertyDescriptor on wrapped native prototypes is not
        // allowed (bug 560072).
      }
      if (!desc || desc.value !== undefined || !("get" in desc)) {
        continue;
      }

      if (DevToolsUtils.hasSafeGetter(desc)) {
        getters.add(name);
      }
    }

    object._safeGetters = getters;
    return getters;
  },

  /**
   * Handle a protocol request to provide the prototype of the object.
   */
  onPrototype: function () {
    let proto = null;
    if (DevToolsUtils.isSafeDebuggerObject(this.obj)) {
      proto = this.obj.proto;
    }
    return { from: this.actorID,
             prototype: this.hooks.createValueGrip(proto) };
  },

  /**
   * Handle a protocol request to provide the property descriptor of the
   * object's specified property.
   *
   * @param request object
   *        The protocol request object.
   */
  onProperty: function (request) {
    if (!request.name) {
      return { error: "missingParameter",
               message: "no property name was specified" };
    }

    return { from: this.actorID,
             descriptor: this._propertyDescriptor(request.name) };
  },

  /**
   * Handle a protocol request to provide the display string for the object.
   */
  onDisplayString: function () {
    const string = stringify(this.obj);
    return { from: this.actorID,
             displayString: this.hooks.createValueGrip(string) };
  },

  /**
   * A helper method that creates a property descriptor for the provided object,
   * properly formatted for sending in a protocol response.
   *
   * @private
   * @param string name
   *        The property that the descriptor is generated for.
   * @param boolean [onlyEnumerable]
   *        Optional: true if you want a descriptor only for an enumerable
   *        property, false otherwise.
   * @return object|undefined
   *         The property descriptor, or undefined if this is not an enumerable
   *         property and onlyEnumerable=true.
   */
  _propertyDescriptor: function (name, onlyEnumerable) {
    if (!DevToolsUtils.isSafeDebuggerObject(this.obj)) {
      return undefined;
    }

    let desc;
    try {
      desc = this.obj.getOwnPropertyDescriptor(name);
    } catch (e) {
      // Calling getOwnPropertyDescriptor on wrapped native prototypes is not
      // allowed (bug 560072). Inform the user with a bogus, but hopefully
      // explanatory, descriptor.
      return {
        configurable: false,
        writable: false,
        enumerable: false,
        value: e.name
      };
    }

    if (!desc || onlyEnumerable && !desc.enumerable) {
      return undefined;
    }

    let retval = {
      configurable: desc.configurable,
      enumerable: desc.enumerable
    };

    if ("value" in desc) {
      retval.writable = desc.writable;
      retval.value = this.hooks.createValueGrip(desc.value);
    } else {
      if ("get" in desc) {
        retval.get = this.hooks.createValueGrip(desc.get);
      }
      if ("set" in desc) {
        retval.set = this.hooks.createValueGrip(desc.set);
      }
    }
    return retval;
  },

  /**
   * Handle a protocol request to provide the source code of a function.
   *
   * @param request object
   *        The protocol request object.
   */
  onDecompile: function (request) {
    if (this.obj.class !== "Function") {
      return { error: "objectNotFunction",
               message: "decompile request is only valid for object grips " +
                        "with a 'Function' class." };
    }

    return { from: this.actorID,
             decompiledCode: this.obj.decompile(!!request.pretty) };
  },

  /**
   * Handle a protocol request to provide the parameters of a function.
   */
  onParameterNames: function () {
    if (this.obj.class !== "Function") {
      return { error: "objectNotFunction",
               message: "'parameterNames' request is only valid for object " +
                        "grips with a 'Function' class." };
    }

    return { parameterNames: this.obj.parameterNames };
  },

  /**
   * Handle a protocol request to release a thread-lifetime grip.
   */
  onRelease: function () {
    this.release();
    return {};
  },

  /**
   * Handle a protocol request to provide the lexical scope of a function.
   */
  onScope: function () {
    if (this.obj.class !== "Function") {
      return { error: "objectNotFunction",
               message: "scope request is only valid for object grips with a" +
                        " 'Function' class." };
    }

    let envActor = this.hooks.createEnvironmentActor(this.obj.environment,
                                                     this.registeredPool);
    if (!envActor) {
      return { error: "notDebuggee",
               message: "cannot access the environment of this function." };
    }

    return { from: this.actorID, scope: envActor.form() };
  },

  /**
   * Handle a protocol request to get the list of dependent promises of a
   * promise.
   *
   * @return object
   *         Returns an object containing an array of object grips of the
   *         dependent promises
   */
  onDependentPromises: function () {
    if (this.obj.class != "Promise") {
      return { error: "objectNotPromise",
               message: "'dependentPromises' request is only valid for " +
                        "object grips with a 'Promise' class." };
    }

    let promises = this.obj.promiseDependentPromises
                           .map(p => this.hooks.createValueGrip(p));

    return { promises };
  },

  /**
   * Handle a protocol request to get the allocation stack of a promise.
   */
  onAllocationStack: function () {
    if (this.obj.class != "Promise") {
      return { error: "objectNotPromise",
               message: "'allocationStack' request is only valid for " +
                        "object grips with a 'Promise' class." };
    }

    let stack = this.obj.promiseAllocationSite;
    let allocationStacks = [];

    while (stack) {
      if (stack.source) {
        let source = this._getSourceOriginalLocation(stack);

        if (source) {
          allocationStacks.push(source);
        }
      }
      stack = stack.parent;
    }

    return Promise.all(allocationStacks).then(stacks => {
      return { allocationStack: stacks };
    });
  },

  /**
   * Handle a protocol request to get the fulfillment stack of a promise.
   */
  onFulfillmentStack: function () {
    if (this.obj.class != "Promise") {
      return { error: "objectNotPromise",
               message: "'fulfillmentStack' request is only valid for " +
                        "object grips with a 'Promise' class." };
    }

    let stack = this.obj.promiseResolutionSite;
    let fulfillmentStacks = [];

    while (stack) {
      if (stack.source) {
        let source = this._getSourceOriginalLocation(stack);

        if (source) {
          fulfillmentStacks.push(source);
        }
      }
      stack = stack.parent;
    }

    return Promise.all(fulfillmentStacks).then(stacks => {
      return { fulfillmentStack: stacks };
    });
  },

  /**
   * Handle a protocol request to get the rejection stack of a promise.
   */
  onRejectionStack: function () {
    if (this.obj.class != "Promise") {
      return { error: "objectNotPromise",
               message: "'rejectionStack' request is only valid for " +
                        "object grips with a 'Promise' class." };
    }

    let stack = this.obj.promiseResolutionSite;
    let rejectionStacks = [];

    while (stack) {
      if (stack.source) {
        let source = this._getSourceOriginalLocation(stack);

        if (source) {
          rejectionStacks.push(source);
        }
      }
      stack = stack.parent;
    }

    return Promise.all(rejectionStacks).then(stacks => {
      return { rejectionStack: stacks };
    });
  },

  /**
   * Helper function for fetching the source location of a SavedFrame stack.
   *
   * @param SavedFrame stack
   *        The promise allocation stack frame
   * @return object
   *         Returns an object containing the source location of the SavedFrame
   *         stack.
   */
  _getSourceOriginalLocation: function (stack) {
    let source;

    // Catch any errors if the source actor cannot be found
    try {
      source = this.hooks.sources().getSourceActorByURL(stack.source);
    } catch (e) {
      // ignored
    }

    if (!source) {
      return null;
    }

    return this.hooks.sources().getOriginalLocation(new GeneratedLocation(
      source,
      stack.line,
      stack.column
    )).then((originalLocation) => {
      return {
        source: originalLocation.originalSourceActor.form(),
        line: originalLocation.originalLine,
        column: originalLocation.originalColumn,
        functionDisplayName: stack.functionDisplayName
      };
    });
  }
};

ObjectActor.prototype.requestTypes = {
  "definitionSite": ObjectActor.prototype.onDefinitionSite,
  "parameterNames": ObjectActor.prototype.onParameterNames,
  "prototypeAndProperties": ObjectActor.prototype.onPrototypeAndProperties,
  "enumProperties": ObjectActor.prototype.onEnumProperties,
  "prototype": ObjectActor.prototype.onPrototype,
  "property": ObjectActor.prototype.onProperty,
  "displayString": ObjectActor.prototype.onDisplayString,
  "ownPropertyNames": ObjectActor.prototype.onOwnPropertyNames,
  "decompile": ObjectActor.prototype.onDecompile,
  "release": ObjectActor.prototype.onRelease,
  "scope": ObjectActor.prototype.onScope,
  "dependentPromises": ObjectActor.prototype.onDependentPromises,
  "allocationStack": ObjectActor.prototype.onAllocationStack,
  "fulfillmentStack": ObjectActor.prototype.onFulfillmentStack,
  "rejectionStack": ObjectActor.prototype.onRejectionStack,
  "enumEntries": ObjectActor.prototype.onEnumEntries,
  "enumSymbols": ObjectActor.prototype.onEnumSymbols,
};

/**
 * Creates an actor to iterate over an object's property names and values.
 *
 * @param objectActor ObjectActor
 *        The object actor.
 * @param options Object
 *        A dictionary object with various boolean attributes:
 *        - enumEntries Boolean
 *          If true, enumerates the entries of a Map or Set object
 *          instead of enumerating properties.
 *        - ignoreIndexedProperties Boolean
 *          If true, filters out Array items.
 *          e.g. properties names between `0` and `object.length`.
 *        - ignoreNonIndexedProperties Boolean
 *          If true, filters out items that aren't array items
 *          e.g. properties names that are not a number between `0`
 *          and `object.length`.
 *        - sort Boolean
 *          If true, the iterator will sort the properties by name
 *          before dispatching them.
 *        - query String
 *          If non-empty, will filter the properties by names and values
 *          containing this query string. The match is not case-sensitive.
 *          Regarding value filtering it just compare to the stringification
 *          of the property value.
 */
function PropertyIteratorActor(objectActor, options) {
  if (!DevToolsUtils.isSafeDebuggerObject(objectActor.obj)) {
    this.iterator = {
      size: 0,
      propertyName: index => undefined,
      propertyDescription: index => undefined,
    };
  } else if (options.enumEntries) {
    let cls = objectActor.obj.class;
    if (cls == "Map") {
      this.iterator = enumMapEntries(objectActor);
    } else if (cls == "WeakMap") {
      this.iterator = enumWeakMapEntries(objectActor);
    } else if (cls == "Set") {
      this.iterator = enumSetEntries(objectActor);
    } else if (cls == "WeakSet") {
      this.iterator = enumWeakSetEntries(objectActor);
    } else {
      throw new Error("Unsupported class to enumerate entries from: " + cls);
    }
  } else if (
    isArray(objectActor.obj)
    && options.ignoreNonIndexedProperties
    && !options.query
  ) {
    this.iterator = enumArrayProperties(objectActor, options);
  } else {
    this.iterator = enumObjectProperties(objectActor, options);
  }
}

PropertyIteratorActor.prototype = {
  actorPrefix: "propertyIterator",

  grip() {
    return {
      type: this.actorPrefix,
      actor: this.actorID,
      count: this.iterator.size
    };
  },

  names({ indexes }) {
    let list = [];
    for (let idx of indexes) {
      list.push(this.iterator.propertyName(idx));
    }
    return {
      names: indexes
    };
  },

  slice({ start, count }) {
    let ownProperties = Object.create(null);
    for (let i = start, m = start + count; i < m; i++) {
      let name = this.iterator.propertyName(i);
      ownProperties[name] = this.iterator.propertyDescription(i);
    }
    return {
      ownProperties
    };
  },

  all() {
    return this.slice({ start: 0, count: this.iterator.size });
  }
};

PropertyIteratorActor.prototype.requestTypes = {
  "names": PropertyIteratorActor.prototype.names,
  "slice": PropertyIteratorActor.prototype.slice,
  "all": PropertyIteratorActor.prototype.all,
};

function enumArrayProperties(objectActor, options) {
  return {
    size: getArrayLength(objectActor.obj),
    propertyName(index) {
      return index;
    },
    propertyDescription(index) {
      return objectActor._propertyDescriptor(index);
    }
  };
}

function enumObjectProperties(objectActor, options) {
  let names = [];
  try {
    names = objectActor.obj.getOwnPropertyNames();
  } catch (ex) {
    // Calling getOwnPropertyNames() on some wrapped native prototypes is not
    // allowed: "cannot modify properties of a WrappedNative". See bug 952093.
  }

  if (options.ignoreNonIndexedProperties || options.ignoreIndexedProperties) {
    let length = DevToolsUtils.getProperty(objectActor.obj, "length");
    let sliceIndex;

    const isLengthTrustworthy =
      isUint32(length)
      && (!length || isArrayIndex(names[length - 1]))
      && !isArrayIndex(names[length]);

    if (!isLengthTrustworthy) {
      // The length property may not reflect what the object looks like, let's find
      // where indexed properties end.

      if (!isArrayIndex(names[0])) {
        // If the first item is not a number, this means there is no indexed properties
        // in this object.
        sliceIndex = 0;
      } else {
        sliceIndex = names.length;
        while (sliceIndex > 0) {
          if (isArrayIndex(names[sliceIndex - 1])) {
            break;
          }
          sliceIndex--;
        }
      }
    } else {
      sliceIndex = length;
    }

    // It appears that getOwnPropertyNames always returns indexed properties
    // first, so we can safely slice `names` for/against indexed properties.
    // We do such clever operation to optimize very large array inspection,
    // like webaudio buffers.
    if (options.ignoreIndexedProperties) {
      // Keep items after `sliceIndex` index
      names = names.slice(sliceIndex);
    } else if (options.ignoreNonIndexedProperties) {
      // Keep `sliceIndex` first items
      names.length = sliceIndex;
    }
  }

  let safeGetterValues = objectActor._findSafeGetterValues(names, 0);
  let safeGetterNames = Object.keys(safeGetterValues);
  // Merge the safe getter values into the existing properties list.
  for (let name of safeGetterNames) {
    if (!names.includes(name)) {
      names.push(name);
    }
  }

  if (options.query) {
    let { query } = options;
    query = query.toLowerCase();
    names = names.filter(name => {
      // Filter on attribute names
      if (name.toLowerCase().includes(query)) {
        return true;
      }
      // and then on attribute values
      let desc;
      try {
        desc = objectActor.obj.getOwnPropertyDescriptor(name);
      } catch (e) {
        // Calling getOwnPropertyDescriptor on wrapped native prototypes is not
        // allowed (bug 560072).
      }
      if (desc && desc.value &&
          String(desc.value).includes(query)) {
        return true;
      }
      return false;
    });
  }

  if (options.sort) {
    names.sort();
  }

  return {
    size: names.length,
    propertyName(index) {
      return names[index];
    },
    propertyDescription(index) {
      let name = names[index];
      let desc = objectActor._propertyDescriptor(name);
      if (!desc) {
        desc = safeGetterValues[name];
      } else if (name in safeGetterValues) {
        // Merge the safe getter values into the existing properties list.
        let { getterValue, getterPrototypeLevel } = safeGetterValues[name];
        desc.getterValue = getterValue;
        desc.getterPrototypeLevel = getterPrototypeLevel;
      }
      return desc;
    }
  };
}

/**
 * Helper function to create a grip from a Map/Set entry
 */
function gripFromEntry({ obj, hooks }, entry) {
  return hooks.createValueGrip(
    makeDebuggeeValueIfNeeded(obj, Cu.unwaiveXrays(entry)));
}

function enumMapEntries(objectActor) {
  // Iterating over a Map via .entries goes through various intermediate
  // objects - an Iterator object, then a 2-element Array object, then the
  // actual values we care about. We don't have Xrays to Iterator objects,
  // so we get Opaque wrappers for them. And even though we have Xrays to
  // Arrays, the semantics often deny access to the entires based on the
  // nature of the values. So we need waive Xrays for the iterator object
  // and the tupes, and then re-apply them on the underlying values until
  // we fix bug 1023984.
  //
  // Even then though, we might want to continue waiving Xrays here for the
  // same reason we do so for Arrays above - this filtering behavior is likely
  // to be more confusing than beneficial in the case of Object previews.
  let raw = objectActor.obj.unsafeDereference();

  let keys = [...Cu.waiveXrays(Map.prototype.keys.call(raw))];
  return {
    [Symbol.iterator]: function* () {
      for (let key of keys) {
        let value = Map.prototype.get.call(raw, key);
        yield [ key, value ].map(val => gripFromEntry(objectActor, val));
      }
    },
    size: keys.length,
    propertyName(index) {
      return index;
    },
    propertyDescription(index) {
      let key = keys[index];
      let val = Map.prototype.get.call(raw, key);
      return {
        enumerable: true,
        value: {
          type: "mapEntry",
          preview: {
            key: gripFromEntry(objectActor, key),
            value: gripFromEntry(objectActor, val)
          }
        }
      };
    }
  };
}

function enumWeakMapEntries(objectActor) {
  // We currently lack XrayWrappers for WeakMap, so when we iterate over
  // the values, the temporary iterator objects get created in the target
  // compartment. However, we _do_ have Xrays to Object now, so we end up
  // Xraying those temporary objects, and filtering access to |it.value|
  // based on whether or not it's Xrayable and/or callable, which breaks
  // the for/of iteration.
  //
  // This code is designed to handle untrusted objects, so we can safely
  // waive Xrays on the iterable, and relying on the Debugger machinery to
  // make sure we handle the resulting objects carefully.
  let raw = objectActor.obj.unsafeDereference();
  let keys = Cu.waiveXrays(
    ChromeUtils.nondeterministicGetWeakMapKeys(raw));

  return {
    [Symbol.iterator]: function* () {
      for (let key of keys) {
        let value = WeakMap.prototype.get.call(raw, key);
        yield [ key, value ].map(val => gripFromEntry(objectActor, val));
      }
    },
    size: keys.length,
    propertyName(index) {
      return index;
    },
    propertyDescription(index) {
      let key = keys[index];
      let val = WeakMap.prototype.get.call(raw, key);
      return {
        enumerable: true,
        value: {
          type: "mapEntry",
          preview: {
            key: gripFromEntry(objectActor, key),
            value: gripFromEntry(objectActor, val)
          }
        }
      };
    }
  };
}

function enumSetEntries(objectActor) {
  // We currently lack XrayWrappers for Set, so when we iterate over
  // the values, the temporary iterator objects get created in the target
  // compartment. However, we _do_ have Xrays to Object now, so we end up
  // Xraying those temporary objects, and filtering access to |it.value|
  // based on whether or not it's Xrayable and/or callable, which breaks
  // the for/of iteration.
  //
  // This code is designed to handle untrusted objects, so we can safely
  // waive Xrays on the iterable, and relying on the Debugger machinery to
  // make sure we handle the resulting objects carefully.
  let raw = objectActor.obj.unsafeDereference();
  let values = [...Cu.waiveXrays(Set.prototype.values.call(raw))];

  return {
    [Symbol.iterator]: function* () {
      for (let item of values) {
        yield gripFromEntry(objectActor, item);
      }
    },
    size: values.length,
    propertyName(index) {
      return index;
    },
    propertyDescription(index) {
      let val = values[index];
      return {
        enumerable: true,
        value: gripFromEntry(objectActor, val)
      };
    }
  };
}

function enumWeakSetEntries(objectActor) {
  // We currently lack XrayWrappers for WeakSet, so when we iterate over
  // the values, the temporary iterator objects get created in the target
  // compartment. However, we _do_ have Xrays to Object now, so we end up
  // Xraying those temporary objects, and filtering access to |it.value|
  // based on whether or not it's Xrayable and/or callable, which breaks
  // the for/of iteration.
  //
  // This code is designed to handle untrusted objects, so we can safely
  // waive Xrays on the iterable, and relying on the Debugger machinery to
  // make sure we handle the resulting objects carefully.
  let raw = objectActor.obj.unsafeDereference();
  let keys = Cu.waiveXrays(
    ChromeUtils.nondeterministicGetWeakSetKeys(raw));

  return {
    [Symbol.iterator]: function* () {
      for (let item of keys) {
        yield gripFromEntry(objectActor, item);
      }
    },
    size: keys.length,
    propertyName(index) {
      return index;
    },
    propertyDescription(index) {
      let val = keys[index];
      return {
        enumerable: true,
        value: gripFromEntry(objectActor, val)
      };
    }
  };
}

/**
 * Creates an actor to iterate over an object's symbols.
 *
 * @param objectActor ObjectActor
 *        The object actor.
 */
function SymbolIteratorActor(objectActor) {
  let symbols = [];
  if (DevToolsUtils.isSafeDebuggerObject(objectActor.obj)) {
    try {
      symbols = objectActor.obj.getOwnPropertySymbols();
    } catch (err) {
      // The above can throw when the debuggee does not subsume the object's
      // compartment, or for some WrappedNatives like Cu.Sandbox.
    }
  }

  this.iterator = {
    size: symbols.length,
    symbolDescription(index) {
      const symbol = symbols[index];
      return {
        name: symbol.toString(),
        descriptor: objectActor._propertyDescriptor(symbol)
      };
    }
  };
}

SymbolIteratorActor.prototype = {
  actorPrefix: "symbolIterator",

  grip() {
    return {
      type: this.actorPrefix,
      actor: this.actorID,
      count: this.iterator.size
    };
  },

  slice({ start, count }) {
    let ownSymbols = [];
    for (let i = start, m = start + count; i < m; i++) {
      ownSymbols.push(this.iterator.symbolDescription(i));
    }
    return {
      ownSymbols
    };
  },

  all() {
    return this.slice({ start: 0, count: this.iterator.size });
  }
};

SymbolIteratorActor.prototype.requestTypes = {
  "slice": SymbolIteratorActor.prototype.slice,
  "all": SymbolIteratorActor.prototype.all,
};

/**
 * Functions for adding information to ObjectActor grips for the purpose of
 * having customized output. This object holds arrays mapped by
 * Debugger.Object.prototype.class.
 *
 * In each array you can add functions that take three
 * arguments:
 *   - the ObjectActor instance and its hooks to make a preview for,
 *   - the grip object being prepared for the client,
 *   - the raw JS object after calling Debugger.Object.unsafeDereference(). This
 *   argument is only provided if the object is safe for reading properties and
 *   executing methods. See DevToolsUtils.isSafeJSObject().
 *
 * Functions must return false if they cannot provide preview
 * information for the debugger object, or true otherwise.
 */
DebuggerServer.ObjectActorPreviewers = {
  String: [function (objectActor, grip, rawObj) {
    return wrappedPrimitivePreviewer("String", String, objectActor, grip, rawObj);
  }],

  Boolean: [function (objectActor, grip, rawObj) {
    return wrappedPrimitivePreviewer("Boolean", Boolean, objectActor, grip, rawObj);
  }],

  Number: [function (objectActor, grip, rawObj) {
    return wrappedPrimitivePreviewer("Number", Number, objectActor, grip, rawObj);
  }],

  Symbol: [function (objectActor, grip, rawObj) {
    return wrappedPrimitivePreviewer("Symbol", Symbol, objectActor, grip, rawObj);
  }],

  Function: [function ({obj, hooks}, grip) {
    if (obj.name) {
      grip.name = obj.name;
    }

    if (obj.displayName) {
      grip.displayName = obj.displayName.substr(0, 500);
    }

    if (obj.parameterNames) {
      grip.parameterNames = obj.parameterNames;
    }

    // Check if the developer has added a de-facto standard displayName
    // property for us to use.
    let userDisplayName;
    try {
      userDisplayName = obj.getOwnPropertyDescriptor("displayName");
    } catch (e) {
      // The above can throw "permission denied" errors when the debuggee
      // does not subsume the function's compartment.
    }

    if (userDisplayName && typeof userDisplayName.value == "string" &&
        userDisplayName.value) {
      grip.userDisplayName = hooks.createValueGrip(userDisplayName.value);
    }

    let dbgGlobal = hooks.getGlobalDebugObject();
    if (dbgGlobal) {
      let script = dbgGlobal.makeDebuggeeValue(obj.unsafeDereference()).script;
      if (script) {
        grip.location = {
          url: script.url,
          line: script.startLine
        };
      }
    }

    return true;
  }],

  RegExp: [function ({obj, hooks}, grip) {
    let str = DevToolsUtils.callPropertyOnObject(obj, "toString");
    if (typeof str != "string") {
      return false;
    }

    grip.displayString = hooks.createValueGrip(str);
    return true;
  }],

  Date: [function ({obj, hooks}, grip) {
    let time = DevToolsUtils.callPropertyOnObject(obj, "getTime");
    if (typeof time != "number") {
      return false;
    }

    grip.preview = {
      timestamp: hooks.createValueGrip(time),
    };
    return true;
  }],

  Array: [function ({obj, hooks}, grip) {
    let length = getArrayLength(obj);

    grip.preview = {
      kind: "ArrayLike",
      length: length,
    };

    if (hooks.getGripDepth() > 1) {
      return true;
    }

    let raw = obj.unsafeDereference();
    let items = grip.preview.items = [];

    for (let i = 0; i < length; ++i) {
      // Array Xrays filter out various possibly-unsafe properties (like
      // functions, and claim that the value is undefined instead. This
      // is generally the right thing for privileged code accessing untrusted
      // objects, but quite confusing for Object previews. So we manually
      // override this protection by waiving Xrays on the array, and re-applying
      // Xrays on any indexed value props that we pull off of it.
      let desc = Object.getOwnPropertyDescriptor(Cu.waiveXrays(raw), i);
      if (desc && !desc.get && !desc.set) {
        let value = Cu.unwaiveXrays(desc.value);
        value = makeDebuggeeValueIfNeeded(obj, value);
        items.push(hooks.createValueGrip(value));
      } else {
        items.push(null);
      }

      if (items.length == OBJECT_PREVIEW_MAX_ITEMS) {
        break;
      }
    }

    return true;
  }],

  Set: [function (objectActor, grip) {
    let size = DevToolsUtils.getProperty(objectActor.obj, "size");
    if (typeof size != "number") {
      return false;
    }

    grip.preview = {
      kind: "ArrayLike",
      length: size,
    };

    // Avoid recursive object grips.
    if (objectActor.hooks.getGripDepth() > 1) {
      return true;
    }

    let items = grip.preview.items = [];
    for (let item of enumSetEntries(objectActor)) {
      items.push(item);
      if (items.length == OBJECT_PREVIEW_MAX_ITEMS) {
        break;
      }
    }

    return true;
  }],

  WeakSet: [function (objectActor, grip) {
    let enumEntries = enumWeakSetEntries(objectActor);

    grip.preview = {
      kind: "ArrayLike",
      length: enumEntries.size
    };

    // Avoid recursive object grips.
    if (objectActor.hooks.getGripDepth() > 1) {
      return true;
    }

    let items = grip.preview.items = [];
    for (let item of enumEntries) {
      items.push(item);
      if (items.length == OBJECT_PREVIEW_MAX_ITEMS) {
        break;
      }
    }

    return true;
  }],

  Map: [function (objectActor, grip) {
    let size = DevToolsUtils.getProperty(objectActor.obj, "size");
    if (typeof size != "number") {
      return false;
    }

    grip.preview = {
      kind: "MapLike",
      size: size,
    };

    if (objectActor.hooks.getGripDepth() > 1) {
      return true;
    }

    let entries = grip.preview.entries = [];
    for (let entry of enumMapEntries(objectActor)) {
      entries.push(entry);
      if (entries.length == OBJECT_PREVIEW_MAX_ITEMS) {
        break;
      }
    }

    return true;
  }],

  WeakMap: [function (objectActor, grip) {
    let enumEntries = enumWeakMapEntries(objectActor);

    grip.preview = {
      kind: "MapLike",
      size: enumEntries.size
    };

    if (objectActor.hooks.getGripDepth() > 1) {
      return true;
    }

    let entries = grip.preview.entries = [];
    for (let entry of enumEntries) {
      entries.push(entry);
      if (entries.length == OBJECT_PREVIEW_MAX_ITEMS) {
        break;
      }
    }

    return true;
  }],

  DOMStringMap: [function ({obj, hooks}, grip, rawObj) {
    if (!rawObj) {
      return false;
    }

    let keys = obj.getOwnPropertyNames();
    grip.preview = {
      kind: "MapLike",
      size: keys.length,
    };

    if (hooks.getGripDepth() > 1) {
      return true;
    }

    let entries = grip.preview.entries = [];
    for (let key of keys) {
      let value = makeDebuggeeValueIfNeeded(obj, rawObj[key]);
      entries.push([key, hooks.createValueGrip(value)]);
      if (entries.length == OBJECT_PREVIEW_MAX_ITEMS) {
        break;
      }
    }

    return true;
  }],

  Proxy: [function ({obj, hooks}, grip, rawObj) {
    // The `isProxy` getter of the debuggee object only detects proxies without
    // security wrappers. If false, the target and handler are not available.
    let hasTargetAndHandler = obj.isProxy;
    if (hasTargetAndHandler) {
      grip.proxyTarget = hooks.createValueGrip(obj.proxyTarget);
      grip.proxyHandler = hooks.createValueGrip(obj.proxyHandler);
    }

    grip.preview = {
      kind: "Object",
      ownProperties: Object.create(null),
      ownPropertiesLength: 2 * hasTargetAndHandler
    };

    if (hooks.getGripDepth() > 1) {
      return true;
    }

    if (hasTargetAndHandler) {
      grip.preview.ownProperties["<target>"] = {value: grip.proxyTarget};
      grip.preview.ownProperties["<handler>"] = {value: grip.proxyHandler};
    }

    return true;
  }],
};

/**
 * Generic previewer for classes wrapping primitives, like String,
 * Number and Boolean.
 *
 * @param string className
 *        Class name to expect.
 * @param object classObj
 *        The class to expect, eg. String. The valueOf() method of the class is
 *        invoked on the given object.
 * @param ObjectActor objectActor
 *        The object actor
 * @param Object grip
 *        The result grip to fill in
 * @return Booolean true if the object was handled, false otherwise
 */
function wrappedPrimitivePreviewer(className, classObj, objectActor, grip, rawObj) {
  let {obj, hooks} = objectActor;

  let v = null;
  try {
    v = classObj.prototype.valueOf.call(rawObj);
  } catch (ex) {
    // valueOf() can throw if the raw JS object is "misbehaved".
    return false;
  }

  if (v === null) {
    return false;
  }

  let canHandle = GenericObject(objectActor, grip, rawObj, className === "String");
  if (!canHandle) {
    return false;
  }

  grip.preview.wrappedValue =
    hooks.createValueGrip(makeDebuggeeValueIfNeeded(obj, v));
  return true;
}

function GenericObject(objectActor, grip, rawObj, specialStringBehavior = false) {
  let {obj, hooks} = objectActor;
  if (grip.preview || grip.displayString || hooks.getGripDepth() > 1) {
    return false;
  }

  let i = 0, names = [], symbols = [];
  let preview = grip.preview = {
    kind: "Object",
    ownProperties: Object.create(null),
    ownSymbols: [],
  };

  try {
    names = obj.getOwnPropertyNames();
    symbols = obj.getOwnPropertySymbols();
  } catch (ex) {
    // Calling getOwnPropertyNames() on some wrapped native prototypes is not
    // allowed: "cannot modify properties of a WrappedNative". See bug 952093.
  }
  preview.ownPropertiesLength = names.length;
  preview.ownSymbolsLength = symbols.length;

  let length;
  if (specialStringBehavior) {
    length = DevToolsUtils.getProperty(obj, "length");
    if (typeof length != "number") {
      specialStringBehavior = false;
    }
  }

  for (let name of names) {
    if (specialStringBehavior && /^[0-9]+$/.test(name)) {
      let num = parseInt(name, 10);
      if (num.toString() === name && num >= 0 && num < length) {
        continue;
      }
    }

    let desc = objectActor._propertyDescriptor(name, true);
    if (!desc) {
      continue;
    }

    preview.ownProperties[name] = desc;
    if (++i == OBJECT_PREVIEW_MAX_ITEMS) {
      break;
    }
  }

  for (let symbol of symbols) {
    let descriptor = objectActor._propertyDescriptor(symbol, true);
    if (!descriptor) {
      continue;
    }

    preview.ownSymbols.push(Object.assign({
      descriptor
    }, hooks.createValueGrip(symbol)));

    if (++i == OBJECT_PREVIEW_MAX_ITEMS) {
      break;
    }
  }

  if (i < OBJECT_PREVIEW_MAX_ITEMS) {
    preview.safeGetterValues = objectActor._findSafeGetterValues(
      Object.keys(preview.ownProperties),
      OBJECT_PREVIEW_MAX_ITEMS - i);
  }

  return true;
}

// Preview functions that do not rely on the object class.
DebuggerServer.ObjectActorPreviewers.Object = [
  function TypedArray({obj, hooks}, grip) {
    if (!isTypedArray(obj)) {
      return false;
    }

    grip.preview = {
      kind: "ArrayLike",
      length: getArrayLength(obj),
    };

    if (hooks.getGripDepth() > 1) {
      return true;
    }

    let raw = obj.unsafeDereference();
    let global = Cu.getGlobalForObject(DebuggerServer);
    let classProto = global[obj.class].prototype;
    // The Xray machinery for TypedArrays denies indexed access on the grounds
    // that it's slow, and advises callers to do a structured clone instead.
    let safeView = Cu.cloneInto(classProto.subarray.call(raw, 0,
      OBJECT_PREVIEW_MAX_ITEMS), global);
    let items = grip.preview.items = [];
    for (let i = 0; i < safeView.length; i++) {
      items.push(safeView[i]);
    }

    return true;
  },

  function Error({obj, hooks}, grip) {
    switch (obj.class) {
      case "Error":
      case "EvalError":
      case "RangeError":
      case "ReferenceError":
      case "SyntaxError":
      case "TypeError":
      case "URIError":
        let name = DevToolsUtils.getProperty(obj, "name");
        let msg = DevToolsUtils.getProperty(obj, "message");
        let stack = DevToolsUtils.getProperty(obj, "stack");
        let fileName = DevToolsUtils.getProperty(obj, "fileName");
        let lineNumber = DevToolsUtils.getProperty(obj, "lineNumber");
        let columnNumber = DevToolsUtils.getProperty(obj, "columnNumber");
        grip.preview = {
          kind: "Error",
          name: hooks.createValueGrip(name),
          message: hooks.createValueGrip(msg),
          stack: hooks.createValueGrip(stack),
          fileName: hooks.createValueGrip(fileName),
          lineNumber: hooks.createValueGrip(lineNumber),
          columnNumber: hooks.createValueGrip(columnNumber),
        };
        return true;
      default:
        return false;
    }
  },

  function CSSMediaRule({obj, hooks}, grip, rawObj) {
    if (isWorker || !rawObj || obj.class != "CSSMediaRule") {
      return false;
    }
    grip.preview = {
      kind: "ObjectWithText",
      text: hooks.createValueGrip(rawObj.conditionText),
    };
    return true;
  },

  function CSSStyleRule({obj, hooks}, grip, rawObj) {
    if (isWorker || !rawObj || obj.class != "CSSStyleRule") {
      return false;
    }
    grip.preview = {
      kind: "ObjectWithText",
      text: hooks.createValueGrip(rawObj.selectorText),
    };
    return true;
  },

  function ObjectWithURL({obj, hooks}, grip, rawObj) {
    if (isWorker || !rawObj || !(obj.class == "CSSImportRule" ||
                                 obj.class == "CSSStyleSheet" ||
                                 obj.class == "Location" ||
                                 rawObj instanceof Ci.nsIDOMWindow)) {
      return false;
    }

    let url;
    if (rawObj instanceof Ci.nsIDOMWindow && rawObj.location) {
      url = rawObj.location.href;
    } else if (rawObj.href) {
      url = rawObj.href;
    } else {
      return false;
    }

    grip.preview = {
      kind: "ObjectWithURL",
      url: hooks.createValueGrip(url),
    };

    return true;
  },

  function ArrayLike({obj, hooks}, grip, rawObj) {
    if (isWorker || !rawObj ||
        obj.class != "DOMStringList" &&
        obj.class != "DOMTokenList" &&
        obj.class != "CSSRuleList" &&
        obj.class != "MediaList" &&
        obj.class != "StyleSheetList" &&
        obj.class != "CSSValueList" &&
        obj.class != "NamedNodeMap" &&
        !(rawObj instanceof Ci.nsIDOMFileList ||
          rawObj instanceof Ci.nsIDOMNodeList)) {
      return false;
    }

    if (typeof rawObj.length != "number") {
      return false;
    }

    grip.preview = {
      kind: "ArrayLike",
      length: rawObj.length,
    };

    if (hooks.getGripDepth() > 1) {
      return true;
    }

    let items = grip.preview.items = [];

    for (let i = 0; i < rawObj.length &&
                    items.length < OBJECT_PREVIEW_MAX_ITEMS; i++) {
      let value = makeDebuggeeValueIfNeeded(obj, rawObj[i]);
      items.push(hooks.createValueGrip(value));
    }

    return true;
  },

  function CSSStyleDeclaration({obj, hooks}, grip, rawObj) {
    if (isWorker || !rawObj ||
        (obj.class != "CSSStyleDeclaration" &&
         obj.class != "CSS2Properties")) {
      return false;
    }

    grip.preview = {
      kind: "MapLike",
      size: rawObj.length,
    };

    let entries = grip.preview.entries = [];

    for (let i = 0; i < OBJECT_PREVIEW_MAX_ITEMS &&
                    i < rawObj.length; i++) {
      let prop = rawObj[i];
      let value = rawObj.getPropertyValue(prop);
      entries.push([prop, hooks.createValueGrip(value)]);
    }

    return true;
  },

  function DOMNode({obj, hooks}, grip, rawObj) {
    if (isWorker || obj.class == "Object" || !rawObj ||
        !(rawObj instanceof Ci.nsIDOMNode)) {
      return false;
    }

    let preview = grip.preview = {
      kind: "DOMNode",
      nodeType: rawObj.nodeType,
      nodeName: rawObj.nodeName,
      isConnected: rawObj.isConnected === true,
    };

    if (rawObj instanceof Ci.nsIDOMDocument && rawObj.location) {
      preview.location = hooks.createValueGrip(rawObj.location.href);
    } else if (rawObj instanceof Ci.nsIDOMDocumentFragment) {
      preview.childNodesLength = rawObj.childNodes.length;

      if (hooks.getGripDepth() < 2) {
        preview.childNodes = [];
        for (let node of rawObj.childNodes) {
          let actor = hooks.createValueGrip(obj.makeDebuggeeValue(node));
          preview.childNodes.push(actor);
          if (preview.childNodes.length == OBJECT_PREVIEW_MAX_ITEMS) {
            break;
          }
        }
      }
    } else if (rawObj instanceof Ci.nsIDOMElement) {
      // For HTML elements (in an HTML document, at least), the nodeName is an
      // uppercased version of the actual element name.  Check for HTML
      // elements, that is elements in the HTML namespace, and lowercase the
      // nodeName in that case.
      if (rawObj.namespaceURI == "http://www.w3.org/1999/xhtml") {
        preview.nodeName = preview.nodeName.toLowerCase();
      }

      // Add preview for DOM element attributes.
      preview.attributes = {};
      preview.attributesLength = rawObj.attributes.length;
      for (let attr of rawObj.attributes) {
        preview.attributes[attr.nodeName] = hooks.createValueGrip(attr.value);
      }
    } else if (obj.class == "Attr") {
      preview.value = hooks.createValueGrip(rawObj.value);
    } else if (rawObj instanceof Ci.nsIDOMText ||
               rawObj instanceof Ci.nsIDOMComment) {
      preview.textContent = hooks.createValueGrip(rawObj.textContent);
    }

    return true;
  },

  function DOMEvent({obj, hooks}, grip, rawObj) {
    if (isWorker || !rawObj || !(rawObj instanceof Ci.nsIDOMEvent)) {
      return false;
    }

    let preview = grip.preview = {
      kind: "DOMEvent",
      type: rawObj.type,
      properties: Object.create(null),
    };

    if (hooks.getGripDepth() < 2) {
      let target = obj.makeDebuggeeValue(rawObj.target);
      preview.target = hooks.createValueGrip(target);
    }

    let props = [];
    if (rawObj instanceof Ci.nsIDOMMouseEvent) {
      props.push("buttons", "clientX", "clientY", "layerX", "layerY");
    } else if (obj.class == "KeyboardEvent") {
      let modifiers = [];
      if (rawObj.altKey) {
        modifiers.push("Alt");
      }
      if (rawObj.ctrlKey) {
        modifiers.push("Control");
      }
      if (rawObj.metaKey) {
        modifiers.push("Meta");
      }
      if (rawObj.shiftKey) {
        modifiers.push("Shift");
      }
      preview.eventKind = "key";
      preview.modifiers = modifiers;

      props.push("key", "charCode", "keyCode");
    } else if (obj.class == "TransitionEvent") {
      props.push("propertyName", "pseudoElement");
    } else if (obj.class == "AnimationEvent") {
      props.push("animationName", "pseudoElement");
    } else if (rawObj instanceof Ci.nsIDOMClipboardEvent) {
      props.push("clipboardData");
    }

    // Add event-specific properties.
    for (let prop of props) {
      let value = rawObj[prop];
      if (value && (typeof value == "object" || typeof value == "function")) {
        // Skip properties pointing to objects.
        if (hooks.getGripDepth() > 1) {
          continue;
        }
        value = obj.makeDebuggeeValue(value);
      }
      preview.properties[prop] = hooks.createValueGrip(value);
    }

    // Add any properties we find on the event object.
    if (!props.length) {
      let i = 0;
      for (let prop in rawObj) {
        let value = rawObj[prop];
        if (prop == "target" || prop == "type" || value === null ||
            typeof value == "function") {
          continue;
        }
        if (value && typeof value == "object") {
          if (hooks.getGripDepth() > 1) {
            continue;
          }
          value = obj.makeDebuggeeValue(value);
        }
        preview.properties[prop] = hooks.createValueGrip(value);
        if (++i == OBJECT_PREVIEW_MAX_ITEMS) {
          break;
        }
      }
    }

    return true;
  },

  function DOMException({obj, hooks}, grip, rawObj) {
    if (isWorker || !rawObj || !(rawObj instanceof Ci.nsIDOMDOMException)) {
      return false;
    }

    grip.preview = {
      kind: "DOMException",
      name: hooks.createValueGrip(rawObj.name),
      message: hooks.createValueGrip(rawObj.message),
      code: hooks.createValueGrip(rawObj.code),
      result: hooks.createValueGrip(rawObj.result),
      filename: hooks.createValueGrip(rawObj.filename),
      lineNumber: hooks.createValueGrip(rawObj.lineNumber),
      columnNumber: hooks.createValueGrip(rawObj.columnNumber),
    };

    return true;
  },

  function PseudoArray({obj, hooks}, grip, rawObj) {
    // An object is considered a pseudo-array if all the following apply:
    // - All its properties are array indices except, optionally, a "length" property.
    // - At least it has the "0" array index.
    // - The array indices are consecutive.
    // - The value of "length", if present, is the number of array indices.

    let keys;
    try {
      keys = obj.getOwnPropertyNames();
    } catch (err) {
      // The above can throw when the debuggee does not subsume the object's
      // compartment, or for some WrappedNatives like Cu.Sandbox.
      return false;
    }
    let {length} = keys;
    if (length === 0) {
      return false;
    }

    // Array indices should be sorted at the beginning, from smallest to largest.
    // Other properties should be at the end, so check if the last one is "length".
    if (keys[length - 1] === "length") {
      --length;
      if (length === 0 || length !== DevToolsUtils.getProperty(obj, "length")) {
        return false;
      }
    }

    // Check that the last key is the array index expected at that position.
    let lastKey = keys[length - 1];
    if (!isArrayIndex(lastKey) || +lastKey !== length - 1) {
      return false;
    }

    grip.preview = {
      kind: "ArrayLike",
      length: length,
    };

    // Avoid recursive object grips.
    if (hooks.getGripDepth() > 1) {
      return true;
    }

    let items = grip.preview.items = [];
    let numItems = Math.min(OBJECT_PREVIEW_MAX_ITEMS, length);

    for (let i = 0; i < numItems; ++i) {
      let desc = obj.getOwnPropertyDescriptor(i);
      if (desc && "value" in desc) {
        items.push(hooks.createValueGrip(desc.value));
      } else {
        items.push(null);
      }
    }

    return true;
  },

  function Object(objectActor, grip, rawObj) {
    return GenericObject(objectActor, grip, rawObj, /* specialStringBehavior = */ false);
  },
];

/**
 * Get thisDebugger.Object referent's `promiseState`.
 *
 * @returns Object
 *          An object of one of the following forms:
 *          - { state: "pending" }
 *          - { state: "fulfilled", value }
 *          - { state: "rejected", reason }
 */
function getPromiseState(obj) {
  if (obj.class != "Promise") {
    throw new Error(
      "Can't call `getPromiseState` on `Debugger.Object`s that don't " +
      "refer to Promise objects.");
  }

  let state = { state: obj.promiseState };
  if (state.state === "fulfilled") {
    state.value = obj.promiseValue;
  } else if (state.state === "rejected") {
    state.reason = obj.promiseReason;
  }
  return state;
}

/**
 * Determine if a given value is non-primitive.
 *
 * @param Any value
 *        The value to test.
 * @return Boolean
 *         Whether the value is non-primitive.
 */
function isObject(value) {
  const type = typeof value;
  return type == "object" ? value !== null : type == "function";
}

/**
 * Create a function that can safely stringify Debugger.Objects of a given
 * builtin type.
 *
 * @param Function ctor
 *        The builtin class constructor.
 * @return Function
 *         The stringifier for the class.
 */
function createBuiltinStringifier(ctor) {
  return obj => {
    try {
      return ctor.prototype.toString.call(obj.unsafeDereference());
    } catch (err) {
      // The debuggee will see a "Function" class if the object is callable and
      // its compartment is not subsumed. The above will throw if it's not really
      // a function, e.g. if it's a callable proxy.
      return "[object " + obj.class + "]";
    }
  };
}

/**
 * Stringify a Debugger.Object-wrapped Error instance.
 *
 * @param Debugger.Object obj
 *        The object to stringify.
 * @return String
 *         The stringification of the object.
 */
function errorStringify(obj) {
  let name = DevToolsUtils.getProperty(obj, "name");
  if (name === "" || name === undefined) {
    name = obj.class;
  } else if (isObject(name)) {
    name = stringify(name);
  }

  let message = DevToolsUtils.getProperty(obj, "message");
  if (isObject(message)) {
    message = stringify(message);
  }

  if (message === "" || message === undefined) {
    return name;
  }
  return name + ": " + message;
}

/**
 * Stringify a Debugger.Object based on its class.
 *
 * @param Debugger.Object obj
 *        The object to stringify.
 * @return String
 *         The stringification for the object.
 */
function stringify(obj) {
  if (!DevToolsUtils.isSafeDebuggerObject(obj)) {
    if (DevToolsUtils.isCPOW(obj)) {
      return "<cpow>";
    }
    let unwrapped = DevToolsUtils.unwrap(obj);
    if (unwrapped === undefined) {
      return "<invisibleToDebugger>";
    } else if (unwrapped.isProxy) {
      return "<proxy>";
    }
    // The following line should not be reached. It's there just in case somebody
    // modifies isSafeDebuggerObject to return false for additional kinds of objects.
    return "[object " + obj.class + "]";
  } else if (obj.class == "DeadObject") {
    return "<dead object>";
  }

  const stringifier = stringifiers[obj.class] || stringifiers.Object;

  try {
    return stringifier(obj);
  } catch (e) {
    DevToolsUtils.reportException("stringify", e);
    return "<failed to stringify object>";
  }
}

// Used to prevent infinite recursion when an array is found inside itself.
var seen = null;

var stringifiers = {
  Error: errorStringify,
  EvalError: errorStringify,
  RangeError: errorStringify,
  ReferenceError: errorStringify,
  SyntaxError: errorStringify,
  TypeError: errorStringify,
  URIError: errorStringify,
  Boolean: createBuiltinStringifier(Boolean),
  Function: createBuiltinStringifier(Function),
  Number: createBuiltinStringifier(Number),
  RegExp: createBuiltinStringifier(RegExp),
  String: createBuiltinStringifier(String),
  Object: obj => "[object " + obj.class + "]",
  Array: obj => {
    // If we're at the top level then we need to create the Set for tracking
    // previously stringified arrays.
    const topLevel = !seen;
    if (topLevel) {
      seen = new Set();
    } else if (seen.has(obj)) {
      return "";
    }

    seen.add(obj);

    const len = getArrayLength(obj);
    let string = "";

    // Array.length is always a non-negative safe integer.
    for (let i = 0; i < len; i++) {
      const desc = obj.getOwnPropertyDescriptor(i);
      if (desc) {
        const { value } = desc;
        if (value != null) {
          string += isObject(value) ? stringify(value) : value;
        }
      }

      if (i < len - 1) {
        string += ",";
      }
    }

    if (topLevel) {
      seen = null;
    }

    return string;
  },
  DOMException: obj => {
    const message = DevToolsUtils.getProperty(obj, "message") || "<no message>";
    const result = (+DevToolsUtils.getProperty(obj, "result")).toString(16);
    const code = DevToolsUtils.getProperty(obj, "code");
    const name = DevToolsUtils.getProperty(obj, "name") || "<unknown>";

    return '[Exception... "' + message + '" ' +
           'code: "' + code + '" ' +
           'nsresult: "0x' + result + " (" + name + ')"]';
  },
  Promise: obj => {
    const { state, value, reason } = getPromiseState(obj);
    let statePreview = state;
    if (state != "pending") {
      const settledValue = state === "fulfilled" ? value : reason;
      statePreview += ": " + (typeof settledValue === "object" && settledValue !== null
                                ? stringify(settledValue)
                                : settledValue);
    }
    return "Promise (" + statePreview + ")";
  },
};

/**
 * Make a debuggee value for the given object, if needed. Primitive values
 * are left the same.
 *
 * Use case: you have a raw JS object (after unsafe dereference) and you want to
 * send it to the client. In that case you need to use an ObjectActor which
 * requires a debuggee value. The Debugger.Object.prototype.makeDebuggeeValue()
 * method works only for JS objects and functions.
 *
 * @param Debugger.Object obj
 * @param any value
 * @return object
 */
function makeDebuggeeValueIfNeeded(obj, value) {
  if (value && (typeof value == "object" || typeof value == "function")) {
    return obj.makeDebuggeeValue(value);
  }
  return value;
}

/**
 * Creates an actor for the specified "very long" string. "Very long" is specified
 * at the server's discretion.
 *
 * @param string String
 *        The string.
 */
function LongStringActor(string) {
  this.string = string;
  this.stringLength = string.length;
}

LongStringActor.prototype = {
  actorPrefix: "longString",

  rawValue: function () {
    return this.string;
  },

  destroy: function () {
    // Because longStringActors is not a weak map, we won't automatically leave
    // it so we need to manually leave on destroy so that we don't leak
    // memory.
    this._releaseActor();
  },

  /**
   * Returns a grip for this actor for returning in a protocol message.
   */
  grip: function () {
    return {
      "type": "longString",
      "initial": this.string.substring(
        0, DebuggerServer.LONG_STRING_INITIAL_LENGTH),
      "length": this.stringLength,
      "actor": this.actorID
    };
  },

  /**
   * Handle a request to extract part of this actor's string.
   *
   * @param request object
   *        The protocol request object.
   */
  onSubstring: function (request) {
    return {
      "from": this.actorID,
      "substring": this.string.substring(request.start, request.end)
    };
  },

  /**
   * Handle a request to release this LongStringActor instance.
   */
  onRelease: function () {
    // TODO: also check if registeredPool === threadActor.threadLifetimePool
    // when the web console moves away from manually releasing pause-scoped
    // actors.
    this._releaseActor();
    this.registeredPool.removeActor(this);
    return {};
  },

  _releaseActor: function () {
    if (this.registeredPool && this.registeredPool.longStringActors) {
      delete this.registeredPool.longStringActors[this.string];
    }
  }
};

LongStringActor.prototype.requestTypes = {
  "substring": LongStringActor.prototype.onSubstring,
  "release": LongStringActor.prototype.onRelease
};

/**
 * Creates an actor for the specified symbol.
 *
 * @param symbol Symbol
 *        The symbol.
 */
function SymbolActor(symbol) {
  this.symbol = symbol;
}

SymbolActor.prototype = {
  actorPrefix: "symbol",

  rawValue: function () {
    return this.symbol;
  },

  destroy: function () {
    // Because symbolActors is not a weak map, we won't automatically leave
    // it so we need to manually leave on destroy so that we don't leak
    // memory.
    this._releaseActor();
  },

  /**
   * Returns a grip for this actor for returning in a protocol message.
   */
  grip: function () {
    let form = {
      type: "symbol",
      actor: this.actorID,
    };
    let name = getSymbolName(this.symbol);
    if (name !== undefined) {
      // Create a grip for the name because it might be a longString.
      form.name = createValueGrip(name, this.registeredPool);
    }
    return form;
  },

  /**
   * Handle a request to release this SymbolActor instance.
   */
  onRelease: function () {
    // TODO: also check if registeredPool === threadActor.threadLifetimePool
    // when the web console moves away from manually releasing pause-scoped
    // actors.
    this._releaseActor();
    this.registeredPool.removeActor(this);
    return {};
  },

  _releaseActor: function () {
    if (this.registeredPool && this.registeredPool.symbolActors) {
      delete this.registeredPool.symbolActors[this.symbol];
    }
  }
};

SymbolActor.prototype.requestTypes = {
  "release": SymbolActor.prototype.onRelease
};

/**
 * Creates an actor for the specified ArrayBuffer.
 *
 * @param buffer ArrayBuffer
 *        The buffer.
 */
function ArrayBufferActor(buffer) {
  this.buffer = buffer;
  this.bufferLength = buffer.byteLength;
}

ArrayBufferActor.prototype = {
  actorPrefix: "arrayBuffer",

  rawValue: function () {
    return this.buffer;
  },

  destroy: function () {
  },

  grip() {
    return {
      "type": "arrayBuffer",
      "length": this.bufferLength,
      "actor": this.actorID
    };
  },

  onSlice({start, count}) {
    let slice = new Uint8Array(this.buffer, start, count);
    let parts = [], offset = 0;
    const PortionSize = 0x6000; // keep it divisible by 3 for btoa() and join()
    while (offset + PortionSize < count) {
      parts.push(btoa(
        String.fromCharCode.apply(null, slice.subarray(offset, offset + PortionSize))));
      offset += PortionSize;
    }
    parts.push(btoa(String.fromCharCode.apply(null, slice.subarray(offset, count))));
    return {
      "from": this.actorID,
      "encoded": parts.join(""),
    };
  }
};

ArrayBufferActor.prototype.requestTypes = {
  "slice": ArrayBufferActor.prototype.onSlice,
};

/**
 * Create a grip for the given debuggee value.  If the value is an
 * object, will create an actor with the given lifetime.
 */
function createValueGrip(value, pool, makeObjectGrip) {
  switch (typeof value) {
    case "boolean":
      return value;

    case "string":
      if (stringIsLong(value)) {
        return longStringGrip(value, pool);
      }
      return value;

    case "number":
      if (value === Infinity) {
        return { type: "Infinity" };
      } else if (value === -Infinity) {
        return { type: "-Infinity" };
      } else if (Number.isNaN(value)) {
        return { type: "NaN" };
      } else if (!value && 1 / value === -Infinity) {
        return { type: "-0" };
      }
      return value;

    case "undefined":
      return { type: "undefined" };

    case "object":
      if (value === null) {
        return { type: "null" };
      } else if (value.optimizedOut ||
             value.uninitialized ||
             value.missingArguments) {
        // The slot is optimized out, an uninitialized binding, or
        // arguments on a dead scope
        return {
          type: "null",
          optimizedOut: value.optimizedOut,
          uninitialized: value.uninitialized,
          missingArguments: value.missingArguments
        };
      }
      return makeObjectGrip(value, pool);

    case "symbol":
      return symbolGrip(value, pool);

    default:
      assert(false, "Failed to provide a grip for: " + value);
      return null;
  }
}

const symbolProtoToString = Symbol.prototype.toString;

function getSymbolName(symbol) {
  const name = symbolProtoToString.call(symbol).slice("Symbol(".length, -1);
  return name || undefined;
}

/**
 * Returns true if the string is long enough to use a LongStringActor instead
 * of passing the value directly over the protocol.
 *
 * @param str String
 *        The string we are checking the length of.
 */
function stringIsLong(str) {
  return str.length >= DebuggerServer.LONG_STRING_LENGTH;
}

/**
 * Create a grip for the given string.
 *
 * @param str String
 *        The string we are creating a grip for.
 * @param pool ActorPool
 *        The actor pool where the new actor will be added.
 */
function longStringGrip(str, pool) {
  if (!pool.longStringActors) {
    pool.longStringActors = {};
  }

  if (pool.longStringActors.hasOwnProperty(str)) {
    return pool.longStringActors[str].grip();
  }

  let actor = new LongStringActor(str);
  pool.addActor(actor);
  pool.longStringActors[str] = actor;
  return actor.grip();
}

/**
 * Create a grip for the given symbol.
 *
 * @param sym Symbol
 *        The symbol we are creating a grip for.
 * @param pool ActorPool
 *        The actor pool where the new actor will be added.
 */
function symbolGrip(sym, pool) {
  if (!pool.symbolActors) {
    pool.symbolActors = Object.create(null);
  }

  if (sym in pool.symbolActors) {
    return pool.symbolActors[sym].grip();
  }

  let actor = new SymbolActor(sym);
  pool.addActor(actor);
  pool.symbolActors[sym] = actor;
  return actor.grip();
}

/**
 * Create a grip for the given ArrayBuffer.
 *
 * @param buffer ArrayBuffer
 *        The ArrayBuffer we are creating a grip for.
 * @param pool ActorPool
 *        The actor pool where the new actor will be added.
 */
function arrayBufferGrip(buffer, pool) {
  if (!pool.arrayBufferActors) {
    pool.arrayBufferActors = new WeakMap();
  }

  if (pool.arrayBufferActors.has(buffer)) {
    return pool.arrayBufferActors.get(buffer).grip();
  }

  let actor = new ArrayBufferActor(buffer);
  pool.addActor(actor);
  pool.arrayBufferActors.set(buffer, actor);
  return actor.grip();
}

const TYPED_ARRAY_CLASSES = ["Uint8Array", "Uint8ClampedArray", "Uint16Array",
                             "Uint32Array", "Int8Array", "Int16Array", "Int32Array",
                             "Float32Array", "Float64Array"];

/**
 * Returns true if a debuggee object is a typed array.
 *
 * @param obj Debugger.Object
 *        The debuggee object to test.
 * @return Boolean
 */
function isTypedArray(object) {
  return TYPED_ARRAY_CLASSES.includes(object.class);
}

/**
 * Returns true if a debuggee object is an array, including a typed array.
 *
 * @param obj Debugger.Object
 *        The debuggee object to test.
 * @return Boolean
 */
function isArray(object) {
  return isTypedArray(object) || object.class === "Array";
}

/**
 * Returns the length of an array (or typed array).
 *
 * @param obj Debugger.Object
 *        The debuggee object of the array.
 * @return Number
 * @throws if the object is not an array.
 */
function getArrayLength(object) {
  if (!isArray(object)) {
    throw new Error("Expected an array, got a " + object.class);
  }

  // Real arrays have a reliable `length` own property.
  if (object.class === "Array") {
    return DevToolsUtils.getProperty(object, "length");
  }

  // For typed arrays, `DevToolsUtils.getProperty` is not reliable because the `length`
  // getter could be shadowed by an own property, and `getOwnPropertyNames` is
  // unnecessarily slow. Obtain the `length` getter safely and call it manually.
  let typedProto = Object.getPrototypeOf(Uint8Array.prototype);
  let getter = Object.getOwnPropertyDescriptor(typedProto, "length").get;
  return getter.call(object.unsafeDereference());
}

/**
 * Returns true if the parameter can be stored as a 32-bit unsigned integer.
 * If so, it will be suitable for use as the length of an array object.
 *
 * @param num Number
 *        The number to test.
 * @return Boolean
 */
function isUint32(num) {
  return num >>> 0 === num;
}

/**
 * Returns true if the parameter is suitable to be an array index.
 *
 * @param str String
 * @return Boolean
 */
function isArrayIndex(str) {
  // Transform the parameter to a 32-bit unsigned integer.
  let num = str >>> 0;
  // Check that the parameter is a canonical Uint32 index.
  return num + "" === str &&
    // Array indices cannot attain the maximum Uint32 value.
    num != -1 >>> 0;
}

exports.ObjectActor = ObjectActor;
exports.PropertyIteratorActor = PropertyIteratorActor;
exports.LongStringActor = LongStringActor;
exports.SymbolActor = SymbolActor;
exports.createValueGrip = createValueGrip;
exports.stringIsLong = stringIsLong;
exports.longStringGrip = longStringGrip;
exports.arrayBufferGrip = arrayBufferGrip;
PK
!<�F�uu>chrome/devtools/modules/devtools/server/actors/pause-scoped.js/* -*- indent-tabs-mode: nil; js-indent-level: 2; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 { ObjectActor } = require("devtools/server/actors/object");

/**
 * A base actor for any actors that should only respond receive messages in the
 * paused state. Subclasses may expose a `threadActor` which is used to help
 * determine when we are in a paused state. Subclasses should set their own
 * "constructor" property if they want better error messages. You should never
 * instantiate a PauseScopedActor directly, only through subclasses.
 */
function PauseScopedActor() {
}

/**
 * A function decorator for creating methods to handle protocol messages that
 * should only be received while in the paused state.
 *
 * @param method Function
 *        The function we are decorating.
 */
PauseScopedActor.withPaused = function (method) {
  return function () {
    if (this.isPaused()) {
      return method.apply(this, arguments);
    }
    return this._wrongState();
  };
};

PauseScopedActor.prototype = {

  /**
   * Returns true if we are in the paused state.
   */
  isPaused: function () {
    // When there is not a ThreadActor available (like in the webconsole) we
    // have to be optimistic and assume that we are paused so that we can
    // respond to requests.
    return this.threadActor ? this.threadActor.state === "paused" : true;
  },

  /**
   * Returns the wrongState response packet for this actor.
   */
  _wrongState: function () {
    return {
      error: "wrongState",
      message: this.constructor.name +
        " actors can only be accessed while the thread is paused."
    };
  }
};

/**
 * Creates a pause-scoped actor for the specified object.
 * @see ObjectActor
 */
function PauseScopedObjectActor(obj, hooks) {
  ObjectActor.call(this, obj, hooks);
  this.hooks.promote = hooks.promote;
  this.hooks.isThreadLifetimePool = hooks.isThreadLifetimePool;
}

PauseScopedObjectActor.prototype = Object.create(PauseScopedActor.prototype);

Object.assign(PauseScopedObjectActor.prototype, ObjectActor.prototype);

Object.assign(PauseScopedObjectActor.prototype, {
  constructor: PauseScopedObjectActor,
  actorPrefix: "pausedobj",

  onOwnPropertyNames:
    PauseScopedActor.withPaused(ObjectActor.prototype.onOwnPropertyNames),

  onPrototypeAndProperties:
    PauseScopedActor.withPaused(ObjectActor.prototype.onPrototypeAndProperties),

  onPrototype: PauseScopedActor.withPaused(ObjectActor.prototype.onPrototype),
  onProperty: PauseScopedActor.withPaused(ObjectActor.prototype.onProperty),
  onDecompile: PauseScopedActor.withPaused(ObjectActor.prototype.onDecompile),

  onDisplayString:
    PauseScopedActor.withPaused(ObjectActor.prototype.onDisplayString),

  onParameterNames:
    PauseScopedActor.withPaused(ObjectActor.prototype.onParameterNames),

  /**
   * Handle a protocol request to promote a pause-lifetime grip to a
   * thread-lifetime grip.
   *
   * @param request object
   *        The protocol request object.
   */
  onThreadGrip: PauseScopedActor.withPaused(function (request) {
    this.hooks.promote();
    return {};
  }),

  /**
   * Handle a protocol request to release a thread-lifetime grip.
   *
   * @param request object
   *        The protocol request object.
   */
  onRelease: PauseScopedActor.withPaused(function (request) {
    if (this.hooks.isThreadLifetimePool()) {
      return { error: "notReleasable",
               message: "Only thread-lifetime actors can be released." };
    }

    this.release();
    return {};
  }),
});

Object.assign(PauseScopedObjectActor.prototype.requestTypes, {
  "threadGrip": PauseScopedObjectActor.prototype.onThreadGrip,
});

exports.PauseScopedObjectActor = PauseScopedObjectActor;
PK
!<�\���6chrome/devtools/modules/devtools/server/actors/perf.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 protocol = require("devtools/shared/protocol");
const { ActorClassWithSpec, Actor } = protocol;
const { perfSpec } = require("devtools/shared/specs/perf");
const { Ci } = require("chrome");
const Services = require("Services");

loader.lazyImporter(this, "PrivateBrowsingUtils",
  "resource://gre/modules/PrivateBrowsingUtils.jsm");

// Some platforms are built without the Gecko Profiler.
const IS_SUPPORTED_PLATFORM = "nsIProfiler" in Ci;

/**
 * The PerfActor wraps the Gecko Profiler interface
 */
exports.PerfActor = ActorClassWithSpec(perfSpec, {
  initialize(conn) {
    Actor.prototype.initialize.call(this, conn);

    // Only setup the observers on a supported platform.
    if (IS_SUPPORTED_PLATFORM) {
      this._observer = {
        observe: this._observe.bind(this)
      };
      Services.obs.addObserver(this._observer, "profiler-started");
      Services.obs.addObserver(this._observer, "profiler-stopped");
      Services.obs.addObserver(this._observer, "chrome-document-global-created");
      Services.obs.addObserver(this._observer, "last-pb-context-exited");
    }
  },

  destroy() {
    if (!IS_SUPPORTED_PLATFORM) {
      return;
    }
    Services.obs.removeObserver(this._observer, "profiler-started");
    Services.obs.removeObserver(this._observer, "profiler-stopped");
    Services.obs.removeObserver(this._observer, "chrome-document-global-created");
    Services.obs.removeObserver(this._observer, "last-pb-context-exited");
    Actor.prototype.destroy.call(this);
  },

  startProfiler() {
    if (!IS_SUPPORTED_PLATFORM) {
      return false;
    }

    // For a quick implementation, decide on some default values. These may need
    // to be tweaked or made configurable as needed.
    const settings = {
      entries: 1000000,
      interval: 1,
      features: ["js", "stackwalk", "threads", "leaf"],
      threads: ["GeckoMain", "Compositor"]
    };

    try {
      // This can throw an error if the profiler is in the wrong state.
      Services.profiler.StartProfiler(
        settings.entries,
        settings.interval,
        settings.features,
        settings.features.length,
        settings.threads,
        settings.threads.length
      );
    } catch (e) {
      // In case any errors get triggered, bailout with a false.
      return false;
    }

    return true;
  },

  stopProfilerAndDiscardProfile() {
    if (!IS_SUPPORTED_PLATFORM) {
      return;
    }
    Services.profiler.StopProfiler();
  },

  async getProfileAndStopProfiler() {
    if (!IS_SUPPORTED_PLATFORM) {
      return null;
    }
    let profile;
    try {
      // Attempt to pull out the data.
      profile = await Services.profiler.getProfileDataAsync();

      // Stop and discard the buffers.
      Services.profiler.StopProfiler();
    } catch (e) {
      // If there was any kind of error, bailout with no profile.
      return null;
    }

    // Gecko Profiler errors can return an empty object, return null for this case
    // as well.
    if (Object.keys(profile).length === 0) {
      return null;
    }
    return profile;
  },

  isActive() {
    if (!IS_SUPPORTED_PLATFORM) {
      return false;
    }
    return Services.profiler.IsActive();
  },

  isSupportedPlatform() {
    return IS_SUPPORTED_PLATFORM;
  },

  isLockedForPrivateBrowsing() {
    if (!IS_SUPPORTED_PLATFORM) {
      return false;
    }
    return !Services.profiler.CanProfile();
  },

  /**
   * Watch for events that happen within the browser. These can affect the current
   * availability and state of the Gecko Profiler.
   */
  _observe(subject, topic, _data) {
    switch (topic) {
      case "chrome-document-global-created":
        if (PrivateBrowsingUtils.isWindowPrivate(subject)) {
          this.emit("profile-locked-by-private-browsing");
        }
        break;
      case "last-pb-context-exited":
        this.emit("profile-unlocked-from-private-browsing");
        break;
      case "profiler-started":
      case "profiler-stopped":
        this.emit(topic);
        break;
    }
  }
});
PK
!<�OfGchrome/devtools/modules/devtools/server/actors/performance-recording.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Actor, ActorClassWithSpec } = require("devtools/shared/protocol");
const { performanceRecordingSpec } = require("devtools/shared/specs/performance-recording");

loader.lazyRequireGetter(this, "RecordingUtils",
  "devtools/shared/performance/recording-utils");
loader.lazyRequireGetter(this, "PerformanceRecordingCommon",
  "devtools/shared/performance/recording-common", true);

/**
 * This actor wraps the Performance module at devtools/shared/shared/performance.js
 * and provides RDP definitions.
 *
 * @see devtools/shared/shared/performance.js for documentation.
 */
const PerformanceRecordingActor = ActorClassWithSpec(performanceRecordingSpec,
Object.assign({
  form: function (detail) {
    if (detail === "actorid") {
      return this.actorID;
    }

    let form = {
      // actorID is set when this is added to a pool
      actor: this.actorID,
      configuration: this._configuration,
      startingBufferStatus: this._startingBufferStatus,
      console: this._console,
      label: this._label,
      startTime: this._startTime,
      localStartTime: this._localStartTime,
      recording: this._recording,
      completed: this._completed,
      duration: this._duration,
    };

    // Only send profiler data once it exists and it has
    // not yet been sent
    if (this._profile && !this._sentFinalizedData) {
      form.finalizedData = true;
      form.profile = this.getProfile();
      form.systemHost = this.getHostSystemInfo();
      form.systemClient = this.getClientSystemInfo();
      this._sentFinalizedData = true;
    }

    return form;
  },

  /**
   * @param {object} conn
   * @param {object} options
   *        A hash of features that this recording is utilizing.
   * @param {object} meta
   *        A hash of temporary metadata for a recording that is recording
   *        (as opposed to an imported recording).
   */
  initialize: function (conn, options, meta) {
    Actor.prototype.initialize.call(this, conn);
    this._configuration = {
      withMarkers: options.withMarkers || false,
      withTicks: options.withTicks || false,
      withMemory: options.withMemory || false,
      withAllocations: options.withAllocations || false,
      allocationsSampleProbability: options.allocationsSampleProbability || 0,
      allocationsMaxLogLength: options.allocationsMaxLogLength || 0,
      bufferSize: options.bufferSize || 0,
      sampleFrequency: options.sampleFrequency || 1
    };

    this._console = !!options.console;
    this._label = options.label || "";

    if (meta) {
      // Store the start time roughly with Date.now() so when we
      // are checking the duration during a recording, we can get close
      // to the approximate duration to render elements without
      // making a real request
      this._localStartTime = Date.now();

      this._startTime = meta.startTime;
      this._startingBufferStatus = {
        position: meta.position,
        totalSize: meta.totalSize,
        generation: meta.generation
      };

      this._recording = true;
      this._markers = [];
      this._frames = [];
      this._memory = [];
      this._ticks = [];
      this._allocations = { sites: [], timestamps: [], frames: [], sizes: [] };

      this._systemHost = meta.systemHost || {};
      this._systemClient = meta.systemClient || {};
    }
  },

  destroy: function () {
    Actor.prototype.destroy.call(this);
  },

  /**
   * Internal utility called by the PerformanceActor and PerformanceFront on state changes
   * to update the internal state of the PerformanceRecording.
   *
   * @param {string} state
   * @param {object} extraData
   */
  _setState: function (state, extraData) {
    switch (state) {
      case "recording-started": {
        this._recording = true;
        break;
      }
      case "recording-stopping": {
        this._recording = false;
        break;
      }
      case "recording-stopped": {
        this._profile = extraData.profile;
        this._duration = extraData.duration;

        // We filter out all samples that fall out of current profile's range
        // since the profiler is continuously running. Because of this, sample
        // times are not guaranteed to have a zero epoch, so offset the
        // timestamps.
        RecordingUtils.offsetSampleTimes(this._profile, this._startTime);

        // Markers need to be sorted ascending by time, to be properly displayed
        // in a waterfall view.
        this._markers = this._markers.sort((a, b) => (a.start > b.start));

        this._completed = true;
        break;
      }
    }
  },
}, PerformanceRecordingCommon));

exports.PerformanceRecordingActor = PerformanceRecordingActor;
PK
!<��sdgg=chrome/devtools/modules/devtools/server/actors/performance.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Actor, ActorClassWithSpec } = require("devtools/shared/protocol");
const { actorBridgeWithSpec } = require("devtools/server/actors/common");
const { performanceSpec } = require("devtools/shared/specs/performance");

loader.lazyRequireGetter(this, "PerformanceRecorder",
  "devtools/server/performance/recorder", true);
loader.lazyRequireGetter(this, "normalizePerformanceFeatures",
  "devtools/shared/performance/recording-utils", true);

const PIPE_TO_FRONT_EVENTS = new Set([
  "recording-started", "recording-stopping", "recording-stopped",
  "profiler-status", "timeline-data", "console-profile-start"
]);

const RECORDING_STATE_CHANGE_EVENTS = new Set([
  "recording-started", "recording-stopping", "recording-stopped"
]);

/**
 * This actor wraps the Performance module at devtools/shared/shared/performance.js
 * and provides RDP definitions.
 *
 * @see devtools/shared/shared/performance.js for documentation.
 */
var PerformanceActor = ActorClassWithSpec(performanceSpec, {
  traits: {
    features: {
      withMarkers: true,
      withTicks: true,
      withMemory: true,
      withFrames: true,
      withGCEvents: true,
      withDocLoadingEvents: true,
      withAllocations: true,
    },
  },

  initialize: function (conn, tabActor) {
    Actor.prototype.initialize.call(this, conn);

    this._onRecordingStarted = this._onRecordingStarted.bind(this);
    this._onRecordingStopping = this._onRecordingStopping.bind(this);
    this._onRecordingStopped = this._onRecordingStopped.bind(this);
    this._onProfilerStatus = this._onProfilerStatus.bind(this);
    this._onTimelineData = this._onTimelineData.bind(this);
    this._onConsoleProfileStart = this._onConsoleProfileStart.bind(this);

    this.bridge = new PerformanceRecorder(conn, tabActor);

    this.bridge.on("recording-started", this._onRecordingStarted);
    this.bridge.on("recording-stopping", this._onRecordingStopping);
    this.bridge.on("recording-stopped", this._onRecordingStopped);
    this.bridge.on("profiler-status", this._onProfilerStatus);
    this.bridge.on("timeline-data", this._onTimelineData);
    this.bridge.on("console-profile-start", this._onConsoleProfileStart);
  },

  destroy: function () {
    this.bridge.off("recording-started", this._onRecordingStarted);
    this.bridge.off("recording-stopping", this._onRecordingStopping);
    this.bridge.off("recording-stopped", this._onRecordingStopped);
    this.bridge.off("profiler-status", this._onProfilerStatus);
    this.bridge.off("timeline-data", this._onTimelineData);
    this.bridge.off("console-profile-start", this._onConsoleProfileStart);

    this.bridge.destroy();
    Actor.prototype.destroy.call(this);
  },

  connect: function (config) {
    this.bridge.connect({ systemClient: config.systemClient });
    return { traits: this.traits };
  },

  canCurrentlyRecord: function () {
    return this.bridge.canCurrentlyRecord();
  },

  async startRecording(options = {}) {
    if (!this.bridge.canCurrentlyRecord().success) {
      return null;
    }

    let normalizedOptions = normalizePerformanceFeatures(options, this.traits.features);
    let recording = await this.bridge.startRecording(normalizedOptions);
    this.manage(recording);

    return recording;
  },

  stopRecording: actorBridgeWithSpec("stopRecording"),
  isRecording: actorBridgeWithSpec("isRecording"),
  getRecordings: actorBridgeWithSpec("getRecordings"),
  getConfiguration: actorBridgeWithSpec("getConfiguration"),
  setProfilerStatusInterval: actorBridgeWithSpec("setProfilerStatusInterval"),

  /**
   * Filter which events get piped to the front.
   */
  _onRecordingStarted: function (...data) {
    this._onRecorderEvent("recording-started", data);
  },

  _onRecordingStopping: function (...data) {
    this._onRecorderEvent("recording-stopping", data);
  },

  _onRecordingStopped: function (...data) {
    this._onRecorderEvent("recording-stopped", data);
  },

  _onProfilerStatus: function (...data) {
    this._onRecorderEvent("profiler-status", data);
  },

  _onTimelineData: function (...data) {
    this._onRecorderEvent("timeline-data", data);
  },

  _onConsoleProfileStart: function (...data) {
    this._onRecorderEvent("console-profile-start", data);
  },

  _onRecorderEvent: function (eventName, data) {
    // If this is a recording state change, call
    // a method on the related PerformanceRecordingActor so it can
    // update its internal state.
    if (RECORDING_STATE_CHANGE_EVENTS.has(eventName)) {
      let recording = data[0];
      let extraData = data[1];
      recording._setState(eventName, extraData);
    }

    if (PIPE_TO_FRONT_EVENTS.has(eventName)) {
      this.emit(eventName, ...data);
    }
  },
});

exports.PerformanceActor = PerformanceActor;
PK
!<��&	&	<chrome/devtools/modules/devtools/server/actors/preference.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {Ci} = require("chrome");
const protocol = require("devtools/shared/protocol");
const Services = require("Services");
const {preferenceSpec} = require("devtools/shared/specs/preference");

exports.register = function (handle) {
  handle.addGlobalActor(PreferenceActor, "preferenceActor");
};

exports.unregister = function (handle) {
};

var PreferenceActor = protocol.ActorClassWithSpec(preferenceSpec, {

  typeName: "preference",

  getBoolPref: function (name) {
    return Services.prefs.getBoolPref(name);
  },

  getCharPref: function (name) {
    return Services.prefs.getCharPref(name);
  },

  getIntPref: function (name) {
    return Services.prefs.getIntPref(name);
  },

  getAllPrefs: function () {
    let prefs = {};
    Services.prefs.getChildList("").forEach(function (name, index) {
      // append all key/value pairs into a huge json object.
      try {
        let value;
        switch (Services.prefs.getPrefType(name)) {
          case Ci.nsIPrefBranch.PREF_STRING:
            value = Services.prefs.getCharPref(name);
            break;
          case Ci.nsIPrefBranch.PREF_INT:
            value = Services.prefs.getIntPref(name);
            break;
          case Ci.nsIPrefBranch.PREF_BOOL:
            value = Services.prefs.getBoolPref(name);
            break;
          default:
        }
        prefs[name] = {
          value: value,
          hasUserValue: Services.prefs.prefHasUserValue(name)
        };
      } catch (e) {
        // pref exists but has no user or default value
      }
    });
    return prefs;
  },

  setBoolPref: function (name, value) {
    Services.prefs.setBoolPref(name, value);
    Services.prefs.savePrefFile(null);
  },

  setCharPref: function (name, value) {
    Services.prefs.setCharPref(name, value);
    Services.prefs.savePrefFile(null);
  },

  setIntPref: function (name, value) {
    Services.prefs.setIntPref(name, value);
    Services.prefs.savePrefFile(null);
  },

  clearUserPref: function (name) {
    Services.prefs.clearUserPref(name);
    Services.prefs.savePrefFile(null);
  },
});

exports.PreferenceActor = PreferenceActor;
PK
!<�-��Echrome/devtools/modules/devtools/server/actors/pretty-print-worker.js/* -*- indent-tabs-mode: nil; js-indent-level: 2; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

/* global importScripts, workerHelper, self, prettyFast */

/**
 * This file is meant to be loaded as a ChromeWorker. It accepts messages which
 * have data of the form:
 *
 *     { id, url, indent, source }
 *
 * Where `id` is a unique ID to identify this request, `url` is the url of the
 * source being pretty printed, `indent` is the number of spaces to indent the
 * code by, and `source` is the source text.
 *
 * On success, the worker responds with a message of the form:
 *
 *     { id, code, mappings }
 *
 * Where `id` is the same unique ID from the request, `code` is the pretty
 * printed source text, and `mappings` is an array or source mappings from the
 * pretty printed code back to the ugly source text.
 *
 * In the case of an error, the worker responds with a message of the form:
 *
 *     { id, error }
 */

importScripts("resource://devtools/shared/worker/helper.js");
importScripts("resource://devtools/shared/acorn/acorn.js");
importScripts("resource://devtools/shared/sourcemap/source-map.js");
importScripts("resource://devtools/shared/pretty-fast/pretty-fast.js");

workerHelper.createTask(self, "pretty-print", ({ url, indent, source }) => {
  try {
    const prettified = prettyFast(source, {
      url: url,
      indent: " ".repeat(indent)
    });

    return {
      code: prettified.code,
      mappings: prettified.map._mappings
    };
  } catch (e) {
    return new Error(e.message + "\n" + e.stack);
  }
});
PK
!<��ç	�	9chrome/devtools/modules/devtools/server/actors/process.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var { Cc, Ci } = require("chrome");

loader.lazyGetter(this, "ppmm", () => {
  return Cc["@mozilla.org/parentprocessmessagemanager;1"].getService(
    Ci.nsIMessageBroadcaster);
});

function ProcessActorList() {
  this._actors = new Map();
  this._onListChanged = null;
  this._mustNotify = false;

  this._onMessage = this._onMessage.bind(this);
  this._processScript = "data:text/javascript,sendAsyncMessage('debug:new-process');";
}

ProcessActorList.prototype = {
  getList: function () {
    let processes = [];
    for (let i = 0; i < ppmm.childCount; i++) {
      processes.push({
        // XXX: may not be a perfect id, but process message manager doesn't
        // expose anything...
        id: i,
        // XXX Weak, but appear to be stable
        parent: i == 0,
        // TODO: exposes process message manager on frameloaders in order to compute this
        tabCount: undefined,
      });
    }
    this._mustNotify = true;
    this._checkListening();

    return processes;
  },

  get onListChanged() {
    return this._onListChanged;
  },

  set onListChanged(onListChanged) {
    if (typeof onListChanged !== "function" && onListChanged !== null) {
      throw new Error("onListChanged must be either a function or null.");
    }
    if (onListChanged === this._onListChanged) {
      return;
    }

    this._onListChanged = onListChanged;
    this._checkListening();
  },

  _checkListening: function () {
    if (this._onListChanged !== null && this._mustNotify) {
      this._knownProcesses = [];
      for (let i = 0; i < ppmm.childCount; i++) {
        this._knownProcesses.push(ppmm.getChildAt(i));
      }
      ppmm.addMessageListener("debug:new-process", this._onMessage);
      ppmm.loadProcessScript(this._processScript, true);
    } else {
      ppmm.removeMessageListener("debug:new-process", this._onMessage);
      ppmm.removeDelayedProcessScript(this._processScript);
    }
  },

  _notifyListChanged: function () {
    if (this._mustNotify) {
      this._onListChanged();
      this._mustNotify = false;
    }
  },

  _onMessage: function ({ target }) {
    if (this._knownProcesses.includes(target)) {
      return;
    }
    this._notifyListChanged();
  },
};

exports.ProcessActorList = ProcessActorList;
PK
!<��n�tt:chrome/devtools/modules/devtools/server/actors/promises.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 protocol = require("devtools/shared/protocol");
const { promisesSpec } = require("devtools/shared/specs/promises");
const { expectState, ActorPool } = require("devtools/server/actors/common");
const { ObjectActor, createValueGrip } = require("devtools/server/actors/object");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const EventEmitter = require("devtools/shared/event-emitter");

/**
 * The Promises Actor provides support for getting the list of live promises and
 * observing changes to their settlement state.
 */
var PromisesActor = protocol.ActorClassWithSpec(promisesSpec, {
  /**
   * @param conn DebuggerServerConnection.
   * @param parentActor TabActor|RootActor
   */
  initialize: function (conn, parentActor) {
    protocol.Actor.prototype.initialize.call(this, conn);

    this.conn = conn;
    this.parentActor = parentActor;
    this.state = "detached";
    this._dbg = null;
    this._gripDepth = 0;
    this._navigationLifetimePool = null;
    this._newPromises = null;
    this._promisesSettled = null;

    this.objectGrip = this.objectGrip.bind(this);
    this._makePromiseEventHandler = this._makePromiseEventHandler.bind(this);
    this._onWindowReady = this._onWindowReady.bind(this);
  },

  destroy: function () {
    if (this.state === "attached") {
      this.detach();
    }

    protocol.Actor.prototype.destroy.call(this, this.conn);
  },

  get dbg() {
    if (!this._dbg) {
      this._dbg = this.parentActor.makeDebugger();
    }
    return this._dbg;
  },

  /**
   * Attach to the PromisesActor.
   */
  attach: expectState("detached", function () {
    this.dbg.addDebuggees();

    this._navigationLifetimePool = this._createActorPool();
    this.conn.addActorPool(this._navigationLifetimePool);

    this._newPromises = [];
    this._promisesSettled = [];

    this.dbg.findScripts().forEach(s => {
      this.parentActor.sources.createSourceActors(s.source);
    });

    this.dbg.onNewScript = s => {
      this.parentActor.sources.createSourceActors(s.source);
    };

    EventEmitter.on(this.parentActor, "window-ready", this._onWindowReady);

    this.state = "attached";
  }, "attaching to the PromisesActor"),

  /**
   * Detach from the PromisesActor upon Debugger closing.
   */
  detach: expectState("attached", function () {
    this.dbg.removeAllDebuggees();
    this.dbg.enabled = false;
    this._dbg = null;
    this._newPromises = null;
    this._promisesSettled = null;

    if (this._navigationLifetimePool) {
      this.conn.removeActorPool(this._navigationLifetimePool);
      this._navigationLifetimePool = null;
    }

    EventEmitter.off(this.parentActor, "window-ready", this._onWindowReady);

    this.state = "detached";
  }),

  _createActorPool: function () {
    let pool = new ActorPool(this.conn);
    pool.objectActors = new WeakMap();
    return pool;
  },

  /**
   * Create an ObjectActor for the given Promise object.
   *
   * @param object promise
   *        The promise object
   * @return object
   *        An ObjectActor object that wraps the given Promise object
   */
  _createObjectActorForPromise: function (promise) {
    if (this._navigationLifetimePool.objectActors.has(promise)) {
      return this._navigationLifetimePool.objectActors.get(promise);
    }

    let actor = new ObjectActor(promise, {
      getGripDepth: () => this._gripDepth,
      incrementGripDepth: () => this._gripDepth++,
      decrementGripDepth: () => this._gripDepth--,
      createValueGrip: v =>
        createValueGrip(v, this._navigationLifetimePool, this.objectGrip),
      sources: () => this.parentActor.sources,
      createEnvironmentActor: () => DevToolsUtils.reportException(
        "PromisesActor", Error("createEnvironmentActor not yet implemented")),
      getGlobalDebugObject: () => DevToolsUtils.reportException(
        "PromisesActor", Error("getGlobalDebugObject not yet implemented")),
    });

    this._navigationLifetimePool.addActor(actor);
    this._navigationLifetimePool.objectActors.set(promise, actor);

    return actor;
  },

  /**
   * Get a grip for the given Promise object.
   *
   * @param object value
   *        The Promise object
   * @return object
   *        The grip for the given Promise object
   */
  objectGrip: function (value) {
    return this._createObjectActorForPromise(value).grip();
  },

  /**
   * Get a list of ObjectActors for all live Promise Objects.
   */
  listPromises: function () {
    let promises = this.dbg.findObjects({ class: "Promise" });

    this.dbg.onNewPromise = this._makePromiseEventHandler(this._newPromises,
      "new-promises");
    this.dbg.onPromiseSettled = this._makePromiseEventHandler(
      this._promisesSettled, "promises-settled");

    return promises.map(p => this._createObjectActorForPromise(p));
  },

  /**
   * Creates an event handler for onNewPromise that will add the new
   * Promise ObjectActor to the array and schedule it to be emitted as a
   * batch for the provided event.
   *
   * @param array array
   *        The list of Promise ObjectActors to emit
   * @param string eventName
   *        The event name
   */
  _makePromiseEventHandler: function (array, eventName) {
    return promise => {
      let actor = this._createObjectActorForPromise(promise);
      let needsScheduling = array.length == 0;

      array.push(actor);

      if (needsScheduling) {
        DevToolsUtils.executeSoon(() => {
          this.emit(eventName, array.splice(0, array.length));
        });
      }
    };
  },

  _onWindowReady: expectState("attached", function ({ isTopLevel }) {
    if (!isTopLevel) {
      return;
    }

    this._navigationLifetimePool.cleanup();
    this.dbg.removeAllDebuggees();
    this.dbg.addDebuggees();
  })
});

exports.PromisesActor = PromisesActor;
PK
!<�J��7�78chrome/devtools/modules/devtools/server/actors/reflow.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

/**
 * About the types of objects in this file:
 *
 * - ReflowActor: the actor class used for protocol purposes.
 *   Mostly empty, just gets an instance of LayoutChangesObserver and forwards
 *   its "reflows" events to clients.
 *
 * - LayoutChangesObserver: extends Observable and uses the ReflowObserver, to
 *   track reflows on the page.
 *   Used by the LayoutActor, but is also exported on the module, so can be used
 *   by any other actor that needs it.
 *
 * - Observable: A utility parent class, meant at being extended by classes that
 *   need a to observe something on the tabActor's windows.
 *
 * - Dedicated observers: There's only one of them for now: ReflowObserver which
 *   listens to reflow events via the docshell,
 *   These dedicated classes are used by the LayoutChangesObserver.
 */

const {Ci} = require("chrome");
const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
const protocol = require("devtools/shared/protocol");
const EventEmitter = require("devtools/shared/event-emitter");
const {reflowSpec} = require("devtools/shared/specs/reflow");

/**
 * The reflow actor tracks reflows and emits events about them.
 */
exports.ReflowActor = protocol.ActorClassWithSpec(reflowSpec, {
  initialize: function (conn, tabActor) {
    protocol.Actor.prototype.initialize.call(this, conn);

    this.tabActor = tabActor;
    this._onReflow = this._onReflow.bind(this);
    this.observer = getLayoutChangesObserver(tabActor);
    this._isStarted = false;
  },

  destroy: function () {
    this.stop();
    releaseLayoutChangesObserver(this.tabActor);
    this.observer = null;
    this.tabActor = null;

    protocol.Actor.prototype.destroy.call(this);
  },

  /**
   * Start tracking reflows and sending events to clients about them.
   * This is a oneway method, do not expect a response and it won't return a
   * promise.
   */
  start: function () {
    if (!this._isStarted) {
      this.observer.on("reflows", this._onReflow);
      this._isStarted = true;
    }
  },

  /**
   * Stop tracking reflows and sending events to clients about them.
   * This is a oneway method, do not expect a response and it won't return a
   * promise.
   */
  stop: function () {
    if (this._isStarted) {
      this.observer.off("reflows", this._onReflow);
      this._isStarted = false;
    }
  },

  _onReflow: function (reflows) {
    if (this._isStarted) {
      this.emit("reflows", reflows);
    }
  }
});

/**
 * Base class for all sorts of observers that need to listen to events on the
 * tabActor's windows.
 * @param {TabActor} tabActor
 * @param {Function} callback Executed everytime the observer observes something
 */
function Observable(tabActor, callback) {
  this.tabActor = tabActor;
  this.callback = callback;

  this._onWindowReady = this._onWindowReady.bind(this);
  this._onWindowDestroyed = this._onWindowDestroyed.bind(this);

  this.tabActor.on("window-ready", this._onWindowReady);
  this.tabActor.on("window-destroyed", this._onWindowDestroyed);
}

Observable.prototype = {
  /**
   * Is the observer currently observing
   */
  isObserving: false,

  /**
   * Stop observing and detroy this observer instance
   */
  destroy: function () {
    if (this.isDestroyed) {
      return;
    }
    this.isDestroyed = true;

    this.stop();

    this.tabActor.off("window-ready", this._onWindowReady);
    this.tabActor.off("window-destroyed", this._onWindowDestroyed);

    this.callback = null;
    this.tabActor = null;
  },

  /**
   * Start observing whatever it is this observer is supposed to observe
   */
  start: function () {
    if (this.isObserving) {
      return;
    }
    this.isObserving = true;

    this._startListeners(this.tabActor.windows);
  },

  /**
   * Stop observing
   */
  stop: function () {
    if (!this.isObserving) {
      return;
    }
    this.isObserving = false;

    if (this.tabActor.attached && this.tabActor.docShell) {
      // It's only worth stopping if the tabActor is still attached
      this._stopListeners(this.tabActor.windows);
    }
  },

  _onWindowReady: function ({window}) {
    if (this.isObserving) {
      this._startListeners([window]);
    }
  },

  _onWindowDestroyed: function ({window}) {
    if (this.isObserving) {
      this._stopListeners([window]);
    }
  },

  _startListeners: function (windows) {
    // To be implemented by sub-classes.
  },

  _stopListeners: function (windows) {
    // To be implemented by sub-classes.
  },

  /**
   * To be called by sub-classes when something has been observed
   */
  notifyCallback: function (...args) {
    this.isObserving && this.callback && this.callback.apply(null, args);
  }
};

/**
 * The LayouChangesObserver will observe reflows as soon as it is started.
 * Some devtools actors may cause reflows and it may be wanted to "hide" these
 * reflows from the LayouChangesObserver consumers.
 * If this is the case, such actors should require this module and use this
 * global function to turn the ignore mode on and off temporarily.
 *
 * Note that if a node is provided, it will be used to force a sync reflow to
 * make sure all reflows which occurred before switching the mode on or off are
 * either observed or ignored depending on the current mode.
 *
 * @param {Boolean} ignore
 * @param {DOMNode} syncReflowNode The node to use to force a sync reflow
 */
var gIgnoreLayoutChanges = false;
exports.setIgnoreLayoutChanges = function (ignore, syncReflowNode) {
  if (syncReflowNode) {
    let forceSyncReflow = syncReflowNode.offsetWidth; // eslint-disable-line
  }
  gIgnoreLayoutChanges = ignore;
};

/**
 * The LayoutChangesObserver class is instantiated only once per given tab
 * and is used to track reflows and dom and style changes in that tab.
 * The LayoutActor uses this class to send reflow events to its clients.
 *
 * This class isn't exported on the module because it shouldn't be instantiated
 * to avoid creating several instances per tabs.
 * Use `getLayoutChangesObserver(tabActor)`
 * and `releaseLayoutChangesObserver(tabActor)`
 * which are exported to get and release instances.
 *
 * The observer loops every EVENT_BATCHING_DELAY ms and checks if layout changes
 * have happened since the last loop iteration. If there are, it sends the
 * corresponding events:
 *
 * - "reflows", with an array of all the reflows that occured,
 * - "resizes", with an array of all the resizes that occured,
 *
 * @param {TabActor} tabActor
 */
function LayoutChangesObserver(tabActor) {
  this.tabActor = tabActor;

  this._startEventLoop = this._startEventLoop.bind(this);
  this._onReflow = this._onReflow.bind(this);
  this._onResize = this._onResize.bind(this);

  // Creating the various observers we're going to need
  // For now, just the reflow observer, but later we can add markupMutation,
  // styleSheetChanges and styleRuleChanges
  this.reflowObserver = new ReflowObserver(this.tabActor, this._onReflow);
  this.resizeObserver = new WindowResizeObserver(this.tabActor, this._onResize);

  EventEmitter.decorate(this);
}

exports.LayoutChangesObserver = LayoutChangesObserver;

LayoutChangesObserver.prototype = {
  /**
   * How long does this observer waits before emitting batched events.
   * The lower the value, the more event packets will be sent to clients,
   * potentially impacting performance.
   * The higher the value, the more time we'll wait, this is better for
   * performance but has an effect on how soon changes are shown in the toolbox.
   */
  EVENT_BATCHING_DELAY: 300,

  /**
   * Destroying this instance of LayoutChangesObserver will stop the batched
   * events from being sent.
   */
  destroy: function () {
    this.isObserving = false;

    this.reflowObserver.destroy();
    this.reflows = null;

    this.resizeObserver.destroy();
    this.hasResized = false;

    this.tabActor = null;
  },

  start: function () {
    if (this.isObserving) {
      return;
    }
    this.isObserving = true;

    this.reflows = [];
    this.hasResized = false;

    this._startEventLoop();

    this.reflowObserver.start();
    this.resizeObserver.start();
  },

  stop: function () {
    if (!this.isObserving) {
      return;
    }
    this.isObserving = false;

    this._stopEventLoop();

    this.reflows = [];
    this.hasResized = false;

    this.reflowObserver.stop();
    this.resizeObserver.stop();
  },

  /**
   * Start the event loop, which regularly checks if there are any observer
   * events to be sent as batched events
   * Calls itself in a loop.
   */
  _startEventLoop: function () {
    // Avoid emitting events if the tabActor has been detached (may happen
    // during shutdown)
    if (!this.tabActor || !this.tabActor.attached) {
      return;
    }

    // Send any reflows we have
    if (this.reflows && this.reflows.length) {
      this.emit("reflows", this.reflows);
      this.reflows = [];
    }

    // Send any resizes we have
    if (this.hasResized) {
      this.emit("resize");
      this.hasResized = false;
    }

    this.eventLoopTimer = this._setTimeout(this._startEventLoop,
      this.EVENT_BATCHING_DELAY);
  },

  _stopEventLoop: function () {
    this._clearTimeout(this.eventLoopTimer);
  },

  // Exposing set/clearTimeout here to let tests override them if needed
  _setTimeout: function (cb, ms) {
    return setTimeout(cb, ms);
  },
  _clearTimeout: function (t) {
    return clearTimeout(t);
  },

  /**
   * Executed whenever a reflow is observed. Only stacks the reflow in the
   * reflows array.
   * The EVENT_BATCHING_DELAY loop will take care of it later.
   * @param {Number} start When the reflow started
   * @param {Number} end When the reflow ended
   * @param {Boolean} isInterruptible
   */
  _onReflow: function (start, end, isInterruptible) {
    if (gIgnoreLayoutChanges) {
      return;
    }

    // XXX: when/if bug 997092 gets fixed, we will be able to know which
    // elements have been reflowed, which would be a nice thing to add here.
    this.reflows.push({
      start: start,
      end: end,
      isInterruptible: isInterruptible
    });
  },

  /**
   * Executed whenever a resize is observed. Only store a flag saying that a
   * resize occured.
   * The EVENT_BATCHING_DELAY loop will take care of it later.
   */
  _onResize: function () {
    if (gIgnoreLayoutChanges) {
      return;
    }

    this.hasResized = true;
  }
};

/**
 * Get a LayoutChangesObserver instance for a given window. This function makes
 * sure there is only one instance per window.
 * @param {TabActor} tabActor
 * @return {LayoutChangesObserver}
 */
var observedWindows = new Map();
function getLayoutChangesObserver(tabActor) {
  let observerData = observedWindows.get(tabActor);
  if (observerData) {
    observerData.refCounting++;
    return observerData.observer;
  }

  let obs = new LayoutChangesObserver(tabActor);
  observedWindows.set(tabActor, {
    observer: obs,
    // counting references allows to stop the observer when no tabActor owns an
    // instance.
    refCounting: 1
  });
  obs.start();
  return obs;
}
exports.getLayoutChangesObserver = getLayoutChangesObserver;

/**
 * Release a LayoutChangesObserver instance that was retrieved by
 * getLayoutChangesObserver. This is required to ensure the tabActor reference
 * is removed and the observer is eventually stopped and destroyed.
 * @param {TabActor} tabActor
 */
function releaseLayoutChangesObserver(tabActor) {
  let observerData = observedWindows.get(tabActor);
  if (!observerData) {
    return;
  }

  observerData.refCounting--;
  if (!observerData.refCounting) {
    observerData.observer.destroy();
    observedWindows.delete(tabActor);
  }
}
exports.releaseLayoutChangesObserver = releaseLayoutChangesObserver;

/**
 * Reports any reflow that occurs in the tabActor's docshells.
 * @extends Observable
 * @param {TabActor} tabActor
 * @param {Function} callback Executed everytime a reflow occurs
 */
class ReflowObserver extends Observable {
  constructor(tabActor, callback) {
    super(tabActor, callback);
  }

  _startListeners(windows) {
    for (let window of windows) {
      let docshell = window.QueryInterface(Ci.nsIInterfaceRequestor)
                     .getInterface(Ci.nsIWebNavigation)
                     .QueryInterface(Ci.nsIDocShell);
      docshell.addWeakReflowObserver(this);
    }
  }

  _stopListeners(windows) {
    for (let window of windows) {
      try {
        let docshell = window.QueryInterface(Ci.nsIInterfaceRequestor)
                       .getInterface(Ci.nsIWebNavigation)
                       .QueryInterface(Ci.nsIDocShell);
        docshell.removeWeakReflowObserver(this);
      } catch (e) {
        // Corner cases where a global has already been freed may happen, in
        // which case, no need to remove the observer.
      }
    }
  }

  reflow(start, end) {
    this.notifyCallback(start, end, false);
  }

  reflowInterruptible(start, end) {
    this.notifyCallback(start, end, true);
  }
}

ReflowObserver.prototype.QueryInterface = XPCOMUtils
  .generateQI([Ci.nsIReflowObserver, Ci.nsISupportsWeakReference]);

/**
 * Reports window resize events on the tabActor's windows.
 * @extends Observable
 * @param {TabActor} tabActor
 * @param {Function} callback Executed everytime a resize occurs
 */
class WindowResizeObserver extends Observable {
  constructor(tabActor, callback) {
    super(tabActor, callback);
    this.onResize = this.onResize.bind(this);
  }

  _startListeners() {
    this.listenerTarget.addEventListener("resize", this.onResize);
  }

  _stopListeners() {
    this.listenerTarget.removeEventListener("resize", this.onResize);
  }

  onResize() {
    this.notifyCallback();
  }

  get listenerTarget() {
    // For the rootActor, return its window.
    if (this.tabActor.isRootActor) {
      return this.tabActor.window;
    }

    // Otherwise, get the tabActor's chromeEventHandler.
    return this.tabActor.window.QueryInterface(Ci.nsIInterfaceRequestor)
                               .getInterface(Ci.nsIWebNavigation)
                               .QueryInterface(Ci.nsIDocShell)
                               .chromeEventHandler;
  }
}
PK
!<�łc�X�X6chrome/devtools/modules/devtools/server/actors/root.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 { Cc, Ci, Cu } = require("chrome");
const Services = require("Services");
const { ActorPool, appendExtraActors, createExtraActors } = require("devtools/server/actors/common");
const { DebuggerServer } = require("devtools/server/main");

loader.lazyGetter(this, "ppmm", () => {
  return Cc["@mozilla.org/parentprocessmessagemanager;1"].getService(
    Ci.nsIMessageBroadcaster);
});
loader.lazyRequireGetter(this, "WindowActor",
  "devtools/server/actors/window", true);

/* Root actor for the remote debugging protocol. */

/**
 * Create a remote debugging protocol root actor.
 *
 * @param connection
 *     The DebuggerServerConnection whose root actor we are constructing.
 *
 * @param parameters
 *     The properties of |parameters| provide backing objects for the root
 *     actor's requests; if a given property is omitted from |parameters|, the
 *     root actor won't implement the corresponding requests or notifications.
 *     Supported properties:
 *
 *     - tabList: a live list (see below) of tab actors. If present, the
 *       new root actor supports the 'listTabs' request, providing the live
 *       list's elements as its tab actors, and sending 'tabListChanged'
 *       notifications when the live list's contents change. One actor in
 *       this list must have a true '.selected' property.
 *
 *     - addonList: a live list (see below) of addon actors. If present, the
 *       new root actor supports the 'listAddons' request, providing the live
 *       list's elements as its addon actors, and sending 'addonListchanged'
 *       notifications when the live list's contents change.
 *
 *     - globalActorFactories: an object |A| describing further actors to
 *       attach to the 'listTabs' reply. This is the type accumulated by
 *       DebuggerServer.addGlobalActor. For each own property |P| of |A|,
 *       the root actor adds a property named |P| to the 'listTabs'
 *       reply whose value is the name of an actor constructed by
 *       |A[P]|.
 *
 *     - onShutdown: a function to call when the root actor is destroyed.
 *
 * Instance properties:
 *
 * - applicationType: the string the root actor will include as the
 *      "applicationType" property in the greeting packet. By default, this
 *      is "browser".
 *
 * Live lists:
 *
 * A "live list", as used for the |tabList|, is an object that presents a
 * list of actors, and also notifies its clients of changes to the list. A
 * live list's interface is two properties:
 *
 * - getList: a method that returns a promise to the contents of the list.
 *
 * - onListChanged: a handler called, with no arguments, when the set of
 *             values the iterator would produce has changed since the last
 *             time 'iterator' was called. This may only be set to null or a
 *             callable value (one for which the typeof operator returns
 *             'function'). (Note that the live list will not call the
 *             onListChanged handler until the list has been iterated over
 *             once; if nobody's seen the list in the first place, nobody
 *             should care if its contents have changed!)
 *
 * When the list changes, the list implementation should ensure that any
 * actors yielded in previous iterations whose referents (tabs) still exist
 * get yielded again in subsequent iterations. If the underlying referent
 * is the same, the same actor should be presented for it.
 *
 * The root actor registers an 'onListChanged' handler on the appropriate
 * list when it may need to send the client 'tabListChanged' notifications,
 * and is careful to remove the handler whenever it does not need to send
 * such notifications (including when it is destroyed). This means that
 * live list implementations can use the state of the handler property (set
 * or null) to install and remove observers and event listeners.
 *
 * Note that, as the only way for the root actor to see the members of the
 * live list is to begin an iteration over the list, the live list need not
 * actually produce any actors until they are reached in the course of
 * iteration: alliterative lazy live lists.
 */
function RootActor(connection, parameters) {
  this.conn = connection;
  this._parameters = parameters;
  this._onTabListChanged = this.onTabListChanged.bind(this);
  this._onAddonListChanged = this.onAddonListChanged.bind(this);
  this._onWorkerListChanged = this.onWorkerListChanged.bind(this);
  this._onServiceWorkerRegistrationListChanged =
    this.onServiceWorkerRegistrationListChanged.bind(this);
  this._onProcessListChanged = this.onProcessListChanged.bind(this);
  this._extraActors = {};

  this._globalActorPool = new ActorPool(this.conn);
  this.conn.addActorPool(this._globalActorPool);

  this._chromeActor = null;
  this._processActors = new Map();
}

RootActor.prototype = {
  constructor: RootActor,
  applicationType: "browser",

  traits: {
    sources: true,
    // Whether the inspector actor allows modifying outer HTML.
    editOuterHTML: true,
    // Whether the inspector actor allows modifying innerHTML and inserting
    // adjacent HTML.
    pasteHTML: true,
    // Whether the server-side highlighter actor exists and can be used to
    // remotely highlight nodes (see server/actors/highlighters.js)
    highlightable: true,
    // Which custom highlighter does the server-side highlighter actor supports?
    // (see server/actors/highlighters.js)
    customHighlighters: true,
    // Whether the inspector actor implements the getImageDataFromURL
    // method that returns data-uris for image URLs. This is used for image
    // tooltips for instance
    urlToImageDataResolver: true,
    networkMonitor: true,
    // Whether the storage inspector actor to inspect cookies, etc.
    storageInspector: true,
    // Whether storage inspector is read only
    storageInspectorReadOnly: true,
    // Whether conditional breakpoints are supported
    conditionalBreakpoints: true,
    // Whether the server supports full source actors (breakpoints on
    // eval scripts, etc)
    debuggerSourceActors: true,
    // Whether the server can return wasm binary source
    wasmBinarySource: true,
    bulk: true,
    // Whether the style rule actor implements the modifySelector method
    // that modifies the rule's selector
    selectorEditable: true,
    // Whether the page style actor implements the addNewRule method that
    // adds new rules to the page
    addNewRule: true,
    // Whether the dom node actor implements the getUniqueSelector method
    getUniqueSelector: true,
    // Whether the dom node actor implements the getCssPath method
    getCssPath: true,
    // Whether the dom node actor implements the getXPath method
    getXPath: true,
    // Whether the director scripts are supported
    directorScripts: true,
    // Whether the debugger server supports
    // blackboxing/pretty-printing (not supported in Fever Dream yet)
    noBlackBoxing: false,
    noPrettyPrinting: false,
    // Whether the page style actor implements the getUsedFontFaces method
    // that returns the font faces used on a node
    getUsedFontFaces: true,
    // Trait added in Gecko 38, indicating that all features necessary for
    // grabbing allocations from the MemoryActor are available for the performance tool
    memoryActorAllocations: true,
    // Added in Firefox 40. Indicates that the backend supports registering custom
    // commands through the WebConsoleCommands API.
    webConsoleCommands: true,
    // Whether root actor exposes tab actors and access to any window.
    // If allowChromeProcess is true, you can:
    // * get a ChromeActor instance to debug chrome and any non-content
    //   resource via getProcess requests
    // * get a WindowActor instance to debug windows which could be chrome,
    //   like browser windows via getWindow requests
    // If allowChromeProcess is defined, but not true, it means that root actor
    // no longer expose tab actors, but also that the above requests are
    // forbidden for security reasons.
    get allowChromeProcess() {
      return DebuggerServer.allowChromeProcess;
    },
    // Whether or not `getProfile()` supports specifying a `startTime`
    // and `endTime` to filter out samples. Fx40+
    profilerDataFilterable: true,
    // Whether or not the MemoryActor's heap snapshot abilities are
    // fully equipped to handle heap snapshots for the memory tool. Fx44+
    heapSnapshots: true,
    // Whether or not the timeline actor can emit DOMContentLoaded and Load
    // markers, currently in use by the network monitor. Fx45+
    documentLoadingMarkers: true,
    // Whether or not the webextension addon actor have to be connected
    // to retrieve the extension child process tab actors.
    webExtensionAddonConnect: true,
  },

  /**
   * Return a 'hello' packet as specified by the Remote Debugging Protocol.
   */
  sayHello: function () {
    return {
      from: this.actorID,
      applicationType: this.applicationType,
      /* This is not in the spec, but it's used by tests. */
      testConnectionPrefix: this.conn.prefix,
      traits: this.traits
    };
  },

  forwardingCancelled: function (prefix) {
    return {
      from: this.actorID,
      type: "forwardingCancelled",
      prefix,
    };
  },

  /**
   * Destroys the actor from the browser window.
   */
  destroy: function () {
    /* Tell the live lists we aren't watching any more. */
    if (this._parameters.tabList) {
      this._parameters.tabList.onListChanged = null;
    }
    if (this._parameters.addonList) {
      this._parameters.addonList.onListChanged = null;
    }
    if (this._parameters.workerList) {
      this._parameters.workerList.onListChanged = null;
    }
    if (this._parameters.serviceWorkerRegistrationList) {
      this._parameters.serviceWorkerRegistrationList.onListChanged = null;
    }
    if (this._parameters.processList) {
      this._parameters.processList.onListChanged = null;
    }
    if (typeof this._parameters.onShutdown === "function") {
      this._parameters.onShutdown();
    }
    this._extraActors = null;
    this.conn = null;
    this._tabActorPool = null;
    this._globalActorPool = null;
    this._windowActorPool = null;
    this._parameters = null;
    this._chromeActor = null;
    this._processActors.clear();
  },

  /**
   * Gets the "root" form, which lists all the global actors that affect the entire
   * browser.  This can replace usages of `listTabs` that only wanted the global actors
   * and didn't actually care about tabs.
   */
  onGetRoot: function () {
    let reply = {
      from: this.actorID,
    };

    // Create global actors
    if (!this._globalActorPool) {
      this._globalActorPool = new ActorPool(this.conn);
      this.conn.addActorPool(this._globalActorPool);
    }
    this._createExtraActors(this._parameters.globalActorFactories, this._globalActorPool);

    // List the global actors
    this._appendExtraActors(reply);

    return reply;
  },

  /* The 'listTabs' request and the 'tabListChanged' notification. */

  /**
   * Handles the listTabs request. The actors will survive until at least
   * the next listTabs request.
   *
   * ⚠ WARNING ⚠ This can be a very expensive operation, especially if there are many
   * open tabs.  It will cause us to visit every tab, load a frame script, start a
   * debugger server, and read some data.  With lazy tab support (bug 906076), this
   * would trigger any lazy tabs to be loaded, greatly increasing resource usage.  Avoid
   * this method whenever possible.
   */
  onListTabs: async function (request) {
    let tabList = this._parameters.tabList;
    if (!tabList) {
      return { from: this.actorID, error: "noTabs",
               message: "This root actor has no browser tabs." };
    }

    // Now that a client has requested the list of tabs, we reattach the onListChanged
    // listener in order to be notified if the list of tabs changes again in the future.
    tabList.onListChanged = this._onTabListChanged;

    // Walk the tab list, accumulating the array of tab actors for the reply, and moving
    // all the actors to a new ActorPool. We'll replace the old tab actor pool with the
    // one we build here, thus retiring any actors that didn't get listed again, and
    // preparing any new actors to receive packets.
    let newActorPool = new ActorPool(this.conn);
    let tabActorList = [];
    let selected;

    let options = request.options || {};
    let tabActors = await tabList.getList(options);
    for (let tabActor of tabActors) {
      if (tabActor.exited) {
        // Tab actor may have exited while we were gathering the list.
        continue;
      }
      if (tabActor.selected) {
        selected = tabActorList.length;
      }
      tabActor.parentID = this.actorID;
      newActorPool.addActor(tabActor);
      tabActorList.push(tabActor);
    }

    // Start with the root reply, which includes the global actors for the whole browser.
    let reply = this.onGetRoot();

    // Drop the old actorID -> actor map. Actors that still mattered were added to the
    // new map; others will go away.
    if (this._tabActorPool) {
      this.conn.removeActorPool(this._tabActorPool);
    }
    this._tabActorPool = newActorPool;
    this.conn.addActorPool(this._tabActorPool);

    // We'll extend the reply here to also mention all the tabs.
    Object.assign(reply, {
      selected: selected || 0,
      tabs: tabActorList.map(actor => actor.form()),
    });

    return reply;
  },

  onGetTab: async function (options) {
    let tabList = this._parameters.tabList;
    if (!tabList) {
      return { error: "noTabs",
               message: "This root actor has no browser tabs." };
    }
    if (!this._tabActorPool) {
      this._tabActorPool = new ActorPool(this.conn);
      this.conn.addActorPool(this._tabActorPool);
    }

    let tabActor;
    try {
      tabActor = await tabList.getTab(options);
    } catch (error) {
      if (error.error) {
        // Pipe expected errors as-is to the client
        return error;
      }
      return {
        error: "noTab",
        message: "Unexpected error while calling getTab(): " + error
      };
    }

    tabActor.parentID = this.actorID;
    this._tabActorPool.addActor(tabActor);

    return { tab: tabActor.form() };
  },

  onGetWindow: function ({ outerWindowID }) {
    if (!DebuggerServer.allowChromeProcess) {
      return {
        from: this.actorID,
        error: "forbidden",
        message: "You are not allowed to debug windows."
      };
    }
    let window = Services.wm.getOuterWindowWithId(outerWindowID);
    if (!window) {
      return {
        from: this.actorID,
        error: "notFound",
        message: `No window found with outerWindowID ${outerWindowID}`,
      };
    }

    if (!this._windowActorPool) {
      this._windowActorPool = new ActorPool(this.conn);
      this.conn.addActorPool(this._windowActorPool);
    }

    let actor = new WindowActor(this.conn, window);
    actor.parentID = this.actorID;
    this._windowActorPool.addActor(actor);

    return {
      from: this.actorID,
      window: actor.form(),
    };
  },

  onTabListChanged: function () {
    this.conn.send({ from: this.actorID, type: "tabListChanged" });
    /* It's a one-shot notification; no need to watch any more. */
    this._parameters.tabList.onListChanged = null;
  },

  onListAddons: function () {
    let addonList = this._parameters.addonList;
    if (!addonList) {
      return { from: this.actorID, error: "noAddons",
               message: "This root actor has no browser addons." };
    }

    // Reattach the onListChanged listener now that a client requested the list.
    addonList.onListChanged = this._onAddonListChanged;

    return addonList.getList().then((addonActors) => {
      let addonActorPool = new ActorPool(this.conn);
      for (let addonActor of addonActors) {
        addonActorPool.addActor(addonActor);
      }

      if (this._addonActorPool) {
        this.conn.removeActorPool(this._addonActorPool);
      }
      this._addonActorPool = addonActorPool;
      this.conn.addActorPool(this._addonActorPool);

      return {
        "from": this.actorID,
        "addons": addonActors.map(addonActor => addonActor.form())
      };
    });
  },

  onAddonListChanged: function () {
    this.conn.send({ from: this.actorID, type: "addonListChanged" });
    this._parameters.addonList.onListChanged = null;
  },

  onListWorkers: function () {
    let workerList = this._parameters.workerList;
    if (!workerList) {
      return { from: this.actorID, error: "noWorkers",
               message: "This root actor has no workers." };
    }

    // Reattach the onListChanged listener now that a client requested the list.
    workerList.onListChanged = this._onWorkerListChanged;

    return workerList.getList().then(actors => {
      let pool = new ActorPool(this.conn);
      for (let actor of actors) {
        pool.addActor(actor);
      }

      this.conn.removeActorPool(this._workerActorPool);
      this._workerActorPool = pool;
      this.conn.addActorPool(this._workerActorPool);

      return {
        "from": this.actorID,
        "workers": actors.map(actor => actor.form())
      };
    });
  },

  onWorkerListChanged: function () {
    this.conn.send({ from: this.actorID, type: "workerListChanged" });
    this._parameters.workerList.onListChanged = null;
  },

  onListServiceWorkerRegistrations: function () {
    let registrationList = this._parameters.serviceWorkerRegistrationList;
    if (!registrationList) {
      return { from: this.actorID, error: "noServiceWorkerRegistrations",
               message: "This root actor has no service worker registrations." };
    }

    // Reattach the onListChanged listener now that a client requested the list.
    registrationList.onListChanged = this._onServiceWorkerRegistrationListChanged;

    return registrationList.getList().then(actors => {
      let pool = new ActorPool(this.conn);
      for (let actor of actors) {
        pool.addActor(actor);
      }

      this.conn.removeActorPool(this._serviceWorkerRegistrationActorPool);
      this._serviceWorkerRegistrationActorPool = pool;
      this.conn.addActorPool(this._serviceWorkerRegistrationActorPool);

      return {
        "from": this.actorID,
        "registrations": actors.map(actor => actor.form())
      };
    });
  },

  onServiceWorkerRegistrationListChanged: function () {
    this.conn.send({ from: this.actorID, type: "serviceWorkerRegistrationListChanged" });
    this._parameters.serviceWorkerRegistrationList.onListChanged = null;
  },

  onListProcesses: function () {
    let { processList } = this._parameters;
    if (!processList) {
      return { from: this.actorID, error: "noProcesses",
               message: "This root actor has no processes." };
    }
    processList.onListChanged = this._onProcessListChanged;
    return {
      processes: processList.getList()
    };
  },

  onProcessListChanged: function () {
    this.conn.send({ from: this.actorID, type: "processListChanged" });
    this._parameters.processList.onListChanged = null;
  },

  onGetProcess: function (request) {
    if (!DebuggerServer.allowChromeProcess) {
      return { error: "forbidden",
               message: "You are not allowed to debug chrome." };
    }
    if (("id" in request) && typeof (request.id) != "number") {
      return { error: "wrongParameter",
               message: "getProcess requires a valid `id` attribute." };
    }
    // If the request doesn't contains id parameter or id is 0
    // (id == 0, based on onListProcesses implementation)
    if ((!("id" in request)) || request.id === 0) {
      if (!this._chromeActor) {
        // Create a ChromeActor for the parent process
        let { ChromeActor } = require("devtools/server/actors/chrome");
        this._chromeActor = new ChromeActor(this.conn);
        this._globalActorPool.addActor(this._chromeActor);
      }

      return { form: this._chromeActor.form() };
    }

    let { id } = request;
    let mm = ppmm.getChildAt(id);
    if (!mm) {
      return { error: "noProcess",
               message: "There is no process with id '" + id + "'." };
    }
    let form = this._processActors.get(id);
    if (form) {
      return { form };
    }
    let onDestroy = () => {
      this._processActors.delete(id);
    };
    return DebuggerServer.connectToContent(this.conn, mm, onDestroy).then(formResult => {
      this._processActors.set(id, formResult);
      return { form: formResult };
    });
  },

  /* This is not in the spec, but it's used by tests. */
  onEcho: function (request) {
    /*
     * Request packets are frozen. Copy request, so that
     * DebuggerServerConnection.onPacket can attach a 'from' property.
     */
    return Cu.cloneInto(request, {});
  },

  onProtocolDescription: function () {
    return require("devtools/shared/protocol").dumpProtocolSpec();
  },

  /* Support for DebuggerServer.addGlobalActor. */
  _createExtraActors: createExtraActors,
  _appendExtraActors: appendExtraActors,

  /**
   * Remove the extra actor (added by DebuggerServer.addGlobalActor or
   * DebuggerServer.addTabActor) name |name|.
   */
  removeActorByName: function (name) {
    if (name in this._extraActors) {
      const actor = this._extraActors[name];
      if (this._globalActorPool.has(actor)) {
        this._globalActorPool.removeActor(actor);
      }
      if (this._tabActorPool) {
        // Iterate over TabActor instances to also remove tab actors
        // created during listTabs for each document.
        this._tabActorPool.forEach(tab => {
          tab.removeActorByName(name);
        });
      }
      delete this._extraActors[name];
    }
  }
};

RootActor.prototype.requestTypes = {
  getRoot: RootActor.prototype.onGetRoot,
  listTabs: RootActor.prototype.onListTabs,
  getTab: RootActor.prototype.onGetTab,
  getWindow: RootActor.prototype.onGetWindow,
  listAddons: RootActor.prototype.onListAddons,
  listWorkers: RootActor.prototype.onListWorkers,
  listServiceWorkerRegistrations: RootActor.prototype.onListServiceWorkerRegistrations,
  listProcesses: RootActor.prototype.onListProcesses,
  getProcess: RootActor.prototype.onGetProcess,
  echo: RootActor.prototype.onEcho,
  protocolDescription: RootActor.prototype.onProtocolDescription
};

exports.RootActor = RootActor;
PK
!<BA7xA�A�8chrome/devtools/modules/devtools/server/actors/source.js/* -*- indent-tabs-mode: nil; js-indent-level: 2; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 { Cc, Ci } = require("chrome");
const Services = require("Services");
const { BreakpointActor, setBreakpointAtEntryPoints } = require("devtools/server/actors/breakpoint");
const { OriginalLocation, GeneratedLocation } = require("devtools/server/actors/common");
const { createValueGrip, arrayBufferGrip } = require("devtools/server/actors/object");
const { ActorClassWithSpec } = require("devtools/shared/protocol");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const { assert, fetch } = DevToolsUtils;
const { joinURI } = require("devtools/shared/path");
const { sourceSpec } = require("devtools/shared/specs/source");

loader.lazyRequireGetter(this, "SourceMapConsumer", "source-map", true);
loader.lazyRequireGetter(this, "SourceMapGenerator", "source-map", true);
loader.lazyRequireGetter(this, "mapURIToAddonID", "devtools/server/actors/utils/map-uri-to-addon-id");

function isEvalSource(source) {
  let introType = source.introductionType;
  // These are all the sources that are essentially eval-ed (either
  // by calling eval or passing a string to one of these functions).
  return (introType === "eval" ||
          introType === "debugger eval" ||
          introType === "Function" ||
          introType === "eventHandler" ||
          introType === "setTimeout" ||
          introType === "setInterval");
}

exports.isEvalSource = isEvalSource;

function getSourceURL(source, window) {
  if (isEvalSource(source)) {
    // Eval sources have no urls, but they might have a `displayURL`
    // created with the sourceURL pragma. If the introduction script
    // is a non-eval script, generate an full absolute URL relative to it.

    if (source.displayURL && source.introductionScript) {
      if (source.introductionScript.source.url === "debugger eval code") {
        if (window) {
          // If this is a named eval script created from the console, make it
          // relative to the current page. window is only available
          // when we care about this.
          return joinURI(window.location.href, source.displayURL);
        }
      } else if (!isEvalSource(source.introductionScript.source)) {
        return joinURI(source.introductionScript.source.url, source.displayURL);
      }
    }

    return source.displayURL;
  } else if (source.url === "debugger eval code") {
    // Treat code evaluated by the console as unnamed eval scripts
    return null;
  }
  return source.url;
}

exports.getSourceURL = getSourceURL;

/**
 * Resolve a URI back to physical file.
 *
 * Of course, this works only for URIs pointing to local resources.
 *
 * @param  uri
 *         URI to resolve
 * @return
 *         resolved nsIURI
 */
function resolveURIToLocalPath(uri) {
  let resolved;
  switch (uri.scheme) {
    case "jar":
    case "file":
      return uri;

    case "chrome":
      resolved = Cc["@mozilla.org/chrome/chrome-registry;1"]
                  .getService(Ci.nsIChromeRegistry).convertChromeURL(uri);
      return resolveURIToLocalPath(resolved);

    case "resource":
      resolved = Cc["@mozilla.org/network/protocol;1?name=resource"]
                  .getService(Ci.nsIResProtocolHandler).resolveURI(uri);
      uri = Services.io.newURI(resolved);
      return resolveURIToLocalPath(uri);

    default:
      return null;
  }
}

/**
 * A SourceActor provides information about the source of a script. There
 * are two kinds of source actors: ones that represent real source objects,
 * and ones that represent non-existant "original" sources when the real
 * sources are sourcemapped. When a source is sourcemapped, actors are
 * created for both the "generated" and "original" sources, and the client will
 * only see the original sources. We separate these because there isn't
 * a 1:1 mapping of generated to original sources; one generated source
 * may represent N original sources, so we need to create N + 1 separate
 * actors.
 *
 * There are 4 different scenarios for sources that you should
 * understand:
 *
 * - A single non-sourcemapped source that is not inlined in HTML
 *   (separate JS file, eval'ed code, etc)
 * - A single sourcemapped source which creates N original sources
 * - An HTML page with multiple inline scripts, which are distinct
 *   sources, but should be represented as a single source
 * - A pretty-printed source (which may or may not be an original
 *   sourcemapped source), which generates a sourcemap for itself
 *
 * The complexity of `SourceActor` and `ThreadSources` are to handle
 * all of thise cases and hopefully internalize the complexities.
 *
 * @param Debugger.Source source
 *        The source object we are representing.
 * @param ThreadActor thread
 *        The current thread actor.
 * @param String originalUrl
 *        Optional. For sourcemapped urls, the original url this is representing.
 * @param Debugger.Source generatedSource
 *        Optional, passed in when aSourceMap is also passed in. The generated
 *        source object that introduced this source.
 * @param Boolean isInlineSource
 *        Optional. True if this is an inline source from a HTML or XUL page.
 * @param String contentType
 *        Optional. The content type of this source, if immediately available.
 */
let SourceActor = ActorClassWithSpec(sourceSpec, {
  typeName: "source",

  initialize: function ({ source, thread, originalUrl, generatedSource,
                          isInlineSource, contentType }) {
    this._threadActor = thread;
    this._originalUrl = originalUrl;
    this._source = source;
    this._generatedSource = generatedSource;
    this._contentType = contentType;
    this._isInlineSource = isInlineSource;

    this.onSource = this.onSource.bind(this);
    this._invertSourceMap = this._invertSourceMap.bind(this);
    this._encodeAndSetSourceMapURL = this._encodeAndSetSourceMapURL.bind(this);
    this._getSourceText = this._getSourceText.bind(this);

    this._mapSourceToAddon();

    if (this.threadActor.sources.isPrettyPrinted(this.url)) {
      this._init = this.prettyPrint(
        this.threadActor.sources.prettyPrintIndent(this.url)
      ).catch(error => {
        DevToolsUtils.reportException("SourceActor", error);
      });
    } else {
      this._init = null;
    }
  },

  get isSourceMapped() {
    return !!(!this.isInlineSource && (
      this._originalURL || this._generatedSource ||
        this.threadActor.sources.isPrettyPrinted(this.url)
    ));
  },

  get isInlineSource() {
    return this._isInlineSource;
  },

  get threadActor() {
    return this._threadActor;
  },
  get sources() {
    return this._threadActor.sources;
  },
  get dbg() {
    return this.threadActor.dbg;
  },
  get source() {
    return this._source;
  },
  get generatedSource() {
    return this._generatedSource;
  },
  get breakpointActorMap() {
    return this.threadActor.breakpointActorMap;
  },
  get url() {
    if (this.source) {
      return getSourceURL(this.source, this.threadActor._parent.window);
    }
    return this._originalUrl;
  },
  get addonID() {
    return this._addonID;
  },
  get addonPath() {
    return this._addonPath;
  },

  get prettyPrintWorker() {
    return this.threadActor.prettyPrintWorker;
  },

  get isCacheEnabled() {
    if (this.threadActor._parent._getCacheDisabled) {
      return !this.threadActor._parent._getCacheDisabled();
    }
    return true;
  },

  form: function () {
    let source = this.source || this.generatedSource;
    // This might not have a source or a generatedSource because we
    // treat HTML pages with inline scripts as a special SourceActor
    // that doesn't have either
    let introductionUrl = null;
    if (source && source.introductionScript) {
      introductionUrl = source.introductionScript.source.url;
    }

    return {
      actor: this.actorID,
      generatedUrl: this.generatedSource ? this.generatedSource.url : null,
      url: this.url ? this.url.split(" -> ").pop() : null,
      addonID: this._addonID,
      addonPath: this._addonPath,
      isBlackBoxed: this.threadActor.sources.isBlackBoxed(this.url),
      isPrettyPrinted: this.threadActor.sources.isPrettyPrinted(this.url),
      isSourceMapped: this.isSourceMapped,
      sourceMapURL: source ? source.sourceMapURL : null,
      introductionUrl: introductionUrl ? introductionUrl.split(" -> ").pop() : null,
      introductionType: source ? source.introductionType : null
    };
  },

  destroy: function () {
    if (this.registeredPool && this.registeredPool.sourceActors) {
      delete this.registeredPool.sourceActors[this.actorID];
    }
  },

  _mapSourceToAddon: function () {
    let nsuri;
    try {
      nsuri = Services.io.newURI(this.url.split(" -> ").pop());
    } catch (e) {
      // We can't do anything with an invalid URI
      return;
    }

    let localURI = resolveURIToLocalPath(nsuri);
    if (!localURI) {
      return;
    }

    let id = mapURIToAddonID(localURI);
    if (!id) {
      return;
    }
    this._addonID = id;

    if (localURI instanceof Ci.nsIJARURI) {
      // The path in the add-on is easy for jar: uris
      this._addonPath = localURI.JAREntry;
    } else if (localURI instanceof Ci.nsIFileURL) {
      // For file: uris walk up to find the last directory that is part of the
      // add-on
      let target = localURI.file;
      let path = target.leafName;

      // We can assume that the directory containing the source file is part
      // of the add-on
      let root = target.parent;
      let file = root.parent;
      while (file && mapURIToAddonID(Services.io.newFileURI(file))) {
        path = root.leafName + "/" + path;
        root = file;
        file = file.parent;
      }

      if (!file) {
        const error = new Error("Could not find the root of the add-on for " + this.url);
        DevToolsUtils.reportException("SourceActor.prototype._mapSourceToAddon", error);
        return;
      }

      this._addonPath = path;
    }
  },

  _reportLoadSourceError: function (error, map = null) {
    try {
      DevToolsUtils.reportException("SourceActor", error);

      JSON.stringify(this.form(), null, 4).split(/\n/g)
        .forEach(line => console.error("\t", line));

      if (!map) {
        return;
      }

      console.error("\t", "source map's sourceRoot =", map.sourceRoot);

      console.error("\t", "source map's sources =");
      map.sources.forEach(s => {
        let hasSourceContent = map.sourceContentFor(s, true);
        console.error("\t\t", s, "\t",
                      hasSourceContent ? "has source content" : "no source content");
      });

      console.error("\t", "source map's sourcesContent =");
      map.sourcesContent.forEach(c => {
        if (c.length > 80) {
          c = c.slice(0, 77) + "...";
        }
        c = c.replace(/\n/g, "\\n");
        console.error("\t\t", c);
      });
    } catch (e) {
      // ignore
    }
  },

  _getSourceText: function () {
    let toResolvedContent = t => ({
      content: t,
      contentType: this._contentType
    });
    let isWasm = this.source && this.source.introductionType === "wasm";

    let genSource = this.generatedSource || this.source;
    return this.threadActor.sources.fetchSourceMap(genSource).then(map => {
      if (map) {
        try {
          let sourceContent = map.sourceContentFor(this.url);
          if (sourceContent) {
            return toResolvedContent(sourceContent);
          }
        } catch (error) {
          this._reportLoadSourceError(error, map);
          throw error;
        }
      }

      if (isWasm && this.dbg.allowWasmBinarySource) {
        let wasm = this.source.binary;
        let buffer = wasm.buffer;
        assert(
          wasm.byteOffset === 0 && wasm.byteLength === buffer.byteLength,
          "Typed array from wasm source binary must cover entire buffer"
        );
        return toResolvedContent(buffer);
      }

      // Use `source.text` if it exists, is not the "no source" string, and
      // the content type of the source is JavaScript or it is synthesized
      // wasm. It will be "no source" if the Debugger API wasn't able to load
      // the source because sources were discarded
      // (javascript.options.discardSystemSource == true). Re-fetch non-JS
      // sources to get the contentType from the headers.
      if (this.source &&
          this.source.text !== "[no source]" &&
          this._contentType &&
          (this._contentType.includes("javascript") ||
           this._contentType === "text/wasm")) {
        return toResolvedContent(this.source.text);
      }

      // Only load the HTML page source from cache (which exists when
      // there are inline sources). Otherwise, we can't trust the
      // cache because we are most likely here because we are
      // fetching the original text for sourcemapped code, and the
      // page hasn't requested it before (if it has, it was a
      // previous debugging session).
      // Additionally, we should only try the cache if it is currently enabled
      // for the document.  Without this check, the cache may return stale data
      // that doesn't match the document shown in the browser.
      let loadFromCache = this.isInlineSource && this.isCacheEnabled;

      // Fetch the sources with the same principal as the original document
      let win = this.threadActor._parent.window;
      let principal, cacheKey;
      // On xpcshell, we don't have a window but a Sandbox
      if (!isWorker && win instanceof Ci.nsIDOMWindow) {
        let webNav = win.QueryInterface(Ci.nsIInterfaceRequestor)
                        .getInterface(Ci.nsIWebNavigation);
        let channel = webNav.currentDocumentChannel;
        principal = channel.loadInfo.loadingPrincipal;

        // Retrieve the cacheKey in order to load POST requests from cache
        // Note that chrome:// URLs don't support this interface.
        if (loadFromCache &&
          webNav.currentDocumentChannel instanceof Ci.nsICacheInfoChannel) {
          cacheKey = webNav.currentDocumentChannel.cacheKey;
          assert(
            cacheKey,
            "Could not fetch the cacheKey from the related document."
          );
        }
      }

      let sourceFetched = fetch(this.url, {
        principal,
        cacheKey,
        loadFromCache
      });

      // Record the contentType we just learned during fetching
      return sourceFetched
        .then(result => {
          this._contentType = result.contentType;
          return result;
        }, error => {
          this._reportLoadSourceError(error, map);
          throw error;
        });
    });
  },

  /**
   * Get all executable lines from the current source
   * @return Array - Executable lines of the current script
   **/
  getExecutableLines: function () {
    function sortLines(lines) {
      // Converting the Set into an array
      lines = [...lines];
      lines.sort((a, b) => {
        return a - b;
      });
      return lines;
    }

    if (this.generatedSource) {
      return this.threadActor.sources.getSourceMap(this.generatedSource).then(sm => {
        let lines = new Set();

        // Position of executable lines in the generated source
        let offsets = this.getExecutableOffsets(this.generatedSource, false);
        for (let offset of offsets) {
          let {line, source: sourceUrl} = sm.originalPositionFor({
            line: offset.lineNumber,
            column: offset.columnNumber
          });

          if (sourceUrl === this.url) {
            lines.add(line);
          }
        }

        return sortLines(lines);
      });
    }

    let lines = this.getExecutableOffsets(this.source, true);
    return sortLines(lines);
  },

  /**
   * Extract all executable offsets from the given script
   * @param String url - extract offsets of the script with this url
   * @param Boolean onlyLine - will return only the line number
   * @return Set - Executable offsets/lines of the script
   **/
  getExecutableOffsets: function (source, onlyLine) {
    let offsets = new Set();
    for (let s of this.dbg.findScripts({ source })) {
      for (let offset of s.getAllColumnOffsets()) {
        offsets.add(onlyLine ? offset.lineNumber : offset);
      }
    }

    return offsets;
  },

  /**
   * Handler for the "source" packet.
   */
  onSource: function () {
    return Promise.resolve(this._init)
      .then(this._getSourceText)
      .then(({ content, contentType }) => {
        if (typeof content === "object" && content && content.constructor &&
            content.constructor.name === "ArrayBuffer") {
          return {
            source: arrayBufferGrip(content, this.threadActor.threadLifetimePool),
            contentType,
          };
        }
        return {
          source: createValueGrip(content, this.threadActor.threadLifetimePool,
            this.threadActor.objectGrip),
          contentType: contentType
        };
      })
      .catch(error => {
        reportError(error, "Got an exception during SA_onSource: ");
        throw new Error("Could not load the source for " + this.url + ".\n" +
                        DevToolsUtils.safeErrorString(error));
      });
  },

  /**
   * Handler for the "prettyPrint" packet.
   */
  prettyPrint: function (indent) {
    this.threadActor.sources.prettyPrint(this.url, indent);
    return this._getSourceText()
      .then(this._sendToPrettyPrintWorker(indent))
      .then(this._invertSourceMap)
      .then(this._encodeAndSetSourceMapURL)
      .then(() => {
        // We need to reset `_init` now because we have already done the work of
        // pretty printing, and don't want onSource to wait forever for
        // initialization to complete.
        this._init = null;
      })
      .then(this.onSource)
      .catch(error => {
        this.disablePrettyPrint();
        throw new Error(DevToolsUtils.safeErrorString(error));
      });
  },

  /**
   * Return a function that sends a request to the pretty print worker, waits on
   * the worker's response, and then returns the pretty printed code.
   *
   * @param Number indent
   *        The number of spaces to indent by the code by, when we send the
   *        request to the pretty print worker.
   * @returns Function
   *          Returns a function which takes an AST, and returns a promise that
   *          is resolved with `{ code, mappings }` where `code` is the pretty
   *          printed code, and `mappings` is an array of source mappings.
   */
  _sendToPrettyPrintWorker: function (indent) {
    return ({ content }) => {
      return this.prettyPrintWorker.performTask("pretty-print", {
        url: this.url,
        indent,
        source: content
      });
    };
  },

  /**
   * Invert a source map. So if a source map maps from a to b, return a new
   * source map from b to a. We need to do this because the source map we get
   * from _generatePrettyCodeAndMap goes the opposite way we want it to for
   * debugging.
   *
   * Note that the source map is modified in place.
   */
  _invertSourceMap: function ({ code, mappings }) {
    const generator = new SourceMapGenerator({ file: this.url });
    return DevToolsUtils.yieldingEach(mappings._array, m => {
      let mapping = {
        generated: {
          line: m.originalLine,
          column: m.originalColumn
        }
      };
      if (m.source) {
        mapping.source = m.source;
        mapping.original = {
          line: m.generatedLine,
          column: m.generatedColumn
        };
        mapping.name = m.name;
      }
      generator.addMapping(mapping);
    }).then(() => {
      generator.setSourceContent(this.url, code);
      let consumer = SourceMapConsumer.fromSourceMap(generator);

      return {
        code: code,
        map: consumer
      };
    });
  },

  /**
   * Save the source map back to our thread's ThreadSources object so that
   * stepping, breakpoints, debugger statements, etc can use it. If we are
   * pretty printing a source mapped source, we need to compose the existing
   * source map with our new one.
   */
  _encodeAndSetSourceMapURL: function ({ map: sm }) {
    let source = this.generatedSource || this.source;
    let sources = this.threadActor.sources;

    return sources.getSourceMap(source).then(prevMap => {
      if (prevMap) {
        // Compose the source maps
        this._oldSourceMapping = {
          url: source.sourceMapURL,
          map: prevMap
        };

        prevMap = SourceMapGenerator.fromSourceMap(prevMap);
        prevMap.applySourceMap(sm, this.url);
        sm = SourceMapConsumer.fromSourceMap(prevMap);
      }

      let actorSources = this.threadActor.sources;
      actorSources.clearSourceMapCache(source.sourceMapURL);
      actorSources.setSourceMapHard(source, null, sm);
    });
  },

  /**
   * Handler for the "disablePrettyPrint" packet.
   */
  disablePrettyPrint: function () {
    let source = this.generatedSource || this.source;
    let sources = this.threadActor.sources;

    sources.clearSourceMapCache(source.sourceMapURL, { hard: true });

    if (this._oldSourceMapping) {
      sources.setSourceMapHard(source,
                               this._oldSourceMapping.url,
                               this._oldSourceMapping.map);
      this._oldSourceMapping = null;
    }

    this.threadActor.sources.disablePrettyPrint(this.url);
    return this.onSource();
  },

  /**
   * Handler for the "blackbox" packet.
   */
  blackbox: function () {
    this.threadActor.sources.blackBox(this.url);
    if (this.threadActor.state == "paused"
        && this.threadActor.youngestFrame
        && this.threadActor.youngestFrame.script.url == this.url) {
      return true;
    }
    return false;
  },

  /**
   * Handler for the "unblackbox" packet.
   */
  unblackbox: function () {
    this.threadActor.sources.unblackBox(this.url);
  },

  /**
   * Handle a request to set a breakpoint.
   *
   * @param Number line
   *        Line to break on.
   * @param Number column
   *        Column to break on.
   * @param String condition
   *        A condition which must be true for breakpoint to be hit.
   * @param Boolean noSliding
   *        If true, disables breakpoint sliding.
   *
   * @returns Promise
   *          A promise that resolves to a JSON object representing the
   *          response.
   */
  setBreakpoint: function (line, column, condition, noSliding) {
    if (this.threadActor.state !== "paused") {
      let errorObject = {
        error: "wrongState",
        message: "Cannot set breakpoint while debuggee is running."
      };
      throw errorObject;
    }

    let location = new OriginalLocation(this, line, column);
    return this._getOrCreateBreakpointActor(
      location,
      condition,
      noSliding
    ).then((actor) => {
      let response = {
        actor: actor.actorID,
        isPending: actor.isPending
      };

      let actualLocation = actor.originalLocation;
      if (!actualLocation.equals(location)) {
        response.actualLocation = actualLocation.toJSON();
      }

      return response;
    });
  },

  /**
   * Get or create a BreakpointActor for the given location in the original
   * source, and ensure it is set as a breakpoint handler on all scripts that
   * match the given location.
   *
   * @param OriginalLocation originalLocation
   *        An OriginalLocation representing the location of the breakpoint in
   *        the original source.
   * @param String condition
   *        A string that is evaluated whenever the breakpoint is hit. If the
   *        string evaluates to false, the breakpoint is ignored.
   * @param Boolean noSliding
   *        If true, disables breakpoint sliding.
   *
   * @returns BreakpointActor
   *          A BreakpointActor representing the breakpoint.
   */
  _getOrCreateBreakpointActor: function (originalLocation, condition, noSliding) {
    let actor = this.breakpointActorMap.getActor(originalLocation);
    if (!actor) {
      actor = new BreakpointActor(this.threadActor, originalLocation);
      this.threadActor.threadLifetimePool.addActor(actor);
      this.breakpointActorMap.setActor(originalLocation, actor);
    }

    actor.condition = condition;

    return this._setBreakpoint(actor, noSliding);
  },

  /*
   * Ensure the given BreakpointActor is set as a breakpoint handler on all
   * scripts that match its location in the original source.
   *
   * If there are no scripts that match the location of the BreakpointActor,
   * we slide its location to the next closest line (for line breakpoints) or
   * column (for column breakpoint) that does.
   *
   * If breakpoint sliding fails, then either there are no scripts that contain
   * any code for the given location, or they were all garbage collected before
   * the debugger started running. We cannot distinguish between these two
   * cases, so we insert the BreakpointActor in the BreakpointActorMap as
   * a pending breakpoint. Whenever a new script is introduced, this method is
   * called again for each pending breakpoint.
   *
   * @param BreakpointActor actor
   *        The BreakpointActor to be set as a breakpoint handler.
   * @param Boolean noSliding
   *        If true, disables breakpoint sliding.
   *
   * @returns A Promise that resolves to the given BreakpointActor.
   */
  _setBreakpoint: function (actor, noSliding) {
    const { originalLocation } = actor;
    const { originalLine, originalSourceActor } = originalLocation;

    if (!this.isSourceMapped) {
      const generatedLocation = GeneratedLocation.fromOriginalLocation(originalLocation);
      if (!this._setBreakpointAtGeneratedLocation(actor, generatedLocation) &&
          !noSliding) {
        const query = { line: originalLine };
        // For most cases, we have a real source to query for. The
        // only time we don't is for HTML pages. In that case we want
        // to query for scripts in an HTML page based on its URL, as
        // there could be several sources within an HTML page.
        if (this.source) {
          query.source = this.source;
        } else {
          query.url = this.url;
        }
        const scripts = this.dbg.findScripts(query);

        // Never do breakpoint sliding for column breakpoints.
        // Additionally, never do breakpoint sliding if no scripts
        // exist on this line.
        //
        // Sliding can go horribly wrong if we always try to find the
        // next line with valid entry points in the entire file.
        // Scripts may be completely GCed and we never knew they
        // existed, so we end up sliding through whole functions to
        // the user's bewilderment.
        //
        // We can slide reliably if any scripts exist, however, due
        // to how scripts are kept alive. A parent Debugger.Script
        // keeps all of its children alive, so as long as we have a
        // valid script, we can slide through it and know we won't
        // slide through any of its child scripts. Additionally, if a
        // script gets GCed, that means that all parents scripts are
        // GCed as well, and no scripts will exist on those lines
        // anymore. We will never slide through a GCed script.
        if (originalLocation.originalColumn || scripts.length === 0) {
          return Promise.resolve(actor);
        }

        // Find the script that spans the largest amount of code to
        // determine the bounds for sliding.
        const largestScript = scripts.reduce((largestScr, script) => {
          if (script.lineCount > largestScr.lineCount) {
            return script;
          }
          return largestScr;
        });
        const maxLine = largestScript.startLine + largestScript.lineCount - 1;

        let actualLine = originalLine;
        for (; actualLine <= maxLine; actualLine++) {
          const loc = new GeneratedLocation(this, actualLine);
          if (this._setBreakpointAtGeneratedLocation(actor, loc)) {
            break;
          }
        }

        // The above loop should never complete. We only did breakpoint sliding
        // because we found scripts on the line we started from,
        // which means there must be valid entry points somewhere
        // within those scripts.
        if (actualLine > maxLine) {
          return Promise.reject({
            error: "noCodeAtLineColumn",
            message:
              "Could not find any entry points to set a breakpoint on, " +
              "even though I was told a script existed on the line I started " +
              "the search with."
          });
        }

        // Update the actor to use the new location (reusing a
        // previous breakpoint if it already exists on that line).
        const actualLocation = new OriginalLocation(originalSourceActor, actualLine);
        const existingActor = this.breakpointActorMap.getActor(actualLocation);
        this.breakpointActorMap.deleteActor(originalLocation);
        if (existingActor) {
          actor.delete();
          actor = existingActor;
        } else {
          actor.originalLocation = actualLocation;
          this.breakpointActorMap.setActor(actualLocation, actor);
        }
      }

      return Promise.resolve(actor);
    }
    return this.sources.getAllGeneratedLocations(originalLocation)
      .then((generatedLocations) => {
        this._setBreakpointAtAllGeneratedLocations(
          actor,
          generatedLocations
        );

        return actor;
      });
  },

  _setBreakpointAtAllGeneratedLocations: function (actor, generatedLocations) {
    let success = false;
    for (let generatedLocation of generatedLocations) {
      if (this._setBreakpointAtGeneratedLocation(
        actor,
        generatedLocation
      )) {
        success = true;
      }
    }
    return success;
  },

  /*
   * Ensure the given BreakpointActor is set as breakpoint handler on all
   * scripts that match the given location in the generated source.
   *
   * @param BreakpointActor actor
   *        The BreakpointActor to be set as a breakpoint handler.
   * @param GeneratedLocation generatedLocation
   *        A GeneratedLocation representing the location in the generated
   *        source for which the given BreakpointActor is to be set as a
   *        breakpoint handler.
   *
   * @returns A Boolean that is true if the BreakpointActor was set as a
   *          breakpoint handler on at least one script, and false otherwise.
   */
  _setBreakpointAtGeneratedLocation: function (actor, generatedLocation) {
    let {
      generatedSourceActor,
      generatedLine,
      generatedColumn,
      generatedLastColumn
    } = generatedLocation;

    // Find all scripts that match the given source actor and line
    // number.
    const query = { line: generatedLine };
    if (generatedSourceActor.source) {
      query.source = generatedSourceActor.source;
    } else {
      query.url = generatedSourceActor.url;
    }
    let scripts = this.dbg.findScripts(query);

    scripts = scripts.filter((script) => !actor.hasScript(script));

    // Find all entry points that correspond to the given location.
    let entryPoints = [];
    if (generatedColumn === undefined) {
      // This is a line breakpoint, so we are interested in all offsets
      // that correspond to the given line number.
      for (let script of scripts) {
        let offsets = script.getLineOffsets(generatedLine);
        if (offsets.length > 0) {
          entryPoints.push({ script, offsets });
        }
      }
    } else {
      // Compute columnToOffsetMaps for each script so that we can
      // find matching entrypoints for the column breakpoint.
      const columnToOffsetMaps = scripts.map(script =>
        [
          script,
          script.getAllColumnOffsets()
            .filter(({ lineNumber }) => lineNumber === generatedLine)
        ]
      );

      // This is a column breakpoint, so we are interested in all column
      // offsets that correspond to the given line *and* column number.
      for (let [script, columnToOffsetMap] of columnToOffsetMaps) {
        for (let { columnNumber: column, offset } of columnToOffsetMap) {
          if (column >= generatedColumn && column <= generatedLastColumn) {
            entryPoints.push({ script, offsets: [offset] });
          }
        }
      }

      // If we don't find any matching entrypoints,
      // then we should see if the breakpoint comes before or after the column offsets.
      if (entryPoints.length === 0) {
        for (let [script, columnToOffsetMap] of columnToOffsetMaps) {
          if (columnToOffsetMap.length > 0) {
            const firstColumnOffset = columnToOffsetMap[0];
            const lastColumnOffset = columnToOffsetMap[columnToOffsetMap.length - 1];

            if (generatedColumn < firstColumnOffset.columnNumber) {
              entryPoints.push({ script, offsets: [firstColumnOffset.offset] });
            }

            if (generatedColumn > lastColumnOffset.columnNumber) {
              entryPoints.push({ script, offsets: [lastColumnOffset.offset] });
            }
          }
        }
      }
    }

    if (entryPoints.length === 0) {
      return false;
    }

    setBreakpointAtEntryPoints(actor, entryPoints);
    return true;
  }
});

exports.SourceActor = SourceActor;
PK
!<�r?T?T9chrome/devtools/modules/devtools/server/actors/storage.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {Cc, Ci, Cu, CC} = require("chrome");
const protocol = require("devtools/shared/protocol");
const {LongStringActor} = require("devtools/server/actors/string");
const {DebuggerServer} = require("devtools/server/main");
const Services = require("Services");
const defer = require("devtools/shared/defer");
const {isWindowIncluded} = require("devtools/shared/layout/utils");
const specs = require("devtools/shared/specs/storage");

const DEFAULT_VALUE = "value";

loader.lazyRequireGetter(this, "naturalSortCaseInsensitive",
  "devtools/client/shared/natural-sort", true);

// "Lax", "Strict" and "Unset" are special values of the sameSite property
// that should not be translated.
const COOKIE_SAMESITE = {
  LAX: "Lax",
  STRICT: "Strict",
  UNSET: "Unset"
};

// GUID to be used as a separator in compound keys. This must match the same
// constant in devtools/client/storage/ui.js,
// devtools/client/storage/test/head.js and
// devtools/server/tests/browser/head.js
const SEPARATOR_GUID = "{9d414cc5-8319-0a04-0586-c0a6ae01670a}";

loader.lazyImporter(this, "OS", "resource://gre/modules/osfile.jsm");
loader.lazyImporter(this, "Sqlite", "resource://gre/modules/Sqlite.jsm");

// We give this a funny name to avoid confusion with the global
// indexedDB.
loader.lazyGetter(this, "indexedDBForStorage", () => {
  // On xpcshell, we can't instantiate indexedDB without crashing
  try {
    let sandbox
      = Cu.Sandbox(CC("@mozilla.org/systemprincipal;1", "nsIPrincipal")(),
                   {wantGlobalProperties: ["indexedDB"]});
    return sandbox.indexedDB;
  } catch (e) {
    return {};
  }
});

// Maximum number of cookies/local storage key-value-pairs that can be sent
// over the wire to the client in one request.
const MAX_STORE_OBJECT_COUNT = 50;
// Delay for the batch job that sends the accumulated update packets to the
// client (ms).
const BATCH_DELAY = 200;

// MAX_COOKIE_EXPIRY should be 2^63-1, but JavaScript can't handle that
// precision.
const MAX_COOKIE_EXPIRY = Math.pow(2, 62);

// A RegExp for characters that cannot appear in a file/directory name. This is
// used to sanitize the host name for indexed db to lookup whether the file is
// present in <profileDir>/storage/default/ location
var illegalFileNameCharacters = [
  "[",
  // Control characters \001 to \036
  "\\x00-\\x24",
  // Special characters
  "/:*?\\\"<>|\\\\",
  "]"
].join("");
var ILLEGAL_CHAR_REGEX = new RegExp(illegalFileNameCharacters, "g");

// Holder for all the registered storage actors.
var storageTypePool = new Map();

/**
 * An async method equivalent to setTimeout but using Promises
 *
 * @param {number} time
 *        The wait time in milliseconds.
 */
function sleep(time) {
  let deferred = defer();

  setTimeout(() => {
    deferred.resolve(null);
  }, time);

  return deferred.promise;
}

// Helper methods to create a storage actor.
var StorageActors = {};

/**
 * Creates a default object with the common methods required by all storage
 * actors.
 *
 * This default object is missing a couple of required methods that should be
 * implemented seperately for each actor. They are namely:
 *   - observe : Method which gets triggered on the notificaiton of the watched
 *               topic.
 *   - getNamesForHost : Given a host, get list of all known store names.
 *   - getValuesForHost : Given a host (and optionally a name) get all known
 *                        store objects.
 *   - toStoreObject : Given a store object, convert it to the required format
 *                     so that it can be transferred over wire.
 *   - populateStoresForHost : Given a host, populate the map of all store
 *                             objects for it
 *   - getFields: Given a subType(optional), get an array of objects containing
 *                column field info. The info includes,
 *                "name" is name of colume key.
 *                "editable" is 1 means editable field; 0 means uneditable.
 *
 * @param {string} typeName
 *        The typeName of the actor.
 * @param {array} observationTopics
 *        An array of topics which this actor listens to via Notification Observers.
 */
StorageActors.defaults = function (typeName, observationTopics) {
  return {
    typeName: typeName,

    get conn() {
      return this.storageActor.conn;
    },

    /**
     * Returns a list of currently knwon hosts for the target window. This list
     * contains unique hosts from the window + all inner windows.
     */
    get hosts() {
      let hosts = new Set();
      for (let {location} of this.storageActor.windows) {
        let host = this.getHostName(location);

        if (host) {
          hosts.add(host);
        }
      }
      return hosts;
    },

    /**
     * Returns all the windows present on the page. Includes main window + inner
     * iframe windows.
     */
    get windows() {
      return this.storageActor.windows;
    },

    /**
     * Converts the window.location object into a URL (e.g. http://domain.com).
     */
    getHostName(location) {
      if (!location) {
        // Debugging a legacy Firefox extension... no hostname available and no
        // storage possible.
        return null;
      }

      switch (location.protocol) {
        case "data:":
          // data: URLs do not support storage of any type.
          return null;
        case "about:":
          // Fallthrough.
        case "chrome:":
          // Fallthrough.
        case "file:":
          return location.protocol + location.pathname;
        case "resource:":
          return location.origin + location.pathname;
        case "moz-extension:":
          return location.origin;
        case "javascript:":
          return location.href;
        default:
          // http: or unknown protocol.
          return `${location.protocol}//${location.host}`;
      }
    },

    initialize(storageActor) {
      protocol.Actor.prototype.initialize.call(this, null);

      this.storageActor = storageActor;

      this.populateStoresForHosts();
      if (observationTopics) {
        observationTopics.forEach((observationTopic) => {
          Services.obs.addObserver(this, observationTopic);
        });
      }
      this.onWindowReady = this.onWindowReady.bind(this);
      this.onWindowDestroyed = this.onWindowDestroyed.bind(this);
      this.storageActor.on("window-ready", this.onWindowReady);
      this.storageActor.on("window-destroyed", this.onWindowDestroyed);
    },

    destroy() {
      if (observationTopics) {
        observationTopics.forEach((observationTopic) => {
          Services.obs.removeObserver(this, observationTopic);
        });
      }
      this.storageActor.off("window-ready", this.onWindowReady);
      this.storageActor.off("window-destroyed", this.onWindowDestroyed);

      this.hostVsStores.clear();

      protocol.Actor.prototype.destroy.call(this);

      this.storageActor = null;
    },

    getNamesForHost(host) {
      return [...this.hostVsStores.get(host).keys()];
    },

    getValuesForHost(host, name) {
      if (name) {
        return [this.hostVsStores.get(host).get(name)];
      }
      return [...this.hostVsStores.get(host).values()];
    },

    getObjectsSize(host, names) {
      return names.length;
    },

    /**
     * When a new window is added to the page. This generally means that a new
     * iframe is created, or the current window is completely reloaded.
     *
     * @param {window} window
     *        The window which was added.
     */
    async onWindowReady(window) {
      let host = this.getHostName(window.location);
      if (host && !this.hostVsStores.has(host)) {
        await this.populateStoresForHost(host, window);
        let data = {};
        data[host] = this.getNamesForHost(host);
        this.storageActor.update("added", typeName, data);
      }
    },

    /**
     * When a window is removed from the page. This generally means that an
     * iframe was removed, or the current window reload is triggered.
     *
     * @param {window} window
     *        The window which was removed.
     */
    onWindowDestroyed(window) {
      if (!window.location) {
        // Nothing can be done if location object is null
        return;
      }
      let host = this.getHostName(window.location);
      if (host && !this.hosts.has(host)) {
        this.hostVsStores.delete(host);
        let data = {};
        data[host] = [];
        this.storageActor.update("deleted", typeName, data);
      }
    },

    form(form, detail) {
      if (detail === "actorid") {
        return this.actorID;
      }

      let hosts = {};
      for (let host of this.hosts) {
        hosts[host] = [];
      }

      return {
        actor: this.actorID,
        hosts: hosts
      };
    },

    /**
     * Populates a map of known hosts vs a map of stores vs value.
     */
    populateStoresForHosts() {
      this.hostVsStores = new Map();
      for (let host of this.hosts) {
        this.populateStoresForHost(host);
      }
    },

    /**
     * Returns a list of requested store objects. Maximum values returned are
     * MAX_STORE_OBJECT_COUNT. This method returns paginated values whose
     * starting index and total size can be controlled via the options object
     *
     * @param {string} host
     *        The host name for which the store values are required.
     * @param {array:string} names
     *        Array containing the names of required store objects. Empty if all
     *        items are required.
     * @param {object} options
     *        Additional options for the request containing following
     *        properties:
     *         - offset {number} : The begin index of the returned array amongst
     *                  the total values
     *         - size {number} : The number of values required.
     *         - sortOn {string} : The values should be sorted on this property.
     *         - index {string} : In case of indexed db, the IDBIndex to be used
     *                 for fetching the values.
     *
     * @return {object} An object containing following properties:
     *          - offset - The actual offset of the returned array. This might
     *                     be different from the requested offset if that was
     *                     invalid
     *          - total - The total number of entries possible.
     *          - data - The requested values.
     */
    async getStoreObjects(host, names, options = {}) {
      let offset = options.offset || 0;
      let size = options.size || MAX_STORE_OBJECT_COUNT;
      if (size > MAX_STORE_OBJECT_COUNT) {
        size = MAX_STORE_OBJECT_COUNT;
      }
      let sortOn = options.sortOn || "name";

      let toReturn = {
        offset: offset,
        total: 0,
        data: []
      };

      let principal = null;
      if (this.typeName === "indexedDB") {
        // We only acquire principal when the type of the storage is indexedDB
        // because the principal only matters the indexedDB.
        let win = this.storageActor.getWindowFromHost(host);
        if (win) {
          principal = win.document.nodePrincipal;
        }
      }

      if (names) {
        for (let name of names) {
          let values = await this.getValuesForHost(host, name, options,
            this.hostVsStores, principal);

          let {result, objectStores} = values;

          if (result && typeof result.objectsSize !== "undefined") {
            for (let {key, count} of result.objectsSize) {
              this.objectsSize[key] = count;
            }
          }

          if (result) {
            toReturn.data.push(...result.data);
          } else if (objectStores) {
            toReturn.data.push(...objectStores);
          } else {
            toReturn.data.push(...values);
          }
        }

        toReturn.total = this.getObjectsSize(host, names, options);

        if (offset > toReturn.total) {
          // In this case, toReturn.data is an empty array.
          toReturn.offset = toReturn.total;
          toReturn.data = [];
        } else {
          // We need to use natural sort before slicing.
          let sorted = toReturn.data.sort((a, b) => {
            return naturalSortCaseInsensitive(a[sortOn], b[sortOn]);
          });
          let sliced = sorted.slice(offset, offset + size);
          toReturn.data = sliced.map(a => this.toStoreObject(a));
        }
      } else {
        let obj = await this.getValuesForHost(host, undefined, undefined,
                                              this.hostVsStores, principal);
        if (obj.dbs) {
          obj = obj.dbs;
        }

        toReturn.total = obj.length;

        if (offset > toReturn.total) {
          // In this case, toReturn.data is an empty array.
          toReturn.offset = offset = toReturn.total;
          toReturn.data = [];
        } else {
          // We need to use natural sort before slicing.
          let sorted = obj.sort((a, b) => {
            return naturalSortCaseInsensitive(a[sortOn], b[sortOn]);
          });
          let sliced = sorted.slice(offset, offset + size);
          toReturn.data = sliced.map(object => this.toStoreObject(object));
        }
      }

      return toReturn;
    }
  };
};

/**
 * Creates an actor and its corresponding front and registers it to the Storage
 * Actor.
 *
 * @See StorageActors.defaults()
 *
 * @param {object} options
 *        Options required by StorageActors.defaults method which are :
 *         - typeName {string}
 *                    The typeName of the actor.
 *         - observationTopics {array}
 *                            The topics which this actor listens to via
 *                            Notification Observers.
 * @param {object} overrides
 *        All the methods which you want to be different from the ones in
 *        StorageActors.defaults method plus the required ones described there.
 */
StorageActors.createActor = function (options = {}, overrides = {}) {
  let actorObject = StorageActors.defaults(
    options.typeName,
    options.observationTopics || null
  );
  for (let key in overrides) {
    actorObject[key] = overrides[key];
  }

  let actorSpec = specs.childSpecs[options.typeName];
  let actor = protocol.ActorClassWithSpec(actorSpec, actorObject);
  storageTypePool.set(actorObject.typeName, actor);
};

/**
 * The Cookies actor and front.
 */
StorageActors.createActor({
  typeName: "cookies"
}, {
  initialize(storageActor) {
    protocol.Actor.prototype.initialize.call(this, null);

    this.storageActor = storageActor;

    this.maybeSetupChildProcess();
    this.populateStoresForHosts();
    this.addCookieObservers();

    this.onWindowReady = this.onWindowReady.bind(this);
    this.onWindowDestroyed = this.onWindowDestroyed.bind(this);
    this.storageActor.on("window-ready", this.onWindowReady);
    this.storageActor.on("window-destroyed", this.onWindowDestroyed);
  },

  destroy() {
    this.hostVsStores.clear();

    // We need to remove the cookie listeners early in E10S mode so we need to
    // use a conditional here to ensure that we only attempt to remove them in
    // single process mode.
    if (!DebuggerServer.isInChildProcess) {
      this.removeCookieObservers();
    }

    this.storageActor.off("window-ready", this.onWindowReady);
    this.storageActor.off("window-destroyed", this.onWindowDestroyed);

    this._pendingResponse = null;

    protocol.Actor.prototype.destroy.call(this);

    this.storageActor = null;
  },

  /**
   * Given a cookie object, figure out all the matching hosts from the page that
   * the cookie belong to.
   */
  getMatchingHosts(cookies) {
    if (!cookies.length) {
      cookies = [cookies];
    }
    let hosts = new Set();
    for (let host of this.hosts) {
      for (let cookie of cookies) {
        if (this.isCookieAtHost(cookie, host)) {
          hosts.add(host);
        }
      }
    }
    return [...hosts];
  },

  /**
   * Given a cookie object and a host, figure out if the cookie is valid for
   * that host.
   */
  isCookieAtHost(cookie, host) {
    if (cookie.host == null) {
      return host == null;
    }

    host = trimHttpHttpsPort(host);

    if (cookie.host.startsWith(".")) {
      return ("." + host).endsWith(cookie.host);
    }
    if (cookie.host === "") {
      return host.startsWith("file://" + cookie.path);
    }

    return cookie.host == host;
  },

  toStoreObject(cookie) {
    if (!cookie) {
      return null;
    }

    return {
      uniqueKey: `${cookie.name}${SEPARATOR_GUID}${cookie.host}` +
                 `${SEPARATOR_GUID}${cookie.path}`,
      name: cookie.name,
      host: cookie.host || "",
      path: cookie.path || "",

      // because expires is in seconds
      expires: (cookie.expires || 0) * 1000,

      // because creationTime is in micro seconds
      creationTime: cookie.creationTime / 1000,

      // - do -
      lastAccessed: cookie.lastAccessed / 1000,
      value: new LongStringActor(this.conn, cookie.value || ""),
      isDomain: cookie.isDomain,
      isSecure: cookie.isSecure,
      isHttpOnly: cookie.isHttpOnly,
      sameSite: this.getSameSiteStringFromCookie(cookie)
    };
  },

  getSameSiteStringFromCookie(cookie) {
    switch (cookie.sameSite) {
      case cookie.SAMESITE_LAX:
        return COOKIE_SAMESITE.LAX;
      case cookie.SAMESITE_STRICT:
        return COOKIE_SAMESITE.STRICT;
    }
    // cookie.SAMESITE_UNSET
    return COOKIE_SAMESITE.UNSET;
  },

  populateStoresForHost(host) {
    this.hostVsStores.set(host, new Map());
    let doc = this.storageActor.document;

    let cookies = this.getCookiesFromHost(host, doc.nodePrincipal
                                                   .originAttributes);

    for (let cookie of cookies) {
      if (this.isCookieAtHost(cookie, host)) {
        let uniqueKey = `${cookie.name}${SEPARATOR_GUID}${cookie.host}` +
                        `${SEPARATOR_GUID}${cookie.path}`;

        this.hostVsStores.get(host).set(uniqueKey, cookie);
      }
    }
  },

  /**
   * Notification observer for "cookie-change".
   *
   * @param subject
   *        {Cookie|[Array]} A JSON parsed object containing either a single
   *        cookie representation or an array. Array is only in case of
   *        a "batch-deleted" action.
   * @param {string} topic
   *        The topic of the notification.
   * @param {string} action
   *        Additional data associated with the notification. Its the type of
   *        cookie change in the "cookie-change" topic.
   */
  onCookieChanged(subject, topic, action) {
    if (topic !== "cookie-changed" ||
        !this.storageActor ||
        !this.storageActor.windows) {
      return null;
    }

    let hosts = this.getMatchingHosts(subject);
    let data = {};

    switch (action) {
      case "added":
      case "changed":
        if (hosts.length) {
          for (let host of hosts) {
            let uniqueKey = `${subject.name}${SEPARATOR_GUID}${subject.host}` +
                            `${SEPARATOR_GUID}${subject.path}`;

            this.hostVsStores.get(host).set(uniqueKey, subject);
            data[host] = [uniqueKey];
          }
          this.storageActor.update(action, "cookies", data);
        }
        break;

      case "deleted":
        if (hosts.length) {
          for (let host of hosts) {
            let uniqueKey = `${subject.name}${SEPARATOR_GUID}${subject.host}` +
                            `${SEPARATOR_GUID}${subject.path}`;

            this.hostVsStores.get(host).delete(uniqueKey);
            data[host] = [uniqueKey];
          }
          this.storageActor.update("deleted", "cookies", data);
        }
        break;

      case "batch-deleted":
        if (hosts.length) {
          for (let host of hosts) {
            let stores = [];
            for (let cookie of subject) {
              let uniqueKey = `${cookie.name}${SEPARATOR_GUID}${cookie.host}` +
                              `${SEPARATOR_GUID}${cookie.path}`;

              this.hostVsStores.get(host).delete(uniqueKey);
              stores.push(uniqueKey);
            }
            data[host] = stores;
          }
          this.storageActor.update("deleted", "cookies", data);
        }
        break;

      case "cleared":
        if (hosts.length) {
          for (let host of hosts) {
            data[host] = [];
          }
          this.storageActor.update("cleared", "cookies", data);
        }
        break;
    }
    return null;
  },

  async getFields() {
    return [
      { name: "uniqueKey", editable: false, private: true },
      { name: "name", editable: true, hidden: false },
      { name: "host", editable: true, hidden: false },
      { name: "path", editable: true, hidden: false },
      { name: "expires", editable: true, hidden: false },
      { name: "lastAccessed", editable: false, hidden: false },
      { name: "creationTime", editable: false, hidden: true },
      { name: "value", editable: true, hidden: false },
      { name: "isDomain", editable: false, hidden: true },
      { name: "isSecure", editable: true, hidden: true },
      { name: "isHttpOnly", editable: true, hidden: false },
      { name: "sameSite", editable: false, hidden: false }
    ];
  },

  /**
   * Pass the editItem command from the content to the chrome process.
   *
   * @param {Object} data
   *        See editCookie() for format details.
   */
  async editItem(data) {
    let doc = this.storageActor.document;
    data.originAttributes = doc.nodePrincipal
                               .originAttributes;
    this.editCookie(data);
  },

  async addItem(guid) {
    let doc = this.storageActor.document;
    let time = new Date().getTime();
    let expiry = new Date(time + 3600 * 24 * 1000).toGMTString();

    doc.cookie = `${guid}=${DEFAULT_VALUE};expires=${expiry}`;
  },

  async removeItem(host, name) {
    let doc = this.storageActor.document;
    this.removeCookie(host, name, doc.nodePrincipal
                                     .originAttributes);
  },

  async removeAll(host, domain) {
    let doc = this.storageActor.document;
    this.removeAllCookies(host, domain, doc.nodePrincipal
                                           .originAttributes);
  },

  async removeAllSessionCookies(host, domain) {
    let doc = this.storageActor.document;
    this.removeAllSessionCookies(host, domain, doc.nodePrincipal
        .originAttributes);
  },

  maybeSetupChildProcess() {
    cookieHelpers.onCookieChanged = this.onCookieChanged.bind(this);

    if (!DebuggerServer.isInChildProcess) {
      this.getCookiesFromHost =
        cookieHelpers.getCookiesFromHost.bind(cookieHelpers);
      this.addCookieObservers =
        cookieHelpers.addCookieObservers.bind(cookieHelpers);
      this.removeCookieObservers =
        cookieHelpers.removeCookieObservers.bind(cookieHelpers);
      this.editCookie =
        cookieHelpers.editCookie.bind(cookieHelpers);
      this.removeCookie =
        cookieHelpers.removeCookie.bind(cookieHelpers);
      this.removeAllCookies =
        cookieHelpers.removeAllCookies.bind(cookieHelpers);
      this.removeAllSessionCookies =
        cookieHelpers.removeAllSessionCookies.bind(cookieHelpers);
      return;
    }

    const { sendSyncMessage, addMessageListener } =
      this.conn.parentMessageManager;

    this.conn.setupInParent({
      module: "devtools/server/actors/storage",
      setupParent: "setupParentProcessForCookies"
    });

    this.getCookiesFromHost =
      callParentProcess.bind(null, "getCookiesFromHost");
    this.addCookieObservers =
      callParentProcess.bind(null, "addCookieObservers");
    this.removeCookieObservers =
      callParentProcess.bind(null, "removeCookieObservers");
    this.editCookie =
      callParentProcess.bind(null, "editCookie");
    this.removeCookie =
      callParentProcess.bind(null, "removeCookie");
    this.removeAllCookies =
      callParentProcess.bind(null, "removeAllCookies");
    this.removeAllSessionCookies =
      callParentProcess.bind(null, "removeAllSessionCookies");

    addMessageListener("debug:storage-cookie-request-child",
                       cookieHelpers.handleParentRequest);

    function callParentProcess(methodName, ...args) {
      let reply = sendSyncMessage("debug:storage-cookie-request-parent", {
        method: methodName,
        args: args
      });

      if (reply.length === 0) {
        console.error("ERR_DIRECTOR_CHILD_NO_REPLY from " + methodName);
      } else if (reply.length > 1) {
        console.error("ERR_DIRECTOR_CHILD_MULTIPLE_REPLIES from " + methodName);
      }

      let result = reply[0];

      if (methodName === "getCookiesFromHost") {
        return JSON.parse(result);
      }

      return result;
    }
  },
});

var cookieHelpers = {
  getCookiesFromHost(host, originAttributes) {
    // Local files have no host.
    if (host.startsWith("file:///")) {
      host = "";
    }

    host = trimHttpHttpsPort(host);

    let cookies = Services.cookies.getCookiesFromHost(host, originAttributes);
    let store = [];

    while (cookies.hasMoreElements()) {
      let cookie = cookies.getNext().QueryInterface(Ci.nsICookie2);

      store.push(cookie);
    }

    return store;
  },

  /**
   * Apply the results of a cookie edit.
   *
   * @param {Object} data
   *        An object in the following format:
   *        {
   *          host: "http://www.mozilla.org",
   *          field: "value",
   *          editCookie: "name",
   *          oldValue: "%7BHello%7D",
   *          newValue: "%7BHelloo%7D",
   *          items: {
   *            name: "optimizelyBuckets",
   *            path: "/",
   *            host: ".mozilla.org",
   *            expires: "Mon, 02 Jun 2025 12:37:37 GMT",
   *            creationTime: "Tue, 18 Nov 2014 16:21:18 GMT",
   *            lastAccessed: "Wed, 17 Feb 2016 10:06:23 GMT",
   *            value: "%7BHelloo%7D",
   *            isDomain: "true",
   *            isSecure: "false",
   *            isHttpOnly: "false"
   *          }
   *        }
   */
  editCookie(data) {
    let {field, oldValue, newValue} = data;
    let origName = field === "name" ? oldValue : data.items.name;
    let origHost = field === "host" ? oldValue : data.items.host;
    let origPath = field === "path" ? oldValue : data.items.path;
    let cookie = null;

    let enumerator =
      Services.cookies.getCookiesFromHost(origHost, data.originAttributes || {});

    while (enumerator.hasMoreElements()) {
      let nsiCookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
      if (nsiCookie.name === origName &&
          nsiCookie.host === origHost &&
          nsiCookie.path === origPath) {
        cookie = {
          host: nsiCookie.host,
          path: nsiCookie.path,
          name: nsiCookie.name,
          value: nsiCookie.value,
          isSecure: nsiCookie.isSecure,
          isHttpOnly: nsiCookie.isHttpOnly,
          isSession: nsiCookie.isSession,
          expires: nsiCookie.expires,
          originAttributes: nsiCookie.originAttributes
        };
        break;
      }
    }

    if (!cookie) {
      return;
    }

    // If the date is expired set it for 10 seconds in the future.
    let now = new Date();
    if (!cookie.isSession && (cookie.expires * 1000) <= now) {
      let tenSecondsFromNow = (now.getTime() + 10 * 1000) / 1000;

      cookie.expires = tenSecondsFromNow;
    }

    switch (field) {
      case "isSecure":
      case "isHttpOnly":
      case "isSession":
        newValue = newValue === "true";
        break;

      case "expires":
        newValue = Date.parse(newValue) / 1000;

        if (isNaN(newValue)) {
          newValue = MAX_COOKIE_EXPIRY;
        }
        break;

      case "host":
      case "name":
      case "path":
        // Remove the edited cookie.
        Services.cookies.remove(origHost, origName, origPath,
                                false, cookie.originAttributes);
        break;
    }

    // Apply changes.
    cookie[field] = newValue;

    // cookie.isSession is not always set correctly on session cookies so we
    // need to trust cookie.expires instead.
    cookie.isSession = !cookie.expires;

    // Add the edited cookie.
    Services.cookies.add(
      cookie.host,
      cookie.path,
      cookie.name,
      cookie.value,
      cookie.isSecure,
      cookie.isHttpOnly,
      cookie.isSession,
      cookie.isSession ? MAX_COOKIE_EXPIRY : cookie.expires,
      cookie.originAttributes
    );
  },

  _removeCookies(host, opts = {}) {
    // We use a uniqueId to emulate compound keys for cookies. We need to
    // extract the cookie name to remove the correct cookie.
    if (opts.name) {
      let split = opts.name.split(SEPARATOR_GUID);

      opts.name = split[0];
      opts.path = split[2];
    }

    host = trimHttpHttpsPort(host);

    function hostMatches(cookieHost, matchHost) {
      if (cookieHost == null) {
        return matchHost == null;
      }
      if (cookieHost.startsWith(".")) {
        return ("." + matchHost).endsWith(cookieHost);
      }
      return cookieHost == host;
    }

    let enumerator =
      Services.cookies.getCookiesFromHost(host, opts.originAttributes || {});

    while (enumerator.hasMoreElements()) {
      let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
      if (hostMatches(cookie.host, host) &&
          (!opts.name || cookie.name === opts.name) &&
          (!opts.domain || cookie.host === opts.domain) &&
          (!opts.path || cookie.path === opts.path) &&
          (!opts.session || (!cookie.expires && !cookie.maxAge))) {
        Services.cookies.remove(
          cookie.host,
          cookie.name,
          cookie.path,
          false,
          cookie.originAttributes
        );
      }
    }
  },

  removeCookie(host, name, originAttributes) {
    if (name !== undefined) {
      this._removeCookies(host, { name, originAttributes });
    }
  },

  removeAllCookies(host, domain, originAttributes) {
    this._removeCookies(host, { domain, originAttributes });
  },

  removeAllSessionCookies(host, domain, originAttributes) {
    this._removeCookies(host, { domain, originAttributes, session: true });
  },

  addCookieObservers() {
    Services.obs.addObserver(cookieHelpers, "cookie-changed");
    return null;
  },

  removeCookieObservers() {
    Services.obs.removeObserver(cookieHelpers, "cookie-changed");
    return null;
  },

  observe(subject, topic, data) {
    if (!subject) {
      return;
    }

    switch (topic) {
      case "cookie-changed":
        if (data === "batch-deleted") {
          let cookiesNoInterface = subject.QueryInterface(Ci.nsIArray);
          let cookies = [];

          for (let i = 0; i < cookiesNoInterface.length; i++) {
            let cookie = cookiesNoInterface.queryElementAt(i, Ci.nsICookie2);
            cookies.push(cookie);
          }
          cookieHelpers.onCookieChanged(cookies, topic, data);

          return;
        }

        let cookie = subject.QueryInterface(Ci.nsICookie2);
        cookieHelpers.onCookieChanged(cookie, topic, data);
        break;
    }
  },

  handleParentRequest(msg) {
    switch (msg.json.method) {
      case "onCookieChanged":
        let [cookie, topic, data] = msg.data.args;
        cookie = JSON.parse(cookie);
        cookieHelpers.onCookieChanged(cookie, topic, data);
        break;
    }
  },

  handleChildRequest(msg) {
    switch (msg.json.method) {
      case "getCookiesFromHost": {
        let host = msg.data.args[0];
        let originAttributes = msg.data.args[1];
        let cookies = cookieHelpers.getCookiesFromHost(host, originAttributes);
        return JSON.stringify(cookies);
      }
      case "addCookieObservers": {
        return cookieHelpers.addCookieObservers();
      }
      case "removeCookieObservers": {
        return cookieHelpers.removeCookieObservers();
      }
      case "editCookie": {
        let rowdata = msg.data.args[0];
        return cookieHelpers.editCookie(rowdata);
      }
      case "createNewCookie": {
        let host = msg.data.args[0];
        let guid = msg.data.args[1];
        let originAttributes = msg.data.args[2];
        return cookieHelpers.createNewCookie(host, guid, originAttributes);
      }
      case "removeCookie": {
        let host = msg.data.args[0];
        let name = msg.data.args[1];
        let originAttributes = msg.data.args[2];
        return cookieHelpers.removeCookie(host, name, originAttributes);
      }
      case "removeAllCookies": {
        let host = msg.data.args[0];
        let domain = msg.data.args[1];
        let originAttributes = msg.data.args[2];
        return cookieHelpers.removeAllCookies(host, domain, originAttributes);
      }
      case "removeAllSessionCookies": {
        let host = msg.data.args[0];
        let domain = msg.data.args[1];
        let originAttributes = msg.data.args[2];
        return cookieHelpers.removeAllSessionCookies(host, domain, originAttributes);
      }
      default:
        console.error("ERR_DIRECTOR_PARENT_UNKNOWN_METHOD", msg.json.method);
        throw new Error("ERR_DIRECTOR_PARENT_UNKNOWN_METHOD");
    }
  },
};

/**
 * E10S parent/child setup helpers
 */

exports.setupParentProcessForCookies = function ({ mm, prefix }) {
  cookieHelpers.onCookieChanged =
    callChildProcess.bind(null, "onCookieChanged");

  // listen for director-script requests from the child process
  setMessageManager(mm);

  function callChildProcess(methodName, ...args) {
    if (methodName === "onCookieChanged") {
      args[0] = JSON.stringify(args[0]);
    }

    try {
      mm.sendAsyncMessage("debug:storage-cookie-request-child", {
        method: methodName,
        args: args
      });
    } catch (e) {
      // We may receive a NS_ERROR_NOT_INITIALIZED if the target window has
      // been closed. This can legitimately happen in between test runs.
    }
  }

  function setMessageManager(newMM) {
    if (mm) {
      mm.removeMessageListener("debug:storage-cookie-request-parent",
                               cookieHelpers.handleChildRequest);
    }
    mm = newMM;
    if (mm) {
      mm.addMessageListener("debug:storage-cookie-request-parent",
                            cookieHelpers.handleChildRequest);
    }
  }

  return {
    onBrowserSwap: setMessageManager,
    onDisconnected: () => {
      // Although "disconnected-from-child" implies that the child is already
      // disconnected this is not the case. The disconnection takes place after
      // this method has finished. This gives us chance to clean up items within
      // the parent process e.g. observers.
      cookieHelpers.removeCookieObservers();
      setMessageManager(null);
    }
  };
};

/**
 * Helper method to create the overriden object required in
 * StorageActors.createActor for Local Storage and Session Storage.
 * This method exists as both Local Storage and Session Storage have almost
 * identical actors.
 */
function getObjectForLocalOrSessionStorage(type) {
  return {
    getNamesForHost(host) {
      let storage = this.hostVsStores.get(host);
      return storage ? Object.keys(storage) : [];
    },

    getValuesForHost(host, name) {
      let storage = this.hostVsStores.get(host);
      if (!storage) {
        return [];
      }
      if (name) {
        let value = storage ? storage.getItem(name) : null;
        return [{ name, value }];
      }
      if (!storage) {
        return [];
      }
      return Object.keys(storage).map(key => ({
        name: key,
        value: storage.getItem(key)
      }));
    },

    populateStoresForHost(host, window) {
      try {
        this.hostVsStores.set(host, window[type]);
      } catch (ex) {
        console.warn(`Failed to enumerate ${type} for host ${host}: ${ex}`);
      }
    },

    populateStoresForHosts() {
      this.hostVsStores = new Map();
      for (let window of this.windows) {
        let host = this.getHostName(window.location);
        if (host) {
          this.populateStoresForHost(host, window);
        }
      }
    },

    async getFields() {
      return [
        { name: "name", editable: true },
        { name: "value", editable: true }
      ];
    },

    async addItem(guid, host) {
      let storage = this.hostVsStores.get(host);
      if (!storage) {
        return;
      }
      storage.setItem(guid, DEFAULT_VALUE);
    },

    /**
     * Edit localStorage or sessionStorage fields.
     *
     * @param {Object} data
     *        See editCookie() for format details.
     */
    async editItem({host, field, oldValue, items}) {
      let storage = this.hostVsStores.get(host);
      if (!storage) {
        return;
      }

      if (field === "name") {
        storage.removeItem(oldValue);
      }

      storage.setItem(items.name, items.value);
    },

    async removeItem(host, name) {
      let storage = this.hostVsStores.get(host);
      if (!storage) {
        return;
      }
      storage.removeItem(name);
    },

    async removeAll(host) {
      let storage = this.hostVsStores.get(host);
      if (!storage) {
        return;
      }
      storage.clear();
    },

    observe(subject, topic, data) {
      if ((topic != "dom-storage2-changed" &&
           topic != "dom-private-storage2-changed") ||
          data != type) {
        return null;
      }

      let host = this.getSchemaAndHost(subject.url);

      if (!this.hostVsStores.has(host)) {
        return null;
      }

      let action = "changed";
      if (subject.key == null) {
        return this.storageActor.update("cleared", type, [host]);
      } else if (subject.oldValue == null) {
        action = "added";
      } else if (subject.newValue == null) {
        action = "deleted";
      }
      let updateData = {};
      updateData[host] = [subject.key];
      return this.storageActor.update(action, type, updateData);
    },

    /**
     * Given a url, correctly determine its protocol + hostname part.
     */
    getSchemaAndHost(url) {
      let uri = Services.io.newURI(url);
      if (!uri.host) {
        return uri.spec;
      }
      return uri.scheme + "://" + uri.hostPort;
    },

    toStoreObject(item) {
      if (!item) {
        return null;
      }

      return {
        name: item.name,
        value: new LongStringActor(this.conn, item.value || "")
      };
    },
  };
}

/**
 * The Local Storage actor and front.
 */
StorageActors.createActor({
  typeName: "localStorage",
  observationTopics: ["dom-storage2-changed", "dom-private-storage2-changed"]
}, getObjectForLocalOrSessionStorage("localStorage"));

/**
 * The Session Storage actor and front.
 */
StorageActors.createActor({
  typeName: "sessionStorage",
  observationTopics: ["dom-storage2-changed", "dom-private-storage2-changed"]
}, getObjectForLocalOrSessionStorage("sessionStorage"));

StorageActors.createActor({
  typeName: "Cache"
}, {
  async getCachesForHost(host) {
    let uri = Services.io.newURI(host);
    let attrs = this.storageActor
                    .document
                    .nodePrincipal
                    .originAttributes;
    let principal =
      Services.scriptSecurityManager.createCodebasePrincipal(uri, attrs);

    // The first argument tells if you want to get |content| cache or |chrome|
    // cache.
    // The |content| cache is the cache explicitely named by the web content
    // (service worker or web page).
    // The |chrome| cache is the cache implicitely cached by the platform,
    // hosting the source file of the service worker.
    let { CacheStorage } = this.storageActor.window;

    if (!CacheStorage) {
      return [];
    }

    let cache = new CacheStorage("content", principal);
    return cache;
  },

  async preListStores() {
    for (let host of this.hosts) {
      await this.populateStoresForHost(host);
    }
  },

  form(form, detail) {
    if (detail === "actorid") {
      return this.actorID;
    }

    let hosts = {};
    for (let host of this.hosts) {
      hosts[host] = this.getNamesForHost(host);
    }

    return {
      actor: this.actorID,
      hosts: hosts
    };
  },

  getNamesForHost(host) {
    // UI code expect each name to be a JSON string of an array :/
    return [...this.hostVsStores.get(host).keys()].map(a => {
      return JSON.stringify([a]);
    });
  },

  async getValuesForHost(host, name) {
    if (!name) {
      return [];
    }
    // UI is weird and expect a JSON stringified array... and pass it back :/
    name = JSON.parse(name)[0];

    let cache = this.hostVsStores.get(host).get(name);
    let requests = await cache.keys();
    let results = [];
    for (let request of requests) {
      let response = await cache.match(request);
      // Unwrap the response to get access to all its properties if the
      // response happen to be 'opaque', when it is a Cross Origin Request.
      response = response.cloneUnfiltered();
      results.push(await this.processEntry(request, response));
    }
    return results;
  },

  async processEntry(request, response) {
    return {
      url: String(request.url),
      status: String(response.statusText),
    };
  },

  async getFields() {
    return [
      { name: "url", editable: false },
      { name: "status", editable: false }
    ];
  },

  async populateStoresForHost(host) {
    let storeMap = new Map();
    let caches = await this.getCachesForHost(host);
    try {
      for (let name of (await caches.keys())) {
        storeMap.set(name, (await caches.open(name)));
      }
    } catch (ex) {
      console.warn(`Failed to enumerate CacheStorage for host ${host}: ${ex}`);
    }
    this.hostVsStores.set(host, storeMap);
  },

  /**
   * This method is overriden and left blank as for Cache Storage, this
   * operation cannot be performed synchronously. Thus, the preListStores
   * method exists to do the same task asynchronously.
   */
  populateStoresForHosts() {
    this.hostVsStores = new Map();
  },

  /**
   * Given a url, correctly determine its protocol + hostname part.
   */
  getSchemaAndHost(url) {
    let uri = Services.io.newURI(url);
    return uri.scheme + "://" + uri.hostPort;
  },

  toStoreObject(item) {
    return item;
  },

  async removeItem(host, name) {
    const cacheMap = this.hostVsStores.get(host);
    if (!cacheMap) {
      return;
    }

    const parsedName = JSON.parse(name);

    if (parsedName.length == 1) {
      // Delete the whole Cache object
      const [ cacheName ] = parsedName;
      cacheMap.delete(cacheName);
      const cacheStorage = await this.getCachesForHost(host);
      await cacheStorage.delete(cacheName);
      this.onItemUpdated("deleted", host, [ cacheName ]);
    } else if (parsedName.length == 2) {
      // Delete one cached request
      const [ cacheName, url ] = parsedName;
      const cache = cacheMap.get(cacheName);
      if (cache) {
        await cache.delete(url);
        this.onItemUpdated("deleted", host, [ cacheName, url ]);
      }
    }
  },

  async removeAll(host, name) {
    const cacheMap = this.hostVsStores.get(host);
    if (!cacheMap) {
      return;
    }

    const parsedName = JSON.parse(name);

    // Only a Cache object is a valid object to clear
    if (parsedName.length == 1) {
      const [ cacheName ] = parsedName;
      const cache = cacheMap.get(cacheName);
      if (cache) {
        let keys = await cache.keys();
        await Promise.all(keys.map(key => cache.delete(key)));
        this.onItemUpdated("cleared", host, [ cacheName ]);
      }
    }
  },

  /**
   * CacheStorage API doesn't support any notifications, we must fake them
   */
  onItemUpdated(action, host, path) {
    this.storageActor.update(action, "Cache", {
      [host]: [ JSON.stringify(path) ]
    });
  },
});

/**
 * Code related to the Indexed DB actor and front
 */

// Metadata holder objects for various components of Indexed DB

/**
 * Meta data object for a particular index in an object store
 *
 * @param {IDBIndex} index
 *        The particular index from the object store.
 */
function IndexMetadata(index) {
  this._name = index.name;
  this._keyPath = index.keyPath;
  this._unique = index.unique;
  this._multiEntry = index.multiEntry;
}
IndexMetadata.prototype = {
  toObject() {
    return {
      name: this._name,
      keyPath: this._keyPath,
      unique: this._unique,
      multiEntry: this._multiEntry
    };
  }
};

/**
 * Meta data object for a particular object store in a db
 *
 * @param {IDBObjectStore} objectStore
 *        The particular object store from the db.
 */
function ObjectStoreMetadata(objectStore) {
  this._name = objectStore.name;
  this._keyPath = objectStore.keyPath;
  this._autoIncrement = objectStore.autoIncrement;
  this._indexes = [];

  for (let i = 0; i < objectStore.indexNames.length; i++) {
    let index = objectStore.index(objectStore.indexNames[i]);

    let newIndex = {
      keypath: index.keyPath,
      multiEntry: index.multiEntry,
      name: index.name,
      objectStore: {
        autoIncrement: index.objectStore.autoIncrement,
        indexNames: [...index.objectStore.indexNames],
        keyPath: index.objectStore.keyPath,
        name: index.objectStore.name,
      }
    };

    this._indexes.push([newIndex, new IndexMetadata(index)]);
  }
}
ObjectStoreMetadata.prototype = {
  toObject() {
    return {
      name: this._name,
      keyPath: this._keyPath,
      autoIncrement: this._autoIncrement,
      indexes: JSON.stringify(
        [...this._indexes.values()].map(index => index.toObject())
      )
    };
  }
};

/**
 * Meta data object for a particular indexed db in a host.
 *
 * @param {string} origin
 *        The host associated with this indexed db.
 * @param {IDBDatabase} db
 *        The particular indexed db.
 * @param {String} storage
 *        Storage type, either "temporary", "default" or "persistent".
 */
function DatabaseMetadata(origin, db, storage) {
  this._origin = origin;
  this._name = db.name;
  this._version = db.version;
  this._objectStores = [];
  this.storage = storage;

  if (db.objectStoreNames.length) {
    let transaction = db.transaction(db.objectStoreNames, "readonly");

    for (let i = 0; i < transaction.objectStoreNames.length; i++) {
      let objectStore =
        transaction.objectStore(transaction.objectStoreNames[i]);
      this._objectStores.push([transaction.objectStoreNames[i],
                               new ObjectStoreMetadata(objectStore)]);
    }
  }
}
DatabaseMetadata.prototype = {
  get objectStores() {
    return this._objectStores;
  },

  toObject() {
    return {
      uniqueKey: `${this._name}${SEPARATOR_GUID}${this.storage}`,
      name: this._name,
      storage: this.storage,
      origin: this._origin,
      version: this._version,
      objectStores: this._objectStores.size
    };
  }
};

StorageActors.createActor({
  typeName: "indexedDB"
}, {
  initialize(storageActor) {
    protocol.Actor.prototype.initialize.call(this, null);

    this.storageActor = storageActor;

    this.maybeSetupChildProcess();

    this.objectsSize = {};
    this.storageActor = storageActor;
    this.onWindowReady = this.onWindowReady.bind(this);
    this.onWindowDestroyed = this.onWindowDestroyed.bind(this);

    this.storageActor.on("window-ready", this.onWindowReady);
    this.storageActor.on("window-destroyed", this.onWindowDestroyed);
  },

  destroy() {
    this.hostVsStores.clear();
    this.objectsSize = null;

    this.storageActor.off("window-ready", this.onWindowReady);
    this.storageActor.off("window-destroyed", this.onWindowDestroyed);

    protocol.Actor.prototype.destroy.call(this);

    this.storageActor = null;
  },

  /**
   * Remove an indexedDB database from given host with a given name.
   */
  async removeDatabase(host, name) {
    let win = this.storageActor.getWindowFromHost(host);
    if (!win) {
      return { error: `Window for host ${host} not found` };
    }

    let principal = win.document.nodePrincipal;
    return this.removeDB(host, principal, name);
  },

  async removeAll(host, name) {
    let [db, store] = JSON.parse(name);

    let win = this.storageActor.getWindowFromHost(host);
    if (!win) {
      return;
    }

    let principal = win.document.nodePrincipal;
    this.clearDBStore(host, principal, db, store);
  },

  async removeItem(host, name) {
    let [db, store, id] = JSON.parse(name);

    let win = this.storageActor.getWindowFromHost(host);
    if (!win) {
      return;
    }

    let principal = win.document.nodePrincipal;
    this.removeDBRecord(host, principal, db, store, id);
  },

  /**
   * This method is overriden and left blank as for indexedDB, this operation
   * cannot be performed synchronously. Thus, the preListStores method exists to
   * do the same task asynchronously.
   */
  populateStoresForHosts() {},

  getNamesForHost(host) {
    let names = [];

    for (let [dbName, {objectStores}] of this.hostVsStores.get(host)) {
      if (objectStores.size) {
        for (let objectStore of objectStores.keys()) {
          names.push(JSON.stringify([dbName, objectStore]));
        }
      } else {
        names.push(JSON.stringify([dbName]));
      }
    }
    return names;
  },

  /**
   * Returns the total number of entries for various types of requests to
   * getStoreObjects for Indexed DB actor.
   *
   * @param {string} host
   *        The host for the request.
   * @param {array:string} names
   *        Array of stringified name objects for indexed db actor.
   *        The request type depends on the length of any parsed entry from this
   *        array. 0 length refers to request for the whole host. 1 length
   *        refers to request for a particular db in the host. 2 length refers
   *        to a particular object store in a db in a host. 3 length refers to
   *        particular items of an object store in a db in a host.
   * @param {object} options
   *        An options object containing following properties:
   *         - index {string} The IDBIndex for the object store in the db.
   */
  getObjectsSize(host, names, options) {
    // In Indexed DB, we are interested in only the first name, as the pattern
    // should follow in all entries.
    let name = names[0];
    let parsedName = JSON.parse(name);

    if (parsedName.length == 3) {
      // This is the case where specific entries from an object store were
      // requested
      return names.length;
    } else if (parsedName.length == 2) {
      // This is the case where all entries from an object store are requested.
      let index = options.index;
      let [db, objectStore] = parsedName;
      if (this.objectsSize[host + db + objectStore + index]) {
        return this.objectsSize[host + db + objectStore + index];
      }
    } else if (parsedName.length == 1) {
      // This is the case where details of all object stores in a db are
      // requested.
      if (this.hostVsStores.has(host) &&
          this.hostVsStores.get(host).has(parsedName[0])) {
        return this.hostVsStores.get(host).get(parsedName[0]).objectStores.size;
      }
    } else if (!parsedName || !parsedName.length) {
      // This is the case were details of all dbs in a host are requested.
      if (this.hostVsStores.has(host)) {
        return this.hostVsStores.get(host).size;
      }
    }
    return 0;
  },

  /**
   * Purpose of this method is same as populateStoresForHosts but this is async.
   * This exact same operation cannot be performed in populateStoresForHosts
   * method, as that method is called in initialize method of the actor, which
   * cannot be asynchronous.
   */
  async preListStores() {
    this.hostVsStores = new Map();

    for (let host of this.hosts) {
      await this.populateStoresForHost(host);
    }
  },

  async populateStoresForHost(host) {
    let storeMap = new Map();

    let win = this.storageActor.getWindowFromHost(host);
    if (win) {
      let principal = win.document.nodePrincipal;
      let {names} = await this.getDBNamesForHost(host, principal);

      for (let {name, storage} of names) {
        let metadata = await this.getDBMetaData(host, principal, name, storage);

        metadata = indexedDBHelpers.patchMetadataMapsAndProtos(metadata);

        storeMap.set(`${name} (${storage})`, metadata);
      }
    }

    this.hostVsStores.set(host, storeMap);
  },

  /**
   * Returns the over-the-wire implementation of the indexed db entity.
   */
  toStoreObject(item) {
    if (!item) {
      return null;
    }

    if ("indexes" in item) {
      // Object store meta data
      return {
        objectStore: item.name,
        keyPath: item.keyPath,
        autoIncrement: item.autoIncrement,
        indexes: item.indexes
      };
    }
    if ("objectStores" in item) {
      // DB meta data
      return {
        uniqueKey: `${item.name} (${item.storage})`,
        db: item.name,
        storage: item.storage,
        origin: item.origin,
        version: item.version,
        objectStores: item.objectStores
      };
    }

    let value = JSON.stringify(item.value);

    // FIXME: Bug 1318029 - Due to a bug that is thrown whenever a
    // LongStringActor string reaches DebuggerServer.LONG_STRING_LENGTH we need
    // to trim the value. When the bug is fixed we should stop trimming the
    // string here.
    let maxLength = DebuggerServer.LONG_STRING_LENGTH - 1;
    if (value.length > maxLength) {
      value = value.substr(0, maxLength);
    }

    // Indexed db entry
    return {
      name: item.name,
      value: new LongStringActor(this.conn, value)
    };
  },

  form(form, detail) {
    if (detail === "actorid") {
      return this.actorID;
    }

    let hosts = {};
    for (let host of this.hosts) {
      hosts[host] = this.getNamesForHost(host);
    }

    return {
      actor: this.actorID,
      hosts: hosts
    };
  },

  onItemUpdated(action, host, path) {
    // Database was removed, remove it from stores map
    if (action === "deleted" && path.length === 1) {
      if (this.hostVsStores.has(host)) {
        this.hostVsStores.get(host).delete(path[0]);
      }
    }

    this.storageActor.update(action, "indexedDB", {
      [host]: [ JSON.stringify(path) ]
    });
  },

  maybeSetupChildProcess() {
    if (!DebuggerServer.isInChildProcess) {
      this.backToChild = (func, rv) => rv;
      this.clearDBStore = indexedDBHelpers.clearDBStore;
      this.findIDBPathsForHost = indexedDBHelpers.findIDBPathsForHost;
      this.findSqlitePathsForHost = indexedDBHelpers.findSqlitePathsForHost;
      this.findStorageTypePaths = indexedDBHelpers.findStorageTypePaths;
      this.getDBMetaData = indexedDBHelpers.getDBMetaData;
      this.getDBNamesForHost = indexedDBHelpers.getDBNamesForHost;
      this.getNameFromDatabaseFile = indexedDBHelpers.getNameFromDatabaseFile;
      this.getObjectStoreData = indexedDBHelpers.getObjectStoreData;
      this.getSanitizedHost = indexedDBHelpers.getSanitizedHost;
      this.getValuesForHost = indexedDBHelpers.getValuesForHost;
      this.openWithPrincipal = indexedDBHelpers.openWithPrincipal;
      this.removeDB = indexedDBHelpers.removeDB;
      this.removeDBRecord = indexedDBHelpers.removeDBRecord;
      this.splitNameAndStorage = indexedDBHelpers.splitNameAndStorage;
      return;
    }

    const { sendAsyncMessage, addMessageListener } =
      this.conn.parentMessageManager;

    this.conn.setupInParent({
      module: "devtools/server/actors/storage",
      setupParent: "setupParentProcessForIndexedDB"
    });

    this.getDBMetaData = callParentProcessAsync.bind(null, "getDBMetaData");
    this.splitNameAndStorage = callParentProcessAsync.bind(null, "splitNameAndStorage");
    this.getDBNamesForHost = callParentProcessAsync.bind(null, "getDBNamesForHost");
    this.getValuesForHost = callParentProcessAsync.bind(null, "getValuesForHost");
    this.removeDB = callParentProcessAsync.bind(null, "removeDB");
    this.removeDBRecord = callParentProcessAsync.bind(null, "removeDBRecord");
    this.clearDBStore = callParentProcessAsync.bind(null, "clearDBStore");

    addMessageListener("debug:storage-indexedDB-request-child", msg => {
      switch (msg.json.method) {
        case "backToChild": {
          let [func, rv] = msg.json.args;
          let deferred = unresolvedPromises.get(func);
          if (deferred) {
            unresolvedPromises.delete(func);
            deferred.resolve(rv);
          }
          break;
        }
        case "onItemUpdated": {
          let [action, host, path] = msg.json.args;
          this.onItemUpdated(action, host, path);
        }
      }
    });

    let unresolvedPromises = new Map();
    function callParentProcessAsync(methodName, ...args) {
      let deferred = defer();

      unresolvedPromises.set(methodName, deferred);

      sendAsyncMessage("debug:storage-indexedDB-request-parent", {
        method: methodName,
        args: args
      });

      return deferred.promise;
    }
  },

  async getFields(subType) {
    switch (subType) {
      // Detail of database
      case "database":
        return [
          { name: "objectStore", editable: false },
          { name: "keyPath", editable: false },
          { name: "autoIncrement", editable: false },
          { name: "indexes", editable: false },
        ];

      // Detail of object store
      case "object store":
        return [
          { name: "name", editable: false },
          { name: "value", editable: false }
        ];

      // Detail of indexedDB for one origin
      default:
        return [
          { name: "uniqueKey", editable: false, private: true },
          { name: "db", editable: false },
          { name: "storage", editable: false },
          { name: "origin", editable: false },
          { name: "version", editable: false },
          { name: "objectStores", editable: false },
        ];
    }
  }
});

var indexedDBHelpers = {
  backToChild(...args) {
    let mm = Cc["@mozilla.org/globalmessagemanager;1"]
               .getService(Ci.nsIMessageListenerManager);

    mm.broadcastAsyncMessage("debug:storage-indexedDB-request-child", {
      method: "backToChild",
      args: args
    });
  },

  onItemUpdated(action, host, path) {
    let mm = Cc["@mozilla.org/globalmessagemanager;1"]
               .getService(Ci.nsIMessageListenerManager);

    mm.broadcastAsyncMessage("debug:storage-indexedDB-request-child", {
      method: "onItemUpdated",
      args: [ action, host, path ]
    });
  },

  /**
   * Fetches and stores all the metadata information for the given database
   * `name` for the given `host` with its `principal`. The stored metadata
   * information is of `DatabaseMetadata` type.
   */
  async getDBMetaData(host, principal, name, storage) {
    let request = this.openWithPrincipal(principal, name, storage);
    let success = defer();

    request.onsuccess = event => {
      let db = event.target.result;
      let dbData = new DatabaseMetadata(host, db, storage);
      db.close();

      success.resolve(this.backToChild("getDBMetaData", dbData));
    };
    request.onerror = ({target}) => {
      console.error(
        `Error opening indexeddb database ${name} for host ${host}`, target.error);
      success.resolve(this.backToChild("getDBMetaData", null));
    };
    return success.promise;
  },

  splitNameAndStorage: function (name) {
    let lastOpenBracketIndex = name.lastIndexOf("(");
    let lastCloseBracketIndex = name.lastIndexOf(")");
    let delta = lastCloseBracketIndex - lastOpenBracketIndex - 1;

    let storage = name.substr(lastOpenBracketIndex + 1, delta);

    name = name.substr(0, lastOpenBracketIndex - 1);

    return { storage, name };
  },

  /**
   * Opens an indexed db connection for the given `principal` and
   * database `name`.
   */
  openWithPrincipal: function (principal, name, storage) {
    return indexedDBForStorage.openForPrincipal(principal, name,
                                                { storage: storage });
  },

  async removeDB(host, principal, dbName) {
    let result = new Promise(resolve => {
      let {name, storage} = this.splitNameAndStorage(dbName);
      let request =
        indexedDBForStorage.deleteForPrincipal(principal, name,
                                               { storage: storage });

      request.onsuccess = () => {
        resolve({});
        this.onItemUpdated("deleted", host, [dbName]);
      };

      request.onblocked = () => {
        console.warn(`Deleting indexedDB database ${name} for host ${host} is blocked`);
        resolve({ blocked: true });
      };

      request.onerror = () => {
        let { error } = request;
        console.warn(
          `Error deleting indexedDB database ${name} for host ${host}: ${error}`);
        resolve({ error: error.message });
      };

      // If the database is blocked repeatedly, the onblocked event will not
      // be fired again. To avoid waiting forever, report as blocked if nothing
      // else happens after 3 seconds.
      setTimeout(() => resolve({ blocked: true }), 3000);
    });

    return this.backToChild("removeDB", await result);
  },

  async removeDBRecord(host, principal, dbName, storeName, id) {
    let db;
    let {name, storage} = this.splitNameAndStorage(dbName);

    try {
      db = await new Promise((resolve, reject) => {
        let request = this.openWithPrincipal(principal, name, storage);
        request.onsuccess = ev => resolve(ev.target.result);
        request.onerror = ev => reject(ev.target.error);
      });

      let transaction = db.transaction(storeName, "readwrite");
      let store = transaction.objectStore(storeName);

      await new Promise((resolve, reject) => {
        let request = store.delete(id);
        request.onsuccess = () => resolve();
        request.onerror = ev => reject(ev.target.error);
      });

      this.onItemUpdated("deleted", host, [dbName, storeName, id]);
    } catch (error) {
      let recordPath = [dbName, storeName, id].join("/");
      console.error(`Failed to delete indexedDB record: ${recordPath}: ${error}`);
    }

    if (db) {
      db.close();
    }

    return this.backToChild("removeDBRecord", null);
  },

  async clearDBStore(host, principal, dbName, storeName) {
    let db;
    let {name, storage} = this.splitNameAndStorage(dbName);

    try {
      db = await new Promise((resolve, reject) => {
        let request = this.openWithPrincipal(principal, name, storage);
        request.onsuccess = ev => resolve(ev.target.result);
        request.onerror = ev => reject(ev.target.error);
      });

      let transaction = db.transaction(storeName, "readwrite");
      let store = transaction.objectStore(storeName);

      await new Promise((resolve, reject) => {
        let request = store.clear();
        request.onsuccess = () => resolve();
        request.onerror = ev => reject(ev.target.error);
      });

      this.onItemUpdated("cleared", host, [dbName, storeName]);
    } catch (error) {
      let storePath = [dbName, storeName].join("/");
      console.error(`Failed to clear indexedDB store: ${storePath}: ${error}`);
    }

    if (db) {
      db.close();
    }

    return this.backToChild("clearDBStore", null);
  },

  /**
   * Fetches all the databases and their metadata for the given `host`.
   */
  async getDBNamesForHost(host, principal) {
    let sanitizedHost = this.getSanitizedHost(host) + principal.originSuffix;
    let profileDir = OS.Constants.Path.profileDir;
    let files = [];
    let names = [];
    let storagePath = OS.Path.join(profileDir, "storage");

    // We expect sqlite DB paths to look something like this:
    // - PathToProfileDir/storage/default/http+++www.example.com/
    //   idb/1556056096MeysDaabta.sqlite
    // - PathToProfileDir/storage/permanent/http+++www.example.com/
    //   idb/1556056096MeysDaabta.sqlite
    // - PathToProfileDir/storage/temporary/http+++www.example.com/
    //   idb/1556056096MeysDaabta.sqlite
    // The subdirectory inside the storage folder is determined by the storage
    // type:
    // - default:   { storage: "default" } or not specified.
    // - permanent: { storage: "persistent" }.
    // - temporary: { storage: "temporary" }.
    let sqliteFiles = await this.findSqlitePathsForHost(storagePath, sanitizedHost);

    for (let file of sqliteFiles) {
      let splitPath = OS.Path.split(file).components;
      let idbIndex = splitPath.indexOf("idb");
      let storage = splitPath[idbIndex - 2];
      let relative = file.substr(profileDir.length + 1);

      files.push({
        file: relative,
        storage: storage === "permanent" ? "persistent" : storage
      });
    }

    if (files.length > 0) {
      for (let {file, storage} of files) {
        let name = await this.getNameFromDatabaseFile(file);
        if (name) {
          names.push({
            name,
            storage
          });
        }
      }
    }

    return this.backToChild("getDBNamesForHost", {names});
  },

  /**
   * Find all SQLite files that hold IndexedDB data for a host, such as:
   *   storage/temporary/http+++www.example.com/idb/1556056096MeysDaabta.sqlite
   */
  async findSqlitePathsForHost(storagePath, sanitizedHost) {
    let sqlitePaths = [];
    let idbPaths = await this.findIDBPathsForHost(storagePath, sanitizedHost);
    for (let idbPath of idbPaths) {
      let iterator = new OS.File.DirectoryIterator(idbPath);
      await iterator.forEach(entry => {
        if (!entry.isDir && entry.path.endsWith(".sqlite")) {
          sqlitePaths.push(entry.path);
        }
      });
      iterator.close();
    }
    return sqlitePaths;
  },

  /**
   * Find all paths that hold IndexedDB data for a host, such as:
   *   storage/temporary/http+++www.example.com/idb
   */
  async findIDBPathsForHost(storagePath, sanitizedHost) {
    let idbPaths = [];
    let typePaths = await this.findStorageTypePaths(storagePath);
    for (let typePath of typePaths) {
      let idbPath = OS.Path.join(typePath, sanitizedHost, "idb");
      if (await OS.File.exists(idbPath)) {
        idbPaths.push(idbPath);
      }
    }
    return idbPaths;
  },

  /**
   * Find all the storage types, such as "default", "permanent", or "temporary".
   * These names have changed over time, so it seems simpler to look through all types
   * that currently exist in the profile.
   */
  async findStorageTypePaths(storagePath) {
    let iterator = new OS.File.DirectoryIterator(storagePath);
    let typePaths = [];
    await iterator.forEach(entry => {
      if (entry.isDir) {
        typePaths.push(entry.path);
      }
    });
    iterator.close();
    return typePaths;
  },

  /**
   * Removes any illegal characters from the host name to make it a valid file
   * name.
   */
  getSanitizedHost(host) {
    if (host.startsWith("about:")) {
      host = "moz-safe-" + host;
    }
    return host.replace(ILLEGAL_CHAR_REGEX, "+");
  },

  /**
   * Retrieves the proper indexed db database name from the provided .sqlite
   * file location.
   */
  async getNameFromDatabaseFile(path) {
    let connection = null;
    let retryCount = 0;

    // Content pages might be having an open transaction for the same indexed db
    // which this sqlite file belongs to. In that case, sqlite.openConnection
    // will throw. Thus we retry for some time to see if lock is removed.
    while (!connection && retryCount++ < 25) {
      try {
        connection = await Sqlite.openConnection({ path: path });
      } catch (ex) {
        // Continuously retrying is overkill. Waiting for 100ms before next try
        await sleep(100);
      }
    }

    if (!connection) {
      return null;
    }

    let rows = await connection.execute("SELECT name FROM database");
    if (rows.length != 1) {
      return null;
    }

    let name = rows[0].getResultByName("name");

    await connection.close();

    return name;
  },

  async getValuesForHost(host, name = "null", options,
                                          hostVsStores, principal) {
    name = JSON.parse(name);
    if (!name || !name.length) {
      // This means that details about the db in this particular host are
      // requested.
      let dbs = [];
      if (hostVsStores.has(host)) {
        for (let [, db] of hostVsStores.get(host)) {
          db = indexedDBHelpers.patchMetadataMapsAndProtos(db);
          dbs.push(db.toObject());
        }
      }
      return this.backToChild("getValuesForHost", {dbs: dbs});
    }

    let [db2, objectStore, id] = name;
    if (!objectStore) {
      // This means that details about all the object stores in this db are
      // requested.
      let objectStores = [];
      if (hostVsStores.has(host) && hostVsStores.get(host).has(db2)) {
        let db = hostVsStores.get(host).get(db2);

        db = indexedDBHelpers.patchMetadataMapsAndProtos(db);

        let objectStores2 = db.objectStores;

        for (let objectStore2 of objectStores2) {
          objectStores.push(objectStore2[1].toObject());
        }
      }
      return this.backToChild("getValuesForHost", {objectStores: objectStores});
    }
    // Get either all entries from the object store, or a particular id
    let storage = hostVsStores.get(host).get(db2).storage;
    let result = await this.getObjectStoreData(host, principal, db2, storage, {
      objectStore: objectStore,
      id: id,
      index: options.index,
      offset: 0,
      size: options.size
    });
    return this.backToChild("getValuesForHost", {result: result});
  },

  /**
   * Returns all or requested entries from a particular objectStore from the db
   * in the given host.
   *
   * @param {string} host
   *        The given host.
   * @param {nsIPrincipal} principal
   *        The principal of the given document.
   * @param {string} dbName
   *        The name of the indexed db from the above host.
   * @param {String} storage
   *        Storage type, either "temporary", "default" or "persistent".
   * @param {Object} requestOptions
   *        An object in the following format:
   *        {
   *          objectStore: The name of the object store from the above db,
   *          id:          Id of the requested entry from the above object
   *                       store. null if all entries from the above object
   *                       store are requested,
   *          index:       Name of the IDBIndex to be iterated on while fetching
   *                       entries. null or "name" if no index is to be
   *                       iterated,
   *          offset:      offset of the entries to be fetched,
   *          size:        The intended size of the entries to be fetched
   *        }
   */
  getObjectStoreData(host, principal, dbName, storage, requestOptions) {
    let {name} = this.splitNameAndStorage(dbName);
    let request = this.openWithPrincipal(principal, name, storage);
    let success = defer();
    let {objectStore, id, index, offset, size} = requestOptions;
    let data = [];
    let db;

    if (!size || size > MAX_STORE_OBJECT_COUNT) {
      size = MAX_STORE_OBJECT_COUNT;
    }

    request.onsuccess = event => {
      db = event.target.result;

      let transaction = db.transaction(objectStore, "readonly");
      let source = transaction.objectStore(objectStore);
      if (index && index != "name") {
        source = source.index(index);
      }

      source.count().onsuccess = event2 => {
        let objectsSize = [];
        let count = event2.target.result;
        objectsSize.push({
          key: host + dbName + objectStore + index,
          count: count
        });

        if (!offset) {
          offset = 0;
        } else if (offset > count) {
          db.close();
          success.resolve([]);
          return;
        }

        if (id) {
          source.get(id).onsuccess = event3 => {
            db.close();
            success.resolve([{name: id, value: event3.target.result}]);
          };
        } else {
          source.openCursor().onsuccess = event4 => {
            let cursor = event4.target.result;

            if (!cursor || data.length >= size) {
              db.close();
              success.resolve({
                data: data,
                objectsSize: objectsSize
              });
              return;
            }
            if (offset-- <= 0) {
              data.push({name: cursor.key, value: cursor.value});
            }
            cursor.continue();
          };
        }
      };
    };
    request.onerror = () => {
      db.close();
      success.resolve([]);
    };
    return success.promise;
  },

  /**
   * When indexedDB metadata is parsed to and from JSON then the object's
   * prototype is dropped and any Maps are changed to arrays of arrays. This
   * method is used to repair the prototypes and fix any broken Maps.
   */
  patchMetadataMapsAndProtos(metadata) {
    let md = Object.create(DatabaseMetadata.prototype);
    Object.assign(md, metadata);

    md._objectStores = new Map(metadata._objectStores);

    for (let [name, store] of md._objectStores) {
      let obj = Object.create(ObjectStoreMetadata.prototype);
      Object.assign(obj, store);

      md._objectStores.set(name, obj);

      if (typeof store._indexes.length !== "undefined") {
        obj._indexes = new Map(store._indexes);
      }

      for (let [name2, value] of obj._indexes) {
        let obj2 = Object.create(IndexMetadata.prototype);
        Object.assign(obj2, value);

        obj._indexes.set(name2, obj2);
      }
    }

    return md;
  },

  handleChildRequest(msg) {
    let args = msg.data.args;

    switch (msg.json.method) {
      case "getDBMetaData": {
        let [host, principal, name, storage] = args;
        return indexedDBHelpers.getDBMetaData(host, principal, name, storage);
      }
      case "splitNameAndStorage": {
        let [name] = args;
        return indexedDBHelpers.splitNameAndStorage(name);
      }
      case "getDBNamesForHost": {
        let [host, principal] = args;
        return indexedDBHelpers.getDBNamesForHost(host, principal);
      }
      case "getValuesForHost": {
        let [host, name, options, hostVsStores, principal] = args;
        return indexedDBHelpers.getValuesForHost(host, name, options,
                                                 hostVsStores, principal);
      }
      case "removeDB": {
        let [host, principal, dbName] = args;
        return indexedDBHelpers.removeDB(host, principal, dbName);
      }
      case "removeDBRecord": {
        let [host, principal, db, store, id] = args;
        return indexedDBHelpers.removeDBRecord(host, principal, db, store, id);
      }
      case "clearDBStore": {
        let [host, principal, db, store] = args;
        return indexedDBHelpers.clearDBStore(host, principal, db, store);
      }
      default:
        console.error("ERR_DIRECTOR_PARENT_UNKNOWN_METHOD", msg.json.method);
        throw new Error("ERR_DIRECTOR_PARENT_UNKNOWN_METHOD");
    }
  }
};

/**
 * E10S parent/child setup helpers
 */

exports.setupParentProcessForIndexedDB = function ({ mm, prefix }) {
  // listen for director-script requests from the child process
  setMessageManager(mm);

  function setMessageManager(newMM) {
    if (mm) {
      mm.removeMessageListener("debug:storage-indexedDB-request-parent",
                               indexedDBHelpers.handleChildRequest);
    }
    mm = newMM;
    if (mm) {
      mm.addMessageListener("debug:storage-indexedDB-request-parent",
                            indexedDBHelpers.handleChildRequest);
    }
  }

  return {
    onBrowserSwap: setMessageManager,
    onDisconnected: () => setMessageManager(null),
  };
};

/**
 * General helpers
 */
function trimHttpHttpsPort(url) {
  let match = url.match(/(.+):\d+$/);

  if (match) {
    url = match[1];
  }
  if (url.startsWith("http://")) {
    return url.substr(7);
  }
  if (url.startsWith("https://")) {
    return url.substr(8);
  }
  return url;
}

/**
 * The main Storage Actor.
 */
let StorageActor = protocol.ActorClassWithSpec(specs.storageSpec, {
  typeName: "storage",

  get window() {
    return this.parentActor.window;
  },

  get document() {
    return this.parentActor.window.document;
  },

  get windows() {
    return this.childWindowPool;
  },

  initialize(conn, tabActor) {
    protocol.Actor.prototype.initialize.call(this, conn);

    this.parentActor = tabActor;

    this.childActorPool = new Map();
    this.childWindowPool = new Set();

    // Fetch all the inner iframe windows in this tab.
    this.fetchChildWindows(this.parentActor.docShell);

    // Initialize the registered store types
    for (let [store, ActorConstructor] of storageTypePool) {
      this.childActorPool.set(store, new ActorConstructor(this));
    }

    // Notifications that help us keep track of newly added windows and windows
    // that got removed
    Services.obs.addObserver(this, "content-document-global-created");
    Services.obs.addObserver(this, "inner-window-destroyed");
    this.onPageChange = this.onPageChange.bind(this);

    let handler = tabActor.chromeEventHandler;
    handler.addEventListener("pageshow", this.onPageChange, true);
    handler.addEventListener("pagehide", this.onPageChange, true);

    this.destroyed = false;
    this.boundUpdate = {};
  },

  destroy() {
    clearTimeout(this.batchTimer);
    this.batchTimer = null;
    // Remove observers
    Services.obs.removeObserver(this, "content-document-global-created");
    Services.obs.removeObserver(this, "inner-window-destroyed");
    this.destroyed = true;
    if (this.parentActor.browser) {
      this.parentActor.browser.removeEventListener("pageshow", this.onPageChange, true);
      this.parentActor.browser.removeEventListener("pagehide", this.onPageChange, true);
    }
    // Destroy the registered store types
    for (let actor of this.childActorPool.values()) {
      actor.destroy();
    }
    this.childActorPool.clear();
    this.childWindowPool.clear();

    this.childActorPool = null;
    this.childWindowPool = null;
    this.parentActor = null;
    this.boundUpdate = null;
    this.registeredPool = null;
    this._pendingResponse = null;

    protocol.Actor.prototype.destroy.call(this);
  },

  /**
   * Given a docshell, recursively find out all the child windows from it.
   *
   * @param {nsIDocShell} item
   *        The docshell from which all inner windows need to be extracted.
   */
  fetchChildWindows(item) {
    let docShell = item.QueryInterface(Ci.nsIDocShell)
                       .QueryInterface(Ci.nsIDocShellTreeItem);
    if (!docShell.contentViewer) {
      return null;
    }
    let window = docShell.contentViewer.DOMDocument.defaultView;
    if (window.location.href == "about:blank") {
      // Skip out about:blank windows as Gecko creates them multiple times while
      // creating any global.
      return null;
    }
    this.childWindowPool.add(window);
    for (let i = 0; i < docShell.childCount; i++) {
      let child = docShell.getChildAt(i);
      this.fetchChildWindows(child);
    }
    return null;
  },

  isIncludedInTopLevelWindow(window) {
    return isWindowIncluded(this.window, window);
  },

  getWindowFromInnerWindowID(innerID) {
    innerID = innerID.QueryInterface(Ci.nsISupportsPRUint64).data;
    for (let win of this.childWindowPool.values()) {
      let id = win.QueryInterface(Ci.nsIInterfaceRequestor)
                   .getInterface(Ci.nsIDOMWindowUtils)
                   .currentInnerWindowID;
      if (id == innerID) {
        return win;
      }
    }
    return null;
  },

  getWindowFromHost(host) {
    for (let win of this.childWindowPool.values()) {
      let origin = win.document
                      .nodePrincipal
                      .originNoSuffix;
      let url = win.document.URL;
      if (origin === host || url === host) {
        return win;
      }
    }
    return null;
  },

  /**
   * Event handler for any docshell update. This lets us figure out whenever
   * any new window is added, or an existing window is removed.
   */
  observe(subject, topic) {
    if (subject.location &&
        (!subject.location.href || subject.location.href == "about:blank")) {
      return null;
    }

    if (topic == "content-document-global-created" &&
        this.isIncludedInTopLevelWindow(subject)) {
      this.childWindowPool.add(subject);
      this.emit("window-ready", subject);
    } else if (topic == "inner-window-destroyed") {
      let window = this.getWindowFromInnerWindowID(subject);
      if (window) {
        this.childWindowPool.delete(window);
        this.emit("window-destroyed", window);
      }
    }
    return null;
  },

  /**
   * Called on "pageshow" or "pagehide" event on the chromeEventHandler of
   * current tab.
   *
   * @param {event} The event object passed to the handler. We are using these
   *        three properties from the event:
   *         - target {document} The document corresponding to the event.
   *         - type {string} Name of the event - "pageshow" or "pagehide".
   *         - persisted {boolean} true if there was no
   *                     "content-document-global-created" notification along
   *                     this event.
   */
  onPageChange({target, type, persisted}) {
    if (this.destroyed) {
      return;
    }

    let window = target.defaultView;

    if (type == "pagehide" && this.childWindowPool.delete(window)) {
      this.emit("window-destroyed", window);
    } else if (type == "pageshow" && persisted && window.location.href &&
               window.location.href != "about:blank" &&
               this.isIncludedInTopLevelWindow(window)) {
      this.childWindowPool.add(window);
      this.emit("window-ready", window);
    }
  },

  /**
   * Lists the available hosts for all the registered storage types.
   *
   * @returns {object} An object containing with the following structure:
   *  - <storageType> : [{
   *      actor: <actorId>,
   *      host: <hostname>
   *    }]
   */
  async listStores() {
    let toReturn = {};

    for (let [name, value] of this.childActorPool) {
      if (value.preListStores) {
        await value.preListStores();
      }
      toReturn[name] = value;
    }

    return toReturn;
  },

  /**
   * This method is called by the registered storage types so as to tell the
   * Storage Actor that there are some changes in the stores. Storage Actor then
   * notifies the client front about these changes at regular (BATCH_DELAY)
   * interval.
   *
   * @param {string} action
   *        The type of change. One of "added", "changed" or "deleted"
   * @param {string} storeType
   *        The storage actor in which this change has occurred.
   * @param {object} data
   *        The update object. This object is of the following format:
   *         - {
   *             <host1>: [<store_names1>, <store_name2>...],
   *             <host2>: [<store_names34>...],
   *           }
   *           Where host1, host2 are the host in which this change happened and
   *           [<store_namesX] is an array of the names of the changed store objects.
   *           Pass an empty array if the host itself was affected: either completely
   *           removed or cleared.
   */
  update(action, storeType, data) {
    if (action == "cleared") {
      this.emit("stores-cleared", { [storeType]: data });
      return null;
    }

    if (this.batchTimer) {
      clearTimeout(this.batchTimer);
    }
    if (!this.boundUpdate[action]) {
      this.boundUpdate[action] = {};
    }
    if (!this.boundUpdate[action][storeType]) {
      this.boundUpdate[action][storeType] = {};
    }
    for (let host in data) {
      if (!this.boundUpdate[action][storeType][host]) {
        this.boundUpdate[action][storeType][host] = [];
      }
      for (let name of data[host]) {
        if (!this.boundUpdate[action][storeType][host].includes(name)) {
          this.boundUpdate[action][storeType][host].push(name);
        }
      }
    }
    if (action == "added") {
      // If the same store name was previously deleted or changed, but now is
      // added somehow, dont send the deleted or changed update.
      this.removeNamesFromUpdateList("deleted", storeType, data);
      this.removeNamesFromUpdateList("changed", storeType, data);
    } else if (action == "changed" && this.boundUpdate.added &&
             this.boundUpdate.added[storeType]) {
      // If something got added and changed at the same time, then remove those
      // items from changed instead.
      this.removeNamesFromUpdateList("changed", storeType,
                                     this.boundUpdate.added[storeType]);
    } else if (action == "deleted") {
      // If any item got delete, or a host got delete, no point in sending
      // added or changed update
      this.removeNamesFromUpdateList("added", storeType, data);
      this.removeNamesFromUpdateList("changed", storeType, data);

      for (let host in data) {
        if (data[host].length == 0 && this.boundUpdate.added &&
            this.boundUpdate.added[storeType] &&
            this.boundUpdate.added[storeType][host]) {
          delete this.boundUpdate.added[storeType][host];
        }
        if (data[host].length == 0 && this.boundUpdate.changed &&
            this.boundUpdate.changed[storeType] &&
            this.boundUpdate.changed[storeType][host]) {
          delete this.boundUpdate.changed[storeType][host];
        }
      }
    }

    this.batchTimer = setTimeout(() => {
      clearTimeout(this.batchTimer);
      this.emit("stores-update", this.boundUpdate);
      this.boundUpdate = {};
    }, BATCH_DELAY);

    return null;
  },

  /**
   * This method removes data from the this.boundUpdate object in the same
   * manner like this.update() adds data to it.
   *
   * @param {string} action
   *        The type of change. One of "added", "changed" or "deleted"
   * @param {string} storeType
   *        The storage actor for which you want to remove the updates data.
   * @param {object} data
   *        The update object. This object is of the following format:
   *         - {
   *             <host1>: [<store_names1>, <store_name2>...],
   *             <host2>: [<store_names34>...],
   *           }
   *           Where host1, host2 are the hosts which you want to remove and
   *           [<store_namesX] is an array of the names of the store objects.
   */
  removeNamesFromUpdateList(action, storeType, data) {
    for (let host in data) {
      if (this.boundUpdate[action] && this.boundUpdate[action][storeType] &&
          this.boundUpdate[action][storeType][host]) {
        for (let name in data[host]) {
          let index = this.boundUpdate[action][storeType][host].indexOf(name);
          if (index > -1) {
            this.boundUpdate[action][storeType][host].splice(index, 1);
          }
        }
        if (!this.boundUpdate[action][storeType][host].length) {
          delete this.boundUpdate[action][storeType][host];
        }
      }
    }
    return null;
  }
});

exports.StorageActor = StorageActor;
PK
!<�>N��8chrome/devtools/modules/devtools/server/actors/string.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var {DebuggerServer} = require("devtools/server/main");

var protocol = require("devtools/shared/protocol");
const {longStringSpec} = require("devtools/shared/specs/string");

exports.LongStringActor = protocol.ActorClassWithSpec(longStringSpec, {
  initialize: function (conn, str) {
    protocol.Actor.prototype.initialize.call(this, conn);
    this.str = str;
    this.short = (this.str.length < DebuggerServer.LONG_STRING_LENGTH);
  },

  destroy: function () {
    this.str = null;
    protocol.Actor.prototype.destroy.call(this);
  },

  form: function () {
    if (this.short) {
      return this.str;
    }
    return {
      type: "longString",
      actor: this.actorID,
      length: this.str.length,
      initial: this.str.substring(0, DebuggerServer.LONG_STRING_INITIAL_LENGTH)
    };
  },

  substring: function (start, end) {
    return Promise.resolve(this.str.substring(start, end));
  },

  release: function () { }
});
PK
!<����K�K�8chrome/devtools/modules/devtools/server/actors/styles.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {Ci} = require("chrome");
const Services = require("Services");
const protocol = require("devtools/shared/protocol");
const {LongStringActor} = require("devtools/server/actors/string");
const InspectorUtils = require("InspectorUtils");

// This will also add the "stylesheet" actor type for protocol.js to recognize

const {pageStyleSpec, styleRuleSpec, ELEMENT_STYLE} = require("devtools/shared/specs/styles");

loader.lazyRequireGetter(this, "CssLogic", "devtools/server/css-logic", true);
loader.lazyRequireGetter(this, "SharedCssLogic", "devtools/shared/inspector/css-logic");
loader.lazyRequireGetter(this, "getDefinedGeometryProperties",
  "devtools/server/actors/highlighters/geometry-editor", true);
loader.lazyRequireGetter(this, "isCssPropertyKnown",
  "devtools/server/actors/css-properties", true);
loader.lazyRequireGetter(this, "parseNamedDeclarations",
  "devtools/shared/css/parsing-utils", true);
loader.lazyRequireGetter(this, "UPDATE_PRESERVING_RULES",
  "devtools/server/actors/stylesheets", true);
loader.lazyRequireGetter(this, "UPDATE_GENERAL",
  "devtools/server/actors/stylesheets", true);

loader.lazyGetter(this, "PSEUDO_ELEMENTS", () => {
  return InspectorUtils.getCSSPseudoElementNames();
});
loader.lazyGetter(this, "FONT_VARIATIONS_ENABLED", () => {
  return Services.prefs.getBoolPref("layout.css.font-variations.enabled");
});

const XHTML_NS = "http://www.w3.org/1999/xhtml";
const FONT_PREVIEW_TEXT = "Abc";
const FONT_PREVIEW_FONT_SIZE = 40;
const FONT_PREVIEW_FILLSTYLE = "black";
const NORMAL_FONT_WEIGHT = 400;
const BOLD_FONT_WEIGHT = 700;
// Offset (in px) to avoid cutting off text edges of italic fonts.
const FONT_PREVIEW_OFFSET = 4;

/**
 * The PageStyle actor lets the client look at the styles on a page, as
 * they are applied to a given node.
 */
var PageStyleActor = protocol.ActorClassWithSpec(pageStyleSpec, {
  /**
   * Create a PageStyleActor.
   *
   * @param inspector
   *    The InspectorActor that owns this PageStyleActor.
   *
   * @constructor
   */
  initialize: function (inspector) {
    protocol.Actor.prototype.initialize.call(this, null);
    this.inspector = inspector;
    if (!this.inspector.walker) {
      throw Error("The inspector's WalkerActor must be created before " +
                   "creating a PageStyleActor.");
    }
    this.walker = inspector.walker;
    this.cssLogic = new CssLogic(InspectorUtils.isInheritedProperty);

    // Stores the association of DOM objects -> actors
    this.refMap = new Map();

    // Maps document elements to style elements, used to add new rules.
    this.styleElements = new WeakMap();

    this.onFrameUnload = this.onFrameUnload.bind(this);
    this.onStyleSheetAdded = this.onStyleSheetAdded.bind(this);

    this.inspector.tabActor.on("will-navigate", this.onFrameUnload);
    this.inspector.tabActor.on("stylesheet-added", this.onStyleSheetAdded);

    this._styleApplied = this._styleApplied.bind(this);
    this._watchedSheets = new Set();
  },

  destroy: function () {
    if (!this.walker) {
      return;
    }
    protocol.Actor.prototype.destroy.call(this);
    this.inspector.tabActor.off("will-navigate", this.onFrameUnload);
    this.inspector.tabActor.off("stylesheet-added", this.onStyleSheetAdded);
    this.inspector = null;
    this.walker = null;
    this.refMap = null;
    this.cssLogic = null;
    this.styleElements = null;

    for (let sheet of this._watchedSheets) {
      sheet.off("style-applied", this._styleApplied);
    }
    this._watchedSheets.clear();
  },

  get conn() {
    return this.inspector.conn;
  },

  form: function (detail) {
    if (detail === "actorid") {
      return this.actorID;
    }

    return {
      actor: this.actorID,
      traits: {
        // Whether the actor has had bug 1103993 fixed, which means that the
        // getApplied method calls cssLogic.highlight(node) to recreate the
        // style cache. Clients requesting getApplied from actors that have not
        // been fixed must make sure cssLogic.highlight(node) was called before.
        getAppliedCreatesStyleCache: true,
        // Whether addNewRule accepts the editAuthored argument.
        authoredStyles: true,
        // Whether getAllUsedFontFaces/getUsedFontFaces accepts the includeVariations
        // argument.
        fontVariations: FONT_VARIATIONS_ENABLED,
      }
    };
  },

  /**
   * Called when a style sheet is updated.
   */
  _styleApplied: function (kind, styleSheet) {
    // No matter what kind of update is done, we need to invalidate
    // the keyframe cache.
    this.cssLogic.reset();
    if (kind === UPDATE_GENERAL) {
      this.emit("stylesheet-updated", styleSheet);
    }
  },

  /**
   * Return or create a StyleRuleActor for the given item.
   * @param item Either a CSSStyleRule or a DOM element.
   */
  _styleRef: function (item) {
    if (this.refMap.has(item)) {
      return this.refMap.get(item);
    }
    let actor = StyleRuleActor(this, item);
    this.manage(actor);
    this.refMap.set(item, actor);

    return actor;
  },

  /**
   * Update the association between a StyleRuleActor and its
   * corresponding item.  This is used when a StyleRuleActor updates
   * as style sheet and starts using a new rule.
   *
   * @param oldItem The old association; either a CSSStyleRule or a
   *                DOM element.
   * @param item Either a CSSStyleRule or a DOM element.
   * @param actor a StyleRuleActor
   */
  updateStyleRef: function (oldItem, item, actor) {
    this.refMap.delete(oldItem);
    this.refMap.set(item, actor);
  },

  /**
   * Return or create a StyleSheetActor for the given CSSStyleSheet.
   * @param  {CSSStyleSheet} sheet
   *         The style sheet to create an actor for.
   * @return {StyleSheetActor}
   *         The actor for this style sheet
   */
  _sheetRef: function (sheet) {
    let tabActor = this.inspector.tabActor;
    let actor = tabActor.createStyleSheetActor(sheet);
    return actor;
  },

  /**
   * Get the computed style for a node.
   *
   * @param NodeActor node
   * @param object options
   *   `filter`: A string filter that affects the "matched" handling.
   *     'user': Include properties from user style sheets.
   *     'ua': Include properties from user and user-agent sheets.
   *     Default value is 'ua'
   *   `markMatched`: true if you want the 'matched' property to be added
   *     when a computed property has been modified by a style included
   *     by `filter`.
   *   `onlyMatched`: true if unmatched properties shouldn't be included.
   *
   * @returns a JSON blob with the following form:
   *   {
   *     "property-name": {
   *       value: "property-value",
   *       priority: "!important" <optional>
   *       matched: <true if there are matched selectors for this value>
   *     },
   *     ...
   *   }
   */
  getComputed: function (node, options) {
    let ret = Object.create(null);

    this.cssLogic.sourceFilter = options.filter || SharedCssLogic.FILTER.UA;
    this.cssLogic.highlight(node.rawNode);
    let computed = this.cssLogic.computedStyle || [];

    Array.prototype.forEach.call(computed, name => {
      ret[name] = {
        value: computed.getPropertyValue(name),
        priority: computed.getPropertyPriority(name) || undefined
      };
    });

    if (options.markMatched || options.onlyMatched) {
      let matched = this.cssLogic.hasMatchedSelectors(Object.keys(ret));
      for (let key in ret) {
        if (matched[key]) {
          ret[key].matched = options.markMatched ? true : undefined;
        } else if (options.onlyMatched) {
          delete ret[key];
        }
      }
    }

    return ret;
  },

  /**
   * Get all the fonts from a page.
   *
   * @param object options
   *   `includePreviews`: Whether to also return image previews of the fonts.
   *   `previewText`: The text to display in the previews.
   *   `previewFontSize`: The font size of the text in the previews.
   *
   * @returns object
   *   object with 'fontFaces', a list of fonts that apply to this node.
   */
  getAllUsedFontFaces: function (options) {
    let windows = this.inspector.tabActor.windows;
    let fontsList = [];
    for (let win of windows) {
      fontsList = [...fontsList,
                   ...this.getUsedFontFaces(win.document.body, options)];
    }
    return fontsList;
  },

  /**
   * Get the font faces used in an element.
   *
   * @param NodeActor node / actual DOM node
   *    The node to get fonts from.
   * @param object options
   *   `includePreviews`: Whether to also return image previews of the fonts.
   *   `previewText`: The text to display in the previews.
   *   `previewFontSize`: The font size of the text in the previews.
   *
   * @returns object
   *   object with 'fontFaces', a list of fonts that apply to this node.
   */
  getUsedFontFaces: function (node, options) {
    // node.rawNode is defined for NodeActor objects
    let actualNode = node.rawNode || node;
    let contentDocument = actualNode.ownerDocument;
    // We don't get fonts for a node, but for a range
    let rng = contentDocument.createRange();
    rng.selectNodeContents(actualNode);
    let fonts = InspectorUtils.getUsedFontFaces(rng);
    let fontsArray = [];

    for (let i = 0; i < fonts.length; i++) {
      let font = fonts[i];
      let fontFace = {
        name: font.name,
        CSSFamilyName: font.CSSFamilyName,
        srcIndex: font.srcIndex,
        URI: font.URI,
        format: font.format,
        localName: font.localName,
        metadata: font.metadata
      };

      // If this font comes from a @font-face rule
      if (font.rule) {
        let styleActor = StyleRuleActor(this, font.rule);
        this.manage(styleActor);
        fontFace.rule = styleActor;
        fontFace.ruleText = font.rule.cssText;
      }

      // Get the weight and style of this font for the preview and sort order
      let weight = NORMAL_FONT_WEIGHT, style = "";
      if (font.rule) {
        weight = font.rule.style.getPropertyValue("font-weight")
                 || NORMAL_FONT_WEIGHT;
        if (weight == "bold") {
          weight = BOLD_FONT_WEIGHT;
        } else if (weight == "normal") {
          weight = NORMAL_FONT_WEIGHT;
        }
        style = font.rule.style.getPropertyValue("font-style") || "";
      }
      fontFace.weight = weight;
      fontFace.style = style;

      if (options.includePreviews) {
        let opts = {
          previewText: options.previewText,
          previewFontSize: options.previewFontSize,
          fontStyle: weight + " " + style,
          fillStyle: options.previewFillStyle
        };
        let { dataURL, size } = getFontPreviewData(font.CSSFamilyName,
                                                   contentDocument, opts);
        fontFace.preview = {
          data: LongStringActor(this.conn, dataURL),
          size: size
        };
      }

      if (options.includeVariations && FONT_VARIATIONS_ENABLED) {
        fontFace.variationAxes = font.getVariationAxes();
        fontFace.variationInstances = font.getVariationInstances();
      }

      fontsArray.push(fontFace);
    }

    // @font-face fonts at the top, then alphabetically, then by weight
    fontsArray.sort(function (a, b) {
      return a.weight > b.weight ? 1 : -1;
    });
    fontsArray.sort(function (a, b) {
      if (a.CSSFamilyName == b.CSSFamilyName) {
        return 0;
      }
      return a.CSSFamilyName > b.CSSFamilyName ? 1 : -1;
    });
    fontsArray.sort(function (a, b) {
      if ((a.rule && b.rule) || (!a.rule && !b.rule)) {
        return 0;
      }
      return !a.rule && b.rule ? 1 : -1;
    });

    return fontsArray;
  },

  /**
   * Get a list of selectors that match a given property for a node.
   *
   * @param NodeActor node
   * @param string property
   * @param object options
   *   `filter`: A string filter that affects the "matched" handling.
   *     'user': Include properties from user style sheets.
   *     'ua': Include properties from user and user-agent sheets.
   *     Default value is 'ua'
   *
   * @returns a JSON object with the following form:
   *   {
   *     // An ordered list of rules that apply
   *     matched: [{
   *       rule: <rule actorid>,
   *       sourceText: <string>, // The source of the selector, relative
   *                             // to the node in question.
   *       selector: <string>, // the selector ID that matched
   *       value: <string>, // the value of the property
   *       status: <int>,
   *         // The status of the match - high numbers are better placed
   *         // to provide styling information:
   *         // 3: Best match, was used.
   *         // 2: Matched, but was overridden.
   *         // 1: Rule from a parent matched.
   *         // 0: Unmatched (never returned in this API)
   *     }, ...],
   *
   *     // The full form of any domrule referenced.
   *     rules: [ <domrule>, ... ], // The full form of any domrule referenced
   *
   *     // The full form of any sheets referenced.
   *     sheets: [ <domsheet>, ... ]
   *  }
   */
  getMatchedSelectors: function (node, property, options) {
    this.cssLogic.sourceFilter = options.filter || SharedCssLogic.FILTER.UA;
    this.cssLogic.highlight(node.rawNode);

    let rules = new Set();
    let sheets = new Set();

    let matched = [];
    let propInfo = this.cssLogic.getPropertyInfo(property);
    for (let selectorInfo of propInfo.matchedSelectors) {
      let cssRule = selectorInfo.selector.cssRule;
      let domRule = cssRule.sourceElement || cssRule.domRule;

      let rule = this._styleRef(domRule);
      rules.add(rule);

      matched.push({
        rule: rule,
        sourceText: this.getSelectorSource(selectorInfo, node.rawNode),
        selector: selectorInfo.selector.text,
        name: selectorInfo.property,
        value: selectorInfo.value,
        status: selectorInfo.status
      });
    }

    this.expandSets(rules, sheets);

    return {
      matched: matched,
      rules: [...rules],
      sheets: [...sheets]
    };
  },

  // Get a selector source for a CssSelectorInfo relative to a given
  // node.
  getSelectorSource: function (selectorInfo, relativeTo) {
    let result = selectorInfo.selector.text;
    if (selectorInfo.elementStyle) {
      let source = selectorInfo.sourceElement;
      if (source === relativeTo) {
        result = "this";
      } else {
        result = CssLogic.getShortName(source);
      }
      result += ".style";
    }
    return result;
  },

  /**
   * Get the set of styles that apply to a given node.
   * @param NodeActor node
   * @param object options
   *   `filter`: A string filter that affects the "matched" handling.
   *     'user': Include properties from user style sheets.
   *     'ua': Include properties from user and user-agent sheets.
   *     Default value is 'ua'
   *   `inherited`: Include styles inherited from parent nodes.
   *   `matchedSelectors`: Include an array of specific selectors that
   *     caused this rule to match its node.
   *   `skipPseudo`: Exclude styles applied to pseudo elements of the provided node.
   */
  async getApplied(node, options) {
    if (!node) {
      return {entries: [], rules: [], sheets: []};
    }

    this.cssLogic.highlight(node.rawNode);
    let entries = [];
    entries = entries.concat(this._getAllElementRules(node, undefined,
                                                      options));

    let result = this.getAppliedProps(node, entries, options);
    for (let rule of result.rules) {
      // See the comment in |form| to understand this.
      await rule.getAuthoredCssText();
    }
    return result;
  },

  _hasInheritedProps: function (style) {
    return Array.prototype.some.call(style, prop => {
      return InspectorUtils.isInheritedProperty(prop);
    });
  },

  async isPositionEditable(node) {
    if (!node || node.rawNode.nodeType !== node.rawNode.ELEMENT_NODE) {
      return false;
    }

    let props = getDefinedGeometryProperties(node.rawNode);

    // Elements with only `width` and `height` are currently not considered
    // editable.
    return props.has("top") ||
           props.has("right") ||
           props.has("left") ||
           props.has("bottom");
  },

  /**
   * Helper function for getApplied, gets all the rules from a given
   * element. See getApplied for documentation on parameters.
   * @param NodeActor node
   * @param bool inherited
   * @param object options

   * @return Array The rules for a given element. Each item in the
   *               array has the following signature:
   *                - rule RuleActor
   *                - isSystem Boolean
   *                - inherited Boolean
   *                - pseudoElement String
   */
  _getAllElementRules: function (node, inherited, options) {
    let {bindingElement, pseudo} =
        CssLogic.getBindingElementAndPseudo(node.rawNode);
    let rules = [];

    if (!bindingElement || !bindingElement.style) {
      return rules;
    }

    let elementStyle = this._styleRef(bindingElement);
    let showElementStyles = !inherited && !pseudo;
    let showInheritedStyles = inherited &&
                              this._hasInheritedProps(bindingElement.style);

    let rule = {
      rule: elementStyle,
      pseudoElement: null,
      isSystem: false,
      inherited: false
    };

    // First any inline styles
    if (showElementStyles) {
      rules.push(rule);
    }

    // Now any inherited styles
    if (showInheritedStyles) {
      rule.inherited = inherited;
      rules.push(rule);
    }

    // Add normal rules.  Typically this is passing in the node passed into the
    // function, unless if that node was ::before/::after.  In which case,
    // it will pass in the parentNode along with "::before"/"::after".
    this._getElementRules(bindingElement, pseudo, inherited, options)
        .forEach(oneRule => {
          // The only case when there would be a pseudo here is
          // ::before/::after, and in this case we want to tell the
          // view that it belongs to the element (which is a
          // _moz_generated_content native anonymous element).
          oneRule.pseudoElement = null;
          rules.push(oneRule);
        });

    // Now any pseudos.
    if (showElementStyles && !options.skipPseudo) {
      for (let readPseudo of PSEUDO_ELEMENTS) {
        this._getElementRules(bindingElement, readPseudo, inherited, options)
            .forEach(oneRule => {
              rules.push(oneRule);
            });
      }
    }

    return rules;
  },

  /**
   * Helper function for _getAllElementRules, returns the rules from a given
   * element. See getApplied for documentation on parameters.
   * @param DOMNode node
   * @param string pseudo
   * @param DOMNode inherited
   * @param object options
   *
   * @returns Array
   */
  _getElementRules: function (node, pseudo, inherited, options) {
    let domRules = InspectorUtils.getCSSStyleRules(node, pseudo);
    if (!domRules) {
      return [];
    }

    let rules = [];

    // getCSSStyleRules returns ordered from least-specific to
    // most-specific.
    for (let i = domRules.length - 1; i >= 0; i--) {
      let domRule = domRules[i];

      let isSystem = !SharedCssLogic.isContentStylesheet(domRule.parentStyleSheet);

      if (isSystem && options.filter != SharedCssLogic.FILTER.UA) {
        continue;
      }

      if (inherited) {
        // Don't include inherited rules if none of its properties
        // are inheritable.
        let hasInherited = [...domRule.style].some(
          prop => InspectorUtils.isInheritedProperty(prop)
        );
        if (!hasInherited) {
          continue;
        }
      }

      let ruleActor = this._styleRef(domRule);
      rules.push({
        rule: ruleActor,
        inherited: inherited,
        isSystem: isSystem,
        pseudoElement: pseudo
      });
    }
    return rules;
  },

  /**
   * Given a node and a CSS rule, walk up the DOM looking for a
   * matching element rule.  Return an array of all found entries, in
   * the form generated by _getAllElementRules.  Note that this will
   * always return an array of either zero or one element.
   *
   * @param {NodeActor} node the node
   * @param {CSSStyleRule} filterRule the rule to filter for
   * @return {Array} array of zero or one elements; if one, the element
   *                 is the entry as returned by _getAllElementRules.
   */
  findEntryMatchingRule: function (node, filterRule) {
    const options = {matchedSelectors: true, inherited: true};
    let entries = [];
    let parent = this.walker.parentNode(node);
    while (parent && parent.rawNode.nodeType != Ci.nsIDOMNode.DOCUMENT_NODE) {
      entries = entries.concat(this._getAllElementRules(parent, parent,
                                                        options));
      parent = this.walker.parentNode(parent);
    }

    return entries.filter(entry => entry.rule.rawRule === filterRule);
  },

  /**
   * Helper function for getApplied that fetches a set of style properties that
   * apply to the given node and associated rules
   * @param NodeActor node
   * @param object options
   *   `filter`: A string filter that affects the "matched" handling.
   *     'user': Include properties from user style sheets.
   *     'ua': Include properties from user and user-agent sheets.
   *     Default value is 'ua'
   *   `inherited`: Include styles inherited from parent nodes.
   *   `matchedSelectors`: Include an array of specific selectors that
   *     caused this rule to match its node.
   *   `skipPseudo`: Exclude styles applied to pseudo elements of the provided node.
   * @param array entries
   *   List of appliedstyle objects that lists the rules that apply to the
   *   node. If adding a new rule to the stylesheet, only the new rule entry
   *   is provided and only the style properties that apply to the new
   *   rule is fetched.
   * @returns Object containing the list of rule entries, rule actors and
   *   stylesheet actors that applies to the given node and its associated
   *   rules.
   */
  getAppliedProps: function (node, entries, options) {
    if (options.inherited) {
      let parent = this.walker.parentNode(node);
      while (parent && parent.rawNode.nodeType != Ci.nsIDOMNode.DOCUMENT_NODE) {
        entries = entries.concat(this._getAllElementRules(parent, parent,
                                                          options));
        parent = this.walker.parentNode(parent);
      }
    }

    if (options.matchedSelectors) {
      for (let entry of entries) {
        if (entry.rule.type === ELEMENT_STYLE) {
          continue;
        }

        let domRule = entry.rule.rawRule;
        let selectors = CssLogic.getSelectors(domRule);
        let element = entry.inherited ? entry.inherited.rawNode : node.rawNode;

        let {bindingElement, pseudo} =
            CssLogic.getBindingElementAndPseudo(element);
        entry.matchedSelectors = [];
        for (let i = 0; i < selectors.length; i++) {
          if (InspectorUtils.selectorMatchesElement(bindingElement, domRule, i,
                                                    pseudo)) {
            entry.matchedSelectors.push(selectors[i]);
          }
        }
      }
    }

    // Add all the keyframes rule associated with the element
    let computedStyle = this.cssLogic.computedStyle;
    if (computedStyle) {
      let animationNames = computedStyle.animationName.split(",");
      animationNames = animationNames.map(name => name.trim());

      if (animationNames) {
        // Traverse through all the available keyframes rule and add
        // the keyframes rule that matches the computed animation name
        for (let keyframesRule of this.cssLogic.keyframesRules) {
          if (animationNames.indexOf(keyframesRule.name) > -1) {
            for (let rule of keyframesRule.cssRules) {
              entries.push({
                rule: this._styleRef(rule),
                keyframes: this._styleRef(keyframesRule)
              });
            }
          }
        }
      }
    }

    let rules = new Set();
    let sheets = new Set();
    entries.forEach(entry => rules.add(entry.rule));
    this.expandSets(rules, sheets);

    return {
      entries: entries,
      rules: [...rules],
      sheets: [...sheets]
    };
  },

  /**
   * Expand Sets of rules and sheets to include all parent rules and sheets.
   */
  expandSets: function (ruleSet, sheetSet) {
    // Sets include new items in their iteration
    for (let rule of ruleSet) {
      if (rule.rawRule.parentRule) {
        let parent = this._styleRef(rule.rawRule.parentRule);
        if (!ruleSet.has(parent)) {
          ruleSet.add(parent);
        }
      }
      if (rule.rawRule.parentStyleSheet) {
        let parent = this._sheetRef(rule.rawRule.parentStyleSheet);
        if (!sheetSet.has(parent)) {
          sheetSet.add(parent);
        }
      }
    }

    for (let sheet of sheetSet) {
      if (sheet.rawSheet.parentStyleSheet) {
        let parent = this._sheetRef(sheet.rawSheet.parentStyleSheet);
        if (!sheetSet.has(parent)) {
          sheetSet.add(parent);
        }
      }
    }
  },

  /**
   * Get layout-related information about a node.
   * This method returns an object with properties giving information about
   * the node's margin, border, padding and content region sizes, as well
   * as information about the type of box, its position, z-index, etc...
   * @param {NodeActor} node
   * @param {Object} options The only available option is autoMargins.
   * If set to true, the element's margins will receive an extra check to see
   * whether they are set to "auto" (knowing that the computed-style in this
   * case would return "0px").
   * The returned object will contain an extra property (autoMargins) listing
   * all margins that are set to auto, e.g. {top: "auto", left: "auto"}.
   * @return {Object}
   */
  getLayout: function (node, options) {
    this.cssLogic.highlight(node.rawNode);

    let layout = {};

    // First, we update the first part of the box model view, with
    // the size of the element.

    let clientRect = node.rawNode.getBoundingClientRect();
    layout.width = parseFloat(clientRect.width.toPrecision(6));
    layout.height = parseFloat(clientRect.height.toPrecision(6));

    // We compute and update the values of margins & co.
    let style = CssLogic.getComputedStyle(node.rawNode);
    for (let prop of [
      "position",
      "top",
      "right",
      "bottom",
      "left",
      "margin-top",
      "margin-right",
      "margin-bottom",
      "margin-left",
      "padding-top",
      "padding-right",
      "padding-bottom",
      "padding-left",
      "border-top-width",
      "border-right-width",
      "border-bottom-width",
      "border-left-width",
      "z-index",
      "box-sizing",
      "display",
      "float",
      "line-height"
    ]) {
      layout[prop] = style.getPropertyValue(prop);
    }

    if (options.autoMargins) {
      layout.autoMargins = this.processMargins(this.cssLogic);
    }

    for (let i in this.map) {
      let property = this.map[i].property;
      this.map[i].value = parseFloat(style.getPropertyValue(property));
    }

    return layout;
  },

  /**
   * Find 'auto' margin properties.
   */
  processMargins: function (cssLogic) {
    let margins = {};

    for (let prop of ["top", "bottom", "left", "right"]) {
      let info = cssLogic.getPropertyInfo("margin-" + prop);
      let selectors = info.matchedSelectors;
      if (selectors && selectors.length > 0 && selectors[0].value == "auto") {
        margins[prop] = "auto";
      }
    }

    return margins;
  },

  /**
   * On page navigation, tidy up remaining objects.
   */
  onFrameUnload: function () {
    this.styleElements = new WeakMap();
  },

  /**
   * When a stylesheet is added, handle the related StyleSheetActor to listen for changes.
   * @param  {StyleSheetActor} actor
   *         The actor for the added stylesheet.
   */
  onStyleSheetAdded: function (actor) {
    if (!this._watchedSheets.has(actor)) {
      this._watchedSheets.add(actor);
      actor.on("style-applied", this._styleApplied);
    }
  },

  /**
   * Helper function to addNewRule to get or create a style tag in the provided
   * document.
   *
   * @param {Document} document
   *        The document in which the style element should be appended.
   * @returns DOMElement of the style tag
   */
  getStyleElement: function (document) {
    if (!this.styleElements.has(document)) {
      let style = document.createElementNS(XHTML_NS, "style");
      style.setAttribute("type", "text/css");
      document.documentElement.appendChild(style);
      this.styleElements.set(document, style);
    }

    return this.styleElements.get(document);
  },

  /**
   * Helper function for adding a new rule and getting its applied style
   * properties
   * @param NodeActor node
   * @param CSSStyleRule rule
   * @returns Object containing its applied style properties
   */
  getNewAppliedProps: function (node, rule) {
    let ruleActor = this._styleRef(rule);
    return this.getAppliedProps(node, [{ rule: ruleActor }],
      { matchedSelectors: true });
  },

  /**
   * Adds a new rule, and returns the new StyleRuleActor.
   * @param {NodeActor} node
   * @param {String} pseudoClasses The list of pseudo classes to append to the
   *        new selector.
   * @param {Boolean} editAuthored
   *        True if the selector should be updated by editing the
   *        authored text; false if the selector should be updated via
   *        CSSOM.
   * @returns {StyleRuleActor} the new rule
   */
  async addNewRule(node, pseudoClasses, editAuthored = false) {
    let style = this.getStyleElement(node.rawNode.ownerDocument);
    let sheet = style.sheet;
    let cssRules = sheet.cssRules;
    let rawNode = node.rawNode;
    let classes = [...rawNode.classList];

    let selector;
    if (rawNode.id) {
      selector = "#" + CSS.escape(rawNode.id);
    } else if (classes.length > 0) {
      selector = "." + classes.map(c => CSS.escape(c)).join(".");
    } else {
      selector = rawNode.localName;
    }

    if (pseudoClasses && pseudoClasses.length > 0) {
      selector += pseudoClasses.join("");
    }

    let index = sheet.insertRule(selector + " {}", cssRules.length);

    // If inserting the rule succeeded, go ahead and edit the source
    // text if requested.
    if (editAuthored) {
      let sheetActor = this._sheetRef(sheet);
      let {str: authoredText} = await sheetActor.getText();
      authoredText += "\n" + selector + " {\n" + "}";
      await sheetActor.update(authoredText, false);
    }

    return this.getNewAppliedProps(node, sheet.cssRules.item(index));
  }
});
exports.PageStyleActor = PageStyleActor;

/**
 * An actor that represents a CSS style object on the protocol.
 *
 * We slightly flatten the CSSOM for this actor, it represents
 * both the CSSRule and CSSStyle objects in one actor.  For nodes
 * (which have a CSSStyle but no CSSRule) we create a StyleRuleActor
 * with a special rule type (100).
 */
var StyleRuleActor = protocol.ActorClassWithSpec(styleRuleSpec, {
  initialize: function (pageStyle, item) {
    protocol.Actor.prototype.initialize.call(this, null);
    this.pageStyle = pageStyle;
    this.rawStyle = item.style;
    this._parentSheet = null;
    this._onStyleApplied = this._onStyleApplied.bind(this);

    if (CSSRule.isInstance(item)) {
      this.type = item.type;
      this.rawRule = item;
      if ((this.type === CSSRule.STYLE_RULE ||
           this.type === CSSRule.KEYFRAME_RULE) &&
          this.rawRule.parentStyleSheet) {
        this.line = InspectorUtils.getRelativeRuleLine(this.rawRule);
        this.column = InspectorUtils.getRuleColumn(this.rawRule);
        this._parentSheet = this.rawRule.parentStyleSheet;
        this._computeRuleIndex();
        this.sheetActor = this.pageStyle._sheetRef(this._parentSheet);
        this.sheetActor.on("style-applied", this._onStyleApplied);
      }
    } else {
      // Fake a rule
      this.type = ELEMENT_STYLE;
      this.rawNode = item;
      this.rawRule = {
        style: item.style,
        toString: function () {
          return "[element rule " + this.style + "]";
        }
      };
    }
  },

  get conn() {
    return this.pageStyle.conn;
  },

  destroy: function () {
    if (!this.rawStyle) {
      return;
    }
    protocol.Actor.prototype.destroy.call(this);
    this.rawStyle = null;
    this.pageStyle = null;
    this.rawNode = null;
    this.rawRule = null;
    if (this.sheetActor) {
      this.sheetActor.off("style-applied", this._onStyleApplied);
    }
  },

  // Objects returned by this actor are owned by the PageStyleActor
  // to which this rule belongs.
  get marshallPool() {
    return this.pageStyle;
  },

  // True if this rule supports as-authored styles, meaning that the
  // rule text can be rewritten using setRuleText.
  get canSetRuleText() {
    return this.type === ELEMENT_STYLE ||
           (this._parentSheet &&
            // If a rule has been modified via CSSOM, then we should fall
            // back to non-authored editing.
            // https://bugzilla.mozilla.org/show_bug.cgi?id=1224121
            !this.sheetActor.hasRulesModifiedByCSSOM() &&
            // Special case about:PreferenceStyleSheet, as it is generated on
            // the fly and the URI is not registered with the about:handler
            // https://bugzilla.mozilla.org/show_bug.cgi?id=935803#c37
            this._parentSheet.href !== "about:PreferenceStyleSheet");
  },

  getDocument: function (sheet) {
    if (sheet.ownerNode) {
      return sheet.ownerNode.nodeType == sheet.ownerNode.DOCUMENT_NODE ?
             sheet.ownerNode : sheet.ownerNode.ownerDocument;
    } else if (sheet.parentStyleSheet) {
      return this.getDocument(sheet.parentStyleSheet);
    }
    throw (new Error("Failed trying to get the document of an invalid stylesheet"));
  },

  toString: function () {
    return "[StyleRuleActor for " + this.rawRule + "]";
  },

  form: function (detail) {
    if (detail === "actorid") {
      return this.actorID;
    }

    let form = {
      actor: this.actorID,
      type: this.type,
      line: this.line || undefined,
      column: this.column,
      traits: {
        // Whether the style rule actor implements the modifySelector2 method
        // that allows for unmatched rule to be added
        modifySelectorUnmatched: true,
        // Whether the style rule actor implements the setRuleText
        // method.
        canSetRuleText: this.canSetRuleText,
      }
    };

    if (this.rawRule.parentRule) {
      form.parentRule =
        this.pageStyle._styleRef(this.rawRule.parentRule).actorID;

      // CSS rules that we call media rules are STYLE_RULES that are children
      // of MEDIA_RULEs. We need to check the parentRule to check if a rule is
      // a media rule so we do this here instead of in the switch statement
      // below.
      if (this.rawRule.parentRule.type === CSSRule.MEDIA_RULE) {
        form.media = [];
        for (let i = 0, n = this.rawRule.parentRule.media.length; i < n; i++) {
          form.media.push(this.rawRule.parentRule.media.item(i));
        }
      }
    }
    if (this._parentSheet) {
      form.parentStyleSheet =
        this.pageStyle._sheetRef(this._parentSheet).actorID;
    }

    // One tricky thing here is that other methods in this actor must
    // ensure that authoredText has been set before |form| is called.
    // This has to be treated specially, for now, because we cannot
    // synchronously compute the authored text, but |form| also cannot
    // return a promise.  See bug 1205868.
    form.authoredText = this.authoredText;

    switch (this.type) {
      case CSSRule.STYLE_RULE:
        form.selectors = CssLogic.getSelectors(this.rawRule);
        form.cssText = this.rawStyle.cssText || "";
        break;
      case ELEMENT_STYLE:
        // Elements don't have a parent stylesheet, and therefore
        // don't have an associated URI.  Provide a URI for
        // those.
        let doc = this.rawNode.ownerDocument;
        form.href = doc.location ? doc.location.href : "";
        form.cssText = this.rawStyle.cssText || "";
        form.authoredText = this.rawNode.getAttribute("style");
        break;
      case CSSRule.CHARSET_RULE:
        form.encoding = this.rawRule.encoding;
        break;
      case CSSRule.IMPORT_RULE:
        form.href = this.rawRule.href;
        break;
      case CSSRule.KEYFRAMES_RULE:
        form.cssText = this.rawRule.cssText;
        form.name = this.rawRule.name;
        break;
      case CSSRule.KEYFRAME_RULE:
        form.cssText = this.rawStyle.cssText || "";
        form.keyText = this.rawRule.keyText || "";
        break;
    }

    // Parse the text into a list of declarations so the client doesn't have to
    // and so that we can safely determine if a declaration is valid rather than
    // have the client guess it.
    if (form.authoredText || form.cssText) {
      let declarations = parseNamedDeclarations(isCssPropertyKnown,
                                                form.authoredText || form.cssText,
                                                true);

      // We need to grab CSS from the window, since calling supports() on the
      // one from the current global will fail due to not being an HTML global.
      let CSS = this.pageStyle.inspector.tabActor.window.CSS;
      form.declarations = declarations.map(decl => {
        // Use the 1-arg CSS.supports() call so that we also accept !important
        // in the value.
        decl.isValid = CSS.supports(`${decl.name}:${decl.value}`);
        return decl;
      });
    }

    return form;
  },

  /**
   * Send an event notifying that the location of the rule has
   * changed.
   *
   * @param {Number} line the new line number
   * @param {Number} column the new column number
   */
  _notifyLocationChanged: function (line, column) {
    this.emit("location-changed", line, column);
  },

  /**
   * Compute the index of this actor's raw rule in its parent style
   * sheet.  The index is a vector where each element is the index of
   * a given CSS rule in its parent.  A vector is used to support
   * nested rules.
   */
  _computeRuleIndex: function () {
    let rule = this.rawRule;
    let result = [];

    while (rule) {
      let cssRules;
      if (rule.parentRule) {
        cssRules = rule.parentRule.cssRules;
      } else {
        cssRules = rule.parentStyleSheet.cssRules;
      }

      let found = false;
      for (let i = 0; i < cssRules.length; i++) {
        if (rule === cssRules.item(i)) {
          found = true;
          result.unshift(i);
          break;
        }
      }

      if (!found) {
        this._ruleIndex = null;
        return;
      }

      rule = rule.parentRule;
    }

    this._ruleIndex = result;
  },

  /**
   * Get the rule corresponding to |this._ruleIndex| from the given
   * style sheet.
   *
   * @param  {DOMStyleSheet} sheet
   *         The style sheet.
   * @return {CSSStyleRule} the rule corresponding to
   * |this._ruleIndex|
   */
  _getRuleFromIndex: function (parentSheet) {
    let currentRule = null;
    for (let i of this._ruleIndex) {
      if (currentRule === null) {
        currentRule = parentSheet.cssRules[i];
      } else {
        currentRule = currentRule.cssRules.item(i);
      }
    }
    return currentRule;
  },

  /**
   * This is attached to the parent style sheet actor's
   * "style-applied" event.
   */
  _onStyleApplied: function (kind) {
    if (kind === UPDATE_GENERAL) {
      // A general change means that the rule actors are invalidated,
      // so stop listening to events now.
      if (this.sheetActor) {
        this.sheetActor.off("style-applied", this._onStyleApplied);
      }
    } else if (this._ruleIndex) {
      // The sheet was updated by this actor, in a way that preserves
      // the rules.  Now, recompute our new rule from the style sheet,
      // so that we aren't left with a reference to a dangling rule.
      let oldRule = this.rawRule;
      this.rawRule = this._getRuleFromIndex(this._parentSheet);
      // Also tell the page style so that future calls to _styleRef
      // return the same StyleRuleActor.
      this.pageStyle.updateStyleRef(oldRule, this.rawRule, this);
      let line = InspectorUtils.getRelativeRuleLine(this.rawRule);
      let column = InspectorUtils.getRuleColumn(this.rawRule);
      if (line !== this.line || column !== this.column) {
        this._notifyLocationChanged(line, column);
      }
      this.line = line;
      this.column = column;
    }
  },

  /**
   * Return a promise that resolves to the authored form of a rule's
   * text, if available.  If the authored form is not available, the
   * returned promise simply resolves to the empty string.  If the
   * authored form is available, this also sets |this.authoredText|.
   * The authored text will include invalid and otherwise ignored
   * properties.
   */
  getAuthoredCssText: function () {
    if (!this.canSetRuleText ||
        (this.type !== CSSRule.STYLE_RULE &&
         this.type !== CSSRule.KEYFRAME_RULE)) {
      return Promise.resolve("");
    }

    if (typeof this.authoredText === "string") {
      return Promise.resolve(this.authoredText);
    }

    let parentStyleSheet =
        this.pageStyle._sheetRef(this._parentSheet);
    return parentStyleSheet.getText().then((longStr) => {
      let cssText = longStr.str;
      let {text} = getRuleText(cssText, this.line, this.column);

      // Cache the result on the rule actor to avoid parsing again next time
      this.authoredText = text;
      return this.authoredText;
    });
  },

  /**
   * Set the contents of the rule.  This rewrites the rule in the
   * stylesheet and causes it to be re-evaluated.
   *
   * @param {String} newText the new text of the rule
   * @returns the rule with updated properties
   */
  async setRuleText(newText) {
    if (!this.canSetRuleText) {
      throw new Error("invalid call to setRuleText");
    }

    if (this.type === ELEMENT_STYLE) {
      // For element style rules, set the node's style attribute.
      this.rawNode.setAttribute("style", newText);
    } else {
      // For stylesheet rules, set the text in the stylesheet.
      let parentStyleSheet = this.pageStyle._sheetRef(this._parentSheet);
      let {str: cssText} = await parentStyleSheet.getText();

      let {offset, text} = getRuleText(cssText, this.line, this.column);
      cssText = cssText.substring(0, offset) + newText +
        cssText.substring(offset + text.length);

      await parentStyleSheet.update(cssText, false, UPDATE_PRESERVING_RULES);
    }

    this.authoredText = newText;

    return this;
  },

  /**
   * Modify a rule's properties. Passed an array of modifications:
   * {
   *   type: "set",
   *   name: <string>,
   *   value: <string>,
   *   priority: <optional string>
   * }
   *  or
   * {
   *   type: "remove",
   *   name: <string>,
   * }
   *
   * @returns the rule with updated properties
   */
  modifyProperties: function (modifications) {
    // Use a fresh element for each call to this function to prevent side
    // effects that pop up based on property values that were already set on the
    // element.

    let document;
    if (this.rawNode) {
      document = this.rawNode.ownerDocument;
    } else {
      let parentStyleSheet = this._parentSheet;
      while (parentStyleSheet.ownerRule) {
        parentStyleSheet = parentStyleSheet.ownerRule.parentStyleSheet;
      }

      document = this.getDocument(parentStyleSheet);
    }

    let tempElement = document.createElementNS(XHTML_NS, "div");

    for (let mod of modifications) {
      if (mod.type === "set") {
        tempElement.style.setProperty(mod.name, mod.value, mod.priority || "");
        this.rawStyle.setProperty(mod.name,
          tempElement.style.getPropertyValue(mod.name), mod.priority || "");
      } else if (mod.type === "remove") {
        this.rawStyle.removeProperty(mod.name);
      }
    }

    return this;
  },

  /**
   * Helper function for modifySelector and modifySelector2, inserts the new
   * rule with the new selector into the parent style sheet and removes the
   * current rule. Returns the newly inserted css rule or null if the rule is
   * unsuccessfully inserted to the parent style sheet.
   *
   * @param {String} value
   *        The new selector value
   * @param {Boolean} editAuthored
   *        True if the selector should be updated by editing the
   *        authored text; false if the selector should be updated via
   *        CSSOM.
   *
   * @returns {CSSRule}
   *        The new CSS rule added
   */
  async _addNewSelector(value, editAuthored) {
    let rule = this.rawRule;
    let parentStyleSheet = this._parentSheet;

    // We know the selector modification is ok, so if the client asked
    // for the authored text to be edited, do it now.
    if (editAuthored) {
      let document = this.getDocument(this._parentSheet);
      try {
        document.querySelector(value);
      } catch (e) {
        return null;
      }

      let sheetActor = this.pageStyle._sheetRef(parentStyleSheet);
      let {str: authoredText} = await sheetActor.getText();
      let [startOffset, endOffset] = getSelectorOffsets(authoredText, this.line,
                                                        this.column);
      authoredText = authoredText.substring(0, startOffset) + value +
        authoredText.substring(endOffset);
      await sheetActor.update(authoredText, false, UPDATE_PRESERVING_RULES);
    } else {
      let cssRules = parentStyleSheet.cssRules;
      let cssText = rule.cssText;
      let selectorText = rule.selectorText;

      for (let i = 0; i < cssRules.length; i++) {
        if (rule === cssRules.item(i)) {
          try {
            // Inserts the new style rule into the current style sheet and
            // delete the current rule
            let ruleText = cssText.slice(selectorText.length).trim();
            parentStyleSheet.insertRule(value + " " + ruleText, i);
            parentStyleSheet.deleteRule(i + 1);
            break;
          } catch (e) {
            // The selector could be invalid, or the rule could fail to insert.
            return null;
          }
        }
      }
    }

    return this._getRuleFromIndex(parentStyleSheet);
  },

  /**
   * Modify the current rule's selector by inserting a new rule with the new
   * selector value and removing the current rule.
   *
   * Note this method was kept for backward compatibility, but unmatched rules
   * support was added in FF41.
   *
   * @param string value
   *        The new selector value
   * @returns boolean
   *        Returns a boolean if the selector in the stylesheet was modified,
   *        and false otherwise
   */
  async modifySelector(value) {
    if (this.type === ELEMENT_STYLE) {
      return false;
    }

    let document = this.getDocument(this._parentSheet);
    // Extract the selector, and pseudo elements and classes
    let [selector] = value.split(/(:{1,2}.+$)/);
    let selectorElement;

    try {
      selectorElement = document.querySelector(selector);
    } catch (e) {
      return false;
    }

    // Check if the selector is valid and not the same as the original
    // selector
    if (selectorElement && this.rawRule.selectorText !== value) {
      await this._addNewSelector(value, false);
      return true;
    }
    return false;
  },

  /**
   * Modify the current rule's selector by inserting a new rule with the new
   * selector value and removing the current rule.
   *
   * In contrast with the modifySelector method which was used before FF41,
   * this method also returns information about the new rule and applied style
   * so that consumers can immediately display the new rule, whether or not the
   * selector matches the current element without having to refresh the whole
   * list.
   *
   * @param {DOMNode} node
   *        The current selected element
   * @param {String} value
   *        The new selector value
   * @param {Boolean} editAuthored
   *        True if the selector should be updated by editing the
   *        authored text; false if the selector should be updated via
   *        CSSOM.
   * @returns {Object}
   *        Returns an object that contains the applied style properties of the
   *        new rule and a boolean indicating whether or not the new selector
   *        matches the current selected element
   */
  modifySelector2: function (node, value, editAuthored = false) {
    if (this.type === ELEMENT_STYLE ||
        this.rawRule.selectorText === value) {
      return { ruleProps: null, isMatching: true };
    }

    let selectorPromise = this._addNewSelector(value, editAuthored);

    if (editAuthored) {
      selectorPromise = selectorPromise.then((newCssRule) => {
        if (newCssRule) {
          let style = this.pageStyle._styleRef(newCssRule);
          // See the comment in |form| to understand this.
          return style.getAuthoredCssText().then(() => newCssRule);
        }
        return newCssRule;
      });
    }

    return selectorPromise.then((newCssRule) => {
      let ruleProps = null;
      let isMatching = false;

      if (newCssRule) {
        let ruleEntry = this.pageStyle.findEntryMatchingRule(node, newCssRule);
        if (ruleEntry.length === 1) {
          ruleProps =
            this.pageStyle.getAppliedProps(node, ruleEntry,
                                           { matchedSelectors: true });
        } else {
          ruleProps = this.pageStyle.getNewAppliedProps(node, newCssRule);
        }

        isMatching = ruleProps.entries.some((ruleProp) =>
          ruleProp.matchedSelectors.length > 0);
      }

      return { ruleProps, isMatching };
    });
  }
});

/**
 * Helper function for getting an image preview of the given font.
 *
 * @param font {string}
 *        Name of font to preview
 * @param doc {Document}
 *        Document to use to render font
 * @param options {object}
 *        Object with options 'previewText' and 'previewFontSize'
 *
 * @return dataUrl
 *         The data URI of the font preview image
 */
function getFontPreviewData(font, doc, options) {
  options = options || {};
  let previewText = options.previewText || FONT_PREVIEW_TEXT;
  let previewFontSize = options.previewFontSize || FONT_PREVIEW_FONT_SIZE;
  let fillStyle = options.fillStyle || FONT_PREVIEW_FILLSTYLE;
  let fontStyle = options.fontStyle || "";

  let canvas = doc.createElementNS(XHTML_NS, "canvas");
  let ctx = canvas.getContext("2d");
  let fontValue = fontStyle + " " + previewFontSize + "px " + font + ", serif";

  // Get the correct preview text measurements and set the canvas dimensions
  ctx.font = fontValue;
  ctx.fillStyle = fillStyle;
  let textWidth = Math.round(ctx.measureText(previewText).width);

  canvas.width = textWidth * 2 + FONT_PREVIEW_OFFSET * 4;
  canvas.height = previewFontSize * 3;

  // we have to reset these after changing the canvas size
  ctx.font = fontValue;
  ctx.fillStyle = fillStyle;

  // Oversample the canvas for better text quality
  ctx.textBaseline = "top";
  ctx.scale(2, 2);
  ctx.fillText(previewText,
               FONT_PREVIEW_OFFSET,
               Math.round(previewFontSize / 3));

  let dataURL = canvas.toDataURL("image/png");

  return {
    dataURL: dataURL,
    size: textWidth + FONT_PREVIEW_OFFSET * 2
  };
}

exports.getFontPreviewData = getFontPreviewData;

/**
 * Get the text content of a rule given some CSS text, a line and a column
 * Consider the following example:
 * body {
 *  color: red;
 * }
 * p {
 *  line-height: 2em;
 *  color: blue;
 * }
 * Calling the function with the whole text above and line=4 and column=1 would
 * return "line-height: 2em; color: blue;"
 * @param {String} initialText
 * @param {Number} line (1-indexed)
 * @param {Number} column (1-indexed)
 * @return {object} An object of the form {offset: number, text: string}
 *                  The offset is the index into the input string where
 *                  the rule text started.  The text is the content of
 *                  the rule.
 */
function getRuleText(initialText, line, column) {
  if (typeof line === "undefined" || typeof column === "undefined") {
    throw new Error("Location information is missing");
  }

  let {offset: textOffset, text} =
      getTextAtLineColumn(initialText, line, column);
  let lexer = InspectorUtils.getCSSLexer(text);

  // Search forward for the opening brace.
  while (true) {
    let token = lexer.nextToken();
    if (!token) {
      throw new Error("couldn't find start of the rule");
    }
    if (token.tokenType === "symbol" && token.text === "{") {
      break;
    }
  }

  // Now collect text until we see the matching close brace.
  let braceDepth = 1;
  let startOffset, endOffset;
  while (true) {
    let token = lexer.nextToken();
    if (!token) {
      break;
    }
    if (startOffset === undefined) {
      startOffset = token.startOffset;
    }
    if (token.tokenType === "symbol") {
      if (token.text === "{") {
        ++braceDepth;
      } else if (token.text === "}") {
        --braceDepth;
        if (braceDepth == 0) {
          break;
        }
      }
    }
    endOffset = token.endOffset;
  }

  // If the rule was of the form "selector {" with no closing brace
  // and no properties, just return an empty string.
  if (startOffset === undefined) {
    return {offset: 0, text: ""};
  }
  // If the input didn't have any tokens between the braces (e.g.,
  // "div {}"), then the endOffset won't have been set yet; so account
  // for that here.
  if (endOffset === undefined) {
    endOffset = startOffset;
  }

  // Note that this approach will preserve comments, despite the fact
  // that cssTokenizer skips them.
  return {offset: textOffset + startOffset,
          text: text.substring(startOffset, endOffset)};
}

exports.getRuleText = getRuleText;

/**
 * Compute the start and end offsets of a rule's selector text, given
 * the CSS text and the line and column at which the rule begins.
 * @param {String} initialText
 * @param {Number} line (1-indexed)
 * @param {Number} column (1-indexed)
 * @return {array} An array with two elements: [startOffset, endOffset].
 *                 The elements mark the bounds in |initialText| of
 *                 the CSS rule's selector.
 */
function getSelectorOffsets(initialText, line, column) {
  if (typeof line === "undefined" || typeof column === "undefined") {
    throw new Error("Location information is missing");
  }

  let {offset: textOffset, text} =
      getTextAtLineColumn(initialText, line, column);
  let lexer = InspectorUtils.getCSSLexer(text);

  // Search forward for the opening brace.
  let endOffset;
  while (true) {
    let token = lexer.nextToken();
    if (!token) {
      break;
    }
    if (token.tokenType === "symbol" && token.text === "{") {
      if (endOffset === undefined) {
        break;
      }
      return [textOffset, textOffset + endOffset];
    }
    // Preserve comments and whitespace just before the "{".
    if (token.tokenType !== "comment" && token.tokenType !== "whitespace") {
      endOffset = token.endOffset;
    }
  }

  throw new Error("could not find bounds of rule");
}

/**
 * Return the offset and substring of |text| that starts at the given
 * line and column.
 * @param {String} text
 * @param {Number} line (1-indexed)
 * @param {Number} column (1-indexed)
 * @return {object} An object of the form {offset: number, text: string},
 *                  where the offset is the offset into the input string
 *                  where the text starts, and where text is the text.
 */
function getTextAtLineColumn(text, line, column) {
  let offset;
  if (line > 1) {
    let rx = new RegExp("(?:[^\\r\\n\\f]*(?:\\r\\n|\\n|\\r|\\f)){" + (line - 1) + "}");
    offset = rx.exec(text)[0].length;
  } else {
    offset = 0;
  }
  offset += column - 1;
  return {offset: offset, text: text.substr(offset) };
}

exports.getTextAtLineColumn = getTextAtLineColumn;
PK
!<+���k�k=chrome/devtools/modules/devtools/server/actors/stylesheets.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {Ci} = require("chrome");
const Services = require("Services");
const defer = require("devtools/shared/defer");
const protocol = require("devtools/shared/protocol");
const {LongStringActor} = require("devtools/server/actors/string");
const {fetch} = require("devtools/shared/DevToolsUtils");
const {mediaRuleSpec, styleSheetSpec,
       styleSheetsSpec} = require("devtools/shared/specs/stylesheets");
const {
  addPseudoClassLock, removePseudoClassLock } = require("devtools/server/actors/highlighters/utils/markup");
const InspectorUtils = require("InspectorUtils");

loader.lazyRequireGetter(this, "CssLogic", "devtools/shared/inspector/css-logic");
loader.lazyRequireGetter(this, "addPseudoClassLock",
  "devtools/server/actors/highlighters/utils/markup", true);
loader.lazyRequireGetter(this, "removePseudoClassLock",
  "devtools/server/actors/highlighters/utils/markup", true);
loader.lazyRequireGetter(this, "loadSheet", "devtools/shared/layout/utils", true);

var TRANSITION_PSEUDO_CLASS = ":-moz-styleeditor-transitioning";
var TRANSITION_DURATION_MS = 500;
var TRANSITION_BUFFER_MS = 1000;
var TRANSITION_RULE_SELECTOR =
`:root${TRANSITION_PSEUDO_CLASS}, :root${TRANSITION_PSEUDO_CLASS} *`;

var TRANSITION_SHEET = "data:text/css;charset=utf-8," + encodeURIComponent(`
  ${TRANSITION_RULE_SELECTOR} {
    transition-duration: ${TRANSITION_DURATION_MS}ms !important;
    transition-delay: 0ms !important;
    transition-timing-function: ease-out !important;
    transition-property: all !important;
  }
`);

// The possible kinds of style-applied events.
// UPDATE_PRESERVING_RULES means that the update is guaranteed to
// preserve the number and order of rules on the style sheet.
// UPDATE_GENERAL covers any other kind of change to the style sheet.
const UPDATE_PRESERVING_RULES = 0;
exports.UPDATE_PRESERVING_RULES = UPDATE_PRESERVING_RULES;
const UPDATE_GENERAL = 1;
exports.UPDATE_GENERAL = UPDATE_GENERAL;

// If the user edits a style sheet, we stash a copy of the edited text
// here, keyed by the style sheet.  This way, if the tools are closed
// and then reopened, the edited text will be available.  A weak map
// is used so that navigation by the user will eventually cause the
// edited text to be collected.
let modifiedStyleSheets = new WeakMap();

/**
 * A MediaRuleActor lives on the server and provides access to properties
 * of a DOM @media rule and emits events when it changes.
 */
var MediaRuleActor = protocol.ActorClassWithSpec(mediaRuleSpec, {
  get window() {
    return this.parentActor.window;
  },

  get document() {
    return this.window.document;
  },

  get matches() {
    return this.mql ? this.mql.matches : null;
  },

  initialize: function (mediaRule, parentActor) {
    protocol.Actor.prototype.initialize.call(this, null);

    this.rawRule = mediaRule;
    this.parentActor = parentActor;
    this.conn = this.parentActor.conn;

    this._matchesChange = this._matchesChange.bind(this);

    this.line = InspectorUtils.getRuleLine(mediaRule);
    this.column = InspectorUtils.getRuleColumn(mediaRule);

    try {
      this.mql = this.window.matchMedia(mediaRule.media.mediaText);
    } catch (e) {
      // Ignored
    }

    if (this.mql) {
      this.mql.addListener(this._matchesChange);
    }
  },

  destroy: function () {
    if (this.mql) {
      this.mql.removeListener(this._matchesChange);
    }

    protocol.Actor.prototype.destroy.call(this);
  },

  form: function (detail) {
    if (detail === "actorid") {
      return this.actorID;
    }

    let form = {
      actor: this.actorID,  // actorID is set when this is added to a pool
      mediaText: this.rawRule.media.mediaText,
      conditionText: this.rawRule.conditionText,
      matches: this.matches,
      line: this.line,
      column: this.column,
      parentStyleSheet: this.parentActor.actorID
    };

    return form;
  },

  _matchesChange: function () {
    this.emit("matches-change", this.matches);
  }
});

/**
 * A StyleSheetActor represents a stylesheet on the server.
 */
var StyleSheetActor = protocol.ActorClassWithSpec(styleSheetSpec, {
  toString: function () {
    return "[StyleSheetActor " + this.actorID + "]";
  },

  /**
   * Window of target
   */
  get window() {
    return this.parentActor.window;
  },

  /**
   * Document of target.
   */
  get document() {
    return this.window.document;
  },

  /**
   * StyleSheet's window.
   */
  get ownerWindow() {
    // eslint-disable-next-line mozilla/use-ownerGlobal
    return this.ownerDocument.defaultView;
  },

  get ownerNode() {
    return this.rawSheet.ownerNode;
  },

  /**
   * URL of underlying stylesheet.
   */
  get href() {
    return this.rawSheet.href;
  },

  /**
   * Returns the stylesheet href or the document href if the sheet is inline.
   */
  get safeHref() {
    let href = this.href;
    if (!href) {
      if (this.ownerNode.nodeType == this.ownerNode.DOCUMENT_NODE) {
        href = this.ownerNode.location.href;
      } else if (this.ownerNode.ownerDocument &&
                 this.ownerNode.ownerDocument.location) {
        href = this.ownerNode.ownerDocument.location.href;
      }
    }
    return href;
  },

  /**
   * Retrieve the index (order) of stylesheet in the document.
   *
   * @return number
   */
  get styleSheetIndex() {
    if (this._styleSheetIndex == -1) {
      for (let i = 0; i < this.document.styleSheets.length; i++) {
        if (this.document.styleSheets[i] == this.rawSheet) {
          this._styleSheetIndex = i;
          break;
        }
      }
    }
    return this._styleSheetIndex;
  },

  destroy: function () {
    if (this._transitionTimeout && this.window) {
      this.window.clearTimeout(this._transitionTimeout);
      removePseudoClassLock(
                   this.document.documentElement, TRANSITION_PSEUDO_CLASS);
    }
  },

  initialize: function (styleSheet, parentActor) {
    protocol.Actor.prototype.initialize.call(this, null);

    this.rawSheet = styleSheet;
    this.parentActor = parentActor;
    this.conn = this.parentActor.conn;

    // text and index are unknown until source load
    this.text = null;
    this._styleSheetIndex = -1;

    // When the style is imported, `styleSheet.ownerNode` is null,
    // so retrieve the topmost parent style sheet which has an ownerNode
    let parentStyleSheet = styleSheet;
    while (parentStyleSheet.parentStyleSheet) {
      parentStyleSheet = parentStyleSheet.parentStyleSheet;
    }
    // When the style is injected via nsIDOMWindowUtils.loadSheet, even
    // the parent style sheet has no owner, so default back to tab actor
    // document
    if (parentStyleSheet.ownerNode) {
      this.ownerDocument = parentStyleSheet.ownerNode.ownerDocument;
    } else {
      this.ownerDocument = parentActor.window;
    }
  },

  /**
   * Test whether this sheet has been modified by CSSOM.
   * @return {Boolean} true if changed by CSSOM.
   */
  hasRulesModifiedByCSSOM: function () {
    return InspectorUtils.hasRulesModifiedByCSSOM(this.rawSheet);
  },

  /**
   * Get the raw stylesheet's cssRules once the sheet has been loaded.
   *
   * @return {Promise}
   *         Promise that resolves with a CSSRuleList
   */
  getCSSRules: function () {
    let rules;
    try {
      rules = this.rawSheet.cssRules;
    } catch (e) {
      // sheet isn't loaded yet
    }

    if (rules) {
      return Promise.resolve(rules);
    }

    if (!this.ownerNode) {
      return Promise.resolve([]);
    }

    if (this._cssRules) {
      return this._cssRules;
    }

    let deferred = defer();

    let onSheetLoaded = (event) => {
      this.ownerNode.removeEventListener("load", onSheetLoaded);

      deferred.resolve(this.rawSheet.cssRules);
    };

    this.ownerNode.addEventListener("load", onSheetLoaded);

    // cache so we don't add many listeners if this is called multiple times.
    this._cssRules = deferred.promise;

    return this._cssRules;
  },

  /**
   * Get the current state of the actor
   *
   * @return {object}
   *         With properties of the underlying stylesheet, plus 'text',
   *        'styleSheetIndex' and 'parentActor' if it's @imported
   */
  form: function (detail) {
    if (detail === "actorid") {
      return this.actorID;
    }

    let docHref;
    if (this.ownerNode) {
      if (this.ownerNode.nodeType == this.ownerNode.DOCUMENT_NODE) {
        docHref = this.ownerNode.location.href;
      } else if (this.ownerNode.ownerDocument && this.ownerNode.ownerDocument.location) {
        docHref = this.ownerNode.ownerDocument.location.href;
      }
    }

    let form = {
      actor: this.actorID,  // actorID is set when this actor is added to a pool
      href: this.href,
      nodeHref: docHref,
      disabled: this.rawSheet.disabled,
      title: this.rawSheet.title,
      system: !CssLogic.isContentStylesheet(this.rawSheet),
      styleSheetIndex: this.styleSheetIndex,
      sourceMapURL: this.rawSheet.sourceMapURL,
    };

    try {
      form.ruleCount = this.rawSheet.cssRules.length;
    } catch (e) {
      // stylesheet had an @import rule that wasn't loaded yet
      this.getCSSRules().then(() => {
        this._notifyPropertyChanged("ruleCount");
      });
    }
    return form;
  },

  /**
   * Toggle the disabled property of the style sheet
   *
   * @return {object}
   *         'disabled' - the disabled state after toggling.
   */
  toggleDisabled: function () {
    this.rawSheet.disabled = !this.rawSheet.disabled;
    this._notifyPropertyChanged("disabled");

    return this.rawSheet.disabled;
  },

  /**
   * Send an event notifying that a property of the stylesheet
   * has changed.
   *
   * @param  {string} property
   *         Name of the changed property
   */
  _notifyPropertyChanged: function (property) {
    this.emit("property-change", property, this.form()[property]);
  },

  /**
   * Protocol method to get the text of this stylesheet.
   */
  getText: function () {
    return this._getText().then((text) => {
      return new LongStringActor(this.conn, text || "");
    });
  },

  /**
   * Fetch the text for this stylesheet from the cache or network. Return
   * cached text if it's already been fetched.
   *
   * @return {Promise}
   *         Promise that resolves with a string text of the stylesheet.
   */
  _getText: function () {
    if (typeof this.text === "string") {
      return Promise.resolve(this.text);
    }

    let cssText = modifiedStyleSheets.get(this.rawSheet);
    if (cssText !== undefined) {
      this.text = cssText;
      return Promise.resolve(cssText);
    }

    if (!this.href) {
      // this is an inline <style> sheet
      let content = this.ownerNode.textContent;
      this.text = content;
      return Promise.resolve(content);
    }

    return this.fetchStylesheet(this.href).then(({ content }) => {
      this.text = content;
      return content;
    });
  },

  /**
   * Fetch a stylesheet at the provided URL. Returns a promise that will resolve the
   * result of the fetch command.
   *
   * @param  {String} href
   *         The href of the stylesheet to retrieve.
   * @return {Promise} a promise that resolves with an object with the following members
   *         on success:
   *           - content: the document at that URL, as a string,
   *           - contentType: the content type of the document
   *         If an error occurs, the promise is rejected with that error.
   */
  async fetchStylesheet(href) {
    // Check if network monitor observed this load, and if so, use that.
    let result = this.fetchStylesheetFromNetworkMonitor(href);
    if (result) {
      return result;
    }

    let options = {
      loadFromCache: true,
      policy: Ci.nsIContentPolicy.TYPE_INTERNAL_STYLESHEET,
      charset: this._getCSSCharset()
    };

    // Bug 1282660 - We use the system principal to load the default internal
    // stylesheets instead of the content principal since such stylesheets
    // require system principal to load. At meanwhile, we strip the loadGroup
    // for preventing the assertion of the userContextId mismatching.

    // chrome|file|resource|moz-extension protocols rely on the system principal.
    let excludedProtocolsRe = /^(chrome|file|resource|moz-extension):\/\//;
    if (!excludedProtocolsRe.test(this.href)) {
      // Stylesheets using other protocols should use the content principal.
      options.window = this.ownerWindow;
      options.principal = this.ownerDocument.nodePrincipal;
    }

    try {
      result = await fetch(this.href, options);
    } catch (e) {
      // The list of excluded protocols can be missing some protocols, try to use the
      // system principal if the first fetch failed.
      console.error(`stylesheets actor: fetch failed for ${this.href},` +
        ` using system principal instead.`);
      options.window = undefined;
      options.principal = undefined;
      result = await fetch(this.href, options);
    }

    return result;
  },

  /**
   * Try to locate the console actor if it exists via our parent actor (the tab).
   */
  get _consoleActor() {
    if (this.parentActor.exited) {
      return null;
    }
    let form = this.parentActor.form();
    return this.conn._getOrCreateActor(form.consoleActor);
  },

  /**
   * Try to fetch the stylesheet text from the network monitor.  If it was enabled during
   * the load, it should have a copy of the text saved.
   *
   * @param string href
   *        The URL of the sheet to fetch.
   */
  fetchStylesheetFromNetworkMonitor(href) {
    let consoleActor = this._consoleActor;
    if (!consoleActor) {
      return null;
    }
    let request = consoleActor.getNetworkEventActorForURL(href);
    if (!request) {
      return null;
    }
    let content = request._response.content;
    if (request._discardResponseBody || request._truncated || !content) {
      return null;
    }
    if (content.text.type != "longString") {
      // For short strings, the text is available directly.
      return {
        content: content.text,
        contentType: content.mimeType,
      };
    }
    // For long strings, look up the actor that holds the full text.
    let longStringActor = this.conn._getOrCreateActor(content.text.actor);
    if (!longStringActor) {
      return null;
    }
    return {
      content: longStringActor.rawValue(),
      contentType: content.mimeType,
    };
  },

  /**
   * Protocol method to get the media rules for the stylesheet.
   */
  getMediaRules: function () {
    return this._getMediaRules();
  },

  /**
   * Get all the @media rules in this stylesheet.
   *
   * @return {promise}
   *         A promise that resolves with an array of MediaRuleActors.
   */
  _getMediaRules: function () {
    return this.getCSSRules().then((rules) => {
      let mediaRules = [];
      for (let i = 0; i < rules.length; i++) {
        let rule = rules[i];
        if (rule.type != CSSRule.MEDIA_RULE) {
          continue;
        }
        let actor = new MediaRuleActor(rule, this);
        this.manage(actor);

        mediaRules.push(actor);
      }
      return mediaRules;
    });
  },

  /**
   * Get the charset of the stylesheet according to the character set rules
   * defined in <http://www.w3.org/TR/CSS2/syndata.html#charset>.
   * Note that some of the algorithm is implemented in DevToolsUtils.fetch.
   */
  _getCSSCharset: function () {
    let sheet = this.rawSheet;
    if (sheet) {
      // Do we have a @charset rule in the stylesheet?
      // step 2 of syndata.html (without the BOM check).
      if (sheet.cssRules) {
        let rules = sheet.cssRules;
        if (rules.length
            && rules.item(0).type == CSSRule.CHARSET_RULE) {
          return rules.item(0).encoding;
        }
      }

      // step 3: charset attribute of <link> or <style> element, if it exists
      if (sheet.ownerNode && sheet.ownerNode.getAttribute) {
        let linkCharset = sheet.ownerNode.getAttribute("charset");
        if (linkCharset != null) {
          return linkCharset;
        }
      }

      // step 4 (1 of 2): charset of referring stylesheet.
      let parentSheet = sheet.parentStyleSheet;
      if (parentSheet && parentSheet.cssRules &&
          parentSheet.cssRules[0].type == CSSRule.CHARSET_RULE) {
        return parentSheet.cssRules[0].encoding;
      }

      // step 4 (2 of 2): charset of referring document.
      if (sheet.ownerNode && sheet.ownerNode.ownerDocument.characterSet) {
        return sheet.ownerNode.ownerDocument.characterSet;
      }
    }

    // step 5: default to utf-8.
    return "UTF-8";
  },

  /**
   * Update the style sheet in place with new text.
   *
   * @param  {object} request
   *         'text' - new text
   *         'transition' - whether to do CSS transition for change.
   *         'kind' - either UPDATE_PRESERVING_RULES or UPDATE_GENERAL
   */
  update: function (text, transition, kind = UPDATE_GENERAL) {
    InspectorUtils.parseStyleSheet(this.rawSheet, text);

    modifiedStyleSheets.set(this.rawSheet, text);

    this.text = text;

    if (kind != UPDATE_PRESERVING_RULES) {
      this._notifyPropertyChanged("ruleCount");
    }

    if (transition) {
      this._startTransition(kind);
    } else {
      this.emit("style-applied", kind, this);
    }

    this._getMediaRules().then((rules) => {
      this.emit("media-rules-changed", rules);
    });
  },

  /**
   * Insert a catch-all transition sheet into the document. Set a timeout
   * to remove the transition after a certain time.
   */
  _startTransition: function (kind) {
    if (!this._transitionSheetLoaded) {
      this._transitionSheetLoaded = true;
      // We don't remove this sheet. It uses an internal selector that
      // we only apply via locks, so there's no need to load and unload
      // it all the time.
      loadSheet(this.window, TRANSITION_SHEET);
    }

    addPseudoClassLock(this.document.documentElement, TRANSITION_PSEUDO_CLASS);

    // Set up clean up and commit after transition duration (+buffer)
    // @see _onTransitionEnd
    this.window.clearTimeout(this._transitionTimeout);
    this._transitionTimeout = this.window.setTimeout(
      this._onTransitionEnd.bind(this, kind),
      TRANSITION_DURATION_MS + TRANSITION_BUFFER_MS);
  },

  /**
   * This cleans up class and rule added for transition effect and then
   * notifies that the style has been applied.
   */
  _onTransitionEnd: function (kind) {
    this._transitionTimeout = null;
    removePseudoClassLock(this.document.documentElement, TRANSITION_PSEUDO_CLASS);
    this.emit("style-applied", kind, this);
  }
});

exports.StyleSheetActor = StyleSheetActor;

/**
 * Creates a StyleSheetsActor. StyleSheetsActor provides remote access to the
 * stylesheets of a document.
 */
var StyleSheetsActor = protocol.ActorClassWithSpec(styleSheetsSpec, {
  /**
   * The window we work with, taken from the parent actor.
   */
  get window() {
    return this.parentActor.window;
  },

  /**
   * The current content document of the window we work with.
   */
  get document() {
    return this.window.document;
  },

  form: function () {
    return { actor: this.actorID };
  },

  initialize: function (conn, tabActor) {
    protocol.Actor.prototype.initialize.call(this, null);

    this.parentActor = tabActor;

    this._onNewStyleSheetActor = this._onNewStyleSheetActor.bind(this);
    this._onSheetAdded = this._onSheetAdded.bind(this);
    this._onWindowReady = this._onWindowReady.bind(this);
    this._transitionSheetLoaded = false;

    this.parentActor.on("stylesheet-added", this._onNewStyleSheetActor);
    this.parentActor.on("window-ready", this._onWindowReady);

    // We listen for StyleSheetApplicableStateChanged rather than
    // StyleSheetAdded, because the latter will be sent before the
    // rules are ready.  Using the former (with a check to ensure that
    // the sheet is enabled) ensures that the sheet is ready before we
    // try to make an actor for it.
    this.parentActor.chromeEventHandler
      .addEventListener("StyleSheetApplicableStateChanged", this._onSheetAdded, true);

    // This is used when creating a new style sheet, so that we can
    // pass the correct flag when emitting our stylesheet-added event.
    // See addStyleSheet and _onNewStyleSheetActor for more details.
    this._nextStyleSheetIsNew = false;
  },

  destroy: function () {
    for (let win of this.parentActor.windows) {
      // This flag only exists for devtools, so we are free to clear
      // it when we're done.
      win.document.styleSheetChangeEventsEnabled = false;
    }

    this.parentActor.off("stylesheet-added", this._onNewStyleSheetActor);
    this.parentActor.off("window-ready", this._onWindowReady);

    this.parentActor.chromeEventHandler.removeEventListener("StyleSheetAdded",
                                                            this._onSheetAdded, true);

    protocol.Actor.prototype.destroy.call(this);
  },

  /**
   * Event handler that is called when a the tab actor emits window-ready.
   *
   * @param {Event} evt
   *        The triggering event.
   */
  _onWindowReady: function (evt) {
    this._addStyleSheets(evt.window);
  },

  /**
   * Event handler that is called when a the tab actor emits stylesheet-added.
   *
   * @param {StyleSheetActor} actor
   *        The new style sheet actor.
   */
  _onNewStyleSheetActor: function (actor) {
    // Forward it to the client side.
    this.emit("stylesheet-added", actor, this._nextStyleSheetIsNew);
    this._nextStyleSheetIsNew = false;
  },

  /**
   * Protocol method for getting a list of StyleSheetActors representing
   * all the style sheets in this document.
   */
  async getStyleSheets() {
    let actors = [];

    for (let win of this.parentActor.windows) {
      let sheets = await this._addStyleSheets(win);
      actors = actors.concat(sheets);
    }
    return actors;
  },

  /**
   * Check if we should be showing this stylesheet.
   *
   * @param {DOMCSSStyleSheet} sheet
   *        Stylesheet we're interested in
   *
   * @return boolean
   *         Whether the stylesheet should be listed.
   */
  _shouldListSheet: function (sheet) {
    // Special case about:PreferenceStyleSheet, as it is generated on the
    // fly and the URI is not registered with the about: handler.
    // https://bugzilla.mozilla.org/show_bug.cgi?id=935803#c37
    if (sheet.href && sheet.href.toLowerCase() == "about:preferencestylesheet") {
      return false;
    }

    return true;
  },

  /**
   * Event handler that is called when a new style sheet is added to
   * a document.  In particular,  StyleSheetApplicableStateChanged is
   * listened for, because StyleSheetAdded is sent too early, before
   * the rules are ready.
   *
   * @param {Event} evt
   *        The triggering event.
   */
  _onSheetAdded: function (evt) {
    let sheet = evt.stylesheet;
    if (this._shouldListSheet(sheet) && !this._haveAncestorWithSameURL(sheet)) {
      this.parentActor.createStyleSheetActor(sheet);
    }
  },

  /**
   * Add all the stylesheets for the document in this window to the map and
   * create an actor for each one if not already created.
   *
   * @param {Window} win
   *        Window for which to add stylesheets
   *
   * @return {Promise}
   *         Promise that resolves to an array of StyleSheetActors
   */
  _addStyleSheets: function (win) {
    return (async function () {
      let doc = win.document;
      // We have to set this flag in order to get the
      // StyleSheetApplicableStateChanged events.  See Document.webidl.
      doc.styleSheetChangeEventsEnabled = true;

      let isChrome = Services.scriptSecurityManager.isSystemPrincipal(doc.nodePrincipal);
      let styleSheets =
        isChrome ? InspectorUtils.getAllStyleSheets(doc) : doc.styleSheets;
      let actors = [];
      for (let i = 0; i < styleSheets.length; i++) {
        let sheet = styleSheets[i];
        if (!this._shouldListSheet(sheet)) {
          continue;
        }

        let actor = this.parentActor.createStyleSheetActor(sheet);
        actors.push(actor);

        // Get all sheets, including imported ones
        let imports = await this._getImported(doc, actor);
        actors = actors.concat(imports);
      }
      return actors;
    }.bind(this))();
  },

  /**
   * Get all the stylesheets @imported from a stylesheet.
   *
   * @param  {Document} doc
   *         The document including the stylesheet
   * @param  {DOMStyleSheet} styleSheet
   *         Style sheet to search
   * @return {Promise}
   *         A promise that resolves with an array of StyleSheetActors
   */
  _getImported: function (doc, styleSheet) {
    return (async function () {
      let rules = await styleSheet.getCSSRules();
      let imported = [];

      for (let i = 0; i < rules.length; i++) {
        let rule = rules[i];
        if (rule.type == CSSRule.IMPORT_RULE) {
          // With the Gecko style system, the associated styleSheet may be null
          // if it has already been seen because an import cycle for the same
          // URL.  With Stylo, the styleSheet will exist (which is correct per
          // the latest CSSOM spec), so we also need to check ancestors for the
          // same URL to avoid cycles.
          let sheet = rule.styleSheet;
          if (!sheet || this._haveAncestorWithSameURL(sheet) ||
              !this._shouldListSheet(sheet)) {
            continue;
          }
          let actor = this.parentActor.createStyleSheetActor(rule.styleSheet);
          imported.push(actor);

          // recurse imports in this stylesheet as well
          let children = await this._getImported(doc, actor);
          imported = imported.concat(children);
        } else if (rule.type != CSSRule.CHARSET_RULE) {
          // @import rules must precede all others except @charset
          break;
        }
      }

      return imported;
    }.bind(this))();
  },

  /**
   * Check all ancestors to see if this sheet's URL matches theirs as a way to
   * detect an import cycle.
   *
   * @param {DOMStyleSheet} sheet
   */
  _haveAncestorWithSameURL(sheet) {
    let sheetHref = sheet.href;
    while (sheet.parentStyleSheet) {
      if (sheet.parentStyleSheet.href == sheetHref) {
        return true;
      }
      sheet = sheet.parentStyleSheet;
    }
    return false;
  },

  /**
   * Create a new style sheet in the document with the given text.
   * Return an actor for it.
   *
   * @param  {object} request
   *         Debugging protocol request object, with 'text property'
   * @return {object}
   *         Object with 'styelSheet' property for form on new actor.
   */
  addStyleSheet: function (text) {
    // This is a bit convoluted.  The style sheet actor may be created
    // by a notification from platform.  In this case, we can't easily
    // pass the "new" flag through to createStyleSheetActor, so we set
    // a flag locally and check it before sending an event to the
    // client.  See |_onNewStyleSheetActor|.
    this._nextStyleSheetIsNew = true;

    let parent = this.document.documentElement;
    let style = this.document.createElementNS("http://www.w3.org/1999/xhtml", "style");
    style.setAttribute("type", "text/css");

    if (text) {
      style.appendChild(this.document.createTextNode(text));
    }
    parent.appendChild(style);

    let actor = this.parentActor.createStyleSheetActor(style.sheet);
    return actor;
  }
});

exports.StyleSheetsActor = StyleSheetsActor;
PK
!<'�����5chrome/devtools/modules/devtools/server/actors/tab.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

/* global XPCNativeWrapper */

// For performance matters, this file should only be loaded in the targeted
// document process. For example, it shouldn't be evaluated in the parent
// process until we try to debug a document living in the parent process.

var { Ci, Cu, Cr, Cc } = require("chrome");
var Services = require("Services");
var { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
var {
  ActorPool, createExtraActors, appendExtraActors
} = require("devtools/server/actors/common");
var { DebuggerServer } = require("devtools/server/main");
var DevToolsUtils = require("devtools/shared/DevToolsUtils");
var { assert } = DevToolsUtils;
var { TabSources } = require("./utils/TabSources");
var makeDebugger = require("./utils/make-debugger");
const EventEmitter = require("devtools/shared/event-emitter");

const EXTENSION_CONTENT_JSM = "resource://gre/modules/ExtensionContent.jsm";

loader.lazyRequireGetter(this, "ThreadActor", "devtools/server/actors/thread", true);
loader.lazyRequireGetter(this, "unwrapDebuggerObjectGlobal", "devtools/server/actors/thread", true);
loader.lazyRequireGetter(this, "WorkerActorList", "devtools/server/actors/worker-list", true);
loader.lazyImporter(this, "ExtensionContent", EXTENSION_CONTENT_JSM);

loader.lazyRequireGetter(this, "StyleSheetActor", "devtools/server/actors/stylesheets", true);

function getWindowID(window) {
  return window.QueryInterface(Ci.nsIInterfaceRequestor)
               .getInterface(Ci.nsIDOMWindowUtils)
               .currentInnerWindowID;
}

function getDocShellChromeEventHandler(docShell) {
  let handler = docShell.chromeEventHandler;
  if (!handler) {
    try {
      // Toplevel xul window's docshell doesn't have chromeEventHandler
      // attribute. The chrome event handler is just the global window object.
      handler = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                        .getInterface(Ci.nsIDOMWindow);
    } catch (e) {
      // ignore
    }
  }
  return handler;
}

function getChildDocShells(parentDocShell) {
  let docShellsEnum = parentDocShell.getDocShellEnumerator(
    Ci.nsIDocShellTreeItem.typeAll,
    Ci.nsIDocShell.ENUMERATE_FORWARDS
  );

  let docShells = [];
  while (docShellsEnum.hasMoreElements()) {
    let docShell = docShellsEnum.getNext();
    docShell.QueryInterface(Ci.nsIInterfaceRequestor)
            .getInterface(Ci.nsIWebProgress);
    docShells.push(docShell);
  }
  return docShells;
}

exports.getChildDocShells = getChildDocShells;

/**
 * Browser-specific actors.
 */

function getInnerId(window) {
  return window.QueryInterface(Ci.nsIInterfaceRequestor)
               .getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
}

/**
 * Creates a TabActor whose main goal is to manage lifetime and
 * expose the tab actors being registered via DebuggerServer.registerModule.
 * But also track the lifetime of the document being tracked.
 *
 * ### Main requests:
 *
 * `attach`/`detach` requests:
 *  - start/stop document watching:
 *    Starts watching for new documents and emits `tabNavigated` and
 *    `frameUpdate` over RDP.
 *  - retrieve the thread actor:
 *    Instantiates a ThreadActor that can be later attached to in order to
 *    debug JS sources in the document.
 * `switchToFrame`:
 *  Change the targeted document of the whole TabActor, and its child tab actors
 *  to an iframe or back to its original document.
 *
 * Most of the TabActor properties (like `chromeEventHandler` or `docShells`)
 * are meant to be used by the various child tab actors.
 *
 * ### RDP events:
 *
 *  - `tabNavigated`:
 *    Sent when the tab is about to navigate or has just navigated to
 *    a different document.
 *    This event contains the following attributes:
 *     * url (string) The new URI being loaded.
 *     * nativeConsoleAPI (boolean) `false` if the console API of the page has
 *                                          been overridden (e.g. by Firebug),
 *                                  `true`  if the Gecko implementation is used.
 *     * state (string) `start` if we just start requesting the new URL,
 *                      `stop`  if the new URL is done loading.
 *     * isFrameSwitching (boolean) Indicates the event is dispatched when
 *                                  switching the TabActor context to
 *                                  a different frame. When we switch to
 *                                  an iframe, there is no document load.
 *                                  The targeted document is most likely
 *                                  going to be already done loading.
 *     * title (string) The document title being loaded.
 *                      (sent only on state=stop)
 *
 *  - `frameUpdate`:
 *    Sent when there was a change in the child frames contained in the document
 *    or when the tab's context was switched to another frame.
 *    This event can have four different forms depending on the type of change:
 *    * One or many frames are updated:
 *      { frames: [{ id, url, title, parentID }, ...] }
 *    * One frame got destroyed:
 *      { frames: [{ id, destroy: true }]}
 *    * All frames got destroyed:
 *      { destroyAll: true }
 *    * We switched the context of the TabActor to a specific frame:
 *      { selected: #id }
 *
 * ### Internal, non-rdp events:
 * Various events are also dispatched on the TabActor itself that are not
 * related to RDP, so, not sent to the client. They all relate to the documents
 * tracked by the TabActor (its main targeted document, but also any of its
 * iframes).
 *  - will-navigate
 *    This event fires once navigation starts.
 *    All pending user prompts are dealt with,
 *    but it is fired before the first request starts.
 *  - navigate
 *    This event is fired once the document's readyState is "complete".
 *  - window-ready
 *    This event is fired in various distinct scenarios:
 *     * When a new Window object is crafted, equivalent of `DOMWindowCreated`.
 *       It is dispatched before any page script is executed.
 *     * We will have already received a window-ready event for this window
 *       when it was created, but we received a window-destroyed event when
 *       it was frozen into the bfcache, and now the user navigated back to
 *       this page, so it's now live again and we should resume handling it.
 *     * For each existing document, when an `attach` request is received.
 *       At this point scripts in the page will be already loaded.
 *     * When `swapFrameLoaders` is used, such as with moving tabs between
 *       windows or toggling Responsive Design Mode.
 *  - window-destroyed
 *    This event is fired in two cases:
 *     * When the window object is destroyed, i.e. when the related document
 *       is garbage collected. This can happen when the tab is closed or the
 *       iframe is removed from the DOM.
 *       It is equivalent of `inner-window-destroyed` event.
 *     * When the page goes into the bfcache and gets frozen.
 *       The equivalent of `pagehide`.
 *  - changed-toplevel-document
 *    This event fires when we switch the TabActor targeted document
 *    to one of its iframes, or back to its original top document.
 *    It is dispatched between window-destroyed and window-ready.
 *  - stylesheet-added
 *    This event is fired when a StyleSheetActor is created.
 *    It contains the following attribute :
 *     * actor (StyleSheetActor) The created actor.
 *
 * Note that *all* these events are dispatched in the following order
 * when we switch the context of the TabActor to a given iframe:
 *  - will-navigate
 *  - window-destroyed
 *  - changed-toplevel-document
 *  - window-ready
 *  - navigate
 *
 * This class is subclassed by ContentActor and others.
 * Subclasses are expected to implement a getter for the docShell property.
 *
 * @param connection DebuggerServerConnection
 *        The conection to the client.
 */
function TabActor(connection) {
  // This usage of decorate should be removed in favor of using ES6 extends EventEmitter.
  // See Bug 1394816.
  EventEmitter.decorate(this);

  this.conn = connection;
  this._tabActorPool = null;
  // A map of actor names to actor instances provided by extensions.
  this._extraActors = {};
  this._exited = false;
  this._sources = null;

  // Map of DOM stylesheets to StyleSheetActors
  this._styleSheetActors = new Map();

  this._shouldAddNewGlobalAsDebuggee =
    this._shouldAddNewGlobalAsDebuggee.bind(this);

  this.makeDebugger = makeDebugger.bind(null, {
    findDebuggees: () => {
      return this.windows.concat(this.webextensionsContentScriptGlobals);
    },
    shouldAddNewGlobalAsDebuggee: this._shouldAddNewGlobalAsDebuggee
  });

  // Flag eventually overloaded by sub classes in order to watch new docshells
  // Used by the ChromeActor to list all frames in the Browser Toolbox
  this.listenForNewDocShells = false;

  this.traits = {
    reconfigure: true,
    // Supports frame listing via `listFrames` request and `frameUpdate` events
    // as well as frame switching via `switchToFrame` request
    frames: true,
    // Do not require to send reconfigure request to reset the document state
    // to what it was before using the TabActor
    noTabReconfigureOnClose: true,
    // Supports the logInPage request.
    logInPage: true,
  };

  this._workerActorList = null;
  this._workerActorPool = null;
  this._onWorkerActorListChanged = this._onWorkerActorListChanged.bind(this);
}

TabActor.prototype = {
  traits: null,

  // Optional console API listener options (e.g. used by the WebExtensionActor to
  // filter console messages by addonID), set to an empty (no options) object by default.
  consoleAPIListenerOptions: {},

  // Optional TabSources filter function (e.g. used by the WebExtensionActor to filter
  // sources by addonID), allow all sources by default.
  _allowSource() {
    return true;
  },

  get exited() {
    return this._exited;
  },

  get attached() {
    return !!this._attached;
  },

  _tabPool: null,
  get tabActorPool() {
    return this._tabPool;
  },

  _contextPool: null,
  get contextActorPool() {
    return this._contextPool;
  },

  // A constant prefix that will be used to form the actor ID by the server.
  actorPrefix: "tab",

  /**
   * An object on which listen for DOMWindowCreated and pageshow events.
   */
  get chromeEventHandler() {
    return getDocShellChromeEventHandler(this.docShell);
  },

  /**
   * Getter for the nsIMessageManager associated to the tab.
   */
  get messageManager() {
    try {
      return this.docShell
        .QueryInterface(Ci.nsIInterfaceRequestor)
        .getInterface(Ci.nsIContentFrameMessageManager);
    } catch (e) {
      return null;
    }
  },

  /**
   * Getter for the tab's doc shell.
   */
  get docShell() {
    throw new Error(
      "The docShell getter should be implemented by a subclass of TabActor");
  },

  /**
   * Getter for the list of all docshell in this tabActor
   * @return {Array}
   */
  get docShells() {
    return getChildDocShells(this.docShell);
  },

  /**
   * Getter for the tab content's DOM window.
   */
  get window() {
    // On xpcshell, there is no document
    if (this.docShell) {
      return this.docShell
        .QueryInterface(Ci.nsIInterfaceRequestor)
        .getInterface(Ci.nsIDOMWindow);
    }
    return null;
  },

  get outerWindowID() {
    if (this.window) {
      return this.window.QueryInterface(Ci.nsIInterfaceRequestor)
                        .getInterface(Ci.nsIDOMWindowUtils)
                        .outerWindowID;
    }
    return null;
  },

  /**
   * Getter for the WebExtensions ContentScript globals related to the
   * current tab content's DOM window.
   */
  get webextensionsContentScriptGlobals() {
    // Ignore xpcshell runtime which spawn TabActors without a window
    // and only retrieve the content scripts globals if the ExtensionContent JSM module
    // has been already loaded (which is true if the WebExtensions internals have already
    // been loaded in the same content process).
    if (this.window && Cu.isModuleLoaded(EXTENSION_CONTENT_JSM)) {
      return ExtensionContent.getContentScriptGlobals(this.window);
    }

    return [];
  },

  /**
   * Getter for the list of all content DOM windows in this tabActor
   * @return {Array}
   */
  get windows() {
    return this.docShells.map(docShell => {
      return docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                     .getInterface(Ci.nsIDOMWindow);
    });
  },

  /**
   * Getter for the original docShell the tabActor got attached to in the first
   * place.
   * Note that your actor should normally *not* rely on this top level docShell
   * if you want it to show information relative to the iframe that's currently
   * being inspected in the toolbox.
   */
  get originalDocShell() {
    if (!this._originalWindow) {
      return this.docShell;
    }

    return this._originalWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                               .getInterface(Ci.nsIWebNavigation)
                               .QueryInterface(Ci.nsIDocShell);
  },

  /**
   * Getter for the original window the tabActor got attached to in the first
   * place.
   * Note that your actor should normally *not* rely on this top level window if
   * you want it to show information relative to the iframe that's currently
   * being inspected in the toolbox.
   */
  get originalWindow() {
    return this._originalWindow || this.window;
  },

  /**
   * Getter for the nsIWebProgress for watching this window.
   */
  get webProgress() {
    return this.docShell
      .QueryInterface(Ci.nsIInterfaceRequestor)
      .getInterface(Ci.nsIWebProgress);
  },

  /**
   * Getter for the nsIWebNavigation for the tab.
   */
  get webNavigation() {
    return this.docShell
      .QueryInterface(Ci.nsIInterfaceRequestor)
      .getInterface(Ci.nsIWebNavigation);
  },

  /**
   * Getter for the tab's document.
   */
  get contentDocument() {
    return this.webNavigation.document;
  },

  /**
   * Getter for the tab title.
   * @return string
   *         Tab title.
   */
  get title() {
    return this.contentDocument.contentTitle;
  },

  /**
   * Getter for the tab URL.
   * @return string
   *         Tab URL.
   */
  get url() {
    if (this.webNavigation.currentURI) {
      return this.webNavigation.currentURI.spec;
    }
    // Abrupt closing of the browser window may leave callbacks without a
    // currentURI.
    return null;
  },

  get sources() {
    if (!this._sources) {
      this._sources = new TabSources(this.threadActor, this._allowSource);
    }
    return this._sources;
  },

  form() {
    assert(!this.exited,
               "form() shouldn't be called on exited browser actor.");
    assert(this.actorID,
               "tab should have an actorID.");

    let response = {
      actor: this.actorID
    };

    // We may try to access window while the document is closing, then
    // accessing window throws. Also on xpcshell we are using tabactor even if
    // there is no valid document.
    if (this.docShell && !this.docShell.isBeingDestroyed()) {
      response.title = this.title;
      response.url = this.url;
      response.outerWindowID = this.outerWindowID;
    }

    // Always use the same ActorPool, so existing actor instances
    // (created in createExtraActors) are not lost.
    if (!this._tabActorPool) {
      this._tabActorPool = new ActorPool(this.conn);
      this.conn.addActorPool(this._tabActorPool);
    }

    // Walk over tab actor factories and make sure they are all
    // instantiated and added into the ActorPool. Note that some
    // factories can be added dynamically by extensions.
    this._createExtraActors(DebuggerServer.tabActorFactories,
      this._tabActorPool);

    this._appendExtraActors(response);
    return response;
  },

  /**
   * Called when the actor is removed from the connection.
   */
  destroy() {
    this.exit();
  },

  /**
   * Called by the root actor when the underlying tab is closed.
   */
  exit() {
    if (this.exited) {
      return;
    }

    // Tell the thread actor that the tab is closed, so that it may terminate
    // instead of resuming the debuggee script.
    if (this._attached) {
      this.threadActor._tabClosed = true;
    }

    this._detach();

    Object.defineProperty(this, "docShell", {
      value: null,
      configurable: true
    });

    this._extraActors = null;

    this._exited = true;
  },

  /**
   * Return true if the given global is associated with this tab and should be
   * added as a debuggee, false otherwise.
   */
  _shouldAddNewGlobalAsDebuggee(wrappedGlobal) {
    if (wrappedGlobal.hostAnnotations &&
        wrappedGlobal.hostAnnotations.type == "document" &&
        wrappedGlobal.hostAnnotations.element === this.window) {
      return true;
    }

    // Otherwise, check if it is a WebExtension content script sandbox
    let global = unwrapDebuggerObjectGlobal(wrappedGlobal);
    if (!global) {
      return false;
    }

    // Check if the global is a sdk page-mod sandbox.
    let metadata = {};
    let id = "";
    try {
      id = getInnerId(this.window);
      metadata = Cu.getSandboxMetadata(global);
    } catch (e) {
      // ignore
    }
    if (metadata
        && metadata["inner-window-id"]
        && metadata["inner-window-id"] == id) {
      return true;
    }

    return false;
  },

  /* Support for DebuggerServer.addTabActor. */
  _createExtraActors: createExtraActors,
  _appendExtraActors: appendExtraActors,

  /**
   * Does the actual work of attaching to a tab.
   */
  _attach() {
    if (this._attached) {
      return;
    }

    // Create a pool for tab-lifetime actors.
    assert(!this._tabPool, "Shouldn't have a tab pool if we weren't attached.");
    this._tabPool = new ActorPool(this.conn);
    this.conn.addActorPool(this._tabPool);

    // ... and a pool for context-lifetime actors.
    this._pushContext();

    // on xpcshell, there is no document
    if (this.window) {
      this._progressListener = new DebuggerProgressListener(this);

      // Save references to the original document we attached to
      this._originalWindow = this.window;

      // Ensure replying to attach() request first
      // before notifying about new docshells.
      DevToolsUtils.executeSoon(() => this._watchDocshells());
    }

    this._attached = true;
  },

  _watchDocshells() {
    // In child processes, we watch all docshells living in the process.
    if (this.listenForNewDocShells) {
      Services.obs.addObserver(this, "webnavigation-create");
    }
    Services.obs.addObserver(this, "webnavigation-destroy");

    // We watch for all child docshells under the current document,
    this._progressListener.watch(this.docShell);

    // And list all already existing ones.
    this._updateChildDocShells();
  },

  _unwatchDocShell(docShell) {
    if (this._progressListener) {
      this._progressListener.unwatch(docShell);
    }
  },

  onSwitchToFrame(request) {
    let windowId = request.windowId;
    let win;

    try {
      win = Services.wm.getOuterWindowWithId(windowId);
    } catch (e) {
      // ignore
    }
    if (!win) {
      return { error: "noWindow",
               message: "The related docshell is destroyed or not found" };
    } else if (win == this.window) {
      return {};
    }

    // Reply first before changing the document
    DevToolsUtils.executeSoon(() => this._changeTopLevelDocument(win));

    return {};
  },

  onListFrames(request) {
    let windows = this._docShellsToWindows(this.docShells);
    return { frames: windows };
  },

  onListWorkers(request) {
    if (!this.attached) {
      return { error: "wrongState" };
    }

    if (this._workerActorList === null) {
      this._workerActorList = new WorkerActorList(this.conn, {
        type: Ci.nsIWorkerDebugger.TYPE_DEDICATED,
        window: this.window
      });
    }

    return this._workerActorList.getList().then((actors) => {
      let pool = new ActorPool(this.conn);
      for (let actor of actors) {
        pool.addActor(actor);
      }

      this.conn.removeActorPool(this._workerActorPool);
      this._workerActorPool = pool;
      this.conn.addActorPool(this._workerActorPool);

      this._workerActorList.onListChanged = this._onWorkerActorListChanged;

      return {
        "from": this.actorID,
        "workers": actors.map((actor) => actor.form())
      };
    });
  },

  onLogInPage(request) {
    let {text, category, flags} = request;
    let scriptErrorClass = Cc["@mozilla.org/scripterror;1"];
    let scriptError = scriptErrorClass.createInstance(Ci.nsIScriptError);
    scriptError.initWithWindowID(text, null, null, 0, 0, flags,
                                 category, getInnerId(this.window));
    Services.console.logMessage(scriptError);
    return {};
  },

  _onWorkerActorListChanged() {
    this._workerActorList.onListChanged = null;
    this.conn.sendActorEvent(this.actorID, "workerListChanged");
  },

  observe(subject, topic, data) {
    // Ignore any event that comes before/after the tab actor is attached
    // That typically happens during firefox shutdown.
    if (!this.attached) {
      return;
    }

    subject.QueryInterface(Ci.nsIDocShell);

    if (topic == "webnavigation-create") {
      this._onDocShellCreated(subject);
    } else if (topic == "webnavigation-destroy") {
      this._onDocShellDestroy(subject);
    }
  },

  _onDocShellCreated(docShell) {
    // (chrome-)webnavigation-create is fired very early during docshell
    // construction. In new root docshells within child processes, involving
    // TabChild, this event is from within this call:
    //   https://hg.mozilla.org/mozilla-central/annotate/74d7fb43bb44/dom/ipc/TabChild.cpp#l912
    // whereas the chromeEventHandler (and most likely other stuff) is set
    // later:
    //   https://hg.mozilla.org/mozilla-central/annotate/74d7fb43bb44/dom/ipc/TabChild.cpp#l944
    // So wait a tick before watching it:
    DevToolsUtils.executeSoon(() => {
      // Bug 1142752: sometimes, the docshell appears to be immediately
      // destroyed, bailout early to prevent random exceptions.
      if (docShell.isBeingDestroyed()) {
        return;
      }

      // In child processes, we have new root docshells,
      // let's watch them and all their child docshells.
      if (this._isRootDocShell(docShell)) {
        this._progressListener.watch(docShell);
      }
      this._notifyDocShellsUpdate([docShell]);
    });
  },

  _onDocShellDestroy(docShell) {
    // Stop watching this docshell (the unwatch() method will check if we
    // started watching it before).
    this._unwatchDocShell(docShell);

    let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                              .getInterface(Ci.nsIWebProgress);
    this._notifyDocShellDestroy(webProgress);

    if (webProgress.DOMWindow == this._originalWindow) {
      // If the original top level document we connected to is removed,
      // we try to switch to any other top level document
      let rootDocShells = this.docShells
                              .filter(d => {
                                return d != this.docShell &&
                                       this._isRootDocShell(d);
                              });
      if (rootDocShells.length > 0) {
        let newRoot = rootDocShells[0];
        this._originalWindow = newRoot.DOMWindow;
        this._changeTopLevelDocument(this._originalWindow);
      } else {
        // If for some reason (typically during Firefox shutdown), the original
        // document is destroyed, and there is no other top level docshell,
        // we detach the tab actor to unregister all listeners and prevent any
        // exception
        this.exit();
      }
      return;
    }

    // If the currently targeted context is destroyed,
    // and we aren't on the top-level document,
    // we have to switch to the top-level one.
    if (webProgress.DOMWindow == this.window &&
        this.window != this._originalWindow) {
      this._changeTopLevelDocument(this._originalWindow);
    }
  },

  _isRootDocShell(docShell) {
    // Should report as root docshell:
    //  - New top level window's docshells, when using ChromeActor against a
    // process. It allows tracking iframes of the newly opened windows
    // like Browser console or new browser windows.
    //  - MozActivities or window.open frames on B2G, where a new root docshell
    // is spawn in the child process of the app.
    return !docShell.parent;
  },

  _docShellToWindow(docShell) {
    let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                              .getInterface(Ci.nsIWebProgress);
    let window = webProgress.DOMWindow;
    let id = window.QueryInterface(Ci.nsIInterfaceRequestor)
                   .getInterface(Ci.nsIDOMWindowUtils)
                   .outerWindowID;
    let parentID = undefined;
    // Ignore the parent of the original document on non-e10s firefox,
    // as we get the xul window as parent and don't care about it.
    if (window.parent && window != this._originalWindow) {
      parentID = window.parent
                       .QueryInterface(Ci.nsIInterfaceRequestor)
                       .getInterface(Ci.nsIDOMWindowUtils)
                       .outerWindowID;
    }

    return {
      id,
      parentID,
      url: window.location.href,
      title: window.document.title,
    };
  },

  // Convert docShell list to windows objects list being sent to the client
  _docShellsToWindows(docshells) {
    return docshells.map(docShell => this._docShellToWindow(docShell));
  },

  _notifyDocShellsUpdate(docshells) {
    let windows = this._docShellsToWindows(docshells);

    // Do not send the `frameUpdate` event if the windows array is empty.
    if (windows.length == 0) {
      return;
    }

    this.conn.send({
      from: this.actorID,
      type: "frameUpdate",
      frames: windows
    });
  },

  _updateChildDocShells() {
    this._notifyDocShellsUpdate(this.docShells);
  },

  _notifyDocShellDestroy(webProgress) {
    webProgress = webProgress.QueryInterface(Ci.nsIWebProgress);
    let id = webProgress.DOMWindow
                        .QueryInterface(Ci.nsIInterfaceRequestor)
                        .getInterface(Ci.nsIDOMWindowUtils)
                        .outerWindowID;
    this.conn.send({
      from: this.actorID,
      type: "frameUpdate",
      frames: [{
        id,
        destroy: true
      }]
    });
  },

  _notifyDocShellDestroyAll() {
    this.conn.send({
      from: this.actorID,
      type: "frameUpdate",
      destroyAll: true
    });
  },

  /**
   * Creates a thread actor and a pool for context-lifetime actors. It then sets
   * up the content window for debugging.
   */
  _pushContext() {
    assert(!this._contextPool, "Can't push multiple contexts");

    this._contextPool = new ActorPool(this.conn);
    this.conn.addActorPool(this._contextPool);

    this.threadActor = new ThreadActor(this, this.window);
    this._contextPool.addActor(this.threadActor);
  },

  /**
   * Exits the current thread actor and removes the context-lifetime actor pool.
   * The content window is no longer being debugged after this call.
   */
  _popContext() {
    assert(!!this._contextPool, "No context to pop.");

    this.conn.removeActorPool(this._contextPool);
    this._contextPool = null;
    this.threadActor.exit();
    this.threadActor = null;
    this._sources = null;
  },

  /**
   * Does the actual work of detaching from a tab.
   *
   * @returns false if the tab wasn't attached or true of detaching succeeds.
   */
  _detach() {
    if (!this.attached) {
      return false;
    }

    // Check for docShell availability, as it can be already gone
    // during Firefox shutdown.
    if (this.docShell) {
      this._unwatchDocShell(this.docShell);
      this._restoreDocumentSettings();
    }
    if (this._progressListener) {
      this._progressListener.destroy();
      this._progressListener = null;
      this._originalWindow = null;

      // Removes the observers being set in _watchDocShells
      if (this.listenForNewDocShells) {
        Services.obs.removeObserver(this, "webnavigation-create");
      }
      Services.obs.removeObserver(this, "webnavigation-destroy");
    }

    this._popContext();

    // Shut down actors that belong to this tab's pool.
    for (let sheetActor of this._styleSheetActors.values()) {
      this._tabPool.removeActor(sheetActor);
    }
    this._styleSheetActors.clear();
    this.conn.removeActorPool(this._tabPool);
    this._tabPool = null;
    if (this._tabActorPool) {
      this.conn.removeActorPool(this._tabActorPool);
      this._tabActorPool = null;
    }

    // Make sure that no more workerListChanged notifications are sent.
    if (this._workerActorList !== null) {
      this._workerActorList.onListChanged = null;
      this._workerActorList = null;
    }

    if (this._workerActorPool !== null) {
      this.conn.removeActorPool(this._workerActorPool);
      this._workerActorPool = null;
    }

    this._attached = false;

    this.conn.send({ from: this.actorID,
                     type: "tabDetached" });

    return true;
  },

  // Protocol Request Handlers

  onAttach(request) {
    if (this.exited) {
      return { type: "exited" };
    }

    this._attach();

    return {
      type: "tabAttached",
      threadActor: this.threadActor.actorID,
      cacheDisabled: this._getCacheDisabled(),
      javascriptEnabled: this._getJavascriptEnabled(),
      traits: this.traits,
    };
  },

  onDetach(request) {
    if (!this._detach()) {
      return { error: "wrongState" };
    }

    return { type: "detached" };
  },

  /**
   * Bring the tab's window to front.
   */
  onFocus() {
    if (this.window) {
      this.window.focus();
    }
    return {};
  },

  /**
   * Reload the page in this tab.
   */
  onReload(request) {
    let force = request && request.options && request.options.force;
    // Wait a tick so that the response packet can be dispatched before the
    // subsequent navigation event packet.
    Services.tm.dispatchToMainThread(DevToolsUtils.makeInfallible(() => {
      // This won't work while the browser is shutting down and we don't really
      // care.
      if (Services.startup.shuttingDown) {
        return;
      }
      this.webNavigation.reload(force ?
        Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE :
        Ci.nsIWebNavigation.LOAD_FLAGS_NONE);
    }, "TabActor.prototype.onReload's delayed body"));
    return {};
  },

  /**
   * Navigate this tab to a new location
   */
  onNavigateTo(request) {
    // Wait a tick so that the response packet can be dispatched before the
    // subsequent navigation event packet.
    Services.tm.dispatchToMainThread(DevToolsUtils.makeInfallible(() => {
      this.window.location = request.url;
    }, "TabActor.prototype.onNavigateTo's delayed body"));
    return {};
  },

  /**
   * Reconfigure options.
   */
  onReconfigure(request) {
    let options = request.options || {};

    if (!this.docShell) {
      // The tab is already closed.
      return {};
    }
    this._toggleDevToolsSettings(options);

    return {};
  },

  /**
   * Handle logic to enable/disable JS/cache/Service Worker testing.
   */
  _toggleDevToolsSettings(options) {
    // Wait a tick so that the response packet can be dispatched before the
    // subsequent navigation event packet.
    let reload = false;

    if (typeof options.javascriptEnabled !== "undefined" &&
        options.javascriptEnabled !== this._getJavascriptEnabled()) {
      this._setJavascriptEnabled(options.javascriptEnabled);
      reload = true;
    }
    if (typeof options.cacheDisabled !== "undefined" &&
        options.cacheDisabled !== this._getCacheDisabled()) {
      this._setCacheDisabled(options.cacheDisabled);
    }
    if ((typeof options.serviceWorkersTestingEnabled !== "undefined") &&
        (options.serviceWorkersTestingEnabled !==
         this._getServiceWorkersTestingEnabled())) {
      this._setServiceWorkersTestingEnabled(
        options.serviceWorkersTestingEnabled
      );
    }

    // Reload if:
    //  - there's an explicit `performReload` flag and it's true
    //  - there's no `performReload` flag, but it makes sense to do so
    let hasExplicitReloadFlag = "performReload" in options;
    if ((hasExplicitReloadFlag && options.performReload) ||
       (!hasExplicitReloadFlag && reload)) {
      this.onReload();
    }
  },

  /**
   * Opposite of the _toggleDevToolsSettings method, that reset document state
   * when closing the toolbox.
   */
  _restoreDocumentSettings() {
    this._restoreJavascript();
    this._setCacheDisabled(false);
    this._setServiceWorkersTestingEnabled(false);
  },

  /**
   * Disable or enable the cache via docShell.
   */
  _setCacheDisabled(disabled) {
    let enable = Ci.nsIRequest.LOAD_NORMAL;
    let disable = Ci.nsIRequest.LOAD_BYPASS_CACHE |
                  Ci.nsIRequest.INHIBIT_CACHING;

    this.docShell.defaultLoadFlags = disabled ? disable : enable;
  },

  /**
   * Disable or enable JS via docShell.
   */
  _wasJavascriptEnabled: null,
  _setJavascriptEnabled(allow) {
    if (this._wasJavascriptEnabled === null) {
      this._wasJavascriptEnabled = this.docShell.allowJavascript;
    }
    this.docShell.allowJavascript = allow;
  },

  /**
   * Restore JS state, before the actor modified it.
   */
  _restoreJavascript() {
    if (this._wasJavascriptEnabled !== null) {
      this._setJavascriptEnabled(this._wasJavascriptEnabled);
      this._wasJavascriptEnabled = null;
    }
  },

  /**
   * Return JS allowed status.
   */
  _getJavascriptEnabled() {
    if (!this.docShell) {
      // The tab is already closed.
      return null;
    }

    return this.docShell.allowJavascript;
  },

  /**
   * Disable or enable the service workers testing features.
   */
  _setServiceWorkersTestingEnabled(enabled) {
    let windowUtils = this.window.QueryInterface(Ci.nsIInterfaceRequestor)
                                 .getInterface(Ci.nsIDOMWindowUtils);
    windowUtils.serviceWorkersTestingEnabled = enabled;
  },

  /**
   * Return cache allowed status.
   */
  _getCacheDisabled() {
    if (!this.docShell) {
      // The tab is already closed.
      return null;
    }

    let disable = Ci.nsIRequest.LOAD_BYPASS_CACHE |
                  Ci.nsIRequest.INHIBIT_CACHING;
    return this.docShell.defaultLoadFlags === disable;
  },

  /**
   * Return service workers testing allowed status.
   */
  _getServiceWorkersTestingEnabled() {
    if (!this.docShell) {
      // The tab is already closed.
      return null;
    }

    let windowUtils = this.window.QueryInterface(Ci.nsIInterfaceRequestor)
                                 .getInterface(Ci.nsIDOMWindowUtils);
    return windowUtils.serviceWorkersTestingEnabled;
  },

  /**
   * Prepare to enter a nested event loop by disabling debuggee events.
   */
  preNest() {
    if (!this.window) {
      // The tab is already closed.
      return;
    }
    let windowUtils = this.window
                          .QueryInterface(Ci.nsIInterfaceRequestor)
                          .getInterface(Ci.nsIDOMWindowUtils);
    windowUtils.suppressEventHandling(true);
    windowUtils.suspendTimeouts();
  },

  /**
   * Prepare to exit a nested event loop by enabling debuggee events.
   */
  postNest(nestData) {
    if (!this.window) {
      // The tab is already closed.
      return;
    }
    let windowUtils = this.window
                          .QueryInterface(Ci.nsIInterfaceRequestor)
                          .getInterface(Ci.nsIDOMWindowUtils);
    windowUtils.resumeTimeouts();
    windowUtils.suppressEventHandling(false);
  },

  _changeTopLevelDocument(window) {
    // Fake a will-navigate on the previous document
    // to let a chance to unregister it
    this._willNavigate(this.window, window.location.href, null, true);

    this._windowDestroyed(this.window, null, true);

    // Immediately change the window as this window, if in process of unload
    // may already be non working on the next cycle and start throwing
    this._setWindow(window);

    DevToolsUtils.executeSoon(() => {
      // No need to do anything more if the actor is not attached anymore
      // e.g. the client has been closed and the actors destroyed in the meantime.
      if (!this.attached) {
        return;
      }

      // Then fake window-ready and navigate on the given document
      this._windowReady(window, true);
      DevToolsUtils.executeSoon(() => {
        this._navigate(window, true);
      });
    });
  },

  _setWindow(window) {
    let docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsIWebNavigation)
                         .QueryInterface(Ci.nsIDocShell);
    // Here is the very important call where we switch the currently
    // targeted context (it will indirectly update this.window and
    // many other attributes defined from docShell).
    Object.defineProperty(this, "docShell", {
      value: docShell,
      enumerable: true,
      configurable: true
    });
    this.emit("changed-toplevel-document");
    this.conn.send({
      from: this.actorID,
      type: "frameUpdate",
      selected: this.outerWindowID
    });
  },

  /**
   * Handle location changes, by clearing the previous debuggees and enabling
   * debugging, which may have been disabled temporarily by the
   * DebuggerProgressListener.
   */
  _windowReady(window, isFrameSwitching = false) {
    let isTopLevel = window == this.window;

    // We just reset iframe list on WillNavigate, so we now list all existing
    // frames when we load a new document in the original window
    if (window == this._originalWindow && !isFrameSwitching) {
      this._updateChildDocShells();
    }

    this.emit("window-ready", {
      window: window,
      isTopLevel: isTopLevel,
      id: getWindowID(window)
    });

    // TODO bug 997119: move that code to ThreadActor by listening to
    // window-ready
    let threadActor = this.threadActor;
    if (isTopLevel && threadActor.state != "detached") {
      this.sources.reset({ sourceMaps: true });
      threadActor.clearDebuggees();
      threadActor.dbg.enabled = true;
      threadActor.maybePauseOnExceptions();
      // Update the global no matter if the debugger is on or off,
      // otherwise the global will be wrong when enabled later.
      threadActor.global = window;
    }

    // Refresh the debuggee list when a new window object appears (top window or
    // iframe).
    if (threadActor.attached) {
      threadActor.dbg.addDebuggees();
    }
  },

  _windowDestroyed(window, id = null, isFrozen = false) {
    this.emit("window-destroyed", {
      window: window,
      isTopLevel: window == this.window,
      id: id || getWindowID(window),
      isFrozen: isFrozen
    });
  },

  /**
   * Start notifying server and client about a new document
   * being loaded in the currently targeted context.
   */
  _willNavigate(window, newURI, request, isFrameSwitching = false) {
    let isTopLevel = window == this.window;
    let reset = false;

    if (window == this._originalWindow && !isFrameSwitching) {
      // Clear the iframe list if the original top-level document changes.
      this._notifyDocShellDestroyAll();

      // If the top level document changes and we are targeting
      // an iframe, we need to reset to the upcoming new top level document.
      // But for this will-navigate event, we will dispatch on the old window.
      // (The inspector codebase expect to receive will-navigate for the
      // currently displayed document in order to cleanup the markup view)
      if (this.window != this._originalWindow) {
        reset = true;
        window = this.window;
        isTopLevel = true;
      }
    }

    // will-navigate event needs to be dispatched synchronously,
    // by calling the listeners in the order or registration.
    // This event fires once navigation starts,
    // (all pending user prompts are dealt with),
    // but before the first request starts.
    this.emit("will-navigate", {
      window: window,
      isTopLevel: isTopLevel,
      newURI: newURI,
      request: request
    });

    // We don't do anything for inner frames in TabActor.
    // (we will only update thread actor on window-ready)
    if (!isTopLevel) {
      return;
    }

    // Proceed normally only if the debuggee is not paused.
    // TODO bug 997119: move that code to ThreadActor by listening to
    // will-navigate
    let threadActor = this.threadActor;
    if (threadActor.state == "paused") {
      this.conn.send(
        threadActor.unsafeSynchronize(Promise.resolve(threadActor.onResume())));
      threadActor.dbg.enabled = false;
    }
    threadActor.disableAllBreakpoints();

    this.conn.send({
      from: this.actorID,
      type: "tabNavigated",
      url: newURI,
      nativeConsoleAPI: true,
      state: "start",
      isFrameSwitching: isFrameSwitching
    });

    if (reset) {
      this._setWindow(this._originalWindow);
    }
  },

  /**
   * Notify server and client about a new document done loading in the current
   * targeted context.
   */
  _navigate(window, isFrameSwitching = false) {
    let isTopLevel = window == this.window;

    // navigate event needs to be dispatched synchronously,
    // by calling the listeners in the order or registration.
    // This event is fired once the document is loaded,
    // after the load event, it's document ready-state is 'complete'.
    this.emit("navigate", {
      window: window,
      isTopLevel: isTopLevel
    });

    // We don't do anything for inner frames in TabActor.
    // (we will only update thread actor on window-ready)
    if (!isTopLevel) {
      return;
    }

    // TODO bug 997119: move that code to ThreadActor by listening to navigate
    let threadActor = this.threadActor;
    if (threadActor.state == "running") {
      threadActor.dbg.enabled = true;
    }

    this.conn.send({
      from: this.actorID,
      type: "tabNavigated",
      url: this.url,
      title: this.title,
      nativeConsoleAPI: this.hasNativeConsoleAPI(this.window),
      state: "stop",
      isFrameSwitching: isFrameSwitching
    });
  },

  /**
   * Tells if the window.console object is native or overwritten by script in
   * the page.
   *
   * @param nsIDOMWindow window
   *        The window object you want to check.
   * @return boolean
   *         True if the window.console object is native, or false otherwise.
   */
  hasNativeConsoleAPI(window) {
    let isNative = false;
    try {
      // We are very explicitly examining the "console" property of
      // the non-Xrayed object here.
      let console = window.wrappedJSObject.console;
      isNative = new XPCNativeWrapper(console).IS_NATIVE_CONSOLE;
    } catch (ex) {
      // ignore
    }
    return isNative;
  },

  /**
   * Create or return the StyleSheetActor for a style sheet. This method
   * is here because the Style Editor and Inspector share style sheet actors.
   *
   * @param DOMStyleSheet styleSheet
   *        The style sheet to create an actor for.
   * @return StyleSheetActor actor
   *         The actor for this style sheet.
   *
   */
  createStyleSheetActor(styleSheet) {
    if (this._styleSheetActors.has(styleSheet)) {
      return this._styleSheetActors.get(styleSheet);
    }
    let actor = new StyleSheetActor(styleSheet, this);
    this._styleSheetActors.set(styleSheet, actor);

    this._tabPool.addActor(actor);
    this.emit("stylesheet-added", actor);

    return actor;
  },

  removeActorByName(name) {
    if (name in this._extraActors) {
      const actor = this._extraActors[name];
      if (this._tabActorPool.has(actor)) {
        this._tabActorPool.removeActor(actor);
      }
      delete this._extraActors[name];
    }
  },
};

/**
 * The request types this actor can handle.
 */
TabActor.prototype.requestTypes = {
  "attach": TabActor.prototype.onAttach,
  "detach": TabActor.prototype.onDetach,
  "focus": TabActor.prototype.onFocus,
  "reload": TabActor.prototype.onReload,
  "navigateTo": TabActor.prototype.onNavigateTo,
  "reconfigure": TabActor.prototype.onReconfigure,
  "switchToFrame": TabActor.prototype.onSwitchToFrame,
  "listFrames": TabActor.prototype.onListFrames,
  "listWorkers": TabActor.prototype.onListWorkers,
  "logInPage": TabActor.prototype.onLogInPage,
};

exports.TabActor = TabActor;

/**
 * The DebuggerProgressListener object is an nsIWebProgressListener which
 * handles onStateChange events for the inspected browser. If the user tries to
 * navigate away from a paused page, the listener makes sure that the debuggee
 * is resumed before the navigation begins.
 *
 * @param TabActor aTabActor
 *        The tab actor associated with this listener.
 */
function DebuggerProgressListener(tabActor) {
  this._tabActor = tabActor;
  this._onWindowCreated = this.onWindowCreated.bind(this);
  this._onWindowHidden = this.onWindowHidden.bind(this);

  // Watch for windows destroyed (global observer that will need filtering)
  Services.obs.addObserver(this, "inner-window-destroyed");

  // XXX: for now we maintain the list of windows we know about in this instance
  // so that we can discriminate windows we care about when observing
  // inner-window-destroyed events. Bug 1016952 would remove the need for this.
  this._knownWindowIDs = new Map();

  this._watchedDocShells = new WeakSet();
}

DebuggerProgressListener.prototype = {
  QueryInterface: XPCOMUtils.generateQI([
    Ci.nsIWebProgressListener,
    Ci.nsISupportsWeakReference,
    Ci.nsISupports,
  ]),

  destroy() {
    Services.obs.removeObserver(this, "inner-window-destroyed");
    this._knownWindowIDs.clear();
    this._knownWindowIDs = null;
  },

  watch(docShell) {
    // Add the docshell to the watched set. We're actually adding the window,
    // because docShell objects are not wrappercached and would be rejected
    // by the WeakSet.
    let docShellWindow = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                                 .getInterface(Ci.nsIDOMWindow);
    this._watchedDocShells.add(docShellWindow);

    let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                              .getInterface(Ci.nsIWebProgress);
    webProgress.addProgressListener(this,
                                    Ci.nsIWebProgress.NOTIFY_STATE_WINDOW |
                                    Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT);

    let handler = getDocShellChromeEventHandler(docShell);
    handler.addEventListener("DOMWindowCreated", this._onWindowCreated, true);
    handler.addEventListener("pageshow", this._onWindowCreated, true);
    handler.addEventListener("pagehide", this._onWindowHidden, true);

    // Dispatch the _windowReady event on the tabActor for pre-existing windows
    for (let win of this._getWindowsInDocShell(docShell)) {
      this._tabActor._windowReady(win);
      this._knownWindowIDs.set(getWindowID(win), win);
    }
  },

  unwatch(docShell) {
    let docShellWindow = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                                 .getInterface(Ci.nsIDOMWindow);
    if (!this._watchedDocShells.has(docShellWindow)) {
      return;
    }

    let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                              .getInterface(Ci.nsIWebProgress);
    // During process shutdown, the docshell may already be cleaned up and throw
    try {
      webProgress.removeProgressListener(this);
    } catch (e) {
      // ignore
    }

    let handler = getDocShellChromeEventHandler(docShell);
    handler.removeEventListener("DOMWindowCreated",
      this._onWindowCreated, true);
    handler.removeEventListener("pageshow", this._onWindowCreated, true);
    handler.removeEventListener("pagehide", this._onWindowHidden, true);

    for (let win of this._getWindowsInDocShell(docShell)) {
      this._knownWindowIDs.delete(getWindowID(win));
    }
  },

  _getWindowsInDocShell(docShell) {
    return getChildDocShells(docShell).map(d => {
      return d.QueryInterface(Ci.nsIInterfaceRequestor)
              .getInterface(Ci.nsIDOMWindow);
    });
  },

  onWindowCreated: DevToolsUtils.makeInfallible(function (evt) {
    if (!this._tabActor.attached) {
      return;
    }

    // If we're in a frame swap (which occurs when toggling RDM, for example), then we can
    // ignore this event, as the window never really went anywhere for our purposes.
    if (evt.inFrameSwap) {
      return;
    }

    let window = evt.target.defaultView;
    let innerID = getWindowID(window);

    // This method is alled on DOMWindowCreated and pageshow
    // The common scenario is DOMWindowCreated, which is fired when the document
    // loads. But we are to listen for pageshow in order to handle BFCache.
    // When a page does into the BFCache, a pagehide event is fired with persisted=true
    // but it doesn't necessarely mean persisted will be true for the pageshow
    // event fired when the page is reloaded from the BFCache (see bug 1378133)
    // So just check if we already know this window and accept any that isn't known yet
    if (this._knownWindowIDs.has(innerID)) {
      return;
    }

    this._tabActor._windowReady(window);

    this._knownWindowIDs.set(innerID, window);
  }, "DebuggerProgressListener.prototype.onWindowCreated"),

  onWindowHidden: DevToolsUtils.makeInfallible(function (evt) {
    if (!this._tabActor.attached) {
      return;
    }

    // If we're in a frame swap (which occurs when toggling RDM, for example), then we can
    // ignore this event, as the window isn't really going anywhere for our purposes.
    if (evt.inFrameSwap) {
      return;
    }

    // Only act as if the window has been destroyed if the 'pagehide' event
    // was sent for a persisted window (persisted is set when the page is put
    // and frozen in the bfcache). If the page isn't persisted, the observer's
    // inner-window-destroyed event will handle it.
    if (!evt.persisted) {
      return;
    }

    let window = evt.target.defaultView;
    this._tabActor._windowDestroyed(window, null, true);
    this._knownWindowIDs.delete(getWindowID(window));
  }, "DebuggerProgressListener.prototype.onWindowHidden"),

  observe: DevToolsUtils.makeInfallible(function (subject, topic) {
    if (!this._tabActor.attached) {
      return;
    }

    // Because this observer will be called for all inner-window-destroyed in
    // the application, we need to filter out events for windows we are not
    // watching
    let innerID = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
    let window = this._knownWindowIDs.get(innerID);
    if (window) {
      this._knownWindowIDs.delete(innerID);
      this._tabActor._windowDestroyed(window, innerID);
    }
  }, "DebuggerProgressListener.prototype.observe"),

  onStateChange:
  DevToolsUtils.makeInfallible(function (progress, request, flag, status) {
    if (!this._tabActor.attached) {
      return;
    }

    let isStart = flag & Ci.nsIWebProgressListener.STATE_START;
    let isStop = flag & Ci.nsIWebProgressListener.STATE_STOP;
    let isDocument = flag & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT;
    let isWindow = flag & Ci.nsIWebProgressListener.STATE_IS_WINDOW;

    // Catch any iframe location change
    if (isDocument && isStop) {
      // Watch document stop to ensure having the new iframe url.
      progress.QueryInterface(Ci.nsIDocShell);
      this._tabActor._notifyDocShellsUpdate([progress]);
    }

    let window = progress.DOMWindow;
    if (isDocument && isStart) {
      // One of the earliest events that tells us a new URI
      // is being loaded in this window.
      let newURI = request instanceof Ci.nsIChannel ? request.URI.spec : null;
      this._tabActor._willNavigate(window, newURI, request);
    }
    if (isWindow && isStop) {
      // Don't dispatch "navigate" event just yet when there is a redirect to
      // about:neterror page.
      // Navigating to about:neterror will make `status` be something else than NS_OK.
      // But for some error like NS_BINDING_ABORTED we don't want to emit any `navigate`
      // event as the page load has been cancelled and the related page document is going
      // to be a dead wrapper.
      if (request.status != Cr.NS_OK && request.status != Cr.NS_BINDING_ABORTED) {
        // Instead, listen for DOMContentLoaded as about:neterror is loaded
        // with LOAD_BACKGROUND flags and never dispatches load event.
        // That may be the same reason why there is no onStateChange event
        // for about:neterror loads.
        let handler = getDocShellChromeEventHandler(progress);
        let onLoad = evt => {
          // Ignore events from iframes
          if (evt.target === window.document) {
            handler.removeEventListener("DOMContentLoaded", onLoad, true);
            this._tabActor._navigate(window);
          }
        };
        handler.addEventListener("DOMContentLoaded", onLoad, true);
      } else {
        // Somewhat equivalent of load event.
        // (window.document.readyState == complete)
        this._tabActor._navigate(window);
      }
    }
  }, "DebuggerProgressListener.prototype.onStateChange")
};
PK
!<bX�օ���8chrome/devtools/modules/devtools/server/actors/thread.js/* -*- indent-tabs-mode: nil; js-indent-level: 2; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 Services = require("Services");
const { Cr } = require("chrome");
const { ActorPool, GeneratedLocation } = require("devtools/server/actors/common");
const { createValueGrip, longStringGrip } = require("devtools/server/actors/object");
const { ActorClassWithSpec } = require("devtools/shared/protocol");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const flags = require("devtools/shared/flags");
const { assert, dumpn } = DevToolsUtils;
const { DevToolsWorker } = require("devtools/shared/worker/worker");
const { threadSpec } = require("devtools/shared/specs/script");

loader.lazyGetter(this, "Debugger", () => {
  let Debugger = require("Debugger");
  hackDebugger(Debugger);
  return Debugger;
});
loader.lazyRequireGetter(this, "findCssSelector", "devtools/shared/inspector/css-logic", true);
loader.lazyRequireGetter(this, "BreakpointActor", "devtools/server/actors/breakpoint", true);
loader.lazyRequireGetter(this, "setBreakpointAtEntryPoints", "devtools/server/actors/breakpoint", true);
loader.lazyRequireGetter(this, "EnvironmentActor", "devtools/server/actors/environment", true);
loader.lazyRequireGetter(this, "SourceActorStore", "devtools/server/actors/utils/source-actor-store", true);
loader.lazyRequireGetter(this, "BreakpointActorMap", "devtools/server/actors/utils/breakpoint-actor-map", true);
loader.lazyRequireGetter(this, "PauseScopedObjectActor", "devtools/server/actors/pause-scoped", true);
loader.lazyRequireGetter(this, "EventLoopStack", "devtools/server/actors/utils/event-loop", true);
loader.lazyRequireGetter(this, "FrameActor", "devtools/server/actors/frame", true);
loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");

/**
 * JSD2 actors.
 */

/**
 * Creates a ThreadActor.
 *
 * ThreadActors manage a JSInspector object and manage execution/inspection
 * of debuggees.
 *
 * @param aParent object
 *        This |ThreadActor|'s parent actor. It must implement the following
 *        properties:
 *          - url: The URL string of the debuggee.
 *          - window: The global window object.
 *          - preNest: Function called before entering a nested event loop.
 *          - postNest: Function called after exiting a nested event loop.
 *          - makeDebugger: A function that takes no arguments and instantiates
 *            a Debugger that manages its globals on its own.
 * @param aGlobal object [optional]
 *        An optional (for content debugging only) reference to the content
 *        window.
 */
const ThreadActor = ActorClassWithSpec(threadSpec, {
  initialize: function (parent, global) {
    this._state = "detached";
    this._frameActors = [];
    this._parent = parent;
    this._dbg = null;
    this._gripDepth = 0;
    this._threadLifetimePool = null;
    this._tabClosed = false;
    this._scripts = null;
    this._pauseOnDOMEvents = null;

    this._options = {
      useSourceMaps: false,
      autoBlackBox: false
    };

    this.breakpointActorMap = new BreakpointActorMap();
    this.sourceActorStore = new SourceActorStore();

    this._debuggerSourcesSeen = null;

    // A map of actorID -> actor for breakpoints created and managed by the
    // server.
    this._hiddenBreakpoints = new Map();

    this.global = global;

    this._allEventsListener = this._allEventsListener.bind(this);
    this.onNewGlobal = this.onNewGlobal.bind(this);
    this.onNewSourceEvent = this.onNewSourceEvent.bind(this);
    this.onUpdatedSourceEvent = this.onUpdatedSourceEvent.bind(this);

    this.uncaughtExceptionHook = this.uncaughtExceptionHook.bind(this);
    this.onDebuggerStatement = this.onDebuggerStatement.bind(this);
    this.onNewScript = this.onNewScript.bind(this);
    this.objectGrip = this.objectGrip.bind(this);
    this.pauseObjectGrip = this.pauseObjectGrip.bind(this);
    this._onWindowReady = this._onWindowReady.bind(this);
    EventEmitter.on(this._parent, "window-ready", this._onWindowReady);
    // Set a wrappedJSObject property so |this| can be sent via the observer svc
    // for the xpcshell harness.
    this.wrappedJSObject = this;
  },

  // Used by the ObjectActor to keep track of the depth of grip() calls.
  _gripDepth: null,

  get dbg() {
    if (!this._dbg) {
      this._dbg = this._parent.makeDebugger();
      this._dbg.uncaughtExceptionHook = this.uncaughtExceptionHook;
      this._dbg.onDebuggerStatement = this.onDebuggerStatement;
      this._dbg.onNewScript = this.onNewScript;
      this._dbg.on("newGlobal", this.onNewGlobal);
      // Keep the debugger disabled until a client attaches.
      this._dbg.enabled = this._state != "detached";
    }
    return this._dbg;
  },

  get globalDebugObject() {
    if (!this._parent.window) {
      return null;
    }
    return this.dbg.makeGlobalObjectReference(this._parent.window);
  },

  get state() {
    return this._state;
  },

  get attached() {
    return this.state == "attached" ||
           this.state == "running" ||
           this.state == "paused";
  },

  get threadLifetimePool() {
    if (!this._threadLifetimePool) {
      this._threadLifetimePool = new ActorPool(this.conn);
      this.conn.addActorPool(this._threadLifetimePool);
      this._threadLifetimePool.objectActors = new WeakMap();
    }
    return this._threadLifetimePool;
  },

  get sources() {
    return this._parent.sources;
  },

  get youngestFrame() {
    if (this.state != "paused") {
      return null;
    }
    return this.dbg.getNewestFrame();
  },

  _prettyPrintWorker: null,
  get prettyPrintWorker() {
    if (!this._prettyPrintWorker) {
      this._prettyPrintWorker = new DevToolsWorker(
        "resource://devtools/server/actors/pretty-print-worker.js",
        { name: "pretty-print",
          verbose: flags.wantLogging }
      );
    }
    return this._prettyPrintWorker;
  },

  /**
   * Keep track of all of the nested event loops we use to pause the debuggee
   * when we hit a breakpoint/debugger statement/etc in one place so we can
   * resolve them when we get resume packets. We have more than one (and keep
   * them in a stack) because we can pause within client evals.
   */
  _threadPauseEventLoops: null,
  _pushThreadPause: function () {
    if (!this._threadPauseEventLoops) {
      this._threadPauseEventLoops = [];
    }
    const eventLoop = this._nestedEventLoops.push();
    this._threadPauseEventLoops.push(eventLoop);
    eventLoop.enter();
  },
  _popThreadPause: function () {
    const eventLoop = this._threadPauseEventLoops.pop();
    assert(eventLoop, "Should have an event loop.");
    eventLoop.resolve();
  },

  /**
   * Remove all debuggees and clear out the thread's sources.
   */
  clearDebuggees: function () {
    if (this._dbg) {
      this.dbg.removeAllDebuggees();
    }
    this._sources = null;
    this._scripts = null;
  },

  /**
   * Listener for our |Debugger|'s "newGlobal" event.
   */
  onNewGlobal: function (global) {
    // Notify the client.
    this.conn.send({
      from: this.actorID,
      type: "newGlobal",
      // TODO: after bug 801084 lands see if we need to JSONify this.
      hostAnnotations: global.hostAnnotations
    });
  },

  destroy: function () {
    dumpn("in ThreadActor.prototype.destroy");
    if (this._state == "paused") {
      this.onResume();
    }

    // Blow away our source actor ID store because those IDs are only
    // valid for this connection. This is ok because we never keep
    // things like breakpoints across connections.
    this._sourceActorStore = null;

    EventEmitter.off(this._parent, "window-ready", this._onWindowReady);
    this.sources.off("newSource", this.onNewSourceEvent);
    this.sources.off("updatedSource", this.onUpdatedSourceEvent);
    this.clearDebuggees();
    this.conn.removeActorPool(this._threadLifetimePool);
    this._threadLifetimePool = null;

    if (this._prettyPrintWorker) {
      this._prettyPrintWorker.destroy();
      this._prettyPrintWorker = null;
    }

    if (!this._dbg) {
      return;
    }
    this._dbg.enabled = false;
    this._dbg = null;
  },

  /**
   * destroy the debugger and put the actor in the exited state.
   */
  exit: function () {
    this.destroy();
    this._state = "exited";
  },

  // Request handlers
  onAttach: function (request) {
    if (this.state === "exited") {
      return { type: "exited" };
    }

    if (this.state !== "detached") {
      return { error: "wrongState",
               message: "Current state is " + this.state };
    }

    this._state = "attached";
    this._debuggerSourcesSeen = new WeakSet();

    Object.assign(this._options, request.options || {});
    this.sources.setOptions(this._options);
    this.sources.on("newSource", this.onNewSourceEvent);
    this.sources.on("updatedSource", this.onUpdatedSourceEvent);

    // Initialize an event loop stack. This can't be done in the constructor,
    // because this.conn is not yet initialized by the actor pool at that time.
    this._nestedEventLoops = new EventLoopStack({
      hooks: this._parent,
      connection: this.conn,
      thread: this
    });

    this.dbg.addDebuggees();
    this.dbg.enabled = true;
    try {
      // Put ourselves in the paused state.
      let packet = this._paused();
      if (!packet) {
        return { error: "notAttached" };
      }
      packet.why = { type: "attached" };

      // Send the response to the attach request now (rather than
      // returning it), because we're going to start a nested event loop
      // here.
      this.conn.send(packet);

      // Start a nested event loop.
      this._pushThreadPause();

      // We already sent a response to this request, don't send one
      // now.
      return null;
    } catch (e) {
      reportError(e);
      return { error: "notAttached", message: e.toString() };
    }
  },

  onDetach: function (request) {
    this.destroy();
    this._state = "detached";
    this._debuggerSourcesSeen = null;

    dumpn("ThreadActor.prototype.onDetach: returning 'detached' packet");
    return {
      type: "detached"
    };
  },

  onReconfigure: function (request) {
    if (this.state == "exited") {
      return { error: "wrongState" };
    }
    const options = request.options || {};

    if ("observeAsmJS" in options) {
      this.dbg.allowUnobservedAsmJS = !options.observeAsmJS;
    }
    if ("wasmBinarySource" in options) {
      this.dbg.allowWasmBinarySource = !!options.wasmBinarySource;
    }

    Object.assign(this._options, options);

    // Update the global source store
    this.sources.setOptions(options);

    return {};
  },

  /**
   * Pause the debuggee, by entering a nested event loop, and return a 'paused'
   * packet to the client.
   *
   * @param Debugger.Frame frame
   *        The newest debuggee frame in the stack.
   * @param object reason
   *        An object with a 'type' property containing the reason for the pause.
   * @param function onPacket
   *        Hook to modify the packet before it is sent. Feel free to return a
   *        promise.
   */
  _pauseAndRespond: function (frame, reason, onPacket = function (k) {
    return k;
  }) {
    try {
      let packet = this._paused(frame);
      if (!packet) {
        return undefined;
      }
      packet.why = reason;

      let generatedLocation = this.sources.getFrameLocation(frame);
      this.sources.getOriginalLocation(generatedLocation)
                  .then((originalLocation) => {
                    if (!originalLocation.originalSourceActor) {
          // The only time the source actor will be null is if there
          // was a sourcemap and it tried to look up the original
          // location but there was no original URL. This is a strange
          // scenario so we simply don't pause.
                      DevToolsUtils.reportException(
            "ThreadActor",
            new Error("Attempted to pause in a script with a sourcemap but " +
                      "could not find original location.")
          );

                      return undefined;
                    }

                    packet.frame.where = {
                      source: originalLocation.originalSourceActor.form(),
                      line: originalLocation.originalLine,
                      column: originalLocation.originalColumn
                    };
                    Promise.resolve(onPacket(packet))
          .catch(error => {
            reportError(error);
            return {
              error: "unknownError",
              message: error.message + "\n" + error.stack
            };
          })
          .then(pkt => {
            this.conn.send(pkt);
          });

                    return undefined;
                  });

      this._pushThreadPause();
    } catch (e) {
      reportError(e, "Got an exception during TA__pauseAndRespond: ");
    }

    // If the browser tab has been closed, terminate the debuggee script
    // instead of continuing. Executing JS after the content window is gone is
    // a bad idea.
    return this._tabClosed ? null : undefined;
  },

  _makeOnEnterFrame: function ({ pauseAndRespond }) {
    return frame => {
      const generatedLocation = this.sources.getFrameLocation(frame);
      let { originalSourceActor } = this.unsafeSynchronize(
        this.sources.getOriginalLocation(generatedLocation));
      let url = originalSourceActor.url;

      return this.sources.isBlackBoxed(url)
        ? undefined
        : pauseAndRespond(frame);
    };
  },

  _makeOnPop: function ({ thread, pauseAndRespond, createValueGrip: createValueGripHook,
                          startLocation }) {
    const result = function (completion) {
      // onPop is called with 'this' set to the current frame.

      const generatedLocation = thread.sources.getFrameLocation(this);
      const { originalSourceActor } = thread.unsafeSynchronize(
        thread.sources.getOriginalLocation(generatedLocation));
      const url = originalSourceActor.url;

      if (thread.sources.isBlackBoxed(url)) {
        return undefined;
      }

      // Note that we're popping this frame; we need to watch for
      // subsequent step events on its caller.
      this.reportedPop = true;

      return pauseAndRespond(this, packet => {
        packet.why.frameFinished = {};
        if (!completion) {
          packet.why.frameFinished.terminated = true;
        } else if (completion.hasOwnProperty("return")) {
          packet.why.frameFinished.return = createValueGripHook(completion.return);
        } else if (completion.hasOwnProperty("yield")) {
          packet.why.frameFinished.return = createValueGripHook(completion.yield);
        } else {
          packet.why.frameFinished.throw = createValueGripHook(completion.throw);
        }
        return packet;
      });
    };

    // When stepping out, we don't want to stop at a breakpoint that
    // happened to be set exactly at the spot where we stepped out.
    // See bug 970469.  We record the original location here and check
    // it when a breakpoint is hit.  Furthermore we store this on the
    // function because, while we could store it directly on the
    // frame, if we did we'd also have to find the appropriate spot to
    // clear it.
    result.originalLocation = startLocation;

    return result;
  },

  _makeOnStep: function ({ thread, pauseAndRespond, startFrame,
                           startLocation, steppingType }) {
    // Breaking in place: we should always pause.
    if (steppingType === "break") {
      return function () {
        return pauseAndRespond(this);
      };
    }

    // Otherwise take what a "step" means into consideration.
    return function () {
      // onStep is called with 'this' set to the current frame.

      // Only allow stepping stops at entry points for the line, when
      // the stepping occurs in a single frame.  The "same frame"
      // check makes it so a sequence of steps can step out of a frame
      // and into subsequent calls in the outer frame.  E.g., if there
      // is a call "a(b())" and the user steps into b, then this
      // condition makes it possible to step out of b and into a.
      if (this === startFrame &&
          !this.script.getOffsetLocation(this.offset).isEntryPoint) {
        return undefined;
      }

      const generatedLocation = thread.sources.getFrameLocation(this);
      const newLocation = thread.unsafeSynchronize(thread.sources.getOriginalLocation(
        generatedLocation));

      // Cases when we should pause because we have executed enough to consider
      // a "step" to have occured:
      //
      // 1.1. We change frames.
      // 1.2. We change URLs (can happen without changing frames thanks to
      //      source mapping).
      // 1.3. We change lines.
      //
      // Cases when we should always continue execution, even if one of the
      // above cases is true:
      //
      // 2.1. We are in a source mapped region, but inside a null mapping
      //      (doesn't correlate to any region of original source)
      // 2.2. The source we are in is black boxed.

      // Cases 2.1 and 2.2
      if (newLocation.originalUrl == null
          || thread.sources.isBlackBoxed(newLocation.originalUrl)) {
        return undefined;
      }

      // Cases 1.1, 1.2 and 1.3
      if (this !== startFrame
          || startLocation.originalUrl !== newLocation.originalUrl
          || startLocation.originalLine !== newLocation.originalLine) {
        return pauseAndRespond(this);
      }

      // Otherwise, let execution continue (we haven't executed enough code to
      // consider this a "step" yet).
      return undefined;
    };
  },

  /**
   * Define the JS hook functions for stepping.
   */
  _makeSteppingHooks: function (startLocation, steppingType) {
    // Bind these methods and state because some of the hooks are called
    // with 'this' set to the current frame. Rather than repeating the
    // binding in each _makeOnX method, just do it once here and pass it
    // in to each function.
    const steppingHookState = {
      pauseAndRespond: (frame, onPacket = k=>k) => {
        return this._pauseAndRespond(frame, { type: "resumeLimit" }, onPacket);
      },
      createValueGrip: v => createValueGrip(v, this._pausePool,
        this.objectGrip),
      thread: this,
      startFrame: this.youngestFrame,
      startLocation: startLocation,
      steppingType: steppingType
    };

    return {
      onEnterFrame: this._makeOnEnterFrame(steppingHookState),
      onPop: this._makeOnPop(steppingHookState),
      onStep: this._makeOnStep(steppingHookState)
    };
  },

  /**
   * Handle attaching the various stepping hooks we need to attach when we
   * receive a resume request with a resumeLimit property.
   *
   * @param Object request
   *        The request packet received over the RDP.
   * @returns A promise that resolves to true once the hooks are attached, or is
   *          rejected with an error packet.
   */
  _handleResumeLimit: function (request) {
    let steppingType = request.resumeLimit.type;
    if (!["break", "step", "next", "finish"].includes(steppingType)) {
      return Promise.reject({ error: "badParameterType",
                              message: "Unknown resumeLimit type" });
    }

    const generatedLocation = this.sources.getFrameLocation(this.youngestFrame);
    return this.sources.getOriginalLocation(generatedLocation)
      .then(originalLocation => {
        const { onEnterFrame, onPop, onStep } = this._makeSteppingHooks(originalLocation,
                                                                        steppingType);

        // Make sure there is still a frame on the stack if we are to continue
        // stepping.
        let stepFrame = this._getNextStepFrame(this.youngestFrame);
        if (stepFrame) {
          switch (steppingType) {
            case "step":
              this.dbg.onEnterFrame = onEnterFrame;
              // Fall through.
            case "break":
            case "next":
              if (stepFrame.script) {
                stepFrame.onStep = onStep;
              }
              stepFrame.onPop = onPop;
              break;
            case "finish":
              stepFrame.onPop = onPop;
          }
        }

        return true;
      });
  },

  /**
   * Clear the onStep and onPop hooks from the given frame and all of the frames
   * below it.
   *
   * @param Debugger.Frame aFrame
   *        The frame we want to clear the stepping hooks from.
   */
  _clearSteppingHooks: function (frame) {
    if (frame && frame.live) {
      while (frame) {
        frame.onStep = undefined;
        frame.onPop = undefined;
        frame = frame.older;
      }
    }
  },

  /**
   * Listen to the debuggee's DOM events if we received a request to do so.
   *
   * @param Object request
   *        The resume request packet received over the RDP.
   */
  _maybeListenToEvents: function (request) {
    // Break-on-DOMEvents is only supported in content debugging.
    let events = request.pauseOnDOMEvents;
    if (this.global && events &&
        (events == "*" ||
        (Array.isArray(events) && events.length))) {
      this._pauseOnDOMEvents = events;
      Services.els.addListenerForAllEvents(this.global, this._allEventsListener, true);
    }
  },

  /**
   * If we are tasked with breaking on the load event, we have to add the
   * listener early enough.
   */
  _onWindowReady: function () {
    this._maybeListenToEvents({
      pauseOnDOMEvents: this._pauseOnDOMEvents
    });
  },

  /**
   * Handle a protocol request to resume execution of the debuggee.
   */
  onResume: function (request) {
    if (this._state !== "paused") {
      return {
        error: "wrongState",
        message: "Can't resume when debuggee isn't paused. Current state is '"
          + this._state + "'",
        state: this._state
      };
    }

    // In case of multiple nested event loops (due to multiple debuggers open in
    // different tabs or multiple debugger clients connected to the same tab)
    // only allow resumption in a LIFO order.
    if (this._nestedEventLoops.size && this._nestedEventLoops.lastPausedUrl
        && (this._nestedEventLoops.lastPausedUrl !== this._parent.url
            || this._nestedEventLoops.lastConnection !== this.conn)) {
      return {
        error: "wrongOrder",
        message: "trying to resume in the wrong order.",
        lastPausedUrl: this._nestedEventLoops.lastPausedUrl
      };
    }

    let resumeLimitHandled;
    if (request && request.resumeLimit) {
      resumeLimitHandled = this._handleResumeLimit(request);
    } else {
      this._clearSteppingHooks(this.youngestFrame);
      resumeLimitHandled = Promise.resolve(true);
    }

    return resumeLimitHandled.then(() => {
      if (request) {
        this._options.pauseOnExceptions = request.pauseOnExceptions;
        this._options.ignoreCaughtExceptions = request.ignoreCaughtExceptions;
        this.maybePauseOnExceptions();
        this._maybeListenToEvents(request);
      }

      let packet = this._resumed();
      this._popThreadPause();
      // Tell anyone who cares of the resume (as of now, that's the xpcshell harness and
      // devtools-startup.js when handling the --wait-for-jsdebugger flag)
      if (Services.obs) {
        Services.obs.notifyObservers(this, "devtools-thread-resumed");
      }
      return packet;
    }, error => {
      return error instanceof Error
        ? { error: "unknownError",
            message: DevToolsUtils.safeErrorString(error) }
        // It is a known error, and the promise was rejected with an error
        // packet.
        : error;
    });
  },

  /**
   * Spin up a nested event loop so we can synchronously resolve a promise.
   *
   * DON'T USE THIS UNLESS YOU ABSOLUTELY MUST! Nested event loops suck: the
   * world's state can change out from underneath your feet because JS is no
   * longer run-to-completion.
   *
   * @param p
   *        The promise we want to resolve.
   * @returns The promise's resolution.
   */
  unsafeSynchronize: function (p) {
    let needNest = true;
    let eventLoop;
    let returnVal;

    p.then((resolvedVal) => {
      needNest = false;
      returnVal = resolvedVal;
    })
    .catch((error) => {
      reportError(error, "Error inside unsafeSynchronize:");
    })
    .then(() => {
      if (eventLoop) {
        eventLoop.resolve();
      }
    });

    if (needNest) {
      eventLoop = this._nestedEventLoops.push();
      eventLoop.enter();
    }

    return returnVal;
  },

  /**
   * Set the debugging hook to pause on exceptions if configured to do so.
   */
  maybePauseOnExceptions: function () {
    if (this._options.pauseOnExceptions) {
      this.dbg.onExceptionUnwind = this.onExceptionUnwind.bind(this);
    }
  },

  /**
   * A listener that gets called for every event fired on the page, when a list
   * of interesting events was provided with the pauseOnDOMEvents property. It
   * is used to set server-managed breakpoints on any existing event listeners
   * for those events.
   *
   * @param Event event
   *        The event that was fired.
   */
  _allEventsListener: function (event) {
    if (this._pauseOnDOMEvents == "*" ||
        this._pauseOnDOMEvents.includes(event.type)) {
      for (let listener of this._getAllEventListeners(event.target)) {
        if (event.type == listener.type || this._pauseOnDOMEvents == "*") {
          this._breakOnEnter(listener.script);
        }
      }
    }
  },

  /**
   * Return an array containing all the event listeners attached to the
   * specified event target and its ancestors in the event target chain.
   *
   * @param EventTarget eventTarget
   *        The target the event was dispatched on.
   * @returns Array
   */
  _getAllEventListeners: function (eventTarget) {
    let targets = Services.els.getEventTargetChainFor(eventTarget, true);
    let listeners = [];

    for (let target of targets) {
      let handlers = Services.els.getListenerInfoFor(target);
      for (let handler of handlers) {
        // Null is returned for all-events handlers, and native event listeners
        // don't provide any listenerObject, which makes them not that useful to
        // a JS debugger.
        if (!handler || !handler.listenerObject || !handler.type) {
          continue;
        }
        // Create a listener-like object suitable for our purposes.
        let l = Object.create(null);
        l.type = handler.type;
        let listener = handler.listenerObject;
        let listenerDO = this.globalDebugObject.makeDebuggeeValue(listener);
        // If the listener is not callable, assume it is an event handler object.
        if (!listenerDO.callable) {
          // For some events we don't have permission to access the
          // 'handleEvent' property when running in content scope.
          if (!listenerDO.unwrap()) {
            continue;
          }
          let heDesc;
          while (!heDesc && listenerDO) {
            heDesc = listenerDO.getOwnPropertyDescriptor("handleEvent");
            listenerDO = listenerDO.proto;
          }
          if (heDesc && heDesc.value) {
            listenerDO = heDesc.value;
          }
        }
        // When the listener is a bound function, we are actually interested in
        // the target function.
        while (listenerDO.isBoundFunction) {
          listenerDO = listenerDO.boundTargetFunction;
        }
        l.script = listenerDO.script;
        // Chrome listeners won't be converted to debuggee values, since their
        // compartment is not added as a debuggee.
        if (!l.script) {
          continue;
        }
        listeners.push(l);
      }
    }
    return listeners;
  },

  /**
   * Set a breakpoint on the first line of the given script that has an entry
   * point.
   */
  _breakOnEnter: function (script) {
    let offsets = script.getAllOffsets();
    for (let line = 0, n = offsets.length; line < n; line++) {
      if (offsets[line]) {
        // N.B. Hidden breakpoints do not have an original location, and are not
        // stored in the breakpoint actor map.
        let actor = new BreakpointActor(this);
        this.threadLifetimePool.addActor(actor);

        let scripts = this.dbg.findScripts({ source: script.source, line: line });
        let entryPoints = findEntryPointsForLine(scripts, line);
        setBreakpointAtEntryPoints(actor, entryPoints);
        this._hiddenBreakpoints.set(actor.actorID, actor);
        break;
      }
    }
  },

  /**
   * Helper method that returns the next frame when stepping.
   */
  _getNextStepFrame: function (frame) {
    let stepFrame = frame.reportedPop ? frame.older : frame;
    if (!stepFrame || !stepFrame.script) {
      stepFrame = null;
    }
    return stepFrame;
  },

  onClientEvaluate: function (request) {
    if (this.state !== "paused") {
      return { error: "wrongState",
               message: "Debuggee must be paused to evaluate code." };
    }

    let frame = this._requestFrame(request.frame);
    if (!frame) {
      return { error: "unknownFrame",
               message: "Evaluation frame not found" };
    }

    if (!frame.environment) {
      return { error: "notDebuggee",
               message: "cannot access the environment of this frame." };
    }

    let youngest = this.youngestFrame;

    // Put ourselves back in the running state and inform the client.
    let resumedPacket = this._resumed();
    this.conn.send(resumedPacket);

    // Run the expression.
    // XXX: test syntax errors
    let completion = frame.eval(request.expression);

    // Put ourselves back in the pause state.
    let packet = this._paused(youngest);
    packet.why = { type: "clientEvaluated",
                   frameFinished: this.createProtocolCompletionValue(completion) };

    // Return back to our previous pause's event loop.
    return packet;
  },

  onFrames: function (request) {
    if (this.state !== "paused") {
      return { error: "wrongState",
               message: "Stack frames are only available while the debuggee is paused."};
    }

    let start = request.start ? request.start : 0;
    let count = request.count;

    // Find the starting frame...
    let frame = this.youngestFrame;
    let i = 0;
    while (frame && (i < start)) {
      frame = frame.older;
      i++;
    }

    // Return request.count frames, or all remaining
    // frames if count is not defined.
    let promises = [];
    for (; frame && (!count || i < (start + count)); i++, frame = frame.older) {
      let form = this._createFrameActor(frame).form();
      form.depth = i;

      let framePromise = this.sources.getOriginalLocation(new GeneratedLocation(
        this.sources.createNonSourceMappedActor(frame.script.source),
        form.where.line,
        form.where.column
      )).then((originalLocation) => {
        if (!originalLocation.originalSourceActor) {
          return null;
        }

        let sourceForm = originalLocation.originalSourceActor.form();
        form.where = {
          source: sourceForm,
          line: originalLocation.originalLine,
          column: originalLocation.originalColumn
        };
        form.source = sourceForm;
        return form;
      });
      promises.push(framePromise);
    }

    return Promise.all(promises).then(function (frames) {
      // Filter null values because sourcemapping may have failed.
      return { frames: frames.filter(x => !!x) };
    });
  },

  onReleaseMany: function (request) {
    if (!request.actors) {
      return { error: "missingParameter",
               message: "no actors were specified" };
    }

    let res;
    for (let actorID of request.actors) {
      let actor = this.threadLifetimePool.get(actorID);
      if (!actor) {
        if (!res) {
          res = { error: "notReleasable",
                  message: "Only thread-lifetime actors can be released." };
        }
        continue;
      }
      actor.onRelease();
    }
    return res ? res : {};
  },

  /**
   * Get the script and source lists from the debugger.
   */
  _discoverSources: function () {
    // Only get one script per Debugger.Source.
    const sourcesToScripts = new Map();
    const scripts = this.dbg.findScripts();

    for (let i = 0, len = scripts.length; i < len; i++) {
      let s = scripts[i];
      if (s.source) {
        sourcesToScripts.set(s.source, s);
      }
    }

    return Promise.all([...sourcesToScripts.values()].map(script => {
      return this.sources.createSourceActors(script.source);
    }));
  },

  onSources: function (request) {
    return this._discoverSources().then(() => {
      // No need to flush the new source packets here, as we are sending the
      // list of sources out immediately and we don't need to invoke the
      // overhead of an RDP packet for every source right now. Let the default
      // timeout flush the buffered packets.

      return {
        sources: this.sources.iter().map(s => s.form())
      };
    });
  },

  /**
   * Disassociate all breakpoint actors from their scripts and clear the
   * breakpoint handlers. This method can be used when the thread actor intends
   * to keep the breakpoint store, but needs to clear any actual breakpoints,
   * e.g. due to a page navigation. This way the breakpoint actors' script
   * caches won't hold on to the Debugger.Script objects leaking memory.
   */
  disableAllBreakpoints: function () {
    for (let bpActor of this.breakpointActorMap.findActors()) {
      bpActor.removeScripts();
    }
  },

  /**
   * Handle a protocol request to pause the debuggee.
   */
  onInterrupt: function (request) {
    if (this.state == "exited") {
      return { type: "exited" };
    } else if (this.state == "paused") {
      // TODO: return the actual reason for the existing pause.
      return { type: "paused", why: { type: "alreadyPaused" } };
    } else if (this.state != "running") {
      return { error: "wrongState",
               message: "Received interrupt request in " + this.state +
                        " state." };
    }

    try {
      // If execution should pause just before the next JavaScript bytecode is
      // executed, just set an onEnterFrame handler.
      if (request.when == "onNext") {
        let onEnterFrame = (frame) => {
          return this._pauseAndRespond(frame, { type: "interrupted", onNext: true });
        };
        this.dbg.onEnterFrame = onEnterFrame;

        return { type: "willInterrupt" };
      }

      // If execution should pause immediately, just put ourselves in the paused
      // state.
      let packet = this._paused();
      if (!packet) {
        return { error: "notInterrupted" };
      }
      packet.why = { type: "interrupted" };

      // Send the response to the interrupt request now (rather than
      // returning it), because we're going to start a nested event loop
      // here.
      this.conn.send(packet);

      // Start a nested event loop.
      this._pushThreadPause();

      // We already sent a response to this request, don't send one
      // now.
      return null;
    } catch (e) {
      reportError(e);
      return { error: "notInterrupted", message: e.toString() };
    }
  },

  /**
   * Handle a protocol request to retrieve all the event listeners on the page.
   */
  onEventListeners: function (request) {
    // This request is only supported in content debugging.
    if (!this.global) {
      return {
        error: "notImplemented",
        message: "eventListeners request is only supported in content debugging"
      };
    }

    let nodes = this.global.document.getElementsByTagName("*");
    nodes = [this.global].concat([].slice.call(nodes));
    let listeners = [];

    for (let node of nodes) {
      let handlers = Services.els.getListenerInfoFor(node);

      for (let handler of handlers) {
        // Create a form object for serializing the listener via the protocol.
        let listenerForm = Object.create(null);
        let listener = handler.listenerObject;
        // Native event listeners don't provide any listenerObject or type and
        // are not that useful to a JS debugger.
        if (!listener || !handler.type) {
          continue;
        }

        // There will be no tagName if the event listener is set on the window.
        let selector = node.tagName ? findCssSelector(node) : "window";
        let nodeDO = this.globalDebugObject.makeDebuggeeValue(node);
        listenerForm.node = {
          selector: selector,
          object: createValueGrip(nodeDO, this._pausePool, this.objectGrip)
        };
        listenerForm.type = handler.type;
        listenerForm.capturing = handler.capturing;
        listenerForm.allowsUntrusted = handler.allowsUntrusted;
        listenerForm.inSystemEventGroup = handler.inSystemEventGroup;
        let handlerName = "on" + listenerForm.type;
        listenerForm.isEventHandler = false;
        if (typeof node.hasAttribute !== "undefined") {
          listenerForm.isEventHandler = !!node.hasAttribute(handlerName);
        }
        if (node[handlerName]) {
          listenerForm.isEventHandler = !!node[handlerName];
        }
        // Get the Debugger.Object for the listener object.
        let listenerDO = this.globalDebugObject.makeDebuggeeValue(listener);
        // If the listener is an object with a 'handleEvent' method, use that.
        if (listenerDO.class == "Object" || listenerDO.class == "XULElement") {
          // For some events we don't have permission to access the
          // 'handleEvent' property when running in content scope.
          if (!listenerDO.unwrap()) {
            continue;
          }
          let heDesc;
          while (!heDesc && listenerDO) {
            heDesc = listenerDO.getOwnPropertyDescriptor("handleEvent");
            listenerDO = listenerDO.proto;
          }
          if (heDesc && heDesc.value) {
            listenerDO = heDesc.value;
          }
        }
        // When the listener is a bound function, we are actually interested in
        // the target function.
        while (listenerDO.isBoundFunction) {
          listenerDO = listenerDO.boundTargetFunction;
        }
        listenerForm.function = createValueGrip(listenerDO, this._pausePool,
          this.objectGrip);
        listeners.push(listenerForm);
      }
    }
    return { listeners: listeners };
  },

  /**
   * Return the Debug.Frame for a frame mentioned by the protocol.
   */
  _requestFrame: function (frameID) {
    if (!frameID) {
      return this.youngestFrame;
    }

    if (this._framePool.has(frameID)) {
      return this._framePool.get(frameID).frame;
    }

    return undefined;
  },

  _paused: function (frame) {
    // We don't handle nested pauses correctly.  Don't try - if we're
    // paused, just continue running whatever code triggered the pause.
    // We don't want to actually have nested pauses (although we
    // have nested event loops).  If code runs in the debuggee during
    // a pause, it should cause the actor to resume (dropping
    // pause-lifetime actors etc) and then repause when complete.

    if (this.state === "paused") {
      return undefined;
    }

    // Clear stepping hooks.
    this.dbg.onEnterFrame = undefined;
    this.dbg.onExceptionUnwind = undefined;
    if (frame) {
      frame.onStep = undefined;
      frame.onPop = undefined;
    }

    // Clear DOM event breakpoints.
    // XPCShell tests don't use actual DOM windows for globals and cause
    // removeListenerForAllEvents to throw.
    if (!isWorker && this.global && !this.global.toString().includes("Sandbox")) {
      Services.els.removeListenerForAllEvents(this.global, this._allEventsListener, true);
      for (let [, bp] of this._hiddenBreakpoints) {
        bp.delete();
      }
      this._hiddenBreakpoints.clear();
    }

    this._state = "paused";

    // Create the actor pool that will hold the pause actor and its
    // children.
    assert(!this._pausePool, "No pause pool should exist yet");
    this._pausePool = new ActorPool(this.conn);
    this.conn.addActorPool(this._pausePool);

    // Give children of the pause pool a quick link back to the
    // thread...
    this._pausePool.threadActor = this;

    // Create the pause actor itself...
    assert(!this._pauseActor, "No pause actor should exist yet");
    this._pauseActor = new PauseActor(this._pausePool);
    this._pausePool.addActor(this._pauseActor);

    // Update the list of frames.
    let poppedFrames = this._updateFrames();

    // Send off the paused packet and spin an event loop.
    let packet = { from: this.actorID,
                   type: "paused",
                   actor: this._pauseActor.actorID };
    if (frame) {
      packet.frame = this._createFrameActor(frame).form();
    }

    if (poppedFrames) {
      packet.poppedFrames = poppedFrames;
    }

    return packet;
  },

  _resumed: function () {
    this._state = "running";

    // Drop the actors in the pause actor pool.
    this.conn.removeActorPool(this._pausePool);

    this._pausePool = null;
    this._pauseActor = null;

    return { from: this.actorID, type: "resumed" };
  },

  /**
   * Expire frame actors for frames that have been popped.
   *
   * @returns A list of actor IDs whose frames have been popped.
   */
  _updateFrames: function () {
    let popped = [];

    // Create the actor pool that will hold the still-living frames.
    let framePool = new ActorPool(this.conn);
    let frameList = [];

    for (let frameActor of this._frameActors) {
      if (frameActor.frame.live) {
        framePool.addActor(frameActor);
        frameList.push(frameActor);
      } else {
        popped.push(frameActor.actorID);
      }
    }

    // Remove the old frame actor pool, this will expire
    // any actors that weren't added to the new pool.
    if (this._framePool) {
      this.conn.removeActorPool(this._framePool);
    }

    this._frameActors = frameList;
    this._framePool = framePool;
    this.conn.addActorPool(framePool);

    return popped;
  },

  _createFrameActor: function (frame) {
    if (frame.actor) {
      return frame.actor;
    }

    let actor = new FrameActor(frame, this);
    this._frameActors.push(actor);
    this._framePool.addActor(actor);
    frame.actor = actor;

    return actor;
  },

  /**
   * Create and return an environment actor that corresponds to the provided
   * Debugger.Environment.
   * @param Debugger.Environment environment
   *        The lexical environment we want to extract.
   * @param object pool
   *        The pool where the newly-created actor will be placed.
   * @return The EnvironmentActor for environment or undefined for host
   *         functions or functions scoped to a non-debuggee global.
   */
  createEnvironmentActor: function (environment, pool) {
    if (!environment) {
      return undefined;
    }

    if (environment.actor) {
      return environment.actor;
    }

    let actor = new EnvironmentActor(environment, this);
    pool.addActor(actor);
    environment.actor = actor;

    return actor;
  },

  /**
   * Return a protocol completion value representing the given
   * Debugger-provided completion value.
   */
  createProtocolCompletionValue: function (completion) {
    let protoValue = {};
    if (completion == null) {
      protoValue.terminated = true;
    } else if ("return" in completion) {
      protoValue.return = createValueGrip(completion.return,
        this._pausePool, this.objectGrip);
    } else if ("throw" in completion) {
      protoValue.throw = createValueGrip(completion.throw,
        this._pausePool, this.objectGrip);
    } else {
      protoValue.return = createValueGrip(completion.yield,
        this._pausePool, this.objectGrip);
    }
    return protoValue;
  },

  /**
   * Create a grip for the given debuggee object.
   *
   * @param value Debugger.Object
   *        The debuggee object value.
   * @param pool ActorPool
   *        The actor pool where the new object actor will be added.
   */
  objectGrip: function (value, pool) {
    if (!pool.objectActors) {
      pool.objectActors = new WeakMap();
    }

    if (pool.objectActors.has(value)) {
      return pool.objectActors.get(value).grip();
    } else if (this.threadLifetimePool.objectActors.has(value)) {
      return this.threadLifetimePool.objectActors.get(value).grip();
    }

    let actor = new PauseScopedObjectActor(value, {
      getGripDepth: () => this._gripDepth,
      incrementGripDepth: () => this._gripDepth++,
      decrementGripDepth: () => this._gripDepth--,
      createValueGrip: v => createValueGrip(v, this._pausePool,
        this.pauseObjectGrip),
      sources: () => this.sources,
      createEnvironmentActor: (e, p) =>
        this.createEnvironmentActor(e, p),
      promote: () => this.threadObjectGrip(actor),
      isThreadLifetimePool: () =>
        actor.registeredPool !== this.threadLifetimePool,
      getGlobalDebugObject: () => this.globalDebugObject
    });
    pool.addActor(actor);
    pool.objectActors.set(value, actor);
    return actor.grip();
  },

  /**
   * Create a grip for the given debuggee object with a pause lifetime.
   *
   * @param value Debugger.Object
   *        The debuggee object value.
   */
  pauseObjectGrip: function (value) {
    if (!this._pausePool) {
      throw new Error("Object grip requested while not paused.");
    }

    return this.objectGrip(value, this._pausePool);
  },

  /**
   * Extend the lifetime of the provided object actor to thread lifetime.
   *
   * @param actor object
   *        The object actor.
   */
  threadObjectGrip: function (actor) {
    // We want to reuse the existing actor ID, so we just remove it from the
    // current pool's weak map and then let pool.addActor do the rest.
    actor.registeredPool.objectActors.delete(actor.obj);
    this.threadLifetimePool.addActor(actor);
    this.threadLifetimePool.objectActors.set(actor.obj, actor);
  },

  /**
   * Handle a protocol request to promote multiple pause-lifetime grips to
   * thread-lifetime grips.
   *
   * @param aRequest object
   *        The protocol request object.
   */
  onThreadGrips: function (request) {
    if (this.state != "paused") {
      return { error: "wrongState" };
    }

    if (!request.actors) {
      return { error: "missingParameter",
               message: "no actors were specified" };
    }

    for (let actorID of request.actors) {
      let actor = this._pausePool.get(actorID);
      if (actor) {
        this.threadObjectGrip(actor);
      }
    }
    return {};
  },

  /**
   * Create a long string grip that is scoped to a pause.
   *
   * @param string String
   *        The string we are creating a grip for.
   */
  pauseLongStringGrip: function (string) {
    return longStringGrip(string, this._pausePool);
  },

  /**
   * Create a long string grip that is scoped to a thread.
   *
   * @param string String
   *        The string we are creating a grip for.
   */
  threadLongStringGrip: function (string) {
    return longStringGrip(string, this._threadLifetimePool);
  },

  // JS Debugger API hooks.

  /**
   * A function that the engine calls when a call to a debug event hook,
   * breakpoint handler, watchpoint handler, or similar function throws some
   * exception.
   *
   * @param exception exception
   *        The exception that was thrown in the debugger code.
   */
  uncaughtExceptionHook: function (exception) {
    dumpn("Got an exception: " + exception.message + "\n" + exception.stack);
  },

  /**
   * A function that the engine calls when a debugger statement has been
   * executed in the specified frame.
   *
   * @param frame Debugger.Frame
   *        The stack frame that contained the debugger statement.
   */
  onDebuggerStatement: function (frame) {
    // Don't pause if we are currently stepping (in or over) or the frame is
    // black-boxed.
    const generatedLocation = this.sources.getFrameLocation(frame);
    const { originalSourceActor } = this.unsafeSynchronize(
      this.sources.getOriginalLocation(generatedLocation));
    const url = originalSourceActor ? originalSourceActor.url : null;

    return this.sources.isBlackBoxed(url) || frame.onStep
      ? undefined
      : this._pauseAndRespond(frame, { type: "debuggerStatement" });
  },

  /**
   * A function that the engine calls when an exception has been thrown and has
   * propagated to the specified frame.
   *
   * @param youngestFrame Debugger.Frame
   *        The youngest remaining stack frame.
   * @param value object
   *        The exception that was thrown.
   */
  onExceptionUnwind: function (youngestFrame, value) {
    let willBeCaught = false;
    for (let frame = youngestFrame; frame != null; frame = frame.older) {
      if (frame.script.isInCatchScope(frame.offset)) {
        willBeCaught = true;
        break;
      }
    }

    if (willBeCaught && this._options.ignoreCaughtExceptions) {
      return undefined;
    }

    // NS_ERROR_NO_INTERFACE exceptions are a special case in browser code,
    // since they're almost always thrown by QueryInterface functions, and
    // handled cleanly by native code.
    if (value == Cr.NS_ERROR_NO_INTERFACE) {
      return undefined;
    }

    const generatedLocation = this.sources.getFrameLocation(youngestFrame);
    const { originalSourceActor } = this.unsafeSynchronize(
      this.sources.getOriginalLocation(generatedLocation));
    const url = originalSourceActor ? originalSourceActor.url : null;

    if (this.sources.isBlackBoxed(url)) {
      return undefined;
    }

    try {
      let packet = this._paused(youngestFrame);
      if (!packet) {
        return undefined;
      }

      packet.why = { type: "exception",
                     exception: createValueGrip(value, this._pausePool,
                                                this.objectGrip)
      };
      this.conn.send(packet);

      this._pushThreadPause();
    } catch (e) {
      reportError(e, "Got an exception during TA_onExceptionUnwind: ");
    }

    return undefined;
  },

  /**
   * A function that the engine calls when a new script has been loaded into the
   * scope of the specified debuggee global.
   *
   * @param script Debugger.Script
   *        The source script that has been loaded into a debuggee compartment.
   * @param global Debugger.Object
   *        A Debugger.Object instance whose referent is the global object.
   */
  onNewScript: function (script, global) {
    this._addSource(script.source);
  },

  /**
   * A function called when there's a new source from a thread actor's sources.
   * Emits `newSource` on the tab actor.
   *
   * @param {SourceActor} source
   */
  onNewSourceEvent: function (source) {
    const type = "newSource";
    this.conn.send({
      from: this._parent.actorID,
      type,
      source: source.form()
    });

    // For compatibility and debugger still using `newSource` on the thread client,
    // still emit this event here. Clean up in bug 1247084
    this.conn.send({
      from: this.actorID,
      type,
      source: source.form()
    });
  },

  /**
   * A function called when there's an updated source from a thread actor' sources.
   * Emits `updatedSource` on the tab actor.
   *
   * @param {SourceActor} source
   */
  onUpdatedSourceEvent: function (source) {
    this.conn.send({
      from: this._parent.actorID,
      type: "updatedSource",
      source: source.form()
    });
  },

  /**
   * Add the provided source to the server cache.
   *
   * @param aSource Debugger.Source
   *        The source that will be stored.
   * @returns true, if the source was added; false otherwise.
   */
  _addSource: function (source) {
    if (!this.sources.allowSource(source)) {
      return false;
    }

    // Preloaded WebExtension content scripts may be cached internally by
    // ExtensionContent.jsm and ThreadActor would ignore them on a page reload
    // because it finds them in the _debuggerSourcesSeen WeakSet,
    // and so we also need to be sure that there is still a source actor for the source.
    if (this._debuggerSourcesSeen.has(source) && this.sources.hasSourceActor(source)) {
      return false;
    }

    let sourceActor = this.sources.createNonSourceMappedActor(source);
    let bpActors = [...this.breakpointActorMap.findActors()];

    if (this._options.useSourceMaps) {
      let promises = [];

      // Go ahead and establish the source actors for this script, which
      // fetches sourcemaps if available and sends onNewSource
      // notifications.
      let sourceActorsCreated = this.sources._createSourceMappedActors(source);

      if (bpActors.length) {
        // We need to use unsafeSynchronize here because if the page is being reloaded,
        // this call will replace the previous set of source actors for this source
        // with a new one. If the source actors have not been replaced by the time
        // we try to reset the breakpoints below, their location objects will still
        // point to the old set of source actors, which point to different
        // scripts.
        this.unsafeSynchronize(sourceActorsCreated);
      }

      for (const actor of bpActors) {
        if (actor.isPending) {
          promises.push(actor.originalLocation.originalSourceActor._setBreakpoint(actor));
        } else {
          promises.push(
            this.sources.getAllGeneratedLocations(actor.originalLocation).then(
              (generatedLocations) => {
                if (generatedLocations.length > 0 &&
                    generatedLocations[0].generatedSourceActor
                                         .actorID === sourceActor.actorID) {
                  sourceActor._setBreakpointAtAllGeneratedLocations(
                    actor, generatedLocations);
                }
              }));
        }
      }

      if (promises.length > 0) {
        this.unsafeSynchronize(Promise.all(promises));
      }
    } else {
      // Bug 1225160: If addSource is called in response to a new script
      // notification, and this notification was triggered by loading a JSM from
      // chrome code, calling unsafeSynchronize could cause a debuggee timer to
      // fire. If this causes the JSM to be loaded a second time, the browser
      // will crash, because loading JSMS is not reentrant, and the first load
      // has not completed yet.
      //
      // The root of the problem is that unsafeSynchronize can cause debuggee
      // code to run. Unfortunately, fixing that is prohibitively difficult. The
      // best we can do at the moment is disable source maps for the browser
      // debugger, and carefully avoid the use of unsafeSynchronize in this
      // function when source maps are disabled.
      for (let actor of bpActors) {
        if (actor.isPending) {
          actor.originalLocation.originalSourceActor._setBreakpoint(actor);
        } else {
          actor.originalLocation.originalSourceActor._setBreakpointAtGeneratedLocation(
            actor, GeneratedLocation.fromOriginalLocation(actor.originalLocation)
          );
        }
      }
    }

    this._debuggerSourcesSeen.add(source);
    return true;
  },

  /**
   * Get prototypes and properties of multiple objects.
   */
  onPrototypesAndProperties: function (request) {
    let result = {};
    for (let actorID of request.actors) {
      // This code assumes that there are no lazily loaded actors returned
      // by this call.
      let actor = this.conn.getActor(actorID);
      if (!actor) {
        return { from: this.actorID,
                 error: "noSuchActor" };
      }
      let handler = actor.onPrototypeAndProperties;
      if (!handler) {
        return { from: this.actorID,
                 error: "unrecognizedPacketType",
                 message: ('Actor "' + actorID +
                           '" does not recognize the packet type ' +
                           '"prototypeAndProperties"') };
      }
      result[actorID] = handler.call(actor, {});
    }
    return { from: this.actorID,
             actors: result };
  }
});

Object.assign(ThreadActor.prototype.requestTypes, {
  "attach": ThreadActor.prototype.onAttach,
  "detach": ThreadActor.prototype.onDetach,
  "reconfigure": ThreadActor.prototype.onReconfigure,
  "resume": ThreadActor.prototype.onResume,
  "clientEvaluate": ThreadActor.prototype.onClientEvaluate,
  "frames": ThreadActor.prototype.onFrames,
  "interrupt": ThreadActor.prototype.onInterrupt,
  "eventListeners": ThreadActor.prototype.onEventListeners,
  "releaseMany": ThreadActor.prototype.onReleaseMany,
  "sources": ThreadActor.prototype.onSources,
  "threadGrips": ThreadActor.prototype.onThreadGrips,
  "prototypesAndProperties": ThreadActor.prototype.onPrototypesAndProperties
});

exports.ThreadActor = ThreadActor;

/**
 * Creates a PauseActor.
 *
 * PauseActors exist for the lifetime of a given debuggee pause.  Used to
 * scope pause-lifetime grips.
 *
 * @param ActorPool aPool
 *        The actor pool created for this pause.
 */
function PauseActor(pool) {
  this.pool = pool;
}

PauseActor.prototype = {
  actorPrefix: "pause"
};

function hackDebugger(Debugger) {
  // TODO: Improve native code instead of hacking on top of it

  /**
   * Override the toString method in order to get more meaningful script output
   * for debugging the debugger.
   */
  Debugger.Script.prototype.toString = function () {
    if (this.type == "wasm") {
      return "[wasm]";
    }

    let output = "";
    if (this.url) {
      output += this.url;
    }

    if (typeof this.staticLevel != "undefined") {
      output += ":L" + this.staticLevel;
    }
    if (typeof this.startLine != "undefined") {
      output += ":" + this.startLine;
      if (this.lineCount && this.lineCount > 1) {
        output += "-" + (this.startLine + this.lineCount - 1);
      }
    }
    if (typeof this.startLine != "undefined") {
      output += ":" + this.startLine;
      if (this.lineCount && this.lineCount > 1) {
        output += "-" + (this.startLine + this.lineCount - 1);
      }
    }
    if (this.strictMode) {
      output += ":strict";
    }
    return output;
  };

  /**
   * Helper property for quickly getting to the line number a stack frame is
   * currently paused at.
   */
  Object.defineProperty(Debugger.Frame.prototype, "line", {
    configurable: true,
    get: function () {
      if (this.script) {
        return this.script.getOffsetLocation(this.offset).lineNumber;
      }
      return null;
    }
  });
}

/**
 * Creates an actor for handling chrome debugging. ChromeDebuggerActor is a
 * thin wrapper over ThreadActor, slightly changing some of its behavior.
 *
 * @param connection object
 *        The DebuggerServerConnection with which this ChromeDebuggerActor
 *        is associated. (Currently unused, but required to make this
 *        constructor usable with addGlobalActor.)
 *
 * @param parent object
 *        This actor's parent actor. See ThreadActor for a list of expected
 *        properties.
 */
function ChromeDebuggerActor(connection, parent) {
  ThreadActor.prototype.initialize.call(this, parent);
}

ChromeDebuggerActor.prototype = Object.create(ThreadActor.prototype);

Object.assign(ChromeDebuggerActor.prototype, {
  constructor: ChromeDebuggerActor,

  // A constant prefix that will be used to form the actor ID by the server.
  actorPrefix: "chromeDebugger"
});

exports.ChromeDebuggerActor = ChromeDebuggerActor;

/**
 * Creates an actor for handling add-on debugging. AddonThreadActor is
 * a thin wrapper over ThreadActor.
 *
 * @param connection object
 *        The DebuggerServerConnection with which this AddonThreadActor
 *        is associated. (Currently unused, but required to make this
 *        constructor usable with addGlobalActor.)
 *
 * @param parent object
 *        This actor's parent actor. See ThreadActor for a list of expected
 *        properties.
 */
function AddonThreadActor(connection, parent) {
  ThreadActor.prototype.initialize.call(this, parent);
}

AddonThreadActor.prototype = Object.create(ThreadActor.prototype);

Object.assign(AddonThreadActor.prototype, {
  constructor: AddonThreadActor,

  // A constant prefix that will be used to form the actor ID by the server.
  actorPrefix: "addonThread"
});

exports.AddonThreadActor = AddonThreadActor;

// Utility functions.

/**
 * Report the given error in the error console and to stdout.
 *
 * @param Error error
 *        The error object you wish to report.
 * @param String prefix
 *        An optional prefix for the reported error message.
 */
var oldReportError = reportError;
this.reportError = function (error, prefix = "") {
  assert(error instanceof Error, "Must pass Error objects to reportError");
  let msg = prefix + error.message + ":\n" + error.stack;
  oldReportError(msg);
  dumpn(msg);
};

/**
 * Find the scripts which contain offsets that are an entry point to the given
 * line.
 *
 * @param Array scripts
 *        The set of Debugger.Scripts to consider.
 * @param Number line
 *        The line we are searching for entry points into.
 * @returns Array of objects of the form { script, offsets } where:
 *          - script is a Debugger.Script
 *          - offsets is an array of offsets that are entry points into the
 *            given line.
 */
function findEntryPointsForLine(scripts, line) {
  const entryPoints = [];
  for (let script of scripts) {
    const offsets = script.getLineOffsets(line);
    if (offsets.length) {
      entryPoints.push({ script, offsets });
    }
  }
  return entryPoints;
}

/**
 * Unwrap a global that is wrapped in a |Debugger.Object|, or if the global has
 * become a dead object, return |undefined|.
 *
 * @param Debugger.Object wrappedGlobal
 *        The |Debugger.Object| which wraps a global.
 *
 * @returns {Object|undefined}
 *          Returns the unwrapped global object or |undefined| if unwrapping
 *          failed.
 */
exports.unwrapDebuggerObjectGlobal = wrappedGlobal => {
  try {
    // Because of bug 991399 we sometimes get nuked window references here. We
    // just bail out in that case.
    //
    // Note that addon sandboxes have a DOMWindow as their prototype. So make
    // sure that we can touch the prototype too (whatever it is), in case _it_
    // is it a nuked window reference. We force stringification to make sure
    // that any dead object proxies make themselves known.
    let global = wrappedGlobal.unsafeDereference();
    Object.getPrototypeOf(global) + "";
    return global;
  } catch (e) {
    return undefined;
  }
};
PK
!<�/v��:chrome/devtools/modules/devtools/server/actors/timeline.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";

/**
 * Many Gecko operations (painting, reflows, restyle, ...) can be tracked
 * in real time. A marker is a representation of one operation. A marker
 * has a name, start and end timestamps. Markers are stored in docShells.
 *
 * This actor exposes this tracking mechanism to the devtools protocol.
 * Most of the logic is handled in devtools/server/performance/timeline.js
 * This just wraps that module up and exposes it via RDP.
 *
 * For more documentation:
 * @see devtools/server/performance/timeline.js
 */

const protocol = require("devtools/shared/protocol");
const { Option, RetVal } = protocol;
const { actorBridgeWithSpec } = require("devtools/server/actors/common");
const { Timeline } = require("devtools/server/performance/timeline");
const { timelineSpec } = require("devtools/shared/specs/timeline");

/**
 * The timeline actor pops and forwards timeline markers registered in docshells.
 */
exports.TimelineActor = protocol.ActorClassWithSpec(timelineSpec, {
  /**
   * Initializes this actor with the provided connection and tab actor.
   */
  initialize: function (conn, tabActor) {
    protocol.Actor.prototype.initialize.call(this, conn);
    this.tabActor = tabActor;
    this.bridge = new Timeline(tabActor);

    this._onTimelineDocLoading = this._onTimelineDocLoading.bind(this);
    this._onTimelineMarkers = this._onTimelineMarkers.bind(this);
    this._onTimelineTicks = this._onTimelineTicks.bind(this);
    this._onTimelineMemory = this._onTimelineMemory.bind(this);
    this._onTimelineFrames = this._onTimelineFrames.bind(this);

    this.bridge.on("doc-loading", this._onTimelineDocLoading);
    this.bridge.on("markers", this._onTimelineMarkers);
    this.bridge.on("ticks", this._onTimelineTicks);
    this.bridge.on("memory", this._onTimelineMemory);
    this.bridge.on("frames", this._onTimelineFrames);
  },

  /**
   * Destroys this actor, stopping recording first.
   */
  destroy: function () {
    this.bridge.off("doc-loading", this._onTimelineDocLoading);
    this.bridge.off("markers", this._onTimelineMarkers);
    this.bridge.off("ticks", this._onTimelineTicks);
    this.bridge.off("memory", this._onTimelineMemory);
    this.bridge.off("frames", this._onTimelineFrames);
    this.bridge.destroy();
    this.bridge = null;
    this.tabActor = null;
    protocol.Actor.prototype.destroy.call(this);
  },

  /**
   * Propagate events from the Timeline module over RDP if the event is defined here.
   */
  _onTimelineDocLoading: function (...args) {
    this.emit("doc-loading", ...args);
  },
  _onTimelineMarkers: function (...args) {
    this.emit("markers", ...args);
  },
  _onTimelineTicks: function (...args) {
    this.emit("ticks", ...args);
  },
  _onTimelineMemory: function (...args) {
    this.emit("memory", ...args);
  },
  _onTimelineFrames: function (...args) {
    this.emit("frames", ...args);
  },

  isRecording: actorBridgeWithSpec("isRecording", {
    request: {},
    response: {
      value: RetVal("boolean")
    }
  }),

  start: actorBridgeWithSpec("start", {
    request: {
      withMarkers: Option(0, "boolean"),
      withTicks: Option(0, "boolean"),
      withMemory: Option(0, "boolean"),
      withFrames: Option(0, "boolean"),
      withGCEvents: Option(0, "boolean"),
      withDocLoadingEvents: Option(0, "boolean")
    },
    response: {
      value: RetVal("number")
    }
  }),

  stop: actorBridgeWithSpec("stop", {
    response: {
      // Set as possibly nullable due to the end time possibly being
      // undefined during destruction
      value: RetVal("nullable:number")
    }
  }),
});
PK
!<�`qmqmBchrome/devtools/modules/devtools/server/actors/utils/TabSources.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 DevToolsUtils = require("devtools/shared/DevToolsUtils");
const { assert, fetch } = DevToolsUtils;
const EventEmitter = require("devtools/shared/event-emitter");
const { OriginalLocation, GeneratedLocation } = require("devtools/server/actors/common");
const { joinURI } = require("devtools/shared/path");

loader.lazyRequireGetter(this, "SourceActor", "devtools/server/actors/source", true);
loader.lazyRequireGetter(this, "isEvalSource", "devtools/server/actors/source", true);
loader.lazyRequireGetter(this, "SourceMapConsumer", "source-map", true);
loader.lazyRequireGetter(this, "SourceMapGenerator", "source-map", true);
loader.lazyRequireGetter(this, "WasmRemap", "devtools/shared/wasm-source-map", true);

/**
 * Manages the sources for a thread. Handles source maps, locations in the
 * sources, etc for ThreadActors.
 */
function TabSources(threadActor, allowSourceFn = () => true) {
  EventEmitter.decorate(this);

  this._thread = threadActor;
  this._useSourceMaps = true;
  this._autoBlackBox = true;
  this._anonSourceMapId = 1;
  this.allowSource = source => {
    return !isHiddenSource(source) && allowSourceFn(source);
  };

  this.blackBoxedSources = new Set();
  this.prettyPrintedSources = new Map();
  this.neverAutoBlackBoxSources = new Set();

  // generated Debugger.Source -> promise of SourceMapConsumer
  this._sourceMaps = new Map();
  // sourceMapURL -> promise of SourceMapConsumer
  this._sourceMapCache = Object.create(null);
  // Debugger.Source -> SourceActor
  this._sourceActors = new Map();
  // url -> SourceActor
  this._sourceMappedSourceActors = Object.create(null);
}

/**
 * Matches strings of the form "foo.min.js" or "foo-min.js", etc. If the regular
 * expression matches, we can be fairly sure that the source is minified, and
 * treat it as such.
 */
const MINIFIED_SOURCE_REGEXP = /\bmin\.js$/;

TabSources.prototype = {
  /**
   * Update preferences and clear out existing sources
   */
  setOptions: function (options) {
    let shouldReset = false;

    if ("useSourceMaps" in options) {
      shouldReset = true;
      this._useSourceMaps = options.useSourceMaps;
    }

    if ("autoBlackBox" in options) {
      shouldReset = true;
      this._autoBlackBox = options.autoBlackBox;
    }

    if (shouldReset) {
      this.reset();
    }
  },

  /**
   * Clear existing sources so they are recreated on the next access.
   *
   * @param Object opts
   *        Specify { sourceMaps: true } if you also want to clear
   *        the source map cache (usually done on reload).
   */
  reset: function (opts = {}) {
    this._sourceActors = new Map();
    this._sourceMaps = new Map();
    this._sourceMappedSourceActors = Object.create(null);

    if (opts.sourceMaps) {
      this._sourceMapCache = Object.create(null);
    }
  },

  /**
   * Return the source actor representing the `source` (or
   * `originalUrl`), creating one if none exists already. May return
   * null if the source is disallowed.
   *
   * @param Debugger.Source source
   *        The source to make an actor for
   * @param String originalUrl
   *        The original source URL of a sourcemapped source
   * @param optional Debguger.Source generatedSource
   *        The generated source that introduced this source via source map,
   *        if any.
   * @param optional String contentType
   *        The content type of the source, if immediately available.
   * @returns a SourceActor representing the source or null.
   */
  source: function ({ source, originalUrl, generatedSource,
                       isInlineSource, contentType }) {
    assert(source || (originalUrl && generatedSource),
           "TabSources.prototype.source needs an originalUrl or a source");

    if (source) {
      // If a source is passed, we are creating an actor for a real
      // source, which may or may not be sourcemapped.

      if (!this.allowSource(source)) {
        return null;
      }

      // It's a hack, but inline HTML scripts each have real sources,
      // but we want to represent all of them as one source as the
      // HTML page. The actor representing this fake HTML source is
      // stored in this array, which always has a URL, so check it
      // first.
      if (source.url in this._sourceMappedSourceActors) {
        return this._sourceMappedSourceActors[source.url];
      }

      if (isInlineSource) {
        // If it's an inline source, the fake HTML source hasn't been
        // created yet (would have returned above), so flip this source
        // into a sourcemapped state by giving it an `originalUrl` which
        // is the HTML url.
        originalUrl = source.url;
        source = null;
      } else if (this._sourceActors.has(source)) {
        return this._sourceActors.get(source);
      }
    } else if (originalUrl) {
      // Not all "original" scripts are distinctly separate from the
      // generated script. Pretty-printed sources have a sourcemap for
      // themselves, so we need to make sure there a real source
      // doesn't already exist with this URL.
      for (let [sourceData, actor] of this._sourceActors) {
        if (sourceData.url === originalUrl) {
          return actor;
        }
      }

      if (originalUrl in this._sourceMappedSourceActors) {
        return this._sourceMappedSourceActors[originalUrl];
      }
    }

    let actor = new SourceActor({
      thread: this._thread,
      source: source,
      originalUrl: originalUrl,
      generatedSource: generatedSource,
      isInlineSource: isInlineSource,
      contentType: contentType
    });

    let sourceActorStore = this._thread.sourceActorStore;
    let id = sourceActorStore.getReusableActorId(source, originalUrl);
    if (id) {
      actor.actorID = id;
    }

    this._thread.threadLifetimePool.addActor(actor);
    sourceActorStore.setReusableActorId(source, originalUrl, actor.actorID);

    if (this._autoBlackBox &&
        !this.neverAutoBlackBoxSources.has(actor.url) &&
        this._isMinifiedURL(actor.url)) {
      this.blackBox(actor.url);
      this.neverAutoBlackBoxSources.add(actor.url);
    }

    if (source) {
      this._sourceActors.set(source, actor);
    } else {
      this._sourceMappedSourceActors[originalUrl] = actor;
    }

    this._emitNewSource(actor);
    return actor;
  },

  _emitNewSource: function (actor) {
    if (!actor.source) {
      // Always notify if we don't have a source because that means
      // it's something that has been sourcemapped, or it represents
      // the HTML file that contains inline sources.
      this.emit("newSource", actor);
    } else {
      // If sourcemapping is enabled and a source has sourcemaps, we
      // create `SourceActor` instances for both the original and
      // generated sources. The source actors for the generated
      // sources are only for internal use, however; breakpoints are
      // managed by these internal actors. We only want to notify the
      // user of the original sources though, so if the actor has a
      // `Debugger.Source` instance and a valid source map (meaning
      // it's a generated source), don't send the notification.
      this.fetchSourceMap(actor.source).then(map => {
        if (!map) {
          this.emit("newSource", actor);
        }
      });
    }
  },

  _getSourceActor: function (source) {
    if (source.url in this._sourceMappedSourceActors) {
      return this._sourceMappedSourceActors[source.url];
    }

    if (this._sourceActors.has(source)) {
      return this._sourceActors.get(source);
    }

    return null;
  },

  hasSourceActor: function (source) {
    return !!this._getSourceActor(source);
  },

  getSourceActor: function (source) {
    const sourceActor = this._getSourceActor(source);

    if (!sourceActor) {
      throw new Error("getSource: could not find source actor for " +
                      (source.url || "source"));
    }

    return sourceActor;
  },

  getSourceActorByURL: function (url) {
    if (url) {
      for (let [source, actor] of this._sourceActors) {
        if (source.url === url) {
          return actor;
        }
      }

      if (url in this._sourceMappedSourceActors) {
        return this._sourceMappedSourceActors[url];
      }
    }

    throw new Error("getSourceActorByURL: could not find source for " + url);
  },

  /**
   * Returns true if the URL likely points to a minified resource, false
   * otherwise.
   *
   * @param String uri
   *        The url to test.
   * @returns Boolean
   */
  _isMinifiedURL: function (uri) {
    if (!uri) {
      return false;
    }

    try {
      let url = new URL(uri);
      let pathname = url.pathname;
      return MINIFIED_SOURCE_REGEXP.test(pathname.slice(pathname.lastIndexOf("/") + 1));
    } catch (e) {
      // Not a valid URL so don't try to parse out the filename, just test the
      // whole thing with the minified source regexp.
      return MINIFIED_SOURCE_REGEXP.test(uri);
    }
  },

  /**
   * Create a source actor representing this source. This ignores
   * source mapping and always returns an actor representing this real
   * source. Use `createSourceActors` if you want to respect source maps.
   *
   * @param Debugger.Source source
   *        The source instance to create an actor for.
   * @returns SourceActor
   */
  createNonSourceMappedActor: function (source) {
    // Don't use getSourceURL because we don't want to consider the
    // displayURL property if it's an eval source. We only want to
    // consider real URLs, otherwise if there is a URL but it's
    // invalid the code below will not set the content type, and we
    // will later try to fetch the contents of the URL to figure out
    // the content type, but it's a made up URL for eval sources.
    let url = isEvalSource(source) ? null : source.url;
    let spec = { source };

    // XXX bug 915433: We can't rely on Debugger.Source.prototype.text
    // if the source is an HTML-embedded <script> tag. Since we don't
    // have an API implemented to detect whether this is the case, we
    // need to be conservative and only treat valid js files as real
    // sources. Otherwise, use the `originalUrl` property to treat it
    // as an HTML source that manages multiple inline sources.

    // Assume the source is inline if the element that introduced it is not a
    // script element, or does not have a src attribute.
    let element = source.element ? source.element.unsafeDereference() : null;
    if (element && (element.tagName !== "SCRIPT" || !element.hasAttribute("src"))) {
      spec.isInlineSource = true;
    } else if (source.introductionType === "wasm") {
      // Wasm sources are not JavaScript. Give them their own content-type.
      spec.contentType = "text/wasm";
    } else if (source.introductionType === "debugger eval") {
      // All debugger eval code should have a text/javascript content-type.
      // See Bug 1399064
      spec.contentType = "text/javascript";
    } else if (url) {
      // There are a few special URLs that we know are JavaScript:
      // inline `javascript:` and code coming from the console
      if (url.indexOf("Scratchpad/") === 0 ||
          url.indexOf("javascript:") === 0 ||
          url === "debugger eval code") {
        spec.contentType = "text/javascript";
      } else {
        try {
          let pathname = new URL(url).pathname;
          let filename = pathname.slice(pathname.lastIndexOf("/") + 1);
          let index = filename.lastIndexOf(".");
          let extension = index >= 0 ? filename.slice(index + 1) : "";
          if (extension === "xml") {
            // XUL inline scripts may not correctly have the
            // `source.element` property, so do a blunt check here if
            // it's an xml page.
            spec.isInlineSource = true;
          } else if (extension === "js") {
            spec.contentType = "text/javascript";
          }
        } catch (e) {
          // This only needs to be here because URL is not yet exposed to
          // workers. (BUG 1258892)
          const filename = url;
          const index = filename.lastIndexOf(".");
          const extension = index >= 0 ? filename.slice(index + 1) : "";
          if (extension === "js") {
            spec.contentType = "text/javascript";
          }
        }
      }
    } else {
      // Assume the content is javascript if there's no URL
      spec.contentType = "text/javascript";
    }

    return this.source(spec);
  },

  /**
   * This is an internal function that returns a promise of an array
   * of source actors representing all the source mapped sources of
   * `source`, or `null` if the source is not sourcemapped or
   * sourcemapping is disabled. Users should call `createSourceActors`
   * instead of this.
   *
   * @param Debugger.Source source
   *        The source instance to create actors for.
   * @return Promise of an array of source actors
   */
  _createSourceMappedActors: function (source) {
    if (!this._useSourceMaps || !source.sourceMapURL) {
      return Promise.resolve(null);
    }

    return this.fetchSourceMap(source)
      .then(map => {
        if (map) {
          return map.sources.map(s => {
            return this.source({ originalUrl: s, generatedSource: source });
          }).filter(isNotNull);
        }
        return null;
      });
  },

  /**
   * Creates the source actors representing the appropriate sources
   * of `source`. If sourcemapped, returns actors for all of the original
   * sources, otherwise returns a 1-element array with the actor for
   * `source`.
   *
   * @param Debugger.Source source
   *        The source instance to create actors for.
   * @param Promise of an array of source actors
   */
  createSourceActors: function (source) {
    return this._createSourceMappedActors(source).then(actors => {
      let actor = this.createNonSourceMappedActor(source);
      return (actors || [actor]).filter(isNotNull);
    });
  },

  /**
   * Return a promise of a SourceMapConsumer for the source map for
   * `source`; if we already have such a promise extant, return that.
   * This will fetch the source map if we don't have a cached object
   * and source maps are enabled (see `_fetchSourceMap`).
   *
   * @param Debugger.Source source
   *        The source instance to get sourcemaps for.
   * @return Promise of a SourceMapConsumer
   */
  fetchSourceMap: function (source) {
    if (!this._useSourceMaps) {
      return Promise.resolve(null);
    } else if (this._sourceMaps.has(source)) {
      return this._sourceMaps.get(source);
    } else if (!source || !source.sourceMapURL) {
      return Promise.resolve(null);
    }

    let sourceMapURL = source.sourceMapURL;
    if (source.url) {
      sourceMapURL = joinURI(source.url, sourceMapURL);
    }
    let result = this._fetchSourceMap(sourceMapURL, source.url);

    let isWasm = source.introductionType == "wasm";
    if (isWasm) {
      result = result.then((map) => new WasmRemap(map));
    }

    // The promises in `_sourceMaps` must be the exact same instances
    // as returned by `_fetchSourceMap` for `clearSourceMapCache` to
    // work.
    this._sourceMaps.set(source, result);
    return result;
  },

  /**
   * Return a promise of a SourceMapConsumer for the source map for
   * `source`. The resolved result may be null if the source does not
   * have a source map or source maps are disabled.
   */
  getSourceMap: function (source) {
    return Promise.resolve(this._sourceMaps.get(source));
  },

  /**
   * Set a SourceMapConsumer for the source map for |source|.
   */
  setSourceMap: function (source, map) {
    this._sourceMaps.set(source, Promise.resolve(map));
  },

  /**
   * Return a promise of a SourceMapConsumer for the source map located at
   * |absSourceMapURL|, which must be absolute. If there is already such a
   * promise extant, return it. This will not fetch if source maps are
   * disabled.
   *
   * @param string absSourceMapURL
   *        The source map URL, in absolute form, not relative.
   * @param string sourceURL
   *        When the source map URL is a data URI, there is no sourceRoot on the
   *        source map, and the source map's sources are relative, we resolve
   *        them from sourceURL.
   */
  _fetchSourceMap: function (absSourceMapURL, sourceURL) {
    assert(this._useSourceMaps,
           "Cannot fetch sourcemaps if they are disabled");

    if (this._sourceMapCache[absSourceMapURL]) {
      return this._sourceMapCache[absSourceMapURL];
    }

    let fetching = fetch(absSourceMapURL, { loadFromCache: false })
      .then(({ content }) => {
        return new SourceMapConsumer(content,
                                     this._getSourceMapRoot(absSourceMapURL, sourceURL));
      })
      .catch(error => {
        if (!DevToolsUtils.reportingDisabled) {
          DevToolsUtils.reportException("TabSources.prototype._fetchSourceMap", error);
        }
        return null;
      });
    this._sourceMapCache[absSourceMapURL] = fetching;
    return fetching;
  },

  /**
   * Compute the URL to pass to the SourceMapConsumer constructor as
   * the "source map's URL".
   */
  _getSourceMapRoot: function (absSourceMapURL, scriptURL) {
    // Pass in the source map URL; except if it is a data: URL, fall
    // back to using the source's URL, if possible.
    if (scriptURL && absSourceMapURL.startsWith("data:")) {
      return scriptURL;
    }
    return absSourceMapURL;
  },

  /**
   * Clears the source map cache. Source maps are cached by URL so
   * they can be reused across separate Debugger instances (once in
   * this cache, they will never be reparsed again). They are
   * also cached by Debugger.Source objects for usefulness. By default
   * this just removes the Debugger.Source cache, but you can remove
   * the lower-level URL cache with the `hard` option.
   *
   * @param sourceMapURL string
   *        The source map URL to uncache
   * @param opts object
   *        An object with the following properties:
   *        - hard: Also remove the lower-level URL cache, which will
   *          make us completely forget about the source map.
   */
  clearSourceMapCache: function (sourceMapURL, opts = { hard: false }) {
    let oldSm = this._sourceMapCache[sourceMapURL];

    if (opts.hard) {
      delete this._sourceMapCache[sourceMapURL];
    }

    if (oldSm) {
      // Clear out the current cache so all sources will get the new one
      for (let [source, sm] of this._sourceMaps.entries()) {
        if (sm === oldSm) {
          this._sourceMaps.delete(source);
        }
      }
    }
  },

  /*
   * Forcefully change the source map of a source, changing the
   * sourceMapURL and installing the source map in the cache. This is
   * necessary to expose changes across Debugger instances
   * (pretty-printing is the use case). Generate a random url if one
   * isn't specified, allowing you to set "anonymous" source maps.
   *
   * @param source Debugger.Source
   *        The source to change the sourceMapURL property
   * @param url string
   *        The source map URL (optional)
   * @param map SourceMapConsumer
   *        The source map instance
   */
  setSourceMapHard: function (source, url, map) {
    if (!url) {
      // This is a littly hacky, but we want to forcefully set a
      // sourcemap regardless of sourcemap settings. We want to
      // literally change the sourceMapURL so that all debuggers will
      // get this and pretty-printing will Just Work (Debugger.Source
      // instances are per-debugger, so we can't key off that). To
      // avoid tons of work serializing the sourcemap into a data url,
      // just make a fake URL and stick the sourcemap there.
      url = "internal://sourcemap" + (this._anonSourceMapId++) + "/";
    }
    source.sourceMapURL = url;

    // Forcefully set the sourcemap cache. This will be used even if
    // sourcemaps are disabled.
    this._sourceMapCache[url] = Promise.resolve(map);
    this.emit("updatedSource", this.getSourceActor(source));
  },

  /**
   * Return the non-source-mapped location of the given Debugger.Frame. If the
   * frame does not have a script, the location's properties are all null.
   *
   * @param Debugger.Frame frame
   *        The frame whose location we are getting.
   * @returns Object
   *          Returns an object of the form { source, line, column }
   */
  getFrameLocation: function (frame) {
    if (!frame || !frame.script) {
      return new GeneratedLocation();
    }
    let {lineNumber, columnNumber} =
        frame.script.getOffsetLocation(frame.offset);
    return new GeneratedLocation(
      this.createNonSourceMappedActor(frame.script.source),
      lineNumber,
      columnNumber
    );
  },

  /**
   * Returns a promise of the location in the original source if the source is
   * source mapped, otherwise a promise of the same location. This can
   * be called with a source from *any* Debugger instance and we make
   * sure to that it works properly, reusing source maps if already
   * fetched. Use this from any actor that needs sourcemapping.
   */
  getOriginalLocation: function (generatedLocation) {
    let {
      generatedSourceActor,
      generatedLine,
      generatedColumn
    } = generatedLocation;
    let source = generatedSourceActor.source;

    // In certain scenarios the source map may have not been fetched
    // yet (or at least tied to this Debugger.Source instance), so use
    // `fetchSourceMap` instead of `getSourceMap`. This allows this
    // function to be called from anywere (across debuggers) and it
    // should just automatically work.
    return this.fetchSourceMap(source).then(map => {
      if (map) {
        let {
          source: originalUrl,
          line: originalLine,
          column: originalColumn,
          name: originalName
        } = map.originalPositionFor({
          line: generatedLine,
          column: generatedColumn == null ? Infinity : generatedColumn
        });

        // Since the `Debugger.Source` instance may come from a
        // different `Debugger` instance (any actor can call this
        // method), we can't rely on any of the source discovery
        // setup (`_discoverSources`, etc) to have been run yet. So
        // we have to assume that the actor may not already exist,
        // and we might need to create it, so use `source` and give
        // it the required parameters for a sourcemapped source.
        return new OriginalLocation(
          originalUrl ? this.source({
            originalUrl: originalUrl,
            generatedSource: source
          }) : null,
          originalLine,
          originalColumn,
          originalName
        );
      }

      // No source map
      return OriginalLocation.fromGeneratedLocation(generatedLocation);
    });
  },

  getAllGeneratedLocations: function (originalLocation) {
    let {
      originalSourceActor,
      originalLine,
      originalColumn
    } = originalLocation;

    let source = (originalSourceActor.source ||
                  originalSourceActor.generatedSource);

    return this.fetchSourceMap(source).then((map) => {
      if (map) {
        map.computeColumnSpans();

        return map.allGeneratedPositionsFor({
          source: originalSourceActor.url,
          line: originalLine,
          column: originalColumn
        }).map(({ line, column, lastColumn }) => {
          return new GeneratedLocation(
            this.createNonSourceMappedActor(source),
            line,
            column,
            lastColumn
          );
        });
      }

      return [GeneratedLocation.fromOriginalLocation(originalLocation)];
    });
  },

  /**
   * Returns a promise of the location in the generated source corresponding to
   * the original source and line given.
   *
   * When we pass a script S representing generated code to `sourceMap`,
   * above, that returns a promise P. The process of resolving P populates
   * the tables this function uses; thus, it won't know that S's original
   * source URLs map to S until P is resolved.
   */
  getGeneratedLocation: function (originalLocation) {
    let { originalSourceActor } = originalLocation;

    // Both original sources and normal sources could have sourcemaps,
    // because normal sources can be pretty-printed which generates a
    // sourcemap for itself. Check both of the source properties to make it work
    // for both kinds of sources.
    let source = originalSourceActor.source || originalSourceActor.generatedSource;

    // See comment about `fetchSourceMap` in `getOriginalLocation`.
    return this.fetchSourceMap(source).then((map) => {
      if (map) {
        let {
          originalLine,
          originalColumn
        } = originalLocation;

        let {
          line: generatedLine,
          column: generatedColumn
        } = map.generatedPositionFor({
          source: originalSourceActor.url,
          line: originalLine,
          column: originalColumn == null ? 0 : originalColumn,
          bias: SourceMapConsumer.LEAST_UPPER_BOUND
        });

        return new GeneratedLocation(
          this.createNonSourceMappedActor(source),
          generatedLine,
          generatedColumn
        );
      }

      return GeneratedLocation.fromOriginalLocation(originalLocation);
    });
  },

  /**
   * Returns true if URL for the given source is black boxed.
   *
   * @param url String
   *        The URL of the source which we are checking whether it is black
   *        boxed or not.
   */
  isBlackBoxed: function (url) {
    return this.blackBoxedSources.has(url);
  },

  /**
   * Add the given source URL to the set of sources that are black boxed.
   *
   * @param url String
   *        The URL of the source which we are black boxing.
   */
  blackBox: function (url) {
    this.blackBoxedSources.add(url);
  },

  /**
   * Remove the given source URL to the set of sources that are black boxed.
   *
   * @param url String
   *        The URL of the source which we are no longer black boxing.
   */
  unblackBox: function (url) {
    this.blackBoxedSources.delete(url);
  },

  /**
   * Returns true if the given URL is pretty printed.
   *
   * @param url String
   *        The URL of the source that might be pretty printed.
   */
  isPrettyPrinted: function (url) {
    return this.prettyPrintedSources.has(url);
  },

  /**
   * Add the given URL to the set of sources that are pretty printed.
   *
   * @param url String
   *        The URL of the source to be pretty printed.
   */
  prettyPrint: function (url, indent) {
    this.prettyPrintedSources.set(url, indent);
  },

  /**
   * Return the indent the given URL was pretty printed by.
   */
  prettyPrintIndent: function (url) {
    return this.prettyPrintedSources.get(url);
  },

  /**
   * Remove the given URL from the set of sources that are pretty printed.
   *
   * @param url String
   *        The URL of the source that is no longer pretty printed.
   */
  disablePrettyPrint: function (url) {
    this.prettyPrintedSources.delete(url);
  },

  iter: function () {
    let actors = Object.keys(this._sourceMappedSourceActors).map(k => {
      return this._sourceMappedSourceActors[k];
    });
    for (let actor of this._sourceActors.values()) {
      if (!this._sourceMaps.has(actor.source)) {
        actors.push(actor);
      }
    }
    return actors;
  }
};

/*
 * Checks if a source should never be displayed to the user because
 * it's either internal or we don't support in the UI yet.
 */
function isHiddenSource(source) {
  return source.introductionType === "Function.prototype";
}

/**
 * Returns true if its argument is not null.
 */
function isNotNull(thing) {
  return thing !== null;
}

exports.TabSources = TabSources;
exports.isHiddenSource = isHiddenSource;
PK
!<x���	�	Lchrome/devtools/modules/devtools/server/actors/utils/actor-registry-utils.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 { Cu, CC } = require("chrome");

const { DebuggerServer } = require("devtools/server/main");

/**
 * Support for actor registration. Main used by ActorRegistryActor
 * for dynamic registration of new actors.
 *
 * @param sourceText {String} Source of the actor implementation
 * @param fileName {String} URL of the actor module (for proper stack traces)
 * @param options {Object} Configuration object
 */
exports.registerActor = function (sourceText, fileName, options) {
  // Register in the current process
  exports.registerActorInCurrentProcess(sourceText, fileName, options);
  // Register in any child processes
  return DebuggerServer.setupInChild({
    module: "devtools/server/actors/utils/actor-registry-utils",
    setupChild: "registerActorInCurrentProcess",
    args: [sourceText, fileName, options],
    waitForEval: true
  });
};

exports.registerActorInCurrentProcess = function (sourceText, fileName, options) {
  const principal = CC("@mozilla.org/systemprincipal;1", "nsIPrincipal")();
  const sandbox = Cu.Sandbox(principal);
  sandbox.exports = {};
  sandbox.require = require;

  Cu.evalInSandbox(sourceText, sandbox, "1.8", fileName, 1);

  let { prefix, constructor, type } = options;

  if (type.global && !DebuggerServer.globalActorFactories.hasOwnProperty(prefix)) {
    DebuggerServer.addGlobalActor({
      constructorName: constructor,
      constructorFun: sandbox[constructor]
    }, prefix);
  }

  if (type.tab && !DebuggerServer.tabActorFactories.hasOwnProperty(prefix)) {
    DebuggerServer.addTabActor({
      constructorName: constructor,
      constructorFun: sandbox[constructor]
    }, prefix);
  }
};

exports.unregisterActor = function (options) {
  // Unregister in the current process
  exports.unregisterActorInCurrentProcess(options);
  // Unregister in any child processes
  DebuggerServer.setupInChild({
    module: "devtools/server/actors/utils/actor-registry-utils",
    setupChild: "unregisterActorInCurrentProcess",
    args: [options]
  });
};

exports.unregisterActorInCurrentProcess = function (options) {
  if (options.tab) {
    DebuggerServer.removeTabActor(options);
  }

  if (options.global) {
    DebuggerServer.removeGlobalActor(options);
  }
};
PK
!<�b�^�	�	Dchrome/devtools/modules/devtools/server/actors/utils/audionodes.json{
  "OscillatorNode": {
    "source": true,
    "properties": {
      "type": {},
      "frequency": {
        "param": true
      },
      "detune": {
        "param": true
      }
    }
  },
  "GainNode": {
    "properties": { "gain": { "param": true }}
  },
  "DelayNode": {
    "properties": { "delayTime": { "param": true }}
  },
  "AudioBufferSourceNode": {
    "source": true,
    "properties": {
      "buffer": { "Buffer": true },
      "playbackRate": {
        "param": true
      },
      "loop": {},
      "loopStart": {},
      "loopEnd": {}
    }
  },
  "ScriptProcessorNode": {
    "properties": { "bufferSize": { "readonly": true }}
  },
  "PannerNode": {
    "properties": {
      "panningModel": {},
      "distanceModel": {},
      "refDistance": {},
      "maxDistance": {},
      "rolloffFactor": {},
      "coneInnerAngle": {},
      "coneOuterAngle": {},
      "coneOuterGain": {}
    }
  },
  "ConvolverNode": {
    "properties": {
      "buffer": { "Buffer": true },
      "normalize": {}
    }
  },
  "DynamicsCompressorNode": {
    "properties": {
      "threshold": { "param": true },
      "knee": { "param": true },
      "ratio": { "param": true },
      "reduction": {},
      "attack": { "param": true },
      "release": { "param": true }
    }
  },
  "BiquadFilterNode": {
    "properties": {
      "type": {},
      "frequency": { "param": true },
      "Q": { "param": true },
      "detune": { "param": true },
      "gain": { "param": true }
    }
  },
  "WaveShaperNode": {
    "properties": {
      "curve": { "Float32Array": true },
      "oversample": {}
    }
  },
  "AnalyserNode": {
    "properties": {
      "fftSize": {},
      "minDecibels": {},
      "maxDecibels": {},
      "smoothingTimeConstant": {},
      "frequencyBinCount": { "readonly": true }
    }
  },
  "AudioDestinationNode": {
    "unbypassable": true
  },
  "ChannelSplitterNode": {
    "unbypassable": true
  },
  "ChannelMergerNode": {
    "unbypassable": true
  },
  "MediaElementAudioSourceNode": {
    "source": true
  },
  "MediaStreamAudioSourceNode": {
    "source": true
  },
  "MediaStreamAudioDestinationNode": {
    "unbypassable": true,
    "properties": {
      "stream": { "MediaStream": true }
    }
  },
  "StereoPannerNode": {
    "properties": {
      "pan": { "param": true }
    }
  },
  "ConstantSourceNode": {
    "source": true,
    "properties": {
      "offset": { "param": true }
    }
  },
  "IIRFilterNode": {
  }
}
PK
!<��.�.Kchrome/devtools/modules/devtools/server/actors/utils/automation-timeline.js/**
 * web-audio-automation-timeline - 1.0.3
 * https://github.com/jsantell/web-audio-automation-timeline
 * MIT License, copyright (c) 2014 Jordan Santell
 */
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.Timeline=e()}}(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){
module.exports = require("./lib/timeline").Timeline;

},{"./lib/timeline":4}],2:[function(require,module,exports){
var F = require("./formulas");

function TimelineEvent (eventName, value, time, timeConstant, duration) {
  this.type = eventName;
  this.value = value;
  this.time = time;
  this.constant = timeConstant || 0;
  this.duration = duration || 0;
}
exports.TimelineEvent = TimelineEvent;


TimelineEvent.prototype.exponentialApproach = function (lastValue, time) {
  return F.exponentialApproach(this.time, lastValue, this.value, this.constant, time);
}

TimelineEvent.prototype.extractValueFromCurve = function (time) {
  return F.extractValueFromCurve(this.time, this.value, this.value.length, this.duration, time);
}

TimelineEvent.prototype.linearInterpolate = function (next, time) {
  return F.linearInterpolate(this.time, this.value, next.time, next.value, time);
}

TimelineEvent.prototype.exponentialInterpolate = function (next, time) {
  return F.exponentialInterpolate(this.time, this.value, next.time, next.value, time);
}

},{"./formulas":3}],3:[function(require,module,exports){
var EPSILON = 0.0000000001;

exports.linearInterpolate = function (t0, v0, t1, v1, t) {
  return v0 + (v1 - v0) * ((t - t0) / (t1 - t0));
};

exports.exponentialInterpolate = function (t0, v0, t1, v1, t) {
  return v0 * Math.pow(v1 / v0, (t - t0) / (t1 - t0));
};

exports.extractValueFromCurve = function (start, curve, curveLength, duration, t) {
  var ratio;

  // If time is after duration, return the last curve value,
  // or if ratio is >= 1
  if (t >= start + duration || (ratio = Math.max((t - start) / duration, 0)) >= 1) {
    return curve[curveLength - 1];
  }

  return curve[~~(curveLength * ratio)];
};

exports.exponentialApproach = function (t0, v0, v1, timeConstant, t) {
  return v1 + (v0 - v1) * Math.exp(-(t - t0) / timeConstant);
};

// Since we are going to accumulate error by adding 0.01 multiple times
// in a loop, we want to fuzz the equality check in `getValueAtTime`
exports.fuzzyEqual = function (lhs, rhs) {
  return Math.abs(lhs - rhs) < EPSILON;
};

exports.EPSILON = EPSILON;

},{}],4:[function(require,module,exports){
var TimelineEvent = require("./event").TimelineEvent;
var F = require("./formulas");

exports.Timeline = Timeline;

function Timeline (defaultValue) {
  this.events = [];

  this._value = defaultValue || 0;
}

Timeline.prototype.getEventCount = function () {
  return this.events.length;
};

Timeline.prototype.value = function () {
  return this._value;
};

Timeline.prototype.setValue = function (value) {
  if (this.events.length === 0) {
    this._value = value;
  }
};

Timeline.prototype.getValue = function () {
  if (this.events.length) {
    throw new Error("Can only call `getValue` when there are 0 events.");
  }

  return this._value;
};

Timeline.prototype.getValueAtTime = function (time) {
  return this._getValueAtTimeHelper(time);
};

Timeline.prototype._getValueAtTimeHelper = function (time) {
  var bailOut = false;
  var previous = null;
  var next = null;
  var lastComputedValue = null; // Used for `setTargetAtTime` nodes
  var events = this.events;
  var e;

  for (var i = 0; !bailOut && i < events.length; i++) {
    if (F.fuzzyEqual(time, events[i].time)) {
      // Find the last event with the same time as `time`
      do {
        ++i;
      } while (i < events.length && F.fuzzyEqual(time, events[i].time));

      e = events[i - 1];

      // `setTargetAtTime` can be handled no matter what their next event is (if they have one)
      if (e.type === "setTargetAtTime") {
        lastComputedValue = this._lastComputedValue(e);
        return e.exponentialApproach(lastComputedValue, time);
      }

      // `setValueCurveAtTime` events can be handled no matter what their next event node is
      // (if they have one)
      if (e.type === "setValueCurveAtTime") {
        return e.extractValueFromCurve(time);
      }

      // For other event types
      return e.value;
    }
    previous = next;
    next = events[i];

    if (time < events[i].time) {
      bailOut = true;
    }
  }

  // Handle the case where the time is past all of the events
  if (!bailOut) {
    previous = next;
    next = null;
  }

  // Just return the default value if we did not find anything
  if (!previous && !next) {
    return this._value;
  }

  // If the requested time is before all of the existing events
  if (!previous) {
    return this._value;
  }

  // `setTargetAtTime` can be handled no matter what their next event is (if they have one)
  if (previous.type === "setTargetAtTime") {
    lastComputedValue = this._lastComputedValue(previous);
    return previous.exponentialApproach(lastComputedValue, time);
  }

  // `setValueCurveAtTime` events can be handled no matter what their next event node is
  // (if they have one)
  if (previous.type === "setValueCurveAtTime") {
    return previous.extractValueFromCurve(time);
  }

  if (!next) {
    if (~["setValueAtTime", "linearRampToValueAtTime", "exponentialRampToValueAtTime"].indexOf(previous.type)) {
      return previous.value;
    }
    if (previous.type === "setValueCurveAtTime") {
      return previous.extractValueFromCurve(time);
    }
    if (previous.type === "setTargetAtTime") {
      throw new Error("unreached");
    }
    throw new Error("unreached");
  }

  // Finally handle the case where we have both a previous and a next event
  // First handle the case where our range ends up in a ramp event
  if (next.type === "linearRampToValueAtTime") {
    return previous.linearInterpolate(next, time);
  } else if (next.type === "exponentialRampToValueAtTime") {
    return previous.exponentialInterpolate(next, time);
  }

  // Now handle all other cases
  if (~["setValueAtTime", "linearRampToValueAtTime", "exponentialRampToValueAtTime"].indexOf(previous.type)) {
    // If the next event type is neither linear or exponential ramp,
    // the value is constant.
    return previous.value;
  }
  if (previous.type === "setValueCurveAtTime") {
    return previous.extractValueFromCurve(time);
  }
  if (previous.type === "setTargetAtTime") {
    throw new Error("unreached");
  }
  throw new Error("unreached");
};

Timeline.prototype._insertEvent = function (ev) {
  var events = this.events;

  if (ev.type === "setValueCurveAtTime") {
    if (!ev.value || !ev.value.length) {
      throw new Error("NS_ERROR_DOM_SYNTAX_ERR");
    }
  }

  if (ev.type === "setTargetAtTime") {
    if (F.fuzzyEqual(ev.constant, 0)) {
      throw new Error("NS_ERROR_DOM_SYNTAX_ERR");
    }
  }

  // Make sure that non-curve events don't fall within the duration of a
  // curve event.
  for (var i = 0; i < events.length; i++) {
    if (events[i].type === "setValueCurveAtTime" &&
        events[i].time <= ev.time &&
        (events[i].time + events[i].duration) >= ev.time) {
      throw new Error("NS_ERROR_DOM_SYNTAX_ERR");
    }
  }

  // Make sure that curve events don't fall in a range which includes other
  // events.
  if (ev.type === "setValueCurveAtTime") {
    for (var i = 0; i < events.length; i++) {
      if (events[i].time > ev.time &&
          events[i].time < (ev.time + ev.duration)) {
        throw new Error("NS_ERROR_DOM_SYNTAX_ERR");
      }
    }
  }

  // Make sure that invalid values are not used for exponential curves
  if (ev.type === "exponentialRampToValueAtTime") {
    if (ev.value <= 0) throw new Error("NS_ERROR_DOM_SYNTAX_ERR");
    var prev = this._getPreviousEvent(ev.time);
    if (prev) {
      if (prev.value <= 0) {
        throw new Error("NS_ERROR_DOM_SYNTAX_ERR");
      }
    } else {
      if (this._value <= 0) {
        throw new Error("NS_ERROR_DOM_SYNTAX_ERR");
      }
    }
  }

  for (var i = 0; i < events.length; i++) {
    if (ev.time === events[i].time) {
      if (ev.type === events[i].type) {
        // If times and types are equal, replace the event;
        events[i] = ev;
      } else {
        // Otherwise, place the element after the last event of another type
        do { i++; }
        while (i < events.length && ev.type !== events[i].type && ev.time === events[i].time);
        events.splice(i, 0, ev);
      }
      return;
    }
    // Otherwise, place the event right after the latest existing event
    if (ev.time < events[i].time) {
      events.splice(i, 0, ev);
      return;
    }
  }

  // If we couldn't find a place for the event, just append it to the list
  this.events.push(ev);
};

Timeline.prototype._getPreviousEvent = function (time) {
  var previous = null, next = null;
  var bailOut = false;
  var events = this.events;

  for (var i = 0; !bailOut && i < events.length; i++) {
    if (time === events[i]) {
      do { ++i; }
      while (i < events.length && time === events[i].time);
      return events[i - 1];
    }
    previous = next;
    next = events[i];
    if (time < events[i].time) {
      bailOut = true;
    }
  }

  // Handle the case where the time is past all the events
  if (!bailOut) {
    previous = next;
  }

  return previous;
};

/**
 * Calculates the previous value of the timeline, used for
 * `setTargetAtTime` nodes. Takes an event, and returns
 * the previous computed value for any sample taken during that
 * exponential approach node.
 */
Timeline.prototype._lastComputedValue = function (event) {
  // If equal times, return the value for the previous event, before
  // the `setTargetAtTime` node.
  var lastEvent = this._getPreviousEvent(event.time - F.EPSILON);

  // If no event before the setTargetAtTime event, then return the
  // intrinsic value.
  if (!lastEvent) {
    return this._value;
  }
  // Otherwise, return the value for the previous event, which should
  // always be the last computed value (? I think?)
  else {
    return lastEvent.value;
  }
};

Timeline.prototype.setValueAtTime = function (value, startTime) {
  this._insertEvent(new TimelineEvent("setValueAtTime", value, startTime));
};

Timeline.prototype.linearRampToValueAtTime = function (value, endTime) {
  this._insertEvent(new TimelineEvent("linearRampToValueAtTime", value, endTime));
};

Timeline.prototype.exponentialRampToValueAtTime = function (value, endTime) {
  this._insertEvent(new TimelineEvent("exponentialRampToValueAtTime", value, endTime));
};

Timeline.prototype.setTargetAtTime = function (value, startTime, timeConstant) {
  this._insertEvent(new TimelineEvent("setTargetAtTime", value, startTime, timeConstant));
};

Timeline.prototype.setValueCurveAtTime = function (value, startTime, duration) {
  this._insertEvent(new TimelineEvent("setValueCurveAtTime", value, startTime, null, duration));
};

Timeline.prototype.cancelScheduledValues = function (time) {
  for (var i = 0; i < this.events.length; i++) {
    if (this.events[i].time >= time) {
      this.events = this.events.slice(0, i);
      break;
    }
  }
};

Timeline.prototype.cancelAllEvents = function () {
  this.events.length = 0;
};

},{"./event":2,"./formulas":3}]},{},[1])(1)
});PK
!<\G�``Lchrome/devtools/modules/devtools/server/actors/utils/breakpoint-actor-map.js/* -*- indent-tabs-mode: nil; js-indent-level: 2; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 { OriginalLocation } = require("devtools/server/actors/common");

/**
 * A BreakpointActorMap is a map from locations to instances of BreakpointActor.
 */
function BreakpointActorMap() {
  this._size = 0;
  this._actors = {};
}

BreakpointActorMap.prototype = {
  /**
   * Return the number of BreakpointActors in this BreakpointActorMap.
   *
   * @returns Number
   *          The number of BreakpointActor in this BreakpointActorMap.
   */
  get size() {
    return this._size;
  },

  /**
   * Generate all BreakpointActors that match the given location in
   * this BreakpointActorMap.
   *
   * @param OriginalLocation location
   *        The location for which matching BreakpointActors should be generated.
   */
  findActors: function* (location = new OriginalLocation()) {
    // Fast shortcut for when we know we won't find any actors. Surprisingly
    // enough, this speeds up refreshing when there are no breakpoints set by
    // about 2x!
    if (this.size === 0) {
      return;
    }

    function* findKeys(obj, key) {
      if (key !== undefined) {
        if (key in obj) {
          yield key;
        }
      } else {
        for (key of Object.keys(obj)) {
          yield key;
        }
      }
    }

    let query = {
      sourceActorID: location.originalSourceActor
                     ? location.originalSourceActor.actorID
                     : undefined,
      line: location.originalLine,
    };

    // If location contains a line, assume we are searching for a whole line
    // breakpoint, and set begin/endColumn accordingly. Otherwise, we are
    // searching for all breakpoints, so begin/endColumn should be left unset.
    if (location.originalLine) {
      query.beginColumn = location.originalColumn ? location.originalColumn : 0;
      query.endColumn = location.originalColumn ? location.originalColumn + 1 : Infinity;
    } else {
      query.beginColumn = location.originalColumn ? query.originalColumn : undefined;
      query.endColumn = location.originalColumn ? query.originalColumn + 1 : undefined;
    }

    for (let sourceActorID of findKeys(this._actors, query.sourceActorID)) {
      let actor = this._actors[sourceActorID];
      for (let line of findKeys(actor, query.line)) {
        for (let beginColumn of findKeys(actor[line], query.beginColumn)) {
          for (let endColumn of findKeys(actor[line][beginColumn],
               query.endColumn)) {
            yield actor[line][beginColumn][endColumn];
          }
        }
      }
    }
  },

  /**
   * Return the BreakpointActor at the given location in this
   * BreakpointActorMap.
   *
   * @param OriginalLocation location
   *        The location for which the BreakpointActor should be returned.
   *
   * @returns BreakpointActor actor
   *          The BreakpointActor at the given location.
   */
  getActor: function (originalLocation) {
    for (let actor of this.findActors(originalLocation)) {
      return actor;
    }

    return null;
  },

  /**
   * Set the given BreakpointActor to the given location in this
   * BreakpointActorMap.
   *
   * @param OriginalLocation location
   *        The location to which the given BreakpointActor should be set.
   *
   * @param BreakpointActor actor
   *        The BreakpointActor to be set to the given location.
   */
  setActor: function (location, actor) {
    let { originalSourceActor, originalLine, originalColumn } = location;

    let sourceActorID = originalSourceActor.actorID;
    let line = originalLine;
    let beginColumn = originalColumn ? originalColumn : 0;
    let endColumn = originalColumn ? originalColumn + 1 : Infinity;

    if (!this._actors[sourceActorID]) {
      this._actors[sourceActorID] = [];
    }
    if (!this._actors[sourceActorID][line]) {
      this._actors[sourceActorID][line] = [];
    }
    if (!this._actors[sourceActorID][line][beginColumn]) {
      this._actors[sourceActorID][line][beginColumn] = [];
    }
    if (!this._actors[sourceActorID][line][beginColumn][endColumn]) {
      ++this._size;
    }
    this._actors[sourceActorID][line][beginColumn][endColumn] = actor;
  },

  /**
   * Delete the BreakpointActor from the given location in this
   * BreakpointActorMap.
   *
   * @param OriginalLocation location
   *        The location from which the BreakpointActor should be deleted.
   */
  deleteActor: function (location) {
    let { originalSourceActor, originalLine, originalColumn } = location;

    let sourceActorID = originalSourceActor.actorID;
    let line = originalLine;
    let beginColumn = originalColumn ? originalColumn : 0;
    let endColumn = originalColumn ? originalColumn + 1 : Infinity;

    if (this._actors[sourceActorID]) {
      if (this._actors[sourceActorID][line]) {
        if (this._actors[sourceActorID][line][beginColumn]) {
          if (this._actors[sourceActorID][line][beginColumn][endColumn]) {
            --this._size;
          }
          delete this._actors[sourceActorID][line][beginColumn][endColumn];
          if (Object.keys(this._actors[sourceActorID][line][beginColumn]).length === 0) {
            delete this._actors[sourceActorID][line][beginColumn];
          }
        }
        if (Object.keys(this._actors[sourceActorID][line]).length === 0) {
          delete this._actors[sourceActorID][line];
        }
      }
    }
  }
};

exports.BreakpointActorMap = BreakpointActorMap;
PK
!<?��ˏ�Fchrome/devtools/modules/devtools/server/actors/utils/css-grid-utils.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Cu } = require("chrome");

/**
 * Returns the grid fragment array with all the grid fragment data stringifiable.
 *
 * @param  {Object} fragments
 *         Grid fragment object.
 * @return {Array} representation with the grid fragment data stringifiable.
 */
function getStringifiableFragments(fragments = []) {
  if (fragments[0] && Cu.isDeadWrapper(fragments[0])) {
    return {};
  }

  return fragments.map(getStringifiableFragment);
}

/**
 * Returns a string representation of the CSS Grid data as returned by
 * node.getGridFragments. This is useful to compare grid state at each update and redraw
 * the highlighter if needed. It also seralizes the grid fragment data so it can be used
 * by protocol.js.
 *
 * @param  {Object} fragments
 *         Grid fragment object.
 * @return {String} representation of the CSS grid fragment data.
 */
function stringifyGridFragments(fragments) {
  return JSON.stringify(getStringifiableFragments(fragments));
}

function getStringifiableFragment(fragment) {
  return {
    areas: getStringifiableAreas(fragment.areas),
    cols: getStringifiableDimension(fragment.cols),
    rows: getStringifiableDimension(fragment.rows)
  };
}

function getStringifiableAreas(areas) {
  return [...areas].map(getStringifiableArea);
}

function getStringifiableDimension(dimension) {
  return {
    lines: [...dimension.lines].map(getStringifiableLine),
    tracks: [...dimension.tracks].map(getStringifiableTrack),
  };
}

function getStringifiableArea({ columnEnd, columnStart, name, rowEnd, rowStart, type }) {
  return { columnEnd, columnStart, name, rowEnd, rowStart, type };
}

function getStringifiableLine({ breadth, names, number, start }) {
  return { breadth, names, number, start };
}

function getStringifiableTrack({ breadth, start, state, type }) {
  return { breadth, start, state, type };
}

exports.getStringifiableFragments = getStringifiableFragments;
exports.stringifyGridFragments = stringifyGridFragments;
PK
!<t��sSSBchrome/devtools/modules/devtools/server/actors/utils/event-loop.js/* -*- indent-tabs-mode: nil; js-indent-level: 2; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 xpcInspector = require("xpcInspector");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const { dumpn } = DevToolsUtils;

/**
 * Manages pushing event loops and automatically pops and exits them in the
 * correct order as they are resolved.
 *
 * @param ThreadActor thread
 *        The thread actor instance that owns this EventLoopStack.
 * @param DebuggerServerConnection connection
 *        The remote protocol connection associated with this event loop stack.
 * @param Object hooks
 *        An object with the following properties:
 *          - url: The URL string of the debuggee we are spinning an event loop
 *                 for.
 *          - preNest: function called before entering a nested event loop
 *          - postNest: function called after exiting a nested event loop
 */
function EventLoopStack({ thread, connection, hooks }) {
  this._hooks = hooks;
  this._thread = thread;
  this._connection = connection;
}

EventLoopStack.prototype = {
  /**
   * The number of nested event loops on the stack.
   */
  get size() {
    return xpcInspector.eventLoopNestLevel;
  },

  /**
   * The URL of the debuggee who pushed the event loop on top of the stack.
   */
  get lastPausedUrl() {
    let url = null;
    if (this.size > 0) {
      try {
        url = xpcInspector.lastNestRequestor.url;
      } catch (e) {
        // The tab's URL getter may throw if the tab is destroyed by the time
        // this code runs, but we don't really care at this point.
        dumpn(e);
      }
    }
    return url;
  },

  /**
   * The DebuggerServerConnection of the debugger who pushed the event loop on
   * top of the stack
   */
  get lastConnection() {
    return xpcInspector.lastNestRequestor._connection;
  },

  /**
   * Push a new nested event loop onto the stack.
   *
   * @returns EventLoop
   */
  push: function () {
    return new EventLoop({
      thread: this._thread,
      connection: this._connection,
      hooks: this._hooks
    });
  }
};

/**
 * An object that represents a nested event loop. It is used as the nest
 * requestor with nsIJSInspector instances.
 *
 * @param ThreadActor thread
 *        The thread actor that is creating this nested event loop.
 * @param DebuggerServerConnection connection
 *        The remote protocol connection associated with this event loop.
 * @param Object hooks
 *        The same hooks object passed into EventLoopStack during its
 *        initialization.
 */
function EventLoop({ thread, connection, hooks }) {
  this._thread = thread;
  this._hooks = hooks;
  this._connection = connection;

  this.enter = this.enter.bind(this);
  this.resolve = this.resolve.bind(this);
}

EventLoop.prototype = {
  entered: false,
  resolved: false,
  get url() {
    return this._hooks.url;
  },

  /**
   * Enter this nested event loop.
   */
  enter: function () {
    let nestData = this._hooks.preNest
      ? this._hooks.preNest()
      : null;

    this.entered = true;
    xpcInspector.enterNestedEventLoop(this);

    // Keep exiting nested event loops while the last requestor is resolved.
    if (xpcInspector.eventLoopNestLevel > 0) {
      const { resolved } = xpcInspector.lastNestRequestor;
      if (resolved) {
        xpcInspector.exitNestedEventLoop();
      }
    }

    if (this._hooks.postNest) {
      this._hooks.postNest(nestData);
    }
  },

  /**
   * Resolve this nested event loop.
   *
   * @returns boolean
   *          True if we exited this nested event loop because it was on top of
   *          the stack, false if there is another nested event loop above this
   *          one that hasn't resolved yet.
   */
  resolve: function () {
    if (!this.entered) {
      throw new Error("Can't resolve an event loop before it has been entered!");
    }
    if (this.resolved) {
      throw new Error("Already resolved this nested event loop!");
    }
    this.resolved = true;
    if (this === xpcInspector.lastNestRequestor) {
      xpcInspector.exitNestedEventLoop();
      return true;
    }
    return false;
  },
};

exports.EventLoopStack = EventLoopStack;
PK
!<>�XAhhEchrome/devtools/modules/devtools/server/actors/utils/make-debugger.js/* -*- indent-tabs-mode: nil; js-indent-level: 2; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 EventEmitter = require("devtools/shared/event-emitter");
const Debugger = require("Debugger");

const { reportException } = require("devtools/shared/DevToolsUtils");

/**
 * Multiple actors that use a |Debugger| instance come in a few versions, each
 * with a different set of debuggees. One version for content tabs (globals
 * within a tab), one version for chrome debugging (all globals), and sometimes
 * a third version for addon debugging (chrome globals the addon is loaded in
 * and content globals the addon injects scripts into). The |makeDebugger|
 * function helps us avoid repeating the logic for finding and maintaining the
 * correct set of globals for a given |Debugger| instance across each version of
 * all of our actors.
 *
 * The |makeDebugger| function expects a single object parameter with the
 * following properties:
 *
 * @param Function findDebuggees
 *        Called with one argument: a |Debugger| instance. This function should
 *        return an iterable of globals to be added to the |Debugger|
 *        instance. The globals may be wrapped in a |Debugger.Object|, or
 *        unwrapped.
 *
 * @param Function shouldAddNewGlobalAsDebuggee
 *        Called with one argument: a |Debugger.Object| wrapping a global
 *        object. This function must return |true| if the global object should
 *        be added as debuggee, and |false| otherwise.
 *
 * @returns Debugger
 *          Returns a |Debugger| instance that can manage its set of debuggee
 *          globals itself and is decorated with the |EventEmitter| class.
 *
 *          Events emitted by the returned |Debugger| instance:
 *
 *            - "newGlobal": Emitted when a new global has been added as a
 *               debuggee. Passes the |Debugger.Object| wrapping the new
 *               debuggee global to listeners.
 *
 *          Existing |Debugger| properties set on the returned |Debugger|
 *          instance:
 *
 *            - onNewGlobalObject: The |Debugger| will automatically add new
 *              globals as debuggees if calling |shouldAddNewGlobalAsDebuggee|
 *              with the global returns true.
 *
 *            - uncaughtExceptionHook: The |Debugger| already has an error
 *              reporter attached to |uncaughtExceptionHook|, so if any
 *              |Debugger| hooks fail, the error will be reported.
 *
 *          New properties set on the returned |Debugger| instance:
 *
 *            - addDebuggees: A function which takes no arguments. It adds all
 *              current globals that should be debuggees (as determined by
 *              |findDebuggees|) to the |Debugger| instance.
 */
module.exports = function makeDebugger({ findDebuggees, shouldAddNewGlobalAsDebuggee }) {
  const dbg = new Debugger();
  EventEmitter.decorate(dbg);

  dbg.allowUnobservedAsmJS = true;
  dbg.uncaughtExceptionHook = reportDebuggerHookException;

  dbg.onNewGlobalObject = function (global) {
    if (shouldAddNewGlobalAsDebuggee(global)) {
      safeAddDebuggee(this, global);
    }
  };

  dbg.addDebuggees = function () {
    for (let global of findDebuggees(this)) {
      safeAddDebuggee(this, global);
    }
  };

  return dbg;
};

const reportDebuggerHookException = e => reportException("Debugger Hook", e);

/**
 * Add |global| as a debuggee to |dbg|, handling error cases.
 */
function safeAddDebuggee(dbg, global) {
  try {
    let wrappedGlobal = dbg.addDebuggee(global);
    if (wrappedGlobal) {
      dbg.emit("newGlobal", wrappedGlobal);
    }
  } catch (e) {
    // Ignoring attempt to add the debugger's compartment as a debuggee.
  }
}
PK
!<��_�##Kchrome/devtools/modules/devtools/server/actors/utils/map-uri-to-addon-id.js/* -*- indent-tabs-mode: nil; js-indent-level: 2; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 DevToolsUtils = require("devtools/shared/DevToolsUtils");
const Services = require("Services");

loader.lazyServiceGetter(this, "AddonPathService",
                         "@mozilla.org/addon-path-service;1",
                         "amIAddonPathService");

const B2G_ID = "{3c2e2abc-06d4-11e1-ac3b-374f68613e61}";
const GRAPHENE_ID = "{d1bfe7d9-c01e-4237-998b-7b5f960a4314}";

/**
 * This is a wrapper around amIAddonPathService.mapURIToAddonID which always returns
 * false on B2G and graphene to avoid loading the add-on manager there and
 * reports any exceptions rather than throwing so that the caller doesn't have
 * to worry about them.
 */
if (!Services.appinfo
    || Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT
    || Services.appinfo.ID === undefined /* XPCShell */
    || Services.appinfo.ID == B2G_ID
    || Services.appinfo.ID == GRAPHENE_ID
    || !AddonPathService) {
  module.exports = function mapURIToAddonId(uri) {
    return false;
  };
} else {
  module.exports = function mapURIToAddonId(uri) {
    try {
      return AddonPathService.mapURIToAddonId(uri);
    } catch (e) {
      DevToolsUtils.reportException("mapURIToAddonId", e);
      return false;
    }
  };
}
PK
!<T'����Dchrome/devtools/modules/devtools/server/actors/utils/shapes-utils.js"use strict";

/**
 * Get the distance between two points on a plane.
 * @param {Number} x1 the x coord of the first point
 * @param {Number} y1 the y coord of the first point
 * @param {Number} x2 the x coord of the second point
 * @param {Number} y2 the y coord of the second point
 * @returns {Number} the distance between the two points
 */
const getDistance = (x1, y1, x2, y2) => {
  return Math.hypot(x2 - x1, y2 - y1);
};

/**
 * Determine if the given x/y coords are along the edge of the given ellipse.
 * We allow for a small area around the edge that still counts as being on the edge.
 * @param {Number} x the x coordinate of the click
 * @param {Number} y the y coordinate of the click
 * @param {Number} cx the x coordinate of the center of the ellipse
 * @param {Number} cy the y coordinate of the center of the ellipse
 * @param {Number} rx the x radius of the ellipse
 * @param {Number} ry the y radius of the ellipse
 * @param {Number} clickWidthX the width of the area that counts as being on the edge
 *                             along the x radius.
 * @param {Number} clickWidthY the width of the area that counts as being on the edge
 *                             along the y radius.
 * @returns {Boolean} whether the click counts as being on the edge of the ellipse.
 */
const clickedOnEllipseEdge = (x, y, cx, cy, rx, ry, clickWidthX, clickWidthY) => {
  // The formula to determine if something is inside or on the edge of an ellipse is:
  // (x - cx)^2/rx^2 + (y - cy)^2/ry^2 <= 1. If > 1, it's outside.
  // We make two ellipses, adjusting rx and ry with clickWidthX and clickWidthY
  // to allow for an area around the edge of the ellipse that can be clicked on.
  // If the click was outside the inner ellipse and inside the outer ellipse, return true.
  let inner = ((x - cx) ** 2) / (rx - clickWidthX) ** 2 +
              ((y - cy) ** 2) / (ry - clickWidthY) ** 2;
  let outer = ((x - cx) ** 2) / (rx + clickWidthX) ** 2 +
              ((y - cy) ** 2) / (ry + clickWidthY) ** 2;
  return inner >= 1 && outer <= 1;
};

/**
 * Get the distance between a point and a line defined by two other points.
 * @param {Number} x1 the x coordinate of the first point in the line
 * @param {Number} y1 the y coordinate of the first point in the line
 * @param {Number} x2 the x coordinate of the second point in the line
 * @param {Number} y2 the y coordinate of the second point in the line
 * @param {Number} x3 the x coordinate of the point for which the distance is found
 * @param {Number} y3 the y coordinate of the point for which the distance is found
 * @returns {Number} the distance between (x3,y3) and the line defined by
 *          (x1,y1) and (y1,y2)
 */
const distanceToLine = (x1, y1, x2, y2, x3, y3) => {
  // https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line#Line_defined_by_two_points
  let num = Math.abs((y2 - y1) * x3 - (x2 - x1) * y3 + x2 * y1 - y2 * x1);
  let denom = getDistance(x1, y1, x2, y2);
  return num / denom;
};

/**
 * Get the point on the line defined by points a,b that is closest to point c
 * @param {Number} ax the x coordinate of point a
 * @param {Number} ay the y coordinate of point a
 * @param {Number} bx the x coordinate of point b
 * @param {Number} by the y coordinate of point b
 * @param {Number} cx the x coordinate of point c
 * @param {Number} cy the y coordinate of point c
 * @returns {Array} a 2 element array that contains the x/y coords of the projected point
 */
const projection = (ax, ay, bx, by, cx, cy) => {
  // https://en.wikipedia.org/wiki/Vector_projection#Vector_projection_2
  let ab = [bx - ax, by - ay];
  let ac = [cx - ax, cy - ay];
  let scalar = dotProduct(ab, ac) / dotProduct(ab, ab);
  return [ax + scalar * ab[0], ay + scalar * ab[1]];
};

/**
 * Get the dot product of two vectors, represented by arrays of numbers.
 * @param {Array} a the first vector
 * @param {Array} b the second vector
 * @returns {Number} the dot product of a and b
 */
const dotProduct = (a, b) => {
  return a.reduce((prev, curr, i) => {
    return prev + curr * b[i];
  }, 0);
};

/**
 * Determine if the given x/y coords are above the given point.
 * @param {Number} x the x coordinate of the click
 * @param {Number} y the y coordinate of the click
 * @param {Number} pointX the x coordinate of the center of the point
 * @param {Number} pointY the y coordinate of the center of the point
 * @param {Number} radiusX the x radius of the point
 * @param {Number} radiusY the y radius of the point
 * @returns {Boolean} whether the click was on the point
 */
const clickedOnPoint = (x, y, pointX, pointY, radiusX, radiusY) => {
  return x >= pointX - radiusX && x <= pointX + radiusX &&
         y >= pointY - radiusY && y <= pointY + radiusY;
};

const roundTo = (value, exp) => {
  // If the exp is undefined or zero...
  if (typeof exp === "undefined" || +exp === 0) {
    return Math.round(value);
  }
  value = +value;
  exp = +exp;
  // If the value is not a number or the exp is not an integer...
  if (isNaN(value) || !(typeof exp === "number" && exp % 1 === 0)) {
    return NaN;
  }
  // Shift
  value = value.toString().split("e");
  value = Math.round(+(value[0] + "e" + (value[1] ? (+value[1] - exp) : -exp)));
  // Shift back
  value = value.toString().split("e");
  return +(value[0] + "e" + (value[1] ? (+value[1] + exp) : exp));
};

exports.getDistance = getDistance;
exports.clickedOnEllipseEdge = clickedOnEllipseEdge;
exports.distanceToLine = distanceToLine;
exports.projection = projection;
exports.clickedOnPoint = clickedOnPoint;
exports.roundTo = roundTo;
PK
!<-��d��Jchrome/devtools/modules/devtools/server/actors/utils/source-actor-store.js/* -*- indent-tabs-mode: nil; js-indent-level: 2; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

loader.lazyRequireGetter(this, "getSourceURL", "devtools/server/actors/source", true);

/**
 * Keeps track of persistent sources across reloads and ties different
 * source instances to the same actor id so that things like
 * breakpoints survive reloads. ThreadSources uses this to force the
 * same actorID on a SourceActor.
 */
function SourceActorStore() {
  // source identifier --> actor id
  this._sourceActorIds = Object.create(null);
}

SourceActorStore.prototype = {
  /**
   * Lookup an existing actor id that represents this source, if available.
   */
  getReusableActorId: function (source, originalUrl) {
    let url = this.getUniqueKey(source, originalUrl);
    if (url && url in this._sourceActorIds) {
      return this._sourceActorIds[url];
    }
    return null;
  },

  /**
   * Update a source with an actorID.
   */
  setReusableActorId: function (source, originalUrl, actorID) {
    let url = this.getUniqueKey(source, originalUrl);
    if (url) {
      this._sourceActorIds[url] = actorID;
    }
  },

  /**
   * Make a unique URL from a source that identifies it across reloads.
   */
  getUniqueKey: function (source, originalUrl) {
    if (originalUrl) {
      // Original source from a sourcemap.
      return originalUrl;
    }

    return getSourceURL(source);
  }
};

exports.SourceActorStore = SourceActorStore;
PK
!<^I�=chrome/devtools/modules/devtools/server/actors/utils/stack.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 helper class that stores stack frame objects.  Each frame is
 * assigned an index, and if a frame is added more than once, the same
 * index is used.  Users of the class can get an array of all frames
 * that have been added.
 */
class StackFrameCache {
  /**
   * Initialize this object.
   */
  constructor() {
    this._framesToIndices = null;
    this._framesToForms = null;
    this._lastEventSize = 0;
  }

  /**
   * Prepare to accept frames.
   */
  initFrames() {
    if (this._framesToIndices) {
      // The maps are already initialized.
      return;
    }

    this._framesToIndices = new Map();
    this._framesToForms = new Map();
    this._lastEventSize = 0;
  }

  /**
   * Forget all stored frames and reset to the initialized state.
   */
  clearFrames() {
    this._framesToIndices.clear();
    this._framesToIndices = null;
    this._framesToForms.clear();
    this._framesToForms = null;
    this._lastEventSize = 0;
  }

  /**
   * Add a frame to this stack frame cache, and return the index of
   * the frame.
   */
  addFrame(frame) {
    this._assignFrameIndices(frame);
    this._createFrameForms(frame);
    return this._framesToIndices.get(frame);
  }

  /**
   * A helper method for the memory actor.  This populates the packet
   * object with "frames" property. Each of these
   * properties will be an array indexed by frame ID.  "frames" will
   * contain frame objects (see makeEvent).
   *
   * @param packet
   *        The packet to update.
   *
   * @returns packet
   */
  updateFramePacket(packet) {
    // Now that we are guaranteed to have a form for every frame, we know the
    // size the "frames" property's array must be. We use that information to
    // create dense arrays even though we populate them out of order.
    const size = this._framesToForms.size;
    packet.frames = Array(size).fill(null);

    // Populate the "frames" properties.
    for (let [stack, index] of this._framesToIndices) {
      packet.frames[index] = this._framesToForms.get(stack);
    }

    return packet;
  }

  /**
   * If any new stack frames have been added to this cache since the
   * last call to makeEvent (clearing the cache also resets the "last
   * call"), then return a new array describing the new frames.  If no
   * new frames are available, return null.
   *
   * The frame cache assumes that the user of the cache keeps track of
   * all previously-returned arrays and, in theory, concatenates them
   * all to form a single array holding all frames added to the cache
   * since the last reset.  This concatenated array can be indexed by
   * the frame ID.  The array returned by this function, though, is
   * dense and starts at 0.
   *
   * Each element in the array is an object of the form:
   * {
   *   line: <line number for this frame>,
   *   column: <column number for this frame>,
   *   source: <filename string for this frame>,
   *   functionDisplayName: <this frame's inferred function name function or null>,
   *   parent: <frame ID -- an index into the concatenated array mentioned above>
   *   asyncCause: the async cause, or null
   *   asyncParent: <frame ID -- an index into the concatenated array mentioned above>
   * }
   *
   * The intent of this approach is to make it simpler to efficiently
   * send frame information over the debugging protocol, by only
   * sending new frames.
   *
   * @returns array or null
   */
  makeEvent() {
    const size = this._framesToForms.size;
    if (!size || size <= this._lastEventSize) {
      return null;
    }

    let packet = Array(size - this._lastEventSize).fill(null);
    for (let [stack, index] of this._framesToIndices) {
      if (index >= this._lastEventSize) {
        packet[index - this._lastEventSize] = this._framesToForms.get(stack);
      }
    }

    this._lastEventSize = size;

    return packet;
  }

  /**
   * Assigns an index to the given frame and its parents, if an index is not
   * already assigned.
   *
   * @param SavedFrame frame
   *        A frame to assign an index to.
   */
  _assignFrameIndices(frame) {
    if (this._framesToIndices.has(frame)) {
      return;
    }

    if (frame) {
      this._assignFrameIndices(frame.parent);
      this._assignFrameIndices(frame.asyncParent);
    }

    const index = this._framesToIndices.size;
    this._framesToIndices.set(frame, index);
  }

  /**
   * Create the form for the given frame, if one doesn't already exist.
   *
   * @param SavedFrame frame
   *        A frame to create a form for.
   */
  _createFrameForms(frame) {
    if (this._framesToForms.has(frame)) {
      return;
    }

    let form = null;
    if (frame) {
      form = {
        line: frame.line,
        column: frame.column,
        source: frame.source,
        functionDisplayName: frame.functionDisplayName,
        parent: this._framesToIndices.get(frame.parent),
        asyncParent: this._framesToIndices.get(frame.asyncParent),
        asyncCause: frame.asyncCause
      };
      this._createFrameForms(frame.parent);
      this._createFrameForms(frame.asyncParent);
    }

    this._framesToForms.set(frame, form);
  }
}

exports.StackFrameCache = StackFrameCache;
PK
!<à/�� � Echrome/devtools/modules/devtools/server/actors/utils/walker-search.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 walker-search module provides a simple API to index and search strings
 * and elements inside a given document.
 * It indexes tag names, attribute names and values, and text contents.
 * It provides a simple search function that returns a list of nodes that
 * matched.
 */

/**
 * The WalkerIndex class indexes the document (and all subdocs) from
 * a given walker.
 *
 * It is only indexed the first time the data is accessed and will be
 * re-indexed if a mutation happens between requests.
 *
 * @param {Walker} walker The walker to be indexed
 */
function WalkerIndex(walker) {
  this.walker = walker;
  this.clearIndex = this.clearIndex.bind(this);

  // Kill the index when mutations occur, the next data get will re-index.
  this.walker.on("any-mutation", this.clearIndex);
}

WalkerIndex.prototype = {
  /**
   * Destroy this instance, releasing all data and references
   */
  destroy: function () {
    this.walker.off("any-mutation", this.clearIndex);
  },

  clearIndex: function () {
    if (!this.currentlyIndexing) {
      this._data = null;
    }
  },

  get doc() {
    return this.walker.rootDoc;
  },

  /**
   * Get the indexed data
   * This getter also indexes if it hasn't been done yet or if the state is
   * dirty
   *
   * @returns Map<String, Array<{type:String, node:DOMNode}>>
   *          A Map keyed on the searchable value, containing an array with
   *          objects containing the 'type' (one of ALL_RESULTS_TYPES), and
   *          the DOM Node.
   */
  get data() {
    if (!this._data) {
      this._data = new Map();
      this.index();
    }

    return this._data;
  },

  _addToIndex: function (type, node, value) {
    // Add an entry for this value if there isn't one
    let entry = this._data.get(value);
    if (!entry) {
      this._data.set(value, []);
    }

    // Add the type/node to the list
    this._data.get(value).push({
      type: type,
      node: node
    });
  },

  index: function () {
    // Handle case where iterating nextNode() with the deepTreeWalker triggers
    // a mutation (Bug 1222558)
    this.currentlyIndexing = true;

    let documentWalker = this.walker.getDocumentWalker(this.doc);
    while (documentWalker.nextNode()) {
      let node = documentWalker.currentNode;

      if (node.nodeType === 1) {
        // For each element node, we get the tagname and all attributes names
        // and values
        let localName = node.localName;
        if (localName === "_moz_generated_content_before") {
          this._addToIndex("tag", node, "::before");
          this._addToIndex("text", node, node.textContent.trim());
        } else if (localName === "_moz_generated_content_after") {
          this._addToIndex("tag", node, "::after");
          this._addToIndex("text", node, node.textContent.trim());
        } else {
          this._addToIndex("tag", node, node.localName);
        }

        for (let {name, value} of node.attributes) {
          this._addToIndex("attributeName", node, name);
          this._addToIndex("attributeValue", node, value);
        }
      } else if (node.textContent && node.textContent.trim().length) {
        // For comments and text nodes, we get the text
        this._addToIndex("text", node, node.textContent.trim());
      }
    }

    this.currentlyIndexing = false;
  }
};

exports.WalkerIndex = WalkerIndex;

/**
 * The WalkerSearch class provides a way to search an indexed document as well
 * as find elements that match a given css selector.
 *
 * Usage example:
 * let s = new WalkerSearch(doc);
 * let res = s.search("lang", index);
 * for (let {matched, results} of res) {
 *   for (let {node, type} of results) {
 *     console.log("The query matched a node's " + type);
 *     console.log("Node that matched", node);
 *    }
 * }
 * s.destroy();
 *
 * @param {Walker} the walker to be searched
 */
function WalkerSearch(walker) {
  this.walker = walker;
  this.index = new WalkerIndex(this.walker);
}

WalkerSearch.prototype = {
  destroy: function () {
    this.index.destroy();
    this.walker = null;
  },

  _addResult: function (node, type, results) {
    if (!results.has(node)) {
      results.set(node, []);
    }

    let matches = results.get(node);

    // Do not add if the exact same result is already in the list
    let isKnown = false;
    for (let match of matches) {
      if (match.type === type) {
        isKnown = true;
        break;
      }
    }

    if (!isKnown) {
      matches.push({type});
    }
  },

  _searchIndex: function (query, options, results) {
    for (let [matched, res] of this.index.data) {
      if (!options.searchMethod(query, matched)) {
        continue;
      }

      // Add any relevant results (skipping non-requested options).
      res.filter(entry => {
        return options.types.includes(entry.type);
      }).forEach(({node, type}) => {
        this._addResult(node, type, results);
      });
    }
  },

  _searchSelectors: function (query, options, results) {
    // If the query is just one "word", no need to search because _searchIndex
    // will lead the same results since it has access to tagnames anyway
    let isSelector = query && query.match(/[ >~.#\[\]]/);
    if (!options.types.includes("selector") || !isSelector) {
      return;
    }

    let nodes = this.walker._multiFrameQuerySelectorAll(query);
    for (let node of nodes) {
      this._addResult(node, "selector", results);
    }
  },

  /**
   * Search the document
   * @param {String} query What to search for
   * @param {Object} options The following options are accepted:
   * - searchMethod {String} one of WalkerSearch.SEARCH_METHOD_*
   *   defaults to WalkerSearch.SEARCH_METHOD_CONTAINS (does not apply to
   *   selector search type)
   * - types {Array} a list of things to search for (tag, text, attributes, etc)
   *   defaults to WalkerSearch.ALL_RESULTS_TYPES
   * @return {Array} An array is returned with each item being an object like:
   * {
   *   node: <the dom node that matched>,
   *   type: <the type of match: one of WalkerSearch.ALL_RESULTS_TYPES>
   * }
   */
  search: function (query, options = {}) {
    options.searchMethod = options.searchMethod || WalkerSearch.SEARCH_METHOD_CONTAINS;
    options.types = options.types || WalkerSearch.ALL_RESULTS_TYPES;

    // Empty strings will return no results, as will non-string input
    if (typeof query !== "string") {
      query = "";
    }

    // Store results in a map indexed by nodes to avoid duplicate results
    let results = new Map();

    // Search through the indexed data
    this._searchIndex(query, options, results);

    // Search with querySelectorAll
    this._searchSelectors(query, options, results);

    // Concatenate all results into an Array to return
    let resultList = [];
    for (let [node, matches] of results) {
      for (let {type} of matches) {
        resultList.push({
          node: node,
          type: type,
        });

        // For now, just do one result per node since the frontend
        // doesn't have a way to highlight each result individually
        // yet.
        break;
      }
    }

    let documents = this.walker.tabActor.windows.map(win=>win.document);

    // Sort the resulting nodes by order of appearance in the DOM
    resultList.sort((a, b) => {
      // Disconnected nodes won't get good results from compareDocumentPosition
      // so check the order of their document instead.
      if (a.node.ownerDocument != b.node.ownerDocument) {
        let indA = documents.indexOf(a.node.ownerDocument);
        let indB = documents.indexOf(b.node.ownerDocument);
        return indA - indB;
      }
      // If the same document, then sort on DOCUMENT_POSITION_FOLLOWING (4)
      // which means B is after A.
      return a.node.compareDocumentPosition(b.node) & 4 ? -1 : 1;
    });

    return resultList;
  }
};

WalkerSearch.SEARCH_METHOD_CONTAINS = (query, candidate) => {
  return query && candidate.toLowerCase().includes(query.toLowerCase());
};

WalkerSearch.ALL_RESULTS_TYPES = ["tag", "text", "attributeName",
                                  "attributeValue", "selector"];

exports.WalkerSearch = WalkerSearch;
PK
!<ӭ-�g�g:chrome/devtools/modules/devtools/server/actors/webaudio.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 XPCNativeWrapper */

const { Cu } = require("chrome");

const protocol = require("devtools/shared/protocol");
const { CallWatcherActor } = require("devtools/server/actors/call-watcher");
const { createValueGrip } = require("devtools/server/actors/object");
const AutomationTimeline = require("./utils/automation-timeline");
const {
  audionodeSpec,
  webAudioSpec
} = require("devtools/shared/specs/webaudio");
const { WebAudioFront } = require("devtools/shared/fronts/webaudio");

const Services = require("Services");

const AUDIO_NODE_DEFINITION = require("devtools/server/actors/utils/audionodes.json");
const ENABLE_AUTOMATION = false;
const AUTOMATION_GRANULARITY = 2000;
const AUTOMATION_GRANULARITY_MAX = 6000;

const AUDIO_GLOBALS = [
  "BaseAudioContext", "AudioContext", "AudioNode", "AudioParam"
];

/**
 * An Audio Node actor allowing communication to a specific audio node in the
 * Audio Context graph.
 */
var AudioNodeActor = exports.AudioNodeActor = protocol.ActorClassWithSpec(audionodeSpec, {
  form: function (detail) {
    if (detail === "actorid") {
      return this.actorID;
    }

    return {
      // actorID is set when this is added to a pool
      actor: this.actorID,
      type: this.type,
      source: this.source,
      bypassable: this.bypassable,
    };
  },

  /**
   * Create the Audio Node actor.
   *
   * @param DebuggerServerConnection conn
   *        The server connection.
   * @param AudioNode node
   *        The AudioNode that was created.
   */
  initialize: function (conn, node) {
    protocol.Actor.prototype.initialize.call(this, conn);

    // Store ChromeOnly property `id` to identify AudioNode,
    // rather than storing a strong reference, and store a weak
    // ref to underlying node for controlling.
    this.nativeID = node.id;
    this.node = Cu.getWeakReference(node);

    // Stores the AutomationTimelines for this node's AudioParams.
    this.automation = {};

    try {
      this.type = getConstructorName(node);
    } catch (e) {
      this.type = "";
    }

    this.source = !!AUDIO_NODE_DEFINITION[this.type].source;
    this.bypassable = !AUDIO_NODE_DEFINITION[this.type].unbypassable;

    // Create automation timelines for all AudioParams
    Object.keys(AUDIO_NODE_DEFINITION[this.type].properties || {})
      .filter(isAudioParam.bind(null, node))
      .forEach(paramName => {
        this.automation[paramName] = new AutomationTimeline(node[paramName].defaultValue);
      });
  },

  /**
   * Returns the string name of the audio type.
   *
   * DEPRECATED: Use `audionode.type` instead, left here for legacy reasons.
   */
  getType: function () {
    return this.type;
  },

  /**
   * Returns a boolean indicating if the AudioNode has been "bypassed",
   * via `AudioNodeActor#bypass` method.
   *
   * @return Boolean
   */
  isBypassed: function () {
    let node = this.node.get();
    if (node === null) {
      return false;
    }

    // Cast to boolean incase `passThrough` is undefined,
    // like for AudioDestinationNode
    return !!node.passThrough;
  },

  /**
   * Takes a boolean, either enabling or disabling the "passThrough" option
   * on an AudioNode. If a node is bypassed, an effects processing node (like gain,
   * biquad), will allow the audio stream to pass through the node, unaffected.
   * Returns the bypass state of the node.
   *
   * @param Boolean enable
   *        Whether the bypass value should be set on or off.
   * @return Boolean
   */
  bypass: function (enable) {
    let node = this.node.get();

    if (node === null) {
      return undefined;
    }

    if (this.bypassable) {
      node.passThrough = enable;
    }

    return this.isBypassed();
  },

  /**
   * Changes a param on the audio node. Responds with either `undefined`
   * on success, or a description of the error upon param set failure.
   *
   * @param String param
   *        Name of the AudioParam to change.
   * @param String value
   *        Value to change AudioParam to.
   */
  setParam: function (param, value) {
    let node = this.node.get();

    if (node === null) {
      return CollectedAudioNodeError();
    }

    try {
      if (isAudioParam(node, param)) {
        node[param].value = value;
        this.automation[param].setValue(value);
      } else {
        node[param] = value;
      }
      return undefined;
    } catch (e) {
      return constructError(e);
    }
  },

  /**
   * Gets a param on the audio node.
   *
   * @param String param
   *        Name of the AudioParam to fetch.
   */
  getParam: function (param) {
    let node = this.node.get();

    if (node === null) {
      return CollectedAudioNodeError();
    }

    // Check to see if it's an AudioParam -- if so,
    // return the `value` property of the parameter.
    let value = isAudioParam(node, param) ? node[param].value : node[param];

    // Return the grip form of the value; at this time,
    // there shouldn't be any non-primitives at the moment, other than
    // AudioBuffer or Float32Array references and the like,
    // so this just formats the value to be displayed in the VariablesView,
    // without using real grips and managing via actor pools.
    let grip = createValueGrip(value, null, createObjectGrip);

    return grip;
  },

  /**
   * Get an object containing key-value pairs of additional attributes
   * to be consumed by a front end, like if a property should be read only,
   * or is a special type (Float32Array, Buffer, etc.)
   *
   * @param String param
   *        Name of the AudioParam whose flags are desired.
   */
  getParamFlags: function (param) {
    return ((AUDIO_NODE_DEFINITION[this.type] || {}).properties || {})[param];
  },

  /**
   * Get an array of objects each containing a `param` and `value` property,
   * corresponding to a property name and current value of the audio node.
   */
  getParams: function (param) {
    let props = Object.keys(AUDIO_NODE_DEFINITION[this.type].properties || {});
    return props.map(prop =>
      ({ param: prop, value: this.getParam(prop), flags: this.getParamFlags(prop) }));
  },

  /**
   * Connects this audionode to an AudioParam via `node.connect(param)`.
   */
  connectParam: function (destActor, paramName, output) {
    let srcNode = this.node.get();
    let destNode = destActor.node.get();

    if (srcNode === null || destNode === null) {
      return CollectedAudioNodeError();
    }

    try {
      // Connect via the unwrapped node, so we can call the
      // patched method that fires the webaudio actor's `connect-param` event.
      // Connect directly to the wrapped `destNode`, otherwise
      // the patched method thinks this is a new node and won't be
      // able to find it in `_nativeToActorID`.
      XPCNativeWrapper.unwrap(srcNode).connect(destNode[paramName], output);
    } catch (e) {
      return constructError(e);
    }
    return undefined;
  },

  /**
   * Connects this audionode to another via `node.connect(dest)`.
   */
  connectNode: function (destActor, output, input) {
    let srcNode = this.node.get();
    let destNode = destActor.node.get();

    if (srcNode === null || destNode === null) {
      return CollectedAudioNodeError();
    }

    try {
      // Connect via the unwrapped node, so we can call the
      // patched method that fires the webaudio actor's `connect-node` event.
      // Connect directly to the wrapped `destNode`, otherwise
      // the patched method thinks this is a new node and won't be
      // able to find it in `_nativeToActorID`.
      XPCNativeWrapper.unwrap(srcNode).connect(destNode, output, input);
    } catch (e) {
      return constructError(e);
    }
    return undefined;
  },

  /**
   * Disconnects this audionode from all connections via `node.disconnect()`.
   */
  disconnect: function (destActor, output) {
    let node = this.node.get();

    if (node === null) {
      return CollectedAudioNodeError();
    }

    try {
      // Disconnect via the unwrapped node, so we can call the
      // patched method that fires the webaudio actor's `disconnect` event.
      XPCNativeWrapper.unwrap(node).disconnect(output);
    } catch (e) {
      return constructError(e);
    }
    return undefined;
  },

  getAutomationData: function (paramName) {
    let timeline = this.automation[paramName];
    if (!timeline) {
      return null;
    }

    let values = [];
    let i = 0;

    if (!timeline.events.length) {
      return { events: timeline.events, values };
    }

    let firstEvent = timeline.events[0];
    let lastEvent = timeline.events[timeline.events.length - 1];
    // `setValueCurveAtTime` will have a duration value -- other
    // events will have duration of `0`.
    let timeDelta = (lastEvent.time + lastEvent.duration) - firstEvent.time;
    let scale = timeDelta / AUTOMATION_GRANULARITY;

    for (; i < AUTOMATION_GRANULARITY; i++) {
      let delta = firstEvent.time + (i * scale);
      let value = timeline.getValueAtTime(delta);
      values.push({ delta, value });
    }

    // If the last event is setTargetAtTime, the automation
    // doesn't actually begin until the event's time, and exponentially
    // approaches the target value. In this case, we add more values
    // until we're "close enough" to the target.
    if (lastEvent.type === "setTargetAtTime") {
      for (; i < AUTOMATION_GRANULARITY_MAX; i++) {
        let delta = firstEvent.time + (++i * scale);
        let value = timeline.getValueAtTime(delta);
        values.push({ delta, value });
      }
    }

    return { events: timeline.events, values };
  },

  /**
   * Called via WebAudioActor, registers an automation event
   * for the AudioParam called.
   *
   * @param String paramName
   *        Name of the AudioParam.
   * @param String eventName
   *        Name of the automation event called.
   * @param Array args
   *        Arguments passed into the automation call.
   */
  addAutomationEvent: function (paramName, eventName, args = []) {
    let node = this.node.get();
    let timeline = this.automation[paramName];

    if (node === null) {
      return CollectedAudioNodeError();
    }

    if (!timeline || !node[paramName][eventName]) {
      return InvalidCommandError();
    }

    try {
      // Using the unwrapped node and parameter, the corresponding
      // WebAudioActor event will be fired, subsequently calling
      // `_recordAutomationEvent`. Some finesse is required to handle
      // the cast of TypedArray arguments over the protocol, which is
      // taken care of below. The event will cast the argument back
      // into an array to be broadcasted from WebAudioActor, but the
      // double-casting will only occur when starting from `addAutomationEvent`,
      // which is only used in tests.
      let param = XPCNativeWrapper.unwrap(node[paramName]);
      let contentGlobal = Cu.getGlobalForObject(param);
      let contentArgs = Cu.cloneInto(args, contentGlobal);

      // If calling `setValueCurveAtTime`, the first argument
      // is a Float32Array, which won't be able to be serialized
      // over the protocol. Cast a normal array to a Float32Array here.
      if (eventName === "setValueCurveAtTime") {
        // Create a Float32Array from the content, seeding with an array
        // from the same scope.
        let curve = new contentGlobal.Float32Array(contentArgs[0]);
        contentArgs[0] = curve;
      }

      // Apply the args back from the content scope, which is necessary
      // due to the method wrapping changing in bug 1130901 to be exported
      // directly to the content scope.
      param[eventName].apply(param, contentArgs);
    } catch (e) {
      return constructError(e);
    }
    return undefined;
  },

  /**
   * Registers the automation event in the AudioNodeActor's
   * internal timeline. Called when setting automation via
   * `addAutomationEvent`, or from the WebAudioActor's listening
   * to the event firing via content.
   *
   * @param String paramName
   *        Name of the AudioParam.
   * @param String eventName
   *        Name of the automation event called.
   * @param Array args
   *        Arguments passed into the automation call.
   */
  _recordAutomationEvent: function (paramName, eventName, args) {
    let timeline = this.automation[paramName];
    timeline[eventName].apply(timeline, args);
  }
});

/**
 * The Web Audio Actor handles simple interaction with an BaseAudioContext
 * high-level methods. After instantiating this actor, you'll need to set it
 * up by calling setup().
 */
exports.WebAudioActor = protocol.ActorClassWithSpec(webAudioSpec, {
  initialize: function (conn, tabActor) {
    protocol.Actor.prototype.initialize.call(this, conn);
    this.tabActor = tabActor;

    this._onContentFunctionCall = this._onContentFunctionCall.bind(this);

    // Store ChromeOnly ID (`nativeID` property on AudioNodeActor) mapped
    // to the associated actorID, so we don't have to expose `nativeID`
    // to the client in any way.
    this._nativeToActorID = new Map();

    this._onGlobalDestroyed = this._onGlobalDestroyed.bind(this);
    this._onGlobalCreated = this._onGlobalCreated.bind(this);
  },

  destroy: function (conn) {
    protocol.Actor.prototype.destroy.call(this, conn);
    this.finalize();
  },

  /**
   * Returns definition of all AudioNodes, such as AudioParams, and
   * flags.
   */
  getDefinition: function () {
    return AUDIO_NODE_DEFINITION;
  },

  /**
   * Starts waiting for the current tab actor's document global to be
   * created, in order to instrument the Canvas context and become
   * aware of everything the content does with Web Audio.
   *
   * See ContentObserver and WebAudioInstrumenter for more details.
   */
  setup: function ({ reload }) {
    // Used to track when something is happening with the web audio API
    // the first time, to ultimately fire `start-context` event
    this._firstNodeCreated = false;

    // Clear out stored nativeIDs on reload as we do not want to track
    // AudioNodes that are no longer on this document.
    this._nativeToActorID.clear();

    if (this._initialized) {
      if (reload) {
        this.tabActor.window.location.reload();
      }
      return;
    }

    this._initialized = true;

    this._callWatcher = new CallWatcherActor(this.conn, this.tabActor);
    this._callWatcher.onCall = this._onContentFunctionCall;
    this._callWatcher.setup({
      tracedGlobals: AUDIO_GLOBALS,
      startRecording: true,
      performReload: reload,
      holdWeak: true,
      storeCalls: false
    });
    // Bind to `window-ready` so we can reenable recording on the
    // call watcher
    this.tabActor.on("window-ready", this._onGlobalCreated);
    // Bind to the `window-destroyed` event so we can unbind events between
    // the global destruction and the `finalize` cleanup method on the actor.
    this.tabActor.on("window-destroyed", this._onGlobalDestroyed);
  },

  /**
   * Invoked whenever an instrumented function is called, like an
   * BaseAudioContext method or an AudioNode method.
   */
  _onContentFunctionCall: function (functionCall) {
    let { name } = functionCall.details;

    // All Web Audio nodes inherit from AudioNode's prototype, so
    // hook into the `connect` and `disconnect` methods
    if (WebAudioFront.NODE_ROUTING_METHODS.has(name)) {
      this._handleRoutingCall(functionCall);
    } else if (WebAudioFront.NODE_CREATION_METHODS.has(name)) {
      this._handleCreationCall(functionCall);
    } else if (ENABLE_AUTOMATION && WebAudioFront.AUTOMATION_METHODS.has(name)) {
      this._handleAutomationCall(functionCall);
    }
  },

  _handleRoutingCall: function (functionCall) {
    let { caller, args, name } = functionCall.details;
    let source = caller;
    let dest = args[0];
    let isAudioPar = dest ? getConstructorName(dest) === "AudioParam" : false;

    // audionode.connect(param)
    if (name === "connect" && isAudioPar) {
      this._onConnectParam(source, dest);
    } else if (name === "connect") {
      // audionode.connect(node)
      this._onConnectNode(source, dest);
    } else if (name === "disconnect") {
      // audionode.disconnect()
      this._onDisconnectNode(source);
    }
  },

  _handleCreationCall: function (functionCall) {
    let { caller, result } = functionCall.details;
    // Keep track of the first node created, so we can alert
    // the front end that an audio context is being used since
    // we're not hooking into the constructor itself, just its
    // instance's methods.
    if (!this._firstNodeCreated) {
      // Fire the start-up event if this is the first node created
      // and trigger a `create-node` event for the context destination
      this._onStartContext();
      this._onCreateNode(caller.destination);
      this._firstNodeCreated = true;
    }
    this._onCreateNode(result);
  },

  _handleAutomationCall: function (functionCall) {
    let { caller, name, args } = functionCall.details;
    let wrappedParam = new XPCNativeWrapper(caller);

    // Sanitize arguments, as these should all be numbers,
    // with the exception of a TypedArray, which needs
    // casted to an Array
    args = sanitizeAutomationArgs(args);

    let nodeActor = this._getActorByNativeID(wrappedParam._parentID);
    nodeActor._recordAutomationEvent(wrappedParam._paramName, name, args);

    this._onAutomationEvent({
      node: nodeActor,
      paramName: wrappedParam._paramName,
      eventName: name,
      args: args
    });
  },

  /**
   * Stops listening for document global changes and puts this actor
   * to hibernation. This method is called automatically just before the
   * actor is destroyed.
   */
  finalize: function () {
    if (!this._initialized) {
      return;
    }
    this._initialized = false;

    try {
      Services.obs.removeObserver(this, "webaudio-node-demise");
    } catch (e) {
      // Maybe we've shutdown already and it's too late to remove the observer. So avoid
      // NS_ERROR_FAILURE errors with this silent try/catch.
    }

    this.tabActor.off("window-destroyed", this._onGlobalDestroyed);
    this.tabActor.off("window-ready", this._onGlobalCreated);
    this.tabActor = null;
    this._nativeToActorID = null;
    this._callWatcher.eraseRecording();
    this._callWatcher.finalize();
    this._callWatcher = null;
  },

  /**
   * Helper for constructing an AudioNodeActor, assigning to
   * internal weak map, and tracking via `manage` so it is assigned
   * an `actorID`.
   */
  _constructAudioNode: function (node) {
    // Ensure AudioNode is wrapped.
    node = new XPCNativeWrapper(node);

    this._instrumentParams(node);

    let actor = new AudioNodeActor(this.conn, node);
    this.manage(actor);
    this._nativeToActorID.set(node.id, actor.actorID);
    return actor;
  },

  /**
   * Takes an XrayWrapper node, and attaches the node's `nativeID`
   * to the AudioParams as `_parentID`, as well as the the type of param
   * as a string on `_paramName`.
   */
  _instrumentParams: function (node) {
    let type = getConstructorName(node);
    Object.keys(AUDIO_NODE_DEFINITION[type].properties || {})
      .filter(isAudioParam.bind(null, node))
      .forEach(paramName => {
        let param = node[paramName];
        param._parentID = node.id;
        param._paramName = paramName;
      });
  },

  /**
   * Takes an AudioNode and returns the stored actor for it.
   * In some cases, we won't have an actor stored (for example,
   * connecting to an AudioDestinationNode, since it's implicitly
   * created), so make a new actor and store that.
   */
  _getActorByNativeID: function (nativeID) {
    // Ensure we have a Number, rather than a string
    // return via notification.
    nativeID = ~~nativeID;

    let actorID = this._nativeToActorID.get(nativeID);
    let actor = actorID != null ? this.conn.getActor(actorID) : null;
    return actor;
  },

  /**
   * Called on first audio node creation, signifying audio context usage
   */
  _onStartContext: function () {
    Services.obs.addObserver(this, "webaudio-node-demise");
    this.emit("start-context");
  },

  /**
   * Called when one audio node is connected to another.
   */
  _onConnectNode: function (source, dest) {
    let sourceActor = this._getActorByNativeID(source.id);
    let destActor = this._getActorByNativeID(dest.id);

    this.emit("connect-node", {
      source: sourceActor,
      dest: destActor
    });
  },

  /**
   * Called when an audio node is connected to an audio param.
   */
  _onConnectParam: function (source, param) {
    let sourceActor = this._getActorByNativeID(source.id);
    let destActor = this._getActorByNativeID(param._parentID);
    this.emit("connect-param", {
      source: sourceActor,
      dest: destActor,
      param: param._paramName
    });
  },

  /**
   * Called when an audio node is disconnected.
   */
  _onDisconnectNode: function (node) {
    let actor = this._getActorByNativeID(node.id);
    this.emit("disconnect-node", actor);
  },

  /**
   * Called when a parameter changes on an audio node
   */
  _onParamChange: function (node, param, value) {
    let actor = this._getActorByNativeID(node.id);
    this.emit("param-change", {
      source: actor,
      param: param,
      value: value
    });
  },

  /**
   * Called on node creation.
   */
  _onCreateNode: function (node) {
    let actor = this._constructAudioNode(node);
    this.emit("create-node", actor);
  },

  /**
   * Called by the ObserverService when webaudio-node-demise events are emitted.
   */
  observe: function (subject, topic, data) {
    switch (topic) {
      case "webaudio-node-demise":
        // Cast the data to an integer.
        this._handleNodeDestroyed(~~data);
        break;
    }
  },

  /**
   * Handles `webaudio-node-demise` events. Emits the associated actor to the front if
   * found.
   * @param {Number} nodeNativeID The ID for the audio node.
   */
  _handleNodeDestroyed: function (nodeNativeID) {
    let actor = this._getActorByNativeID(nodeNativeID);

    // If actorID exists, emit; in the case where we get demise
    // notifications for a document that no longer exists,
    // the mapping should not be found, so we do not emit an event.
    if (actor) {
      this._nativeToActorID.delete(nodeNativeID);
      this.emit("destroy-node", actor);
    }
  },

  /**
   * Ensures that the new global has recording on
   * so we can proxy the function calls.
   */
  _onGlobalCreated: function () {
    // Used to track when something is happening with the web audio API
    // the first time, to ultimately fire `start-context` event
    this._firstNodeCreated = false;

    // Clear out stored nativeIDs on reload as we do not want to track
    // AudioNodes that are no longer on this document.
    this._nativeToActorID.clear();

    this._callWatcher.resumeRecording();
  },

  /**
   * Fired when an automation event is added to an AudioNode.
   */
  _onAutomationEvent: function ({node, paramName, eventName, args}) {
    this.emit("automation-event", {
      node: node,
      paramName: paramName,
      eventName: eventName,
      args: args
    });
  },

  /**
   * Called when the underlying ContentObserver fires `global-destroyed`
   * so we can cleanup some things between the global being destroyed and
   * when the actor's `finalize` method gets called.
   */
  _onGlobalDestroyed: function ({id}) {
    if (this._callWatcher._tracedWindowId !== id) {
      return;
    }

    if (this._nativeToActorID) {
      this._nativeToActorID.clear();
    }
    Services.obs.removeObserver(this, "webaudio-node-demise");
  }
});

/**
 * Determines whether or not property is an AudioParam.
 *
 * @param AudioNode node
 *        An AudioNode.
 * @param String prop
 *        Property of `node` to evaluate to see if it's an AudioParam.
 * @return Boolean
 */
function isAudioParam(node, prop) {
  return !!(node[prop] && /AudioParam/.test(node[prop].toString()));
}

/**
 * Takes an `Error` object and constructs a JSON-able response
 *
 * @param Error err
 *        A TypeError, RangeError, etc.
 * @return Object
 */
function constructError(err) {
  return {
    message: err.message,
    type: err.constructor.name
  };
}

/**
 * Creates and returns a JSON-able response used to indicate
 * attempt to access an AudioNode that has been GC'd.
 *
 * @return Object
 */
function CollectedAudioNodeError() {
  return {
    message: "AudioNode has been garbage collected and can no longer be reached.",
    type: "UnreachableAudioNode"
  };
}

function InvalidCommandError() {
  return {
    message: "The command on AudioNode is invalid.",
    type: "InvalidCommand"
  };
}

/**
 * Takes an object and converts it's `toString()` form, like
 * "[object OscillatorNode]" or "[object Float32Array]",
 * or XrayWrapper objects like "[object XrayWrapper [object Array]]"
 * to a string of just the constructor name, like "OscillatorNode",
 * or "Float32Array".
 */
function getConstructorName(obj) {
  return Object.prototype.toString.call(obj).match(/\[object ([^\[\]]*)\]\]?$/)[1];
}

/**
 * Create a grip-like object to pass in renderable information
 * to the front-end for things like Float32Arrays, AudioBuffers,
 * without tracking them in an actor pool.
 */
function createObjectGrip(value) {
  return {
    type: "object",
    preview: {
      kind: "ObjectWithText",
      text: ""
    },
    class: getConstructorName(value)
  };
}

/**
 * Converts all TypedArrays of the array that cannot
 * be passed over the wire into a normal Array equivilent.
 */
function sanitizeAutomationArgs(args) {
  return args.reduce((newArgs, el) => {
    let isArray = typeof el === "object" && getConstructorName(el) === "Float32Array";
    newArgs.push(isArray ? castToArray(el) : el);
    return newArgs;
  }, []);
}

/**
 * Casts TypedArray to a normal array via a
 * new scope.
 */
function castToArray(typedArray) {
  // The Xray machinery for TypedArrays denies indexed access on the grounds
  // that it's slow, and advises callers to do a structured clone instead.
  let global = Cu.getGlobalForObject(this);
  let safeView = Cu.cloneInto(typedArray.subarray(), global);
  return copyInto([], safeView);
}

/**
 * Copies values of an array-like `source` into
 * a similarly array-like `dest`.
 */
function copyInto(dest, source) {
  for (let i = 0; i < source.length; i++) {
    dest[i] = source[i];
  }
  return dest;
}
PK
!<��IR`}`}<chrome/devtools/modules/devtools/server/actors/webbrowser.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

var { Ci } = require("chrome");
var Services = require("Services");
const defer = require("devtools/shared/defer");
var { DebuggerServer } = require("devtools/server/main");
var DevToolsUtils = require("devtools/shared/DevToolsUtils");

loader.lazyRequireGetter(this, "RootActor", "devtools/server/actors/root", true);
loader.lazyRequireGetter(this, "BrowserAddonActor", "devtools/server/actors/addon", true);
loader.lazyRequireGetter(this, "WebExtensionParentActor", "devtools/server/actors/webextension-parent", true);
loader.lazyRequireGetter(this, "WorkerActorList", "devtools/server/actors/worker-list", true);
loader.lazyRequireGetter(this, "ServiceWorkerRegistrationActorList", "devtools/server/actors/worker-list", true);
loader.lazyRequireGetter(this, "ProcessActorList", "devtools/server/actors/process", true);
loader.lazyImporter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm");
loader.lazyImporter(this, "PlacesUtils", "resource://gre/modules/PlacesUtils.jsm");

/**
 * Browser-specific actors.
 */

/**
 * Yield all windows of type |windowType|, from the oldest window to the
 * youngest, using nsIWindowMediator::getEnumerator. We're usually
 * interested in "navigator:browser" windows.
 */
function* allAppShellDOMWindows(windowType) {
  let e = Services.wm.getEnumerator(windowType);
  while (e.hasMoreElements()) {
    yield e.getNext();
  }
}

exports.allAppShellDOMWindows = allAppShellDOMWindows;

/**
 * Retrieve the window type of the top-level window |window|.
 */
function appShellDOMWindowType(window) {
  /* This is what nsIWindowMediator's enumerator checks. */
  return window.document.documentElement.getAttribute("windowtype");
}

/**
 * Send Debugger:Shutdown events to all "navigator:browser" windows.
 */
function sendShutdownEvent() {
  for (let win of allAppShellDOMWindows(DebuggerServer.chromeWindowType)) {
    let evt = win.document.createEvent("Event");
    evt.initEvent("Debugger:Shutdown", true, false);
    win.document.documentElement.dispatchEvent(evt);
  }
}

exports.sendShutdownEvent = sendShutdownEvent;

/**
 * Construct a root actor appropriate for use in a server running in a
 * browser. The returned root actor:
 * - respects the factories registered with DebuggerServer.addGlobalActor,
 * - uses a BrowserTabList to supply tab actors,
 * - sends all navigator:browser window documents a Debugger:Shutdown event
 *   when it exits.
 *
 * * @param connection DebuggerServerConnection
 *          The conection to the client.
 */
function createRootActor(connection) {
  return new RootActor(connection, {
    tabList: new BrowserTabList(connection),
    addonList: new BrowserAddonList(connection),
    workerList: new WorkerActorList(connection, {}),
    serviceWorkerRegistrationList:
      new ServiceWorkerRegistrationActorList(connection),
    processList: new ProcessActorList(),
    globalActorFactories: DebuggerServer.globalActorFactories,
    onShutdown: sendShutdownEvent
  });
}

/**
 * A live list of BrowserTabActors representing the current browser tabs,
 * to be provided to the root actor to answer 'listTabs' requests.
 *
 * This object also takes care of listening for TabClose events and
 * onCloseWindow notifications, and exiting the BrowserTabActors concerned.
 *
 * (See the documentation for RootActor for the definition of the "live
 * list" interface.)
 *
 * @param connection DebuggerServerConnection
 *     The connection in which this list's tab actors may participate.
 *
 * Some notes:
 *
 * This constructor is specific to the desktop browser environment; it
 * maintains the tab list by tracking XUL windows and their XUL documents'
 * "tabbrowser", "tab", and "browser" elements. What's entailed in maintaining
 * an accurate list of open tabs in this context?
 *
 * - Opening and closing XUL windows:
 *
 * An nsIWindowMediatorListener is notified when new XUL windows (i.e., desktop
 * windows) are opened and closed. It is not notified of individual content
 * browser tabs coming and going within such a XUL window. That seems
 * reasonable enough; it's concerned with XUL windows, not tab elements in the
 * window's XUL document.
 *
 * However, even if we attach TabOpen and TabClose event listeners to each XUL
 * window as soon as it is created:
 *
 * - we do not receive a TabOpen event for the initial empty tab of a new XUL
 *   window; and
 *
 * - we do not receive TabClose events for the tabs of a XUL window that has
 *   been closed.
 *
 * This means that TabOpen and TabClose events alone are not sufficient to
 * maintain an accurate list of live tabs and mark tab actors as closed
 * promptly. Our nsIWindowMediatorListener onCloseWindow handler must find and
 * exit all actors for tabs that were in the closing window.
 *
 * Since this is a bit hairy, we don't make each individual attached tab actor
 * responsible for noticing when it has been closed; we watch for that, and
 * promise to call each actor's 'exit' method when it's closed, regardless of
 * how we learn the news.
 *
 * - nsIWindowMediator locks
 *
 * nsIWindowMediator holds a lock protecting its list of top-level windows
 * while it calls nsIWindowMediatorListener methods. nsIWindowMediator's
 * GetEnumerator method also tries to acquire that lock. Thus, enumerating
 * windows from within a listener method deadlocks (bug 873589). Rah. One
 * can sometimes work around this by leaving the enumeration for a later
 * tick.
 *
 * - Dragging tabs between windows:
 *
 * When a tab is dragged from one desktop window to another, we receive a
 * TabOpen event for the new tab, and a TabClose event for the old tab; tab XUL
 * elements do not really move from one document to the other (although their
 * linked browser's content window objects do).
 *
 * However, while we could thus assume that each tab stays with the XUL window
 * it belonged to when it was created, I'm not sure this is behavior one should
 * rely upon. When a XUL window is closed, we take the less efficient, more
 * conservative approach of simply searching the entire table for actors that
 * belong to the closing XUL window, rather than trying to somehow track which
 * XUL window each tab belongs to.
 *
 * - Title changes:
 *
 * For tabs living in the child process, we listen for DOMTitleChange message
 * via the top-level window's message manager. Doing this also allows listening
 * for title changes on Fennec.
 * But as these messages aren't sent for tabs loaded in the parent process,
 * we also listen for TabAttrModified event, which is fired only on Firefox
 * desktop.
 */
function BrowserTabList(connection) {
  this._connection = connection;

  /*
   * The XUL document of a tabbed browser window has "tab" elements, whose
   * 'linkedBrowser' JavaScript properties are "browser" elements; those
   * browsers' 'contentWindow' properties are wrappers on the tabs' content
   * window objects.
   *
   * This map's keys are "browser" XUL elements; it maps each browser element
   * to the tab actor we've created for its content window, if we've created
   * one. This map serves several roles:
   *
   * - During iteration, we use it to find actors we've created previously.
   *
   * - On a TabClose event, we use it to find the tab's actor and exit it.
   *
   * - When the onCloseWindow handler is called, we iterate over it to find all
   *   tabs belonging to the closing XUL window, and exit them.
   *
   * - When it's empty, and the onListChanged hook is null, we know we can
   *   stop listening for events and notifications.
   *
   * We listen for TabClose events and onCloseWindow notifications in order to
   * send onListChanged notifications, but also to tell actors when their
   * referent has gone away and remove entries for dead browsers from this map.
   * If that code is working properly, neither this map nor the actors in it
   * should ever hold dead tabs alive.
   */
  this._actorByBrowser = new Map();

  /* The current onListChanged handler, or null. */
  this._onListChanged = null;

  /*
   * True if we've been iterated over since we last called our onListChanged
   * hook.
   */
  this._mustNotify = false;

  /* True if we're testing, and should throw if consistency checks fail. */
  this._testing = false;
}

BrowserTabList.prototype.constructor = BrowserTabList;

/**
 * Get the selected browser for the given navigator:browser window.
 * @private
 * @param window nsIChromeWindow
 *        The navigator:browser window for which you want the selected browser.
 * @return nsIDOMElement|null
 *         The currently selected xul:browser element, if any. Note that the
 *         browser window might not be loaded yet - the function will return
 *         |null| in such cases.
 */
BrowserTabList.prototype._getSelectedBrowser = function (window) {
  return window.gBrowser ? window.gBrowser.selectedBrowser : null;
};

/**
 * Produces an iterable (in this case a generator) to enumerate all available
 * browser tabs.
 */
BrowserTabList.prototype._getBrowsers = function* () {
  // Iterate over all navigator:browser XUL windows.
  for (let win of allAppShellDOMWindows(DebuggerServer.chromeWindowType)) {
    // For each tab in this XUL window, ensure that we have an actor for
    // it, reusing existing actors where possible. We actually iterate
    // over 'browser' XUL elements, and BrowserTabActor uses
    // browser.contentWindow as the debuggee global.
    for (let browser of this._getChildren(win)) {
      yield browser;
    }
  }
};

BrowserTabList.prototype._getChildren = function (window) {
  if (!window.gBrowser) {
    return [];
  }
  let { gBrowser } = window;
  if (!gBrowser.browsers) {
    return [];
  }
  return gBrowser.browsers.filter(browser => {
    // Filter tabs that are closing. listTabs calls made right after TabClose
    // events still list tabs in process of being closed.
    let tab = gBrowser.getTabForBrowser(browser);
    return !tab.closing;
  });
};

BrowserTabList.prototype.getList = function (browserActorOptions) {
  let topXULWindow = Services.wm.getMostRecentWindow(
    DebuggerServer.chromeWindowType);
  let selectedBrowser = null;
  if (topXULWindow) {
    selectedBrowser = this._getSelectedBrowser(topXULWindow);
  }

  // As a sanity check, make sure all the actors presently in our map get
  // picked up when we iterate over all windows' tabs.
  let initialMapSize = this._actorByBrowser.size;
  this._foundCount = 0;

  // To avoid mysterious behavior if tabs are closed or opened mid-iteration,
  // we update the map first, and then make a second pass over it to yield
  // the actors. Thus, the sequence yielded is always a snapshot of the
  // actors that were live when we began the iteration.

  let actorPromises = [];

  for (let browser of this._getBrowsers()) {
    let selected = browser === selectedBrowser;
    actorPromises.push(
      this._getActorForBrowser(browser, browserActorOptions)
          .then(actor => {
            // Set the 'selected' properties on all actors correctly.
            actor.selected = selected;
            return actor;
          }, e => {
            if (e.error === "tabDestroyed") {
              // Return null if a tab was destroyed while retrieving the tab list.
              return null;
            }

            // Forward unexpected errors.
            throw e;
          })
    );
  }

  if (this._testing && initialMapSize !== this._foundCount) {
    throw new Error("_actorByBrowser map contained actors for dead tabs");
  }

  this._mustNotify = true;
  this._checkListening();

  return Promise.all(actorPromises).then(values => {
    // Filter out null values if we received a tabDestroyed error.
    return values.filter(value => value != null);
  });
};

/**
 * @param browserActorOptions see options argument of BrowserTabActor constructor.
 */
BrowserTabList.prototype._getActorForBrowser = function (browser, browserActorOptions) {
  // Do we have an existing actor for this browser? If not, create one.
  let actor = this._actorByBrowser.get(browser);
  if (actor) {
    this._foundCount++;
    return actor.update(browserActorOptions);
  }

  actor = new BrowserTabActor(this._connection, browser, browserActorOptions);
  this._actorByBrowser.set(browser, actor);
  this._checkListening();
  return actor.connect();
};

BrowserTabList.prototype.getTab = function ({ outerWindowID, tabId }) {
  if (typeof outerWindowID == "number") {
    // First look for in-process frames with this ID
    let window = Services.wm.getOuterWindowWithId(outerWindowID);
    // Safety check to prevent debugging top level window via getTab
    if (window && window.isChromeWindow) {
      return Promise.reject({
        error: "forbidden",
        message: "Window with outerWindowID '" + outerWindowID + "' is chrome"
      });
    }
    if (window) {
      let iframe = window.QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsIDOMWindowUtils)
                         .containerElement;
      if (iframe) {
        return this._getActorForBrowser(iframe);
      }
    }
    // Then also look on registered <xul:browsers> when using outerWindowID for
    // OOP tabs
    for (let browser of this._getBrowsers()) {
      if (browser.outerWindowID == outerWindowID) {
        return this._getActorForBrowser(browser);
      }
    }
    return Promise.reject({
      error: "noTab",
      message: "Unable to find tab with outerWindowID '" + outerWindowID + "'"
    });
  } else if (typeof tabId == "number") {
    // Tabs OOP
    for (let browser of this._getBrowsers()) {
      if (browser.frameLoader &&
          browser.frameLoader.tabParent &&
          browser.frameLoader.tabParent.tabId === tabId) {
        return this._getActorForBrowser(browser);
      }
    }
    return Promise.reject({
      error: "noTab",
      message: "Unable to find tab with tabId '" + tabId + "'"
    });
  }

  let topXULWindow = Services.wm.getMostRecentWindow(
    DebuggerServer.chromeWindowType);
  if (topXULWindow) {
    let selectedBrowser = this._getSelectedBrowser(topXULWindow);
    return this._getActorForBrowser(selectedBrowser);
  }
  return Promise.reject({
    error: "noTab",
    message: "Unable to find any selected browser"
  });
};

Object.defineProperty(BrowserTabList.prototype, "onListChanged", {
  enumerable: true,
  configurable: true,
  get() {
    return this._onListChanged;
  },
  set(v) {
    if (v !== null && typeof v !== "function") {
      throw new Error(
        "onListChanged property may only be set to 'null' or a function");
    }
    this._onListChanged = v;
    this._checkListening();
  }
});

/**
 * The set of tabs has changed somehow. Call our onListChanged handler, if
 * one is set, and if we haven't already called it since the last iteration.
 */
BrowserTabList.prototype._notifyListChanged = function () {
  if (!this._onListChanged) {
    return;
  }
  if (this._mustNotify) {
    this._onListChanged();
    this._mustNotify = false;
  }
};

/**
 * Exit |actor|, belonging to |browser|, and notify the onListChanged
 * handle if needed.
 */
BrowserTabList.prototype._handleActorClose = function (actor, browser) {
  if (this._testing) {
    if (this._actorByBrowser.get(browser) !== actor) {
      throw new Error("BrowserTabActor not stored in map under given browser");
    }
    if (actor.browser !== browser) {
      throw new Error("actor's browser and map key don't match");
    }
  }

  this._actorByBrowser.delete(browser);
  actor.exit();

  this._notifyListChanged();
  this._checkListening();
};

/**
 * Make sure we are listening or not listening for activity elsewhere in
 * the browser, as appropriate. Other than setting up newly created XUL
 * windows, all listener / observer management should happen here.
 */
BrowserTabList.prototype._checkListening = function () {
  /*
   * If we have an onListChanged handler that we haven't sent an announcement
   * to since the last iteration, we need to watch for tab creation as well as
   * change of the currently selected tab and tab title changes of tabs in
   * parent process via TabAttrModified (tabs oop uses DOMTitleChanges).
   *
   * Oddly, we don't need to watch for 'close' events here. If our actor list
   * is empty, then either it was empty the last time we iterated, and no
   * close events are possible, or it was not empty the last time we
   * iterated, but all the actors have since been closed, and we must have
   * sent a notification already when they closed.
   */
  this._listenForEventsIf(this._onListChanged && this._mustNotify,
                          "_listeningForTabOpen",
                          ["TabOpen", "TabSelect", "TabAttrModified"]);

  /* If we have live actors, we need to be ready to mark them dead. */
  this._listenForEventsIf(this._actorByBrowser.size > 0,
                          "_listeningForTabClose",
                          ["TabClose", "TabRemotenessChange"]);

  /*
   * We must listen to the window mediator in either case, since that's the
   * only way to find out about tabs that come and go when top-level windows
   * are opened and closed.
   */
  this._listenToMediatorIf((this._onListChanged && this._mustNotify) ||
                           (this._actorByBrowser.size > 0));

  /*
   * We also listen for title changed from the child process.
   * This allows listening for title changes from Fennec and OOP tabs in Fx.
   */
  this._listenForMessagesIf(this._onListChanged && this._mustNotify,
                            "_listeningForTitleChange",
                            ["DOMTitleChanged"]);
};

/*
 * Add or remove event listeners for all XUL windows.
 *
 * @param shouldListen boolean
 *    True if we should add event handlers; false if we should remove them.
 * @param guard string
 *    The name of a guard property of 'this', indicating whether we're
 *    already listening for those events.
 * @param eventNames array of strings
 *    An array of event names.
 */
BrowserTabList.prototype._listenForEventsIf =
  function (shouldListen, guard, eventNames) {
    if (!shouldListen !== !this[guard]) {
      let op = shouldListen ? "addEventListener" : "removeEventListener";
      for (let win of allAppShellDOMWindows(DebuggerServer.chromeWindowType)) {
        for (let name of eventNames) {
          win[op](name, this, false);
        }
      }
      this[guard] = shouldListen;
    }
  };

/*
 * Add or remove message listeners for all XUL windows.
 *
 * @param aShouldListen boolean
 *    True if we should add message listeners; false if we should remove them.
 * @param aGuard string
 *    The name of a guard property of 'this', indicating whether we're
 *    already listening for those messages.
 * @param aMessageNames array of strings
 *    An array of message names.
 */
BrowserTabList.prototype._listenForMessagesIf =
  function (shouldListen, guard, messageNames) {
    if (!shouldListen !== !this[guard]) {
      let op = shouldListen ? "addMessageListener" : "removeMessageListener";
      for (let win of allAppShellDOMWindows(DebuggerServer.chromeWindowType)) {
        for (let name of messageNames) {
          win.messageManager[op](name, this);
        }
      }
      this[guard] = shouldListen;
    }
  };

/**
 * Implement nsIMessageListener.
 */
BrowserTabList.prototype.receiveMessage = DevToolsUtils.makeInfallible(
  function (message) {
    let browser = message.target;
    switch (message.name) {
      case "DOMTitleChanged": {
        let actor = this._actorByBrowser.get(browser);
        if (actor) {
          this._notifyListChanged();
          this._checkListening();
        }
        break;
      }
    }
  });

/**
 * Implement nsIDOMEventListener.
 */
BrowserTabList.prototype.handleEvent =
DevToolsUtils.makeInfallible(function (event) {
  let browser = event.target.linkedBrowser;
  switch (event.type) {
    case "TabOpen":
    case "TabSelect": {
      /* Don't create a new actor; iterate will take care of that. Just notify. */
      this._notifyListChanged();
      this._checkListening();
      break;
    }
    case "TabClose": {
      let actor = this._actorByBrowser.get(browser);
      if (actor) {
        this._handleActorClose(actor, browser);
      }
      break;
    }
    case "TabRemotenessChange": {
      // We have to remove the cached actor as we have to create a new instance.
      let actor = this._actorByBrowser.get(browser);
      if (actor) {
        this._actorByBrowser.delete(browser);
        // Don't create a new actor; iterate will take care of that. Just notify.
        this._notifyListChanged();
        this._checkListening();
      }
      break;
    }
    case "TabAttrModified": {
      // Remote <browser> title changes are handled via DOMTitleChange message
      // TabAttrModified is only here for browsers in parent process which
      // don't send this message.
      if (browser.isRemoteBrowser) {
        break;
      }
      let actor = this._actorByBrowser.get(browser);
      if (actor) {
        // TabAttrModified is fired in various cases, here only care about title
        // changes
        if (event.detail.changed.includes("label")) {
          this._notifyListChanged();
          this._checkListening();
        }
      }
      break;
    }
  }
}, "BrowserTabList.prototype.handleEvent");

/*
 * If |shouldListen| is true, ensure we've registered a listener with the
 * window mediator. Otherwise, ensure we haven't registered a listener.
 */
BrowserTabList.prototype._listenToMediatorIf = function (shouldListen) {
  if (!shouldListen !== !this._listeningToMediator) {
    let op = shouldListen ? "addListener" : "removeListener";
    Services.wm[op](this);
    this._listeningToMediator = shouldListen;
  }
};

/**
 * nsIWindowMediatorListener implementation.
 *
 * See _onTabClosed for explanation of why we needn't actually tweak any
 * actors or tables here.
 *
 * An nsIWindowMediatorListener's methods get passed all sorts of windows; we
 * only care about the tab containers. Those have 'getBrowser' methods.
 */
BrowserTabList.prototype.onOpenWindow =
DevToolsUtils.makeInfallible(function (window) {
  let handleLoad = DevToolsUtils.makeInfallible(() => {
    /* We don't want any further load events from this window. */
    window.removeEventListener("load", handleLoad);

    if (appShellDOMWindowType(window) !== DebuggerServer.chromeWindowType) {
      return;
    }

    // Listen for future tab activity.
    if (this._listeningForTabOpen) {
      window.addEventListener("TabOpen", this);
      window.addEventListener("TabSelect", this);
      window.addEventListener("TabAttrModified", this);
    }
    if (this._listeningForTabClose) {
      window.addEventListener("TabClose", this);
      window.addEventListener("TabRemotenessChange", this);
    }
    if (this._listeningForTitleChange) {
      window.messageManager.addMessageListener("DOMTitleChanged", this);
    }

    // As explained above, we will not receive a TabOpen event for this
    // document's initial tab, so we must notify our client of the new tab
    // this will have.
    this._notifyListChanged();
  });

  /*
   * You can hardly do anything at all with a XUL window at this point; it
   * doesn't even have its document yet. Wait until its document has
   * loaded, and then see what we've got. This also avoids
   * nsIWindowMediator enumeration from within listeners (bug 873589).
   */
  window = window.QueryInterface(Ci.nsIInterfaceRequestor)
                 .getInterface(Ci.nsIDOMWindow);

  window.addEventListener("load", handleLoad);
}, "BrowserTabList.prototype.onOpenWindow");

BrowserTabList.prototype.onCloseWindow =
DevToolsUtils.makeInfallible(function (window) {
  window = window.QueryInterface(Ci.nsIInterfaceRequestor)
                   .getInterface(Ci.nsIDOMWindow);

  if (appShellDOMWindowType(window) !== DebuggerServer.chromeWindowType) {
    return;
  }

  /*
   * nsIWindowMediator deadlocks if you call its GetEnumerator method from
   * a nsIWindowMediatorListener's onCloseWindow hook (bug 873589), so
   * handle the close in a different tick.
   */
  Services.tm.dispatchToMainThread(DevToolsUtils.makeInfallible(() => {
    /*
     * Scan the entire map for actors representing tabs that were in this
     * top-level window, and exit them.
     */
    for (let [browser, actor] of this._actorByBrowser) {
      /* The browser document of a closed window has no default view. */
      if (!browser.ownerGlobal) {
        this._handleActorClose(actor, browser);
      }
    }
  }, "BrowserTabList.prototype.onCloseWindow's delayed body"));
}, "BrowserTabList.prototype.onCloseWindow");

exports.BrowserTabList = BrowserTabList;

/**
 * Creates a tab actor for handling requests to a single browser frame.
 * Both <xul:browser> and <iframe mozbrowser> are supported.
 * This actor is a shim that connects to a ContentActor in a remote browser process.
 * All RDP packets get forwarded using the message manager.
 *
 * @param connection The main RDP connection.
 * @param browser <xul:browser> or <iframe mozbrowser> element to connect to.
 * @param options
 *        - {Boolean} favicons: true if the form should include the favicon for the tab.
 */
function BrowserTabActor(connection, browser, options = {}) {
  this._conn = connection;
  this._browser = browser;
  this._form = null;
  this.exited = false;
  this.options = options;
}

BrowserTabActor.prototype = {
  async connect() {
    let onDestroy = () => {
      if (this._deferredUpdate) {
        // Reject the update promise if the tab was destroyed while requesting an update
        this._deferredUpdate.reject({
          error: "tabDestroyed",
          message: "Tab destroyed while performing a BrowserTabActor update"
        });
      }
      this.exit();
    };
    let connect = DebuggerServer.connectToChild(this._conn, this._browser, onDestroy);
    let form = await connect;

    this._form = form;
    if (this.options.favicons) {
      this._form.favicon = await this.getFaviconData();
    }

    return this;
  },

  get _tabbrowser() {
    if (this._browser && typeof this._browser.getTabBrowser == "function") {
      return this._browser.getTabBrowser();
    }
    return null;
  },

  get _mm() {
    // Get messageManager from XUL browser (which might be a specialized tunnel for RDM)
    // or else fallback to asking the frameLoader itself.
    return this._browser.messageManager ||
           this._browser.frameLoader.messageManager;
  },

  async getFaviconData() {
    try {
      let { data } = await PlacesUtils.promiseFaviconData(this._form.url);
      return data;
    } catch (e) {
      // Favicon unavailable for this url.
      return null;
    }
  },

  /**
   * @param {Object} options
   *        See BrowserTabActor constructor.
   */
  async update(options = {}) {
    // Update the BrowserTabActor options.
    this.options = options;

    // If the child happens to be crashed/close/detach, it won't have _form set,
    // so only request form update if some code is still listening on the other
    // side.
    if (this.exited) {
      return this.connect();
    }

    let form = await new Promise(resolve => {
      let onFormUpdate = msg => {
        // There may be more than just one content.js (ContentActor) up and running
        if (this._form.actor != msg.json.actor) {
          return;
        }
        this._mm.removeMessageListener("debug:form", onFormUpdate);

        resolve(msg.json);
      };

      this._mm.addMessageListener("debug:form", onFormUpdate);
      this._mm.sendAsyncMessage("debug:form");
    });

    this._form = form;
    if (this.options.favicons) {
      this._form.favicon = await this.getFaviconData();
    }

    return this;
  },

  /**
   * If we don't have a title from the content side because it's a zombie tab, try to find
   * it on the chrome side.
   */
  get title() {
    // On Fennec, we can check the session store data for zombie tabs
    if (this._browser && this._browser.__SS_restore) {
      let sessionStore = this._browser.__SS_data;
      // Get the last selected entry
      let entry = sessionStore.entries[sessionStore.index - 1];
      return entry.title;
    }
    // If contentTitle is empty (e.g. on a not-yet-restored tab), but there is a
    // tabbrowser (i.e. desktop Firefox, but not Fennec), we can use the label
    // as the title.
    if (this._tabbrowser) {
      let tab = this._tabbrowser.getTabForBrowser(this._browser);
      if (tab) {
        return tab.label;
      }
    }
    return "";
  },

  /**
   * If we don't have a url from the content side because it's a zombie tab, try to find
   * it on the chrome side.
   */
  get url() {
    // On Fennec, we can check the session store data for zombie tabs
    if (this._browser && this._browser.__SS_restore) {
      let sessionStore = this._browser.__SS_data;
      // Get the last selected entry
      let entry = sessionStore.entries[sessionStore.index - 1];
      return entry.url;
    }
    return null;
  },

  form() {
    let form = Object.assign({}, this._form);
    // In some cases, the title and url fields might be empty.  Zombie tabs (not yet
    // restored) are a good example.  In such cases, try to look up values for these
    // fields using other data in the parent process.
    if (!form.title) {
      form.title = this.title;
    }
    if (!form.url) {
      form.url = this.url;
    }

    return form;
  },

  exit() {
    this._browser = null;
    this._form = null;
    this.exited = true;
  },
};

exports.BrowserTabActor = BrowserTabActor;

function BrowserAddonList(connection) {
  this._connection = connection;
  this._actorByAddonId = new Map();
  this._onListChanged = null;
}

BrowserAddonList.prototype.getList = function () {
  let deferred = defer();
  AddonManager.getAllAddons((addons) => {
    for (let addon of addons) {
      let actor = this._actorByAddonId.get(addon.id);
      if (!actor) {
        if (addon.isWebExtension) {
          actor = new WebExtensionParentActor(this._connection, addon);
        } else {
          actor = new BrowserAddonActor(this._connection, addon);
        }

        this._actorByAddonId.set(addon.id, actor);
      }
    }

    deferred.resolve([...this._actorByAddonId].map(([_, actor]) => actor));
  });

  return deferred.promise;
};

Object.defineProperty(BrowserAddonList.prototype, "onListChanged", {
  enumerable: true,
  configurable: true,
  get() {
    return this._onListChanged;
  },
  set(v) {
    if (v !== null && typeof v != "function") {
      throw new Error(
        "onListChanged property may only be set to 'null' or a function");
    }
    this._onListChanged = v;
    this._adjustListener();
  }
});

BrowserAddonList.prototype.onInstalled = function (addon) {
  this._notifyListChanged();
  this._adjustListener();
};

BrowserAddonList.prototype.onUninstalled = function (addon) {
  this._actorByAddonId.delete(addon.id);
  this._notifyListChanged();
  this._adjustListener();
};

BrowserAddonList.prototype._notifyListChanged = function () {
  if (this._onListChanged) {
    this._onListChanged();
  }
};

BrowserAddonList.prototype._adjustListener = function () {
  if (this._onListChanged) {
    // As long as the callback exists, we need to listen for changes
    // so we can notify about add-on changes.
    AddonManager.addAddonListener(this);
  } else if (this._actorByAddonId.size === 0) {
    // When the callback does not exist, we only need to keep listening
    // if the actor cache will need adjusting when add-ons change.
    AddonManager.removeAddonListener(this);
  }
};

exports.BrowserAddonList = BrowserAddonList;

exports.register = function (handle) {
  handle.setRootActor(createRootActor);
};

exports.unregister = function (handle) {
  handle.setRootActor(null);
};
PK
!<B1H�Tchrome/devtools/modules/devtools/server/actors/webconsole/content-process-forward.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm", {});
const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", {});

XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
                                   "@mozilla.org/childprocessmessagemanager;1",
                                   "nsIMessageSender");
ChromeUtils.defineModuleGetter(this, "E10SUtils",
                               "resource://gre/modules/E10SUtils.jsm");

/*
 * The message manager has an upper limit on message sizes that it can
 * reliably forward to the parent so we limit the size of console log event
 * messages that we forward here. The web console is local and receives the
 * full console message, but addons subscribed to console event messages
 * in the parent receive the truncated version. Due to fragmentation,
 * messages as small as 1MB have resulted in IPC allocation failures on
 * 32-bit platforms. To limit IPC allocation sizes, console.log messages
 * with arguments with total size > MSG_MGR_CONSOLE_MAX_SIZE (bytes) have
 * their arguments completely truncated. MSG_MGR_CONSOLE_VAR_SIZE is an
 * approximation of how much space (in bytes) a JS non-string variable will
 * require in the manager's implementation. For strings, we use 2 bytes per
 * char. The console message URI and function name are limited to
 * MSG_MGR_CONSOLE_INFO_MAX characters. We don't attempt to calculate
 * the exact amount of space the message manager implementation will require
 * for a given message so this is imperfect.
 */
const MSG_MGR_CONSOLE_MAX_SIZE = 1024 * 1024; // 1MB
const MSG_MGR_CONSOLE_VAR_SIZE = 8;
const MSG_MGR_CONSOLE_INFO_MAX = 1024;

function ContentProcessForward() {
  Services.obs.addObserver(this, "console-api-log-event");
  Services.obs.addObserver(this, "xpcom-shutdown");
  cpmm.addMessageListener("DevTools:StopForwardingContentProcessMessage", this);
}
ContentProcessForward.prototype = {
  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                         Ci.nsISupportsWeakReference]),

  receiveMessage(message) {
    if (message.name == "DevTools:StopForwardingContentProcessMessage") {
      this.uninit();
    }
  },

  observe(subject, topic, data) {
    switch (topic) {
      case "console-api-log-event": {
        let consoleMsg = subject.wrappedJSObject;

        let msgData = {
          ...consoleMsg,
          arguments: [],
          filename: consoleMsg.filename.substring(0, MSG_MGR_CONSOLE_INFO_MAX),
          functionName: consoleMsg.functionName &&
            consoleMsg.functionName.substring(0, MSG_MGR_CONSOLE_INFO_MAX),
          // Prevents cyclic object error when using msgData in sendAsyncMessage
          wrappedJSObject: null,
        };

        // We can't send objects over the message manager, so we sanitize
        // them out, replacing those arguments with "<unavailable>".
        let unavailString = "<unavailable>";
        let unavailStringLength = unavailString.length * 2; // 2-bytes per char

        // When the sum of argument sizes reaches MSG_MGR_CONSOLE_MAX_SIZE,
        // replace all arguments with "<truncated>".
        let totalArgLength = 0;

        // Walk through the arguments, checking the type and size.
        for (let arg of consoleMsg.arguments) {
          if ((typeof arg == "object" || typeof arg == "function") &&
              arg !== null) {
            if (Services.appinfo.remoteType === E10SUtils.EXTENSION_REMOTE_TYPE) {
              // For OOP extensions: we want the developer to be able to see the
              // logs in the Browser Console. When the Addon Toolbox will be more
              // prominent we can revisit.
              try {
                // If the argument is clonable, then send it as-is. If
                // cloning fails, fall back to the unavailable string.
                arg = Cu.cloneInto(arg, {});
              } catch (e) {
                arg = unavailString;
              }
            } else {
              arg = unavailString;
            }
            totalArgLength += unavailStringLength;
          } else if (typeof arg == "string") {
            totalArgLength += arg.length * 2; // 2-bytes per char
          } else {
            totalArgLength += MSG_MGR_CONSOLE_VAR_SIZE;
          }

          if (totalArgLength <= MSG_MGR_CONSOLE_MAX_SIZE) {
            msgData.arguments.push(arg);
          } else {
            // arguments take up too much space
            msgData.arguments = ["<truncated>"];
            break;
          }
        }

        cpmm.sendAsyncMessage("Console:Log", msgData);
        break;
      }

      case "xpcom-shutdown":
        this.uninit();
        break;
    }
  },

  uninit() {
    Services.obs.removeObserver(this, "console-api-log-event");
    Services.obs.removeObserver(this, "xpcom-shutdown");
    cpmm.removeMessageListener("DevTools:StopForwardingContentProcessMessage", this);
  }
};

// loadProcessScript loads in all processes, including the parent,
// in which we don't need any forwarding
if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
  new ContentProcessForward();
}

PK
!<�2
C
CFchrome/devtools/modules/devtools/server/actors/webconsole/listeners.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {Cc, Ci, components} = require("chrome");
const {isWindowIncluded} = require("devtools/shared/layout/utils");
const Services = require("Services");
const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
const {CONSOLE_WORKER_IDS, WebConsoleUtils} = require("devtools/server/actors/webconsole/utils");

XPCOMUtils.defineLazyServiceGetter(this,
                                   "swm",
                                   "@mozilla.org/serviceworkers/manager;1",
                                   "nsIServiceWorkerManager");
loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");

// Process script used to forward console calls from content processes to parent process
const CONTENT_PROCESS_SCRIPT = "resource://devtools/server/actors/webconsole/content-process-forward.js";

// The page errors listener

/**
 * The nsIConsoleService listener. This is used to send all of the console
 * messages (JavaScript, CSS and more) to the remote Web Console instance.
 *
 * @constructor
 * @param nsIDOMWindow [window]
 *        Optional - the window object for which we are created. This is used
 *        for filtering out messages that belong to other windows.
 * @param object listener
 *        The listener object must have one method:
 *        - onConsoleServiceMessage(). This method is invoked with one argument,
 *        the nsIConsoleMessage, whenever a relevant message is received.
 */
function ConsoleServiceListener(window, listener) {
  this.window = window;
  this.listener = listener;
}
exports.ConsoleServiceListener = ConsoleServiceListener;

ConsoleServiceListener.prototype =
{
  QueryInterface: XPCOMUtils.generateQI([Ci.nsIConsoleListener]),

  /**
   * The content window for which we listen to page errors.
   * @type nsIDOMWindow
   */
  window: null,

  /**
   * The listener object which is notified of messages from the console service.
   * @type object
   */
  listener: null,

  /**
   * Initialize the nsIConsoleService listener.
   */
  init: function () {
    Services.console.registerListener(this);
  },

  /**
   * The nsIConsoleService observer. This method takes all the script error
   * messages belonging to the current window and sends them to the remote Web
   * Console instance.
   *
   * @param nsIConsoleMessage message
   *        The message object coming from the nsIConsoleService.
   */
  observe: function (message) {
    if (!this.listener) {
      return;
    }

    if (this.window) {
      if (!(message instanceof Ci.nsIScriptError) ||
          !message.outerWindowID ||
          !this.isCategoryAllowed(message.category)) {
        return;
      }

      let errorWindow = Services.wm.getOuterWindowWithId(message.outerWindowID);
      if (!errorWindow || !isWindowIncluded(this.window, errorWindow)) {
        return;
      }
    }

    this.listener.onConsoleServiceMessage(message);
  },

  /**
   * Check if the given message category is allowed to be tracked or not.
   * We ignore chrome-originating errors as we only care about content.
   *
   * @param string category
   *        The message category you want to check.
   * @return boolean
   *         True if the category is allowed to be logged, false otherwise.
   */
  isCategoryAllowed: function (category) {
    if (!category) {
      return false;
    }

    switch (category) {
      case "XPConnect JavaScript":
      case "component javascript":
      case "chrome javascript":
      case "chrome registration":
      case "XBL":
      case "XBL Prototype Handler":
      case "XBL Content Sink":
      case "xbl javascript":
        return false;
    }

    return true;
  },

  /**
   * Get the cached page errors for the current inner window and its (i)frames.
   *
   * @param boolean [includePrivate=false]
   *        Tells if you want to also retrieve messages coming from private
   *        windows. Defaults to false.
   * @return array
   *         The array of cached messages. Each element is an nsIScriptError or
   *         an nsIConsoleMessage
   */
  getCachedMessages: function (includePrivate = false) {
    let errors = Services.console.getMessageArray() || [];

    // if !this.window, we're in a browser console. Still need to filter
    // private messages.
    if (!this.window) {
      return errors.filter((error) => {
        if (error instanceof Ci.nsIScriptError) {
          if (!includePrivate && error.isFromPrivateWindow) {
            return false;
          }
        }

        return true;
      });
    }

    let ids = WebConsoleUtils.getInnerWindowIDsForFrames(this.window);

    return errors.filter((error) => {
      if (error instanceof Ci.nsIScriptError) {
        if (!includePrivate && error.isFromPrivateWindow) {
          return false;
        }
        if (ids &&
            (!ids.includes(error.innerWindowID) ||
             !this.isCategoryAllowed(error.category))) {
          return false;
        }
      } else if (ids && ids[0]) {
        // If this is not an nsIScriptError and we need to do window-based
        // filtering we skip this message.
        return false;
      }

      return true;
    });
  },

  /**
   * Remove the nsIConsoleService listener.
   */
  destroy: function () {
    Services.console.unregisterListener(this);
    this.listener = this.window = null;
  },
};

// The window.console API observer

/**
 * The window.console API observer. This allows the window.console API messages
 * to be sent to the remote Web Console instance.
 *
 * @constructor
 * @param nsIDOMWindow window
 *        Optional - the window object for which we are created. This is used
 *        for filtering out messages that belong to other windows.
 * @param object owner
 *        The owner object must have the following methods:
 *        - onConsoleAPICall(). This method is invoked with one argument, the
 *        Console API message that comes from the observer service, whenever
 *        a relevant console API call is received.
 * @param object filteringOptions
 *        Optional - The filteringOptions that this listener should listen to:
 *        - addonId: filter console messages based on the addonId.
 */
function ConsoleAPIListener(window, owner, {addonId} = {}) {
  this.window = window;
  this.owner = owner;
  this.addonId = addonId;
}
exports.ConsoleAPIListener = ConsoleAPIListener;

ConsoleAPIListener.prototype =
{
  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),

  /**
   * The content window for which we listen to window.console API calls.
   * @type nsIDOMWindow
   */
  window: null,

  /**
   * The owner object which is notified of window.console API calls. It must
   * have a onConsoleAPICall method which is invoked with one argument: the
   * console API call object that comes from the observer service.
   *
   * @type object
   * @see WebConsoleActor
   */
  owner: null,

  /**
   * The addonId that we listen for. If not null then only messages from this
   * console will be returned.
   */
  addonId: null,

  /**
   * Initialize the window.console API observer.
   */
  init: function () {
    // Note that the observer is process-wide. We will filter the messages as
    // needed, see CAL_observe().
    Services.obs.addObserver(this, "console-api-log-event");
  },

  /**
   * The console API message observer. When messages are received from the
   * observer service we forward them to the remote Web Console instance.
   *
   * @param object message
   *        The message object receives from the observer service.
   * @param string topic
   *        The message topic received from the observer service.
   */
  observe: function (message, topic) {
    if (!this.owner) {
      return;
    }

    // Here, wrappedJSObject is not a security wrapper but a property defined
    // by the XPCOM component which allows us to unwrap the XPCOM interface and
    // access the underlying JSObject.
    let apiMessage = message.wrappedJSObject;

    if (!this.isMessageRelevant(apiMessage)) {
      return;
    }

    this.owner.onConsoleAPICall(apiMessage);
  },

  /**
   * Given a message, return true if this window should show it and false
   * if it should be ignored.
   *
   * @param message
   *        The message from the Storage Service
   * @return bool
   *         Do we care about this message?
   */
  isMessageRelevant: function (message) {
    let workerType = WebConsoleUtils.getWorkerType(message);

    if (this.window && workerType === "ServiceWorker") {
      // For messages from Service Workers, message.ID is the
      // scope, which can be used to determine whether it's controlling
      // a window.
      let scope = message.ID;

      if (!this.window.shouldReportForServiceWorkerScope(scope)) {
        return false;
      }
    }

    if (this.window && !workerType) {
      let msgWindow = Services.wm.getCurrentInnerWindowWithId(message.innerID);
      if (!msgWindow || !isWindowIncluded(this.window, msgWindow)) {
        // Not the same window!
        return false;
      }
    }

    if (this.addonId) {
      // ConsoleAPI.jsm messages contains a consoleID, (and it is currently
      // used in Addon SDK add-ons), the standard 'console' object
      // (which is used in regular webpages and in WebExtensions pages)
      // contains the originAttributes of the source document principal.

      // Filtering based on the originAttributes used by
      // the Console API object.
      if (message.addonId == this.addonId) {
        return true;
      }

      // Filtering based on the old-style consoleID property used by
      // the legacy Console JSM module.
      if (message.consoleID && message.consoleID == `addon/${this.addonId}`) {
        return true;
      }

      return false;
    }

    return true;
  },

  /**
   * Get the cached messages for the current inner window and its (i)frames.
   *
   * @param boolean [includePrivate=false]
   *        Tells if you want to also retrieve messages coming from private
   *        windows. Defaults to false.
   * @return array
   *         The array of cached messages.
   */
  getCachedMessages: function (includePrivate = false) {
    let messages = [];
    let ConsoleAPIStorage = Cc["@mozilla.org/consoleAPI-storage;1"]
                              .getService(Ci.nsIConsoleAPIStorage);

    // if !this.window, we're in a browser console. Retrieve all events
    // for filtering based on privacy.
    if (!this.window) {
      messages = ConsoleAPIStorage.getEvents();
    } else {
      let ids = WebConsoleUtils.getInnerWindowIDsForFrames(this.window);
      ids.forEach((id) => {
        messages = messages.concat(ConsoleAPIStorage.getEvents(id));
      });
    }

    CONSOLE_WORKER_IDS.forEach((id) => {
      messages = messages.concat(ConsoleAPIStorage.getEvents(id));
    });

    messages = messages.filter(msg => {
      return this.isMessageRelevant(msg);
    });

    if (includePrivate) {
      return messages;
    }

    return messages.filter((m) => !m.private);
  },

  /**
   * Destroy the console API listener.
   */
  destroy: function () {
    Services.obs.removeObserver(this, "console-api-log-event");
    this.window = this.owner = null;
  },
};

/**
 * A ReflowObserver that listens for reflow events from the page.
 * Implements nsIReflowObserver.
 *
 * @constructor
 * @param object window
 *        The window for which we need to track reflow.
 * @param object owner
 *        The listener owner which needs to implement:
 *        - onReflowActivity(reflowInfo)
 */

function ConsoleReflowListener(window, listener) {
  this.docshell = window.QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsIWebNavigation)
                         .QueryInterface(Ci.nsIDocShell);
  this.listener = listener;
  this.docshell.addWeakReflowObserver(this);
}

exports.ConsoleReflowListener = ConsoleReflowListener;

ConsoleReflowListener.prototype =
{
  QueryInterface: XPCOMUtils.generateQI([Ci.nsIReflowObserver,
                                         Ci.nsISupportsWeakReference]),
  docshell: null,
  listener: null,

  /**
   * Forward reflow event to listener.
   *
   * @param DOMHighResTimeStamp start
   * @param DOMHighResTimeStamp end
   * @param boolean interruptible
   */
  sendReflow: function (start, end, interruptible) {
    let frame = components.stack.caller.caller;

    let filename = frame ? frame.filename : null;

    if (filename) {
      // Because filename could be of the form "xxx.js -> xxx.js -> xxx.js",
      // we only take the last part.
      filename = filename.split(" ").pop();
    }

    this.listener.onReflowActivity({
      interruptible: interruptible,
      start: start,
      end: end,
      sourceURL: filename,
      sourceLine: frame ? frame.lineNumber : null,
      functionName: frame ? frame.name : null
    });
  },

  /**
   * On uninterruptible reflow
   *
   * @param DOMHighResTimeStamp start
   * @param DOMHighResTimeStamp end
   */
  reflow: function (start, end) {
    this.sendReflow(start, end, false);
  },

  /**
   * On interruptible reflow
   *
   * @param DOMHighResTimeStamp start
   * @param DOMHighResTimeStamp end
   */
  reflowInterruptible: function (start, end) {
    this.sendReflow(start, end, true);
  },

  /**
   * Unregister listener.
   */
  destroy: function () {
    this.docshell.removeWeakReflowObserver(this);
    this.listener = this.docshell = null;
  },
};

/**
 * Forward console message calls from content processes to the parent process.
 * Used by Browser console and toolbox to see messages from all processes.
 *
 * @constructor
 * @param object owner
 *        The listener owner which needs to implement:
 *        - onConsoleAPICall(message)
 */
function ContentProcessListener(listener) {
  this.listener = listener;

  Services.ppmm.addMessageListener("Console:Log", this);
  Services.ppmm.loadProcessScript(CONTENT_PROCESS_SCRIPT, true);
}

exports.ContentProcessListener = ContentProcessListener;

ContentProcessListener.prototype = {
  receiveMessage(message) {
    let logMsg = message.data;
    logMsg.wrappedJSObject = logMsg;
    this.listener.onConsoleAPICall(logMsg);
  },

  destroy() {
    // Tell the content processes to stop listening and forwarding messages
    Services.ppmm.broadcastAsyncMessage("DevTools:StopForwardingContentProcessMessage");

    Services.ppmm.removeMessageListener("Console:Log", this);
    Services.ppmm.removeDelayedProcessScript(CONTENT_PROCESS_SCRIPT);

    this.listener = null;
  }
};

/**
 * Forward `DOMContentLoaded` and `load` events with precise timing
 * of when events happened according to window.performance numbers.
 *
 * @constructor
 * @param object console
 *        The web console actor.
 */
function DocumentEventsListener(console) {
  this.console = console;

  this.onWindowReady = this.onWindowReady.bind(this);
  this.onContentLoaded = this.onContentLoaded.bind(this);
  this.onLoad = this.onLoad.bind(this);
  this.listen();
}

exports.DocumentEventsListener = DocumentEventsListener;

DocumentEventsListener.prototype = {
  listen() {
    EventEmitter.on(this.console.parentActor, "window-ready", this.onWindowReady);
    this.onWindowReady({ window: this.console.window, isTopLevel: true });
  },

  onWindowReady({ window, isTopLevel }) {
    // Ignore iframes
    if (!isTopLevel) {
      return;
    }

    let { readyState } = window.document;
    if (readyState != "interactive" && readyState != "complete") {
      window.addEventListener("DOMContentLoaded", this.onContentLoaded, { once: true });
    } else {
      this.onContentLoaded({ target: window.document });
    }
    if (readyState != "complete") {
      window.addEventListener("load", this.onLoad, { once: true });
    } else {
      this.onLoad({ target: window.document });
    }
  },

  onContentLoaded(event) {
    let window = event.target.defaultView;
    let packet = {
      from: this.console.actorID,
      type: "documentEvent",
      name: "dom-interactive",
      // milliseconds since the UNIX epoch, when the parser finished its work
      // on the main document, that is when its Document.readyState changes to
      // 'interactive' and the corresponding readystatechange event is thrown
      time: window.performance.timing.domInteractive
    };
    this.console.conn.send(packet);
  },

  onLoad(event) {
    let window = event.target.defaultView;
    let packet = {
      from: this.console.actorID,
      type: "documentEvent",
      name: "dom-complete",
      // milliseconds since the UNIX epoch, when the parser finished its work
      // on the main document, that is when its Document.readyState changes to
      // 'complete' and the corresponding readystatechange event is thrown
      time: window.performance.timing.domComplete
    };
    this.console.conn.send(packet);
  },

  destroy() {
    EventEmitter.off(this.console.parentActor, "window-ready", this.onWindowReady);

    this.listener = null;
  }
};

PK
!<�㮰\F\FBchrome/devtools/modules/devtools/server/actors/webconsole/utils.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft= javascript ts=2 et sw=2 tw=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 {Ci, Cu} = require("chrome");

// Note that this is only used in WebConsoleCommands, see $0 and pprint().
if (!isWorker) {
  loader.lazyImporter(this, "VariablesView", "resource://devtools/client/shared/widgets/VariablesView.jsm");
}

const CONSOLE_WORKER_IDS = exports.CONSOLE_WORKER_IDS = [
  "SharedWorker",
  "ServiceWorker",
  "Worker"
];

var WebConsoleUtils = {

  /**
   * Given a message, return one of CONSOLE_WORKER_IDS if it matches
   * one of those.
   *
   * @return string
   */
  getWorkerType: function (message) {
    let id = message ? message.innerID : null;
    return CONSOLE_WORKER_IDS[CONSOLE_WORKER_IDS.indexOf(id)] || null;
  },

  /**
   * Clone an object.
   *
   * @param object object
   *        The object you want cloned.
   * @param boolean recursive
   *        Tells if you want to dig deeper into the object, to clone
   *        recursively.
   * @param function [filter]
   *        Optional, filter function, called for every property. Three
   *        arguments are passed: key, value and object. Return true if the
   *        property should be added to the cloned object. Return false to skip
   *        the property.
   * @return object
   *         The cloned object.
   */
  cloneObject: function (object, recursive, filter) {
    if (typeof object != "object") {
      return object;
    }

    let temp;

    if (Array.isArray(object)) {
      temp = [];
      Array.forEach(object, function (value, index) {
        if (!filter || filter(index, value, object)) {
          temp.push(recursive ? WebConsoleUtils.cloneObject(value) : value);
        }
      });
    } else {
      temp = {};
      for (let key in object) {
        let value = object[key];
        if (object.hasOwnProperty(key) &&
            (!filter || filter(key, value, object))) {
          temp[key] = recursive ? WebConsoleUtils.cloneObject(value) : value;
        }
      }
    }

    return temp;
  },

  /**
   * Gets the ID of the inner window of this DOM window.
   *
   * @param nsIDOMWindow window
   * @return integer
   *         Inner ID for the given window.
   */
  getInnerWindowId: function (window) {
    return window.QueryInterface(Ci.nsIInterfaceRequestor)
             .getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
  },

  /**
   * Recursively gather a list of inner window ids given a
   * top level window.
   *
   * @param nsIDOMWindow window
   * @return Array
   *         list of inner window ids.
   */
  getInnerWindowIDsForFrames: function (window) {
    let innerWindowID = this.getInnerWindowId(window);
    let ids = [innerWindowID];

    if (window.frames) {
      for (let i = 0; i < window.frames.length; i++) {
        let frame = window.frames[i];
        ids = ids.concat(this.getInnerWindowIDsForFrames(frame));
      }
    }

    return ids;
  },

  /**
   * Get the property descriptor for the given object.
   *
   * @param object object
   *        The object that contains the property.
   * @param string prop
   *        The property you want to get the descriptor for.
   * @return object
   *         Property descriptor.
   */
  getPropertyDescriptor: function (object, prop) {
    let desc = null;
    while (object) {
      try {
        if ((desc = Object.getOwnPropertyDescriptor(object, prop))) {
          break;
        }
      } catch (ex) {
        // Native getters throw here. See bug 520882.
        // null throws TypeError.
        if (ex.name != "NS_ERROR_XPC_BAD_CONVERT_JS" &&
            ex.name != "NS_ERROR_XPC_BAD_OP_ON_WN_PROTO" &&
            ex.name != "TypeError") {
          throw ex;
        }
      }

      try {
        object = Object.getPrototypeOf(object);
      } catch (ex) {
        if (ex.name == "TypeError") {
          return desc;
        }
        throw ex;
      }
    }
    return desc;
  },

  /**
   * Create a grip for the given value. If the value is an object,
   * an object wrapper will be created.
   *
   * @param mixed value
   *        The value you want to create a grip for, before sending it to the
   *        client.
   * @param function objectWrapper
   *        If the value is an object then the objectWrapper function is
   *        invoked to give us an object grip. See this.getObjectGrip().
   * @return mixed
   *         The value grip.
   */
  createValueGrip: function (value, objectWrapper) {
    switch (typeof value) {
      case "boolean":
        return value;
      case "string":
        return objectWrapper(value);
      case "number":
        if (value === Infinity) {
          return { type: "Infinity" };
        } else if (value === -Infinity) {
          return { type: "-Infinity" };
        } else if (Number.isNaN(value)) {
          return { type: "NaN" };
        } else if (!value && 1 / value === -Infinity) {
          return { type: "-0" };
        }
        return value;
      case "undefined":
        return { type: "undefined" };
      case "object":
        if (value === null) {
          return { type: "null" };
        }
        // Fall through.
      case "function":
        return objectWrapper(value);
      default:
        console.error("Failed to provide a grip for value of " + typeof value
                      + ": " + value);
        return null;
    }
  },
};

exports.WebConsoleUtils = WebConsoleUtils;

/**
 * WebConsole commands manager.
 *
 * Defines a set of functions /variables ("commands") that are available from
 * the Web Console but not from the web page.
 *
 */
var WebConsoleCommands = {
  _registeredCommands: new Map(),
  _originalCommands: new Map(),

  /**
   * @private
   * Reserved for built-in commands. To register a command from the code of an
   * add-on, see WebConsoleCommands.register instead.
   *
   * @see WebConsoleCommands.register
   */
  _registerOriginal: function (name, command) {
    this.register(name, command);
    this._originalCommands.set(name, this.getCommand(name));
  },

  /**
   * Register a new command.
   * @param {string} name The command name (exemple: "$")
   * @param {(function|object)} command The command to register.
   *  It can be a function so the command is a function (like "$()"),
   *  or it can also be a property descriptor to describe a getter / value (like
   *  "$0").
   *
   *  The command function or the command getter are passed a owner object as
   *  their first parameter (see the example below).
   *
   *  Note that setters don't work currently and "enumerable" and "configurable"
   *  are forced to true.
   *
   * @example
   *
   *   WebConsoleCommands.register("$", function JSTH_$(owner, selector)
   *   {
   *     return owner.window.document.querySelector(selector);
   *   });
   *
   *   WebConsoleCommands.register("$0", {
   *     get: function(owner) {
   *       return owner.makeDebuggeeValue(owner.selectedNode);
   *     }
   *   });
   */
  register: function (name, command) {
    this._registeredCommands.set(name, command);
  },

  /**
   * Unregister a command.
   *
   * If the command being unregister overrode a built-in command,
   * the latter is restored.
   *
   * @param {string} name The name of the command
   */
  unregister: function (name) {
    this._registeredCommands.delete(name);
    if (this._originalCommands.has(name)) {
      this.register(name, this._originalCommands.get(name));
    }
  },

  /**
   * Returns a command by its name.
   *
   * @param {string} name The name of the command.
   *
   * @return {(function|object)} The command.
   */
  getCommand: function (name) {
    return this._registeredCommands.get(name);
  },

  /**
   * Returns true if a command is registered with the given name.
   *
   * @param {string} name The name of the command.
   *
   * @return {boolean} True if the command is registered.
   */
  hasCommand: function (name) {
    return this._registeredCommands.has(name);
  },
};

exports.WebConsoleCommands = WebConsoleCommands;

/*
 * Built-in commands.
  *
  * A list of helper functions used by Firebug can be found here:
  *   http://getfirebug.com/wiki/index.php/Command_Line_API
 */

/**
 * Find a node by ID.
 *
 * @param string id
 *        The ID of the element you want.
 * @return nsIDOMNode or null
 *         The result of calling document.querySelector(selector).
 */
WebConsoleCommands._registerOriginal("$", function (owner, selector) {
  try {
    return owner.window.document.querySelector(selector);
  } catch (err) {
    // Throw an error like `err` but that belongs to `owner.window`.
    throw new owner.window.DOMException(err.message, err.name);
  }
});

/**
 * Find the nodes matching a CSS selector.
 *
 * @param string selector
 *        A string that is passed to window.document.querySelectorAll.
 * @return nsIDOMNodeList
 *         Returns the result of document.querySelectorAll(selector).
 */
WebConsoleCommands._registerOriginal("$$", function (owner, selector) {
  let nodes;
  try {
    nodes = owner.window.document.querySelectorAll(selector);
  } catch (err) {
    // Throw an error like `err` but that belongs to `owner.window`.
    throw new owner.window.DOMException(err.message, err.name);
  }

  // Calling owner.window.Array.from() doesn't work without accessing the
  // wrappedJSObject, so just loop through the results instead.
  let result = new owner.window.Array();
  for (let i = 0; i < nodes.length; i++) {
    result.push(nodes[i]);
  }
  return result;
});

/**
 * Returns the result of the last console input evaluation
 *
 * @return object|undefined
 * Returns last console evaluation or undefined
 */
WebConsoleCommands._registerOriginal("$_", {
  get: function (owner) {
    return owner.consoleActor.getLastConsoleInputEvaluation();
  }
});

/**
 * Runs an xPath query and returns all matched nodes.
 *
 * @param string xPath
 *        xPath search query to execute.
 * @param [optional] nsIDOMNode context
 *        Context to run the xPath query on. Uses window.document if not set.
 * @return array of nsIDOMNode
 */
WebConsoleCommands._registerOriginal("$x", function (owner, xPath, context) {
  let nodes = new owner.window.Array();

  // Not waiving Xrays, since we want the original Document.evaluate function,
  // instead of anything that's been redefined.
  let doc = owner.window.document;
  context = context || doc;

  let results = doc.evaluate(xPath, context, null,
                             owner.window.XPathResult.ANY_TYPE, null);
  let node;
  while ((node = results.iterateNext())) {
    nodes.push(node);
  }

  return nodes;
});

/**
 * Returns the currently selected object in the highlighter.
 *
 * @return Object representing the current selection in the
 *         Inspector, or null if no selection exists.
 */
WebConsoleCommands._registerOriginal("$0", {
  get: function (owner) {
    return owner.makeDebuggeeValue(owner.selectedNode);
  }
});

/**
 * Clears the output of the WebConsole.
 */
WebConsoleCommands._registerOriginal("clear", function (owner) {
  owner.helperResult = {
    type: "clearOutput",
  };
});

/**
 * Clears the input history of the WebConsole.
 */
WebConsoleCommands._registerOriginal("clearHistory", function (owner) {
  owner.helperResult = {
    type: "clearHistory",
  };
});

/**
 * Returns the result of Object.keys(object).
 *
 * @param object object
 *        Object to return the property names from.
 * @return array of strings
 */
WebConsoleCommands._registerOriginal("keys", function (owner, object) {
  // Need to waive Xrays so we can iterate functions and accessor properties
  return Cu.cloneInto(Object.keys(Cu.waiveXrays(object)), owner.window);
});

/**
 * Returns the values of all properties on object.
 *
 * @param object object
 *        Object to display the values from.
 * @return array of string
 */
WebConsoleCommands._registerOriginal("values", function (owner, object) {
  let values = [];
  // Need to waive Xrays so we can iterate functions and accessor properties
  let waived = Cu.waiveXrays(object);
  let names = Object.getOwnPropertyNames(waived);

  for (let name of names) {
    values.push(waived[name]);
  }

  return Cu.cloneInto(values, owner.window);
});

/**
 * Opens a help window in MDN.
 */
WebConsoleCommands._registerOriginal("help", function (owner) {
  owner.helperResult = { type: "help" };
});

/**
 * Change the JS evaluation scope.
 *
 * @param DOMElement|string|window window
 *        The window object to use for eval scope. This can be a string that
 *        is used to perform document.querySelector(), to find the iframe that
 *        you want to cd() to. A DOMElement can be given as well, the
 *        .contentWindow property is used. Lastly, you can directly pass
 *        a window object. If you call cd() with no arguments, the current
 *        eval scope is cleared back to its default (the top window).
 */
WebConsoleCommands._registerOriginal("cd", function (owner, window) {
  if (!window) {
    owner.consoleActor.evalWindow = null;
    owner.helperResult = { type: "cd" };
    return;
  }

  if (typeof window == "string") {
    window = owner.window.document.querySelector(window);
  }
  if (window instanceof Ci.nsIDOMElement && window.contentWindow) {
    window = window.contentWindow;
  }
  if (!(window instanceof Ci.nsIDOMWindow)) {
    owner.helperResult = {
      type: "error",
      message: "cdFunctionInvalidArgument"
    };
    return;
  }

  owner.consoleActor.evalWindow = window;
  owner.helperResult = { type: "cd" };
});

/**
 * Inspects the passed object. This is done by opening the PropertyPanel.
 *
 * @param object object
 *        Object to inspect.
 */
WebConsoleCommands._registerOriginal("inspect", function (owner, object) {
  let dbgObj = owner.makeDebuggeeValue(object);
  let grip = owner.createValueGrip(dbgObj);
  owner.helperResult = {
    type: "inspectObject",
    input: owner.evalInput,
    object: grip,
  };
});

/**
 * Prints object to the output.
 *
 * @param object object
 *        Object to print to the output.
 * @return string
 */
WebConsoleCommands._registerOriginal("pprint", function (owner, object) {
  if (object === null || object === undefined || object === true ||
      object === false) {
    owner.helperResult = {
      type: "error",
      message: "helperFuncUnsupportedTypeError",
    };
    return null;
  }

  owner.helperResult = { rawOutput: true };

  if (typeof object == "function") {
    return object + "\n";
  }

  let output = [];

  let obj = object;
  for (let name in obj) {
    let desc = WebConsoleUtils.getPropertyDescriptor(obj, name) || {};
    if (desc.get || desc.set) {
      // TODO: Bug 842672 - toolkit/ imports modules from browser/.
      let getGrip = VariablesView.getGrip(desc.get);
      let setGrip = VariablesView.getGrip(desc.set);
      let getString = VariablesView.getString(getGrip);
      let setString = VariablesView.getString(setGrip);
      output.push(name + ":", "  get: " + getString, "  set: " + setString);
    } else {
      let valueGrip = VariablesView.getGrip(obj[name]);
      let valueString = VariablesView.getString(valueGrip);
      output.push(name + ": " + valueString);
    }
  }

  return "  " + output.join("\n  ");
});

/**
 * Print the String representation of a value to the output, as-is.
 *
 * @param any value
 *        A value you want to output as a string.
 * @return void
 */
WebConsoleCommands._registerOriginal("print", function (owner, value) {
  owner.helperResult = { rawOutput: true };
  if (typeof value === "symbol") {
    return Symbol.prototype.toString.call(value);
  }
  // Waiving Xrays here allows us to see a closer representation of the
  // underlying object. This may execute arbitrary content code, but that
  // code will run with content privileges, and the result will be rendered
  // inert by coercing it to a String.
  return String(Cu.waiveXrays(value));
});

/**
 * Copy the String representation of a value to the clipboard.
 *
 * @param any value
 *        A value you want to copy as a string.
 * @return void
 */
WebConsoleCommands._registerOriginal("copy", function (owner, value) {
  let payload;
  try {
    if (value instanceof Ci.nsIDOMElement) {
      payload = value.outerHTML;
    } else if (typeof value == "string") {
      payload = value;
    } else {
      payload = JSON.stringify(value, null, "  ");
    }
  } catch (ex) {
    payload = "/* " + ex + " */";
  }
  owner.helperResult = {
    type: "copyValueToClipboard",
    value: payload,
  };
});

/**
 * (Internal only) Add the bindings to |owner.sandbox|.
 * This is intended to be used by the WebConsole actor only.
  *
  * @param object owner
  *        The owning object.
  */
function addWebConsoleCommands(owner) {
  // Not supporting extra commands in workers yet.  This should be possible to
  // add one by one as long as they don't require jsm, Cu, etc.
  let commands = isWorker ? [] : WebConsoleCommands._registeredCommands;
  if (!owner) {
    throw new Error("The owner is required");
  }
  for (let [name, command] of commands) {
    if (typeof command === "function") {
      owner.sandbox[name] = command.bind(undefined, owner);
    } else if (typeof command === "object") {
      let clone = Object.assign({}, command, {
        // We force the enumerability and the configurability (so the
        // WebConsoleActor can reconfigure the property).
        enumerable: true,
        configurable: true
      });

      if (typeof command.get === "function") {
        clone.get = command.get.bind(undefined, owner);
      }
      if (typeof command.set === "function") {
        clone.set = command.set.bind(undefined, owner);
      }

      Object.defineProperty(owner.sandbox, name, clone);
    }
  }
}

exports.addWebConsoleCommands = addWebConsoleCommands;
PK
!<�X����Mchrome/devtools/modules/devtools/server/actors/webconsole/worker-listeners.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft= javascript ts=2 et sw=2 tw=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/. */

/* global setConsoleEventHandler, retrieveConsoleEvents */

"use strict";

// This file is loaded on the server side for worker debugging.
// Since the server is running in the worker thread, it doesn't
// have access to Services / Components but the listeners defined here
// are imported by webconsole-utils and used for the webconsole actor.

function ConsoleAPIListener(window, owner, consoleID) {
  this.window = window;
  this.owner = owner;
  this.consoleID = consoleID;
  this.observe = this.observe.bind(this);
}

ConsoleAPIListener.prototype = {
  init: function () {
    setConsoleEventHandler(this.observe);
  },
  destroy: function () {
    setConsoleEventHandler(null);
  },
  observe: function (message) {
    this.owner.onConsoleAPICall(message.wrappedJSObject);
  },
  getCachedMessages: function () {
    return retrieveConsoleEvents();
  }
};

exports.ConsoleAPIListener = ConsoleAPIListener;
PK
!<���w�.�.<chrome/devtools/modules/devtools/server/actors/webconsole.js/* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */
/* vim: set ts=2 et sw=2 tw=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";

/* global XPCNativeWrapper */

const Services = require("Services");
const { Cc, Ci, Cu } = require("chrome");
const { DebuggerServer, ActorPool } = require("devtools/server/main");
const { ThreadActor } = require("devtools/server/actors/thread");
const { ObjectActor, LongStringActor, createValueGrip, stringIsLong } = require("devtools/server/actors/object");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const ErrorDocs = require("devtools/server/actors/errordocs");

loader.lazyRequireGetter(this, "NetworkMonitor", "devtools/shared/webconsole/network-monitor", true);
loader.lazyRequireGetter(this, "NetworkMonitorChild", "devtools/shared/webconsole/network-monitor", true);
loader.lazyRequireGetter(this, "ConsoleProgressListener", "devtools/shared/webconsole/network-monitor", true);
loader.lazyRequireGetter(this, "StackTraceCollector", "devtools/shared/webconsole/network-monitor", true);
loader.lazyRequireGetter(this, "ServerLoggingListener", "devtools/shared/webconsole/server-logger", true);
loader.lazyRequireGetter(this, "JSPropertyProvider", "devtools/shared/webconsole/js-property-provider", true);
loader.lazyRequireGetter(this, "Parser", "resource://devtools/shared/Parser.jsm", true);
loader.lazyRequireGetter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm", true);
loader.lazyRequireGetter(this, "addWebConsoleCommands", "devtools/server/actors/webconsole/utils", true);
loader.lazyRequireGetter(this, "CONSOLE_WORKER_IDS", "devtools/server/actors/webconsole/utils", true);
loader.lazyRequireGetter(this, "WebConsoleUtils", "devtools/server/actors/webconsole/utils", true);
loader.lazyRequireGetter(this, "EnvironmentActor", "devtools/server/actors/environment", true);
loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");

// Overwrite implemented listeners for workers so that we don't attempt
// to load an unsupported module.
if (isWorker) {
  loader.lazyRequireGetter(this, "ConsoleAPIListener", "devtools/server/actors/webconsole/worker-listeners", true);
  loader.lazyRequireGetter(this, "ConsoleServiceListener", "devtools/server/actors/webconsole/worker-listeners", true);
} else {
  loader.lazyRequireGetter(this, "ConsoleAPIListener", "devtools/server/actors/webconsole/listeners", true);
  loader.lazyRequireGetter(this, "ConsoleServiceListener", "devtools/server/actors/webconsole/listeners", true);
  loader.lazyRequireGetter(this, "ConsoleReflowListener", "devtools/server/actors/webconsole/listeners", true);
  loader.lazyRequireGetter(this, "ContentProcessListener", "devtools/server/actors/webconsole/listeners", true);
  loader.lazyRequireGetter(this, "DocumentEventsListener", "devtools/server/actors/webconsole/listeners", true);
}

function isObject(value) {
  return Object(value) === value;
}

/**
 * The WebConsoleActor implements capabilities needed for the Web Console
 * feature.
 *
 * @constructor
 * @param object connection
 *        The connection to the client, DebuggerServerConnection.
 * @param object [parentActor]
 *        Optional, the parent actor.
 */
function WebConsoleActor(connection, parentActor) {
  this.conn = connection;
  this.parentActor = parentActor;

  this._actorPool = new ActorPool(this.conn);
  this.conn.addActorPool(this._actorPool);

  this._prefs = {};

  this.dbg = this.parentActor.makeDebugger();

  this._netEvents = new Map();
  this._networkEventActorsByURL = new Map();
  this._gripDepth = 0;
  this._listeners = new Set();
  this._lastConsoleInputEvaluation = undefined;

  this.objectGrip = this.objectGrip.bind(this);
  this._onWillNavigate = this._onWillNavigate.bind(this);
  this._onChangedToplevelDocument = this._onChangedToplevelDocument.bind(this);
  EventEmitter.on(this.parentActor, "changed-toplevel-document",
            this._onChangedToplevelDocument);
  this._onObserverNotification = this._onObserverNotification.bind(this);
  if (this.parentActor.isRootActor) {
    Services.obs.addObserver(this._onObserverNotification,
                             "last-pb-context-exited");
  }

  this.traits = {
    evaluateJSAsync: true,
    transferredResponseSize: true,
    selectedObjectActor: true, // 44+
  };
}

WebConsoleActor.prototype =
{
  /**
   * Debugger instance.
   *
   * @see jsdebugger.jsm
   */
  dbg: null,

  /**
   * This is used by the ObjectActor to keep track of the depth of grip() calls.
   * @private
   * @type number
   */
  _gripDepth: null,

  /**
   * Actor pool for all of the actors we send to the client.
   * @private
   * @type object
   * @see ActorPool
   */
  _actorPool: null,

  /**
   * Web Console-related preferences.
   * @private
   * @type object
   */
  _prefs: null,

  /**
   * Holds a map between nsIChannel objects and NetworkEventActors for requests
   * created with sendHTTPRequest or found via the network listener.
   *
   * @private
   * @type Map
   */
  _netEvents: null,

  /**
   * Holds a map from URL to NetworkEventActors for requests noticed by the network
   * listener.  Requests are added when they start, so the actor might not yet have all
   * data for the request until it has completed.
   *
   * @private
   * @type Map
   */
  _networkEventActorsByURL: null,

  /**
   * Holds a set of all currently registered listeners.
   *
   * @private
   * @type Set
   */
  _listeners: null,

  /**
   * The debugger server connection instance.
   * @type object
   */
  conn: null,

  /**
   * List of supported features by the console actor.
   * @type object
   */
  traits: null,

  /**
   * The window or sandbox we work with.
   * Note that even if it is named `window` it refers to the current
   * global we are debugging, which can be a Sandbox for addons
   * or browser content toolbox.
   *
   * @type nsIDOMWindow or Sandbox
   */
  get window() {
    if (this.parentActor.isRootActor) {
      return this._getWindowForBrowserConsole();
    }
    return this.parentActor.window;
  },

  /**
   * Get a window to use for the browser console.
   *
   * @private
   * @return nsIDOMWindow
   *         The window to use, or null if no window could be found.
   */
  _getWindowForBrowserConsole: function () {
    // Check if our last used chrome window is still live.
    let window = this._lastChromeWindow && this._lastChromeWindow.get();
    // If not, look for a new one.
    if (!window || window.closed) {
      window = this.parentActor.window;
      if (!window) {
        // Try to find the Browser Console window to use instead.
        window = Services.wm.getMostRecentWindow("devtools:webconsole");
        // We prefer the normal chrome window over the console window,
        // so we'll look for those windows in order to replace our reference.
        let onChromeWindowOpened = () => {
          // We'll look for this window when someone next requests window()
          Services.obs.removeObserver(onChromeWindowOpened, "domwindowopened");
          this._lastChromeWindow = null;
        };
        Services.obs.addObserver(onChromeWindowOpened, "domwindowopened");
      }

      this._handleNewWindow(window);
    }

    return window;
  },

  /**
   * Store a newly found window on the actor to be used in the future.
   *
   * @private
   * @param nsIDOMWindow window
   *        The window to store on the actor (can be null).
   */
  _handleNewWindow: function (window) {
    if (window) {
      if (this._hadChromeWindow) {
        Services.console.logStringMessage("Webconsole context has changed");
      }
      this._lastChromeWindow = Cu.getWeakReference(window);
      this._hadChromeWindow = true;
    } else {
      this._lastChromeWindow = null;
    }
  },

  /**
   * Whether we've been using a window before.
   *
   * @private
   * @type boolean
   */
  _hadChromeWindow: false,

  /**
   * A weak reference to the last chrome window we used to work with.
   *
   * @private
   * @type nsIWeakReference
   */
  _lastChromeWindow: null,

  // The evalWindow is used at the scope for JS evaluation.
  _evalWindow: null,
  get evalWindow() {
    return this._evalWindow || this.window;
  },

  set evalWindow(window) {
    this._evalWindow = window;

    if (!this._progressListenerActive) {
      EventEmitter.on(this.parentActor, "will-navigate", this._onWillNavigate);
      this._progressListenerActive = true;
    }
  },

  /**
   * Flag used to track if we are listening for events from the progress
   * listener of the tab actor. We use the progress listener to clear
   * this.evalWindow on page navigation.
   *
   * @private
   * @type boolean
   */
  _progressListenerActive: false,

  /**
   * The ConsoleServiceListener instance.
   * @type object
   */
  consoleServiceListener: null,

  /**
   * The ConsoleAPIListener instance.
   */
  consoleAPIListener: null,

  /**
   * The NetworkMonitor instance.
   */
  networkMonitor: null,

  /**
   * The NetworkMonitor instance living in the same (child) process.
   */
  networkMonitorChild: null,

  /**
   * The ConsoleProgressListener instance.
   */
  consoleProgressListener: null,

  /**
   * The ConsoleReflowListener instance.
   */
  consoleReflowListener: null,

  /**
   * The Web Console Commands names cache.
   * @private
   * @type array
   */
  _webConsoleCommandsCache: null,

  actorPrefix: "console",

  get globalDebugObject() {
    return this.parentActor.threadActor.globalDebugObject;
  },

  grip: function () {
    return { actor: this.actorID };
  },

  hasNativeConsoleAPI: function (window) {
    if (isWorker) {
      // Can't use XPCNativeWrapper as a way to check for console API in workers
      return true;
    }

    let isNative = false;
    try {
      // We are very explicitly examining the "console" property of
      // the non-Xrayed object here.
      let console = window.wrappedJSObject.console;
      isNative = new XPCNativeWrapper(console).IS_NATIVE_CONSOLE;
    } catch (ex) {
      // ignored
    }
    return isNative;
  },

  _findProtoChain: ThreadActor.prototype._findProtoChain,
  _removeFromProtoChain: ThreadActor.prototype._removeFromProtoChain,

  /**
   * Destroy the current WebConsoleActor instance.
   */
  destroy() {
    if (this.consoleServiceListener) {
      this.consoleServiceListener.destroy();
      this.consoleServiceListener = null;
    }
    if (this.consoleAPIListener) {
      this.consoleAPIListener.destroy();
      this.consoleAPIListener = null;
    }
    if (this.networkMonitor) {
      this.networkMonitor.destroy();
      this.networkMonitor = null;
    }
    if (this.networkMonitorChild) {
      this.networkMonitorChild.destroy();
      this.networkMonitorChild = null;
    }
    if (this.stackTraceCollector) {
      this.stackTraceCollector.destroy();
      this.stackTraceCollector = null;
    }
    if (this.consoleProgressListener) {
      this.consoleProgressListener.destroy();
      this.consoleProgressListener = null;
    }
    if (this.consoleReflowListener) {
      this.consoleReflowListener.destroy();
      this.consoleReflowListener = null;
    }
    if (this.serverLoggingListener) {
      this.serverLoggingListener.destroy();
      this.serverLoggingListener = null;
    }
    if (this.contentProcessListener) {
      this.contentProcessListener.destroy();
      this.contentProcessListener = null;
    }

    EventEmitter.off(this.parentActor, "changed-toplevel-document",
               this._onChangedToplevelDocument);

    this.conn.removeActorPool(this._actorPool);

    if (this.parentActor.isRootActor) {
      Services.obs.removeObserver(this._onObserverNotification,
                                  "last-pb-context-exited");
    }

    this._actorPool = null;
    this._webConsoleCommandsCache = null;
    this._lastConsoleInputEvaluation = null;
    this._evalWindow = null;
    this._netEvents.clear();
    this.dbg.enabled = false;
    this.dbg = null;
    this.conn = null;
  },

  /**
   * Create and return an environment actor that corresponds to the provided
   * Debugger.Environment. This is a straightforward clone of the ThreadActor's
   * method except that it stores the environment actor in the web console
   * actor's pool.
   *
   * @param Debugger.Environment environment
   *        The lexical environment we want to extract.
   * @return The EnvironmentActor for |environment| or |undefined| for host
   *         functions or functions scoped to a non-debuggee global.
   */
  createEnvironmentActor: function (environment) {
    if (!environment) {
      return undefined;
    }

    if (environment.actor) {
      return environment.actor;
    }

    let actor = new EnvironmentActor(environment, this);
    this._actorPool.addActor(actor);
    environment.actor = actor;

    return actor;
  },

  /**
   * Create a grip for the given value.
   *
   * @param mixed value
   * @return object
   */
  createValueGrip: function (value) {
    return createValueGrip(value, this._actorPool, this.objectGrip);
  },

  /**
   * Make a debuggee value for the given value.
   *
   * @param mixed value
   *        The value you want to get a debuggee value for.
   * @param boolean useObjectGlobal
   *        If |true| the object global is determined and added as a debuggee,
   *        otherwise |this.window| is used when makeDebuggeeValue() is invoked.
   * @return object
   *         Debuggee value for |value|.
   */
  makeDebuggeeValue: function (value, useObjectGlobal) {
    if (useObjectGlobal && isObject(value)) {
      try {
        let global = Cu.getGlobalForObject(value);
        let dbgGlobal = this.dbg.makeGlobalObjectReference(global);
        return dbgGlobal.makeDebuggeeValue(value);
      } catch (ex) {
        // The above can throw an exception if value is not an actual object
        // or 'Object in compartment marked as invisible to Debugger'
      }
    }
    let dbgGlobal = this.dbg.makeGlobalObjectReference(this.window);
    return dbgGlobal.makeDebuggeeValue(value);
  },

  /**
   * Create a grip for the given object.
   *
   * @param object object
   *        The object you want.
   * @param object pool
   *        An ActorPool where the new actor instance is added.
   * @param object
   *        The object grip.
   */
  objectGrip: function (object, pool) {
    let actor = new ObjectActor(object, {
      getGripDepth: () => this._gripDepth,
      incrementGripDepth: () => this._gripDepth++,
      decrementGripDepth: () => this._gripDepth--,
      createValueGrip: v => this.createValueGrip(v),
      sources: () => DevToolsUtils.reportException("WebConsoleActor",
        Error("sources not yet implemented")),
      createEnvironmentActor: (env) => this.createEnvironmentActor(env),
      getGlobalDebugObject: () => this.globalDebugObject
    });
    pool.addActor(actor);
    return actor.grip();
  },

  /**
   * Create a grip for the given string.
   *
   * @param string string
   *        The string you want to create the grip for.
   * @param object pool
   *        An ActorPool where the new actor instance is added.
   * @return object
   *         A LongStringActor object that wraps the given string.
   */
  longStringGrip: function (string, pool) {
    let actor = new LongStringActor(string);
    pool.addActor(actor);
    return actor.grip();
  },

  /**
   * Create a long string grip if needed for the given string.
   *
   * @private
   * @param string string
   *        The string you want to create a long string grip for.
   * @return string|object
   *         A string is returned if |string| is not a long string.
   *         A LongStringActor grip is returned if |string| is a long string.
   */
  _createStringGrip: function (string) {
    if (string && stringIsLong(string)) {
      return this.longStringGrip(string, this._actorPool);
    }
    return string;
  },

  /**
   * Get an object actor by its ID.
   *
   * @param string actorID
   * @return object
   */
  getActorByID: function (actorID) {
    return this._actorPool.get(actorID);
  },

  /**
   * Release an actor.
   *
   * @param object actor
   *        The actor instance you want to release.
   */
  releaseActor: function (actor) {
    this._actorPool.removeActor(actor);
  },

  /**
   * Returns the latest web console input evaluation.
   * This is undefined if no evaluations have been completed.
   *
   * @return object
   */
  getLastConsoleInputEvaluation: function () {
    return this._lastConsoleInputEvaluation;
  },

  /**
   * This helper is used by the WebExtensionInspectedWindowActor to
   * inspect an object in the developer toolbox.
   */
  inspectObject(dbgObj, inspectFromAnnotation) {
    this.conn.sendActorEvent(this.actorID, "inspectObject", {
      objectActor: this.createValueGrip(dbgObj),
      inspectFromAnnotation,
    });
  },

  // Request handlers for known packet types.

  /**
   * Handler for the "startListeners" request.
   *
   * @param object request
   *        The JSON request object received from the Web Console client.
   * @return object
   *         The response object which holds the startedListeners array.
   */
  onStartListeners: function (request) {
    let startedListeners = [];
    let window = !this.parentActor.isRootActor ? this.window : null;
    let messageManager = null;

    // Check if the actor is running in a child process (but only if
    // Services.appinfo exists, to prevent onStartListeners to fail
    // when the target is a Worker).
    let processBoundary = Services.appinfo && (
      Services.appinfo.processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT
    );

    // Retrieve a message manager from the parent actor if this actor is
    // not currently running in the main process.
    if (processBoundary) {
      messageManager = this.parentActor.messageManager;
    }

    while (request.listeners.length > 0) {
      let listener = request.listeners.shift();
      switch (listener) {
        case "PageError":
          // Workers don't support this message type yet
          if (isWorker) {
            break;
          }
          if (!this.consoleServiceListener) {
            this.consoleServiceListener =
              new ConsoleServiceListener(window, this);
            this.consoleServiceListener.init();
          }
          startedListeners.push(listener);
          break;
        case "ConsoleAPI":
          if (!this.consoleAPIListener) {
            // Create the consoleAPIListener
            // (and apply the filtering options defined in the parent actor).
            this.consoleAPIListener = new ConsoleAPIListener(
              window, this, this.parentActor.consoleAPIListenerOptions);
            this.consoleAPIListener.init();
          }
          startedListeners.push(listener);
          break;
        case "NetworkActivity":
          // Workers don't support this message type
          if (isWorker) {
            break;
          }
          if (!this.networkMonitor) {
            // Create a StackTraceCollector that's going to be shared both by
            // the NetworkMonitorChild (getting messages about requests from
            // parent) and by the NetworkMonitor that directly watches service
            // workers requests.
            this.stackTraceCollector = new StackTraceCollector({ window });
            this.stackTraceCollector.init();

            if (messageManager && processBoundary) {
              // Start a network monitor in the parent process to listen to
              // most requests than happen in parent
              this.networkMonitor =
                new NetworkMonitorChild(this.parentActor.outerWindowID,
                                        messageManager, this.conn, this);
              this.networkMonitor.init();
              // Spawn also one in the child to listen to service workers
              this.networkMonitorChild = new NetworkMonitor({ window }, this);
              this.networkMonitorChild.init();
            } else {
              this.networkMonitor = new NetworkMonitor({ window }, this);
              this.networkMonitor.init();
            }
          }
          startedListeners.push(listener);
          break;
        case "FileActivity":
          // Workers don't support this message type
          if (isWorker) {
            break;
          }
          if (this.window instanceof Ci.nsIDOMWindow) {
            if (!this.consoleProgressListener) {
              this.consoleProgressListener =
                new ConsoleProgressListener(this.window, this);
            }
            this.consoleProgressListener.startMonitor(this.consoleProgressListener
                                                      .MONITOR_FILE_ACTIVITY);
            startedListeners.push(listener);
          }
          break;
        case "ReflowActivity":
          // Workers don't support this message type
          if (isWorker) {
            break;
          }
          if (!this.consoleReflowListener) {
            this.consoleReflowListener =
              new ConsoleReflowListener(this.window, this);
          }
          startedListeners.push(listener);
          break;
        case "ServerLogging":
          // Workers don't support this message type
          if (isWorker) {
            break;
          }
          if (!this.serverLoggingListener) {
            this.serverLoggingListener =
              new ServerLoggingListener(this.window, this);
          }
          startedListeners.push(listener);
          break;
        case "ContentProcessMessages":
          // Workers don't support this message type
          if (isWorker) {
            break;
          }
          if (!this.contentProcessListener) {
            this.contentProcessListener = new ContentProcessListener(this);
          }
          startedListeners.push(listener);
          break;
        case "DocumentEvents":
          // Workers don't support this message type
          if (isWorker) {
            break;
          }
          if (!this.documentEventsListener) {
            this.documentEventsListener = new DocumentEventsListener(this);
          }
          startedListeners.push(listener);
          break;
      }
    }

    // Update the live list of running listeners
    startedListeners.forEach(this._listeners.add, this._listeners);

    return {
      startedListeners: startedListeners,
      nativeConsoleAPI: this.hasNativeConsoleAPI(this.window),
      traits: this.traits,
    };
  },

  /**
   * Handler for the "stopListeners" request.
   *
   * @param object request
   *        The JSON request object received from the Web Console client.
   * @return object
   *         The response packet to send to the client: holds the
   *         stoppedListeners array.
   */
  onStopListeners: function (request) {
    let stoppedListeners = [];

    // If no specific listeners are requested to be detached, we stop all
    // listeners.
    let toDetach = request.listeners ||
      ["PageError", "ConsoleAPI", "NetworkActivity",
       "FileActivity", "ServerLogging", "ContentProcessMessages"];

    while (toDetach.length > 0) {
      let listener = toDetach.shift();
      switch (listener) {
        case "PageError":
          if (this.consoleServiceListener) {
            this.consoleServiceListener.destroy();
            this.consoleServiceListener = null;
          }
          stoppedListeners.push(listener);
          break;
        case "ConsoleAPI":
          if (this.consoleAPIListener) {
            this.consoleAPIListener.destroy();
            this.consoleAPIListener = null;
          }
          stoppedListeners.push(listener);
          break;
        case "NetworkActivity":
          if (this.networkMonitor) {
            this.networkMonitor.destroy();
            this.networkMonitor = null;
          }
          if (this.networkMonitorChild) {
            this.networkMonitorChild.destroy();
            this.networkMonitorChild = null;
          }
          if (this.stackTraceCollector) {
            this.stackTraceCollector.destroy();
            this.stackTraceCollector = null;
          }
          stoppedListeners.push(listener);
          break;
        case "FileActivity":
          if (this.consoleProgressListener) {
            this.consoleProgressListener.stopMonitor(this.consoleProgressListener
                                                     .MONITOR_FILE_ACTIVITY);
            this.consoleProgressListener = null;
          }
          stoppedListeners.push(listener);
          break;
        case "ReflowActivity":
          if (this.consoleReflowListener) {
            this.consoleReflowListener.destroy();
            this.consoleReflowListener = null;
          }
          stoppedListeners.push(listener);
          break;
        case "ServerLogging":
          if (this.serverLoggingListener) {
            this.serverLoggingListener.destroy();
            this.serverLoggingListener = null;
          }
          stoppedListeners.push(listener);
          break;
        case "ContentProcessMessages":
          if (this.contentProcessListener) {
            this.contentProcessListener.destroy();
            this.contentProcessListener = null;
          }
          stoppedListeners.push(listener);
          break;
        case "DocumentEvents":
          if (this.documentEventsListener) {
            this.documentEventsListener.destroy();
            this.documentEventsListener = null;
          }
          stoppedListeners.push(listener);
          break;
      }
    }

    // Update the live list of running listeners
    stoppedListeners.forEach(this._listeners.delete, this._listeners);

    return { stoppedListeners: stoppedListeners };
  },

  /**
   * Handler for the "getCachedMessages" request. This method sends the cached
   * error messages and the window.console API calls to the client.
   *
   * @param object request
   *        The JSON request object received from the Web Console client.
   * @return object
   *         The response packet to send to the client: it holds the cached
   *         messages array.
   */
  onGetCachedMessages: function (request) {
    let types = request.messageTypes;
    if (!types) {
      return {
        error: "missingParameter",
        message: "The messageTypes parameter is missing.",
      };
    }

    let messages = [];

    while (types.length > 0) {
      let type = types.shift();
      switch (type) {
        case "ConsoleAPI": {
          if (!this.consoleAPIListener) {
            break;
          }

          // See `window` definition. It isn't always a DOM Window.
          let winStartTime = this.window && this.window.performance ?
            this.window.performance.timing.navigationStart : 0;

          let cache = this.consoleAPIListener
                      .getCachedMessages(!this.parentActor.isRootActor);
          cache.forEach((cachedMessage) => {
            // Filter out messages that came from a ServiceWorker but happened
            // before the page was requested.
            if (cachedMessage.innerID === "ServiceWorker" &&
                winStartTime > cachedMessage.timeStamp) {
              return;
            }

            let message = this.prepareConsoleMessageForRemote(cachedMessage);
            message._type = type;
            messages.push(message);
          });
          break;
        }
        case "PageError": {
          if (!this.consoleServiceListener) {
            break;
          }
          let cache = this.consoleServiceListener
                      .getCachedMessages(!this.parentActor.isRootActor);
          cache.forEach((cachedMessage) => {
            let message = null;
            if (cachedMessage instanceof Ci.nsIScriptError) {
              message = this.preparePageErrorForRemote(cachedMessage);
              message._type = type;
            } else {
              message = {
                _type: "LogMessage",
                message: this._createStringGrip(cachedMessage.message),
                timeStamp: cachedMessage.timeStamp,
              };
            }
            messages.push(message);
          });
          break;
        }
      }
    }

    return {
      from: this.actorID,
      messages: messages,
    };
  },

  /**
   * Handler for the "evaluateJSAsync" request. This method evaluates the given
   * JavaScript string and sends back a packet with a unique ID.
   * The result will be returned later as an unsolicited `evaluationResult`,
   * that can be associated back to this request via the `resultID` field.
   *
   * @param object request
   *        The JSON request object received from the Web Console client.
   * @return object
   *         The response packet to send to with the unique id in the
   *         `resultID` field.
   */
  onEvaluateJSAsync: function (request) {
    // We want to be able to run console commands without waiting
    // for the first to return (see Bug 1088861).

    // First, send a response packet with the id only.
    let resultID = Date.now();
    this.conn.send({
      from: this.actorID,
      resultID: resultID
    });

    // Then, execute the script that may pause.
    let response = this.onEvaluateJS(request);
    response.resultID = resultID;

    // Finally, send an unsolicited evaluationResult packet with
    // the normal return value
    this.conn.sendActorEvent(this.actorID, "evaluationResult", response);
  },

  /**
   * Handler for the "evaluateJS" request. This method evaluates the given
   * JavaScript string and sends back the result.
   *
   * @param object request
   *        The JSON request object received from the Web Console client.
   * @return object
   *         The evaluation response packet.
   */
  onEvaluateJS: function (request) {
    let input = request.text;
    let timestamp = Date.now();

    let evalOptions = {
      bindObjectActor: request.bindObjectActor,
      frameActor: request.frameActor,
      url: request.url,
      selectedNodeActor: request.selectedNodeActor,
      selectedObjectActor: request.selectedObjectActor,
    };

    let evalInfo = this.evalWithDebugger(input, evalOptions);
    let evalResult = evalInfo.result;
    let helperResult = evalInfo.helperResult;

    let result, errorDocURL, errorMessage, errorNotes = null, errorGrip = null,
      frame = null;
    if (evalResult) {
      if ("return" in evalResult) {
        result = evalResult.return;
      } else if ("yield" in evalResult) {
        result = evalResult.yield;
      } else if ("throw" in evalResult) {
        let error = evalResult.throw;

        errorGrip = this.createValueGrip(error);

        errorMessage = String(error);
        if (typeof error === "object" && error !== null) {
          try {
            errorMessage = DevToolsUtils.callPropertyOnObject(error, "toString");
          } catch (e) {
            // If the debuggee is not allowed to access the "toString" property
            // of the error object, calling this property from the debuggee's
            // compartment will fail. The debugger should show the error object
            // as it is seen by the debuggee, so this behavior is correct.
            //
            // Unfortunately, we have at least one test that assumes calling the
            // "toString" property of an error object will succeed if the
            // debugger is allowed to access it, regardless of whether the
            // debuggee is allowed to access it or not.
            //
            // To accomodate these tests, if calling the "toString" property
            // from the debuggee compartment fails, we rewrap the error object
            // in the debugger's compartment, and then call the "toString"
            // property from there.
            if (typeof error.unsafeDereference === "function") {
              errorMessage = error.unsafeDereference().toString();
            }
          }
        }

        // It is possible that we won't have permission to unwrap an
        // object and retrieve its errorMessageName.
        try {
          errorDocURL = ErrorDocs.GetURL(error);
        } catch (ex) {
          // ignored
        }

        try {
          let line = error.errorLineNumber;
          let column = error.errorColumnNumber;

          if (typeof line === "number" && typeof column === "number") {
            // Set frame only if we have line/column numbers.
            frame = {
              source: "debugger eval code",
              line,
              column
            };
          }
        } catch (ex) {
          // ignored
        }

        try {
          let notes = error.errorNotes;
          if (notes && notes.length) {
            errorNotes = [];
            for (let note of notes) {
              errorNotes.push({
                messageBody: this._createStringGrip(note.message),
                frame: {
                  source: note.fileName,
                  line: note.lineNumber,
                  column: note.columnNumber,
                }
              });
            }
          }
        } catch (ex) {
          // ignored
        }
      }
    }

    // If a value is encountered that the debugger server doesn't support yet,
    // the console should remain functional.
    let resultGrip;
    try {
      resultGrip = this.createValueGrip(result);
    } catch (e) {
      errorMessage = e;
    }

    this._lastConsoleInputEvaluation = result;

    return {
      from: this.actorID,
      input: input,
      result: resultGrip,
      timestamp: timestamp,
      exception: errorGrip,
      exceptionMessage: this._createStringGrip(errorMessage),
      exceptionDocURL: errorDocURL,
      frame,
      helperResult: helperResult,
      notes: errorNotes,
    };
  },

  /**
   * The Autocomplete request handler.
   *
   * @param object request
   *        The request message - what input to autocomplete.
   * @return object
   *         The response message - matched properties.
   */
  onAutocomplete: function (request) {
    let frameActorId = request.frameActor;
    let dbgObject = null;
    let environment = null;
    let hadDebuggee = false;

    // This is the case of the paused debugger
    if (frameActorId) {
      let frameActor = this.conn.getActor(frameActorId);
      try {
        // Need to try/catch since accessing frame.environment
        // can throw "Debugger.Frame is not live"
        let frame = frameActor.frame;
        environment = frame.environment;
      } catch (e) {
        DevToolsUtils.reportException("onAutocomplete",
          Error("The frame actor was not found: " + frameActorId));
      }
    } else {
      // This is the general case (non-paused debugger)
      hadDebuggee = this.dbg.hasDebuggee(this.evalWindow);
      dbgObject = this.dbg.addDebuggee(this.evalWindow);
    }

    let result = JSPropertyProvider(dbgObject, environment, request.text,
                                    request.cursor, frameActorId) || {};

    if (!hadDebuggee && dbgObject) {
      this.dbg.removeDebuggee(this.evalWindow);
    }

    let matches = result.matches || [];
    let reqText = request.text.substr(0, request.cursor);

    // We consider '$' as alphanumerc because it is used in the names of some
    // helper functions.
    let lastNonAlphaIsDot = /[.][a-zA-Z0-9$]*$/.test(reqText);
    if (!lastNonAlphaIsDot) {
      if (!this._webConsoleCommandsCache) {
        let helpers = {
          sandbox: Object.create(null)
        };
        addWebConsoleCommands(helpers);
        this._webConsoleCommandsCache =
          Object.getOwnPropertyNames(helpers.sandbox);
      }
      matches = matches.concat(this._webConsoleCommandsCache
          .filter(n => n.startsWith(result.matchProp)));
    }

    return {
      from: this.actorID,
      matches: matches.sort(),
      matchProp: result.matchProp,
    };
  },

  /**
   * The "clearMessagesCache" request handler.
   */
  onClearMessagesCache: function () {
    // TODO: Bug 717611 - Web Console clear button does not clear cached errors
    let windowId = !this.parentActor.isRootActor ?
                   WebConsoleUtils.getInnerWindowId(this.window) : null;
    let ConsoleAPIStorage = Cc["@mozilla.org/consoleAPI-storage;1"]
                              .getService(Ci.nsIConsoleAPIStorage);
    ConsoleAPIStorage.clearEvents(windowId);

    CONSOLE_WORKER_IDS.forEach((id) => {
      ConsoleAPIStorage.clearEvents(id);
    });

    if (this.parentActor.isRootActor) {
      Services.console.logStringMessage(null); // for the Error Console
      Services.console.reset();
    }
    return {};
  },

  /**
   * The "getPreferences" request handler.
   *
   * @param object request
   *        The request message - which preferences need to be retrieved.
   * @return object
   *         The response message - a { key: value } object map.
   */
  onGetPreferences: function (request) {
    let prefs = Object.create(null);
    for (let key of request.preferences) {
      prefs[key] = this._prefs[key];
    }
    return { preferences: prefs };
  },

  /**
   * The "setPreferences" request handler.
   *
   * @param object request
   *        The request message - which preferences need to be updated.
   */
  onSetPreferences: function (request) {
    for (let key in request.preferences) {
      this._prefs[key] = request.preferences[key];

      if (this.networkMonitor) {
        if (key == "NetworkMonitor.saveRequestAndResponseBodies") {
          this.networkMonitor.saveRequestAndResponseBodies = this._prefs[key];
          if (this.networkMonitorChild) {
            this.networkMonitorChild.saveRequestAndResponseBodies =
              this._prefs[key];
          }
        } else if (key == "NetworkMonitor.throttleData") {
          this.networkMonitor.throttleData = this._prefs[key];
          if (this.networkMonitorChild) {
            this.networkMonitorChild.throttleData = this._prefs[key];
          }
        }
      }
    }
    return { updated: Object.keys(request.preferences) };
  },

  // End of request handlers.

  /**
   * Create an object with the API we expose to the Web Console during
   * JavaScript evaluation.
   * This object inherits properties and methods from the Web Console actor.
   *
   * @private
   * @param object debuggerGlobal
   *        A Debugger.Object that wraps a content global. This is used for the
   *        Web Console Commands.
   * @return object
   *         The same object as |this|, but with an added |sandbox| property.
   *         The sandbox holds methods and properties that can be used as
   *         bindings during JS evaluation.
   */
  _getWebConsoleCommands: function (debuggerGlobal) {
    let helpers = {
      window: this.evalWindow,
      chromeWindow: this.chromeWindow.bind(this),
      makeDebuggeeValue: debuggerGlobal.makeDebuggeeValue.bind(debuggerGlobal),
      createValueGrip: this.createValueGrip.bind(this),
      sandbox: Object.create(null),
      helperResult: null,
      consoleActor: this,
    };
    addWebConsoleCommands(helpers);

    let evalWindow = this.evalWindow;
    function maybeExport(obj, name) {
      if (typeof obj[name] != "function") {
        return;
      }

      // By default, chrome-implemented functions that are exposed to content
      // refuse to accept arguments that are cross-origin for the caller. This
      // is generally the safe thing, but causes problems for certain console
      // helpers like cd(), where we users sometimes want to pass a cross-origin
      // window. To circumvent this restriction, we use exportFunction along
      // with a special option designed for this purpose. See bug 1051224.
      obj[name] =
        Cu.exportFunction(obj[name], evalWindow, { allowCrossOriginArguments: true });
    }
    for (let name in helpers.sandbox) {
      let desc = Object.getOwnPropertyDescriptor(helpers.sandbox, name);

      // Workers don't have access to Cu so won't be able to exportFunction.
      if (!isWorker) {
        maybeExport(desc, "get");
        maybeExport(desc, "set");
        maybeExport(desc, "value");
      }
      if (desc.value) {
        // Make sure the helpers can be used during eval.
        desc.value = debuggerGlobal.makeDebuggeeValue(desc.value);
      }
      Object.defineProperty(helpers.sandbox, name, desc);
    }
    return helpers;
  },

  /**
   * Evaluates a string using the debugger API.
   *
   * To allow the variables view to update properties from the Web Console we
   * provide the "bindObjectActor" mechanism: the Web Console tells the
   * ObjectActor ID for which it desires to evaluate an expression. The
   * Debugger.Object pointed at by the actor ID is bound such that it is
   * available during expression evaluation (executeInGlobalWithBindings()).
   *
   * Example:
   *   _self['foobar'] = 'test'
   * where |_self| refers to the desired object.
   *
   * The |frameActor| property allows the Web Console client to provide the
   * frame actor ID, such that the expression can be evaluated in the
   * user-selected stack frame.
   *
   * For the above to work we need the debugger and the Web Console to share
   * a connection, otherwise the Web Console actor will not find the frame
   * actor.
   *
   * The Debugger.Frame comes from the jsdebugger's Debugger instance, which
   * is different from the Web Console's Debugger instance. This means that
   * for evaluation to work, we need to create a new instance for the Web
   * Console Commands helpers - they need to be Debugger.Objects coming from the
   * jsdebugger's Debugger instance.
   *
   * When |bindObjectActor| is used objects can come from different iframes,
   * from different domains. To avoid permission-related errors when objects
   * come from a different window, we also determine the object's own global,
   * such that evaluation happens in the context of that global. This means that
   * evaluation will happen in the object's iframe, rather than the top level
   * window.
   *
   * @param string string
   *        String to evaluate.
   * @param object [options]
   *        Options for evaluation:
   *        - bindObjectActor: the ObjectActor ID to use for evaluation.
   *          |evalWithBindings()| will be called with one additional binding:
   *          |_self| which will point to the Debugger.Object of the given
   *          ObjectActor.
   *        - selectedObjectActor: Like bindObjectActor, but executes with the
   *          top level window as the global.
   *        - frameActor: the FrameActor ID to use for evaluation. The given
   *        debugger frame is used for evaluation, instead of the global window.
   *        - selectedNodeActor: the NodeActor ID of the currently selected node
   *        in the Inspector (or null, if there is no selection). This is used
   *        for helper functions that make reference to the currently selected
   *        node, like $0.
   *         - url: the url to evaluate the script as. Defaults to
   *         "debugger eval code".
   * @return object
   *         An object that holds the following properties:
   *         - dbg: the debugger where the string was evaluated.
   *         - frame: (optional) the frame where the string was evaluated.
   *         - window: the Debugger.Object for the global where the string was
   *         evaluated.
   *         - result: the result of the evaluation.
   *         - helperResult: any result coming from a Web Console commands
   *         function.
   */
  /* eslint-disable complexity */
  evalWithDebugger: function (string, options = {}) {
    let trimmedString = string.trim();
    // The help function needs to be easy to guess, so we make the () optional.
    if (trimmedString == "help" || trimmedString == "?") {
      string = "help()";
    }

    // Add easter egg for console.mihai().
    if (trimmedString == "console.mihai()" || trimmedString == "console.mihai();") {
      string = "\"http://incompleteness.me/blog/2015/02/09/console-dot-mihai/\"";
    }

    // Find the Debugger.Frame of the given FrameActor.
    let frame = null, frameActor = null;
    if (options.frameActor) {
      frameActor = this.conn.getActor(options.frameActor);
      if (frameActor) {
        frame = frameActor.frame;
      } else {
        DevToolsUtils.reportException("evalWithDebugger",
          Error("The frame actor was not found: " + options.frameActor));
      }
    }

    // If we've been given a frame actor in whose scope we should evaluate the
    // expression, be sure to use that frame's Debugger (that is, the JavaScript
    // debugger's Debugger) for the whole operation, not the console's Debugger.
    // (One Debugger will treat a different Debugger's Debugger.Object instances
    // as ordinary objects, not as references to be followed, so mixing
    // debuggers causes strange behaviors.)
    let dbg = frame ? frameActor.threadActor.dbg : this.dbg;
    let dbgWindow = dbg.makeGlobalObjectReference(this.evalWindow);

    // If we have an object to bind to |_self|, create a Debugger.Object
    // referring to that object, belonging to dbg.
    let bindSelf = null;
    if (options.bindObjectActor || options.selectedObjectActor) {
      let objActor = this.getActorByID(options.bindObjectActor ||
                                       options.selectedObjectActor);
      if (objActor) {
        let jsVal = objActor.rawValue();

        if (isObject(jsVal)) {
          // If we use the makeDebuggeeValue method of jsVal's own global, then
          // we'll get a D.O that sees jsVal as viewed from its own compartment -
          // that is, without wrappers. The evalWithBindings call will then wrap
          // jsVal appropriately for the evaluation compartment.
          bindSelf = dbgWindow.makeDebuggeeValue(jsVal);
          if (options.bindObjectActor) {
            let global = Cu.getGlobalForObject(jsVal);
            try {
              let _dbgWindow = dbg.makeGlobalObjectReference(global);
              dbgWindow = _dbgWindow;
            } catch (err) {
              // The above will throw if `global` is invisible to debugger.
            }
          }
        } else {
          bindSelf = jsVal;
        }
      }
    }

    // Get the Web Console commands for the given debugger window.
    let helpers = this._getWebConsoleCommands(dbgWindow);
    let bindings = helpers.sandbox;
    if (bindSelf) {
      bindings._self = bindSelf;
    }

    if (options.selectedNodeActor) {
      let actor = this.conn.getActor(options.selectedNodeActor);
      if (actor) {
        helpers.selectedNode = actor.rawNode;
      }
    }

    // Check if the Debugger.Frame or Debugger.Object for the global include
    // $ or $$. We will not overwrite these functions with the Web Console
    // commands.
    let found$ = false, found$$ = false;
    if (frame) {
      let env = frame.environment;
      if (env) {
        found$ = !!env.find("$");
        found$$ = !!env.find("$$");
      }
    } else {
      found$ = !!dbgWindow.getOwnPropertyDescriptor("$");
      found$$ = !!dbgWindow.getOwnPropertyDescriptor("$$");
    }

    let $ = null, $$ = null;
    if (found$) {
      $ = bindings.$;
      delete bindings.$;
    }
    if (found$$) {
      $$ = bindings.$$;
      delete bindings.$$;
    }

    // Ready to evaluate the string.
    helpers.evalInput = string;

    let evalOptions;
    if (typeof options.url == "string") {
      evalOptions = { url: options.url };
    }

    // If the debugger object is changed from the last evaluation,
    // adopt this._lastConsoleInputEvaluation value in the new debugger,
    // to prevents "Debugger.Object belongs to a different Debugger" exceptions
    // related to the $_ bindings.
    if (this._lastConsoleInputEvaluation &&
        this._lastConsoleInputEvaluation.global !== dbgWindow) {
      this._lastConsoleInputEvaluation = dbg.adoptDebuggeeValue(
        this._lastConsoleInputEvaluation
      );
    }

    let result;

    if (frame) {
      result = frame.evalWithBindings(string, bindings, evalOptions);
    } else {
      result = dbgWindow.executeInGlobalWithBindings(string, bindings, evalOptions);
      // Attempt to initialize any declarations found in the evaluated string
      // since they may now be stuck in an "initializing" state due to the
      // error. Already-initialized bindings will be ignored.
      if ("throw" in result) {
        let ast;
        // Parse errors will raise an exception. We can/should ignore the error
        // since it's already being handled elsewhere and we are only interested
        // in initializing bindings.
        try {
          ast = Parser.reflectionAPI.parse(string);
        } catch (ex) {
          ast = {"body": []};
        }
        for (let line of ast.body) {
          // Only let and const declarations put bindings into an
          // "initializing" state.
          if (!(line.kind == "let" || line.kind == "const")) {
            continue;
          }

          let identifiers = [];
          for (let decl of line.declarations) {
            switch (decl.id.type) {
              case "Identifier":
                // let foo = bar;
                identifiers.push(decl.id.name);
                break;
              case "ArrayPattern":
                // let [foo, bar]    = [1, 2];
                // let [foo=99, bar] = [1, 2];
                for (let e of decl.id.elements) {
                  if (e.type == "Identifier") {
                    identifiers.push(e.name);
                  } else if (e.type == "AssignmentExpression") {
                    identifiers.push(e.left.name);
                  }
                }
                break;
              case "ObjectPattern":
                // let {bilbo, my}    = {bilbo: "baggins", my: "precious"};
                // let {blah: foo}    = {blah: yabba()}
                // let {blah: foo=99} = {blah: yabba()}
                for (let prop of decl.id.properties) {
                  // key
                  if (prop.key.type == "Identifier") {
                    identifiers.push(prop.key.name);
                  }
                  // value
                  if (prop.value.type == "Identifier") {
                    identifiers.push(prop.value.name);
                  } else if (prop.value.type == "AssignmentExpression") {
                    identifiers.push(prop.value.left.name);
                  }
                }
                break;
            }
          }

          for (let name of identifiers) {
            dbgWindow.forceLexicalInitializationByName(name);
          }
        }
      }
    }

    let helperResult = helpers.helperResult;
    delete helpers.evalInput;
    delete helpers.helperResult;
    delete helpers.selectedNode;

    if ($) {
      bindings.$ = $;
    }
    if ($$) {
      bindings.$$ = $$;
    }

    if (bindings._self) {
      delete bindings._self;
    }

    return {
      result: result,
      helperResult: helperResult,
      dbg: dbg,
      frame: frame,
      window: dbgWindow,
    };
  },
  /* eslint-enable complexity */

  // Event handlers for various listeners.

  /**
   * Handler for messages received from the ConsoleServiceListener. This method
   * sends the nsIConsoleMessage to the remote Web Console client.
   *
   * @param nsIConsoleMessage message
   *        The message we need to send to the client.
   */
  onConsoleServiceMessage: function (message) {
    let packet;
    if (message instanceof Ci.nsIScriptError) {
      packet = {
        from: this.actorID,
        type: "pageError",
        pageError: this.preparePageErrorForRemote(message),
      };
    } else {
      packet = {
        from: this.actorID,
        type: "logMessage",
        message: this._createStringGrip(message.message),
        timeStamp: message.timeStamp,
      };
    }
    this.conn.send(packet);
  },

  /**
   * Prepare an nsIScriptError to be sent to the client.
   *
   * @param nsIScriptError pageError
   *        The page error we need to send to the client.
   * @return object
   *         The object you can send to the remote client.
   */
  preparePageErrorForRemote: function (pageError) {
    let stack = null;
    // Convert stack objects to the JSON attributes expected by client code
    // Bug 1348885: If the global from which this error came from has been
    // nuked, stack is going to be a dead wrapper.
    if (pageError.stack && !Cu.isDeadWrapper(pageError.stack)) {
      stack = [];
      let s = pageError.stack;
      while (s !== null) {
        stack.push({
          filename: s.source,
          lineNumber: s.line,
          columnNumber: s.column,
          functionName: s.functionDisplayName
        });
        s = s.parent;
      }
    }
    let lineText = pageError.sourceLine;
    if (lineText && lineText.length > DebuggerServer.LONG_STRING_INITIAL_LENGTH) {
      lineText = lineText.substr(0, DebuggerServer.LONG_STRING_INITIAL_LENGTH);
    }

    let notesArray = null;
    let notes = pageError.notes;
    if (notes && notes.length) {
      notesArray = [];
      for (let i = 0, len = notes.length; i < len; i++) {
        let note = notes.queryElementAt(i, Ci.nsIScriptErrorNote);
        notesArray.push({
          messageBody: this._createStringGrip(note.errorMessage),
          frame: {
            source: note.sourceName,
            line: note.lineNumber,
            column: note.columnNumber,
          }
        });
      }
    }

    return {
      errorMessage: this._createStringGrip(pageError.errorMessage),
      errorMessageName: pageError.errorMessageName,
      exceptionDocURL: ErrorDocs.GetURL(pageError),
      sourceName: pageError.sourceName,
      lineText: lineText,
      lineNumber: pageError.lineNumber,
      columnNumber: pageError.columnNumber,
      category: pageError.category,
      timeStamp: pageError.timeStamp,
      warning: !!(pageError.flags & pageError.warningFlag),
      error: !!(pageError.flags & pageError.errorFlag),
      exception: !!(pageError.flags & pageError.exceptionFlag),
      strict: !!(pageError.flags & pageError.strictFlag),
      info: !!(pageError.flags & pageError.infoFlag),
      private: pageError.isFromPrivateWindow,
      stacktrace: stack,
      notes: notesArray,
    };
  },

  /**
   * Handler for window.console API calls received from the ConsoleAPIListener.
   * This method sends the object to the remote Web Console client.
   *
   * @see ConsoleAPIListener
   * @param object message
   *        The console API call we need to send to the remote client.
   */
  onConsoleAPICall: function (message) {
    let packet = {
      from: this.actorID,
      type: "consoleAPICall",
      message: this.prepareConsoleMessageForRemote(message),
    };
    this.conn.send(packet);
  },

  /**
   * Handler for network events. This method is invoked when a new network event
   * is about to be recorded.
   *
   * @see NetworkEventActor
   * @see NetworkMonitor from webconsole/utils.js
   *
   * @param object event
   *        The initial network request event information.
   * @return object
   *         A new NetworkEventActor is returned. This is used for tracking the
   *         network request and response.
   */
  onNetworkEvent: function (event) {
    let actor = this.getNetworkEventActor(event.channelId);
    actor.init(event);

    this._networkEventActorsByURL.set(actor._request.url, actor);

    let packet = {
      from: this.actorID,
      type: "networkEvent",
      eventActor: actor.grip()
    };

    this.conn.send(packet);

    return actor;
  },

  /**
   * Get the NetworkEventActor for a nsIHttpChannel, if it exists,
   * otherwise create a new one.
   *
   * @param string channelId
   *        The id of the channel for the network event.
   * @return object
   *         The NetworkEventActor for the given channel.
   */
  getNetworkEventActor: function (channelId) {
    let actor = this._netEvents.get(channelId);
    if (actor) {
      // delete from map as we should only need to do this check once
      this._netEvents.delete(channelId);
      return actor;
    }

    actor = new NetworkEventActor(this);
    this._actorPool.addActor(actor);
    return actor;
  },

  /**
   * Get the NetworkEventActor for a given URL that may have been noticed by the network
   * listener.  Requests are added when they start, so the actor might not yet have all
   * data for the request until it has completed.
   *
   * @param string url
   *        The URL of the request to search for.
   */
  getNetworkEventActorForURL(url) {
    return this._networkEventActorsByURL.get(url);
  },

  /**
   * Send a new HTTP request from the target's window.
   *
   * @param object message
   *        Object with 'request' - the HTTP request details.
   */
  onSendHTTPRequest(message) {
    let { url, method, headers, body } = message.request;

    // Set the loadingNode and loadGroup to the target document - otherwise the
    // request won't show up in the opened netmonitor.
    let doc = this.window.document;

    let channel = NetUtil.newChannel({
      uri: NetUtil.newURI(url),
      loadingNode: doc,
      securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
      contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER
    });

    channel.QueryInterface(Ci.nsIHttpChannel);

    channel.loadGroup = doc.documentLoadGroup;
    channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE |
                         Ci.nsIRequest.INHIBIT_CACHING |
                         Ci.nsIRequest.LOAD_ANONYMOUS;

    channel.requestMethod = method;

    for (let {name, value} of headers) {
      channel.setRequestHeader(name, value, false);
    }

    if (body) {
      channel.QueryInterface(Ci.nsIUploadChannel2);
      let bodyStream = Cc["@mozilla.org/io/string-input-stream;1"]
        .createInstance(Ci.nsIStringInputStream);
      bodyStream.setData(body, body.length);
      channel.explicitSetUploadStream(bodyStream, null, -1, method, false);
    }

    NetUtil.asyncFetch(channel, () => {});

    let actor = this.getNetworkEventActor(channel.channelId);

    // map channel to actor so we can associate future events with it
    this._netEvents.set(channel.channelId, actor);

    return {
      from: this.actorID,
      eventActor: actor.grip()
    };
  },

  /**
   * Handler for file activity. This method sends the file request information
   * to the remote Web Console client.
   *
   * @see ConsoleProgressListener
   * @param string fileURI
   *        The requested file URI.
   */
  onFileActivity: function (fileURI) {
    let packet = {
      from: this.actorID,
      type: "fileActivity",
      uri: fileURI,
    };
    this.conn.send(packet);
  },

  /**
   * Handler for reflow activity. This method forwards reflow events to the
   * remote Web Console client.
   *
   * @see ConsoleReflowListener
   * @param Object reflowInfo
   */
  onReflowActivity: function (reflowInfo) {
    let packet = {
      from: this.actorID,
      type: "reflowActivity",
      interruptible: reflowInfo.interruptible,
      start: reflowInfo.start,
      end: reflowInfo.end,
      sourceURL: reflowInfo.sourceURL,
      sourceLine: reflowInfo.sourceLine,
      functionName: reflowInfo.functionName
    };

    this.conn.send(packet);
  },

  /**
   * Handler for server logging. This method forwards log events to the
   * remote Web Console client.
   *
   * @see ServerLoggingListener
   * @param object message
   *        The console API call on the server we need to send to the remote client.
   */
  onServerLogCall: function (message) {
    // Clone all data into the content scope (that's where
    // passed arguments comes from).
    let msg = Cu.cloneInto(message, this.window);

    // All arguments within the message need to be converted into
    // debuggees to properly send it to the client side.
    // Use the default target: this.window as the global object
    // since that's the correct scope for data in the message.
    // The 'false' argument passed into prepareConsoleMessageForRemote()
    // ensures that makeDebuggeeValue uses content debuggee.
    // See also:
    // * makeDebuggeeValue()
    // * prepareConsoleMessageForRemote()
    msg = this.prepareConsoleMessageForRemote(msg, false);

    let packet = {
      from: this.actorID,
      type: "serverLogCall",
      message: msg,
    };

    this.conn.send(packet);
  },

  // End of event handlers for various listeners.

  /**
   * Prepare a message from the console API to be sent to the remote Web Console
   * instance.
   *
   * @param object message
   *        The original message received from console-api-log-event.
   * @param boolean aUseObjectGlobal
   *        If |true| the object global is determined and added as a debuggee,
   *        otherwise |this.window| is used when makeDebuggeeValue() is invoked.
   * @return object
   *         The object that can be sent to the remote client.
   */
  prepareConsoleMessageForRemote: function (message, useObjectGlobal = true) {
    let result = WebConsoleUtils.cloneObject(message);

    result.workerType = WebConsoleUtils.getWorkerType(result) || "none";

    delete result.wrappedJSObject;
    delete result.ID;
    delete result.innerID;
    delete result.consoleID;

    result.arguments = Array.map(message.arguments || [], (obj) => {
      let dbgObj = this.makeDebuggeeValue(obj, useObjectGlobal);
      return this.createValueGrip(dbgObj);
    });

    result.styles = Array.map(message.styles || [], (string) => {
      return this.createValueGrip(string);
    });

    result.category = message.category || "webdev";

    return result;
  },

  /**
   * Find the XUL window that owns the content window.
   *
   * @return Window
   *         The XUL window that owns the content window.
   */
  chromeWindow: function () {
    let window = null;
    try {
      window = this.window.QueryInterface(Ci.nsIInterfaceRequestor)
             .getInterface(Ci.nsIWebNavigation).QueryInterface(Ci.nsIDocShell)
             .chromeEventHandler.ownerGlobal;
    } catch (ex) {
      // The above can fail because chromeEventHandler is not available for all
      // kinds of |this.window|.
    }

    return window;
  },

  /**
   * Notification observer for the "last-pb-context-exited" topic.
   *
   * @private
   * @param object subject
   *        Notification subject - in this case it is the inner window ID that
   *        was destroyed.
   * @param string topic
   *        Notification topic.
   */
  _onObserverNotification: function (subject, topic) {
    switch (topic) {
      case "last-pb-context-exited":
        this.conn.send({
          from: this.actorID,
          type: "lastPrivateContextExited",
        });
        break;
    }
  },

  /**
   * The "will-navigate" progress listener. This is used to clear the current
   * eval scope.
   */
  _onWillNavigate: function ({ window, isTopLevel }) {
    if (isTopLevel) {
      this._evalWindow = null;
      EventEmitter.off(this.parentActor, "will-navigate", this._onWillNavigate);
      this._progressListenerActive = false;
    }
  },

  /**
   * This listener is called when we switch to another frame,
   * mostly to unregister previous listeners and start listening on the new document.
   */
  _onChangedToplevelDocument: function () {
    // Convert the Set to an Array
    let listeners = [...this._listeners];

    // Unregister existing listener on the previous document
    // (pass a copy of the array as it will shift from it)
    this.onStopListeners({listeners: listeners.slice()});

    // This method is called after this.window is changed,
    // so we register new listener on this new window
    this.onStartListeners({listeners: listeners});

    // Also reset the cached top level chrome window being targeted
    this._lastChromeWindow = null;
  },
};

WebConsoleActor.prototype.requestTypes =
{
  startListeners: WebConsoleActor.prototype.onStartListeners,
  stopListeners: WebConsoleActor.prototype.onStopListeners,
  getCachedMessages: WebConsoleActor.prototype.onGetCachedMessages,
  evaluateJS: WebConsoleActor.prototype.onEvaluateJS,
  evaluateJSAsync: WebConsoleActor.prototype.onEvaluateJSAsync,
  autocomplete: WebConsoleActor.prototype.onAutocomplete,
  clearMessagesCache: WebConsoleActor.prototype.onClearMessagesCache,
  getPreferences: WebConsoleActor.prototype.onGetPreferences,
  setPreferences: WebConsoleActor.prototype.onSetPreferences,
  sendHTTPRequest: WebConsoleActor.prototype.onSendHTTPRequest
};

exports.WebConsoleActor = WebConsoleActor;

/**
 * Creates an actor for a network event.
 *
 * @constructor
 * @param object webConsoleActor
 *        The parent WebConsoleActor instance for this object.
 */
function NetworkEventActor(webConsoleActor) {
  this.parent = webConsoleActor;
  this.conn = this.parent.conn;

  this._request = {
    method: null,
    url: null,
    httpVersion: null,
    headers: [],
    cookies: [],
    headersSize: null,
    postData: {},
  };

  this._response = {
    headers: [],
    cookies: [],
    content: {},
  };

  this._timings = {};
  this._stackTrace = {};

  // Keep track of LongStringActors owned by this NetworkEventActor.
  this._longStringActors = new Set();
}

NetworkEventActor.prototype =
{
  _request: null,
  _response: null,
  _timings: null,
  _longStringActors: null,

  actorPrefix: "netEvent",

  /**
   * Returns a grip for this actor for returning in a protocol message.
   */
  grip: function () {
    return {
      actor: this.actorID,
      startedDateTime: this._startedDateTime,
      timeStamp: Date.parse(this._startedDateTime),
      url: this._request.url,
      method: this._request.method,
      isXHR: this._isXHR,
      cause: this._cause,
      fromCache: this._fromCache,
      fromServiceWorker: this._fromServiceWorker,
      private: this._private,
    };
  },

  /**
   * Releases this actor from the pool.
   */
  release: function () {
    for (let grip of this._longStringActors) {
      let actor = this.parent.getActorByID(grip.actor);
      if (actor) {
        this.parent.releaseActor(actor);
      }
    }
    this._longStringActors = new Set();

    if (this._request.url) {
      this.parent._networkEventActorsByURL.delete(this._request.url);
    }
    if (this.channel) {
      this.parent._netEvents.delete(this.channel);
    }
    this.parent.releaseActor(this);
  },

  /**
   * Handle a protocol request to release a grip.
   */
  onRelease: function () {
    this.release();
    return {};
  },

  /**
   * Set the properties of this actor based on it's corresponding
   * network event.
   *
   * @param object networkEvent
   *        The network event associated with this actor.
   */
  init: function (networkEvent) {
    this._startedDateTime = networkEvent.startedDateTime;
    this._isXHR = networkEvent.isXHR;
    this._cause = networkEvent.cause;
    this._fromCache = networkEvent.fromCache;
    this._fromServiceWorker = networkEvent.fromServiceWorker;

    // Stack trace info isn't sent automatically. The client
    // needs to request it explicitly using getStackTrace
    // packet.
    this._stackTrace = networkEvent.cause.stacktrace;
    delete networkEvent.cause.stacktrace;
    networkEvent.cause.stacktraceAvailable =
      !!(this._stackTrace && this._stackTrace.length);

    for (let prop of ["method", "url", "httpVersion", "headersSize"]) {
      this._request[prop] = networkEvent[prop];
    }

    this._discardRequestBody = networkEvent.discardRequestBody;
    this._discardResponseBody = networkEvent.discardResponseBody;
    this._truncated = false;
    this._private = networkEvent.private;
  },

  /**
   * The "getRequestHeaders" packet type handler.
   *
   * @return object
   *         The response packet - network request headers.
   */
  onGetRequestHeaders: function () {
    return {
      from: this.actorID,
      headers: this._request.headers,
      headersSize: this._request.headersSize,
      rawHeaders: this._request.rawHeaders,
    };
  },

  /**
   * The "getRequestCookies" packet type handler.
   *
   * @return object
   *         The response packet - network request cookies.
   */
  onGetRequestCookies: function () {
    return {
      from: this.actorID,
      cookies: this._request.cookies,
    };
  },

  /**
   * The "getRequestPostData" packet type handler.
   *
   * @return object
   *         The response packet - network POST data.
   */
  onGetRequestPostData: function () {
    return {
      from: this.actorID,
      postData: this._request.postData,
      postDataDiscarded: this._discardRequestBody,
    };
  },

  /**
   * The "getSecurityInfo" packet type handler.
   *
   * @return object
   *         The response packet - connection security information.
   */
  onGetSecurityInfo: function () {
    return {
      from: this.actorID,
      securityInfo: this._securityInfo,
    };
  },

  /**
   * The "getResponseHeaders" packet type handler.
   *
   * @return object
   *         The response packet - network response headers.
   */
  onGetResponseHeaders: function () {
    return {
      from: this.actorID,
      headers: this._response.headers,
      headersSize: this._response.headersSize,
      rawHeaders: this._response.rawHeaders,
    };
  },

  /**
   * The "getResponseCookies" packet type handler.
   *
   * @return object
   *         The response packet - network response cookies.
   */
  onGetResponseCookies: function () {
    return {
      from: this.actorID,
      cookies: this._response.cookies,
    };
  },

  /**
   * The "getResponseContent" packet type handler.
   *
   * @return object
   *         The response packet - network response content.
   */
  onGetResponseContent: function () {
    return {
      from: this.actorID,
      content: this._response.content,
      contentDiscarded: this._discardResponseBody,
    };
  },

  /**
   * The "getEventTimings" packet type handler.
   *
   * @return object
   *         The response packet - network event timings.
   */
  onGetEventTimings: function () {
    return {
      from: this.actorID,
      timings: this._timings,
      totalTime: this._totalTime,
      offsets: this._offsets
    };
  },

  /**
   * The "getStackTrace" packet type handler.
   *
   * @return object
   *         The response packet - stack trace.
   */
  onGetStackTrace: function () {
    return {
      from: this.actorID,
      stacktrace: this._stackTrace,
    };
  },

  /** ****************************************************************
   * Listeners for new network event data coming from NetworkMonitor.
   ******************************************************************/

  /**
   * Add network request headers.
   *
   * @param array headers
   *        The request headers array.
   * @param string rawHeaders
   *        The raw headers source.
   */
  addRequestHeaders: function (headers, rawHeaders) {
    this._request.headers = headers;
    this._prepareHeaders(headers);

    rawHeaders = this.parent._createStringGrip(rawHeaders);
    if (typeof rawHeaders == "object") {
      this._longStringActors.add(rawHeaders);
    }
    this._request.rawHeaders = rawHeaders;

    let packet = {
      from: this.actorID,
      type: "networkEventUpdate",
      updateType: "requestHeaders",
      headers: headers.length,
      headersSize: this._request.headersSize,
    };

    this.conn.send(packet);
  },

  /**
   * Add network request cookies.
   *
   * @param array cookies
   *        The request cookies array.
   */
  addRequestCookies: function (cookies) {
    this._request.cookies = cookies;
    this._prepareHeaders(cookies);

    let packet = {
      from: this.actorID,
      type: "networkEventUpdate",
      updateType: "requestCookies",
      cookies: cookies.length,
    };

    this.conn.send(packet);
  },

  /**
   * Add network request POST data.
   *
   * @param object postData
   *        The request POST data.
   */
  addRequestPostData: function (postData) {
    this._request.postData = postData;
    postData.text = this.parent._createStringGrip(postData.text);
    if (typeof postData.text == "object") {
      this._longStringActors.add(postData.text);
    }

    let packet = {
      from: this.actorID,
      type: "networkEventUpdate",
      updateType: "requestPostData",
      dataSize: postData.text.length,
      discardRequestBody: this._discardRequestBody,
    };

    this.conn.send(packet);
  },

  /**
   * Add the initial network response information.
   *
   * @param object info
   *        The response information.
   * @param string rawHeaders
   *        The raw headers source.
   */
  addResponseStart: function (info, rawHeaders) {
    rawHeaders = this.parent._createStringGrip(rawHeaders);
    if (typeof rawHeaders == "object") {
      this._longStringActors.add(rawHeaders);
    }
    this._response.rawHeaders = rawHeaders;

    this._response.httpVersion = info.httpVersion;
    this._response.status = info.status;
    this._response.statusText = info.statusText;
    this._response.headersSize = info.headersSize;
    this._discardResponseBody = info.discardResponseBody;

    let packet = {
      from: this.actorID,
      type: "networkEventUpdate",
      updateType: "responseStart",
      response: info
    };

    this.conn.send(packet);
  },

  /**
   * Add connection security information.
   *
   * @param object info
   *        The object containing security information.
   */
  addSecurityInfo: function (info) {
    this._securityInfo = info;

    let packet = {
      from: this.actorID,
      type: "networkEventUpdate",
      updateType: "securityInfo",
      state: info.state,
    };

    this.conn.send(packet);
  },

  /**
   * Add network response headers.
   *
   * @param array headers
   *        The response headers array.
   */
  addResponseHeaders: function (headers) {
    this._response.headers = headers;
    this._prepareHeaders(headers);

    let packet = {
      from: this.actorID,
      type: "networkEventUpdate",
      updateType: "responseHeaders",
      headers: headers.length,
      headersSize: this._response.headersSize,
    };

    this.conn.send(packet);
  },

  /**
   * Add network response cookies.
   *
   * @param array cookies
   *        The response cookies array.
   */
  addResponseCookies: function (cookies) {
    this._response.cookies = cookies;
    this._prepareHeaders(cookies);

    let packet = {
      from: this.actorID,
      type: "networkEventUpdate",
      updateType: "responseCookies",
      cookies: cookies.length,
    };

    this.conn.send(packet);
  },

  /**
   * Add network response content.
   *
   * @param object content
   *        The response content.
   * @param object
   *        - boolean discardedResponseBody
   *          Tells if the response content was recorded or not.
   *        - boolean truncated
   *          Tells if the some of the response content is missing.
   */
  addResponseContent: function (content, {discardResponseBody, truncated}) {
    this._truncated = truncated;
    this._response.content = content;
    content.text = this.parent._createStringGrip(content.text);
    if (typeof content.text == "object") {
      this._longStringActors.add(content.text);
    }

    let packet = {
      from: this.actorID,
      type: "networkEventUpdate",
      updateType: "responseContent",
      mimeType: content.mimeType,
      contentSize: content.size,
      encoding: content.encoding,
      transferredSize: content.transferredSize,
      discardResponseBody,
    };

    this.conn.send(packet);
  },

  /**
   * Add network event timing information.
   *
   * @param number total
   *        The total time of the network event.
   * @param object timings
   *        Timing details about the network event.
   */
  addEventTimings: function (total, timings, offsets) {
    this._totalTime = total;
    this._timings = timings;
    this._offsets = offsets;

    let packet = {
      from: this.actorID,
      type: "networkEventUpdate",
      updateType: "eventTimings",
      totalTime: total
    };

    this.conn.send(packet);
  },

  /**
   * Prepare the headers array to be sent to the client by using the
   * LongStringActor for the header values, when needed.
   *
   * @private
   * @param array aHeaders
   */
  _prepareHeaders: function (headers) {
    for (let header of headers) {
      header.value = this.parent._createStringGrip(header.value);
      if (typeof header.value == "object") {
        this._longStringActors.add(header.value);
      }
    }
  },
};

NetworkEventActor.prototype.requestTypes =
{
  "release": NetworkEventActor.prototype.onRelease,
  "getRequestHeaders": NetworkEventActor.prototype.onGetRequestHeaders,
  "getRequestCookies": NetworkEventActor.prototype.onGetRequestCookies,
  "getRequestPostData": NetworkEventActor.prototype.onGetRequestPostData,
  "getResponseHeaders": NetworkEventActor.prototype.onGetResponseHeaders,
  "getResponseCookies": NetworkEventActor.prototype.onGetResponseCookies,
  "getResponseContent": NetworkEventActor.prototype.onGetResponseContent,
  "getEventTimings": NetworkEventActor.prototype.onGetEventTimings,
  "getSecurityInfo": NetworkEventActor.prototype.onGetSecurityInfo,
  "getStackTrace": NetworkEventActor.prototype.onGetStackTrace,
};
PK
!<
���Y�YOchrome/devtools/modules/devtools/server/actors/webextension-inspected-window.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 protocol = require("devtools/shared/protocol");

const {Cc, Ci, Cu, Cr} = require("chrome");

const {DebuggerServer} = require("devtools/server/main");
const Services = require("Services");

loader.lazyGetter(this, "NodeActor", () => require("devtools/server/actors/inspector/node").NodeActor, true);

const {
  XPCOMUtils,
} = require("resource://gre/modules/XPCOMUtils.jsm");

const {
  webExtensionInspectedWindowSpec,
} = require("devtools/shared/specs/webextension-inspected-window");

const {WebExtensionPolicy} = Cu.getGlobalForObject(XPCOMUtils);

// A weak set of the documents for which a warning message has been
// already logged (so that we don't keep emitting the same warning if an
// extension keeps calling the devtools.inspectedWindow.eval API method
// when it fails to retrieve a result, but we do log the warning message
// if the user reloads the window):
//
// WeakSet<Document>
const deniedWarningDocuments = new WeakSet();

function isSystemPrincipalWindow(window) {
  return window.document.nodePrincipal.isSystemPrincipal;
}

// Create the exceptionInfo property in the format expected by a
// WebExtension inspectedWindow.eval API calls.
function createExceptionInfoResult(props) {
  return {
    exceptionInfo: {
      isError: true,
      code: "E_PROTOCOLERROR",
      description: "Unknown Inspector protocol error",

      // Apply the passed properties.
      ...props,
    }
  };
}

// Show a warning message in the webconsole when an extension
// eval request has been denied, so that the user knows about it
// even if the extension doesn't report the error itself.
function logAccessDeniedWarning(window, callerInfo, extensionPolicy) {
  // Do not log the same warning multiple times for the same document.
  if (deniedWarningDocuments.has(window.document)) {
    return;
  }

  deniedWarningDocuments.add(window.document);

  const {name} = extensionPolicy;

  // System principals have a null nodePrincipal.URI and so we use
  // the url from window.location.href.
  const reportedURI = isSystemPrincipalWindow(window) ?
    Services.io.newURI(window.location.href) : window.document.nodePrincipal.URI;

  const error = Cc["@mozilla.org/scripterror;1"].createInstance(Ci.nsIScriptError);

  const msg = `The extension "${name}" is not allowed to access ${reportedURI.spec}`;

  const innerWindowId = window.QueryInterface(Ci.nsIInterfaceRequestor)
                              .getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;

  const errorFlag = 0;

  let {url, lineNumber} = callerInfo;

  let callerURI = callerInfo.url && Services.io.newURI(callerInfo.url);

  // callerInfo.url is not the full path to the file that called the WebExtensions
  // API yet (Bug 1448878), and so we associate the error to the url of the extension
  // manifest.json file as a fallback.
  if (callerURI.filePath === "/") {
    url = extensionPolicy.getURL("/manifest.json");
    lineNumber = null;
  }

  error.initWithWindowID(msg, url, lineNumber, 0, 0, errorFlag, "webExtensions",
                         innerWindowId);
  Services.console.logMessage(error);
}

function CustomizedReload(params) {
  this.docShell = params.tabActor.window
                        .QueryInterface(Ci.nsIInterfaceRequestor)
                        .getInterface(Ci.nsIDocShell);
  this.docShell.QueryInterface(Ci.nsIWebProgress);

  this.inspectedWindowEval = params.inspectedWindowEval;
  this.callerInfo = params.callerInfo;

  this.ignoreCache = params.ignoreCache;
  this.injectedScript = params.injectedScript;
  this.userAgent = params.userAgent;

  this.customizedReloadWindows = new WeakSet();
}

CustomizedReload.prototype = {
  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
                                         Ci.nsISupportsWeakReference,
                                         Ci.nsISupports]),
  get window() {
    return this.docShell.DOMWindow;
  },

  get webNavigation() {
    return this.docShell
               .QueryInterface(Ci.nsIInterfaceRequestor)
               .getInterface(Ci.nsIWebNavigation);
  },

  start() {
    if (!this.waitForReloadCompleted) {
      this.waitForReloadCompleted = new Promise((resolve, reject) => {
        this.resolveReloadCompleted = resolve;
        this.rejectReloadCompleted = reject;

        if (this.userAgent) {
          this.docShell.customUserAgent = this.userAgent;
        }

        let reloadFlags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;

        if (this.ignoreCache) {
          reloadFlags |= Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE;
        }

        try {
          if (this.injectedScript) {
            // Listen to the newly created document elements only if there is an
            // injectedScript to evaluate.
            Services.obs.addObserver(this, "document-element-inserted");
          }

          // Watch the loading progress and clear the current CustomizedReload once the
          // page has been reloaded (or if its reloading has been interrupted).
          this.docShell.addProgressListener(this,
                                            Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT);

          this.webNavigation.reload(reloadFlags);
        } catch (err) {
          // Cancel the injected script listener if the reload fails
          // (which will also report the error by rejecting the promise).
          this.stop(err);
        }
      });
    }

    return this.waitForReloadCompleted;
  },

  observe(subject, topic, data) {
    if (topic !== "document-element-inserted") {
      return;
    }

    const document = subject;
    const window = document && document.defaultView;

    // Filter out non interesting documents.
    if (!document || !document.location || !window) {
      return;
    }

    let subjectDocShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
                                .getInterface(Ci.nsIWebNavigation)
                                .QueryInterface(Ci.nsIDocShell);

    // Keep track of the set of window objects where we are going to inject
    // the injectedScript: the top level window and all its descendant
    // that are still of type content (filtering out loaded XUL pages, if any).
    if (window == this.window) {
      this.customizedReloadWindows.add(window);
    } else if (subjectDocShell.sameTypeParent) {
      let parentWindow = subjectDocShell.sameTypeParent
                                        .QueryInterface(Ci.nsIInterfaceRequestor)
                                        .getInterface(Ci.nsIDOMWindow);
      if (parentWindow && this.customizedReloadWindows.has(parentWindow)) {
        this.customizedReloadWindows.add(window);
      }
    }

    if (this.customizedReloadWindows.has(window)) {
      const {
        apiErrorResult
      } = this.inspectedWindowEval(this.callerInfo, this.injectedScript, {}, window);

      // Log only apiErrorResult, because no one is waiting for the
      // injectedScript result, and any exception is going to be logged
      // in the inspectedWindow webconsole.
      if (apiErrorResult) {
        console.error(
          "Unexpected Error in injectedScript during inspectedWindow.reload for",
          `${this.callerInfo.url}:${this.callerInfo.lineNumber}`,
          apiErrorResult
        );
      }
    }
  },

  onStateChange(webProgress, request, state, status) {
    if (webProgress.DOMWindow !== this.window) {
      return;
    }

    if (state & Ci.nsIWebProgressListener.STATE_STOP) {
      if (status == Cr.NS_BINDING_ABORTED) {
        // The customized reload has been interrupted and we can clear
        // the CustomizedReload and reject the promise.
        const url = this.window.location.href;
        this.stop(new Error(
          `devtools.inspectedWindow.reload on ${url} has been interrupted`
        ));
      } else {
        // Once the top level frame has been loaded, we can clear the customized reload
        // and resolve the promise.
        this.stop();
      }
    }
  },

  stop(error) {
    if (this.stopped) {
      return;
    }

    this.docShell.removeProgressListener(this);

    if (this.injectedScript) {
      Services.obs.removeObserver(this, "document-element-inserted");
    }

    // Reset the customized user agent.
    if (this.userAgent && this.docShell.customUserAgent == this.userAgent) {
      this.docShell.customUserAgent = null;
    }

    if (error) {
      this.rejectReloadCompleted(error);
    } else {
      this.resolveReloadCompleted();
    }

    this.stopped = true;
  }
};

var WebExtensionInspectedWindowActor = protocol.ActorClassWithSpec(
  webExtensionInspectedWindowSpec,
  {
    /**
     * Created the WebExtension InspectedWindow actor
     */
    initialize(conn, tabActor) {
      protocol.Actor.prototype.initialize.call(this, conn);
      this.tabActor = tabActor;
    },

    destroy(conn) {
      protocol.Actor.prototype.destroy.call(this, conn);

      if (this.customizedReload) {
        this.customizedReload.stop(
          new Error("WebExtensionInspectedWindowActor destroyed")
        );
        delete this.customizedReload;
      }

      if (this._dbg) {
        this._dbg.enabled = false;
        delete this._dbg;
      }
    },

    get dbg() {
      if (this._dbg) {
        return this._dbg;
      }

      this._dbg = this.tabActor.makeDebugger();
      return this._dbg;
    },

    get window() {
      return this.tabActor.window;
    },

    get webNavigation() {
      return this.tabActor.webNavigation;
    },

    createEvalBindings(dbgWindow, options) {
      const bindings = Object.create(null);

      let selectedDOMNode;

      if (options.toolboxSelectedNodeActorID) {
        let actor = DebuggerServer.searchAllConnectionsForActor(
          options.toolboxSelectedNodeActorID
        );
        if (actor && actor instanceof NodeActor) {
          selectedDOMNode = actor.rawNode;
        }
      }

      Object.defineProperty(bindings, "$0", {
        enumerable: true,
        configurable: true,
        get: () => {
          if (selectedDOMNode && !Cu.isDeadWrapper(selectedDOMNode)) {
            return dbgWindow.makeDebuggeeValue(selectedDOMNode);
          }

          return undefined;
        },
      });

      // This function is used by 'eval' and 'reload' requests, but only 'eval'
      // passes 'toolboxConsoleActor' from the client side in order to set
      // the 'inspect' binding.
      Object.defineProperty(bindings, "inspect", {
        enumerable: true,
        configurable: true,
        value: dbgWindow.makeDebuggeeValue((object) => {
          const dbgObj = dbgWindow.makeDebuggeeValue(object);

          let consoleActor = DebuggerServer.searchAllConnectionsForActor(
            options.toolboxConsoleActorID
          );
          if (consoleActor) {
            consoleActor.inspectObject(dbgObj,
                                       "webextension-devtools-inspectedWindow-eval");
          } else {
            // TODO(rpl): evaluate if it would be better to raise an exception
            // to the caller code instead.
            console.error("Toolbox Console RDP Actor not found");
          }
        }),
      });

      return bindings;
    },

    /**
     * Reload the target tab, optionally bypass cache, customize the userAgent and/or
     * inject a script in targeted document or any of its sub-frame.
     *
     * @param {webExtensionCallerInfo} callerInfo
     *   the addonId and the url (the addon base url or the url of the actual caller
     *   filename and lineNumber) used to log useful debugging information in the
     *   produced error logs and eval stack trace.
     *
     * @param {webExtensionReloadOptions} options
     *   used to optionally enable the reload customizations.
     * @param {boolean|undefined}       options.ignoreCache
     *   enable/disable the cache bypass headers.
     * @param {string|undefined}        options.userAgent
     *   customize the userAgent during the page reload.
     * @param {string|undefined}        options.injectedScript
     *   evaluate the provided javascript code in the top level and every sub-frame
     *   created during the page reload, before any other script in the page has been
     *   executed.
     */
    reload(callerInfo, {ignoreCache, userAgent, injectedScript}) {
      if (isSystemPrincipalWindow(this.window)) {
        console.error("Ignored inspectedWindow.reload on system principal target for " +
                      `${callerInfo.url}:${callerInfo.lineNumber}`);
        return {};
      }

      const delayedReload = () => {
        // This won't work while the browser is shutting down and we don't really
        // care.
        if (Services.startup.shuttingDown) {
          return;
        }

        if (injectedScript || userAgent) {
          if (this.customizedReload) {
            // TODO(rpl): check what chrome does, and evaluate if queue the new reload
            // after the current one has been completed.
            console.error(
              "Reload already in progress. Ignored inspectedWindow.reload for " +
              `${callerInfo.url}:${callerInfo.lineNumber}`
            );
            return;
          }

          try {
            this.customizedReload = new CustomizedReload({
              tabActor: this.tabActor,
              inspectedWindowEval: this.eval.bind(this),
              callerInfo, injectedScript, userAgent, ignoreCache,
            });

            this.customizedReload.start()
                .then(() => {
                  delete this.customizedReload;
                })
                .catch(err => {
                  delete this.customizedReload;
                  console.error(err);
                });
          } catch (err) {
            // Cancel the customized reload (if any) on exception during the
            // reload setup.
            if (this.customizedReload) {
              this.customizedReload.stop(err);
            }

            throw err;
          }
        } else {
          // If there is no custom user agent and/or injected script, then
          // we can reload the target without subscribing any observer/listener.
          let reloadFlags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
          if (ignoreCache) {
            reloadFlags |= Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE;
          }
          this.webNavigation.reload(reloadFlags);
        }
      };

      // Execute the reload in a dispatched runnable, so that we can
      // return the reply to the caller before the reload is actually
      // started.
      Services.tm.dispatchToMainThread(delayedReload);

      return {};
    },

    /**
     * Evaluate the provided javascript code in a target window (that is always the
     * tabActor window when called through RDP protocol, or the passed customTargetWindow
     * when called directly from the CustomizedReload instances).
     *
     * @param {webExtensionCallerInfo} callerInfo
     *   the addonId and the url (the addon base url or the url of the actual caller
     *   filename and lineNumber) used to log useful debugging information in the
     *   produced error logs and eval stack trace.
     *
     * @param {string} expression
     *   the javascript code to be evaluated in the target window
     *
     * @param {webExtensionEvalOptions} evalOptions
     *   used to optionally enable the eval customizations.
     *   NOTE: none of the eval options is currently implemented, they will be already
     *   reported as unsupported by the WebExtensions schema validation wrappers, but
     *   an additional level of error reporting is going to be applied here, so that
     *   if the server and the client have different ideas of which option is supported
     *   the eval call result will contain detailed informations (in the format usually
     *   expected for errors not raised in the evaluated javascript code).
     *
     * @param {DOMWindow|undefined} customTargetWindow
     *   Used in the CustomizedReload instances to evaluate the `injectedScript`
     *   javascript code in every sub-frame of the target window during the tab reload.
     *   NOTE: this parameter is not part of the RDP protocol exposed by this actor, when
     *   it is called over the remote debugging protocol the target window is always
     *   `tabActor.window`.
     */
    eval(callerInfo, expression, options, customTargetWindow) {
      const window = customTargetWindow || this.window;
      options = options || {};

      const extensionPolicy = WebExtensionPolicy.getByID(callerInfo.addonId);

      if (!extensionPolicy) {
        return createExceptionInfoResult({
          description: "Inspector protocol error: %s %s",
          details: [
            "Caller extension not found for",
            callerInfo.url
          ],
        });
      }

      if (!window) {
        return createExceptionInfoResult({
          description: "Inspector protocol error: %s",
          details: [
            "The target window is not defined. inspectedWindow.eval not executed.",
          ],
        });
      }

      // Log the error for the user to know that the extension request has been denied
      // (the extension may not warn the user at all).
      const logEvalDenied = () => {
        logAccessDeniedWarning(window, callerInfo, extensionPolicy);
      };

      if (isSystemPrincipalWindow(window)) {
        logEvalDenied();

        // On denied JS evaluation, report it to the extension using the same data format
        // used in the corresponding chrome API method to report issues that are
        // not exceptions raised in the evaluated javascript code.
        return createExceptionInfoResult({
          description: "Inspector protocol error: %s",
          details: [
            "This target has a system principal. inspectedWindow.eval denied.",
          ],
        });
      }

      let docPrincipalURI = window.document.nodePrincipal.URI;

      // Deny on document principals listed as restricted or
      // related to the about: pages (only about:blank and about:srcdoc are
      // allowed and their are expected to not have their about URI associated
      // to the principal).
      if (WebExtensionPolicy.isRestrictedURI(docPrincipalURI) ||
          docPrincipalURI.schemeIs("about")) {
        logEvalDenied();

        return createExceptionInfoResult({
          description: "Inspector protocol error: %s %s",
          details: [
            "This extension is not allowed on the current inspected window origin",
            docPrincipalURI.spec,
          ],
        });
      }

      const windowAddonId = window.document.nodePrincipal.addonId;

      if (windowAddonId && extensionPolicy.id !== windowAddonId) {
        logEvalDenied();

        return createExceptionInfoResult({
          description: "Inspector protocol error: %s on %s",
          details: [
            "This extension is not allowed to access this extension page.",
            window.document.location.origin,
          ],
        });
      }

      // Raise an error on the unsupported options.
      if (options.frameURL || options.contextSecurityOrigin ||
          options.useContentScriptContext) {
        return createExceptionInfoResult({
          description: "Inspector protocol error: %s",
          details: [
            "The inspectedWindow.eval options are currently not supported",
          ],
        });
      }

      const dbgWindow = this.dbg.makeGlobalObjectReference(window);

      let evalCalledFrom = callerInfo.url;
      if (callerInfo.lineNumber) {
        evalCalledFrom += `:${callerInfo.lineNumber}`;
      }

      const bindings = this.createEvalBindings(dbgWindow, options);

      const result = dbgWindow.executeInGlobalWithBindings(expression, bindings, {
        url: `debugger eval called from ${evalCalledFrom} - eval code`,
      });

      let evalResult;

      if (result) {
        if ("return" in result) {
          evalResult = result.return;
        } else if ("yield" in result) {
          evalResult = result.yield;
        } else if ("throw" in result) {
          const throwErr = result.throw;

          // XXXworkers: Calling unsafeDereference() returns an object with no
          // toString method in workers. See Bug 1215120.
          const unsafeDereference = throwErr && (typeof throwErr === "object") &&
            throwErr.unsafeDereference();
          const message = unsafeDereference && unsafeDereference.toString ?
            unsafeDereference.toString() : String(throwErr);
          const stack = unsafeDereference && unsafeDereference.stack ?
            unsafeDereference.stack : null;

          return {
            exceptionInfo: {
              isException: true,
              value: `${message}\n\t${stack}`,
            },
          };
        }
      } else {
        // TODO(rpl): can the result of executeInGlobalWithBinding be null or
        // undefined? (which means that it is not a return, a yield or a throw).
        console.error("Unexpected empty inspectedWindow.eval result for",
                      `${callerInfo.url}:${callerInfo.lineNumber}`);
      }

      if (evalResult) {
        try {
          // Return the evalResult as a grip (used by the WebExtensions
          // devtools inspector's sidebar.setExpression API method).
          if (options.evalResultAsGrip) {
            if (!options.toolboxConsoleActorID) {
              return createExceptionInfoResult({
                description: "Inspector protocol error: %s - %s",
                details: [
                  "Unexpected invalid sidebar panel expression request",
                  "missing toolboxConsoleActorID",
                ],
              });
            }

            let consoleActor = DebuggerServer.searchAllConnectionsForActor(
              options.toolboxConsoleActorID
            );

            return {valueGrip: consoleActor.createValueGrip(evalResult)};
          }

          if (evalResult && typeof evalResult === "object") {
            evalResult = evalResult.unsafeDereference();
          }
          evalResult = JSON.parse(JSON.stringify(evalResult));
        } catch (err) {
          // The evaluation result cannot be sent over the RDP Protocol,
          // report it as with the same data format used in the corresponding
          // chrome API method.
          return createExceptionInfoResult({
            description: "Inspector protocol error: %s",
            details: [
              String(err),
            ],
          });
        }
      }

      return {value: evalResult};
    }
  }
);

exports.WebExtensionInspectedWindowActor = WebExtensionInspectedWindowActor;
PK
!<���}::Echrome/devtools/modules/devtools/server/actors/webextension-parent.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {DebuggerServer} = require("devtools/server/main");
const protocol = require("devtools/shared/protocol");
const {webExtensionSpec} = require("devtools/shared/specs/webextension-parent");

loader.lazyImporter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm");
loader.lazyImporter(this, "ExtensionParent", "resource://gre/modules/ExtensionParent.jsm");

/**
 * Creates the actor that represents the addon in the parent process, which connects
 * itself to a WebExtensionChildActor counterpart which is created in the
 * extension process (or in the main process if the WebExtensions OOP mode is disabled).
 *
 * The WebExtensionParentActor subscribes itself as an AddonListener on the AddonManager
 * and forwards this events to child actor (e.g. on addon reload or when the addon is
 * uninstalled completely) and connects to the child extension process using a `browser`
 * element provided by the extension internals (it is not related to any single extension,
 * but it will be created automatically to the currently selected "WebExtensions OOP mode"
 * and it persist across the extension reloads (it is destroyed once the actor exits).
 * WebExtensionActor is a child of RootActor, it can be retrieved via
 * RootActor.listAddons request.
 *
 * @param {DebuggerServerConnection} conn
 *        The connection to the client.
 * @param {AddonWrapper} addon
 *        The target addon.
 */
const WebExtensionParentActor = protocol.ActorClassWithSpec(webExtensionSpec, {
  initialize(conn, addon) {
    this.conn = conn;
    this.addon = addon;
    this.id = addon.id;
    this._childFormPromise = null;

    AddonManager.addAddonListener(this);
  },

  destroy() {
    AddonManager.removeAddonListener(this);

    this.addon = null;
    this._childFormPromise = null;

    if (this._destroyProxyChildActor) {
      this._destroyProxyChildActor();
      delete this._destroyProxyChildActor;
    }
  },

  setOptions() {
    // NOTE: not used anymore for webextensions, still used in the legacy addons,
    // addon manager is currently going to call it automatically on every addon.
  },

  reload() {
    return this.addon.reload().then(() => {
      return {};
    });
  },

  form() {
    let policy = ExtensionParent.WebExtensionPolicy.getByID(this.id);
    return {
      actor: this.actorID,
      id: this.id,
      name: this.addon.name,
      url: this.addon.sourceURI ? this.addon.sourceURI.spec : undefined,
      iconURL: this.addon.iconURL,
      debuggable: this.addon.isDebuggable,
      temporarilyInstalled: this.addon.temporarilyInstalled,
      type: this.addon.type,
      isWebExtension: this.addon.isWebExtension,
      isAPIExtension: this.addon.isAPIExtension,
      manifestURL: policy && policy.getURL("manifest.json"),
      warnings: ExtensionParent.DebugUtils.getExtensionManifestWarnings(this.id),
    };
  },

  connect() {
    if (this._childFormPormise) {
      return this._childFormPromise;
    }

    let proxy = new ProxyChildActor(this.conn, this);
    this._childFormPromise = proxy.connect().then(form => {
      // Merge into the child actor form, some addon metadata
      // (e.g. the addon name shown in the addon debugger window title).
      return Object.assign(form, {
        id: this.addon.id,
        name: this.addon.name,
        iconURL: this.addon.iconURL,
        // Set the isOOP attribute on the connected child actor form.
        isOOP: proxy.isOOP,
      });
    });
    this._destroyProxyChildActor = () => proxy.destroy();

    return this._childFormPromise;
  },

  // ProxyChildActor callbacks.

  onProxyChildActorDestroy() {
    // Invalidate the cached child actor and form Promise
    // if the child actor exits.
    this._childFormPromise = null;
    delete this._destroyProxyChildActor;
  },

  // AddonManagerListener callbacks.

  onInstalled(addon) {
    if (addon.id != this.id) {
      return;
    }

    // Update the AddonManager's addon object on reload/update.
    this.addon = addon;
  },

  onUninstalled(addon) {
    if (addon != this.addon) {
      return;
    }

    this.destroy();
  },
});

exports.WebExtensionParentActor = WebExtensionParentActor;

function ProxyChildActor(connection, parentActor) {
  this._conn = connection;
  this._parentActor = parentActor;
  this.addonId = parentActor.id;

  this._onChildExit = this._onChildExit.bind(this);

  this._form = null;
  this._browser = null;
  this._childActorID = null;
}

ProxyChildActor.prototype = {
  /**
   * Connect the webextension child actor.
   */
  async connect() {
    if (this._browser) {
      throw new Error("This actor is already connected to the extension process");
    }

    // Called when the debug browser element has been destroyed
    // (no actor is using it anymore to connect the child extension process).
    const onDestroy = this.destroy.bind(this);

    this._browser = await ExtensionParent.DebugUtils.getExtensionProcessBrowser(this);

    this._form = await DebuggerServer.connectToChild(this._conn, this._browser, onDestroy,
                                                     {addonId: this.addonId});

    this._childActorID = this._form.actor;

    // Exit the proxy child actor if the child actor has been destroyed.
    this._mm.addMessageListener("debug:webext_child_exit", this._onChildExit);

    return this._form;
  },

  get isOOP() {
    return this._browser ? this._browser.isRemoteBrowser : undefined;
  },

  get _mm() {
    return this._browser && (
      this._browser.messageManager ||
      this._browser.frameLoader.messageManager);
  },

  destroy() {
    if (this._mm) {
      this._mm.removeMessageListener("debug:webext_child_exit", this._onChildExit);

      this._mm.sendAsyncMessage("debug:webext_parent_exit", {
        actor: this._childActorID,
      });

      ExtensionParent.DebugUtils.releaseExtensionProcessBrowser(this);
    }

    if (this._parentActor) {
      this._parentActor.onProxyChildActorDestroy();
    }

    this._parentActor = null;
    this._browser = null;
    this._childActorID = null;
    this._form = null;
  },

  /**
   * Handle the child actor exit.
   */
  _onChildExit(msg) {
    if (msg.json.actor !== this._childActorID) {
      return;
    }

    this.destroy();
  },
};
PK
!<�+_9_9>chrome/devtools/modules/devtools/server/actors/webextension.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Ci, Cu, Cc } = require("chrome");
const Services = require("Services");

const { ChromeActor } = require("./chrome");
const makeDebugger = require("./utils/make-debugger");

loader.lazyRequireGetter(this, "mapURIToAddonID", "devtools/server/actors/utils/map-uri-to-addon-id");
loader.lazyRequireGetter(this, "unwrapDebuggerObjectGlobal", "devtools/server/actors/thread", true);
loader.lazyRequireGetter(this, "ChromeUtils");

const FALLBACK_DOC_MESSAGE = "Your addon does not have any document opened yet.";

/**
 * Creates a TabActor for debugging all the contexts associated to a target WebExtensions
 * add-on running in a child extension process.
 * Most of the implementation is inherited from ChromeActor (which inherits most of its
 * implementation from TabActor).
 * WebExtensionChildActor is created by a WebExtensionParentActor counterpart, when its
 * parent actor's `connect` method has been called (on the listAddons RDP package),
 * it runs in the same process that the extension is running into (which can be the main
 * process if the extension is running in non-oop mode, or the child extension process
 * if the extension is running in oop-mode).
 *
 * A WebExtensionChildActor contains all tab actors, like a regular ChromeActor
 * or TabActor.
 *
 * History lecture:
 * - The add-on actors used to not inherit TabActor because of the different way the
 * add-on APIs where exposed to the add-on itself, and for this reason the Addon Debugger
 * has only a sub-set of the feature available in the Tab or in the Browser Toolbox.
 * - In a WebExtensions add-on all the provided contexts (background, popups etc.),
 * besides the Content Scripts which run in the content process, hooked to an existent
 * tab, by creating a new WebExtensionActor which inherits from ChromeActor, we can
 * provide a full features Addon Toolbox (which is basically like a BrowserToolbox which
 * filters the visible sources and frames to the one that are related to the target
 * add-on).
 * - When the WebExtensions OOP mode has been introduced, this actor has been refactored
 * and moved from the main process to the new child extension process.
 *
 * @param {DebuggerServerConnection} conn
 *        The connection to the client.
 * @param {nsIMessageSender} chromeGlobal.
 *        The chromeGlobal where this actor has been injected by the
 *        DebuggerServer.connectToChild method.
 * @param {string} prefix
 *        the custom RDP prefix to use.
 * @param {string} addonId
 *        the addonId of the target WebExtension.
 */
function WebExtensionChildActor(conn, chromeGlobal, prefix, addonId) {
  ChromeActor.call(this, conn);

  this._chromeGlobal = chromeGlobal;
  this._prefix = prefix;
  this.id = addonId;

  // Redefine the messageManager getter to return the chromeGlobal
  // as the messageManager for this actor (which is the browser XUL
  // element used by the parent actor running in the main process to
  // connect to the extension process).
  Object.defineProperty(this, "messageManager", {
    enumerable: true,
    configurable: true,
    get: () => {
      return this._chromeGlobal;
    }
  });

  // Bind the _allowSource helper to this, it is used in the
  // TabActor to lazily create the TabSources instance.
  this._allowSource = this._allowSource.bind(this);
  this._onParentExit = this._onParentExit.bind(this);

  this._chromeGlobal.addMessageListener("debug:webext_parent_exit", this._onParentExit);

  // Set the consoleAPIListener filtering options
  // (retrieved and used in the related webconsole child actor).
  this.consoleAPIListenerOptions = {
    addonId: this.id,
  };

  this.aps = Cc["@mozilla.org/addons/policy-service;1"]
               .getService(Ci.nsIAddonPolicyService);

  // This creates a Debugger instance for debugging all the add-on globals.
  this.makeDebugger = makeDebugger.bind(null, {
    findDebuggees: dbg => {
      return dbg.findAllGlobals().filter(this._shouldAddNewGlobalAsDebuggee);
    },
    shouldAddNewGlobalAsDebuggee: this._shouldAddNewGlobalAsDebuggee.bind(this),
  });

  // Try to discovery an existent extension page to attach (which will provide the initial
  // URL shown in the window tittle when the addon debugger is opened).
  let extensionWindow = this._searchForExtensionWindow();

  if (extensionWindow) {
    this._setWindow(extensionWindow);
  }
}
exports.WebExtensionChildActor = WebExtensionChildActor;

WebExtensionChildActor.prototype = Object.create(ChromeActor.prototype);

WebExtensionChildActor.prototype.actorPrefix = "webExtension";
WebExtensionChildActor.prototype.constructor = WebExtensionChildActor;

// NOTE: This is needed to catch in the webextension webconsole all the
// errors raised by the WebExtension internals that are not currently
// associated with any window.
WebExtensionChildActor.prototype.isRootActor = true;

/**
 * Called when the actor is removed from the connection.
 */
WebExtensionChildActor.prototype.exit = function () {
  if (this._chromeGlobal) {
    let chromeGlobal = this._chromeGlobal;
    this._chromeGlobal = null;

    chromeGlobal.removeMessageListener("debug:webext_parent_exit", this._onParentExit);

    chromeGlobal.sendAsyncMessage("debug:webext_child_exit", {
      actor: this.actorID
    });
  }

  this.addon = null;
  this.id = null;

  return ChromeActor.prototype.exit.apply(this);
};

// Private helpers.

WebExtensionChildActor.prototype._createFallbackWindow = function () {
  if (this.fallbackWindow) {
    // Skip if there is already an existent fallback window.
    return;
  }

  // Create an empty hidden window as a fallback (e.g. the background page could be
  // not defined for the target add-on or not yet when the actor instance has been
  // created).
  this.fallbackWebNav = Services.appShell.createWindowlessBrowser(true);

  // Save the reference to the fallback DOMWindow.
  this.fallbackWindow = this.fallbackWebNav.QueryInterface(Ci.nsIInterfaceRequestor)
                                           .getInterface(Ci.nsIDOMWindow);

  // Insert the fallback doc message.
  this.fallbackWindow.document.body.innerText = FALLBACK_DOC_MESSAGE;
};

WebExtensionChildActor.prototype._destroyFallbackWindow = function () {
  if (this.fallbackWebNav) {
    // Explicitly close the fallback windowless browser to prevent it to leak
    // (and to prevent it to freeze devtools xpcshell tests).
    this.fallbackWebNav.loadURI("about:blank", 0, null, null, null);
    this.fallbackWebNav.close();

    this.fallbackWebNav = null;
    this.fallbackWindow = null;
  }
};

// Discovery an extension page to use as a default target window.
// NOTE: This currently fail to discovery an extension page running in a
// windowless browser when running in non-oop mode, and the background page
// is set later using _onNewExtensionWindow.
WebExtensionChildActor.prototype._searchForExtensionWindow = function () {
  let e = Services.ww.getWindowEnumerator(null);
  while (e.hasMoreElements()) {
    let window = e.getNext();

    if (window.document.nodePrincipal.addonId == this.id) {
      return window;
    }
  }

  return undefined;
};

// Customized ChromeActor/TabActor hooks.

WebExtensionChildActor.prototype._onDocShellDestroy = function (docShell) {
  // Stop watching this docshell (the unwatch() method will check if we
  // started watching it before).
  this._unwatchDocShell(docShell);

  // Let the _onDocShellDestroy notify that the docShell has been destroyed.
  let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
        .getInterface(Ci.nsIWebProgress);
  this._notifyDocShellDestroy(webProgress);

  // If the destroyed docShell was the current docShell and the actor is
  // currently attached, switch to the fallback window
  if (this.attached && docShell == this.docShell) {
    // Creates a fallback window if it doesn't exist yet.
    this._createFallbackWindow();
    this._changeTopLevelDocument(this.fallbackWindow);
  }
};

WebExtensionChildActor.prototype._onNewExtensionWindow = function (window) {
  if (!this.window || this.window === this.fallbackWindow) {
    this._changeTopLevelDocument(window);
  }
};

WebExtensionChildActor.prototype._attach = function () {
  // NOTE: we need to be sure that `this.window` can return a
  // window before calling the ChromeActor.onAttach, or the TabActor
  // will not be subscribed to the child doc shell updates.

  if (!this.window || this.window.document.nodePrincipal.addonId !== this.id) {
    // Discovery an existent extension page to attach.
    let extensionWindow = this._searchForExtensionWindow();

    if (!extensionWindow) {
      this._createFallbackWindow();
      this._setWindow(this.fallbackWindow);
    } else {
      this._setWindow(extensionWindow);
    }
  }

  // Call ChromeActor's _attach to listen for any new/destroyed chrome docshell
  ChromeActor.prototype._attach.apply(this);
};

WebExtensionChildActor.prototype._detach = function () {
  // Call ChromeActor's _detach to unsubscribe new/destroyed chrome docshell listeners.
  ChromeActor.prototype._detach.apply(this);

  // Stop watching for new extension windows.
  this._destroyFallbackWindow();
};

/**
 * Return the json details related to a docShell.
 */
WebExtensionChildActor.prototype._docShellToWindow = function (docShell) {
  const baseWindowDetails = ChromeActor.prototype._docShellToWindow.call(this, docShell);

  let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                            .getInterface(Ci.nsIWebProgress);
  let window = webProgress.DOMWindow;

  // Collect the addonID from the document origin attributes and its sameType top level
  // frame.
  let addonID = window.document.nodePrincipal.addonId;
  let sameTypeRootAddonID = docShell.QueryInterface(Ci.nsIDocShellTreeItem)
                                    .sameTypeRootTreeItem
                                    .QueryInterface(Ci.nsIInterfaceRequestor)
                                    .getInterface(Ci.nsIDOMWindow)
                                    .document.nodePrincipal.addonId;

  return Object.assign(baseWindowDetails, {
    addonID,
    sameTypeRootAddonID,
  });
};

/**
 * Return an array of the json details related to an array/iterator of docShells.
 */
WebExtensionChildActor.prototype._docShellsToWindows = function (docshells) {
  return ChromeActor.prototype._docShellsToWindows.call(this, docshells)
                    .filter(windowDetails => {
                      // Filter the docShells based on the addon id of the window or
                      // its sameType top level frame.
                      return windowDetails.addonID === this.id ||
                             windowDetails.sameTypeRootAddonID === this.id;
                    });
};

WebExtensionChildActor.prototype.isExtensionWindow = function (window) {
  return window.document.nodePrincipal.addonId == this.id;
};

WebExtensionChildActor.prototype.isExtensionWindowDescendent = function (window) {
  // Check if the source is coming from a descendant docShell of an extension window.
  let docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
                       .getInterface(Ci.nsIDocShell);
  let rootWin = docShell.sameTypeRootTreeItem.QueryInterface(Ci.nsIInterfaceRequestor)
                                             .getInterface(Ci.nsIDOMWindow);
  return this.isExtensionWindow(rootWin);
};

/**
 * Return true if the given source is associated with this addon and should be
 * added to the visible sources (retrieved and used by the webbrowser actor module).
 */
WebExtensionChildActor.prototype._allowSource = function (source) {
  // Use the source.element to detect the allowed source, if any.
  if (source.element) {
    let domEl = unwrapDebuggerObjectGlobal(source.element);
    return (this.isExtensionWindow(domEl.ownerGlobal) ||
            this.isExtensionWindowDescendent(domEl.ownerGlobal));
  }

  // Fallback to check the uri if there is no source.element associated to the source.

  // Retrieve the first component of source.url in the form "url1 -> url2 -> ...".
  let url = source.url.split(" -> ").pop();

  // Filter out the code introduced by evaluating code in the webconsole.
  if (url === "debugger eval code") {
    return false;
  }

  let uri;

  // Try to decode the url.
  try {
    uri = Services.io.newURI(url);
  } catch (err) {
    Cu.reportError(`Unexpected invalid url: ${url}`);
    return false;
  }

  // Filter out resource and chrome sources (which are related to the loaded internals).
  if (["resource", "chrome", "file"].includes(uri.scheme)) {
    return false;
  }

  try {
    let addonID = this.aps.extensionURIToAddonId(uri);

    return addonID == this.id;
  } catch (err) {
    // extensionURIToAddonId raises an exception on non-extension URLs.
    return false;
  }
};

/**
 * Return true if the given global is associated with this addon and should be
 * added as a debuggee, false otherwise.
 */
WebExtensionChildActor.prototype._shouldAddNewGlobalAsDebuggee = function (newGlobal) {
  const global = unwrapDebuggerObjectGlobal(newGlobal);

  if (global instanceof Ci.nsIDOMWindow) {
    try {
      global.document;
    } catch (e) {
      // The global might be a sandbox with a window object in its proto chain. If the
      // window navigated away since the sandbox was created, it can throw a security
      // exception during this property check as the sandbox no longer has access to
      // its own proto.
      return false;
    }

    // Filter out any global which contains a XUL document.
    if (ChromeUtils.getClassName(global.document) == "XULDocument") {
      return false;
    }

    // Change top level document as a simulated frame switching.
    if (global.document.ownerGlobal && this.isExtensionWindow(global)) {
      this._onNewExtensionWindow(global.document.ownerGlobal);
    }

    return global.document.ownerGlobal &&
           this.isExtensionWindowDescendent(global.document.ownerGlobal);
  }

  try {
    // This will fail for non-Sandbox objects, hence the try-catch block.
    let metadata = Cu.getSandboxMetadata(global);
    if (metadata) {
      return metadata.addonID === this.id;
    }
  } catch (e) {
    // Unable to retrieve the sandbox metadata.
  }

  return false;
};

// Handlers for the messages received from the parent actor.

WebExtensionChildActor.prototype._onParentExit = function (msg) {
  if (msg.json.actor !== this.actorID) {
    return;
  }

  this.exit();
};
PK
!<�J����7chrome/devtools/modules/devtools/server/actors/webgl.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 XPCNativeWrapper */

const {Cu} = require("chrome");
const EventEmitter = require("devtools/shared/event-emitter");
const defer = require("devtools/shared/defer");
const protocol = require("devtools/shared/protocol");
const { ContentObserver } = require("devtools/shared/content-observer");
const {
  shaderSpec,
  programSpec,
  webGLSpec,
} = require("devtools/shared/specs/webgl");

const WEBGL_CONTEXT_NAMES = ["webgl", "experimental-webgl", "moz-webgl"];

// These traits are bit masks. Make sure they're powers of 2.
const PROGRAM_DEFAULT_TRAITS = 0;
const PROGRAM_BLACKBOX_TRAIT = 1;
const PROGRAM_HIGHLIGHT_TRAIT = 2;

/**
 * A WebGL Shader contributing to building a WebGL Program.
 * You can either retrieve, or compile the source of a shader, which will
 * automatically inflict the necessary changes to the WebGL state.
 */
var ShaderActor = protocol.ActorClassWithSpec(shaderSpec, {
  /**
   * Create the shader actor.
   *
   * @param DebuggerServerConnection conn
   *        The server connection.
   * @param WebGLProgram program
   *        The WebGL program being linked.
   * @param WebGLShader shader
   *        The cooresponding vertex or fragment shader.
   * @param WebGLProxy proxy
   *        The proxy methods for the WebGL context owning this shader.
   */
  initialize: function (conn, program, shader, proxy) {
    protocol.Actor.prototype.initialize.call(this, conn);
    this.program = program;
    this.shader = shader;
    this.text = proxy.getShaderSource(shader);
    this.linkedProxy = proxy;
  },

  /**
   * Gets the source code for this shader.
   */
  getText: function () {
    return this.text;
  },

  /**
   * Sets and compiles new source code for this shader.
   */
  compile: function (text) {
    // Get the shader and corresponding program to change via the WebGL proxy.
    let { linkedProxy: proxy, shader, program } = this;

    // Get the new shader source to inject.
    let oldText = this.text;
    let newText = text;

    // Overwrite the shader's source.
    let error = proxy.compileShader(program, shader, this.text = newText);

    // If something went wrong, revert to the previous shader.
    if (error.compile || error.link) {
      proxy.compileShader(program, shader, this.text = oldText);
      return error;
    }
    return undefined;
  }
});

/**
 * A WebGL program is composed (at the moment, analogue to OpenGL ES 2.0)
 * of two shaders: a vertex shader and a fragment shader.
 */
var ProgramActor = protocol.ActorClassWithSpec(programSpec, {
  /**
   * Create the program actor.
   *
   * @param DebuggerServerConnection conn
   *        The server connection.
   * @param WebGLProgram program
   *        The WebGL program being linked.
   * @param WebGLShader[] shaders
   *        The WebGL program's cooresponding vertex and fragment shaders.
   * @param WebGLCache cache
   *        The state storage for the WebGL context owning this program.
   * @param WebGLProxy proxy
   *        The proxy methods for the WebGL context owning this program.
   */
  initialize: function (conn, [program, shaders, cache, proxy]) {
    protocol.Actor.prototype.initialize.call(this, conn);
    this._shaderActorsCache = { vertex: null, fragment: null };
    this.program = program;
    this.shaders = shaders;
    this.linkedCache = cache;
    this.linkedProxy = proxy;
  },

  get ownerWindow() {
    return this.linkedCache.ownerWindow;
  },

  get ownerContext() {
    return this.linkedCache.ownerContext;
  },

  /**
   * Gets the vertex shader linked to this program. This method guarantees
   * a single actor instance per shader.
   */
  getVertexShader: function () {
    return this._getShaderActor("vertex");
  },

  /**
   * Gets the fragment shader linked to this program. This method guarantees
   * a single actor instance per shader.
   */
  getFragmentShader: function () {
    return this._getShaderActor("fragment");
  },

  /**
   * Highlights any geometry rendered using this program.
   */
  highlight: function (tint) {
    this.linkedProxy.highlightTint = tint;
    this.linkedCache.setProgramTrait(this.program, PROGRAM_HIGHLIGHT_TRAIT);
  },

  /**
   * Allows geometry to be rendered normally using this program.
   */
  unhighlight: function () {
    this.linkedCache.unsetProgramTrait(this.program, PROGRAM_HIGHLIGHT_TRAIT);
  },

  /**
   * Prevents any geometry from being rendered using this program.
   */
  blackbox: function () {
    this.linkedCache.setProgramTrait(this.program, PROGRAM_BLACKBOX_TRAIT);
  },

  /**
   * Allows geometry to be rendered using this program.
   */
  unblackbox: function () {
    this.linkedCache.unsetProgramTrait(this.program, PROGRAM_BLACKBOX_TRAIT);
  },

  /**
   * Returns a cached ShaderActor instance based on the required shader type.
   *
   * @param string type
   *        Either "vertex" or "fragment".
   * @return ShaderActor
   *         The respective shader actor instance.
   */
  _getShaderActor: function (type) {
    if (this._shaderActorsCache[type]) {
      return this._shaderActorsCache[type];
    }
    let proxy = this.linkedProxy;
    let shader = proxy.getShaderOfType(this.shaders, type);
    let shaderActor = new ShaderActor(this.conn, this.program, shader, proxy);
    this._shaderActorsCache[type] = shaderActor;
    return this._shaderActorsCache[type];
  }
});

/**
 * The WebGL Actor handles simple interaction with a WebGL context via a few
 * high-level methods. After instantiating this actor, you'll need to set it
 * up by calling setup().
 */
exports.WebGLActor = protocol.ActorClassWithSpec(webGLSpec, {
  initialize: function (conn, tabActor) {
    protocol.Actor.prototype.initialize.call(this, conn);
    this.tabActor = tabActor;
    this._onGlobalCreated = this._onGlobalCreated.bind(this);
    this._onGlobalDestroyed = this._onGlobalDestroyed.bind(this);
    this._onProgramLinked = this._onProgramLinked.bind(this);
  },
  destroy: function (conn) {
    protocol.Actor.prototype.destroy.call(this, conn);
    this.finalize();
  },

  /**
   * Starts waiting for the current tab actor's document global to be
   * created, in order to instrument the Canvas context and become
   * aware of everything the content does WebGL-wise.
   *
   * See ContentObserver and WebGLInstrumenter for more details.
   */
  setup: function ({ reload }) {
    if (this._initialized) {
      return;
    }
    this._initialized = true;

    this._programActorsCache = [];
    this._webglObserver = new WebGLObserver();

    this.tabActor.on("window-ready", this._onGlobalCreated);
    this.tabActor.on("window-destroyed", this._onGlobalDestroyed);
    EventEmitter.on(this._webglObserver, "program-linked", this._onProgramLinked);

    if (reload) {
      this.tabActor.window.location.reload();
    }
  },

  /**
   * Stops listening for document global changes and puts this actor
   * to hibernation. This method is called automatically just before the
   * actor is destroyed.
   */
  finalize: function () {
    if (!this._initialized) {
      return;
    }
    this._initialized = false;

    this.tabActor.off("window-ready", this._onGlobalCreated);
    this.tabActor.off("window-destroyed", this._onGlobalDestroyed);
    EventEmitter.off(this._webglObserver, "program-linked", this._onProgramLinked);

    this._programActorsCache = null;
    this._contentObserver = null;
    this._webglObserver = null;
  },

  /**
   * Gets an array of cached program actors for the current tab actor's window.
   * This is useful for dealing with bfcache, when no new programs are linked.
   */
  getPrograms: function () {
    let id = ContentObserver.GetInnerWindowID(this.tabActor.window);
    return this._programActorsCache.filter(e => e.ownerWindow == id);
  },

  /**
   * Waits for one frame via `requestAnimationFrame` on the tab actor's window.
   * Used in tests.
   */
  waitForFrame: function () {
    let deferred = defer();
    this.tabActor.window.requestAnimationFrame(deferred.resolve);
    return deferred.promise;
  },

  /**
   * Gets a pixel's RGBA value from a context specified by selector
   * and the coordinates of the pixel in question.
   * Currently only used in tests.
   *
   * @param string selector
   *        A string selector to select the canvas in question from the DOM.
   * @param Object position
   *        An object with an `x` and `y` property indicating coordinates of
   *        the pixel being inspected.
   * @return Object
   *        An object containing `r`, `g`, `b`, and `a` properties of the pixel.
   */
  getPixel: function ({ selector, position }) {
    let { x, y } = position;
    let canvas = this.tabActor.window.document.querySelector(selector);
    let context = XPCNativeWrapper.unwrap(canvas.getContext("webgl"));
    let { proxy } = this._webglObserver.for(context);
    let height = canvas.height;

    let buffer = new this.tabActor.window.Uint8Array(4);
    buffer = XPCNativeWrapper.unwrap(buffer);

    proxy.readPixels(
      x, height - y - 1, 1, 1, context.RGBA, context.UNSIGNED_BYTE, buffer
    );

    return { r: buffer[0], g: buffer[1], b: buffer[2], a: buffer[3] };
  },

  /**
   * Gets an array of all cached program actors belonging to all windows.
   * This should only be used for tests.
   */
  _getAllPrograms: function () {
    return this._programActorsCache;
  },

  /**
   * Invoked whenever the current tab actor's document global is created.
   */
  _onGlobalCreated: function ({id, window, isTopLevel}) {
    if (isTopLevel) {
      WebGLInstrumenter.handle(window, this._webglObserver);
      this.emit("global-created", id);
    }
  },

  /**
   * Invoked whenever the current tab actor's inner window is destroyed.
   */
  _onGlobalDestroyed: function ({id, isTopLevel, isFrozen}) {
    if (isTopLevel && !isFrozen) {
      removeFromArray(this._programActorsCache, e => e.ownerWindow == id);
      this._webglObserver.unregisterContextsForWindow(id);
      this.emit("global-destroyed", id);
    }
  },

  /**
   * Invoked whenever an observed WebGL context links a program.
   */
  _onProgramLinked: function (...args) {
    let programActor = new ProgramActor(this.conn, args);
    this._programActorsCache.push(programActor);
    this.emit("program-linked", programActor);
  }
});

/**
 * Instruments a HTMLCanvasElement with the appropriate inspection methods.
 */
var WebGLInstrumenter = {
  /**
   * Overrides the getContext method in the HTMLCanvasElement prototype.
   *
   * @param nsIDOMWindow window
   *        The window to perform the instrumentation in.
   * @param WebGLObserver observer
   *        The observer watching function calls in the context.
   */
  handle: function (window, observer) {
    let self = this;

    let id = ContentObserver.GetInnerWindowID(window);
    let canvasElem = XPCNativeWrapper.unwrap(window.HTMLCanvasElement);
    let canvasPrototype = canvasElem.prototype;
    let originalGetContext = canvasPrototype.getContext;

    /**
     * Returns a drawing context on the canvas, or null if the context ID is
     * not supported. This override creates an observer for the targeted context
     * type and instruments specific functions in the targeted context instance.
     */
    canvasPrototype.getContext = function (name, options) {
      // Make sure a context was able to be created.
      let context = originalGetContext.call(this, name, options);
      if (!context) {
        return context;
      }
      // Make sure a WebGL (not a 2D) context will be instrumented.
      if (!WEBGL_CONTEXT_NAMES.includes(name)) {
        return context;
      }
      // Repeated calls to 'getContext' return the same instance, no need to
      // instrument everything again.
      if (observer.for(context)) {
        return context;
      }

      // Create a separate state storage for this context.
      observer.registerContextForWindow(id, context);

      // Link our observer to the new WebGL context methods.
      for (let { timing, callback, functions } of self._methods) {
        for (let func of functions) {
          self._instrument(observer, context, func, callback, timing);
        }
      }

      // Return the decorated context back to the content consumer, which
      // will continue using it normally.
      return context;
    };
  },

  /**
   * Overrides a specific method in a HTMLCanvasElement context.
   *
   * @param WebGLObserver observer
   *        The observer watching function calls in the context.
   * @param WebGLRenderingContext context
   *        The targeted WebGL context instance.
   * @param string funcName
   *        The function to override.
   * @param array callbackName [optional]
   *        The two callback function names in the observer, corresponding to
   *        the "before" and "after" invocation times. If unspecified, they will
   *        default to the name of the function to override.
   * @param number timing [optional]
   *        When to issue the callback in relation to the actual context
   *        function call. Availalble values are -1 for "before" (default)
   *        1 for "after" and 0 for "before and after".
   */
  _instrument: function (observer, context, funcName, callbackName = [], timing = -1) {
    let { cache, proxy } = observer.for(context);
    let originalFunc = context[funcName];
    let beforeFuncName = callbackName[0] || funcName;
    let afterFuncName = callbackName[1] || callbackName[0] || funcName;

    context[funcName] = function (...glArgs) {
      if (timing <= 0 && !observer.suppressHandlers) {
        let glBreak = observer[beforeFuncName](glArgs, cache, proxy);
        if (glBreak) {
          return undefined;
        }
      }

      // Invoking .apply on an unxrayed content function doesn't work, because
      // the arguments array is inaccessible to it. Get Xrays back.
      let glResult = Cu.waiveXrays(Cu.unwaiveXrays(originalFunc).apply(this, glArgs));

      if (timing >= 0 && !observer.suppressHandlers) {
        let glBreak = observer[afterFuncName](glArgs, glResult, cache, proxy);
        if (glBreak) {
          return undefined;
        }
      }

      return glResult;
    };
  },

  /**
   * Override mappings for WebGL methods.
   */
  _methods: [{
    // after
    timing: 1,
    functions: [
      "linkProgram", "getAttribLocation", "getUniformLocation"
    ]
  }, {
    // before
    timing: -1,
    callback: [
      "toggleVertexAttribArray"
    ],
    functions: [
      "enableVertexAttribArray", "disableVertexAttribArray"
    ]
  }, {
    // before
    timing: -1,
    callback: [
      "attribute_"
    ],
    functions: [
      "vertexAttrib1f", "vertexAttrib2f", "vertexAttrib3f", "vertexAttrib4f",
      "vertexAttrib1fv", "vertexAttrib2fv", "vertexAttrib3fv", "vertexAttrib4fv",
      "vertexAttribPointer"
    ]
  }, {
    // before
    timing: -1,
    callback: [
      "uniform_"
    ],
    functions: [
      "uniform1i", "uniform2i", "uniform3i", "uniform4i",
      "uniform1f", "uniform2f", "uniform3f", "uniform4f",
      "uniform1iv", "uniform2iv", "uniform3iv", "uniform4iv",
      "uniform1fv", "uniform2fv", "uniform3fv", "uniform4fv",
      "uniformMatrix2fv", "uniformMatrix3fv", "uniformMatrix4fv"
    ]
  }, {
    // before
    timing: -1,
    functions: [
      "useProgram", "enable", "disable", "blendColor",
      "blendEquation", "blendEquationSeparate",
      "blendFunc", "blendFuncSeparate"
    ]
  }, {
    // before and after
    timing: 0,
    callback: [
      "beforeDraw_", "afterDraw_"
    ],
    functions: [
      "drawArrays", "drawElements"
    ]
  }]
  // TODO: It'd be a good idea to handle other functions as well:
  //   - getActiveUniform
  //   - getUniform
  //   - getActiveAttrib
  //   - getVertexAttrib
};

/**
 * An observer that captures a WebGL context's method calls.
 */
function WebGLObserver() {
  this._contexts = new Map();
}

WebGLObserver.prototype = {
  _contexts: null,

  /**
   * Creates a WebGLCache and a WebGLProxy for the specified window and context.
   *
   * @param number id
   *        The id of the window containing the WebGL context.
   * @param WebGLRenderingContext context
   *        The WebGL context used in the cache and proxy instances.
   */
  registerContextForWindow: function (id, context) {
    let cache = new WebGLCache(id, context);
    let proxy = new WebGLProxy(id, context, cache, this);
    cache.refreshState(proxy);

    this._contexts.set(context, {
      ownerWindow: id,
      cache: cache,
      proxy: proxy
    });
  },

  /**
   * Removes all WebGLCache and WebGLProxy instances for a particular window.
   *
   * @param number id
   *        The id of the window containing the WebGL context.
   */
  unregisterContextsForWindow: function (id) {
    removeFromMap(this._contexts, e => e.ownerWindow == id);
  },

  /**
   * Gets the WebGLCache and WebGLProxy instances for a particular context.
   *
   * @param WebGLRenderingContext context
   *        The WebGL context used in the cache and proxy instances.
   * @return object
   *         An object containing the corresponding { cache, proxy } instances.
   */
  for: function (context) {
    return this._contexts.get(context);
  },

  /**
   * Set this flag to true to stop observing any context function calls.
   */
  suppressHandlers: false,

  /**
   * Called immediately *after* 'linkProgram' is requested in the context.
   *
   * @param array glArgs
   *        Overridable arguments with which the function is called.
   * @param void glResult
   *        The returned value of the original function call.
   * @param WebGLCache cache
   *        The state storage for the WebGL context initiating this call.
   * @param WebGLProxy proxy
   *        The proxy methods for the WebGL context initiating this call.
   */
  linkProgram: function (glArgs, glResult, cache, proxy) {
    let program = glArgs[0];
    let shaders = proxy.getAttachedShaders(program);
    cache.addProgram(program, PROGRAM_DEFAULT_TRAITS);
    EventEmitter.emit(this, "program-linked", program, shaders, cache, proxy);
  },

  /**
   * Called immediately *after* 'getAttribLocation' is requested in the context.
   *
   * @param array glArgs
   *        Overridable arguments with which the function is called.
   * @param GLint glResult
   *        The returned value of the original function call.
   * @param WebGLCache cache
   *        The state storage for the WebGL context initiating this call.
   */
  getAttribLocation: function (glArgs, glResult, cache) {
    // Make sure the attribute's value is legal before caching.
    if (glResult < 0) {
      return;
    }
    let [program, name] = glArgs;
    cache.addAttribute(program, name, glResult);
  },

  /**
   * Called immediately *after* 'getUniformLocation' is requested in the context.
   *
   * @param array glArgs
   *        Overridable arguments with which the function is called.
   * @param WebGLUniformLocation glResult
   *        The returned value of the original function call.
   * @param WebGLCache cache
   *        The state storage for the WebGL context initiating this call.
   */
  getUniformLocation: function (glArgs, glResult, cache) {
    // Make sure the uniform's value is legal before caching.
    if (!glResult) {
      return;
    }
    let [program, name] = glArgs;
    cache.addUniform(program, name, glResult);
  },

  /**
   * Called immediately *before* 'enableVertexAttribArray' or
   * 'disableVertexAttribArray'is requested in the context.
   *
   * @param array glArgs
   *        Overridable arguments with which the function is called.
   * @param WebGLCache cache
   *        The state storage for the WebGL context initiating this call.
   */
  toggleVertexAttribArray: function (glArgs, cache) {
    glArgs[0] = cache.getCurrentAttributeLocation(glArgs[0]);
    // Return true to break original function call.
    return glArgs[0] < 0;
  },

  /**
   * Called immediately *before* 'attribute_' is requested in the context.
   *
   * @param array glArgs
   *        Overridable arguments with which the function is called.
   * @param WebGLCache cache
   *        The state storage for the WebGL context initiating this call.
   */
  attribute_: function (glArgs, cache) {
    glArgs[0] = cache.getCurrentAttributeLocation(glArgs[0]);
    // Return true to break original function call.
    return glArgs[0] < 0;
  },

  /**
   * Called immediately *before* 'uniform_' is requested in the context.
   *
   * @param array glArgs
   *        Overridable arguments with which the function is called.
   * @param WebGLCache cache
   *        The state storage for the WebGL context initiating this call.
   */
  uniform_: function (glArgs, cache) {
    glArgs[0] = cache.getCurrentUniformLocation(glArgs[0]);
    // Return true to break original function call.
    return !glArgs[0];
  },

  /**
   * Called immediately *before* 'useProgram' is requested in the context.
   *
   * @param array glArgs
   *        Overridable arguments with which the function is called.
   * @param WebGLCache cache
   *        The state storage for the WebGL context initiating this call.
   */
  useProgram: function (glArgs, cache) {
    // Manually keeping a cache and not using gl.getParameter(CURRENT_PROGRAM)
    // because gl.get* functions are slow as potatoes.
    cache.currentProgram = glArgs[0];
  },

  /**
   * Called immediately *before* 'enable' is requested in the context.
   *
   * @param array glArgs
   *        Overridable arguments with which the function is called.
   * @param WebGLCache cache
   *        The state storage for the WebGL context initiating this call.
   */
  enable: function (glArgs, cache) {
    cache.currentState[glArgs[0]] = true;
  },

  /**
   * Called immediately *before* 'disable' is requested in the context.
   *
   * @param array glArgs
   *        Overridable arguments with which the function is called.
   * @param WebGLCache cache
   *        The state storage for the WebGL context initiating this call.
   */
  disable: function (glArgs, cache) {
    cache.currentState[glArgs[0]] = false;
  },

  /**
   * Called immediately *before* 'blendColor' is requested in the context.
   *
   * @param array glArgs
   *        Overridable arguments with which the function is called.
   * @param WebGLCache cache
   *        The state storage for the WebGL context initiating this call.
   */
  blendColor: function (glArgs, cache) {
    let blendColor = cache.currentState.blendColor;
    blendColor[0] = glArgs[0];
    blendColor[1] = glArgs[1];
    blendColor[2] = glArgs[2];
    blendColor[3] = glArgs[3];
  },

  /**
   * Called immediately *before* 'blendEquation' is requested in the context.
   *
   * @param array glArgs
   *        Overridable arguments with which the function is called.
   * @param WebGLCache cache
   *        The state storage for the WebGL context initiating this call.
   */
  blendEquation: function (glArgs, cache) {
    let state = cache.currentState;
    state.blendEquationRgb = state.blendEquationAlpha = glArgs[0];
  },

  /**
   * Called immediately *before* 'blendEquationSeparate' is requested in the context.
   *
   * @param array glArgs
   *        Overridable arguments with which the function is called.
   * @param WebGLCache cache
   *        The state storage for the WebGL context initiating this call.
   */
  blendEquationSeparate: function (glArgs, cache) {
    let state = cache.currentState;
    state.blendEquationRgb = glArgs[0];
    state.blendEquationAlpha = glArgs[1];
  },

  /**
   * Called immediately *before* 'blendFunc' is requested in the context.
   *
   * @param array glArgs
   *        Overridable arguments with which the function is called.
   * @param WebGLCache cache
   *        The state storage for the WebGL context initiating this call.
   */
  blendFunc: function (glArgs, cache) {
    let state = cache.currentState;
    state.blendSrcRgb = state.blendSrcAlpha = glArgs[0];
    state.blendDstRgb = state.blendDstAlpha = glArgs[1];
  },

  /**
   * Called immediately *before* 'blendFuncSeparate' is requested in the context.
   *
   * @param array glArgs
   *        Overridable arguments with which the function is called.
   * @param WebGLCache cache
   *        The state storage for the WebGL context initiating this call.
   */
  blendFuncSeparate: function (glArgs, cache) {
    let state = cache.currentState;
    state.blendSrcRgb = glArgs[0];
    state.blendDstRgb = glArgs[1];
    state.blendSrcAlpha = glArgs[2];
    state.blendDstAlpha = glArgs[3];
  },

  /**
   * Called immediately *before* 'drawArrays' or 'drawElements' is requested
   * in the context.
   *
   * @param array glArgs
   *        Overridable arguments with which the function is called.
   * @param WebGLCache cache
   *        The state storage for the WebGL context initiating this call.
   * @param WebGLProxy proxy
   *        The proxy methods for the WebGL context initiating this call.
   */
  beforeDraw_: function (glArgs, cache, proxy) {
    let traits = cache.currentProgramTraits;

    // Handle program blackboxing.
    if (traits & PROGRAM_BLACKBOX_TRAIT) {
      // Return true to break original function call.
      return true;
    }
    // Handle program highlighting.
    if (traits & PROGRAM_HIGHLIGHT_TRAIT) {
      proxy.enableHighlighting();
    }

    return false;
  },

  /**
   * Called immediately *after* 'drawArrays' or 'drawElements' is requested
   * in the context.
   *
   * @param array glArgs
   *        Overridable arguments with which the function is called.
   * @param void glResult
   *        The returned value of the original function call.
   * @param WebGLCache cache
   *        The state storage for the WebGL context initiating this call.
   * @param WebGLProxy proxy
   *        The proxy methods for the WebGL context initiating this call.
   */
  afterDraw_: function (glArgs, glResult, cache, proxy) {
    let traits = cache.currentProgramTraits;

    // Handle program highlighting.
    if (traits & PROGRAM_HIGHLIGHT_TRAIT) {
      proxy.disableHighlighting();
    }
  }
};

/**
 * A mechanism for storing a single WebGL context's state, programs, shaders,
 * attributes or uniforms.
 *
 * @param number id
 *        The id of the window containing the WebGL context.
 * @param WebGLRenderingContext context
 *        The WebGL context for which the state is stored.
 */
function WebGLCache(id, context) {
  this._id = id;
  this._gl = context;
  this._programs = new Map();
  this.currentState = {};
}

WebGLCache.prototype = {
  _id: 0,
  _gl: null,
  _programs: null,
  _currentProgramInfo: null,
  _currentAttributesMap: null,
  _currentUniformsMap: null,

  get ownerWindow() {
    return this._id;
  },

  get ownerContext() {
    return this._gl;
  },

  /**
   * A collection of flags or properties representing the context's state.
   * Implemented as an object hash and not a Map instance because keys are
   * always either strings or numbers.
   */
  currentState: null,

  /**
   * Populates the current state with values retrieved from the context.
   *
   * @param WebGLProxy proxy
   *        The proxy methods for the WebGL context owning the state.
   */
  refreshState: function (proxy) {
    let gl = this._gl;
    let s = this.currentState;

    // Populate only with the necessary parameters. Not all default WebGL
    // state values are required.
    s[gl.BLEND] = proxy.isEnabled("BLEND");
    s.blendColor = proxy.getParameter("BLEND_COLOR");
    s.blendEquationRgb = proxy.getParameter("BLEND_EQUATION_RGB");
    s.blendEquationAlpha = proxy.getParameter("BLEND_EQUATION_ALPHA");
    s.blendSrcRgb = proxy.getParameter("BLEND_SRC_RGB");
    s.blendSrcAlpha = proxy.getParameter("BLEND_SRC_ALPHA");
    s.blendDstRgb = proxy.getParameter("BLEND_DST_RGB");
    s.blendDstAlpha = proxy.getParameter("BLEND_DST_ALPHA");
  },

  /**
   * Adds a program to the cache.
   *
   * @param WebGLProgram program
   *        The shader for which the traits are to be cached.
   * @param number traits
   *        A default properties mask set for the program.
   */
  addProgram: function (program, traits) {
    this._programs.set(program, {
      traits: traits,
      // keys are GLints (numbers)
      attributes: [],
      // keys are WebGLUniformLocations (objects)
      uniforms: new Map()
    });
  },

  /**
   * Adds a specific trait to a program. The effect of such properties is
   * determined by the consumer of this cache.
   *
   * @param WebGLProgram program
   *        The program to add the trait to.
   * @param number trait
   *        The property added to the program.
   */
  setProgramTrait: function (program, trait) {
    this._programs.get(program).traits |= trait;
  },

  /**
   * Removes a specific trait from a program.
   *
   * @param WebGLProgram program
   *        The program to remove the trait from.
   * @param number trait
   *        The property removed from the program.
   */
  unsetProgramTrait: function (program, trait) {
    this._programs.get(program).traits &= ~trait;
  },

  /**
   * Sets the currently used program in the context.
   * @param WebGLProgram program
   */
  set currentProgram(program) {
    let programInfo = this._programs.get(program);
    if (programInfo == null) {
      return;
    }
    this._currentProgramInfo = programInfo;
    this._currentAttributesMap = programInfo.attributes;
    this._currentUniformsMap = programInfo.uniforms;
  },

  /**
   * Gets the traits for the currently used program.
   * @return number
   */
  get currentProgramTraits() {
    return this._currentProgramInfo.traits;
  },

  /**
   * Adds an attribute to the cache.
   *
   * @param WebGLProgram program
   *        The program for which the attribute is bound.
   * @param string name
   *        The attribute name.
   * @param GLint value
   *        The attribute value.
   */
  addAttribute: function (program, name, value) {
    this._programs.get(program).attributes[value] = {
      name: name,
      value: value
    };
  },

  /**
   * Adds a uniform to the cache.
   *
   * @param WebGLProgram program
   *        The program for which the uniform is bound.
   * @param string name
   *        The uniform name.
   * @param WebGLUniformLocation value
   *        The uniform value.
   */
  addUniform: function (program, name, value) {
    this._programs.get(program).uniforms.set(new XPCNativeWrapper(value), {
      name: name,
      value: value
    });
  },

  /**
   * Updates the attribute locations for a specific program.
   * This is necessary, for example, when the shader is relinked and all the
   * attribute locations become obsolete.
   *
   * @param WebGLProgram program
   *        The program for which the attributes need updating.
   */
  updateAttributesForProgram: function (program) {
    let attributes = this._programs.get(program).attributes;
    for (let attribute of attributes) {
      attribute.value = this._gl.getAttribLocation(program, attribute.name);
    }
  },

  /**
   * Updates the uniform locations for a specific program.
   * This is necessary, for example, when the shader is relinked and all the
   * uniform locations become obsolete.
   *
   * @param WebGLProgram program
   *        The program for which the uniforms need updating.
   */
  updateUniformsForProgram: function (program) {
    let uniforms = this._programs.get(program).uniforms;
    for (let [, uniform] of uniforms) {
      uniform.value = this._gl.getUniformLocation(program, uniform.name);
    }
  },

  /**
   * Gets the actual attribute location in a specific program.
   * When relinked, all the attribute locations become obsolete and are updated
   * in the cache. This method returns the (current) real attribute location.
   *
   * @param GLint initialValue
   *        The initial attribute value.
   * @return GLint
   *         The current attribute value, or the initial value if it's already
   *         up to date with its corresponding program.
   */
  getCurrentAttributeLocation: function (initialValue) {
    let attributes = this._currentAttributesMap;
    let currentInfo = attributes ? attributes[initialValue] : null;
    return currentInfo ? currentInfo.value : initialValue;
  },

  /**
   * Gets the actual uniform location in a specific program.
   * When relinked, all the uniform locations become obsolete and are updated
   * in the cache. This method returns the (current) real uniform location.
   *
   * @param WebGLUniformLocation initialValue
   *        The initial uniform value.
   * @return WebGLUniformLocation
   *         The current uniform value, or the initial value if it's already
   *         up to date with its corresponding program.
   */
  getCurrentUniformLocation: function (initialValue) {
    let uniforms = this._currentUniformsMap;
    let currentInfo = uniforms ? uniforms.get(initialValue) : null;
    return currentInfo ? currentInfo.value : initialValue;
  }
};

/**
 * A mechanism for injecting or qureying state into/from a single WebGL context.
 *
 * Any interaction with a WebGL context should go through this proxy.
 * Otherwise, the corresponding observer would register the calls as coming
 * from content, which is usually not desirable. Infinite call stacks are bad.
 *
 * @param number id
 *        The id of the window containing the WebGL context.
 * @param WebGLRenderingContext context
 *        The WebGL context used for the proxy methods.
 * @param WebGLCache cache
 *        The state storage for the corresponding context.
 * @param WebGLObserver observer
 *        The observer watching function calls in the corresponding context.
 */
function WebGLProxy(id, context, cache, observer) {
  this._id = id;
  this._gl = context;
  this._cache = cache;
  this._observer = observer;

  let exports = [
    "isEnabled",
    "getParameter",
    "getAttachedShaders",
    "getShaderSource",
    "getShaderOfType",
    "compileShader",
    "enableHighlighting",
    "disableHighlighting",
    "readPixels"
  ];
  exports.forEach(e => {
    this[e] = (...args) => this._call(e, args);
  });
}

WebGLProxy.prototype = {
  _id: 0,
  _gl: null,
  _cache: null,
  _observer: null,

  get ownerWindow() {
    return this._id;
  },
  get ownerContext() {
    return this._gl;
  },

  /**
   * Test whether a WebGL capability is enabled.
   *
   * @param string name
   *        The WebGL capability name, for example "BLEND".
   * @return boolean
   *         True if enabled, false otherwise.
   */
  _isEnabled: function (name) {
    return this._gl.isEnabled(this._gl[name]);
  },

  /**
   * Returns the value for the specified WebGL parameter name.
   *
   * @param string name
   *        The WebGL parameter name, for example "BLEND_COLOR".
   * @return any
   *         The corresponding parameter's value.
   */
  _getParameter: function (name) {
    return this._gl.getParameter(this._gl[name]);
  },

  /**
   * Returns the renderbuffer property value for the specified WebGL parameter.
   * If no renderbuffer binding is available, null is returned.
   *
   * @param string name
   *        The WebGL parameter name, for example "BLEND_COLOR".
   * @return any
   *         The corresponding parameter's value.
   */
  _getRenderbufferParameter: function (name) {
    if (!this._getParameter("RENDERBUFFER_BINDING")) {
      return null;
    }
    let gl = this._gl;
    return gl.getRenderbufferParameter(gl.RENDERBUFFER, gl[name]);
  },

  /**
   * Returns the framebuffer property value for the specified WebGL parameter.
   * If no framebuffer binding is available, null is returned.
   *
   * @param string type
   *        The framebuffer object attachment point, for example "COLOR_ATTACHMENT0".
   * @param string name
   *        The WebGL parameter name, for example "FRAMEBUFFER_ATTACHMENT_OBJECT_NAME".
   *        If unspecified, defaults to "FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE".
   * @return any
   *         The corresponding parameter's value.
   */
  _getFramebufferAttachmentParameter(type, name = "FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE") {
    if (!this._getParameter("FRAMEBUFFER_BINDING")) {
      return null;
    }
    let gl = this._gl;
    return gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl[type], gl[name]);
  },

  /**
   * Returns the shader objects attached to a program object.
   *
   * @param WebGLProgram program
   *        The program for which to retrieve the attached shaders.
   * @return array
   *         The attached vertex and fragment shaders.
   */
  _getAttachedShaders: function (program) {
    return this._gl.getAttachedShaders(program);
  },

  /**
   * Returns the source code string from a shader object.
   *
   * @param WebGLShader shader
   *        The shader for which to retrieve the source code.
   * @return string
   *         The shader's source code.
   */
  _getShaderSource: function (shader) {
    return this._gl.getShaderSource(shader);
  },

  /**
   * Finds a shader of the specified type in a list.
   *
   * @param WebGLShader[] shaders
   *        The shaders for which to check the type.
   * @param string type
   *        Either "vertex" or "fragment".
   * @return WebGLShader | null
   *         The shader of the specified type, or null if nothing is found.
   */
  _getShaderOfType: function (shaders, type) {
    let gl = this._gl;
    let shaderTypeEnum = {
      vertex: gl.VERTEX_SHADER,
      fragment: gl.FRAGMENT_SHADER
    }[type];

    for (let shader of shaders) {
      if (gl.getShaderParameter(shader, gl.SHADER_TYPE) == shaderTypeEnum) {
        return shader;
      }
    }
    return null;
  },

  /**
   * Changes a shader's source code and relinks the respective program.
   *
   * @param WebGLProgram program
   *        The program who's linked shader is to be modified.
   * @param WebGLShader shader
   *        The shader to be modified.
   * @param string text
   *        The new shader source code.
   * @return object
   *         An object containing the compilation and linking status.
   */
  _compileShader: function (program, shader, text) {
    let gl = this._gl;
    gl.shaderSource(shader, text);
    gl.compileShader(shader);
    gl.linkProgram(program);

    let error = { compile: "", link: "" };

    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
      error.compile = gl.getShaderInfoLog(shader);
    }
    if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
      error.link = gl.getShaderInfoLog(shader);
    }

    this._cache.updateAttributesForProgram(program);
    this._cache.updateUniformsForProgram(program);

    return error;
  },

  /**
   * Enables color blending based on the geometry highlight tint.
   */
  _enableHighlighting: function () {
    let gl = this._gl;

    // Avoid changing the blending params when "rendering to texture".

    // Check drawing to a custom framebuffer bound to the default renderbuffer.
    let hasFramebuffer = this._getParameter("FRAMEBUFFER_BINDING");
    let hasRenderbuffer = this._getParameter("RENDERBUFFER_BINDING");
    if (hasFramebuffer && !hasRenderbuffer) {
      return;
    }

    // Check drawing to a depth or stencil component of the framebuffer.
    let writesDepth = this._getFramebufferAttachmentParameter("DEPTH_ATTACHMENT");
    let writesStencil = this._getFramebufferAttachmentParameter("STENCIL_ATTACHMENT");
    if (writesDepth || writesStencil) {
      return;
    }

    // Non-premultiplied alpha blending based on a predefined constant color.
    // Simply using gl.colorMask won't work, because we want non-tinted colors
    // to be drawn as black, not ignored.
    gl.enable(gl.BLEND);
    gl.blendColor.apply(gl, this.highlightTint);
    gl.blendEquation(gl.FUNC_ADD);
    gl.blendFunc(gl.CONSTANT_COLOR, gl.ONE_MINUS_SRC_ALPHA, gl.CONSTANT_COLOR, gl.ZERO);
    this.wasHighlighting = true;
  },

  /**
   * Disables color blending based on the geometry highlight tint, by
   * reverting the corresponding params back to their original values.
   */
  _disableHighlighting: function () {
    let gl = this._gl;
    let s = this._cache.currentState;

    gl[s[gl.BLEND] ? "enable" : "disable"](gl.BLEND);
    gl.blendColor.apply(gl, s.blendColor);
    gl.blendEquationSeparate(s.blendEquationRgb, s.blendEquationAlpha);
    gl.blendFuncSeparate(s.blendSrcRgb, s.blendDstRgb, s.blendSrcAlpha, s.blendDstAlpha);
  },

  /**
   * Returns the pixel values at the position specified on the canvas.
   */
  _readPixels: function (x, y, w, h, format, type, buffer) {
    this._gl.readPixels(x, y, w, h, format, type, buffer);
  },

  /**
   * The color tint used for highlighting geometry.
   * @see _enableHighlighting and _disableHighlighting.
   */
  highlightTint: [0, 0, 0, 0],

  /**
   * Executes a function in this object.
   *
   * This method makes sure that any handlers in the context observer are
   * suppressed, hence stopping observing any context function calls.
   *
   * @param string funcName
   *        The function to call.
   * @param array args
   *        An array of arguments.
   * @return any
   *         The called function result.
   */
  _call: function (funcName, args) {
    let prevState = this._observer.suppressHandlers;

    this._observer.suppressHandlers = true;
    let result = this["_" + funcName].apply(this, args);
    this._observer.suppressHandlers = prevState;

    return result;
  }
};

// Utility functions.

function removeFromMap(map, predicate) {
  for (let [key, value] of map) {
    if (predicate(value)) {
      map.delete(key);
    }
  }
}

function removeFromArray(array, predicate) {
  for (let i = 0; i < array.length;) {
    if (predicate(array[i])) {
      array.splice(i, 1);
    } else {
      i++;
    }
  }
}
PK
!<kkcغ	�	8chrome/devtools/modules/devtools/server/actors/window.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Ci } = require("chrome");
const Services = require("Services");
const { TabActor } = require("./tab");

/**
 * Creates a WindowActor for debugging a single window, like a browser window in Firefox,
 * but it can be used to reach any window in the process.  (Currently this is parent
 * process only because the root actor's `onGetWindow` doesn't try to cross process
 * boundaries.)  Both chrome and content windows are supported.
 *
 * Most of the implementation is inherited from TabActor.  WindowActor exposes all tab
 * actors via its form() request, like TabActor.
 *
 * You can request a specific window's actor via RootActor.getWindow().
 *
 * @param connection DebuggerServerConnection
 *        The connection to the client.
 * @param window DOMWindow
 *        The window.
 */
function WindowActor(connection, window) {
  TabActor.call(this, connection);

  let docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
                       .getInterface(Ci.nsIDocShell);
  Object.defineProperty(this, "docShell", {
    value: docShell,
    configurable: true
  });
}

WindowActor.prototype = Object.create(TabActor.prototype);

// Bug 1266561: This setting is mysteriously named, we should split up the
// functionality that is triggered by it.
WindowActor.prototype.isRootActor = true;

WindowActor.prototype.observe = function (subject, topic, data) {
  TabActor.prototype.observe.call(this, subject, topic, data);
  if (!this.attached) {
    return;
  }
  if (topic == "chrome-webnavigation-destroy") {
    this._onDocShellDestroy(subject);
  }
};

WindowActor.prototype._attach = function () {
  if (this.attached) {
    return false;
  }

  TabActor.prototype._attach.call(this);

  // Listen for chrome docshells in addition to content docshells
  if (this.docShell.itemType == Ci.nsIDocShellTreeItem.typeChrome) {
    Services.obs.addObserver(this, "chrome-webnavigation-destroy");
  }

  return true;
};

WindowActor.prototype._detach = function () {
  if (!this.attached) {
    return false;
  }

  if (this.docShell.itemType == Ci.nsIDocShellTreeItem.typeChrome) {
    Services.obs.removeObserver(this, "chrome-webnavigation-destroy");
  }

  TabActor.prototype._detach.call(this);

  return true;
};

exports.WindowActor = WindowActor;
PK
!<�};���=chrome/devtools/modules/devtools/server/actors/worker-list.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Ci } = require("chrome");
const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
loader.lazyRequireGetter(this, "WorkerActor", "devtools/server/actors/worker", true);
loader.lazyRequireGetter(this, "ServiceWorkerRegistrationActor", "devtools/server/actors/worker", true);

XPCOMUtils.defineLazyServiceGetter(
  this, "wdm",
  "@mozilla.org/dom/workers/workerdebuggermanager;1",
  "nsIWorkerDebuggerManager"
);

XPCOMUtils.defineLazyServiceGetter(
  this, "swm",
  "@mozilla.org/serviceworkers/manager;1",
  "nsIServiceWorkerManager"
);

function matchWorkerDebugger(dbg, options) {
  if ("type" in options && dbg.type !== options.type) {
    return false;
  }
  if ("window" in options) {
    let window = dbg.window;
    while (window !== null && window.parent !== window) {
      window = window.parent;
    }

    if (window !== options.window) {
      return false;
    }
  }

  return true;
}

function WorkerActorList(conn, options) {
  this._conn = conn;
  this._options = options;
  this._actors = new Map();
  this._onListChanged = null;
  this._mustNotify = false;
  this.onRegister = this.onRegister.bind(this);
  this.onUnregister = this.onUnregister.bind(this);
}

WorkerActorList.prototype = {
  getList() {
    // Create a set of debuggers.
    let dbgs = new Set();
    let e = wdm.getWorkerDebuggerEnumerator();
    while (e.hasMoreElements()) {
      let dbg = e.getNext().QueryInterface(Ci.nsIWorkerDebugger);
      if (matchWorkerDebugger(dbg, this._options)) {
        dbgs.add(dbg);
      }
    }

    // Delete each actor for which we don't have a debugger.
    for (let [dbg, ] of this._actors) {
      if (!dbgs.has(dbg)) {
        this._actors.delete(dbg);
      }
    }

    // Create an actor for each debugger for which we don't have one.
    for (let dbg of dbgs) {
      if (!this._actors.has(dbg)) {
        this._actors.set(dbg, new WorkerActor(this._conn, dbg));
      }
    }

    let actors = [];
    for (let [, actor] of this._actors) {
      actors.push(actor);
    }

    if (!this._mustNotify) {
      if (this._onListChanged !== null) {
        wdm.addListener(this);
      }
      this._mustNotify = true;
    }

    return Promise.resolve(actors);
  },

  get onListChanged() {
    return this._onListChanged;
  },

  set onListChanged(onListChanged) {
    if (typeof onListChanged !== "function" && onListChanged !== null) {
      throw new Error("onListChanged must be either a function or null.");
    }
    if (onListChanged === this._onListChanged) {
      return;
    }

    if (this._mustNotify) {
      if (this._onListChanged === null && onListChanged !== null) {
        wdm.addListener(this);
      }
      if (this._onListChanged !== null && onListChanged === null) {
        wdm.removeListener(this);
      }
    }
    this._onListChanged = onListChanged;
  },

  _notifyListChanged() {
    this._onListChanged();

    if (this._onListChanged !== null) {
      wdm.removeListener(this);
    }
    this._mustNotify = false;
  },

  onRegister(dbg) {
    if (matchWorkerDebugger(dbg, this._options)) {
      this._notifyListChanged();
    }
  },

  onUnregister(dbg) {
    if (matchWorkerDebugger(dbg, this._options)) {
      this._notifyListChanged();
    }
  }
};

exports.WorkerActorList = WorkerActorList;

function ServiceWorkerRegistrationActorList(conn) {
  this._conn = conn;
  this._actors = new Map();
  this._onListChanged = null;
  this._mustNotify = false;
  this.onRegister = this.onRegister.bind(this);
  this.onUnregister = this.onUnregister.bind(this);
}

ServiceWorkerRegistrationActorList.prototype = {
  getList() {
    // Create a set of registrations.
    let registrations = new Set();
    let array = swm.getAllRegistrations();
    for (let index = 0; index < array.length; ++index) {
      registrations.add(
        array.queryElementAt(index, Ci.nsIServiceWorkerRegistrationInfo));
    }

    // Delete each actor for which we don't have a registration.
    for (let [registration, ] of this._actors) {
      if (!registrations.has(registration)) {
        this._actors.delete(registration);
      }
    }

    // Create an actor for each registration for which we don't have one.
    for (let registration of registrations) {
      if (!this._actors.has(registration)) {
        this._actors.set(registration,
          new ServiceWorkerRegistrationActor(this._conn, registration));
      }
    }

    if (!this._mustNotify) {
      if (this._onListChanged !== null) {
        swm.addListener(this);
      }
      this._mustNotify = true;
    }

    let actors = [];
    for (let [, actor] of this._actors) {
      actors.push(actor);
    }

    return Promise.resolve(actors);
  },

  get onListchanged() {
    return this._onListchanged;
  },

  set onListChanged(onListChanged) {
    if (typeof onListChanged !== "function" && onListChanged !== null) {
      throw new Error("onListChanged must be either a function or null.");
    }

    if (this._mustNotify) {
      if (this._onListChanged === null && onListChanged !== null) {
        swm.addListener(this);
      }
      if (this._onListChanged !== null && onListChanged === null) {
        swm.removeListener(this);
      }
    }
    this._onListChanged = onListChanged;
  },

  _notifyListChanged() {
    this._onListChanged();

    if (this._onListChanged !== null) {
      swm.removeListener(this);
    }
    this._mustNotify = false;
  },

  onRegister(registration) {
    this._notifyListChanged();
  },

  onUnregister(registration) {
    this._notifyListChanged();
  }
};

exports.ServiceWorkerRegistrationActorList = ServiceWorkerRegistrationActorList;
PK
!<��Ŀ.�.8chrome/devtools/modules/devtools/server/actors/worker.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Ci } = require("chrome");
const { DebuggerServer } = require("devtools/server/main");
const Services = require("Services");
const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
const protocol = require("devtools/shared/protocol");
const {
  workerSpec,
  pushSubscriptionSpec,
  serviceWorkerRegistrationSpec,
  serviceWorkerSpec,
} = require("devtools/shared/specs/worker");

loader.lazyRequireGetter(this, "ChromeUtils");

XPCOMUtils.defineLazyServiceGetter(
  this, "swm",
  "@mozilla.org/serviceworkers/manager;1",
  "nsIServiceWorkerManager"
);

XPCOMUtils.defineLazyServiceGetter(
  this, "PushService",
  "@mozilla.org/push/Service;1",
  "nsIPushService"
);

let WorkerActor = protocol.ActorClassWithSpec(workerSpec, {
  initialize(conn, dbg) {
    protocol.Actor.prototype.initialize.call(this, conn);
    this._dbg = dbg;
    this._attached = false;
    this._threadActor = null;
    this._transport = null;
  },

  form(detail) {
    if (detail === "actorid") {
      return this.actorID;
    }
    let form = {
      actor: this.actorID,
      consoleActor: this._consoleActor,
      url: this._dbg.url,
      type: this._dbg.type
    };
    if (this._dbg.type === Ci.nsIWorkerDebugger.TYPE_SERVICE) {
      let registration = this._getServiceWorkerRegistrationInfo();
      form.scope = registration.scope;
      let newestWorker = (registration.activeWorker ||
                          registration.waitingWorker ||
                          registration.installingWorker);
      form.fetch = newestWorker && newestWorker.handlesFetchEvents;
    }
    return form;
  },

  attach() {
    if (this._dbg.isClosed) {
      return { error: "closed" };
    }

    if (!this._attached) {
      // Automatically disable their internal timeout that shut them down
      // Should be refactored by having actors specific to service workers
      if (this._dbg.type == Ci.nsIWorkerDebugger.TYPE_SERVICE) {
        let worker = this._getServiceWorkerInfo();
        if (worker) {
          worker.attachDebugger();
        }
      }
      this._dbg.addListener(this);
      this._attached = true;
    }

    return {
      type: "attached",
      url: this._dbg.url
    };
  },

  detach() {
    if (!this._attached) {
      return { error: "wrongState" };
    }

    this._detach();

    return { type: "detached" };
  },

  destroy() {
    protocol.Actor.prototype.destroy.call(this);
    if (this._attached) {
      this._detach();
    }
  },

  connect(options) {
    if (!this._attached) {
      return { error: "wrongState" };
    }

    if (this._threadActor !== null) {
      return {
        type: "connected",
        threadActor: this._threadActor
      };
    }

    return DebuggerServer.connectToWorker(
      this.conn, this._dbg, this.actorID, options
    ).then(({ threadActor, transport, consoleActor }) => {
      this._threadActor = threadActor;
      this._transport = transport;
      this._consoleActor = consoleActor;

      return {
        type: "connected",
        threadActor: this._threadActor,
        consoleActor: this._consoleActor
      };
    }, (error) => {
      return { error: error.toString() };
    });
  },

  push() {
    if (this._dbg.type !== Ci.nsIWorkerDebugger.TYPE_SERVICE) {
      return { error: "wrongType" };
    }
    let registration = this._getServiceWorkerRegistrationInfo();
    let originAttributes = ChromeUtils.originAttributesToSuffix(
      this._dbg.principal.originAttributes);
    swm.sendPushEvent(originAttributes, registration.scope);
    return { type: "pushed" };
  },

  onClose() {
    if (this._attached) {
      this._detach();
    }

    this.conn.sendActorEvent(this.actorID, "close");
  },

  onError(filename, lineno, message) {
    reportError("ERROR:" + filename + ":" + lineno + ":" + message + "\n");
  },

  _getServiceWorkerRegistrationInfo() {
    return swm.getRegistrationByPrincipal(this._dbg.principal, this._dbg.url);
  },

  _getServiceWorkerInfo() {
    let registration = this._getServiceWorkerRegistrationInfo();
    return registration.getWorkerByID(this._dbg.serviceWorkerID);
  },

  _detach() {
    if (this._threadActor !== null) {
      this._transport.close();
      this._transport = null;
      this._threadActor = null;
    }

    // If the worker is already destroyed, nsIWorkerDebugger.type throws
    // (_dbg.closed appears to be false when it throws)
    let type;
    try {
      type = this._dbg.type;
    } catch (e) {
      // nothing
    }

    if (type == Ci.nsIWorkerDebugger.TYPE_SERVICE) {
      let worker = this._getServiceWorkerInfo();
      if (worker) {
        worker.detachDebugger();
      }
    }

    this._dbg.removeListener(this);
    this._attached = false;
  }
});

exports.WorkerActor = WorkerActor;

let PushSubscriptionActor = protocol.ActorClassWithSpec(pushSubscriptionSpec, {
  initialize(conn, subscription) {
    protocol.Actor.prototype.initialize.call(this, conn);
    this._subscription = subscription;
  },

  form(detail) {
    if (detail === "actorid") {
      return this.actorID;
    }
    let subscription = this._subscription;
    return {
      actor: this.actorID,
      endpoint: subscription.endpoint,
      pushCount: subscription.pushCount,
      lastPush: subscription.lastPush,
      quota: subscription.quota
    };
  },

  destroy() {
    protocol.Actor.prototype.destroy.call(this);
    this._subscription = null;
  },
});

let ServiceWorkerActor = protocol.ActorClassWithSpec(serviceWorkerSpec, {
  initialize(conn, worker) {
    protocol.Actor.prototype.initialize.call(this, conn);
    this._worker = worker;
  },

  form() {
    if (!this._worker) {
      return null;
    }

    return {
      url: this._worker.scriptSpec,
      state: this._worker.state,
      fetch: this._worker.handlesFetchEvents
    };
  },

  destroy() {
    protocol.Actor.prototype.destroy.call(this);
    this._worker = null;
  },
});

// Lazily load the service-worker-child.js process script only once.
let _serviceWorkerProcessScriptLoaded = false;

let ServiceWorkerRegistrationActor =
protocol.ActorClassWithSpec(serviceWorkerRegistrationSpec, {
  /**
   * Create the ServiceWorkerRegistrationActor
   * @param DebuggerServerConnection conn
   *   The server connection.
   * @param ServiceWorkerRegistrationInfo registration
   *   The registration's information.
   */
  initialize(conn, registration) {
    protocol.Actor.prototype.initialize.call(this, conn);
    this._conn = conn;
    this._registration = registration;
    this._pushSubscriptionActor = null;
    this._registration.addListener(this);

    let {installingWorker, waitingWorker, activeWorker} = registration;
    this._installingWorker = new ServiceWorkerActor(conn, installingWorker);
    this._waitingWorker = new ServiceWorkerActor(conn, waitingWorker);
    this._activeWorker = new ServiceWorkerActor(conn, activeWorker);

    Services.obs.addObserver(this, PushService.subscriptionModifiedTopic);
  },

  onChange() {
    this._installingWorker.destroy();
    this._waitingWorker.destroy();
    this._activeWorker.destroy();

    let {installingWorker, waitingWorker, activeWorker} = this._registration;
    this._installingWorker = new ServiceWorkerActor(this._conn, installingWorker);
    this._waitingWorker = new ServiceWorkerActor(this._conn, waitingWorker);
    this._activeWorker = new ServiceWorkerActor(this._conn, activeWorker);

    this.emit("registration-changed");
  },

  form(detail) {
    if (detail === "actorid") {
      return this.actorID;
    }
    let registration = this._registration;
    let installingWorker = this._installingWorker.form();
    let waitingWorker = this._waitingWorker.form();
    let activeWorker = this._activeWorker.form();

    let newestWorker = (activeWorker || waitingWorker || installingWorker);

    let isE10s = Services.appinfo.browserTabsRemoteAutostart;
    return {
      actor: this.actorID,
      scope: registration.scope,
      url: registration.scriptSpec,
      installingWorker,
      waitingWorker,
      activeWorker,
      fetch: newestWorker && newestWorker.fetch,
      // - In e10s: only active registrations are available.
      // - In non-e10s: registrations always have at least one worker, if the worker is
      // active, the registration is active.
      active: isE10s ? true : !!activeWorker
    };
  },

  destroy() {
    protocol.Actor.prototype.destroy.call(this);
    Services.obs.removeObserver(this, PushService.subscriptionModifiedTopic);
    this._registration.removeListener(this);
    this._registration = null;
    if (this._pushSubscriptionActor) {
      this._pushSubscriptionActor.destroy();
    }
    this._pushSubscriptionActor = null;

    this._installingWorker.destroy();
    this._waitingWorker.destroy();
    this._activeWorker.destroy();

    this._installingWorker = null;
    this._waitingWorker = null;
    this._activeWorker = null;
  },

  /**
   * Standard observer interface to listen to push messages and changes.
   */
  observe(subject, topic, data) {
    let scope = this._registration.scope;
    if (data !== scope) {
      // This event doesn't concern us, pretend nothing happened.
      return;
    }
    switch (topic) {
      case PushService.subscriptionModifiedTopic:
        if (this._pushSubscriptionActor) {
          this._pushSubscriptionActor.destroy();
          this._pushSubscriptionActor = null;
        }
        this.emit("push-subscription-modified");
        break;
    }
  },

  start() {
    if (!_serviceWorkerProcessScriptLoaded) {
      Services.ppmm.loadProcessScript(
        "resource://devtools/server/service-worker-child.js", true);
      _serviceWorkerProcessScriptLoaded = true;
    }

    // XXX: Send the permissions down to the content process before starting
    // the service worker within the content process. As we don't know what
    // content process we're starting the service worker in (as we're using a
    // broadcast channel to talk to it), we just broadcast the permissions to
    // everyone as well.
    //
    // This call should be replaced with a proper implementation when
    // ServiceWorker debugging is improved to support multiple content processes
    // correctly.
    Services.perms.broadcastPermissionsForPrincipalToAllContentProcesses(
      this._registration.principal);

    Services.ppmm.broadcastAsyncMessage("serviceWorkerRegistration:start", {
      scope: this._registration.scope
    });
    return { type: "started" };
  },

  unregister() {
    let { principal, scope } = this._registration;
    let unregisterCallback = {
      unregisterSucceeded: function () {},
      unregisterFailed: function () {
        console.error("Failed to unregister the service worker for " + scope);
      },
      QueryInterface: XPCOMUtils.generateQI(
        [Ci.nsIServiceWorkerUnregisterCallback])
    };
    swm.propagateUnregister(principal, unregisterCallback, scope);

    return { type: "unregistered" };
  },

  getPushSubscription() {
    let registration = this._registration;
    let pushSubscriptionActor = this._pushSubscriptionActor;
    if (pushSubscriptionActor) {
      return Promise.resolve(pushSubscriptionActor);
    }
    return new Promise((resolve, reject) => {
      PushService.getSubscription(
        registration.scope,
        registration.principal,
        (result, subscription) => {
          if (!subscription) {
            resolve(null);
            return;
          }
          pushSubscriptionActor = new PushSubscriptionActor(this._conn, subscription);
          this._pushSubscriptionActor = pushSubscriptionActor;
          resolve(pushSubscriptionActor);
        }
      );
    });
  },
});

exports.ServiceWorkerRegistrationActor = ServiceWorkerRegistrationActor;
PK
!<���rr0chrome/devtools/modules/devtools/server/child.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 addEventListener, addMessageListener, removeMessageListener, sendAsyncMessage */

try {
  var chromeGlobal = this;

  // Encapsulate in its own scope to allows loading this frame script more than once.
  (function () {
    const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});

    const DevToolsUtils = require("devtools/shared/DevToolsUtils");
    const { dumpn } = DevToolsUtils;
    const { DebuggerServer, ActorPool } = require("devtools/server/main");

    DebuggerServer.init();
    // We want a special server without any root actor and only tab actors.
    // We are going to spawn a ContentActor instance in the next few lines,
    // it is going to act like a root actor without being one.
    DebuggerServer.registerActors({ tab: true });

    let connections = new Map();

    let onConnect = DevToolsUtils.makeInfallible(function (msg) {
      removeMessageListener("debug:connect", onConnect);

      let mm = msg.target;
      let prefix = msg.data.prefix;
      let addonId = msg.data.addonId;

      // Using the JS debugger causes problems when we're trying to
      // schedule those zone groups across different threads. Calling
      // blockThreadedExecution causes Gecko to switch to a simpler
      // single-threaded model until unblockThreadedExecution is
      // called later. We cannot start the debugger until the callback
      // passed to blockThreadedExecution has run, signaling that
      // we're running single-threaded.
      Cu.blockThreadedExecution(() => {
        let conn = DebuggerServer.connectToParent(prefix, mm);
        conn.parentMessageManager = mm;
        connections.set(prefix, conn);

        let actor;

        if (addonId) {
          const { WebExtensionChildActor } = require("devtools/server/actors/webextension");
          actor = new WebExtensionChildActor(conn, chromeGlobal, prefix, addonId);
        } else {
          const { ContentActor } = require("devtools/server/actors/content");
          actor = new ContentActor(conn, chromeGlobal, prefix);
        }

        let actorPool = new ActorPool(conn);
        actorPool.addActor(actor);
        conn.addActorPool(actorPool);

        sendAsyncMessage("debug:actor", {actor: actor.form(), prefix: prefix});
      });
    });

    addMessageListener("debug:connect", onConnect);

    // Allows executing module setup helper from the parent process.
    // See also: DebuggerServer.setupInChild()
    let onSetupInChild = DevToolsUtils.makeInfallible(msg => {
      let { module, setupChild, args } = msg.data;
      let m;

      try {
        m = require(module);

        if (!(setupChild in m)) {
          dumpn(`ERROR: module '${module}' does not export '${setupChild}'`);
          return false;
        }

        m[setupChild].apply(m, args);
      } catch (e) {
        let errorMessage =
          "Exception during actor module setup running in the child process: ";
        DevToolsUtils.reportException(errorMessage + e);
        dumpn(`ERROR: ${errorMessage}\n\t module: '${module}'\n\t ` +
              `setupChild: '${setupChild}'\n${DevToolsUtils.safeErrorString(e)}`);
        return false;
      }
      if (msg.data.id) {
        // Send a message back to know when it is processed
        sendAsyncMessage("debug:setup-in-child-response", {id: msg.data.id});
      }
      return true;
    });

    addMessageListener("debug:setup-in-child", onSetupInChild);

    let onDisconnect = DevToolsUtils.makeInfallible(function (msg) {
      let prefix = msg.data.prefix;
      let conn = connections.get(prefix);
      if (!conn) {
        // Several copies of this frame script can be running for a single frame since it
        // is loaded once for each DevTools connection to the frame.  If this disconnect
        // request doesn't match a connection known here, ignore it.
        return;
      }

      Cu.unblockThreadedExecution();

      removeMessageListener("debug:disconnect", onDisconnect);
      // Call DebuggerServerConnection.close to destroy all child actors. It should end up
      // calling DebuggerServerConnection.onClosed that would actually cleanup all actor
      // pools.
      conn.close();
      connections.delete(prefix);
    });
    addMessageListener("debug:disconnect", onDisconnect);

    // In non-e10s mode, the "debug:disconnect" message isn't always received before the
    // messageManager connection goes away.  Watching for "unload" here ensures we close
    // any connections when the frame is unloaded.
    addEventListener("unload", () => {
      for (let conn of connections.values()) {
        conn.close();
      }
      connections.clear();
    });
  })();
} catch (e) {
  dump(`Exception in app child process: ${e}\n`);
}
PK
!<X�-��Jchrome/devtools/modules/devtools/server/content-process-debugger-server.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 addMessageListener, removeMessageListener */

"use strict";

const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm", {});

function onInit(message) {
  // Only reply if we are in a real content process
  if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
    let {init} = ChromeUtils.import("resource://devtools/server/content-server.jsm", {});
    init(message);
  }
}

function onClose() {
  removeMessageListener("debug:init-content-server", onInit);
  removeMessageListener("debug:close-content-server", onClose);
}

addMessageListener("debug:init-content-server", onInit);
addMessageListener("debug:close-content-server", onClose);
PK
!<wE���:chrome/devtools/modules/devtools/server/content-server.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

/* exported init */
this.EXPORTED_SYMBOLS = ["init"];

let gLoader;

function setupServer(mm) {
  // Prevent spawning multiple server per process, even if the caller call us
  // multiple times
  if (gLoader) {
    return gLoader;
  }

  // Lazy load Loader.jsm to prevent loading any devtools dependency too early.
  let { DevToolsLoader } =
    ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});

  // Init a custom, invisible DebuggerServer, in order to not pollute the
  // debugger with all devtools modules, nor break the debugger itself with
  // using it in the same process.
  gLoader = new DevToolsLoader();
  gLoader.invisibleToDebugger = true;
  let { DebuggerServer } = gLoader.require("devtools/server/main");

  DebuggerServer.init();
  // For browser content toolbox, we do need a regular root actor and all tab
  // actors, but don't need all the "browser actors" that are only useful when
  // debugging the parent process via the browser toolbox.
  DebuggerServer.registerActors({ root: true, tab: true });

  // Clean up things when the client disconnects
  mm.addMessageListener("debug:content-process-destroy", function onDestroy() {
    mm.removeMessageListener("debug:content-process-destroy", onDestroy);

    Cu.unblockThreadedExecution();

    DebuggerServer.destroy();
    gLoader.destroy();
    gLoader = null;
  });

  return gLoader;
}

function init(msg) {
  let mm = msg.target;
  mm.QueryInterface(Ci.nsISyncMessageSender);
  let prefix = msg.data.prefix;

  // Using the JS debugger causes problems when we're trying to
  // schedule those zone groups across different threads. Calling
  // blockThreadedExecution causes Gecko to switch to a simpler
  // single-threaded model until unblockThreadedExecution is called
  // later. We cannot start the debugger until the callback passed to
  // blockThreadedExecution has run, signaling that we're running
  // single-threaded.
  Cu.blockThreadedExecution(() => {
    // Setup a server if none started yet
    let loader = setupServer(mm);

    // Connect both parent/child processes debugger servers RDP via message
    // managers
    let { DebuggerServer } = loader.require("devtools/server/main");
    let conn = DebuggerServer.connectToParent(prefix, mm);
    conn.parentMessageManager = mm;

    let { ChildProcessActor } =
        loader.require("devtools/server/actors/child-process");
    let { ActorPool } = loader.require("devtools/server/main");
    let actor = new ChildProcessActor(conn);
    let actorPool = new ActorPool(conn);
    actorPool.addActor(actor);
    conn.addActorPool(actorPool);

    let response = { actor: actor.form() };
    mm.sendAsyncMessage("debug:content-process-actor", response);
  });
}
PK
!<����4chrome/devtools/modules/devtools/server/css-logic.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=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/. */

/*
 * About the objects defined in this file:
 * - CssLogic contains style information about a view context. It provides
 *   access to 2 sets of objects: Css[Sheet|Rule|Selector] provide access to
 *   information that does not change when the selected element changes while
 *   Css[Property|Selector]Info provide information that is dependent on the
 *   selected element.
 *   Its key methods are highlight(), getPropertyInfo() and forEachSheet(), etc
 *
 * - CssSheet provides a more useful API to a DOM CSSSheet for our purposes,
 *   including shortSource and href.
 * - CssRule a more useful API to a DOM CSSRule including access to the group
 *   of CssSelectors that the rule provides properties for
 * - CssSelector A single selector - i.e. not a selector group. In other words
 *   a CssSelector does not contain ','. This terminology is different from the
 *   standard DOM API, but more inline with the definition in the spec.
 *
 * - CssPropertyInfo contains style information for a single property for the
 *   highlighted element.
 * - CssSelectorInfo is a wrapper around CssSelector, which adds sorting with
 *   reference to the selected element.
 */

"use strict";

const { Cu } = require("chrome");
const nodeConstants = require("devtools/shared/dom-node-constants");
const {
  getBindingElementAndPseudo,
  getCSSStyleRules,
  l10n,
  isContentStylesheet,
  shortSource,
  FILTER,
  STATUS
} = require("devtools/shared/inspector/css-logic");
const InspectorUtils = require("InspectorUtils");

/**
 * @param {function} isInherited A function that determines if the CSS property
 *                   is inherited.
 */
function CssLogic(isInherited) {
  // The cache of examined CSS properties.
  this._isInherited = isInherited;
  this._propertyInfos = {};
}

exports.CssLogic = CssLogic;

CssLogic.prototype = {
  // Both setup by highlight().
  viewedElement: null,
  viewedDocument: null,

  // The cache of the known sheets.
  _sheets: null,

  // Have the sheets been cached?
  _sheetsCached: false,

  // The total number of rules, in all stylesheets, after filtering.
  _ruleCount: 0,

  // The computed styles for the viewedElement.
  _computedStyle: null,

  // Source filter. Only display properties coming from the given source
  _sourceFilter: FILTER.USER,

  // Used for tracking unique CssSheet/CssRule/CssSelector objects, in a run of
  // processMatchedSelectors().
  _passId: 0,

  // Used for tracking matched CssSelector objects.
  _matchId: 0,

  _matchedRules: null,
  _matchedSelectors: null,

  // Cached keyframes rules in all stylesheets
  _keyframesRules: null,

  /**
   * Reset various properties
   */
  reset: function () {
    this._propertyInfos = {};
    this._ruleCount = 0;
    this._sheetIndex = 0;
    this._sheets = {};
    this._sheetsCached = false;
    this._matchedRules = null;
    this._matchedSelectors = null;
    this._keyframesRules = [];
  },

  /**
   * Focus on a new element - remove the style caches.
   *
   * @param {nsIDOMElement} aViewedElement the element the user has highlighted
   * in the Inspector.
   */
  highlight: function (viewedElement) {
    if (!viewedElement) {
      this.viewedElement = null;
      this.viewedDocument = null;
      this._computedStyle = null;
      this.reset();
      return;
    }

    if (viewedElement === this.viewedElement) {
      return;
    }

    this.viewedElement = viewedElement;

    let doc = this.viewedElement.ownerDocument;
    if (doc != this.viewedDocument) {
      // New document: clear/rebuild the cache.
      this.viewedDocument = doc;

      // Hunt down top level stylesheets, and cache them.
      this._cacheSheets();
    } else {
      // Clear cached data in the CssPropertyInfo objects.
      this._propertyInfos = {};
    }

    this._matchedRules = null;
    this._matchedSelectors = null;
    this._computedStyle = CssLogic.getComputedStyle(this.viewedElement);
  },

  /**
   * Get the values of all the computed CSS properties for the highlighted
   * element.
   * @returns {object} The computed CSS properties for a selected element
   */
  get computedStyle() {
    return this._computedStyle;
  },

  /**
   * Get the source filter.
   * @returns {string} The source filter being used.
   */
  get sourceFilter() {
    return this._sourceFilter;
  },

  /**
   * Source filter. Only display properties coming from the given source (web
   * address). Note that in order to avoid information overload we DO NOT show
   * unmatched system rules.
   * @see FILTER.*
   */
  set sourceFilter(value) {
    let oldValue = this._sourceFilter;
    this._sourceFilter = value;

    let ruleCount = 0;

    // Update the CssSheet objects.
    this.forEachSheet(function (sheet) {
      sheet._sheetAllowed = -1;
      if (sheet.contentSheet && sheet.sheetAllowed) {
        ruleCount += sheet.ruleCount;
      }
    }, this);

    this._ruleCount = ruleCount;

    // Full update is needed because the this.processMatchedSelectors() method
    // skips UA stylesheets if the filter does not allow such sheets.
    let needFullUpdate = (oldValue == FILTER.UA || value == FILTER.UA);

    if (needFullUpdate) {
      this._matchedRules = null;
      this._matchedSelectors = null;
      this._propertyInfos = {};
    } else {
      // Update the CssPropertyInfo objects.
      for (let property in this._propertyInfos) {
        this._propertyInfos[property].needRefilter = true;
      }
    }
  },

  /**
   * Return a CssPropertyInfo data structure for the currently viewed element
   * and the specified CSS property. If there is no currently viewed element we
   * return an empty object.
   *
   * @param {string} property The CSS property to look for.
   * @return {CssPropertyInfo} a CssPropertyInfo structure for the given
   * property.
   */
  getPropertyInfo: function (property) {
    if (!this.viewedElement) {
      return {};
    }

    let info = this._propertyInfos[property];
    if (!info) {
      info = new CssPropertyInfo(this, property, this._isInherited);
      this._propertyInfos[property] = info;
    }

    return info;
  },

  /**
   * Cache all the stylesheets in the inspected document
   * @private
   */
  _cacheSheets: function () {
    this._passId++;
    this.reset();

    // styleSheets isn't an array, but forEach can work on it anyway
    Array.prototype.forEach.call(this.viewedDocument.styleSheets,
        this._cacheSheet, this);

    this._sheetsCached = true;
  },

  /**
   * Cache a stylesheet if it falls within the requirements: if it's enabled,
   * and if the @media is allowed. This method also walks through the stylesheet
   * cssRules to find @imported rules, to cache the stylesheets of those rules
   * as well. In addition, the @keyframes rules in the stylesheet are cached.
   *
   * @private
   * @param {CSSStyleSheet} domSheet the CSSStyleSheet object to cache.
   */
  _cacheSheet: function (domSheet) {
    if (domSheet.disabled) {
      return;
    }

    // Only work with stylesheets that have their media allowed.
    if (!this.mediaMatches(domSheet)) {
      return;
    }

    // Cache the sheet.
    let cssSheet = this.getSheet(domSheet, this._sheetIndex++);
    if (cssSheet._passId != this._passId) {
      cssSheet._passId = this._passId;

      // Find import and keyframes rules.
      for (let aDomRule of cssSheet.getCssRules()) {
        if (aDomRule.type == CSSRule.IMPORT_RULE &&
            aDomRule.styleSheet &&
            this.mediaMatches(aDomRule)) {
          this._cacheSheet(aDomRule.styleSheet);
        } else if (aDomRule.type == CSSRule.KEYFRAMES_RULE) {
          this._keyframesRules.push(aDomRule);
        }
      }
    }
  },

  /**
   * Retrieve the list of stylesheets in the document.
   *
   * @return {array} the list of stylesheets in the document.
   */
  get sheets() {
    if (!this._sheetsCached) {
      this._cacheSheets();
    }

    let sheets = [];
    this.forEachSheet(function (sheet) {
      if (sheet.contentSheet) {
        sheets.push(sheet);
      }
    }, this);

    return sheets;
  },

  /**
   * Retrieve the list of keyframes rules in the document.
   *
   * @ return {array} the list of keyframes rules in the document.
   */
  get keyframesRules() {
    if (!this._sheetsCached) {
      this._cacheSheets();
    }
    return this._keyframesRules;
  },

  /**
   * Retrieve a CssSheet object for a given a CSSStyleSheet object. If the
   * stylesheet is already cached, you get the existing CssSheet object,
   * otherwise the new CSSStyleSheet object is cached.
   *
   * @param {CSSStyleSheet} domSheet the CSSStyleSheet object you want.
   * @param {number} index the index, within the document, of the stylesheet.
   *
   * @return {CssSheet} the CssSheet object for the given CSSStyleSheet object.
   */
  getSheet: function (domSheet, index) {
    let cacheId = "";

    if (domSheet.href) {
      cacheId = domSheet.href;
    } else if (domSheet.ownerNode && domSheet.ownerNode.ownerDocument) {
      cacheId = domSheet.ownerNode.ownerDocument.location;
    }

    let sheet = null;
    let sheetFound = false;

    if (cacheId in this._sheets) {
      for (let i = 0, numSheets = this._sheets[cacheId].length;
           i < numSheets;
           i++) {
        sheet = this._sheets[cacheId][i];
        if (sheet.domSheet === domSheet) {
          if (index != -1) {
            sheet.index = index;
          }
          sheetFound = true;
          break;
        }
      }
    }

    if (!sheetFound) {
      if (!(cacheId in this._sheets)) {
        this._sheets[cacheId] = [];
      }

      sheet = new CssSheet(this, domSheet, index);
      if (sheet.sheetAllowed && sheet.contentSheet) {
        this._ruleCount += sheet.ruleCount;
      }

      this._sheets[cacheId].push(sheet);
    }

    return sheet;
  },

  /**
   * Process each cached stylesheet in the document using your callback.
   *
   * @param {function} callback the function you want executed for each of the
   * CssSheet objects cached.
   * @param {object} scope the scope you want for the callback function. scope
   * will be the this object when callback executes.
   */
  forEachSheet: function (callback, scope) {
    for (let cacheId in this._sheets) {
      let sheets = this._sheets[cacheId];
      for (let i = 0; i < sheets.length; i++) {
        // We take this as an opportunity to clean dead sheets
        try {
          let sheet = sheets[i];
          // If accessing domSheet raises an exception, then the style
          // sheet is a dead object.
          sheet.domSheet;
          callback.call(scope, sheet, i, sheets);
        } catch (e) {
          sheets.splice(i, 1);
          i--;
        }
      }
    }
  },

  /**

  /**
   * Get the number CSSRule objects in the document, counted from all of
   * the stylesheets. System sheets are excluded. If a filter is active, this
   * tells only the number of CSSRule objects inside the selected
   * CSSStyleSheet.
   *
   * WARNING: This only provides an estimate of the rule count, and the results
   * could change at a later date. Todo remove this
   *
   * @return {number} the number of CSSRule (all rules).
   */
  get ruleCount() {
    if (!this._sheetsCached) {
      this._cacheSheets();
    }

    return this._ruleCount;
  },

  /**
   * Process the CssSelector objects that match the highlighted element and its
   * parent elements. scope.callback() is executed for each CssSelector
   * object, being passed the CssSelector object and the match status.
   *
   * This method also includes all of the element.style properties, for each
   * highlighted element parent and for the highlighted element itself.
   *
   * Note that the matched selectors are cached, such that next time your
   * callback is invoked for the cached list of CssSelector objects.
   *
   * @param {function} callback the function you want to execute for each of
   * the matched selectors.
   * @param {object} scope the scope you want for the callback function. scope
   * will be the this object when callback executes.
   */
  processMatchedSelectors: function (callback, scope) {
    if (this._matchedSelectors) {
      if (callback) {
        this._passId++;
        this._matchedSelectors.forEach(function (value) {
          callback.call(scope, value[0], value[1]);
          value[0].cssRule._passId = this._passId;
        }, this);
      }
      return;
    }

    if (!this._matchedRules) {
      this._buildMatchedRules();
    }

    this._matchedSelectors = [];
    this._passId++;

    for (let i = 0; i < this._matchedRules.length; i++) {
      let rule = this._matchedRules[i][0];
      let status = this._matchedRules[i][1];

      rule.selectors.forEach(function (selector) {
        if (selector._matchId !== this._matchId &&
           (selector.elementStyle ||
            this.selectorMatchesElement(rule.domRule,
                                        selector.selectorIndex))) {
          selector._matchId = this._matchId;
          this._matchedSelectors.push([ selector, status ]);
          if (callback) {
            callback.call(scope, selector, status);
          }
        }
      }, this);

      rule._passId = this._passId;
    }
  },

  /**
   * Check if the given selector matches the highlighted element or any of its
   * parents.
   *
   * @private
   * @param {DOMRule} domRule
   *        The DOM Rule containing the selector.
   * @param {Number} idx
   *        The index of the selector within the DOMRule.
   * @return {boolean}
   *         true if the given selector matches the highlighted element or any
   *         of its parents, otherwise false is returned.
   */
  selectorMatchesElement: function (domRule, idx) {
    let element = this.viewedElement;
    do {
      if (InspectorUtils.selectorMatchesElement(element, domRule, idx)) {
        return true;
      }
    } while ((element = element.parentNode) &&
             element.nodeType === nodeConstants.ELEMENT_NODE);

    return false;
  },

  /**
   * Check if the highlighted element or it's parents have matched selectors.
   *
   * @param {array} aProperties The list of properties you want to check if they
   * have matched selectors or not.
   * @return {object} An object that tells for each property if it has matched
   * selectors or not. Object keys are property names and values are booleans.
   */
  hasMatchedSelectors: function (properties) {
    if (!this._matchedRules) {
      this._buildMatchedRules();
    }

    let result = {};

    this._matchedRules.some(function (value) {
      let rule = value[0];
      let status = value[1];
      properties = properties.filter((property) => {
        // We just need to find if a rule has this property while it matches
        // the viewedElement (or its parents).
        if (rule.getPropertyValue(property) &&
            (status == STATUS.MATCHED ||
             (status == STATUS.PARENT_MATCH &&
              this._isInherited(property)))) {
          result[property] = true;
          return false;
        }
        // Keep the property for the next rule.
        return true;
      });
      return properties.length == 0;
    }, this);

    return result;
  },

  /**
   * Build the array of matched rules for the currently highlighted element.
   * The array will hold rules that match the viewedElement and its parents.
   *
   * @private
   */
  _buildMatchedRules: function () {
    let domRules;
    let element = this.viewedElement;
    let filter = this.sourceFilter;
    let sheetIndex = 0;

    this._matchId++;
    this._passId++;
    this._matchedRules = [];

    if (!element) {
      return;
    }

    do {
      let status = this.viewedElement === element ?
                   STATUS.MATCHED : STATUS.PARENT_MATCH;

      try {
        domRules = getCSSStyleRules(element);
      } catch (ex) {
        console.log("CL__buildMatchedRules error: " + ex);
        continue;
      }

      // getCSSStyleRules can return null with a shadow DOM element.
      let numDomRules = domRules ? domRules.length : 0;
      for (let i = 0; i < numDomRules; i++) {
        let domRule = domRules[i];
        if (domRule.type !== CSSRule.STYLE_RULE) {
          continue;
        }

        let sheet = this.getSheet(domRule.parentStyleSheet, -1);
        if (sheet._passId !== this._passId) {
          sheet.index = sheetIndex++;
          sheet._passId = this._passId;
        }

        if (filter === FILTER.USER && !sheet.contentSheet) {
          continue;
        }

        let rule = sheet.getRule(domRule);
        if (rule._passId === this._passId) {
          continue;
        }

        rule._matchId = this._matchId;
        rule._passId = this._passId;
        this._matchedRules.push([rule, status]);
      }

      // Add element.style information.
      if (element.style && element.style.length > 0) {
        let rule = new CssRule(null, { style: element.style }, element);
        rule._matchId = this._matchId;
        rule._passId = this._passId;
        this._matchedRules.push([rule, status]);
      }
    } while ((element = element.parentNode) &&
              element.nodeType === nodeConstants.ELEMENT_NODE);
  },

  /**
   * Tells if the given DOM CSS object matches the current view media.
   *
   * @param {object} domObject The DOM CSS object to check.
   * @return {boolean} True if the DOM CSS object matches the current view
   * media, or false otherwise.
   */
  mediaMatches: function (domObject) {
    let mediaText = domObject.media.mediaText;
    return !mediaText ||
      this.viewedDocument.defaultView.matchMedia(mediaText).matches;
  },
};

/**
 * If the element has an id, return '#id'. Otherwise return 'tagname[n]' where
 * n is the index of this element in its siblings.
 * <p>A technically more 'correct' output from the no-id case might be:
 * 'tagname:nth-of-type(n)' however this is unlikely to be more understood
 * and it is longer.
 *
 * @param {nsIDOMElement} element the element for which you want the short name.
 * @return {string} the string to be displayed for element.
 */
CssLogic.getShortName = function (element) {
  if (!element) {
    return "null";
  }
  if (element.id) {
    return "#" + element.id;
  }
  let priorSiblings = 0;
  let temp = element;
  while ((temp = temp.previousElementSibling)) {
    priorSiblings++;
  }
  return element.tagName + "[" + priorSiblings + "]";
};

/**
 * Get a string list of selectors for a given DOMRule.
 *
 * @param {DOMRule} domRule
 *        The DOMRule to parse.
 * @return {Array}
 *         An array of string selectors.
 */
CssLogic.getSelectors = function (domRule) {
  let selectors = [];

  let len = InspectorUtils.getSelectorCount(domRule);
  for (let i = 0; i < len; i++) {
    let text = InspectorUtils.getSelectorText(domRule, i);
    selectors.push(text);
  }
  return selectors;
};

/**
 * Given a node, check to see if it is a ::before or ::after element.
 * If so, return the node that is accessible from within the document
 * (the parent of the anonymous node), along with which pseudo element
 * it was.  Otherwise, return the node itself.
 *
 * @returns {Object}
 *            - {DOMNode} node The non-anonymous node
 *            - {string} pseudo One of ':before', ':after', or null.
 */
CssLogic.getBindingElementAndPseudo = getBindingElementAndPseudo;

/**
 * Get the computed style on a node.  Automatically handles reading
 * computed styles on a ::before/::after element by reading on the
 * parent node with the proper pseudo argument.
 *
 * @param {Node}
 * @returns {CSSStyleDeclaration}
 */
CssLogic.getComputedStyle = function (node) {
  if (!node ||
      Cu.isDeadWrapper(node) ||
      node.nodeType !== nodeConstants.ELEMENT_NODE ||
      !node.ownerGlobal) {
    return null;
  }

  let {bindingElement, pseudo} = CssLogic.getBindingElementAndPseudo(node);
  return node.ownerGlobal.getComputedStyle(bindingElement, pseudo);
};

/**
 * Get a source for a stylesheet, taking into account embedded stylesheets
 * for which we need to use document.defaultView.location.href rather than
 * sheet.href
 *
 * @param {CSSStyleSheet} sheet the DOM object for the style sheet.
 * @return {string} the address of the stylesheet.
 */
CssLogic.href = function (sheet) {
  let href = sheet.href;
  if (!href) {
    href = sheet.ownerNode.ownerDocument.location;
  }

  return href;
};

/**
 * A safe way to access cached bits of information about a stylesheet.
 *
 * @constructor
 * @param {CssLogic} cssLogic pointer to the CssLogic instance working with
 * this CssSheet object.
 * @param {CSSStyleSheet} domSheet reference to a DOM CSSStyleSheet object.
 * @param {number} index tells the index/position of the stylesheet within the
 * main document.
 */
function CssSheet(cssLogic, domSheet, index) {
  this._cssLogic = cssLogic;
  this.domSheet = domSheet;
  this.index = this.contentSheet ? index : -100 * index;

  // Cache of the sheets href. Cached by the getter.
  this._href = null;
  // Short version of href for use in select boxes etc. Cached by getter.
  this._shortSource = null;

  // null for uncached.
  this._sheetAllowed = null;

  // Cached CssRules from the given stylesheet.
  this._rules = {};

  this._ruleCount = -1;
}

CssSheet.prototype = {
  _passId: null,
  _contentSheet: null,

  /**
   * Tells if the stylesheet is provided by the browser or not.
   *
   * @return {boolean} false if this is a browser-provided stylesheet, or true
   * otherwise.
   */
  get contentSheet() {
    if (this._contentSheet === null) {
      this._contentSheet = isContentStylesheet(this.domSheet);
    }
    return this._contentSheet;
  },

  /**
   * Tells if the stylesheet is disabled or not.
   * @return {boolean} true if this stylesheet is disabled, or false otherwise.
   */
  get disabled() {
    return this.domSheet.disabled;
  },

  /**
   * Get a source for a stylesheet, using CssLogic.href
   *
   * @return {string} the address of the stylesheet.
   */
  get href() {
    if (this._href) {
      return this._href;
    }

    this._href = CssLogic.href(this.domSheet);
    return this._href;
  },

  /**
   * Create a shorthand version of the href of a stylesheet.
   *
   * @return {string} the shorthand source of the stylesheet.
   */
  get shortSource() {
    if (this._shortSource) {
      return this._shortSource;
    }

    this._shortSource = shortSource(this.domSheet);
    return this._shortSource;
  },

  /**
   * Tells if the sheet is allowed or not by the current CssLogic.sourceFilter.
   *
   * @return {boolean} true if the stylesheet is allowed by the sourceFilter, or
   * false otherwise.
   */
  get sheetAllowed() {
    if (this._sheetAllowed !== null) {
      return this._sheetAllowed;
    }

    this._sheetAllowed = true;

    let filter = this._cssLogic.sourceFilter;
    if (filter === FILTER.USER && !this.contentSheet) {
      this._sheetAllowed = false;
    }
    if (filter !== FILTER.USER && filter !== FILTER.UA) {
      this._sheetAllowed = (filter === this.href);
    }

    return this._sheetAllowed;
  },

  /**
   * Retrieve the number of rules in this stylesheet.
   *
   * @return {number} the number of CSSRule objects in this stylesheet.
   */
  get ruleCount() {
    try {
      return this._ruleCount > -1 ?
        this._ruleCount :
        this.getCssRules().length;
    } catch (e) {
      return 0;
    }
  },

  /**
   * Retrieve the array of css rules for this stylesheet.
   *
   * Accessing cssRules on a stylesheet that is not completely loaded can throw a
   * DOMException (Bug 625013). This wrapper will return an empty array instead.
   *
   * @return {Array} array of css rules.
   **/
  getCssRules: function () {
    try {
      return this.domSheet.cssRules;
    } catch (e) {
      return [];
    }
  },

  /**
   * Retrieve a CssRule object for the given CSSStyleRule. The CssRule object is
   * cached, such that subsequent retrievals return the same CssRule object for
   * the same CSSStyleRule object.
   *
   * @param {CSSStyleRule} aDomRule the CSSStyleRule object for which you want a
   * CssRule object.
   * @return {CssRule} the cached CssRule object for the given CSSStyleRule
   * object.
   */
  getRule: function (domRule) {
    let cacheId = domRule.type + domRule.selectorText;

    let rule = null;
    let ruleFound = false;

    if (cacheId in this._rules) {
      for (let i = 0, rulesLen = this._rules[cacheId].length;
           i < rulesLen;
           i++) {
        rule = this._rules[cacheId][i];
        if (rule.domRule === domRule) {
          ruleFound = true;
          break;
        }
      }
    }

    if (!ruleFound) {
      if (!(cacheId in this._rules)) {
        this._rules[cacheId] = [];
      }

      rule = new CssRule(this, domRule);
      this._rules[cacheId].push(rule);
    }

    return rule;
  },

  toString: function () {
    return "CssSheet[" + this.shortSource + "]";
  }
};

/**
 * Information about a single CSSStyleRule.
 *
 * @param {CSSSheet|null} cssSheet the CssSheet object of the stylesheet that
 * holds the CSSStyleRule. If the rule comes from element.style, set this
 * argument to null.
 * @param {CSSStyleRule|object} domRule the DOM CSSStyleRule for which you want
 * to cache data. If the rule comes from element.style, then provide
 * an object of the form: {style: element.style}.
 * @param {Element} [element] If the rule comes from element.style, then this
 * argument must point to the element.
 * @constructor
 */
function CssRule(cssSheet, domRule, element) {
  this._cssSheet = cssSheet;
  this.domRule = domRule;

  let parentRule = domRule.parentRule;
  if (parentRule && parentRule.type == CSSRule.MEDIA_RULE) {
    this.mediaText = parentRule.media.mediaText;
  }

  if (this._cssSheet) {
    // parse domRule.selectorText on call to this.selectors
    this._selectors = null;
    this.line = InspectorUtils.getRuleLine(this.domRule);
    this.column = InspectorUtils.getRuleColumn(this.domRule);
    this.source = this._cssSheet.shortSource + ":" + this.line;
    if (this.mediaText) {
      this.source += " @media " + this.mediaText;
    }
    this.href = this._cssSheet.href;
    this.contentRule = this._cssSheet.contentSheet;
  } else if (element) {
    this._selectors = [ new CssSelector(this, "@element.style", 0) ];
    this.line = -1;
    this.source = l10n("rule.sourceElement");
    this.href = "#";
    this.contentRule = true;
    this.sourceElement = element;
  }
}

CssRule.prototype = {
  _passId: null,

  mediaText: "",

  get isMediaRule() {
    return !!this.mediaText;
  },

  /**
   * Check if the parent stylesheet is allowed by the CssLogic.sourceFilter.
   *
   * @return {boolean} true if the parent stylesheet is allowed by the current
   * sourceFilter, or false otherwise.
   */
  get sheetAllowed() {
    return this._cssSheet ? this._cssSheet.sheetAllowed : true;
  },

  /**
   * Retrieve the parent stylesheet index/position in the viewed document.
   *
   * @return {number} the parent stylesheet index/position in the viewed
   * document.
   */
  get sheetIndex() {
    return this._cssSheet ? this._cssSheet.index : 0;
  },

  /**
   * Retrieve the style property value from the current CSSStyleRule.
   *
   * @param {string} property the CSS property name for which you want the
   * value.
   * @return {string} the property value.
   */
  getPropertyValue: function (property) {
    return this.domRule.style.getPropertyValue(property);
  },

  /**
   * Retrieve the style property priority from the current CSSStyleRule.
   *
   * @param {string} property the CSS property name for which you want the
   * priority.
   * @return {string} the property priority.
   */
  getPropertyPriority: function (property) {
    return this.domRule.style.getPropertyPriority(property);
  },

  /**
   * Retrieve the list of CssSelector objects for each of the parsed selectors
   * of the current CSSStyleRule.
   *
   * @return {array} the array hold the CssSelector objects.
   */
  get selectors() {
    if (this._selectors) {
      return this._selectors;
    }

    // Parse the CSSStyleRule.selectorText string.
    this._selectors = [];

    if (!this.domRule.selectorText) {
      return this._selectors;
    }

    let selectors = CssLogic.getSelectors(this.domRule);

    for (let i = 0, len = selectors.length; i < len; i++) {
      this._selectors.push(new CssSelector(this, selectors[i], i));
    }

    return this._selectors;
  },

  toString: function () {
    return "[CssRule " + this.domRule.selectorText + "]";
  },
};

/**
 * The CSS selector class allows us to document the ranking of various CSS
 * selectors.
 *
 * @constructor
 * @param {CssRule} cssRule the CssRule instance from where the selector comes.
 * @param {string} selector The selector that we wish to investigate.
 * @param {Number} index The index of the selector within it's rule.
 */
function CssSelector(cssRule, selector, index) {
  this.cssRule = cssRule;
  this.text = selector;
  this.elementStyle = this.text == "@element.style";
  this._specificity = null;
  this.selectorIndex = index;
}

exports.CssSelector = CssSelector;

CssSelector.prototype = {
  _matchId: null,

  /**
   * Retrieve the CssSelector source, which is the source of the CssSheet owning
   * the selector.
   *
   * @return {string} the selector source.
   */
  get source() {
    return this.cssRule.source;
  },

  /**
   * Retrieve the CssSelector source element, which is the source of the CssRule
   * owning the selector. This is only available when the CssSelector comes from
   * an element.style.
   *
   * @return {string} the source element selector.
   */
  get sourceElement() {
    return this.cssRule.sourceElement;
  },

  /**
   * Retrieve the address of the CssSelector. This points to the address of the
   * CssSheet owning this selector.
   *
   * @return {string} the address of the CssSelector.
   */
  get href() {
    return this.cssRule.href;
  },

  /**
   * Check if the selector comes from a browser-provided stylesheet.
   *
   * @return {boolean} true if the selector comes from a content-provided
   * stylesheet, or false otherwise.
   */
  get contentRule() {
    return this.cssRule.contentRule;
  },

  /**
   * Check if the parent stylesheet is allowed by the CssLogic.sourceFilter.
   *
   * @return {boolean} true if the parent stylesheet is allowed by the current
   * sourceFilter, or false otherwise.
   */
  get sheetAllowed() {
    return this.cssRule.sheetAllowed;
  },

  /**
   * Retrieve the parent stylesheet index/position in the viewed document.
   *
   * @return {number} the parent stylesheet index/position in the viewed
   * document.
   */
  get sheetIndex() {
    return this.cssRule.sheetIndex;
  },

  /**
   * Retrieve the line of the parent CSSStyleRule in the parent CSSStyleSheet.
   *
   * @return {number} the line of the parent CSSStyleRule in the parent
   * stylesheet.
   */
  get ruleLine() {
    return this.cssRule.line;
  },

  /**
   * Retrieve the column of the parent CSSStyleRule in the parent CSSStyleSheet.
   *
   * @return {number} the column of the parent CSSStyleRule in the parent
   * stylesheet.
   */
  get ruleColumn() {
    return this.cssRule.column;
  },

  /**
   * Retrieve specificity information for the current selector.
   *
   * @see http://www.w3.org/TR/css3-selectors/#specificity
   * @see http://www.w3.org/TR/CSS2/selector.html
   *
   * @return {Number} The selector's specificity.
   */
  get specificity() {
    if (this.elementStyle) {
      // We can't ask specificity from DOMUtils as element styles don't provide
      // CSSStyleRule interface DOMUtils expect. However, specificity of element
      // style is constant, 1,0,0,0 or 0x40000000, just return the constant
      // directly. @see http://www.w3.org/TR/CSS2/cascade.html#specificity
      return 0x40000000;
    }

    if (this._specificity) {
      return this._specificity;
    }

    this._specificity = InspectorUtils.getSpecificity(this.cssRule.domRule,
                                                      this.selectorIndex);

    return this._specificity;
  },

  toString: function () {
    return this.text;
  },
};

/**
 * A cache of information about the matched rules, selectors and values attached
 * to a CSS property, for the highlighted element.
 *
 * The heart of the CssPropertyInfo object is the _findMatchedSelectors()
 * method. This are invoked when the PropertyView tries to access the
 * .matchedSelectors array.
 * Results are cached, for later reuse.
 *
 * @param {CssLogic} cssLogic Reference to the parent CssLogic instance
 * @param {string} property The CSS property we are gathering information for
 * @param {function} isInherited A function that determines if the CSS property
 *                   is inherited.
 * @constructor
 */
function CssPropertyInfo(cssLogic, property, isInherited) {
  this._cssLogic = cssLogic;
  this.property = property;
  this._value = "";
  this._isInherited = isInherited;

  // An array holding CssSelectorInfo objects for each of the matched selectors
  // that are inside a CSS rule. Only rules that hold the this.property are
  // counted. This includes rules that come from filtered stylesheets (those
  // that have sheetAllowed = false).
  this._matchedSelectors = null;
}

CssPropertyInfo.prototype = {
  /**
   * Retrieve the computed style value for the current property, for the
   * highlighted element.
   *
   * @return {string} the computed style value for the current property, for the
   * highlighted element.
   */
  get value() {
    if (!this._value && this._cssLogic.computedStyle) {
      try {
        this._value =
          this._cssLogic.computedStyle.getPropertyValue(this.property);
      } catch (ex) {
        console.log("Error reading computed style for " + this.property);
        console.log(ex);
      }
    }
    return this._value;
  },

  /**
   * Retrieve the array holding CssSelectorInfo objects for each of the matched
   * selectors, from each of the matched rules. Only selectors coming from
   * allowed stylesheets are included in the array.
   *
   * @return {array} the list of CssSelectorInfo objects of selectors that match
   * the highlighted element and its parents.
   */
  get matchedSelectors() {
    if (!this._matchedSelectors) {
      this._findMatchedSelectors();
    } else if (this.needRefilter) {
      this._refilterSelectors();
    }

    return this._matchedSelectors;
  },

  /**
   * Find the selectors that match the highlighted element and its parents.
   * Uses CssLogic.processMatchedSelectors() to find the matched selectors,
   * passing in a reference to CssPropertyInfo._processMatchedSelector() to
   * create CssSelectorInfo objects, which we then sort
   * @private
   */
  _findMatchedSelectors: function () {
    this._matchedSelectors = [];
    this.needRefilter = false;

    this._cssLogic.processMatchedSelectors(this._processMatchedSelector, this);

    // Sort the selectors by how well they match the given element.
    this._matchedSelectors.sort(function (selectorInfo1, selectorInfo2) {
      if (selectorInfo1.status > selectorInfo2.status) {
        return -1;
      } else if (selectorInfo2.status > selectorInfo1.status) {
        return 1;
      }
      return selectorInfo1.compareTo(selectorInfo2);
    });

    // Now we know which of the matches is best, we can mark it BEST_MATCH.
    if (this._matchedSelectors.length > 0 &&
        this._matchedSelectors[0].status > STATUS.UNMATCHED) {
      this._matchedSelectors[0].status = STATUS.BEST;
    }
  },

  /**
   * Process a matched CssSelector object.
   *
   * @private
   * @param {CssSelector} selector the matched CssSelector object.
   * @param {STATUS} status the CssSelector match status.
   */
  _processMatchedSelector: function (selector, status) {
    let cssRule = selector.cssRule;
    let value = cssRule.getPropertyValue(this.property);
    if (value &&
        (status == STATUS.MATCHED ||
         (status == STATUS.PARENT_MATCH &&
          this._isInherited(this.property)))) {
      let selectorInfo = new CssSelectorInfo(selector, this.property, value,
          status);
      this._matchedSelectors.push(selectorInfo);
    }
  },

  /**
   * Refilter the matched selectors array when the CssLogic.sourceFilter
   * changes. This allows for quick filter changes.
   * @private
   */
  _refilterSelectors: function () {
    let passId = ++this._cssLogic._passId;

    let iterator = function (selectorInfo) {
      let cssRule = selectorInfo.selector.cssRule;
      if (cssRule._passId != passId) {
        cssRule._passId = passId;
      }
    };

    if (this._matchedSelectors) {
      this._matchedSelectors.forEach(iterator);
    }

    this.needRefilter = false;
  },

  toString: function () {
    return "CssPropertyInfo[" + this.property + "]";
  },
};

/**
 * A class that holds information about a given CssSelector object.
 *
 * Instances of this class are given to CssHtmlTree in the array of matched
 * selectors. Each such object represents a displayable row in the PropertyView
 * objects. The information given by this object blends data coming from the
 * CssSheet, CssRule and from the CssSelector that own this object.
 *
 * @param {CssSelector} selector The CssSelector object for which to
 *        present information.
 * @param {string} property The property for which information should
 *        be retrieved.
 * @param {string} value The property value from the CssRule that owns
 *        the selector.
 * @param {STATUS} status The selector match status.
 * @constructor
 */
function CssSelectorInfo(selector, property, value, status) {
  this.selector = selector;
  this.property = property;
  this.status = status;
  this.value = value;
  let priority = this.selector.cssRule.getPropertyPriority(this.property);
  this.important = (priority === "important");
}

CssSelectorInfo.prototype = {
  /**
   * Retrieve the CssSelector source, which is the source of the CssSheet owning
   * the selector.
   *
   * @return {string} the selector source.
   */
  get source() {
    return this.selector.source;
  },

  /**
   * Retrieve the CssSelector source element, which is the source of the CssRule
   * owning the selector. This is only available when the CssSelector comes from
   * an element.style.
   *
   * @return {string} the source element selector.
   */
  get sourceElement() {
    return this.selector.sourceElement;
  },

  /**
   * Retrieve the address of the CssSelector. This points to the address of the
   * CssSheet owning this selector.
   *
   * @return {string} the address of the CssSelector.
   */
  get href() {
    return this.selector.href;
  },

  /**
   * Check if the CssSelector comes from element.style or not.
   *
   * @return {boolean} true if the CssSelector comes from element.style, or
   * false otherwise.
   */
  get elementStyle() {
    return this.selector.elementStyle;
  },

  /**
   * Retrieve specificity information for the current selector.
   *
   * @return {object} an object holding specificity information for the current
   * selector.
   */
  get specificity() {
    return this.selector.specificity;
  },

  /**
   * Retrieve the parent stylesheet index/position in the viewed document.
   *
   * @return {number} the parent stylesheet index/position in the viewed
   * document.
   */
  get sheetIndex() {
    return this.selector.sheetIndex;
  },

  /**
   * Check if the parent stylesheet is allowed by the CssLogic.sourceFilter.
   *
   * @return {boolean} true if the parent stylesheet is allowed by the current
   * sourceFilter, or false otherwise.
   */
  get sheetAllowed() {
    return this.selector.sheetAllowed;
  },

  /**
   * Retrieve the line of the parent CSSStyleRule in the parent CSSStyleSheet.
   *
   * @return {number} the line of the parent CSSStyleRule in the parent
   * stylesheet.
   */
  get ruleLine() {
    return this.selector.ruleLine;
  },

  /**
   * Retrieve the column of the parent CSSStyleRule in the parent CSSStyleSheet.
   *
   * @return {number} the column of the parent CSSStyleRule in the parent
   * stylesheet.
   */
  get ruleColumn() {
    return this.selector.ruleColumn;
  },

  /**
   * Check if the selector comes from a browser-provided stylesheet.
   *
   * @return {boolean} true if the selector comes from a browser-provided
   * stylesheet, or false otherwise.
   */
  get contentRule() {
    return this.selector.contentRule;
  },

  /**
   * Compare the current CssSelectorInfo instance to another instance, based on
   * specificity information.
   *
   * @param {CssSelectorInfo} that The instance to compare ourselves against.
   * @return number -1, 0, 1 depending on how that compares with this.
   */
  compareTo: function (that) {
    if (!this.contentRule && that.contentRule) {
      return 1;
    }
    if (this.contentRule && !that.contentRule) {
      return -1;
    }

    if (this.elementStyle && !that.elementStyle) {
      if (!this.important && that.important) {
        return 1;
      }
      return -1;
    }

    if (!this.elementStyle && that.elementStyle) {
      if (this.important && !that.important) {
        return -1;
      }
      return 1;
    }

    if (this.important && !that.important) {
      return -1;
    }
    if (that.important && !this.important) {
      return 1;
    }

    if (this.specificity > that.specificity) {
      return -1;
    }
    if (that.specificity > this.specificity) {
      return 1;
    }

    if (this.sheetIndex > that.sheetIndex) {
      return -1;
    }
    if (that.sheetIndex > this.sheetIndex) {
      return 1;
    }

    if (this.ruleLine > that.ruleLine) {
      return -1;
    }
    if (that.ruleLine > this.ruleLine) {
      return 1;
    }

    if (this.ruleColumn > that.ruleColumn) {
      return -1;
    }
    if (that.ruleColumn > this.ruleColumn) {
      return 1;
    }

    return 0;
  },

  toString: function () {
    return this.selector + " -> " + this.value;
  },
};
PK
!<��s�7�78chrome/devtools/modules/devtools/server/event-parsers.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 file contains event parsers that are then used by developer tools in
// order to find information about events affecting an HTML element.

"use strict";

const Services = require("Services");

// eslint-disable-next-line
const JQUERY_LIVE_REGEX = /return typeof \w+.*.event\.triggered[\s\S]*\.event\.(dispatch|handle).*arguments/;

var parsers = [
  {
    id: "jQuery events",
    getListeners: function (node) {
      let global = node.ownerGlobal.wrappedJSObject;
      let hasJQuery = global.jQuery && global.jQuery.fn && global.jQuery.fn.jquery;

      if (!hasJQuery) {
        return undefined;
      }

      let jQuery = global.jQuery;
      let handlers = [];

      // jQuery 1.2+
      let data = jQuery._data || jQuery.data;
      if (data) {
        let eventsObj = data(node, "events");
        for (let type in eventsObj) {
          let events = eventsObj[type];
          for (let key in events) {
            let event = events[key];

            if (node.wrappedJSObject == global.document && event.selector) {
              continue;
            }

            if (typeof event === "object" || typeof event === "function") {
              let eventInfo = {
                type: type,
                handler: event.handler || event,
                tags: "jQuery",
                hide: {
                  capturing: true,
                  dom0: true
                }
              };

              handlers.push(eventInfo);
            }
          }
        }
      }

      // JQuery 1.0 & 1.1
      let entry = jQuery(node)[0];

      if (!entry) {
        return handlers;
      }

      for (let type in entry.events) {
        let events = entry.events[type];
        for (let key in events) {
          let event = events[key];

          if (node.wrappedJSObject == global.document && event.selector) {
            continue;
          }

          if (typeof events[key] === "function") {
            let eventInfo = {
              type: type,
              handler: events[key],
              tags: "jQuery",
              hide: {
                capturing: true,
                dom0: true
              }
            };

            handlers.push(eventInfo);
          }
        }
      }

      return handlers;
    }
  },
  {
    id: "jQuery live events",
    hasListeners: function (node) {
      return jQueryLiveGetListeners(node, true);
    },
    getListeners: function (node) {
      return jQueryLiveGetListeners(node, false);
    },
    normalizeListener: function (handlerDO) {
      function isFunctionInProxy(funcDO) {
        // If the anonymous function is inside the |proxy| function and the
        // function only has guessed atom, the guessed atom should starts with
        // "proxy/".
        let displayName = funcDO.displayName;
        if (displayName && displayName.startsWith("proxy/")) {
          return true;
        }

        // If the anonymous function is inside the |proxy| function and the
        // function gets name at compile time by SetFunctionName, its guessed
        // atom doesn't contain "proxy/".  In that case, check if the caller is
        // "proxy" function, as a fallback.
        let calleeDO = funcDO.environment.callee;
        if (!calleeDO) {
          return false;
        }
        let calleeName = calleeDO.displayName;
        return calleeName == "proxy";
      }

      function getFirstFunctionVariable(funcDO) {
        // The handler function inside the |proxy| function should point the
        // unwrapped function via environment variable.
        let names = funcDO.environment.names();
        for (let varName of names) {
          let varDO = handlerDO.environment.getVariable(varName);
          if (!varDO) {
            continue;
          }
          if (varDO.class == "Function") {
            return varDO;
          }
        }
        return null;
      }

      if (!isFunctionInProxy(handlerDO)) {
        return handlerDO;
      }

      const MAX_NESTED_HANDLER_COUNT = 2;
      for (let i = 0; i < MAX_NESTED_HANDLER_COUNT; i++) {
        let funcDO = getFirstFunctionVariable(handlerDO);
        if (!funcDO) {
          return handlerDO;
        }

        handlerDO = funcDO;
        if (isFunctionInProxy(handlerDO)) {
          continue;
        }
        break;
      }

      return handlerDO;
    }
  },
  {
    id: "DOM events",
    hasListeners: function (node) {
      let listeners;

      if (node.nodeName.toLowerCase() === "html") {
        let winListeners =
          Services.els.getListenerInfoFor(node.ownerGlobal) || [];
        let docElementListeners =
          Services.els.getListenerInfoFor(node) || [];
        let docListeners =
          Services.els.getListenerInfoFor(node.parentNode) || [];

        listeners = [...winListeners, ...docElementListeners, ...docListeners];
      } else {
        listeners = Services.els.getListenerInfoFor(node) || [];
      }

      for (let listener of listeners) {
        if (listener.listenerObject && listener.type) {
          return true;
        }
      }

      return false;
    },
    getListeners: function (node) {
      let handlers = [];
      let listeners = Services.els.getListenerInfoFor(node);

      // The Node actor's getEventListenerInfo knows that when an html tag has
      // been passed we need the window object so we don't need to account for
      // event hoisting here as we did in hasListeners.

      for (let listenerObj of listeners) {
        let listener = listenerObj.listenerObject;

        // If there is no JS event listener skip this.
        if (!listener || JQUERY_LIVE_REGEX.test(listener.toString())) {
          continue;
        }

        let eventInfo = {
          capturing: listenerObj.capturing,
          type: listenerObj.type,
          handler: listener
        };

        handlers.push(eventInfo);
      }

      return handlers;
    }
  },

  {
    id: "React events",
    hasListeners: function (node) {
      return reactGetListeners(node, true);
    },

    getListeners: function (node) {
      return reactGetListeners(node, false);
    },

    normalizeListener: function (handlerDO, listener) {
      let functionText = "";

      if (handlerDO.boundTargetFunction) {
        handlerDO = handlerDO.boundTargetFunction;
      }

      let introScript = handlerDO.script.source.introductionScript;
      let script = handlerDO.script;

      // If this is a Babel transpiled function we have no access to the
      // source location so we need to hide the filename and debugger
      // icon.
      if (introScript && introScript.displayName.endsWith("/transform.run")) {
        listener.hide.debugger = true;
        listener.hide.filename = true;

        if (!handlerDO.isArrowFunction) {
          functionText += "function (";
        } else {
          functionText += "(";
        }

        functionText += handlerDO.parameterNames.join(", ");

        functionText += ") {\n";

        let scriptSource = script.source.text;
        functionText +=
          scriptSource.substr(script.sourceStart, script.sourceLength);

        listener.override.handler = functionText;
      }

      return handlerDO;
    }
  },
];

function reactGetListeners(node, boolOnEventFound) {
  function getProps() {
    for (let key of Object.keys(node)) {
      if (key.startsWith("__reactInternalInstance$")) {
        let value = node[key];
        if (value.memoizedProps) {
          return value.memoizedProps; // React 16
        }
        return value._currentElement.props; // React 15
      }
    }
    return null;
  }

  node = node.wrappedJSObject || node;

  let handlers = [];
  let props = getProps();

  if (props) {
    for (let name in props) {
      if (name.startsWith("on")) {
        let prop = props[name];
        let listener = prop.__reactBoundMethod || prop;

        if (typeof listener !== "function") {
          continue;
        }

        if (boolOnEventFound) {
          return true;
        }

        let handler = {
          type: name,
          handler: listener,
          tags: "React",
          hide: {
            dom0: true
          },
          override: {
            capturing: name.endsWith("Capture")
          }
        };

        handlers.push(handler);
      }
    }
  }

  if (boolOnEventFound) {
    return false;
  }

  return handlers;
}

function jQueryLiveGetListeners(node, boolOnEventFound) {
  let global = node.ownerGlobal.wrappedJSObject;
  let hasJQuery = global.jQuery && global.jQuery.fn && global.jQuery.fn.jquery;

  if (!hasJQuery) {
    return undefined;
  }

  let jQuery = global.jQuery;
  let handlers = [];
  let data = jQuery._data || jQuery.data;

  if (data) {
    // Live events are added to the document and bubble up to all elements.
    // Any element matching the specified selector will trigger the live
    // event.
    let events = data(global.document, "events");

    for (let type in events) {
      let eventHolder = events[type];

      for (let idx in eventHolder) {
        if (typeof idx !== "string" || isNaN(parseInt(idx, 10))) {
          continue;
        }

        let event = eventHolder[idx];
        let selector = event.selector;

        if (!selector && event.data) {
          selector = event.data.selector || event.data || event.selector;
        }

        if (!selector || !node.ownerDocument) {
          continue;
        }

        let matches;
        try {
          matches = node.matches && node.matches(selector);
        } catch (e) {
          // Invalid selector, do nothing.
        }

        if (boolOnEventFound && matches) {
          return true;
        }

        if (!matches) {
          continue;
        }

        if (!boolOnEventFound &&
            (typeof event === "object" || typeof event === "function")) {
          let eventInfo = {
            type: event.origType || event.type.substr(selector.length + 1),
            handler: event.handler || event,
            tags: "jQuery,Live",
            hide: {
              dom0: true,
              capturing: true
            }
          };

          if (!eventInfo.type && event.data && event.data.live) {
            eventInfo.type = event.data.live;
          }

          handlers.push(eventInfo);
        }
      }
    }
  }

  if (boolOnEventFound) {
    return false;
  }
  return handlers;
}

this.EventParsers = function EventParsers() {
  if (this._eventParsers.size === 0) {
    for (let parserObj of parsers) {
      this.registerEventParser(parserObj);
    }
  }
};

exports.EventParsers = EventParsers;

EventParsers.prototype = {
  // NOTE: This is shared amongst all instances.
  _eventParsers: new Map(),

  get parsers() {
    return this._eventParsers;
  },

  /**
   * Register a new event parser to be used in the processing of event info.
   *
   * @param {Object} parserObj
   *        Each parser must contain the following properties:
   *        - parser, which must take the following form:
   *   {
   *     id {String}: "jQuery events",          // Unique id.
   *     getListeners: function(node) { },      // Function that takes a node
   *                                            // and returns an array of
   *                                            // eventInfo objects (see
   *                                            // below).
   *
   *     hasListeners: function(node) { },      // Optional function that takes
   *                                            // a node and returns a boolean
   *                                            // indicating whether a node has
   *                                            // listeners attached.
   *
   *     normalizeListener:
   *        function(fnDO, listener) { },       // Optional function that takes
   *                                            // a Debugger.Object instance
   *                                            // and an optional listener
   *                                            // object. Both objects can be
   *                                            // manipulated to display the
   *                                            // correct information in the
   *                                            // event bubble.
   *   }
   *
   * An eventInfo object should take the following form:
   *   {
   *     type {String}:      "click",
   *     handler {Function}: event handler,
   *     tags {String}:      "jQuery,Live", // These tags will be displayed as
   *                                        // attributes in the events popup.
   *     hide: {               // Hide or show fields:
   *       debugger: false,    // Debugger icon
   *       type: false,        // Event type e.g. click
   *       filename: false,    // Filename
   *       capturing: false,   // Capturing
   *       dom0: false         // DOM 0
   *     },
   *
   *     override: {                        // The following can be overridden:
   *       handler: someEventHandler,
   *       type: "click",
   *       origin: "http://www.mozilla.com",
   *       searchString: 'onclick="doSomething()"',
   *       dom0: true,
   *       capturing: true
   *     }
   *   }
   */
  registerEventParser: function (parserObj) {
    let parserId = parserObj.id;

    if (!parserId) {
      throw new Error("Cannot register new event parser with id " + parserId);
    }
    if (this._eventParsers.has(parserId)) {
      throw new Error("Duplicate event parser id " + parserId);
    }

    this._eventParsers.set(parserId, {
      getListeners: parserObj.getListeners,
      hasListeners: parserObj.hasListeners,
      normalizeListener: parserObj.normalizeListener
    });
  },

  /**
   * Removes parser that matches a given parserId.
   *
   * @param {String} parserId
   *        id of the event parser to unregister.
   */
  unregisterEventParser: function (parserId) {
    this._eventParsers.delete(parserId);
  },

  /**
   * Tidy up parsers.
   */
  destroy: function () {
    for (let [id] of this._eventParsers) {
      this.unregisterEventParser(id, true);
    }
  }
};
PK
!<!�x^�^�/chrome/devtools/modules/devtools/server/main.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

/**
 * Toolkit glue for the remote debugging protocol, loaded into the
 * debugging global.
 */
var { Ci, Cc } = require("chrome");
var Services = require("Services");
var { ActorPool, OriginalLocation, RegisteredActorFactory,
      ObservedActorFactory } = require("devtools/server/actors/common");
var { LocalDebuggerTransport, ChildDebuggerTransport, WorkerDebuggerTransport } =
  require("devtools/shared/transport/transport");
var DevToolsUtils = require("devtools/shared/DevToolsUtils");
var { dumpn } = DevToolsUtils;
var flags = require("devtools/shared/flags");

DevToolsUtils.defineLazyGetter(this, "DebuggerSocket", () => {
  let { DebuggerSocket } = require("devtools/shared/security/socket");
  return DebuggerSocket;
});
DevToolsUtils.defineLazyGetter(this, "Authentication", () => {
  return require("devtools/shared/security/auth");
});
DevToolsUtils.defineLazyGetter(this, "generateUUID", () => {
  let { generateUUID } = Cc["@mozilla.org/uuid-generator;1"]
                           .getService(Ci.nsIUUIDGenerator);
  return generateUUID;
});

// Overload `Components` to prevent SDK loader exception on Components
// object usage
Object.defineProperty(this, "Components", {
  get() {
    return require("chrome").components;
  }
});

if (isWorker) {
  flags.wantLogging = true;
  flags.wantVerbose = true;
} else {
  const LOG_PREF = "devtools.debugger.log";
  const VERBOSE_PREF = "devtools.debugger.log.verbose";

  flags.wantLogging = Services.prefs.getBoolPref(LOG_PREF);
  flags.wantVerbose =
    Services.prefs.getPrefType(VERBOSE_PREF) !== Services.prefs.PREF_INVALID &&
    Services.prefs.getBoolPref(VERBOSE_PREF);
}

const CONTENT_PROCESS_DBG_SERVER_SCRIPT =
  "resource://devtools/server/content-process-debugger-server.js";

function loadSubScript(url) {
  try {
    Services.scriptloader.loadSubScript(url, this);
  } catch (e) {
    let errorStr = "Error loading: " + url + ":\n" +
                   (e.fileName ? "at " + e.fileName + " : " + e.lineNumber + "\n" : "") +
                   e + " - " + e.stack + "\n";
    dump(errorStr);
    reportError(errorStr);
    throw e;
  }
}

loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");

var gRegisteredModules = Object.create(null);

/**
 * The ModuleAPI object is passed to modules loaded using the
 * DebuggerServer.registerModule() API.  Modules can use this
 * object to register actor factories.
 * Factories registered through the module API will be removed
 * when the module is unregistered or when the server is
 * destroyed.
 */
function ModuleAPI() {
  let activeTabActors = new Set();
  let activeGlobalActors = new Set();

  return {
    // See DebuggerServer.setRootActor for a description.
    setRootActor(factory) {
      DebuggerServer.setRootActor(factory);
    },

    // See DebuggerServer.addGlobalActor for a description.
    addGlobalActor(factory, name) {
      DebuggerServer.addGlobalActor(factory, name);
      activeGlobalActors.add(factory);
    },
    // See DebuggerServer.removeGlobalActor for a description.
    removeGlobalActor(factory) {
      DebuggerServer.removeGlobalActor(factory);
      activeGlobalActors.delete(factory);
    },

    // See DebuggerServer.addTabActor for a description.
    addTabActor(factory, name) {
      DebuggerServer.addTabActor(factory, name);
      activeTabActors.add(factory);
    },
    // See DebuggerServer.removeTabActor for a description.
    removeTabActor(factory) {
      DebuggerServer.removeTabActor(factory);
      activeTabActors.delete(factory);
    },

    // Destroy the module API object, unregistering any
    // factories registered by the module.
    destroy() {
      for (let factory of activeTabActors) {
        DebuggerServer.removeTabActor(factory);
      }
      activeTabActors = null;
      for (let factory of activeGlobalActors) {
        DebuggerServer.removeGlobalActor(factory);
      }
      activeGlobalActors = null;
    }
  };
}

/** *
 * Public API
 */
var DebuggerServer = {
  _listeners: [],
  _initialized: false,
  // Flag to check if the content process debugger server script was already loaded.
  _contentProcessScriptLoaded: false,
  // Map of global actor names to actor constructors provided by extensions.
  globalActorFactories: {},
  // Map of tab actor names to actor constructors provided by extensions.
  tabActorFactories: {},

  LONG_STRING_LENGTH: 10000,
  LONG_STRING_INITIAL_LENGTH: 1000,
  LONG_STRING_READ_LENGTH: 65 * 1024,

  /**
   * The windowtype of the chrome window to use for actors that use the global
   * window (i.e the global style editor). Set this to your main window type,
   * for example "navigator:browser".
   */
  chromeWindowType: "navigator:browser",

  /**
   * Allow debugging chrome of (parent or child) processes.
   */
  allowChromeProcess: false,

  /**
   * We run a special server in child process whose main actor is an instance
   * of ContentActor, but that isn't a root actor. Instead there is no root
   * actor registered on DebuggerServer.
   */
  get rootlessServer() {
    return !this.isModuleRegistered("devtools/server/actors/webbrowser");
  },

  /**
   * Initialize the debugger server.
   */
  init() {
    if (this.initialized) {
      return;
    }

    this._connections = {};
    this._nextConnID = 0;

    this._initialized = true;
  },

  get protocol() {
    return require("devtools/shared/protocol");
  },

  get initialized() {
    return this._initialized;
  },

  /**
   * Performs cleanup tasks before shutting down the debugger server. Such tasks
   * include clearing any actor constructors added at runtime. This method
   * should be called whenever a debugger server is no longer useful, to avoid
   * memory leaks. After this method returns, the debugger server must be
   * initialized again before use.
   */
  destroy() {
    if (!this._initialized) {
      return;
    }

    for (let connID of Object.getOwnPropertyNames(this._connections)) {
      this._connections[connID].close();
    }

    for (let id of Object.getOwnPropertyNames(gRegisteredModules)) {
      this.unregisterModule(id);
    }
    gRegisteredModules = Object.create(null);

    this.closeAllListeners();
    this.globalActorFactories = {};
    this.tabActorFactories = {};
    this._initialized = false;

    dumpn("Debugger server is shut down.");
  },

  /**
   * Raises an exception if the server has not been properly initialized.
   */
  _checkInit() {
    if (!this._initialized) {
      throw new Error("DebuggerServer has not been initialized.");
    }

    if (!this.rootlessServer && !this.createRootActor) {
      throw new Error("Use DebuggerServer.addActors() to add a root actor " +
                      "implementation.");
    }
  },

  /**
   * Register different type of actors. Only register the one that are not already
   * registered.
   *
   * @param root boolean
   *        Registers the root actor from webbrowser module, which is used to
   *        connect to and fetch any other actor.
   * @param browser boolean
   *        Registers all the parent process actors useful for debugging the
   *        runtime itself, like preferences and addons actors.
   * @param tab boolean
   *        Registers all the tab actors like console, script, ... all useful
   *        for debugging a target context.
   */
  registerActors({ root, browser, tab }) {
    if (browser) {
      this._addBrowserActors();
    }

    if (root) {
      this.registerModule("devtools/server/actors/webbrowser");
    }

    if (tab) {
      this._addTabActors();
    }
  },

  /**
   * Register all possible actors for this DebuggerServer.
   */
  registerAllActors() {
    this.registerActors({ root: true, browser: true, tab: true });
  },

  /**
   * Load a subscript into the debugging global.
   *
   * @param url string A url that will be loaded as a subscript into the
   *        debugging global.  The user must load at least one script
   *        that implements a createRootActor() function to create the
   *        server's root actor.
   */
  addActors(url) {
    loadSubScript.call(this, url);
  },

  /**
   * Register a CommonJS module with the debugger server.
   * @param id string
   *        The ID of a CommonJS module.  This module must export 'register'
   *        and 'unregister' functions if no `options` argument is given.
   *        If `options` is set, the actor is going to be registered
   *        immediately, but loaded only when a client starts sending packets
   *        to an actor with the same id.
   *
   * @param options object (optional)
   *        This parameter is still optional, but not providing it is
   *        deprecated and will result in eagerly loading the actor module
   *        with the memory overhead that entails.
   *        An object with 3 mandatory attributes:
   *        - prefix (string):
   *          The prefix of an actor is used to compute:
   *          - the `actorID` of each new actor instance (ex: prefix1).
   *            (See ActorPool.addActor)
   *          - the actor name in the listTabs request. Sending a listTabs
   *            request to the root actor returns actor IDs. IDs are in
   *            dictionaries, with actor names as keys and actor IDs as values.
   *            The actor name is the prefix to which the "Actor" string is
   *            appended. So for an actor with the `console` prefix, the actor
   *            name will be `consoleActor`.
   *        - constructor (string):
   *          the name of the exported symbol to be used as the actor
   *          constructor.
   *        - type (a dictionary of booleans with following attribute names):
   *          - "global"
   *            registers a global actor instance, if true.
   *            A global actor has the root actor as its parent.
   *          - "tab"
   *            registers a tab actor instance, if true.
   *            A new actor will be created for each tab and each app.
   */
  registerModule(id, options) {
    if (id in gRegisteredModules) {
      return;
    }

    if (options) {
      // Lazy loaded actors
      let {prefix, constructor, type} = options;
      if (typeof (prefix) !== "string") {
        throw new Error(`Lazy actor definition for '${id}' requires a string ` +
                        `'prefix' option.`);
      }
      if (typeof (constructor) !== "string") {
        throw new Error(`Lazy actor definition for '${id}' requires a string ` +
                        `'constructor' option.`);
      }
      if (!("global" in type) && !("tab" in type)) {
        throw new Error(`Lazy actor definition for '${id}' requires a dictionary ` +
                        `'type' option whose attributes can be 'global' or 'tab'.`);
      }
      let name = prefix + "Actor";
      let mod = {
        id: id,
        prefix: prefix,
        constructorName: constructor,
        type: type,
        globalActor: type.global,
        tabActor: type.tab
      };
      gRegisteredModules[id] = mod;
      if (mod.tabActor) {
        this.addTabActor(mod, name);
      }
      if (mod.globalActor) {
        this.addGlobalActor(mod, name);
      }
    } else {
      // Deprecated actors being loaded at startup
      let moduleAPI = ModuleAPI();
      let mod = require(id);
      mod.register(moduleAPI);
      gRegisteredModules[id] = {
        module: mod,
        api: moduleAPI
      };
    }
  },

  /**
   * Returns true if a module id has been registered.
   */
  isModuleRegistered(id) {
    return (id in gRegisteredModules);
  },

  /**
   * Unregister a previously-loaded CommonJS module from the debugger server.
   */
  unregisterModule(id) {
    let mod = gRegisteredModules[id];
    if (!mod) {
      throw new Error("Tried to unregister a module that was not previously registered.");
    }

    // Lazy actors
    if (mod.tabActor) {
      this.removeTabActor(mod);
    }
    if (mod.globalActor) {
      this.removeGlobalActor(mod);
    }

    if (mod.module) {
      // Deprecated non-lazy module API
      mod.module.unregister(mod.api);
      mod.api.destroy();
    }

    delete gRegisteredModules[id];
  },

  /**
   * Install Firefox-specific actors.
   *
   * /!\ Be careful when adding a new actor, especially global actors.
   * Any new global actor will be exposed and returned by the root actor.
   */
  _addBrowserActors() {
    this.registerModule("devtools/server/actors/preference", {
      prefix: "preference",
      constructor: "PreferenceActor",
      type: { global: true }
    });
    this.registerModule("devtools/server/actors/actor-registry", {
      prefix: "actorRegistry",
      constructor: "ActorRegistryActor",
      type: { global: true }
    });
    this.registerModule("devtools/server/actors/addons", {
      prefix: "addons",
      constructor: "AddonsActor",
      type: { global: true }
    });
    this.registerModule("devtools/server/actors/device", {
      prefix: "device",
      constructor: "DeviceActor",
      type: { global: true }
    });
    this.registerModule("devtools/server/actors/heap-snapshot-file", {
      prefix: "heapSnapshotFile",
      constructor: "HeapSnapshotFileActor",
      type: { global: true }
    });
    // Always register this as a global module, even while there is a pref turning
    // on and off the other performance actor. This actor shouldn't conflict with
    // the other one. These are also lazily loaded so there shouldn't be a performance
    // impact.
    this.registerModule("devtools/server/actors/perf", {
      prefix: "perf",
      constructor: "PerfActor",
      type: { global: true }
    });
  },

  /**
   * Install tab actors.
   */
  _addTabActors() {
    this.registerModule("devtools/server/actors/webconsole", {
      prefix: "console",
      constructor: "WebConsoleActor",
      type: { tab: true }
    });
    this.registerModule("devtools/server/actors/inspector/inspector", {
      prefix: "inspector",
      constructor: "InspectorActor",
      type: { tab: true }
    });
    this.registerModule("devtools/server/actors/call-watcher", {
      prefix: "callWatcher",
      constructor: "CallWatcherActor",
      type: { tab: true }
    });
    this.registerModule("devtools/server/actors/canvas", {
      prefix: "canvas",
      constructor: "CanvasActor",
      type: { tab: true }
    });
    this.registerModule("devtools/server/actors/webgl", {
      prefix: "webgl",
      constructor: "WebGLActor",
      type: { tab: true }
    });
    this.registerModule("devtools/server/actors/webaudio", {
      prefix: "webaudio",
      constructor: "WebAudioActor",
      type: { tab: true }
    });
    this.registerModule("devtools/server/actors/stylesheets", {
      prefix: "styleSheets",
      constructor: "StyleSheetsActor",
      type: { tab: true }
    });
    this.registerModule("devtools/server/actors/storage", {
      prefix: "storage",
      constructor: "StorageActor",
      type: { tab: true }
    });
    this.registerModule("devtools/server/actors/gcli", {
      prefix: "gcli",
      constructor: "GcliActor",
      type: { tab: true }
    });
    this.registerModule("devtools/server/actors/memory", {
      prefix: "memory",
      constructor: "MemoryActor",
      type: { tab: true }
    });
    this.registerModule("devtools/server/actors/framerate", {
      prefix: "framerate",
      constructor: "FramerateActor",
      type: { tab: true }
    });
    this.registerModule("devtools/server/actors/eventlooplag", {
      prefix: "eventLoopLag",
      constructor: "EventLoopLagActor",
      type: { tab: true }
    });
    this.registerModule("devtools/server/actors/reflow", {
      prefix: "reflow",
      constructor: "ReflowActor",
      type: { tab: true }
    });
    this.registerModule("devtools/server/actors/css-properties", {
      prefix: "cssProperties",
      constructor: "CssPropertiesActor",
      type: { tab: true }
    });
    this.registerModule("devtools/server/actors/csscoverage", {
      prefix: "cssUsage",
      constructor: "CSSUsageActor",
      type: { tab: true }
    });
    this.registerModule("devtools/server/actors/timeline", {
      prefix: "timeline",
      constructor: "TimelineActor",
      type: { tab: true }
    });
    if ("nsIProfiler" in Ci &&
        !Services.prefs.getBoolPref("devtools.performance.new-panel-enabled", false)) {
      this.registerModule("devtools/server/actors/performance", {
        prefix: "performance",
        constructor: "PerformanceActor",
        type: { tab: true }
      });
    }
    this.registerModule("devtools/server/actors/animation", {
      prefix: "animations",
      constructor: "AnimationsActor",
      type: { tab: true }
    });
    this.registerModule("devtools/server/actors/promises", {
      prefix: "promises",
      constructor: "PromisesActor",
      type: { tab: true }
    });
    this.registerModule("devtools/server/actors/emulation", {
      prefix: "emulation",
      constructor: "EmulationActor",
      type: { tab: true }
    });
    this.registerModule("devtools/server/actors/webextension-inspected-window", {
      prefix: "webExtensionInspectedWindow",
      constructor: "WebExtensionInspectedWindowActor",
      type: { tab: true }
    });
    this.registerModule("devtools/server/actors/accessibility", {
      prefix: "accessibility",
      constructor: "AccessibilityActor",
      type: { tab: true }
    });
  },

  /**
   * Passes a set of options to the BrowserAddonActors for the given ID.
   *
   * @param id string
   *        The ID of the add-on to pass the options to
   * @param options object
   *        The options.
   * @return a promise that will be resolved when complete.
   */
  setAddonOptions(id, options) {
    if (!this._initialized) {
      return Promise.resolve();
    }

    let promises = [];

    // Pass to all connections
    for (let connID of Object.getOwnPropertyNames(this._connections)) {
      promises.push(this._connections[connID].setAddonOptions(id, options));
    }

    return Promise.all(promises);
  },

  get listeningSockets() {
    return this._listeners.length;
  },

  /**
   * Creates a socket listener for remote debugger connections.
   *
   * After calling this, set some socket options, such as the port / path to
   * listen on, and then call |open| on the listener.
   *
   * See SocketListener in devtools/shared/security/socket.js for available
   * options.
   *
   * @return SocketListener
   *         A SocketListener instance that is waiting to be configured and
   *         opened is returned.  This single listener can be closed at any
   *         later time by calling |close| on the SocketListener.  If remote
   *         connections are disabled, an error is thrown.
   */
  createListener() {
    if (!Services.prefs.getBoolPref("devtools.debugger.remote-enabled")) {
      throw new Error("Can't create listener, remote debugging disabled");
    }
    this._checkInit();
    return DebuggerSocket.createListener();
  },

  /**
   * Add a SocketListener instance to the server's set of active
   * SocketListeners.  This is called by a SocketListener after it is opened.
   */
  _addListener(listener) {
    this._listeners.push(listener);
  },

  /**
   * Remove a SocketListener instance from the server's set of active
   * SocketListeners.  This is called by a SocketListener after it is closed.
   */
  _removeListener(listener) {
    this._listeners = this._listeners.filter(l => l !== listener);
  },

  /**
   * Closes and forgets all previously opened listeners.
   *
   * @return boolean
   *         Whether any listeners were actually closed.
   */
  closeAllListeners() {
    if (!this.listeningSockets) {
      return false;
    }

    for (let listener of this._listeners) {
      listener.close();
    }

    return true;
  },

  /**
   * Creates a new connection to the local debugger speaking over a fake
   * transport. This connection results in straightforward calls to the onPacket
   * handlers of each side.
   *
   * @param prefix string [optional]
   *    If given, all actors in this connection will have names starting
   *    with |prefix + '/'|.
   * @returns a client-side DebuggerTransport for communicating with
   *    the newly-created connection.
   */
  connectPipe(prefix) {
    this._checkInit();

    let serverTransport = new LocalDebuggerTransport();
    let clientTransport = new LocalDebuggerTransport(serverTransport);
    serverTransport.other = clientTransport;
    let connection = this._onConnection(serverTransport, prefix);

    // I'm putting this here because I trust you.
    //
    // There are times, when using a local connection, when you're going
    // to be tempted to just get direct access to the server.  Resist that
    // temptation!  If you succumb to that temptation, you will make the
    // fine developers that work on Fennec and Firefox OS sad.  They're
    // professionals, they'll try to act like they understand, but deep
    // down you'll know that you hurt them.
    //
    // This reference allows you to give in to that temptation.  There are
    // times this makes sense: tests, for example, and while porting a
    // previously local-only codebase to the remote protocol.
    //
    // But every time you use this, you will feel the shame of having
    // used a property that starts with a '_'.
    clientTransport._serverConnection = connection;

    return clientTransport;
  },

  /**
   * In a content child process, create a new connection that exchanges
   * nsIMessageSender messages with our parent process.
   *
   * @param prefix
   *    The prefix we should use in our nsIMessageSender message names and
   *    actor names. This connection will use messages named
   *    "debug:<prefix>:packet", and all its actors will have names
   *    beginning with "<prefix>/".
   */
  connectToParent(prefix, scopeOrManager) {
    this._checkInit();

    let transport = isWorker ?
                    new WorkerDebuggerTransport(scopeOrManager, prefix) :
                    new ChildDebuggerTransport(scopeOrManager, prefix);

    return this._onConnection(transport, prefix, true);
  },

  connectToContent(connection, mm, onDestroy) {
    return new Promise(resolve => {
      let prefix = connection.allocID("content-process");
      let actor, childTransport;

      mm.addMessageListener("debug:content-process-actor", function listener(msg) {
        // Arbitrarily choose the first content process to reply
        // XXX: This code needs to be updated if we use more than one content process
        mm.removeMessageListener("debug:content-process-actor", listener);

        // Pipe Debugger message from/to parent/child via the message manager
        childTransport = new ChildDebuggerTransport(mm, prefix);
        childTransport.hooks = {
          onPacket: connection.send.bind(connection),
          onClosed() {}
        };
        childTransport.ready();

        connection.setForwarding(prefix, childTransport);

        dumpn("establishing forwarding for process with prefix " + prefix);

        actor = msg.json.actor;

        resolve(actor);
      });

      // Load the content process debugger server script only once.
      if (!this._contentProcessScriptLoaded) {
        // Load the process script that will receive the debug:init-content-server message
        Services.ppmm.loadProcessScript(CONTENT_PROCESS_DBG_SERVER_SCRIPT, true);
        this._contentProcessScriptLoaded = true;
      }

      // Send a message to the content process debugger server script to forward it the
      // prefix.
      mm.sendAsyncMessage("debug:init-content-server", {
        prefix: prefix
      });

      function onClose() {
        Services.obs.removeObserver(onMessageManagerClose, "message-manager-close");
        EventEmitter.off(connection, "closed", onClose);
        if (childTransport) {
          // If we have a child transport, the actor has already
          // been created. We need to stop using this message manager.
          childTransport.close();
          childTransport = null;
          connection.cancelForwarding(prefix);

          // ... and notify the child process to clean the tab actors.
          try {
            mm.sendAsyncMessage("debug:content-process-destroy");
          } catch (e) {
            // Nothing to do
          }
        }

        if (onDestroy) {
          onDestroy(mm);
        }
      }

      let onMessageManagerClose = DevToolsUtils.makeInfallible((subject, topic, data) => {
        if (subject == mm) {
          onClose();
          connection.send({ from: actor.actor, type: "tabDetached" });
        }
      });
      Services.obs.addObserver(onMessageManagerClose,
                               "message-manager-close");

      EventEmitter.on(connection, "closed", onClose);
    });
  },

  connectToWorker(connection, dbg, id, options) {
    return new Promise((resolve, reject) => {
      // Step 1: Ensure the worker debugger is initialized.
      if (!dbg.isInitialized) {
        dbg.initialize("resource://devtools/server/worker.js");

        // Create a listener for rpc requests from the worker debugger. Only do
        // this once, when the worker debugger is first initialized, rather than
        // for each connection.
        let listener = {
          onClose: () => {
            dbg.removeListener(listener);
          },

          onMessage: (message) => {
            message = JSON.parse(message);
            if (message.type !== "rpc") {
              return;
            }

            Promise.resolve().then(() => {
              let method = {
                "fetch": DevToolsUtils.fetch,
              }[message.method];
              if (!method) {
                throw Error("Unknown method: " + message.method);
              }

              return method.apply(undefined, message.params);
            }).then((value) => {
              dbg.postMessage(JSON.stringify({
                type: "rpc",
                result: value,
                error: null,
                id: message.id
              }));
            }, (reason) => {
              dbg.postMessage(JSON.stringify({
                type: "rpc",
                result: null,
                error: reason,
                id: message.id
              }));
            });
          }
        };

        dbg.addListener(listener);
      }

      // Step 2: Send a connect request to the worker debugger.
      dbg.postMessage(JSON.stringify({
        type: "connect",
        id,
        options,
      }));

      // Steps 3-5 are performed on the worker thread (see worker.js).

      // Step 6: Wait for a connection response from the worker debugger.
      let listener = {
        onClose: () => {
          dbg.removeListener(listener);

          reject("closed");
        },

        onMessage: (message) => {
          message = JSON.parse(message);
          if (message.type !== "connected" || message.id !== id) {
            return;
          }

          // The initial connection message has been received, don't
          // need to listen any longer
          dbg.removeListener(listener);

          // Step 7: Create a transport for the connection to the worker.
          let transport = new WorkerDebuggerTransport(dbg, id);
          transport.ready();
          transport.hooks = {
            onClosed: () => {
              if (!dbg.isClosed) {
                // If the worker happens to be shutting down while we are trying
                // to close the connection, there is a small interval during
                // which no more runnables can be dispatched to the worker, but
                // the worker debugger has not yet been closed. In that case,
                // the call to postMessage below will fail. The onClosed hook on
                // DebuggerTransport is not supposed to throw exceptions, so we
                // need to make sure to catch these early.
                try {
                  dbg.postMessage(JSON.stringify({
                    type: "disconnect",
                    id,
                  }));
                } catch (e) {
                  // We can safely ignore these exceptions. The only time the
                  // call to postMessage can fail is if the worker is either
                  // shutting down, or has finished shutting down. In both
                  // cases, there is nothing to clean up, so we don't care
                  // whether this message arrives or not.
                }
              }

              connection.cancelForwarding(id);
            },

            onPacket: (packet) => {
              // Ensure that any packets received from the server on the worker
              // thread are forwarded to the client on the main thread, as if
              // they had been sent by the server on the main thread.
              connection.send(packet);
            }
          };

          // Ensure that any packets received from the client on the main thread
          // to actors on the worker thread are forwarded to the server on the
          // worker thread.
          connection.setForwarding(id, transport);

          resolve({
            threadActor: message.threadActor,
            consoleActor: message.consoleActor,
            transport: transport
          });
        }
      };
      dbg.addListener(listener);
    });
  },

  /**
   * Check if the server is running in the child process.
   */
  get isInChildProcess() {
    return Services.appinfo.processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
  },

  /**
   * In a chrome parent process, ask all content child processes
   * to execute a given module setup helper.
   *
   * @param module
   *        The module to be required
   * @param setupChild
   *        The name of the setup helper exported by the above module
   *        (setup helper signature: function ({mm}) { ... })
   * @param waitForEval (optional)
   *        If true, the returned promise only resolves once code in child
   *        is evaluated
   */
  setupInChild({ module, setupChild, args, waitForEval }) {
    if (this._childMessageManagers.size == 0) {
      return Promise.resolve();
    }
    return new Promise(done => {
      // If waitForEval is set, pass a unique id and expect child.js to send
      // a message back once the code in child is evaluated.
      if (typeof (waitForEval) != "boolean") {
        waitForEval = false;
      }
      let count = this._childMessageManagers.size;
      let id = waitForEval ? generateUUID().toString() : null;

      this._childMessageManagers.forEach(mm => {
        if (waitForEval) {
          // Listen for the end of each child execution
          let evalListener = msg => {
            if (msg.data.id !== id) {
              return;
            }
            mm.removeMessageListener("debug:setup-in-child-response", evalListener);
            if (--count === 0) {
              done();
            }
          };
          mm.addMessageListener("debug:setup-in-child-response", evalListener);
        }
        mm.sendAsyncMessage("debug:setup-in-child", {
          module: module,
          setupChild: setupChild,
          args: args,
          id: id,
        });
      });

      if (!waitForEval) {
        done();
      }
    });
  },

  /**
   * Live list of all currenctly attached child's message managers.
   */
  _childMessageManagers: new Set(),

  /**
   * Connect to a child process.
   *
   * @param object connection
   *        The debugger server connection to use.
   * @param nsIDOMElement frame
   *        The browser element that holds the child process.
   * @param function [onDestroy]
   *        Optional function to invoke when the child process closes
   *        or the connection shuts down. (Need to forget about the
   *        related TabActor)
   * @return object
   *         A promise object that is resolved once the connection is
   *         established.
   */
  connectToChild(connection, frame, onDestroy, {addonId} = {}) {
    return new Promise(resolve => {
      // Get messageManager from XUL browser (which might be a specialized tunnel for RDM)
      // or else fallback to asking the frameLoader itself.
      let mm = frame.messageManager || frame.frameLoader.messageManager;
      mm.loadFrameScript("resource://devtools/server/child.js", false);

      let trackMessageManager = () => {
        frame.addEventListener("DevTools:BrowserSwap", onBrowserSwap);
        mm.addMessageListener("debug:setup-in-parent", onSetupInParent);
        if (!actor) {
          mm.addMessageListener("debug:actor", onActorCreated);
        }
        DebuggerServer._childMessageManagers.add(mm);
      };

      let untrackMessageManager = () => {
        frame.removeEventListener("DevTools:BrowserSwap", onBrowserSwap);
        mm.removeMessageListener("debug:setup-in-parent", onSetupInParent);
        if (!actor) {
          mm.removeMessageListener("debug:actor", onActorCreated);
        }
        DebuggerServer._childMessageManagers.delete(mm);
      };

      let actor, childTransport;
      let prefix = connection.allocID("child");
      // Compute the same prefix that's used by DebuggerServerConnection
      let connPrefix = prefix + "/";

      // provides hook to actor modules that need to exchange messages
      // between e10s parent and child processes
      let parentModules = [];
      let onSetupInParent = function (msg) {
        // We may have multiple connectToChild instance running for the same tab
        // and need to filter the messages.
        if (msg.json.prefix != connPrefix) {
          return false;
        }

        let { module, setupParent } = msg.json;
        let m;

        try {
          m = require(module);

          if (!(setupParent in m)) {
            dumpn(`ERROR: module '${module}' does not export '${setupParent}'`);
            return false;
          }

          parentModules.push(m[setupParent]({ mm, prefix: connPrefix }));

          return true;
        } catch (e) {
          let errorMessage =
            "Exception during actor module setup running in the parent process: ";
          DevToolsUtils.reportException(errorMessage + e);
          dumpn(`ERROR: ${errorMessage}\n\t module: '${module}'\n\t ` +
                `setupParent: '${setupParent}'\n${DevToolsUtils.safeErrorString(e)}`);
          return false;
        }
      };

      let onActorCreated = DevToolsUtils.makeInfallible(function (msg) {
        if (msg.json.prefix != prefix) {
          return;
        }
        mm.removeMessageListener("debug:actor", onActorCreated);

        // Pipe Debugger message from/to parent/child via the message manager
        childTransport = new ChildDebuggerTransport(mm, prefix);
        childTransport.hooks = {
          onPacket: connection.send.bind(connection),
          onClosed() {}
        };
        childTransport.ready();

        connection.setForwarding(prefix, childTransport);

        dumpn("establishing forwarding for app with prefix " + prefix);

        actor = msg.json.actor;
        resolve(actor);
      }).bind(this);

      // Listen for browser frame swap
      let onBrowserSwap = ({ detail: newFrame }) => {
        // Remove listeners from old frame and mm
        untrackMessageManager();
        // Update frame and mm to point to the new browser frame
        frame = newFrame;
        // Get messageManager from XUL browser (which might be a specialized tunnel for
        // RDM) or else fallback to asking the frameLoader itself.
        mm = frame.messageManager || frame.frameLoader.messageManager;
        // Add listeners to new frame and mm
        trackMessageManager();

        // provides hook to actor modules that need to exchange messages
        // between e10s parent and child processes
        parentModules.forEach(mod => {
          if (mod.onBrowserSwap) {
            mod.onBrowserSwap(mm);
          }
        });

        if (childTransport) {
          childTransport.swapBrowser(mm);
        }
      };

      let destroy = DevToolsUtils.makeInfallible(function () {
        EventEmitter.off(connection, "closed", destroy);
        Services.obs.removeObserver(onMessageManagerClose, "message-manager-close");

        // provides hook to actor modules that need to exchange messages
        // between e10s parent and child processes
        parentModules.forEach(mod => {
          if (mod.onDisconnected) {
            mod.onDisconnected();
          }
        });
        // TODO: Remove this deprecated path once it's no longer needed by add-ons.
        DebuggerServer.emit("disconnected-from-child:" + connPrefix,
                            { mm, prefix: connPrefix });

        if (childTransport) {
          // If we have a child transport, the actor has already
          // been created. We need to stop using this message manager.
          childTransport.close();
          childTransport = null;
          connection.cancelForwarding(prefix);

          // ... and notify the child process to clean the tab actors.
          try {
            // Bug 1169643: Ignore any exception as the child process
            // may already be destroyed by now.
            mm.sendAsyncMessage("debug:disconnect", { prefix });
          } catch (e) {
            // Nothing to do
          }
        } else {
          // Otherwise, the app has been closed before the actor
          // had a chance to be created, so we are not able to create
          // the actor.
          resolve(null);
        }
        if (actor) {
          // The ContentActor within the child process doesn't necessary
          // have time to uninitialize itself when the app is closed/killed.
          // So ensure telling the client that the related actor is detached.
          connection.send({ from: actor.actor, type: "tabDetached" });
          actor = null;
        }

        if (onDestroy) {
          onDestroy(mm);
        }

        // Cleanup all listeners
        untrackMessageManager();
      });

      // Listen for various messages and frame events
      trackMessageManager();

      // Listen for app process exit
      let onMessageManagerClose = function (subject, topic, data) {
        if (subject == mm) {
          destroy();
        }
      };
      Services.obs.addObserver(onMessageManagerClose,
                               "message-manager-close");

      // Listen for connection close to cleanup things
      // when user unplug the device or we lose the connection somehow.
      EventEmitter.on(connection, "closed", destroy);

      mm.sendAsyncMessage("debug:connect", { prefix, addonId });
    });
  },

  /**
   * Create a new debugger connection for the given transport. Called after
   * connectPipe(), from connectToParent, or from an incoming socket
   * connection handler.
   *
   * If present, |forwardingPrefix| is a forwarding prefix that a parent
   * server is using to recognizes messages intended for this server. Ensure
   * that all our actors have names beginning with |forwardingPrefix + '/'|.
   * In particular, the root actor's name will be |forwardingPrefix + '/root'|.
   */
  _onConnection(transport, forwardingPrefix, noRootActor = false) {
    let connID;
    if (forwardingPrefix) {
      connID = forwardingPrefix + "/";
    } else {
      // Multiple servers can be started at the same time, and when that's the
      // case, they are loaded in separate devtools loaders.
      // So, use the current loader ID to prefix the connection ID and make it
      // unique.
      connID = "server" + loader.id + ".conn" + this._nextConnID++ + ".";
    }

    let conn = new DebuggerServerConnection(connID, transport);
    this._connections[connID] = conn;

    // Create a root actor for the connection and send the hello packet.
    if (!noRootActor) {
      conn.rootActor = this.createRootActor(conn);
      if (forwardingPrefix) {
        conn.rootActor.actorID = forwardingPrefix + "/root";
      } else {
        conn.rootActor.actorID = "root";
      }
      conn.addActor(conn.rootActor);
      transport.send(conn.rootActor.sayHello());
    }
    transport.ready();

    this.emit("connectionchange", "opened", conn);
    return conn;
  },

  /**
   * Remove the connection from the debugging server.
   */
  _connectionClosed(connection) {
    delete this._connections[connection.prefix];
    this.emit("connectionchange", "closed", connection);
  },

  // DebuggerServer extension API.

  setRootActor(actorFactory) {
    this.createRootActor = actorFactory;
  },

  /**
   * Registers handlers for new tab-scoped request types defined dynamically.
   * This is used for example by add-ons to augment the functionality of the tab
   * actor. Note that the name or actorPrefix of the request type is not allowed
   * to clash with existing protocol packet properties, like 'title', 'url' or
   * 'actor', since that would break the protocol.
   *
   * @param actor function, object
   *      In case of function:
   *        The constructor function for this request type. This expects to be
   *        called as a constructor (i.e. with 'new'), and passed two
   *        arguments: the DebuggerServerConnection, and the BrowserTabActor
   *        with which it will be associated.
   *        Only used for deprecated eagerly loaded actors.
   *      In case of object:
   *        First argument of RegisteredActorFactory constructor.
   *        See the it's definition for more info.
   *
   * @param name string [optional]
   *        The name of the new request type. If this is not present, the
   *        actorPrefix property of the constructor prototype is used.
   */
  addTabActor(actor, name = actor.prototype.actorPrefix) {
    if (["title", "url", "actor"].includes(name)) {
      throw Error(name + " is not allowed");
    }
    if (DebuggerServer.tabActorFactories.hasOwnProperty(name)) {
      throw Error(name + " already exists");
    }
    DebuggerServer.tabActorFactories[name] = new RegisteredActorFactory(actor, name);
  },

  /**
   * Unregisters the handler for the specified tab-scoped request type.
   * This may be used for example by add-ons when shutting down or upgrading.
   * When unregistering an existing tab actor remove related tab factory
   * as well as all existing instances of the actor.
   *
   * @param actor function, object
   *      In case of function:
   *        The constructor function for this request type.
   *      In case of object:
   *        Same object being given to related addTabActor call.
   */
  removeTabActor(actor) {
    for (let name in DebuggerServer.tabActorFactories) {
      let handler = DebuggerServer.tabActorFactories[name];
      if ((handler.name && handler.name == actor.name) ||
          (handler.id && handler.id == actor.id)) {
        delete DebuggerServer.tabActorFactories[name];
        for (let connID of Object.getOwnPropertyNames(this._connections)) {
          // DebuggerServerConnection in child process don't have rootActor
          if (this._connections[connID].rootActor) {
            this._connections[connID].rootActor.removeActorByName(name);
          }
        }
      }
    }
  },

  /**
   * Registers handlers for new browser-scoped request types defined
   * dynamically. This is used for example by add-ons to augment the
   * functionality of the root actor. Note that the name or actorPrefix of the
   * request type is not allowed to clash with existing protocol packet
   * properties, like 'from', 'tabs' or 'selected', since that would break the
   * protocol.
   *
   * @param actor function, object
   *      In case of function:
   *        The constructor function for this request type. This expects to be
   *        called as a constructor (i.e. with 'new'), and passed two
   *        arguments: the DebuggerServerConnection, and the BrowserRootActor
   *        with which it will be associated.
   *        Only used for deprecated eagerly loaded actors.
   *      In case of object:
   *        First argument of RegisteredActorFactory constructor.
   *        See the it's definition for more info.
   *
   * @param name string [optional]
   *        The name of the new request type. If this is not present, the
   *        actorPrefix property of the constructor prototype is used.
   */
  addGlobalActor(actor, name = actor.prototype.actorPrefix) {
    if (["from", "tabs", "selected"].includes(name)) {
      throw Error(name + " is not allowed");
    }
    if (DebuggerServer.globalActorFactories.hasOwnProperty(name)) {
      throw Error(name + " already exists");
    }
    DebuggerServer.globalActorFactories[name] = new RegisteredActorFactory(actor, name);
  },

  /**
   * Unregisters the handler for the specified browser-scoped request type.
   * This may be used for example by add-ons when shutting down or upgrading.
   * When unregistering an existing global actor remove related global factory
   * as well as all existing instances of the actor.
   *
   * @param actor function, object
   *      In case of function:
   *        The constructor function for this request type.
   *      In case of object:
   *        Same object being given to related addGlobalActor call.
   */
  removeGlobalActor(actor) {
    for (let name in DebuggerServer.globalActorFactories) {
      let handler = DebuggerServer.globalActorFactories[name];
      if ((handler.name && handler.name == actor.name) ||
          (handler.id && handler.id == actor.id)) {
        delete DebuggerServer.globalActorFactories[name];
        for (let connID of Object.getOwnPropertyNames(this._connections)) {
          this._connections[connID].rootActor.removeActorByName(name);
        }
      }
    }
  },

  /**
   * Called when DevTools are unloaded to remove the contend process server script for the
   * list of scripts loaded for each new content process. Will also remove message
   * listeners from already loaded scripts.
   */
  removeContentServerScript() {
    Services.ppmm.removeDelayedProcessScript(CONTENT_PROCESS_DBG_SERVER_SCRIPT);
    try {
      Services.ppmm.broadcastAsyncMessage("debug:close-content-server");
    } catch (e) {
      // Nothing to do
    }
  },

  /**
   * Searches all active connections for an actor matching an ID.
   *
   * ⚠ TO BE USED ONLY FROM SERVER CODE OR TESTING ONLY! ⚠`
   *
   * This is helpful for some tests which depend on reaching into the server to check some
   * properties of an actor, and it is also used by the actors related to the
   * DevTools WebExtensions API to be able to interact with the actors created for the
   * panels natively provided by the DevTools Toolbox.
   */
  searchAllConnectionsForActor(actorID) {
    // NOTE: the actor IDs are generated with the following format:
    //
    //   `server${loaderID}.conn${ConnectionID}${ActorPrefix}${ActorID}`
    //
    // as an optimization we can come up with a regexp to query only
    // the right connection via its id.
    for (let connID of Object.getOwnPropertyNames(this._connections)) {
      let actor = this._connections[connID].getActor(actorID);
      if (actor) {
        return actor;
      }
    }
    return null;
  },
};

// Expose these to save callers the trouble of importing DebuggerSocket
DevToolsUtils.defineLazyGetter(DebuggerServer, "Authenticators", () => {
  return Authentication.Authenticators;
});
DevToolsUtils.defineLazyGetter(DebuggerServer, "AuthenticationResult", () => {
  return Authentication.AuthenticationResult;
});

EventEmitter.decorate(DebuggerServer);

if (this.exports) {
  exports.DebuggerServer = DebuggerServer;
  exports.ActorPool = ActorPool;
  exports.OriginalLocation = OriginalLocation;
}

// Needed on B2G (See header note)
this.DebuggerServer = DebuggerServer;
this.ActorPool = ActorPool;
this.OriginalLocation = OriginalLocation;

// When using DebuggerServer.addActors, some symbols are expected to be in
// the scope of the added actor even before the corresponding modules are
// loaded, so let's explicitly bind the expected symbols here.
var includes = ["Components", "Ci", "Cu", "require", "Services", "DebuggerServer",
                "ActorPool", "DevToolsUtils"];
includes.forEach(name => {
  DebuggerServer[name] = this[name];
});

/**
 * Creates a DebuggerServerConnection.
 *
 * Represents a connection to this debugging global from a client.
 * Manages a set of actors and actor pools, allocates actor ids, and
 * handles incoming requests.
 *
 * @param prefix string
 *        All actor IDs created by this connection should be prefixed
 *        with prefix.
 * @param transport transport
 *        Packet transport for the debugging protocol.
 */
function DebuggerServerConnection(prefix, transport) {
  this._prefix = prefix;
  this._transport = transport;
  this._transport.hooks = this;
  this._nextID = 1;

  this._actorPool = new ActorPool(this);
  this._extraPools = [this._actorPool];

  // Responses to a given actor must be returned the the client
  // in the same order as the requests that they're replying to, but
  // Implementations might finish serving requests in a different
  // order.  To keep things in order we generate a promise for each
  // request, chained to the promise for the request before it.
  // This map stores the latest request promise in the chain, keyed
  // by an actor ID string.
  this._actorResponses = new Map();

  /*
   * We can forward packets to other servers, if the actors on that server
   * all use a distinct prefix on their names. This is a map from prefixes
   * to transports: it maps a prefix P to a transport T if T conveys
   * packets to the server whose actors' names all begin with P + "/".
   */
  this._forwardingPrefixes = new Map();
}

DebuggerServerConnection.prototype = {
  _prefix: null,
  get prefix() {
    return this._prefix;
  },

  _transport: null,
  get transport() {
    return this._transport;
  },

  /**
   * Message manager used to communicate with the parent process,
   * set by child.js. Is only defined for connections instantiated
   * within a child process.
   */
  parentMessageManager: null,

  close() {
    if (this._transport) {
      this._transport.close();
    }
  },

  send(packet) {
    this.transport.send(packet);
  },

  /**
   * Used when sending a bulk reply from an actor.
   * @see DebuggerTransport.prototype.startBulkSend
   */
  startBulkSend(header) {
    return this.transport.startBulkSend(header);
  },

  allocID(prefix) {
    return this.prefix + (prefix || "") + this._nextID++;
  },

  /**
   * Add a map of actor IDs to the connection.
   */
  addActorPool(actorPool) {
    this._extraPools.push(actorPool);
  },

  /**
   * Remove a previously-added pool of actors to the connection.
   *
   * @param ActorPool actorPool
   *        The ActorPool instance you want to remove.
   * @param boolean noCleanup [optional]
   *        True if you don't want to destroy each actor from the pool, false
   *        otherwise.
   */
  removeActorPool(actorPool, noCleanup) {
    // When a connection is closed, it removes each of its actor pools. When an
    // actor pool is removed, it calls the destroy method on each of its
    // actors. Some actors, such as ThreadActor, manage their own actor pools.
    // When the destroy method is called on these actors, they manually
    // remove their actor pools. Consequently, this method is reentrant.
    //
    // In addition, some actors, such as ThreadActor, perform asynchronous work
    // (in the case of ThreadActor, because they need to resume), before they
    // remove each of their actor pools. Since we don't wait for this work to
    // be completed, we can end up in this function recursively after the
    // connection already set this._extraPools to null.
    //
    // This is a bug: if the destroy method can perform asynchronous work,
    // then we should wait for that work to be completed before setting this.
    // _extraPools to null. As a temporary solution, it should be acceptable
    // to just return early (if this._extraPools has been set to null, all
    // actors pools for this connection should already have been removed).
    if (this._extraPools === null) {
      return;
    }
    let index = this._extraPools.lastIndexOf(actorPool);
    if (index > -1) {
      let pool = this._extraPools.splice(index, 1);
      if (!noCleanup) {
        pool.forEach(p => p.destroy());
      }
    }
  },

  /**
   * Add an actor to the default actor pool for this connection.
   */
  addActor(actor) {
    this._actorPool.addActor(actor);
  },

  /**
   * Remove an actor to the default actor pool for this connection.
   */
  removeActor(actor) {
    this._actorPool.removeActor(actor);
  },

  /**
   * Match the api expected by the protocol library.
   */
  unmanage(actor) {
    return this.removeActor(actor);
  },

  /**
   * Look up an actor implementation for an actorID.  Will search
   * all the actor pools registered with the connection.
   *
   * @param actorID string
   *        Actor ID to look up.
   */
  getActor(actorID) {
    let pool = this.poolFor(actorID);
    if (pool) {
      return pool.get(actorID);
    }

    if (actorID === "root") {
      return this.rootActor;
    }

    return null;
  },

  _getOrCreateActor(actorID) {
    let actor = this.getActor(actorID);
    if (!actor) {
      this.transport.send({ from: actorID ? actorID : "root",
                            error: "noSuchActor",
                            message: "No such actor for ID: " + actorID });
      return null;
    }

    // Dynamically-loaded actors have to be created lazily.
    if (actor instanceof ObservedActorFactory) {
      try {
        actor = actor.createActor();
      } catch (e) {
        this.transport.send(this._unknownError(
          "Error occurred while creating actor '" + actor.name,
          e));
      }
    } else if (typeof (actor) !== "object") {
      // ActorPools should now contain only actor instances (i.e. objects)
      // or ObservedActorFactory instances.
      throw new Error("Unexpected actor constructor/function in ActorPool " +
                      "for actorID=" + actorID + ".");
    }

    return actor;
  },

  poolFor(actorID) {
    for (let pool of this._extraPools) {
      if (pool.has(actorID)) {
        return pool;
      }
    }
    return null;
  },

  _unknownError(prefix, error) {
    let errorString = prefix + ": " + DevToolsUtils.safeErrorString(error);
    reportError(errorString);
    dumpn(errorString);
    return {
      error: "unknownError",
      message: errorString
    };
  },

  _queueResponse: function (from, type, responseOrPromise) {
    let pendingResponse = this._actorResponses.get(from) || Promise.resolve(null);
    let responsePromise = pendingResponse.then(() => {
      return responseOrPromise;
    }).then(response => {
      if (!this.transport) {
        throw new Error(`Connection closed, pending response from ${from}, ` +
                        `type ${type} failed`);
      }
      if (!response.from) {
        response.from = from;
      }
      this.transport.send(response);
    }).catch((e) => {
      if (!this.transport) {
        throw new Error(`Connection closed, pending error from ${from}, ` +
                        `type ${type} failed`);
      }
      let errorPacket = this._unknownError(
        "error occurred while processing '" + type, e);
      errorPacket.from = from;
      this.transport.send(errorPacket);
    });

    this._actorResponses.set(from, responsePromise);
  },

  /**
   * Passes a set of options to the BrowserAddonActors for the given ID.
   *
   * @param id string
   *        The ID of the add-on to pass the options to
   * @param options object
   *        The options.
   * @return a promise that will be resolved when complete.
   */
  setAddonOptions(id, options) {
    let addonList = this.rootActor._parameters.addonList;
    if (!addonList) {
      return Promise.resolve();
    }
    return addonList.getList().then((addonActors) => {
      for (let actor of addonActors) {
        if (actor.id != id) {
          continue;
        }
        actor.setOptions(options);
        return;
      }
    });
  },

  /* Forwarding packets to other transports based on actor name prefixes. */

  /*
   * Arrange to forward packets to another server. This is how we
   * forward debugging connections to child processes.
   *
   * If we receive a packet for an actor whose name begins with |prefix|
   * followed by '/', then we will forward that packet to |transport|.
   *
   * This overrides any prior forwarding for |prefix|.
   *
   * @param prefix string
   *    The actor name prefix, not including the '/'.
   * @param transport object
   *    A packet transport to which we should forward packets to actors
   *    whose names begin with |(prefix + '/').|
   */
  setForwarding(prefix, transport) {
    this._forwardingPrefixes.set(prefix, transport);
  },

  /*
   * Stop forwarding messages to actors whose names begin with
   * |prefix+'/'|. Such messages will now elicit 'noSuchActor' errors.
   */
  cancelForwarding(prefix) {
    this._forwardingPrefixes.delete(prefix);

    // Notify the client that forwarding in now cancelled for this prefix.
    // There could be requests in progress that the client should abort rather leaving
    // handing indefinitely.
    if (this.rootActor) {
      this.send(this.rootActor.forwardingCancelled(prefix));
    }
  },

  sendActorEvent(actorID, eventName, event = {}) {
    event.from = actorID;
    event.type = eventName;
    this.send(event);
  },

  // Transport hooks.

  /**
   * Called by DebuggerTransport to dispatch incoming packets as appropriate.
   *
   * @param packet object
   *        The incoming packet.
   */
  onPacket(packet) {
    // If the actor's name begins with a prefix we've been asked to
    // forward, do so.
    //
    // Note that the presence of a prefix alone doesn't indicate that
    // forwarding is needed: in DebuggerServerConnection instances in child
    // processes, every actor has a prefixed name.
    if (this._forwardingPrefixes.size > 0) {
      let to = packet.to;
      let separator = to.lastIndexOf("/");
      while (separator >= 0) {
        to = to.substring(0, separator);
        let forwardTo = this._forwardingPrefixes.get(packet.to.substring(0, separator));
        if (forwardTo) {
          forwardTo.send(packet);
          return;
        }
        separator = to.lastIndexOf("/");
      }
    }

    let actor = this._getOrCreateActor(packet.to);
    if (!actor) {
      return;
    }

    let ret = null;

    // handle "requestTypes" RDP request.
    if (packet.type == "requestTypes") {
      ret = { from: actor.actorID, requestTypes: Object.keys(actor.requestTypes) };
    } else if (actor.requestTypes && actor.requestTypes[packet.type]) {
      // Dispatch the request to the actor.
      try {
        this.currentPacket = packet;
        ret = actor.requestTypes[packet.type].bind(actor)(packet, this);
      } catch (e) {
        this.transport.send(this._unknownError(
          "error occurred while processing '" + packet.type,
          e));
      } finally {
        this.currentPacket = undefined;
      }
    } else {
      ret = { error: "unrecognizedPacketType",
              message: ("Actor " + actor.actorID +
                        " does not recognize the packet type " +
                        packet.type) };
    }

    // There will not be a return value if a bulk reply is sent.
    if (ret) {
      this._queueResponse(packet.to, packet.type, ret);
    }
  },

  /**
   * Called by the DebuggerTransport to dispatch incoming bulk packets as
   * appropriate.
   *
   * @param packet object
   *        The incoming packet, which contains:
   *        * actor:  Name of actor that will receive the packet
   *        * type:   Name of actor's method that should be called on receipt
   *        * length: Size of the data to be read
   *        * stream: This input stream should only be used directly if you can
   *                  ensure that you will read exactly |length| bytes and will
   *                  not close the stream when reading is complete
   *        * done:   If you use the stream directly (instead of |copyTo|
   *                  below), you must signal completion by resolving /
   *                  rejecting this deferred.  If it's rejected, the transport
   *                  will be closed.  If an Error is supplied as a rejection
   *                  value, it will be logged via |dumpn|.  If you do use
   *                  |copyTo|, resolving is taken care of for you when copying
   *                  completes.
   *        * copyTo: A helper function for getting your data out of the stream
   *                  that meets the stream handling requirements above, and has
   *                  the following signature:
   *          @param  output nsIAsyncOutputStream
   *                  The stream to copy to.
   *          @return Promise
   *                  The promise is resolved when copying completes or rejected
   *                  if any (unexpected) errors occur.
   *                  This object also emits "progress" events for each chunk
   *                  that is copied.  See stream-utils.js.
   */
  onBulkPacket(packet) {
    let { actor: actorKey, type } = packet;

    let actor = this._getOrCreateActor(actorKey);
    if (!actor) {
      return;
    }

    // Dispatch the request to the actor.
    let ret;
    if (actor.requestTypes && actor.requestTypes[type]) {
      try {
        ret = actor.requestTypes[type].call(actor, packet);
      } catch (e) {
        this.transport.send(this._unknownError(
          "error occurred while processing bulk packet '" + type, e));
        packet.done.reject(e);
      }
    } else {
      let message = "Actor " + actorKey +
                    " does not recognize the bulk packet type " + type;
      ret = { error: "unrecognizedPacketType",
              message: message };
      packet.done.reject(new Error(message));
    }

    // If there is a JSON response, queue it for sending back to the client.
    if (ret) {
      this._queueResponse(actorKey, type, ret);
    }
  },

  /**
   * Called by DebuggerTransport when the underlying stream is closed.
   *
   * @param status nsresult
   *        The status code that corresponds to the reason for closing
   *        the stream.
   */
  onClosed(status) {
    dumpn("Cleaning up connection.");
    if (!this._actorPool) {
      // Ignore this call if the connection is already closed.
      return;
    }
    this._actorPool = null;

    EventEmitter.emit(this, "closed", status);

    this._extraPools.forEach(p => p.destroy());
    this._extraPools = null;

    this.rootActor = null;
    this._transport = null;
    DebuggerServer._connectionClosed(this);
  },

  /*
   * Debugging helper for inspecting the state of the actor pools.
   */
  _dumpPools() {
    dumpn("/-------------------- dumping pools:");
    if (this._actorPool) {
      dumpn("--------------------- actorPool actors: " +
            uneval(Object.keys(this._actorPool._actors)));
    }
    for (let pool of this._extraPools) {
      if (pool !== this._actorPool) {
        dumpn("--------------------- extraPool actors: " +
              uneval(Object.keys(pool._actors)));
      }
    }
  },

  /*
   * Debugging helper for inspecting the state of an actor pool.
   */
  _dumpPool(pool) {
    dumpn("/-------------------- dumping pool:");
    dumpn("--------------------- actorPool actors: " +
          uneval(Object.keys(pool._actors)));
  },

  /**
   * In a content child process, ask the DebuggerServer in the parent process
   * to execute a given module setup helper.
   *
   * @param module
   *        The module to be required
   * @param setupParent
   *        The name of the setup helper exported by the above module
   *        (setup helper signature: function ({mm}) { ... })
   * @return boolean
   *         true if the setup helper returned successfully
   */
  setupInParent({ module, setupParent }) {
    if (!this.parentMessageManager) {
      return false;
    }

    let { sendSyncMessage } = this.parentMessageManager;

    return sendSyncMessage("debug:setup-in-parent", {
      prefix: this.prefix,
      module: module,
      setupParent: setupParent
    });
  },
};
PK
!<}�&D�
�
@chrome/devtools/modules/devtools/server/performance/framerate.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 very simple utility for monitoring framerate. Takes a `tabActor`
 * and monitors framerate over time. The actor wrapper around this
 * can be found at devtools/server/actors/framerate.js
 */
class Framerate {
  constructor(tabActor) {
    this.tabActor = tabActor;
    this._contentWin = tabActor.window;
    this._onRefreshDriverTick = this._onRefreshDriverTick.bind(this);
    this._onGlobalCreated = this._onGlobalCreated.bind(this);
    this.tabActor.on("window-ready", this._onGlobalCreated);
  }

  destroy(conn) {
    this.tabActor.off("window-ready", this._onGlobalCreated);
    this.stopRecording();
  }

  /**
   * Starts monitoring framerate, storing the frames per second.
   */
  startRecording() {
    if (this._recording) {
      return;
    }
    this._recording = true;
    this._ticks = [];
    this._startTime = this.tabActor.docShell.now();
    this._rafID = this._contentWin.requestAnimationFrame(this._onRefreshDriverTick);
  }

  /**
   * Stops monitoring framerate, returning the recorded values.
   */
  stopRecording(beginAt = 0, endAt = Number.MAX_SAFE_INTEGER) {
    if (!this._recording) {
      return [];
    }
    let ticks = this.getPendingTicks(beginAt, endAt);
    this.cancelRecording();
    return ticks;
  }

  /**
   * Stops monitoring framerate, without returning the recorded values.
   */
  cancelRecording() {
    this._contentWin.cancelAnimationFrame(this._rafID);
    this._recording = false;
    this._ticks = null;
    this._rafID = -1;
  }

  /**
   * Returns whether this instance is currently recording.
   */
  isRecording() {
    return !!this._recording;
  }

  /**
   * Gets the refresh driver ticks recorded so far.
   */
  getPendingTicks(beginAt = 0, endAt = Number.MAX_SAFE_INTEGER) {
    if (!this._ticks) {
      return [];
    }
    return this._ticks.filter(e => e >= beginAt && e <= endAt);
  }

  /**
   * Function invoked along with the refresh driver.
   */
  _onRefreshDriverTick() {
    if (!this._recording) {
      return;
    }
    this._rafID = this._contentWin.requestAnimationFrame(this._onRefreshDriverTick);
    this._ticks.push(this.tabActor.docShell.now() - this._startTime);
  }

  /**
   * When the content window for the tab actor is created.
   */
  _onGlobalCreated(win) {
    if (this._recording) {
      this._contentWin.cancelAnimationFrame(this._rafID);
      this._rafID = this._contentWin.requestAnimationFrame(this._onRefreshDriverTick);
    }
  }
}

exports.Framerate = Framerate;
PK
!<�~7�8�8=chrome/devtools/modules/devtools/server/performance/memory.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Cc, Ci, Cu } = require("chrome");
const { reportException } = require("devtools/shared/DevToolsUtils");
const { expectState } = require("devtools/server/actors/common");

loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
loader.lazyRequireGetter(this, "DeferredTask",
  "resource://gre/modules/DeferredTask.jsm", true);
loader.lazyRequireGetter(this, "StackFrameCache",
  "devtools/server/actors/utils/stack", true);
loader.lazyRequireGetter(this, "ChromeUtils");
loader.lazyRequireGetter(this, "HeapSnapshotFileUtils",
  "devtools/shared/heapsnapshot/HeapSnapshotFileUtils");
loader.lazyRequireGetter(this, "ChromeActor", "devtools/server/actors/chrome",
                         true);
loader.lazyRequireGetter(this, "ChildProcessActor",
                         "devtools/server/actors/child-process", true);

/**
 * A class that returns memory data for a parent actor's window.
 * Using a tab-scoped actor with this instance will measure the memory footprint of its
 * parent tab. Using a global-scoped actor instance however, will measure the memory
 * footprint of the chrome window referenced by its root actor.
 *
 * To be consumed by actor's, like MemoryActor using this module to
 * send information over RDP, and TimelineActor for using more light-weight
 * utilities like GC events and measuring memory consumption.
 */
function Memory(parent, frameCache = new StackFrameCache()) {
  EventEmitter.decorate(this);

  this.parent = parent;
  this._mgr = Cc["@mozilla.org/memory-reporter-manager;1"]
                .getService(Ci.nsIMemoryReporterManager);
  this.state = "detached";
  this._dbg = null;
  this._frameCache = frameCache;

  this._onGarbageCollection = this._onGarbageCollection.bind(this);
  this._emitAllocations = this._emitAllocations.bind(this);
  this._onWindowReady = this._onWindowReady.bind(this);

  EventEmitter.on(this.parent, "window-ready", this._onWindowReady);
}

Memory.prototype = {
  destroy: function () {
    EventEmitter.off(this.parent, "window-ready", this._onWindowReady);

    this._mgr = null;
    if (this.state === "attached") {
      this.detach();
    }
  },

  get dbg() {
    if (!this._dbg) {
      this._dbg = this.parent.makeDebugger();
    }
    return this._dbg;
  },

  /**
   * Attach to this MemoryBridge.
   *
   * This attaches the MemoryBridge's Debugger instance so that you can start
   * recording allocations or take a census of the heap. In addition, the
   * MemoryBridge will start emitting GC events.
   */
  attach: expectState("detached", function () {
    this.dbg.addDebuggees();
    this.dbg.memory.onGarbageCollection = this._onGarbageCollection.bind(this);
    this.state = "attached";
  }, "attaching to the debugger"),

  /**
   * Detach from this MemoryBridge.
   */
  detach: expectState("attached", function () {
    this._clearDebuggees();
    this.dbg.enabled = false;
    this._dbg = null;
    this.state = "detached";
  }, "detaching from the debugger"),

  /**
   * Gets the current MemoryBridge attach/detach state.
   */
  getState: function () {
    return this.state;
  },

  _clearDebuggees: function () {
    if (this._dbg) {
      if (this.isRecordingAllocations()) {
        this.dbg.memory.drainAllocationsLog();
      }
      this._clearFrames();
      this.dbg.removeAllDebuggees();
    }
  },

  _clearFrames: function () {
    if (this.isRecordingAllocations()) {
      this._frameCache.clearFrames();
    }
  },

  /**
   * Handler for the parent actor's "window-ready" event.
   */
  _onWindowReady: function ({ isTopLevel }) {
    if (this.state == "attached") {
      this._clearDebuggees();
      if (isTopLevel && this.isRecordingAllocations()) {
        this._frameCache.initFrames();
      }
      this.dbg.addDebuggees();
    }
  },

  /**
   * Returns a boolean indicating whether or not allocation
   * sites are being tracked.
   */
  isRecordingAllocations: function () {
    return this.dbg.memory.trackingAllocationSites;
  },

  /**
   * Save a heap snapshot scoped to the current debuggees' portion of the heap
   * graph.
   *
   * @param {Object|null} boundaries
   *
   * @returns {String} The snapshot id.
   */
  saveHeapSnapshot: expectState("attached", function (boundaries = null) {
    // If we are observing the whole process, then scope the snapshot
    // accordingly. Otherwise, use the debugger's debuggees.
    if (!boundaries) {
      if (this.parent instanceof ChromeActor ||
          this.parent instanceof ChildProcessActor) {
        boundaries = { runtime: true };
      } else {
        boundaries = { debugger: this.dbg };
      }
    }
    return ChromeUtils.saveHeapSnapshotGetId(boundaries);
  }, "saveHeapSnapshot"),

  /**
   * Take a census of the heap. See js/src/doc/Debugger/Debugger.Memory.md for
   * more information.
   */
  takeCensus: expectState("attached", function () {
    return this.dbg.memory.takeCensus();
  }, "taking census"),

  /**
   * Start recording allocation sites.
   *
   * @param {number} options.probability
   *                 The probability we sample any given allocation when recording
   *                 allocations. Must be between 0 and 1 -- defaults to 1.
   * @param {number} options.maxLogLength
   *                 The maximum number of allocation events to keep in the
   *                 log. If new allocs occur while at capacity, oldest
   *                 allocations are lost. Must fit in a 32 bit signed integer.
   * @param {number} options.drainAllocationsTimeout
   *                 A number in milliseconds of how often, at least, an `allocation`
   *                 event gets emitted (and drained), and also emits and drains on every
   *                 GC event, resetting the timer.
   */
  startRecordingAllocations: expectState("attached", function (options = {}) {
    if (this.isRecordingAllocations()) {
      return this._getCurrentTime();
    }

    this._frameCache.initFrames();

    this.dbg.memory.allocationSamplingProbability = options.probability != null
      ? options.probability
      : 1.0;

    this.drainAllocationsTimeoutTimer = options.drainAllocationsTimeout;

    if (this.drainAllocationsTimeoutTimer != null) {
      if (this._poller) {
        this._poller.disarm();
      }
      this._poller = new DeferredTask(this._emitAllocations,
                                      this.drainAllocationsTimeoutTimer, 0);
      this._poller.arm();
    }

    if (options.maxLogLength != null) {
      this.dbg.memory.maxAllocationsLogLength = options.maxLogLength;
    }
    this.dbg.memory.trackingAllocationSites = true;

    return this._getCurrentTime();
  }, "starting recording allocations"),

  /**
   * Stop recording allocation sites.
   */
  stopRecordingAllocations: expectState("attached", function () {
    if (!this.isRecordingAllocations()) {
      return this._getCurrentTime();
    }
    this.dbg.memory.trackingAllocationSites = false;
    this._clearFrames();

    if (this._poller) {
      this._poller.disarm();
      this._poller = null;
    }

    return this._getCurrentTime();
  }, "stopping recording allocations"),

  /**
   * Return settings used in `startRecordingAllocations` for `probability`
   * and `maxLogLength`. Currently only uses in tests.
   */
  getAllocationsSettings: expectState("attached", function () {
    return {
      maxLogLength: this.dbg.memory.maxAllocationsLogLength,
      probability: this.dbg.memory.allocationSamplingProbability
    };
  }, "getting allocations settings"),

  /**
   * Get a list of the most recent allocations since the last time we got
   * allocations, as well as a summary of all allocations since we've been
   * recording.
   *
   * @returns Object
   *          An object of the form:
   *
   *            {
   *              allocations: [<index into "frames" below>, ...],
   *              allocationsTimestamps: [
   *                <timestamp for allocations[0]>,
   *                <timestamp for allocations[1]>,
   *                ...
   *              ],
   *              allocationSizes: [
   *                <bytesize for allocations[0]>,
   *                <bytesize for allocations[1]>,
   *                ...
   *              ],
   *              frames: [
   *                {
   *                  line: <line number for this frame>,
   *                  column: <column number for this frame>,
   *                  source: <filename string for this frame>,
   *                  functionDisplayName:
   *                    <this frame's inferred function name function or null>,
   *                  parent: <index into "frames">
   *                },
   *                ...
   *              ],
   *            }
   *
   *          The timestamps' unit is microseconds since the epoch.
   *
   *          Subsequent `getAllocations` request within the same recording and
   *          tab navigation will always place the same stack frames at the same
   *          indices as previous `getAllocations` requests in the same
   *          recording. In other words, it is safe to use the index as a
   *          unique, persistent id for its frame.
   *
   *          Additionally, the root node (null) is always at index 0.
   *
   *          We use the indices into the "frames" array to avoid repeating the
   *          description of duplicate stack frames both when listing
   *          allocations, and when many stacks share the same tail of older
   *          frames. There shouldn't be any duplicates in the "frames" array,
   *          as that would defeat the purpose of this compression trick.
   *
   *          In the future, we might want to split out a frame's "source" and
   *          "functionDisplayName" properties out the same way we have split
   *          frames out with the "frames" array. While this would further
   *          compress the size of the response packet, it would increase CPU
   *          usage to build the packet, and it should, of course, be guided by
   *          profiling and done only when necessary.
   */
  getAllocations: expectState("attached", function () {
    if (this.dbg.memory.allocationsLogOverflowed) {
      // Since the last time we drained the allocations log, there have been
      // more allocations than the log's capacity, and we lost some data. There
      // isn't anything actionable we can do about this, but put a message in
      // the browser console so we at least know that it occurred.
      reportException("MemoryBridge.prototype.getAllocations",
                      "Warning: allocations log overflowed and lost some data.");
    }

    const allocations = this.dbg.memory.drainAllocationsLog();
    const packet = {
      allocations: [],
      allocationsTimestamps: [],
      allocationSizes: [],
    };
    for (let { frame: stack, timestamp, size } of allocations) {
      if (stack && Cu.isDeadWrapper(stack)) {
        continue;
      }

      // Safe because SavedFrames are frozen/immutable.
      let waived = Cu.waiveXrays(stack);

      // Ensure that we have a form, size, and index for new allocations
      // because we potentially haven't seen some or all of them yet. After this
      // loop, we can rely on the fact that every frame we deal with already has
      // its metadata stored.
      let index = this._frameCache.addFrame(waived);

      packet.allocations.push(index);
      packet.allocationsTimestamps.push(timestamp);
      packet.allocationSizes.push(size);
    }

    return this._frameCache.updateFramePacket(packet);
  }, "getting allocations"),

  /*
   * Force a browser-wide GC.
   */
  forceGarbageCollection: function () {
    for (let i = 0; i < 3; i++) {
      Cu.forceGC();
    }
  },

  /**
   * Force an XPCOM cycle collection. For more information on XPCOM cycle
   * collection, see
   * https://developer.mozilla.org/en-US/docs/Interfacing_with_the_XPCOM_cycle_collector#What_the_cycle_collector_does
   */
  forceCycleCollection: function () {
    Cu.forceCC();
  },

  /**
   * A method that returns a detailed breakdown of the memory consumption of the
   * associated window.
   *
   * @returns object
   */
  measure: function () {
    let result = {};

    let jsObjectsSize = {};
    let jsStringsSize = {};
    let jsOtherSize = {};
    let domSize = {};
    let styleSize = {};
    let otherSize = {};
    let totalSize = {};
    let jsMilliseconds = {};
    let nonJSMilliseconds = {};

    try {
      this._mgr.sizeOfTab(this.parent.window, jsObjectsSize, jsStringsSize, jsOtherSize,
                          domSize, styleSize, otherSize, totalSize, jsMilliseconds,
                          nonJSMilliseconds);
      result.total = totalSize.value;
      result.domSize = domSize.value;
      result.styleSize = styleSize.value;
      result.jsObjectsSize = jsObjectsSize.value;
      result.jsStringsSize = jsStringsSize.value;
      result.jsOtherSize = jsOtherSize.value;
      result.otherSize = otherSize.value;
      result.jsMilliseconds = jsMilliseconds.value.toFixed(1);
      result.nonJSMilliseconds = nonJSMilliseconds.value.toFixed(1);
    } catch (e) {
      reportException("MemoryBridge.prototype.measure", e);
    }

    return result;
  },

  residentUnique: function () {
    return this._mgr.residentUnique;
  },

  /**
   * Handler for GC events on the Debugger.Memory instance.
   */
  _onGarbageCollection: function (data) {
    this.emit("garbage-collection", data);

    // If `drainAllocationsTimeout` set, fire an allocations event with the drained log,
    // which will restart the timer.
    if (this._poller) {
      this._poller.disarm();
      this._emitAllocations();
    }
  },

  /**
   * Called on `drainAllocationsTimeoutTimer` interval if and only if set
   * during `startRecordingAllocations`, or on a garbage collection event if
   * drainAllocationsTimeout was set.
   * Drains allocation log and emits as an event and restarts the timer.
   */
  _emitAllocations: function () {
    this.emit("allocations", this.getAllocations());
    this._poller.arm();
  },

  /**
   * Accesses the docshell to return the current process time.
   */
  _getCurrentTime: function () {
    return (this.parent.isRootActor ? this.parent.docShell :
                                      this.parent.originalDocShell).now();
  },
};

exports.Memory = Memory;
PK
!<>��rDrD?chrome/devtools/modules/devtools/server/performance/profiler.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Cu } = require("chrome");
const Services = require("Services");

loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
loader.lazyRequireGetter(this, "DevToolsUtils", "devtools/shared/DevToolsUtils");
loader.lazyRequireGetter(this, "DeferredTask", "resource://gre/modules/DeferredTask.jsm", true);

// Events piped from system observers to Profiler instances.
const PROFILER_SYSTEM_EVENTS = [
  "console-api-profiler",
  "profiler-started",
  "profiler-stopped"
];

// How often the "profiler-status" is emitted by default (in ms)
const BUFFER_STATUS_INTERVAL_DEFAULT = 5000;

var DEFAULT_PROFILER_OPTIONS = {
  // When using the DevTools Performance Tools, this will be overridden
  // by the pref `devtools.performance.profiler.buffer-size`.
  entries: Math.pow(10, 7),
  // When using the DevTools Performance Tools, this will be overridden
  // by the pref `devtools.performance.profiler.sample-rate-khz`.
  interval: 1,
  features: ["js"],
  threadFilters: ["GeckoMain"]
};

/**
 * Main interface for interacting with nsIProfiler
 */
const ProfilerManager = (function () {
  let consumers = new Set();

  return {

    // How often the "profiler-status" is emitted
    _profilerStatusInterval: BUFFER_STATUS_INTERVAL_DEFAULT,

    // How many subscribers there
    _profilerStatusSubscribers: 0,

    // Has the profiler ever been started by the actor?
    started: false,

    /**
     * The nsIProfiler is target agnostic and interacts with the whole platform.
     * Therefore, special care needs to be given to make sure different profiler
     * consumers (i.e. "toolboxes") don't interfere with each other. Register
     * the profiler actor instances here.
     *
     * @param Profiler instance
     *        A profiler actor class.
     */
    addInstance: function (instance) {
      consumers.add(instance);

      // Lazily register events
      this.registerEventListeners();
    },

    /**
     * Remove the profiler actor instances here.
     *
     * @param Profiler instance
     *        A profiler actor class.
     */
    removeInstance: function (instance) {
      consumers.delete(instance);

      if (this.length < 0) {
        let msg = "Somehow the number of started profilers is now negative.";
        DevToolsUtils.reportException("Profiler", msg);
      }

      if (this.length === 0) {
        this.unregisterEventListeners();
        this.stop();
      }
    },

    /**
     * Starts the nsIProfiler module. Doing so will discard any samples
     * that might have been accumulated so far.
     *
     * @param {number} entries [optional]
     * @param {number} interval [optional]
     * @param {Array<string>} features [optional]
     * @param {Array<string>} threadFilters [description]
     *
     * @return {object}
     */
    start: function (options = {}) {
      let config = this._profilerStartOptions = {
        entries: options.entries || DEFAULT_PROFILER_OPTIONS.entries,
        interval: options.interval || DEFAULT_PROFILER_OPTIONS.interval,
        features: options.features || DEFAULT_PROFILER_OPTIONS.features,
        threadFilters: options.threadFilters || DEFAULT_PROFILER_OPTIONS.threadFilters,
      };

      // The start time should be before any samples we might be
      // interested in.
      let currentTime = Services.profiler.getElapsedTime();

      try {
        Services.profiler.StartProfiler(
          config.entries,
          config.interval,
          config.features,
          config.features.length,
          config.threadFilters,
          config.threadFilters.length
        );
      } catch (e) {
        // For some reason, the profiler couldn't be started. This could happen,
        // for example, when in private browsing mode.
        Cu.reportError(`Could not start the profiler module: ${e.message}`);
        return { started: false, reason: e, currentTime };
      }
      this.started = true;

      this._updateProfilerStatusPolling();

      let { position, totalSize, generation } = this.getBufferInfo();
      return { started: true, position, totalSize, generation, currentTime };
    },

    /**
     * Attempts to stop the nsIProfiler module.
     */
    stop: function () {
      // Actually stop the profiler only if the last client has stopped profiling.
      // Since this is used as a root actor, and the profiler module interacts
      // with the whole platform, we need to avoid a case in which the profiler
      // is stopped when there might be other clients still profiling.
      // Also check for `started` to only stop the profiler when the actor
      // actually started it. This is to prevent stopping the profiler initiated
      // by some other code, like Talos.
      if (this.length <= 1 && this.started) {
        Services.profiler.StopProfiler();
        this.started = false;
      }
      this._updateProfilerStatusPolling();
      return { started: false };
    },

    /**
     * Returns all the samples accumulated since the profiler was started,
     * along with the current time. The data has the following format:
     * {
     *   libs: string,
     *   meta: {
     *     interval: number,
     *     platform: string,
     *     ...
     *   },
     *   threads: [{
     *     samples: [{
     *       frames: [{
     *         line: number,
     *         location: string,
     *         category: number
     *       } ... ],
     *       name: string
     *       responsiveness: number
     *       time: number
     *     } ... ]
     *   } ... ]
     * }
     *
     *
     * @param number startTime
     *        Since the circular buffer will only grow as long as the profiler lives,
     *        the buffer can contain unwanted samples. Pass in a `startTime` to only
     *        retrieve samples that took place after the `startTime`, with 0 being
     *        when the profiler just started.
     * @param boolean stringify
     *        Whether or not the returned profile object should be a string or not to
     *        save JSON parse/stringify cycle if emitting over RDP.
     */
    getProfile: function (options) {
      let startTime = options.startTime || 0;
      let profile = options.stringify ?
        Services.profiler.GetProfile(startTime) :
        Services.profiler.getProfileData(startTime);

      return { profile: profile, currentTime: Services.profiler.getElapsedTime() };
    },

    /**
     * Returns an array of feature strings, describing the profiler features
     * that are available on this platform. Can be called while the profiler
     * is stopped.
     *
     * @return {object}
     */
    getFeatures: function () {
      return { features: Services.profiler.GetFeatures([]) };
    },

    /**
     * Returns an object with the values of the current status of the
     * circular buffer in the profiler, returning `position`, `totalSize`,
     * and the current `generation` of the buffer.
     *
     * @return {object}
     */
    getBufferInfo: function () {
      let position = {}, totalSize = {}, generation = {};
      Services.profiler.GetBufferInfo(position, totalSize, generation);
      return {
        position: position.value,
        totalSize: totalSize.value,
        generation: generation.value
      };
    },

    /**
     * Returns the configuration used that was originally passed in to start up the
     * profiler. Used for tests, and does not account for others using nsIProfiler.
     *
     * @param {object}
     */
    getStartOptions: function () {
      return this._profilerStartOptions || {};
    },

    /**
     * Verifies whether or not the nsIProfiler module has started.
     * If already active, the current time is also returned.
     *
     * @return {object}
     */
    isActive: function () {
      let isActive = Services.profiler.IsActive();
      let elapsedTime = isActive ? Services.profiler.getElapsedTime() : undefined;
      let { position, totalSize, generation } = this.getBufferInfo();
      return {
        isActive,
        currentTime: elapsedTime,
        position,
        totalSize,
        generation
      };
    },

    /**
     * Returns an array of objects that describes the shared libraries
     * which are currently loaded into our process. Can be called while the
     * profiler is stopped.
     */
    get sharedLibraries() {
      return {
        sharedLibraries: Services.profiler.sharedLibraries
      };
    },

    /**
     * Number of profiler instances.
     *
     * @return {number}
     */
    get length() {
      return consumers.size;
    },

    /**
     * Callback for all observed notifications.
     * @param object subject
     * @param string topic
     * @param object data
     */
    observe: sanitizeHandler(function (subject, topic, data) {
      let details;

      // An optional label may be specified when calling `console.profile`.
      // If that's the case, stringify it and send it over with the response.
      let { action, arguments: args } = subject || {};
      let profileLabel = args && args.length > 0 ? `${args[0]}` : void 0;

      // If the event was generated from `console.profile` or `console.profileEnd`
      // we need to start the profiler right away and then just notify the client.
      // Otherwise, we'll lose precious samples.
      if (topic === "console-api-profiler" &&
          (action === "profile" || action === "profileEnd")) {
        let { isActive, currentTime } = this.isActive();

        // Start the profiler only if it wasn't already active. Otherwise, any
        // samples that might have been accumulated so far will be discarded.
        if (!isActive && action === "profile") {
          this.start();
          details = { profileLabel, currentTime: 0 };
        } else if (!isActive) {
          // Otherwise, if inactive and a call to profile end, do nothing
          // and don't emit event.
          return;
        }

        // Otherwise, the profiler is already active, so just send
        // to the front the current time, label, and the notification
        // adds the action as well.
        details = { profileLabel, currentTime };
      }

      // Propagate the event to the profiler instances that
      // are subscribed to this event.
      this.emitEvent(topic, { subject, topic, data, details });
    }, "ProfilerManager.observe"),

    /**
     * Registers handlers for the following events to be emitted
     * on active Profiler instances:
     *   - "console-api-profiler"
     *   - "profiler-started"
     *   - "profiler-stopped"
     *   - "profiler-status"
     *
     * The ProfilerManager listens to all events, and individual
     * consumers filter which events they are interested in.
     */
    registerEventListeners: function () {
      if (!this._eventsRegistered) {
        PROFILER_SYSTEM_EVENTS.forEach(eventName =>
          Services.obs.addObserver(this, eventName));
        this._eventsRegistered = true;
      }
    },

    /**
     * Unregisters handlers for all system events.
     */
    unregisterEventListeners: function () {
      if (this._eventsRegistered) {
        PROFILER_SYSTEM_EVENTS.forEach(eventName =>
          Services.obs.removeObserver(this, eventName));
        this._eventsRegistered = false;
      }
    },

    /**
     * Takes an event name and additional data and emits them
     * through each profiler instance that is subscribed to the event.
     *
     * @param {string} eventName
     * @param {object} data
     */
    emitEvent: function (eventName, data) {
      let subscribers = Array.from(consumers).filter(c => {
        return c.subscribedEvents.has(eventName);
      });

      for (let subscriber of subscribers) {
        subscriber.emit(eventName, data);
      }
    },

    /**
     * Updates the frequency that the "profiler-status" event is emitted
     * during recording.
     *
     * @param {number} interval
     */
    setProfilerStatusInterval: function (interval) {
      this._profilerStatusInterval = interval;
      if (this._poller) {
        this._poller._delayMs = interval;
      }
    },

    subscribeToProfilerStatusEvents: function () {
      this._profilerStatusSubscribers++;
      this._updateProfilerStatusPolling();
    },

    unsubscribeToProfilerStatusEvents: function () {
      this._profilerStatusSubscribers--;
      this._updateProfilerStatusPolling();
    },

    /**
     * Will enable or disable "profiler-status" events depending on
     * if there are subscribers and if the profiler is current recording.
     */
    _updateProfilerStatusPolling: function () {
      if (this._profilerStatusSubscribers > 0 && Services.profiler.IsActive()) {
        if (!this._poller) {
          this._poller = new DeferredTask(this._emitProfilerStatus.bind(this),
                                          this._profilerStatusInterval, 0);
        }
        this._poller.arm();
      } else if (this._poller) {
        // No subscribers; turn off if it exists.
        this._poller.disarm();
      }
    },

    _emitProfilerStatus: function () {
      this.emitEvent("profiler-status", this.isActive());
      this._poller.arm();
    }
  };
})();

/**
 * The profiler actor provides remote access to the built-in nsIProfiler module.
 */
class Profiler {
  constructor() {
    EventEmitter.decorate(this);

    this.subscribedEvents = new Set();
    ProfilerManager.addInstance(this);
  }

  destroy() {
    this.unregisterEventNotifications({ events: Array.from(this.subscribedEvents) });
    this.subscribedEvents = null;

    ProfilerManager.removeInstance(this);
  }

  /**
   * @see ProfilerManager.start
   */
  start(options) {
    return ProfilerManager.start(options);
  }

  /**
   * @see ProfilerManager.stop
   */
  stop() {
    return ProfilerManager.stop();
  }

  /**
   * @see ProfilerManager.getProfile
   */
  getProfile(request = {}) {
    return ProfilerManager.getProfile(request);
  }

  /**
   * @see ProfilerManager.getFeatures
   */
  getFeatures() {
    return ProfilerManager.getFeatures();
  }

  /**
   * @see ProfilerManager.getBufferInfo
   */
  getBufferInfo() {
    return ProfilerManager.getBufferInfo();
  }

  /**
   * @see ProfilerManager.getStartOptions
   */
  getStartOptions() {
    return ProfilerManager.getStartOptions();
  }

  /**
   * @see ProfilerManager.isActive
   */
  isActive() {
    return ProfilerManager.isActive();
  }

  /**
   * @see ProfilerManager.sharedLibraries
   */
  sharedLibraries() {
    return ProfilerManager.sharedLibraries;
  }

  /**
   * @see ProfilerManager.setProfilerStatusInterval
   */
  setProfilerStatusInterval(interval) {
    return ProfilerManager.setProfilerStatusInterval(interval);
  }

  /**
   * Subscribes this instance to one of several events defined in
   * an events array.
   * - "console-api-profiler",
   * - "profiler-started",
   * - "profiler-stopped"
   * - "profiler-status"
   *
   * @param {Array<string>} data.event
   * @return {object}
   */
  registerEventNotifications(data = {}) {
    let response = [];
    (data.events || []).forEach(e => {
      if (!this.subscribedEvents.has(e)) {
        if (e === "profiler-status") {
          ProfilerManager.subscribeToProfilerStatusEvents();
        }
        this.subscribedEvents.add(e);
        response.push(e);
      }
    });
    return { registered: response };
  }

  /**
   * Unsubscribes this instance to one of several events defined in
   * an events array.
   *
   * @param {Array<string>} data.event
   * @return {object}
   */
  unregisterEventNotifications(data = {}) {
    let response = [];
    (data.events || []).forEach(e => {
      if (this.subscribedEvents.has(e)) {
        if (e === "profiler-status") {
          ProfilerManager.unsubscribeToProfilerStatusEvents();
        }
        this.subscribedEvents.delete(e);
        response.push(e);
      }
    });
    return { registered: response };
  }

  /**
   * Checks whether or not the profiler module can currently run.
   * @return boolean
   */
  static canProfile() {
    return Services.profiler.CanProfile();
  }
}

/**
 * JSON.stringify callback used in Profiler.prototype.observe.
 */
function cycleBreaker(key, value) {
  if (key == "wrappedJSObject") {
    return undefined;
  }
  return value;
}

/**
 * Create JSON objects suitable for transportation across the RDP,
 * by breaking cycles and making a copy of the `subject` and `data` via
 * JSON.stringifying those values with a replacer that omits properties
 * known to introduce cycles, and then JSON.parsing the result.
 * This spends some CPU cycles, but it's simple.
 *
 * @TODO Also wraps it in a `makeInfallible` -- is this still necessary?
 *
 * @param {function} handler
 * @return {function}
 */
function sanitizeHandler(handler, identifier) {
  return DevToolsUtils.makeInfallible(function (subject, topic, data) {
    subject = (subject && !Cu.isXrayWrapper(subject) && subject.wrappedJSObject)
              || subject;
    subject = JSON.parse(JSON.stringify(subject, cycleBreaker));
    data = (data && !Cu.isXrayWrapper(data) && data.wrappedJSObject) || data;
    data = JSON.parse(JSON.stringify(data, cycleBreaker));

    // Pass in clean data to the underlying handler
    return handler.call(this, subject, topic, data);
  }, identifier);
}

exports.Profiler = Profiler;
PK
!<���]?]??chrome/devtools/modules/devtools/server/performance/recorder.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Cu } = require("chrome");

loader.lazyRequireGetter(this, "Services");
loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");

loader.lazyRequireGetter(this, "Memory",
  "devtools/server/performance/memory", true);
loader.lazyRequireGetter(this, "Timeline",
  "devtools/server/performance/timeline", true);
loader.lazyRequireGetter(this, "Profiler",
  "devtools/server/performance/profiler", true);
loader.lazyRequireGetter(this, "PerformanceRecordingActor",
  "devtools/server/actors/performance-recording", true);
loader.lazyRequireGetter(this, "PerformanceRecordingFront",
  "devtools/server/actors/performance-recording", true);
loader.lazyRequireGetter(this, "mapRecordingOptions",
  "devtools/shared/performance/recording-utils", true);
loader.lazyRequireGetter(this, "DevToolsUtils",
  "devtools/shared/DevToolsUtils");
loader.lazyRequireGetter(this, "getSystemInfo",
  "devtools/shared/system", true);

const PROFILER_EVENTS = [
  "console-api-profiler",
  "profiler-started",
  "profiler-stopped",
  "profiler-status"
];

// Max time in milliseconds for the allocations event to occur, which will
// occur on every GC, or at least as often as DRAIN_ALLOCATIONS_TIMEOUT.
const DRAIN_ALLOCATIONS_TIMEOUT = 2000;

/**
 * A connection to underlying actors (profiler, memory, framerate, etc.)
 * shared by all tools in a target.
 *
 * @param Target target
 *        The target owning this connection.
 */
function PerformanceRecorder(conn, tabActor) {
  EventEmitter.decorate(this);

  this.conn = conn;
  this.tabActor = tabActor;

  this._pendingConsoleRecordings = [];
  this._recordings = [];

  this._onTimelineData = this._onTimelineData.bind(this);
  this._onProfilerEvent = this._onProfilerEvent.bind(this);
}

PerformanceRecorder.prototype = {
  /**
   * Initializes a connection to the profiler and other miscellaneous actors.
   * If in the process of opening, or already open, nothing happens.
   *
   * @param {Object} options.systemClient
   *        Metadata about the client's system to attach to the recording models.
   *
   * @return object
   *         A promise that is resolved once the connection is established.
   */
  connect: function (options) {
    if (this._connected) {
      return;
    }

    // Sets `this._profiler`, `this._timeline` and `this._memory`.
    // Only initialize the timeline and memory fronts if the respective actors
    // are available. Older Gecko versions don't have existing implementations,
    // in which case all the methods we need can be easily mocked.
    this._connectComponents();
    this._registerListeners();

    this._systemClient = options.systemClient;

    this._connected = true;
  },

  /**
   * Destroys this connection.
   */
  destroy: function () {
    this._unregisterListeners();
    this._disconnectComponents();

    this._connected = null;
    this._profiler = null;
    this._timeline = null;
    this._memory = null;
    this._target = null;
    this._client = null;
  },

  /**
   * Initializes fronts and connects to the underlying actors using the facades
   * found in ./actors.js.
   */
  _connectComponents: function () {
    this._profiler = new Profiler(this.tabActor);
    this._memory = new Memory(this.tabActor);
    this._timeline = new Timeline(this.tabActor);
    this._profiler.registerEventNotifications({ events: PROFILER_EVENTS });
  },

  /**
   * Registers listeners on events from the underlying
   * actors, so the connection can handle them.
   */
  _registerListeners: function () {
    this._timeline.on("*", this._onTimelineData);
    this._memory.on("*", this._onTimelineData);
    this._profiler.on("*", this._onProfilerEvent);
  },

  /**
   * Unregisters listeners on events on the underlying actors.
   */
  _unregisterListeners: function () {
    this._timeline.off("*", this._onTimelineData);
    this._memory.off("*", this._onTimelineData);
    this._profiler.off("*", this._onProfilerEvent);
  },

  /**
   * Closes the connections to non-profiler actors.
   */
  _disconnectComponents: function () {
    this._profiler.unregisterEventNotifications({ events: PROFILER_EVENTS });
    this._profiler.destroy();
    this._timeline.destroy();
    this._memory.destroy();
  },

  _onProfilerEvent: function (topic, data) {
    if (topic === "console-api-profiler") {
      if (data.subject.action === "profile") {
        this._onConsoleProfileStart(data.details);
      } else if (data.subject.action === "profileEnd") {
        this._onConsoleProfileEnd(data.details);
      }
    } else if (topic === "profiler-stopped") {
      this._onProfilerUnexpectedlyStopped();
    } else if (topic === "profiler-status") {
      this.emit("profiler-status", data);
    }
  },

  /**
   * Invoked whenever `console.profile` is called.
   *
   * @param string profileLabel
   *        The provided string argument if available; undefined otherwise.
   * @param number currentTime
   *        The time (in milliseconds) when the call was made, relative to when
   *        the nsIProfiler module was started.
   */
  async _onConsoleProfileStart({ profileLabel, currentTime }) {
    let recordings = this._recordings;

    // Abort if a profile with this label already exists.
    if (recordings.find(e => e.getLabel() === profileLabel)) {
      return;
    }

    // Immediately emit this so the client can start setting things up,
    // expecting a recording very soon.
    this.emit("console-profile-start");

    await this.startRecording(Object.assign({}, getPerformanceRecordingPrefs(), {
      console: true,
      label: profileLabel
    }));
  },

  /**
   * Invoked whenever `console.profileEnd` is called.
   *
   * @param string profileLabel
   *        The provided string argument if available; undefined otherwise.
   * @param number currentTime
   *        The time (in milliseconds) when the call was made, relative to when
   *        the nsIProfiler module was started.
   */
  async _onConsoleProfileEnd(data) {
    // If no data, abort; can occur if profiler isn't running and we get a surprise
    // call to console.profileEnd()
    if (!data) {
      return;
    }
    let { profileLabel } = data;

    let pending = this._recordings.filter(r => r.isConsole() && r.isRecording());
    if (pending.length === 0) {
      return;
    }

    let model;
    // Try to find the corresponding `console.profile` call if
    // a label was used in profileEnd(). If no matches, abort.
    if (profileLabel) {
      model = pending.find(e => e.getLabel() === profileLabel);
    } else {
      // If no label supplied, pop off the most recent pending console recording
      model = pending[pending.length - 1];
    }

    // If `profileEnd()` was called with a label, and there are no matching
    // sessions, abort.
    if (!model) {
      Cu.reportError(
        "console.profileEnd() called with label that does not match a recording.");
      return;
    }

    await this.stopRecording(model);
  },

 /**
  * TODO handle bug 1144438
  */
  _onProfilerUnexpectedlyStopped: function () {
    Cu.reportError("Profiler unexpectedly stopped.", arguments);
  },

  /**
   * Called whenever there is timeline data of any of the following types:
   * - markers
   * - frames
   * - memory
   * - ticks
   * - allocations
   */
  _onTimelineData: function (eventName, ...data) {
    let eventData = Object.create(null);

    switch (eventName) {
      case "markers": {
        eventData = { markers: data[0], endTime: data[1] };
        break;
      }
      case "ticks": {
        eventData = { delta: data[0], timestamps: data[1] };
        break;
      }
      case "memory": {
        eventData = { delta: data[0], measurement: data[1] };
        break;
      }
      case "frames": {
        eventData = { delta: data[0], frames: data[1] };
        break;
      }
      case "allocations": {
        eventData = data[0];
        break;
      }
    }

    // Filter by only recordings that are currently recording;
    // TODO should filter by recordings that have realtimeMarkers enabled.
    let activeRecordings = this._recordings.filter(r => r.isRecording());

    if (activeRecordings.length) {
      this.emit("timeline-data", eventName, eventData, activeRecordings);
    }
  },

  /**
   * Checks whether or not recording is currently supported. At the moment,
   * this is only influenced by private browsing mode and the profiler.
   */
  canCurrentlyRecord: function () {
    let success = true;
    let reasons = [];

    if (!Profiler.canProfile()) {
      success = false;
      reasons.push("profiler-unavailable");
    }

    // Check other factors that will affect the possibility of successfully
    // starting a recording here.

    return { success, reasons };
  },

  /**
   * Begins a recording session
   *
   * @param boolean options.withMarkers
   * @param boolean options.withTicks
   * @param boolean options.withMemory
   * @param boolean options.withAllocations
   * @param boolean options.allocationsSampleProbability
   * @param boolean options.allocationsMaxLogLength
   * @param boolean options.bufferSize
   * @param boolean options.sampleFrequency
   * @param boolean options.console
   * @param string options.label
   * @param boolean options.realtimeMarkers
   * @return object
   *         A promise that is resolved once recording has started.
   */
  async startRecording(options) {
    let profilerStart, timelineStart, memoryStart;

    profilerStart = (async function () {
      let data = await this._profiler.isActive();
      if (data.isActive) {
        return data;
      }
      let startData = await this._profiler.start(
        mapRecordingOptions("profiler", options)
      );

      // If no current time is exposed from starting, set it to 0 -- this is an
      // older Gecko that does not return its starting time, and uses an epoch based
      // on the profiler's start time.
      if (startData.currentTime == null) {
        startData.currentTime = 0;
      }
      return startData;
    }.bind(this))();

    // Timeline will almost always be on if using the DevTools, but using component
    // independently could result in no timeline.
    if (options.withMarkers || options.withTicks || options.withMemory) {
      timelineStart = this._timeline.start(mapRecordingOptions("timeline", options));
    }

    if (options.withAllocations) {
      if (this._memory.getState() === "detached") {
        this._memory.attach();
      }
      let recordingOptions = Object.assign(mapRecordingOptions("memory", options), {
        drainAllocationsTimeout: DRAIN_ALLOCATIONS_TIMEOUT
      });
      memoryStart = this._memory.startRecordingAllocations(recordingOptions);
    }

    let [profilerStartData, timelineStartData, memoryStartData] = await Promise.all([
      profilerStart, timelineStart, memoryStart
    ]);

    let data = Object.create(null);
    // Filter out start times that are not actually used (0 or undefined), and
    // find the earliest time since all sources use same epoch.
    let startTimes = [
      profilerStartData.currentTime,
      memoryStartData,
      timelineStartData
    ].filter(Boolean);
    data.startTime = Math.min(...startTimes);
    data.position = profilerStartData.position;
    data.generation = profilerStartData.generation;
    data.totalSize = profilerStartData.totalSize;

    data.systemClient = this._systemClient;
    data.systemHost = await getSystemInfo();

    let model = new PerformanceRecordingActor(this.conn, options, data);
    this._recordings.push(model);

    this.emit("recording-started", model);
    return model;
  },

  /**
   * Manually ends the recording session for the corresponding PerformanceRecording.
   *
   * @param PerformanceRecording model
   *        The corresponding PerformanceRecording that belongs to the recording
   *        session wished to stop.
   * @return PerformanceRecording
   *         Returns the same model, populated with the profiling data.
   */
  async stopRecording(model) {
    // If model isn't in the Recorder's internal store,
    // then do nothing, like if this was a console.profileEnd
    // from a different target.
    if (!this._recordings.includes(model)) {
      return model;
    }

    // Flag the recording as no longer recording, so that `model.isRecording()`
    // is false. Do this before we fetch all the data, and then subsequently
    // the recording can be considered "completed".
    this.emit("recording-stopping", model);

    // Currently there are two ways profiles stop recording. Either manually in the
    // performance tool, or via console.profileEnd. Once a recording is done,
    // we want to deliver the model to the performance tool (either as a return
    // from the PerformanceFront or via `console-profile-stop` event) and then
    // remove it from the internal store.
    //
    // In the case where a console.profile is generated via the console (so the tools are
    // open), we initialize the Performance tool so it can listen to those events.
    this._recordings.splice(this._recordings.indexOf(model), 1);

    let startTime = model._startTime;
    let profilerData = this._profiler.getProfile({ startTime });

    // Only if there are no more sessions recording do we stop
    // the underlying memory and timeline actors. If we're still recording,
    // juse use Date.now() for the memory and timeline end times, as those
    // are only used in tests.
    if (!this.isRecording()) {
      // Check to see if memory is recording, so we only stop recording
      // if necessary (otherwise if the memory component is not attached, this will fail)
      if (this._memory.isRecordingAllocations()) {
        this._memory.stopRecordingAllocations();
      }
      this._timeline.stop();
    }

    let recordingData = {
      // Data available only at the end of a recording.
      profile: profilerData.profile,
      // End times for all the actors.
      duration: profilerData.currentTime - startTime,
    };

    this.emit("recording-stopped", model, recordingData);
    return model;
  },

  /**
   * Checks all currently stored recording handles and returns a boolean
   * if there is a session currently being recorded.
   *
   * @return Boolean
   */
  isRecording: function () {
    return this._recordings.some(h => h.isRecording());
  },

  /**
   * Returns all current recordings.
   */
  getRecordings: function () {
    return this._recordings;
  },

  /**
   * Sets how often the "profiler-status" event should be emitted.
   * Used in tests.
   */
  setProfilerStatusInterval: function (n) {
    this._profiler.setProfilerStatusInterval(n);
  },

  /**
   * Returns the configurations set on underlying components, used in tests.
   * Returns an object with `probability`, `maxLogLength` for allocations, and
   * `features`, `threadFilters`, `entries` and `interval` for profiler.
   *
   * @return {object}
   */
  getConfiguration: function () {
    let allocationSettings = Object.create(null);

    if (this._memory.getState() === "attached") {
      allocationSettings = this._memory.getAllocationsSettings();
    }

    return Object.assign({}, allocationSettings, this._profiler.getStartOptions());
  },

  toString: () => "[object PerformanceRecorder]"
};

/**
 * Creates an object of configurations based off of
 * preferences for a PerformanceRecording.
 */
function getPerformanceRecordingPrefs() {
  return {
    withMarkers: true,
    withMemory: Services.prefs.getBoolPref("devtools.performance.ui.enable-memory"),
    withTicks: Services.prefs.getBoolPref("devtools.performance.ui.enable-framerate"),
    withAllocations:
      Services.prefs.getBoolPref("devtools.performance.ui.enable-allocations"),
    allocationsSampleProbability:
      +Services.prefs.getCharPref("devtools.performance.memory.sample-probability"),
    allocationsMaxLogLength:
      Services.prefs.getIntPref("devtools.performance.memory.max-log-length")
  };
}

exports.PerformanceRecorder = PerformanceRecorder;
PK
!<:1f�e+e+?chrome/devtools/modules/devtools/server/performance/timeline.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";

/**
 * Many Gecko operations (painting, reflows, restyle, ...) can be tracked
 * in real time. A marker is a representation of one operation. A marker
 * has a name, start and end timestamps. Markers are stored in docShells.
 *
 * This module exposes this tracking mechanism. To use with devtools' RDP,
 * use devtools/server/actors/timeline.js directly.
 *
 * To start/stop recording markers:
 *   timeline.start()
 *   timeline.stop()
 *   timeline.isRecording()
 *
 * When markers are available, an event is emitted:
 *   timeline.on("markers", function(markers) {...})
 */

const { Ci, Cu } = require("chrome");

// Be aggressive about lazy loading, as this will run on every
// toolbox startup
loader.lazyRequireGetter(this, "Memory", "devtools/server/performance/memory", true);
loader.lazyRequireGetter(this, "Framerate", "devtools/server/performance/framerate", true);
loader.lazyRequireGetter(this, "StackFrameCache", "devtools/server/actors/utils/stack", true);
loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");

// How often do we pull markers from the docShells, and therefore, how often do
// we send events to the front (knowing that when there are no markers in the
// docShell, no event is sent). In milliseconds.
const DEFAULT_TIMELINE_DATA_PULL_TIMEOUT = 200;

/**
 * The timeline actor pops and forwards timeline markers registered in docshells.
 */
function Timeline(tabActor) {
  EventEmitter.decorate(this);

  this.tabActor = tabActor;

  this._isRecording = false;
  this._stackFrames = null;
  this._memory = null;
  this._framerate = null;

  // Make sure to get markers from new windows as they become available
  this._onWindowReady = this._onWindowReady.bind(this);
  this._onGarbageCollection = this._onGarbageCollection.bind(this);
  this.tabActor.on("window-ready", this._onWindowReady);
}

Timeline.prototype = {
  /**
   * Destroys this actor, stopping recording first.
   */
  destroy: function () {
    this.stop();

    this.tabActor.off("window-ready", this._onWindowReady);
    this.tabActor = null;
  },

  /**
   * Get the list of docShells in the currently attached tabActor. Note that we
   * always list the docShells included in the real root docShell, even if the
   * tabActor was switched to a child frame. This is because for now, paint
   * markers are only recorded at parent frame level so switching the timeline
   * to a child frame would hide all paint markers.
   * See https://bugzilla.mozilla.org/show_bug.cgi?id=1050773#c14
   * @return {Array}
   */
  get docShells() {
    let originalDocShell;
    let docShells = [];

    if (this.tabActor.isRootActor) {
      originalDocShell = this.tabActor.docShell;
    } else {
      originalDocShell = this.tabActor.originalDocShell;
    }

    if (!originalDocShell) {
      return docShells;
    }

    let docShellsEnum = originalDocShell.getDocShellEnumerator(
      Ci.nsIDocShellTreeItem.typeAll,
      Ci.nsIDocShell.ENUMERATE_FORWARDS
    );

    while (docShellsEnum.hasMoreElements()) {
      let docShell = docShellsEnum.getNext();
      docShells.push(docShell.QueryInterface(Ci.nsIDocShell));
    }

    return docShells;
  },

  /**
   * At regular intervals, pop the markers from the docshell, and forward
   * markers, memory, tick and frames events, if any.
   */
  _pullTimelineData: function () {
    let docShells = this.docShells;
    if (!this._isRecording || !docShells.length) {
      return;
    }

    let endTime = docShells[0].now();
    let markers = [];

    // Gather markers if requested.
    if (this._withMarkers || this._withDocLoadingEvents) {
      for (let docShell of docShells) {
        for (let marker of docShell.popProfileTimelineMarkers()) {
          markers.push(marker);

          // The docshell may return markers with stack traces attached.
          // Here we transform the stack traces via the stack frame cache,
          // which lets us preserve tail sharing when transferring the
          // frames to the client.  We must waive xrays here because Firefox
          // doesn't understand that the Debugger.Frame object is safe to
          // use from chrome.  See Tutorial-Alloc-Log-Tree.md.
          if (this._withFrames) {
            if (marker.stack) {
              marker.stack = this._stackFrames.addFrame(Cu.waiveXrays(marker.stack));
            }
            if (marker.endStack) {
              marker.endStack = this._stackFrames.addFrame(
                Cu.waiveXrays(marker.endStack)
              );
            }
          }

          // Emit some helper events for "DOMContentLoaded" and "Load" markers.
          if (this._withDocLoadingEvents) {
            if (marker.name == "document::DOMContentLoaded" ||
                marker.name == "document::Load") {
              this.emit("doc-loading", marker, endTime);
            }
          }
        }
      }
    }

    // Emit markers if requested.
    if (this._withMarkers && markers.length > 0) {
      this.emit("markers", markers, endTime);
    }

    // Emit framerate data if requested.
    if (this._withTicks) {
      this.emit("ticks", endTime, this._framerate.getPendingTicks());
    }

    // Emit memory data if requested.
    if (this._withMemory) {
      this.emit("memory", endTime, this._memory.measure());
    }

    // Emit stack frames data if requested.
    if (this._withFrames && this._withMarkers) {
      let frames = this._stackFrames.makeEvent();
      if (frames) {
        this.emit("frames", endTime, frames);
      }
    }

    this._dataPullTimeout = setTimeout(() => {
      this._pullTimelineData();
    }, DEFAULT_TIMELINE_DATA_PULL_TIMEOUT);
  },

  /**
   * Are we recording profile markers currently?
   */
  isRecording: function () {
    return this._isRecording;
  },

  /**
   * Start recording profile markers.
   *
   * @option {boolean} withMarkers
   *         Boolean indicating whether or not timeline markers are emitted
   *         once they're accumulated every `DEFAULT_TIMELINE_DATA_PULL_TIMEOUT`
   *         milliseconds.
   * @option {boolean} withTicks
   *         Boolean indicating whether a `ticks` event is fired and a
   *         FramerateActor is created.
   * @option {boolean} withMemory
   *         Boolean indiciating whether we want memory measurements sampled.
   * @option {boolean} withFrames
   *         Boolean indicating whether or not stack frames should be handled
   *         from timeline markers.
   * @option {boolean} withGCEvents
   *         Boolean indicating whether or not GC markers should be emitted.
   *         TODO: Remove these fake GC markers altogether in bug 1198127.
   * @option {boolean} withDocLoadingEvents
   *         Boolean indicating whether or not DOMContentLoaded and Load
   *         marker events are emitted.
   */
  async start({
    withMarkers,
    withTicks,
    withMemory,
    withFrames,
    withGCEvents,
    withDocLoadingEvents,
  }) {
    let docShells = this.docShells;
    if (!docShells.length) {
      return -1;
    }
    let startTime = this._startTime = docShells[0].now();
    if (this._isRecording) {
      return startTime;
    }

    this._isRecording = true;
    this._withMarkers = !!withMarkers;
    this._withTicks = !!withTicks;
    this._withMemory = !!withMemory;
    this._withFrames = !!withFrames;
    this._withGCEvents = !!withGCEvents;
    this._withDocLoadingEvents = !!withDocLoadingEvents;

    if (this._withMarkers || this._withDocLoadingEvents) {
      for (let docShell of docShells) {
        docShell.recordProfileTimelineMarkers = true;
      }
    }

    if (this._withTicks) {
      this._framerate = new Framerate(this.tabActor);
      this._framerate.startRecording();
    }

    if (this._withMemory || this._withGCEvents) {
      this._memory = new Memory(this.tabActor, this._stackFrames);
      this._memory.attach();
    }

    if (this._withGCEvents) {
      this._memory.on("garbage-collection", this._onGarbageCollection);
    }

    if (this._withFrames && this._withMarkers) {
      this._stackFrames = new StackFrameCache();
      this._stackFrames.initFrames();
    }

    this._pullTimelineData();
    return startTime;
  },

  /**
   * Stop recording profile markers.
   */
  async stop() {
    let docShells = this.docShells;
    if (!docShells.length) {
      return -1;
    }
    let endTime = this._startTime = docShells[0].now();
    if (!this._isRecording) {
      return endTime;
    }

    if (this._withMarkers || this._withDocLoadingEvents) {
      for (let docShell of docShells) {
        docShell.recordProfileTimelineMarkers = false;
      }
    }

    if (this._withTicks) {
      this._framerate.stopRecording();
      this._framerate.destroy();
      this._framerate = null;
    }

    if (this._withMemory || this._withGCEvents) {
      this._memory.detach();
      this._memory.destroy();
    }

    if (this._withGCEvents) {
      this._memory.off("garbage-collection", this._onGarbageCollection);
    }

    if (this._withFrames && this._withMarkers) {
      this._stackFrames = null;
    }

    this._isRecording = false;
    this._withMarkers = false;
    this._withTicks = false;
    this._withMemory = false;
    this._withFrames = false;
    this._withDocLoadingEvents = false;
    this._withGCEvents = false;

    clearTimeout(this._dataPullTimeout);

    return endTime;
  },

  /**
   * When a new window becomes available in the tabActor, start recording its
   * markers if we were recording.
   */
  _onWindowReady: function ({ window }) {
    if (this._isRecording) {
      let docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
                           .getInterface(Ci.nsIWebNavigation)
                           .QueryInterface(Ci.nsIDocShell);
      docShell.recordProfileTimelineMarkers = true;
    }
  },

  /**
   * Fired when the Memory component emits a `garbage-collection` event. Used to
   * take the data and make it look like the rest of our markers.
   *
   * A GC "marker" here represents a full GC cycle, which may contain several incremental
   * events within its `collection` array. The marker contains a `reason` field,
   * indicating why there was a GC, and may contain a `nonincrementalReason` when
   * SpiderMonkey could not incrementally collect garbage.
   */
  _onGarbageCollection: function ({
    collections, gcCycleNumber, reason, nonincrementalReason
  }) {
    let docShells = this.docShells;
    if (!this._isRecording || !docShells.length) {
      return;
    }

    let endTime = docShells[0].now();

    this.emit("markers", collections.map(({
      startTimestamp: start, endTimestamp: end
    }) => {
      return {
        name: "GarbageCollection",
        causeName: reason,
        nonincrementalReason: nonincrementalReason,
        cycle: gcCycleNumber,
        start,
        end,
      };
    }), endTime);
  },
};

exports.Timeline = Timeline;
PK
!<���4chrome/devtools/modules/devtools/server/primitive.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 WebGLPrimitivesType = {
  "POINTS": 0,
  "LINES": 1,
  "LINE_LOOP": 2,
  "LINE_STRIP": 3,
  "TRIANGLES": 4,
  "TRIANGLE_STRIP": 5,
  "TRIANGLE_FAN": 6
};

/**
 * A utility for monitoring WebGL primitive draws. Takes a `tabActor`
 * and monitors primitive draws over time.
 */
const WebGLDrawArrays = "drawArrays";
const WebGLDrawElements = "drawElements";

exports.WebGLPrimitiveCounter = class WebGLPrimitiveCounter {
  constructor(tabActor) {
    this.tabActor = tabActor;
  }

  destroy() {}

  /**
   * Starts monitoring primitive draws, storing the primitives count per tick.
   */
  resetCounts() {
    this._tris = 0;
    this._vertices = 0;
    this._points = 0;
    this._lines = 0;
    this._startTime = this.tabActor.docShell.now();
  }

  /**
   * Stops monitoring primitive draws, returning the recorded values.
   */
  getCounts() {
    let result = {
      tris: this._tris,
      vertices: this._vertices,
      points: this._points,
      lines: this._lines
    };

    this._tris = 0;
    this._vertices = 0;
    this._points = 0;
    this._lines = 0;
    return result;
  }

  /**
   * Handles WebGL draw primitive functions to catch primitive info.
   */
  handleDrawPrimitive(functionCall) {
    let { name, args } = functionCall.details;

    if (name === WebGLDrawArrays) {
      this._processDrawArrays(args);
    } else if (name === WebGLDrawElements) {
      this._processDrawElements(args);
    }
  }

  /**
   * Processes WebGL drawArrays method to count primitve numbers
   */
  _processDrawArrays(args) {
    let mode = args[0];
    let count = args[2];

    switch (mode) {
      case WebGLPrimitivesType.POINTS:
        this._vertices += count;
        this._points += count;
        break;
      case WebGLPrimitivesType.LINES:
        this._vertices += count;
        this._lines += (count / 2);
        break;
      case WebGLPrimitivesType.LINE_LOOP:
        this._vertices += count;
        this._lines += count;
        break;
      case WebGLPrimitivesType.LINE_STRIP:
        this._vertices += count;
        this._lines += (count - 1);
        break;
      case WebGLPrimitivesType.TRIANGLES:
        this._tris += (count / 3);
        this._vertices += count;
        break;
      case WebGLPrimitivesType.TRIANGLE_STRIP:
        this._tris += (count - 2);
        this._vertices += count;
        break;
      case WebGLPrimitivesType.TRIANGLE_FAN:
        this._tris += (count - 2);
        this._vertices += count;
        break;
      default:
        console.error("_processDrawArrays doesn't define this type.");
        break;
    }
  }

  /**
   * Processes WebGL drawElements method to count primitve numbers
   */
  _processDrawElements(args) {
    let mode = args[0];
    let count = args[1];

    switch (mode) {
      case WebGLPrimitivesType.POINTS:
        this._vertices += count;
        this._points += count;
        break;
      case WebGLPrimitivesType.LINES:
        this._vertices += count;
        this._lines += (count / 2);
        break;
      case WebGLPrimitivesType.LINE_LOOP:
        this._vertices += count;
        this._lines += count;
        break;
      case WebGLPrimitivesType.LINE_STRIP:
        this._vertices += count;
        this._lines += (count - 1);
        break;
      case WebGLPrimitivesType.TRIANGLES:
        let tris = count / 3;
        let vertex = tris * 3;

        if (tris > 1) {
          vertex = tris * 2;
        }
        this._tris += tris;
        this._vertices += vertex;
        break;
      case WebGLPrimitivesType.TRIANGLE_STRIP:
        this._tris += (count - 2);
        this._vertices += count;
        break;
      case WebGLPrimitivesType.TRIANGLE_FAN:
        this._tris += (count - 2);
        this._vertices += count;
        break;
      default:
        console.error("_processDrawElements doesn't define this type.");
        break;
    }
  }
};
PK
!<5��22?chrome/devtools/modules/devtools/server/service-worker-child.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 addMessageListener */

"use strict";

let swm = Cc["@mozilla.org/serviceworkers/manager;1"]
  .getService(Ci.nsIServiceWorkerManager);

addMessageListener("serviceWorkerRegistration:start", message => {
  let { data } = message;
  let array = swm.getAllRegistrations();

  // Find the service worker registration with the desired scope.
  for (let i = 0; i < array.length; i++) {
    let registration =
      array.queryElementAt(i, Ci.nsIServiceWorkerRegistrationInfo);
    // XXX: In some rare cases, `registration.activeWorker` can be null for a
    // brief moment (e.g. while the service worker is first installing, or if
    // there was an unhandled exception during install that will cause the
    // registration to be removed). We can't do much about it here, simply
    // ignore these cases.
    if (registration.scope === data.scope && registration.activeWorker) {
      // Briefly attaching a debugger to the active service worker will cause
      // it to start running.
      registration.activeWorker.attachDebugger();
      registration.activeWorker.detachDebugger();
      return;
    }
  }
});
PK
!<5)'~~;chrome/devtools/modules/devtools/server/websocket-server.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Cc, CC } = require("chrome");
const { executeSoon } = require("devtools/shared/DevToolsUtils");
const { delimitedRead } = require("devtools/shared/transport/stream-utils");
const CryptoHash = CC("@mozilla.org/security/hash;1", "nsICryptoHash", "initWithString");
const threadManager = Cc["@mozilla.org/thread-manager;1"].getService();

// Limit the header size to put an upper bound on allocated memory
const HEADER_MAX_LEN = 8000;

/**
 * Read a line from async input stream and return promise that resolves to the line once
 * it has been read. If the line is longer than HEADER_MAX_LEN, will throw error.
 */
function readLine(input) {
  return new Promise((resolve, reject) => {
    let line = "";
    let wait = () => {
      input.asyncWait(stream => {
        try {
          let amountToRead = HEADER_MAX_LEN - line.length;
          line += delimitedRead(input, "\n", amountToRead);

          if (line.endsWith("\n")) {
            resolve(line.trimRight());
            return;
          }

          if (line.length >= HEADER_MAX_LEN) {
            throw new Error(
              `Failed to read HTTP header longer than ${HEADER_MAX_LEN} bytes`);
          }

          wait();
        } catch (ex) {
          reject(ex);
        }
      }, 0, 0, threadManager.currentThread);
    };

    wait();
  });
}

/**
 * Write a string of bytes to async output stream and return promise that resolves once
 * all data has been written. Doesn't do any utf-16/utf-8 conversion - the string is
 * treated as an array of bytes.
 */
function writeString(output, data) {
  return new Promise((resolve, reject) => {
    let wait = () => {
      if (data.length === 0) {
        resolve();
        return;
      }

      output.asyncWait(stream => {
        try {
          let written = output.write(data, data.length);
          data = data.slice(written);
          wait();
        } catch (ex) {
          reject(ex);
        }
      }, 0, 0, threadManager.currentThread);
    };

    wait();
  });
}

/**
 * Read HTTP request from async input stream.
 * @return Request line (string) and Map of header names and values.
 */
const readHttpRequest = async function (input) {
  let requestLine = "";
  let headers = new Map();

  while (true) {
    let line = await readLine(input);
    if (line.length == 0) {
      break;
    }

    if (!requestLine) {
      requestLine = line;
    } else {
      let colon = line.indexOf(":");
      if (colon == -1) {
        throw new Error(`Malformed HTTP header: ${line}`);
      }

      let name = line.slice(0, colon).toLowerCase();
      let value = line.slice(colon + 1).trim();
      headers.set(name, value);
    }
  }

  return { requestLine, headers };
};

/**
 * Write HTTP response (array of strings) to async output stream.
 */
function writeHttpResponse(output, response) {
  let responseString = response.join("\r\n") + "\r\n\r\n";
  return writeString(output, responseString);
}

/**
 * Process the WebSocket handshake headers and return the key to be sent in
 * Sec-WebSocket-Accept response header.
 */
function processRequest({ requestLine, headers }) {
  let [ method, path ] = requestLine.split(" ");
  if (method !== "GET") {
    throw new Error("The handshake request must use GET method");
  }

  if (path !== "/") {
    throw new Error("The handshake request has unknown path");
  }

  let upgrade = headers.get("upgrade");
  if (!upgrade || upgrade !== "websocket") {
    throw new Error("The handshake request has incorrect Upgrade header");
  }

  let connection = headers.get("connection");
  if (!connection || !connection.split(",").map(t => t.trim()).includes("Upgrade")) {
    throw new Error("The handshake request has incorrect Connection header");
  }

  let version = headers.get("sec-websocket-version");
  if (!version || version !== "13") {
    throw new Error("The handshake request must have Sec-WebSocket-Version: 13");
  }

  // Compute the accept key
  let key = headers.get("sec-websocket-key");
  if (!key) {
    throw new Error("The handshake request must have a Sec-WebSocket-Key header");
  }

  return { acceptKey: computeKey(key) };
}

function computeKey(key) {
  let str = key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";

  let data = Array.from(str, ch => ch.charCodeAt(0));
  let hash = new CryptoHash("sha1");
  hash.update(data, data.length);
  return hash.finish(true);
}

/**
 * Perform the server part of a WebSocket opening handshake on an incoming connection.
 */
const serverHandshake = async function (input, output) {
  // Read the request
  let request = await readHttpRequest(input);

  try {
    // Check and extract info from the request
    let { acceptKey } = processRequest(request);

    // Send response headers
    await writeHttpResponse(output, [
      "HTTP/1.1 101 Switching Protocols",
      "Upgrade: websocket",
      "Connection: Upgrade",
      `Sec-WebSocket-Accept: ${acceptKey}`,
    ]);
  } catch (error) {
    // Send error response in case of error
    await writeHttpResponse(output, [ "HTTP/1.1 400 Bad Request" ]);
    throw error;
  }
};

/**
 * Accept an incoming WebSocket server connection.
 * Takes an established nsISocketTransport in the parameters.
 * Performs the WebSocket handshake and waits for the WebSocket to open.
 * Returns Promise with a WebSocket ready to send and receive messages.
 */
const accept = async function (transport, input, output) {
  await serverHandshake(input, output);

  let transportProvider = {
    setListener(upgradeListener) {
      // The onTransportAvailable callback shouldn't be called synchronously.
      executeSoon(() => {
        upgradeListener.onTransportAvailable(transport, input, output);
      });
    }
  };

  return new Promise((resolve, reject) => {
    let socket = WebSocket.createServerWebSocket(null, [], transportProvider, "");
    socket.addEventListener("close", () => {
      input.close();
      output.close();
    });

    socket.onopen = () => resolve(socket);
    socket.onerror = err => reject(err);
  });
};

exports.accept = accept;
PK
!<8�f��
�
1chrome/devtools/modules/devtools/server/worker.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

/* eslint-env mozilla/chrome-worker */
/* global worker, loadSubScript, global */

// This function is used to do remote procedure calls from the worker to the
// main thread. It is exposed as a built-in global to every module by the
// worker loader. To make sure the worker loader can access it, it needs to be
// defined before loading the worker loader script below.
this.rpc = function (method, ...params) {
  let id = nextId++;

  postMessage(JSON.stringify({
    type: "rpc",
    method: method,
    params: params,
    id: id
  }));

  let deferred = defer();
  rpcDeferreds[id] = deferred;
  return deferred.promise;
};

loadSubScript("resource://devtools/shared/worker/loader.js");

var defer = worker.require("devtools/shared/defer");
var { ActorPool } = worker.require("devtools/server/actors/common");
var { ThreadActor } = worker.require("devtools/server/actors/thread");
var { WebConsoleActor } = worker.require("devtools/server/actors/webconsole");
var { TabSources } = worker.require("devtools/server/actors/utils/TabSources");
var makeDebugger = worker.require("devtools/server/actors/utils/make-debugger");
var { DebuggerServer } = worker.require("devtools/server/main");

DebuggerServer.init();
DebuggerServer.createRootActor = function () {
  throw new Error("Should never get here!");
};

var connections = Object.create(null);
var nextId = 0;
var rpcDeferreds = [];

this.addEventListener("message", function (event) {
  let packet = JSON.parse(event.data);
  switch (packet.type) {
    case "connect":
      // Step 3: Create a connection to the parent.
      let connection = DebuggerServer.connectToParent(packet.id, this);
      connections[packet.id] = {
        connection,
        rpcs: []
      };

      // Step 4: Create a thread actor for the connection to the parent.
      let pool = new ActorPool(connection);
      connection.addActorPool(pool);

      let sources = null;

      let parent = {
        actorID: packet.id,

        makeDebugger: makeDebugger.bind(null, {
          findDebuggees: () => {
            return [this.global];
          },

          shouldAddNewGlobalAsDebuggee: () => {
            return true;
          },
        }),

        get sources() {
          if (sources === null) {
            sources = new TabSources(threadActor);
          }
          return sources;
        },

        window: global
      };

      let threadActor = new ThreadActor(parent, global);
      pool.addActor(threadActor);

      // parentActor.threadActor is needed from the webconsole for grip previewing
      parent.threadActor = threadActor;

      let consoleActor = new WebConsoleActor(connection, parent);
      pool.addActor(consoleActor);

      // Step 5: Send a response packet to the parent to notify
      // it that a connection has been established.
      postMessage(JSON.stringify({
        type: "connected",
        id: packet.id,
        threadActor: threadActor.actorID,
        consoleActor: consoleActor.actorID,
      }));
      break;

    case "disconnect":
      connections[packet.id].connection.close();
      break;

    case "rpc":
      let deferred = rpcDeferreds[packet.id];
      delete rpcDeferreds[packet.id];
      if (packet.error) {
        deferred.reject(packet.error);
      }
      deferred.resolve(packet.result);
      break;
  }
});
PK
!<Q�O�e�e8chrome/devtools/modules/devtools/shared/DevToolsUtils.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 setImmediate, rpc */

"use strict";

/* General utilities used throughout devtools. */

var { Ci, Cu, components } = require("chrome");
var Services = require("Services");
var promise = require("promise");
var defer = require("devtools/shared/defer");
var flags = require("./flags");
var {getStack, callFunctionWithAsyncStack} = require("devtools/shared/platform/stack");

loader.lazyRequireGetter(this, "FileUtils",
                         "resource://gre/modules/FileUtils.jsm", true);

// Using this name lets the eslint plugin know about lazy defines in
// this file.
var DevToolsUtils = exports;

// Re-export the thread-safe utils.
const ThreadSafeDevToolsUtils = require("./ThreadSafeDevToolsUtils.js");
for (let key of Object.keys(ThreadSafeDevToolsUtils)) {
  exports[key] = ThreadSafeDevToolsUtils[key];
}

/**
 * Helper for Cu.isCrossProcessWrapper that works with Debugger.Objects.
 * This will always return false in workers (see the implementation in
 * ThreadSafeDevToolsUtils.js).
 *
 * @param Debugger.Object debuggerObject
 * @return bool
 */
exports.isCPOW = function (debuggerObject) {
  try {
    return Cu.isCrossProcessWrapper(debuggerObject.unsafeDereference());
  } catch (e) { }
  return false;
};

/**
 * Waits for the next tick in the event loop to execute a callback.
 */
exports.executeSoon = function (fn) {
  if (isWorker) {
    setImmediate(fn);
  } else {
    let executor;
    // Only enable async stack reporting when DEBUG_JS_MODULES is set
    // (customized local builds) to avoid a performance penalty.
    if (AppConstants.DEBUG_JS_MODULES || flags.testing) {
      let stack = getStack();
      executor = () => {
        callFunctionWithAsyncStack(fn, stack, "DevToolsUtils.executeSoon");
      };
    } else {
      executor = fn;
    }
    Services.tm.dispatchToMainThread({
      run: exports.makeInfallible(executor)
    });
  }
};

/**
 * Waits for the next tick in the event loop.
 *
 * @return Promise
 *         A promise that is resolved after the next tick in the event loop.
 */
exports.waitForTick = function () {
  let deferred = defer();
  exports.executeSoon(deferred.resolve);
  return deferred.promise;
};

/**
 * Waits for the specified amount of time to pass.
 *
 * @param number delay
 *        The amount of time to wait, in milliseconds.
 * @return Promise
 *         A promise that is resolved after the specified amount of time passes.
 */
exports.waitForTime = function (delay) {
  let deferred = defer();
  setTimeout(deferred.resolve, delay);
  return deferred.promise;
};

/**
 * Like Array.prototype.forEach, but doesn't cause jankiness when iterating over
 * very large arrays by yielding to the browser and continuing execution on the
 * next tick.
 *
 * @param Array array
 *        The array being iterated over.
 * @param Function fn
 *        The function called on each item in the array. If a promise is
 *        returned by this function, iterating over the array will be paused
 *        until the respective promise is resolved.
 * @returns Promise
 *          A promise that is resolved once the whole array has been iterated
 *          over, and all promises returned by the fn callback are resolved.
 */
exports.yieldingEach = function (array, fn) {
  const deferred = defer();

  let i = 0;
  let len = array.length;
  let outstanding = [deferred.promise];

  (function loop() {
    const start = Date.now();

    while (i < len) {
      // Don't block the main thread for longer than 16 ms at a time. To
      // maintain 60fps, you have to render every frame in at least 16ms; we
      // aren't including time spent in non-JS here, but this is Good
      // Enough(tm).
      if (Date.now() - start > 16) {
        exports.executeSoon(loop);
        return;
      }

      try {
        outstanding.push(fn(array[i], i++));
      } catch (e) {
        deferred.reject(e);
        return;
      }
    }

    deferred.resolve();
  }());

  return promise.all(outstanding);
};

/**
 * Like XPCOMUtils.defineLazyGetter, but with a |this| sensitive getter that
 * allows the lazy getter to be defined on a prototype and work correctly with
 * instances.
 *
 * @param Object object
 *        The prototype object to define the lazy getter on.
 * @param String key
 *        The key to define the lazy getter on.
 * @param Function callback
 *        The callback that will be called to determine the value. Will be
 *        called with the |this| value of the current instance.
 */
exports.defineLazyPrototypeGetter = function (object, key, callback) {
  Object.defineProperty(object, key, {
    configurable: true,
    get: function () {
      const value = callback.call(this);

      Object.defineProperty(this, key, {
        configurable: true,
        writable: true,
        value: value
      });

      return value;
    }
  });
};

/**
 * Safely get the property value from a Debugger.Object for a given key. Walks
 * the prototype chain until the property is found.
 *
 * @param Debugger.Object object
 *        The Debugger.Object to get the value from.
 * @param String key
 *        The key to look for.
 * @return Any
 */
exports.getProperty = function (object, key) {
  let root = object;
  while (object && exports.isSafeDebuggerObject(object)) {
    let desc;
    try {
      desc = object.getOwnPropertyDescriptor(key);
    } catch (e) {
      // The above can throw when the debuggee does not subsume the object's
      // compartment, or for some WrappedNatives like Cu.Sandbox.
      return undefined;
    }
    if (desc) {
      if ("value" in desc) {
        return desc.value;
      }
      // Call the getter if it's safe.
      if (exports.hasSafeGetter(desc)) {
        try {
          return desc.get.call(root).return;
        } catch (e) {
          // If anything goes wrong report the error and return undefined.
          exports.reportException("getProperty", e);
        }
      }
      return undefined;
    }
    object = object.proto;
  }
  return undefined;
};

/**
 * Removes all the non-opaque security wrappers of a debuggee object.
 *
 * @param obj Debugger.Object
 *        The debuggee object to be unwrapped.
 * @return Debugger.Object|null|undefined
 *      - If the object has no wrapper, the same `obj` is returned. Note DeadObject
 *        objects belong to this case.
 *      - Otherwise, if the debuggee doesn't subsume object's compartment, returns `null`.
 *      - Otherwise, if the object belongs to an invisible-to-debugger compartment,
 *        returns `undefined`. Note CPOW objects belong to this case.
 *      - Otherwise, returns the unwrapped object.
 */
exports.unwrap = function unwrap(obj) {
  // Check if `obj` has an opaque wrapper.
  if (obj.class === "Opaque") {
    return obj;
  }

  // Attempt to unwrap via `obj.unwrap()`. Note that:
  // - This will return `null` if the debuggee does not subsume object's compartment.
  // - This will throw if the object belongs to an invisible-to-debugger compartment.
  //   This case includes CPOWs (see bug 1391449).
  // - This will return `obj` if there is no wrapper.
  let unwrapped;
  try {
    unwrapped = obj.unwrap();
  } catch (err) {
    return undefined;
  }

  // Check if further unwrapping is not possible.
  if (!unwrapped || unwrapped === obj) {
    return unwrapped;
  }

  // Recursively remove additional security wrappers.
  return unwrap(unwrapped);
};

/**
 * Checks whether a debuggee object is safe. Unsafe objects may run proxy traps or throw
 * when using `proto`, `isExtensible`, `isFrozen` or `isSealed`. Note that safe objects
 * may still throw when calling `getOwnPropertyNames`, `getOwnPropertyDescriptor`, etc.
 * Also note CPOW objects are considered to be unsafe, and DeadObject objects to be safe.
 *
 * @param obj Debugger.Object
 *        The debuggee object to be checked.
 * @return boolean
 */
exports.isSafeDebuggerObject = function (obj) {
  let unwrapped = exports.unwrap(obj);

  // Objects belonging to an invisible-to-debugger compartment might be proxies,
  // so just in case consider them unsafe. CPOWs are included in this case.
  if (unwrapped === undefined) {
    return false;
  }

  // If the debuggee does not subsume the object's compartment, most properties won't
  // be accessible. Cross-origin Window and Location objects might expose some, though.
  // Therefore, it must be considered safe. Note that proxy objects have fully opaque
  // security wrappers, so proxy traps won't run in this case.
  if (unwrapped === null) {
    return true;
  }

  // Proxy objects can run traps when accessed. `isProxy` getter is called on `unwrapped`
  // instead of on `obj` in order to detect proxies behind transparent wrappers.
  if (unwrapped.isProxy) {
    return false;
  }

  return true;
};

/**
 * Determines if a descriptor has a getter which doesn't call into JavaScript.
 *
 * @param Object desc
 *        The descriptor to check for a safe getter.
 * @return Boolean
 *         Whether a safe getter was found.
 */
exports.hasSafeGetter = function (desc) {
  // Scripted functions that are CCWs will not appear scripted until after
  // unwrapping.
  let fn = desc.get;
  fn = fn && exports.unwrap(fn);
  return fn && fn.callable && fn.class == "Function" && fn.script === undefined;
};

/**
 * Check if it is safe to read properties and execute methods from the given JS
 * object. Safety is defined as being protected from unintended code execution
 * from content scripts (or cross-compartment code).
 *
 * See bugs 945920 and 946752 for discussion.
 *
 * @type Object obj
 *       The object to check.
 * @return Boolean
 *         True if it is safe to read properties from obj, or false otherwise.
 */
exports.isSafeJSObject = function (obj) {
  // If we are running on a worker thread, Cu is not available. In this case,
  // we always return false, just to be on the safe side.
  if (isWorker) {
    return false;
  }

  if (Cu.getGlobalForObject(obj) ==
      Cu.getGlobalForObject(exports.isSafeJSObject)) {
    // obj is not a cross-compartment wrapper.
    return true;
  }

  // Xray wrappers protect against unintended code execution.
  if (Cu.isXrayWrapper(obj)) {
    return true;
  }

  // If there aren't Xrays, only allow chrome objects.
  let principal = Cu.getObjectPrincipal(obj);
  if (!Services.scriptSecurityManager.isSystemPrincipal(principal)) {
    return false;
  }

  // Scripted proxy objects without Xrays can run their proxy traps.
  if (Cu.isProxy(obj)) {
    return false;
  }

  // Even if `obj` looks safe, an unsafe object in its prototype chain may still
  // run unintended code, e.g. when using the `instanceof` operator.
  let proto = Object.getPrototypeOf(obj);
  if (proto && !exports.isSafeJSObject(proto)) {
    return false;
  }

  // Allow non-problematic chrome objects.
  return true;
};

exports.dumpn = function (str) {
  if (flags.wantLogging) {
    dump("DBG-SERVER: " + str + "\n");
  }
};

/**
 * A verbose logger for low-level tracing.
 */
exports.dumpv = function (msg) {
  if (flags.wantVerbose) {
    exports.dumpn(msg);
  }
};

/**
 * Defines a getter on a specified object that will be created upon first use.
 *
 * @param object
 *        The object to define the lazy getter on.
 * @param name
 *        The name of the getter to define on object.
 * @param lambda
 *        A function that returns what the getter should return.  This will
 *        only ever be called once.
 */
exports.defineLazyGetter = function (object, name, lambda) {
  Object.defineProperty(object, name, {
    get: function () {
      delete object[name];
      object[name] = lambda.apply(object);
      return object[name];
    },
    configurable: true,
    enumerable: true
  });
};

DevToolsUtils.defineLazyGetter(this, "AppConstants", () => {
  if (isWorker) {
    return {};
  }
  return require("resource://gre/modules/AppConstants.jsm").AppConstants;
});

/**
 * No operation. The empty function.
 */
exports.noop = function () { };

let assertionFailureCount = 0;

Object.defineProperty(exports, "assertionFailureCount", {
  get() {
    return assertionFailureCount;
  }
});

function reallyAssert(condition, message) {
  if (!condition) {
    assertionFailureCount++;
    const err = new Error("Assertion failure: " + message);
    exports.reportException("DevToolsUtils.assert", err);
    throw err;
  }
}

/**
 * DevToolsUtils.assert(condition, message)
 *
 * @param Boolean condition
 * @param String message
 *
 * Assertions are enabled when any of the following are true:
 *   - This is a DEBUG_JS_MODULES build
 *   - This is a DEBUG build
 *   - flags.testing is set to true
 *
 * If assertions are enabled, then `condition` is checked and if false-y, the
 * assertion failure is logged and then an error is thrown.
 *
 * If assertions are not enabled, then this function is a no-op.
 */
Object.defineProperty(exports, "assert", {
  get: () => (AppConstants.DEBUG || AppConstants.DEBUG_JS_MODULES || flags.testing)
    ? reallyAssert
    : exports.noop,
});

/**
 * Defines a getter on a specified object for a module.  The module will not
 * be imported until first use.
 *
 * @param object
 *        The object to define the lazy getter on.
 * @param name
 *        The name of the getter to define on object for the module.
 * @param resource
 *        The URL used to obtain the module.
 * @param symbol
 *        The name of the symbol exported by the module.
 *        This parameter is optional and defaults to name.
 */
exports.defineLazyModuleGetter = function (object, name, resource, symbol) {
  this.defineLazyGetter(object, name, function () {
    let temp = {};
    ChromeUtils.import(resource, temp);
    return temp[symbol || name];
  });
};

DevToolsUtils.defineLazyGetter(this, "NetUtil", () => {
  return require("resource://gre/modules/NetUtil.jsm").NetUtil;
});

DevToolsUtils.defineLazyGetter(this, "OS", () => {
  return require("resource://gre/modules/osfile.jsm").OS;
});

DevToolsUtils.defineLazyGetter(this, "NetworkHelper", () => {
  return require("devtools/shared/webconsole/network-helper");
});

/**
 * Performs a request to load the desired URL and returns a promise.
 *
 * @param urlIn String
 *        The URL we will request.
 * @param aOptions Object
 *        An object with the following optional properties:
 *        - loadFromCache: if false, will bypass the cache and
 *          always load fresh from the network (default: true)
 *        - policy: the nsIContentPolicy type to apply when fetching the URL
 *                  (only works when loading from system principal)
 *        - window: the window to get the loadGroup from
 *        - charset: the charset to use if the channel doesn't provide one
 *        - principal: the principal to use, if omitted, the request is loaded
 *                     with a codebase principal corresponding to the url being
 *                     loaded, using the origin attributes of the window, if any.
 *        - cacheKey: when loading from cache, use this key to retrieve a cache
 *                    specific to a given SHEntry. (Allows loading POST
 *                    requests from cache)
 * @returns Promise that resolves with an object with the following members on
 *          success:
 *           - content: the document at that URL, as a string,
 *           - contentType: the content type of the document
 *
 *          If an error occurs, the promise is rejected with that error.
 *
 * XXX: It may be better to use nsITraceableChannel to get to the sources
 * without relying on caching when we can (not for eval, etc.):
 * http://www.softwareishard.com/blog/firebug/nsitraceablechannel-intercept-http-traffic/
 */
function mainThreadFetch(urlIn, aOptions = { loadFromCache: true,
                                             policy: Ci.nsIContentPolicy.TYPE_OTHER,
                                             window: null,
                                             charset: null,
                                             principal: null,
                                             cacheKey: null }) {
  // Create a channel.
  let url = urlIn.split(" -> ").pop();
  let channel;
  try {
    channel = newChannelForURL(url, aOptions);
  } catch (ex) {
    return promise.reject(ex);
  }

  // Set the channel options.
  channel.loadFlags = aOptions.loadFromCache
    ? channel.LOAD_FROM_CACHE
    : channel.LOAD_BYPASS_CACHE;

  // When loading from cache, the cacheKey allows us to target a specific
  // SHEntry and offer ways to restore POST requests from cache.
  if (aOptions.loadFromCache &&
      aOptions.cacheKey && channel instanceof Ci.nsICacheInfoChannel) {
    channel.cacheKey = aOptions.cacheKey;
  }

  if (aOptions.window) {
    // Respect private browsing.
    channel.loadGroup = aOptions.window.QueryInterface(Ci.nsIInterfaceRequestor)
                          .getInterface(Ci.nsIWebNavigation)
                          .QueryInterface(Ci.nsIDocumentLoader)
                          .loadGroup;
  }

  let deferred = defer();
  let onResponse = (stream, status, request) => {
    if (!components.isSuccessCode(status)) {
      deferred.reject(new Error(`Failed to fetch ${url}. Code ${status}.`));
      return;
    }

    try {
      // We cannot use NetUtil to do the charset conversion as if charset
      // information is not available and our default guess is wrong the method
      // might fail and we lose the stream data. This means we can't fall back
      // to using the locale default encoding (bug 1181345).

      // Read and decode the data according to the locale default encoding.
      let available = stream.available();
      let source = NetUtil.readInputStreamToString(stream, available);
      stream.close();

      // We do our own BOM sniffing here because there's no convenient
      // implementation of the "decode" algorithm
      // (https://encoding.spec.whatwg.org/#decode) exposed to JS.
      let bomCharset = null;
      if (available >= 3 && source.codePointAt(0) == 0xef &&
          source.codePointAt(1) == 0xbb && source.codePointAt(2) == 0xbf) {
        bomCharset = "UTF-8";
        source = source.slice(3);
      } else if (available >= 2 && source.codePointAt(0) == 0xfe &&
                 source.codePointAt(1) == 0xff) {
        bomCharset = "UTF-16BE";
        source = source.slice(2);
      } else if (available >= 2 && source.codePointAt(0) == 0xff &&
                 source.codePointAt(1) == 0xfe) {
        bomCharset = "UTF-16LE";
        source = source.slice(2);
      }

      // If the channel or the caller has correct charset information, the
      // content will be decoded correctly. If we have to fall back to UTF-8 and
      // the guess is wrong, the conversion fails and convertToUnicode returns
      // the input unmodified. Essentially we try to decode the data as UTF-8
      // and if that fails, we use the locale specific default encoding. This is
      // the best we can do if the source does not provide charset info.
      let charset = bomCharset;
      if (!charset) {
        try {
          charset = channel.contentCharset;
        } catch (e) {
          // Accessing `contentCharset` on content served by a service worker in
          // non-e10s may throw.
        }
      }
      if (!charset) {
        charset = aOptions.charset || "UTF-8";
      }
      let unicodeSource = NetworkHelper.convertToUnicode(source, charset);

      deferred.resolve({
        content: unicodeSource,
        contentType: request.contentType
      });
    } catch (ex) {
      let uri = request.originalURI;
      if (ex.name === "NS_BASE_STREAM_CLOSED" && uri instanceof Ci.nsIFileURL) {
        // Empty files cause NS_BASE_STREAM_CLOSED exception. Use OS.File to
        // differentiate between empty files and other errors (bug 1170864).
        // This can be removed when bug 982654 is fixed.

        uri.QueryInterface(Ci.nsIFileURL);
        let result = OS.File.read(uri.file.path).then(bytes => {
          // Convert the bytearray to a String.
          let decoder = new TextDecoder();
          let content = decoder.decode(bytes);

          // We can't detect the contentType without opening a channel
          // and that failed already. This is the best we can do here.
          return {
            content,
            contentType: "text/plain"
          };
        });

        deferred.resolve(result);
      } else {
        deferred.reject(ex);
      }
    }
  };

  // Open the channel
  try {
    NetUtil.asyncFetch(channel, onResponse);
  } catch (ex) {
    return promise.reject(ex);
  }

  return deferred.promise;
}

/**
 * Opens a channel for given URL. Tries a bit harder than NetUtil.newChannel.
 *
 * @param {String} url - The URL to open a channel for.
 * @param {Object} options - The options object passed to @method fetch.
 * @return {nsIChannel} - The newly created channel. Throws on failure.
 */
function newChannelForURL(url, { policy, window, principal }) {
  let securityFlags = Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL;

  let uri;
  try {
    uri = Services.io.newURI(url);
  } catch (e) {
    // In the xpcshell tests, the script url is the absolute path of the test
    // file, which will make a malformed URI error be thrown. Add the file
    // scheme to see if it helps.
    uri = Services.io.newURI("file://" + url);
  }
  let channelOptions = {
    contentPolicyType: policy,
    securityFlags: securityFlags,
    uri: uri
  };

  // Ensure that we have some contentPolicyType type set if one was
  // not provided.
  if (!channelOptions.contentPolicyType) {
    channelOptions.contentPolicyType = Ci.nsIContentPolicy.TYPE_OTHER;
  }

  // If a window is provided, always use it's document as the loadingNode.
  // This will provide the correct principal, origin attributes, service
  // worker controller, etc.
  if (window) {
    channelOptions.loadingNode = window.document;
  } else {
    // If a window is not provided, then we must set a loading principal.

    // If the caller did not provide a principal, then we use the URI
    // to create one.  Note, it's not clear what use cases require this
    // and it may not be correct.
    let prin = principal;
    if (!prin) {
      prin = Services.scriptSecurityManager
                     .createCodebasePrincipal(uri, {});
    }

    channelOptions.loadingPrincipal = prin;
  }

  try {
    return NetUtil.newChannel(channelOptions);
  } catch (e) {
    // In xpcshell tests on Windows, nsExternalProtocolHandler::NewChannel()
    // can throw NS_ERROR_UNKNOWN_PROTOCOL if the external protocol isn't
    // supported by Windows, so we also need to handle the exception here if
    // parsing the URL above doesn't throw.
    return newChannelForURL("file://" + url, { policy, window, principal });
  }
}

// Fetch is defined differently depending on whether we are on the main thread
// or a worker thread.
if (this.isWorker) {
  // Services is not available in worker threads, nor is there any other way
  // to fetch a URL. We need to enlist the help from the main thread here, by
  // issuing an rpc request, to fetch the URL on our behalf.
  exports.fetch = function (url, options) {
    return rpc("fetch", url, options);
  };
} else {
  exports.fetch = mainThreadFetch;
}

/**
 * Open the file at the given path for reading.
 *
 * @param {String} filePath
 *
 * @returns Promise<nsIInputStream>
 */
exports.openFileStream = function (filePath) {
  return new Promise((resolve, reject) => {
    const uri = NetUtil.newURI(new FileUtils.File(filePath));
    NetUtil.asyncFetch(
      { uri, loadUsingSystemPrincipal: true },
      (stream, result) => {
        if (!components.isSuccessCode(result)) {
          reject(new Error(`Could not open "${filePath}": result = ${result}`));
          return;
        }

        resolve(stream);
      }
    );
  });
};

/*
 * All of the flags have been moved to a different module. Make sure
 * nobody is accessing them anymore, and don't write new code using
 * them. We can remove this code after a while.
 */
function errorOnFlag(exports, name) {
  Object.defineProperty(exports, name, {
    get: () => {
      const msg = `Cannot get the flag ${name}. ` +
            `Use the "devtools/shared/flags" module instead`;
      console.error(msg);
      throw new Error(msg);
    },
    set: () => {
      const msg = `Cannot set the flag ${name}. ` +
            `Use the "devtools/shared/flags" module instead`;
      console.error(msg);
      throw new Error(msg);
    }
  });
}

errorOnFlag(exports, "testing");
errorOnFlag(exports, "wantLogging");
errorOnFlag(exports, "wantVerbose");

// Calls the property with the given `name` on the given `object`, where
// `name` is a string, and `object` a Debugger.Object instance.
//
// This function uses only the Debugger.Object API to call the property. It
// avoids the use of unsafeDeference. This is useful for example in workers,
// where unsafeDereference will return an opaque security wrapper to the
// referent.
function callPropertyOnObject(object, name) {
  // Find the property.
  let descriptor;
  let proto = object;
  do {
    descriptor = proto.getOwnPropertyDescriptor(name);
    if (descriptor !== undefined) {
      break;
    }
    proto = proto.proto;
  } while (proto !== null);
  if (descriptor === undefined) {
    throw new Error("No such property");
  }
  let value = descriptor.value;
  if (typeof value !== "object" || value === null || !("callable" in value)) {
    throw new Error("Not a callable object.");
  }

  // Call the property.
  let result = value.call(object);
  if (result === null) {
    throw new Error("Code was terminated.");
  }
  if ("throw" in result) {
    throw result.throw;
  }
  return result.return;
}

exports.callPropertyOnObject = callPropertyOnObject;
PK
!<��َgg2chrome/devtools/modules/devtools/shared/Loader.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

/**
 * Manages the base loader (base-loader.js) instance used to load the developer tools.
 */

var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm", {});
var { Loader, Require, resolveURI, unload } =
  ChromeUtils.import("resource://devtools/shared/base-loader.js", {});
var { requireRawId } = ChromeUtils.import("resource://devtools/shared/loader-plugin-raw.jsm", {});

this.EXPORTED_SYMBOLS = ["DevToolsLoader", "devtools", "BuiltinProvider",
                         "require", "loader"];

/**
 * Providers are different strategies for loading the devtools.
 */

/**
 * Used when the tools should be loaded from the Firefox package itself.
 * This is the default case.
 */
function BuiltinProvider() {}
BuiltinProvider.prototype = {
  load: function () {
    const paths = {
      // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
      "devtools": "resource://devtools",
      // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
      "gcli": "resource://devtools/shared/gcli/source/lib/gcli",
      // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
      "acorn": "resource://devtools/shared/acorn",
      // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
      "acorn/util/walk": "resource://devtools/shared/acorn/walk.js",
      // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
      "source-map": "resource://devtools/shared/sourcemap/source-map.js",
      // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
      // Allow access to xpcshell test items from the loader.
      "xpcshell-test": "resource://test",

      // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
      // Allow access to locale data using paths closer to what is
      // used in the source tree.
      "devtools/client/locales": "chrome://devtools/locale",
      "devtools/shared/locales": "chrome://devtools-shared/locale",
      "devtools/shim/locales": "chrome://devtools-shim/locale",
      "toolkit/locales": "chrome://global/locale",
    };
    // When creating a Loader invisible to the Debugger, we have to ensure
    // using only modules and not depend on any JSM. As everything that is
    // not loaded with Loader isn't going to respect `invisibleToDebugger`.
    // But we have to keep using Promise.jsm for other loader to prevent
    // breaking unhandled promise rejection in tests.
    if (this.invisibleToDebugger) {
      paths.promise = "resource://gre/modules/Promise-backend.js";
    }
    this.loader = new Loader({
      paths,
      invisibleToDebugger: this.invisibleToDebugger,
      sharedGlobal: true,
      sandboxName: "DevTools (Module loader)",
      requireHook: (id, require) => {
        if (id.startsWith("raw!") || id.startsWith("theme-loader!")) {
          return requireRawId(id, require);
        }
        return require(id);
      },
    });
  },

  unload: function (reason) {
    unload(this.loader, reason);
    delete this.loader;
  },
};

var gNextLoaderID = 0;

/**
 * The main devtools API. The standard instance of this loader is exported as
 * |devtools| below, but if a fresh copy of the loader is needed, then a new
 * one can also be created.
 */
this.DevToolsLoader = function DevToolsLoader() {
  this.require = this.require.bind(this);

  Services.obs.addObserver(this, "devtools-unload");
};

DevToolsLoader.prototype = {
  destroy: function (reason = "shutdown") {
    Services.obs.removeObserver(this, "devtools-unload");

    if (this._provider) {
      this._provider.unload(reason);
      delete this._provider;
    }
  },

  get provider() {
    if (!this._provider) {
      this._loadProvider();
    }
    return this._provider;
  },

  _provider: null,

  get id() {
    if (this._id) {
      return this._id;
    }
    this._id = ++gNextLoaderID;
    return this._id;
  },

  /**
   * A dummy version of require, in case a provider hasn't been chosen yet when
   * this is first called.  This will then be replaced by the real version.
   * @see setProvider
   */
  require: function () {
    if (!this._provider) {
      this._loadProvider();
    }
    return this.require.apply(this, arguments);
  },

  /**
   * Return true if |id| refers to something requiring help from a
   * loader plugin.
   */
  isLoaderPluginId: function (id) {
    return id.startsWith("raw!");
  },

  /**
   * Override the provider used to load the tools.
   */
  setProvider: function (provider) {
    if (provider === this._provider) {
      return;
    }

    if (this._provider) {
      delete this.require;
      this._provider.unload("newprovider");
    }
    this._provider = provider;

    // Pass through internal loader settings specific to this loader instance
    this._provider.invisibleToDebugger = this.invisibleToDebugger;

    this._provider.load();
    this.require = Require(this._provider.loader, { id: "devtools" });

    // Fetch custom pseudo modules and globals
    let { modules, globals } = this.require("devtools/shared/builtin-modules");

    // When creating a Loader for the browser toolbox, we have to use
    // Promise-backend.js, as a Loader module. Instead of Promise.jsm which
    // can't be flagged as invisible to debugger.
    if (this.invisibleToDebugger) {
      delete modules.promise;
    }

    // Register custom pseudo modules to the current loader instance
    let loader = this._provider.loader;
    for (let id in modules) {
      let uri = resolveURI(id, loader.mapping);
      loader.modules[uri] = {
        get exports() {
          return modules[id];
        }
      };
    }

    // Register custom globals to the current loader instance
    globals.loader.id = this.id;
    Object.defineProperties(loader.globals, Object.getOwnPropertyDescriptors(globals));

    // Expose lazy helpers on loader
    this.lazyGetter = globals.loader.lazyGetter;
    this.lazyImporter = globals.loader.lazyImporter;
    this.lazyServiceGetter = globals.loader.lazyServiceGetter;
    this.lazyRequireGetter = globals.loader.lazyRequireGetter;
  },

  /**
   * Choose a default tools provider based on the preferences.
   */
  _loadProvider: function () {
    this.setProvider(new BuiltinProvider());
  },

  /**
   * Handles "devtools-unload" event
   *
   * @param String data
   *    reason passed to modules when unloaded
   */
  observe: function (subject, topic, data) {
    if (topic != "devtools-unload") {
      return;
    }
    this.destroy(data);
  },

  /**
   * Sets whether the compartments loaded by this instance should be invisible
   * to the debugger.  Invisibility is needed for loaders that support debugging
   * of chrome code.  This is true of remote target environments, like Fennec or
   * B2G.  It is not the default case for desktop Firefox because we offer the
   * Browser Toolbox for chrome debugging there, which uses its own, separate
   * loader instance.
   * @see devtools/client/framework/ToolboxProcess.jsm
   */
  invisibleToDebugger: Services.appinfo.name !== "Firefox"
};

// Export the standard instance of DevToolsLoader used by the tools.
this.devtools = this.loader = new DevToolsLoader();

this.require = this.devtools.require.bind(this.devtools);

// For compatibility reasons, expose these symbols on "devtools":
Object.defineProperty(this.devtools, "Toolbox", {
  get: () => this.require("devtools/client/framework/toolbox").Toolbox
});
Object.defineProperty(this.devtools, "TargetFactory", {
  get: () => this.require("devtools/client/framework/target").TargetFactory
});
PK
!<�:�&\�\�2chrome/devtools/modules/devtools/shared/Parser.jsm/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");

ChromeUtils.defineModuleGetter(this,
  "Reflect", "resource://gre/modules/reflect.jsm");

this.EXPORTED_SYMBOLS = ["Parser", "ParserHelpers", "SyntaxTreeVisitor"];

/**
 * A JS parser using the reflection API.
 */
this.Parser = function Parser() {
  this._cache = new Map();
  this.errors = [];
  this.logExceptions = true;
};

Parser.prototype = {
  /**
   * Gets a collection of parser methods for a specified source.
   *
   * @param string source
   *        The source text content.
   * @param string url [optional]
   *        The source url. The AST nodes will be cached, so you can use this
   *        identifier to avoid parsing the whole source again.
   */
  get(source, url = "") {
    // Try to use the cached AST nodes, to avoid useless parsing operations.
    if (this._cache.has(url)) {
      return this._cache.get(url);
    }

    // The source may not necessarily be JS, in which case we need to extract
    // all the scripts. Fastest/easiest way is with a regular expression.
    // Don't worry, the rules of using a <script> tag are really strict,
    // this will work.
    let regexp = /<script[^>]*?(?:>([^]*?)<\/script\s*>|\/>)/gim;
    let syntaxTrees = [];
    let scriptMatches = [];
    let scriptMatch;

    if (source.match(/^\s*</)) {
      // First non whitespace character is &lt, so most definitely HTML.
      while ((scriptMatch = regexp.exec(source))) {
        // Contents are captured at index 1 or nothing: Self-closing scripts
        // won't capture code content
        scriptMatches.push(scriptMatch[1] || "");
      }
    }

    // If there are no script matches, send the whole source directly to the
    // reflection API to generate the AST nodes.
    if (!scriptMatches.length) {
      // Reflect.parse throws when encounters a syntax error.
      try {
        let nodes = Reflect.parse(source);
        let length = source.length;
        syntaxTrees.push(new SyntaxTree(nodes, url, length));
      } catch (e) {
        this.errors.push(e);
        if (this.logExceptions) {
          DevToolsUtils.reportException(url, e);
        }
      }
    } else {
      // Generate the AST nodes for each script.
      for (let script of scriptMatches) {
        // Reflect.parse throws when encounters a syntax error.
        try {
          let nodes = Reflect.parse(script);
          let offset = source.indexOf(script);
          let length = script.length;
          syntaxTrees.push(new SyntaxTree(nodes, url, length, offset));
        } catch (e) {
          this.errors.push(e);
          if (this.logExceptions) {
            DevToolsUtils.reportException(url, e);
          }
        }
      }
    }

    let pool = new SyntaxTreesPool(syntaxTrees, url);

    // Cache the syntax trees pool by the specified url. This is entirely
    // optional, but it's strongly encouraged to cache ASTs because
    // generating them can be costly with big/complex sources.
    if (url) {
      this._cache.set(url, pool);
    }

    return pool;
  },

  /**
   * Clears all the parsed sources from cache.
   */
  clearCache() {
    this._cache.clear();
  },

  /**
   * Clears the AST for a particular source.
   *
   * @param String url
   *        The URL of the source that is being cleared.
   */
  clearSource(url) {
    this._cache.delete(url);
  },

  _cache: null,
  errors: null
};

/**
 * A pool handling a collection of AST nodes generated by the reflection API.
 *
 * @param object syntaxTrees
 *        A collection of AST nodes generated for a source.
 * @param string url [optional]
 *        The source url.
 */
function SyntaxTreesPool(syntaxTrees, url = "<unknown>") {
  this._trees = syntaxTrees;
  this._url = url;
  this._cache = new Map();
}

SyntaxTreesPool.prototype = {
  /**
   * @see SyntaxTree.prototype.getIdentifierAt
   */
  getIdentifierAt({ line, column, scriptIndex, ignoreLiterals }) {
    return this._call("getIdentifierAt",
      scriptIndex, line, column, ignoreLiterals)[0];
  },

  /**
   * @see SyntaxTree.prototype.getNamedFunctionDefinitions
   */
  getNamedFunctionDefinitions(substring) {
    return this._call("getNamedFunctionDefinitions", -1, substring);
  },

  /**
   * @return SyntaxTree
   *         The last tree in this._trees
   */
  getLastSyntaxTree() {
    return this._trees[this._trees.length - 1];
  },

  /**
   * Gets the total number of scripts in the parent source.
   * @return number
   */
  get scriptCount() {
    return this._trees.length;
  },

  /**
   * Finds the start and length of the script containing the specified offset
   * relative to its parent source.
   *
   * @param number atOffset
   *        The offset relative to the parent source.
   * @return object
   *         The offset and length relative to the enclosing script.
   */
  getScriptInfo(atOffset) {
    let info = { start: -1, length: -1, index: -1 };

    for (let { offset, length } of this._trees) {
      info.index++;
      if (offset <= atOffset && offset + length >= atOffset) {
        info.start = offset;
        info.length = length;
        return info;
      }
    }

    info.index = -1;
    return info;
  },

  /**
   * Handles a request for a specific or all known syntax trees.
   *
   * @param string functionName
   *        The function name to call on the SyntaxTree instances.
   * @param number syntaxTreeIndex
   *        The syntax tree for which to handle the request. If the tree at
   *        the specified index isn't found, the accumulated results for all
   *        syntax trees are returned.
   * @param any params
   *        Any kind params to pass to the request function.
   * @return array
   *         The results given by all known syntax trees.
   */
  _call(functionName, syntaxTreeIndex, ...params) {
    let results = [];
    let requestId = [functionName, syntaxTreeIndex, params].toSource();

    if (this._cache.has(requestId)) {
      return this._cache.get(requestId);
    }

    let requestedTree = this._trees[syntaxTreeIndex];
    let targettedTrees = requestedTree ? [requestedTree] : this._trees;

    for (let syntaxTree of targettedTrees) {
      try {
        let parseResults = syntaxTree[functionName].apply(syntaxTree, params);
        if (parseResults) {
          parseResults.sourceUrl = syntaxTree.url;
          parseResults.scriptLength = syntaxTree.length;
          parseResults.scriptOffset = syntaxTree.offset;
          results.push(parseResults);
        }
      } catch (e) {
        // Can't guarantee that the tree traversal logic is forever perfect :)
        // Language features may be added, in which case the recursive methods
        // need to be updated. If an exception is thrown here, file a bug.
        DevToolsUtils.reportException(
          `Syntax tree visitor for ${this._url}`, e);
      }
    }
    this._cache.set(requestId, results);
    return results;
  },

  _trees: null,
  _cache: null
};

/**
 * A collection of AST nodes generated by the reflection API.
 *
 * @param object nodes
 *        The AST nodes.
 * @param string url
 *        The source url.
 * @param number length
 *        The total number of chars of the parsed script in the parent source.
 * @param number offset [optional]
 *        The char offset of the parsed script in the parent source.
 */
function SyntaxTree(nodes, url, length, offset = 0) {
  this.AST = nodes;
  this.url = url;
  this.length = length;
  this.offset = offset;
}

SyntaxTree.prototype = {
  /**
   * Gets the identifier at the specified location.
   *
   * @param number line
   *        The line in the source.
   * @param number column
   *        The column in the source.
   * @param boolean ignoreLiterals
   *        Specifies if alone literals should be ignored.
   * @return object
   *         An object containing identifier information as { name, location,
   *         evalString } properties, or null if nothing is found.
   */
  getIdentifierAt(line, column, ignoreLiterals) {
    let info = null;

    SyntaxTreeVisitor.walk(this.AST, {
      /**
       * Callback invoked for each identifier node.
       * @param Node node
       */
      onIdentifier(node) {
        if (ParserHelpers.nodeContainsPoint(node, line, column)) {
          info = {
            name: node.name,
            location: ParserHelpers.getNodeLocation(node),
            evalString: ParserHelpers.getIdentifierEvalString(node)
          };

          // Abruptly halt walking the syntax tree.
          SyntaxTreeVisitor.break = true;
        }
      },

      /**
       * Callback invoked for each literal node.
       * @param Node node
       */
      onLiteral(node) {
        if (!ignoreLiterals) {
          this.onIdentifier(node);
        }
      },

      /**
       * Callback invoked for each 'this' node.
       * @param Node node
       */
      onThisExpression(node) {
        this.onIdentifier(node);
      }
    });

    return info;
  },

  /**
   * Searches for all function definitions (declarations and expressions)
   * whose names (or inferred names) contain a string.
   *
   * @param string substring
   *        The string to be contained in the function name (or inferred name).
   *        Can be an empty string to match all functions.
   * @return array
   *         All the matching function declarations and expressions, as
   *         { functionName, functionLocation ... } object hashes.
   */
  getNamedFunctionDefinitions(substring) {
    let lowerCaseToken = substring.toLowerCase();
    let store = [];

    function includesToken(name) {
      return name && name.toLowerCase().includes(lowerCaseToken);
    }

    SyntaxTreeVisitor.walk(this.AST, {
      /**
       * Callback invoked for each function declaration node.
       * @param Node node
       */
      onFunctionDeclaration(node) {
        let functionName = node.id.name;
        if (includesToken(functionName)) {
          store.push({
            functionName: functionName,
            functionLocation: ParserHelpers.getNodeLocation(node)
          });
        }
      },

      /**
       * Callback invoked for each function expression node.
       * @param Node node
       */
      onFunctionExpression(node) {
        // Function expressions don't necessarily have a name.
        let functionName = node.id ? node.id.name : "";
        let functionLocation = ParserHelpers.getNodeLocation(node);

        // Infer the function's name from an enclosing syntax tree node.
        let inferredInfo = ParserHelpers.inferFunctionExpressionInfo(node);
        let inferredName = inferredInfo.name;
        let inferredChain = inferredInfo.chain;
        let inferredLocation = inferredInfo.loc;

        // Current node may be part of a larger assignment expression stack.
        if (node._parent.type == "AssignmentExpression") {
          this.onFunctionExpression(node._parent);
        }

        if (includesToken(functionName) || includesToken(inferredName)) {
          store.push({
            functionName: functionName,
            functionLocation: functionLocation,
            inferredName: inferredName,
            inferredChain: inferredChain,
            inferredLocation: inferredLocation
          });
        }
      },

      /**
       * Callback invoked for each arrow expression node.
       * @param Node node
       */
      onArrowFunctionExpression(node) {
        // Infer the function's name from an enclosing syntax tree node.
        let inferredInfo = ParserHelpers.inferFunctionExpressionInfo(node);
        let inferredName = inferredInfo.name;
        let inferredChain = inferredInfo.chain;
        let inferredLocation = inferredInfo.loc;

        // Current node may be part of a larger assignment expression stack.
        if (node._parent.type == "AssignmentExpression") {
          this.onFunctionExpression(node._parent);
        }

        if (includesToken(inferredName)) {
          store.push({
            inferredName: inferredName,
            inferredChain: inferredChain,
            inferredLocation: inferredLocation
          });
        }
      }
    });

    return store;
  },

  AST: null,
  url: "",
  length: 0,
  offset: 0
};

/**
 * Parser utility methods.
 */
var ParserHelpers = {
  /**
   * Gets the location information for a node. Not all nodes have a
   * location property directly attached, or the location information
   * is incorrect, in which cases it's accessible via the parent.
   *
   * @param Node node
   *        The node who's location needs to be retrieved.
   * @return object
   *         An object containing { line, column } information.
   */
  getNodeLocation(node) {
    if (node.type != "Identifier") {
      return node.loc;
    }
    // Work around the fact that some identifier nodes don't have the
    // correct location attached.
    let { loc: parentLocation, type: parentType } = node._parent;
    let { loc: nodeLocation } = node;
    if (!nodeLocation) {
      if (parentType == "FunctionDeclaration" ||
          parentType == "FunctionExpression") {
        // e.g. "function foo() {}" or "{ bar: function foo() {} }"
        // The location is unavailable for the identifier node "foo".
        let loc = Cu.cloneInto(parentLocation, {});
        loc.end.line = loc.start.line;
        loc.end.column = loc.start.column + node.name.length;
        return loc;
      }
      if (parentType == "MemberExpression") {
        // e.g. "foo.bar"
        // The location is unavailable for the identifier node "bar".
        let loc = Cu.cloneInto(parentLocation, {});
        loc.start.line = loc.end.line;
        loc.start.column = loc.end.column - node.name.length;
        return loc;
      }
      if (parentType == "LabeledStatement") {
        // e.g. label: ...
        // The location is unavailable for the identifier node "label".
        let loc = Cu.cloneInto(parentLocation, {});
        loc.end.line = loc.start.line;
        loc.end.column = loc.start.column + node.name.length;
        return loc;
      }
      if (parentType == "ContinueStatement" || parentType == "BreakStatement") {
        // e.g. continue label; or break label;
        // The location is unavailable for the identifier node "label".
        let loc = Cu.cloneInto(parentLocation, {});
        loc.start.line = loc.end.line;
        loc.start.column = loc.end.column - node.name.length;
        return loc;
      }
    } else if (parentType == "VariableDeclarator") {
      // e.g. "let foo = 42"
      // The location incorrectly spans across the whole variable declaration,
      // not just the identifier node "foo".
      let loc = Cu.cloneInto(nodeLocation, {});
      loc.end.line = loc.start.line;
      loc.end.column = loc.start.column + node.name.length;
      return loc;
    }
    return node.loc;
  },

  /**
   * Checks if a node's bounds contains a specified line.
   *
   * @param Node node
   *        The node's bounds used as reference.
   * @param number line
   *        The line number to check.
   * @return boolean
   *         True if the line and column is contained in the node's bounds.
   */
  nodeContainsLine(node, line) {
    let { start: s, end: e } = this.getNodeLocation(node);
    return s.line <= line && e.line >= line;
  },

  /**
   * Checks if a node's bounds contains a specified line and column.
   *
   * @param Node node
   *        The node's bounds used as reference.
   * @param number line
   *        The line number to check.
   * @param number column
   *        The column number to check.
   * @return boolean
   *         True if the line and column is contained in the node's bounds.
   */
  nodeContainsPoint(node, line, column) {
    let { start: s, end: e } = this.getNodeLocation(node);
    return s.line == line && e.line == line &&
           s.column <= column && e.column >= column;
  },

  /**
   * Try to infer a function expression's name & other details based on the
   * enclosing VariableDeclarator, AssignmentExpression or ObjectExpression.
   *
   * @param Node node
   *        The function expression node to get the name for.
   * @return object
   *         The inferred function name, or empty string can't infer the name,
   *         along with the chain (a generic "context", like a prototype chain)
   *         and location if available.
   */
  inferFunctionExpressionInfo(node) {
    let parent = node._parent;

    // A function expression may be defined in a variable declarator,
    // e.g. var foo = function(){}, in which case it is possible to infer
    // the variable name.
    if (parent.type == "VariableDeclarator") {
      return {
        name: parent.id.name,
        chain: null,
        loc: this.getNodeLocation(parent.id)
      };
    }

    // Function expressions can also be defined in assignment expressions,
    // e.g. foo = function(){} or foo.bar = function(){}, in which case it is
    // possible to infer the assignee name ("foo" and "bar" respectively).
    if (parent.type == "AssignmentExpression") {
      let propertyChain = this._getMemberExpressionPropertyChain(parent.left);
      let propertyLeaf = propertyChain.pop();
      return {
        name: propertyLeaf,
        chain: propertyChain,
        loc: this.getNodeLocation(parent.left)
      };
    }

    // If a function expression is defined in an object expression,
    // e.g. { foo: function(){} }, then it is possible to infer the name
    // from the corresponding property.
    if (parent.type == "ObjectExpression") {
      let propertyKey = this._getObjectExpressionPropertyKeyForValue(node);
      let propertyChain = this._getObjectExpressionPropertyChain(parent);
      let propertyLeaf = propertyKey.name;
      return {
        name: propertyLeaf,
        chain: propertyChain,
        loc: this.getNodeLocation(propertyKey)
      };
    }

    // Can't infer the function expression's name.
    return {
      name: "",
      chain: null,
      loc: null
    };
  },

  /**
   * Gets the name of an object expression's property to which a specified
   * value is assigned.
   *
   * Used for inferring function expression information and retrieving
   * an identifier evaluation string.
   *
   * For example, if "node" represents the "bar" identifier in a hypothetical
   * "{ foo: bar }" object expression, the returned node is the "foo"
   * identifier.
   *
   * @param Node node
   *        The value node in an object expression.
   * @return object
   *         The key identifier node in the object expression.
   */
  _getObjectExpressionPropertyKeyForValue(node) {
    let parent = node._parent;
    if (parent.type != "ObjectExpression") {
      return null;
    }
    for (let property of parent.properties) {
      if (property.value == node) {
        return property.key;
      }
    }
    return null;
  },

  /**
   * Gets an object expression's property chain to its parent
   * variable declarator or assignment expression, if available.
   *
   * Used for inferring function expression information and retrieving
   * an identifier evaluation string.
   *
   * For example, if node represents the "baz: {}" object expression in a
   * hypothetical "foo = { bar: { baz: {} } }" assignment expression, the
   * returned chain is ["foo", "bar", "baz"].
   *
   * @param Node node
   *        The object expression node to begin the scan from.
   * @param array aStore [optional]
   *        The chain to store the nodes into.
   * @return array
   *         The chain to the parent variable declarator, as strings.
   */
  _getObjectExpressionPropertyChain(node, aStore = []) {
    switch (node.type) {
      case "ObjectExpression":
        this._getObjectExpressionPropertyChain(node._parent, aStore);
        let propertyKey = this._getObjectExpressionPropertyKeyForValue(node);
        if (propertyKey) {
          aStore.push(propertyKey.name);
        }
        break;
      // Handle "var foo = { ... }" variable declarators.
      case "VariableDeclarator":
        aStore.push(node.id.name);
        break;
      // Handle "foo.bar = { ... }" assignment expressions, since they're
      // commonly used when defining an object's prototype methods; e.g:
      // "Foo.prototype = { ... }".
      case "AssignmentExpression":
        this._getMemberExpressionPropertyChain(node.left, aStore);
        break;
      // Additionally handle stuff like "foo = bar.baz({ ... })", because it's
      // commonly used in prototype-based inheritance in many libraries; e.g:
      // "Foo = Bar.extend({ ... })".
      case "NewExpression":
      case "CallExpression":
        this._getObjectExpressionPropertyChain(node._parent, aStore);
        break;
    }
    return aStore;
  },

  /**
   * Gets a member expression's property chain.
   *
   * Used for inferring function expression information and retrieving
   * an identifier evaluation string.
   *
   * For example, if node represents a hypothetical "foo.bar.baz"
   * member expression, the returned chain ["foo", "bar", "baz"].
   *
   * More complex expressions like foo.bar().baz are intentionally not handled.
   *
   * @param Node node
   *        The member expression node to begin the scan from.
   * @param array store [optional]
   *        The chain to store the nodes into.
   * @return array
   *         The full member chain, as strings.
   */
  _getMemberExpressionPropertyChain(node, store = []) {
    switch (node.type) {
      case "MemberExpression":
        this._getMemberExpressionPropertyChain(node.object, store);
        this._getMemberExpressionPropertyChain(node.property, store);
        break;
      case "ThisExpression":
        store.push("this");
        break;
      case "Identifier":
        store.push(node.name);
        break;
    }
    return store;
  },

  /**
   * Returns an evaluation string which can be used to obtain the
   * current value for the respective identifier.
   *
   * @param Node node
   *        The leaf node (e.g. Identifier, Literal) to begin the scan from.
   * @return string
   *         The corresponding evaluation string, or empty string if
   *         the specified leaf node can't be used.
   */
  getIdentifierEvalString(node) {
    switch (node._parent.type) {
      case "ObjectExpression":
        // If the identifier is the actual property value, it can be used
        // directly as an evaluation string. Otherwise, construct the property
        // access chain, since the value might have changed.
        if (!this._getObjectExpressionPropertyKeyForValue(node)) {
          let propertyChain =
            this._getObjectExpressionPropertyChain(node._parent);
          let propertyLeaf = node.name;
          return [...propertyChain, propertyLeaf].join(".");
        }
        break;
      case "MemberExpression":
        // Make sure this is a property identifier, not the parent object.
        if (node._parent.property == node) {
          return this._getMemberExpressionPropertyChain(node._parent).join(".");
        }
        break;
    }
    switch (node.type) {
      case "ThisExpression":
        return "this";
      case "Identifier":
        return node.name;
      case "Literal":
        return uneval(node.value);
      default:
        return "";
    }
  }
};

/**
 * A visitor for a syntax tree generated by the reflection API.
 * See https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API.
 *
 * All node types implement the following interface:
 * interface Node {
 *   type: string;
 *   loc: SourceLocation | null;
 * }
 */
var SyntaxTreeVisitor = {
  /**
   * Walks a syntax tree.
   *
   * @param object tree
   *        The AST nodes generated by the reflection API
   * @param object callbacks
   *        A map of all the callbacks to invoke when passing through certain
   *        types of noes (e.g: onFunctionDeclaration, onBlockStatement etc.).
   */
  walk(tree, callbacks) {
    this.break = false;
    this[tree.type](tree, callbacks);
  },

  /**
   * Filters all the nodes in this syntax tree based on a predicate.
   *
   * @param object tree
   *        The AST nodes generated by the reflection API
   * @param function predicate
   *        The predicate ran on each node.
   * @return array
   *         An array of nodes validating the predicate.
   */
  filter(tree, predicate) {
    let store = [];
    this.walk(tree, {
      onNode: e => {
        if (predicate(e)) {
          store.push(e);
        }
      }
    });
    return store;
  },

  /**
   * A flag checked on each node in the syntax tree. If true, walking is
   * abruptly halted.
   */
  break: false,

  /**
   * A complete program source tree.
   *
   * interface Program <: Node {
   *   type: "Program";
   *   body: [ Statement ];
   * }
   */
  Program(node, callbacks) {
    if (callbacks.onProgram) {
      callbacks.onProgram(node);
    }
    for (let statement of node.body) {
      this[statement.type](statement, node, callbacks);
    }
  },

  /**
   * Any statement.
   *
   * interface Statement <: Node { }
   */
  Statement(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onStatement) {
      callbacks.onStatement(node);
    }
  },

  /**
   * An empty statement, i.e., a solitary semicolon.
   *
   * interface EmptyStatement <: Statement {
   *   type: "EmptyStatement";
   * }
   */
  EmptyStatement(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onEmptyStatement) {
      callbacks.onEmptyStatement(node);
    }
  },

  /**
   * A block statement, i.e., a sequence of statements surrounded by braces.
   *
   * interface BlockStatement <: Statement {
   *   type: "BlockStatement";
   *   body: [ Statement ];
   * }
   */
  BlockStatement(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onBlockStatement) {
      callbacks.onBlockStatement(node);
    }
    for (let statement of node.body) {
      this[statement.type](statement, node, callbacks);
    }
  },

  /**
   * An expression statement, i.e., a statement consisting of a single
   * expression.
   *
   * interface ExpressionStatement <: Statement {
   *   type: "ExpressionStatement";
   *   expression: Expression;
   * }
   */
  ExpressionStatement(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onExpressionStatement) {
      callbacks.onExpressionStatement(node);
    }
    this[node.expression.type](node.expression, node, callbacks);
  },

  /**
   * An if statement.
   *
   * interface IfStatement <: Statement {
   *   type: "IfStatement";
   *   test: Expression;
   *   consequent: Statement;
   *   alternate: Statement | null;
   * }
   */
  IfStatement(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onIfStatement) {
      callbacks.onIfStatement(node);
    }
    this[node.test.type](node.test, node, callbacks);
    this[node.consequent.type](node.consequent, node, callbacks);
    if (node.alternate) {
      this[node.alternate.type](node.alternate, node, callbacks);
    }
  },

  /**
   * A labeled statement, i.e., a statement prefixed by a break/continue label.
   *
   * interface LabeledStatement <: Statement {
   *   type: "LabeledStatement";
   *   label: Identifier;
   *   body: Statement;
   * }
   */
  LabeledStatement(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onLabeledStatement) {
      callbacks.onLabeledStatement(node);
    }
    this[node.label.type](node.label, node, callbacks);
    this[node.body.type](node.body, node, callbacks);
  },

  /**
   * A break statement.
   *
   * interface BreakStatement <: Statement {
   *   type: "BreakStatement";
   *   label: Identifier | null;
   * }
   */
  BreakStatement(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onBreakStatement) {
      callbacks.onBreakStatement(node);
    }
    if (node.label) {
      this[node.label.type](node.label, node, callbacks);
    }
  },

  /**
   * A continue statement.
   *
   * interface ContinueStatement <: Statement {
   *   type: "ContinueStatement";
   *   label: Identifier | null;
   * }
   */
  ContinueStatement(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onContinueStatement) {
      callbacks.onContinueStatement(node);
    }
    if (node.label) {
      this[node.label.type](node.label, node, callbacks);
    }
  },

  /**
   * A with statement.
   *
   * interface WithStatement <: Statement {
   *   type: "WithStatement";
   *   object: Expression;
   *   body: Statement;
   * }
   */
  WithStatement(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onWithStatement) {
      callbacks.onWithStatement(node);
    }
    this[node.object.type](node.object, node, callbacks);
    this[node.body.type](node.body, node, callbacks);
  },

  /**
   * A switch statement. The lexical flag is metadata indicating whether the
   * switch statement contains any unnested let declarations (and therefore
   * introduces a new lexical scope).
   *
   * interface SwitchStatement <: Statement {
   *   type: "SwitchStatement";
   *   discriminant: Expression;
   *   cases: [ SwitchCase ];
   *   lexical: boolean;
   * }
   */
  SwitchStatement(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onSwitchStatement) {
      callbacks.onSwitchStatement(node);
    }
    this[node.discriminant.type](node.discriminant, node, callbacks);
    for (let _case of node.cases) {
      this[_case.type](_case, node, callbacks);
    }
  },

  /**
   * A return statement.
   *
   * interface ReturnStatement <: Statement {
   *   type: "ReturnStatement";
   *   argument: Expression | null;
   * }
   */
  ReturnStatement(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onReturnStatement) {
      callbacks.onReturnStatement(node);
    }
    if (node.argument) {
      this[node.argument.type](node.argument, node, callbacks);
    }
  },

  /**
   * A throw statement.
   *
   * interface ThrowStatement <: Statement {
   *   type: "ThrowStatement";
   *   argument: Expression;
   * }
   */
  ThrowStatement(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onThrowStatement) {
      callbacks.onThrowStatement(node);
    }
    this[node.argument.type](node.argument, node, callbacks);
  },

  /**
   * A try statement.
   *
   * interface TryStatement <: Statement {
   *   type: "TryStatement";
   *   block: BlockStatement;
   *   handler: CatchClause | null;
   *   finalizer: BlockStatement | null;
   * }
   */
  TryStatement(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onTryStatement) {
      callbacks.onTryStatement(node);
    }
    this[node.block.type](node.block, node, callbacks);
    if (node.handler) {
      this[node.handler.type](node.handler, node, callbacks);
    }
    if (node.finalizer) {
      this[node.finalizer.type](node.finalizer, node, callbacks);
    }
  },

  /**
   * A while statement.
   *
   * interface WhileStatement <: Statement {
   *   type: "WhileStatement";
   *   test: Expression;
   *   body: Statement;
   * }
   */
  WhileStatement(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onWhileStatement) {
      callbacks.onWhileStatement(node);
    }
    this[node.test.type](node.test, node, callbacks);
    this[node.body.type](node.body, node, callbacks);
  },

  /**
   * A do/while statement.
   *
   * interface DoWhileStatement <: Statement {
   *   type: "DoWhileStatement";
   *   body: Statement;
   *   test: Expression;
   * }
   */
  DoWhileStatement(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onDoWhileStatement) {
      callbacks.onDoWhileStatement(node);
    }
    this[node.body.type](node.body, node, callbacks);
    this[node.test.type](node.test, node, callbacks);
  },

  /**
   * A for statement.
   *
   * interface ForStatement <: Statement {
   *   type: "ForStatement";
   *   init: VariableDeclaration | Expression | null;
   *   test: Expression | null;
   *   update: Expression | null;
   *   body: Statement;
   * }
   */
  ForStatement(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onForStatement) {
      callbacks.onForStatement(node);
    }
    if (node.init) {
      this[node.init.type](node.init, node, callbacks);
    }
    if (node.test) {
      this[node.test.type](node.test, node, callbacks);
    }
    if (node.update) {
      this[node.update.type](node.update, node, callbacks);
    }
    this[node.body.type](node.body, node, callbacks);
  },

  /**
   * A for/in statement, or, if each is true, a for each/in statement.
   *
   * interface ForInStatement <: Statement {
   *   type: "ForInStatement";
   *   left: VariableDeclaration | Expression;
   *   right: Expression;
   *   body: Statement;
   *   each: boolean;
   * }
   */
  ForInStatement(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onForInStatement) {
      callbacks.onForInStatement(node);
    }
    this[node.left.type](node.left, node, callbacks);
    this[node.right.type](node.right, node, callbacks);
    this[node.body.type](node.body, node, callbacks);
  },

  /**
   * A for/of statement.
   *
   * interface ForOfStatement <: Statement {
   *   type: "ForOfStatement";
   *   left: VariableDeclaration | Expression;
   *   right: Expression;
   *   body: Statement;
   * }
   */
  ForOfStatement(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onForOfStatement) {
      callbacks.onForOfStatement(node);
    }
    this[node.left.type](node.left, node, callbacks);
    this[node.right.type](node.right, node, callbacks);
    this[node.body.type](node.body, node, callbacks);
  },

  /**
   * A let statement.
   *
   * interface LetStatement <: Statement {
   *   type: "LetStatement";
   *   head: [ { id: Pattern, init: Expression | null } ];
   *   body: Statement;
   * }
   */
  LetStatement(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onLetStatement) {
      callbacks.onLetStatement(node);
    }
    for (let { id, init } of node.head) {
      this[id.type](id, node, callbacks);
      if (init) {
        this[init.type](init, node, callbacks);
      }
    }
    this[node.body.type](node.body, node, callbacks);
  },

  /**
   * A debugger statement.
   *
   * interface DebuggerStatement <: Statement {
   *   type: "DebuggerStatement";
   * }
   */
  DebuggerStatement(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onDebuggerStatement) {
      callbacks.onDebuggerStatement(node);
    }
  },

  /**
   * Any declaration node. Note that declarations are considered statements;
   * this is because declarations can appear in any statement context in the
   * language recognized by the SpiderMonkey parser.
   *
   * interface Declaration <: Statement { }
   */
  Declaration(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onDeclaration) {
      callbacks.onDeclaration(node);
    }
  },

  /**
   * A function declaration.
   *
   * interface FunctionDeclaration <: Function, Declaration {
   *   type: "FunctionDeclaration";
   *   id: Identifier;
   *   params: [ Pattern ];
   *   defaults: [ Expression ];
   *   rest: Identifier | null;
   *   body: BlockStatement | Expression;
   *   generator: boolean;
   *   expression: boolean;
   * }
   */
  FunctionDeclaration(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onFunctionDeclaration) {
      callbacks.onFunctionDeclaration(node);
    }
    this[node.id.type](node.id, node, callbacks);
    for (let param of node.params) {
      this[param.type](param, node, callbacks);
    }
    for (let _default of node.defaults) {
      if (_default) {
        this[_default.type](_default, node, callbacks);
      }
    }
    if (node.rest) {
      this[node.rest.type](node.rest, node, callbacks);
    }
    this[node.body.type](node.body, node, callbacks);
  },

  /**
   * A variable declaration, via one of var, let, or const.
   *
   * interface VariableDeclaration <: Declaration {
   *   type: "VariableDeclaration";
   *   declarations: [ VariableDeclarator ];
   *   kind: "var" | "let" | "const";
   * }
   */
  VariableDeclaration(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onVariableDeclaration) {
      callbacks.onVariableDeclaration(node);
    }
    for (let declaration of node.declarations) {
      this[declaration.type](declaration, node, callbacks);
    }
  },

  /**
   * A variable declarator.
   *
   * interface VariableDeclarator <: Node {
   *   type: "VariableDeclarator";
   *   id: Pattern;
   *   init: Expression | null;
   * }
   */
  VariableDeclarator(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onVariableDeclarator) {
      callbacks.onVariableDeclarator(node);
    }
    this[node.id.type](node.id, node, callbacks);
    if (node.init) {
      this[node.init.type](node.init, node, callbacks);
    }
  },

  /**
   * Any expression node. Since the left-hand side of an assignment may be any
   * expression in general, an expression can also be a pattern.
   *
   * interface Expression <: Node, Pattern { }
   */
  Expression(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onExpression) {
      callbacks.onExpression(node);
    }
  },

  /**
   * A this expression.
   *
   * interface ThisExpression <: Expression {
   *   type: "ThisExpression";
   * }
   */
  ThisExpression(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onThisExpression) {
      callbacks.onThisExpression(node);
    }
  },

  /**
   * An array expression.
   *
   * interface ArrayExpression <: Expression {
   *   type: "ArrayExpression";
   *   elements: [ Expression | null ];
   * }
   */
  ArrayExpression(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onArrayExpression) {
      callbacks.onArrayExpression(node);
    }
    for (let element of node.elements) {
      if (element) {
        this[element.type](element, node, callbacks);
      }
    }
  },

  /**
   * A spread expression.
   *
   * interface SpreadExpression <: Expression {
   *   type: "SpreadExpression";
   *   expression: Expression;
   * }
   */
  SpreadExpression(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onSpreadExpression) {
      callbacks.onSpreadExpression(node);
    }
    this[node.expression.type](node.expression, node, callbacks);
  },

  /**
   * An object expression. A literal property in an object expression can have
   * either a string or number as its value. Ordinary property initializers
   * have a kind value "init"; getters and setters have the kind values "get"
   * and "set", respectively.
   *
   * interface ObjectExpression <: Expression {
   *   type: "ObjectExpression";
   *   properties: [ { key: Literal | Identifier | ComputedName,
   *                   value: Expression,
   *                   kind: "init" | "get" | "set" } ];
   * }
   */
  ObjectExpression(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onObjectExpression) {
      callbacks.onObjectExpression(node);
    }
    for (let { key, value } of node.properties) {
      this[key.type](key, node, callbacks);
      this[value.type](value, node, callbacks);
    }
  },

  /**
   * A computed property name in object expression, like in { [a]: b }
   *
   * interface ComputedName <: Node {
   *   type: "ComputedName";
   *   name: Expression;
   * }
   */
  ComputedName(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onComputedName) {
      callbacks.onComputedName(node);
    }
    this[node.name.type](node.name, node, callbacks);
  },

  /**
   * A function expression.
   *
   * interface FunctionExpression <: Function, Expression {
   *   type: "FunctionExpression";
   *   id: Identifier | null;
   *   params: [ Pattern ];
   *   defaults: [ Expression ];
   *   rest: Identifier | null;
   *   body: BlockStatement | Expression;
   *   generator: boolean;
   *   expression: boolean;
   * }
   */
  FunctionExpression(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onFunctionExpression) {
      callbacks.onFunctionExpression(node);
    }
    if (node.id) {
      this[node.id.type](node.id, node, callbacks);
    }
    for (let param of node.params) {
      this[param.type](param, node, callbacks);
    }
    for (let _default of node.defaults) {
      if (_default) {
        this[_default.type](_default, node, callbacks);
      }
    }
    if (node.rest) {
      this[node.rest.type](node.rest, node, callbacks);
    }
    this[node.body.type](node.body, node, callbacks);
  },

  /**
   * An arrow expression.
   *
   * interface ArrowFunctionExpression <: Function, Expression {
   *   type: "ArrowFunctionExpression";
   *   params: [ Pattern ];
   *   defaults: [ Expression ];
   *   rest: Identifier | null;
   *   body: BlockStatement | Expression;
   *   generator: boolean;
   *   expression: boolean;
   * }
   */
  ArrowFunctionExpression(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onArrowFunctionExpression) {
      callbacks.onArrowFunctionExpression(node);
    }
    for (let param of node.params) {
      this[param.type](param, node, callbacks);
    }
    for (let _default of node.defaults) {
      if (_default) {
        this[_default.type](_default, node, callbacks);
      }
    }
    if (node.rest) {
      this[node.rest.type](node.rest, node, callbacks);
    }
    this[node.body.type](node.body, node, callbacks);
  },

  /**
   * A sequence expression, i.e., a comma-separated sequence of expressions.
   *
   * interface SequenceExpression <: Expression {
   *   type: "SequenceExpression";
   *   expressions: [ Expression ];
   * }
   */
  SequenceExpression(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onSequenceExpression) {
      callbacks.onSequenceExpression(node);
    }
    for (let expression of node.expressions) {
      this[expression.type](expression, node, callbacks);
    }
  },

  /**
   * A unary operator expression.
   *
   * interface UnaryExpression <: Expression {
   *   type: "UnaryExpression";
   *   operator: UnaryOperator;
   *   prefix: boolean;
   *   argument: Expression;
   * }
   */
  UnaryExpression(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onUnaryExpression) {
      callbacks.onUnaryExpression(node);
    }
    this[node.argument.type](node.argument, node, callbacks);
  },

  /**
   * A binary operator expression.
   *
   * interface BinaryExpression <: Expression {
   *   type: "BinaryExpression";
   *   operator: BinaryOperator;
   *   left: Expression;
   *   right: Expression;
   * }
   */
  BinaryExpression(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onBinaryExpression) {
      callbacks.onBinaryExpression(node);
    }
    this[node.left.type](node.left, node, callbacks);
    this[node.right.type](node.right, node, callbacks);
  },

  /**
   * An assignment operator expression.
   *
   * interface AssignmentExpression <: Expression {
   *   type: "AssignmentExpression";
   *   operator: AssignmentOperator;
   *   left: Expression;
   *   right: Expression;
   * }
   */
  AssignmentExpression(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onAssignmentExpression) {
      callbacks.onAssignmentExpression(node);
    }
    this[node.left.type](node.left, node, callbacks);
    this[node.right.type](node.right, node, callbacks);
  },

  /**
   * An update (increment or decrement) operator expression.
   *
   * interface UpdateExpression <: Expression {
   *   type: "UpdateExpression";
   *   operator: UpdateOperator;
   *   argument: Expression;
   *   prefix: boolean;
   * }
   */
  UpdateExpression(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onUpdateExpression) {
      callbacks.onUpdateExpression(node);
    }
    this[node.argument.type](node.argument, node, callbacks);
  },

  /**
   * A logical operator expression.
   *
   * interface LogicalExpression <: Expression {
   *   type: "LogicalExpression";
   *   operator: LogicalOperator;
   *   left: Expression;
   *   right: Expression;
   * }
   */
  LogicalExpression(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onLogicalExpression) {
      callbacks.onLogicalExpression(node);
    }
    this[node.left.type](node.left, node, callbacks);
    this[node.right.type](node.right, node, callbacks);
  },

  /**
   * A conditional expression, i.e., a ternary ?/: expression.
   *
   * interface ConditionalExpression <: Expression {
   *   type: "ConditionalExpression";
   *   test: Expression;
   *   alternate: Expression;
   *   consequent: Expression;
   * }
   */
  ConditionalExpression(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onConditionalExpression) {
      callbacks.onConditionalExpression(node);
    }
    this[node.test.type](node.test, node, callbacks);
    this[node.alternate.type](node.alternate, node, callbacks);
    this[node.consequent.type](node.consequent, node, callbacks);
  },

  /**
   * A new expression.
   *
   * interface NewExpression <: Expression {
   *   type: "NewExpression";
   *   callee: Expression;
   *   arguments: [ Expression | null ];
   * }
   */
  NewExpression(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onNewExpression) {
      callbacks.onNewExpression(node);
    }
    this[node.callee.type](node.callee, node, callbacks);
    for (let argument of node.arguments) {
      if (argument) {
        this[argument.type](argument, node, callbacks);
      }
    }
  },

  /**
   * A function or method call expression.
   *
   * interface CallExpression <: Expression {
   *   type: "CallExpression";
   *   callee: Expression;
   *   arguments: [ Expression | null ];
   * }
   */
  CallExpression(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onCallExpression) {
      callbacks.onCallExpression(node);
    }
    this[node.callee.type](node.callee, node, callbacks);
    for (let argument of node.arguments) {
      if (argument) {
        if (!this[argument.type]) {
          console.error("Unknown parser object:", argument.type);
        }
        this[argument.type](argument, node, callbacks);
      }
    }
  },

  /**
   * A member expression. If computed is true, the node corresponds to a
   * computed e1[e2] expression and property is an Expression. If computed is
   * false, the node corresponds to a static e1.x expression and property is an
   * Identifier.
   *
   * interface MemberExpression <: Expression {
   *   type: "MemberExpression";
   *   object: Expression;
   *   property: Identifier | Expression;
   *   computed: boolean;
   * }
   */
  MemberExpression(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onMemberExpression) {
      callbacks.onMemberExpression(node);
    }
    this[node.object.type](node.object, node, callbacks);
    this[node.property.type](node.property, node, callbacks);
  },

  /**
   * A yield expression.
   *
   * interface YieldExpression <: Expression {
   *   argument: Expression | null;
   * }
   */
  YieldExpression(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onYieldExpression) {
      callbacks.onYieldExpression(node);
    }
    if (node.argument) {
      this[node.argument.type](node.argument, node, callbacks);
    }
  },

  /**
   * An array comprehension. The blocks array corresponds to the sequence of
   * for and for each blocks. The optional filter expression corresponds to the
   * final if clause, if present.
   *
   * interface ComprehensionExpression <: Expression {
   *   body: Expression;
   *   blocks: [ ComprehensionBlock ];
   *   filter: Expression | null;
   * }
   */
  ComprehensionExpression(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onComprehensionExpression) {
      callbacks.onComprehensionExpression(node);
    }
    this[node.body.type](node.body, node, callbacks);
    for (let block of node.blocks) {
      this[block.type](block, node, callbacks);
    }
    if (node.filter) {
      this[node.filter.type](node.filter, node, callbacks);
    }
  },

  /**
   * A generator expression. As with array comprehensions, the blocks array
   * corresponds to the sequence of for and for each blocks, and the optional
   * filter expression corresponds to the final if clause, if present.
   *
   * interface GeneratorExpression <: Expression {
   *   body: Expression;
   *   blocks: [ ComprehensionBlock ];
   *   filter: Expression | null;
   * }
   */
  GeneratorExpression(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onGeneratorExpression) {
      callbacks.onGeneratorExpression(node);
    }
    this[node.body.type](node.body, node, callbacks);
    for (let block of node.blocks) {
      this[block.type](block, node, callbacks);
    }
    if (node.filter) {
      this[node.filter.type](node.filter, node, callbacks);
    }
  },

  /**
   * A graph expression, aka "sharp literal," such as #1={ self: #1# }.
   *
   * interface GraphExpression <: Expression {
   *   index: uint32;
   *   expression: Literal;
   * }
   */
  GraphExpression(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onGraphExpression) {
      callbacks.onGraphExpression(node);
    }
    this[node.expression.type](node.expression, node, callbacks);
  },

  /**
   * A graph index expression, aka "sharp variable," such as #1#.
   *
   * interface GraphIndexExpression <: Expression {
   *   index: uint32;
   * }
   */
  GraphIndexExpression(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onGraphIndexExpression) {
      callbacks.onGraphIndexExpression(node);
    }
  },

  /**
   * A let expression.
   *
   * interface LetExpression <: Expression {
   *   type: "LetExpression";
   *   head: [ { id: Pattern, init: Expression | null } ];
   *   body: Expression;
   * }
   */
  LetExpression(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onLetExpression) {
      callbacks.onLetExpression(node);
    }
    for (let { id, init } of node.head) {
      this[id.type](id, node, callbacks);
      if (init) {
        this[init.type](init, node, callbacks);
      }
    }
    this[node.body.type](node.body, node, callbacks);
  },

  /**
   * Any pattern.
   *
   * interface Pattern <: Node { }
   */
  Pattern(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onPattern) {
      callbacks.onPattern(node);
    }
  },

  /**
   * An object-destructuring pattern. A literal property in an object pattern
   * can have either a string or number as its value.
   *
   * interface ObjectPattern <: Pattern {
   *   type: "ObjectPattern";
   *   properties: [ { key: Literal | Identifier, value: Pattern } ];
   * }
   */
  ObjectPattern(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onObjectPattern) {
      callbacks.onObjectPattern(node);
    }
    for (let { key, value } of node.properties) {
      this[key.type](key, node, callbacks);
      this[value.type](value, node, callbacks);
    }
  },

  /**
   * An array-destructuring pattern.
   *
   * interface ArrayPattern <: Pattern {
   *   type: "ArrayPattern";
   *   elements: [ Pattern | null ];
   * }
   */
  ArrayPattern(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onArrayPattern) {
      callbacks.onArrayPattern(node);
    }
    for (let element of node.elements) {
      if (element) {
        this[element.type](element, node, callbacks);
      }
    }
  },

  /**
   * A case (if test is an Expression) or default (if test is null) clause in
   * the body of a switch statement.
   *
   * interface SwitchCase <: Node {
   *   type: "SwitchCase";
   *   test: Expression | null;
   *   consequent: [ Statement ];
   * }
   */
  SwitchCase(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onSwitchCase) {
      callbacks.onSwitchCase(node);
    }
    if (node.test) {
      this[node.test.type](node.test, node, callbacks);
    }
    for (let consequent of node.consequent) {
      this[consequent.type](consequent, node, callbacks);
    }
  },

  /**
   * A catch clause following a try block. The optional guard property
   * corresponds to the optional expression guard on the bound variable.
   *
   * interface CatchClause <: Node {
   *   type: "CatchClause";
   *   param: Pattern;
   *   guard: Expression | null;
   *   body: BlockStatement;
   * }
   */
  CatchClause(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onCatchClause) {
      callbacks.onCatchClause(node);
    }
    this[node.param.type](node.param, node, callbacks);
    if (node.guard) {
      this[node.guard.type](node.guard, node, callbacks);
    }
    this[node.body.type](node.body, node, callbacks);
  },

  /**
   * A for or for each block in an array comprehension or generator expression.
   *
   * interface ComprehensionBlock <: Node {
   *   left: Pattern;
   *   right: Expression;
   *   each: boolean;
   * }
   */
  ComprehensionBlock(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onComprehensionBlock) {
      callbacks.onComprehensionBlock(node);
    }
    this[node.left.type](node.left, node, callbacks);
    this[node.right.type](node.right, node, callbacks);
  },

  /**
   * An identifier. Note that an identifier may be an expression or a
   * destructuring pattern.
   *
   * interface Identifier <: Node, Expression, Pattern {
   *   type: "Identifier";
   *   name: string;
   * }
   */
  Identifier(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onIdentifier) {
      callbacks.onIdentifier(node);
    }
  },

  /**
   * A literal token. Note that a literal can be an expression.
   *
   * interface Literal <: Node, Expression {
   *   type: "Literal";
   *   value: string | boolean | null | number | RegExp;
   * }
   */
  Literal(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onLiteral) {
      callbacks.onLiteral(node);
    }
  },

  /**
   * A template string literal.
   *
   * interface TemplateLiteral <: Node {
   *   type: "TemplateLiteral";
   *   elements: [ Expression ];
   * }
   */
  TemplateLiteral(node, parent, callbacks) {
    node._parent = parent;

    if (this.break) {
      return;
    }
    if (callbacks.onNode) {
      if (callbacks.onNode(node, parent) === false) {
        return;
      }
    }
    if (callbacks.onTemplateLiteral) {
      callbacks.onTemplateLiteral(node);
    }
    for (let element of node.elements) {
      if (element) {
        this[element.type](element, node, callbacks);
      }
    }
  }
};

XPCOMUtils.defineLazyGetter(Parser, "reflectionAPI", () => Reflect);
PK
!<�
Dh�&�&Bchrome/devtools/modules/devtools/shared/ThreadSafeDevToolsUtils.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

/**
 * General utilities used throughout devtools that can also be used in
 * workers.
 */

/**
 * Immutably reduce the given `...objs` into one object. The reduction is
 * applied from left to right, so `immutableUpdate({ a: 1 }, { a: 2 })` will
 * result in `{ a: 2 }`. The resulting object is frozen.
 *
 * Example usage:
 *
 *     const original = { foo: 1, bar: 2, baz: 3 };
 *     const modified = immutableUpdate(original, { baz: 0, bang: 4 });
 *
 *     // We get the new object that we expect...
 *     assert(modified.baz === 0);
 *     assert(modified.bang === 4);
 *
 *     // However, the original is not modified.
 *     assert(original.baz === 2);
 *     assert(original.bang === undefined);
 *
 * @param {...Object} ...objs
 * @returns {Object}
 */
exports.immutableUpdate = function (...objs) {
  return Object.freeze(Object.assign({}, ...objs));
};

/**
 * Utility function for updating an object with the properties of
 * other objects.
 *
 * DEPRECATED: Just use Object.assign() instead!
 *
 * @param aTarget Object
 *        The object being updated.
 * @param aNewAttrs Object
 *        The rest params are objects to update aTarget with. You
 *        can pass as many as you like.
 */
exports.update = function update(target, ...args) {
  for (let attrs of args) {
    for (let key in attrs) {
      let desc = Object.getOwnPropertyDescriptor(attrs, key);

      if (desc) {
        Object.defineProperty(target, key, desc);
      }
    }
  }
  return target;
};

/**
 * Utility function for getting the values from an object as an array
 *
 * @param object Object
 *        The object to iterate over
 */
exports.values = function values(object) {
  return Object.keys(object).map(k => object[k]);
};

/**
 * This is overridden in DevToolsUtils for the main thread, where we have the
 * Cu object available.
 */
exports.isCPOW = function () {
  return false;
};

/**
 * Report that |who| threw an exception, |exception|.
 */
exports.reportException = function reportException(who, exception) {
  const msg = `${who} threw an exception: ${exports.safeErrorString(exception)}`;
  dump(msg + "\n");

  if (typeof console !== "undefined" && console && console.error) {
    console.error(msg);
  }
};

/**
 * Given a handler function that may throw, return an infallible handler
 * function that calls the fallible handler, and logs any exceptions it
 * throws.
 *
 * @param handler function
 *      A handler function, which may throw.
 * @param aName string
 *      A name for handler, for use in error messages. If omitted, we use
 *      handler.name.
 *
 * (SpiderMonkey does generate good names for anonymous functions, but we
 * don't have a way to get at them from JavaScript at the moment.)
 */
exports.makeInfallible = function (handler, name = handler.name) {
  return function () {
    try {
      return handler.apply(this, arguments);
    } catch (ex) {
      let who = "Handler function";
      if (name) {
        who += " " + name;
      }
      exports.reportException(who, ex);
      return undefined;
    }
  };
};

/**
 * Turn the |error| into a string, without fail.
 *
 * @param {Error|any} error
 */
exports.safeErrorString = function (error) {
  try {
    let errorString = error.toString();
    if (typeof errorString == "string") {
      // Attempt to attach a stack to |errorString|. If it throws an error, or
      // isn't a string, don't use it.
      try {
        if (error.stack) {
          let stack = error.stack.toString();
          if (typeof stack == "string") {
            errorString += "\nStack: " + stack;
          }
        }
      } catch (ee) {
        // Ignore.
      }

      // Append additional line and column number information to the output,
      // since it might not be part of the stringified error.
      if (typeof error.lineNumber == "number" && typeof error.columnNumber == "number") {
        errorString += "Line: " + error.lineNumber + ", column: " + error.columnNumber;
      }

      return errorString;
    }
  } catch (ee) {
    // Ignore.
  }

  // We failed to find a good error description, so do the next best thing.
  return Object.prototype.toString.call(error);
};

/**
 * Interleaves two arrays element by element, returning the combined array, like
 * a zip. In the case of arrays with different sizes, undefined values will be
 * interleaved at the end along with the extra values of the larger array.
 *
 * @param Array a
 * @param Array b
 * @returns Array
 *          The combined array, in the form [a1, b1, a2, b2, ...]
 */
exports.zip = function (a, b) {
  if (!b) {
    return a;
  }
  if (!a) {
    return b;
  }
  const pairs = [];
  for (let i = 0, aLength = a.length, bLength = b.length;
       i < aLength || i < bLength;
       i++) {
    pairs.push([a[i], b[i]]);
  }
  return pairs;
};

/**
 * Converts an object into an array with 2-element arrays as key/value
 * pairs of the object. `{ foo: 1, bar: 2}` would become
 * `[[foo, 1], [bar 2]]` (order not guaranteed).
 *
 * @param object obj
 * @returns array
 */
exports.entries = function entries(obj) {
  return Object.keys(obj).map(k => [k, obj[k]]);
};

/*
 * Takes an array of 2-element arrays as key/values pairs and
 * constructs an object using them.
 */
exports.toObject = function (arr) {
  const obj = {};
  for (let [k, v] of arr) {
    obj[k] = v;
  }
  return obj;
};

/**
 * Composes the given functions into a single function, which will
 * apply the results of each function right-to-left, starting with
 * applying the given arguments to the right-most function.
 * `compose(foo, bar, baz)` === `args => foo(bar(baz(args)))`
 *
 * @param ...function funcs
 * @returns function
 */
exports.compose = function compose(...funcs) {
  return (...args) => {
    const initialValue = funcs[funcs.length - 1](...args);
    const leftFuncs = funcs.slice(0, -1);
    return leftFuncs.reduceRight((composed, f) => f(composed),
                                 initialValue);
  };
};

/**
 * Return true if `thing` is a generator function, false otherwise.
 */
exports.isGenerator = function (fn) {
  if (typeof fn !== "function") {
    return false;
  }
  let proto = Object.getPrototypeOf(fn);
  if (!proto) {
    return false;
  }
  let ctor = proto.constructor;
  if (!ctor) {
    return false;
  }
  return ctor.name == "GeneratorFunction";
};

/**
 * Return true if `thing` is an async function, false otherwise.
 */
exports.isAsyncFunction = function (fn) {
  if (typeof fn !== "function") {
    return false;
  }
  let proto = Object.getPrototypeOf(fn);
  if (!proto) {
    return false;
  }
  let ctor = proto.constructor;
  if (!ctor) {
    return false;
  }
  return ctor.name == "AsyncFunction";
};

/**
 * Return true if `thing` is a Promise or then-able, false otherwise.
 */
exports.isPromise = function (p) {
  return p && typeof p.then === "function";
};

/**
 * Return true if `thing` is a SavedFrame, false otherwise.
 */
exports.isSavedFrame = function (thing) {
  return Object.prototype.toString.call(thing) === "[object SavedFrame]";
};

/**
 * Return true iff `thing` is a `Set` object (possibly from another global).
 */
exports.isSet = function (thing) {
  return Object.prototype.toString.call(thing) === "[object Set]";
};

/**
 * Given a list of lists, flatten it. Only flattens one level; does not
 * recursively flatten all levels.
 *
 * @param {Array<Array<Any>>} lists
 * @return {Array<Any>}
 */
exports.flatten = function (lists) {
  return Array.prototype.concat.apply([], lists);
};

/**
 * Returns a promise that is resolved or rejected when all promises have settled
 * (resolved or rejected).
 *
 * This differs from Promise.all, which will reject immediately after the first
 * rejection, instead of waiting for the remaining promises to settle.
 *
 * @param values
 *        Iterable of promises that may be pending, resolved, or rejected. When
 *        when all promises have settled (resolved or rejected), the returned
 *        promise will be resolved or rejected as well.
 *
 * @return A new promise that is fulfilled when all values have settled
 *         (resolved or rejected). Its resolution value will be an array of all
 *         resolved values in the given order, or undefined if values is an
 *         empty array. The reject reason will be forwarded from the first
 *         promise in the list of given promises to be rejected.
 */
exports.settleAll = values => {
  if (values === null || typeof (values[Symbol.iterator]) != "function") {
    throw new Error("settleAll() expects an iterable.");
  }

  return new Promise((resolve, reject) => {
    values = Array.isArray(values) ? values : [...values];
    let countdown = values.length;
    let resolutionValues = new Array(countdown);
    let rejectionValue;
    let rejectionOccurred = false;

    if (!countdown) {
      resolve(resolutionValues);
      return;
    }

    function checkForCompletion() {
      if (--countdown > 0) {
        return;
      }
      if (!rejectionOccurred) {
        resolve(resolutionValues);
      } else {
        reject(rejectionValue);
      }
    }

    for (let i = 0; i < values.length; i++) {
      let index = i;
      let value = values[i];
      let resolver = result => {
        resolutionValues[index] = result;
        checkForCompletion();
      };
      let rejecter = error => {
        if (!rejectionOccurred) {
          rejectionValue = error;
          rejectionOccurred = true;
        }
        checkForCompletion();
      };

      if (value && typeof (value.then) == "function") {
        value.then(resolver, rejecter);
      } else {
        // Given value is not a promise, forward it as a resolution value.
        resolver(value);
      }
    }
  });
};
PK
!<z���
�
�6chrome/devtools/modules/devtools/shared/acorn/acorn.js(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.acorn = 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(_dereq_,module,exports){
// A recursive descent parser operates by defining functions for all
// syntactic elements, and recursively calling those, each function
// advancing the input stream and returning an AST node. Precedence
// of constructs (for example, the fact that `!x[1]` means `!(x[1])`
// instead of `(!x)[1]` is handled by the fact that the parser
// function that parses unary prefix operators is called first, and
// in turn calls the function that parses `[]` subscripts — that
// way, it'll receive the node for `x[1]` already parsed, and wraps
// *that* in the unary operator node.
//
// Acorn uses an [operator precedence parser][opp] to handle binary
// operator precedence, because it is much more compact than using
// the technique outlined above, which uses different, nesting
// functions to specify precedence, for all of the ten binary
// precedence levels that JavaScript defines.
//
// [opp]: http://en.wikipedia.org/wiki/Operator-precedence_parser

"use strict";

var _tokentype = _dereq_("./tokentype");

var _state = _dereq_("./state");

var pp = _state.Parser.prototype;

// Check if property name clashes with already added.
// Object/class getters and setters are not allowed to clash —
// either with each other or with an init property — and in
// strict mode, init properties are also not allowed to be repeated.

pp.checkPropClash = function (prop, propHash) {
  if (this.options.ecmaVersion >= 6 && (prop.computed || prop.method || prop.shorthand)) return;
  var key = prop.key;var name = undefined;
  switch (key.type) {
    case "Identifier":
      name = key.name;break;
    case "Literal":
      name = String(key.value);break;
    default:
      return;
  }
  var kind = prop.kind;

  if (this.options.ecmaVersion >= 6) {
    if (name === "__proto__" && kind === "init") {
      if (propHash.proto) this.raise(key.start, "Redefinition of __proto__ property");
      propHash.proto = true;
    }
    return;
  }
  name = "$" + name;
  var other = propHash[name];
  if (other) {
    var isGetSet = kind !== "init";
    if ((this.strict || isGetSet) && other[kind] || !(isGetSet ^ other.init)) this.raise(key.start, "Redefinition of property");
  } else {
    other = propHash[name] = {
      init: false,
      get: false,
      set: false
    };
  }
  other[kind] = true;
};

// ### Expression parsing

// These nest, from the most general expression type at the top to
// 'atomic', nondivisible expression types at the bottom. Most of
// the functions will simply let the function(s) below them parse,
// and, *if* the syntactic construct they handle is present, wrap
// the AST node that the inner parser gave them in another node.

// Parse a full expression. The optional arguments are used to
// forbid the `in` operator (in for loops initalization expressions)
// and provide reference for storing '=' operator inside shorthand
// property assignment in contexts where both object expression
// and object pattern might appear (so it's possible to raise
// delayed syntax error at correct position).

pp.parseExpression = function (noIn, refDestructuringErrors) {
  var startPos = this.start,
      startLoc = this.startLoc;
  var expr = this.parseMaybeAssign(noIn, refDestructuringErrors);
  if (this.type === _tokentype.types.comma) {
    var node = this.startNodeAt(startPos, startLoc);
    node.expressions = [expr];
    while (this.eat(_tokentype.types.comma)) node.expressions.push(this.parseMaybeAssign(noIn, refDestructuringErrors));
    return this.finishNode(node, "SequenceExpression");
  }
  return expr;
};

// Parse an assignment expression. This includes applications of
// operators like `+=`.

pp.parseMaybeAssign = function (noIn, refDestructuringErrors, afterLeftParse) {
  if (this.type == _tokentype.types._yield && this.inGenerator) return this.parseYield();

  var validateDestructuring = false;
  if (!refDestructuringErrors) {
    refDestructuringErrors = { shorthandAssign: 0, trailingComma: 0 };
    validateDestructuring = true;
  }
  var startPos = this.start,
      startLoc = this.startLoc;
  if (this.type == _tokentype.types.parenL || this.type == _tokentype.types.name) this.potentialArrowAt = this.start;
  var left = this.parseMaybeConditional(noIn, refDestructuringErrors);
  if (afterLeftParse) left = afterLeftParse.call(this, left, startPos, startLoc);
  if (this.type.isAssign) {
    if (validateDestructuring) this.checkPatternErrors(refDestructuringErrors, true);
    var node = this.startNodeAt(startPos, startLoc);
    node.operator = this.value;
    node.left = this.type === _tokentype.types.eq ? this.toAssignable(left) : left;
    refDestructuringErrors.shorthandAssign = 0; // reset because shorthand default was used correctly
    this.checkLVal(left);
    this.next();
    node.right = this.parseMaybeAssign(noIn);
    return this.finishNode(node, "AssignmentExpression");
  } else {
    if (validateDestructuring) this.checkExpressionErrors(refDestructuringErrors, true);
  }
  return left;
};

// Parse a ternary conditional (`?:`) operator.

pp.parseMaybeConditional = function (noIn, refDestructuringErrors) {
  var startPos = this.start,
      startLoc = this.startLoc;
  var expr = this.parseExprOps(noIn, refDestructuringErrors);
  if (this.checkExpressionErrors(refDestructuringErrors)) return expr;
  if (this.eat(_tokentype.types.question)) {
    var node = this.startNodeAt(startPos, startLoc);
    node.test = expr;
    node.consequent = this.parseMaybeAssign();
    this.expect(_tokentype.types.colon);
    node.alternate = this.parseMaybeAssign(noIn);
    return this.finishNode(node, "ConditionalExpression");
  }
  return expr;
};

// Start the precedence parser.

pp.parseExprOps = function (noIn, refDestructuringErrors) {
  var startPos = this.start,
      startLoc = this.startLoc;
  var expr = this.parseMaybeUnary(refDestructuringErrors);
  if (this.checkExpressionErrors(refDestructuringErrors)) return expr;
  return this.parseExprOp(expr, startPos, startLoc, -1, noIn);
};

// Parse binary operators with the operator precedence parsing
// algorithm. `left` is the left-hand side of the operator.
// `minPrec` provides context that allows the function to stop and
// defer further parser to one of its callers when it encounters an
// operator that has a lower precedence than the set it is parsing.

pp.parseExprOp = function (left, leftStartPos, leftStartLoc, minPrec, noIn) {
  var prec = this.type.binop;
  if (prec != null && (!noIn || this.type !== _tokentype.types._in)) {
    if (prec > minPrec) {
      var node = this.startNodeAt(leftStartPos, leftStartLoc);
      node.left = left;
      node.operator = this.value;
      var op = this.type;
      this.next();
      var startPos = this.start,
          startLoc = this.startLoc;
      node.right = this.parseExprOp(this.parseMaybeUnary(), startPos, startLoc, prec, noIn);
      this.finishNode(node, op === _tokentype.types.logicalOR || op === _tokentype.types.logicalAND ? "LogicalExpression" : "BinaryExpression");
      return this.parseExprOp(node, leftStartPos, leftStartLoc, minPrec, noIn);
    }
  }
  return left;
};

// Parse unary operators, both prefix and postfix.

pp.parseMaybeUnary = function (refDestructuringErrors) {
  if (this.type.prefix) {
    var node = this.startNode(),
        update = this.type === _tokentype.types.incDec;
    node.operator = this.value;
    node.prefix = true;
    this.next();
    node.argument = this.parseMaybeUnary();
    this.checkExpressionErrors(refDestructuringErrors, true);
    if (update) this.checkLVal(node.argument);else if (this.strict && node.operator === "delete" && node.argument.type === "Identifier") this.raise(node.start, "Deleting local variable in strict mode");
    return this.finishNode(node, update ? "UpdateExpression" : "UnaryExpression");
  }
  var startPos = this.start,
      startLoc = this.startLoc;
  var expr = this.parseExprSubscripts(refDestructuringErrors);
  if (this.checkExpressionErrors(refDestructuringErrors)) return expr;
  while (this.type.postfix && !this.canInsertSemicolon()) {
    var node = this.startNodeAt(startPos, startLoc);
    node.operator = this.value;
    node.prefix = false;
    node.argument = expr;
    this.checkLVal(expr);
    this.next();
    expr = this.finishNode(node, "UpdateExpression");
  }
  return expr;
};

// Parse call, dot, and `[]`-subscript expressions.

pp.parseExprSubscripts = function (refDestructuringErrors) {
  var startPos = this.start,
      startLoc = this.startLoc;
  var expr = this.parseExprAtom(refDestructuringErrors);
  var skipArrowSubscripts = expr.type === "ArrowFunctionExpression" && this.input.slice(this.lastTokStart, this.lastTokEnd) !== ")";
  if (this.checkExpressionErrors(refDestructuringErrors) || skipArrowSubscripts) return expr;
  return this.parseSubscripts(expr, startPos, startLoc);
};

pp.parseSubscripts = function (base, startPos, startLoc, noCalls) {
  for (;;) {
    if (this.eat(_tokentype.types.dot)) {
      var node = this.startNodeAt(startPos, startLoc);
      node.object = base;
      node.property = this.parseIdent(true);
      node.computed = false;
      base = this.finishNode(node, "MemberExpression");
    } else if (this.eat(_tokentype.types.bracketL)) {
      var node = this.startNodeAt(startPos, startLoc);
      node.object = base;
      node.property = this.parseExpression();
      node.computed = true;
      this.expect(_tokentype.types.bracketR);
      base = this.finishNode(node, "MemberExpression");
    } else if (!noCalls && this.eat(_tokentype.types.parenL)) {
      var node = this.startNodeAt(startPos, startLoc);
      node.callee = base;
      node.arguments = this.parseExprList(_tokentype.types.parenR, false);
      base = this.finishNode(node, "CallExpression");
    } else if (this.type === _tokentype.types.backQuote) {
      var node = this.startNodeAt(startPos, startLoc);
      node.tag = base;
      node.quasi = this.parseTemplate();
      base = this.finishNode(node, "TaggedTemplateExpression");
    } else {
      return base;
    }
  }
};

// Parse an atomic expression — either a single token that is an
// expression, an expression started by a keyword like `function` or
// `new`, or an expression wrapped in punctuation like `()`, `[]`,
// or `{}`.

pp.parseExprAtom = function (refDestructuringErrors) {
  var node = undefined,
      canBeArrow = this.potentialArrowAt == this.start;
  switch (this.type) {
    case _tokentype.types._super:
      if (!this.inFunction) this.raise(this.start, "'super' outside of function or class");
    case _tokentype.types._this:
      var type = this.type === _tokentype.types._this ? "ThisExpression" : "Super";
      node = this.startNode();
      this.next();
      return this.finishNode(node, type);

    case _tokentype.types._yield:
      if (this.inGenerator) this.unexpected();

    case _tokentype.types.name:
      var startPos = this.start,
          startLoc = this.startLoc;
      var id = this.parseIdent(this.type !== _tokentype.types.name);
      if (canBeArrow && !this.canInsertSemicolon() && this.eat(_tokentype.types.arrow)) return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), [id]);
      return id;

    case _tokentype.types.regexp:
      var value = this.value;
      node = this.parseLiteral(value.value);
      node.regex = { pattern: value.pattern, flags: value.flags };
      return node;

    case _tokentype.types.num:case _tokentype.types.string:
      return this.parseLiteral(this.value);

    case _tokentype.types._null:case _tokentype.types._true:case _tokentype.types._false:
      node = this.startNode();
      node.value = this.type === _tokentype.types._null ? null : this.type === _tokentype.types._true;
      node.raw = this.type.keyword;
      this.next();
      return this.finishNode(node, "Literal");

    case _tokentype.types.parenL:
      return this.parseParenAndDistinguishExpression(canBeArrow);

    case _tokentype.types.bracketL:
      node = this.startNode();
      this.next();
      // check whether this is array comprehension or regular array
      if (this.options.ecmaVersion >= 7 && this.type === _tokentype.types._for) {
        return this.parseComprehension(node, false);
      }
      node.elements = this.parseExprList(_tokentype.types.bracketR, true, true, refDestructuringErrors);
      return this.finishNode(node, "ArrayExpression");

    case _tokentype.types.braceL:
      return this.parseObj(false, refDestructuringErrors);

    case _tokentype.types._function:
      node = this.startNode();
      this.next();
      return this.parseFunction(node, false);

    case _tokentype.types._class:
      return this.parseClass(this.startNode(), false);

    case _tokentype.types._new:
      return this.parseNew();

    case _tokentype.types.backQuote:
      return this.parseTemplate();

    default:
      this.unexpected();
  }
};

pp.parseLiteral = function (value) {
  var node = this.startNode();
  node.value = value;
  node.raw = this.input.slice(this.start, this.end);
  this.next();
  return this.finishNode(node, "Literal");
};

pp.parseParenExpression = function () {
  this.expect(_tokentype.types.parenL);
  var val = this.parseExpression();
  this.expect(_tokentype.types.parenR);
  return val;
};

pp.parseParenAndDistinguishExpression = function (canBeArrow) {
  var startPos = this.start,
      startLoc = this.startLoc,
      val = undefined;
  if (this.options.ecmaVersion >= 6) {
    this.next();

    if (this.options.ecmaVersion >= 7 && this.type === _tokentype.types._for) {
      return this.parseComprehension(this.startNodeAt(startPos, startLoc), true);
    }

    var innerStartPos = this.start,
        innerStartLoc = this.startLoc;
    var exprList = [],
        first = true;
    var refDestructuringErrors = { shorthandAssign: 0, trailingComma: 0 },
        spreadStart = undefined,
        innerParenStart = undefined;
    while (this.type !== _tokentype.types.parenR) {
      first ? first = false : this.expect(_tokentype.types.comma);
      if (this.type === _tokentype.types.ellipsis) {
        spreadStart = this.start;
        exprList.push(this.parseParenItem(this.parseRest()));
        break;
      } else {
        if (this.type === _tokentype.types.parenL && !innerParenStart) {
          innerParenStart = this.start;
        }
        exprList.push(this.parseMaybeAssign(false, refDestructuringErrors, this.parseParenItem));
      }
    }
    var innerEndPos = this.start,
        innerEndLoc = this.startLoc;
    this.expect(_tokentype.types.parenR);

    if (canBeArrow && !this.canInsertSemicolon() && this.eat(_tokentype.types.arrow)) {
      this.checkPatternErrors(refDestructuringErrors, true);
      if (innerParenStart) this.unexpected(innerParenStart);
      return this.parseParenArrowList(startPos, startLoc, exprList);
    }

    if (!exprList.length) this.unexpected(this.lastTokStart);
    if (spreadStart) this.unexpected(spreadStart);
    this.checkExpressionErrors(refDestructuringErrors, true);

    if (exprList.length > 1) {
      val = this.startNodeAt(innerStartPos, innerStartLoc);
      val.expressions = exprList;
      this.finishNodeAt(val, "SequenceExpression", innerEndPos, innerEndLoc);
    } else {
      val = exprList[0];
    }
  } else {
    val = this.parseParenExpression();
  }

  if (this.options.preserveParens) {
    var par = this.startNodeAt(startPos, startLoc);
    par.expression = val;
    return this.finishNode(par, "ParenthesizedExpression");
  } else {
    return val;
  }
};

pp.parseParenItem = function (item) {
  return item;
};

pp.parseParenArrowList = function (startPos, startLoc, exprList) {
  return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), exprList);
};

// New's precedence is slightly tricky. It must allow its argument
// to be a `[]` or dot subscript expression, but not a call — at
// least, not without wrapping it in parentheses. Thus, it uses the

var empty = [];

pp.parseNew = function () {
  var node = this.startNode();
  var meta = this.parseIdent(true);
  if (this.options.ecmaVersion >= 6 && this.eat(_tokentype.types.dot)) {
    node.meta = meta;
    node.property = this.parseIdent(true);
    if (node.property.name !== "target") this.raise(node.property.start, "The only valid meta property for new is new.target");
    if (!this.inFunction) this.raise(node.start, "new.target can only be used in functions");
    return this.finishNode(node, "MetaProperty");
  }
  var startPos = this.start,
      startLoc = this.startLoc;
  node.callee = this.parseSubscripts(this.parseExprAtom(), startPos, startLoc, true);
  if (this.eat(_tokentype.types.parenL)) node.arguments = this.parseExprList(_tokentype.types.parenR, false);else node.arguments = empty;
  return this.finishNode(node, "NewExpression");
};

// Parse template expression.

pp.parseTemplateElement = function () {
  var elem = this.startNode();
  elem.value = {
    raw: this.input.slice(this.start, this.end).replace(/\r\n?/g, '\n'),
    cooked: this.value
  };
  this.next();
  elem.tail = this.type === _tokentype.types.backQuote;
  return this.finishNode(elem, "TemplateElement");
};

pp.parseTemplate = function () {
  var node = this.startNode();
  this.next();
  node.expressions = [];
  var curElt = this.parseTemplateElement();
  node.quasis = [curElt];
  while (!curElt.tail) {
    this.expect(_tokentype.types.dollarBraceL);
    node.expressions.push(this.parseExpression());
    this.expect(_tokentype.types.braceR);
    node.quasis.push(curElt = this.parseTemplateElement());
  }
  this.next();
  return this.finishNode(node, "TemplateLiteral");
};

// Parse an object literal or binding pattern.

pp.parseObj = function (isPattern, refDestructuringErrors) {
  var node = this.startNode(),
      first = true,
      propHash = {};
  node.properties = [];
  this.next();
  while (!this.eat(_tokentype.types.braceR)) {
    if (!first) {
      this.expect(_tokentype.types.comma);
      if (this.afterTrailingComma(_tokentype.types.braceR)) break;
    } else first = false;

    var prop = this.startNode(),
        isGenerator = undefined,
        startPos = undefined,
        startLoc = undefined;
    if (this.options.ecmaVersion >= 6) {
      prop.method = false;
      prop.shorthand = false;
      if (isPattern || refDestructuringErrors) {
        startPos = this.start;
        startLoc = this.startLoc;
      }
      if (!isPattern) isGenerator = this.eat(_tokentype.types.star);
    }
    this.parsePropertyName(prop);
    this.parsePropertyValue(prop, isPattern, isGenerator, startPos, startLoc, refDestructuringErrors);
    this.checkPropClash(prop, propHash);
    node.properties.push(this.finishNode(prop, "Property"));
  }
  return this.finishNode(node, isPattern ? "ObjectPattern" : "ObjectExpression");
};

pp.parsePropertyValue = function (prop, isPattern, isGenerator, startPos, startLoc, refDestructuringErrors) {
  if (this.eat(_tokentype.types.colon)) {
    prop.value = isPattern ? this.parseMaybeDefault(this.start, this.startLoc) : this.parseMaybeAssign(false, refDestructuringErrors);
    prop.kind = "init";
  } else if (this.options.ecmaVersion >= 6 && this.type === _tokentype.types.parenL) {
    if (isPattern) this.unexpected();
    prop.kind = "init";
    prop.method = true;
    prop.value = this.parseMethod(isGenerator);
  } else if (this.options.ecmaVersion >= 5 && !prop.computed && prop.key.type === "Identifier" && (prop.key.name === "get" || prop.key.name === "set") && (this.type != _tokentype.types.comma && this.type != _tokentype.types.braceR)) {
    if (isGenerator || isPattern) this.unexpected();
    prop.kind = prop.key.name;
    this.parsePropertyName(prop);
    prop.value = this.parseMethod(false);
    var paramCount = prop.kind === "get" ? 0 : 1;
    if (prop.value.params.length !== paramCount) {
      var start = prop.value.start;
      if (prop.kind === "get") this.raise(start, "getter should have no params");else this.raise(start, "setter should have exactly one param");
    }
  } else if (this.options.ecmaVersion >= 6 && !prop.computed && prop.key.type === "Identifier") {
    prop.kind = "init";
    if (isPattern) {
      if (this.keywords.test(prop.key.name) || (this.strict ? this.reservedWordsStrictBind : this.reservedWords).test(prop.key.name)) this.raise(prop.key.start, "Binding " + prop.key.name);
      prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key);
    } else if (this.type === _tokentype.types.eq && refDestructuringErrors) {
      if (!refDestructuringErrors.shorthandAssign) refDestructuringErrors.shorthandAssign = this.start;
      prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key);
    } else {
      prop.value = prop.key;
    }
    prop.shorthand = true;
  } else this.unexpected();
};

pp.parsePropertyName = function (prop) {
  if (this.options.ecmaVersion >= 6) {
    if (this.eat(_tokentype.types.bracketL)) {
      prop.computed = true;
      prop.key = this.parseMaybeAssign();
      this.expect(_tokentype.types.bracketR);
      return prop.key;
    } else {
      prop.computed = false;
    }
  }
  return prop.key = this.type === _tokentype.types.num || this.type === _tokentype.types.string ? this.parseExprAtom() : this.parseIdent(true);
};

// Initialize empty function node.

pp.initFunction = function (node) {
  node.id = null;
  if (this.options.ecmaVersion >= 6) {
    node.generator = false;
    node.expression = false;
  }
};

// Parse object or class method.

pp.parseMethod = function (isGenerator) {
  var node = this.startNode();
  this.initFunction(node);
  this.expect(_tokentype.types.parenL);
  node.params = this.parseBindingList(_tokentype.types.parenR, false, false);
  if (this.options.ecmaVersion >= 6) node.generator = isGenerator;
  this.parseFunctionBody(node, false);
  return this.finishNode(node, "FunctionExpression");
};

// Parse arrow function expression with given parameters.

pp.parseArrowExpression = function (node, params) {
  this.initFunction(node);
  node.params = this.toAssignableList(params, true);
  this.parseFunctionBody(node, true);
  return this.finishNode(node, "ArrowFunctionExpression");
};

// Parse function body and check parameters.

pp.parseFunctionBody = function (node, isArrowFunction) {
  var isExpression = isArrowFunction && this.type !== _tokentype.types.braceL;

  if (isExpression) {
    node.body = this.parseMaybeAssign();
    node.expression = true;
  } else {
    // Start a new scope with regard to labels and the `inFunction`
    // flag (restore them to their old value afterwards).
    var oldInFunc = this.inFunction,
        oldInGen = this.inGenerator,
        oldLabels = this.labels;
    this.inFunction = true;this.inGenerator = node.generator;this.labels = [];
    node.body = this.parseBlock(true);
    node.expression = false;
    this.inFunction = oldInFunc;this.inGenerator = oldInGen;this.labels = oldLabels;
  }

  // If this is a strict mode function, verify that argument names
  // are not repeated, and it does not try to bind the words `eval`
  // or `arguments`.
  if (this.strict || !isExpression && node.body.body.length && this.isUseStrict(node.body.body[0])) {
    var oldStrict = this.strict;
    this.strict = true;
    if (node.id) this.checkLVal(node.id, true);
    this.checkParams(node);
    this.strict = oldStrict;
  } else if (isArrowFunction) {
    this.checkParams(node);
  }
};

// Checks function params for various disallowed patterns such as using "eval"
// or "arguments" and duplicate parameters.

pp.checkParams = function (node) {
  var nameHash = {};
  for (var i = 0; i < node.params.length; i++) {
    this.checkLVal(node.params[i], true, nameHash);
  }
};

// Parses a comma-separated list of expressions, and returns them as
// an array. `close` is the token type that ends the list, and
// `allowEmpty` can be turned on to allow subsequent commas with
// nothing in between them to be parsed as `null` (which is needed
// for array literals).

pp.parseExprList = function (close, allowTrailingComma, allowEmpty, refDestructuringErrors) {
  var elts = [],
      first = true;
  while (!this.eat(close)) {
    if (!first) {
      this.expect(_tokentype.types.comma);
      if (this.type === close && refDestructuringErrors && !refDestructuringErrors.trailingComma) {
        refDestructuringErrors.trailingComma = this.lastTokStart;
      }
      if (allowTrailingComma && this.afterTrailingComma(close)) break;
    } else first = false;

    var elt = undefined;
    if (allowEmpty && this.type === _tokentype.types.comma) elt = null;else if (this.type === _tokentype.types.ellipsis) elt = this.parseSpread(refDestructuringErrors);else elt = this.parseMaybeAssign(false, refDestructuringErrors);
    elts.push(elt);
  }
  return elts;
};

// Parse the next token as an identifier. If `liberal` is true (used
// when parsing properties), it will also convert keywords into
// identifiers.

pp.parseIdent = function (liberal) {
  var node = this.startNode();
  if (liberal && this.options.allowReserved == "never") liberal = false;
  if (this.type === _tokentype.types.name) {
    if (!liberal && (this.strict ? this.reservedWordsStrict : this.reservedWords).test(this.value) && (this.options.ecmaVersion >= 6 || !this.input.slice(this.start, this.end).includes("\\"))) this.raise(this.start, "The keyword '" + this.value + "' is reserved");
    node.name = this.value;
  } else if (liberal && this.type.keyword) {
    node.name = this.type.keyword;
  } else {
    this.unexpected();
  }
  this.next();
  return this.finishNode(node, "Identifier");
};

// Parses yield expression inside generator.

pp.parseYield = function () {
  var node = this.startNode();
  this.next();
  if (this.type == _tokentype.types.semi || this.canInsertSemicolon() || this.type != _tokentype.types.star && !this.type.startsExpr) {
    node.delegate = false;
    node.argument = null;
  } else {
    node.delegate = this.eat(_tokentype.types.star);
    node.argument = this.parseMaybeAssign();
  }
  return this.finishNode(node, "YieldExpression");
};

// Parses array and generator comprehensions.

pp.parseComprehension = function (node, isGenerator) {
  node.blocks = [];
  while (this.type === _tokentype.types._for) {
    var block = this.startNode();
    this.next();
    this.expect(_tokentype.types.parenL);
    block.left = this.parseBindingAtom();
    this.checkLVal(block.left, true);
    this.expectContextual("of");
    block.right = this.parseExpression();
    this.expect(_tokentype.types.parenR);
    node.blocks.push(this.finishNode(block, "ComprehensionBlock"));
  }
  node.filter = this.eat(_tokentype.types._if) ? this.parseParenExpression() : null;
  node.body = this.parseExpression();
  this.expect(isGenerator ? _tokentype.types.parenR : _tokentype.types.bracketR);
  node.generator = isGenerator;
  return this.finishNode(node, "ComprehensionExpression");
};

},{"./state":10,"./tokentype":14}],2:[function(_dereq_,module,exports){
// This is a trick taken from Esprima. It turns out that, on
// non-Chrome browsers, to check whether a string is in a set, a
// predicate containing a big ugly `switch` statement is faster than
// a regular expression, and on Chrome the two are about on par.
// This function uses `eval` (non-lexical) to produce such a
// predicate from a space-separated string of words.
//
// It starts by sorting the words by length.

// Reserved word lists for various dialects of the language

"use strict";

exports.__esModule = true;
exports.isIdentifierStart = isIdentifierStart;
exports.isIdentifierChar = isIdentifierChar;
var reservedWords = {
  3: "abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized throws transient volatile",
  5: "class enum extends super const export import",
  6: "enum",
  strict: "implements interface let package private protected public static yield",
  strictBind: "eval arguments"
};

exports.reservedWords = reservedWords;
// And the keywords

var ecma5AndLessKeywords = "break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this";

var keywords = {
  5: ecma5AndLessKeywords,
  6: ecma5AndLessKeywords + " let const class extends export import yield super"
};

exports.keywords = keywords;
// ## Character categories

// Big ugly regular expressions that match characters in the
// whitespace, identifier, and identifier-start categories. These
// are only applied when a character is found to actually have a
// code point above 128.
// Generated by `bin/generate-identifier-regex.js`.

var nonASCIIidentifierStartChars = "ªµºÀ-ÖØ-öø-ˁˆ-ˑˠ-ˤˬˮͰ-ʹͶͷͺ-ͽͿΆΈ-ΊΌΎ-ΡΣ-ϵϷ-ҁҊ-ԯԱ-Ֆՙա-ևא-תװ-ײؠ-يٮٯٱ-ۓەۥۦۮۯۺ-ۼۿܐܒ-ܯݍ-ޥޱߊ-ߪߴߵߺࠀ-ࠕࠚࠤࠨࡀ-ࡘࢠ-ࢲऄ-हऽॐक़-ॡॱ-ঀঅ-ঌএঐও-নপ-রলশ-হঽৎড়ঢ়য়-ৡৰৱਅ-ਊਏਐਓ-ਨਪ-ਰਲਲ਼ਵਸ਼ਸਹਖ਼-ੜਫ਼ੲ-ੴઅ-ઍએ-ઑઓ-નપ-રલળવ-હઽૐૠૡଅ-ଌଏଐଓ-ନପ-ରଲଳଵ-ହଽଡ଼ଢ଼ୟ-ୡୱஃஅ-ஊஎ-ஐஒ-கஙசஜஞடணதந-பம-ஹௐఅ-ఌఎ-ఐఒ-నప-హఽౘౙౠౡಅ-ಌಎ-ಐಒ-ನಪ-ಳವ-ಹಽೞೠೡೱೲഅ-ഌഎ-ഐഒ-ഺഽൎൠൡൺ-ൿඅ-ඖක-නඳ-රලව-ෆก-ะาำเ-ๆກຂຄງຈຊຍດ-ທນ-ຟມ-ຣລວສຫອ-ະາຳຽເ-ໄໆໜ-ໟༀཀ-ཇཉ-ཬྈ-ྌက-ဪဿၐ-ၕၚ-ၝၡၥၦၮ-ၰၵ-ႁႎႠ-ჅჇჍა-ჺჼ-ቈቊ-ቍቐ-ቖቘቚ-ቝበ-ኈኊ-ኍነ-ኰኲ-ኵኸ-ኾዀዂ-ዅወ-ዖዘ-ጐጒ-ጕጘ-ፚᎀ-ᎏᎠ-Ᏼᐁ-ᙬᙯ-ᙿᚁ-ᚚᚠ-ᛪᛮ-ᛸᜀ-ᜌᜎ-ᜑᜠ-ᜱᝀ-ᝑᝠ-ᝬᝮ-ᝰក-ឳៗៜᠠ-ᡷᢀ-ᢨᢪᢰ-ᣵᤀ-ᤞᥐ-ᥭᥰ-ᥴᦀ-ᦫᧁ-ᧇᨀ-ᨖᨠ-ᩔᪧᬅ-ᬳᭅ-ᭋᮃ-ᮠᮮᮯᮺ-ᯥᰀ-ᰣᱍ-ᱏᱚ-ᱽᳩ-ᳬᳮ-ᳱᳵᳶᴀ-ᶿḀ-ἕἘ-Ἕἠ-ὅὈ-Ὅὐ-ὗὙὛὝὟ-ώᾀ-ᾴᾶ-ᾼιῂ-ῄῆ-ῌῐ-ΐῖ-Ίῠ-Ῥῲ-ῴῶ-ῼⁱⁿₐ-ₜℂℇℊ-ℓℕ℘-ℝℤΩℨK-ℹℼ-ℿⅅ-ⅉⅎⅠ-ↈⰀ-Ⱞⰰ-ⱞⱠ-ⳤⳫ-ⳮⳲⳳⴀ-ⴥⴧⴭⴰ-ⵧⵯⶀ-ⶖⶠ-ⶦⶨ-ⶮⶰ-ⶶⶸ-ⶾⷀ-ⷆⷈ-ⷎⷐ-ⷖⷘ-ⷞ々-〇〡-〩〱-〵〸-〼ぁ-ゖ゛-ゟァ-ヺー-ヿㄅ-ㄭㄱ-ㆎㆠ-ㆺㇰ-ㇿ㐀-䶵一-鿌ꀀ-ꒌꓐ-ꓽꔀ-ꘌꘐ-ꘟꘪꘫꙀ-ꙮꙿ-ꚝꚠ-ꛯꜗ-ꜟꜢ-ꞈꞋ-ꞎꞐ-ꞭꞰꞱꟷ-ꠁꠃ-ꠅꠇ-ꠊꠌ-ꠢꡀ-ꡳꢂ-ꢳꣲ-ꣷꣻꤊ-ꤥꤰ-ꥆꥠ-ꥼꦄ-ꦲꧏꧠ-ꧤꧦ-ꧯꧺ-ꧾꨀ-ꨨꩀ-ꩂꩄ-ꩋꩠ-ꩶꩺꩾ-ꪯꪱꪵꪶꪹ-ꪽꫀꫂꫛ-ꫝꫠ-ꫪꫲ-ꫴꬁ-ꬆꬉ-ꬎꬑ-ꬖꬠ-ꬦꬨ-ꬮꬰ-ꭚꭜ-ꭟꭤꭥꯀ-ꯢ가-힣ힰ-ퟆퟋ-ퟻ豈-舘並-龎ff-stﬓ-ﬗיִײַ-ﬨשׁ-זּטּ-לּמּנּסּףּפּצּ-ﮱﯓ-ﴽﵐ-ﶏﶒ-ﷇﷰ-ﷻﹰ-ﹴﹶ-ﻼA-Za-zヲ-하-ᅦᅧ-ᅬᅭ-ᅲᅳ-ᅵ";
var nonASCIIidentifierChars = "‌‍·̀-ͯ·҃-֑҇-ׇֽֿׁׂׅׄؐ-ًؚ-٩ٰۖ-ۜ۟-۪ۤۧۨ-ۭ۰-۹ܑܰ-݊ަ-ް߀-߉߫-߳ࠖ-࠙ࠛ-ࠣࠥ-ࠧࠩ-࡙࠭-࡛ࣤ-ःऺ-़ा-ॏ॑-ॗॢॣ०-९ঁ-ঃ়া-ৄেৈো-্ৗৢৣ০-৯ਁ-ਃ਼ਾ-ੂੇੈੋ-੍ੑ੦-ੱੵઁ-ઃ઼ા-ૅે-ૉો-્ૢૣ૦-૯ଁ-ଃ଼ା-ୄେୈୋ-୍ୖୗୢୣ୦-୯ஂா-ூெ-ைொ-்ௗ௦-௯ఀ-ఃా-ౄె-ైొ-్ౕౖౢౣ౦-౯ಁ-ಃ಼ಾ-ೄೆ-ೈೊ-್ೕೖೢೣ೦-೯ഁ-ഃാ-ൄെ-ൈൊ-്ൗൢൣ൦-൯ංඃ්ා-ුූෘ-ෟ෦-෯ෲෳัิ-ฺ็-๎๐-๙ັິ-ູົຼ່-ໍ໐-໙༘༙༠-༩༹༵༷༾༿ཱ-྄྆྇ྍ-ྗྙ-ྼ࿆ါ-ှ၀-၉ၖ-ၙၞ-ၠၢ-ၤၧ-ၭၱ-ၴႂ-ႍႏ-ႝ፝-፟፩-፱ᜒ-᜔ᜲ-᜴ᝒᝓᝲᝳ឴-៓៝០-៩᠋-᠍᠐-᠙ᢩᤠ-ᤫᤰ-᤻᥆-᥏ᦰ-ᧀᧈᧉ᧐-᧚ᨗ-ᨛᩕ-ᩞ᩠-᩿᩼-᪉᪐-᪙᪰-᪽ᬀ-ᬄ᬴-᭄᭐-᭙᭫-᭳ᮀ-ᮂᮡ-ᮭ᮰-᮹᯦-᯳ᰤ-᰷᱀-᱉᱐-᱙᳐-᳔᳒-᳨᳭ᳲ-᳴᳸᳹᷀-᷵᷼-᷿‿⁀⁔⃐-⃥⃜⃡-⃰⳯-⵿⳱ⷠ-〪ⷿ-゙゚〯꘠-꘩꙯ꙴ-꙽ꚟ꛰꛱ꠂ꠆ꠋꠣ-ꠧꢀꢁꢴ-꣄꣐-꣙꣠-꣱꤀-꤉ꤦ-꤭ꥇ-꥓ꦀ-ꦃ꦳-꧀꧐-꧙ꧥ꧰-꧹ꨩ-ꨶꩃꩌꩍ꩐-꩙ꩻ-ꩽꪰꪲ-ꪴꪷꪸꪾ꪿꫁ꫫ-ꫯꫵ꫶ꯣ-ꯪ꯬꯭꯰-꯹ﬞ︀-️︠-︭︳︴﹍-﹏0-9_";

var nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]");
var nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]");

nonASCIIidentifierStartChars = nonASCIIidentifierChars = null;

// These are a run-length and offset encoded representation of the
// >0xffff code points that are a valid part of identifiers. The
// offset starts at 0x10000, and each pair of numbers represents an
// offset to the next range, and then a size of the range. They were
// generated by tools/generate-identifier-regex.js
var astralIdentifierStartCodes = [0, 11, 2, 25, 2, 18, 2, 1, 2, 14, 3, 13, 35, 122, 70, 52, 268, 28, 4, 48, 48, 31, 17, 26, 6, 37, 11, 29, 3, 35, 5, 7, 2, 4, 43, 157, 99, 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, 98, 21, 11, 25, 71, 55, 7, 1, 65, 0, 16, 3, 2, 2, 2, 26, 45, 28, 4, 28, 36, 7, 2, 27, 28, 53, 11, 21, 11, 18, 14, 17, 111, 72, 955, 52, 76, 44, 33, 24, 27, 35, 42, 34, 4, 0, 13, 47, 15, 3, 22, 0, 38, 17, 2, 24, 133, 46, 39, 7, 3, 1, 3, 21, 2, 6, 2, 1, 2, 4, 4, 0, 32, 4, 287, 47, 21, 1, 2, 0, 185, 46, 82, 47, 21, 0, 60, 42, 502, 63, 32, 0, 449, 56, 1288, 920, 104, 110, 2962, 1070, 13266, 568, 8, 30, 114, 29, 19, 47, 17, 3, 32, 20, 6, 18, 881, 68, 12, 0, 67, 12, 16481, 1, 3071, 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, 4149, 196, 1340, 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, 16355, 541];
var astralIdentifierCodes = [509, 0, 227, 0, 150, 4, 294, 9, 1368, 2, 2, 1, 6, 3, 41, 2, 5, 0, 166, 1, 1306, 2, 54, 14, 32, 9, 16, 3, 46, 10, 54, 9, 7, 2, 37, 13, 2, 9, 52, 0, 13, 2, 49, 13, 16, 9, 83, 11, 168, 11, 6, 9, 8, 2, 57, 0, 2, 6, 3, 1, 3, 2, 10, 0, 11, 1, 3, 6, 4, 4, 316, 19, 13, 9, 214, 6, 3, 8, 112, 16, 16, 9, 82, 12, 9, 9, 535, 9, 20855, 9, 135, 4, 60, 6, 26, 9, 1016, 45, 17, 3, 19723, 1, 5319, 4, 4, 5, 9, 7, 3, 6, 31, 3, 149, 2, 1418, 49, 4305, 6, 792618, 239];

// This has a complexity linear to the value of the code. The
// assumption is that looking up astral identifier characters is
// rare.
function isInAstralSet(code, set) {
  var pos = 0x10000;
  for (var i = 0; i < set.length; i += 2) {
    pos += set[i];
    if (pos > code) return false;
    pos += set[i + 1];
    if (pos >= code) return true;
  }
}

// Test whether a given character code starts an identifier.

function isIdentifierStart(code, astral) {
  if (code < 65) return code === 36;
  if (code < 91) return true;
  if (code < 97) return code === 95;
  if (code < 123) return true;
  if (code <= 0xffff) return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code));
  if (astral === false) return false;
  return isInAstralSet(code, astralIdentifierStartCodes);
}

// Test whether a given character is part of an identifier.

function isIdentifierChar(code, astral) {
  if (code < 48) return code === 36;
  if (code < 58) return true;
  if (code < 65) return false;
  if (code < 91) return true;
  if (code < 97) return code === 95;
  if (code < 123) return true;
  if (code <= 0xffff) return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code));
  if (astral === false) return false;
  return isInAstralSet(code, astralIdentifierStartCodes) || isInAstralSet(code, astralIdentifierCodes);
}

},{}],3:[function(_dereq_,module,exports){
// Acorn is a tiny, fast JavaScript parser written in JavaScript.
//
// Acorn was written by Marijn Haverbeke, Ingvar Stepanyan, and
// various contributors and released under an MIT license.
//
// Git repositories for Acorn are available at
//
//     http://marijnhaverbeke.nl/git/acorn
//     https://github.com/ternjs/acorn.git
//
// Please use the [github bug tracker][ghbt] to report issues.
//
// [ghbt]: https://github.com/ternjs/acorn/issues
//
// This file defines the main parser interface. The library also comes
// with a [error-tolerant parser][dammit] and an
// [abstract syntax tree walker][walk], defined in other files.
//
// [dammit]: acorn_loose.js
// [walk]: util/walk.js

"use strict";

exports.__esModule = true;
exports.parse = parse;
exports.parseExpressionAt = parseExpressionAt;
exports.tokenizer = tokenizer;

var _state = _dereq_("./state");

_dereq_("./parseutil");

_dereq_("./statement");

_dereq_("./lval");

_dereq_("./expression");

_dereq_("./location");

exports.Parser = _state.Parser;
exports.plugins = _state.plugins;

var _options = _dereq_("./options");

exports.defaultOptions = _options.defaultOptions;

var _locutil = _dereq_("./locutil");

exports.Position = _locutil.Position;
exports.SourceLocation = _locutil.SourceLocation;
exports.getLineInfo = _locutil.getLineInfo;

var _node = _dereq_("./node");

exports.Node = _node.Node;

var _tokentype = _dereq_("./tokentype");

exports.TokenType = _tokentype.TokenType;
exports.tokTypes = _tokentype.types;

var _tokencontext = _dereq_("./tokencontext");

exports.TokContext = _tokencontext.TokContext;
exports.tokContexts = _tokencontext.types;

var _identifier = _dereq_("./identifier");

exports.isIdentifierChar = _identifier.isIdentifierChar;
exports.isIdentifierStart = _identifier.isIdentifierStart;

var _tokenize = _dereq_("./tokenize");

exports.Token = _tokenize.Token;

var _whitespace = _dereq_("./whitespace");

exports.isNewLine = _whitespace.isNewLine;
exports.lineBreak = _whitespace.lineBreak;
exports.lineBreakG = _whitespace.lineBreakG;
var version = "2.6.4";

exports.version = version;
// The main exported interface (under `self.acorn` when in the
// browser) is a `parse` function that takes a code string and
// returns an abstract syntax tree as specified by [Mozilla parser
// API][api].
//
// [api]: https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API

function parse(input, options) {
  return new _state.Parser(options, input).parse();
}

// This function tries to parse a single expression at a given
// offset in a string. Useful for parsing mixed-language formats
// that embed JavaScript expressions.

function parseExpressionAt(input, pos, options) {
  var p = new _state.Parser(options, input, pos);
  p.nextToken();
  return p.parseExpression();
}

// Acorn is organized as a tokenizer and a recursive-descent parser.
// The `tokenizer` export provides an interface to the tokenizer.

function tokenizer(input, options) {
  return new _state.Parser(options, input);
}

},{"./expression":1,"./identifier":2,"./location":4,"./locutil":5,"./lval":6,"./node":7,"./options":8,"./parseutil":9,"./state":10,"./statement":11,"./tokencontext":12,"./tokenize":13,"./tokentype":14,"./whitespace":16}],4:[function(_dereq_,module,exports){
"use strict";

var _state = _dereq_("./state");

var _locutil = _dereq_("./locutil");

var pp = _state.Parser.prototype;

// This function is used to raise exceptions on parse errors. It
// takes an offset integer (into the current `input`) to indicate
// the location of the error, attaches the position to the end
// of the error message, and then raises a `SyntaxError` with that
// message.

pp.raise = function (pos, message) {
  var loc = _locutil.getLineInfo(this.input, pos);
  message += " (" + loc.line + ":" + loc.column + ")";
  var err = new SyntaxError(message);
  err.pos = pos;err.loc = loc;err.raisedAt = this.pos;
  throw err;
};

pp.curPosition = function () {
  if (this.options.locations) {
    return new _locutil.Position(this.curLine, this.pos - this.lineStart);
  }
};

},{"./locutil":5,"./state":10}],5:[function(_dereq_,module,exports){
"use strict";

exports.__esModule = true;
exports.getLineInfo = getLineInfo;

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var _whitespace = _dereq_("./whitespace");

// These are used when `options.locations` is on, for the
// `startLoc` and `endLoc` properties.

var Position = (function () {
  function Position(line, col) {
    _classCallCheck(this, Position);

    this.line = line;
    this.column = col;
  }

  Position.prototype.offset = function offset(n) {
    return new Position(this.line, this.column + n);
  };

  return Position;
})();

exports.Position = Position;

var SourceLocation = function SourceLocation(p, start, end) {
  _classCallCheck(this, SourceLocation);

  this.start = start;
  this.end = end;
  if (p.sourceFile !== null) this.source = p.sourceFile;
}

// The `getLineInfo` function is mostly useful when the
// `locations` option is off (for performance reasons) and you
// want to find the line/column position for a given character
// offset. `input` should be the code string that the offset refers
// into.

;

exports.SourceLocation = SourceLocation;

function getLineInfo(input, offset) {
  for (var line = 1, cur = 0;;) {
    _whitespace.lineBreakG.lastIndex = cur;
    var match = _whitespace.lineBreakG.exec(input);
    if (match && match.index < offset) {
      ++line;
      cur = match.index + match[0].length;
    } else {
      return new Position(line, offset - cur);
    }
  }
}

},{"./whitespace":16}],6:[function(_dereq_,module,exports){
"use strict";

var _tokentype = _dereq_("./tokentype");

var _state = _dereq_("./state");

var _util = _dereq_("./util");

var pp = _state.Parser.prototype;

// Convert existing expression atom to assignable pattern
// if possible.

pp.toAssignable = function (node, isBinding) {
  if (this.options.ecmaVersion >= 6 && node) {
    switch (node.type) {
      case "Identifier":
      case "ObjectPattern":
      case "ArrayPattern":
        break;

      case "ObjectExpression":
        node.type = "ObjectPattern";
        for (var i = 0; i < node.properties.length; i++) {
          var prop = node.properties[i];
          if (prop.kind !== "init") this.raise(prop.key.start, "Object pattern can't contain getter or setter");
          this.toAssignable(prop.value, isBinding);
        }
        break;

      case "ArrayExpression":
        node.type = "ArrayPattern";
        this.toAssignableList(node.elements, isBinding);
        break;

      case "AssignmentExpression":
        if (node.operator === "=") {
          node.type = "AssignmentPattern";
          delete node.operator;
          // falls through to AssignmentPattern
        } else {
            this.raise(node.left.end, "Only '=' operator can be used for specifying default value.");
            break;
          }

      case "AssignmentPattern":
        if (node.right.type === "YieldExpression") this.raise(node.right.start, "Yield expression cannot be a default value");
        break;

      case "ParenthesizedExpression":
        node.expression = this.toAssignable(node.expression, isBinding);
        break;

      case "MemberExpression":
        if (!isBinding) break;

      default:
        this.raise(node.start, "Assigning to rvalue");
    }
  }
  return node;
};

// Convert list of expression atoms to binding list.

pp.toAssignableList = function (exprList, isBinding) {
  var end = exprList.length;
  if (end) {
    var last = exprList[end - 1];
    if (last && last.type == "RestElement") {
      --end;
    } else if (last && last.type == "SpreadElement") {
      last.type = "RestElement";
      var arg = last.argument;
      this.toAssignable(arg, isBinding);
      if (arg.type !== "Identifier" && arg.type !== "MemberExpression" && arg.type !== "ArrayPattern") this.unexpected(arg.start);
      --end;
    }

    if (isBinding && last.type === "RestElement" && last.argument.type !== "Identifier") this.unexpected(last.argument.start);
  }
  for (var i = 0; i < end; i++) {
    var elt = exprList[i];
    if (elt) this.toAssignable(elt, isBinding);
  }
  return exprList;
};

// Parses spread element.

pp.parseSpread = function (refDestructuringErrors) {
  var node = this.startNode();
  this.next();
  node.argument = this.parseMaybeAssign(refDestructuringErrors);
  return this.finishNode(node, "SpreadElement");
};

pp.parseRest = function (allowNonIdent) {
  var node = this.startNode();
  this.next();

  // RestElement inside of a function parameter must be an identifier
  if (allowNonIdent) node.argument = this.type === _tokentype.types.name ? this.parseIdent() : this.unexpected();else node.argument = this.type === _tokentype.types.name || this.type === _tokentype.types.bracketL ? this.parseBindingAtom() : this.unexpected();

  return this.finishNode(node, "RestElement");
};

// Parses lvalue (assignable) atom.

pp.parseBindingAtom = function () {
  if (this.options.ecmaVersion < 6) return this.parseIdent();
  switch (this.type) {
    case _tokentype.types.name:
      return this.parseIdent();

    case _tokentype.types.bracketL:
      var node = this.startNode();
      this.next();
      node.elements = this.parseBindingList(_tokentype.types.bracketR, true, true);
      return this.finishNode(node, "ArrayPattern");

    case _tokentype.types.braceL:
      return this.parseObj(true);

    default:
      this.unexpected();
  }
};

pp.parseBindingList = function (close, allowEmpty, allowTrailingComma, allowNonIdent) {
  var elts = [],
      first = true;
  while (!this.eat(close)) {
    if (first) first = false;else this.expect(_tokentype.types.comma);
    if (allowEmpty && this.type === _tokentype.types.comma) {
      elts.push(null);
    } else if (allowTrailingComma && this.afterTrailingComma(close)) {
      break;
    } else if (this.type === _tokentype.types.ellipsis) {
      var rest = this.parseRest(allowNonIdent);
      this.parseBindingListItem(rest);
      elts.push(rest);
      this.expect(close);
      break;
    } else {
      var elem = this.parseMaybeDefault(this.start, this.startLoc);
      this.parseBindingListItem(elem);
      elts.push(elem);
    }
  }
  return elts;
};

pp.parseBindingListItem = function (param) {
  return param;
};

// Parses assignment pattern around given atom if possible.

pp.parseMaybeDefault = function (startPos, startLoc, left) {
  left = left || this.parseBindingAtom();
  if (this.options.ecmaVersion < 6 || !this.eat(_tokentype.types.eq)) return left;
  var node = this.startNodeAt(startPos, startLoc);
  node.left = left;
  node.right = this.parseMaybeAssign();
  return this.finishNode(node, "AssignmentPattern");
};

// Verify that a node is an lval — something that can be assigned
// to.

pp.checkLVal = function (expr, isBinding, checkClashes) {
  switch (expr.type) {
    case "Identifier":
      if (this.strict && this.reservedWordsStrictBind.test(expr.name)) this.raise(expr.start, (isBinding ? "Binding " : "Assigning to ") + expr.name + " in strict mode");
      if (checkClashes) {
        if (_util.has(checkClashes, expr.name)) this.raise(expr.start, "Argument name clash");
        checkClashes[expr.name] = true;
      }
      break;

    case "MemberExpression":
      if (isBinding) this.raise(expr.start, (isBinding ? "Binding" : "Assigning to") + " member expression");
      break;

    case "ObjectPattern":
      for (var i = 0; i < expr.properties.length; i++) {
        this.checkLVal(expr.properties[i].value, isBinding, checkClashes);
      }break;

    case "ArrayPattern":
      for (var i = 0; i < expr.elements.length; i++) {
        var elem = expr.elements[i];
        if (elem) this.checkLVal(elem, isBinding, checkClashes);
      }
      break;

    case "AssignmentPattern":
      this.checkLVal(expr.left, isBinding, checkClashes);
      break;

    case "RestElement":
      this.checkLVal(expr.argument, isBinding, checkClashes);
      break;

    case "ParenthesizedExpression":
      this.checkLVal(expr.expression, isBinding, checkClashes);
      break;

    default:
      this.raise(expr.start, (isBinding ? "Binding" : "Assigning to") + " rvalue");
  }
};

},{"./state":10,"./tokentype":14,"./util":15}],7:[function(_dereq_,module,exports){
"use strict";

exports.__esModule = true;

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var _state = _dereq_("./state");

var _locutil = _dereq_("./locutil");

var Node = function Node(parser, pos, loc) {
  _classCallCheck(this, Node);

  this.type = "";
  this.start = pos;
  this.end = 0;
  if (parser.options.locations) this.loc = new _locutil.SourceLocation(parser, loc);
  if (parser.options.directSourceFile) this.sourceFile = parser.options.directSourceFile;
  if (parser.options.ranges) this.range = [pos, 0];
}

// Start an AST node, attaching a start offset.

;

exports.Node = Node;
var pp = _state.Parser.prototype;

pp.startNode = function () {
  return new Node(this, this.start, this.startLoc);
};

pp.startNodeAt = function (pos, loc) {
  return new Node(this, pos, loc);
};

// Finish an AST node, adding `type` and `end` properties.

function finishNodeAt(node, type, pos, loc) {
  node.type = type;
  node.end = pos;
  if (this.options.locations) node.loc.end = loc;
  if (this.options.ranges) node.range[1] = pos;
  return node;
}

pp.finishNode = function (node, type) {
  return finishNodeAt.call(this, node, type, this.lastTokEnd, this.lastTokEndLoc);
};

// Finish node at given position

pp.finishNodeAt = function (node, type, pos, loc) {
  return finishNodeAt.call(this, node, type, pos, loc);
};

},{"./locutil":5,"./state":10}],8:[function(_dereq_,module,exports){
"use strict";

exports.__esModule = true;
exports.getOptions = getOptions;

var _util = _dereq_("./util");

var _locutil = _dereq_("./locutil");

// A second optional argument can be given to further configure
// the parser process. These options are recognized:

var defaultOptions = {
  // `ecmaVersion` indicates the ECMAScript version to parse. Must
  // be either 3, or 5, or 6. This influences support for strict
  // mode, the set of reserved words, support for getters and
  // setters and other features.
  ecmaVersion: 5,
  // Source type ("script" or "module") for different semantics
  sourceType: "script",
  // `onInsertedSemicolon` can be a callback that will be called
  // when a semicolon is automatically inserted. It will be passed
  // th position of the comma as an offset, and if `locations` is
  // enabled, it is given the location as a `{line, column}` object
  // as second argument.
  onInsertedSemicolon: null,
  // `onTrailingComma` is similar to `onInsertedSemicolon`, but for
  // trailing commas.
  onTrailingComma: null,
  // By default, reserved words are only enforced if ecmaVersion >= 5.
  // Set `allowReserved` to a boolean value to explicitly turn this on
  // an off. When this option has the value "never", reserved words
  // and keywords can also not be used as property names.
  allowReserved: null,
  // When enabled, a return at the top level is not considered an
  // error.
  allowReturnOutsideFunction: false,
  // When enabled, import/export statements are not constrained to
  // appearing at the top of the program.
  allowImportExportEverywhere: false,
  // When enabled, hashbang directive in the beginning of file
  // is allowed and treated as a line comment.
  allowHashBang: false,
  // When `locations` is on, `loc` properties holding objects with
  // `start` and `end` properties in `{line, column}` form (with
  // line being 1-based and column 0-based) will be attached to the
  // nodes.
  locations: false,
  // A function can be passed as `onToken` option, which will
  // cause Acorn to call that function with object in the same
  // format as tokens returned from `tokenizer().getToken()`. Note
  // that you are not allowed to call the parser from the
  // callback—that will corrupt its internal state.
  onToken: null,
  // A function can be passed as `onComment` option, which will
  // cause Acorn to call that function with `(block, text, start,
  // end)` parameters whenever a comment is skipped. `block` is a
  // boolean indicating whether this is a block (`/* */`) comment,
  // `text` is the content of the comment, and `start` and `end` are
  // character offsets that denote the start and end of the comment.
  // When the `locations` option is on, two more parameters are
  // passed, the full `{line, column}` locations of the start and
  // end of the comments. Note that you are not allowed to call the
  // parser from the callback—that will corrupt its internal state.
  onComment: null,
  // Nodes have their start and end characters offsets recorded in
  // `start` and `end` properties (directly on the node, rather than
  // the `loc` object, which holds line/column data. To also add a
  // [semi-standardized][range] `range` property holding a `[start,
  // end]` array with the same numbers, set the `ranges` option to
  // `true`.
  //
  // [range]: https://bugzilla.mozilla.org/show_bug.cgi?id=745678
  ranges: false,
  // It is possible to parse multiple files into a single AST by
  // passing the tree produced by parsing the first file as
  // `program` option in subsequent parses. This will add the
  // toplevel forms of the parsed file to the `Program` (top) node
  // of an existing parse tree.
  program: null,
  // When `locations` is on, you can pass this to record the source
  // file in every node's `loc` object.
  sourceFile: null,
  // This value, if given, is stored in every node, whether
  // `locations` is on or off.
  directSourceFile: null,
  // When enabled, parenthesized expressions are represented by
  // (non-standard) ParenthesizedExpression nodes
  preserveParens: false,
  plugins: {}
};

exports.defaultOptions = defaultOptions;
// Interpret and default an options object

function getOptions(opts) {
  var options = {};
  for (var opt in defaultOptions) {
    options[opt] = opts && _util.has(opts, opt) ? opts[opt] : defaultOptions[opt];
  }if (options.allowReserved == null) options.allowReserved = options.ecmaVersion < 5;

  if (_util.isArray(options.onToken)) {
    (function () {
      var tokens = options.onToken;
      options.onToken = function (token) {
        return tokens.push(token);
      };
    })();
  }
  if (_util.isArray(options.onComment)) options.onComment = pushComment(options, options.onComment);

  return options;
}

function pushComment(options, array) {
  return function (block, text, start, end, startLoc, endLoc) {
    var comment = {
      type: block ? 'Block' : 'Line',
      value: text,
      start: start,
      end: end
    };
    if (options.locations) comment.loc = new _locutil.SourceLocation(this, startLoc, endLoc);
    if (options.ranges) comment.range = [start, end];
    array.push(comment);
  };
}

},{"./locutil":5,"./util":15}],9:[function(_dereq_,module,exports){
"use strict";

var _tokentype = _dereq_("./tokentype");

var _state = _dereq_("./state");

var _whitespace = _dereq_("./whitespace");

var pp = _state.Parser.prototype;

// ## Parser utilities

// Test whether a statement node is the string literal `"use strict"`.

pp.isUseStrict = function (stmt) {
  return this.options.ecmaVersion >= 5 && stmt.type === "ExpressionStatement" && stmt.expression.type === "Literal" && stmt.expression.raw.slice(1, -1) === "use strict";
};

// Predicate that tests whether the next token is of the given
// type, and if yes, consumes it as a side effect.

pp.eat = function (type) {
  if (this.type === type) {
    this.next();
    return true;
  } else {
    return false;
  }
};

// Tests whether parsed token is a contextual keyword.

pp.isContextual = function (name) {
  return this.type === _tokentype.types.name && this.value === name;
};

// Consumes contextual keyword if possible.

pp.eatContextual = function (name) {
  return this.value === name && this.eat(_tokentype.types.name);
};

// Asserts that following token is given contextual keyword.

pp.expectContextual = function (name) {
  if (!this.eatContextual(name)) this.unexpected();
};

// Test whether a semicolon can be inserted at the current position.

pp.canInsertSemicolon = function () {
  return this.type === _tokentype.types.eof || this.type === _tokentype.types.braceR || _whitespace.lineBreak.test(this.input.slice(this.lastTokEnd, this.start));
};

pp.insertSemicolon = function () {
  if (this.canInsertSemicolon()) {
    if (this.options.onInsertedSemicolon) this.options.onInsertedSemicolon(this.lastTokEnd, this.lastTokEndLoc);
    return true;
  }
};

// Consume a semicolon, or, failing that, see if we are allowed to
// pretend that there is a semicolon at this position.

pp.semicolon = function () {
  if (!this.eat(_tokentype.types.semi) && !this.insertSemicolon()) this.unexpected();
};

pp.afterTrailingComma = function (tokType) {
  if (this.type == tokType) {
    if (this.options.onTrailingComma) this.options.onTrailingComma(this.lastTokStart, this.lastTokStartLoc);
    this.next();
    return true;
  }
};

// Expect a token of a given type. If found, consume it, otherwise,
// raise an unexpected token error.

pp.expect = function (type) {
  this.eat(type) || this.unexpected();
};

// Raise an unexpected token error.

pp.unexpected = function (pos) {
  this.raise(pos != null ? pos : this.start, "Unexpected token");
};

pp.checkPatternErrors = function (refDestructuringErrors, andThrow) {
  var pos = refDestructuringErrors && refDestructuringErrors.trailingComma;
  if (!andThrow) return !!pos;
  if (pos) this.raise(pos, "Trailing comma is not permitted in destructuring patterns");
};

pp.checkExpressionErrors = function (refDestructuringErrors, andThrow) {
  var pos = refDestructuringErrors && refDestructuringErrors.shorthandAssign;
  if (!andThrow) return !!pos;
  if (pos) this.raise(pos, "Shorthand property assignments are valid only in destructuring patterns");
};

},{"./state":10,"./tokentype":14,"./whitespace":16}],10:[function(_dereq_,module,exports){
"use strict";

exports.__esModule = true;

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var _identifier = _dereq_("./identifier");

var _tokentype = _dereq_("./tokentype");

var _whitespace = _dereq_("./whitespace");

var _options = _dereq_("./options");

// Registered plugins
var plugins = {};

exports.plugins = plugins;
function keywordRegexp(words) {
  return new RegExp("^(" + words.replace(/ /g, "|") + ")$");
}

var Parser = (function () {
  function Parser(options, input, startPos) {
    _classCallCheck(this, Parser);

    this.options = options = _options.getOptions(options);
    this.sourceFile = options.sourceFile;
    this.keywords = keywordRegexp(_identifier.keywords[options.ecmaVersion >= 6 ? 6 : 5]);
    var reserved = options.allowReserved ? "" : _identifier.reservedWords[options.ecmaVersion] + (options.sourceType == "module" ? " await" : "");
    this.reservedWords = keywordRegexp(reserved);
    var reservedStrict = (reserved ? reserved + " " : "") + _identifier.reservedWords.strict;
    this.reservedWordsStrict = keywordRegexp(reservedStrict);
    this.reservedWordsStrictBind = keywordRegexp(reservedStrict + " " + _identifier.reservedWords.strictBind);
    this.input = String(input);

    // Used to signal to callers of `readWord1` whether the word
    // contained any escape sequences. This is needed because words with
    // escape sequences must not be interpreted as keywords.
    this.containsEsc = false;

    // Load plugins
    this.loadPlugins(options.plugins);

    // Set up token state

    // The current position of the tokenizer in the input.
    if (startPos) {
      this.pos = startPos;
      this.lineStart = Math.max(0, this.input.lastIndexOf("\n", startPos));
      this.curLine = this.input.slice(0, this.lineStart).split(_whitespace.lineBreak).length;
    } else {
      this.pos = this.lineStart = 0;
      this.curLine = 1;
    }

    // Properties of the current token:
    // Its type
    this.type = _tokentype.types.eof;
    // For tokens that include more information than their type, the value
    this.value = null;
    // Its start and end offset
    this.start = this.end = this.pos;
    // And, if locations are used, the {line, column} object
    // corresponding to those offsets
    this.startLoc = this.endLoc = this.curPosition();

    // Position information for the previous token
    this.lastTokEndLoc = this.lastTokStartLoc = null;
    this.lastTokStart = this.lastTokEnd = this.pos;

    // The context stack is used to superficially track syntactic
    // context to predict whether a regular expression is allowed in a
    // given position.
    this.context = this.initialContext();
    this.exprAllowed = true;

    // Figure out if it's a module code.
    this.strict = this.inModule = options.sourceType === "module";

    // Used to signify the start of a potential arrow function
    this.potentialArrowAt = -1;

    // Flags to track whether we are in a function, a generator.
    this.inFunction = this.inGenerator = false;
    // Labels in scope.
    this.labels = [];

    // If enabled, skip leading hashbang line.
    if (this.pos === 0 && options.allowHashBang && this.input.slice(0, 2) === '#!') this.skipLineComment(2);
  }

  // DEPRECATED Kept for backwards compatibility until 3.0 in case a plugin uses them

  Parser.prototype.isKeyword = function isKeyword(word) {
    return this.keywords.test(word);
  };

  Parser.prototype.isReservedWord = function isReservedWord(word) {
    return this.reservedWords.test(word);
  };

  Parser.prototype.extend = function extend(name, f) {
    this[name] = f(this[name]);
  };

  Parser.prototype.loadPlugins = function loadPlugins(pluginConfigs) {
    for (var _name in pluginConfigs) {
      var plugin = plugins[_name];
      if (!plugin) throw new Error("Plugin '" + _name + "' not found");
      plugin(this, pluginConfigs[_name]);
    }
  };

  Parser.prototype.parse = function parse() {
    var node = this.options.program || this.startNode();
    this.nextToken();
    return this.parseTopLevel(node);
  };

  return Parser;
})();

exports.Parser = Parser;

},{"./identifier":2,"./options":8,"./tokentype":14,"./whitespace":16}],11:[function(_dereq_,module,exports){
"use strict";

var _tokentype = _dereq_("./tokentype");

var _state = _dereq_("./state");

var _whitespace = _dereq_("./whitespace");

var pp = _state.Parser.prototype;

// ### Statement parsing

// Parse a program. Initializes the parser, reads any number of
// statements, and wraps them in a Program node.  Optionally takes a
// `program` argument.  If present, the statements will be appended
// to its body instead of creating a new node.

pp.parseTopLevel = function (node) {
  var first = true;
  if (!node.body) node.body = [];
  while (this.type !== _tokentype.types.eof) {
    var stmt = this.parseStatement(true, true);
    node.body.push(stmt);
    if (first) {
      if (this.isUseStrict(stmt)) this.setStrict(true);
      first = false;
    }
  }
  this.next();
  if (this.options.ecmaVersion >= 6) {
    node.sourceType = this.options.sourceType;
  }
  return this.finishNode(node, "Program");
};

var loopLabel = { kind: "loop" },
    switchLabel = { kind: "switch" };

// Parse a single statement.
//
// If expecting a statement and finding a slash operator, parse a
// regular expression literal. This is to handle cases like
// `if (foo) /blah/.exec(foo)`, where looking at the previous token
// does not help.

pp.parseStatement = function (declaration, topLevel) {
  var starttype = this.type,
      node = this.startNode();

  // Most types of statements are recognized by the keyword they
  // start with. Many are trivial to parse, some require a bit of
  // complexity.

  switch (starttype) {
    case _tokentype.types._break:case _tokentype.types._continue:
      return this.parseBreakContinueStatement(node, starttype.keyword);
    case _tokentype.types._debugger:
      return this.parseDebuggerStatement(node);
    case _tokentype.types._do:
      return this.parseDoStatement(node);
    case _tokentype.types._for:
      return this.parseForStatement(node);
    case _tokentype.types._function:
      if (!declaration && this.options.ecmaVersion >= 6) this.unexpected();
      return this.parseFunctionStatement(node);
    case _tokentype.types._class:
      if (!declaration) this.unexpected();
      return this.parseClass(node, true);
    case _tokentype.types._if:
      return this.parseIfStatement(node);
    case _tokentype.types._return:
      return this.parseReturnStatement(node);
    case _tokentype.types._switch:
      return this.parseSwitchStatement(node);
    case _tokentype.types._throw:
      return this.parseThrowStatement(node);
    case _tokentype.types._try:
      return this.parseTryStatement(node);
    case _tokentype.types._let:case _tokentype.types._const:
      if (!declaration) this.unexpected(); // NOTE: falls through to _var
    case _tokentype.types._var:
      return this.parseVarStatement(node, starttype);
    case _tokentype.types._while:
      return this.parseWhileStatement(node);
    case _tokentype.types._with:
      return this.parseWithStatement(node);
    case _tokentype.types.braceL:
      return this.parseBlock();
    case _tokentype.types.semi:
      return this.parseEmptyStatement(node);
    case _tokentype.types._export:
    case _tokentype.types._import:
      if (!this.options.allowImportExportEverywhere) {
        if (!topLevel) this.raise(this.start, "'import' and 'export' may only appear at the top level");
        if (!this.inModule) this.raise(this.start, "'import' and 'export' may appear only with 'sourceType: module'");
      }
      return starttype === _tokentype.types._import ? this.parseImport(node) : this.parseExport(node);

    // If the statement does not start with a statement keyword or a
    // brace, it's an ExpressionStatement or LabeledStatement. We
    // simply start parsing an expression, and afterwards, if the
    // next token is a colon and the expression was a simple
    // Identifier node, we switch to interpreting it as a label.
    default:
      var maybeName = this.value,
          expr = this.parseExpression();
      if (starttype === _tokentype.types.name && expr.type === "Identifier" && this.eat(_tokentype.types.colon)) return this.parseLabeledStatement(node, maybeName, expr);else return this.parseExpressionStatement(node, expr);
  }
};

pp.parseBreakContinueStatement = function (node, keyword) {
  var isBreak = keyword == "break";
  this.next();
  if (this.eat(_tokentype.types.semi) || this.insertSemicolon()) node.label = null;else if (this.type !== _tokentype.types.name) this.unexpected();else {
    node.label = this.parseIdent();
    this.semicolon();
  }

  // Verify that there is an actual destination to break or
  // continue to.
  for (var i = 0; i < this.labels.length; ++i) {
    var lab = this.labels[i];
    if (node.label == null || lab.name === node.label.name) {
      if (lab.kind != null && (isBreak || lab.kind === "loop")) break;
      if (node.label && isBreak) break;
    }
  }
  if (i === this.labels.length) this.raise(node.start, "Unsyntactic " + keyword);
  return this.finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement");
};

pp.parseDebuggerStatement = function (node) {
  this.next();
  this.semicolon();
  return this.finishNode(node, "DebuggerStatement");
};

pp.parseDoStatement = function (node) {
  this.next();
  this.labels.push(loopLabel);
  node.body = this.parseStatement(false);
  this.labels.pop();
  this.expect(_tokentype.types._while);
  node.test = this.parseParenExpression();
  if (this.options.ecmaVersion >= 6) this.eat(_tokentype.types.semi);else this.semicolon();
  return this.finishNode(node, "DoWhileStatement");
};

// Disambiguating between a `for` and a `for`/`in` or `for`/`of`
// loop is non-trivial. Basically, we have to parse the init `var`
// statement or expression, disallowing the `in` operator (see
// the second parameter to `parseExpression`), and then check
// whether the next token is `in` or `of`. When there is no init
// part (semicolon immediately after the opening parenthesis), it
// is a regular `for` loop.

pp.parseForStatement = function (node) {
  this.next();
  this.labels.push(loopLabel);
  this.expect(_tokentype.types.parenL);
  if (this.type === _tokentype.types.semi) return this.parseFor(node, null);
  if (this.type === _tokentype.types._var || this.type === _tokentype.types._let || this.type === _tokentype.types._const) {
    var _init = this.startNode(),
        varKind = this.type;
    this.next();
    this.parseVar(_init, true, varKind);
    this.finishNode(_init, "VariableDeclaration");
    if ((this.type === _tokentype.types._in || this.options.ecmaVersion >= 6 && this.isContextual("of")) && _init.declarations.length === 1 && !(varKind !== _tokentype.types._var && _init.declarations[0].init)) return this.parseForIn(node, _init);
    return this.parseFor(node, _init);
  }
  var refDestructuringErrors = { shorthandAssign: 0, trailingComma: 0 };
  var init = this.parseExpression(true, refDestructuringErrors);
  if (this.type === _tokentype.types._in || this.options.ecmaVersion >= 6 && this.isContextual("of")) {
    this.checkPatternErrors(refDestructuringErrors, true);
    this.toAssignable(init);
    this.checkLVal(init);
    return this.parseForIn(node, init);
  } else {
    this.checkExpressionErrors(refDestructuringErrors, true);
  }
  return this.parseFor(node, init);
};

pp.parseFunctionStatement = function (node) {
  this.next();
  return this.parseFunction(node, true);
};

pp.parseIfStatement = function (node) {
  this.next();
  node.test = this.parseParenExpression();
  node.consequent = this.parseStatement(false);
  node.alternate = this.eat(_tokentype.types._else) ? this.parseStatement(false) : null;
  return this.finishNode(node, "IfStatement");
};

pp.parseReturnStatement = function (node) {
  if (!this.inFunction && !this.options.allowReturnOutsideFunction) this.raise(this.start, "'return' outside of function");
  this.next();

  // In `return` (and `break`/`continue`), the keywords with
  // optional arguments, we eagerly look for a semicolon or the
  // possibility to insert one.

  if (this.eat(_tokentype.types.semi) || this.insertSemicolon()) node.argument = null;else {
    node.argument = this.parseExpression();this.semicolon();
  }
  return this.finishNode(node, "ReturnStatement");
};

pp.parseSwitchStatement = function (node) {
  this.next();
  node.discriminant = this.parseParenExpression();
  node.cases = [];
  this.expect(_tokentype.types.braceL);
  this.labels.push(switchLabel);

  // Statements under must be grouped (by label) in SwitchCase
  // nodes. `cur` is used to keep the node that we are currently
  // adding statements to.

  for (var cur, sawDefault = false; this.type != _tokentype.types.braceR;) {
    if (this.type === _tokentype.types._case || this.type === _tokentype.types._default) {
      var isCase = this.type === _tokentype.types._case;
      if (cur) this.finishNode(cur, "SwitchCase");
      node.cases.push(cur = this.startNode());
      cur.consequent = [];
      this.next();
      if (isCase) {
        cur.test = this.parseExpression();
      } else {
        if (sawDefault) this.raise(this.lastTokStart, "Multiple default clauses");
        sawDefault = true;
        cur.test = null;
      }
      this.expect(_tokentype.types.colon);
    } else {
      if (!cur) this.unexpected();
      cur.consequent.push(this.parseStatement(true));
    }
  }
  if (cur) this.finishNode(cur, "SwitchCase");
  this.next(); // Closing brace
  this.labels.pop();
  return this.finishNode(node, "SwitchStatement");
};

pp.parseThrowStatement = function (node) {
  this.next();
  if (_whitespace.lineBreak.test(this.input.slice(this.lastTokEnd, this.start))) this.raise(this.lastTokEnd, "Illegal newline after throw");
  node.argument = this.parseExpression();
  this.semicolon();
  return this.finishNode(node, "ThrowStatement");
};

// Reused empty array added for node fields that are always empty.

var empty = [];

pp.parseTryStatement = function (node) {
  this.next();
  node.block = this.parseBlock();
  node.handler = null;
  if (this.type === _tokentype.types._catch) {
    var clause = this.startNode();
    this.next();
    this.expect(_tokentype.types.parenL);
    clause.param = this.parseBindingAtom();
    this.checkLVal(clause.param, true);
    this.expect(_tokentype.types.parenR);
    clause.body = this.parseBlock();
    node.handler = this.finishNode(clause, "CatchClause");
  }
  node.finalizer = this.eat(_tokentype.types._finally) ? this.parseBlock() : null;
  if (!node.handler && !node.finalizer) this.raise(node.start, "Missing catch or finally clause");
  return this.finishNode(node, "TryStatement");
};

pp.parseVarStatement = function (node, kind) {
  this.next();
  this.parseVar(node, false, kind);
  this.semicolon();
  return this.finishNode(node, "VariableDeclaration");
};

pp.parseWhileStatement = function (node) {
  this.next();
  node.test = this.parseParenExpression();
  this.labels.push(loopLabel);
  node.body = this.parseStatement(false);
  this.labels.pop();
  return this.finishNode(node, "WhileStatement");
};

pp.parseWithStatement = function (node) {
  if (this.strict) this.raise(this.start, "'with' in strict mode");
  this.next();
  node.object = this.parseParenExpression();
  node.body = this.parseStatement(false);
  return this.finishNode(node, "WithStatement");
};

pp.parseEmptyStatement = function (node) {
  this.next();
  return this.finishNode(node, "EmptyStatement");
};

pp.parseLabeledStatement = function (node, maybeName, expr) {
  for (var i = 0; i < this.labels.length; ++i) {
    if (this.labels[i].name === maybeName) this.raise(expr.start, "Label '" + maybeName + "' is already declared");
  }var kind = this.type.isLoop ? "loop" : this.type === _tokentype.types._switch ? "switch" : null;
  for (var i = this.labels.length - 1; i >= 0; i--) {
    var label = this.labels[i];
    if (label.statementStart == node.start) {
      label.statementStart = this.start;
      label.kind = kind;
    } else break;
  }
  this.labels.push({ name: maybeName, kind: kind, statementStart: this.start });
  node.body = this.parseStatement(true);
  this.labels.pop();
  node.label = expr;
  return this.finishNode(node, "LabeledStatement");
};

pp.parseExpressionStatement = function (node, expr) {
  node.expression = expr;
  this.semicolon();
  return this.finishNode(node, "ExpressionStatement");
};

// Parse a semicolon-enclosed block of statements, handling `"use
// strict"` declarations when `allowStrict` is true (used for
// function bodies).

pp.parseBlock = function (allowStrict) {
  var node = this.startNode(),
      first = true,
      oldStrict = undefined;
  node.body = [];
  this.expect(_tokentype.types.braceL);
  while (!this.eat(_tokentype.types.braceR)) {
    var stmt = this.parseStatement(true);
    node.body.push(stmt);
    if (first && allowStrict && this.isUseStrict(stmt)) {
      oldStrict = this.strict;
      this.setStrict(this.strict = true);
    }
    first = false;
  }
  if (oldStrict === false) this.setStrict(false);
  return this.finishNode(node, "BlockStatement");
};

// Parse a regular `for` loop. The disambiguation code in
// `parseStatement` will already have parsed the init statement or
// expression.

pp.parseFor = function (node, init) {
  node.init = init;
  this.expect(_tokentype.types.semi);
  node.test = this.type === _tokentype.types.semi ? null : this.parseExpression();
  this.expect(_tokentype.types.semi);
  node.update = this.type === _tokentype.types.parenR ? null : this.parseExpression();
  this.expect(_tokentype.types.parenR);
  node.body = this.parseStatement(false);
  this.labels.pop();
  return this.finishNode(node, "ForStatement");
};

// Parse a `for`/`in` and `for`/`of` loop, which are almost
// same from parser's perspective.

pp.parseForIn = function (node, init) {
  var type = this.type === _tokentype.types._in ? "ForInStatement" : "ForOfStatement";
  this.next();
  node.left = init;
  node.right = this.parseExpression();
  this.expect(_tokentype.types.parenR);
  node.body = this.parseStatement(false);
  this.labels.pop();
  return this.finishNode(node, type);
};

// Parse a list of variable declarations.

pp.parseVar = function (node, isFor, kind) {
  node.declarations = [];
  node.kind = kind.keyword;
  for (;;) {
    var decl = this.startNode();
    this.parseVarId(decl);
    if (this.eat(_tokentype.types.eq)) {
      decl.init = this.parseMaybeAssign(isFor);
    } else if (kind === _tokentype.types._const && !(this.type === _tokentype.types._in || this.options.ecmaVersion >= 6 && this.isContextual("of"))) {
      this.unexpected();
    } else if (decl.id.type != "Identifier" && !(isFor && (this.type === _tokentype.types._in || this.isContextual("of")))) {
      this.raise(this.lastTokEnd, "Complex binding patterns require an initialization value");
    } else {
      decl.init = null;
    }
    node.declarations.push(this.finishNode(decl, "VariableDeclarator"));
    if (!this.eat(_tokentype.types.comma)) break;
  }
  return node;
};

pp.parseVarId = function (decl) {
  decl.id = this.parseBindingAtom();
  this.checkLVal(decl.id, true);
};

// Parse a function declaration or literal (depending on the
// `isStatement` parameter).

pp.parseFunction = function (node, isStatement, allowExpressionBody) {
  this.initFunction(node);
  if (this.options.ecmaVersion >= 6) node.generator = this.eat(_tokentype.types.star);
  if (isStatement || this.type === _tokentype.types.name) node.id = this.parseIdent();
  this.parseFunctionParams(node);
  this.parseFunctionBody(node, allowExpressionBody);
  return this.finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression");
};

pp.parseFunctionParams = function (node) {
  this.expect(_tokentype.types.parenL);
  node.params = this.parseBindingList(_tokentype.types.parenR, false, false, true);
};

// Parse a class declaration or literal (depending on the
// `isStatement` parameter).

pp.parseClass = function (node, isStatement) {
  this.next();
  this.parseClassId(node, isStatement);
  this.parseClassSuper(node);
  var classBody = this.startNode();
  var hadConstructor = false;
  classBody.body = [];
  this.expect(_tokentype.types.braceL);
  while (!this.eat(_tokentype.types.braceR)) {
    if (this.eat(_tokentype.types.semi)) continue;
    var method = this.startNode();
    var isGenerator = this.eat(_tokentype.types.star);
    var isMaybeStatic = this.type === _tokentype.types.name && this.value === "static";
    this.parsePropertyName(method);
    method["static"] = isMaybeStatic && this.type !== _tokentype.types.parenL;
    if (method["static"]) {
      if (isGenerator) this.unexpected();
      isGenerator = this.eat(_tokentype.types.star);
      this.parsePropertyName(method);
    }
    method.kind = "method";
    var isGetSet = false;
    if (!method.computed) {
      var key = method.key;

      if (!isGenerator && key.type === "Identifier" && this.type !== _tokentype.types.parenL && (key.name === "get" || key.name === "set")) {
        isGetSet = true;
        method.kind = key.name;
        key = this.parsePropertyName(method);
      }
      if (!method["static"] && (key.type === "Identifier" && key.name === "constructor" || key.type === "Literal" && key.value === "constructor")) {
        if (hadConstructor) this.raise(key.start, "Duplicate constructor in the same class");
        if (isGetSet) this.raise(key.start, "Constructor can't have get/set modifier");
        if (isGenerator) this.raise(key.start, "Constructor can't be a generator");
        method.kind = "constructor";
        hadConstructor = true;
      }
    }
    this.parseClassMethod(classBody, method, isGenerator);
    if (isGetSet) {
      var paramCount = method.kind === "get" ? 0 : 1;
      if (method.value.params.length !== paramCount) {
        var start = method.value.start;
        if (method.kind === "get") this.raise(start, "getter should have no params");else this.raise(start, "setter should have exactly one param");
      }
    }
  }
  node.body = this.finishNode(classBody, "ClassBody");
  return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression");
};

pp.parseClassMethod = function (classBody, method, isGenerator) {
  method.value = this.parseMethod(isGenerator);
  classBody.body.push(this.finishNode(method, "MethodDefinition"));
};

pp.parseClassId = function (node, isStatement) {
  node.id = this.type === _tokentype.types.name ? this.parseIdent() : isStatement ? this.unexpected() : null;
};

pp.parseClassSuper = function (node) {
  node.superClass = this.eat(_tokentype.types._extends) ? this.parseExprSubscripts() : null;
};

// Parses module export declaration.

pp.parseExport = function (node) {
  this.next();
  // export * from '...'
  if (this.eat(_tokentype.types.star)) {
    this.expectContextual("from");
    node.source = this.type === _tokentype.types.string ? this.parseExprAtom() : this.unexpected();
    this.semicolon();
    return this.finishNode(node, "ExportAllDeclaration");
  }
  if (this.eat(_tokentype.types._default)) {
    // export default ...
    var expr = this.parseMaybeAssign();
    var needsSemi = true;
    if (expr.type == "FunctionExpression" || expr.type == "ClassExpression") {
      needsSemi = false;
      if (expr.id) {
        expr.type = expr.type == "FunctionExpression" ? "FunctionDeclaration" : "ClassDeclaration";
      }
    }
    node.declaration = expr;
    if (needsSemi) this.semicolon();
    return this.finishNode(node, "ExportDefaultDeclaration");
  }
  // export var|const|let|function|class ...
  if (this.shouldParseExportStatement()) {
    node.declaration = this.parseStatement(true);
    node.specifiers = [];
    node.source = null;
  } else {
    // export { x, y as z } [from '...']
    node.declaration = null;
    node.specifiers = this.parseExportSpecifiers();
    if (this.eatContextual("from")) {
      node.source = this.type === _tokentype.types.string ? this.parseExprAtom() : this.unexpected();
    } else {
      // check for keywords used as local names
      for (var i = 0; i < node.specifiers.length; i++) {
        if (this.keywords.test(node.specifiers[i].local.name) || this.reservedWords.test(node.specifiers[i].local.name)) {
          this.unexpected(node.specifiers[i].local.start);
        }
      }

      node.source = null;
    }
    this.semicolon();
  }
  return this.finishNode(node, "ExportNamedDeclaration");
};

pp.shouldParseExportStatement = function () {
  return this.type.keyword;
};

// Parses a comma-separated list of module exports.

pp.parseExportSpecifiers = function () {
  var nodes = [],
      first = true;
  // export { x, y as z } [from '...']
  this.expect(_tokentype.types.braceL);
  while (!this.eat(_tokentype.types.braceR)) {
    if (!first) {
      this.expect(_tokentype.types.comma);
      if (this.afterTrailingComma(_tokentype.types.braceR)) break;
    } else first = false;

    var node = this.startNode();
    node.local = this.parseIdent(this.type === _tokentype.types._default);
    node.exported = this.eatContextual("as") ? this.parseIdent(true) : node.local;
    nodes.push(this.finishNode(node, "ExportSpecifier"));
  }
  return nodes;
};

// Parses import declaration.

pp.parseImport = function (node) {
  this.next();
  // import '...'
  if (this.type === _tokentype.types.string) {
    node.specifiers = empty;
    node.source = this.parseExprAtom();
  } else {
    node.specifiers = this.parseImportSpecifiers();
    this.expectContextual("from");
    node.source = this.type === _tokentype.types.string ? this.parseExprAtom() : this.unexpected();
  }
  this.semicolon();
  return this.finishNode(node, "ImportDeclaration");
};

// Parses a comma-separated list of module imports.

pp.parseImportSpecifiers = function () {
  var nodes = [],
      first = true;
  if (this.type === _tokentype.types.name) {
    // import defaultObj, { x, y as z } from '...'
    var node = this.startNode();
    node.local = this.parseIdent();
    this.checkLVal(node.local, true);
    nodes.push(this.finishNode(node, "ImportDefaultSpecifier"));
    if (!this.eat(_tokentype.types.comma)) return nodes;
  }
  if (this.type === _tokentype.types.star) {
    var node = this.startNode();
    this.next();
    this.expectContextual("as");
    node.local = this.parseIdent();
    this.checkLVal(node.local, true);
    nodes.push(this.finishNode(node, "ImportNamespaceSpecifier"));
    return nodes;
  }
  this.expect(_tokentype.types.braceL);
  while (!this.eat(_tokentype.types.braceR)) {
    if (!first) {
      this.expect(_tokentype.types.comma);
      if (this.afterTrailingComma(_tokentype.types.braceR)) break;
    } else first = false;

    var node = this.startNode();
    node.imported = this.parseIdent(true);
    node.local = this.eatContextual("as") ? this.parseIdent() : node.imported;
    this.checkLVal(node.local, true);
    nodes.push(this.finishNode(node, "ImportSpecifier"));
  }
  return nodes;
};

},{"./state":10,"./tokentype":14,"./whitespace":16}],12:[function(_dereq_,module,exports){
// The algorithm used to determine whether a regexp can appear at a
// given point in the program is loosely based on sweet.js' approach.
// See https://github.com/mozilla/sweet.js/wiki/design

"use strict";

exports.__esModule = true;

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var _state = _dereq_("./state");

var _tokentype = _dereq_("./tokentype");

var _whitespace = _dereq_("./whitespace");

var TokContext = function TokContext(token, isExpr, preserveSpace, override) {
  _classCallCheck(this, TokContext);

  this.token = token;
  this.isExpr = !!isExpr;
  this.preserveSpace = !!preserveSpace;
  this.override = override;
};

exports.TokContext = TokContext;
var types = {
  b_stat: new TokContext("{", false),
  b_expr: new TokContext("{", true),
  b_tmpl: new TokContext("${", true),
  p_stat: new TokContext("(", false),
  p_expr: new TokContext("(", true),
  q_tmpl: new TokContext("`", true, true, function (p) {
    return p.readTmplToken();
  }),
  f_expr: new TokContext("function", true)
};

exports.types = types;
var pp = _state.Parser.prototype;

pp.initialContext = function () {
  return [types.b_stat];
};

pp.braceIsBlock = function (prevType) {
  if (prevType === _tokentype.types.colon) {
    var _parent = this.curContext();
    if (_parent === types.b_stat || _parent === types.b_expr) return !_parent.isExpr;
  }
  if (prevType === _tokentype.types._return) return _whitespace.lineBreak.test(this.input.slice(this.lastTokEnd, this.start));
  if (prevType === _tokentype.types._else || prevType === _tokentype.types.semi || prevType === _tokentype.types.eof || prevType === _tokentype.types.parenR) return true;
  if (prevType == _tokentype.types.braceL) return this.curContext() === types.b_stat;
  return !this.exprAllowed;
};

pp.updateContext = function (prevType) {
  var update = undefined,
      type = this.type;
  if (type.keyword && prevType == _tokentype.types.dot) this.exprAllowed = false;else if (update = type.updateContext) update.call(this, prevType);else this.exprAllowed = type.beforeExpr;
};

// Token-specific context update code

_tokentype.types.parenR.updateContext = _tokentype.types.braceR.updateContext = function () {
  if (this.context.length == 1) {
    this.exprAllowed = true;
    return;
  }
  var out = this.context.pop();
  if (out === types.b_stat && this.curContext() === types.f_expr) {
    this.context.pop();
    this.exprAllowed = false;
  } else if (out === types.b_tmpl) {
    this.exprAllowed = true;
  } else {
    this.exprAllowed = !out.isExpr;
  }
};

_tokentype.types.braceL.updateContext = function (prevType) {
  this.context.push(this.braceIsBlock(prevType) ? types.b_stat : types.b_expr);
  this.exprAllowed = true;
};

_tokentype.types.dollarBraceL.updateContext = function () {
  this.context.push(types.b_tmpl);
  this.exprAllowed = true;
};

_tokentype.types.parenL.updateContext = function (prevType) {
  var statementParens = prevType === _tokentype.types._if || prevType === _tokentype.types._for || prevType === _tokentype.types._with || prevType === _tokentype.types._while;
  this.context.push(statementParens ? types.p_stat : types.p_expr);
  this.exprAllowed = true;
};

_tokentype.types.incDec.updateContext = function () {
  // tokExprAllowed stays unchanged
};

_tokentype.types._function.updateContext = function () {
  if (this.curContext() !== types.b_stat) this.context.push(types.f_expr);
  this.exprAllowed = false;
};

_tokentype.types.backQuote.updateContext = function () {
  if (this.curContext() === types.q_tmpl) this.context.pop();else this.context.push(types.q_tmpl);
  this.exprAllowed = false;
};

},{"./state":10,"./tokentype":14,"./whitespace":16}],13:[function(_dereq_,module,exports){
"use strict";

exports.__esModule = true;

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var _identifier = _dereq_("./identifier");

var _tokentype = _dereq_("./tokentype");

var _state = _dereq_("./state");

var _locutil = _dereq_("./locutil");

var _whitespace = _dereq_("./whitespace");

// Object type used to represent tokens. Note that normally, tokens
// simply exist as properties on the parser object. This is only
// used for the onToken callback and the external tokenizer.

var Token = function Token(p) {
  _classCallCheck(this, Token);

  this.type = p.type;
  this.value = p.value;
  this.start = p.start;
  this.end = p.end;
  if (p.options.locations) this.loc = new _locutil.SourceLocation(p, p.startLoc, p.endLoc);
  if (p.options.ranges) this.range = [p.start, p.end];
}

// ## Tokenizer

;

exports.Token = Token;
var pp = _state.Parser.prototype;

// Are we running under Rhino?
var isRhino = typeof Packages == "object" && Object.prototype.toString.call(Packages) == "[object JavaPackage]";

// Move to the next token

pp.next = function () {
  if (this.options.onToken) this.options.onToken(new Token(this));

  this.lastTokEnd = this.end;
  this.lastTokStart = this.start;
  this.lastTokEndLoc = this.endLoc;
  this.lastTokStartLoc = this.startLoc;
  this.nextToken();
};

pp.getToken = function () {
  this.next();
  return new Token(this);
};

// If we're in an ES6 environment, make parsers iterable
if (typeof Symbol !== "undefined") pp[Symbol.iterator] = function () {
  var self = this;
  return { next: function next() {
      var token = self.getToken();
      return {
        done: token.type === _tokentype.types.eof,
        value: token
      };
    } };
};

// Toggle strict mode. Re-reads the next number or string to please
// pedantic tests (`"use strict"; 010;` should fail).

pp.setStrict = function (strict) {
  this.strict = strict;
  if (this.type !== _tokentype.types.num && this.type !== _tokentype.types.string) return;
  this.pos = this.start;
  if (this.options.locations) {
    while (this.pos < this.lineStart) {
      this.lineStart = this.input.lastIndexOf("\n", this.lineStart - 2) + 1;
      --this.curLine;
    }
  }
  this.nextToken();
};

pp.curContext = function () {
  return this.context[this.context.length - 1];
};

// Read a single token, updating the parser object's token-related
// properties.

pp.nextToken = function () {
  var curContext = this.curContext();
  if (!curContext || !curContext.preserveSpace) this.skipSpace();

  this.start = this.pos;
  if (this.options.locations) this.startLoc = this.curPosition();
  if (this.pos >= this.input.length) return this.finishToken(_tokentype.types.eof);

  if (curContext.override) return curContext.override(this);else this.readToken(this.fullCharCodeAtPos());
};

pp.readToken = function (code) {
  // Identifier or keyword. '\uXXXX' sequences are allowed in
  // identifiers, so '\' also dispatches to that.
  if (_identifier.isIdentifierStart(code, this.options.ecmaVersion >= 6) || code === 92 /* '\' */) return this.readWord();

  return this.getTokenFromCode(code);
};

pp.fullCharCodeAtPos = function () {
  var code = this.input.charCodeAt(this.pos);
  if (code <= 0xd7ff || code >= 0xe000) return code;
  var next = this.input.charCodeAt(this.pos + 1);
  return (code << 10) + next - 0x35fdc00;
};

pp.skipBlockComment = function () {
  var startLoc = this.options.onComment && this.curPosition();
  var start = this.pos,
      end = this.input.indexOf("*/", this.pos += 2);
  if (end === -1) this.raise(this.pos - 2, "Unterminated comment");
  this.pos = end + 2;
  if (this.options.locations) {
    _whitespace.lineBreakG.lastIndex = start;
    var match = undefined;
    while ((match = _whitespace.lineBreakG.exec(this.input)) && match.index < this.pos) {
      ++this.curLine;
      this.lineStart = match.index + match[0].length;
    }
  }
  if (this.options.onComment) this.options.onComment(true, this.input.slice(start + 2, end), start, this.pos, startLoc, this.curPosition());
};

pp.skipLineComment = function (startSkip) {
  var start = this.pos;
  var startLoc = this.options.onComment && this.curPosition();
  var ch = this.input.charCodeAt(this.pos += startSkip);
  while (this.pos < this.input.length && ch !== 10 && ch !== 13 && ch !== 8232 && ch !== 8233) {
    ++this.pos;
    ch = this.input.charCodeAt(this.pos);
  }
  if (this.options.onComment) this.options.onComment(false, this.input.slice(start + startSkip, this.pos), start, this.pos, startLoc, this.curPosition());
};

// Called at the start of the parse and after every token. Skips
// whitespace and comments, and.

pp.skipSpace = function () {
  loop: while (this.pos < this.input.length) {
    var ch = this.input.charCodeAt(this.pos);
    switch (ch) {
      case 32:case 160:
        // ' '
        ++this.pos;
        break;
      case 13:
        if (this.input.charCodeAt(this.pos + 1) === 10) {
          ++this.pos;
        }
      case 10:case 8232:case 8233:
        ++this.pos;
        if (this.options.locations) {
          ++this.curLine;
          this.lineStart = this.pos;
        }
        break;
      case 47:
        // '/'
        switch (this.input.charCodeAt(this.pos + 1)) {
          case 42:
            // '*'
            this.skipBlockComment();
            break;
          case 47:
            this.skipLineComment(2);
            break;
          default:
            break loop;
        }
        break;
      default:
        if (ch > 8 && ch < 14 || ch >= 5760 && _whitespace.nonASCIIwhitespace.test(String.fromCharCode(ch))) {
          ++this.pos;
        } else {
          break loop;
        }
    }
  }
};

// Called at the end of every token. Sets `end`, `val`, and
// maintains `context` and `exprAllowed`, and skips the space after
// the token, so that the next one's `start` will point at the
// right position.

pp.finishToken = function (type, val) {
  this.end = this.pos;
  if (this.options.locations) this.endLoc = this.curPosition();
  var prevType = this.type;
  this.type = type;
  this.value = val;

  this.updateContext(prevType);
};

// ### Token reading

// This is the function that is called to fetch the next token. It
// is somewhat obscure, because it works in character codes rather
// than characters, and because operator parsing has been inlined
// into it.
//
// All in the name of speed.
//
pp.readToken_dot = function () {
  var next = this.input.charCodeAt(this.pos + 1);
  if (next >= 48 && next <= 57) return this.readNumber(true);
  var next2 = this.input.charCodeAt(this.pos + 2);
  if (this.options.ecmaVersion >= 6 && next === 46 && next2 === 46) {
    // 46 = dot '.'
    this.pos += 3;
    return this.finishToken(_tokentype.types.ellipsis);
  } else {
    ++this.pos;
    return this.finishToken(_tokentype.types.dot);
  }
};

pp.readToken_slash = function () {
  // '/'
  var next = this.input.charCodeAt(this.pos + 1);
  if (this.exprAllowed) {
    ++this.pos;return this.readRegexp();
  }
  if (next === 61) return this.finishOp(_tokentype.types.assign, 2);
  return this.finishOp(_tokentype.types.slash, 1);
};

pp.readToken_mult_modulo = function (code) {
  // '%*'
  var next = this.input.charCodeAt(this.pos + 1);
  if (next === 61) return this.finishOp(_tokentype.types.assign, 2);
  return this.finishOp(code === 42 ? _tokentype.types.star : _tokentype.types.modulo, 1);
};

pp.readToken_pipe_amp = function (code) {
  // '|&'
  var next = this.input.charCodeAt(this.pos + 1);
  if (next === code) return this.finishOp(code === 124 ? _tokentype.types.logicalOR : _tokentype.types.logicalAND, 2);
  if (next === 61) return this.finishOp(_tokentype.types.assign, 2);
  return this.finishOp(code === 124 ? _tokentype.types.bitwiseOR : _tokentype.types.bitwiseAND, 1);
};

pp.readToken_caret = function () {
  // '^'
  var next = this.input.charCodeAt(this.pos + 1);
  if (next === 61) return this.finishOp(_tokentype.types.assign, 2);
  return this.finishOp(_tokentype.types.bitwiseXOR, 1);
};

pp.readToken_plus_min = function (code) {
  // '+-'
  var next = this.input.charCodeAt(this.pos + 1);
  if (next === code) {
    if (next == 45 && this.input.charCodeAt(this.pos + 2) == 62 && _whitespace.lineBreak.test(this.input.slice(this.lastTokEnd, this.pos))) {
      // A `-->` line comment
      this.skipLineComment(3);
      this.skipSpace();
      return this.nextToken();
    }
    return this.finishOp(_tokentype.types.incDec, 2);
  }
  if (next === 61) return this.finishOp(_tokentype.types.assign, 2);
  return this.finishOp(_tokentype.types.plusMin, 1);
};

pp.readToken_lt_gt = function (code) {
  // '<>'
  var next = this.input.charCodeAt(this.pos + 1);
  var size = 1;
  if (next === code) {
    size = code === 62 && this.input.charCodeAt(this.pos + 2) === 62 ? 3 : 2;
    if (this.input.charCodeAt(this.pos + size) === 61) return this.finishOp(_tokentype.types.assign, size + 1);
    return this.finishOp(_tokentype.types.bitShift, size);
  }
  if (next == 33 && code == 60 && this.input.charCodeAt(this.pos + 2) == 45 && this.input.charCodeAt(this.pos + 3) == 45) {
    if (this.inModule) this.unexpected();
    // `<!--`, an XML-style comment that should be interpreted as a line comment
    this.skipLineComment(4);
    this.skipSpace();
    return this.nextToken();
  }
  if (next === 61) size = this.input.charCodeAt(this.pos + 2) === 61 ? 3 : 2;
  return this.finishOp(_tokentype.types.relational, size);
};

pp.readToken_eq_excl = function (code) {
  // '=!'
  var next = this.input.charCodeAt(this.pos + 1);
  if (next === 61) return this.finishOp(_tokentype.types.equality, this.input.charCodeAt(this.pos + 2) === 61 ? 3 : 2);
  if (code === 61 && next === 62 && this.options.ecmaVersion >= 6) {
    // '=>'
    this.pos += 2;
    return this.finishToken(_tokentype.types.arrow);
  }
  return this.finishOp(code === 61 ? _tokentype.types.eq : _tokentype.types.prefix, 1);
};

pp.getTokenFromCode = function (code) {
  switch (code) {
    // The interpretation of a dot depends on whether it is followed
    // by a digit or another two dots.
    case 46:
      // '.'
      return this.readToken_dot();

    // Punctuation tokens.
    case 40:
      ++this.pos;return this.finishToken(_tokentype.types.parenL);
    case 41:
      ++this.pos;return this.finishToken(_tokentype.types.parenR);
    case 59:
      ++this.pos;return this.finishToken(_tokentype.types.semi);
    case 44:
      ++this.pos;return this.finishToken(_tokentype.types.comma);
    case 91:
      ++this.pos;return this.finishToken(_tokentype.types.bracketL);
    case 93:
      ++this.pos;return this.finishToken(_tokentype.types.bracketR);
    case 123:
      ++this.pos;return this.finishToken(_tokentype.types.braceL);
    case 125:
      ++this.pos;return this.finishToken(_tokentype.types.braceR);
    case 58:
      ++this.pos;return this.finishToken(_tokentype.types.colon);
    case 63:
      ++this.pos;return this.finishToken(_tokentype.types.question);

    case 96:
      // '`'
      if (this.options.ecmaVersion < 6) break;
      ++this.pos;
      return this.finishToken(_tokentype.types.backQuote);

    case 48:
      // '0'
      var next = this.input.charCodeAt(this.pos + 1);
      if (next === 120 || next === 88) return this.readRadixNumber(16); // '0x', '0X' - hex number
      if (this.options.ecmaVersion >= 6) {
        if (next === 111 || next === 79) return this.readRadixNumber(8); // '0o', '0O' - octal number
        if (next === 98 || next === 66) return this.readRadixNumber(2); // '0b', '0B' - binary number
      }
    // Anything else beginning with a digit is an integer, octal
    // number, or float.
    case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 57:
      // 1-9
      return this.readNumber(false);

    // Quotes produce strings.
    case 34:case 39:
      // '"', "'"
      return this.readString(code);

    // Operators are parsed inline in tiny state machines. '=' (61) is
    // often referred to. `finishOp` simply skips the amount of
    // characters it is given as second argument, and returns a token
    // of the type given by its first argument.

    case 47:
      // '/'
      return this.readToken_slash();

    case 37:case 42:
      // '%*'
      return this.readToken_mult_modulo(code);

    case 124:case 38:
      // '|&'
      return this.readToken_pipe_amp(code);

    case 94:
      // '^'
      return this.readToken_caret();

    case 43:case 45:
      // '+-'
      return this.readToken_plus_min(code);

    case 60:case 62:
      // '<>'
      return this.readToken_lt_gt(code);

    case 61:case 33:
      // '=!'
      return this.readToken_eq_excl(code);

    case 126:
      // '~'
      return this.finishOp(_tokentype.types.prefix, 1);
  }

  this.raise(this.pos, "Unexpected character '" + codePointToString(code) + "'");
};

pp.finishOp = function (type, size) {
  var str = this.input.slice(this.pos, this.pos + size);
  this.pos += size;
  return this.finishToken(type, str);
};

// Parse a regular expression. Some context-awareness is necessary,
// since a '/' inside a '[]' set does not end the expression.

function tryCreateRegexp(src, flags, throwErrorAt, parser) {
  try {
    return new RegExp(src, flags);
  } catch (e) {
    if (throwErrorAt !== undefined) {
      if (e instanceof SyntaxError) parser.raise(throwErrorAt, "Error parsing regular expression: " + e.message);
      throw e;
    }
  }
}

var regexpUnicodeSupport = !!tryCreateRegexp("￿", "u");

pp.readRegexp = function () {
  var _this = this;

  var escaped = undefined,
      inClass = undefined,
      start = this.pos;
  for (;;) {
    if (this.pos >= this.input.length) this.raise(start, "Unterminated regular expression");
    var ch = this.input.charAt(this.pos);
    if (_whitespace.lineBreak.test(ch)) this.raise(start, "Unterminated regular expression");
    if (!escaped) {
      if (ch === "[") inClass = true;else if (ch === "]" && inClass) inClass = false;else if (ch === "/" && !inClass) break;
      escaped = ch === "\\";
    } else escaped = false;
    ++this.pos;
  }
  var content = this.input.slice(start, this.pos);
  ++this.pos;
  // Need to use `readWord1` because '\uXXXX' sequences are allowed
  // here (don't ask).
  var mods = this.readWord1();
  var tmp = content;
  if (mods) {
    var validFlags = /^[gmsiy]*$/;
    if (this.options.ecmaVersion >= 6) validFlags = /^[gmsiyu]*$/;
    if (!validFlags.test(mods)) this.raise(start, "Invalid regular expression flag");
    if (mods.includes('u') && !regexpUnicodeSupport) {
      // Replace each astral symbol and every Unicode escape sequence that
      // possibly represents an astral symbol or a paired surrogate with a
      // single ASCII symbol to avoid throwing on regular expressions that
      // are only valid in combination with the `/u` flag.
      // Note: replacing with the ASCII symbol `x` might cause false
      // negatives in unlikely scenarios. For example, `[\u{61}-b]` is a
      // perfectly valid pattern that is equivalent to `[a-b]`, but it would
      // be replaced by `[x-b]` which throws an error.
      tmp = tmp.replace(/\\u\{([0-9a-fA-F]+)\}/g, function (_match, code, offset) {
        code = Number("0x" + code);
        if (code > 0x10FFFF) _this.raise(start + offset + 3, "Code point out of bounds");
        return "x";
      });
      tmp = tmp.replace(/\\u([a-fA-F0-9]{4})|[\uD800-\uDBFF][\uDC00-\uDFFF]/g, "x");
    }
  }
  // Detect invalid regular expressions.
  var value = null;
  // Rhino's regular expression parser is flaky and throws uncatchable exceptions,
  // so don't do detection if we are running under Rhino
  if (!isRhino) {
    tryCreateRegexp(tmp, undefined, start, this);
    // Get a regular expression object for this pattern-flag pair, or `null` in
    // case the current environment doesn't support the flags it uses.
    value = tryCreateRegexp(content, mods);
  }
  return this.finishToken(_tokentype.types.regexp, { pattern: content, flags: mods, value: value });
};

// Read an integer in the given radix. Return null if zero digits
// were read, the integer value otherwise. When `len` is given, this
// will return `null` unless the integer has exactly `len` digits.

pp.readInt = function (radix, len) {
  var start = this.pos,
      total = 0;
  for (var i = 0, e = len == null ? Infinity : len; i < e; ++i) {
    var code = this.input.charCodeAt(this.pos),
        val = undefined;
    if (code >= 97) val = code - 97 + 10; // a
    else if (code >= 65) val = code - 65 + 10; // A
      else if (code >= 48 && code <= 57) val = code - 48; // 0-9
        else val = Infinity;
    if (val >= radix) break;
    ++this.pos;
    total = total * radix + val;
  }
  if (this.pos === start || len != null && this.pos - start !== len) return null;

  return total;
};

pp.readRadixNumber = function (radix) {
  this.pos += 2; // 0x
  var val = this.readInt(radix);
  if (val == null) this.raise(this.start + 2, "Expected number in radix " + radix);
  if (_identifier.isIdentifierStart(this.fullCharCodeAtPos())) this.raise(this.pos, "Identifier directly after number");
  return this.finishToken(_tokentype.types.num, val);
};

// Read an integer, octal integer, or floating-point number.

pp.readNumber = function (startsWithDot) {
  var start = this.pos,
      isFloat = false,
      octal = this.input.charCodeAt(this.pos) === 48;
  if (!startsWithDot && this.readInt(10) === null) this.raise(start, "Invalid number");
  var next = this.input.charCodeAt(this.pos);
  if (next === 46) {
    // '.'
    ++this.pos;
    this.readInt(10);
    isFloat = true;
    next = this.input.charCodeAt(this.pos);
  }
  if (next === 69 || next === 101) {
    // 'eE'
    next = this.input.charCodeAt(++this.pos);
    if (next === 43 || next === 45) ++this.pos; // '+-'
    if (this.readInt(10) === null) this.raise(start, "Invalid number");
    isFloat = true;
  }
  if (_identifier.isIdentifierStart(this.fullCharCodeAtPos())) this.raise(this.pos, "Identifier directly after number");

  var str = this.input.slice(start, this.pos),
      val = undefined;
  if (isFloat) val = parseFloat(str);else if (!octal || str.length === 1) val = parseInt(str, 10);else if (/[89]/.test(str) || this.strict) this.raise(start, "Invalid number");else val = parseInt(str, 8);
  return this.finishToken(_tokentype.types.num, val);
};

// Read a string value, interpreting backslash-escapes.

pp.readCodePoint = function () {
  var ch = this.input.charCodeAt(this.pos),
      code = undefined;

  if (ch === 123) {
    if (this.options.ecmaVersion < 6) this.unexpected();
    var codePos = ++this.pos;
    code = this.readHexChar(this.input.indexOf('}', this.pos) - this.pos);
    ++this.pos;
    if (code > 0x10FFFF) this.raise(codePos, "Code point out of bounds");
  } else {
    code = this.readHexChar(4);
  }
  return code;
};

function codePointToString(code) {
  // UTF-16 Decoding
  if (code <= 0xFFFF) return String.fromCharCode(code);
  code -= 0x10000;
  return String.fromCharCode((code >> 10) + 0xD800, (code & 1023) + 0xDC00);
}

pp.readString = function (quote) {
  var out = "",
      chunkStart = ++this.pos;
  for (;;) {
    if (this.pos >= this.input.length) this.raise(this.start, "Unterminated string constant");
    var ch = this.input.charCodeAt(this.pos);
    if (ch === quote) break;
    if (ch === 92) {
      // '\'
      out += this.input.slice(chunkStart, this.pos);
      out += this.readEscapedChar(false);
      chunkStart = this.pos;
    } else {
      if (_whitespace.isNewLine(ch)) this.raise(this.start, "Unterminated string constant");
      ++this.pos;
    }
  }
  out += this.input.slice(chunkStart, this.pos++);
  return this.finishToken(_tokentype.types.string, out);
};

// Reads template string tokens.

pp.readTmplToken = function () {
  var out = "",
      chunkStart = this.pos;
  for (;;) {
    if (this.pos >= this.input.length) this.raise(this.start, "Unterminated template");
    var ch = this.input.charCodeAt(this.pos);
    if (ch === 96 || ch === 36 && this.input.charCodeAt(this.pos + 1) === 123) {
      // '`', '${'
      if (this.pos === this.start && this.type === _tokentype.types.template) {
        if (ch === 36) {
          this.pos += 2;
          return this.finishToken(_tokentype.types.dollarBraceL);
        } else {
          ++this.pos;
          return this.finishToken(_tokentype.types.backQuote);
        }
      }
      out += this.input.slice(chunkStart, this.pos);
      return this.finishToken(_tokentype.types.template, out);
    }
    if (ch === 92) {
      // '\'
      out += this.input.slice(chunkStart, this.pos);
      out += this.readEscapedChar(true);
      chunkStart = this.pos;
    } else if (_whitespace.isNewLine(ch)) {
      out += this.input.slice(chunkStart, this.pos);
      ++this.pos;
      switch (ch) {
        case 13:
          if (this.input.charCodeAt(this.pos) === 10) ++this.pos;
        case 10:
          out += "\n";
          break;
        default:
          out += String.fromCharCode(ch);
          break;
      }
      if (this.options.locations) {
        ++this.curLine;
        this.lineStart = this.pos;
      }
      chunkStart = this.pos;
    } else {
      ++this.pos;
    }
  }
};

// Used to read escaped characters

pp.readEscapedChar = function (inTemplate) {
  var ch = this.input.charCodeAt(++this.pos);
  ++this.pos;
  switch (ch) {
    case 110:
      return "\n"; // 'n' -> '\n'
    case 114:
      return "\r"; // 'r' -> '\r'
    case 120:
      return String.fromCharCode(this.readHexChar(2)); // 'x'
    case 117:
      return codePointToString(this.readCodePoint()); // 'u'
    case 116:
      return "\t"; // 't' -> '\t'
    case 98:
      return "\b"; // 'b' -> '\b'
    case 118:
      return "\u000b"; // 'v' -> '\u000b'
    case 102:
      return "\f"; // 'f' -> '\f'
    case 13:
      if (this.input.charCodeAt(this.pos) === 10) ++this.pos; // '\r\n'
    case 10:
      // ' \n'
      if (this.options.locations) {
        this.lineStart = this.pos;++this.curLine;
      }
      return "";
    default:
      if (ch >= 48 && ch <= 55) {
        var octalStr = this.input.substr(this.pos - 1, 3).match(/^[0-7]+/)[0];
        var octal = parseInt(octalStr, 8);
        if (octal > 255) {
          octalStr = octalStr.slice(0, -1);
          octal = parseInt(octalStr, 8);
        }
        if (octal > 0 && (this.strict || inTemplate)) {
          this.raise(this.pos - 2, "Octal literal in strict mode");
        }
        this.pos += octalStr.length - 1;
        return String.fromCharCode(octal);
      }
      return String.fromCharCode(ch);
  }
};

// Used to read character escape sequences ('\x', '\u', '\U').

pp.readHexChar = function (len) {
  var codePos = this.pos;
  var n = this.readInt(16, len);
  if (n === null) this.raise(codePos, "Bad character escape sequence");
  return n;
};

// Read an identifier, and return it as a string. Sets `this.containsEsc`
// to whether the word contained a '\u' escape.
//
// Incrementally adds only escaped chars, adding other chunks as-is
// as a micro-optimization.

pp.readWord1 = function () {
  this.containsEsc = false;
  var word = "",
      first = true,
      chunkStart = this.pos;
  var astral = this.options.ecmaVersion >= 6;
  while (this.pos < this.input.length) {
    var ch = this.fullCharCodeAtPos();
    if (_identifier.isIdentifierChar(ch, astral)) {
      this.pos += ch <= 0xffff ? 1 : 2;
    } else if (ch === 92) {
      // "\"
      this.containsEsc = true;
      word += this.input.slice(chunkStart, this.pos);
      var escStart = this.pos;
      if (this.input.charCodeAt(++this.pos) != 117) // "u"
        this.raise(this.pos, "Expecting Unicode escape sequence \\uXXXX");
      ++this.pos;
      var esc = this.readCodePoint();
      if (!(first ? _identifier.isIdentifierStart : _identifier.isIdentifierChar)(esc, astral)) this.raise(escStart, "Invalid Unicode escape");
      word += codePointToString(esc);
      chunkStart = this.pos;
    } else {
      break;
    }
    first = false;
  }
  return word + this.input.slice(chunkStart, this.pos);
};

// Read an identifier or keyword token. Will check for reserved
// words when necessary.

pp.readWord = function () {
  var word = this.readWord1();
  var type = _tokentype.types.name;
  if ((this.options.ecmaVersion >= 6 || !this.containsEsc) && this.keywords.test(word)) type = _tokentype.keywords[word];
  return this.finishToken(type, word);
};

},{"./identifier":2,"./locutil":5,"./state":10,"./tokentype":14,"./whitespace":16}],14:[function(_dereq_,module,exports){
// ## Token types

// The assignment of fine-grained, information-carrying type objects
// allows the tokenizer to store the information it has about a
// token in a way that is very cheap for the parser to look up.

// All token type variables start with an underscore, to make them
// easy to recognize.

// The `beforeExpr` property is used to disambiguate between regular
// expressions and divisions. It is set on all token types that can
// be followed by an expression (thus, a slash after them would be a
// regular expression).
//
// The `startsExpr` property is used to check if the token ends a
// `yield` expression. It is set on all token types that either can
// directly start an expression (like a quotation mark) or can
// continue an expression (like the body of a string).
//
// `isLoop` marks a keyword as starting a loop, which is important
// to know when parsing a label, in order to allow or disallow
// continue jumps to that label.

"use strict";

exports.__esModule = true;

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var TokenType = function TokenType(label) {
  var conf = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];

  _classCallCheck(this, TokenType);

  this.label = label;
  this.keyword = conf.keyword;
  this.beforeExpr = !!conf.beforeExpr;
  this.startsExpr = !!conf.startsExpr;
  this.isLoop = !!conf.isLoop;
  this.isAssign = !!conf.isAssign;
  this.prefix = !!conf.prefix;
  this.postfix = !!conf.postfix;
  this.binop = conf.binop || null;
  this.updateContext = null;
};

exports.TokenType = TokenType;

function binop(name, prec) {
  return new TokenType(name, { beforeExpr: true, binop: prec });
}
var beforeExpr = { beforeExpr: true },
    startsExpr = { startsExpr: true };

var types = {
  num: new TokenType("num", startsExpr),
  regexp: new TokenType("regexp", startsExpr),
  string: new TokenType("string", startsExpr),
  name: new TokenType("name", startsExpr),
  eof: new TokenType("eof"),

  // Punctuation token types.
  bracketL: new TokenType("[", { beforeExpr: true, startsExpr: true }),
  bracketR: new TokenType("]"),
  braceL: new TokenType("{", { beforeExpr: true, startsExpr: true }),
  braceR: new TokenType("}"),
  parenL: new TokenType("(", { beforeExpr: true, startsExpr: true }),
  parenR: new TokenType(")"),
  comma: new TokenType(",", beforeExpr),
  semi: new TokenType(";", beforeExpr),
  colon: new TokenType(":", beforeExpr),
  dot: new TokenType("."),
  question: new TokenType("?", beforeExpr),
  arrow: new TokenType("=>", beforeExpr),
  template: new TokenType("template"),
  ellipsis: new TokenType("...", beforeExpr),
  backQuote: new TokenType("`", startsExpr),
  dollarBraceL: new TokenType("${", { beforeExpr: true, startsExpr: true }),

  // Operators. These carry several kinds of properties to help the
  // parser use them properly (the presence of these properties is
  // what categorizes them as operators).
  //
  // `binop`, when present, specifies that this operator is a binary
  // operator, and will refer to its precedence.
  //
  // `prefix` and `postfix` mark the operator as a prefix or postfix
  // unary operator.
  //
  // `isAssign` marks all of `=`, `+=`, `-=` etcetera, which act as
  // binary operators with a very low precedence, that should result
  // in AssignmentExpression nodes.

  eq: new TokenType("=", { beforeExpr: true, isAssign: true }),
  assign: new TokenType("_=", { beforeExpr: true, isAssign: true }),
  incDec: new TokenType("++/--", { prefix: true, postfix: true, startsExpr: true }),
  prefix: new TokenType("prefix", { beforeExpr: true, prefix: true, startsExpr: true }),
  logicalOR: binop("||", 1),
  logicalAND: binop("&&", 2),
  bitwiseOR: binop("|", 3),
  bitwiseXOR: binop("^", 4),
  bitwiseAND: binop("&", 5),
  equality: binop("==/!=", 6),
  relational: binop("</>", 7),
  bitShift: binop("<</>>", 8),
  plusMin: new TokenType("+/-", { beforeExpr: true, binop: 9, prefix: true, startsExpr: true }),
  modulo: binop("%", 10),
  star: binop("*", 10),
  slash: binop("/", 10)
};

exports.types = types;
// Map keyword names to token types.

var keywords = {};

exports.keywords = keywords;
// Succinct definitions of keyword token types
function kw(name) {
  var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];

  options.keyword = name;
  keywords[name] = types["_" + name] = new TokenType(name, options);
}

kw("break");
kw("case", beforeExpr);
kw("catch");
kw("continue");
kw("debugger");
kw("default", beforeExpr);
kw("do", { isLoop: true, beforeExpr: true });
kw("else", beforeExpr);
kw("finally");
kw("for", { isLoop: true });
kw("function", startsExpr);
kw("if");
kw("return", beforeExpr);
kw("switch");
kw("throw", beforeExpr);
kw("try");
kw("var");
kw("let");
kw("const");
kw("while", { isLoop: true });
kw("with");
kw("new", { beforeExpr: true, startsExpr: true });
kw("this", startsExpr);
kw("super", startsExpr);
kw("class");
kw("extends", beforeExpr);
kw("export");
kw("import");
kw("yield", { beforeExpr: true, startsExpr: true });
kw("null", startsExpr);
kw("true", startsExpr);
kw("false", startsExpr);
kw("in", { beforeExpr: true, binop: 7 });
kw("instanceof", { beforeExpr: true, binop: 7 });
kw("typeof", { beforeExpr: true, prefix: true, startsExpr: true });
kw("void", { beforeExpr: true, prefix: true, startsExpr: true });
kw("delete", { beforeExpr: true, prefix: true, startsExpr: true });

},{}],15:[function(_dereq_,module,exports){
"use strict";

exports.__esModule = true;
exports.isArray = isArray;
exports.has = has;

function isArray(obj) {
  return Object.prototype.toString.call(obj) === "[object Array]";
}

// Checks if an object has a property.

function has(obj, propName) {
  return Object.prototype.hasOwnProperty.call(obj, propName);
}

},{}],16:[function(_dereq_,module,exports){
// Matches a whole line break (where CRLF is considered a single
// line break). Used to count lines.

"use strict";

exports.__esModule = true;
exports.isNewLine = isNewLine;
var lineBreak = /\r\n?|\n|\u2028|\u2029/;
exports.lineBreak = lineBreak;
var lineBreakG = new RegExp(lineBreak.source, "g");

exports.lineBreakG = lineBreakG;

function isNewLine(code) {
  return code === 10 || code === 13 || code === 0x2028 || code == 0x2029;
}

var nonASCIIwhitespace = /[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/;
exports.nonASCIIwhitespace = nonASCIIwhitespace;

},{}]},{},[3])(3)
});PK
!<�@ʬ���<chrome/devtools/modules/devtools/shared/acorn/acorn_loose.js(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.acorn || (g.acorn = {})).loose = 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(_dereq_,module,exports){
"use strict";

module.exports = typeof acorn != 'undefined' ? acorn : require("./acorn");

},{}],2:[function(_dereq_,module,exports){
"use strict";

var _state = _dereq_("./state");

var _parseutil = _dereq_("./parseutil");

var _ = _dereq_("..");

var lp = _state.LooseParser.prototype;

lp.checkLVal = function (expr) {
  if (!expr) return expr;
  switch (expr.type) {
    case "Identifier":
    case "MemberExpression":
      return expr;

    case "ParenthesizedExpression":
      expr.expression = this.checkLVal(expr.expression);
      return expr;

    default:
      return this.dummyIdent();
  }
};

lp.parseExpression = function (noIn) {
  var start = this.storeCurrentPos();
  var expr = this.parseMaybeAssign(noIn);
  if (this.tok.type === _.tokTypes.comma) {
    var node = this.startNodeAt(start);
    node.expressions = [expr];
    while (this.eat(_.tokTypes.comma)) node.expressions.push(this.parseMaybeAssign(noIn));
    return this.finishNode(node, "SequenceExpression");
  }
  return expr;
};

lp.parseParenExpression = function () {
  this.pushCx();
  this.expect(_.tokTypes.parenL);
  var val = this.parseExpression();
  this.popCx();
  this.expect(_.tokTypes.parenR);
  return val;
};

lp.parseMaybeAssign = function (noIn) {
  var start = this.storeCurrentPos();
  var left = this.parseMaybeConditional(noIn);
  if (this.tok.type.isAssign) {
    var node = this.startNodeAt(start);
    node.operator = this.tok.value;
    node.left = this.tok.type === _.tokTypes.eq ? this.toAssignable(left) : this.checkLVal(left);
    this.next();
    node.right = this.parseMaybeAssign(noIn);
    return this.finishNode(node, "AssignmentExpression");
  }
  return left;
};

lp.parseMaybeConditional = function (noIn) {
  var start = this.storeCurrentPos();
  var expr = this.parseExprOps(noIn);
  if (this.eat(_.tokTypes.question)) {
    var node = this.startNodeAt(start);
    node.test = expr;
    node.consequent = this.parseMaybeAssign();
    node.alternate = this.expect(_.tokTypes.colon) ? this.parseMaybeAssign(noIn) : this.dummyIdent();
    return this.finishNode(node, "ConditionalExpression");
  }
  return expr;
};

lp.parseExprOps = function (noIn) {
  var start = this.storeCurrentPos();
  var indent = this.curIndent,
      line = this.curLineStart;
  return this.parseExprOp(this.parseMaybeUnary(noIn), start, -1, noIn, indent, line);
};

lp.parseExprOp = function (left, start, minPrec, noIn, indent, line) {
  if (this.curLineStart != line && this.curIndent < indent && this.tokenStartsLine()) return left;
  var prec = this.tok.type.binop;
  if (prec != null && (!noIn || this.tok.type !== _.tokTypes._in)) {
    if (prec > minPrec) {
      var node = this.startNodeAt(start);
      node.left = left;
      node.operator = this.tok.value;
      this.next();
      if (this.curLineStart != line && this.curIndent < indent && this.tokenStartsLine()) {
        node.right = this.dummyIdent();
      } else {
        var rightStart = this.storeCurrentPos();
        node.right = this.parseExprOp(this.parseMaybeUnary(noIn), rightStart, prec, noIn, indent, line);
      }
      this.finishNode(node, /&&|\|\|/.test(node.operator) ? "LogicalExpression" : "BinaryExpression");
      return this.parseExprOp(node, start, minPrec, noIn, indent, line);
    }
  }
  return left;
};

lp.parseMaybeUnary = function (noIn) {
  if (this.tok.type.prefix) {
    var node = this.startNode(),
        update = this.tok.type === _.tokTypes.incDec;
    node.operator = this.tok.value;
    node.prefix = true;
    this.next();
    node.argument = this.parseMaybeUnary(noIn);
    if (update) node.argument = this.checkLVal(node.argument);
    return this.finishNode(node, update ? "UpdateExpression" : "UnaryExpression");
  } else if (this.tok.type === _.tokTypes.ellipsis) {
    var node = this.startNode();
    this.next();
    node.argument = this.parseMaybeUnary(noIn);
    return this.finishNode(node, "SpreadElement");
  }
  var start = this.storeCurrentPos();
  var expr = this.parseExprSubscripts();
  while (this.tok.type.postfix && !this.canInsertSemicolon()) {
    var node = this.startNodeAt(start);
    node.operator = this.tok.value;
    node.prefix = false;
    node.argument = this.checkLVal(expr);
    this.next();
    expr = this.finishNode(node, "UpdateExpression");
  }
  return expr;
};

lp.parseExprSubscripts = function () {
  var start = this.storeCurrentPos();
  return this.parseSubscripts(this.parseExprAtom(), start, false, this.curIndent, this.curLineStart);
};

lp.parseSubscripts = function (base, start, noCalls, startIndent, line) {
  for (;;) {
    if (this.curLineStart != line && this.curIndent <= startIndent && this.tokenStartsLine()) {
      if (this.tok.type == _.tokTypes.dot && this.curIndent == startIndent) --startIndent;else return base;
    }

    if (this.eat(_.tokTypes.dot)) {
      var node = this.startNodeAt(start);
      node.object = base;
      if (this.curLineStart != line && this.curIndent <= startIndent && this.tokenStartsLine()) node.property = this.dummyIdent();else node.property = this.parsePropertyAccessor() || this.dummyIdent();
      node.computed = false;
      base = this.finishNode(node, "MemberExpression");
    } else if (this.tok.type == _.tokTypes.bracketL) {
      this.pushCx();
      this.next();
      var node = this.startNodeAt(start);
      node.object = base;
      node.property = this.parseExpression();
      node.computed = true;
      this.popCx();
      this.expect(_.tokTypes.bracketR);
      base = this.finishNode(node, "MemberExpression");
    } else if (!noCalls && this.tok.type == _.tokTypes.parenL) {
      var node = this.startNodeAt(start);
      node.callee = base;
      node.arguments = this.parseExprList(_.tokTypes.parenR);
      base = this.finishNode(node, "CallExpression");
    } else if (this.tok.type == _.tokTypes.backQuote) {
      var node = this.startNodeAt(start);
      node.tag = base;
      node.quasi = this.parseTemplate();
      base = this.finishNode(node, "TaggedTemplateExpression");
    } else {
      return base;
    }
  }
};

lp.parseExprAtom = function () {
  var node = undefined;
  switch (this.tok.type) {
    case _.tokTypes._this:
    case _.tokTypes._super:
      var type = this.tok.type === _.tokTypes._this ? "ThisExpression" : "Super";
      node = this.startNode();
      this.next();
      return this.finishNode(node, type);

    case _.tokTypes.name:
      var start = this.storeCurrentPos();
      var id = this.parseIdent();
      return this.eat(_.tokTypes.arrow) ? this.parseArrowExpression(this.startNodeAt(start), [id]) : id;

    case _.tokTypes.regexp:
      node = this.startNode();
      var val = this.tok.value;
      node.regex = { pattern: val.pattern, flags: val.flags };
      node.value = val.value;
      node.raw = this.input.slice(this.tok.start, this.tok.end);
      this.next();
      return this.finishNode(node, "Literal");

    case _.tokTypes.num:case _.tokTypes.string:
      node = this.startNode();
      node.value = this.tok.value;
      node.raw = this.input.slice(this.tok.start, this.tok.end);
      this.next();
      return this.finishNode(node, "Literal");

    case _.tokTypes._null:case _.tokTypes._true:case _.tokTypes._false:
      node = this.startNode();
      node.value = this.tok.type === _.tokTypes._null ? null : this.tok.type === _.tokTypes._true;
      node.raw = this.tok.type.keyword;
      this.next();
      return this.finishNode(node, "Literal");

    case _.tokTypes.parenL:
      var parenStart = this.storeCurrentPos();
      this.next();
      var inner = this.parseExpression();
      this.expect(_.tokTypes.parenR);
      if (this.eat(_.tokTypes.arrow)) {
        return this.parseArrowExpression(this.startNodeAt(parenStart), inner.expressions || (_parseutil.isDummy(inner) ? [] : [inner]));
      }
      if (this.options.preserveParens) {
        var par = this.startNodeAt(parenStart);
        par.expression = inner;
        inner = this.finishNode(par, "ParenthesizedExpression");
      }
      return inner;

    case _.tokTypes.bracketL:
      node = this.startNode();
      node.elements = this.parseExprList(_.tokTypes.bracketR, true);
      return this.finishNode(node, "ArrayExpression");

    case _.tokTypes.braceL:
      return this.parseObj();

    case _.tokTypes._class:
      return this.parseClass();

    case _.tokTypes._function:
      node = this.startNode();
      this.next();
      return this.parseFunction(node, false);

    case _.tokTypes._new:
      return this.parseNew();

    case _.tokTypes._yield:
      node = this.startNode();
      this.next();
      if (this.semicolon() || this.canInsertSemicolon() || this.tok.type != _.tokTypes.star && !this.tok.type.startsExpr) {
        node.delegate = false;
        node.argument = null;
      } else {
        node.delegate = this.eat(_.tokTypes.star);
        node.argument = this.parseMaybeAssign();
      }
      return this.finishNode(node, "YieldExpression");

    case _.tokTypes.backQuote:
      return this.parseTemplate();

    default:
      return this.dummyIdent();
  }
};

lp.parseNew = function () {
  var node = this.startNode(),
      startIndent = this.curIndent,
      line = this.curLineStart;
  var meta = this.parseIdent(true);
  if (this.options.ecmaVersion >= 6 && this.eat(_.tokTypes.dot)) {
    node.meta = meta;
    node.property = this.parseIdent(true);
    return this.finishNode(node, "MetaProperty");
  }
  var start = this.storeCurrentPos();
  node.callee = this.parseSubscripts(this.parseExprAtom(), start, true, startIndent, line);
  if (this.tok.type == _.tokTypes.parenL) {
    node.arguments = this.parseExprList(_.tokTypes.parenR);
  } else {
    node.arguments = [];
  }
  return this.finishNode(node, "NewExpression");
};

lp.parseTemplateElement = function () {
  var elem = this.startNode();
  elem.value = {
    raw: this.input.slice(this.tok.start, this.tok.end).replace(/\r\n?/g, '\n'),
    cooked: this.tok.value
  };
  this.next();
  elem.tail = this.tok.type === _.tokTypes.backQuote;
  return this.finishNode(elem, "TemplateElement");
};

lp.parseTemplate = function () {
  var node = this.startNode();
  this.next();
  node.expressions = [];
  var curElt = this.parseTemplateElement();
  node.quasis = [curElt];
  while (!curElt.tail) {
    this.next();
    node.expressions.push(this.parseExpression());
    if (this.expect(_.tokTypes.braceR)) {
      curElt = this.parseTemplateElement();
    } else {
      curElt = this.startNode();
      curElt.value = { cooked: '', raw: '' };
      curElt.tail = true;
    }
    node.quasis.push(curElt);
  }
  this.expect(_.tokTypes.backQuote);
  return this.finishNode(node, "TemplateLiteral");
};

lp.parseObj = function () {
  var node = this.startNode();
  node.properties = [];
  this.pushCx();
  var indent = this.curIndent + 1,
      line = this.curLineStart;
  this.eat(_.tokTypes.braceL);
  if (this.curIndent + 1 < indent) {
    indent = this.curIndent;line = this.curLineStart;
  }
  while (!this.closes(_.tokTypes.braceR, indent, line)) {
    var prop = this.startNode(),
        isGenerator = undefined,
        start = undefined;
    if (this.options.ecmaVersion >= 6) {
      start = this.storeCurrentPos();
      prop.method = false;
      prop.shorthand = false;
      isGenerator = this.eat(_.tokTypes.star);
    }
    this.parsePropertyName(prop);
    if (_parseutil.isDummy(prop.key)) {
      if (_parseutil.isDummy(this.parseMaybeAssign())) this.next();this.eat(_.tokTypes.comma);continue;
    }
    if (this.eat(_.tokTypes.colon)) {
      prop.kind = "init";
      prop.value = this.parseMaybeAssign();
    } else if (this.options.ecmaVersion >= 6 && (this.tok.type === _.tokTypes.parenL || this.tok.type === _.tokTypes.braceL)) {
      prop.kind = "init";
      prop.method = true;
      prop.value = this.parseMethod(isGenerator);
    } else if (this.options.ecmaVersion >= 5 && prop.key.type === "Identifier" && !prop.computed && (prop.key.name === "get" || prop.key.name === "set") && (this.tok.type != _.tokTypes.comma && this.tok.type != _.tokTypes.braceR)) {
      prop.kind = prop.key.name;
      this.parsePropertyName(prop);
      prop.value = this.parseMethod(false);
    } else {
      prop.kind = "init";
      if (this.options.ecmaVersion >= 6) {
        if (this.eat(_.tokTypes.eq)) {
          var assign = this.startNodeAt(start);
          assign.operator = "=";
          assign.left = prop.key;
          assign.right = this.parseMaybeAssign();
          prop.value = this.finishNode(assign, "AssignmentExpression");
        } else {
          prop.value = prop.key;
        }
      } else {
        prop.value = this.dummyIdent();
      }
      prop.shorthand = true;
    }
    node.properties.push(this.finishNode(prop, "Property"));
    this.eat(_.tokTypes.comma);
  }
  this.popCx();
  if (!this.eat(_.tokTypes.braceR)) {
    // If there is no closing brace, make the node span to the start
    // of the next token (this is useful for Tern)
    this.last.end = this.tok.start;
    if (this.options.locations) this.last.loc.end = this.tok.loc.start;
  }
  return this.finishNode(node, "ObjectExpression");
};

lp.parsePropertyName = function (prop) {
  if (this.options.ecmaVersion >= 6) {
    if (this.eat(_.tokTypes.bracketL)) {
      prop.computed = true;
      prop.key = this.parseExpression();
      this.expect(_.tokTypes.bracketR);
      return;
    } else {
      prop.computed = false;
    }
  }
  var key = this.tok.type === _.tokTypes.num || this.tok.type === _.tokTypes.string ? this.parseExprAtom() : this.parseIdent();
  prop.key = key || this.dummyIdent();
};

lp.parsePropertyAccessor = function () {
  if (this.tok.type === _.tokTypes.name || this.tok.type.keyword) return this.parseIdent();
};

lp.parseIdent = function () {
  var name = this.tok.type === _.tokTypes.name ? this.tok.value : this.tok.type.keyword;
  if (!name) return this.dummyIdent();
  var node = this.startNode();
  this.next();
  node.name = name;
  return this.finishNode(node, "Identifier");
};

lp.initFunction = function (node) {
  node.id = null;
  node.params = [];
  if (this.options.ecmaVersion >= 6) {
    node.generator = false;
    node.expression = false;
  }
};

// Convert existing expression atom to assignable pattern
// if possible.

lp.toAssignable = function (node, binding) {
  if (!node || node.type == "Identifier" || node.type == "MemberExpression" && !binding) {
    // Okay
  } else if (node.type == "ParenthesizedExpression") {
      node.expression = this.toAssignable(node.expression, binding);
    } else if (this.options.ecmaVersion < 6) {
      return this.dummyIdent();
    } else if (node.type == "ObjectExpression") {
      node.type = "ObjectPattern";
      var props = node.properties;
      for (var i = 0; i < props.length; i++) {
        props[i].value = this.toAssignable(props[i].value, binding);
      }
    } else if (node.type == "ArrayExpression") {
      node.type = "ArrayPattern";
      this.toAssignableList(node.elements, binding);
    } else if (node.type == "SpreadElement") {
      node.type = "RestElement";
      node.argument = this.toAssignable(node.argument, binding);
    } else if (node.type == "AssignmentExpression") {
      node.type = "AssignmentPattern";
      delete node.operator;
    } else {
      return this.dummyIdent();
    }
  return node;
};

lp.toAssignableList = function (exprList, binding) {
  for (var i = 0; i < exprList.length; i++) {
    exprList[i] = this.toAssignable(exprList[i], binding);
  }return exprList;
};

lp.parseFunctionParams = function (params) {
  params = this.parseExprList(_.tokTypes.parenR);
  return this.toAssignableList(params, true);
};

lp.parseMethod = function (isGenerator) {
  var node = this.startNode();
  this.initFunction(node);
  node.params = this.parseFunctionParams();
  node.generator = isGenerator || false;
  node.expression = this.options.ecmaVersion >= 6 && this.tok.type !== _.tokTypes.braceL;
  node.body = node.expression ? this.parseMaybeAssign() : this.parseBlock();
  return this.finishNode(node, "FunctionExpression");
};

lp.parseArrowExpression = function (node, params) {
  this.initFunction(node);
  node.params = this.toAssignableList(params, true);
  node.expression = this.tok.type !== _.tokTypes.braceL;
  node.body = node.expression ? this.parseMaybeAssign() : this.parseBlock();
  return this.finishNode(node, "ArrowFunctionExpression");
};

lp.parseExprList = function (close, allowEmpty) {
  this.pushCx();
  var indent = this.curIndent,
      line = this.curLineStart,
      elts = [];
  this.next(); // Opening bracket
  while (!this.closes(close, indent + 1, line)) {
    if (this.eat(_.tokTypes.comma)) {
      elts.push(allowEmpty ? null : this.dummyIdent());
      continue;
    }
    var elt = this.parseMaybeAssign();
    if (_parseutil.isDummy(elt)) {
      if (this.closes(close, indent, line)) break;
      this.next();
    } else {
      elts.push(elt);
    }
    this.eat(_.tokTypes.comma);
  }
  this.popCx();
  if (!this.eat(close)) {
    // If there is no closing brace, make the node span to the start
    // of the next token (this is useful for Tern)
    this.last.end = this.tok.start;
    if (this.options.locations) this.last.loc.end = this.tok.loc.start;
  }
  return elts;
};

},{"..":1,"./parseutil":4,"./state":5}],3:[function(_dereq_,module,exports){
// Acorn: Loose parser
//
// This module provides an alternative parser (`parse_dammit`) that
// exposes that same interface as `parse`, but will try to parse
// anything as JavaScript, repairing syntax error the best it can.
// There are circumstances in which it will raise an error and give
// up, but they are very rare. The resulting AST will be a mostly
// valid JavaScript AST (as per the [Mozilla parser API][api], except
// that:
//
// - Return outside functions is allowed
//
// - Label consistency (no conflicts, break only to existing labels)
//   is not enforced.
//
// - Bogus Identifier nodes with a name of `"✖"` are inserted whenever
//   the parser got too confused to return anything meaningful.
//
// [api]: https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API
//
// The expected use for this is to *first* try `acorn.parse`, and only
// if that fails switch to `parse_dammit`. The loose parser might
// parse badly indented code incorrectly, so **don't** use it as
// your default parser.
//
// Quite a lot of acorn.js is duplicated here. The alternative was to
// add a *lot* of extra cruft to that file, making it less readable
// and slower. Copying and editing the code allowed me to make
// invasive changes and simplifications without creating a complicated
// tangle.

"use strict";

exports.__esModule = true;
exports.parse_dammit = parse_dammit;

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj["default"] = obj; return newObj; } }

var _ = _dereq_("..");

var acorn = _interopRequireWildcard(_);

var _state = _dereq_("./state");

_dereq_("./tokenize");

_dereq_("./statement");

_dereq_("./expression");

exports.LooseParser = _state.LooseParser;
exports.pluginsLoose = _state.pluginsLoose;

acorn.defaultOptions.tabSize = 4;

function parse_dammit(input, options) {
  var p = new _state.LooseParser(input, options);
  p.next();
  return p.parseTopLevel();
}

// Don't define new properties on acorn because of:
// TypeError: can't define property "parse_dammit": Object is not extensible
// acorn.parse_dammit = parse_dammit;
// acorn.LooseParser = _state.LooseParser;
// acorn.pluginsLoose = _state.pluginsLoose;

},{"..":1,"./expression":2,"./state":5,"./statement":6,"./tokenize":7}],4:[function(_dereq_,module,exports){
"use strict";

exports.__esModule = true;
exports.isDummy = isDummy;

function isDummy(node) {
  return node.name == "✖";
}

},{}],5:[function(_dereq_,module,exports){
"use strict";

exports.__esModule = true;

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var _ = _dereq_("..");

// Registered plugins
var pluginsLoose = {};

exports.pluginsLoose = pluginsLoose;

var LooseParser = (function () {
  function LooseParser(input, options) {
    _classCallCheck(this, LooseParser);

    this.toks = _.tokenizer(input, options);
    this.options = this.toks.options;
    this.input = this.toks.input;
    this.tok = this.last = { type: _.tokTypes.eof, start: 0, end: 0 };
    if (this.options.locations) {
      var here = this.toks.curPosition();
      this.tok.loc = new _.SourceLocation(this.toks, here, here);
    }
    this.ahead = []; // Tokens ahead
    this.context = []; // Indentation contexted
    this.curIndent = 0;
    this.curLineStart = 0;
    this.nextLineStart = this.lineEnd(this.curLineStart) + 1;
    // Load plugins
    this.options.pluginsLoose = options.pluginsLoose || {};
    this.loadPlugins(this.options.pluginsLoose);
  }

  LooseParser.prototype.startNode = function startNode() {
    return new _.Node(this.toks, this.tok.start, this.options.locations ? this.tok.loc.start : null);
  };

  LooseParser.prototype.storeCurrentPos = function storeCurrentPos() {
    return this.options.locations ? [this.tok.start, this.tok.loc.start] : this.tok.start;
  };

  LooseParser.prototype.startNodeAt = function startNodeAt(pos) {
    if (this.options.locations) {
      return new _.Node(this.toks, pos[0], pos[1]);
    } else {
      return new _.Node(this.toks, pos);
    }
  };

  LooseParser.prototype.finishNode = function finishNode(node, type) {
    node.type = type;
    node.end = this.last.end;
    if (this.options.locations) node.loc.end = this.last.loc.end;
    if (this.options.ranges) node.range[1] = this.last.end;
    return node;
  };

  LooseParser.prototype.dummyNode = function dummyNode(type) {
    var dummy = this.startNode();
    dummy.type = type;
    dummy.end = dummy.start;
    if (this.options.locations) dummy.loc.end = dummy.loc.start;
    if (this.options.ranges) dummy.range[1] = dummy.start;
    this.last = { type: _.tokTypes.name, start: dummy.start, end: dummy.start, loc: dummy.loc };
    return dummy;
  };

  LooseParser.prototype.dummyIdent = function dummyIdent() {
    var dummy = this.dummyNode("Identifier");
    dummy.name = "✖";
    return dummy;
  };

  LooseParser.prototype.dummyString = function dummyString() {
    var dummy = this.dummyNode("Literal");
    dummy.value = dummy.raw = "✖";
    return dummy;
  };

  LooseParser.prototype.eat = function eat(type) {
    if (this.tok.type === type) {
      this.next();
      return true;
    } else {
      return false;
    }
  };

  LooseParser.prototype.isContextual = function isContextual(name) {
    return this.tok.type === _.tokTypes.name && this.tok.value === name;
  };

  LooseParser.prototype.eatContextual = function eatContextual(name) {
    return this.tok.value === name && this.eat(_.tokTypes.name);
  };

  LooseParser.prototype.canInsertSemicolon = function canInsertSemicolon() {
    return this.tok.type === _.tokTypes.eof || this.tok.type === _.tokTypes.braceR || _.lineBreak.test(this.input.slice(this.last.end, this.tok.start));
  };

  LooseParser.prototype.semicolon = function semicolon() {
    return this.eat(_.tokTypes.semi);
  };

  LooseParser.prototype.expect = function expect(type) {
    if (this.eat(type)) return true;
    for (var i = 1; i <= 2; i++) {
      if (this.lookAhead(i).type == type) {
        for (var j = 0; j < i; j++) {
          this.next();
        }return true;
      }
    }
  };

  LooseParser.prototype.pushCx = function pushCx() {
    this.context.push(this.curIndent);
  };

  LooseParser.prototype.popCx = function popCx() {
    this.curIndent = this.context.pop();
  };

  LooseParser.prototype.lineEnd = function lineEnd(pos) {
    while (pos < this.input.length && !_.isNewLine(this.input.charCodeAt(pos))) ++pos;
    return pos;
  };

  LooseParser.prototype.indentationAfter = function indentationAfter(pos) {
    for (var count = 0;; ++pos) {
      var ch = this.input.charCodeAt(pos);
      if (ch === 32) ++count;else if (ch === 9) count += this.options.tabSize;else return count;
    }
  };

  LooseParser.prototype.closes = function closes(closeTok, indent, line, blockHeuristic) {
    if (this.tok.type === closeTok || this.tok.type === _.tokTypes.eof) return true;
    return line != this.curLineStart && this.curIndent < indent && this.tokenStartsLine() && (!blockHeuristic || this.nextLineStart >= this.input.length || this.indentationAfter(this.nextLineStart) < indent);
  };

  LooseParser.prototype.tokenStartsLine = function tokenStartsLine() {
    for (var p = this.tok.start - 1; p >= this.curLineStart; --p) {
      var ch = this.input.charCodeAt(p);
      if (ch !== 9 && ch !== 32) return false;
    }
    return true;
  };

  LooseParser.prototype.extend = function extend(name, f) {
    this[name] = f(this[name]);
  };

  LooseParser.prototype.loadPlugins = function loadPlugins(pluginConfigs) {
    for (var _name in pluginConfigs) {
      var plugin = pluginsLoose[_name];
      if (!plugin) throw new Error("Plugin '" + _name + "' not found");
      plugin(this, pluginConfigs[_name]);
    }
  };

  return LooseParser;
})();

exports.LooseParser = LooseParser;

},{"..":1}],6:[function(_dereq_,module,exports){
"use strict";

var _state = _dereq_("./state");

var _parseutil = _dereq_("./parseutil");

var _ = _dereq_("..");

var lp = _state.LooseParser.prototype;

lp.parseTopLevel = function () {
  var node = this.startNodeAt(this.options.locations ? [0, _.getLineInfo(this.input, 0)] : 0);
  node.body = [];
  while (this.tok.type !== _.tokTypes.eof) node.body.push(this.parseStatement());
  this.last = this.tok;
  if (this.options.ecmaVersion >= 6) {
    node.sourceType = this.options.sourceType;
  }
  return this.finishNode(node, "Program");
};

lp.parseStatement = function () {
  var starttype = this.tok.type,
      node = this.startNode();

  switch (starttype) {
    case _.tokTypes._break:case _.tokTypes._continue:
      this.next();
      var isBreak = starttype === _.tokTypes._break;
      if (this.semicolon() || this.canInsertSemicolon()) {
        node.label = null;
      } else {
        node.label = this.tok.type === _.tokTypes.name ? this.parseIdent() : null;
        this.semicolon();
      }
      return this.finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement");

    case _.tokTypes._debugger:
      this.next();
      this.semicolon();
      return this.finishNode(node, "DebuggerStatement");

    case _.tokTypes._do:
      this.next();
      node.body = this.parseStatement();
      node.test = this.eat(_.tokTypes._while) ? this.parseParenExpression() : this.dummyIdent();
      this.semicolon();
      return this.finishNode(node, "DoWhileStatement");

    case _.tokTypes._for:
      this.next();
      this.pushCx();
      this.expect(_.tokTypes.parenL);
      if (this.tok.type === _.tokTypes.semi) return this.parseFor(node, null);
      if (this.tok.type === _.tokTypes._var || this.tok.type === _.tokTypes._let || this.tok.type === _.tokTypes._const) {
        var _init = this.parseVar(true);
        if (_init.declarations.length === 1 && (this.tok.type === _.tokTypes._in || this.isContextual("of"))) {
          return this.parseForIn(node, _init);
        }
        return this.parseFor(node, _init);
      }
      var init = this.parseExpression(true);
      if (this.tok.type === _.tokTypes._in || this.isContextual("of")) return this.parseForIn(node, this.toAssignable(init));
      return this.parseFor(node, init);

    case _.tokTypes._function:
      this.next();
      return this.parseFunction(node, true);

    case _.tokTypes._if:
      this.next();
      node.test = this.parseParenExpression();
      node.consequent = this.parseStatement();
      node.alternate = this.eat(_.tokTypes._else) ? this.parseStatement() : null;
      return this.finishNode(node, "IfStatement");

    case _.tokTypes._return:
      this.next();
      if (this.eat(_.tokTypes.semi) || this.canInsertSemicolon()) node.argument = null;else {
        node.argument = this.parseExpression();this.semicolon();
      }
      return this.finishNode(node, "ReturnStatement");

    case _.tokTypes._switch:
      var blockIndent = this.curIndent,
          line = this.curLineStart;
      this.next();
      node.discriminant = this.parseParenExpression();
      node.cases = [];
      this.pushCx();
      this.expect(_.tokTypes.braceL);

      var cur = undefined;
      while (!this.closes(_.tokTypes.braceR, blockIndent, line, true)) {
        if (this.tok.type === _.tokTypes._case || this.tok.type === _.tokTypes._default) {
          var isCase = this.tok.type === _.tokTypes._case;
          if (cur) this.finishNode(cur, "SwitchCase");
          node.cases.push(cur = this.startNode());
          cur.consequent = [];
          this.next();
          if (isCase) cur.test = this.parseExpression();else cur.test = null;
          this.expect(_.tokTypes.colon);
        } else {
          if (!cur) {
            node.cases.push(cur = this.startNode());
            cur.consequent = [];
            cur.test = null;
          }
          cur.consequent.push(this.parseStatement());
        }
      }
      if (cur) this.finishNode(cur, "SwitchCase");
      this.popCx();
      this.eat(_.tokTypes.braceR);
      return this.finishNode(node, "SwitchStatement");

    case _.tokTypes._throw:
      this.next();
      node.argument = this.parseExpression();
      this.semicolon();
      return this.finishNode(node, "ThrowStatement");

    case _.tokTypes._try:
      this.next();
      node.block = this.parseBlock();
      node.handler = null;
      if (this.tok.type === _.tokTypes._catch) {
        var clause = this.startNode();
        this.next();
        this.expect(_.tokTypes.parenL);
        clause.param = this.toAssignable(this.parseExprAtom(), true);
        this.expect(_.tokTypes.parenR);
        clause.body = this.parseBlock();
        node.handler = this.finishNode(clause, "CatchClause");
      }
      node.finalizer = this.eat(_.tokTypes._finally) ? this.parseBlock() : null;
      if (!node.handler && !node.finalizer) return node.block;
      return this.finishNode(node, "TryStatement");

    case _.tokTypes._var:
    case _.tokTypes._let:
    case _.tokTypes._const:
      return this.parseVar();

    case _.tokTypes._while:
      this.next();
      node.test = this.parseParenExpression();
      node.body = this.parseStatement();
      return this.finishNode(node, "WhileStatement");

    case _.tokTypes._with:
      this.next();
      node.object = this.parseParenExpression();
      node.body = this.parseStatement();
      return this.finishNode(node, "WithStatement");

    case _.tokTypes.braceL:
      return this.parseBlock();

    case _.tokTypes.semi:
      this.next();
      return this.finishNode(node, "EmptyStatement");

    case _.tokTypes._class:
      return this.parseClass(true);

    case _.tokTypes._import:
      return this.parseImport();

    case _.tokTypes._export:
      return this.parseExport();

    default:
      var expr = this.parseExpression();
      if (_parseutil.isDummy(expr)) {
        this.next();
        if (this.tok.type === _.tokTypes.eof) return this.finishNode(node, "EmptyStatement");
        return this.parseStatement();
      } else if (starttype === _.tokTypes.name && expr.type === "Identifier" && this.eat(_.tokTypes.colon)) {
        node.body = this.parseStatement();
        node.label = expr;
        return this.finishNode(node, "LabeledStatement");
      } else {
        node.expression = expr;
        this.semicolon();
        return this.finishNode(node, "ExpressionStatement");
      }
  }
};

lp.parseBlock = function () {
  var node = this.startNode();
  this.pushCx();
  this.expect(_.tokTypes.braceL);
  var blockIndent = this.curIndent,
      line = this.curLineStart;
  node.body = [];
  while (!this.closes(_.tokTypes.braceR, blockIndent, line, true)) node.body.push(this.parseStatement());
  this.popCx();
  this.eat(_.tokTypes.braceR);
  return this.finishNode(node, "BlockStatement");
};

lp.parseFor = function (node, init) {
  node.init = init;
  node.test = node.update = null;
  if (this.eat(_.tokTypes.semi) && this.tok.type !== _.tokTypes.semi) node.test = this.parseExpression();
  if (this.eat(_.tokTypes.semi) && this.tok.type !== _.tokTypes.parenR) node.update = this.parseExpression();
  this.popCx();
  this.expect(_.tokTypes.parenR);
  node.body = this.parseStatement();
  return this.finishNode(node, "ForStatement");
};

lp.parseForIn = function (node, init) {
  var type = this.tok.type === _.tokTypes._in ? "ForInStatement" : "ForOfStatement";
  this.next();
  node.left = init;
  node.right = this.parseExpression();
  this.popCx();
  this.expect(_.tokTypes.parenR);
  node.body = this.parseStatement();
  return this.finishNode(node, type);
};

lp.parseVar = function (noIn) {
  var node = this.startNode();
  node.kind = this.tok.type.keyword;
  this.next();
  node.declarations = [];
  do {
    var decl = this.startNode();
    decl.id = this.options.ecmaVersion >= 6 ? this.toAssignable(this.parseExprAtom(), true) : this.parseIdent();
    decl.init = this.eat(_.tokTypes.eq) ? this.parseMaybeAssign(noIn) : null;
    node.declarations.push(this.finishNode(decl, "VariableDeclarator"));
  } while (this.eat(_.tokTypes.comma));
  if (!node.declarations.length) {
    var decl = this.startNode();
    decl.id = this.dummyIdent();
    node.declarations.push(this.finishNode(decl, "VariableDeclarator"));
  }
  if (!noIn) this.semicolon();
  return this.finishNode(node, "VariableDeclaration");
};

lp.parseClass = function (isStatement) {
  var node = this.startNode();
  this.next();
  if (this.tok.type === _.tokTypes.name) node.id = this.parseIdent();else if (isStatement) node.id = this.dummyIdent();else node.id = null;
  node.superClass = this.eat(_.tokTypes._extends) ? this.parseExpression() : null;
  node.body = this.startNode();
  node.body.body = [];
  this.pushCx();
  var indent = this.curIndent + 1,
      line = this.curLineStart;
  this.eat(_.tokTypes.braceL);
  if (this.curIndent + 1 < indent) {
    indent = this.curIndent;line = this.curLineStart;
  }
  while (!this.closes(_.tokTypes.braceR, indent, line)) {
    if (this.semicolon()) continue;
    var method = this.startNode(),
        isGenerator = undefined;
    if (this.options.ecmaVersion >= 6) {
      method["static"] = false;
      isGenerator = this.eat(_.tokTypes.star);
    }
    this.parsePropertyName(method);
    if (_parseutil.isDummy(method.key)) {
      if (_parseutil.isDummy(this.parseMaybeAssign())) this.next();this.eat(_.tokTypes.comma);continue;
    }
    if (method.key.type === "Identifier" && !method.computed && method.key.name === "static" && (this.tok.type != _.tokTypes.parenL && this.tok.type != _.tokTypes.braceL)) {
      method["static"] = true;
      isGenerator = this.eat(_.tokTypes.star);
      this.parsePropertyName(method);
    } else {
      method["static"] = false;
    }
    if (this.options.ecmaVersion >= 5 && method.key.type === "Identifier" && !method.computed && (method.key.name === "get" || method.key.name === "set") && this.tok.type !== _.tokTypes.parenL && this.tok.type !== _.tokTypes.braceL) {
      method.kind = method.key.name;
      this.parsePropertyName(method);
      method.value = this.parseMethod(false);
    } else {
      if (!method.computed && !method["static"] && !isGenerator && (method.key.type === "Identifier" && method.key.name === "constructor" || method.key.type === "Literal" && method.key.value === "constructor")) {
        method.kind = "constructor";
      } else {
        method.kind = "method";
      }
      method.value = this.parseMethod(isGenerator);
    }
    node.body.body.push(this.finishNode(method, "MethodDefinition"));
  }
  this.popCx();
  if (!this.eat(_.tokTypes.braceR)) {
    // If there is no closing brace, make the node span to the start
    // of the next token (this is useful for Tern)
    this.last.end = this.tok.start;
    if (this.options.locations) this.last.loc.end = this.tok.loc.start;
  }
  this.semicolon();
  this.finishNode(node.body, "ClassBody");
  return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression");
};

lp.parseFunction = function (node, isStatement) {
  this.initFunction(node);
  if (this.options.ecmaVersion >= 6) {
    node.generator = this.eat(_.tokTypes.star);
  }
  if (this.tok.type === _.tokTypes.name) node.id = this.parseIdent();else if (isStatement) node.id = this.dummyIdent();
  node.params = this.parseFunctionParams();
  node.body = this.parseBlock();
  return this.finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression");
};

lp.parseExport = function () {
  var node = this.startNode();
  this.next();
  if (this.eat(_.tokTypes.star)) {
    node.source = this.eatContextual("from") ? this.parseExprAtom() : null;
    return this.finishNode(node, "ExportAllDeclaration");
  }
  if (this.eat(_.tokTypes._default)) {
    var expr = this.parseMaybeAssign();
    if (expr.id) {
      switch (expr.type) {
        case "FunctionExpression":
          expr.type = "FunctionDeclaration";break;
        case "ClassExpression":
          expr.type = "ClassDeclaration";break;
      }
    }
    node.declaration = expr;
    this.semicolon();
    return this.finishNode(node, "ExportDefaultDeclaration");
  }
  if (this.tok.type.keyword) {
    node.declaration = this.parseStatement();
    node.specifiers = [];
    node.source = null;
  } else {
    node.declaration = null;
    node.specifiers = this.parseExportSpecifierList();
    node.source = this.eatContextual("from") ? this.parseExprAtom() : null;
    this.semicolon();
  }
  return this.finishNode(node, "ExportNamedDeclaration");
};

lp.parseImport = function () {
  var node = this.startNode();
  this.next();
  if (this.tok.type === _.tokTypes.string) {
    node.specifiers = [];
    node.source = this.parseExprAtom();
    node.kind = '';
  } else {
    var elt = undefined;
    if (this.tok.type === _.tokTypes.name && this.tok.value !== "from") {
      elt = this.startNode();
      elt.local = this.parseIdent();
      this.finishNode(elt, "ImportDefaultSpecifier");
      this.eat(_.tokTypes.comma);
    }
    node.specifiers = this.parseImportSpecifierList();
    node.source = this.eatContextual("from") && this.tok.type == _.tokTypes.string ? this.parseExprAtom() : this.dummyString();
    if (elt) node.specifiers.unshift(elt);
  }
  this.semicolon();
  return this.finishNode(node, "ImportDeclaration");
};

lp.parseImportSpecifierList = function () {
  var elts = [];
  if (this.tok.type === _.tokTypes.star) {
    var elt = this.startNode();
    this.next();
    if (this.eatContextual("as")) elt.local = this.parseIdent();
    elts.push(this.finishNode(elt, "ImportNamespaceSpecifier"));
  } else {
    var indent = this.curIndent,
        line = this.curLineStart,
        continuedLine = this.nextLineStart;
    this.pushCx();
    this.eat(_.tokTypes.braceL);
    if (this.curLineStart > continuedLine) continuedLine = this.curLineStart;
    while (!this.closes(_.tokTypes.braceR, indent + (this.curLineStart <= continuedLine ? 1 : 0), line)) {
      var elt = this.startNode();
      if (this.eat(_.tokTypes.star)) {
        if (this.eatContextual("as")) elt.local = this.parseIdent();
        this.finishNode(elt, "ImportNamespaceSpecifier");
      } else {
        if (this.isContextual("from")) break;
        elt.imported = this.parseIdent();
        if (_parseutil.isDummy(elt.imported)) break;
        elt.local = this.eatContextual("as") ? this.parseIdent() : elt.imported;
        this.finishNode(elt, "ImportSpecifier");
      }
      elts.push(elt);
      this.eat(_.tokTypes.comma);
    }
    this.eat(_.tokTypes.braceR);
    this.popCx();
  }
  return elts;
};

lp.parseExportSpecifierList = function () {
  var elts = [];
  var indent = this.curIndent,
      line = this.curLineStart,
      continuedLine = this.nextLineStart;
  this.pushCx();
  this.eat(_.tokTypes.braceL);
  if (this.curLineStart > continuedLine) continuedLine = this.curLineStart;
  while (!this.closes(_.tokTypes.braceR, indent + (this.curLineStart <= continuedLine ? 1 : 0), line)) {
    if (this.isContextual("from")) break;
    var elt = this.startNode();
    elt.local = this.parseIdent();
    if (_parseutil.isDummy(elt.local)) break;
    elt.exported = this.eatContextual("as") ? this.parseIdent() : elt.local;
    this.finishNode(elt, "ExportSpecifier");
    elts.push(elt);
    this.eat(_.tokTypes.comma);
  }
  this.eat(_.tokTypes.braceR);
  this.popCx();
  return elts;
};

},{"..":1,"./parseutil":4,"./state":5}],7:[function(_dereq_,module,exports){
"use strict";

var _ = _dereq_("..");

var _state = _dereq_("./state");

var lp = _state.LooseParser.prototype;

function isSpace(ch) {
  return ch < 14 && ch > 8 || ch === 32 || ch === 160 || _.isNewLine(ch);
}

lp.next = function () {
  this.last = this.tok;
  if (this.ahead.length) this.tok = this.ahead.shift();else this.tok = this.readToken();

  if (this.tok.start >= this.nextLineStart) {
    while (this.tok.start >= this.nextLineStart) {
      this.curLineStart = this.nextLineStart;
      this.nextLineStart = this.lineEnd(this.curLineStart) + 1;
    }
    this.curIndent = this.indentationAfter(this.curLineStart);
  }
};

lp.readToken = function () {
  for (;;) {
    try {
      this.toks.next();
      if (this.toks.type === _.tokTypes.dot && this.input.substr(this.toks.end, 1) === "." && this.options.ecmaVersion >= 6) {
        this.toks.end++;
        this.toks.type = _.tokTypes.ellipsis;
      }
      return new _.Token(this.toks);
    } catch (e) {
      if (!(e instanceof SyntaxError)) throw e;

      // Try to skip some text, based on the error message, and then continue
      var msg = e.message,
          pos = e.raisedAt,
          replace = true;
      if (/unterminated/i.test(msg)) {
        pos = this.lineEnd(e.pos + 1);
        if (/string/.test(msg)) {
          replace = { start: e.pos, end: pos, type: _.tokTypes.string, value: this.input.slice(e.pos + 1, pos) };
        } else if (/regular expr/i.test(msg)) {
          var re = this.input.slice(e.pos, pos);
          try {
            re = new RegExp(re);
          } catch (e) {}
          replace = { start: e.pos, end: pos, type: _.tokTypes.regexp, value: re };
        } else if (/template/.test(msg)) {
          replace = { start: e.pos, end: pos,
            type: _.tokTypes.template,
            value: this.input.slice(e.pos, pos) };
        } else {
          replace = false;
        }
      } else if (/invalid (unicode|regexp|number)|expecting unicode|octal literal|is reserved|directly after number|expected number in radix/i.test(msg)) {
        while (pos < this.input.length && !isSpace(this.input.charCodeAt(pos))) ++pos;
      } else if (/character escape|expected hexadecimal/i.test(msg)) {
        while (pos < this.input.length) {
          var ch = this.input.charCodeAt(pos++);
          if (ch === 34 || ch === 39 || _.isNewLine(ch)) break;
        }
      } else if (/unexpected character/i.test(msg)) {
        pos++;
        replace = false;
      } else if (/regular expression/i.test(msg)) {
        replace = true;
      } else {
        throw e;
      }
      this.resetTo(pos);
      if (replace === true) replace = { start: pos, end: pos, type: _.tokTypes.name, value: "✖" };
      if (replace) {
        if (this.options.locations) replace.loc = new _.SourceLocation(this.toks, _.getLineInfo(this.input, replace.start), _.getLineInfo(this.input, replace.end));
        return replace;
      }
    }
  }
};

lp.resetTo = function (pos) {
  this.toks.pos = pos;
  var ch = this.input.charAt(pos - 1);
  this.toks.exprAllowed = !ch || /[\[\{\(,;:?\/*=+\-~!|&%^<>]/.test(ch) || /[enwfd]/.test(ch) && /\b(keywords|case|else|return|throw|new|in|(instance|type)of|delete|void)$/.test(this.input.slice(pos - 10, pos));

  if (this.options.locations) {
    this.toks.curLine = 1;
    this.toks.lineStart = _.lineBreakG.lastIndex = 0;
    var match = undefined;
    while ((match = _.lineBreakG.exec(this.input)) && match.index < pos) {
      ++this.toks.curLine;
      this.toks.lineStart = match.index + match[0].length;
    }
  }
};

lp.lookAhead = function (n) {
  while (n > this.ahead.length) this.ahead.push(this.readToken());
  return this.ahead[n - 1];
};

},{"..":1,"./state":5}]},{},[3])(3)
});PK
!<ENoL445chrome/devtools/modules/devtools/shared/acorn/walk.js(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.acorn || (g.acorn = {})).walk = 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(_dereq_,module,exports){
// AST walker module for Mozilla Parser API compatible trees

// A simple walk is one where you simply specify callbacks to be
// called on specific nodes. The last two arguments are optional. A
// simple use would be
//
//     walk.simple(myTree, {
//         Expression: function(node) { ... }
//     });
//
// to do something with all expressions. All Parser API node types
// can be used to identify node types, as well as Expression,
// Statement, and ScopeBody, which denote categories of nodes.
//
// The base argument can be used to pass a custom (recursive)
// walker, and state can be used to give this walked an initial
// state.

"use strict";

exports.__esModule = true;
exports.simple = simple;
exports.ancestor = ancestor;
exports.recursive = recursive;
exports.findNodeAt = findNodeAt;
exports.findNodeAround = findNodeAround;
exports.findNodeAfter = findNodeAfter;
exports.findNodeBefore = findNodeBefore;
exports.make = make;

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function simple(node, visitors, base, state, override) {
  if (!base) base = exports.base;(function c(node, st, override) {
    var type = override || node.type,
        found = visitors[type];
    base[type](node, st, c);
    if (found) found(node, st);
  })(node, state, override);
}

// An ancestor walk builds up an array of ancestor nodes (including
// the current node) and passes them to the callback as the state parameter.

function ancestor(node, visitors, base, state) {
  if (!base) base = exports.base;
  if (!state) state = [];(function c(node, st, override) {
    var type = override || node.type,
        found = visitors[type];
    if (node != st[st.length - 1]) {
      st = st.slice();
      st.push(node);
    }
    base[type](node, st, c);
    if (found) found(node, st);
  })(node, state);
}

// A recursive walk is one where your functions override the default
// walkers. They can modify and replace the state parameter that's
// threaded through the walk, and can opt how and whether to walk
// their child nodes (by calling their third argument on these
// nodes).

function recursive(node, state, funcs, base, override) {
  var visitor = funcs ? exports.make(funcs, base) : base;(function c(node, st, override) {
    visitor[override || node.type](node, st, c);
  })(node, state, override);
}

function makeTest(test) {
  if (typeof test == "string") return function (type) {
    return type == test;
  };else if (!test) return function () {
    return true;
  };else return test;
}

var Found = function Found(node, state) {
  _classCallCheck(this, Found);

  this.node = node;this.state = state;
}

// Find a node with a given start, end, and type (all are optional,
// null can be used as wildcard). Returns a {node, state} object, or
// undefined when it doesn't find a matching node.
;

function findNodeAt(node, start, end, test, base, state) {
  test = makeTest(test);
  if (!base) base = exports.base;
  try {
    ;(function c(node, st, override) {
      var type = override || node.type;
      if ((start == null || node.start <= start) && (end == null || node.end >= end)) base[type](node, st, c);
      if ((start == null || node.start == start) && (end == null || node.end == end) && test(type, node)) throw new Found(node, st);
    })(node, state);
  } catch (e) {
    if (e instanceof Found) return e;
    throw e;
  }
}

// Find the innermost node of a given type that contains the given
// position. Interface similar to findNodeAt.

function findNodeAround(node, pos, test, base, state) {
  test = makeTest(test);
  if (!base) base = exports.base;
  try {
    ;(function c(node, st, override) {
      var type = override || node.type;
      if (node.start > pos || node.end < pos) return;
      base[type](node, st, c);
      if (test(type, node)) throw new Found(node, st);
    })(node, state);
  } catch (e) {
    if (e instanceof Found) return e;
    throw e;
  }
}

// Find the outermost matching node after a given position.

function findNodeAfter(node, pos, test, base, state) {
  test = makeTest(test);
  if (!base) base = exports.base;
  try {
    ;(function c(node, st, override) {
      if (node.end < pos) return;
      var type = override || node.type;
      if (node.start >= pos && test(type, node)) throw new Found(node, st);
      base[type](node, st, c);
    })(node, state);
  } catch (e) {
    if (e instanceof Found) return e;
    throw e;
  }
}

// Find the outermost matching node before a given position.

function findNodeBefore(node, pos, test, base, state) {
  test = makeTest(test);
  if (!base) base = exports.base;
  var max = undefined;(function c(node, st, override) {
    if (node.start > pos) return;
    var type = override || node.type;
    if (node.end <= pos && (!max || max.node.end < node.end) && test(type, node)) max = new Found(node, st);
    base[type](node, st, c);
  })(node, state);
  return max;
}

// Used to create a custom walker. Will fill in all missing node
// type properties with the defaults.

function make(funcs, base) {
  if (!base) base = exports.base;
  var visitor = {};
  for (var type in base) visitor[type] = base[type];
  for (var type in funcs) visitor[type] = funcs[type];
  return visitor;
}

function skipThrough(node, st, c) {
  c(node, st);
}
function ignore(_node, _st, _c) {}

// Node walkers.

var base = {};

exports.base = base;
base.Program = base.BlockStatement = function (node, st, c) {
  for (var i = 0; i < node.body.length; ++i) {
    c(node.body[i], st, "Statement");
  }
};
base.Statement = skipThrough;
base.EmptyStatement = ignore;
base.ExpressionStatement = base.ParenthesizedExpression = function (node, st, c) {
  return c(node.expression, st, "Expression");
};
base.IfStatement = function (node, st, c) {
  c(node.test, st, "Expression");
  c(node.consequent, st, "Statement");
  if (node.alternate) c(node.alternate, st, "Statement");
};
base.LabeledStatement = function (node, st, c) {
  return c(node.body, st, "Statement");
};
base.BreakStatement = base.ContinueStatement = ignore;
base.WithStatement = function (node, st, c) {
  c(node.object, st, "Expression");
  c(node.body, st, "Statement");
};
base.SwitchStatement = function (node, st, c) {
  c(node.discriminant, st, "Expression");
  for (var i = 0; i < node.cases.length; ++i) {
    var cs = node.cases[i];
    if (cs.test) c(cs.test, st, "Expression");
    for (var j = 0; j < cs.consequent.length; ++j) {
      c(cs.consequent[j], st, "Statement");
    }
  }
};
base.ReturnStatement = base.YieldExpression = function (node, st, c) {
  if (node.argument) c(node.argument, st, "Expression");
};
base.ThrowStatement = base.SpreadElement = function (node, st, c) {
  return c(node.argument, st, "Expression");
};
base.TryStatement = function (node, st, c) {
  c(node.block, st, "Statement");
  if (node.handler) {
    c(node.handler.param, st, "Pattern");
    c(node.handler.body, st, "ScopeBody");
  }
  if (node.finalizer) c(node.finalizer, st, "Statement");
};
base.WhileStatement = base.DoWhileStatement = function (node, st, c) {
  c(node.test, st, "Expression");
  c(node.body, st, "Statement");
};
base.ForStatement = function (node, st, c) {
  if (node.init) c(node.init, st, "ForInit");
  if (node.test) c(node.test, st, "Expression");
  if (node.update) c(node.update, st, "Expression");
  c(node.body, st, "Statement");
};
base.ForInStatement = base.ForOfStatement = function (node, st, c) {
  c(node.left, st, "ForInit");
  c(node.right, st, "Expression");
  c(node.body, st, "Statement");
};
base.ForInit = function (node, st, c) {
  if (node.type == "VariableDeclaration") c(node, st);else c(node, st, "Expression");
};
base.DebuggerStatement = ignore;

base.FunctionDeclaration = function (node, st, c) {
  return c(node, st, "Function");
};
base.VariableDeclaration = function (node, st, c) {
  for (var i = 0; i < node.declarations.length; ++i) {
    c(node.declarations[i], st);
  }
};
base.VariableDeclarator = function (node, st, c) {
  c(node.id, st, "Pattern");
  if (node.init) c(node.init, st, "Expression");
};

base.Function = function (node, st, c) {
  if (node.id) c(node.id, st, "Pattern");
  for (var i = 0; i < node.params.length; i++) {
    c(node.params[i], st, "Pattern");
  }c(node.body, st, node.expression ? "ScopeExpression" : "ScopeBody");
};
// FIXME drop these node types in next major version
// (They are awkward, and in ES6 every block can be a scope.)
base.ScopeBody = function (node, st, c) {
  return c(node, st, "Statement");
};
base.ScopeExpression = function (node, st, c) {
  return c(node, st, "Expression");
};

base.Pattern = function (node, st, c) {
  if (node.type == "Identifier") c(node, st, "VariablePattern");else if (node.type == "MemberExpression") c(node, st, "MemberPattern");else c(node, st);
};
base.VariablePattern = ignore;
base.MemberPattern = skipThrough;
base.RestElement = function (node, st, c) {
  return c(node.argument, st, "Pattern");
};
base.ArrayPattern = function (node, st, c) {
  for (var i = 0; i < node.elements.length; ++i) {
    var elt = node.elements[i];
    if (elt) c(elt, st, "Pattern");
  }
};
base.ObjectPattern = function (node, st, c) {
  for (var i = 0; i < node.properties.length; ++i) {
    c(node.properties[i].value, st, "Pattern");
  }
};

base.Expression = skipThrough;
base.ThisExpression = base.Super = base.MetaProperty = ignore;
base.ArrayExpression = function (node, st, c) {
  for (var i = 0; i < node.elements.length; ++i) {
    var elt = node.elements[i];
    if (elt) c(elt, st, "Expression");
  }
};
base.ObjectExpression = function (node, st, c) {
  for (var i = 0; i < node.properties.length; ++i) {
    c(node.properties[i], st);
  }
};
base.FunctionExpression = base.ArrowFunctionExpression = base.FunctionDeclaration;
base.SequenceExpression = base.TemplateLiteral = function (node, st, c) {
  for (var i = 0; i < node.expressions.length; ++i) {
    c(node.expressions[i], st, "Expression");
  }
};
base.UnaryExpression = base.UpdateExpression = function (node, st, c) {
  c(node.argument, st, "Expression");
};
base.BinaryExpression = base.LogicalExpression = function (node, st, c) {
  c(node.left, st, "Expression");
  c(node.right, st, "Expression");
};
base.AssignmentExpression = base.AssignmentPattern = function (node, st, c) {
  c(node.left, st, "Pattern");
  c(node.right, st, "Expression");
};
base.ConditionalExpression = function (node, st, c) {
  c(node.test, st, "Expression");
  c(node.consequent, st, "Expression");
  c(node.alternate, st, "Expression");
};
base.NewExpression = base.CallExpression = function (node, st, c) {
  c(node.callee, st, "Expression");
  if (node.arguments) for (var i = 0; i < node.arguments.length; ++i) {
    c(node.arguments[i], st, "Expression");
  }
};
base.MemberExpression = function (node, st, c) {
  c(node.object, st, "Expression");
  if (node.computed) c(node.property, st, "Expression");
};
base.ExportNamedDeclaration = base.ExportDefaultDeclaration = function (node, st, c) {
  if (node.declaration) c(node.declaration, st, node.type == "ExportNamedDeclaration" || node.declaration.id ? "Statement" : "Expression");
  if (node.source) c(node.source, st, "Expression");
};
base.ExportAllDeclaration = function (node, st, c) {
  c(node.source, st, "Expression");
};
base.ImportDeclaration = function (node, st, c) {
  for (var i = 0; i < node.specifiers.length; i++) {
    c(node.specifiers[i], st);
  }c(node.source, st, "Expression");
};
base.ImportSpecifier = base.ImportDefaultSpecifier = base.ImportNamespaceSpecifier = base.Identifier = base.Literal = ignore;

base.TaggedTemplateExpression = function (node, st, c) {
  c(node.tag, st, "Expression");
  c(node.quasi, st);
};
base.ClassDeclaration = base.ClassExpression = function (node, st, c) {
  return c(node, st, "Class");
};
base.Class = function (node, st, c) {
  if (node.id) c(node.id, st, "Pattern");
  if (node.superClass) c(node.superClass, st, "Expression");
  for (var i = 0; i < node.body.body.length; i++) {
    c(node.body.body[i], st);
  }
};
base.MethodDefinition = base.Property = function (node, st, c) {
  if (node.computed) c(node.key, st, "Expression");
  c(node.value, st, "Expression");
};
base.ComprehensionExpression = function (node, st, c) {
  for (var i = 0; i < node.blocks.length; i++) {
    c(node.blocks[i].right, st, "Expression");
  }c(node.body, st, "Expression");
};

},{}]},{},[1])(1)
});PK
!<�c6���8chrome/devtools/modules/devtools/shared/apps/Devices.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
const EventEmitter = require("devtools/shared/old-event-emitter");

/* exported EXPORTED_SYMBOLS */

const EXPORTED_SYMBOLS = ["Devices"];

var addonInstalled = false;

const Devices = {
  _devices: {},

  get helperAddonInstalled() {
    return addonInstalled;
  },
  set helperAddonInstalled(v) {
    addonInstalled = v;
    if (!addonInstalled) {
      for (let name in this._devices) {
        this.unregister(name);
      }
    }
    this.emit("addon-status-updated", v);
  },

  register: function (name, device) {
    this._devices[name] = device;
    this.emit("register");
  },

  unregister: function (name) {
    delete this._devices[name];
    this.emit("unregister");
  },

  available: function () {
    return Object.keys(this._devices).sort();
  },

  getByName: function (name) {
    return this._devices[name];
  }
};
Object.defineProperty(this, "Devices", {
  value: Devices,
  enumerable: true,
  writable: false
});

EventEmitter.decorate(Devices);
PK
!<o�&kee8chrome/devtools/modules/devtools/shared/async-storage.js/**
 *
 * Adapted from https://github.com/mozilla-b2g/gaia/blob/f09993563fb5fec4393eb71816ce76cb00463190/shared/js/async_storage.js
 * (converted to use Promises instead of callbacks).
 *
 * This file defines an asynchronous version of the localStorage API, backed by
 * an IndexedDB database.  It creates a global asyncStorage object that has
 * methods like the localStorage object.
 *
 * To store a value use setItem:
 *
 *   asyncStorage.setItem("key", "value");
 *
 * This returns a promise in case you want confirmation that the value has been stored.
 *
 *  asyncStorage.setItem("key", "newvalue").then(function() {
 *    console.log("new value stored");
 *  });
 *
 * To read a value, call getItem(), but note that you must wait for a promise
 * resolution for the value to be retrieved.
 *
 *  asyncStorage.getItem("key").then(function(value) {
 *    console.log("The value of key is:", value);
 *  });
 *
 * Note that unlike localStorage, asyncStorage does not allow you to store and
 * retrieve values by setting and querying properties directly. You cannot just
 * write asyncStorage.key; you have to explicitly call setItem() or getItem().
 *
 * removeItem(), clear(), length(), and key() are like the same-named methods of
 * localStorage, and all return a promise.
 *
 * The asynchronous nature of getItem() makes it tricky to retrieve multiple
 * values. But unlike localStorage, asyncStorage does not require the values you
 * store to be strings.  So if you need to save multiple values and want to
 * retrieve them together, in a single asynchronous operation, just group the
 * values into a single object. The properties of this object may not include
 * DOM elements, but they may include things like Blobs and typed arrays.
 *
 */

"use strict";

const DBNAME = "devtools-async-storage";
const DBVERSION = 1;
const STORENAME = "keyvaluepairs";
var db = null;

function withStore(type, onsuccess, onerror) {
  if (db) {
    let transaction = db.transaction(STORENAME, type);
    let store = transaction.objectStore(STORENAME);
    onsuccess(store);
  } else {
    let openreq = indexedDB.open(DBNAME, DBVERSION);
    openreq.onerror = function withStoreOnError() {
      onerror();
    };
    openreq.onupgradeneeded = function withStoreOnUpgradeNeeded() {
      // First time setup: create an empty object store
      openreq.result.createObjectStore(STORENAME);
    };
    openreq.onsuccess = function withStoreOnSuccess() {
      db = openreq.result;
      let transaction = db.transaction(STORENAME, type);
      let store = transaction.objectStore(STORENAME);
      onsuccess(store);
    };
  }
}

function getItem(itemKey) {
  return new Promise((resolve, reject) => {
    let req;
    withStore("readonly", (store) => {
      store.transaction.oncomplete = function onComplete() {
        let value = req.result;
        if (value === undefined) {
          value = null;
        }
        resolve(value);
      };
      req = store.get(itemKey);
      req.onerror = function getItemOnError() {
        reject("Error in asyncStorage.getItem(): ", req.error.name);
      };
    }, reject);
  });
}

function setItem(itemKey, value) {
  return new Promise((resolve, reject) => {
    withStore("readwrite", (store) => {
      store.transaction.oncomplete = resolve;
      let req = store.put(value, itemKey);
      req.onerror = function setItemOnError() {
        reject("Error in asyncStorage.setItem(): ", req.error.name);
      };
    }, reject);
  });
}

function removeItem(itemKey) {
  return new Promise((resolve, reject) => {
    withStore("readwrite", (store) => {
      store.transaction.oncomplete = resolve;
      let req = store.delete(itemKey);
      req.onerror = function removeItemOnError() {
        reject("Error in asyncStorage.removeItem(): ", req.error.name);
      };
    }, reject);
  });
}

function clear() {
  return new Promise((resolve, reject) => {
    withStore("readwrite", (store) => {
      store.transaction.oncomplete = resolve;
      let req = store.clear();
      req.onerror = function clearOnError() {
        reject("Error in asyncStorage.clear(): ", req.error.name);
      };
    }, reject);
  });
}

function length() {
  return new Promise((resolve, reject) => {
    let req;
    withStore("readonly", (store) => {
      store.transaction.oncomplete = function onComplete() {
        resolve(req.result);
      };
      req = store.count();
      req.onerror = function lengthOnError() {
        reject("Error in asyncStorage.length(): ", req.error.name);
      };
    }, reject);
  });
}

function key(n) {
  return new Promise((resolve, reject) => {
    if (n < 0) {
      resolve(null);
      return;
    }

    let req;
    withStore("readonly", (store) => {
      store.transaction.oncomplete = function onComplete() {
        let cursor = req.result;
        resolve(cursor ? cursor.key : null);
      };
      let advanced = false;
      req = store.openCursor();
      req.onsuccess = function keyOnSuccess() {
        let cursor = req.result;
        if (!cursor) {
          // this means there weren"t enough keys
          return;
        }
        if (n === 0 || advanced) {
          // Either 1) we have the first key, return it if that's what they
          // wanted, or 2) we"ve got the nth key.
          return;
        }

        // Otherwise, ask the cursor to skip ahead n records
        advanced = true;
        cursor.advance(n);
      };
      req.onerror = function keyOnError() {
        reject("Error in asyncStorage.key(): ", req.error.name);
      };
    }, reject);
  });
}

exports.getItem = getItem;
exports.setItem = setItem;
exports.removeItem = removeItem;
exports.clear = clear;
exports.length = length;
exports.key = key;
PK
!<eJ�ѻ�6chrome/devtools/modules/devtools/shared/async-utils.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

/**
 * Helpers for async functions. Async functions are generator functions that are
 * run by Tasks. An async function returns a Promise for the resolution of the
 * function. When the function returns, the promise is resolved with the
 * returned value. If it throws the promise rejects with the thrown error.
 *
 * See Task documentation at https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/Task.jsm.
 */

/**
 * Adds an event listener to the given element, and then removes its event
 * listener once the event is called, returning the event object as a promise.
 * @param  nsIDOMElement element
 *         The DOM element to listen on
 * @param  String event
 *         The name of the event type to listen for
 * @param  Boolean useCapture
 *         Should we initiate the capture phase?
 * @return Promise
 *         The promise resolved with the event object when the event first
 *         happens
 */
exports.listenOnce = function listenOnce(element, event, useCapture) {
  return new Promise(function (resolve, reject) {
    let onEvent = function (ev) {
      element.removeEventListener(event, onEvent, useCapture);
      resolve(ev);
    };
    element.addEventListener(event, onEvent, useCapture);
  });
};
PK
!<��q��Y�Y6chrome/devtools/modules/devtools/shared/base-loader.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 __URI__ */
/* exported Loader, resolveURI, Module, Require, unload */

"use strict";

this.EXPORTED_SYMBOLS = ["Loader", "resolveURI", "Module", "Require", "unload"];

const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu,
        results: Cr, manager: Cm } = Components;
const systemPrincipal = CC("@mozilla.org/systemprincipal;1", "nsIPrincipal")();

const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm", {});
const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", {});
const { normalize, dirname } = ChromeUtils.import("resource://gre/modules/osfile/ospath_unix.jsm", {});

XPCOMUtils.defineLazyServiceGetter(this, "resProto",
                                   "@mozilla.org/network/protocol;1?name=resource",
                                   "nsIResProtocolHandler");

ChromeUtils.defineModuleGetter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm");

const { defineLazyGetter } = XPCOMUtils;

// Define some shortcuts.
const bind = Function.call.bind(Function.bind);
function* getOwnIdentifiers(x) {
  yield* Object.getOwnPropertyNames(x);
  yield* Object.getOwnPropertySymbols(x);
}

function isJSONURI(uri) {
  return uri.endsWith(".json");
}
function isJSMURI(uri) {
  return uri.endsWith(".jsm");
}
function isJSURI(uri) {
  return uri.endsWith(".js");
}
const AbsoluteRegExp = /^(resource|chrome|file|jar):/;
function isAbsoluteURI(uri) {
  return AbsoluteRegExp.test(uri);
}
function isRelative(id) {
  return id.startsWith(".");
}

function sourceURI(uri) {
  return String(uri).split(" -> ").pop();
}

function isntLoaderFrame(frame) {
  return frame.fileName !== __URI__;
}

function parseURI(uri) {
  return String(uri).split(" -> ").pop();
}

function parseStack(stack) {
  let lines = String(stack).split("\n");
  return lines.reduce(function (frames, line) {
    if (line) {
      let atIndex = line.indexOf("@");
      let columnIndex = line.lastIndexOf(":");
      let lineIndex = line.lastIndexOf(":", columnIndex - 1);
      let fileName = parseURI(line.slice(atIndex + 1, lineIndex));
      let lineNumber = parseInt(line.slice(lineIndex + 1, columnIndex), 10);
      let columnNumber = parseInt(line.slice(columnIndex + 1), 10);
      let name = line.slice(0, atIndex).split("(").shift();
      frames.unshift({
        fileName: fileName,
        name: name,
        lineNumber: lineNumber,
        columnNumber: columnNumber
      });
    }
    return frames;
  }, []);
}

function serializeStack(frames) {
  return frames.reduce(function (stack, frame) {
    return frame.name + "@" +
           frame.fileName + ":" +
           frame.lineNumber + ":" +
           frame.columnNumber + "\n" +
           stack;
  }, "");
}

function readURI(uri) {
  let nsURI = NetUtil.newURI(uri);
  if (nsURI.scheme == "resource") {
    // Resolve to a real URI, this will catch any obvious bad paths without
    // logging assertions in debug builds, see bug 1135219
    uri = resProto.resolveURI(nsURI);
  }

  let stream = NetUtil.newChannel({
    uri: NetUtil.newURI(uri, "UTF-8"),
    loadUsingSystemPrincipal: true}
  ).open2();
  let count = stream.available();
  let data = NetUtil.readInputStreamToString(stream, count, {
    charset: "UTF-8"
  });

  stream.close();

  return data;
}

// Combines all arguments into a resolved, normalized path
function join(base, ...paths) {
  // If this is an absolute URL, we need to normalize only the path portion,
  // or we wind up stripping too many slashes and producing invalid URLs.
  let match = /^((?:resource|file|chrome)\:\/\/[^\/]*|jar:[^!]+!)(.*)/.exec(base);
  if (match) {
    return match[1] + normalize([match[2], ...paths].join("/"));
  }

  return normalize([base, ...paths].join("/"));
}

// Function takes set of options and returns a JS sandbox. Function may be
// passed set of options:
//  - `name`: A string value which identifies the sandbox in about:memory. Will
//    throw exception if omitted.
// - `prototype`: Ancestor for the sandbox that will be created. Defaults to
//    `{}`.
// - `invisibleToDebugger`: True, if the sandbox is part of the debugger
//    implementation and should not be tracked by debugger API.
// For more details see:
// https://developer.mozilla.org/en/Components.utils.Sandbox
function Sandbox(options) {
  // Normalize options and rename to match `Cu.Sandbox` expectations.
  options = {
    // Do not expose `Components` if you really need them (bad idea!) you
    // still can expose via prototype.
    wantComponents: false,
    sandboxName: options.name,
    sandboxPrototype: "prototype" in options ? options.prototype : {},
    invisibleToDebugger: "invisibleToDebugger" in options ?
                         options.invisibleToDebugger : false,
  };

  let sandbox = Cu.Sandbox(systemPrincipal, options);

  delete sandbox.Components;

  return sandbox;
}

// Populates `exports` of the given CommonJS `module` object, in the context
// of the given `loader` by evaluating code associated with it.
function load(loader, module) {
  let { sandboxes, globals } = loader;
  let require = Require(loader, module);

  // We expose set of properties defined by `CommonJS` specification via
  // prototype of the sandbox. Also globals are deeper in the prototype
  // chain so that each module has access to them as well.
  let descriptors = {
    require: {
      configurable: true,
      enumerable: true,
      writable: true,
      value: require
    },
    module: {
      configurable: true,
      enumerable: true,
      writable: true,
      value: module
    },
    exports: {
      configurable: true,
      enumerable: true,
      writable: true,
      value: module.exports
    },
  };

  let sandbox;
  if (loader.useSharedGlobalSandbox) {
    // Create a new object in this sandbox, that will be used as
    // the scope object for this particular module
    sandbox = new loader.sharedGlobalSandbox.Object();
    descriptors.lazyRequire = {
      configurable: true,
      value: lazyRequire.bind(sandbox),
    };
    descriptors.lazyRequireModule = {
      configurable: true,
      value: lazyRequireModule.bind(sandbox),
    };

    if ("console" in globals) {
      descriptors.console = {
        configurable: true,
        get() {
          return globals.console;
        },
      };
    }
    let define = Object.getOwnPropertyDescriptor(globals, "define");
    if (define && define.value) {
      descriptors.define = define;
    }
    if ("DOMParser" in globals) {
      descriptors.DOMParser = Object.getOwnPropertyDescriptor(globals, "DOMParser");
    }
    Object.defineProperties(sandbox, descriptors);
  } else {
    sandbox = Sandbox({
      name: module.uri,
      prototype: Object.create(globals, descriptors),
      invisibleToDebugger: loader.invisibleToDebugger
    });
  }
  sandboxes[module.uri] = sandbox;

  let originalExports = module.exports;
  try {
    Services.scriptloader.loadSubScript(module.uri, sandbox, "UTF-8");
  } catch (error) {
    let { message, fileName, lineNumber } = error;
    let stack = error.stack || Error().stack;
    let frames = parseStack(stack).filter(isntLoaderFrame);
    let toString = String(error);
    let file = sourceURI(fileName);

    // Note that `String(error)` where error is from subscript loader does
    // not puts `:` after `"Error"` unlike regular errors thrown by JS code.
    // If there is a JS stack then this error has already been handled by an
    // inner module load.
    if (/^Error opening input stream/.test(String(error))) {
      let caller = frames.slice(0).pop();
      fileName = caller.fileName;
      lineNumber = caller.lineNumber;
      message = "Module `" + module.id + "` is not found at " + module.uri;
      toString = message;
    } else if (frames[frames.length - 1].fileName !== file) {
      // Workaround for a Bug 910653. Errors thrown by subscript loader
      // do not include `stack` field and above created error won't have
      // fileName or lineNumber of the module being loaded, so we ensure
      // it does.
      frames.push({ fileName: file, lineNumber: lineNumber, name: "" });
    }

    let prototype = typeof (error) === "object" ? error.constructor.prototype :
                    Error.prototype;

    throw Object.create(prototype, {
      message: { value: message, writable: true, configurable: true },
      fileName: { value: fileName, writable: true, configurable: true },
      lineNumber: { value: lineNumber, writable: true, configurable: true },
      stack: { value: serializeStack(frames), writable: true, configurable: true },
      toString: { value: () => toString, writable: true, configurable: true },
    });
  }

  // Only freeze the exports object if we created it ourselves. Modules
  // which completely replace the exports object and still want it
  // frozen need to freeze it themselves.
  if (module.exports === originalExports) {
    Object.freeze(module.exports);
  }

  return module;
}

// Utility function to normalize module `uri`s so they have `.js` extension.
function normalizeExt(uri) {
  if (isJSURI(uri) || isJSONURI(uri) || isJSMURI(uri)) {
    return uri;
  }
  return uri + ".js";
}

// Utility function to join paths. In common case `base` is a
// `requirer.uri` but in some cases it may be `baseURI`. In order to
// avoid complexity we require `baseURI` with a trailing `/`.
function resolve(id, base) {
  if (!isRelative(id)) {
    return id;
  }

  let baseDir = dirname(base);

  let resolved;
  if (baseDir.includes(":")) {
    resolved = join(baseDir, id);
  } else {
    resolved = normalize(`${baseDir}/${id}`);
  }

  // Joining and normalizing removes the "./" from relative files.
  // We need to ensure the resolution still has the root
  if (base.startsWith("./")) {
    resolved = "./" + resolved;
  }

  return resolved;
}

function compileMapping(paths) {
  // Make mapping array that is sorted from longest path to shortest path.
  let mapping = Object.keys(paths)
                      .sort((a, b) => b.length - a.length)
                      .map(path => [path, paths[path]]);

  const PATTERN = /([.\\?+*(){}[\]^$])/g;
  const escapeMeta = str => str.replace(PATTERN, "\\$1");

  let patterns = [];
  paths = {};

  for (let [path, uri] of mapping) {
    // Strip off any trailing slashes to make comparisons simpler
    if (path.endsWith("/")) {
      path = path.slice(0, -1);
      uri = uri.replace(/\/+$/, "");
    }

    paths[path] = uri;

    // We only want to match path segments explicitly. Examples:
    // * "foo/bar" matches for "foo/bar"
    // * "foo/bar" matches for "foo/bar/baz"
    // * "foo/bar" does not match for "foo/bar-1"
    // * "foo/bar/" does not match for "foo/bar"
    // * "foo/bar/" matches for "foo/bar/baz"
    //
    // Check for an empty path, an exact match, or a substring match
    // with the next character being a forward slash.
    if (path == "") {
      patterns.push("");
    } else {
      patterns.push(`${escapeMeta(path)}(?=$|/)`);
    }
  }

  let pattern = new RegExp(`^(${patterns.join("|")})`);

  // This will replace the longest matching path mapping at the start of
  // the ID string with its mapped value.
  return id => {
    return id.replace(pattern, (m0, m1) => paths[m1]);
  };
}

function resolveURI(id, mapping) {
  // Do not resolve if already a resource URI
  if (isAbsoluteURI(id)) {
    return normalizeExt(id);
  }

  return normalizeExt(mapping(id));
}

/**
 * Defines lazy getters on the given object, which lazily require the
 * given module the first time they are accessed, and then resolve that
 * module's exported properties.
 *
 * @param {object} obj
 *        The target object on which to define the lazy getters.
 * @param {string} moduleId
 *        The ID of the module to require, as passed to require().
 * @param {Array<string | object>} args
 *        Any number of properties to import from the module. A string
 *        will cause the property to be defined which resolves to the
 *        same property in the module's exports. An object will define a
 *        lazy getter for every value in the object which corresponds to
 *        the given key in the module's exports, as in an ordinary
 *        destructuring assignment.
 */
function lazyRequire(obj, moduleId, ...args) {
  let module;
  let getModule = () => {
    if (!module) {
      module = this.require(moduleId);
    }
    return module;
  };

  for (let props of args) {
    if (typeof props !== "object") {
      props = {[props]: props};
    }

    for (let [fromName, toName] of Object.entries(props)) {
      defineLazyGetter(obj, toName, () => getModule()[fromName]);
    }
  }
}

/**
 * Defines a lazy getter on the given object which causes a module to be
 * lazily imported the first time it is accessed.
 *
 * @param {object} obj
 *        The target object on which to define the lazy getter.
 * @param {string} moduleId
 *        The ID of the module to require, as passed to require().
 * @param {string} [prop = moduleId]
 *        The name of the lazy getter property to define.
 */
function lazyRequireModule(obj, moduleId, prop = moduleId) {
  defineLazyGetter(obj, prop, () => this.require(moduleId));
}

// Creates version of `require` that will be exposed to the given `module`
// in the context of the given `loader`. Each module gets own limited copy
// of `require` that is allowed to load only a modules that are associated
// with it during link time.
function Require(loader, requirer) {
  let {
    modules, mapping, mappingCache, requireHook
  } = loader;

  function require(id) {
    if (!id) {
      // Throw if `id` is not passed.
      throw Error("You must provide a module name when calling require() from "
                  + requirer.id, requirer.uri);
    }

    if (requireHook) {
      return requireHook(id, _require);
    }

    return _require(id);
  }

  function _require(id) {
    let { uri, requirement } = getRequirements(id);

    let module = null;
    // If module is already cached by loader then just use it.
    if (uri in modules) {
      module = modules[uri];
    } else if (isJSMURI(uri)) {
      module = modules[uri] = Module(requirement, uri);
      module.exports = ChromeUtils.import(uri, {});
    } else if (isJSONURI(uri)) {
      let data;

      // First attempt to load and parse json uri
      // ex: `test.json`
      // If that doesn"t exist, check for `test.json.js`
      // for node parity
      try {
        data = JSON.parse(readURI(uri));
        module = modules[uri] = Module(requirement, uri);
        module.exports = data;
      } catch (err) {
        // If error thrown from JSON parsing, throw that, do not
        // attempt to find .json.js file
        if (err && /JSON\.parse/.test(err.message)) {
          throw err;
        }
        uri = uri + ".js";
      }
    }

    // If not yet cached, load and cache it.
    // We also freeze module to prevent it from further changes
    // at runtime.
    if (!(uri in modules)) {
      // Many of the loader's functionalities are dependent
      // on modules[uri] being set before loading, so we set it and
      // remove it if we have any errors.
      module = modules[uri] = Module(requirement, uri);
      try {
        Object.freeze(load(loader, module));
      } catch (e) {
        // Clear out modules cache so we can throw on a second invalid require
        delete modules[uri];
        // Also clear out the Sandbox that was created
        delete loader.sandboxes[uri];
        throw e;
      }
    }

    return module.exports;
  }

  // Resolution function taking a module name/path and
  // returning a resourceURI and a `requirement` used by the loader.
  // Used by both `require` and `require.resolve`.
  function getRequirements(id) {
    if (!id) {
      // Throw if `id` is not passed.
      throw Error("you must provide a module name when calling require() from "
                  + requirer.id, requirer.uri);
    }

    let requirement, uri;

    if (modules[id]) {
      uri = requirement = id;
    } else if (requirer) {
      // Resolve `id` to its requirer if it's relative.
      requirement = resolve(id, requirer.id);
    } else {
      requirement = id;
    }

    // Resolves `uri` of module using loaders resolve function.
    if (!uri) {
      if (mappingCache.has(requirement)) {
        uri = mappingCache.get(requirement);
      } else {
        uri = resolveURI(requirement, mapping);
        mappingCache.set(requirement, uri);
      }
    }

    // Throw if `uri` can not be resolved.
    if (!uri) {
      throw Error("Module: Can not resolve '" + id + "' module required by " +
                  requirer.id + " located at " + requirer.uri, requirer.uri);
    }

    return { uri: uri, requirement: requirement };
  }

  // Expose the `resolve` function for this `Require` instance
  require.resolve = _require.resolve = function (id) {
    let { uri } = getRequirements(id);
    return uri;
  };

  // This is like webpack's require.context.  It returns a new require
  // function that prepends the prefix to any requests.
  require.context = prefix => {
    return id => {
      return require(prefix + id);
    };
  };

  return require;
}

// Makes module object that is made available to CommonJS modules when they
// are evaluated, along with `exports` and `require`.
function Module(id, uri) {
  return Object.create(null, {
    id: { enumerable: true, value: id },
    exports: { enumerable: true, writable: true, value: Object.create(null),
               configurable: true },
    uri: { value: uri }
  });
}

// Takes `loader`, and unload `reason` string and notifies all observers that
// they should cleanup after them-self.
function unload(loader, reason) {
  // subject is a unique object created per loader instance.
  // This allows any code to cleanup on loader unload regardless of how
  // it was loaded. To handle unload for specific loader subject may be
  // asserted against loader.destructor or require("@loader/unload")
  // Note: We don not destroy loader's module cache or sandboxes map as
  // some modules may do cleanup in subsequent turns of event loop. Destroying
  // cache may cause module identity problems in such cases.
  let subject = { wrappedJSObject: loader.destructor };
  Services.obs.notifyObservers(subject, "sdk:loader:destroy", reason);
}

// Function makes new loader that can be used to load CommonJS modules.
// Loader takes following options:
// - `paths`: Mandatory dictionary of require path mapped to absolute URIs.
//   Object keys are path prefix used in require(), values are URIs where each
//   prefix should be mapped to.
// - `sharedGlobal`: Boolean, if True, loads all module in a single, shared
//   global in order to create only one global and compartment.
// - `globals`: Optional map of globals, that all module scopes will inherit
//   from. Map is also exposed under `globals` property of the returned loader
//   so it can be extended further later. Defaults to `{}`.
// - `sandboxName`: String, name of the sandbox displayed in about:memory.
// - `invisibleToDebugger`: Boolean. Should be true when loading debugger
//   modules, in order to ignore them from the Debugger API.
// - `sandboxPrototype`: Object used to define globals on all module's
//   sandboxes.
// - `requireHook`: Optional function used to replace native require function
//   from loader. This function receive the module path as first argument,
//   and native require method as second argument.
function Loader(options) {
  let { paths, sharedGlobal, globals } = options;
  if (!globals) {
    globals = {};
  }

  // We create an identity object that will be dispatched on an unload
  // event as subject. This way unload listeners will be able to assert
  // which loader is unloaded. Please note that we intentionally don"t
  // use `loader` as subject to prevent a loader access leakage through
  // observer notifications.
  let destructor = Object.create(null);

  let mapping = compileMapping(paths);

  // Define pseudo modules.
  let modules = {
    "@loader/unload": destructor,
    "@loader/options": options,
    "chrome": { Cc, Ci, Cu, Cr, Cm,
                CC: bind(CC, Components), components: Components,
                ChromeWorker
    }
  };

  const builtinModuleExports = modules;
  modules = {};
  for (let id of Object.keys(builtinModuleExports)) {
    // We resolve `uri` from `id` since modules are cached by `uri`.
    let uri = resolveURI(id, mapping);
    let module = Module(id, uri);

    // Lazily expose built-in modules in order to
    // allow them to be loaded lazily.
    Object.defineProperty(module, "exports", {
      enumerable: true,
      get: function () {
        return builtinModuleExports[id];
      }
    });

    modules[uri] = module;
  }

  // Create the unique sandbox we will be using for all modules,
  // so that we prevent creating a new compartment per module.
  // The side effect is that all modules will share the same
  // global objects.
  let sharedGlobalSandbox = Sandbox({
    name: options.sandboxName || "DevTools",
    invisibleToDebugger: options.invisibleToDebugger || false,
    prototype: options.sandboxPrototype || globals,
  });

  if (options.sandboxPrototype) {
    // If we were given a sandboxPrototype, we have to define the globals on
    // the sandbox directly. Note that this will not work for callers who
    // depend on being able to add globals after the loader was created.
    for (let name of getOwnIdentifiers(globals)) {
      Object.defineProperty(sharedGlobalSandbox, name,
                            Object.getOwnPropertyDescriptor(globals, name));
    }
  }

  // Loader object is just a representation of a environment
  // state. We mark its properties non-enumerable
  // as they are pure implementation detail that no one should rely upon.
  let returnObj = {
    destructor: { enumerable: false, value: destructor },
    globals: { enumerable: false, value: globals },
    mapping: { enumerable: false, value: mapping },
    mappingCache: { enumerable: false, value: new Map() },
    // Map of module objects indexed by module URIs.
    modules: { enumerable: false, value: modules },
    useSharedGlobalSandbox: { enumerable: false, value: !!sharedGlobal },
    sharedGlobalSandbox: { enumerable: false, value: sharedGlobalSandbox },
    // Map of module sandboxes indexed by module URIs.
    sandboxes: { enumerable: false, value: {} },
    // Whether the modules loaded should be ignored by the debugger
    invisibleToDebugger: { enumerable: false,
                           value: options.invisibleToDebugger || false },
    requireHook: { enumerable: false, value: options.requireHook },
  };

  return Object.create(null, returnObj);
}
PK
!<b�	ڵ'�':chrome/devtools/modules/devtools/shared/builtin-modules.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 defines custom globals injected in all our modules and also
 * pseudo modules that aren't separate files but just dynamically set values.
 *
 * As it does so, the module itself doesn't have access to these globals,
 * nor the pseudo modules. Be careful to avoid loading any other js module as
 * they would also miss them.
 */

const { Cu, CC, Cc, Ci } = require("chrome");
const promise = require("resource://gre/modules/Promise.jsm").Promise;
const jsmScope = require("resource://gre/modules/Services.jsm");
const { Services } = jsmScope;
// Steal various globals only available in JSM scope (and not Sandbox one)
const {
  console,
  HeapSnapshot,
} = Cu.getGlobalForObject(jsmScope);

// Create a single Sandbox to access global properties needed in this module.
// Sandbox are memory expensive, so we should create as little as possible.
const {
  atob,
  btoa,
  ChromeUtils,
  CSS,
  CSSRule,
  FileReader,
  FormData,
  indexedDB,
  InspectorUtils,
  TextDecoder,
  TextEncoder,
  URL,
  XMLHttpRequest,
} = Cu.Sandbox(CC("@mozilla.org/systemprincipal;1", "nsIPrincipal")(), {
  wantGlobalProperties: [
    "atob",
    "btoa",
    "ChromeUtils",
    "CSS",
    "CSSRule",
    "FileReader",
    "FormData",
    "indexedDB",
    "TextDecoder",
    "TextEncoder",
    "InspectorUtils",
    "URL",
    "XMLHttpRequest",
  ]
});

/**
 * Defines a getter on a specified object that will be created upon first use.
 *
 * @param object
 *        The object to define the lazy getter on.
 * @param name
 *        The name of the getter to define on object.
 * @param lambda
 *        A function that returns what the getter should return.  This will
 *        only ever be called once.
 */
function defineLazyGetter(object, name, lambda) {
  Object.defineProperty(object, name, {
    get: function () {
      // Redefine this accessor property as a data property.
      // Delete it first, to rule out "too much recursion" in case object is
      // a proxy whose defineProperty handler might unwittingly trigger this
      // getter again.
      delete object[name];
      let value = lambda.apply(object);
      Object.defineProperty(object, name, {
        value,
        writable: true,
        configurable: true,
        enumerable: true
      });
      return value;
    },
    configurable: true,
    enumerable: true
  });
}

/**
 * Defines a getter on a specified object for a service.  The service will not
 * be obtained until first use.
 *
 * @param object
 *        The object to define the lazy getter on.
 * @param name
 *        The name of the getter to define on object for the service.
 * @param contract
 *        The contract used to obtain the service.
 * @param interfaceName
 *        The name of the interface to query the service to.
 */
function defineLazyServiceGetter(object, name, contract, interfaceName) {
  defineLazyGetter(object, name, function () {
    return Cc[contract].getService(Ci[interfaceName]);
  });
}

/**
 * Defines a getter on a specified object for a module.  The module will not
 * be imported until first use. The getter allows to execute setup and
 * teardown code (e.g.  to register/unregister to services) and accepts
 * a proxy object which acts on behalf of the module until it is imported.
 *
 * @param object
 *        The object to define the lazy getter on.
 * @param name
 *        The name of the getter to define on object for the module.
 * @param resource
 *        The URL used to obtain the module.
 * @param symbol
 *        The name of the symbol exported by the module.
 *        This parameter is optional and defaults to name.
 * @param preLambda
 *        A function that is executed when the proxy is set up.
 *        This will only ever be called once.
 * @param postLambda
 *        A function that is executed when the module has been imported to
 *        run optional teardown procedures on the proxy object.
 *        This will only ever be called once.
 * @param proxy
 *        An object which acts on behalf of the module to be imported until
 *        the module has been imported.
 */
function defineLazyModuleGetter(object, name, resource, symbol,
                                preLambda, postLambda, proxy) {
  proxy = proxy || {};

  if (typeof (preLambda) === "function") {
    preLambda.apply(proxy);
  }

  defineLazyGetter(object, name, function () {
    let temp = {};
    try {
      ChromeUtils.import(resource, temp);

      if (typeof (postLambda) === "function") {
        postLambda.apply(proxy);
      }
    } catch (ex) {
      Cu.reportError("Failed to load module " + resource + ".");
      throw ex;
    }
    return temp[symbol || name];
  });
}

/**
 * Define a getter property on the given object that requires the given
 * module. This enables delaying importing modules until the module is
 * actually used.
 *
 * @param Object obj
 *    The object to define the property on.
 * @param String property
 *    The property name.
 * @param String module
 *    The module path.
 * @param Boolean destructure
 *    Pass true if the property name is a member of the module's exports.
 */
function lazyRequireGetter(obj, property, module, destructure) {
  Object.defineProperty(obj, property, {
    get: () => {
      // Redefine this accessor property as a data property.
      // Delete it first, to rule out "too much recursion" in case obj is
      // a proxy whose defineProperty handler might unwittingly trigger this
      // getter again.
      delete obj[property];
      let value = destructure
        ? require(module)[property]
        : require(module || property);
      Object.defineProperty(obj, property, {
        value,
        writable: true,
        configurable: true,
        enumerable: true
      });
      return value;
    },
    configurable: true,
    enumerable: true
  });
}

// List of pseudo modules exposed to all devtools modules.
exports.modules = {
  ChromeUtils,
  FileReader,
  HeapSnapshot,
  InspectorUtils,
  promise,
  // Expose "chrome" Promise, which aren't related to any document
  // and so are never frozen, even if the browser loader module which
  // pull it is destroyed. See bug 1402779.
  Promise,
  Services: Object.create(Services),
};

defineLazyGetter(exports.modules, "Debugger", () => {
  // addDebuggerToGlobal only allows adding the Debugger object to a global. The
  // this object is not guaranteed to be a global (in particular on B2G, due to
  // compartment sharing), so add the Debugger object to a sandbox instead.
  let sandbox = Cu.Sandbox(CC("@mozilla.org/systemprincipal;1", "nsIPrincipal")());
  Cu.evalInSandbox(
    "Components.utils.import('resource://gre/modules/jsdebugger.jsm');" +
    "addDebuggerToGlobal(this);",
    sandbox
  );
  return sandbox.Debugger;
});

defineLazyGetter(exports.modules, "Timer", () => {
  let {setTimeout, clearTimeout} = require("resource://gre/modules/Timer.jsm");
  // Do not return Cu.import result, as SDK loader would freeze Timer.jsm globals...
  return {
    setTimeout,
    clearTimeout
  };
});

defineLazyGetter(exports.modules, "xpcInspector", () => {
  return Cc["@mozilla.org/jsinspector;1"].getService(Ci.nsIJSInspector);
});

// List of all custom globals exposed to devtools modules.
// Changes here should be mirrored to devtools/.eslintrc.
exports.globals = {
  atob,
  btoa,
  console,
  CSS,
  CSSRule,
  // Make sure `define` function exists.  This allows defining some modules
  // in AMD format while retaining CommonJS compatibility through this hook.
  // JSON Viewer needs modules in AMD format, as it currently uses RequireJS
  // from a content document and can't access our usual loaders.  So, any
  // modules shared with the JSON Viewer should include a define wrapper:
  //
  //   // Make this available to both AMD and CJS environments
  //   define(function(require, exports, module) {
  //     ... code ...
  //   });
  //
  // Bug 1248830 will work out a better plan here for our content module
  // loading needs, especially as we head towards devtools.html.
  define(factory) {
    factory(this.require, this.exports, this.module);
  },
  DocumentFragment: Ci.nsIDOMDocumentFragment,
  Element: Ci.nsIDOMElement,
  FormData,
  isWorker: false,
  loader: {
    lazyGetter: defineLazyGetter,
    lazyImporter: defineLazyModuleGetter,
    lazyServiceGetter: defineLazyServiceGetter,
    lazyRequireGetter: lazyRequireGetter,
    // Defined by Loader.jsm
    id: null
  },
  Node: Ci.nsIDOMNode,
  reportError: Cu.reportError,
  TextDecoder,
  TextEncoder,
  URL,
  XMLHttpRequest,
};
// SDK loader copy globals property descriptors on each module global object
// so that we have to memoize them from here in order to instanciate each
// global only once.
// `globals` is a cache object on which we put all global values
// and we set getters on `exports.globals` returning `globals` values.
let globals = {};
function lazyGlobal(name, getter) {
  defineLazyGetter(globals, name, getter);
  Object.defineProperty(exports.globals, name, {
    get: function () {
      return globals[name];
    },
    configurable: true,
    enumerable: true
  });
}

// Lazily define a few things so that the corresponding jsms are only loaded
// when used.
lazyGlobal("clearTimeout", () => {
  return require("resource://gre/modules/Timer.jsm").clearTimeout;
});
lazyGlobal("setTimeout", () => {
  return require("resource://gre/modules/Timer.jsm").setTimeout;
});
lazyGlobal("clearInterval", () => {
  return require("resource://gre/modules/Timer.jsm").clearInterval;
});
lazyGlobal("setInterval", () => {
  return require("resource://gre/modules/Timer.jsm").setInterval;
});
lazyGlobal("DOMParser", () => {
  return CC("@mozilla.org/xmlextras/domparser;1", "nsIDOMParser");
});
lazyGlobal("WebSocket", () => {
  return Services.appShell.hiddenDOMWindow.WebSocket;
});
lazyGlobal("indexedDB", () => {
  return require("devtools/shared/indexed-db").createDevToolsIndexedDB(indexedDB);
});
PK
!<�^ ��>chrome/devtools/modules/devtools/shared/client/addon-client.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {DebuggerClient} = require("devtools/shared/client/debugger-client");

function AddonClient(client, actor) {
  this._client = client;
  this._actor = actor;
  this.request = this._client.request;
  this.events = [];
}

AddonClient.prototype = {
  get actor() {
    return this._actor;
  },
  get _transport() {
    return this._client._transport;
  },

  /**
   * Detach the client from the addon actor.
   *
   * @param function onResponse
   *        Called with the response packet.
   */
  detach: DebuggerClient.requester({
    type: "detach"
  }, {
    after: function (response) {
      if (this._client.activeAddon === this) {
        this._client.activeAddon = null;
      }
      this._client.unregisterClient(this);
      return response;
    },
  })
};

module.exports = AddonClient;
PK
!<:���Echrome/devtools/modules/devtools/shared/client/array-buffer-client.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {arg, DebuggerClient} = require("devtools/shared/client/debugger-client");

/**
 * A ArrayBufferClient provides a way to access ArrayBuffer from the
 * debugger server.
 *
 * @param client DebuggerClient
 *        The debugger client parent.
 * @param grip Object
 *        A pause-lifetime ArrayBuffer grip returned by the protocol.
 */
function ArrayBufferClient(client, grip) {
  this._grip = grip;
  this._client = client;
  this.request = this._client.request;
}
ArrayBufferClient.prototype = {
  get actor() {
    return this._grip.actor;
  },
  get length() {
    return this._grip.length;
  },
  get _transport() {
    return this._client._transport;
  },

  valid: true,

  slice: DebuggerClient.requester({
    type: "slice",
    start: arg(0),
    count: arg(1)
  }),
};

module.exports = ArrayBufferClient;
PK
!<P�	��Cchrome/devtools/modules/devtools/shared/client/breakpoint-client.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 promise = require("devtools/shared/deprecated-sync-thenables");

const eventSource = require("devtools/shared/client/event-source");
const {DebuggerClient} = require("devtools/shared/client/debugger-client");

/**
 * Breakpoint clients are used to remove breakpoints that are no longer used.
 *
 * @param client DebuggerClient
 *        The debugger client parent.
 * @param sourceClient SourceClient
 *        The source where this breakpoint exists
 * @param actor string
 *        The actor ID for this breakpoint.
 * @param location object
 *        The location of the breakpoint. This is an object with two properties:
 *        url and line.
 * @param condition string
 *        The conditional expression of the breakpoint
 */
function BreakpointClient(client, sourceClient, actor, location, condition) {
  this._client = client;
  this._actor = actor;
  this.location = location;
  this.location.actor = sourceClient.actor;
  this.location.url = sourceClient.url;
  this.source = sourceClient;
  this.request = this._client.request;

  // The condition property should only exist if it's a truthy value
  if (condition) {
    this.condition = condition;
  }
}

BreakpointClient.prototype = {

  _actor: null,
  get actor() {
    return this._actor;
  },
  get _transport() {
    return this._client._transport;
  },

  /**
   * Remove the breakpoint from the server.
   */
  remove: DebuggerClient.requester({
    type: "delete"
  }),

  /**
   * Determines if this breakpoint has a condition
   */
  hasCondition: function () {
    let root = this._client.mainRoot;
    // XXX bug 990137: We will remove support for client-side handling of
    // conditional breakpoints
    if (root.traits.conditionalBreakpoints) {
      return "condition" in this;
    }
    return "conditionalExpression" in this;
  },

  /**
   * Get the condition of this breakpoint. Currently we have to
   * support locally emulated conditional breakpoints until the
   * debugger servers are updated (see bug 990137). We used a
   * different property when moving it server-side to ensure that we
   * are testing the right code.
   */
  getCondition: function () {
    let root = this._client.mainRoot;
    if (root.traits.conditionalBreakpoints) {
      return this.condition;
    }
    return this.conditionalExpression;
  },

  /**
   * Set the condition of this breakpoint
   */
  setCondition: function (gThreadClient, condition) {
    let root = this._client.mainRoot;
    let deferred = promise.defer();

    if (root.traits.conditionalBreakpoints) {
      let info = {
        line: this.location.line,
        column: this.location.column,
        condition: condition
      };

      // Remove the current breakpoint and add a new one with the
      // condition.
      this.remove(response => {
        if (response && response.error) {
          deferred.reject(response);
          return;
        }

        this.source.setBreakpoint(info, (resp, newBreakpoint) => {
          if (resp && resp.error) {
            deferred.reject(resp);
          } else {
            deferred.resolve(newBreakpoint);
          }
        });
      });
    } else {
      // The property shouldn't even exist if the condition is blank
      if (condition === "") {
        delete this.conditionalExpression;
      } else {
        this.conditionalExpression = condition;
      }
      deferred.resolve(this);
    }

    return deferred.promise;
  }
};

eventSource(BreakpointClient.prototype);

module.exports = BreakpointClient;
PK
!<A�$//Dchrome/devtools/modules/devtools/shared/client/connection-manager.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 {Cc, Ci, Cr} = require("chrome");
const EventEmitter = require("devtools/shared/old-event-emitter");
const { DebuggerServer } = require("devtools/server/main");
const { DebuggerClient } = require("devtools/shared/client/debugger-client");
const Services = require("Services");

const REMOTE_TIMEOUT = "devtools.debugger.remote-timeout";

/**
 * Connection Manager.
 *
 * To use this module:
 * const {ConnectionManager} = require("devtools/shared/client/connection-manager");
 *
 * # ConnectionManager
 *
 * Methods:
 *  . Connection createConnection(host, port)
 *  . void       destroyConnection(connection)
 *  . Number     getFreeTCPPort()
 *
 * Properties:
 *  . Array      connections
 *
 * # Connection
 *
 * A connection is a wrapper around a debugger client. It has a simple
 * API to instantiate a connection to a debugger server. Once disconnected,
 * no need to re-create a Connection object. Calling `connect()` again
 * will re-create a debugger client.
 *
 * Methods:
 *  . connect()             Connect to host:port. Expect a "connecting" event.
 *                          If no host is not specified, a local pipe is used
 *  . connect(transport)    Connect via transport. Expect a "connecting" event.
 *  . disconnect()          Disconnect if connected. Expect a "disconnecting" event
 *
 * Properties:
 *  . host                  IP address or hostname
 *  . port                  Port
 *  . logs                  Current logs. "newlog" event notifies new available logs
 *  . store                 Reference to a local data store (see below)
 *  . keepConnecting        Should the connection keep trying to connect?
 *  . timeoutDelay          When should we give up (in ms)?
 *                          0 means wait forever.
 *  . encryption            Should the connection be encrypted?
 *  . authentication        What authentication scheme should be used?
 *  . authenticator         The |Authenticator| instance used.  Overriding
 *                          properties of this instance may be useful to
 *                          customize authentication UX for a specific use case.
 *  . advertisement         The server's advertisement if found by discovery
 *  . status                Connection status:
 *                            Connection.Status.CONNECTED
 *                            Connection.Status.DISCONNECTED
 *                            Connection.Status.CONNECTING
 *                            Connection.Status.DISCONNECTING
 *                            Connection.Status.DESTROYED
 *
 * Events (as in event-emitter.js):
 *  . Connection.Events.CONNECTING      Trying to connect to host:port
 *  . Connection.Events.CONNECTED       Connection is successful
 *  . Connection.Events.DISCONNECTING   Trying to disconnect from server
 *  . Connection.Events.DISCONNECTED    Disconnected (at client request,
 *                                      or because of a timeout or connection error)
 *  . Connection.Events.STATUS_CHANGED  The connection status (connection.status)
 *                                      has changed
 *  . Connection.Events.TIMEOUT         Connection timeout
 *  . Connection.Events.HOST_CHANGED    Host has changed
 *  . Connection.Events.PORT_CHANGED    Port has changed
 *  . Connection.Events.NEW_LOG         A new log line is available
 *
 */

var ConnectionManager = {
  _connections: new Set(),
  createConnection: function (host, port) {
    let c = new Connection(host, port);
    c.once("destroy", (event) => this.destroyConnection(c));
    this._connections.add(c);
    this.emit("new", c);
    return c;
  },
  destroyConnection: function (connection) {
    if (this._connections.has(connection)) {
      this._connections.delete(connection);
      if (connection.status != Connection.Status.DESTROYED) {
        connection.destroy();
      }
    }
  },
  get connections() {
    return [...this._connections];
  },
  getFreeTCPPort: function () {
    let serv = Cc["@mozilla.org/network/server-socket;1"]
                 .createInstance(Ci.nsIServerSocket);
    serv.init(-1, true, -1);
    let port = serv.port;
    serv.close();
    return port;
  },
};

EventEmitter.decorate(ConnectionManager);

var lastID = -1;

function Connection(host, port) {
  EventEmitter.decorate(this);
  this.uid = ++lastID;
  this.host = host;
  this.port = port;
  this._setStatus(Connection.Status.DISCONNECTED);
  this._onDisconnected = this._onDisconnected.bind(this);
  this._onConnected = this._onConnected.bind(this);
  this._onTimeout = this._onTimeout.bind(this);
  this.resetOptions();
}

Connection.Status = {
  CONNECTED: "connected",
  DISCONNECTED: "disconnected",
  CONNECTING: "connecting",
  DISCONNECTING: "disconnecting",
  DESTROYED: "destroyed",
};

Connection.Events = {
  CONNECTED: Connection.Status.CONNECTED,
  DISCONNECTED: Connection.Status.DISCONNECTED,
  CONNECTING: Connection.Status.CONNECTING,
  DISCONNECTING: Connection.Status.DISCONNECTING,
  DESTROYED: Connection.Status.DESTROYED,
  TIMEOUT: "timeout",
  STATUS_CHANGED: "status-changed",
  HOST_CHANGED: "host-changed",
  PORT_CHANGED: "port-changed",
  NEW_LOG: "new_log"
};

Connection.prototype = {
  logs: "",
  log: function (str) {
    let d = new Date();
    let hours = ("0" + d.getHours()).slice(-2);
    let minutes = ("0" + d.getMinutes()).slice(-2);
    let seconds = ("0" + d.getSeconds()).slice(-2);
    let timestamp = [hours, minutes, seconds].join(":") + ": ";
    str = timestamp + str;
    this.logs += "\n" + str;
    this.emit(Connection.Events.NEW_LOG, str);
  },

  get client() {
    return this._client;
  },

  get host() {
    return this._host;
  },

  set host(value) {
    if (this._host && this._host == value) {
      return;
    }
    this._host = value;
    this.emit(Connection.Events.HOST_CHANGED);
  },

  get port() {
    return this._port;
  },

  set port(value) {
    if (this._port && this._port == value) {
      return;
    }
    this._port = value;
    this.emit(Connection.Events.PORT_CHANGED);
  },

  get authentication() {
    return this._authentication;
  },

  set authentication(value) {
    this._authentication = value;
    // Create an |Authenticator| of this type
    if (!value) {
      this.authenticator = null;
      return;
    }
    let AuthenticatorType = DebuggerClient.Authenticators.get(value);
    this.authenticator = new AuthenticatorType.Client();
  },

  get advertisement() {
    return this._advertisement;
  },

  set advertisement(advertisement) {
    // The full advertisement may contain more info than just the standard keys
    // below, so keep a copy for use during connection later.
    this._advertisement = advertisement;
    if (advertisement) {
      ["host", "port", "encryption", "authentication"].forEach(key => {
        this[key] = advertisement[key];
      });
    }
  },

  /**
   * Settings to be passed to |socketConnect| at connection time.
   */
  get socketSettings() {
    let settings = {};
    if (this.advertisement) {
      // Use the advertisement as starting point if it exists, as it may contain
      // extra data, like the server's cert.
      Object.assign(settings, this.advertisement);
    }
    Object.assign(settings, {
      host: this.host,
      port: this.port,
      encryption: this.encryption,
      authenticator: this.authenticator
    });
    return settings;
  },

  timeoutDelay: Services.prefs.getIntPref(REMOTE_TIMEOUT),

  resetOptions() {
    this.keepConnecting = false;
    this.timeoutDelay = Services.prefs.getIntPref(REMOTE_TIMEOUT);
    this.encryption = false;
    this.authentication = null;
    this.advertisement = null;
  },

  disconnect: function (force) {
    if (this.status == Connection.Status.DESTROYED) {
      return;
    }
    clearTimeout(this._timeoutID);
    if (this.status == Connection.Status.CONNECTED ||
        this.status == Connection.Status.CONNECTING) {
      this.log("disconnecting");
      this._setStatus(Connection.Status.DISCONNECTING);
      if (this._client) {
        this._client.close();
      }
    }
  },

  connect: function (transport) {
    if (this.status == Connection.Status.DESTROYED) {
      return;
    }
    if (!this._client) {
      this._customTransport = transport;
      if (this._customTransport) {
        this.log("connecting (custom transport)");
      } else {
        this.log("connecting to " + this.host + ":" + this.port);
      }
      this._setStatus(Connection.Status.CONNECTING);

      if (this.timeoutDelay > 0) {
        this._timeoutID = setTimeout(this._onTimeout, this.timeoutDelay);
      }
      this._clientConnect();
    } else {
      let msg = "Can't connect. Client is not fully disconnected";
      this.log(msg);
      throw new Error(msg);
    }
  },

  destroy: function () {
    this.log("killing connection");
    clearTimeout(this._timeoutID);
    this.keepConnecting = false;
    if (this._client) {
      this._client.close();
      this._client = null;
    }
    this._setStatus(Connection.Status.DESTROYED);
  },

  async _getTransport() {
    if (this._customTransport) {
      return this._customTransport;
    }
    if (!this.host) {
      return DebuggerServer.connectPipe();
    }
    let settings = this.socketSettings;
    let transport = await DebuggerClient.socketConnect(settings);
    return transport;
  },

  _clientConnect: function () {
    this._getTransport().then(transport => {
      if (!transport) {
        return;
      }
      this._client = new DebuggerClient(transport);
      this._client.addOneTimeListener("closed", this._onDisconnected);
      this._client.connect().then(this._onConnected);
    }, e => {
      // If we're continuously trying to connect, we expect the connection to be
      // rejected a couple times, so don't log these.
      if (!this.keepConnecting || e.result !== Cr.NS_ERROR_CONNECTION_REFUSED) {
        console.error(e);
      }
      // In some cases, especially on Mac, the openOutputStream call in
      // DebuggerClient.socketConnect may throw NS_ERROR_NOT_INITIALIZED.
      // It occurs when we connect agressively to the simulator,
      // and keep trying to open a socket to the server being started in
      // the simulator.
      this._onDisconnected();
    });
  },

  get status() {
    return this._status;
  },

  _setStatus: function (value) {
    if (this._status && this._status == value) {
      return;
    }
    this._status = value;
    this.emit(value);
    this.emit(Connection.Events.STATUS_CHANGED, value);
  },

  _onDisconnected: function () {
    this._client = null;
    this._customTransport = null;

    if (this._status == Connection.Status.CONNECTING && this.keepConnecting) {
      setTimeout(() => this._clientConnect(), 100);
      return;
    }

    clearTimeout(this._timeoutID);

    switch (this.status) {
      case Connection.Status.CONNECTED:
        this.log("disconnected (unexpected)");
        break;
      case Connection.Status.CONNECTING:
        this.log("connection error. Possible causes: USB port not connected, port not " +
                 "forwarded (adb forward), wrong host or port, remote debugging not " +
                 "enabled on the device.");
        break;
      default:
        this.log("disconnected");
    }
    this._setStatus(Connection.Status.DISCONNECTED);
  },

  _onConnected: function () {
    this.log("connected");
    clearTimeout(this._timeoutID);
    this._setStatus(Connection.Status.CONNECTED);
  },

  _onTimeout: function () {
    this.log("connection timeout. Possible causes: didn't click on 'accept' (prompt).");
    this.emit(Connection.Events.TIMEOUT);
    this.disconnect();
  },
};

exports.ConnectionManager = ConnectionManager;
exports.Connection = Connection;
PK
!<�g�5RR;chrome/devtools/modules/devtools/shared/client/constants.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

/**
 * Set of protocol messages that affect thread state, and the
 * state the actor is in after each message.
 */
const ThreadStateTypes = {
  "paused": "paused",
  "resumed": "attached",
  "detached": "detached",
  "running": "attached"
};

/**
 * Set of protocol messages that are sent by the server without a prior request
 * by the client.
 */
const UnsolicitedNotifications = {
  "consoleAPICall": "consoleAPICall",
  "eventNotification": "eventNotification",
  "fileActivity": "fileActivity",
  "lastPrivateContextExited": "lastPrivateContextExited",
  "logMessage": "logMessage",
  "networkEvent": "networkEvent",
  "networkEventUpdate": "networkEventUpdate",
  "documentEvent": "documentEvent",
  "newGlobal": "newGlobal",
  "newScript": "newScript",
  "tabDetached": "tabDetached",
  "tabListChanged": "tabListChanged",
  "reflowActivity": "reflowActivity",
  "addonListChanged": "addonListChanged",
  "workerListChanged": "workerListChanged",
  "serviceWorkerRegistrationListChanged": "serviceWorkerRegistrationList",
  "tabNavigated": "tabNavigated",
  "frameUpdate": "frameUpdate",
  "pageError": "pageError",
  "documentLoad": "documentLoad",
  "enteredFrame": "enteredFrame",
  "exitedFrame": "exitedFrame",
  "appOpen": "appOpen",
  "appClose": "appClose",
  "appInstall": "appInstall",
  "appUninstall": "appUninstall",
  "evaluationResult": "evaluationResult",
  "newSource": "newSource",
  "updatedSource": "updatedSource",
  "inspectObject": "inspectObject"
};

/**
 * Set of pause types that are sent by the server and not as an immediate
 * response to a client request.
 */
const UnsolicitedPauses = {
  "resumeLimit": "resumeLimit",
  "debuggerStatement": "debuggerStatement",
  "breakpoint": "breakpoint",
  "DOMEvent": "DOMEvent",
  "watchpoint": "watchpoint",
  "exception": "exception"
};

module.exports = {
  ThreadStateTypes,
  UnsolicitedNotifications,
  UnsolicitedPauses,
};
PK
!<�ܬ�����Achrome/devtools/modules/devtools/shared/client/debugger-client.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Services = require("Services");
const promise = require("devtools/shared/deprecated-sync-thenables");

const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const { getStack, callFunctionWithAsyncStack } = require("devtools/shared/platform/stack");
const eventSource = require("devtools/shared/client/event-source");
const {
  ThreadStateTypes,
  UnsolicitedNotifications,
  UnsolicitedPauses,
} = require("./constants");

loader.lazyRequireGetter(this, "Authentication", "devtools/shared/security/auth");
loader.lazyRequireGetter(this, "DebuggerSocket", "devtools/shared/security/socket", true);
loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
loader.lazyRequireGetter(this, "getDeviceFront", "devtools/shared/fronts/device", true);

loader.lazyRequireGetter(this, "WebConsoleClient", "devtools/shared/webconsole/client", true);
loader.lazyRequireGetter(this, "AddonClient", "devtools/shared/client/addon-client");
loader.lazyRequireGetter(this, "RootClient", "devtools/shared/client/root-client");
loader.lazyRequireGetter(this, "TabClient", "devtools/shared/client/tab-client");
loader.lazyRequireGetter(this, "ThreadClient", "devtools/shared/client/thread-client");
loader.lazyRequireGetter(this, "TraceClient", "devtools/shared/client/trace-client");
loader.lazyRequireGetter(this, "WorkerClient", "devtools/shared/client/worker-client");
loader.lazyRequireGetter(this, "ObjectClient", "devtools/shared/client/object-client");

const noop = () => {};

// Define the minimum officially supported version of Firefox when connecting to a remote
// runtime. (Use ".0a1" to support the very first nightly version)
// This is usually the current ESR version.
const MIN_SUPPORTED_PLATFORM_VERSION = "52.0a1";
const MS_PER_DAY = 86400000;

/**
 * Creates a client for the remote debugging protocol server. This client
 * provides the means to communicate with the server and exchange the messages
 * required by the protocol in a traditional JavaScript API.
 */
function DebuggerClient(transport) {
  this._transport = transport;
  this._transport.hooks = this;

  // Map actor ID to client instance for each actor type.
  this._clients = new Map();

  this._pendingRequests = new Map();
  this._activeRequests = new Map();
  this._eventsEnabled = true;

  this.traits = {};

  this.request = this.request.bind(this);
  this.localTransport = this._transport.onOutputStreamReady === undefined;

  /*
   * As the first thing on the connection, expect a greeting packet from
   * the connection's root actor.
   */
  this.mainRoot = null;
  this.expectReply("root", (packet) => {
    this.mainRoot = new RootClient(this, packet);
    this.emit("connected", packet.applicationType, packet.traits);
  });
}

/**
 * A declarative helper for defining methods that send requests to the server.
 *
 * @param packetSkeleton
 *        The form of the packet to send. Can specify fields to be filled from
 *        the parameters by using the |arg| function.
 * @param before
 *        The function to call before sending the packet. Is passed the packet,
 *        and the return value is used as the new packet. The |this| context is
 *        the instance of the client object we are defining a method for.
 * @param after
 *        The function to call after the response is received. It is passed the
 *        response, and the return value is considered the new response that
 *        will be passed to the callback. The |this| context is the instance of
 *        the client object we are defining a method for.
 * @return Request
 *         The `Request` object that is a Promise object and resolves once
 *         we receive the response. (See request method for more details)
 */
DebuggerClient.requester = function (packetSkeleton, config = {}) {
  let { before, after } = config;
  return DevToolsUtils.makeInfallible(function (...args) {
    let outgoingPacket = {
      to: packetSkeleton.to || this.actor
    };

    let maxPosition = -1;
    for (let k of Object.keys(packetSkeleton)) {
      if (packetSkeleton[k] instanceof DebuggerClient.Argument) {
        let { position } = packetSkeleton[k];
        outgoingPacket[k] = packetSkeleton[k].getArgument(args);
        maxPosition = Math.max(position, maxPosition);
      } else {
        outgoingPacket[k] = packetSkeleton[k];
      }
    }

    if (before) {
      outgoingPacket = before.call(this, outgoingPacket);
    }

    return this.request(outgoingPacket, DevToolsUtils.makeInfallible((response) => {
      if (after) {
        let { from } = response;
        response = after.call(this, response);
        if (!response.from) {
          response.from = from;
        }
      }

      // The callback is always the last parameter.
      let thisCallback = args[maxPosition + 1];
      if (thisCallback) {
        thisCallback(response);
      }
      return response;
    }, "DebuggerClient.requester request callback"));
  }, "DebuggerClient.requester");
};

function arg(pos) {
  return new DebuggerClient.Argument(pos);
}
exports.arg = arg;

DebuggerClient.Argument = function (position) {
  this.position = position;
};

DebuggerClient.Argument.prototype.getArgument = function (params) {
  if (!(this.position in params)) {
    throw new Error("Bad index into params: " + this.position);
  }
  return params[this.position];
};

// Expose these to save callers the trouble of importing DebuggerSocket
DebuggerClient.socketConnect = function (options) {
  // Defined here instead of just copying the function to allow lazy-load
  return DebuggerSocket.connect(options);
};
DevToolsUtils.defineLazyGetter(DebuggerClient, "Authenticators", () => {
  return Authentication.Authenticators;
});
DevToolsUtils.defineLazyGetter(DebuggerClient, "AuthenticationResult", () => {
  return Authentication.AuthenticationResult;
});

DebuggerClient.prototype = {
  /**
   * Connect to the server and start exchanging protocol messages.
   *
   * @param onConnected function
   *        If specified, will be called when the greeting packet is
   *        received from the debugging server.
   *
   * @return Promise
   *         Resolves once connected with an array whose first element
   *         is the application type, by default "browser", and the second
   *         element is the traits object (help figure out the features
   *         and behaviors of the server we connect to. See RootActor).
   */
  connect: function (onConnected) {
    let deferred = promise.defer();
    this.emit("connect");

    // Also emit the event on the |DebuggerClient| object (not on the instance),
    // so it's possible to track all instances.
    EventEmitter.emit(DebuggerClient, "connect", this);

    this.addOneTimeListener("connected", (name, applicationType, traits) => {
      this.traits = traits;
      if (onConnected) {
        onConnected(applicationType, traits);
      }
      deferred.resolve([applicationType, traits]);
    });

    this._transport.ready();
    return deferred.promise;
  },

  /**
   * Tells if the remote device is using a supported version of Firefox.
   *
   * @return Object with the following attributes:
   *   * String incompatible
   *            null 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 checkRuntimeVersion(listTabsForm) {
    let incompatible = null;

    // Instead of requiring to pass `listTabsForm` here,
    // we can call getRoot() instead, but only once Firefox ESR59 is released
    let deviceFront = await getDeviceFront(this, listTabsForm);
    let desc = await deviceFront.getDescription();

    // 1) Check for Firefox too recent on device.
    // Compare device and firefox build IDs
    // and only compare by day (strip hours/minutes) to prevent
    // warning against builds of the same day.
    let runtimeID = desc.appbuildid.substr(0, 8);
    let localID = Services.appinfo.appBuildID.substr(0, 8);
    function buildIDToDate(buildID) {
      let fields = buildID.match(/(\d{4})(\d{2})(\d{2})/);
      // Date expects 0 - 11 for months
      return new Date(fields[1], Number.parseInt(fields[2], 10) - 1, fields[3]);
    }
    let runtimeDate = buildIDToDate(runtimeID);
    let localDate = buildIDToDate(localID);
    // Allow device 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.
    if (runtimeDate - localDate > 7 * MS_PER_DAY) {
      incompatible = "too-recent";
    }

    // 2) Check for too old Firefox on device
    let platformversion = desc.platformversion;
    if (Services.vc.compare(platformversion, MIN_SUPPORTED_PLATFORM_VERSION) < 0) {
      incompatible = "too-old";
    }

    return {
      incompatible,
      minVersion: MIN_SUPPORTED_PLATFORM_VERSION,
      runtimeVersion: platformversion,
      localID,
      runtimeID,
    };
  },

  /**
   * Shut down communication with the debugging server.
   *
   * @param onClosed function
   *        If specified, will be called when the debugging connection
   *        has been closed. This parameter is deprecated - please use
   *        the returned Promise.
   * @return Promise
   *         Resolves after the underlying transport is closed.
   */
  close: function (onClosed) {
    let deferred = promise.defer();
    if (onClosed) {
      deferred.promise.then(onClosed);
    }

    // Disable detach event notifications, because event handlers will be in a
    // cleared scope by the time they run.
    this._eventsEnabled = false;

    let cleanup = () => {
      this._transport.close();
      this._transport = null;
    };

    // If the connection is already closed,
    // there is no need to detach client
    // as we won't be able to send any message.
    if (this._closed) {
      cleanup();
      deferred.resolve();
      return deferred.promise;
    }

    this.addOneTimeListener("closed", deferred.resolve);

    // Call each client's `detach` method by calling
    // lastly registered ones first to give a chance
    // to detach child clients first.
    let clients = [...this._clients.values()];
    this._clients.clear();
    const detachClients = () => {
      let client = clients.pop();
      if (!client) {
        // All clients detached.
        cleanup();
        return;
      }
      if (client.detach) {
        client.detach(detachClients);
        return;
      }
      detachClients();
    };
    detachClients();

    return deferred.promise;
  },

  /*
   * This function exists only to preserve DebuggerClient's interface;
   * new code should say 'client.mainRoot.listTabs()'.
   */
  listTabs: function (options, onResponse) {
    return this.mainRoot.listTabs(options, onResponse);
  },

  /*
   * This function exists only to preserve DebuggerClient's interface;
   * new code should say 'client.mainRoot.listAddons()'.
   */
  listAddons: function (onResponse) {
    return this.mainRoot.listAddons(onResponse);
  },

  getTab: function (filter) {
    return this.mainRoot.getTab(filter);
  },

  /**
   * Attach to a tab actor.
   *
   * @param string tabActor
   *        The actor ID for the tab to attach.
   * @param function onResponse
   *        Called with the response packet and a TabClient
   *        (which will be undefined on error).
   */
  attachTab: function (tabActor, onResponse = noop) {
    if (this._clients.has(tabActor)) {
      let cachedTab = this._clients.get(tabActor);
      let cachedResponse = {
        cacheDisabled: cachedTab.cacheDisabled,
        javascriptEnabled: cachedTab.javascriptEnabled,
        traits: cachedTab.traits,
      };
      DevToolsUtils.executeSoon(() => onResponse(cachedResponse, cachedTab));
      return promise.resolve([cachedResponse, cachedTab]);
    }

    let packet = {
      to: tabActor,
      type: "attach"
    };
    return this.request(packet).then(response => {
      let tabClient;
      if (!response.error) {
        tabClient = new TabClient(this, response);
        this.registerClient(tabClient);
      }
      onResponse(response, tabClient);
      return [response, tabClient];
    });
  },

  attachWorker: function (workerActor, onResponse = noop) {
    let workerClient = this._clients.get(workerActor);
    if (workerClient !== undefined) {
      let response = {
        from: workerClient.actor,
        type: "attached",
        url: workerClient.url
      };
      DevToolsUtils.executeSoon(() => onResponse(response, workerClient));
      return promise.resolve([response, workerClient]);
    }

    return this.request({ to: workerActor, type: "attach" }).then(response => {
      if (response.error) {
        onResponse(response, null);
        return [response, null];
      }

      workerClient = new WorkerClient(this, response);
      this.registerClient(workerClient);
      onResponse(response, workerClient);
      return [response, workerClient];
    });
  },

  /**
   * Attach to an addon actor.
   *
   * @param string addonActor
   *        The actor ID for the addon to attach.
   * @param function onResponse
   *        Called with the response packet and a AddonClient
   *        (which will be undefined on error).
   */
  attachAddon: function (addonActor, onResponse = noop) {
    let packet = {
      to: addonActor,
      type: "attach"
    };
    return this.request(packet).then(response => {
      let addonClient;
      if (!response.error) {
        addonClient = new AddonClient(this, addonActor);
        this.registerClient(addonClient);
        this.activeAddon = addonClient;
      }
      onResponse(response, addonClient);
      return [response, addonClient];
    });
  },

  /**
   * Attach to a Web Console actor.
   *
   * @param string consoleActor
   *        The ID for the console actor to attach to.
   * @param array listeners
   *        The console listeners you want to start.
   * @param function onResponse
   *        Called with the response packet and a WebConsoleClient
   *        instance (which will be undefined on error).
   */
  attachConsole:
  function (consoleActor, listeners, onResponse = noop) {
    let packet = {
      to: consoleActor,
      type: "startListeners",
      listeners: listeners,
    };

    return this.request(packet).then(response => {
      let consoleClient;
      if (!response.error) {
        if (this._clients.has(consoleActor)) {
          consoleClient = this._clients.get(consoleActor);
        } else {
          consoleClient = new WebConsoleClient(this, response);
          this.registerClient(consoleClient);
        }
      }
      onResponse(response, consoleClient);
      return [response, consoleClient];
    });
  },

  /**
   * Attach to a global-scoped thread actor for chrome debugging.
   *
   * @param string threadActor
   *        The actor ID for the thread to attach.
   * @param function onResponse
   *        Called with the response packet and a ThreadClient
   *        (which will be undefined on error).
   * @param object options
   *        Configuration options.
   *        - useSourceMaps: whether to use source maps or not.
   */
  attachThread: function (threadActor, onResponse = noop, options = {}) {
    if (this._clients.has(threadActor)) {
      let client = this._clients.get(threadActor);
      DevToolsUtils.executeSoon(() => onResponse({}, client));
      return promise.resolve([{}, client]);
    }

    let packet = {
      to: threadActor,
      type: "attach",
      options,
    };
    return this.request(packet).then(response => {
      let threadClient;
      if (!response.error) {
        threadClient = new ThreadClient(this, threadActor);
        this.registerClient(threadClient);
      }
      onResponse(response, threadClient);
      return [response, threadClient];
    });
  },

  /**
   * Attach to a trace actor.
   *
   * @param string traceActor
   *        The actor ID for the tracer to attach.
   * @param function onResponse
   *        Called with the response packet and a TraceClient
   *        (which will be undefined on error).
   */
  attachTracer: function (traceActor, onResponse = noop) {
    if (this._clients.has(traceActor)) {
      let client = this._clients.get(traceActor);
      DevToolsUtils.executeSoon(() => onResponse({}, client));
      return promise.resolve([{}, client]);
    }

    let packet = {
      to: traceActor,
      type: "attach"
    };
    return this.request(packet).then(response => {
      let traceClient;
      if (!response.error) {
        traceClient = new TraceClient(this, traceActor);
        this.registerClient(traceClient);
      }
      onResponse(response, traceClient);
      return [response, traceClient];
    });
  },

  /**
   * Fetch the ChromeActor for the main process or ChildProcessActor for a
   * a given child process ID.
   *
   * @param number id
   *        The ID for the process to attach (returned by `listProcesses`).
   *        Connected to the main process if omitted, or is 0.
   */
  getProcess: function (id) {
    let packet = {
      to: "root",
      type: "getProcess"
    };
    if (typeof (id) == "number") {
      packet.id = id;
    }
    return this.request(packet);
  },

  /**
   * Release an object actor.
   *
   * @param string actor
   *        The actor ID to send the request to.
   * @param onResponse function
   *        If specified, will be called with the response packet when
   *        debugging server responds.
   */
  release: DebuggerClient.requester({
    to: arg(0),
    type: "release"
  }),

  /**
   * Send a request to the debugging server.
   *
   * @param packet object
   *        A JSON packet to send to the debugging server.
   * @param onResponse function
   *        If specified, will be called with the JSON response packet when
   *        debugging server responds.
   * @return Request
   *         This object emits a number of events to allow you to respond to
   *         different parts of the request lifecycle.
   *         It is also a Promise object, with a `then` method, that is resolved
   *         whenever a JSON or a Bulk response is received; and is rejected
   *         if the response is an error.
   *         Note: This return value can be ignored if you are using JSON alone,
   *         because the callback provided in |onResponse| will be bound to the
   *         "json-reply" event automatically.
   *
   *         Events emitted:
   *         * json-reply: The server replied with a JSON packet, which is
   *           passed as event data.
   *         * bulk-reply: The server replied with bulk data, which you can read
   *           using the event data object containing:
   *           * actor:  Name of actor that received the packet
   *           * type:   Name of actor's method that was called on receipt
   *           * length: Size of the data to be read
   *           * stream: This input stream should only be used directly if you
   *                     can ensure that you will read exactly |length| bytes
   *                     and will not close the stream when reading is complete
   *           * done:   If you use the stream directly (instead of |copyTo|
   *                     below), you must signal completion by resolving /
   *                     rejecting this deferred.  If it's rejected, the
   *                     transport will be closed.  If an Error is supplied as a
   *                     rejection value, it will be logged via |dumpn|.  If you
   *                     do use |copyTo|, resolving is taken care of for you
   *                     when copying completes.
   *           * copyTo: A helper function for getting your data out of the
   *                     stream that meets the stream handling requirements
   *                     above, and has the following signature:
   *             @param  output nsIAsyncOutputStream
   *                     The stream to copy to.
   *             @return Promise
   *                     The promise is resolved when copying completes or
   *                     rejected if any (unexpected) errors occur.
   *                     This object also emits "progress" events for each chunk
   *                     that is copied.  See stream-utils.js.
   */
  request: function (packet, onResponse) {
    if (!this.mainRoot) {
      throw Error("Have not yet received a hello packet from the server.");
    }
    let type = packet.type || "";
    if (!packet.to) {
      throw Error("'" + type + "' request packet has no destination.");
    }

    // The onResponse callback might modify the response, so we need to call
    // it and resolve the promise with its result if it's truthy.
    const safeOnResponse = response => {
      if (!onResponse) {
        return response;
      }
      return onResponse(response) || response;
    };

    if (this._closed) {
      let msg = "'" + type + "' request packet to " +
                "'" + packet.to + "' " +
               "can't be sent as the connection is closed.";
      let resp = { error: "connectionClosed", message: msg };
      return promise.reject(safeOnResponse(resp));
    }

    let request = new Request(packet);
    request.format = "json";
    request.stack = getStack();

    // Implement a Promise like API on the returned object
    // that resolves/rejects on request response
    let deferred = promise.defer();
    function listenerJson(resp) {
      removeRequestListeners();
      if (resp.error) {
        deferred.reject(safeOnResponse(resp));
      } else {
        deferred.resolve(safeOnResponse(resp));
      }
    }
    function listenerBulk(resp) {
      removeRequestListeners();
      deferred.resolve(safeOnResponse(resp));
    }

    const removeRequestListeners = () => {
      request.off("json-reply", listenerJson);
      request.off("bulk-reply", listenerBulk);
    };

    request.on("json-reply", listenerJson);
    request.on("bulk-reply", listenerBulk);

    this._sendOrQueueRequest(request);
    request.then = deferred.promise.then.bind(deferred.promise);

    return request;
  },

  /**
   * Transmit streaming data via a bulk request.
   *
   * This method initiates the bulk send process by queuing up the header data.
   * The caller receives eventual access to a stream for writing.
   *
   * Since this opens up more options for how the server might respond (it could
   * send back either JSON or bulk data), and the returned Request object emits
   * events for different stages of the request process that you may want to
   * react to.
   *
   * @param request Object
   *        This is modeled after the format of JSON packets above, but does not
   *        actually contain the data, but is instead just a routing header:
   *          * actor:  Name of actor that will receive the packet
   *          * type:   Name of actor's method that should be called on receipt
   *          * length: Size of the data to be sent
   * @return Request
   *         This object emits a number of events to allow you to respond to
   *         different parts of the request lifecycle.
   *
   *         Events emitted:
   *         * bulk-send-ready: Ready to send bulk data to the server, using the
   *           event data object containing:
   *           * stream:   This output stream should only be used directly if
   *                       you can ensure that you will write exactly |length|
   *                       bytes and will not close the stream when writing is
   *                       complete
   *           * done:     If you use the stream directly (instead of |copyFrom|
   *                       below), you must signal completion by resolving /
   *                       rejecting this deferred.  If it's rejected, the
   *                       transport will be closed.  If an Error is supplied as
   *                       a rejection value, it will be logged via |dumpn|.  If
   *                       you do use |copyFrom|, resolving is taken care of for
   *                       you when copying completes.
   *           * copyFrom: A helper function for getting your data onto the
   *                       stream that meets the stream handling requirements
   *                       above, and has the following signature:
   *             @param  input nsIAsyncInputStream
   *                     The stream to copy from.
   *             @return Promise
   *                     The promise is resolved when copying completes or
   *                     rejected if any (unexpected) errors occur.
   *                     This object also emits "progress" events for each chunk
   *                     that is copied.  See stream-utils.js.
   *         * json-reply: The server replied with a JSON packet, which is
   *           passed as event data.
   *         * bulk-reply: The server replied with bulk data, which you can read
   *           using the event data object containing:
   *           * actor:  Name of actor that received the packet
   *           * type:   Name of actor's method that was called on receipt
   *           * length: Size of the data to be read
   *           * stream: This input stream should only be used directly if you
   *                     can ensure that you will read exactly |length| bytes
   *                     and will not close the stream when reading is complete
   *           * done:   If you use the stream directly (instead of |copyTo|
   *                     below), you must signal completion by resolving /
   *                     rejecting this deferred.  If it's rejected, the
   *                     transport will be closed.  If an Error is supplied as a
   *                     rejection value, it will be logged via |dumpn|.  If you
   *                     do use |copyTo|, resolving is taken care of for you
   *                     when copying completes.
   *           * copyTo: A helper function for getting your data out of the
   *                     stream that meets the stream handling requirements
   *                     above, and has the following signature:
   *             @param  output nsIAsyncOutputStream
   *                     The stream to copy to.
   *             @return Promise
   *                     The promise is resolved when copying completes or
   *                     rejected if any (unexpected) errors occur.
   *                     This object also emits "progress" events for each chunk
   *                     that is copied.  See stream-utils.js.
   */
  startBulkRequest: function (request) {
    if (!this.traits.bulk) {
      throw Error("Server doesn't support bulk transfers");
    }
    if (!this.mainRoot) {
      throw Error("Have not yet received a hello packet from the server.");
    }
    if (!request.type) {
      throw Error("Bulk packet is missing the required 'type' field.");
    }
    if (!request.actor) {
      throw Error("'" + request.type + "' bulk packet has no destination.");
    }
    if (!request.length) {
      throw Error("'" + request.type + "' bulk packet has no length.");
    }

    request = new Request(request);
    request.format = "bulk";

    this._sendOrQueueRequest(request);

    return request;
  },

  /**
   * If a new request can be sent immediately, do so.  Otherwise, queue it.
   */
  _sendOrQueueRequest(request) {
    let actor = request.actor;
    if (!this._activeRequests.has(actor)) {
      this._sendRequest(request);
    } else {
      this._queueRequest(request);
    }
  },

  /**
   * Send a request.
   * @throws Error if there is already an active request in flight for the same
   *         actor.
   */
  _sendRequest(request) {
    let actor = request.actor;
    this.expectReply(actor, request);

    if (request.format === "json") {
      this._transport.send(request.request);
      return;
    }

    this._transport.startBulkSend(request.request).then((...args) => {
      request.emit("bulk-send-ready", ...args);
    });
  },

  /**
   * Queue a request to be sent later.  Queues are only drained when an in
   * flight request to a given actor completes.
   */
  _queueRequest(request) {
    let actor = request.actor;
    let queue = this._pendingRequests.get(actor) || [];
    queue.push(request);
    this._pendingRequests.set(actor, queue);
  },

  /**
   * Attempt the next request to a given actor (if any).
   */
  _attemptNextRequest(actor) {
    if (this._activeRequests.has(actor)) {
      return;
    }
    let queue = this._pendingRequests.get(actor);
    if (!queue) {
      return;
    }
    let request = queue.shift();
    if (queue.length === 0) {
      this._pendingRequests.delete(actor);
    }
    this._sendRequest(request);
  },

  /**
   * Arrange to hand the next reply from |actor| to the handler bound to
   * |request|.
   *
   * DebuggerClient.prototype.request / startBulkRequest usually takes care of
   * establishing the handler for a given request, but in rare cases (well,
   * greetings from new root actors, is the only case at the moment) we must be
   * prepared for a "reply" that doesn't correspond to any request we sent.
   */
  expectReply: function (actor, request) {
    if (this._activeRequests.has(actor)) {
      throw Error("clashing handlers for next reply from " + actor);
    }

    // If a handler is passed directly (as it is with the handler for the root
    // actor greeting), create a dummy request to bind this to.
    if (typeof request === "function") {
      let handler = request;
      request = new Request();
      request.on("json-reply", handler);
    }

    this._activeRequests.set(actor, request);
  },

  // Transport hooks.

  /**
   * Called by DebuggerTransport to dispatch incoming packets as appropriate.
   *
   * @param packet object
   *        The incoming packet.
   */
  onPacket: function (packet) {
    if (!packet.from) {
      DevToolsUtils.reportException(
        "onPacket",
        new Error("Server did not specify an actor, dropping packet: " +
                  JSON.stringify(packet)));
      return;
    }

    // If we have a registered Front for this actor, let it handle the packet
    // and skip all the rest of this unpleasantness.
    let front = this.getActor(packet.from);
    if (front) {
      front.onPacket(packet);
      return;
    }

    // Check for "forwardingCancelled" here instead of using a client to handle it.
    // This is necessary because we might receive this event while the client is closing,
    // and the clients have already been removed by that point.
    if (this.mainRoot &&
        packet.from == this.mainRoot.actor &&
        packet.type == "forwardingCancelled") {
      this.purgeRequests(packet.prefix);
      return;
    }

    if (this._clients.has(packet.from) && packet.type) {
      let client = this._clients.get(packet.from);
      let type = packet.type;
      if (client.events.includes(type)) {
        client.emit(type, packet);
        // we ignore the rest, as the client is expected to handle this packet.
        return;
      }
    }

    let activeRequest;
    // See if we have a handler function waiting for a reply from this
    // actor. (Don't count unsolicited notifications or pauses as
    // replies.)
    if (this._activeRequests.has(packet.from) &&
        !(packet.type in UnsolicitedNotifications) &&
        !(packet.type == ThreadStateTypes.paused &&
          packet.why.type in UnsolicitedPauses)) {
      activeRequest = this._activeRequests.get(packet.from);
      this._activeRequests.delete(packet.from);
    }

    // If there is a subsequent request for the same actor, hand it off to the
    // transport.  Delivery of packets on the other end is always async, even
    // in the local transport case.
    this._attemptNextRequest(packet.from);

    // Packets that indicate thread state changes get special treatment.
    if (packet.type in ThreadStateTypes &&
        this._clients.has(packet.from) &&
        typeof this._clients.get(packet.from)._onThreadState == "function") {
      this._clients.get(packet.from)._onThreadState(packet);
    }

    // Only try to notify listeners on events, not responses to requests
    // that lack a packet type.
    if (packet.type) {
      this.emit(packet.type, packet);
    }

    if (activeRequest) {
      let emitReply = () => activeRequest.emit("json-reply", packet);
      if (activeRequest.stack) {
        callFunctionWithAsyncStack(emitReply, activeRequest.stack,
                                   "DevTools RDP");
      } else {
        emitReply();
      }
    }
  },

  /**
   * Called by the DebuggerTransport to dispatch incoming bulk packets as
   * appropriate.
   *
   * @param packet object
   *        The incoming packet, which contains:
   *        * actor:  Name of actor that will receive the packet
   *        * type:   Name of actor's method that should be called on receipt
   *        * length: Size of the data to be read
   *        * stream: This input stream should only be used directly if you can
   *                  ensure that you will read exactly |length| bytes and will
   *                  not close the stream when reading is complete
   *        * done:   If you use the stream directly (instead of |copyTo|
   *                  below), you must signal completion by resolving /
   *                  rejecting this deferred.  If it's rejected, the transport
   *                  will be closed.  If an Error is supplied as a rejection
   *                  value, it will be logged via |dumpn|.  If you do use
   *                  |copyTo|, resolving is taken care of for you when copying
   *                  completes.
   *        * copyTo: A helper function for getting your data out of the stream
   *                  that meets the stream handling requirements above, and has
   *                  the following signature:
   *          @param  output nsIAsyncOutputStream
   *                  The stream to copy to.
   *          @return Promise
   *                  The promise is resolved when copying completes or rejected
   *                  if any (unexpected) errors occur.
   *                  This object also emits "progress" events for each chunk
   *                  that is copied.  See stream-utils.js.
   */
  onBulkPacket: function (packet) {
    let { actor } = packet;

    if (!actor) {
      DevToolsUtils.reportException(
        "onBulkPacket",
        new Error("Server did not specify an actor, dropping bulk packet: " +
                  JSON.stringify(packet)));
      return;
    }

    // See if we have a handler function waiting for a reply from this
    // actor.
    if (!this._activeRequests.has(actor)) {
      return;
    }

    let activeRequest = this._activeRequests.get(actor);
    this._activeRequests.delete(actor);

    // If there is a subsequent request for the same actor, hand it off to the
    // transport.  Delivery of packets on the other end is always async, even
    // in the local transport case.
    this._attemptNextRequest(actor);

    activeRequest.emit("bulk-reply", packet);
  },

  /**
   * Called by DebuggerTransport when the underlying stream is closed.
   *
   * @param status nsresult
   *        The status code that corresponds to the reason for closing
   *        the stream.
   */
  onClosed: function () {
    this._closed = true;
    this.emit("closed");

    this.purgeRequests();

    // The |_pools| array on the client-side currently is used only by
    // protocol.js to store active fronts, mirroring the actor pools found in
    // the server.  So, read all usages of "pool" as "protocol.js front".
    //
    // In the normal case where we shutdown cleanly, the toolbox tells each tool
    // to close, and they each call |destroy| on any fronts they were using.
    // When |destroy| or |cleanup| is called on a protocol.js front, it also
    // removes itself from the |_pools| array.  Once the toolbox has shutdown,
    // the connection is closed, and we reach here.  All fronts (should have
    // been) |destroy|ed, so |_pools| should empty.
    //
    // If the connection instead aborts unexpectedly, we may end up here with
    // all fronts used during the life of the connection.  So, we call |cleanup|
    // on them clear their state, reject pending requests, and remove themselves
    // from |_pools|.  This saves the toolbox from hanging indefinitely, in case
    // it waits for some server response before shutdown that will now never
    // arrive.
    for (let pool of this._pools) {
      pool.cleanup();
    }
  },

  /**
   * Purge pending and active requests in this client.
   *
   * @param prefix string (optional)
   *        If a prefix is given, only requests for actor IDs that start with the prefix
   *        will be cleaned up.  This is useful when forwarding of a portion of requests
   *        is cancelled on the server.
   */
  purgeRequests(prefix = "") {
    let reject = function (type, request) {
      // Server can send packets on its own and client only pass a callback
      // to expectReply, so that there is no request object.
      let msg;
      if (request.request) {
        msg = "'" + request.request.type + "' " + type + " request packet" +
              " to '" + request.actor + "' " +
              "can't be sent as the connection just closed.";
      } else {
        msg = "server side packet can't be received as the connection just closed.";
      }
      let packet = { error: "connectionClosed", message: msg };
      request.emit("json-reply", packet);
    };

    let pendingRequestsToReject = [];
    this._pendingRequests.forEach((requests, actor) => {
      if (!actor.startsWith(prefix)) {
        return;
      }
      this._pendingRequests.delete(actor);
      pendingRequestsToReject = pendingRequestsToReject.concat(requests);
    });
    pendingRequestsToReject.forEach(request => reject("pending", request));

    let activeRequestsToReject = [];
    this._activeRequests.forEach((request, actor) => {
      if (!actor.startsWith(prefix)) {
        return;
      }
      this._activeRequests.delete(actor);
      activeRequestsToReject = activeRequestsToReject.concat(request);
    });
    activeRequestsToReject.forEach(request => reject("active", request));
  },

  /**
   * Search for all requests in process for this client, including those made via
   * protocol.js and wait all of them to complete.  Since the requests seen when this is
   * first called may in turn trigger more requests, we keep recursing through this
   * function until there is no more activity.
   *
   * This is a fairly heavy weight process, so it's only meant to be used in tests.
   *
   * @return Promise
   *         Resolved when all requests have settled.
   */
  waitForRequestsToSettle() {
    let requests = [];

    // Gather all pending and active requests in this client
    // The request object supports a Promise API for completion (it has .then())
    this._pendingRequests.forEach(requestsForActor => {
      // Each value is an array of pending requests
      requests = requests.concat(requestsForActor);
    });
    this._activeRequests.forEach(requestForActor => {
      // Each value is a single active request
      requests = requests.concat(requestForActor);
    });

    // protocol.js
    // Use a Set because some fronts (like domwalker) seem to have multiple parents.
    let fronts = new Set();
    let poolsToVisit = [...this._pools];

    // With protocol.js, each front can potentially have it's own pools containing child
    // fronts, forming a tree.  Descend through all the pools to locate all child fronts.
    while (poolsToVisit.length) {
      let pool = poolsToVisit.shift();
      fronts.add(pool);
      for (let child of pool.poolChildren()) {
        poolsToVisit.push(child);
      }
    }

    // For each front, wait for its requests to settle
    for (let front of fronts) {
      if (front.hasRequests()) {
        requests.push(front.waitForRequestsToSettle());
      }
    }

    // Abort early if there are no requests
    if (!requests.length) {
      return Promise.resolve();
    }

    return DevToolsUtils.settleAll(requests).catch(() => {
      // One of the requests might have failed, but ignore that situation here and pipe
      // both success and failure through the same path.  The important part is just that
      // we waited.
    }).then(() => {
      // Repeat, more requests may have started in response to those we just waited for
      return this.waitForRequestsToSettle();
    });
  },

  registerClient: function (client) {
    let actorID = client.actor;
    if (!actorID) {
      throw new Error("DebuggerServer.registerClient expects " +
                      "a client instance with an `actor` attribute.");
    }
    if (!Array.isArray(client.events)) {
      throw new Error("DebuggerServer.registerClient expects " +
                      "a client instance with an `events` attribute " +
                      "that is an array.");
    }
    if (client.events.length > 0 && typeof (client.emit) != "function") {
      throw new Error("DebuggerServer.registerClient expects " +
                      "a client instance with non-empty `events` array to" +
                      "have an `emit` function.");
    }
    if (this._clients.has(actorID)) {
      throw new Error("DebuggerServer.registerClient already registered " +
                      "a client for this actor.");
    }
    this._clients.set(actorID, client);
  },

  unregisterClient: function (client) {
    let actorID = client.actor;
    if (!actorID) {
      throw new Error("DebuggerServer.unregisterClient expects " +
                      "a Client instance with a `actor` attribute.");
    }
    this._clients.delete(actorID);
  },

  /**
   * Actor lifetime management, echos the server's actor pools.
   */
  __pools: null,
  get _pools() {
    if (this.__pools) {
      return this.__pools;
    }
    this.__pools = new Set();
    return this.__pools;
  },

  addActorPool: function (pool) {
    this._pools.add(pool);
  },
  removeActorPool: function (pool) {
    this._pools.delete(pool);
  },
  getActor: function (actorID) {
    let pool = this.poolFor(actorID);
    return pool ? pool.get(actorID) : null;
  },

  poolFor: function (actorID) {
    for (let pool of this._pools) {
      if (pool.has(actorID)) {
        return pool;
      }
    }
    return null;
  },

  /**
   * Currently attached addon.
   */
  activeAddon: null,

  /**
   * Creates an object client for this DebuggerClient and the grip in parameter,
   * @param {Object} grip: The grip to create the ObjectClient for.
   * @returns {ObjectClient}
   */
  createObjectClient: function (grip) {
    return new ObjectClient(this, grip);
  }
};

eventSource(DebuggerClient.prototype);

class Request extends EventEmitter {
  constructor(request) {
    super();
    this.request = request;
  }

  get actor() {
    return this.request.to || this.request.actor;
  }
}

module.exports = {
  arg,
  DebuggerClient,
};
PK
!<d�o�rrDchrome/devtools/modules/devtools/shared/client/environment-client.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {arg, DebuggerClient} = require("devtools/shared/client/debugger-client");
const eventSource = require("devtools/shared/client/event-source");

/**
 * Environment clients are used to manipulate the lexical environment actors.
 *
 * @param client DebuggerClient
 *        The debugger client parent.
 * @param form Object
 *        The form sent across the remote debugging protocol.
 */
function EnvironmentClient(client, form) {
  this._client = client;
  this._form = form;
  this.request = this._client.request;
}
exports.EnvironmentClient = EnvironmentClient;

EnvironmentClient.prototype = {

  get actor() {
    return this._form.actor;
  },
  get _transport() {
    return this._client._transport;
  },

  /**
   * Fetches the bindings introduced by this lexical environment.
   */
  getBindings: DebuggerClient.requester({
    type: "bindings"
  }),

  /**
   * Changes the value of the identifier whose name is name (a string) to that
   * represented by value (a grip).
   */
  assign: DebuggerClient.requester({
    type: "assign",
    name: arg(0),
    value: arg(1)
  })
};

eventSource(EnvironmentClient.prototype);

module.exports = EnvironmentClient;
PK
!<��Bҳ�>chrome/devtools/modules/devtools/shared/client/event-source.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 DevToolsUtils = require("devtools/shared/DevToolsUtils");

/**
 * TODO: Get rid of this API in favor of EventTarget (bug 1042642)
 *
 * Add simple event notification to a prototype object. Any object that has
 * some use for event notifications or the observer pattern in general can be
 * augmented with the necessary facilities by passing its prototype to this
 * function.
 *
 * @param proto object
 *        The prototype object that will be modified.
 */
function eventSource(proto) {
  /**
   * Add a listener to the event source for a given event.
   *
   * @param name string
   *        The event to listen for.
   * @param listener function
   *        Called when the event is fired. If the same listener
   *        is added more than once, it will be called once per
   *        addListener call.
   */
  proto.addListener = function (name, listener) {
    if (typeof listener != "function") {
      throw TypeError("Listeners must be functions.");
    }

    if (!this._listeners) {
      this._listeners = {};
    }

    this._getListeners(name).push(listener);
  };

  /**
   * Add a listener to the event source for a given event. The
   * listener will be removed after it is called for the first time.
   *
   * @param name string
   *        The event to listen for.
   * @param listener function
   *        Called when the event is fired.
   * @returns Promise
   *          Resolved with an array of the arguments of the event.
   */
  proto.addOneTimeListener = function (name, listener) {
    return new Promise(resolve => {
      let l = (eventName, ...rest) => {
        this.removeListener(name, l);
        if (listener) {
          listener(eventName, ...rest);
        }
        resolve(rest[0]);
      };
      this.addListener(name, l);
    });
  };

  /**
   * Remove a listener from the event source previously added with
   * addListener().
   *
   * @param name string
   *        The event name used during addListener to add the listener.
   * @param listener function
   *        The callback to remove. If addListener was called multiple
   *        times, all instances will be removed.
   */
  proto.removeListener = function (name, listener) {
    if (!this._listeners || (listener && !this._listeners[name])) {
      return;
    }

    if (!listener) {
      this._listeners[name] = [];
    } else {
      this._listeners[name] =
        this._listeners[name].filter(l => l != listener);
    }
  };

  /**
   * Returns the listeners for the specified event name. If none are defined it
   * initializes an empty list and returns that.
   *
   * @param name string
   *        The event name.
   */
  proto._getListeners = function (name) {
    if (name in this._listeners) {
      return this._listeners[name];
    }
    this._listeners[name] = [];
    return this._listeners[name];
  };

  /**
   * Notify listeners of an event.
   *
   * @param name string
   *        The event to fire.
   * @param arguments
   *        All arguments will be passed along to the listeners,
   *        including the name argument.
   */
  proto.emit = function () {
    if (!this._listeners) {
      return;
    }

    let name = arguments[0];
    let listeners = this._getListeners(name).slice(0);

    for (let listener of listeners) {
      try {
        listener.apply(null, arguments);
      } catch (e) {
        // Prevent a bad listener from interfering with the others.
        DevToolsUtils.reportException("notify event '" + name + "'", e);
      }
    }
  };
}

module.exports = eventSource;
PK
!<YoooDchrome/devtools/modules/devtools/shared/client/long-string-client.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {arg, DebuggerClient} = require("devtools/shared/client/debugger-client");
/**
 * A LongStringClient provides a way to access "very long" strings from the
 * debugger server.
 *
 * @param client DebuggerClient
 *        The debugger client parent.
 * @param grip Object
 *        A pause-lifetime long string grip returned by the protocol.
 */
function LongStringClient(client, grip) {
  this._grip = grip;
  this._client = client;
  this.request = this._client.request;
}

LongStringClient.prototype = {
  get actor() {
    return this._grip.actor;
  },
  get length() {
    return this._grip.length;
  },
  get initial() {
    return this._grip.initial;
  },
  get _transport() {
    return this._client._transport;
  },

  valid: true,

  /**
   * Get the substring of this LongString from start to end.
   *
   * @param start Number
   *        The starting index.
   * @param end Number
   *        The ending index.
   * @param callback Function
   *        The function called when we receive the substring.
   */
  substring: DebuggerClient.requester({
    type: "substring",
    start: arg(0),
    end: arg(1)
  }),
};

module.exports = LongStringClient;
PK
!<�W=���?chrome/devtools/modules/devtools/shared/client/object-client.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {arg, DebuggerClient} = require("devtools/shared/client/debugger-client");
loader.lazyRequireGetter(this, "PropertyIteratorClient", "devtools/shared/client/property-iterator-client");
loader.lazyRequireGetter(this, "SymbolIteratorClient", "devtools/shared/client/symbol-iterator-client");

/**
 * Grip clients are used to retrieve information about the relevant object.
 *
 * @param client DebuggerClient
 *        The debugger client parent.
 * @param grip object
 *        A pause-lifetime object grip returned by the protocol.
 */
function ObjectClient(client, grip) {
  this._grip = grip;
  this._client = client;
  this.request = this._client.request;
}

ObjectClient.prototype = {
  get actor() {
    return this._grip.actor;
  },
  get _transport() {
    return this._client._transport;
  },

  valid: true,

  get isFrozen() {
    return this._grip.frozen;
  },
  get isSealed() {
    return this._grip.sealed;
  },
  get isExtensible() {
    return this._grip.extensible;
  },

  getDefinitionSite: DebuggerClient.requester({
    type: "definitionSite"
  }, {
    before: function (packet) {
      if (this._grip.class != "Function") {
        throw new Error("getDefinitionSite is only valid for function grips.");
      }
      return packet;
    }
  }),

  /**
   * Request the names of a function's formal parameters.
   *
   * @param onResponse function
   *        Called with an object of the form:
   *        { parameterNames:[<parameterName>, ...] }
   *        where each <parameterName> is the name of a parameter.
   */
  getParameterNames: DebuggerClient.requester({
    type: "parameterNames"
  }, {
    before: function (packet) {
      if (this._grip.class !== "Function") {
        throw new Error("getParameterNames is only valid for function grips.");
      }
      return packet;
    },
  }),

  /**
   * Request the names of the properties defined on the object and not its
   * prototype.
   *
   * @param onResponse function Called with the request's response.
   */
  getOwnPropertyNames: DebuggerClient.requester({
    type: "ownPropertyNames"
  }),

  /**
   * Request the prototype and own properties of the object.
   *
   * @param onResponse function Called with the request's response.
   */
  getPrototypeAndProperties: DebuggerClient.requester({
    type: "prototypeAndProperties"
  }),

  /**
   * Request a PropertyIteratorClient instance to ease listing
   * properties for this object.
   *
   * @param options Object
   *        A dictionary object with various boolean attributes:
   *        - ignoreIndexedProperties Boolean
   *          If true, filters out Array items.
   *          e.g. properties names between `0` and `object.length`.
   *        - ignoreNonIndexedProperties Boolean
   *          If true, filters out items that aren't array items
   *          e.g. properties names that are not a number between `0`
   *          and `object.length`.
   *        - sort Boolean
   *          If true, the iterator will sort the properties by name
   *          before dispatching them.
   * @param onResponse function Called with the client instance.
   */
  enumProperties: DebuggerClient.requester({
    type: "enumProperties",
    options: arg(0)
  }, {
    after: function (response) {
      if (response.iterator) {
        return { iterator: new PropertyIteratorClient(this._client, response.iterator) };
      }
      return response;
    },
  }),

  /**
   * Request a PropertyIteratorClient instance to enumerate entries in a
   * Map/Set-like object.
   *
   * @param onResponse function Called with the request's response.
   */
  enumEntries: DebuggerClient.requester({
    type: "enumEntries"
  }, {
    before: function (packet) {
      if (!["Map", "WeakMap", "Set", "WeakSet"].includes(this._grip.class)) {
        throw new Error("enumEntries is only valid for Map/Set-like grips.");
      }
      return packet;
    },
    after: function (response) {
      if (response.iterator) {
        return {
          iterator: new PropertyIteratorClient(this._client, response.iterator)
        };
      }
      return response;
    }
  }),

  /**
   * Request a SymbolIteratorClient instance to enumerate symbols in an object.
   *
   * @param onResponse function Called with the request's response.
   */
  enumSymbols: DebuggerClient.requester({
    type: "enumSymbols"
  }, {
    before: function (packet) {
      if (this._grip.type !== "object") {
        throw new Error("enumSymbols is only valid for objects grips.");
      }
      return packet;
    },
    after: function (response) {
      if (response.iterator) {
        return {
          iterator: new SymbolIteratorClient(this._client, response.iterator)
        };
      }
      return response;
    }
  }),

  /**
   * Request the property descriptor of the object's specified property.
   *
   * @param name string The name of the requested property.
   * @param onResponse function Called with the request's response.
   */
  getProperty: DebuggerClient.requester({
    type: "property",
    name: arg(0)
  }),

  /**
   * Request the prototype of the object.
   *
   * @param onResponse function Called with the request's response.
   */
  getPrototype: DebuggerClient.requester({
    type: "prototype"
  }),

  /**
   * Request the display string of the object.
   *
   * @param onResponse function Called with the request's response.
   */
  getDisplayString: DebuggerClient.requester({
    type: "displayString"
  }),

  /**
   * Request the scope of the object.
   *
   * @param onResponse function Called with the request's response.
   */
  getScope: DebuggerClient.requester({
    type: "scope"
  }, {
    before: function (packet) {
      if (this._grip.class !== "Function") {
        throw new Error("scope is only valid for function grips.");
      }
      return packet;
    },
  }),

  /**
   * Request the promises directly depending on the current promise.
   */
  getDependentPromises: DebuggerClient.requester({
    type: "dependentPromises"
  }, {
    before: function (packet) {
      if (this._grip.class !== "Promise") {
        throw new Error("getDependentPromises is only valid for promise " +
          "grips.");
      }
      return packet;
    }
  }),

  /**
   * Request the stack to the promise's allocation point.
   */
  getPromiseAllocationStack: DebuggerClient.requester({
    type: "allocationStack"
  }, {
    before: function (packet) {
      if (this._grip.class !== "Promise") {
        throw new Error("getAllocationStack is only valid for promise grips.");
      }
      return packet;
    }
  }),

  /**
   * Request the stack to the promise's fulfillment point.
   */
  getPromiseFulfillmentStack: DebuggerClient.requester({
    type: "fulfillmentStack"
  }, {
    before: function (packet) {
      if (this._grip.class !== "Promise") {
        throw new Error("getPromiseFulfillmentStack is only valid for " +
          "promise grips.");
      }
      return packet;
    }
  }),

  /**
   * Request the stack to the promise's rejection point.
   */
  getPromiseRejectionStack: DebuggerClient.requester({
    type: "rejectionStack"
  }, {
    before: function (packet) {
      if (this._grip.class !== "Promise") {
        throw new Error("getPromiseRejectionStack is only valid for " +
          "promise grips.");
      }
      return packet;
    }
  })
};

module.exports = ObjectClient;
PK
!<��Y���Jchrome/devtools/modules/devtools/shared/client/property-iterator-client.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {arg, DebuggerClient} = require("devtools/shared/client/debugger-client");

/**
 * A PropertyIteratorClient provides a way to access to property names and
 * values of an object efficiently, slice by slice.
 * Note that the properties can be sorted in the backend,
 * this is controled while creating the PropertyIteratorClient
 * from ObjectClient.enumProperties.
 *
 * @param client DebuggerClient
 *        The debugger client parent.
 * @param grip Object
 *        A PropertyIteratorActor grip returned by the protocol via
 *        TabActor.enumProperties request.
 */
function PropertyIteratorClient(client, grip) {
  this._grip = grip;
  this._client = client;
  this.request = this._client.request;
}

PropertyIteratorClient.prototype = {
  get actor() {
    return this._grip.actor;
  },

  /**
   * Get the total number of properties available in the iterator.
   */
  get count() {
    return this._grip.count;
  },

  /**
   * Get one or more property names that correspond to the positions in the
   * indexes parameter.
   *
   * @param indexes Array
   *        An array of property indexes.
   * @param callback Function
   *        The function called when we receive the property names.
   */
  names: DebuggerClient.requester({
    type: "names",
    indexes: arg(0)
  }, {}),

  /**
   * Get a set of following property value(s).
   *
   * @param start Number
   *        The index of the first property to fetch.
   * @param count Number
   *        The number of properties to fetch.
   * @param callback Function
   *        The function called when we receive the property values.
   */
  slice: DebuggerClient.requester({
    type: "slice",
    start: arg(0),
    count: arg(1)
  }, {}),

  /**
   * Get all the property values.
   *
   * @param callback Function
   *        The function called when we receive the property values.
   */
  all: DebuggerClient.requester({
    type: "all"
  }, {}),
};

module.exports = PropertyIteratorClient;
PK
!<�=chrome/devtools/modules/devtools/shared/client/root-client.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Ci } = require("chrome");
const { arg, DebuggerClient } = require("devtools/shared/client/debugger-client");

/**
 * A RootClient object represents a root actor on the server. Each
 * DebuggerClient keeps a RootClient instance representing the root actor
 * for the initial connection; DebuggerClient's 'listTabs' and
 * 'listChildProcesses' methods forward to that root actor.
 *
 * @param client object
 *      The client connection to which this actor belongs.
 * @param greeting string
 *      The greeting packet from the root actor we're to represent.
 *
 * Properties of a RootClient instance:
 *
 * @property actor string
 *      The name of this child's root actor.
 * @property applicationType string
 *      The application type, as given in the root actor's greeting packet.
 * @property traits object
 *      The traits object, as given in the root actor's greeting packet.
 */
function RootClient(client, greeting) {
  this._client = client;
  this.actor = greeting.from;
  this.applicationType = greeting.applicationType;
  this.traits = greeting.traits;
}
exports.RootClient = RootClient;

RootClient.prototype = {
  constructor: RootClient,

  /**
   * Gets the "root" form, which lists all the global actors that affect the entire
   * browser.  This can replace usages of `listTabs` that only wanted the global actors
   * and didn't actually care about tabs.
   */
  getRoot: DebuggerClient.requester({ type: "getRoot" }),

   /**
   * List the open tabs.
   *
   * @param object options
   *        Optional flags for listTabs:
   *        - boolean favicons: return favicon data
   * @param function onResponse
   *        Called with the response packet.
   */
  listTabs: DebuggerClient.requester({ type: "listTabs", options: arg(0) }),

  /**
   * List the installed addons.
   *
   * @param function onResponse
   *        Called with the response packet.
   */
  listAddons: DebuggerClient.requester({ type: "listAddons" }),

  /**
   * List the registered workers.
   *
   * @param function onResponse
   *        Called with the response packet.
   */
  listWorkers: DebuggerClient.requester({ type: "listWorkers" }),

  /**
   * List the registered service workers.
   *
   * @param function onResponse
   *        Called with the response packet.
   */
  listServiceWorkerRegistrations: DebuggerClient.requester({
    type: "listServiceWorkerRegistrations"
  }),

  /**
   * List the running processes.
   *
   * @param function onResponse
   *        Called with the response packet.
   */
  listProcesses: DebuggerClient.requester({ type: "listProcesses" }),

  /**
   * Fetch the TabActor for the currently selected tab, or for a specific
   * tab given as first parameter.
   *
   * @param [optional] object filter
   *        A dictionary object with following optional attributes:
   *         - outerWindowID: used to match tabs in parent process
   *         - tabId: used to match tabs in child processes
   *         - tab: a reference to xul:tab element
   *        If nothing is specified, returns the actor for the currently
   *        selected tab.
   */
  getTab: function (filter) {
    let packet = {
      to: this.actor,
      type: "getTab"
    };

    if (filter) {
      if (typeof (filter.outerWindowID) == "number") {
        packet.outerWindowID = filter.outerWindowID;
      } else if (typeof (filter.tabId) == "number") {
        packet.tabId = filter.tabId;
      } else if ("tab" in filter) {
        let browser = filter.tab.linkedBrowser;
        if (browser.frameLoader.tabParent) {
          // Tabs in child process
          packet.tabId = browser.frameLoader.tabParent.tabId;
        } else if (browser.outerWindowID) {
          // <xul:browser> tabs in parent process
          packet.outerWindowID = browser.outerWindowID;
        } else {
          // <iframe mozbrowser> tabs in parent process
          let windowUtils = browser.contentWindow
                                   .QueryInterface(Ci.nsIInterfaceRequestor)
                                   .getInterface(Ci.nsIDOMWindowUtils);
          packet.outerWindowID = windowUtils.outerWindowID;
        }
      } else {
        // Throw if a filter object have been passed but without
        // any clearly idenfified filter.
        throw new Error("Unsupported argument given to getTab request");
      }
    }

    return this.request(packet);
  },

  /**
   * Fetch the WindowActor for a specific window, like a browser window in
   * Firefox, but it can be used to reach any window in the process.
   *
   * @param number outerWindowID
   *        The outerWindowID of the top level window you are looking for.
   */
  getWindow: function ({ outerWindowID }) {
    if (!outerWindowID) {
      throw new Error("Must specify outerWindowID");
    }

    let packet = {
      to: this.actor,
      type: "getWindow",
      outerWindowID,
    };

    return this.request(packet);
  },

  /**
   * Description of protocol's actors and methods.
   *
   * @param function onResponse
   *        Called with the response packet.
   */
  protocolDescription: DebuggerClient.requester({ type: "protocolDescription" }),

  /*
   * Methods constructed by DebuggerClient.requester require these forwards
   * on their 'this'.
   */
  get _transport() {
    return this._client._transport;
  },
  get request() {
    return this._client.request;
  }
};

module.exports = RootClient;
PK
!<K*M�chrome/devtools/modules/devtools/shared/client/source-client.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {DebuggerClient} = require("devtools/shared/client/debugger-client");
loader.lazyRequireGetter(this, "BreakpointClient", "devtools/shared/client/breakpoint-client");

const noop = () => {};

/**
 * A SourceClient provides a way to access the source text of a script.
 *
 * @param client ThreadClient
 *        The thread client parent.
 * @param form Object
 *        The form sent across the remote debugging protocol.
 */
function SourceClient(client, form) {
  this._form = form;
  this._isBlackBoxed = form.isBlackBoxed;
  this._isPrettyPrinted = form.isPrettyPrinted;
  this._activeThread = client;
  this._client = client.client;
}

SourceClient.prototype = {
  get _transport() {
    return this._client._transport;
  },
  get isBlackBoxed() {
    return this._isBlackBoxed;
  },
  get isPrettyPrinted() {
    return this._isPrettyPrinted;
  },
  get actor() {
    return this._form.actor;
  },
  get request() {
    return this._client.request;
  },
  get url() {
    return this._form.url;
  },

  /**
   * Black box this SourceClient's source.
   *
   * @param callback Function
   *        The callback function called when we receive the response from the server.
   */
  blackBox: DebuggerClient.requester({
    type: "blackbox"
  }, {
    after: function (response) {
      if (!response.error) {
        this._isBlackBoxed = true;
        if (this._activeThread) {
          this._activeThread.emit("blackboxchange", this);
        }
      }
      return response;
    }
  }),

  /**
   * Un-black box this SourceClient's source.
   *
   * @param callback Function
   *        The callback function called when we receive the response from the server.
   */
  unblackBox: DebuggerClient.requester({
    type: "unblackbox"
  }, {
    after: function (response) {
      if (!response.error) {
        this._isBlackBoxed = false;
        if (this._activeThread) {
          this._activeThread.emit("blackboxchange", this);
        }
      }
      return response;
    }
  }),

  /**
   * Get Executable Lines from a source
   *
   * @param callback Function
   *        The callback function called when we receive the response from the server.
   */
  getExecutableLines: function (cb = noop) {
    let packet = {
      to: this._form.actor,
      type: "getExecutableLines"
    };

    return this._client.request(packet).then(res => {
      cb(res.lines);
      return res.lines;
    });
  },

  /**
   * Get a long string grip for this SourceClient's source.
   */
  source: function (callback = noop) {
    let packet = {
      to: this._form.actor,
      type: "source"
    };
    return this._client.request(packet).then(response => {
      return this._onSourceResponse(response, callback);
    });
  },

  /**
   * Pretty print this source's text.
   */
  prettyPrint: function (indent, callback = noop) {
    const packet = {
      to: this._form.actor,
      type: "prettyPrint",
      indent
    };
    return this._client.request(packet).then(response => {
      if (!response.error) {
        this._isPrettyPrinted = true;
        this._activeThread._clearFrames();
        this._activeThread.emit("prettyprintchange", this);
      }
      return this._onSourceResponse(response, callback);
    });
  },

  /**
   * Stop pretty printing this source's text.
   */
  disablePrettyPrint: function (callback = noop) {
    const packet = {
      to: this._form.actor,
      type: "disablePrettyPrint"
    };
    return this._client.request(packet).then(response => {
      if (!response.error) {
        this._isPrettyPrinted = false;
        this._activeThread._clearFrames();
        this._activeThread.emit("prettyprintchange", this);
      }
      return this._onSourceResponse(response, callback);
    });
  },

  _onSourceResponse: function (response, callback) {
    if (response.error) {
      callback(response);
      return response;
    }

    if (typeof response.source === "string") {
      callback(response);
      return response;
    }

    let { contentType, source } = response;
    if (source.type === "arrayBuffer") {
      let arrayBuffer = this._activeThread.threadArrayBuffer(source);
      return arrayBuffer.slice(0, arrayBuffer.length).then(function (resp) {
        if (resp.error) {
          callback(resp);
          return resp;
        }
        // Keeping str as a string, ArrayBuffer/Uint8Array will not survive
        // setIn/mergeIn operations.
        const str = atob(resp.encoded);
        let newResponse = {
          source: {
            binary: str,
            toString: () => "[wasm]",
          },
          contentType,
        };
        callback(newResponse);
        return newResponse;
      });
    }

    let longString = this._activeThread.threadLongString(source);
    return longString.substring(0, longString.length).then(function (resp) {
      if (resp.error) {
        callback(resp);
        return resp;
      }

      let newResponse = {
        source: resp.substring,
        contentType: contentType
      };
      callback(newResponse);
      return newResponse;
    });
  },

  /**
   * Request to set a breakpoint in the specified location.
   *
   * @param object location
   *        The location and condition of the breakpoint in
   *        the form of { line[, column, condition] }.
   * @param function onResponse
   *        Called with the thread's response.
   */
  setBreakpoint: function ({ line, column, condition, noSliding }, onResponse = noop) {
    // A helper function that sets the breakpoint.
    let doSetBreakpoint = callback => {
      let root = this._client.mainRoot;
      let location = {
        line,
        column,
      };

      let packet = {
        to: this.actor,
        type: "setBreakpoint",
        location,
        condition,
        noSliding,
      };

      // Backwards compatibility: send the breakpoint request to the
      // thread if the server doesn't support Debugger.Source actors.
      if (!root.traits.debuggerSourceActors) {
        packet.to = this._activeThread.actor;
        packet.location.url = this.url;
      }

      return this._client.request(packet).then(response => {
        // Ignoring errors, since the user may be setting a breakpoint in a
        // dead script that will reappear on a page reload.
        let bpClient;
        if (response.actor) {
          bpClient = new BreakpointClient(
            this._client,
            this,
            response.actor,
            location,
            root.traits.conditionalBreakpoints ? condition : undefined
          );
        }
        onResponse(response, bpClient);
        if (callback) {
          callback();
        }
        return [response, bpClient];
      });
    };

    // If the debuggee is paused, just set the breakpoint.
    if (this._activeThread.paused) {
      return doSetBreakpoint();
    }
    // Otherwise, force a pause in order to set the breakpoint.
    return this._activeThread.interrupt().then(response => {
      if (response.error) {
        // Can't set the breakpoint if pausing failed.
        onResponse(response);
        return response;
      }

      const { type, why } = response;
      const cleanUp = type == "paused" && why.type == "interrupted"
            ? () => this._activeThread.resume()
            : noop;

      return doSetBreakpoint(cleanUp);
    });
  }
};

module.exports = SourceClient;
PK
!<�u�8==Hchrome/devtools/modules/devtools/shared/client/symbol-iterator-client.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {arg, DebuggerClient} = require("devtools/shared/client/debugger-client");

/**
 * A SymbolIteratorClient provides a way to access to symbols
 * of an object efficiently, slice by slice.
 *
 * @param client DebuggerClient
 *        The debugger client parent.
 * @param grip Object
 *        A SymbolIteratorActor grip returned by the protocol via
 *        TabActor.enumSymbols request.
 */
function SymbolIteratorClient(client, grip) {
  this._grip = grip;
  this._client = client;
  this.request = this._client.request;
}

SymbolIteratorClient.prototype = {
  get actor() {
    return this._grip.actor;
  },

  /**
   * Get the total number of symbols available in the iterator.
   */
  get count() {
    return this._grip.count;
  },

  /**
   * Get a set of following symbols.
   *
   * @param start Number
   *        The index of the first symbol to fetch.
   * @param count Number
   *        The number of symbols to fetch.
   * @param callback Function
   *        The function called when we receive the symbols.
   */
  slice: DebuggerClient.requester({
    type: "slice",
    start: arg(0),
    count: arg(1)
  }, {}),

  /**
   * Get all the symbols.
   *
   * @param callback Function
   *        The function called when we receive the symbols.
   */
  all: DebuggerClient.requester({
    type: "all"
  }, {}),
};

module.exports = SymbolIteratorClient;
PK
!<�k�A��<chrome/devtools/modules/devtools/shared/client/tab-client.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 promise = require("devtools/shared/deprecated-sync-thenables");

const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const eventSource = require("devtools/shared/client/event-source");
const {arg, DebuggerClient} = require("devtools/shared/client/debugger-client");
loader.lazyRequireGetter(this, "ThreadClient", "devtools/shared/client/thread-client");

const noop = () => {};

/**
 * Creates a tab client for the remote debugging protocol server. This client
 * is a front to the tab actor created in the server side, hiding the protocol
 * details in a traditional JavaScript API.
 *
 * @param client DebuggerClient
 *        The debugger client parent.
 * @param form object
 *        The protocol form for this tab.
 */
function TabClient(client, form) {
  this.client = client;
  this._actor = form.from;
  this._threadActor = form.threadActor;
  this.javascriptEnabled = form.javascriptEnabled;
  this.cacheDisabled = form.cacheDisabled;
  this.thread = null;
  this.request = this.client.request;
  this.traits = form.traits || {};
  this.events = ["workerListChanged"];
}

TabClient.prototype = {
  get actor() {
    return this._actor;
  },
  get _transport() {
    return this.client._transport;
  },

  /**
   * Attach to a thread actor.
   *
   * @param object options
   *        Configuration options.
   *        - useSourceMaps: whether to use source maps or not.
   * @param function onResponse
   *        Called with the response packet and a ThreadClient
   *        (which will be undefined on error).
   */
  attachThread: function (options = {}, onResponse = noop) {
    if (this.thread) {
      DevToolsUtils.executeSoon(() => onResponse({}, this.thread));
      return promise.resolve([{}, this.thread]);
    }

    let packet = {
      to: this._threadActor,
      type: "attach",
      options,
    };
    return this.request(packet).then(response => {
      if (!response.error) {
        this.thread = new ThreadClient(this, this._threadActor);
        this.client.registerClient(this.thread);
      }
      onResponse(response, this.thread);
      return [response, this.thread];
    });
  },

  /**
   * Detach the client from the tab actor.
   *
   * @param function onResponse
   *        Called with the response packet.
   */
  detach: DebuggerClient.requester({
    type: "detach"
  }, {
    before: function (packet) {
      if (this.thread) {
        this.thread.detach();
      }
      return packet;
    },
    after: function (response) {
      this.client.unregisterClient(this);
      return response;
    },
  }),

  /**
   * Bring the window to the front.
   */
  focus: DebuggerClient.requester({
    type: "focus"
  }, {}),

  /**
   * Reload the page in this tab.
   *
   * @param [optional] object options
   *        An object with a `force` property indicating whether or not
   *        this reload should skip the cache
   */
  reload: function (options = { force: false }) {
    return this._reload(options);
  },
  _reload: DebuggerClient.requester({
    type: "reload",
    options: arg(0)
  }),

  /**
   * Navigate to another URL.
   *
   * @param string url
   *        The URL to navigate to.
   */
  navigateTo: DebuggerClient.requester({
    type: "navigateTo",
    url: arg(0)
  }),

  /**
   * Reconfigure the tab actor.
   *
   * @param object options
   *        A dictionary object of the new options to use in the tab actor.
   * @param function onResponse
   *        Called with the response packet.
   */
  reconfigure: DebuggerClient.requester({
    type: "reconfigure",
    options: arg(0)
  }),

  listWorkers: DebuggerClient.requester({
    type: "listWorkers"
  }),

  attachWorker: function (workerActor, onResponse) {
    return this.client.attachWorker(workerActor, onResponse);
  },
};

eventSource(TabClient.prototype);

module.exports = TabClient;
PK
!<���BJBJ?chrome/devtools/modules/devtools/shared/client/thread-client.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 promise = require("devtools/shared/deprecated-sync-thenables");

const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const {arg, DebuggerClient} = require("devtools/shared/client/debugger-client");
const eventSource = require("devtools/shared/client/event-source");
const {ThreadStateTypes} = require("devtools/shared/client/constants");

loader.lazyRequireGetter(this, "ArrayBufferClient", "devtools/shared/client/array-buffer-client");
loader.lazyRequireGetter(this, "EnvironmentClient", "devtools/shared/client/environment-client");
loader.lazyRequireGetter(this, "LongStringClient", "devtools/shared/client/long-string-client");
loader.lazyRequireGetter(this, "ObjectClient", "devtools/shared/client/object-client");
loader.lazyRequireGetter(this, "SourceClient", "devtools/shared/client/source-client");

const noop = () => {};

/**
 * Creates a thread client for the remote debugging protocol server. This client
 * is a front to the thread actor created in the server side, hiding the
 * protocol details in a traditional JavaScript API.
 *
 * @param client DebuggerClient|TabClient
 *        The parent of the thread (tab for tab-scoped debuggers, DebuggerClient
 *        for chrome debuggers).
 * @param actor string
 *        The actor ID for this thread.
 */
function ThreadClient(client, actor) {
  this._parent = client;
  this.client = client instanceof DebuggerClient ? client : client.client;
  this._actor = actor;
  this._frameCache = [];
  this._scriptCache = {};
  this._pauseGrips = {};
  this._threadGrips = {};
  this.request = this.client.request;
}

ThreadClient.prototype = {
  _state: "paused",
  get state() {
    return this._state;
  },
  get paused() {
    return this._state === "paused";
  },

  _pauseOnExceptions: false,
  _ignoreCaughtExceptions: false,
  _pauseOnDOMEvents: null,

  _actor: null,
  get actor() {
    return this._actor;
  },

  get _transport() {
    return this.client._transport;
  },

  _assertPaused: function (command) {
    if (!this.paused) {
      throw Error(command + " command sent while not paused. Currently " + this._state);
    }
  },

  /**
   * Resume a paused thread. If the optional limit parameter is present, then
   * the thread will also pause when that limit is reached.
   *
   * @param [optional] object limit
   *        An object with a type property set to the appropriate limit (next,
   *        step, or finish) per the remote debugging protocol specification.
   *        Use null to specify no limit.
   * @param function onResponse
   *        Called with the response packet.
   */
  _doResume: DebuggerClient.requester({
    type: "resume",
    resumeLimit: arg(0)
  }, {
    before: function (packet) {
      this._assertPaused("resume");

      // Put the client in a tentative "resuming" state so we can prevent
      // further requests that should only be sent in the paused state.
      this._previousState = this._state;
      this._state = "resuming";

      if (this._pauseOnExceptions) {
        packet.pauseOnExceptions = this._pauseOnExceptions;
      }
      if (this._ignoreCaughtExceptions) {
        packet.ignoreCaughtExceptions = this._ignoreCaughtExceptions;
      }
      if (this._pauseOnDOMEvents) {
        packet.pauseOnDOMEvents = this._pauseOnDOMEvents;
      }
      return packet;
    },
    after: function (response) {
      if (response.error && this._state == "resuming") {
        // There was an error resuming, update the state to the new one
        // reported by the server, if given (only on wrongState), otherwise
        // reset back to the previous state.
        if (response.state) {
          this._state = ThreadStateTypes[response.state];
        } else {
          this._state = this._previousState;
        }
      }
      delete this._previousState;
      return response;
    },
  }),

  /**
   * Reconfigure the thread actor.
   *
   * @param object options
   *        A dictionary object of the new options to use in the thread actor.
   * @param function onResponse
   *        Called with the response packet.
   */
  reconfigure: DebuggerClient.requester({
    type: "reconfigure",
    options: arg(0)
  }),

  /**
   * Resume a paused thread.
   */
  resume: function (onResponse) {
    return this._doResume(null, onResponse);
  },

  /**
   * Resume then pause without stepping.
   *
   * @param function onResponse
   *        Called with the response packet.
   */
  resumeThenPause: function (onResponse) {
    return this._doResume({ type: "break" }, onResponse);
  },

  /**
   * Step over a function call.
   *
   * @param function onResponse
   *        Called with the response packet.
   */
  stepOver: function (onResponse) {
    return this._doResume({ type: "next" }, onResponse);
  },

  /**
   * Step into a function call.
   *
   * @param function onResponse
   *        Called with the response packet.
   */
  stepIn: function (onResponse) {
    return this._doResume({ type: "step" }, onResponse);
  },

  /**
   * Step out of a function call.
   *
   * @param function onResponse
   *        Called with the response packet.
   */
  stepOut: function (onResponse) {
    return this._doResume({ type: "finish" }, onResponse);
  },

  /**
   * Immediately interrupt a running thread.
   *
   * @param function onResponse
   *        Called with the response packet.
   */
  interrupt: function (onResponse) {
    return this._doInterrupt(null, onResponse);
  },

  /**
   * Pause execution right before the next JavaScript bytecode is executed.
   *
   * @param function onResponse
   *        Called with the response packet.
   */
  breakOnNext: function (onResponse) {
    return this._doInterrupt("onNext", onResponse);
  },

  /**
   * Interrupt a running thread.
   *
   * @param function onResponse
   *        Called with the response packet.
   */
  _doInterrupt: DebuggerClient.requester({
    type: "interrupt",
    when: arg(0)
  }),

  /**
   * Enable or disable pausing when an exception is thrown.
   *
   * @param boolean pauseOnExceptions
   *        Enables pausing if true, disables otherwise.
   * @param boolean ignoreCaughtExceptions
   *        Whether to ignore caught exceptions
   * @param function onResponse
   *        Called with the response packet.
   */
  pauseOnExceptions: function (pauseOnExceptions,
                               ignoreCaughtExceptions,
                               onResponse = noop) {
    this._pauseOnExceptions = pauseOnExceptions;
    this._ignoreCaughtExceptions = ignoreCaughtExceptions;

    // Otherwise send the flag using a standard resume request.
    if (!this.paused) {
      return this.interrupt(response => {
        if (response.error) {
          // Can't continue if pausing failed.
          onResponse(response);
          return response;
        }
        return this.resume(onResponse);
      });
    }

    onResponse();
    return promise.resolve();
  },

  /**
   * Enable pausing when the specified DOM events are triggered. Disabling
   * pausing on an event can be realized by calling this method with the updated
   * array of events that doesn't contain it.
   *
   * @param array|string events
   *        An array of strings, representing the DOM event types to pause on,
   *        or "*" to pause on all DOM events. Pass an empty array to
   *        completely disable pausing on DOM events.
   * @param function onResponse
   *        Called with the response packet in a future turn of the event loop.
   */
  pauseOnDOMEvents: function (events, onResponse = noop) {
    this._pauseOnDOMEvents = events;
    // If the debuggee is paused, the value of the array will be communicated in
    // the next resumption. Otherwise we have to force a pause in order to send
    // the array.
    if (this.paused) {
      DevToolsUtils.executeSoon(() => onResponse({}));
      return {};
    }
    return this.interrupt(response => {
      // Can't continue if pausing failed.
      if (response.error) {
        onResponse(response);
        return response;
      }
      return this.resume(onResponse);
    });
  },

  /**
   * Send a clientEvaluate packet to the debuggee. Response
   * will be a resume packet.
   *
   * @param string frame
   *        The actor ID of the frame where the evaluation should take place.
   * @param string expression
   *        The expression that will be evaluated in the scope of the frame
   *        above.
   * @param function onResponse
   *        Called with the response packet.
   */
  eval: DebuggerClient.requester({
    type: "clientEvaluate",
    frame: arg(0),
    expression: arg(1)
  }, {
    before: function (packet) {
      this._assertPaused("eval");
      // Put the client in a tentative "resuming" state so we can prevent
      // further requests that should only be sent in the paused state.
      this._state = "resuming";
      return packet;
    },
    after: function (response) {
      if (response.error) {
        // There was an error resuming, back to paused state.
        this._state = "paused";
      }
      return response;
    },
  }),

  /**
   * Detach from the thread actor.
   *
   * @param function onResponse
   *        Called with the response packet.
   */
  detach: DebuggerClient.requester({
    type: "detach"
  }, {
    after: function (response) {
      this.client.unregisterClient(this);
      this._parent.thread = null;
      return response;
    },
  }),

  /**
   * Release multiple thread-lifetime object actors. If any pause-lifetime
   * actors are included in the request, a |notReleasable| error will return,
   * but all the thread-lifetime ones will have been released.
   *
   * @param array actors
   *        An array with actor IDs to release.
   */
  releaseMany: DebuggerClient.requester({
    type: "releaseMany",
    actors: arg(0),
  }),

  /**
   * Promote multiple pause-lifetime object actors to thread-lifetime ones.
   *
   * @param array actors
   *        An array with actor IDs to promote.
   */
  threadGrips: DebuggerClient.requester({
    type: "threadGrips",
    actors: arg(0)
  }),

  /**
   * Return the event listeners defined on the page.
   *
   * @param onResponse Function
   *        Called with the thread's response.
   */
  eventListeners: DebuggerClient.requester({
    type: "eventListeners"
  }),

  /**
   * Request the loaded sources for the current thread.
   *
   * @param onResponse Function
   *        Called with the thread's response.
   */
  getSources: DebuggerClient.requester({
    type: "sources"
  }),

  /**
   * Clear the thread's source script cache. A scriptscleared event
   * will be sent.
   */
  _clearScripts: function () {
    if (Object.keys(this._scriptCache).length > 0) {
      this._scriptCache = {};
      this.emit("scriptscleared");
    }
  },

  /**
   * Request frames from the callstack for the current thread.
   *
   * @param start integer
   *        The number of the youngest stack frame to return (the youngest
   *        frame is 0).
   * @param count integer
   *        The maximum number of frames to return, or null to return all
   *        frames.
   * @param onResponse function
   *        Called with the thread's response.
   */
  getFrames: DebuggerClient.requester({
    type: "frames",
    start: arg(0),
    count: arg(1)
  }),

  /**
   * An array of cached frames. Clients can observe the framesadded and
   * framescleared event to keep up to date on changes to this cache,
   * and can fill it using the fillFrames method.
   */
  get cachedFrames() {
    return this._frameCache;
  },

  /**
   * true if there are more stack frames available on the server.
   */
  get moreFrames() {
    return this.paused && (!this._frameCache || this._frameCache.length == 0
          || !this._frameCache[this._frameCache.length - 1].oldest);
  },

  /**
   * Request the frame environment.
   *
   * @param frameId string
   */
  getEnvironment: function (frameId) {
    return this.request({ to: frameId, type: "getEnvironment" });
  },

  /**
   * Ensure that at least total stack frames have been loaded in the
   * ThreadClient's stack frame cache. A framesadded event will be
   * sent when the stack frame cache is updated.
   *
   * @param total number
   *        The minimum number of stack frames to be included.
   * @param callback function
   *        Optional callback function called when frames have been loaded
   * @returns true if a framesadded notification should be expected.
   */
  fillFrames: function (total, callback = noop) {
    this._assertPaused("fillFrames");
    if (this._frameCache.length >= total) {
      return false;
    }

    let numFrames = this._frameCache.length;

    this.getFrames(numFrames, total - numFrames, (response) => {
      if (response.error) {
        callback(response);
        return;
      }

      let threadGrips = DevToolsUtils.values(this._threadGrips);

      for (let i in response.frames) {
        let frame = response.frames[i];
        if (!frame.where.source) {
          // Older servers use urls instead, so we need to resolve
          // them to source actors
          for (let grip of threadGrips) {
            if (grip instanceof SourceClient && grip.url === frame.url) {
              frame.where.source = grip._form;
            }
          }
        }

        this._frameCache[frame.depth] = frame;
      }

      // If we got as many frames as we asked for, there might be more
      // frames available.
      this.emit("framesadded");

      callback(response);
    });

    return true;
  },

  /**
   * Clear the thread's stack frame cache. A framescleared event
   * will be sent.
   */
  _clearFrames: function () {
    if (this._frameCache.length > 0) {
      this._frameCache = [];
      this.emit("framescleared");
    }
  },

  /**
   * Return a ObjectClient object for the given object grip.
   *
   * @param grip object
   *        A pause-lifetime object grip returned by the protocol.
   */
  pauseGrip: function (grip) {
    if (grip.actor in this._pauseGrips) {
      return this._pauseGrips[grip.actor];
    }

    let client = new ObjectClient(this.client, grip);
    this._pauseGrips[grip.actor] = client;
    return client;
  },

  /**
   * Get or create a long string client, checking the grip client cache if it
   * already exists.
   *
   * @param grip Object
   *        The long string grip returned by the protocol.
   * @param gripCacheName String
   *        The property name of the grip client cache to check for existing
   *        clients in.
   */
  _longString: function (grip, gripCacheName) {
    if (grip.actor in this[gripCacheName]) {
      return this[gripCacheName][grip.actor];
    }

    let client = new LongStringClient(this.client, grip);
    this[gripCacheName][grip.actor] = client;
    return client;
  },

  /**
   * Return an instance of LongStringClient for the given long string grip that
   * is scoped to the current pause.
   *
   * @param grip Object
   *        The long string grip returned by the protocol.
   */
  pauseLongString: function (grip) {
    return this._longString(grip, "_pauseGrips");
  },

  /**
   * Return an instance of LongStringClient for the given long string grip that
   * is scoped to the thread lifetime.
   *
   * @param grip Object
   *        The long string grip returned by the protocol.
   */
  threadLongString: function (grip) {
    return this._longString(grip, "_threadGrips");
  },

  /**
   * Get or create an ArrayBuffer client, checking the grip client cache if it
   * already exists.
   *
   * @param grip Object
   *        The ArrayBuffer grip returned by the protocol.
   * @param gripCacheName String
   *        The property name of the grip client cache to check for existing
   *        clients in.
   */
  _arrayBuffer: function (grip, gripCacheName) {
    if (grip.actor in this[gripCacheName]) {
      return this[gripCacheName][grip.actor];
    }

    let client = new ArrayBufferClient(this.client, grip);
    this[gripCacheName][grip.actor] = client;
    return client;
  },

  /**
   * Return an instance of ArrayBufferClient for the given ArrayBuffer grip that
   * is scoped to the thread lifetime.
   *
   * @param grip Object
   *        The ArrayBuffer grip returned by the protocol.
   */
  threadArrayBuffer: function (grip) {
    return this._arrayBuffer(grip, "_threadGrips");
  },

  /**
   * Clear and invalidate all the grip clients from the given cache.
   *
   * @param gripCacheName
   *        The property name of the grip cache we want to clear.
   */
  _clearObjectClients: function (gripCacheName) {
    for (let id in this[gripCacheName]) {
      this[gripCacheName][id].valid = false;
    }
    this[gripCacheName] = {};
  },

  /**
   * Invalidate pause-lifetime grip clients and clear the list of current grip
   * clients.
   */
  _clearPauseGrips: function () {
    this._clearObjectClients("_pauseGrips");
  },

  /**
   * Invalidate thread-lifetime grip clients and clear the list of current grip
   * clients.
   */
  _clearThreadGrips: function () {
    this._clearObjectClients("_threadGrips");
  },

  /**
   * Handle thread state change by doing necessary cleanup and notifying all
   * registered listeners.
   */
  _onThreadState: function (packet) {
    this._state = ThreadStateTypes[packet.type];
    // The debugger UI may not be initialized yet so we want to keep
    // the packet around so it knows what to pause state to display
    // when it's initialized
    this._lastPausePacket = packet.type === "resumed" ? null : packet;
    this._clearFrames();
    this._clearPauseGrips();
    packet.type === ThreadStateTypes.detached && this._clearThreadGrips();
    this.client._eventsEnabled && this.emit(packet.type, packet);
  },

  getLastPausePacket: function () {
    return this._lastPausePacket;
  },

  /**
   * Return an EnvironmentClient instance for the given environment actor form.
   */
  environment: function (form) {
    return new EnvironmentClient(this.client, form);
  },

  /**
   * Return an instance of SourceClient for the given source actor form.
   */
  source: function (form) {
    if (form.actor in this._threadGrips) {
      return this._threadGrips[form.actor];
    }

    this._threadGrips[form.actor] = new SourceClient(this, form);
    return this._threadGrips[form.actor];
  },

  /**
   * Request the prototype and own properties of mutlipleObjects.
   *
   * @param onResponse function
   *        Called with the request's response.
   * @param actors [string]
   *        List of actor ID of the queried objects.
   */
  getPrototypesAndProperties: DebuggerClient.requester({
    type: "prototypesAndProperties",
    actors: arg(0)
  }),

  events: ["newSource"]
};

eventSource(ThreadClient.prototype);

module.exports = ThreadClient;
PK
!<P��)
)
>chrome/devtools/modules/devtools/shared/client/trace-client.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {arg, DebuggerClient} = require("devtools/shared/client/debugger-client");

/**
 * Creates a tracing profiler client for the remote debugging protocol
 * server. This client is a front to the trace actor created on the
 * server side, hiding the protocol details in a traditional
 * JavaScript API.
 *
 * @param client DebuggerClient
 *        The debugger client parent.
 * @param actor string
 *        The actor ID for this thread.
 */
function TraceClient(client, actor) {
  this._client = client;
  this._actor = actor;
  this._activeTraces = new Set();
  this._waitingPackets = new Map();
  this._expectedPacket = 0;
  this.request = this._client.request;
  this.events = [];
}

TraceClient.prototype = {
  get actor() {
    return this._actor;
  },
  get tracing() {
    return this._activeTraces.size > 0;
  },

  get _transport() {
    return this._client._transport;
  },

  /**
   * Detach from the trace actor.
   */
  detach: DebuggerClient.requester({
    type: "detach"
  }, {
    after: function (response) {
      this._client.unregisterClient(this);
      return response;
    },
  }),

  /**
   * Start a new trace.
   *
   * @param trace [string]
   *        An array of trace types to be recorded by the new trace.
   *
   * @param name string
   *        The name of the new trace.
   *
   * @param onResponse function
   *        Called with the request's response.
   */
  startTrace: DebuggerClient.requester({
    type: "startTrace",
    name: arg(1),
    trace: arg(0)
  }, {
    after: function (response) {
      if (response.error) {
        return response;
      }

      if (!this.tracing) {
        this._waitingPackets.clear();
        this._expectedPacket = 0;
      }
      this._activeTraces.add(response.name);

      return response;
    },
  }),

  /**
   * End a trace. If a name is provided, stop the named
   * trace. Otherwise, stop the most recently started trace.
   *
   * @param name string
   *        The name of the trace to stop.
   *
   * @param onResponse function
   *        Called with the request's response.
   */
  stopTrace: DebuggerClient.requester({
    type: "stopTrace",
    name: arg(0)
  }, {
    after: function (response) {
      if (response.error) {
        return response;
      }

      this._activeTraces.delete(response.name);

      return response;
    },
  })
};

module.exports = TraceClient;
PK
!<�U��?chrome/devtools/modules/devtools/shared/client/worker-client.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {DebuggerClient} = require("devtools/shared/client/debugger-client");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const eventSource = require("devtools/shared/client/event-source");
loader.lazyRequireGetter(this, "ThreadClient", "devtools/shared/client/thread-client");

const noop = () => {};

function WorkerClient(client, form) {
  this.client = client;
  this._actor = form.from;
  this._isClosed = false;
  this._url = form.url;

  this._onClose = this._onClose.bind(this);

  this.addListener("close", this._onClose);

  this.traits = {};
}

WorkerClient.prototype = {
  get _transport() {
    return this.client._transport;
  },

  get request() {
    return this.client.request;
  },

  get actor() {
    return this._actor;
  },

  get url() {
    return this._url;
  },

  get isClosed() {
    return this._isClosed;
  },

  detach: DebuggerClient.requester({ type: "detach" }, {
    after: function (response) {
      if (this.thread) {
        this.client.unregisterClient(this.thread);
      }
      this.client.unregisterClient(this);
      return response;
    },
  }),

  attachThread: function (options = {}, onResponse = noop) {
    if (this.thread) {
      let response = [{
        type: "connected",
        threadActor: this.thread._actor,
        consoleActor: this.consoleActor,
      }, this.thread];
      DevToolsUtils.executeSoon(() => onResponse(response));
      return response;
    }

    // The connect call on server doesn't attach the thread as of version 44.
    return this.request({
      to: this._actor,
      type: "connect",
      options,
    }).then(connectResponse => {
      if (connectResponse.error) {
        onResponse(connectResponse, null);
        return [connectResponse, null];
      }

      return this.request({
        to: connectResponse.threadActor,
        type: "attach",
        options,
      }).then(attachResponse => {
        if (attachResponse.error) {
          onResponse(attachResponse, null);
        }

        this.thread = new ThreadClient(this, connectResponse.threadActor);
        this.consoleActor = connectResponse.consoleActor;
        this.client.registerClient(this.thread);

        onResponse(connectResponse, this.thread);
        return [connectResponse, this.thread];
      });
    }, error => {
      onResponse(error, null);
    });
  },

  _onClose: function () {
    this.removeListener("close", this._onClose);

    if (this.thread) {
      this.client.unregisterClient(this.thread);
    }
    this.client.unregisterClient(this);
    this._isClosed = true;
  },

  reconfigure: function () {
    return Promise.resolve();
  },

  events: ["close"]
};

eventSource(WorkerClient.prototype);

module.exports = WorkerClient;
PK
!<��Bi��;chrome/devtools/modules/devtools/shared/content-observer.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {Ci} = require("chrome");
const Services = require("Services");

const EventEmitter = require("devtools/shared/event-emitter");

/**
 * Handles adding an observer for the creation of content document globals,
 * event sent immediately after a web content document window has been set up,
 * but before any script code has been executed.
 */
function ContentObserver(tabActor) {
  this._contentWindow = tabActor.window;
  this._onContentGlobalCreated = this._onContentGlobalCreated.bind(this);
  this._onInnerWindowDestroyed = this._onInnerWindowDestroyed.bind(this);
  this.startListening();
}

module.exports.ContentObserver = ContentObserver;

ContentObserver.prototype = {
  /**
   * Starts listening for the required observer messages.
   */
  startListening: function () {
    Services.obs.addObserver(
      this._onContentGlobalCreated, "content-document-global-created");
    Services.obs.addObserver(
      this._onInnerWindowDestroyed, "inner-window-destroyed");
  },

  /**
   * Stops listening for the required observer messages.
   */
  stopListening: function () {
    Services.obs.removeObserver(
      this._onContentGlobalCreated, "content-document-global-created");
    Services.obs.removeObserver(
      this._onInnerWindowDestroyed, "inner-window-destroyed");
  },

  /**
   * Fired immediately after a web content document window has been set up.
   */
  _onContentGlobalCreated: function (subject, topic, data) {
    if (subject == this._contentWindow) {
      EventEmitter.emit(this, "global-created", subject);
    }
  },

  /**
   * Fired when an inner window is removed from the backward/forward cache.
   */
  _onInnerWindowDestroyed: function (subject, topic, data) {
    let id = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
    EventEmitter.emit(this, "global-destroyed", id);
  }
};

// Utility functions.

ContentObserver.GetInnerWindowID = function (window) {
  return window
    .QueryInterface(Ci.nsIInterfaceRequestor)
    .getInterface(Ci.nsIDOMWindowUtils)
    .currentInnerWindowID;
};
PK
!<����7chrome/devtools/modules/devtools/shared/css/color-db.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

// /!\  Auto-generated from nsColorNameList.h.
// This should be kept in sync with that list.
// test_cssColorDatabase.js tries to enforce this.

const cssColors = {
  aliceblue: [240, 248, 255, 1],
  antiquewhite: [250, 235, 215, 1],
  aqua: [0, 255, 255, 1],
  aquamarine: [127, 255, 212, 1],
  azure: [240, 255, 255, 1],
  beige: [245, 245, 220, 1],
  bisque: [255, 228, 196, 1],
  black: [0, 0, 0, 1],
  blanchedalmond: [255, 235, 205, 1],
  blue: [0, 0, 255, 1],
  blueviolet: [138, 43, 226, 1],
  brown: [165, 42, 42, 1],
  burlywood: [222, 184, 135, 1],
  cadetblue: [95, 158, 160, 1],
  chartreuse: [127, 255, 0, 1],
  chocolate: [210, 105, 30, 1],
  coral: [255, 127, 80, 1],
  cornflowerblue: [100, 149, 237, 1],
  cornsilk: [255, 248, 220, 1],
  crimson: [220, 20, 60, 1],
  cyan: [0, 255, 255, 1],
  darkblue: [0, 0, 139, 1],
  darkcyan: [0, 139, 139, 1],
  darkgoldenrod: [184, 134, 11, 1],
  darkgray: [169, 169, 169, 1],
  darkgreen: [0, 100, 0, 1],
  darkgrey: [169, 169, 169, 1],
  darkkhaki: [189, 183, 107, 1],
  darkmagenta: [139, 0, 139, 1],
  darkolivegreen: [85, 107, 47, 1],
  darkorange: [255, 140, 0, 1],
  darkorchid: [153, 50, 204, 1],
  darkred: [139, 0, 0, 1],
  darksalmon: [233, 150, 122, 1],
  darkseagreen: [143, 188, 143, 1],
  darkslateblue: [72, 61, 139, 1],
  darkslategray: [47, 79, 79, 1],
  darkslategrey: [47, 79, 79, 1],
  darkturquoise: [0, 206, 209, 1],
  darkviolet: [148, 0, 211, 1],
  deeppink: [255, 20, 147, 1],
  deepskyblue: [0, 191, 255, 1],
  dimgray: [105, 105, 105, 1],
  dimgrey: [105, 105, 105, 1],
  dodgerblue: [30, 144, 255, 1],
  firebrick: [178, 34, 34, 1],
  floralwhite: [255, 250, 240, 1],
  forestgreen: [34, 139, 34, 1],
  fuchsia: [255, 0, 255, 1],
  gainsboro: [220, 220, 220, 1],
  ghostwhite: [248, 248, 255, 1],
  gold: [255, 215, 0, 1],
  goldenrod: [218, 165, 32, 1],
  gray: [128, 128, 128, 1],
  grey: [128, 128, 128, 1],
  green: [0, 128, 0, 1],
  greenyellow: [173, 255, 47, 1],
  honeydew: [240, 255, 240, 1],
  hotpink: [255, 105, 180, 1],
  indianred: [205, 92, 92, 1],
  indigo: [75, 0, 130, 1],
  ivory: [255, 255, 240, 1],
  khaki: [240, 230, 140, 1],
  lavender: [230, 230, 250, 1],
  lavenderblush: [255, 240, 245, 1],
  lawngreen: [124, 252, 0, 1],
  lemonchiffon: [255, 250, 205, 1],
  lightblue: [173, 216, 230, 1],
  lightcoral: [240, 128, 128, 1],
  lightcyan: [224, 255, 255, 1],
  lightgoldenrodyellow: [250, 250, 210, 1],
  lightgray: [211, 211, 211, 1],
  lightgreen: [144, 238, 144, 1],
  lightgrey: [211, 211, 211, 1],
  lightpink: [255, 182, 193, 1],
  lightsalmon: [255, 160, 122, 1],
  lightseagreen: [32, 178, 170, 1],
  lightskyblue: [135, 206, 250, 1],
  lightslategray: [119, 136, 153, 1],
  lightslategrey: [119, 136, 153, 1],
  lightsteelblue: [176, 196, 222, 1],
  lightyellow: [255, 255, 224, 1],
  lime: [0, 255, 0, 1],
  limegreen: [50, 205, 50, 1],
  linen: [250, 240, 230, 1],
  magenta: [255, 0, 255, 1],
  maroon: [128, 0, 0, 1],
  mediumaquamarine: [102, 205, 170, 1],
  mediumblue: [0, 0, 205, 1],
  mediumorchid: [186, 85, 211, 1],
  mediumpurple: [147, 112, 219, 1],
  mediumseagreen: [60, 179, 113, 1],
  mediumslateblue: [123, 104, 238, 1],
  mediumspringgreen: [0, 250, 154, 1],
  mediumturquoise: [72, 209, 204, 1],
  mediumvioletred: [199, 21, 133, 1],
  midnightblue: [25, 25, 112, 1],
  mintcream: [245, 255, 250, 1],
  mistyrose: [255, 228, 225, 1],
  moccasin: [255, 228, 181, 1],
  navajowhite: [255, 222, 173, 1],
  navy: [0, 0, 128, 1],
  oldlace: [253, 245, 230, 1],
  olive: [128, 128, 0, 1],
  olivedrab: [107, 142, 35, 1],
  orange: [255, 165, 0, 1],
  orangered: [255, 69, 0, 1],
  orchid: [218, 112, 214, 1],
  palegoldenrod: [238, 232, 170, 1],
  palegreen: [152, 251, 152, 1],
  paleturquoise: [175, 238, 238, 1],
  palevioletred: [219, 112, 147, 1],
  papayawhip: [255, 239, 213, 1],
  peachpuff: [255, 218, 185, 1],
  peru: [205, 133, 63, 1],
  pink: [255, 192, 203, 1],
  plum: [221, 160, 221, 1],
  powderblue: [176, 224, 230, 1],
  purple: [128, 0, 128, 1],
  rebeccapurple: [102, 51, 153, 1],
  red: [255, 0, 0, 1],
  rosybrown: [188, 143, 143, 1],
  royalblue: [65, 105, 225, 1],
  saddlebrown: [139, 69, 19, 1],
  salmon: [250, 128, 114, 1],
  sandybrown: [244, 164, 96, 1],
  seagreen: [46, 139, 87, 1],
  seashell: [255, 245, 238, 1],
  sienna: [160, 82, 45, 1],
  silver: [192, 192, 192, 1],
  skyblue: [135, 206, 235, 1],
  slateblue: [106, 90, 205, 1],
  slategray: [112, 128, 144, 1],
  slategrey: [112, 128, 144, 1],
  snow: [255, 250, 250, 1],
  springgreen: [0, 255, 127, 1],
  steelblue: [70, 130, 180, 1],
  tan: [210, 180, 140, 1],
  teal: [0, 128, 128, 1],
  thistle: [216, 191, 216, 1],
  tomato: [255, 99, 71, 1],
  turquoise: [64, 224, 208, 1],
  violet: [238, 130, 238, 1],
  wheat: [245, 222, 179, 1],
  white: [255, 255, 255, 1],
  whitesmoke: [245, 245, 245, 1],
  yellow: [255, 255, 0, 1],
  yellowgreen: [154, 205, 50, 1],
};

exports.cssColors = cssColors;
PK
!<E�&كك4chrome/devtools/modules/devtools/shared/css/color.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Services = require("Services");

loader.lazyRequireGetter(this, "CSS_ANGLEUNIT",
  "devtools/shared/css/properties-db", true);

const {getAngleValueInDegrees} = require("devtools/shared/css/parsing-utils");

const {getCSSLexer} = require("devtools/shared/css/lexer");
const {cssColors} = require("devtools/shared/css/color-db");

const COLOR_UNIT_PREF = "devtools.defaultColorUnit";

const SPECIALVALUES = new Set([
  "currentcolor",
  "initial",
  "inherit",
  "transparent",
  "unset"
]);

/**
 * This module is used to convert between various color types.
 *
 * Usage:
 *   let {colorUtils} = require("devtools/shared/css/color");
 *   let color = new colorUtils.CssColor("red");
 *   // In order to support css-color-4 color function, pass true to the
 *   // second argument.
 *   // e.g.
 *   //   let color = new colorUtils.CssColor("red", true);
 *
 *   color.authored === "red"
 *   color.hasAlpha === false
 *   color.valid === true
 *   color.transparent === false // transparent has a special status.
 *   color.name === "red"        // returns hex when no name available.
 *   color.hex === "#f00"        // returns shortHex when available else returns
 *                                  longHex. If alpha channel is present then we
 *                                  return this.alphaHex if available,
 *                                  or this.longAlphaHex if not.
 *   color.alphaHex === "#f00f"  // returns short alpha hex when available
 *                                  else returns longAlphaHex.
 *   color.longHex === "#ff0000" // If alpha channel is present then we return
 *                                  this.longAlphaHex.
 *   color.longAlphaHex === "#ff0000ff"
 *   color.rgb === "rgb(255, 0, 0)" // If alpha channel is present
 *                                  // then we return this.rgba.
 *   color.rgba === "rgba(255, 0, 0, 1)"
 *   color.hsl === "hsl(0, 100%, 50%)"
 *   color.hsla === "hsla(0, 100%, 50%, 1)" // If alpha channel is present
 *                                             then we return this.rgba.
 *
 *   color.toString() === "#f00"; // Outputs the color type determined in the
 *                                   COLOR_UNIT_PREF constant (above).
 *   // Color objects can be reused
 *   color.newColor("green") === "#0f0"; // true
 *
 *   Valid values for COLOR_UNIT_PREF are contained in CssColor.COLORUNIT.
 */

function CssColor(colorValue, supportsCssColor4ColorFunction = false) {
  this.newColor(colorValue);
  this.cssColor4 = supportsCssColor4ColorFunction;
}

module.exports.colorUtils = {
  CssColor: CssColor,
  rgbToHsl: rgbToHsl,
  setAlpha: setAlpha,
  classifyColor: classifyColor,
  rgbToColorName: rgbToColorName,
  colorToRGBA: colorToRGBA,
  isValidCSSColor: isValidCSSColor,
  calculateContrastRatio: calculateContrastRatio,
};

/**
 * Values used in COLOR_UNIT_PREF
 */
CssColor.COLORUNIT = {
  "authored": "authored",
  "hex": "hex",
  "name": "name",
  "rgb": "rgb",
  "hsl": "hsl"
};

CssColor.prototype = {
  _colorUnit: null,
  _colorUnitUppercase: false,

  // The value as-authored.
  authored: null,
  // A lower-cased copy of |authored|.
  lowerCased: null,

  // Whether the value should be parsed using css-color-4 rules.
  cssColor4: false,

  _setColorUnitUppercase: function (color) {
    // Specifically exclude the case where the color is
    // case-insensitive.  This makes it so that "#000" isn't
    // considered "upper case" for the purposes of color cycling.
    this._colorUnitUppercase = (color === color.toUpperCase()) &&
      (color !== color.toLowerCase());
  },

  get colorUnit() {
    if (this._colorUnit === null) {
      let defaultUnit = Services.prefs.getCharPref(COLOR_UNIT_PREF);
      this._colorUnit = CssColor.COLORUNIT[defaultUnit];
      this._setColorUnitUppercase(this.authored);
    }
    return this._colorUnit;
  },

  set colorUnit(unit) {
    this._colorUnit = unit;
  },

  /**
   * If the current color unit pref is "authored", then set the
   * default color unit from the given color.  Otherwise, leave the
   * color unit untouched.
   *
   * @param {String} color The color to use
   */
  setAuthoredUnitFromColor: function (color) {
    if (Services.prefs.getCharPref(COLOR_UNIT_PREF) ===
        CssColor.COLORUNIT.authored) {
      this._colorUnit = classifyColor(color);
      this._setColorUnitUppercase(color);
    }
  },

  get hasAlpha() {
    if (!this.valid) {
      return false;
    }
    return this.getRGBATuple().a !== 1;
  },

  get valid() {
    return isValidCSSColor(this.authored, this.cssColor4);
  },

  /**
   * Return true for all transparent values e.g. rgba(0, 0, 0, 0).
   */
  get transparent() {
    try {
      let tuple = this.getRGBATuple();
      return !(tuple.r || tuple.g || tuple.b || tuple.a);
    } catch (e) {
      return false;
    }
  },

  get specialValue() {
    return SPECIALVALUES.has(this.lowerCased) ? this.authored : null;
  },

  get name() {
    let invalidOrSpecialValue = this._getInvalidOrSpecialValue();
    if (invalidOrSpecialValue !== false) {
      return invalidOrSpecialValue;
    }

    let tuple = this.getRGBATuple();

    if (tuple.a !== 1) {
      return this.hex;
    }
    let {r, g, b} = tuple;
    return rgbToColorName(r, g, b) || this.hex;
  },

  get hex() {
    let invalidOrSpecialValue = this._getInvalidOrSpecialValue();
    if (invalidOrSpecialValue !== false) {
      return invalidOrSpecialValue;
    }
    if (this.hasAlpha) {
      return this.alphaHex;
    }

    let hex = this.longHex;
    if (hex.charAt(1) == hex.charAt(2) &&
        hex.charAt(3) == hex.charAt(4) &&
        hex.charAt(5) == hex.charAt(6)) {
      hex = "#" + hex.charAt(1) + hex.charAt(3) + hex.charAt(5);
    }
    return hex;
  },

  get alphaHex() {
    let invalidOrSpecialValue = this._getInvalidOrSpecialValue();
    if (invalidOrSpecialValue !== false) {
      return invalidOrSpecialValue;
    }

    let alphaHex = this.longAlphaHex;
    if (alphaHex.charAt(1) == alphaHex.charAt(2) &&
        alphaHex.charAt(3) == alphaHex.charAt(4) &&
        alphaHex.charAt(5) == alphaHex.charAt(6) &&
        alphaHex.charAt(7) == alphaHex.charAt(8)) {
      alphaHex = "#" + alphaHex.charAt(1) + alphaHex.charAt(3) +
        alphaHex.charAt(5) + alphaHex.charAt(7);
    }
    return alphaHex;
  },

  get longHex() {
    let invalidOrSpecialValue = this._getInvalidOrSpecialValue();
    if (invalidOrSpecialValue !== false) {
      return invalidOrSpecialValue;
    }
    if (this.hasAlpha) {
      return this.longAlphaHex;
    }

    let tuple = this.getRGBATuple();
    return "#" + ((1 << 24) + (tuple.r << 16) + (tuple.g << 8) +
                  (tuple.b << 0)).toString(16).substr(-6);
  },

  get longAlphaHex() {
    let invalidOrSpecialValue = this._getInvalidOrSpecialValue();
    if (invalidOrSpecialValue !== false) {
      return invalidOrSpecialValue;
    }

    let tuple = this.getRGBATuple();
    return "#" + ((1 << 24) + (tuple.r << 16) + (tuple.g << 8) +
                  (tuple.b << 0)).toString(16).substr(-6) +
                  Math.round(tuple.a * 255).toString(16).padEnd(2, "0");
  },

  get rgb() {
    let invalidOrSpecialValue = this._getInvalidOrSpecialValue();
    if (invalidOrSpecialValue !== false) {
      return invalidOrSpecialValue;
    }
    if (!this.hasAlpha) {
      if (this.lowerCased.startsWith("rgb(")) {
        // The color is valid and begins with rgb(.
        return this.authored;
      }
      let tuple = this.getRGBATuple();
      return "rgb(" + tuple.r + ", " + tuple.g + ", " + tuple.b + ")";
    }
    return this.rgba;
  },

  get rgba() {
    let invalidOrSpecialValue = this._getInvalidOrSpecialValue();
    if (invalidOrSpecialValue !== false) {
      return invalidOrSpecialValue;
    }
    if (this.lowerCased.startsWith("rgba(")) {
      // The color is valid and begins with rgba(.
      return this.authored;
    }
    let components = this.getRGBATuple();
    return "rgba(" + components.r + ", " +
                     components.g + ", " +
                     components.b + ", " +
                     components.a + ")";
  },

  get hsl() {
    let invalidOrSpecialValue = this._getInvalidOrSpecialValue();
    if (invalidOrSpecialValue !== false) {
      return invalidOrSpecialValue;
    }
    if (this.lowerCased.startsWith("hsl(")) {
      // The color is valid and begins with hsl(.
      return this.authored;
    }
    if (this.hasAlpha) {
      return this.hsla;
    }
    return this._hsl();
  },

  get hsla() {
    let invalidOrSpecialValue = this._getInvalidOrSpecialValue();
    if (invalidOrSpecialValue !== false) {
      return invalidOrSpecialValue;
    }
    if (this.lowerCased.startsWith("hsla(")) {
      // The color is valid and begins with hsla(.
      return this.authored;
    }
    if (this.hasAlpha) {
      let a = this.getRGBATuple().a;
      return this._hsl(a);
    }
    return this._hsl(1);
  },

  /**
   * Check whether the current color value is in the special list e.g.
   * transparent or invalid.
   *
   * @return {String|Boolean}
   *         - If the current color is a special value e.g. "transparent" then
   *           return the color.
   *         - If the color is invalid return an empty string.
   *         - If the color is a regular color e.g. #F06 so we return false
   *           to indicate that the color is neither invalid or special.
   */
  _getInvalidOrSpecialValue: function () {
    if (this.specialValue) {
      return this.specialValue;
    }
    if (!this.valid) {
      return "";
    }
    return false;
  },

  /**
   * Change color
   *
   * @param  {String} color
   *         Any valid color string
   */
  newColor: function (color) {
    // Store a lower-cased version of the color to help with format
    // testing.  The original text is kept as well so it can be
    // returned when needed.
    this.lowerCased = color.toLowerCase();
    this.authored = color;
    this._setColorUnitUppercase(color);
    return this;
  },

  nextColorUnit: function () {
    // Reorder the formats array to have the current format at the
    // front so we can cycle through.
    let formats = ["hex", "hsl", "rgb", "name"];
    let currentFormat = classifyColor(this.toString());
    let putOnEnd = formats.splice(0, formats.indexOf(currentFormat));
    formats = formats.concat(putOnEnd);
    let currentDisplayedColor = this[formats[0]];

    for (let format of formats) {
      if (this[format].toLowerCase() !== currentDisplayedColor.toLowerCase()) {
        this.colorUnit = CssColor.COLORUNIT[format];
        break;
      }
    }

    return this.toString();
  },

  /**
   * Return a string representing a color of type defined in COLOR_UNIT_PREF.
   */
  toString: function () {
    let color;

    switch (this.colorUnit) {
      case CssColor.COLORUNIT.authored:
        color = this.authored;
        break;
      case CssColor.COLORUNIT.hex:
        color = this.hex;
        break;
      case CssColor.COLORUNIT.hsl:
        color = this.hsl;
        break;
      case CssColor.COLORUNIT.name:
        color = this.name;
        break;
      case CssColor.COLORUNIT.rgb:
        color = this.rgb;
        break;
      default:
        color = this.rgb;
    }

    if (this._colorUnitUppercase &&
        this.colorUnit != CssColor.COLORUNIT.authored) {
      color = color.toUpperCase();
    }

    return color;
  },

  /**
   * Returns a RGBA 4-Tuple representation of a color or transparent as
   * appropriate.
   */
  getRGBATuple: function () {
    let tuple = colorToRGBA(this.authored, this.cssColor4);

    tuple.a = parseFloat(tuple.a.toFixed(1));

    return tuple;
  },

  /**
   * Returns a HSLA 4-Tuple representation of a color or transparent as
   * appropriate.
   */
  _getHSLATuple: function () {
    let {r, g, b, a} = colorToRGBA(this.authored, this.cssColor4);

    let [h, s, l] = rgbToHsl([r, g, b]);

    return {
      h,
      s,
      l,
      a: parseFloat(a.toFixed(1))
    };
  },

  _hsl: function (maybeAlpha) {
    if (this.lowerCased.startsWith("hsl(") && maybeAlpha === undefined) {
      // We can use it as-is.
      return this.authored;
    }

    let {r, g, b} = this.getRGBATuple();
    let [h, s, l] = rgbToHsl([r, g, b]);
    if (maybeAlpha !== undefined) {
      return "hsla(" + h + ", " + s + "%, " + l + "%, " + maybeAlpha + ")";
    }
    return "hsl(" + h + ", " + s + "%, " + l + "%)";
  },

  /**
   * This method allows comparison of CssColor objects using ===.
   */
  valueOf: function () {
    return this.rgba;
  },

  /**
   * Check whether the color is fully transparent (alpha === 0).
   *
   * @return {Boolean} True if the color is transparent and valid.
   */
  isTransparent: function () {
    return this.getRGBATuple().a === 0;
  },
};

/**
 * Convert rgb value to hsl
 *
 * @param {array} rgb
 *         Array of rgb values
 * @return {array}
 *         Array of hsl values.
 */
function rgbToHsl([r, g, b]) {
  r = r / 255;
  g = g / 255;
  b = b / 255;

  let max = Math.max(r, g, b);
  let min = Math.min(r, g, b);
  let h;
  let s;
  let l = (max + min) / 2;

  if (max == min) {
    h = s = 0;
  } else {
    let d = max - min;
    s = l > 0.5 ? d / (2 - max - min) : d / (max + min);

    switch (max) {
      case r:
        h = ((g - b) / d) % 6;
        break;
      case g:
        h = (b - r) / d + 2;
        break;
      case b:
        h = (r - g) / d + 4;
        break;
    }
    h *= 60;
    if (h < 0) {
      h += 360;
    }
  }

  return [roundTo(h, 1), roundTo(s * 100, 1), roundTo(l * 100, 1)];
}

function roundTo(number, digits) {
  const multiplier = Math.pow(10, digits);
  return Math.round(number * multiplier) / multiplier;
}

/**
 * Takes a color value of any type (hex, hsl, hsla, rgb, rgba)
 * and an alpha value to generate an rgba string with the correct
 * alpha value.
 *
 * @param  {String} colorValue
 *         Color in the form of hex, hsl, hsla, rgb, rgba.
 * @param  {Number} alpha
 *         Alpha value for the color, between 0 and 1.
 * @param  {Boolean} useCssColor4ColorFunction
 *         use css-color-4 color function or not.
 * @return {String}
 *         Converted color with `alpha` value in rgba form.
 */
function setAlpha(colorValue, alpha, useCssColor4ColorFunction = false) {
  let color = new CssColor(colorValue, useCssColor4ColorFunction);

  // Throw if the color supplied is not valid.
  if (!color.valid) {
    throw new Error("Invalid color.");
  }

  // If an invalid alpha valid, just set to 1.
  if (!(alpha >= 0 && alpha <= 1)) {
    alpha = 1;
  }

  let { r, g, b } = color.getRGBATuple();
  return "rgba(" + r + ", " + g + ", " + b + ", " + alpha + ")";
}

/**
 * Given a color, classify its type as one of the possible color
 * units, as known by |CssColor.colorUnit|.
 *
 * @param  {String} value
 *         The color, in any form accepted by CSS.
 * @return {String}
 *         The color classification, one of "rgb", "hsl", "hex", or "name".
 */
function classifyColor(value) {
  value = value.toLowerCase();
  if (value.startsWith("rgb(") || value.startsWith("rgba(")) {
    return CssColor.COLORUNIT.rgb;
  } else if (value.startsWith("hsl(") || value.startsWith("hsla(")) {
    return CssColor.COLORUNIT.hsl;
  } else if (/^#[0-9a-f]+$/.exec(value)) {
    return CssColor.COLORUNIT.hex;
  }
  return CssColor.COLORUNIT.name;
}

// This holds a map from colors back to color names for use by
// rgbToColorName.
var cssRGBMap;

/**
 * Given a color, return its name, if it has one. Otherwise
 * returns an empty string.
 *
 * @param {Number} r, g, b  The color components.
 * @return {String} the name of the color or an empty string
 */
function rgbToColorName(r, g, b) {
  if (!cssRGBMap) {
    cssRGBMap = {};
    for (let name in cssColors) {
      let key = JSON.stringify(cssColors[name]);
      if (!(key in cssRGBMap)) {
        cssRGBMap[key] = name;
      }
    }
  }
  return cssRGBMap[JSON.stringify([r, g, b, 1])] || "";
}

// Translated from nsColor.cpp.
function _hslValue(m1, m2, h) {
  if (h < 0.0) {
    h += 1.0;
  }
  if (h > 1.0) {
    h -= 1.0;
  }
  if (h < 1.0 / 6.0) {
    return m1 + (m2 - m1) * h * 6.0;
  }
  if (h < 1.0 / 2.0) {
    return m2;
  }
  if (h < 2.0 / 3.0) {
    return m1 + (m2 - m1) * (2.0 / 3.0 - h) * 6.0;
  }
  return m1;
}

// Translated from nsColor.cpp.  All three values are expected to be
// in the range 0-1.
function hslToRGB([h, s, l]) {
  let r, g, b;
  let m1, m2;
  if (l <= 0.5) {
    m2 = l * (s + 1);
  } else {
    m2 = l + s - l * s;
  }
  m1 = l * 2 - m2;
  r = Math.round(255 * _hslValue(m1, m2, h + 1.0 / 3.0));
  g = Math.round(255 * _hslValue(m1, m2, h));
  b = Math.round(255 * _hslValue(m1, m2, h - 1.0 / 3.0));
  return [r, g, b];
}

/**
 * A helper function to convert a hex string like "F0C" or "F0C8" to a color.
 *
 * @param {String} name the color string
 * @return {Object} an object of the form {r, g, b, a}; or null if the
 *         name was not a valid color
 */
function hexToRGBA(name) {
  let r, g, b, a = 1;

  if (name.length === 3) {
    // short hex string (e.g. F0C)
    r = parseInt(name.charAt(0) + name.charAt(0), 16);
    g = parseInt(name.charAt(1) + name.charAt(1), 16);
    b = parseInt(name.charAt(2) + name.charAt(2), 16);
  } else if (name.length === 4) {
    // short alpha hex string (e.g. F0CA)
    r = parseInt(name.charAt(0) + name.charAt(0), 16);
    g = parseInt(name.charAt(1) + name.charAt(1), 16);
    b = parseInt(name.charAt(2) + name.charAt(2), 16);
    a = parseInt(name.charAt(3) + name.charAt(3), 16) / 255;
  } else if (name.length === 6) {
    // hex string (e.g. FD01CD)
    r = parseInt(name.charAt(0) + name.charAt(1), 16);
    g = parseInt(name.charAt(2) + name.charAt(3), 16);
    b = parseInt(name.charAt(4) + name.charAt(5), 16);
  } else if (name.length === 8) {
    // alpha hex string (e.g. FD01CDAB)
    r = parseInt(name.charAt(0) + name.charAt(1), 16);
    g = parseInt(name.charAt(2) + name.charAt(3), 16);
    b = parseInt(name.charAt(4) + name.charAt(5), 16);
    a = parseInt(name.charAt(6) + name.charAt(7), 16) / 255;
  } else {
    return null;
  }
  a = Math.round(a * 10) / 10;
  return {r, g, b, a};
}

/**
 * A helper function to clamp a value.
 *
 * @param {Number} value The value to clamp
 * @param {Number} min The minimum value
 * @param {Number} max The maximum value
 * @return {Number} A value between min and max
 */
function clamp(value, min, max) {
  if (value < min) {
    value = min;
  }
  if (value > max) {
    value = max;
  }
  return value;
}

/**
 * A helper function to get a token from a lexer, skipping comments
 * and whitespace.
 *
 * @param {CSSLexer} lexer The lexer
 * @return {CSSToken} The next non-whitespace, non-comment token; or
 * null at EOF.
 */
function getToken(lexer) {
  if (lexer._hasPushBackToken) {
    lexer._hasPushBackToken = false;
    return lexer._currentToken;
  }

  while (true) {
    let token = lexer.nextToken();
    if (!token || (token.tokenType !== "comment" &&
                   token.tokenType !== "whitespace")) {
      lexer._currentToken = token;
      return token;
    }
  }
}

/**
 * A helper function to put a token back to lexer for the next call of
 * getToken().
 *
 * @param {CSSLexer} lexer The lexer
 */
function unGetToken(lexer) {
  if (lexer._hasPushBackToken) {
    throw new Error("Double pushback.");
  }
  lexer._hasPushBackToken = true;
}

/**
 * A helper function that checks if the next token matches symbol.
 * If so, reads the token and returns true.  If not, pushes the
 * token back and returns false.
 *
 * @param {CSSLexer} lexer The lexer.
 * @param {String} symbol The symbol.
 * @return {Boolean} The expect symbol is parsed or not.
 */
function expectSymbol(lexer, symbol) {
  let token = getToken(lexer);
  if (!token) {
    return false;
  }

  if (token.tokenType !== "symbol" || token.text !== symbol) {
    unGetToken(lexer);
    return false;
  }

  return true;
}

const COLOR_COMPONENT_TYPE = {
  "integer": "integer",
  "number": "number",
  "percentage": "percentage",
};

/**
 * Parse a <integer> or a <number> or a <percentage> color component. If
 * |separator| is provided (not an empty string ""), this function will also
 * attempt to parse that character after parsing the color component. The range
 * of output component value is [0, 1] if the component type is percentage.
 * Otherwise, the range is [0, 255].
 *
 * @param {CSSLexer} lexer The lexer.
 * @param {COLOR_COMPONENT_TYPE} type The color component type.
 * @param {String} separator The separator.
 * @param {Array} colorArray [out] The parsed color component will push into this array.
 * @return {Boolean} Return false on error.
 */
function parseColorComponent(lexer, type, separator, colorArray) {
  let token = getToken(lexer);

  if (!token) {
    return false;
  }

  switch (type) {
    case COLOR_COMPONENT_TYPE.integer:
      if (token.tokenType !== "number" || !token.isInteger) {
        return false;
      }
      break;
    case COLOR_COMPONENT_TYPE.number:
      if (token.tokenType !== "number") {
        return false;
      }
      break;
    case COLOR_COMPONENT_TYPE.percentage:
      if (token.tokenType !== "percentage") {
        return false;
      }
      break;
    default:
      throw new Error("Invalid color component type.");
  }

  let colorComponent = 0;
  if (type === COLOR_COMPONENT_TYPE.percentage) {
    colorComponent = clamp(token.number, 0, 1);
  } else {
    colorComponent = clamp(token.number, 0, 255);
  }

  if (separator !== "" && !expectSymbol(lexer, separator)) {
    return false;
  }

  colorArray.push(colorComponent);

  return true;
}

/**
 * Parse an optional [ separator <alpha-value> ] expression, followed by a
 * close-parenthesis, at the end of a css color function (e.g. rgba() or hsla()).
 * If this function simply encounters a close-parenthesis (without the
 * [ separator <alpha-value> ]), it will still succeed. Then put a fully-opaque
 * alpha value into the colorArray. The range of output alpha value is [0, 1].
 *
 * @param {CSSLexer} lexer The lexer
 * @param {String} separator The separator.
 * @param {Array} colorArray [out] The parsed color component will push into this array.
 * @return {Boolean} Return false on error.
 */
function parseColorOpacityAndCloseParen(lexer, separator, colorArray) {
  // The optional [separator <alpha-value>] was omitted, so set the opacity
  // to a fully-opaque value '1.0' and return success.
  if (expectSymbol(lexer, ")")) {
    colorArray.push(1);
    return true;
  }

  if (!expectSymbol(lexer, separator)) {
    return false;
  }

  let token = getToken(lexer);
  if (!token) {
    return false;
  }

  // <number> or <percentage>
  if (token.tokenType !== "number" && token.tokenType !== "percentage") {
    return false;
  }

  if (!expectSymbol(lexer, ")")) {
    return false;
  }

  colorArray.push(clamp(token.number, 0, 1));

  return true;
}

/**
 * Parse a hue value.
 *   <hue> = <number> | <angle>
 *
 * @param {CSSLexer} lexer The lexer
 * @param {Array} colorArray [out] The parsed color component will push into this array.
 * @return {Boolean} Return false on error.
 */
function parseHue(lexer, colorArray) {
  let token = getToken(lexer);

  if (!token) {
    return false;
  }

  let val = 0;
  if (token.tokenType === "number") {
    val = token.number;
  } else if (token.tokenType === "dimension" && token.text in CSS_ANGLEUNIT) {
    val = getAngleValueInDegrees(token.number, token.text);
  } else {
    return false;
  }

  val = val / 360.0;
  colorArray.push(val - Math.floor(val));

  return true;
}

/**
 * A helper function to parse the color components of hsl()/hsla() function.
 * hsl() and hsla() are now aliases.
 *
 * @param {CSSLexer} lexer The lexer
 * @return {Array} An array of the form [r,g,b,a]; or null on error.
 */
function parseHsl(lexer) {
  // comma-less expression:
  // hsl() = hsl( <hue> <saturation> <lightness> [ / <alpha-value> ]? )
  // the expression with comma:
  // hsl() = hsl( <hue>, <saturation>, <lightness>, <alpha-value>? )
  //
  // <hue> = <number> | <angle>
  // <alpha-value> = <number> | <percentage>

  const commaSeparator = ",";
  let hsl = [];
  let a = [];

  // Parse hue.
  if (!parseHue(lexer, hsl)) {
    return null;
  }

  // Look for a comma separator after "hue" component to determine if the
  // expression is comma-less or not.
  let hasComma = expectSymbol(lexer, commaSeparator);

  // Parse saturation, lightness and opacity.
  // The saturation and lightness are <percentage>, so reuse the <percentage>
  // version of parseColorComponent function for them. No need to check the
  // separator after 'lightness'. It will be checked in opacity value parsing.
  let separatorBeforeAlpha = hasComma ? commaSeparator : "/";
  if (parseColorComponent(lexer, COLOR_COMPONENT_TYPE.percentage,
                          hasComma ? commaSeparator : "", hsl) &&
      parseColorComponent(lexer, COLOR_COMPONENT_TYPE.percentage, "", hsl) &&
      parseColorOpacityAndCloseParen(lexer, separatorBeforeAlpha, a)) {
    return [...hslToRGB(hsl), ...a];
  }

  return null;
}

/**
 * A helper function to parse the color arguments of old style hsl()/hsla()
 * function.
 *
 * @param {CSSLexer} lexer The lexer.
 * @param {Boolean} hasAlpha The color function has alpha component or not.
 * @return {Array} An array of the form [r,g,b,a]; or null on error.
 */
function parseOldStyleHsl(lexer, hasAlpha) {
  // hsla() = hsla( <hue>, <saturation>, <lightness>, <alpha-value> )
  // hsl() = hsl( <hue>, <saturation>, <lightness> )
  //
  // <hue> = <number>
  // <alpha-value> = <number>

  const commaSeparator = ",";
  const closeParen = ")";
  let hsl = [];
  let a = [];

  // Parse hue.
  let token = getToken(lexer);
  if (!token || token.tokenType !== "number") {
    return null;
  }
  if (!expectSymbol(lexer, commaSeparator)) {
    return null;
  }
  let val = token.number / 360.0;
  hsl.push(val - Math.floor(val));

  // Parse saturation, lightness and opacity.
  // The saturation and lightness are <percentage>, so reuse the <percentage>
  // version of parseColorComponent function for them. The opacity is <number>
  if (hasAlpha) {
    if (parseColorComponent(lexer, COLOR_COMPONENT_TYPE.percentage,
                            commaSeparator, hsl) &&
        parseColorComponent(lexer, COLOR_COMPONENT_TYPE.percentage,
                            commaSeparator, hsl) &&
        parseColorComponent(lexer, COLOR_COMPONENT_TYPE.number,
                            closeParen, a)) {
      return [...hslToRGB(hsl), ...a];
    }
  } else if (parseColorComponent(lexer, COLOR_COMPONENT_TYPE.percentage,
                                 commaSeparator, hsl) &&
             parseColorComponent(lexer, COLOR_COMPONENT_TYPE.percentage,
                                 closeParen, hsl)) {
    return [...hslToRGB(hsl), 1];
  }

  return null;
}

/**
 * A helper function to parse the color arguments of rgb()/rgba() function.
 * rgb() and rgba() now are aliases.
 *
 * @param {CSSLexer} lexer The lexer.
 * @return {Array} An array of the form [r,g,b,a]; or null on error.
 */
function parseRgb(lexer) {
  // comma-less expression:
  //   rgb() = rgb( component{3} [ / <alpha-value> ]? )
  // the expression with comma:
  //   rgb() = rgb( component#{3} , <alpha-value>? )
  //
  // component = <number> | <percentage>
  // <alpa-value> = <number> | <percentage>

  const commaSeparator = ",";
  let rgba = [];

  let token = getToken(lexer);
  if (token.tokenType !== "percentage" && token.tokenType !== "number") {
    return null;
  }
  unGetToken(lexer);
  let type = (token.tokenType === "percentage") ?
             COLOR_COMPONENT_TYPE.percentage :
             COLOR_COMPONENT_TYPE.number;

  // Parse R.
  if (!parseColorComponent(lexer, type, "", rgba)) {
    return null;
  }
  let hasComma = expectSymbol(lexer, commaSeparator);

  // Parse G, B and A.
  // No need to check the separator after 'B'. It will be checked in 'A' values
  // parsing.
  let separatorBeforeAlpha = hasComma ? commaSeparator : "/";
  if (parseColorComponent(lexer, type, hasComma ? commaSeparator : "", rgba) &&
      parseColorComponent(lexer, type, "", rgba) &&
      parseColorOpacityAndCloseParen(lexer, separatorBeforeAlpha, rgba)) {
    if (type === COLOR_COMPONENT_TYPE.percentage) {
      rgba[0] = Math.round(255 * rgba[0]);
      rgba[1] = Math.round(255 * rgba[1]);
      rgba[2] = Math.round(255 * rgba[2]);
    }
    return rgba;
  }

  return null;
}

/**
 * A helper function to parse the color arguments of old style rgb()/rgba()
 * function.
 *
 * @param {CSSLexer} lexer The lexer.
 * @param {Boolean} hasAlpha The color function has alpha component or not.
 * @return {Array} An array of the form [r,g,b,a]; or null on error.
 */
function parseOldStyleRgb(lexer, hasAlpha) {
  // rgba() = rgba( component#{3} , <alpha-value> )
  // rgb() = rgb( component#{3} )
  //
  // component = <integer> | <percentage>
  // <alpha-value> = <number>

  const commaSeparator = ",";
  const closeParen = ")";
  let rgba = [];

  let token = getToken(lexer);
  if (token.tokenType !== "percentage" &&
      (token.tokenType !== "number" || !token.isInteger)) {
    return null;
  }
  unGetToken(lexer);
  let type = (token.tokenType === "percentage") ?
             COLOR_COMPONENT_TYPE.percentage :
             COLOR_COMPONENT_TYPE.integer;

  // Parse R. G, B and A.
  if (hasAlpha) {
    if (!parseColorComponent(lexer, type, commaSeparator, rgba) ||
        !parseColorComponent(lexer, type, commaSeparator, rgba) ||
        !parseColorComponent(lexer, type, commaSeparator, rgba) ||
        !parseColorComponent(lexer, COLOR_COMPONENT_TYPE.number,
                             closeParen, rgba)) {
      return null;
    }
  } else if (!parseColorComponent(lexer, type, commaSeparator, rgba) ||
             !parseColorComponent(lexer, type, commaSeparator, rgba) ||
             !parseColorComponent(lexer, type, closeParen, rgba)) {
    return null;
  }

  if (type === COLOR_COMPONENT_TYPE.percentage) {
    rgba[0] = Math.round(255 * rgba[0]);
    rgba[1] = Math.round(255 * rgba[1]);
    rgba[2] = Math.round(255 * rgba[2]);
  }
  if (!hasAlpha) {
    rgba.push(1);
  }

  return rgba;
}

/**
 * Convert a string representing a color to an object holding the
 * color's components.  Any valid CSS color form can be passed in.
 *
 * @param {String} name the color
 * @param {Boolean} useCssColor4ColorFunction use css-color-4 color function or not.
 * @return {Object} an object of the form {r, g, b, a}; or null if the
 *         name was not a valid color
 */
function colorToRGBA(name, useCssColor4ColorFunction = false) {
  name = name.trim().toLowerCase();

  if (name in cssColors) {
    let result = cssColors[name];
    return {r: result[0], g: result[1], b: result[2], a: result[3]};
  } else if (name === "transparent") {
    return {r: 0, g: 0, b: 0, a: 0};
  } else if (name === "currentcolor") {
    return {r: 0, g: 0, b: 0, a: 1};
  }

  let lexer = getCSSLexer(name);

  let func = getToken(lexer);
  if (!func) {
    return null;
  }

  if (func.tokenType === "id" || func.tokenType === "hash") {
    if (getToken(lexer) !== null) {
      return null;
    }
    return hexToRGBA(func.text);
  }

  const expectedFunctions = ["rgba", "rgb", "hsla", "hsl"];
  if (!func || func.tokenType !== "function" ||
      !expectedFunctions.includes(func.text)) {
    return null;
  }

  let hsl = func.text === "hsl" || func.text === "hsla";

  let vals;
  if (!useCssColor4ColorFunction) {
    let hasAlpha = (func.text === "rgba" || func.text === "hsla");
    vals = hsl ? parseOldStyleHsl(lexer, hasAlpha) : parseOldStyleRgb(lexer, hasAlpha);
  } else {
    vals = hsl ? parseHsl(lexer) : parseRgb(lexer);
  }

  if (!vals) {
    return null;
  }
  if (getToken(lexer) !== null) {
    return null;
  }

  return {r: vals[0], g: vals[1], b: vals[2], a: vals[3]};
}

/**
 * Check whether a string names a valid CSS color.
 *
 * @param {String} name The string to check
 * @param {Boolean} useCssColor4ColorFunction use css-color-4 color function or not.
 * @return {Boolean} True if the string is a CSS color name.
 */
function isValidCSSColor(name, useCssColor4ColorFunction = false) {
  return colorToRGBA(name, useCssColor4ColorFunction) !== null;
}

/**
 * Calculates the luminance of a rgba tuple based on the formula given in
 * https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
 *
 * @param {Array} rgba An array with [r,g,b,a] values.
 * @return {Number} The calculated luminance.
 */
function calculateLuminance(rgba) {
  for (let i = 0; i < 3; i++) {
    rgba[i] /= 255;
    rgba[i] = (rgba[i] < 0.03928) ? (rgba[i] / 12.92) :
                                    Math.pow(((rgba[i] + 0.055) / 1.055), 2.4);
  }
  return 0.2126 * rgba[0] + 0.7152 * rgba[1] + 0.0722 * rgba[2];
}

/**
 * Calculates the contrast ratio of 2 rgba tuples based on the formula in
 * https://www.w3.org/TR/2008/REC-WCAG20-20081211/#visual-audio-contrast7
 *
 * @param {Array} backgroundColor An array with [r,g,b,a] values containing
 * the background color.
 * @param {Array} textColor An array with [r,g,b,a] values containing
 * the text color.
 * @return {Number} The calculated luminance.
 */
function calculateContrastRatio(backgroundColor, textColor) {
  let backgroundLuminance = calculateLuminance(backgroundColor);
  let textLuminance = calculateLuminance(textColor);
  let ratio = (textLuminance + 0.05) / (backgroundLuminance + 0.05);

  return (ratio > 1.0) ? ratio : (1 / ratio);
}
PK
!< ��L�L�Fchrome/devtools/modules/devtools/shared/css/generated/properties-db.js/* THIS IS AN AUTOGENERATED FILE.  DO NOT EDIT */

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 automatically generated by `mach devtools-css-db`. It contains
 * a static list of CSS properties that can be computed by Gecko. The actual script
 * to generate these files can be found at devtools/shared/css/generate-properties-db.js.
 */

/**
 * A list of CSS Properties and their various characteristics.
 */
exports.CSS_PROPERTIES = {
  "-moz-animation": {
    "isInherited": false,
    "subproperties": [
      "animation-duration",
      "animation-timing-function",
      "animation-delay",
      "animation-direction",
      "animation-fill-mode",
      "animation-iteration-count",
      "animation-play-state",
      "animation-name"
    ],
    "supports": [
      7,
      9,
      10
    ],
    "values": [
      "alternate",
      "alternate-reverse",
      "backwards",
      "both",
      "cubic-bezier",
      "ease",
      "ease-in",
      "ease-in-out",
      "ease-out",
      "forwards",
      "infinite",
      "inherit",
      "initial",
      "linear",
      "none",
      "normal",
      "paused",
      "reverse",
      "running",
      "step-end",
      "step-start",
      "steps",
      "unset"
    ]
  },
  "-moz-animation-delay": {
    "isInherited": false,
    "subproperties": [
      "animation-delay"
    ],
    "supports": [
      9
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-moz-animation-direction": {
    "isInherited": false,
    "subproperties": [
      "animation-direction"
    ],
    "supports": [],
    "values": [
      "alternate",
      "alternate-reverse",
      "inherit",
      "initial",
      "normal",
      "reverse",
      "unset"
    ]
  },
  "-moz-animation-duration": {
    "isInherited": false,
    "subproperties": [
      "animation-duration"
    ],
    "supports": [
      9
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-moz-animation-fill-mode": {
    "isInherited": false,
    "subproperties": [
      "animation-fill-mode"
    ],
    "supports": [],
    "values": [
      "backwards",
      "both",
      "forwards",
      "inherit",
      "initial",
      "none",
      "unset"
    ]
  },
  "-moz-animation-iteration-count": {
    "isInherited": false,
    "subproperties": [
      "animation-iteration-count"
    ],
    "supports": [
      7
    ],
    "values": [
      "infinite",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-moz-animation-name": {
    "isInherited": false,
    "subproperties": [
      "animation-name"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "none",
      "unset"
    ]
  },
  "-moz-animation-play-state": {
    "isInherited": false,
    "subproperties": [
      "animation-play-state"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "paused",
      "running",
      "unset"
    ]
  },
  "-moz-animation-timing-function": {
    "isInherited": false,
    "subproperties": [
      "animation-timing-function"
    ],
    "supports": [
      10
    ],
    "values": [
      "cubic-bezier",
      "ease",
      "ease-in",
      "ease-in-out",
      "ease-out",
      "inherit",
      "initial",
      "linear",
      "step-end",
      "step-start",
      "steps",
      "unset"
    ]
  },
  "-moz-appearance": {
    "isInherited": false,
    "subproperties": [
      "-moz-appearance"
    ],
    "supports": [],
    "values": [
      "-moz-gtk-info-bar",
      "-moz-mac-active-source-list-selection",
      "-moz-mac-disclosure-button-closed",
      "-moz-mac-disclosure-button-open",
      "-moz-mac-fullscreen-button",
      "-moz-mac-help-button",
      "-moz-mac-source-list",
      "-moz-mac-source-list-selection",
      "-moz-mac-vibrancy-dark",
      "-moz-mac-vibrancy-light",
      "-moz-mac-vibrant-titlebar-dark",
      "-moz-mac-vibrant-titlebar-light",
      "-moz-win-borderless-glass",
      "-moz-win-browsertabbar-toolbox",
      "-moz-win-communications-toolbox",
      "-moz-win-exclude-glass",
      "-moz-win-glass",
      "-moz-win-media-toolbox",
      "-moz-window-button-box",
      "-moz-window-button-box-maximized",
      "-moz-window-button-close",
      "-moz-window-button-maximize",
      "-moz-window-button-minimize",
      "-moz-window-button-restore",
      "-moz-window-frame-bottom",
      "-moz-window-frame-left",
      "-moz-window-frame-right",
      "-moz-window-titlebar",
      "-moz-window-titlebar-maximized",
      "button",
      "button-arrow-down",
      "button-arrow-next",
      "button-arrow-previous",
      "button-arrow-up",
      "button-bevel",
      "button-focus",
      "caret",
      "checkbox",
      "checkbox-container",
      "checkbox-label",
      "checkmenuitem",
      "dialog",
      "dualbutton",
      "groupbox",
      "inherit",
      "initial",
      "inner-spin-button",
      "listbox",
      "listitem",
      "menuarrow",
      "menubar",
      "menucheckbox",
      "menuimage",
      "menuitem",
      "menuitemtext",
      "menulist",
      "menulist-button",
      "menulist-text",
      "menulist-textfield",
      "menupopup",
      "menuradio",
      "menuseparator",
      "meterbar",
      "meterchunk",
      "none",
      "number-input",
      "progressbar",
      "progressbar-vertical",
      "progresschunk",
      "progresschunk-vertical",
      "radio",
      "radio-container",
      "radio-label",
      "radiomenuitem",
      "range",
      "range-thumb",
      "resizer",
      "resizerpanel",
      "scale-horizontal",
      "scale-vertical",
      "scalethumb-horizontal",
      "scalethumb-vertical",
      "scalethumbend",
      "scalethumbstart",
      "scalethumbtick",
      "scrollbar",
      "scrollbar-horizontal",
      "scrollbar-small",
      "scrollbar-vertical",
      "scrollbarbutton-down",
      "scrollbarbutton-left",
      "scrollbarbutton-right",
      "scrollbarbutton-up",
      "scrollbarthumb-horizontal",
      "scrollbarthumb-vertical",
      "scrollbartrack-horizontal",
      "scrollbartrack-vertical",
      "searchfield",
      "separator",
      "spinner",
      "spinner-downbutton",
      "spinner-textfield",
      "spinner-upbutton",
      "splitter",
      "statusbar",
      "statusbarpanel",
      "tab",
      "tab-scroll-arrow-back",
      "tab-scroll-arrow-forward",
      "tabpanel",
      "tabpanels",
      "textfield",
      "textfield-multiline",
      "toolbar",
      "toolbarbutton",
      "toolbarbutton-dropdown",
      "toolbargripper",
      "toolbox",
      "tooltip",
      "treeheader",
      "treeheadercell",
      "treeheadersortarrow",
      "treeitem",
      "treeline",
      "treetwisty",
      "treetwistyopen",
      "treeview",
      "unset",
      "window"
    ]
  },
  "-moz-backface-visibility": {
    "isInherited": false,
    "subproperties": [
      "backface-visibility"
    ],
    "supports": [],
    "values": [
      "hidden",
      "inherit",
      "initial",
      "unset",
      "visible"
    ]
  },
  "-moz-binding": {
    "isInherited": false,
    "subproperties": [
      "-moz-binding"
    ],
    "supports": [
      11
    ],
    "values": [
      "inherit",
      "initial",
      "none",
      "unset",
      "url"
    ]
  },
  "-moz-border-end": {
    "isInherited": false,
    "subproperties": [
      "border-inline-end-width",
      "border-inline-end-style",
      "border-inline-end-color"
    ],
    "supports": [
      2,
      6
    ],
    "values": [
      "COLOR",
      "calc",
      "currentColor",
      "dashed",
      "dotted",
      "double",
      "groove",
      "hidden",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "inset",
      "medium",
      "none",
      "outset",
      "rgb",
      "rgba",
      "ridge",
      "solid",
      "thick",
      "thin",
      "transparent",
      "unset"
    ]
  },
  "-moz-border-end-color": {
    "isInherited": false,
    "subproperties": [
      "border-inline-end-color"
    ],
    "supports": [
      2
    ],
    "values": [
      "COLOR",
      "currentColor",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "rgb",
      "rgba",
      "transparent",
      "unset"
    ]
  },
  "-moz-border-end-style": {
    "isInherited": false,
    "subproperties": [
      "border-inline-end-style"
    ],
    "supports": [],
    "values": [
      "dashed",
      "dotted",
      "double",
      "groove",
      "hidden",
      "inherit",
      "initial",
      "inset",
      "none",
      "outset",
      "ridge",
      "solid",
      "unset"
    ]
  },
  "-moz-border-end-width": {
    "isInherited": false,
    "subproperties": [
      "border-inline-end-width"
    ],
    "supports": [
      6
    ],
    "values": [
      "calc",
      "inherit",
      "initial",
      "medium",
      "thick",
      "thin",
      "unset"
    ]
  },
  "-moz-border-image": {
    "isInherited": false,
    "subproperties": [
      "border-image-source",
      "border-image-slice",
      "border-image-width",
      "border-image-outset",
      "border-image-repeat"
    ],
    "supports": [
      4,
      5,
      6,
      7,
      8,
      11
    ],
    "values": [
      "-moz-element",
      "-moz-image-rect",
      "-moz-linear-gradient",
      "-moz-radial-gradient",
      "-moz-repeating-linear-gradient",
      "-moz-repeating-radial-gradient",
      "fill",
      "inherit",
      "initial",
      "linear-gradient",
      "none",
      "radial-gradient",
      "repeat",
      "repeating-linear-gradient",
      "repeating-radial-gradient",
      "round",
      "space",
      "stretch",
      "unset",
      "url"
    ]
  },
  "-moz-border-start": {
    "isInherited": false,
    "subproperties": [
      "border-inline-start-width",
      "border-inline-start-style",
      "border-inline-start-color"
    ],
    "supports": [
      2,
      6
    ],
    "values": [
      "COLOR",
      "calc",
      "currentColor",
      "dashed",
      "dotted",
      "double",
      "groove",
      "hidden",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "inset",
      "medium",
      "none",
      "outset",
      "rgb",
      "rgba",
      "ridge",
      "solid",
      "thick",
      "thin",
      "transparent",
      "unset"
    ]
  },
  "-moz-border-start-color": {
    "isInherited": false,
    "subproperties": [
      "border-inline-start-color"
    ],
    "supports": [
      2
    ],
    "values": [
      "COLOR",
      "currentColor",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "rgb",
      "rgba",
      "transparent",
      "unset"
    ]
  },
  "-moz-border-start-style": {
    "isInherited": false,
    "subproperties": [
      "border-inline-start-style"
    ],
    "supports": [],
    "values": [
      "dashed",
      "dotted",
      "double",
      "groove",
      "hidden",
      "inherit",
      "initial",
      "inset",
      "none",
      "outset",
      "ridge",
      "solid",
      "unset"
    ]
  },
  "-moz-border-start-width": {
    "isInherited": false,
    "subproperties": [
      "border-inline-start-width"
    ],
    "supports": [
      6
    ],
    "values": [
      "calc",
      "inherit",
      "initial",
      "medium",
      "thick",
      "thin",
      "unset"
    ]
  },
  "-moz-box-align": {
    "isInherited": false,
    "subproperties": [
      "-moz-box-align"
    ],
    "supports": [],
    "values": [
      "baseline",
      "center",
      "end",
      "inherit",
      "initial",
      "start",
      "stretch",
      "unset"
    ]
  },
  "-moz-box-direction": {
    "isInherited": false,
    "subproperties": [
      "-moz-box-direction"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "normal",
      "reverse",
      "unset"
    ]
  },
  "-moz-box-flex": {
    "isInherited": false,
    "subproperties": [
      "-moz-box-flex"
    ],
    "supports": [
      7
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-moz-box-ordinal-group": {
    "isInherited": false,
    "subproperties": [
      "-moz-box-ordinal-group"
    ],
    "supports": [
      7
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-moz-box-orient": {
    "isInherited": false,
    "subproperties": [
      "-moz-box-orient"
    ],
    "supports": [],
    "values": [
      "block-axis",
      "horizontal",
      "inherit",
      "initial",
      "inline-axis",
      "unset",
      "vertical"
    ]
  },
  "-moz-box-pack": {
    "isInherited": false,
    "subproperties": [
      "-moz-box-pack"
    ],
    "supports": [],
    "values": [
      "center",
      "end",
      "inherit",
      "initial",
      "justify",
      "start",
      "unset"
    ]
  },
  "-moz-box-sizing": {
    "isInherited": false,
    "subproperties": [
      "box-sizing"
    ],
    "supports": [],
    "values": [
      "border-box",
      "content-box",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-moz-column-count": {
    "isInherited": false,
    "subproperties": [
      "column-count"
    ],
    "supports": [
      7
    ],
    "values": [
      "auto",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-moz-column-fill": {
    "isInherited": false,
    "subproperties": [
      "column-fill"
    ],
    "supports": [],
    "values": [
      "auto",
      "balance",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-moz-column-gap": {
    "isInherited": false,
    "subproperties": [
      "column-gap"
    ],
    "supports": [
      6
    ],
    "values": [
      "calc",
      "inherit",
      "initial",
      "normal",
      "unset"
    ]
  },
  "-moz-column-rule": {
    "isInherited": false,
    "subproperties": [
      "column-rule-width",
      "column-rule-style",
      "column-rule-color"
    ],
    "supports": [
      2,
      6
    ],
    "values": [
      "COLOR",
      "calc",
      "currentColor",
      "dashed",
      "dotted",
      "double",
      "groove",
      "hidden",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "inset",
      "medium",
      "none",
      "outset",
      "rgb",
      "rgba",
      "ridge",
      "solid",
      "thick",
      "thin",
      "transparent",
      "unset"
    ]
  },
  "-moz-column-rule-color": {
    "isInherited": false,
    "subproperties": [
      "column-rule-color"
    ],
    "supports": [
      2
    ],
    "values": [
      "COLOR",
      "currentColor",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "rgb",
      "rgba",
      "transparent",
      "unset"
    ]
  },
  "-moz-column-rule-style": {
    "isInherited": false,
    "subproperties": [
      "column-rule-style"
    ],
    "supports": [],
    "values": [
      "dashed",
      "dotted",
      "double",
      "groove",
      "hidden",
      "inherit",
      "initial",
      "inset",
      "none",
      "outset",
      "ridge",
      "solid",
      "unset"
    ]
  },
  "-moz-column-rule-width": {
    "isInherited": false,
    "subproperties": [
      "column-rule-width"
    ],
    "supports": [
      6
    ],
    "values": [
      "calc",
      "inherit",
      "initial",
      "medium",
      "thick",
      "thin",
      "unset"
    ]
  },
  "-moz-column-width": {
    "isInherited": false,
    "subproperties": [
      "column-width"
    ],
    "supports": [
      6
    ],
    "values": [
      "auto",
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-moz-columns": {
    "isInherited": false,
    "subproperties": [
      "column-count",
      "column-width"
    ],
    "supports": [
      6,
      7
    ],
    "values": [
      "auto",
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-moz-context-properties": {
    "isInherited": true,
    "subproperties": [
      "-moz-context-properties"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-moz-control-character-visibility": {
    "isInherited": true,
    "subproperties": [
      "-moz-control-character-visibility"
    ],
    "supports": [],
    "values": [
      "hidden",
      "inherit",
      "initial",
      "unset",
      "visible"
    ]
  },
  "-moz-float-edge": {
    "isInherited": false,
    "subproperties": [
      "-moz-float-edge"
    ],
    "supports": [],
    "values": [
      "content-box",
      "inherit",
      "initial",
      "margin-box",
      "unset"
    ]
  },
  "-moz-font-feature-settings": {
    "isInherited": true,
    "subproperties": [
      "font-feature-settings"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-moz-font-language-override": {
    "isInherited": true,
    "subproperties": [
      "font-language-override"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "normal",
      "unset"
    ]
  },
  "-moz-force-broken-image-icon": {
    "isInherited": false,
    "subproperties": [
      "-moz-force-broken-image-icon"
    ],
    "supports": [
      7
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-moz-hyphens": {
    "isInherited": true,
    "subproperties": [
      "hyphens"
    ],
    "supports": [],
    "values": [
      "auto",
      "inherit",
      "initial",
      "manual",
      "none",
      "unset"
    ]
  },
  "-moz-image-region": {
    "isInherited": true,
    "subproperties": [
      "-moz-image-region"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-moz-margin-end": {
    "isInherited": false,
    "subproperties": [
      "margin-inline-end"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "auto",
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-moz-margin-start": {
    "isInherited": false,
    "subproperties": [
      "margin-inline-start"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "auto",
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-moz-orient": {
    "isInherited": false,
    "subproperties": [
      "-moz-orient"
    ],
    "supports": [],
    "values": [
      "block",
      "horizontal",
      "inherit",
      "initial",
      "inline",
      "unset",
      "vertical"
    ]
  },
  "-moz-outline-radius": {
    "isInherited": false,
    "subproperties": [
      "-moz-outline-radius-topleft",
      "-moz-outline-radius-topright",
      "-moz-outline-radius-bottomright",
      "-moz-outline-radius-bottomleft"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-moz-outline-radius-bottomleft": {
    "isInherited": false,
    "subproperties": [
      "-moz-outline-radius-bottomleft"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-moz-outline-radius-bottomright": {
    "isInherited": false,
    "subproperties": [
      "-moz-outline-radius-bottomright"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-moz-outline-radius-topleft": {
    "isInherited": false,
    "subproperties": [
      "-moz-outline-radius-topleft"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-moz-outline-radius-topright": {
    "isInherited": false,
    "subproperties": [
      "-moz-outline-radius-topright"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-moz-padding-end": {
    "isInherited": false,
    "subproperties": [
      "padding-inline-end"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-moz-padding-start": {
    "isInherited": false,
    "subproperties": [
      "padding-inline-start"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-moz-perspective": {
    "isInherited": false,
    "subproperties": [
      "perspective"
    ],
    "supports": [
      6
    ],
    "values": [
      "inherit",
      "initial",
      "none",
      "unset"
    ]
  },
  "-moz-perspective-origin": {
    "isInherited": false,
    "subproperties": [
      "perspective-origin"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "bottom",
      "calc",
      "center",
      "inherit",
      "initial",
      "left",
      "right",
      "top",
      "unset"
    ]
  },
  "-moz-stack-sizing": {
    "isInherited": false,
    "subproperties": [
      "-moz-stack-sizing"
    ],
    "supports": [],
    "values": [
      "ignore",
      "ignore-horizontal",
      "ignore-vertical",
      "inherit",
      "initial",
      "stretch-to-fit",
      "unset"
    ]
  },
  "-moz-tab-size": {
    "isInherited": true,
    "subproperties": [
      "-moz-tab-size"
    ],
    "supports": [
      6,
      7
    ],
    "values": [
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-moz-text-size-adjust": {
    "isInherited": true,
    "subproperties": [
      "-moz-text-size-adjust"
    ],
    "supports": [],
    "values": [
      "auto",
      "inherit",
      "initial",
      "none",
      "unset"
    ]
  },
  "-moz-transform": {
    "isInherited": false,
    "subproperties": [
      "transform"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-moz-transform-origin": {
    "isInherited": false,
    "subproperties": [
      "transform-origin"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "bottom",
      "center",
      "inherit",
      "initial",
      "left",
      "right",
      "top",
      "unset"
    ]
  },
  "-moz-transform-style": {
    "isInherited": false,
    "subproperties": [
      "transform-style"
    ],
    "supports": [],
    "values": [
      "flat",
      "inherit",
      "initial",
      "preserve-3d",
      "unset"
    ]
  },
  "-moz-transition": {
    "isInherited": false,
    "subproperties": [
      "transition-property",
      "transition-duration",
      "transition-timing-function",
      "transition-delay"
    ],
    "supports": [
      9,
      10
    ],
    "values": [
      "all",
      "cubic-bezier",
      "ease",
      "ease-in",
      "ease-in-out",
      "ease-out",
      "inherit",
      "initial",
      "linear",
      "none",
      "step-end",
      "step-start",
      "steps",
      "unset"
    ]
  },
  "-moz-transition-delay": {
    "isInherited": false,
    "subproperties": [
      "transition-delay"
    ],
    "supports": [
      9
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-moz-transition-duration": {
    "isInherited": false,
    "subproperties": [
      "transition-duration"
    ],
    "supports": [
      9
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-moz-transition-property": {
    "isInherited": false,
    "subproperties": [
      "transition-property"
    ],
    "supports": [],
    "values": [
      "all",
      "inherit",
      "initial",
      "none",
      "unset"
    ]
  },
  "-moz-transition-timing-function": {
    "isInherited": false,
    "subproperties": [
      "transition-timing-function"
    ],
    "supports": [
      10
    ],
    "values": [
      "cubic-bezier",
      "ease",
      "ease-in",
      "ease-in-out",
      "ease-out",
      "inherit",
      "initial",
      "linear",
      "step-end",
      "step-start",
      "steps",
      "unset"
    ]
  },
  "-moz-user-focus": {
    "isInherited": true,
    "subproperties": [
      "-moz-user-focus"
    ],
    "supports": [],
    "values": [
      "ignore",
      "inherit",
      "initial",
      "none",
      "normal",
      "select-after",
      "select-all",
      "select-before",
      "select-menu",
      "select-same",
      "unset"
    ]
  },
  "-moz-user-input": {
    "isInherited": true,
    "subproperties": [
      "-moz-user-input"
    ],
    "supports": [],
    "values": [
      "auto",
      "inherit",
      "initial",
      "none",
      "unset"
    ]
  },
  "-moz-user-modify": {
    "isInherited": true,
    "subproperties": [
      "-moz-user-modify"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "read-only",
      "read-write",
      "unset",
      "write-only"
    ]
  },
  "-moz-user-select": {
    "isInherited": false,
    "subproperties": [
      "-moz-user-select"
    ],
    "supports": [],
    "values": [
      "-moz-all",
      "-moz-none",
      "-moz-text",
      "all",
      "auto",
      "element",
      "elements",
      "inherit",
      "initial",
      "none",
      "text",
      "toggle",
      "tri-state",
      "unset"
    ]
  },
  "-moz-window-dragging": {
    "isInherited": false,
    "subproperties": [
      "-moz-window-dragging"
    ],
    "supports": [],
    "values": [
      "default",
      "drag",
      "inherit",
      "initial",
      "no-drag",
      "unset"
    ]
  },
  "-moz-window-opacity": {
    "isInherited": false,
    "subproperties": [
      "-moz-window-opacity"
    ],
    "supports": [
      7
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-moz-window-transform": {
    "isInherited": false,
    "subproperties": [
      "-moz-window-transform"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-moz-window-transform-origin": {
    "isInherited": false,
    "subproperties": [
      "-moz-window-transform-origin"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "bottom",
      "center",
      "inherit",
      "initial",
      "left",
      "right",
      "top",
      "unset"
    ]
  },
  "-webkit-align-content": {
    "isInherited": false,
    "subproperties": [
      "align-content"
    ],
    "supports": [],
    "values": [
      "baseline",
      "center",
      "end",
      "flex-end",
      "flex-start",
      "inherit",
      "initial",
      "last baseline",
      "left",
      "normal",
      "right",
      "space-around",
      "space-between",
      "space-evenly",
      "start",
      "stretch",
      "unset"
    ]
  },
  "-webkit-align-items": {
    "isInherited": false,
    "subproperties": [
      "align-items"
    ],
    "supports": [],
    "values": [
      "baseline",
      "center",
      "end",
      "flex-end",
      "flex-start",
      "inherit",
      "initial",
      "last baseline",
      "left",
      "normal",
      "right",
      "self-end",
      "self-start",
      "start",
      "stretch",
      "unset"
    ]
  },
  "-webkit-align-self": {
    "isInherited": false,
    "subproperties": [
      "align-self"
    ],
    "supports": [],
    "values": [
      "auto",
      "baseline",
      "center",
      "end",
      "flex-end",
      "flex-start",
      "inherit",
      "initial",
      "last baseline",
      "left",
      "normal",
      "right",
      "self-end",
      "self-start",
      "start",
      "stretch",
      "unset"
    ]
  },
  "-webkit-animation": {
    "isInherited": false,
    "subproperties": [
      "animation-duration",
      "animation-timing-function",
      "animation-delay",
      "animation-direction",
      "animation-fill-mode",
      "animation-iteration-count",
      "animation-play-state",
      "animation-name"
    ],
    "supports": [
      7,
      9,
      10
    ],
    "values": [
      "alternate",
      "alternate-reverse",
      "backwards",
      "both",
      "cubic-bezier",
      "ease",
      "ease-in",
      "ease-in-out",
      "ease-out",
      "forwards",
      "infinite",
      "inherit",
      "initial",
      "linear",
      "none",
      "normal",
      "paused",
      "reverse",
      "running",
      "step-end",
      "step-start",
      "steps",
      "unset"
    ]
  },
  "-webkit-animation-delay": {
    "isInherited": false,
    "subproperties": [
      "animation-delay"
    ],
    "supports": [
      9
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-webkit-animation-direction": {
    "isInherited": false,
    "subproperties": [
      "animation-direction"
    ],
    "supports": [],
    "values": [
      "alternate",
      "alternate-reverse",
      "inherit",
      "initial",
      "normal",
      "reverse",
      "unset"
    ]
  },
  "-webkit-animation-duration": {
    "isInherited": false,
    "subproperties": [
      "animation-duration"
    ],
    "supports": [
      9
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-webkit-animation-fill-mode": {
    "isInherited": false,
    "subproperties": [
      "animation-fill-mode"
    ],
    "supports": [],
    "values": [
      "backwards",
      "both",
      "forwards",
      "inherit",
      "initial",
      "none",
      "unset"
    ]
  },
  "-webkit-animation-iteration-count": {
    "isInherited": false,
    "subproperties": [
      "animation-iteration-count"
    ],
    "supports": [
      7
    ],
    "values": [
      "infinite",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-webkit-animation-name": {
    "isInherited": false,
    "subproperties": [
      "animation-name"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "none",
      "unset"
    ]
  },
  "-webkit-animation-play-state": {
    "isInherited": false,
    "subproperties": [
      "animation-play-state"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "paused",
      "running",
      "unset"
    ]
  },
  "-webkit-animation-timing-function": {
    "isInherited": false,
    "subproperties": [
      "animation-timing-function"
    ],
    "supports": [
      10
    ],
    "values": [
      "cubic-bezier",
      "ease",
      "ease-in",
      "ease-in-out",
      "ease-out",
      "inherit",
      "initial",
      "linear",
      "step-end",
      "step-start",
      "steps",
      "unset"
    ]
  },
  "-webkit-backface-visibility": {
    "isInherited": false,
    "subproperties": [
      "backface-visibility"
    ],
    "supports": [],
    "values": [
      "hidden",
      "inherit",
      "initial",
      "unset",
      "visible"
    ]
  },
  "-webkit-background-clip": {
    "isInherited": false,
    "subproperties": [
      "background-clip"
    ],
    "supports": [],
    "values": [
      "border-box",
      "content-box",
      "inherit",
      "initial",
      "padding-box",
      "text",
      "unset"
    ]
  },
  "-webkit-background-origin": {
    "isInherited": false,
    "subproperties": [
      "background-origin"
    ],
    "supports": [],
    "values": [
      "border-box",
      "content-box",
      "inherit",
      "initial",
      "padding-box",
      "unset"
    ]
  },
  "-webkit-background-size": {
    "isInherited": false,
    "subproperties": [
      "background-size"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "contain",
      "cover",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-webkit-border-bottom-left-radius": {
    "isInherited": false,
    "subproperties": [
      "border-bottom-left-radius"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-webkit-border-bottom-right-radius": {
    "isInherited": false,
    "subproperties": [
      "border-bottom-right-radius"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-webkit-border-image": {
    "isInherited": false,
    "subproperties": [
      "border-image-source",
      "border-image-slice",
      "border-image-width",
      "border-image-outset",
      "border-image-repeat"
    ],
    "supports": [
      4,
      5,
      6,
      7,
      8,
      11
    ],
    "values": [
      "-moz-element",
      "-moz-image-rect",
      "-moz-linear-gradient",
      "-moz-radial-gradient",
      "-moz-repeating-linear-gradient",
      "-moz-repeating-radial-gradient",
      "fill",
      "inherit",
      "initial",
      "linear-gradient",
      "none",
      "radial-gradient",
      "repeat",
      "repeating-linear-gradient",
      "repeating-radial-gradient",
      "round",
      "space",
      "stretch",
      "unset",
      "url"
    ]
  },
  "-webkit-border-radius": {
    "isInherited": false,
    "subproperties": [
      "border-top-left-radius",
      "border-top-right-radius",
      "border-bottom-right-radius",
      "border-bottom-left-radius"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-webkit-border-top-left-radius": {
    "isInherited": false,
    "subproperties": [
      "border-top-left-radius"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-webkit-border-top-right-radius": {
    "isInherited": false,
    "subproperties": [
      "border-top-right-radius"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-webkit-box-align": {
    "isInherited": false,
    "subproperties": [
      "-moz-box-align"
    ],
    "supports": [],
    "values": [
      "baseline",
      "center",
      "end",
      "inherit",
      "initial",
      "start",
      "stretch",
      "unset"
    ]
  },
  "-webkit-box-direction": {
    "isInherited": false,
    "subproperties": [
      "-moz-box-direction"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "normal",
      "reverse",
      "unset"
    ]
  },
  "-webkit-box-flex": {
    "isInherited": false,
    "subproperties": [
      "-moz-box-flex"
    ],
    "supports": [
      7
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-webkit-box-ordinal-group": {
    "isInherited": false,
    "subproperties": [
      "-moz-box-ordinal-group"
    ],
    "supports": [
      7
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-webkit-box-orient": {
    "isInherited": false,
    "subproperties": [
      "-moz-box-orient"
    ],
    "supports": [],
    "values": [
      "block-axis",
      "horizontal",
      "inherit",
      "initial",
      "inline-axis",
      "unset",
      "vertical"
    ]
  },
  "-webkit-box-pack": {
    "isInherited": false,
    "subproperties": [
      "-moz-box-pack"
    ],
    "supports": [],
    "values": [
      "center",
      "end",
      "inherit",
      "initial",
      "justify",
      "start",
      "unset"
    ]
  },
  "-webkit-box-shadow": {
    "isInherited": false,
    "subproperties": [
      "box-shadow"
    ],
    "supports": [
      2,
      6
    ],
    "values": [
      "COLOR",
      "calc",
      "currentColor",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "inset",
      "none",
      "rgb",
      "rgba",
      "transparent",
      "unset"
    ]
  },
  "-webkit-box-sizing": {
    "isInherited": false,
    "subproperties": [
      "box-sizing"
    ],
    "supports": [],
    "values": [
      "border-box",
      "content-box",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-webkit-filter": {
    "isInherited": false,
    "subproperties": [
      "filter"
    ],
    "supports": [
      11
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-webkit-flex": {
    "isInherited": false,
    "subproperties": [
      "flex-grow",
      "flex-shrink",
      "flex-basis"
    ],
    "supports": [
      6,
      7,
      8
    ],
    "values": [
      "-moz-available",
      "-moz-fit-content",
      "-moz-max-content",
      "-moz-min-content",
      "auto",
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-webkit-flex-basis": {
    "isInherited": false,
    "subproperties": [
      "flex-basis"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "-moz-available",
      "-moz-fit-content",
      "-moz-max-content",
      "-moz-min-content",
      "auto",
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-webkit-flex-direction": {
    "isInherited": false,
    "subproperties": [
      "flex-direction"
    ],
    "supports": [],
    "values": [
      "column",
      "column-reverse",
      "inherit",
      "initial",
      "row",
      "row-reverse",
      "unset"
    ]
  },
  "-webkit-flex-flow": {
    "isInherited": false,
    "subproperties": [
      "flex-direction",
      "flex-wrap"
    ],
    "supports": [],
    "values": [
      "column",
      "column-reverse",
      "inherit",
      "initial",
      "nowrap",
      "row",
      "row-reverse",
      "unset",
      "wrap",
      "wrap-reverse"
    ]
  },
  "-webkit-flex-grow": {
    "isInherited": false,
    "subproperties": [
      "flex-grow"
    ],
    "supports": [
      7
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-webkit-flex-shrink": {
    "isInherited": false,
    "subproperties": [
      "flex-shrink"
    ],
    "supports": [
      7
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-webkit-flex-wrap": {
    "isInherited": false,
    "subproperties": [
      "flex-wrap"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "nowrap",
      "unset",
      "wrap",
      "wrap-reverse"
    ]
  },
  "-webkit-justify-content": {
    "isInherited": false,
    "subproperties": [
      "justify-content"
    ],
    "supports": [],
    "values": [
      "baseline",
      "center",
      "end",
      "flex-end",
      "flex-start",
      "inherit",
      "initial",
      "last baseline",
      "left",
      "normal",
      "right",
      "space-around",
      "space-between",
      "space-evenly",
      "start",
      "stretch",
      "unset"
    ]
  },
  "-webkit-mask": {
    "isInherited": false,
    "subproperties": [
      "mask-image",
      "mask-repeat",
      "mask-position-x",
      "mask-position-y",
      "mask-clip",
      "mask-origin",
      "mask-size",
      "mask-composite",
      "mask-mode"
    ],
    "supports": [
      4,
      5,
      6,
      8,
      11
    ],
    "values": [
      "-moz-element",
      "-moz-image-rect",
      "-moz-linear-gradient",
      "-moz-radial-gradient",
      "-moz-repeating-linear-gradient",
      "-moz-repeating-radial-gradient",
      "add",
      "alpha",
      "border-box",
      "bottom",
      "center",
      "contain",
      "content-box",
      "cover",
      "exclude",
      "fill-box",
      "inherit",
      "initial",
      "intersect",
      "left",
      "linear-gradient",
      "luminance",
      "match-source",
      "no-clip",
      "no-repeat",
      "none",
      "padding-box",
      "radial-gradient",
      "repeat",
      "repeat-x",
      "repeat-y",
      "repeating-linear-gradient",
      "repeating-radial-gradient",
      "right",
      "round",
      "space",
      "stroke-box",
      "subtract",
      "top",
      "unset",
      "url",
      "view-box"
    ]
  },
  "-webkit-mask-clip": {
    "isInherited": false,
    "subproperties": [
      "mask-clip"
    ],
    "supports": [],
    "values": [
      "border-box",
      "content-box",
      "fill-box",
      "inherit",
      "initial",
      "no-clip",
      "padding-box",
      "stroke-box",
      "unset",
      "view-box"
    ]
  },
  "-webkit-mask-composite": {
    "isInherited": false,
    "subproperties": [
      "mask-composite"
    ],
    "supports": [],
    "values": [
      "add",
      "exclude",
      "inherit",
      "initial",
      "intersect",
      "subtract",
      "unset"
    ]
  },
  "-webkit-mask-image": {
    "isInherited": false,
    "subproperties": [
      "mask-image"
    ],
    "supports": [
      4,
      5,
      11
    ],
    "values": [
      "-moz-element",
      "-moz-image-rect",
      "-moz-linear-gradient",
      "-moz-radial-gradient",
      "-moz-repeating-linear-gradient",
      "-moz-repeating-radial-gradient",
      "inherit",
      "initial",
      "linear-gradient",
      "none",
      "radial-gradient",
      "repeating-linear-gradient",
      "repeating-radial-gradient",
      "unset",
      "url"
    ]
  },
  "-webkit-mask-origin": {
    "isInherited": false,
    "subproperties": [
      "mask-origin"
    ],
    "supports": [],
    "values": [
      "border-box",
      "content-box",
      "fill-box",
      "inherit",
      "initial",
      "padding-box",
      "stroke-box",
      "unset",
      "view-box"
    ]
  },
  "-webkit-mask-position": {
    "isInherited": false,
    "subproperties": [
      "mask-position-x",
      "mask-position-y"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "bottom",
      "center",
      "inherit",
      "initial",
      "left",
      "right",
      "top",
      "unset"
    ]
  },
  "-webkit-mask-position-x": {
    "isInherited": false,
    "subproperties": [
      "mask-position-x"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "bottom",
      "center",
      "inherit",
      "initial",
      "left",
      "right",
      "top",
      "unset"
    ]
  },
  "-webkit-mask-position-y": {
    "isInherited": false,
    "subproperties": [
      "mask-position-y"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "bottom",
      "center",
      "inherit",
      "initial",
      "left",
      "right",
      "top",
      "unset"
    ]
  },
  "-webkit-mask-repeat": {
    "isInherited": false,
    "subproperties": [
      "mask-repeat"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "no-repeat",
      "repeat",
      "repeat-x",
      "repeat-y",
      "round",
      "space",
      "unset"
    ]
  },
  "-webkit-mask-size": {
    "isInherited": false,
    "subproperties": [
      "mask-size"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "contain",
      "cover",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-webkit-order": {
    "isInherited": false,
    "subproperties": [
      "order"
    ],
    "supports": [
      7
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-webkit-perspective": {
    "isInherited": false,
    "subproperties": [
      "perspective"
    ],
    "supports": [
      6
    ],
    "values": [
      "inherit",
      "initial",
      "none",
      "unset"
    ]
  },
  "-webkit-perspective-origin": {
    "isInherited": false,
    "subproperties": [
      "perspective-origin"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "bottom",
      "calc",
      "center",
      "inherit",
      "initial",
      "left",
      "right",
      "top",
      "unset"
    ]
  },
  "-webkit-text-fill-color": {
    "isInherited": true,
    "subproperties": [
      "-webkit-text-fill-color"
    ],
    "supports": [
      2
    ],
    "values": [
      "COLOR",
      "currentColor",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "rgb",
      "rgba",
      "transparent",
      "unset"
    ]
  },
  "-webkit-text-size-adjust": {
    "isInherited": true,
    "subproperties": [
      "-moz-text-size-adjust"
    ],
    "supports": [],
    "values": [
      "auto",
      "inherit",
      "initial",
      "none",
      "unset"
    ]
  },
  "-webkit-text-stroke": {
    "isInherited": true,
    "subproperties": [
      "-webkit-text-stroke-width",
      "-webkit-text-stroke-color"
    ],
    "supports": [
      2,
      6
    ],
    "values": [
      "COLOR",
      "calc",
      "currentColor",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "medium",
      "rgb",
      "rgba",
      "thick",
      "thin",
      "transparent",
      "unset"
    ]
  },
  "-webkit-text-stroke-color": {
    "isInherited": true,
    "subproperties": [
      "-webkit-text-stroke-color"
    ],
    "supports": [
      2
    ],
    "values": [
      "COLOR",
      "currentColor",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "rgb",
      "rgba",
      "transparent",
      "unset"
    ]
  },
  "-webkit-text-stroke-width": {
    "isInherited": true,
    "subproperties": [
      "-webkit-text-stroke-width"
    ],
    "supports": [
      6
    ],
    "values": [
      "calc",
      "inherit",
      "initial",
      "medium",
      "thick",
      "thin",
      "unset"
    ]
  },
  "-webkit-transform": {
    "isInherited": false,
    "subproperties": [
      "transform"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-webkit-transform-origin": {
    "isInherited": false,
    "subproperties": [
      "transform-origin"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "bottom",
      "center",
      "inherit",
      "initial",
      "left",
      "right",
      "top",
      "unset"
    ]
  },
  "-webkit-transform-style": {
    "isInherited": false,
    "subproperties": [
      "transform-style"
    ],
    "supports": [],
    "values": [
      "flat",
      "inherit",
      "initial",
      "preserve-3d",
      "unset"
    ]
  },
  "-webkit-transition": {
    "isInherited": false,
    "subproperties": [
      "transition-property",
      "transition-duration",
      "transition-timing-function",
      "transition-delay"
    ],
    "supports": [
      9,
      10
    ],
    "values": [
      "all",
      "cubic-bezier",
      "ease",
      "ease-in",
      "ease-in-out",
      "ease-out",
      "inherit",
      "initial",
      "linear",
      "none",
      "step-end",
      "step-start",
      "steps",
      "unset"
    ]
  },
  "-webkit-transition-delay": {
    "isInherited": false,
    "subproperties": [
      "transition-delay"
    ],
    "supports": [
      9
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-webkit-transition-duration": {
    "isInherited": false,
    "subproperties": [
      "transition-duration"
    ],
    "supports": [
      9
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "-webkit-transition-property": {
    "isInherited": false,
    "subproperties": [
      "transition-property"
    ],
    "supports": [],
    "values": [
      "all",
      "inherit",
      "initial",
      "none",
      "unset"
    ]
  },
  "-webkit-transition-timing-function": {
    "isInherited": false,
    "subproperties": [
      "transition-timing-function"
    ],
    "supports": [
      10
    ],
    "values": [
      "cubic-bezier",
      "ease",
      "ease-in",
      "ease-in-out",
      "ease-out",
      "inherit",
      "initial",
      "linear",
      "step-end",
      "step-start",
      "steps",
      "unset"
    ]
  },
  "-webkit-user-select": {
    "isInherited": false,
    "subproperties": [
      "-moz-user-select"
    ],
    "supports": [],
    "values": [
      "-moz-all",
      "-moz-none",
      "-moz-text",
      "all",
      "auto",
      "element",
      "elements",
      "inherit",
      "initial",
      "none",
      "text",
      "toggle",
      "tri-state",
      "unset"
    ]
  },
  "align-content": {
    "isInherited": false,
    "subproperties": [
      "align-content"
    ],
    "supports": [],
    "values": [
      "baseline",
      "center",
      "end",
      "flex-end",
      "flex-start",
      "inherit",
      "initial",
      "last baseline",
      "left",
      "normal",
      "right",
      "space-around",
      "space-between",
      "space-evenly",
      "start",
      "stretch",
      "unset"
    ]
  },
  "align-items": {
    "isInherited": false,
    "subproperties": [
      "align-items"
    ],
    "supports": [],
    "values": [
      "baseline",
      "center",
      "end",
      "flex-end",
      "flex-start",
      "inherit",
      "initial",
      "last baseline",
      "left",
      "normal",
      "right",
      "self-end",
      "self-start",
      "start",
      "stretch",
      "unset"
    ]
  },
  "align-self": {
    "isInherited": false,
    "subproperties": [
      "align-self"
    ],
    "supports": [],
    "values": [
      "auto",
      "baseline",
      "center",
      "end",
      "flex-end",
      "flex-start",
      "inherit",
      "initial",
      "last baseline",
      "left",
      "normal",
      "right",
      "self-end",
      "self-start",
      "start",
      "stretch",
      "unset"
    ]
  },
  "all": {
    "isInherited": false,
    "subproperties": [
      "align-content",
      "align-items",
      "align-self",
      "animation-delay",
      "animation-direction",
      "animation-duration",
      "animation-fill-mode",
      "animation-iteration-count",
      "animation-name",
      "animation-play-state",
      "animation-timing-function",
      "-moz-appearance",
      "backface-visibility",
      "background-attachment",
      "background-blend-mode",
      "background-clip",
      "background-color",
      "background-image",
      "background-origin",
      "background-position-x",
      "background-position-y",
      "background-repeat",
      "background-size",
      "-moz-binding",
      "block-size",
      "border-block-end-color",
      "border-block-end-style",
      "border-block-end-width",
      "border-block-start-color",
      "border-block-start-style",
      "border-block-start-width",
      "border-bottom-color",
      "border-bottom-left-radius",
      "border-bottom-right-radius",
      "border-bottom-style",
      "border-bottom-width",
      "border-collapse",
      "border-image-outset",
      "border-image-repeat",
      "border-image-slice",
      "border-image-source",
      "border-image-width",
      "border-inline-end-color",
      "border-inline-end-style",
      "border-inline-end-width",
      "border-inline-start-color",
      "border-inline-start-style",
      "border-inline-start-width",
      "border-left-color",
      "border-left-style",
      "border-left-width",
      "border-right-color",
      "border-right-style",
      "border-right-width",
      "border-spacing",
      "border-top-color",
      "border-top-left-radius",
      "border-top-right-radius",
      "border-top-style",
      "border-top-width",
      "bottom",
      "-moz-box-align",
      "box-decoration-break",
      "-moz-box-direction",
      "-moz-box-flex",
      "-moz-box-ordinal-group",
      "-moz-box-orient",
      "-moz-box-pack",
      "box-shadow",
      "box-sizing",
      "caption-side",
      "caret-color",
      "clear",
      "clip",
      "clip-path",
      "clip-rule",
      "color",
      "color-adjust",
      "color-interpolation",
      "color-interpolation-filters",
      "column-count",
      "column-fill",
      "column-gap",
      "column-rule-color",
      "column-rule-style",
      "column-rule-width",
      "column-span",
      "column-width",
      "contain",
      "content",
      "-moz-context-properties",
      "-moz-control-character-visibility",
      "counter-increment",
      "counter-reset",
      "cursor",
      "display",
      "dominant-baseline",
      "empty-cells",
      "fill",
      "fill-opacity",
      "fill-rule",
      "filter",
      "flex-basis",
      "flex-direction",
      "flex-grow",
      "flex-shrink",
      "flex-wrap",
      "float",
      "-moz-float-edge",
      "flood-color",
      "flood-opacity",
      "font-family",
      "font-feature-settings",
      "font-kerning",
      "font-language-override",
      "font-optical-sizing",
      "font-size",
      "font-size-adjust",
      "-moz-font-smoothing-background-color",
      "font-stretch",
      "font-style",
      "font-synthesis",
      "font-variant-alternates",
      "font-variant-caps",
      "font-variant-east-asian",
      "font-variant-ligatures",
      "font-variant-numeric",
      "font-variant-position",
      "font-variation-settings",
      "font-weight",
      "-moz-force-broken-image-icon",
      "grid-auto-columns",
      "grid-auto-flow",
      "grid-auto-rows",
      "grid-column-end",
      "grid-column-gap",
      "grid-column-start",
      "grid-row-end",
      "grid-row-gap",
      "grid-row-start",
      "grid-template-areas",
      "grid-template-columns",
      "grid-template-rows",
      "height",
      "hyphens",
      "initial-letter",
      "image-orientation",
      "-moz-image-region",
      "image-rendering",
      "ime-mode",
      "inline-size",
      "isolation",
      "justify-content",
      "justify-items",
      "justify-self",
      "left",
      "letter-spacing",
      "lighting-color",
      "line-height",
      "list-style-image",
      "list-style-position",
      "list-style-type",
      "margin-block-end",
      "margin-block-start",
      "margin-bottom",
      "margin-inline-end",
      "margin-inline-start",
      "margin-left",
      "margin-right",
      "margin-top",
      "marker-end",
      "marker-mid",
      "marker-start",
      "mask-clip",
      "mask-composite",
      "mask-image",
      "mask-mode",
      "mask-origin",
      "mask-position-x",
      "mask-position-y",
      "mask-repeat",
      "mask-size",
      "mask-type",
      "max-block-size",
      "max-height",
      "max-inline-size",
      "max-width",
      "min-block-size",
      "-moz-min-font-size-ratio",
      "min-height",
      "min-inline-size",
      "min-width",
      "mix-blend-mode",
      "object-fit",
      "object-position",
      "offset-block-end",
      "offset-block-start",
      "offset-inline-end",
      "offset-inline-start",
      "opacity",
      "order",
      "-moz-orient",
      "-moz-osx-font-smoothing",
      "outline-color",
      "outline-offset",
      "-moz-outline-radius-bottomleft",
      "-moz-outline-radius-bottomright",
      "-moz-outline-radius-topleft",
      "-moz-outline-radius-topright",
      "outline-style",
      "outline-width",
      "overflow-clip-box-block",
      "overflow-clip-box-inline",
      "overflow-x",
      "overflow-y",
      "padding-block-end",
      "padding-block-start",
      "padding-bottom",
      "padding-inline-end",
      "padding-inline-start",
      "padding-left",
      "padding-right",
      "padding-top",
      "page-break-after",
      "page-break-before",
      "page-break-inside",
      "paint-order",
      "perspective",
      "perspective-origin",
      "pointer-events",
      "position",
      "quotes",
      "resize",
      "right",
      "rotate",
      "ruby-align",
      "ruby-position",
      "scroll-behavior",
      "overscroll-behavior-x",
      "overscroll-behavior-y",
      "scroll-snap-coordinate",
      "scroll-snap-destination",
      "scroll-snap-points-x",
      "scroll-snap-points-y",
      "scroll-snap-type-x",
      "scroll-snap-type-y",
      "shape-image-threshold",
      "shape-outside",
      "shape-rendering",
      "-moz-stack-sizing",
      "stop-color",
      "stop-opacity",
      "stroke",
      "stroke-dasharray",
      "stroke-dashoffset",
      "stroke-linecap",
      "stroke-linejoin",
      "stroke-miterlimit",
      "stroke-opacity",
      "stroke-width",
      "-x-system-font",
      "-moz-tab-size",
      "table-layout",
      "text-align",
      "text-align-last",
      "text-anchor",
      "text-combine-upright",
      "text-decoration-color",
      "text-decoration-line",
      "text-decoration-style",
      "text-emphasis-color",
      "text-emphasis-position",
      "text-emphasis-style",
      "-webkit-text-fill-color",
      "text-indent",
      "text-justify",
      "text-orientation",
      "text-overflow",
      "text-rendering",
      "text-shadow",
      "-moz-text-size-adjust",
      "-webkit-text-stroke-color",
      "-webkit-text-stroke-width",
      "scale",
      "text-transform",
      "top",
      "-moz-top-layer",
      "touch-action",
      "transform",
      "transform-box",
      "transform-origin",
      "transform-style",
      "transition-delay",
      "transition-duration",
      "transition-property",
      "transition-timing-function",
      "translate",
      "-moz-user-focus",
      "-moz-user-input",
      "-moz-user-modify",
      "-moz-user-select",
      "vector-effect",
      "vertical-align",
      "visibility",
      "white-space",
      "width",
      "will-change",
      "-moz-window-dragging",
      "-moz-window-shadow",
      "-moz-window-opacity",
      "-moz-window-transform",
      "-moz-window-transform-origin",
      "word-break",
      "word-spacing",
      "overflow-wrap",
      "writing-mode",
      "z-index"
    ],
    "supports": [
      1,
      2,
      4,
      5,
      6,
      7,
      8,
      9,
      10,
      11
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "animation": {
    "isInherited": false,
    "subproperties": [
      "animation-duration",
      "animation-timing-function",
      "animation-delay",
      "animation-direction",
      "animation-fill-mode",
      "animation-iteration-count",
      "animation-play-state",
      "animation-name"
    ],
    "supports": [
      7,
      9,
      10
    ],
    "values": [
      "alternate",
      "alternate-reverse",
      "backwards",
      "both",
      "cubic-bezier",
      "ease",
      "ease-in",
      "ease-in-out",
      "ease-out",
      "forwards",
      "infinite",
      "inherit",
      "initial",
      "linear",
      "none",
      "normal",
      "paused",
      "reverse",
      "running",
      "step-end",
      "step-start",
      "steps",
      "unset"
    ]
  },
  "animation-delay": {
    "isInherited": false,
    "subproperties": [
      "animation-delay"
    ],
    "supports": [
      9
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "animation-direction": {
    "isInherited": false,
    "subproperties": [
      "animation-direction"
    ],
    "supports": [],
    "values": [
      "alternate",
      "alternate-reverse",
      "inherit",
      "initial",
      "normal",
      "reverse",
      "unset"
    ]
  },
  "animation-duration": {
    "isInherited": false,
    "subproperties": [
      "animation-duration"
    ],
    "supports": [
      9
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "animation-fill-mode": {
    "isInherited": false,
    "subproperties": [
      "animation-fill-mode"
    ],
    "supports": [],
    "values": [
      "backwards",
      "both",
      "forwards",
      "inherit",
      "initial",
      "none",
      "unset"
    ]
  },
  "animation-iteration-count": {
    "isInherited": false,
    "subproperties": [
      "animation-iteration-count"
    ],
    "supports": [
      7
    ],
    "values": [
      "infinite",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "animation-name": {
    "isInherited": false,
    "subproperties": [
      "animation-name"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "none",
      "unset"
    ]
  },
  "animation-play-state": {
    "isInherited": false,
    "subproperties": [
      "animation-play-state"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "paused",
      "running",
      "unset"
    ]
  },
  "animation-timing-function": {
    "isInherited": false,
    "subproperties": [
      "animation-timing-function"
    ],
    "supports": [
      10
    ],
    "values": [
      "cubic-bezier",
      "ease",
      "ease-in",
      "ease-in-out",
      "ease-out",
      "inherit",
      "initial",
      "linear",
      "step-end",
      "step-start",
      "steps",
      "unset"
    ]
  },
  "backface-visibility": {
    "isInherited": false,
    "subproperties": [
      "backface-visibility"
    ],
    "supports": [],
    "values": [
      "hidden",
      "inherit",
      "initial",
      "unset",
      "visible"
    ]
  },
  "background": {
    "isInherited": false,
    "subproperties": [
      "background-color",
      "background-image",
      "background-repeat",
      "background-attachment",
      "background-clip",
      "background-origin",
      "background-position-x",
      "background-position-y",
      "background-size"
    ],
    "supports": [
      2,
      4,
      5,
      6,
      8,
      11
    ],
    "values": [
      "COLOR",
      "-moz-element",
      "-moz-image-rect",
      "-moz-linear-gradient",
      "-moz-radial-gradient",
      "-moz-repeating-linear-gradient",
      "-moz-repeating-radial-gradient",
      "border-box",
      "bottom",
      "center",
      "contain",
      "content-box",
      "cover",
      "currentColor",
      "fixed",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "left",
      "linear-gradient",
      "local",
      "no-repeat",
      "none",
      "padding-box",
      "radial-gradient",
      "repeat",
      "repeat-x",
      "repeat-y",
      "repeating-linear-gradient",
      "repeating-radial-gradient",
      "rgb",
      "rgba",
      "right",
      "round",
      "scroll",
      "space",
      "text",
      "top",
      "transparent",
      "unset",
      "url"
    ]
  },
  "background-attachment": {
    "isInherited": false,
    "subproperties": [
      "background-attachment"
    ],
    "supports": [],
    "values": [
      "fixed",
      "inherit",
      "initial",
      "local",
      "scroll",
      "unset"
    ]
  },
  "background-blend-mode": {
    "isInherited": false,
    "subproperties": [
      "background-blend-mode"
    ],
    "supports": [],
    "values": [
      "color",
      "color-burn",
      "color-dodge",
      "darken",
      "difference",
      "exclusion",
      "hard-light",
      "hue",
      "inherit",
      "initial",
      "lighten",
      "luminosity",
      "multiply",
      "normal",
      "overlay",
      "saturation",
      "screen",
      "soft-light",
      "unset"
    ]
  },
  "background-clip": {
    "isInherited": false,
    "subproperties": [
      "background-clip"
    ],
    "supports": [],
    "values": [
      "border-box",
      "content-box",
      "inherit",
      "initial",
      "padding-box",
      "text",
      "unset"
    ]
  },
  "background-color": {
    "isInherited": false,
    "subproperties": [
      "background-color"
    ],
    "supports": [
      2
    ],
    "values": [
      "COLOR",
      "currentColor",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "rgb",
      "rgba",
      "transparent",
      "unset"
    ]
  },
  "background-image": {
    "isInherited": false,
    "subproperties": [
      "background-image"
    ],
    "supports": [
      4,
      5,
      11
    ],
    "values": [
      "-moz-element",
      "-moz-image-rect",
      "-moz-linear-gradient",
      "-moz-radial-gradient",
      "-moz-repeating-linear-gradient",
      "-moz-repeating-radial-gradient",
      "inherit",
      "initial",
      "linear-gradient",
      "none",
      "radial-gradient",
      "repeating-linear-gradient",
      "repeating-radial-gradient",
      "unset",
      "url"
    ]
  },
  "background-origin": {
    "isInherited": false,
    "subproperties": [
      "background-origin"
    ],
    "supports": [],
    "values": [
      "border-box",
      "content-box",
      "inherit",
      "initial",
      "padding-box",
      "unset"
    ]
  },
  "background-position": {
    "isInherited": false,
    "subproperties": [
      "background-position-x",
      "background-position-y"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "bottom",
      "center",
      "inherit",
      "initial",
      "left",
      "right",
      "top",
      "unset"
    ]
  },
  "background-position-x": {
    "isInherited": false,
    "subproperties": [
      "background-position-x"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "bottom",
      "center",
      "inherit",
      "initial",
      "left",
      "right",
      "top",
      "unset"
    ]
  },
  "background-position-y": {
    "isInherited": false,
    "subproperties": [
      "background-position-y"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "bottom",
      "center",
      "inherit",
      "initial",
      "left",
      "right",
      "top",
      "unset"
    ]
  },
  "background-repeat": {
    "isInherited": false,
    "subproperties": [
      "background-repeat"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "no-repeat",
      "repeat",
      "repeat-x",
      "repeat-y",
      "round",
      "space",
      "unset"
    ]
  },
  "background-size": {
    "isInherited": false,
    "subproperties": [
      "background-size"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "contain",
      "cover",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "block-size": {
    "isInherited": false,
    "subproperties": [
      "block-size"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "auto",
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "border": {
    "isInherited": false,
    "subproperties": [
      "border-top-width",
      "border-right-width",
      "border-bottom-width",
      "border-left-width",
      "border-top-style",
      "border-right-style",
      "border-bottom-style",
      "border-left-style",
      "border-top-color",
      "border-right-color",
      "border-bottom-color",
      "border-left-color",
      "border-image-source",
      "border-image-slice",
      "border-image-width",
      "border-image-outset",
      "border-image-repeat"
    ],
    "supports": [
      2,
      6
    ],
    "values": [
      "COLOR",
      "-moz-element",
      "-moz-image-rect",
      "-moz-linear-gradient",
      "-moz-radial-gradient",
      "-moz-repeating-linear-gradient",
      "-moz-repeating-radial-gradient",
      "calc",
      "currentColor",
      "dashed",
      "dotted",
      "double",
      "fill",
      "groove",
      "hidden",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "inset",
      "linear-gradient",
      "medium",
      "none",
      "outset",
      "radial-gradient",
      "repeat",
      "repeating-linear-gradient",
      "repeating-radial-gradient",
      "rgb",
      "rgba",
      "ridge",
      "round",
      "solid",
      "space",
      "stretch",
      "thick",
      "thin",
      "transparent",
      "unset",
      "url"
    ]
  },
  "border-block-end": {
    "isInherited": false,
    "subproperties": [
      "border-block-end-width",
      "border-block-end-style",
      "border-block-end-color"
    ],
    "supports": [
      2,
      6
    ],
    "values": [
      "COLOR",
      "calc",
      "currentColor",
      "dashed",
      "dotted",
      "double",
      "groove",
      "hidden",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "inset",
      "medium",
      "none",
      "outset",
      "rgb",
      "rgba",
      "ridge",
      "solid",
      "thick",
      "thin",
      "transparent",
      "unset"
    ]
  },
  "border-block-end-color": {
    "isInherited": false,
    "subproperties": [
      "border-block-end-color"
    ],
    "supports": [
      2
    ],
    "values": [
      "COLOR",
      "currentColor",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "rgb",
      "rgba",
      "transparent",
      "unset"
    ]
  },
  "border-block-end-style": {
    "isInherited": false,
    "subproperties": [
      "border-block-end-style"
    ],
    "supports": [],
    "values": [
      "dashed",
      "dotted",
      "double",
      "groove",
      "hidden",
      "inherit",
      "initial",
      "inset",
      "none",
      "outset",
      "ridge",
      "solid",
      "unset"
    ]
  },
  "border-block-end-width": {
    "isInherited": false,
    "subproperties": [
      "border-block-end-width"
    ],
    "supports": [
      6
    ],
    "values": [
      "calc",
      "inherit",
      "initial",
      "medium",
      "thick",
      "thin",
      "unset"
    ]
  },
  "border-block-start": {
    "isInherited": false,
    "subproperties": [
      "border-block-start-width",
      "border-block-start-style",
      "border-block-start-color"
    ],
    "supports": [
      2,
      6
    ],
    "values": [
      "COLOR",
      "calc",
      "currentColor",
      "dashed",
      "dotted",
      "double",
      "groove",
      "hidden",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "inset",
      "medium",
      "none",
      "outset",
      "rgb",
      "rgba",
      "ridge",
      "solid",
      "thick",
      "thin",
      "transparent",
      "unset"
    ]
  },
  "border-block-start-color": {
    "isInherited": false,
    "subproperties": [
      "border-block-start-color"
    ],
    "supports": [
      2
    ],
    "values": [
      "COLOR",
      "currentColor",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "rgb",
      "rgba",
      "transparent",
      "unset"
    ]
  },
  "border-block-start-style": {
    "isInherited": false,
    "subproperties": [
      "border-block-start-style"
    ],
    "supports": [],
    "values": [
      "dashed",
      "dotted",
      "double",
      "groove",
      "hidden",
      "inherit",
      "initial",
      "inset",
      "none",
      "outset",
      "ridge",
      "solid",
      "unset"
    ]
  },
  "border-block-start-width": {
    "isInherited": false,
    "subproperties": [
      "border-block-start-width"
    ],
    "supports": [
      6
    ],
    "values": [
      "calc",
      "inherit",
      "initial",
      "medium",
      "thick",
      "thin",
      "unset"
    ]
  },
  "border-bottom": {
    "isInherited": false,
    "subproperties": [
      "border-bottom-width",
      "border-bottom-style",
      "border-bottom-color"
    ],
    "supports": [
      2,
      6
    ],
    "values": [
      "COLOR",
      "calc",
      "currentColor",
      "dashed",
      "dotted",
      "double",
      "groove",
      "hidden",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "inset",
      "medium",
      "none",
      "outset",
      "rgb",
      "rgba",
      "ridge",
      "solid",
      "thick",
      "thin",
      "transparent",
      "unset"
    ]
  },
  "border-bottom-color": {
    "isInherited": false,
    "subproperties": [
      "border-bottom-color"
    ],
    "supports": [
      2
    ],
    "values": [
      "COLOR",
      "currentColor",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "rgb",
      "rgba",
      "transparent",
      "unset"
    ]
  },
  "border-bottom-left-radius": {
    "isInherited": false,
    "subproperties": [
      "border-bottom-left-radius"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "border-bottom-right-radius": {
    "isInherited": false,
    "subproperties": [
      "border-bottom-right-radius"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "border-bottom-style": {
    "isInherited": false,
    "subproperties": [
      "border-bottom-style"
    ],
    "supports": [],
    "values": [
      "dashed",
      "dotted",
      "double",
      "groove",
      "hidden",
      "inherit",
      "initial",
      "inset",
      "none",
      "outset",
      "ridge",
      "solid",
      "unset"
    ]
  },
  "border-bottom-width": {
    "isInherited": false,
    "subproperties": [
      "border-bottom-width"
    ],
    "supports": [
      6
    ],
    "values": [
      "calc",
      "inherit",
      "initial",
      "medium",
      "thick",
      "thin",
      "unset"
    ]
  },
  "border-collapse": {
    "isInherited": true,
    "subproperties": [
      "border-collapse"
    ],
    "supports": [],
    "values": [
      "collapse",
      "inherit",
      "initial",
      "separate",
      "unset"
    ]
  },
  "border-color": {
    "isInherited": false,
    "subproperties": [
      "border-top-color",
      "border-right-color",
      "border-bottom-color",
      "border-left-color"
    ],
    "supports": [
      2
    ],
    "values": [
      "COLOR",
      "currentColor",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "rgb",
      "rgba",
      "transparent",
      "unset"
    ]
  },
  "border-image": {
    "isInherited": false,
    "subproperties": [
      "border-image-source",
      "border-image-slice",
      "border-image-width",
      "border-image-outset",
      "border-image-repeat"
    ],
    "supports": [
      4,
      5,
      6,
      7,
      8,
      11
    ],
    "values": [
      "-moz-element",
      "-moz-image-rect",
      "-moz-linear-gradient",
      "-moz-radial-gradient",
      "-moz-repeating-linear-gradient",
      "-moz-repeating-radial-gradient",
      "fill",
      "inherit",
      "initial",
      "linear-gradient",
      "none",
      "radial-gradient",
      "repeat",
      "repeating-linear-gradient",
      "repeating-radial-gradient",
      "round",
      "space",
      "stretch",
      "unset",
      "url"
    ]
  },
  "border-image-outset": {
    "isInherited": false,
    "subproperties": [
      "border-image-outset"
    ],
    "supports": [
      6,
      7
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "border-image-repeat": {
    "isInherited": false,
    "subproperties": [
      "border-image-repeat"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "repeat",
      "round",
      "space",
      "stretch",
      "unset"
    ]
  },
  "border-image-slice": {
    "isInherited": false,
    "subproperties": [
      "border-image-slice"
    ],
    "supports": [
      7,
      8
    ],
    "values": [
      "fill",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "border-image-source": {
    "isInherited": false,
    "subproperties": [
      "border-image-source"
    ],
    "supports": [
      4,
      5,
      11
    ],
    "values": [
      "-moz-element",
      "-moz-image-rect",
      "-moz-linear-gradient",
      "-moz-radial-gradient",
      "-moz-repeating-linear-gradient",
      "-moz-repeating-radial-gradient",
      "inherit",
      "initial",
      "linear-gradient",
      "none",
      "radial-gradient",
      "repeating-linear-gradient",
      "repeating-radial-gradient",
      "unset",
      "url"
    ]
  },
  "border-image-width": {
    "isInherited": false,
    "subproperties": [
      "border-image-width"
    ],
    "supports": [
      6,
      7,
      8
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "border-inline-end": {
    "isInherited": false,
    "subproperties": [
      "border-inline-end-width",
      "border-inline-end-style",
      "border-inline-end-color"
    ],
    "supports": [
      2,
      6
    ],
    "values": [
      "COLOR",
      "calc",
      "currentColor",
      "dashed",
      "dotted",
      "double",
      "groove",
      "hidden",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "inset",
      "medium",
      "none",
      "outset",
      "rgb",
      "rgba",
      "ridge",
      "solid",
      "thick",
      "thin",
      "transparent",
      "unset"
    ]
  },
  "border-inline-end-color": {
    "isInherited": false,
    "subproperties": [
      "border-inline-end-color"
    ],
    "supports": [
      2
    ],
    "values": [
      "COLOR",
      "currentColor",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "rgb",
      "rgba",
      "transparent",
      "unset"
    ]
  },
  "border-inline-end-style": {
    "isInherited": false,
    "subproperties": [
      "border-inline-end-style"
    ],
    "supports": [],
    "values": [
      "dashed",
      "dotted",
      "double",
      "groove",
      "hidden",
      "inherit",
      "initial",
      "inset",
      "none",
      "outset",
      "ridge",
      "solid",
      "unset"
    ]
  },
  "border-inline-end-width": {
    "isInherited": false,
    "subproperties": [
      "border-inline-end-width"
    ],
    "supports": [
      6
    ],
    "values": [
      "calc",
      "inherit",
      "initial",
      "medium",
      "thick",
      "thin",
      "unset"
    ]
  },
  "border-inline-start": {
    "isInherited": false,
    "subproperties": [
      "border-inline-start-width",
      "border-inline-start-style",
      "border-inline-start-color"
    ],
    "supports": [
      2,
      6
    ],
    "values": [
      "COLOR",
      "calc",
      "currentColor",
      "dashed",
      "dotted",
      "double",
      "groove",
      "hidden",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "inset",
      "medium",
      "none",
      "outset",
      "rgb",
      "rgba",
      "ridge",
      "solid",
      "thick",
      "thin",
      "transparent",
      "unset"
    ]
  },
  "border-inline-start-color": {
    "isInherited": false,
    "subproperties": [
      "border-inline-start-color"
    ],
    "supports": [
      2
    ],
    "values": [
      "COLOR",
      "currentColor",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "rgb",
      "rgba",
      "transparent",
      "unset"
    ]
  },
  "border-inline-start-style": {
    "isInherited": false,
    "subproperties": [
      "border-inline-start-style"
    ],
    "supports": [],
    "values": [
      "dashed",
      "dotted",
      "double",
      "groove",
      "hidden",
      "inherit",
      "initial",
      "inset",
      "none",
      "outset",
      "ridge",
      "solid",
      "unset"
    ]
  },
  "border-inline-start-width": {
    "isInherited": false,
    "subproperties": [
      "border-inline-start-width"
    ],
    "supports": [
      6
    ],
    "values": [
      "calc",
      "inherit",
      "initial",
      "medium",
      "thick",
      "thin",
      "unset"
    ]
  },
  "border-left": {
    "isInherited": false,
    "subproperties": [
      "border-left-width",
      "border-left-style",
      "border-left-color"
    ],
    "supports": [
      2,
      6
    ],
    "values": [
      "COLOR",
      "calc",
      "currentColor",
      "dashed",
      "dotted",
      "double",
      "groove",
      "hidden",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "inset",
      "medium",
      "none",
      "outset",
      "rgb",
      "rgba",
      "ridge",
      "solid",
      "thick",
      "thin",
      "transparent",
      "unset"
    ]
  },
  "border-left-color": {
    "isInherited": false,
    "subproperties": [
      "border-left-color"
    ],
    "supports": [
      2
    ],
    "values": [
      "COLOR",
      "currentColor",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "rgb",
      "rgba",
      "transparent",
      "unset"
    ]
  },
  "border-left-style": {
    "isInherited": false,
    "subproperties": [
      "border-left-style"
    ],
    "supports": [],
    "values": [
      "dashed",
      "dotted",
      "double",
      "groove",
      "hidden",
      "inherit",
      "initial",
      "inset",
      "none",
      "outset",
      "ridge",
      "solid",
      "unset"
    ]
  },
  "border-left-width": {
    "isInherited": false,
    "subproperties": [
      "border-left-width"
    ],
    "supports": [
      6
    ],
    "values": [
      "calc",
      "inherit",
      "initial",
      "medium",
      "thick",
      "thin",
      "unset"
    ]
  },
  "border-radius": {
    "isInherited": false,
    "subproperties": [
      "border-top-left-radius",
      "border-top-right-radius",
      "border-bottom-right-radius",
      "border-bottom-left-radius"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "border-right": {
    "isInherited": false,
    "subproperties": [
      "border-right-width",
      "border-right-style",
      "border-right-color"
    ],
    "supports": [
      2,
      6
    ],
    "values": [
      "COLOR",
      "calc",
      "currentColor",
      "dashed",
      "dotted",
      "double",
      "groove",
      "hidden",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "inset",
      "medium",
      "none",
      "outset",
      "rgb",
      "rgba",
      "ridge",
      "solid",
      "thick",
      "thin",
      "transparent",
      "unset"
    ]
  },
  "border-right-color": {
    "isInherited": false,
    "subproperties": [
      "border-right-color"
    ],
    "supports": [
      2
    ],
    "values": [
      "COLOR",
      "currentColor",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "rgb",
      "rgba",
      "transparent",
      "unset"
    ]
  },
  "border-right-style": {
    "isInherited": false,
    "subproperties": [
      "border-right-style"
    ],
    "supports": [],
    "values": [
      "dashed",
      "dotted",
      "double",
      "groove",
      "hidden",
      "inherit",
      "initial",
      "inset",
      "none",
      "outset",
      "ridge",
      "solid",
      "unset"
    ]
  },
  "border-right-width": {
    "isInherited": false,
    "subproperties": [
      "border-right-width"
    ],
    "supports": [
      6
    ],
    "values": [
      "calc",
      "inherit",
      "initial",
      "medium",
      "thick",
      "thin",
      "unset"
    ]
  },
  "border-spacing": {
    "isInherited": true,
    "subproperties": [
      "border-spacing"
    ],
    "supports": [
      6
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "border-style": {
    "isInherited": false,
    "subproperties": [
      "border-top-style",
      "border-right-style",
      "border-bottom-style",
      "border-left-style"
    ],
    "supports": [],
    "values": [
      "dashed",
      "dotted",
      "double",
      "groove",
      "hidden",
      "inherit",
      "initial",
      "inset",
      "none",
      "outset",
      "ridge",
      "solid",
      "unset"
    ]
  },
  "border-top": {
    "isInherited": false,
    "subproperties": [
      "border-top-width",
      "border-top-style",
      "border-top-color"
    ],
    "supports": [
      2,
      6
    ],
    "values": [
      "COLOR",
      "calc",
      "currentColor",
      "dashed",
      "dotted",
      "double",
      "groove",
      "hidden",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "inset",
      "medium",
      "none",
      "outset",
      "rgb",
      "rgba",
      "ridge",
      "solid",
      "thick",
      "thin",
      "transparent",
      "unset"
    ]
  },
  "border-top-color": {
    "isInherited": false,
    "subproperties": [
      "border-top-color"
    ],
    "supports": [
      2
    ],
    "values": [
      "COLOR",
      "currentColor",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "rgb",
      "rgba",
      "transparent",
      "unset"
    ]
  },
  "border-top-left-radius": {
    "isInherited": false,
    "subproperties": [
      "border-top-left-radius"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "border-top-right-radius": {
    "isInherited": false,
    "subproperties": [
      "border-top-right-radius"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "border-top-style": {
    "isInherited": false,
    "subproperties": [
      "border-top-style"
    ],
    "supports": [],
    "values": [
      "dashed",
      "dotted",
      "double",
      "groove",
      "hidden",
      "inherit",
      "initial",
      "inset",
      "none",
      "outset",
      "ridge",
      "solid",
      "unset"
    ]
  },
  "border-top-width": {
    "isInherited": false,
    "subproperties": [
      "border-top-width"
    ],
    "supports": [
      6
    ],
    "values": [
      "calc",
      "inherit",
      "initial",
      "medium",
      "thick",
      "thin",
      "unset"
    ]
  },
  "border-width": {
    "isInherited": false,
    "subproperties": [
      "border-top-width",
      "border-right-width",
      "border-bottom-width",
      "border-left-width"
    ],
    "supports": [
      6
    ],
    "values": [
      "calc",
      "inherit",
      "initial",
      "medium",
      "thick",
      "thin",
      "unset"
    ]
  },
  "bottom": {
    "isInherited": false,
    "subproperties": [
      "bottom"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "auto",
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "box-decoration-break": {
    "isInherited": false,
    "subproperties": [
      "box-decoration-break"
    ],
    "supports": [],
    "values": [
      "clone",
      "inherit",
      "initial",
      "slice",
      "unset"
    ]
  },
  "box-shadow": {
    "isInherited": false,
    "subproperties": [
      "box-shadow"
    ],
    "supports": [
      2,
      6
    ],
    "values": [
      "COLOR",
      "calc",
      "currentColor",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "inset",
      "none",
      "rgb",
      "rgba",
      "transparent",
      "unset"
    ]
  },
  "box-sizing": {
    "isInherited": false,
    "subproperties": [
      "box-sizing"
    ],
    "supports": [],
    "values": [
      "border-box",
      "content-box",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "caption-side": {
    "isInherited": true,
    "subproperties": [
      "caption-side"
    ],
    "supports": [],
    "values": [
      "bottom",
      "bottom-outside",
      "inherit",
      "initial",
      "left",
      "right",
      "top",
      "top-outside",
      "unset"
    ]
  },
  "caret-color": {
    "isInherited": true,
    "subproperties": [
      "caret-color"
    ],
    "supports": [
      2
    ],
    "values": [
      "COLOR",
      "auto",
      "currentColor",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "rgb",
      "rgba",
      "transparent",
      "unset"
    ]
  },
  "clear": {
    "isInherited": false,
    "subproperties": [
      "clear"
    ],
    "supports": [],
    "values": [
      "both",
      "inherit",
      "initial",
      "inline-end",
      "inline-start",
      "left",
      "none",
      "right",
      "unset"
    ]
  },
  "clip": {
    "isInherited": false,
    "subproperties": [
      "clip"
    ],
    "supports": [],
    "values": [
      "auto",
      "inherit",
      "initial",
      "rect",
      "unset"
    ]
  },
  "clip-path": {
    "isInherited": false,
    "subproperties": [
      "clip-path"
    ],
    "supports": [
      11
    ],
    "values": [
      "border-box",
      "circle",
      "content-box",
      "ellipse",
      "fill-box",
      "inherit",
      "initial",
      "inset",
      "margin-box",
      "none",
      "padding-box",
      "polygon",
      "stroke-box",
      "unset",
      "url",
      "view-box"
    ]
  },
  "clip-rule": {
    "isInherited": true,
    "subproperties": [
      "clip-rule"
    ],
    "supports": [],
    "values": [
      "evenodd",
      "inherit",
      "initial",
      "nonzero",
      "unset"
    ]
  },
  "color": {
    "isInherited": true,
    "subproperties": [
      "color"
    ],
    "supports": [
      2
    ],
    "values": [
      "COLOR",
      "currentColor",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "rgb",
      "rgba",
      "transparent",
      "unset"
    ]
  },
  "color-adjust": {
    "isInherited": true,
    "subproperties": [
      "color-adjust"
    ],
    "supports": [],
    "values": [
      "economy",
      "exact",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "color-interpolation": {
    "isInherited": true,
    "subproperties": [
      "color-interpolation"
    ],
    "supports": [],
    "values": [
      "auto",
      "inherit",
      "initial",
      "linearrgb",
      "srgb",
      "unset"
    ]
  },
  "color-interpolation-filters": {
    "isInherited": true,
    "subproperties": [
      "color-interpolation-filters"
    ],
    "supports": [],
    "values": [
      "auto",
      "inherit",
      "initial",
      "linearrgb",
      "srgb",
      "unset"
    ]
  },
  "column-count": {
    "isInherited": false,
    "subproperties": [
      "column-count"
    ],
    "supports": [
      7
    ],
    "values": [
      "auto",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "column-fill": {
    "isInherited": false,
    "subproperties": [
      "column-fill"
    ],
    "supports": [],
    "values": [
      "auto",
      "balance",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "column-gap": {
    "isInherited": false,
    "subproperties": [
      "column-gap"
    ],
    "supports": [
      6
    ],
    "values": [
      "calc",
      "inherit",
      "initial",
      "normal",
      "unset"
    ]
  },
  "column-rule": {
    "isInherited": false,
    "subproperties": [
      "column-rule-width",
      "column-rule-style",
      "column-rule-color"
    ],
    "supports": [
      2,
      6
    ],
    "values": [
      "COLOR",
      "calc",
      "currentColor",
      "dashed",
      "dotted",
      "double",
      "groove",
      "hidden",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "inset",
      "medium",
      "none",
      "outset",
      "rgb",
      "rgba",
      "ridge",
      "solid",
      "thick",
      "thin",
      "transparent",
      "unset"
    ]
  },
  "column-rule-color": {
    "isInherited": false,
    "subproperties": [
      "column-rule-color"
    ],
    "supports": [
      2
    ],
    "values": [
      "COLOR",
      "currentColor",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "rgb",
      "rgba",
      "transparent",
      "unset"
    ]
  },
  "column-rule-style": {
    "isInherited": false,
    "subproperties": [
      "column-rule-style"
    ],
    "supports": [],
    "values": [
      "dashed",
      "dotted",
      "double",
      "groove",
      "hidden",
      "inherit",
      "initial",
      "inset",
      "none",
      "outset",
      "ridge",
      "solid",
      "unset"
    ]
  },
  "column-rule-width": {
    "isInherited": false,
    "subproperties": [
      "column-rule-width"
    ],
    "supports": [
      6
    ],
    "values": [
      "calc",
      "inherit",
      "initial",
      "medium",
      "thick",
      "thin",
      "unset"
    ]
  },
  "column-width": {
    "isInherited": false,
    "subproperties": [
      "column-width"
    ],
    "supports": [
      6
    ],
    "values": [
      "auto",
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "columns": {
    "isInherited": false,
    "subproperties": [
      "column-count",
      "column-width"
    ],
    "supports": [
      6,
      7
    ],
    "values": [
      "auto",
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "content": {
    "isInherited": false,
    "subproperties": [
      "content"
    ],
    "supports": [
      11
    ],
    "values": [
      "-moz-alt-content",
      "attr",
      "close-quote",
      "counter",
      "counters",
      "inherit",
      "initial",
      "no-close-quote",
      "no-open-quote",
      "none",
      "normal",
      "open-quote",
      "unset",
      "url"
    ]
  },
  "counter-increment": {
    "isInherited": false,
    "subproperties": [
      "counter-increment"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "none",
      "unset"
    ]
  },
  "counter-reset": {
    "isInherited": false,
    "subproperties": [
      "counter-reset"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "none",
      "unset"
    ]
  },
  "cursor": {
    "isInherited": true,
    "subproperties": [
      "cursor"
    ],
    "supports": [
      11
    ],
    "values": [
      "alias",
      "all-scroll",
      "auto",
      "cell",
      "col-resize",
      "context-menu",
      "copy",
      "crosshair",
      "default",
      "e-resize",
      "ew-resize",
      "grab",
      "grabbing",
      "help",
      "inherit",
      "initial",
      "move",
      "n-resize",
      "ne-resize",
      "nesw-resize",
      "no-drop",
      "none",
      "not-allowed",
      "ns-resize",
      "nw-resize",
      "nwse-resize",
      "pointer",
      "progress",
      "row-resize",
      "s-resize",
      "se-resize",
      "sw-resize",
      "text",
      "unset",
      "vertical-text",
      "w-resize",
      "wait",
      "zoom-in",
      "zoom-out"
    ]
  },
  "direction": {
    "isInherited": true,
    "subproperties": [
      "direction"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "ltr",
      "rtl",
      "unset"
    ]
  },
  "display": {
    "isInherited": false,
    "subproperties": [
      "display"
    ],
    "supports": [],
    "values": [
      "-moz-box",
      "-moz-deck",
      "-moz-grid",
      "-moz-grid-group",
      "-moz-grid-line",
      "-moz-groupbox",
      "-moz-inline-box",
      "-moz-inline-grid",
      "-moz-inline-stack",
      "-moz-popup",
      "-moz-stack",
      "-webkit-box",
      "-webkit-flex",
      "-webkit-inline-box",
      "-webkit-inline-flex",
      "block",
      "contents",
      "flex",
      "flow-root",
      "grid",
      "inherit",
      "initial",
      "inline",
      "inline-block",
      "inline-flex",
      "inline-grid",
      "inline-table",
      "list-item",
      "none",
      "ruby",
      "ruby-base",
      "ruby-base-container",
      "ruby-text",
      "ruby-text-container",
      "table",
      "table-caption",
      "table-cell",
      "table-column",
      "table-column-group",
      "table-footer-group",
      "table-header-group",
      "table-row",
      "table-row-group",
      "unset"
    ]
  },
  "dominant-baseline": {
    "isInherited": false,
    "subproperties": [
      "dominant-baseline"
    ],
    "supports": [],
    "values": [
      "alphabetic",
      "auto",
      "central",
      "hanging",
      "ideographic",
      "inherit",
      "initial",
      "mathematical",
      "middle",
      "no-change",
      "reset-size",
      "text-after-edge",
      "text-before-edge",
      "unset",
      "use-script"
    ]
  },
  "empty-cells": {
    "isInherited": true,
    "subproperties": [
      "empty-cells"
    ],
    "supports": [],
    "values": [
      "hide",
      "inherit",
      "initial",
      "show",
      "unset"
    ]
  },
  "fill": {
    "isInherited": true,
    "subproperties": [
      "fill"
    ],
    "supports": [
      2,
      11
    ],
    "values": [
      "context-fill",
      "context-stroke",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "fill-opacity": {
    "isInherited": true,
    "subproperties": [
      "fill-opacity"
    ],
    "supports": [
      7
    ],
    "values": [
      "context-fill-opacity",
      "context-stroke-opacity",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "fill-rule": {
    "isInherited": true,
    "subproperties": [
      "fill-rule"
    ],
    "supports": [],
    "values": [
      "evenodd",
      "inherit",
      "initial",
      "nonzero",
      "unset"
    ]
  },
  "filter": {
    "isInherited": false,
    "subproperties": [
      "filter"
    ],
    "supports": [
      11
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "flex": {
    "isInherited": false,
    "subproperties": [
      "flex-grow",
      "flex-shrink",
      "flex-basis"
    ],
    "supports": [
      6,
      7,
      8
    ],
    "values": [
      "-moz-available",
      "-moz-fit-content",
      "-moz-max-content",
      "-moz-min-content",
      "auto",
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "flex-basis": {
    "isInherited": false,
    "subproperties": [
      "flex-basis"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "-moz-available",
      "-moz-fit-content",
      "-moz-max-content",
      "-moz-min-content",
      "auto",
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "flex-direction": {
    "isInherited": false,
    "subproperties": [
      "flex-direction"
    ],
    "supports": [],
    "values": [
      "column",
      "column-reverse",
      "inherit",
      "initial",
      "row",
      "row-reverse",
      "unset"
    ]
  },
  "flex-flow": {
    "isInherited": false,
    "subproperties": [
      "flex-direction",
      "flex-wrap"
    ],
    "supports": [],
    "values": [
      "column",
      "column-reverse",
      "inherit",
      "initial",
      "nowrap",
      "row",
      "row-reverse",
      "unset",
      "wrap",
      "wrap-reverse"
    ]
  },
  "flex-grow": {
    "isInherited": false,
    "subproperties": [
      "flex-grow"
    ],
    "supports": [
      7
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "flex-shrink": {
    "isInherited": false,
    "subproperties": [
      "flex-shrink"
    ],
    "supports": [
      7
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "flex-wrap": {
    "isInherited": false,
    "subproperties": [
      "flex-wrap"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "nowrap",
      "unset",
      "wrap",
      "wrap-reverse"
    ]
  },
  "float": {
    "isInherited": false,
    "subproperties": [
      "float"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "inline-end",
      "inline-start",
      "left",
      "none",
      "right",
      "unset"
    ]
  },
  "flood-color": {
    "isInherited": false,
    "subproperties": [
      "flood-color"
    ],
    "supports": [
      2
    ],
    "values": [
      "COLOR",
      "currentColor",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "rgb",
      "rgba",
      "transparent",
      "unset"
    ]
  },
  "flood-opacity": {
    "isInherited": false,
    "subproperties": [
      "flood-opacity"
    ],
    "supports": [
      7
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "font": {
    "isInherited": true,
    "subproperties": [
      "font-family",
      "font-style",
      "font-weight",
      "font-size",
      "line-height",
      "font-size-adjust",
      "font-stretch",
      "-x-system-font",
      "font-feature-settings",
      "font-language-override",
      "font-kerning",
      "font-optical-sizing",
      "font-variation-settings",
      "font-variant-alternates",
      "font-variant-caps",
      "font-variant-east-asian",
      "font-variant-ligatures",
      "font-variant-numeric",
      "font-variant-position"
    ],
    "supports": [
      6,
      7,
      8
    ],
    "values": [
      "-moz-block-height",
      "-moz-button",
      "-moz-desktop",
      "-moz-dialog",
      "-moz-document",
      "-moz-field",
      "-moz-info",
      "-moz-list",
      "-moz-pull-down-menu",
      "-moz-window",
      "-moz-workspace",
      "all-petite-caps",
      "all-small-caps",
      "auto",
      "bold",
      "bolder",
      "calc",
      "caption",
      "common-ligatures",
      "condensed",
      "contextual",
      "diagonal-fractions",
      "discretionary-ligatures",
      "expanded",
      "extra-condensed",
      "extra-expanded",
      "full-width",
      "historical-forms",
      "historical-ligatures",
      "icon",
      "inherit",
      "initial",
      "italic",
      "jis04",
      "jis78",
      "jis83",
      "jis90",
      "large",
      "larger",
      "lighter",
      "lining-nums",
      "medium",
      "menu",
      "message-box",
      "no-common-ligatures",
      "no-contextual",
      "no-discretionary-ligatures",
      "no-historical-ligatures",
      "none",
      "normal",
      "oblique",
      "oldstyle-nums",
      "ordinal",
      "petite-caps",
      "proportional-nums",
      "proportional-width",
      "ruby",
      "semi-condensed",
      "semi-expanded",
      "simplified",
      "slashed-zero",
      "small",
      "small-caps",
      "small-caption",
      "smaller",
      "stacked-fractions",
      "status-bar",
      "sub",
      "super",
      "tabular-nums",
      "titling-caps",
      "traditional",
      "ultra-condensed",
      "ultra-expanded",
      "unicase",
      "unset",
      "x-large",
      "x-small",
      "xx-large",
      "xx-small"
    ]
  },
  "font-family": {
    "isInherited": true,
    "subproperties": [
      "font-family"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "font-feature-settings": {
    "isInherited": true,
    "subproperties": [
      "font-feature-settings"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "font-kerning": {
    "isInherited": true,
    "subproperties": [
      "font-kerning"
    ],
    "supports": [],
    "values": [
      "auto",
      "inherit",
      "initial",
      "none",
      "normal",
      "unset"
    ]
  },
  "font-language-override": {
    "isInherited": true,
    "subproperties": [
      "font-language-override"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "normal",
      "unset"
    ]
  },
  "font-size": {
    "isInherited": true,
    "subproperties": [
      "font-size"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "calc",
      "inherit",
      "initial",
      "large",
      "larger",
      "medium",
      "small",
      "smaller",
      "unset",
      "x-large",
      "x-small",
      "xx-large",
      "xx-small"
    ]
  },
  "font-size-adjust": {
    "isInherited": true,
    "subproperties": [
      "font-size-adjust"
    ],
    "supports": [
      7
    ],
    "values": [
      "inherit",
      "initial",
      "none",
      "unset"
    ]
  },
  "font-stretch": {
    "isInherited": true,
    "subproperties": [
      "font-stretch"
    ],
    "supports": [],
    "values": [
      "condensed",
      "expanded",
      "extra-condensed",
      "extra-expanded",
      "inherit",
      "initial",
      "normal",
      "semi-condensed",
      "semi-expanded",
      "ultra-condensed",
      "ultra-expanded",
      "unset"
    ]
  },
  "font-style": {
    "isInherited": true,
    "subproperties": [
      "font-style"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "italic",
      "normal",
      "oblique",
      "unset"
    ]
  },
  "font-synthesis": {
    "isInherited": true,
    "subproperties": [
      "font-synthesis"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "style",
      "unset",
      "weight"
    ]
  },
  "font-variant": {
    "isInherited": true,
    "subproperties": [
      "font-variant-alternates",
      "font-variant-caps",
      "font-variant-east-asian",
      "font-variant-ligatures",
      "font-variant-numeric",
      "font-variant-position"
    ],
    "supports": [],
    "values": [
      "all-petite-caps",
      "all-small-caps",
      "common-ligatures",
      "contextual",
      "diagonal-fractions",
      "discretionary-ligatures",
      "full-width",
      "historical-forms",
      "historical-ligatures",
      "inherit",
      "initial",
      "jis04",
      "jis78",
      "jis83",
      "jis90",
      "lining-nums",
      "no-common-ligatures",
      "no-contextual",
      "no-discretionary-ligatures",
      "no-historical-ligatures",
      "normal",
      "oldstyle-nums",
      "ordinal",
      "petite-caps",
      "proportional-nums",
      "proportional-width",
      "ruby",
      "simplified",
      "slashed-zero",
      "small-caps",
      "stacked-fractions",
      "sub",
      "super",
      "tabular-nums",
      "titling-caps",
      "traditional",
      "unicase",
      "unset"
    ]
  },
  "font-variant-alternates": {
    "isInherited": true,
    "subproperties": [
      "font-variant-alternates"
    ],
    "supports": [],
    "values": [
      "historical-forms",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "font-variant-caps": {
    "isInherited": true,
    "subproperties": [
      "font-variant-caps"
    ],
    "supports": [],
    "values": [
      "all-petite-caps",
      "all-small-caps",
      "inherit",
      "initial",
      "normal",
      "petite-caps",
      "small-caps",
      "titling-caps",
      "unicase",
      "unset"
    ]
  },
  "font-variant-east-asian": {
    "isInherited": true,
    "subproperties": [
      "font-variant-east-asian"
    ],
    "supports": [],
    "values": [
      "full-width",
      "inherit",
      "initial",
      "jis04",
      "jis78",
      "jis83",
      "jis90",
      "proportional-width",
      "ruby",
      "simplified",
      "traditional",
      "unset"
    ]
  },
  "font-variant-ligatures": {
    "isInherited": true,
    "subproperties": [
      "font-variant-ligatures"
    ],
    "supports": [],
    "values": [
      "common-ligatures",
      "contextual",
      "discretionary-ligatures",
      "historical-ligatures",
      "inherit",
      "initial",
      "no-common-ligatures",
      "no-contextual",
      "no-discretionary-ligatures",
      "no-historical-ligatures",
      "unset"
    ]
  },
  "font-variant-numeric": {
    "isInherited": true,
    "subproperties": [
      "font-variant-numeric"
    ],
    "supports": [],
    "values": [
      "diagonal-fractions",
      "inherit",
      "initial",
      "lining-nums",
      "oldstyle-nums",
      "ordinal",
      "proportional-nums",
      "slashed-zero",
      "stacked-fractions",
      "tabular-nums",
      "unset"
    ]
  },
  "font-variant-position": {
    "isInherited": true,
    "subproperties": [
      "font-variant-position"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "normal",
      "sub",
      "super",
      "unset"
    ]
  },
  "font-weight": {
    "isInherited": true,
    "subproperties": [
      "font-weight"
    ],
    "supports": [
      7
    ],
    "values": [
      "bold",
      "bolder",
      "inherit",
      "initial",
      "lighter",
      "normal",
      "unset"
    ]
  },
  "grid": {
    "isInherited": false,
    "subproperties": [
      "grid-template-areas",
      "grid-template-rows",
      "grid-template-columns",
      "grid-auto-flow",
      "grid-auto-rows",
      "grid-auto-columns"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "column",
      "dense",
      "inherit",
      "initial",
      "max-content",
      "min-content",
      "row",
      "unset"
    ]
  },
  "grid-area": {
    "isInherited": false,
    "subproperties": [
      "grid-row-start",
      "grid-column-start",
      "grid-row-end",
      "grid-column-end"
    ],
    "supports": [
      7
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "grid-auto-columns": {
    "isInherited": false,
    "subproperties": [
      "grid-auto-columns"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "inherit",
      "initial",
      "max-content",
      "min-content",
      "unset"
    ]
  },
  "grid-auto-flow": {
    "isInherited": false,
    "subproperties": [
      "grid-auto-flow"
    ],
    "supports": [],
    "values": [
      "column",
      "dense",
      "inherit",
      "initial",
      "row",
      "unset"
    ]
  },
  "grid-auto-rows": {
    "isInherited": false,
    "subproperties": [
      "grid-auto-rows"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "inherit",
      "initial",
      "max-content",
      "min-content",
      "unset"
    ]
  },
  "grid-column": {
    "isInherited": false,
    "subproperties": [
      "grid-column-start",
      "grid-column-end"
    ],
    "supports": [
      7
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "grid-column-end": {
    "isInherited": false,
    "subproperties": [
      "grid-column-end"
    ],
    "supports": [
      7
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "grid-column-gap": {
    "isInherited": false,
    "subproperties": [
      "grid-column-gap"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "grid-column-start": {
    "isInherited": false,
    "subproperties": [
      "grid-column-start"
    ],
    "supports": [
      7
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "grid-gap": {
    "isInherited": false,
    "subproperties": [
      "grid-row-gap",
      "grid-column-gap"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "grid-row": {
    "isInherited": false,
    "subproperties": [
      "grid-row-start",
      "grid-row-end"
    ],
    "supports": [
      7
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "grid-row-end": {
    "isInherited": false,
    "subproperties": [
      "grid-row-end"
    ],
    "supports": [
      7
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "grid-row-gap": {
    "isInherited": false,
    "subproperties": [
      "grid-row-gap"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "grid-row-start": {
    "isInherited": false,
    "subproperties": [
      "grid-row-start"
    ],
    "supports": [
      7
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "grid-template": {
    "isInherited": false,
    "subproperties": [
      "grid-template-areas",
      "grid-template-rows",
      "grid-template-columns"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "inherit",
      "initial",
      "max-content",
      "min-content",
      "unset"
    ]
  },
  "grid-template-areas": {
    "isInherited": false,
    "subproperties": [
      "grid-template-areas"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "grid-template-columns": {
    "isInherited": false,
    "subproperties": [
      "grid-template-columns"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "inherit",
      "initial",
      "max-content",
      "min-content",
      "unset"
    ]
  },
  "grid-template-rows": {
    "isInherited": false,
    "subproperties": [
      "grid-template-rows"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "inherit",
      "initial",
      "max-content",
      "min-content",
      "unset"
    ]
  },
  "height": {
    "isInherited": false,
    "subproperties": [
      "height"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "-moz-available",
      "-moz-fit-content",
      "-moz-max-content",
      "-moz-min-content",
      "auto",
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "hyphens": {
    "isInherited": true,
    "subproperties": [
      "hyphens"
    ],
    "supports": [],
    "values": [
      "auto",
      "inherit",
      "initial",
      "manual",
      "none",
      "unset"
    ]
  },
  "image-orientation": {
    "isInherited": true,
    "subproperties": [
      "image-orientation"
    ],
    "supports": [
      1
    ],
    "values": [
      "flip",
      "from-image",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "image-rendering": {
    "isInherited": true,
    "subproperties": [
      "image-rendering"
    ],
    "supports": [],
    "values": [
      "-moz-crisp-edges",
      "auto",
      "inherit",
      "initial",
      "optimizequality",
      "optimizespeed",
      "unset"
    ]
  },
  "ime-mode": {
    "isInherited": false,
    "subproperties": [
      "ime-mode"
    ],
    "supports": [],
    "values": [
      "active",
      "auto",
      "disabled",
      "inactive",
      "inherit",
      "initial",
      "normal",
      "unset"
    ]
  },
  "inline-size": {
    "isInherited": false,
    "subproperties": [
      "inline-size"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "-moz-available",
      "-moz-fit-content",
      "-moz-max-content",
      "-moz-min-content",
      "auto",
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "isolation": {
    "isInherited": false,
    "subproperties": [
      "isolation"
    ],
    "supports": [],
    "values": [
      "auto",
      "inherit",
      "initial",
      "isolate",
      "unset"
    ]
  },
  "justify-content": {
    "isInherited": false,
    "subproperties": [
      "justify-content"
    ],
    "supports": [],
    "values": [
      "baseline",
      "center",
      "end",
      "flex-end",
      "flex-start",
      "inherit",
      "initial",
      "last baseline",
      "left",
      "normal",
      "right",
      "space-around",
      "space-between",
      "space-evenly",
      "start",
      "stretch",
      "unset"
    ]
  },
  "justify-items": {
    "isInherited": false,
    "subproperties": [
      "justify-items"
    ],
    "supports": [],
    "values": [
      "auto",
      "baseline",
      "center",
      "end",
      "flex-end",
      "flex-start",
      "inherit",
      "initial",
      "last baseline",
      "left",
      "normal",
      "right",
      "self-end",
      "self-start",
      "start",
      "stretch",
      "unset"
    ]
  },
  "justify-self": {
    "isInherited": false,
    "subproperties": [
      "justify-self"
    ],
    "supports": [],
    "values": [
      "auto",
      "baseline",
      "center",
      "end",
      "flex-end",
      "flex-start",
      "inherit",
      "initial",
      "last baseline",
      "left",
      "normal",
      "right",
      "self-end",
      "self-start",
      "start",
      "stretch",
      "unset"
    ]
  },
  "left": {
    "isInherited": false,
    "subproperties": [
      "left"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "auto",
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "letter-spacing": {
    "isInherited": true,
    "subproperties": [
      "letter-spacing"
    ],
    "supports": [
      6
    ],
    "values": [
      "calc",
      "inherit",
      "initial",
      "normal",
      "unset"
    ]
  },
  "lighting-color": {
    "isInherited": false,
    "subproperties": [
      "lighting-color"
    ],
    "supports": [
      2
    ],
    "values": [
      "COLOR",
      "currentColor",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "rgb",
      "rgba",
      "transparent",
      "unset"
    ]
  },
  "line-height": {
    "isInherited": true,
    "subproperties": [
      "line-height"
    ],
    "supports": [
      6,
      7,
      8
    ],
    "values": [
      "-moz-block-height",
      "calc",
      "inherit",
      "initial",
      "normal",
      "unset"
    ]
  },
  "list-style": {
    "isInherited": true,
    "subproperties": [
      "list-style-type",
      "list-style-image",
      "list-style-position"
    ],
    "supports": [
      11
    ],
    "values": [
      "arabic-indic",
      "armenian",
      "bengali",
      "cambodian",
      "circle",
      "cjk-decimal",
      "cjk-earthly-branch",
      "cjk-heavenly-stem",
      "cjk-ideographic",
      "decimal",
      "decimal-leading-zero",
      "devanagari",
      "disc",
      "disclosure-closed",
      "disclosure-open",
      "ethiopic-numeric",
      "georgian",
      "gujarati",
      "gurmukhi",
      "hebrew",
      "hiragana",
      "hiragana-iroha",
      "inherit",
      "initial",
      "inside",
      "japanese-formal",
      "japanese-informal",
      "kannada",
      "katakana",
      "katakana-iroha",
      "khmer",
      "korean-hangul-formal",
      "korean-hanja-formal",
      "korean-hanja-informal",
      "lao",
      "lower-alpha",
      "lower-armenian",
      "lower-greek",
      "lower-latin",
      "lower-roman",
      "malayalam",
      "mongolian",
      "myanmar",
      "none",
      "oriya",
      "outside",
      "persian",
      "simp-chinese-formal",
      "simp-chinese-informal",
      "square",
      "symbols",
      "tamil",
      "telugu",
      "thai",
      "tibetan",
      "trad-chinese-formal",
      "trad-chinese-informal",
      "unset",
      "upper-alpha",
      "upper-armenian",
      "upper-latin",
      "upper-roman",
      "url"
    ]
  },
  "list-style-image": {
    "isInherited": true,
    "subproperties": [
      "list-style-image"
    ],
    "supports": [
      11
    ],
    "values": [
      "inherit",
      "initial",
      "none",
      "unset",
      "url"
    ]
  },
  "list-style-position": {
    "isInherited": true,
    "subproperties": [
      "list-style-position"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "inside",
      "outside",
      "unset"
    ]
  },
  "list-style-type": {
    "isInherited": true,
    "subproperties": [
      "list-style-type"
    ],
    "supports": [],
    "values": [
      "arabic-indic",
      "armenian",
      "bengali",
      "cambodian",
      "circle",
      "cjk-decimal",
      "cjk-earthly-branch",
      "cjk-heavenly-stem",
      "cjk-ideographic",
      "decimal",
      "decimal-leading-zero",
      "devanagari",
      "disc",
      "disclosure-closed",
      "disclosure-open",
      "ethiopic-numeric",
      "georgian",
      "gujarati",
      "gurmukhi",
      "hebrew",
      "hiragana",
      "hiragana-iroha",
      "inherit",
      "initial",
      "japanese-formal",
      "japanese-informal",
      "kannada",
      "katakana",
      "katakana-iroha",
      "khmer",
      "korean-hangul-formal",
      "korean-hanja-formal",
      "korean-hanja-informal",
      "lao",
      "lower-alpha",
      "lower-armenian",
      "lower-greek",
      "lower-latin",
      "lower-roman",
      "malayalam",
      "mongolian",
      "myanmar",
      "none",
      "oriya",
      "persian",
      "simp-chinese-formal",
      "simp-chinese-informal",
      "square",
      "symbols",
      "tamil",
      "telugu",
      "thai",
      "tibetan",
      "trad-chinese-formal",
      "trad-chinese-informal",
      "unset",
      "upper-alpha",
      "upper-armenian",
      "upper-latin",
      "upper-roman"
    ]
  },
  "margin": {
    "isInherited": false,
    "subproperties": [
      "margin-top",
      "margin-right",
      "margin-bottom",
      "margin-left"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "auto",
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "margin-block-end": {
    "isInherited": false,
    "subproperties": [
      "margin-block-end"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "auto",
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "margin-block-start": {
    "isInherited": false,
    "subproperties": [
      "margin-block-start"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "auto",
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "margin-bottom": {
    "isInherited": false,
    "subproperties": [
      "margin-bottom"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "auto",
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "margin-inline-end": {
    "isInherited": false,
    "subproperties": [
      "margin-inline-end"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "auto",
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "margin-inline-start": {
    "isInherited": false,
    "subproperties": [
      "margin-inline-start"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "auto",
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "margin-left": {
    "isInherited": false,
    "subproperties": [
      "margin-left"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "auto",
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "margin-right": {
    "isInherited": false,
    "subproperties": [
      "margin-right"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "auto",
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "margin-top": {
    "isInherited": false,
    "subproperties": [
      "margin-top"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "auto",
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "marker": {
    "isInherited": true,
    "subproperties": [
      "marker-start",
      "marker-mid",
      "marker-end"
    ],
    "supports": [
      11
    ],
    "values": [
      "inherit",
      "initial",
      "none",
      "unset",
      "url"
    ]
  },
  "marker-end": {
    "isInherited": true,
    "subproperties": [
      "marker-end"
    ],
    "supports": [
      11
    ],
    "values": [
      "inherit",
      "initial",
      "none",
      "unset",
      "url"
    ]
  },
  "marker-mid": {
    "isInherited": true,
    "subproperties": [
      "marker-mid"
    ],
    "supports": [
      11
    ],
    "values": [
      "inherit",
      "initial",
      "none",
      "unset",
      "url"
    ]
  },
  "marker-start": {
    "isInherited": true,
    "subproperties": [
      "marker-start"
    ],
    "supports": [
      11
    ],
    "values": [
      "inherit",
      "initial",
      "none",
      "unset",
      "url"
    ]
  },
  "mask": {
    "isInherited": false,
    "subproperties": [
      "mask-image",
      "mask-repeat",
      "mask-position-x",
      "mask-position-y",
      "mask-clip",
      "mask-origin",
      "mask-size",
      "mask-composite",
      "mask-mode"
    ],
    "supports": [
      4,
      5,
      6,
      8,
      11
    ],
    "values": [
      "-moz-element",
      "-moz-image-rect",
      "-moz-linear-gradient",
      "-moz-radial-gradient",
      "-moz-repeating-linear-gradient",
      "-moz-repeating-radial-gradient",
      "add",
      "alpha",
      "border-box",
      "bottom",
      "center",
      "contain",
      "content-box",
      "cover",
      "exclude",
      "fill-box",
      "inherit",
      "initial",
      "intersect",
      "left",
      "linear-gradient",
      "luminance",
      "match-source",
      "no-clip",
      "no-repeat",
      "none",
      "padding-box",
      "radial-gradient",
      "repeat",
      "repeat-x",
      "repeat-y",
      "repeating-linear-gradient",
      "repeating-radial-gradient",
      "right",
      "round",
      "space",
      "stroke-box",
      "subtract",
      "top",
      "unset",
      "url",
      "view-box"
    ]
  },
  "mask-clip": {
    "isInherited": false,
    "subproperties": [
      "mask-clip"
    ],
    "supports": [],
    "values": [
      "border-box",
      "content-box",
      "fill-box",
      "inherit",
      "initial",
      "no-clip",
      "padding-box",
      "stroke-box",
      "unset",
      "view-box"
    ]
  },
  "mask-composite": {
    "isInherited": false,
    "subproperties": [
      "mask-composite"
    ],
    "supports": [],
    "values": [
      "add",
      "exclude",
      "inherit",
      "initial",
      "intersect",
      "subtract",
      "unset"
    ]
  },
  "mask-image": {
    "isInherited": false,
    "subproperties": [
      "mask-image"
    ],
    "supports": [
      4,
      5,
      11
    ],
    "values": [
      "-moz-element",
      "-moz-image-rect",
      "-moz-linear-gradient",
      "-moz-radial-gradient",
      "-moz-repeating-linear-gradient",
      "-moz-repeating-radial-gradient",
      "inherit",
      "initial",
      "linear-gradient",
      "none",
      "radial-gradient",
      "repeating-linear-gradient",
      "repeating-radial-gradient",
      "unset",
      "url"
    ]
  },
  "mask-mode": {
    "isInherited": false,
    "subproperties": [
      "mask-mode"
    ],
    "supports": [],
    "values": [
      "alpha",
      "inherit",
      "initial",
      "luminance",
      "match-source",
      "unset"
    ]
  },
  "mask-origin": {
    "isInherited": false,
    "subproperties": [
      "mask-origin"
    ],
    "supports": [],
    "values": [
      "border-box",
      "content-box",
      "fill-box",
      "inherit",
      "initial",
      "padding-box",
      "stroke-box",
      "unset",
      "view-box"
    ]
  },
  "mask-position": {
    "isInherited": false,
    "subproperties": [
      "mask-position-x",
      "mask-position-y"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "bottom",
      "center",
      "inherit",
      "initial",
      "left",
      "right",
      "top",
      "unset"
    ]
  },
  "mask-position-x": {
    "isInherited": false,
    "subproperties": [
      "mask-position-x"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "bottom",
      "center",
      "inherit",
      "initial",
      "left",
      "right",
      "top",
      "unset"
    ]
  },
  "mask-position-y": {
    "isInherited": false,
    "subproperties": [
      "mask-position-y"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "bottom",
      "center",
      "inherit",
      "initial",
      "left",
      "right",
      "top",
      "unset"
    ]
  },
  "mask-repeat": {
    "isInherited": false,
    "subproperties": [
      "mask-repeat"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "no-repeat",
      "repeat",
      "repeat-x",
      "repeat-y",
      "round",
      "space",
      "unset"
    ]
  },
  "mask-size": {
    "isInherited": false,
    "subproperties": [
      "mask-size"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "contain",
      "cover",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "mask-type": {
    "isInherited": false,
    "subproperties": [
      "mask-type"
    ],
    "supports": [],
    "values": [
      "alpha",
      "inherit",
      "initial",
      "luminance",
      "unset"
    ]
  },
  "max-block-size": {
    "isInherited": false,
    "subproperties": [
      "max-block-size"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "calc",
      "inherit",
      "initial",
      "none",
      "unset"
    ]
  },
  "max-height": {
    "isInherited": false,
    "subproperties": [
      "max-height"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "-moz-available",
      "-moz-fit-content",
      "-moz-max-content",
      "-moz-min-content",
      "calc",
      "inherit",
      "initial",
      "none",
      "unset"
    ]
  },
  "max-inline-size": {
    "isInherited": false,
    "subproperties": [
      "max-inline-size"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "-moz-available",
      "-moz-fit-content",
      "-moz-max-content",
      "-moz-min-content",
      "calc",
      "inherit",
      "initial",
      "none",
      "unset"
    ]
  },
  "max-width": {
    "isInherited": false,
    "subproperties": [
      "max-width"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "-moz-available",
      "-moz-fit-content",
      "-moz-max-content",
      "-moz-min-content",
      "calc",
      "inherit",
      "initial",
      "none",
      "unset"
    ]
  },
  "min-block-size": {
    "isInherited": false,
    "subproperties": [
      "min-block-size"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "auto",
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "min-height": {
    "isInherited": false,
    "subproperties": [
      "min-height"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "-moz-available",
      "-moz-fit-content",
      "-moz-max-content",
      "-moz-min-content",
      "auto",
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "min-inline-size": {
    "isInherited": false,
    "subproperties": [
      "min-inline-size"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "-moz-available",
      "-moz-fit-content",
      "-moz-max-content",
      "-moz-min-content",
      "auto",
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "min-width": {
    "isInherited": false,
    "subproperties": [
      "min-width"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "-moz-available",
      "-moz-fit-content",
      "-moz-max-content",
      "-moz-min-content",
      "auto",
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "mix-blend-mode": {
    "isInherited": false,
    "subproperties": [
      "mix-blend-mode"
    ],
    "supports": [],
    "values": [
      "color",
      "color-burn",
      "color-dodge",
      "darken",
      "difference",
      "exclusion",
      "hard-light",
      "hue",
      "inherit",
      "initial",
      "lighten",
      "luminosity",
      "multiply",
      "normal",
      "overlay",
      "saturation",
      "screen",
      "soft-light",
      "unset"
    ]
  },
  "object-fit": {
    "isInherited": false,
    "subproperties": [
      "object-fit"
    ],
    "supports": [],
    "values": [
      "contain",
      "cover",
      "fill",
      "inherit",
      "initial",
      "none",
      "scale-down",
      "unset"
    ]
  },
  "object-position": {
    "isInherited": false,
    "subproperties": [
      "object-position"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "bottom",
      "calc",
      "center",
      "inherit",
      "initial",
      "left",
      "right",
      "top",
      "unset"
    ]
  },
  "offset-block-end": {
    "isInherited": false,
    "subproperties": [
      "offset-block-end"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "auto",
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "offset-block-start": {
    "isInherited": false,
    "subproperties": [
      "offset-block-start"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "auto",
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "offset-inline-end": {
    "isInherited": false,
    "subproperties": [
      "offset-inline-end"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "auto",
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "offset-inline-start": {
    "isInherited": false,
    "subproperties": [
      "offset-inline-start"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "auto",
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "opacity": {
    "isInherited": false,
    "subproperties": [
      "opacity"
    ],
    "supports": [
      7
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "order": {
    "isInherited": false,
    "subproperties": [
      "order"
    ],
    "supports": [
      7
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "outline": {
    "isInherited": false,
    "subproperties": [
      "outline-width",
      "outline-style",
      "outline-color"
    ],
    "supports": [
      2,
      6
    ],
    "values": [
      "COLOR",
      "auto",
      "calc",
      "currentColor",
      "dashed",
      "dotted",
      "double",
      "groove",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "inset",
      "medium",
      "none",
      "outset",
      "rgb",
      "rgba",
      "ridge",
      "solid",
      "thick",
      "thin",
      "transparent",
      "unset"
    ]
  },
  "outline-color": {
    "isInherited": false,
    "subproperties": [
      "outline-color"
    ],
    "supports": [
      2
    ],
    "values": [
      "COLOR",
      "currentColor",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "rgb",
      "rgba",
      "transparent",
      "unset"
    ]
  },
  "outline-offset": {
    "isInherited": false,
    "subproperties": [
      "outline-offset"
    ],
    "supports": [
      6
    ],
    "values": [
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "outline-style": {
    "isInherited": false,
    "subproperties": [
      "outline-style"
    ],
    "supports": [],
    "values": [
      "auto",
      "dashed",
      "dotted",
      "double",
      "groove",
      "inherit",
      "initial",
      "inset",
      "none",
      "outset",
      "ridge",
      "solid",
      "unset"
    ]
  },
  "outline-width": {
    "isInherited": false,
    "subproperties": [
      "outline-width"
    ],
    "supports": [
      6
    ],
    "values": [
      "calc",
      "inherit",
      "initial",
      "medium",
      "thick",
      "thin",
      "unset"
    ]
  },
  "overflow": {
    "isInherited": false,
    "subproperties": [
      "overflow-x",
      "overflow-y"
    ],
    "supports": [],
    "values": [
      "-moz-hidden-unscrollable",
      "auto",
      "hidden",
      "inherit",
      "initial",
      "scroll",
      "unset",
      "visible"
    ]
  },
  "overflow-wrap": {
    "isInherited": true,
    "subproperties": [
      "overflow-wrap"
    ],
    "supports": [],
    "values": [
      "break-word",
      "inherit",
      "initial",
      "normal",
      "unset"
    ]
  },
  "overflow-x": {
    "isInherited": false,
    "subproperties": [
      "overflow-x"
    ],
    "supports": [],
    "values": [
      "-moz-hidden-unscrollable",
      "auto",
      "hidden",
      "inherit",
      "initial",
      "scroll",
      "unset",
      "visible"
    ]
  },
  "overflow-y": {
    "isInherited": false,
    "subproperties": [
      "overflow-y"
    ],
    "supports": [],
    "values": [
      "-moz-hidden-unscrollable",
      "auto",
      "hidden",
      "inherit",
      "initial",
      "scroll",
      "unset",
      "visible"
    ]
  },
  "overscroll-behavior": {
    "isInherited": false,
    "subproperties": [
      "overscroll-behavior-x",
      "overscroll-behavior-y"
    ],
    "supports": [],
    "values": [
      "auto",
      "contain",
      "inherit",
      "initial",
      "none",
      "unset"
    ]
  },
  "overscroll-behavior-x": {
    "isInherited": false,
    "subproperties": [
      "overscroll-behavior-x"
    ],
    "supports": [],
    "values": [
      "auto",
      "contain",
      "inherit",
      "initial",
      "none",
      "unset"
    ]
  },
  "overscroll-behavior-y": {
    "isInherited": false,
    "subproperties": [
      "overscroll-behavior-y"
    ],
    "supports": [],
    "values": [
      "auto",
      "contain",
      "inherit",
      "initial",
      "none",
      "unset"
    ]
  },
  "padding": {
    "isInherited": false,
    "subproperties": [
      "padding-top",
      "padding-right",
      "padding-bottom",
      "padding-left"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "padding-block-end": {
    "isInherited": false,
    "subproperties": [
      "padding-block-end"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "padding-block-start": {
    "isInherited": false,
    "subproperties": [
      "padding-block-start"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "padding-bottom": {
    "isInherited": false,
    "subproperties": [
      "padding-bottom"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "padding-inline-end": {
    "isInherited": false,
    "subproperties": [
      "padding-inline-end"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "padding-inline-start": {
    "isInherited": false,
    "subproperties": [
      "padding-inline-start"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "padding-left": {
    "isInherited": false,
    "subproperties": [
      "padding-left"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "padding-right": {
    "isInherited": false,
    "subproperties": [
      "padding-right"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "padding-top": {
    "isInherited": false,
    "subproperties": [
      "padding-top"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "page-break-after": {
    "isInherited": false,
    "subproperties": [
      "page-break-after"
    ],
    "supports": [],
    "values": [
      "always",
      "auto",
      "avoid",
      "inherit",
      "initial",
      "left",
      "right",
      "unset"
    ]
  },
  "page-break-before": {
    "isInherited": false,
    "subproperties": [
      "page-break-before"
    ],
    "supports": [],
    "values": [
      "always",
      "auto",
      "avoid",
      "inherit",
      "initial",
      "left",
      "right",
      "unset"
    ]
  },
  "page-break-inside": {
    "isInherited": false,
    "subproperties": [
      "page-break-inside"
    ],
    "supports": [],
    "values": [
      "auto",
      "avoid",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "paint-order": {
    "isInherited": true,
    "subproperties": [
      "paint-order"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "perspective": {
    "isInherited": false,
    "subproperties": [
      "perspective"
    ],
    "supports": [
      6
    ],
    "values": [
      "inherit",
      "initial",
      "none",
      "unset"
    ]
  },
  "perspective-origin": {
    "isInherited": false,
    "subproperties": [
      "perspective-origin"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "bottom",
      "calc",
      "center",
      "inherit",
      "initial",
      "left",
      "right",
      "top",
      "unset"
    ]
  },
  "place-content": {
    "isInherited": false,
    "subproperties": [
      "align-content",
      "justify-content"
    ],
    "supports": [],
    "values": [
      "baseline",
      "center",
      "end",
      "flex-end",
      "flex-start",
      "inherit",
      "initial",
      "last baseline",
      "left",
      "normal",
      "right",
      "space-around",
      "space-between",
      "space-evenly",
      "start",
      "stretch",
      "unset"
    ]
  },
  "place-items": {
    "isInherited": false,
    "subproperties": [
      "align-items",
      "justify-items"
    ],
    "supports": [],
    "values": [
      "auto",
      "baseline",
      "center",
      "end",
      "flex-end",
      "flex-start",
      "inherit",
      "initial",
      "last baseline",
      "left",
      "normal",
      "right",
      "self-end",
      "self-start",
      "start",
      "stretch",
      "unset"
    ]
  },
  "place-self": {
    "isInherited": false,
    "subproperties": [
      "align-self",
      "justify-self"
    ],
    "supports": [],
    "values": [
      "auto",
      "baseline",
      "center",
      "end",
      "flex-end",
      "flex-start",
      "inherit",
      "initial",
      "last baseline",
      "left",
      "normal",
      "right",
      "self-end",
      "self-start",
      "start",
      "stretch",
      "unset"
    ]
  },
  "pointer-events": {
    "isInherited": true,
    "subproperties": [
      "pointer-events"
    ],
    "supports": [],
    "values": [
      "all",
      "auto",
      "fill",
      "inherit",
      "initial",
      "none",
      "painted",
      "stroke",
      "unset",
      "visible",
      "visiblefill",
      "visiblepainted",
      "visiblestroke"
    ]
  },
  "position": {
    "isInherited": false,
    "subproperties": [
      "position"
    ],
    "supports": [],
    "values": [
      "absolute",
      "fixed",
      "inherit",
      "initial",
      "relative",
      "static",
      "sticky",
      "unset"
    ]
  },
  "quotes": {
    "isInherited": true,
    "subproperties": [
      "quotes"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "none",
      "unset"
    ]
  },
  "resize": {
    "isInherited": false,
    "subproperties": [
      "resize"
    ],
    "supports": [],
    "values": [
      "both",
      "horizontal",
      "inherit",
      "initial",
      "none",
      "unset",
      "vertical"
    ]
  },
  "right": {
    "isInherited": false,
    "subproperties": [
      "right"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "auto",
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "ruby-align": {
    "isInherited": true,
    "subproperties": [
      "ruby-align"
    ],
    "supports": [],
    "values": [
      "center",
      "inherit",
      "initial",
      "space-around",
      "space-between",
      "start",
      "unset"
    ]
  },
  "ruby-position": {
    "isInherited": true,
    "subproperties": [
      "ruby-position"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "over",
      "under",
      "unset"
    ]
  },
  "scroll-behavior": {
    "isInherited": false,
    "subproperties": [
      "scroll-behavior"
    ],
    "supports": [],
    "values": [
      "auto",
      "inherit",
      "initial",
      "smooth",
      "unset"
    ]
  },
  "scroll-snap-coordinate": {
    "isInherited": false,
    "subproperties": [
      "scroll-snap-coordinate"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "bottom",
      "center",
      "inherit",
      "initial",
      "left",
      "right",
      "top",
      "unset"
    ]
  },
  "scroll-snap-destination": {
    "isInherited": false,
    "subproperties": [
      "scroll-snap-destination"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "bottom",
      "center",
      "inherit",
      "initial",
      "left",
      "right",
      "top",
      "unset"
    ]
  },
  "scroll-snap-points-x": {
    "isInherited": false,
    "subproperties": [
      "scroll-snap-points-x"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "scroll-snap-points-y": {
    "isInherited": false,
    "subproperties": [
      "scroll-snap-points-y"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "scroll-snap-type": {
    "isInherited": false,
    "subproperties": [
      "scroll-snap-type-x",
      "scroll-snap-type-y"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "mandatory",
      "none",
      "proximity",
      "unset"
    ]
  },
  "scroll-snap-type-x": {
    "isInherited": false,
    "subproperties": [
      "scroll-snap-type-x"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "mandatory",
      "none",
      "proximity",
      "unset"
    ]
  },
  "scroll-snap-type-y": {
    "isInherited": false,
    "subproperties": [
      "scroll-snap-type-y"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "mandatory",
      "none",
      "proximity",
      "unset"
    ]
  },
  "shape-rendering": {
    "isInherited": true,
    "subproperties": [
      "shape-rendering"
    ],
    "supports": [],
    "values": [
      "auto",
      "crispedges",
      "geometricprecision",
      "inherit",
      "initial",
      "optimizespeed",
      "unset"
    ]
  },
  "stop-color": {
    "isInherited": false,
    "subproperties": [
      "stop-color"
    ],
    "supports": [
      2
    ],
    "values": [
      "COLOR",
      "currentColor",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "rgb",
      "rgba",
      "transparent",
      "unset"
    ]
  },
  "stop-opacity": {
    "isInherited": false,
    "subproperties": [
      "stop-opacity"
    ],
    "supports": [
      7
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "stroke": {
    "isInherited": true,
    "subproperties": [
      "stroke"
    ],
    "supports": [
      2,
      11
    ],
    "values": [
      "context-fill",
      "context-stroke",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "stroke-dasharray": {
    "isInherited": true,
    "subproperties": [
      "stroke-dasharray"
    ],
    "supports": [
      6,
      7,
      8
    ],
    "values": [
      "context-value",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "stroke-dashoffset": {
    "isInherited": true,
    "subproperties": [
      "stroke-dashoffset"
    ],
    "supports": [
      6,
      7,
      8
    ],
    "values": [
      "context-value",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "stroke-linecap": {
    "isInherited": true,
    "subproperties": [
      "stroke-linecap"
    ],
    "supports": [],
    "values": [
      "butt",
      "inherit",
      "initial",
      "round",
      "square",
      "unset"
    ]
  },
  "stroke-linejoin": {
    "isInherited": true,
    "subproperties": [
      "stroke-linejoin"
    ],
    "supports": [],
    "values": [
      "bevel",
      "inherit",
      "initial",
      "miter",
      "round",
      "unset"
    ]
  },
  "stroke-miterlimit": {
    "isInherited": true,
    "subproperties": [
      "stroke-miterlimit"
    ],
    "supports": [
      7
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "stroke-opacity": {
    "isInherited": true,
    "subproperties": [
      "stroke-opacity"
    ],
    "supports": [
      7
    ],
    "values": [
      "context-fill-opacity",
      "context-stroke-opacity",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "stroke-width": {
    "isInherited": true,
    "subproperties": [
      "stroke-width"
    ],
    "supports": [
      6,
      7,
      8
    ],
    "values": [
      "context-value",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "table-layout": {
    "isInherited": false,
    "subproperties": [
      "table-layout"
    ],
    "supports": [],
    "values": [
      "auto",
      "fixed",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "text-align": {
    "isInherited": true,
    "subproperties": [
      "text-align"
    ],
    "supports": [],
    "values": [
      "-moz-center",
      "-moz-left",
      "-moz-right",
      "center",
      "end",
      "inherit",
      "initial",
      "justify",
      "left",
      "match-parent",
      "right",
      "start",
      "unset"
    ]
  },
  "text-align-last": {
    "isInherited": true,
    "subproperties": [
      "text-align-last"
    ],
    "supports": [],
    "values": [
      "auto",
      "center",
      "end",
      "inherit",
      "initial",
      "justify",
      "left",
      "right",
      "start",
      "unset"
    ]
  },
  "text-anchor": {
    "isInherited": true,
    "subproperties": [
      "text-anchor"
    ],
    "supports": [],
    "values": [
      "end",
      "inherit",
      "initial",
      "middle",
      "start",
      "unset"
    ]
  },
  "text-combine-upright": {
    "isInherited": true,
    "subproperties": [
      "text-combine-upright"
    ],
    "supports": [],
    "values": [
      "all",
      "digits",
      "inherit",
      "initial",
      "none",
      "unset"
    ]
  },
  "text-decoration": {
    "isInherited": false,
    "subproperties": [
      "text-decoration-color",
      "text-decoration-line",
      "text-decoration-style"
    ],
    "supports": [
      2
    ],
    "values": [
      "COLOR",
      "-moz-none",
      "blink",
      "currentColor",
      "dashed",
      "dotted",
      "double",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "line-through",
      "none",
      "overline",
      "rgb",
      "rgba",
      "solid",
      "transparent",
      "underline",
      "unset",
      "wavy"
    ]
  },
  "text-decoration-color": {
    "isInherited": false,
    "subproperties": [
      "text-decoration-color"
    ],
    "supports": [
      2
    ],
    "values": [
      "COLOR",
      "currentColor",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "rgb",
      "rgba",
      "transparent",
      "unset"
    ]
  },
  "text-decoration-line": {
    "isInherited": false,
    "subproperties": [
      "text-decoration-line"
    ],
    "supports": [],
    "values": [
      "blink",
      "inherit",
      "initial",
      "line-through",
      "none",
      "overline",
      "underline",
      "unset"
    ]
  },
  "text-decoration-style": {
    "isInherited": false,
    "subproperties": [
      "text-decoration-style"
    ],
    "supports": [],
    "values": [
      "-moz-none",
      "dashed",
      "dotted",
      "double",
      "inherit",
      "initial",
      "solid",
      "unset",
      "wavy"
    ]
  },
  "text-emphasis": {
    "isInherited": true,
    "subproperties": [
      "text-emphasis-style",
      "text-emphasis-color"
    ],
    "supports": [
      2
    ],
    "values": [
      "COLOR",
      "currentColor",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "rgb",
      "rgba",
      "transparent",
      "unset"
    ]
  },
  "text-emphasis-color": {
    "isInherited": true,
    "subproperties": [
      "text-emphasis-color"
    ],
    "supports": [
      2
    ],
    "values": [
      "COLOR",
      "currentColor",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "rgb",
      "rgba",
      "transparent",
      "unset"
    ]
  },
  "text-emphasis-position": {
    "isInherited": true,
    "subproperties": [
      "text-emphasis-position"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "left",
      "over",
      "right",
      "under",
      "unset"
    ]
  },
  "text-emphasis-style": {
    "isInherited": true,
    "subproperties": [
      "text-emphasis-style"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "text-indent": {
    "isInherited": true,
    "subproperties": [
      "text-indent"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "text-justify": {
    "isInherited": true,
    "subproperties": [
      "text-justify"
    ],
    "supports": [],
    "values": [
      "auto",
      "distribute",
      "inherit",
      "initial",
      "inter-character",
      "inter-word",
      "none",
      "unset"
    ]
  },
  "text-orientation": {
    "isInherited": true,
    "subproperties": [
      "text-orientation"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "mixed",
      "sideways",
      "sideways-right",
      "unset",
      "upright"
    ]
  },
  "text-overflow": {
    "isInherited": false,
    "subproperties": [
      "text-overflow"
    ],
    "supports": [],
    "values": [
      "clip",
      "ellipsis",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "text-rendering": {
    "isInherited": true,
    "subproperties": [
      "text-rendering"
    ],
    "supports": [],
    "values": [
      "auto",
      "geometricprecision",
      "inherit",
      "initial",
      "optimizelegibility",
      "optimizespeed",
      "unset"
    ]
  },
  "text-shadow": {
    "isInherited": true,
    "subproperties": [
      "text-shadow"
    ],
    "supports": [
      2,
      6
    ],
    "values": [
      "COLOR",
      "calc",
      "currentColor",
      "hsl",
      "hsla",
      "inherit",
      "initial",
      "none",
      "rgb",
      "rgba",
      "transparent",
      "unset"
    ]
  },
  "text-transform": {
    "isInherited": true,
    "subproperties": [
      "text-transform"
    ],
    "supports": [],
    "values": [
      "capitalize",
      "full-width",
      "inherit",
      "initial",
      "lowercase",
      "none",
      "unset",
      "uppercase"
    ]
  },
  "top": {
    "isInherited": false,
    "subproperties": [
      "top"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "auto",
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "touch-action": {
    "isInherited": false,
    "subproperties": [
      "touch-action"
    ],
    "supports": [],
    "values": [
      "auto",
      "inherit",
      "initial",
      "manipulation",
      "none",
      "pan-x",
      "pan-y",
      "unset"
    ]
  },
  "transform": {
    "isInherited": false,
    "subproperties": [
      "transform"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "transform-box": {
    "isInherited": false,
    "subproperties": [
      "transform-box"
    ],
    "supports": [],
    "values": [
      "border-box",
      "fill-box",
      "inherit",
      "initial",
      "unset",
      "view-box"
    ]
  },
  "transform-origin": {
    "isInherited": false,
    "subproperties": [
      "transform-origin"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "bottom",
      "center",
      "inherit",
      "initial",
      "left",
      "right",
      "top",
      "unset"
    ]
  },
  "transform-style": {
    "isInherited": false,
    "subproperties": [
      "transform-style"
    ],
    "supports": [],
    "values": [
      "flat",
      "inherit",
      "initial",
      "preserve-3d",
      "unset"
    ]
  },
  "transition": {
    "isInherited": false,
    "subproperties": [
      "transition-property",
      "transition-duration",
      "transition-timing-function",
      "transition-delay"
    ],
    "supports": [
      9,
      10
    ],
    "values": [
      "all",
      "cubic-bezier",
      "ease",
      "ease-in",
      "ease-in-out",
      "ease-out",
      "inherit",
      "initial",
      "linear",
      "none",
      "step-end",
      "step-start",
      "steps",
      "unset"
    ]
  },
  "transition-delay": {
    "isInherited": false,
    "subproperties": [
      "transition-delay"
    ],
    "supports": [
      9
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "transition-duration": {
    "isInherited": false,
    "subproperties": [
      "transition-duration"
    ],
    "supports": [
      9
    ],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "transition-property": {
    "isInherited": false,
    "subproperties": [
      "transition-property"
    ],
    "supports": [],
    "values": [
      "all",
      "inherit",
      "initial",
      "none",
      "unset"
    ]
  },
  "transition-timing-function": {
    "isInherited": false,
    "subproperties": [
      "transition-timing-function"
    ],
    "supports": [
      10
    ],
    "values": [
      "cubic-bezier",
      "ease",
      "ease-in",
      "ease-in-out",
      "ease-out",
      "inherit",
      "initial",
      "linear",
      "step-end",
      "step-start",
      "steps",
      "unset"
    ]
  },
  "unicode-bidi": {
    "isInherited": false,
    "subproperties": [
      "unicode-bidi"
    ],
    "supports": [],
    "values": [
      "bidi-override",
      "embed",
      "inherit",
      "initial",
      "isolate",
      "isolate-override",
      "normal",
      "plaintext",
      "unset"
    ]
  },
  "vector-effect": {
    "isInherited": false,
    "subproperties": [
      "vector-effect"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "non-scaling-stroke",
      "none",
      "unset"
    ]
  },
  "vertical-align": {
    "isInherited": false,
    "subproperties": [
      "vertical-align"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "-moz-middle-with-baseline",
      "baseline",
      "bottom",
      "calc",
      "inherit",
      "initial",
      "middle",
      "sub",
      "super",
      "text-bottom",
      "text-top",
      "top",
      "unset"
    ]
  },
  "visibility": {
    "isInherited": true,
    "subproperties": [
      "visibility"
    ],
    "supports": [],
    "values": [
      "collapse",
      "hidden",
      "inherit",
      "initial",
      "unset",
      "visible"
    ]
  },
  "white-space": {
    "isInherited": true,
    "subproperties": [
      "white-space"
    ],
    "supports": [],
    "values": [
      "-moz-pre-space",
      "inherit",
      "initial",
      "normal",
      "nowrap",
      "pre",
      "pre-line",
      "pre-wrap",
      "unset"
    ]
  },
  "width": {
    "isInherited": false,
    "subproperties": [
      "width"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "-moz-available",
      "-moz-fit-content",
      "-moz-max-content",
      "-moz-min-content",
      "auto",
      "calc",
      "inherit",
      "initial",
      "unset"
    ]
  },
  "will-change": {
    "isInherited": false,
    "subproperties": [
      "will-change"
    ],
    "supports": [],
    "values": [
      "inherit",
      "initial",
      "unset"
    ]
  },
  "word-break": {
    "isInherited": true,
    "subproperties": [
      "word-break"
    ],
    "supports": [],
    "values": [
      "break-all",
      "inherit",
      "initial",
      "keep-all",
      "normal",
      "unset"
    ]
  },
  "word-spacing": {
    "isInherited": true,
    "subproperties": [
      "word-spacing"
    ],
    "supports": [
      6,
      8
    ],
    "values": [
      "calc",
      "inherit",
      "initial",
      "normal",
      "unset"
    ]
  },
  "word-wrap": {
    "isInherited": true,
    "subproperties": [
      "overflow-wrap"
    ],
    "supports": [],
    "values": [
      "break-word",
      "inherit",
      "initial",
      "normal",
      "unset"
    ]
  },
  "writing-mode": {
    "isInherited": true,
    "subproperties": [
      "writing-mode"
    ],
    "supports": [],
    "values": [
      "horizontal-tb",
      "inherit",
      "initial",
      "lr",
      "lr-tb",
      "rl",
      "rl-tb",
      "sideways-lr",
      "sideways-rl",
      "tb",
      "tb-rl",
      "unset",
      "vertical-lr",
      "vertical-rl"
    ]
  },
  "z-index": {
    "isInherited": false,
    "subproperties": [
      "z-index"
    ],
    "supports": [
      7
    ],
    "values": [
      "auto",
      "inherit",
      "initial",
      "unset"
    ]
  }
};

/**
 * A list of the pseudo elements.
 */
exports.PSEUDO_ELEMENTS = [
  ":after",
  ":before",
  ":backdrop",
  ":cue",
  ":first-letter",
  ":first-line",
  ":-moz-selection",
  ":-moz-focus-inner",
  ":-moz-focus-outer",
  ":-moz-list-bullet",
  ":-moz-list-number",
  ":-moz-progress-bar",
  ":-moz-range-track",
  ":-moz-range-progress",
  ":-moz-range-thumb",
  ":-moz-meter-bar",
  ":-moz-placeholder",
  ":placeholder",
  ":-moz-color-swatch"
];

/**
 * A list of the preferences keys for whether a CSS property is enabled or not. This is
 * exposed for testing purposes.
 */
exports.PREFERENCES = [
  [
    "all",
    "layout.css.all-shorthand.enabled"
  ],
  [
    "background-blend-mode",
    "layout.css.background-blend-mode.enabled"
  ],
  [
    "box-decoration-break",
    "layout.css.box-decoration-break.enabled"
  ],
  [
    "color-adjust",
    "layout.css.color-adjust.enabled"
  ],
  [
    "column-span",
    "layout.css.column-span.enabled"
  ],
  [
    "contain",
    "layout.css.contain.enabled"
  ],
  [
    "font-optical-sizing",
    "layout.css.font-variations.enabled"
  ],
  [
    "font-variation-settings",
    "layout.css.font-variations.enabled"
  ],
  [
    "initial-letter",
    "layout.css.initial-letter.enabled"
  ],
  [
    "image-orientation",
    "layout.css.image-orientation.enabled"
  ],
  [
    "isolation",
    "layout.css.isolation.enabled"
  ],
  [
    "mix-blend-mode",
    "layout.css.mix-blend-mode.enabled"
  ],
  [
    "-moz-osx-font-smoothing",
    "layout.css.osx-font-smoothing.enabled"
  ],
  [
    "overflow-clip-box",
    "layout.css.overflow-clip-box.enabled"
  ],
  [
    "overflow-clip-box-block",
    "layout.css.overflow-clip-box.enabled"
  ],
  [
    "overflow-clip-box-inline",
    "layout.css.overflow-clip-box.enabled"
  ],
  [
    "rotate",
    "layout.css.individual-transform.enabled"
  ],
  [
    "scroll-behavior",
    "layout.css.scroll-behavior.property-enabled"
  ],
  [
    "overscroll-behavior",
    "layout.css.overscroll-behavior.enabled"
  ],
  [
    "overscroll-behavior-x",
    "layout.css.overscroll-behavior.enabled"
  ],
  [
    "overscroll-behavior-y",
    "layout.css.overscroll-behavior.enabled"
  ],
  [
    "scroll-snap-coordinate",
    "layout.css.scroll-snap.enabled"
  ],
  [
    "scroll-snap-destination",
    "layout.css.scroll-snap.enabled"
  ],
  [
    "scroll-snap-points-x",
    "layout.css.scroll-snap.enabled"
  ],
  [
    "scroll-snap-points-y",
    "layout.css.scroll-snap.enabled"
  ],
  [
    "scroll-snap-type",
    "layout.css.scroll-snap.enabled"
  ],
  [
    "scroll-snap-type-x",
    "layout.css.scroll-snap.enabled"
  ],
  [
    "scroll-snap-type-y",
    "layout.css.scroll-snap.enabled"
  ],
  [
    "shape-image-threshold",
    "layout.css.shape-outside.enabled"
  ],
  [
    "shape-outside",
    "layout.css.shape-outside.enabled"
  ],
  [
    "text-combine-upright",
    "layout.css.text-combine-upright.enabled"
  ],
  [
    "-webkit-text-fill-color",
    "layout.css.prefixes.webkit"
  ],
  [
    "text-justify",
    "layout.css.text-justify.enabled"
  ],
  [
    "-webkit-text-stroke",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-text-stroke-color",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-text-stroke-width",
    "layout.css.prefixes.webkit"
  ],
  [
    "scale",
    "layout.css.individual-transform.enabled"
  ],
  [
    "touch-action",
    "layout.css.touch_action.enabled"
  ],
  [
    "transform-box",
    "svg.transform-box.enabled"
  ],
  [
    "translate",
    "layout.css.individual-transform.enabled"
  ],
  [
    "-moz-transform",
    "layout.css.prefixes.transforms"
  ],
  [
    "-moz-transform-origin",
    "layout.css.prefixes.transforms"
  ],
  [
    "-moz-perspective-origin",
    "layout.css.prefixes.transforms"
  ],
  [
    "-moz-perspective",
    "layout.css.prefixes.transforms"
  ],
  [
    "-moz-transform-style",
    "layout.css.prefixes.transforms"
  ],
  [
    "-moz-backface-visibility",
    "layout.css.prefixes.transforms"
  ],
  [
    "-moz-border-image",
    "layout.css.prefixes.border-image"
  ],
  [
    "-moz-transition",
    "layout.css.prefixes.transitions"
  ],
  [
    "-moz-transition-delay",
    "layout.css.prefixes.transitions"
  ],
  [
    "-moz-transition-duration",
    "layout.css.prefixes.transitions"
  ],
  [
    "-moz-transition-property",
    "layout.css.prefixes.transitions"
  ],
  [
    "-moz-transition-timing-function",
    "layout.css.prefixes.transitions"
  ],
  [
    "-moz-animation",
    "layout.css.prefixes.animations"
  ],
  [
    "-moz-animation-delay",
    "layout.css.prefixes.animations"
  ],
  [
    "-moz-animation-direction",
    "layout.css.prefixes.animations"
  ],
  [
    "-moz-animation-duration",
    "layout.css.prefixes.animations"
  ],
  [
    "-moz-animation-fill-mode",
    "layout.css.prefixes.animations"
  ],
  [
    "-moz-animation-iteration-count",
    "layout.css.prefixes.animations"
  ],
  [
    "-moz-animation-name",
    "layout.css.prefixes.animations"
  ],
  [
    "-moz-animation-play-state",
    "layout.css.prefixes.animations"
  ],
  [
    "-moz-animation-timing-function",
    "layout.css.prefixes.animations"
  ],
  [
    "-moz-box-sizing",
    "layout.css.prefixes.box-sizing"
  ],
  [
    "-moz-font-feature-settings",
    "layout.css.prefixes.font-features"
  ],
  [
    "-moz-font-language-override",
    "layout.css.prefixes.font-features"
  ],
  [
    "-moz-column-span",
    "layout.css.column-span.enabled"
  ],
  [
    "-webkit-animation",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-animation-delay",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-animation-direction",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-animation-duration",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-animation-fill-mode",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-animation-iteration-count",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-animation-name",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-animation-play-state",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-animation-timing-function",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-filter",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-text-size-adjust",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-transform",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-transform-origin",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-transform-style",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-backface-visibility",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-perspective",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-perspective-origin",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-transition",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-transition-delay",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-transition-duration",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-transition-property",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-transition-timing-function",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-border-radius",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-border-top-left-radius",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-border-top-right-radius",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-border-bottom-left-radius",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-border-bottom-right-radius",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-background-clip",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-background-origin",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-background-size",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-border-image",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-box-shadow",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-box-sizing",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-box-flex",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-box-ordinal-group",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-box-orient",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-box-direction",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-box-align",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-box-pack",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-flex-direction",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-flex-wrap",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-flex-flow",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-order",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-flex",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-flex-grow",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-flex-shrink",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-flex-basis",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-justify-content",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-align-items",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-align-self",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-align-content",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-user-select",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-mask",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-mask-clip",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-mask-composite",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-mask-image",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-mask-origin",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-mask-position",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-mask-position-x",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-mask-position-y",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-mask-repeat",
    "layout.css.prefixes.webkit"
  ],
  [
    "-webkit-mask-size",
    "layout.css.prefixes.webkit"
  ]
];
PK
!<`	q��4chrome/devtools/modules/devtools/shared/css/lexer.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 CSS Lexer.  This file is a bit unusual -- it is a more or less
// direct translation of layout/style/nsCSSScanner.cpp and
// layout/style/CSSLexer.cpp into JS.  This implements the
// CSSLexer.webidl interface, and the intent is to try to keep it in
// sync with changes to the platform CSS lexer.  Due to this goal,
// this file violates some naming conventions and consequently locally
// disables some eslint rules.

/* eslint-disable camelcase, mozilla/no-aArgs, no-else-return */

"use strict";

// White space of any kind.  No value fields are used.  Note that
// comments do *not* count as white space; comments separate tokens
// but are not themselves tokens.
const eCSSToken_Whitespace = "whitespace"; //
// A comment.
const eCSSToken_Comment = "comment"; // /*...*/

// Identifier-like tokens.  mIdent is the text of the identifier.
// The difference between ID and Hash is: if the text after the #
// would have been a valid Ident if the # hadn't been there, the
// scanner produces an ID token.  Otherwise it produces a Hash token.
// (This distinction is required by css3-selectors.)
const eCSSToken_Ident = "ident"; // word
const eCSSToken_Function = "function"; // word(
const eCSSToken_AtKeyword = "at"; // @word
const eCSSToken_ID = "id"; // #word
const eCSSToken_Hash = "hash"; // #0word

// Numeric tokens.  mNumber is the floating-point value of the
// number, and mHasSign indicates whether there was an explicit sign
// (+ or -) in front of the number.  If mIntegerValid is true, the
// number had the lexical form of an integer, and mInteger is its
// integer value.  Lexically integer values outside the range of a
// 32-bit signed number are clamped to the maximum values; mNumber
// will indicate a 'truer' value in that case.  Percentage tokens
// are always considered not to be integers, even if their numeric
// value is integral (100% => mNumber = 1.0).  For Dimension
// tokens, mIdent holds the text of the unit.
const eCSSToken_Number = "number"; // 1 -5 +2e3 3.14159 7.297352e-3
const eCSSToken_Dimension = "dimension"; // 24px 8.5in
const eCSSToken_Percentage = "percentage"; // 85% 1280.4%

// String-like tokens.  In all cases, mIdent holds the text
// belonging to the string, and mSymbol holds the delimiter
// character, which may be ', ", or zero (only for unquoted URLs).
// Bad_String and Bad_URL tokens are emitted when the closing
// delimiter or parenthesis was missing.
const eCSSToken_String = "string"; // 'foo bar' "foo bar"
const eCSSToken_Bad_String = "bad_string"; // 'foo bar
const eCSSToken_URL = "url"; // url(foobar) url("foo bar")
const eCSSToken_Bad_URL = "bad_url"; // url(foo

// Any one-character symbol.  mSymbol holds the character.
const eCSSToken_Symbol = "symbol"; // . ; { } ! *

// Match operators.  These are single tokens rather than pairs of
// Symbol tokens because css3-selectors forbids the presence of
// comments between the two characters.  No value fields are used;
// the token type indicates which operator.
const eCSSToken_Includes = "includes"; // ~=
const eCSSToken_Dashmatch = "dashmatch"; // |=
const eCSSToken_Beginsmatch = "beginsmatch"; // ^=
const eCSSToken_Endsmatch = "endsmatch"; // $=
const eCSSToken_Containsmatch = "containsmatch"; // *=

// Unicode-range token: currently used only in @font-face.
// The lexical rule for this token includes several forms that are
// semantically invalid.  Therefore, mIdent always holds the
// complete original text of the token (so we can print it
// accurately in diagnostics), and mIntegerValid is true iff the
// token is semantically valid.  In that case, mInteger holds the
// lowest value included in the range, and mInteger2 holds the
// highest value included in the range.
const eCSSToken_URange = "urange"; // U+007e U+01?? U+2000-206F

// HTML comment delimiters, ignored as a unit when they appear at
// the top level of a style sheet, for compatibility with websites
// written for compatibility with pre-CSS browsers.  This token type
// subsumes the css2.1 CDO and CDC tokens, which are always treated
// the same by the parser.  mIdent holds the text of the token, for
// diagnostics.
const eCSSToken_HTMLComment = "htmlcomment"; // <!-- -->

const eEOFCharacters_None = 0x0000;

// to handle \<EOF> inside strings
const eEOFCharacters_DropBackslash = 0x0001;

// to handle \<EOF> outside strings
const eEOFCharacters_ReplacementChar = 0x0002;

// to close comments
const eEOFCharacters_Asterisk = 0x0004;
const eEOFCharacters_Slash = 0x0008;

// to close double-quoted strings
const eEOFCharacters_DoubleQuote = 0x0010;

// to close single-quoted strings
const eEOFCharacters_SingleQuote = 0x0020;

// to close URLs
const eEOFCharacters_CloseParen = 0x0040;

// Bridge the char/string divide.
const APOSTROPHE = "'".charCodeAt(0);
const ASTERISK = "*".charCodeAt(0);
const CARRIAGE_RETURN = "\r".charCodeAt(0);
const CIRCUMFLEX_ACCENT = "^".charCodeAt(0);
const COMMERCIAL_AT = "@".charCodeAt(0);
const DIGIT_NINE = "9".charCodeAt(0);
const DIGIT_ZERO = "0".charCodeAt(0);
const DOLLAR_SIGN = "$".charCodeAt(0);
const EQUALS_SIGN = "=".charCodeAt(0);
const EXCLAMATION_MARK = "!".charCodeAt(0);
const FULL_STOP = ".".charCodeAt(0);
const GREATER_THAN_SIGN = ">".charCodeAt(0);
const HYPHEN_MINUS = "-".charCodeAt(0);
const LATIN_CAPITAL_LETTER_E = "E".charCodeAt(0);
const LATIN_CAPITAL_LETTER_U = "U".charCodeAt(0);
const LATIN_SMALL_LETTER_E = "e".charCodeAt(0);
const LATIN_SMALL_LETTER_U = "u".charCodeAt(0);
const LEFT_PARENTHESIS = "(".charCodeAt(0);
const LESS_THAN_SIGN = "<".charCodeAt(0);
const LINE_FEED = "\n".charCodeAt(0);
const NUMBER_SIGN = "#".charCodeAt(0);
const PERCENT_SIGN = "%".charCodeAt(0);
const PLUS_SIGN = "+".charCodeAt(0);
const QUESTION_MARK = "?".charCodeAt(0);
const QUOTATION_MARK = "\"".charCodeAt(0);
const REVERSE_SOLIDUS = "\\".charCodeAt(0);
const RIGHT_PARENTHESIS = ")".charCodeAt(0);
const SOLIDUS = "/".charCodeAt(0);
const TILDE = "~".charCodeAt(0);
const VERTICAL_LINE = "|".charCodeAt(0);

const UCS2_REPLACEMENT_CHAR = 0xFFFD;

const kImpliedEOFCharacters = [
  UCS2_REPLACEMENT_CHAR,
  ASTERISK,
  SOLIDUS,
  QUOTATION_MARK,
  APOSTROPHE,
  RIGHT_PARENTHESIS,
  0
];

/**
 * Ensure that the character is valid.  If it is valid, return it;
 * otherwise, return the replacement character.
 *
 * @param {Number} c the character to check
 * @return {Number} the character or its replacement
 */
function ensureValidChar(c) {
  if (c >= 0x00110000 || (c & 0xFFF800) == 0xD800) {
    // Out of range or a surrogate.
    return UCS2_REPLACEMENT_CHAR;
  }
  return c;
}

/**
 * Turn a string into an array of character codes.
 *
 * @param {String} str the input string
 * @return {Array} an array of character codes, one per character in
 *         the input string.
 */
function stringToCodes(str) {
  return Array.prototype.map.call(str, (c) => c.charCodeAt(0));
}

const IS_HEX_DIGIT = 0x01;
const IS_IDSTART = 0x02;
const IS_IDCHAR = 0x04;
const IS_URL_CHAR = 0x08;
const IS_HSPACE = 0x10;
const IS_VSPACE = 0x20;
const IS_SPACE = IS_HSPACE | IS_VSPACE;
const IS_STRING = 0x40;

const H = IS_HSPACE;
const V = IS_VSPACE;
const I = IS_IDCHAR;
const J = IS_IDSTART;
const U = IS_URL_CHAR;
const S = IS_STRING;
const X = IS_HEX_DIGIT;

const SH = S | H;
const SU = S | U;
const SUI = S | U | I;
const SUIJ = S | U | I | J;
const SUIX = S | U | I | X;
const SUIJX = S | U | I | J | X;

/* eslint-disable indent, indent-legacy, no-multi-spaces, comma-spacing, spaced-comment */
const gLexTable = [
// 00    01    02    03    04    05    06    07
    0,    S,    S,    S,    S,    S,    S,    S,
// 08   TAB    LF    0B    FF    CR    0E    0F
    S,   SH,    V,    S,    V,    V,    S,    S,
// 10    11    12    13    14    15    16    17
    S,    S,    S,    S,    S,    S,    S,    S,
// 18    19    1A    1B    1C    1D    1E    1F
    S,    S,    S,    S,    S,    S,    S,    S,
//SPC     !     "     #     $     %     &     '
   SH,   SU,    0,   SU,   SU,   SU,   SU,    0,
//  (     )     *     +     ,     -     .     /
    S,    S,   SU,   SU,   SU,  SUI,   SU,   SU,
//  0     1     2     3     4     5     6     7
 SUIX, SUIX, SUIX, SUIX, SUIX, SUIX, SUIX, SUIX,
//  8     9     :     ;     <     =     >     ?
 SUIX, SUIX,   SU,   SU,   SU,   SU,   SU,   SU,
//  @     A     B     C     D     E     F     G
   SU,SUIJX,SUIJX,SUIJX,SUIJX,SUIJX,SUIJX, SUIJ,
//  H     I     J     K     L     M     N     O
 SUIJ, SUIJ, SUIJ, SUIJ, SUIJ, SUIJ, SUIJ, SUIJ,
//  P     Q     R     S     T     U     V     W
 SUIJ, SUIJ, SUIJ, SUIJ, SUIJ, SUIJ, SUIJ, SUIJ,
//  X     Y     Z     [     \     ]     ^     _
 SUIJ, SUIJ, SUIJ,   SU,    J,   SU,   SU, SUIJ,
//  `     a     b     c     d     e     f     g
   SU,SUIJX,SUIJX,SUIJX,SUIJX,SUIJX,SUIJX, SUIJ,
//  h     i     j     k     l     m     n     o
 SUIJ, SUIJ, SUIJ, SUIJ, SUIJ, SUIJ, SUIJ, SUIJ,
//  p     q     r     s     t     u     v     w
 SUIJ, SUIJ, SUIJ, SUIJ, SUIJ, SUIJ, SUIJ, SUIJ,
//  x     y     z     {     |     }     ~    7F
 SUIJ, SUIJ, SUIJ,   SU,   SU,   SU,   SU,    S,
];
/* eslint-enable indent, indent-legacy, no-multi-spaces, comma-spacing, spaced-comment */

/**
 * True if 'ch' is in character class 'cls', which should be one of
 * the constants above or some combination of them.  All characters
 * above U+007F are considered to be in 'cls'.  EOF is never in 'cls'.
 */
function IsOpenCharClass(ch, cls) {
  return ch >= 0 && (ch >= 128 || (gLexTable[ch] & cls) != 0);
}

/**
 * True if 'ch' is in character class 'cls', which should be one of
 * the constants above or some combination of them.  No characters
 * above U+007F are considered to be in 'cls'. EOF is never in 'cls'.
 */
function IsClosedCharClass(ch, cls) {
  return ch >= 0 && ch < 128 && (gLexTable[ch] & cls) != 0;
}

/**
 * True if 'ch' is CSS whitespace, i.e. any of the ASCII characters
 * TAB, LF, FF, CR, or SPC.
 */
function IsWhitespace(ch) {
  return IsClosedCharClass(ch, IS_SPACE);
}

/**
 * True if 'ch' is horizontal whitespace, i.e. TAB or SPC.
 */
function IsHorzSpace(ch) {
  return IsClosedCharClass(ch, IS_HSPACE);
}

/**
 * True if 'ch' is vertical whitespace, i.e. LF, FF, or CR.  Vertical
 * whitespace requires special handling when consumed, see AdvanceLine.
 */
function IsVertSpace(ch) {
  return IsClosedCharClass(ch, IS_VSPACE);
}

/**
 * True if 'ch' is a character that can appear in the middle of an identifier.
 * This includes U+0000 since it is handled as U+FFFD, but for purposes of
 * GatherText it should not be included in IsOpenCharClass.
 */
function IsIdentChar(ch) {
  return IsOpenCharClass(ch, IS_IDCHAR) || ch == 0;
}

/**
 * True if 'ch' is a character that by itself begins an identifier.
 * This includes U+0000 since it is handled as U+FFFD, but for purposes of
 * GatherText it should not be included in IsOpenCharClass.
 * (This is a subset of IsIdentChar.)
 */
function IsIdentStart(ch) {
  return IsOpenCharClass(ch, IS_IDSTART) || ch == 0;
}

/**
 * True if the two-character sequence aFirstChar+aSecondChar begins an
 * identifier.
 */
function StartsIdent(aFirstChar, aSecondChar) {
  return IsIdentStart(aFirstChar) ||
    (aFirstChar == HYPHEN_MINUS && (aSecondChar == HYPHEN_MINUS ||
                                    IsIdentStart(aSecondChar)));
}

/**
 * True if 'ch' is a decimal digit.
 */
function IsDigit(ch) {
  return (ch >= DIGIT_ZERO) && (ch <= DIGIT_NINE);
}

/**
 * True if 'ch' is a hexadecimal digit.
 */
function IsHexDigit(ch) {
  return IsClosedCharClass(ch, IS_HEX_DIGIT);
}

/**
 * Assuming that 'ch' is a decimal digit, return its numeric value.
 */
function DecimalDigitValue(ch) {
  return ch - DIGIT_ZERO;
}

/**
 * Assuming that 'ch' is a hexadecimal digit, return its numeric value.
 */
function HexDigitValue(ch) {
  if (IsDigit(ch)) {
    return DecimalDigitValue(ch);
  } else {
    // Note: c&7 just keeps the low three bits which causes
    // upper and lower case alphabetics to both yield their
    // "relative to 10" value for computing the hex value.
    return (ch & 0x7) + 9;
  }
}

/**
 * If 'ch' can be the first character of a two-character match operator
 * token, return the token type code for that token, otherwise return
 * eCSSToken_Symbol to indicate that it can't.
 */
function MatchOperatorType(ch) {
  switch (ch) {
    case TILDE: return eCSSToken_Includes;
    case VERTICAL_LINE: return eCSSToken_Dashmatch;
    case CIRCUMFLEX_ACCENT: return eCSSToken_Beginsmatch;
    case DOLLAR_SIGN: return eCSSToken_Endsmatch;
    case ASTERISK: return eCSSToken_Containsmatch;
    default: return eCSSToken_Symbol;
  }
}

function Scanner(buffer) {
  this.mBuffer = buffer || "";
  this.mOffset = 0;
  this.mCount = this.mBuffer.length;
  this.mLineNumber = 1;
  this.mLineOffset = 0;
  this.mTokenLineOffset = 0;
  this.mTokenOffset = 0;
  this.mTokenLineNumber = 1;
  this.mEOFCharacters = eEOFCharacters_None;
}

Scanner.prototype = {
  /**
   * @see CSSLexer.lineNumber
   */
  get lineNumber() {
    return this.mTokenLineNumber - 1;
  },

  /**
   * @see CSSLexer.columnNumber
   */
  get columnNumber() {
    return this.mTokenOffset - this.mTokenLineOffset;
  },

  /**
   * @see CSSLexer.performEOFFixup
   */
  performEOFFixup: function (aInputString, aPreserveBackslash) {
    let result = aInputString;

    let eofChars = this.mEOFCharacters;

    if (aPreserveBackslash &&
        (eofChars & (eEOFCharacters_DropBackslash |
                     eEOFCharacters_ReplacementChar)) != 0) {
      eofChars &= ~(eEOFCharacters_DropBackslash |
                    eEOFCharacters_ReplacementChar);
      result += "\\";
    }

    if ((eofChars & eEOFCharacters_DropBackslash) != 0 &&
        result.length > 0 && result.endsWith("\\")) {
      result = result.slice(0, -1);
    }

    let extra = [];
    this.AppendImpliedEOFCharacters(eofChars, extra);
    let asString = String.fromCharCode.apply(null, extra);

    return result + asString;
  },

  /**
   * @see CSSLexer.nextToken
   */
  nextToken: function () {
    let token = {};
    if (!this.Next(token)) {
      return null;
    }

    let resultToken = {};
    resultToken.tokenType = token.mType;
    resultToken.startOffset = this.mTokenOffset;
    resultToken.endOffset = this.mOffset;

    let constructText = () => {
      return String.fromCharCode.apply(null, token.mIdent);
    };

    switch (token.mType) {
      case eCSSToken_Whitespace:
        break;

      case eCSSToken_Ident:
      case eCSSToken_Function:
      case eCSSToken_AtKeyword:
      case eCSSToken_ID:
      case eCSSToken_Hash:
        resultToken.text = constructText();
        break;

      case eCSSToken_Dimension:
        resultToken.text = constructText();
        /* Fall through.  */
      case eCSSToken_Number:
      case eCSSToken_Percentage:
        resultToken.number = token.mNumber;
        resultToken.hasSign = token.mHasSign;
        resultToken.isInteger = token.mIntegerValid;
        break;

      case eCSSToken_String:
      case eCSSToken_Bad_String:
      case eCSSToken_URL:
      case eCSSToken_Bad_URL:
        resultToken.text = constructText();
        /* Don't bother emitting the delimiter, as it is readily extracted
           from the source string when needed.  */
        break;

      case eCSSToken_Symbol:
        resultToken.text = String.fromCharCode(token.mSymbol);
        break;

      case eCSSToken_Includes:
      case eCSSToken_Dashmatch:
      case eCSSToken_Beginsmatch:
      case eCSSToken_Endsmatch:
      case eCSSToken_Containsmatch:
      case eCSSToken_URange:
        break;

      case eCSSToken_Comment:
      case eCSSToken_HTMLComment:
        /* The comment text is easily extracted from the source string,
           and is rarely useful.  */
        break;
    }

    return resultToken;
  },

  /**
   * Return the raw UTF-16 code unit at position |this.mOffset + n| within
   * the read buffer.  If that is beyond the end of the buffer, returns
   * -1 to indicate end of input.
   */
  Peek: function (n = 0) {
    if (this.mOffset + n >= this.mCount) {
      return -1;
    }
    return this.mBuffer.charCodeAt(this.mOffset + n);
  },

  /**
   * Advance |this.mOffset| over |n| code units.  Advance(0) is a no-op.
   * If |n| is greater than the distance to end of input, will silently
   * stop at the end.  May not be used to advance over a line boundary;
   * AdvanceLine() must be used instead.
   */
  Advance: function (n = 1) {
    if (this.mOffset + n >= this.mCount || this.mOffset + n < this.mOffset) {
      this.mOffset = this.mCount;
    } else {
      this.mOffset += n;
    }
  },

  /**
   * Advance |this.mOffset| over a line boundary.
   */
  AdvanceLine: function () {
    // Advance over \r\n as a unit.
    if (this.mBuffer.charCodeAt(this.mOffset) == CARRIAGE_RETURN &&
        this.mOffset + 1 < this.mCount &&
        this.mBuffer.charCodeAt(this.mOffset + 1) == LINE_FEED) {
      this.mOffset += 2;
    } else {
      this.mOffset += 1;
    }
    // 0 is a magical line number meaning that we don't know (i.e., script)
    if (this.mLineNumber != 0) {
      this.mLineNumber++;
    }
    this.mLineOffset = this.mOffset;
  },

  /**
   * Skip over a sequence of whitespace characters (vertical or
   * horizontal) starting at the current read position.
   */
  SkipWhitespace: function () {
    for (;;) {
      let ch = this.Peek();
      if (!IsWhitespace(ch)) { // EOF counts as non-whitespace
        break;
      }
      if (IsVertSpace(ch)) {
        this.AdvanceLine();
      } else {
        this.Advance();
      }
    }
  },

  /**
   * Skip over one CSS comment starting at the current read position.
   */
  SkipComment: function () {
    this.Advance(2);
    for (;;) {
      let ch = this.Peek();
      if (ch < 0) {
        this.SetEOFCharacters(eEOFCharacters_Asterisk | eEOFCharacters_Slash);
        return;
      }
      if (ch == ASTERISK) {
        this.Advance();
        ch = this.Peek();
        if (ch < 0) {
          this.SetEOFCharacters(eEOFCharacters_Slash);
          return;
        }
        if (ch == SOLIDUS) {
          this.Advance();
          return;
        }
      } else if (IsVertSpace(ch)) {
        this.AdvanceLine();
      } else {
        this.Advance();
      }
    }
  },

  /**
   * If there is a valid escape sequence starting at the current read
   * position, consume it, decode it, append the result to |aOutput|,
   * and return true.  Otherwise, consume nothing, leave |aOutput|
   * unmodified, and return false.  If |aInString| is true, accept the
   * additional form of escape sequence allowed within string-like tokens.
   */
  GatherEscape: function (aOutput, aInString) {
    let ch = this.Peek(1);
    if (ch < 0) {
      // If we are in a string (or a url() containing a string), we want to drop
      // the backslash on the floor.  Otherwise, we want to treat it as a U+FFFD
      // character.
      this.Advance();
      if (aInString) {
        this.SetEOFCharacters(eEOFCharacters_DropBackslash);
      } else {
        aOutput.push(UCS2_REPLACEMENT_CHAR);
        this.SetEOFCharacters(eEOFCharacters_ReplacementChar);
      }
      return true;
    }
    if (IsVertSpace(ch)) {
      if (aInString) {
        // In strings (and in url() containing a string), escaped
        // newlines are completely removed, to allow splitting over
        // multiple lines.
        this.Advance();
        this.AdvanceLine();
        return true;
      }
      // Outside of strings, backslash followed by a newline is not an escape.
      return false;
    }

    if (!IsHexDigit(ch)) {
      // "Any character (except a hexadecimal digit, linefeed, carriage
      // return, or form feed) can be escaped with a backslash to remove
      // its special meaning." -- CSS2.1 section 4.1.3
      this.Advance(2);
      if (ch == 0) {
        aOutput.push(UCS2_REPLACEMENT_CHAR);
      } else {
        aOutput.push(ch);
      }
      return true;
    }

    // "[at most six hexadecimal digits following a backslash] stand
    // for the ISO 10646 character with that number, which must not be
    // zero. (It is undefined in CSS 2.1 what happens if a style sheet
    // does contain a character with Unicode codepoint zero.)"
    //   -- CSS2.1 section 4.1.3

    // At this point we know we have \ followed by at least one
    // hexadecimal digit, therefore the escape sequence is valid and we
    // can go ahead and consume the backslash.
    this.Advance();
    let val = 0;
    let i = 0;
    do {
      val = val * 16 + HexDigitValue(ch);
      i++;
      this.Advance();
      ch = this.Peek();
    } while (i < 6 && IsHexDigit(ch));

    // "Interpret the hex digits as a hexadecimal number. If this
    // number is zero, or is greater than the maximum allowed
    // codepoint, return U+FFFD REPLACEMENT CHARACTER" -- CSS Syntax
    // Level 3
    if (val == 0) {
      aOutput.push(UCS2_REPLACEMENT_CHAR);
    } else {
      aOutput.push(ensureValidChar(val));
    }

    // Consume exactly one whitespace character after a
    // hexadecimal escape sequence.
    if (IsVertSpace(ch)) {
      this.AdvanceLine();
    } else if (IsHorzSpace(ch)) {
      this.Advance();
    }
    return true;
  },

  /**
   * Consume a run of "text" beginning with the current read position,
   * consisting of characters in the class |aClass| (which must be a
   * suitable argument to IsOpenCharClass) plus escape sequences.
   * Append the text to |aText|, after decoding escape sequences.
   *
   * Returns true if at least one character was appended to |aText|,
   * false otherwise.
   */
  GatherText: function (aClass, aText) {
    let start = this.mOffset;
    let inString = aClass == IS_STRING;

    for (;;) {
      // Consume runs of unescaped characters in one go.
      let n = this.mOffset;
      while (n < this.mCount && IsOpenCharClass(this.mBuffer.charCodeAt(n),
                                                aClass)) {
        n++;
      }
      if (n > this.mOffset) {
        let substr = this.mBuffer.slice(this.mOffset, n);
        Array.prototype.push.apply(aText, stringToCodes(substr));
        this.mOffset = n;
      }
      if (n == this.mCount) {
        break;
      }

      let ch = this.Peek();
      if (ch == 0) {
        this.Advance();
        aText.push(UCS2_REPLACEMENT_CHAR);
        continue;
      }

      if (ch != REVERSE_SOLIDUS) {
        break;
      }
      if (!this.GatherEscape(aText, inString)) {
        break;
      }
    }

    return this.mOffset > start;
  },

  /**
   * Scan an Ident token.  This also handles Function and URL tokens,
   * both of which begin indistinguishably from an identifier.  It can
   * produce a Symbol token when an apparent identifier actually led
   * into an invalid escape sequence.
   */
  ScanIdent: function (aToken) {
    if (!this.GatherText(IS_IDCHAR, aToken.mIdent)) {
      aToken.mSymbol = this.Peek();
      this.Advance();
      return true;
    }

    if (this.Peek() != LEFT_PARENTHESIS) {
      aToken.mType = eCSSToken_Ident;
      return true;
    }

    this.Advance();
    aToken.mType = eCSSToken_Function;

    let asString = String.fromCharCode.apply(null, aToken.mIdent);
    if (asString.toLowerCase() === "url") {
      this.NextURL(aToken);
    }
    return true;
  },

  /**
   * Scan an AtKeyword token.  Also handles production of Symbol when
   * an '@' is not followed by an identifier.
   */
  ScanAtKeyword: function (aToken) {
    // Fall back for when '@' isn't followed by an identifier.
    aToken.mSymbol = COMMERCIAL_AT;
    this.Advance();

    let ch = this.Peek();
    if (StartsIdent(ch, this.Peek(1))) {
      if (this.GatherText(IS_IDCHAR, aToken.mIdent)) {
        aToken.mType = eCSSToken_AtKeyword;
      }
    }
    return true;
  },

  /**
   * Scan a Hash token.  Handles the distinction between eCSSToken_ID
   * and eCSSToken_Hash, and handles production of Symbol when a '#'
   * is not followed by identifier characters.
   */
  ScanHash: function (aToken) {
    // Fall back for when '#' isn't followed by identifier characters.
    aToken.mSymbol = NUMBER_SIGN;
    this.Advance();

    let ch = this.Peek();
    if (IsIdentChar(ch) || ch == REVERSE_SOLIDUS) {
      let type =
          StartsIdent(ch, this.Peek(1)) ? eCSSToken_ID : eCSSToken_Hash;
      aToken.mIdent.length = 0;
      if (this.GatherText(IS_IDCHAR, aToken.mIdent)) {
        aToken.mType = type;
      }
    }

    return true;
  },

  /**
   * Scan a Number, Percentage, or Dimension token (all of which begin
   * like a Number).  Can produce a Symbol when a '.' is not followed by
   * digits, or when '+' or '-' are not followed by either a digit or a
   * '.' and then a digit.  Can also produce a HTMLComment when it
   * encounters '-->'.
   */
  ScanNumber: function (aToken) {
    let c = this.Peek();

    // Sign of the mantissa (-1 or 1).
    let sign = c == HYPHEN_MINUS ? -1 : 1;
    // Absolute value of the integer part of the mantissa.  This is a double so
    // we don't run into overflow issues for consumers that only care about our
    // floating-point value while still being able to express the full int32_t
    // range for consumers who want integers.
    let intPart = 0;
    // Fractional part of the mantissa.  This is a double so that when
    // we convert to float at the end we'll end up rounding to nearest
    // float instead of truncating down (as we would if fracPart were
    // a float and we just effectively lost the last several digits).
    let fracPart = 0;
    // Absolute value of the power of 10 that we should multiply by
    // (only relevant for numbers in scientific notation).  Has to be
    // a signed integer, because multiplication of signed by unsigned
    // converts the unsigned to signed, so if we plan to actually
    // multiply by expSign...
    let exponent = 0;
    // Sign of the exponent.
    let expSign = 1;

    aToken.mHasSign = (c == PLUS_SIGN || c == HYPHEN_MINUS);
    if (aToken.mHasSign) {
      this.Advance();
      c = this.Peek();
    }

    let gotDot = (c == FULL_STOP);

    if (!gotDot) {
      // Scan the integer part of the mantissa.
      do {
        intPart = 10 * intPart + DecimalDigitValue(c);
        this.Advance();
        c = this.Peek();
      } while (IsDigit(c));

      gotDot = (c == FULL_STOP) && IsDigit(this.Peek(1));
    }

    if (gotDot) {
      // Scan the fractional part of the mantissa.
      this.Advance();
      c = this.Peek();
      // Power of ten by which we need to divide our next digit
      let divisor = 10;
      do {
        fracPart += DecimalDigitValue(c) / divisor;
        divisor *= 10;
        this.Advance();
        c = this.Peek();
      } while (IsDigit(c));
    }

    let gotE = false;
    if (c == LATIN_SMALL_LETTER_E || c == LATIN_CAPITAL_LETTER_E) {
      let expSignChar = this.Peek(1);
      let nextChar = this.Peek(2);
      if (IsDigit(expSignChar) ||
          ((expSignChar == HYPHEN_MINUS || expSignChar == PLUS_SIGN) &&
           IsDigit(nextChar))) {
        gotE = true;
        if (expSignChar == HYPHEN_MINUS) {
          expSign = -1;
        }
        this.Advance(); // consumes the E
        if (expSignChar == HYPHEN_MINUS || expSignChar == PLUS_SIGN) {
          this.Advance();
          c = nextChar;
        } else {
          c = expSignChar;
        }
        do {
          exponent = 10 * exponent + DecimalDigitValue(c);
          this.Advance();
          c = this.Peek();
        } while (IsDigit(c));
      }
    }

    let type = eCSSToken_Number;

    // Set mIntegerValid for all cases (except %, below) because we need
    // it for the "2n" in :nth-child(2n).
    aToken.mIntegerValid = false;

    // Time to reassemble our number.
    // Do all the math in double precision so it's truncated only once.
    let value = sign * (intPart + fracPart);
    if (gotE) {
      // Explicitly cast expSign*exponent to double to avoid issues with
      // overloaded pow() on Windows.
      value *= Math.pow(10.0, expSign * exponent);
    } else if (!gotDot) {
      // Clamp values outside of integer range.
      if (sign > 0) {
        aToken.mInteger = Math.min(intPart, Number.MAX_SAFE_INTEGER);
      } else {
        aToken.mInteger = Math.max(-intPart, Number.MIN_SAFE_INTEGER);
      }
      aToken.mIntegerValid = true;
    }

    let ident = aToken.mIdent;

    // Check for Dimension and Percentage tokens.
    if (c >= 0) {
      if (StartsIdent(c, this.Peek(1))) {
        if (this.GatherText(IS_IDCHAR, ident)) {
          type = eCSSToken_Dimension;
        }
      } else if (c == PERCENT_SIGN) {
        this.Advance();
        type = eCSSToken_Percentage;
        value = value / 100.0;
        aToken.mIntegerValid = false;
      }
    }
    aToken.mNumber = value;
    aToken.mType = type;
    return true;
  },

  /**
   * Scan a string constant ('foo' or "foo").  Will always produce
   * either a String or a Bad_String token; the latter occurs when the
   * close quote is missing.  Always returns true (for convenience in Next()).
   */
  ScanString: function (aToken) {
    let aStop = this.Peek();
    aToken.mType = eCSSToken_String;
    aToken.mSymbol = aStop; // Remember how it's quoted.
    this.Advance();

    for (;;) {
      this.GatherText(IS_STRING, aToken.mIdent);

      let ch = this.Peek();
      if (ch == -1) {
        this.AddEOFCharacters(aStop == QUOTATION_MARK ?
                              eEOFCharacters_DoubleQuote :
                              eEOFCharacters_SingleQuote);
        break; // EOF ends a string token with no error.
      }
      if (ch == aStop) {
        this.Advance();
        break;
      }
      // Both " and ' are excluded from IS_STRING.
      if (ch == QUOTATION_MARK || ch == APOSTROPHE) {
        aToken.mIdent.push(ch);
        this.Advance();
        continue;
      }

      aToken.mType = eCSSToken_Bad_String;
      break;
    }
    return true;
  },

  /**
   * Scan a unicode-range token.  These match the regular expression
   *
   *     u\+[0-9a-f?]{1,6}(-[0-9a-f]{1,6})?
   *
   * However, some such tokens are "invalid".  There are three valid forms:
   *
   *     u+[0-9a-f]{x}              1 <= x <= 6
   *     u+[0-9a-f]{x}\?{y}         1 <= x+y <= 6
   *     u+[0-9a-f]{x}-[0-9a-f]{y}  1 <= x <= 6, 1 <= y <= 6
   *
   * All unicode-range tokens have their text recorded in mIdent; valid ones
   * are also decoded into mInteger and mInteger2, and mIntegerValid is set.
   * Note that this does not validate the numeric range, only the syntactic
   * form.
   */
  ScanURange: function (aResult) {
    let intro1 = this.Peek();
    let intro2 = this.Peek(1);
    let ch = this.Peek(2);

    aResult.mIdent.push(intro1);
    aResult.mIdent.push(intro2);
    this.Advance(2);

    let valid = true;
    let haveQues = false;
    let low = 0;
    let high = 0;
    let i = 0;

    do {
      aResult.mIdent.push(ch);
      if (IsHexDigit(ch)) {
        if (haveQues) {
          valid = false; // All question marks should be at the end.
        }
        low = low * 16 + HexDigitValue(ch);
        high = high * 16 + HexDigitValue(ch);
      } else {
        haveQues = true;
        low = low * 16 + 0x0;
        high = high * 16 + 0xF;
      }

      i++;
      this.Advance();
      ch = this.Peek();
    } while (i < 6 && (IsHexDigit(ch) || ch == QUESTION_MARK));

    if (ch == HYPHEN_MINUS && IsHexDigit(this.Peek(1))) {
      if (haveQues) {
        valid = false;
      }

      aResult.mIdent.push(ch);
      this.Advance();
      ch = this.Peek();
      high = 0;
      i = 0;
      do {
        aResult.mIdent.push(ch);
        high = high * 16 + HexDigitValue(ch);

        i++;
        this.Advance();
        ch = this.Peek();
      } while (i < 6 && IsHexDigit(ch));
    }

    aResult.mInteger = low;
    aResult.mInteger2 = high;
    aResult.mIntegerValid = valid;
    aResult.mType = eCSSToken_URange;
    return true;
  },

  SetEOFCharacters: function (aEOFCharacters) {
    this.mEOFCharacters = aEOFCharacters;
  },

  AddEOFCharacters: function (aEOFCharacters) {
    this.mEOFCharacters = this.mEOFCharacters | aEOFCharacters;
  },

  AppendImpliedEOFCharacters: function (aEOFCharacters, aResult) {
    // First, ignore eEOFCharacters_DropBackslash.
    let c = aEOFCharacters >> 1;

    // All of the remaining EOFCharacters bits represent appended characters,
    // and the bits are in the order that they need appending.
    for (let p of kImpliedEOFCharacters) {
      if (c & 1) {
        aResult.push(p);
      }
      c >>= 1;
    }
  },

  /**
   * Consume the part of an URL token after the initial 'url('.  Caller
   * is assumed to have consumed 'url(' already.  Will always produce
   * either an URL or a Bad_URL token.
   *
   * Exposed for use by nsCSSParser::ParseMozDocumentRule, which applies
   * the special lexical rules for URL tokens in a nonstandard context.
   */
  NextURL: function (aToken) {
    this.SkipWhitespace();

    // aToken.mIdent may be "url" at this point; clear that out
    aToken.mIdent.length = 0;

    let hasString = false;
    let ch = this.Peek();
    // Do we have a string?
    if (ch == QUOTATION_MARK || ch == APOSTROPHE) {
      this.ScanString(aToken);
      if (aToken.mType == eCSSToken_Bad_String) {
        aToken.mType = eCSSToken_Bad_URL;
        return;
      }
      hasString = true;
    } else {
      // Otherwise, this is the start of a non-quoted url (which may be empty).
      aToken.mSymbol = 0;
      this.GatherText(IS_URL_CHAR, aToken.mIdent);
    }

    // Consume trailing whitespace and then look for a close parenthesis.
    this.SkipWhitespace();
    ch = this.Peek();
    // ch can be less than zero indicating EOF
    if (ch < 0 || ch == RIGHT_PARENTHESIS) {
      this.Advance();
      aToken.mType = eCSSToken_URL;
      if (ch < 0) {
        this.AddEOFCharacters(eEOFCharacters_CloseParen);
      }
    } else {
      aToken.mType = eCSSToken_Bad_URL;
      if (!hasString) {
        // Consume until before the next right parenthesis, which follows
        // how <bad-url-token> is consumed in CSS Syntax 3 spec.
        // Note that, we only do this when "url(" is not followed by a
        // string, because in the spec, "url(" followed by a string is
        // handled as a url function rather than a <url-token>, so the
        // rest of content before ")" should be consumed in balance,
        // which will be done by the parser.
        // The closing ")" is not consumed here. It is left to the parser
        // so that the parser can handle both cases.
        do {
          if (IsVertSpace(ch)) {
            this.AdvanceLine();
          } else {
            this.Advance();
          }
          ch = this.Peek();
        } while (ch >= 0 && ch != RIGHT_PARENTHESIS);
      }
    }
  },

  /**
   * Primary scanner entry point.  Consume one token and fill in
   * |aToken| accordingly.  Will skip over any number of comments first,
   * and will also skip over rather than return whitespace and comment
   * tokens, depending on the value of |aSkip|.
   *
   * Returns true if it successfully consumed a token, false if EOF has
   * been reached.  Will always advance the current read position by at
   * least one character unless called when already at EOF.
   */
  Next: function (aToken, aSkip) {
    let ch;

    // do this here so we don't have to do it in dozens of other places
    aToken.mIdent = [];
    aToken.mType = eCSSToken_Symbol;

    this.mTokenOffset = this.mOffset;
    this.mTokenLineOffset = this.mLineOffset;
    this.mTokenLineNumber = this.mLineNumber;

    ch = this.Peek();
    if (IsWhitespace(ch)) {
      this.SkipWhitespace();
      aToken.mType = eCSSToken_Whitespace;
      return true;
    }
    if (ch == SOLIDUS && // !IsSVGMode() &&
        this.Peek(1) == ASTERISK) {
      this.SkipComment();
      aToken.mType = eCSSToken_Comment;
      return true;
    }

    // EOF
    if (ch < 0) {
      return false;
    }

    // 'u' could be UNICODE-RANGE or an identifier-family token
    if (ch == LATIN_SMALL_LETTER_U || ch == LATIN_CAPITAL_LETTER_U) {
      let c2 = this.Peek(1);
      let c3 = this.Peek(2);
      if (c2 == PLUS_SIGN && (IsHexDigit(c3) || c3 == QUESTION_MARK)) {
        return this.ScanURange(aToken);
      }
      return this.ScanIdent(aToken);
    }

    // identifier family
    if (IsIdentStart(ch)) {
      return this.ScanIdent(aToken);
    }

    // number family
    if (IsDigit(ch)) {
      return this.ScanNumber(aToken);
    }

    if (ch == FULL_STOP && IsDigit(this.Peek(1))) {
      return this.ScanNumber(aToken);
    }

    if (ch == PLUS_SIGN) {
      let c2 = this.Peek(1);
      if (IsDigit(c2) || (c2 == FULL_STOP && IsDigit(this.Peek(2)))) {
        return this.ScanNumber(aToken);
      }
    }

    // HYPHEN_MINUS can start an identifier-family token, a number-family token,
    // or an HTML-comment
    if (ch == HYPHEN_MINUS) {
      let c2 = this.Peek(1);
      let c3 = this.Peek(2);
      if (IsIdentStart(c2) || (c2 == HYPHEN_MINUS && c3 != GREATER_THAN_SIGN)) {
        return this.ScanIdent(aToken);
      }
      if (IsDigit(c2) || (c2 == FULL_STOP && IsDigit(c3))) {
        return this.ScanNumber(aToken);
      }
      if (c2 == HYPHEN_MINUS && c3 == GREATER_THAN_SIGN) {
        this.Advance(3);
        aToken.mType = eCSSToken_HTMLComment;
        aToken.mIdent = stringToCodes("-->");
        return true;
      }
    }

    // the other HTML-comment token
    if (ch == LESS_THAN_SIGN &&
        this.Peek(1) == EXCLAMATION_MARK &&
        this.Peek(2) == HYPHEN_MINUS &&
        this.Peek(3) == HYPHEN_MINUS) {
      this.Advance(4);
      aToken.mType = eCSSToken_HTMLComment;
      aToken.mIdent = stringToCodes("<!--");
      return true;
    }

    // AT_KEYWORD
    if (ch == COMMERCIAL_AT) {
      return this.ScanAtKeyword(aToken);
    }

    // HASH
    if (ch == NUMBER_SIGN) {
      return this.ScanHash(aToken);
    }

    // STRING
    if (ch == QUOTATION_MARK || ch == APOSTROPHE) {
      return this.ScanString(aToken);
    }

    // Match operators: ~= |= ^= $= *=
    let opType = MatchOperatorType(ch);
    if (opType != eCSSToken_Symbol && this.Peek(1) == EQUALS_SIGN) {
      aToken.mType = opType;
      this.Advance(2);
      return true;
    }

    // Otherwise, a symbol (DELIM).
    aToken.mSymbol = ch;
    this.Advance();
    return true;
  },
};

/**
 * Create and return a new CSS lexer, conforming to the @see CSSLexer
 * webidl interface.
 *
 * @param {String} input the CSS text to lex
 * @return {CSSLexer} the new lexer
 */
function getCSSLexer(input) {
  return new Scanner(input);
}

exports.getCSSLexer = getCSSLexer;
PK
!<��	�ưư<chrome/devtools/modules/devtools/shared/css/parsing-utils.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=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/. */

// This file holds various CSS parsing and rewriting utilities.
// Some entry points of note are:
// parseDeclarations - parse a CSS rule into declarations
// RuleRewriter - rewrite CSS rule text
// parsePseudoClassesAndAttributes - parse selector and extract
//     pseudo-classes
// parseSingleValue - parse a single CSS property value

"use strict";

loader.lazyRequireGetter(this, "CSS_ANGLEUNIT",
  "devtools/shared/css/properties-db", true);

const promise = require("promise");
const {getCSSLexer} = require("devtools/shared/css/lexer");

const SELECTOR_ATTRIBUTE = exports.SELECTOR_ATTRIBUTE = 1;
const SELECTOR_ELEMENT = exports.SELECTOR_ELEMENT = 2;
const SELECTOR_PSEUDO_CLASS = exports.SELECTOR_PSEUDO_CLASS = 3;

// Used to test whether a newline appears anywhere in some text.
const NEWLINE_RX = /[\r\n]/;
// Used to test whether a bit of text starts an empty comment, either
// an "ordinary" /* ... */ comment, or a "heuristic bypass" comment
// like /*! ... */.
const EMPTY_COMMENT_START_RX = /^\/\*!?[ \r\n\t\f]*$/;
// Used to test whether a bit of text ends an empty comment.
const EMPTY_COMMENT_END_RX = /^[ \r\n\t\f]*\*\//;
// Used to test whether a string starts with a blank line.
const BLANK_LINE_RX = /^[ \t]*(?:\r\n|\n|\r|\f|$)/;

// When commenting out a declaration, we put this character into the
// comment opener so that future parses of the commented text know to
// bypass the property name validity heuristic.
const COMMENT_PARSING_HEURISTIC_BYPASS_CHAR = "!";

/**
 * A generator function that lexes a CSS source string, yielding the
 * CSS tokens.  Comment tokens are dropped.
 *
 * @param {String} CSS source string
 * @yield {CSSToken} The next CSSToken that is lexed
 * @see CSSToken for details about the returned tokens
 */
function* cssTokenizer(string) {
  let lexer = getCSSLexer(string);
  while (true) {
    let token = lexer.nextToken();
    if (!token) {
      break;
    }
    // None of the existing consumers want comments.
    if (token.tokenType !== "comment") {
      yield token;
    }
  }
}

/**
 * Pass |string| to the CSS lexer and return an array of all the
 * returned tokens.  Comment tokens are not included.  In addition to
 * the usual information, each token will have starting and ending
 * line and column information attached.  Specifically, each token
 * has an additional "loc" attribute.  This attribute is an object
 * of the form {line: L, column: C}.  Lines and columns are both zero
 * based.
 *
 * It's best not to add new uses of this function.  In general it is
 * simpler and better to use the CSSToken offsets, rather than line
 * and column.  Also, this function lexes the entire input string at
 * once, rather than lazily yielding a token stream.  Use
 * |cssTokenizer| or |getCSSLexer| instead.
 *
 * @param{String} string The input string.
 * @return {Array} An array of tokens (@see CSSToken) that have
 *        line and column information.
 */
function cssTokenizerWithLineColumn(string) {
  let lexer = getCSSLexer(string);
  let result = [];
  let prevToken = undefined;
  while (true) {
    let token = lexer.nextToken();
    let lineNumber = lexer.lineNumber;
    let columnNumber = lexer.columnNumber;

    if (prevToken) {
      prevToken.loc.end = {
        line: lineNumber,
        column: columnNumber
      };
    }

    if (!token) {
      break;
    }

    if (token.tokenType === "comment") {
      // We've already dealt with the previous token's location.
      prevToken = undefined;
    } else {
      let startLoc = {
        line: lineNumber,
        column: columnNumber
      };
      token.loc = {start: startLoc};

      result.push(token);
      prevToken = token;
    }
  }

  return result;
}

/**
 * Escape a comment body.  Find the comment start and end strings in a
 * string and inserts backslashes so that the resulting text can
 * itself be put inside a comment.
 *
 * @param {String} inputString
 *                 input string
 * @return {String} the escaped result
 */
function escapeCSSComment(inputString) {
  let result = inputString.replace(/\/(\\*)\*/g, "/\\$1*");
  return result.replace(/\*(\\*)\//g, "*\\$1/");
}

/**
 * Un-escape a comment body.  This undoes any comment escaping that
 * was done by escapeCSSComment.  That is, given input like "/\*
 * comment *\/", it will strip the backslashes.
 *
 * @param {String} inputString
 *                 input string
 * @return {String} the un-escaped result
 */
function unescapeCSSComment(inputString) {
  let result = inputString.replace(/\/\\(\\*)\*/g, "/$1*");
  return result.replace(/\*\\(\\*)\//g, "*$1/");
}

/**
 * A helper function for @see parseDeclarations that handles parsing
 * of comment text.  This wraps a recursive call to parseDeclarations
 * with the processing needed to ensure that offsets in the result
 * refer back to the original, unescaped, input string.
 *
 * @param {Function} isCssPropertyKnown
 *        A function to check if the CSS property is known. This is either an
 *        internal server function or from the CssPropertiesFront.
 * @param {String} commentText The text of the comment, without the
 *                             delimiters.
 * @param {Number} startOffset The offset of the comment opener
 *                             in the original text.
 * @param {Number} endOffset The offset of the comment closer
 *                           in the original text.
 * @return {array} Array of declarations of the same form as returned
 *                 by parseDeclarations.
 */
function parseCommentDeclarations(isCssPropertyKnown, commentText, startOffset,
                                  endOffset) {
  let commentOverride = false;
  if (commentText === "") {
    return [];
  } else if (commentText[0] === COMMENT_PARSING_HEURISTIC_BYPASS_CHAR) {
    // This is the special sign that the comment was written by
    // rewriteDeclarations and so we should bypass the usual
    // heuristic.
    commentOverride = true;
    commentText = commentText.substring(1);
  }

  let rewrittenText = unescapeCSSComment(commentText);

  // We might have rewritten an embedded comment.  For example
  // /\* ... *\/ would turn into /* ... */.
  // This rewriting is necessary for proper lexing, but it means
  // that the offsets we get back can be off.  So now we compute
  // a map so that we can rewrite offsets later.  The map is the same
  // length as |rewrittenText| and tells us how to map an index
  // into |rewrittenText| to an index into |commentText|.
  //
  // First, we find the location of each comment starter or closer in
  // |rewrittenText|.  At these spots we put a 1 into |rewrites|.
  // Then we walk the array again, using the elements to compute a
  // delta, which we use to make the final mapping.
  //
  // Note we allocate one extra entry because we can see an ending
  // offset that is equal to the length.
  let rewrites = new Array(rewrittenText.length + 1).fill(0);

  let commentRe = /\/\\*\*|\*\\*\//g;
  while (true) {
    let matchData = commentRe.exec(rewrittenText);
    if (!matchData) {
      break;
    }
    rewrites[matchData.index] = 1;
  }

  let delta = 0;
  for (let i = 0; i <= rewrittenText.length; ++i) {
    delta += rewrites[i];
    // |startOffset| to add the offset from the comment starter, |+2|
    // for the length of the "/*", then |i| and |delta| as described
    // above.
    rewrites[i] = startOffset + 2 + i + delta;
    if (commentOverride) {
      ++rewrites[i];
    }
  }

  // Note that we pass "false" for parseComments here.  It doesn't
  // seem worthwhile to support declarations in comments-in-comments
  // here, as there's no way to generate those using the tools, and
  // users would be crazy to write such things.
  let newDecls = parseDeclarationsInternal(isCssPropertyKnown, rewrittenText,
                                           false, true, commentOverride);
  for (let decl of newDecls) {
    decl.offsets[0] = rewrites[decl.offsets[0]];
    decl.offsets[1] = rewrites[decl.offsets[1]];
    decl.colonOffsets[0] = rewrites[decl.colonOffsets[0]];
    decl.colonOffsets[1] = rewrites[decl.colonOffsets[1]];
    decl.commentOffsets = [startOffset, endOffset];
  }
  return newDecls;
}

/**
 * A helper function for parseDeclarationsInternal that creates a new
 * empty declaration.
 *
 * @return {object} an empty declaration of the form returned by
 *                  parseDeclarations
 */
function getEmptyDeclaration() {
  return {name: "", value: "", priority: "",
          terminator: "",
          offsets: [undefined, undefined],
          colonOffsets: false};
}

/**
 * Like trim, but only trims CSS-allowed whitespace.
 */
function cssTrim(str) {
  let match = /^[ \t\r\n\f]*(.*?)[ \t\r\n\f]*$/.exec(str);
  if (match) {
    return match[1];
  }
  return str;
}

/**
 * Like trimRight, but only trims CSS-allowed whitespace.
 */
function cssTrimRight(str) {
  let match = /^(.*?)[ \t\r\n\f]*$/.exec(str);
  if (match) {
    return match[1];
  }
  return str;
}

/**
 * A helper function that does all the parsing work for
 * parseDeclarations.  This is separate because it has some arguments
 * that don't make sense in isolation.
 *
 * The return value and arguments are like parseDeclarations, with
 * these additional arguments.
 *
 * @param {Function} isCssPropertyKnown
 *        Function to check if the CSS property is known.
 * @param {Boolean} inComment
 *        If true, assume that this call is parsing some text
 *        which came from a comment in another declaration.
 *        In this case some heuristics are used to avoid parsing
 *        text which isn't obviously a series of declarations.
 * @param {Boolean} commentOverride
 *        This only makes sense when inComment=true.
 *        When true, assume that the comment was generated by
 *        rewriteDeclarations, and skip the usual name-checking
 *        heuristic.
 */
function parseDeclarationsInternal(isCssPropertyKnown, inputString,
                                   parseComments, inComment, commentOverride) {
  if (inputString === null || inputString === undefined) {
    throw new Error("empty input string");
  }

  let lexer = getCSSLexer(inputString);

  let declarations = [getEmptyDeclaration()];
  let lastProp = declarations[0];

  let current = "", hasBang = false;
  while (true) {
    let token = lexer.nextToken();
    if (!token) {
      break;
    }

    // Ignore HTML comment tokens (but parse anything they might
    // happen to surround).
    if (token.tokenType === "htmlcomment") {
      continue;
    }

    // Update the start and end offsets of the declaration, but only
    // when we see a significant token.
    if (token.tokenType !== "whitespace" && token.tokenType !== "comment") {
      if (lastProp.offsets[0] === undefined) {
        lastProp.offsets[0] = token.startOffset;
      }
      lastProp.offsets[1] = token.endOffset;
    } else if (lastProp.name && !current && !hasBang &&
               !lastProp.priority && lastProp.colonOffsets[1]) {
      // Whitespace appearing after the ":" is attributed to it.
      lastProp.colonOffsets[1] = token.endOffset;
    }

    if (token.tokenType === "symbol" && token.text === ":") {
      if (!lastProp.name) {
        // Set the current declaration name if there's no name yet
        lastProp.name = cssTrim(current);
        lastProp.colonOffsets = [token.startOffset, token.endOffset];
        current = "";
        hasBang = false;

        // When parsing a comment body, if the left-hand-side is not a
        // valid property name, then drop it and stop parsing.
        if (inComment && !commentOverride &&
            !isCssPropertyKnown(lastProp.name)) {
          lastProp.name = null;
          break;
        }
      } else {
        // Otherwise, just append ':' to the current value (declaration value
        // with colons)
        current += ":";
      }
    } else if (token.tokenType === "symbol" && token.text === ";") {
      lastProp.terminator = "";
      // When parsing a comment, if the name hasn't been set, then we
      // have probably just seen an ordinary semicolon used in text,
      // so drop this and stop parsing.
      if (inComment && !lastProp.name) {
        current = "";
        break;
      }
      lastProp.value = cssTrim(current);
      current = "";
      hasBang = false;
      declarations.push(getEmptyDeclaration());
      lastProp = declarations[declarations.length - 1];
    } else if (token.tokenType === "ident") {
      if (token.text === "important" && hasBang) {
        lastProp.priority = "important";
        hasBang = false;
      } else {
        if (hasBang) {
          current += "!";
        }
        // Re-escape the token to avoid dequoting problems.
        // See bug 1287620.
        current += CSS.escape(token.text);
      }
    } else if (token.tokenType === "symbol" && token.text === "!") {
      hasBang = true;
    } else if (token.tokenType === "whitespace") {
      if (current !== "") {
        current += " ";
      }
    } else if (token.tokenType === "comment") {
      if (parseComments && !lastProp.name && !lastProp.value) {
        let commentText = inputString.substring(token.startOffset + 2,
                                                token.endOffset - 2);
        let newDecls = parseCommentDeclarations(isCssPropertyKnown, commentText,
                                                token.startOffset,
                                                token.endOffset);

        // Insert the new declarations just before the final element.
        let lastDecl = declarations.pop();
        declarations = [...declarations, ...newDecls, lastDecl];
      } else {
        current += " ";
      }
    } else {
      current += inputString.substring(token.startOffset, token.endOffset);
    }
  }

  // Handle whatever trailing properties or values might still be there
  if (current) {
    if (!lastProp.name) {
      // Ignore this case in comments.
      if (!inComment) {
        // Trailing property found, e.g. p1:v1;p2:v2;p3
        lastProp.name = cssTrim(current);
      }
    } else {
      // Trailing value found, i.e. value without an ending ;
      lastProp.value = cssTrim(current);
      let terminator = lexer.performEOFFixup("", true);
      lastProp.terminator = terminator + ";";
      // If the input was unterminated, attribute the remainder to
      // this property.  This avoids some bad behavior when rewriting
      // an unterminated comment.
      if (terminator) {
        lastProp.offsets[1] = inputString.length;
      }
    }
  }

  // Remove declarations that have neither a name nor a value
  declarations = declarations.filter(prop => prop.name || prop.value);

  return declarations;
}

/**
 * Returns an array of CSS declarations given a string.
 * For example, parseDeclarations(isCssPropertyKnown, "width: 1px; height: 1px")
 * would return:
 * [{name:"width", value: "1px"}, {name: "height", "value": "1px"}]
 *
 * The input string is assumed to only contain declarations so { and }
 * characters will be treated as part of either the property or value,
 * depending where it's found.
 *
 * @param {Function} isCssPropertyKnown
 *        A function to check if the CSS property is known. This is either an
 *        internal server function or from the CssPropertiesFront.
 *        that are supported by the server.
 * @param {String} inputString
 *        An input string of CSS
 * @param {Boolean} parseComments
 *        If true, try to parse the contents of comments as well.
 *        A comment will only be parsed if it occurs outside of
 *        the body of some other declaration.
 * @return {Array} an array of objects with the following signature:
 *         [{"name": string, "value": string, "priority": string,
 *           "terminator": string,
 *           "offsets": [start, end], "colonOffsets": [start, end]},
 *          ...]
 *         Here, "offsets" holds the offsets of the start and end
 *         of the declaration text, in a form suitable for use with
 *         String.substring.
 *         "terminator" is a string to use to terminate the declaration,
 *         usually "" to mean no additional termination is needed.
 *         "colonOffsets" holds the start and end locations of the
 *         ":" that separates the property name from the value.
 *         If the declaration appears in a comment, then there will
 *         be an additional {"commentOffsets": [start, end] property
 *         on the object, which will hold the offsets of the start
 *         and end of the enclosing comment.
 */
function parseDeclarations(isCssPropertyKnown, inputString,
                           parseComments = false) {
  return parseDeclarationsInternal(isCssPropertyKnown, inputString,
                                   parseComments, false, false);
}

/**
 * Like @see parseDeclarations, but removes properties that do not
 * have a name.
 */
function parseNamedDeclarations(isCssPropertyKnown, inputString,
                                parseComments = false) {
  return parseDeclarations(isCssPropertyKnown, inputString, parseComments)
         .filter(item => !!item.name);
}

/**
 * Return an object that can be used to rewrite declarations in some
 * source text.  The source text and parsing are handled in the same
 * way as @see parseNamedDeclarations, with |parseComments| being true.
 * Rewriting is done by calling one of the modification functions like
 * setPropertyEnabled.  The returned object has the same interface
 * as @see RuleModificationList.
 *
 * An example showing how to disable the 3rd property in a rule:
 *
 *    let rewriter = new RuleRewriter(isCssPropertyKnown, ruleActor,
 *                                    ruleActor.authoredText);
 *    rewriter.setPropertyEnabled(3, "color", false);
 *    rewriter.apply().then(() => { ... the change is made ... });
 *
 * The exported rewriting methods are |renameProperty|, |setPropertyEnabled|,
 * |createProperty|, |setProperty|, and |removeProperty|.  The |apply|
 * method can be used to send the edited text to the StyleRuleActor;
 * |getDefaultIndentation| is useful for the methods requiring a
 * default indentation value; and |getResult| is useful for testing.
 *
 * Additionally, editing will set the |changedDeclarations| property
 * on this object.  This property has the same form as the |changed|
 * property of the object returned by |getResult|.
 *
 * @param {Function} isCssPropertyKnown
 *        A function to check if the CSS property is known. This is either an
 *        internal server function or from the CssPropertiesFront.
 *        that are supported by the server. Note that if Bug 1222047
 *        is completed then isCssPropertyKnown will not need to be passed in.
 *        The CssProperty front will be able to obtained directly from the
 *        RuleRewriter.
 * @param {StyleRuleFront} rule The style rule to use.  Note that this
 *        is only needed by the |apply| and |getDefaultIndentation| methods;
 *        and in particular for testing it can be |null|.
 * @param {String} inputString The CSS source text to parse and modify.
 * @return {Object} an object that can be used to rewrite the input text.
 */
function RuleRewriter(isCssPropertyKnown, rule, inputString) {
  this.rule = rule;
  this.isCssPropertyKnown = isCssPropertyKnown;

  // Keep track of which any declarations we had to rewrite while
  // performing the requested action.
  this.changedDeclarations = {};

  // If not null, a promise that must be wait upon before |apply| can
  // do its work.
  this.editPromise = null;

  // If the |defaultIndentation| property is set, then it is used;
  // otherwise the RuleRewriter will try to compute the default
  // indentation based on the style sheet's text.  This override
  // facility is for testing.
  this.defaultIndentation = null;

  this.startInitialization(inputString);
}

RuleRewriter.prototype = {
  /**
   * An internal function to initialize the rewriter with a given
   * input string.
   *
   * @param {String} inputString the input to use
   */
  startInitialization: function (inputString) {
    this.inputString = inputString;
    // Whether there are any newlines in the input text.
    this.hasNewLine = /[\r\n]/.test(this.inputString);
    // The declarations.
    this.declarations = parseNamedDeclarations(this.isCssPropertyKnown, this.inputString,
                                               true);
    this.decl = null;
    this.result = null;
  },

  /**
   * An internal function to complete initialization and set some
   * properties for further processing.
   *
   * @param {Number} index The index of the property to modify
   */
  completeInitialization: function (index) {
    if (index < 0) {
      throw new Error("Invalid index " + index + ". Expected positive integer");
    }
    // |decl| is the declaration to be rewritten, or null if there is no
    // declaration corresponding to |index|.
    // |result| is used to accumulate the result text.
    if (index < this.declarations.length) {
      this.decl = this.declarations[index];
      this.result = this.inputString.substring(0, this.decl.offsets[0]);
    } else {
      this.decl = null;
      this.result = this.inputString;
    }
  },

  /**
   * A helper function to compute the indentation of some text.  This
   * examines the rule's existing text to guess the indentation to use;
   * unlike |getDefaultIndentation|, which examines the entire style
   * sheet.
   *
   * @param {String} string the input text
   * @param {Number} offset the offset at which to compute the indentation
   * @return {String} the indentation at the indicated position
   */
  getIndentation: function (string, offset) {
    let originalOffset = offset;
    for (--offset; offset >= 0; --offset) {
      let c = string[offset];
      if (c === "\r" || c === "\n" || c === "\f") {
        return string.substring(offset + 1, originalOffset);
      }
      if (c !== " " && c !== "\t") {
        // Found some non-whitespace character before we found a newline
        // -- let's reset the starting point and keep going, as we saw
        // something on the line before the declaration.
        originalOffset = offset;
      }
    }
    // Ran off the end.
    return "";
  },

  /**
   * Modify a property value to ensure it is "lexically safe" for
   * insertion into a style sheet.  This function doesn't attempt to
   * ensure that the resulting text is a valid value for the given
   * property; but rather just that inserting the text into the style
   * sheet will not cause unwanted changes to other rules or
   * declarations.
   *
   * @param {String} text The input text.  This should include the trailing ";".
   * @return {Array} An array of the form [anySanitized, text], where
   *                 |anySanitized| is a boolean that indicates
   *                  whether anything substantive has changed; and
   *                  where |text| is the text that has been rewritten
   *                  to be "lexically safe".
   */
  sanitizePropertyValue: function (text) {
    // Start by stripping any trailing ";".  This is done here to
    // avoid the case where the user types "url(" (which is turned
    // into "url(;" by the rule view before coming here), being turned
    // into "url(;)" by this code -- due to the way "url(...)" is
    // parsed as a single token.
    text = text.replace(/;$/, "");
    let lexer = getCSSLexer(text);

    let result = "";
    let previousOffset = 0;
    let parenStack = [];
    let anySanitized = false;

    // Push a closing paren on the stack.
    let pushParen = (token, closer) => {
      result = result + text.substring(previousOffset, token.startOffset) +
        text.substring(token.startOffset, token.endOffset);
      // We set the location of the paren in a funny way, to handle
      // the case where we've seen a function token, where the paren
      // appears at the end.
      parenStack.push({closer, offset: result.length - 1});
      previousOffset = token.endOffset;
    };

    // Pop a closing paren from the stack.
    let popSomeParens = (closer) => {
      while (parenStack.length > 0) {
        let paren = parenStack.pop();

        if (paren.closer === closer) {
          return true;
        }

        // Found a non-matching closing paren, so quote it.  Note that
        // these are processed in reverse order.
        result = result.substring(0, paren.offset) + "\\" +
          result.substring(paren.offset);
        anySanitized = true;
      }
      return false;
    };

    while (true) {
      let token = lexer.nextToken();
      if (!token) {
        break;
      }

      if (token.tokenType === "symbol") {
        switch (token.text) {
          case ";":
            // We simply drop the ";" here.  This lets us cope with
            // declarations that don't have a ";" and also other
            // termination.  The caller handles adding the ";" again.
            result += text.substring(previousOffset, token.startOffset);
            previousOffset = token.endOffset;
            break;

          case "{":
            pushParen(token, "}");
            break;

          case "(":
            pushParen(token, ")");
            break;

          case "[":
            pushParen(token, "]");
            break;

          case "}":
          case ")":
          case "]":
            // Did we find an unmatched close bracket?
            if (!popSomeParens(token.text)) {
              // Copy out text from |previousOffset|.
              result += text.substring(previousOffset, token.startOffset);
              // Quote the offending symbol.
              result += "\\" + token.text;
              previousOffset = token.endOffset;
              anySanitized = true;
            }
            break;
        }
      } else if (token.tokenType === "function") {
        pushParen(token, ")");
      }
    }

    // Fix up any unmatched parens.
    popSomeParens(null);

    // Copy out any remaining text, then any needed terminators.
    result += text.substring(previousOffset, text.length);
    let eofFixup = lexer.performEOFFixup("", true);
    if (eofFixup) {
      anySanitized = true;
      result += eofFixup;
    }
    return [anySanitized, result];
  },

  /**
   * Start at |index| and skip whitespace
   * backward in |string|.  Return the index of the first
   * non-whitespace character, or -1 if the entire string was
   * whitespace.
   * @param {String} string the input string
   * @param {Number} index the index at which to start
   * @return {Number} index of the first non-whitespace character, or -1
   */
  skipWhitespaceBackward: function (string, index) {
    for (--index;
         index >= 0 && (string[index] === " " || string[index] === "\t");
         --index) {
      // Nothing.
    }
    return index;
  },

  /**
   * Terminate a given declaration, if needed.
   *
   * @param {Number} index The index of the rule to possibly
   *                       terminate.  It might be invalid, so this
   *                       function must check for that.
   */
  maybeTerminateDecl: function (index) {
    if (index < 0 || index >= this.declarations.length
        // No need to rewrite declarations in comments.
        || ("commentOffsets" in this.declarations[index])) {
      return;
    }

    let termDecl = this.declarations[index];
    let endIndex = termDecl.offsets[1];
    // Due to an oddity of the lexer, we might have gotten a bit of
    // extra whitespace in a trailing bad_url token -- so be sure to
    // skip that as well.
    endIndex = this.skipWhitespaceBackward(this.result, endIndex) + 1;

    let trailingText = this.result.substring(endIndex);
    if (termDecl.terminator) {
      // Insert the terminator just at the end of the declaration,
      // before any trailing whitespace.
      this.result = this.result.substring(0, endIndex) + termDecl.terminator +
        trailingText;
      // In a couple of cases, we may have had to add something to
      // terminate the declaration, but the termination did not
      // actually affect the property's value -- and at this spot, we
      // only care about reporting value changes.  In particular, we
      // might have added a plain ";", or we might have terminated a
      // comment with "*/;".  Neither of these affect the value.
      if (termDecl.terminator !== ";" && termDecl.terminator !== "*/;") {
        this.changedDeclarations[index] =
          termDecl.value + termDecl.terminator.slice(0, -1);
      }
    }
    // If the rule generally has newlines, but this particular
    // declaration doesn't have a trailing newline, insert one now.
    // Maybe this style is too weird to bother with.
    if (this.hasNewLine && !NEWLINE_RX.test(trailingText)) {
      this.result += "\n";
    }
  },

  /**
   * Sanitize the given property value and return the sanitized form.
   * If the property is rewritten during sanitization, make a note in
   * |changedDeclarations|.
   *
   * @param {String} text The property text.
   * @param {Number} index The index of the property.
   * @return {String} The sanitized text.
   */
  sanitizeText: function (text, index) {
    let [anySanitized, sanitizedText] = this.sanitizePropertyValue(text);
    if (anySanitized) {
      this.changedDeclarations[index] = sanitizedText;
    }
    return sanitizedText;
  },

  /**
   * Rename a declaration.
   *
   * @param {Number} index index of the property in the rule.
   * @param {String} name current name of the property
   * @param {String} newName new name of the property
   */
  renameProperty: function (index, name, newName) {
    this.completeInitialization(index);
    this.result += CSS.escape(newName);
    // We could conceivably compute the name offsets instead so we
    // could preserve white space and comments on the LHS of the ":".
    this.completeCopying(this.decl.colonOffsets[0]);
  },

  /**
   * Enable or disable a declaration
   *
   * @param {Number} index index of the property in the rule.
   * @param {String} name current name of the property
   * @param {Boolean} isEnabled true if the property should be enabled;
   *                        false if it should be disabled
   */
  setPropertyEnabled: function (index, name, isEnabled) {
    this.completeInitialization(index);
    const decl = this.decl;
    let copyOffset = decl.offsets[1];
    if (isEnabled) {
      // Enable it.  First see if the comment start can be deleted.
      let commentStart = decl.commentOffsets[0];
      if (EMPTY_COMMENT_START_RX.test(this.result.substring(commentStart))) {
        this.result = this.result.substring(0, commentStart);
      } else {
        this.result += "*/ ";
      }

      // Insert the name and value separately, so we can report
      // sanitization changes properly.
      let commentNamePart =
          this.inputString.substring(decl.offsets[0],
                                     decl.colonOffsets[1]);
      this.result += unescapeCSSComment(commentNamePart);

      // When uncommenting, we must be sure to sanitize the text, to
      // avoid things like /* decl: }; */, which will be accepted as
      // a property but which would break the entire style sheet.
      let newText = this.inputString.substring(decl.colonOffsets[1],
                                               decl.offsets[1]);
      newText = cssTrimRight(unescapeCSSComment(newText));
      this.result += this.sanitizeText(newText, index) + ";";

      // See if the comment end can be deleted.
      let trailingText = this.inputString.substring(decl.offsets[1]);
      if (EMPTY_COMMENT_END_RX.test(trailingText)) {
        copyOffset = decl.commentOffsets[1];
      } else {
        this.result += " /*";
      }
    } else {
      // Disable it.  Note that we use our special comment syntax
      // here.
      let declText = this.inputString.substring(decl.offsets[0],
                                                decl.offsets[1]);
      this.result += "/*" + COMMENT_PARSING_HEURISTIC_BYPASS_CHAR +
        " " + escapeCSSComment(declText) + " */";
    }
    this.completeCopying(copyOffset);
  },

  /**
   * Return a promise that will be resolved to the default indentation
   * of the rule.  This is a helper for internalCreateProperty.
   *
   * @return {Promise} a promise that will be resolved to a string
   *         that holds the default indentation that should be used
   *         for edits to the rule.
   */
  getDefaultIndentation: function () {
    return this.rule.parentStyleSheet.guessIndentation();
  },

  /**
   * An internal function to create a new declaration.  This does all
   * the work of |createProperty|.
   *
   * @param {Number} index index of the property in the rule.
   * @param {String} name name of the new property
   * @param {String} value value of the new property
   * @param {String} priority priority of the new property; either
   *                          the empty string or "important"
   * @param {Boolean} enabled True if the new property should be
   *                          enabled, false if disabled
   * @return {Promise} a promise that is resolved when the edit has
   *                   completed
   */
  async internalCreateProperty(index, name, value, priority, enabled) {
    this.completeInitialization(index);
    let newIndentation = "";
    if (this.hasNewLine) {
      if (this.declarations.length > 0) {
        newIndentation = this.getIndentation(this.inputString,
                                             this.declarations[0].offsets[0]);
      } else if (this.defaultIndentation) {
        newIndentation = this.defaultIndentation;
      } else {
        newIndentation = await this.getDefaultIndentation();
      }
    }

    this.maybeTerminateDecl(index - 1);

    // If we generally have newlines, and if skipping whitespace
    // backward stops at a newline, then insert our text before that
    // whitespace.  This ensures the indentation we computed is what
    // is actually used.
    let savedWhitespace = "";
    if (this.hasNewLine) {
      let wsOffset = this.skipWhitespaceBackward(this.result,
                                                 this.result.length);
      if (this.result[wsOffset] === "\r" || this.result[wsOffset] === "\n") {
        savedWhitespace = this.result.substring(wsOffset + 1);
        this.result = this.result.substring(0, wsOffset + 1);
      }
    }

    let newText = CSS.escape(name) + ": " + this.sanitizeText(value, index);
    if (priority === "important") {
      newText += " !important";
    }
    newText += ";";

    if (!enabled) {
      newText = "/*" + COMMENT_PARSING_HEURISTIC_BYPASS_CHAR + " " +
        escapeCSSComment(newText) + " */";
    }

    this.result += newIndentation + newText;
    if (this.hasNewLine) {
      this.result += "\n";
    }
    this.result += savedWhitespace;

    if (this.decl) {
      // Still want to copy in the declaration previously at this
      // index.
      this.completeCopying(this.decl.offsets[0]);
    }
  },

  /**
   * Create a new declaration.
   *
   * @param {Number} index index of the property in the rule.
   * @param {String} name name of the new property
   * @param {String} value value of the new property
   * @param {String} priority priority of the new property; either
   *                          the empty string or "important"
   * @param {Boolean} enabled True if the new property should be
   *                          enabled, false if disabled
   */
  createProperty: function (index, name, value, priority, enabled) {
    this.editPromise = this.internalCreateProperty(index, name, value,
                                                   priority, enabled);
  },

  /**
   * Set a declaration's value.
   *
   * @param {Number} index index of the property in the rule.
   *                       This can be -1 in the case where
   *                       the rule does not support setRuleText;
   *                       generally for setting properties
   *                       on an element's style.
   * @param {String} name the property's name
   * @param {String} value the property's value
   * @param {String} priority the property's priority, either the empty
   *                          string or "important"
   */
  setProperty: function (index, name, value, priority) {
    this.completeInitialization(index);
    // We might see a "set" on a previously non-existent property; in
    // that case, act like "create".
    if (!this.decl) {
      this.createProperty(index, name, value, priority, true);
      return;
    }

    // Note that this assumes that "set" never operates on disabled
    // properties.
    this.result += this.inputString.substring(this.decl.offsets[0],
                                              this.decl.colonOffsets[1]) +
      this.sanitizeText(value, index);

    if (priority === "important") {
      this.result += " !important";
    }
    this.result += ";";
    this.completeCopying(this.decl.offsets[1]);
  },

  /**
   * Remove a declaration.
   *
   * @param {Number} index index of the property in the rule.
   * @param {String} name the name of the property to remove
   */
  removeProperty: function (index, name) {
    this.completeInitialization(index);

    // If asked to remove a property that does not exist, bail out.
    if (!this.decl) {
      return;
    }

    // If the property is disabled, then first enable it, and then
    // delete it.  We take this approach because we want to remove the
    // entire comment if possible; but the logic for dealing with
    // comments is hairy and already implemented in
    // setPropertyEnabled.
    if (this.decl.commentOffsets) {
      this.setPropertyEnabled(index, name, true);
      this.startInitialization(this.result);
      this.completeInitialization(index);
    }

    let copyOffset = this.decl.offsets[1];
    // Maybe removing this rule left us with a completely blank
    // line.  In this case, we'll delete the whole thing.  We only
    // bother with this if we're looking at sources that already
    // have a newline somewhere.
    if (this.hasNewLine) {
      let nlOffset = this.skipWhitespaceBackward(this.result,
                                                 this.decl.offsets[0]);
      if (nlOffset < 0 || this.result[nlOffset] === "\r" ||
          this.result[nlOffset] === "\n") {
        let trailingText = this.inputString.substring(copyOffset);
        let match = BLANK_LINE_RX.exec(trailingText);
        if (match) {
          this.result = this.result.substring(0, nlOffset + 1);
          copyOffset += match[0].length;
        }
      }
    }
    this.completeCopying(copyOffset);
  },

  /**
   * An internal function to copy any trailing text to the output
   * string.
   *
   * @param {Number} copyOffset Offset into |inputString| of the
   *        final text to copy to the output string.
   */
  completeCopying: function (copyOffset) {
    // Add the trailing text.
    this.result += this.inputString.substring(copyOffset);
  },

  /**
   * Apply the modifications in this object to the associated rule.
   *
   * @return {Promise} A promise which will be resolved when the modifications
   *         are complete.
   */
  apply: function () {
    return promise.resolve(this.editPromise).then(() => {
      return this.rule.setRuleText(this.result);
    });
  },

  /**
   * Get the result of the rewriting.  This is used for testing.
   *
   * @return {object} an object of the form {changed: object, text: string}
   *                  |changed| is an object where each key is
   *                  the index of a property whose value had to be
   *                  rewritten during the sanitization process, and
   *                  whose value is the new text of the property.
   *                  |text| is the rewritten text of the rule.
   */
  getResult: function () {
    return {changed: this.changedDeclarations, text: this.result};
  },
};

/**
 * Returns an array of the parsed CSS selector value and type given a string.
 *
 * The components making up the CSS selector can be extracted into 3 different
 * types: element, attribute and pseudoclass. The object that is appended to
 * the returned array contains the value related to one of the 3 types described
 * along with the actual type.
 *
 * The following are the 3 types that can be returned in the object signature:
 * (1) SELECTOR_ATTRIBUTE
 * (2) SELECTOR_ELEMENT
 * (3) SELECTOR_PSEUDO_CLASS
 *
 * @param {String} value
 *        The CSS selector text.
 * @return {Array} an array of objects with the following signature:
 *         [{ "value": string, "type": integer }, ...]
 */
function parsePseudoClassesAndAttributes(value) {
  if (!value) {
    throw new Error("empty input string");
  }

  let tokens = cssTokenizer(value);
  let result = [];
  let current = "";
  let functionCount = 0;
  let hasAttribute = false;
  let hasColon = false;

  for (let token of tokens) {
    if (token.tokenType === "ident") {
      current += value.substring(token.startOffset, token.endOffset);

      if (hasColon && !functionCount) {
        if (current) {
          result.push({ value: current, type: SELECTOR_PSEUDO_CLASS });
        }

        current = "";
        hasColon = false;
      }
    } else if (token.tokenType === "symbol" && token.text === ":") {
      if (!hasColon) {
        if (current) {
          result.push({ value: current, type: SELECTOR_ELEMENT });
        }

        current = "";
        hasColon = true;
      }

      current += token.text;
    } else if (token.tokenType === "function") {
      current += value.substring(token.startOffset, token.endOffset);
      functionCount++;
    } else if (token.tokenType === "symbol" && token.text === ")") {
      current += token.text;

      if (hasColon && functionCount == 1) {
        if (current) {
          result.push({ value: current, type: SELECTOR_PSEUDO_CLASS });
        }

        current = "";
        functionCount--;
        hasColon = false;
      } else {
        functionCount--;
      }
    } else if (token.tokenType === "symbol" && token.text === "[") {
      if (!hasAttribute && !functionCount) {
        if (current) {
          result.push({ value: current, type: SELECTOR_ELEMENT });
        }

        current = "";
        hasAttribute = true;
      }

      current += token.text;
    } else if (token.tokenType === "symbol" && token.text === "]") {
      current += token.text;

      if (hasAttribute && !functionCount) {
        if (current) {
          result.push({ value: current, type: SELECTOR_ATTRIBUTE });
        }

        current = "";
        hasAttribute = false;
      }
    } else {
      current += value.substring(token.startOffset, token.endOffset);
    }
  }

  if (current) {
    result.push({ value: current, type: SELECTOR_ELEMENT });
  }

  return result;
}

/**
 * Expects a single CSS value to be passed as the input and parses the value
 * and priority.
 *
 * @param {Function} isCssPropertyKnown
 *        A function to check if the CSS property is known. This is either an
 *        internal server function or from the CssPropertiesFront.
 *        that are supported by the server.
 * @param {String} value
 *        The value from the text editor.
 * @return {Object} an object with 'value' and 'priority' properties.
 */
function parseSingleValue(isCssPropertyKnown, value) {
  let declaration = parseDeclarations(isCssPropertyKnown,
                                      "a: " + value + ";")[0];
  return {
    value: declaration ? declaration.value : "",
    priority: declaration ? declaration.priority : ""
  };
}

/**
 * Convert an angle value to degree.
 *
 * @param {Number} angleValue The angle value.
 * @param {CSS_ANGLEUNIT} angleUnit The angleValue's angle unit.
 * @return {Number} An angle value in degree.
 */
function getAngleValueInDegrees(angleValue, angleUnit) {
  switch (angleUnit) {
    case CSS_ANGLEUNIT.deg:
      return angleValue;
    case CSS_ANGLEUNIT.grad:
      return angleValue * 0.9;
    case CSS_ANGLEUNIT.rad:
      return angleValue * 180 / Math.PI;
    case CSS_ANGLEUNIT.turn:
      return angleValue * 360;
    default:
      throw new Error("No matched angle unit.");
  }
}

exports.cssTokenizer = cssTokenizer;
exports.cssTokenizerWithLineColumn = cssTokenizerWithLineColumn;
exports.escapeCSSComment = escapeCSSComment;
// unescapeCSSComment is exported for testing.
exports._unescapeCSSComment = unescapeCSSComment;
exports.parseDeclarations = parseDeclarations;
exports.parseNamedDeclarations = parseNamedDeclarations;
// parseCommentDeclarations is exported for testing.
exports._parseCommentDeclarations = parseCommentDeclarations;
exports.RuleRewriter = RuleRewriter;
exports.parsePseudoClassesAndAttributes = parsePseudoClassesAndAttributes;
exports.parseSingleValue = parseSingleValue;
exports.getAngleValueInDegrees = getAngleValueInDegrees;
PK
!<�����<chrome/devtools/modules/devtools/shared/css/properties-db.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 contains static lists of CSS properties and values. Some of the small lists
 * are edited manually, while the larger ones are generated by a script. The comments
 * above each list indicates how it should be updated.
 */

let db;

// Allow this require to fail in case it's been deleted in the process of running
// `mach devtools-css-db` to regenerate the database.
try {
  db = require("devtools/shared/css/generated/properties-db");
} catch (error) {
  console.error(`If this error is being displayed and "mach devtools-css-db" is not ` +
                `being run, then it needs to be fixed.`, error);
  db = {
    CSS_PROPERTIES: {},
    PSEUDO_ELEMENTS: []
  };
}

/**
 * All CSS types that properties can support. This list can be manually edited.
 */
exports.CSS_TYPES = {
  "ANGLE": 1,
  "COLOR": 2,
  "FREQUENCY": 3,
  "GRADIENT": 4,
  "IMAGE_RECT": 5,
  "LENGTH": 6,
  "NUMBER": 7,
  "PERCENTAGE": 8,
  "TIME": 9,
  "TIMING_FUNCTION": 10,
  "URL": 11,
};

/**
 * All CSS <angle> types that properties can support. This list can be manually edited.
 */
exports.CSS_ANGLEUNIT = {
  "deg": "deg",
  "rad": "rad",
  "grad": "grad",
  "turn": "turn"
};

/**
 * All cubic-bezier CSS timing-function names. This list can be manually edited.
 */
exports.BEZIER_KEYWORDS = ["linear", "ease-in-out", "ease-in", "ease-out", "ease"];

/**
 * Functions that accept a color argument. This list can be manually edited.
 */
exports.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", "drop-shadow"];

/**
 * Functions that accept an angle argument. This list can be manually edited.
 */
exports.ANGLE_TAKING_FUNCTIONS = ["linear-gradient", "-moz-linear-gradient",
                                  "repeating-linear-gradient",
                                  "-moz-repeating-linear-gradient", "rotate", "rotateX",
                                  "rotateY", "rotateZ", "rotate3d", "skew", "skewX",
                                  "skewY", "hue-rotate"];

exports.BASIC_SHAPE_FUNCTIONS = ["polygon", "circle", "ellipse", "inset"];

/**
 * The list of all CSS Pseudo Elements.
 *
 * This list can be updated with `mach devtools-css-db`.
 */
exports.PSEUDO_ELEMENTS = db.PSEUDO_ELEMENTS;

/**
 * A list of CSS Properties and their various characteristics. This is used on the
 * client-side when the CssPropertiesActor is not found, or when the client and server
 * are the same version. A single property takes the form:
 *
 *  "animation": {
 *    "isInherited": false,
 *    "supports": [ 7, 9, 10 ]
 *  }
 */
exports.CSS_PROPERTIES = db.CSS_PROPERTIES;

exports.CSS_PROPERTIES_DB = {
  properties: db.CSS_PROPERTIES,
  pseudoElements: db.PSEUDO_ELEMENTS
};
PK
!<�`q�ll3chrome/devtools/modules/devtools/shared/debounce.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

/**
 * Create a debouncing function wrapper to only call the target function after a certain
 * amount of time has passed without it being called.
 *
 * @param {Function} func
 *         The function to debounce
 * @param {number} wait
 *         The wait period
 * @param {Object} scope
 *         The scope to use for func
 * @return {Function} The debounced function
 */
exports.debounce = function (func, wait, scope) {
  let timer = null;

  return function () {
    if (timer) {
      clearTimeout(timer);
    }

    let args = arguments;
    timer = setTimeout(function () {
      timer = null;
      func.apply(scope, args);
    }, wait);
  };
};
PK
!<��6��0chrome/devtools/modules/devtools/shared/defer.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

// See bug 1273941 to understand this choice of promise.
const Promise = require("promise");

/**
 * Returns a deferred object, with a resolve and reject property.
 * https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/Promise.jsm/Deferred
 */
module.exports = function defer() {
  let resolve, reject;
  let promise = new Promise(function () {
    resolve = arguments[0];
    reject = arguments[1];
  });
  return {
    resolve: resolve,
    reject: reject,
    promise: promise
  };
};
PK
!<(���33Dchrome/devtools/modules/devtools/shared/deprecated-sync-thenables.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 MODULE IS DEPRECATED. Use DOM Promises instead.
 */

/* eslint-disable */

"use strict";

this.Promise = {};

module.exports = Promise;

function fulfilled(value) {
  return { then: function then(fulfill) { fulfill(value); } };
}

function rejected(reason) {
  return { then: function then(fulfill, reject) { reject(reason); } };
}

function isPromise(value) {
  return value && typeof (value.then) === "function";
}

function defer() {
  var observers = [];
  var result = null;
  var promise = {
    then: function then(onFulfill, onError) {
      var deferred = defer();

      function resolve(value) {
        try {
          deferred.resolve(onFulfill ? onFulfill(value) : value);
        } catch (error) {
          deferred.resolve(rejected(error));
        }
      }

      function reject(reason) {
        try {
          if (onError) deferred.resolve(onError(reason));
          else deferred.resolve(rejected(reason));
        } catch (error) {
          deferred.resolve(rejected(error));
        }
      }

      if (observers) {
        observers.push({ resolve: resolve, reject: reject });
      } else {
        result.then(resolve, reject);
      }

      return deferred.promise;
    },
    catch: function (callback) {
      return this.then(null, callback);
    }
  };

  var deferred = {
    promise: promise,
    resolve: function resolve(value) {
      if (!result) {
        result = isPromise(value) ? value : fulfilled(value);
        while (observers.length) {
          var observer = observers.shift();
          result.then(observer.resolve, observer.reject);
        }
        observers = null;
      }
    },
    reject: function reject(reason) {
      deferred.resolve(rejected(reason));
    }
  };

  return deferred;
}
Promise.defer = defer;

function resolve(value) {
  var deferred = defer();
  deferred.resolve(value);
  return deferred.promise;
}
Promise.resolve = resolve;

function reject(reason) {
  var deferred = defer();
  deferred.reject(reason);
  return deferred.promise;
}
Promise.reject = reject;

var promised = (function () {
  var call = Function.call;
  var concat = Array.prototype.concat;
  function execute(args) { return call.apply(call, args); }
  function promisedConcat(promises, unknown) {
    return promises.then(function (values) {
      return resolve(unknown).then(function (value) {
        return values.concat([ value ]);
      });
    });
  }
  return function promised(f) {
    return function promised() {
      return concat.apply([ f, this ], arguments).
        reduce(promisedConcat, resolve([])).
        then(execute);
    };
  };
})();
Promise.all = promised(Array);
PK
!<C,ߺR3R3>chrome/devtools/modules/devtools/shared/discovery/discovery.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 implements a UDP mulitcast device discovery protocol that:
 *   * Is optimized for mobile devices
 *   * Doesn't require any special schema for service info
 *
 * To ensure it works well on mobile devices, there is no heartbeat or other
 * recurring transmission.
 *
 * Devices are typically in one of two groups: scanning for services or
 * providing services (though they may be in both groups as well).
 *
 * Scanning devices listen on UPDATE_PORT for UDP multicast traffic.  When the
 * scanning device wants to force an update of the services available, it sends
 * a status packet to SCAN_PORT.
 *
 * Service provider devices listen on SCAN_PORT for any packets from scanning
 * devices.  If one is recevied, the provider device sends a status packet
 * (listing the services it offers) to UPDATE_PORT.
 *
 * Scanning devices purge any previously known devices after REPLY_TIMEOUT ms
 * from that start of a scan if no reply is received during the most recent
 * scan.
 *
 * When a service is registered, is supplies a regular object with any details
 * about itself (a port number, for example) in a service-defined format, which
 * is then available to scanning devices.
 */

const { Cu, CC, Cc, Ci } = require("chrome");
const EventEmitter = require("devtools/shared/old-event-emitter");
const Services = require("Services");

const UDPSocket = CC("@mozilla.org/network/udp-socket;1",
                     "nsIUDPSocket",
                     "init");

const SCAN_PORT = 50624;
const UPDATE_PORT = 50625;
const ADDRESS = "224.0.0.115";
const REPLY_TIMEOUT = 5000;

const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");

XPCOMUtils.defineLazyGetter(this, "converter", () => {
  let conv = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
             .createInstance(Ci.nsIScriptableUnicodeConverter);
  conv.charset = "utf8";
  return conv;
});

XPCOMUtils.defineLazyGetter(this, "sysInfo", () => {
  return Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2);
});

var logging = Services.prefs.getBoolPref("devtools.discovery.log");
function log(msg) {
  if (logging) {
    console.log("DISCOVERY: " + msg);
  }
}

/**
 * Each Transport instance owns a single UDPSocket.
 * @param port integer
 *        The port to listen on for incoming UDP multicast packets.
 */
function Transport(port) {
  EventEmitter.decorate(this);
  try {
    this.socket = new UDPSocket(port, false,
                                Services.scriptSecurityManager.getSystemPrincipal());
    this.socket.joinMulticast(ADDRESS);
    this.socket.asyncListen(this);
  } catch (e) {
    log("Failed to start new socket: " + e);
  }
}

Transport.prototype = {

  /**
   * Send a object to some UDP port.
   * @param object object
   *        Object which is the message to send
   * @param port integer
   *        UDP port to send the message to
   */
  send: function (object, port) {
    if (logging) {
      log("Send to " + port + ":\n" + JSON.stringify(object, null, 2));
    }
    let message = JSON.stringify(object);
    let rawMessage = converter.convertToByteArray(message);
    try {
      this.socket.send(ADDRESS, port, rawMessage, rawMessage.length);
    } catch (e) {
      log("Failed to send message: " + e);
    }
  },

  destroy: function () {
    this.socket.close();
  },

  // nsIUDPSocketListener

  onPacketReceived: function (socket, message) {
    let messageData = message.data;
    let object = JSON.parse(messageData);
    object.from = message.fromAddr.address;
    let port = message.fromAddr.port;
    if (port == this.socket.port) {
      log("Ignoring looped message");
      return;
    }
    if (logging) {
      log("Recv on " + this.socket.port + ":\n" +
          JSON.stringify(object, null, 2));
    }
    this.emit("message", object);
  },

  onStopListening: function () {}

};

/**
 * Manages the local device's name.  The name can be generated in serveral
 * platform-specific ways (see |_generate|).  The aim is for each device on the
 * same local network to have a unique name.
 */
function LocalDevice() {
  this._name = LocalDevice.UNKNOWN;
  // Trigger |_get| to load name eagerly
  this._get();
}

LocalDevice.UNKNOWN = "unknown";

LocalDevice.prototype = {

  _get: function () {
    // Without Settings API, just generate a name and stop, since the value
    // can't be persisted.
    this._generate();
  },

  /**
   * Generate a new device name from various platform-specific properties.
   * Triggers the |name| setter to persist if needed.
   */
  _generate: function () {
    if (Services.appinfo.widgetToolkit == "android") {
      // For Firefox for Android, use the device's model name.
      // TODO: Bug 1180997: Find the right way to expose an editable name
      this.name = sysInfo.get("device");
    } else {
      this.name = Cc["@mozilla.org/network/dns-service;1"].getService(Ci.nsIDNSService)
                                                          .myHostName;
    }
  },

  get name() {
    return this._name;
  },

  set name(name) {
    this._name = name;
    log("Device: " + this._name);
  }

};

function Discovery() {
  EventEmitter.decorate(this);

  this.localServices = {};
  this.remoteServices = {};
  this.device = new LocalDevice();
  this.replyTimeout = REPLY_TIMEOUT;

  // Defaulted to Transport, but can be altered by tests
  this._factories = { Transport: Transport };

  this._transports = {
    scan: null,
    update: null
  };
  this._expectingReplies = {
    from: new Set()
  };

  this._onRemoteScan = this._onRemoteScan.bind(this);
  this._onRemoteUpdate = this._onRemoteUpdate.bind(this);
  this._purgeMissingDevices = this._purgeMissingDevices.bind(this);
}

Discovery.prototype = {

  /**
   * Add a new service offered by this device.
   * @param service string
   *        Name of the service
   * @param info object
   *        Arbitrary data about the service to announce to scanning devices
   */
  addService: function (service, info) {
    log("ADDING LOCAL SERVICE");
    if (Object.keys(this.localServices).length === 0) {
      this._startListeningForScan();
    }
    this.localServices[service] = info;
  },

  /**
   * Remove a service offered by this device.
   * @param service string
   *        Name of the service
   */
  removeService: function (service) {
    delete this.localServices[service];
    if (Object.keys(this.localServices).length === 0) {
      this._stopListeningForScan();
    }
  },

  /**
   * Scan for service updates from other devices.
   */
  scan: function () {
    this._startListeningForUpdate();
    this._waitForReplies();
    // TODO Bug 1027457: Use timer to debounce
    this._sendStatusTo(SCAN_PORT);
  },

  /**
   * Get a list of all remote devices currently offering some service.:w
   */
  getRemoteDevices: function () {
    let devices = new Set();
    for (let service in this.remoteServices) {
      for (let device in this.remoteServices[service]) {
        devices.add(device);
      }
    }
    return [...devices];
  },

  /**
   * Get a list of all remote devices currently offering a particular service.
   */
  getRemoteDevicesWithService: function (service) {
    let devicesWithService = this.remoteServices[service] || {};
    return Object.keys(devicesWithService);
  },

  /**
   * Get service info (any details registered by the remote device) for a given
   * service on a device.
   */
  getRemoteService: function (service, device) {
    let devicesWithService = this.remoteServices[service] || {};
    return devicesWithService[device];
  },

  _waitForReplies: function () {
    clearTimeout(this._expectingReplies.timer);
    this._expectingReplies.from = new Set(this.getRemoteDevices());
    this._expectingReplies.timer =
      setTimeout(this._purgeMissingDevices, this.replyTimeout);
  },

  get Transport() {
    return this._factories.Transport;
  },

  _startListeningForScan: function () {
    if (this._transports.scan) {
      // Already listening
      return;
    }
    log("LISTEN FOR SCAN");
    this._transports.scan = new this.Transport(SCAN_PORT);
    this._transports.scan.on("message", this._onRemoteScan);
  },

  _stopListeningForScan: function () {
    if (!this._transports.scan) {
      // Not listening
      return;
    }
    this._transports.scan.off("message", this._onRemoteScan);
    this._transports.scan.destroy();
    this._transports.scan = null;
  },

  _startListeningForUpdate: function () {
    if (this._transports.update) {
      // Already listening
      return;
    }
    log("LISTEN FOR UPDATE");
    this._transports.update = new this.Transport(UPDATE_PORT);
    this._transports.update.on("message", this._onRemoteUpdate);
  },

  _stopListeningForUpdate: function () {
    if (!this._transports.update) {
      // Not listening
      return;
    }
    this._transports.update.off("message", this._onRemoteUpdate);
    this._transports.update.destroy();
    this._transports.update = null;
  },

  _restartListening: function () {
    if (this._transports.scan) {
      this._stopListeningForScan();
      this._startListeningForScan();
    }
    if (this._transports.update) {
      this._stopListeningForUpdate();
      this._startListeningForUpdate();
    }
  },

  /**
   * When sending message, we can use either transport, so just pick the first
   * one currently alive.
   */
  get _outgoingTransport() {
    if (this._transports.scan) {
      return this._transports.scan;
    }
    if (this._transports.update) {
      return this._transports.update;
    }
    return null;
  },

  _sendStatusTo: function (port) {
    let status = {
      device: this.device.name,
      services: this.localServices
    };
    this._outgoingTransport.send(status, port);
  },

  _onRemoteScan: function () {
    // Send my own status in response
    log("GOT SCAN REQUEST");
    this._sendStatusTo(UPDATE_PORT);
  },

  _onRemoteUpdate: function (e, update) {
    log("GOT REMOTE UPDATE");

    let remoteDevice = update.device;
    let remoteHost = update.from;

    // Record the reply as received so it won't be purged as missing
    this._expectingReplies.from.delete(remoteDevice);

    // First, loop over the known services
    for (let service in this.remoteServices) {
      let devicesWithService = this.remoteServices[service];
      let hadServiceForDevice = !!devicesWithService[remoteDevice];
      let haveServiceForDevice = service in update.services;
      // If the remote device used to have service, but doesn't any longer, then
      // it was deleted, so we remove it here.
      if (hadServiceForDevice && !haveServiceForDevice) {
        delete devicesWithService[remoteDevice];
        log("REMOVED " + service + ", DEVICE " + remoteDevice);
        this.emit(service + "-device-removed", remoteDevice);
      }
    }

    // Second, loop over the services in the received update
    for (let service in update.services) {
      // Detect if this is a new device for this service
      let newDevice = !this.remoteServices[service] ||
                      !this.remoteServices[service][remoteDevice];

      // Look up the service info we may have received previously from the same
      // remote device
      let devicesWithService = this.remoteServices[service] || {};
      let oldDeviceInfo = devicesWithService[remoteDevice];

      // Store the service info from the remote device
      let newDeviceInfo = Cu.cloneInto(update.services[service], {});
      newDeviceInfo.host = remoteHost;
      devicesWithService[remoteDevice] = newDeviceInfo;
      this.remoteServices[service] = devicesWithService;

      // If this is a new service for the remote device, announce the addition
      if (newDevice) {
        log("ADDED " + service + ", DEVICE " + remoteDevice);
        this.emit(service + "-device-added", remoteDevice, newDeviceInfo);
      }

      // If we've seen this service from the remote device, but the details have
      // changed, announce the update
      if (!newDevice &&
          JSON.stringify(oldDeviceInfo) != JSON.stringify(newDeviceInfo)) {
        log("UPDATED " + service + ", DEVICE " + remoteDevice);
        this.emit(service + "-device-updated", remoteDevice, newDeviceInfo);
      }
    }
  },

  _purgeMissingDevices: function () {
    log("PURGING MISSING DEVICES");
    for (let service in this.remoteServices) {
      let devicesWithService = this.remoteServices[service];
      for (let remoteDevice in devicesWithService) {
        // If we're still expecting a reply from a remote device when it's time
        // to purge, then the service is removed.
        if (this._expectingReplies.from.has(remoteDevice)) {
          delete devicesWithService[remoteDevice];
          log("REMOVED " + service + ", DEVICE " + remoteDevice);
          this.emit(service + "-device-removed", remoteDevice);
        }
      }
    }
  }

};

var discovery = new Discovery();

module.exports = discovery;
PK
!<}�7���=chrome/devtools/modules/devtools/shared/dom-node-constants.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 define */

// 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
  };
});
PK
!<�%r��Dchrome/devtools/modules/devtools/shared/dom-node-filter-constants.js"use strict";

module.exports = {
  FILTER_ACCEPT: 1,
  FILTER_REJECT: 2,
  FILTER_SKIP: 3,

  SHOW_ALL: 0xFFFFFFFF,
  SHOW_ELEMENT: 0x00000001,
  SHOW_ATTRIBUTE: 0x00000002,
  SHOW_TEXT: 0x00000004,
  SHOW_CDATA_SECTION: 0x00000008,
  SHOW_ENTITY_REFERENCE: 0x00000010,
  SHOW_ENTITY: 0x00000020,
  SHOW_PROCESSING_INSTRUCTION: 0x00000040,
  SHOW_COMMENT: 0x00000080,
  SHOW_DOCUMENT: 0x00000100,
  SHOW_DOCUMENT_TYPE: 0x00000200,
  SHOW_DOCUMENT_FRAGMENT: 0x00000400,
  SHOW_NOTATION: 0x00000800
};
PK
!<*Box$x$8chrome/devtools/modules/devtools/shared/event-emitter.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 BAD_LISTENER = "The event listener must be a function, or an object that has " +
                     "`EventEmitter.handler` Symbol.";

const eventListeners = Symbol("EventEmitter/listeners");
const onceOriginalListener = Symbol("EventEmitter/once-original-listener");
const handler = Symbol("EventEmitter/event-handler");

class EventEmitter {
  constructor() {
    this[eventListeners] = new Map();
  }

  /**
   * Registers an event `listener` that is called every time events of
   * specified `type` is emitted on the given event `target`.
   *
   * @param {Object} target
   *    Event target object.
   * @param {String} type
   *    The type of event.
   * @param {Function|Object} listener
   *    The listener that processes the event.
   */
  static on(target, type, listener) {
    if (typeof listener !== "function" && !isEventHandler(listener)) {
      throw new Error(BAD_LISTENER);
    }

    if (!(eventListeners in target)) {
      target[eventListeners] = new Map();
    }

    let events = target[eventListeners];

    if (events.has(type)) {
      events.get(type).add(listener);
    } else {
      events.set(type, new Set([listener]));
    }
  }

  /**
   * Removes an event `listener` for the given event `type` on the given event
   * `target`. If no `listener` is passed removes all listeners of the given
   * `type`. If `type` is not passed removes all the listeners of the given
   * event `target`.
   * @param {Object} target
   *    The event target object.
   * @param {String} [type]
   *    The type of event.
   * @param {Function|Object} [listener]
   *    The listener that processes the event.
   */
  static off(target, type, listener) {
    let length = arguments.length;
    let events = target[eventListeners];

    if (!events) {
      return;
    }

    if (length === 3) {
      // Trying to remove from the `target` the `listener` specified for the
      // event's `type` given.
      let listenersForType = events.get(type);

      // If we don't have listeners for the event's type, we bail out.
      if (!listenersForType) {
        return;
      }

      // If the listeners list contains the listener given, we just remove it.
      if (listenersForType.has(listener)) {
        listenersForType.delete(listener);
      } else {
        // If it's not present, there is still the possibility that the listener
        // have been added using `once`, since the method wraps the original listener
        // in another function.
        // So we iterate all the listeners to check if any of them is a wrapper to
        // the `listener` given.
        for (let value of listenersForType.values()) {
          if (onceOriginalListener in value && value[onceOriginalListener] === listener) {
            listenersForType.delete(value);
            break;
          }
        }
      }
    } else if (length === 2) {
      // No listener was given, it means we're removing all the listeners from
      // the given event's `type`.
      if (events.has(type)) {
        events.delete(type);
      }
    } else if (length === 1) {
      // With only the `target` given, we're removing all the isteners from the object.
      events.clear();
    }
  }

  /**
   * Registers an event `listener` that is called only the next time an event
   * of the specified `type` is emitted on the given event `target`.
   * It returns a promised resolved once the specified event `type` is emitted.
   *
   * @param {Object} target
   *    Event target object.
   * @param {String} type
   *    The type of the event.
   * @param {Function|Object} [listener]
   *    The listener that processes the event.
   * @return {Promise}
   *    The promise resolved once the event `type` is emitted.
   */
  static once(target, type, listener) {
    return new Promise(resolve => {
      // This is the actual listener that will be added to the target's listener, it wraps
      // the call to the original `listener` given.
      let newListener = (first, ...rest) => {
        // To prevent side effects we're removing the listener upfront.
        EventEmitter.off(target, type, newListener);

        if (listener) {
          if (isEventHandler(listener)) {
            // if the `listener` given is actually an object that handles the events
            // using `EventEmitter.handler`, we want to call that function, passing also
            // the event's type as first argument, and the `listener` (the object) as
            // contextual object.
            listener[handler](type, first, ...rest);
          } else {
            // Otherwise we'll just call it
            listener.call(target, first, ...rest);
          }
        }

        // We resolve the promise once the listener is called.
        resolve(first);
      };

      newListener[onceOriginalListener] = listener;
      EventEmitter.on(target, type, newListener);
    });
  }

  static emit(target, type, ...rest) {
    logEvent(type, rest);

    if (!(eventListeners in target)) {
      return;
    }

    if (target[eventListeners].has(type)) {
      // Creating a temporary Set with the original listeners, to avoiding side effects
      // in emit.
      let listenersForType = new Set(target[eventListeners].get(type));

      for (let listener of listenersForType) {
        // If the object was destroyed during event emission, stop emitting.
        if (!(eventListeners in target)) {
          break;
        }

        let events = target[eventListeners];
        let listeners = events.get(type);

        // If listeners were removed during emission, make sure the
        // event handler we're going to fire wasn't removed.
        if (listeners && listeners.has(listener)) {
          try {
            if (isEventHandler(listener)) {
              listener[handler](type, ...rest);
            } else {
              listener.call(target, ...rest);
            }
          } catch (ex) {
            // Prevent a bad listener from interfering with the others.
            let msg = ex + ": " + ex.stack;
            console.error(msg);
            dump(msg + "\n");
          }
        }
      }
    }

    // Backward compatibility with the SDK event-emitter: support wildcard listeners that
    // will be called for any event. The arguments passed to the listener are the event
    // type followed by the actual arguments.
    // !!! This API will be removed by Bug 1391261.
    let hasWildcardListeners = target[eventListeners].has("*");
    if (type !== "*" && hasWildcardListeners) {
      EventEmitter.emit(target, "*", type, ...rest);
    }
  }

  /**
   * Returns a number of event listeners registered for the given event `type`
   * on the given event `target`.
   *
   * @param {Object} target
   *    Event target object.
   * @param {String} type
   *    The type of event.
   * @return {Number}
   *    The number of event listeners.
   */
  static count(target, type) {
    if (eventListeners in target) {
      let listenersForType = target[eventListeners].get(type);

      if (listenersForType) {
        return listenersForType.size;
      }
    }

    return 0;
  }

  /**
   * Decorate an object with event emitter functionality; basically using the
   * class' prototype as mixin.
   *
   * @param Object target
   *    The object to decorate.
   * @return Object
   *    The object given, mixed.
   */
  static decorate(target) {
    let descriptors = Object.getOwnPropertyDescriptors(this.prototype);
    delete descriptors.constructor;
    return Object.defineProperties(target, descriptors);
  }

  static get handler() {
    return handler;
  }

  on(...args) {
    EventEmitter.on(this, ...args);
  }

  off(...args) {
    EventEmitter.off(this, ...args);
  }

  once(...args) {
    return EventEmitter.once(this, ...args);
  }

  emit(...args) {
    EventEmitter.emit(this, ...args);
  }
}

module.exports = EventEmitter;

const isEventHandler = (listener) =>
  listener && handler in listener && typeof listener[handler] === "function";

const Services = require("Services");
const { describeNthCaller } = require("devtools/shared/platform/stack");
let loggingEnabled = true;

if (!isWorker) {
  loggingEnabled = Services.prefs.getBoolPref("devtools.dump.emit");
  Services.prefs.addObserver("devtools.dump.emit", {
    observe: () => {
      loggingEnabled = Services.prefs.getBoolPref("devtools.dump.emit");
    }
  });
}

function serialize(target) {
  let out = String(target);

  if (target && target.nodeName) {
    out += " (" + target.nodeName;
    if (target.id) {
      out += "#" + target.id;
    }
    if (target.className) {
      out += "." + target.className;
    }
    out += ")";
  }

  return out;
}

function logEvent(type, args) {
  if (!loggingEnabled) {
    return;
  }

  let argsOut = "";
  let description = describeNthCaller(2);

  // We need this try / catch to prevent any dead object errors.
  try {
    argsOut = args.map(serialize).join(", ");
  } catch (e) {
    // Object is dead so the toolbox is most likely shutting down,
    // do nothing.
  }

  dump(`EMITTING: emit(${type}${argsOut}) from ${description}\n`);
}
PK
!<�$$1chrome/devtools/modules/devtools/shared/extend.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

/**
 * Utility function, that is useful for creating objects that inherit from other
 * objects, without associated classes.
 *
 * Replacement for `extends` API from "sdk/core/heritage".
 */
exports.extend = function (prototype, properties) {
  return Object.create(prototype, Object.getOwnPropertyDescriptors(properties));
};
PK
!<�;U���0chrome/devtools/modules/devtools/shared/flags.js"use strict";

/*
 * Create a writable property by tracking it with a private variable.
 * We cannot make a normal property writeable on `exports` because
 * the module system freezes it.
 */
function makeWritableFlag(exports, name) {
  let flag = false;
  Object.defineProperty(exports, name, {
    get: function () {
      return flag;
    },
    set: function (state) {
      flag = state;
    }
  });
}

makeWritableFlag(exports, "wantLogging");
makeWritableFlag(exports, "wantVerbose");

// When the testing flag is set, various behaviors may be altered from
// production mode, typically to enable easier testing or enhanced
// debugging.
makeWritableFlag(exports, "testing");
PK
!<ʃ����?chrome/devtools/modules/devtools/shared/fronts/accessibility.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  custom,
  Front,
  FrontClassWithSpec,
  preEvent
} = require("devtools/shared/protocol.js");
const {
  accessibleSpec,
  accessibleWalkerSpec,
  accessibilitySpec
} = require("devtools/shared/specs/accessibility");
const events = require("devtools/shared/event-emitter");

const AccessibleFront = FrontClassWithSpec(accessibleSpec, {
  initialize(client, form) {
    Front.prototype.initialize.call(this, client, form);
  },

  marshallPool() {
    return this.parent();
  },

  get role() {
    return this._form.role;
  },

  get name() {
    return this._form.name;
  },

  get value() {
    return this._form.value;
  },

  get description() {
    return this._form.description;
  },

  get help() {
    return this._form.help;
  },

  get keyboardShortcut() {
    return this._form.keyboardShortcut;
  },

  get childCount() {
    return this._form.childCount;
  },

  get domNodeType() {
    return this._form.domNodeType;
  },

  get indexInParent() {
    return this._form.indexInParent;
  },

  get states() {
    return this._form.states;
  },

  get actions() {
    return this._form.actions;
  },

  get attributes() {
    return this._form.attributes;
  },

  form(form, detail) {
    if (detail === "actorid") {
      this.actorID = form;
      return;
    }

    this.actorID = form.actor;
    this._form = form;
  },

  nameChange: preEvent("name-change", function (name, parent, walker) {
    this._form.name = name;
    // Name change event affects the tree rendering, we fire this event on
    // accessibility walker as the point of interaction for UI.
    if (walker) {
      events.emit(walker, "name-change", this, parent);
    }
  }),

  valueChange: preEvent("value-change", function (value) {
    this._form.value = value;
  }),

  descriptionChange: preEvent("description-change", function (description) {
    this._form.description = description;
  }),

  helpChange: preEvent("help-change", function (help) {
    this._form.help = help;
  }),

  shortcutChange: preEvent("shortcut-change", function (keyboardShortcut) {
    this._form.keyboardShortcut = keyboardShortcut;
  }),

  reorder: preEvent("reorder", function (childCount, walker) {
    this._form.childCount = childCount;
    // Reorder event affects the tree rendering, we fire this event on
    // accessibility walker as the point of interaction for UI.
    if (walker) {
      events.emit(walker, "reorder", this);
    }
  }),

  textChange: preEvent("text-change", function (walker) {
    // Text event affects the tree rendering, we fire this event on
    // accessibility walker as the point of interaction for UI.
    if (walker) {
      events.emit(walker, "text-change", this);
    }
  }),

  indexInParentChange: preEvent("index-in-parent-change", function (indexInParent) {
    this._form.indexInParent = indexInParent;
  }),

  statesChange: preEvent("states-change", function (states) {
    this._form.states = states;
  }),

  actionsChange: preEvent("actions-change", function (actions) {
    this._form.actions = actions;
  }),

  attributesChange: preEvent("attributes-change", function (attributes) {
    this._form.attributes = attributes;
  })
});

const AccessibleWalkerFront = FrontClassWithSpec(accessibleWalkerSpec, {
  accessibleDestroy: preEvent("accessible-destroy", function (accessible) {
    accessible.destroy();
  }),

  form(json) {
    this.actorID = json.actor;
  },

  pick: custom(function (doFocus) {
    if (doFocus) {
      return this.pickAndFocus();
    }

    return this._pick();
  }, {
    impl: "_pick"
  })
});

const AccessibilityFront = FrontClassWithSpec(accessibilitySpec, {
  initialize(client, form) {
    Front.prototype.initialize.call(this, client, form);
    this.actorID = form.accessibilityActor;
    this.manage(this);
  },

  bootstrap: custom(function () {
    return this._bootstrap().then(state => {
      this.enabled = state.enabled;
      this.canBeEnabled = state.canBeEnabled;
      this.canBeDisabled = state.canBeDisabled;
    });
  }, {
    impl: "_bootstrap"
  }),

  init: preEvent("init", function () {
    this.enabled = true;
  }),

  shutdown: preEvent("shutdown", function () {
    this.enabled = false;
  }),

  canBeEnabled: preEvent("can-be-enabled-change", function (canBeEnabled) {
    this.canBeEnabled = canBeEnabled;
  }),

  canBeDisabled: preEvent("can-be-disabled-change", function (canBeDisabled) {
    this.canBeDisabled = canBeDisabled;
  })
});

exports.AccessibleFront = AccessibleFront;
exports.AccessibleWalkerFront = AccessibleWalkerFront;
exports.AccessibilityFront = AccessibilityFront;
PK
!<�/����@chrome/devtools/modules/devtools/shared/fronts/actor-registry.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { components } = require("chrome");
const Services = require("Services");
const { actorActorSpec, actorRegistrySpec } = require("devtools/shared/specs/actor-registry");
const protocol = require("devtools/shared/protocol");
const { custom } = protocol;

loader.lazyImporter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm");

const ActorActorFront = protocol.FrontClassWithSpec(actorActorSpec, {
  initialize: function (client, form) {
    protocol.Front.prototype.initialize.call(this, client, form);
  }
});

exports.ActorActorFront = ActorActorFront;

function request(uri) {
  return new Promise((resolve, reject) => {
    try {
      uri = Services.io.newURI(uri);
    } catch (e) {
      reject(e);
    }

    NetUtil.asyncFetch({
      uri,
      loadUsingSystemPrincipal: true,
    }, (stream, status, req) => {
      if (!components.isSuccessCode(status)) {
        reject(new Error("Request failed with status code = "
                         + status
                         + " after NetUtil.asyncFetch for url = "
                         + uri));
        return;
      }

      let source = NetUtil.readInputStreamToString(stream, stream.available());
      stream.close();
      resolve(source);
    });
  });
}

const ActorRegistryFront = protocol.FrontClassWithSpec(actorRegistrySpec, {
  initialize: function (client, form) {
    protocol.Front.prototype.initialize.call(this, client,
                                             { actor: form.actorRegistryActor });

    this.manage(this);
  },

  registerActor: custom(function (uri, options) {
    return request(uri, options)
      .then(sourceText => {
        return this._registerActor(sourceText, uri, options);
      });
  }, {
    impl: "_registerActor"
  })
});

exports.ActorRegistryFront = ActorRegistryFront;
PK
!<:���__8chrome/devtools/modules/devtools/shared/fronts/addons.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {addonsSpec} = require("devtools/shared/specs/addons");
const protocol = require("devtools/shared/protocol");

const AddonsFront = protocol.FrontClassWithSpec(addonsSpec, {
  initialize: function (client, {addonsActor}) {
    protocol.Front.prototype.initialize.call(this, client);
    this.actorID = addonsActor;
    this.manage(this);
  }
});

exports.AddonsFront = AddonsFront;
PK
!<�Lݳ�;chrome/devtools/modules/devtools/shared/fronts/animation.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  Front,
  FrontClassWithSpec,
  custom,
  preEvent
} = require("devtools/shared/protocol");
const {
  animationPlayerSpec,
  animationsSpec
} = require("devtools/shared/specs/animation");

const AnimationPlayerFront = FrontClassWithSpec(animationPlayerSpec, {
  initialize: function (conn, form, detail, ctx) {
    Front.prototype.initialize.call(this, conn, form, detail, ctx);

    this.state = {};
  },

  form: function (form, detail) {
    if (detail === "actorid") {
      this.actorID = form;
      return;
    }
    this._form = form;
    this.state = this.initialState;
  },

  destroy: function () {
    Front.prototype.destroy.call(this);
  },

  /**
   * If the AnimationsActor was given a reference to the WalkerActor previously
   * then calling this getter will return the animation target NodeFront.
   */
  get animationTargetNodeFront() {
    if (!this._form.animationTargetNodeActorID) {
      return null;
    }

    return this.conn.getActor(this._form.animationTargetNodeActorID);
  },

  /**
   * Getter for the initial state of the player. Up to date states can be
   * retrieved by calling the getCurrentState method.
   */
  get initialState() {
    return {
      type: this._form.type,
      startTime: this._form.startTime,
      previousStartTime: this._form.previousStartTime,
      currentTime: this._form.currentTime,
      playState: this._form.playState,
      playbackRate: this._form.playbackRate,
      name: this._form.name,
      duration: this._form.duration,
      delay: this._form.delay,
      endDelay: this._form.endDelay,
      iterationCount: this._form.iterationCount,
      iterationStart: this._form.iterationStart,
      easing: this._form.easing,
      fill: this._form.fill,
      direction: this._form.direction,
      animationTimingFunction: this._form.animationTimingFunction,
      isRunningOnCompositor: this._form.isRunningOnCompositor,
      propertyState: this._form.propertyState,
      documentCurrentTime: this._form.documentCurrentTime
    };
  },

  /**
   * Executed when the AnimationPlayerActor emits a "changed" event. Used to
   * update the local knowledge of the state.
   */
  onChanged: preEvent("changed", function (partialState) {
    let {state} = this.reconstructState(partialState);
    this.state = state;
  }),

  /**
   * Refresh the current state of this animation on the client from information
   * found on the server. Doesn't return anything, just stores the new state.
   */
  async refreshState() {
    let data = await this.getCurrentState();
    if (this.currentStateHasChanged) {
      this.state = data;
    }
  },

  /**
   * getCurrentState interceptor re-constructs incomplete states since the actor
   * only sends the values that have changed.
   */
  getCurrentState: custom(function () {
    this.currentStateHasChanged = false;
    return this._getCurrentState().then(partialData => {
      let {state, hasChanged} = this.reconstructState(partialData);
      this.currentStateHasChanged = hasChanged;
      return state;
    });
  }, {
    impl: "_getCurrentState"
  }),

  reconstructState: function (data) {
    let hasChanged = false;

    for (let key in this.state) {
      if (typeof data[key] === "undefined") {
        data[key] = this.state[key];
      } else if (data[key] !== this.state[key]) {
        hasChanged = true;
      }
    }

    return {state: data, hasChanged};
  }
});

exports.AnimationPlayerFront = AnimationPlayerFront;

const AnimationsFront = FrontClassWithSpec(animationsSpec, {
  initialize: function (client, {animationsActor}) {
    Front.prototype.initialize.call(this, client, {actor: animationsActor});
    this.manage(this);
  },

  destroy: function () {
    Front.prototype.destroy.call(this);
  }
});

exports.AnimationsFront = AnimationsFront;
PK
!<3;X^^>chrome/devtools/modules/devtools/shared/fronts/call-watcher.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { functionCallSpec, callWatcherSpec } = require("devtools/shared/specs/call-watcher");
const protocol = require("devtools/shared/protocol");

/**
 * The corresponding Front object for the FunctionCallActor.
 */
const FunctionCallFront = protocol.FrontClassWithSpec(functionCallSpec, {
  initialize: function (client, form) {
    protocol.Front.prototype.initialize.call(this, client, form);
  },

  /**
   * Adds some generic information directly to this instance,
   * to avoid extra roundtrips.
   */
  form: function (form) {
    this.actorID = form.actor;
    this.type = form.type;
    this.name = form.name;
    this.file = form.file;
    this.line = form.line;
    this.timestamp = form.timestamp;
    this.callerPreview = form.callerPreview;
    this.argsPreview = form.argsPreview;
    this.resultPreview = form.resultPreview;
  }
});

exports.FunctionCallFront = FunctionCallFront;

/**
 * The corresponding Front object for the CallWatcherActor.
 */
var CallWatcherFront =
exports.CallWatcherFront =
protocol.FrontClassWithSpec(callWatcherSpec, {
  initialize: function (client, { callWatcherActor }) {
    protocol.Front.prototype.initialize.call(this, client, { actor: callWatcherActor });
    this.manage(this);
  }
});

/**
 * Constants.
 */
CallWatcherFront.METHOD_FUNCTION = 0;
CallWatcherFront.GETTER_FUNCTION = 1;
CallWatcherFront.SETTER_FUNCTION = 2;

CallWatcherFront.KNOWN_METHODS = {};

CallWatcherFront.KNOWN_METHODS.CanvasRenderingContext2D = {
  drawWindow: {
    enums: new Set([6])
  },
};

CallWatcherFront.KNOWN_METHODS.WebGLRenderingContext = {
  activeTexture: {
    enums: new Set([0]),
  },
  bindBuffer: {
    enums: new Set([0]),
  },
  bindFramebuffer: {
    enums: new Set([0]),
  },
  bindRenderbuffer: {
    enums: new Set([0]),
  },
  bindTexture: {
    enums: new Set([0]),
  },
  blendEquation: {
    enums: new Set([0]),
  },
  blendEquationSeparate: {
    enums: new Set([0, 1]),
  },
  blendFunc: {
    enums: new Set([0, 1]),
  },
  blendFuncSeparate: {
    enums: new Set([0, 1, 2, 3]),
  },
  bufferData: {
    enums: new Set([0, 1, 2]),
  },
  bufferSubData: {
    enums: new Set([0, 1]),
  },
  checkFramebufferStatus: {
    enums: new Set([0]),
  },
  clear: {
    enums: new Set([0]),
  },
  compressedTexImage2D: {
    enums: new Set([0, 2]),
  },
  compressedTexSubImage2D: {
    enums: new Set([0, 6]),
  },
  copyTexImage2D: {
    enums: new Set([0, 2]),
  },
  copyTexSubImage2D: {
    enums: new Set([0]),
  },
  createShader: {
    enums: new Set([0]),
  },
  cullFace: {
    enums: new Set([0]),
  },
  depthFunc: {
    enums: new Set([0]),
  },
  disable: {
    enums: new Set([0]),
  },
  drawArrays: {
    enums: new Set([0]),
  },
  drawElements: {
    enums: new Set([0, 2]),
  },
  enable: {
    enums: new Set([0]),
  },
  framebufferRenderbuffer: {
    enums: new Set([0, 1, 2]),
  },
  framebufferTexture2D: {
    enums: new Set([0, 1, 2]),
  },
  frontFace: {
    enums: new Set([0]),
  },
  generateMipmap: {
    enums: new Set([0]),
  },
  getBufferParameter: {
    enums: new Set([0, 1]),
  },
  getParameter: {
    enums: new Set([0]),
  },
  getFramebufferAttachmentParameter: {
    enums: new Set([0, 1, 2]),
  },
  getProgramParameter: {
    enums: new Set([1]),
  },
  getRenderbufferParameter: {
    enums: new Set([0, 1]),
  },
  getShaderParameter: {
    enums: new Set([1]),
  },
  getShaderPrecisionFormat: {
    enums: new Set([0, 1]),
  },
  getTexParameter: {
    enums: new Set([0, 1]),
  },
  getVertexAttrib: {
    enums: new Set([1]),
  },
  getVertexAttribOffset: {
    enums: new Set([1]),
  },
  hint: {
    enums: new Set([0, 1]),
  },
  isEnabled: {
    enums: new Set([0]),
  },
  pixelStorei: {
    enums: new Set([0]),
  },
  readPixels: {
    enums: new Set([4, 5]),
  },
  renderbufferStorage: {
    enums: new Set([0, 1]),
  },
  stencilFunc: {
    enums: new Set([0]),
  },
  stencilFuncSeparate: {
    enums: new Set([0, 1]),
  },
  stencilMaskSeparate: {
    enums: new Set([0]),
  },
  stencilOp: {
    enums: new Set([0, 1, 2]),
  },
  stencilOpSeparate: {
    enums: new Set([0, 1, 2, 3]),
  },
  texImage2D: {
    enums: args => args.length > 6 ? new Set([0, 2, 6, 7]) : new Set([0, 2, 3, 4]),
  },
  texParameterf: {
    enums: new Set([0, 1]),
  },
  texParameteri: {
    enums: new Set([0, 1, 2]),
  },
  texSubImage2D: {
    enums: args => args.length === 9 ? new Set([0, 6, 7]) : new Set([0, 4, 5]),
  },
  vertexAttribPointer: {
    enums: new Set([2])
  },
};
PK
!<��T��
�
8chrome/devtools/modules/devtools/shared/fronts/canvas.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  frameSnapshotSpec,
  canvasSpec,
  CANVAS_CONTEXTS,
  ANIMATION_GENERATORS,
  LOOP_GENERATORS,
  DRAW_CALLS,
  INTERESTING_CALLS,
} = require("devtools/shared/specs/canvas");
const protocol = require("devtools/shared/protocol");
const promise = require("promise");

/**
 * The corresponding Front object for the FrameSnapshotActor.
 */
const FrameSnapshotFront = protocol.FrontClassWithSpec(frameSnapshotSpec, {
  initialize: function (client, form) {
    protocol.Front.prototype.initialize.call(this, client, form);
    this._animationFrameEndScreenshot = null;
    this._cachedScreenshots = new WeakMap();
  },

  /**
   * This implementation caches the animation frame end screenshot to optimize
   * frontend requests to `generateScreenshotFor`.
   */
  getOverview: protocol.custom(function () {
    return this._getOverview().then(data => {
      this._animationFrameEndScreenshot = data.screenshot;
      return data;
    });
  }, {
    impl: "_getOverview"
  }),

  /**
   * This implementation saves a roundtrip to the backend if the screenshot
   * was already generated and retrieved once.
   */
  generateScreenshotFor: protocol.custom(function (functionCall) {
    if (CanvasFront.ANIMATION_GENERATORS.has(functionCall.name) ||
        CanvasFront.LOOP_GENERATORS.has(functionCall.name)) {
      return promise.resolve(this._animationFrameEndScreenshot);
    }
    let cachedScreenshot = this._cachedScreenshots.get(functionCall);
    if (cachedScreenshot) {
      return cachedScreenshot;
    }
    let screenshot = this._generateScreenshotFor(functionCall);
    this._cachedScreenshots.set(functionCall, screenshot);
    return screenshot;
  }, {
    impl: "_generateScreenshotFor"
  })
});

exports.FrameSnapshotFront = FrameSnapshotFront;

/**
 * The corresponding Front object for the CanvasActor.
 */
const CanvasFront = protocol.FrontClassWithSpec(canvasSpec, {
  initialize: function (client, { canvasActor }) {
    protocol.Front.prototype.initialize.call(this, client, { actor: canvasActor });
    this.manage(this);
  }
});

/**
 * Constants.
 */
CanvasFront.CANVAS_CONTEXTS = new Set(CANVAS_CONTEXTS);
CanvasFront.ANIMATION_GENERATORS = new Set(ANIMATION_GENERATORS);
CanvasFront.LOOP_GENERATORS = new Set(LOOP_GENERATORS);
CanvasFront.DRAW_CALLS = new Set(DRAW_CALLS);
CanvasFront.INTERESTING_CALLS = new Set(INTERESTING_CALLS);
CanvasFront.THUMBNAIL_SIZE = 50;
CanvasFront.WEBGL_SCREENSHOT_MAX_HEIGHT = 256;
CanvasFront.INVALID_SNAPSHOT_IMAGE = {
  index: -1,
  width: 0,
  height: 0,
  pixels: []
};

exports.CanvasFront = CanvasFront;
PK
!<!���/�/@chrome/devtools/modules/devtools/shared/fronts/css-properties.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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, "CSS_PROPERTIES_DB",
  "devtools/shared/css/properties-db", true);

loader.lazyRequireGetter(this, "cssColors",
  "devtools/shared/css/color-db", true);

const { FrontClassWithSpec, Front } = require("devtools/shared/protocol");
const { cssPropertiesSpec } = require("devtools/shared/specs/css-properties");

/**
 * Build up a regular expression that matches a CSS variable token. This is an
 * ident token that starts with two dashes "--".
 *
 * https://www.w3.org/TR/css-syntax-3/#ident-token-diagram
 */
var NON_ASCII = "[^\\x00-\\x7F]";
var ESCAPE = "\\\\[^\n\r]";
var FIRST_CHAR = ["[_a-z]", NON_ASCII, ESCAPE].join("|");
var TRAILING_CHAR = ["[_a-z0-9-]", NON_ASCII, ESCAPE].join("|");
var IS_VARIABLE_TOKEN = new RegExp(`^--(${FIRST_CHAR})(${TRAILING_CHAR})*$`,
                                   "i");
/**
 * Check that this is a CSS variable.
 *
 * @param {String} input
 * @return {Boolean}
 */
function isCssVariable(input) {
  return !!input.match(IS_VARIABLE_TOKEN);
}

var cachedCssProperties = new WeakMap();

/**
 * The CssProperties front provides a mechanism to have a one-time asynchronous
 * load of a CSS properties database. This is then fed into the CssProperties
 * interface that provides synchronous methods for finding out what CSS
 * properties the current server supports.
 */
const CssPropertiesFront = FrontClassWithSpec(cssPropertiesSpec, {
  initialize: function (client, { cssPropertiesActor }) {
    Front.prototype.initialize.call(this, client, {actor: cssPropertiesActor});
    this.manage(this);
  }
});

/**
 * Query the feature supporting status in the featureSet.
 *
 * @param {Hashmap} featureSet the feature set hashmap
 * @param {String} feature the feature name string
 * @return {Boolean} has the feature or not
 */
function hasFeature(featureSet, feature) {
  if (feature in featureSet) {
    return featureSet[feature];
  }
  return false;
}

/**
 * Ask questions to a CSS database. This class does not care how the database
 * gets loaded in, only the questions that you can ask to it.
 * Prototype functions are bound to 'this' so they can be passed around as helper
 * functions.
 *
 * @param {Object} db
 *                 A database of CSS properties
 * @param {Object} inheritedList
 *                 The key is the property name, the value is whether or not
 *                 that property is inherited.
 */
function CssProperties(db) {
  this.properties = db.properties;
  this.pseudoElements = db.pseudoElements;

  // supported feature
  this.cssColor4ColorFunction = hasFeature(db.supportedFeature,
                                           "css-color-4-color-function");

  this.isKnown = this.isKnown.bind(this);
  this.isInherited = this.isInherited.bind(this);
  this.supportsType = this.supportsType.bind(this);
  this.isValidOnClient = this.isValidOnClient.bind(this);
  this.supportsCssColor4ColorFunction =
    this.supportsCssColor4ColorFunction.bind(this);

  // A weakly held dummy HTMLDivElement to test CSS properties on the client.
  this._dummyElements = new WeakMap();
}

CssProperties.prototype = {
  /**
   * Checks to see if the property is known by the browser. This function has
   * `this` already bound so that it can be passed around by reference.
   *
   * @param {String} property The property name to be checked.
   * @return {Boolean}
   */
  isKnown(property) {
    return !!this.properties[property] || isCssVariable(property);
  },

  /**
   * Quickly check if a CSS name/value combo is valid on the client.
   *
   * @param {String} Property name.
   * @param {String} Property value.
   * @param {Document} The client's document object.
   * @return {Boolean}
   */
  isValidOnClient(name, value, doc) {
    let dummyElement = this._dummyElements.get(doc);
    if (!dummyElement) {
      dummyElement = doc.createElement("div");
      this._dummyElements.set(doc, dummyElement);
    }

    // `!important` is not a valid value when setting a style declaration in the
    // CSS Object Model.
    const sanitizedValue = ("" + value).replace(/!\s*important\s*$/, "");

    // Test the style on the element.
    dummyElement.style[name] = sanitizedValue;
    const isValid = !!dummyElement.style[name];

    // Reset the state of the dummy element;
    dummyElement.style[name] = "";
    return isValid;
  },

  /**
   * Get a function that will check the validity of css name/values for a given document.
   * Useful for injecting isValidOnClient into components when needed.
   *
   * @param {Document} The client's document object.
   * @return {Function} this.isValidOnClient with the document pre-set.
   */
  getValidityChecker(doc) {
    return (name, value) => this.isValidOnClient(name, value, doc);
  },

  /**
   * Checks to see if the property is an inherited one.
   *
   * @param {String} property The property name to be checked.
   * @return {Boolean}
   */
  isInherited(property) {
    return (this.properties[property] && this.properties[property].isInherited) ||
            isCssVariable(property);
  },

  /**
   * Checks if the property supports the given CSS type.
   * CSS types should come from devtools/shared/css/properties-db.js' CSS_TYPES.
   *
   * @param {String} property The property to be checked.
   * @param {Number} type One of the type values from CSS_TYPES.
   * @return {Boolean}
   */
  supportsType(property, type) {
    return this.properties[property] && this.properties[property].supports.includes(type);
  },

  /**
   * Gets the CSS values for a given property name.
   *
   * @param {String} property The property to use.
   * @return {Array} An array of strings.
   */
  getValues(property) {
    return this.properties[property] ? this.properties[property].values : [];
  },

  /**
   * Gets the CSS property names.
   *
   * @return {Array} An array of strings.
   */
  getNames(property) {
    return Object.keys(this.properties);
  },

  /**
   * Return a list of subproperties for the given property.  If |name|
   * does not name a valid property, an empty array is returned.  If
   * the property is not a shorthand property, then array containing
   * just the property itself is returned.
   *
   * @param {String} name The property to query
   * @return {Array} An array of subproperty names.
   */
  getSubproperties(name) {
    if (this.isKnown(name)) {
      if (this.properties[name] && this.properties[name].subproperties) {
        return this.properties[name].subproperties;
      }
      return [name];
    }
    return [];
  },

  /**
   * Checking for the css-color-4 color function support.
   *
   * @return {Boolean} Return true if the server supports css-color-4 color function.
   */
  supportsCssColor4ColorFunction() {
    return this.cssColor4ColorFunction;
  },
};

/**
 * Create a CssProperties object with a fully loaded CSS database. The
 * CssProperties interface can be queried synchronously, but the initialization
 * is potentially async and should be handled up-front when the tool is created.
 *
 * The front is returned only with this function so that it can be destroyed
 * once the toolbox is destroyed.
 *
 * @param {Toolbox} The current toolbox.
 * @returns {Promise} Resolves to {cssProperties, cssPropertiesFront}.
 */
const initCssProperties = async function (toolbox) {
  const client = toolbox.target.client;
  if (cachedCssProperties.has(client)) {
    return cachedCssProperties.get(client);
  }

  let db, front;

  // Get the list dynamically if the cssProperties actor exists.
  if (toolbox.target.hasActor("cssProperties")) {
    front = CssPropertiesFront(client, toolbox.target.form);
    db = await front.getCSSDatabase();
  } else {
    // The target does not support this actor, so require a static list of supported
    // properties.
    db = CSS_PROPERTIES_DB;
  }

  const cssProperties = new CssProperties(normalizeCssData(db));
  cachedCssProperties.set(client, {cssProperties, front});
  return {cssProperties, front};
};

/**
 * Synchronously get a cached and initialized CssProperties.
 *
 * @param {Toolbox} The current toolbox.
 * @returns {CssProperties}
 */
function getCssProperties(toolbox) {
  if (!cachedCssProperties.has(toolbox.target.client)) {
    throw new Error("The CSS database has not been initialized, please make " +
                    "sure initCssDatabase was called once before for this " +
                    "toolbox.");
  }
  return cachedCssProperties.get(toolbox.target.client).cssProperties;
}

/**
 * Get a client-side CssProperties. This is useful for dependencies in tests, or parts
 * of the codebase that don't particularly need to match every known CSS property on
 * the target.
 * @return {CssProperties}
 */
function getClientCssProperties() {
  return new CssProperties(normalizeCssData(CSS_PROPERTIES_DB));
}

/**
 * Even if the target has the cssProperties actor, the returned data may not be in the
 * same shape or have all of the data we need. This normalizes the data and fills in
 * any missing information like color values.
 *
 * @return {Object} The normalized CSS database.
 */
function normalizeCssData(db) {
  // If there is a `from` attributes, it means that it comes from RDP
  // and it is not the client CSS_PROPERTIES_DB object.
  // (prevent comparing to CSS_PROPERTIES_DB to avoid loading client database)
  if (typeof (db.from) == "string") {
    // Firefox 49's getCSSDatabase() just returned the properties object, but
    // now it returns an object with multiple types of CSS information.
    if (!db.properties) {
      db = { properties: db };
    }

    let missingSupports = !db.properties.color.supports;
    let missingValues = !db.properties.color.values;
    let missingSubproperties = !db.properties.background.subproperties;
    let missingIsInherited = !db.properties.font.isInherited;

    let missingSomething = missingSupports || missingValues || missingSubproperties ||
      missingIsInherited;

    if (missingSomething) {
      for (let name in db.properties) {
        // Skip the current property if we can't find it in CSS_PROPERTIES_DB.
        if (typeof CSS_PROPERTIES_DB.properties[name] !== "object") {
          continue;
        }

        // Add "supports" information to the css properties if it's missing.
        if (missingSupports) {
          db.properties[name].supports = CSS_PROPERTIES_DB.properties[name].supports;
        }
        // Add "values" information to the css properties if it's missing.
        if (missingValues) {
          db.properties[name].values = CSS_PROPERTIES_DB.properties[name].values;
        }
        // Add "subproperties" information to the css properties if it's missing.
        if (missingSubproperties) {
          db.properties[name].subproperties =
            CSS_PROPERTIES_DB.properties[name].subproperties;
        }
        // Add "isInherited" information to the css properties if it's missing.
        if (missingIsInherited) {
          db.properties[name].isInherited =
            CSS_PROPERTIES_DB.properties[name].isInherited;
        }
      }
    }
  }

  reattachCssColorValues(db);

  // If there is no supportedFeature in db, create an empty one.
  if (!db.supportedFeature) {
    db.supportedFeature = {};
  }

  return db;
}

/**
 * Color values are omitted to save on space. Add them back here.
 * @param {Object} The CSS database.
 */
function reattachCssColorValues(db) {
  if (db.properties.color.values[0] === "COLOR") {
    const colors = Object.keys(cssColors);

    for (let name in db.properties) {
      const property = db.properties[name];
      // "values" can be undefined if {name} was not found in CSS_PROPERTIES_DB.
      if (property.values && property.values[0] === "COLOR") {
        property.values.shift();
        property.values = property.values.concat(colors).sort();
      }
    }
  }
}

module.exports = {
  CssPropertiesFront,
  CssProperties,
  getCssProperties,
  getClientCssProperties,
  initCssProperties,
  isCssVariable,
};
PK
!<^�^q�
�
=chrome/devtools/modules/devtools/shared/fronts/csscoverage.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {cssUsageSpec} = require("devtools/shared/specs/csscoverage");
const protocol = require("devtools/shared/protocol");
const {custom} = protocol;

const {LocalizationHelper} = require("devtools/shared/l10n");
const L10N = new LocalizationHelper("devtools/shared/locales/csscoverage.properties");

loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);

/**
 * Allow: let foo = l10n.lookup("csscoverageFoo");
 */
const l10n = exports.l10n = {
  lookup: (msg) => L10N.getStr(msg)
};

/**
 * Running more than one usage report at a time is probably bad for performance
 * and it isn't particularly useful, and it's confusing from a notification POV
 * so we only allow one.
 */
var isRunning = false;
var notification;
var target;
var chromeWindow;

/**
 * Front for CSSUsageActor
 */
const CSSUsageFront = protocol.FrontClassWithSpec(cssUsageSpec, {
  initialize: function (client, form) {
    protocol.Front.prototype.initialize.call(this, client, form);
    this.actorID = form.cssUsageActor;
    this.manage(this);
  },

  _onStateChange: protocol.preEvent("state-change", function (ev) {
    isRunning = ev.isRunning;
    ev.target = target;

    if (isRunning) {
      let gnb = chromeWindow.document.getElementById("global-notificationbox");
      notification = gnb.getNotificationWithValue("csscoverage-running");

      if (notification == null) {
        let notifyStop = reason => {
          if (reason == "removed") {
            this.stop();
          }
        };

        let msg = l10n.lookup("csscoverageRunningReply");
        notification = gnb.appendNotification(msg, "csscoverage-running",
                                              "",
                                              gnb.PRIORITY_INFO_HIGH,
                                              null,
                                              notifyStop);
      }
    } else {
      if (notification) {
        notification.remove();
        notification = undefined;
      }

      gDevTools.showToolbox(target, "styleeditor");
      target = undefined;
    }
  }),

  /**
   * Server-side start is above. Client-side start adds a notification box
   */
  start: custom(function (newChromeWindow, newTarget, noreload = false) {
    target = newTarget;
    chromeWindow = newChromeWindow;

    return this._start(noreload);
  }, {
    impl: "_start"
  }),

  /**
   * Server-side start is above. Client-side start adds a notification box
   */
  toggle: custom(function (newChromeWindow, newTarget) {
    target = newTarget;
    chromeWindow = newChromeWindow;

    return this._toggle();
  }, {
    impl: "_toggle"
  }),

  /**
   * We count STARTING and STOPPING as 'running'
   */
  isRunning: function () {
    return isRunning;
  }
});

exports.CSSUsageFront = CSSUsageFront;

const knownFronts = new WeakMap();

/**
 * Create a CSSUsageFront only when needed (returns a promise)
 * For notes on target.makeRemote(), see
 * https://bugzilla.mozilla.org/show_bug.cgi?id=1016330#c7
 */
exports.getUsage = function (trgt) {
  return trgt.makeRemote().then(() => {
    let front = knownFronts.get(trgt.client);
    if (front == null && trgt.form.cssUsageActor != null) {
      front = new CSSUsageFront(trgt.client, trgt.form);
      knownFronts.set(trgt.client, front);
    }
    return front;
  });
};
PK
!<��1*OO8chrome/devtools/modules/devtools/shared/fronts/device.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {Cu} = require("chrome");
const {deviceSpec} = require("devtools/shared/specs/device");
const protocol = require("devtools/shared/protocol");
const defer = require("devtools/shared/defer");

const DeviceFront = protocol.FrontClassWithSpec(deviceSpec, {
  initialize: function (client, form) {
    protocol.Front.prototype.initialize.call(this, client);
    this.actorID = form.deviceActor;
    this.manage(this);
  },

  screenshotToBlob: function () {
    return this.screenshotToDataURL().then(longstr => {
      return longstr.string().then(dataURL => {
        let deferred = defer();
        longstr.release().catch(Cu.reportError);
        let req = new XMLHttpRequest();
        req.open("GET", dataURL, true);
        req.responseType = "blob";
        req.onload = () => {
          deferred.resolve(req.response);
        };
        req.onerror = () => {
          deferred.reject(req.status);
        };
        req.send();
        return deferred.promise;
      });
    });
  },
});

const _knownDeviceFronts = new WeakMap();

/**
 * Retrieve the device front already created for the provided client, if available.
 */
exports.getKnownDeviceFront = function (client) {
  return _knownDeviceFronts.get(client);
};

/**
 * Only one DeviceFront is created for a given client, afterwards the instance is cached
 * and returned immediately.
 */
exports.getDeviceFront = function (client, form) {
  if (!form.deviceActor) {
    return null;
  }

  if (_knownDeviceFronts.has(client)) {
    return _knownDeviceFronts.get(client);
  }

  let front = new DeviceFront(client, form);
  _knownDeviceFronts.set(client, front);
  return front;
};
PK
!<gwB���;chrome/devtools/modules/devtools/shared/fronts/emulation.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Front, FrontClassWithSpec } = require("devtools/shared/protocol");
const { emulationSpec } = require("devtools/shared/specs/emulation");

/**
 * The corresponding Front object for the EmulationActor.
 */
const EmulationFront = FrontClassWithSpec(emulationSpec, {
  initialize: function (client, form) {
    Front.prototype.initialize.call(this, client);
    this.actorID = form.emulationActor;
    this.manage(this);
  },

  destroy: function () {
    Front.prototype.destroy.call(this);
  },
});

exports.EmulationFront = EmulationFront;
PK
!<�F',]]>chrome/devtools/modules/devtools/shared/fronts/eventlooplag.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Front, FrontClassWithSpec } = require("devtools/shared/protocol");
const { eventLoopLagSpec } = require("devtools/shared/specs/eventlooplag");

exports.EventLoopLagFront = FrontClassWithSpec(eventLoopLagSpec, {
  initialize: function (client, form) {
    Front.prototype.initialize.call(this, client);
    this.actorID = form.eventLoopLagActor;
    this.manage(this);
  },
});
PK
!<���_��;chrome/devtools/modules/devtools/shared/fronts/framerate.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Front, FrontClassWithSpec } = require("devtools/shared/protocol");
const { framerateSpec } = require("devtools/shared/specs/framerate");

/**
 * The corresponding Front object for the FramerateActor.
 */
var FramerateFront = exports.FramerateFront = FrontClassWithSpec(framerateSpec, {
  initialize: function (client, { framerateActor }) {
    Front.prototype.initialize.call(this, client, { actor: framerateActor });
    this.manage(this);
  }
});

exports.FramerateFront = FramerateFront;
PK
!<i�:996chrome/devtools/modules/devtools/shared/fronts/gcli.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Front, FrontClassWithSpec } = require("devtools/shared/protocol");
const { gcliSpec } = require("devtools/shared/specs/gcli");

/**
 *
 */
const GcliFront = exports.GcliFront = FrontClassWithSpec(gcliSpec, {
  initialize: function (client, tabForm) {
    Front.prototype.initialize.call(this, client);
    this.actorID = tabForm.gcliActor;

    // XXX: This is the first actor type in its hierarchy to use the protocol
    // library, so we're going to self-own on the client side for now.
    this.manage(this);
  },
});

// A cache of created fronts: WeakMap<Client, Front>
const knownFronts = new WeakMap();

/**
 * Create a GcliFront only when needed (returns a promise)
 * For notes on target.makeRemote(), see
 * https://bugzilla.mozilla.org/show_bug.cgi?id=1016330#c7
 */
exports.GcliFront.create = function (target) {
  return target.makeRemote().then(() => {
    let front = knownFronts.get(target.client);
    if (front == null && target.form.gcliActor != null) {
      front = new GcliFront(target.client, target.form);
      knownFronts.set(target.client, front);
    }
    return front;
  });
};
PK
!<�	G�((>chrome/devtools/modules/devtools/shared/fronts/highlighters.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { FrontClassWithSpec, custom } = require("devtools/shared/protocol");
const {
  customHighlighterSpec,
  highlighterSpec
} = require("devtools/shared/specs/highlighters");

const HighlighterFront = FrontClassWithSpec(highlighterSpec, {
  // Update the object given a form representation off the wire.
  form: function (json) {
    this.actorID = json.actor;
    // FF42+ HighlighterActors starts exposing custom form, with traits object
    this.traits = json.traits || {};
  },

  pick: custom(function (doFocus) {
    if (doFocus && this.pickAndFocus) {
      return this.pickAndFocus();
    }
    return this._pick();
  }, {
    impl: "_pick"
  })
});

exports.HighlighterFront = HighlighterFront;

const CustomHighlighterFront = FrontClassWithSpec(customHighlighterSpec, {});

exports.CustomHighlighterFront = CustomHighlighterFront;
PK
!<`��1@1@;chrome/devtools/modules/devtools/shared/fronts/inspector.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  Front,
  FrontClassWithSpec,
  custom,
  preEvent,
  types
} = require("devtools/shared/protocol.js");
const {
  inspectorSpec,
  walkerSpec
} = require("devtools/shared/specs/inspector");
const defer = require("devtools/shared/defer");
loader.lazyRequireGetter(this, "nodeConstants",
  "devtools/shared/dom-node-constants");
loader.lazyRequireGetter(this, "CommandUtils",
  "devtools/client/shared/developer-toolbar", true);

/**
 * Client side of the DOM walker.
 */
const WalkerFront = FrontClassWithSpec(walkerSpec, {
  // Set to true if cleanup should be requested after every mutation list.
  autoCleanup: true,

  /**
   * This is kept for backward-compatibility reasons with older remote target.
   * Targets previous to bug 916443
   */
  pick: custom(function () {
    return this._pick().then(response => {
      return response.node;
    });
  }, {impl: "_pick"}),

  initialize: function (client, form) {
    this._createRootNodePromise();
    Front.prototype.initialize.call(this, client, form);
    this._orphaned = new Set();
    this._retainedOrphans = new Set();
  },

  destroy: function () {
    Front.prototype.destroy.call(this);
  },

  // Update the object given a form representation off the wire.
  form: function (json) {
    this.actorID = json.actor;
    this.rootNode = types.getType("domnode").read(json.root, this);
    this._rootNodeDeferred.resolve(this.rootNode);
    // FF42+ the actor starts exposing traits
    this.traits = json.traits || {};
  },

  /**
   * Clients can use walker.rootNode to get the current root node of the
   * walker, but during a reload the root node might be null.  This
   * method returns a promise that will resolve to the root node when it is
   * set.
   */
  getRootNode: function () {
    return this._rootNodeDeferred.promise;
  },

  /**
   * Create the root node promise, triggering the "new-root" notification
   * on resolution.
   */
  _createRootNodePromise: function () {
    this._rootNodeDeferred = defer();
    this._rootNodeDeferred.promise.then(() => {
      this.emit("new-root");
    });
  },

  /**
   * When reading an actor form off the wire, we want to hook it up to its
   * parent front.  The protocol guarantees that the parent will be seen
   * by the client in either a previous or the current request.
   * So if we've already seen this parent return it, otherwise create
   * a bare-bones stand-in node.  The stand-in node will be updated
   * with a real form by the end of the deserialization.
   */
  ensureParentFront: function (id) {
    let front = this.get(id);
    if (front) {
      return front;
    }

    return types.getType("domnode").read({ actor: id }, this, "standin");
  },

  /**
   * See the documentation for WalkerActor.prototype.retainNode for
   * information on retained nodes.
   *
   * From the client's perspective, `retainNode` can fail if the node in
   * question is removed from the ownership tree before the `retainNode`
   * request reaches the server.  This can only happen if the client has
   * asked the server to release nodes but hasn't gotten a response
   * yet: Either a `releaseNode` request or a `getMutations` with `cleanup`
   * set is outstanding.
   *
   * If either of those requests is outstanding AND releases the retained
   * node, this request will fail with noSuchActor, but the ownership tree
   * will stay in a consistent state.
   *
   * Because the protocol guarantees that requests will be processed and
   * responses received in the order they were sent, we get the right
   * semantics by setting our local retained flag on the node only AFTER
   * a SUCCESSFUL retainNode call.
   */
  retainNode: custom(function (node) {
    return this._retainNode(node).then(() => {
      node.retained = true;
    });
  }, {
    impl: "_retainNode",
  }),

  unretainNode: custom(function (node) {
    return this._unretainNode(node).then(() => {
      node.retained = false;
      if (this._retainedOrphans.has(node)) {
        this._retainedOrphans.delete(node);
        this._releaseFront(node);
      }
    });
  }, {
    impl: "_unretainNode"
  }),

  releaseNode: custom(function (node, options = {}) {
    // NodeFront.destroy will destroy children in the ownership tree too,
    // mimicking what the server will do here.
    let actorID = node.actorID;
    this._releaseFront(node, !!options.force);
    return this._releaseNode({ actorID: actorID });
  }, {
    impl: "_releaseNode"
  }),

  findInspectingNode: custom(function () {
    return this._findInspectingNode().then(response => {
      return response.node;
    });
  }, {
    impl: "_findInspectingNode"
  }),

  querySelector: custom(function (queryNode, selector) {
    return this._querySelector(queryNode, selector).then(response => {
      return response.node;
    });
  }, {
    impl: "_querySelector"
  }),

  getNodeActorFromObjectActor: custom(function (objectActorID) {
    return this._getNodeActorFromObjectActor(objectActorID).then(response => {
      return response ? response.node : null;
    });
  }, {
    impl: "_getNodeActorFromObjectActor"
  }),

  getNodeActorFromWindowID: custom(function (windowID) {
    return this._getNodeActorFromWindowID(windowID).then(response => {
      return response ? response.node : null;
    });
  }, {
    impl: "_getNodeActorFromWindowID"
  }),

  getStyleSheetOwnerNode: custom(function (styleSheetActorID) {
    return this._getStyleSheetOwnerNode(styleSheetActorID).then(response => {
      return response ? response.node : null;
    });
  }, {
    impl: "_getStyleSheetOwnerNode"
  }),

  getNodeFromActor: custom(function (actorID, path) {
    return this._getNodeFromActor(actorID, path).then(response => {
      return response ? response.node : null;
    });
  }, {
    impl: "_getNodeFromActor"
  }),

  /*
   * Incrementally search the document for a given string.
   * For modern servers, results will be searched with using the WalkerActor
   * `search` function (includes tag names, attributes, and text contents).
   * Only 1 result is sent back, and calling the method again with the same
   * query will send the next result. When there are no more results to be sent
   * back, null is sent.
   * @param {String} query
   * @param {Object} options
   *    - "reverse": search backwards
   *    - "selectorOnly": treat input as a selector string (don't search text
   *                      tags, attributes, etc)
   */
  search: custom(async function (query, options = { }) {
    let nodeList;
    let searchType;
    let searchData = this.searchData = this.searchData || { };
    let selectorOnly = !!options.selectorOnly;

    if (selectorOnly) {
      searchType = "selector";
      nodeList = await this.multiFrameQuerySelectorAll(query);
    } else {
      searchType = "search";
      let result = await this._search(query, options);
      nodeList = result.list;
    }

    // If this is a new search, start at the beginning.
    if (searchData.query !== query ||
        searchData.selectorOnly !== selectorOnly) {
      searchData.selectorOnly = selectorOnly;
      searchData.query = query;
      searchData.index = -1;
    }

    if (!nodeList.length) {
      return null;
    }

    // Move search result cursor and cycle if necessary.
    searchData.index = options.reverse ? searchData.index - 1 :
                                         searchData.index + 1;
    if (searchData.index >= nodeList.length) {
      searchData.index = 0;
    }
    if (searchData.index < 0) {
      searchData.index = nodeList.length - 1;
    }

    // Send back the single node, along with any relevant search data
    let node = await nodeList.item(searchData.index);
    return {
      type: searchType,
      node: node,
      resultsLength: nodeList.length,
      resultsIndex: searchData.index,
    };
  }, {
    impl: "_search"
  }),

  _releaseFront: function (node, force) {
    if (node.retained && !force) {
      node.reparent(null);
      this._retainedOrphans.add(node);
      return;
    }

    if (node.retained) {
      // Forcing a removal.
      this._retainedOrphans.delete(node);
    }

    // Release any children
    for (let child of node.treeChildren()) {
      this._releaseFront(child, force);
    }

    // All children will have been removed from the node by this point.
    node.reparent(null);
    node.destroy();
  },

  /**
   * Get any unprocessed mutation records and process them.
   */
  getMutations: custom(function (options = {}) {
    return this._getMutations(options).then(mutations => {
      let emitMutations = [];
      for (let change of mutations) {
        // The target is only an actorID, get the associated front.
        let targetID;
        let targetFront;

        if (change.type === "newRoot") {
          // We may receive a new root without receiving any documentUnload
          // beforehand. Like when opening tools in middle of a document load.
          if (this.rootNode) {
            this._createRootNodePromise();
          }
          this.rootNode = types.getType("domnode").read(change.target, this);
          this._rootNodeDeferred.resolve(this.rootNode);
          targetID = this.rootNode.actorID;
          targetFront = this.rootNode;
        } else {
          targetID = change.target;
          targetFront = this.get(targetID);
        }

        if (!targetFront) {
          console.warn("Got a mutation for an unexpected actor: " + targetID +
            ", please file a bug on bugzilla.mozilla.org!");
          console.trace();
          continue;
        }

        let emittedMutation = Object.assign(change, { target: targetFront });

        if (change.type === "childList" ||
            change.type === "nativeAnonymousChildList") {
          // Update the ownership tree according to the mutation record.
          let addedFronts = [];
          let removedFronts = [];
          for (let removed of change.removed) {
            let removedFront = this.get(removed);
            if (!removedFront) {
              console.error("Got a removal of an actor we didn't know about: " +
                removed);
              continue;
            }
            // Remove from the ownership tree
            removedFront.reparent(null);

            // This node is orphaned unless we get it in the 'added' list
            // eventually.
            this._orphaned.add(removedFront);
            removedFronts.push(removedFront);
          }
          for (let added of change.added) {
            let addedFront = this.get(added);
            if (!addedFront) {
              console.error("Got an addition of an actor we didn't know " +
                "about: " + added);
              continue;
            }
            addedFront.reparent(targetFront);

            // The actor is reconnected to the ownership tree, unorphan
            // it.
            this._orphaned.delete(addedFront);
            addedFronts.push(addedFront);
          }

          // Before passing to users, replace the added and removed actor
          // ids with front in the mutation record.
          emittedMutation.added = addedFronts;
          emittedMutation.removed = removedFronts;

          // If this is coming from a DOM mutation, the actor's numChildren
          // was passed in. Otherwise, it is simulated from a frame load or
          // unload, so don't change the front's form.
          if ("numChildren" in change) {
            targetFront._form.numChildren = change.numChildren;
          }
        } else if (change.type === "frameLoad") {
          // Nothing we need to do here, except verify that we don't have any
          // document children, because we should have gotten a documentUnload
          // first.
          for (let child of targetFront.treeChildren()) {
            if (child.nodeType === nodeConstants.DOCUMENT_NODE) {
              console.warn("Got an unexpected frameLoad in the inspector, " +
                "please file a bug on bugzilla.mozilla.org!");
              console.trace();
            }
          }
        } else if (change.type === "documentUnload") {
          if (targetFront === this.rootNode) {
            this._createRootNodePromise();
          }

          // We try to give fronts instead of actorIDs, but these fronts need
          // to be destroyed now.
          emittedMutation.target = targetFront.actorID;
          emittedMutation.targetParent = targetFront.parentNode();

          // Release the document node and all of its children, even retained.
          this._releaseFront(targetFront, true);
        } else if (change.type === "unretained") {
          // Retained orphans were force-released without the intervention of
          // client (probably a navigated frame).
          for (let released of change.nodes) {
            let releasedFront = this.get(released);
            this._retainedOrphans.delete(released);
            this._releaseFront(releasedFront, true);
          }
        } else {
          targetFront.updateMutation(change);
        }

        // Update the inlineTextChild property of the target for a selected list of
        // mutation types.
        if (change.type === "inlineTextChild" ||
            change.type === "childList" ||
            change.type === "nativeAnonymousChildList") {
          if (change.inlineTextChild) {
            targetFront.inlineTextChild =
              types.getType("domnode").read(change.inlineTextChild, this);
          } else {
            targetFront.inlineTextChild = undefined;
          }
        }

        emitMutations.push(emittedMutation);
      }

      if (options.cleanup) {
        for (let node of this._orphaned) {
          // This will move retained nodes to this._retainedOrphans.
          this._releaseFront(node);
        }
        this._orphaned = new Set();
      }

      this.emit("mutations", emitMutations);
    });
  }, {
    impl: "_getMutations"
  }),

  /**
   * Handle the `new-mutations` notification by fetching the
   * available mutation records.
   */
  onMutations: preEvent("new-mutations", function () {
    // Fetch and process the mutations.
    this.getMutations({cleanup: this.autoCleanup}).catch(() => {});
  }),

  isLocal: function () {
    return !!this.conn._transport._serverConnection;
  },

  removeNode: custom(async function (node) {
    let previousSibling = await this.previousSibling(node);
    let nextSibling = await this._removeNode(node);
    return {
      previousSibling: previousSibling,
      nextSibling: nextSibling,
    };
  }, {
    impl: "_removeNode"
  }),
});

exports.WalkerFront = WalkerFront;

/**
 * Client side of the inspector actor, which is used to create
 * inspector-related actors, including the walker.
 */
var InspectorFront = FrontClassWithSpec(inspectorSpec, {
  initialize: function (client, tabForm) {
    Front.prototype.initialize.call(this, client);
    this.actorID = tabForm.inspectorActor;

    // XXX: This is the first actor type in its hierarchy to use the protocol
    // library, so we're going to self-own on the client side for now.
    this.manage(this);
  },

  destroy: function () {
    delete this.walker;
    Front.prototype.destroy.call(this);
  },

  getWalker: custom(function (options = {}) {
    return this._getWalker(options).then(walker => {
      this.walker = walker;
      return walker;
    });
  }, {
    impl: "_getWalker"
  }),

  getPageStyle: custom(function () {
    return this._getPageStyle().then(pageStyle => {
      // We need a walker to understand node references from the
      // node style.
      if (this.walker) {
        return pageStyle;
      }
      return this.getWalker().then(() => {
        return pageStyle;
      });
    });
  }, {
    impl: "_getPageStyle"
  }),

  pickColorFromPage: custom(async function (toolbox, options) {
    if (toolbox) {
      // If the eyedropper was already started using the gcli command, hide it so we don't
      // end up with 2 instances of the eyedropper on the page.
      CommandUtils.executeOnTarget(toolbox.target, "eyedropper --hide");
    }

    await this._pickColorFromPage(options);
  }, {
    impl: "_pickColorFromPage"
  })
});

exports.InspectorFront = InspectorFront;
PK
!<9���8chrome/devtools/modules/devtools/shared/fronts/layout.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { FrontClassWithSpec } = require("devtools/shared/protocol");
const { flexboxSpec, gridSpec, layoutSpec } = require("devtools/shared/specs/layout");

const FlexboxFront = FrontClassWithSpec(flexboxSpec, {
  form: function (form, detail) {
    if (detail === "actorid") {
      this.actorID = form;
      return;
    }
    this._form = form;
  },

  /**
   * In some cases, the FlexboxActor already knows the NodeActor ID of the node where the
   * flexbox is located. In such cases, this getter returns the NodeFront for it.
   */
  get containerNodeFront() {
    if (!this._form.containerNodeActorID) {
      return null;
    }

    return this.conn.getActor(this._form.containerNodeActorID);
  },
});

const GridFront = FrontClassWithSpec(gridSpec, {
  form: function (form, detail) {
    if (detail === "actorid") {
      this.actorID = form;
      return;
    }
    this._form = form;
  },

  /**
   * In some cases, the GridActor already knows the NodeActor ID of the node where the
   * grid is located. In such cases, this getter returns the NodeFront for it.
   */
  get containerNodeFront() {
    if (!this._form.containerNodeActorID) {
      return null;
    }

    return this.conn.getActor(this._form.containerNodeActorID);
  },

  /**
   * Get the text direction of the grid container.
   * Added in Firefox 60.
   */
  get direction() {
    if (!this._form.direction) {
      return "ltr";
    }

    return this._form.direction;
  },

  /**
   * Getter for the grid fragments data.
   */
  get gridFragments() {
    return this._form.gridFragments;
  },

  /**
   * Get the writing mode of the grid container.
   * Added in Firefox 60.
   */
  get writingMode() {
    if (!this._form.writingMode) {
      return "horizontal-tb";
    }

    return this._form.writingMode;
  },
});

const LayoutFront = FrontClassWithSpec(layoutSpec, {});

exports.FlexboxFront = FlexboxFront;
exports.GridFront = GridFront;
exports.LayoutFront = LayoutFront;
PK
!<X�\��8chrome/devtools/modules/devtools/shared/fronts/memory.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { memorySpec } = require("devtools/shared/specs/memory");
const protocol = require("devtools/shared/protocol");

loader.lazyRequireGetter(this, "FileUtils",
                         "resource://gre/modules/FileUtils.jsm", true);
loader.lazyRequireGetter(this, "HeapSnapshotFileUtils",
                         "devtools/shared/heapsnapshot/HeapSnapshotFileUtils");

const MemoryFront = protocol.FrontClassWithSpec(memorySpec, {
  initialize: function (client, form, rootForm = null) {
    protocol.Front.prototype.initialize.call(this, client, form);
    this._client = client;
    this.actorID = form.memoryActor;
    this.heapSnapshotFileActorID = rootForm
      ? rootForm.heapSnapshotFileActor
      : null;
    this.manage(this);
  },

  /**
   * Save a heap snapshot, transfer it from the server to the client if the
   * server and client do not share a file system, and return the local file
   * path to the heap snapshot.
   *
   * Note that this is safe to call for actors inside sandoxed child processes,
   * as we jump through the correct IPDL hoops.
   *
   * @params Boolean options.forceCopy
   *         Always force a bulk data copy of the saved heap snapshot, even when
   *         the server and client share a file system.
   *
   * @params {Object|undefined} options.boundaries
   *         The boundaries for the heap snapshot. See
   *         ChromeUtils.webidl for more details.
   *
   * @returns Promise<String>
   */
  saveHeapSnapshot: protocol.custom(async function (options = {}) {
    const snapshotId = await this._saveHeapSnapshotImpl(options.boundaries);

    if (!options.forceCopy &&
        (await HeapSnapshotFileUtils.haveHeapSnapshotTempFile(snapshotId))) {
      return HeapSnapshotFileUtils.getHeapSnapshotTempFilePath(snapshotId);
    }

    return this.transferHeapSnapshot(snapshotId);
  }, {
    impl: "_saveHeapSnapshotImpl"
  }),

  /**
   * Given that we have taken a heap snapshot with the given id, transfer the
   * heap snapshot file to the client. The path to the client's local file is
   * returned.
   *
   * @param {String} snapshotId
   *
   * @returns Promise<String>
   */
  transferHeapSnapshot: protocol.custom(function (snapshotId) {
    if (!this.heapSnapshotFileActorID) {
      throw new Error("MemoryFront initialized without a rootForm");
    }

    const request = this._client.request({
      to: this.heapSnapshotFileActorID,
      type: "transferHeapSnapshot",
      snapshotId
    });

    return new Promise((resolve, reject) => {
      const outFilePath =
        HeapSnapshotFileUtils.getNewUniqueHeapSnapshotTempFilePath();
      const outFile = new FileUtils.File(outFilePath);

      const outFileStream = FileUtils.openSafeFileOutputStream(outFile);
      request.on("bulk-reply", async function ({ copyTo }) {
        await copyTo(outFileStream);
        FileUtils.closeSafeFileOutputStream(outFileStream);
        resolve(outFilePath);
      });
    });
  })
});

exports.MemoryFront = MemoryFront;
PK
!<%G���1�16chrome/devtools/modules/devtools/shared/fronts/node.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  Front,
  FrontClassWithSpec,
  custom,
  types
} = require("devtools/shared/protocol.js");

const {
  nodeSpec,
  nodeListSpec,
} = require("devtools/shared/specs/node");

const promise = require("promise");
const { SimpleStringFront } = require("devtools/shared/fronts/string");

loader.lazyRequireGetter(this, "nodeConstants",
  "devtools/shared/dom-node-constants");

const HIDDEN_CLASS = "__fx-devtools-hide-shortcut__";

/**
 * Client side of a node list as returned by querySelectorAll()
 */
const NodeListFront = FrontClassWithSpec(nodeListSpec, {
  initialize: function (client, form) {
    Front.prototype.initialize.call(this, client, form);
  },

  destroy: function () {
    Front.prototype.destroy.call(this);
  },

  marshallPool: function () {
    return this.parent();
  },

  // Update the object given a form representation off the wire.
  form: function (json) {
    this.length = json.length;
  },

  item: custom(function (index) {
    return this._item(index).then(response => {
      return response.node;
    });
  }, {
    impl: "_item"
  }),

  items: custom(function (start, end) {
    return this._items(start, end).then(response => {
      return response.nodes;
    });
  }, {
    impl: "_items"
  })
});

exports.NodeListFront = NodeListFront;

/**
 * Convenience API for building a list of attribute modifications
 * for the `modifyAttributes` request.
 */
class AttributeModificationList {
  constructor(node) {
    this.node = node;
    this.modifications = [];
  }

  apply() {
    let ret = this.node.modifyAttributes(this.modifications);
    return ret;
  }

  destroy() {
    this.node = null;
    this.modification = null;
  }

  setAttributeNS(ns, name, value) {
    this.modifications.push({
      attributeNamespace: ns,
      attributeName: name,
      newValue: value
    });
  }

  setAttribute(name, value) {
    this.setAttributeNS(undefined, name, value);
  }

  removeAttributeNS(ns, name) {
    this.setAttributeNS(ns, name, undefined);
  }

  removeAttribute(name) {
    this.setAttributeNS(undefined, name, undefined);
  }
}

/**
 * Client side of the node actor.
 *
 * Node fronts are strored in a tree that mirrors the DOM tree on the
 * server, but with a few key differences:
 *  - Not all children will be necessary loaded for each node.
 *  - The order of children isn't guaranteed to be the same as the DOM.
 * Children are stored in a doubly-linked list, to make addition/removal
 * and traversal quick.
 *
 * Due to the order/incompleteness of the child list, it is safe to use
 * the parent node from clients, but the `children` request should be used
 * to traverse children.
 */
const NodeFront = FrontClassWithSpec(nodeSpec, {
  initialize: function (conn, form, detail, ctx) {
    // The parent node
    this._parent = null;
    // The first child of this node.
    this._child = null;
    // The next sibling of this node.
    this._next = null;
    // The previous sibling of this node.
    this._prev = null;
    Front.prototype.initialize.call(this, conn, form, detail, ctx);
  },

  /**
   * Destroy a node front.  The node must have been removed from the
   * ownership tree before this is called, unless the whole walker front
   * is being destroyed.
   */
  destroy: function () {
    Front.prototype.destroy.call(this);
  },

  // Update the object given a form representation off the wire.
  form: function (form, detail, ctx) {
    if (detail === "actorid") {
      this.actorID = form;
      return;
    }

    // backward-compatibility: shortValue indicates we are connected to old server
    if (form.shortValue) {
      // If the value is not complete, set nodeValue to null, it will be fetched
      // when calling getNodeValue()
      form.nodeValue = form.incompleteValue ? null : form.shortValue;
    }

    // Shallow copy of the form.  We could just store a reference, but
    // eventually we'll want to update some of the data.
    this._form = Object.assign({}, form);
    this._form.attrs = this._form.attrs ? this._form.attrs.slice() : [];

    if (form.parent) {
      // Get the owner actor for this actor (the walker), and find the
      // parent node of this actor from it, creating a standin node if
      // necessary.
      let parentNodeFront = ctx.marshallPool().ensureParentFront(form.parent);
      this.reparent(parentNodeFront);
    }

    if (form.inlineTextChild) {
      this.inlineTextChild =
        types.getType("domnode").read(form.inlineTextChild, ctx);
    } else {
      this.inlineTextChild = undefined;
    }
  },

  /**
   * Returns the parent NodeFront for this NodeFront.
   */
  parentNode: function () {
    return this._parent;
  },

  /**
   * Process a mutation entry as returned from the walker's `getMutations`
   * request.  Only tries to handle changes of the node's contents
   * themselves (character data and attribute changes), the walker itself
   * will keep the ownership tree up to date.
   */
  updateMutation: function (change) {
    if (change.type === "attributes") {
      // We'll need to lazily reparse the attributes after this change.
      this._attrMap = undefined;

      // Update any already-existing attributes.
      let found = false;
      for (let i = 0; i < this.attributes.length; i++) {
        let attr = this.attributes[i];
        if (attr.name == change.attributeName &&
            attr.namespace == change.attributeNamespace) {
          if (change.newValue !== null) {
            attr.value = change.newValue;
          } else {
            this.attributes.splice(i, 1);
          }
          found = true;
          break;
        }
      }
      // This is a new attribute. The null check is because of Bug 1192270,
      // in the case of a newly added then removed attribute
      if (!found && change.newValue !== null) {
        this.attributes.push({
          name: change.attributeName,
          namespace: change.attributeNamespace,
          value: change.newValue
        });
      }
    } else if (change.type === "characterData") {
      this._form.nodeValue = change.newValue;
    } else if (change.type === "pseudoClassLock") {
      this._form.pseudoClassLocks = change.pseudoClassLocks;
    } else if (change.type === "events") {
      this._form.hasEventListeners = change.hasEventListeners;
    }
  },

  // Some accessors to make NodeFront feel more like an nsIDOMNode

  get id() {
    return this.getAttribute("id");
  },

  get nodeType() {
    return this._form.nodeType;
  },
  get namespaceURI() {
    return this._form.namespaceURI;
  },
  get nodeName() {
    return this._form.nodeName;
  },
  get displayName() {
    let {displayName, nodeName} = this._form;

    // Keep `nodeName.toLowerCase()` for backward compatibility
    return displayName || nodeName.toLowerCase();
  },
  get doctypeString() {
    return "<!DOCTYPE " + this._form.name +
     (this._form.publicId ? " PUBLIC \"" + this._form.publicId + "\"" : "") +
     (this._form.systemId ? " \"" + this._form.systemId + "\"" : "") +
     ">";
  },

  get baseURI() {
    return this._form.baseURI;
  },

  get className() {
    return this.getAttribute("class") || "";
  },

  get hasChildren() {
    return this._form.numChildren > 0;
  },
  get numChildren() {
    return this._form.numChildren;
  },
  get hasEventListeners() {
    return this._form.hasEventListeners;
  },

  get isBeforePseudoElement() {
    return this._form.isBeforePseudoElement;
  },
  get isAfterPseudoElement() {
    return this._form.isAfterPseudoElement;
  },
  get isPseudoElement() {
    return this.isBeforePseudoElement || this.isAfterPseudoElement;
  },
  get isAnonymous() {
    return this._form.isAnonymous;
  },
  get isInHTMLDocument() {
    return this._form.isInHTMLDocument;
  },
  get tagName() {
    return this.nodeType === nodeConstants.ELEMENT_NODE ? this.nodeName : null;
  },

  get isDocumentElement() {
    return !!this._form.isDocumentElement;
  },

  // doctype properties
  get name() {
    return this._form.name;
  },
  get publicId() {
    return this._form.publicId;
  },
  get systemId() {
    return this._form.systemId;
  },

  getAttribute: function (name) {
    let attr = this._getAttribute(name);
    return attr ? attr.value : null;
  },
  hasAttribute: function (name) {
    this._cacheAttributes();
    return (name in this._attrMap);
  },

  get hidden() {
    let cls = this.getAttribute("class");
    return cls && cls.indexOf(HIDDEN_CLASS) > -1;
  },

  get attributes() {
    return this._form.attrs;
  },

  get pseudoClassLocks() {
    return this._form.pseudoClassLocks || [];
  },
  hasPseudoClassLock: function (pseudo) {
    return this.pseudoClassLocks.some(locked => locked === pseudo);
  },

  get displayType() {
    return this._form.displayType;
  },

  get isDisplayed() {
    // The NodeActor's form contains the isDisplayed information as a boolean
    // starting from FF32. Before that, the property is missing
    return "isDisplayed" in this._form ? this._form.isDisplayed : true;
  },

  get isTreeDisplayed() {
    let parent = this;
    while (parent) {
      if (!parent.isDisplayed) {
        return false;
      }
      parent = parent.parentNode();
    }
    return true;
  },

  getNodeValue: custom(function () {
    // backward-compatibility: if nodevalue is null and shortValue is defined, the actual
    // value of the node needs to be fetched on the server.
    if (this._form.nodeValue === null && this._form.shortValue) {
      return this._getNodeValue();
    }

    let str = this._form.nodeValue || "";
    return promise.resolve(new SimpleStringFront(str));
  }, {
    impl: "_getNodeValue"
  }),

  // Accessors for custom form properties.

  getFormProperty: function (name) {
    return this._form.props ? this._form.props[name] : null;
  },

  hasFormProperty: function (name) {
    return this._form.props ? (name in this._form.props) : null;
  },

  get formProperties() {
    return this._form.props;
  },

  /**
   * Return a new AttributeModificationList for this node.
   */
  startModifyingAttributes: function () {
    return new AttributeModificationList(this);
  },

  _cacheAttributes: function () {
    if (typeof this._attrMap != "undefined") {
      return;
    }
    this._attrMap = {};
    for (let attr of this.attributes) {
      this._attrMap[attr.name] = attr;
    }
  },

  _getAttribute: function (name) {
    this._cacheAttributes();
    return this._attrMap[name] || undefined;
  },

  /**
   * Set this node's parent.  Note that the children saved in
   * this tree are unordered and incomplete, so shouldn't be used
   * instead of a `children` request.
   */
  reparent: function (parent) {
    if (this._parent === parent) {
      return;
    }

    if (this._parent && this._parent._child === this) {
      this._parent._child = this._next;
    }
    if (this._prev) {
      this._prev._next = this._next;
    }
    if (this._next) {
      this._next._prev = this._prev;
    }
    this._next = null;
    this._prev = null;
    this._parent = parent;
    if (!parent) {
      // Subtree is disconnected, we're done
      return;
    }
    this._next = parent._child;
    if (this._next) {
      this._next._prev = this;
    }
    parent._child = this;
  },

  /**
   * Return all the known children of this node.
   */
  treeChildren: function () {
    let ret = [];
    for (let child = this._child; child != null; child = child._next) {
      ret.push(child);
    }
    return ret;
  },

  /**
   * Do we use a local target?
   * Useful to know if a rawNode is available or not.
   *
   * This will, one day, be removed. External code should
   * not need to know if the target is remote or not.
   */
  isLocalToBeDeprecated: function () {
    return !!this.conn._transport._serverConnection;
  },

  /**
   * Get an nsIDOMNode for the given node front.  This only works locally,
   * and is only intended as a stopgap during the transition to the remote
   * protocol.  If you depend on this you're likely to break soon.
   */
  rawNode: function (rawNode) {
    if (!this.isLocalToBeDeprecated()) {
      console.warn("Tried to use rawNode on a remote connection.");
      return null;
    }
    const { DebuggerServer } = require("devtools/server/main");
    let actor = DebuggerServer.searchAllConnectionsForActor(this.actorID);
    if (!actor) {
      // Can happen if we try to get the raw node for an already-expired
      // actor.
      return null;
    }
    return actor.rawNode;
  }
});

exports.NodeFront = NodeFront;
PK
!<���_::6chrome/devtools/modules/devtools/shared/fronts/perf.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { FrontClassWithSpec, Front } = require("devtools/shared/protocol");
const { perfSpec } = require("devtools/shared/specs/perf");

exports.PerfFront = FrontClassWithSpec(perfSpec, {
  initialize: function (client, form) {
    Front.prototype.initialize.call(this, client, form);
    this.actorID = form.perfActor;
    this.manage(this);
  }
});
PK
!<����Gchrome/devtools/modules/devtools/shared/fronts/performance-recording.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Front, FrontClassWithSpec } = require("devtools/shared/protocol");
const { performanceRecordingSpec } = require("devtools/shared/specs/performance-recording");

loader.lazyRequireGetter(this, "PerformanceIO",
  "devtools/client/performance/modules/io");
loader.lazyRequireGetter(this, "PerformanceRecordingCommon",
  "devtools/shared/performance/recording-common", true);
loader.lazyRequireGetter(this, "RecordingUtils",
  "devtools/shared/performance/recording-utils");

/**
 * This can be used on older Profiler implementations, but the methods cannot
 * be changed -- you must introduce a new method, and detect the server.
 */
const PerformanceRecordingFront = FrontClassWithSpec(performanceRecordingSpec,
Object.assign({
  form: function (form, detail) {
    if (detail === "actorid") {
      this.actorID = form;
      return;
    }
    this.actorID = form.actor;
    this._form = form;
    this._configuration = form.configuration;
    this._startingBufferStatus = form.startingBufferStatus;
    this._console = form.console;
    this._label = form.label;
    this._startTime = form.startTime;
    this._localStartTime = form.localStartTime;
    this._recording = form.recording;
    this._completed = form.completed;
    this._duration = form.duration;

    if (form.finalizedData) {
      this._profile = form.profile;
      this._systemHost = form.systemHost;
      this._systemClient = form.systemClient;
    }

    // Sort again on the client side if we're using realtime markers and the recording
    // just finished. This is because GC/Compositing markers can come into the array out
    // of order with the other markers, leading to strange collapsing in waterfall view.
    if (this._completed && !this._markersSorted) {
      this._markers = this._markers.sort((a, b) => (a.start > b.start));
      this._markersSorted = true;
    }
  },

  initialize: function (client, form, config) {
    Front.prototype.initialize.call(this, client, form);
    this._markers = [];
    this._frames = [];
    this._memory = [];
    this._ticks = [];
    this._allocations = { sites: [], timestamps: [], frames: [], sizes: [] };
  },

  destroy: function () {
    Front.prototype.destroy.call(this);
  },

  /**
   * Saves the current recording to a file.
   *
   * @param nsIFile file
   *        The file to stream the data into.
   */
  exportRecording: function (file) {
    let recordingData = this.getAllData();
    return PerformanceIO.saveRecordingToFile(recordingData, file);
  },

  /**
   * Fired whenever the PerformanceFront emits markers, memory or ticks.
   */
  _addTimelineData: function (eventName, data) {
    let config = this.getConfiguration();

    switch (eventName) {
      // Accumulate timeline markers into an array. Furthermore, the timestamps
      // do not have a zero epoch, so offset all of them by the start time.
      case "markers": {
        if (!config.withMarkers) {
          break;
        }
        let { markers } = data;
        RecordingUtils.offsetMarkerTimes(markers, this._startTime);
        RecordingUtils.pushAll(this._markers, markers);
        break;
      }
      // Accumulate stack frames into an array.
      case "frames": {
        if (!config.withMarkers) {
          break;
        }
        let { frames } = data;
        RecordingUtils.pushAll(this._frames, frames);
        break;
      }
      // Accumulate memory measurements into an array. Furthermore, the timestamp
      // does not have a zero epoch, so offset it by the actor's start time.
      case "memory": {
        if (!config.withMemory) {
          break;
        }
        let { delta, measurement } = data;
        this._memory.push({
          delta: delta - this._startTime,
          value: measurement.total / 1024 / 1024
        });
        break;
      }
      // Save the accumulated refresh driver ticks.
      case "ticks": {
        if (!config.withTicks) {
          break;
        }
        let { timestamps } = data;
        this._ticks = timestamps;
        break;
      }
      // Accumulate allocation sites into an array.
      case "allocations": {
        if (!config.withAllocations) {
          break;
        }
        let {
          allocations: sites,
          allocationsTimestamps: timestamps,
          allocationSizes: sizes,
          frames,
        } = data;

        RecordingUtils.offsetAndScaleTimestamps(timestamps, this._startTime);
        RecordingUtils.pushAll(this._allocations.sites, sites);
        RecordingUtils.pushAll(this._allocations.timestamps, timestamps);
        RecordingUtils.pushAll(this._allocations.frames, frames);
        RecordingUtils.pushAll(this._allocations.sizes, sizes);
        break;
      }
    }
  },

  toString: () => "[object PerformanceRecordingFront]"
}, PerformanceRecordingCommon));

exports.PerformanceRecordingFront = PerformanceRecordingFront;
PK
!<�ى���=chrome/devtools/modules/devtools/shared/fronts/performance.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Cu } = require("chrome");
const { Front, FrontClassWithSpec, custom, preEvent } = require("devtools/shared/protocol");
const { PerformanceRecordingFront } = require("devtools/shared/fronts/performance-recording");
const { performanceSpec } = require("devtools/shared/specs/performance");

loader.lazyRequireGetter(this, "PerformanceIO",
  "devtools/client/performance/modules/io");
loader.lazyRequireGetter(this, "getSystemInfo",
  "devtools/shared/system", true);

const PerformanceFront = FrontClassWithSpec(performanceSpec, {
  initialize: function (client, form) {
    Front.prototype.initialize.call(this, client, form);
    this.actorID = form.performanceActor;
    this.manage(this);
  },

  destroy: function () {
    Front.prototype.destroy.call(this);
  },

  /**
   * Conenct to the server, and handle once-off tasks like storing traits
   * or system info.
   */
  connect: custom(async function () {
    let systemClient = await getSystemInfo();
    let { traits } = await this._connect({ systemClient });
    this._traits = traits;

    return this._traits;
  }, {
    impl: "_connect"
  }),

  get traits() {
    if (!this._traits) {
      Cu.reportError("Cannot access traits of PerformanceFront before " +
                     "calling `connect()`.");
    }
    return this._traits;
  },

  /**
   * Pass in a PerformanceRecording and get a normalized value from 0 to 1 of how much
   * of this recording's lifetime remains without being overwritten.
   *
   * @param {PerformanceRecording} recording
   * @return {number?}
   */
  getBufferUsageForRecording: function (recording) {
    if (!recording.isRecording()) {
      return void 0;
    }
    let {
      position: currentPosition,
      totalSize,
      generation: currentGeneration
    } = this._currentBufferStatus;
    let {
      position: origPosition,
      generation: origGeneration
    } = recording.getStartingBufferStatus();

    let normalizedCurrent = (totalSize * (currentGeneration - origGeneration)) +
                            currentPosition;
    let percent = (normalizedCurrent - origPosition) / totalSize;

    // Clamp between 0 and 1; can get negative percentage values when a new
    // recording starts and the currentBufferStatus has not yet been updated. Rather
    // than fetching another status update, just clamp to 0, and this will be updated
    // on the next profiler-status event.
    if (percent < 0) {
      return 0;
    } else if (percent > 1) {
      return 1;
    }

    return percent;
  },

  /**
   * Loads a recording from a file.
   *
   * @param {nsIFile} file
   *        The file to import the data from.
   * @return {Promise<PerformanceRecordingFront>}
   */
  importRecording: function (file) {
    return PerformanceIO.loadRecordingFromFile(file).then(recordingData => {
      let model = new PerformanceRecordingFront();
      model._imported = true;
      model._label = recordingData.label || "";
      model._duration = recordingData.duration;
      model._markers = recordingData.markers;
      model._frames = recordingData.frames;
      model._memory = recordingData.memory;
      model._ticks = recordingData.ticks;
      model._allocations = recordingData.allocations;
      model._profile = recordingData.profile;
      model._configuration = recordingData.configuration || {};
      model._systemHost = recordingData.systemHost;
      model._systemClient = recordingData.systemClient;
      return model;
    });
  },

  /**
   * Store profiler status when the position has been update so we can
   * calculate recording's buffer percentage usage after emitting the event.
   */
  _onProfilerStatus: preEvent("profiler-status", function (data) {
    this._currentBufferStatus = data;
  }),

  /**
   * For all PerformanceRecordings that are recording, and needing realtime markers,
   * apply the timeline data to the front PerformanceRecording (so we only have one event
   * for each timeline data chunk as they could be shared amongst several recordings).
   */
  _onTimelineEvent: preEvent("timeline-data", function (type, data, recordings) {
    for (let recording of recordings) {
      recording._addTimelineData(type, data);
    }
  }),
});

exports.PerformanceFront = PerformanceFront;

exports.createPerformanceFront = function createPerformanceFront(target) {
  return new PerformanceFront(target.client, target.form);
};
PK
!<1U�3��<chrome/devtools/modules/devtools/shared/fronts/preference.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {preferenceSpec} = require("devtools/shared/specs/preference");
const protocol = require("devtools/shared/protocol");

const PreferenceFront = protocol.FrontClassWithSpec(preferenceSpec, {
  initialize: function (client, form) {
    protocol.Front.prototype.initialize.call(this, client);
    this.actorID = form.preferenceActor;
    this.manage(this);
  },
});

const _knownPreferenceFronts = new WeakMap();

exports.getPreferenceFront = function (client, form) {
  if (!form.preferenceActor) {
    return null;
  }

  if (_knownPreferenceFronts.has(client)) {
    return _knownPreferenceFronts.get(client);
  }

  let front = new PreferenceFront(client, form);
  _knownPreferenceFronts.set(client, front);
  return front;
};
PK
!<�{T��:chrome/devtools/modules/devtools/shared/fronts/promises.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  Front,
  FrontClassWithSpec,
} = require("devtools/shared/protocol");
const { promisesSpec } = require("devtools/shared/specs/promises");

/**
 * PromisesFront, the front for the PromisesActor.
 */
const PromisesFront = FrontClassWithSpec(promisesSpec, {
  initialize: function (client, form) {
    Front.prototype.initialize.call(this, client, form);
    this.actorID = form.promisesActor;
    this.manage(this);
  },

  destroy: function () {
    Front.prototype.destroy.call(this);
  }
});

exports.PromisesFront = PromisesFront;
PK
!<jIZ}}8chrome/devtools/modules/devtools/shared/fronts/reflow.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {reflowSpec} = require("devtools/shared/specs/reflow");
const protocol = require("devtools/shared/protocol");

/**
 * Usage example of the reflow front:
 *
 * let front = ReflowFront(toolbox.target.client, toolbox.target.form);
 * front.on("reflows", this._onReflows);
 * front.start();
 * // now wait for events to come
 */
const ReflowFront = protocol.FrontClassWithSpec(reflowSpec, {
  initialize: function (client, {reflowActor}) {
    protocol.Front.prototype.initialize.call(this, client, {actor: reflowActor});
    this.manage(this);
  },

  destroy: function () {
    protocol.Front.prototype.destroy.call(this);
  },
});

exports.ReflowFront = ReflowFront;
PK
!<DL気�9chrome/devtools/modules/devtools/shared/fronts/storage.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 protocol = require("devtools/shared/protocol");
const specs = require("devtools/shared/specs/storage");

for (let childSpec of Object.values(specs.childSpecs)) {
  protocol.FrontClassWithSpec(childSpec, {
    form(form, detail) {
      if (detail === "actorid") {
        this.actorID = form;
        return null;
      }

      this.actorID = form.actor;
      this.hosts = form.hosts;
      return null;
    }
  });
}

const StorageFront = protocol.FrontClassWithSpec(specs.storageSpec, {
  initialize(client, tabForm) {
    protocol.Front.prototype.initialize.call(this, client);
    this.actorID = tabForm.storageActor;
    this.manage(this);
  }
});

exports.StorageFront = StorageFront;
PK
!<�ҳ���8chrome/devtools/modules/devtools/shared/fronts/string.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {DebuggerServer} = require("devtools/server/main");
const promise = require("promise");
const {longStringSpec, SimpleStringFront} = require("devtools/shared/specs/string");
const protocol = require("devtools/shared/protocol");

const LongStringFront = protocol.FrontClassWithSpec(longStringSpec, {
  initialize: function (client) {
    protocol.Front.prototype.initialize.call(this, client);
  },

  destroy: function () {
    this.initial = null;
    this.length = null;
    this.strPromise = null;
    protocol.Front.prototype.destroy.call(this);
  },

  form: function (form) {
    this.actorID = form.actor;
    this.initial = form.initial;
    this.length = form.length;
  },

  string: function () {
    if (!this.strPromise) {
      let promiseRest = (thusFar) => {
        if (thusFar.length === this.length) {
          return promise.resolve(thusFar);
        }
        return this.substring(thusFar.length,
                              thusFar.length + DebuggerServer.LONG_STRING_READ_LENGTH)
          .then((next) => promiseRest(thusFar + next));
      };

      this.strPromise = promiseRest(this.initial);
    }
    return this.strPromise;
  }
});

exports.LongStringFront = LongStringFront;
exports.SimpleStringFront = SimpleStringFront;
PK
!</v���0�08chrome/devtools/modules/devtools/shared/fronts/styles.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  Front,
  FrontClassWithSpec,
  custom,
  preEvent
} = require("devtools/shared/protocol");
const {
  pageStyleSpec,
  styleRuleSpec
} = require("devtools/shared/specs/styles");
const promise = require("promise");

loader.lazyRequireGetter(this, "RuleRewriter",
  "devtools/shared/css/parsing-utils", true);

/**
 * PageStyleFront, the front object for the PageStyleActor
 */
const PageStyleFront = FrontClassWithSpec(pageStyleSpec, {
  initialize: function (conn, form, ctx, detail) {
    Front.prototype.initialize.call(this, conn, form, ctx, detail);
    this.inspector = this.parent();
  },

  form: function (form, detail) {
    if (detail === "actorid") {
      this.actorID = form;
      return;
    }
    this._form = form;
  },

  destroy: function () {
    Front.prototype.destroy.call(this);
  },

  get walker() {
    return this.inspector.walker;
  },

  get supportsAuthoredStyles() {
    return this._form.traits && this._form.traits.authoredStyles;
  },

  get supportsFontVariations() {
    return this._form.traits && this._form.traits.fontVariations;
  },

  getMatchedSelectors: custom(function (node, property, options) {
    return this._getMatchedSelectors(node, property, options).then(ret => {
      return ret.matched;
    });
  }, {
    impl: "_getMatchedSelectors"
  }),

  getApplied: custom(async function (node, options = {}) {
    // If the getApplied method doesn't recreate the style cache itself, this
    // means a call to cssLogic.highlight is required before trying to access
    // the applied rules. Issue a request to getLayout if this is the case.
    // See https://bugzilla.mozilla.org/show_bug.cgi?id=1103993#c16.
    if (!this._form.traits || !this._form.traits.getAppliedCreatesStyleCache) {
      await this.getLayout(node);
    }
    let ret = await this._getApplied(node, options);
    return ret.entries;
  }, {
    impl: "_getApplied"
  }),

  addNewRule: custom(function (node, pseudoClasses) {
    let addPromise;
    if (this.supportsAuthoredStyles) {
      addPromise = this._addNewRule(node, pseudoClasses, true);
    } else {
      addPromise = this._addNewRule(node, pseudoClasses);
    }
    return addPromise.then(ret => {
      return ret.entries[0];
    });
  }, {
    impl: "_addNewRule"
  })
});

exports.PageStyleFront = PageStyleFront;

/**
 * StyleRuleFront, the front for the StyleRule actor.
 */
const StyleRuleFront = FrontClassWithSpec(styleRuleSpec, {
  initialize: function (client, form, ctx, detail) {
    Front.prototype.initialize.call(this, client, form, ctx, detail);
  },

  destroy: function () {
    Front.prototype.destroy.call(this);
  },

  form: function (form, detail) {
    if (detail === "actorid") {
      this.actorID = form;
      return;
    }
    this.actorID = form.actor;
    this._form = form;
    if (this._mediaText) {
      this._mediaText = null;
    }
  },

  /**
   * Ensure _form is updated when location-changed is emitted.
   */
  _locationChangedPre: preEvent("location-changed", function (line, column) {
    this._clearOriginalLocation();
    this._form.line = line;
    this._form.column = column;
  }),

  /**
   * Return a new RuleModificationList or RuleRewriter for this node.
   * A RuleRewriter will be returned when the rule's canSetRuleText
   * trait is true; otherwise a RuleModificationList will be
   * returned.
   *
   * @param {CssPropertiesFront} cssProperties
   *                             This is needed by the RuleRewriter.
   * @return {RuleModificationList}
   */
  startModifyingProperties: function (cssProperties) {
    if (this.canSetRuleText) {
      return new RuleRewriter(cssProperties.isKnown, this, this.authoredText);
    }
    return new RuleModificationList(this);
  },

  get type() {
    return this._form.type;
  },
  get line() {
    return this._form.line || -1;
  },
  get column() {
    return this._form.column || -1;
  },
  get cssText() {
    return this._form.cssText;
  },
  get authoredText() {
    return this._form.authoredText || this._form.cssText;
  },
  get declarations() {
    return this._form.declarations || [];
  },
  get keyText() {
    return this._form.keyText;
  },
  get name() {
    return this._form.name;
  },
  get selectors() {
    return this._form.selectors;
  },
  get media() {
    return this._form.media;
  },
  get mediaText() {
    if (!this._form.media) {
      return null;
    }
    if (this._mediaText) {
      return this._mediaText;
    }
    this._mediaText = this.media.join(", ");
    return this._mediaText;
  },

  get parentRule() {
    return this.conn.getActor(this._form.parentRule);
  },

  get parentStyleSheet() {
    return this.conn.getActor(this._form.parentStyleSheet);
  },

  get element() {
    return this.conn.getActor(this._form.element);
  },

  get href() {
    if (this._form.href) {
      return this._form.href;
    }
    let sheet = this.parentStyleSheet;
    return sheet ? sheet.href : "";
  },

  get nodeHref() {
    let sheet = this.parentStyleSheet;
    return sheet ? sheet.nodeHref : "";
  },

  get supportsModifySelectorUnmatched() {
    return this._form.traits && this._form.traits.modifySelectorUnmatched;
  },

  get canSetRuleText() {
    return this._form.traits && this._form.traits.canSetRuleText;
  },

  get location() {
    return {
      source: this.parentStyleSheet,
      href: this.href,
      line: this.line,
      column: this.column
    };
  },

  _clearOriginalLocation: function () {
    this._originalLocation = null;
  },

  getOriginalLocation: function () {
    if (this._originalLocation) {
      return promise.resolve(this._originalLocation);
    }
    let parentSheet = this.parentStyleSheet;
    if (!parentSheet) {
      // This rule doesn't belong to a stylesheet so it is an inline style.
      // Inline styles do not have any mediaText so we can return early.
      return promise.resolve(this.location);
    }
    return parentSheet.getOriginalLocation(this.line, this.column)
      .then(({ fromSourceMap, source, line, column }) => {
        let location = {
          href: source,
          line: line,
          column: column,
          mediaText: this.mediaText
        };
        if (fromSourceMap === false) {
          location.source = this.parentStyleSheet;
        }
        if (!source) {
          location.href = this.href;
        }
        this._originalLocation = location;
        return location;
      });
  },

  modifySelector: custom(async function (node, value) {
    let response;
    if (this.supportsModifySelectorUnmatched) {
      // If the debugee supports adding unmatched rules (post FF41)
      if (this.canSetRuleText) {
        response = await this.modifySelector2(node, value, true);
      } else {
        response = await this.modifySelector2(node, value);
      }
    } else {
      response = await this._modifySelector(value);
    }

    if (response.ruleProps) {
      response.ruleProps = response.ruleProps.entries[0];
    }
    return response;
  }, {
    impl: "_modifySelector"
  }),

  setRuleText: custom(function (newText) {
    this._form.authoredText = newText;
    return this._setRuleText(newText);
  }, {
    impl: "_setRuleText"
  })
});

exports.StyleRuleFront = StyleRuleFront;

/**
 * Convenience API for building a list of attribute modifications
 * for the `modifyProperties` request.  A RuleModificationList holds a
 * list of modifications that will be applied to a StyleRuleActor.
 * The modifications are processed in the order in which they are
 * added to the RuleModificationList.
 *
 * Objects of this type expose the same API as @see RuleRewriter.
 * This lets the inspector use (mostly) the same code, regardless of
 * whether the server implements setRuleText.
 */
class RuleModificationList {
  /**
   * Initialize a RuleModificationList.
   * @param {StyleRuleFront} rule the associated rule
   */
  constructor(rule) {
    this.rule = rule;
    this.modifications = [];
  }

  /**
   * Apply the modifications in this object to the associated rule.
   *
   * @return {Promise} A promise which will be resolved when the modifications
   *         are complete; @see StyleRuleActor.modifyProperties.
   */
  apply() {
    return this.rule.modifyProperties(this.modifications);
  }

  /**
   * Add a "set" entry to the modification list.
   *
   * @param {Number} index index of the property in the rule.
   *                       This can be -1 in the case where
   *                       the rule does not support setRuleText;
   *                       generally for setting properties
   *                       on an element's style.
   * @param {String} name the property's name
   * @param {String} value the property's value
   * @param {String} priority the property's priority, either the empty
   *                          string or "important"
   */
  setProperty(index, name, value, priority) {
    this.modifications.push({
      type: "set",
      name: name,
      value: value,
      priority: priority
    });
  }

  /**
   * Add a "remove" entry to the modification list.
   *
   * @param {Number} index index of the property in the rule.
   *                       This can be -1 in the case where
   *                       the rule does not support setRuleText;
   *                       generally for setting properties
   *                       on an element's style.
   * @param {String} name the name of the property to remove
   */
  removeProperty(index, name) {
    this.modifications.push({
      type: "remove",
      name: name
    });
  }

  /**
   * Rename a property.  This implementation acts like
   * |removeProperty|, because |setRuleText| is not available.
   *
   * @param {Number} index index of the property in the rule.
   *                       This can be -1 in the case where
   *                       the rule does not support setRuleText;
   *                       generally for setting properties
   *                       on an element's style.
   * @param {String} name current name of the property
   *
   * This parameter is also passed, but as it is not used in this
   * implementation, it is omitted.  It is documented here as this
   * code also defined the interface implemented by @see RuleRewriter.
   * @param {String} newName new name of the property
   */
  renameProperty(index, name) {
    this.removeProperty(index, name);
  }

  /**
   * Enable or disable a property.  This implementation acts like
   * |removeProperty| when disabling, or a no-op when enabling,
   * because |setRuleText| is not available.
   *
   * @param {Number} index index of the property in the rule.
   *                       This can be -1 in the case where
   *                       the rule does not support setRuleText;
   *                       generally for setting properties
   *                       on an element's style.
   * @param {String} name current name of the property
   * @param {Boolean} isEnabled true if the property should be enabled;
   *                        false if it should be disabled
   */
  setPropertyEnabled(index, name, isEnabled) {
    if (!isEnabled) {
      this.removeProperty(index, name);
    }
  }

  /**
   * Create a new property.  This implementation does nothing, because
   * |setRuleText| is not available.
   *
   * These parameters are passed, but as they are not used in this
   * implementation, they are omitted.  They are documented here as
   * this code also defined the interface implemented by @see
   * RuleRewriter.
   *
   * @param {Number} index index of the property in the rule.
   *                       This can be -1 in the case where
   *                       the rule does not support setRuleText;
   *                       generally for setting properties
   *                       on an element's style.
   * @param {String} name name of the new property
   * @param {String} value value of the new property
   * @param {String} priority priority of the new property; either
   *                          the empty string or "important"
   * @param {Boolean} enabled True if the new property should be
   *                          enabled, false if disabled
   */
  createProperty() {
    // Nothing.
  }
}
PK
!<����=chrome/devtools/modules/devtools/shared/fronts/stylesheets.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Front, FrontClassWithSpec } = require("devtools/shared/protocol");
const {
  mediaRuleSpec,
  styleSheetSpec,
  styleSheetsSpec
} = require("devtools/shared/specs/stylesheets");
const promise = require("promise");

loader.lazyRequireGetter(this, "getIndentationFromPrefs",
  "devtools/shared/indentation", true);
loader.lazyRequireGetter(this, "getIndentationFromString",
  "devtools/shared/indentation", true);

/**
 * Corresponding client-side front for a MediaRuleActor.
 */
const MediaRuleFront = FrontClassWithSpec(mediaRuleSpec, {
  initialize: function (client, form) {
    Front.prototype.initialize.call(this, client, form);

    this._onMatchesChange = this._onMatchesChange.bind(this);
    this.on("matches-change", this._onMatchesChange);
  },

  _onMatchesChange: function (matches) {
    this._form.matches = matches;
  },

  form: function (form, detail) {
    if (detail === "actorid") {
      this.actorID = form;
      return;
    }
    this.actorID = form.actor;
    this._form = form;
  },

  get mediaText() {
    return this._form.mediaText;
  },
  get conditionText() {
    return this._form.conditionText;
  },
  get matches() {
    return this._form.matches;
  },
  get line() {
    return this._form.line || -1;
  },
  get column() {
    return this._form.column || -1;
  },
  get parentStyleSheet() {
    return this.conn.getActor(this._form.parentStyleSheet);
  }
});

exports.MediaRuleFront = MediaRuleFront;

/**
 * StyleSheetFront is the client-side counterpart to a StyleSheetActor.
 */
const StyleSheetFront = FrontClassWithSpec(styleSheetSpec, {
  initialize: function (conn, form) {
    Front.prototype.initialize.call(this, conn, form);

    this._onPropertyChange = this._onPropertyChange.bind(this);
    this.on("property-change", this._onPropertyChange);
  },

  destroy: function () {
    this.off("property-change", this._onPropertyChange);
    Front.prototype.destroy.call(this);
  },

  _onPropertyChange: function (property, value) {
    this._form[property] = value;
  },

  form: function (form, detail) {
    if (detail === "actorid") {
      this.actorID = form;
      return;
    }
    this.actorID = form.actor;
    this._form = form;
  },

  get href() {
    return this._form.href;
  },
  get nodeHref() {
    return this._form.nodeHref;
  },
  get disabled() {
    return !!this._form.disabled;
  },
  get title() {
    return this._form.title;
  },
  get isSystem() {
    return this._form.system;
  },
  get styleSheetIndex() {
    return this._form.styleSheetIndex;
  },
  get ruleCount() {
    return this._form.ruleCount;
  },
  get sourceMapURL() {
    return this._form.sourceMapURL;
  },

  /**
   * Get the indentation to use for edits to this style sheet.
   *
   * @return {Promise} A promise that will resolve to a string that
   * should be used to indent a block in this style sheet.
   */
  guessIndentation: function () {
    let prefIndent = getIndentationFromPrefs();
    if (prefIndent) {
      let {indentUnit, indentWithTabs} = prefIndent;
      return promise.resolve(indentWithTabs ? "\t" : " ".repeat(indentUnit));
    }

    return (async function () {
      let longStr = await this.getText();
      let source = await longStr.string();

      let {indentUnit, indentWithTabs} = getIndentationFromString(source);

      return indentWithTabs ? "\t" : " ".repeat(indentUnit);
    }.bind(this))();
  }
});

exports.StyleSheetFront = StyleSheetFront;

/**
 * The corresponding Front object for the StyleSheetsActor.
 */
const StyleSheetsFront = FrontClassWithSpec(styleSheetsSpec, {
  initialize: function (client, tabForm) {
    Front.prototype.initialize.call(this, client);
    this.actorID = tabForm.styleSheetsActor;
    this.manage(this);
  }
});

exports.StyleSheetsFront = StyleSheetsFront;
PK
!<����:chrome/devtools/modules/devtools/shared/fronts/timeline.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  Front,
  FrontClassWithSpec,
} = require("devtools/shared/protocol");
const { timelineSpec } = require("devtools/shared/specs/timeline");

/**
 * TimelineFront, the front for the TimelineActor.
 */
const TimelineFront = FrontClassWithSpec(timelineSpec, {
  initialize: function (client, { timelineActor }) {
    Front.prototype.initialize.call(this, client, { actor: timelineActor });
    this.manage(this);
  },
  destroy: function () {
    Front.prototype.destroy.call(this);
  },
});

exports.TimelineFront = TimelineFront;
PK
!<�x`��
�
:chrome/devtools/modules/devtools/shared/fronts/webaudio.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  audionodeSpec,
  webAudioSpec,
  AUTOMATION_METHODS,
  NODE_CREATION_METHODS,
  NODE_ROUTING_METHODS,
} = require("devtools/shared/specs/webaudio");
const protocol = require("devtools/shared/protocol");
loader.lazyRequireGetter(this, "AUDIO_NODE_DEFINITION",
  "devtools/server/actors/utils/audionodes.json");

/**
 * The corresponding Front object for the AudioNodeActor.
 *
 * @attribute {String} type
 *            The type of audio node, like "OscillatorNode", "MediaElementAudioSourceNode"
 * @attribute {Boolean} source
 *            Boolean indicating if the node is a source node, like BufferSourceNode,
 *            MediaElementAudioSourceNode, OscillatorNode, etc.
 * @attribute {Boolean} bypassable
 *            Boolean indicating if the audio node is bypassable (splitter,
 *            merger and destination nodes, for example, are not)
 */
const AudioNodeFront = protocol.FrontClassWithSpec(audionodeSpec, {
  form: function (form, detail) {
    if (detail === "actorid") {
      this.actorID = form;
      return;
    }

    this.actorID = form.actor;
    this.type = form.type;
    this.source = form.source;
    this.bypassable = form.bypassable;
  },

  initialize: function (client, form) {
    protocol.Front.prototype.initialize.call(this, client, form);
    // if we were manually passed a form, this was created manually and
    // needs to own itself for now.
    if (form) {
      this.manage(this);
    }
  }
});

exports.AudioNodeFront = AudioNodeFront;

/**
 * The corresponding Front object for the WebAudioActor.
 */
const WebAudioFront = protocol.FrontClassWithSpec(webAudioSpec, {
  initialize: function (client, { webaudioActor }) {
    protocol.Front.prototype.initialize.call(this, client, { actor: webaudioActor });
    this.manage(this);
  },

  /**
   * If connecting to older geckos (<Fx43), where audio node actor's do not
   * contain `type`, `source` and `bypassable` properties, fetch
   * them manually here.
   */
  _onCreateNode: protocol.preEvent("create-node", function (audionode) {
    if (!audionode.type) {
      return audionode.getType().then(type => {
        audionode.type = type;
        audionode.source = !!AUDIO_NODE_DEFINITION[type].source;
        audionode.bypassable = !AUDIO_NODE_DEFINITION[type].unbypassable;
      });
    }
    return null;
  }),
});

WebAudioFront.AUTOMATION_METHODS = new Set(AUTOMATION_METHODS);
WebAudioFront.NODE_CREATION_METHODS = new Set(NODE_CREATION_METHODS);
WebAudioFront.NODE_ROUTING_METHODS = new Set(NODE_ROUTING_METHODS);

exports.WebAudioFront = WebAudioFront;
PK
!<{���{{Ochrome/devtools/modules/devtools/shared/fronts/webextension-inspected-window.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  webExtensionInspectedWindowSpec,
} = require("devtools/shared/specs/webextension-inspected-window");

const protocol = require("devtools/shared/protocol");

/**
 * The corresponding Front object for the WebExtensionInspectedWindowActor.
 */
const WebExtensionInspectedWindowFront = protocol.FrontClassWithSpec(
  webExtensionInspectedWindowSpec,
  {
    initialize: function (client, { webExtensionInspectedWindowActor }) {
      protocol.Front.prototype.initialize.call(this, client, {
        actor: webExtensionInspectedWindowActor
      });
      this.manage(this);
    }
  }
);

exports.WebExtensionInspectedWindowFront = WebExtensionInspectedWindowFront;
PK
!<�1�H��7chrome/devtools/modules/devtools/shared/fronts/webgl.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  shaderSpec,
  programSpec,
  webGLSpec,
} = require("devtools/shared/specs/webgl");
const protocol = require("devtools/shared/protocol");

/**
 * The corresponding Front object for the ShaderActor.
 */
const ShaderFront = protocol.FrontClassWithSpec(shaderSpec, {
  initialize: function (client, form) {
    protocol.Front.prototype.initialize.call(this, client, form);
  }
});

exports.ShaderFront = ShaderFront;

/**
 * The corresponding Front object for the ProgramActor.
 */
const ProgramFront = protocol.FrontClassWithSpec(programSpec, {
  initialize: function (client, form) {
    protocol.Front.prototype.initialize.call(this, client, form);
  }
});

exports.ProgramFront = ProgramFront;

/**
 * The corresponding Front object for the WebGLActor.
 */
const WebGLFront = protocol.FrontClassWithSpec(webGLSpec, {
  initialize: function (client, { webglActor }) {
    protocol.Front.prototype.initialize.call(this, client, { actor: webglActor });
    this.manage(this);
  }
});

exports.WebGLFront = WebGLFront;
PK
!<�ƨ��	�	=chrome/devtools/modules/devtools/shared/gcli/command-state.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/shared/old-event-emitter");

const getTargetId = ({tab}) => tab.linkedBrowser.outerWindowID;
const enabledCommands = new Map();

/**
 * The `CommandState` is a singleton that provides utility methods to keep the commands'
 * state in sync between the toolbox, the toolbar and the content.
 */
const CommandState = EventEmitter.decorate({
  /**
   * Returns if a command is enabled on a given target.
   *
   * @param {Object} target
   *                  The target object must have a tab's reference.
   * @param {String} command
   *                  The command's name used in gcli.
   * @ returns {Boolean} returns `false` if the command is not enabled for the target
   *                    given, or if the target given hasn't a tab; `true` otherwise.
   */
  isEnabledForTarget(target, command) {
    if (!target.tab || !enabledCommands.has(command)) {
      return false;
    }

    return enabledCommands.get(command).has(getTargetId(target));
  },

  /**
   * Enables a command on a given target.
   * Emits a "changed" event to notify potential observers about the new commands state.
   *
   * @param {Object} target
   *                  The target object must have a tab's reference.
   * @param {String} command
   *                  The command's name used in gcli.
   */
  enableForTarget(target, command) {
    if (!target.tab) {
      return;
    }

    if (!enabledCommands.has(command)) {
      enabledCommands.set(command, new Set());
    }

    enabledCommands.get(command).add(getTargetId(target));

    CommandState.emit("changed", {target, command});
  },

  /**
   * Disabled a command on a given target.
   * Emits a "changed" event to notify potential observers about the new commands state.
   *
   * @param {Object} target
   *                  The target object must have a tab's reference.
   * @param {String} command
   *                  The command's name used in gcli.
   */
  disableForTarget(target, command) {
    if (!target.tab || !enabledCommands.has(command)) {
      return;
    }

    enabledCommands.get(command).delete(getTargetId(target));

    CommandState.emit("changed", {target, command});
  },
});
exports.CommandState = CommandState;

PK
!<H�N'N'>chrome/devtools/modules/devtools/shared/gcli/commands/addon.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

/**
 * You can't require the AddonManager in a child process, but GCLI wants to
 * check for 'items' in all processes, so we return empty array if the
 * AddonManager is not available
 */
function getAddonManager() {
  try {
    return {
      AddonManager: require("resource://gre/modules/AddonManager.jsm").AddonManager,
      addonManagerActive: true
    };
  } catch (ex) {
    // Fake up an AddonManager just enough to let the file load
    return {
      AddonManager: {
        getAllAddons() {},
        getAddonsByTypes() {}
      },
      addonManagerActive: false
    };
  }
}

const { AddonManager, addonManagerActive } = getAddonManager();
const l10n = require("gcli/l10n");

/**
 * Takes a function that uses a callback as its last parameter, and returns a
 * new function that returns a promise instead.
 * This should probably live in async-util
 */
const promiseify = function (scope, functionWithLastParamCallback) {
  return (...args) => {
    return new Promise(resolve => {
      args.push((...results) => {
        resolve(results.length > 1 ? results : results[0]);
      });
      functionWithLastParamCallback.apply(scope, args);
    });
  };
};

// Convert callback based functions to promise based ones
const getAllAddons = promiseify(AddonManager, AddonManager.getAllAddons);
const getAddonsByTypes = promiseify(AddonManager, AddonManager.getAddonsByTypes);

/**
 * Return a string array containing the pending operations on an addon
 */
function pendingOperations(addon) {
  let allOperations = [
    "PENDING_ENABLE", "PENDING_DISABLE", "PENDING_UNINSTALL",
    "PENDING_INSTALL", "PENDING_UPGRADE"
  ];
  return allOperations.reduce(function (operations, opName) {
    return addon.pendingOperations & AddonManager[opName] ?
      operations.concat(opName) :
      operations;
  }, []);
}

var items = [
  {
    item: "type",
    name: "addon",
    parent: "selection",
    stringifyProperty: "name",
    cacheable: true,
    constructor: function () {
      // Tell GCLI to clear the cache of addons when one is added or removed
      let listener = {
        onInstalled: addon => {
          this.clearCache();
        },
        onUninstalled: addon => {
          this.clearCache();
        },
      };
      AddonManager.addAddonListener(listener);
    },
    lookup: function () {
      return getAllAddons().then(addons => {
        return addons.map(addon => {
          let name = addon.name + " " + addon.version;
          name = name.trim().replace(/\s/g, "_");
          return { name: name, value: addon };
        });
      });
    }
  },
  {
    name: "addon",
    description: l10n.lookup("addonDesc")
  },
  {
    name: "addon list",
    description: l10n.lookup("addonListDesc"),
    returnType: "addonsInfo",
    params: [{
      name: "type",
      type: {
        name: "selection",
        data: [ "dictionary", "extension", "locale", "plugin", "theme", "all" ]
      },
      defaultValue: "all",
      description: l10n.lookup("addonListTypeDesc")
    }],
    exec: function (args, context) {
      let types = (args.type === "all") ? null : [ args.type ];
      return getAddonsByTypes(types).then(addons => {
        // Remove all hidden add-ons.
        addons = addons.filter(addon => {
          return !addon.hidden;
        });

        // Change the add-ons array to something we can work with.
        addons = addons.map(function (addon) {
          return {
            name: addon.name,
            version: addon.version,
            isActive: addon.isActive,
            pendingOperations: pendingOperations(addon)
          };
        });

        return { addons: addons, type: args.type };
      });
    }
  },
  {
    item: "converter",
    from: "addonsInfo",
    to: "view",
    exec: function (addonsInfo, context) {
      if (!addonsInfo.addons.length) {
        return context.createView({
          html: "<p>${message}</p>",
          data: { message: l10n.lookup("addonNoneOfType") }
        });
      }

      let headerLookups = {
        "dictionary": "addonListDictionaryHeading",
        "extension": "addonListExtensionHeading",
        "locale": "addonListLocaleHeading",
        "plugin": "addonListPluginHeading",
        "theme": "addonListThemeHeading",
        "all": "addonListAllHeading"
      };
      let header = l10n.lookup(headerLookups[addonsInfo.type] ||
                               "addonListUnknownHeading");

      let operationLookups = {
        "PENDING_ENABLE": "addonPendingEnable",
        "PENDING_DISABLE": "addonPendingDisable",
        "PENDING_UNINSTALL": "addonPendingUninstall",
        "PENDING_INSTALL": "addonPendingInstall",
        "PENDING_UPGRADE": "addonPendingUpgrade"
      };
      function lookupOperation(opName) {
        let lookupName = operationLookups[opName];
        return lookupName ? l10n.lookup(lookupName) : opName;
      }

      function arrangeAddons(addons) {
        let enabledAddons = [];
        let disabledAddons = [];
        addons.forEach(function (addon) {
          if (addon.isActive) {
            enabledAddons.push(addon);
          } else {
            disabledAddons.push(addon);
          }
        });

        function compareAddonNames(nameA, nameB) {
          return String(nameA.name).localeCompare(nameB.name);
        }
        enabledAddons.sort(compareAddonNames);
        disabledAddons.sort(compareAddonNames);

        return enabledAddons.concat(disabledAddons);
      }

      function isActiveForToggle(addon) {
        return (addon.isActive && ~~addon.pendingOperations.indexOf("PENDING_DISABLE"));
      }

      return context.createView({
        html:
          "<table>" +
          " <caption>${header}</caption>" +
          " <tbody>" +
          "  <tr foreach='addon in ${addons}'" +
          "      class=\"gcli-addon-${addon.status}\">" +
          "    <td>${addon.name} ${addon.version}</td>" +
          "    <td>${addon.pendingOperations}</td>" +
          "    <td>" +
          "      <span class='gcli-out-shortcut'" +
          "          data-command='addon ${addon.toggleActionName} ${addon.label}'" +
          "          onclick='${onclick}' ondblclick='${ondblclick}'" +
          "      >${addon.toggleActionMessage}</span>" +
          "    </td>" +
          "  </tr>" +
          " </tbody>" +
          "</table>",
        data: {
          header: header,
          addons: arrangeAddons(addonsInfo.addons).map(function (addon) {
            return {
              name: addon.name,
              label: addon.name.replace(/\s/g, "_") +
                    (addon.version ? "_" + addon.version : ""),
              status: addon.isActive ? "enabled" : "disabled",
              version: addon.version,
              pendingOperations: addon.pendingOperations.length ?
                (" (" + l10n.lookup("addonPending") + ": "
                 + addon.pendingOperations.map(lookupOperation).join(", ")
                 + ")") :
                "",
              toggleActionName: isActiveForToggle(addon) ? "disable" : "enable",
              toggleActionMessage: isActiveForToggle(addon) ?
                l10n.lookup("addonListOutDisable") :
                l10n.lookup("addonListOutEnable")
            };
          }),
          onclick: context.update,
          ondblclick: context.updateExec
        }
      });
    }
  },
  {
    item: "command",
    runAt: "client",
    name: "addon enable",
    description: l10n.lookup("addonEnableDesc"),
    params: [
      {
        name: "addon",
        type: "addon",
        description: l10n.lookup("addonNameDesc")
      }
    ],
    exec: function (args, context) {
      let name = (args.addon.name + " " + args.addon.version).trim();
      if (args.addon.userDisabled) {
        args.addon.userDisabled = false;
        return l10n.lookupFormat("addonEnabled", [ name ]);
      }

      return l10n.lookupFormat("addonAlreadyEnabled", [ name ]);
    }
  },
  {
    item: "command",
    runAt: "client",
    name: "addon disable",
    description: l10n.lookup("addonDisableDesc"),
    params: [
      {
        name: "addon",
        type: "addon",
        description: l10n.lookup("addonNameDesc")
      }
    ],
    exec: function (args, context) {
      // If the addon is not disabled or is set to "click to play" then
      // disable it. Otherwise display the message "Add-on is already
      // disabled."
      let name = (args.addon.name + " " + args.addon.version).trim();
      if (!args.addon.userDisabled ||
          args.addon.userDisabled === AddonManager.STATE_ASK_TO_ACTIVATE) {
        args.addon.userDisabled = true;
        return l10n.lookupFormat("addonDisabled", [ name ]);
      }

      return l10n.lookupFormat("addonAlreadyDisabled", [ name ]);
    }
  },
  {
    item: "command",
    runAt: "client",
    name: "addon ctp",
    description: l10n.lookup("addonCtpDesc"),
    params: [
      {
        name: "addon",
        type: "addon",
        description: l10n.lookup("addonNameDesc")
      }
    ],
    exec: function (args, context) {
      let name = (args.addon.name + " " + args.addon.version).trim();
      if (args.addon.type !== "plugin") {
        return l10n.lookupFormat("addonCantCtp", [ name ]);
      }

      if (!args.addon.userDisabled ||
          args.addon.userDisabled === true) {
        args.addon.userDisabled = AddonManager.STATE_ASK_TO_ACTIVATE;

        if (args.addon.userDisabled !== AddonManager.STATE_ASK_TO_ACTIVATE) {
          // Some plugins (e.g. OpenH264 shipped with Firefox) cannot be set to
          // click-to-play. Handle this.

          return l10n.lookupFormat("addonNoCtp", [ name ]);
        }

        return l10n.lookupFormat("addonCtp", [ name ]);
      }

      return l10n.lookupFormat("addonAlreadyCtp", [ name ]);
    }
  }
];

exports.items = addonManagerActive ? items : [];
PK
!<U32�33Achrome/devtools/modules/devtools/shared/gcli/commands/appcache.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 l10n = require("gcli/l10n");

loader.lazyImporter(this, "AppCacheUtils", "resource://devtools/client/shared/AppCacheUtils.jsm");

exports.items = [
  {
    item: "command",
    name: "appcache",
    description: l10n.lookup("appCacheDesc")
  },
  {
    item: "command",
    runAt: "server",
    name: "appcache validate",
    description: l10n.lookup("appCacheValidateDesc"),
    manual: l10n.lookup("appCacheValidateManual"),
    returnType: "appcacheerrors",
    params: [{
      group: "options",
      params: [
        {
          type: "string",
          name: "uri",
          description: l10n.lookup("appCacheValidateUriDesc"),
          defaultValue: null,
        }
      ]
    }],
    exec: function (args, context) {
      let utils;
      let deferred = context.defer();

      if (args.uri) {
        utils = new AppCacheUtils(args.uri);
      } else {
        utils = new AppCacheUtils(context.environment.document);
      }

      utils.validateManifest().then(function (errors) {
        deferred.resolve([errors, utils.manifestURI || "-"]);
      });

      return deferred.promise;
    }
  },
  {
    item: "converter",
    from: "appcacheerrors",
    to: "view",
    exec: function ([errors, manifestURI], context) {
      if (errors.length == 0) {
        return context.createView({
          html: "<span>" + l10n.lookup("appCacheValidatedSuccessfully") + "</span>"
        });
      }

      return context.createView({
        html:
          "<div>" +
          "  <h4>Manifest URI: ${manifestURI}</h4>" +
          "  <ol>" +
          "    <li foreach='error in ${errors}'>${error.msg}</li>" +
          "  </ol>" +
          "</div>",
        data: {
          errors: errors,
          manifestURI: manifestURI
        }
      });
    }
  },
  {
    item: "command",
    runAt: "server",
    name: "appcache clear",
    description: l10n.lookup("appCacheClearDesc"),
    manual: l10n.lookup("appCacheClearManual"),
    exec: function (args, context) {
      let utils = new AppCacheUtils(args.uri);
      utils.clearAll();

      return l10n.lookup("appCacheClearCleared");
    }
  },
  {
    item: "command",
    runAt: "server",
    name: "appcache list",
    description: l10n.lookup("appCacheListDesc"),
    manual: l10n.lookup("appCacheListManual"),
    returnType: "appcacheentries",
    params: [{
      group: "options",
      params: [
        {
          type: "string",
          name: "search",
          description: l10n.lookup("appCacheListSearchDesc"),
          defaultValue: null,
        },
      ]
    }],
    exec: function (args, context) {
      let utils = new AppCacheUtils();
      return utils.listEntries(args.search);
    }
  },
  {
    item: "converter",
    from: "appcacheentries",
    to: "view",
    exec: function (entries, context) {
      return context.createView({
        html: "" +
          "<ul class='gcli-appcache-list'>" +
          "  <li foreach='entry in ${entries}'>" +
          "    <table class='gcli-appcache-detail'>" +
          "      <tr>" +
          "        <td>" + l10n.lookup("appCacheListKey") + "</td>" +
          "        <td>${entry.key}</td>" +
          "      </tr>" +
          "      <tr>" +
          "        <td>" + l10n.lookup("appCacheListFetchCount") + "</td>" +
          "        <td>${entry.fetchCount}</td>" +
          "      </tr>" +
          "      <tr>" +
          "        <td>" + l10n.lookup("appCacheListLastFetched") + "</td>" +
          "        <td>${entry.lastFetched}</td>" +
          "      </tr>" +
          "      <tr>" +
          "        <td>" + l10n.lookup("appCacheListLastModified") + "</td>" +
          "        <td>${entry.lastModified}</td>" +
          "      </tr>" +
          "      <tr>" +
          "        <td>" + l10n.lookup("appCacheListExpirationTime") + "</td>" +
          "        <td>${entry.expirationTime}</td>" +
          "      </tr>" +
          "      <tr>" +
          "        <td>" + l10n.lookup("appCacheListDataSize") + "</td>" +
          "        <td>${entry.dataSize}</td>" +
          "      </tr>" +
          "      <tr>" +
          "        <td>" + l10n.lookup("appCacheListDeviceID") + "</td>" +
          "        <td>${entry.deviceID} <span class='gcli-out-shortcut' " +
          "onclick='${onclick}' ondblclick='${ondblclick}' " +
          "data-command='appcache viewentry ${entry.key}'" +
          ">" + l10n.lookup("appCacheListViewEntry") + "</span>" +
          "        </td>" +
          "      </tr>" +
          "    </table>" +
          "  </li>" +
          "</ul>",
        data: {
          entries: entries,
          onclick: context.update,
          ondblclick: context.updateExec
        }
      });
    }
  },
  {
    item: "command",
    runAt: "server",
    name: "appcache viewentry",
    description: l10n.lookup("appCacheViewEntryDesc"),
    manual: l10n.lookup("appCacheViewEntryManual"),
    params: [
      {
        type: "string",
        name: "key",
        description: l10n.lookup("appCacheViewEntryKey"),
        defaultValue: null,
      }
    ],
    exec: function (args, context) {
      let utils = new AppCacheUtils();
      return utils.viewEntry(args.key);
    }
  }
];
PK
!<_�;�		@chrome/devtools/modules/devtools/shared/gcli/commands/calllog.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Cu } = require("chrome");
const l10n = require("gcli/l10n");
const gcli = require("gcli/index");
const Debugger = require("Debugger");

loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
loader.lazyRequireGetter(this, "TargetFactory", "devtools/client/framework/target", true);

var debuggers = [];
var chromeDebuggers = [];
var sandboxes = [];

exports.items = [
  {
    name: "calllog",
    description: l10n.lookup("calllogDesc")
  },
  {
    item: "command",
    runAt: "client",
    name: "calllog start",
    description: l10n.lookup("calllogStartDesc"),

    exec: function (args, context) {
      let contentWindow = context.environment.window;

      let dbg = new Debugger(contentWindow);
      dbg.onEnterFrame = frame => {
        // BUG 773652 -  Make the output from the GCLI calllog command nicer
        contentWindow.console.log("Method call: " + this.callDescription(frame));
      };

      debuggers.push(dbg);

      let gBrowser = context.environment.chromeDocument.defaultView.gBrowser;
      let target = TargetFactory.forTab(gBrowser.selectedTab);
      gDevTools.showToolbox(target, "webconsole");

      return l10n.lookup("calllogStartReply");
    },

    callDescription: function (frame) {
      let name = "<anonymous>";
      if (frame.callee.name) {
        name = frame.callee.name;
      } else {
        let desc = frame.callee.getOwnPropertyDescriptor("displayName");
        if (desc && desc.value && typeof desc.value == "string") {
          name = desc.value;
        }
      }

      let args = frame.arguments.map(this.valueToString).join(", ");
      return name + "(" + args + ")";
    },

    valueToString: function (value) {
      if (typeof value !== "object" || value === null) {
        return uneval(value);
      }
      return "[object " + value.class + "]";
    }
  },
  {
    item: "command",
    runAt: "client",
    name: "calllog stop",
    description: l10n.lookup("calllogStopDesc"),

    exec: function (args, context) {
      let numDebuggers = debuggers.length;
      if (numDebuggers == 0) {
        return l10n.lookup("calllogStopNoLogging");
      }

      for (let dbg of debuggers) {
        dbg.onEnterFrame = undefined;
      }
      debuggers = [];

      return l10n.lookupFormat("calllogStopReply", [ numDebuggers ]);
    }
  },
  {
    item: "command",
    runAt: "client",
    name: "calllog chromestart",
    description: l10n.lookup("calllogChromeStartDesc"),
    get hidden() {
      return gcli.hiddenByChromePref();
    },
    params: [
      {
        name: "sourceType",
        type: {
          name: "selection",
          data: ["content-variable", "chrome-variable", "jsm", "javascript"]
        }
      },
      {
        name: "source",
        type: "string",
        description: l10n.lookup("calllogChromeSourceTypeDesc"),
        manual: l10n.lookup("calllogChromeSourceTypeManual"),
      }
    ],
    exec: function (args, context) {
      let globalObj;
      let contentWindow = context.environment.window;

      if (args.sourceType == "jsm") {
        try {
          globalObj = require(args.source);
        } catch (e) {
          return l10n.lookup("callLogChromeInvalidJSM");
        }
      } else if (args.sourceType == "content-variable") {
        if (args.source in contentWindow) {
          globalObj = Cu.getGlobalForObject(contentWindow[args.source]);
        } else {
          throw new Error(l10n.lookup("callLogChromeVarNotFoundContent"));
        }
      } else if (args.sourceType == "chrome-variable") {
        let chromeWin = context.environment.chromeDocument.defaultView;
        if (args.source in chromeWin) {
          globalObj = Cu.getGlobalForObject(chromeWin[args.source]);
        } else {
          return l10n.lookup("callLogChromeVarNotFoundChrome");
        }
      } else {
        let chromeWin = context.environment.chromeDocument.defaultView;
        let sandbox = new Cu.Sandbox(chromeWin, {
          sandboxPrototype: chromeWin,
          wantXrays: false,
          sandboxName: "gcli-cmd-calllog-chrome"
        });
        let returnVal;
        try {
          returnVal = Cu.evalInSandbox(args.source, sandbox, "ECMAv5");
          sandboxes.push(sandbox);
        } catch (e) {
          // We need to save the message before cleaning up else e contains a dead
          // object.
          let msg = l10n.lookup("callLogChromeEvalException") + ": " + e;
          Cu.nukeSandbox(sandbox);
          return msg;
        }

        if (typeof returnVal == "undefined") {
          return l10n.lookup("callLogChromeEvalNeedsObject");
        }

        globalObj = Cu.getGlobalForObject(returnVal);
      }

      let dbg = new Debugger(globalObj);
      chromeDebuggers.push(dbg);

      dbg.onEnterFrame = frame => {
        // BUG 773652 -  Make the output from the GCLI calllog command nicer
        contentWindow.console.log(l10n.lookup("callLogChromeMethodCall") +
                                  ": " + this.callDescription(frame));
      };

      let gBrowser = context.environment.chromeDocument.defaultView.gBrowser;
      let target = TargetFactory.forTab(gBrowser.selectedTab);
      gDevTools.showToolbox(target, "webconsole");

      return l10n.lookup("calllogChromeStartReply");
    },

    valueToString: function (value) {
      if (typeof value !== "object" || value === null) {
        return uneval(value);
      }
      return "[object " + value.class + "]";
    },

    callDescription: function (frame) {
      let name = frame.callee.name || l10n.lookup("callLogChromeAnonFunction");
      let args = frame.arguments.map(this.valueToString).join(", ");
      return name + "(" + args + ")";
    }
  },
  {
    item: "command",
    runAt: "client",
    name: "calllog chromestop",
    description: l10n.lookup("calllogChromeStopDesc"),
    get hidden() {
      return gcli.hiddenByChromePref();
    },
    exec: function (args, context) {
      let numDebuggers = chromeDebuggers.length;
      if (numDebuggers == 0) {
        return l10n.lookup("calllogChromeStopNoLogging");
      }

      for (let dbg of chromeDebuggers) {
        dbg.onEnterFrame = undefined;
        dbg.enabled = false;
      }
      for (let sandbox of sandboxes) {
        Cu.nukeSandbox(sandbox);
      }
      chromeDebuggers = [];
      sandboxes = [];

      return l10n.lookupFormat("calllogChromeStopReply", [ numDebuggers ]);
    }
  }
];
PK
!<xs��<chrome/devtools/modules/devtools/shared/gcli/commands/cmd.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Cc, Ci, Cu } = require("chrome");
const Services = require("Services");
const { OS } = require("resource://gre/modules/osfile.jsm");
const { Task } = require("devtools/shared/task");

const gcli = require("gcli/index");
const l10n = require("gcli/l10n");

loader.lazyImporter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm");

const PREF_DIR = "devtools.commands.dir";

/**
 * Load all the .mozcmd files in the directory pointed to by PREF_DIR
 * @return A promise of an array of items suitable for gcli.addItems or
 * using in gcli.addItemsByModule
 */
function loadItemsFromMozDir() {
  let dirName = Services.prefs.getStringPref(PREF_DIR).trim();
  if (dirName == "") {
    return Promise.resolve([]);
  }

  // replaces ~ with the home directory path in unix and windows
  if (dirName.indexOf("~") == 0) {
    let homeDirFile = Services.dirsvc.get("Home", Ci.nsIFile);
    let homeDir = homeDirFile.path;
    dirName = dirName.substr(1);
    dirName = homeDir + dirName;
  }

  // statPromise resolves to nothing if dirName is a directory, or it
  // rejects with an error message otherwise
  let statPromise = OS.File.stat(dirName);
  statPromise = statPromise.then(
    function onSuccess(stat) {
      if (!stat.isDir) {
        throw new Error("'" + dirName + "' is not a directory.");
      }
    },
    function onFailure(reason) {
      if (reason instanceof OS.File.Error && reason.becauseNoSuchFile) {
        throw new Error("'" + dirName + "' does not exist.");
      } else {
        throw reason;
      }
    }
  );

  // We need to return (a promise of) an array of items from the *.mozcmd
  // files in dirName (which we can assume to be a valid directory now)
  return Task.async(function* () {
    yield statPromise;
    let itemPromises = [];

    let iterator = new OS.File.DirectoryIterator(dirName);
    try {
      yield iterator.forEach(entry => {
        if (entry.name.match(/.*\.mozcmd$/) && !entry.isDir) {
          itemPromises.push(loadCommandFile(entry));
        }
      });
      iterator.close();
      let itemsArray = yield Promise.all(itemPromises);
      return itemsArray.reduce((prev, curr) => {
        return prev.concat(curr);
      }, []);
    } catch (e) {
      iterator.close();
      throw e;
    }
  });
}

exports.mozDirLoader = function (name) {
  return loadItemsFromMozDir().then(items => {
    return { items };
  });
};

/**
 * Load the commands from a single file
 * @param OS.File.DirectoryIterator.Entry entry The DirectoryIterator
 * Entry of the file containing the commands that we should read
 */
function loadCommandFile(entry) {
  let readPromise = OS.File.read(entry.path);
  readPromise = readPromise.then(array => {
    let decoder = new TextDecoder();
    let source = decoder.decode(array);
    let principal = Cc["@mozilla.org/systemprincipal;1"]
                      .createInstance(Ci.nsIPrincipal);

    let sandbox = new Cu.Sandbox(principal, {
      sandboxName: entry.path
    });
    let data = Cu.evalInSandbox(source, sandbox, "1.8", entry.name, 1);

    if (!Array.isArray(data)) {
      console.error("Command file '" + entry.name + "' does not have top level array.");
      return null;
    }

    return data;
  });
  return readPromise;
}

exports.items = [
  {
    name: "cmd",
    get hidden() {
      return !Services.prefs.prefHasUserValue(PREF_DIR);
    },
    description: l10n.lookup("cmdDesc")
  },
  {
    item: "command",
    runAt: "client",
    name: "cmd refresh",
    description: l10n.lookup("cmdRefreshDesc"),
    get hidden() {
      return !Services.prefs.prefHasUserValue(PREF_DIR);
    },
    exec: function (args, context) {
      gcli.load();

      let dirName = Services.prefs.getStringPref(PREF_DIR).trim();
      return l10n.lookupFormat("cmdStatus3", [ dirName ]);
    }
  },
  {
    item: "command",
    runAt: "client",
    name: "cmd setdir",
    description: l10n.lookup("cmdSetdirDesc"),
    manual: l10n.lookup("cmdSetdirManual3"),
    params: [
      {
        name: "directory",
        description: l10n.lookup("cmdSetdirDirectoryDesc"),
        type: {
          name: "file",
          filetype: "directory",
          existing: "yes"
        },
        defaultValue: null
      }
    ],
    returnType: "string",
    get hidden() {
      // !Services.prefs.prefHasUserValue(PREF_DIR);
      return true;
    },
    exec: function (args, context) {
      Services.prefs.setStringPref(PREF_DIR, args.directory);

      gcli.load();

      return l10n.lookupFormat("cmdStatus3", [ args.directory ]);
    }
  }
];
PK
!<A���,&,&?chrome/devtools/modules/devtools/shared/gcli/commands/cookie.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

/**
 * XXX: bug 1221488 is required to make these commands run on the server.
 * If we want these commands to run on remote devices/connections, they need to
 * run on the server (runAt=server). Unfortunately, cookie commands not only
 * need to run on the server, they also need to access to the parent process to
 * retrieve and manipulate cookies via nsICookieManager.
 * However, server-running commands have no way of accessing the parent process
 * for now.
 *
 * So, because these cookie commands, as of today, only run in the developer
 * toolbar (the gcli command bar), and because this toolbar is only available on
 * a local Firefox desktop tab (not in webide or the browser toolbox), we can
 * make the commands run on the client.
 * This way, they'll always run in the parent process.
 */

const { Ci } = require("chrome");
const Services = require("Services");
const l10n = require("gcli/l10n");

/**
 * Check host value and remove port part as it is not used
 * for storing cookies.
 *
 * Parameter will usually be `new URL(context.environment.target.url).host`
 */
function sanitizeHost(host) {
  if (host == null || host == "") {
    throw new Error(l10n.lookup("cookieListOutNonePage"));
  }
  return host.split(":")[0];
}

/**
 * The cookie 'expires' value needs converting into something more readable.
 *
 * And the unit of expires is sec, the unit that in argument of Date() needs
 * millisecond.
 */
function translateExpires(expires) {
  if (expires == 0) {
    return l10n.lookup("cookieListOutSession");
  }

  let expiresMsec = expires * 1000;

  return (new Date(expiresMsec)).toLocaleString();
}

/**
 * Check if a given cookie matches a given host
 */
function isCookieAtHost(cookie, host) {
  if (cookie.host == null) {
    return host == null;
  }
  if (cookie.host.startsWith(".")) {
    return ("." + host).endsWith(cookie.host);
  }
  if (cookie.host === "") {
    return host.startsWith("file://" + cookie.path);
  }
  return cookie.host == host;
}

exports.items = [
  {
    name: "cookie",
    description: l10n.lookup("cookieDesc"),
    manual: l10n.lookup("cookieManual")
  },
  {
    item: "command",
    runAt: "client",
    name: "cookie list",
    description: l10n.lookup("cookieListDesc"),
    manual: l10n.lookup("cookieListManual"),
    returnType: "cookies",
    exec: function (args, context) {
      if (context.environment.target.isRemote) {
        throw new Error("The cookie gcli commands only work in a local tab, " +
                        "see bug 1221488");
      }
      let host = new URL(context.environment.target.url).host;
      let contentWindow = context.environment.window;
      host = sanitizeHost(host);
      let { originAttributes } = contentWindow.document.nodePrincipal;
      let enm = Services.cookies.getCookiesFromHost(host, originAttributes);

      let cookies = [];
      while (enm.hasMoreElements()) {
        let cookie = enm.getNext().QueryInterface(Ci.nsICookie);
        if (isCookieAtHost(cookie, host)) {
          cookies.push({
            host: cookie.host,
            name: cookie.name,
            value: cookie.value,
            path: cookie.path,
            expires: cookie.expires,
            secure: cookie.secure,
            httpOnly: cookie.httpOnly,
            sameDomain: cookie.sameDomain
          });
        }
      }

      return cookies;
    }
  },
  {
    item: "command",
    runAt: "client",
    name: "cookie remove",
    description: l10n.lookup("cookieRemoveDesc"),
    manual: l10n.lookup("cookieRemoveManual"),
    params: [
      {
        name: "name",
        type: "string",
        description: l10n.lookup("cookieRemoveKeyDesc"),
      }
    ],
    exec: function (args, context) {
      if (context.environment.target.isRemote) {
        throw new Error("The cookie gcli commands only work in a local tab, " +
                        "see bug 1221488");
      }
      let host = new URL(context.environment.target.url).host;
      let contentWindow = context.environment.window;
      host = sanitizeHost(host);
      let { originAttributes } = contentWindow.document.nodePrincipal;
      let enm = Services.cookies.getCookiesFromHost(host, originAttributes);

      while (enm.hasMoreElements()) {
        let cookie = enm.getNext().QueryInterface(Ci.nsICookie);
        if (isCookieAtHost(cookie, host)) {
          if (cookie.name == args.name) {
            Services.cookies.remove(cookie.host, cookie.name, cookie.path,
                                    false, cookie.originAttributes);
          }
        }
      }
    }
  },
  {
    item: "converter",
    from: "cookies",
    to: "view",
    exec: function (cookies, context) {
      if (cookies.length == 0) {
        let host = new URL(context.environment.target.url).host;
        host = sanitizeHost(host);
        let msg = l10n.lookupFormat("cookieListOutNoneHost", [ host ]);
        return context.createView({ html: "<span>" + msg + "</span>" });
      }

      for (let cookie of cookies) {
        cookie.expires = translateExpires(cookie.expires);

        let noAttrs = !cookie.secure && !cookie.httpOnly && !cookie.sameDomain;
        cookie.attrs = (cookie.secure ? "secure" : " ") +
                       (cookie.httpOnly ? "httpOnly" : " ") +
                       (cookie.sameDomain ? "sameDomain" : " ") +
                       (noAttrs ? l10n.lookup("cookieListOutNone") : " ");
      }

      return context.createView({
        html:
          "<ul class='gcli-cookielist-list'>" +
          "  <li foreach='cookie in ${cookies}'>" +
          "    <div>${cookie.name}=${cookie.value}</div>" +
          "    <table class='gcli-cookielist-detail'>" +
          "      <tr>" +
          "        <td>" + l10n.lookup("cookieListOutHost") + "</td>" +
          "        <td>${cookie.host}</td>" +
          "      </tr>" +
          "      <tr>" +
          "        <td>" + l10n.lookup("cookieListOutPath") + "</td>" +
          "        <td>${cookie.path}</td>" +
          "      </tr>" +
          "      <tr>" +
          "        <td>" + l10n.lookup("cookieListOutExpires") + "</td>" +
          "        <td>${cookie.expires}</td>" +
          "      </tr>" +
          "      <tr>" +
          "        <td>" + l10n.lookup("cookieListOutAttributes") + "</td>" +
          "        <td>${cookie.attrs}</td>" +
          "      </tr>" +
          "      <tr><td colspan='2'>" +
          "        <span class='gcli-out-shortcut' onclick='${onclick}'" +
          "            data-command='cookie set ${cookie.name} '" +
          "            >" + l10n.lookup("cookieListOutEdit") + "</span>" +
          "        <span class='gcli-out-shortcut'" +
          "            onclick='${onclick}' ondblclick='${ondblclick}'" +
          "            data-command='cookie remove ${cookie.name}'" +
          "            >" + l10n.lookup("cookieListOutRemove") + "</span>" +
          "      </td></tr>" +
          "    </table>" +
          "  </li>" +
          "</ul>",
        data: {
          options: { allowEval: true },
          cookies: cookies,
          onclick: context.update,
          ondblclick: context.updateExec
        }
      });
    }
  },
  {
    item: "command",
    runAt: "client",
    name: "cookie set",
    description: l10n.lookup("cookieSetDesc"),
    manual: l10n.lookup("cookieSetManual"),
    params: [
      {
        name: "name",
        type: "string",
        description: l10n.lookup("cookieSetKeyDesc")
      },
      {
        name: "value",
        type: "string",
        description: l10n.lookup("cookieSetValueDesc")
      },
      {
        group: l10n.lookup("cookieSetOptionsDesc"),
        params: [
          {
            name: "path",
            type: { name: "string", allowBlank: true },
            defaultValue: "/",
            description: l10n.lookup("cookieSetPathDesc")
          },
          {
            name: "domain",
            type: "string",
            defaultValue: null,
            description: l10n.lookup("cookieSetDomainDesc")
          },
          {
            name: "secure",
            type: "boolean",
            description: l10n.lookup("cookieSetSecureDesc")
          },
          {
            name: "httpOnly",
            type: "boolean",
            description: l10n.lookup("cookieSetHttpOnlyDesc")
          },
          {
            name: "session",
            type: "boolean",
            description: l10n.lookup("cookieSetSessionDesc")
          },
          {
            name: "expires",
            type: "string",
            defaultValue: "Jan 17, 2038",
            description: l10n.lookup("cookieSetExpiresDesc")
          },
        ]
      }
    ],
    exec: function (args, context) {
      if (context.environment.target.isRemote) {
        throw new Error("The cookie gcli commands only work in a local tab, " +
                        "see bug 1221488");
      }
      let host = new URL(context.environment.target.url).host;
      host = sanitizeHost(host);
      let time = Date.parse(args.expires) / 1000;
      let contentWindow = context.environment.window;
      Services.cookies.add(args.domain ? "." + args.domain : host,
                           args.path ? args.path : "/",
                           args.name,
                           args.value,
                           args.secure,
                           args.httpOnly,
                           args.session,
                           time,
                           contentWindow.document.nodePrincipal.originAttributes);
    }
  }
];
PK
!<݌��jjDchrome/devtools/modules/devtools/shared/gcli/commands/csscoverage.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 domtemplate = require("gcli/util/domtemplate");
const csscoverage = require("devtools/shared/fronts/csscoverage");
const l10n = csscoverage.l10n;

loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);

const { Chart } = require("devtools/client/shared/widgets/Chart");

/**
 * The commands/converters for GCLI
 */
exports.items = [
  {
    name: "csscoverage",
    hidden: true,
    description: l10n.lookup("csscoverageDesc"),
  },
  {
    item: "command",
    runAt: "client",
    name: "csscoverage start",
    hidden: true,
    description: l10n.lookup("csscoverageStartDesc2"),
    params: [
      {
        name: "noreload",
        type: "boolean",
        description: l10n.lookup("csscoverageStartNoReloadDesc"),
        manual: l10n.lookup("csscoverageStartNoReloadManual")
      }
    ],
    exec: function* (args, context) {
      let usage = yield csscoverage.getUsage(context.environment.target);
      if (usage == null) {
        throw new Error(l10n.lookup("csscoverageNoRemoteError"));
      }
      yield usage.start(context.environment.chromeWindow,
                        context.environment.target, args.noreload);
    }
  },
  {
    item: "command",
    runAt: "client",
    name: "csscoverage stop",
    hidden: true,
    description: l10n.lookup("csscoverageStopDesc2"),
    exec: function* (args, context) {
      let target = context.environment.target;
      let usage = yield csscoverage.getUsage(target);
      if (usage == null) {
        throw new Error(l10n.lookup("csscoverageNoRemoteError"));
      }
      yield usage.stop();
      yield gDevTools.showToolbox(target, "styleeditor");
    }
  },
  {
    item: "command",
    runAt: "client",
    name: "csscoverage oneshot",
    hidden: true,
    description: l10n.lookup("csscoverageOneShotDesc2"),
    exec: function* (args, context) {
      let target = context.environment.target;
      let usage = yield csscoverage.getUsage(target);
      if (usage == null) {
        throw new Error(l10n.lookup("csscoverageNoRemoteError"));
      }
      yield usage.oneshot();
      yield gDevTools.showToolbox(target, "styleeditor");
    }
  },
  {
    item: "command",
    runAt: "client",
    name: "csscoverage toggle",
    hidden: true,
    description: l10n.lookup("csscoverageToggleDesc2"),
    state: {
      isChecked: function (target) {
        return csscoverage.getUsage(target).then(usage => {
          return usage.isRunning();
        });
      },
      onChange: function (target, handler) {
        csscoverage.getUsage(target).then(usage => {
          this.handler = ev => {
            handler("state-change", ev);
          };
          usage.on("state-change", this.handler);
        });
      },
      offChange: function (target, handler) {
        csscoverage.getUsage(target).then(usage => {
          usage.off("state-change", this.handler);
          this.handler = undefined;
        });
      },
    },
    exec: function* (args, context) {
      let target = context.environment.target;
      let usage = yield csscoverage.getUsage(target);
      if (usage == null) {
        throw new Error(l10n.lookup("csscoverageNoRemoteError"));
      }

      yield usage.toggle(context.environment.chromeWindow,
                         context.environment.target);
    }
  },
  {
    item: "command",
    runAt: "client",
    name: "csscoverage report",
    hidden: true,
    description: l10n.lookup("csscoverageReportDesc2"),
    exec: function* (args, context) {
      let usage = yield csscoverage.getUsage(context.environment.target);
      if (usage == null) {
        throw new Error(l10n.lookup("csscoverageNoRemoteError"));
      }

      return {
        isTypedData: true,
        type: "csscoveragePageReport",
        data: yield usage.createPageReport()
      };
    }
  },
  {
    item: "converter",
    from: "csscoveragePageReport",
    to: "dom",
    exec: function* (csscoveragePageReport, context) {
      let target = context.environment.target;

      let toolbox = yield gDevTools.showToolbox(target, "styleeditor");
      let panel = toolbox.getCurrentPanel();

      let host = panel._panelDoc.querySelector(".csscoverage-report");
      let templ = panel._panelDoc.querySelector(".csscoverage-template");

      templ = templ.cloneNode(true);
      templ.hidden = false;

      let data = {
        preload: csscoveragePageReport.preload,
        unused: csscoveragePageReport.unused,
        summary: csscoveragePageReport.summary,
        onback: () => {
          // The back button clears and hides .csscoverage-report
          while (host.hasChildNodes()) {
            host.firstChild.remove();
          }
          host.hidden = true;
        }
      };

      let addOnClick = rule => {
        rule.onclick = () => {
          panel.selectStyleSheet(rule.url, rule.start.line);
        };
      };

      data.preload.forEach(page => {
        page.rules.forEach(addOnClick);
      });
      data.unused.forEach(page => {
        page.rules.forEach(addOnClick);
      });

      let options = { allowEval: true, stack: "styleeditor.xul" };
      domtemplate.template(templ, data, options);

      while (templ.hasChildNodes()) {
        host.appendChild(templ.firstChild);
      }

      // Create a new chart.
      let container = host.querySelector(".csscoverage-report-chart");
      let chart = Chart.PieTable(panel._panelDoc, {
        // px
        diameter: 200,
        title: "CSS Usage",
        data: [
          { size: data.summary.preload, label: "Used Preload" },
          { size: data.summary.used, label: "Used" },
          { size: data.summary.unused, label: "Unused" }
        ]
      });
      container.appendChild(chart.node);

      host.hidden = false;
    }
  }
];
PK
!<FZV��?chrome/devtools/modules/devtools/shared/gcli/commands/folder.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Ci, CC } = require("chrome");
const Services = require("Services");
const l10n = require("gcli/l10n");

function showFolder(path) {
  let NSLocalFile = CC("@mozilla.org/file/local;1", "nsIFile",
                        "initWithPath");

  try {
    let file = new NSLocalFile(path);

    if (file.exists()) {
      file.reveal();
      return l10n.lookupFormat("folderOpenDirResult", [path]);
    }
    return l10n.lookup("folderInvalidPath");
  } catch (e) {
    return l10n.lookup("folderInvalidPath");
  }
}

exports.items = [
  {
    name: "folder",
    description: l10n.lookup("folderDesc")
  },
  {
    item: "command",
    runAt: "client",
    name: "folder open",
    description: l10n.lookup("folderOpenDesc"),
    params: [
      {
        name: "path",
        type: { name: "string", allowBlank: true },
        defaultValue: "~",
        description: l10n.lookup("folderOpenDir")
      }
    ],
    returnType: "string",
    exec: function (args, context) {
      let dirName = args.path;

      // replaces ~ with the home directory path in unix and windows
      if (dirName.indexOf("~") == 0) {
        let homeDirFile = Services.dirsvc.get("Home", Ci.nsIFile);
        let homeDir = homeDirFile.path;
        dirName = dirName.substr(1);
        dirName = homeDir + dirName;
      }

      return showFolder(dirName);
    }
  },
  {
    item: "command",
    runAt: "client",
    name: "folder openprofile",
    description: l10n.lookup("folderOpenProfileDesc"),
    returnType: "string",
    exec: function (args, context) {
      // Get the profile directory.
      let currProfD = Services.dirsvc.get("ProfD", Ci.nsIFile);
      let profileDir = currProfD.path;
      return showFolder(profileDir);
    }
  }
];
PK
!<�{����Bchrome/devtools/modules/devtools/shared/gcli/commands/highlight.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 l10n = require("gcli/l10n");
require("devtools/server/actors/inspector/inspector");
const {HighlighterEnvironment}  = require("devtools/server/actors/highlighters");
const {BoxModelHighlighter} = require("devtools/server/actors/highlighters/box-model");

const {PluralForm} = require("devtools/shared/plural-form");
const {LocalizationHelper} = require("devtools/shared/l10n");
const L10N = new LocalizationHelper("devtools/shared/locales/gclicommands.properties");

// How many maximum nodes can be highlighted in parallel
const MAX_HIGHLIGHTED_ELEMENTS = 100;

// Store the environment object used to create highlighters so it can be
// destroyed later.
var highlighterEnv;

// Stores the highlighters instances so they can be destroyed later.
// also export them so tests can access those and assert they got created
// correctly.
exports.highlighters = [];

/**
 * Destroy all existing highlighters
 */
function unhighlightAll() {
  for (let highlighter of exports.highlighters) {
    highlighter.destroy();
  }
  exports.highlighters.length = 0;

  if (highlighterEnv) {
    highlighterEnv.destroy();
    highlighterEnv = null;
  }
}

exports.items = [
  {
    item: "command",
    runAt: "server",
    name: "highlight",
    description: l10n.lookup("highlightDesc"),
    manual: l10n.lookup("highlightManual"),
    params: [
      {
        name: "selector",
        type: "nodelist",
        description: l10n.lookup("highlightSelectorDesc"),
        manual: l10n.lookup("highlightSelectorManual")
      },
      {
        group: l10n.lookup("highlightOptionsDesc"),
        params: [
          {
            name: "hideguides",
            type: "boolean",
            description: l10n.lookup("highlightHideGuidesDesc"),
            manual: l10n.lookup("highlightHideGuidesManual")
          },
          {
            name: "showinfobar",
            type: "boolean",
            description: l10n.lookup("highlightShowInfoBarDesc"),
            manual: l10n.lookup("highlightShowInfoBarManual")
          },
          {
            name: "showall",
            type: "boolean",
            description: l10n.lookup("highlightShowAllDesc"),
            manual: l10n.lookup("highlightShowAllManual")
          },
          {
            name: "region",
            type: {
              name: "selection",
              data: ["content", "padding", "border", "margin"]
            },
            description: l10n.lookup("highlightRegionDesc"),
            manual: l10n.lookup("highlightRegionManual"),
            defaultValue: "border"
          },
          {
            name: "fill",
            type: "string",
            description: l10n.lookup("highlightFillDesc"),
            manual: l10n.lookup("highlightFillManual"),
            defaultValue: null
          },
          {
            name: "keep",
            type: "boolean",
            description: l10n.lookup("highlightKeepDesc"),
            manual: l10n.lookup("highlightKeepManual")
          }
        ]
      }
    ],
    exec: function (args, context) {
      // Remove all existing highlighters unless told otherwise
      if (!args.keep) {
        unhighlightAll();
      }

      let env = context.environment;
      highlighterEnv = new HighlighterEnvironment();
      highlighterEnv.initFromWindow(env.window);

      // Unhighlight on navigate
      highlighterEnv.once("will-navigate", unhighlightAll);

      let i = 0;
      for (let node of args.selector) {
        if (!args.showall && i >= MAX_HIGHLIGHTED_ELEMENTS) {
          break;
        }

        let highlighter = new BoxModelHighlighter(highlighterEnv);
        if (args.fill) {
          highlighter.regionFill[args.region] = args.fill;
        }
        highlighter.show(node, {
          region: args.region,
          hideInfoBar: !args.showinfobar,
          hideGuides: args.hideguides,
          showOnly: args.region
        });
        exports.highlighters.push(highlighter);
        i++;
      }

      let highlightText = L10N.getStr("highlightOutputConfirm2");
      let output = PluralForm.get(args.selector.length, highlightText)
                             .replace("%1$S", args.selector.length);
      if (args.selector.length > i) {
        output = l10n.lookupFormat("highlightOutputMaxReached",
          ["" + args.selector.length, "" + i]);
      }

      return output;
    }
  },
  {
    item: "command",
    runAt: "server",
    name: "unhighlight",
    description: l10n.lookup("unhighlightDesc"),
    manual: l10n.lookup("unhighlightManual"),
    exec: unhighlightAll
  }
];
PK
!<�#��>chrome/devtools/modules/devtools/shared/gcli/commands/index.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { createSystem, connectFront, disconnectFront } = require("gcli/system");
const { GcliFront } = require("devtools/shared/fronts/gcli");

/**
 * This is the basic list of modules that should be loaded into each
 * requisition instance whether server side or client side
 */
exports.baseModules = [
  "gcli/types/delegate",
  "gcli/types/selection",
  "gcli/types/array",

  "gcli/types/boolean",
  "gcli/types/command",
  "gcli/types/date",
  "gcli/types/file",
  "gcli/types/javascript",
  "gcli/types/node",
  "gcli/types/number",
  "gcli/types/resource",
  "gcli/types/setting",
  "gcli/types/string",
  "gcli/types/union",
  "gcli/types/url",

  "gcli/fields/fields",
  "gcli/fields/delegate",
  "gcli/fields/selection",

  "gcli/ui/focus",
  "gcli/ui/intro",

  "gcli/converters/converters",
  "gcli/converters/basic",
  "gcli/converters/terminal",

  "gcli/languages/command",
  "gcli/languages/javascript",

  "gcli/commands/clear",
  "gcli/commands/context",
  "gcli/commands/help",
  "gcli/commands/pref",
];

/**
 * Some commands belong to a tool (see getToolModules). This is a list of the
 * modules that are *not* owned by a tool.
 */
exports.devtoolsModules = [
  "devtools/shared/gcli/commands/addon",
  "devtools/shared/gcli/commands/appcache",
  "devtools/shared/gcli/commands/calllog",
  "devtools/shared/gcli/commands/cmd",
  "devtools/shared/gcli/commands/cookie",
  "devtools/shared/gcli/commands/csscoverage",
  "devtools/shared/gcli/commands/folder",
  "devtools/shared/gcli/commands/highlight",
  "devtools/shared/gcli/commands/inject",
  "devtools/shared/gcli/commands/jsb",
  "devtools/shared/gcli/commands/listen",
  "devtools/shared/gcli/commands/measure",
  "devtools/shared/gcli/commands/media",
  "devtools/shared/gcli/commands/pagemod",
  "devtools/shared/gcli/commands/paintflashing",
  "devtools/shared/gcli/commands/qsa",
  "devtools/shared/gcli/commands/restart",
  "devtools/shared/gcli/commands/rulers",
  "devtools/shared/gcli/commands/screenshot",
  "devtools/shared/gcli/commands/security",
];

/**
 * Register commands from tools with 'command: [ "some/module" ]' definitions.
 * The map/reduce incantation squashes the array of arrays to a single array.
 */
try {
  const { defaultTools } = require("devtools/client/definitions");
  exports.devtoolsToolModules = defaultTools.map(def => def.commands || [])
                                   .reduce((prev, curr) => prev.concat(curr), []);
} catch (e) {
  // "devtools/client/definitions" is only accessible from Firefox
  exports.devtoolsToolModules = [];
}

/**
 * Register commands from toolbox buttons with 'command: [ "some/module" ]'
 * definitions.  The map/reduce incantation squashes the array of arrays to a
 * single array.
 */
try {
  const { ToolboxButtons } = require("devtools/client/definitions");
  exports.devtoolsButtonModules = ToolboxButtons.map(def => def.commands || [])
                                     .reduce((prev, curr) => prev.concat(curr), []);
} catch (e) {
  // "devtools/client/definitions" is only accessible from Firefox
  exports.devtoolsButtonModules = [];
}

/**
 * Add modules to a system for use in a content process (but don't call load)
 */
exports.addAllItemsByModule = function (system) {
  system.addItemsByModule(exports.baseModules, { delayedLoad: true });
  system.addItemsByModule(exports.devtoolsModules, { delayedLoad: true });
  system.addItemsByModule(exports.devtoolsToolModules, { delayedLoad: true });
  system.addItemsByModule(exports.devtoolsButtonModules, { delayedLoad: true });

  const { mozDirLoader } = require("devtools/shared/gcli/commands/cmd");
  system.addItemsByModule("mozcmd", { delayedLoad: true, loader: mozDirLoader });
};

/**
 * This is WeakMap<Target, Links> where Links is an object that looks like
 *   { refs: number, promise: Promise<System>, front: GcliFront }
 */
var linksForTarget = new WeakMap();

/**
 * The toolbox uses the following properties on a command to allow it to be
 * added to the toolbox toolbar
 */
var customProperties = [ "buttonId", "buttonClass", "tooltipText" ];

/**
 * Create a system which connects to a GCLI in a remote target
 * @return Promise<System> for the given target
 */
exports.getSystem = function (target) {
  const existingLinks = linksForTarget.get(target);
  if (existingLinks != null) {
    existingLinks.refs++;
    return existingLinks.promise;
  }

  const system = createSystem({ location: "client" });

  exports.addAllItemsByModule(system);

  // Load the client system
  const links = {
    refs: 1,
    system,
    promise: system.load().then(() => {
      return GcliFront.create(target).then(front => {
        links.front = front;
        return connectFront(system, front, customProperties).then(() => system);
      });
    })
  };

  linksForTarget.set(target, links);
  return links.promise;
};

/**
 * Someone that called getSystem doesn't need it any more, so decrement the
 * count of users of the system for that target, and destroy if needed
 */
exports.releaseSystem = function (target) {
  const links = linksForTarget.get(target);
  if (links == null) {
    throw new Error("releaseSystem called for unknown target");
  }

  links.refs--;
  if (links.refs === 0) {
    disconnectFront(links.system, links.front);
    links.system.destroy();
    linksForTarget.delete(target);
  }
};
PK
!<Y-�^�	�	?chrome/devtools/modules/devtools/shared/gcli/commands/inject.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Services = require("Services");
const { listenOnce } = require("devtools/shared/async-utils");
const l10n = require("gcli/l10n");

exports.items = [
  {
    item: "command",
    runAt: "server",
    name: "inject",
    description: l10n.lookup("injectDesc"),
    manual: l10n.lookup("injectManual2"),
    params: [{
      name: "library",
      type: {
        name: "union",
        alternatives: [
          {
            name: "selection",
            lookup: [
              {
                name: "jQuery",
                value: {
                  name: "jQuery",
                  src: Services.prefs.getCharPref("devtools.gcli.jquerySrc")
                }
              },
              {
                name: "lodash",
                value: {
                  name: "lodash",
                  src: Services.prefs.getCharPref("devtools.gcli.lodashSrc")
                }
              },
              {
                name: "underscore",
                value: {
                  name: "underscore",
                  src: Services.prefs.getCharPref("devtools.gcli.underscoreSrc")
                }
              }
            ]
          },
          {
            name: "url"
          }
        ]
      },
      description: l10n.lookup("injectLibraryDesc")
    }],
    exec: function* (args, context) {
      let document = context.environment.document;
      let library = args.library;

      let name;
      let src;

      if (library.type === "selection") {
        name = library.selection.name;
        src = library.selection.src;
      } else {
        // The library url is a URL object, let's turn it into a string.
        let urlStr = library.url + "";
        name = src = urlStr;
      }

      if (context.environment.window.location.protocol == "https:") {
        src = src.replace(/^http:/, "https:");
      }

      try {
        // Check if URI is valid
        Services.io.newURI(src);
      } catch (e) {
        return l10n.lookupFormat("injectFailed", [name]);
      }

      let newSource = document.createElement("script");
      newSource.setAttribute("src", src);

      let loadPromise = listenOnce(newSource, "load");
      document.head.appendChild(newSource);

      yield loadPromise;

      return l10n.lookupFormat("injectLoaded", [name]);
    }
  }
];
PK
!<�����<chrome/devtools/modules/devtools/shared/gcli/commands/jsb.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 l10n = require("gcli/l10n");

loader.lazyImporter(this, "Preferences", "resource://gre/modules/Preferences.jsm");
loader.lazyImporter(this, "ScratchpadManager", "resource://devtools/client/scratchpad/scratchpad-manager.jsm");

loader.lazyRequireGetter(this, "beautify", "devtools/shared/jsbeautify/beautify");

exports.items = [
  {
    item: "command",
    runAt: "client",
    name: "jsb",
    description: l10n.lookup("jsbDesc"),
    returnValue: "string",
    params: [
      {
        name: "url",
        type: "string",
        description: l10n.lookup("jsbUrlDesc")
      },
      {
        group: l10n.lookup("jsbOptionsDesc"),
        params: [
          {
            name: "indentSize",
            type: "number",
            description: l10n.lookup("jsbIndentSizeDesc"),
            manual: l10n.lookup("jsbIndentSizeManual"),
            defaultValue: Preferences.get("devtools.editor.tabsize", 2),
          },
          {
            name: "indentChar",
            type: {
              name: "selection",
              lookup: [
                { name: "space", value: " " },
                { name: "tab", value: "\t" }
              ]
            },
            description: l10n.lookup("jsbIndentCharDesc"),
            manual: l10n.lookup("jsbIndentCharManual"),
            defaultValue: " ",
          },
          {
            name: "doNotPreserveNewlines",
            type: "boolean",
            description: l10n.lookup("jsbDoNotPreserveNewlinesDesc")
          },
          {
            name: "preserveMaxNewlines",
            type: "number",
            description: l10n.lookup("jsbPreserveMaxNewlinesDesc"),
            manual: l10n.lookup("jsbPreserveMaxNewlinesManual"),
            defaultValue: -1
          },
          {
            name: "jslintHappy",
            type: "boolean",
            description: l10n.lookup("jsbJslintHappyDesc"),
            manual: l10n.lookup("jsbJslintHappyManual")
          },
          {
            name: "braceStyle",
            type: {
              name: "selection",
              data: ["collapse", "expand", "end-expand", "expand-strict"]
            },
            description: l10n.lookup("jsbBraceStyleDesc2"),
            manual: l10n.lookup("jsbBraceStyleManual2"),
            defaultValue: "collapse"
          },
          {
            name: "noSpaceBeforeConditional",
            type: "boolean",
            description: l10n.lookup("jsbNoSpaceBeforeConditionalDesc")
          },
          {
            name: "unescapeStrings",
            type: "boolean",
            description: l10n.lookup("jsbUnescapeStringsDesc"),
            manual: l10n.lookup("jsbUnescapeStringsManual")
          }
        ]
      }
    ],
    exec: function (args, context) {
      /* eslint-disable camelcase */
      let opts = {
        indent_size: args.indentSize,
        indent_char: args.indentChar,
        preserve_newlines: !args.doNotPreserveNewlines,
        max_preserve_newlines: args.preserveMaxNewlines == -1 ?
                              undefined : args.preserveMaxNewlines,
        jslint_happy: args.jslintHappy,
        brace_style: args.braceStyle,
        space_before_conditional: !args.noSpaceBeforeConditional,
        unescape_strings: args.unescapeStrings
      };
      /* eslint-enable camelcase */
      let xhr = new XMLHttpRequest();

      let deferred = context.defer();

      xhr.onreadystatechange = function () {
        if (xhr.readyState == 4) {
          if (xhr.status == 200 || xhr.status == 0) {
            let result = beautify.js(xhr.responseText, opts);

            ScratchpadManager.openScratchpad({text: result});

            deferred.resolve();
          } else {
            deferred.reject("Unable to load page to beautify: " + args.url + " " +
                            xhr.status + " " + xhr.statusText);
          }
        }
      };
      try {
        xhr.open("GET", args.url, true);
        xhr.send(null);
      } catch (e) {
        return l10n.lookup("jsbInvalidURL");
      }
      return deferred.promise;
    }
  }
];
PK
!<X"m��?chrome/devtools/modules/devtools/shared/gcli/commands/listen.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Services = require("Services");
const l10n = require("gcli/l10n");
const { DevToolsLoader } = require("resource://devtools/shared/Loader.jsm");
const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");

const BRAND_SHORT_NAME =
  Services.strings.createBundle("chrome://branding/locale/brand.properties")
                  .GetStringFromName("brandShortName");

XPCOMUtils.defineLazyGetter(this, "debuggerServer", () => {
  // Create a separate loader instance, so that we can be sure to receive
  // a separate instance of the DebuggingServer from the rest of the
  // devtools.  This allows us to safely use the tools against even the
  // actors and DebuggingServer itself, especially since we can mark
  // serverLoader as invisible to the debugger (unlike the usual loader
  // settings).
  let serverLoader = new DevToolsLoader();
  serverLoader.invisibleToDebugger = true;
  let { DebuggerServer: debuggerServer } = serverLoader.require("devtools/server/main");
  debuggerServer.init();
  debuggerServer.registerAllActors();
  debuggerServer.allowChromeProcess = !l10n.hiddenByChromePref();
  return debuggerServer;
});

exports.items = [
  {
    item: "command",
    runAt: "client",
    name: "listen",
    description: l10n.lookup("listenDesc"),
    manual: l10n.lookupFormat("listenManual2", [ BRAND_SHORT_NAME ]),
    params: [
      {
        name: "port",
        type: "number",
        get defaultValue() {
          return Services.prefs.getIntPref("devtools.debugger.remote-port");
        },
        description: l10n.lookup("listenPortDesc"),
      },
      {
        name: "protocol",
        get defaultValue() {
          let webSocket = Services.prefs
                          .getBoolPref("devtools.debugger.remote-websocket");
          let protocol;
          if (webSocket === true) {
            protocol = "websocket";
          } else {
            protocol = "mozilla-rdp";
          }
          return protocol;
        },
        type: {
          name: "selection",
          data: [ "mozilla-rdp", "websocket"],
        },
        description: l10n.lookup("listenProtocolDesc"),
      },
    ],
    exec: function (args, context) {
      let listener = debuggerServer.createListener();
      if (!listener) {
        throw new Error(l10n.lookup("listenDisabledOutput"));
      }

      let webSocket = false;
      if (args.protocol === "websocket") {
        webSocket = true;
      } else if (args.protocol === "mozilla-rdp") {
        webSocket = false;
      }

      listener.portOrPath = args.port;
      listener.webSocket = webSocket;
      listener.open();

      if (debuggerServer.initialized) {
        return l10n.lookupFormat("listenInitOutput", [ "" + args.port ]);
      }

      return l10n.lookup("listenNoInitOutput");
    },
  },
  {
    item: "command",
    runAt: "client",
    name: "unlisten",
    description: l10n.lookup("unlistenDesc"),
    manual: l10n.lookup("unlistenManual"),
    exec: function (args, context) {
      debuggerServer.closeAllListeners();
      return l10n.lookup("unlistenOutput");
    }
  }
];
PK
!<����
�
@chrome/devtools/modules/devtools/shared/gcli/commands/measure.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/shared/event-emitter");

loader.lazyRequireGetter(this, "CommandState",
  "devtools/shared/gcli/command-state", true);

const l10n = require("gcli/l10n");
require("devtools/server/actors/inspector/inspector");
const { HighlighterEnvironment } =
  require("devtools/server/actors/highlighters");
const { MeasuringToolHighlighter } =
  require("devtools/server/actors/highlighters/measuring-tool");

const highlighters = new WeakMap();

exports.items = [
  // The client measure command is used to maintain the toolbar button state
  // only and redirects to the server command to actually toggle the measuring
  // tool (see `measure_server` below).
  {
    name: "measure",
    runAt: "client",
    description: l10n.lookup("measureDesc"),
    manual: l10n.lookup("measureManual"),
    buttonId: "command-button-measure",
    buttonClass: "command-button",
    tooltipText: l10n.lookup("measureTooltip"),
    state: {
      isChecked: (target) => CommandState.isEnabledForTarget(target, "measure"),
      onChange: (target, handler) => CommandState.on("changed", handler),
      offChange: (target, handler) => CommandState.off("changed", handler)
    },
    exec: function* (args, context) {
      let { target } = context.environment;

      // Pipe the call to the server command.
      let response = yield context.updateExec("measure_server");
      let isEnabled = response.data;

      if (isEnabled) {
        CommandState.enableForTarget(target, "measure");
      } else {
        CommandState.disableForTarget(target, "measure");
      }

      // Toggle off the button when the page navigates because the measuring
      // tool is removed automatically by the MeasuringToolHighlighter on the
      // server then.
      target.once("will-navigate", () =>
        CommandState.disableForTarget(target, "measure"));
    }
  },
  // The server measure command is hidden by default, it's just used by the
  // client command.
  {
    name: "measure_server",
    runAt: "server",
    hidden: true,
    returnType: "highlighterVisibility",
    exec: function (args, context) {
      let env = context.environment;
      let { document } = env;

      // Calling the command again after the measuring tool has been shown once,
      // hides it.
      if (highlighters.has(document)) {
        let { highlighter } = highlighters.get(document);
        highlighter.destroy();
        return false;
      }

      // Otherwise, display the measuring tool.
      let environment = new HighlighterEnvironment();
      environment.initFromWindow(env.window);
      let highlighter = new MeasuringToolHighlighter(environment);

      // Store the instance of the measuring tool highlighter for this document
      // so we can hide it later.
      highlighters.set(document, { highlighter, environment });

      // Listen to the highlighter's destroy event which may happen if the
      // window is refreshed or closed with the measuring tool shown.
      EventEmitter.once(highlighter, "destroy", () => {
        if (highlighters.has(document)) {
          let { environment: toDestroy } = highlighters.get(document);
          toDestroy.destroy();
          highlighters.delete(document);
        }
      });

      highlighter.show();
      return true;
    }
  }
];
PK
!<<�d���>chrome/devtools/modules/devtools/shared/gcli/commands/media.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {Ci} = require("chrome");
const l10n = require("gcli/l10n");

function getContentViewer(context) {
  let {window} = context.environment;
  return window.QueryInterface(Ci.nsIInterfaceRequestor)
               .getInterface(Ci.nsIDocShell)
               .contentViewer;
}

exports.items = [
  {
    name: "media",
    description: l10n.lookup("mediaDesc")
  },
  {
    item: "command",
    runAt: "server",
    name: "media emulate",
    description: l10n.lookup("mediaEmulateDesc"),
    manual: l10n.lookup("mediaEmulateManual"),
    params: [
      {
        name: "type",
        description: l10n.lookup("mediaEmulateType"),
        type: {
          name: "selection",
          data: [
            "braille", "embossed", "handheld", "print", "projection",
            "screen", "speech", "tty", "tv"
          ]
        }
      }
    ],
    exec: function (args, context) {
      let contentViewer = getContentViewer(context);
      contentViewer.emulateMedium(args.type);
    }
  },
  {
    item: "command",
    runAt: "server",
    name: "media reset",
    description: l10n.lookup("mediaResetDesc"),
    exec: function (args, context) {
      let contentViewer = getContentViewer(context);
      contentViewer.stopEmulatingMedium();
    }
  }
];
PK
!<Oك���@chrome/devtools/modules/devtools/shared/gcli/commands/pagemod.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Cc, Ci } = require("chrome");
const l10n = require("gcli/l10n");

exports.items = [
  {
    name: "pagemod",
    description: l10n.lookup("pagemodDesc"),
  },
  {
    item: "command",
    runAt: "server",
    name: "pagemod replace",
    description: l10n.lookup("pagemodReplaceDesc"),
    params: [
      {
        name: "search",
        type: "string",
        description: l10n.lookup("pagemodReplaceSearchDesc"),
      },
      {
        name: "replace",
        type: "string",
        description: l10n.lookup("pagemodReplaceReplaceDesc"),
      },
      {
        name: "ignoreCase",
        type: "boolean",
        description: l10n.lookup("pagemodReplaceIgnoreCaseDesc"),
      },
      {
        name: "selector",
        type: "string",
        description: l10n.lookup("pagemodReplaceSelectorDesc"),
        defaultValue: "*:not(script):not(style):not(embed):not(object):not(frame):not(iframe):not(frameset)", // eslint-disable-line
      },
      {
        name: "root",
        type: "node",
        description: l10n.lookup("pagemodReplaceRootDesc"),
        defaultValue: null,
      },
      {
        name: "attrOnly",
        type: "boolean",
        description: l10n.lookup("pagemodReplaceAttrOnlyDesc"),
      },
      {
        name: "contentOnly",
        type: "boolean",
        description: l10n.lookup("pagemodReplaceContentOnlyDesc"),
      },
      {
        name: "attributes",
        type: "string",
        description: l10n.lookup("pagemodReplaceAttributesDesc"),
        defaultValue: null,
      },
    ],
    // Make a given string safe to use in a regular expression.
    escapeRegex: function (string) {
      return string.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
    },
    exec: function (args, context) {
      let searchTextNodes = !args.attrOnly;
      let searchAttributes = !args.contentOnly;
      let regexOptions = args.ignoreCase ? "ig" : "g";
      let search = new RegExp(this.escapeRegex(args.search), regexOptions);
      let attributeRegex = null;
      if (args.attributes) {
        attributeRegex = new RegExp(args.attributes, regexOptions);
      }

      let root = args.root || context.environment.document;
      let elements = root.querySelectorAll(args.selector);
      elements = Array.prototype.slice.call(elements);

      let replacedTextNodes = 0;
      let replacedAttributes = 0;

      function replaceAttribute() {
        replacedAttributes++;
        return args.replace;
      }
      function replaceTextNode() {
        replacedTextNodes++;
        return args.replace;
      }

      for (let i = 0; i < elements.length; i++) {
        let element = elements[i];
        if (searchTextNodes) {
          for (let y = 0; y < element.childNodes.length; y++) {
            let node = element.childNodes[y];
            if (node.nodeType == node.TEXT_NODE) {
              node.textContent = node.textContent.replace(search, replaceTextNode);
            }
          }
        }

        if (searchAttributes) {
          if (!element.attributes) {
            continue;
          }
          for (let y = 0; y < element.attributes.length; y++) {
            let attr = element.attributes[y];
            if (!attributeRegex || attributeRegex.test(attr.name)) {
              attr.value = attr.value.replace(search, replaceAttribute);
            }
          }
        }
      }

      return l10n.lookupFormat("pagemodReplaceResult",
        [elements.length, replacedTextNodes, replacedAttributes]);
    }
  },
  {
    name: "pagemod remove",
    description: l10n.lookup("pagemodRemoveDesc"),
  },
  {
    item: "command",
    runAt: "server",
    name: "pagemod remove element",
    description: l10n.lookup("pagemodRemoveElementDesc"),
    params: [
      {
        name: "search",
        type: "string",
        description: l10n.lookup("pagemodRemoveElementSearchDesc"),
      },
      {
        name: "root",
        type: "node",
        description: l10n.lookup("pagemodRemoveElementRootDesc"),
        defaultValue: null,
      },
      {
        name: "stripOnly",
        type: "boolean",
        description: l10n.lookup("pagemodRemoveElementStripOnlyDesc"),
      },
      {
        name: "ifEmptyOnly",
        type: "boolean",
        description: l10n.lookup("pagemodRemoveElementIfEmptyOnlyDesc"),
      },
    ],
    exec: function (args, context) {
      let root = args.root || context.environment.document;
      let elements = Array.prototype.slice.call(root.querySelectorAll(args.search));

      let removed = 0;
      for (let i = 0; i < elements.length; i++) {
        let element = elements[i];
        let parentNode = element.parentNode;
        if (!parentNode || !element.removeChild) {
          continue;
        }
        if (args.stripOnly) {
          while (element.hasChildNodes()) {
            parentNode.insertBefore(element.childNodes[0], element);
          }
        }
        if (!args.ifEmptyOnly || !element.hasChildNodes()) {
          element.remove();
          removed++;
        }
      }

      return l10n.lookupFormat("pagemodRemoveElementResultMatchedAndRemovedElements",
                              [elements.length, removed]);
    }
  },
  {
    item: "command",
    runAt: "server",
    name: "pagemod remove attribute",
    description: l10n.lookup("pagemodRemoveAttributeDesc"),
    params: [
      {
        name: "searchAttributes",
        type: "string",
        description: l10n.lookup("pagemodRemoveAttributeSearchAttributesDesc"),
      },
      {
        name: "searchElements",
        type: "string",
        description: l10n.lookup("pagemodRemoveAttributeSearchElementsDesc"),
      },
      {
        name: "root",
        type: "node",
        description: l10n.lookup("pagemodRemoveAttributeRootDesc"),
        defaultValue: null,
      },
      {
        name: "ignoreCase",
        type: "boolean",
        description: l10n.lookup("pagemodRemoveAttributeIgnoreCaseDesc"),
      },
    ],
    exec: function (args, context) {
      let root = args.root || context.environment.document;
      let regexOptions = args.ignoreCase ? "ig" : "g";
      let attributeRegex = new RegExp(args.searchAttributes, regexOptions);
      let elements = root.querySelectorAll(args.searchElements);
      elements = Array.prototype.slice.call(elements);

      let removed = 0;
      for (let i = 0; i < elements.length; i++) {
        let element = elements[i];
        if (!element.attributes) {
          continue;
        }

        let attrs = Array.prototype.slice.call(element.attributes);
        for (let y = 0; y < attrs.length; y++) {
          let attr = attrs[y];
          if (attributeRegex.test(attr.name)) {
            element.removeAttribute(attr.name);
            removed++;
          }
        }
      }

      return l10n.lookupFormat("pagemodRemoveAttributeResult",
                              [elements.length, removed]);
    }
  },
  // This command allows the user to export the page to HTML after DOM changes
  {
    name: "export",
    description: l10n.lookup("exportDesc"),
  },
  {
    item: "command",
    runAt: "server",
    name: "export html",
    description: l10n.lookup("exportHtmlDesc"),
    params: [
      {
        name: "destination",
        type: {
          name: "selection",
          data: [ "window", "stdout", "clipboard" ]
        },
        defaultValue: "window"
      }
    ],
    exec: function (args, context) {
      let html = context.environment.document.documentElement.outerHTML;
      if (args.destination === "stdout") {
        return html;
      }

      if (args.desination === "clipboard") {
        let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"]
                          .getService(Ci.nsIClipboardHelper);
        clipboard.copyString(url);
        return "";
      }

      let url = "data:text/plain;charset=utf8," + encodeURIComponent(html);
      context.environment.window.open(url);
      return "";
    }
  }
];
PK
!<�q}VhhFchrome/devtools/modules/devtools/shared/gcli/commands/paintflashing.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Ci } = require("chrome");

loader.lazyRequireGetter(this, "CommandState",
  "devtools/shared/gcli/command-state", true);

var telemetry;
try {
  const Telemetry = require("devtools/client/shared/telemetry");
  telemetry = new Telemetry();
} catch (e) {
  // DevTools Telemetry module only available in Firefox
}

const gcli = require("gcli/index");
const l10n = require("gcli/l10n");

/**
 * Fire events and telemetry when paintFlashing happens
 */
function onPaintFlashingChanged(target, flashing) {
  if (flashing) {
    CommandState.enableForTarget(target, "paintflashing");
  } else {
    CommandState.disableForTarget(target, "paintflashing");
  }

  target.once("will-navigate", () =>
    CommandState.disableForTarget(target, "paintflashing"));

  if (!telemetry) {
    return;
  }
  if (flashing) {
    telemetry.toolOpened("paintflashing");
  } else {
    telemetry.toolClosed("paintflashing");
  }
}

/**
 * Alter the paintFlashing state of a window and report on the new value.
 * This works with chrome or content windows.
 *
 * This is a bizarre method that you could argue should be broken up into
 * separate getter and setter functions, however keeping it as one helps
 * to simplify the commands below.
 *
 * @param state {string} One of:
 * - "on" which does window.paintFlashing = true
 * - "off" which does window.paintFlashing = false
 * - "toggle" which does window.paintFlashing = !window.paintFlashing
 * - "query" which does nothing
 * @return The new value of the window.paintFlashing flag
 */
function setPaintFlashing(window, state) {
  const winUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsIDOMWindowUtils);

  if (!["on", "off", "toggle", "query"].includes(state)) {
    throw new Error(`Unsupported state: ${state}`);
  }

  if (state === "on") {
    winUtils.paintFlashing = true;
  } else if (state === "off") {
    winUtils.paintFlashing = false;
  } else if (state === "toggle") {
    winUtils.paintFlashing = !winUtils.paintFlashing;
  }

  return winUtils.paintFlashing;
}

exports.items = [
  {
    name: "paintflashing",
    description: l10n.lookup("paintflashingDesc")
  },
  {
    item: "command",
    runAt: "client",
    name: "paintflashing on",
    description: l10n.lookup("paintflashingOnDesc"),
    manual: l10n.lookup("paintflashingManual"),
    params: [{
      group: "options",
      params: [
        {
          type: "boolean",
          name: "chrome",
          get hidden() {
            return gcli.hiddenByChromePref();
          },
          description: l10n.lookup("paintflashingChromeDesc"),
        }
      ]
    }],
    exec: function* (args, context) {
      if (!args.chrome) {
        const output = yield context.updateExec("paintflashing_server --state on");

        onPaintFlashingChanged(context.environment.target, output.data);
      } else {
        setPaintFlashing(context.environment.chromeWindow, "on");
      }
    }
  },
  {
    item: "command",
    runAt: "client",
    name: "paintflashing off",
    description: l10n.lookup("paintflashingOffDesc"),
    manual: l10n.lookup("paintflashingManual"),
    params: [{
      group: "options",
      params: [
        {
          type: "boolean",
          name: "chrome",
          get hidden() {
            return gcli.hiddenByChromePref();
          },
          description: l10n.lookup("paintflashingChromeDesc"),
        }
      ]
    }],
    exec: function* (args, context) {
      if (!args.chrome) {
        const output = yield context.updateExec("paintflashing_server --state off");

        onPaintFlashingChanged(context.environment.target, output.data);
      } else {
        setPaintFlashing(context.environment.chromeWindow, "off");
      }
    }
  },
  {
    item: "command",
    runAt: "client",
    name: "paintflashing toggle",
    hidden: true,
    buttonId: "command-button-paintflashing",
    buttonClass: "command-button",
    state: {
      isChecked: (target) => CommandState.isEnabledForTarget(target, "paintflashing"),
      onChange: (_, handler) => CommandState.on("changed", handler),
      offChange: (_, handler) => CommandState.off("changed", handler),
    },
    tooltipText: l10n.lookup("paintflashingTooltip"),
    description: l10n.lookup("paintflashingToggleDesc"),
    manual: l10n.lookup("paintflashingManual"),
    exec: function* (args, context) {
      const output = yield context.updateExec("paintflashing_server --state toggle");

      onPaintFlashingChanged(context.environment.target, output.data);
    }
  },
  {
    item: "command",
    runAt: "server",
    name: "paintflashing_server",
    hidden: true,
    params: [
      {
        name: "state",
        type: {
          name: "selection",
          data: [ "on", "off", "toggle", "query" ]
        }
      },
    ],
    returnType: "paintFlashingState",
    exec: function (args, context) {
      let { window } = context.environment;

      return setPaintFlashing(window, args.state);
    }
  }
];
PK
!<�/�@@<chrome/devtools/modules/devtools/shared/gcli/commands/qsa.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 l10n = require("gcli/l10n");

exports.items = [
  {
    item: "command",
    runAt: "server",
    name: "qsa",
    description: l10n.lookup("qsaDesc"),
    params: [{
      name: "query",
      type: "nodelist",
      description: l10n.lookup("qsaQueryDesc")
    }],
    exec: function (args, context) {
      return args.query.length;
    }
  }
];
PK
!<����@chrome/devtools/modules/devtools/shared/gcli/commands/restart.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Cc, Ci } = require("chrome");
const l10n = require("gcli/l10n");
const Services = require("Services");

const BRAND_SHORT_NAME =
  Services.strings.createBundle("chrome://branding/locale/brand.properties")
                  .GetStringFromName("brandShortName");

/**
 * Restart command
 *
 * @param boolean nocache
 *        Disables loading content from cache upon restart.
 *
 * Examples :
 * >> restart
 * - restarts browser immediately
 * >> restart --nocache
 * - restarts immediately and starts Firefox without using cache
 */
exports.items = [
  {
    item: "command",
    runAt: "client",
    name: "restart",
    description: l10n.lookupFormat("restartBrowserDesc", [ BRAND_SHORT_NAME ]),
    params: [{
      group: l10n.lookup("restartBrowserGroupOptions"),
      params: [
        {
          name: "nocache",
          type: "boolean",
          description: l10n.lookup("restartBrowserNocacheDesc")
        },
        {
          name: "safemode",
          type: "boolean",
          description: l10n.lookup("restartBrowserSafemodeDesc")
        }
      ]
    }],
    returnType: "string",
    exec: function Restart(args, context) {
      let canceled = Cc["@mozilla.org/supports-PRBool;1"]
                      .createInstance(Ci.nsISupportsPRBool);
      Services.obs.notifyObservers(canceled, "quit-application-requested", "restart");
      if (canceled.data) {
        return l10n.lookup("restartBrowserRequestCancelled");
      }

      // disable loading content from cache.
      if (args.nocache) {
        Services.appinfo.invalidateCachesOnRestart();
      }

      if (args.safemode) {
        // restart in safemode
        Services.startup.restartInSafeMode(Ci.nsIAppStartup.eAttemptQuit);
      } else {
        // restart normally
        Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart);
      }

      return l10n.lookupFormat("restartBrowserRestarting", [ BRAND_SHORT_NAME ]);
    }
  }
];
PK
!<�%`
`
?chrome/devtools/modules/devtools/shared/gcli/commands/rulers.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/shared/event-emitter");

loader.lazyRequireGetter(this, "CommandState",
  "devtools/shared/gcli/command-state", true);

const l10n = require("gcli/l10n");
require("devtools/server/actors/inspector/inspector");
const { HighlighterEnvironment } =
  require("devtools/server/actors/highlighters");
const { RulersHighlighter } =
  require("devtools/server/actors/highlighters/rulers");

const highlighters = new WeakMap();

exports.items = [
  // The client rulers command is used to maintain the toolbar button state only
  // and redirects to the server command to actually toggle the rulers (see
  // rulers_server below).
  {
    name: "rulers",
    runAt: "client",
    description: l10n.lookup("rulersDesc"),
    manual: l10n.lookup("rulersManual"),
    buttonId: "command-button-rulers",
    buttonClass: "command-button",
    tooltipText: l10n.lookup("rulersTooltip"),
    state: {
      isChecked: (target) => CommandState.isEnabledForTarget(target, "rulers"),
      onChange: (target, handler) => CommandState.on("changed", handler),
      offChange: (target, handler) => CommandState.off("changed", handler)
    },
    exec: function* (args, context) {
      let { target } = context.environment;

      // Pipe the call to the server command.
      let response = yield context.updateExec("rulers_server");
      let isEnabled = response.data;

      if (isEnabled) {
        CommandState.enableForTarget(target, "rulers");
      } else {
        CommandState.disableForTarget(target, "rulers");
      }

      // Toggle off the button when the page navigates because the rulers are
      // removed automatically by the RulersHighlighter on the server then.
      target.once("will-navigate", () => CommandState.disableForTarget(target, "rulers"));
    }
  },
  // The server rulers command is hidden by default, it's just used by the
  // client command.
  {
    name: "rulers_server",
    runAt: "server",
    hidden: true,
    returnType: "highlighterVisibility",
    exec: function (args, context) {
      let env = context.environment;
      let { document } = env;

      // Calling the command again after the rulers have been shown once hides
      // them.
      if (highlighters.has(document)) {
        let { highlighter } = highlighters.get(document);
        highlighter.destroy();
        return false;
      }

      // Otherwise, display the rulers.
      let environment = new HighlighterEnvironment();
      environment.initFromWindow(env.window);
      let highlighter = new RulersHighlighter(environment);

      // Store the instance of the rulers highlighter for this document so we
      // can hide it later.
      highlighters.set(document, { highlighter, environment });

      // Listen to the highlighter's destroy event which may happen if the
      // window is refreshed or closed with the rulers shown.
      EventEmitter.once(highlighter, "destroy", () => {
        if (highlighters.has(document)) {
          let { environment: toDestroy } = highlighters.get(document);
          toDestroy.destroy();
          highlighters.delete(document);
        }
      });

      highlighter.show();
      return true;
    }
  }
];
PK
!<�`��	P	PCchrome/devtools/modules/devtools/shared/gcli/commands/screenshot.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Cc, Ci, Cr, Cu } = require("chrome");
const l10n = require("gcli/l10n");
const Services = require("Services");
const { NetUtil } = require("resource://gre/modules/NetUtil.jsm");
const { getRect } = require("devtools/shared/layout/utils");
const defer = require("devtools/shared/defer");
const { Task } = require("devtools/shared/task");

loader.lazyImporter(this, "Downloads", "resource://gre/modules/Downloads.jsm");
loader.lazyImporter(this, "OS", "resource://gre/modules/osfile.jsm");
loader.lazyImporter(this, "FileUtils", "resource://gre/modules/FileUtils.jsm");
loader.lazyImporter(this, "PrivateBrowsingUtils",
                          "resource://gre/modules/PrivateBrowsingUtils.jsm");

// String used as an indication to generate default file name in the following
// format: "Screen Shot yyyy-mm-dd at HH.MM.SS.png"
const FILENAME_DEFAULT_VALUE = " ";
const CONTAINER_FLASHING_DURATION = 500;

/*
 * There are 2 commands and 1 converter here. The 2 commands are nearly
 * identical except that one runs on the client and one in the server.
 *
 * The server command is hidden, and is designed to be called from the client
 * command.
 */

/**
 * Both commands have the same initial filename parameter
 */
const filenameParam = {
  name: "filename",
  type: "string",
  defaultValue: FILENAME_DEFAULT_VALUE,
  description: l10n.lookup("screenshotFilenameDesc"),
  manual: l10n.lookup("screenshotFilenameManual")
};

/**
 * Both commands have almost the same set of standard optional parameters, except for the
 * type of the --selector option, which can be a node only on the server.
 */
const getScreenshotCommandParams = function (isClient) {
  return {
    group: l10n.lookup("screenshotGroupOptions"),
    params: [
      {
        name: "clipboard",
        type: "boolean",
        description: l10n.lookup("screenshotClipboardDesc"),
        manual: l10n.lookup("screenshotClipboardManual")
      },
      {
        name: "imgur",
        type: "boolean",
        description: l10n.lookup("screenshotImgurDesc"),
        manual: l10n.lookup("screenshotImgurManual")
      },
      {
        name: "delay",
        type: { name: "number", min: 0 },
        defaultValue: 0,
        description: l10n.lookup("screenshotDelayDesc"),
        manual: l10n.lookup("screenshotDelayManual")
      },
      {
        name: "dpr",
        type: { name: "number", min: 0, allowFloat: true },
        defaultValue: 0,
        description: l10n.lookup("screenshotDPRDesc"),
        manual: l10n.lookup("screenshotDPRManual")
      },
      {
        name: "fullpage",
        type: "boolean",
        description: l10n.lookup("screenshotFullPageDesc"),
        manual: l10n.lookup("screenshotFullPageManual")
      },
      {
        name: "selector",
        // On the client side, don't try to parse the selector as a node as it will
        // trigger an unsafe CPOW.
        type: isClient ? "string" : "node",
        defaultValue: null,
        description: l10n.lookup("inspectNodeDesc"),
        manual: l10n.lookup("inspectNodeManual")
      },
      {
        name: "file",
        type: "boolean",
        description: l10n.lookup("screenshotFileDesc"),
        manual: l10n.lookup("screenshotFileManual"),
      },
    ]
  };
};

const clientScreenshotParams = getScreenshotCommandParams(true);
const serverScreenshotParams = getScreenshotCommandParams(false);

exports.items = [
  {
    /**
     * Format an 'imageSummary' (as output by the screenshot command).
     * An 'imageSummary' is a simple JSON object that looks like this:
     *
     * {
     *   destinations: [ "..." ], // Required array of descriptions of the
     *                            // locations of the result image (the command
     *                            // can have multiple outputs)
     *   data: "...",             // Optional Base64 encoded image data
     *   width:1024, height:768,  // Dimensions of the image data, required
     *                            // if data != null
     *   filename: "...",         // If set, clicking the image will open the
     *                            // folder containing the given file
     *   href: "...",             // If set, clicking the image will open the
     *                            // link in a new tab
     * }
     */
    item: "converter",
    from: "imageSummary",
    to: "dom",
    exec: function (imageSummary, context) {
      const document = context.document;
      const root = document.createElement("div");

      // Add a line to the result for each destination
      imageSummary.destinations.forEach(destination => {
        const title = document.createElement("div");
        title.textContent = destination;
        root.appendChild(title);
      });

      // Add the thumbnail image
      if (imageSummary.data != null) {
        const image = context.document.createElement("div");
        const previewHeight = parseInt(256 * imageSummary.height / imageSummary.width,
                                       10);
        const style = "" +
            "width: 256px;" +
            "height: " + previewHeight + "px;" +
            "max-height: 256px;" +
            "background-image: url('" + imageSummary.data + "');" +
            "background-size: 256px " + previewHeight + "px;" +
            "margin: 4px;" +
            "display: block;";
        image.setAttribute("style", style);
        root.appendChild(image);
      }

      // Click handler
      if (imageSummary.href || imageSummary.filename) {
        root.style.cursor = "pointer";
        root.addEventListener("click", () => {
          if (imageSummary.href) {
            let mainWindow = context.environment.chromeWindow;
            mainWindow.openUILinkIn(imageSummary.href, "tab");
          } else if (imageSummary.filename) {
            const file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
            file.initWithPath(imageSummary.filename);
            file.reveal();
          }
        });
      }

      return root;
    }
  },
  {
    item: "command",
    runAt: "client",
    name: "screenshot",
    description: l10n.lookup("screenshotDesc"),
    manual: l10n.lookup("screenshotManual"),
    returnType: "imageSummary",
    buttonId: "command-button-screenshot",
    buttonClass: "command-button",
    tooltipText: l10n.lookup("screenshotTooltipPage"),
    params: [
      filenameParam,
      clientScreenshotParams,
    ],
    exec: function (args, context) {
      // Re-execute the command on the server
      const command = context.typed.replace(/^screenshot/, "screenshot_server");
      let capture = context.updateExec(command).then(output => {
        return output.error ? Promise.reject(output.data) : output.data;
      });

      simulateCameraEffect(context.environment.chromeDocument, "shutter");
      return capture.then(saveScreenshot.bind(null, args, context));
    },
  },
  {
    item: "command",
    runAt: "server",
    name: "screenshot_server",
    hidden: true,
    returnType: "imageSummary",
    params: [
      filenameParam,
      serverScreenshotParams,
    ],
    exec: function (args, context) {
      return captureScreenshot(args, context.environment.document);
    },
  }
];

/**
 * This function is called to simulate camera effects
 */
function simulateCameraEffect(document, effect) {
  let window = document.defaultView;
  if (effect === "shutter") {
    if (Services.prefs.getBoolPref("devtools.screenshot.audio.enabled")) {
      const audioCamera = new window.Audio("resource://devtools/client/themes/audio/shutter.wav");
      audioCamera.play();
    }
  }
  if (effect == "flash") {
    const frames = Cu.cloneInto({ opacity: [ 0, 1 ] }, window);
    document.documentElement.animate(frames, CONTAINER_FLASHING_DURATION);
  }
}

/**
 * This function simply handles the --delay argument before calling
 * createScreenshotData
 */
function captureScreenshot(args, document) {
  if (args.delay > 0) {
    return new Promise((resolve, reject) => {
      document.defaultView.setTimeout(() => {
        createScreenshotData(document, args).then(resolve, reject);
      }, args.delay * 1000);
    });
  }
  return createScreenshotData(document, args);
}

/**
 * There are several possible destinations for the screenshot, SKIP is used
 * in saveScreenshot() whenever one of them is not used
 */
const SKIP = Promise.resolve();

/**
 * Save the captured screenshot to one of several destinations.
 */
function saveScreenshot(args, context, reply) {
  const fileNeeded = args.filename != FILENAME_DEFAULT_VALUE ||
    (!args.imgur && !args.clipboard) || args.file;

  return Promise.all([
    args.clipboard ? saveToClipboard(context, reply) : SKIP,
    args.imgur ? uploadToImgur(reply) : SKIP,
    fileNeeded ? saveToFile(context, reply) : SKIP,
  ]).then(() => reply);
}

/**
 * This does the dirty work of creating a base64 string out of an
 * area of the browser window
 */
function createScreenshotData(document, args) {
  const window = document.defaultView;
  let left = 0;
  let top = 0;
  let width;
  let height;
  const currentX = window.scrollX;
  const currentY = window.scrollY;

  let filename = getFilename(args.filename);

  if (args.fullpage) {
    // Bug 961832: GCLI screenshot shows fixed position element in wrong
    // position if we don't scroll to top
    window.scrollTo(0, 0);
    width = window.innerWidth + window.scrollMaxX - window.scrollMinX;
    height = window.innerHeight + window.scrollMaxY - window.scrollMinY;
    filename = filename.replace(".png", "-fullpage.png");
  } else if (args.selector) {
    ({ top, left, width, height } = getRect(window, args.selector, window));
  } else {
    left = window.scrollX;
    top = window.scrollY;
    width = window.innerWidth;
    height = window.innerHeight;
  }

  // Only adjust for scrollbars when considering the full window
  if (!args.selector) {
    const winUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsIDOMWindowUtils);
    const scrollbarHeight = {};
    const scrollbarWidth = {};
    winUtils.getScrollbarSize(false, scrollbarWidth, scrollbarHeight);
    width -= scrollbarWidth.value;
    height -= scrollbarHeight.value;
  }

  const canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
  const ctx = canvas.getContext("2d");
  const ratio = args.dpr ? args.dpr : window.devicePixelRatio;
  canvas.width = width * ratio;
  canvas.height = height * ratio;
  ctx.scale(ratio, ratio);
  ctx.drawWindow(window, left, top, width, height, "#fff");
  const data = canvas.toDataURL("image/png", "");

  // See comment above on bug 961832
  if (args.fullpage) {
    window.scrollTo(currentX, currentY);
  }

  simulateCameraEffect(document, "flash");

  return Promise.resolve({
    destinations: [],
    data: data,
    height: height,
    width: width,
    filename: filename,
  });
}

/**
 * We may have a filename specified in args, or we might have to generate
 * one.
 */
function getFilename(defaultName) {
  // Create a name for the file if not present
  if (defaultName != FILENAME_DEFAULT_VALUE) {
    return defaultName;
  }

  const date = new Date();
  let dateString = date.getFullYear() + "-" + (date.getMonth() + 1) +
                  "-" + date.getDate();
  dateString = dateString.split("-").map(function (part) {
    if (part.length == 1) {
      part = "0" + part;
    }
    return part;
  }).join("-");

  const timeString = date.toTimeString().replace(/:/g, ".").split(" ")[0];
  return l10n.lookupFormat("screenshotGeneratedFilename",
                           [ dateString, timeString ]) + ".png";
}

/**
 * Save the image data to the clipboard. This returns a promise, so it can
 * be treated exactly like imgur / file processing, but it's really sync
 * for now.
 */
function saveToClipboard(context, reply) {
  return new Promise(resolve => {
    try {
      const channel = NetUtil.newChannel({
        uri: reply.data,
        loadUsingSystemPrincipal: true,
        contentPolicyType: Ci.nsIContentPolicy.TYPE_INTERNAL_IMAGE
      });
      const input = channel.open2();

      const loadContext = context.environment.chromeWindow
                                 .QueryInterface(Ci.nsIInterfaceRequestor)
                                 .getInterface(Ci.nsIWebNavigation)
                                 .QueryInterface(Ci.nsILoadContext);

      const callback = {
        onImageReady(container, status) {
          if (!container) {
            console.error("imgTools.decodeImageAsync failed");
            reply.destinations.push(l10n.lookup("screenshotErrorCopying"));
            resolve();
            return;
          }

          try {
            const wrapped = Cc["@mozilla.org/supports-interface-pointer;1"]
                              .createInstance(Ci.nsISupportsInterfacePointer);
            wrapped.data = container;

            const trans = Cc["@mozilla.org/widget/transferable;1"]
                            .createInstance(Ci.nsITransferable);
            trans.init(loadContext);
            trans.addDataFlavor(channel.contentType);
            trans.setTransferData(channel.contentType, wrapped, -1);

            Services.clipboard.setData(trans, null, Ci.nsIClipboard.kGlobalClipboard);

            reply.destinations.push(l10n.lookup("screenshotCopied"));
          } catch (ex) {
            console.error(ex);
            reply.destinations.push(l10n.lookup("screenshotErrorCopying"));
          }
          resolve();
        }
      };

      const threadManager = Cc["@mozilla.org/thread-manager;1"].getService();
      const imgTools = Cc["@mozilla.org/image/tools;1"]
                          .getService(Ci.imgITools);
      imgTools.decodeImageAsync(input, channel.contentType, callback,
                                threadManager.currentThread);
    } catch (ex) {
      console.error(ex);
      reply.destinations.push(l10n.lookup("screenshotErrorCopying"));
      resolve();
    }
  });
}

/**
 * Upload screenshot data to Imgur, returning a promise of a URL (as a string)
 */
function uploadToImgur(reply) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    const fd = new FormData();
    fd.append("image", reply.data.split(",")[1]);
    fd.append("type", "base64");
    fd.append("title", reply.filename);

    const postURL = Services.prefs.getCharPref("devtools.gcli.imgurUploadURL");
    const clientID = "Client-ID " +
                     Services.prefs.getCharPref("devtools.gcli.imgurClientID");

    xhr.open("POST", postURL);
    xhr.setRequestHeader("Authorization", clientID);
    xhr.send(fd);
    xhr.responseType = "json";

    xhr.onreadystatechange = function () {
      if (xhr.readyState == 4) {
        if (xhr.status == 200) {
          reply.href = xhr.response.data.link;
          reply.destinations.push(l10n.lookupFormat("screenshotImgurUploaded",
                                                    [ reply.href ]));
        } else {
          reply.destinations.push(l10n.lookup("screenshotImgurError"));
        }

        resolve();
      }
    };
  });
}

/**
 * Progress listener that forwards calls to a transfer object.
 *
 * This is used below in saveToFile to forward progress updates from the
 * nsIWebBrowserPersist object that does the actual saving to the nsITransfer
 * which just represents the operation for the Download Manager.  This keeps the
 * Download Manager updated on saving progress and completion, so that it gives
 * visual feedback from the downloads toolbar button when the save is done.
 *
 * It also allows the browser window to show auth prompts if needed (should not
 * be needed for saving screenshots).
 *
 * This code is borrowed directly from contentAreaUtils.js.
 */
function DownloadListener(win, transfer) {
  this.window = win;
  this.transfer = transfer;

  // For most method calls, forward to the transfer object.
  for (let name in transfer) {
    if (name != "QueryInterface" &&
        name != "onStateChange") {
      this[name] = (...args) => transfer[name].apply(transfer, args);
    }
  }

  // Allow saveToFile to await completion for error handling
  this._completedDeferred = defer();
  this.completed = this._completedDeferred.promise;
}

DownloadListener.prototype = {
  QueryInterface: function (iid) {
    if (iid.equals(Ci.nsIInterfaceRequestor) ||
        iid.equals(Ci.nsIWebProgressListener) ||
        iid.equals(Ci.nsIWebProgressListener2) ||
        iid.equals(Ci.nsISupports)) {
      return this;
    }
    throw Cr.NS_ERROR_NO_INTERFACE;
  },

  getInterface: function (iid) {
    if (iid.equals(Ci.nsIAuthPrompt) ||
        iid.equals(Ci.nsIAuthPrompt2)) {
      let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"]
                 .getService(Ci.nsIPromptFactory);
      return ww.getPrompt(this.window, iid);
    }

    throw Cr.NS_ERROR_NO_INTERFACE;
  },

  onStateChange: function (webProgress, request, state, status) {
    // Check if the download has completed
    if ((state & Ci.nsIWebProgressListener.STATE_STOP) &&
        (state & Ci.nsIWebProgressListener.STATE_IS_NETWORK)) {
      if (status == Cr.NS_OK) {
        this._completedDeferred.resolve();
      } else {
        this._completedDeferred.reject();
      }
    }

    this.transfer.onStateChange.apply(this.transfer, arguments);
  }
};

/**
 * Save the screenshot data to disk, returning a promise which is resolved on
 * completion.
 */
var saveToFile = Task.async(function* (context, reply) {
  let document = context.environment.chromeDocument;
  let window = context.environment.chromeWindow;

  // Check there is a .png extension to filename
  if (!reply.filename.match(/.png$/i)) {
    reply.filename += ".png";
  }

  let downloadsDir = yield Downloads.getPreferredDownloadsDirectory();
  let downloadsDirExists = yield OS.File.exists(downloadsDir);
  if (downloadsDirExists) {
    // If filename is absolute, it will override the downloads directory and
    // still be applied as expected.
    reply.filename = OS.Path.join(downloadsDir, reply.filename);
  }

  let sourceURI = Services.io.newURI(reply.data);
  let targetFile = new FileUtils.File(reply.filename);
  let targetFileURI = Services.io.newFileURI(targetFile);

  // Create download and track its progress.
  // This is adapted from saveURL in contentAreaUtils.js, but simplified greatly
  // and modified to allow saving to arbitrary paths on disk.  Using these
  // objects as opposed to just writing with OS.File allows us to tie into the
  // download manager to record a download entry and to get visual feedback from
  // the downloads toolbar button when the save is done.
  const nsIWBP = Ci.nsIWebBrowserPersist;
  const flags = nsIWBP.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
                nsIWBP.PERSIST_FLAGS_FORCE_ALLOW_COOKIES |
                nsIWBP.PERSIST_FLAGS_BYPASS_CACHE |
                nsIWBP.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
  let isPrivate =
    PrivateBrowsingUtils.isContentWindowPrivate(document.defaultView);
  let persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
                  .createInstance(Ci.nsIWebBrowserPersist);
  persist.persistFlags = flags;
  let tr = Cc["@mozilla.org/transfer;1"].createInstance(Ci.nsITransfer);
  tr.init(sourceURI,
          targetFileURI,
          "",
          null,
          null,
          null,
          persist,
          isPrivate);
  let listener = new DownloadListener(window, tr);
  persist.progressListener = listener;
  persist.savePrivacyAwareURI(sourceURI,
                              null,
                              document.documentURIObject,
                              Ci.nsIHttpChannel.REFERRER_POLICY_UNSET,
                              null,
                              null,
                              targetFileURI,
                              isPrivate);

  try {
    // Await successful completion of the save via the listener
    yield listener.completed;
    reply.destinations.push(l10n.lookup("screenshotSavedToFile") +
                            ` "${reply.filename}"`);
  } catch (ex) {
    console.error(ex);
    reply.destinations.push(l10n.lookup("screenshotErrorSavingToFile") + " " +
                            reply.filename);
  }
});
PK
!<��9-9-Achrome/devtools/modules/devtools/shared/gcli/commands/security.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
 * The Security devtool supports the following arguments:
 * * Security CSP
 *   Provides feedback about the current CSP
 *
 *  * Security referrer
 *    Provides information about the current referrer policy
 */

"use strict";

const { Ci } = require("chrome");
const l10n = require("gcli/l10n");

const GOOD_IMG_SRC = "chrome://devtools/skin/images/gcli_sec_good.svg";
const MOD_IMG_SRC = "chrome://devtools/skin/images/gcli_sec_moderate.svg";
const BAD_IMG_SRC = "chrome://devtools/skin/images/gcli_sec_bad.svg";

// special handling within policy
const POLICY_REPORT_ONLY = "report-only";

// special handling of directives
const DIR_UPGRADE_INSECURE = "upgrade-insecure-requests";
const DIR_BLOCK_ALL_MIXED_CONTENT = "block-all-mixed-content";

// special handling of sources
const SRC_UNSAFE_INLINE = "'unsafe-inline'";
const SRC_UNSAFE_EVAL = "'unsafe-eval'";

const WILDCARD_MSG = l10n.lookup("securityCSPRemWildCard");
const XSS_WARNING_MSG = l10n.lookup("securityCSPPotentialXSS");
const NO_CSP_ON_PAGE_MSG = l10n.lookup("securityCSPNoCSPOnPage");
const CONTENT_SECURITY_POLICY_MSG = l10n.lookup("securityCSPHeaderOnPage");
const CONTENT_SECURITY_POLICY_REPORT_ONLY_MSG = l10n.lookup("securityCSPROHeaderOnPage");

const NEXT_URI_HEADER = l10n.lookup("securityReferrerNextURI");
const CALCULATED_REFERRER_HEADER = l10n.lookup("securityReferrerCalculatedReferrer");
/* The official names from the W3C Referrer Policy Draft http://www.w3.org/TR/referrer-policy/ */
const REFERRER_POLICY_NAMES = [
  "None When Downgrade (default)",
  "None", "Origin Only",
  "Origin When Cross-Origin", "Unsafe URL"
];

exports.items = [
  {
    // --- General Security information
    name: "security",
    description: l10n.lookup("securityPrivacyDesc"),
    manual: l10n.lookup("securityManual")
  },
  {
    // --- CSP specific Security information
    item: "command",
    runAt: "server",
    name: "security csp",
    description: l10n.lookup("securityCSPDesc"),
    manual: l10n.lookup("securityCSPManual"),
    returnType: "securityCSPInfo",
    exec: function (args, context) {
      let cspJSON = context.environment.document.nodePrincipal.cspJSON;
      let cspOBJ = JSON.parse(cspJSON);

      let outPolicies = [];

      let policies = cspOBJ["csp-policies"];

      // loop over all the different policies
      for (let csp in policies) {
        let curPolicy = policies[csp];

        // loop over all the directive-values within that policy
        let outDirectives = [];
        let outHeader = CONTENT_SECURITY_POLICY_MSG;
        for (let dir in curPolicy) {
          let curDir = curPolicy[dir];

          // when iterating properties within the obj we might also
          // encounter the 'report-only' flag, which is not a csp directive.
          if (dir === POLICY_REPORT_ONLY) {
            outHeader = curPolicy[POLICY_REPORT_ONLY] === true ?
                          CONTENT_SECURITY_POLICY_REPORT_ONLY_MSG :
                          CONTENT_SECURITY_POLICY_MSG;
            continue;
          }

          // loop over all the directive-sources within that directive
          let outSrcs = [];

          // special case handling for the directives
          // upgrade-insecure-requests and block-all-mixed-content
          // which do not include any srcs
          if (dir === DIR_UPGRADE_INSECURE ||
              dir === DIR_BLOCK_ALL_MIXED_CONTENT) {
            outSrcs.push({
              icon: GOOD_IMG_SRC,
              // no src
              src: "",
              // no description
              desc: ""
            });
          }

          for (let src in curDir) {
            let curSrc = curDir[src];

            // the default icon and descritpion of the directive-src
            let outIcon = GOOD_IMG_SRC;
            let outDesc = "";

            if (curSrc.indexOf("*") > -1) {
              outIcon = MOD_IMG_SRC;
              outDesc = WILDCARD_MSG;
            }
            if (curSrc == SRC_UNSAFE_INLINE || curSrc == SRC_UNSAFE_EVAL) {
              outIcon = BAD_IMG_SRC;
              outDesc = XSS_WARNING_MSG;
            }
            outSrcs.push({
              icon: outIcon,
              src: curSrc,
              desc: outDesc
            });
          }
          // append info about that directive to the directives array
          outDirectives.push({
            dirValue: dir,
            dirSrc: outSrcs
          });
        }
        // append info about the policy to the policies array
        outPolicies.push({
          header: outHeader,
          directives: outDirectives
        });
      }
      return outPolicies;
    }
  },
  {
    item: "converter",
    from: "securityCSPInfo",
    to: "view",
    exec: function (cspInfo, context) {
      const url = context.environment.target.url;

      if (cspInfo.length == 0) {
        return context.createView({
          html:
            "<table class='gcli-csp-detail' cellspacing='10' valign='top'>" +
            "  <tr>" +
            "    <td> <img src='" + BAD_IMG_SRC + "' width='20px' /> </td> " +
            "    <td>" + NO_CSP_ON_PAGE_MSG + " <b>" + url + "</b></td>" +
            "  </tr>" +
            "</table>"});
      }

      /* eslint-disable max-len */
      return context.createView({
        html:
          "<table class='gcli-csp-detail' cellspacing='10' valign='top'>" +
          // iterate all policies
          "  <tr foreach='csp in ${cspinfo}' >" +
          "    <td> ${csp.header} <b>" + url + "</b><br/><br/>" +
          "      <table class='gcli-csp-dir-detail' valign='top'>" +
          // >> iterate all directives
          "        <tr foreach='dir in ${csp.directives}' >" +
          "          <td valign='top'> ${dir.dirValue} </td>" +
          "          <td valign='top'>" +
          "            <table class='gcli-csp-src-detail' valign='top'>" +
          // >> >> iterate all srs
          "              <tr foreach='src in ${dir.dirSrc}' >" +
          "                <td valign='center' width='20px'> <img src= \"${src.icon}\" width='20px' /> </td> " +
          "                <td valign='center' width='200px'> ${src.src} </td>" +
          "                <td valign='center'> ${src.desc} </td>" +
          "              </tr>" +
          "            </table>" +
          "          </td>" +
          "        </tr>" +
          "      </table>" +
          "    </td>" +
          "  </tr>" +
          "</table>",
        data: {
          cspinfo: cspInfo,
        }
      });
      /* eslint-enable max-len */
    }
  },
  {
    // --- Referrer Policy specific Security information
    item: "command",
    runAt: "server",
    name: "security referrer",
    description: l10n.lookup("securityReferrerPolicyDesc"),
    manual: l10n.lookup("securityReferrerPolicyManual"),
    returnType: "securityReferrerPolicyInfo",
    exec: function (args, context) {
      let doc = context.environment.document;

      let { referrerPolicy } = doc;

      let pageURI = doc.documentURIObject;
      let sameDomainReferrer = "";
      let otherDomainReferrer = "";
      let downgradeReferrer = "";
      let otherDowngradeReferrer = "";
      let origin = pageURI.prePath;

      switch (referrerPolicy) {
        case Ci.nsIHttpChannel.REFERRER_POLICY_NO_REFERRER:
          // sends no referrer
          sameDomainReferrer
            = otherDomainReferrer
            = downgradeReferrer
            = otherDowngradeReferrer
            = "(no referrer)";
          break;
        case Ci.nsIHttpChannel.REFERRER_POLICY_ORIGIN:
          // only sends the origin of the referring URL
          sameDomainReferrer
            = otherDomainReferrer
            = downgradeReferrer
            = otherDowngradeReferrer
            = origin;
          break;
        case Ci.nsIHttpChannel.REFERRER_POLICY_ORIGIN_WHEN_XORIGIN:
          // same as default, but reduced to ORIGIN when cross-origin.
          sameDomainReferrer = pageURI.spec;
          otherDomainReferrer
            = downgradeReferrer
            = otherDowngradeReferrer
            = origin;
          break;
        case Ci.nsIHttpChannel.REFERRER_POLICY_UNSAFE_URL:
          // always sends the referrer, even on downgrade.
          sameDomainReferrer
            = otherDomainReferrer
            = downgradeReferrer
            = otherDowngradeReferrer
            = pageURI.spec;
          break;
        case Ci.nsIHttpChannel.REFERRER_POLICY_NO_REFERRER_WHEN_DOWNGRADE:
          // default state, doesn't send referrer from https->http
          sameDomainReferrer = otherDomainReferrer = pageURI.spec;
          downgradeReferrer = otherDowngradeReferrer = "(no referrer)";
          break;
        default:
          // this is a new referrer policy which we do not know about
          sameDomainReferrer
            = otherDomainReferrer
            = downgradeReferrer
            = otherDowngradeReferrer
            = "(unknown Referrer Policy)";
          break;
      }

      let sameDomainUri = origin + "/*";

      let referrerUrls = [
        // add the referrer uri 'referrer' we would send when visiting 'uri'
        {
          uri: pageURI.scheme + "://example.com/",
          referrer: otherDomainReferrer,
          description: l10n.lookup("securityReferrerPolicyOtherDomain")},
        {
          uri: sameDomainUri,
          referrer: sameDomainReferrer,
          description: l10n.lookup("securityReferrerPolicySameDomain")}
      ];

      if (pageURI.schemeIs("https")) {
        // add the referrer we would send on downgrading http->https
        if (sameDomainReferrer != downgradeReferrer) {
          referrerUrls.push({
            uri: "http://" + pageURI.hostPort + "/*",
            referrer: downgradeReferrer,
            description:
              l10n.lookup("securityReferrerPolicySameDomainDowngrade")
          });
        }
        if (otherDomainReferrer != otherDowngradeReferrer) {
          referrerUrls.push({
            uri: "http://example.com/",
            referrer: otherDowngradeReferrer,
            description:
              l10n.lookup("securityReferrerPolicyOtherDomainDowngrade")
          });
        }
      }

      return {
        header: l10n.lookupFormat("securityReferrerPolicyReportHeader",
                                  [pageURI.spec]),
        policyName: REFERRER_POLICY_NAMES[referrerPolicy],
        urls: referrerUrls
      };
    }
  },
  {
    item: "converter",
    from: "securityReferrerPolicyInfo",
    to: "view",
    exec: function (referrerPolicyInfo, context) {
      return context.createView({
        html:
          "<div class='gcli-referrer-policy'>" +
          "  <strong> ${rpi.header} </strong> <br />" +
          "  ${rpi.policyName} <br />" +
          "  <table class='gcli-referrer-policy-detail' cellspacing='10' >" +
          "    <tr>" +
          "      <th> " + NEXT_URI_HEADER + " </th>" +
          "      <th> " + CALCULATED_REFERRER_HEADER + " </th>" +
          "    </tr>" +
          // iterate all policies
          "    <tr foreach='nextURI in ${rpi.urls}' >" +
          "      <td> ${nextURI.description} (e.g., ${nextURI.uri}) </td>" +
          "      <td> ${nextURI.referrer} </td>" +
          "    </tr>" +
          "  </table>" +
          "</div>",
        data: {
          rpi: referrerPolicyInfo,
        }
      });
    }
  }
];
PK
!<
���Cchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/cli.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var util = require('./util/util');
var host = require('./util/host');
var l10n = require('./util/l10n');

var view = require('./ui/view');
var Parameter = require('./commands/commands').Parameter;
var CommandOutputManager = require('./commands/commands').CommandOutputManager;

var Status = require('./types/types').Status;
var Conversion = require('./types/types').Conversion;
var commandModule = require('./types/command');
var selectionModule = require('./types/selection');

var Argument = require('./types/types').Argument;
var ArrayArgument = require('./types/types').ArrayArgument;
var NamedArgument = require('./types/types').NamedArgument;
var TrueNamedArgument = require('./types/types').TrueNamedArgument;
var MergedArgument = require('./types/types').MergedArgument;
var ScriptArgument = require('./types/types').ScriptArgument;

var RESOLVED = Promise.resolve(undefined);

// Helper to produce a `deferred` object
// using DOM Promise
function defer() {
  let resolve, reject;
  let p = new Promise((a, b) => {
    resolve = a;
    reject = b;
  });
  return {
    promise: p,
    resolve: resolve,
    reject: reject
  };
}

/**
 * This is a list of the known command line components to enable certain
 * privileged commands to alter parts of a running command line. It is an array
 * of objects shaped like:
 *   { conversionContext:..., executionContext:..., mapping:... }
 * So lookup is O(n) where 'n' is the number of command lines.
 */
var instances = [];

/**
 * An indexOf that looks-up both types of context
 */
function instanceIndex(context) {
  for (var i = 0; i < instances.length; i++) {
    var instance = instances[i];
    if (instance.conversionContext === context ||
        instance.executionContext === context) {
      return i;
    }
  }
  return -1;
}

/**
 * findInstance gets access to a Terminal object given a conversionContext or
 * an executionContext (it doesn't have to be a terminal object, just whatever
 * was passed into addMapping()
 */
exports.getMapping = function(context) {
  var index = instanceIndex(context);
  if (index === -1) {
    console.log('Missing mapping for context: ', context);
    console.log('Known contexts: ', instances);
    throw new Error('Missing mapping for context');
  }
  return instances[index].mapping;
};

/**
 * Add a requisition context->terminal mapping
 */
var addMapping = function(requisition) {
  if (instanceIndex(requisition.conversionContext) !== -1) {
    throw new Error('Remote existing mapping before adding a new one');
  }

  instances.push({
    conversionContext: requisition.conversionContext,
    executionContext: requisition.executionContext,
    mapping: { requisition: requisition }
  });
};

/**
 * Remove a requisition context->terminal mapping
 */
var removeMapping = function(requisition) {
  var index = instanceIndex(requisition.conversionContext);
  instances.splice(index, 1);
};

/**
 * Assignment is a link between a parameter and the data for that parameter.
 * The data for the parameter is available as in the preferred type and as
 * an Argument for the CLI.
 * <p>We also record validity information where applicable.
 * <p>For values, null and undefined have distinct definitions. null means
 * that a value has been provided, undefined means that it has not.
 * Thus, null is a valid default value, and common because it identifies an
 * parameter that is optional. undefined means there is no value from
 * the command line.
 * @constructor
 */
function Assignment(param) {
  // The parameter that we are assigning to
  this.param = param;
  this.conversion = undefined;
}

/**
 * Easy accessor for conversion.arg.
 * This is a read-only property because writes to arg should be done through
 * the 'conversion' property.
 */
Object.defineProperty(Assignment.prototype, 'arg', {
  get: function() {
    return this.conversion == null ? undefined : this.conversion.arg;
  },
  enumerable: true
});

/**
 * Easy accessor for conversion.value.
 * This is a read-only property because writes to value should be done through
 * the 'conversion' property.
 */
Object.defineProperty(Assignment.prototype, 'value', {
  get: function() {
    return this.conversion == null ? undefined : this.conversion.value;
  },
  enumerable: true
});

/**
 * Easy (and safe) accessor for conversion.message
 */
Object.defineProperty(Assignment.prototype, 'message', {
  get: function() {
    if (this.conversion != null && this.conversion.message) {
      return this.conversion.message;
    }
    // ERROR conversions have messages, VALID conversions don't need one, so
    // we just need to consider INCOMPLETE conversions.
    if (this.getStatus() === Status.INCOMPLETE) {
      return l10n.lookupFormat('cliIncompleteParam', [ this.param.name ]);
    }
    return '';
  },
  enumerable: true
});

/**
 * Easy (and safe) accessor for conversion.getPredictions()
 * @return An array of objects with name and value elements. For example:
 * [ { name:'bestmatch', value:foo1 }, { name:'next', value:foo2 }, ... ]
 */
Assignment.prototype.getPredictions = function(context) {
  return this.conversion == null ? [] : this.conversion.getPredictions(context);
};

/**
 * Accessor for a prediction by index.
 * This is useful above <tt>getPredictions()[index]</tt> because it normalizes
 * index to be within the bounds of the predictions, which means that the UI
 * can maintain an index of which prediction to choose without caring how many
 * predictions there are.
 * @param rank The index of the prediction to choose
 */
Assignment.prototype.getPredictionRanked = function(context, rank) {
  if (rank == null) {
    rank = 0;
  }

  if (this.isInName()) {
    return Promise.resolve(undefined);
  }

  return this.getPredictions(context).then(predictions => {
    if (predictions.length === 0) {
      return undefined;
    }

    rank = rank % predictions.length;
    if (rank < 0) {
      rank = predictions.length + rank;
    }
    return predictions[rank];
  });
};

/**
 * Some places want to take special action if we are in the name part of a
 * named argument (i.e. the '--foo' bit).
 * Currently this does not take actual cursor position into account, it just
 * assumes that the cursor is at the end. In the future we will probably want
 * to take this into account.
 */
Assignment.prototype.isInName = function() {
  return this.conversion.arg.type === 'NamedArgument' &&
         this.conversion.arg.prefix.slice(-1) !== ' ';
};

/**
 * Work out what the status of the current conversion is which involves looking
 * not only at the conversion, but also checking if data has been provided
 * where it should.
 * @param arg For assignments with multiple args (e.g. array assignments) we
 * can narrow the search for status to a single argument.
 */
Assignment.prototype.getStatus = function(arg) {
  if (this.param.isDataRequired && !this.conversion.isDataProvided()) {
    return Status.INCOMPLETE;
  }

  // Selection/Boolean types with a defined range of values will say that
  // '' is INCOMPLETE, but the parameter may be optional, so we don't ask
  // if the user doesn't need to enter something and hasn't done so.
  if (!this.param.isDataRequired && this.arg.type === 'BlankArgument') {
    return Status.VALID;
  }

  return this.conversion.getStatus(arg);
};

/**
 * Helper when we're rebuilding command lines.
 */
Assignment.prototype.toString = function() {
  return this.conversion.toString();
};

/**
 * For test/debug use only. The output from this function is subject to wanton
 * random change without notice, and should not be relied upon to even exist
 * at some later date.
 */
Object.defineProperty(Assignment.prototype, '_summaryJson', {
  get: function() {
    return {
      param: this.param.name + '/' + this.param.type.name,
      defaultValue: this.param.defaultValue,
      arg: this.conversion.arg._summaryJson,
      value: this.value,
      message: this.message,
      status: this.getStatus().toString()
    };
  },
  enumerable: true
});

exports.Assignment = Assignment;


/**
 * How to dynamically execute JavaScript code
 */
var customEval = eval;

/**
 * Setup a function to be called in place of 'eval', generally for security
 * reasons
 */
exports.setEvalFunction = function(newCustomEval) {
  customEval = newCustomEval;
};

/**
 * Remove the binding done by setEvalFunction().
 * We purposely set customEval to undefined rather than to 'eval' because there
 * is an implication of setEvalFunction that we're in a security sensitive
 * situation. What if we can trick GCLI into calling unsetEvalFunction() at the
 * wrong time?
 * So to properly undo the effects of setEvalFunction(), you need to call
 * setEvalFunction(eval) rather than unsetEvalFunction(), however the latter is
 * preferred in most cases.
 */
exports.unsetEvalFunction = function() {
  customEval = undefined;
};

/**
 * 'eval' command
 */
var evalCmd = {
  item: 'command',
  name: '{',
  params: [
    {
      name: 'javascript',
      type: 'javascript',
      description: ''
    }
  ],
  hidden: true,
  description: { key: 'cliEvalJavascript' },
  exec: function(args, context) {
    var reply = customEval(args.javascript);
    return context.typedData(typeof reply, reply);
  },
  isCommandRegexp: /^\s*\{\s*/
};

exports.items = [ evalCmd ];

/**
 * This is a special assignment to reflect the command itself.
 */
function CommandAssignment(requisition) {
  var commandParamMetadata = {
    name: '__command',
    type: { name: 'command', allowNonExec: false }
  };
  // This is a hack so that rather than reply with a generic description of the
  // command assignment, we reply with the description of the assigned command,
  // (using a generic term if there is no assigned command)
  var self = this;
  Object.defineProperty(commandParamMetadata, 'description', {
    get: function() {
      var value = self.value;
      return value && value.description ?
          value.description :
          'The command to execute';
    },
    enumerable: true
  });
  this.param = new Parameter(requisition.system.types, commandParamMetadata);
}

CommandAssignment.prototype = Object.create(Assignment.prototype);

CommandAssignment.prototype.getStatus = function(arg) {
  return Status.combine(
    Assignment.prototype.getStatus.call(this, arg),
    this.conversion.value && this.conversion.value.exec ?
            Status.VALID : Status.INCOMPLETE
  );
};

exports.CommandAssignment = CommandAssignment;


/**
 * Special assignment used when ignoring parameters that don't have a home
 */
function UnassignedAssignment(requisition, arg) {
  var isIncompleteName = (arg.text.charAt(0) === '-');
  this.param = new Parameter(requisition.system.types, {
    name: '__unassigned',
    description: l10n.lookup('cliOptions'),
    type: {
      name: 'param',
      requisition: requisition,
      isIncompleteName: isIncompleteName
    }
  });

  // It would be nice to do 'conversion = parm.type.parse(arg, ...)' except
  // that type.parse returns a promise (even though it's synchronous in this
  // case)
  if (isIncompleteName) {
    var lookup = commandModule.getDisplayedParamLookup(requisition);
    var predictions = selectionModule.findPredictions(arg, lookup);
    this.conversion = selectionModule.convertPredictions(arg, predictions);
  }
  else {
    var message = l10n.lookup('cliUnusedArg');
    this.conversion = new Conversion(undefined, arg, Status.ERROR, message);
  }

  this.conversion.assignment = this;
}

UnassignedAssignment.prototype = Object.create(Assignment.prototype);

UnassignedAssignment.prototype.getStatus = function(arg) {
  return this.conversion.getStatus();
};

var logErrors = true;

/**
 * Allow tests that expect failures to avoid clogging up the console
 */
Object.defineProperty(exports, 'logErrors', {
  get: function() {
    return logErrors;
  },
  set: function(val) {
    logErrors = val;
  },
  enumerable: true
});

/**
 * A Requisition collects the information needed to execute a command.
 *
 * (For a definition of the term, see http://en.wikipedia.org/wiki/Requisition)
 * This term is used because carries the notion of a work-flow, or process to
 * getting the information to execute a command correct.
 * There is little point in a requisition for parameter-less commands because
 * there is no information to collect. A Requisition is a collection of
 * assignments of values to parameters, each handled by an instance of
 * Assignment.
 *
 * @param system Allows access to the various plug-in points in GCLI. At a
 * minimum it must contain commands and types objects.
 * @param options A set of options to customize how GCLI is used. Includes:
 * - environment An optional opaque object passed to commands in the
 *   Execution Context.
 * - document A DOM Document passed to commands using the Execution Context in
 *   order to allow creation of DOM nodes. If missing Requisition will use the
 *   global 'document', or leave undefined.
 * - commandOutputManager A custom commandOutputManager to which output should
 *   be sent
 * @constructor
 */
function Requisition(system, options) {
  options = options || {};

  this.environment = options.environment || {};
  this.document = options.document;
  if (this.document == null) {
    try {
      this.document = document;
    }
    catch (ex) {
      // Ignore
    }
  }

  this.commandOutputManager = options.commandOutputManager || new CommandOutputManager();
  this.system = system;

  this.shell = {
    cwd: '/', // Where we store the current working directory
    env: {}   // Where we store the current environment
  };

  // The command that we are about to execute.
  // @see setCommandConversion()
  this.commandAssignment = new CommandAssignment(this);

  // The object that stores of Assignment objects that we are filling out.
  // The Assignment objects are stored under their param.name for named
  // lookup. Note: We make use of the property of Javascript objects that
  // they are not just hashmaps, but linked-list hashmaps which iterate in
  // insertion order.
  // _assignments excludes the commandAssignment.
  this._assignments = {};

  // The count of assignments. Excludes the commandAssignment
  this.assignmentCount = 0;

  // Used to store cli arguments in the order entered on the cli
  this._args = [];

  // Used to store cli arguments that were not assigned to parameters
  this._unassigned = [];

  // Changes can be asynchronous, when one update starts before another
  // finishes we abandon the former change
  this._nextUpdateId = 0;

  // We can set a prefix to typed commands to make it easier to focus on
  // Allowing us to type "add -a; commit" in place of "git add -a; git commit"
  this.prefix = '';

  addMapping(this);
  this._setBlankAssignment(this.commandAssignment);

  // If a command calls context.update then the UI needs some way to be
  // informed of the change
  this.onExternalUpdate = util.createEvent('Requisition.onExternalUpdate');
}

/**
 * Avoid memory leaks
 */
Requisition.prototype.destroy = function() {
  this.document = undefined;
  this.environment = undefined;
  removeMapping(this);
};

/**
 * If we're about to make an asynchronous change when other async changes could
 * overtake this one, then we want to be able to bail out if overtaken. The
 * value passed back from beginChange should be passed to endChangeCheckOrder
 * on completion of calculation, before the results are applied in order to
 * check that the calculation has not been overtaken
 */
Requisition.prototype._beginChange = function() {
  var updateId = this._nextUpdateId;
  this._nextUpdateId++;
  return updateId;
};

/**
 * Check to see if another change has started since updateId started.
 * This allows us to bail out of an update.
 * It's hard to make updates atomic because until you've responded to a parse
 * of the command argument, you don't know how to parse the arguments to that
 * command.
 */
Requisition.prototype._isChangeCurrent = function(updateId) {
  return updateId + 1 === this._nextUpdateId;
};

/**
 * See notes on beginChange
 */
Requisition.prototype._endChangeCheckOrder = function(updateId) {
  if (updateId + 1 !== this._nextUpdateId) {
    // An update that started after we did has already finished, so our
    // changes are out of date. Abandon further work.
    return false;
  }

  return true;
};

var legacy = false;

/**
 * Functions and data related to the execution of a command
 */
Object.defineProperty(Requisition.prototype, 'executionContext', {
  get: function() {
    if (this._executionContext == null) {
      this._executionContext = {
        defer: defer,
        typedData: function(type, data) {
          return {
            isTypedData: true,
            data: data,
            type: type
          };
        },
        getArgsObject: this.getArgsObject.bind(this)
      };

      // Alias requisition so we're clear about what's what
      var requisition = this;
      Object.defineProperty(this._executionContext, 'prefix', {
        get: function() { return requisition.prefix; },
        enumerable: true
      });
      Object.defineProperty(this._executionContext, 'typed', {
        get: function() { return requisition.toString(); },
        enumerable: true
      });
      Object.defineProperty(this._executionContext, 'environment', {
        get: function() { return requisition.environment; },
        enumerable: true
      });
      Object.defineProperty(this._executionContext, 'shell', {
        get: function() { return requisition.shell; },
        enumerable: true
      });
      Object.defineProperty(this._executionContext, 'system', {
        get: function() { return requisition.system; },
        enumerable: true
      });

      this._executionContext.updateExec = this._contextUpdateExec.bind(this);

      if (legacy) {
        this._executionContext.createView = view.createView;
        this._executionContext.exec = this.exec.bind(this);
        this._executionContext.update = this._contextUpdate.bind(this);

        Object.defineProperty(this._executionContext, 'document', {
          get: function() { return requisition.document; },
          enumerable: true
        });
      }
    }

    return this._executionContext;
  },
  enumerable: true
});

/**
 * Functions and data related to the conversion of the output of a command
 */
Object.defineProperty(Requisition.prototype, 'conversionContext', {
  get: function() {
    if (this._conversionContext == null) {
      this._conversionContext = {
        defer: defer,

        createView: view.createView,
        exec: this.exec.bind(this),
        update: this._contextUpdate.bind(this),
        updateExec: this._contextUpdateExec.bind(this)
      };

      // Alias requisition so we're clear about what's what
      var requisition = this;

      Object.defineProperty(this._conversionContext, 'document', {
        get: function() { return requisition.document; },
        enumerable: true
      });
      Object.defineProperty(this._conversionContext, 'environment', {
        get: function() { return requisition.environment; },
        enumerable: true
      });
      Object.defineProperty(this._conversionContext, 'system', {
        get: function() { return requisition.system; },
        enumerable: true
      });
    }

    return this._conversionContext;
  },
  enumerable: true
});

/**
 * Assignments have an order, so we need to store them in an array.
 * But we also need named access ...
 * @return The found assignment, or undefined, if no match was found
 */
Requisition.prototype.getAssignment = function(nameOrNumber) {
  var name = (typeof nameOrNumber === 'string') ?
    nameOrNumber :
    Object.keys(this._assignments)[nameOrNumber];
  return this._assignments[name] || undefined;
};

/**
 * Where parameter name == assignment names - they are the same
 */
Requisition.prototype.getParameterNames = function() {
  return Object.keys(this._assignments);
};

/**
 * The overall status is the most severe status.
 * There is no such thing as an INCOMPLETE overall status because the
 * definition of INCOMPLETE takes into account the cursor position to say 'this
 * isn't quite ERROR because the user can fix it by typing', however overall,
 * this is still an error status.
 */
Object.defineProperty(Requisition.prototype, 'status', {
  get: function() {
    var status = Status.VALID;
    if (this._unassigned.length !== 0) {
      var isAllIncomplete = true;
      this._unassigned.forEach(function(assignment) {
        if (!assignment.param.type.isIncompleteName) {
          isAllIncomplete = false;
        }
      });
      status = isAllIncomplete ? Status.INCOMPLETE : Status.ERROR;
    }

    this.getAssignments(true).forEach(function(assignment) {
      var assignStatus = assignment.getStatus();
      if (assignStatus > status) {
        status = assignStatus;
      }
    }, this);
    if (status === Status.INCOMPLETE) {
      status = Status.ERROR;
    }
    return status;
  },
  enumerable : true
});

/**
 * If ``requisition.status != VALID`` message then return a string which
 * best describes what is wrong. Generally error messages are delivered by
 * looking at the error associated with the argument at the cursor, but there
 * are times when you just want to say 'tell me the worst'.
 * If ``requisition.status != VALID`` then return ``null``.
 */
Requisition.prototype.getStatusMessage = function() {
  if (this.commandAssignment.getStatus() !== Status.VALID) {
    return l10n.lookupFormat('cliUnknownCommand2',
                             [ this.commandAssignment.arg.text ]);
  }

  var assignments = this.getAssignments();
  for (var i = 0; i < assignments.length; i++) {
    if (assignments[i].getStatus() !== Status.VALID) {
      return assignments[i].message;
    }
  }

  if (this._unassigned.length !== 0) {
    return l10n.lookup('cliUnusedArg');
  }

  return null;
};

/**
 * Extract the names and values of all the assignments, and return as
 * an object.
 */
Requisition.prototype.getArgsObject = function() {
  var args = {};
  this.getAssignments().forEach(function(assignment) {
    args[assignment.param.name] = assignment.conversion.isDataProvided() ?
            assignment.value :
            assignment.param.defaultValue;
  }, this);
  return args;
};

/**
 * Access the arguments as an array.
 * @param includeCommand By default only the parameter arguments are
 * returned unless (includeCommand === true), in which case the list is
 * prepended with commandAssignment.arg
 */
Requisition.prototype.getAssignments = function(includeCommand) {
  var assignments = [];
  if (includeCommand === true) {
    assignments.push(this.commandAssignment);
  }
  Object.keys(this._assignments).forEach(function(name) {
    assignments.push(this.getAssignment(name));
  }, this);
  return assignments;
};

/**
 * There are a few places where we need to know what the 'next thing' is. What
 * is the user going to be filling out next (assuming they don't enter a named
 * argument). The next argument is the first in line that is both blank, and
 * that can be filled in positionally.
 * @return The next assignment to be used, or null if all the positional
 * parameters have values.
 */
Requisition.prototype._getFirstBlankPositionalAssignment = function() {
  var reply = null;
  Object.keys(this._assignments).some(function(name) {
    var assignment = this.getAssignment(name);
    if (assignment.arg.type === 'BlankArgument' &&
            assignment.param.isPositionalAllowed) {
      reply = assignment;
      return true; // i.e. break
    }
    return false;
  }, this);
  return reply;
};

/**
 * The update process is asynchronous, so there is (unavoidably) a window
 * where we've worked out the command but don't yet understand all the params.
 * If we try to do things to a requisition in this window we may get
 * inconsistent results. Asynchronous promises have made the window bigger.
 * The only time we've seen this in practice is during focus events due to
 * clicking on a shortcut. The focus want to check the cursor position while
 * the shortcut is updating the command line.
 * This function allows us to detect and back out of this problem.
 * We should be able to remove this function when all the state in a
 * requisition can be encapsulated and updated atomically.
 */
Requisition.prototype.isUpToDate = function() {
  if (!this._args) {
    return false;
  }
  for (var i = 0; i < this._args.length; i++) {
    if (this._args[i].assignment == null) {
      return false;
    }
  }
  return true;
};

/**
 * Look through the arguments attached to our assignments for the assignment
 * at the given position.
 * @param {number} cursor The cursor position to query
 */
Requisition.prototype.getAssignmentAt = function(cursor) {
  // We short circuit this one because we may have no args, or no args with
  // any size and the alg below only finds arguments with size.
  if (cursor === 0) {
    return this.commandAssignment;
  }

  var assignForPos = [];
  var i, j;
  for (i = 0; i < this._args.length; i++) {
    var arg = this._args[i];
    var assignment = arg.assignment;

    // prefix and text are clearly part of the argument
    for (j = 0; j < arg.prefix.length; j++) {
      assignForPos.push(assignment);
    }
    for (j = 0; j < arg.text.length; j++) {
      assignForPos.push(assignment);
    }

    // suffix is part of the argument only if this is a named parameter,
    // otherwise it looks forwards
    if (arg.assignment.arg.type === 'NamedArgument') {
      // leave the argument as it is
    }
    else if (this._args.length > i + 1) {
      // first to the next argument
      assignment = this._args[i + 1].assignment;
    }
    else {
      // then to the first blank positional parameter, leaving 'as is' if none
      var nextAssignment = this._getFirstBlankPositionalAssignment();
      if (nextAssignment != null) {
        assignment = nextAssignment;
      }
    }

    for (j = 0; j < arg.suffix.length; j++) {
      assignForPos.push(assignment);
    }
  }

  // Possible shortcut, we don't really need to go through all the args
  // to work out the solution to this

  return assignForPos[cursor - 1];
};

/**
 * Extract a canonical version of the input
 * @return a promise of a string which is the canonical version of what was
 * typed
 */
Requisition.prototype.toCanonicalString = function() {
  var cmd = this.commandAssignment.value ?
      this.commandAssignment.value.name :
      this.commandAssignment.arg.text;

  // Canonically, if we've opened with a { then we should have a } to close
  var lineSuffix = '';
  if (cmd === '{') {
    var scriptSuffix = this.getAssignment(0).arg.suffix;
    lineSuffix = (!scriptSuffix.includes('}')) ? ' }' : '';
  }

  var ctx = this.executionContext;

  // First stringify all the arguments
  var argPromise = util.promiseEach(this.getAssignments(), assignment => {
    // Bug 664377: This will cause problems if there is a non-default value
    // after a default value. Also we need to decide when to use
    // named parameters in place of positional params. Both can wait.
    if (assignment.value === assignment.param.defaultValue) {
      return '';
    }

    var val = assignment.param.type.stringify(assignment.value, ctx);
    return Promise.resolve(val).then(str => {
      return ' ' + str;
    });
  });

  return argPromise.then(strings => {
    return cmd + strings.join('') + lineSuffix;
  });
};

/**
 * Reconstitute the input from the args
 */
Requisition.prototype.toString = function() {
  if (!this._args) {
    throw new Error('toString requires a command line. See source.');
  }

  return this._args.map(function(arg) {
    return arg.toString();
  }).join('');
};

/**
 * For test/debug use only. The output from this function is subject to wanton
 * random change without notice, and should not be relied upon to even exist
 * at some later date.
 */
Object.defineProperty(Requisition.prototype, '_summaryJson', {
  get: function() {
    var summary = {
      $args: this._args.map(function(arg) {
        return arg._summaryJson;
      }),
      _command: this.commandAssignment._summaryJson,
      _unassigned: this._unassigned.forEach(function(assignment) {
        return assignment._summaryJson;
      })
    };

    Object.keys(this._assignments).forEach(name => {
      summary[name] = this.getAssignment(name)._summaryJson;
    });

    return summary;
  },
  enumerable: true
});

/**
 * When any assignment changes, we might need to update the _args array to
 * match and inform people of changes to the typed input text.
 */
Requisition.prototype._setAssignmentInternal = function(assignment, conversion) {
  var oldConversion = assignment.conversion;

  assignment.conversion = conversion;
  assignment.conversion.assignment = assignment;

  // Do nothing if the conversion is unchanged
  if (assignment.conversion.equals(oldConversion)) {
    if (assignment === this.commandAssignment) {
      this._setBlankArguments();
    }
    return;
  }

  // When the command changes, we need to keep a bunch of stuff in sync
  if (assignment === this.commandAssignment) {
    this._assignments = {};

    var command = this.commandAssignment.value;
    if (command) {
      for (var i = 0; i < command.params.length; i++) {
        var param = command.params[i];
        var newAssignment = new Assignment(param);
        this._setBlankAssignment(newAssignment);
        this._assignments[param.name] = newAssignment;
      }
    }
    this.assignmentCount = Object.keys(this._assignments).length;
  }
};

/**
 * Internal function to alter the given assignment using the given arg.
 * @param assignment The assignment to alter
 * @param arg The new value for the assignment. An instance of Argument, or an
 * instance of Conversion, or null to set the blank value.
 * @param options There are a number of ways to customize how the assignment
 * is made, including:
 * - internal: (default:false) External updates are required to do more work,
 *   including adjusting the args in this requisition to stay in sync.
 *   On the other hand non internal changes use beginChange to back out of
 *   changes when overtaken asynchronously.
 *   Setting internal:true effectively means this is being called as part of
 *   the update process.
 * - matchPadding: (default:false) Alter the whitespace on the prefix and
 *   suffix of the new argument to match that of the old argument. This only
 *   makes sense with internal=false
 * @return A promise that resolves to undefined when the assignment is complete
 */
Requisition.prototype.setAssignment = function(assignment, arg, options) {
  options = options || {};
  if (!options.internal) {
    var originalArgs = assignment.arg.getArgs();

    // Update the args array
    var replacementArgs = arg.getArgs();
    var maxLen = Math.max(originalArgs.length, replacementArgs.length);
    for (var i = 0; i < maxLen; i++) {
      // If there are no more original args, or if the original arg was blank
      // (i.e. not typed by the user), we'll just need to add at the end
      if (i >= originalArgs.length || originalArgs[i].type === 'BlankArgument') {
        this._args.push(replacementArgs[i]);
        continue;
      }

      var index = this._args.indexOf(originalArgs[i]);
      if (index === -1) {
        console.error('Couldn\'t find ', originalArgs[i], ' in ', this._args);
        throw new Error('Couldn\'t find ' + originalArgs[i]);
      }

      // If there are no more replacement args, we just remove the original args
      // Otherwise swap original args and replacements
      if (i >= replacementArgs.length) {
        this._args.splice(index, 1);
      }
      else {
        if (options.matchPadding) {
          if (replacementArgs[i].prefix.length === 0 &&
              this._args[index].prefix.length !== 0) {
            replacementArgs[i].prefix = this._args[index].prefix;
          }
          if (replacementArgs[i].suffix.length === 0 &&
              this._args[index].suffix.length !== 0) {
            replacementArgs[i].suffix = this._args[index].suffix;
          }
        }
        this._args[index] = replacementArgs[i];
      }
    }
  }

  var updateId = options.internal ? null : this._beginChange();

  var setAssignmentInternal = conversion => {
    if (options.internal || this._isChangeCurrent(updateId)) {
      this._setAssignmentInternal(assignment, conversion);
    }

    if (!options.internal) {
      this._endChangeCheckOrder(updateId);
    }

    return Promise.resolve(undefined);
  };

  if (arg == null) {
    var blank = assignment.param.type.getBlank(this.executionContext);
    return setAssignmentInternal(blank);
  }

  if (typeof arg.getStatus === 'function') {
    // It's not really an arg, it's a conversion already
    return setAssignmentInternal(arg);
  }

  var parsed = assignment.param.type.parse(arg, this.executionContext);
  return parsed.then(setAssignmentInternal);
};

/**
 * Reset an assignment to its default value.
 * For internal use only.
 * Happens synchronously.
 */
Requisition.prototype._setBlankAssignment = function(assignment) {
  var blank = assignment.param.type.getBlank(this.executionContext);
  this._setAssignmentInternal(assignment, blank);
};

/**
 * Reset all the assignments to their default values.
 * For internal use only.
 * Happens synchronously.
 */
Requisition.prototype._setBlankArguments = function() {
  this.getAssignments().forEach(this._setBlankAssignment.bind(this));
};

/**
 * Input trace gives us an array of Argument tracing objects, one for each
 * character in the typed input, from which we can derive information about how
 * to display this typed input. It's a bit like toString on steroids.
 * <p>
 * The returned object has the following members:<ul>
 * <li>character: The character to which this arg trace refers.
 * <li>arg: The Argument to which this character is assigned.
 * <li>part: One of ['prefix'|'text'|suffix'] - how was this char understood
 * </ul>
 * <p>
 * The Argument objects are as output from tokenize() rather than as applied
 * to Assignments by _assign() (i.e. they are not instances of NamedArgument,
 * ArrayArgument, etc).
 * <p>
 * To get at the arguments applied to the assignments simply call
 * <tt>arg.assignment.arg</tt>. If <tt>arg.assignment.arg !== arg</tt> then
 * the arg applied to the assignment will contain the original arg.
 * See _assign() for details.
 */
Requisition.prototype.createInputArgTrace = function() {
  if (!this._args) {
    throw new Error('createInputMap requires a command line. See source.');
  }

  var args = [];
  var i;
  this._args.forEach(function(arg) {
    for (i = 0; i < arg.prefix.length; i++) {
      args.push({ arg: arg, character: arg.prefix[i], part: 'prefix' });
    }
    for (i = 0; i < arg.text.length; i++) {
      args.push({ arg: arg, character: arg.text[i], part: 'text' });
    }
    for (i = 0; i < arg.suffix.length; i++) {
      args.push({ arg: arg, character: arg.suffix[i], part: 'suffix' });
    }
  });

  return args;
};

/**
 * If the last character is whitespace then things that we suggest to add to
 * the end don't need a space prefix.
 * While this is quite a niche function, it has 2 benefits:
 * - it's more correct because we can distinguish between final whitespace that
 *   is part of an unclosed string, and parameter separating whitespace.
 * - also it's faster than toString() the whole thing and checking the end char
 * @return true iff the last character is interpreted as parameter separating
 * whitespace
 */
Requisition.prototype.typedEndsWithSeparator = function() {
  if (!this._args) {
    throw new Error('typedEndsWithSeparator requires a command line. See source.');
  }

  if (this._args.length === 0) {
    return false;
  }

  // This is not as easy as doing (this.toString().slice(-1) === ' ')
  // See the doc comments above; We're checking for separators, not spaces
  var lastArg = this._args.slice(-1)[0];
  if (lastArg.suffix.slice(-1) === ' ') {
    return true;
  }

  return lastArg.text === '' && lastArg.suffix === ''
      && lastArg.prefix.slice(-1) === ' ';
};

/**
 * Return an array of Status scores so we can create a marked up
 * version of the command line input.
 * @param cursor We only take a status of INCOMPLETE to be INCOMPLETE when the
 * cursor is actually in the argument. Otherwise it's an error.
 * @return Array of objects each containing <tt>status</tt> property and a
 * <tt>string</tt> property containing the characters to which the status
 * applies. Concatenating the strings in order gives the original input.
 */
Requisition.prototype.getInputStatusMarkup = function(cursor) {
  var argTraces = this.createInputArgTrace();
  // Generally the 'argument at the cursor' is the argument before the cursor
  // unless it is before the first char, in which case we take the first.
  cursor = cursor === 0 ? 0 : cursor - 1;
  var cTrace = argTraces[cursor];

  var markup = [];
  for (var i = 0; i < argTraces.length; i++) {
    var argTrace = argTraces[i];
    var arg = argTrace.arg;
    var status = Status.VALID;
    // When things get very async we can get here while something else is
    // doing an update, in which case arg.assignment == null, so we check first
    if (argTrace.part === 'text' && arg.assignment != null) {
      status = arg.assignment.getStatus(arg);
      // Promote INCOMPLETE to ERROR  ...
      if (status === Status.INCOMPLETE) {
        // If the cursor is in the prefix or suffix of an argument then we
        // don't consider it in the argument for the purposes of preventing
        // the escalation to ERROR. However if this is a NamedArgument, then we
        // allow the suffix (as space between 2 parts of the argument) to be in.
        // We use arg.assignment.arg not arg because we're looking at the arg
        // that got put into the assignment not as returned by tokenize()
        var isNamed = (cTrace.arg.assignment.arg.type === 'NamedArgument');
        var isInside = cTrace.part === 'text' ||
                        (isNamed && cTrace.part === 'suffix');
        if (arg.assignment !== cTrace.arg.assignment || !isInside) {
          // And if we're not in the command
          if (!(arg.assignment instanceof CommandAssignment)) {
            status = Status.ERROR;
          }
        }
      }
    }

    markup.push({ status: status, string: argTrace.character });
  }

  // De-dupe: merge entries where 2 adjacent have same status
  i = 0;
  while (i < markup.length - 1) {
    if (markup[i].status === markup[i + 1].status) {
      markup[i].string += markup[i + 1].string;
      markup.splice(i + 1, 1);
    }
    else {
      i++;
    }
  }

  return markup;
};

/**
 * Describe the state of the current input in a way that allows display of
 * predictions and completion hints
 * @param start The location of the cursor
 * @param rank The index of the chosen prediction
 * @return A promise of an object containing the following properties:
 * - statusMarkup: An array of Status scores so we can create a marked up
 *   version of the command line input. See getInputStatusMarkup() for details
 * - unclosedJs: Is the entered command a JS command with no closing '}'?
 * - directTabText: A promise of the text that we *add* to the command line
 *   when TAB is pressed, to be displayed directly after the cursor. See also
 *   arrowTabText.
 * - emptyParameters: A promise of the text that describes the arguments that
 *   the user is yet to type.
 * - arrowTabText: A promise of the text that *replaces* the current argument
 *   when TAB is pressed, generally displayed after a "|->" symbol. See also
 *   directTabText.
 */
Requisition.prototype.getStateData = function(start, rank) {
  var typed = this.toString();
  var current = this.getAssignmentAt(start);
  var context = this.executionContext;
  var predictionPromise = (typed.trim().length !== 0) ?
                          current.getPredictionRanked(context, rank) :
                          Promise.resolve(null);

  return predictionPromise.then(prediction => {
    // directTabText is for when the current input is a prefix of the completion
    // arrowTabText is for when we need to use an -> to show what will be used
    var directTabText = '';
    var arrowTabText = '';
    var emptyParameters = [];

    if (typed.trim().length !== 0) {
      var cArg = current.arg;

      if (prediction) {
        var tabText = prediction.name;
        var existing = cArg.text;

        // Normally the cursor being just before whitespace means that you are
        // 'in' the previous argument, which means that the prediction is based
        // on that argument, however NamedArguments break this by having 2 parts
        // so we need to prepend the tabText with a space for NamedArguments,
        // but only when there isn't already a space at the end of the prefix
        // (i.e. ' --name' not ' --name ')
        if (current.isInName()) {
          tabText = ' ' + tabText;
        }

        if (existing !== tabText) {
          // Decide to use directTabText or arrowTabText
          // Strip any leading whitespace from the user inputted value because
          // the tabText will never have leading whitespace.
          var inputValue = existing.replace(/^\s*/, '');
          var isStrictCompletion = tabText.indexOf(inputValue) === 0;
          if (isStrictCompletion && start === typed.length) {
            // Display the suffix of the prediction as the completion
            var numLeadingSpaces = existing.match(/^(\s*)/)[0].length;

            directTabText = tabText.slice(existing.length - numLeadingSpaces);
          }
          else {
            // Display the '-> prediction' at the end of the completer element
            // \u21E5 is the JS escape right arrow
            arrowTabText = '\u21E5 ' + tabText;
          }
        }
      }
      else {
        // There's no prediction, but if this is a named argument that needs a
        // value (that is without any) then we need to show that one is needed
        // For example 'git commit --message ', clearly needs some more text
        if (cArg.type === 'NamedArgument' && cArg.valueArg == null) {
          emptyParameters.push('<' + current.param.type.name + '>\u00a0');
        }
      }
    }

    // Add a space between the typed text (+ directTabText) and the hints,
    // making sure we don't add 2 sets of padding
    if (directTabText !== '') {
      directTabText += '\u00a0'; // a.k.a &nbsp;
    }
    else if (!this.typedEndsWithSeparator()) {
      emptyParameters.unshift('\u00a0');
    }

    // Calculate the list of parameters to be filled in
    // We generate an array of emptyParameter markers for each positional
    // parameter to the current command.
    // Generally each emptyParameter marker begins with a space to separate it
    // from whatever came before, unless what comes before ends in a space.

    this.getAssignments().forEach(assignment => {
      // Named arguments are handled with a group [options] marker
      if (!assignment.param.isPositionalAllowed) {
        return;
      }

      // No hints if we've got content for this parameter
      if (assignment.arg.toString().trim() !== '') {
        return;
      }

      // No hints if we have a prediction
      if (directTabText !== '' && current === assignment) {
        return;
      }

      var text = (assignment.param.isDataRequired) ?
          '<' + assignment.param.name + '>\u00a0' :
          '[' + assignment.param.name + ']\u00a0';

      emptyParameters.push(text);
    });

    var command = this.commandAssignment.value;
    var addOptionsMarker = false;

    // We add an '[options]' marker when there are named parameters that are
    // not filled in and not hidden, and we don't have any directTabText
    if (command && command.hasNamedParameters) {
      command.params.forEach(function(param) {
        var arg = this.getAssignment(param.name).arg;
        if (!param.isPositionalAllowed && !param.hidden
                && arg.type === 'BlankArgument') {
          addOptionsMarker = true;
        }
      }, this);
    }

    if (addOptionsMarker) {
      // Add an nbsp if we don't have one at the end of the input or if
      // this isn't the first param we've mentioned
      emptyParameters.push('[options]\u00a0');
    }

    // Is the entered command a JS command with no closing '}'?
    var unclosedJs = command && command.name === '{' &&
                     !this.getAssignment(0).arg.suffix.includes('}');

    return {
      statusMarkup: this.getInputStatusMarkup(start),
      unclosedJs: unclosedJs,
      directTabText: directTabText,
      arrowTabText: arrowTabText,
      emptyParameters: emptyParameters
    };
  });
};

/**
 * Pressing TAB sometimes requires that we add a space to denote that we're on
 * to the 'next thing'.
 * @param assignment The assignment to which to append the space
 */
Requisition.prototype._addSpace = function(assignment) {
  var arg = assignment.arg.beget({ suffixSpace: true });
  if (arg !== assignment.arg) {
    return this.setAssignment(assignment, arg);
  }
  else {
    return Promise.resolve(undefined);
  }
};

/**
 * Complete the argument at <tt>cursor</tt>.
 * Basically the same as:
 *   assignment = getAssignmentAt(cursor);
 *   assignment.value = assignment.conversion.predictions[0];
 * Except it's done safely, and with particular care to where we place the
 * space, which is complex, and annoying if we get it wrong.
 *
 * WARNING: complete() can happen asynchronously.
 *
 * @param cursor The cursor configuration. Should have start and end properties
 * which should be set to start and end of the selection.
 * @param rank The index of the prediction that we should choose.
 * This number is not bounded by the size of the prediction array, we take the
 * modulus to get it within bounds
 * @return A promise which completes (with undefined) when any outstanding
 * completion tasks are done.
 */
Requisition.prototype.complete = function(cursor, rank) {
  var assignment = this.getAssignmentAt(cursor.start);

  var context = this.executionContext;
  var predictionPromise = assignment.getPredictionRanked(context, rank);
  return predictionPromise.then(prediction => {
    var outstanding = [];

    // Note: Since complete is asynchronous we should perhaps have a system to
    // bail out of making changes if the command line has changed since TAB
    // was pressed. It's not yet clear if this will be a problem.

    if (prediction == null) {
      // No predictions generally means we shouldn't change anything on TAB,
      // but TAB has the connotation of 'next thing' and when we're at the end
      // of a thing that implies that we should add a space. i.e.
      // 'help<TAB>' -> 'help '
      // But we should only do this if the thing that we're 'completing' is
      // valid and doesn't already end in a space.
      if (assignment.arg.suffix.slice(-1) !== ' ' &&
              assignment.getStatus() === Status.VALID) {
        outstanding.push(this._addSpace(assignment));
      }

      // Also add a space if we are in the name part of an assignment, however
      // this time we don't want the 'push the space to the next assignment'
      // logic, so we don't use addSpace
      if (assignment.isInName()) {
        var newArg = assignment.arg.beget({ prefixPostSpace: true });
        outstanding.push(this.setAssignment(assignment, newArg));
      }
    }
    else {
      // Mutate this argument to hold the completion
      var arg = assignment.arg.beget({
        text: prediction.name,
        dontQuote: (assignment === this.commandAssignment)
      });
      var assignPromise = this.setAssignment(assignment, arg);

      if (!prediction.incomplete) {
        assignPromise = assignPromise.then(() => {
          // The prediction is complete, add a space to let the user move-on
          return this._addSpace(assignment).then(() => {
            // Bug 779443 - Remove or explain the re-parse
            if (assignment instanceof UnassignedAssignment) {
              return this.update(this.toString());
            }
          });
        });
      }

      outstanding.push(assignPromise);
    }

    return Promise.all(outstanding).then(() => {
      return true;
    });
  });
};

/**
 * Replace the current value with the lower value if such a concept exists.
 */
Requisition.prototype.nudge = function(assignment, by) {
  var ctx = this.executionContext;
  var val = assignment.param.type.nudge(assignment.value, by, ctx);
  return Promise.resolve(val).then(replacement => {
    if (replacement != null) {
      var val = assignment.param.type.stringify(replacement, ctx);
      return Promise.resolve(val).then(str => {
        var arg = assignment.arg.beget({ text: str });
        return this.setAssignment(assignment, arg);
      });
    }
  });
};

/**
 * Helper to find the 'data-command' attribute, used by |update()|
 */
function getDataCommandAttribute(element) {
  var command = element.getAttribute('data-command');
  if (!command) {
    command = element.querySelector('*[data-command]')
                     .getAttribute('data-command');
  }
  return command;
}

/**
 * Designed to be called from context.update(). Acts just like update() except
 * that it also calls onExternalUpdate() to inform the UI of an unexpected
 * change to the current command.
 */
Requisition.prototype._contextUpdate = function(typed) {
  return this.update(typed).then(reply => {
    this.onExternalUpdate({ typed: typed });
    return reply;
  });
};

/**
 * Called by the UI when ever the user interacts with a command line input
 * @param typed The contents of the input field OR an HTML element (or an event
 * that targets an HTML element) which has a data-command attribute or a child
 * with the same that contains the command to update with
 */
Requisition.prototype.update = function(typed) {
  // Should be "if (typed instanceof HTMLElement)" except Gecko
  if (typeof typed.querySelector === 'function') {
    typed = getDataCommandAttribute(typed);
  }
  // Should be "if (typed instanceof Event)" except Gecko
  if (typeof typed.currentTarget === 'object') {
    typed = getDataCommandAttribute(typed.currentTarget);
  }

  var updateId = this._beginChange();

  this._args = exports.tokenize(typed);
  var args = this._args.slice(0); // i.e. clone

  this._split(args);

  return this._assign(args).then(() => {
    return this._endChangeCheckOrder(updateId);
  });
};

/**
 * Similar to update('') except that it's guaranteed to execute synchronously
 */
Requisition.prototype.clear = function() {
  var arg = new Argument('', '', '');
  this._args = [ arg ];

  var conversion = commandModule.parse(this.executionContext, arg, false);
  this.setAssignment(this.commandAssignment, conversion, { internal: true });
};

/**
 * tokenize() is a state machine. These are the states.
 */
var In = {
  /**
   * The last character was ' '.
   * Typing a ' ' character will not change the mode
   * Typing one of '"{ will change mode to SINGLE_Q, DOUBLE_Q or SCRIPT.
   * Anything else goes into SIMPLE mode.
   */
  WHITESPACE: 1,

  /**
   * The last character was part of a parameter.
   * Typing ' ' returns to WHITESPACE mode. Any other character
   * (including '"{} which are otherwise special) does not change the mode.
   */
  SIMPLE: 2,

  /**
   * We're inside single quotes: '
   * Typing ' returns to WHITESPACE mode. Other characters do not change mode.
   */
  SINGLE_Q: 3,

  /**
   * We're inside double quotes: "
   * Typing " returns to WHITESPACE mode. Other characters do not change mode.
   */
  DOUBLE_Q: 4,

  /**
   * We're inside { and }
   * Typing } returns to WHITESPACE mode. Other characters do not change mode.
   * SCRIPT mode is slightly different from other modes in that spaces between
   * the {/} delimiters and the actual input are not considered significant.
   * e.g: " x " is a 3 character string, delimited by double quotes, however
   * { x } is a 1 character JavaScript surrounded by whitespace and {}
   * delimiters.
   * In the short term we assume that the JS routines can make sense of the
   * extra whitespace, however at some stage we may need to move the space into
   * the Argument prefix/suffix.
   * Also we don't attempt to handle nested {}. See bug 678961
   */
  SCRIPT: 5
};

/**
 * Split up the input taking into account ', " and {.
 * We don't consider \t or other classical whitespace characters to split
 * arguments apart. For one thing these characters are hard to type, but also
 * if the user has gone to the trouble of pasting a TAB character into the
 * input field (or whatever it takes), they probably mean it.
 */
exports.tokenize = function(typed) {
  // For blank input, place a dummy empty argument into the list
  if (typed == null || typed.length === 0) {
    return [ new Argument('', '', '') ];
  }

  if (isSimple(typed)) {
    return [ new Argument(typed, '', '') ];
  }

  var mode = In.WHITESPACE;

  // First we swap out escaped characters that are special to the tokenizer.
  // So a backslash followed by any of ['"{} ] is turned into a unicode private
  // char so we can swap back later
  typed = typed
      .replace(/\\\\/g, '\uF000')
      .replace(/\\ /g, '\uF001')
      .replace(/\\'/g, '\uF002')
      .replace(/\\"/g, '\uF003')
      .replace(/\\{/g, '\uF004')
      .replace(/\\}/g, '\uF005');

  function unescape2(escaped) {
    return escaped
        .replace(/\uF000/g, '\\\\')
        .replace(/\uF001/g, '\\ ')
        .replace(/\uF002/g, '\\\'')
        .replace(/\uF003/g, '\\\"')
        .replace(/\uF004/g, '\\{')
        .replace(/\uF005/g, '\\}');
  }

  var i = 0;          // The index of the current character
  var start = 0;      // Where did this section start?
  var prefix = '';    // Stuff that comes before the current argument
  var args = [];      // The array that we're creating
  var blockDepth = 0; // For JS with nested {}

  // This is just a state machine. We're going through the string char by char
  // The 'mode' is one of the 'In' states. As we go, we're adding Arguments
  // to the 'args' array.

  while (true) {
    var c = typed[i];
    var str;
    switch (mode) {
      case In.WHITESPACE:
        if (c === '\'') {
          prefix = typed.substring(start, i + 1);
          mode = In.SINGLE_Q;
          start = i + 1;
        }
        else if (c === '"') {
          prefix = typed.substring(start, i + 1);
          mode = In.DOUBLE_Q;
          start = i + 1;
        }
        else if (c === '{') {
          prefix = typed.substring(start, i + 1);
          mode = In.SCRIPT;
          blockDepth++;
          start = i + 1;
        }
        else if (/ /.test(c)) {
          // Still whitespace, do nothing
        }
        else {
          prefix = typed.substring(start, i);
          mode = In.SIMPLE;
          start = i;
        }
        break;

      case In.SIMPLE:
        // There is an edge case of xx'xx which we are assuming to
        // be a single parameter (and same with ")
        if (c === ' ') {
          str = unescape2(typed.substring(start, i));
          args.push(new Argument(str, prefix, ''));
          mode = In.WHITESPACE;
          start = i;
          prefix = '';
        }
        break;

      case In.SINGLE_Q:
        if (c === '\'') {
          str = unescape2(typed.substring(start, i));
          args.push(new Argument(str, prefix, c));
          mode = In.WHITESPACE;
          start = i + 1;
          prefix = '';
        }
        break;

      case In.DOUBLE_Q:
        if (c === '"') {
          str = unescape2(typed.substring(start, i));
          args.push(new Argument(str, prefix, c));
          mode = In.WHITESPACE;
          start = i + 1;
          prefix = '';
        }
        break;

      case In.SCRIPT:
        if (c === '{') {
          blockDepth++;
        }
        else if (c === '}') {
          blockDepth--;
          if (blockDepth === 0) {
            str = unescape2(typed.substring(start, i));
            args.push(new ScriptArgument(str, prefix, c));
            mode = In.WHITESPACE;
            start = i + 1;
            prefix = '';
          }
        }
        break;
    }

    i++;

    if (i >= typed.length) {
      // There is nothing else to read - tidy up
      if (mode === In.WHITESPACE) {
        if (i !== start) {
          // There's whitespace at the end of the typed string. Add it to the
          // last argument's suffix, creating an empty argument if needed.
          var extra = typed.substring(start, i);
          var lastArg = args[args.length - 1];
          if (!lastArg) {
            args.push(new Argument('', extra, ''));
          }
          else {
            lastArg.suffix += extra;
          }
        }
      }
      else if (mode === In.SCRIPT) {
        str = unescape2(typed.substring(start, i + 1));
        args.push(new ScriptArgument(str, prefix, ''));
      }
      else {
        str = unescape2(typed.substring(start, i + 1));
        args.push(new Argument(str, prefix, ''));
      }
      break;
    }
  }

  return args;
};

/**
 * If the input has no spaces, quotes, braces or escapes,
 * we can take the fast track.
 */
function isSimple(typed) {
   for (var i = 0; i < typed.length; i++) {
     var c = typed.charAt(i);
     if (c === ' ' || c === '"' || c === '\'' ||
         c === '{' || c === '}' || c === '\\') {
       return false;
     }
   }
   return true;
}

/**
 * Looks in the commands for a command extension that matches what has been
 * typed at the command line.
 */
Requisition.prototype._split = function(args) {
  // Handle the special case of the user typing { javascript(); }
  // We use the hidden 'eval' command directly rather than shift()ing one of
  // the parameters, and parse()ing it.
  var conversion;
  if (args[0].type === 'ScriptArgument') {
    // Special case: if the user enters { console.log('foo'); } then we need to
    // use the hidden 'eval' command
    var command = this.system.commands.get(evalCmd.name);
    conversion = new Conversion(command, new ScriptArgument());
    this._setAssignmentInternal(this.commandAssignment, conversion);
    return;
  }

  var argsUsed = 1;

  while (argsUsed <= args.length) {
    var arg = (argsUsed === 1) ?
              args[0] :
              new MergedArgument(args, 0, argsUsed);

    if (this.prefix != null && this.prefix !== '') {
      var prefixArg = new Argument(this.prefix, '', ' ');
      var prefixedArg = new MergedArgument([ prefixArg, arg ]);

      conversion = commandModule.parse(this.executionContext, prefixedArg, false);
      if (conversion.value == null) {
        conversion = commandModule.parse(this.executionContext, arg, false);
      }
    }
    else {
      conversion = commandModule.parse(this.executionContext, arg, false);
    }

    // We only want to carry on if this command is a parent command,
    // which means that there is a commandAssignment, but not one with
    // an exec function.
    if (!conversion.value || conversion.value.exec) {
      break;
    }

    // Previously we needed a way to hide commands depending context.
    // We have not resurrected that feature yet, but if we do we should
    // insert code here to ignore certain commands depending on the
    // context/environment

    argsUsed++;
  }

  // This could probably be re-written to consume args as we go
  for (var i = 0; i < argsUsed; i++) {
    args.shift();
  }

  this._setAssignmentInternal(this.commandAssignment, conversion);
};

/**
 * Add all the passed args to the list of unassigned assignments.
 */
Requisition.prototype._addUnassignedArgs = function(args) {
  args.forEach(arg => {
    this._unassigned.push(new UnassignedAssignment(this, arg));
  });

  return RESOLVED;
};

/**
 * Work out which arguments are applicable to which parameters.
 */
Requisition.prototype._assign = function(args) {
  // See comment in _split. Avoid multiple updates
  var noArgUp = { internal: true };

  this._unassigned = [];

  if (!this.commandAssignment.value) {
    return this._addUnassignedArgs(args);
  }

  if (args.length === 0) {
    this._setBlankArguments();
    return RESOLVED;
  }

  // Create an error if the command does not take parameters, but we have
  // been given them ...
  if (this.assignmentCount === 0) {
    return this._addUnassignedArgs(args);
  }

  // Special case: if there is only 1 parameter, and that's of type
  // text, then we put all the params into the first param
  if (this.assignmentCount === 1) {
    var assignment = this.getAssignment(0);
    if (assignment.param.type.name === 'string') {
      var arg = (args.length === 1) ? args[0] : new MergedArgument(args);
      return this.setAssignment(assignment, arg, noArgUp);
    }
  }

  // Positional arguments can still be specified by name, but if they are
  // then we need to ignore them when working them out positionally
  var unassignedParams = this.getParameterNames();

  // We collect the arguments used in arrays here before assigning
  var arrayArgs = {};

  // Extract all the named parameters
  var assignments = this.getAssignments(false);
  var namedDone = util.promiseEach(assignments, function(assignment) {
    // Loop over the arguments
    // Using while rather than loop because we remove args as we go
    var i = 0;
    while (i < args.length) {
      if (!assignment.param.isKnownAs(args[i].text)) {
        // Skip this parameter and handle as a positional parameter
        i++;
        continue;
      }

      var arg = args.splice(i, 1)[0];
      /* jshint loopfunc:true */
      unassignedParams = unassignedParams.filter(function(test) {
        return test !== assignment.param.name;
      });

      // boolean parameters don't have values, default to false
      if (assignment.param.type.name === 'boolean') {
        arg = new TrueNamedArgument(arg);
      }
      else {
        var valueArg = null;
        if (i + 1 <= args.length) {
          valueArg = args.splice(i, 1)[0];
        }
        arg = new NamedArgument(arg, valueArg);
      }

      if (assignment.param.type.name === 'array') {
        var arrayArg = arrayArgs[assignment.param.name];
        if (!arrayArg) {
          arrayArg = new ArrayArgument();
          arrayArgs[assignment.param.name] = arrayArg;
        }
        arrayArg.addArgument(arg);
        return RESOLVED;
      }
      else {
        if (assignment.arg.type === 'BlankArgument') {
          return this.setAssignment(assignment, arg, noArgUp);
        }
        else {
          return this._addUnassignedArgs(arg.getArgs());
        }
      }
    }
  }, this);

  // What's left are positional parameters: assign in order
  var positionalDone = namedDone.then(() => {
    return util.promiseEach(unassignedParams, function(name) {
      var assignment = this.getAssignment(name);

      // If not set positionally, and we can't set it non-positionally,
      // we have to default it to prevent previous values surviving
      if (!assignment.param.isPositionalAllowed) {
        this._setBlankAssignment(assignment);
        return RESOLVED;
      }

      // If this is a positional array argument, then it swallows the
      // rest of the arguments.
      if (assignment.param.type.name === 'array') {
        var arrayArg = arrayArgs[assignment.param.name];
        if (!arrayArg) {
          arrayArg = new ArrayArgument();
          arrayArgs[assignment.param.name] = arrayArg;
        }
        arrayArg.addArguments(args);
        args = [];
        // The actual assignment to the array parameter is done below
        return RESOLVED;
      }

      // Set assignment to defaults if there are no more arguments
      if (args.length === 0) {
        this._setBlankAssignment(assignment);
        return RESOLVED;
      }

      var arg = args.splice(0, 1)[0];
      // --foo and -f are named parameters, -4 is a number. So '-' is either
      // the start of a named parameter or a number depending on the context
      var isIncompleteName = assignment.param.type.name === 'number' ?
          /-[-a-zA-Z_]/.test(arg.text) :
          arg.text.charAt(0) === '-';

      if (isIncompleteName) {
        this._unassigned.push(new UnassignedAssignment(this, arg));
        return RESOLVED;
      }
      else {
        return this.setAssignment(assignment, arg, noArgUp);
      }
    }, this);
  });

  // Now we need to assign the array argument (if any)
  var arrayDone = positionalDone.then(() => {
    return util.promiseEach(Object.keys(arrayArgs), function(name) {
      var assignment = this.getAssignment(name);
      return this.setAssignment(assignment, arrayArgs[name], noArgUp);
    }, this);
  });

  // What's left is can't be assigned, but we need to officially unassign them
  return arrayDone.then(() => {
    return this._addUnassignedArgs(args);
  });
};

/**
 * Entry point for keyboard accelerators or anything else that wants to execute
 * a command.
 * @param options Object describing how the execution should be handled.
 * (optional). Contains some of the following properties:
 * - hidden (boolean, default=false) Should the output be hidden from the
 *   commandOutputManager for this requisition
 * - command/args A fast shortcut to executing a known command with a known
 *   set of parsed arguments.
 */
Requisition.prototype.exec = function(options) {
  var command = null;
  var args = null;
  var hidden = false;

  if (options) {
    if (options.hidden) {
      hidden = true;
    }

    if (options.command != null) {
      // Fast track by looking up the command directly since passed args
      // means there is no command line to parse.
      command = this.system.commands.get(options.command);
      if (!command) {
        console.error('Command not found: ' + options.command);
      }
      args = options.args;
    }
  }

  if (!command) {
    command = this.commandAssignment.value;
    args = this.getArgsObject();
  }

  // Display JavaScript input without the initial { or closing }
  var typed = this.toString();
  if (evalCmd.isCommandRegexp.test(typed)) {
    typed = typed.replace(evalCmd.isCommandRegexp, '');
    // Bug 717763: What if the JavaScript naturally ends with a }?
    typed = typed.replace(/\s*}\s*$/, '');
  }

  var output = new Output({
    command: command,
    args: args,
    typed: typed,
    canonical: this.toCanonicalString(),
    hidden: hidden
  });

  this.commandOutputManager.onOutput({ output: output });

  var onDone = function(data) {
    output.complete(data, false);
    return output;
  };

  var onError = function(data, ex) {
    if (logErrors) {
      if (ex != null) {
        util.errorHandler(ex);
      }
      else {
        console.error(data);
      }
    }

    if (data != null && typeof data === 'string') {
      data = data.replace(/^Protocol error: /, ''); // Temp fix for bug 1035296
    }

    data = (data != null && data.isTypedData) ? data : {
      isTypedData: true,
      data: data,
      type: 'error'
    };
    output.complete(data, true);
    return output;
  };

  if (this.status !== Status.VALID) {
    var ex = new Error(this.getStatusMessage());
    // We only reject a call to exec if GCLI breaks. Errors with commands are
    // exposed in the 'error' status of the Output object
    return Promise.resolve(onError(ex)).then(output => {
      this.clear();
      return output;
    });
  }
  else {
    try {
      return host.exec(() => {
        return command.exec(args, this.executionContext);
      }).then(onDone, onError);
    }
    catch (ex) {
      var data = (typeof ex.message === 'string' && ex.stack != null) ?
                 ex.message : ex;
      return Promise.resolve(onError(data, ex));
    }
    finally {
      this.clear();
    }
  }
};

/**
 * Designed to be called from context.updateExec(). Acts just like updateExec()
 * except that it also calls onExternalUpdate() to inform the UI of an
 * unexpected change to the current command.
 */
Requisition.prototype._contextUpdateExec = function(typed, options) {
  var reqOpts = {
    document: this.document,
    environment: this.environment
  };
  var child = new Requisition(this.system, reqOpts);
  return child.updateExec(typed, options).then(function(reply) {
    child.destroy();
    return reply;
  }.bind(child));
};

/**
 * A shortcut for calling update, resolving the promise and then exec.
 * @param input The string to execute
 * @param options Passed to exec
 * @return A promise of an output object
 */
Requisition.prototype.updateExec = function(input, options) {
  return this.update(input).then(() => {
    return this.exec(options);
  });
};

exports.Requisition = Requisition;

/**
 * A simple object to hold information about the output of a command
 */
function Output(options) {
  options = options || {};
  this.command = options.command || '';
  this.args = options.args || {};
  this.typed = options.typed || '';
  this.canonical = options.canonical || '';
  this.hidden = options.hidden === true ? true : false;

  this.type = undefined;
  this.data = undefined;
  this.completed = false;
  this.error = false;
  this.start = new Date();

  this.promise = new Promise((resolve, reject) => {
    this._resolve = resolve;
  });
}

/**
 * Called when there is data to display, and the command has finished executing
 * See changed() for details on parameters.
 */
Output.prototype.complete = function(data, error) {
  this.end = new Date();
  this.completed = true;
  this.error = error;

  if (data != null && data.isTypedData) {
    this.data = data.data;
    this.type = data.type;
  }
  else {
    this.data = data;
    this.type = this.command.returnType;
    if (this.type == null) {
      this.type = (this.data == null) ? 'undefined' : typeof this.data;
    }
  }

  if (this.type === 'object') {
    throw new Error('No type from output of ' + this.typed);
  }

  this._resolve();
};

/**
 * Call converters.convert using the data in this Output object
 */
Output.prototype.convert = function(type, conversionContext) {
  var converters = conversionContext.system.converters;
  return converters.convert(this.data, this.type, type, conversionContext);
};

Output.prototype.toJson = function() {
  // Exceptions don't stringify, so we try a bit harder
  var data = this.data;
  if (this.error && JSON.stringify(this.data) === '{}') {
    data = {
      columnNumber: data.columnNumber,
      fileName: data.fileName,
      lineNumber: data.lineNumber,
      message: data.message,
      stack: data.stack
    };
  }

  return {
    typed: this.typed,
    type: this.type,
    data: data,
    isError: this.error
  };
};

exports.Output = Output;
PK
!<���{��Nchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/commands/clear.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var l10n = require('../util/l10n');

exports.items = [
  {
    // A command to clear the output area
    item: 'command',
    runAt: 'client',
    name: 'clear',
    description: l10n.lookup('clearDesc'),
    returnType: 'clearoutput',
    exec: function(args, context) { }
  },
  {
    item: 'converter',
    from: 'clearoutput',
    to: 'view',
    exec: function(ignore, conversionContext) {
      return {
        html: '<span onload="${onload}"></span>',
        data: {
          onload: function(ev) {
            // element starts off being the span above, and we walk up the
            // tree looking for the terminal
            var element = ev.target;
            while (element != null && element.terminal == null) {
              element = element.parentElement;
            }

            if (element == null) {
              // This is only an event handler on a completed command
              // So we're relying on this showing up in the console
              throw new Error('Failed to find clear');
            }

            element.terminal.clear();
          }
        }
      };
    }
  }
];
PK
!<d���,D,DQchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/commands/commands.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var util = require('../util/util');
var l10n = require('../util/l10n');

/**
 * Implement the localization algorithm for any documentation objects (i.e.
 * description and manual) in a command.
 * @param data The data assigned to a description or manual property
 * @param onUndefined If data == null, should we return the data untouched or
 * lookup a 'we don't know' key in it's place.
 */
function lookup(data, onUndefined) {
  if (data == null) {
    if (onUndefined) {
      return l10n.lookup(onUndefined);
    }

    return data;
  }

  if (typeof data === 'string') {
    return data;
  }

  if (typeof data === 'object') {
    if (data.key) {
      return l10n.lookup(data.key);
    }

    var locales = l10n.getPreferredLocales();
    var translated;
    locales.some(function(locale) {
      translated = data[locale];
      return translated != null;
    });
    if (translated != null) {
      return translated;
    }

    console.error('Can\'t find locale in descriptions: ' +
            'locales=' + JSON.stringify(locales) + ', ' +
            'description=' + JSON.stringify(data));
    return '(No description)';
  }

  return l10n.lookup(onUndefined);
}


/**
 * The command object is mostly just setup around a commandSpec (as passed to
 * Commands.add()).
 */
function Command(types, commandSpec) {
  Object.keys(commandSpec).forEach(function(key) {
    this[key] = commandSpec[key];
  }, this);

  if (!this.name) {
    throw new Error('All registered commands must have a name');
  }

  if (this.params == null) {
    this.params = [];
  }
  if (!Array.isArray(this.params)) {
    throw new Error('command.params must be an array in ' + this.name);
  }

  this.hasNamedParameters = false;
  this.description = 'description' in this ? this.description : undefined;
  this.description = lookup(this.description, 'canonDescNone');
  this.manual = 'manual' in this ? this.manual : undefined;
  this.manual = lookup(this.manual);

  // At this point this.params has nested param groups. We want to flatten it
  // out and replace the param object literals with Parameter objects
  var paramSpecs = this.params;
  this.params = [];
  this.paramGroups = {};
  this._shortParams = {};

  var addParam = param => {
    var groupName = param.groupName || l10n.lookup('canonDefaultGroupName');
    this.params.push(param);
    if (!this.paramGroups.hasOwnProperty(groupName)) {
      this.paramGroups[groupName] = [];
    }
    this.paramGroups[groupName].push(param);
  };

  // Track if the user is trying to mix default params and param groups.
  // All the non-grouped parameters must come before all the param groups
  // because non-grouped parameters can be assigned positionally, so their
  // index is important. We don't want 'holes' in the order caused by
  // parameter groups.
  var usingGroups = false;

  // In theory this could easily be made recursive, so param groups could
  // contain nested param groups. Current thinking is that the added
  // complexity for the UI probably isn't worth it, so this implementation
  // prevents nesting.
  paramSpecs.forEach(function(spec) {
    if (!spec.group) {
      var param = new Parameter(types, spec, this, null);
      addParam(param);

      if (!param.isPositionalAllowed) {
        this.hasNamedParameters = true;
      }

      if (usingGroups && param.groupName == null) {
        throw new Error('Parameters can\'t come after param groups.' +
                        ' Ignoring ' + this.name + '/' + spec.name);
      }

      if (param.groupName != null) {
        usingGroups = true;
      }
    }
    else {
      spec.params.forEach(function(ispec) {
        var param = new Parameter(types, ispec, this, spec.group);
        addParam(param);

        if (!param.isPositionalAllowed) {
          this.hasNamedParameters = true;
        }
      }, this);

      usingGroups = true;
    }
  }, this);

  this.params.forEach(function(param) {
    if (param.short != null) {
      if (this._shortParams[param.short] != null) {
        throw new Error('Multiple params using short name ' + param.short);
      }
      this._shortParams[param.short] = param;
    }
  }, this);
}

/**
 * JSON serializer that avoids non-serializable data
 * @param customProps Array of strings containing additional properties which,
 * if specified in the command spec, will be included in the JSON. Normally we
 * transfer only the properties required for GCLI to function.
 */
Command.prototype.toJson = function(customProps) {
  var json = {
    item: 'command',
    name: this.name,
    params: this.params.map(function(param) { return param.toJson(); }),
    returnType: this.returnType,
    isParent: (this.exec == null)
  };

  if (this.description !==  l10n.lookup('canonDescNone')) {
    json.description = this.description;
  }
  if (this.manual != null) {
    json.manual = this.manual;
  }
  if (this.hidden != null) {
    json.hidden = this.hidden;
  }

  if (Array.isArray(customProps)) {
    customProps.forEach(prop => {
      if (this[prop] != null) {
        json[prop] = this[prop];
      }
    });
  }

  return json;
};

/**
 * Easy way to lookup parameters by full name
 */
Command.prototype.getParameterByName = function(name) {
  var reply;
  this.params.forEach(function(param) {
    if (param.name === name) {
      reply = param;
    }
  });
  return reply;
};

/**
 * Easy way to lookup parameters by short name
 */
Command.prototype.getParameterByShortName = function(short) {
  return this._shortParams[short];
};

exports.Command = Command;


/**
 * A wrapper for a paramSpec so we can sort out shortened versions names for
 * option switches
 */
function Parameter(types, paramSpec, command, groupName) {
  this.command = command || { name: 'unnamed' };
  this.paramSpec = paramSpec;
  this.name = this.paramSpec.name;
  this.type = this.paramSpec.type;
  this.short = this.paramSpec.short;

  if (this.short != null && !/[0-9A-Za-z]/.test(this.short)) {
    throw new Error('\'short\' value must be a single alphanumeric digit.');
  }

  this.groupName = groupName;
  if (this.groupName != null) {
    if (this.paramSpec.option != null) {
      throw new Error('Can\'t have a "option" property in a nested parameter');
    }
  }
  else {
    if (this.paramSpec.option != null) {
      this.groupName = (this.paramSpec.option === true) ?
                       l10n.lookup('canonDefaultGroupName') :
                       '' + this.paramSpec.option;
    }
  }

  if (!this.name) {
    throw new Error('In ' + this.command.name +
                    ': all params must have a name');
  }

  var typeSpec = this.type;
  this.type = types.createType(typeSpec);
  if (this.type == null) {
    console.error('Known types: ' + types.getTypeNames().join(', '));
    throw new Error('In ' + this.command.name + '/' + this.name +
                    ': can\'t find type for: ' + JSON.stringify(typeSpec));
  }

  // boolean parameters have an implicit defaultValue:false, which should
  // not be changed. See the docs.
  if (this.type.name === 'boolean' &&
      this.paramSpec.defaultValue !== undefined) {
    throw new Error('In ' + this.command.name + '/' + this.name +
                    ': boolean parameters can not have a defaultValue.' +
                    ' Ignoring');
  }

  // All parameters that can only be set via a named parameter must have a
  // non-undefined default value
  if (!this.isPositionalAllowed && this.paramSpec.defaultValue === undefined &&
      this.type.getBlank == null && this.type.name !== 'boolean') {
    throw new Error('In ' + this.command.name + '/' + this.name +
                    ': Missing defaultValue for optional parameter.');
  }

  if (this.paramSpec.defaultValue !== undefined) {
    this.defaultValue = this.paramSpec.defaultValue;
  }
  else {
    Object.defineProperty(this, 'defaultValue', {
      get: function() {
        return this.type.getBlank().value;
      },
      enumerable: true
    });
  }

  // Resolve the documentation
  this.manual = lookup(this.paramSpec.manual);
  this.description = lookup(this.paramSpec.description, 'canonDescNone');

  // Is the user required to enter data for this parameter? (i.e. has
  // defaultValue been set to something other than undefined)
  // TODO: When the defaultValue comes from type.getBlank().value (see above)
  // then perhaps we should set using something like
  //   isDataRequired = (type.getBlank().status !== VALID)
  this.isDataRequired = (this.defaultValue === undefined);

  // Are we allowed to assign data to this parameter using positional
  // parameters?
  this.isPositionalAllowed = this.groupName == null;
}

/**
 * Does the given name uniquely identify this param (among the other params
 * in this command)
 * @param name The name to check
 */
Parameter.prototype.isKnownAs = function(name) {
  return (name === '--' + this.name) || (name === '-' + this.short);
};

/**
 * Reflect the paramSpec 'hidden' property (dynamically so it can change)
 */
Object.defineProperty(Parameter.prototype, 'hidden', {
  get: function() {
    return this.paramSpec.hidden;
  },
  enumerable: true
});

/**
 * JSON serializer that avoids non-serializable data
 */
Parameter.prototype.toJson = function() {
  var json = {
    name: this.name,
    type: this.type.getSpec(this.command.name, this.name),
    short: this.short
  };

  // Values do not need to be serializable, so we don't try. For the client
  // side (which doesn't do any executing) we only care whether default value is
  // undefined, null, or something else.
  if (this.paramSpec.defaultValue !== undefined) {
    json.defaultValue = (this.paramSpec.defaultValue === null) ? null : {};
  }
  if (this.paramSpec.description != null) {
    json.description = this.paramSpec.description;
  }
  if (this.paramSpec.manual != null) {
    json.manual = this.paramSpec.manual;
  }
  if (this.paramSpec.hidden != null) {
    json.hidden = this.paramSpec.hidden;
  }

  // groupName can be set outside a paramSpec, (e.g. in grouped parameters)
  // but it works like 'option' does so we use 'option' for groupNames
  if (this.groupName != null || this.paramSpec.option != null) {
    json.option = this.groupName || this.paramSpec.option;
  }

  return json;
};

exports.Parameter = Parameter;


/**
 * A store for a list of commands
 * @param types Each command uses a set of Types to parse its parameters so the
 * Commands container needs access to the list of available types.
 * @param location String that, if set will force all commands to have a
 * matching runAt property to be accepted
 */
function Commands(types, location) {
  this.types = types;
  this.location = location;

  // A lookup hash of our registered commands
  this._commands = {};
  // A sorted list of command names, we regularly want them in order, so pre-sort
  this._commandNames = [];
  // A lookup of the original commandSpecs by command name
  this._commandSpecs = {};

  // Enable people to be notified of changes to the list of commands
  this.onCommandsChange = util.createEvent('commands.onCommandsChange');
}

/**
 * Add a command to the list of known commands.
 * @param commandSpec The command and its metadata.
 * @return The new command, or null if a location property has been set and the
 * commandSpec doesn't have a matching runAt property.
 */
Commands.prototype.add = function(commandSpec) {
  if (this.location != null && commandSpec.runAt != null &&
      commandSpec.runAt !== this.location) {
    return;
  }

  if (this._commands[commandSpec.name] != null) {
    // Roughly commands.remove() without the event call, which we do later
    delete this._commands[commandSpec.name];
    this._commandNames = this._commandNames.filter(function(test) {
      return test !== commandSpec.name;
    });
  }

  var command = new Command(this.types, commandSpec);
  this._commands[commandSpec.name] = command;
  this._commandNames.push(commandSpec.name);
  this._commandNames.sort();

  this._commandSpecs[commandSpec.name] = commandSpec;

  this.onCommandsChange();
  return command;
};

/**
 * Remove an individual command. The opposite of Commands.add().
 * Removing a non-existent command is a no-op.
 * @param commandOrName Either a command name or the command itself.
 * @return true if a command was removed, false otherwise.
 */
Commands.prototype.remove = function(commandOrName) {
  var name = typeof commandOrName === 'string' ?
          commandOrName :
          commandOrName.name;

  if (!this._commands[name]) {
    return false;
  }

  // See start of commands.add if changing this code
  delete this._commands[name];
  delete this._commandSpecs[name];
  this._commandNames = this._commandNames.filter(function(test) {
    return test !== name;
  });

  this.onCommandsChange();
  return true;
};

/**
 * Retrieve a command by name
 * @param name The name of the command to retrieve
 */
Commands.prototype.get = function(name) {
  // '|| undefined' is to silence 'reference to undefined property' warnings
  return this._commands[name] || undefined;
};

/**
 * Get an array of all the registered commands.
 */
Commands.prototype.getAll = function() {
  return Object.keys(this._commands).map(function(name) {
    return this._commands[name];
  }, this);
};

/**
 * Get access to the stored commandMetaDatas (i.e. before they were made into
 * instances of Command/Parameters) so we can remote them.
 * @param customProps Array of strings containing additional properties which,
 * if specified in the command spec, will be included in the JSON. Normally we
 * transfer only the properties required for GCLI to function.
 */
Commands.prototype.getCommandSpecs = function(customProps) {
  var commandSpecs = [];

  Object.keys(this._commands).forEach(name => {
    var command = this._commands[name];
    if (!command.noRemote) {
      commandSpecs.push(command.toJson(customProps));
    }
  });

  return commandSpecs;
};

/**
 * Add a set of commands that are executed somewhere else, optionally with a
 * command prefix to distinguish these commands from a local set of commands.
 * @param commandSpecs Presumably as obtained from getCommandSpecs
 * @param remoter Function to call on exec of a new remote command. This is
 * defined just like an exec function (i.e. that takes args/context as params
 * and returns a promise) with one extra feature, that the context includes a
 * 'commandName' property that contains the original command name.
 * @param prefix The name prefix that we assign to all command names
 * @param to URL-like string that describes where the commands are executed.
 * This is to complete the parent command description.
 */
Commands.prototype.addProxyCommands = function(commandSpecs, remoter, prefix, to) {
  if (prefix != null) {
    if (this._commands[prefix] != null) {
      throw new Error(l10n.lookupFormat('canonProxyExists', [ prefix ]));
    }

    // We need to add the parent command so all the commands from the other
    // system have a parent
    this.add({
      name: prefix,
      isProxy: true,
      description: l10n.lookupFormat('canonProxyDesc', [ to ]),
      manual: l10n.lookupFormat('canonProxyManual', [ to ])
    });
  }

  commandSpecs.forEach(commandSpec => {
    var originalName = commandSpec.name;
    if (!commandSpec.isParent) {
      commandSpec.exec = (args, context) => {
        context.commandName = originalName;
        return remoter(args, context);
      };
    }

    if (prefix != null) {
      commandSpec.name = prefix + ' ' + commandSpec.name;
    }
    commandSpec.isProxy = true;
    this.add(commandSpec);
  });
};

/**
 * Remove a set of commands added with addProxyCommands.
 * @param prefix The name prefix that we assign to all command names
 */
Commands.prototype.removeProxyCommands = function(prefix) {
  var toRemove = [];
  Object.keys(this._commandSpecs).forEach(name => {
    if (name.indexOf(prefix) === 0) {
      toRemove.push(name);
    }
  });

  var removed = [];
  toRemove.forEach(name => {
    var command = this.get(name);
    if (command.isProxy) {
      this.remove(name);
      removed.push(name);
    }
    else {
      console.error('Skipping removal of \'' + name +
                    '\' because it is not a proxy command.');
    }
  });

  return removed;
};

exports.Commands = Commands;

/**
 * CommandOutputManager stores the output objects generated by executed
 * commands.
 *
 * CommandOutputManager is exposed to the the outside world and could (but
 * shouldn't) be used before gcli.startup() has been called.
 * This could should be defensive to that where possible, and we should
 * certainly document if the use of it or similar will fail if used too soon.
 */
function CommandOutputManager() {
  this.onOutput = util.createEvent('CommandOutputManager.onOutput');
}

exports.CommandOutputManager = CommandOutputManager;
PK
!<z�0�11Pchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/commands/context.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var l10n = require('../util/l10n');
var cli = require('../cli');

/**
 * 'context' command
 */
var context = {
  item: 'command',
  name: 'context',
  description: l10n.lookup('contextDesc'),
  manual: l10n.lookup('contextManual'),
  params: [
   {
     name: 'prefix',
     type: 'command',
     description: l10n.lookup('contextPrefixDesc'),
     defaultValue: null
   }
  ],
  returnType: 'string',
  // The context command is client only because it's essentially sugar for
  // typing commands. When there is a command prefix in action, it is the job
  // of the remoter to add the prefix to the typed strings that are sent for
  // remote execution
  noRemote: true,
  exec: function echo(args, context) {
    var requisition = cli.getMapping(context).requisition;

    if (args.prefix == null) {
      requisition.prefix = null;
      return l10n.lookup('contextEmptyReply');
    }

    if (args.prefix.exec != null) {
      throw new Error(l10n.lookupFormat('contextNotParentError',
                                        [ args.prefix.name ]));
    }

    requisition.prefix = args.prefix.name;
    return l10n.lookupFormat('contextReply', [ args.prefix.name ]);
  }
};

exports.items = [ context ];
PK
!<�b�V0V0Mchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/commands/help.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var l10n = require('../util/l10n');
var cli = require('../cli');

/**
 * Add an 'paramGroups' accessor to a command metadata object to sort the
 * params into groups according to the option of the param.
 */
function addParamGroups(command) {
  Object.defineProperty(command, 'paramGroups', {
    get: function() {
      var paramGroups = {};
      this.params.forEach(function(param) {
        var groupName = param.option || l10n.lookup('canonDefaultGroupName');
        if (paramGroups[groupName] == null) {
          paramGroups[groupName] = [];
        }
        paramGroups[groupName].push(param);
      });
      return paramGroups;
    },
    enumerable: true
  });
}

/**
 * Get a data block for the help_man.html/help_man.txt templates
 */
function getHelpManData(commandData, context) {
  // Filter out hidden parameters
  commandData.command.params = commandData.command.params.filter(
    param => !param.hidden
  );

  addParamGroups(commandData.command);
  commandData.subcommands.forEach(addParamGroups);

  return {
    l10n: l10n.propertyLookup,
    onclick: context.update,
    ondblclick: context.updateExec,
    describe: function(item) {
      return item.manual || item.description;
    },
    getTypeDescription: function(param) {
      var input = '';
      if (param.defaultValue === undefined) {
        input = l10n.lookup('helpManRequired');
      }
      else if (param.defaultValue === null) {
        input = l10n.lookup('helpManOptional');
      }
      else {
        // We need defaultText to work the text version of defaultValue
        input = l10n.lookup('helpManOptional');
        /*
        var val = param.type.stringify(param.defaultValue);
        input = Promise.resolve(val).then(function(defaultValue) {
          return l10n.lookupFormat('helpManDefault', [ defaultValue ]);
        }.bind(this));
        */
      }

      return Promise.resolve(input).then(defaultDescr => {
        return '(' + (param.type.name || param.type) + ', ' + defaultDescr + ')';
      });
    },
    getSynopsis: function(param) {
      var name = param.name + (param.short ? '|-' + param.short : '');
      if (param.option == null) {
        return param.defaultValue !== undefined ?
            '[' + name + ']' :
            '<' + name + '>';
      }
      else {
        return param.type === 'boolean' || param.type.name === 'boolean' ?
            '[--' + name + ']' :
            '[--' + name + ' ...]';
      }
    },
    command: commandData.command,
    subcommands: commandData.subcommands
  };
}

/**
 * Get a data block for the help_list.html/help_list.txt templates
 */
function getHelpListData(commandsData, context) {
  commandsData.commands.forEach(addParamGroups);

  var heading;
  if (commandsData.commands.length === 0) {
    heading = l10n.lookupFormat('helpListNone', [ commandsData.prefix ]);
  }
  else if (commandsData.prefix == null) {
    heading = l10n.lookup('helpListAll');
  }
  else {
    heading = l10n.lookupFormat('helpListPrefix', [ commandsData.prefix ]);
  }

  return {
    l10n: l10n.propertyLookup,
    includeIntro: commandsData.prefix == null,
    heading: heading,
    onclick: context.update,
    ondblclick: context.updateExec,
    matchingCommands: commandsData.commands
  };
}

/**
 * Create a block of data suitable to be passed to the help_list.html template
 */
function getMatchingCommands(context, prefix) {
  var commands = cli.getMapping(context).requisition.system.commands;
  var reply = commands.getAll().filter(function(command) {
    if (command.hidden) {
      return false;
    }

    if (prefix && command.name.indexOf(prefix) !== 0) {
      // Filtered out because they don't match the search
      return false;
    }
    if (!prefix && command.name.includes(' ')) {
      // We don't show sub commands with plain 'help'
      return false;
    }
    return true;
  });

  reply.sort(function(c1, c2) {
    return c1.name.localeCompare(c2.name);
  });

  reply = reply.map(function(command) {
    return command.toJson();
  });

  return reply;
}

/**
 * Find all the sub commands of the given command
 */
function getSubCommands(context, command) {
  var commands = cli.getMapping(context).requisition.system.commands;
  var subcommands = commands.getAll().filter(function(subcommand) {
    return subcommand.name.indexOf(command.name) === 0 &&
           subcommand.name !== command.name &&
           !subcommand.hidden;
  });

  subcommands.sort(function(c1, c2) {
    return c1.name.localeCompare(c2.name);
  });

  subcommands = subcommands.map(function(subcommand) {
    return subcommand.toJson();
  });

  return subcommands;
}

var helpCss = '' +
  '.gcli-help-name {\n' +
  '  text-align: end;\n' +
  '}\n' +
  '\n' +
  '.gcli-help-arrow {\n' +
  '  color: #AAA;\n' +
  '}\n' +
  '\n' +
  '.gcli-help-description {\n' +
  '  margin: 0 20px;\n' +
  '  padding: 0;\n' +
  '}\n' +
  '\n' +
  '.gcli-help-parameter {\n' +
  '  margin: 0 30px;\n' +
  '  padding: 0;\n' +
  '}\n' +
  '\n' +
  '.gcli-help-header {\n' +
  '  margin: 10px 0 6px;\n' +
  '}\n';

exports.items = [
  {
    // 'help' command
    item: 'command',
    name: 'help',
    runAt: 'client',
    description: l10n.lookup('helpDesc'),
    manual: l10n.lookup('helpManual'),
    params: [
      {
        name: 'search',
        type: 'string',
        description: l10n.lookup('helpSearchDesc'),
        manual: l10n.lookup('helpSearchManual3'),
        defaultValue: null
      }
    ],

    exec: function(args, context) {
      var commands = cli.getMapping(context).requisition.system.commands;
      var command = commands.get(args.search);
      if (command) {
        return context.typedData('commandData', {
          command: command.toJson(),
          subcommands: getSubCommands(context, command)
        });
      }

      return context.typedData('commandsData', {
        prefix: args.search,
        commands: getMatchingCommands(context, args.search)
      });
    }
  },
  {
    // Convert a command into an HTML man page
    item: 'converter',
    from: 'commandData',
    to: 'view',
    exec: function(commandData, context) {
      return {
        html:
          '<div>\n' +
          '  <p class="gcli-help-header">\n' +
          '    ${l10n.helpManSynopsis}:\n' +
          '    <span class="gcli-out-shortcut" data-command="${command.name}"\n' +
          '        onclick="${onclick}" ondblclick="${ondblclick}">\n' +
          '      ${command.name}\n' +
          '      <span foreach="param in ${command.params}">${getSynopsis(param)} </span>\n' +
          '    </span>\n' +
          '  </p>\n' +
          '\n' +
          '  <p class="gcli-help-description">${describe(command)}</p>\n' +
          '\n' +
          '  <div if="${!command.isParent}">\n' +
          '    <div foreach="groupName in ${command.paramGroups}">\n' +
          '      <p class="gcli-help-header">${groupName}:</p>\n' +
          '      <ul class="gcli-help-parameter">\n' +
          '        <li if="${command.params.length === 0}">${l10n.helpManNone}</li>\n' +
          '        <li foreach="param in ${command.paramGroups[groupName]}">\n' +
          '          <code>${getSynopsis(param)}</code> <em>${getTypeDescription(param)}</em>\n' +
          '          <br/>\n' +
          '          ${describe(param)}\n' +
          '        </li>\n' +
          '      </ul>\n' +
          '    </div>\n' +
          '  </div>\n' +
          '\n' +
          '  <div if="${command.isParent}">\n' +
          '    <p class="gcli-help-header">${l10n.subCommands}:</p>\n' +
          '    <ul class="gcli-help-${subcommands}">\n' +
          '      <li if="${subcommands.length === 0}">${l10n.subcommandsNone}</li>\n' +
          '      <li foreach="subcommand in ${subcommands}">\n' +
          '        ${subcommand.name}: ${subcommand.description}\n' +
          '        <span class="gcli-out-shortcut" data-command="help ${subcommand.name}"\n' +
          '            onclick="${onclick}" ondblclick="${ondblclick}">\n' +
          '          help ${subcommand.name}\n' +
          '        </span>\n' +
          '      </li>\n' +
          '    </ul>\n' +
          '  </div>\n' +
          '\n' +
          '</div>\n',
        options: { allowEval: true, stack: 'commandData->view' },
        data: getHelpManData(commandData, context),
        css: helpCss,
        cssId: 'gcli-help'
      };
    }
  },
  {
    // Convert a command into a string based man page
    item: 'converter',
    from: 'commandData',
    to: 'stringView',
    exec: function(commandData, context) {
      return {
        html:
          '<div>## ${command.name}\n' +
          '\n' +
          '# ${l10n.helpManSynopsis}: ${command.name} <loop foreach="param in ${command.params}">${getSynopsis(param)} </loop>\n' +
          '\n' +
          '# ${l10n.helpManDescription}:\n' +
          '\n' +
          '${command.manual || command.description}\n' +
          '\n' +
          '<loop foreach="groupName in ${command.paramGroups}">\n' +
          '<span if="${!command.isParent}"># ${groupName}:\n' +
          '\n' +
          '<span if="${command.params.length === 0}">${l10n.helpManNone}</span><loop foreach="param in ${command.paramGroups[groupName]}">* ${param.name}: ${getTypeDescription(param)}\n' +
          '  ${param.manual || param.description}\n' +
          '</loop>\n' +
          '</span>\n' +
          '</loop>\n' +
          '\n' +
          '<span if="${command.isParent}"># ${l10n.subCommands}:</span>\n' +
          '\n' +
          '<span if="${subcommands.length === 0}">${l10n.subcommandsNone}</span>\n' +
          '<loop foreach="subcommand in ${subcommands}">* ${subcommand.name}: ${subcommand.description}\n' +
          '</loop>\n' +
          '</div>\n',
        options: { allowEval: true, stack: 'commandData->stringView' },
        data: getHelpManData(commandData, context)
      };
    }
  },
  {
    // Convert a list of commands into a formatted list
    item: 'converter',
    from: 'commandsData',
    to: 'view',
    exec: function(commandsData, context) {
      return {
        html:
          '<div>\n' +
          '  <div if="${includeIntro}">\n' +
          '    <p>${l10n.helpIntro}</p>\n' +
          '  </div>\n' +
          '\n' +
          '  <p>${heading}</p>\n' +
          '\n' +
          '  <table>\n' +
          '    <tr foreach="command in ${matchingCommands}">\n' +
          '      <td class="gcli-help-name">${command.name}</td>\n' +
          '      <td class="gcli-help-arrow">-</td>\n' +
          '      <td>\n' +
          '        ${command.description}\n' +
          '        <span class="gcli-out-shortcut"\n' +
          '            onclick="${onclick}" ondblclick="${ondblclick}"\n' +
          '            data-command="help ${command.name}">help ${command.name}</span>\n' +
          '      </td>\n' +
          '    </tr>\n' +
          '  </table>\n' +
          '</div>\n',
        options: { allowEval: true, stack: 'commandsData->view' },
        data: getHelpListData(commandsData, context),
        css: helpCss,
        cssId: 'gcli-help'
      };
    }
  },
  {
    // Convert a list of commands into a formatted list
    item: 'converter',
    from: 'commandsData',
    to: 'stringView',
    exec: function(commandsData, context) {
      return {
        html:
          '<pre><span if="${includeIntro}">## ${l10n.helpIntro}</span>\n' +
          '\n' +
          '# ${heading}\n' +
          '\n' +
          '<loop foreach="command in ${matchingCommands}">${command.name} &#x2192; ${command.description}\n' +
          '</loop></pre>',
        options: { allowEval: true, stack: 'commandsData->stringView' },
        data: getHelpListData(commandsData, context)
      };
    }
  }
];
PK
!<�a�!QQNchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/commands/mocks.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var cli = require('../cli');
var mockCommands = require('../test/mockCommands');
var mockFileCommands = require('../test/mockFileCommands');
var mockSettings = require('../test/mockSettings');

var isNode = (typeof(process) !== 'undefined' &&
             process.title.includes('node'));

exports.items = [
  {
    item: 'command',
    name: 'mocks',
    description: 'Add/remove mock commands',
    params: [
      {
        name: 'included',
        type: {
          name: 'selection',
          data: [ 'on', 'off' ]
        },
        description: 'Turn mock commands on or off',
      }
    ],
    returnType: 'string',

    exec: function(args, context) {
      var requisition = cli.getMapping(context).requisition;
      this[args.included](requisition);
      return 'Mock commands are now ' + args.included;
    },

    on: function(requisition) {
      mockCommands.setup(requisition);
      mockSettings.setup(requisition.system);

      if (isNode) {
        mockFileCommands.setup(requisition);
      }
    },

    off: function(requisition) {
      mockCommands.shutdown(requisition);
      mockSettings.shutdown(requisition.system);

      if (isNode) {
        mockFileCommands.shutdown(requisition);
      }
    }
  }
];
PK
!<#�P^�	�	Mchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/commands/pref.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var l10n = require('../util/l10n');

exports.items = [
  {
    // 'pref' command
    item: 'command',
    name: 'pref',
    description: l10n.lookup('prefDesc'),
    manual: l10n.lookup('prefManual')
  },
  {
    // 'pref show' command
    item: 'command',
    name: 'pref show',
    runAt: 'client',
    description: l10n.lookup('prefShowDesc'),
    manual: l10n.lookup('prefShowManual'),
    params: [
      {
        name: 'setting',
        type: 'setting',
        description: l10n.lookup('prefShowSettingDesc'),
        manual: l10n.lookup('prefShowSettingManual')
      }
    ],
    exec: function(args, context) {
      return l10n.lookupFormat('prefShowSettingValue',
                               [ args.setting.name, args.setting.value ]);
    }
  },
  {
    // 'pref set' command
    item: 'command',
    name: 'pref set',
    runAt: 'client',
    description: l10n.lookup('prefSetDesc'),
    manual: l10n.lookup('prefSetManual'),
    params: [
      {
        name: 'setting',
        type: 'setting',
        description: l10n.lookup('prefSetSettingDesc'),
        manual: l10n.lookup('prefSetSettingManual')
      },
      {
        name: 'value',
        type: 'settingValue',
        description: l10n.lookup('prefSetValueDesc'),
        manual: l10n.lookup('prefSetValueManual')
      }
    ],
    exec: function(args, context) {
      args.setting.value = args.value;
    }
  },
  {
    // 'pref reset' command
    item: 'command',
    name: 'pref reset',
    runAt: 'client',
    description: l10n.lookup('prefResetDesc'),
    manual: l10n.lookup('prefResetManual'),
    params: [
      {
        name: 'setting',
        type: 'setting',
        description: l10n.lookup('prefResetSettingDesc'),
        manual: l10n.lookup('prefResetSettingManual')
      }
    ],
    exec: function(args, context) {
      args.setting.setDefault();
    }
  }
];
PK
!<~�lp��Qchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/commands/preflist.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var l10n = require('../util/l10n');

/**
 * Format a list of settings for display
 */
var prefsViewConverter = {
  item: 'converter',
  from: 'prefsData',
  to: 'view',
  exec: function(prefsData, conversionContext) {
    var prefList = new PrefList(prefsData, conversionContext);
    return {
      html:
        '<div ignore="${onLoad(__element)}">\n' +
        '  <!-- This is broken, and unimportant. Comment out for now\n' +
        '  <div class="gcli-pref-list-filter">\n' +
        '    ${l10n.prefOutputFilter}:\n' +
        '    <input onKeyUp="${onFilterChange}" value="${search}"/>\n' +
        '  </div>\n' +
        '  -->\n' +
        '  <table class="gcli-pref-list-table">\n' +
        '    <colgroup>\n' +
        '      <col class="gcli-pref-list-name"/>\n' +
        '      <col class="gcli-pref-list-value"/>\n' +
        '    </colgroup>\n' +
        '    <tr>\n' +
        '      <th>${l10n.prefOutputName}</th>\n' +
        '      <th>${l10n.prefOutputValue}</th>\n' +
        '    </tr>\n' +
        '  </table>\n' +
        '  <div class="gcli-pref-list-scroller">\n' +
        '    <table class="gcli-pref-list-table" save="${table}">\n' +
        '    </table>\n' +
        '  </div>\n' +
        '</div>\n',
      data: prefList,
      options: {
        blankNullUndefined: true,
        allowEval: true,
        stack: 'prefsData->view'
      },
      css:
        '.gcli-pref-list-scroller {\n' +
        '  max-height: 200px;\n' +
        '  overflow-y: auto;\n' +
        '  overflow-x: hidden;\n' +
        '  display: inline-block;\n' +
        '}\n' +
        '\n' +
        '.gcli-pref-list-table {\n' +
        '  width: 500px;\n' +
        '  table-layout: fixed;\n' +
        '}\n' +
        '\n' +
        '.gcli-pref-list-table tr > th {\n' +
        '  text-align: left;\n' +
        '}\n' +
        '\n' +
        '.gcli-pref-list-table tr > td {\n' +
        '  text-overflow: elipsis;\n' +
        '  word-wrap: break-word;\n' +
        '}\n' +
        '\n' +
        '.gcli-pref-list-name {\n' +
        '  width: 70%;\n' +
        '}\n' +
        '\n' +
        '.gcli-pref-list-command {\n' +
        '  display: none;\n' +
        '}\n' +
        '\n' +
        '.gcli-pref-list-row:hover .gcli-pref-list-command {\n' +
        '  /* \'pref list\' is a bit broken and unimportant. Band-aid follows */\n' +
        '  /* display: inline-block; */\n' +
        '}\n',
      cssId: 'gcli-pref-list'
    };
  }
};

/**
 * Format a list of settings for display
 */
var prefsStringConverter = {
  item: 'converter',
  from: 'prefsData',
  to: 'string',
  exec: function(prefsData, conversionContext) {
    var reply = '';
    prefsData.settings.forEach(function(setting) {
      reply += setting.name + ' -> ' + setting.value + '\n';
    });
    return reply;
  }
};

/**
 * 'pref list' command
 */
var prefList = {
  item: 'command',
  name: 'pref list',
  description: l10n.lookup('prefListDesc'),
  manual: l10n.lookup('prefListManual'),
  params: [
    {
      name: 'search',
      type: 'string',
      defaultValue: null,
      description: l10n.lookup('prefListSearchDesc'),
      manual: l10n.lookup('prefListSearchManual')
    }
  ],
  returnType: 'prefsData',
  exec: function(args, context) {
    return new Promise(function(resolve, reject) {
      // This can be slow, get out of the way of the main thread
      setTimeout(() => {
        var prefsData = {
          settings: context.system.settings.getAll(args.search),
          search: args.search
        };
        resolve(prefsData);
      }, 10);
    });
  }
};

/**
 * A manager for our version of about:config
 */
function PrefList(prefsData, conversionContext) {
  this.search = prefsData.search;
  this.settings = prefsData.settings;
  this.conversionContext = conversionContext;

  this.onLoad = this.onLoad.bind(this);
}

/**
 * A load event handler registered by the template engine so we can load the
 * inner document
 */
PrefList.prototype.onLoad = function(element) {
  var table = element.querySelector('.gcli-pref-list-table');
  this.updateTable(table);
  return '';
};

/**
 * Forward localization lookups
 */
PrefList.prototype.l10n = l10n.propertyLookup;

/**
 * Called from the template onkeyup for the filter element
 */
PrefList.prototype.updateTable = function(table) {
  var view = this.conversionContext.createView({
    html:
      '<table>\n' +
      '  <colgroup>\n' +
      '    <col class="gcli-pref-list-name"/>\n' +
      '    <col class="gcli-pref-list-value"/>\n' +
      '  </colgroup>\n' +
      '  <tr class="gcli-pref-list-row" foreach="setting in ${settings}">\n' +
      '    <td>${setting.name}</td>\n' +
      '    <td onclick="${onSetClick}" data-command="pref set ${setting.name} ">\n' +
      '      ${setting.value}\n' +
      '      [Edit]\n' +
      '    </td>\n' +
      '  </tr>\n' +
      '</table>\n',
    options: { blankNullUndefined: true, stack: 'prefsData#inner' },
    data: this
  });

  view.appendTo(table, true);
};

PrefList.prototype.onFilterChange = function(ev) {
  if (ev.target.value !== this.search) {
    this.search = ev.target.value;

    var root = ev.target.parentNode.parentNode;
    var table = root.querySelector('.gcli-pref-list-table');
    this.updateTable(table);
  }
};

PrefList.prototype.onSetClick = function(ev) {
  var typed = ev.currentTarget.getAttribute('data-command');
  this.conversionContext.update(typed);
};

exports.items = [ prefsViewConverter, prefsStringConverter, prefList ];
PK
!<�8:/EEMchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/commands/test.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var examiner = require('../testharness/examiner');
var stati = require('../testharness/status').stati;
var helpers = require('../test/helpers');
var suite = require('../test/suite');
var cli = require('../cli');
var Requisition = require('../cli').Requisition;
var createRequisitionAutomator = require('../test/automators/requisition').createRequisitionAutomator;

var isNode = (typeof(process) !== 'undefined' &&
             process.title.includes('node'));

suite.init(isNode);

exports.optionsContainer = [];

exports.items = [
  {
    item: 'type',
    name: 'suite',
    parent: 'selection',
    cacheable: true,
    lookup: function() {
      return Object.keys(examiner.suites).map(function(name) {
        return { name: name, value: examiner.suites[name] };
      });
    }
  },
  {
    item: 'command',
    name: 'test',
    description: 'Run GCLI unit tests',
    params: [
      {
        name: 'suite',
        type: 'suite',
        description: 'Test suite to run.',
        defaultValue: examiner
      },
      {
        name: 'usehost',
        type: 'boolean',
        description: 'Run the unit tests in the host window',
        option: true
      }
    ],
    returnType: 'examiner-output',
    noRemote: true,
    exec: function(args, context) {
      if (args.usehost && exports.optionsContainer.length === 0) {
        throw new Error('Can\'t use --usehost without injected options');
      }

      var options;
      if (args.usehost) {
        options = exports.optionsContainer[0];
      }
      else {
        var env = {
          document: document,
          window: window
        };
        options = {
          isNode: isNode,
          isFirefox: false,
          isPhantomjs: false,
          requisition: new Requisition(context.system, { environment: env })
        };
        options.automator = createRequisitionAutomator(options.requisition);
      }

      var requisition = options.requisition;
      requisition.system.commands.get('mocks').on(requisition);
      helpers.resetResponseTimes();
      examiner.reset();

      return args.suite.run(options).then(function() {
        requisition.system.commands.get('mocks').off(requisition);
        var output = context.typedData('examiner-output', examiner.toRemote());

        if (output.data.summary.status === stati.pass) {
          return output;
        }
        else {
          cli.logErrors = false;
          throw output;
        }
      });
    }
  },
  {
    item: 'converter',
    from: 'examiner-output',
    to: 'string',
    exec: function(output, conversionContext) {
      return '\n' + examiner.detailedResultLog('NodeJS/NoDom') +
             '\n' + helpers.timingSummary;
    }
  },
  {
    item: 'converter',
    from: 'examiner-output',
    to: 'view',
    exec: function(output, conversionContext) {
      return {
        html:
          '<div>\n' +
          '  <table class="gcliTestResults">\n' +
          '    <thead>\n' +
          '      <tr>\n' +
          '        <th class="gcliTestSuite">Suite</th>\n' +
          '        <th>Test</th>\n' +
          '        <th>Results</th>\n' +
          '        <th>Checks</th>\n' +
          '        <th>Notes</th>\n' +
          '      </tr>\n' +
          '    </thead>\n' +
          '    <tbody foreach="suite in ${suites}">\n' +
          '      <tr foreach="test in ${suite.tests}" title="${suite.name}.${test.name}()">\n' +
          '        <td class="gcliTestSuite">${suite.name}</td>\n' +
          '        <td class="gcliTestTitle">${test.title}</td>\n' +
          '        <td class="gcliTest${test.status.name}">${test.status.name}</td>\n' +
          '        <td class="gcliTestChecks">${test.checks}</td>\n' +
          '        <td class="gcliTestMessages">\n' +
          '          <div foreach="failure in ${test.failures}">\n' +
          '            ${failure.message}\n' +
          '            <ul if="${failure.params}">\n' +
          '              <li>P1: ${failure.p1}</li>\n' +
          '              <li>P2: ${failure.p2}</li>\n' +
          '            </ul>\n' +
          '          </div>\n' +
          '        </td>\n' +
          '      </tr>\n' +
          '    </tbody>\n' +
          '    <tfoot>\n' +
          '      <tr>\n' +
          '        <th></th>\n' +
          '        <th>Total</th>\n' +
          '        <th>${summary.status.name}</th>\n' +
          '        <th class="gcliTestChecks">${summary.checks}</th>\n' +
          '        <th></th>\n' +
          '      </tr>\n' +
          '    </tfoot>\n' +
          '  </table>\n' +
          '</div>',
        css:
          '.gcliTestSkipped {\n' +
          '  background-color: #EEE;\n' +
          '  color: #000;\n' +
          '}\n' +
          '\n' +
          '.gcliTestExecuting {\n' +
          '  background-color: #888;\n' +
          '  color: #FFF;\n' +
          '}\n' +
          '\n' +
          '.gcliTestWaiting {\n' +
          '  background-color: #FFA;\n' +
          '  color: #000;\n' +
          '}\n' +
          '\n' +
          '.gcliTestPass {\n' +
          '  background-color: #8F8;\n' +
          '  color: #000;\n' +
          '}\n' +
          '\n' +
          '.gcliTestFail {\n' +
          '  background-color: #F00;\n' +
          '  color: #FFF;\n' +
          '}\n' +
          '\n' +
          'td.gcliTestSuite {\n' +
          '  font-family: monospace;\n' +
          '  font-size: 90%;\n' +
          '  text-align: right;\n' +
          '}\n' +
          '\n' +
          '.gcliTestResults th.gcliTestSuite,\n' +
          '.gcliTestResults .gcliTestChecks {\n' +
          '  text-align: right;\n' +
          '}\n' +
          '\n' +
          '.gcliTestResults th {\n' +
          '  text-align: left;\n' +
          '}\n' +
          '\n' +
          '.gcliTestMessages ul {\n' +
          '  margin: 0 0 10px;\n' +
          '  padding-left: 20px;\n' +
          '  list-style-type: square;\n' +
          '}\n',
        cssId: 'gcli-test',
        data: output,
        options: { allowEval: true, stack: 'test.html' }
      };
    }
  }
];
PK
!<9���Uchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/connectors/connectors.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

/**
 * This is how to implement a connector
 *  var baseConnector = {
 *    item: 'connector',
 *    name: 'foo',
 *
 *    connect: function(url) {
 *      return Promise.resolve(new FooConnection(url));
 *    }
 *  };
 */

/**
 * A prototype base for Connectors
 */
function Connection() {
}

/**
 * Add an event listener
 */
Connection.prototype.on = function(event, action) {
  if (!this._listeners) {
    this._listeners = {};
  }
  if (!this._listeners[event]) {
    this._listeners[event] = [];
  }
  this._listeners[event].push(action);
};

/**
 * Remove an event listener
 */
Connection.prototype.off = function(event, action) {
  if (!this._listeners) {
    return;
  }
  var actions = this._listeners[event];
  if (actions) {
    this._listeners[event] = actions.filter(li => {
      return li !== action;
    });
  }
};

/**
 * Emit an event. For internal use only
 */
Connection.prototype._emit = function(event, data) {
  if (this._listeners == null || this._listeners[event] == null) {
    return;
  }

  var listeners = this._listeners[event];
  listeners.forEach(listener => {
    // Fail fast if we mutate the list of listeners while emitting
    if (listeners !== this._listeners[event]) {
      throw new Error('Listener list changed while emitting');
    }

    try {
      listener.call(null, data);
    }
    catch (ex) {
      console.log('Error calling listeners to ' + event);
      console.error(ex);
    }
  });
};

/**
 * Send a message to the other side of the connection
 */
Connection.prototype.call = function(feature, data) {
  throw new Error('Not implemented');
};

/**
 * Disconnecting a Connection destroys the resources it holds. There is no
 * common route back to being connected once this has been called
 */
Connection.prototype.disconnect = function() {
  return Promise.resolve();
};

exports.Connection = Connection;

/**
 * A manager for the registered Connectors
 */
function Connectors() {
  // This is where we cache the connectors that we know about
  this._registered = {};
}

/**
 * Add a new connector to the cache
 */
Connectors.prototype.add = function(connector) {
  this._registered[connector.name] = connector;
};

/**
 * Remove an existing connector from the cache
 */
Connectors.prototype.remove = function(connector) {
  var name = typeof connector === 'string' ? connector : connector.name;
  delete this._registered[name];
};

/**
 * Get access to the list of known connectors
 */
Connectors.prototype.getAll = function() {
  return Object.keys(this._registered).map(name => {
    return this._registered[name];
  });
};

var defaultConnectorName;

/**
 * Get access to a connector by name. If name is undefined then first try to
 * use the same connector that we used last time, and if there was no last
 * time, then just use the first registered connector as a default.
 */
Connectors.prototype.get = function(name) {
  if (name == null) {
    name = (defaultConnectorName == null) ?
        Object.keys(this._registered)[0] :
        defaultConnectorName;
  }

  defaultConnectorName = name;
  return this._registered[name];
};

exports.Connectors = Connectors;
PK
!<~ߦג�Pchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/converters/basic.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var util = require('../util/util');

/**
 * Several converters are just data.toString inside a 'p' element
 */
function nodeFromDataToString(data, conversionContext) {
  var node = util.createElement(conversionContext.document, 'p');
  node.textContent = data.toString();
  return node;
}

exports.items = [
  {
    item: 'converter',
    from: 'string',
    to: 'dom',
    exec: nodeFromDataToString
  },
  {
    item: 'converter',
    from: 'number',
    to: 'dom',
    exec: nodeFromDataToString
  },
  {
    item: 'converter',
    from: 'boolean',
    to: 'dom',
    exec: nodeFromDataToString
  },
  {
    item: 'converter',
    from: 'undefined',
    to: 'dom',
    exec: function(data, conversionContext) {
      return util.createElement(conversionContext.document, 'span');
    }
  },
  {
    item: 'converter',
    from: 'json',
    to: 'view',
    exec: function(json, context) {
      var html = JSON.stringify(json, null, '&#160;').replace(/\n/g, '<br/>');
      return {
        html: '<pre>' + html + '</pre>'
      };
    }
  },
  {
    item: 'converter',
    from: 'number',
    to: 'string',
    exec: function(data) { return '' + data; }
  },
  {
    item: 'converter',
    from: 'boolean',
    to: 'string',
    exec: function(data) { return '' + data; }
  },
  {
    item: 'converter',
    from: 'undefined',
    to: 'string',
    exec: function(data) { return ''; }
  },
  {
    item: 'converter',
    from: 'json',
    to: 'string',
    exec: function(json, conversionContext) {
      return JSON.stringify(json, null, '  ');
    }
  }
];
PK
!<t\HCCUchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/converters/converters.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var util = require('../util/util');
var host = require('../util/host');

// It's probably easiest to read this bottom to top

/**
 * Best guess at creating a DOM element from random data
 */
var fallbackDomConverter = {
  from: '*',
  to: 'dom',
  exec: function(data, conversionContext) {
    return conversionContext.document.createTextNode(data || '');
  }
};

/**
 * Best guess at creating a string from random data
 */
var fallbackStringConverter = {
  from: '*',
  to: 'string',
  exec: function(data, conversionContext) {
    return data == null ? '' : data.toString();
  }
};

/**
 * Convert a view object to a DOM element
 */
var viewDomConverter = {
  item: 'converter',
  from: 'view',
  to: 'dom',
  exec: function(view, conversionContext) {
    if (!view.isView) {
      view = conversionContext.createView(view);
    }
    return view.toDom(conversionContext.document);
  }
};

/**
 * Convert a view object to a string
 */
var viewStringConverter = {
  item: 'converter',
  from: 'view',
  to: 'string',
  exec: function(view, conversionContext) {
    if (!view.isView) {
      view = conversionContext.createView(view);
    }
    return view.toDom(conversionContext.document).textContent;
  }
};

/**
 * Convert a view object to a string
 */
var stringViewStringConverter = {
  item: 'converter',
  from: 'stringView',
  to: 'string',
  exec: function(view, conversionContext) {
    if (!view.isView) {
      view = conversionContext.createView(view);
    }
    return view.toDom(conversionContext.document).textContent;
  }
};

/**
 * Convert an exception to a DOM element
 */
var errorDomConverter = {
  item: 'converter',
  from: 'error',
  to: 'dom',
  exec: function(ex, conversionContext) {
    var node = util.createElement(conversionContext.document, 'p');
    node.className = 'gcli-error';
    node.textContent = errorStringConverter.exec(ex, conversionContext);
    return node;
  }
};

/**
 * Convert an exception to a string
 */
var errorStringConverter = {
  item: 'converter',
  from: 'error',
  to: 'string',
  exec: function(ex, conversionContext) {
    if (typeof ex === 'string') {
      return ex;
    }
    if (ex instanceof Error) {
      return '' + ex;
    }
    if (typeof ex.message === 'string') {
      return ex.message;
    }
    return '' + ex;
  }
};

/**
 * Create a new converter by using 2 converters, one after the other
 */
function getChainConverter(first, second) {
  if (first.to !== second.from) {
    throw new Error('Chain convert impossible: ' + first.to + '!=' + second.from);
  }
  return {
    from: first.from,
    to: second.to,
    exec: function(data, conversionContext) {
      var intermediate = first.exec(data, conversionContext);
      return second.exec(intermediate, conversionContext);
    }
  };
}

/**
 * A manager for the registered Converters
 */
function Converters() {
  // This is where we cache the converters that we know about
  this._registered = {
    from: {}
  };
}

/**
 * Add a new converter to the cache
 */
Converters.prototype.add = function(converter) {
  var fromMatch = this._registered.from[converter.from];
  if (fromMatch == null) {
    fromMatch = {};
    this._registered.from[converter.from] = fromMatch;
  }

  fromMatch[converter.to] = converter;
};

/**
 * Remove an existing converter from the cache
 */
Converters.prototype.remove = function(converter) {
  var fromMatch = this._registered.from[converter.from];
  if (fromMatch == null) {
    return;
  }

  if (fromMatch[converter.to] === converter) {
    fromMatch[converter.to] = null;
  }
};

/**
 * Work out the best converter that we've got, for a given conversion.
 */
Converters.prototype.get = function(from, to) {
  var fromMatch = this._registered.from[from];
  if (fromMatch == null) {
    return this._getFallbackConverter(from, to);
  }

  var converter = fromMatch[to];
  if (converter == null) {
    // Someone is going to love writing a graph search algorithm to work out
    // the smallest number of conversions, or perhaps the least 'lossy'
    // conversion but for now the only 2 step conversions which we are going to
    // special case are foo->view->dom and foo->stringView->string.
    if (to === 'dom') {
      converter = fromMatch.view;
      if (converter != null) {
        return getChainConverter(converter, viewDomConverter);
      }
    }

    if (to === 'string') {
      converter = fromMatch.stringView;
      if (converter != null) {
        return getChainConverter(converter, stringViewStringConverter);
      }
      converter = fromMatch.view;
      if (converter != null) {
        return getChainConverter(converter, viewStringConverter);
      }
    }

    return this._getFallbackConverter(from, to);
  }
  return converter;
};

/**
 * Get all the registered converters. Most for debugging
 */
Converters.prototype.getAll = function() {
  return Object.keys(this._registered.from).map(name => {
    return this._registered.from[name];
  });
};

/**
 * Helper for get to pick the best fallback converter
 */
Converters.prototype._getFallbackConverter = function(from, to) {
  console.error('No converter from ' + from + ' to ' + to + '. Using fallback');

  if (to === 'dom') {
    return fallbackDomConverter;
  }

  if (to === 'string') {
    return fallbackStringConverter;
  }

  throw new Error('No conversion possible from ' + from + ' to ' + to + '.');
};

/**
 * Convert some data from one type to another
 * @param data The object to convert
 * @param from The type of the data right now
 * @param to The type that we would like the data in
 * @param conversionContext An execution context (i.e. simplified requisition)
 * which is often required for access to a document, or createView function
 */
Converters.prototype.convert = function(data, from, to, conversionContext) {
  try {
    if (from === to) {
      return Promise.resolve(data);
    }

    var converter = this.get(from, to);
    return host.exec(() => {
      return converter.exec(data, conversionContext);
    });
  }
  catch (ex) {
    var converter = this.get('error', to);
    return host.exec(() => {
      return converter.exec(ex, conversionContext);
    });
  }
};

exports.Converters = Converters;

/**
 * Items for export
 */
exports.items = [
  viewDomConverter, viewStringConverter, stringViewStringConverter,
  errorDomConverter, errorStringConverter
];
PK
!<W��WWOchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/converters/html.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var util = require('../util/util');

/**
 * 'html' means a string containing HTML markup. We use innerHTML to inject
 * this into a DOM which has security implications, so this module will not
 * be used in all implementations.
 */
exports.items = [
  {
    item: 'converter',
    from: 'html',
    to: 'dom',
    exec: function(html, conversionContext) {
      var div = util.createElement(conversionContext.document, 'div');
      div.innerHTML = html;
      return div;
    }
  },
  {
    item: 'converter',
    from: 'html',
    to: 'string',
    exec: function(html, conversionContext) {
      var div = util.createElement(conversionContext.document, 'div');
      div.innerHTML = html;
      return div.textContent;
    }
  }
];
PK
!<Iu�Schrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/converters/terminal.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var util = require('../util/util');

/**
 * A 'terminal' object is a string or an array of strings, which are typically
 * the output from a shell command
 */
exports.items = [
  {
    item: 'converter',
    from: 'terminal',
    to: 'dom',
    createTextArea: function(text, conversionContext) {
      var node = util.createElement(conversionContext.document, 'textarea');
      node.classList.add('gcli-row-subterminal');
      node.readOnly = true;
      node.textContent = text;
      return node;
    },
    exec: function(data, conversionContext) {
      if (Array.isArray(data)) {
        var node = util.createElement(conversionContext.document, 'div');
        data.forEach(function(member) {
          node.appendChild(this.createTextArea(member, conversionContext));
        });
        return node;
      }
      return this.createTextArea(data);
    }
  },
  {
    item: 'converter',
    from: 'terminal',
    to: 'string',
    exec: function(data, conversionContext) {
      return Array.isArray(data) ? data.join('') : '' + data;
    }
  }
];
PK
!<�qx�	�	Ochrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/fields/delegate.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var util = require('../util/util');
var Field = require('./fields').Field;

/**
 * A field that works with delegate types by delaying resolution until that
 * last possible time
 */
function DelegateField(type, options) {
  Field.call(this, type, options);
  this.options = options;

  this.element = util.createElement(this.document, 'div');
  this.update();

  this.onFieldChange = util.createEvent('DelegateField.onFieldChange');
}

DelegateField.prototype = Object.create(Field.prototype);

DelegateField.prototype.update = function() {
  var subtype = this.type.getType(this.options.requisition.executionContext);
  if (typeof subtype.parse !== 'function') {
    subtype = this.options.requisition.system.types.createType(subtype);
  }

  // It's not clear that we can compare subtypes in this way.
  // Perhaps we need a type.equals(...) function
  if (subtype === this.subtype) {
    return;
  }

  if (this.field) {
    this.field.destroy();
  }

  this.subtype = subtype;
  var fields = this.options.requisition.system.fields;
  this.field = fields.get(subtype, this.options);

  util.clearElement(this.element);
  this.element.appendChild(this.field.element);
};

DelegateField.claim = function(type, context) {
  return type.isDelegate ? Field.MATCH : Field.NO_MATCH;
};

DelegateField.prototype.destroy = function() {
  this.element = undefined;
  this.options = undefined;
  if (this.field) {
    this.field.destroy();
  }
  this.subtype = undefined;
  Field.prototype.destroy.call(this);
};

DelegateField.prototype.setConversion = function(conversion) {
  this.field.setConversion(conversion);
};

DelegateField.prototype.getConversion = function() {
  return this.field.getConversion();
};

Object.defineProperty(DelegateField.prototype, 'isImportant', {
  get: function() {
    return this.field.isImportant;
  },
  enumerable: true
});

/**
 * Exported items
 */
exports.items = [
  DelegateField
];
PK
!<q����Mchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/fields/fields.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var util = require('../util/util');

/**
 * A Field is a way to get input for a single parameter.
 * This class is designed to be inherited from. It's important that all
 * subclasses have a similar constructor signature because they are created
 * via Fields.get(...)
 * @param type The type to use in conversions
 * @param options A set of properties to help fields configure themselves:
 * - document: The document we use in calling createElement
 * - requisition: The requisition that we're attached to
 */
function Field(type, options) {
  this.type = type;
  this.document = options.document;
  this.requisition = options.requisition;
}

/**
 * Enable registration of fields using addItems
 */
Field.prototype.item = 'field';

/**
 * Subclasses should assign their element with the DOM node that gets added
 * to the 'form'. It doesn't have to be an input node, just something that
 * contains it.
 */
Field.prototype.element = undefined;

/**
 * Called from the outside to indicate that the command line has changed and
 * the field should update itself
 */
Field.prototype.update = function() {
};

/**
 * Indicates that this field should drop any resources that it has created
 */
Field.prototype.destroy = function() {
  this.messageElement = undefined;
  this.document = undefined;
  this.requisition = undefined;
};

// Note: We could/should probably change Fields from working with Conversions
// to working with Arguments (Tokens), which makes for less calls to parse()

/**
 * Update this field display with the value from this conversion.
 * Subclasses should provide an implementation of this function.
 */
Field.prototype.setConversion = function(conversion) {
  throw new Error('Field should not be used directly');
};

/**
 * Extract a conversion from the values in this field.
 * Subclasses should provide an implementation of this function.
 */
Field.prototype.getConversion = function() {
  throw new Error('Field should not be used directly');
};

/**
 * Set the element where messages and validation errors will be displayed
 * @see setMessage()
 */
Field.prototype.setMessageElement = function(element) {
  this.messageElement = element;
};

/**
 * Display a validation message in the UI
 */
Field.prototype.setMessage = function(message) {
  if (this.messageElement) {
    util.setTextContent(this.messageElement, message || '');
  }
};

/**
 * Some fields contain information that is more important to the user, for
 * example error messages and completion menus.
 */
Field.prototype.isImportant = false;

/**
 * 'static/abstract' method to allow implementations of Field to lay a claim
 * to a type. This allows claims of various strength to be weighted up.
 * See the Field.*MATCH values.
 */
Field.claim = function(type, context) {
  throw new Error('Field should not be used directly');
};

/**
 * How good a match is a field for a given type
 */
Field.MATCH = 3;           // Good match
Field.DEFAULT = 2;         // A default match
Field.BASIC = 1;           // OK in an emergency. i.e. assume Strings
Field.NO_MATCH = 0;        // This field can't help with the given type

exports.Field = Field;


/**
 * A manager for the registered Fields
 */
function Fields() {
  // Internal array of known fields
  this._fieldCtors = [];
}

/**
 * Add a field definition by field constructor
 * @param fieldCtor Constructor function of new Field
 */
Fields.prototype.add = function(fieldCtor) {
  if (typeof fieldCtor !== 'function') {
    console.error('fields.add erroring on ', fieldCtor);
    throw new Error('fields.add requires a Field constructor');
  }
  this._fieldCtors.push(fieldCtor);
};

/**
 * Remove a Field definition
 * @param field A previously registered field, specified either with a field
 * name or from the field name
 */
Fields.prototype.remove = function(field) {
  if (typeof field !== 'string') {
    this._fieldCtors = this._fieldCtors.filter(function(test) {
      return test !== field;
    });
  }
  else if (field instanceof Field) {
    this.remove(field.name);
  }
  else {
    console.error('fields.remove erroring on ', field);
    throw new Error('fields.remove requires an instance of Field');
  }
};

/**
 * Find the best possible matching field from the specification of the type
 * of field required.
 * @param type An instance of Type that we will represent
 * @param options A set of properties that we should attempt to match, and use
 * in the construction of the new field object:
 * - document: The document to use in creating new elements
 * - requisition: The requisition we're monitoring,
 * @return A newly constructed field that best matches the input options
 */
Fields.prototype.get = function(type, options) {
  var FieldConstructor;
  var highestClaim = -1;
  this._fieldCtors.forEach(function(fieldCtor) {
    var context = (options.requisition == null) ?
                  null : options.requisition.executionContext;
    var claim = fieldCtor.claim(type, context);
    if (claim > highestClaim) {
      highestClaim = claim;
      FieldConstructor = fieldCtor;
    }
  });

  if (!FieldConstructor) {
    console.error('Unknown field type ', type, ' in ', this._fieldCtors);
    throw new Error('Can\'t find field for ' + type);
  }

  if (highestClaim < Field.DEFAULT) {
    return new BlankField(type, options);
  }

  return new FieldConstructor(type, options);
};

/**
 * Get all the registered fields. Most for debugging
 */
Fields.prototype.getAll = function() {
  return this._fieldCtors.slice();
};

exports.Fields = Fields;

/**
 * For use with delegate types that do not yet have anything to resolve to.
 * BlankFields are not for general use.
 */
function BlankField(type, options) {
  Field.call(this, type, options);

  this.element = util.createElement(this.document, 'div');

  this.onFieldChange = util.createEvent('BlankField.onFieldChange');
}

BlankField.prototype = Object.create(Field.prototype);

BlankField.claim = function(type, context) {
  return type.name === 'blank' ? Field.MATCH : Field.NO_MATCH;
};

BlankField.prototype.destroy = function() {
  this.element = undefined;
  Field.prototype.destroy.call(this);
};

BlankField.prototype.setConversion = function(conversion) {
  this.setMessage(conversion.message);
};

BlankField.prototype.getConversion = function() {
  return this.type.parseString('', this.requisition.executionContext);
};

/**
 * Items for export
 */
exports.items = [ BlankField ];
PK
!<������Pchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/fields/selection.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var util = require('../util/util');
var Menu = require('../ui/menu').Menu;

var Argument = require('../types/types').Argument;
var Conversion = require('../types/types').Conversion;
var Field = require('./fields').Field;

/**
 * A field that allows selection of one of a number of options
 */
function SelectionField(type, options) {
  Field.call(this, type, options);

  this.arg = new Argument();

  this.menu = new Menu({
    document: this.document,
    maxPredictions: Conversion.maxPredictions
  });
  this.element = this.menu.element;

  this.onFieldChange = util.createEvent('SelectionField.onFieldChange');

  // i.e. Register this.onItemClick as the default action for a menu click
  this.menu.onItemClick.add(this.itemClicked, this);
}

SelectionField.prototype = Object.create(Field.prototype);

SelectionField.claim = function(type, context) {
  if (context == null) {
    return Field.NO_MATCH;
  }
  return type.getType(context).hasPredictions ? Field.DEFAULT : Field.NO_MATCH;
};

SelectionField.prototype.destroy = function() {
  this.menu.onItemClick.remove(this.itemClicked, this);
  this.menu.destroy();
  this.menu = undefined;
  this.element = undefined;
  Field.prototype.destroy.call(this);
};

SelectionField.prototype.setConversion = function(conversion) {
  this.arg = conversion.arg;
  this.setMessage(conversion.message);

  var context = this.requisition.executionContext;
  conversion.getPredictions(context).then(predictions => {
    var items = predictions.map(function(prediction) {
      // If the prediction value is an 'item' (that is an object with a name and
      // description) then use that, otherwise use the prediction itself, because
      // at least that has a name.
      return prediction.value && prediction.value.description ?
          prediction.value :
          prediction;
    }, this);
    if (this.menu != null) {
      this.menu.show(items, conversion.arg.text);
    }
  }).catch(util.errorHandler);
};

SelectionField.prototype.itemClicked = function(ev) {
  var arg = new Argument(ev.name, '', ' ');
  var context = this.requisition.executionContext;

  this.type.parse(arg, context).then(conversion => {
    this.onFieldChange({ conversion: conversion });
    this.setMessage(conversion.message);
  }).catch(util.errorHandler);
};

SelectionField.prototype.getConversion = function() {
  // This tweaks the prefix/suffix of the argument to fit
  this.arg = this.arg.beget({ text: this.input.value });
  return this.type.parse(this.arg, this.requisition.executionContext);
};

/**
 * Allow the terminal to use RETURN to chose the current menu item when
 * it can't execute the command line
 * @return true if an item was 'clicked', false otherwise
 */
SelectionField.prototype.selectChoice = function() {
  var selected = this.menu.selected;
  if (selected == null) {
    return false;
  }

  this.itemClicked({ name: selected });
  return true;
};

Object.defineProperty(SelectionField.prototype, 'isImportant', {
  get: function() {
    return this.type.name !== 'command';
  },
  enumerable: true
});

/**
 * Allow registration and de-registration.
 */
exports.items = [ SelectionField ];
PK
!<���
��Echrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/index.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var Cc = require('chrome').Cc;
var Ci = require('chrome').Ci;


var prefSvc = Cc['@mozilla.org/preferences-service;1']
                        .getService(Ci.nsIPrefService);
var prefBranch = prefSvc.getBranch(null).QueryInterface(Ci.nsIPrefBranch);

exports.hiddenByChromePref = function() {
  return !prefBranch.prefHasUserValue('devtools.chrome.enabled');
};
PK
!<�y���Dchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/l10n.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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";

var Cc = require("chrome").Cc;
var Ci = require("chrome").Ci;

var prefSvc = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService);
var prefBranch = prefSvc.getBranch(null).QueryInterface(Ci.nsIPrefBranch);

const {LocalizationHelper} = require("devtools/shared/l10n");
const L10N = new LocalizationHelper("devtools/shared/locales/gclicommands.properties");

/**
 * Lookup a string in the GCLI string bundle
 */
exports.lookup = function (name) {
  try {
    return L10N.getStr(name);
  } catch (ex) {
    throw new Error("Failure in lookup('" + name + "')");
  }
};

/**
 * An alternative to lookup().
 * <code>l10n.lookup("BLAH") === l10n.propertyLookup.BLAH</code>
 * This is particularly nice for templates because you can pass
 * <code>l10n:l10n.propertyLookup</code> in the template data and use it
 * like <code>${l10n.BLAH}</code>
 */
exports.propertyLookup = new Proxy({}, {
  get: function (rcvr, name) {
    return exports.lookup(name);
  }
});

/**
 * Lookup a string in the GCLI string bundle
 */
exports.lookupFormat = function (name, swaps) {
  try {
    return L10N.getFormatStr(name, ...swaps);
  } catch (ex) {
    throw new Error("Failure in lookupFormat('" + name + "')");
  }
};

/**
 * Allow GCLI users to be hidden by the "devtools.chrome.enabled" pref.
 * Use it in commands like this:
 * <pre>
 *   name: "somecommand",
 *   hidden: l10n.hiddenByChromePref(),
 *   exec: function (args, context) { ... }
 * </pre>
 */
exports.hiddenByChromePref = function () {
  return !prefBranch.getBoolPref("devtools.chrome.enabled");
};
PK
!<��c��Schrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/languages/command.html
<div>
  <div class="gcli-row-in" save="${rowinEle}" aria-live="assertive"
      onclick="${onclick}" ondblclick="${ondblclick}"
      data-command="${output.canonical}">
    <span
        save="${promptEle}"
        class="gcli-row-prompt ${promptClass}">:</span><span
        class="gcli-row-in-typed">${output.typed}</span>
    <div class="gcli-row-throbber" save="${throbEle}"></div>
  </div>
  <div class="gcli-row-out" aria-live="assertive" save="${rowoutEle}">
  </div>
</div>
PK
!<�*��B�BQchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/languages/command.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var util = require('../util/util');
var domtemplate = require('../util/domtemplate');
var host = require('../util/host');

var Status = require('../types/types').Status;
var cli = require('../cli');
var Requisition = require('../cli').Requisition;
var CommandAssignment = require('../cli').CommandAssignment;
var intro = require('../ui/intro');

var RESOLVED = Promise.resolve(true);

/**
 * Various ways in which we need to manipulate the caret/selection position.
 * A value of null means we're not expecting a change
 */
var Caret = exports.Caret = {
  /**
   * We are expecting changes, but we don't need to move the cursor
   */
  NO_CHANGE: 0,

  /**
   * We want the entire input area to be selected
   */
  SELECT_ALL: 1,

  /**
   * The whole input has changed - push the cursor to the end
   */
  TO_END: 2,

  /**
   * A part of the input has changed - push the cursor to the end of the
   * changed section
   */
  TO_ARG_END: 3
};

/**
 * Shared promise for loading command.html
 */
var commandHtmlPromise;

var commandLanguage = exports.commandLanguage = {
  // Language implementation for GCLI commands
  item: 'language',
  name: 'commands',
  prompt: ':',
  proportionalFonts: true,

  constructor: function(terminal) {
    this.terminal = terminal;
    this.document = terminal.document;
    this.focusManager = terminal.focusManager;

    var options = this.terminal.options;
    this.requisition = options.requisition;
    if (this.requisition == null) {
      if (options.environment == null) {
        options.environment = {};
        options.environment.document = options.document || this.document;
        options.environment.window = options.environment.document.defaultView;
      }

      this.requisition = new Requisition(terminal.system, options);
    }

    // We also keep track of the last known arg text for the current assignment
    this.lastText = undefined;

    // Used to effect caret changes. See _processCaretChange()
    this._caretChange = null;

    // We keep track of which assignment the cursor is in
    this.assignment = this.requisition.getAssignmentAt(0);

    if (commandHtmlPromise == null) {
      commandHtmlPromise = host.staticRequire(module, './command.html');
    }

    return commandHtmlPromise.then(commandHtml => {
      this.commandDom = host.toDom(this.document, commandHtml);

      this.requisition.commandOutputManager.onOutput.add(this.outputted, this);
      var mapping = cli.getMapping(this.requisition.executionContext);
      mapping.terminal = this.terminal;

      this.requisition.onExternalUpdate.add(this.textChanged, this);

      return this;
    });
  },

  destroy: function() {
    var mapping = cli.getMapping(this.requisition.executionContext);
    delete mapping.terminal;

    this.requisition.commandOutputManager.onOutput.remove(this.outputted, this);
    this.requisition.onExternalUpdate.remove(this.textChanged, this);

    this.terminal = undefined;
    this.requisition = undefined;
    this.commandDom = undefined;
  },

  textChanged: function() {
    if (this.terminal == null) {
      return; // This can happen post-destroy()
    }

    if (this.terminal._caretChange == null) {
      // We weren't expecting a change so this was requested by the hint system
      // we should move the cursor to the end of the 'changed section', and the
      // best we can do for that right now is the end of the current argument.
      this.terminal._caretChange = Caret.TO_ARG_END;
    }

    var newStr = this.requisition.toString();
    var input = this.terminal.getInputState();

    input.typed = newStr;
    this._processCaretChange(input);

    // We don't update terminal._previousValue. Should we?
    // Shouldn't this really be a function of terminal?
    if (this.terminal.inputElement.value !== newStr) {
      this.terminal.inputElement.value = newStr;
    }
    this.terminal.onInputChange({ inputState: input });

    // We get here for minor things like whitespace change in arg prefix,
    // so we ignore anything but an actual value change.
    if (this.assignment.arg.text === this.lastText) {
      return;
    }

    this.lastText = this.assignment.arg.text;

    this.terminal.field.update();
    this.terminal.field.setConversion(this.assignment.conversion);
    util.setTextContent(this.terminal.descriptionEle, this.description);
  },

  // Called internally whenever we think that the current assignment might
  // have changed, typically on mouse-clicks or key presses.
  caretMoved: function(start) {
    if (!this.requisition.isUpToDate()) {
      return;
    }
    var newAssignment = this.requisition.getAssignmentAt(start);
    if (newAssignment == null) {
      return;
    }

    if (this.assignment !== newAssignment) {
      if (this.assignment.param.type.onLeave) {
        this.assignment.param.type.onLeave(this.assignment);
      }

      // This can be kicked off either by requisition doing an assign or by
      // terminal noticing a cursor movement out of a command, so we should
      // check that this really is a new assignment
      var isNew = (this.assignment !== newAssignment);

      this.assignment = newAssignment;
      this.terminal.updateCompletion().catch(util.errorHandler);

      if (isNew) {
        this.updateHints();
      }

      if (this.assignment.param.type.onEnter) {
        this.assignment.param.type.onEnter(this.assignment);
      }
    }
    else {
      if (this.assignment && this.assignment.param.type.onChange) {
        this.assignment.param.type.onChange(this.assignment);
      }
    }

    // Warning: compare the logic here with the logic in fieldChanged, which
    // is slightly different. They should probably be the same
    var error = (this.assignment.status === Status.ERROR);
    this.focusManager.setError(error);
  },

  // Called whenever the assignment that we're providing help with changes
  updateHints: function() {
    this.lastText = this.assignment.arg.text;

    var field = this.terminal.field;
    if (field) {
      field.onFieldChange.remove(this.terminal.fieldChanged, this.terminal);
      field.destroy();
    }

    var fields = this.terminal.system.fields;
    field = this.terminal.field = fields.get(this.assignment.param.type, {
      document: this.terminal.document,
      requisition: this.requisition
    });

    this.focusManager.setImportantFieldFlag(field.isImportant);

    field.onFieldChange.add(this.terminal.fieldChanged, this.terminal);
    field.setConversion(this.assignment.conversion);

    // Filled in by the template process
    this.terminal.errorEle = undefined;
    this.terminal.descriptionEle = undefined;

    var contents = this.terminal.tooltipTemplate.cloneNode(true);
    domtemplate.template(contents, this.terminal, {
      blankNullUndefined: true,
      stack: 'terminal.html#tooltip'
    });

    util.clearElement(this.terminal.tooltipElement);
    this.terminal.tooltipElement.appendChild(contents);
    this.terminal.tooltipElement.style.display = 'block';

    field.setMessageElement(this.terminal.errorEle);
  },

  /**
   * See also handleDownArrow for some symmetry
   */
  handleUpArrow: function() {
    // If the user is on a valid value, then we increment the value, but if
    // they've typed something that's not right we page through predictions
    if (this.assignment.getStatus() === Status.VALID) {
      return this.requisition.nudge(this.assignment, 1).then(() => {
        this.textChanged();
        this.focusManager.onInputChange();
        return true;
      });
    }

    return Promise.resolve(false);
  },

  /**
   * See also handleUpArrow for some symmetry
   */
  handleDownArrow: function() {
    if (this.assignment.getStatus() === Status.VALID) {
      return this.requisition.nudge(this.assignment, -1).then(() => {
        this.textChanged();
        this.focusManager.onInputChange();
        return true;
      });
    }

    return Promise.resolve(false);
  },

  /**
   * RETURN checks status and might exec
   */
  handleReturn: function(input) {
    // Deny RETURN unless the command might work
    if (this.requisition.status !== Status.VALID) {
      return Promise.resolve(false);
    }

    this.terminal.history.add(input);
    this.terminal.unsetChoice().catch(util.errorHandler);

    this.terminal._previousValue = this.terminal.inputElement.value;
    this.terminal.inputElement.value = '';

    return this.requisition.exec().then(() => {
      this.textChanged();
      return true;
    });
  },

  /**
   * Warning: We get TAB events for more than just the user pressing TAB in our
   * input element.
   */
  handleTab: function() {
    // It's possible for TAB to not change the input, in which case the
    // textChanged event will not fire, and the caret move will not be
    // processed. So we check that this is done first
    this.terminal._caretChange = Caret.TO_ARG_END;
    var inputState = this.terminal.getInputState();
    this._processCaretChange(inputState);

    this.terminal._previousValue = this.terminal.inputElement.value;

    // The changes made by complete may happen asynchronously, so after the
    // the call to complete() we should avoid making changes before the end
    // of the event loop
    var index = this.terminal.getChoiceIndex();
    return this.requisition.complete(inputState.cursor, index).then(updated => {
      // Abort UI changes if this UI update has been overtaken
      if (!updated) {
        return RESOLVED;
      }
      this.textChanged();
      return this.terminal.unsetChoice();
    });
  },

  /**
   * The input text has changed in some way.
   */
  handleInput: function(value) {
    this.terminal._caretChange = Caret.NO_CHANGE;

    return this.requisition.update(value).then(updated => {
      // Abort UI changes if this UI update has been overtaken
      if (!updated) {
        return RESOLVED;
      }
      this.textChanged();
      return this.terminal.unsetChoice();
    });
  },

  /**
   * Counterpart to |setInput| for moving the cursor.
   * @param cursor A JS object shaped like { start: x, end: y }
   */
  setCursor: function(cursor) {
    this._caretChange = Caret.NO_CHANGE;
    this._processCaretChange({
      typed: this.terminal.inputElement.value,
      cursor: cursor
    });
  },

  /**
   * If this._caretChange === Caret.TO_ARG_END, we alter the input object to move
   * the selection start to the end of the current argument.
   * @param input An object shaped like { typed:'', cursor: { start:0, end:0 }}
   */
  _processCaretChange: function(input) {
    var start, end;
    switch (this._caretChange) {
      case Caret.SELECT_ALL:
        start = 0;
        end = input.typed.length;
        break;

      case Caret.TO_END:
        start = input.typed.length;
        end = input.typed.length;
        break;

      case Caret.TO_ARG_END:
        // There could be a fancy way to do this involving assignment/arg math
        // but it doesn't seem easy, so we cheat a move the cursor to just before
        // the next space, or the end of the input
        start = input.cursor.start;
        do {
          start++;
        }
        while (start < input.typed.length && input.typed[start - 1] !== ' ');

        end = start;
        break;

      default:
        start = input.cursor.start;
        end = input.cursor.end;
        break;
    }

    start = (start > input.typed.length) ? input.typed.length : start;
    end = (end > input.typed.length) ? input.typed.length : end;

    var newInput = {
      typed: input.typed,
      cursor: { start: start, end: end }
    };

    if (this.terminal.inputElement.selectionStart !== start) {
      this.terminal.inputElement.selectionStart = start;
    }
    if (this.terminal.inputElement.selectionEnd !== end) {
      this.terminal.inputElement.selectionEnd = end;
    }

    this.caretMoved(start);

    this._caretChange = null;
    return newInput;
  },

  /**
   * Calculate the properties required by the template process for completer.html
   */
  getCompleterTemplateData: function() {
    var input = this.terminal.getInputState();
    var start = input.cursor.start;
    var index = this.terminal.getChoiceIndex();

    return this.requisition.getStateData(start, index).then(function(data) {
      // Calculate the statusMarkup required to show wavy lines underneath the
      // input text (like that of an inline spell-checker) which used by the
      // template process for completer.html
      // i.e. s/space/&nbsp/g in the string (for HTML display) and status to an
      // appropriate class name (i.e. lower cased, prefixed with gcli-in-)
      data.statusMarkup.forEach(function(member) {
        member.string = member.string.replace(/ /g, '\u00a0'); // i.e. &nbsp;
        member.className = 'gcli-in-' + member.status.toString().toLowerCase();
      }, this);

      return data;
    });
  },

  /**
   * Called by the onFieldChange event (via the terminal) on the current Field
   */
  fieldChanged: function(ev) {
    this.requisition.setAssignment(this.assignment, ev.conversion.arg,
                                   { matchPadding: true }).then(() => {
      this.textChanged();
    });

    var isError = ev.conversion.message != null && ev.conversion.message !== '';
    this.focusManager.setError(isError);
  },

  /**
   * Monitor for new command executions
   */
  outputted: function(ev) {
    if (ev.output.hidden) {
      return;
    }

    var template = this.commandDom.cloneNode(true);
    var templateOptions = { stack: 'terminal.html#outputView' };

    var context = this.requisition.conversionContext;
    var data = {
      onclick: context.update,
      ondblclick: context.updateExec,
      language: this,
      output: ev.output,
      promptClass: (ev.output.error ? 'gcli-row-error' : '') +
                   (ev.output.completed ? ' gcli-row-complete' : ''),
      // Elements attached to this by template().
      rowinEle: null,
      rowoutEle: null,
      throbEle: null,
      promptEle: null
    };

    domtemplate.template(template, data, templateOptions);

    ev.output.promise.then(() => {
      var document = data.rowoutEle.ownerDocument;

      if (ev.output.completed) {
        data.promptEle.classList.add('gcli-row-complete');
      }
      if (ev.output.error) {
        data.promptEle.classList.add('gcli-row-error');
      }

      util.clearElement(data.rowoutEle);

      return ev.output.convert('dom', context).then(node => {
        this.terminal.scrollToBottom();
        data.throbEle.style.display = ev.output.completed ? 'none' : 'block';

        if (node == null) {
          data.promptEle.classList.add('gcli-row-error');
          // TODO: show some error to the user
        }

        this._linksToNewTab(node);
        data.rowoutEle.appendChild(node);

        var event = document.createEvent('Event');
        event.initEvent('load', true, true);
        event.addedElement = node;
        node.dispatchEvent(event);
      });
    }).catch(console.error);

    this.terminal.addElement(data.rowinEle);
    this.terminal.addElement(data.rowoutEle);
    this.terminal.scrollToBottom();

    this.focusManager.outputted();
  },

  /**
   * Find elements with href attributes and add a target=_blank so opened links
   * will open in a new window
   */
  _linksToNewTab: function(element) {
    var links = element.querySelectorAll('*[href]');
    for (var i = 0; i < links.length; i++) {
      links[i].setAttribute('target', '_blank');
    }
    return element;
  },

  /**
   * Show a short introduction to this language
   */
  showIntro: function() {
    intro.maybeShowIntro(this.requisition.commandOutputManager,
                         this.requisition.conversionContext);
  },
};

/**
 * The description (displayed at the top of the hint area) should be blank if
 * we're entering the CommandAssignment (because it's obvious) otherwise it's
 * the parameter description.
 */
Object.defineProperty(commandLanguage, 'description', {
  get: function() {
    if (this.assignment == null || (
        this.assignment instanceof CommandAssignment &&
        this.assignment.value == null)) {
      return '';
    }

    return this.assignment.param.manual || this.assignment.param.description;
  },
  enumerable: true
});

/**
 * Present an error message to the hint popup
 */
Object.defineProperty(commandLanguage, 'message', {
  get: function() {
    return this.assignment.conversion.message;
  },
  enumerable: true
});

exports.items = [ commandLanguage ];
PK
!<q?��	�	Tchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/languages/javascript.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var host = require('../util/host');
var prism = require('../util/prism').Prism;

function isMultiline(text) {
  return typeof text === 'string' && text.indexOf('\n') > -1;
}

exports.items = [
  {
    // Language implementation for Javascript
    item: 'language',
    name: 'javascript',
    prompt: '>',

    constructor: function(terminal) {
      this.document = terminal.document;
      this.focusManager = terminal.focusManager;

      this.updateHints();
    },

    destroy: function() {
      this.document = undefined;
    },

    exec: function(input) {
      return this.evaluate(input).then(response => {
        var output = (response.exception != null) ?
                      response.exception.class :
                      response.output;

        var isSameString = typeof output === 'string' &&
                           input.substr(1, input.length - 2) === output;
        var isSameOther = typeof output !== 'string' &&
                          input === '' + output;

        // Place strings in quotes
        if (typeof output === 'string' && response.exception == null) {
          if (!output.includes('\'')) {
            output = '\'' + output + '\'';
          }
          else {
            output = output.replace(/\\/, '\\').replace(/"/, '"').replace(/'/, '\'');
            output = '"' + output + '"';
          }
        }

        var line;
        if (isSameString || isSameOther || output === undefined) {
          line = input;
        }
        else if (isMultiline(output)) {
          line = input + '\n/*\n' + output + '\n*/';
        }
        else {
          line = input + ' // ' + output;
        }

        var grammar = prism.languages[this.name];
        return prism.highlight(line, grammar, this.name);
      });
    },

    evaluate: function(input) {
      return host.script.evaluate(input);
    }
  }
];
PK
!<d��.DDSchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/languages/languages.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var util = require('../util/util');

var RESOLVED = Promise.resolve(true);

/**
 * This is the base implementation for all languages
 */
var baseLanguage = {
  item: 'language',
  name: undefined,

  constructor: function(terminal) {
  },

  destroy: function() {
  },

  updateHints: function() {
    util.clearElement(this.terminal.tooltipElement);
  },

  description: '',
  message: '',
  caretMoved: function() {},

  handleUpArrow: function() {
    return Promise.resolve(false);
  },

  handleDownArrow: function() {
    return Promise.resolve(false);
  },

  handleTab: function() {
    return this.terminal.unsetChoice().then(function() {
      return RESOLVED;
    }, util.errorHandler);
  },

  handleInput: function(input) {
    if (input === ':') {
      return this.terminal.setInput('').then(() => {
        return this.terminal.pushLanguage('commands');
      });
    }

    return this.terminal.unsetChoice().then(function() {
      return RESOLVED;
    }, util.errorHandler);
  },

  handleReturn: function(input) {
    var rowoutEle = this.document.createElement('pre');
    rowoutEle.classList.add('gcli-row-out');
    rowoutEle.classList.add('gcli-row-script');
    rowoutEle.setAttribute('aria-live', 'assertive');

    return this.exec(input).then(line => {
      rowoutEle.innerHTML = line;

      this.terminal.addElement(rowoutEle);
      this.terminal.scrollToBottom();

      this.focusManager.outputted();

      this.terminal.unsetChoice().catch(util.errorHandler);
      this.terminal.inputElement.value = '';
    });
  },

  setCursor: function(cursor) {
    this.terminal.inputElement.selectionStart = cursor.start;
    this.terminal.inputElement.selectionEnd = cursor.end;
  },

  getCompleterTemplateData: function() {
    return Promise.resolve({
      statusMarkup: [
        {
          string: this.terminal.inputElement.value.replace(/ /g, '\u00a0'), // i.e. &nbsp;
          className: 'gcli-in-valid'
        }
      ],
      unclosedJs: false,
      directTabText: '',
      arrowTabText: '',
      emptyParameters: ''
    });
  },

  showIntro: function() {
  },

  exec: function(input) {
    throw new Error('Missing implementation of handleReturn() or exec() ' + this.name);
  }
};

/**
 * A manager for the registered Languages
 */
function Languages() {
  // This is where we cache the languages that we know about
  this._registered = {};
}

/**
 * Add a new language to the cache
 */
Languages.prototype.add = function(language) {
  this._registered[language.name] = language;
};

/**
 * Remove an existing language from the cache
 */
Languages.prototype.remove = function(language) {
  var name = typeof language === 'string' ? language : language.name;
  delete this._registered[name];
};

/**
 * Get access to the list of known languages
 */
Languages.prototype.getAll = function() {
  return Object.keys(this._registered).map(name => {
    return this._registered[name];
  });
};

/**
 * Find a previously registered language
 */
Languages.prototype.createLanguage = function(name, terminal) {
  if (name == null) {
    name = Object.keys(this._registered)[0];
  }

  var language = (typeof name === 'string') ? this._registered[name] : name;
  if (!language) {
    console.error('Known languages: ' + Object.keys(this._registered).join(', '));
    throw new Error('Unknown language: \'' + name + '\'');
  }

  // clone 'type'
  var newInstance = {};
  util.copyProperties(baseLanguage, newInstance);
  util.copyProperties(language, newInstance);

  if (typeof newInstance.constructor === 'function') {
    var reply = newInstance.constructor(terminal);
    return Promise.resolve(reply).then(function() {
      return newInstance;
    });
  }
  else {
    return Promise.resolve(newInstance);
  }
};

exports.Languages = Languages;
PK
!<��T''Ochrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/mozui/completer.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var util = require('../util/util');
var host = require('../util/host');
var domtemplate = require('../util/domtemplate');

var completerHtml =
  '<description\n' +
  '    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">\n' +
  '  <loop foreach="member in ${statusMarkup}">\n' +
  '    <label class="${member.className}" value="${member.string}"></label>\n' +
  '  </loop>\n' +
  '  <label class="gcli-in-ontab" value="${directTabText}"/>\n' +
  '  <label class="gcli-in-todo" foreach="param in ${emptyParameters}" value="${param}"/>\n' +
  '  <label class="gcli-in-ontab" value="${arrowTabText}"/>\n' +
  '  <label class="gcli-in-closebrace" if="${unclosedJs}" value="}"/>\n' +
  '</description>\n';

/**
 * Completer is an 'input-like' element that sits  an input element annotating
 * it with visual goodness.
 * @param components Object that links to other UI components. GCLI provided:
 * - requisition: A GCLI Requisition object whose state is monitored
 * - element: Element to use as root
 * - autoResize: (default=false): Should we attempt to sync the dimensions of
 *   the complete element with the input element.
 */
function Completer(components) {
  this.requisition = components.requisition;
  this.input = { typed: '', cursor: { start: 0, end: 0 } };
  this.choice = 0;

  this.element = components.element;
  this.element.classList.add('gcli-in-complete');
  this.element.setAttribute('tabindex', '-1');
  this.element.setAttribute('aria-live', 'polite');

  this.document = this.element.ownerDocument;

  this.inputter = components.inputter;

  this.inputter.onInputChange.add(this.update, this);
  this.inputter.onAssignmentChange.add(this.update, this);
  this.inputter.onChoiceChange.add(this.update, this);

  this.autoResize = components.autoResize;
  if (this.autoResize) {
    this.inputter.onResize.add(this.resized, this);

    var dimensions = this.inputter.getDimensions();
    if (dimensions) {
      this.resized(dimensions);
    }
  }

  this.template = host.toDom(this.document, completerHtml);
  // We want the spans to line up without the spaces in the template
  util.removeWhitespace(this.template, true);

  this.update();
}

/**
 * Avoid memory leaks
 */
Completer.prototype.destroy = function() {
  this.inputter.onInputChange.remove(this.update, this);
  this.inputter.onAssignmentChange.remove(this.update, this);
  this.inputter.onChoiceChange.remove(this.update, this);

  if (this.autoResize) {
    this.inputter.onResize.remove(this.resized, this);
  }

  this.document = undefined;
  this.element = undefined;
  this.template = undefined;
  this.inputter = undefined;
};

/**
 * Ensure that the completion element is the same size and the inputter element
 */
Completer.prototype.resized = function(ev) {
  this.element.style.top = ev.top + 'px';
  this.element.style.height = ev.height + 'px';
  this.element.style.lineHeight = ev.height + 'px';
  this.element.style.left = ev.left + 'px';
  this.element.style.width = ev.width + 'px';
};

/**
 * Bring the completion element up to date with what the requisition says
 */
Completer.prototype.update = function(ev) {
  this.choice = (ev && ev.choice != null) ? ev.choice : 0;

  this._getCompleterTemplateData().then(data => {
    if (this.template == null) {
      return; // destroy() has been called
    }

    var template = this.template.cloneNode(true);
    domtemplate.template(template, data, { stack: 'completer.html' });

    util.clearElement(this.element);
    while (template.hasChildNodes()) {
      this.element.appendChild(template.firstChild);
    }
  });
};

/**
 * Calculate the properties required by the template process for completer.html
 */
Completer.prototype._getCompleterTemplateData = function() {
  var input = this.inputter.getInputState();
  var start = input.cursor.start;

  return this.requisition.getStateData(start, this.choice).then(function(data) {
    // Calculate the statusMarkup required to show wavy lines underneath the
    // input text (like that of an inline spell-checker) which used by the
    // template process for completer.html
    // i.e. s/space/&nbsp/g in the string (for HTML display) and status to an
    // appropriate class name (i.e. lower cased, prefixed with gcli-in-)
    data.statusMarkup.forEach(function(member) {
      member.string = member.string.replace(/ /g, '\u00a0'); // i.e. &nbsp;
      member.className = 'gcli-in-' + member.status.toString().toLowerCase();
    }, this);

    return data;
  });
};

exports.Completer = Completer;
PK
!<���MMNchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/mozui/inputter.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var util = require('../util/util');
var KeyEvent = require('../util/util').KeyEvent;

var Status = require('../types/types').Status;
var History = require('../ui/history').History;

var Telemetry = require("devtools/client/shared/telemetry");

var RESOLVED = Promise.resolve(true);

/**
 * A wrapper to take care of the functions concerning an input element
 * @param components Object that links to other UI components. GCLI provided:
 * - requisition
 * - focusManager
 * - element
 */
function Inputter(components) {
  this.requisition = components.requisition;
  this.focusManager = components.focusManager;

  this.element = components.element;
  this.element.classList.add('gcli-in-input');
  this.element.spellcheck = false;

  this.document = this.element.ownerDocument;

  // Used to distinguish focus from TAB in CLI. See onKeyUp()
  this.lastTabDownAt = 0;

  // Used to effect caret changes. See _processCaretChange()
  this._caretChange = null;

  // Use telemetry
  this._telemetry = new Telemetry();

  // Ensure that TAB/UP/DOWN isn't handled by the browser
  this.onKeyDown = this.onKeyDown.bind(this);
  this.onKeyUp = this.onKeyUp.bind(this);
  this.element.addEventListener('keydown', this.onKeyDown);
  this.element.addEventListener('keyup', this.onKeyUp);

  // Setup History
  this.history = new History();
  this._scrollingThroughHistory = false;

  // Used when we're selecting which prediction to complete with
  this._choice = null;
  this.onChoiceChange = util.createEvent('Inputter.onChoiceChange');

  // Cursor position affects hint severity
  this.onMouseUp = this.onMouseUp.bind(this);
  this.element.addEventListener('mouseup', this.onMouseUp);

  if (this.focusManager) {
    this.focusManager.addMonitoredElement(this.element, 'input');
  }

  // Initially an asynchronous completion isn't in-progress
  this._completed = RESOLVED;

  this.textChanged = this.textChanged.bind(this);

  this.outputted = this.outputted.bind(this);
  this.requisition.commandOutputManager.onOutput.add(this.outputted, this);

  this.assignment = this.requisition.getAssignmentAt(0);
  this.onAssignmentChange = util.createEvent('Inputter.onAssignmentChange');
  this.onInputChange = util.createEvent('Inputter.onInputChange');

  this.onResize = util.createEvent('Inputter.onResize');
  this.onWindowResize = this.onWindowResize.bind(this);
  this.document.defaultView.addEventListener('resize', this.onWindowResize);
  this.requisition.onExternalUpdate.add(this.textChanged, this);

  this._previousValue = undefined;
  this.requisition.update(this.element.value || '');
}

/**
 * Avoid memory leaks
 */
Inputter.prototype.destroy = function() {
  this.document.defaultView.removeEventListener('resize', this.onWindowResize);

  this.requisition.commandOutputManager.onOutput.remove(this.outputted, this);
  this.requisition.onExternalUpdate.remove(this.textChanged, this);
  if (this.focusManager) {
    this.focusManager.removeMonitoredElement(this.element, 'input');
  }

  this.element.removeEventListener('mouseup', this.onMouseUp);
  this.element.removeEventListener('keydown', this.onKeyDown);
  this.element.removeEventListener('keyup', this.onKeyUp);

  this.history.destroy();

  if (this.style) {
    this.style.remove();
    this.style = undefined;
  }

  this.textChanged = undefined;
  this.outputted = undefined;
  this.onMouseUp = undefined;
  this.onKeyDown = undefined;
  this.onKeyUp = undefined;
  this.onWindowResize = undefined;
  this.tooltip = undefined;
  this.document = undefined;
  this.element = undefined;
  this._telemetry = undefined;
};

/**
 * Make ourselves visually similar to the input element, and make the input
 * element transparent so our background shines through
 */
Inputter.prototype.onWindowResize = function() {
  // Mochitest sometimes causes resize after shutdown. See Bug 743190
  if (!this.element) {
    return;
  }

  this.onResize(this.getDimensions());
};

/**
 * Make ourselves visually similar to the input element, and make the input
 * element transparent so our background shines through
 */
Inputter.prototype.getDimensions = function() {
  var fixedLoc = {};
  var currentElement = this.element.parentNode;
  while (currentElement && currentElement.nodeName !== '#document') {
    var style = this.document.defaultView.getComputedStyle(currentElement);
    if (style) {
      var position = style.getPropertyValue('position');
      if (position === 'absolute' || position === 'fixed') {
        var bounds = currentElement.getBoundingClientRect();
        fixedLoc.top = bounds.top;
        fixedLoc.left = bounds.left;
        break;
      }
    }
    currentElement = currentElement.parentNode;
  }

  var rect = this.element.getBoundingClientRect();
  return {
    top: rect.top - (fixedLoc.top || 0) + 1,
    height: rect.bottom - rect.top - 1,
    left: rect.left - (fixedLoc.left || 0) + 2,
    width: rect.right - rect.left
  };
};

/**
 * Pass 'outputted' events on to the focus manager
 */
Inputter.prototype.outputted = function() {
  if (this.focusManager) {
    this.focusManager.outputted();
  }
};

/**
 * Handler for the input-element.onMouseUp event
 */
Inputter.prototype.onMouseUp = function(ev) {
  this._checkAssignment();
};

/**
 * Function called when we think the text might have changed
 */
Inputter.prototype.textChanged = function() {
  if (!this.document) {
    return; // This can happen post-destroy()
  }

  if (this._caretChange == null) {
    // We weren't expecting a change so this was requested by the hint system
    // we should move the cursor to the end of the 'changed section', and the
    // best we can do for that right now is the end of the current argument.
    this._caretChange = Caret.TO_ARG_END;
  }

  var newStr = this.requisition.toString();
  var input = this.getInputState();

  input.typed = newStr;
  this._processCaretChange(input);

  if (this.element.value !== newStr) {
    this.element.value = newStr;
  }
  this.onInputChange({ inputState: input });

  this.tooltip.textChanged();
};

/**
 * Various ways in which we need to manipulate the caret/selection position.
 * A value of null means we're not expecting a change
 */
var Caret = {
  /**
   * We are expecting changes, but we don't need to move the cursor
   */
  NO_CHANGE: 0,

  /**
   * We want the entire input area to be selected
   */
  SELECT_ALL: 1,

  /**
   * The whole input has changed - push the cursor to the end
   */
  TO_END: 2,

  /**
   * A part of the input has changed - push the cursor to the end of the
   * changed section
   */
  TO_ARG_END: 3
};

/**
 * If this._caretChange === Caret.TO_ARG_END, we alter the input object to move
 * the selection start to the end of the current argument.
 * @param input An object shaped like { typed:'', cursor: { start:0, end:0 }}
 */
Inputter.prototype._processCaretChange = function(input) {
  var start, end;
  switch (this._caretChange) {
    case Caret.SELECT_ALL:
      start = 0;
      end = input.typed.length;
      break;

    case Caret.TO_END:
      start = input.typed.length;
      end = input.typed.length;
      break;

    case Caret.TO_ARG_END:
      // There could be a fancy way to do this involving assignment/arg math
      // but it doesn't seem easy, so we cheat a move the cursor to just before
      // the next space, or the end of the input
      start = input.cursor.start;
      do {
        start++;
      }
      while (start < input.typed.length && input.typed[start - 1] !== ' ');

      end = start;
      break;

    default:
      start = input.cursor.start;
      end = input.cursor.end;
      break;
  }

  start = (start > input.typed.length) ? input.typed.length : start;
  end = (end > input.typed.length) ? input.typed.length : end;

  var newInput = {
    typed: input.typed,
    cursor: { start: start, end: end }
  };

  if (this.element.selectionStart !== start) {
    this.element.selectionStart = start;
  }
  if (this.element.selectionEnd !== end) {
    this.element.selectionEnd = end;
  }

  this._checkAssignment(start);

  this._caretChange = null;
  return newInput;
};

/**
 * To be called internally whenever we think that the current assignment might
 * have changed, typically on mouse-clicks or key presses.
 * @param start Optional - if specified, the cursor position to use in working
 * out the current assignment. This is needed because setting the element
 * selection start is only recognised when the event loop has finished
 */
Inputter.prototype._checkAssignment = function(start) {
  if (start == null) {
    start = this.element.selectionStart;
  }
  if (!this.requisition.isUpToDate()) {
    return;
  }
  var newAssignment = this.requisition.getAssignmentAt(start);
  if (newAssignment == null) {
    return;
  }
  if (this.assignment !== newAssignment) {
    if (this.assignment.param.type.onLeave) {
      this.assignment.param.type.onLeave(this.assignment);
    }

    this.assignment = newAssignment;
    this.onAssignmentChange({ assignment: this.assignment });

    if (this.assignment.param.type.onEnter) {
      this.assignment.param.type.onEnter(this.assignment);
    }
  }
  else {
    if (this.assignment && this.assignment.param.type.onChange) {
      this.assignment.param.type.onChange(this.assignment);
    }
  }

  // This is slightly nasty - the focusManager generally relies on people
  // telling it what it needs to know (which makes sense because the event
  // system to do it with events would be unnecessarily complex). However
  // requisition doesn't know about the focusManager either. So either one
  // needs to know about the other, or a third-party needs to break the
  // deadlock. These 2 lines are all we're quibbling about, so for now we hack
  if (this.focusManager) {
    var error = (this.assignment.status === Status.ERROR);
    this.focusManager.setError(error);
  }
};

/**
 * Set the input field to a value, for external use.
 * This function updates the data model. It sets the caret to the end of the
 * input. It does not make any similarity checks so calling this function with
 * it's current value resets the cursor position.
 * It does not execute the input or affect the history.
 * This function should not be called internally, by Inputter and never as a
 * result of a keyboard event on this.element or bug 676520 could be triggered.
 */
Inputter.prototype.setInput = function(str) {
  this._caretChange = Caret.TO_END;
  return this.requisition.update(str).then(updated => {
    this.textChanged();
    return updated;
  });
};

/**
 * Counterpart to |setInput| for moving the cursor.
 * @param cursor An object shaped like { start: x, end: y }
 */
Inputter.prototype.setCursor = function(cursor) {
  this._caretChange = Caret.NO_CHANGE;
  this._processCaretChange({ typed: this.element.value, cursor: cursor });
  return RESOLVED;
};

/**
 * Focus the input element
 */
Inputter.prototype.focus = function() {
  this.element.focus();
  this._checkAssignment();
};

/**
 * Ensure certain keys (arrows, tab, etc) that we would like to handle
 * are not handled by the browser
 */
Inputter.prototype.onKeyDown = function(ev) {
  if (ev.keyCode === KeyEvent.DOM_VK_UP || ev.keyCode === KeyEvent.DOM_VK_DOWN) {
    ev.preventDefault();
    return;
  }

  // The following keys do not affect the state of the command line so we avoid
  // informing the focusManager about keyboard events that involve these keys
  if (ev.keyCode === KeyEvent.DOM_VK_F1 ||
      ev.keyCode === KeyEvent.DOM_VK_ESCAPE ||
      ev.keyCode === KeyEvent.DOM_VK_UP ||
      ev.keyCode === KeyEvent.DOM_VK_DOWN) {
    return;
  }

  if (this.focusManager) {
    this.focusManager.onInputChange();
  }

  if (ev.keyCode === KeyEvent.DOM_VK_TAB) {
    this.lastTabDownAt = 0;
    if (!ev.shiftKey) {
      ev.preventDefault();
      // Record the timestamp of this TAB down so onKeyUp can distinguish
      // focus from TAB in the CLI.
      this.lastTabDownAt = ev.timeStamp;
    }
    if (ev.metaKey || ev.altKey || ev.crtlKey) {
      if (this.document.commandDispatcher) {
        this.document.commandDispatcher.advanceFocus();
      }
      else {
        this.element.blur();
      }
    }
  }
};

/**
 * Handler for use with DOM events, which just calls the promise enabled
 * handleKeyUp function but checks the exit state of the promise so we know
 * if something went wrong.
 */
Inputter.prototype.onKeyUp = function(ev) {
  this.handleKeyUp(ev).catch(util.errorHandler);
};

/**
 * The main keyboard processing loop
 * @return A promise that resolves (to undefined) when the actions kicked off
 * by this handler are completed.
 */
Inputter.prototype.handleKeyUp = function(ev) {
  if (this.focusManager && ev.keyCode === KeyEvent.DOM_VK_F1) {
    this.focusManager.helpRequest();
    return RESOLVED;
  }

  if (this.focusManager && ev.keyCode === KeyEvent.DOM_VK_ESCAPE) {
    this.focusManager.removeHelp();
    return RESOLVED;
  }

  if (ev.keyCode === KeyEvent.DOM_VK_UP) {
    return this._handleUpArrow();
  }

  if (ev.keyCode === KeyEvent.DOM_VK_DOWN) {
    return this._handleDownArrow();
  }

  if (ev.keyCode === KeyEvent.DOM_VK_RETURN) {
    return this._handleReturn();
  }

  if (ev.keyCode === KeyEvent.DOM_VK_TAB && !ev.shiftKey) {
    return this._handleTab(ev);
  }

  if (this._previousValue === this.element.value) {
    return RESOLVED;
  }

  this._scrollingThroughHistory = false;
  this._caretChange = Caret.NO_CHANGE;

  this._completed = this.requisition.update(this.element.value);
  this._previousValue = this.element.value;

  return this._completed.then(() => {
    // Abort UI changes if this UI update has been overtaken
    if (this._previousValue === this.element.value) {
      this._choice = null;
      this.textChanged();
      this.onChoiceChange({ choice: this._choice });
    }
  });
};

/**
 * See also _handleDownArrow for some symmetry
 */
Inputter.prototype._handleUpArrow = function() {
  if (this.tooltip && this.tooltip.isMenuShowing) {
    this.changeChoice(-1);
    return RESOLVED;
  }

  if (this.element.value === '' || this._scrollingThroughHistory) {
    this._scrollingThroughHistory = true;
    return this.requisition.update(this.history.backward()).then(updated => {
      this.textChanged();
      return updated;
    });
  }

  // If the user is on a valid value, then we increment the value, but if
  // they've typed something that's not right we page through predictions
  if (this.assignment.getStatus() === Status.VALID) {
    return this.requisition.nudge(this.assignment, 1).then(() => {
      // See notes on focusManager.onInputChange in onKeyDown
      this.textChanged();
      if (this.focusManager) {
        this.focusManager.onInputChange();
      }
    });
  }

  this.changeChoice(-1);
  return RESOLVED;
};

/**
 * See also _handleUpArrow for some symmetry
 */
Inputter.prototype._handleDownArrow = function() {
  if (this.tooltip && this.tooltip.isMenuShowing) {
    this.changeChoice(+1);
    return RESOLVED;
  }

  if (this.element.value === '' || this._scrollingThroughHistory) {
    this._scrollingThroughHistory = true;
    return this.requisition.update(this.history.forward()).then(updated => {
      this.textChanged();
      return updated;
    });
  }

  // See notes above for the UP key
  if (this.assignment.getStatus() === Status.VALID) {
    return this.requisition.nudge(this.assignment, -1).then(() => {
      // See notes on focusManager.onInputChange in onKeyDown
      this.textChanged();
      if (this.focusManager) {
        this.focusManager.onInputChange();
      }
    });
  }

  this.changeChoice(+1);
  return RESOLVED;
};

/**
 * RETURN checks status and might exec
 */
Inputter.prototype._handleReturn = function() {
  // Deny RETURN unless the command might work
  if (this.requisition.status === Status.VALID) {
    this._scrollingThroughHistory = false;
    this.history.add(this.element.value);

    let name = this.requisition.commandAssignment.value.name;
    this._telemetry.logKeyed("DEVTOOLS_GCLI_COMMANDS_KEYED", name);

    return this.requisition.exec().then(() => {
      this.textChanged();
    });
  }

  // If we can't execute the command, but there is a menu choice to use
  // then use it.
  if (!this.tooltip.selectChoice()) {
    this.focusManager.setError(true);
  }

  this._choice = null;
  return RESOLVED;
};

/**
 * Warning: We get TAB events for more than just the user pressing TAB in our
 * input element.
 */
Inputter.prototype._handleTab = function(ev) {
  // Being able to complete 'nothing' is OK if there is some context, but
  // when there is nothing on the command line it just looks bizarre.
  var hasContents = (this.element.value.length > 0);

  // If the TAB keypress took the cursor from another field to this one,
  // then they get the keydown/keypress, and we get the keyup. In this
  // case we don't want to do any completion.
  // If the time of the keydown/keypress of TAB was close (i.e. within
  // 1 second) to the time of the keyup then we assume that we got them
  // both, and do the completion.
  if (hasContents && this.lastTabDownAt + 1000 > ev.timeStamp) {
    // It's possible for TAB to not change the input, in which case the caret
    // move will not be processed. So we check that this is done first
    this._caretChange = Caret.TO_ARG_END;
    var inputState = this.getInputState();
    this._processCaretChange(inputState);

    if (this._choice == null) {
      this._choice = 0;
    }

    // The changes made by complete may happen asynchronously, so after the
    // the call to complete() we should avoid making changes before the end
    // of the event loop
    this._completed = this.requisition.complete(inputState.cursor,
                                                this._choice);
    this._previousValue = this.element.value;
  }
  this.lastTabDownAt = 0;
  this._scrollingThroughHistory = false;

  return this._completed.then(updated => {
    // Abort UI changes if this UI update has been overtaken
    if (updated) {
      this.textChanged();
      this._choice = null;
      this.onChoiceChange({ choice: this._choice });
    }
  });
};

/**
 * Used by onKeyUp for UP/DOWN to change the current choice from an options
 * menu.
 */
Inputter.prototype.changeChoice = function(amount) {
  if (this._choice == null) {
    this._choice = 0;
  }
  // There's an annoying up is down thing here, the menu is presented
  // with the zeroth index at the top working down, so the UP arrow needs
  // pick the choice below because we're working down
  this._choice += amount;
  this.onChoiceChange({ choice: this._choice });
};

/**
 * Pull together an input object, which may include XUL hacks
 */
Inputter.prototype.getInputState = function() {
  var input = {
    typed: this.element.value,
    cursor: {
      start: this.element.selectionStart,
      end: this.element.selectionEnd
    }
  };

  // Workaround for potential XUL bug 676520 where textbox gives incorrect
  // values for its content
  if (input.typed == null) {
    input = { typed: '', cursor: { start: 0, end: 0 } };
  }

  return input;
};

exports.Inputter = Inputter;
PK
!<�[;h�#�#Mchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/mozui/tooltip.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var util = require('../util/util');
var host = require('../util/host');
var domtemplate = require('../util/domtemplate');

var CommandAssignment = require('../cli').CommandAssignment;

var tooltipHtml =
  '<div class="gcli-tt" aria-live="polite">\n' +
  '  <div class="gcli-tt-description" save="${descriptionEle}">${description}</div>\n' +
  '  ${field.element}\n' +
  '  <div class="gcli-tt-error" save="${errorEle}">${assignment.conversion.message}</div>\n' +
  '  <div class="gcli-tt-highlight" save="${highlightEle}"></div>\n' +
  '</div>';

/**
 * A widget to display an inline dialog which allows the user to fill out
 * the arguments to a command.
 * @param components Object that links to other UI components. GCLI provided:
 * - requisition: The Requisition to fill out
 * - inputter: An instance of Inputter
 * - focusManager: Component to manage hiding/showing this element
 * - panelElement (optional): The element to show/hide on visibility events
 * - element: The root element to populate
 */
function Tooltip(components) {
  this.inputter = components.inputter;
  this.requisition = components.requisition;
  this.focusManager = components.focusManager;

  this.element = components.element;
  this.element.classList.add('gcliterm-tooltip');
  this.document = this.element.ownerDocument;

  this.panelElement = components.panelElement;
  if (this.panelElement) {
    this.panelElement.classList.add('gcli-panel-hide');
    this.focusManager.onVisibilityChange.add(this.visibilityChanged, this);
  }
  this.focusManager.addMonitoredElement(this.element, 'tooltip');

  // We cache the fields we create so we can destroy them later
  this.fields = [];

  this.template = host.toDom(this.document, tooltipHtml);
  this.templateOptions = { blankNullUndefined: true, stack: 'tooltip.html' };

  this.inputter.onChoiceChange.add(this.choiceChanged, this);
  this.inputter.onAssignmentChange.add(this.assignmentChanged, this);

  // We keep a track of which assignment the cursor is in
  this.assignment = undefined;
  this.assignmentChanged({ assignment: this.inputter.assignment });

  // We also keep track of the last known arg text for the current assignment
  this.lastText = undefined;
}

/**
 * Avoid memory leaks
 */
Tooltip.prototype.destroy = function() {
  this.inputter.onAssignmentChange.remove(this.assignmentChanged, this);
  this.inputter.onChoiceChange.remove(this.choiceChanged, this);

  if (this.panelElement) {
    this.focusManager.onVisibilityChange.remove(this.visibilityChanged, this);
  }
  this.focusManager.removeMonitoredElement(this.element, 'tooltip');

  if (this.style) {
    this.style.remove();
    this.style = undefined;
  }

  this.field.onFieldChange.remove(this.fieldChanged, this);
  this.field.destroy();

  this.lastText = undefined;
  this.assignment = undefined;

  this.errorEle = undefined;
  this.descriptionEle = undefined;
  this.highlightEle = undefined;

  this.document = undefined;
  this.element = undefined;
  this.panelElement = undefined;
  this.template = undefined;
};

/**
 * The inputter acts on UP/DOWN if there is a menu showing
 */
Object.defineProperty(Tooltip.prototype, 'isMenuShowing', {
  get: function() {
    return this.focusManager.isTooltipVisible &&
           this.field != null &&
           this.field.menu != null;
  },
  enumerable: true
});

/**
 * Called whenever the assignment that we're providing help with changes
 */
Tooltip.prototype.assignmentChanged = function(ev) {
  // This can be kicked off either by requisition doing an assign or by
  // inputter noticing a cursor movement out of a command, so we should check
  // that this really is a new assignment
  if (this.assignment === ev.assignment) {
    return;
  }

  this.assignment = ev.assignment;
  this.lastText = this.assignment.arg.text;

  if (this.field) {
    this.field.onFieldChange.remove(this.fieldChanged, this);
    this.field.destroy();
  }

  this.field = this.requisition.system.fields.get(this.assignment.param.type, {
    document: this.document,
    requisition: this.requisition
  });

  this.focusManager.setImportantFieldFlag(this.field.isImportant);

  this.field.onFieldChange.add(this.fieldChanged, this);
  this.field.setConversion(this.assignment.conversion);

  // Filled in by the template process
  this.errorEle = undefined;
  this.descriptionEle = undefined;
  this.highlightEle = undefined;

  var contents = this.template.cloneNode(true);
  domtemplate.template(contents, this, this.templateOptions);
  util.clearElement(this.element);
  this.element.appendChild(contents);
  this.element.style.display = 'block';

  this.field.setMessageElement(this.errorEle);

  this._updatePosition();
};

/**
 * Forward the event to the current field
 */
Tooltip.prototype.choiceChanged = function(ev) {
  if (this.field && this.field.menu) {
    var conversion = this.assignment.conversion;
    var context = this.requisition.executionContext;
    conversion.constrainPredictionIndex(context, ev.choice).then(choice => {
      this.field.menu._choice = choice;
      this.field.menu._updateHighlight();
    }).catch(util.errorHandler);
  }
};

/**
 * Allow the inputter to use RETURN to chose the current menu item when
 * it can't execute the command line
 * @return true if there was a selection to use, false otherwise
 */
Tooltip.prototype.selectChoice = function(ev) {
  if (this.field && this.field.selectChoice) {
    return this.field.selectChoice();
  }
  return false;
};

/**
 * Called by the onFieldChange event on the current Field
 */
Tooltip.prototype.fieldChanged = function(ev) {
  this.requisition.setAssignment(this.assignment, ev.conversion.arg,
                                 { matchPadding: true });

  var isError = ev.conversion.message != null && ev.conversion.message !== '';
  this.focusManager.setError(isError);

  // Nasty hack, the inputter won't know about the text change yet, so it will
  // get it's calculations wrong. We need to wait until the current set of
  // changes has had a chance to propagate
  this.document.defaultView.setTimeout(() => {
    this.inputter.focus();
  }, 10);
};

/**
 * Called by the Inputter when the text changes
 */
Tooltip.prototype.textChanged = function() {
  // We get here for minor things like whitespace change in arg prefix,
  // so we ignore anything but an actual value change.
  if (this.assignment.arg.text === this.lastText) {
    return;
  }

  this.lastText = this.assignment.arg.text;

  this.field.setConversion(this.assignment.conversion);
  util.setTextContent(this.descriptionEle, this.description);

  this._updatePosition();
};

/**
 * Called to move the tooltip to the correct horizontal position
 */
Tooltip.prototype._updatePosition = function() {
  var dimensions = this.getDimensionsOfAssignment();

  // 10 is roughly the width of a char
  if (this.panelElement) {
    this.panelElement.style.left = (dimensions.start * 10) + 'px';
  }

  this.focusManager.updatePosition(dimensions);
};

/**
 * Returns a object containing 'start' and 'end' properties which identify the
 * number of pixels from the left hand edge of the input element that represent
 * the text portion of the current assignment.
 */
Tooltip.prototype.getDimensionsOfAssignment = function() {
  var before = '';
  var assignments = this.requisition.getAssignments(true);
  for (var i = 0; i < assignments.length; i++) {
    if (assignments[i] === this.assignment) {
      break;
    }
    before += assignments[i].toString();
  }
  before += this.assignment.arg.prefix;

  var startChar = before.length;
  before += this.assignment.arg.text;
  var endChar = before.length;

  return { start: startChar, end: endChar };
};

/**
 * The description (displayed at the top of the hint area) should be blank if
 * we're entering the CommandAssignment (because it's obvious) otherwise it's
 * the parameter description.
 */
Object.defineProperty(Tooltip.prototype, 'description', {
  get: function() {
    if (this.assignment instanceof CommandAssignment &&
            this.assignment.value == null) {
      return '';
    }

    return this.assignment.param.manual || this.assignment.param.description;
  },
  enumerable: true
});

/**
 * Tweak CSS to show/hide the output
 */
Tooltip.prototype.visibilityChanged = function(ev) {
  if (!this.panelElement) {
    return;
  }

  if (ev.tooltipVisible) {
    this.panelElement.classList.remove('gcli-panel-hide');
  }
  else {
    this.panelElement.classList.add('gcli-panel-hide');
  }
};

exports.Tooltip = Tooltip;
PK
!<<6-��Hchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/settings.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var imports = {};

var Cc = require('chrome').Cc;
var Ci = require('chrome').Ci;
var Cu = require('chrome').Cu;

var XPCOMUtils = require('resource://gre/modules/XPCOMUtils.jsm').XPCOMUtils;
var Services = require("Services");

XPCOMUtils.defineLazyGetter(imports, 'prefBranch', function() {
  var prefService = Cc['@mozilla.org/preferences-service;1']
          .getService(Ci.nsIPrefService);
  return prefService.getBranch(null).QueryInterface(Ci.nsIPrefBranch);
});

var util = require('./util/util');

/**
 * All local settings have this prefix when used in Firefox
 */
var DEVTOOLS_PREFIX = 'devtools.gcli.';

/**
 * A manager for the registered Settings
 */
function Settings(types, settingValues) {
  this._types = types;

  if (settingValues != null) {
    throw new Error('settingValues is not supported when writing to prefs');
  }

  // Collection of preferences for sorted access
  this._settingsAll = [];

  // Collection of preferences for fast indexed access
  this._settingsMap = new Map();

  // Flag so we know if we've read the system preferences
  this._hasReadSystem = false;

  // Event for use to detect when the list of settings changes
  this.onChange = util.createEvent('Settings.onChange');
}

/**
 * Load system prefs if they've not been loaded already
 * @return true
 */
Settings.prototype._readSystem = function() {
  if (this._hasReadSystem) {
    return;
  }

  imports.prefBranch.getChildList('').forEach(name => {
    var setting = new Setting(this, name);
    this._settingsAll.push(setting);
    this._settingsMap.set(name, setting);
  });

  this._settingsAll.sort((s1, s2) => {
    return s1.name.localeCompare(s2.name);
  });

  this._hasReadSystem = true;
};

/**
 * Get an array containing all known Settings filtered to match the given
 * filter (string) at any point in the name of the setting
 */
Settings.prototype.getAll = function(filter) {
  this._readSystem();

  if (filter == null) {
    return this._settingsAll;
  }

  return this._settingsAll.filter(setting => {
    return setting.name.includes(filter);
  });
};

/**
 * Add a new setting
 */
Settings.prototype.add = function(prefSpec) {
  var setting = new Setting(this, prefSpec);

  if (this._settingsMap.has(setting.name)) {
    // Once exists already, we're going to need to replace it in the array
    for (var i = 0; i < this._settingsAll.length; i++) {
      if (this._settingsAll[i].name === setting.name) {
        this._settingsAll[i] = setting;
      }
    }
  }

  this._settingsMap.set(setting.name, setting);
  this.onChange({ added: setting.name });

  return setting;
};

/**
 * Getter for an existing setting. Generally use of this function should be
 * avoided. Systems that define a setting should export it if they wish it to
 * be available to the outside, or not otherwise. Use of this function breaks
 * that boundary and also hides dependencies. Acceptable uses include testing
 * and embedded uses of GCLI that pre-define all settings (e.g. Firefox)
 * @param name The name of the setting to fetch
 * @return The found Setting object, or undefined if the setting was not found
 */
Settings.prototype.get = function(name) {
  // We might be able to give the answer without needing to read all system
  // settings if this is an internal setting
  var found = this._settingsMap.get(name);
  if (!found) {
    found = this._settingsMap.get(DEVTOOLS_PREFIX + name);
  }

  if (found) {
    return found;
  }

  if (this._hasReadSystem) {
    return undefined;
  }
  else {
    this._readSystem();
    found = this._settingsMap.get(name);
    if (!found) {
      found = this._settingsMap.get(DEVTOOLS_PREFIX + name);
    }
    return found;
  }
};

/**
 * Remove a setting. A no-op in this case
 */
Settings.prototype.remove = function() {
};

exports.Settings = Settings;

/**
 * A class to wrap up the properties of a Setting.
 * @see toolkit/components/viewconfig/content/config.js
 */
function Setting(settings, prefSpec) {
  this._settings = settings;
  if (typeof prefSpec === 'string') {
    // We're coming from getAll() i.e. a full listing of prefs
    this.name = prefSpec;
    this.description = '';
  }
  else {
    // A specific addition by GCLI
    this.name = DEVTOOLS_PREFIX + prefSpec.name;

    if (prefSpec.ignoreTypeDifference !== true && prefSpec.type) {
      if (this.type.name !== prefSpec.type) {
        throw new Error('Locally declared type (' + prefSpec.type + ') != ' +
            'Mozilla declared type (' + this.type.name + ') for ' + this.name);
      }
    }

    this.description = prefSpec.description;
  }

  this.onChange = util.createEvent('Setting.onChange');
}

/**
 * Reset this setting to it's initial default value
 */
Setting.prototype.setDefault = function() {
  imports.prefBranch.clearUserPref(this.name);
  Services.prefs.savePrefFile(null);
};

/**
 * What type is this property: boolean/integer/string?
 */
Object.defineProperty(Setting.prototype, 'type', {
  get: function() {
    switch (imports.prefBranch.getPrefType(this.name)) {
      case imports.prefBranch.PREF_BOOL:
        return this._settings._types.createType('boolean');

      case imports.prefBranch.PREF_INT:
        return this._settings._types.createType('number');

      case imports.prefBranch.PREF_STRING:
        return this._settings._types.createType('string');

      default:
        throw new Error('Unknown type for ' + this.name);
    }
  },
  enumerable: true
});

/**
 * What type is this property: boolean/integer/string?
 */
Object.defineProperty(Setting.prototype, 'value', {
  get: function() {
    switch (imports.prefBranch.getPrefType(this.name)) {
      case imports.prefBranch.PREF_BOOL:
        return imports.prefBranch.getBoolPref(this.name);

      case imports.prefBranch.PREF_INT:
        return imports.prefBranch.getIntPref(this.name);

      case imports.prefBranch.PREF_STRING:
        var value = imports.prefBranch.getStringPref(this.name);
        // In case of a localized string
        if (/^chrome:\/\/.+\/locale\/.+\.properties/.test(value)) {
          value = imports.prefBranch.getComplexValue(this.name,
                  Ci.nsIPrefLocalizedString).data;
        }
        return value;

      default:
        throw new Error('Invalid value for ' + this.name);
    }
  },

  set: function(value) {
    if (imports.prefBranch.prefIsLocked(this.name)) {
      throw new Error('Locked preference ' + this.name);
    }

    switch (imports.prefBranch.getPrefType(this.name)) {
      case imports.prefBranch.PREF_BOOL:
        imports.prefBranch.setBoolPref(this.name, value);
        break;

      case imports.prefBranch.PREF_INT:
        imports.prefBranch.setIntPref(this.name, value);
        break;

      case imports.prefBranch.PREF_STRING:
        imports.prefBranch.setStringPref(this.name, value);
        break;

      default:
        throw new Error('Invalid value for ' + this.name);
    }

    Services.prefs.savePrefFile(null);
  },

  enumerable: true
});
PK
!<
k - -Fchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/system.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var util = require('./util/util');
var Commands = require('./commands/commands').Commands;
var Connectors = require('./connectors/connectors').Connectors;
var Converters = require('./converters/converters').Converters;
var Fields = require('./fields/fields').Fields;
var Languages = require('./languages/languages').Languages;
var Settings = require('./settings').Settings;
var Types = require('./types/types').Types;

/**
 * This is the heart of the API that we expose to the outside.
 * @param options Object that customizes how the system acts. Valid properties:
 * - commands, connectors, converters, fields, languages, settings, types:
 *   Custom configured manager objects for these item types
 * - location: a system with a location will ignore commands that don't have a
 *   matching runAt property. This is principly for client/server setups where
 *   we import commands from the server to the client, so a system with
 *   `{ location: 'client' }` will silently ignore commands with
 *   `{ runAt: 'server' }`. Any system without a location will accept commands
 *   with any runAt property (including none).
 */
exports.createSystem = function(options) {
  options = options || {};
  var location = options.location;

  // The plural/singular thing may make you want to scream, but it allows us
  // to say components[getItemType(item)], so a lookup here (and below) saves
  // multiple lookups in the middle of the code
  var components = {
    connector: options.connectors || new Connectors(),
    converter: options.converters || new Converters(),
    field: options.fields || new Fields(),
    language: options.languages || new Languages(),
    type: options.types || new Types()
  };
  components.setting = new Settings(components.type);
  components.command = new Commands(components.type, location);

  var getItemType = function(item) {
    if (item.item) {
      return item.item;
    }
    // Some items are registered using the constructor so we need to check
    // the prototype for the the type of the item
    return (item.prototype && item.prototype.item) ?
           item.prototype.item : 'command';
  };

  var addItem = function(item) {
    try {
      components[getItemType(item)].add(item);
    }
    catch (ex) {
      if (item != null) {
        console.error('While adding: ' + item.name);
      }
      throw ex;
    }
  };

  var removeItem = function(item) {
    components[getItemType(item)].remove(item);
  };

  /**
   * loadableModules is a lookup of names to module loader functions (like
   * the venerable 'require') to which we can pass a name and get back a
   * JS object (or a promise of a JS object). This allows us to have custom
   * loaders to get stuff from the filesystem etc.
   */
  var loadableModules = {};

  /**
   * loadedModules is a lookup by name of the things returned by the functions
   * in loadableModules so we can track what we need to unload / reload.
   */
  var loadedModules = {};

  var unloadModule = function(name) {
    var existingModule = loadedModules[name];
    if (existingModule != null) {
      existingModule.items.forEach(removeItem);
    }
    delete loadedModules[name];
  };

  var loadModule = function(name) {
    var existingModule = loadedModules[name];
    unloadModule(name);

    // And load the new items
    try {
      var loader = loadableModules[name];
      return Promise.resolve(loader(name)).then(function(newModule) {
        if (existingModule === newModule) {
          return;
        }

        if (newModule == null) {
          throw 'Module \'' + name + '\' not found';
        }

        if (newModule.items == null || typeof newModule.items.forEach !== 'function') {
          console.log('Exported properties: ' + Object.keys(newModule).join(', '));
          throw 'Module \'' + name + '\' has no \'items\' array export';
        }

        newModule.items.forEach(addItem);

        loadedModules[name] = newModule;
      });
    }
    catch (ex) {
      console.error('Failed to load module ' + name + ': ' + ex);
      console.error(ex.stack);

      return Promise.resolve();
    }
  };

  var pendingChanges = false;

  var system = {
    addItems: function(items) {
      items.forEach(addItem);
    },

    removeItems: function(items) {
      items.forEach(removeItem);
    },

    addItemsByModule: function(names, options) {
      var promises = [];

      options = options || {};
      if (!options.delayedLoad) {
        // We could be about to add many commands, just report the change once
        this.commands.onCommandsChange.holdFire();
      }

      if (typeof names === 'string') {
        names = [ names ];
      }
      names.forEach(function(name) {
        if (options.loader == null) {
          options.loader = function(name) {
            return require(name);
          };
        }
        loadableModules[name] = options.loader;

        if (options.delayedLoad) {
          pendingChanges = true;
        }
        else {
          promises.push(loadModule(name).catch(console.error));
        }
      });

      if (options.delayedLoad) {
        return Promise.resolve();
      }
      else {
        return Promise.all(promises).then(() => {
          this.commands.onCommandsChange.resumeFire();
        });
      }
    },

    removeItemsByModule: function(name) {
      this.commands.onCommandsChange.holdFire();

      delete loadableModules[name];
      unloadModule(name);

      this.commands.onCommandsChange.resumeFire();
    },

    load: function() {
      if (!pendingChanges) {
        return Promise.resolve();
      }
      this.commands.onCommandsChange.holdFire();

      // clone loadedModules, so we can remove what is left at the end
      var modules = Object.keys(loadedModules).map(function(name) {
        return loadedModules[name];
      });

      var promises = Object.keys(loadableModules).map(function(name) {
        delete modules[name];
        return loadModule(name).catch(console.error);
      });

      Object.keys(modules).forEach(unloadModule);
      pendingChanges = false;

      return Promise.all(promises).then(() => {
        this.commands.onCommandsChange.resumeFire();
      });
    },

    destroy: function() {
      this.commands.onCommandsChange.holdFire();

      Object.keys(loadedModules).forEach(function(name) {
        unloadModule(name);
      });

      this.commands.onCommandsChange.resumeFire();
    },

    toString: function() {
      return 'System [' +
             'commands:' + components.command.getAll().length + ', ' +
             'connectors:' + components.connector.getAll().length + ', ' +
             'converters:' + components.converter.getAll().length + ', ' +
             'fields:' + components.field.getAll().length + ', ' +
             'settings:' + components.setting.getAll().length + ', ' +
             'types:' + components.type.getTypeNames().length + ']';
    }
  };

  Object.defineProperty(system, 'commands', {
    get: function() { return components.command; },
    enumerable: true
  });

  Object.defineProperty(system, 'connectors', {
    get: function() { return components.connector; },
    enumerable: true
  });

  Object.defineProperty(system, 'converters', {
    get: function() { return components.converter; },
    enumerable: true
  });

  Object.defineProperty(system, 'fields', {
    get: function() { return components.field; },
    enumerable: true
  });

  Object.defineProperty(system, 'languages', {
    get: function() { return components.language; },
    enumerable: true
  });

  Object.defineProperty(system, 'settings', {
    get: function() { return components.setting; },
    enumerable: true
  });

  Object.defineProperty(system, 'types', {
    get: function() { return components.type; },
    enumerable: true
  });

  return system;
};

/**
 * Connect a local system with another at the other end of a connector
 * @param system System to which we're adding commands
 * @param front Front which allows access to the remote system from which we
 * import commands
 * @param customProps Array of strings specifying additional properties defined
 * on remote commands that should be considered part of the metadata for the
 * commands imported into the local system
 */
exports.connectFront = function(system, front, customProps) {
  system._handleCommandsChanged = function() {
    syncItems(system, front, customProps).catch(util.errorHandler);
  };
  front.on('commands-changed', system._handleCommandsChanged);

  return syncItems(system, front, customProps);
};

/**
 * Undo the effect of #connectFront
 */
exports.disconnectFront = function(system, front) {
  front.off('commands-changed', system._handleCommandsChanged);
  system._handleCommandsChanged = undefined;
  removeItemsFromFront(system, front);
};

/**
 * Remove the items in this system that came from a previous sync action, and
 * re-add them. See connectFront() for explanation of properties
 */
function syncItems(system, front, customProps) {
  return front.specs(customProps).then(function(specs) {
    removeItemsFromFront(system, front);

    var remoteItems = addLocalFunctions(specs, front);
    system.addItems(remoteItems);

    return system;
  });
};

/**
 * Take the data from the 'specs' command (or the 'commands-changed' event) and
 * add function to proxy the execution back over the front
 */
function addLocalFunctions(specs, front) {
  // Inject an 'exec' function into the commands, and the front into
  // all the remote types
  specs.forEach(function(commandSpec) {
    // HACK: Tack the front to the command so we know how to remove it
    // in removeItemsFromFront() below
    commandSpec.front = front;

    // Tell the type instances for a command how to contact their counterparts
    // Don't confuse this with setting the front on the commandSpec which is
    // about associating a proxied command with it's source for later removal.
    // This is actually going to be used by the type
    commandSpec.params.forEach(function(param) {
      if (typeof param.type !== 'string') {
        param.type.front = front;
      }
    });

    if (!commandSpec.isParent) {
      commandSpec.exec = function(args, context) {
        var typed = (context.prefix ? context.prefix + ' ' : '') + context.typed;
        return front.execute(typed).then(function(reply) {
          var typedData = context.typedData(reply.type, reply.data);
          return reply.isError ? Promise.reject(typedData) : typedData;
        });
      };
    }

    commandSpec.isProxy = true;
  });

  return specs;
}

/**
 * Go through all the commands removing any that are associated with the
 * given front. The method of association is the hack in addLocalFunctions.
 */
function removeItemsFromFront(system, front) {
  system.commands.getAll().forEach(function(command) {
    if (command.front === front) {
      system.commands.remove(command);
    }
  });
}
PK
!<��x�	�	Kchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/types/array.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var ArrayConversion = require('./types').ArrayConversion;
var ArrayArgument = require('./types').ArrayArgument;

exports.items = [
  {
    // A set of objects of the same type
    item: 'type',
    name: 'array',
    subtype: undefined,

    constructor: function() {
      if (!this.subtype) {
        console.error('Array.typeSpec is missing subtype. Assuming string.' +
            this.name);
        this.subtype = 'string';
      }
      this.subtype = this.types.createType(this.subtype);
    },

    getSpec: function(commandName, paramName) {
      return {
        name: 'array',
        subtype: this.subtype.getSpec(commandName, paramName),
      };
    },

    stringify: function(values, context) {
      if (values == null) {
        return '';
      }
      // BUG 664204: Check for strings with spaces and add quotes
      return values.join(' ');
    },

    parse: function(arg, context) {
      if (arg.type !== 'ArrayArgument') {
        console.error('non ArrayArgument to ArrayType.parse', arg);
        throw new Error('non ArrayArgument to ArrayType.parse');
      }

      // Parse an argument to a conversion
      // Hack alert. ArrayConversion needs to be able to answer questions about
      // the status of individual conversions in addition to the overall state.
      // |subArg.conversion| allows us to do that easily.
      var subArgParse = subArg => {
        return this.subtype.parse(subArg, context).then(conversion => {
          subArg.conversion = conversion;
          return conversion;
        });
      };

      var conversionPromises = arg.getArguments().map(subArgParse);
      return Promise.all(conversionPromises).then(function(conversions) {
        return new ArrayConversion(conversions, arg);
      });
    },

    getBlank: function(context) {
      return new ArrayConversion([], new ArrayArgument());
    }
  },
];
PK
!<������Mchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/types/boolean.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var Status = require('./types').Status;
var Conversion = require('./types').Conversion;
var BlankArgument = require('./types').BlankArgument;
var SelectionType = require('./selection').SelectionType;

exports.items = [
  {
    // 'boolean' type
    item: 'type',
    name: 'boolean',
    parent: 'selection',

    getSpec: function() {
      return 'boolean';
    },

    lookup: [
      { name: 'false', value: false },
      { name: 'true', value: true }
    ],

    parse: function(arg, context) {
      if (arg.type === 'TrueNamedArgument') {
        return Promise.resolve(new Conversion(true, arg));
      }
      if (arg.type === 'FalseNamedArgument') {
        return Promise.resolve(new Conversion(false, arg));
      }
      return SelectionType.prototype.parse.call(this, arg, context);
    },

    stringify: function(value, context) {
      if (value == null) {
        return '';
      }
      return '' + value;
    },

    getBlank: function(context) {
      return new Conversion(false, new BlankArgument(), Status.VALID, '',
                            Promise.resolve(this.lookup));
    }
  }
];
PK
!<�$M��Mchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/types/command.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var l10n = require('../util/l10n');
var spell = require('../util/spell');
var SelectionType = require('./selection').SelectionType;
var Status = require('./types').Status;
var Conversion = require('./types').Conversion;
var cli = require('../cli');

exports.items = [
  {
    // Select from the available parameters to a command
    item: 'type',
    name: 'param',
    parent: 'selection',
    stringifyProperty: 'name',
    requisition: undefined,
    isIncompleteName: undefined,

    getSpec: function() {
      throw new Error('param type is not remotable');
    },

    lookup: function() {
      return exports.getDisplayedParamLookup(this.requisition);
    },

    parse: function(arg, context) {
      if (this.isIncompleteName) {
        return SelectionType.prototype.parse.call(this, arg, context);
      }
      else {
        var message = l10n.lookup('cliUnusedArg');
        return Promise.resolve(new Conversion(undefined, arg, Status.ERROR, message));
      }
    }
  },
  {
    // Select from the available commands
    // This is very similar to a SelectionType, however the level of hackery in
    // SelectionType to make it handle Commands correctly was to high, so we
    // simplified.
    // If you are making changes to this code, you should check there too.
    item: 'type',
    name: 'command',
    parent: 'selection',
    stringifyProperty: 'name',
    allowNonExec: true,

    getSpec: function() {
      return {
        name: 'command',
        allowNonExec: this.allowNonExec
      };
    },

    lookup: function(context) {
      var commands = cli.getMapping(context).requisition.system.commands;
      return exports.getCommandLookup(commands);
    },

    parse: function(arg, context) {
      var conversion = exports.parse(context, arg, this.allowNonExec);
      return Promise.resolve(conversion);
    }
  }
];

exports.getDisplayedParamLookup = function(requisition) {
  var displayedParams = [];
  var command = requisition.commandAssignment.value;
  if (command != null) {
    command.params.forEach(function(param) {
      var arg = requisition.getAssignment(param.name).arg;
      if (!param.isPositionalAllowed && arg.type === 'BlankArgument') {
        displayedParams.push({ name: '--' + param.name, value: param });
      }
    });
  }
  return displayedParams;
};

exports.parse = function(context, arg, allowNonExec) {
  var commands = cli.getMapping(context).requisition.system.commands;
  var lookup = exports.getCommandLookup(commands);
  var predictions = exports.findPredictions(arg, lookup);
  return exports.convertPredictions(commands, arg, allowNonExec, predictions);
};

exports.getCommandLookup = function(commands) {
  var sorted = commands.getAll().sort(function(c1, c2) {
    return c1.name.localeCompare(c2.name);
  });
  return sorted.map(function(command) {
    return { name: command.name, value: command };
  });
};

exports.findPredictions = function(arg, lookup) {
  var predictions = [];
  var i, option;
  var maxPredictions = Conversion.maxPredictions;
  var match = arg.text.toLowerCase();

  // Add an option to our list of predicted options
  var addToPredictions = function(option) {
    if (arg.text.length === 0) {
      // If someone hasn't typed anything, we only show top level commands in
      // the menu. i.e. sub-commands (those with a space in their name) are
      // excluded. We do this to keep the list at an overview level.
      if (!option.name.includes(' ')) {
        predictions.push(option);
      }
    }
    else {
      // If someone has typed something, then we exclude parent commands
      // (those without an exec). We do this because the user is drilling
      // down and doesn't need the summary level.
      if (option.value.exec != null) {
        predictions.push(option);
      }
    }
  };

  // If the arg has a suffix then we're kind of 'done'. Only an exact
  // match will do.
  if (arg.suffix.match(/ +/)) {
    for (i = 0; i < lookup.length && predictions.length < maxPredictions; i++) {
      option = lookup[i];
      if (option.name === arg.text ||
          option.name.indexOf(arg.text + ' ') === 0) {
        addToPredictions(option);
      }
    }

    return predictions;
  }

  // Cache lower case versions of all the option names
  for (i = 0; i < lookup.length; i++) {
    option = lookup[i];
    if (option._gcliLowerName == null) {
      option._gcliLowerName = option.name.toLowerCase();
    }
  }

  // Exact hidden matches. If 'hidden: true' then we only allow exact matches
  // All the tests after here check that !option.value.hidden
  for (i = 0; i < lookup.length && predictions.length < maxPredictions; i++) {
    option = lookup[i];
    if (option.name === arg.text) {
      addToPredictions(option);
    }
  }

  // Start with prefix matching
  for (i = 0; i < lookup.length && predictions.length < maxPredictions; i++) {
    option = lookup[i];
    if (option._gcliLowerName.indexOf(match) === 0 && !option.value.hidden) {
      if (!predictions.includes(option)) {
        addToPredictions(option);
      }
    }
  }

  // Try infix matching if we get less half max matched
  if (predictions.length < (maxPredictions / 2)) {
    for (i = 0; i < lookup.length && predictions.length < maxPredictions; i++) {
      option = lookup[i];
      if (option._gcliLowerName.includes(match) && !option.value.hidden) {
        if (!predictions.includes(option)) {
          addToPredictions(option);
        }
      }
    }
  }

  // Try fuzzy matching if we don't get a prefix match
  if (predictions.length === 0) {
    var names = [];
    lookup.forEach(function(opt) {
      if (!opt.value.hidden) {
        names.push(opt.name);
      }
    });
    var corrected = spell.correct(match, names);
    if (corrected) {
      lookup.forEach(function(opt) {
        if (opt.name === corrected) {
          predictions.push(opt);
        }
      });
    }
  }

  return predictions;
};

exports.convertPredictions = function(commands, arg, allowNonExec, predictions) {
  var command = commands.get(arg.text);
  // Helper function - Commands like 'context' work best with parent
  // commands which are not executable. However obviously to execute a
  // command, it needs an exec function.
  var execWhereNeeded = (allowNonExec ||
                  (command != null && typeof command.exec === 'function'));

  var isExact = command && command.name === arg.text &&
                execWhereNeeded && predictions.length === 1;
  var alternatives = isExact ? [] : predictions;

  if (command) {
    var status = execWhereNeeded ? Status.VALID : Status.INCOMPLETE;
    return new Conversion(command, arg, status, '', alternatives);
  }

  if (predictions.length === 0) {
    var msg = l10n.lookupFormat('typesSelectionNomatch', [ arg.text ]);
    return new Conversion(undefined, arg, Status.ERROR, msg, alternatives);
  }

  command = predictions[0].value;

  if (predictions.length === 1) {
    // Is it an exact match of an executable command,
    // or just the only possibility?
    if (command.name === arg.text && execWhereNeeded) {
      return new Conversion(command, arg, Status.VALID, '');
    }

    return new Conversion(undefined, arg, Status.INCOMPLETE, '', alternatives);
  }

  // It's valid if the text matches, even if there are several options
  if (predictions[0].name === arg.text) {
    return new Conversion(command, arg, Status.VALID, '', alternatives);
  }

  return new Conversion(undefined, arg, Status.INCOMPLETE, '', alternatives);
};
PK
!<=19=��Jchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/types/date.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var l10n = require('../util/l10n');
var Status = require('./types').Status;
var Conversion = require('./types').Conversion;

/**
 * Helper for stringify() to left pad a single digit number with a single '0'
 * so 1 -> '01', 42 -> '42', etc.
 */
function pad(number) {
  var r = String(number);
  return r.length === 1 ? '0' + r : r;
}

/**
 * Utility to convert a string to a date, throwing if the date can't be
 * parsed rather than having an invalid date
 */
function toDate(str) {
  var millis = Date.parse(str);
  if (isNaN(millis)) {
    throw new Error(l10n.lookupFormat('typesDateNan', [ str ]));
  }
  return new Date(millis);
}

/**
 * Is |thing| a valid date?
 * @see http://stackoverflow.com/questions/1353684/detecting-an-invalid-date-date-instance-in-javascript
 */
function isDate(thing) {
  return Object.prototype.toString.call(thing) === '[object Date]'
          && !isNaN(thing.getTime());
}

exports.items = [
  {
    // ECMA 5.1 §15.9.1.1
    // @see http://stackoverflow.com/questions/11526504/minimum-and-maximum-date
    item: 'type',
    name: 'date',
    step: 1,
    min: new Date(-8640000000000000),
    max: new Date(8640000000000000),

    constructor: function() {
      this._origMin = this.min;
      if (this.min != null) {
        if (typeof this.min === 'string') {
          this.min = toDate(this.min);
        }
        else if (isDate(this.min) || typeof this.min === 'function') {
          this.min = this.min;
        }
        else {
          throw new Error('date min value must be one of string/date/function');
        }
      }

      this._origMax = this.max;
      if (this.max != null) {
        if (typeof this.max === 'string') {
          this.max = toDate(this.max);
        }
        else if (isDate(this.max) || typeof this.max === 'function') {
          this.max = this.max;
        }
        else {
          throw new Error('date max value must be one of string/date/function');
        }
      }
    },

    getSpec: function() {
      var spec = {
        name: 'date'
      };
      if (this.step !== 1) {
        spec.step = this.step;
      }
      if (this._origMax != null) {
        spec.max = this._origMax;
      }
      if (this._origMin != null) {
        spec.min = this._origMin;
      }
      return spec;
    },

    stringify: function(value, context) {
      if (!isDate(value)) {
        return '';
      }

      var str = pad(value.getFullYear()) + '-' +
                pad(value.getMonth() + 1) + '-' +
                pad(value.getDate());

      // Only add in the time if it's not midnight
      if (value.getHours() !== 0 || value.getMinutes() !== 0 ||
          value.getSeconds() !== 0 || value.getMilliseconds() !== 0) {

        // What string should we use to separate the date from the time?
        // There are 3 options:
        // 'T': This is the standard from ISO8601. i.e. 2013-05-20T11:05
        //      The good news - it's a standard. The bad news - it's weird and
        //      alien to many if not most users
        // ' ': This looks nicest, but needs escaping (which GCLI will do
        //      automatically) so it would look like: '2013-05-20 11:05'
        //      Good news: looks best, bad news: on completion we place the
        //      cursor after the final ', breaking repeated increment/decrement
        // '\ ': It's possible that we could find a way to use a \ to escape
        //      the space, so the output would look like: 2013-05-20\ 11:05
        //      This would involve changes to a number of parts, and is
        //      probably too complex a solution for this problem for now
        // In the short term I'm going for ' ', and raising the priority of
        // cursor positioning on actions like increment/decrement/tab.

        str += ' ' + pad(value.getHours());
        str += ':' + pad(value.getMinutes());

        // Only add in seconds/milliseconds if there is anything to report
        if (value.getSeconds() !== 0 || value.getMilliseconds() !== 0) {
          str += ':' + pad(value.getSeconds());
          if (value.getMilliseconds() !== 0) {
            var milliVal = (value.getUTCMilliseconds() / 1000).toFixed(3);
            str += '.' + String(milliVal).slice(2, 5);
          }
        }
      }

      return str;
    },

    getMax: function(context) {
      if (typeof this.max === 'function') {
        return this._max(context);
      }
      if (isDate(this.max)) {
        return this.max;
      }
      return undefined;
    },

    getMin: function(context) {
      if (typeof this.min === 'function') {
        return this._min(context);
      }
      if (isDate(this.min)) {
        return this.min;
      }
      return undefined;
    },

    parse: function(arg, context) {
      var value;

      if (arg.text.replace(/\s/g, '').length === 0) {
        return Promise.resolve(new Conversion(undefined, arg, Status.INCOMPLETE, ''));
      }

      // Lots of room for improvement here: 1h ago, in two days, etc.
      // Should "1h ago" dynamically update the step?
      if (arg.text.toLowerCase() === 'now' ||
          arg.text.toLowerCase() === 'today') {
        value = new Date();
      }
      else if (arg.text.toLowerCase() === 'yesterday') {
        value = new Date();
        value.setDate(value.getDate() - 1);
      }
      else if (arg.text.toLowerCase() === 'tomorrow') {
        value = new Date();
        value.setDate(value.getDate() + 1);
      }
      else {
        // So now actual date parsing.
        // Javascript dates are a mess. Like the default date libraries in most
        // common languages, but with added browser weirdness.
        // There is an argument for saying that the user will expect dates to
        // be formatted as JavaScript dates, except that JS dates are of
        // themselves very unexpected.
        // See http://blog.dygraphs.com/2012/03/javascript-and-dates-what-mess.html

        // The timezone used by Date.parse depends on whether or not the string
        // can be interpreted as ISO-8601, so "2000-01-01" is not the same as
        // "2000/01/01" (unless your TZ aligns with UTC) because the first is
        // ISO-8601 and therefore assumed to be UTC, where the latter is
        // assumed to be in the local timezone.

        // First, if the user explicitly includes a 'Z' timezone marker, then
        // we assume they know what they are doing with timezones. ISO-8601
        // uses 'Z' as a marker for 'Zulu time', zero hours offset i.e. UTC
        if (arg.text.includes('Z')) {
          value = new Date(arg.text);
        }
        else {
          // Now we don't want the browser to assume ISO-8601 and therefore use
          // UTC so we replace the '-' with '/'
          value = new Date(arg.text.replace(/-/g, '/'));
        }

        if (isNaN(value.getTime())) {
          var msg = l10n.lookupFormat('typesDateNan', [ arg.text ]);
          return Promise.resolve(new Conversion(undefined, arg, Status.ERROR, msg));
        }
      }

      return Promise.resolve(new Conversion(value, arg));
    },

    nudge: function(value, by, context) {
      if (!isDate(value)) {
        return new Date();
      }

      var newValue = new Date(value);
      newValue.setDate(value.getDate() + (by * this.step));

      if (newValue < this.getMin(context)) {
        return this.getMin(context);
      }
      else if (newValue > this.getMax(context)) {
        return this.getMax();
      }
      else {
        return newValue;
      }
    }
  }
];
PK
!<>�����Nchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/types/delegate.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var Conversion = require('./types').Conversion;
var Status = require('./types').Status;
var BlankArgument = require('./types').BlankArgument;

/**
 * The types we expose for registration
 */
exports.items = [
  // A type for "we don't know right now, but hope to soon"
  {
    item: 'type',
    name: 'delegate',

    getSpec: function(commandName, paramName) {
      return {
        name: 'delegate',
        param: paramName
      };
    },

    // Child types should implement this method to return an instance of the type
    // that should be used. If no type is available, or some sort of temporary
    // placeholder is required, BlankType can be used.
    delegateType: undefined,

    stringify: function(value, context) {
      return this.getType(context).then(delegated => {
        return delegated.stringify(value, context);
      });
    },

    parse: function(arg, context) {
      return this.getType(context).then(delegated => {
        return delegated.parse(arg, context);
      });
    },

    nudge: function(value, by, context) {
      return this.getType(context).then(delegated => {
        return delegated.nudge ?
               delegated.nudge(value, by, context) :
               undefined;
      });
    },

    getType: function(context) {
      if (this.delegateType === undefined) {
        return Promise.resolve(this.types.createType('blank'));
      }

      var type = this.delegateType(context);
      if (typeof type.parse !== 'function') {
        type = this.types.createType(type);
      }
      return Promise.resolve(type);
    },

    // DelegateType is designed to be inherited from, so DelegateField needs a
    // way to check if something works like a delegate without using 'name'
    isDelegate: true,

    // Technically we perhaps should proxy this, except that properties are
    // inherently synchronous, so we can't. It doesn't seem important enough to
    // change the function definition to accommodate this right now
    isImportant: false
  },
  {
    item: 'type',
    name: 'remote',
    paramName: undefined,
    blankIsValid: false,
    hasPredictions: true,

    getSpec: function(commandName, paramName) {
      return {
        name: 'remote',
        commandName: commandName,
        paramName: paramName,
        blankIsValid: this.blankIsValid
      };
    },

    getBlank: function(context) {
      if (this.blankIsValid) {
        return new Conversion({ stringified: '' },
                              new BlankArgument(), Status.VALID);
      }
      else {
        return new Conversion(undefined, new BlankArgument(),
                              Status.INCOMPLETE, '');
      }
    },

    stringify: function(value, context) {
      if (value == null) {
        return '';
      }
      // remote types are client only, and we don't attempt to transfer value
      // objects to the client (we can't be sure the are jsonable) so it is a
      // bit strange to be asked to stringify a value object, however since
      // parse creates a Conversion with a (fake) value object we might be
      // asked to stringify that. We can stringify fake value objects.
      if (typeof value.stringified === 'string') {
        return value.stringified;
      }
      throw new Error('Can\'t stringify that value');
    },

    parse: function(arg, context) {
      return this.front.parseType(context.typed, this.paramName).then(json => {
        var status = Status.fromString(json.status);
        return new Conversion(undefined, arg, status, json.message, json.predictions);
      });
    },

    nudge: function(value, by, context) {
      return this.front.nudgeType(context.typed, by, this.paramName).then(json => {
        return { stringified: json.arg };
      });
    }
  },
  // 'blank' is a type for use with DelegateType when we don't know yet.
  // It should not be used anywhere else.
  {
    item: 'type',
    name: 'blank',

    getSpec: function(commandName, paramName) {
      return 'blank';
    },

    stringify: function(value, context) {
      return '';
    },

    parse: function(arg, context) {
      return Promise.resolve(new Conversion(undefined, arg));
    }
  }
];
PK
!<�@ϾOOJchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/types/file.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

/*
 * The file type is a bit of a spiders-web, but there isn't a nice solution
 * yet. The core of the problem is that the modules used by Firefox and NodeJS
 * intersect with the modules used by the web, but not each other. Except here.
 * So we have to do something fancy to get the sharing but not mess up the web.
 *
 * This file requires 'gcli/types/fileparser', and there are 4 implementations
 * of this:
 * - '/lib/gcli/types/fileparser.js', the default web version that uses XHR to
 *   talk to the node server
 * - '/lib/server/gcli/types/fileparser.js', an NodeJS stub, and ...
 * - '/mozilla/gcli/types/fileparser.js', the Firefox implementation both of
 *   these are shims which import
 * - 'gcli/util/fileparser', does the real work, except the actual file access
 *
 * The file access comes from the 'gcli/util/filesystem' module, and there are
 * 2 implementations of this:
 * - '/lib/server/gcli/util/filesystem.js', which uses NodeJS APIs
 * - '/mozilla/gcli/util/filesystem.js', which uses OS.File APIs
 */

var fileparser = require('./fileparser');
var Conversion = require('./types').Conversion;

exports.items = [
  {
    item: 'type',
    name: 'file',

    filetype: 'any',    // One of 'file', 'directory', 'any'
    existing: 'maybe',  // Should be one of 'yes', 'no', 'maybe'
    matches: undefined, // RegExp to match the file part of the path

    hasPredictions: true,

    constructor: function() {
      if (this.filetype !== 'any' && this.filetype !== 'file' &&
          this.filetype !== 'directory') {
        throw new Error('filetype must be one of [any|file|directory]');
      }

      if (this.existing !== 'yes' && this.existing !== 'no' &&
          this.existing !== 'maybe') {
        throw new Error('existing must be one of [yes|no|maybe]');
      }
    },

    getSpec: function(commandName, paramName) {
      return {
        name: 'remote',
        commandName: commandName,
        paramName: paramName
      };
    },

    stringify: function(file) {
      if (file == null) {
        return '';
      }

      return file.toString();
    },

    parse: function(arg, context) {
      var options = {
        filetype: this.filetype,
        existing: this.existing,
        matches: this.matches
      };
      var promise = fileparser.parse(context, arg.text, options);

      return promise.then(function(reply) {
        return new Conversion(reply.value, arg, reply.status,
                              reply.message, reply.predictor);
      });
    }
  }
];
PK
!<��[=��Pchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/types/fileparser.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

exports.parse = require('../util/fileparser').parse;
PK
!<�|3��:�:Pchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/types/javascript.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var l10n = require('../util/l10n');

var Conversion = require('./types').Conversion;
var Type = require('./types').Type;
var Status = require('./types').Status;

/**
 * 'javascript' handles scripted input
 */
function JavascriptType(typeSpec) {
}

JavascriptType.prototype = Object.create(Type.prototype);

JavascriptType.prototype.getSpec = function(commandName, paramName) {
  return {
    name: 'remote',
    paramName: paramName
  };
};

JavascriptType.prototype.stringify = function(value, context) {
  if (value == null) {
    return '';
  }
  return value;
};

/**
 * When sorting out completions, there is no point in displaying millions of
 * matches - this the number of matches that we aim for
 */
JavascriptType.MAX_COMPLETION_MATCHES = 10;

JavascriptType.prototype.parse = function(arg, context) {
  var typed = arg.text;
  var scope = (context.environment.window == null) ?
              null : context.environment.window;

  // No input is undefined
  if (typed === '') {
    return Promise.resolve(new Conversion(undefined, arg, Status.INCOMPLETE));
  }
  // Just accept numbers
  if (!isNaN(parseFloat(typed)) && isFinite(typed)) {
    return Promise.resolve(new Conversion(typed, arg));
  }
  // Just accept constants like true/false/null/etc
  if (typed.trim().match(/(null|undefined|NaN|Infinity|true|false)/)) {
    return Promise.resolve(new Conversion(typed, arg));
  }

  // Analyze the input text and find the beginning of the last part that
  // should be completed.
  var beginning = this._findCompletionBeginning(typed);

  // There was an error analyzing the string.
  if (beginning.err) {
    return Promise.resolve(new Conversion(typed, arg, Status.ERROR, beginning.err));
  }

  // If the current state is ParseState.COMPLEX, then we can't do completion.
  // so bail out now
  if (beginning.state === ParseState.COMPLEX) {
    return Promise.resolve(new Conversion(typed, arg));
  }

  // If the current state is not ParseState.NORMAL, then we are inside of a
  // string which means that no completion is possible.
  if (beginning.state !== ParseState.NORMAL) {
    return Promise.resolve(new Conversion(typed, arg, Status.INCOMPLETE, ''));
  }

  var completionPart = typed.substring(beginning.startPos);
  var properties = completionPart.split('.');
  var matchProp;
  var prop;

  if (properties.length > 1) {
    matchProp = properties.pop().trimLeft();
    for (var i = 0; i < properties.length; i++) {
      prop = properties[i].trim();

      // We can't complete on null.foo, so bail out
      if (scope == null) {
        return Promise.resolve(new Conversion(typed, arg, Status.ERROR,
                                        l10n.lookup('jstypeParseScope')));
      }

      if (prop === '') {
        return Promise.resolve(new Conversion(typed, arg, Status.INCOMPLETE, ''));
      }

      // Check if prop is a getter function on 'scope'. Functions can change
      // other stuff so we can't execute them to get the next object. Stop here.
      if (this._isSafeProperty(scope, prop)) {
        return Promise.resolve(new Conversion(typed, arg));
      }

      try {
        scope = scope[prop];
      }
      catch (ex) {
        // It would be nice to be able to report this error in some way but
        // as it can happen just when someone types '{sessionStorage.', it
        // almost doesn't really count as an error, so we ignore it
        return Promise.resolve(new Conversion(typed, arg, Status.VALID, ''));
      }
    }
  }
  else {
    matchProp = properties[0].trimLeft();
  }

  // If the reason we just stopped adjusting the scope was a non-simple string,
  // then we're not sure if the input is valid or invalid, so accept it
  if (prop && !prop.match(/^[0-9A-Za-z]*$/)) {
    return Promise.resolve(new Conversion(typed, arg));
  }

  // However if the prop was a simple string, it is an error
  if (scope == null) {
    var msg = l10n.lookupFormat('jstypeParseMissing', [ prop ]);
    return Promise.resolve(new Conversion(typed, arg, Status.ERROR, msg));
  }

  // If the thing we're looking for isn't a simple string, then we're not going
  // to find it, but we're not sure if it's valid or invalid, so accept it
  if (!matchProp.match(/^[0-9A-Za-z]*$/)) {
    return Promise.resolve(new Conversion(typed, arg));
  }

  // Skip Iterators and Generators.
  if (this._isIteratorOrGenerator(scope)) {
    return Promise.resolve(new Conversion(typed, arg));
  }

  var matchLen = matchProp.length;
  var prefix = matchLen === 0 ? typed : typed.slice(0, -matchLen);
  var status = Status.INCOMPLETE;
  var message = '';

  // We really want an array of matches (for sorting) but it's easier to
  // detect existing members if we're using a map initially
  var matches = {};

  // We only display a maximum of MAX_COMPLETION_MATCHES, so there is no point
  // in digging up the prototype chain for matches that we're never going to
  // use. Initially look for matches directly on the object itself and then
  // look up the chain to find more
  var distUpPrototypeChain = 0;
  var root = scope;
  try {
    while (root != null &&
        Object.keys(matches).length < JavascriptType.MAX_COMPLETION_MATCHES) {

      /* jshint loopfunc:true */
      Object.keys(root).forEach(function(property) {
        // Only add matching properties. Also, as we're walking up the
        // prototype chain, properties on 'higher' prototypes don't override
        // similarly named properties lower down
        if (property.indexOf(matchProp) === 0 && !(property in matches)) {
          matches[property] = {
            prop: property,
            distUpPrototypeChain: distUpPrototypeChain
          };
        }
      });

      distUpPrototypeChain++;
      root = Object.getPrototypeOf(root);
    }
  }
  catch (ex) {
    return Promise.resolve(new Conversion(typed, arg, Status.INCOMPLETE, ''));
  }

  // Convert to an array for sorting, and while we're at it, note if we got
  // an exact match so we know that this input is valid
  matches = Object.keys(matches).map(function(property) {
    if (property === matchProp) {
      status = Status.VALID;
    }
    return matches[property];
  });

  // The sort keys are:
  // - Being on the object itself, not in the prototype chain
  // - The lack of existence of a vendor prefix
  // - The name
  matches.sort(function(m1, m2) {
    if (m1.distUpPrototypeChain !== m2.distUpPrototypeChain) {
      return m1.distUpPrototypeChain - m2.distUpPrototypeChain;
    }
    // Push all vendor prefixes to the bottom of the list
    return isVendorPrefixed(m1.prop) ?
      (isVendorPrefixed(m2.prop) ? m1.prop.localeCompare(m2.prop) : 1) :
      (isVendorPrefixed(m2.prop) ? -1 : m1.prop.localeCompare(m2.prop));
  });

  // Trim to size. There is a bug for doing a better job of finding matches
  // (bug 682694), but in the mean time there is a performance problem
  // associated with creating a large number of DOM nodes that few people will
  // ever read, so trim ...
  if (matches.length > JavascriptType.MAX_COMPLETION_MATCHES) {
    matches = matches.slice(0, JavascriptType.MAX_COMPLETION_MATCHES - 1);
  }

  // Decorate the matches with:
  // - a description
  // - a value (for the menu) and,
  // - an incomplete flag which reports if we should assume that the user isn't
  //   going to carry on the JS expression with this input so far
  var predictions = matches.map(function(match) {
    var description;
    var incomplete = true;

    if (this._isSafeProperty(scope, match.prop)) {
      description = '(property getter)';
    }
    else {
      try {
        var value = scope[match.prop];

        if (typeof value === 'function') {
          description = '(function)';
        }
        else if (typeof value === 'boolean' || typeof value === 'number') {
          description = '= ' + value;
          incomplete = false;
        }
        else if (typeof value === 'string') {
          if (value.length > 40) {
            value = value.substring(0, 37) + '…';
          }
          description = '= \'' + value + '\'';
          incomplete = false;
        }
        else {
          description = '(' + typeof value + ')';
        }
      }
      catch (ex) {
        description = '(' + l10n.lookup('jstypeParseError') + ')';
      }
    }

    return {
      name: prefix + match.prop,
      value: {
        name: prefix + match.prop,
        description: description
      },
      description: description,
      incomplete: incomplete
    };
  }, this);

  if (predictions.length === 0) {
    status = Status.ERROR;
    message = l10n.lookupFormat('jstypeParseMissing', [ matchProp ]);
  }

  // If the match is the only one possible, and its VALID, predict nothing
  if (predictions.length === 1 && status === Status.VALID) {
    predictions = [];
  }

  return Promise.resolve(new Conversion(typed, arg, status, message,
                                  Promise.resolve(predictions)));
};

/**
 * Does the given property have a prefix that indicates that it is vendor
 * specific?
 */
function isVendorPrefixed(name) {
  return name.indexOf('moz') === 0 ||
         name.indexOf('webkit') === 0 ||
         name.indexOf('ms') === 0;
}

/**
 * Constants used in return value of _findCompletionBeginning()
 */
var ParseState = {
  /**
   * We have simple input like window.foo, without any punctuation that makes
   * completion prediction be confusing or wrong
   */
  NORMAL: 0,

  /**
   * The cursor is in some Javascript that makes completion hard to predict,
   * like console.log(
   */
  COMPLEX: 1,

  /**
   * The cursor is inside single quotes (')
   */
  QUOTE: 2,

  /**
   * The cursor is inside single quotes (")
   */
  DQUOTE: 3
};

var OPEN_BODY = '{[('.split('');
var CLOSE_BODY = '}])'.split('');
var OPEN_CLOSE_BODY = {
  '{': '}',
  '[': ']',
  '(': ')'
};

/**
 * How we distinguish between simple and complex JS input. We attempt
 * completion against simple JS.
 */
var simpleChars = /[a-zA-Z0-9.]/;

/**
 * Analyzes a given string to find the last statement that is interesting for
 * later completion.
 * @param text A string to analyze
 * @return If there was an error in the string detected, then a object like
 *   { err: 'ErrorMesssage' }
 * is returned, otherwise a object like
 *   {
 *     state: ParseState.NORMAL|ParseState.QUOTE|ParseState.DQUOTE,
 *     startPos: index of where the last statement begins
 *   }
 */
JavascriptType.prototype._findCompletionBeginning = function(text) {
  var bodyStack = [];

  var state = ParseState.NORMAL;
  var start = 0;
  var c;
  var complex = false;

  for (var i = 0; i < text.length; i++) {
    c = text[i];
    if (!simpleChars.test(c)) {
      complex = true;
    }

    switch (state) {
      // Normal JS state.
      case ParseState.NORMAL:
        if (c === '"') {
          state = ParseState.DQUOTE;
        }
        else if (c === '\'') {
          state = ParseState.QUOTE;
        }
        else if (c === ';') {
          start = i + 1;
        }
        else if (c === ' ') {
          start = i + 1;
        }
        else if (OPEN_BODY.includes(c)) {
          bodyStack.push({
            token: c,
            start: start
          });
          start = i + 1;
        }
        else if (CLOSE_BODY.includes(c)) {
          var last = bodyStack.pop();
          if (!last || OPEN_CLOSE_BODY[last.token] != c) {
            return { err: l10n.lookup('jstypeBeginSyntax') };
          }
          if (c === '}') {
            start = i + 1;
          }
          else {
            start = last.start;
          }
        }
        break;

      // Double quote state > " <
      case ParseState.DQUOTE:
        if (c === '\\') {
          i ++;
        }
        else if (c === '\n') {
          return { err: l10n.lookup('jstypeBeginUnterm') };
        }
        else if (c === '"') {
          state = ParseState.NORMAL;
        }
        break;

      // Single quote state > ' <
      case ParseState.QUOTE:
        if (c === '\\') {
          i ++;
        }
        else if (c === '\n') {
          return { err: l10n.lookup('jstypeBeginUnterm') };
        }
        else if (c === '\'') {
          state = ParseState.NORMAL;
        }
        break;
    }
  }

  if (state === ParseState.NORMAL && complex) {
    state = ParseState.COMPLEX;
  }

  return {
    state: state,
    startPos: start
  };
};

/**
 * Return true if the passed object is either an iterator or a generator, and
 * false otherwise
 * @param obj The object to check
 */
JavascriptType.prototype._isIteratorOrGenerator = function(obj) {
  if (obj === null) {
    return false;
  }

  if (typeof aObject === 'object') {
    if (typeof obj.__iterator__ === 'function' ||
        obj.constructor && obj.constructor.name === 'Iterator') {
      return true;
    }

    try {
      var str = obj.toString();
      if (typeof obj.next === 'function' &&
          str.indexOf('[object Generator') === 0) {
        return true;
      }
    }
    catch (ex) {
      // window.history.next throws in the typeof check above.
      return false;
    }
  }

  return false;
};

/**
 * Would calling 'scope[prop]' cause the invocation of a non-native (i.e. user
 * defined) function property?
 * Since calling functions can have side effects, it's only safe to do that if
 * explicitly requested, rather than because we're trying things out for the
 * purposes of completion.
 */
JavascriptType.prototype._isSafeProperty = function(scope, prop) {
  if (typeof scope !== 'object') {
    return false;
  }

  // Walk up the prototype chain of 'scope' looking for a property descriptor
  // for 'prop'
  var propDesc;
  while (scope) {
    try {
      propDesc = Object.getOwnPropertyDescriptor(scope, prop);
      if (propDesc) {
        break;
      }
    }
    catch (ex) {
      // Native getters throw here. See bug 520882.
      if (ex.name === 'NS_ERROR_XPC_BAD_CONVERT_JS' ||
          ex.name === 'NS_ERROR_XPC_BAD_OP_ON_WN_PROTO') {
        return false;
      }
      return true;
    }
    scope = Object.getPrototypeOf(scope);
  }

  if (!propDesc) {
    return false;
  }

  if (!propDesc.get) {
    return false;
  }

  // The property is safe if 'get' isn't a function or if the function has a
  // prototype (in which case it's native)
  return typeof propDesc.get !== 'function' || 'prototype' in propDesc.get;
};

JavascriptType.prototype.name = 'javascript';

exports.items = [ JavascriptType ];
PK
!<�U�E��Jchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/types/node.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var Highlighter = require('../util/host').Highlighter;
var l10n = require('../util/l10n');
var util = require('../util/util');
var Status = require('./types').Status;
var Conversion = require('./types').Conversion;
var BlankArgument = require('./types').BlankArgument;

/**
 * Helper functions to be attached to the prototypes of NodeType and
 * NodeListType to allow terminal to tell us which nodes should be highlighted
 */
function onEnter(assignment) {
  // TODO: GCLI doesn't support passing a context to notifications of cursor
  // position, so onEnter/onLeave/onChange are disabled below until we fix this
  assignment.highlighter = new Highlighter(context.environment.window.document);
  assignment.highlighter.nodelist = assignment.conversion.matches;
}

/** @see #onEnter() */
function onLeave(assignment) {
  if (!assignment.highlighter) {
    return;
  }

  assignment.highlighter.destroy();
  delete assignment.highlighter;
}
/** @see #onEnter() */
function onChange(assignment) {
  if (assignment.conversion.matches == null) {
    return;
  }
  if (!assignment.highlighter) {
    return;
  }

  assignment.highlighter.nodelist = assignment.conversion.matches;
}

/**
 * The exported 'node' and 'nodelist' types
 */
exports.items = [
  {
    // The 'node' type is a CSS expression that refers to a single node
    item: 'type',
    name: 'node',

    getSpec: function(commandName, paramName) {
      return {
        name: 'remote',
        commandName: commandName,
        paramName: paramName
      };
    },

    stringify: function(value, context) {
      if (value == null) {
        return '';
      }
      return value.__gcliQuery || 'Error';
    },

    parse: function(arg, context) {
      var reply;

      if (arg.text === '') {
        reply = new Conversion(undefined, arg, Status.INCOMPLETE);
      }
      else {
        var nodes;
        try {
          nodes = context.environment.window.document.querySelectorAll(arg.text);
          if (nodes.length === 0) {
            reply = new Conversion(undefined, arg, Status.INCOMPLETE,
                                   l10n.lookup('nodeParseNone'));
          }
          else if (nodes.length === 1) {
            var node = nodes.item(0);
            node.__gcliQuery = arg.text;

            reply = new Conversion(node, arg, Status.VALID, '');
          }
          else {
            var msg = l10n.lookupFormat('nodeParseMultiple', [ nodes.length ]);
            reply = new Conversion(undefined, arg, Status.ERROR, msg);
          }

          reply.matches = nodes;
        }
        catch (ex) {
          reply = new Conversion(undefined, arg, Status.ERROR,
                                 l10n.lookup('nodeParseSyntax'));
        }
      }

      return Promise.resolve(reply);
    },

    // onEnter: onEnter,
    // onLeave: onLeave,
    // onChange: onChange
  },
  {
    // The 'nodelist' type is a CSS expression that refers to a node list
    item: 'type',
    name: 'nodelist',

    // The 'allowEmpty' option ensures that we do not complain if the entered
    // CSS selector is valid, but does not match any nodes. There is some
    // overlap between this option and 'defaultValue'. What the user wants, in
    // most cases, would be to use 'defaultText' (i.e. what is typed rather than
    // the value that it represents). However this isn't a concept that exists
    // yet and should probably be a part of GCLI if/when it does.
    // All NodeListTypes have an automatic defaultValue of an empty NodeList so
    // they can easily be used in named parameters.
    allowEmpty: false,

    constructor: function() {
      if (typeof this.allowEmpty !== 'boolean') {
        throw new Error('Legal values for allowEmpty are [true|false]');
      }
    },

    getSpec: function(commandName, paramName) {
      return {
        name: 'remote',
        commandName: commandName,
        paramName: paramName,
        blankIsValid: true
      };
    },

    getBlank: function(context) {
      var emptyNodeList = [];
      if (context != null && context.environment.window != null) {
        var doc = context.environment.window.document;
        emptyNodeList = util.createEmptyNodeList(doc);
      }
      return new Conversion(emptyNodeList, new BlankArgument(), Status.VALID);
    },

    stringify: function(value, context) {
      if (value == null) {
        return '';
      }
      return value.__gcliQuery || 'Error';
    },

    parse: function(arg, context) {
      var reply;
      try {
        if (arg.text === '') {
          reply = new Conversion(undefined, arg, Status.INCOMPLETE);
        }
        else {
          var nodes = context.environment.window.document.querySelectorAll(arg.text);

          if (nodes.length === 0 && !this.allowEmpty) {
            reply = new Conversion(undefined, arg, Status.INCOMPLETE,
                                   l10n.lookup('nodeParseNone'));
          }
          else {
            nodes.__gcliQuery = arg.text;
            reply = new Conversion(nodes, arg, Status.VALID, '');
          }

          reply.matches = nodes;
        }
      }
      catch (ex) {
        reply = new Conversion(undefined, arg, Status.ERROR,
                               l10n.lookup('nodeParseSyntax'));
      }

      return Promise.resolve(reply);
    },

    // onEnter: onEnter,
    // onLeave: onLeave,
    // onChange: onChange
  }
];
PK
!<(��(11Lchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/types/number.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var l10n = require('../util/l10n');
var Status = require('./types').Status;
var Conversion = require('./types').Conversion;

exports.items = [
  {
    // 'number' type
    // Has custom max / min / step values to control increment and decrement
    // and a boolean allowFloat property to clamp values to integers
    item: 'type',
    name: 'number',

    allowFloat: false,
    max: undefined,
    min: undefined,
    step: 1,

    constructor: function() {
      if (!this.allowFloat &&
          (this._isFloat(this.min) ||
           this._isFloat(this.max) ||
           this._isFloat(this.step))) {
        throw new Error('allowFloat is false, but non-integer values given in type spec');
      }
    },

    getSpec: function() {
      var spec = {
        name: 'number'
      };
      if (this.step !== 1) {
        spec.step = this.step;
      }
      if (this.max != null) {
        spec.max = this.max;
      }
      if (this.min != null) {
        spec.min = this.min;
      }
      if (this.allowFloat) {
        spec.allowFloat = true;
      }
      return (Object.keys(spec).length === 1) ? 'number' : spec;
    },

    stringify: function(value, context) {
      if (value == null) {
        return '';
      }
      return '' + value;
    },

    getMin: function(context) {
      if (this.min != null) {
        if (typeof this.min === 'function') {
          return this.min(context);
        }
        if (typeof this.min === 'number') {
          return this.min;
        }
      }
      return undefined;
    },

    getMax: function(context) {
      if (this.max != null) {
        if (typeof this.max === 'function') {
          return this.max(context);
        }
        if (typeof this.max === 'number') {
          return this.max;
        }
      }
      return undefined;
    },

    parse: function(arg, context) {
      var msg;
      if (arg.text.replace(/^\s*-?/, '').length === 0) {
        return Promise.resolve(new Conversion(undefined, arg, Status.INCOMPLETE, ''));
      }

      if (!this.allowFloat && (arg.text.includes('.'))) {
        msg = l10n.lookupFormat('typesNumberNotInt2', [ arg.text ]);
        return Promise.resolve(new Conversion(undefined, arg, Status.ERROR, msg));
      }

      var value;
      if (this.allowFloat) {
        value = parseFloat(arg.text);
      }
      else {
        value = parseInt(arg.text, 10);
      }

      if (isNaN(value)) {
        msg = l10n.lookupFormat('typesNumberNan', [ arg.text ]);
        return Promise.resolve(new Conversion(undefined, arg, Status.ERROR, msg));
      }

      var max = this.getMax(context);
      if (max != null && value > max) {
        msg = l10n.lookupFormat('typesNumberMax', [ value, max ]);
        return Promise.resolve(new Conversion(undefined, arg, Status.ERROR, msg));
      }

      var min = this.getMin(context);
      if (min != null && value < min) {
        msg = l10n.lookupFormat('typesNumberMin', [ value, min ]);
        return Promise.resolve(new Conversion(undefined, arg, Status.ERROR, msg));
      }

      return Promise.resolve(new Conversion(value, arg));
    },

    nudge: function(value, by, context) {
      if (typeof value !== 'number' || isNaN(value)) {
        if (by < 0) {
          return this.getMax(context) || 1;
        }
        else {
          var min = this.getMin(context);
          return min != null ? min : 0;
        }
      }

      var newValue = value + (by * this.step);

      // Snap to the nearest incremental of the step
      if (by < 0) {
        newValue = Math.ceil(newValue / this.step) * this.step;
      }
      else {
        newValue = Math.floor(newValue / this.step) * this.step;
        if (this.getMax(context) == null) {
          return newValue;
        }
      }
      return this._boundsCheck(newValue, context);
    },

    // Return the input value so long as it is within the max/min bounds.
    // If it is lower than the minimum, return the minimum. If it is bigger
    // than the maximum then return the maximum.
    _boundsCheck: function(value, context) {
      var min = this.getMin(context);
      if (min != null && value < min) {
        return min;
      }
      var max = this.getMax(context);
      if (max != null && value > max) {
        return max;
      }
      return value;
    },

    // Return true if the given value is a finite number and not an integer,
    // else return false.
    _isFloat: function(value) {
      return ((typeof value === 'number') && isFinite(value) && (value % 1 !== 0));
    }
  }
];
PK
!<)y�}}Nchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/types/resource.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

exports.clearResourceCache = function() {
  ResourceCache.clear();
};

/**
 * Resources are bits of CSS and JavaScript that the page either includes
 * directly or as a result of reading some remote resource.
 * Resource should not be used directly, but instead through a sub-class like
 * CssResource or ScriptResource.
 */
function Resource(name, type, inline, element) {
  this.name = name;
  this.type = type;
  this.inline = inline;
  this.element = element;
}

/**
 * Get the contents of the given resource as a string.
 * The base Resource leaves this unimplemented.
 */
Resource.prototype.loadContents = function() {
  throw new Error('not implemented');
};

Resource.TYPE_SCRIPT = 'text/javascript';
Resource.TYPE_CSS = 'text/css';

/**
 * A CssResource provides an implementation of Resource that works for both
 * [style] elements and [link type='text/css'] elements in the [head].
 */
function CssResource(domSheet) {
  this.name = domSheet.href;
  if (!this.name) {
    this.name = domSheet.ownerNode && domSheet.ownerNode.id ?
            'css#' + domSheet.ownerNode.id :
            'inline-css';
  }

  this.inline = (domSheet.href == null);
  this.type = Resource.TYPE_CSS;
  this.element = domSheet;
}

CssResource.prototype = Object.create(Resource.prototype);

CssResource.prototype.loadContents = function() {
  return new Promise((resolve, reject) => {
    resolve(this.element.ownerNode.innerHTML);
  });
};

CssResource._getAllStyles = function(context) {
  var resources = [];
  if (context.environment.window == null) {
    return resources;
  }

  var doc = context.environment.window.document;
  Array.prototype.forEach.call(doc.styleSheets, function(domSheet) {
    CssResource._getStyle(domSheet, resources);
  });

  dedupe(resources, function(clones) {
    for (var i = 0; i < clones.length; i++) {
      clones[i].name = clones[i].name + '-' + i;
    }
  });

  return resources;
};

CssResource._getStyle = function(domSheet, resources) {
  var resource = ResourceCache.get(domSheet);
  if (!resource) {
    resource = new CssResource(domSheet);
    ResourceCache.add(domSheet, resource);
  }
  resources.push(resource);

  // Look for imported stylesheets
  try {
    Array.prototype.forEach.call(domSheet.cssRules, function(domRule) {
      if (domRule.type == CSSRule.IMPORT_RULE && domRule.styleSheet) {
        CssResource._getStyle(domRule.styleSheet, resources);
      }
    }, this);
  }
  catch (ex) {
    // For system stylesheets
  }
};

/**
 * A ScriptResource provides an implementation of Resource that works for
 * [script] elements (both with a src attribute, and used directly).
 */
function ScriptResource(scriptNode) {
  this.name = scriptNode.src;
  if (!this.name) {
    this.name = scriptNode.id ?
            'script#' + scriptNode.id :
            'inline-script';
  }

  this.inline = (scriptNode.src === '' || scriptNode.src == null);
  this.type = Resource.TYPE_SCRIPT;
  this.element = scriptNode;
}

ScriptResource.prototype = Object.create(Resource.prototype);

ScriptResource.prototype.loadContents = function() {
  return new Promise((resolve, reject) => {
    if (this.inline) {
      resolve(this.element.innerHTML);
    }
    else {
      // It would be good if there was a better way to get the script source
      var xhr = new XMLHttpRequest();
      xhr.onreadystatechange = function() {
        if (xhr.readyState !== xhr.DONE) {
          return;
        }
        resolve(xhr.responseText);
      };
      xhr.open('GET', this.element.src, true);
      xhr.send();
    }
  });
};

ScriptResource._getAllScripts = function(context) {
  if (context.environment.window == null) {
    return [];
  }

  var doc = context.environment.window.document;
  var scriptNodes = doc.querySelectorAll('script');
  var resources = Array.prototype.map.call(scriptNodes, function(scriptNode) {
    var resource = ResourceCache.get(scriptNode);
    if (!resource) {
      resource = new ScriptResource(scriptNode);
      ResourceCache.add(scriptNode, resource);
    }
    return resource;
  });

  dedupe(resources, function(clones) {
    for (var i = 0; i < clones.length; i++) {
      clones[i].name = clones[i].name + '-' + i;
    }
  });

  return resources;
};

/**
 * Find resources with the same name, and call onDupe to change the names
 */
function dedupe(resources, onDupe) {
  // first create a map of name->[array of resources with same name]
  var names = {};
  resources.forEach(function(scriptResource) {
    if (names[scriptResource.name] == null) {
      names[scriptResource.name] = [];
    }
    names[scriptResource.name].push(scriptResource);
  });

  // Call the de-dupe function for each set of dupes
  Object.keys(names).forEach(function(name) {
    var clones = names[name];
    if (clones.length > 1) {
      onDupe(clones);
    }
  });
}

/**
 * A quick cache of resources against nodes
 * TODO: Potential memory leak when the target document has css or script
 * resources repeatedly added and removed. Solution might be to use a weak
 * hash map or some such.
 */
var ResourceCache = {
  _cached: [],

  /**
   * Do we already have a resource that was created for the given node
   */
  get: function(node) {
    for (var i = 0; i < ResourceCache._cached.length; i++) {
      if (ResourceCache._cached[i].node === node) {
        return ResourceCache._cached[i].resource;
      }
    }
    return null;
  },

  /**
   * Add a resource for a given node
   */
  add: function(node, resource) {
    ResourceCache._cached.push({ node: node, resource: resource });
  },

  /**
   * Drop all cache entries. Helpful to prevent memory leaks
   */
  clear: function() {
    ResourceCache._cached = [];
  }
};

/**
 * The resource type itself
 */
exports.items = [
  {
    item: 'type',
    name: 'resource',
    parent: 'selection',
    cacheable: false,
    include: null,

    constructor: function() {
      if (this.include !== Resource.TYPE_SCRIPT &&
          this.include !== Resource.TYPE_CSS &&
          this.include != null) {
        throw new Error('invalid include property: ' + this.include);
      }
    },

    lookup: function(context) {
      var resources = [];
      if (this.include !== Resource.TYPE_SCRIPT) {
        Array.prototype.push.apply(resources,
                                   CssResource._getAllStyles(context));
      }
      if (this.include !== Resource.TYPE_CSS) {
        Array.prototype.push.apply(resources,
                                   ScriptResource._getAllScripts(context));
      }

      return Promise.resolve(resources.map(function(resource) {
        return { name: resource.name, value: resource };
      }));
    }
  }
];
PK
!<0�+�D/D/Ochrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/types/selection.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var l10n = require('../util/l10n');
var spell = require('../util/spell');
var Type = require('./types').Type;
var Status = require('./types').Status;
var Conversion = require('./types').Conversion;
var BlankArgument = require('./types').BlankArgument;

/**
 * A selection allows the user to pick a value from known set of options.
 * An option is made up of a name (which is what the user types) and a value
 * (which is passed to exec)
 * @param typeSpec Object containing properties that describe how this
 * selection functions. Properties include:
 * - lookup: An array of objects, one for each option, which contain name and
 *   value properties. lookup can be a function which returns this array
 * - data: An array of strings - alternative to 'lookup' where the valid values
 *   are strings. i.e. there is no mapping between what is typed and the value
 *   that is used by the program
 * - stringifyProperty: Conversion from value to string is generally a process
 *   of looking through all the valid options for a matching value, and using
 *   the associated name. However the name maybe available directly from the
 *   value using a property lookup. Setting 'stringifyProperty' allows
 *   SelectionType to take this shortcut.
 * - cacheable: If lookup is a function, then we normally assume that
 *   the values fetched can change. Setting 'cacheable:true' enables internal
 *   caching.
 */
function SelectionType(typeSpec) {
  if (typeSpec) {
    Object.keys(typeSpec).forEach(function(key) {
      this[key] = typeSpec[key];
    }, this);
  }

  if (this.name !== 'selection' &&
      this.lookup == null && this.data == null) {
    throw new Error(this.name + ' has no lookup or data');
  }

  this._dataToLookup = this._dataToLookup.bind(this);
}

SelectionType.prototype = Object.create(Type.prototype);

SelectionType.prototype.getSpec = function(commandName, paramName) {
  var spec = { name: 'selection' };
  if (this.lookup != null && typeof this.lookup !== 'function') {
    spec.lookup = this.lookup;
  }
  if (this.data != null && typeof this.data !== 'function') {
    spec.data = this.data;
  }
  if (this.stringifyProperty != null) {
    spec.stringifyProperty = this.stringifyProperty;
  }
  if (this.cacheable) {
    spec.cacheable = true;
  }
  if (typeof this.lookup === 'function' || typeof this.data === 'function') {
    spec.commandName = commandName;
    spec.paramName = paramName;
    spec.remoteLookup = true;
  }
  return spec;
};

SelectionType.prototype.stringify = function(value, context) {
  if (value == null) {
    return '';
  }
  if (this.stringifyProperty != null) {
    return value[this.stringifyProperty];
  }

  return this.getLookup(context).then(lookup => {
    var name = null;
    lookup.some(function(item) {
      if (item.value === value) {
        name = item.name;
        return true;
      }
      return false;
    }, this);
    return name;
  });
};

/**
 * If typeSpec contained cacheable:true then calls to parse() work on cached
 * data. clearCache() enables the cache to be cleared.
 */
SelectionType.prototype.clearCache = function() {
  this._cachedLookup = undefined;
};

/**
 * There are several ways to get selection data. This unifies them into one
 * single function.
 * @return An array of objects with name and value properties.
 */
SelectionType.prototype.getLookup = function(context) {
  if (this._cachedLookup != null) {
    return this._cachedLookup;
  }

  var reply;

  if (this.remoteLookup) {
    reply = this.front.getSelectionLookup(this.commandName, this.paramName);
    reply = resolve(reply, context);
  }
  else if (typeof this.lookup === 'function') {
    reply = resolve(this.lookup.bind(this), context);
  }
  else if (this.lookup != null) {
    reply = resolve(this.lookup, context);
  }
  else if (this.data != null) {
    reply = resolve(this.data, context).then(this._dataToLookup);
  }
  else {
    throw new Error(this.name + ' has no lookup or data');
  }

  if (this.cacheable) {
    this._cachedLookup = reply;
  }

  if (reply == null) {
    console.error(arguments);
  }
  return reply;
};

/**
 * Both 'lookup' and 'data' properties (see docs on SelectionType constructor)
 * in addition to being real data can be a function or a promise, or even a
 * function which returns a promise of real data, etc. This takes a thing and
 * returns a promise of actual values.
 */
function resolve(thing, context) {
  return Promise.resolve(thing).then(function(resolved) {
    if (typeof resolved === 'function') {
      return resolve(resolved(context), context);
    }
    return resolved;
  });
}

/**
 * Selection can be provided with either a lookup object (in the 'lookup'
 * property) or an array of strings (in the 'data' property). Internally we
 * always use lookup, so we need a way to convert a 'data' array to a lookup.
 */
SelectionType.prototype._dataToLookup = function(data) {
  if (!Array.isArray(data)) {
    throw new Error('data for ' + this.name + ' resolved to non-array');
  }

  return data.map(function(option) {
    return { name: option, value: option };
  });
};

/**
 * Return a list of possible completions for the given arg.
 * @param arg The initial input to match
 * @return A trimmed array of string:value pairs
 */
exports.findPredictions = function(arg, lookup) {
  var predictions = [];
  var i, option;
  var maxPredictions = Conversion.maxPredictions;
  var match = arg.text.toLowerCase();

  // If the arg has a suffix then we're kind of 'done'. Only an exact match
  // will do.
  if (arg.suffix.length > 0) {
    for (i = 0; i < lookup.length && predictions.length < maxPredictions; i++) {
      option = lookup[i];
      if (option.name === arg.text) {
        predictions.push(option);
      }
    }

    return predictions;
  }

  // Cache lower case versions of all the option names
  for (i = 0; i < lookup.length; i++) {
    option = lookup[i];
    if (option._gcliLowerName == null) {
      option._gcliLowerName = option.name.toLowerCase();
    }
  }

  // Exact hidden matches. If 'hidden: true' then we only allow exact matches
  // All the tests after here check that !isHidden(option)
  for (i = 0; i < lookup.length && predictions.length < maxPredictions; i++) {
    option = lookup[i];
    if (option.name === arg.text) {
      predictions.push(option);
    }
  }

  // Start with prefix matching
  for (i = 0; i < lookup.length && predictions.length < maxPredictions; i++) {
    option = lookup[i];
    if (option._gcliLowerName.indexOf(match) === 0 && !isHidden(option)) {
      if (!predictions.includes(option)) {
        predictions.push(option);
      }
    }
  }

  // Try infix matching if we get less half max matched
  if (predictions.length < (maxPredictions / 2)) {
    for (i = 0; i < lookup.length && predictions.length < maxPredictions; i++) {
      option = lookup[i];
      if (option._gcliLowerName.includes(match) && !isHidden(option)) {
        if (!predictions.includes(option)) {
          predictions.push(option);
        }
      }
    }
  }

  // Try fuzzy matching if we don't get a prefix match
  if (predictions.length === 0) {
    var names = [];
    lookup.forEach(function(opt) {
      if (!isHidden(opt)) {
        names.push(opt.name);
      }
    });
    var corrected = spell.correct(match, names);
    if (corrected) {
      lookup.forEach(function(opt) {
        if (opt.name === corrected) {
          predictions.push(opt);
        }
      }, this);
    }
  }

  return predictions;
};

SelectionType.prototype.parse = function(arg, context) {
  return Promise.resolve(this.getLookup(context)).then(lookup => {
    var predictions = exports.findPredictions(arg, lookup);
    return exports.convertPredictions(arg, predictions);
  });
};

/**
 * Decide what sort of conversion to return based on the available predictions
 * and how they match the passed arg
 */
exports.convertPredictions = function(arg, predictions) {
  if (predictions.length === 0) {
    var msg = l10n.lookupFormat('typesSelectionNomatch', [ arg.text ]);
    return new Conversion(undefined, arg, Status.ERROR, msg,
                          Promise.resolve(predictions));
  }

  if (predictions[0].name === arg.text) {
    var value = predictions[0].value;
    return new Conversion(value, arg, Status.VALID, '',
                          Promise.resolve(predictions));
  }

  return new Conversion(undefined, arg, Status.INCOMPLETE, '',
                        Promise.resolve(predictions));
};

/**
 * Checking that an option is hidden involves messing in properties on the
 * value right now (which isn't a good idea really) we really should be marking
 * that on the option, so this encapsulates the problem
 */
function isHidden(option) {
  return option.hidden === true ||
         (option.value != null && option.value.hidden);
}

SelectionType.prototype.getBlank = function(context) {
  var predictFunc = context2 => {
    return Promise.resolve(this.getLookup(context2)).then(function(lookup) {
      return lookup.filter(function(option) {
        return !isHidden(option);
      }).slice(0, Conversion.maxPredictions - 1);
    });
  };

  return new Conversion(undefined, new BlankArgument(), Status.INCOMPLETE, '',
                        predictFunc);
};

/**
 * Increment and decrement are confusing for selections. +1 is -1 and -1 is +1.
 * Given an array e.g. [ 'a', 'b', 'c' ] with the current selection on 'b',
 * displayed to the user in the natural way, i.e.:
 *
 *   'a'
 *   'b' <- highlighted as current value
 *   'c'
 *
 * Pressing the UP arrow should take us to 'a', which decrements this index
 * (compare pressing UP on a number which would increment the number)
 *
 * So for selections, we treat +1 as -1 and -1 as +1.
 */
SelectionType.prototype.nudge = function(value, by, context) {
  return this.getLookup(context).then(lookup => {
    var index = this._findValue(lookup, value);
    if (index === -1) {
      if (by < 0) {
        // We're supposed to be doing a decrement (which means +1), but the
        // value isn't found, so we reset the index to the top of the list
        // which is index 0
        index = 0;
      }
      else {
        // For an increment operation when there is nothing to start from, we
        // want to start from the top, i.e. index 0, so the value before we
        // 'increment' (see note above) must be 1.
        index = 1;
      }
    }

    // This is where we invert the sense of up/down (see doc comment)
    index -= by;

    if (index >= lookup.length) {
      index = 0;
    }
    return lookup[index].value;
  });
};

/**
 * Walk through an array of { name:.., value:... } objects looking for a
 * matching value (using strict equality), returning the matched index (or -1
 * if not found).
 * @param lookup Array of objects with name/value properties to search through
 * @param value The value to search for
 * @return The index at which the match was found, or -1 if no match was found
 */
SelectionType.prototype._findValue = function(lookup, value) {
  var index = -1;
  for (var i = 0; i < lookup.length; i++) {
    var pair = lookup[i];
    if (pair.value === value) {
      index = i;
      break;
    }
  }
  return index;
};

/**
 * This is how we indicate to SelectionField that we have predictions that
 * might work in a menu.
 */
SelectionType.prototype.hasPredictions = true;

SelectionType.prototype.name = 'selection';

exports.SelectionType = SelectionType;
exports.items = [ SelectionType ];
PK
!<t�?Mchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/types/setting.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

exports.items = [
  {
    // A type for selecting a known setting
    item: 'type',
    name: 'setting',
    parent: 'selection',
    cacheable: true,
    lookup: function(context) {
      var settings = context.system.settings;

      // Lazily add a settings.onChange listener to clear the cache
      if (!this._registeredListener) {
        settings.onChange.add(function(ev) {
          this.clearCache();
        }, this);
        this._registeredListener = true;
      }

      return settings.getAll().map(function(setting) {
        return { name: setting.name, value: setting };
      });
    }
  },
  {
    // A type for entering the value of a known setting
    // Customizations:
    // - settingParamName The name of the setting parameter so we can customize
    //   the type that we are expecting to read
    item: 'type',
    name: 'settingValue',
    parent: 'delegate',
    settingParamName: 'setting',
    delegateType: function(context) {
      if (context != null) {
        var setting = context.getArgsObject()[this.settingParamName];
        if (setting != null) {
          return setting.type;
        }
      }

      return 'blank';
    }
  }
];
PK
!<G�ZKKLchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/types/string.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var Status = require('./types').Status;
var Conversion = require('./types').Conversion;

exports.items = [
  {
    // 'string' the most basic string type where all we need to do is to take
    // care of converting escaped characters like \t, \n, etc.
    // For the full list see
    // https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Values,_variables,_and_literals
    // The exception is that we ignore \b because replacing '\b' characters in
    // stringify() with their escaped version injects '\\b' all over the place
    // and the need to support \b seems low)
    // Customizations:
    // allowBlank: Allow a blank string to be counted as valid
    item: 'type',
    name: 'string',
    allowBlank: false,

    getSpec: function() {
      return this.allowBlank ?
             { name: 'string', allowBlank: true } :
             'string';
    },

    stringify: function(value, context) {
      if (value == null) {
        return '';
      }

      return value
           .replace(/\\/g, '\\\\')
           .replace(/\f/g, '\\f')
           .replace(/\n/g, '\\n')
           .replace(/\r/g, '\\r')
           .replace(/\t/g, '\\t')
           .replace(/\v/g, '\\v')
           .replace(/\n/g, '\\n')
           .replace(/\r/g, '\\r')
           .replace(/ /g, '\\ ')
           .replace(/'/g, '\\\'')
           .replace(/"/g, '\\"')
           .replace(/{/g, '\\{')
           .replace(/}/g, '\\}');
    },

    parse: function(arg, context) {
      if (!this.allowBlank && (arg.text == null || arg.text === '')) {
        return Promise.resolve(new Conversion(undefined, arg, Status.INCOMPLETE, ''));
      }

      // The string '\\' (i.e. an escaped \ (represented here as '\\\\' because it
      // is double escaped)) is first converted to a private unicode character and
      // then at the end from \uF000 to a single '\' to avoid the string \\n being
      // converted first to \n and then to a <LF>
      var value = arg.text
           .replace(/\\\\/g, '\uF000')
           .replace(/\\f/g, '\f')
           .replace(/\\n/g, '\n')
           .replace(/\\r/g, '\r')
           .replace(/\\t/g, '\t')
           .replace(/\\v/g, '\v')
           .replace(/\\n/g, '\n')
           .replace(/\\r/g, '\r')
           .replace(/\\ /g, ' ')
           .replace(/\\'/g, '\'')
           .replace(/\\"/g, '"')
           .replace(/\\{/g, '{')
           .replace(/\\}/g, '}')
           .replace(/\uF000/g, '\\');

      return Promise.resolve(new Conversion(value, arg));
    }
  }
];
PK
!<K��!�!�Kchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/types/types.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var util = require('../util/util');

/**
 * We record where in the input string an argument comes so we can report
 * errors against those string positions.
 * @param text The string (trimmed) that contains the argument
 * @param prefix Knowledge of quotation marks and whitespace used prior to the
 * text in the input string allows us to re-generate the original input from
 * the arguments.
 * @param suffix Any quotation marks and whitespace used after the text.
 * Whitespace is normally placed in the prefix to the succeeding argument, but
 * can be used here when this is the last argument.
 * @constructor
 */
function Argument(text, prefix, suffix) {
  if (text === undefined) {
    this.text = '';
    this.prefix = '';
    this.suffix = '';
  }
  else {
    this.text = text;
    this.prefix = prefix !== undefined ? prefix : '';
    this.suffix = suffix !== undefined ? suffix : '';
  }
}

Argument.prototype.type = 'Argument';

/**
 * Return the result of merging these arguments.
 * case and some of the arguments are in quotation marks?
 */
Argument.prototype.merge = function(following) {
  // Is it possible that this gets called when we're merging arguments
  // for the single string?
  return new Argument(
    this.text + this.suffix + following.prefix + following.text,
    this.prefix, following.suffix);
};

/**
 * Returns a new Argument like this one but with various items changed.
 * @param options Values to use in creating a new Argument.
 * Warning: some implementations of beget make additions to the options
 * argument. You should be aware of this in the unlikely event that you want to
 * reuse 'options' arguments.
 * Properties:
 * - text: The new text value
 * - prefixSpace: Should the prefix be altered to begin with a space?
 * - prefixPostSpace: Should the prefix be altered to end with a space?
 * - suffixSpace: Should the suffix be altered to end with a space?
 * - type: Constructor to use in creating new instances. Default: Argument
 * - dontQuote: Should we avoid adding prefix/suffix quotes when the text value
 *   has a space? Needed when we're completing a sub-command.
 */
Argument.prototype.beget = function(options) {
  var text = this.text;
  var prefix = this.prefix;
  var suffix = this.suffix;

  if (options.text != null) {
    text = options.text;

    // We need to add quotes when the replacement string has spaces or is empty
    if (!options.dontQuote) {
      var needsQuote = text.includes(' ') || text.length === 0;
      var hasQuote = /['"]$/.test(prefix);
      if (needsQuote && !hasQuote) {
        prefix = prefix + '\'';
        suffix = '\'' + suffix;
      }
    }
  }

  if (options.prefixSpace && prefix.charAt(0) !== ' ') {
    prefix = ' ' + prefix;
  }

  if (options.prefixPostSpace && prefix.charAt(prefix.length - 1) !== ' ') {
    prefix = prefix + ' ';
  }

  if (options.suffixSpace && suffix.charAt(suffix.length - 1) !== ' ') {
    suffix = suffix + ' ';
  }

  if (text === this.text && suffix === this.suffix && prefix === this.prefix) {
    return this;
  }

  var ArgumentType = options.type || Argument;
  return new ArgumentType(text, prefix, suffix);
};

/**
 * We need to keep track of which assignment we've been assigned to
 */
Object.defineProperty(Argument.prototype, 'assignment', {
  get: function() { return this._assignment; },
  set: function(assignment) { this._assignment = assignment; },
  enumerable: true
});

/**
 * Sub-classes of Argument are collections of arguments, getArgs() gets access
 * to the members of the collection in order to do things like re-create input
 * command lines. For the simple Argument case it's just an array containing
 * only this.
 */
Argument.prototype.getArgs = function() {
  return [ this ];
};

/**
 * We define equals to mean all arg properties are strict equals.
 * Used by Conversion.argEquals and Conversion.equals and ultimately
 * Assignment.equals to avoid reporting a change event when a new conversion
 * is assigned.
 */
Argument.prototype.equals = function(that) {
  if (this === that) {
    return true;
  }
  if (that == null || !(that instanceof Argument)) {
    return false;
  }

  return this.text === that.text &&
       this.prefix === that.prefix && this.suffix === that.suffix;
};

/**
 * Helper when we're putting arguments back together
 */
Argument.prototype.toString = function() {
  // BUG 664207: We should re-escape escaped characters
  // But can we do that reliably?
  return this.prefix + this.text + this.suffix;
};

/**
 * Merge an array of arguments into a single argument.
 * All Arguments in the array are expected to have the same emitter
 */
Argument.merge = function(argArray, start, end) {
  start = (start === undefined) ? 0 : start;
  end = (end === undefined) ? argArray.length : end;

  var joined;
  for (var i = start; i < end; i++) {
    var arg = argArray[i];
    if (!joined) {
      joined = arg;
    }
    else {
      joined = joined.merge(arg);
    }
  }
  return joined;
};

/**
 * For test/debug use only. The output from this function is subject to wanton
 * random change without notice, and should not be relied upon to even exist
 * at some later date.
 */
Object.defineProperty(Argument.prototype, '_summaryJson', {
  get: function() {
    var assignStatus = this.assignment == null ?
            'null' :
            this.assignment.param.name;
    return '<' + this.prefix + ':' + this.text + ':' + this.suffix + '>' +
        ' (a=' + assignStatus + ',' + ' t=' + this.type + ')';
  },
  enumerable: true
});

exports.Argument = Argument;


/**
 * BlankArgument is a marker that the argument wasn't typed but is there to
 * fill a slot. Assignments begin with their arg set to a BlankArgument.
 */
function BlankArgument() {
  this.text = '';
  this.prefix = '';
  this.suffix = '';
}

BlankArgument.prototype = Object.create(Argument.prototype);

BlankArgument.prototype.type = 'BlankArgument';

exports.BlankArgument = BlankArgument;


/**
 * ScriptArgument is a marker that the argument is designed to be JavaScript.
 * It also implements the special rules that spaces after the { or before the
 * } are part of the pre/suffix rather than the content, and that they are
 * never 'blank' so they can be used by Requisition._split() and not raise an
 * ERROR status due to being blank.
 */
function ScriptArgument(text, prefix, suffix) {
  this.text = text !== undefined ? text : '';
  this.prefix = prefix !== undefined ? prefix : '';
  this.suffix = suffix !== undefined ? suffix : '';

  ScriptArgument._moveSpaces(this);
}

ScriptArgument.prototype = Object.create(Argument.prototype);

ScriptArgument.prototype.type = 'ScriptArgument';

/**
 * Private/Dangerous: Alters a ScriptArgument to move the spaces at the start
 * or end of the 'text' into the prefix/suffix. With a string, " a " is 3 chars
 * long, but with a ScriptArgument, { a } is only one char long.
 * Arguments are generally supposed to be immutable, so this method should only
 * be called on a ScriptArgument that isn't exposed to the outside world yet.
 */
ScriptArgument._moveSpaces = function(arg) {
  while (arg.text.charAt(0) === ' ') {
    arg.prefix = arg.prefix + ' ';
    arg.text = arg.text.substring(1);
  }

  while (arg.text.charAt(arg.text.length - 1) === ' ') {
    arg.suffix = ' ' + arg.suffix;
    arg.text = arg.text.slice(0, -1);
  }
};

/**
 * As Argument.beget that implements the space rule documented in the ctor.
 */
ScriptArgument.prototype.beget = function(options) {
  options.type = ScriptArgument;
  var begotten = Argument.prototype.beget.call(this, options);
  ScriptArgument._moveSpaces(begotten);
  return begotten;
};

exports.ScriptArgument = ScriptArgument;


/**
 * Commands like 'echo' with a single string argument, and used with the
 * special format like: 'echo a b c' effectively have a number of arguments
 * merged together.
 */
function MergedArgument(args, start, end) {
  if (!Array.isArray(args)) {
    throw new Error('args is not an array of Arguments');
  }

  if (start === undefined) {
    this.args = args;
  }
  else {
    this.args = args.slice(start, end);
  }

  var arg = Argument.merge(this.args);
  this.text = arg.text;
  this.prefix = arg.prefix;
  this.suffix = arg.suffix;
}

MergedArgument.prototype = Object.create(Argument.prototype);

MergedArgument.prototype.type = 'MergedArgument';

/**
 * Keep track of which assignment we've been assigned to, and allow the
 * original args to do the same.
 */
Object.defineProperty(MergedArgument.prototype, 'assignment', {
  get: function() { return this._assignment; },
  set: function(assignment) {
    this._assignment = assignment;

    this.args.forEach(function(arg) {
      arg.assignment = assignment;
    }, this);
  },
  enumerable: true
});

MergedArgument.prototype.getArgs = function() {
  return this.args;
};

MergedArgument.prototype.equals = function(that) {
  if (this === that) {
    return true;
  }
  if (that == null || !(that instanceof MergedArgument)) {
    return false;
  }

  // We might need to add a check that args is the same here

  return this.text === that.text &&
       this.prefix === that.prefix && this.suffix === that.suffix;
};

exports.MergedArgument = MergedArgument;


/**
 * TrueNamedArguments are for when we have an argument like --verbose which
 * has a boolean value, and thus the opposite of '--verbose' is ''.
 */
function TrueNamedArgument(arg) {
  this.arg = arg;
  this.text = arg.text;
  this.prefix = arg.prefix;
  this.suffix = arg.suffix;
}

TrueNamedArgument.prototype = Object.create(Argument.prototype);

TrueNamedArgument.prototype.type = 'TrueNamedArgument';

Object.defineProperty(TrueNamedArgument.prototype, 'assignment', {
  get: function() { return this._assignment; },
  set: function(assignment) {
    this._assignment = assignment;

    if (this.arg) {
      this.arg.assignment = assignment;
    }
  },
  enumerable: true
});

TrueNamedArgument.prototype.getArgs = function() {
  return [ this.arg ];
};

TrueNamedArgument.prototype.equals = function(that) {
  if (this === that) {
    return true;
  }
  if (that == null || !(that instanceof TrueNamedArgument)) {
    return false;
  }

  return this.text === that.text &&
       this.prefix === that.prefix && this.suffix === that.suffix;
};

/**
 * As Argument.beget that rebuilds nameArg and valueArg
 */
TrueNamedArgument.prototype.beget = function(options) {
  if (options.text) {
    console.error('Can\'t change text of a TrueNamedArgument', this, options);
  }

  options.type = TrueNamedArgument;
  var begotten = Argument.prototype.beget.call(this, options);
  begotten.arg = new Argument(begotten.text, begotten.prefix, begotten.suffix);
  return begotten;
};

exports.TrueNamedArgument = TrueNamedArgument;


/**
 * FalseNamedArguments are for when we don't have an argument like --verbose
 * which has a boolean value, and thus the opposite of '' is '--verbose'.
 */
function FalseNamedArgument() {
  this.text = '';
  this.prefix = '';
  this.suffix = '';
}

FalseNamedArgument.prototype = Object.create(Argument.prototype);

FalseNamedArgument.prototype.type = 'FalseNamedArgument';

FalseNamedArgument.prototype.getArgs = function() {
  return [ ];
};

FalseNamedArgument.prototype.equals = function(that) {
  if (this === that) {
    return true;
  }
  if (that == null || !(that instanceof FalseNamedArgument)) {
    return false;
  }

  return this.text === that.text &&
       this.prefix === that.prefix && this.suffix === that.suffix;
};

exports.FalseNamedArgument = FalseNamedArgument;


/**
 * A named argument is for cases where we have input in one of the following
 * formats:
 * <ul>
 * <li>--param value
 * <li>-p value
 * </ul>
 * We model this as a normal argument but with a long prefix.
 *
 * There are 2 ways to construct a NamedArgument. One using 2 Arguments which
 * are taken to be the argument for the name (e.g. '--param') and one for the
 * value to assign to that parameter.
 * Alternatively, you can pass in the text/prefix/suffix values in the same
 * way as an Argument is constructed. If you do this then you are expected to
 * assign to nameArg and valueArg before exposing the new NamedArgument.
 */
function NamedArgument() {
  if (typeof arguments[0] === 'string') {
    this.nameArg = null;
    this.valueArg = null;
    this.text = arguments[0];
    this.prefix = arguments[1];
    this.suffix = arguments[2];
  }
  else if (arguments[1] == null) {
    this.nameArg = arguments[0];
    this.valueArg = null;
    this.text = '';
    this.prefix = this.nameArg.toString();
    this.suffix = '';
  }
  else {
    this.nameArg = arguments[0];
    this.valueArg = arguments[1];
    this.text = this.valueArg.text;
    this.prefix = this.nameArg.toString() + this.valueArg.prefix;
    this.suffix = this.valueArg.suffix;
  }
}

NamedArgument.prototype = Object.create(Argument.prototype);

NamedArgument.prototype.type = 'NamedArgument';

Object.defineProperty(NamedArgument.prototype, 'assignment', {
  get: function() { return this._assignment; },
  set: function(assignment) {
    this._assignment = assignment;

    this.nameArg.assignment = assignment;
    if (this.valueArg != null) {
      this.valueArg.assignment = assignment;
    }
  },
  enumerable: true
});

NamedArgument.prototype.getArgs = function() {
  return this.valueArg ? [ this.nameArg, this.valueArg ] : [ this.nameArg ];
};

NamedArgument.prototype.equals = function(that) {
  if (this === that) {
    return true;
  }
  if (that == null) {
    return false;
  }

  if (!(that instanceof NamedArgument)) {
    return false;
  }

  // We might need to add a check that nameArg and valueArg are the same

  return this.text === that.text &&
       this.prefix === that.prefix && this.suffix === that.suffix;
};

/**
 * As Argument.beget that rebuilds nameArg and valueArg
 */
NamedArgument.prototype.beget = function(options) {
  options.type = NamedArgument;
  var begotten = Argument.prototype.beget.call(this, options);

  // Cut the prefix into |whitespace|non-whitespace|whitespace+quote so we can
  // rebuild nameArg and valueArg from the parts
  var matches = /^([\s]*)([^\s]*)([\s]*['"]?)$/.exec(begotten.prefix);

  if (this.valueArg == null && begotten.text === '') {
    begotten.nameArg = new Argument(matches[2], matches[1], matches[3]);
    begotten.valueArg = null;
  }
  else {
    begotten.nameArg = new Argument(matches[2], matches[1], '');
    begotten.valueArg = new Argument(begotten.text, matches[3], begotten.suffix);
  }

  return begotten;
};

exports.NamedArgument = NamedArgument;


/**
 * An argument the groups together a number of plain arguments together so they
 * can be jointly assigned to a single array parameter
 */
function ArrayArgument() {
  this.args = [];
}

ArrayArgument.prototype = Object.create(Argument.prototype);

ArrayArgument.prototype.type = 'ArrayArgument';

ArrayArgument.prototype.addArgument = function(arg) {
  this.args.push(arg);
};

ArrayArgument.prototype.addArguments = function(args) {
  Array.prototype.push.apply(this.args, args);
};

ArrayArgument.prototype.getArguments = function() {
  return this.args;
};

Object.defineProperty(ArrayArgument.prototype, 'assignment', {
  get: function() { return this._assignment; },
  set: function(assignment) {
    this._assignment = assignment;

    this.args.forEach(function(arg) {
      arg.assignment = assignment;
    }, this);
  },
  enumerable: true
});

ArrayArgument.prototype.getArgs = function() {
  return this.args;
};

ArrayArgument.prototype.equals = function(that) {
  if (this === that) {
    return true;
  }
  if (that == null) {
    return false;
  }

  if (that.type !== 'ArrayArgument') {
    return false;
  }

  if (this.args.length !== that.args.length) {
    return false;
  }

  for (var i = 0; i < this.args.length; i++) {
    if (!this.args[i].equals(that.args[i])) {
      return false;
    }
  }

  return true;
};

/**
 * Helper when we're putting arguments back together
 */
ArrayArgument.prototype.toString = function() {
  return '{' + this.args.map(function(arg) {
    return arg.toString();
  }, this).join(',') + '}';
};

exports.ArrayArgument = ArrayArgument;

/**
 * Some types can detect validity, that is to say they can distinguish between
 * valid and invalid values.
 * We might want to change these constants to be numbers for better performance
 */
var Status = {
  /**
   * The conversion process worked without any problem, and the value is
   * valid. There are a number of failure states, so the best way to check
   * for failure is (x !== Status.VALID)
   */
  VALID: {
    toString: function() { return 'VALID'; },
    valueOf: function() { return 0; }
  },

  /**
   * A conversion process failed, however it was noted that the string
   * provided to 'parse()' could be VALID by the addition of more characters,
   * so the typing may not be actually incorrect yet, just unfinished.
   * @see Status.ERROR
   */
  INCOMPLETE: {
    toString: function() { return 'INCOMPLETE'; },
    valueOf: function() { return 1; }
  },

  /**
   * The conversion process did not work, the value should be null and a
   * reason for failure should have been provided. In addition some
   * completion values may be available.
   * @see Status.INCOMPLETE
   */
  ERROR: {
    toString: function() { return 'ERROR'; },
    valueOf: function() { return 2; }
  },

  /**
   * A combined status is the worser of the provided statuses. The statuses
   * can be provided either as a set of arguments or a single array
   */
  combine: function() {
    var combined = Status.VALID;
    for (var i = 0; i < arguments.length; i++) {
      var status = arguments[i];
      if (Array.isArray(status)) {
        status = Status.combine.apply(null, status);
      }
      if (status > combined) {
        combined = status;
      }
    }
    return combined;
  },

  fromString: function(str) {
    switch (str) {
      case Status.VALID.toString():
        return Status.VALID;
      case Status.INCOMPLETE.toString():
        return Status.INCOMPLETE;
      case Status.ERROR.toString():
        return Status.ERROR;
      default:
        throw new Error('\'' + str + '\' is not a status');
    }
  }
};

exports.Status = Status;


/**
 * The type.parse() method converts an Argument into a value, Conversion is
 * a wrapper to that value.
 * Conversion is needed to collect a number of properties related to that
 * conversion in one place, i.e. to handle errors and provide traceability.
 * @param value The result of the conversion. null if status == VALID
 * @param arg The data from which the conversion was made
 * @param status See the Status values [VALID|INCOMPLETE|ERROR] defined above.
 * The default status is Status.VALID.
 * @param message If status=ERROR, there should be a message to describe the
 * error. A message is not needed unless for other statuses, but could be
 * present for any status including VALID (in the case where we want to note a
 * warning, for example).
 * See BUG 664676: GCLI conversion error messages should be localized
 * @param predictions If status=INCOMPLETE, there could be predictions as to
 * the options available to complete the input.
 * We generally expect there to be about 7 predictions (to match human list
 * comprehension ability) however it is valid to provide up to about 20,
 * or less. It is the job of the predictor to decide a smart cut-off.
 * For example if there are 4 very good matches and 4 very poor ones,
 * probably only the 4 very good matches should be presented.
 * The predictions are presented either as an array of prediction objects or as
 * a function which returns this array when called with no parameters.
 * Each prediction object has the following shape:
 *     {
 *       name: '...',     // textual completion. i.e. what the cli uses
 *       value: { ... },  // value behind the textual completion
 *       incomplete: true // this completion is only partial (optional)
 *     }
 * The 'incomplete' property could be used to denote a valid completion which
 * could have sub-values (e.g. for tree navigation).
 */
function Conversion(value, arg, status, message, predictions) {
  if (arg == null) {
    throw new Error('Missing arg');
  }

  if (predictions != null && typeof predictions !== 'function' &&
      !Array.isArray(predictions) && typeof predictions.then !== 'function') {
    throw new Error('predictions exists but is not a promise, function or array');
  }

  if (status === Status.ERROR && !message) {
    throw new Error('Conversion has status=ERROR but no message');
  }

  this.value = value;
  this.arg = arg;
  this._status = status || Status.VALID;
  this.message = message;
  this.predictions = predictions;
}

/**
 * Ensure that all arguments that are part of this conversion know what they
 * are assigned to.
 * @param assignment The Assignment (param/conversion link) to inform the
 * argument about.
 */
Object.defineProperty(Conversion.prototype, 'assignment', {
  get: function() { return this.arg.assignment; },
  set: function(assignment) { this.arg.assignment = assignment; },
  enumerable: true
});

/**
 * Work out if there is information provided in the contained argument.
 */
Conversion.prototype.isDataProvided = function() {
  return this.arg.type !== 'BlankArgument';
};

/**
 * 2 conversions are equal if and only if their args are equal (argEquals) and
 * their values are equal (valueEquals).
 * @param that The conversion object to compare against.
 */
Conversion.prototype.equals = function(that) {
  if (this === that) {
    return true;
  }
  if (that == null) {
    return false;
  }
  return this.valueEquals(that) && this.argEquals(that);
};

/**
 * Check that the value in this conversion is strict equal to the value in the
 * provided conversion.
 * @param that The conversion to compare values with
 */
Conversion.prototype.valueEquals = function(that) {
  return that != null && this.value === that.value;
};

/**
 * Check that the argument in this conversion is equal to the value in the
 * provided conversion as defined by the argument (i.e. arg.equals).
 * @param that The conversion to compare arguments with
 */
Conversion.prototype.argEquals = function(that) {
  return that == null ? false : this.arg.equals(that.arg);
};

/**
 * Accessor for the status of this conversion
 */
Conversion.prototype.getStatus = function(arg) {
  return this._status;
};

/**
 * Defined by the toString() value provided by the argument
 */
Conversion.prototype.toString = function() {
  return this.arg.toString();
};

/**
 * If status === INCOMPLETE, then we may be able to provide predictions as to
 * how the argument can be completed.
 * @return An array of items, or a promise of an array of items, where each
 * item is an object with the following properties:
 * - name (mandatory): Displayed to the user, and typed in. No whitespace
 * - description (optional): Short string for display in a tool-tip
 * - manual (optional): Longer description which details usage
 * - incomplete (optional): Indicates that the prediction if used should not
 *   be considered necessarily sufficient, which typically will mean that the
 *   UI should not append a space to the completion
 * - value (optional): If a value property is present, this will be used as the
 *   value of the conversion, otherwise the item itself will be used.
 */
Conversion.prototype.getPredictions = function(context) {
  if (typeof this.predictions === 'function') {
    return this.predictions(context);
  }
  return Promise.resolve(this.predictions || []);
};

/**
 * Return a promise of an index constrained by the available predictions.
 * i.e. (index % predicitons.length)
 * This code can probably be removed when the Firefox developer toolbar isn't
 * needed any more.
 */
Conversion.prototype.constrainPredictionIndex = function(context, index) {
  if (index == null) {
    return Promise.resolve();
  }

  return this.getPredictions(context).then(value => {
    if (value.length === 0) {
      return undefined;
    }

    index = index % value.length;
    if (index < 0) {
      index = value.length + index;
    }
    return index;
  });
};

/**
 * Constant to allow everyone to agree on the maximum number of predictions
 * that should be provided. We actually display 1 less than this number.
 */
Conversion.maxPredictions = 9;

exports.Conversion = Conversion;


/**
 * ArrayConversion is a special Conversion, needed because arrays are converted
 * member by member rather then as a whole, which means we can track the
 * conversion if individual array elements. So an ArrayConversion acts like a
 * normal Conversion (which is needed as Assignment requires a Conversion) but
 * it can also be devolved into a set of Conversions for each array member.
 */
function ArrayConversion(conversions, arg) {
  this.arg = arg;
  this.conversions = conversions;
  this.value = conversions.map(function(conversion) {
    return conversion.value;
  }, this);

  this._status = Status.combine(conversions.map(function(conversion) {
    return conversion.getStatus();
  }));

  // This message is just for reporting errors like "not enough values"
  // rather that for problems with individual values.
  this.message = '';

  // Predictions are generally provided by individual values
  this.predictions = [];
}

ArrayConversion.prototype = Object.create(Conversion.prototype);

Object.defineProperty(ArrayConversion.prototype, 'assignment', {
  get: function() { return this._assignment; },
  set: function(assignment) {
    this._assignment = assignment;

    this.conversions.forEach(function(conversion) {
      conversion.assignment = assignment;
    }, this);
  },
  enumerable: true
});

ArrayConversion.prototype.getStatus = function(arg) {
  if (arg && arg.conversion) {
    return arg.conversion.getStatus();
  }
  return this._status;
};

ArrayConversion.prototype.isDataProvided = function() {
  return this.conversions.length > 0;
};

ArrayConversion.prototype.valueEquals = function(that) {
  if (that == null) {
    return false;
  }

  if (!(that instanceof ArrayConversion)) {
    throw new Error('Can\'t compare values with non ArrayConversion');
  }

  if (this.value === that.value) {
    return true;
  }

  if (this.value.length !== that.value.length) {
    return false;
  }

  for (var i = 0; i < this.conversions.length; i++) {
    if (!this.conversions[i].valueEquals(that.conversions[i])) {
      return false;
    }
  }

  return true;
};

ArrayConversion.prototype.toString = function() {
  return '[ ' + this.conversions.map(function(conversion) {
    return conversion.toString();
  }, this).join(', ') + ' ]';
};

exports.ArrayConversion = ArrayConversion;


/**
 * Most of our types are 'static' e.g. there is only one type of 'string',
 * however some types like 'selection' and 'delegate' are customizable.
 * The basic Type type isn't useful, but does provide documentation about what
 * types do.
 */
function Type() {
}

/**
 * Get a JSONable data structure that entirely describes this type.
 * commandName and paramName are the names of the command and parameter which
 * we are remoting to help the server get back to the remoted action.
 */
Type.prototype.getSpec = function(commandName, paramName) {
  throw new Error('Not implemented');
};

/**
 * Convert the given <tt>value</tt> to a string representation.
 * Where possible, there should be round-tripping between values and their
 * string representations.
 * @param value The object to convert into a string
 * @param context An ExecutionContext to allow basic Requisition access
 */
Type.prototype.stringify = function(value, context) {
  throw new Error('Not implemented');
};

/**
 * Convert the given <tt>arg</tt> to an instance of this type.
 * Where possible, there should be round-tripping between values and their
 * string representations.
 * @param arg An instance of <tt>Argument</tt> to convert.
 * @param context An ExecutionContext to allow basic Requisition access
 * @return Conversion
 */
Type.prototype.parse = function(arg, context) {
  throw new Error('Not implemented');
};

/**
 * A convenience method for times when you don't have an argument to parse
 * but instead have a string.
 * @see #parse(arg)
 */
Type.prototype.parseString = function(str, context) {
  return this.parse(new Argument(str), context);
};

/**
 * The plug-in system, and other things need to know what this type is
 * called. The name alone is not enough to fully specify a type. Types like
 * 'selection' and 'delegate' need extra data, however this function returns
 * only the name, not the extra data.
 */
Type.prototype.name = undefined;

/**
 * If there is some concept of a lower or higher value, return it,
 * otherwise return undefined.
 * @param by number indicating how much to nudge by, usually +1 or -1 which is
 * caused by the user pressing the UP/DOWN keys with the cursor in this type
 */
Type.prototype.nudge = function(value, by, context) {
  return undefined;
};

/**
 * The 'blank value' of most types is 'undefined', but there are exceptions;
 * This allows types to specify a better conversion from empty string than
 * 'undefined'.
 * 2 known examples of this are boolean -> false and array -> []
 */
Type.prototype.getBlank = function(context) {
  return new Conversion(undefined, new BlankArgument(), Status.INCOMPLETE, '');
};

/**
 * This is something of a hack for the benefit of DelegateType which needs to
 * be able to lie about it's type for fields to accept it as one of their own.
 * Sub-types can ignore this unless they're DelegateType.
 * @param context An ExecutionContext to allow basic Requisition access
 */
Type.prototype.getType = function(context) {
  return this;
};

/**
 * addItems allows registrations of a number of things. This allows it to know
 * what type of item, and how it should be registered.
 */
Type.prototype.item = 'type';

exports.Type = Type;

/**
 * 'Types' represents a registry of types
 */
function Types() {
  // Invariant: types[name] = type.name
  this._registered = {};
}

exports.Types = Types;

/**
 * Get an array of the names of registered types
 */
Types.prototype.getTypeNames = function() {
  return Object.keys(this._registered);
};

/**
 * Add a new type to the list available to the system.
 * You can pass 2 things to this function - either an instance of Type, in
 * which case we return this instance when #getType() is called with a 'name'
 * that matches type.name.
 * Also you can pass in a constructor (i.e. function) in which case when
 * #getType() is called with a 'name' that matches Type.prototype.name we will
 * pass the typeSpec into this constructor.
 */
Types.prototype.add = function(type) {
  if (typeof type === 'object') {
    if (!type.name) {
      throw new Error('All registered types must have a name');
    }

    if (type instanceof Type) {
      this._registered[type.name] = type;
    }
    else {
      var name = type.name;
      var parent = type.parent;
      type.name = parent;
      delete type.parent;

      this._registered[name] = this.createType(type);

      type.name = name;
      type.parent = parent;
    }
  }
  else if (typeof type === 'function') {
    if (!type.prototype.name) {
      throw new Error('All registered types must have a name');
    }
    this._registered[type.prototype.name] = type;
  }
  else {
    throw new Error('Unknown type: ' + type);
  }
};

/**
 * Remove a type from the list available to the system
 */
Types.prototype.remove = function(type) {
  delete this._registered[type.name];
};

/**
 * Find a previously registered type
 */
Types.prototype.createType = function(typeSpec) {
  if (typeof typeSpec === 'string') {
    typeSpec = { name: typeSpec };
  }

  if (typeof typeSpec !== 'object') {
    throw new Error('Can\'t extract type from ' + typeSpec);
  }

  var NewTypeCtor, newType;
  if (typeSpec.name == null || typeSpec.name == 'type') {
    NewTypeCtor = Type;
  }
  else {
    NewTypeCtor = this._registered[typeSpec.name];
  }

  if (!NewTypeCtor) {
    console.error('Known types: ' + Object.keys(this._registered).join(', '));
    throw new Error('Unknown type: \'' + typeSpec.name + '\'');
  }

  if (typeof NewTypeCtor === 'function') {
    newType = new NewTypeCtor(typeSpec);
  }
  else {
    // clone 'type'
    newType = {};
    util.copyProperties(NewTypeCtor, newType);
  }

  // Copy the properties of typeSpec onto the new type
  util.copyProperties(typeSpec, newType);

  // Several types need special powers to create child types
  newType.types = this;

  if (typeof NewTypeCtor !== 'function') {
    if (typeof newType.constructor === 'function') {
      newType.constructor();
    }
  }

  return newType;
};
PK
!<�8��NNKchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/types/union.js/*
 * Copyright 2014, Mozilla Foundation and contributors
 *
 * 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';

var l10n = require('../util/l10n');
var Conversion = require('./types').Conversion;
var Status = require('./types').Status;

exports.items = [
  {
    // The union type allows for a combination of different parameter types.
    item: 'type',
    name: 'union',
    hasPredictions: true,

    constructor: function() {
      // Get the properties of the type. Later types in the list should always
      // be more general, so 'catch all' types like string must be last
      this.alternatives = this.alternatives.map(typeData => {
        return this.types.createType(typeData);
      });
    },

    getSpec: function(command, param) {
      var spec = { name: 'union', alternatives: [] };
      this.alternatives.forEach(type => {
        spec.alternatives.push(type.getSpec(command, param));
      });
      return spec;
    },

    stringify: function(value, context) {
      if (value == null) {
        return '';
      }

      var type = this.alternatives.find(function(typeData) {
        return typeData.name === value.type;
      });

      return type.stringify(value[value.type], context);
    },

    parse: function(arg, context) {
      var conversionPromises = this.alternatives.map(type => {
        return type.parse(arg, context);
      });

      return Promise.all(conversionPromises).then(conversions => {
        // Find a list of the predictions made by any conversion
        var predictionPromises = conversions.map(conversion => {
          return conversion.getPredictions(context);
        });

        return Promise.all(predictionPromises).then(allPredictions => {
          // Take one prediction from each set of predictions, ignoring
          // duplicates, until we've got up to Conversion.maxPredictions
          var maxIndex = allPredictions.reduce((prev, prediction) => {
            return Math.max(prev, prediction.length);
          }, 0);
          var predictions = [];

          indexLoop:
          for (var index = 0; index < maxIndex; index++) {
            for (var p = 0; p <= allPredictions.length; p++) {
              if (predictions.length >= Conversion.maxPredictions) {
                break indexLoop;
              }

              if (allPredictions[p] != null) {
                var prediction = allPredictions[p][index];
                if (prediction != null && !predictions.includes(prediction)) {
                  predictions.push(prediction);
                }
              }
            }
          }

          var bestStatus = Status.ERROR;
          var value;
          for (var i = 0; i < conversions.length; i++) {
            var conversion = conversions[i];
            var thisStatus = conversion.getStatus(arg);
            if (thisStatus < bestStatus) {
              bestStatus = thisStatus;
            }
            if (bestStatus === Status.VALID) {
              var type = this.alternatives[i].name;
              value = { type: type };
              value[type] = conversion.value;
              break;
            }
          }

          var msg = (bestStatus === Status.VALID) ?
                    '' :
                    l10n.lookupFormat('typesSelectionNomatch', [ arg.text ]);
          return new Conversion(value, arg, bestStatus, msg, predictions);
        });
      });
    },
  }
];
PK
!<�i�j-	-	Ichrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/types/url.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var host = require('../util/host');
var Status = require('./types').Status;
var Conversion = require('./types').Conversion;

exports.items = [
  {
    item: 'type',
    name: 'url',

    getSpec: function() {
      return 'url';
    },

    stringify: function(value, context) {
      if (value == null) {
        return '';
      }
      return value.href;
    },

    parse: function(arg, context) {
      var conversion;

      try {
        var url = host.createUrl(arg.text);
        conversion = new Conversion(url, arg);
      }
      catch (ex) {
        var predictions = [];
        var status = Status.ERROR;

        // Maybe the URL was missing a scheme?
        if (!arg.text.includes('://')) {
          [ 'http', 'https' ].forEach(scheme => {
            try {
              var http = host.createUrl(scheme + '://' + arg.text);
              predictions.push({ name: http.href, value: http });
            }
            catch (ex) {
              // Ignore
            }
          });

          // Try to create a URL with the current page as a base ref
          if ('window' in context.environment) {
            try {
              var base = context.environment.window.location.href;
              var localized = host.createUrl(arg.text, base);
              predictions.push({ name: localized.href, value: localized });
            }
            catch (ex) {
              // Ignore
            }
          }
        }

        if (predictions.length > 0) {
          status = Status.INCOMPLETE;
        }

        conversion = new Conversion(undefined, arg, status,
                                    ex.message, predictions);
      }

      return Promise.resolve(conversion);
    }
  }
];
PK
!<�o*o*Hchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/ui/focus.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var util = require('../util/util');
var l10n = require('../util/l10n');

/**
 * Record how much help the user wants from the tooltip
 */
var Eagerness = {
  NEVER: 1,
  SOMETIMES: 2,
  ALWAYS: 3
};

/**
 * Export the eagerHelper setting
 */
exports.items = [
  {
    item: 'setting',
    name: 'eagerHelper',
    type: {
      name: 'selection',
      lookup: [
        { name: 'never', value: Eagerness.NEVER },
        { name: 'sometimes', value: Eagerness.SOMETIMES },
        { name: 'always', value: Eagerness.ALWAYS }
      ]
    },
    defaultValue: Eagerness.SOMETIMES,
    description: l10n.lookup('eagerHelperDesc'),
    ignoreTypeDifference: true
  }
];

/**
 * FocusManager solves the problem of tracking focus among a set of nodes.
 * The specific problem we are solving is when the hint element must be visible
 * if either the command line or any of the inputs in the hint element has the
 * focus, and invisible at other times, without hiding and showing the hint
 * element even briefly as the focus changes between them.
 * It does this simply by postponing the hide events by 250ms to see if
 * something else takes focus.
 */
function FocusManager(document, settings) {
  if (document == null) {
    throw new Error('document == null');
  }

  this.document = document;
  this.settings = settings;
  this.debug = false;
  this.blurDelay = 150;
  this.window = this.document.defaultView;

  this._blurDelayTimeout = null; // Result of setTimeout in delaying a blur
  this._monitoredElements = [];  // See addMonitoredElement()

  this._isError = false;
  this._hasFocus = false;
  this._helpRequested = false;
  this._recentOutput = false;

  this.onVisibilityChange = util.createEvent('FocusManager.onVisibilityChange');

  this._focused = this._focused.bind(this);
  if (this.document.addEventListener) {
    this.document.addEventListener('focus', this._focused, true);
  }

  var eagerHelper = this.settings.get('eagerHelper');
  eagerHelper.onChange.add(this._eagerHelperChanged, this);

  this.isTooltipVisible = undefined;
  this.isOutputVisible = undefined;
  this._checkShow();
}

/**
 * Avoid memory leaks
 */
FocusManager.prototype.destroy = function() {
  var eagerHelper = this.settings.get('eagerHelper');
  eagerHelper.onChange.remove(this._eagerHelperChanged, this);

  this.document.removeEventListener('focus', this._focused, true);

  for (var i = 0; i < this._monitoredElements.length; i++) {
    var monitor = this._monitoredElements[i];
    console.error('Hanging monitored element: ', monitor.element);

    monitor.element.removeEventListener('focus', monitor.onFocus, true);
    monitor.element.removeEventListener('blur', monitor.onBlur, true);
  }

  if (this._blurDelayTimeout) {
    this.window.clearTimeout(this._blurDelayTimeout);
    this._blurDelayTimeout = null;
  }

  this._focused = undefined;
  this.document = undefined;
  this.settings = undefined;
  this.window = undefined;
};

/**
 * The easy way to include an element in the set of things that are part of the
 * aggregate focus. Using [add|remove]MonitoredElement() is a simpler way of
 * option than calling report[Focus|Blur]()
 * @param element The element on which to track focus|blur events
 * @param where Optional source string for debugging only
 */
FocusManager.prototype.addMonitoredElement = function(element, where) {
  if (this.debug) {
    console.log('FocusManager.addMonitoredElement(' + (where || 'unknown') + ')');
  }

  var monitor = {
    element: element,
    where: where,
    onFocus: () => { this._reportFocus(where); },
    onBlur: () => { this._reportBlur(where); }
  };

  element.addEventListener('focus', monitor.onFocus, true);
  element.addEventListener('blur', monitor.onBlur, true);

  if (this.document.activeElement === element) {
    this._reportFocus(where);
  }

  this._monitoredElements.push(monitor);
};

/**
 * Undo the effects of addMonitoredElement()
 * @param element The element to stop tracking
 * @param where Optional source string for debugging only
 */
FocusManager.prototype.removeMonitoredElement = function(element, where) {
  if (this.debug) {
    console.log('FocusManager.removeMonitoredElement(' + (where || 'unknown') + ')');
  }

  this._monitoredElements = this._monitoredElements.filter(function(monitor) {
    if (monitor.element === element) {
      element.removeEventListener('focus', monitor.onFocus, true);
      element.removeEventListener('blur', monitor.onBlur, true);
      return false;
    }
    return true;
  });
};

/**
 * Monitor for new command executions
 */
FocusManager.prototype.updatePosition = function(dimensions) {
  var ev = {
    tooltipVisible: this.isTooltipVisible,
    outputVisible: this.isOutputVisible,
    dimensions: dimensions
  };
  this.onVisibilityChange(ev);
};

/**
 * Monitor for new command executions
 */
FocusManager.prototype.outputted = function() {
  this._recentOutput = true;
  this._helpRequested = false;
  this._checkShow();
};

/**
 * We take a focus event anywhere to be an indication that we might be about
 * to lose focus
 */
FocusManager.prototype._focused = function() {
  this._reportBlur('document');
};

/**
 * Some component has received a 'focus' event. This sets the internal status
 * straight away and informs the listeners
 * @param where Optional source string for debugging only
 */
FocusManager.prototype._reportFocus = function(where) {
  if (this.debug) {
    console.log('FocusManager._reportFocus(' + (where || 'unknown') + ')');
  }

  if (this._blurDelayTimeout) {
    if (this.debug) {
      console.log('FocusManager.cancelBlur');
    }
    this.window.clearTimeout(this._blurDelayTimeout);
    this._blurDelayTimeout = null;
  }

  if (!this._hasFocus) {
    this._hasFocus = true;
  }
  this._checkShow();
};

/**
 * Some component has received a 'blur' event. This waits for a while to see if
 * we are going to get any subsequent 'focus' events and then sets the internal
 * status and informs the listeners
 * @param where Optional source string for debugging only
 */
FocusManager.prototype._reportBlur = function(where) {
  if (this.debug) {
    console.log('FocusManager._reportBlur(' + where + ')');
  }

  if (this._hasFocus) {
    if (this._blurDelayTimeout) {
      if (this.debug) {
        console.log('FocusManager.blurPending');
      }
      return;
    }

    this._blurDelayTimeout = this.window.setTimeout(() => {
      if (this.debug) {
        console.log('FocusManager.blur');
      }
      this._hasFocus = false;
      this._checkShow();
      this._blurDelayTimeout = null;
    }, this.blurDelay);
  }
};

/**
 * The setting has changed
 */
FocusManager.prototype._eagerHelperChanged = function() {
  this._checkShow();
};

/**
 * The terminal tells us about keyboard events so we can decide to delay
 * showing the tooltip element
 */
FocusManager.prototype.onInputChange = function() {
  this._recentOutput = false;
  this._checkShow();
};

/**
 * Generally called for something like a F1 key press, when the user explicitly
 * wants help
 */
FocusManager.prototype.helpRequest = function() {
  if (this.debug) {
    console.log('FocusManager.helpRequest');
  }

  this._helpRequested = true;
  this._recentOutput = false;
  this._checkShow();
};

/**
 * Generally called for something like a ESC key press, when the user explicitly
 * wants to get rid of the help
 */
FocusManager.prototype.removeHelp = function() {
  if (this.debug) {
    console.log('FocusManager.removeHelp');
  }

  this._importantFieldFlag = false;
  this._isError = false;
  this._helpRequested = false;
  this._recentOutput = false;
  this._checkShow();
};

/**
 * Set to true whenever a field thinks it's output is important
 */
FocusManager.prototype.setImportantFieldFlag = function(flag) {
  if (this.debug) {
    console.log('FocusManager.setImportantFieldFlag', flag);
  }
  this._importantFieldFlag = flag;
  this._checkShow();
};

/**
 * Set to true whenever a field thinks it's output is important
 */
FocusManager.prototype.setError = function(isError) {
  if (this.debug) {
    console.log('FocusManager._isError', isError);
  }
  this._isError = isError;
  this._checkShow();
};

/**
 * Helper to compare the current showing state with the value calculated by
 * _shouldShow() and take appropriate action
 */
FocusManager.prototype._checkShow = function() {
  var fire = false;
  var ev = {
    tooltipVisible: this.isTooltipVisible,
    outputVisible: this.isOutputVisible
  };

  var showTooltip = this._shouldShowTooltip();
  if (this.isTooltipVisible !== showTooltip.visible) {
    ev.tooltipVisible = this.isTooltipVisible = showTooltip.visible;
    fire = true;
  }

  var showOutput = this._shouldShowOutput();
  if (this.isOutputVisible !== showOutput.visible) {
    ev.outputVisible = this.isOutputVisible = showOutput.visible;
    fire = true;
  }

  if (fire) {
    if (this.debug) {
      console.log('FocusManager.onVisibilityChange', ev);
    }
    this.onVisibilityChange(ev);
  }
};

/**
 * Calculate if we should be showing or hidden taking into account all the
 * available inputs
 */
FocusManager.prototype._shouldShowTooltip = function() {
  var eagerHelper = this.settings.get('eagerHelper');
  if (eagerHelper.value === Eagerness.NEVER) {
    return { visible: false, reason: 'eagerHelperNever' };
  }

  if (eagerHelper.value === Eagerness.ALWAYS) {
    return { visible: true, reason: 'eagerHelperAlways' };
  }

  if (!this._hasFocus) {
    return { visible: false, reason: 'notHasFocus' };
  }

  if (this._isError) {
    return { visible: true, reason: 'isError' };
  }

  if (this._helpRequested) {
    return { visible: true, reason: 'helpRequested' };
  }

  if (this._importantFieldFlag) {
    return { visible: true, reason: 'importantFieldFlag' };
  }

  return { visible: false, reason: 'default' };
};

/**
 * Calculate if we should be showing or hidden taking into account all the
 * available inputs
 */
FocusManager.prototype._shouldShowOutput = function() {
  if (!this._hasFocus) {
    return { visible: false, reason: 'notHasFocus' };
  }

  if (this._recentOutput) {
    return { visible: true, reason: 'recentOutput' };
  }

  return { visible: false, reason: 'default' };
};

exports.FocusManager = FocusManager;
PK
!<�Ǵ���Jchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/ui/history.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

/**
 * A History object remembers commands that have been entered in the past and
 * provides an API for accessing them again.
 * See Bug 681340: Search through history (like C-r in bash)?
 */
function History() {
  // This is the actual buffer where previous commands are kept.
  // 'this._buffer[0]' should always be equal the empty string. This is so
  // that when you try to go in to the "future", you will just get an empty
  // command.
  this._buffer = [''];

  // This is an index in to the history buffer which points to where we
  // currently are in the history.
  this._current = 0;
}

/**
 * Avoid memory leaks
 */
History.prototype.destroy = function() {
  this._buffer = undefined;
};

/**
 * Record and save a new command in the history.
 */
History.prototype.add = function(command) {
  this._buffer.splice(1, 0, command);
  this._current = 0;
};

/**
 * Get the next (newer) command from history.
 */
History.prototype.forward = function() {
  if (this._current > 0 ) {
    this._current--;
  }
  return this._buffer[this._current];
};

/**
 * Get the previous (older) item from history.
 */
History.prototype.backward = function() {
  if (this._current < this._buffer.length - 1) {
    this._current++;
  }
  return this._buffer[this._current];
};

exports.History = History;
PK
!<����
�
Hchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/ui/intro.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var l10n = require('../util/l10n');
var Output = require('../cli').Output;
var view = require('./view');

/**
 * Record if the user has clicked on 'Got It!'
 */
exports.items = [
  {
    item: 'setting',
    name: 'hideIntro',
    type: 'boolean',
    description: l10n.lookup('hideIntroDesc'),
    defaultValue: false
  }
];

/**
 * Called when the UI is ready to add a welcome message to the output
 */
exports.maybeShowIntro = function (commandOutputManager, conversionContext,
                                   outputPanel) {
  var hideIntro = conversionContext.system.settings.get('hideIntro');
  if (hideIntro.value) {
    return;
  }

  var output = new Output(conversionContext);
  output.type = 'view';
  commandOutputManager.onOutput({ output: output });

  var viewData = this.createView(null, conversionContext, true, outputPanel);

  output.complete({ isTypedData: true, type: 'view', data: viewData });
};

/**
 * Called when the UI is ready to add a welcome message to the output
 */
exports.createView = function (ignoreArgs, conversionContext, showHideButton,
                               outputPanel) {
  return view.createView({
    html:
      '<div save="${mainDiv}">\n' +
      '  <p>${l10n.introTextOpening3}</p>\n' +
      '\n' +
      '  <p>\n' +
      '    ${l10n.introTextCommands}\n' +
      '    <span class="gcli-out-shortcut" onclick="${onclick}"\n' +
      '        ondblclick="${ondblclick}"\n' +
      '        data-command="help">help</span>${l10n.introTextKeys2}\n' +
      '    <code>${l10n.introTextF1Escape}</code>.\n' +
      '  </p>\n' +
      '\n' +
      '  <button onclick="${onGotIt}"\n' +
      '      if="${showHideButton}">${l10n.introTextGo}</button>\n' +
      '</div>',
    options: { stack: 'intro.html' },
    data: {
      l10n: l10n.propertyLookup,
      onclick: conversionContext.update,
      ondblclick: conversionContext.updateExec,
      showHideButton: showHideButton,
      onGotIt: function(ev) {
        var settings = conversionContext.system.settings;
        var hideIntro = settings.get('hideIntro');
        hideIntro.value = true;
        outputPanel.remove();
      }
    }
  });
};
PK
!<DD�,,Hchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/ui/menu.css
.gcli-menu {
  overflow: hidden;
  font-size: 90%;
}

.gcli-menu:not(:first-of-type) {
  padding-top: 5px;
}

.gcli-menu-vert {
  white-space: nowrap;
  max-width: 22em;
  display: inline-flex;
  padding-inline-end: 20px;
  -webkit-padding-end: 20px;
}

.gcli-menu-names {
  white-space: nowrap;
  flex-grow: 0;
  flex-shrink: 0;
}

.gcli-menu-descs {
  flex-grow: 1;
  flex-shrink: 1;
}

.gcli-menu-name,
.gcli-menu-desc {
  white-space: nowrap;
}

.gcli-menu-name {
  padding-inline-start: 2px;
  -webkit-padding-start: 2px;
  padding-inline-end: 8px;
  -webkit-padding-end: 8px;
}

.gcli-menu-desc {
  padding-inline-end: 2px;
  -webkit-padding-end: 2px;
  color: #777;
  text-overflow: ellipsis;
  overflow: hidden;
}

.gcli-menu-name:hover,
.gcli-menu-desc:hover {
  background-color: rgba(0, 0, 0, 0.05);
}

.gcli-menu-highlight,
.gcli-menu-highlight.gcli-menu-option:hover {
  background-color: rgba(0, 0, 0, 0.1);
}

.gcli-menu-typed {
  color: #FF6600;
}

.gcli-menu-more {
  font-size: 80%;
  width: 8em;
  display: inline-flex;
  vertical-align: bottom;
}
PK
!<4?���Ichrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/ui/menu.html
<div>
  <div class="gcli-menu-template" aria-live="polite">
    <div class="gcli-menu-names">
      <div class="gcli-menu-name"
          foreach="item in ${items}"
          data-name="${item.name}"
          onclick="${onItemClickInternal}"
          title="${item.manual}">${item.highlight}</div>
    </div>
    <div class="gcli-menu-descs">
      <div class="gcli-menu-desc"
          foreach="item in ${items}"
          data-name="${item.name}"
          onclick="${onItemClickInternal}"
          title="${item.manual}">${item.description}</div>
    </div>
  </div>
  <div class="gcli-menu-more" if="${hasMore}">${l10n.fieldMenuMore}</div>
</div>
PK
!<�(�M!M!Gchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/ui/menu.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var util = require('../util/util');
var l10n = require('../util/l10n');
var domtemplate = require('../util/domtemplate');
var host = require('../util/host');

/**
 * Shared promises for loading resource files
 */
var menuCssPromise;
var menuHtmlPromise;

/**
 * Menu is a display of the commands that are possible given the state of a
 * requisition.
 * @param options A way to customize the menu display.
 * - document: The document to use in creating widgets
 * - maxPredictions (default=8): The maximum predictions to show at one time
 *     If more are requested, a message will be displayed asking the user to
 *     continue typing to narrow the list of options
 */
function Menu(options) {
  options = options || {};
  this.document = options.document || document;
  this.maxPredictions = options.maxPredictions || 8;

  // Keep track of any highlighted items
  this._choice = null;

  // FF can be really hard to debug if doc is null, so we check early on
  if (!this.document) {
    throw new Error('No document');
  }

  this.element =  util.createElement(this.document, 'div');
  this.element.classList.add('gcli-menu');

  if (menuCssPromise == null) {
    menuCssPromise = host.staticRequire(module, './menu.css');
  }
  menuCssPromise.then(menuCss => {
    // Pull the HTML into the DOM, but don't add it to the document
    if (menuCss != null) {
      util.importCss(menuCss, this.document, 'gcli-menu');
    }
  }, console.error);

  this.templateOptions = { blankNullUndefined: true, stack: 'menu.html' };
  if (menuHtmlPromise == null) {
    menuHtmlPromise = host.staticRequire(module, './menu.html');
  }
  menuHtmlPromise.then(menuHtml => {
    if (this.document == null) {
      return; // destroy() has been called
    }

    this.template = host.toDom(this.document, menuHtml);
  }, console.error);

  // Contains the items that should be displayed
  this.items = [];

  this.onItemClick = util.createEvent('Menu.onItemClick');
}

/**
 * Allow the template engine to get at localization strings
 */
Menu.prototype.l10n = l10n.propertyLookup;

/**
 * Avoid memory leaks
 */
Menu.prototype.destroy = function() {
  this.element = undefined;
  this.template = undefined;
  this.document = undefined;
  this.items = undefined;
};

/**
 * The default is to do nothing when someone clicks on the menu.
 * This is called from template.html
 * @param ev The click event from the browser
 */
Menu.prototype.onItemClickInternal = function(ev) {
  var name = ev.currentTarget.getAttribute('data-name');
  if (!name) {
    var named = ev.currentTarget.querySelector('[data-name]');
    name = named.getAttribute('data-name');
  }
  this.onItemClick({ name: name });
};

/**
 * Act as though someone clicked on the selected item
 */
Menu.prototype.clickSelected = function() {
  this.onItemClick({ name: this.selected });
};

/**
 * What is the currently selected item?
 */
Object.defineProperty(Menu.prototype, 'isSelected', {
  get: function() {
    return this.selected != null;
  },
  enumerable: true
});

/**
 * What is the currently selected item?
 */
Object.defineProperty(Menu.prototype, 'selected', {
  get: function() {
    var item = this.element.querySelector('.gcli-menu-name.gcli-menu-highlight');
    if (!item) {
      return null;
    }
    return item.textContent;
  },
  enumerable: true
});

/**
 * Display a number of items in the menu (or hide the menu if there is nothing
 * to display)
 * @param items The items to show in the menu
 * @param match Matching text to highlight in the output
 */
Menu.prototype.show = function(items, match) {
  // If the HTML hasn't loaded yet then just don't show a menu
  if (this.template == null) {
    return;
  }

  this.items = items.filter(item => {
    return item.hidden === undefined || item.hidden !== true;
  });

  this.items = this.items.map(item => {
    return getHighlightingProxy(item, match, this.template.ownerDocument);
  });

  if (this.items.length === 0) {
    this.element.style.display = 'none';
    return;
  }

  if (this.items.length >= this.maxPredictions) {
    this.items.splice(-1);
    this.hasMore = true;
  }
  else {
    this.hasMore = false;
  }

  var options = this.template.cloneNode(true);
  domtemplate.template(options, this, this.templateOptions);

  util.clearElement(this.element);
  this.element.appendChild(options);

  this.element.style.display = 'block';
};

var MAX_ITEMS = 3;

/**
 * Takes an array of items and cuts it into an array of arrays to help us
 * to place the items into columns.
 * The inner arrays will have at most MAX_ITEMS in them, with the number of
 * outer arrays expanding to accommodate.
 */
Object.defineProperty(Menu.prototype, 'itemsSubdivided', {
  get: function() {
    var reply = [];

    var taken = 0;
    while (taken < this.items.length) {
      reply.push(this.items.slice(taken, taken + MAX_ITEMS));
      taken += MAX_ITEMS;
    }

    return reply;
  },
  enumerable: true
});

/**
 * Create a proxy around an item that highlights matching text
 */
function getHighlightingProxy(item, match, document) {
  var proxy = {};
  Object.defineProperties(proxy, {
    highlight: {
      get: function() {
        if (!match) {
          return item.name;
        }

        var value = item.name;
        var startMatch = value.indexOf(match);
        if (startMatch === -1) {
          return value;
        }

        var before = value.substr(0, startMatch);
        var after = value.substr(startMatch + match.length);
        var parent = util.createElement(document, 'span');
        parent.appendChild(document.createTextNode(before));
        var highlight = util.createElement(document, 'span');
        highlight.classList.add('gcli-menu-typed');
        highlight.appendChild(document.createTextNode(match));
        parent.appendChild(highlight);
        parent.appendChild(document.createTextNode(after));
        return parent;
      },
      enumerable: true
    },

    name: {
      value: item.name,
      enumerable: true
    },

    manual: {
      value: item.manual,
      enumerable: true
    },

    description: {
      value: item.description,
      enumerable: true
    }
  });
  return proxy;
}

/**
 * @return {int} current choice index
 */
Menu.prototype.getChoiceIndex = function() {
  return this._choice == null ? 0 : this._choice;
};

/**
 * Highlight the next (for by=1) or previous (for by=-1) option
 */
Menu.prototype.nudgeChoice = function(by) {
  if (this._choice == null) {
    this._choice = 0;
  }

  // There's an annoying up is down thing here, the menu is presented
  // with the zeroth index at the top working down, so the UP arrow needs
  // pick the choice below because we're working down
  this._choice -= by;
  this._updateHighlight();
};

/**
 * Highlight nothing
 */
Menu.prototype.unsetChoice = function() {
  this._choice = null;
  this._updateHighlight();
};

/**
 * Internal option to update the currently highlighted option
 */
Menu.prototype._updateHighlight = function() {
  var names = this.element.querySelectorAll('.gcli-menu-name');
  var descs = this.element.querySelectorAll('.gcli-menu-desc');
  for (var i = 0; i < names.length; i++) {
    names[i].classList.remove('gcli-menu-highlight');
  }
  for (i = 0; i < descs.length; i++) {
    descs[i].classList.remove('gcli-menu-highlight');
  }

  if (this._choice == null || names.length === 0) {
    return;
  }

  var index = this._choice % names.length;
  if (index < 0) {
    index = names.length + index;
  }

  names.item(index).classList.add('gcli-menu-highlight');
  descs.item(index).classList.add('gcli-menu-highlight');
};

/**
 * Hide the menu
 */
Menu.prototype.hide = function() {
  this.element.style.display = 'none';
};

/**
 * Change how much vertical space this menu can take up
 */
Menu.prototype.setMaxHeight = function(height) {
  this.element.style.maxHeight = height + 'px';
};

exports.Menu = Menu;
PK
!<��3���Gchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/ui/view.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var util = require('../util/util');
var host = require('../util/host');
var domtemplate = require('../util/domtemplate');


/**
 * We want to avoid commands having to create DOM structures because that's
 * messy and because we're going to need to have command output displayed in
 * different documents. A View is a way to wrap an HTML template (for
 * domtemplate) in with the data and options to render the template, so anyone
 * can later run the template in the context of any document.
 * View also cuts out a chunk of boiler place code.
 * @param options The information needed to create the DOM from HTML. Includes:
 * - html (required): The HTML source, probably from a call to require
 * - options (default={}): The domtemplate options. See domtemplate for details
 * - data (default={}): The data to domtemplate. See domtemplate for details.
 * - css (default=none): Some CSS to be added to the final document. If 'css'
 *   is used, use of cssId is strongly recommended.
 * - cssId (default=none): An ID to prevent multiple CSS additions. See
 *   util.importCss for more details.
 * @return An object containing a single function 'appendTo()' which runs the
 * template adding the result to the specified element. Takes 2 parameters:
 * - element (required): the element to add to
 * - clear (default=false): if clear===true then remove all pre-existing
 *   children of 'element' before appending the results of this template.
 */
exports.createView = function(options) {
  if (options.html == null) {
    throw new Error('options.html is missing');
  }

  return {
    /**
     * RTTI. Yeah.
     */
    isView: true,

    /**
     * Run the template against the document to which element belongs.
     * @param element The element to append the result to
     * @param clear Set clear===true to remove all children of element
     */
    appendTo: function(element, clear) {
      // Strict check on the off-chance that we later think of other options
      // and want to replace 'clear' with an 'options' parameter, but want to
      // support backwards compat.
      if (clear === true) {
        util.clearElement(element);
      }

      element.appendChild(this.toDom(element.ownerDocument));
    },

    /**
     * Actually convert the view data into a DOM suitable to be appended to
     * an element
     * @param document to use in realizing the template
     */
    toDom: function(document) {
      if (options.css) {
        util.importCss(options.css, document, options.cssId);
      }

      var child = host.toDom(document, options.html);
      domtemplate.template(child, options.data || {}, options.options || {});
      return child;
    }
  };
};
PK
!<�bE���Pchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/util/domtemplate.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var {template} = require("devtools/shared/gcli/templater");
exports.template = template;
PK
!<|�#�#Ochrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/util/fileparser.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var util = require('./util');
var l10n = require('./l10n');
var spell = require('./spell');
var filesystem = require('./filesystem');
var Status = require('../types/types').Status;

/*
 * An implementation of the functions that call the filesystem, designed to
 * support the file type.
 */

/**
 * Helper for the parse() function from the file type.
 * See gcli/util/filesystem.js for details
 */
exports.parse = function(context, typed, options) {
  return filesystem.stat(typed).then(function(stats) {
    // The 'save-as' case - the path should not exist but does
    if (options.existing === 'no' && stats.exists) {
      return {
        value: undefined,
        status: Status.INCOMPLETE,
        message: l10n.lookupFormat('fileErrExists', [ typed ]),
        predictor: undefined // No predictions that we can give here
      };
    }

    if (stats.exists) {
      // The path exists - check it's the correct file type ...
      if (options.filetype === 'file' && !stats.isFile) {
        return {
          value: undefined,
          status: Status.INCOMPLETE,
          message: l10n.lookupFormat('fileErrIsNotFile', [ typed ]),
          predictor: getPredictor(typed, options)
        };
      }

      if (options.filetype === 'directory' && !stats.isDir) {
        return {
          value: undefined,
          status: Status.INCOMPLETE,
          message: l10n.lookupFormat('fileErrIsNotDirectory', [ typed ]),
          predictor: getPredictor(typed, options)
        };
      }

      // ... and that it matches any 'match' RegExp
      if (options.matches != null && !options.matches.test(typed)) {
        return {
          value: undefined,
          status: Status.INCOMPLETE,
          message: l10n.lookupFormat('fileErrDoesntMatch',
                                     [ typed, options.source ]),
          predictor: getPredictor(typed, options)
        };
      }
    }
    else {
      if (options.existing === 'yes') {
        // We wanted something that exists, but it doesn't. But we don't know
        // if the path so far is an ERROR or just INCOMPLETE
        var parentName = filesystem.dirname(typed);
        return filesystem.stat(parentName).then(function(stats) {
          return {
            value: undefined,
            status: stats.isDir ? Status.INCOMPLETE : Status.ERROR,
            message: l10n.lookupFormat('fileErrNotExists', [ typed ]),
            predictor: getPredictor(typed, options)
          };
        });
      }
    }

    // We found no problems
    return {
      value: typed,
      status: Status.VALID,
      message: undefined,
      predictor: getPredictor(typed, options)
    };
  });
};

var RANK_OPTIONS = { noSort: true, prefixZero: true };

/**
 * We want to be able to turn predictions off in Firefox
 */
exports.supportsPredictions = false;

/**
 * Get a function which creates predictions of files that match the given
 * path
 */
function getPredictor(typed, options) {
  if (!exports.supportsPredictions) {
    return undefined;
  }

  return function() {
    var allowFile = (options.filetype !== 'directory');
    var parts = filesystem.split(typed);

    var absolute = (typed.indexOf('/') === 0);
    var roots;
    if (absolute) {
      roots = [ { name: '/', dist: 0, original: '/' } ];
    }
    else {
      roots = dirHistory.getCommonDirectories().map(function(root) {
        return { name: root, dist: 0, original: root };
      });
    }

    // Add each part of the typed pathname onto each of the roots in turn,
    // Finding options from each of those paths, and using these options as
    // our roots for the next part
    var partsAdded = util.promiseEach(parts, function(part, index) {

      var partsSoFar = filesystem.join.apply(filesystem, parts.slice(0, index + 1));

      // We allow this file matches in this pass if we're allowed files at all
      // (i.e this isn't 'cd') and if this is the last part of the path
      var allowFileForPart = (allowFile && index >= parts.length - 1);

      var rootsPromise = util.promiseEach(roots, function(root) {

        // Extend each roots to a list of all the files in each of the roots
        var matchFile = allowFileForPart ? options.matches : null;
        var promise = filesystem.ls(root.name, matchFile);

        var onSuccess = function(entries) {
          // Unless this is the final part filter out the non-directories
          if (!allowFileForPart) {
            entries = entries.filter(function(entry) {
              return entry.isDir;
            });
          }
          var entryMap = {};
          entries.forEach(function(entry) {
            entryMap[entry.pathname] = entry;
          });
          return entryMap;
        };

        var onError = function(err) {
          // We expect errors due to the path not being a directory, not being
          // accessible, or removed since the call to 'readdir'
          return {};
        };

        promise = promise.then(onSuccess, onError);

        // We want to compare all the directory entries with the original root
        // plus the partsSoFar
        var compare = filesystem.join(root.original, partsSoFar);

        return promise.then(function(entryMap) {

          var ranks = spell.rank(compare, Object.keys(entryMap), RANK_OPTIONS);
          // penalize each path by the distance of it's parent
          ranks.forEach(function(rank) {
            rank.original = root.original;
            rank.stats = entryMap[rank.name];
          });
          return ranks;
        });
      });

      return rootsPromise.then(function(data) {
        // data is an array of arrays of ranking objects. Squash down.
        data = data.reduce(function(prev, curr) {
          return prev.concat(curr);
        }, []);

        data.sort(function(r1, r2) {
          return r1.dist - r2.dist;
        });

        // Trim, but by how many?
        // If this is the last run through, we want to present the user with
        // a sensible set of predictions. Otherwise we want to trim the tree
        // to a reasonable set of matches, so we're happy with 1
        // We look through x +/- 3 roots, and find the one with the biggest
        // distance delta, and cut below that
        // x=5 for the last time through, and x=8 otherwise
        var isLast = index >= parts.length - 1;
        var start = isLast ? 1 : 5;
        var end = isLast ? 7 : 10;

        var maxDeltaAt = start;
        var maxDelta = data[start].dist - data[start - 1].dist;

        for (var i = start + 1; i < end; i++) {
          var delta = data[i].dist - data[i - 1].dist;
          if (delta >= maxDelta) {
            maxDelta = delta;
            maxDeltaAt = i;
          }
        }

        // Update the list of roots for the next time round
        roots = data.slice(0, maxDeltaAt);
      });
    });

    return partsAdded.then(function() {
      var predictions = roots.map(function(root) {
        var isFile = root.stats && root.stats.isFile;
        var isDir = root.stats && root.stats.isDir;

        var name = root.name;
        if (isDir && name.charAt(name.length) !== filesystem.sep) {
          name += filesystem.sep;
        }

        return {
          name: name,
          incomplete: !(allowFile && isFile),
          isFile: isFile,  // Added for describe, below
          dist: root.dist, // TODO: Remove - added for debug in describe
        };
      });

      return util.promiseEach(predictions, function(prediction) {
        if (!prediction.isFile) {
          prediction.description = '(' + prediction.dist + ')';
          prediction.dist = undefined;
          prediction.isFile = undefined;
          return prediction;
        }

        return filesystem.describe(prediction.name).then(function(description) {
          prediction.description = description;
          prediction.dist = undefined;
          prediction.isFile = undefined;
          return prediction;
        });
      });
    });
  };
}

// =============================================================================

/*
 * The idea is that we maintain a list of 'directories that the user is
 * interested in'. We store directories in a most-frequently-used cache
 * of some description.
 * But for now we're just using / and ~/
 */
var dirHistory = {
  getCommonDirectories: function() {
    return [
      filesystem.sep,  // i.e. the root directory
      filesystem.home  // i.e. the users home directory
    ];
  },
  addCommonDirectory: function(ignore) {
    // Not implemented yet
  }
};
PK
!<~Y��
�
Ochrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/util/filesystem.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

var Cu = require('chrome').Cu;
var Cc = require('chrome').Cc;
var Ci = require('chrome').Ci;

var OS = require('resource://gre/modules/osfile.jsm').OS;

/**
 * A set of functions that don't really belong in 'fs' (because they're not
 * really universal in scope) but also kind of do (because they're not specific
 * to GCLI
 */

exports.join = OS.Path.join;
exports.sep = OS.Path.sep;
exports.dirname = OS.Path.dirname;

// On B2G, there is no home folder
var home = null;
try {
  var dirService = Cc['@mozilla.org/file/directory_service;1']
                     .getService(Ci.nsIProperties);
  home = dirService.get('Home', Ci.nsIFile).path;
} catch(e) {}
exports.home = home;

if ('winGetDrive' in OS.Path) {
  exports.sep = '\\';
}
else {
  exports.sep = '/';
}

/**
 * Split a path into its components.
 * @param pathname (string) The part to cut up
 * @return An array of path components
 */
exports.split = function(pathname) {
  return OS.Path.split(pathname).components;
};

/**
 * @param pathname string, path of an existing directory
 * @param matches optional regular expression - filter output to include only
 * the files that match the regular expression. The regexp is applied to the
 * filename only not to the full path
 * @return A promise of an array of stat objects for each member of the
 * directory pointed to by ``pathname``, each containing 2 extra properties:
 * - pathname: The full pathname of the file
 * - filename: The final filename part of the pathname
 */
exports.ls = function(pathname, matches) {
  var iterator = new OS.File.DirectoryIterator(pathname);
  var entries = [];

  var iteratePromise = iterator.forEach(function(entry) {
    entries.push({
      exists: true,
      isDir: entry.isDir,
      isFile: !entry.isFile,
      filename: entry.name,
      pathname: entry.path
    });
  });

  return iteratePromise.then(function onSuccess() {
      iterator.close();
      return entries;
    },
    function onFailure(reason) {
      iterator.close();
      throw reason;
    }
  );
};

/**
 * stat() is annoying because it considers stat('/doesnt/exist') to be an
 * error, when the point of stat() is to *find* *out*. So this wrapper just
 * converts 'ENOENT' i.e. doesn't exist to { exists:false } and adds
 * exists:true to stat blocks from existing paths
 */
exports.stat = function(pathname) {
  var onResolve = function(stats) {
    return {
      exists: true,
      isDir: stats.isDir,
      isFile: !stats.isFile
    };
  };

  var onReject = function(err) {
    if (err instanceof OS.File.Error && err.becauseNoSuchFile) {
      return {
        exists: false,
        isDir: false,
        isFile: false
      };
    }
    throw err;
  };

  return OS.File.stat(pathname).then(onResolve, onReject);
};

/**
 * We may read the first line of a file to describe it?
 * Right now, however, we do nothing.
 */
exports.describe = function(pathname) {
  return Promise.resolve('');
};
PK
!<-8�--Ichrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/util/host.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

const { Cc, Ci, Cu } = require("chrome");

Cu.importGlobalProperties(["XMLHttpRequest"]);

var { Task } = require("devtools/shared/task");

var util = require('./util');

function Highlighter(document) {
  this._document = document;
  this._nodes = util.createEmptyNodeList(this._document);
}

Object.defineProperty(Highlighter.prototype, 'nodelist', {
  set: function(nodes) {
    Array.prototype.forEach.call(this._nodes, this._unhighlightNode, this);
    this._nodes = (nodes == null) ?
        util.createEmptyNodeList(this._document) :
        nodes;
    Array.prototype.forEach.call(this._nodes, this._highlightNode, this);
  },
  get: function() {
    return this._nodes;
  },
  enumerable: true
});

Highlighter.prototype.destroy = function() {
  this.nodelist = null;
};

Highlighter.prototype._highlightNode = function(node) {
  // Enable when the highlighter rewrite is done
};

Highlighter.prototype._unhighlightNode = function(node) {
  // Enable when the highlighter rewrite is done
};

exports.Highlighter = Highlighter;

/**
 * See docs in lib/gcli/util/host.js
 */
exports.exec = function(task) {
  return Task.spawn(task);
};

/**
 * The URL API is new enough that we need specific platform help
 */
exports.createUrl = function(uristr, base) {
  return new URL(uristr, base);
};

/**
 * Load some HTML into the given document and return a DOM element.
 * This utility assumes that the html has a single root (other than whitespace)
 */
exports.toDom = function(document, html) {
  var div = util.createElement(document, 'div');
  util.setContents(div, html);
  return div.children[0];
};

/**
 * When dealing with module paths on windows we want to use the unix
 * directory separator rather than the windows one, so we avoid using
 * OS.Path.dirname, and use unix version on all platforms.
 */
var resourceDirName = function(path) {
  var index = path.lastIndexOf('/');
  if (index == -1) {
    return '.';
  }
  while (index >= 0 && path[index] == '/') {
    --index;
  }
  return path.slice(0, index + 1);
};

/**
 * Asynchronously load a text resource
 * @see lib/gcli/util/host.js
 */
exports.staticRequire = function(requistingModule, name) {
  if (name.match(/\.css$/)) {
    return Promise.resolve('');
  }
  else {
    return new Promise((resolve, reject) => {
      var filename = resourceDirName(requistingModule.id) + '/' + name;
      filename = filename.replace(/\/\.\//g, '/');
      filename = 'resource://devtools/shared/gcli/source/lib/' + filename;

      var xhr = new XMLHttpRequest();

      xhr.onload = () => {
        resolve(xhr.responseText);
      };

      xhr.onabort = xhr.onerror = xhr.ontimeout = err => {
        reject(err);
      };

      xhr.open('GET', filename);
      xhr.send();
    });
  }
};

/**
 * A group of functions to help scripting. Small enough that it doesn't need
 * a separate module (it's basically a wrapper around 'eval' in some contexts)
 */
var client;
var target;
var consoleActor;
var webConsoleClient;

exports.script = { };

exports.script.onOutput = util.createEvent('Script.onOutput');

/**
 * Setup the environment to eval JavaScript
 */
exports.script.useTarget = function(tgt) {
  target = tgt;

  // Local debugging needs to make the target remote.
  var targetPromise = target.isRemote ?
                      Promise.resolve(target) :
                      target.makeRemote();

  return targetPromise.then(function() {
    return new Promise((resolve, reject) => {
      client = target._client;

      client.addListener('pageError', function(packet) {
        if (packet.from === consoleActor) {
          // console.log('pageError', packet.pageError);
          exports.script.onOutput({
            level: 'exception',
            message: packet.exception.class
          });
        }
      });

      client.addListener('consoleAPICall', function(type, packet) {
        if (packet.from === consoleActor) {
          var data = packet.message;

          var ev = {
            level: data.level,
            arguments: data.arguments,
          };

          if (data.filename !== 'debugger eval code') {
            ev.source = {
              filename: data.filename,
              lineNumber: data.lineNumber,
              functionName: data.functionName
            };
          }

          exports.script.onOutput(ev);
        }
      });

      consoleActor = target._form.consoleActor;

      var onAttach = function(response, wcc) {
        webConsoleClient = wcc;

        if (response.error != null) {
          reject(response);
        }
        else {
          resolve(response);
        }

        // TODO: add _onTabNavigated code?
      };

      var listeners = [ 'PageError', 'ConsoleAPI' ];
      client.attachConsole(consoleActor, listeners, onAttach);
    });
  });
};

/**
 * Execute some JavaScript
 */
exports.script.evaluate = function(javascript) {
  return new Promise((resolve, reject) => {
    var onResult = function(response) {
      var output = response.result;
      if (typeof output === 'object' && output.type === 'undefined') {
        output = undefined;
      }

      resolve({
        input: response.input,
        output: output,
        exception: response.exception
      });
    };

    webConsoleClient.evaluateJS(javascript, onResult, {});
  });
};
PK
!<�+��V	V	Ichrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/util/l10n.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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";

const {LocalizationHelper} = require("devtools/shared/l10n");
const L10N = new LocalizationHelper("devtools/shared/locales/gcli.properties");

/*
 * Not supported when embedded - we"re doing things the Mozilla way not the
 * require.js way.
 */
exports.registerStringsSource = function (modulePath) {
  throw new Error("registerStringsSource is not available in mozilla");
};

exports.unregisterStringsSource = function (modulePath) {
  throw new Error("unregisterStringsSource is not available in mozilla");
};

exports.lookupSwap = function (key, swaps) {
  throw new Error("lookupSwap is not available in mozilla");
};

exports.lookupPlural = function (key, ord, swaps) {
  throw new Error("lookupPlural is not available in mozilla");
};

exports.getPreferredLocales = function () {
  return [ "root" ];
};

/** @see lookup() in lib/gcli/util/l10n.js */
exports.lookup = function (key) {
  try {
    // Our memory leak hunter walks reachable objects trying to work out what
    // type of thing they are using object.constructor.name. If that causes
    // problems then we can avoid the unknown-key-exception with the following:
    /*
    if (key === "constructor") {
      return { name: "l10n-mem-leak-defeat" };
    }
    */

    return L10N.getStr(key);
  } catch (ex) {
    console.error("Failed to lookup ", key, ex);
    return key;
  }
};

/** @see propertyLookup in lib/gcli/util/l10n.js */
exports.propertyLookup = new Proxy({}, {
  get: function (rcvr, name) {
    return exports.lookup(name);
  }
});

/** @see lookupFormat in lib/gcli/util/l10n.js */
exports.lookupFormat = function (key, swaps) {
  try {
    return L10N.getFormatStr(key, ...swaps);
  } catch (ex) {
    console.error("Failed to format ", key, ex);
    return key;
  }
};
PK
!<G|��..Kchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/util/legacy.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

/**
 * Fake a console for IE9
 */
if (typeof window !== 'undefined' && window.console == null) {
  window.console = {};
}
'debug,log,warn,error,trace,group,groupEnd'.split(',').forEach(function(f) {
  if (typeof window !== 'undefined' && !window.console[f]) {
    window.console[f] = function() {};
  }
});

/**
 * Fake Element.classList for IE9
 * Based on https://gist.github.com/1381839 by Devon Govett
 */
if (typeof document !== 'undefined' && typeof HTMLElement !== 'undefined' &&
        !('classList' in document.documentElement) && Object.defineProperty) {
  Object.defineProperty(HTMLElement.prototype, 'classList', {
    get: function() {
      var self = this;
      function update(fn) {
        return function(value) {
          var classes = self.className.split(/\s+/);
          var index = classes.indexOf(value);
          fn(classes, index, value);
          self.className = classes.join(' ');
        };
      }

      var ret = {
        add: update(function(classes, index, value) {
          ~index || classes.push(value);
        }),
        remove: update(function(classes, index) {
          ~index && classes.splice(index, 1);
        }),
        toggle: update(function(classes, index, value) {
          ~index ? classes.splice(index, 1) : classes.push(value);
        }),
        contains: function(value) {
          return !!~self.className.split(/\s+/).indexOf(value);
        },
        item: function(i) {
          return self.className.split(/\s+/)[i] || null;
        }
      };

      Object.defineProperty(ret, 'length', {
        get: function() {
          return self.className.split(/\s+/).length;
        }
      });

      return ret;
    }
  });
}

/**
 * Array.find
 * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
 */
if (!Array.prototype.find) {
  Object.defineProperty(Array.prototype, 'find', {
    enumerable: false,
    configurable: true,
    writable: true,
    value: function(predicate) {
      if (this == null) {
        throw new TypeError('Array.prototype.find called on null or undefined');
      }
      if (typeof predicate !== 'function') {
        throw new TypeError('predicate must be a function');
      }
      var list = Object(this);
      var length = list.length >>> 0;
      var thisArg = arguments[1];
      var value;

      for (var i = 0; i < length; i++) {
        if (i in list) {
          value = list[i];
          if (predicate.call(thisArg, value, i, list)) {
            return value;
          }
        }
      }
      return undefined;
    }
  });
}

/**
 * String.prototype.trimLeft is non-standard, but it works in Firefox,
 * Chrome and Opera. It's easiest to create a shim here.
 */
if (!String.prototype.trimLeft) {
  String.prototype.trimLeft = function() {
    return String(this).replace(/\s*$/, '');
  };
}

/**
 * Polyfil taken from
 * https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind
 */
if (!Function.prototype.bind) {
  Function.prototype.bind = function(oThis) {
    if (typeof this !== 'function') {
      // closest thing possible to the ECMAScript 5 internal IsCallable function
      throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
    }

    var aArgs = Array.prototype.slice.call(arguments, 1),
        fToBind = this,
        fNOP = function () {},
        fBound = function () {
          return fToBind.apply(this instanceof fNOP && oThis
                                 ? this
                                 : oThis,
                               aArgs.concat(Array.prototype.slice.call(arguments)));
        };

    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();
    return fBound;
  };
}
PK
!<9P��""Jchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/util/prism.js/**
 * Prism: Lightweight, robust, elegant syntax highlighting
 * MIT license http://www.opensource.org/licenses/mit-license.php/
 * @author Lea Verou http://lea.verou.me
 */

'use strict';

// Private helper vars
var lang = /\blang(?:uage)?-(?!\*)(\w+)\b/i;

var Prism = exports.Prism = {
  util: {
    type: function (o) {
      return Object.prototype.toString.call(o).match(/\[object (\w+)\]/)[1];
    },

    // Deep clone a language definition (e.g. to extend it)
    clone: function (o) {
      var type = Prism.util.type(o);

      switch (type) {
        case 'Object':
          var clone = {};

          for (var key in o) {
            if (o.hasOwnProperty(key)) {
              clone[key] = Prism.util.clone(o[key]);
            }
          }

          return clone;

        case 'Array':
          return o.slice();
      }

      return o;
    }
  },

  languages: {
    extend: function (id, redef) {
      var lang = Prism.util.clone(Prism.languages[id]);

      for (var key in redef) {
        lang[key] = redef[key];
      }

      return lang;
    },

    // Insert a token before another token in a language literal
    insertBefore: function (inside, before, insert, root) {
      root = root || Prism.languages;
      var grammar = root[inside];
      var ret = {};

      for (var token in grammar) {

        if (grammar.hasOwnProperty(token)) {

          if (token == before) {

            for (var newToken in insert) {

              if (insert.hasOwnProperty(newToken)) {
                ret[newToken] = insert[newToken];
              }
            }
          }

          ret[token] = grammar[token];
        }
      }

      root[inside] = ret;
      return ret;
    },

    // Traverse a language definition with Depth First Search
    DFS: function(o, callback) {
      for (var i in o) {
        callback.call(o, i, o[i]);

        if (Prism.util.type(o) === 'Object') {
          Prism.languages.DFS(o[i], callback);
        }
      }
    }
  },

  highlightAll: function(async, callback) {
    var elements = document.querySelectorAll('code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code');

    elements.forEach(function(element) {
      Prism.highlightElement(element, async === true, callback);
    });
  },

  highlightElement: function(element, async, callback) {
    // Find language
    var language;
    var grammar;

    var parent = element;
    while (parent && !lang.test(parent.className)) {
      parent = parent.parentNode;
    }

    if (parent) {
      language = (parent.className.match(lang) || [,''])[1];
      grammar = Prism.languages[language];
    }

    if (!grammar) {
      return;
    }

    // Set language on the element, if not present
    element.className = element.className.replace(lang, '').replace(/\s+/g, ' ') + ' language-' + language;

    // Set language on the parent, for styling
    parent = element.parentNode;

    if (/pre/i.test(parent.nodeName)) {
      parent.className = parent.className.replace(lang, '').replace(/\s+/g, ' ') + ' language-' + language;
    }

    var code = element.textContent;

    if (!code) {
      return;
    }

    code = code.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/\u00a0/g, ' ');

    var env = {
      element: element,
      language: language,
      grammar: grammar,
      code: code
    };

    Prism.hooks.run('before-highlight', env);

    env.highlightedCode = Prism.highlight(env.code, env.grammar, env.language);

    Prism.hooks.run('before-insert', env);

    env.element.innerHTML = env.highlightedCode;

    if (callback) {
      callback.call(element);
    }

    Prism.hooks.run('after-highlight', env);
  },

  highlight: function (text, grammar, language) {
    return Token.stringify(Prism.tokenize(text, grammar), language);
  },

  tokenize: function(text, grammar, language) {
    var Token = Prism.Token;

    var strarr = [text];

    var rest = grammar.rest;
    var token;
    if (rest) {
      for (token in rest) {
        grammar[token] = rest[token];
      }

      delete grammar.rest;
    }

    tokenloop:
    for (token in grammar) {
      if (!grammar.hasOwnProperty(token) || !grammar[token]) {
        continue;
      }

      var pattern = grammar[token],
        inside = pattern.inside,
        lookbehind = !!pattern.lookbehind,
        lookbehindLength = 0;

      pattern = pattern.pattern || pattern;

      for (var i=0; i<strarr.length; i++) { // Don’t cache length as it changes during the loop

        var str = strarr[i];

        if (strarr.length > text.length) {
          // Something went terribly wrong, ABORT, ABORT!
          break tokenloop;
        }

        if (str instanceof Token) {
          continue;
        }

        pattern.lastIndex = 0;

        var match = pattern.exec(str);

        if (match) {
          if (lookbehind) {
            lookbehindLength = match[1].length;
          }

          var from = match.index - 1 + lookbehindLength;
          match = match[0].slice(lookbehindLength);
          var len = match.length;
          var to = from + len;
          var before = str.slice(0, from + 1);
          var after = str.slice(to + 1);

          var args = [i, 1];

          if (before) {
            args.push(before);
          }

          var wrapped = new Token(token, inside? Prism.tokenize(match, inside) : match);

          args.push(wrapped);

          if (after) {
            args.push(after);
          }

          Array.prototype.splice.apply(strarr, args);
        }
      }
    }

    return strarr;
  },

  hooks: {
    all: {},

    add: function (name, callback) {
      var hooks = Prism.hooks.all;

      hooks[name] = hooks[name] || [];

      hooks[name].push(callback);
    },

    run: function (name, env) {
      var callbacks = Prism.hooks.all[name];

      if (!callbacks || !callbacks.length) {
        return;
      }

      callbacks.forEach(function(callback) {
        callback(env);
      });
    }
  }
};

var Token = Prism.Token = function(type, content) {
  this.type = type;
  this.content = content;
};

Token.stringify = function(o, language, parent) {
  if (typeof o == 'string') {
    return o;
  }

  if (Object.prototype.toString.call(o) == '[object Array]') {
    return o.map(function(element) {
      return Token.stringify(element, language, o);
    }).join('');
  }

  var env = {
    type: o.type,
    content: Token.stringify(o.content, language, parent),
    tag: 'span',
    classes: ['token', o.type],
    attributes: {},
    language: language,
    parent: parent
  };

  if (env.type == 'comment') {
    env.attributes.spellcheck = 'true';
  }

  Prism.hooks.run('wrap', env);

  var attributes = '';

  for (var name in env.attributes) {
    attributes += name + '="' + (env.attributes[name] || '') + '"';
  }

  return '<' + env.tag + ' class="' + env.classes.join(' ') + '" ' + attributes + '>' + env.content + '</' + env.tag + '>';
};

Prism.languages.clike = {
  'comment': {
    pattern: /(^|[^\\])(\/\*[\w\W]*?\*\/|(^|[^:])\/\/.*?(\r?\n|$))/g,
    lookbehind: true
  },
  'string': /("|')(\\?.)*?\1/g,
  'class-name': {
    pattern: /((?:(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/ig,
    lookbehind: true,
    inside: {
      punctuation: /(\.|\\)/
    }
  },
  'keyword': /\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/g,
  'boolean': /\b(true|false)\b/g,
  'function': {
    pattern: /[a-z0-9_]+\(/ig,
    inside: {
      punctuation: /\(/
    }
  },
  'number': /\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?)\b/g,
  'operator': /[-+]{1,2}|!|&lt;=?|>=?|={1,3}|(&amp;){1,2}|\|?\||\?|\*|\/|\~|\^|\%/g,
  'ignore': /&(lt|gt|amp);/gi,
  'punctuation': /[{}[\];(),.:]/g
};

Prism.languages.javascript = Prism.languages.extend('clike', {
  'keyword': /\b(var|let|if|else|while|do|for|return|in|instanceof|function|new|with|typeof|try|throw|catch|finally|null|break|continue)\b/g,
  'number': /\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?|NaN|-?Infinity)\b/g
});

Prism.languages.insertBefore('javascript', 'keyword', {
  'regex': {
    pattern: /(^|[^/])\/(?!\/)(\[.+?]|\\.|[^/\r\n])+\/[gim]{0,3}(?=\s*($|[\r\n,.;})]))/g,
    lookbehind: true
  }
});

if (Prism.languages.markup) {
  Prism.languages.insertBefore('markup', 'tag', {
    'script': {
      pattern: /(&lt;|<)script[\w\W]*?(>|&gt;)[\w\W]*?(&lt;|<)\/script(>|&gt;)/ig,
      inside: {
        'tag': {
          pattern: /(&lt;|<)script[\w\W]*?(>|&gt;)|(&lt;|<)\/script(>|&gt;)/ig,
          inside: Prism.languages.markup.tag.inside
        },
        rest: Prism.languages.javascript
      }
    }
  });
}
PK
!<2�d���Jchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/util/spell.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

/*
 * A spell-checker based on Damerau-Levenshtein distance.
 */

var CASE_CHANGE_COST = 1;
var INSERTION_COST = 10;
var DELETION_COST = 10;
var SWAP_COST = 10;
var SUBSTITUTION_COST = 20;
var MAX_EDIT_DISTANCE = 40;

/**
 * Compute Damerau-Levenshtein Distance, with a modification to allow a low
 * case-change cost (1/10th of a swap-cost)
 * @see http://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance
 */
var distance = exports.distance = function(wordi, wordj) {
  var wordiLen = wordi.length;
  var wordjLen = wordj.length;

  // We only need to store three rows of our dynamic programming matrix.
  // (Without swap, it would have been two.)
  var row0 = new Array(wordiLen+1);
  var row1 = new Array(wordiLen+1);
  var row2 = new Array(wordiLen+1);
  var tmp;

  var i, j;

  // The distance between the empty string and a string of size i is the cost
  // of i insertions.
  for (i = 0; i <= wordiLen; i++) {
    row1[i] = i * INSERTION_COST;
  }

  // Row-by-row, we're computing the edit distance between substrings wordi[0..i]
  // and wordj[0..j].
  for (j = 1; j <= wordjLen; j++) {
    // Edit distance between wordi[0..0] and wordj[0..j] is the cost of j
    // insertions.
    row0[0] = j * INSERTION_COST;

    for (i = 1; i <= wordiLen; i++) {
      // Handle deletion, insertion and substitution: we can reach each cell
      // from three other cells corresponding to those three operations. We
      // want the minimum cost.
      var dc = row0[i - 1] + DELETION_COST;
      var ic = row1[i] + INSERTION_COST;
      var sc0;
      if (wordi[i-1] === wordj[j-1]) {
        sc0 = 0;
      }
      else {
        if (wordi[i-1].toLowerCase() === wordj[j-1].toLowerCase()) {
          sc0 = CASE_CHANGE_COST;
        }
        else {
          sc0 = SUBSTITUTION_COST;
        }
      }
      var sc = row1[i-1] + sc0;

      row0[i] = Math.min(dc, ic, sc);

      // We handle swap too, eg. distance between help and hlep should be 1. If
      // we find such a swap, there's a chance to update row0[1] to be lower.
      if (i > 1 && j > 1 && wordi[i-1] === wordj[j-2] && wordj[j-1] === wordi[i-2]) {
        row0[i] = Math.min(row0[i], row2[i-2] + SWAP_COST);
      }
    }

    tmp = row2;
    row2 = row1;
    row1 = row0;
    row0 = tmp;
  }

  return row1[wordiLen];
};

/**
 * As distance() except that we say that if word is a prefix of name then we
 * only count the case changes. This allows us to use words that can be
 * completed by typing as more likely than short words
 */
var distancePrefix = exports.distancePrefix = function(word, name) {
  var dist = 0;

  for (var i = 0; i < word.length; i++) {
    if (name[i] !== word[i]) {
      if (name[i].toLowerCase() === word[i].toLowerCase()) {
        dist++;
      }
      else {
        // name does not start with word, even ignoring case, use
        // Damerau-Levenshtein
        return exports.distance(word, name);
      }
    }
  }

  return dist;
};

/**
 * A function that returns the correction for the specified word.
 */
exports.correct = function(word, names) {
  if (names.length === 0) {
    return undefined;
  }

  var distances = {};
  var sortedCandidates;

  names.forEach(function(candidate) {
    distances[candidate] = exports.distance(word, candidate);
  });

  sortedCandidates = names.sort(function(worda, wordb) {
    if (distances[worda] !== distances[wordb]) {
      return distances[worda] - distances[wordb];
    }
    else {
      // if the score is the same, always return the first string
      // in the lexicographical order
      return worda < wordb;
    }
  });

  if (distances[sortedCandidates[0]] <= MAX_EDIT_DISTANCE) {
    return sortedCandidates[0];
  }
  else {
    return undefined;
  }
};

/**
 * Return a ranked list of matches:
 *
 *   spell.rank('fred', [ 'banana', 'fred', 'ed', 'red' ]);
 *     ↓
 *   [
 *      { name: 'fred', dist: 0 },
 *      { name: 'red', dist: 1 },
 *      { name: 'ed', dist: 2 },
 *      { name: 'banana', dist: 10 },
 *   ]
 *
 * @param word The string that we're comparing names against
 * @param names An array of strings to compare word against
 * @param options Comparison options:
 * - noSort: Do not sort the output by distance
 * - prefixZero: Count prefix matches as edit distance 0 (i.e. word='bana' and
 *   names=['banana'], would return { name:'banana': dist: 0 }) This is useful
 *   if someone is typing the matches and may not have finished yet
 */
exports.rank = function(word, names, options) {
  options = options || {};

  var reply = names.map(function(name) {
    // If any name starts with the word then the distance is based on the
    // number of case changes rather than Damerau-Levenshtein
    var algo = options.prefixZero ? distancePrefix : distance;
    return {
      name: name,
      dist: algo(word, name)
    };
  });

  if (!options.noSort) {
    reply = reply.sort(function(d1, d2) {
      return d1.dist - d2.dist;
    });
  }

  return reply;
};
PK
!<H��GGIchrome/devtools/modules/devtools/shared/gcli/source/lib/gcli/util/util.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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';

/*
 * A number of DOM manipulation and event handling utilities.
 */

//------------------------------------------------------------------------------

var eventDebug = false;

/**
 * Patch up broken console API from node
 */
if (eventDebug) {
  if (console.group == null) {
    console.group = function() { console.log(arguments); };
  }
  if (console.groupEnd == null) {
    console.groupEnd = function() { console.log(arguments); };
  }
}

/**
 * Useful way to create a name for a handler, used in createEvent()
 */
function nameFunction(handler) {
  var scope = handler.scope ? handler.scope.constructor.name + '.' : '';
  var name = handler.func.name;
  if (name) {
    return scope + name;
  }
  for (var prop in handler.scope) {
    if (handler.scope[prop] === handler.func) {
      return scope + prop;
    }
  }
  return scope + handler.func;
}

/**
 * Create an event.
 * For use as follows:
 *
 *   function Hat() {
 *     this.putOn = createEvent('Hat.putOn');
 *     ...
 *   }
 *   Hat.prototype.adorn = function(person) {
 *     this.putOn({ hat: hat, person: person });
 *     ...
 *   }
 *
 *   var hat = new Hat();
 *   hat.putOn.add(function(ev) {
 *     console.log('The hat ', ev.hat, ' has is worn by ', ev.person);
 *   }, scope);
 *
 * @param name Optional name to help with debugging
 */
exports.createEvent = function(name) {
  var handlers = [];
  var fireHoldCount = 0;
  var heldEvents = [];
  var eventCombiner;

  /**
   * This is how the event is triggered.
   * @param ev The event object to be passed to the event listeners
   */
  var event = function(ev) {
    if (fireHoldCount > 0) {
      heldEvents.push(ev);
      if (eventDebug) {
        console.log('Held fire: ' + name, ev);
      }
      return;
    }

    if (eventDebug) {
      console.group('Fire: ' + name + ' to ' + handlers.length + ' listeners', ev);
    }

    // Use for rather than forEach because it step debugs better, which is
    // important for debugging events
    for (var i = 0; i < handlers.length; i++) {
      var handler = handlers[i];
      if (eventDebug) {
        console.log(nameFunction(handler));
      }
      handler.func.call(handler.scope, ev);
    }

    if (eventDebug) {
      console.groupEnd();
    }
  };

  /**
   * Add a new handler function
   * @param func The function to call when this event is triggered
   * @param scope Optional 'this' object for the function call
   */
  event.add = function(func, scope) {
    if (typeof func !== 'function') {
      throw new Error(name + ' add(func,...), 1st param is ' + typeof func);
    }

    if (eventDebug) {
      console.log('Adding listener to ' + name);
    }

    handlers.push({ func: func, scope: scope });
  };

  /**
   * Remove a handler function added through add(). Both func and scope must
   * be strict equals (===) the values used in the call to add()
   * @param func The function to call when this event is triggered
   * @param scope Optional 'this' object for the function call
   */
  event.remove = function(func, scope) {
    if (eventDebug) {
      console.log('Removing listener from ' + name);
    }

    var found = false;
    handlers = handlers.filter(function(test) {
      var match = (test.func === func && test.scope === scope);
      if (match) {
        found = true;
      }
      return !match;
    });
    if (!found) {
      console.warn('Handler not found. Attached to ' + name);
    }
  };

  /**
   * Remove all handlers.
   * Reset the state of this event back to it's post create state
   */
  event.removeAll = function() {
    handlers = [];
  };

  /**
   * Fire an event just once using a promise.
   */
  event.once = function() {
    if (arguments.length !== 0) {
      throw new Error('event.once uses promise return values');
    }

    return new Promise(function(resolve, reject) {
      var handler = function(arg) {
        event.remove(handler);
        resolve(arg);
      };

      event.add(handler);
    });
  };

  /**
   * Temporarily prevent this event from firing.
   * @see resumeFire(ev)
   */
  event.holdFire = function() {
    if (eventDebug) {
      console.group('Holding fire: ' + name);
    }

    fireHoldCount++;
  };

  /**
   * Resume firing events.
   * If there are heldEvents, then we fire one event to cover them all. If an
   * event combining function has been provided then we use that to combine the
   * events. Otherwise the last held event is used.
   * @see holdFire()
   */
  event.resumeFire = function() {
    if (eventDebug) {
      console.groupEnd('Resume fire: ' + name);
    }

    if (fireHoldCount === 0) {
      throw new Error('fireHoldCount === 0 during resumeFire on ' + name);
    }

    fireHoldCount--;
    if (heldEvents.length === 0) {
      return;
    }

    if (heldEvents.length === 1) {
      event(heldEvents[0]);
    }
    else {
      var first = heldEvents[0];
      var last = heldEvents[heldEvents.length - 1];
      if (eventCombiner) {
        event(eventCombiner(first, last, heldEvents));
      }
      else {
        event(last);
      }
    }

    heldEvents = [];
  };

  /**
   * When resumeFire has a number of events to combine, by default it just
   * picks the last, however you can provide an eventCombiner which returns a
   * combined event.
   * eventCombiners will be passed 3 parameters:
   * - first The first event to be held
   * - last The last event to be held
   * - all An array containing all the held events
   * The return value from an eventCombiner is expected to be an event object
   */
  Object.defineProperty(event, 'eventCombiner', {
    set: function(newEventCombiner) {
      if (typeof newEventCombiner !== 'function') {
        throw new Error('eventCombiner is not a function');
      }
      eventCombiner = newEventCombiner;
    },

    enumerable: true
  });

  return event;
};

//------------------------------------------------------------------------------

/**
 * promiseEach is roughly like Array.forEach except that the action is taken to
 * be something that completes asynchronously, returning a promise, so we wait
 * for the action to complete for each array element before moving onto the
 * next.
 * @param array An array of objects to enumerate
 * @param action A function to call for each member of the array
 * @param scope Optional object to use as 'this' for the function calls
 * @return A promise which is resolved (with an array of resolution values)
 * when all the array members have been passed to the action function, and
 * rejected as soon as any of the action function calls fails 
 */
exports.promiseEach = function(array, action, scope) {
  if (array.length === 0) {
    return Promise.resolve([]);
  }

  var allReply = [];
  var promise = Promise.resolve();

  array.forEach(function(member, i) {
    promise = promise.then(function() {
      var reply = action.call(scope, member, i, array);
      return Promise.resolve(reply).then(function(data) {
        allReply[i] = data;
      });
    });
  });

  return promise.then(function() {
    return allReply;
  });
};

/**
 * Catching errors from promises isn't as simple as:
 *   promise.then(handler, console.error);
 * for a number of reasons:
 * - chrome's console doesn't have bound functions (why?)
 * - we don't get stack traces out from console.error(ex);
 */
exports.errorHandler = function(ex) {
  if (ex instanceof Error) {
    // V8 weirdly includes the exception message in the stack
    if (ex.stack.includes(ex.message)) {
      console.error(ex.stack);
    }
    else {
      console.error('' + ex);
      console.error(ex.stack);
    }
  }
  else {
    console.error(ex);
  }
};


//------------------------------------------------------------------------------

/**
 * Copy the properties from one object to another in a way that preserves
 * function properties as functions rather than copying the calculated value
 * as copy time
 */
exports.copyProperties = function(src, dest) {
  for (var key in src) {
    var descriptor;
    var obj = src;
    while (true) {
      descriptor = Object.getOwnPropertyDescriptor(obj, key);
      if (descriptor != null) {
        break;
      }
      obj = Object.getPrototypeOf(obj);
      if (obj == null) {
        throw new Error('Can\'t find descriptor of ' + key);
      }
    }

    if ('value' in descriptor) {
      dest[key] = src[key];
    }
    else if ('get' in descriptor) {
      Object.defineProperty(dest, key, {
        get: descriptor.get,
        set: descriptor.set,
        enumerable: descriptor.enumerable
      });
    }
    else {
      throw new Error('Don\'t know how to copy ' + key + ' property.');
    }
  }
};

//------------------------------------------------------------------------------

/**
 * XHTML namespace
 */
exports.NS_XHTML = 'http://www.w3.org/1999/xhtml';

/**
 * XUL namespace
 */
exports.NS_XUL = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul';

/**
 * Create an HTML or XHTML element depending on whether the document is HTML
 * or XML based. Where HTML/XHTML elements are distinguished by whether they
 * are created using doc.createElementNS('http://www.w3.org/1999/xhtml', tag)
 * or doc.createElement(tag)
 * If you want to create a XUL element then you don't have a problem knowing
 * what namespace you want.
 * @param doc The document in which to create the element
 * @param tag The name of the tag to create
 * @returns The created element
 */
exports.createElement = function(doc, tag) {
  if (exports.isXmlDocument(doc)) {
    return doc.createElementNS(exports.NS_XHTML, tag);
  }
  else {
    return doc.createElement(tag);
  }
};

/**
 * Remove all the child nodes from this node
 * @param elem The element that should have it's children removed
 */
exports.clearElement = function(elem) {
  while (elem.hasChildNodes()) {
    elem.firstChild.remove();
  }
};

var isAllWhitespace = /^\s*$/;

/**
 * Iterate over the children of a node looking for TextNodes that have only
 * whitespace content and remove them.
 * This utility is helpful when you have a template which contains whitespace
 * so it looks nice, but where the whitespace interferes with the rendering of
 * the page
 * @param elem The element which should have blank whitespace trimmed
 * @param deep Should this node removal include child elements
 */
exports.removeWhitespace = function(elem, deep) {
  var i = 0;
  while (i < elem.childNodes.length) {
    var child = elem.childNodes.item(i);
    if (child.nodeType === 3 /*Node.TEXT_NODE*/ &&
        isAllWhitespace.test(child.textContent)) {
      elem.removeChild(child);
    }
    else {
      if (deep && child.nodeType === 1 /*Node.ELEMENT_NODE*/) {
        exports.removeWhitespace(child, deep);
      }
      i++;
    }
  }
};

/**
 * Create a style element in the document head, and add the given CSS text to
 * it.
 * @param cssText The CSS declarations to append
 * @param doc The document element to work from
 * @param id Optional id to assign to the created style tag. If the id already
 * exists on the document, we do not add the CSS again.
 */
exports.importCss = function(cssText, doc, id) {
  if (!cssText) {
    return undefined;
  }

  doc = doc || document;

  if (!id) {
    id = 'hash-' + hash(cssText);
  }

  var found = doc.getElementById(id);
  if (found) {
    if (found.tagName.toLowerCase() !== 'style') {
      console.error('Warning: importCss passed id=' + id +
              ', but that pre-exists (and isn\'t a style tag)');
    }
    return found;
  }

  var style = exports.createElement(doc, 'style');
  style.id = id;
  style.appendChild(doc.createTextNode(cssText));

  var head = doc.getElementsByTagName('head')[0] || doc.documentElement;
  head.appendChild(style);

  return style;
};

/**
 * Simple hash function which happens to match Java's |String.hashCode()|
 * Done like this because I we don't need crypto-security, but do need speed,
 * and I don't want to spend a long time working on it.
 * @see http://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/
 */
function hash(str) {
  var h = 0;
  if (str.length === 0) {
    return h;
  }
  for (var i = 0; i < str.length; i++) {
    var character = str.charCodeAt(i);
    h = ((h << 5) - h) + character;
    h = h & h; // Convert to 32bit integer
  }
  return h;
}

/**
 * Shortcut for clearElement/createTextNode/appendChild to make up for the lack
 * of standards around textContent/innerText
 */
exports.setTextContent = function(elem, text) {
  exports.clearElement(elem);
  var child = elem.ownerDocument.createTextNode(text);
  elem.appendChild(child);
};

/**
 * There are problems with innerHTML on XML documents, so we need to do a dance
 * using document.createRange().createContextualFragment() when in XML mode
 */
exports.setContents = function(elem, contents) {
  if (typeof HTMLElement !== 'undefined' && contents instanceof HTMLElement) {
    exports.clearElement(elem);
    elem.appendChild(contents);
    return;
  }

  if ("unsafeSetInnerHTML" in elem) {
    // FIXME: Stop relying on unsanitized HTML.
    elem.unsafeSetInnerHTML(contents);
  } else if ("innerHTML" in elem) {
    elem.innerHTML = contents;
  } else {
    try {
      var ns = elem.ownerDocument.documentElement.namespaceURI;
      if (!ns) {
        ns = exports.NS_XHTML;
      }
      exports.clearElement(elem);
      contents = '<div xmlns="' + ns + '">' + contents + '</div>';
      var range = elem.ownerDocument.createRange();
      var child = range.createContextualFragment(contents).firstChild;
      while (child.hasChildNodes()) {
        elem.appendChild(child.firstChild);
      }
    }
    catch (ex) {
      console.error('Bad XHTML', ex);
      console.trace();
      throw ex;
    }
  }
};

/**
 * How to detect if we're in an XML document.
 * In a Mozilla we check that document.xmlVersion = null, however in Chrome
 * we use document.contentType = undefined.
 * @param doc The document element to work from (defaulted to the global
 * 'document' if missing
 */
exports.isXmlDocument = function(doc) {
  doc = doc || document;
  // Best test for Firefox
  if (doc.contentType && doc.contentType != 'text/html') {
    return true;
  }
  // Best test for Chrome
  if (doc.xmlVersion != null) {
    return true;
  }
  return false;
};

/**
 * We'd really like to be able to do 'new NodeList()'
 */
exports.createEmptyNodeList = function(doc) {
  if (doc.createDocumentFragment) {
    return doc.createDocumentFragment().childNodes;
  }
  return doc.querySelectorAll('x>:root');
};

//------------------------------------------------------------------------------

/**
 * 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: GCLI's keyboard handling should be updated to use DOM-L3
 * https://bugzilla.mozilla.org/show_bug.cgi?id=664991
 */
exports.KeyEvent = {
  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
};
PK
!<���1GUGU9chrome/devtools/modules/devtools/shared/gcli/templater.js/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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";

/* globals document */

/**
 * For full documentation, see:
 * https://github.com/mozilla/domtemplate/blob/master/README.md
 */

/**
 * Begin a new templating process.
 * @param node A DOM element or string referring to an element's id
 * @param data Data to use in filling out the template
 * @param options Options to customize the template processing. One of:
 * - allowEval: boolean (default false) Basic template interpolations are
 *   either property paths (e.g. ${a.b.c.d}), or if allowEval=true then we
 *   allow arbitrary JavaScript
 * - stack: string or array of strings (default empty array) The template
 *   engine maintains a stack of tasks to help debug where it is. This allows
 *   this stack to be prefixed with a template name
 * - blankNullUndefined: By default DOMTemplate exports null and undefined
 *   values using the strings 'null' and 'undefined', which can be helpful for
 *   debugging, but can introduce unnecessary extra logic in a template to
 *   convert null/undefined to ''. By setting blankNullUndefined:true, this
 *   conversion is handled by DOMTemplate
 */
var template = function (node, data, options) {
  let state = {
    options: options || {},
    // We keep a track of the nodes that we've passed through so we can keep
    // data.__element pointing to the correct node
    nodes: []
  };

  state.stack = state.options.stack;

  if (!Array.isArray(state.stack)) {
    if (typeof state.stack === "string") {
      state.stack = [ options.stack ];
    } else {
      state.stack = [];
    }
  }

  processNode(state, node, data);
};

if (typeof exports !== "undefined") {
  exports.template = template;
}
this.template = template;

/**
 * Helper for the places where we need to act asynchronously and keep track of
 * where we are right now
 */
function cloneState(state) {
  return {
    options: state.options,
    stack: state.stack.slice(),
    nodes: state.nodes.slice()
  };
}

/**
 * Regex used to find ${...} sections in some text.
 * Performance note: This regex uses ( and ) to capture the 'script' for
 * further processing. Not all of the uses of this regex use this feature so
 * if use of the capturing group is a performance drain then we should split
 * this regex in two.
 */
var TEMPLATE_REGION = /\$\{([^}]*)\}/g;

/**
 * Recursive function to walk the tree processing the attributes as it goes.
 * @param node the node to process. If you pass a string in instead of a DOM
 * element, it is assumed to be an id for use with document.getElementById()
 * @param data the data to use for node processing.
 */
function processNode(state, node, data) {
  if (typeof node === "string") {
    node = document.getElementById(node);
  }
  if (data == null) {
    data = {};
  }
  state.stack.push(node.nodeName + (node.id ? "#" + node.id : ""));
  let pushedNode = false;
  try {
    // Process attributes
    if (node.attributes && node.attributes.length) {
      // We need to handle 'foreach' and 'if' first because they might stop
      // some types of processing from happening, and foreach must come first
      // because it defines new data on which 'if' might depend.
      if (node.hasAttribute("foreach")) {
        processForEach(state, node, data);
        return;
      }
      if (node.hasAttribute("if")) {
        if (!processIf(state, node, data)) {
          return;
        }
      }
      // Only make the node available once we know it's not going away
      state.nodes.push(data.__element);
      data.__element = node;
      pushedNode = true;
      // It's good to clean up the attributes when we've processed them,
      // but if we do it straight away, we mess up the array index
      let attrs = Array.prototype.slice.call(node.attributes);
      for (let i = 0; i < attrs.length; i++) {
        let value = attrs[i].value;
        let name = attrs[i].name;

        state.stack.push(name);
        try {
          if (name === "save") {
            // Save attributes are a setter using the node
            value = stripBraces(state, value);
            property(state, value, data, node);
            node.removeAttribute("save");
          } else if (name.substring(0, 2) === "on") {
            // If this attribute value contains only an expression
            if (value.substring(0, 2) === "${" && value.slice(-1) === "}" &&
                    !value.includes("${", 2)) {
              value = stripBraces(state, value);
              let func = property(state, value, data);
              if (typeof func === "function") {
                node.removeAttribute(name);
                let capture = node.hasAttribute("capture" + name.substring(2));
                node.addEventListener(name.substring(2), func, capture);
                if (capture) {
                  node.removeAttribute("capture" + name.substring(2));
                }
              } else {
                // Attribute value is not a function - use as a DOM-L0 string
                node.setAttribute(name, func);
              }
            } else {
              // Attribute value is not a single expression use as DOM-L0
              node.setAttribute(name, processString(state, value, data));
            }
          } else {
            node.removeAttribute(name);
            // Remove '_' prefix of attribute names so the DOM won't try
            // to use them before we've processed the template
            if (name.charAt(0) === "_") {
              name = name.substring(1);
            }

            // Async attributes can only work if the whole attribute is async
            let replacement;
            if (value.indexOf("${") === 0 &&
                value.charAt(value.length - 1) === "}") {
              replacement = envEval(state, value.slice(2, -1), data, value);
              if (replacement && typeof replacement.then === "function") {
                node.setAttribute(name, "");
                /* jshint loopfunc:true */
                replacement.then(function (newValue) {
                  node.setAttribute(name, newValue);
                }).catch(console.error);
              } else {
                if (state.options.blankNullUndefined && replacement == null) {
                  replacement = "";
                }
                node.setAttribute(name, replacement);
              }
            } else {
              node.setAttribute(name, processString(state, value, data));
            }
          }
        } finally {
          state.stack.pop();
        }
      }
    }

    // Loop through our children calling processNode. First clone them, so the
    // set of nodes that we visit will be unaffected by additions or removals.
    let childNodes = Array.prototype.slice.call(node.childNodes);
    for (let j = 0; j < childNodes.length; j++) {
      processNode(state, childNodes[j], data);
    }

    /* 3 === Node.TEXT_NODE */
    if (node.nodeType === 3) {
      processTextNode(state, node, data);
    }
  } finally {
    if (pushedNode) {
      data.__element = state.nodes.pop();
    }
    state.stack.pop();
  }
}

/**
 * Handle attribute values where the output can only be a string
 */
function processString(state, value, data) {
  return value.replace(TEMPLATE_REGION, function (path) {
    let insert = envEval(state, path.slice(2, -1), data, value);
    return state.options.blankNullUndefined && insert == null ? "" : insert;
  });
}

/**
 * Handle <x if="${...}">
 * @param node An element with an 'if' attribute
 * @param data The data to use with envEval()
 * @returns true if processing should continue, false otherwise
 */
function processIf(state, node, data) {
  state.stack.push("if");
  try {
    let originalValue = node.getAttribute("if");
    let value = stripBraces(state, originalValue);
    let recurse = true;
    try {
      let reply = envEval(state, value, data, originalValue);
      recurse = !!reply;
    } catch (ex) {
      handleError(state, "Error with '" + value + "'", ex);
      recurse = false;
    }
    if (!recurse) {
      node.remove();
    }
    node.removeAttribute("if");
    return recurse;
  } finally {
    state.stack.pop();
  }
}

/**
 * Handle <x foreach="param in ${array}"> and the special case of
 * <loop foreach="param in ${array}">.
 * This function is responsible for extracting what it has to do from the
 * attributes, and getting the data to work on (including resolving promises
 * in getting the array). It delegates to processForEachLoop to actually
 * unroll the data.
 * @param node An element with a 'foreach' attribute
 * @param data The data to use with envEval()
 */
function processForEach(state, node, data) {
  state.stack.push("foreach");
  try {
    let originalValue = node.getAttribute("foreach");
    let value = originalValue;

    let paramName = "param";
    if (value.charAt(0) === "$") {
      // No custom loop variable name. Use the default: 'param'
      value = stripBraces(state, value);
    } else {
      // Extract the loop variable name from 'NAME in ${ARRAY}'
      let nameArr = value.split(" in ");
      paramName = nameArr[0].trim();
      value = stripBraces(state, nameArr[1].trim());
    }
    node.removeAttribute("foreach");
    try {
      let evaled = envEval(state, value, data, originalValue);
      let cState = cloneState(state);
      handleAsync(evaled, node, function (reply, siblingNode) {
        processForEachLoop(cState, reply, node, siblingNode, data, paramName);
      });
      node.remove();
    } catch (ex) {
      handleError(state, "Error with " + value + "'", ex);
    }
  } finally {
    state.stack.pop();
  }
}

/**
 * Called by processForEach to handle looping over the data in a foreach loop.
 * This works with both arrays and objects.
 * Calls processForEachMember() for each member of 'set'
 * @param set The object containing the data to loop over
 * @param templNode The node to copy for each set member
 * @param sibling The sibling node to which we add things
 * @param data the data to use for node processing
 * @param paramName foreach loops have a name for the parameter currently being
 * processed. The default is 'param'. e.g. <loop foreach="param in ${x}">...
 */
function processForEachLoop(state, set, templNode, sibling, data, paramName) {
  if (Array.isArray(set)) {
    set.forEach(function (member, i) {
      processForEachMember(state, member, templNode, sibling,
                           data, paramName, "" + i);
    });
  } else {
    for (let member in set) {
      if (set.hasOwnProperty(member)) {
        processForEachMember(state, member, templNode, sibling,
                             data, paramName, member);
      }
    }
  }
}

/**
 * Called by processForEachLoop() to resolve any promises in the array (the
 * array itself can also be a promise, but that is resolved by
 * processForEach()). Handle <LOOP> elements (which are taken out of the DOM),
 * clone the template node, and pass the processing on to processNode().
 * @param member The data item to use in templating
 * @param templNode The node to copy for each set member
 * @param siblingNode The parent node to which we add things
 * @param data the data to use for node processing
 * @param paramName The name given to 'member' by the foreach attribute
 * @param frame A name to push on the stack for debugging
 */
function processForEachMember(state, member, templNode, siblingNode, data,
                              paramName, frame) {
  state.stack.push(frame);
  try {
    let cState = cloneState(state);
    handleAsync(member, siblingNode, function (reply, node) {
      // Clone data because we can't be sure that we can safely mutate it
      let newData = Object.create(null);
      Object.keys(data).forEach(function (key) {
        newData[key] = data[key];
      });
      newData[paramName] = reply;
      if (node.parentNode != null) {
        let clone;
        if (templNode.nodeName.toLowerCase() === "loop") {
          for (let i = 0; i < templNode.childNodes.length; i++) {
            clone = templNode.childNodes[i].cloneNode(true);
            node.parentNode.insertBefore(clone, node);
            processNode(cState, clone, newData);
          }
        } else {
          clone = templNode.cloneNode(true);
          clone.removeAttribute("foreach");
          node.parentNode.insertBefore(clone, node);
          processNode(cState, clone, newData);
        }
      }
    });
  } finally {
    state.stack.pop();
  }
}

/**
 * Take a text node and replace it with another text node with the ${...}
 * sections parsed out. We replace the node by altering node.parentNode but
 * we could probably use a DOM Text API to achieve the same thing.
 * @param node The Text node to work on
 * @param data The data to use in calls to envEval()
 */
function processTextNode(state, node, data) {
  // Replace references in other attributes
  let value = node.data;
  // We can't use the string.replace() with function trick (see generic
  // attribute processing in processNode()) because we need to support
  // functions that return DOM nodes, so we can't have the conversion to a
  // string.
  // Instead we process the string as an array of parts. In order to split
  // the string up, we first replace '${' with '\uF001$' and '}' with '\uF002'
  // We can then split using \uF001 or \uF002 to get an array of strings
  // where scripts are prefixed with $.
  // \uF001 and \uF002 are just unicode chars reserved for private use.
  value = value.replace(TEMPLATE_REGION, "\uF001$$$1\uF002");
  // Split a string using the unicode chars F001 and F002.
  let parts = value.split(/\uF001|\uF002/);
  if (parts.length > 1) {
    parts.forEach(function (part) {
      if (part === null || part === undefined || part === "") {
        return;
      }
      if (part.charAt(0) === "$") {
        part = envEval(state, part.slice(1), data, node.data);
      }
      let cState = cloneState(state);
      handleAsync(part, node, function (reply, siblingNode) {
        let doc = siblingNode.ownerDocument;
        if (reply == null) {
          reply = cState.options.blankNullUndefined ? "" : "" + reply;
        }
        if (typeof reply.cloneNode === "function") {
          // i.e. if (reply instanceof Element) { ...
          reply = maybeImportNode(cState, reply, doc);
          siblingNode.parentNode.insertBefore(reply, siblingNode);
        } else if (typeof reply.item === "function" && reply.length) {
          // NodeLists can be live, in which case maybeImportNode can
          // remove them from the document, and thus the NodeList, which in
          // turn breaks iteration. So first we clone the list
          let list = Array.prototype.slice.call(reply, 0);
          list.forEach(function (child) {
            let imported = maybeImportNode(cState, child, doc);
            siblingNode.parentNode.insertBefore(imported, siblingNode);
          });
        } else {
          // if thing isn't a DOM element then wrap its string value in one
          reply = doc.createTextNode(reply.toString());
          siblingNode.parentNode.insertBefore(reply, siblingNode);
        }
      });
    });
    node.remove();
  }
}

/**
 * Return node or a import of node, if it's not in the given document
 * @param node The node that we want to be properly owned
 * @param doc The document that the given node should belong to
 * @return A node that belongs to the given document
 */
function maybeImportNode(state, node, doc) {
  return node.ownerDocument === doc ? node : doc.importNode(node, true);
}

/**
 * A function to handle the fact that some nodes can be promises, so we check
 * and resolve if needed using a marker node to keep our place before calling
 * an inserter function.
 * @param thing The object which could be real data or a promise of real data
 * we use it directly if it's not a promise, or resolve it if it is.
 * @param siblingNode The element before which we insert new elements.
 * @param inserter The function to to the insertion. If thing is not a promise
 * then handleAsync() is just 'inserter(thing, siblingNode)'
 */
function handleAsync(thing, siblingNode, inserter) {
  if (thing != null && typeof thing.then === "function") {
    // Placeholder element to be replaced once we have the real data
    let tempNode = siblingNode.ownerDocument.createElement("span");
    siblingNode.parentNode.insertBefore(tempNode, siblingNode);
    thing.then(function (delayed) {
      inserter(delayed, tempNode);
      if (tempNode.parentNode != null) {
        tempNode.remove();
      }
    }).catch(function (error) {
      console.error(error.stack);
    });
  } else {
    inserter(thing, siblingNode);
  }
}

/**
 * Warn of string does not begin '${' and end '}'
 * @param str the string to check.
 * @return The string stripped of ${ and }, or untouched if it does not match
 */
function stripBraces(state, str) {
  if (!str.match(TEMPLATE_REGION)) {
    handleError(state, "Expected " + str + " to match ${...}");
    return str;
  }
  return str.slice(2, -1);
}

/**
 * Combined getter and setter that works with a path through some data set.
 * For example:
 * <ul>
 * <li>property(state, 'a.b', { a: { b: 99 }}); // returns 99
 * <li>property(state, 'a', { a: { b: 99 }}); // returns { b: 99 }
 * <li>property(state, 'a', { a: { b: 99 }}, 42); // returns 99 and alters the
 * input data to be { a: { b: 42 }}
 * </ul>
 * @param path An array of strings indicating the path through the data, or
 * a string to be cut into an array using <tt>split('.')</tt>
 * @param data the data to use for node processing
 * @param newValue (optional) If defined, this value will replace the
 * original value for the data at the path specified.
 * @return The value pointed to by <tt>path</tt> before any
 * <tt>newValue</tt> is applied.
 */
function property(state, path, data, newValue) {
  try {
    if (typeof path === "string") {
      path = path.split(".");
    }
    let value = data[path[0]];
    if (path.length === 1) {
      if (newValue !== undefined) {
        data[path[0]] = newValue;
      }
      if (typeof value === "function") {
        return value.bind(data);
      }
      return value;
    }
    if (!value) {
      handleError(state, "\"" + path[0] + "\" is undefined");
      return null;
    }
    return property(state, path.slice(1), value, newValue);
  } catch (ex) {
    handleError(state, "Path error with '" + path + "'", ex);
    return "${" + path + "}";
  }
}

/**
 * Like eval, but that creates a context of the variables in <tt>env</tt> in
 * which the script is evaluated.
 * @param script The string to be evaluated.
 * @param data The environment in which to eval the script.
 * @param frame Optional debugging string in case of failure.
 * @return The return value of the script, or the error message if the script
 * execution failed.
 */
function envEval(state, script, data, frame) {
  try {
    state.stack.push(frame.replace(/\s+/g, " "));
    // Detect if a script is capable of being interpreted using property()
    if (/^[_a-zA-Z0-9.]*$/.test(script)) {
      return property(state, script, data);
    }
    if (!state.options.allowEval) {
      handleError(state, "allowEval is not set, however '" + script + "'" +
                  " can not be resolved using a simple property path.");
      return "${" + script + "}";
    }

    // What we're looking to do is basically:
    //   with(data) { return eval(script); }
    // except in strict mode where 'with' is banned.
    // So we create a function which has a parameter list the same as the
    // keys in 'data' and with 'script' as its function body.
    // We then call this function with the values in 'data'
    let keys = allKeys(data);
    let func = Function.apply(null, keys.concat("return " + script));

    let values = keys.map((key) => data[key]);
    return func.apply(null, values);

    // TODO: The 'with' method is different from the code above in the value
    // of 'this' when calling functions. For example:
    //   envEval(state, 'foo()', { foo: function () { return this; } }, ...);
    // The global for 'foo' when using 'with' is the data object. However the
    // code above, the global is null. (Using 'func.apply(data, values)'
    // changes 'this' in the 'foo()' frame, but not in the inside the body
    // of 'foo', so that wouldn't help)
  } catch (ex) {
    handleError(state, "Template error evaluating '" + script + "'", ex);
    return "${" + script + "}";
  } finally {
    state.stack.pop();
  }
}

/**
 * Object.keys() that respects the prototype chain
 */
function allKeys(data) {
  let keys = [];
  for (let key in data) {
    keys.push(key);
  }
  return keys;
}

/**
 * A generic way of reporting errors, for easy overloading in different
 * environments.
 * @param message the error message to report.
 * @param ex optional associated exception.
 */
function handleError(state, message, ex) {
  logError(message + " (In: " + state.stack.join(" > ") + ")");
  if (ex) {
    logError(ex);
  }
}

/**
 * A generic way of reporting errors, for easy overloading in different
 * environments.
 * @param message the error message to report.
 */
function logError(message) {
  console.error(message);
}

exports.template = template;
PK
!<�}W+��8chrome/devtools/modules/devtools/shared/generate-uuid.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Cc, Ci } = require("chrome");
const { generateUUID } =
  Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);

/**
 * Returns a new `uuid`.
 *
 */

module.exports = { generateUUID };
PK
!<8�<�6�6Cchrome/devtools/modules/devtools/shared/heapsnapshot/CensusUtils.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { flatten } = require("resource://devtools/shared/ThreadSafeDevToolsUtils.js");

/** * Visitor ****************************************************************/

/**
 * A Visitor visits each node and edge of a census report tree as the census
 * report is being traversed by `walk`.
 */
function Visitor() { }
exports.Visitor = Visitor;

/**
 * The `enter` method is called when a new sub-report is entered in traversal.
 *
 * @param {Object} breakdown
 *        The breakdown for the sub-report that is being entered by traversal.
 *
 * @param {Object} report
 *        The report generated by the given breakdown.
 *
 * @param {any} edge
 *        The edge leading to this sub-report. The edge is null if (but not iff!
 *        eg, null allocation stack edges) we are entering the root report.
 */
Visitor.prototype.enter = function (breakdown, report, edge) { };

/**
 * The `exit` method is called when traversal of a sub-report has finished.
 *
 * @param {Object} breakdown
 *        The breakdown for the sub-report whose traversal has finished.
 *
 * @param {Object} report
 *        The report generated by the given breakdown.
 *
 * @param {any} edge
 *        The edge leading to this sub-report. The edge is null if (but not iff!
 *        eg, null allocation stack edges) we are entering the root report.
 */
Visitor.prototype.exit = function (breakdown, report, edge) { };

/**
 * The `count` method is called when leaf nodes (reports whose breakdown is
 * by: "count") in the report tree are encountered.
 *
 * @param {Object} breakdown
 *        The count breakdown for this report.
 *
 * @param {Object} report
 *        The report generated by a breakdown by "count".
 *
 * @param {any|null} edge
 *        The edge leading to this count report. The edge is null if we are
 *        entering the root report.
 */
Visitor.prototype.count = function (breakdown, report, edge) { };

/** * getReportEdges *********************************************************/

const EDGES = Object.create(null);

EDGES.count = function (breakdown, report) {
  return [];
};

EDGES.bucket = function (breakdown, report) {
  return [];
};

EDGES.internalType = function (breakdown, report) {
  return Object.keys(report).map(key => ({
    edge: key,
    referent: report[key],
    breakdown: breakdown.then
  }));
};

EDGES.objectClass = function (breakdown, report) {
  return Object.keys(report).map(key => ({
    edge: key,
    referent: report[key],
    breakdown: key === "other" ? breakdown.other : breakdown.then
  }));
};

EDGES.coarseType = function (breakdown, report) {
  return [
    { edge: "objects", referent: report.objects, breakdown: breakdown.objects },
    { edge: "scripts", referent: report.scripts, breakdown: breakdown.scripts },
    { edge: "strings", referent: report.strings, breakdown: breakdown.strings },
    { edge: "other", referent: report.other, breakdown: breakdown.other },
  ];
};

EDGES.allocationStack = function (breakdown, report) {
  const edges = [];
  report.forEach((value, key) => {
    edges.push({
      edge: key,
      referent: value,
      breakdown: key === "noStack" ? breakdown.noStack : breakdown.then
    });
  });
  return edges;
};

EDGES.filename = function (breakdown, report) {
  return Object.keys(report).map(key => ({
    edge: key,
    referent: report[key],
    breakdown: key === "noFilename" ? breakdown.noFilename : breakdown.then
  }));
};

/**
 * Get the set of outgoing edges from `report` as specified by the given
 * breakdown.
 *
 * @param {Object} breakdown
 *        The census breakdown.
 *
 * @param {Object} report
 *        The census report.
 */
function getReportEdges(breakdown, report) {
  return EDGES[breakdown.by](breakdown, report);
}
exports.getReportEdges = getReportEdges;

/** * walk *******************************************************************/

function recursiveWalk(breakdown, edge, report, visitor) {
  if (breakdown.by === "count") {
    visitor.enter(breakdown, report, edge);
    visitor.count(breakdown, report, edge);
    visitor.exit(breakdown, report, edge);
  } else {
    visitor.enter(breakdown, report, edge);
    for (let { edge: ed, referent, breakdown: subBreakdown }
      of getReportEdges(breakdown, report)) {
      recursiveWalk(subBreakdown, ed, referent, visitor);
    }
    visitor.exit(breakdown, report, edge);
  }
}

/**
 * Walk the given `report` that was generated by taking a census with the
 * specified `breakdown`.
 *
 * @param {Object} breakdown
 *        The census breakdown.
 *
 * @param {Object} report
 *        The census report.
 *
 * @param {Visitor} visitor
 *        The Visitor instance to call into while traversing.
 */
function walk(breakdown, report, visitor) {
  recursiveWalk(breakdown, null, report, visitor);
}
exports.walk = walk;

/** * diff *******************************************************************/

/**
 * Return true if the object is a Map, false otherwise. Works with Map objects
 * from other globals, unlike `instanceof`.
 *
 * @returns {Boolean}
 */
function isMap(obj) {
  return Object.prototype.toString.call(obj) === "[object Map]";
}

/**
 * A Visitor for computing the difference between the census report being
 * traversed and the given other census.
 *
 * @param {Object} otherCensus
 *        The other census report.
 */
function DiffVisitor(otherCensus) {
  // The other census we are comparing against.
  this._otherCensus = otherCensus;

  // The total bytes and count of the basis census we are traversing.
  this._totalBytes = 0;
  this._totalCount = 0;

  // Stack maintaining the current corresponding sub-report for the other
  // census we are comparing against.
  this._otherCensusStack = [];

  // Stack maintaining the set of edges visited at each sub-report.
  this._edgesVisited = [new Set()];

  // The final delta census. Valid only after traversal.
  this._results = null;

  // Stack maintaining the results corresponding to each sub-report we are
  // currently traversing.
  this._resultsStack = [];
}

DiffVisitor.prototype = Object.create(Visitor.prototype);

/**
 * Given a report and an outgoing edge, get the edge's referent.
 */
DiffVisitor.prototype._get = function (report, edge) {
  if (!report) {
    return undefined;
  }
  return isMap(report) ? report.get(edge) : report[edge];
};

/**
 * Given a report, an outgoing edge, and a value, set the edge's referent to
 * the given value.
 */
DiffVisitor.prototype._set = function (report, edge, val) {
  if (isMap(report)) {
    report.set(edge, val);
  } else {
    report[edge] = val;
  }
};

/**
 * @overrides Visitor.prototype.enter
 */
DiffVisitor.prototype.enter = function (breakdown, report, edge) {
  const newResults = breakdown.by === "allocationStack" ? new Map() : {};
  let newOther;

  if (!this._results) {
    // This is the first time we have entered a sub-report.
    this._results = newResults;
    newOther = this._otherCensus;
  } else {
    const topResults = this._resultsStack[this._resultsStack.length - 1];
    this._set(topResults, edge, newResults);

    const topOther = this._otherCensusStack[this._otherCensusStack.length - 1];
    newOther = this._get(topOther, edge);
  }

  this._resultsStack.push(newResults);
  this._otherCensusStack.push(newOther);

  const visited = this._edgesVisited[this._edgesVisited.length - 1];
  visited.add(edge);
  this._edgesVisited.push(new Set());
};

/**
 * @overrides Visitor.prototype.exit
 */
DiffVisitor.prototype.exit = function (breakdown, report, edge) {
  // Find all the edges in the other census report that were not traversed and
  // add them to the results directly.
  const other = this._otherCensusStack[this._otherCensusStack.length - 1];
  if (other) {
    const visited = this._edgesVisited[this._edgesVisited.length - 1];
    const unvisited = getReportEdges(breakdown, other)
      .map(e => e.edge)
      .filter(e => !visited.has(e));
    const results = this._resultsStack[this._resultsStack.length - 1];
    for (let edg of unvisited) {
      this._set(results, edg, this._get(other, edg));
    }
  }

  this._otherCensusStack.pop();
  this._resultsStack.pop();
  this._edgesVisited.pop();
};

/**
 * @overrides Visitor.prototype.count
 */
DiffVisitor.prototype.count = function (breakdown, report, edge) {
  const other = this._otherCensusStack[this._otherCensusStack.length - 1];
  const results = this._resultsStack[this._resultsStack.length - 1];

  if (breakdown.count) {
    this._totalCount += report.count;
  }
  if (breakdown.bytes) {
    this._totalBytes += report.bytes;
  }

  if (other) {
    if (breakdown.count) {
      results.count = other.count - report.count;
    }
    if (breakdown.bytes) {
      results.bytes = other.bytes - report.bytes;
    }
  } else {
    if (breakdown.count) {
      results.count = -report.count;
    }
    if (breakdown.bytes) {
      results.bytes = -report.bytes;
    }
  }
};

const basisTotalBytes = exports.basisTotalBytes = Symbol("basisTotalBytes");
const basisTotalCount = exports.basisTotalCount = Symbol("basisTotalCount");

/**
 * Get the resulting report of the difference between the traversed census
 * report and the other census report.
 *
 * @returns {Object}
 *          The delta census report.
 */
DiffVisitor.prototype.results = function () {
  if (!this._results) {
    throw new Error("Attempt to get results before computing diff!");
  }

  if (this._resultsStack.length) {
    throw new Error("Attempt to get results while still computing diff!");
  }

  this._results[basisTotalBytes] = this._totalBytes;
  this._results[basisTotalCount] = this._totalCount;

  return this._results;
};

/**
 * Take the difference between two censuses. The resulting delta report
 * contains the number/size of things that are in the `endCensus` that are not
 * in the `startCensus`.
 *
 * @param {Object} breakdown
 *        The breakdown used to generate both census reports.
 *
 * @param {Object} startCensus
 *        The first census report.
 *
 * @param {Object} endCensus
 *        The second census report.
 *
 * @returns {Object}
 *          A delta report mirroring the structure of the two census reports (as
 *          specified by the given breakdown). Has two additional properties:
 *            - {Number} basisTotalBytes: the total number of bytes in the start
 *                                        census.
 *            - {Number} basisTotalCount: the total count in the start census.
 */
function diff(breakdown, startCensus, endCensus) {
  const visitor = new DiffVisitor(endCensus);
  walk(breakdown, startCensus, visitor);
  return visitor.results();
}
exports.diff = diff;

/**
 * Creates a hash map mapping node IDs to its parent node.
 *
 * @param {CensusTreeNode} node
 * @param {Object<number, TreeNode>} aggregator
 *
 * @return {Object<number, TreeNode>}
 */
const createParentMap = function (node, getId = n => n.id, aggregator = {}) {
  if (node.children) {
    for (let i = 0, length = node.children.length; i < length; i++) {
      const child = node.children[i];
      aggregator[getId(child)] = node;
      createParentMap(child, getId, aggregator);
    }
  }
  return aggregator;
};
exports.createParentMap = createParentMap;

const BUCKET = Object.freeze({ by: "bucket" });

/**
 * Convert a breakdown whose leaves are { by: "count" } to an identical
 * breakdown, except with { by: "bucket" } leaves.
 *
 * @param {Object} breakdown
 * @returns {Object}
 */
exports.countToBucketBreakdown = function (breakdown) {
  if (typeof breakdown !== "object" || !breakdown) {
    return breakdown;
  }

  if (breakdown.by === "count") {
    return BUCKET;
  }

  const keys = Object.keys(breakdown);
  const vals = keys.reduce((vs, k) => {
    vs.push(exports.countToBucketBreakdown(breakdown[k]));
    return vs;
  }, []);

  const result = {};
  for (let i = 0, length = keys.length; i < length; i++) {
    result[keys[i]] = vals[i];
  }

  return Object.freeze(result);
};

/**
 * A Visitor for finding report leaves by their DFS index.
 */
function GetLeavesVisitor(targetIndices) {
  this._index = -1;
  this._targetIndices = targetIndices;
  this._leaves = [];
}

GetLeavesVisitor.prototype = Object.create(Visitor.prototype);

/**
 * @overrides Visitor.prototype.enter
 */
GetLeavesVisitor.prototype.enter = function (breakdown, report, edge) {
  this._index++;
  if (this._targetIndices.has(this._index)) {
    this._leaves.push(report);
  }
};

/**
 * Get the accumulated report leaves after traversal.
 */
GetLeavesVisitor.prototype.leaves = function () {
  if (this._index === -1) {
    throw new Error("Attempt to call `leaves` before traversing report!");
  }
  return this._leaves;
};

/**
 * Given a set of indices of leaves in a pre-order depth-first traversal of the
 * given census report, return the leaves.
 *
 * @param {Set<Number>} indices
 * @param {Object} breakdown
 * @param {Object} report
 *
 * @returns {Array<Object>}
 */
exports.getReportLeaves = function (indices, breakdown, report) {
  const visitor = new GetLeavesVisitor(indices);
  walk(breakdown, report, visitor);
  return visitor.leaves();
};

/**
 * Get a list of the individual node IDs that belong to the census report leaves
 * of the given indices.
 *
 * @param {Set<Number>} indices
 * @param {Object} breakdown
 * @param {HeapSnapshot} snapshot
 *
 * @returns {Array<NodeId>}
 */
exports.getCensusIndividuals = function (indices, countBreakdown, snapshot) {
  const bucketBreakdown = exports.countToBucketBreakdown(countBreakdown);
  const bucketReport = snapshot.takeCensus({ breakdown: bucketBreakdown });
  const buckets = exports.getReportLeaves(indices,
                                          bucketBreakdown,
                                          bucketReport);
  return flatten(buckets);
};
PK
!<��E�)�)Ichrome/devtools/modules/devtools/shared/heapsnapshot/DominatorTreeNode.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { immutableUpdate } = require("resource://devtools/shared/ThreadSafeDevToolsUtils.js");
const { Visitor, walk } = require("resource://devtools/shared/heapsnapshot/CensusUtils.js");
const { deduplicatePaths } = require("resource://devtools/shared/heapsnapshot/shortest-paths");

const DEFAULT_MAX_DEPTH = 4;
const DEFAULT_MAX_SIBLINGS = 15;
const DEFAULT_MAX_NUM_PATHS = 5;

/**
 * A single node in a dominator tree.
 *
 * @param {NodeId} nodeId
 * @param {NodeSize} retainedSize
 */
function DominatorTreeNode(nodeId, label, shallowSize, retainedSize) {
  // The id of this node.
  this.nodeId = nodeId;

  // The label structure generated by describing the given node.
  this.label = label;

  // The shallow size of this node.
  this.shallowSize = shallowSize;

  // The retained size of this node.
  this.retainedSize = retainedSize;

  // The id of this node's parent or undefined if this node is the root.
  this.parentId = undefined;

  // An array of immediately dominated child `DominatorTreeNode`s, or undefined.
  this.children = undefined;

  // An object of the form returned by `deduplicatePaths`, encoding the set of
  // the N shortest retaining paths for this node as a graph.
  this.shortestPaths = undefined;

  // True iff the `children` property does not contain every immediately
  // dominated node.
  //
  // * If children is an array and this property is true: the array does not
  //   contain the complete set of immediately dominated children.
  // * If children is an array and this property is false: the array contains
  //   the complete set of immediately dominated children.
  // * If children is undefined and this property is true: there exist
  //   immediately dominated children for this node, but they have not been
  //   loaded yet.
  // * If children is undefined and this property is false: this node does not
  //   dominate any others and therefore has no children.
  this.moreChildrenAvailable = true;
}

DominatorTreeNode.prototype = null;

module.exports = DominatorTreeNode;

/**
 * Add `child` to the `parent`'s set of children.
 *
 * @param {DominatorTreeNode} parent
 * @param {DominatorTreeNode} child
 */
DominatorTreeNode.addChild = function (parent, child) {
  if (parent.children === undefined) {
    parent.children = [];
  }

  parent.children.push(child);
  child.parentId = parent.nodeId;
};

/**
 * A Visitor that is used to generate a label for a node in the heap snapshot
 * and get its shallow size as well while we are at it.
 */
function LabelAndShallowSizeVisitor() {
  // As we walk the description, we accumulate edges in this array.
  this._labelPieces = [];

  // Once we reach the non-zero count leaf node in the description, we move the
  // labelPieces here to signify that we no longer need to accumulate edges.
  this._label = undefined;

  // Once we reach the non-zero count leaf node in the description, we grab the
  // shallow size and place it here.
  this._shallowSize = 0;
}

DominatorTreeNode.LabelAndShallowSizeVisitor = LabelAndShallowSizeVisitor;

LabelAndShallowSizeVisitor.prototype = Object.create(Visitor);

/**
 * @overrides Visitor.prototype.enter
 */
LabelAndShallowSizeVisitor.prototype.enter = function (breakdown, report, edge) {
  if (this._labelPieces && edge) {
    this._labelPieces.push(edge);
  }
};

/**
 * @overrides Visitor.prototype.exit
 */
LabelAndShallowSizeVisitor.prototype.exit = function (breakdown, report, edge) {
  if (this._labelPieces && edge) {
    this._labelPieces.pop();
  }
};

/**
 * @overrides Visitor.prototype.count
 */
LabelAndShallowSizeVisitor.prototype.count = function (breakdown, report, edge) {
  if (report.count === 0) {
    return;
  }

  this._label = this._labelPieces;
  this._labelPieces = undefined;

  this._shallowSize = report.bytes;
};

/**
 * Get the generated label structure accumulated by this visitor.
 *
 * @returns {Object}
 */
LabelAndShallowSizeVisitor.prototype.label = function () {
  return this._label;
};

/**
 * Get the shallow size of the node this visitor visited.
 *
 * @returns {Number}
 */
LabelAndShallowSizeVisitor.prototype.shallowSize = function () {
  return this._shallowSize;
};

/**
 * Generate a label structure for the node with the given id and grab its
 * shallow size.
 *
 * What is a "label" structure? HeapSnapshot.describeNode essentially takes a
 * census of a single node rather than the whole heap graph. The resulting
 * report has only one count leaf that is non-zero. The label structure is the
 * path in this report from the root to the non-zero count leaf.
 *
 * @param {Number} nodeId
 * @param {HeapSnapshot} snapshot
 * @param {Object} breakdown
 *
 * @returns {Object}
 *          An object with the following properties:
 *          - {Number} shallowSize
 *          - {Object} label
 */
DominatorTreeNode.getLabelAndShallowSize = function (nodeId,
                                                     snapshot,
                                                     breakdown) {
  const description = snapshot.describeNode(breakdown, nodeId);

  const visitor = new LabelAndShallowSizeVisitor();
  walk(breakdown, description, visitor);

  return {
    label: visitor.label(),
    shallowSize: visitor.shallowSize(),
  };
};

/**
 * Do a partial traversal of the given dominator tree and convert it into a tree
 * of `DominatorTreeNode`s. Dominator trees have a node for every node in the
 * snapshot's heap graph, so we must not allocate a JS object for every node. It
 * would be way too many and the node count is effectively unbounded.
 *
 * Go no deeper down the tree than `maxDepth` and only consider at most
 * `maxSiblings` within any single node's children.
 *
 * @param {DominatorTree} dominatorTree
 * @param {HeapSnapshot} snapshot
 * @param {Object} breakdown
 * @param {Number} maxDepth
 * @param {Number} maxSiblings
 *
 * @returns {DominatorTreeNode}
 */
DominatorTreeNode.partialTraversal = function (dominatorTree,
                                               snapshot,
                                               breakdown,
                                               maxDepth = DEFAULT_MAX_DEPTH,
                                               maxSiblings = DEFAULT_MAX_SIBLINGS) {
  function dfs(nodeId, depth) {
    const { label, shallowSize } =
      DominatorTreeNode.getLabelAndShallowSize(nodeId, snapshot, breakdown);
    const retainedSize = dominatorTree.getRetainedSize(nodeId);
    const node = new DominatorTreeNode(nodeId, label, shallowSize, retainedSize);
    const childNodeIds = dominatorTree.getImmediatelyDominated(nodeId);

    const newDepth = depth + 1;
    if (newDepth < maxDepth) {
      const endIdx = Math.min(childNodeIds.length, maxSiblings);
      for (let i = 0; i < endIdx; i++) {
        DominatorTreeNode.addChild(node, dfs(childNodeIds[i], newDepth));
      }
      node.moreChildrenAvailable = endIdx < childNodeIds.length;
    } else {
      node.moreChildrenAvailable = childNodeIds.length > 0;
    }

    return node;
  }

  return dfs(dominatorTree.root, 0);
};

/**
 * Insert more children into the given (partially complete) dominator tree.
 *
 * The tree is updated in an immutable and persistent manner: a new tree is
 * returned, but all unmodified subtrees (which is most) are shared with the
 * original tree. Only the modified nodes are re-allocated.
 *
 * @param {DominatorTreeNode} tree
 * @param {Array<NodeId>} path
 * @param {Array<DominatorTreeNode>} newChildren
 * @param {Boolean} moreChildrenAvailable
 *
 * @returns {DominatorTreeNode}
 */
DominatorTreeNode.insert = function (nodeTree, path, newChildren, moreChildrenAvailable) {
  function insert(tree, i) {
    if (tree.nodeId !== path[i]) {
      return tree;
    }

    if (i == path.length - 1) {
      return immutableUpdate(tree, {
        children: (tree.children || []).concat(newChildren),
        moreChildrenAvailable,
      });
    }

    return tree.children
      ? immutableUpdate(tree, {
        children: tree.children.map(c => insert(c, i + 1))
      })
      : tree;
  }

  return insert(nodeTree, 0);
};

/**
 * Get the new canonical node with the given `id` in `tree` that exists along
 * `path`. If there is no such node along `path`, return null.
 *
 * This is useful if we have a reference to a now-outdated DominatorTreeNode due
 * to a recent call to DominatorTreeNode.insert and want to get the up-to-date
 * version. We don't have to walk the whole tree: if there is an updated version
 * of the node then it *must* be along the path.
 *
 * @param {NodeId} id
 * @param {DominatorTreeNode} tree
 * @param {Array<NodeId>} path
 *
 * @returns {DominatorTreeNode|null}
 */
DominatorTreeNode.getNodeByIdAlongPath = function (id, tree, path) {
  function find(node, i) {
    if (!node || node.nodeId !== path[i]) {
      return null;
    }

    if (node.nodeId === id) {
      return node;
    }

    if (i === path.length - 1 || !node.children) {
      return null;
    }

    const nextId = path[i + 1];
    return find(node.children.find(c => c.nodeId === nextId), i + 1);
  }

  return find(tree, 0);
};

/**
 * Find the shortest retaining paths for the given set of DominatorTreeNodes,
 * and populate each node's `shortestPaths` property with them in place.
 *
 * @param {HeapSnapshot} snapshot
 * @param {Object} breakdown
 * @param {NodeId} start
 * @param {Array<DominatorTreeNode>} treeNodes
 * @param {Number} maxNumPaths
 */
DominatorTreeNode.attachShortestPaths = function (snapshot,
                                                  breakdown,
                                                  start,
                                                  treeNodes,
                                                  maxNumPaths = DEFAULT_MAX_NUM_PATHS) {
  const idToTreeNode = new Map();
  const targets = [];
  for (let node of treeNodes) {
    const id = node.nodeId;
    idToTreeNode.set(id, node);
    targets.push(id);
  }

  const shortestPaths = snapshot.computeShortestPaths(start,
                                                      targets,
                                                      maxNumPaths);

  for (let [target, paths] of shortestPaths) {
    const deduped = deduplicatePaths(target, paths);
    deduped.nodes = deduped.nodes.map(id => {
      const { label } =
        DominatorTreeNode.getLabelAndShallowSize(id, snapshot, breakdown);
      return { id, label };
    });

    idToTreeNode.get(target).shortestPaths = deduped;
  }
};
PK
!<r�ki,,Jchrome/devtools/modules/devtools/shared/heapsnapshot/HeapAnalysesClient.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 DevToolsUtils = require("devtools/shared/DevToolsUtils");
const { DevToolsWorker } = require("devtools/shared/worker/worker");

const WORKER_URL =
  "resource://devtools/shared/heapsnapshot/HeapAnalysesWorker.js";
var workerCounter = 0;

/**
 * A HeapAnalysesClient instance provides a developer-friendly interface for
 * interacting with a HeapAnalysesWorker. This enables users to be ignorant of
 * the message passing protocol used to communicate with the worker. The
 * HeapAnalysesClient owns the worker, and terminating the worker is done by
 * terminating the client (see the `destroy` method).
 */
const HeapAnalysesClient = module.exports = function () {
  this._worker = new DevToolsWorker(WORKER_URL, {
    name: `HeapAnalyses-${workerCounter++}`,
    verbose: DevToolsUtils.dumpv.wantVerbose
  });
};

/**
 * Destroy the worker, causing it to release its resources (such as heap
 * snapshots it has deserialized and read into memory). The client is no longer
 * usable after calling this method.
 */
HeapAnalysesClient.prototype.destroy = function () {
  this._worker.destroy();
  this._worker = null;
};

/**
 * Tell the worker to read into memory the heap snapshot at the given file
 * path. This is a prerequisite for asking the worker to perform various
 * analyses on a heap snapshot.
 *
 * @param {String} snapshotFilePath
 *
 * @returns Promise
 *          The promise is fulfilled if the heap snapshot is successfully
 *          deserialized and read into memory. The promise is rejected if that
 *          does not happen, eg due to a bad file path or malformed heap
 *          snapshot file.
 */
HeapAnalysesClient.prototype.readHeapSnapshot = function (snapshotFilePath) {
  return this._worker.performTask("readHeapSnapshot", { snapshotFilePath });
};

/**
 * Tell the worker to delete all references to the snapshot and dominator trees
 * linked to the provided snapshot file path.
 *
 * @param {String} snapshotFilePath
 * @return Promise<undefined>
 */
HeapAnalysesClient.prototype.deleteHeapSnapshot = function (snapshotFilePath) {
  return this._worker.performTask("deleteHeapSnapshot", { snapshotFilePath });
};

/**
 * Request the creation time given a snapshot file path. Returns `null`
 * if snapshot does not exist.
 *
 * @param {String} snapshotFilePath
 *        The path to the snapshot.
 * @return {Number?}
 *        The unix timestamp of the creation time of the snapshot, or null if
 *        snapshot does not exist.
 */
HeapAnalysesClient.prototype.getCreationTime = function (snapshotFilePath) {
  return this._worker.performTask("getCreationTime", snapshotFilePath);
};

/** * Censuses *****************************************************************/

/**
 * Ask the worker to perform a census analysis on the heap snapshot with the
 * given path. The heap snapshot at the given path must have already been read
 * into memory by the worker (see `readHeapSnapshot`).
 *
 * @param {String} snapshotFilePath
 *
 * @param {Object} censusOptions
 *        A structured-cloneable object specifying the requested census's
 *        breakdown. See the "takeCensus" section of
 *        `js/src/doc/Debugger/Debugger.Memory.md` for detailed documentation.
 *
 * @param {Object} requestOptions
 *        An object specifying options of this worker request.
 *        - {Boolean} asTreeNode
 *          Whether or not the census is returned as a CensusTreeNode,
 *          or just a breakdown report. Defaults to false.
 *          @see `devtools/shared/heapsnapshot/census-tree-node.js`
 *        - {Boolean} asInvertedTreeNode
 *          Whether or not the census is returned as an inverted
 *          CensusTreeNode. Defaults to false.
 *        - {String} filter
 *          A filter string to prune the resulting tree with. Only applies if
 *          either asTreeNode or asInvertedTreeNode is true.
 *
 * @returns Promise<Object>
 *          An object with the following properties:
 *          - report:
 *            The report generated by the given census breakdown, or a
 *            CensusTreeNode generated by the given census breakdown if
 *            `asTreeNode` is true.
 *          - parentMap:
 *            The result of calling CensusUtils.createParentMap on the generated
 *            report. Only exists if asTreeNode or asInvertedTreeNode are set.
 */
HeapAnalysesClient.prototype.takeCensus = function (snapshotFilePath,
                                                    censusOptions,
                                                    requestOptions = {}) {
  return this._worker.performTask("takeCensus", {
    snapshotFilePath,
    censusOptions,
    requestOptions,
  });
};

/**
 * Get the individual nodes that correspond to the given census report leaf
 * indices.
 *
 * @param {Object} opts
 *        An object with the following properties:
 *        - {DominatorTreeId} dominatorTreeId: The id of the dominator tree.
 *        - {Set<Number>} indices: The indices of the census report leaves we
 *          would like to get the individuals for.
 *        - {Object} censusBreakdown: The breakdown used to generate the census.
 *        - {Object} labelBreakdown: The breakdown we would like to use when
 *          labeling the resulting nodes.
 *        - {Number} maxRetainingPaths: The maximum number of retaining paths to
 *          compute for each node.
 *        - {Number} maxIndividuals: The maximum number of individual nodes to
 *          return.
 *
 * @returns {Promise<Object>}
 *          A promise of an object with the following properties:
 *          - {Array<DominatorTreeNode>} nodes: An array of `DominatorTreeNode`s
 *            with their shortest paths attached, and without any dominator tree
 *            child/parent information attached. The results are sorted by
 *            retained size.
 *
 */
HeapAnalysesClient.prototype.getCensusIndividuals = function (opts) {
  return this._worker.performTask("getCensusIndividuals", opts);
};

/**
 * Request that the worker take a census on the heap snapshots with the given
 * paths and then return the difference between them. Both heap snapshots must
 * have already been read into memory by the worker (see `readHeapSnapshot`).
 *
 * @param {String} firstSnapshotFilePath
 *        The first snapshot file path.
 *
 * @param {String} secondSnapshotFilePath
 *        The second snapshot file path.
 *
 * @param {Object} censusOptions
 *        A structured-cloneable object specifying the requested census's
 *        breakdown. See the "takeCensus" section of
 *        `js/src/doc/Debugger/Debugger.Memory.md` for detailed documentation.
 *
 * @param {Object} requestOptions
 *        An object specifying options for this request.
 *        - {Boolean} asTreeNode
 *          Whether the resulting delta report should be converted to a census
 *          tree node before returned. Defaults to false.
 *        - {Boolean} asInvertedTreeNode
 *          Whether or not the census is returned as an inverted
 *          CensusTreeNode. Defaults to false.
 *        - {String} filter
 *          A filter string to prune the resulting tree with. Only applies if
 *          either asTreeNode or asInvertedTreeNode is true.
 *
 * @returns Promise<Object>
 *          - delta:
 *            The delta report generated by diffing the two census reports, or a
 *            CensusTreeNode generated from the delta report if
 *            `requestOptions.asTreeNode` was true.
 *          - parentMap:
 *            The result of calling CensusUtils.createParentMap on the generated
 *            delta. Only exists if asTreeNode or asInvertedTreeNode are set.
 */
HeapAnalysesClient.prototype.takeCensusDiff = function (firstSnapshotFilePath,
                                                        secondSnapshotFilePath,
                                                        censusOptions,
                                                        requestOptions = {}) {
  return this._worker.performTask("takeCensusDiff", {
    firstSnapshotFilePath,
    secondSnapshotFilePath,
    censusOptions,
    requestOptions
  });
};

/** * Dominator Trees **********************************************************/

/**
 * Compute the dominator tree of the heap snapshot loaded from the given file
 * path. Returns the id of the computed dominator tree.
 *
 * @param {String} snapshotFilePath
 *
 * @returns {Promise<DominatorTreeId>}
 */
HeapAnalysesClient.prototype.computeDominatorTree = function (snapshotFilePath) {
  return this._worker.performTask("computeDominatorTree", snapshotFilePath);
};

/**
 * Get the initial, partial view of the dominator tree with the given id.
 *
 * @param {Object} opts
 *        An object specifying options for this request.
 *        - {DominatorTreeId} dominatorTreeId
 *          The id of the dominator tree.
 *        - {Object} breakdown
 *          The breakdown used to generate node labels.
 *        - {Number} maxDepth
 *          The maximum depth to traverse down the tree to create this initial
 *          view.
 *        - {Number} maxSiblings
 *          The maximum number of siblings to visit within each traversed node's
 *          children.
 *        - {Number} maxRetainingPaths
 *          The maximum number of retaining paths to find for each node.
 *
 * @returns {Promise<DominatorTreeNode>}
 */
HeapAnalysesClient.prototype.getDominatorTree = function (opts) {
  return this._worker.performTask("getDominatorTree", opts);
};

/**
 * Get a subset of a nodes children in the dominator tree.
 *
 * @param {Object} opts
 *        An object specifying options for this request.
 *        - {DominatorTreeId} dominatorTreeId
 *          The id of the dominator tree.
 *        - {NodeId} nodeId
 *          The id of the node whose children are being found.
 *        - {Object} breakdown
 *          The breakdown used to generate node labels.
 *        - {Number} startIndex
 *          The starting index within the full set of immediately dominated
 *          children of the children being requested. Children are always sorted
 *          by greatest to least retained size.
 *        - {Number} maxCount
 *          The maximum number of children to return.
 *        - {Number} maxRetainingPaths
 *          The maximum number of retaining paths to find for each node.
 *
 * @returns {Promise<Object>}
 *          A promise of an object with the following properties:
 *          - {Array<DominatorTreeNode>} nodes
 *            The requested nodes that are immediately dominated by the node
 *            identified by `opts.nodeId`.
 *          - {Boolean} moreChildrenAvailable
 *            True iff there are more children available after the returned
 *            nodes.
 *          - {Array<NodeId>} path
 *            The path through the tree from the root to these node's parent, eg
 *            [root's id, child of root's id, child of child of root's id, ..., `nodeId`].
 */
HeapAnalysesClient.prototype.getImmediatelyDominated = function (opts) {
  return this._worker.performTask("getImmediatelyDominated", opts);
};
PK
!<
��G&G&Jchrome/devtools/modules/devtools/shared/heapsnapshot/HeapAnalysesWorker.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 ChromeUtils*/

// This is a worker which reads offline heap snapshots into memory and performs
// heavyweight analyses on them without blocking the main thread. A
// HeapAnalysesWorker is owned and communicated with by a HeapAnalysesClient
// instance. See HeapAnalysesClient.js.

/* global importScripts, workerHelper, self */

"use strict";

importScripts("resource://gre/modules/workers/require.js");
importScripts("resource://devtools/shared/worker/helper.js");
const { censusReportToCensusTreeNode } = require("resource://devtools/shared/heapsnapshot/census-tree-node.js");
const DominatorTreeNode = require("resource://devtools/shared/heapsnapshot/DominatorTreeNode.js");
const CensusUtils = require("resource://devtools/shared/heapsnapshot/CensusUtils.js");

const DEFAULT_START_INDEX = 0;
const DEFAULT_MAX_COUNT = 50;

/**
 * The set of HeapSnapshot instances this worker has read into memory. Keyed by
 * snapshot file path.
 */
const snapshots = Object.create(null);

/**
 * The set of `DominatorTree`s that have been computed, mapped by their id (aka
 * the index into this array).
 *
 * @see /dom/webidl/DominatorTree.webidl
 */
const dominatorTrees = [];

/**
 * The i^th HeapSnapshot in this array is the snapshot used to generate the i^th
 * dominator tree in `dominatorTrees` above. This lets us map from a dominator
 * tree id to the snapshot it came from.
 */
const dominatorTreeSnapshots = [];

/**
 * @see HeapAnalysesClient.prototype.readHeapSnapshot
 */
workerHelper.createTask(self, "readHeapSnapshot", ({ snapshotFilePath }) => {
  snapshots[snapshotFilePath] =
    ChromeUtils.readHeapSnapshot(snapshotFilePath);
  return true;
});

/**
 * @see HeapAnalysesClient.prototype.deleteHeapSnapshot
 */
workerHelper.createTask(self, "deleteHeapSnapshot", ({ snapshotFilePath }) => {
  let snapshot = snapshots[snapshotFilePath];
  if (!snapshot) {
    throw new Error(`No known heap snapshot for '${snapshotFilePath}'`);
  }

  snapshots[snapshotFilePath] = undefined;

  let dominatorTreeId = dominatorTreeSnapshots.indexOf(snapshot);
  if (dominatorTreeId != -1) {
    dominatorTreeSnapshots[dominatorTreeId] = undefined;
    dominatorTrees[dominatorTreeId] = undefined;
  }
});

/**
 * @see HeapAnalysesClient.prototype.takeCensus
 */
workerHelper.createTask(self, "takeCensus", (
{ snapshotFilePath, censusOptions, requestOptions }) => {
  if (!snapshots[snapshotFilePath]) {
    throw new Error(`No known heap snapshot for '${snapshotFilePath}'`);
  }

  let report = snapshots[snapshotFilePath].takeCensus(censusOptions);
  let parentMap;

  if (requestOptions.asTreeNode || requestOptions.asInvertedTreeNode) {
    const opts = { filter: requestOptions.filter || null };
    if (requestOptions.asInvertedTreeNode) {
      opts.invert = true;
    }
    report = censusReportToCensusTreeNode(censusOptions.breakdown, report, opts);
    parentMap = CensusUtils.createParentMap(report);
  }

  return { report, parentMap };
});

/**
 * @see HeapAnalysesClient.prototype.getCensusIndividuals
 */
workerHelper.createTask(self, "getCensusIndividuals", request => {
  const {
    dominatorTreeId,
    indices,
    censusBreakdown,
    labelBreakdown,
    maxRetainingPaths,
    maxIndividuals,
  } = request;

  const dominatorTree = dominatorTrees[dominatorTreeId];
  if (!dominatorTree) {
    throw new Error(
      `There does not exist a DominatorTree with the id ${dominatorTreeId}`);
  }

  const snapshot = dominatorTreeSnapshots[dominatorTreeId];
  const nodeIds = CensusUtils.getCensusIndividuals(indices, censusBreakdown, snapshot);

  const nodes = nodeIds
    .sort((a, b) => dominatorTree.getRetainedSize(b) - dominatorTree.getRetainedSize(a))
    .slice(0, maxIndividuals)
    .map(id => {
      const { label, shallowSize } =
        DominatorTreeNode.getLabelAndShallowSize(id, snapshot, labelBreakdown);
      const retainedSize = dominatorTree.getRetainedSize(id);
      const node = new DominatorTreeNode(id, label, shallowSize, retainedSize);
      node.moreChildrenAvailable = false;
      return node;
    });

  DominatorTreeNode.attachShortestPaths(snapshot,
                                        labelBreakdown,
                                        dominatorTree.root,
                                        nodes,
                                        maxRetainingPaths);

  return { nodes };
});

/**
 * @see HeapAnalysesClient.prototype.takeCensusDiff
 */
workerHelper.createTask(self, "takeCensusDiff", request => {
  const {
    firstSnapshotFilePath,
    secondSnapshotFilePath,
    censusOptions,
    requestOptions
  } = request;

  if (!snapshots[firstSnapshotFilePath]) {
    throw new Error(`No known heap snapshot for '${firstSnapshotFilePath}'`);
  }

  if (!snapshots[secondSnapshotFilePath]) {
    throw new Error(`No known heap snapshot for '${secondSnapshotFilePath}'`);
  }

  const first = snapshots[firstSnapshotFilePath].takeCensus(censusOptions);
  const second = snapshots[secondSnapshotFilePath].takeCensus(censusOptions);
  let delta = CensusUtils.diff(censusOptions.breakdown, first, second);
  let parentMap;

  if (requestOptions.asTreeNode || requestOptions.asInvertedTreeNode) {
    const opts = { filter: requestOptions.filter || null };
    if (requestOptions.asInvertedTreeNode) {
      opts.invert = true;
    }
    delta = censusReportToCensusTreeNode(censusOptions.breakdown, delta, opts);
    parentMap = CensusUtils.createParentMap(delta);
  }

  return { delta, parentMap };
});

/**
 * @see HeapAnalysesClient.prototype.getCreationTime
 */
workerHelper.createTask(self, "getCreationTime", snapshotFilePath => {
  if (!snapshots[snapshotFilePath]) {
    throw new Error(`No known heap snapshot for '${snapshotFilePath}'`);
  }
  return snapshots[snapshotFilePath].creationTime;
});

/**
 * @see HeapAnalysesClient.prototype.computeDominatorTree
 */
workerHelper.createTask(self, "computeDominatorTree", snapshotFilePath => {
  const snapshot = snapshots[snapshotFilePath];
  if (!snapshot) {
    throw new Error(`No known heap snapshot for '${snapshotFilePath}'`);
  }

  const id = dominatorTrees.length;
  dominatorTrees.push(snapshot.computeDominatorTree());
  dominatorTreeSnapshots.push(snapshot);
  return id;
});

/**
 * @see HeapAnalysesClient.prototype.getDominatorTree
 */
workerHelper.createTask(self, "getDominatorTree", request => {
  const {
    dominatorTreeId,
    breakdown,
    maxDepth,
    maxSiblings,
    maxRetainingPaths,
  } = request;

  if (!(dominatorTreeId >= 0 && dominatorTreeId < dominatorTrees.length)) {
    throw new Error(
      `There does not exist a DominatorTree with the id ${dominatorTreeId}`);
  }

  const dominatorTree = dominatorTrees[dominatorTreeId];
  const snapshot = dominatorTreeSnapshots[dominatorTreeId];

  const tree = DominatorTreeNode.partialTraversal(dominatorTree,
                                                  snapshot,
                                                  breakdown,
                                                  maxDepth,
                                                  maxSiblings);

  const nodes = [];
  (function getNodes(node) {
    nodes.push(node);
    if (node.children) {
      for (let i = 0, length = node.children.length; i < length; i++) {
        getNodes(node.children[i]);
      }
    }
  }(tree));

  DominatorTreeNode.attachShortestPaths(snapshot,
                                        breakdown,
                                        dominatorTree.root,
                                        nodes,
                                        maxRetainingPaths);

  return tree;
});

/**
 * @see HeapAnalysesClient.prototype.getImmediatelyDominated
 */
workerHelper.createTask(self, "getImmediatelyDominated", request => {
  const {
    dominatorTreeId,
    nodeId,
    breakdown,
    startIndex,
    maxCount,
    maxRetainingPaths,
  } = request;

  if (!(dominatorTreeId >= 0 && dominatorTreeId < dominatorTrees.length)) {
    throw new Error(
      `There does not exist a DominatorTree with the id ${dominatorTreeId}`);
  }

  const dominatorTree = dominatorTrees[dominatorTreeId];
  const snapshot = dominatorTreeSnapshots[dominatorTreeId];

  const childIds = dominatorTree.getImmediatelyDominated(nodeId);
  if (!childIds) {
    throw new Error(`${nodeId} is not a node id in the dominator tree`);
  }

  const start = startIndex || DEFAULT_START_INDEX;
  const count = maxCount || DEFAULT_MAX_COUNT;
  const end = start + count;

  const nodes = childIds
    .slice(start, end)
    .map(id => {
      const { label, shallowSize } =
        DominatorTreeNode.getLabelAndShallowSize(id, snapshot, breakdown);
      const retainedSize = dominatorTree.getRetainedSize(id);
      const node = new DominatorTreeNode(id, label, shallowSize, retainedSize);
      node.parentId = nodeId;
      // DominatorTree.getImmediatelyDominated will always return non-null here
      // because we got the id directly from the dominator tree.
      node.moreChildrenAvailable = dominatorTree.getImmediatelyDominated(id).length > 0;
      return node;
    });

  const path = [];
  let id = nodeId;
  do {
    path.push(id);
    id = dominatorTree.getImmediateDominator(id);
  } while (id !== null);
  path.reverse();

  const moreChildrenAvailable = childIds.length > end;

  DominatorTreeNode.attachShortestPaths(snapshot,
                                        breakdown,
                                        dominatorTree.root,
                                        nodes,
                                        maxRetainingPaths);

  return { nodes, moreChildrenAvailable, path };
});
PK
!<7A��..Mchrome/devtools/modules/devtools/shared/heapsnapshot/HeapSnapshotFileUtils.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Heap snapshots are always saved in the temp directory, and have a regular
// naming convention. This module provides helpers for working with heap
// snapshot files in a safe manner. Because we attempt to avoid unnecessary
// copies of the heap snapshot files by checking the local filesystem for a heap
// snapshot file with the given snapshot id, we want to ensure that we are only
// attempting to open heap snapshot files and not `~/.ssh/id_rsa`, for
// example. Therefore, the RDP only talks about snapshot ids, or transfering the
// bulk file data. A file path can be recovered from a snapshot id, which allows
// one to check for the presence of the heap snapshot file on the local file
// system, but we don't have to worry about opening arbitrary files.
//
// The heap snapshot file path conventions permits the following forms:
//
//     $TEMP_DIRECTORY/XXXXXXXXXX.fxsnapshot
//     $TEMP_DIRECTORY/XXXXXXXXXX-XXXXX.fxsnapshot
//
// Where the strings of "X" are zero or more digits.

"use strict";

const { Ci } = require("chrome");
loader.lazyRequireGetter(this, "FileUtils",
                         "resource://gre/modules/FileUtils.jsm", true);
loader.lazyRequireGetter(this, "OS", "resource://gre/modules/osfile.jsm", true);

function getHeapSnapshotFileTemplate() {
  return OS.Path.join(OS.Constants.Path.tmpDir, `${Date.now()}.fxsnapshot`);
}

/**
 * Get a unique temp file path for a new heap snapshot. The file is guaranteed
 * not to exist before this call.
 *
 * @returns String
 */
exports.getNewUniqueHeapSnapshotTempFilePath = function () {
  let file = new FileUtils.File(getHeapSnapshotFileTemplate());
  // The call to createUnique will append "-N" after the leaf name (but before
  // the extension) until a new file is found and create it. This guarantees we
  // won't accidentally choose the same file twice.
  file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
  return file.path;
};

function isValidSnapshotFileId(snapshotId) {
  return /^\d+(\-\d+)?$/.test(snapshotId);
}

/**
 * Get the file path for the given snapshot id.
 *
 * @param {String} snapshotId
 *
 * @returns String | null
 */
exports.getHeapSnapshotTempFilePath = function (snapshotId) {
  // Don't want anyone sneaking "../../../.." strings into the snapshot id and
  // trying to make us open arbitrary files.
  if (!isValidSnapshotFileId(snapshotId)) {
    return null;
  }
  return OS.Path.join(OS.Constants.Path.tmpDir, snapshotId + ".fxsnapshot");
};

/**
 * Return true if we have the heap snapshot file for the given snapshot id on
 * the local file system. False is returned otherwise.
 *
 * @returns Promise<Boolean>
 */
exports.haveHeapSnapshotTempFile = function (snapshotId) {
  const path = exports.getHeapSnapshotTempFilePath(snapshotId);
  if (!path) {
    return Promise.resolve(false);
  }

  return OS.File.stat(path).then(() => true,
                                 () => false);
};
PK
!<��M5^]^]Hchrome/devtools/modules/devtools/shared/heapsnapshot/census-tree-node.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";

// CensusTreeNode is an intermediate representation of a census report that
// exists between after a report is generated by taking a census and before the
// report is rendered in the DOM. It must be dead simple to render, with no
// further data processing or massaging needed before rendering DOM nodes. Our
// goal is to do the census report to CensusTreeNode transformation in the
// HeapAnalysesWorker, and ensure that the **only** work that the main thread
// has to do is strictly DOM rendering work.

const {
  Visitor,
  walk,
  basisTotalBytes,
  basisTotalCount,
} = require("resource://devtools/shared/heapsnapshot/CensusUtils.js");

// Monotonically increasing integer for CensusTreeNode `id`s.
let censusTreeNodeIdCounter = 0;

/**
 * Return true if the given object is a SavedFrame stack object, false otherwise.
 *
 * @param {any} obj
 * @returns {Boolean}
 */
function isSavedFrame(obj) {
  return Object.prototype.toString.call(obj) === "[object SavedFrame]";
}

/**
 * A CensusTreeNodeCache maps from SavedFrames to CensusTreeNodes. It is used when
 * aggregating multiple SavedFrame allocation stack keys into a tree of many
 * CensusTreeNodes. Each stack may share older frames, and we want to preserve
 * this sharing when converting to CensusTreeNode, so before creating a new
 * CensusTreeNode, we look for an existing one in one of our CensusTreeNodeCaches.
 */
function CensusTreeNodeCache() {}
CensusTreeNodeCache.prototype = null;

/**
 * The value of a single entry stored in a CensusTreeNodeCache. It is a pair of
 * the CensusTreeNode for this cache value, and the subsequent
 * CensusTreeNodeCache for this node's children.
 *
 * @param {SavedFrame} frame
 *        The frame being cached.
 */
function CensusTreeNodeCacheValue() {
  // The CensusTreeNode for this cache value.
  this.node = undefined;
  // The CensusTreeNodeCache for this frame's children.
  this.children = undefined;
}

CensusTreeNodeCacheValue.prototype = null;

/**
 * Create a unique string for the given SavedFrame (ignoring the frame's parent
 * chain) that can be used as a hash to key this frame within a CensusTreeNodeCache.
 *
 * NB: We manually hash rather than using an ES6 Map because we are purposely
 * ignoring the parent chain and wish to consider frames with everything the
 * same except their parents as the same.
 *
 * @param {SavedFrame} frame
 *        The SavedFrame object we would like to lookup in or insert into a
 *        CensusTreeNodeCache.
 *
 * @returns {String}
 *          The unique string that can be used as a key in a CensusTreeNodeCache.
 */
CensusTreeNodeCache.hashFrame = function (frame) {
  // eslint-disable-next-line max-len
  return `FRAME,${frame.functionDisplayName},${frame.source},${frame.line},${frame.column},${frame.asyncCause}`;
};

/**
 * Create a unique string for the given CensusTreeNode **with regards to
 * siblings at the current depth of the tree, not within the whole tree.** It
 * can be used as a hash to key this node within a CensusTreeNodeCache.
 *
 * @param {CensusTreeNode} node
 *        The node we would like to lookup in or insert into a cache.
 *
 * @returns {String}
 *          The unique string that can be used as a key in a CensusTreeNodeCache.
 */
CensusTreeNodeCache.hashNode = function (node) {
  return isSavedFrame(node.name)
    ? CensusTreeNodeCache.hashFrame(node.name)
    : `NODE,${node.name}`;
};

/**
 * Insert the given CensusTreeNodeCacheValue whose node.name is a SavedFrame
 * object in the given cache.
 *
 * @param {CensusTreeNodeCache} cache
 * @param {CensusTreeNodeCacheValue} value
 */
CensusTreeNodeCache.insertFrame = function (cache, value) {
  cache[CensusTreeNodeCache.hashFrame(value.node.name)] = value;
};

/**
 * Insert the given value in the cache.
 *
 * @param {CensusTreeNodeCache} cache
 * @param {CensusTreeNodeCacheValue} value
 */
CensusTreeNodeCache.insertNode = function (cache, value) {
  if (isSavedFrame(value.node.name)) {
    CensusTreeNodeCache.insertFrame(cache, value);
  } else {
    cache[CensusTreeNodeCache.hashNode(value.node)] = value;
  }
};

/**
 * Lookup `frame` in `cache` and return its value if it exists.
 *
 * @param {CensusTreeNodeCache} cache
 * @param {SavedFrame} frame
 *
 * @returns {undefined|CensusTreeNodeCacheValue}
 */
CensusTreeNodeCache.lookupFrame = function (cache, frame) {
  return cache[CensusTreeNodeCache.hashFrame(frame)];
};

/**
 * Lookup `node` in `cache` and return its value if it exists.
 *
 * @param {CensusTreeNodeCache} cache
 * @param {CensusTreeNode} node
 *
 * @returns {undefined|CensusTreeNodeCacheValue}
 */
CensusTreeNodeCache.lookupNode = function (cache, node) {
  return isSavedFrame(node.name)
    ? CensusTreeNodeCache.lookupFrame(cache, node.name)
    : cache[CensusTreeNodeCache.hashNode(node)];
};

/**
 * Add `child` to `parent`'s set of children and store the parent ID
 * on the child.
 *
 * @param {CensusTreeNode} parent
 * @param {CensusTreeNode} child
 */
function addChild(parent, child) {
  if (!parent.children) {
    parent.children = [];
  }
  child.parent = parent.id;
  parent.children.push(child);
}

/**
 * Get an array of each frame in the provided stack.
 *
 * @param {SavedFrame} stack
 * @returns {Array<SavedFrame>}
 */
function getArrayOfFrames(stack) {
  const frames = [];
  let frame = stack;
  while (frame) {
    frames.push(frame);
    frame = frame.parent;
  }
  frames.reverse();
  return frames;
}

/**
 * Given an `edge` to a sub-`report` whose structure is described by
 * `breakdown`, create a CensusTreeNode tree.
 *
 * @param {Object} breakdown
 *        The breakdown specifying the structure of the given report.
 *
 * @param {Object} report
 *        The census report.
 *
 * @param {null|String|SavedFrame} edge
 *        The edge leading to this report from the parent report.
 *
 * @param {CensusTreeNodeCache} cache
 *        The cache of CensusTreeNodes we have already made for the siblings of
 *        the node being created. The existing nodes are reused when possible.
 *
 * @param {Object} outParams
 *        The return values are attached to this object after this function
 *        returns. Because we create a CensusTreeNode for each frame in a
 *        SavedFrame stack edge, there may multiple nodes per sub-report.
 *
 *          - top: The deepest node in the CensusTreeNode subtree created.
 *
 *          - bottom: The shallowest node in the CensusTreeNode subtree created.
 *                    This is null if the shallowest node in the subtree was
 *                    found in the `cache` and reused.
 *
 *        Note that top and bottom are not necessarily different. In the case
 *        where there is a 1:1 correspondence between an edge in the report and
 *        a CensusTreeNode, top and bottom refer to the same node.
 */
function makeCensusTreeNodeSubTree(breakdown, report, edge, cache, outParams) {
  if (!isSavedFrame(edge)) {
    const node = new CensusTreeNode(edge);
    outParams.top = outParams.bottom = node;
    return;
  }

  const frames = getArrayOfFrames(edge);
  let currentCache = cache;
  let prevNode;
  for (let i = 0, length = frames.length; i < length; i++) {
    const frame = frames[i];

    // Get or create the CensusTreeNodeCacheValue for this frame. If we already
    // have a CensusTreeNodeCacheValue (and hence a CensusTreeNode) for this
    // frame, we don't need to add the node to the previous node's children as
    // we have already done that. If we don't have a CensusTreeNodeCacheValue
    // and CensusTreeNode for this frame, then create one and make sure to hook
    // it up as a child of the previous node.
    let isNewNode = false;
    let val = CensusTreeNodeCache.lookupFrame(currentCache, frame);
    if (!val) {
      isNewNode = true;
      val = new CensusTreeNodeCacheValue();
      val.node = new CensusTreeNode(frame);

      CensusTreeNodeCache.insertFrame(currentCache, val);
      if (prevNode) {
        addChild(prevNode, val.node);
      }
    }

    if (i === 0) {
      outParams.bottom = isNewNode ? val.node : null;
    }
    if (i === length - 1) {
      outParams.top = val.node;
    }

    prevNode = val.node;

    if (i !== length - 1 && !val.children) {
      // This is not the last frame and therefore this node will have
      // children, which we must cache.
      val.children = new CensusTreeNodeCache();
    }

    currentCache = val.children;
  }
}

/**
 * A Visitor that walks a census report and creates the corresponding
 * CensusTreeNode tree.
 */
function CensusTreeNodeVisitor() {
  // The root of the resulting CensusTreeNode tree.
  this._root = null;

  // The stack of CensusTreeNodes that we are in the process of building while
  // walking the census report.
  this._nodeStack = [];

  // To avoid unnecessary allocations, we reuse the same out parameter object
  // passed to `makeCensusTreeNodeSubTree` every time we call it.
  this._outParams = {
    top: null,
    bottom: null,
  };

  // The stack of `CensusTreeNodeCache`s that we use to aggregate many
  // SavedFrame stacks into a single CensusTreeNode tree.
  this._cacheStack = [new CensusTreeNodeCache()];

  // The current index in the DFS of the census report tree.
  this._index = -1;
}

CensusTreeNodeVisitor.prototype = Object.create(Visitor);

/**
 * Create the CensusTreeNode subtree for this sub-report and link it to the
 * parent CensusTreeNode.
 *
 * @overrides Visitor.prototype.enter
 */
CensusTreeNodeVisitor.prototype.enter = function (breakdown, report, edge) {
  this._index++;

  const cache = this._cacheStack[this._cacheStack.length - 1];
  makeCensusTreeNodeSubTree(breakdown, report, edge, cache, this._outParams);
  const { top, bottom } = this._outParams;

  if (!this._root) {
    this._root = bottom;
  } else if (bottom) {
    addChild(this._nodeStack[this._nodeStack.length - 1], bottom);
  }

  this._cacheStack.push(new CensusTreeNodeCache());
  this._nodeStack.push(top);
};

function values(cache) {
  return Object.keys(cache).map(k => cache[k]);
}

function isNonEmpty(node) {
  return (node.children !== undefined && node.children.length)
      || node.bytes !== 0
      || node.count !== 0;
}

/**
 * We have finished adding children to the CensusTreeNode subtree for the
 * current sub-report. Make sure that the children are sorted for every node in
 * the subtree.
 *
 * @overrides Visitor.prototype.exit
 */
CensusTreeNodeVisitor.prototype.exit = function (breakdown, report, edge) {
  // Ensure all children are sorted and have their counts/bytes aggregated. We
  // only need to consider cache children here, because other children
  // correspond to other sub-reports and we already fixed them up in an earlier
  // invocation of `exit`.

  function dfs(node, childrenCache) {
    if (childrenCache) {
      const childValues = values(childrenCache);
      for (let i = 0, length = childValues.length; i < length; i++) {
        dfs(childValues[i].node, childValues[i].children);
      }
    }

    node.totalCount = node.count;
    node.totalBytes = node.bytes;

    if (node.children) {
      // Prune empty leaves.
      node.children = node.children.filter(isNonEmpty);

      node.children.sort(compareByTotal);

      for (let i = 0, length = node.children.length; i < length; i++) {
        node.totalCount += node.children[i].totalCount;
        node.totalBytes += node.children[i].totalBytes;
      }
    }
  }

  const top = this._nodeStack.pop();
  const cache = this._cacheStack.pop();
  dfs(top, cache);
};

/**
 * @overrides Visitor.prototype.count
 */
CensusTreeNodeVisitor.prototype.count = function (breakdown, report, edge) {
  const node = this._nodeStack[this._nodeStack.length - 1];
  node.reportLeafIndex = this._index;

  if (breakdown.count) {
    node.count = report.count;
  }

  if (breakdown.bytes) {
    node.bytes = report.bytes;
  }
};

/**
 * Get the root of the resulting CensusTreeNode tree.
 *
 * @returns {CensusTreeNode}
 */
CensusTreeNodeVisitor.prototype.root = function () {
  if (!this._root) {
    throw new Error("Attempt to get the root before walking the census report!");
  }

  if (this._nodeStack.length) {
    throw new Error("Attempt to get the root while walking the census report!");
  }

  return this._root;
};

/**
 * Create a single, uninitialized CensusTreeNode.
 *
 * @param {null|String|SavedFrame} name
 */
function CensusTreeNode(name) {
  // Display name for this CensusTreeNode. Either null, a string, or a
  // SavedFrame.
  this.name = name;

  // The number of bytes occupied by matching things in the heap snapshot.
  this.bytes = 0;

  // The sum of `this.bytes` and `child.totalBytes` for each child in
  // `this.children`.
  this.totalBytes = 0;

  // The number of things in the heap snapshot that match this node in the
  // census tree.
  this.count = 0;

  // The sum of `this.count` and `child.totalCount` for each child in
  // `this.children`.
  this.totalCount = 0;

  // An array of this node's children, or undefined if it has no children.
  this.children = undefined;

  // The unique ID of this node.
  this.id = ++censusTreeNodeIdCounter;

  // If present, the unique ID of this node's parent. If this node does not have
  // a parent, then undefined.
  this.parent = undefined;

  // The `reportLeafIndex` property allows mapping a CensusTreeNode node back to
  // a leaf in the census report it was generated from. It is always one of the
  // following variants:
  //
  // * A `Number` index pointing a leaf report in a pre-order DFS traversal of
  //   this CensusTreeNode's census report.
  //
  // * A `Set` object containing such indices, when this is part of an inverted
  //   CensusTreeNode tree and multiple leaves in the report map onto this node.
  //
  // * Finally, `undefined` when no leaves in the census report correspond with
  //   this node.
  //
  // The first and third cases are the common cases. The second case is rather
  // uncommon, and to avoid doubling the number of allocations when creating
  // CensusTreeNode trees, and objects that get structured cloned when sending
  // such trees from the HeapAnalysesWorker to the main thread, we only allocate
  // a Set object once a node actually does have multiple leaves it corresponds
  // to.
  this.reportLeafIndex = undefined;
}

CensusTreeNode.prototype = null;

/**
 * Compare the given nodes by their `totalBytes` properties, and breaking ties
 * with the `totalCount`, `bytes`, and `count` properties (in that order).
 *
 * @param {CensusTreeNode} node1
 * @param {CensusTreeNode} node2
 *
 * @returns {Number}
 *          A number suitable for using with Array.prototype.sort.
 */
function compareByTotal(node1, node2) {
  return Math.abs(node2.totalBytes) - Math.abs(node1.totalBytes)
      || Math.abs(node2.totalCount) - Math.abs(node1.totalCount)
      || Math.abs(node2.bytes) - Math.abs(node1.bytes)
      || Math.abs(node2.count) - Math.abs(node1.count);
}

/**
 * Compare the given nodes by their `bytes` properties, and breaking ties with
 * the `count`, `totalBytes`, and `totalCount` properties (in that order).
 *
 * @param {CensusTreeNode} node1
 * @param {CensusTreeNode} node2
 *
 * @returns {Number}
 *          A number suitable for using with Array.prototype.sort.
 */
function compareBySelf(node1, node2) {
  return Math.abs(node2.bytes) - Math.abs(node1.bytes)
      || Math.abs(node2.count) - Math.abs(node1.count)
      || Math.abs(node2.totalBytes) - Math.abs(node1.totalBytes)
      || Math.abs(node2.totalCount) - Math.abs(node1.totalCount);
}

/**
 * Given a parent cache value from a tree we are building and a child node from
 * a tree we are basing the new tree off of, if we already have a corresponding
 * node in the parent's children cache, merge this node's counts with
 * it. Otherwise, create the corresponding node, add it to the parent's children
 * cache, and create the parent->child edge.
 *
 * @param {CensusTreeNodeCacheValue} parentCachevalue
 * @param {CensusTreeNode} node
 *
 * @returns {CensusTreeNodeCacheValue}
 *          The new or extant child node's corresponding cache value.
 */
function insertOrMergeNode(parentCacheValue, node) {
  if (!parentCacheValue.children) {
    parentCacheValue.children = new CensusTreeNodeCache();
  }

  let val = CensusTreeNodeCache.lookupNode(parentCacheValue.children, node);

  if (val) {
    // When inverting, it is possible that multiple leaves in the census report
    // get merged into a single CensusTreeNode node. When this occurs, switch
    // from a single index to a set of indices.
    if (val.node.reportLeafIndex !== undefined &&
        val.node.reportLeafIndex !== node.reportLeafIndex) {
      if (typeof val.node.reportLeafIndex === "number") {
        const oldIndex = val.node.reportLeafIndex;
        val.node.reportLeafIndex = new Set();
        val.node.reportLeafIndex.add(oldIndex);
        val.node.reportLeafIndex.add(node.reportLeafIndex);
      } else {
        val.node.reportLeafIndex.add(node.reportLeafIndex);
      }
    }

    val.node.count += node.count;
    val.node.bytes += node.bytes;
  } else {
    val = new CensusTreeNodeCacheValue();

    val.node = new CensusTreeNode(node.name);
    val.node.reportLeafIndex = node.reportLeafIndex;
    val.node.count = node.count;
    val.node.totalCount = node.totalCount;
    val.node.bytes = node.bytes;
    val.node.totalBytes = node.totalBytes;

    addChild(parentCacheValue.node, val.node);
    CensusTreeNodeCache.insertNode(parentCacheValue.children, val);
  }

  return val;
}

/**
 * Given an un-inverted CensusTreeNode tree, return the corresponding inverted
 * CensusTreeNode tree. The input tree is not modified. The resulting inverted
 * tree is sorted by self bytes rather than by total bytes.
 *
 * @param {CensusTreeNode} tree
 *        The un-inverted tree.
 *
 * @returns {CensusTreeNode}
 *          The corresponding inverted tree.
 */
function invert(tree) {
  const inverted = new CensusTreeNodeCacheValue();
  inverted.node = new CensusTreeNode(null);

  // Do a depth-first search of the un-inverted tree. As we reach each leaf,
  // take the path from the old root to the leaf, reverse that path, and add it
  // to the new, inverted tree's root.

  const path = [];
  (function addInvertedPaths(node) {
    path.push(node);

    if (node.children) {
      for (let i = 0, length = node.children.length; i < length; i++) {
        addInvertedPaths(node.children[i]);
      }
    } else {
      // We found a leaf node, add the reverse path to the inverted tree.
      let currentCacheValue = inverted;
      for (let i = path.length - 1; i >= 0; i--) {
        currentCacheValue = insertOrMergeNode(currentCacheValue, path[i]);
      }
    }

    path.pop();
  }(tree));

  // Ensure that the root node always has the totals.
  inverted.node.totalBytes = tree.totalBytes;
  inverted.node.totalCount = tree.totalCount;

  return inverted.node;
}

/**
 * Given a CensusTreeNode tree and predicate function, create the tree
 * containing only the nodes in any path `(node_0, node_1, ..., node_n-1)` in
 * the given tree where `predicate(node_j)` is true for `0 <= j < n`, `node_0`
 * is the given tree's root, and `node_n-1` is a leaf in the given tree. The
 * given tree is left unmodified.
 *
 * @param {CensusTreeNode} tree
 * @param {Function} predicate
 *
 * @returns {CensusTreeNode}
 */
function filter(tree, predicate) {
  const filtered = new CensusTreeNodeCacheValue();
  filtered.node = new CensusTreeNode(null);

  // Do a DFS over the given tree. If the predicate returns true for any node,
  // add that node and its whole subtree to the filtered tree.

  const path = [];
  let match = false;

  function addMatchingNodes(node) {
    path.push(node);

    let oldMatch = match;
    if (!match && predicate(node)) {
      match = true;
    }

    if (node.children) {
      for (let i = 0, length = node.children.length; i < length; i++) {
        addMatchingNodes(node.children[i]);
      }
    } else if (match) {
      // We found a matching leaf node, add it to the filtered tree.
      let currentCacheValue = filtered;
      for (let i = 0, length = path.length; i < length; i++) {
        currentCacheValue = insertOrMergeNode(currentCacheValue, path[i]);
      }
    }

    match = oldMatch;
    path.pop();
  }

  if (tree.children) {
    for (let i = 0, length = tree.children.length; i < length; i++) {
      addMatchingNodes(tree.children[i]);
    }
  }

  filtered.node.count = tree.count;
  filtered.node.totalCount = tree.totalCount;
  filtered.node.bytes = tree.bytes;
  filtered.node.totalBytes = tree.totalBytes;

  return filtered.node;
}

/**
 * Given a filter string, return a predicate function that takes a node and
 * returns true iff the node matches the filter.
 *
 * @param {String} filterString
 * @returns {Function}
 */
function makeFilterPredicate(filterString) {
  return function (node) {
    if (!node.name) {
      return false;
    }

    if (isSavedFrame(node.name)) {
      return node.name.source.includes(filterString)
        || (node.name.functionDisplayName || "").includes(filterString)
        || (node.name.asyncCause || "").includes(filterString);
    }

    return String(node.name).includes(filterString);
  };
}

/**
 * Takes a report from a census (`dbg.memory.takeCensus()`) and the breakdown
 * used to generate the census and returns a structure used to render
 * a tree to display the data.
 *
 * Returns a recursive "CensusTreeNode" object, looking like:
 *
 * CensusTreeNode = {
 *   // `children` if it exists, is sorted by `bytes`, if they are leaf nodes.
 *   children: ?[<CensusTreeNode...>],
 *   name: <?String>
 *   count: <?Number>
 *   bytes: <?Number>
 *   id: <?Number>
 *   parent: <?Number>
 * }
 *
 * @param {Object} breakdown
 *        The breakdown used to generate the census report.
 *
 * @param {Object} report
 *        The census report generated with the specified breakdown.
 *
 * @param {Object} options
 *        Configuration options.
 *          - invert: Whether to invert the resulting tree or not. Defaults to
 *                    false, ie uninverted.
 *
 * @returns {CensusTreeNode}
 */
exports.censusReportToCensusTreeNode = function (breakdown, report,
                                                 options = {
                                                   invert: false,
                                                   filter: null
                                                 }) {
  // Reset the counter so that turning the same census report into a
  // CensusTreeNode tree repeatedly is idempotent.
  censusTreeNodeIdCounter = 0;

  const visitor = new CensusTreeNodeVisitor();
  walk(breakdown, report, visitor);
  let result = visitor.root();

  if (options.invert) {
    result = invert(result);
  }

  if (typeof options.filter === "string") {
    result = filter(result, makeFilterPredicate(options.filter));
  }

  // If the report is a delta report that was generated by diffing two other
  // reports, make sure to use the basis totals rather than the totals of the
  // difference.
  if (typeof report[basisTotalBytes] === "number") {
    result.totalBytes = report[basisTotalBytes];
    result.totalCount = report[basisTotalCount];
  }

  // Inverting and filtering could have messed up the sort order, so do a
  // depth-first search of the tree and ensure that siblings are sorted.
  const comparator = options.invert ? compareBySelf : compareByTotal;
  (function ensureSorted(node) {
    if (node.children) {
      node.children.sort(comparator);
      for (let i = 0, length = node.children.length; i < length; i++) {
        ensureSorted(node.children[i]);
      }
    }
  }(result));

  return result;
};
PK
!<�>�`�
�
Fchrome/devtools/modules/devtools/shared/heapsnapshot/shortest-paths.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";

/**
 * Compress a set of paths leading to `target` into a single graph, returned as
 * a set of nodes and a set of edges.
 *
 * @param {NodeId} target
 *        The target node passed to `HeapSnapshot.computeShortestPaths`.
 *
 * @param {Array<Path>} paths
 *        An array of paths to `target`, as returned by
 *        `HeapSnapshot.computeShortestPaths`.
 *
 * @returns {Object}
 *          An object with two properties:
 *          - edges: An array of unique objects of the form:
 *              {
 *                 from: <node ID>,
 *                 to: <node ID>,
 *                 name: <string or null>
 *              }
 *          - nodes: An array of unique node IDs. Every `from` and `to` id is
 *            guaranteed to be in this array exactly once.
 */
exports.deduplicatePaths = function (target, paths) {
  // Use this structure to de-duplicate edges among many retaining paths from
  // start to target.
  //
  // Map<FromNodeId, Map<ToNodeId, Set<EdgeName>>>
  const deduped = new Map();

  function insert(from, to, name) {
    let toMap = deduped.get(from);
    if (!toMap) {
      toMap = new Map();
      deduped.set(from, toMap);
    }

    let nameSet = toMap.get(to);
    if (!nameSet) {
      nameSet = new Set();
      toMap.set(to, nameSet);
    }

    nameSet.add(name);
  }

  // eslint-disable-next-line no-labels
  outer: for (let path of paths) {
    const pathLength = path.length;

    // Check for duplicate predecessors in the path, and skip paths that contain
    // them.
    const predecessorsSeen = new Set();
    predecessorsSeen.add(target);
    for (let i = 0; i < pathLength; i++) {
      if (predecessorsSeen.has(path[i].predecessor)) {
        // eslint-disable-next-line no-labels
        continue outer;
      }
      predecessorsSeen.add(path[i].predecessor);
    }

    for (let i = 0; i < pathLength - 1; i++) {
      insert(path[i].predecessor, path[i + 1].predecessor, path[i].edge);
    }

    insert(path[pathLength - 1].predecessor, target, path[pathLength - 1].edge);
  }

  const nodes = [target];
  const edges = [];

  for (let [from, toMap] of deduped) {
    // If the second/third/etc shortest path contains the `target` anywhere
    // other than the very last node, we could accidentally put the `target` in
    // `nodes` more than once.
    if (from !== target) {
      nodes.push(from);
    }

    for (let [to, edgeNameSet] of toMap) {
      for (let name of edgeNameSet) {
        edges.push({ from, to, name });
      }
    }
  }

  return { nodes, edges };
};
PK
!<a�D|��6chrome/devtools/modules/devtools/shared/indentation.js/* -*- indent-tabs-mode: nil; js-indent-level: 2; fill-column: 80 -*- */
/* vim:set ts=2 sw=2 sts=2 et tw=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 Services = require("Services");

const EXPAND_TAB = "devtools.editor.expandtab";
const TAB_SIZE = "devtools.editor.tabsize";
const DETECT_INDENT = "devtools.editor.detectindentation";
const DETECT_INDENT_MAX_LINES = 500;

/**
 * Get the number of indentation units to use to indent a "block"
 * and a boolean indicating whether indentation must be done using tabs.
 *
 * @return {Object} an object of the form {indentUnit, indentWithTabs}.
 *        |indentUnit| is the number of indentation units to use
 *        to indent a "block".
 *        |indentWithTabs| is a boolean which is true if indentation
 *        should be done using tabs.
 */
function getTabPrefs() {
  let indentWithTabs = !Services.prefs.getBoolPref(EXPAND_TAB);
  let indentUnit = Services.prefs.getIntPref(TAB_SIZE);
  return {indentUnit, indentWithTabs};
}

/**
 * Get the indentation to use in an editor, or return false if the user has
 * asked for the indentation to be guessed from some text.
 *
 * @return {false | Object}
 *        Returns false if the "detect indentation" pref is set.
 *        If the pref is not set, returns an object of the same
 *        form as returned by getTabPrefs.
 */
function getIndentationFromPrefs() {
  let shouldDetect = Services.prefs.getBoolPref(DETECT_INDENT);
  if (shouldDetect) {
    return false;
  }

  return getTabPrefs();
}

/**
 * Given a function that can iterate over some text, compute the indentation to
 * use.  This consults various prefs to arrive at a decision.
 *
 * @param {Function} iterFunc A function of three arguments:
 *        (start, end, callback); where |start| and |end| describe
 *        the range of text lines to examine, and |callback| is a function
 *        to be called with the text of each line.
 *
 * @return {Object} an object of the form {indentUnit, indentWithTabs}.
 *        |indentUnit| is the number of indentation units to use
 *        to indent a "block".
 *        |indentWithTabs| is a boolean which is true if indentation
 *        should be done using tabs.
 */
function getIndentationFromIteration(iterFunc) {
  let indentWithTabs = !Services.prefs.getBoolPref(EXPAND_TAB);
  let indentUnit = Services.prefs.getIntPref(TAB_SIZE);
  let shouldDetect = Services.prefs.getBoolPref(DETECT_INDENT);

  if (shouldDetect) {
    let indent = detectIndentation(iterFunc);
    if (indent != null) {
      indentWithTabs = indent.tabs;
      indentUnit = indent.spaces ? indent.spaces : indentUnit;
    }
  }

  return {indentUnit, indentWithTabs};
}

/**
 * A wrapper for @see getIndentationFromIteration which computes the
 * indentation of a given string.
 *
 * @param {String} string the input text
 * @return {Object} an object of the same form as returned by
 *                  getIndentationFromIteration
 */
function getIndentationFromString(string) {
  let iteratorFn = function (start, end, callback) {
    let split = string.split(/\r\n|\r|\n|\f/);
    split.slice(start, end).forEach(callback);
  };
  return getIndentationFromIteration(iteratorFn);
}

/**
 * Detect the indentation used in an editor. Returns an object
 * with 'tabs' - whether this is tab-indented and 'spaces' - the
 * width of one indent in spaces. Or `null` if it's inconclusive.
 */
function detectIndentation(textIteratorFn) {
  // # spaces indent -> # lines with that indent
  let spaces = {};
  // indentation width of the last line we saw
  let last = 0;
  // # of lines that start with a tab
  let tabs = 0;
  // # of indented lines (non-zero indent)
  let total = 0;

  textIteratorFn(0, DETECT_INDENT_MAX_LINES, (text) => {
    if (text.startsWith("\t")) {
      tabs++;
      total++;
      return;
    }
    let width = 0;
    while (text[width] === " ") {
      width++;
    }
    // don't count lines that are all spaces
    if (width == text.length) {
      last = 0;
      return;
    }
    if (width > 1) {
      total++;
    }

    // see how much this line is offset from the line above it
    let indent = Math.abs(width - last);
    if (indent > 1 && indent <= 8) {
      spaces[indent] = (spaces[indent] || 0) + 1;
    }
    last = width;
  });

  // this file is not indented at all
  if (total == 0) {
    return null;
  }

  // mark as tabs if they start more than half the lines
  if (tabs >= total / 2) {
    return { tabs: true };
  }

  // find most frequent non-zero width difference between adjacent lines
  let freqIndent = null, max = 1;
  for (let width in spaces) {
    width = parseInt(width, 10);
    let tally = spaces[width];
    if (tally > max) {
      max = tally;
      freqIndent = width;
    }
  }
  if (!freqIndent) {
    return null;
  }

  return { tabs: false, spaces: freqIndent };
}

exports.EXPAND_TAB = EXPAND_TAB;
exports.TAB_SIZE = TAB_SIZE;
exports.DETECT_INDENT = DETECT_INDENT;
exports.getTabPrefs = getTabPrefs;
exports.getIndentationFromPrefs = getIndentationFromPrefs;
exports.getIndentationFromIteration = getIndentationFromIteration;
exports.getIndentationFromString = getIndentationFromString;
PK
!<N��G

5chrome/devtools/modules/devtools/shared/indexed-db.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 indexedDB helper is a simplified version of sdk/indexed-db. It creates a DB with
 * a principal dedicated to DevTools.
 */

const Services = require("Services");

const PSEUDOURI = "indexeddb://fx-devtools";
const principaluri = Services.io.newURI(PSEUDOURI);
const principal =
  Services.scriptSecurityManager.createCodebasePrincipal(principaluri, {});

/**
 * Create the DevTools dedicated DB, by relying on the real indexedDB object passed as a
 * parameter here.
 *
 * @param {IDBFactory} indexedDB
 *        Real indexedDB object.
 * @return {Object} Wrapper object that implements IDBFactory methods, but for a devtools
 *         specific principal.
 */
exports.createDevToolsIndexedDB = function (indexedDB) {
  return Object.freeze({
    /**
     * Only the standard version of indexedDB.open is supported.
     */
    open(name, version) {
      let options = { storage: "persistent" };
      if (typeof version === "number") {
        options.version = version;
      }
      return indexedDB.openForPrincipal(principal, name, options);
    },
    /**
     * Only the standard version of indexedDB.deleteDatabase is supported.
     */
    deleteDatabase(name) {
      return indexedDB.deleteForPrincipal(principal, name, { storage: "persistent" });
    },
    cmp: indexedDB.cmp.bind(indexedDB),
  });
};
PK
!<oz��H>H>>chrome/devtools/modules/devtools/shared/inspector/css-logic.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=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 { getRootBindingParent } = require("devtools/shared/layout/utils");
const { getTabPrefs } = require("devtools/shared/indentation");
const InspectorUtils = require("InspectorUtils");

const MAX_DATA_URL_LENGTH = 40;

/*
 * About the objects defined in this file:
 * - CssLogic contains style information about a view context. It provides
 *   access to 2 sets of objects: Css[Sheet|Rule|Selector] provide access to
 *   information that does not change when the selected element changes while
 *   Css[Property|Selector]Info provide information that is dependent on the
 *   selected element.
 *   Its key methods are highlight(), getPropertyInfo() and forEachSheet(), etc
 *   It also contains a number of static methods for l10n, naming, etc
 *
 * - CssSheet provides a more useful API to a DOM CSSSheet for our purposes,
 *   including shortSource and href.
 * - CssRule a more useful API to a DOM CSSRule including access to the group
 *   of CssSelectors that the rule provides properties for
 * - CssSelector A single selector - i.e. not a selector group. In other words
 *   a CssSelector does not contain ','. This terminology is different from the
 *   standard DOM API, but more inline with the definition in the spec.
 *
 * - CssPropertyInfo contains style information for a single property for the
 *   highlighted element.
 * - CssSelectorInfo is a wrapper around CssSelector, which adds sorting with
 *   reference to the selected element.
 */

/**
 * Provide access to the style information in a page.
 * CssLogic uses the standard DOM API, and the Gecko InspectorUtils API to
 * access styling information in the page, and present this to the user in a way
 * that helps them understand:
 * - why their expectations may not have been fulfilled
 * - how browsers process CSS
 * @constructor
 */

const Services = require("Services");

loader.lazyImporter(this, "findCssSelector", "resource://gre/modules/css-selector.js");

const CSSLexer = require("devtools/shared/css/lexer");
const {LocalizationHelper} = require("devtools/shared/l10n");
const styleInspectorL10N =
  new LocalizationHelper("devtools/shared/locales/styleinspector.properties");

/**
 * Special values for filter, in addition to an href these values can be used
 */
exports.FILTER = {
  // show properties for all user style sheets.
  USER: "user",
  // USER, plus user-agent (i.e. browser) style sheets
  UA: "ua",
};

/**
 * Each rule has a status, the bigger the number, the better placed it is to
 * provide styling information.
 *
 * These statuses are localized inside the styleinspector.properties
 * string bundle.
 * @see csshtmltree.js RuleView._cacheStatusNames()
 */
exports.STATUS = {
  BEST: 3,
  MATCHED: 2,
  PARENT_MATCH: 1,
  UNMATCHED: 0,
  UNKNOWN: -1,
};

/**
 * Lookup a l10n string in the shared styleinspector string bundle.
 *
 * @param {String} name
 *        The key to lookup.
 * @returns {String} A localized version of the given key.
 */
exports.l10n = name => styleInspectorL10N.getStr(name);

/**
 * Is the given property sheet a content stylesheet?
 *
 * @param {CSSStyleSheet} sheet a stylesheet
 * @return {boolean} true if the given stylesheet is a content stylesheet,
 * false otherwise.
 */
exports.isContentStylesheet = function (sheet) {
  return sheet.parsingMode !== "agent";
};

/**
 * Return a shortened version of a style sheet's source.
 *
 * @param {CSSStyleSheet} sheet the DOM object for the style sheet.
 */
exports.shortSource = function (sheet) {
  // Use a string like "inline" if there is no source href
  if (!sheet || !sheet.href) {
    return exports.l10n("rule.sourceInline");
  }

  // If the sheet is a data URL, return a trimmed version of it.
  let dataUrl = sheet.href.trim().match(/^data:.*?,((?:.|\r|\n)*)$/);
  if (dataUrl) {
    return dataUrl[1].length > MAX_DATA_URL_LENGTH ?
      `${dataUrl[1].substr(0, MAX_DATA_URL_LENGTH - 1)}…` : dataUrl[1];
  }

  // We try, in turn, the filename, filePath, query string, whole thing
  let url = {};
  try {
    url = new URL(sheet.href);
  } catch (ex) {
    // Some UA-provided stylesheets are not valid URLs.
  }

  if (url.pathname) {
    let index = url.pathname.lastIndexOf("/");
    if (index !== -1 && index < url.pathname.length) {
      return url.pathname.slice(index + 1);
    }
    return url.pathname;
  }

  if (url.query) {
    return url.query;
  }

  return sheet.href;
};

const TAB_CHARS = "\t";
const SPACE_CHARS = " ";

/**
 * Prettify minified CSS text.
 * This prettifies CSS code where there is no indentation in usual places while
 * keeping original indentation as-is elsewhere.
 * @param string text The CSS source to prettify.
 * @return string Prettified CSS source
 */
function prettifyCSS(text, ruleCount) {
  if (prettifyCSS.LINE_SEPARATOR == null) {
    let os = Services.appinfo.OS;
    prettifyCSS.LINE_SEPARATOR = (os === "WINNT" ? "\r\n" : "\n");
  }

  // Stylesheets may start and end with HTML comment tags (possibly with whitespaces
  // before and after). Remove those first. Don't do anything there aren't any.
  let trimmed = text.trim();
  if (trimmed.startsWith("<!--")) {
    text = trimmed.replace(/^<!--/, "").replace(/-->$/, "").trim();
  }

  let originalText = text;
  text = text.trim();

  // don't attempt to prettify if there's more than one line per rule.
  let lineCount = text.split("\n").length - 1;
  if (ruleCount !== null && lineCount >= ruleCount) {
    return originalText;
  }

  // We reformat the text using a simple state machine.  The
  // reformatting preserves most of the input text, changing only
  // whitespace.  The rules are:
  //
  // * After a "{" or ";" symbol, ensure there is a newline and
  //   indentation before the next non-comment, non-whitespace token.
  // * Additionally after a "{" symbol, increase the indentation.
  // * A "}" symbol ensures there is a preceding newline, and
  //   decreases the indentation level.
  // * Ensure there is whitespace before a "{".
  //
  // This approach can be confused sometimes, but should do ok on a
  // minified file.
  let indent = "";
  let indentLevel = 0;
  let tokens = CSSLexer.getCSSLexer(text);
  let result = "";
  let pushbackToken = undefined;

  // A helper function that reads tokens, looking for the next
  // non-comment, non-whitespace token.  Comment and whitespace tokens
  // are appended to |result|.  If this encounters EOF, it returns
  // null.  Otherwise it returns the last whitespace token that was
  // seen.  This function also updates |pushbackToken|.
  let readUntilSignificantToken = () => {
    while (true) {
      let token = tokens.nextToken();
      if (!token || token.tokenType !== "whitespace") {
        pushbackToken = token;
        return token;
      }
      // Saw whitespace.  Before committing to it, check the next
      // token.
      let nextToken = tokens.nextToken();
      if (!nextToken || nextToken.tokenType !== "comment") {
        pushbackToken = nextToken;
        return token;
      }
      // Saw whitespace + comment.  Update the result and continue.
      result = result + text.substring(token.startOffset, nextToken.endOffset);
    }
  };

  // State variables for readUntilNewlineNeeded.
  //
  // Starting index of the accumulated tokens.
  let startIndex;
  // Ending index of the accumulated tokens.
  let endIndex;
  // True if any non-whitespace token was seen.
  let anyNonWS;
  // True if the terminating token is "}".
  let isCloseBrace;
  // True if the token just before the terminating token was
  // whitespace.
  let lastWasWS;

  // A helper function that reads tokens until there is a reason to
  // insert a newline.  This updates the state variables as needed.
  // If this encounters EOF, it returns null.  Otherwise it returns
  // the final token read.  Note that if the returned token is "{",
  // then it will not be included in the computed start/end token
  // range.  This is used to handle whitespace insertion before a "{".
  let readUntilNewlineNeeded = () => {
    let token;
    while (true) {
      if (pushbackToken) {
        token = pushbackToken;
        pushbackToken = undefined;
      } else {
        token = tokens.nextToken();
      }
      if (!token) {
        endIndex = text.length;
        break;
      }

      // A "}" symbol must be inserted later, to deal with indentation
      // and newline.
      if (token.tokenType === "symbol" && token.text === "}") {
        isCloseBrace = true;
        break;
      } else if (token.tokenType === "symbol" && token.text === "{") {
        break;
      }

      if (token.tokenType !== "whitespace") {
        anyNonWS = true;
      }

      if (startIndex === undefined) {
        startIndex = token.startOffset;
      }
      endIndex = token.endOffset;

      if (token.tokenType === "symbol" && token.text === ";") {
        break;
      }

      lastWasWS = token.tokenType === "whitespace";
    }
    return token;
  };

  while (true) {
    // Set the initial state.
    startIndex = undefined;
    endIndex = undefined;
    anyNonWS = false;
    isCloseBrace = false;
    lastWasWS = false;

    // Read tokens until we see a reason to insert a newline.
    let token = readUntilNewlineNeeded();

    // Append any saved up text to the result, applying indentation.
    if (startIndex !== undefined) {
      if (isCloseBrace && !anyNonWS) {
        // If we saw only whitespace followed by a "}", then we don't
        // need anything here.
      } else {
        result = result + indent + text.substring(startIndex, endIndex);
        if (isCloseBrace) {
          result += prettifyCSS.LINE_SEPARATOR;
        }
      }
    }

    // Get preference of the user regarding what to use for indentation,
    // spaces or tabs.
    let tabPrefs = getTabPrefs();

    if (isCloseBrace) {
      // Even if the stylesheet contains extra closing braces, the indent level should
      // remain > 0.
      indentLevel = Math.max(0, indentLevel - 1);

      if (tabPrefs.indentWithTabs) {
        indent = TAB_CHARS.repeat(indentLevel);
      } else {
        indent = SPACE_CHARS.repeat(indentLevel);
      }
      result = result + indent + "}";
    }

    if (!token) {
      break;
    }

    if (token.tokenType === "symbol" && token.text === "{") {
      if (!lastWasWS) {
        result += " ";
      }
      result += "{";
      if (tabPrefs.indentWithTabs) {
        indent = TAB_CHARS.repeat(++indentLevel);
      } else {
        indent = SPACE_CHARS.repeat(++indentLevel);
      }
    }

    // Now it is time to insert a newline.  However first we want to
    // deal with any trailing comments.
    token = readUntilSignificantToken();

    // "Early" bail-out if the text does not appear to be minified.
    // Here we ignore the case where whitespace appears at the end of
    // the text.
    if (pushbackToken && token && token.tokenType === "whitespace" &&
        /\n/g.test(text.substring(token.startOffset, token.endOffset))) {
      return originalText;
    }

    // Finally time for that newline.
    result = result + prettifyCSS.LINE_SEPARATOR;

    // Maybe we hit EOF.
    if (!pushbackToken) {
      break;
    }
  }

  return result;
}

exports.prettifyCSS = prettifyCSS;

/**
 * Find a unique CSS selector for a given element
 * @returns a string such that ele.ownerDocument.querySelector(reply) === ele
 * and ele.ownerDocument.querySelectorAll(reply).length === 1
 */
exports.findCssSelector = findCssSelector;

/**
 * Get the full CSS path for a given element.
 * @returns a string that can be used as a CSS selector for the element. It might not
 * match the element uniquely. It does however, represent the full path from the root
 * node to the element.
 */
function getCssPath(ele) {
  ele = getRootBindingParent(ele);
  const document = ele.ownerDocument;
  if (!document || !document.contains(ele)) {
    throw new Error("getCssPath received element not inside document");
  }

  const getElementSelector = element => {
    if (!element.localName) {
      return "";
    }

    let label = element.nodeName == element.nodeName.toUpperCase()
                ? element.localName.toLowerCase()
                : element.localName;

    if (element.id) {
      label += "#" + element.id;
    }

    if (element.classList) {
      for (let cl of element.classList) {
        label += "." + cl;
      }
    }

    return label;
  };

  let paths = [];

  while (ele) {
    if (!ele || ele.nodeType !== Node.ELEMENT_NODE) {
      break;
    }

    paths.splice(0, 0, getElementSelector(ele));
    ele = ele.parentNode;
  }

  return paths.length ? paths.join(" ") : "";
}
exports.getCssPath = getCssPath;

/**
 * Get the xpath for a given element.
 * @param {DomNode} ele
 * @returns a string that can be used as an XPath to find the element uniquely.
 */
function getXPath(ele) {
  ele = getRootBindingParent(ele);
  const document = ele.ownerDocument;
  if (!document || !document.contains(ele)) {
    throw new Error("getXPath received element not inside document");
  }

  // Create a short XPath for elements with IDs.
  if (ele.id) {
    return `//*[@id="${ele.id}"]`;
  }

  // Otherwise walk the DOM up and create a part for each ancestor.
  const parts = [];

  // Use nodeName (instead of localName) so namespace prefix is included (if any).
  while (ele && ele.nodeType === Node.ELEMENT_NODE) {
    let nbOfPreviousSiblings = 0;
    let hasNextSiblings = false;

    // Count how many previous same-name siblings the element has.
    let sibling = ele.previousSibling;
    while (sibling) {
      // Ignore document type declaration.
      if (sibling.nodeType !== Node.DOCUMENT_TYPE_NODE &&
          sibling.nodeName == ele.nodeName) {
        nbOfPreviousSiblings++;
      }

      sibling = sibling.previousSibling;
    }

    // Check if the element has at least 1 next same-name sibling.
    sibling = ele.nextSibling;
    while (sibling) {
      if (sibling.nodeName == ele.nodeName) {
        hasNextSiblings = true;
        break;
      }
      sibling = sibling.nextSibling;
    }

    const prefix = ele.prefix ? ele.prefix + ":" : "";
    const nth = nbOfPreviousSiblings || hasNextSiblings
                ? `[${nbOfPreviousSiblings + 1}]` : "";

    parts.push(prefix + ele.localName + nth);

    ele = ele.parentNode;
  }

  return parts.length ? "/" + parts.reverse().join("/") : "";
}
exports.getXPath = getXPath;

/**
 * Given a node, check to see if it is a ::before or ::after element.
 * If so, return the node that is accessible from within the document
 * (the parent of the anonymous node), along with which pseudo element
 * it was.  Otherwise, return the node itself.
 *
 * @returns {Object}
 *            - {DOMNode} node The non-anonymous node
 *            - {string} pseudo One of ':before', ':after', or null.
 */
function getBindingElementAndPseudo(node) {
  let bindingElement = node;
  let pseudo = null;
  if (node.nodeName == "_moz_generated_content_before") {
    bindingElement = node.parentNode;
    pseudo = ":before";
  } else if (node.nodeName == "_moz_generated_content_after") {
    bindingElement = node.parentNode;
    pseudo = ":after";
  }
  return {
    bindingElement: bindingElement,
    pseudo: pseudo
  };
}
exports.getBindingElementAndPseudo = getBindingElementAndPseudo;

/**
 * Returns css style rules for a given a node.
 * This function can handle ::before or ::after pseudo element as well as
 * normal element.
 */
function getCSSStyleRules(node) {
  let { bindingElement, pseudo } = getBindingElementAndPseudo(node);
  return InspectorUtils.getCSSStyleRules(bindingElement, pseudo);
}
exports.getCSSStyleRules = getCSSStyleRules;
PK
!<�O<<>chrome/devtools/modules/devtools/shared/jsbeautify/beautify.jsvar { cssBeautify } = require("devtools/shared/jsbeautify/src/beautify-css");
var { htmlBeautify } = require("devtools/shared/jsbeautify/src/beautify-html");
var { jsBeautify } = require("devtools/shared/jsbeautify/src/beautify-js");

exports.css = cssBeautify;
exports.html = htmlBeautify;
exports.js = jsBeautify;
PK
!<P����Dchrome/devtools/modules/devtools/shared/jsbeautify/lib/sanitytest.js//
// simple testing interface
// written by Einar Lielmanis, [email protected]
//
// usage:
//
// var t = new SanityTest(function (x) { return x; }, 'my function');
// t.expect('input', 'output');
// t.expect('a', 'a');
// output_somewhere(t.results()); // good for <pre>, html safe-ish
// alert(t.results_raw());        // html unescaped


function SanityTest (func, name_of_test) {

    var test_func = func || function (x) {
        return x;
    };

    var test_name = name_of_test || '';

    var n_failed = 0;
    var n_succeeded = 0;

    this.failures = [];
    this.successes = [];

    this.test_function = function(func, name) {
        test_func = func;
        test_name = name || '';
    };

    this.get_exitcode = function() {
        return n_succeeded === 0 || n_failed !== 0 ? 1 : 0;
    };

    this.expect = function(parameters, expected_value) {
        // multi-parameter calls not supported (I don't need them now).
        var result = test_func(parameters);
        // proper array checking is a pain. i'll maybe do it later, compare strings representations instead
        if ((result === expected_value) || (expected_value instanceof Array && result.join(', ') == expected_value.join(', '))) {
            n_succeeded += 1;
            this.successes.push([test_name, parameters, expected_value, result]);
        } else {
            n_failed += 1;
            this.failures.push([test_name, parameters, expected_value, result]);
        }
    };


    this.results_raw = function() {
        var results = '';
        if (n_failed === 0) {
            if (n_succeeded === 0) {
                results = 'No tests run.';
            } else {
                results = 'All ' + n_succeeded + ' tests passed.';
            }
        } else {
            for (var i = 0 ; i < this.failures.length; i++) {
                var f = this.failures[i];
                if (f[0]) {
                    f[0] = f[0] + ' ';
                }
                results += '---- ' + f[0] + 'input -------\n' + this.prettyprint(f[1]) + '\n';
                results += '---- ' + f[0] + 'expected ----\n' + this.prettyprint(f[2]) + '\n';
                results += '---- ' + f[0] + 'output ------\n' + this.prettyprint(f[3]) + '\n\n';

            }
            results += n_failed + ' tests failed.\n';
        }
        return results;
    };


    this.results = function() {
        return this.lazy_escape(this.results_raw());
    };


    this.prettyprint = function(something, quote_strings) {
        var type = typeof something;
        switch(type.toLowerCase()) {
        case 'string':
            if (quote_strings) {
                return "'" + something.replace("'", "\\'") + "'";
            } else {
                return something;
            }
        case 'number':
            return '' + something;
        case 'boolean':
            return something ? 'true' : 'false';
        case 'undefined':
            return 'undefined';
        case 'object':
            if (something instanceof Array) {
                var x = [];
                var expected_index = 0;
                for (var k in something) {
                    if (k == expected_index) {
                        x.push(this.prettyprint(something[k], true));
                        expected_index += 1;
                    } else {
                        x.push('\n' + k + ': ' + this.prettyprint(something[k], true));
                    }
                }
                return '[' + x.join(', ') + ']';
            } else {
                return 'object: ' + something;
            }
        default:
            return type + ': ' + something;
        }
    };


    this.lazy_escape = function (str) {
        return str.replace(/</g, '&lt;').replace(/\>/g, '&gt;').replace(/\n/g, '<br />');
    };


    this.log = function () {
        if (window.console) {
            if (console.firebug) {
                console.log.apply(console, Array.prototype.slice.call(arguments));
            } else {
                console.log.call(console, Array.prototype.slice.call(arguments));
            }
        }
    };

}

if (typeof module !== 'undefined' && module.exports) {
    module.exports = SanityTest;
}
PK
!<wO�5��Lchrome/devtools/modules/devtools/shared/jsbeautify/lib/urlencode_unpacker.js/*global unescape */
/*jshint curly: false, scripturl: true */
//
// trivial bookmarklet/escaped script detector for the javascript beautifier
// written by Einar Lielmanis <[email protected]>
//
// usage:
//
// if (Urlencoded.detect(some_string)) {
//     var unpacked = Urlencoded.unpack(some_string);
// }
//
//

var isNode = (typeof module !== 'undefined' && module.exports);
if (isNode) {
    var SanityTest = require("devtools/shared/jsbeautify/lib/sanitytest");
}

var Urlencoded = {
    detect: function (str) {
        // the fact that script doesn't contain any space, but has %20 instead
        // should be sufficient check for now.
        if (!str.includes(' ')) {
            if (str.includes('%2')) return true;
            if (str.replace(/[^%]+/g, '').length > 3) return true;
        }
        return false;
    },

    unpack: function (str) {
        if (Urlencoded.detect(str)) {
            if (str.includes('%2B') || str.includes('%2b')) {
                // "+" escaped as "%2B"
                return unescape(str.replace(/\+/g, '%20'));
            } else {
                return unescape(str);
            }
        }
        return str;
    },



    run_tests: function (sanity_test) {
        var t = sanity_test || new SanityTest();
        t.test_function(Urlencoded.detect, "Urlencoded.detect");
        t.expect('', false);
        t.expect('var a = b', false);
        t.expect('var%20a+=+b', true);
        t.expect('var%20a=b', true);
        t.expect('var%20%21%22', true);
        t.expect('javascript:(function(){var%20whatever={init:function(){alert(%22a%22+%22b%22)}};whatever.init()})();', true);
        t.test_function(Urlencoded.unpack, 'Urlencoded.unpack');

        t.expect('javascript:(function(){var%20whatever={init:function(){alert(%22a%22+%22b%22)}};whatever.init()})();',
            'javascript:(function(){var whatever={init:function(){alert("a"+"b")}};whatever.init()})();'
        );
        t.expect('', '');
        t.expect('abcd', 'abcd');
        t.expect('var a = b', 'var a = b');
        t.expect('var%20a=b', 'var a=b');
        t.expect('var%20a=b+1', 'var a=b+1');
        t.expect('var%20a=b%2b1', 'var a=b+1');
        return t;
    }


};

if (isNode) {
    module.exports = Urlencoded;
}
PK
!<'`��4/4/Fchrome/devtools/modules/devtools/shared/jsbeautify/src/beautify-css.js/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
/*

  The MIT License (MIT)

  Copyright (c) 2007-2013 Einar Lielmanis and 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.


 CSS Beautifier
---------------

    Written by Harutyun Amirjanyan, ([email protected])

    Based on code initially developed by: Einar Lielmanis, <[email protected]>
        http://jsbeautifier.org/

    Usage:
        css_beautify(source_text);
        css_beautify(source_text, options);

    The options are (default in brackets):
        indent_size (4)                   — indentation size,
        indent_char (space)               — character to indent with,
        selector_separator_newline (true) - separate selectors with newline or
                                            not (e.g. "a,\nbr" or "a, br")
        end_with_newline (false)          - end with a newline

    e.g

    css_beautify(css_source_text, {
      'indent_size': 1,
      'indent_char': '\t',
      'selector_separator': ' ',
      'end_with_newline': false,
    });
*/

// http://www.w3.org/TR/CSS21/syndata.html#tokenization
// http://www.w3.org/TR/css3-syntax/

(function () {
    function css_beautify(source_text, options) {
        options = options || {};
        var indentSize = options.indent_size || 4;
        var indentCharacter = options.indent_char || ' ';
        var selectorSeparatorNewline = (options.selector_separator_newline === undefined) ? true : options.selector_separator_newline;
        var endWithNewline = (options.end_with_newline === undefined) ? false : options.end_with_newline;

        // compatibility
        if (typeof indentSize === "string") {
            indentSize = parseInt(indentSize, 10);
        }


        // tokenizer
        var whiteRe = /^\s+$/;
        var wordRe = /[\w$\-_]/;

        var pos = -1,
            ch;

        function next() {
            ch = source_text.charAt(++pos);
            return ch;
        }

        function peek() {
            return source_text.charAt(pos + 1);
        }

        function eatString(endChar) {
            var start = pos;
            while (next()) {
                if (ch === "\\") {
                    next();
                    next();
                } else if (ch === endChar) {
                    break;
                } else if (ch === "\n") {
                    break;
                }
            }
            return source_text.substring(start, pos + 1);
        }

        function eatWhitespace() {
            var start = pos;
            while (whiteRe.test(peek())) {
                pos++;
            }
            return pos !== start;
        }

        function skipWhitespace() {
            var start = pos;
            do {} while (whiteRe.test(next()));
            return pos !== start + 1;
        }

        function eatComment(singleLine) {
            var start = pos;
            next();
            while (next()) {
                if (ch === "*" && peek() === "/") {
                    pos++;
                    break;
                } else if (singleLine && ch === "\n") {
                    break;
                }
            }

            return source_text.substring(start, pos + 1);
        }


        function lookBack(str) {
            return source_text.substring(pos - str.length, pos).toLowerCase() ===
                str;
        }

        function isCommentOnLine() {
            var endOfLine = source_text.indexOf('\n', pos);
            if (endOfLine === -1) {
                return false;
            }
            var restOfLine = source_text.substring(pos, endOfLine);
            return restOfLine.includes('//');
        }

        // printer
        var indentString = source_text.match(/^[\r\n]*[\t ]*/)[0];
        var singleIndent = new Array(indentSize + 1).join(indentCharacter);
        var indentLevel = 0;
        var nestedLevel = 0;

        function indent() {
            indentLevel++;
            indentString += singleIndent;
        }

        function outdent() {
            indentLevel--;
            indentString = indentString.slice(0, -indentSize);
        }

        var print = {};
        print["{"] = function (ch) {
            print.singleSpace();
            output.push(ch);
            print.newLine();
        };
        print["}"] = function (ch) {
            print.newLine();
            output.push(ch);
            print.newLine();
        };

        print._lastCharWhitespace = function () {
            return whiteRe.test(output[output.length - 1]);
        };

        print.newLine = function (keepWhitespace) {
            if (!keepWhitespace) {
                while (print._lastCharWhitespace()) {
                    output.pop();
                }
            }

            if (output.length) {
                output.push('\n');
            }
            if (indentString) {
                output.push(indentString);
            }
        };
        print.singleSpace = function () {
            if (output.length && !print._lastCharWhitespace()) {
                output.push(' ');
            }
        };
        var output = [];
        if (indentString) {
            output.push(indentString);
        }
        /*_____________________--------------------_____________________*/

        var insideRule = false;
        var enteringConditionalGroup = false;

        while (true) {
            var isAfterSpace = skipWhitespace();

            if (!ch) {
                break;
            } else if (ch === '/' && peek() === '*') { /* css comment */
                print.newLine();
                output.push(eatComment(), "\n", indentString);
                var header = lookBack("");
                if (header) {
                    print.newLine();
                }
            } else if (ch === '/' && peek() === '/') { // single line comment
                output.push(eatComment(true), indentString);
            } else if (ch === '@') {
                // strip trailing space, if present, for hash property checks
                var atRule = eatString(" ").replace(/ $/, '');

                // pass along the space we found as a separate item
                output.push(atRule, ch);

                // might be a nesting at-rule
                if (atRule in css_beautify.NESTED_AT_RULE) {
                    nestedLevel += 1;
                    if (atRule in css_beautify.CONDITIONAL_GROUP_RULE) {
                        enteringConditionalGroup = true;
                    }
                }
            } else if (ch === '{') {
                eatWhitespace();
                if (peek() === '}') {
                    next();
                    output.push(" {}");
                } else {
                    indent();
                    print["{"](ch);
                    // when entering conditional groups, only rulesets are allowed
                    if (enteringConditionalGroup) {
                        enteringConditionalGroup = false;
                        insideRule = (indentLevel > nestedLevel);
                    } else {
                        // otherwise, declarations are also allowed
                        insideRule = (indentLevel >= nestedLevel);
                    }
                }
            } else if (ch === '}') {
                outdent();
                print["}"](ch);
                insideRule = false;
                if (nestedLevel) {
                    nestedLevel--;
                }
            } else if (ch === ":") {
                eatWhitespace();
                if (insideRule || enteringConditionalGroup) {
                    // 'property: value' delimiter
                    // which could be in a conditional group query
                    output.push(ch, " ");
                } else {
                    if (peek() === ":") {
                        // pseudo-element
                        next();
                        output.push("::");
                    } else {
                        // pseudo-class
                        output.push(ch);
                    }
                }
            } else if (ch === '"' || ch === '\'') {
                output.push(eatString(ch));
            } else if (ch === ';') {
                if (isCommentOnLine()) {
                    var beforeComment = eatString('/');
                    var comment = eatComment(true);
                    output.push(beforeComment, comment.substring(1, comment.length - 1), '\n', indentString);
                } else {
                    output.push(ch, '\n', indentString);
                }
            } else if (ch === '(') { // may be a url
                if (lookBack("url")) {
                    output.push(ch);
                    eatWhitespace();
                    if (next()) {
                        if (ch !== ')' && ch !== '"' && ch !== '\'') {
                            output.push(eatString(')'));
                        } else {
                            pos--;
                        }
                    }
                } else {
                    if (isAfterSpace) {
                        print.singleSpace();
                    }
                    output.push(ch);
                    eatWhitespace();
                }
            } else if (ch === ')') {
                output.push(ch);
            } else if (ch === ',') {
                eatWhitespace();
                output.push(ch);
                if (!insideRule && selectorSeparatorNewline) {
                    print.newLine();
                } else {
                    print.singleSpace();
                }
            } else if (ch === ']') {
                output.push(ch);
            } else if (ch === '[') {
                if (isAfterSpace) {
                    print.singleSpace();
                }
                output.push(ch);
            } else if (ch === '=') { // no whitespace before or after
                eatWhitespace();
                output.push(ch);
            } else {
                if (isAfterSpace) {
                    print.singleSpace();
                }

                output.push(ch);
            }
        }


        var sweetCode = output.join('').replace(/[\n ]+$/, '');

        // establish end_with_newline
        var should = endWithNewline;
        var actually = /\n$/.test(sweetCode);
        if (should && !actually) {
            sweetCode += "\n";
        } else if (!should && actually) {
            sweetCode = sweetCode.slice(0, -1);
        }

        return sweetCode;
    }

    // https://developer.mozilla.org/en-US/docs/Web/CSS/At-rule
    css_beautify.NESTED_AT_RULE = {
        "@page": true,
        "@font-face": true,
        "@keyframes": true,
        // also in CONDITIONAL_GROUP_RULE below
        "@media": true,
        "@supports": true,
        "@document": true
    };
    css_beautify.CONDITIONAL_GROUP_RULE = {
        "@media": true,
        "@supports": true,
        "@document": true
    };

    exports.cssBeautify = css_beautify;
}());
PK
!<�fc+��Gchrome/devtools/modules/devtools/shared/jsbeautify/src/beautify-html.js/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
/*

  The MIT License (MIT)

  Copyright (c) 2007-2013 Einar Lielmanis and 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.


 Style HTML
---------------

  Written by Nochum Sossonko, ([email protected])

  Based on code initially developed by: Einar Lielmanis, <[email protected]>
    http://jsbeautifier.org/

  Usage:
    style_html(html_source);

    style_html(html_source, options);

  The options are:
    indent_inner_html (default false)  — indent <head> and <body> sections,
    indent_size (default 4)          — indentation size,
    indent_char (default space)      — character to indent with,
    wrap_line_length (default 250)            -  maximum amount of characters per line (0 = disable)
    brace_style (default "collapse") - "collapse" | "expand" | "end-expand"
            put braces on the same line as control statements (default), or put braces on own line (Allman / ANSI style), or just put end braces on own line.
    unformatted (defaults to inline tags) - list of tags, that shouldn't be reformatted
    indent_scripts (default normal)  - "keep"|"separate"|"normal"
    preserve_newlines (default true) - whether existing line breaks before elements should be preserved
                                        Only works before elements, not inside tags or for text.
    max_preserve_newlines (default unlimited) - maximum number of line breaks to be preserved in one chunk
    indent_handlebars (default false) - format and indent {{#foo}} and {{/foo}}

    e.g.

    style_html(html_source, {
      'indent_inner_html': false,
      'indent_size': 2,
      'indent_char': ' ',
      'wrap_line_length': 78,
      'brace_style': 'expand',
      'unformatted': ['a', 'sub', 'sup', 'b', 'i', 'u'],
      'preserve_newlines': true,
      'max_preserve_newlines': 5,
      'indent_handlebars': false
    });
*/

(function() {

    function trim(s) {
        return s.replace(/^\s+|\s+$/g, '');
    }

    function ltrim(s) {
        return s.replace(/^\s+/g, '');
    }

    function style_html(html_source, options, js_beautify, css_beautify) {
        //Wrapper function to invoke all the necessary constructors and deal with the output.

        var multi_parser,
            indent_inner_html,
            indent_size,
            indent_character,
            wrap_line_length,
            brace_style,
            unformatted,
            preserve_newlines,
            max_preserve_newlines,
            indent_handlebars;

        options = options || {};

        // backwards compatibility to 1.3.4
        if ((options.wrap_line_length === undefined || parseInt(options.wrap_line_length, 10) === 0) &&
                (options.max_char !== undefined && parseInt(options.max_char, 10) !== 0)) {
            options.wrap_line_length = options.max_char;
        }

        indent_inner_html = (options.indent_inner_html === undefined) ? false : options.indent_inner_html;
        indent_size = (options.indent_size === undefined) ? 4 : parseInt(options.indent_size, 10);
        indent_character = (options.indent_char === undefined) ? ' ' : options.indent_char;
        brace_style = (options.brace_style === undefined) ? 'collapse' : options.brace_style;
        wrap_line_length =  parseInt(options.wrap_line_length, 10) === 0 ? 32786 : parseInt(options.wrap_line_length || 250, 10);
        unformatted = options.unformatted || ['a', 'span', 'bdo', 'em', 'strong', 'dfn', 'code', 'samp', 'kbd', 'var', 'cite', 'abbr', 'acronym', 'q', 'sub', 'sup', 'tt', 'i', 'b', 'big', 'small', 'u', 's', 'strike', 'font', 'ins', 'del', 'pre', 'address', 'dt', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
        preserve_newlines = (options.preserve_newlines === undefined) ? true : options.preserve_newlines;
        max_preserve_newlines = preserve_newlines ?
            (isNaN(parseInt(options.max_preserve_newlines, 10)) ? 32786 : parseInt(options.max_preserve_newlines, 10))
            : 0;
        indent_handlebars = (options.indent_handlebars === undefined) ? false : options.indent_handlebars;

        function Parser() {

            this.pos = 0; //Parser position
            this.token = '';
            this.current_mode = 'CONTENT'; //reflects the current Parser mode: TAG/CONTENT
            this.tags = { //An object to hold tags, their position, and their parent-tags, initiated with default values
                parent: 'parent1',
                parentcount: 1,
                parent1: ''
            };
            this.tag_type = '';
            this.token_text = this.last_token = this.last_text = this.token_type = '';
            this.newlines = 0;
            this.indent_content = indent_inner_html;

            this.Utils = { //Uilities made available to the various functions
                whitespace: "\n\r\t ".split(''),
                single_token: 'br,input,link,meta,!doctype,basefont,base,area,hr,wbr,param,img,isindex,?xml,embed,?php,?,?='.split(','), //all the single tags for HTML
                extra_liners: 'head,body,/html'.split(','), //for tags that need a line of whitespace before them
                in_array: function(what, arr) {
                    for (var i = 0; i < arr.length; i++) {
                        if (what === arr[i]) {
                            return true;
                        }
                    }
                    return false;
                }
            };

            this.traverse_whitespace = function() {
                var input_char = '';

                input_char = this.input.charAt(this.pos);
                if (this.Utils.in_array(input_char, this.Utils.whitespace)) {
                    this.newlines = 0;
                    while (this.Utils.in_array(input_char, this.Utils.whitespace)) {
                        if (preserve_newlines && input_char === '\n' && this.newlines <= max_preserve_newlines) {
                            this.newlines += 1;
                        }

                        this.pos++;
                        input_char = this.input.charAt(this.pos);
                    }
                    return true;
                }
                return false;
            };

            this.get_content = function() { //function to capture regular content between tags

                var input_char = '',
                    content = [],
                    space = false; //if a space is needed

                while (this.input.charAt(this.pos) !== '<') {
                    if (this.pos >= this.input.length) {
                        return content.length ? content.join('') : ['', 'TK_EOF'];
                    }

                    if (this.traverse_whitespace()) {
                        if (content.length) {
                            space = true;
                        }
                        continue; //don't want to insert unnecessary space
                    }

                    if (indent_handlebars) {
                        // Handlebars parsing is complicated.
                        // {{#foo}} and {{/foo}} are formatted tags.
                        // {{something}} should get treated as content, except:
                        // {{else}} specifically behaves like {{#if}} and {{/if}}
                        var peek3 = this.input.substr(this.pos, 3);
                        if (peek3 === '{{#' || peek3 === '{{/') {
                            // These are tags and not content.
                            break;
                        } else if (this.input.substr(this.pos, 2) === '{{') {
                            if (this.get_tag(true) === '{{else}}') {
                                break;
                            }
                        }
                    }

                    input_char = this.input.charAt(this.pos);
                    this.pos++;

                    if (space) {
                        if (this.line_char_count >= this.wrap_line_length) { //insert a line when the wrap_line_length is reached
                            this.print_newline(false, content);
                            this.print_indentation(content);
                        } else {
                            this.line_char_count++;
                            content.push(' ');
                        }
                        space = false;
                    }
                    this.line_char_count++;
                    content.push(input_char); //letter at-a-time (or string) inserted to an array
                }
                return content.length ? content.join('') : '';
            };

            this.get_contents_to = function(name) { //get the full content of a script or style to pass to js_beautify
                if (this.pos === this.input.length) {
                    return ['', 'TK_EOF'];
                }
                var input_char = '';
                var content = '';
                var reg_match = new RegExp('</' + name + '\\s*>', 'igm');
                reg_match.lastIndex = this.pos;
                var reg_array = reg_match.exec(this.input);
                var end_script = reg_array ? reg_array.index : this.input.length; //absolute end of script
                if (this.pos < end_script) { //get everything in between the script tags
                    content = this.input.substring(this.pos, end_script);
                    this.pos = end_script;
                }
                return content;
            };

            this.record_tag = function(tag) { //function to record a tag and its parent in this.tags Object
                if (this.tags[tag + 'count']) { //check for the existence of this tag type
                    this.tags[tag + 'count']++;
                    this.tags[tag + this.tags[tag + 'count']] = this.indent_level; //and record the present indent level
                } else { //otherwise initialize this tag type
                    this.tags[tag + 'count'] = 1;
                    this.tags[tag + this.tags[tag + 'count']] = this.indent_level; //and record the present indent level
                }
                this.tags[tag + this.tags[tag + 'count'] + 'parent'] = this.tags.parent; //set the parent (i.e. in the case of a div this.tags.div1parent)
                this.tags.parent = tag + this.tags[tag + 'count']; //and make this the current parent (i.e. in the case of a div 'div1')
            };

            this.retrieve_tag = function(tag) { //function to retrieve the opening tag to the corresponding closer
                if (this.tags[tag + 'count']) { //if the openener is not in the Object we ignore it
                    var temp_parent = this.tags.parent; //check to see if it's a closable tag.
                    while (temp_parent) { //till we reach '' (the initial value);
                        if (tag + this.tags[tag + 'count'] === temp_parent) { //if this is it use it
                            break;
                        }
                        temp_parent = this.tags[temp_parent + 'parent']; //otherwise keep on climbing up the DOM Tree
                    }
                    if (temp_parent) { //if we caught something
                        this.indent_level = this.tags[tag + this.tags[tag + 'count']]; //set the indent_level accordingly
                        this.tags.parent = this.tags[temp_parent + 'parent']; //and set the current parent
                    }
                    delete this.tags[tag + this.tags[tag + 'count'] + 'parent']; //delete the closed tags parent reference...
                    delete this.tags[tag + this.tags[tag + 'count']]; //...and the tag itself
                    if (this.tags[tag + 'count'] === 1) {
                        delete this.tags[tag + 'count'];
                    } else {
                        this.tags[tag + 'count']--;
                    }
                }
            };

            this.indent_to_tag = function(tag) {
                // Match the indentation level to the last use of this tag, but don't remove it.
                if (!this.tags[tag + 'count']) {
                    return;
                }
                var temp_parent = this.tags.parent;
                while (temp_parent) {
                    if (tag + this.tags[tag + 'count'] === temp_parent) {
                        break;
                    }
                    temp_parent = this.tags[temp_parent + 'parent'];
                }
                if (temp_parent) {
                    this.indent_level = this.tags[tag + this.tags[tag + 'count']];
                }
            };

            this.get_tag = function(peek) { //function to get a full tag and parse its type
                var input_char = '',
                    content = [],
                    comment = '',
                    space = false,
                    tag_start, tag_end,
                    tag_start_char,
                    orig_pos = this.pos,
                    orig_line_char_count = this.line_char_count;

                peek = peek !== undefined ? peek : false;

                do {
                    if (this.pos >= this.input.length) {
                        if (peek) {
                            this.pos = orig_pos;
                            this.line_char_count = orig_line_char_count;
                        }
                        return content.length ? content.join('') : ['', 'TK_EOF'];
                    }

                    input_char = this.input.charAt(this.pos);
                    this.pos++;

                    if (this.Utils.in_array(input_char, this.Utils.whitespace)) { //don't want to insert unnecessary space
                        space = true;
                        continue;
                    }

                    if (input_char === "'" || input_char === '"') {
                        input_char += this.get_unformatted(input_char);
                        space = true;

                    }

                    if (input_char === '=') { //no space before =
                        space = false;
                    }

                    if (content.length && content[content.length - 1] !== '=' && input_char !== '>' && space) {
                        //no space after = or before >
                        if (this.line_char_count >= this.wrap_line_length) {
                            this.print_newline(false, content);
                            this.print_indentation(content);
                        } else {
                            content.push(' ');
                            this.line_char_count++;
                        }
                        space = false;
                    }

                    if (indent_handlebars && tag_start_char === '<') {
                        // When inside an angle-bracket tag, put spaces around
                        // handlebars not inside of strings.
                        if ((input_char + this.input.charAt(this.pos)) === '{{') {
                            input_char += this.get_unformatted('}}');
                            if (content.length && content[content.length - 1] !== ' ' && content[content.length - 1] !== '<') {
                                input_char = ' ' + input_char;
                            }
                            space = true;
                        }
                    }

                    if (input_char === '<' && !tag_start_char) {
                        tag_start = this.pos - 1;
                        tag_start_char = '<';
                    }

                    if (indent_handlebars && !tag_start_char) {
                        if (content.length >= 2 && content[content.length - 1] === '{' && content[content.length - 2] == '{') {
                            if (input_char === '#' || input_char === '/') {
                                tag_start = this.pos - 3;
                            } else {
                                tag_start = this.pos - 2;
                            }
                            tag_start_char = '{';
                        }
                    }

                    this.line_char_count++;
                    content.push(input_char); //inserts character at-a-time (or string)

                    if (content[1] && content[1] === '!') { //if we're in a comment, do something special
                        // We treat all comments as literals, even more than preformatted tags
                        // we just look for the appropriate close tag
                        content = [this.get_comment(tag_start)];
                        break;
                    }

                    if (indent_handlebars && tag_start_char === '{' && content.length > 2 && content[content.length - 2] === '}' && content[content.length - 1] === '}') {
                        break;
                    }
                } while (input_char !== '>');

                var tag_complete = content.join('');
                var tag_index;
                var tag_offset;

                if (tag_complete.includes(' ')) { //if there's whitespace, thats where the tag name ends
                    tag_index = tag_complete.indexOf(' ');
                } else if (tag_complete[0] === '{') {
                    tag_index = tag_complete.indexOf('}');
                } else { //otherwise go with the tag ending
                    tag_index = tag_complete.indexOf('>');
                }
                if (tag_complete[0] === '<' || !indent_handlebars) {
                    tag_offset = 1;
                } else {
                    tag_offset = tag_complete[2] === '#' ? 3 : 2;
                }
                var tag_check = tag_complete.substring(tag_offset, tag_index).toLowerCase();
                if (tag_complete.charAt(tag_complete.length - 2) === '/' ||
                    this.Utils.in_array(tag_check, this.Utils.single_token)) { //if this tag name is a single tag type (either in the list or has a closing /)
                    if (!peek) {
                        this.tag_type = 'SINGLE';
                    }
                } else if (indent_handlebars && tag_complete[0] === '{' && tag_check === 'else') {
                    if (!peek) {
                        this.indent_to_tag('if');
                        this.tag_type = 'HANDLEBARS_ELSE';
                        this.indent_content = true;
                        this.traverse_whitespace();
                    }
                } else if (tag_check === 'script' &&
                    (tag_complete.search('type') === -1 ||
                    (tag_complete.search('type') > -1 &&
                    tag_complete.search(/\b(text|application)\/(x-)?(javascript|ecmascript|jscript|livescript)/) > -1))) {
                    if (!peek) {
                        this.record_tag(tag_check);
                        this.tag_type = 'SCRIPT';
                    }
                } else if (tag_check === 'style' &&
                    (tag_complete.search('type') === -1 ||
                    (tag_complete.search('type') > -1 && tag_complete.search('text/css') > -1))) {
                    if (!peek) {
                        this.record_tag(tag_check);
                        this.tag_type = 'STYLE';
                    }
                } else if (this.is_unformatted(tag_check, unformatted)) { // do not reformat the "unformatted" tags
                    comment = this.get_unformatted('</' + tag_check + '>', tag_complete); //...delegate to get_unformatted function
                    content.push(comment);
                    // Preserve collapsed whitespace either before or after this tag.
                    if (tag_start > 0 && this.Utils.in_array(this.input.charAt(tag_start - 1), this.Utils.whitespace)) {
                        content.splice(0, 0, this.input.charAt(tag_start - 1));
                    }
                    tag_end = this.pos - 1;
                    if (this.Utils.in_array(this.input.charAt(tag_end + 1), this.Utils.whitespace)) {
                        content.push(this.input.charAt(tag_end + 1));
                    }
                    this.tag_type = 'SINGLE';
                } else if (tag_check.charAt(0) === '!') { //peek for <! comment
                    // for comments content is already correct.
                    if (!peek) {
                        this.tag_type = 'SINGLE';
                        this.traverse_whitespace();
                    }
                } else if (!peek) {
                    if (tag_check.charAt(0) === '/') { //this tag is a double tag so check for tag-ending
                        this.retrieve_tag(tag_check.substring(1)); //remove it and all ancestors
                        this.tag_type = 'END';
                        this.traverse_whitespace();
                    } else { //otherwise it's a start-tag
                        this.record_tag(tag_check); //push it on the tag stack
                        if (tag_check.toLowerCase() !== 'html') {
                            this.indent_content = true;
                        }
                        this.tag_type = 'START';

                        // Allow preserving of newlines after a start tag
                        this.traverse_whitespace();
                    }
                    if (this.Utils.in_array(tag_check, this.Utils.extra_liners)) { //check if this double needs an extra line
                        this.print_newline(false, this.output);
                        if (this.output.length && this.output[this.output.length - 2] !== '\n') {
                            this.print_newline(true, this.output);
                        }
                    }
                }

                if (peek) {
                    this.pos = orig_pos;
                    this.line_char_count = orig_line_char_count;
                }

                return content.join(''); //returns fully formatted tag
            };

            this.get_comment = function(start_pos) { //function to return comment content in its entirety
                // this is will have very poor perf, but will work for now.
                var comment = '',
                    delimiter = '>',
                    matched = false;

                this.pos = start_pos;
                input_char = this.input.charAt(this.pos);
                this.pos++;

                while (this.pos <= this.input.length) {
                    comment += input_char;

                    // only need to check for the delimiter if the last chars match
                    if (comment[comment.length - 1] === delimiter[delimiter.length - 1] &&
                        comment.includes(delimiter)) {
                        break;
                    }

                    // only need to search for custom delimiter for the first few characters
                    if (!matched && comment.length < 10) {
                        if (comment.indexOf('<![if') === 0) { //peek for <![if conditional comment
                            delimiter = '<![endif]>';
                            matched = true;
                        } else if (comment.indexOf('<![cdata[') === 0) { //if it's a <[cdata[ comment...
                            delimiter = ']]>';
                            matched = true;
                        } else if (comment.indexOf('<![') === 0) { // some other ![ comment? ...
                            delimiter = ']>';
                            matched = true;
                        } else if (comment.indexOf('<!--') === 0) { // <!-- comment ...
                            delimiter = '-->';
                            matched = true;
                        }
                    }

                    input_char = this.input.charAt(this.pos);
                    this.pos++;
                }

                return comment;
            };

            this.get_unformatted = function(delimiter, orig_tag) { //function to return unformatted content in its entirety

                if (orig_tag && orig_tag.toLowerCase().includes(delimiter)) {
                    return '';
                }
                var input_char = '';
                var content = '';
                var min_index = 0;
                var space = true;
                do {

                    if (this.pos >= this.input.length) {
                        return content;
                    }

                    input_char = this.input.charAt(this.pos);
                    this.pos++;

                    if (this.Utils.in_array(input_char, this.Utils.whitespace)) {
                        if (!space) {
                            this.line_char_count--;
                            continue;
                        }
                        if (input_char === '\n' || input_char === '\r') {
                            content += '\n';
                            /*  Don't change tab indention for unformatted blocks.  If using code for html editing, this will greatly affect <pre> tags if they are specified in the 'unformatted array'
                for (var i=0; i<this.indent_level; i++) {
                  content += this.indent_string;
                }
                space = false; //...and make sure other indentation is erased
                */
                            this.line_char_count = 0;
                            continue;
                        }
                    }
                    content += input_char;
                    this.line_char_count++;
                    space = true;

                    if (indent_handlebars && input_char === '{' && content.length && content[content.length - 2] === '{') {
                        // Handlebars expressions in strings should also be unformatted.
                        content += this.get_unformatted('}}');
                        // These expressions are opaque.  Ignore delimiters found in them.
                        min_index = content.length;
                    }
                } while (!content.toLowerCase().includes(delimiter, min_index));
                return content;
            };

            this.get_token = function() { //initial handler for token-retrieval
                var token;

                if (this.last_token === 'TK_TAG_SCRIPT' || this.last_token === 'TK_TAG_STYLE') { //check if we need to format javascript
                    var type = this.last_token.substr(7);
                    token = this.get_contents_to(type);
                    if (typeof token !== 'string') {
                        return token;
                    }
                    return [token, 'TK_' + type];
                }
                if (this.current_mode === 'CONTENT') {
                    token = this.get_content();
                    if (typeof token !== 'string') {
                        return token;
                    } else {
                        return [token, 'TK_CONTENT'];
                    }
                }

                if (this.current_mode === 'TAG') {
                    token = this.get_tag();
                    if (typeof token !== 'string') {
                        return token;
                    } else {
                        var tag_name_type = 'TK_TAG_' + this.tag_type;
                        return [token, tag_name_type];
                    }
                }
            };

            this.get_full_indent = function(level) {
                level = this.indent_level + level || 0;
                if (level < 1) {
                    return '';
                }

                return Array(level + 1).join(this.indent_string);
            };

            this.is_unformatted = function(tag_check, unformatted) {
                //is this an HTML5 block-level link?
                if (!this.Utils.in_array(tag_check, unformatted)) {
                    return false;
                }

                if (tag_check.toLowerCase() !== 'a' || !this.Utils.in_array('a', unformatted)) {
                    return true;
                }

                //at this point we have an  tag; is its first child something we want to remain
                //unformatted?
                var next_tag = this.get_tag(true /* peek. */ );

                // test next_tag to see if it is just html tag (no external content)
                var tag = (next_tag || "").match(/^\s*<\s*\/?([a-z]*)\s*[^>]*>\s*$/);

                // if next_tag comes back but is not an isolated tag, then
                // let's treat the 'a' tag as having content
                // and respect the unformatted option
                if (!tag || this.Utils.in_array(tag, unformatted)) {
                    return true;
                } else {
                    return false;
                }
            };

            this.printer = function(js_source, indent_character, indent_size, wrap_line_length, brace_style) { //handles input/output and some other printing functions

                this.input = js_source || ''; //gets the input for the Parser
                this.output = [];
                this.indent_character = indent_character;
                this.indent_string = '';
                this.indent_size = indent_size;
                this.brace_style = brace_style;
                this.indent_level = 0;
                this.wrap_line_length = wrap_line_length;
                this.line_char_count = 0; //count to see if wrap_line_length was exceeded

                for (var i = 0; i < this.indent_size; i++) {
                    this.indent_string += this.indent_character;
                }

                this.print_newline = function(force, arr) {
                    this.line_char_count = 0;
                    if (!arr || !arr.length) {
                        return;
                    }
                    if (force || (arr[arr.length - 1] !== '\n')) { //we might want the extra line
                        arr.push('\n');
                    }
                };

                this.print_indentation = function(arr) {
                    for (var i = 0; i < this.indent_level; i++) {
                        arr.push(this.indent_string);
                        this.line_char_count += this.indent_string.length;
                    }
                };

                this.print_token = function(text) {
                    if (text || text !== '') {
                        if (this.output.length && this.output[this.output.length - 1] === '\n') {
                            this.print_indentation(this.output);
                            text = ltrim(text);
                        }
                    }
                    this.print_token_raw(text);
                };

                this.print_token_raw = function(text) {
                    if (text && text !== '') {
                        if (text.length > 1 && text[text.length - 1] === '\n') {
                            // unformatted tags can grab newlines as their last character
                            this.output.push(text.slice(0, -1));
                            this.print_newline(false, this.output);
                        } else {
                            this.output.push(text);
                        }
                    }

                    for (var n = 0; n < this.newlines; n++) {
                        this.print_newline(n > 0, this.output);
                    }
                    this.newlines = 0;
                };

                this.indent = function() {
                    this.indent_level++;
                };

                this.unindent = function() {
                    if (this.indent_level > 0) {
                        this.indent_level--;
                    }
                };
            };
            return this;
        }

        /*_____________________--------------------_____________________*/

        multi_parser = new Parser(); //wrapping functions Parser
        multi_parser.printer(html_source, indent_character, indent_size, wrap_line_length, brace_style); //initialize starting values

        while (true) {
            var t = multi_parser.get_token();
            multi_parser.token_text = t[0];
            multi_parser.token_type = t[1];

            if (multi_parser.token_type === 'TK_EOF') {
                break;
            }

            switch (multi_parser.token_type) {
                case 'TK_TAG_START':
                    multi_parser.print_newline(false, multi_parser.output);
                    multi_parser.print_token(multi_parser.token_text);
                    if (multi_parser.indent_content) {
                        multi_parser.indent();
                        multi_parser.indent_content = false;
                    }
                    multi_parser.current_mode = 'CONTENT';
                    break;
                case 'TK_TAG_STYLE':
                case 'TK_TAG_SCRIPT':
                    multi_parser.print_newline(false, multi_parser.output);
                    multi_parser.print_token(multi_parser.token_text);
                    multi_parser.current_mode = 'CONTENT';
                    break;
                case 'TK_TAG_END':
                    //Print new line only if the tag has no content and has child
                    if (multi_parser.last_token === 'TK_CONTENT' && multi_parser.last_text === '') {
                        var tag_name = multi_parser.token_text.match(/\w+/)[0];
                        var tag_extracted_from_last_output = null;
                        if (multi_parser.output.length) {
                            tag_extracted_from_last_output = multi_parser.output[multi_parser.output.length - 1].match(/(?:<|{{#)\s*(\w+)/);
                        }
                        if (tag_extracted_from_last_output === null ||
                            tag_extracted_from_last_output[1] !== tag_name) {
                            multi_parser.print_newline(false, multi_parser.output);
                        }
                    }
                    multi_parser.print_token(multi_parser.token_text);
                    multi_parser.current_mode = 'CONTENT';
                    break;
                case 'TK_TAG_SINGLE':
                    // Don't add a newline before elements that should remain unformatted.
                    var tag_check = multi_parser.token_text.match(/^\s*<([a-z]+)/i);
                    if (!tag_check || !multi_parser.Utils.in_array(tag_check[1], unformatted)) {
                        multi_parser.print_newline(false, multi_parser.output);
                    }
                    multi_parser.print_token(multi_parser.token_text);
                    multi_parser.current_mode = 'CONTENT';
                    break;
                case 'TK_TAG_HANDLEBARS_ELSE':
                    multi_parser.print_token(multi_parser.token_text);
                    if (multi_parser.indent_content) {
                        multi_parser.indent();
                        multi_parser.indent_content = false;
                    }
                    multi_parser.current_mode = 'CONTENT';
                    break;
                case 'TK_CONTENT':
                    multi_parser.print_token(multi_parser.token_text);
                    multi_parser.current_mode = 'TAG';
                    break;
                case 'TK_STYLE':
                case 'TK_SCRIPT':
                    if (multi_parser.token_text !== '') {
                        multi_parser.print_newline(false, multi_parser.output);
                        var text = multi_parser.token_text,
                            _beautifier,
                            script_indent_level = 1;
                        if (multi_parser.token_type === 'TK_SCRIPT') {
                            _beautifier = typeof js_beautify === 'function' && js_beautify;
                        } else if (multi_parser.token_type === 'TK_STYLE') {
                            _beautifier = typeof css_beautify === 'function' && css_beautify;
                        }

                        if (options.indent_scripts === "keep") {
                            script_indent_level = 0;
                        } else if (options.indent_scripts === "separate") {
                            script_indent_level = -multi_parser.indent_level;
                        }

                        var indentation = multi_parser.get_full_indent(script_indent_level);
                        if (_beautifier) {
                            // call the Beautifier if avaliable
                            text = _beautifier(text.replace(/^\s*/, indentation), options);
                        } else {
                            // simply indent the string otherwise
                            var white = text.match(/^\s*/)[0];
                            var _level = white.match(/[^\n\r]*$/)[0].split(multi_parser.indent_string).length - 1;
                            var reindent = multi_parser.get_full_indent(script_indent_level - _level);
                            text = text.replace(/^\s*/, indentation)
                                .replace(/\r\n|\r|\n/g, '\n' + reindent)
                                .replace(/\s+$/, '');
                        }
                        if (text) {
                            multi_parser.print_token_raw(indentation + trim(text));
                            multi_parser.print_newline(false, multi_parser.output);
                        }
                    }
                    multi_parser.current_mode = 'TAG';
                    break;
            }
            multi_parser.last_token = multi_parser.token_type;
            multi_parser.last_text = multi_parser.token_text;
        }
        return multi_parser.output.join('');
    }

    var beautify = require('devtools/shared/jsbeautify/beautify');

    exports.htmlBeautify = function(html_source, options) {
        return style_html(html_source, options, beautify.js, beautify.css);
    };
}());
PK
!<��	��Echrome/devtools/modules/devtools/shared/jsbeautify/src/beautify-js.js/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */

"use strict";

const acorn = require("acorn/acorn");

/*

  The MIT License (MIT)

  Copyright (c) 2007-2013 Einar Lielmanis and 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.

 JS Beautifier
---------------


  Written by Einar Lielmanis, <[email protected]>
      http://jsbeautifier.org/

  Originally converted to javascript by Vital, <[email protected]>
  "End braces on own line" added by Chris J. Shull, <[email protected]>
  Parsing improvements for brace-less statements by Liam Newman <[email protected]>


  Usage:
    js_beautify(js_source_text);
    js_beautify(js_source_text, options);

  The options are:
    indent_size (default 4)          - indentation size,
    indent_char (default space)      - character to indent with,
    preserve_newlines (default true) - whether existing line breaks should be preserved,
    max_preserve_newlines (default unlimited) - maximum number of line breaks to be preserved in one chunk,

    jslint_happy (default false) - if true, then jslint-stricter mode is enforced.

            jslint_happy       !jslint_happy
            ---------------------------------
            function ()        function()

    brace_style (default "collapse") - "collapse" | "expand" | "end-expand"
            put braces on the same line as control statements (default), or put braces on own line (Allman / ANSI style), or just put end braces on own line.

    space_before_conditional (default true) - should the space before conditional statement be added, "if(true)" vs "if (true)",

    unescape_strings (default false) - should printable characters in strings encoded in \xNN notation be unescaped, "example" vs "\x65\x78\x61\x6d\x70\x6c\x65"

    wrap_line_length (default unlimited) - lines should wrap at next opportunity after this number of characters.
          NOTE: This is not a hard limit. Lines will continue until a point where a newline would
                be preserved if it were present.

    e.g

    js_beautify(js_source_text, {
      'indent_size': 1,
      'indent_char': '\t'
    });

*/

var js_beautify = function js_beautify(js_source_text, options) {
    var beautifier = new Beautifier(js_source_text, options);
    return beautifier.beautify();
};

exports.jsBeautify = js_beautify;

function Beautifier(js_source_text, options) {
    var input, output_lines;
    var token_text, token_type, last_type, last_last_text, indent_string;
    var flags, previous_flags, flag_store;
    var whitespace, wordchar, punct, parser_pos, line_starters, reserved_words, digits;
    var prefix;
    var input_wanted_newline;
    var output_space_before_token;
    var input_length, n_newlines, whitespace_before_token;
    var handlers, MODE, opt;
    var preindent_string = '';



    whitespace = "\n\r\t ".split('');
    wordchar = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$'.split('');
    digits = '0123456789'.split('');

    punct = '+ - * / % & ++ -- = += -= *= /= %= == === != !== > < >= <= >> << >>> >>>= >>= <<= && &= | || ! ~ , : ? ^ ^= |= :: =>';
    punct += ' <%= <% %> <?= <? ?>'; // try to be a good boy and try not to break the markup language identifiers
    punct = punct.split(' ');

    // words which should always start on new line.
    line_starters = 'continue,try,throw,return,var,let,const,if,switch,case,default,for,while,break,function,yield'.split(',');
    reserved_words = line_starters.concat(['do', 'in', 'else', 'get', 'set', 'new', 'catch', 'finally', 'typeof']);


    MODE = {
        BlockStatement: 'BlockStatement', // 'BLOCK'
        Statement: 'Statement', // 'STATEMENT'
        ObjectLiteral: 'ObjectLiteral', // 'OBJECT',
        ArrayLiteral: 'ArrayLiteral', //'[EXPRESSION]',
        ForInitializer: 'ForInitializer', //'(FOR-EXPRESSION)',
        Conditional: 'Conditional', //'(COND-EXPRESSION)',
        Expression: 'Expression' //'(EXPRESSION)'
    };

    handlers = {
        'TK_START_EXPR': handle_start_expr,
        'TK_END_EXPR': handle_end_expr,
        'TK_START_BLOCK': handle_start_block,
        'TK_END_BLOCK': handle_end_block,
        'TK_WORD': handle_word,
        'TK_RESERVED': handle_word,
        'TK_SEMICOLON': handle_semicolon,
        'TK_STRING': handle_string,
        'TK_EQUALS': handle_equals,
        'TK_OPERATOR': handle_operator,
        'TK_COMMA': handle_comma,
        'TK_BLOCK_COMMENT': handle_block_comment,
        'TK_INLINE_COMMENT': handle_inline_comment,
        'TK_COMMENT': handle_comment,
        'TK_DOT': handle_dot,
        'TK_UNKNOWN': handle_unknown
    };

    function create_flags(flags_base, mode) {
        var next_indent_level = 0;
        if (flags_base) {
            next_indent_level = flags_base.indentation_level;
            if (!just_added_newline() &&
                flags_base.line_indent_level > next_indent_level) {
                next_indent_level = flags_base.line_indent_level;
            }
        }

        var next_flags = {
            mode: mode,
            parent: flags_base,
            last_text: flags_base ? flags_base.last_text : '', // last token text
            last_word: flags_base ? flags_base.last_word : '', // last 'TK_WORD' passed
            declaration_statement: false,
            declaration_assignment: false,
            in_html_comment: false,
            multiline_frame: false,
            if_block: false,
            else_block: false,
            do_block: false,
            do_while: false,
            in_case_statement: false, // switch(..){ INSIDE HERE }
            in_case: false, // we're on the exact line with "case 0:"
            case_body: false, // the indented case-action block
            indentation_level: next_indent_level,
            line_indent_level: flags_base ? flags_base.line_indent_level : next_indent_level,
            start_line_index: output_lines.length,
            had_comment: false,
            ternary_depth: 0
        };
        return next_flags;
    }

    // Using object instead of string to allow for later expansion of info about each line

    function create_output_line() {
        return {
            text: []
        };
    }

    // Some interpreters have unexpected results with foo = baz || bar;
    options = options ? options : {};
    opt = {};

    // compatibility
    if (options.space_after_anon_function !== undefined && options.jslint_happy === undefined) {
        options.jslint_happy = options.space_after_anon_function;
    }
    if (options.braces_on_own_line !== undefined) { //graceful handling of deprecated option
        opt.brace_style = options.braces_on_own_line ? "expand" : "collapse";
    }
    opt.brace_style = options.brace_style ? options.brace_style : (opt.brace_style ? opt.brace_style : "collapse");

    // graceful handling of deprecated option
    if (opt.brace_style === "expand-strict") {
        opt.brace_style = "expand";
    }


    opt.indent_size = options.indent_size ? parseInt(options.indent_size, 10) : 4;
    opt.indent_char = options.indent_char ? options.indent_char : ' ';
    opt.preserve_newlines = (options.preserve_newlines === undefined) ? true : options.preserve_newlines;
    opt.break_chained_methods = (options.break_chained_methods === undefined) ? false : options.break_chained_methods;
    opt.max_preserve_newlines = (options.max_preserve_newlines === undefined) ? 0 : parseInt(options.max_preserve_newlines, 10);
    opt.space_in_paren = (options.space_in_paren === undefined) ? false : options.space_in_paren;
    opt.space_in_empty_paren = (options.space_in_empty_paren === undefined) ? false : options.space_in_empty_paren;
    opt.jslint_happy = (options.jslint_happy === undefined) ? false : options.jslint_happy;
    opt.keep_array_indentation = (options.keep_array_indentation === undefined) ? false : options.keep_array_indentation;
    opt.space_before_conditional = (options.space_before_conditional === undefined) ? true : options.space_before_conditional;
    opt.unescape_strings = (options.unescape_strings === undefined) ? false : options.unescape_strings;
    opt.wrap_line_length = (options.wrap_line_length === undefined) ? 0 : parseInt(options.wrap_line_length, 10);
    opt.e4x = (options.e4x === undefined) ? false : options.e4x;

    if(options.indent_with_tabs){
        opt.indent_char = '\t';
        opt.indent_size = 1;
    }

    //----------------------------------
    indent_string = '';
    while (opt.indent_size > 0) {
        indent_string += opt.indent_char;
        opt.indent_size -= 1;
    }

    while (js_source_text && (js_source_text.charAt(0) === ' ' || js_source_text.charAt(0) === '\t')) {
        preindent_string += js_source_text.charAt(0);
        js_source_text = js_source_text.substring(1);
    }
    input = js_source_text;
    // cache the source's length.
    input_length = js_source_text.length;

    last_type = 'TK_START_BLOCK'; // last token type
    last_last_text = ''; // pre-last token text
    output_lines = [create_output_line()];
    output_space_before_token = false;
    whitespace_before_token = [];

    // Stack of parsing/formatting states, including MODE.
    // We tokenize, parse, and output in an almost purely a forward-only stream of token input
    // and formatted output.  This makes the beautifier less accurate than full parsers
    // but also far more tolerant of syntax errors.
    //
    // For example, the default mode is MODE.BlockStatement. If we see a '{' we push a new frame of type
    // MODE.BlockStatement on the the stack, even though it could be object literal.  If we later
    // encounter a ":", we'll switch to to MODE.ObjectLiteral.  If we then see a ";",
    // most full parsers would die, but the beautifier gracefully falls back to
    // MODE.BlockStatement and continues on.
    flag_store = [];
    set_mode(MODE.BlockStatement);

    parser_pos = 0;

    this.beautify = function() {
        /*jshint onevar:true */
        var t, i, keep_whitespace, sweet_code;

        while (true) {
            t = get_next_token();
            token_text = t[0];
            token_type = t[1];

            if (token_type === 'TK_EOF') {
                // Unwind any open statements
                while (flags.mode === MODE.Statement) {
                    restore_mode();
                }
                break;
            }

            keep_whitespace = opt.keep_array_indentation && is_array(flags.mode);
            input_wanted_newline = n_newlines > 0;

            if (keep_whitespace) {
                for (i = 0; i < n_newlines; i += 1) {
                    print_newline(i > 0);
                }
            } else {
                if (opt.max_preserve_newlines && n_newlines > opt.max_preserve_newlines) {
                    n_newlines = opt.max_preserve_newlines;
                }

                if (opt.preserve_newlines) {
                    if (n_newlines > 1) {
                        print_newline();
                        for (i = 1; i < n_newlines; i += 1) {
                            print_newline(true);
                        }
                    }
                }
            }

            handlers[token_type]();

            // The cleanest handling of inline comments is to treat them as though they aren't there.
            // Just continue formatting and the behavior should be logical.
            // Also ignore unknown tokens.  Again, this should result in better behavior.
            if (token_type !== 'TK_INLINE_COMMENT' && token_type !== 'TK_COMMENT' &&
                token_type !== 'TK_BLOCK_COMMENT' && token_type !== 'TK_UNKNOWN') {
                last_last_text = flags.last_text;
                last_type = token_type;
                flags.last_text = token_text;
            }
            flags.had_comment = (token_type === 'TK_INLINE_COMMENT' || token_type === 'TK_COMMENT'
                || token_type === 'TK_BLOCK_COMMENT');
        }


        sweet_code = output_lines[0].text.join('');
        for (var line_index = 1; line_index < output_lines.length; line_index++) {
            sweet_code += '\n' + output_lines[line_index].text.join('');
        }
        sweet_code = sweet_code.replace(/[\r\n ]+$/, '');
        return sweet_code;
    };

    function trim_output(eat_newlines) {
        eat_newlines = (eat_newlines === undefined) ? false : eat_newlines;

        if (output_lines.length) {
            trim_output_line(output_lines[output_lines.length - 1], eat_newlines);

            while (eat_newlines && output_lines.length > 1 &&
                output_lines[output_lines.length - 1].text.length === 0) {
                output_lines.pop();
                trim_output_line(output_lines[output_lines.length - 1], eat_newlines);
            }
        }
    }

    function trim_output_line(line) {
        while (line.text.length &&
            (line.text[line.text.length - 1] === ' ' ||
                line.text[line.text.length - 1] === indent_string ||
                line.text[line.text.length - 1] === preindent_string)) {
            line.text.pop();
        }
    }

    function trim(s) {
        return s.replace(/^\s+|\s+$/g, '');
    }

    // we could use just string.split, but
    // IE doesn't like returning empty strings

    function split_newlines(s) {
        //return s.split(/\x0d\x0a|\x0a/);

        s = s.replace(/\x0d/g, '');
        var out = [],
            idx = s.indexOf("\n");
        while (idx !== -1) {
            out.push(s.substring(0, idx));
            s = s.substring(idx + 1);
            idx = s.indexOf("\n");
        }
        if (s.length) {
            out.push(s);
        }
        return out;
    }

    function just_added_newline() {
        var line = output_lines[output_lines.length - 1];
        return line.text.length === 0;
    }

    function just_added_blankline() {
        if (just_added_newline()) {
            if (output_lines.length === 1) {
                return true; // start of the file and newline = blank
            }

            var line = output_lines[output_lines.length - 2];
            return line.text.length === 0;
        }
        return false;
    }

    function allow_wrap_or_preserved_newline(force_linewrap) {
        force_linewrap = (force_linewrap === undefined) ? false : force_linewrap;
        if (opt.wrap_line_length && !force_linewrap) {
            var line = output_lines[output_lines.length - 1];
            var proposed_line_length = 0;
            // never wrap the first token of a line.
            if (line.text.length > 0) {
                proposed_line_length = line.text.join('').length + token_text.length +
                    (output_space_before_token ? 1 : 0);
                if (proposed_line_length >= opt.wrap_line_length) {
                    force_linewrap = true;
                }
            }
        }
        if (((opt.preserve_newlines && input_wanted_newline) || force_linewrap) && !just_added_newline()) {
            print_newline(false, true);

        }
    }

    function print_newline(force_newline, preserve_statement_flags) {
        output_space_before_token = false;

        if (!preserve_statement_flags) {
            if (flags.last_text !== ';' && flags.last_text !== ',' && flags.last_text !== '=' && last_type !== 'TK_OPERATOR') {
                while (flags.mode === MODE.Statement && !flags.if_block && !flags.do_block) {
                    restore_mode();
                }
            }
        }

        if (output_lines.length === 1 && just_added_newline()) {
            return; // no newline on start of file
        }

        if (force_newline || !just_added_newline()) {
            flags.multiline_frame = true;
            output_lines.push(create_output_line());
        }
    }

    function print_token_line_indentation() {
        if (just_added_newline()) {
            var line = output_lines[output_lines.length - 1];
            if (opt.keep_array_indentation && is_array(flags.mode) && input_wanted_newline) {
                // prevent removing of this whitespace as redundant
                line.text.push('');
                for (var i = 0; i < whitespace_before_token.length; i += 1) {
                    line.text.push(whitespace_before_token[i]);
                }
            } else {
                if (preindent_string) {
                    line.text.push(preindent_string);
                }

                print_indent_string(flags.indentation_level);
            }
        }
    }

    function print_indent_string(level) {
        // Never indent your first output indent at the start of the file
        if (output_lines.length > 1) {
            var line = output_lines[output_lines.length - 1];

            flags.line_indent_level = level;
            for (var i = 0; i < level; i += 1) {
                line.text.push(indent_string);
            }
        }
    }

    function print_token_space_before() {
        var line = output_lines[output_lines.length - 1];
        if (output_space_before_token && line.text.length) {
            var last_output = line.text[line.text.length - 1];
            if (last_output !== ' ' && last_output !== indent_string) { // prevent occassional duplicate space
                line.text.push(' ');
            }
        }
    }

    function print_token(printable_token) {
        printable_token = printable_token || token_text;
        print_token_line_indentation();
        print_token_space_before();
        output_space_before_token = false;
        output_lines[output_lines.length - 1].text.push(printable_token);
    }

    function indent() {
        flags.indentation_level += 1;
    }

    function deindent() {
        if (flags.indentation_level > 0 &&
            ((!flags.parent) || flags.indentation_level > flags.parent.indentation_level))
            flags.indentation_level -= 1;
    }

    function remove_redundant_indentation(frame) {
        // This implementation is effective but has some issues:
        //     - less than great performance due to array splicing
        //     - can cause line wrap to happen too soon due to indent removal
        //           after wrap points are calculated
        // These issues are minor compared to ugly indentation.

        if (frame.multiline_frame) return;

        // remove one indent from each line inside this section
        var index = frame.start_line_index;
        var splice_index = 0;
        var line;

        while (index < output_lines.length) {
            line = output_lines[index];
            index++;

            // skip empty lines
            if (line.text.length === 0) {
                continue;
            }

            // skip the preindent string if present
            if (preindent_string && line.text[0] === preindent_string) {
                splice_index = 1;
            } else {
                splice_index = 0;
            }

            // remove one indent, if present
            if (line.text[splice_index] === indent_string) {
                line.text.splice(splice_index, 1);
            }
        }
    }

    function set_mode(mode) {
        if (flags) {
            flag_store.push(flags);
            previous_flags = flags;
        } else {
            previous_flags = create_flags(null, mode);
        }

        flags = create_flags(previous_flags, mode);
    }

    function is_array(mode) {
        return mode === MODE.ArrayLiteral;
    }

    function is_expression(mode) {
        return in_array(mode, [MODE.Expression, MODE.ForInitializer, MODE.Conditional]);
    }

    function restore_mode() {
        if (flag_store.length > 0) {
            previous_flags = flags;
            flags = flag_store.pop();
            if (previous_flags.mode === MODE.Statement) {
                remove_redundant_indentation(previous_flags);
            }
        }
    }

    function start_of_object_property() {
        return flags.parent.mode === MODE.ObjectLiteral && flags.mode === MODE.Statement && flags.last_text === ':' &&
            flags.ternary_depth === 0;
    }

    function start_of_statement() {
        if (
                (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['var', 'let', 'const']) && token_type === 'TK_WORD') ||
                (last_type === 'TK_RESERVED' && flags.last_text === 'do') ||
                (last_type === 'TK_RESERVED' && flags.last_text === 'return' && !input_wanted_newline) ||
                (last_type === 'TK_RESERVED' && flags.last_text === 'else' && !(token_type === 'TK_RESERVED' && token_text === 'if')) ||
                (last_type === 'TK_END_EXPR' && (previous_flags.mode === MODE.ForInitializer || previous_flags.mode === MODE.Conditional)) ||
                (last_type === 'TK_WORD' && flags.mode === MODE.BlockStatement
                    && !flags.in_case
                    && !(token_text === '--' || token_text === '++')
                    && token_type !== 'TK_WORD' && token_type !== 'TK_RESERVED') ||
                (flags.mode === MODE.ObjectLiteral && flags.last_text === ':' && flags.ternary_depth === 0)

            ) {

            set_mode(MODE.Statement);
            indent();

            if (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['var', 'let', 'const']) && token_type === 'TK_WORD') {
                flags.declaration_statement = true;
            }

            // Issue #276:
            // If starting a new statement with [if, for, while, do], push to a new line.
            // if (a) if (b) if(c) d(); else e(); else f();
            if (!start_of_object_property()) {
                allow_wrap_or_preserved_newline(
                    token_type === 'TK_RESERVED' && in_array(token_text, ['do', 'for', 'if', 'while']));
            }

            return true;
        }
        return false;
    }

    function all_lines_start_with(lines, c) {
        for (var i = 0; i < lines.length; i++) {
            var line = trim(lines[i]);
            if (line.charAt(0) !== c) {
                return false;
            }
        }
        return true;
    }

    function each_line_matches_indent(lines, indent) {
        var i = 0,
            len = lines.length,
            line;
        for (; i < len; i++) {
            line = lines[i];
            // allow empty lines to pass through
            if (line && line.indexOf(indent) !== 0) {
                return false;
            }
        }
        return true;
    }

    function is_special_word(word) {
        return in_array(word, ['case', 'return', 'do', 'if', 'throw', 'else']);
    }

    function in_array(what, arr) {
        for (var i = 0; i < arr.length; i += 1) {
            if (arr[i] === what) {
                return true;
            }
        }
        return false;
    }

    function unescape_string(s) {
        var esc = false,
            out = '',
            pos = 0,
            s_hex = '',
            escaped = 0,
            c;

        while (esc || pos < s.length) {

            c = s.charAt(pos);
            pos++;

            if (esc) {
                esc = false;
                if (c === 'x') {
                    // simple hex-escape \x24
                    s_hex = s.substr(pos, 2);
                    pos += 2;
                } else if (c === 'u') {
                    // unicode-escape, \u2134
                    s_hex = s.substr(pos, 4);
                    pos += 4;
                } else {
                    // some common escape, e.g \n
                    out += '\\' + c;
                    continue;
                }
                if (!s_hex.match(/^[0123456789abcdefABCDEF]+$/)) {
                    // some weird escaping, bail out,
                    // leaving whole string intact
                    return s;
                }

                escaped = parseInt(s_hex, 16);

                if (escaped >= 0x00 && escaped < 0x20) {
                    // leave 0x00...0x1f escaped
                    if (c === 'x') {
                        out += '\\x' + s_hex;
                    } else {
                        out += '\\u' + s_hex;
                    }
                    continue;
                } else if (escaped === 0x22 || escaped === 0x27 || escaped === 0x5c) {
                    // single-quote, apostrophe, backslash - escape these
                    out += '\\' + String.fromCharCode(escaped);
                } else if (c === 'x' && escaped > 0x7e && escaped <= 0xff) {
                    // we bail out on \x7f..\xff,
                    // leaving whole string escaped,
                    // as it's probably completely binary
                    return s;
                } else {
                    out += String.fromCharCode(escaped);
                }
            } else if (c === '\\') {
                esc = true;
            } else {
                out += c;
            }
        }
        return out;
    }

    function is_next(find) {
        var local_pos = parser_pos;
        var c = input.charAt(local_pos);
        while (in_array(c, whitespace) && c !== find) {
            local_pos++;
            if (local_pos >= input_length) {
                return false;
            }
            c = input.charAt(local_pos);
        }
        return c === find;
    }

    function get_next_token() {
        var i, resulting_string;

        n_newlines = 0;

        if (parser_pos >= input_length) {
            return ['', 'TK_EOF'];
        }

        input_wanted_newline = false;
        whitespace_before_token = [];

        var c = input.charAt(parser_pos);
        parser_pos += 1;

        while (in_array(c, whitespace)) {

            if (c === '\n') {
                n_newlines += 1;
                whitespace_before_token = [];
            } else if (n_newlines) {
                if (c === indent_string) {
                    whitespace_before_token.push(indent_string);
                } else if (c !== '\r') {
                    whitespace_before_token.push(' ');
                }
            }

            if (parser_pos >= input_length) {
                return ['', 'TK_EOF'];
            }

            c = input.charAt(parser_pos);
            parser_pos += 1;
        }

        // NOTE: because beautifier doesn't fully parse, it doesn't use acorn.isIdentifierStart.
        // It just treats all identifiers and numbers and such the same.
        if (acorn.isIdentifierChar(input.charCodeAt(parser_pos-1))) {
            if (parser_pos < input_length) {
                while (acorn.isIdentifierChar(input.charCodeAt(parser_pos))) {
                    c += input.charAt(parser_pos);
                    parser_pos += 1;
                    if (parser_pos === input_length) {
                        break;
                    }
                }
            }

            // small and surprisingly unugly hack for 1E-10 representation
            if (parser_pos !== input_length && c.match(/^[0-9]+[Ee]$/) && (input.charAt(parser_pos) === '-' || input.charAt(parser_pos) === '+')) {

                var sign = input.charAt(parser_pos);
                parser_pos += 1;

                var t = get_next_token();
                c += sign + t[0];
                return [c, 'TK_WORD'];
            }

            if (!(last_type === 'TK_DOT' ||
                    (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['set', 'get'])))
                && in_array(c, reserved_words)) {
                if (c === 'in') { // hack for 'in' operator
                    return [c, 'TK_OPERATOR'];
                }
                return [c, 'TK_RESERVED'];
            }
            return [c, 'TK_WORD'];
        }

        if (c === '(' || c === '[') {
            return [c, 'TK_START_EXPR'];
        }

        if (c === ')' || c === ']') {
            return [c, 'TK_END_EXPR'];
        }

        if (c === '{') {
            return [c, 'TK_START_BLOCK'];
        }

        if (c === '}') {
            return [c, 'TK_END_BLOCK'];
        }

        if (c === ';') {
            return [c, 'TK_SEMICOLON'];
        }

        if (c === '/') {
            var comment = '';
            // peek for comment /* ... */
            var inline_comment = true;
            if (input.charAt(parser_pos) === '*') {
                parser_pos += 1;
                if (parser_pos < input_length) {
                    while (parser_pos < input_length && !(input.charAt(parser_pos) === '*' && input.charAt(parser_pos + 1) && input.charAt(parser_pos + 1) === '/')) {
                        c = input.charAt(parser_pos);
                        comment += c;
                        if (c === "\n" || c === "\r") {
                            inline_comment = false;
                        }
                        parser_pos += 1;
                        if (parser_pos >= input_length) {
                            break;
                        }
                    }
                }
                parser_pos += 2;
                if (inline_comment && n_newlines === 0) {
                    return ['/*' + comment + '*/', 'TK_INLINE_COMMENT'];
                } else {
                    return ['/*' + comment + '*/', 'TK_BLOCK_COMMENT'];
                }
            }
            // peek for comment // ...
            if (input.charAt(parser_pos) === '/') {
                comment = c;
                while (input.charAt(parser_pos) !== '\r' && input.charAt(parser_pos) !== '\n') {
                    comment += input.charAt(parser_pos);
                    parser_pos += 1;
                    if (parser_pos >= input_length) {
                        break;
                    }
                }
                return [comment, 'TK_COMMENT'];
            }

        }


        if (c === '`' || c === "'" || c === '"' || // string
            (
                (c === '/') || // regexp
                (opt.e4x && c === "<" && input.slice(parser_pos - 1).match(/^<([-a-zA-Z:0-9_.]+|{[^{}]*}|!\[CDATA\[[\s\S]*?\]\])\s*([-a-zA-Z:0-9_.]+=('[^']*'|"[^"]*"|{[^{}]*})\s*)*\/?\s*>/)) // xml
            ) && ( // regex and xml can only appear in specific locations during parsing
                (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) ||
                (last_type === 'TK_END_EXPR' && in_array(previous_flags.mode, [MODE.Conditional, MODE.ForInitializer])) ||
                (in_array(last_type, ['TK_COMMENT', 'TK_START_EXPR', 'TK_START_BLOCK',
                    'TK_END_BLOCK', 'TK_OPERATOR', 'TK_EQUALS', 'TK_EOF', 'TK_SEMICOLON', 'TK_COMMA'
                ]))
            )) {

            var sep = c,
                esc = false,
                has_char_escapes = false;

            resulting_string = c;

            if (parser_pos < input_length) {
                if (sep === '/') {
                    //
                    // handle regexp
                    //
                    var in_char_class = false;
                    while (esc || in_char_class || input.charAt(parser_pos) !== sep) {
                        resulting_string += input.charAt(parser_pos);
                        if (!esc) {
                            esc = input.charAt(parser_pos) === '\\';
                            if (input.charAt(parser_pos) === '[') {
                                in_char_class = true;
                            } else if (input.charAt(parser_pos) === ']') {
                                in_char_class = false;
                            }
                        } else {
                            esc = false;
                        }
                        parser_pos += 1;
                        if (parser_pos >= input_length) {
                            // incomplete string/rexp when end-of-file reached.
                            // bail out with what had been received so far.
                            return [resulting_string, 'TK_STRING'];
                        }
                    }
                } else if (opt.e4x && sep === '<') {
                    //
                    // handle e4x xml literals
                    //
                    var xmlRegExp = /<(\/?)([-a-zA-Z:0-9_.]+|{[^{}]*}|!\[CDATA\[[\s\S]*?\]\])\s*([-a-zA-Z:0-9_.]+=('[^']*'|"[^"]*"|{[^{}]*})\s*)*(\/?)\s*>/g;
                    var xmlStr = input.slice(parser_pos - 1);
                    var match = xmlRegExp.exec(xmlStr);
                    if (match && match.index === 0) {
                        var rootTag = match[2];
                        var depth = 0;
                        while (match) {
                            var isEndTag = !! match[1];
                            var tagName = match[2];
                            var isSingletonTag = ( !! match[match.length - 1]) || (tagName.slice(0, 8) === "![CDATA[");
                            if (tagName === rootTag && !isSingletonTag) {
                                if (isEndTag) {
                                    --depth;
                                } else {
                                    ++depth;
                                }
                            }
                            if (depth <= 0) {
                                break;
                            }
                            match = xmlRegExp.exec(xmlStr);
                        }
                        var xmlLength = match ? match.index + match[0].length : xmlStr.length;
                        parser_pos += xmlLength - 1;
                        return [xmlStr.slice(0, xmlLength), "TK_STRING"];
                    }
                } else {
                    //
                    // handle string
                    //
                    while (esc || input.charAt(parser_pos) !== sep) {
                        resulting_string += input.charAt(parser_pos);
                        if (esc) {
                            if (input.charAt(parser_pos) === 'x' || input.charAt(parser_pos) === 'u') {
                                has_char_escapes = true;
                            }
                            esc = false;
                        } else {
                            esc = input.charAt(parser_pos) === '\\';
                        }
                        parser_pos += 1;
                        if (parser_pos >= input_length) {
                            // incomplete string/rexp when end-of-file reached.
                            // bail out with what had been received so far.
                            return [resulting_string, 'TK_STRING'];
                        }
                    }

                }
            }

            parser_pos += 1;
            resulting_string += sep;

            if (has_char_escapes && opt.unescape_strings) {
                resulting_string = unescape_string(resulting_string);
            }

            if (sep === '/') {
                // regexps may have modifiers /regexp/MOD , so fetch those, too
                while (parser_pos < input_length && in_array(input.charAt(parser_pos), wordchar)) {
                    resulting_string += input.charAt(parser_pos);
                    parser_pos += 1;
                }
            }
            return [resulting_string, 'TK_STRING'];
        }

        if (c === '#') {


            if (output_lines.length === 1 && output_lines[0].text.length === 0 &&
                input.charAt(parser_pos) === '!') {
                // shebang
                resulting_string = c;
                while (parser_pos < input_length && c !== '\n') {
                    c = input.charAt(parser_pos);
                    resulting_string += c;
                    parser_pos += 1;
                }
                return [trim(resulting_string) + '\n', 'TK_UNKNOWN'];
            }



            // Spidermonkey-specific sharp variables for circular references
            // https://developer.mozilla.org/En/Sharp_variables_in_JavaScript
            // http://dxr.mozilla.org/mozilla-central/source/js/src/jsscan.cpp around line 1935
            var sharp = '#';
            if (parser_pos < input_length && in_array(input.charAt(parser_pos), digits)) {
                do {
                    c = input.charAt(parser_pos);
                    sharp += c;
                    parser_pos += 1;
                } while (parser_pos < input_length && c !== '#' && c !== '=');
                if (c === '#') {
                    //
                } else if (input.charAt(parser_pos) === '[' && input.charAt(parser_pos + 1) === ']') {
                    sharp += '[]';
                    parser_pos += 2;
                } else if (input.charAt(parser_pos) === '{' && input.charAt(parser_pos + 1) === '}') {
                    sharp += '{}';
                    parser_pos += 2;
                }
                return [sharp, 'TK_WORD'];
            }
        }

        if (c === '<' && input.substring(parser_pos - 1, parser_pos + 3) === '<!--') {
            parser_pos += 3;
            c = '<!--';
            while (input.charAt(parser_pos) !== '\n' && parser_pos < input_length) {
                c += input.charAt(parser_pos);
                parser_pos++;
            }
            flags.in_html_comment = true;
            return [c, 'TK_COMMENT'];
        }

        if (c === '-' && flags.in_html_comment && input.substring(parser_pos - 1, parser_pos + 2) === '-->') {
            flags.in_html_comment = false;
            parser_pos += 2;
            return ['-->', 'TK_COMMENT'];
        }

        if (c === '.') {
            return [c, 'TK_DOT'];
        }

        if (in_array(c, punct)) {
            while (parser_pos < input_length && in_array(c + input.charAt(parser_pos), punct)) {
                c += input.charAt(parser_pos);
                parser_pos += 1;
                if (parser_pos >= input_length) {
                    break;
                }
            }

            if (c === ',') {
                return [c, 'TK_COMMA'];
            } else if (c === '=') {
                return [c, 'TK_EQUALS'];
            } else {
                return [c, 'TK_OPERATOR'];
            }
        }

        return [c, 'TK_UNKNOWN'];
    }

    function handle_start_expr() {
        if (start_of_statement()) {
            // The conditional starts the statement if appropriate.
        }

        var next_mode = MODE.Expression;
        if (token_text === '[') {

            if (last_type === 'TK_WORD' || flags.last_text === ')') {
                // this is array index specifier, break immediately
                // a[x], fn()[x]
                if (last_type === 'TK_RESERVED' && in_array(flags.last_text, line_starters)) {
                    output_space_before_token = true;
                }
                set_mode(next_mode);
                print_token();
                indent();
                if (opt.space_in_paren) {
                    output_space_before_token = true;
                }
                return;
            }

            next_mode = MODE.ArrayLiteral;
            if (is_array(flags.mode)) {
                if (flags.last_text === '[' ||
                    (flags.last_text === ',' && (last_last_text === ']' || last_last_text === '}'))) {
                    // ], [ goes to new line
                    // }, [ goes to new line
                    if (!opt.keep_array_indentation) {
                        print_newline();
                    }
                }
            }

        } else {
            if (last_type === 'TK_RESERVED' && flags.last_text === 'for') {
                next_mode = MODE.ForInitializer;
            } else if (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['if', 'while'])) {
                next_mode = MODE.Conditional;
            } else {
                // next_mode = MODE.Expression;
            }
        }

        if (flags.last_text === ';' || last_type === 'TK_START_BLOCK') {
            print_newline();
        } else if (last_type === 'TK_END_EXPR' || last_type === 'TK_START_EXPR' || last_type === 'TK_END_BLOCK' || flags.last_text === '.') {
            // TODO: Consider whether forcing this is required.  Review failing tests when removed.
            allow_wrap_or_preserved_newline(input_wanted_newline);
            // do nothing on (( and )( and ][ and ]( and .(
        } else if (!(last_type === 'TK_RESERVED' && token_text === '(') && last_type !== 'TK_WORD' && last_type !== 'TK_OPERATOR') {
            output_space_before_token = true;
        } else if ((last_type === 'TK_RESERVED' && (flags.last_word === 'function' || flags.last_word === 'typeof')) ||
            (flags.last_text === '*' && last_last_text === 'function')) {
            // function() vs function ()
            if (opt.jslint_happy) {
                output_space_before_token = true;
            }
        } else if (last_type === 'TK_RESERVED' && (in_array(flags.last_text, line_starters) || flags.last_text === 'catch')) {
            if (opt.space_before_conditional) {
                output_space_before_token = true;
            }
        }

        // Support of this kind of newline preservation.
        // a = (b &&
        //     (c || d));
        if (token_text === '(') {
            if (last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') {
                if (!start_of_object_property()) {
                    allow_wrap_or_preserved_newline();
                }
            }
        }

        set_mode(next_mode);
        print_token();
        if (opt.space_in_paren) {
            output_space_before_token = true;
        }

        // In all cases, if we newline while inside an expression it should be indented.
        indent();
    }

    function handle_end_expr() {
        // statements inside expressions are not valid syntax, but...
        // statements must all be closed when their container closes
        while (flags.mode === MODE.Statement) {
            restore_mode();
        }

        if (flags.multiline_frame) {
            allow_wrap_or_preserved_newline(token_text === ']' && is_array(flags.mode) && !opt.keep_array_indentation);
        }

        if (opt.space_in_paren) {
            if (last_type === 'TK_START_EXPR' && ! opt.space_in_empty_paren) {
                // () [] no inner space in empty parens like these, ever, ref #320
                trim_output();
                output_space_before_token = false;
            } else {
                output_space_before_token = true;
            }
        }
        if (token_text === ']' && opt.keep_array_indentation) {
            print_token();
            restore_mode();
        } else {
            restore_mode();
            print_token();
        }
        remove_redundant_indentation(previous_flags);

        // do {} while () // no statement required after
        if (flags.do_while && previous_flags.mode === MODE.Conditional) {
            previous_flags.mode = MODE.Expression;
            flags.do_block = false;
            flags.do_while = false;

        }
    }

    function handle_start_block() {
        set_mode(MODE.BlockStatement);

        var empty_braces = is_next('}');
        var empty_anonymous_function = empty_braces && flags.last_word === 'function' &&
            last_type === 'TK_END_EXPR';

        if (opt.brace_style === "expand") {
            if (last_type !== 'TK_OPERATOR' &&
                (empty_anonymous_function ||
                    last_type === 'TK_EQUALS' ||
                    (last_type === 'TK_RESERVED' && is_special_word(flags.last_text) && flags.last_text !== 'else'))) {
                output_space_before_token = true;
            } else {
                print_newline(false, true);
            }
        } else { // collapse
            if (last_type !== 'TK_OPERATOR' && last_type !== 'TK_START_EXPR') {
                if (last_type === 'TK_START_BLOCK') {
                    print_newline();
                } else {
                    output_space_before_token = true;
                }
            } else {
                // if TK_OPERATOR or TK_START_EXPR
                if (is_array(previous_flags.mode) && flags.last_text === ',') {
                    if (last_last_text === '}') {
                        // }, { in array context
                        output_space_before_token = true;
                    } else {
                        print_newline(); // [a, b, c, {
                    }
                }
            }
        }
        print_token();
        indent();
    }

    function handle_end_block() {
        // statements must all be closed when their container closes
        while (flags.mode === MODE.Statement) {
            restore_mode();
        }
        var empty_braces = last_type === 'TK_START_BLOCK';

        if (opt.brace_style === "expand") {
            if (!empty_braces) {
                print_newline();
            }
        } else {
            // skip {}
            if (!empty_braces) {
                if (is_array(flags.mode) && opt.keep_array_indentation) {
                    // we REALLY need a newline here, but newliner would skip that
                    opt.keep_array_indentation = false;
                    print_newline();
                    opt.keep_array_indentation = true;

                } else {
                    print_newline();
                }
            }
        }
        restore_mode();
        print_token();
    }

    function handle_word() {
        if (start_of_statement()) {
            // The conditional starts the statement if appropriate.
        } else if (input_wanted_newline && !is_expression(flags.mode) &&
            (last_type !== 'TK_OPERATOR' || (flags.last_text === '--' || flags.last_text === '++')) &&
            last_type !== 'TK_EQUALS' &&
            (opt.preserve_newlines || !(last_type === 'TK_RESERVED' && in_array(flags.last_text, ['var', 'let', 'const', 'set', 'get'])))) {

            print_newline();
        }

        if (flags.do_block && !flags.do_while) {
            if (token_type === 'TK_RESERVED' && token_text === 'while') {
                // do {} ## while ()
                output_space_before_token = true;
                print_token();
                output_space_before_token = true;
                flags.do_while = true;
                return;
            } else {
                // do {} should always have while as the next word.
                // if we don't see the expected while, recover
                print_newline();
                flags.do_block = false;
            }
        }

        // if may be followed by else, or not
        // Bare/inline ifs are tricky
        // Need to unwind the modes correctly: if (a) if (b) c(); else d(); else e();
        if (flags.if_block) {
            if (!flags.else_block && (token_type === 'TK_RESERVED' && token_text === 'else')) {
                flags.else_block = true;
            } else {
                while (flags.mode === MODE.Statement) {
                    restore_mode();
                }
                flags.if_block = false;
                flags.else_block = false;
            }
        }

        if (token_type === 'TK_RESERVED' && (token_text === 'case' || (token_text === 'default' && flags.in_case_statement))) {
            print_newline();
            if (flags.case_body || opt.jslint_happy) {
                // switch cases following one another
                deindent();
                flags.case_body = false;
            }
            print_token();
            flags.in_case = true;
            flags.in_case_statement = true;
            return;
        }

        if (token_type === 'TK_RESERVED' && token_text === 'function') {
            if (in_array(flags.last_text, ['}', ';']) || (just_added_newline() && ! in_array(flags.last_text, ['{', ':', '=', ',']))) {
                // make sure there is a nice clean space of at least one blank line
                // before a new function definition
                if ( ! just_added_blankline() && ! flags.had_comment) {
                    print_newline();
                    print_newline(true);
                }
            }
            if (last_type === 'TK_RESERVED' || last_type === 'TK_WORD') {
                if (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['get', 'set', 'new', 'return'])) {
                    output_space_before_token = true;
                } else {
                    print_newline();
                }
            } else if (last_type === 'TK_OPERATOR' || flags.last_text === '=') {
                // foo = function
                output_space_before_token = true;
            } else if (is_expression(flags.mode)) {
                // (function
            } else {
                print_newline();
            }
        }

        if (last_type === 'TK_COMMA' || last_type === 'TK_START_EXPR' || last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') {
            if (!start_of_object_property()) {
                allow_wrap_or_preserved_newline();
            }
        }

        if (token_type === 'TK_RESERVED' && token_text === 'function') {
            print_token();
            flags.last_word = token_text;
            return;
        }

        prefix = 'NONE';

        if (last_type === 'TK_END_BLOCK') {
            if (!(token_type === 'TK_RESERVED' && in_array(token_text, ['else', 'catch', 'finally']))) {
                prefix = 'NEWLINE';
            } else {
                if (opt.brace_style === "expand" || opt.brace_style === "end-expand") {
                    prefix = 'NEWLINE';
                } else {
                    prefix = 'SPACE';
                    output_space_before_token = true;
                }
            }
        } else if (last_type === 'TK_SEMICOLON' && flags.mode === MODE.BlockStatement) {
            // TODO: Should this be for STATEMENT as well?
            prefix = 'NEWLINE';
        } else if (last_type === 'TK_SEMICOLON' && is_expression(flags.mode)) {
            prefix = 'SPACE';
        } else if (last_type === 'TK_STRING') {
            prefix = 'NEWLINE';
        } else if (last_type === 'TK_RESERVED' || last_type === 'TK_WORD' ||
            (flags.last_text === '*' && last_last_text === 'function')) {
            prefix = 'SPACE';
        } else if (last_type === 'TK_START_BLOCK') {
            prefix = 'NEWLINE';
        } else if (last_type === 'TK_END_EXPR') {
            output_space_before_token = true;
            prefix = 'NEWLINE';
        }

        if (token_type === 'TK_RESERVED' && in_array(token_text, line_starters) && flags.last_text !== ')') {
            if (flags.last_text === 'else') {
                prefix = 'SPACE';
            } else {
                prefix = 'NEWLINE';
            }

        }

        if (token_type === 'TK_RESERVED' && in_array(token_text, ['else', 'catch', 'finally'])) {
            if (last_type !== 'TK_END_BLOCK' || opt.brace_style === "expand" || opt.brace_style === "end-expand") {
                print_newline();
            } else {
                trim_output(true);
                var line = output_lines[output_lines.length - 1];
                // If we trimmed and there's something other than a close block before us
                // put a newline back in.  Handles '} // comment' scenario.
                if (line.text[line.text.length - 1] !== '}') {
                    print_newline();
                }
                output_space_before_token = true;
            }
        } else if (prefix === 'NEWLINE') {
            if (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) {
                // no newline between 'return nnn'
                output_space_before_token = true;
            } else if (last_type !== 'TK_END_EXPR') {
                if ((last_type !== 'TK_START_EXPR' || !(token_type === 'TK_RESERVED' && in_array(token_text, ['var', 'let', 'const']))) && flags.last_text !== ':') {
                    // no need to force newline on 'var': for (var x = 0...)
                    if (token_type === 'TK_RESERVED' && token_text === 'if' && flags.last_word === 'else' && flags.last_text !== '{') {
                        // no newline for } else if {
                        output_space_before_token = true;
                    } else {
                        print_newline();
                    }
                }
            } else if (token_type === 'TK_RESERVED' && in_array(token_text, line_starters) && flags.last_text !== ')') {
                print_newline();
            }
        } else if (is_array(flags.mode) && flags.last_text === ',' && last_last_text === '}') {
            print_newline(); // }, in lists get a newline treatment
        } else if (prefix === 'SPACE') {
            output_space_before_token = true;
        }
        print_token();
        flags.last_word = token_text;

        if (token_type === 'TK_RESERVED' && token_text === 'do') {
            flags.do_block = true;
        }

        if (token_type === 'TK_RESERVED' && token_text === 'if') {
            flags.if_block = true;
        }
    }

    function handle_semicolon() {
        if (start_of_statement()) {
            // The conditional starts the statement if appropriate.
            // Semicolon can be the start (and end) of a statement
            output_space_before_token = false;
        }
        while (flags.mode === MODE.Statement && !flags.if_block && !flags.do_block) {
            restore_mode();
        }
        print_token();
        if (flags.mode === MODE.ObjectLiteral) {
            // if we're in OBJECT mode and see a semicolon, its invalid syntax
            // recover back to treating this as a BLOCK
            flags.mode = MODE.BlockStatement;
        }
    }

    function handle_string() {
        if (start_of_statement()) {
            // The conditional starts the statement if appropriate.
            // One difference - strings want at least a space before
            output_space_before_token = true;
        } else if (last_type === 'TK_RESERVED' || last_type === 'TK_WORD') {
            output_space_before_token = true;
        } else if (last_type === 'TK_COMMA' || last_type === 'TK_START_EXPR' || last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') {
            if (!start_of_object_property()) {
                allow_wrap_or_preserved_newline();
            }
        } else {
            print_newline();
        }
        print_token();
    }

    function handle_equals() {
        if (start_of_statement()) {
            // The conditional starts the statement if appropriate.
        }

        if (flags.declaration_statement) {
            // just got an '=' in a var-line, different formatting/line-breaking, etc will now be done
            flags.declaration_assignment = true;
        }
        output_space_before_token = true;
        print_token();
        output_space_before_token = true;
    }

    function handle_comma() {
        if (flags.declaration_statement) {
            if (is_expression(flags.parent.mode)) {
                // do not break on comma, for(var a = 1, b = 2)
                flags.declaration_assignment = false;
            }

            print_token();

            if (flags.declaration_assignment) {
                flags.declaration_assignment = false;
                print_newline(false, true);
            } else {
                output_space_before_token = true;
            }
            return;
        }

        print_token();
        if (flags.mode === MODE.ObjectLiteral ||
            (flags.mode === MODE.Statement && flags.parent.mode === MODE.ObjectLiteral)) {
            if (flags.mode === MODE.Statement) {
                restore_mode();
            }
            print_newline();
        } else {
            // EXPR or DO_BLOCK
            output_space_before_token = true;
        }

    }

    function handle_operator() {
        // Check if this is a BlockStatement that should be treated as a ObjectLiteral
        if (token_text === ':' && flags.mode === MODE.BlockStatement &&
                last_last_text === '{' &&
                (last_type === 'TK_WORD' || last_type === 'TK_RESERVED')){
            flags.mode = MODE.ObjectLiteral;
        }

        if (start_of_statement()) {
            // The conditional starts the statement if appropriate.
        }

        var space_before = true;
        var space_after = true;
        if (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) {
            // "return" had a special handling in TK_WORD. Now we need to return the favor
            output_space_before_token = true;
            print_token();
            return;
        }

        // hack for actionscript's import .*;
        if (token_text === '*' && last_type === 'TK_DOT' && !last_last_text.match(/^\d+$/)) {
            print_token();
            return;
        }

        if (token_text === ':' && flags.in_case) {
            flags.case_body = true;
            indent();
            print_token();
            print_newline();
            flags.in_case = false;
            return;
        }

        if (token_text === '::') {
            // no spaces around exotic namespacing syntax operator
            print_token();
            return;
        }

        // http://www.ecma-international.org/ecma-262/5.1/#sec-7.9.1
        // if there is a newline between -- or ++ and anything else we should preserve it.
        if (input_wanted_newline && (token_text === '--' || token_text === '++')) {
            print_newline();
        }

        // Allow line wrapping between operators
        if (last_type === 'TK_OPERATOR') {
            allow_wrap_or_preserved_newline();
        }

        if (in_array(token_text, ['--', '++', '!', '~']) || (in_array(token_text, ['-', '+']) && (in_array(last_type, ['TK_START_BLOCK', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR']) || in_array(flags.last_text, line_starters) || flags.last_text === ','))) {
            // unary operators (and binary +/- pretending to be unary) special cases

            space_before = false;
            space_after = false;

            if (flags.last_text === ';' && is_expression(flags.mode)) {
                // for (;; ++i)
                //        ^^^
                space_before = true;
            }

            if (last_type === 'TK_RESERVED') {
                space_before = true;
            }

            if ((flags.mode === MODE.BlockStatement || flags.mode === MODE.Statement) && (flags.last_text === '{' || flags.last_text === ';')) {
                // { foo; --i }
                // foo(); --bar;
                print_newline();
            }
        } else if (token_text === ':') {
            if (flags.ternary_depth === 0) {
                if (flags.mode === MODE.BlockStatement) {
                    flags.mode = MODE.ObjectLiteral;
                }
                space_before = false;
            } else {
                flags.ternary_depth -= 1;
            }
        } else if (token_text === '?') {
            flags.ternary_depth += 1;
        } else if (token_text === '*' && last_type === 'TK_RESERVED' && flags.last_text === 'function') {
            space_before = false;
            space_after = false;
        }
        output_space_before_token = output_space_before_token || space_before;
        print_token();
        output_space_before_token = space_after;
    }

    function handle_block_comment() {
        var lines = split_newlines(token_text);
        var j; // iterator for this case
        var javadoc = false;
        var starless = false;
        var lastIndent = whitespace_before_token.join('');
        var lastIndentLength = lastIndent.length;

        // block comment starts with a new line
        print_newline(false, true);
        if (lines.length > 1) {
            if (all_lines_start_with(lines.slice(1), '*')) {
                javadoc = true;
            }
            else if (each_line_matches_indent(lines.slice(1), lastIndent)) {
                starless = true;
            }
        }

        // first line always indented
        print_token(lines[0]);
        for (j = 1; j < lines.length; j++) {
            print_newline(false, true);
            if (javadoc) {
                // javadoc: reformat and re-indent
                print_token(' ' + trim(lines[j]));
            } else if (starless && lines[j].length > lastIndentLength) {
                // starless: re-indent non-empty content, avoiding trim
                print_token(lines[j].substring(lastIndentLength));
            } else {
                // normal comments output raw
                output_lines[output_lines.length - 1].text.push(lines[j]);
            }
        }

        // for comments of more than one line, make sure there's a new line after
        print_newline(false, true);
    }

    function handle_inline_comment() {
        output_space_before_token = true;
        print_token();
        output_space_before_token = true;
    }

    function handle_comment() {
        if (input_wanted_newline) {
            print_newline(false, true);
        } else {
            trim_output(true);
        }

        output_space_before_token = true;
        print_token();
        print_newline(false, true);
    }

    function handle_dot() {
        if (start_of_statement()) {
            // The conditional starts the statement if appropriate.
        }

        if (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) {
            output_space_before_token = true;
        } else {
            // allow preserved newlines before dots in general
            // force newlines on dots after close paren when break_chained - for bar().baz()
            allow_wrap_or_preserved_newline(flags.last_text === ')' && opt.break_chained_methods);
        }

        print_token();
    }

    function handle_unknown() {
        print_token();

        if (token_text[token_text.length - 1] === '\n') {
            print_newline();
        }
    }
}
PK
!<w���(�(�Hchrome/devtools/modules/devtools/shared/jsbeautify/src/beautify-tests.js/*global js_beautify: true */

function run_beautifier_tests(test_obj, Urlencoded, js_beautify, html_beautify, css_beautify)
{

    var opts = {
        indent_size: 4,
        indent_char: ' ',
        preserve_newlines: true,
        jslint_happy: false,
        keep_array_indentation: false,
        brace_style: 'collapse',
        space_before_conditional: true,
        break_chained_methods: false,
        selector_separator: '\n',
        end_with_newline: true
    };

    function test_js_beautifier(input)
    {
        return js_beautify(input, opts);
    }

    function test_html_beautifier(input)
    {
        return html_beautify(input, opts);
    }

    function test_css_beautifier(input)
    {
        return css_beautify(input, opts);
    }

    var sanitytest;

    // test the input on beautifier with the current flag settings
    // does not check the indentation / surroundings as bt() does
    function test_fragment(input, expected)
    {
        expected = expected || input;
        sanitytest.expect(input, expected);
        // if the expected is different from input, run it again
        // expected output should be unchanged when run twice.
        if (expected != input) {
            sanitytest.expect(expected, expected);
        }
    }



    // test the input on beautifier with the current flag settings
    // test both the input as well as { input } wrapping
    function bt(input, expectation)
    {
        var wrapped_input, wrapped_expectation;

        expectation = expectation || input;
        sanitytest.test_function(test_js_beautifier, 'js_beautify');
        test_fragment(input, expectation);

        // test also the returned indentation
        // e.g if input = "asdf();"
        // then test that this remains properly formatted as well:
        // {
        //     asdf();
        //     indent;
        // }

        if (opts.indent_size === 4 && input) {
            wrapped_input = '{\n' + input.replace(/^(.+)$/mg, '    $1') + '\n    foo = bar;\n}';
            wrapped_expectation = '{\n' + expectation.replace(/^(.+)$/mg, '    $1') + '\n    foo = bar;\n}';
            test_fragment(wrapped_input, wrapped_expectation);
        }

    }

    // test html
    function bth(input, expectation)
    {
        var wrapped_input, wrapped_expectation, field_input, field_expectation;

        expectation = expectation || input;
        sanitytest.test_function(test_html_beautifier, 'html_beautify');
        test_fragment(input, expectation);

        if (opts.indent_size === 4 && input) {
            wrapped_input = '<div>\n' + input.replace(/^(.+)$/mg, '    $1') + '\n    <span>inline</span>\n</div>';
            wrapped_expectation = '<div>\n' + expectation.replace(/^(.+)$/mg, '    $1') + '\n    <span>inline</span>\n</div>';
            test_fragment(wrapped_input, wrapped_expectation);
        }

        // Test that handlebars non-block {{}} tags act as content and do not
        // get any spacing or line breaks.
        if (input.includes('content')) {
            // Just {{field}}
            field_input = input.replace(/content/g, '{{field}}');
            field_expectation = expectation.replace(/content/g, '{{field}}');
            test_fragment(field_input, field_expectation);

            // handlebars comment
            field_input = input.replace(/content/g, '{{! comment}}');
            field_expectation = expectation.replace(/content/g, '{{! comment}}');
            test_fragment(field_input, field_expectation);

            // mixed {{field}} and content
            field_input = input.replace(/content/g, 'pre{{field1}} {{field2}} {{field3}}post');
            field_expectation = expectation.replace(/content/g, 'pre{{field1}} {{field2}} {{field3}}post');
            test_fragment(field_input, field_expectation);
        }
    }

    // test css
    function btc(input, expectation)
    {
        var wrapped_input, wrapped_expectation;

        expectation = expectation || input;
        sanitytest.test_function(test_css_beautifier, 'css_beautify');
        test_fragment(input, expectation);
    }

    // test the input on beautifier with the current flag settings,
    // but dont't
    function bt_braces(input, expectation)
    {
        var braces_ex = opts.brace_style;
        opts.brace_style = 'expand';
        bt(input, expectation);
        opts.brace_style = braces_ex;
    }

    function beautifier_tests()
    {
        sanitytest = test_obj;

        opts.indent_size       = 4;
        opts.indent_char       = ' ';
        opts.preserve_newlines = true;
        opts.jslint_happy      = false;
        opts.keep_array_indentation = false;
        opts.brace_style       = "collapse";


        // unicode support
        bt('var ' + String.fromCharCode(3232) + '_' + String.fromCharCode(3232) + ' = "hi";');
        bt('var ' + String.fromCharCode(228) + 'x = {\n    ' + String.fromCharCode(228) + 'rgerlich: true\n};');

        bt('');
        bt('return .5');
        test_fragment('   return .5');
        test_fragment('   return .5;\n   a();');
        bt('a        =          1', 'a = 1');
        bt('a=1', 'a = 1');
        bt("a();\n\nb();", "a();\n\nb();");
        bt('var a = 1 var b = 2', "var a = 1\nvar b = 2");
        bt('var a=1, b=c[d], e=6;', 'var a = 1,\n    b = c[d],\n    e = 6;');
        bt('var a,\n    b,\n    c;');
        bt('let a = 1 let b = 2', "let a = 1\nlet b = 2");
        bt('let a=1, b=c[d], e=6;', 'let a = 1,\n    b = c[d],\n    e = 6;');
        bt('let a,\n    b,\n    c;');
        bt('const a = 1 const b = 2', "const a = 1\nconst b = 2");
        bt('const a=1, b=c[d], e=6;', 'const a = 1,\n    b = c[d],\n    e = 6;');
        bt('const a,\n    b,\n    c;');
        bt('a = " 12345 "');
        bt("a = ' 12345 '");
        bt('if (a == 1) b = 2;', "if (a == 1) b = 2;");
        bt('if(1){2}else{3}', "if (1) {\n    2\n} else {\n    3\n}");
        bt('if(1||2);', 'if (1 || 2);');
        bt('(a==1)||(b==2)', '(a == 1) || (b == 2)');
        bt('var a = 1 if (2) 3;', "var a = 1\nif (2) 3;");
        bt('a = a + 1');
        bt('a = a == 1');
        bt('/12345[^678]*9+/.match(a)');
        bt('a /= 5');
        bt('a = 0.5 * 3');
        bt('a *= 10.55');
        bt('a < .5');
        bt('a <= .5');
        bt('a<.5', 'a < .5');
        bt('a<=.5', 'a <= .5');
        bt('a = 0xff;');
        bt('a=0xff+4', 'a = 0xff + 4');
        bt('a = [1, 2, 3, 4]');
        bt('F*(g/=f)*g+b', 'F * (g /= f) * g + b');
        bt('a.b({c:d})', 'a.b({\n    c: d\n})');
        bt('a.b\n(\n{\nc:\nd\n}\n)', 'a.b({\n    c: d\n})');
        bt('a.b({c:"d"})', 'a.b({\n    c: "d"\n})');
        bt('a.b\n(\n{\nc:\n"d"\n}\n)', 'a.b({\n    c: "d"\n})');
        bt('a=!b', 'a = !b');
        bt('a=!!b', 'a = !!b');
        bt('a?b:c', 'a ? b : c');
        bt('a?1:2', 'a ? 1 : 2');
        bt('a?(b):c', 'a ? (b) : c');
        bt('x={a:1,b:w=="foo"?x:y,c:z}', 'x = {\n    a: 1,\n    b: w == "foo" ? x : y,\n    c: z\n}');
        bt('x=a?b?c?d:e:f:g;', 'x = a ? b ? c ? d : e : f : g;');
        bt('x=a?b?c?d:{e1:1,e2:2}:f:g;', 'x = a ? b ? c ? d : {\n    e1: 1,\n    e2: 2\n} : f : g;');
        bt('function void(void) {}');
        bt('if(!a)foo();', 'if (!a) foo();');
        bt('a=~a', 'a = ~a');
        bt('a;/*comment*/b;', "a; /*comment*/\nb;");
        bt('a;/* comment */b;', "a; /* comment */\nb;");
        test_fragment('a;/*\ncomment\n*/b;', "a;\n/*\ncomment\n*/\nb;"); // simple comments don't get touched at all
        bt('a;/**\n* javadoc\n*/b;', "a;\n/**\n * javadoc\n */\nb;");
        test_fragment('a;/**\n\nno javadoc\n*/b;', "a;\n/**\n\nno javadoc\n*/\nb;");
        bt('a;/*\n* javadoc\n*/b;', "a;\n/*\n * javadoc\n */\nb;"); // comment blocks detected and reindented even w/o javadoc starter
        bt('if(a)break;', "if (a) break;");
        bt('if(a){break}', "if (a) {\n    break\n}");
        bt('if((a))foo();', 'if ((a)) foo();');
        bt('for(var i=0;;) a', 'for (var i = 0;;) a');
        bt('for(var i=0;;)\na', 'for (var i = 0;;)\n    a');
        bt('a++;', 'a++;');
        bt('for(;;i++)a()', 'for (;; i++) a()');
        bt('for(;;i++)\na()', 'for (;; i++)\n    a()');
        bt('for(;;++i)a', 'for (;; ++i) a');
        bt('return(1)', 'return (1)');
        bt('try{a();}catch(b){c();}finally{d();}', "try {\n    a();\n} catch (b) {\n    c();\n} finally {\n    d();\n}");
        bt('(xx)()'); // magic function call
        bt('a[1]()'); // another magic function call
        bt('if(a){b();}else if(c) foo();', "if (a) {\n    b();\n} else if (c) foo();");
        bt('switch(x) {case 0: case 1: a(); break; default: break}', "switch (x) {\n    case 0:\n    case 1:\n        a();\n        break;\n    default:\n        break\n}");
        bt('switch(x){case -1:break;case !y:break;}', 'switch (x) {\n    case -1:\n        break;\n    case !y:\n        break;\n}');
        bt('a !== b');
        bt('if (a) b(); else c();', "if (a) b();\nelse c();");
        bt("// comment\n(function something() {})"); // typical greasemonkey start
        bt("{\n\n    x();\n\n}"); // was: duplicating newlines
        bt('if (a in b) foo();');
        bt('var a, b;');
        //  bt('var a, b');
        bt('{a:1, b:2}', "{\n    a: 1,\n    b: 2\n}");
        bt('a={1:[-1],2:[+1]}', 'a = {\n    1: [-1],\n    2: [+1]\n}');
        bt('var l = {\'a\':\'1\', \'b\':\'2\'}', "var l = {\n    'a': '1',\n    'b': '2'\n}");
        bt('if (template.user[n] in bk) foo();');
        bt('{{}/z/}', "{\n    {}\n    /z/\n}");
        bt('return 45', "return 45");
        bt('return this.prevObject ||\n\n    this.constructor(null);');
        bt('If[1]', "If[1]");
        bt('Then[1]', "Then[1]");
        bt('a = 1e10', "a = 1e10");
        bt('a = 1.3e10', "a = 1.3e10");
        bt('a = 1.3e-10', "a = 1.3e-10");
        bt('a = -1.3e-10', "a = -1.3e-10");
        bt('a = 1e-10', "a = 1e-10");
        bt('a = e - 10', "a = e - 10");
        bt('a = 11-10', "a = 11 - 10");
        bt("a = 1;// comment", "a = 1; // comment");
        bt("a = 1; // comment", "a = 1; // comment");
        bt("a = 1;\n // comment", "a = 1;\n// comment");
        bt('a = [-1, -1, -1]');

        // The exact formatting these should have is open for discussion, but they are at least reasonable
        bt('a = [ // comment\n    -1, -1, -1\n]');
        bt('var a = [ // comment\n    -1, -1, -1\n]');
        bt('a = [ // comment\n    -1, // comment\n    -1, -1\n]');
        bt('var a = [ // comment\n    -1, // comment\n    -1, -1\n]');

        bt('o = [{a:b},{c:d}]', 'o = [{\n    a: b\n}, {\n    c: d\n}]');

        bt("if (a) {\n    do();\n}"); // was: extra space appended

        bt("if (a) {\n// comment\n}else{\n// comment\n}", "if (a) {\n    // comment\n} else {\n    // comment\n}"); // if/else statement with empty body
        bt("if (a) {\n// comment\n// comment\n}", "if (a) {\n    // comment\n    // comment\n}"); // multiple comments indentation
        bt("if (a) b() else c();", "if (a) b()\nelse c();");
        bt("if (a) b() else if c() d();", "if (a) b()\nelse if c() d();");

        bt("{}");
        bt("{\n\n}");
        bt("do { a(); } while ( 1 );", "do {\n    a();\n} while (1);");
        bt("do {} while (1);");
        bt("do {\n} while (1);", "do {} while (1);");
        bt("do {\n\n} while (1);");
        bt("var a = x(a, b, c)");
        bt("delete x if (a) b();", "delete x\nif (a) b();");
        bt("delete x[x] if (a) b();", "delete x[x]\nif (a) b();");
        bt("for(var a=1,b=2)d", "for (var a = 1, b = 2) d");
        bt("for(var a=1,b=2,c=3) d", "for (var a = 1, b = 2, c = 3) d");
        bt("for(var a=1,b=2,c=3;d<3;d++)\ne", "for (var a = 1, b = 2, c = 3; d < 3; d++)\n    e");
        bt("function x(){(a||b).c()}", "function x() {\n    (a || b).c()\n}");
        bt("function x(){return - 1}", "function x() {\n    return -1\n}");
        bt("function x(){return ! a}", "function x() {\n    return !a\n}");
        bt("x => x", "x => x");
        bt("(x) => x", "(x) => x");
        bt("x => { x }", "x => {\n    x\n}");
        bt("(x) => { x }", "(x) => {\n    x\n}");

        // a common snippet in jQuery plugins
        bt("settings = $.extend({},defaults,settings);", "settings = $.extend({}, defaults, settings);");

        // reserved words used as property names
        bt("$http().then().finally().default()", "$http().then().finally().default()");
        bt("$http()\n.then()\n.finally()\n.default()", "$http()\n    .then()\n    .finally()\n    .default()");
        bt("$http().when.in.new.catch().throw()", "$http().when.in.new.catch().throw()");
        bt("$http()\n.when\n.in\n.new\n.catch()\n.throw()", "$http()\n    .when\n    .in\n    .new\n    .catch()\n    .throw()");

        bt('{xxx;}()', '{\n    xxx;\n}()');

        bt("a = 'a'\nb = 'b'");
        bt("a = /reg/exp");
        bt("a = /reg/");
        bt('/abc/.test()');
        bt('/abc/i.test()');
        bt("{/abc/i.test()}", "{\n    /abc/i.test()\n}");
        bt('var x=(a)/a;', 'var x = (a) / a;');

        bt('x != -1', 'x != -1');

        bt('for (; s-->0;)t', 'for (; s-- > 0;) t');
        bt('for (; s++>0;)u', 'for (; s++ > 0;) u');
        bt('a = s++>s--;', 'a = s++ > s--;');
        bt('a = s++>--s;', 'a = s++ > --s;');

        bt('{x=#1=[]}', '{\n    x = #1=[]\n}');
        bt('{a:#1={}}', '{\n    a: #1={}\n}');
        bt('{a:#1#}', '{\n    a: #1#\n}');

        test_fragment('"incomplete-string');
        test_fragment("'incomplete-string");
        test_fragment('/incomplete-regex');
        test_fragment('`incomplete-template-string');

        test_fragment('{a:1},{a:2}', '{\n    a: 1\n}, {\n    a: 2\n}');
        test_fragment('var ary=[{a:1}, {a:2}];', 'var ary = [{\n    a: 1\n}, {\n    a: 2\n}];');

        test_fragment('{a:#1', '{\n    a: #1'); // incomplete
        test_fragment('{a:#', '{\n    a: #'); // incomplete

        test_fragment('}}}', '}\n}\n}'); // incomplete

        test_fragment('<!--\nvoid();\n// -->', '<!--\nvoid();\n// -->');

        test_fragment('a=/regexp', 'a = /regexp'); // incomplete regexp

        bt('{a:#1=[],b:#1#,c:#999999#}', '{\n    a: #1=[],\n    b: #1#,\n    c: #999999#\n}');

        bt("a = 1e+2");
        bt("a = 1e-2");
        bt("do{x()}while(a>1)", "do {\n    x()\n} while (a > 1)");

        bt("x(); /reg/exp.match(something)", "x();\n/reg/exp.match(something)");

        test_fragment("something();(", "something();\n(");
        test_fragment("#!she/bangs, she bangs\nf=1", "#!she/bangs, she bangs\n\nf = 1");
        test_fragment("#!she/bangs, she bangs\n\nf=1", "#!she/bangs, she bangs\n\nf = 1");
        test_fragment("#!she/bangs, she bangs\n\n/* comment */", "#!she/bangs, she bangs\n\n/* comment */");
        test_fragment("#!she/bangs, she bangs\n\n\n/* comment */", "#!she/bangs, she bangs\n\n\n/* comment */");
        test_fragment("#", "#");
        test_fragment("#!", "#!");

        bt("function namespace::something()");

        test_fragment("<!--\nsomething();\n-->", "<!--\nsomething();\n-->");
        test_fragment("<!--\nif(i<0){bla();}\n-->", "<!--\nif (i < 0) {\n    bla();\n}\n-->");

        bt('{foo();--bar;}', '{\n    foo();\n    --bar;\n}');
        bt('{foo();++bar;}', '{\n    foo();\n    ++bar;\n}');
        bt('{--bar;}', '{\n    --bar;\n}');
        bt('{++bar;}', '{\n    ++bar;\n}');

        // Handling of newlines around unary ++ and -- operators
        bt('{foo\n++bar;}', '{\n    foo\n    ++bar;\n}');
        bt('{foo++\nbar;}', '{\n    foo++\n    bar;\n}');

        // This is invalid, but harder to guard against. Issue #203.
        bt('{foo\n++\nbar;}', '{\n    foo\n    ++\n    bar;\n}');


        // regexps
        bt('a(/abc\\/\\/def/);b()', "a(/abc\\/\\/def/);\nb()");
        bt('a(/a[b\\[\\]c]d/);b()', "a(/a[b\\[\\]c]d/);\nb()");
        test_fragment('a(/a[b\\[', "a(/a[b\\["); // incomplete char class
        // allow unescaped / in char classes
        bt('a(/[a/b]/);b()', "a(/[a/b]/);\nb()");

        bt('function foo() {\n    return [\n        "one",\n        "two"\n    ];\n}');
        bt('a=[[1,2],[4,5],[7,8]]', "a = [\n    [1, 2],\n    [4, 5],\n    [7, 8]\n]");
        bt('a=[[1,2],[4,5],function(){},[7,8]]',
            "a = [\n    [1, 2],\n    [4, 5],\n    function() {},\n    [7, 8]\n]");
        bt('a=[[1,2],[4,5],function(){},function(){},[7,8]]',
            "a = [\n    [1, 2],\n    [4, 5],\n    function() {},\n    function() {},\n    [7, 8]\n]");
        bt('a=[[1,2],[4,5],function(){},[7,8]]',
            "a = [\n    [1, 2],\n    [4, 5],\n    function() {},\n    [7, 8]\n]");
        bt('a=[b,c,function(){},function(){},d]',
            "a = [b, c,\n    function() {},\n    function() {},\n    d\n]");
        bt('a=[a[1],b[4],c[d[7]]]', "a = [a[1], b[4], c[d[7]]]");
        bt('[1,2,[3,4,[5,6],7],8]', "[1, 2, [3, 4, [5, 6], 7], 8]");

        bt('[[["1","2"],["3","4"]],[["5","6","7"],["8","9","0"]],[["1","2","3"],["4","5","6","7"],["8","9","0"]]]',
            '[\n    [\n        ["1", "2"],\n        ["3", "4"]\n    ],\n    [\n        ["5", "6", "7"],\n        ["8", "9", "0"]\n    ],\n    [\n        ["1", "2", "3"],\n        ["4", "5", "6", "7"],\n        ["8", "9", "0"]\n    ]\n]');

        bt('{[x()[0]];indent;}', '{\n    [x()[0]];\n    indent;\n}');

        bt('return ++i', 'return ++i');
        bt('return !!x', 'return !!x');
        bt('return !x', 'return !x');
        bt('return [1,2]', 'return [1, 2]');
        bt('return;', 'return;');
        bt('return\nfunc', 'return\nfunc');
        bt('catch(e)', 'catch (e)');
        bt('yield [1, 2]');

        bt('var a=1,b={foo:2,bar:3},{baz:4,wham:5},c=4;',
            'var a = 1,\n    b = {\n        foo: 2,\n        bar: 3\n    },\n    {\n        baz: 4,\n        wham: 5\n    }, c = 4;');
        bt('var a=1,b={foo:2,bar:3},{baz:4,wham:5},\nc=4;',
            'var a = 1,\n    b = {\n        foo: 2,\n        bar: 3\n    },\n    {\n        baz: 4,\n        wham: 5\n    },\n    c = 4;');


        // inline comment
        bt('function x(/*int*/ start, /*string*/ foo)', 'function x( /*int*/ start, /*string*/ foo)');

        // javadoc comment
        bt('/**\n* foo\n*/', '/**\n * foo\n */');
        bt('{\n/**\n* foo\n*/\n}', '{\n    /**\n     * foo\n     */\n}');

        // starless block comment
        bt('/**\nfoo\n*/');
        bt('/**\nfoo\n**/');
        bt('/**\nfoo\nbar\n**/');
        bt('/**\nfoo\n\nbar\n**/');
        bt('/**\nfoo\n    bar\n**/');
        bt('{\n/**\nfoo\n*/\n}', '{\n    /**\n    foo\n    */\n}');
        bt('{\n/**\nfoo\n**/\n}', '{\n    /**\n    foo\n    **/\n}');
        bt('{\n/**\nfoo\nbar\n**/\n}', '{\n    /**\n    foo\n    bar\n    **/\n}');
        bt('{\n/**\nfoo\n\nbar\n**/\n}', '{\n    /**\n    foo\n\n    bar\n    **/\n}');
        bt('{\n/**\nfoo\n    bar\n**/\n}', '{\n    /**\n    foo\n        bar\n    **/\n}');
        bt('{\n    /**\n    foo\nbar\n    **/\n}');

        bt('var a,b,c=1,d,e,f=2;', 'var a, b, c = 1,\n    d, e, f = 2;');
        bt('var a,b,c=[],d,e,f=2;', 'var a, b, c = [],\n    d, e, f = 2;');
        bt('function() {\n    var a, b, c, d, e = [],\n        f;\n}');

        bt('do/regexp/;\nwhile(1);', 'do /regexp/;\nwhile (1);'); // hmmm

        bt('var a = a,\na;\nb = {\nb\n}', 'var a = a,\n    a;\nb = {\n    b\n}');

        bt('var a = a,\n    /* c */\n    b;');
        bt('var a = a,\n    // c\n    b;');

        bt('foo.("bar");'); // weird element referencing


        bt('if (a) a()\nelse b()\nnewline()');
        bt('if (a) a()\nnewline()');
        bt('a=typeof(x)', 'a = typeof(x)');

        bt('var a = function() {\n        return null;\n    },\n    b = false;');

        bt('var a = function() {\n    func1()\n}');
        bt('var a = function() {\n    func1()\n}\nvar b = function() {\n    func2()\n}');

        // code with and without semicolons
        bt( 'var whatever = require("whatever");\nfunction() {\n    a = 6;\n}',
            'var whatever = require("whatever");\n\nfunction() {\n    a = 6;\n}');
        bt( 'var whatever = require("whatever")\nfunction() {\n    a = 6\n}',
            'var whatever = require("whatever")\n\nfunction() {\n    a = 6\n}');


        opts.jslint_happy = true;

        bt('a=typeof(x)', 'a = typeof (x)');
        bt('x();\n\nfunction(){}', 'x();\n\nfunction () {}');
        bt('function () {\n    var a, b, c, d, e = [],\n        f;\n}');
        bt('switch(x) {case 0: case 1: a(); break; default: break}',
            "switch (x) {\ncase 0:\ncase 1:\n    a();\n    break;\ndefault:\n    break\n}");
        bt('switch(x){case -1:break;case !y:break;}',
            'switch (x) {\ncase -1:\n    break;\ncase !y:\n    break;\n}');
        test_fragment("// comment 1\n(function()", "// comment 1\n(function ()"); // typical greasemonkey start
        bt('var o1=$.extend(a);function(){alert(x);}', 'var o1 = $.extend(a);\n\nfunction () {\n    alert(x);\n}');
        bt('function* () {\n    yield 1;\n}');

        opts.jslint_happy = false;

        bt('switch(x) {case 0: case 1: a(); break; default: break}',
            "switch (x) {\n    case 0:\n    case 1:\n        a();\n        break;\n    default:\n        break\n}");
        bt('switch(x){case -1:break;case !y:break;}',
            'switch (x) {\n    case -1:\n        break;\n    case !y:\n        break;\n}');
        test_fragment("// comment 2\n(function()", "// comment 2\n(function()"); // typical greasemonkey start
        bt("var a2, b2, c2, d2 = 0, c = function() {}, d = '';", "var a2, b2, c2, d2 = 0,\n    c = function() {},\n    d = '';");
        bt("var a2, b2, c2, d2 = 0, c = function() {},\nd = '';", "var a2, b2, c2, d2 = 0,\n    c = function() {},\n    d = '';");
        bt('var o2=$.extend(a);function(){alert(x);}', 'var o2 = $.extend(a);\n\nfunction() {\n    alert(x);\n}');
        bt('function*() {\n    yield 1;\n}');

        bt('function* x() {\n    yield 1;\n}');

        bt('{"x":[{"a":1,"b":3},7,8,8,8,8,{"b":99},{"a":11}]}', '{\n    "x": [{\n            "a": 1,\n            "b": 3\n        },\n        7, 8, 8, 8, 8, {\n            "b": 99\n        }, {\n            "a": 11\n        }\n    ]\n}');

        bt('{"1":{"1a":"1b"},"2"}', '{\n    "1": {\n        "1a": "1b"\n    },\n    "2"\n}');
        bt('{a:{a:b},c}', '{\n    a: {\n        a: b\n    },\n    c\n}');

        bt('{[y[a]];keep_indent;}', '{\n    [y[a]];\n    keep_indent;\n}');

        bt('if (x) {y} else { if (x) {y}}', 'if (x) {\n    y\n} else {\n    if (x) {\n        y\n    }\n}');

        bt('if (foo) one()\ntwo()\nthree()');
        bt('if (1 + foo() && bar(baz()) / 2) one()\ntwo()\nthree()');
        bt('if (1 + foo() && bar(baz()) / 2) one();\ntwo();\nthree();');

        opts.indent_size = 1;
        opts.indent_char = ' ';
        bt('{ one_char() }', "{\n one_char()\n}");

        bt('var a,b=1,c=2', 'var a, b = 1,\n c = 2');

        opts.indent_size = 4;
        opts.indent_char = ' ';
        bt('{ one_char() }', "{\n    one_char()\n}");

        opts.indent_size = 1;
        opts.indent_char = "\t";
        bt('{ one_char() }', "{\n\tone_char()\n}");
        bt('x = a ? b : c; x;', 'x = a ? b : c;\nx;');

        //set to something else than it should change to, but with tabs on, should override
        opts.indent_size = 5;
        opts.indent_char = ' ';
        opts.indent_with_tabs = true;

        bt('{ one_char() }', "{\n\tone_char()\n}");
        bt('x = a ? b : c; x;', 'x = a ? b : c;\nx;');

        opts.indent_size = 4;
        opts.indent_char = ' ';
        opts.indent_with_tabs = false;

        opts.preserve_newlines = false;

        bt('var\na=dont_preserve_newlines;', 'var a = dont_preserve_newlines;');

        // make sure the blank line between function definitions stays
        // even when preserve_newlines = false
        bt('function foo() {\n    return 1;\n}\n\nfunction foo() {\n    return 1;\n}');
        bt('function foo() {\n    return 1;\n}\nfunction foo() {\n    return 1;\n}',
           'function foo() {\n    return 1;\n}\n\nfunction foo() {\n    return 1;\n}'
          );
        bt('function foo() {\n    return 1;\n}\n\n\nfunction foo() {\n    return 1;\n}',
           'function foo() {\n    return 1;\n}\n\nfunction foo() {\n    return 1;\n}'
          );

        opts.preserve_newlines = true;
        bt('var\na=do_preserve_newlines;', 'var\n    a = do_preserve_newlines;');
        bt('// a\n// b\n\n// c\n// d');
        bt('if (foo) //  comment\n{\n    bar();\n}');


        opts.keep_array_indentation = false;
        bt("a = ['a', 'b', 'c',\n   'd', 'e', 'f']",
            "a = ['a', 'b', 'c',\n    'd', 'e', 'f'\n]");
        bt("a = ['a', 'b', 'c',\n   'd', 'e', 'f',\n        'g', 'h', 'i']",
            "a = ['a', 'b', 'c',\n    'd', 'e', 'f',\n    'g', 'h', 'i'\n]");
        bt("a = ['a', 'b', 'c',\n       'd', 'e', 'f',\n            'g', 'h', 'i']",
            "a = ['a', 'b', 'c',\n    'd', 'e', 'f',\n    'g', 'h', 'i'\n]");
        bt('var x = [{}\n]', 'var x = [{}]');
        bt('var x = [{foo:bar}\n]', 'var x = [{\n    foo: bar\n}]');
        bt("a = ['something',\n    'completely',\n    'different'];\nif (x);",
            "a = ['something',\n    'completely',\n    'different'\n];\nif (x);");
        bt("a = ['a','b','c']", "a = ['a', 'b', 'c']");

        bt("a = ['a',   'b','c']", "a = ['a', 'b', 'c']");
        bt("x = [{'a':0}]",
            "x = [{\n    'a': 0\n}]");
        bt('{a([[a1]], {b;});}',
            '{\n    a([\n        [a1]\n    ], {\n        b;\n    });\n}');
        bt("a();\n   [\n   ['sdfsdfsd'],\n        ['sdfsdfsdf']\n   ].toString();",
            "a();\n[\n    ['sdfsdfsd'],\n    ['sdfsdfsdf']\n].toString();");
        bt("a();\na = [\n   ['sdfsdfsd'],\n        ['sdfsdfsdf']\n   ].toString();",
            "a();\na = [\n    ['sdfsdfsd'],\n    ['sdfsdfsdf']\n].toString();");
        bt("function() {\n    Foo([\n        ['sdfsdfsd'],\n        ['sdfsdfsdf']\n    ]);\n}",
            "function() {\n    Foo([\n        ['sdfsdfsd'],\n        ['sdfsdfsdf']\n    ]);\n}");
        bt('function foo() {\n    return [\n        "one",\n        "two"\n    ];\n}');
        // 4 spaces per indent input, processed with 4-spaces per indent
        bt( "function foo() {\n" +
            "    return [\n" +
            "        {\n" +
            "            one: 'x',\n" +
            "            two: [\n" +
            "                {\n" +
            "                    id: 'a',\n" +
            "                    name: 'apple'\n" +
            "                }, {\n" +
            "                    id: 'b',\n" +
            "                    name: 'banana'\n" +
            "                }\n" +
            "            ]\n" +
            "        }\n" +
            "    ];\n" +
            "}",
            "function foo() {\n" +
            "    return [{\n" +
            "        one: 'x',\n" +
            "        two: [{\n" +
            "            id: 'a',\n" +
            "            name: 'apple'\n" +
            "        }, {\n" +
            "            id: 'b',\n" +
            "            name: 'banana'\n" +
            "        }]\n" +
            "    }];\n" +
            "}");
        // 3 spaces per indent input, processed with 4-spaces per indent
        bt( "function foo() {\n" +
            "   return [\n" +
            "      {\n" +
            "         one: 'x',\n" +
            "         two: [\n" +
            "            {\n" +
            "               id: 'a',\n" +
            "               name: 'apple'\n" +
            "            }, {\n" +
            "               id: 'b',\n" +
            "               name: 'banana'\n" +
            "            }\n" +
            "         ]\n" +
            "      }\n" +
            "   ];\n" +
            "}",
            "function foo() {\n" +
            "    return [{\n" +
            "        one: 'x',\n" +
            "        two: [{\n" +
            "            id: 'a',\n" +
            "            name: 'apple'\n" +
            "        }, {\n" +
            "            id: 'b',\n" +
            "            name: 'banana'\n" +
            "        }]\n" +
            "    }];\n" +
            "}");

        opts.keep_array_indentation = true;
        bt("a = ['a', 'b', 'c',\n   'd', 'e', 'f']");
        bt("a = ['a', 'b', 'c',\n   'd', 'e', 'f',\n        'g', 'h', 'i']");
        bt("a = ['a', 'b', 'c',\n       'd', 'e', 'f',\n            'g', 'h', 'i']");
        bt('var x = [{}\n]', 'var x = [{}\n]');
        bt('var x = [{foo:bar}\n]', 'var x = [{\n        foo: bar\n    }\n]');
        bt("a = ['something',\n    'completely',\n    'different'];\nif (x);");
        bt("a = ['a','b','c']", "a = ['a', 'b', 'c']");
        bt("a = ['a',   'b','c']", "a = ['a', 'b', 'c']");
        bt("x = [{'a':0}]",
            "x = [{\n    'a': 0\n}]");
        bt('{a([[a1]], {b;});}',
            '{\n    a([[a1]], {\n        b;\n    });\n}');
        bt("a();\n   [\n   ['sdfsdfsd'],\n        ['sdfsdfsdf']\n   ].toString();",
            "a();\n   [\n   ['sdfsdfsd'],\n        ['sdfsdfsdf']\n   ].toString();");
        bt("a();\na = [\n   ['sdfsdfsd'],\n        ['sdfsdfsdf']\n   ].toString();",
            "a();\na = [\n   ['sdfsdfsd'],\n        ['sdfsdfsdf']\n   ].toString();");
        bt("function() {\n    Foo([\n        ['sdfsdfsd'],\n        ['sdfsdfsdf']\n    ]);\n}",
            "function() {\n    Foo([\n        ['sdfsdfsd'],\n        ['sdfsdfsdf']\n    ]);\n}");
        bt('function foo() {\n    return [\n        "one",\n        "two"\n    ];\n}');
        // 4 spaces per indent input, processed with 4-spaces per indent
        bt( "function foo() {\n" +
            "    return [\n" +
            "        {\n" +
            "            one: 'x',\n" +
            "            two: [\n" +
            "                {\n" +
            "                    id: 'a',\n" +
            "                    name: 'apple'\n" +
            "                }, {\n" +
            "                    id: 'b',\n" +
            "                    name: 'banana'\n" +
            "                }\n" +
            "            ]\n" +
            "        }\n" +
            "    ];\n" +
            "}");
        // 3 spaces per indent input, processed with 4-spaces per indent
        // Should be unchanged, but is not - #445
//         bt( "function foo() {\n" +
//             "   return [\n" +
//             "      {\n" +
//             "         one: 'x',\n" +
//             "         two: [\n" +
//             "            {\n" +
//             "               id: 'a',\n" +
//             "               name: 'apple'\n" +
//             "            }, {\n" +
//             "               id: 'b',\n" +
//             "               name: 'banana'\n" +
//             "            }\n" +
//             "         ]\n" +
//             "      }\n" +
//             "   ];\n" +
//             "}");


        opts.keep_array_indentation = false;


        bt('a = //comment\n    /regex/;');

        test_fragment('/*\n * X\n */');
        test_fragment('/*\r\n * X\r\n */', '/*\n * X\n */');

        bt('if (a)\n{\nb;\n}\nelse\n{\nc;\n}', 'if (a) {\n    b;\n} else {\n    c;\n}');


        opts.brace_style = 'expand';

        bt('//case 1\nif (a == 1)\n{}\n//case 2\nelse if (a == 2)\n{}');
        bt('if(1){2}else{3}', "if (1)\n{\n    2\n}\nelse\n{\n    3\n}");
        bt('try{a();}catch(b){c();}catch(d){}finally{e();}',
            "try\n{\n    a();\n}\ncatch (b)\n{\n    c();\n}\ncatch (d)\n{}\nfinally\n{\n    e();\n}");
        bt('if(a){b();}else if(c) foo();',
            "if (a)\n{\n    b();\n}\nelse if (c) foo();");
        bt('if(X)if(Y)a();else b();else c();',
            "if (X)\n    if (Y) a();\n    else b();\nelse c();");
        bt("if (a) {\n// comment\n}else{\n// comment\n}",
            "if (a)\n{\n    // comment\n}\nelse\n{\n    // comment\n}"); // if/else statement with empty body
        bt('if (x) {y} else { if (x) {y}}',
            'if (x)\n{\n    y\n}\nelse\n{\n    if (x)\n    {\n        y\n    }\n}');
        bt('if (a)\n{\nb;\n}\nelse\n{\nc;\n}',
            'if (a)\n{\n    b;\n}\nelse\n{\n    c;\n}');
        test_fragment('    /*\n* xx\n*/\n// xx\nif (foo) {\n    bar();\n}',
                      '    /*\n     * xx\n     */\n    // xx\n    if (foo)\n    {\n        bar();\n    }');
        bt('if (foo)\n{}\nelse /regex/.test();');
        bt('if (foo) /regex/.test();');
        bt('if (a)\n{\nb;\n}\nelse\n{\nc;\n}', 'if (a)\n{\n    b;\n}\nelse\n{\n    c;\n}');
        test_fragment('if (foo) {', 'if (foo)\n{');
        test_fragment('foo {', 'foo\n{');
        test_fragment('return {', 'return {'); // return needs the brace.
        test_fragment('return /* inline */ {', 'return /* inline */ {');
        // test_fragment('return\n{', 'return\n{'); // can't support this?, but that's an improbable and extreme case anyway.
        test_fragment('return;\n{', 'return;\n{');
        bt("throw {}");
        bt("throw {\n    foo;\n}");
        bt('var foo = {}');
        bt('if (foo) bar();\nelse break');
        bt('function x() {\n    foo();\n}zzz', 'function x()\n{\n    foo();\n}\nzzz');
        bt('a: do {} while (); xxx', 'a: do {} while ();\nxxx');
        bt('var a = new function();');
        bt('var a = new function() {};');
        bt('var a = new function()\n{};', 'var a = new function() {};');
        bt('var a = new function a()\n{};');
        bt('var a = new function a()\n    {},\n    b = new function b()\n    {};');
        test_fragment('new function');
        bt("foo({\n    'a': 1\n},\n10);",
            "foo(\n    {\n        'a': 1\n    },\n    10);");
        bt('(["foo","bar"]).each(function(i) {return i;});',
            '(["foo", "bar"]).each(function(i)\n{\n    return i;\n});');
        bt('(function(i) {return i;})();',
            '(function(i)\n{\n    return i;\n})();');
        bt( "test( /*Argument 1*/ {\n" +
            "    'Value1': '1'\n" +
            "}, /*Argument 2\n" +
            " */ {\n" +
            "    'Value2': '2'\n" +
            "});",
            // expected
            "test( /*Argument 1*/\n" +
            "    {\n" +
            "        'Value1': '1'\n" +
            "    },\n" +
            "    /*Argument 2\n" +
            "     */\n" +
            "    {\n" +
            "        'Value2': '2'\n" +
            "    });");
        bt( "test(\n" +
            "/*Argument 1*/ {\n" +
            "    'Value1': '1'\n" +
            "},\n" +
            "/*Argument 2\n" +
            " */ {\n" +
            "    'Value2': '2'\n" +
            "});",
            // expected
            "test(\n" +
            "    /*Argument 1*/\n" +
            "    {\n" +
            "        'Value1': '1'\n" +
            "    },\n" +
            "    /*Argument 2\n" +
            "     */\n" +
            "    {\n" +
            "        'Value2': '2'\n" +
            "    });");
        bt( "test( /*Argument 1*/\n" +
            "{\n" +
            "    'Value1': '1'\n" +
            "}, /*Argument 2\n" +
            " */\n" +
            "{\n" +
            "    'Value2': '2'\n" +
            "});",
            // expected
            "test( /*Argument 1*/\n" +
            "    {\n" +
            "        'Value1': '1'\n" +
            "    },\n" +
            "    /*Argument 2\n" +
            "     */\n" +
            "    {\n" +
            "        'Value2': '2'\n" +
            "    });");

        opts.brace_style = 'collapse';

        bt('//case 1\nif (a == 1) {}\n//case 2\nelse if (a == 2) {}');
        bt('if(1){2}else{3}', "if (1) {\n    2\n} else {\n    3\n}");
        bt('try{a();}catch(b){c();}catch(d){}finally{e();}',
             "try {\n    a();\n} catch (b) {\n    c();\n} catch (d) {} finally {\n    e();\n}");
        bt('if(a){b();}else if(c) foo();',
            "if (a) {\n    b();\n} else if (c) foo();");
        bt("if (a) {\n// comment\n}else{\n// comment\n}",
            "if (a) {\n    // comment\n} else {\n    // comment\n}"); // if/else statement with empty body
        bt('if (x) {y} else { if (x) {y}}',
            'if (x) {\n    y\n} else {\n    if (x) {\n        y\n    }\n}');
        bt('if (a)\n{\nb;\n}\nelse\n{\nc;\n}',
            'if (a) {\n    b;\n} else {\n    c;\n}');
        test_fragment('    /*\n* xx\n*/\n// xx\nif (foo) {\n    bar();\n}',
                      '    /*\n     * xx\n     */\n    // xx\n    if (foo) {\n        bar();\n    }');
        bt('if (foo) {} else /regex/.test();');
        bt('if (foo) /regex/.test();');
        bt('if (a)\n{\nb;\n}\nelse\n{\nc;\n}', 'if (a) {\n    b;\n} else {\n    c;\n}');
        test_fragment('if (foo) {', 'if (foo) {');
        test_fragment('foo {', 'foo {');
        test_fragment('return {', 'return {'); // return needs the brace.
        test_fragment('return /* inline */ {', 'return /* inline */ {');
        // test_fragment('return\n{', 'return\n{'); // can't support this?, but that's an improbable and extreme case anyway.
        test_fragment('return;\n{', 'return; {');
        bt("throw {}");
        bt("throw {\n    foo;\n}");
        bt('var foo = {}');
        bt('if (foo) bar();\nelse break');
        bt('function x() {\n    foo();\n}zzz', 'function x() {\n    foo();\n}\nzzz');
        bt('a: do {} while (); xxx', 'a: do {} while ();\nxxx');
        bt('var a = new function();');
        bt('var a = new function() {};');
        bt('var a = new function a() {};');
        test_fragment('new function');
        bt("foo({\n    'a': 1\n},\n10);",
            "foo({\n        'a': 1\n    },\n    10);");
        bt('(["foo","bar"]).each(function(i) {return i;});',
            '(["foo", "bar"]).each(function(i) {\n    return i;\n});');
        bt('(function(i) {return i;})();',
            '(function(i) {\n    return i;\n})();');
        bt( "test( /*Argument 1*/ {\n" +
            "    'Value1': '1'\n" +
            "}, /*Argument 2\n" +
            " */ {\n" +
            "    'Value2': '2'\n" +
            "});",
            // expected
            "test( /*Argument 1*/ {\n" +
            "        'Value1': '1'\n" +
            "    },\n" +
            "    /*Argument 2\n" +
            "     */\n" +
            "    {\n" +
            "        'Value2': '2'\n" +
            "    });");
        bt( "test(\n" +
            "/*Argument 1*/ {\n" +
            "    'Value1': '1'\n" +
            "},\n" +
            "/*Argument 2\n" +
            " */ {\n" +
            "    'Value2': '2'\n" +
            "});",
            // expected
            "test(\n" +
            "    /*Argument 1*/\n" +
            "    {\n" +
            "        'Value1': '1'\n" +
            "    },\n" +
            "    /*Argument 2\n" +
            "     */\n" +
            "    {\n" +
            "        'Value2': '2'\n" +
            "    });");
        bt( "test( /*Argument 1*/\n" +
            "{\n" +
            "    'Value1': '1'\n" +
            "}, /*Argument 2\n" +
            " */\n" +
            "{\n" +
            "    'Value2': '2'\n" +
            "});",
            // expected
            "test( /*Argument 1*/ {\n" +
            "        'Value1': '1'\n" +
            "    },\n" +
            "    /*Argument 2\n" +
            "     */\n" +
            "    {\n" +
            "        'Value2': '2'\n" +
            "    });");

        opts.brace_style = "end-expand";

        bt('//case 1\nif (a == 1) {}\n//case 2\nelse if (a == 2) {}');
        bt('if(1){2}else{3}', "if (1) {\n    2\n}\nelse {\n    3\n}");
        bt('try{a();}catch(b){c();}catch(d){}finally{e();}',
            "try {\n    a();\n}\ncatch (b) {\n    c();\n}\ncatch (d) {}\nfinally {\n    e();\n}");
        bt('if(a){b();}else if(c) foo();',
            "if (a) {\n    b();\n}\nelse if (c) foo();");
        bt("if (a) {\n// comment\n}else{\n// comment\n}",
            "if (a) {\n    // comment\n}\nelse {\n    // comment\n}"); // if/else statement with empty body
        bt('if (x) {y} else { if (x) {y}}',
            'if (x) {\n    y\n}\nelse {\n    if (x) {\n        y\n    }\n}');
        bt('if (a)\n{\nb;\n}\nelse\n{\nc;\n}',
            'if (a) {\n    b;\n}\nelse {\n    c;\n}');
        test_fragment('    /*\n* xx\n*/\n// xx\nif (foo) {\n    bar();\n}',
                      '    /*\n     * xx\n     */\n    // xx\n    if (foo) {\n        bar();\n    }');
        bt('if (foo) {}\nelse /regex/.test();');
        bt('if (foo) /regex/.test();');
        bt('if (a)\n{\nb;\n}\nelse\n{\nc;\n}', 'if (a) {\n    b;\n}\nelse {\n    c;\n}');
        test_fragment('if (foo) {', 'if (foo) {');
        test_fragment('foo {', 'foo {');
        test_fragment('return {', 'return {'); // return needs the brace.
        test_fragment('return /* inline */ {', 'return /* inline */ {');
        // test_fragment('return\n{', 'return\n{'); // can't support this?, but that's an improbable and extreme case anyway.
        test_fragment('return;\n{', 'return; {');
        bt("throw {}");
        bt("throw {\n    foo;\n}");
        bt('var foo = {}');
        bt('if (foo) bar();\nelse break');
        bt('function x() {\n    foo();\n}zzz', 'function x() {\n    foo();\n}\nzzz');
        bt('a: do {} while (); xxx', 'a: do {} while ();\nxxx');
        bt('var a = new function();');
        bt('var a = new function() {};');
        bt('var a = new function a() {};');
        test_fragment('new function');
        bt("foo({\n    'a': 1\n},\n10);",
            "foo({\n        'a': 1\n    },\n    10);");
        bt('(["foo","bar"]).each(function(i) {return i;});',
            '(["foo", "bar"]).each(function(i) {\n    return i;\n});');
        bt('(function(i) {return i;})();',
            '(function(i) {\n    return i;\n})();');
        bt( "test( /*Argument 1*/ {\n" +
            "    'Value1': '1'\n" +
            "}, /*Argument 2\n" +
            " */ {\n" +
            "    'Value2': '2'\n" +
            "});",
            // expected
            "test( /*Argument 1*/ {\n" +
            "        'Value1': '1'\n" +
            "    },\n" +
            "    /*Argument 2\n" +
            "     */\n" +
            "    {\n" +
            "        'Value2': '2'\n" +
            "    });");
        bt( "test(\n" +
            "/*Argument 1*/ {\n" +
            "    'Value1': '1'\n" +
            "},\n" +
            "/*Argument 2\n" +
            " */ {\n" +
            "    'Value2': '2'\n" +
            "});",
            // expected
            "test(\n" +
            "    /*Argument 1*/\n" +
            "    {\n" +
            "        'Value1': '1'\n" +
            "    },\n" +
            "    /*Argument 2\n" +
            "     */\n" +
            "    {\n" +
            "        'Value2': '2'\n" +
            "    });");
        bt( "test( /*Argument 1*/\n" +
            "{\n" +
            "    'Value1': '1'\n" +
            "}, /*Argument 2\n" +
            " */\n" +
            "{\n" +
            "    'Value2': '2'\n" +
            "});",
            // expected
            "test( /*Argument 1*/ {\n" +
            "        'Value1': '1'\n" +
            "    },\n" +
            "    /*Argument 2\n" +
            "     */\n" +
            "    {\n" +
            "        'Value2': '2'\n" +
            "    });");

        opts.brace_style = 'collapse';


        bt('a = <?= external() ?> ;'); // not the most perfect thing in the world, but you're the weirdo beaufifying php mix-ins with javascript beautifier
        bt('a = <%= external() %> ;');

        bt('// func-comment\n\nfunction foo() {}\n\n// end-func-comment');

        test_fragment('roo = {\n    /*\n    ****\n      FOO\n    ****\n    */\n    BAR: 0\n};');

        bt('"foo""bar""baz"', '"foo"\n"bar"\n"baz"');
        bt("'foo''bar''baz'", "'foo'\n'bar'\n'baz'");


        test_fragment("if (zz) {\n    // ....\n}\n(function");

        bt("{\n    get foo() {}\n}");
        bt("{\n    var a = get\n    foo();\n}");
        bt("{\n    set foo() {}\n}");
        bt("{\n    var a = set\n    foo();\n}");
        bt("var x = {\n    get function()\n}");
        bt("var x = {\n    set function()\n}");
        bt("var x = set\n\na() {}", "var x = set\n\n    a() {}");
        bt("var x = set\n\nfunction() {}", "var x = set\n\n    function() {}");

        bt('<!-- foo\nbar();\n-->');
        bt('<!-- dont crash');
        bt('for () /abc/.test()');
        bt('if (k) /aaa/m.test(v) && l();');
        bt('switch (true) {\n    case /swf/i.test(foo):\n        bar();\n}');
        bt('createdAt = {\n    type: Date,\n    default: Date.now\n}');
        bt('switch (createdAt) {\n    case a:\n        Date,\n    default:\n        Date.now\n}');
        opts.space_before_conditional = false;
        bt('if(a) b()');
        opts.space_before_conditional = true;


        opts.preserve_newlines = true;
        bt('var a = 42; // foo\n\nvar b;');
        bt('var a = 42; // foo\n\n\nvar b;');
        bt("var a = 'foo' +\n    'bar';");
        bt("var a = \"foo\" +\n    \"bar\";");
        bt('this.oa = new OAuth(\n' +
           '    _requestToken,\n' +
           '    _accessToken,\n' +
           '    consumer_key\n' +
           ');');


        opts.unescape_strings = false;
        test_fragment('"\\x22\\x27", \'\\x22\\x27\', "\\x5c", \'\\x5c\', "\\xff and \\xzz", "unicode \\u0000 \\u0022 \\u0027 \\u005c \\uffff \\uzzzz"');
        opts.unescape_strings = true;
        test_fragment('"\\x20\\x40\\x4a"', '" @J"');
        test_fragment('"\\xff\\x40\\x4a"');
        test_fragment('"\\u0072\\u016B\\u0137\\u012B\\u0074\\u0069\\u0073"', '"rūķītis"');
        test_fragment('"Google Chrome est\\u00E1 actualizado."', '"Google Chrome está actualizado."');
        /*
        bt('"\\x22\\x27",\'\\x22\\x27\',"\\x5c",\'\\x5c\',"\\xff and \\xzz","unicode \\u0000 \\u0022 \\u0027 \\u005c \\uffff \\uzzzz"',
           '"\\"\'", \'"\\\'\', "\\\\", \'\\\\\', "\\xff and \\xzz", "unicode \\u0000 \\" \' \\\\ \\uffff \\uzzzz"');
        */
        opts.unescape_strings = false;

        bt('return function();');
        bt('var a = function();');
        bt('var a = 5 + function();');

        bt('3.*7;', '3. * 7;');
        bt('import foo.*;', 'import foo.*;'); // actionscript's import
        test_fragment('function f(a: a, b: b)'); // actionscript

        bt('{\n    foo // something\n    ,\n    bar // something\n    baz\n}');
        bt('function a(a) {} function b(b) {} function c(c) {}', 'function a(a) {}\n\nfunction b(b) {}\n\nfunction c(c) {}');
        bt('foo(a, function() {})');

        bt('foo(a, /regex/)');

        bt('/* foo */\n"x"');

        opts.break_chained_methods = false;
        opts.preserve_newlines = false;
        bt('foo\n.bar()\n.baz().cucumber(fat)', 'foo.bar().baz().cucumber(fat)');
        bt('foo\n.bar()\n.baz().cucumber(fat); foo.bar().baz().cucumber(fat)', 'foo.bar().baz().cucumber(fat);\nfoo.bar().baz().cucumber(fat)');
        bt('foo\n.bar()\n.baz().cucumber(fat)\n foo.bar().baz().cucumber(fat)', 'foo.bar().baz().cucumber(fat)\nfoo.bar().baz().cucumber(fat)');
        bt('this\n.something = foo.bar()\n.baz().cucumber(fat)', 'this.something = foo.bar().baz().cucumber(fat)');
        bt('this.something.xxx = foo.moo.bar()');
        bt('this\n.something\n.xxx = foo.moo\n.bar()', 'this.something.xxx = foo.moo.bar()');

        opts.break_chained_methods = false;
        opts.preserve_newlines = true;
        bt('foo\n.bar()\n.baz().cucumber(fat)', 'foo\n    .bar()\n    .baz().cucumber(fat)');
        bt('foo\n.bar()\n.baz().cucumber(fat); foo.bar().baz().cucumber(fat)', 'foo\n    .bar()\n    .baz().cucumber(fat);\nfoo.bar().baz().cucumber(fat)');
        bt('foo\n.bar()\n.baz().cucumber(fat)\n foo.bar().baz().cucumber(fat)', 'foo\n    .bar()\n    .baz().cucumber(fat)\nfoo.bar().baz().cucumber(fat)');
        bt('this\n.something = foo.bar()\n.baz().cucumber(fat)', 'this\n    .something = foo.bar()\n    .baz().cucumber(fat)');
        bt('this.something.xxx = foo.moo.bar()');
        bt('this\n.something\n.xxx = foo.moo\n.bar()', 'this\n    .something\n    .xxx = foo.moo\n    .bar()');

        opts.break_chained_methods = true;
        opts.preserve_newlines = false;
        bt('foo\n.bar()\n.baz().cucumber(fat)', 'foo.bar()\n    .baz()\n    .cucumber(fat)');
        bt('foo\n.bar()\n.baz().cucumber(fat); foo.bar().baz().cucumber(fat)', 'foo.bar()\n    .baz()\n    .cucumber(fat);\nfoo.bar()\n    .baz()\n    .cucumber(fat)');
        bt('foo\n.bar()\n.baz().cucumber(fat)\n foo.bar().baz().cucumber(fat)', 'foo.bar()\n    .baz()\n    .cucumber(fat)\nfoo.bar()\n    .baz()\n    .cucumber(fat)');
        bt('this\n.something = foo.bar()\n.baz().cucumber(fat)', 'this.something = foo.bar()\n    .baz()\n    .cucumber(fat)');
        bt('this.something.xxx = foo.moo.bar()');
        bt('this\n.something\n.xxx = foo.moo\n.bar()', 'this.something.xxx = foo.moo.bar()');

        opts.break_chained_methods = true;
        opts.preserve_newlines = true;
        bt('foo\n.bar()\n.baz().cucumber(fat)', 'foo\n    .bar()\n    .baz()\n    .cucumber(fat)');
        bt('foo\n.bar()\n.baz().cucumber(fat); foo.bar().baz().cucumber(fat)', 'foo\n    .bar()\n    .baz()\n    .cucumber(fat);\nfoo.bar()\n    .baz()\n    .cucumber(fat)');
        bt('foo\n.bar()\n.baz().cucumber(fat)\n foo.bar().baz().cucumber(fat)', 'foo\n    .bar()\n    .baz()\n    .cucumber(fat)\nfoo.bar()\n    .baz()\n    .cucumber(fat)');
        bt('this\n.something = foo.bar()\n.baz().cucumber(fat)', 'this\n    .something = foo.bar()\n    .baz()\n    .cucumber(fat)');
        bt('this.something.xxx = foo.moo.bar()');
        bt('this\n.something\n.xxx = foo.moo\n.bar()', 'this\n    .something\n    .xxx = foo.moo\n    .bar()');

        opts.break_chained_methods = false;

        // Line wrap test intputs
        //.............---------1---------2---------3---------4---------5---------6---------7
        //.............1234567890123456789012345678901234567890123456789012345678901234567890
        wrap_input_1=('foo.bar().baz().cucumber((fat && "sassy") || (leans\n&& mean));\n' +
                      'Test_very_long_variable_name_this_should_never_wrap\n.but_this_can\n' +
                      'if (wraps_can_occur && inside_an_if_block) that_is_\n.okay();\n' +
                      'object_literal = {\n' +
                      '    property: first_token_should_never_wrap + but_this_can,\n' +
                      '    propertz: first_token_should_never_wrap + !but_this_can,\n' +
                      '    proper: "first_token_should_never_wrap" + "but_this_can"\n' +
                      '}');

        //.............---------1---------2---------3---------4---------5---------6---------7
        //.............1234567890123456789012345678901234567890123456789012345678901234567890
        wrap_input_2=('{\n' +
                      '    foo.bar().baz().cucumber((fat && "sassy") || (leans\n&& mean));\n' +
                      '    Test_very_long_variable_name_this_should_never_wrap\n.but_this_can\n' +
                      '    if (wraps_can_occur && inside_an_if_block) that_is_\n.okay();\n' +
                      '    object_literal = {\n' +
                      '        property: first_token_should_never_wrap + but_this_can,\n' +
                      '        propertz: first_token_should_never_wrap + !but_this_can,\n' +
                      '        proper: "first_token_should_never_wrap" + "but_this_can"\n' +
                      '    }' +
                      '}');

        opts.preserve_newlines = false;
        opts.wrap_line_length = 0;
        //.............---------1---------2---------3---------4---------5---------6---------7
        //.............1234567890123456789012345678901234567890123456789012345678901234567890
        test_fragment(wrap_input_1,
                      /* expected */
                      'foo.bar().baz().cucumber((fat && "sassy") || (leans && mean));\n' +
                      'Test_very_long_variable_name_this_should_never_wrap.but_this_can\n' +
                      'if (wraps_can_occur && inside_an_if_block) that_is_.okay();\n' +
                      'object_literal = {\n' +
                      '    property: first_token_should_never_wrap + but_this_can,\n' +
                      '    propertz: first_token_should_never_wrap + !but_this_can,\n' +
                      '    proper: "first_token_should_never_wrap" + "but_this_can"\n' +
                      '}');

        opts.wrap_line_length = 70;
        //.............---------1---------2---------3---------4---------5---------6---------7
        //.............1234567890123456789012345678901234567890123456789012345678901234567890
        test_fragment(wrap_input_1,
                      /* expected */
                      'foo.bar().baz().cucumber((fat && "sassy") || (leans && mean));\n' +
                      'Test_very_long_variable_name_this_should_never_wrap.but_this_can\n' +
                      'if (wraps_can_occur && inside_an_if_block) that_is_.okay();\n' +
                      'object_literal = {\n' +
                      '    property: first_token_should_never_wrap + but_this_can,\n' +
                      '    propertz: first_token_should_never_wrap + !but_this_can,\n' +
                      '    proper: "first_token_should_never_wrap" + "but_this_can"\n' +
                      '}');

        opts.wrap_line_length = 40;
        //.............---------1---------2---------3---------4---------5---------6---------7
        //.............1234567890123456789012345678901234567890123456789012345678901234567890
        test_fragment(wrap_input_1,
                      /* expected */
                      'foo.bar().baz().cucumber((fat &&\n' +
                      '    "sassy") || (leans && mean));\n' +
                      'Test_very_long_variable_name_this_should_never_wrap\n' +
                      '    .but_this_can\n' +
                      'if (wraps_can_occur &&\n' +
                      '    inside_an_if_block) that_is_.okay();\n' +
                      'object_literal = {\n' +
                      '    property: first_token_should_never_wrap +\n' +
                      '        but_this_can,\n' +
                      '    propertz: first_token_should_never_wrap +\n' +
                      '        !but_this_can,\n' +
                      '    proper: "first_token_should_never_wrap" +\n' +
                      '        "but_this_can"\n' +
                      '}');

        opts.wrap_line_length = 41;
        // NOTE: wrap is only best effort - line continues until next wrap point is found.
        //.............---------1---------2---------3---------4---------5---------6---------7
        //.............1234567890123456789012345678901234567890123456789012345678901234567890
        test_fragment(wrap_input_1,
                      /* expected */
                      'foo.bar().baz().cucumber((fat && "sassy") ||\n' +
                      '    (leans && mean));\n' +
                      'Test_very_long_variable_name_this_should_never_wrap\n' +
                      '    .but_this_can\n' +
                      'if (wraps_can_occur &&\n' +
                      '    inside_an_if_block) that_is_.okay();\n' +
                      'object_literal = {\n' +
                      '    property: first_token_should_never_wrap +\n' +
                      '        but_this_can,\n' +
                      '    propertz: first_token_should_never_wrap +\n' +
                      '        !but_this_can,\n' +
                      '    proper: "first_token_should_never_wrap" +\n' +
                      '        "but_this_can"\n' +
                      '}');

        opts.wrap_line_length = 45;
        // NOTE: wrap is only best effort - line continues until next wrap point is found.
        //.............---------1---------2---------3---------4---------5---------6---------7
        //.............1234567890123456789012345678901234567890123456789012345678901234567890
        test_fragment(wrap_input_2,
                      /* expected */
                      '{\n' +
                      '    foo.bar().baz().cucumber((fat && "sassy") ||\n' +
                      '        (leans && mean));\n' +
                      '    Test_very_long_variable_name_this_should_never_wrap\n' +
                      '        .but_this_can\n' +
                      '    if (wraps_can_occur &&\n' +
                      '        inside_an_if_block) that_is_.okay();\n' +
                      '    object_literal = {\n' +
                      '        property: first_token_should_never_wrap +\n' +
                      '            but_this_can,\n' +
                      '        propertz: first_token_should_never_wrap +\n' +
                      '            !but_this_can,\n' +
                      '        proper: "first_token_should_never_wrap" +\n' +
                      '            "but_this_can"\n' +
                      '    }\n'+
                      '}');

        opts.preserve_newlines = true;
        opts.wrap_line_length = 0;
        //.............---------1---------2---------3---------4---------5---------6---------7
        //.............1234567890123456789012345678901234567890123456789012345678901234567890
        test_fragment(wrap_input_1,
                      /* expected */
                      'foo.bar().baz().cucumber((fat && "sassy") || (leans && mean));\n' +
                      'Test_very_long_variable_name_this_should_never_wrap\n' +
                      '    .but_this_can\n' +
                      'if (wraps_can_occur && inside_an_if_block) that_is_\n' +
                      '    .okay();\n' +
                      'object_literal = {\n' +
                      '    property: first_token_should_never_wrap + but_this_can,\n' +
                      '    propertz: first_token_should_never_wrap + !but_this_can,\n' +
                      '    proper: "first_token_should_never_wrap" + "but_this_can"\n' +
                      '}');

        opts.wrap_line_length = 70;
        //.............---------1---------2---------3---------4---------5---------6---------7
        //.............1234567890123456789012345678901234567890123456789012345678901234567890
        test_fragment(wrap_input_1,
                      /* expected */
                      'foo.bar().baz().cucumber((fat && "sassy") || (leans && mean));\n' +
                      'Test_very_long_variable_name_this_should_never_wrap\n' +
                      '    .but_this_can\n' +
                      'if (wraps_can_occur && inside_an_if_block) that_is_\n' +
                      '    .okay();\n' +
                      'object_literal = {\n' +
                      '    property: first_token_should_never_wrap + but_this_can,\n' +
                      '    propertz: first_token_should_never_wrap + !but_this_can,\n' +
                      '    proper: "first_token_should_never_wrap" + "but_this_can"\n' +
                      '}');


        opts.wrap_line_length = 40;
        //.............---------1---------2---------3---------4---------5---------6---------7
        //.............1234567890123456789012345678901234567890123456789012345678901234567890
        test_fragment(wrap_input_1,
                      /* expected */
                      'foo.bar().baz().cucumber((fat &&\n' +
                      '    "sassy") || (leans && mean));\n' +
                      'Test_very_long_variable_name_this_should_never_wrap\n' +
                      '    .but_this_can\n' +
                      'if (wraps_can_occur &&\n' +
                      '    inside_an_if_block) that_is_\n' +
                      '    .okay();\n' +
                      'object_literal = {\n' +
                      '    property: first_token_should_never_wrap +\n' +
                      '        but_this_can,\n' +
                      '    propertz: first_token_should_never_wrap +\n' +
                      '        !but_this_can,\n' +
                      '    proper: "first_token_should_never_wrap" +\n' +
                      '        "but_this_can"\n' +
                      '}');

        opts.wrap_line_length = 41;
        // NOTE: wrap is only best effort - line continues until next wrap point is found.
        //.............---------1---------2---------3---------4---------5---------6---------7
        //.............1234567890123456789012345678901234567890123456789012345678901234567890
        test_fragment(wrap_input_1,
                      /* expected */
                      'foo.bar().baz().cucumber((fat && "sassy") ||\n' +
                      '    (leans && mean));\n' +
                      'Test_very_long_variable_name_this_should_never_wrap\n' +
                      '    .but_this_can\n' +
                      'if (wraps_can_occur &&\n' +
                      '    inside_an_if_block) that_is_\n' +
                      '    .okay();\n' +
                      'object_literal = {\n' +
                      '    property: first_token_should_never_wrap +\n' +
                      '        but_this_can,\n' +
                      '    propertz: first_token_should_never_wrap +\n' +
                      '        !but_this_can,\n' +
                      '    proper: "first_token_should_never_wrap" +\n' +
                      '        "but_this_can"\n' +
                      '}');

        opts.wrap_line_length = 45;
        // NOTE: wrap is only best effort - line continues until next wrap point is found.
        //.............---------1---------2---------3---------4---------5---------6---------7
        //.............1234567890123456789012345678901234567890123456789012345678901234567890
        test_fragment(wrap_input_2,
                      /* expected */
                      '{\n' +
                      '    foo.bar().baz().cucumber((fat && "sassy") ||\n' +
                      '        (leans && mean));\n' +
                      '    Test_very_long_variable_name_this_should_never_wrap\n' +
                      '        .but_this_can\n' +
                      '    if (wraps_can_occur &&\n' +
                      '        inside_an_if_block) that_is_\n' +
                      '        .okay();\n' +
                      '    object_literal = {\n' +
                      '        property: first_token_should_never_wrap +\n' +
                      '            but_this_can,\n' +
                      '        propertz: first_token_should_never_wrap +\n' +
                      '            !but_this_can,\n' +
                      '        proper: "first_token_should_never_wrap" +\n' +
                      '            "but_this_can"\n' +
                      '    }\n'+
                      '}');

        opts.wrap_line_length = 0;

        opts.preserve_newlines = false;
        bt('if (foo) // comment\n    bar();');
        bt('if (foo) // comment\n    (bar());');
        bt('if (foo) // comment\n    (bar());');
        bt('if (foo) // comment\n    /asdf/;');
        bt('this.oa = new OAuth(\n' +
           '    _requestToken,\n' +
           '    _accessToken,\n' +
           '    consumer_key\n' +
           ');',
           'this.oa = new OAuth(_requestToken, _accessToken, consumer_key);');
        bt('foo = {\n    x: y, // #44\n    w: z // #44\n}');
        bt('switch (x) {\n    case "a":\n        // comment on newline\n        break;\n    case "b": // comment on same line\n        break;\n}');
        bt('this.type =\n    this.options =\n    // comment\n    this.enabled null;',
           'this.type = this.options =\n    // comment\n    this.enabled null;');
        bt('someObj\n    .someFunc1()\n    // This comment should not break the indent\n    .someFunc2();',
           'someObj.someFunc1()\n    // This comment should not break the indent\n    .someFunc2();');

        bt('if (true ||\n!true) return;', 'if (true || !true) return;');

        // these aren't ready yet.
        //bt('if (foo) // comment\n    bar() /*i*/ + baz() /*j\n*/ + asdf();');
        bt('if\n(foo)\nif\n(bar)\nif\n(baz)\nwhee();\na();',
            'if (foo)\n    if (bar)\n        if (baz) whee();\na();');
        bt('if\n(foo)\nif\n(bar)\nif\n(baz)\nwhee();\nelse\na();',
            'if (foo)\n    if (bar)\n        if (baz) whee();\n        else a();');
        bt('if (foo)\nbar();\nelse\ncar();',
            'if (foo) bar();\nelse car();');

        bt('if (foo) if (bar) if (baz);\na();',
            'if (foo)\n    if (bar)\n        if (baz);\na();');
        bt('if (foo) if (bar) if (baz) whee();\na();',
            'if (foo)\n    if (bar)\n        if (baz) whee();\na();');
        bt('if (foo) a()\nif (bar) if (baz) whee();\na();',
            'if (foo) a()\nif (bar)\n    if (baz) whee();\na();');
        bt('if (foo);\nif (bar) if (baz) whee();\na();',
            'if (foo);\nif (bar)\n    if (baz) whee();\na();');
        bt('if (options)\n' +
           '    for (var p in options)\n' +
           '        this[p] = options[p];',
           'if (options)\n'+
           '    for (var p in options) this[p] = options[p];');
        bt('if (options) for (var p in options) this[p] = options[p];',
           'if (options)\n    for (var p in options) this[p] = options[p];');

        bt('if (options) do q(); while (b());',
           'if (options)\n    do q(); while (b());');
        bt('if (options) while (b()) q();',
           'if (options)\n    while (b()) q();');
        bt('if (options) do while (b()) q(); while (a());',
           'if (options)\n    do\n        while (b()) q(); while (a());');

        bt('function f(a, b, c,\nd, e) {}',
            'function f(a, b, c, d, e) {}');

        bt('function f(a,b) {if(a) b()}function g(a,b) {if(!a) b()}',
            'function f(a, b) {\n    if (a) b()\n}\n\nfunction g(a, b) {\n    if (!a) b()\n}');
        bt('function f(a,b) {if(a) b()}\n\n\n\nfunction g(a,b) {if(!a) b()}',
            'function f(a, b) {\n    if (a) b()\n}\n\nfunction g(a, b) {\n    if (!a) b()\n}');

        // This is not valid syntax, but still want to behave reasonably and not side-effect
        bt('(if(a) b())(if(a) b())',
            '(\n    if (a) b())(\n    if (a) b())');
        bt('(if(a) b())\n\n\n(if(a) b())',
            '(\n    if (a) b())\n(\n    if (a) b())');



        bt("if\n(a)\nb();", "if (a) b();");
        bt('var a =\nfoo', 'var a = foo');
        bt('var a = {\n"a":1,\n"b":2}', "var a = {\n    \"a\": 1,\n    \"b\": 2\n}");
        bt("var a = {\n'a':1,\n'b':2}", "var a = {\n    'a': 1,\n    'b': 2\n}");
        bt('var a = /*i*/ "b";');
        bt('var a = /*i*/\n"b";', 'var a = /*i*/ "b";');
        bt('var a = /*i*/\nb;', 'var a = /*i*/ b;');
        bt('{\n\n\n"x"\n}', '{\n    "x"\n}');
        bt('if(a &&\nb\n||\nc\n||d\n&&\ne) e = f', 'if (a && b || c || d && e) e = f');
        bt('if(a &&\n(b\n||\nc\n||d)\n&&\ne) e = f', 'if (a && (b || c || d) && e) e = f');
        test_fragment('\n\n"x"', '"x"');
        bt('a = 1;\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nb = 2;',
                'a = 1;\nb = 2;');

        opts.preserve_newlines = true;
        bt('if (foo) // comment\n    bar();');
        bt('if (foo) // comment\n    (bar());');
        bt('if (foo) // comment\n    (bar());');
        bt('if (foo) // comment\n    /asdf/;');
        bt('foo = {\n    x: y, // #44\n    w: z // #44\n}');
        bt('switch (x) {\n    case "a":\n        // comment on newline\n        break;\n    case "b": // comment on same line\n        break;\n}');
        bt('this.type =\n    this.options =\n    // comment\n    this.enabled null;');
        bt('someObj\n    .someFunc1()\n    // This comment should not break the indent\n    .someFunc2();');

        bt('if (true ||\n!true) return;', 'if (true ||\n    !true) return;');

        // these aren't ready yet.
        // bt('if (foo) // comment\n    bar() /*i*/ + baz() /*j\n*/ + asdf();');
        bt('if\n(foo)\nif\n(bar)\nif\n(baz)\nwhee();\na();',
            'if (foo)\n    if (bar)\n        if (baz)\n            whee();\na();');
        bt('if\n(foo)\nif\n(bar)\nif\n(baz)\nwhee();\nelse\na();',
            'if (foo)\n    if (bar)\n        if (baz)\n            whee();\n        else\n            a();');
        bt('if (foo) bar();\nelse\ncar();',
            'if (foo) bar();\nelse\n    car();');

        bt('if (foo) if (bar) if (baz);\na();',
            'if (foo)\n    if (bar)\n        if (baz);\na();');
        bt('if (foo) if (bar) if (baz) whee();\na();',
            'if (foo)\n    if (bar)\n        if (baz) whee();\na();');
        bt('if (foo) a()\nif (bar) if (baz) whee();\na();',
            'if (foo) a()\nif (bar)\n    if (baz) whee();\na();');
        bt('if (foo);\nif (bar) if (baz) whee();\na();',
            'if (foo);\nif (bar)\n    if (baz) whee();\na();');
        bt('if (options)\n' +
           '    for (var p in options)\n' +
           '        this[p] = options[p];');
        bt('if (options) for (var p in options) this[p] = options[p];',
           'if (options)\n    for (var p in options) this[p] = options[p];');

        bt('if (options) do q(); while (b());',
           'if (options)\n    do q(); while (b());');
        bt('if (options) do; while (b());',
           'if (options)\n    do; while (b());');
        bt('if (options) while (b()) q();',
           'if (options)\n    while (b()) q();');
        bt('if (options) do while (b()) q(); while (a());',
           'if (options)\n    do\n        while (b()) q(); while (a());');

        bt('function f(a, b, c,\nd, e) {}',
            'function f(a, b, c,\n    d, e) {}');

        bt('function f(a,b) {if(a) b()}function g(a,b) {if(!a) b()}',
            'function f(a, b) {\n    if (a) b()\n}\n\nfunction g(a, b) {\n    if (!a) b()\n}');
        bt('function f(a,b) {if(a) b()}\n\n\n\nfunction g(a,b) {if(!a) b()}',
            'function f(a, b) {\n    if (a) b()\n}\n\n\n\nfunction g(a, b) {\n    if (!a) b()\n}');
        // This is not valid syntax, but still want to behave reasonably and not side-effect
        bt('(if(a) b())(if(a) b())',
            '(\n    if (a) b())(\n    if (a) b())');
        bt('(if(a) b())\n\n\n(if(a) b())',
            '(\n    if (a) b())\n\n\n(\n    if (a) b())');

        // space between functions
        bt('/*\n * foo\n */\nfunction foo() {}');
        bt('// a nice function\nfunction foo() {}');
        bt('function foo() {}\nfunction foo() {}',
            'function foo() {}\n\nfunction foo() {}'
        );



        bt("if\n(a)\nb();", "if (a)\n    b();");
        bt('var a =\nfoo', 'var a =\n    foo');
        bt('var a = {\n"a":1,\n"b":2}', "var a = {\n    \"a\": 1,\n    \"b\": 2\n}");
        bt("var a = {\n'a':1,\n'b':2}", "var a = {\n    'a': 1,\n    'b': 2\n}");
        bt('var a = /*i*/ "b";');
        bt('var a = /*i*/\n"b";', 'var a = /*i*/\n    "b";');
        bt('var a = /*i*/\nb;', 'var a = /*i*/\n    b;');
        bt('{\n\n\n"x"\n}', '{\n\n\n    "x"\n}');
        bt('if(a &&\nb\n||\nc\n||d\n&&\ne) e = f', 'if (a &&\n    b ||\n    c || d &&\n    e) e = f');
        bt('if(a &&\n(b\n||\nc\n||d)\n&&\ne) e = f', 'if (a &&\n    (b ||\n        c || d) &&\n    e) e = f');
        test_fragment('\n\n"x"', '"x"');

        // this beavior differs between js and python, defaults to unlimited in js, 10 in python
        bt('a = 1;\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nb = 2;',
            'a = 1;\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nb = 2;');
        opts.max_preserve_newlines = 8;
        bt('a = 1;\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nb = 2;',
            'a = 1;\n\n\n\n\n\n\n\nb = 2;');

        // Test the option to have spaces within parens
        opts.space_in_paren = false;
        bt('if(p) foo(a,b)', 'if (p) foo(a, b)');
        bt('try{while(true){willThrow()}}catch(result)switch(result){case 1:++result }',
           'try {\n    while (true) {\n        willThrow()\n    }\n} catch (result) switch (result) {\n    case 1:\n        ++result\n}');
        bt('((e/((a+(b)*c)-d))^2)*5;', '((e / ((a + (b) * c) - d)) ^ 2) * 5;');
        bt('function f(a,b) {if(a) b()}function g(a,b) {if(!a) b()}',
            'function f(a, b) {\n    if (a) b()\n}\n\nfunction g(a, b) {\n    if (!a) b()\n}');
        bt('a=[];',
            'a = [];');
        bt('a=[b,c,d];',
            'a = [b, c, d];');
        bt('a= f[b];',
            'a = f[b];');
        opts.space_in_paren = true;
        bt('if(p) foo(a,b)', 'if ( p ) foo( a, b )');
        bt('try{while(true){willThrow()}}catch(result)switch(result){case 1:++result }',
           'try {\n    while ( true ) {\n        willThrow()\n    }\n} catch ( result ) switch ( result ) {\n    case 1:\n        ++result\n}');
        bt('((e/((a+(b)*c)-d))^2)*5;', '( ( e / ( ( a + ( b ) * c ) - d ) ) ^ 2 ) * 5;');
        bt('function f(a,b) {if(a) b()}function g(a,b) {if(!a) b()}',
            'function f( a, b ) {\n    if ( a ) b()\n}\n\nfunction g( a, b ) {\n    if ( !a ) b()\n}');
        bt('a=[];',
            'a = [];');
        bt('a=[b,c,d];',
            'a = [ b, c, d ];');
        bt('a= f[b];',
            'a = f[ b ];');
        opts.space_in_empty_paren = true;
        bt('if(p) foo(a,b)', 'if ( p ) foo( a, b )');
        bt('try{while(true){willThrow()}}catch(result)switch(result){case 1:++result }',
           'try {\n    while ( true ) {\n        willThrow( )\n    }\n} catch ( result ) switch ( result ) {\n    case 1:\n        ++result\n}');
        bt('((e/((a+(b)*c)-d))^2)*5;', '( ( e / ( ( a + ( b ) * c ) - d ) ) ^ 2 ) * 5;');
        bt('function f(a,b) {if(a) b()}function g(a,b) {if(!a) b()}',
            'function f( a, b ) {\n    if ( a ) b( )\n}\n\nfunction g( a, b ) {\n    if ( !a ) b( )\n}');
        bt('a=[];',
            'a = [ ];');
        bt('a=[b,c,d];',
            'a = [ b, c, d ];');
        bt('a= f[b];',
            'a = f[ b ];');
        opts.space_in_empty_paren = false;
        opts.space_in_paren = false;

        // Test template strings
        bt('`This is a ${template} string.`', '`This is a ${template} string.`');
        bt('`This\n  is\n  a\n  ${template}\n  string.`', '`This\n  is\n  a\n  ${template}\n  string.`');

        // Test that e4x literals passed through when e4x-option is enabled
        bt('xml=<a b="c"><d/><e>\n foo</e>x</a>;', 'xml = < a b = "c" > < d / > < e >\n    foo < /e>x</a > ;');
        opts.e4x = true;
        bt('xml=<a b="c"><d/><e>\n foo</e>x</a>;', 'xml = <a b="c"><d/><e>\n foo</e>x</a>;');
        bt('<a b=\'This is a quoted "c".\'/>', '<a b=\'This is a quoted "c".\'/>');
        bt('<a b="This is a quoted \'c\'."/>', '<a b="This is a quoted \'c\'."/>');
        bt('<a b="A quote \' inside string."/>', '<a b="A quote \' inside string."/>');
        bt('<a b=\'A quote " inside string.\'/>', '<a b=\'A quote " inside string.\'/>');
        bt('<a b=\'Some """ quotes ""  inside string.\'/>', '<a b=\'Some """ quotes ""  inside string.\'/>');
        // Handles inline expressions
        bt('xml=<{a} b="c"><d/><e v={z}>\n foo</e>x</{a}>;', 'xml = <{a} b="c"><d/><e v={z}>\n foo</e>x</{a}>;');
        // xml literals with special characters in elem names
        // see http://www.w3.org/TR/REC-xml/#NT-NameChar
        bt('xml = <_:.valid.xml- _:.valid.xml-="123"/>;', 'xml = <_:.valid.xml- _:.valid.xml-="123"/>;');
        // Handles CDATA
        bt('xml=<![CDATA[ b="c"><d/><e v={z}>\n foo</e>x/]]>;', 'xml = <![CDATA[ b="c"><d/><e v={z}>\n foo</e>x/]]>;');
        bt('xml=<![CDATA[]]>;', 'xml = <![CDATA[]]>;');
        bt('xml=<a b="c"><![CDATA[d/></a></{}]]></a>;', 'xml = <a b="c"><![CDATA[d/></a></{}]]></a>;');

        // Handles messed up tags, as long as it isn't the same name
        // as the root tag. Also handles tags of same name as root tag
        // as long as nesting matches.
        bt('xml=<a x="jn"><c></b></f><a><d jnj="jnn"><f></a ></nj></a>;',
         'xml = <a x="jn"><c></b></f><a><d jnj="jnn"><f></a ></nj></a>;');
        // If xml is not terminated, the remainder of the file is treated
        // as part of the xml-literal (passed through unaltered)
        test_fragment('xml=<a></b>\nc<b;', 'xml = <a></b>\nc<b;');
        opts.e4x = false;

        // START tests for issue 241
        bt('obj\n' +
           '    .last({\n' +
           '        foo: 1,\n' +
           '        bar: 2\n' +
           '    });\n' +
           'var test = 1;');

        bt('obj\n' +
           '    .last(a, function() {\n' +
           '        var test;\n' +
           '    });\n' +
           'var test = 1;');

        bt('obj.first()\n' +
           '    .second()\n' +
           '    .last(function(err, response) {\n' +
           '        console.log(err);\n' +
           '    });');

        // END tests for issue 241


        // START tests for issue 268 and 275
        bt('obj.last(a, function() {\n' +
           '    var test;\n' +
           '});\n' +
           'var test = 1;');
        bt('obj.last(a,\n' +
           '    function() {\n' +
           '        var test;\n' +
           '    });\n' +
           'var test = 1;');

        bt('(function() {if (!window.FOO) window.FOO || (window.FOO = function() {var b = {bar: "zort"};});})();',
           '(function() {\n' +
           '    if (!window.FOO) window.FOO || (window.FOO = function() {\n' +
           '        var b = {\n' +
           '            bar: "zort"\n' +
           '        };\n' +
           '    });\n' +
           '})();');
        // END tests for issue 268 and 275

        // START tests for issue 281
        bt('define(["dojo/_base/declare", "my/Employee", "dijit/form/Button",\n' +
           '    "dojo/_base/lang", "dojo/Deferred"\n' +
           '], function(declare, Employee, Button, lang, Deferred) {\n' +
           '    return declare(Employee, {\n' +
           '        constructor: function() {\n' +
           '            new Button({\n' +
           '                onClick: lang.hitch(this, function() {\n' +
           '                    new Deferred().then(lang.hitch(this, function() {\n' +
           '                        this.salary * 0.25;\n' +
           '                    }));\n' +
           '                })\n' +
           '            });\n' +
           '        }\n' +
           '    });\n' +
           '});');

        bt('define(["dojo/_base/declare", "my/Employee", "dijit/form/Button",\n' +
           '        "dojo/_base/lang", "dojo/Deferred"\n' +
           '    ],\n' +
           '    function(declare, Employee, Button, lang, Deferred) {\n' +
           '        return declare(Employee, {\n' +
           '            constructor: function() {\n' +
           '                new Button({\n' +
           '                    onClick: lang.hitch(this, function() {\n' +
           '                        new Deferred().then(lang.hitch(this, function() {\n' +
           '                            this.salary * 0.25;\n' +
           '                        }));\n' +
           '                    })\n' +
           '                });\n' +
           '            }\n' +
           '        });\n' +
           '    });');
        // END tests for issue 281

        // START tests for issue 459
        bt( '(function() {\n' +
            '    return {\n' +
            '        foo: function() {\n' +
            '            return "bar";\n' +
            '        },\n' +
            '        bar: ["bar"]\n' +
            '    };\n' +
            '}());');
        // END tests for issue 459

        bt('var a=1,b={bang:2},c=3;',
            'var a = 1,\n    b = {\n        bang: 2\n    },\n    c = 3;');
        bt('var a={bing:1},b=2,c=3;',
            'var a = {\n        bing: 1\n    },\n    b = 2,\n    c = 3;');
        Urlencoded.run_tests(sanitytest);

        bth('');
        bth('<div></div>');
        bth('<div>content</div>');
        bth('<div><div></div></div>',
            '<div>\n' +
            '    <div></div>\n' +
            '</div>');
        bth('<div><div>content</div></div>',
            '<div>\n' +
            '    <div>content</div>\n' +
            '</div>');
        bth('<div>\n' +
            '    <span>content</span>\n' +
            '</div>');
        bth('<div>\n' +
            '</div>');
        bth('<div>\n' +
            '    content\n' +
            '</div>');
        bth('<div>\n' +
            '    </div>',
            '<div>\n' +
            '</div>');
        bth('    <div>\n' +
            '    </div>',
            '<div>\n' +
            '</div>');
        bth('    <div>\n' +
            '</div>',
            '<div>\n' +
            '</div>');
        bth('<div        >content</div>',
            '<div>content</div>');
        bth('<div     thinger="preserve  space  here"   ></div  >',
            '<div thinger="preserve  space  here"></div>');
        bth('content\n' +
            '    <div>\n' +
            '    </div>\n' +
            'content',
            'content\n' +
            '<div>\n' +
            '</div>\n' +
            'content');
        bth('<li>\n' +
            '    <div>\n' +
            '    </div>\n' +
            '</li>');
        bth('<li>\n' +
            '<div>\n' +
            '</div>\n' +
            '</li>',
            '<li>\n' +
            '    <div>\n' +
            '    </div>\n' +
            '</li>');
        bth('<li>\n' +
            '    content\n' +
            '</li>\n' +
            '<li>\n' +
            '    content\n' +
            '</li>');
    
    // START tests for issue 453
    bth('<script type="text/unknown"><div></div></script>',
      '<script type="text/unknown">\n' +
      '    <div></div>\n' +
      '</script>');
    bth('<script type="text/javascript"><div></div></script>',
      '<script type="text/javascript">\n' +
      '    < div > < /div>\n' +
      '</script>');
    bth('<script><div></div></script>',
      '<script>\n' +
      '    < div > < /div>\n' +
      '</script>');
    bth('<script type="text/javascript">var foo = "bar";</script>',
      '<script type="text/javascript">\n' +
      '    var foo = "bar";\n' +
      '</script>');     
    bth('<script type="application/javascript">var foo = "bar";</script>',
      '<script type="application/javascript">\n' +
      '    var foo = "bar";\n' +
      '</script>');
    bth('<script type="application/javascript">var foo = "bar";</script>',
      '<script type="application/javascript">\n' +
      '    var foo = "bar";\n' +
      '</script>');
    bth('<script type="application/x-javascript">var foo = "bar";</script>',
      '<script type="application/x-javascript">\n' +
      '    var foo = "bar";\n' +
      '</script>');
    bth('<script type="application/ecmascript">var foo = "bar";</script>',
      '<script type="application/ecmascript">\n' +
      '    var foo = "bar";\n' +
      '</script>');
    bth('<script type="text/javascript1.5">var foo = "bar";</script>',
      '<script type="text/javascript1.5">\n' +
      '    var foo = "bar";\n' +
      '</script>');
    bth('<script>var foo = "bar";</script>',
      '<script>\n' +
      '    var foo = "bar";\n' +
      '</script>');         
          
    bth('<style type="text/unknown"><tag></tag></style>',
      '<style type="text/unknown">\n' +
      '    <tag></tag>\n' +
      '</style>');  
    bth('<style type="text/css"><tag></tag></style>',
      '<style type="text/css">\n' +
      '    <tag></tag>\n' +
      '</style>');    
    bth('<style><tag></tag></style>',
      '<style>\n' +
      '    <tag></tag>\n' +
      '</style>');        
    bth('<style type="text/css">.selector {font-size:12px;}</style>',
      '<style type="text/css">\n' +
      '    .selector {\n' +
      '        font-size: 12px;\n' +
      '    }\n'+
      '</style>');
    bth('<style>.selector {font-size:12px;}</style>',
      '<style>\n' +
      '    .selector {\n' +
      '        font-size: 12px;\n' +
      '    }\n'+
      '</style>');      
    // END tests for issue 453
      
        // Tests that don't pass, but probably should.
        // bth('<div><span>content</span></div>');

        // Handlebars tests
        // Without the indent option on, handlebars are treated as content.
        opts.indent_handlebars = false;
        bth('{{#if 0}}\n' +
            '    <div>\n' +
            '    </div>\n' +
            '{{/if}}',
            '{{#if 0}}\n' +
            '<div>\n' +
            '</div>\n' +
            '{{/if}}');
        bth('<div>\n' +
            '{{#each thing}}\n' +
            '    {{name}}\n' +
            '{{/each}}\n' +
            '</div>',
            '<div>\n' +
            '    {{#each thing}} {{name}} {{/each}}\n' +
            '</div>');

        opts.indent_handlebars = true;
        bth('{{#if 0}}{{/if}}');
        bth('{{#if 0}}content{{/if}}');
        bth('{{#if 0}}\n' +
            '{{/if}}');
        bth('{{#if     words}}{{/if}}',
            '{{#if words}}{{/if}}');
        bth('{{#if     words}}content{{/if}}',
            '{{#if words}}content{{/if}}');
        bth('{{#if     words}}content{{/if}}',
            '{{#if words}}content{{/if}}');
        bth('{{#if 1}}\n' +
            '    <div>\n' +
            '    </div>\n' +
            '{{/if}}');
        bth('{{#if 1}}\n' +
            '<div>\n' +
            '</div>\n' +
            '{{/if}}',
            '{{#if 1}}\n' +
            '    <div>\n' +
            '    </div>\n' +
            '{{/if}}');
        bth('<div>\n' +
            '    {{#if 1}}\n' +
            '    {{/if}}\n' +
            '</div>');
        bth('<div>\n' +
            '{{#if 1}}\n' +
            '{{/if}}\n' +
            '</div>',
            '<div>\n' +
            '    {{#if 1}}\n' +
            '    {{/if}}\n' +
            '</div>');
        bth('{{#if}}\n' +
            '{{#each}}\n' +
            '{{#if}}\n' +
            'content\n' +
            '{{/if}}\n' +
            '{{#if}}\n' +
            'content\n' +
            '{{/if}}\n' +
            '{{/each}}\n' +
            '{{/if}}',
            '{{#if}}\n' +
            '    {{#each}}\n' +
            '        {{#if}}\n' +
            '            content\n' +
            '        {{/if}}\n' +
            '        {{#if}}\n' +
            '            content\n' +
            '        {{/if}}\n' +
            '    {{/each}}\n' +
            '{{/if}}');
        bth('{{#if 1}}\n' +
            '    <div>\n' +
            '    </div>\n' +
            '{{/if}}');

        // Test {{else}} aligned with {{#if}} and {{/if}}
        bth('{{#if 1}}\n' +
            '    content\n' +
            '    {{else}}\n' +
            '    content\n' +
            '{{/if}}',
            '{{#if 1}}\n' +
            '    content\n' +
            '{{else}}\n' +
            '    content\n' +
            '{{/if}}');
        bth('{{#if 1}}\n' +
            '    {{else}}\n' +
            '    {{/if}}',
            '{{#if 1}}\n' +
            '{{else}}\n' +
            '{{/if}}');
        bth('{{#if thing}}\n' +
            '{{#if otherthing}}\n' +
            '    content\n' +
            '    {{else}}\n' +
            'content\n' +
            '    {{/if}}\n' +
            '       {{else}}\n'+
            'content\n' +
            '{{/if}}',
            '{{#if thing}}\n' +
            '    {{#if otherthing}}\n' +
            '        content\n' +
            '    {{else}}\n' +
            '        content\n' +
            '    {{/if}}\n' +
            '{{else}}\n'+
            '    content\n' +
            '{{/if}}');

        // Test {{}} inside of <> tags, which should be separated by spaces
        // for readability, unless they are inside a string.
        bth('<div{{somestyle}}></div>',
            '<div {{somestyle}}></div>');
        bth('<div{{#if test}}class="foo"{{/if}}>content</div>',
            '<div {{#if test}} class="foo" {{/if}}>content</div>');
        bth('<div{{#if thing}}{{somestyle}}class="{{class}}"{{else}}class="{{class2}}"{{/if}}>content</div>',
            '<div {{#if thing}} {{somestyle}} class="{{class}}" {{else}} class="{{class2}}" {{/if}}>content</div>');
        bth('<span{{#if condition}}class="foo"{{/if}}>content</span>',
            '<span {{#if condition}} class="foo" {{/if}}>content</span>');
        bth('<div unformatted="{{#if}}content{{/if}}">content</div>');
        bth('<div unformatted="{{#if  }}    content{{/if}}">content</div>');

        // Quotes found inside of Handlebars expressions inside of quoted
        // strings themselves should not be considered string delimiters.
        bth('<div class="{{#if thingIs "value"}}content{{/if}}"></div>');
        bth('<div class="{{#if thingIs \'value\'}}content{{/if}}"></div>');
        bth('<div class=\'{{#if thingIs "value"}}content{{/if}}\'></div>');
        bth('<div class=\'{{#if thingIs \'value\'}}content{{/if}}\'></div>');

        opts.wrap_line_length = 0;
        //...---------1---------2---------3---------4---------5---------6---------7
        //...1234567890123456789012345678901234567890123456789012345678901234567890
        bth('<div>Some text that should not wrap at all.</div>',
            /* expected */
            '<div>Some text that should not wrap at all.</div>');

        // A value of 0 means no max line length, and should not wrap.
        //...---------1---------2---------3---------4---------5---------6---------7---------8---------9--------10--------11--------12--------13--------14--------15--------16--------17--------18--------19--------20--------21--------22--------23--------24--------25--------26--------27--------28--------29
        //...12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
        bth('<div>Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all.</div>',
            /* expected */
            '<div>Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all.</div>');

        opts.wrap_line_length = "0";
        //...---------1---------2---------3---------4---------5---------6---------7
        //...1234567890123456789012345678901234567890123456789012345678901234567890
        bth('<div>Some text that should not wrap at all.</div>',
            /* expected */
            '<div>Some text that should not wrap at all.</div>');

        // A value of "0" means no max line length, and should not wrap
        //...---------1---------2---------3---------4---------5---------6---------7---------8---------9--------10--------11--------12--------13--------14--------15--------16--------17--------18--------19--------20--------21--------22--------23--------24--------25--------26--------27--------28--------29
        //...12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
        bth('<div>Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all.</div>',
            /* expected */
            '<div>Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all.</div>');

        //BUGBUG: This should wrap before 40 not after.
        opts.wrap_line_length = 40;
        //...---------1---------2---------3---------4---------5---------6---------7
        //...1234567890123456789012345678901234567890123456789012345678901234567890
        bth('<div>Some test text that should wrap_inside_this section here.</div>',
            /* expected */
            '<div>Some test text that should wrap_inside_this\n' +
            '    section here.</div>');

        opts.wrap_line_length = "40";
        //...---------1---------2---------3---------4---------5---------6---------7
        //...1234567890123456789012345678901234567890123456789012345678901234567890
        bth('<div>Some test text that should wrap_inside_this section here.</div>',
            /* expected */
            '<div>Some test text that should wrap_inside_this\n' +
            '    section here.</div>');

        opts.indent_size = 1;
        opts.indent_char = '\t';
        opts.preserve_newlines = false;
        bth('<div>\n\tfoo\n</div>', '<div>foo</div>');

        opts.preserve_newlines = true;
        bth('<div>\n\tfoo\n</div>');



        // test preserve_newlines and max_preserve_newlines
        opts.preserve_newlines = false;
        test_fragment('<div>Should not</div>\n\n\n' +
                      '<div>preserve newlines</div>',
                      '<div>Should not</div>\n' +
                      '<div>preserve newlines</div>');

        opts.preserve_newlines = true;
        opts.max_preserve_newlines  = 0;
        test_fragment('<div>Should</div>\n\n\n' +
                      '<div>preserve zero newlines</div>',
                      '<div>Should</div>\n' +
                      '<div>preserve zero newlines</div>');

        opts.max_preserve_newlines  = 1;
        test_fragment('<div>Should</div>\n\n\n' +
                      '<div>preserve one newline</div>',
                      '<div>Should</div>\n\n' +
                      '<div>preserve one newline</div>');

        opts.max_preserve_newlines  = null;
        test_fragment('<div>Should</div>\n\n\n' +
                      '<div>preserve one newline</div>',
                      '<div>Should</div>\n\n\n' +
                      '<div>preserve one newline</div>');
        // css beautifier
        opts.indent_size = 1;
        opts.indent_char = '\t';
        opts.selector_separator_newline = true;
        opts.end_with_newline = true;

        // test basic css beautifier
        btc('', '\n');
        btc(".tabs{}", ".tabs {}\n");
        btc(".tabs{color:red;}", ".tabs {\n\tcolor: red;\n}\n");
        btc(".tabs{color:rgb(255, 255, 0)}", ".tabs {\n\tcolor: rgb(255, 255, 0)\n}\n");
        btc(".tabs{background:url('back.jpg')}", ".tabs {\n\tbackground: url('back.jpg')\n}\n");
        btc("#bla, #foo{color:red}", "#bla,\n#foo {\n\tcolor: red\n}\n");
        btc("@media print {.tab{}}", "@media print {\n\t.tab {}\n}\n");
        btc("@media print {.tab{background-image:url([email protected])}}", "@media print {\n\t.tab {\n\t\tbackground-image: url([email protected])\n\t}\n}\n");

        // comments
        btc("/* test */", "/* test */\n");
        btc(".tabs{/* test */}", ".tabs {\n\t/* test */\n}\n");
        btc("/* header */.tabs {}", "/* header */\n\n.tabs {}\n");

        //single line comment support (less/sass)
        btc(".tabs{\n// comment\nwidth:10px;\n}", ".tabs {\n\t// comment\n\twidth: 10px;\n}\n");
        btc(".tabs{// comment\nwidth:10px;\n}", ".tabs {\n\t// comment\n\twidth: 10px;\n}\n");
        btc("//comment\n.tabs{width:10px;}", "//comment\n.tabs {\n\twidth: 10px;\n}\n");
        btc(".tabs{//comment\n//2nd single line comment\nwidth:10px;}", ".tabs {\n\t//comment\n\t//2nd single line comment\n\twidth: 10px;\n}\n");
        btc(".tabs{width:10px;//end of line comment\n}", ".tabs {\n\twidth: 10px;//end of line comment\n}\n");
        btc(".tabs{width:10px;//end of line comment\nheight:10px;}", ".tabs {\n\twidth: 10px;//end of line comment\n\theight: 10px;\n}\n");
        btc(".tabs{width:10px;//end of line comment\nheight:10px;//another\n}", ".tabs {\n\twidth: 10px;//end of line comment\n\theight: 10px;//another\n}\n");

        // separate selectors
        btc("#bla, #foo{color:red}", "#bla,\n#foo {\n\tcolor: red\n}\n");
        btc("a, img {padding: 0.2px}", "a,\nimg {\n\tpadding: 0.2px\n}\n");

        // block nesting
        btc("#foo {\n\tbackground-image: url([email protected]);\n\t@font-face {\n\t\tfont-family: 'Bitstream Vera Serif Bold';\n\t\tsrc: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');\n\t}\n}\n");
        btc("@media screen {\n\t#foo:hover {\n\t\tbackground-image: url([email protected]);\n\t}\n\t@font-face {\n\t\tfont-family: 'Bitstream Vera Serif Bold';\n\t\tsrc: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');\n\t}\n}\n");
/*
@font-face {
    font-family: 'Bitstream Vera Serif Bold';
    src: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');
}
@media screen {
    #foo:hover {
        background-image: url(foo.png);
    }
    @media screen and (min-device-pixel-ratio: 2) {
        @font-face {
            font-family: 'Helvetica Neue'
        }
        #foo:hover {
            background-image: url([email protected]);
        }
    }
}
*/
        btc("@font-face {\n\tfont-family: 'Bitstream Vera Serif Bold';\n\tsrc: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');\n}\n@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo.png);\n\t}\n\t@media screen and (min-device-pixel-ratio: 2) {\n\t\t@font-face {\n\t\t\tfont-family: 'Helvetica Neue'\n\t\t}\n\t\t#foo:hover {\n\t\t\tbackground-image: url([email protected]);\n\t\t}\n\t}\n}\n");

        // test options
        opts.indent_size = 2;
        opts.indent_char = ' ';
        opts.selector_separator_newline = false;

        btc("#bla, #foo{color:green}", "#bla, #foo {\n  color: green\n}\n");
        btc("@media print {.tab{}}", "@media print {\n  .tab {}\n}\n");
        btc("@media print {.tab,.bat{}}", "@media print {\n  .tab, .bat {}\n}\n");
        btc("#bla, #foo{color:black}", "#bla, #foo {\n  color: black\n}\n");

        // pseudo-classes and pseudo-elements
        btc("#foo:hover {\n  background-image: url([email protected])\n}\n");
        btc("#foo *:hover {\n  color: purple\n}\n");
        btc("::selection {\n  color: #ff0000;\n}\n");

        // TODO: don't break nested pseduo-classes
        btc("@media screen {.tab,.bat:hover {color:red}}", "@media screen {\n  .tab, .bat:hover {\n    color: red\n  }\n}\n");

        // particular edge case with braces and semicolons inside tags that allows custom text
        btc("a:not(\"foobar\\\";{}omg\"){\ncontent: 'example\\';{} text';\ncontent: \"example\\\";{} text\";}",
            "a:not(\"foobar\\\";{}omg\") {\n  content: 'example\\';{} text';\n  content: \"example\\\";{} text\";\n}\n");

        btc('html.js [data-custom="123"] {\n  opacity: 1.00;\n}\n'); // may not eat the space before "["
        btc('html.js *[data-custom="123"] {\n  opacity: 1.00;\n}\n');

        return sanitytest;
    }

    return beautifier_tests();
}

if (typeof exports !== "undefined") {
    exports.run_beautifier_tests = run_beautifier_tests;
}
PK
!<�D{�#�#/chrome/devtools/modules/devtools/shared/l10n.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 parsePropertiesFile = require("./node-properties/node-properties");
const { sprintf } = require("./sprintfjs/sprintf");

const propertiesMap = {};

// We need some special treatment here for webpack.
//
// Webpack doesn't always handle dynamic requires in the best way.  In
// particular if it sees an unrestricted dynamic require, it will try
// to put all the files it can find into the generated pack.  (It can
// also try a bit to parse the expression passed to require, but in
// our case this doesn't work, because our call below doesn't provide
// enough information.)
//
// Webpack also provides a way around this: require.context.  The idea
// here is to tell webpack some constraints so that it can include
// fewer files in the pack.
//
// Here we introduce new require contexts for each possible locale
// directory.  Then we use the correct context to load the property
// file.  In the webpack case this results in just the locale property
// files being included in the pack; and in the devtools case this is
// a wordy no-op.
const reqShared = require.context("raw!devtools/shared/locales/",
                                  true, /^.*\.properties$/);
const reqClient = require.context("raw!devtools/client/locales/",
                                  true, /^.*\.properties$/);
const reqShim = require.context("raw!devtools/shim/locales/",
                                  true, /^.*\.properties$/);
const reqGlobal = require.context("raw!toolkit/locales/",
                                  true, /^.*\.properties$/);

// Map used to memoize Number formatters.
const numberFormatters = new Map();
const getNumberFormatter = function (decimals) {
  let formatter = numberFormatters.get(decimals);
  if (!formatter) {
    // Create and memoize a formatter for the provided decimals
    formatter = Intl.NumberFormat(undefined, {
      maximumFractionDigits: decimals,
      minimumFractionDigits: decimals
    });
    numberFormatters.set(decimals, formatter);
  }

  return formatter;
};

/**
 * Memoized getter for properties files that ensures a given url is only required and
 * parsed once.
 *
 * @param {String} url
 *        The URL of the properties file to parse.
 * @return {Object} parsed properties mapped in an object.
 */
function getProperties(url) {
  if (!propertiesMap[url]) {
    // See the comment above about webpack and require contexts.  Here
    // we take an input like "devtools/shared/locales/debugger.properties"
    // and decide which context require function to use.  Despite the
    // string processing here, in the end a string identical to |url|
    // ends up being passed to "require".
    let index = url.lastIndexOf("/");
    // Turn "mumble/locales/resource.properties" => "./resource.properties".
    let baseName = "." + url.substr(index);
    let reqFn;
    if (/^toolkit/.test(url)) {
      reqFn = reqGlobal;
    } else if (/^devtools\/shared/.test(url)) {
      reqFn = reqShared;
    } else if (/^devtools\/shim/.test(url)) {
      reqFn = reqShim;
    } else {
      reqFn = reqClient;
    }
    propertiesMap[url] = parsePropertiesFile(reqFn(baseName));
  }

  return propertiesMap[url];
}

/**
 * Localization convenience methods.
 *
 * @param string stringBundleName
 *        The desired string bundle's name.
 */
function LocalizationHelper(stringBundleName) {
  this.stringBundleName = stringBundleName;
}

LocalizationHelper.prototype = {
  /**
   * L10N shortcut function.
   *
   * @param string name
   * @return string
   */
  getStr: function (name) {
    let properties = getProperties(this.stringBundleName);
    if (name in properties) {
      return properties[name];
    }

    throw new Error("No localization found for [" + name + "]");
  },

  /**
   * L10N shortcut function.
   *
   * @param string name
   * @param array args
   * @return string
   */
  getFormatStr: function (name, ...args) {
    return sprintf(this.getStr(name), ...args);
  },

  /**
   * L10N shortcut function for numeric arguments that need to be formatted.
   * All numeric arguments will be fixed to 2 decimals and given a localized
   * decimal separator. Other arguments will be left alone.
   *
   * @param string name
   * @param array args
   * @return string
   */
  getFormatStrWithNumbers: function (name, ...args) {
    let newArgs = args.map(x => {
      return typeof x == "number" ? this.numberWithDecimals(x, 2) : x;
    });

    return this.getFormatStr(name, ...newArgs);
  },

  /**
   * Converts a number to a locale-aware string format and keeps a certain
   * number of decimals.
   *
   * @param number number
   *        The number to convert.
   * @param number decimals [optional]
   *        Total decimals to keep.
   * @return string
   *         The localized number as a string.
   */
  numberWithDecimals: function (number, decimals = 0) {
    // Do not show decimals for integers.
    if (number === (number|0)) {
      return getNumberFormatter(0).format(number);
    }

    // If this isn't a number (and yes, `isNaN(null)` is false), return zero.
    if (isNaN(number) || number === null) {
      return getNumberFormatter(0).format(0);
    }

    // Localize the number using a memoized Intl.NumberFormat formatter.
    let localized = getNumberFormatter(decimals).format(number);

    // Convert the localized number to a number again.
    let localizedNumber = localized * 1;
    // Check if this number is now equal to an integer.
    if (localizedNumber === (localizedNumber|0)) {
    // If it is, remove the fraction part.
      return getNumberFormatter(0).format(localizedNumber);
    }

    return localized;
  }
};

function getPropertiesForNode(node) {
  let bundleEl = node.closest("[data-localization-bundle]");
  if (!bundleEl) {
    return null;
  }

  let propertiesUrl = bundleEl.getAttribute("data-localization-bundle");
  return getProperties(propertiesUrl);
}

/**
 * Translate existing markup annotated with data-localization attributes.
 *
 * How to use data-localization in markup:
 *
 *   <div data-localization="content=myContent;title=myTitle"/>
 *
 * The data-localization attribute identifies an element as being localizable.
 * The content of the attribute is semi-colon separated list of descriptors.
 * - "title=myTitle" means the "title" attribute should be replaced with the localized
 *   string corresponding to the key "myTitle".
 * - "content=myContent" means the text content of the node should be replaced by the
 *   string corresponding to "myContent"
 *
 * How to define the localization bundle in markup:
 *
 *   <div data-localization-bundle="url/to/my.properties">
 *     [...]
 *       <div data-localization="content=myContent;title=myTitle"/>
 *
 * Set the data-localization-bundle on an ancestor of the nodes that should be localized.
 *
 * @param {Element} root
 *        The root node to use for the localization
 */
function localizeMarkup(root) {
  let elements = root.querySelectorAll("[data-localization]");
  for (let element of elements) {
    let properties = getPropertiesForNode(element);
    if (!properties) {
      continue;
    }

    let attributes = element.getAttribute("data-localization").split(";");
    for (let attribute of attributes) {
      let [name, value] = attribute.trim().split("=");
      if (name === "content") {
        element.textContent = properties[value];
      } else {
        element.setAttribute(name, properties[value]);
      }
    }

    element.removeAttribute("data-localization");
  }
}

const sharedL10N = new LocalizationHelper("devtools/shared/locales/shared.properties");

/**
 * A helper for having the same interface as LocalizationHelper, but for more
 * than one file. Useful for abstracting l10n string locations.
 */
function MultiLocalizationHelper(...stringBundleNames) {
  let instances = stringBundleNames.map(bundle => {
    return new LocalizationHelper(bundle);
  });

  // Get all function members of the LocalizationHelper class, making sure we're
  // not executing any potential getters while doing so, and wrap all the
  // methods we've found to work on all given string bundles.
  Object.getOwnPropertyNames(LocalizationHelper.prototype)
    .map(name => ({
      name: name,
      descriptor: Object.getOwnPropertyDescriptor(LocalizationHelper.prototype,
                                                  name)
    }))
    .filter(({ descriptor }) => descriptor.value instanceof Function)
    .forEach(method => {
      this[method.name] = (...args) => {
        for (let l10n of instances) {
          try {
            return method.descriptor.value.apply(l10n, args);
          } catch (e) {
            // Do nothing
          }
        }
        return null;
      };
    });
}

exports.LocalizationHelper = LocalizationHelper;
exports.localizeMarkup = localizeMarkup;
exports.MultiLocalizationHelper = MultiLocalizationHelper;
Object.defineProperty(exports, "ELLIPSIS", { get: () => sharedL10N.getStr("ellipsis") });
PK
!<�j�##?chrome/devtools/modules/devtools/shared/layout/dom-matrix-2d.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

/**
 * Returns a matrix for the scaling given.
 * Calling `scale()` or `scale(1) returns a new identity matrix.
 *
 * @param {Number} [sx = 1]
 *        the abscissa of the scaling vector.
 *        If unspecified, it will equal to `1`.
 * @param {Number} [sy = sx]
 *        The ordinate of the scaling vector.
 *        If not present, its default value is `sx`, leading to a uniform scaling.
 * @return {Array}
 *         The new matrix.
 */
const scale = (sx = 1, sy = sx) => [
  sx, 0, 0,
  0, sy, 0,
  0, 0, 1
];
exports.scale = scale;

/**
 * Returns a matrix for the translation given.
 * Calling `translate()` or `translate(0) returns a new identity matrix.
 *
 * @param {Number} [tx = 0]
 *        The abscissa of the translating vector.
 *        If unspecified, it will equal to `0`.
 * @param {Number} [ty = tx]
 *        The ordinate of the translating vector.
 *        If unspecified, it will equal to `tx`.
 * @return {Array}
 *         The new matrix.
 */
const translate = (tx = 0, ty = tx) => [
  1, 0, tx,
  0, 1, ty,
  0, 0, 1
];
exports.translate = translate;

/**
 * Returns a matrix that reflects about the Y axis.  For example, the point (x1, y1) would
 * become (-x1, y1).
 *
 * @return {Array}
 *         The new matrix.
 */
const reflectAboutY = () => [
  -1, 0, 0,
  0,  1, 0,
  0,  0, 1,
];
exports.reflectAboutY = reflectAboutY;

/**
 * Returns a matrix for the rotation given.
 * Calling `rotate()` or `rotate(0)` returns a new identity matrix.
 *
 * @param {Number} [angle = 0]
 *        The angle, in radians, for which to return a corresponding rotation matrix.
 *        If unspecified, it will equal `0`.
 * @return {Array}
 *         The new matrix.
 */
const rotate = (angle = 0) => {
  let cos = Math.cos(angle);
  let sin = Math.sin(angle);

  return [
    cos,  sin, 0,
    -sin, cos, 0,
    0,    0,   1
  ];
};
exports.rotate = rotate;

/**
 * Returns a new identity matrix.
 *
 * @return {Array}
 *         The new matrix.
 */
const identity = () => [
  1, 0, 0,
  0, 1, 0,
  0, 0, 1
];
exports.identity = identity;

/**
 * Multiplies two matrices and returns a new matrix with the result.
 *
 * @param {Array} M1
 *        The first operand.
 * @param {Array} M2
 *        The second operand.
 * @return {Array}
 *        The resulting matrix.
 */
const multiply = (M1, M2) => {
  let c11 = M1[0] * M2[0] + M1[1] * M2[3] + M1[2] * M2[6];
  let c12 = M1[0] * M2[1] + M1[1] * M2[4] + M1[2] * M2[7];
  let c13 = M1[0] * M2[2] + M1[1] * M2[5] + M1[2] * M2[8];

  let c21 = M1[3] * M2[0] + M1[4] * M2[3] + M1[5] * M2[6];
  let c22 = M1[3] * M2[1] + M1[4] * M2[4] + M1[5] * M2[7];
  let c23 = M1[3] * M2[2] + M1[4] * M2[5] + M1[5] * M2[8];

  let c31 = M1[6] * M2[0] + M1[7] * M2[3] + M1[8] * M2[6];
  let c32 = M1[6] * M2[1] + M1[7] * M2[4] + M1[8] * M2[7];
  let c33 = M1[6] * M2[2] + M1[7] * M2[5] + M1[8] * M2[8];

  return [
    c11, c12, c13,
    c21, c22, c23,
    c31, c32, c33
  ];
};
exports.multiply = multiply;

/**
 * Applies the given matrix to a point.
 *
 * @param {Array} M
 *        The matrix to apply.
 * @param {Array} P
 *        The point's vector.
 * @return {Array}
 *        The resulting point's vector.
 */
const apply = (M, P) => [
  M[0] * P[0] + M[1] * P[1] + M[2],
  M[3] * P[0] + M[4] * P[1] + M[5],
];
exports.apply = apply;

/**
 * Returns `true` if the given matrix is a identity matrix.
 *
 * @param {Array} M
 *        The matrix to check
 * @return {Boolean}
 *        `true` if the matrix passed is a identity matrix, `false` otherwise.
 */
const isIdentity = (M) =>
  M[0] === 1 && M[1] === 0 && M[2] === 0 &&
  M[3] === 0 && M[4] === 1 && M[5] === 0 &&
  M[6] === 0 && M[7] === 0 && M[8] === 1;
exports.isIdentity = isIdentity;

/**
 * Get the change of basis matrix and inverted change of basis matrix
 * for the coordinate system based on the two given vectors, as well as
 * the lengths of the two given vectors.
 *
 * @param {Array} u
 *        The first vector, serving as the "x axis" of the coordinate system.
 * @param {Array} v
 *        The second vector, serving as the "y axis" of the coordinate system.
 * @return {Object}
 *        { basis, invertedBasis, uLength, vLength }
 *        basis and invertedBasis are the change of basis matrices. uLength and
 *        vLength are the lengths of u and v.
 */
const getBasis = (u, v) => {
  let uLength = Math.abs(Math.sqrt(u[0] ** 2 + u[1] ** 2));
  let vLength = Math.abs(Math.sqrt(v[0] ** 2 + v[1] ** 2));
  let basis =
    [ u[0] / uLength, v[0] / vLength, 0,
      u[1] / uLength, v[1] / vLength, 0,
      0,                0,                1 ];
  let determinant = 1 / (basis[0] * basis[4] - basis[1] * basis[3]);
  let invertedBasis =
    [ basis[4] / determinant,  -basis[1] / determinant, 0,
      -basis[3] / determinant,  basis[0] / determinant, 0,
      0,                        0,                      1 ];
  return { basis, invertedBasis, uLength, vLength };
};
exports.getBasis = getBasis;

/**
 * Convert the given matrix to a new coordinate system, based on the change of basis
 * matrix.
 *
 * @param {Array} M
 *        The matrix to convert
 * @param {Array} basis
 *        The change of basis matrix
 * @param {Array} invertedBasis
 *        The inverted change of basis matrix
 * @return {Array}
 *        The converted matrix.
 */
const changeMatrixBase = (M, basis, invertedBasis) => {
  return multiply(invertedBasis, multiply(M, basis));
};
exports.changeMatrixBase = changeMatrixBase;

/**
 * Returns the transformation matrix for the given node, relative to the ancestor passed
 * as second argument; considering the ancestor transformation too.
 * If no ancestor is specified, it will returns the transformation matrix relative to the
 * node's parent element.
 *
 * @param {DOMNode} node
 *        The node.
 * @param {DOMNode} ancestor
 *        The ancestor of the node given.
 * @return {Array}
 *        The transformation matrix.
 */
function getNodeTransformationMatrix(node, ancestor = node.parentElement) {
  let { a, b, c, d, e, f } = ancestor.getTransformToParent()
                                     .multiply(node.getTransformToAncestor(ancestor));

  return [
    a, c, e,
    b, d, f,
    0, 0, 1
  ];
}
exports.getNodeTransformationMatrix = getNodeTransformationMatrix;

/**
 * Returns the matrix to rotate, translate, and reflect (if needed) from the element's
 * top-left origin into the actual writing mode and text direction applied to the element.
 *
 * @param  {Object} size
 *         An element's untransformed `width` and `height`.
 * @param  {Object} style
 *         The computed `writingMode` and `direction` properties for the element.
 * @return {Array}
 *         The matrix with adjustments for writing mode and text direction, if any.
 */
function getWritingModeMatrix(size, style) {
  let currentMatrix = identity();
  let { width, height } = size;
  let { direction, writingMode } = style;

  switch (writingMode) {
    case "horizontal-tb":
      // This is the initial value. No further adjustment needed.
      break;
    case "vertical-rl":
      currentMatrix = multiply(
        translate(width, 0),
        rotate(-Math.PI / 2)
      );
      break;
    case "vertical-lr":
      currentMatrix = multiply(
        reflectAboutY(),
        rotate(-Math.PI / 2)
      );
      break;
    case "sideways-rl":
      currentMatrix = multiply(
        translate(width, 0),
        rotate(-Math.PI / 2)
      );
      break;
    case "sideways-lr":
      currentMatrix = multiply(
        rotate(Math.PI / 2),
        translate(-height, 0)
      );
      break;
    default:
      console.error(`Unexpected writing-mode: ${writingMode}`);
  }

  switch (direction) {
    case "ltr":
      // This is the initial value. No further adjustment needed.
      break;
    case "rtl":
      let rowLength = width;
      if (writingMode != "horizontal-tb") {
        rowLength = height;
      }
      currentMatrix = multiply(currentMatrix, translate(rowLength, 0));
      currentMatrix = multiply(currentMatrix, reflectAboutY());
      break;
    default:
      console.error(`Unexpected direction: ${direction}`);
  }

  return currentMatrix;
}
exports.getWritingModeMatrix = getWritingModeMatrix;

/**
 * Convert from the matrix format used in this module:
 *   a, c, e,
 *   b, d, f,
 *   0, 0, 1
 * to the format used by the `matrix()` CSS transform function:
 *   a, b, c, d, e, f
 *
 * @param  {Array} M
 *         The matrix in this module's 9 element format.
 * @return {String}
 *         The matching 6 element CSS transform function.
 */
function getCSSMatrixTransform(M) {
  let [
    a, c, e,
    b, d, f,
  ] = M;
  return `matrix(${a}, ${b}, ${c}, ${d}, ${e}, ${f})`;
}
exports.getCSSMatrixTransform = getCSSMatrixTransform;
PK
!<��r�P�P7chrome/devtools/modules/devtools/shared/layout/utils.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Ci, Cc } = require("chrome");
const nodeFilterConstants = require("devtools/shared/dom-node-filter-constants");

const SHEET_TYPE = {
  "agent": "AGENT_SHEET",
  "user": "USER_SHEET",
  "author": "AUTHOR_SHEET"
};

loader.lazyRequireGetter(this, "setIgnoreLayoutChanges", "devtools/server/actors/reflow", true);
exports.setIgnoreLayoutChanges = (...args) =>
  this.setIgnoreLayoutChanges(...args);

/**
 * Returns the `DOMWindowUtils` for the window given.
 *
 * @param {DOMWindow} win
 * @returns {DOMWindowUtils}
 */
const utilsCache = new WeakMap();
function utilsFor(win) {
  if (!utilsCache.has(win)) {
    utilsCache.set(win, win.QueryInterface(Ci.nsIInterfaceRequestor)
                           .getInterface(Ci.nsIDOMWindowUtils));
  }
  return utilsCache.get(win);
}

/**
 * like win.top, but goes through mozbrowsers and mozapps iframes.
 *
 * @param {DOMWindow} win
 * @return {DOMWindow}
 */
function getTopWindow(win) {
  let docShell = win.QueryInterface(Ci.nsIInterfaceRequestor)
                    .getInterface(Ci.nsIWebNavigation)
                    .QueryInterface(Ci.nsIDocShell);

  if (!docShell.isMozBrowser) {
    return win.top;
  }

  let topDocShell =
    docShell.getSameTypeRootTreeItemIgnoreBrowserBoundaries();

  return topDocShell
          ? topDocShell.contentViewer.DOMDocument.defaultView
          : null;
}

exports.getTopWindow = getTopWindow;

/**
 * Returns `true` is the window given is a top level window.
 * like win.top === win, but goes through mozbrowsers and mozapps iframes.
 *
 * @param {DOMWindow} win
 * @return {Boolean}
 */
const isTopWindow = win => win && getTopWindow(win) === win;
exports.isTopWindow = isTopWindow;

/**
   * Check a window is part of the boundary window given.
   *
   * @param {DOMWindow} boundaryWindow
   * @param {DOMWindow} win
   * @return {Boolean}
   */
function isWindowIncluded(boundaryWindow, win) {
  if (win === boundaryWindow) {
    return true;
  }

  let parent = getParentWindow(win);

  if (!parent || parent === win) {
    return false;
  }

  return isWindowIncluded(boundaryWindow, parent);
}
exports.isWindowIncluded = isWindowIncluded;

/**
 * like win.parent, but goes through mozbrowsers and mozapps iframes.
 *
 * @param {DOMWindow} win
 * @return {DOMWindow}
 */
function getParentWindow(win) {
  if (isTopWindow(win)) {
    return null;
  }

  let docShell = win.QueryInterface(Ci.nsIInterfaceRequestor)
                 .getInterface(Ci.nsIWebNavigation)
                 .QueryInterface(Ci.nsIDocShell);

  if (!docShell.isMozBrowser) {
    return win.parent;
  }

  let parentDocShell =
    docShell.getSameTypeParentIgnoreBrowserBoundaries();

  return parentDocShell
          ? parentDocShell.contentViewer.DOMDocument.defaultView
          : null;
}

exports.getParentWindow = getParentWindow;

/**
 * like win.frameElement, but goes through mozbrowsers and mozapps iframes.
 *
 * @param {DOMWindow} win
 *        The window to get the frame for
 * @return {DOMNode}
 *         The element in which the window is embedded.
 */
const getFrameElement = (win) =>
  isTopWindow(win) ? null : utilsFor(win).containerElement;
exports.getFrameElement = getFrameElement;

/**
 * Get the x/y offsets for of all the parent frames of a given node, limited to
 * the boundary window given.
 *
 * @param {DOMWindow} boundaryWindow
 *        The window where to stop to iterate. If `null` is given, the top
 *        window is used.
 * @param {DOMNode} node
 *        The node for which we are to get the offset
 * @return {Array}
 *         The frame offset [x, y]
 */
function getFrameOffsets(boundaryWindow, node) {
  let xOffset = 0;
  let yOffset = 0;

  let frameWin = getWindowFor(node);
  let scale = getCurrentZoom(node);

  if (boundaryWindow === null) {
    boundaryWindow = getTopWindow(frameWin);
  } else if (typeof boundaryWindow === "undefined") {
    throw new Error("No boundaryWindow given. Use null for the default one.");
  }

  while (frameWin !== boundaryWindow) {
    let frameElement = getFrameElement(frameWin);
    if (!frameElement) {
      break;
    }

    // We are in an iframe.
    // We take into account the parent iframe position and its
    // offset (borders and padding).
    let frameRect = frameElement.getBoundingClientRect();

    let [offsetTop, offsetLeft] = getFrameContentOffset(frameElement);

    xOffset += frameRect.left + offsetLeft;
    yOffset += frameRect.top + offsetTop;

    frameWin = getParentWindow(frameWin);
  }

  return [xOffset * scale, yOffset * scale];
}
exports.getFrameOffsets = getFrameOffsets;

/**
 * Get box quads adjusted for iframes and zoom level.
 *
 * @param {DOMWindow} boundaryWindow
 *        The window where to stop to iterate. If `null` is given, the top
 *        window is used.
 * @param {DOMNode} node
 *        The node for which we are to get the box model region
 *        quads.
 * @param {String} region
 *        The box model region to return: "content", "padding", "border" or
 *        "margin".
 * @return {Array}
 *        An array of objects that have the same structure as quads returned by
 *        getBoxQuads. An empty array if the node has no quads or is invalid.
 */
function getAdjustedQuads(boundaryWindow, node, region) {
  if (!node || !node.getBoxQuads) {
    return [];
  }

  let quads = node.getBoxQuads({
    box: region
  });

  if (!quads.length) {
    return [];
  }

  let [xOffset, yOffset] = getFrameOffsets(boundaryWindow, node);
  let scale = getCurrentZoom(node);
  let { scrollX, scrollY } = boundaryWindow;

  xOffset += scrollX * scale;
  yOffset += scrollY * scale;

  let adjustedQuads = [];
  for (let quad of quads) {
    adjustedQuads.push({
      p1: {
        w: quad.p1.w * scale,
        x: quad.p1.x * scale + xOffset,
        y: quad.p1.y * scale + yOffset,
        z: quad.p1.z * scale
      },
      p2: {
        w: quad.p2.w * scale,
        x: quad.p2.x * scale + xOffset,
        y: quad.p2.y * scale + yOffset,
        z: quad.p2.z * scale
      },
      p3: {
        w: quad.p3.w * scale,
        x: quad.p3.x * scale + xOffset,
        y: quad.p3.y * scale + yOffset,
        z: quad.p3.z * scale
      },
      p4: {
        w: quad.p4.w * scale,
        x: quad.p4.x * scale + xOffset,
        y: quad.p4.y * scale + yOffset,
        z: quad.p4.z * scale
      },
      bounds: {
        bottom: quad.bounds.bottom * scale + yOffset,
        height: quad.bounds.height * scale,
        left: quad.bounds.left * scale + xOffset,
        right: quad.bounds.right * scale + xOffset,
        top: quad.bounds.top * scale + yOffset,
        width: quad.bounds.width * scale,
        x: quad.bounds.x * scale + xOffset,
        y: quad.bounds.y * scale + yOffset
      }
    });
  }

  return adjustedQuads;
}
exports.getAdjustedQuads = getAdjustedQuads;

/**
 * Compute the absolute position and the dimensions of a node, relativalely
 * to the root window.

 * @param {DOMWindow} boundaryWindow
 *        The window where to stop to iterate. If `null` is given, the top
 *        window is used.
 * @param {DOMNode} node
 *        a DOM element to get the bounds for
 * @param {DOMWindow} contentWindow
 *        the content window holding the node
 * @return {Object}
 *         A rect object with the {top, left, width, height} properties
 */
function getRect(boundaryWindow, node, contentWindow) {
  let frameWin = node.ownerDocument.defaultView;
  let clientRect = node.getBoundingClientRect();

  if (boundaryWindow === null) {
    boundaryWindow = getTopWindow(frameWin);
  } else if (typeof boundaryWindow === "undefined") {
    throw new Error("No boundaryWindow given. Use null for the default one.");
  }

  // Go up in the tree of frames to determine the correct rectangle.
  // clientRect is read-only, we need to be able to change properties.
  let rect = {
    top: clientRect.top + contentWindow.pageYOffset,
    left: clientRect.left + contentWindow.pageXOffset,
    width: clientRect.width,
    height: clientRect.height
  };

  // We iterate through all the parent windows.
  while (frameWin !== boundaryWindow) {
    let frameElement = getFrameElement(frameWin);
    if (!frameElement) {
      break;
    }

    // We are in an iframe.
    // We take into account the parent iframe position and its
    // offset (borders and padding).
    let frameRect = frameElement.getBoundingClientRect();

    let [offsetTop, offsetLeft] = getFrameContentOffset(frameElement);

    rect.top += frameRect.top + offsetTop;
    rect.left += frameRect.left + offsetLeft;

    frameWin = getParentWindow(frameWin);
  }

  return rect;
}
exports.getRect = getRect;

/**
 * Get the 4 bounding points for a node taking iframes into account.
 * Note that for transformed nodes, this will return the untransformed bound.
 *
 * @param {DOMWindow} boundaryWindow
 *        The window where to stop to iterate. If `null` is given, the top
 *        window is used.
 * @param {DOMNode} node
 * @return {Object}
 *         An object with p1,p2,p3,p4 properties being {x,y} objects
 */
function getNodeBounds(boundaryWindow, node) {
  if (!node) {
    return null;
  }
  let { scrollX, scrollY } = boundaryWindow;
  let scale = getCurrentZoom(node);

  // Find out the offset of the node in its current frame
  let offsetLeft = 0;
  let offsetTop = 0;
  let el = node;
  while (el && el.parentNode) {
    offsetLeft += el.offsetLeft;
    offsetTop += el.offsetTop;
    el = el.offsetParent;
  }

  // Also take scrolled containers into account
  el = node;
  while (el && el.parentNode) {
    if (el.scrollTop) {
      offsetTop -= el.scrollTop;
    }
    if (el.scrollLeft) {
      offsetLeft -= el.scrollLeft;
    }
    el = el.parentNode;
  }

  // And add the potential frame offset if the node is nested
  let [xOffset, yOffset] = getFrameOffsets(boundaryWindow, node);
  xOffset += (offsetLeft + scrollX) * scale;
  yOffset += (offsetTop + scrollY) * scale;

  // Get the width and height
  let width = node.offsetWidth * scale;
  let height = node.offsetHeight * scale;

  return {
    p1: {x: xOffset, y: yOffset},
    p2: {x: xOffset + width, y: yOffset},
    p3: {x: xOffset + width, y: yOffset + height},
    p4: {x: xOffset, y: yOffset + height},
    top: yOffset,
    right: xOffset + width,
    bottom: yOffset + height,
    left: xOffset,
    width,
    height
  };
}
exports.getNodeBounds = getNodeBounds;

/**
 * Same as doing iframe.contentWindow but works with all types of container
 * elements that act like frames (e.g. <embed>), where 'contentWindow' isn't a
 * property that can be accessed.
 * This uses the inIDeepTreeWalker instead.
 * @param {DOMNode} frame
 * @return {Window}
 */
function safelyGetContentWindow(frame) {
  if (frame.contentWindow) {
    return frame.contentWindow;
  }

  let walker = Cc["@mozilla.org/inspector/deep-tree-walker;1"]
               .createInstance(Ci.inIDeepTreeWalker);
  walker.showSubDocuments = true;
  walker.showDocumentsAsNodes = true;
  walker.init(frame, nodeFilterConstants.SHOW_ALL);
  walker.currentNode = frame;

  let document = walker.nextNode();
  if (!document || !document.defaultView) {
    throw new Error("Couldn't get the content window inside frame " + frame);
  }

  return document.defaultView;
}

/**
 * Returns a frame's content offset (frame border + padding).
 * Note: this function shouldn't need to exist, had the platform provided a
 * suitable API for determining the offset between the frame's content and
 * its bounding client rect. Bug 626359 should provide us with such an API.
 *
 * @param {DOMNode} frame
 *        The frame.
 * @return {Array} [offsetTop, offsetLeft]
 *         offsetTop is the distance from the top of the frame and the top of
 *         the content document.
 *         offsetLeft is the distance from the left of the frame and the left
 *         of the content document.
 */
function getFrameContentOffset(frame) {
  let style = safelyGetContentWindow(frame).getComputedStyle(frame);

  // In some cases, the computed style is null
  if (!style) {
    return [0, 0];
  }

  let paddingTop = parseInt(style.getPropertyValue("padding-top"), 10);
  let paddingLeft = parseInt(style.getPropertyValue("padding-left"), 10);

  let borderTop = parseInt(style.getPropertyValue("border-top-width"), 10);
  let borderLeft = parseInt(style.getPropertyValue("border-left-width"), 10);

  return [borderTop + paddingTop, borderLeft + paddingLeft];
}

/**
 * Check if a node and its document are still alive
 * and attached to the window.
 *
 * @param {DOMNode} node
 * @return {Boolean}
 */
function isNodeConnected(node) {
  if (!node.ownerDocument || !node.ownerDocument.defaultView) {
    return false;
  }

  try {
    return !(node.compareDocumentPosition(node.ownerDocument.documentElement) &
             node.DOCUMENT_POSITION_DISCONNECTED);
  } catch (e) {
    // "can't access dead object" error
    return false;
  }
}
exports.isNodeConnected = isNodeConnected;

/**
 * Traverse getBindingParent until arriving upon the bound element
 * responsible for the generation of the specified node.
 * See https://developer.mozilla.org/en-US/docs/XBL/XBL_1.0_Reference/DOM_Interfaces#getBindingParent.
 *
 * @param {DOMNode} node
 * @return {DOMNode}
 *         If node is not anonymous, this will return node. Otherwise,
 *         it will return the bound element
 *
 */
function getRootBindingParent(node) {
  let parent;
  let doc = node.ownerDocument;
  if (!doc) {
    return node;
  }
  while ((parent = doc.getBindingParent(node))) {
    node = parent;
  }
  return node;
}
exports.getRootBindingParent = getRootBindingParent;

function getBindingParent(node) {
  let doc = node.ownerDocument;
  if (!doc) {
    return null;
  }

  // If there is no binding parent then it is not anonymous.
  let parent = doc.getBindingParent(node);
  if (!parent) {
    return null;
  }

  return parent;
}
exports.getBindingParent = getBindingParent;

/**
 * Determine whether a node is anonymous by determining if there
 * is a bindingParent.
 *
 * @param {DOMNode} node
 * @return {Boolean}
 *
 */
const isAnonymous = (node) => getRootBindingParent(node) !== node;
exports.isAnonymous = isAnonymous;

/**
 * Determine whether a node has a bindingParent.
 *
 * @param {DOMNode} node
 * @return {Boolean}
 *
 */
const hasBindingParent = (node) => !!getBindingParent(node);

/**
 * Determine whether a node is native anonymous content (as opposed
 * to XBL anonymous or shadow DOM).
 * Native anonymous content includes elements like internals to form
 * controls and ::before/::after.
 *
 * @param {DOMNode} node
 * @return {Boolean}
 *
 */
const isNativeAnonymous = (node) =>
  hasBindingParent(node) && !(isXBLAnonymous(node) || isShadowAnonymous(node));

exports.isNativeAnonymous = isNativeAnonymous;

/**
 * Determine whether a node is XBL anonymous content (as opposed
 * to native anonymous or shadow DOM).
 * See https://developer.mozilla.org/en-US/docs/XBL/XBL_1.0_Reference/Anonymous_Content.
 *
 * @param {DOMNode} node
 * @return {Boolean}
 *
 */
function isXBLAnonymous(node) {
  let parent = getBindingParent(node);
  if (!parent) {
    return false;
  }

  // Shadow nodes also show up in getAnonymousNodes, so return false.
  if (parent.shadowRoot && parent.shadowRoot.contains(node)) {
    return false;
  }

  let anonNodes = [...node.ownerDocument.getAnonymousNodes(parent) || []];
  return anonNodes.indexOf(node) > -1;
}
exports.isXBLAnonymous = isXBLAnonymous;

/**
 * Determine whether a node is a child of a shadow root.
 * See https://w3c.github.io/webcomponents/spec/shadow/
 *
 * @param {DOMNode} node
 * @return {Boolean}
 */
function isShadowAnonymous(node) {
  let parent = getBindingParent(node);
  if (!parent) {
    return false;
  }

  // If there is a shadowRoot and this is part of it then this
  // is not native anonymous
  return parent.shadowRoot && parent.shadowRoot.contains(node);
}
exports.isShadowAnonymous = isShadowAnonymous;

/**
 * Get the current zoom factor applied to the container window of a given node.
 * Container windows are used as a weakmap key to store the corresponding
 * nsIDOMWindowUtils instance to avoid querying it every time.
 *
 * @param {DOMNode|DOMWindow}
 *        The node for which the zoom factor should be calculated, or its
 *        owner window.
 * @return {Number}
 */
function getCurrentZoom(node) {
  let win = getWindowFor(node);

  if (!win) {
    throw new Error("Unable to get the zoom from the given argument.");
  }

  return utilsFor(win).fullZoom;
}
exports.getCurrentZoom = getCurrentZoom;

/**
 * Get the display pixel ratio for a given window.
 * The `devicePixelRatio` property is affected by the zoom (see bug 809788), so we have to
 * divide by the zoom value in order to get just the display density, expressed as pixel
 * ratio (the physical display pixel compares to a pixel on a “normal” density screen).
 *
 * @param {DOMNode|DOMWindow}
 *        The node for which the zoom factor should be calculated, or its
 *        owner window.
 * @return {Number}
 */
function getDisplayPixelRatio(node) {
  let win = getWindowFor(node);
  return win.devicePixelRatio / utilsFor(win).fullZoom;
}
exports.getDisplayPixelRatio = getDisplayPixelRatio;

/**
 * Returns the window's dimensions for the `window` given.
 *
 * @return {Object} An object with `width` and `height` properties, representing the
 * number of pixels for the document's size.
 */
function getWindowDimensions(window) {
  // First we'll try without flushing layout, because it's way faster.
  let windowUtils = utilsFor(window);
  let { width, height } = windowUtils.getRootBounds();

  if (!width || !height) {
    // We need a flush after all :'(
    width = window.innerWidth + window.scrollMaxX - window.scrollMinX;
    height = window.innerHeight + window.scrollMaxY - window.scrollMinY;

    let scrollbarHeight = {};
    let scrollbarWidth = {};
    windowUtils.getScrollbarSize(false, scrollbarWidth, scrollbarHeight);
    width -= scrollbarWidth.value;
    height -= scrollbarHeight.value;
  }

  return { width, height };
}
exports.getWindowDimensions = getWindowDimensions;

/**
 * Returns the viewport's dimensions for the `window` given.
 *
 * @return {Object} An object with `width` and `height` properties, representing the
 * number of pixels for the viewport's size.
 */
function getViewportDimensions(window) {
  let windowUtils = utilsFor(window);

  let scrollbarHeight = {};
  let scrollbarWidth = {};
  windowUtils.getScrollbarSize(false, scrollbarWidth, scrollbarHeight);

  let width = window.innerWidth - scrollbarWidth.value;
  let height = window.innerHeight - scrollbarHeight.value;

  return { width, height };
}
exports.getViewportDimensions = getViewportDimensions;

/**
 * Return the default view for a given node, where node can be:
 * - a DOM node
 * - the document node
 * - the window itself
 * @param {DOMNode|DOMWindow|DOMDocument} node The node to get the window for.
 * @return {DOMWindow}
 */
function getWindowFor(node) {
  if (node instanceof Ci.nsIDOMNode) {
    if (node.nodeType === node.DOCUMENT_NODE) {
      return node.defaultView;
    }
    return node.ownerDocument.defaultView;
  } else if (node instanceof Ci.nsIDOMWindow) {
    return node;
  }
  return null;
}

/**
 * Synchronously loads a style sheet from `uri` and adds it to the list of
 * additional style sheets of the document.
 * The sheets added takes effect immediately, and only on the document of the
 * `window` given.
 *
 * @param {DOMWindow} window
 * @param {String} url
 * @param {String} [type="agent"]
 */
function loadSheet(window, url, type = "agent") {
  if (!(type in SHEET_TYPE)) {
    type = "agent";
  }

  let windowUtils = utilsFor(window);
  try {
    windowUtils.loadSheetUsingURIString(url, windowUtils[SHEET_TYPE[type]]);
  } catch (e) {
    // The method fails if the url is already loaded.
  }
}
exports.loadSheet = loadSheet;

/**
 * Remove the document style sheet at `sheetURI` from the list of additional
 * style sheets of the document. The removal takes effect immediately.
 *
 * @param {DOMWindow} window
 * @param {String} url
 * @param {String} [type="agent"]
 */
function removeSheet(window, url, type = "agent") {
  if (!(type in SHEET_TYPE)) {
    type = "agent";
  }

  let windowUtils = utilsFor(window);
  try {
    windowUtils.removeSheetUsingURIString(url, windowUtils[SHEET_TYPE[type]]);
  } catch (e) {
    // The method fails if the url is already removed.
  }
}
exports.removeSheet = removeSheet;
PK
!<����ff=chrome/devtools/modules/devtools/shared/loader-plugin-raw.jsm/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm", {});

/**
 * A function that can be used as part of a require hook for a
 * loader.js Loader.
 * This function handles "raw!" and "theme-loader!" requires.
 * See also: https://github.com/webpack/raw-loader.
 */
this.requireRawId = function (id, require) {
  let index = id.indexOf("!");
  let rawId = id.slice(index + 1);
  let uri = require.resolve(rawId);
  // If the original string did not end with ".js", then
  // require.resolve might have added the suffix.  We don't want to
  // add a suffix for a raw load (if needed the caller can specify it
  // manually), so remove it here.
  if (!id.endsWith(".js") && uri.endsWith(".js")) {
    uri = uri.slice(0, -3);
  }

  let stream = NetUtil.newChannel({
    uri: NetUtil.newURI(uri, "UTF-8"),
    loadUsingSystemPrincipal: true
  }).open2();

  let count = stream.available();
  let data = NetUtil.readInputStreamToString(stream, count, {
    charset: "UTF-8"
  });
  stream.close();

  // For the time being it doesn't seem worthwhile to cache the
  // result here.
  return data;
};

this.EXPORTED_SYMBOLS = ["requireRawId"];
PK
!<JJع�K�KJchrome/devtools/modules/devtools/shared/node-properties/node-properties.js/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2014 Gabriel Llamas
 *
 * 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.
 *
 */

"use strict";

var hex = function (c){
  switch (c){
    case "0": return 0;
    case "1": return 1;
    case "2": return 2;
    case "3": return 3;
    case "4": return 4;
    case "5": return 5;
    case "6": return 6;
    case "7": return 7;
    case "8": return 8;
    case "9": return 9;
    case "a": case "A": return 10;
    case "b": case "B": return 11;
    case "c": case "C": return 12;
    case "d": case "D": return 13;
    case "e": case "E": return 14;
    case "f": case "F": return 15;
  }
};

var parse = function (data, options, handlers, control){
  var c;
  var code;
  var escape;
  var skipSpace = true;
  var isCommentLine;
  var isSectionLine;
  var newLine = true;
  var multiLine;
  var isKey = true;
  var key = "";
  var value = "";
  var section;
  var unicode;
  var unicodeRemaining;
  var escapingUnicode;
  var keySpace;
  var sep;
  var ignoreLine;

  var line = function (){
    if (key || value || sep){
      handlers.line (key, value);
      key = "";
      value = "";
      sep = false;
    }
  };

  var escapeString = function (key, c, code){
    if (escapingUnicode && unicodeRemaining){
      unicode = (unicode << 4) + hex (c);
      if (--unicodeRemaining) return key;
      escape = false;
      escapingUnicode = false;
      return key + String.fromCharCode (unicode);
    }

    //code 117: u
    if (code === 117){
      unicode = 0;
      escapingUnicode = true;
      unicodeRemaining = 4;
      return key;
    }

    escape = false;

    //code 116: t
    //code 114: r
    //code 110: n
    //code 102: f
    if (code === 116) return key + "\t";
    else if (code === 114) return key + "\r";
    else if (code === 110) return key + "\n";
    else if (code === 102) return key + "\f";

    return key + c;
  };

  var isComment;
  var isSeparator;

  if (options._strict){
    isComment = function (c, code, options){
      return options._comments[c];
    };

    isSeparator = function (c, code, options){
      return options._separators[c];
    };
  }else{
    isComment = function (c, code, options){
      //code 35: #
      //code 33: !
      return code === 35 || code === 33 || options._comments[c];
    };

    isSeparator = function (c, code, options){
      //code 61: =
      //code 58: :
      return code === 61 || code === 58 || options._separators[c];
    };
  }

  for (var i=~~control.resume; i<data.length; i++){
    if (control.abort) return;
    if (control.pause){
      //The next index is always the start of a new line, it's a like a fresh
      //start, there's no need to save the current state
      control.resume = i;
      return;
    }

    c = data[i];
    code = data.charCodeAt (i);

    //code 13: \r
    if (code === 13) continue;

    if (isCommentLine){
      //code 10: \n
      if (code === 10){
        isCommentLine = false;
        newLine = true;
        skipSpace = true;
      }
      continue;
    }

    //code 93: ]
    if (isSectionLine && code === 93){
      handlers.section (section);
      //Ignore chars after the section in the same line
      ignoreLine = true;
      continue;
    }

    if (skipSpace){
      //code 32: " " (space)
      //code 9: \t
      //code 12: \f
      if (code === 32 || code === 9 || code === 12){
        continue;
      }
      //code 10: \n
      if (!multiLine && code === 10){
        //Empty line or key w/ separator and w/o value
        isKey = true;
        keySpace = false;
        newLine = true;
        line ();
        continue;
      }
      skipSpace = false;
      multiLine = false;
    }

    if (newLine){
      newLine = false;
      if (isComment (c, code, options)){
        isCommentLine = true;
        continue;
      }
      //code 91: [
      if (options.sections && code === 91){
        section = "";
        isSectionLine = true;
        control.skipSection = false;
        continue;
      }
    }

    //code 10: \n
    if (code !== 10){
      if (control.skipSection || ignoreLine) continue;

      if (!isSectionLine){
        if (!escape && isKey && isSeparator (c, code, options)){
          //sep is needed to detect empty key and empty value with a
          //non-whitespace separator
          sep = true;
          isKey = false;
          keySpace = false;
          //Skip whitespace between separator and value
          skipSpace = true;
          continue;
        }
      }

      //code 92: "\" (backslash)
      if (code === 92){
        if (escape){
          if (escapingUnicode) continue;

          if (keySpace){
            //Line with whitespace separator
            keySpace = false;
            isKey = false;
          }

          if (isSectionLine) section += "\\";
          else if (isKey) key += "\\";
          else value += "\\";
        }
        escape = !escape;
      }else{
        if (keySpace){
          //Line with whitespace separator
          keySpace = false;
          isKey = false;
        }

        if (isSectionLine){
          if (escape) section = escapeString (section, c, code);
          else section += c;
        }else if (isKey){
          if (escape){
            key = escapeString (key, c, code);
          }else{
            //code 32: " " (space)
            //code 9: \t
            //code 12: \f
            if (code === 32 || code === 9 || code === 12){
              keySpace = true;
              //Skip whitespace between key and separator
              skipSpace = true;
              continue;
            }
            key += c;
          }
        }else{
          if (escape) value = escapeString (value, c, code);
          else value += c;
        }
      }
    }else{
      if (escape){
        if (!escapingUnicode){
          escape = false;
        }
        skipSpace = true;
        multiLine = true;
      }else{
        if (isSectionLine){
          isSectionLine = false;
          if (!ignoreLine){
            //The section doesn't end with ], it's a key
            control.error = new Error ("The section line \"" + section +
                "\" must end with \"]\"");
            return;
          }
          ignoreLine = false;
        }
        newLine = true;
        skipSpace = true;
        isKey = true;

        line ();
      }
    }
  }

  control.parsed = true;

  if (isSectionLine && !ignoreLine){
    //The section doesn't end with ], it's a key
    control.error = new Error ("The section line \"" + section + "\" must end" +
        "with \"]\"");
    return;
  }
  line ();
};

var INCLUDE_KEY = "include";
var INDEX_FILE = "index.properties";

var cast = function (value){
  if (value === null || value === "null") return null;
  if (value === "undefined") return undefined;
  if (value === "true") return true;
  if (value === "false") return false;
  var v = Number (value);
  return isNaN (v) ? value : v;
};

var expand = function  (o, str, options, cb){
  if (!options.variables || !str) return cb (null, str);

  var stack = [];
  var c;
  var cp;
  var key = "";
  var section = null;
  var v;
  var holder;
  var t;
  var n;

  for (var i=0; i<str.length; i++){
    c = str[i];

    if (cp === "$" && c === "{"){
      key = key.substring (0, key.length - 1);
      stack.push ({
        key: key,
        section: section
      });
      key = "";
      section = null;
      continue;
    }else if (stack.length){
      if (options.sections && c === "|"){
        section = key;
        key = "";
        continue;
      }else if (c === "}"){
        holder = section !== null ? searchValue (o, section, true) : o;
        if (!holder){
          return cb (new Error ("The section \"" + section + "\" does not " +
              "exist"));
        }

        v = options.namespaces ? searchValue (holder, key) : holder[key];
        if (v === undefined){
          //Read the external vars
          v = options.namespaces
              ? searchValue (options._vars, key)
              : options._vars[key]

          if (v === undefined){
            return cb (new Error ("The property \"" + key + "\" does not " +
                "exist"));
          }
        }

        t = stack.pop ();
        section = t.section;
        key = t.key + (v === null ? "" : v);
        continue;
      }
    }

    cp = c;
    key += c;
  }

  if (stack.length !== 0){
    return cb (new Error ("Malformed variable: " + str));
  }

  cb (null, key);
};

var searchValue = function (o, chain, section){
  var n = chain.split (".");
  var str;

  for (var i=0; i<n.length-1; i++){
    str = n[i];
    if (o[str] === undefined) return;
    o = o[str];
  }

  var v = o[n[n.length - 1]];
  if (section){
    if (typeof v !== "object") return;
    return v;
  }else{
    if (typeof v === "object") return;
    return v;
  }
};

var namespaceKey = function (o, key, value){
  var n = key.split (".");
  var str;

  for (var i=0; i<n.length-1; i++){
    str = n[i];
    if (o[str] === undefined){
      o[str] = {};
    }else if (typeof o[str] !== "object"){
      throw new Error ("Invalid namespace chain in the property name '" +
          key + "' ('" + str + "' has already a value)");
    }
    o = o[str];
  }

  o[n[n.length - 1]] = value;
};

var namespaceSection = function (o, section){
  var n = section.split (".");
  var str;

  for (var i=0; i<n.length; i++){
    str = n[i];
    if (o[str] === undefined){
      o[str] = {};
    }else if (typeof o[str] !== "object"){
      throw new Error ("Invalid namespace chain in the section name '" +
          section + "' ('" + str + "' has already a value)");
    }
    o = o[str];
  }

  return o;
};

var merge = function (o1, o2){
  for (var p in o2){
    try{
      if (o1[p].constructor === Object){
        o1[p] = merge (o1[p], o2[p]);
      }else{
        o1[p] = o2[p];
      }
    }catch (e){
      o1[p] = o2[p];
    }
  }
  return o1;
}

var build = function (data, options, dirname, cb){
  var o = {};

  if (options.namespaces){
    var n = {};
  }

  var control = {
    abort: false,
    skipSection: false
  };

  if (options.include){
    var remainingIncluded = 0;

    var include = function (value){
      if (currentSection !== null){
        return abort (new Error ("Cannot include files from inside a " +
            "section: " + currentSection));
      }

      var p = path.resolve (dirname, value);
      if (options._included[p]) return;

      options._included[p] = true;
      remainingIncluded++;
      control.pause = true;

      read (p, options, function (error, included){
        if (error) return abort (error);

        remainingIncluded--;
        merge (options.namespaces ? n : o, included);
        control.pause = false;

        if (!control.parsed){
          parse (data, options, handlers, control);
          if (control.error) return abort (control.error);
        }

        if (!remainingIncluded) cb (null, options.namespaces ? n : o);
      });
    };
  }

  if (!data){
    if (cb) return cb (null, o);
    return o;
  }

  var currentSection = null;
  var currentSectionStr = null;

  var abort = function (error){
    control.abort = true;
    if (cb) return cb (error);
    throw error;
  };

  var handlers = {};
  var reviver = {
    assert: function (){
      return this.isProperty ? reviverLine.value : true;
    }
  };
  var reviverLine = {};

  //Line handler
  //For speed reasons, if "namespaces" is enabled, the old object is still
  //populated, e.g.: ${a.b} reads the "a.b" property from { "a.b": 1 }, instead
  //of having a unique object { a: { b: 1 } } which is slower to search for
  //the "a.b" value
  //If "a.b" is not found, then the external vars are read. If "namespaces" is
  //enabled, the var "a.b" is split and it searches the a.b value. If it is not
  //enabled, then the var "a.b" searches the "a.b" value

  var line;
  var error;

  if (options.reviver){
    if (options.sections){
      line = function (key, value){
        if (options.include && key === INCLUDE_KEY) return include (value);

        reviverLine.value = value;
        reviver.isProperty = true;
        reviver.isSection = false;

        value = options.reviver.call (reviver, key, value, currentSectionStr);
        if (value !== undefined){
          if (options.namespaces){
            try{
              namespaceKey (currentSection === null ? n : currentSection,
                  key, value);
            }catch (error){
              abort (error);
            }
          }else{
            if (currentSection === null) o[key] = value;
            else currentSection[key] = value;
          }
        }
      };
    }else{
      line = function (key, value){
        if (options.include && key === INCLUDE_KEY) return include (value);

        reviverLine.value = value;
        reviver.isProperty = true;
        reviver.isSection = false;

        value = options.reviver.call (reviver, key, value);
        if (value !== undefined){
          if (options.namespaces){
            try{
              namespaceKey (n, key, value);
            }catch (error){
              abort (error);
            }
          }else{
            o[key] = value;
          }
        }
      };
    }
  }else{
    if (options.sections){
      line = function (key, value){
        if (options.include && key === INCLUDE_KEY) return include (value);

        if (options.namespaces){
          try{
            namespaceKey (currentSection === null ? n : currentSection, key,
                value);
          }catch (error){
            abort (error);
          }
        }else{
          if (currentSection === null) o[key] = value;
          else currentSection[key] = value;
        }
      };
    }else{
      line = function (key, value){
        if (options.include && key === INCLUDE_KEY) return include (value);

        if (options.namespaces){
          try{
            namespaceKey (n, key, value);
          }catch (error){
            abort (error);
          }
        }else{
          o[key] = value;
        }
      };
    }
  }

  //Section handler
  var section;
  if (options.sections){
    if (options.reviver){
      section = function (section){
        currentSectionStr = section;
        reviverLine.section = section;
        reviver.isProperty = false;
        reviver.isSection = true;

        var add = options.reviver.call (reviver, null, null, section);
        if (add){
          if (options.namespaces){
            try{
              currentSection = namespaceSection (n, section);
            }catch (error){
              abort (error);
            }
          }else{
            currentSection = o[section] = {};
          }
        }else{
          control.skipSection = true;
        }
      };
    }else{
      section = function (section){
        currentSectionStr = section;
        if (options.namespaces){
          try{
            currentSection = namespaceSection (n, section);
          }catch (error){
            abort (error);
          }
        }else{
          currentSection = o[section] = {};
        }
      };
    }
  }

  //Variables
  if (options.variables){
    handlers.line = function (key, value){
      expand (options.namespaces ? n : o, key, options, function (error, key){
        if (error) return abort (error);

        expand (options.namespaces ? n : o, value, options,
            function (error, value){
          if (error) return abort (error);

          line (key, cast (value || null));
        });
      });
    };

    if (options.sections){
      handlers.section = function (s){
        expand (options.namespaces ? n : o, s, options, function (error, s){
          if (error) return abort (error);

          section (s);
        });
      };
    }
  }else{
    handlers.line = function (key, value){
      line (key, cast (value || null));
    };

    if (options.sections){
      handlers.section = section;
    }
  }

  parse (data, options, handlers, control);
  if (control.error) return abort (control.error);

  if (control.abort || control.pause) return;

  if (cb) return cb (null, options.namespaces ? n : o);
  return options.namespaces ? n : o;
};

var read = function (f, options, cb){
  fs.stat (f, function (error, stats){
    if (error) return cb (error);

    var dirname;

    if (stats.isDirectory ()){
      dirname = f;
      f = path.join (f, INDEX_FILE);
    }else{
      dirname = path.dirname (f);
    }

    fs.readFile (f, { encoding: "utf8" }, function (error, data){
      if (error) return cb (error);
      build (data, options, dirname, cb);
    });
  });
};

module.exports = function (data, options, cb){
  if (typeof options === "function"){
    cb = options;
    options = {};
  }

  options = options || {};
  var code;

  if (options.include){
    if (!cb) throw new Error ("A callback must be passed if the 'include' " +
        "option is enabled");
    options._included = {};
  }

  options = options || {};
  options._strict = options.strict && (options.comments || options.separators);
  options._vars = options.vars || {};

  var comments = options.comments || [];
  if (!Array.isArray (comments)) comments = [comments];
  var c = {};
  comments.forEach (function (comment){
    code = comment.charCodeAt (0);
    if (comment.length > 1 || code < 33 || code > 126){
      throw new Error ("The comment token must be a single printable ASCII " +
          "character");
    }
    c[comment] = true;
  });
  options._comments = c;

  var separators = options.separators || [];
  if (!Array.isArray (separators)) separators = [separators];
  var s = {};
  separators.forEach (function (separator){
    code = separator.charCodeAt (0);
    if (separator.length > 1 || code < 33 || code > 126){
      throw new Error ("The separator token must be a single printable ASCII " +
          "character");
    }
    s[separator] = true;
  });
  options._separators = s;

  if (options.path){
    if (!cb) throw new Error ("A callback must be passed if the 'path' " +
        "option is enabled");
    if (options.include){
      read (data, options, cb);
    }else{
      fs.readFile (data, { encoding: "utf8" }, function (error, data){
        if (error) return cb (error);
        build (data, options, ".", cb);
      });
    }
  }else{
    return build (data, options, ".", cb);
  }
};
PK
!<K��\RR<chrome/devtools/modules/devtools/shared/old-event-emitter.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Services = require("Services");
const defer = require("devtools/shared/defer");
const { describeNthCaller } = require("devtools/shared/platform/stack");
let loggingEnabled = true;

if (!isWorker) {
  loggingEnabled = Services.prefs.getBoolPref("devtools.dump.emit");
  Services.prefs.addObserver("devtools.dump.emit", {
    observe: () => {
      loggingEnabled = Services.prefs.getBoolPref("devtools.dump.emit");
    }
  });
}

let EventEmitter = this.EventEmitter = function () {};
module.exports = EventEmitter;

/**
 * Decorate an object with event emitter functionality.
 *
 * @param Object objectToDecorate
 *        Bind all public methods of EventEmitter to
 *        the objectToDecorate object.
 * @return Object the object given.
 */
EventEmitter.decorate = function (objectToDecorate) {
  let emitter = new EventEmitter();
  objectToDecorate.on = emitter.on.bind(emitter);
  objectToDecorate.off = emitter.off.bind(emitter);
  objectToDecorate.once = emitter.once.bind(emitter);
  objectToDecorate.emit = emitter.emit.bind(emitter);

  return objectToDecorate;
};

EventEmitter.prototype = {
  /**
   * Connect a listener.
   *
   * @param string event
   *        The event name to which we're connecting.
   * @param function listener
   *        Called when the event is fired.
   */
  on(event, listener) {
    if (!this._eventEmitterListeners) {
      this._eventEmitterListeners = new Map();
    }
    if (!this._eventEmitterListeners.has(event)) {
      this._eventEmitterListeners.set(event, []);
    }
    this._eventEmitterListeners.get(event).push(listener);
  },

  /**
   * Listen for the next time an event is fired.
   *
   * @param string event
   *        The event name to which we're connecting.
   * @param function listener
   *        (Optional) Called when the event is fired. Will be called at most
   *        one time.
   * @return promise
   *        A promise which is resolved when the event next happens. The
   *        resolution value of the promise is the first event argument. If
   *        you need access to second or subsequent event arguments (it's rare
   *        that this is needed) then use listener
   */
  once(event, listener) {
    let deferred = defer();

    let handler = (_, first, ...rest) => {
      this.off(event, handler);
      if (listener) {
        listener(event, first, ...rest);
      }
      deferred.resolve(first);
    };

    handler._originalListener = listener;
    this.on(event, handler);

    return deferred.promise;
  },

  /**
   * Remove a previously-registered event listener.  Works for events
   * registered with either on or once.
   *
   * @param string event
   *        The event name whose listener we're disconnecting.
   * @param function listener
   *        The listener to remove.
   */
  off(event, listener) {
    if (!this._eventEmitterListeners) {
      return;
    }
    let listeners = this._eventEmitterListeners.get(event);
    if (listeners) {
      this._eventEmitterListeners.set(event, listeners.filter(l => {
        return l !== listener && l._originalListener !== listener;
      }));
    }
  },

  /**
   * Emit an event.  All arguments to this method will
   * be sent to listener functions.
   */
  emit(event) {
    this.logEvent(event, arguments);

    if (!this._eventEmitterListeners || !this._eventEmitterListeners.has(event)) {
      return;
    }

    let originalListeners = this._eventEmitterListeners.get(event);
    for (let listener of this._eventEmitterListeners.get(event)) {
      // If the object was destroyed during event emission, stop
      // emitting.
      if (!this._eventEmitterListeners) {
        break;
      }

      // If listeners were removed during emission, make sure the
      // event handler we're going to fire wasn't removed.
      if (originalListeners === this._eventEmitterListeners.get(event) ||
        this._eventEmitterListeners.get(event).some(l => l === listener)) {
        try {
          listener.apply(null, arguments);
        } catch (ex) {
          // Prevent a bad listener from interfering with the others.
          let msg = ex + ": " + ex.stack;
          console.error(msg);
          dump(msg + "\n");
        }
      }
    }
  },

  logEvent(event, args) {
    if (!loggingEnabled) {
      return;
    }

    let description = describeNthCaller(2);

    let argOut = "(";
    if (args.length === 1) {
      argOut += event;
    }

    let out = "EMITTING: ";

    // We need this try / catch to prevent any dead object errors.
    try {
      for (let i = 1; i < args.length; i++) {
        if (i === 1) {
          argOut = "(" + event + ", ";
        } else {
          argOut += ", ";
        }

        let arg = args[i];
        argOut += arg;

        if (arg && arg.nodeName) {
          argOut += " (" + arg.nodeName;
          if (arg.id) {
            argOut += "#" + arg.id;
          }
          if (arg.className) {
            argOut += "." + arg.className;
          }
          argOut += ")";
        }
      }
    } catch (e) {
      // Object is dead so the toolbox is most likely shutting down,
      // do nothing.
    }

    argOut += ")";
    out += "emit" + argOut + " from " + description + "\n";

    dump(out);
  },
};
PK
!<���$ff/chrome/devtools/modules/devtools/shared/path.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

/*
 * Join all the arguments together and normalize the resulting URI.
 * The initial path must be an full URI with a protocol (i.e. http://).
 */
exports.joinURI = (initialPath, ...paths) => {
  let url;

  try {
    url = new URL(initialPath);
  } catch (e) {
    return null;
  }

  for (let path of paths) {
    if (path) {
      url = new URL(path, url);
    }
  }

  return url.href;
};
PK
!<�M���Gchrome/devtools/modules/devtools/shared/performance/recording-common.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 mixin to be used for PerformanceRecordingActor, PerformanceRecordingFront,
 * and LegacyPerformanceRecording for helper methods to access data.
 */

exports.PerformanceRecordingCommon = {
  // Private fields, only needed when a recording is started or stopped.
  _console: false,
  _imported: false,
  _recording: false,
  _completed: false,
  _configuration: {},
  _startingBufferStatus: null,
  _localStartTime: 0,

  // Serializable fields, necessary and sufficient for import and export.
  _label: "",
  _duration: 0,
  _markers: null,
  _frames: null,
  _memory: null,
  _ticks: null,
  _allocations: null,
  _profile: null,
  _systemHost: null,
  _systemClient: null,

  /**
   * Helper methods for returning the status of the recording.
   * These methods should be consistent on both the front and actor.
   */
  isRecording: function () {
    return this._recording;
  },
  isCompleted: function () {
    return this._completed || this.isImported();
  },
  isFinalizing: function () {
    return !this.isRecording() && !this.isCompleted();
  },
  isConsole: function () {
    return this._console;
  },
  isImported: function () {
    return this._imported;
  },

  /**
   * Helper methods for returning configuration for the recording.
   * These methods should be consistent on both the front and actor.
   */
  getConfiguration: function () {
    return this._configuration;
  },
  getLabel: function () {
    return this._label;
  },

  /**
   * Gets duration of this recording, in milliseconds.
   * @return number
   */
  getDuration: function () {
    // Compute an approximate ending time for the current recording if it is
    // still in progress. This is needed to ensure that the view updates even
    // when new data is not being generated. If recording is completed, use
    // the duration from the profiler; if between recording and being finalized,
    // use the last estimated duration.
    if (this.isRecording()) {
      this._estimatedDuration = Date.now() - this._localStartTime;
      return this._estimatedDuration;
    }
    return this._duration || this._estimatedDuration || 0;
  },

  /**
   * Helper methods for returning recording data.
   * These methods should be consistent on both the front and actor.
   */
  getMarkers: function () {
    return this._markers;
  },
  getFrames: function () {
    return this._frames;
  },
  getMemory: function () {
    return this._memory;
  },
  getTicks: function () {
    return this._ticks;
  },
  getAllocations: function () {
    return this._allocations;
  },
  getProfile: function () {
    return this._profile;
  },
  getHostSystemInfo: function () {
    return this._systemHost;
  },
  getClientSystemInfo: function () {
    return this._systemClient;
  },
  getStartingBufferStatus: function () {
    return this._startingBufferStatus;
  },

  getAllData: function () {
    let label = this.getLabel();
    let duration = this.getDuration();
    let markers = this.getMarkers();
    let frames = this.getFrames();
    let memory = this.getMemory();
    let ticks = this.getTicks();
    let allocations = this.getAllocations();
    let profile = this.getProfile();
    let configuration = this.getConfiguration();
    let systemHost = this.getHostSystemInfo();
    let systemClient = this.getClientSystemInfo();

    return {
      label,
      duration,
      markers,
      frames,
      memory,
      ticks,
      allocations,
      profile,
      configuration,
      systemHost,
      systemClient
    };
  },
};
PK
!<ǟ�^D^DFchrome/devtools/modules/devtools/shared/performance/recording-utils.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";

/**
 * Utility functions for managing recording models and their internal data,
 * such as filtering profile samples or offsetting timestamps.
 */

function mapRecordingOptions(type, options) {
  if (type === "profiler") {
    return {
      entries: options.bufferSize,
      interval: options.sampleFrequency ? (1000 / (options.sampleFrequency * 1000))
                                        : void 0
    };
  }

  if (type === "memory") {
    return {
      probability: options.allocationsSampleProbability,
      maxLogLength: options.allocationsMaxLogLength
    };
  }

  if (type === "timeline") {
    return {
      withMarkers: true,
      withTicks: options.withTicks,
      withMemory: options.withMemory,
      withFrames: true,
      withGCEvents: true,
      withDocLoadingEvents: false
    };
  }

  return options;
}

/**
 * Takes an options object for `startRecording`, and normalizes
 * it based off of server support. For example, if the user
 * requests to record memory `withMemory = true`, but the server does
 * not support that feature, then the `false` will overwrite user preference
 * in order to define the recording with what is actually available, not
 * what the user initially requested.
 *
 * @param {object} options
 * @param {boolean}
 */
function normalizePerformanceFeatures(options, supportedFeatures) {
  return Object.keys(options).reduce((modifiedOptions, feature) => {
    if (supportedFeatures[feature] !== false) {
      modifiedOptions[feature] = options[feature];
    }
    return modifiedOptions;
  }, Object.create(null));
}

/**
 * Filters all the samples in the provided profiler data to be more recent
 * than the specified start time.
 *
 * @param object profile
 *        The profiler data received from the backend.
 * @param number profilerStartTime
 *        The earliest acceptable sample time (in milliseconds).
 */
function filterSamples(profile, profilerStartTime) {
  let firstThread = profile.threads[0];
  const TIME_SLOT = firstThread.samples.schema.time;
  firstThread.samples.data = firstThread.samples.data.filter(e => {
    return e[TIME_SLOT] >= profilerStartTime;
  });
}

/**
 * Offsets all the samples in the provided profiler data by the specified time.
 *
 * @param object profile
 *        The profiler data received from the backend.
 * @param number timeOffset
 *        The amount of time to offset by (in milliseconds).
 */
function offsetSampleTimes(profile, timeOffset) {
  let firstThread = profile.threads[0];
  const TIME_SLOT = firstThread.samples.schema.time;
  let samplesData = firstThread.samples.data;
  for (let i = 0; i < samplesData.length; i++) {
    samplesData[i][TIME_SLOT] -= timeOffset;
  }
}

/**
 * Offsets all the markers in the provided timeline data by the specified time.
 *
 * @param array markers
 *        The markers array received from the backend.
 * @param number timeOffset
 *        The amount of time to offset by (in milliseconds).
 */
function offsetMarkerTimes(markers, timeOffset) {
  for (let marker of markers) {
    marker.start -= timeOffset;
    marker.end -= timeOffset;
  }
}

/**
 * Offsets and scales all the timestamps in the provided array by the
 * specified time and scale factor.
 *
 * @param array array
 *        A list of timestamps received from the backend.
 * @param number timeOffset
 *        The amount of time to offset by (in milliseconds).
 * @param number timeScale
 *        The factor to scale by, after offsetting.
 */
function offsetAndScaleTimestamps(timestamps, timeOffset, timeScale) {
  for (let i = 0, len = timestamps.length; i < len; i++) {
    timestamps[i] -= timeOffset;
    if (timeScale) {
      timestamps[i] /= timeScale;
    }
  }
}

/**
 * Push all elements of src array into dest array. Marker data will come in small chunks
 * and add up over time, whereas allocation arrays can be > 500000 elements (and
 * Function.prototype.apply throws if applying more than 500000 elements, which
 * is what spawned this separate function), so iterate one element at a time.
 * @see bug 1166823
 * @see http://jsperf.com/concat-large-arrays
 * @see http://jsperf.com/concat-large-arrays/2
 *
 * @param {Array} dest
 * @param {Array} src
 */
function pushAll(dest, src) {
  let length = src.length;
  for (let i = 0; i < length; i++) {
    dest.push(src[i]);
  }
}

/**
 * Cache used in `RecordingUtils.getProfileThreadFromAllocations`.
 */
var gProfileThreadFromAllocationCache = new WeakMap();

/**
 * Converts allocation data from the memory actor to something that follows
 * the same structure as the samples data received from the profiler.
 *
 * @see MemoryActor.prototype.getAllocations for more information.
 *
 * @param object allocations
 *        A list of { sites, timestamps, frames, sizes } arrays.
 * @return object
 *         The "profile" describing the allocations log.
 */
function getProfileThreadFromAllocations(allocations) {
  let cached = gProfileThreadFromAllocationCache.get(allocations);
  if (cached) {
    return cached;
  }

  let { sites, timestamps, frames, sizes } = allocations;
  let uniqueStrings = new UniqueStrings();

  // Convert allocation frames to the the stack and frame tables expected by
  // the profiler format.
  //
  // Since the allocations log is already presented as a tree, we would be
  // wasting time if we jumped through the same hoops as deflateProfile below
  // and instead use the existing structure of the allocations log to build up
  // the profile JSON.
  //
  // The allocations.frames array corresponds roughly to the profile stack
  // table: a trie of all stacks. We could work harder to further deduplicate
  // each individual frame as the profiler does, but it is not necessary for
  // correctness.
  let stackTable = new Array(frames.length);
  let frameTable = new Array(frames.length);

  // Array used to concat the location.
  let locationConcatArray = new Array(5);

  for (let i = 0; i < frames.length; i++) {
    let frame = frames[i];
    if (!frame) {
      stackTable[i] = frameTable[i] = null;
      continue;
    }

    let prefix = frame.parent;

    // Schema:
    //   [prefix, frame]
    stackTable[i] = [frames[prefix] ? prefix : null, i];

    // Schema:
    //   [location]
    //
    // The only field a frame will have in an allocations profile is location.
    //
    // If frame.functionDisplayName is present, the format is
    //   "functionDisplayName (source:line:column)"
    // Otherwise, it is
    //   "source:line:column"
    //
    // A static array is used to join to save memory on intermediate strings.
    locationConcatArray[0] = frame.source;
    locationConcatArray[1] = ":";
    locationConcatArray[2] = String(frame.line);
    locationConcatArray[3] = ":";
    locationConcatArray[4] = String(frame.column);
    locationConcatArray[5] = "";

    let location = locationConcatArray.join("");
    let funcName = frame.functionDisplayName;

    if (funcName) {
      locationConcatArray[0] = funcName;
      locationConcatArray[1] = " (";
      locationConcatArray[2] = location;
      locationConcatArray[3] = ")";
      locationConcatArray[4] = "";
      locationConcatArray[5] = "";
      location = locationConcatArray.join("");
    }

    frameTable[i] = [uniqueStrings.getOrAddStringIndex(location)];
  }

  let samples = new Array(sites.length);
  let writePos = 0;
  for (let i = 0; i < sites.length; i++) {
    // Schema:
    //   [stack, time, size]
    //
    // Originally, sites[i] indexes into the frames array. Note that in the
    // loop above, stackTable[sites[i]] and frames[sites[i]] index the same
    // information.
    let stackIndex = sites[i];
    if (frames[stackIndex]) {
      samples[writePos++] = [stackIndex, timestamps[i], sizes[i]];
    }
  }
  samples.length = writePos;

  let thread = {
    name: "allocations",
    samples: allocationsWithSchema(samples),
    stackTable: stackTableWithSchema(stackTable),
    frameTable: frameTableWithSchema(frameTable),
    stringTable: uniqueStrings.stringTable
  };

  gProfileThreadFromAllocationCache.set(allocations, thread);
  return thread;
}

function allocationsWithSchema(data) {
  let slot = 0;
  return {
    schema: {
      stack: slot++,
      time: slot++,
      size: slot++,
    },
    data: data
  };
}

/**
 * Deduplicates a profile by deduplicating stacks, frames, and strings.
 *
 * This is used to adapt version 2 profiles from the backend to version 3, for
 * use with older Geckos (like B2G).
 *
 * Note that the schemas used by this must be kept in sync with schemas used
 * by the C++ UniqueStacks class in tools/profiler/ProfileEntry.cpp.
 *
 * @param object profile
 *               A profile with version 2.
 */
function deflateProfile(profile) {
  profile.threads = profile.threads.map((thread) => {
    let uniqueStacks = new UniqueStacks();
    return deflateThread(thread, uniqueStacks);
  });

  profile.meta.version = 3;
  return profile;
}

/**
 * Given an array of frame objects, deduplicates each frame as well as all
 * prefixes in the stack. Returns the index of the deduplicated stack.
 *
 * @param object frames
 *               Array of frame objects.
 * @param UniqueStacks uniqueStacks
 * @return number index
 */
function deflateStack(frames, uniqueStacks) {
  // Deduplicate every prefix in the stack by keeping track of the current
  // prefix hash.
  let prefixIndex = null;
  for (let i = 0; i < frames.length; i++) {
    let frameIndex = uniqueStacks.getOrAddFrameIndex(frames[i]);
    prefixIndex = uniqueStacks.getOrAddStackIndex(prefixIndex, frameIndex);
  }
  return prefixIndex;
}

/**
 * Given an array of sample objects, deduplicate each sample's stack and
 * convert the samples to a table with a schema. Returns the deflated samples.
 *
 * @param object samples
 *               Array of samples
 * @param UniqueStacks uniqueStacks
 * @return object
 */
function deflateSamples(samples, uniqueStacks) {
  // Schema:
  //   [stack, time, responsiveness, rss, uss]

  let deflatedSamples = new Array(samples.length);
  for (let i = 0; i < samples.length; i++) {
    let sample = samples[i];
    deflatedSamples[i] = [
      deflateStack(sample.frames, uniqueStacks),
      sample.time,
      sample.responsiveness,
      sample.rss,
      sample.uss
    ];
  }

  return samplesWithSchema(deflatedSamples);
}

/**
 * Given an array of marker objects, convert the markers to a table with a
 * schema. Returns the deflated markers.
 *
 * If a marker contains a backtrace as its payload, the backtrace stack is
 * deduplicated in the context of the profile it's in.
 *
 * @param object markers
 *               Array of markers
 * @param UniqueStacks uniqueStacks
 * @return object
 */
function deflateMarkers(markers, uniqueStacks) {
  // Schema:
  //   [name, time, data]

  let deflatedMarkers = new Array(markers.length);
  for (let i = 0; i < markers.length; i++) {
    let marker = markers[i];
    if (marker.data && marker.data.type === "tracing" && marker.data.stack) {
      marker.data.stack = deflateThread(marker.data.stack, uniqueStacks);
    }

    deflatedMarkers[i] = [
      uniqueStacks.getOrAddStringIndex(marker.name),
      marker.time,
      marker.data
    ];
  }

  let slot = 0;
  return {
    schema: {
      name: slot++,
      time: slot++,
      data: slot++
    },
    data: deflatedMarkers
  };
}

/**
 * Deflate a thread.
 *
 * @param object thread
 *               The profile thread.
 * @param UniqueStacks uniqueStacks
 * @return object
 */
function deflateThread(thread, uniqueStacks) {
  // Some extra threads in a profile come stringified as a full profile (so
  // it has nested threads itself) so the top level "thread" does not have markers
  // or samples. We don't use this anyway so just make this safe to deflate.
  // can be a string rather than an object on import. Bug 1173695
  if (typeof thread === "string") {
    thread = JSON.parse(thread);
  }
  if (!thread.samples) {
    thread.samples = [];
  }
  if (!thread.markers) {
    thread.markers = [];
  }

  return {
    name: thread.name,
    tid: thread.tid,
    samples: deflateSamples(thread.samples, uniqueStacks),
    markers: deflateMarkers(thread.markers, uniqueStacks),
    stackTable: uniqueStacks.getStackTableWithSchema(),
    frameTable: uniqueStacks.getFrameTableWithSchema(),
    stringTable: uniqueStacks.getStringTable()
  };
}

function stackTableWithSchema(data) {
  let slot = 0;
  return {
    schema: {
      prefix: slot++,
      frame: slot++
    },
    data: data
  };
}

function frameTableWithSchema(data) {
  let slot = 0;
  return {
    schema: {
      location: slot++,
      implementation: slot++,
      optimizations: slot++,
      line: slot++,
      category: slot++
    },
    data: data
  };
}

function samplesWithSchema(data) {
  let slot = 0;
  return {
    schema: {
      stack: slot++,
      time: slot++,
      responsiveness: slot++,
      rss: slot++,
      uss: slot++
    },
    data: data
  };
}

/**
 * A helper class to deduplicate strings.
 */
function UniqueStrings() {
  this.stringTable = [];
  this._stringHash = Object.create(null);
}

UniqueStrings.prototype.getOrAddStringIndex = function (s) {
  if (!s) {
    return null;
  }

  let stringHash = this._stringHash;
  let stringTable = this.stringTable;
  let index = stringHash[s];
  if (index !== undefined) {
    return index;
  }

  index = stringTable.length;
  stringHash[s] = index;
  stringTable.push(s);
  return index;
};

/**
 * A helper class to deduplicate old-version profiles.
 *
 * The main functionality provided is deduplicating frames and stacks.
 *
 * For example, given 2 stacks
 *   [A, B, C]
 * and
 *   [A, B, D]
 *
 * There are 4 unique frames: A, B, C, and D.
 * There are 4 unique prefixes: [A], [A, B], [A, B, C], [A, B, D]
 *
 * For the example, the output of using UniqueStacks is:
 *
 * Frame table:
 *   [A, B, C, D]
 *
 * That is, A has id 0, B has id 1, etc.
 *
 * Since stack prefixes are themselves deduplicated (shared), stacks are
 * represented as a tree, or more concretely, a pair of ids, the prefix and
 * the leaf.
 *
 * Stack table:
 *   [
 *     [null, 0],
 *     [0,    1],
 *     [1,    2],
 *     [1,    3]
 *   ]
 *
 * That is, [A] has id 0 and value [null, 0]. This means it has no prefix, and
 * has the leaf frame 0, which resolves to A in the frame table.
 *
 * [A, B] has id 1 and value [0, 1]. This means it has prefix 0, which is [A],
 * and leaf 1, thus [A, B].
 *
 * [A, B, C] has id 2 and value [1, 2]. This means it has prefix 1, which in
 * turn is [A, B], and leaf 2, thus [A, B, C].
 *
 * [A, B, D] has id 3 and value [1, 3]. Note how it shares the prefix 1 with
 * [A, B, C].
 */
function UniqueStacks() {
  this._frameTable = [];
  this._stackTable = [];
  this._frameHash = Object.create(null);
  this._stackHash = Object.create(null);
  this._uniqueStrings = new UniqueStrings();
}

UniqueStacks.prototype.getStackTableWithSchema = function () {
  return stackTableWithSchema(this._stackTable);
};

UniqueStacks.prototype.getFrameTableWithSchema = function () {
  return frameTableWithSchema(this._frameTable);
};

UniqueStacks.prototype.getStringTable = function () {
  return this._uniqueStrings.stringTable;
};

UniqueStacks.prototype.getOrAddFrameIndex = function (frame) {
  // Schema:
  //   [location, implementation, optimizations, line, category]

  let frameHash = this._frameHash;
  let frameTable = this._frameTable;

  let locationIndex = this.getOrAddStringIndex(frame.location);
  let implementationIndex = this.getOrAddStringIndex(frame.implementation);

  // Super dumb.
  let hash = `${locationIndex} ${implementationIndex || ""} ` +
             `${frame.line || ""} ${frame.category || ""}`;

  let index = frameHash[hash];
  if (index !== undefined) {
    return index;
  }

  index = frameTable.length;
  frameHash[hash] = index;
  frameTable.push([
    this.getOrAddStringIndex(frame.location),
    this.getOrAddStringIndex(frame.implementation),
    // Don't bother with JIT optimization info for deflating old profile data
    // format to the new format.
    null,
    frame.line,
    frame.category
  ]);
  return index;
};

UniqueStacks.prototype.getOrAddStackIndex = function (prefixIndex, frameIndex) {
  // Schema:
  //   [prefix, frame]

  let stackHash = this._stackHash;
  let stackTable = this._stackTable;

  // Also super dumb.
  let hash = prefixIndex + " " + frameIndex;

  let index = stackHash[hash];
  if (index !== undefined) {
    return index;
  }

  index = stackTable.length;
  stackHash[hash] = index;
  stackTable.push([prefixIndex, frameIndex]);
  return index;
};

UniqueStacks.prototype.getOrAddStringIndex = function (s) {
  return this._uniqueStrings.getOrAddStringIndex(s);
};

exports.pushAll = pushAll;
exports.mapRecordingOptions = mapRecordingOptions;
exports.normalizePerformanceFeatures = normalizePerformanceFeatures;
exports.filterSamples = filterSamples;
exports.offsetSampleTimes = offsetSampleTimes;
exports.offsetMarkerTimes = offsetMarkerTimes;
exports.offsetAndScaleTimestamps = offsetAndScaleTimestamps;
exports.getProfileThreadFromAllocations = getProfileThreadFromAllocations;
exports.deflateProfile = deflateProfile;
exports.deflateThread = deflateThread;
exports.UniqueStrings = UniqueStrings;
exports.UniqueStacks = UniqueStacks;
PK
!<��M<<=chrome/devtools/modules/devtools/shared/platform/clipboard.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Helpers for clipboard handling.

"use strict";

const {Cc, Ci} = require("chrome");
const Services = require("Services");
const clipboardHelper = Cc["@mozilla.org/widget/clipboardhelper;1"]
      .getService(Ci.nsIClipboardHelper);

function copyString(string) {
  clipboardHelper.copyString(string);
}

/**
 * Retrieve the current clipboard data matching the flavor "text/unicode".
 *
 * @return {String} Clipboard text content, null if no text clipboard data is available.
 */
function getText() {
  let flavor = "text/unicode";

  let xferable = Cc["@mozilla.org/widget/transferable;1"]
                 .createInstance(Ci.nsITransferable);

  if (!xferable) {
    throw new Error("Couldn't get the clipboard data due to an internal error " +
                    "(couldn't create a Transferable object).");
  }

  xferable.init(null);
  xferable.addDataFlavor(flavor);

  // Get the data into our transferable.
  Services.clipboard.getData(
    xferable,
    Services.clipboard.kGlobalClipboard
  );

  let data = {};
  try {
    xferable.getTransferData(flavor, data, {});
  } catch (e) {
    // Clipboard doesn't contain data in flavor, return null.
    return null;
  }

  // There's no data available, return.
  if (!data.value) {
    return null;
  }

  return data.value.QueryInterface(Ci.nsISupportsString).data;
}

exports.copyString = copyString;
exports.getText = getText;
PK
!<v�")  9chrome/devtools/modules/devtools/shared/platform/stack.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 few wrappers for stack-manipulation.  This version of the module
// is used in chrome code.

"use strict";

const { Cu, components } = require("chrome");

/**
 * Return a description of the Nth caller, suitable for logging.
 *
 * @param {Number} n the caller to describe
 * @return {String} a description of the nth caller.
 */
function describeNthCaller(n) {
  if (isWorker) {
    return "";
  }

  let caller = components.stack;
  // Do one extra iteration to skip this function.
  while (n >= 0) {
    --n;
    caller = caller.caller;
  }

  let func = caller.name;
  let file = caller.filename;
  if (file.includes(" -> ")) {
    file = caller.filename.split(/ -> /)[1];
  }
  let path = file + ":" + caller.lineNumber;

  return func + "() -> " + path;
}

/**
 * Return a stack object that can be serialized and, when
 * deserialized, passed to callFunctionWithAsyncStack.
 */
function getStack() {
  return components.stack.caller;
}

/**
 * Like Cu.callFunctionWithAsyncStack but handles the isWorker case
 * -- |Cu| isn't defined in workers.
 */
function callFunctionWithAsyncStack(callee, stack, id) {
  if (isWorker) {
    return callee();
  }
  return Cu.callFunctionWithAsyncStack(callee, stack, id);
}

exports.callFunctionWithAsyncStack = callFunctionWithAsyncStack;
exports.describeNthCaller = describeNthCaller;
exports.getStack = getStack;
PK
!<��ս�6chrome/devtools/modules/devtools/shared/plural-form.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/*
 * The code below is mostly is a slight modification of intl/locale/PluralForm.jsm that
 * removes dependencies on chrome privileged APIs. To make maintenance easier, this file
 * is kept as close as possible to the original in terms of implementation.
 * The modified methods here are
 * - makeGetter (remove code adding the caller name to the log)
 * - get ruleNum() (rely on LocalizationHelper instead of String.services)
 * - log() (rely on console.log)
 *
 * Disable eslint warnings to preserve original code style.
 */

/* eslint-disable */

/**
 * This module provides the PluralForm object which contains a method to figure
 * out which plural form of a word to use for a given number based on the
 * current localization. There is also a makeGetter method that creates a get
 * function for the desired plural rule. This is useful for extensions that
 * specify their own plural rule instead of relying on the browser default.
 * (I.e., the extension hasn't been localized to the browser's locale.)
 *
 * See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
 *
 * List of methods:
 *
 * string pluralForm
 * get(int aNum, string aWords)
 *
 * int numForms
 * numForms()
 *
 * [string pluralForm get(int aNum, string aWords), int numForms numForms()]
 * makeGetter(int aRuleNum)
 * Note: Basically, makeGetter returns 2 functions that do "get" and "numForm"
 */

const { LocalizationHelper } = require("devtools/shared/l10n");
const L10N = new LocalizationHelper("toolkit/locales/intl.properties");

// These are the available plural functions that give the appropriate index
// based on the plural rule number specified. The first element is the number
// of plural forms and the second is the function to figure out the index.
const gFunctions = [
  // 0: Chinese
  [1, (n) => 0],
  // 1: English
  [2, (n) => n!=1?1:0],
  // 2: French
  [2, (n) => n>1?1:0],
  // 3: Latvian
  [3, (n) => n%10==1&&n%100!=11?1:n%10==0?0:2],
  // 4: Scottish Gaelic
  [4, (n) => n==1||n==11?0:n==2||n==12?1:n>0&&n<20?2:3],
  // 5: Romanian
  [3, (n) => n==1?0:n==0||n%100>0&&n%100<20?1:2],
  // 6: Lithuanian
  [3, (n) => n%10==1&&n%100!=11?0:n%10>=2&&(n%100<10||n%100>=20)?2:1],
  // 7: Russian
  [3, (n) => n%10==1&&n%100!=11?0:n%10>=2&&n%10<=4&&(n%100<10||n%100>=20)?1:2],
  // 8: Slovak
  [3, (n) => n==1?0:n>=2&&n<=4?1:2],
  // 9: Polish
  [3, (n) => n==1?0:n%10>=2&&n%10<=4&&(n%100<10||n%100>=20)?1:2],
  // 10: Slovenian
  [4, (n) => n%100==1?0:n%100==2?1:n%100==3||n%100==4?2:3],
  // 11: Irish Gaeilge
  [5, (n) => n==1?0:n==2?1:n>=3&&n<=6?2:n>=7&&n<=10?3:4],
  // 12: Arabic
  [6, (n) => n==0?5:n==1?0:n==2?1:n%100>=3&&n%100<=10?2:n%100>=11&&n%100<=99?3:4],
  // 13: Maltese
  [4, (n) => n==1?0:n==0||n%100>0&&n%100<=10?1:n%100>10&&n%100<20?2:3],
  // 14: Unused
  [3, (n) => n%10==1?0:n%10==2?1:2],
  // 15: Icelandic, Macedonian
  [2, (n) => n%10==1&&n%100!=11?0:1],
  // 16: Breton
  [5, (n) => n%10==1&&n%100!=11&&n%100!=71&&n%100!=91?0:n%10==2&&n%100!=12&&n%100!=72&&n%100!=92?1:(n%10==3||n%10==4||n%10==9)&&n%100!=13&&n%100!=14&&n%100!=19&&n%100!=73&&n%100!=74&&n%100!=79&&n%100!=93&&n%100!=94&&n%100!=99?2:n%1000000==0&&n!=0?3:4],
  // 17: Shuar
  [2, (n) => n!=0?1:0],
  // 18: Welsh
  [6, (n) => n==0?0:n==1?1:n==2?2:n==3?3:n==6?4:5],
];

const PluralForm = {
  /**
   * Get the correct plural form of a word based on the number
   *
   * @param aNum
   *        The number to decide which plural form to use
   * @param aWords
   *        A semi-colon (;) separated string of words to pick the plural form
   * @return The appropriate plural form of the word
   */
  get get()
  {
    // This method will lazily load to avoid perf when it is first needed and
    // creates getPluralForm function. The function it creates is based on the
    // value of pluralRule specified in the intl stringbundle.
    // See: http://developer.mozilla.org/en/docs/Localization_and_Plurals

    // Delete the getters to be overwritten
    delete this.numForms;
    delete this.get;

    // Make the plural form get function and set it as the default get
    [this.get, this.numForms] = this.makeGetter(this.ruleNum);
    return this.get;
  },

  /**
   * Create a pair of plural form functions for the given plural rule number.
   *
   * @param aRuleNum
   *        The plural rule number to create functions
   * @return A pair: [function that gets the right plural form,
   *                  function that returns the number of plural forms]
   */
  makeGetter: function(aRuleNum)
  {
    // Default to "all plural" if the value is out of bounds or invalid
    if (aRuleNum < 0 || aRuleNum >= gFunctions.length || isNaN(aRuleNum)) {
      log(["Invalid rule number: ", aRuleNum, " -- defaulting to 0"]);
      aRuleNum = 0;
    }

    // Get the desired pluralRule function
    let [numForms, pluralFunc] = gFunctions[aRuleNum];

    // Return functions that give 1) the number of forms and 2) gets the right
    // plural form
    return [function(aNum, aWords) {
      // Figure out which index to use for the semi-colon separated words
      let index = pluralFunc(aNum ? Number(aNum) : 0);
      let words = aWords ? aWords.split(/;/) : [""];

      // Explicitly check bounds to avoid strict warnings
      let ret = index < words.length ? words[index] : undefined;

      // Check for array out of bounds or empty strings
      if ((ret == undefined) || (ret == "")) {
        // Display a message in the error console
        log(["Index #", index, " of '", aWords, "' for value ", aNum,
            " is invalid -- plural rule #", aRuleNum, ";"]);

        // Default to the first entry (which might be empty, but not undefined)
        ret = words[0];
      }

      return ret;
    }, () => numForms];
  },

  /**
   * Get the number of forms for the current plural rule
   *
   * @return The number of forms
   */
  get numForms()
  {
    // We lazily load numForms, so trigger the init logic with get()
    this.get();
    return this.numForms;
  },

  /**
   * Get the plural rule number from the intl stringbundle
   *
   * @return The plural rule number
   */
  get ruleNum()
  {
    try {
      return parseInt(L10N.getStr("pluralRule"), 10);
    } catch (e) {
    // Fallback to English if the pluralRule property is not available.
      return 1;
    }
  }
};

/**
 * Private helper function to log errors to the error console and command line
 *
 * @param aMsg
 *        Error message to log or an array of strings to concat
 */
function log(aMsg)
{
  let msg = "plural-form.js: " + (aMsg.join ? aMsg.join("") : aMsg);
  console.log(msg + "\n");
}

exports.PluralForm = PluralForm;

/* eslint-ensable */
PK
!<��
�]�]Bchrome/devtools/modules/devtools/shared/pretty-fast/pretty-fast.js/* -*- indent-tabs-mode: nil; js-indent-level: 2; fill-column: 80 -*- */
/*
 * Copyright 2013 Mozilla Foundation and contributors
 * Licensed under the New BSD license. See LICENSE.md or:
 * http://opensource.org/licenses/BSD-2-Clause
 */
(function (root, factory) {
  "use strict";

  if (typeof define === "function" && define.amd) {
    define(factory);
  } else if (typeof exports === "object") {
    module.exports = factory();
  } else {
    root.prettyFast = factory();
  }
}(this, () => {
  "use strict";

  var acorn = this.acorn || require("acorn/acorn");
  var sourceMap = this.sourceMap || require("source-map");
  var SourceNode = sourceMap.SourceNode;

  // If any of these tokens are seen before a "[" token, we know that "[" token
  // is the start of an array literal, rather than a property access.
  //
  // The only exception is "}", which would need to be disambiguated by
  // parsing. The majority of the time, an open bracket following a closing
  // curly is going to be an array literal, so we brush the complication under
  // the rug, and handle the ambiguity by always assuming that it will be an
  // array literal.
  var PRE_ARRAY_LITERAL_TOKENS = {
    "typeof": true,
    "void": true,
    "delete": true,
    "case": true,
    "do": true,
    "=": true,
    "in": true,
    "{": true,
    "*": true,
    "/": true,
    "%": true,
    "else": true,
    ";": true,
    "++": true,
    "--": true,
    "+": true,
    "-": true,
    "~": true,
    "!": true,
    ":": true,
    "?": true,
    ">>": true,
    ">>>": true,
    "<<": true,
    "||": true,
    "&&": true,
    "<": true,
    ">": true,
    "<=": true,
    ">=": true,
    "instanceof": true,
    "&": true,
    "^": true,
    "|": true,
    "==": true,
    "!=": true,
    "===": true,
    "!==": true,
    ",": true,

    "}": true
  };

  /**
   * Determines if we think that the given token starts an array literal.
   *
   * @param Object token
   *        The token we want to determine if it is an array literal.
   * @param Object lastToken
   *        The last token we added to the pretty printed results.
   *
   * @returns Boolean
   *          True if we believe it is an array literal, false otherwise.
   */
  function isArrayLiteral(token, lastToken) {
    if (token.type.label != "[") {
      return false;
    }
    if (!lastToken) {
      return true;
    }
    if (lastToken.type.isAssign) {
      return true;
    }
    return !!PRE_ARRAY_LITERAL_TOKENS[
      lastToken.type.keyword || lastToken.type.label
    ];
  }

  // If any of these tokens are followed by a token on a new line, we know that
  // ASI cannot happen.
  var PREVENT_ASI_AFTER_TOKENS = {
    // Binary operators
    "*": true,
    "/": true,
    "%": true,
    "+": true,
    "-": true,
    "<<": true,
    ">>": true,
    ">>>": true,
    "<": true,
    ">": true,
    "<=": true,
    ">=": true,
    "instanceof": true,
    "in": true,
    "==": true,
    "!=": true,
    "===": true,
    "!==": true,
    "&": true,
    "^": true,
    "|": true,
    "&&": true,
    "||": true,
    ",": true,
    ".": true,
    "=": true,
    "*=": true,
    "/=": true,
    "%=": true,
    "+=": true,
    "-=": true,
    "<<=": true,
    ">>=": true,
    ">>>=": true,
    "&=": true,
    "^=": true,
    "|=": true,
    // Unary operators
    "delete": true,
    "void": true,
    "typeof": true,
    "~": true,
    "!": true,
    "new": true,
    // Function calls and grouped expressions
    "(": true
  };

  // If any of these tokens are on a line after the token before it, we know
  // that ASI cannot happen.
  var PREVENT_ASI_BEFORE_TOKENS = {
    // Binary operators
    "*": true,
    "/": true,
    "%": true,
    "<<": true,
    ">>": true,
    ">>>": true,
    "<": true,
    ">": true,
    "<=": true,
    ">=": true,
    "instanceof": true,
    "in": true,
    "==": true,
    "!=": true,
    "===": true,
    "!==": true,
    "&": true,
    "^": true,
    "|": true,
    "&&": true,
    "||": true,
    ",": true,
    ".": true,
    "=": true,
    "*=": true,
    "/=": true,
    "%=": true,
    "+=": true,
    "-=": true,
    "<<=": true,
    ">>=": true,
    ">>>=": true,
    "&=": true,
    "^=": true,
    "|=": true,
    // Function calls
    "(": true
  };

  /**
   * Determines if Automatic Semicolon Insertion (ASI) occurs between these
   * tokens.
   *
   * @param Object token
   *        The current token.
   * @param Object lastToken
   *        The last token we added to the pretty printed results.
   *
   * @returns Boolean
   *          True if we believe ASI occurs.
   */
  function isASI(token, lastToken) {
    if (!lastToken) {
      return false;
    }
    if (token.loc.start.line === lastToken.loc.start.line) {
      return false;
    }
    if (PREVENT_ASI_AFTER_TOKENS[
      lastToken.type.label || lastToken.type.keyword
    ]) {
      return false;
    }
    if (PREVENT_ASI_BEFORE_TOKENS[token.type.label || token.type.keyword]) {
      return false;
    }
    return true;
  }

  /**
   * Determine if we have encountered a getter or setter.
   *
   * @param Object token
   *        The current token. If this is a getter or setter, it would be the
   *        property name.
   * @param Object lastToken
   *        The last token we added to the pretty printed results. If this is a
   *        getter or setter, it would be the `get` or `set` keyword
   *        respectively.
   * @param Array stack
   *        The stack of open parens/curlies/brackets/etc.
   *
   * @returns Boolean
   *          True if this is a getter or setter.
   */
  function isGetterOrSetter(token, lastToken, stack) {
    return stack[stack.length - 1] == "{"
      && lastToken
      && lastToken.type.label == "name"
      && (lastToken.value == "get" || lastToken.value == "set")
      && token.type.label == "name";
  }

  /**
   * Determine if we should add a newline after the given token.
   *
   * @param Object token
   *        The token we are looking at.
   * @param Array stack
   *        The stack of open parens/curlies/brackets/etc.
   *
   * @returns Boolean
   *          True if we should add a newline.
   */
  function isLineDelimiter(token, stack) {
    if (token.isArrayLiteral) {
      return true;
    }
    var ttl = token.type.label;
    var top = stack[stack.length - 1];
    return ttl == ";" && top != "("
      || ttl == "{"
      || ttl == "," && top != "("
      || ttl == ":" && (top == "case" || top == "default");
  }

  /**
   * Append the necessary whitespace to the result after we have added the given
   * token.
   *
   * @param Object token
   *        The token that was just added to the result.
   * @param Function write
   *        The function to write to the pretty printed results.
   * @param Array stack
   *        The stack of open parens/curlies/brackets/etc.
   *
   * @returns Boolean
   *          Returns true if we added a newline to result, false in all other
   *          cases.
   */
  function appendNewline(token, write, stack) {
    if (isLineDelimiter(token, stack)) {
      write("\n", token.loc.start.line, token.loc.start.column);
      return true;
    }
    return false;
  }

  /**
   * Determines if we need to add a space between the last token we added and
   * the token we are about to add.
   *
   * @param Object token
   *        The token we are about to add to the pretty printed code.
   * @param Object lastToken
   *        The last token added to the pretty printed code.
   */
  function needsSpaceAfter(token, lastToken) {
    if (lastToken) {
      if (lastToken.type.isLoop) {
        return true;
      }
      if (lastToken.type.isAssign) {
        return true;
      }
      if (lastToken.type.binop != null) {
        return true;
      }

      var ltt = lastToken.type.label;
      if (ltt == "?") {
        return true;
      }
      if (ltt == ":") {
        return true;
      }
      if (ltt == ",") {
        return true;
      }
      if (ltt == ";") {
        return true;
      }

      var ltk = lastToken.type.keyword;
      if (ltk != null) {
        if (ltk == "break" || ltk == "continue" || ltk == "return") {
          return token.type.label != ";";
        }
        if (ltk != "debugger"
            && ltk != "null"
            && ltk != "true"
            && ltk != "false"
            && ltk != "this"
            && ltk != "default") {
          return true;
        }
      }

      if (ltt == ")" && (token.type.label != ")"
                         && token.type.label != "]"
                         && token.type.label != ";"
                         && token.type.label != ","
                         && token.type.label != ".")) {
        return true;
      }
    }

    if (token.type.isAssign) {
      return true;
    }
    if (token.type.binop != null) {
      return true;
    }
    if (token.type.label == "?") {
      return true;
    }

    return false;
  }

  /**
   * Add the required whitespace before this token, whether that is a single
   * space, newline, and/or the indent on fresh lines.
   *
   * @param Object token
   *        The token we are about to add to the pretty printed code.
   * @param Object lastToken
   *        The last token we added to the pretty printed code.
   * @param Boolean addedNewline
   *        Whether we added a newline after adding the last token to the pretty
   *        printed code.
   * @param Function write
   *        The function to write pretty printed code to the result SourceNode.
   * @param Object options
   *        The options object.
   * @param Number indentLevel
   *        The number of indents deep we are.
   * @param Array stack
   *        The stack of open curlies, brackets, etc.
   */
  function prependWhiteSpace(token, lastToken, addedNewline, write, options,
                             indentLevel, stack) {
    var ttk = token.type.keyword;
    var ttl = token.type.label;
    var newlineAdded = addedNewline;
    var ltt = lastToken ? lastToken.type.label : null;

    // Handle whitespace and newlines after "}" here instead of in
    // `isLineDelimiter` because it is only a line delimiter some of the
    // time. For example, we don't want to put "else if" on a new line after
    // the first if's block.
    if (lastToken && ltt == "}") {
      if (ttk == "while" && stack[stack.length - 1] == "do") {
        write(" ",
              lastToken.loc.start.line,
              lastToken.loc.start.column);
      } else if (ttk == "else" ||
                 ttk == "catch" ||
                 ttk == "finally") {
        write(" ",
              lastToken.loc.start.line,
              lastToken.loc.start.column);
      } else if (ttl != "(" &&
                 ttl != ";" &&
                 ttl != "," &&
                 ttl != ")" &&
                 ttl != ".") {
        write("\n",
              lastToken.loc.start.line,
              lastToken.loc.start.column);
        newlineAdded = true;
      }
    }

    if (isGetterOrSetter(token, lastToken, stack)) {
      write(" ",
            lastToken.loc.start.line,
            lastToken.loc.start.column);
    }

    if (ttl == ":" && stack[stack.length - 1] == "?") {
      write(" ",
            lastToken.loc.start.line,
            lastToken.loc.start.column);
    }

    if (lastToken && ltt != "}" && ttk == "else") {
      write(" ",
            lastToken.loc.start.line,
            lastToken.loc.start.column);
    }

    function ensureNewline() {
      if (!newlineAdded) {
        write("\n",
              lastToken.loc.start.line,
              lastToken.loc.start.column);
        newlineAdded = true;
      }
    }

    if (isASI(token, lastToken)) {
      ensureNewline();
    }

    if (decrementsIndent(ttl, stack)) {
      ensureNewline();
    }

    if (newlineAdded) {
      if (ttk == "case" || ttk == "default") {
        write(repeat(options.indent, indentLevel - 1),
              token.loc.start.line,
              token.loc.start.column);
      } else {
        write(repeat(options.indent, indentLevel),
              token.loc.start.line,
              token.loc.start.column);
      }
    } else if (needsSpaceAfter(token, lastToken)) {
      write(" ",
            lastToken.loc.start.line,
            lastToken.loc.start.column);
    }
  }

  /**
   * Repeat the `str` string `n` times.
   *
   * @param String str
   *        The string to be repeated.
   * @param Number n
   *        The number of times to repeat the string.
   *
   * @returns String
   *          The repeated string.
   */
  function repeat(str, n) {
    var result = "";
    while (n > 0) {
      if (n & 1) {
        result += str;
      }
      n >>= 1;
      str += str;
    }
    return result;
  }

  /**
   * Make sure that we output the escaped character combination inside string
   * literals instead of various problematic characters.
   */
  var sanitize = (function () {
    var escapeCharacters = {
      // Backslash
      "\\": "\\\\",
      // Newlines
      "\n": "\\n",
      // Carriage return
      "\r": "\\r",
      // Tab
      "\t": "\\t",
      // Vertical tab
      "\v": "\\v",
      // Form feed
      "\f": "\\f",
      // Null character
      "\0": "\\0",
      // Single quotes
      "'": "\\'"
    };

    var regExpString = "("
      + Object.keys(escapeCharacters)
              .map(function (c) { return escapeCharacters[c]; })
              .join("|")
      + ")";
    var escapeCharactersRegExp = new RegExp(regExpString, "g");

    return function (str) {
      return str.replace(escapeCharactersRegExp, function (_, c) {
        return escapeCharacters[c];
      });
    };
  }());
  /**
   * Add the given token to the pretty printed results.
   *
   * @param Object token
   *        The token to add.
   * @param Function write
   *        The function to write pretty printed code to the result SourceNode.
   */
  function addToken(token, write) {
    if (token.type.label == "string") {
      write("'" + sanitize(token.value) + "'",
            token.loc.start.line,
            token.loc.start.column);
    } else if (token.type.label == "regexp") {
      write(String(token.value.value),
            token.loc.start.line,
            token.loc.start.column);
    } else {
      write(String(token.value != null ? token.value : token.type.label),
            token.loc.start.line,
            token.loc.start.column);
    }
  }

  /**
   * Returns true if the given token type belongs on the stack.
   */
  function belongsOnStack(token) {
    var ttl = token.type.label;
    var ttk = token.type.keyword;
    return ttl == "{"
      || ttl == "("
      || ttl == "["
      || ttl == "?"
      || ttk == "do"
      || ttk == "switch"
      || ttk == "case"
      || ttk == "default";
  }

  /**
   * Returns true if the given token should cause us to pop the stack.
   */
  function shouldStackPop(token, stack) {
    var ttl = token.type.label;
    var ttk = token.type.keyword;
    var top = stack[stack.length - 1];
    return ttl == "]"
      || ttl == ")"
      || ttl == "}"
      || (ttl == ":" && (top == "case" || top == "default" || top == "?"))
      || (ttk == "while" && top == "do");
  }

  /**
   * Returns true if the given token type should cause us to decrement the
   * indent level.
   */
  function decrementsIndent(tokenType, stack) {
    return tokenType == "}"
      || (tokenType == "]" && stack[stack.length - 1] == "[\n");
  }

  /**
   * Returns true if the given token should cause us to increment the indent
   * level.
   */
  function incrementsIndent(token) {
    return token.type.label == "{"
      || token.isArrayLiteral
      || token.type.keyword == "switch";
  }

  /**
   * Add a comment to the pretty printed code.
   *
   * @param Function write
   *        The function to write pretty printed code to the result SourceNode.
   * @param Number indentLevel
   *        The number of indents deep we are.
   * @param Object options
   *        The options object.
   * @param Boolean block
   *        True if the comment is a multiline block style comment.
   * @param String text
   *        The text of the comment.
   * @param Number line
   *        The line number to comment appeared on.
   * @param Number column
   *        The column number the comment appeared on.
   */
  function addComment(write, indentLevel, options, block, text, line, column) {
    var indentString = repeat(options.indent, indentLevel);

    write(indentString, line, column);
    if (block) {
      write("/*");
      write(text
            .split(new RegExp("/\n" + indentString + "/", "g"))
            .join("\n" + indentString));
      write("*/");
    } else {
      write("//");
      write(text);
    }
    write("\n");
  }

  /**
   * The main function.
   *
   * @param String input
   *        The ugly JS code we want to pretty print.
   * @param Object options
   *        The options object. Provides configurability of the pretty
   *        printing. Properties:
   *          - url: The URL string of the ugly JS code.
   *          - indent: The string to indent code by.
   *
   * @returns Object
   *          An object with the following properties:
   *            - code: The pretty printed code string.
   *            - map: A SourceMapGenerator instance.
   */
  return function prettyFast(input, options) {
    // The level of indents deep we are.
    var indentLevel = 0;

    // We will accumulate the pretty printed code in this SourceNode.
    var result = new SourceNode();

    /**
     * Write a pretty printed string to the result SourceNode.
     *
     * We buffer our writes so that we only create one mapping for each line in
     * the source map. This enhances performance by avoiding extraneous mapping
     * serialization, and flattening the tree that
     * `SourceNode#toStringWithSourceMap` will have to recursively walk. When
     * timing how long it takes to pretty print jQuery, this optimization
     * brought the time down from ~390 ms to ~190ms!
     *
     * @param String str
     *        The string to be added to the result.
     * @param Number line
     *        The line number the string came from in the ugly source.
     * @param Number column
     *        The column number the string came from in the ugly source.
     */
    var write = (function () {
      var buffer = [];
      var bufferLine = -1;
      var bufferColumn = -1;
      return function write(str, line, column) {
        if (line != null && bufferLine === -1) {
          bufferLine = line;
        }
        if (column != null && bufferColumn === -1) {
          bufferColumn = column;
        }
        buffer.push(str);

        if (str == "\n") {
          var lineStr = "";
          for (var i = 0, len = buffer.length; i < len; i++) {
            lineStr += buffer[i];
          }
          result.add(new SourceNode(bufferLine, bufferColumn, options.url,
                                    lineStr));
          buffer.splice(0, buffer.length);
          bufferLine = -1;
          bufferColumn = -1;
        }
      };
    }());

    // Whether or not we added a newline on after we added the last token.
    var addedNewline = false;

    // The current token we will be adding to the pretty printed code.
    var token;

    // Shorthand for token.type.label, so we don't have to repeatedly access
    // properties.
    var ttl;

    // Shorthand for token.type.keyword, so we don't have to repeatedly access
    // properties.
    var ttk;

    // The last token we added to the pretty printed code.
    var lastToken;

    // Stack of token types/keywords that can affect whether we want to add a
    // newline or a space. We can make that decision based on what token type is
    // on the top of the stack. For example, a comma in a parameter list should
    // be followed by a space, while a comma in an object literal should be
    // followed by a newline.
    //
    // Strings that go on the stack:
    //
    //   - "{"
    //   - "("
    //   - "["
    //   - "[\n"
    //   - "do"
    //   - "?"
    //   - "switch"
    //   - "case"
    //   - "default"
    //
    // The difference between "[" and "[\n" is that "[\n" is used when we are
    // treating "[" and "]" tokens as line delimiters and should increment and
    // decrement the indent level when we find them.
    var stack = [];

    // Pass through acorn's tokenizer and append tokens and comments into a
    // single queue to process.  For example, the source file:
    //
    //     foo
    //     // a
    //     // b
    //     bar
    //
    // After this process, tokenQueue has the following token stream:
    //
    //     [ foo, '// a', '// b', bar]
    var tokenQueue = [];

    var tokens = acorn.tokenizer(input, {
      locations: true,
      sourceFile: options.url,
      onComment: function (block, text, start, end, startLoc, endLoc) {
        tokenQueue.push({
          type: {},
          comment: true,
          block: block,
          text: text,
          loc: { start: startLoc, end: endLoc }
        });
      }
    });

    for (;;) {
      token = tokens.getToken();
      tokenQueue.push(token);
      if (token.type.label == "eof") {
        break;
      }
    }

    for (var i = 0; i < tokenQueue.length; i++) {
      token = tokenQueue[i];

      if (token.comment) {
        var commentIndentLevel = indentLevel;
        if (lastToken && (lastToken.loc.end.line == token.loc.start.line)) {
          commentIndentLevel = 0;
          write(" ");
        }
        addComment(write, commentIndentLevel, options, token.block, token.text,
                   token.loc.start.line, token.loc.start.column);
        addedNewline = true;
        continue;
      }

      ttk = token.type.keyword;
      ttl = token.type.label;

      if (ttl == "eof") {
        if (!addedNewline) {
          write("\n");
        }
        break;
      }

      token.isArrayLiteral = isArrayLiteral(token, lastToken);

      if (belongsOnStack(token)) {
        if (token.isArrayLiteral) {
          stack.push("[\n");
        } else {
          stack.push(ttl || ttk);
        }
      }

      if (decrementsIndent(ttl, stack)) {
        indentLevel--;
        if (ttl == "}"
            && stack.length > 1
            && stack[stack.length - 2] == "switch") {
          indentLevel--;
        }
      }

      prependWhiteSpace(token, lastToken, addedNewline, write, options,
                        indentLevel, stack);
      addToken(token, write);

      // If the next token is going to be a comment starting on the same line,
      // then no need to add one here
      var nextToken = tokenQueue[i + 1];
      if (!nextToken || !nextToken.comment || token.loc.end.line != nextToken.loc.start.line) {
        addedNewline = appendNewline(token, write, stack);
      }

      if (shouldStackPop(token, stack)) {
        stack.pop();
        if (token == "}" && stack.length
            && stack[stack.length - 1] == "switch") {
          stack.pop();
        }
      }

      if (incrementsIndent(token)) {
        indentLevel++;
      }

      // Acorn's tokenizer re-uses tokens, so we have to copy the last token on
      // every iteration. We follow acorn's lead here, and reuse the lastToken
      // object the same way that acorn reuses the token object. This allows us
      // to avoid allocations and minimize GC pauses.
      if (!lastToken) {
        lastToken = { loc: { start: {}, end: {} } };
      }
      lastToken.start = token.start;
      lastToken.end = token.end;
      lastToken.loc.start.line = token.loc.start.line;
      lastToken.loc.start.column = token.loc.start.column;
      lastToken.loc.end.line = token.loc.end.line;
      lastToken.loc.end.column = token.loc.end.column;
      lastToken.type = token.type;
      lastToken.value = token.value;
      lastToken.isArrayLiteral = token.isArrayLiteral;
    }

    return result.toStringWithSourceMap({ file: options.url });
  };

}));
PK
!<�Og����3chrome/devtools/modules/devtools/shared/protocol.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var promise = require("promise");
var defer = require("devtools/shared/defer");
const { extend } = require("devtools/shared/extend");
var EventEmitter = require("devtools/shared/event-emitter");
var {getStack, callFunctionWithAsyncStack} = require("devtools/shared/platform/stack");
var {settleAll} = require("devtools/shared/DevToolsUtils");
var {lazyLoadSpec, lazyLoadFront} = require("devtools/shared/specs/index");

/**
 * Types: named marshallers/demarshallers.
 *
 * Types provide a 'write' function that takes a js representation and
 * returns a protocol representation, and a "read" function that
 * takes a protocol representation and returns a js representation.
 *
 * The read and write methods are also passed a context object that
 * represent the actor or front requesting the translation.
 *
 * Types are referred to with a typestring.  Basic types are
 * registered by name using addType, and more complex types can
 * be generated by adding detail to the type name.
 */

var types = Object.create(null);
exports.types = types;

var registeredTypes = types.registeredTypes = new Map();
var registeredLifetimes = types.registeredLifetimes = new Map();

/**
 * Return the type object associated with a given typestring.
 * If passed a type object, it will be returned unchanged.
 *
 * Types can be registered with addType, or can be created on
 * the fly with typestrings.  Examples:
 *
 *   boolean
 *   threadActor
 *   threadActor#detail
 *   array:threadActor
 *   array:array:threadActor#detail
 *
 * @param [typestring|type] type
 *    Either a typestring naming a type or a type object.
 *
 * @returns a type object.
 */
types.getType = function (type) {
  if (!type) {
    return types.Primitive;
  }

  if (typeof (type) !== "string") {
    return type;
  }

  // If already registered, we're done here.
  let reg = registeredTypes.get(type);
  if (reg) {
    return reg;
  }

  // Try to lazy load the spec, if not already loaded.
  if (lazyLoadSpec(type)) {
    // If a spec module was lazy loaded, it will synchronously call
    // generateActorSpec, and set the type in `registeredTypes`.
    reg = registeredTypes.get(type);
    if (reg) {
      return reg;
    }
  }

  // New type, see if it's a collection/lifetime type:
  let sep = type.indexOf(":");
  if (sep >= 0) {
    let collection = type.substring(0, sep);
    let subtype = types.getType(type.substring(sep + 1));

    if (collection === "array") {
      return types.addArrayType(subtype);
    } else if (collection === "nullable") {
      return types.addNullableType(subtype);
    }

    if (registeredLifetimes.has(collection)) {
      return types.addLifetimeType(collection, subtype);
    }

    throw Error("Unknown collection type: " + collection);
  }

  // Not a collection, might be actor detail
  let pieces = type.split("#", 2);
  if (pieces.length > 1) {
    return types.addActorDetail(type, pieces[0], pieces[1]);
  }

  throw Error("Unknown type: " + type);
};

/**
 * Don't allow undefined when writing primitive types to packets.  If
 * you want to allow undefined, use a nullable type.
 */
function identityWrite(v) {
  if (v === undefined) {
    throw Error("undefined passed where a value is required");
  }
  // This has to handle iterator->array conversion because arrays of
  // primitive types pass through here.
  if (v && typeof (v) === "object" && Symbol.iterator in v) {
    return [...v];
  }
  return v;
}

/**
 * Add a type to the type system.
 *
 * When registering a type, you can provide `read` and `write` methods.
 *
 * The `read` method will be passed a JS object value from the JSON
 * packet and must return a native representation.  The `write` method will
 * be passed a native representation and should provide a JSONable value.
 *
 * These methods will both be passed a context.  The context is the object
 * performing or servicing the request - on the server side it will be
 * an Actor, on the client side it will be a Front.
 *
 * @param typestring name
 *    Name to register
 * @param object typeObject
 *    An object whose properties will be stored in the type, including
 *    the `read` and `write` methods.
 * @param object options
 *    Can specify `thawed` to prevent the type from being frozen.
 *
 * @returns a type object that can be used in protocol definitions.
 */
types.addType = function (name, typeObject = {}, options = {}) {
  if (registeredTypes.has(name)) {
    throw Error("Type '" + name + "' already exists.");
  }

  let type = Object.assign({
    toString() {
      return "[protocol type:" + name + "]";
    },
    name: name,
    primitive: !(typeObject.read || typeObject.write),
    read: identityWrite,
    write: identityWrite
  }, typeObject);

  registeredTypes.set(name, type);

  return type;
};

/**
 * Remove a type previously registered with the system.
 * Primarily useful for types registered by addons.
 */
types.removeType = function (name) {
  // This type may still be referenced by other types, make sure
  // those references don't work.
  let type = registeredTypes.get(name);

  type.name = "DEFUNCT:" + name;
  type.category = "defunct";
  type.primitive = false;
  type.read = type.write = function () {
    throw new Error("Using defunct type: " + name);
  };

  registeredTypes.delete(name);
};

/**
 * Add an array type to the type system.
 *
 * getType() will call this function if provided an "array:<type>"
 * typestring.
 *
 * @param type subtype
 *    The subtype to be held by the array.
 */
types.addArrayType = function (subtype) {
  subtype = types.getType(subtype);

  let name = "array:" + subtype.name;

  // Arrays of primitive types are primitive types themselves.
  if (subtype.primitive) {
    return types.addType(name);
  }
  return types.addType(name, {
    category: "array",
    read: (v, ctx) => [...v].map(i => subtype.read(i, ctx)),
    write: (v, ctx) => [...v].map(i => subtype.write(i, ctx))
  });
};

/**
 * Add a dict type to the type system.  This allows you to serialize
 * a JS object that contains non-primitive subtypes.
 *
 * Properties of the value that aren't included in the specializations
 * will be serialized as primitive values.
 *
 * @param object specializations
 *    A dict of property names => type
 */
types.addDictType = function (name, specializations) {
  return types.addType(name, {
    category: "dict",
    specializations: specializations,
    read: (v, ctx) => {
      let ret = {};
      for (let prop in v) {
        if (prop in specializations) {
          ret[prop] = types.getType(specializations[prop]).read(v[prop], ctx);
        } else {
          ret[prop] = v[prop];
        }
      }
      return ret;
    },

    write: (v, ctx) => {
      let ret = {};
      for (let prop in v) {
        if (prop in specializations) {
          ret[prop] = types.getType(specializations[prop]).write(v[prop], ctx);
        } else {
          ret[prop] = v[prop];
        }
      }
      return ret;
    }
  });
};

/**
 * Register an actor type with the type system.
 *
 * Types are marshalled differently when communicating server->client
 * than they are when communicating client->server.  The server needs
 * to provide useful information to the client, so uses the actor's
 * `form` method to get a json representation of the actor.  When
 * making a request from the client we only need the actor ID string.
 *
 * This function can be called before the associated actor has been
 * constructed, but the read and write methods won't work until
 * the associated addActorImpl or addActorFront methods have been
 * called during actor/front construction.
 *
 * @param string name
 *    The typestring to register.
 */
types.addActorType = function (name) {
  // We call addActorType from:
  //   FrontClassWithSpec when registering front synchronously,
  //   generateActorSpec when defining specs,
  //   specs modules to register actor type early to use them in other types
  if (registeredTypes.has(name)) {
    return registeredTypes.get(name);
  }
  let type = types.addType(name, {
    _actor: true,
    category: "actor",
    read: (v, ctx, detail) => {
      // If we're reading a request on the server side, just
      // find the actor registered with this actorID.
      if (ctx instanceof Actor) {
        return ctx.conn.getActor(v);
      }

      // Reading a response on the client side, check for an
      // existing front on the connection, and create the front
      // if it isn't found.
      let actorID = typeof (v) === "string" ? v : v.actor;
      let front = ctx.conn.getActor(actorID);
      if (!front) {
        // If front isn't instanciated yet, create one.

        // Try lazy loading front if not already loaded.
        // The front module will synchronously call `FrontClassWithSpec` and
        // augment `type` with the `frontClass` attribute.
        if (!type.frontClass) {
          lazyLoadFront(name);
        }

        front = new type.frontClass(ctx.conn); // eslint-disable-line new-cap
        front.actorID = actorID;
        ctx.marshallPool().manage(front);
      }

      v = type.formType(detail).read(v, front, detail);
      front.form(v, detail, ctx);

      return front;
    },
    write: (v, ctx, detail) => {
      // If returning a response from the server side, make sure
      // the actor is added to a parent object and return its form.
      if (v instanceof Actor) {
        if (!v.actorID) {
          ctx.marshallPool().manage(v);
        }
        return type.formType(detail).write(v.form(detail), ctx, detail);
      }

      // Writing a request from the client side, just send the actor id.
      return v.actorID;
    },
    formType: (detail) => {
      if (!("formType" in type.actorSpec)) {
        return types.Primitive;
      }

      let formAttr = "formType";
      if (detail) {
        formAttr += "#" + detail;
      }

      if (!(formAttr in type.actorSpec)) {
        throw new Error("No type defined for " + formAttr);
      }

      return type.actorSpec[formAttr];
    }
  });
  return type;
};

types.addNullableType = function (subtype) {
  subtype = types.getType(subtype);
  return types.addType("nullable:" + subtype.name, {
    category: "nullable",
    read: (value, ctx) => {
      if (value == null) {
        return value;
      }
      return subtype.read(value, ctx);
    },
    write: (value, ctx) => {
      if (value == null) {
        return value;
      }
      return subtype.write(value, ctx);
    }
  });
};

/**
 * Register an actor detail type.  This is just like an actor type, but
 * will pass a detail hint to the actor's form method during serialization/
 * deserialization.
 *
 * This is called by getType() when passed an 'actorType#detail' string.
 *
 * @param string name
 *   The typestring to register this type as.
 * @param type actorType
 *   The actor type you'll be detailing.
 * @param string detail
 *   The detail to pass.
 */
types.addActorDetail = function (name, actorType, detail) {
  actorType = types.getType(actorType);
  if (!actorType._actor) {
    throw Error(`Details only apply to actor types, tried to add detail '${detail}' ` +
                `to ${actorType.name}`);
  }
  return types.addType(name, {
    _actor: true,
    category: "detail",
    read: (v, ctx) => actorType.read(v, ctx, detail),
    write: (v, ctx) => actorType.write(v, ctx, detail)
  });
};

/**
 * Register an actor lifetime.  This lets the type system find a parent
 * actor that differs from the actor fulfilling the request.
 *
 * @param string name
 *    The lifetime name to use in typestrings.
 * @param string prop
 *    The property of the actor that holds the parent that should be used.
 */
types.addLifetime = function (name, prop) {
  if (registeredLifetimes.has(name)) {
    throw Error("Lifetime '" + name + "' already registered.");
  }
  registeredLifetimes.set(name, prop);
};

/**
 * Remove a previously-registered lifetime.  Useful for lifetimes registered
 * in addons.
 */
types.removeLifetime = function (name) {
  registeredLifetimes.delete(name);
};

/**
 * Register a lifetime type.  This creates an actor type tied to the given
 * lifetime.
 *
 * This is called by getType() when passed a '<lifetimeType>:<actorType>'
 * typestring.
 *
 * @param string lifetime
 *    A lifetime string previously regisered with addLifetime()
 * @param type subtype
 *    An actor type
 */
types.addLifetimeType = function (lifetime, subtype) {
  subtype = types.getType(subtype);
  if (!subtype._actor) {
    throw Error(`Lifetimes only apply to actor types, tried to apply ` +
                `lifetime '${lifetime}' to ${subtype.name}`);
  }
  let prop = registeredLifetimes.get(lifetime);
  return types.addType(lifetime + ":" + subtype.name, {
    category: "lifetime",
    read: (value, ctx) => subtype.read(value, ctx[prop]),
    write: (value, ctx) => subtype.write(value, ctx[prop])
  });
};

// Add a few named primitive types.
types.Primitive = types.addType("primitive");
types.String = types.addType("string");
types.Number = types.addType("number");
types.Boolean = types.addType("boolean");
types.JSON = types.addType("json");

/**
 * Request/Response templates and generation
 *
 * Request packets are specified as json templates with
 * Arg and Option placeholders where arguments should be
 * placed.
 *
 * Reponse packets are also specified as json templates,
 * with a RetVal placeholder where the return value should be
 * placed.
 */

/**
 * Placeholder for simple arguments.
 *
 * @param number index
 *    The argument index to place at this position.
 * @param type type
 *    The argument should be marshalled as this type.
 * @constructor
 */
var Arg = function (index, type) {
  this.index = index;
  // Prevent force loading all Arg types by accessing it only when needed
  loader.lazyGetter(this, "type", function () {
    return types.getType(type);
  });
};

Arg.prototype = {
  write: function (arg, ctx) {
    return this.type.write(arg, ctx);
  },

  read: function (v, ctx, outArgs) {
    outArgs[this.index] = this.type.read(v, ctx);
  },

  describe: function () {
    return {
      _arg: this.index,
      type: this.type.name,
    };
  }
};

// Outside of protocol.js, Arg is called as factory method, without the new keyword.
exports.Arg = function (index, type) {
  return new Arg(index, type);
};

/**
 * Placeholder for an options argument value that should be hoisted
 * into the packet.
 *
 * If provided in a method specification:
 *
 *   { optionArg: Option(1)}
 *
 * Then arguments[1].optionArg will be placed in the packet in this
 * value's place.
 *
 * @param number index
 *    The argument index of the options value.
 * @param type type
 *    The argument should be marshalled as this type.
 * @constructor
 */
var Option = function (index, type) {
  Arg.call(this, index, type);
};

Option.prototype = extend(Arg.prototype, {
  write: function (arg, ctx, name) {
    // Ignore if arg is undefined or null; allow other falsy values
    if (arg == undefined || arg[name] == undefined) {
      return undefined;
    }
    let v = arg[name];
    return this.type.write(v, ctx);
  },
  read: function (v, ctx, outArgs, name) {
    if (outArgs[this.index] === undefined) {
      outArgs[this.index] = {};
    }
    if (v === undefined) {
      return;
    }
    outArgs[this.index][name] = this.type.read(v, ctx);
  },

  describe: function () {
    return {
      _option: this.index,
      type: this.type.name,
    };
  }
});

// Outside of protocol.js, Option is called as factory method, without the new keyword.
exports.Option = function (index, type) {
  return new Option(index, type);
};

/**
 * Placeholder for return values in a response template.
 *
 * @param type type
 *    The return value should be marshalled as this type.
 */
var RetVal = function (type) {
  // Prevent force loading all RetVal types by accessing it only when needed
  loader.lazyGetter(this, "type", function () {
    return types.getType(type);
  });
};

RetVal.prototype = {
  write: function (v, ctx) {
    return this.type.write(v, ctx);
  },

  read: function (v, ctx) {
    return this.type.read(v, ctx);
  },

  describe: function () {
    return {
      _retval: this.type.name
    };
  }
};

// Outside of protocol.js, RetVal is called as factory method, without the new keyword.
exports.RetVal = function (type) {
  return new RetVal(type);
};

/* Template handling functions */

/**
 * Get the value at a given path, or undefined if not found.
 */
function getPath(obj, path) {
  for (let name of path) {
    if (!(name in obj)) {
      return undefined;
    }
    obj = obj[name];
  }
  return obj;
}

/**
 * Find Placeholders in the template and save them along with their
 * paths.
 */
function findPlaceholders(template, constructor, path = [], placeholders = []) {
  if (!template || typeof (template) != "object") {
    return placeholders;
  }

  if (template instanceof constructor) {
    placeholders.push({ placeholder: template, path: [...path] });
    return placeholders;
  }

  for (let name in template) {
    path.push(name);
    findPlaceholders(template[name], constructor, path, placeholders);
    path.pop();
  }

  return placeholders;
}

function describeTemplate(template) {
  return JSON.parse(JSON.stringify(template, (key, value) => {
    if (value.describe) {
      return value.describe();
    }
    return value;
  }));
}

/**
 * Manages a request template.
 *
 * @param object template
 *    The request template.
 * @construcor
 */
var Request = function (template = {}) {
  this.type = template.type;
  this.template = template;
  this.args = findPlaceholders(template, Arg);
};

Request.prototype = {
  /**
   * Write a request.
   *
   * @param array fnArgs
   *    The function arguments to place in the request.
   * @param object ctx
   *    The object making the request.
   * @returns a request packet.
   */
  write: function (fnArgs, ctx) {
    let str = JSON.stringify(this.template, (key, value) => {
      if (value instanceof Arg) {
        return value.write(value.index in fnArgs ? fnArgs[value.index] : undefined,
                           ctx, key);
      }
      return value;
    });
    return JSON.parse(str);
  },

  /**
   * Read a request.
   *
   * @param object packet
   *    The request packet.
   * @param object ctx
   *    The object making the request.
   * @returns an arguments array
   */
  read: function (packet, ctx) {
    let fnArgs = [];
    for (let templateArg of this.args) {
      let arg = templateArg.placeholder;
      let path = templateArg.path;
      let name = path[path.length - 1];
      arg.read(getPath(packet, path), ctx, fnArgs, name);
    }
    return fnArgs;
  },

  describe: function () {
    return describeTemplate(this.template);
  }
};

/**
 * Manages a response template.
 *
 * @param object template
 *    The response template.
 * @construcor
 */
var Response = function (template = {}) {
  this.template = template;
  let placeholders = findPlaceholders(template, RetVal);
  if (placeholders.length > 1) {
    throw Error("More than one RetVal specified in response");
  }
  let placeholder = placeholders.shift();
  if (placeholder) {
    this.retVal = placeholder.placeholder;
    this.path = placeholder.path;
  }
};

Response.prototype = {
  /**
   * Write a response for the given return value.
   *
   * @param val ret
   *    The return value.
   * @param object ctx
   *    The object writing the response.
   */
  write: function (ret, ctx) {
    return JSON.parse(JSON.stringify(this.template, function (key, value) {
      if (value instanceof RetVal) {
        return value.write(ret, ctx);
      }
      return value;
    }));
  },

  /**
   * Read a return value from the given response.
   *
   * @param object packet
   *    The response packet.
   * @param object ctx
   *    The object reading the response.
   */
  read: function (packet, ctx) {
    if (!this.retVal) {
      return undefined;
    }
    let v = getPath(packet, this.path);
    return this.retVal.read(v, ctx);
  },

  describe: function () {
    return describeTemplate(this.template);
  }
};

/**
 * Actor and Front implementations
 */

/**
 * A protocol object that can manage the lifetime of other protocol
 * objects. Pools are used on both sides of the connection to help coordinate lifetimes.
 *
 * @param optional conn
 *   Either a DebuggerServerConnection or a DebuggerClient.  Must have
 *   addActorPool, removeActorPool, and poolFor.
 *   conn can be null if the subclass provides a conn property.
 * @constructor
 */
var Pool = function (conn) {
  if (conn) {
    this.conn = conn;
  }
};

Pool.prototype = extend(EventEmitter.prototype, {
  /**
   * Return the parent pool for this client.
   */
  parent: function () {
    return this.conn.poolFor(this.actorID);
  },

  /**
   * Override this if you want actors returned by this actor
   * to belong to a different actor by default.
   */
  marshallPool: function () {
    return this;
  },

  /**
   * Pool is the base class for all actors, even leaf nodes.
   * If the child map is actually referenced, go ahead and create
   * the stuff needed by the pool.
   */
  __poolMap: null,
  get _poolMap() {
    if (this.__poolMap) {
      return this.__poolMap;
    }
    this.__poolMap = new Map();
    this.conn.addActorPool(this);
    return this.__poolMap;
  },

  /**
   * Add an actor as a child of this pool.
   */
  manage: function (actor) {
    if (!actor.actorID) {
      actor.actorID = this.conn.allocID(actor.actorPrefix || actor.typeName);
    }

    this._poolMap.set(actor.actorID, actor);
    return actor;
  },

  /**
   * Remove an actor as a child of this pool.
   */
  unmanage: function (actor) {
    this.__poolMap && this.__poolMap.delete(actor.actorID);
  },

  // true if the given actor ID exists in the pool.
  has: function (actorID) {
    return this.__poolMap && this._poolMap.has(actorID);
  },

  // The actor for a given actor id stored in this pool
  actor: function (actorID) {
    return this.__poolMap ? this._poolMap.get(actorID) : null;
  },

  // Same as actor, should update debugger connection to use 'actor'
  // and then remove this.
  get: function (actorID) {
    return this.__poolMap ? this._poolMap.get(actorID) : null;
  },

  // True if this pool has no children.
  isEmpty: function () {
    return !this.__poolMap || this._poolMap.size == 0;
  },

  // Generator that yields each non-self child of the pool.
  poolChildren: function* () {
    if (!this.__poolMap) {
      return;
    }
    for (let actor of this.__poolMap.values()) {
      // Self-owned actors are ok, but don't need visiting twice.
      if (actor === this) {
        continue;
      }
      yield actor;
    }
  },

  /**
   * Destroy this item, removing it from a parent if it has one,
   * and destroying all children if necessary.
   */
  destroy: function () {
    let parent = this.parent();
    if (parent) {
      parent.unmanage(this);
    }
    if (!this.__poolMap) {
      return;
    }
    for (let actor of this.__poolMap.values()) {
      // Self-owned actors are ok, but don't need destroying twice.
      if (actor === this) {
        continue;
      }
      let destroy = actor.destroy;
      if (destroy) {
        // Disconnect destroy while we're destroying in case of (misbehaving)
        // circular ownership.
        actor.destroy = null;
        destroy.call(actor);
        actor.destroy = destroy;
      }
    }
    this.conn.removeActorPool(this, true);
    this.__poolMap.clear();
    this.__poolMap = null;
  },

  /**
   * For getting along with the debugger server pools, should be removable
   * eventually.
   */
  cleanup: function () {
    this.destroy();
  }
});
exports.Pool = Pool;

/**
 * An actor in the actor tree.
 *
 * @param optional conn
 *   Either a DebuggerServerConnection or a DebuggerClient.  Must have
 *   addActorPool, removeActorPool, and poolFor.
 *   conn can be null if the subclass provides a conn property.
 * @constructor
 */
var Actor = function (conn) {
  Pool.call(this, conn);

  // Forward events to the connection.
  if (this._actorSpec && this._actorSpec.events) {
    for (let key of this._actorSpec.events.keys()) {
      let name = key;
      let sendEvent = this._sendEvent.bind(this, name);
      this.on(name, (...args) => {
        sendEvent.apply(null, args);
      });
    }
  }
};

Actor.prototype = extend(Pool.prototype, {
  // Will contain the actor's ID
  actorID: null,

  // Existing Actors extending this class expect initialize to contain constructor logic.
  initialize: Actor,

  toString: function () {
    return "[Actor " + this.typeName + "/" + this.actorID + "]";
  },

  _sendEvent: function (name, ...args) {
    if (!this._actorSpec.events.has(name)) {
      // It's ok to emit events that don't go over the wire.
      return;
    }
    let request = this._actorSpec.events.get(name);
    let packet;
    try {
      packet = request.write(args, this);
    } catch (ex) {
      console.error("Error sending event: " + name);
      throw ex;
    }
    packet.from = packet.from || this.actorID;
    this.conn.send(packet);
  },

  destroy: function () {
    Pool.prototype.destroy.call(this);
    this.actorID = null;
  },

  /**
   * Override this method in subclasses to serialize the actor.
   * @param [optional] string hint
   *   Optional string to customize the form.
   * @returns A jsonable object.
   */
  form: function (hint) {
    return { actor: this.actorID };
  },

  writeError: function (error) {
    console.error(error);
    if (error.stack) {
      dump(error.stack);
    }
    this.conn.send({
      from: this.actorID,
      error: error.error || "unknownError",
      message: error.message
    });
  },

  _queueResponse: function (create) {
    let pending = this._pendingResponse || promise.resolve(null);
    let response = create(pending);
    this._pendingResponse = response;
  }
});
exports.Actor = Actor;

/**
 * Tags a prtotype method as an actor method implementation.
 *
 * @param function fn
 *    The implementation function, will be returned.
 * @param spec
 *    The method specification, with the following (optional) properties:
 *      request (object): a request template.
 *      response (object): a response template.
 *      oneway (bool): 'true' if no response should be sent.
 */
exports.method = function (fn, spec = {}) {
  fn._methodSpec = Object.freeze(spec);
  if (spec.request) {
    Object.freeze(spec.request);
  }
  if (spec.response) {
    Object.freeze(spec.response);
  }
  return fn;
};

/**
 * Generates an actor specification from an actor description.
 */
var generateActorSpec = function (actorDesc) {
  let actorSpec = {
    typeName: actorDesc.typeName,
    methods: []
  };

  // Find method and form specifications attached to properties.
  for (let name of Object.getOwnPropertyNames(actorDesc)) {
    let desc = Object.getOwnPropertyDescriptor(actorDesc, name);
    if (!desc.value) {
      continue;
    }

    if (name.startsWith("formType")) {
      if (typeof (desc.value) === "string") {
        actorSpec[name] = types.getType(desc.value);
      } else if (desc.value.name && registeredTypes.has(desc.value.name)) {
        actorSpec[name] = desc.value;
      } else {
        // Shorthand for a newly-registered DictType.
        actorSpec[name] = types.addDictType(actorDesc.typeName + "__" + name, desc.value);
      }
    }

    if (desc.value._methodSpec) {
      let methodSpec = desc.value._methodSpec;
      let spec = {};
      spec.name = methodSpec.name || name;
      spec.request = new Request(Object.assign({type: spec.name},
                                          methodSpec.request || undefined));
      spec.response = new Response(methodSpec.response || undefined);
      spec.release = methodSpec.release;
      spec.oneway = methodSpec.oneway;

      actorSpec.methods.push(spec);
    }
  }

  // Find additional method specifications
  if (actorDesc.methods) {
    for (let name in actorDesc.methods) {
      let methodSpec = actorDesc.methods[name];
      let spec = {};

      spec.name = methodSpec.name || name;
      spec.request = new Request(Object.assign({type: spec.name},
                                          methodSpec.request || undefined));
      spec.response = new Response(methodSpec.response || undefined);
      spec.release = methodSpec.release;
      spec.oneway = methodSpec.oneway;

      actorSpec.methods.push(spec);
    }
  }

  // Find event specifications
  if (actorDesc.events) {
    actorSpec.events = new Map();
    for (let name in actorDesc.events) {
      let eventRequest = actorDesc.events[name];
      Object.freeze(eventRequest);
      actorSpec.events.set(name, new Request(Object.assign({type: name}, eventRequest)));
    }
  }

  if (!registeredTypes.has(actorSpec.typeName)) {
    types.addActorType(actorSpec.typeName);
  }
  registeredTypes.get(actorSpec.typeName).actorSpec = actorSpec;

  return actorSpec;
};
exports.generateActorSpec = generateActorSpec;

/**
 * Generates request handlers as described by the given actor specification on
 * the given actor prototype. Returns the actor prototype.
 */
var generateRequestHandlers = function (actorSpec, actorProto) {
  if (actorProto._actorSpec) {
    throw new Error("actorProto called twice on the same actor prototype!");
  }

  actorProto.typeName = actorSpec.typeName;

  // Generate request handlers for each method definition
  actorProto.requestTypes = Object.create(null);
  actorSpec.methods.forEach(spec => {
    let handler = function (packet, conn) {
      try {
        let args;
        try {
          args = spec.request.read(packet, this);
        } catch (ex) {
          console.error("Error reading request: " + packet.type);
          throw ex;
        }

        let ret = this[spec.name].apply(this, args);

        let sendReturn = (retToSend) => {
          if (spec.oneway) {
            // No need to send a response.
            return;
          }

          let response;
          try {
            response = spec.response.write(retToSend, this);
          } catch (ex) {
            console.error("Error writing response to: " + spec.name);
            throw ex;
          }
          response.from = this.actorID;
          // If spec.release has been specified, destroy the object.
          if (spec.release) {
            try {
              this.destroy();
            } catch (e) {
              this.writeError(e);
              return;
            }
          }

          conn.send(response);
        };

        this._queueResponse(p => {
          return p
            .then(() => ret)
            .then(sendReturn)
            .catch(this.writeError.bind(this));
        });
      } catch (e) {
        this._queueResponse(p => {
          return p.then(() => this.writeError(e));
        });
      }
    };

    actorProto.requestTypes[spec.request.type] = handler;
  });

  actorProto._actorSpec = actorSpec;

  return actorProto;
};

/**
 * Create an actor class for the given actor specification and prototype.
 *
 * @param object actorSpec
 *    The actor specification. Must have a 'typeName' property.
 * @param object actorProto
 *    The actor prototype. Should have method definitions, can have event
 *    definitions.
 */
var ActorClassWithSpec = function (actorSpec, actorProto) {
  if (!actorSpec.typeName) {
    throw Error("Actor specification must have a typeName member.");
  }

  // Existing Actors are relying on the initialize instead of constructor methods.
  let cls = function () {
    let instance = Object.create(cls.prototype);
    instance.initialize.apply(instance, arguments);
    return instance;
  };
  cls.prototype = extend(Actor.prototype, generateRequestHandlers(actorSpec, actorProto));

  return cls;
};
exports.ActorClassWithSpec = ActorClassWithSpec;

/**
 * Base class for client-side actor fronts.
 *
 * @param optional conn
 *   Either a DebuggerServerConnection or a DebuggerClient.  Must have
 *   addActorPool, removeActorPool, and poolFor.
 *   conn can be null if the subclass provides a conn property.
 * @param optional form
 *   The json form provided by the server.
 * @constructor
 */
var Front = function (conn = null, form = null, detail = null, context = null) {
  Pool.call(this, conn);
  this._requests = [];

  // protocol.js no longer uses this data in the constructor, only external
  // uses do.  External usage of manually-constructed fronts will be
  // drastically reduced if we convert the root and tab actors to
  // protocol.js, in which case this can probably go away.
  if (form) {
    this.actorID = form.actor;
    form = types.getType(this.typeName).formType(detail).read(form, this, detail);
    this.form(form, detail, context);
  }
};

Front.prototype = extend(Pool.prototype, {
  actorID: null,

  // Existing Fronts extending this class expect initialize to contain constructor logic.
  initialize: Front,

  destroy: function () {
    // Reject all outstanding requests, they won't make sense after
    // the front is destroyed.
    while (this._requests && this._requests.length > 0) {
      let { deferred, to, type, stack } = this._requests.shift();
      let msg = "Connection closed, pending request to " + to +
                ", type " + type + " failed" +
                "\n\nRequest stack:\n" + stack.formattedStack;
      deferred.reject(new Error(msg));
    }
    Pool.prototype.destroy.call(this);
    this.actorID = null;
  },

  manage: function (front) {
    if (!front.actorID) {
      throw new Error("Can't manage front without an actor ID.\n" +
                      "Ensure server supports " + front.typeName + ".");
    }
    return Pool.prototype.manage.call(this, front);
  },

  /**
   * @returns a promise that will resolve to the actorID this front
   * represents.
   */
  actor: function () {
    return promise.resolve(this.actorID);
  },

  toString: function () {
    return "[Front for " + this.typeName + "/" + this.actorID + "]";
  },

  /**
   * Update the actor from its representation.
   * Subclasses should override this.
   */
  form: function (form) {},

  /**
   * Send a packet on the connection.
   */
  send: function (packet) {
    if (packet.to) {
      this.conn._transport.send(packet);
    } else {
      this.actor().then(actorID => {
        packet.to = actorID;
        this.conn._transport.send(packet);
      }).catch(console.error);
    }
  },

  /**
   * Send a two-way request on the connection.
   */
  request: function (packet) {
    let deferred = defer();
    // Save packet basics for debugging
    let { to, type } = packet;
    this._requests.push({
      deferred,
      to: to || this.actorID,
      type,
      stack: getStack(),
    });
    this.send(packet);
    return deferred.promise;
  },

  /**
   * Handler for incoming packets from the client's actor.
   */
  onPacket: function (packet) {
    // Pick off event packets
    let type = packet.type || undefined;
    if (this._clientSpec.events && this._clientSpec.events.has(type)) {
      let event = this._clientSpec.events.get(packet.type);
      let args;
      try {
        args = event.request.read(packet, this);
      } catch (ex) {
        console.error("Error reading event: " + packet.type);
        console.exception(ex);
        throw ex;
      }
      if (event.pre) {
        let results = event.pre.map(pre => pre.apply(this, args));

        // Check to see if any of the preEvents returned a promise -- if so,
        // wait for their resolution before emitting. Otherwise, emit synchronously.
        if (results.some(result => result && typeof result.then === "function")) {
          promise.all(results).then(() => {
            return EventEmitter.emit.apply(null, [this, event.name].concat(args));
          });
          return;
        }
      }

      EventEmitter.emit.apply(null, [this, event.name].concat(args));
      return;
    }

    // Remaining packets must be responses.
    if (this._requests.length === 0) {
      let msg = "Unexpected packet " + this.actorID + ", " + JSON.stringify(packet);
      let err = Error(msg);
      console.error(err);
      throw err;
    }

    let { deferred, stack } = this._requests.shift();
    callFunctionWithAsyncStack(() => {
      if (packet.error) {
        // "Protocol error" is here to avoid TBPL heuristics. See also
        // https://dxr.mozilla.org/webtools-central/source/tbpl/php/inc/GeneralErrorFilter.php
        let message;
        if (packet.error && packet.message) {
          message = "Protocol error (" + packet.error + "): " + packet.message;
        } else {
          message = packet.error;
        }
        deferred.reject(message);
      } else {
        deferred.resolve(packet);
      }
    }, stack, "DevTools RDP");
  },

  hasRequests() {
    return !!this._requests.length;
  },

  /**
   * Wait for all current requests from this front to settle.  This is especially useful
   * for tests and other utility environments that may not have events or mechanisms to
   * await the completion of requests without this utility.
   *
   * @return Promise
   *         Resolved when all requests have settled.
   */
  waitForRequestsToSettle() {
    return settleAll(this._requests.map(({ deferred }) => deferred.promise));
  },
});

exports.Front = Front;

/**
 * A method tagged with preEvent will be called after recieving a packet
 * for that event, and before the front emits the event.
 */
exports.preEvent = function (eventName, fn) {
  fn._preEvent = eventName;
  return fn;
};

/**
 * Mark a method as a custom front implementation, replacing the generated
 * front method.
 *
 * @param function fn
 *    The front implementation, will be returned.
 * @param object options
 *    Options object:
 *      impl (string): If provided, the generated front method will be
 *        stored as this property on the prototype.
 */
exports.custom = function (fn, options = {}) {
  fn._customFront = options;
  return fn;
};

/**
 * Generates request methods as described by the given actor specification on
 * the given front prototype. Returns the front prototype.
 */
var generateRequestMethods = function (actorSpec, frontProto) {
  if (frontProto._actorSpec) {
    throw new Error("frontProto called twice on the same front prototype!");
  }

  frontProto.typeName = actorSpec.typeName;

  // Generate request methods.
  let methods = actorSpec.methods;
  methods.forEach(spec => {
    let name = spec.name;

    // If there's already a property by this name in the front, it must
    // be a custom front method.
    if (name in frontProto) {
      let custom = frontProto[spec.name]._customFront;
      if (custom === undefined) {
        throw Error(`Existing method for ${spec.name} not marked customFront while ` +
                    ` processing ${actorSpec.typeName}.`);
      }
      // If the user doesn't need the impl don't generate it.
      if (!custom.impl) {
        return;
      }
      name = custom.impl;
    }

    frontProto[name] = function (...args) {
      let packet;
      try {
        packet = spec.request.write(args, this);
      } catch (ex) {
        console.error("Error writing request: " + name);
        throw ex;
      }
      if (spec.oneway) {
        // Fire-and-forget oneway packets.
        this.send(packet);
        return undefined;
      }

      return this.request(packet).then(response => {
        let ret;
        try {
          ret = spec.response.read(response, this);
        } catch (ex) {
          console.error("Error reading response to: " + name);
          throw ex;
        }
        return ret;
      });
    };

    // Release methods should call the destroy function on return.
    if (spec.release) {
      let fn = frontProto[name];
      frontProto[name] = function (...args) {
        return fn.apply(this, args).then(result => {
          this.destroy();
          return result;
        });
      };
    }
  });

  // Process event specifications
  frontProto._clientSpec = {};

  let actorEvents = actorSpec.events;
  if (actorEvents) {
    // This actor has events, scan the prototype for preEvent handlers...
    let preHandlers = new Map();
    for (let name of Object.getOwnPropertyNames(frontProto)) {
      let desc = Object.getOwnPropertyDescriptor(frontProto, name);
      if (!desc.value) {
        continue;
      }
      if (desc.value._preEvent) {
        let preEvent = desc.value._preEvent;
        if (!actorEvents.has(preEvent)) {
          throw Error("preEvent for event that doesn't exist: " + preEvent);
        }
        let handlers = preHandlers.get(preEvent);
        if (!handlers) {
          handlers = [];
          preHandlers.set(preEvent, handlers);
        }
        handlers.push(desc.value);
      }
    }

    frontProto._clientSpec.events = new Map();

    for (let [name, request] of actorEvents) {
      frontProto._clientSpec.events.set(request.type, {
        name: name,
        request: request,
        pre: preHandlers.get(name)
      });
    }
  }

  frontProto._actorSpec = actorSpec;

  return frontProto;
};

/**
 * Create a front class for the given actor specification and front prototype.
 *
 * @param object actorSpec
 *    The actor specification you're creating a front for.
 * @param object proto
 *    The object prototype.  Must have a 'typeName' property,
 *    should have method definitions, can have event definitions.
 */
var FrontClassWithSpec = function (actorSpec, frontProto) {
  // Existing Fronts are relying on the initialize instead of constructor methods.
  let cls = function () {
    let instance = Object.create(cls.prototype);
    instance.initialize.apply(instance, arguments);
    return instance;
  };
  cls.prototype = extend(Front.prototype, generateRequestMethods(actorSpec, frontProto));

  if (!registeredTypes.has(actorSpec.typeName)) {
    types.addActorType(actorSpec.typeName);
  }
  registeredTypes.get(actorSpec.typeName).frontClass = cls;

  return cls;
};
exports.FrontClassWithSpec = FrontClassWithSpec;

exports.dumpActorSpec = function (type) {
  let actorSpec = type.actorSpec;
  let ret = {
    category: "actor",
    typeName: type.name,
    methods: [],
    events: {}
  };

  for (let method of actorSpec.methods) {
    ret.methods.push({
      name: method.name,
      release: method.release || undefined,
      oneway: method.oneway || undefined,
      request: method.request.describe(),
      response: method.response.describe()
    });
  }

  if (actorSpec.events) {
    for (let [name, request] of actorSpec.events) {
      ret.events[name] = request.describe();
    }
  }

  JSON.stringify(ret);

  return ret;
};

exports.dumpProtocolSpec = function () {
  let ret = {
    types: {},
  };

  for (let [name, type] of registeredTypes) {
    // Force lazy instantiation if needed.
    type = types.getType(name);
    let category = type.category || undefined;
    if (category === "dict") {
      ret.types[name] = {
        category: "dict",
        typeName: name,
        specializations: type.specializations
      };
    } else if (category === "actor") {
      ret.types[name] = exports.dumpActorSpec(type);
    }
  }

  return ret;
};
PK
!<�)��P\P\?chrome/devtools/modules/devtools/shared/qrcode/decoder/index.js/*
  Ported to JavaScript by Lazar Laszlo 2011

  [email protected], www.lazarsoft.info
*/
/*
*
* Copyright 2007 ZXing authors
*
* 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 imgU8 = null;

var imgU32 = null;

var imgWidth = 0;

var imgHeight = 0;

var maxImgSize = 1024 * 1024;

var sizeOfDataLengthInfo = [ [ 10, 9, 8, 8 ], [ 12, 11, 16, 10 ], [ 14, 13, 16, 12 ] ];

var GridSampler = {};

GridSampler.checkAndNudgePoints = function(image, points) {
  let width = imgWidth;
  let height = imgHeight;
  let nudged = true;
  for (let offset = 0; offset < points.length && nudged; offset += 2) {
    let x = Math.floor(points[offset]);
    let y = Math.floor(points[offset + 1]);
    if (x < -1 || x > width || y < -1 || y > height) {
      throw "Error.checkAndNudgePoints ";
    }
    nudged = false;
    if (x == -1) {
      points[offset] = 0;
      nudged = true;
    } else if (x == width) {
      points[offset] = width - 1;
      nudged = true;
    }
    if (y == -1) {
      points[offset + 1] = 0;
      nudged = true;
    } else if (y == height) {
      points[offset + 1] = height - 1;
      nudged = true;
    }
  }
  nudged = true;
  for (let offset = points.length - 2; offset >= 0 && nudged; offset -= 2) {
    let x = Math.floor(points[offset]);
    let y = Math.floor(points[offset + 1]);
    if (x < -1 || x > width || y < -1 || y > height) {
      throw "Error.checkAndNudgePoints ";
    }
    nudged = false;
    if (x == -1) {
      points[offset] = 0;
      nudged = true;
    } else if (x == width) {
      points[offset] = width - 1;
      nudged = true;
    }
    if (y == -1) {
      points[offset + 1] = 0;
      nudged = true;
    } else if (y == height) {
      points[offset + 1] = height - 1;
      nudged = true;
    }
  }
};

GridSampler.sampleGrid3 = function(image, dimension, transform) {
  let bits = new BitMatrix(dimension);
  let points = new Array(dimension << 1);
  for (let y = 0; y < dimension; y++) {
    let max = points.length;
    let iValue = y + 0.5;
    for (let x = 0; x < max; x += 2) {
      points[x] = (x >> 1) + 0.5;
      points[x + 1] = iValue;
    }
    transform.transformPoints1(points);
    GridSampler.checkAndNudgePoints(image, points);
    try {
      for (let x = 0; x < max; x += 2) {
        let xpoint = Math.floor(points[x]) * 4 + Math.floor(points[x + 1]) * imgWidth * 4;
        let bit = image[Math.floor(points[x]) + imgWidth * Math.floor(points[x + 1])];
        imgU8[xpoint] = bit ? 255 : 0;
        imgU8[xpoint + 1] = bit ? 255 : 0;
        imgU8[xpoint + 2] = 0;
        imgU8[xpoint + 3] = 255;
        if (bit) bits.set_Renamed(x >> 1, y);
      }
    } catch (aioobe) {
      throw "Error.checkAndNudgePoints";
    }
  }
  return bits;
};

GridSampler.sampleGridx = function(image, dimension, p1ToX, p1ToY, p2ToX, p2ToY, p3ToX, p3ToY, p4ToX, p4ToY, p1FromX, p1FromY, p2FromX, p2FromY, p3FromX, p3FromY, p4FromX, p4FromY) {
  let transform = PerspectiveTransform.quadrilateralToQuadrilateral(p1ToX, p1ToY, p2ToX, p2ToY, p3ToX, p3ToY, p4ToX, p4ToY, p1FromX, p1FromY, p2FromX, p2FromY, p3FromX, p3FromY, p4FromX, p4FromY);
  return GridSampler.sampleGrid3(image, dimension, transform);
};

function ECB(count, dataCodewords) {
  this.count = count;
  this.dataCodewords = dataCodewords;
  this.__defineGetter__("Count", function() {
    return this.count;
  });
  this.__defineGetter__("DataCodewords", function() {
    return this.dataCodewords;
  });
}

function ECBlocks(ecCodewordsPerBlock, ecBlocks1, ecBlocks2) {
  this.ecCodewordsPerBlock = ecCodewordsPerBlock;
  if (ecBlocks2) this.ecBlocks = new Array(ecBlocks1, ecBlocks2); else this.ecBlocks = new Array(ecBlocks1);
  this.__defineGetter__("ECCodewordsPerBlock", function() {
    return this.ecCodewordsPerBlock;
  });
  this.__defineGetter__("TotalECCodewords", function() {
    return this.ecCodewordsPerBlock * this.NumBlocks;
  });
  this.__defineGetter__("NumBlocks", function() {
    let total = 0;
    for (let i = 0; i < this.ecBlocks.length; i++) {
      total += this.ecBlocks[i].length;
    }
    return total;
  });
  this.getECBlocks = function() {
    return this.ecBlocks;
  };
}

function Version(versionNumber, alignmentPatternCenters, ecBlocks1, ecBlocks2, ecBlocks3, ecBlocks4) {
  this.versionNumber = versionNumber;
  this.alignmentPatternCenters = alignmentPatternCenters;
  this.ecBlocks = new Array(ecBlocks1, ecBlocks2, ecBlocks3, ecBlocks4);
  let total = 0;
  let ecCodewords = ecBlocks1.ECCodewordsPerBlock;
  let ecbArray = ecBlocks1.getECBlocks();
  for (let i = 0; i < ecbArray.length; i++) {
    let ecBlock = ecbArray[i];
    total += ecBlock.Count * (ecBlock.DataCodewords + ecCodewords);
  }
  this.totalCodewords = total;
  this.__defineGetter__("VersionNumber", function() {
    return this.versionNumber;
  });
  this.__defineGetter__("AlignmentPatternCenters", function() {
    return this.alignmentPatternCenters;
  });
  this.__defineGetter__("TotalCodewords", function() {
    return this.totalCodewords;
  });
  this.__defineGetter__("DimensionForVersion", function() {
    return 17 + 4 * this.versionNumber;
  });
  this.buildFunctionPattern = function() {
    let dimension = this.DimensionForVersion;
    let bitMatrix = new BitMatrix(dimension);
    bitMatrix.setRegion(0, 0, 9, 9);
    bitMatrix.setRegion(dimension - 8, 0, 8, 9);
    bitMatrix.setRegion(0, dimension - 8, 9, 8);
    let max = this.alignmentPatternCenters.length;
    for (let x = 0; x < max; x++) {
      let i = this.alignmentPatternCenters[x] - 2;
      for (let y = 0; y < max; y++) {
        if (x === 0 && (y === 0 || y === max - 1) || x === max - 1 && y === 0) {
          continue;
        }
        bitMatrix.setRegion(this.alignmentPatternCenters[y] - 2, i, 5, 5);
      }
    }
    bitMatrix.setRegion(6, 9, 1, dimension - 17);
    bitMatrix.setRegion(9, 6, dimension - 17, 1);
    if (this.versionNumber > 6) {
      bitMatrix.setRegion(dimension - 11, 0, 3, 6);
      bitMatrix.setRegion(0, dimension - 11, 6, 3);
    }
    return bitMatrix;
  };
  this.getECBlocksForLevel = function(ecLevel) {
    return this.ecBlocks[ecLevel.ordinal()];
  };
}

Version.VERSION_DECODE_INFO = new Array(31892, 34236, 39577, 42195, 48118, 51042, 55367, 58893, 63784, 68472, 70749, 76311, 79154, 84390, 87683, 92361, 96236, 102084, 102881, 110507, 110734, 117786, 119615, 126325, 127568, 133589, 136944, 141498, 145311, 150283, 152622, 158308, 161089, 167017);

Version.VERSIONS = buildVersions();

Version.getVersionForNumber = function(versionNumber) {
  if (versionNumber < 1 || versionNumber > 40) {
    throw "ArgumentException";
  }
  return Version.VERSIONS[versionNumber - 1];
};

Version.getProvisionalVersionForDimension = function(dimension) {
  if (dimension % 4 != 1) {
    throw "Error getProvisionalVersionForDimension";
  }
  try {
    return Version.getVersionForNumber(dimension - 17 >> 2);
  } catch (iae) {
    throw "Error getVersionForNumber";
  }
};

Version.decodeVersionInformation = function(versionBits) {
  let bestDifference = 4294967295;
  let bestVersion = 0;
  for (let i = 0; i < Version.VERSION_DECODE_INFO.length; i++) {
    let targetVersion = Version.VERSION_DECODE_INFO[i];
    if (targetVersion == versionBits) {
      return this.getVersionForNumber(i + 7);
    }
    let bitsDifference = FormatInformation.numBitsDiffering(versionBits, targetVersion);
    if (bitsDifference < bestDifference) {
      bestVersion = i + 7;
      bestDifference = bitsDifference;
    }
  }
  if (bestDifference <= 3) {
    return this.getVersionForNumber(bestVersion);
  }
  return null;
};

function buildVersions() {
  return new Array(new Version(1, new Array(), new ECBlocks(7, new ECB(1, 19)), new ECBlocks(10, new ECB(1, 16)), new ECBlocks(13, new ECB(1, 13)), new ECBlocks(17, new ECB(1, 9))), new Version(2, new Array(6, 18), new ECBlocks(10, new ECB(1, 34)), new ECBlocks(16, new ECB(1, 28)), new ECBlocks(22, new ECB(1, 22)), new ECBlocks(28, new ECB(1, 16))), new Version(3, new Array(6, 22), new ECBlocks(15, new ECB(1, 55)), new ECBlocks(26, new ECB(1, 44)), new ECBlocks(18, new ECB(2, 17)), new ECBlocks(22, new ECB(2, 13))), new Version(4, new Array(6, 26), new ECBlocks(20, new ECB(1, 80)), new ECBlocks(18, new ECB(2, 32)), new ECBlocks(26, new ECB(2, 24)), new ECBlocks(16, new ECB(4, 9))), new Version(5, new Array(6, 30), new ECBlocks(26, new ECB(1, 108)), new ECBlocks(24, new ECB(2, 43)), new ECBlocks(18, new ECB(2, 15), new ECB(2, 16)), new ECBlocks(22, new ECB(2, 11), new ECB(2, 12))), new Version(6, new Array(6, 34), new ECBlocks(18, new ECB(2, 68)), new ECBlocks(16, new ECB(4, 27)), new ECBlocks(24, new ECB(4, 19)), new ECBlocks(28, new ECB(4, 15))), new Version(7, new Array(6, 22, 38), new ECBlocks(20, new ECB(2, 78)), new ECBlocks(18, new ECB(4, 31)), new ECBlocks(18, new ECB(2, 14), new ECB(4, 15)), new ECBlocks(26, new ECB(4, 13), new ECB(1, 14))), new Version(8, new Array(6, 24, 42), new ECBlocks(24, new ECB(2, 97)), new ECBlocks(22, new ECB(2, 38), new ECB(2, 39)), new ECBlocks(22, new ECB(4, 18), new ECB(2, 19)), new ECBlocks(26, new ECB(4, 14), new ECB(2, 15))), new Version(9, new Array(6, 26, 46), new ECBlocks(30, new ECB(2, 116)), new ECBlocks(22, new ECB(3, 36), new ECB(2, 37)), new ECBlocks(20, new ECB(4, 16), new ECB(4, 17)), new ECBlocks(24, new ECB(4, 12), new ECB(4, 13))), new Version(10, new Array(6, 28, 50), new ECBlocks(18, new ECB(2, 68), new ECB(2, 69)), new ECBlocks(26, new ECB(4, 43), new ECB(1, 44)), new ECBlocks(24, new ECB(6, 19), new ECB(2, 20)), new ECBlocks(28, new ECB(6, 15), new ECB(2, 16))), new Version(11, new Array(6, 30, 54), new ECBlocks(20, new ECB(4, 81)), new ECBlocks(30, new ECB(1, 50), new ECB(4, 51)), new ECBlocks(28, new ECB(4, 22), new ECB(4, 23)), new ECBlocks(24, new ECB(3, 12), new ECB(8, 13))), new Version(12, new Array(6, 32, 58), new ECBlocks(24, new ECB(2, 92), new ECB(2, 93)), new ECBlocks(22, new ECB(6, 36), new ECB(2, 37)), new ECBlocks(26, new ECB(4, 20), new ECB(6, 21)), new ECBlocks(28, new ECB(7, 14), new ECB(4, 15))), new Version(13, new Array(6, 34, 62), new ECBlocks(26, new ECB(4, 107)), new ECBlocks(22, new ECB(8, 37), new ECB(1, 38)), new ECBlocks(24, new ECB(8, 20), new ECB(4, 21)), new ECBlocks(22, new ECB(12, 11), new ECB(4, 12))), new Version(14, new Array(6, 26, 46, 66), new ECBlocks(30, new ECB(3, 115), new ECB(1, 116)), new ECBlocks(24, new ECB(4, 40), new ECB(5, 41)), new ECBlocks(20, new ECB(11, 16), new ECB(5, 17)), new ECBlocks(24, new ECB(11, 12), new ECB(5, 13))), new Version(15, new Array(6, 26, 48, 70), new ECBlocks(22, new ECB(5, 87), new ECB(1, 88)), new ECBlocks(24, new ECB(5, 41), new ECB(5, 42)), new ECBlocks(30, new ECB(5, 24), new ECB(7, 25)), new ECBlocks(24, new ECB(11, 12), new ECB(7, 13))), new Version(16, new Array(6, 26, 50, 74), new ECBlocks(24, new ECB(5, 98), new ECB(1, 99)), new ECBlocks(28, new ECB(7, 45), new ECB(3, 46)), new ECBlocks(24, new ECB(15, 19), new ECB(2, 20)), new ECBlocks(30, new ECB(3, 15), new ECB(13, 16))), new Version(17, new Array(6, 30, 54, 78), new ECBlocks(28, new ECB(1, 107), new ECB(5, 108)), new ECBlocks(28, new ECB(10, 46), new ECB(1, 47)), new ECBlocks(28, new ECB(1, 22), new ECB(15, 23)), new ECBlocks(28, new ECB(2, 14), new ECB(17, 15))), new Version(18, new Array(6, 30, 56, 82), new ECBlocks(30, new ECB(5, 120), new ECB(1, 121)), new ECBlocks(26, new ECB(9, 43), new ECB(4, 44)), new ECBlocks(28, new ECB(17, 22), new ECB(1, 23)), new ECBlocks(28, new ECB(2, 14), new ECB(19, 15))), new Version(19, new Array(6, 30, 58, 86), new ECBlocks(28, new ECB(3, 113), new ECB(4, 114)), new ECBlocks(26, new ECB(3, 44), new ECB(11, 45)), new ECBlocks(26, new ECB(17, 21), new ECB(4, 22)), new ECBlocks(26, new ECB(9, 13), new ECB(16, 14))), new Version(20, new Array(6, 34, 62, 90), new ECBlocks(28, new ECB(3, 107), new ECB(5, 108)), new ECBlocks(26, new ECB(3, 41), new ECB(13, 42)), new ECBlocks(30, new ECB(15, 24), new ECB(5, 25)), new ECBlocks(28, new ECB(15, 15), new ECB(10, 16))), new Version(21, new Array(6, 28, 50, 72, 94), new ECBlocks(28, new ECB(4, 116), new ECB(4, 117)), new ECBlocks(26, new ECB(17, 42)), new ECBlocks(28, new ECB(17, 22), new ECB(6, 23)), new ECBlocks(30, new ECB(19, 16), new ECB(6, 17))), new Version(22, new Array(6, 26, 50, 74, 98), new ECBlocks(28, new ECB(2, 111), new ECB(7, 112)), new ECBlocks(28, new ECB(17, 46)), new ECBlocks(30, new ECB(7, 24), new ECB(16, 25)), new ECBlocks(24, new ECB(34, 13))), new Version(23, new Array(6, 30, 54, 74, 102), new ECBlocks(30, new ECB(4, 121), new ECB(5, 122)), new ECBlocks(28, new ECB(4, 47), new ECB(14, 48)), new ECBlocks(30, new ECB(11, 24), new ECB(14, 25)), new ECBlocks(30, new ECB(16, 15), new ECB(14, 16))), new Version(24, new Array(6, 28, 54, 80, 106), new ECBlocks(30, new ECB(6, 117), new ECB(4, 118)), new ECBlocks(28, new ECB(6, 45), new ECB(14, 46)), new ECBlocks(30, new ECB(11, 24), new ECB(16, 25)), new ECBlocks(30, new ECB(30, 16), new ECB(2, 17))), new Version(25, new Array(6, 32, 58, 84, 110), new ECBlocks(26, new ECB(8, 106), new ECB(4, 107)), new ECBlocks(28, new ECB(8, 47), new ECB(13, 48)), new ECBlocks(30, new ECB(7, 24), new ECB(22, 25)), new ECBlocks(30, new ECB(22, 15), new ECB(13, 16))), new Version(26, new Array(6, 30, 58, 86, 114), new ECBlocks(28, new ECB(10, 114), new ECB(2, 115)), new ECBlocks(28, new ECB(19, 46), new ECB(4, 47)), new ECBlocks(28, new ECB(28, 22), new ECB(6, 23)), new ECBlocks(30, new ECB(33, 16), new ECB(4, 17))), new Version(27, new Array(6, 34, 62, 90, 118), new ECBlocks(30, new ECB(8, 122), new ECB(4, 123)), new ECBlocks(28, new ECB(22, 45), new ECB(3, 46)), new ECBlocks(30, new ECB(8, 23), new ECB(26, 24)), new ECBlocks(30, new ECB(12, 15), new ECB(28, 16))), new Version(28, new Array(6, 26, 50, 74, 98, 122), new ECBlocks(30, new ECB(3, 117), new ECB(10, 118)), new ECBlocks(28, new ECB(3, 45), new ECB(23, 46)), new ECBlocks(30, new ECB(4, 24), new ECB(31, 25)), new ECBlocks(30, new ECB(11, 15), new ECB(31, 16))), new Version(29, new Array(6, 30, 54, 78, 102, 126), new ECBlocks(30, new ECB(7, 116), new ECB(7, 117)), new ECBlocks(28, new ECB(21, 45), new ECB(7, 46)), new ECBlocks(30, new ECB(1, 23), new ECB(37, 24)), new ECBlocks(30, new ECB(19, 15), new ECB(26, 16))), new Version(30, new Array(6, 26, 52, 78, 104, 130), new ECBlocks(30, new ECB(5, 115), new ECB(10, 116)), new ECBlocks(28, new ECB(19, 47), new ECB(10, 48)), new ECBlocks(30, new ECB(15, 24), new ECB(25, 25)), new ECBlocks(30, new ECB(23, 15), new ECB(25, 16))), new Version(31, new Array(6, 30, 56, 82, 108, 134), new ECBlocks(30, new ECB(13, 115), new ECB(3, 116)), new ECBlocks(28, new ECB(2, 46), new ECB(29, 47)), new ECBlocks(30, new ECB(42, 24), new ECB(1, 25)), new ECBlocks(30, new ECB(23, 15), new ECB(28, 16))), new Version(32, new Array(6, 34, 60, 86, 112, 138), new ECBlocks(30, new ECB(17, 115)), new ECBlocks(28, new ECB(10, 46), new ECB(23, 47)), new ECBlocks(30, new ECB(10, 24), new ECB(35, 25)), new ECBlocks(30, new ECB(19, 15), new ECB(35, 16))), new Version(33, new Array(6, 30, 58, 86, 114, 142), new ECBlocks(30, new ECB(17, 115), new ECB(1, 116)), new ECBlocks(28, new ECB(14, 46), new ECB(21, 47)), new ECBlocks(30, new ECB(29, 24), new ECB(19, 25)), new ECBlocks(30, new ECB(11, 15), new ECB(46, 16))), new Version(34, new Array(6, 34, 62, 90, 118, 146), new ECBlocks(30, new ECB(13, 115), new ECB(6, 116)), new ECBlocks(28, new ECB(14, 46), new ECB(23, 47)), new ECBlocks(30, new ECB(44, 24), new ECB(7, 25)), new ECBlocks(30, new ECB(59, 16), new ECB(1, 17))), new Version(35, new Array(6, 30, 54, 78, 102, 126, 150), new ECBlocks(30, new ECB(12, 121), new ECB(7, 122)), new ECBlocks(28, new ECB(12, 47), new ECB(26, 48)), new ECBlocks(30, new ECB(39, 24), new ECB(14, 25)), new ECBlocks(30, new ECB(22, 15), new ECB(41, 16))), new Version(36, new Array(6, 24, 50, 76, 102, 128, 154), new ECBlocks(30, new ECB(6, 121), new ECB(14, 122)), new ECBlocks(28, new ECB(6, 47), new ECB(34, 48)), new ECBlocks(30, new ECB(46, 24), new ECB(10, 25)), new ECBlocks(30, new ECB(2, 15), new ECB(64, 16))), new Version(37, new Array(6, 28, 54, 80, 106, 132, 158), new ECBlocks(30, new ECB(17, 122), new ECB(4, 123)), new ECBlocks(28, new ECB(29, 46), new ECB(14, 47)), new ECBlocks(30, new ECB(49, 24), new ECB(10, 25)), new ECBlocks(30, new ECB(24, 15), new ECB(46, 16))), new Version(38, new Array(6, 32, 58, 84, 110, 136, 162), new ECBlocks(30, new ECB(4, 122), new ECB(18, 123)), new ECBlocks(28, new ECB(13, 46), new ECB(32, 47)), new ECBlocks(30, new ECB(48, 24), new ECB(14, 25)), new ECBlocks(30, new ECB(42, 15), new ECB(32, 16))), new Version(39, new Array(6, 26, 54, 82, 110, 138, 166), new ECBlocks(30, new ECB(20, 117), new ECB(4, 118)), new ECBlocks(28, new ECB(40, 47), new ECB(7, 48)), new ECBlocks(30, new ECB(43, 24), new ECB(22, 25)), new ECBlocks(30, new ECB(10, 15), new ECB(67, 16))), new Version(40, new Array(6, 30, 58, 86, 114, 142, 170), new ECBlocks(30, new ECB(19, 118), new ECB(6, 119)), new ECBlocks(28, new ECB(18, 47), new ECB(31, 48)), new ECBlocks(30, new ECB(34, 24), new ECB(34, 25)), new ECBlocks(30, new ECB(20, 15), new ECB(61, 16))));
}

function PerspectiveTransform(a11, a21, a31, a12, a22, a32, a13, a23, a33) {
  this.a11 = a11;
  this.a12 = a12;
  this.a13 = a13;
  this.a21 = a21;
  this.a22 = a22;
  this.a23 = a23;
  this.a31 = a31;
  this.a32 = a32;
  this.a33 = a33;
  this.transformPoints1 = function(points) {
    let max = points.length;
    let a11 = this.a11;
    let a12 = this.a12;
    let a13 = this.a13;
    let a21 = this.a21;
    let a22 = this.a22;
    let a23 = this.a23;
    let a31 = this.a31;
    let a32 = this.a32;
    let a33 = this.a33;
    for (let i = 0; i < max; i += 2) {
      let x = points[i];
      let y = points[i + 1];
      let denominator = a13 * x + a23 * y + a33;
      points[i] = (a11 * x + a21 * y + a31) / denominator;
      points[i + 1] = (a12 * x + a22 * y + a32) / denominator;
    }
  };
  this.transformPoints2 = function(xValues, yValues) {
    let n = xValues.length;
    for (let i = 0; i < n; i++) {
      let x = xValues[i];
      let y = yValues[i];
      let denominator = this.a13 * x + this.a23 * y + this.a33;
      xValues[i] = (this.a11 * x + this.a21 * y + this.a31) / denominator;
      yValues[i] = (this.a12 * x + this.a22 * y + this.a32) / denominator;
    }
  };
  this.buildAdjoint = function() {
    return new PerspectiveTransform(this.a22 * this.a33 - this.a23 * this.a32, this.a23 * this.a31 - this.a21 * this.a33, this.a21 * this.a32 - this.a22 * this.a31, this.a13 * this.a32 - this.a12 * this.a33, this.a11 * this.a33 - this.a13 * this.a31, this.a12 * this.a31 - this.a11 * this.a32, this.a12 * this.a23 - this.a13 * this.a22, this.a13 * this.a21 - this.a11 * this.a23, this.a11 * this.a22 - this.a12 * this.a21);
  };
  this.times = function(other) {
    return new PerspectiveTransform(this.a11 * other.a11 + this.a21 * other.a12 + this.a31 * other.a13, this.a11 * other.a21 + this.a21 * other.a22 + this.a31 * other.a23, this.a11 * other.a31 + this.a21 * other.a32 + this.a31 * other.a33, this.a12 * other.a11 + this.a22 * other.a12 + this.a32 * other.a13, this.a12 * other.a21 + this.a22 * other.a22 + this.a32 * other.a23, this.a12 * other.a31 + this.a22 * other.a32 + this.a32 * other.a33, this.a13 * other.a11 + this.a23 * other.a12 + this.a33 * other.a13, this.a13 * other.a21 + this.a23 * other.a22 + this.a33 * other.a23, this.a13 * other.a31 + this.a23 * other.a32 + this.a33 * other.a33);
  };
}

PerspectiveTransform.quadrilateralToQuadrilateral = function(x0, y0, x1, y1, x2, y2, x3, y3, x0p, y0p, x1p, y1p, x2p, y2p, x3p, y3p) {
  let qToS = this.quadrilateralToSquare(x0, y0, x1, y1, x2, y2, x3, y3);
  let sToQ = this.squareToQuadrilateral(x0p, y0p, x1p, y1p, x2p, y2p, x3p, y3p);
  return sToQ.times(qToS);
};

PerspectiveTransform.squareToQuadrilateral = function(x0, y0, x1, y1, x2, y2, x3, y3) {
  let dy2 = y3 - y2;
  let dy3 = y0 - y1 + y2 - y3;
  if (dy2 === 0 && dy3 === 0) {
    return new PerspectiveTransform(x1 - x0, x2 - x1, x0, y1 - y0, y2 - y1, y0, 0, 0, 1);
  } else {
    let dx1 = x1 - x2;
    let dx2 = x3 - x2;
    let dx3 = x0 - x1 + x2 - x3;
    let dy1 = y1 - y2;
    let denominator = dx1 * dy2 - dx2 * dy1;
    let a13 = (dx3 * dy2 - dx2 * dy3) / denominator;
    let a23 = (dx1 * dy3 - dx3 * dy1) / denominator;
    return new PerspectiveTransform(x1 - x0 + a13 * x1, x3 - x0 + a23 * x3, x0, y1 - y0 + a13 * y1, y3 - y0 + a23 * y3, y0, a13, a23, 1);
  }
};

PerspectiveTransform.quadrilateralToSquare = function(x0, y0, x1, y1, x2, y2, x3, y3) {
  return this.squareToQuadrilateral(x0, y0, x1, y1, x2, y2, x3, y3).buildAdjoint();
};

function DetectorResult(bits, points) {
  this.bits = bits;
  this.points = points;
}

function Detector(image) {
  this.image = image;
  this.resultPointCallback = null;
  this.sizeOfBlackWhiteBlackRun = function(fromX, fromY, toX, toY) {
    let steep = Math.abs(toY - fromY) > Math.abs(toX - fromX);
    if (steep) {
      let temp = fromX;
      fromX = fromY;
      fromY = temp;
      temp = toX;
      toX = toY;
      toY = temp;
    }
    let dx = Math.abs(toX - fromX);
    let dy = Math.abs(toY - fromY);
    let error = -dx >> 1;
    let ystep = fromY < toY ? 1 : -1;
    let xstep = fromX < toX ? 1 : -1;
    let state = 0;
    for (let x = fromX, y = fromY; x != toX; x += xstep) {
      let realX = steep ? y : x;
      let realY = steep ? x : y;
      if (state == 1) {
        if (this.image[realX + realY * imgWidth]) {
          state++;
        }
      } else {
        if (!this.image[realX + realY * imgWidth]) {
          state++;
        }
      }
      if (state == 3) {
        let diffX = x - fromX;
        let diffY = y - fromY;
        return Math.sqrt(diffX * diffX + diffY * diffY);
      }
      error += dy;
      if (error > 0) {
        if (y == toY) {
          break;
        }
        y += ystep;
        error -= dx;
      }
    }
    let diffX2 = toX - fromX;
    let diffY2 = toY - fromY;
    return Math.sqrt(diffX2 * diffX2 + diffY2 * diffY2);
  };
  this.sizeOfBlackWhiteBlackRunBothWays = function(fromX, fromY, toX, toY) {
    let result = this.sizeOfBlackWhiteBlackRun(fromX, fromY, toX, toY);
    let scale = 1;
    let otherToX = fromX - (toX - fromX);
    if (otherToX < 0) {
      scale = fromX / (fromX - otherToX);
      otherToX = 0;
    } else if (otherToX >= imgWidth) {
      scale = (imgWidth - 1 - fromX) / (otherToX - fromX);
      otherToX = imgWidth - 1;
    }
    let otherToY = Math.floor(fromY - (toY - fromY) * scale);
    scale = 1;
    if (otherToY < 0) {
      scale = fromY / (fromY - otherToY);
      otherToY = 0;
    } else if (otherToY >= imgHeight) {
      scale = (imgHeight - 1 - fromY) / (otherToY - fromY);
      otherToY = imgHeight - 1;
    }
    otherToX = Math.floor(fromX + (otherToX - fromX) * scale);
    result += this.sizeOfBlackWhiteBlackRun(fromX, fromY, otherToX, otherToY);
    return result - 1;
  };
  this.calculateModuleSizeOneWay = function(pattern, otherPattern) {
    let moduleSizeEst1 = this.sizeOfBlackWhiteBlackRunBothWays(Math.floor(pattern.X), Math.floor(pattern.Y), Math.floor(otherPattern.X), Math.floor(otherPattern.Y));
    let moduleSizeEst2 = this.sizeOfBlackWhiteBlackRunBothWays(Math.floor(otherPattern.X), Math.floor(otherPattern.Y), Math.floor(pattern.X), Math.floor(pattern.Y));
    if (isNaN(moduleSizeEst1)) {
      return moduleSizeEst2 / 7;
    }
    if (isNaN(moduleSizeEst2)) {
      return moduleSizeEst1 / 7;
    }
    return (moduleSizeEst1 + moduleSizeEst2) / 14;
  };
  this.calculateModuleSize = function(topLeft, topRight, bottomLeft) {
    return (this.calculateModuleSizeOneWay(topLeft, topRight) + this.calculateModuleSizeOneWay(topLeft, bottomLeft)) / 2;
  };
  this.distance = function(pattern1, pattern2) {
    let xDiff = pattern1.X - pattern2.X;
    let yDiff = pattern1.Y - pattern2.Y;
    return Math.sqrt(xDiff * xDiff + yDiff * yDiff);
  };
  this.computeDimension = function(topLeft, topRight, bottomLeft, moduleSize) {
    let tltrCentersDimension = Math.round(this.distance(topLeft, topRight) / moduleSize);
    let tlblCentersDimension = Math.round(this.distance(topLeft, bottomLeft) / moduleSize);
    let dimension = (tltrCentersDimension + tlblCentersDimension >> 1) + 7;
    switch (dimension & 3) {
     case 0:
      dimension++;
      break;

     case 2:
      dimension--;
      break;

     case 3:
      throw "Error";
    }
    return dimension;
  };
  this.findAlignmentInRegion = function(overallEstModuleSize, estAlignmentX, estAlignmentY, allowanceFactor) {
    let allowance = Math.floor(allowanceFactor * overallEstModuleSize);
    let alignmentAreaLeftX = Math.max(0, estAlignmentX - allowance);
    let alignmentAreaRightX = Math.min(imgWidth - 1, estAlignmentX + allowance);
    if (alignmentAreaRightX - alignmentAreaLeftX < overallEstModuleSize * 3) {
      throw "Error";
    }
    let alignmentAreaTopY = Math.max(0, estAlignmentY - allowance);
    let alignmentAreaBottomY = Math.min(imgHeight - 1, estAlignmentY + allowance);
    let alignmentFinder = new AlignmentPatternFinder(this.image, alignmentAreaLeftX, alignmentAreaTopY, alignmentAreaRightX - alignmentAreaLeftX, alignmentAreaBottomY - alignmentAreaTopY, overallEstModuleSize, this.resultPointCallback);
    return alignmentFinder.find();
  };
  this.createTransform = function(topLeft, topRight, bottomLeft, alignmentPattern, dimension) {
    let dimMinusThree = dimension - 3.5;
    let bottomRightX;
    let bottomRightY;
    let sourceBottomRightX;
    let sourceBottomRightY;
    if (alignmentPattern !== null) {
      bottomRightX = alignmentPattern.X;
      bottomRightY = alignmentPattern.Y;
      sourceBottomRightX = sourceBottomRightY = dimMinusThree - 3;
    } else {
      bottomRightX = topRight.X - topLeft.X + bottomLeft.X;
      bottomRightY = topRight.Y - topLeft.Y + bottomLeft.Y;
      sourceBottomRightX = sourceBottomRightY = dimMinusThree;
    }
    let transform = PerspectiveTransform.quadrilateralToQuadrilateral(3.5, 3.5, dimMinusThree, 3.5, sourceBottomRightX, sourceBottomRightY, 3.5, dimMinusThree, topLeft.X, topLeft.Y, topRight.X, topRight.Y, bottomRightX, bottomRightY, bottomLeft.X, bottomLeft.Y);
    return transform;
  };
  this.sampleGrid = function(image, transform, dimension) {
    let sampler = GridSampler;
    return sampler.sampleGrid3(image, dimension, transform);
  };
  this.processFinderPatternInfo = function(info) {
    let topLeft = info.TopLeft;
    let topRight = info.TopRight;
    let bottomLeft = info.BottomLeft;
    let moduleSize = this.calculateModuleSize(topLeft, topRight, bottomLeft);
    if (moduleSize < 1) {
      throw "Error";
    }
    let dimension = this.computeDimension(topLeft, topRight, bottomLeft, moduleSize);
    let provisionalVersion = Version.getProvisionalVersionForDimension(dimension);
    let modulesBetweenFPCenters = provisionalVersion.DimensionForVersion - 7;
    let alignmentPattern = null;
    if (provisionalVersion.AlignmentPatternCenters.length > 0) {
      let bottomRightX = topRight.X - topLeft.X + bottomLeft.X;
      let bottomRightY = topRight.Y - topLeft.Y + bottomLeft.Y;
      let correctionToTopLeft = 1 - 3 / modulesBetweenFPCenters;
      let estAlignmentX = Math.floor(topLeft.X + correctionToTopLeft * (bottomRightX - topLeft.X));
      let estAlignmentY = Math.floor(topLeft.Y + correctionToTopLeft * (bottomRightY - topLeft.Y));
      for (let i = 4; i <= 16; i <<= 1) {
        alignmentPattern = this.findAlignmentInRegion(moduleSize, estAlignmentX, estAlignmentY, i);
        break;
      }
    }
    let transform = this.createTransform(topLeft, topRight, bottomLeft, alignmentPattern, dimension);
    let bits = this.sampleGrid(this.image, transform, dimension);
    let points;
    if (alignmentPattern === null) {
      points = new Array(bottomLeft, topLeft, topRight);
    } else {
      points = new Array(bottomLeft, topLeft, topRight, alignmentPattern);
    }
    return new DetectorResult(bits, points);
  };
  this.detect = function() {
    let info = new FinderPatternFinder().findFinderPattern(this.image);
    return this.processFinderPatternInfo(info);
  };
}

var FORMAT_INFO_MASK_QR = 21522;

var FORMAT_INFO_DECODE_LOOKUP = new Array(new Array(21522, 0), new Array(20773, 1), new Array(24188, 2), new Array(23371, 3), new Array(17913, 4), new Array(16590, 5), new Array(20375, 6), new Array(19104, 7), new Array(30660, 8), new Array(29427, 9), new Array(32170, 10), new Array(30877, 11), new Array(26159, 12), new Array(25368, 13), new Array(27713, 14), new Array(26998, 15), new Array(5769, 16), new Array(5054, 17), new Array(7399, 18), new Array(6608, 19), new Array(1890, 20), new Array(597, 21), new Array(3340, 22), new Array(2107, 23), new Array(13663, 24), new Array(12392, 25), new Array(16177, 26), new Array(14854, 27), new Array(9396, 28), new Array(8579, 29), new Array(11994, 30), new Array(11245, 31));

var BITS_SET_IN_HALF_BYTE = new Array(0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4);

function FormatInformation(formatInfo) {
  this.errorCorrectionLevel = ErrorCorrectionLevel.forBits(formatInfo >> 3 & 3);
  this.dataMask = formatInfo & 7;
  this.__defineGetter__("ErrorCorrectionLevel", function() {
    return this.errorCorrectionLevel;
  });
  this.__defineGetter__("DataMask", function() {
    return this.dataMask;
  });
  this.GetHashCode = function() {
    return this.errorCorrectionLevel.ordinal() << 3 | this.dataMask;
  };
  this.Equals = function(o) {
    let other = o;
    return this.errorCorrectionLevel == other.errorCorrectionLevel && this.dataMask == other.dataMask;
  };
}

FormatInformation.numBitsDiffering = function(a, b) {
  a ^= b;
  return BITS_SET_IN_HALF_BYTE[a & 15] + BITS_SET_IN_HALF_BYTE[URShift(a, 4) & 15] + BITS_SET_IN_HALF_BYTE[URShift(a, 8) & 15] + BITS_SET_IN_HALF_BYTE[URShift(a, 12) & 15] + BITS_SET_IN_HALF_BYTE[URShift(a, 16) & 15] + BITS_SET_IN_HALF_BYTE[URShift(a, 20) & 15] + BITS_SET_IN_HALF_BYTE[URShift(a, 24) & 15] + BITS_SET_IN_HALF_BYTE[URShift(a, 28) & 15];
};

FormatInformation.decodeFormatInformation = function(maskedFormatInfo) {
  let formatInfo = FormatInformation.doDecodeFormatInformation(maskedFormatInfo);
  if (formatInfo !== null) {
    return formatInfo;
  }
  return FormatInformation.doDecodeFormatInformation(maskedFormatInfo ^ FORMAT_INFO_MASK_QR);
};

FormatInformation.doDecodeFormatInformation = function(maskedFormatInfo) {
  let bestDifference = 4294967295;
  let bestFormatInfo = 0;
  for (let i = 0; i < FORMAT_INFO_DECODE_LOOKUP.length; i++) {
    let decodeInfo = FORMAT_INFO_DECODE_LOOKUP[i];
    let targetInfo = decodeInfo[0];
    if (targetInfo == maskedFormatInfo) {
      return new FormatInformation(decodeInfo[1]);
    }
    let bitsDifference = this.numBitsDiffering(maskedFormatInfo, targetInfo);
    if (bitsDifference < bestDifference) {
      bestFormatInfo = decodeInfo[1];
      bestDifference = bitsDifference;
    }
  }
  if (bestDifference <= 3) {
    return new FormatInformation(bestFormatInfo);
  }
  return null;
};

function ErrorCorrectionLevel(ordinal, bits, name) {
  this.ordinal_Renamed_Field = ordinal;
  this.bits = bits;
  this.name = name;
  this.__defineGetter__("Bits", function() {
    return this.bits;
  });
  this.__defineGetter__("Name", function() {
    return this.name;
  });
  this.ordinal = function() {
    return this.ordinal_Renamed_Field;
  };
}

var L = new ErrorCorrectionLevel(0, 1, "L");

var M = new ErrorCorrectionLevel(1, 0, "M");

var Q = new ErrorCorrectionLevel(2, 3, "Q");

var H = new ErrorCorrectionLevel(3, 2, "H");

var FOR_BITS = new Array(M, L, H, Q);

ErrorCorrectionLevel.forBits = function(bits) {
  if (bits < 0 || bits >= FOR_BITS.length) {
    throw "ArgumentException";
  }
  return FOR_BITS[bits];
};

function BitMatrix(width, height) {
  if (!height) height = width;
  if (width < 1 || height < 1) {
    throw "Both dimensions must be greater than 0";
  }
  this.width = width;
  this.height = height;
  let rowSize = width >> 5;
  if ((width & 31) !== 0) {
    rowSize++;
  }
  this.rowSize = rowSize;
  this.bits = new Array(rowSize * height);
  for (let i = 0; i < this.bits.length; i++) this.bits[i] = 0;
  this.__defineGetter__("Width", function() {
    return this.width;
  });
  this.__defineGetter__("Height", function() {
    return this.height;
  });
  this.__defineGetter__("Dimension", function() {
    if (this.width != this.height) {
      throw "Can't call getDimension() on a non-square matrix";
    }
    return this.width;
  });
  this.get_Renamed = function(x, y) {
    let offset = y * this.rowSize + (x >> 5);
    return (URShift(this.bits[offset], x & 31) & 1) !== 0;
  };
  this.set_Renamed = function(x, y) {
    let offset = y * this.rowSize + (x >> 5);
    this.bits[offset] |= 1 << (x & 31);
  };
  this.flip = function(x, y) {
    let offset = y * this.rowSize + (x >> 5);
    this.bits[offset] ^= 1 << (x & 31);
  };
  this.clear = function() {
    let max = this.bits.length;
    for (let i = 0; i < max; i++) {
      this.bits[i] = 0;
    }
  };
  this.setRegion = function(left, top, width, height) {
    if (top < 0 || left < 0) {
      throw "Left and top must be nonnegative";
    }
    if (height < 1 || width < 1) {
      throw "Height and width must be at least 1";
    }
    let right = left + width;
    let bottom = top + height;
    if (bottom > this.height || right > this.width) {
      throw "The region must fit inside the matrix";
    }
    for (let y = top; y < bottom; y++) {
      let offset = y * this.rowSize;
      for (let x = left; x < right; x++) {
        this.bits[offset + (x >> 5)] |= 1 << (x & 31);
      }
    }
  };
}

function DataBlock(numDataCodewords, codewords) {
  this.numDataCodewords = numDataCodewords;
  this.codewords = codewords;
  this.__defineGetter__("NumDataCodewords", function() {
    return this.numDataCodewords;
  });
  this.__defineGetter__("Codewords", function() {
    return this.codewords;
  });
}

DataBlock.getDataBlocks = function(rawCodewords, version, ecLevel) {
  if (rawCodewords.length != version.TotalCodewords) {
    throw "ArgumentException";
  }
  let ecBlocks = version.getECBlocksForLevel(ecLevel);
  let totalBlocks = 0;
  let ecBlockArray = ecBlocks.getECBlocks();
  for (let i = 0; i < ecBlockArray.length; i++) {
    totalBlocks += ecBlockArray[i].Count;
  }
  let result = new Array(totalBlocks);
  let numResultBlocks = 0;
  for (let j = 0; j < ecBlockArray.length; j++) {
    let ecBlock = ecBlockArray[j];
    for (let i = 0; i < ecBlock.Count; i++) {
      let numDataCodewords = ecBlock.DataCodewords;
      let numBlockCodewords = ecBlocks.ECCodewordsPerBlock + numDataCodewords;
      result[numResultBlocks++] = new DataBlock(numDataCodewords, new Array(numBlockCodewords));
    }
  }
  let shorterBlocksTotalCodewords = result[0].codewords.length;
  let longerBlocksStartAt = result.length - 1;
  while (longerBlocksStartAt >= 0) {
    let numCodewords = result[longerBlocksStartAt].codewords.length;
    if (numCodewords == shorterBlocksTotalCodewords) {
      break;
    }
    longerBlocksStartAt--;
  }
  longerBlocksStartAt++;
  let shorterBlocksNumDataCodewords = shorterBlocksTotalCodewords - ecBlocks.ECCodewordsPerBlock;
  let rawCodewordsOffset = 0;
  for (let i = 0; i < shorterBlocksNumDataCodewords; i++) {
    for (let j = 0; j < numResultBlocks; j++) {
      result[j].codewords[i] = rawCodewords[rawCodewordsOffset++];
    }
  }
  for (let j = longerBlocksStartAt; j < numResultBlocks; j++) {
    result[j].codewords[shorterBlocksNumDataCodewords] = rawCodewords[rawCodewordsOffset++];
  }
  let max = result[0].codewords.length;
  for (let i = shorterBlocksNumDataCodewords; i < max; i++) {
    for (let j = 0; j < numResultBlocks; j++) {
      let iOffset = j < longerBlocksStartAt ? i : i + 1;
      result[j].codewords[iOffset] = rawCodewords[rawCodewordsOffset++];
    }
  }
  return result;
};

var DataMask = {};

function BitMatrixParser(bitMatrix) {
  let dimension = bitMatrix.Dimension;
  if (dimension < 21 || (dimension & 3) != 1) {
    throw "Error BitMatrixParser";
  }
  this.bitMatrix = bitMatrix;
  this.parsedVersion = null;
  this.parsedFormatInfo = null;
  this.copyBit = function(i, j, versionBits) {
    return this.bitMatrix.get_Renamed(i, j) ? versionBits << 1 | 1 : versionBits << 1;
  };
  this.readFormatInformation = function() {
    if (this.parsedFormatInfo !== null) {
      return this.parsedFormatInfo;
    }
    let formatInfoBits = 0;
    for (let i = 0; i < 6; i++) {
      formatInfoBits = this.copyBit(i, 8, formatInfoBits);
    }
    formatInfoBits = this.copyBit(7, 8, formatInfoBits);
    formatInfoBits = this.copyBit(8, 8, formatInfoBits);
    formatInfoBits = this.copyBit(8, 7, formatInfoBits);
    for (let j = 5; j >= 0; j--) {
      formatInfoBits = this.copyBit(8, j, formatInfoBits);
    }
    this.parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits);
    if (this.parsedFormatInfo !== null) {
      return this.parsedFormatInfo;
    }
    let dimension = this.bitMatrix.Dimension;
    formatInfoBits = 0;
    let iMin = dimension - 8;
    for (let i = dimension - 1; i >= iMin; i--) {
      formatInfoBits = this.copyBit(i, 8, formatInfoBits);
    }
    for (let j = dimension - 7; j < dimension; j++) {
      formatInfoBits = this.copyBit(8, j, formatInfoBits);
    }
    this.parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits);
    if (this.parsedFormatInfo !== null) {
      return this.parsedFormatInfo;
    }
    throw "Error readFormatInformation";
  };
  this.readVersion = function() {
    if (this.parsedVersion !== null) {
      return this.parsedVersion;
    }
    let dimension = this.bitMatrix.Dimension;
    let provisionalVersion = dimension - 17 >> 2;
    if (provisionalVersion <= 6) {
      return Version.getVersionForNumber(provisionalVersion);
    }
    let versionBits = 0;
    let ijMin = dimension - 11;
    for (let j = 5; j >= 0; j--) {
      for (let i = dimension - 9; i >= ijMin; i--) {
        versionBits = this.copyBit(i, j, versionBits);
      }
    }
    this.parsedVersion = Version.decodeVersionInformation(versionBits);
    if (this.parsedVersion !== null && this.parsedVersion.DimensionForVersion == dimension) {
      return this.parsedVersion;
    }
    versionBits = 0;
    for (let i = 5; i >= 0; i--) {
      for (let j = dimension - 9; j >= ijMin; j--) {
        versionBits = this.copyBit(i, j, versionBits);
      }
    }
    this.parsedVersion = Version.decodeVersionInformation(versionBits);
    if (this.parsedVersion !== null && this.parsedVersion.DimensionForVersion == dimension) {
      return this.parsedVersion;
    }
    throw "Error readVersion";
  };
  this.readCodewords = function() {
    let formatInfo = this.readFormatInformation();
    let version = this.readVersion();
    let dataMask = DataMask.forReference(formatInfo.DataMask);
    let dimension = this.bitMatrix.Dimension;
    dataMask.unmaskBitMatrix(this.bitMatrix, dimension);
    let functionPattern = version.buildFunctionPattern();
    let readingUp = true;
    let result = new Array(version.TotalCodewords);
    let resultOffset = 0;
    let currentByte = 0;
    let bitsRead = 0;
    for (let j = dimension - 1; j > 0; j -= 2) {
      if (j == 6) {
        j--;
      }
      for (let count = 0; count < dimension; count++) {
        let i = readingUp ? dimension - 1 - count : count;
        for (let col = 0; col < 2; col++) {
          if (!functionPattern.get_Renamed(j - col, i)) {
            bitsRead++;
            currentByte <<= 1;
            if (this.bitMatrix.get_Renamed(j - col, i)) {
              currentByte |= 1;
            }
            if (bitsRead == 8) {
              result[resultOffset++] = currentByte;
              bitsRead = 0;
              currentByte = 0;
            }
          }
        }
      }
      readingUp ^= true;
    }
    if (resultOffset != version.TotalCodewords) {
      throw "Error readCodewords";
    }
    return result;
  };
}

DataMask.forReference = function(reference) {
  if (reference < 0 || reference > 7) {
    throw "System.ArgumentException";
  }
  return DataMask.DATA_MASKS[reference];
};

function DataMask000() {
  this.unmaskBitMatrix = function(bits, dimension) {
    for (let i = 0; i < dimension; i++) {
      for (let j = 0; j < dimension; j++) {
        if (this.isMasked(i, j)) {
          bits.flip(j, i);
        }
      }
    }
  };
  this.isMasked = function(i, j) {
    return (i + j & 1) === 0;
  };
}

function DataMask001() {
  this.unmaskBitMatrix = function(bits, dimension) {
    for (let i = 0; i < dimension; i++) {
      for (let j = 0; j < dimension; j++) {
        if (this.isMasked(i, j)) {
          bits.flip(j, i);
        }
      }
    }
  };
  this.isMasked = function(i, j) {
    return (i & 1) === 0;
  };
}

function DataMask010() {
  this.unmaskBitMatrix = function(bits, dimension) {
    for (let i = 0; i < dimension; i++) {
      for (let j = 0; j < dimension; j++) {
        if (this.isMasked(i, j)) {
          bits.flip(j, i);
        }
      }
    }
  };
  this.isMasked = function(i, j) {
    return j % 3 === 0;
  };
}

function DataMask011() {
  this.unmaskBitMatrix = function(bits, dimension) {
    for (let i = 0; i < dimension; i++) {
      for (let j = 0; j < dimension; j++) {
        if (this.isMasked(i, j)) {
          bits.flip(j, i);
        }
      }
    }
  };
  this.isMasked = function(i, j) {
    return (i + j) % 3 === 0;
  };
}

function DataMask100() {
  this.unmaskBitMatrix = function(bits, dimension) {
    for (let i = 0; i < dimension; i++) {
      for (let j = 0; j < dimension; j++) {
        if (this.isMasked(i, j)) {
          bits.flip(j, i);
        }
      }
    }
  };
  this.isMasked = function(i, j) {
    return (URShift(i, 1) + j / 3 & 1) === 0;
  };
}

function DataMask101() {
  this.unmaskBitMatrix = function(bits, dimension) {
    for (let i = 0; i < dimension; i++) {
      for (let j = 0; j < dimension; j++) {
        if (this.isMasked(i, j)) {
          bits.flip(j, i);
        }
      }
    }
  };
  this.isMasked = function(i, j) {
    let temp = i * j;
    return (temp & 1) + temp % 3 === 0;
  };
}

function DataMask110() {
  this.unmaskBitMatrix = function(bits, dimension) {
    for (let i = 0; i < dimension; i++) {
      for (let j = 0; j < dimension; j++) {
        if (this.isMasked(i, j)) {
          bits.flip(j, i);
        }
      }
    }
  };
  this.isMasked = function(i, j) {
    let temp = i * j;
    return ((temp & 1) + temp % 3 & 1) === 0;
  };
}

function DataMask111() {
  this.unmaskBitMatrix = function(bits, dimension) {
    for (let i = 0; i < dimension; i++) {
      for (let j = 0; j < dimension; j++) {
        if (this.isMasked(i, j)) {
          bits.flip(j, i);
        }
      }
    }
  };
  this.isMasked = function(i, j) {
    return ((i + j & 1) + i * j % 3 & 1) === 0;
  };
}

DataMask.DATA_MASKS = new Array(new DataMask000(), new DataMask001(), new DataMask010(), new DataMask011(), new DataMask100(), new DataMask101(), new DataMask110(), new DataMask111());

function ReedSolomonDecoder(field) {
  this.field = field;
  this.decode = function(received, twoS) {
    let poly = new GF256Poly(this.field, received);
    let syndromeCoefficients = new Array(twoS);
    for (let i = 0; i < syndromeCoefficients.length; i++) syndromeCoefficients[i] = 0;
    let dataMatrix = false;
    let noError = true;
    for (let i = 0; i < twoS; i++) {
      let value = poly.evaluateAt(this.field.exp(dataMatrix ? i + 1 : i));
      syndromeCoefficients[syndromeCoefficients.length - 1 - i] = value;
      if (value !== 0) {
        noError = false;
      }
    }
    if (noError) {
      return;
    }
    let syndrome = new GF256Poly(this.field, syndromeCoefficients);
    let sigmaOmega = this.runEuclideanAlgorithm(this.field.buildMonomial(twoS, 1), syndrome, twoS);
    let sigma = sigmaOmega[0];
    let omega = sigmaOmega[1];
    let errorLocations = this.findErrorLocations(sigma);
    let errorMagnitudes = this.findErrorMagnitudes(omega, errorLocations, dataMatrix);
    for (let i = 0; i < errorLocations.length; i++) {
      let position = received.length - 1 - this.field.log(errorLocations[i]);
      if (position < 0) {
        throw "ReedSolomonException Bad error location";
      }
      received[position] = GF256.addOrSubtract(received[position], errorMagnitudes[i]);
    }
  };
  this.runEuclideanAlgorithm = function(a, b, R) {
    if (a.Degree < b.Degree) {
      let temp = a;
      a = b;
      b = temp;
    }
    let rLast = a;
    let r = b;
    let sLast = this.field.One;
    let s = this.field.Zero;
    let tLast = this.field.Zero;
    let t = this.field.One;
    while (r.Degree >= Math.floor(R / 2)) {
      let rLastLast = rLast;
      let sLastLast = sLast;
      let tLastLast = tLast;
      rLast = r;
      sLast = s;
      tLast = t;
      if (rLast.Zero) {
        throw "r_{i-1} was zero";
      }
      r = rLastLast;
      let q = this.field.Zero;
      let denominatorLeadingTerm = rLast.getCoefficient(rLast.Degree);
      let dltInverse = this.field.inverse(denominatorLeadingTerm);
      while (r.Degree >= rLast.Degree && !r.Zero) {
        let degreeDiff = r.Degree - rLast.Degree;
        let scale = this.field.multiply(r.getCoefficient(r.Degree), dltInverse);
        q = q.addOrSubtract(this.field.buildMonomial(degreeDiff, scale));
        r = r.addOrSubtract(rLast.multiplyByMonomial(degreeDiff, scale));
      }
      s = q.multiply1(sLast).addOrSubtract(sLastLast);
      t = q.multiply1(tLast).addOrSubtract(tLastLast);
    }
    let sigmaTildeAtZero = t.getCoefficient(0);
    if (sigmaTildeAtZero === 0) {
      throw "ReedSolomonException sigmaTilde(0) was zero";
    }
    let inverse = this.field.inverse(sigmaTildeAtZero);
    let sigma = t.multiply2(inverse);
    let omega = r.multiply2(inverse);
    return new Array(sigma, omega);
  };
  this.findErrorLocations = function(errorLocator) {
    let numErrors = errorLocator.Degree;
    if (numErrors == 1) {
      return new Array(errorLocator.getCoefficient(1));
    }
    let result = new Array(numErrors);
    let e = 0;
    for (let i = 1; i < 256 && e < numErrors; i++) {
      if (errorLocator.evaluateAt(i) === 0) {
        result[e] = this.field.inverse(i);
        e++;
      }
    }
    if (e != numErrors) {
      throw "Error locator degree does not match number of roots";
    }
    return result;
  };
  this.findErrorMagnitudes = function(errorEvaluator, errorLocations, dataMatrix) {
    let s = errorLocations.length;
    let result = new Array(s);
    for (let i = 0; i < s; i++) {
      let xiInverse = this.field.inverse(errorLocations[i]);
      let denominator = 1;
      for (let j = 0; j < s; j++) {
        if (i != j) {
          denominator = this.field.multiply(denominator, GF256.addOrSubtract(1, this.field.multiply(errorLocations[j], xiInverse)));
        }
      }
      result[i] = this.field.multiply(errorEvaluator.evaluateAt(xiInverse), this.field.inverse(denominator));
      if (dataMatrix) {
        result[i] = this.field.multiply(result[i], xiInverse);
      }
    }
    return result;
  };
}

function GF256Poly(field, coefficients) {
  if (coefficients === null || coefficients.length === 0) {
    throw "System.ArgumentException";
  }
  this.field = field;
  let coefficientsLength = coefficients.length;
  if (coefficientsLength > 1 && coefficients[0] === 0) {
    let firstNonZero = 1;
    while (firstNonZero < coefficientsLength && coefficients[firstNonZero] === 0) {
      firstNonZero++;
    }
    if (firstNonZero == coefficientsLength) {
      this.coefficients = field.Zero.coefficients;
    } else {
      this.coefficients = new Array(coefficientsLength - firstNonZero);
      for (let i = 0; i < this.coefficients.length; i++) this.coefficients[i] = 0;
      for (let ci = 0; ci < this.coefficients.length; ci++) this.coefficients[ci] = coefficients[firstNonZero + ci];
    }
  } else {
    this.coefficients = coefficients;
  }
  this.__defineGetter__("Zero", function() {
    return this.coefficients[0] === 0;
  });
  this.__defineGetter__("Degree", function() {
    return this.coefficients.length - 1;
  });
  this.__defineGetter__("Coefficients", function() {
    return this.coefficients;
  });
  this.getCoefficient = function(degree) {
    return this.coefficients[this.coefficients.length - 1 - degree];
  };
  this.evaluateAt = function(a) {
    if (a === 0) {
      return this.getCoefficient(0);
    }
    let size = this.coefficients.length;
    if (a == 1) {
      let result = 0;
      for (let i = 0; i < size; i++) {
        result = GF256.addOrSubtract(result, this.coefficients[i]);
      }
      return result;
    }
    let result2 = this.coefficients[0];
    for (let i = 1; i < size; i++) {
      result2 = GF256.addOrSubtract(this.field.multiply(a, result2), this.coefficients[i]);
    }
    return result2;
  };
  this.addOrSubtract = function(other) {
    if (this.field != other.field) {
      throw "GF256Polys do not have same GF256 field";
    }
    if (this.Zero) {
      return other;
    }
    if (other.Zero) {
      return this;
    }
    let smallerCoefficients = this.coefficients;
    let largerCoefficients = other.coefficients;
    if (smallerCoefficients.length > largerCoefficients.length) {
      let temp = smallerCoefficients;
      smallerCoefficients = largerCoefficients;
      largerCoefficients = temp;
    }
    let sumDiff = new Array(largerCoefficients.length);
    let lengthDiff = largerCoefficients.length - smallerCoefficients.length;
    for (let ci = 0; ci < lengthDiff; ci++) sumDiff[ci] = largerCoefficients[ci];
    for (let i = lengthDiff; i < largerCoefficients.length; i++) {
      sumDiff[i] = GF256.addOrSubtract(smallerCoefficients[i - lengthDiff], largerCoefficients[i]);
    }
    return new GF256Poly(field, sumDiff);
  };
  this.multiply1 = function(other) {
    if (this.field != other.field) {
      throw "GF256Polys do not have same GF256 field";
    }
    if (this.Zero || other.Zero) {
      return this.field.Zero;
    }
    let aCoefficients = this.coefficients;
    let aLength = aCoefficients.length;
    let bCoefficients = other.coefficients;
    let bLength = bCoefficients.length;
    let product = new Array(aLength + bLength - 1);
    for (let i = 0; i < aLength; i++) {
      let aCoeff = aCoefficients[i];
      for (let j = 0; j < bLength; j++) {
        product[i + j] = GF256.addOrSubtract(product[i + j], this.field.multiply(aCoeff, bCoefficients[j]));
      }
    }
    return new GF256Poly(this.field, product);
  };
  this.multiply2 = function(scalar) {
    if (scalar === 0) {
      return this.field.Zero;
    }
    if (scalar == 1) {
      return this;
    }
    let size = this.coefficients.length;
    let product = new Array(size);
    for (let i = 0; i < size; i++) {
      product[i] = this.field.multiply(this.coefficients[i], scalar);
    }
    return new GF256Poly(this.field, product);
  };
  this.multiplyByMonomial = function(degree, coefficient) {
    if (degree < 0) {
      throw "System.ArgumentException";
    }
    if (coefficient === 0) {
      return this.field.Zero;
    }
    let size = this.coefficients.length;
    let product = new Array(size + degree);
    for (let i = 0; i < product.length; i++) product[i] = 0;
    for (let i = 0; i < size; i++) {
      product[i] = this.field.multiply(this.coefficients[i], coefficient);
    }
    return new GF256Poly(this.field, product);
  };
  this.divide = function(other) {
    if (this.field != other.field) {
      throw "GF256Polys do not have same GF256 field";
    }
    if (other.Zero) {
      throw "Divide by 0";
    }
    let quotient = this.field.Zero;
    let remainder = this;
    let denominatorLeadingTerm = other.getCoefficient(other.Degree);
    let inverseDenominatorLeadingTerm = this.field.inverse(denominatorLeadingTerm);
    while (remainder.Degree >= other.Degree && !remainder.Zero) {
      let degreeDifference = remainder.Degree - other.Degree;
      let scale = this.field.multiply(remainder.getCoefficient(remainder.Degree), inverseDenominatorLeadingTerm);
      let term = other.multiplyByMonomial(degreeDifference, scale);
      let iterationQuotient = this.field.buildMonomial(degreeDifference, scale);
      quotient = quotient.addOrSubtract(iterationQuotient);
      remainder = remainder.addOrSubtract(term);
    }
    return new Array(quotient, remainder);
  };
}

function GF256(primitive) {
  this.expTable = new Array(256);
  this.logTable = new Array(256);
  let x = 1;
  for (let i = 0; i < 256; i++) {
    this.expTable[i] = x;
    x <<= 1;
    if (x >= 256) {
      x ^= primitive;
    }
  }
  for (let i = 0; i < 255; i++) {
    this.logTable[this.expTable[i]] = i;
  }
  let at0 = new Array(1);
  at0[0] = 0;
  this.zero = new GF256Poly(this, new Array(at0));
  let at1 = new Array(1);
  at1[0] = 1;
  this.one = new GF256Poly(this, new Array(at1));
  this.__defineGetter__("Zero", function() {
    return this.zero;
  });
  this.__defineGetter__("One", function() {
    return this.one;
  });
  this.buildMonomial = function(degree, coefficient) {
    if (degree < 0) {
      throw "System.ArgumentException";
    }
    if (coefficient === 0) {
      return this.zero;
    }
    let coefficients = new Array(degree + 1);
    for (let i = 0; i < coefficients.length; i++) coefficients[i] = 0;
    coefficients[0] = coefficient;
    return new GF256Poly(this, coefficients);
  };
  this.exp = function(a) {
    return this.expTable[a];
  };
  this.log = function(a) {
    if (a === 0) {
      throw "System.ArgumentException";
    }
    return this.logTable[a];
  };
  this.inverse = function(a) {
    if (a === 0) {
      throw "System.ArithmeticException";
    }
    return this.expTable[255 - this.logTable[a]];
  };
  this.multiply = function(a, b) {
    if (a === 0 || b === 0) {
      return 0;
    }
    if (a == 1) {
      return b;
    }
    if (b == 1) {
      return a;
    }
    return this.expTable[(this.logTable[a] + this.logTable[b]) % 255];
  };
}

GF256.QR_CODE_FIELD = new GF256(285);

GF256.DATA_MATRIX_FIELD = new GF256(301);

GF256.addOrSubtract = function(a, b) {
  return a ^ b;
};

var Decoder = {};

Decoder.rsDecoder = new ReedSolomonDecoder(GF256.QR_CODE_FIELD);

Decoder.correctErrors = function(codewordBytes, numDataCodewords) {
  let numCodewords = codewordBytes.length;
  let codewordsInts = new Array(numCodewords);
  for (let i = 0; i < numCodewords; i++) {
    codewordsInts[i] = codewordBytes[i] & 255;
  }
  let numECCodewords = codewordBytes.length - numDataCodewords;
  try {
    Decoder.rsDecoder.decode(codewordsInts, numECCodewords);
  } catch (rse) {
    throw rse;
  }
  for (let i = 0; i < numDataCodewords; i++) {
    codewordBytes[i] = codewordsInts[i];
  }
};

Decoder.decode = function(bits) {
  let parser = new BitMatrixParser(bits);
  let version = parser.readVersion();
  let ecLevel = parser.readFormatInformation().ErrorCorrectionLevel;
  let codewords = parser.readCodewords();
  let dataBlocks = DataBlock.getDataBlocks(codewords, version, ecLevel);
  let totalBytes = 0;
  for (let i = 0; i < dataBlocks.length; i++) {
    totalBytes += dataBlocks[i].NumDataCodewords;
  }
  let resultBytes = new Array(totalBytes);
  let resultOffset = 0;
  for (let j = 0; j < dataBlocks.length; j++) {
    let dataBlock = dataBlocks[j];
    let codewordBytes = dataBlock.Codewords;
    let numDataCodewords = dataBlock.NumDataCodewords;
    Decoder.correctErrors(codewordBytes, numDataCodewords);
    for (let i = 0; i < numDataCodewords; i++) {
      resultBytes[resultOffset++] = codewordBytes[i];
    }
  }
  let reader = new QRCodeDataBlockReader(resultBytes, version.VersionNumber, ecLevel.Bits);
  return reader;
};

// mozilla: Get access to a window
var Services = require("Services");

var DebuggerServer = require("devtools/server/main").DebuggerServer;

var window = Services.wm.getMostRecentWindow(DebuggerServer.chromeWindowType);

var document = window.document;

var Image = window.Image;

var HTML_NS = "http://www.w3.org/1999/xhtml";

var qrcode = {};

qrcode.callback = null;

qrcode.errback = null;

qrcode.decode = function(src) {
  if (arguments.length === 0) {
    let canvas_qr = document.getElementById("qr-canvas");
    let context = canvas_qr.getContext("2d");
    imgWidth = canvas_qr.width;
    imgHeight = canvas_qr.height;
    imgU8 = context.getImageData(0, 0, imgWidth, imgHeight).data;
    imgU32 = new Uint32Array(imgU8.buffer);
    qrcode.result = qrcode.process(context);
    if (qrcode.callback !== null) {
      qrcode.callback(qrcode.result);
    }
    return qrcode.result;
  } else {
    let image = new Image();
    image.onload = function() {
      // mozilla: Use HTML namespace explicitly
      let canvas_qr = document.createElementNS(HTML_NS, "canvas");
      let context = canvas_qr.getContext("2d");
      let nheight = image.height;
      let nwidth = image.width;
      if (image.width * image.height > maxImgSize) {
        let ir = image.width / image.height;
        nheight = Math.sqrt(maxImgSize / ir);
        nwidth = ir * nheight;
      }
      canvas_qr.width = nwidth;
      canvas_qr.height = nheight;
      context.drawImage(image, 0, 0, canvas_qr.width, canvas_qr.height);
      imgWidth = canvas_qr.width;
      imgHeight = canvas_qr.height;
      try {
        imgU8 = context.getImageData(0, 0, canvas_qr.width, canvas_qr.height).data;
        imgU32 = new Uint32Array(imgU8.buffer);
      } catch (e) {
        qrcode.result = "Cross domain image reading not supported in your browser! Save it to your computer then drag and drop the file!";
        if (qrcode.callback !== null) {
          qrcode.callback(qrcode.result);
        }
        return;
      }
      try {
        qrcode.result = qrcode.process(context);
        if (qrcode.callback !== null) {
          qrcode.callback(qrcode.result);
        }
      } catch (e) {
        if (qrcode.errback !== null) {
          qrcode.errback(e);
        } else {
          console.error(e);
        }
        qrcode.result = "error decoding QR Code";
      }
    };
    image.src = src;
  }
};

qrcode.isUrl = function(s) {
  let regexp = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;
  return regexp.test(s);
};

qrcode.decode_url = function(s) {
  let escaped = "";
  try {
    escaped = escape(s);
  } catch (e) {
    console.log(e);
    escaped = s;
  }
  let ret = "";
  try {
    ret = decodeURIComponent(escaped);
  } catch (e) {
    console.log(e);
    ret = escaped;
  }
  return ret;
};

qrcode.decode_utf8 = function(s) {
  if (qrcode.isUrl(s)) return qrcode.decode_url(s); else return s;
};

qrcode.process = function(ctx) {
  let image = qrcode.grayScaleToBitmap(qrcode.grayscale());
  let detector = new Detector(image);
  let qRCodeMatrix = detector.detect();
  let reader = Decoder.decode(qRCodeMatrix.bits);
  let data = reader.DataByte;
  let str = "";
  for (let i = 0; i < data.length; i++) {
    for (let j = 0; j < data[i].length; j++) str += String.fromCharCode(data[i][j]);
  }
  return qrcode.decode_utf8(str);
};

qrcode.getMiddleBrightnessPerArea = function(image) {
  let numSqrtArea = 4;
  let areaWidth = Math.floor(imgWidth / numSqrtArea);
  let areaHeight = Math.floor(imgHeight / numSqrtArea);
  let minmax = new Array(numSqrtArea);
  for (let i = 0; i < numSqrtArea; i++) {
    minmax[i] = new Array(numSqrtArea);
    for (let i2 = 0; i2 < numSqrtArea; i2++) {
      minmax[i][i2] = new Array(0, 0);
    }
  }
  for (let ay = 0; ay < numSqrtArea; ay++) {
    for (let ax = 0; ax < numSqrtArea; ax++) {
      minmax[ax][ay][0] = 255;
      for (let dy = 0; dy < areaHeight; dy++) {
        for (let dx = 0; dx < areaWidth; dx++) {
          let target = image[areaWidth * ax + dx + (areaHeight * ay + dy) * imgWidth];
          if (target < minmax[ax][ay][0]) minmax[ax][ay][0] = target;
          if (target > minmax[ax][ay][1]) minmax[ax][ay][1] = target;
        }
      }
    }
  }
  let middle = new Array(numSqrtArea);
  for (let i3 = 0; i3 < numSqrtArea; i3++) {
    middle[i3] = new Array(numSqrtArea);
  }
  for (let ay = 0; ay < numSqrtArea; ay++) {
    for (let ax = 0; ax < numSqrtArea; ax++) {
      middle[ax][ay] = Math.floor((minmax[ax][ay][0] + minmax[ax][ay][1]) / 2);
    }
  }
  return middle;
};

qrcode.grayScaleToBitmap = function(grayScale) {
  let middle = qrcode.getMiddleBrightnessPerArea(grayScale);
  let sqrtNumArea = middle.length;
  let areaWidth = Math.floor(imgWidth / sqrtNumArea);
  let areaHeight = Math.floor(imgHeight / sqrtNumArea);
  let bitmap = new Array(imgHeight * imgWidth);
  for (let ay = 0; ay < sqrtNumArea; ay++) {
    for (let ax = 0; ax < sqrtNumArea; ax++) {
      for (let dy = 0; dy < areaHeight; dy++) {
        for (let dx = 0; dx < areaWidth; dx++) {
          bitmap[areaWidth * ax + dx + (areaHeight * ay + dy) * imgWidth] = grayScale[areaWidth * ax + dx + (areaHeight * ay + dy) * imgWidth] < middle[ax][ay] ? true : false;
        }
      }
    }
  }
  return bitmap;
};

qrcode.grayscale = function() {
  let ret = new Uint8ClampedArray(imgWidth * imgHeight);
  for (let y = 0; y < imgHeight; y++) {
    for (let x = 0; x < imgWidth; x++) {
      let point = x + y * imgWidth;
      let rgba = imgU32[point];
      let p = (rgba & 0xFF) + ((rgba >> 8) & 0xFF) + ((rgba >> 16) & 0xFF);
      ret[x + y * imgWidth] = p / 3;
    }
  }
  return ret;
};

function URShift(number, bits) {
  if (number >= 0) return number >> bits; else return (number >> bits) + (2 << ~bits);
}

// mozilla: Add module support
module.exports = {
  decodeFromURI: function(src, cb, errcb) {
    if (cb) {
      qrcode.callback = cb;
    }
    if (errcb) {
      qrcode.errback = errcb;
    }
    return qrcode.decode(src);
  },
  decodeFromCanvas: function(canvas, cb) {
    let context = canvas.getContext("2d");
    imgWidth = canvas.width;
    imgHeight = canvas.height;
    imgU8 = context.getImageData(0, 0, imgWidth, imgHeight).data;
    imgU32 = new Uint32Array(imgU8.buffer);
    let result = qrcode.process(context);
    if (cb) {
      cb(result);
    }
    return result;
  }
};

var MIN_SKIP = 3;

var MAX_MODULES = 57;

var INTEGER_MATH_SHIFT = 8;

var CENTER_QUORUM = 2;

qrcode.orderBestPatterns = function(patterns) {
  function distance(pattern1, pattern2) {
    let xDiff = pattern1.X - pattern2.X;
    let yDiff = pattern1.Y - pattern2.Y;
    return Math.sqrt(xDiff * xDiff + yDiff * yDiff);
  }
  function crossProductZ(pointA, pointB, pointC) {
    let bX = pointB.x;
    let bY = pointB.y;
    return (pointC.x - bX) * (pointA.y - bY) - (pointC.y - bY) * (pointA.x - bX);
  }
  let zeroOneDistance = distance(patterns[0], patterns[1]);
  let oneTwoDistance = distance(patterns[1], patterns[2]);
  let zeroTwoDistance = distance(patterns[0], patterns[2]);
  let pointA, pointB, pointC;
  if (oneTwoDistance >= zeroOneDistance && oneTwoDistance >= zeroTwoDistance) {
    pointB = patterns[0];
    pointA = patterns[1];
    pointC = patterns[2];
  } else if (zeroTwoDistance >= oneTwoDistance && zeroTwoDistance >= zeroOneDistance) {
    pointB = patterns[1];
    pointA = patterns[0];
    pointC = patterns[2];
  } else {
    pointB = patterns[2];
    pointA = patterns[0];
    pointC = patterns[1];
  }
  if (crossProductZ(pointA, pointB, pointC) < 0) {
    let temp = pointA;
    pointA = pointC;
    pointC = temp;
  }
  patterns[0] = pointA;
  patterns[1] = pointB;
  patterns[2] = pointC;
};

function FinderPattern(posX, posY, estimatedModuleSize) {
  this.x = posX;
  this.y = posY;
  this.count = 1;
  this.estimatedModuleSize = estimatedModuleSize;
  this.__defineGetter__("EstimatedModuleSize", function() {
    return this.estimatedModuleSize;
  });
  this.__defineGetter__("Count", function() {
    return this.count;
  });
  this.__defineGetter__("X", function() {
    return this.x;
  });
  this.__defineGetter__("Y", function() {
    return this.y;
  });
  this.incrementCount = function() {
    this.count++;
  };
  this.aboutEquals = function(moduleSize, i, j) {
    if (Math.abs(i - this.y) <= moduleSize && Math.abs(j - this.x) <= moduleSize) {
      let moduleSizeDiff = Math.abs(moduleSize - this.estimatedModuleSize);
      return moduleSizeDiff <= 1 || moduleSizeDiff / this.estimatedModuleSize <= 1;
    }
    return false;
  };
}

function FinderPatternInfo(patternCenters) {
  this.bottomLeft = patternCenters[0];
  this.topLeft = patternCenters[1];
  this.topRight = patternCenters[2];
  this.__defineGetter__("BottomLeft", function() {
    return this.bottomLeft;
  });
  this.__defineGetter__("TopLeft", function() {
    return this.topLeft;
  });
  this.__defineGetter__("TopRight", function() {
    return this.topRight;
  });
}

function FinderPatternFinder() {
  this.image = null;
  this.possibleCenters = [];
  this.hasSkipped = false;
  this.crossCheckStateCount = new Array(0, 0, 0, 0, 0);
  this.resultPointCallback = null;
  this.__defineGetter__("CrossCheckStateCount", function() {
    this.crossCheckStateCount[0] = 0;
    this.crossCheckStateCount[1] = 0;
    this.crossCheckStateCount[2] = 0;
    this.crossCheckStateCount[3] = 0;
    this.crossCheckStateCount[4] = 0;
    return this.crossCheckStateCount;
  });
  this.foundPatternCross = function(stateCount) {
    let totalModuleSize = 0;
    for (let i = 0; i < 5; i++) {
      let count = stateCount[i];
      if (count === 0) {
        return false;
      }
      totalModuleSize += count;
    }
    if (totalModuleSize < 7) {
      return false;
    }
    let moduleSize = Math.floor((totalModuleSize << INTEGER_MATH_SHIFT) / 7);
    let maxVariance = Math.floor(moduleSize / 2);
    return Math.abs(moduleSize - (stateCount[0] << INTEGER_MATH_SHIFT)) < maxVariance && Math.abs(moduleSize - (stateCount[1] << INTEGER_MATH_SHIFT)) < maxVariance && Math.abs(3 * moduleSize - (stateCount[2] << INTEGER_MATH_SHIFT)) < 3 * maxVariance && Math.abs(moduleSize - (stateCount[3] << INTEGER_MATH_SHIFT)) < maxVariance && Math.abs(moduleSize - (stateCount[4] << INTEGER_MATH_SHIFT)) < maxVariance;
  };
  this.centerFromEnd = function(stateCount, end) {
    return end - stateCount[4] - stateCount[3] - stateCount[2] / 2;
  };
  this.crossCheckVertical = function(startI, centerJ, maxCount, originalStateCountTotal) {
    let image = this.image;
    let maxI = imgHeight;
    let stateCount = this.CrossCheckStateCount;
    let i = startI;
    while (i >= 0 && image[centerJ + i * imgWidth]) {
      stateCount[2]++;
      i--;
    }
    if (i < 0) {
      return NaN;
    }
    while (i >= 0 && !image[centerJ + i * imgWidth] && stateCount[1] <= maxCount) {
      stateCount[1]++;
      i--;
    }
    if (i < 0 || stateCount[1] > maxCount) {
      return NaN;
    }
    while (i >= 0 && image[centerJ + i * imgWidth] && stateCount[0] <= maxCount) {
      stateCount[0]++;
      i--;
    }
    if (stateCount[0] > maxCount) {
      return NaN;
    }
    i = startI + 1;
    while (i < maxI && image[centerJ + i * imgWidth]) {
      stateCount[2]++;
      i++;
    }
    if (i == maxI) {
      return NaN;
    }
    while (i < maxI && !image[centerJ + i * imgWidth] && stateCount[3] < maxCount) {
      stateCount[3]++;
      i++;
    }
    if (i == maxI || stateCount[3] >= maxCount) {
      return NaN;
    }
    while (i < maxI && image[centerJ + i * imgWidth] && stateCount[4] < maxCount) {
      stateCount[4]++;
      i++;
    }
    if (stateCount[4] >= maxCount) {
      return NaN;
    }
    let stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4];
    if (5 * Math.abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal) {
      return NaN;
    }
    return this.foundPatternCross(stateCount) ? this.centerFromEnd(stateCount, i) : NaN;
  };
  this.crossCheckHorizontal = function(startJ, centerI, maxCount, originalStateCountTotal) {
    let image = this.image;
    let maxJ = imgWidth;
    let stateCount = this.CrossCheckStateCount;
    let j = startJ;
    while (j >= 0 && image[j + centerI * imgWidth]) {
      stateCount[2]++;
      j--;
    }
    if (j < 0) {
      return NaN;
    }
    while (j >= 0 && !image[j + centerI * imgWidth] && stateCount[1] <= maxCount) {
      stateCount[1]++;
      j--;
    }
    if (j < 0 || stateCount[1] > maxCount) {
      return NaN;
    }
    while (j >= 0 && image[j + centerI * imgWidth] && stateCount[0] <= maxCount) {
      stateCount[0]++;
      j--;
    }
    if (stateCount[0] > maxCount) {
      return NaN;
    }
    j = startJ + 1;
    while (j < maxJ && image[j + centerI * imgWidth]) {
      stateCount[2]++;
      j++;
    }
    if (j == maxJ) {
      return NaN;
    }
    while (j < maxJ && !image[j + centerI * imgWidth] && stateCount[3] < maxCount) {
      stateCount[3]++;
      j++;
    }
    if (j == maxJ || stateCount[3] >= maxCount) {
      return NaN;
    }
    while (j < maxJ && image[j + centerI * imgWidth] && stateCount[4] < maxCount) {
      stateCount[4]++;
      j++;
    }
    if (stateCount[4] >= maxCount) {
      return NaN;
    }
    let stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4];
    if (5 * Math.abs(stateCountTotal - originalStateCountTotal) >= originalStateCountTotal) {
      return NaN;
    }
    return this.foundPatternCross(stateCount) ? this.centerFromEnd(stateCount, j) : NaN;
  };
  this.handlePossibleCenter = function(stateCount, i, j) {
    let stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4];
    let centerJ = this.centerFromEnd(stateCount, j);
    let centerI = this.crossCheckVertical(i, Math.floor(centerJ), stateCount[2], stateCountTotal);
    if (!isNaN(centerI)) {
      centerJ = this.crossCheckHorizontal(Math.floor(centerJ), Math.floor(centerI), stateCount[2], stateCountTotal);
      if (!isNaN(centerJ)) {
        let estimatedModuleSize = stateCountTotal / 7;
        let found = false;
        let max = this.possibleCenters.length;
        for (let index = 0; index < max; index++) {
          let center = this.possibleCenters[index];
          if (center.aboutEquals(estimatedModuleSize, centerI, centerJ)) {
            center.incrementCount();
            found = true;
            break;
          }
        }
        if (!found) {
          let point = new FinderPattern(centerJ, centerI, estimatedModuleSize);
          this.possibleCenters.push(point);
          if (this.resultPointCallback !== null) {
            this.resultPointCallback.foundPossibleResultPoint(point);
          }
        }
        return true;
      }
    }
    return false;
  };
  this.selectBestPatterns = function() {
    let startSize = this.possibleCenters.length;
    if (startSize < 3) {
      throw Error("Couldn't find enough finder patterns");
    }
    if (startSize > 3) {
      let totalModuleSize = 0;
      let square = 0;
      for (let i = 0; i < startSize; i++) {
        let centerValue = this.possibleCenters[i].EstimatedModuleSize;
        totalModuleSize += centerValue;
        square += centerValue * centerValue;
      }
      let average = totalModuleSize / startSize;
      this.possibleCenters.sort(function(center1, center2) {
        let dA = Math.abs(center2.EstimatedModuleSize - average);
        let dB = Math.abs(center1.EstimatedModuleSize - average);
        if (dA < dB) {
          return -1;
        } else if (dA == dB) {
          return 0;
        } else {
          return 1;
        }
      });
      let stdDev = Math.sqrt(square / startSize - average * average);
      let limit = Math.max(0.2 * average, stdDev);
      for (let i = 0; i < this.possibleCenters.length && this.possibleCenters.length > 3; i++) {
        let pattern = this.possibleCenters[i];
        if (Math.abs(pattern.EstimatedModuleSize - average) > limit) {
          // mozilla: use splice instead
          this.possibleCenters.splice(i, 1);
          i--;
        }
      }
    }
    if (this.possibleCenters.length > 3) {
      this.possibleCenters.sort(function(a, b) {
        if (a.count > b.count) {
          return -1;
        }
        if (a.count < b.count) {
          return 1;
        }
        return 0;
      });
    }
    return new Array(this.possibleCenters[0], this.possibleCenters[1], this.possibleCenters[2]);
  };
  this.findRowSkip = function() {
    let max = this.possibleCenters.length;
    if (max <= 1) {
      return 0;
    }
    let firstConfirmedCenter = null;
    for (let i = 0; i < max; i++) {
      let center = this.possibleCenters[i];
      if (center.Count >= CENTER_QUORUM) {
        if (firstConfirmedCenter === null) {
          firstConfirmedCenter = center;
        } else {
          this.hasSkipped = true;
          return Math.floor((Math.abs(firstConfirmedCenter.X - center.X) - Math.abs(firstConfirmedCenter.Y - center.Y)) / 2);
        }
      }
    }
    return 0;
  };
  this.haveMultiplyConfirmedCenters = function() {
    let confirmedCount = 0;
    let totalModuleSize = 0;
    let max = this.possibleCenters.length;
    for (let i = 0; i < max; i++) {
      let pattern = this.possibleCenters[i];
      if (pattern.Count >= CENTER_QUORUM) {
        confirmedCount++;
        totalModuleSize += pattern.EstimatedModuleSize;
      }
    }
    if (confirmedCount < 3) {
      return false;
    }
    let average = totalModuleSize / max;
    let totalDeviation = 0;
    for (let i = 0; i < max; i++) {
      let pattern = this.possibleCenters[i];
      totalDeviation += Math.abs(pattern.EstimatedModuleSize - average);
    }
    return totalDeviation <= 0.05 * totalModuleSize;
  };
  this.findFinderPattern = function(image) {
    let tryHarder = false;
    this.image = image;
    let maxI = imgHeight;
    let maxJ = imgWidth;
    let iSkip = Math.floor(3 * maxI / (4 * MAX_MODULES));
    if (iSkip < MIN_SKIP || tryHarder) {
      iSkip = MIN_SKIP;
    }
    let done = false;
    let stateCount = new Array(5);
    for (let i = iSkip - 1; i < maxI && !done; i += iSkip) {
      stateCount[0] = 0;
      stateCount[1] = 0;
      stateCount[2] = 0;
      stateCount[3] = 0;
      stateCount[4] = 0;
      let currentState = 0;
      for (let j = 0; j < maxJ; j++) {
        if (image[j + i * imgWidth]) {
          if ((currentState & 1) == 1) {
            currentState++;
          }
          stateCount[currentState]++;
        } else {
          if ((currentState & 1) === 0) {
            if (currentState == 4) {
              if (this.foundPatternCross(stateCount)) {
                let confirmed = this.handlePossibleCenter(stateCount, i, j);
                if (confirmed) {
                  iSkip = 2;
                  if (this.hasSkipped) {
                    done = this.haveMultiplyConfirmedCenters();
                  } else {
                    let rowSkip = this.findRowSkip();
                    if (rowSkip > stateCount[2]) {
                      i += rowSkip - stateCount[2] - iSkip;
                      j = maxJ - 1;
                    }
                  }
                } else {
                  do {
                    j++;
                  } while (j < maxJ && !image[j + i * imgWidth]);
                  j--;
                }
                currentState = 0;
                stateCount[0] = 0;
                stateCount[1] = 0;
                stateCount[2] = 0;
                stateCount[3] = 0;
                stateCount[4] = 0;
              } else {
                stateCount[0] = stateCount[2];
                stateCount[1] = stateCount[3];
                stateCount[2] = stateCount[4];
                stateCount[3] = 1;
                stateCount[4] = 0;
                currentState = 3;
              }
            } else {
              stateCount[++currentState]++;
            }
          } else {
            stateCount[currentState]++;
          }
        }
      }
      if (this.foundPatternCross(stateCount)) {
        let confirmed = this.handlePossibleCenter(stateCount, i, maxJ);
        if (confirmed) {
          iSkip = stateCount[0];
          if (this.hasSkipped) {
            done = this.haveMultiplyConfirmedCenters();
          }
        }
      }
    }
    let patternInfo = this.selectBestPatterns();
    qrcode.orderBestPatterns(patternInfo);
    return new FinderPatternInfo(patternInfo);
  };
}

function AlignmentPattern(posX, posY, estimatedModuleSize) {
  this.x = posX;
  this.y = posY;
  this.count = 1;
  this.estimatedModuleSize = estimatedModuleSize;
  this.__defineGetter__("EstimatedModuleSize", function() {
    return this.estimatedModuleSize;
  });
  this.__defineGetter__("Count", function() {
    return this.count;
  });
  this.__defineGetter__("X", function() {
    return Math.floor(this.x);
  });
  this.__defineGetter__("Y", function() {
    return Math.floor(this.y);
  });
  this.incrementCount = function() {
    this.count++;
  };
  this.aboutEquals = function(moduleSize, i, j) {
    if (Math.abs(i - this.y) <= moduleSize && Math.abs(j - this.x) <= moduleSize) {
      let moduleSizeDiff = Math.abs(moduleSize - this.estimatedModuleSize);
      return moduleSizeDiff <= 1 || moduleSizeDiff / this.estimatedModuleSize <= 1;
    }
    return false;
  };
}

function AlignmentPatternFinder(image, startX, startY, width, height, moduleSize, resultPointCallback) {
  this.image = image;
  this.possibleCenters = [];
  this.startX = startX;
  this.startY = startY;
  this.width = width;
  this.height = height;
  this.moduleSize = moduleSize;
  this.crossCheckStateCount = new Array(0, 0, 0);
  this.resultPointCallback = resultPointCallback;
  this.centerFromEnd = function(stateCount, end) {
    return end - stateCount[2] - stateCount[1] / 2;
  };
  this.foundPatternCross = function(stateCount) {
    let moduleSize = this.moduleSize;
    let maxVariance = moduleSize / 2;
    for (let i = 0; i < 3; i++) {
      if (Math.abs(moduleSize - stateCount[i]) >= maxVariance) {
        return false;
      }
    }
    return true;
  };
  this.crossCheckVertical = function(startI, centerJ, maxCount, originalStateCountTotal) {
    let image = this.image;
    let maxI = imgHeight;
    let stateCount = this.crossCheckStateCount;
    stateCount[0] = 0;
    stateCount[1] = 0;
    stateCount[2] = 0;
    let i = startI;
    while (i >= 0 && image[centerJ + i * imgWidth] && stateCount[1] <= maxCount) {
      stateCount[1]++;
      i--;
    }
    if (i < 0 || stateCount[1] > maxCount) {
      return NaN;
    }
    while (i >= 0 && !image[centerJ + i * imgWidth] && stateCount[0] <= maxCount) {
      stateCount[0]++;
      i--;
    }
    if (stateCount[0] > maxCount) {
      return NaN;
    }
    i = startI + 1;
    while (i < maxI && image[centerJ + i * imgWidth] && stateCount[1] <= maxCount) {
      stateCount[1]++;
      i++;
    }
    if (i == maxI || stateCount[1] > maxCount) {
      return NaN;
    }
    while (i < maxI && !image[centerJ + i * imgWidth] && stateCount[2] <= maxCount) {
      stateCount[2]++;
      i++;
    }
    if (stateCount[2] > maxCount) {
      return NaN;
    }
    let stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2];
    if (5 * Math.abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal) {
      return NaN;
    }
    return this.foundPatternCross(stateCount) ? this.centerFromEnd(stateCount, i) : NaN;
  };
  this.handlePossibleCenter = function(stateCount, i, j) {
    let stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2];
    let centerJ = this.centerFromEnd(stateCount, j);
    let centerI = this.crossCheckVertical(i, Math.floor(centerJ), 2 * stateCount[1], stateCountTotal);
    if (!isNaN(centerI)) {
      let estimatedModuleSize = (stateCount[0] + stateCount[1] + stateCount[2]) / 3;
      let max = this.possibleCenters.length;
      for (let index = 0; index < max; index++) {
        let center = this.possibleCenters[index];
        if (center.aboutEquals(estimatedModuleSize, centerI, centerJ)) {
          return new AlignmentPattern(centerJ, centerI, estimatedModuleSize);
        }
      }
      let point = new AlignmentPattern(centerJ, centerI, estimatedModuleSize);
      this.possibleCenters.push(point);
      if (this.resultPointCallback !== null) {
        this.resultPointCallback.foundPossibleResultPoint(point);
      }
    }
    return null;
  };
  this.find = function() {
    let startX = this.startX;
    let height = this.height;
    let maxJ = startX + width;
    let middleI = startY + (height >> 1);
    let stateCount = new Array(0, 0, 0);
    for (let iGen = 0; iGen < height; iGen++) {
      let i = middleI + ((iGen & 1) === 0 ? iGen + 1 >> 1 : -(iGen + 1 >> 1));
      stateCount[0] = 0;
      stateCount[1] = 0;
      stateCount[2] = 0;
      let j = startX;
      while (j < maxJ && !image[j + imgWidth * i]) {
        j++;
      }
      let currentState = 0;
      while (j < maxJ) {
        if (image[j + i * imgWidth]) {
          if (currentState == 1) {
            stateCount[currentState]++;
          } else {
            if (currentState == 2) {
              if (this.foundPatternCross(stateCount)) {
                let confirmed = this.handlePossibleCenter(stateCount, i, j);
                if (confirmed !== null) {
                  return confirmed;
                }
              }
              stateCount[0] = stateCount[2];
              stateCount[1] = 1;
              stateCount[2] = 0;
              currentState = 1;
            } else {
              stateCount[++currentState]++;
            }
          }
        } else {
          if (currentState == 1) {
            currentState++;
          }
          stateCount[currentState]++;
        }
        j++;
      }
      if (this.foundPatternCross(stateCount)) {
        let confirmed = this.handlePossibleCenter(stateCount, i, maxJ);
        if (confirmed !== null) {
          return confirmed;
        }
      }
    }
    if (this.possibleCenters.length !== 0) {
      return this.possibleCenters[0];
    }
    throw "Couldn't find enough alignment patterns";
  };
}

function QRCodeDataBlockReader(blocks, version, numErrorCorrectionCode) {
  this.blockPointer = 0;
  this.bitPointer = 7;
  this.dataLength = 0;
  this.blocks = blocks;
  this.numErrorCorrectionCode = numErrorCorrectionCode;
  if (version <= 9) this.dataLengthMode = 0; else if (version >= 10 && version <= 26) this.dataLengthMode = 1; else if (version >= 27 && version <= 40) this.dataLengthMode = 2;
  this.getNextBits = function(numBits) {
    let bits = 0;
    if (numBits < this.bitPointer + 1) {
      let mask = 0;
      for (let i = 0; i < numBits; i++) {
        mask += 1 << i;
      }
      mask <<= this.bitPointer - numBits + 1;
      bits = (this.blocks[this.blockPointer] & mask) >> this.bitPointer - numBits + 1;
      this.bitPointer -= numBits;
      return bits;
    } else if (numBits < this.bitPointer + 1 + 8) {
      let mask1 = 0;
      for (let i = 0; i < this.bitPointer + 1; i++) {
        mask1 += 1 << i;
      }
      bits = (this.blocks[this.blockPointer] & mask1) << numBits - (this.bitPointer + 1);
      this.blockPointer++;
      bits += this.blocks[this.blockPointer] >> 8 - (numBits - (this.bitPointer + 1));
      this.bitPointer = this.bitPointer - numBits % 8;
      if (this.bitPointer < 0) {
        this.bitPointer = 8 + this.bitPointer;
      }
      return bits;
    } else if (numBits < this.bitPointer + 1 + 16) {
      let mask1 = 0;
      let mask3 = 0;
      for (let i = 0; i < this.bitPointer + 1; i++) {
        mask1 += 1 << i;
      }
      let bitsFirstBlock = (this.blocks[this.blockPointer] & mask1) << numBits - (this.bitPointer + 1);
      this.blockPointer++;
      let bitsSecondBlock = this.blocks[this.blockPointer] << numBits - (this.bitPointer + 1 + 8);
      this.blockPointer++;
      for (let i = 0; i < numBits - (this.bitPointer + 1 + 8); i++) {
        mask3 += 1 << i;
      }
      mask3 <<= 8 - (numBits - (this.bitPointer + 1 + 8));
      let bitsThirdBlock = (this.blocks[this.blockPointer] & mask3) >> 8 - (numBits - (this.bitPointer + 1 + 8));
      bits = bitsFirstBlock + bitsSecondBlock + bitsThirdBlock;
      this.bitPointer = this.bitPointer - (numBits - 8) % 8;
      if (this.bitPointer < 0) {
        this.bitPointer = 8 + this.bitPointer;
      }
      return bits;
    } else {
      return 0;
    }
  };
  this.NextMode = function() {
    if (this.blockPointer > this.blocks.length - this.numErrorCorrectionCode - 2) return 0; else return this.getNextBits(4);
  };
  this.getDataLength = function(modeIndicator) {
    let index = 0;
    while (true) {
      if (modeIndicator >> index == 1) break;
      index++;
    }
    return this.getNextBits(sizeOfDataLengthInfo[this.dataLengthMode][index]);
  };
  this.getRomanAndFigureString = function(dataLength) {
    var length = dataLength;
    let intData = 0;
    let strData = "";
    let tableRomanAndFigure = new Array("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", " ", "$", "%", "*", "+", "-", ".", "/", ":");
    do {
      if (length > 1) {
        intData = this.getNextBits(11);
        let firstLetter = Math.floor(intData / 45);
        let secondLetter = intData % 45;
        strData += tableRomanAndFigure[firstLetter];
        strData += tableRomanAndFigure[secondLetter];
        length -= 2;
      } else if (length == 1) {
        intData = this.getNextBits(6);
        strData += tableRomanAndFigure[intData];
        length -= 1;
      }
    } while (length > 0);
    return strData;
  };
  this.getFigureString = function(dataLength) {
    var length = dataLength;
    let intData = 0;
    let strData = "";
    do {
      if (length >= 3) {
        intData = this.getNextBits(10);
        if (intData < 100) strData += "0";
        if (intData < 10) strData += "0";
        length -= 3;
      } else if (length == 2) {
        intData = this.getNextBits(7);
        if (intData < 10) strData += "0";
        length -= 2;
      } else if (length == 1) {
        intData = this.getNextBits(4);
        length -= 1;
      }
      strData += intData;
    } while (length > 0);
    return strData;
  };
  this.get8bitByteArray = function(dataLength) {
    var length = dataLength;
    let intData = 0;
    let output = [];
    do {
      intData = this.getNextBits(8);
      output.push(intData);
      length--;
    } while (length > 0);
    return output;
  };
  this.getKanjiString = function(dataLength) {
    var length = dataLength;
    let intData = 0;
    let unicodeString = "";
    do {
      intData = this.getNextBits(13);
      let lowerByte = intData % 192;
      let higherByte = intData / 192;
      let tempWord = (higherByte << 8) + lowerByte;
      let shiftjisWord = 0;
      if (tempWord + 33088 <= 40956) {
        shiftjisWord = tempWord + 33088;
      } else {
        shiftjisWord = tempWord + 49472;
      }
      unicodeString += String.fromCharCode(shiftjisWord);
      length--;
    } while (length > 0);
    return unicodeString;
  };
  this.__defineGetter__("DataByte", function() {
    let output = [];
    let MODE_NUMBER = 1;
    let MODE_ROMAN_AND_NUMBER = 2;
    let MODE_8BIT_BYTE = 4;
    let MODE_KANJI = 8;
    do {
      let mode = this.NextMode();
      if (mode === 0) {
        if (output.length > 0) break; else throw "Empty data block";
      }
      if (mode != MODE_NUMBER && mode != MODE_ROMAN_AND_NUMBER && mode != MODE_8BIT_BYTE && mode != MODE_KANJI) {
        throw "Invalid mode: " + mode + " in (block:" + this.blockPointer + " bit:" + this.bitPointer + ")";
      }
      let dataLength = this.getDataLength(mode);
      if (dataLength < 1) {
        throw "Invalid data length: " + dataLength;
      }
      let temp_str;
      let ta;
      switch (mode) {
       case MODE_NUMBER:
        temp_str = this.getFigureString(dataLength);
        ta = new Array(temp_str.length);
        for (let j = 0; j < temp_str.length; j++) ta[j] = temp_str.charCodeAt(j);
        output.push(ta);
        break;

       case MODE_ROMAN_AND_NUMBER:
        temp_str = this.getRomanAndFigureString(dataLength);
        ta = new Array(temp_str.length);
        for (let j = 0; j < temp_str.length; j++) ta[j] = temp_str.charCodeAt(j);
        output.push(ta);
        break;

       case MODE_8BIT_BYTE:
        let temp_sbyteArray3 = this.get8bitByteArray(dataLength);
        output.push(temp_sbyteArray3);
        break;

       case MODE_KANJI:
        temp_str = this.getKanjiString(dataLength);
        output.push(temp_str);
        break;
      }
    } while (true);
    return output;
  });
}
PK
!<�L�f����?chrome/devtools/modules/devtools/shared/qrcode/encoder/index.js//---------------------------------------------------------------------
//
// QR Code Generator for JavaScript
//
// Copyright (c) 2009 Kazuhiko Arase
//
// URL: http://www.d-project.com/
//
// Licensed under the MIT license:
//	http://www.opensource.org/licenses/mit-license.php
//
// The word 'QR Code' is registered trademark of
// DENSO WAVE INCORPORATED
//	http://www.denso-wave.com/qrcode/faqpatent-e.html
//
//---------------------------------------------------------------------

var qrcode = function() {

	//---------------------------------------------------------------------
	// qrcode
	//---------------------------------------------------------------------

	/**
	 * qrcode
	 * @param typeNumber 1 to 10
	 * @param errorCorrectLevel 'L','M','Q','H'
	 */
	var qrcode = function(typeNumber, errorCorrectLevel) {

		var PAD0 = 0xEC;
		var PAD1 = 0x11;

		var _typeNumber = typeNumber;
		var _errorCorrectLevel = QRErrorCorrectLevel[errorCorrectLevel];
		var _modules = null;
		var _moduleCount = 0;
		var _dataCache = null;
		var _dataList = new Array();

		var _this = {};

		var makeImpl = function(test, maskPattern) {

			_moduleCount = _typeNumber * 4 + 17;
			_modules = function(moduleCount) {
				var modules = new Array(moduleCount);
				for (var row = 0; row < moduleCount; row += 1) {
					modules[row] = new Array(moduleCount);
					for (var col = 0; col < moduleCount; col += 1) {
						modules[row][col] = null;
					}
				}
				return modules;
			}(_moduleCount);

			setupPositionProbePattern(0, 0);
			setupPositionProbePattern(_moduleCount - 7, 0);
			setupPositionProbePattern(0, _moduleCount - 7);
			setupPositionAdjustPattern();
			setupTimingPattern();
			setupTypeInfo(test, maskPattern);

			if (_typeNumber >= 7) {
				setupTypeNumber(test);
			}

			if (_dataCache == null) {
				_dataCache = createData(_typeNumber, _errorCorrectLevel, _dataList);
			}

			mapData(_dataCache, maskPattern);
		};

		var setupPositionProbePattern = function(row, col) {

			for (var r = -1; r <= 7; r += 1) {

				if (row + r <= -1 || _moduleCount <= row + r) continue;

				for (var c = -1; c <= 7; c += 1) {

					if (col + c <= -1 || _moduleCount <= col + c) continue;

					if ( (0 <= r && r <= 6 && (c == 0 || c == 6) )
							|| (0 <= c && c <= 6 && (r == 0 || r == 6) )
							|| (2 <= r && r <= 4 && 2 <= c && c <= 4) ) {
						_modules[row + r][col + c] = true;
					} else {
						_modules[row + r][col + c] = false;
					}
				}
			}
		};

		var getBestMaskPattern = function() {

			var minLostPoint = 0;
			var pattern = 0;

			for (var i = 0; i < 8; i += 1) {

				makeImpl(true, i);

				var lostPoint = QRUtil.getLostPoint(_this);

				if (i == 0 || minLostPoint > lostPoint) {
					minLostPoint = lostPoint;
					pattern = i;
				}
			}

			return pattern;
		};

		var setupTimingPattern = function() {

			for (var r = 8; r < _moduleCount - 8; r += 1) {
				if (_modules[r][6] != null) {
					continue;
				}
				_modules[r][6] = (r % 2 == 0);
			}

			for (var c = 8; c < _moduleCount - 8; c += 1) {
				if (_modules[6][c] != null) {
					continue;
				}
				_modules[6][c] = (c % 2 == 0);
			}
		};

		var setupPositionAdjustPattern = function() {

			var pos = QRUtil.getPatternPosition(_typeNumber);

			for (var i = 0; i < pos.length; i += 1) {

				for (var j = 0; j < pos.length; j += 1) {

					var row = pos[i];
					var col = pos[j];

					if (_modules[row][col] != null) {
						continue;
					}

					for (var r = -2; r <= 2; r += 1) {

						for (var c = -2; c <= 2; c += 1) {

							if (r == -2 || r == 2 || c == -2 || c == 2
									|| (r == 0 && c == 0) ) {
								_modules[row + r][col + c] = true;
							} else {
								_modules[row + r][col + c] = false;
							}
						}
					}
				}
			}
		};

		var setupTypeNumber = function(test) {

			var bits = QRUtil.getBCHTypeNumber(_typeNumber);

			for (var i = 0; i < 18; i += 1) {
				var mod = (!test && ( (bits >> i) & 1) == 1);
				_modules[Math.floor(i / 3)][i % 3 + _moduleCount - 8 - 3] = mod;
			}

			for (var i = 0; i < 18; i += 1) {
				var mod = (!test && ( (bits >> i) & 1) == 1);
				_modules[i % 3 + _moduleCount - 8 - 3][Math.floor(i / 3)] = mod;
			}
		};

		var setupTypeInfo = function(test, maskPattern) {

			var data = (_errorCorrectLevel << 3) | maskPattern;
			var bits = QRUtil.getBCHTypeInfo(data);

			// vertical
			for (var i = 0; i < 15; i += 1) {

				var mod = (!test && ( (bits >> i) & 1) == 1);

				if (i < 6) {
					_modules[i][8] = mod;
				} else if (i < 8) {
					_modules[i + 1][8] = mod;
				} else {
					_modules[_moduleCount - 15 + i][8] = mod;
				}
			}

			// horizontal
			for (var i = 0; i < 15; i += 1) {

				var mod = (!test && ( (bits >> i) & 1) == 1);

				if (i < 8) {
					_modules[8][_moduleCount - i - 1] = mod;
				} else if (i < 9) {
					_modules[8][15 - i - 1 + 1] = mod;
				} else {
					_modules[8][15 - i - 1] = mod;
				}
			}

			// fixed module
			_modules[_moduleCount - 8][8] = (!test);
		};

		var mapData = function(data, maskPattern) {

			var inc = -1;
			var row = _moduleCount - 1;
			var bitIndex = 7;
			var byteIndex = 0;
			var maskFunc = QRUtil.getMaskFunction(maskPattern);

			for (var col = _moduleCount - 1; col > 0; col -= 2) {

				if (col == 6) col -= 1;

				while (true) {

					for (var c = 0; c < 2; c += 1) {

						if (_modules[row][col - c] == null) {

							var dark = false;

							if (byteIndex < data.length) {
								dark = ( ( (data[byteIndex] >>> bitIndex) & 1) == 1);
							}

							var mask = maskFunc(row, col - c);

							if (mask) {
								dark = !dark;
							}

							_modules[row][col - c] = dark;
							bitIndex -= 1;

							if (bitIndex == -1) {
								byteIndex += 1;
								bitIndex = 7;
							}
						}
					}

					row += inc;

					if (row < 0 || _moduleCount <= row) {
						row -= inc;
						inc = -inc;
						break;
					}
				}
			}
		};

		var createBytes = function(buffer, rsBlocks) {

			var offset = 0;

			var maxDcCount = 0;
			var maxEcCount = 0;

			var dcdata = new Array(rsBlocks.length);
			var ecdata = new Array(rsBlocks.length);

			for (var r = 0; r < rsBlocks.length; r += 1) {

				var dcCount = rsBlocks[r].dataCount;
				var ecCount = rsBlocks[r].totalCount - dcCount;

				maxDcCount = Math.max(maxDcCount, dcCount);
				maxEcCount = Math.max(maxEcCount, ecCount);

				dcdata[r] = new Array(dcCount);

				for (var i = 0; i < dcdata[r].length; i += 1) {
					dcdata[r][i] = 0xff & buffer.getBuffer()[i + offset];
				}
				offset += dcCount;

				var rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount);
				var rawPoly = qrPolynomial(dcdata[r], rsPoly.getLength() - 1);

				var modPoly = rawPoly.mod(rsPoly);
				ecdata[r] = new Array(rsPoly.getLength() - 1);
				for (var i = 0; i < ecdata[r].length; i += 1) {
					var modIndex = i + modPoly.getLength() - ecdata[r].length;
					ecdata[r][i] = (modIndex >= 0)? modPoly.getAt(modIndex) : 0;
				}
			}

			var totalCodeCount = 0;
			for (var i = 0; i < rsBlocks.length; i += 1) {
				totalCodeCount += rsBlocks[i].totalCount;
			}

			var data = new Array(totalCodeCount);
			var index = 0;

			for (var i = 0; i < maxDcCount; i += 1) {
				for (var r = 0; r < rsBlocks.length; r += 1) {
					if (i < dcdata[r].length) {
						data[index] = dcdata[r][i];
						index += 1;
					}
				}
			}

			for (var i = 0; i < maxEcCount; i += 1) {
				for (var r = 0; r < rsBlocks.length; r += 1) {
					if (i < ecdata[r].length) {
						data[index] = ecdata[r][i];
						index += 1;
					}
				}
			}

			return data;
		};

		var createData = function(typeNumber, errorCorrectLevel, dataList) {

			var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, errorCorrectLevel);

			var buffer = qrBitBuffer();

			for (var i = 0; i < dataList.length; i += 1) {
				var data = dataList[i];
				buffer.put(data.getMode(), 4);
				buffer.put(data.getLength(), QRUtil.getLengthInBits(data.getMode(), typeNumber) );
				data.write(buffer);
			}

			// calc num max data.
			var totalDataCount = 0;
			for (var i = 0; i < rsBlocks.length; i += 1) {
				totalDataCount += rsBlocks[i].dataCount;
			}

			if (buffer.getLengthInBits() > totalDataCount * 8) {
				throw new Error('code length overflow. ('
					+ buffer.getLengthInBits()
					+ '>'
					+ totalDataCount * 8
					+ ')');
			}

			// end code
			if (buffer.getLengthInBits() + 4 <= totalDataCount * 8) {
				buffer.put(0, 4);
			}

			// padding
			while (buffer.getLengthInBits() % 8 != 0) {
				buffer.putBit(false);
			}

			// padding
			while (true) {

				if (buffer.getLengthInBits() >= totalDataCount * 8) {
					break;
				}
				buffer.put(PAD0, 8);

				if (buffer.getLengthInBits() >= totalDataCount * 8) {
					break;
				}
				buffer.put(PAD1, 8);
			}

			return createBytes(buffer, rsBlocks);
		};

		_this.addData = function(data) {
			var newData = qr8BitByte(data);
			_dataList.push(newData);
			_dataCache = null;
		};

		_this.isDark = function(row, col) {
			if (row < 0 || _moduleCount <= row || col < 0 || _moduleCount <= col) {
				throw new Error(row + ',' + col);
			}
			return _modules[row][col];
		};

		_this.getModuleCount = function() {
			return _moduleCount;
		};

		_this.make = function() {
			makeImpl(false, getBestMaskPattern() );
		};

		_this.createTableTag = function(cellSize, margin) {

			cellSize = cellSize || 2;
			margin = (typeof margin == 'undefined')? cellSize * 4 : margin;

			var qrHtml = '';

			qrHtml += '<table style="';
			qrHtml += ' border-width: 0px; border-style: none;';
			qrHtml += ' border-collapse: collapse;';
			qrHtml += ' padding: 0px; margin: ' + margin + 'px;';
			qrHtml += '">';
			qrHtml += '<tbody>';

			for (var r = 0; r < _this.getModuleCount(); r += 1) {

				qrHtml += '<tr>';

				for (var c = 0; c < _this.getModuleCount(); c += 1) {
					qrHtml += '<td style="';
					qrHtml += ' border-width: 0px; border-style: none;';
					qrHtml += ' border-collapse: collapse;';
					qrHtml += ' padding: 0px; margin: 0px;';
					qrHtml += ' width: ' + cellSize + 'px;';
					qrHtml += ' height: ' + cellSize + 'px;';
					qrHtml += ' background-color: ';
					qrHtml += _this.isDark(r, c)? '#000000' : '#ffffff';
					qrHtml += ';';
					qrHtml += '"/>';
				}

				qrHtml += '</tr>';
			}

			qrHtml += '</tbody>';
			qrHtml += '</table>';

			return qrHtml;
		};

		_this.createImgTag = function(cellSize, margin) {

			cellSize = cellSize || 2;
			margin = (typeof margin == 'undefined')? cellSize * 4 : margin;

			var size = _this.getModuleCount() * cellSize + margin * 2;
			var min = margin;
			var max = size - margin;

			return createImgTag(size, size, function(x, y) {
				if (min <= x && x < max && min <= y && y < max) {
					var c = Math.floor( (x - min) / cellSize);
					var r = Math.floor( (y - min) / cellSize);
					return _this.isDark(r, c)? 0 : 1;
				} else {
					return 1;
				}
			} );
		};

		_this.createImgData = function(cellSize, margin) {

			cellSize = cellSize || 2;
			margin = (typeof margin == 'undefined')? cellSize * 4 : margin;

			var size = _this.getModuleCount() * cellSize + margin * 2;
			var min = margin;
			var max = size - margin;

			var base64 = createGifData(size, size, function(x, y) {
				if (min <= x && x < max && min <= y && y < max) {
					var c = Math.floor( (x - min) / cellSize);
					var r = Math.floor( (y - min) / cellSize);
					return _this.isDark(r, c)? 0 : 1;
				} else {
					return 1;
				}
			} );

                        return {
                                src: 'data:image/gif;base64,' + base64,
                                width: size,
                                height: size
                        };
		};

		return _this;
	};

	//---------------------------------------------------------------------
	// qrcode.stringToBytes
	//---------------------------------------------------------------------

	qrcode.stringToBytes = function(s) {
		var bytes = new Array();
		for (var i = 0; i < s.length; i += 1) {
			var c = s.charCodeAt(i);
			bytes.push(c & 0xff);
		}
		return bytes;
	};

	//---------------------------------------------------------------------
	// qrcode.createStringToBytes
	//---------------------------------------------------------------------

	/**
	 * @param unicodeData base64 string of byte array.
	 * [16bit Unicode],[16bit Bytes], ...
	 * @param numChars
	 */
	qrcode.createStringToBytes = function(unicodeData, numChars) {

		// create conversion map.

		var unicodeMap = function() {

			var bin = base64DecodeInputStream(unicodeData);
			var read = function() {
				var b = bin.read();
				if (b == -1) throw new Error();
				return b;
			};

			var count = 0;
			var unicodeMap = {};
			while (true) {
				var b0 = bin.read();
				if (b0 == -1) break;
				var b1 = read();
				var b2 = read();
				var b3 = read();
				var k = String.fromCharCode( (b0 << 8) | b1);
				var v = (b2 << 8) | b3;
				unicodeMap[k] = v;
				count += 1;
			}
			if (count != numChars) {
				throw new Error(count + ' != ' + numChars);
			}

			return unicodeMap;
		}();

		var unknownChar = '?'.charCodeAt(0);

		return function(s) {
			var bytes = new Array();
			for (var i = 0; i < s.length; i += 1) {
				var c = s.charCodeAt(i);
				if (c < 128) {
					bytes.push(c);
				} else {
					var b = unicodeMap[s.charAt(i)];
					if (typeof b == 'number') {
						if ( (b & 0xff) == b) {
							// 1byte
							bytes.push(b);
						} else {
							// 2bytes
							bytes.push(b >>> 8);
							bytes.push(b & 0xff);
						}
					} else {
						bytes.push(unknownChar);
					}
				}
			}
			return bytes;
		};
	};

	//---------------------------------------------------------------------
	// QRMode
	//---------------------------------------------------------------------

	var QRMode = {
		MODE_NUMBER :		1 << 0,
		MODE_ALPHA_NUM : 	1 << 1,
		MODE_8BIT_BYTE : 	1 << 2,
		MODE_KANJI :		1 << 3
	};

	//---------------------------------------------------------------------
	// QRErrorCorrectLevel
	//---------------------------------------------------------------------

	var QRErrorCorrectLevel = {
		L : 1,
		M : 0,
		Q : 3,
		H : 2
	};
        // mozilla: Add module support
        exports.QRErrorCorrectLevel = QRErrorCorrectLevel;

	//---------------------------------------------------------------------
	// QRMaskPattern
	//---------------------------------------------------------------------

	var QRMaskPattern = {
		PATTERN000 : 0,
		PATTERN001 : 1,
		PATTERN010 : 2,
		PATTERN011 : 3,
		PATTERN100 : 4,
		PATTERN101 : 5,
		PATTERN110 : 6,
		PATTERN111 : 7
	};

	//---------------------------------------------------------------------
	// QRUtil
	//---------------------------------------------------------------------

	var QRUtil = function() {

		var PATTERN_POSITION_TABLE = [
			[],
			[6, 18],
			[6, 22],
			[6, 26],
			[6, 30],
			[6, 34],
			[6, 22, 38],
			[6, 24, 42],
			[6, 26, 46],
			[6, 28, 50],
			[6, 30, 54],
			[6, 32, 58],
			[6, 34, 62],
			[6, 26, 46, 66],
			[6, 26, 48, 70],
			[6, 26, 50, 74],
			[6, 30, 54, 78],
			[6, 30, 56, 82],
			[6, 30, 58, 86],
			[6, 34, 62, 90],
			[6, 28, 50, 72, 94],
			[6, 26, 50, 74, 98],
			[6, 30, 54, 78, 102],
			[6, 28, 54, 80, 106],
			[6, 32, 58, 84, 110],
			[6, 30, 58, 86, 114],
			[6, 34, 62, 90, 118],
			[6, 26, 50, 74, 98, 122],
			[6, 30, 54, 78, 102, 126],
			[6, 26, 52, 78, 104, 130],
			[6, 30, 56, 82, 108, 134],
			[6, 34, 60, 86, 112, 138],
			[6, 30, 58, 86, 114, 142],
			[6, 34, 62, 90, 118, 146],
			[6, 30, 54, 78, 102, 126, 150],
			[6, 24, 50, 76, 102, 128, 154],
			[6, 28, 54, 80, 106, 132, 158],
			[6, 32, 58, 84, 110, 136, 162],
			[6, 26, 54, 82, 110, 138, 166],
			[6, 30, 58, 86, 114, 142, 170]
		];
		var G15 = (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0);
		var G18 = (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0);
		var G15_MASK = (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1);

		var _this = {};

		var getBCHDigit = function(data) {
			var digit = 0;
			while (data != 0) {
				digit += 1;
				data >>>= 1;
			}
			return digit;
		};

		_this.getBCHTypeInfo = function(data) {
			var d = data << 10;
			while (getBCHDigit(d) - getBCHDigit(G15) >= 0) {
				d ^= (G15 << (getBCHDigit(d) - getBCHDigit(G15) ) );
			}
			return ( (data << 10) | d) ^ G15_MASK;
		};

		_this.getBCHTypeNumber = function(data) {
			var d = data << 12;
			while (getBCHDigit(d) - getBCHDigit(G18) >= 0) {
				d ^= (G18 << (getBCHDigit(d) - getBCHDigit(G18) ) );
			}
			return (data << 12) | d;
		};

		_this.getPatternPosition = function(typeNumber) {
			return PATTERN_POSITION_TABLE[typeNumber - 1];
		};

		_this.getMaskFunction = function(maskPattern) {

			switch (maskPattern) {

			case QRMaskPattern.PATTERN000 :
				return function(i, j) { return (i + j) % 2 == 0; };
			case QRMaskPattern.PATTERN001 :
				return function(i, j) { return i % 2 == 0; };
			case QRMaskPattern.PATTERN010 :
				return function(i, j) { return j % 3 == 0; };
			case QRMaskPattern.PATTERN011 :
				return function(i, j) { return (i + j) % 3 == 0; };
			case QRMaskPattern.PATTERN100 :
				return function(i, j) { return (Math.floor(i / 2) + Math.floor(j / 3) ) % 2 == 0; };
			case QRMaskPattern.PATTERN101 :
				return function(i, j) { return (i * j) % 2 + (i * j) % 3 == 0; };
			case QRMaskPattern.PATTERN110 :
				return function(i, j) { return ( (i * j) % 2 + (i * j) % 3) % 2 == 0; };
			case QRMaskPattern.PATTERN111 :
				return function(i, j) { return ( (i * j) % 3 + (i + j) % 2) % 2 == 0; };

			default :
				throw new Error('bad maskPattern:' + maskPattern);
			}
		};

		_this.getErrorCorrectPolynomial = function(errorCorrectLength) {
			var a = qrPolynomial([1], 0);
			for (var i = 0; i < errorCorrectLength; i += 1) {
				a = a.multiply(qrPolynomial([1, QRMath.gexp(i)], 0) );
			}
			return a;
		};

		_this.getLengthInBits = function(mode, type) {

			if (1 <= type && type < 10) {

				// 1 - 9

				switch(mode) {
				case QRMode.MODE_NUMBER 	: return 10;
				case QRMode.MODE_ALPHA_NUM 	: return 9;
				case QRMode.MODE_8BIT_BYTE	: return 8;
				case QRMode.MODE_KANJI		: return 8;
				default :
					throw new Error('mode:' + mode);
				}

			} else if (type < 27) {

				// 10 - 26

				switch(mode) {
				case QRMode.MODE_NUMBER 	: return 12;
				case QRMode.MODE_ALPHA_NUM 	: return 11;
				case QRMode.MODE_8BIT_BYTE	: return 16;
				case QRMode.MODE_KANJI		: return 10;
				default :
					throw new Error('mode:' + mode);
				}

			} else if (type < 41) {

				// 27 - 40

				switch(mode) {
				case QRMode.MODE_NUMBER 	: return 14;
				case QRMode.MODE_ALPHA_NUM	: return 13;
				case QRMode.MODE_8BIT_BYTE	: return 16;
				case QRMode.MODE_KANJI		: return 12;
				default :
					throw new Error('mode:' + mode);
				}

			} else {
				throw new Error('type:' + type);
			}
		};

		_this.getLostPoint = function(qrcode) {

			var moduleCount = qrcode.getModuleCount();

			var lostPoint = 0;

			// LEVEL1

			for (var row = 0; row < moduleCount; row += 1) {
				for (var col = 0; col < moduleCount; col += 1) {

					var sameCount = 0;
					var dark = qrcode.isDark(row, col);

					for (var r = -1; r <= 1; r += 1) {

						if (row + r < 0 || moduleCount <= row + r) {
							continue;
						}

						for (var c = -1; c <= 1; c += 1) {

							if (col + c < 0 || moduleCount <= col + c) {
								continue;
							}

							if (r == 0 && c == 0) {
								continue;
							}

							if (dark == qrcode.isDark(row + r, col + c) ) {
								sameCount += 1;
							}
						}
					}

					if (sameCount > 5) {
						lostPoint += (3 + sameCount - 5);
					}
				}
			};

			// LEVEL2

			for (var row = 0; row < moduleCount - 1; row += 1) {
				for (var col = 0; col < moduleCount - 1; col += 1) {
					var count = 0;
					if (qrcode.isDark(row, col) ) count += 1;
					if (qrcode.isDark(row + 1, col) ) count += 1;
					if (qrcode.isDark(row, col + 1) ) count += 1;
					if (qrcode.isDark(row + 1, col + 1) ) count += 1;
					if (count == 0 || count == 4) {
						lostPoint += 3;
					}
				}
			}

			// LEVEL3

			for (var row = 0; row < moduleCount; row += 1) {
				for (var col = 0; col < moduleCount - 6; col += 1) {
					if (qrcode.isDark(row, col)
							&& !qrcode.isDark(row, col + 1)
							&&  qrcode.isDark(row, col + 2)
							&&  qrcode.isDark(row, col + 3)
							&&  qrcode.isDark(row, col + 4)
							&& !qrcode.isDark(row, col + 5)
							&&  qrcode.isDark(row, col + 6) ) {
						lostPoint += 40;
					}
				}
			}

			for (var col = 0; col < moduleCount; col += 1) {
				for (var row = 0; row < moduleCount - 6; row += 1) {
					if (qrcode.isDark(row, col)
							&& !qrcode.isDark(row + 1, col)
							&&  qrcode.isDark(row + 2, col)
							&&  qrcode.isDark(row + 3, col)
							&&  qrcode.isDark(row + 4, col)
							&& !qrcode.isDark(row + 5, col)
							&&  qrcode.isDark(row + 6, col) ) {
						lostPoint += 40;
					}
				}
			}

			// LEVEL4

			var darkCount = 0;

			for (var col = 0; col < moduleCount; col += 1) {
				for (var row = 0; row < moduleCount; row += 1) {
					if (qrcode.isDark(row, col) ) {
						darkCount += 1;
					}
				}
			}

			var ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5;
			lostPoint += ratio * 10;

			return lostPoint;
		};

		return _this;
	}();

	//---------------------------------------------------------------------
	// QRMath
	//---------------------------------------------------------------------

	var QRMath = function() {

		var EXP_TABLE = new Array(256);
		var LOG_TABLE = new Array(256);

		// initialize tables
		for (var i = 0; i < 8; i += 1) {
			EXP_TABLE[i] = 1 << i;
		}
		for (var i = 8; i < 256; i += 1) {
			EXP_TABLE[i] = EXP_TABLE[i - 4]
				^ EXP_TABLE[i - 5]
				^ EXP_TABLE[i - 6]
				^ EXP_TABLE[i - 8];
		}
		for (var i = 0; i < 255; i += 1) {
			LOG_TABLE[EXP_TABLE[i] ] = i;
		}

		var _this = {};

		_this.glog = function(n) {

			if (n < 1) {
				throw new Error('glog(' + n + ')');
			}

			return LOG_TABLE[n];
		};

		_this.gexp = function(n) {

			while (n < 0) {
				n += 255;
			}

			while (n >= 256) {
				n -= 255;
			}

			return EXP_TABLE[n];
		};

		return _this;
	}();

	//---------------------------------------------------------------------
	// qrPolynomial
	//---------------------------------------------------------------------

	function qrPolynomial(num, shift) {

		if (typeof num.length == 'undefined') {
			throw new Error(num.length + '/' + shift);
		}

		var _num = function() {
			var offset = 0;
			while (offset < num.length && num[offset] == 0) {
				offset += 1;
			}
			var _num = new Array(num.length - offset + shift);
			for (var i = 0; i < num.length - offset; i += 1) {
				_num[i] = num[i + offset];
			}
			return _num;
		}();

		var _this = {};

		_this.getAt = function(index) {
			return _num[index];
		};

		_this.getLength = function() {
			return _num.length;
		};

		_this.multiply = function(e) {

			var num = new Array(_this.getLength() + e.getLength() - 1);

			for (var i = 0; i < _this.getLength(); i += 1) {
				for (var j = 0; j < e.getLength(); j += 1) {
					num[i + j] ^= QRMath.gexp(QRMath.glog(_this.getAt(i) ) + QRMath.glog(e.getAt(j) ) );
				}
			}

			return qrPolynomial(num, 0);
		};

		_this.mod = function(e) {

			if (_this.getLength() - e.getLength() < 0) {
				return _this;
			}

			var ratio = QRMath.glog(_this.getAt(0) ) - QRMath.glog(e.getAt(0) );

			var num = new Array(_this.getLength() );
			for (var i = 0; i < _this.getLength(); i += 1) {
				num[i] = _this.getAt(i);
			}

			for (var i = 0; i < e.getLength(); i += 1) {
				num[i] ^= QRMath.gexp(QRMath.glog(e.getAt(i) ) + ratio);
			}

			// recursive call
			return qrPolynomial(num, 0).mod(e);
		};

		return _this;
	};

	//---------------------------------------------------------------------
	// QRRSBlock
	//---------------------------------------------------------------------

	var QRRSBlock = function() {

		var RS_BLOCK_TABLE = [

			// L
			// M
			// Q
			// H

			// 1
			[1, 26, 19],
			[1, 26, 16],
			[1, 26, 13],
			[1, 26, 9],

			// 2
			[1, 44, 34],
			[1, 44, 28],
			[1, 44, 22],
			[1, 44, 16],

			// 3
			[1, 70, 55],
			[1, 70, 44],
			[2, 35, 17],
			[2, 35, 13],

			// 4
			[1, 100, 80],
			[2, 50, 32],
			[2, 50, 24],
			[4, 25, 9],

			// 5
			[1, 134, 108],
			[2, 67, 43],
			[2, 33, 15, 2, 34, 16],
			[2, 33, 11, 2, 34, 12],

			// 6
			[2, 86, 68],
			[4, 43, 27],
			[4, 43, 19],
			[4, 43, 15],

			// 7
			[2, 98, 78],
			[4, 49, 31],
			[2, 32, 14, 4, 33, 15],
			[4, 39, 13, 1, 40, 14],

			// 8
			[2, 121, 97],
			[2, 60, 38, 2, 61, 39],
			[4, 40, 18, 2, 41, 19],
			[4, 40, 14, 2, 41, 15],

			// 9
			[2, 146, 116],
			[3, 58, 36, 2, 59, 37],
			[4, 36, 16, 4, 37, 17],
			[4, 36, 12, 4, 37, 13],

			// 10
			[2, 86, 68, 2, 87, 69],
			[4, 69, 43, 1, 70, 44],
			[6, 43, 19, 2, 44, 20],
			[6, 43, 15, 2, 44, 16]
		];

		var qrRSBlock = function(totalCount, dataCount) {
			var _this = {};
			_this.totalCount = totalCount;
			_this.dataCount = dataCount;
			return _this;
		};

		var _this = {};

		var getRsBlockTable = function(typeNumber, errorCorrectLevel) {

			switch(errorCorrectLevel) {
			case QRErrorCorrectLevel.L :
				return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 0];
			case QRErrorCorrectLevel.M :
				return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 1];
			case QRErrorCorrectLevel.Q :
				return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 2];
			case QRErrorCorrectLevel.H :
				return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 3];
			default :
				return undefined;
			}
		};

		_this.getRSBlocks = function(typeNumber, errorCorrectLevel) {

			var rsBlock = getRsBlockTable(typeNumber, errorCorrectLevel);

			if (typeof rsBlock == 'undefined') {
				throw new Error('bad rs block @ typeNumber:' + typeNumber +
						'/errorCorrectLevel:' + errorCorrectLevel);
			}

			var length = rsBlock.length / 3;

			var list = new Array();

			for (var i = 0; i < length; i += 1) {

				var count = rsBlock[i * 3 + 0];
				var totalCount = rsBlock[i * 3 + 1];
				var dataCount = rsBlock[i * 3 + 2];

				for (var j = 0; j < count; j += 1) {
					list.push(qrRSBlock(totalCount, dataCount) );
				}
			}

			return list;
		};

		return _this;
	}();

        // mozilla: Add module support
        exports.QRRSBlock = QRRSBlock;

	//---------------------------------------------------------------------
	// qrBitBuffer
	//---------------------------------------------------------------------

	var qrBitBuffer = function() {

		var _buffer = new Array();
		var _length = 0;

		var _this = {};

		_this.getBuffer = function() {
			return _buffer;
		};

		_this.getAt = function(index) {
			var bufIndex = Math.floor(index / 8);
			return ( (_buffer[bufIndex] >>> (7 - index % 8) ) & 1) == 1;
		};

		_this.put = function(num, length) {
			for (var i = 0; i < length; i += 1) {
				_this.putBit( ( (num >>> (length - i - 1) ) & 1) == 1);
			}
		};

		_this.getLengthInBits = function() {
			return _length;
		};

		_this.putBit = function(bit) {

			var bufIndex = Math.floor(_length / 8);
			if (_buffer.length <= bufIndex) {
				_buffer.push(0);
			}

			if (bit) {
				_buffer[bufIndex] |= (0x80 >>> (_length % 8) );
			}

			_length += 1;
		};

		return _this;
	};

	//---------------------------------------------------------------------
	// qr8BitByte
	//---------------------------------------------------------------------

	var qr8BitByte = function(data) {

		var _mode = QRMode.MODE_8BIT_BYTE;
		var _data = data;
		var _bytes = qrcode.stringToBytes(data);

		var _this = {};

		_this.getMode = function() {
			return _mode;
		};

		_this.getLength = function(buffer) {
			return _bytes.length;
		};

		_this.write = function(buffer) {
			for (var i = 0; i < _bytes.length; i += 1) {
				buffer.put(_bytes[i], 8);
			}
		};

		return _this;
	};

	//=====================================================================
	// GIF Support etc.
	//

	//---------------------------------------------------------------------
	// byteArrayOutputStream
	//---------------------------------------------------------------------

	var byteArrayOutputStream = function() {

		var _bytes = new Array();

		var _this = {};

		_this.writeByte = function(b) {
			_bytes.push(b & 0xff);
		};

		_this.writeShort = function(i) {
			_this.writeByte(i);
			_this.writeByte(i >>> 8);
		};

		_this.writeBytes = function(b, off, len) {
			off = off || 0;
			len = len || b.length;
			for (var i = 0; i < len; i += 1) {
				_this.writeByte(b[i + off]);
			}
		};

		_this.writeString = function(s) {
			for (var i = 0; i < s.length; i += 1) {
				_this.writeByte(s.charCodeAt(i) );
			}
		};

		_this.toByteArray = function() {
			return _bytes;
		};

		_this.toString = function() {
			var s = '';
			s += '[';
			for (var i = 0; i < _bytes.length; i += 1) {
				if (i > 0) {
					s += ',';
				}
				s += _bytes[i];
			}
			s += ']';
			return s;
		};

		return _this;
	};

	//---------------------------------------------------------------------
	// base64EncodeOutputStream
	//---------------------------------------------------------------------

	var base64EncodeOutputStream = function() {

		var _buffer = 0;
		var _buflen = 0;
		var _length = 0;
		var _base64 = '';

		var _this = {};

		var writeEncoded = function(b) {
			_base64 += String.fromCharCode(encode(b & 0x3f) );
		};

		var encode = function(n) {
			if (n < 0) {
				// error.
			} else if (n < 26) {
				return 0x41 + n;
			} else if (n < 52) {
				return 0x61 + (n - 26);
			} else if (n < 62) {
				return 0x30 + (n - 52);
			} else if (n == 62) {
				return 0x2b;
			} else if (n == 63) {
				return 0x2f;
			}
			throw new Error('n:' + n);
		};

		_this.writeByte = function(n) {

			_buffer = (_buffer << 8) | (n & 0xff);
			_buflen += 8;
			_length += 1;

			while (_buflen >= 6) {
				writeEncoded(_buffer >>> (_buflen - 6) );
				_buflen -= 6;
			}
		};

		_this.flush = function() {

			if (_buflen > 0) {
				writeEncoded(_buffer << (6 - _buflen) );
				_buffer = 0;
				_buflen = 0;
			}

			if (_length % 3 != 0) {
				// padding
				var padlen = 3 - _length % 3;
				for (var i = 0; i < padlen; i += 1) {
					_base64 += '=';
				}
			}
		};

		_this.toString = function() {
			return _base64;
		};

		return _this;
	};

	//---------------------------------------------------------------------
	// base64DecodeInputStream
	//---------------------------------------------------------------------

	var base64DecodeInputStream = function(str) {

		var _str = str;
		var _pos = 0;
		var _buffer = 0;
		var _buflen = 0;

		var _this = {};

		_this.read = function() {

			while (_buflen < 8) {

				if (_pos >= _str.length) {
					if (_buflen == 0) {
						return -1;
					}
					throw new Error('unexpected end of file./' + _buflen);
				}

				var c = _str.charAt(_pos);
				_pos += 1;

				if (c == '=') {
					_buflen = 0;
					return -1;
				} else if (c.match(/^\s$/) ) {
					// ignore if whitespace.
					continue;
				}

				_buffer = (_buffer << 6) | decode(c.charCodeAt(0) );
				_buflen += 6;
			}

			var n = (_buffer >>> (_buflen - 8) ) & 0xff;
			_buflen -= 8;
			return n;
		};

		var decode = function(c) {
			if (0x41 <= c && c <= 0x5a) {
				return c - 0x41;
			} else if (0x61 <= c && c <= 0x7a) {
				return c - 0x61 + 26;
			} else if (0x30 <= c && c <= 0x39) {
				return c - 0x30 + 52;
			} else if (c == 0x2b) {
				return 62;
			} else if (c == 0x2f) {
				return 63;
			} else {
				throw new Error('c:' + c);
			}
		};

		return _this;
	};

	//---------------------------------------------------------------------
	// gifImage (B/W)
	//---------------------------------------------------------------------

	var gifImage = function(width, height) {

		var _width = width;
		var _height = height;
		var _data = new Array(width * height);

		var _this = {};

		_this.setPixel = function(x, y, pixel) {
			_data[y * _width + x] = pixel;
		};

		_this.write = function(out) {

			//---------------------------------
			// GIF Signature

			out.writeString('GIF87a');

			//---------------------------------
			// Screen Descriptor

			out.writeShort(_width);
			out.writeShort(_height);

			out.writeByte(0x80); // 2bit
			out.writeByte(0);
			out.writeByte(0);

			//---------------------------------
			// Global Color Map

			// black
			out.writeByte(0x00);
			out.writeByte(0x00);
			out.writeByte(0x00);

			// white
			out.writeByte(0xff);
			out.writeByte(0xff);
			out.writeByte(0xff);

			//---------------------------------
			// Image Descriptor

			out.writeString(',');
			out.writeShort(0);
			out.writeShort(0);
			out.writeShort(_width);
			out.writeShort(_height);
			out.writeByte(0);

			//---------------------------------
			// Local Color Map

			//---------------------------------
			// Raster Data

			var lzwMinCodeSize = 2;
			var raster = getLZWRaster(lzwMinCodeSize);

			out.writeByte(lzwMinCodeSize);

			var offset = 0;

			while (raster.length - offset > 255) {
				out.writeByte(255);
				out.writeBytes(raster, offset, 255);
				offset += 255;
			}

			out.writeByte(raster.length - offset);
			out.writeBytes(raster, offset, raster.length - offset);
			out.writeByte(0x00);

			//---------------------------------
			// GIF Terminator
			out.writeString(';');
		};

		var bitOutputStream = function(out) {

			var _out = out;
			var _bitLength = 0;
			var _bitBuffer = 0;

			var _this = {};

			_this.write = function(data, length) {

				if ( (data >>> length) != 0) {
					throw new Error('length over');
				}

				while (_bitLength + length >= 8) {
					_out.writeByte(0xff & ( (data << _bitLength) | _bitBuffer) );
					length -= (8 - _bitLength);
					data >>>= (8 - _bitLength);
					_bitBuffer = 0;
					_bitLength = 0;
				}

				_bitBuffer = (data << _bitLength) | _bitBuffer;
				_bitLength = _bitLength + length;
			};

			_this.flush = function() {
				if (_bitLength > 0) {
					_out.writeByte(_bitBuffer);
				}
			};

			return _this;
		};

		var getLZWRaster = function(lzwMinCodeSize) {

			var clearCode = 1 << lzwMinCodeSize;
			var endCode = (1 << lzwMinCodeSize) + 1;
			var bitLength = lzwMinCodeSize + 1;

			// Setup LZWTable
			var table = lzwTable();

			for (var i = 0; i < clearCode; i += 1) {
				table.add(String.fromCharCode(i) );
			}
			table.add(String.fromCharCode(clearCode) );
			table.add(String.fromCharCode(endCode) );

			var byteOut = byteArrayOutputStream();
			var bitOut = bitOutputStream(byteOut);

			// clear code
			bitOut.write(clearCode, bitLength);

			var dataIndex = 0;

			var s = String.fromCharCode(_data[dataIndex]);
			dataIndex += 1;

			while (dataIndex < _data.length) {

				var c = String.fromCharCode(_data[dataIndex]);
				dataIndex += 1;

				if (table.contains(s + c) ) {

					s = s + c;

				} else {

					bitOut.write(table.indexOf(s), bitLength);

					if (table.size() < 0xfff) {

						if (table.size() == (1 << bitLength) ) {
							bitLength += 1;
						}

						table.add(s + c);
					}

					s = c;
				}
			}

			bitOut.write(table.indexOf(s), bitLength);

			// end code
			bitOut.write(endCode, bitLength);

			bitOut.flush();

			return byteOut.toByteArray();
		};

		var lzwTable = function() {

			var _map = {};
			var _size = 0;

			var _this = {};

			_this.add = function(key) {
				if (_this.contains(key) ) {
					throw new Error('dup key:' + key);
				}
				_map[key] = _size;
				_size += 1;
			};

			_this.size = function() {
				return _size;
			};

			_this.indexOf = function(key) {
				return _map[key];
			};

			_this.contains = function(key) {
				return typeof _map[key] != 'undefined';
			};

			return _this;
		};

		return _this;
	};

        var createGifData = function(width, height, getPixel) {

        	var gif = gifImage(width, height);
		for (var y = 0; y < height; y += 1) {
			for (var x = 0; x < width; x += 1) {
				gif.setPixel(x, y, getPixel(x, y) );
			}
		}

		var b = byteArrayOutputStream();
		gif.write(b);

		var base64 = base64EncodeOutputStream();
		var bytes = b.toByteArray();
		for (var i = 0; i < bytes.length; i += 1) {
			base64.writeByte(bytes[i]);
		}
		base64.flush();
                return base64;

        };

	var createImgTag = function(width, height, getPixel, alt) {

                var base64 = createGifData(width, height, getPixel);
		var img = '';
		img += '<img';
		img += '\u0020src="';
		img += 'data:image/gif;base64,';
		img += base64;
		img += '"';
		img += '\u0020width="';
		img += width;
		img += '"';
		img += '\u0020height="';
		img += height;
		img += '"';
		if (alt) {
			img += '\u0020alt="';
			img += alt;
			img += '"';
		}
		img += '/>';

		return img;
	};

	//---------------------------------------------------------------------
	// returns qrcode function.

	return qrcode;
}();

// mozilla: Add module support
exports.Encoder = qrcode;
PK
!<��k�
�
7chrome/devtools/modules/devtools/shared/qrcode/index.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 promise = require("promise");
const defer = require("devtools/shared/defer");

// Lazily require encoder and decoder in case only one is needed
Object.defineProperty(this, "Encoder", {
  get: () => require("./encoder/index").Encoder
});
Object.defineProperty(this, "QRRSBlock", {
  get: () => require("./encoder/index").QRRSBlock
});
Object.defineProperty(this, "QRErrorCorrectLevel", {
  get: () => require("./encoder/index").QRErrorCorrectLevel
});
Object.defineProperty(this, "decoder", {
  get: () => {
    // Some applications don't ship the decoder, see moz.build
    try {
      return require("./decoder/index");
    } catch (e) {
      return null;
    }
  }
});

/**
 * There are many "versions" of QR codes, which describes how many dots appear
 * in the resulting image, thus limiting the amount of data that can be
 * represented.
 *
 * The encoder used here allows for versions 1 - 10 (more dots for larger
 * versions).
 *
 * It expects you to pick a version large enough to contain your message.  Here
 * we search for the mimimum version based on the message length.
 * @param string message
 *        Text to encode
 * @param string quality
 *        Quality level: L, M, Q, H
 * @return integer
 */
exports.findMinimumVersion = function (message, quality) {
  let msgLength = message.length;
  let qualityLevel = QRErrorCorrectLevel[quality];
  for (let version = 1; version <= 10; version++) {
    let rsBlocks = QRRSBlock.getRSBlocks(version, qualityLevel);
    let maxLength = rsBlocks.reduce((prev, block) => {
      return prev + block.dataCount;
    }, 0);
    // Remove two bytes to fit header info
    maxLength -= 2;
    if (msgLength <= maxLength) {
      return version;
    }
  }
  throw new Error("Message too large");
};

/**
 * Simple wrapper around the underlying encoder's API.
 * @param string  message
 *        Text to encode
 * @param string  quality (optional)
          Quality level: L, M, Q, H
 * @param integer version (optional)
 *        QR code "version" large enough to contain the message
 * @return object with the following fields:
 *   * src:    an image encoded a data URI
 *   * height: image height
 *   * width:  image width
 */
exports.encodeToDataURI = function (message, quality, version) {
  quality = quality || "H";
  version = version || exports.findMinimumVersion(message, quality);
  let encoder = new Encoder(version, quality);
  encoder.addData(message);
  encoder.make();
  return encoder.createImgData();
};

/**
 * Simple wrapper around the underlying decoder's API.
 * @param string URI
 *        URI of an image of a QR code
 * @return Promise
 *         The promise will be resolved with a string, which is the data inside
 *         the QR code.
 */
exports.decodeFromURI = function (URI) {
  if (!decoder) {
    return promise.reject();
  }
  let deferred = defer();
  decoder.decodeFromURI(URI, deferred.resolve, deferred.reject);
  return deferred.promise;
};

/**
 * Decode a QR code that has been drawn to a canvas element.
 * @param Canvas canvas
 *        <canvas> element to read from
 * @return string
 *         The data inside the QR code
 */
exports.decodeFromCanvas = function (canvas) {
  if (!decoder) {
    throw new Error("Decoder not available");
  }
  return decoder.decodeFromCanvas(canvas);
};
PK
!<���~O~O8chrome/devtools/modules/devtools/shared/security/auth.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

var { Ci, Cc } = require("chrome");
var Services = require("Services");
var defer = require("devtools/shared/defer");
var DevToolsUtils = require("devtools/shared/DevToolsUtils");
var { dumpn, dumpv } = DevToolsUtils;
loader.lazyRequireGetter(this, "prompt",
  "devtools/shared/security/prompt");
loader.lazyRequireGetter(this, "cert",
  "devtools/shared/security/cert");
loader.lazyRequireGetter(this, "asyncStorage",
  "devtools/shared/async-storage");

/**
 * A simple enum-like object with keys mirrored to values.
 * This makes comparison to a specfic value simpler without having to repeat and
 * mis-type the value.
 */
function createEnum(obj) {
  for (let key in obj) {
    obj[key] = key;
  }
  return obj;
}

/**
 * |allowConnection| implementations can return various values as their |result|
 * field to indicate what action to take.  By specifying these, we can
 * centralize the common actions available, while still allowing embedders to
 * present their UI in whatever way they choose.
 */
var AuthenticationResult = exports.AuthenticationResult = createEnum({

  /**
   * Close all listening sockets, and disable them from opening again.
   */
  DISABLE_ALL: null,

  /**
   * Deny the current connection.
   */
  DENY: null,

  /**
   * Additional data needs to be exchanged before a result can be determined.
   */
  PENDING: null,

  /**
   * Allow the current connection.
   */
  ALLOW: null,

  /**
   * Allow the current connection, and persist this choice for future
   * connections from the same client.  This requires a trustable mechanism to
   * identify the client in the future, such as the cert used during OOB_CERT.
   */
  ALLOW_PERSIST: null

});

/**
 * An |Authenticator| implements an authentication mechanism via various hooks
 * in the client and server debugger socket connection path (see socket.js).
 *
 * |Authenticator|s are stateless objects.  Each hook method is passed the state
 * it needs by the client / server code in socket.js.
 *
 * Separate instances of the |Authenticator| are created for each use (client
 * connection, server listener) in case some methods are customized by the
 * embedder for a given use case.
 */
var Authenticators = {};

/**
 * The Prompt authenticator displays a server-side user prompt that includes
 * connection details, and asks the user to verify the connection.  There are
 * no cryptographic properties at work here, so it is up to the user to be sure
 * that the client can be trusted.
 */
var Prompt = Authenticators.Prompt = {};

Prompt.mode = "PROMPT";

Prompt.Client = function () {};
Prompt.Client.prototype = {

  mode: Prompt.mode,

  /**
   * When client is about to make a new connection, verify that the connection settings
   * are compatible with this authenticator.
   * @throws if validation requirements are not met
   */
  validateSettings() {},

  /**
   * When client has just made a new socket connection, validate the connection
   * to ensure it meets the authenticator's policies.
   *
   * @param host string
   *        The host name or IP address of the debugger server.
   * @param port number
   *        The port number of the debugger server.
   * @param encryption boolean (optional)
   *        Whether the server requires encryption.  Defaults to false.
   * @param cert object (optional)
   *        The server's cert details.
   * @param s nsISocketTransport
   *        Underlying socket transport, in case more details are needed.
   * @return boolean
   *         Whether the connection is valid.
   */
  validateConnection() {
    return true;
  },

  /**
   * Work with the server to complete any additional steps required by this
   * authenticator's policies.
   *
   * Debugging commences after this hook completes successfully.
   *
   * @param host string
   *        The host name or IP address of the debugger server.
   * @param port number
   *        The port number of the debugger server.
   * @param encryption boolean (optional)
   *        Whether the server requires encryption.  Defaults to false.
   * @param transport DebuggerTransport
   *        A transport that can be used to communicate with the server.
   * @return A promise can be used if there is async behavior.
   */
  authenticate() {},

};

Prompt.Server = function () {};
Prompt.Server.prototype = {

  mode: Prompt.mode,

  /**
   * Verify that listener settings are appropriate for this authentication mode.
   *
   * @param listener SocketListener
   *        The socket listener about to be opened.
   * @throws if validation requirements are not met
   */
  validateOptions() {},

  /**
   * Augment options on the listening socket about to be opened.
   *
   * @param listener SocketListener
   *        The socket listener about to be opened.
   * @param socket nsIServerSocket
   *        The socket that is about to start listening.
   */
  augmentSocketOptions() {},

  /**
   * Augment the service discovery advertisement with any additional data needed
   * to support this authentication mode.
   *
   * @param listener SocketListener
   *        The socket listener that was just opened.
   * @param advertisement object
   *        The advertisement being built.
   */
  augmentAdvertisement(listener, advertisement) {
    advertisement.authentication = Prompt.mode;
  },

  /**
   * Determine whether a connection the server should be allowed or not based on
   * this authenticator's policies.
   *
   * @param session object
   *        In PROMPT mode, the |session| includes:
   *        {
   *          client: {
   *            host,
   *            port
   *          },
   *          server: {
   *            host,
   *            port
   *          },
   *          transport
   *        }
   * @return An AuthenticationResult value.
   *         A promise that will be resolved to the above is also allowed.
   */
  authenticate({ client, server }) {
    if (!Services.prefs.getBoolPref("devtools.debugger.prompt-connection")) {
      return AuthenticationResult.ALLOW;
    }
    return this.allowConnection({
      authentication: this.mode,
      client,
      server
    });
  },

  /**
   * Prompt the user to accept or decline the incoming connection.  The default
   * implementation is used unless this is overridden on a particular
   * authenticator instance.
   *
   * It is expected that the implementation of |allowConnection| will show a
   * prompt to the user so that they can allow or deny the connection.
   *
   * @param session object
   *        In PROMPT mode, the |session| includes:
   *        {
   *          authentication: "PROMPT",
   *          client: {
   *            host,
   *            port
   *          },
   *          server: {
   *            host,
   *            port
   *          }
   *        }
   * @return An AuthenticationResult value.
   *         A promise that will be resolved to the above is also allowed.
   */
  allowConnection: prompt.Server.defaultAllowConnection,

};

/**
 * The out-of-band (OOB) cert authenticator is based on self-signed X.509 certs
 * at both the client and server end.
 *
 * The user is first prompted to verify the connection, similar to the prompt
 * method above.  This prompt may display cert fingerprints if desired.
 *
 * Assuming the user approves the connection, further UI is used to assist the
 * user in tranferring out-of-band (OOB) verification of the client's
 * certificate.  For example, this could take the form of a QR code that the
 * client displays which is then scanned by a camera on the server.
 *
 * Since it is assumed that an attacker can't forge the client's X.509 cert, the
 * user may also choose to always allow a client, which would permit immediate
 * connections in the future with no user interaction needed.
 *
 * See docs/wifi.md for details of the authentication design.
 */
var OOBCert = Authenticators.OOBCert = {};

OOBCert.mode = "OOB_CERT";

OOBCert.Client = function () {};
OOBCert.Client.prototype = {

  mode: OOBCert.mode,

  /**
   * When client is about to make a new connection, verify that the connection settings
   * are compatible with this authenticator.
   * @throws if validation requirements are not met
   */
  validateSettings({ encryption }) {
    if (!encryption) {
      throw new Error(`${OOBCert.mode} authentication requires encryption.`);
    }
  },

  /**
   * When client has just made a new socket connection, validate the connection
   * to ensure it meets the authenticator's policies.
   *
   * @param host string
   *        The host name or IP address of the debugger server.
   * @param port number
   *        The port number of the debugger server.
   * @param encryption boolean (optional)
   *        Whether the server requires encryption.  Defaults to false.
   * @param cert object (optional)
   *        The server's cert details.
   * @param socket nsISocketTransport
   *        Underlying socket transport, in case more details are needed.
   * @return boolean
   *         Whether the connection is valid.
   */
  validateConnection({ cert, socket }) {
    // Step B.7
    // Client verifies that Server's cert matches hash(ServerCert) from the
    // advertisement
    dumpv("Validate server cert hash");
    let serverCert = socket.securityInfo.QueryInterface(Ci.nsISSLStatusProvider)
                           .SSLStatus.serverCert;
    let advertisedCert = cert;
    if (serverCert.sha256Fingerprint != advertisedCert.sha256) {
      dumpn("Server cert hash doesn't match advertisement");
      return false;
    }
    return true;
  },

  /**
   * Work with the server to complete any additional steps required by this
   * authenticator's policies.
   *
   * Debugging commences after this hook completes successfully.
   *
   * @param host string
   *        The host name or IP address of the debugger server.
   * @param port number
   *        The port number of the debugger server.
   * @param encryption boolean (optional)
   *        Whether the server requires encryption.  Defaults to false.
   * @param cert object (optional)
   *        The server's cert details.
   * @param transport DebuggerTransport
   *        A transport that can be used to communicate with the server.
   * @return A promise can be used if there is async behavior.
   */
  authenticate({ host, port, cert, transport }) {
    let deferred = defer();
    let oobData;

    let activeSendDialog;
    let closeDialog = () => {
      // Close any prompts the client may have been showing from previous
      // authentication steps
      if (activeSendDialog && activeSendDialog.close) {
        activeSendDialog.close();
        activeSendDialog = null;
      }
    };

    transport.hooks = {
      onPacket: async (packet) => {
        closeDialog();
        let { authResult } = packet;
        switch (authResult) {
          case AuthenticationResult.PENDING:
            // Step B.8
            // Client creates hash(ClientCert) + K(random 128-bit number)
            oobData = await this._createOOB();
            activeSendDialog = this.sendOOB({
              host,
              port,
              cert,
              authResult,
              oob: oobData
            });
            break;
          case AuthenticationResult.ALLOW:
            // Step B.12
            // Client verifies received value matches K
            if (packet.k != oobData.k) {
              transport.close(new Error("Auth secret mismatch"));
              return;
            }
            // Step B.13
            // Debugging begins
            transport.hooks = null;
            deferred.resolve(transport);
            break;
          case AuthenticationResult.ALLOW_PERSIST:
            // Server previously persisted Client as allowed
            // Step C.5
            // Debugging begins
            transport.hooks = null;
            deferred.resolve(transport);
            break;
          default:
            transport.close(new Error("Invalid auth result: " + authResult));
            break;
        }
      },
      onClosed(reason) {
        closeDialog();
        // Transport died before auth completed
        transport.hooks = null;
        deferred.reject(reason);
      }
    };
    transport.ready();
    return deferred.promise;
  },

  /**
   * Create the package of data that needs to be transferred across the OOB
   * channel.
   */
  async _createOOB() {
    let clientCert = await cert.local.getOrCreate();
    return {
      sha256: clientCert.sha256Fingerprint,
      k: this._createRandom()
    };
  },

  _createRandom() {
    // 16 bytes / 128 bits
    const length = 16;
    let rng = Cc["@mozilla.org/security/random-generator;1"]
              .createInstance(Ci.nsIRandomGenerator);
    let bytes = rng.generateRandomBytes(length);
    return bytes.map(byte => byte.toString(16)).join("");
  },

  /**
   * Send data across the OOB channel to the server to authenticate the devices.
   *
   * @param host string
   *        The host name or IP address of the debugger server.
   * @param port number
   *        The port number of the debugger server.
   * @param cert object (optional)
   *        The server's cert details.
   * @param authResult AuthenticationResult
   *        Authentication result sent from the server.
   * @param oob object (optional)
   *        The token data to be transferred during OOB_CERT step 8:
   *        * sha256: hash(ClientCert)
   *        * k     : K(random 128-bit number)
   * @return object containing:
   *         * close: Function to hide the notification
   */
  sendOOB: prompt.Client.defaultSendOOB,

};

OOBCert.Server = function () {};
OOBCert.Server.prototype = {

  mode: OOBCert.mode,

  /**
   * Verify that listener settings are appropriate for this authentication mode.
   *
   * @param listener SocketListener
   *        The socket listener about to be opened.
   * @throws if validation requirements are not met
   */
  validateOptions(listener) {
    if (!listener.encryption) {
      throw new Error(OOBCert.mode + " authentication requires encryption.");
    }
  },

  /**
   * Augment options on the listening socket about to be opened.
   *
   * @param listener SocketListener
   *        The socket listener about to be opened.
   * @param socket nsIServerSocket
   *        The socket that is about to start listening.
   */
  augmentSocketOptions(listener, socket) {
    let requestCert = Ci.nsITLSServerSocket.REQUIRE_ALWAYS;
    socket.setRequestClientCertificate(requestCert);
  },

  /**
   * Augment the service discovery advertisement with any additional data needed
   * to support this authentication mode.
   *
   * @param listener SocketListener
   *        The socket listener that was just opened.
   * @param advertisement object
   *        The advertisement being built.
   */
  augmentAdvertisement(listener, advertisement) {
    advertisement.authentication = OOBCert.mode;
    // Step A.4
    // Server announces itself via service discovery
    // Announcement contains hash(ServerCert) as additional data
    advertisement.cert = listener.cert;
  },

  /**
   * Determine whether a connection the server should be allowed or not based on
   * this authenticator's policies.
   *
   * @param session object
   *        In OOB_CERT mode, the |session| includes:
   *        {
   *          client: {
   *            host,
   *            port,
   *            cert: {
   *              sha256
   *            },
   *          },
   *          server: {
   *            host,
   *            port,
   *            cert: {
   *              sha256
   *            }
   *          },
   *          transport
   *        }
   * @return An AuthenticationResult value.
   *         A promise that will be resolved to the above is also allowed.
   */
  async authenticate({ client, server, transport }) {
    // Step B.3 / C.3
    // TLS connection established, authentication begins
    const storageKey = `devtools.auth.${this.mode}.approved-clients`;
    let approvedClients = (await asyncStorage.getItem(storageKey)) || {};
    // Step C.4
    // Server sees that ClientCert is from a known client via hash(ClientCert)
    if (approvedClients[client.cert.sha256]) {
      let authResult = AuthenticationResult.ALLOW_PERSIST;
      transport.send({ authResult });
      // Step C.5
      // Debugging begins
      return authResult;
    }

    // Step B.4
    // Server sees that ClientCert is from a unknown client
    // Tell client they are unknown and should display OOB client UX
    transport.send({
      authResult: AuthenticationResult.PENDING
    });

    // Step B.5
    // User is shown a Allow / Deny / Always Allow prompt on the Server
    // with Client name and hash(ClientCert)
    let authResult = await this.allowConnection({
      authentication: this.mode,
      client,
      server
    });

    switch (authResult) {
      case AuthenticationResult.ALLOW_PERSIST:
      case AuthenticationResult.ALLOW:
        // Further processing
        break;
      default:
        // Abort for any negative results
        return authResult;
    }

    // Examine additional data for authentication
    let oob = await this.receiveOOB();
    if (!oob) {
      dumpn("Invalid OOB data received");
      return AuthenticationResult.DENY;
    }

    let { sha256, k } = oob;
    // The OOB auth prompt should have transferred:
    // hash(ClientCert) + K(random 128-bit number)
    // from the client.
    if (!sha256 || !k) {
      dumpn("Invalid OOB data received");
      return AuthenticationResult.DENY;
    }

    // Step B.10
    // Server verifies that Client's cert matches hash(ClientCert) from
    // out-of-band channel
    if (client.cert.sha256 != sha256) {
      dumpn("Client cert hash doesn't match OOB data");
      return AuthenticationResult.DENY;
    }

    // Step B.11
    // Server sends K to Client over TLS connection
    transport.send({ authResult, k });

    // Persist Client if we want to always allow in the future
    if (authResult === AuthenticationResult.ALLOW_PERSIST) {
      approvedClients[client.cert.sha256] = true;
      await asyncStorage.setItem(storageKey, approvedClients);
    }

    // Client may decide to abort if K does not match.
    // Server's portion of authentication is now complete.

    // Step B.13
    // Debugging begins
    return authResult;
  },

  /**
   * Prompt the user to accept or decline the incoming connection. The default
   * implementation is used unless this is overridden on a particular
   * authenticator instance.
   *
   * It is expected that the implementation of |allowConnection| will show a
   * prompt to the user so that they can allow or deny the connection.
   *
   * @param session object
   *        In OOB_CERT mode, the |session| includes:
   *        {
   *          authentication: "OOB_CERT",
   *          client: {
   *            host,
   *            port,
   *            cert: {
   *              sha256
   *            },
   *          },
   *          server: {
   *            host,
   *            port,
   *            cert: {
   *              sha256
   *            }
   *          }
   *        }
   * @return An AuthenticationResult value.
   *         A promise that will be resolved to the above is also allowed.
   */
  allowConnection: prompt.Server.defaultAllowConnection,

  /**
   * The user must transfer some data through some out of band mechanism from
   * the client to the server to authenticate the devices.
   *
   * @return An object containing:
   *         * sha256: hash(ClientCert)
   *         * k     : K(random 128-bit number)
   *         A promise that will be resolved to the above is also allowed.
   */
  receiveOOB: prompt.Server.defaultReceiveOOB,

};

exports.Authenticators = {
  get(mode) {
    if (!mode) {
      mode = Prompt.mode;
    }
    for (let key in Authenticators) {
      let auth = Authenticators[key];
      if (auth.mode === mode) {
        return auth;
      }
    }
    throw new Error("Unknown authenticator mode: " + mode);
  }
};
PK
!<�P��QQ8chrome/devtools/modules/devtools/shared/security/cert.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

var { Ci, Cc } = require("chrome");
var defer = require("devtools/shared/defer");
var DevToolsUtils = require("devtools/shared/DevToolsUtils");
DevToolsUtils.defineLazyGetter(this, "localCertService", () => {
  // Ensure PSM is initialized to support TLS sockets
  Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports);
  return Cc["@mozilla.org/security/local-cert-service;1"]
         .getService(Ci.nsILocalCertService);
});

const localCertName = "devtools";

exports.local = {

  /**
   * Get or create a new self-signed X.509 cert to represent this device for
   * DevTools purposes over a secure transport, like TLS.
   *
   * The cert is stored permanently in the profile's key store after first use,
   * and is valid for 1 year.  If an expired or otherwise invalid cert is found,
   * it is removed and a new one is made.
   *
   * @return promise
   */
  getOrCreate() {
    let deferred = defer();
    localCertService.getOrCreateCert(localCertName, {
      handleCert: function (cert, rv) {
        if (rv) {
          deferred.reject(rv);
          return;
        }
        deferred.resolve(cert);
      }
    });
    return deferred.promise;
  },

  /**
   * Remove the DevTools self-signed X.509 cert for this device.
   *
   * @return promise
   */
  remove() {
    let deferred = defer();
    localCertService.removeCert(localCertName, {
      handleCert: function (rv) {
        if (rv) {
          deferred.reject(rv);
          return;
        }
        deferred.resolve();
      }
    });
    return deferred.promise;
  }

};
PK
!<8sP���:chrome/devtools/modules/devtools/shared/security/prompt.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

var { Ci } = require("chrome");
var Services = require("Services");
var DevToolsUtils = require("devtools/shared/DevToolsUtils");
loader.lazyRequireGetter(this, "DebuggerSocket",
  "devtools/shared/security/socket", true);
loader.lazyRequireGetter(this, "AuthenticationResult",
  "devtools/shared/security/auth", true);

const {LocalizationHelper} = require("devtools/shared/l10n");
const L10N = new LocalizationHelper("devtools/shared/locales/debugger.properties");

var Client = exports.Client = {};
var Server = exports.Server = {};

/**
 * During OOB_CERT authentication, a notification dialog like this is used to
 * to display a token which the user must transfer through some mechanism to the
 * server to authenticate the devices.
 *
 * This implementation presents the token as text for the user to transfer
 * manually.  For a mobile device, you should override this implementation with
 * something more convenient, such as displaying a QR code.
 *
 * @param host string
 *        The host name or IP address of the debugger server.
 * @param port number
 *        The port number of the debugger server.
 * @param cert object (optional)
 *        The server's cert details.
 * @param authResult AuthenticationResult
 *        Authentication result sent from the server.
 * @param oob object (optional)
 *        The token data to be transferred during OOB_CERT step 8:
 *        * sha256: hash(ClientCert)
 *        * k     : K(random 128-bit number)
 * @return object containing:
 *         * close: Function to hide the notification
 */
Client.defaultSendOOB = ({ authResult, oob }) => {
  // Only show in the PENDING state
  if (authResult != AuthenticationResult.PENDING) {
    throw new Error("Expected PENDING result, got " + authResult);
  }
  let title = L10N.getStr("clientSendOOBTitle");
  let header = L10N.getStr("clientSendOOBHeader");
  let hashMsg = L10N.getFormatStr("clientSendOOBHash", oob.sha256);
  let token = oob.sha256.replace(/:/g, "").toLowerCase() + oob.k;
  let tokenMsg = L10N.getFormatStr("clientSendOOBToken", token);
  let msg = `${header}\n\n${hashMsg}\n${tokenMsg}`;
  let prompt = Services.prompt;
  let flags = prompt.BUTTON_POS_0 * prompt.BUTTON_TITLE_CANCEL;

  // Listen for the window our prompt opens, so we can close it programatically
  let promptWindow;
  let windowListener = {
    onOpenWindow(xulWindow) {
      let win = xulWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsIDOMWindow);
      win.addEventListener("load", function () {
        if (win.document.documentElement.getAttribute("id") != "commonDialog") {
          return;
        }
        // Found the window
        promptWindow = win;
        Services.wm.removeListener(windowListener);
      }, {once: true});
    },
    onCloseWindow() {},
  };
  Services.wm.addListener(windowListener);

  // nsIPrompt is typically a blocking API, so |executeSoon| to get around this
  DevToolsUtils.executeSoon(() => {
    prompt.confirmEx(null, title, msg, flags, null, null, null, null,
                     { value: false });
  });

  return {
    close() {
      if (!promptWindow) {
        return;
      }
      promptWindow.document.documentElement.acceptDialog();
      promptWindow = null;
    }
  };
};

/**
 * Prompt the user to accept or decline the incoming connection.  This is the
 * default implementation that products embedding the debugger server may
 * choose to override.  This can be overridden via |allowConnection| on the
 * socket's authenticator instance.
 *
 * @param session object
 *        The session object will contain at least the following fields:
 *        {
 *          authentication,
 *          client: {
 *            host,
 *            port
 *          },
 *          server: {
 *            host,
 *            port
 *          }
 *        }
 *        Specific authentication modes may include additional fields.  Check
 *        the different |allowConnection| methods in ./auth.js.
 * @return An AuthenticationResult value.
 *         A promise that will be resolved to the above is also allowed.
 */
Server.defaultAllowConnection = ({ client, server }) => {
  let title = L10N.getStr("remoteIncomingPromptTitle");
  let header = L10N.getStr("remoteIncomingPromptHeader");
  let clientEndpoint = `${client.host}:${client.port}`;
  let clientMsg = L10N.getFormatStr("remoteIncomingPromptClientEndpoint", clientEndpoint);
  let serverEndpoint = `${server.host}:${server.port}`;
  let serverMsg = L10N.getFormatStr("remoteIncomingPromptServerEndpoint", serverEndpoint);
  let footer = L10N.getStr("remoteIncomingPromptFooter");
  let msg = `${header}\n\n${clientMsg}\n${serverMsg}\n\n${footer}`;
  let disableButton = L10N.getStr("remoteIncomingPromptDisable");
  let prompt = Services.prompt;
  let flags = prompt.BUTTON_POS_0 * prompt.BUTTON_TITLE_OK +
              prompt.BUTTON_POS_1 * prompt.BUTTON_TITLE_CANCEL +
              prompt.BUTTON_POS_2 * prompt.BUTTON_TITLE_IS_STRING +
              prompt.BUTTON_POS_1_DEFAULT;
  let result = prompt.confirmEx(null, title, msg, flags, null, null,
                                disableButton, null, { value: false });
  if (result === 0) {
    return AuthenticationResult.ALLOW;
  }
  if (result === 2) {
    return AuthenticationResult.DISABLE_ALL;
  }
  return AuthenticationResult.DENY;
};

/**
 * During OOB_CERT authentication, the user must transfer some data through some
 * out of band mechanism from the client to the server to authenticate the
 * devices.
 *
 * This implementation prompts the user for a token as constructed by
 * |Client.defaultSendOOB| that the user needs to transfer manually.  For a
 * mobile device, you should override this implementation with something more
 * convenient, such as reading a QR code.
 *
 * @return An object containing:
 *         * sha256: hash(ClientCert)
 *         * k     : K(random 128-bit number)
 *         A promise that will be resolved to the above is also allowed.
 */
Server.defaultReceiveOOB = () => {
  let title = L10N.getStr("serverReceiveOOBTitle");
  let msg = L10N.getStr("serverReceiveOOBBody");
  let input = { value: null };
  let prompt = Services.prompt;
  let result = prompt.prompt(null, title, msg, input, null, { value: false });
  if (!result) {
    return null;
  }
  // Re-create original object from token
  input = input.value.trim();
  let sha256 = input.substring(0, 64);
  sha256 = sha256.replace(/\w{2}/g, "$&:").slice(0, -1).toUpperCase();
  let k = input.substring(64);
  return { sha256, k };
};
PK
!<!N���a�a:chrome/devtools/modules/devtools/shared/security/socket.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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";

var { Ci, Cc, CC, Cr } = require("chrome");

// Ensure PSM is initialized to support TLS sockets
Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports);

var Services = require("Services");
var promise = require("promise");
var defer = require("devtools/shared/defer");
var DevToolsUtils = require("devtools/shared/DevToolsUtils");
var { dumpn, dumpv } = DevToolsUtils;
loader.lazyRequireGetter(this, "WebSocketServer",
  "devtools/server/websocket-server");
loader.lazyRequireGetter(this, "DebuggerTransport",
  "devtools/shared/transport/transport", true);
loader.lazyRequireGetter(this, "WebSocketDebuggerTransport",
  "devtools/shared/transport/websocket-transport");
loader.lazyRequireGetter(this, "DebuggerServer",
  "devtools/server/main", true);
loader.lazyRequireGetter(this, "discovery",
  "devtools/shared/discovery/discovery");
loader.lazyRequireGetter(this, "cert",
  "devtools/shared/security/cert");
loader.lazyRequireGetter(this, "Authenticators",
  "devtools/shared/security/auth", true);
loader.lazyRequireGetter(this, "AuthenticationResult",
  "devtools/shared/security/auth", true);

DevToolsUtils.defineLazyGetter(this, "nsFile", () => {
  return CC("@mozilla.org/file/local;1", "nsIFile", "initWithPath");
});

DevToolsUtils.defineLazyGetter(this, "socketTransportService", () => {
  return Cc["@mozilla.org/network/socket-transport-service;1"]
         .getService(Ci.nsISocketTransportService);
});

DevToolsUtils.defineLazyGetter(this, "certOverrideService", () => {
  return Cc["@mozilla.org/security/certoverride;1"]
         .getService(Ci.nsICertOverrideService);
});

DevToolsUtils.defineLazyGetter(this, "nssErrorsService", () => {
  return Cc["@mozilla.org/nss_errors_service;1"]
         .getService(Ci.nsINSSErrorsService);
});

var DebuggerSocket = {};

/**
 * Connects to a debugger server socket.
 *
 * @param host string
 *        The host name or IP address of the debugger server.
 * @param port number
 *        The port number of the debugger server.
 * @param encryption boolean (optional)
 *        Whether the server requires encryption.  Defaults to false.
 * @param webSocket boolean (optional)
 *        Whether to use WebSocket protocol to connect. Defaults to false.
 * @param authenticator Authenticator (optional)
 *        |Authenticator| instance matching the mode in use by the server.
 *        Defaults to a PROMPT instance if not supplied.
 * @param cert object (optional)
 *        The server's cert details.  Used with OOB_CERT authentication.
 * @return promise
 *         Resolved to a DebuggerTransport instance.
 */
DebuggerSocket.connect = async function (settings) {
  // Default to PROMPT |Authenticator| instance if not supplied
  if (!settings.authenticator) {
    settings.authenticator = new (Authenticators.get().Client)();
  }
  _validateSettings(settings);
  let { host, port, encryption, authenticator, cert } = settings;
  let transport = await _getTransport(settings);
  await authenticator.authenticate({
    host,
    port,
    encryption,
    cert,
    transport
  });
  transport.connectionSettings = settings;
  return transport;
};

/**
 * Validate that the connection settings have been set to a supported configuration.
 */
function _validateSettings(settings) {
  let { encryption, webSocket, authenticator } = settings;

  if (webSocket && encryption) {
    throw new Error("Encryption not supported on WebSocket transport");
  }
  authenticator.validateSettings(settings);
}

/**
 * Try very hard to create a DevTools transport, potentially making several
 * connect attempts in the process.
 *
 * @param host string
 *        The host name or IP address of the debugger server.
 * @param port number
 *        The port number of the debugger server.
 * @param encryption boolean (optional)
 *        Whether the server requires encryption.  Defaults to false.
 * @param webSocket boolean (optional)
 *        Whether to use WebSocket protocol to connect to the server. Defaults to false.
 * @param authenticator Authenticator
 *        |Authenticator| instance matching the mode in use by the server.
 *        Defaults to a PROMPT instance if not supplied.
 * @param cert object (optional)
 *        The server's cert details.  Used with OOB_CERT authentication.
 * @return transport DebuggerTransport
 *         A possible DevTools transport (if connection succeeded and streams
 *         are actually alive and working)
 */
var _getTransport = async function (settings) {
  let { host, port, encryption, webSocket } = settings;

  if (webSocket) {
    // Establish a connection and wait until the WebSocket is ready to send and receive
    let socket = await new Promise((resolve, reject) => {
      let s = new WebSocket(`ws://${host}:${port}`);
      s.onopen = () => resolve(s);
      s.onerror = err => reject(err);
    });

    return new WebSocketDebuggerTransport(socket);
  }

  let attempt = await _attemptTransport(settings);
  if (attempt.transport) {
    // Success
    return attempt.transport;
  }

  // If the server cert failed validation, store a temporary override and make
  // a second attempt.
  if (encryption && attempt.certError) {
    _storeCertOverride(attempt.s, host, port);
  } else {
    throw new Error("Connection failed");
  }

  attempt = await _attemptTransport(settings);
  if (attempt.transport) {
    // Success
    return attempt.transport;
  }

  throw new Error("Connection failed even after cert override");
};

/**
 * Make a single attempt to connect and create a DevTools transport.  This could
 * fail if the remote host is unreachable, for example.  If there is security
 * error due to the use of self-signed certs, you should make another attempt
 * after storing a cert override.
 *
 * @param host string
 *        The host name or IP address of the debugger server.
 * @param port number
 *        The port number of the debugger server.
 * @param encryption boolean (optional)
 *        Whether the server requires encryption.  Defaults to false.
 * @param authenticator Authenticator
 *        |Authenticator| instance matching the mode in use by the server.
 *        Defaults to a PROMPT instance if not supplied.
 * @param cert object (optional)
 *        The server's cert details.  Used with OOB_CERT authentication.
 * @return transport DebuggerTransport
 *         A possible DevTools transport (if connection succeeded and streams
 *         are actually alive and working)
 * @return certError boolean
 *         Flag noting if cert trouble caused the streams to fail
 * @return s nsISocketTransport
 *         Underlying socket transport, in case more details are needed.
 */
var _attemptTransport = async function (settings) {
  let { authenticator } = settings;
  // _attemptConnect only opens the streams.  Any failures at that stage
  // aborts the connection process immedidately.
  let { s, input, output } = await _attemptConnect(settings);

  // Check if the input stream is alive.  If encryption is enabled, we need to
  // watch out for cert errors by testing the input stream.
  let alive, certError;
  try {
    let results = await _isInputAlive(input);
    alive = results.alive;
    certError = results.certError;
  } catch (e) {
    // For other unexpected errors, like NS_ERROR_CONNECTION_REFUSED, we reach
    // this block.
    input.close();
    output.close();
    throw e;
  }
  dumpv("Server cert accepted? " + !certError);

  // The |Authenticator| examines the connection as well and may determine it
  // should be dropped.
  alive = alive && authenticator.validateConnection({
    host: settings.host,
    port: settings.port,
    encryption: settings.encryption,
    cert: settings.cert,
    socket: s
  });

  let transport;
  if (alive) {
    transport = new DebuggerTransport(input, output);
  } else {
    // Something went wrong, close the streams.
    input.close();
    output.close();
  }

  return { transport, certError, s };
};

/**
 * Try to connect to a remote server socket.
 *
 * If successsful, the socket transport and its opened streams are returned.
 * Typically, this will only fail if the host / port is unreachable.  Other
 * problems, such as security errors, will allow this stage to succeed, but then
 * fail later when the streams are actually used.
 * @return s nsISocketTransport
 *         Underlying socket transport, in case more details are needed.
 * @return input nsIAsyncInputStream
 *         The socket's input stream.
 * @return output nsIAsyncOutputStream
 *         The socket's output stream.
 */
var _attemptConnect = async function ({ host, port, encryption }) {
  let s;
  if (encryption) {
    s = socketTransportService.createTransport(["ssl"], 1, host, port, null);
  } else {
    s = socketTransportService.createTransport(null, 0, host, port, null);
  }
  // By default the CONNECT socket timeout is very long, 65535 seconds,
  // so that if we race to be in CONNECT state while the server socket is still
  // initializing, the connection is stuck in connecting state for 18.20 hours!
  s.setTimeout(Ci.nsISocketTransport.TIMEOUT_CONNECT, 2);

  // If encrypting, load the client cert now, so we can deliver it at just the
  // right time.
  let clientCert;
  if (encryption) {
    clientCert = await cert.local.getOrCreate();
  }

  let deferred = defer();
  let input;
  let output;
  // Delay opening the input stream until the transport has fully connected.
  // The goal is to avoid showing the user a client cert UI prompt when
  // encryption is used.  This prompt is shown when the client opens the input
  // stream and does not know which client cert to present to the server.  To
  // specify a client cert programmatically, we need to access the transport's
  // nsISSLSocketControl interface, which is not accessible until the transport
  // has connected.
  s.setEventSink({
    onTransportStatus(transport, status) {
      if (status != Ci.nsISocketTransport.STATUS_CONNECTING_TO) {
        return;
      }
      if (encryption) {
        let sslSocketControl =
          transport.securityInfo.QueryInterface(Ci.nsISSLSocketControl);
        sslSocketControl.clientCert = clientCert;
      }
      try {
        input = s.openInputStream(0, 0, 0);
      } catch (e) {
        deferred.reject(e);
      }
      deferred.resolve({ s, input, output });
    }
  }, Services.tm.currentThread);

  // openOutputStream may throw NS_ERROR_NOT_INITIALIZED if we hit some race
  // where the nsISocketTransport gets shutdown in between its instantiation and
  // the call to this method.
  try {
    output = s.openOutputStream(0, 0, 0);
  } catch (e) {
    deferred.reject(e);
  }

  deferred.promise.catch(e => {
    if (input) {
      input.close();
    }
    if (output) {
      output.close();
    }
    DevToolsUtils.reportException("_attemptConnect", e);
  });

  return deferred.promise;
};

/**
 * Check if the input stream is alive.  For an encrypted connection, it may not
 * be if the client refuses the server's cert.  A cert error is expected on
 * first connection to a new host because the cert is self-signed.
 */
function _isInputAlive(input) {
  let deferred = defer();
  input.asyncWait({
    onInputStreamReady(stream) {
      try {
        stream.available();
        deferred.resolve({ alive: true });
      } catch (e) {
        try {
          // getErrorClass may throw if you pass a non-NSS error
          let errorClass = nssErrorsService.getErrorClass(e.result);
          if (errorClass === Ci.nsINSSErrorsService.ERROR_CLASS_BAD_CERT) {
            deferred.resolve({ certError: true });
          } else {
            deferred.reject(e);
          }
        } catch (nssErr) {
          deferred.reject(e);
        }
      }
    }
  }, 0, 0, Services.tm.currentThread);
  return deferred.promise;
}

/**
 * To allow the connection to proceed with self-signed cert, we store a cert
 * override.  This implies that we take on the burden of authentication for
 * these connections.
 */
function _storeCertOverride(s, host, port) {
  let cert = s.securityInfo.QueryInterface(Ci.nsISSLStatusProvider)
              .SSLStatus.serverCert;
  let overrideBits = Ci.nsICertOverrideService.ERROR_UNTRUSTED |
                     Ci.nsICertOverrideService.ERROR_MISMATCH;
  certOverrideService.rememberValidityOverride(host, port, cert, overrideBits,
                                               true /* temporary */);
}

/**
 * Creates a new socket listener for remote connections to the DebuggerServer.
 * This helps contain and organize the parts of the server that may differ or
 * are particular to one given listener mechanism vs. another.
 */
function SocketListener() {}

SocketListener.prototype = {

  /* Socket Options */

  /**
   * The port or path to listen on.
   *
   * If given an integer, the port to listen on.  Use -1 to choose any available
   * port. Otherwise, the path to the unix socket domain file to listen on.
   */
  portOrPath: null,

  /**
   * Controls whether this listener is announced via the service discovery
   * mechanism.
   */
  discoverable: false,

  /**
   * Controls whether this listener's transport uses encryption.
   */
  encryption: false,

  /**
   * Controls the |Authenticator| used, which hooks various socket steps to
   * implement an authentication policy.  It is expected that different use
   * cases may override pieces of the |Authenticator|.  See auth.js.
   *
   * Here we set the default |Authenticator|, which is |Prompt|.
   */
  authenticator: new (Authenticators.get().Server)(),

  /**
   * Validate that all options have been set to a supported configuration.
   */
  _validateOptions: function () {
    if (this.portOrPath === null) {
      throw new Error("Must set a port / path to listen on.");
    }
    if (this.discoverable && !Number(this.portOrPath)) {
      throw new Error("Discovery only supported for TCP sockets.");
    }
    if (this.encryption && this.webSocket) {
      throw new Error("Encryption not supported on WebSocket transport");
    }
    this.authenticator.validateOptions(this);
  },

  /**
   * Listens on the given port or socket file for remote debugger connections.
   */
  open: function () {
    this._validateOptions();
    DebuggerServer._addListener(this);

    let flags = Ci.nsIServerSocket.KeepWhenOffline;
    // A preference setting can force binding on the loopback interface.
    if (Services.prefs.getBoolPref("devtools.debugger.force-local")) {
      flags |= Ci.nsIServerSocket.LoopbackOnly;
    }

    let self = this;
    return (async function () {
      let backlog = 4;
      self._socket = self._createSocketInstance();
      if (self.isPortBased) {
        let port = Number(self.portOrPath);
        self._socket.initSpecialConnection(port, flags, backlog);
      } else {
        let file = nsFile(self.portOrPath);
        if (file.exists()) {
          file.remove(false);
        }
        self._socket.initWithFilename(file, parseInt("666", 8), backlog);
      }
      await self._setAdditionalSocketOptions();
      self._socket.asyncListen(self);
      dumpn("Socket listening on: " + (self.port || self.portOrPath));
    })().then(() => {
      this._advertise();
    }).catch(e => {
      dumpn("Could not start debugging listener on '" + this.portOrPath +
            "': " + e);
      this.close();
    });
  },

  _advertise: function () {
    if (!this.discoverable || !this.port) {
      return;
    }

    let advertisement = {
      port: this.port,
      encryption: this.encryption,
    };

    this.authenticator.augmentAdvertisement(this, advertisement);

    discovery.addService("devtools", advertisement);
  },

  _createSocketInstance: function () {
    if (this.encryption) {
      return Cc["@mozilla.org/network/tls-server-socket;1"]
             .createInstance(Ci.nsITLSServerSocket);
    }
    return Cc["@mozilla.org/network/server-socket;1"]
           .createInstance(Ci.nsIServerSocket);
  },

  async _setAdditionalSocketOptions() {
    if (this.encryption) {
      this._socket.serverCert = await cert.local.getOrCreate();
      this._socket.setSessionCache(false);
      this._socket.setSessionTickets(false);
      let requestCert = Ci.nsITLSServerSocket.REQUEST_NEVER;
      this._socket.setRequestClientCertificate(requestCert);
    }
    this.authenticator.augmentSocketOptions(this, this._socket);
  },

  /**
   * Closes the SocketListener.  Notifies the server to remove the listener from
   * the set of active SocketListeners.
   */
  close: function () {
    if (this.discoverable && this.port) {
      discovery.removeService("devtools");
    }
    if (this._socket) {
      this._socket.close();
      this._socket = null;
    }
    DebuggerServer._removeListener(this);
  },

  get host() {
    if (!this._socket) {
      return null;
    }
    if (Services.prefs.getBoolPref("devtools.debugger.force-local")) {
      return "127.0.0.1";
    }
    return "0.0.0.0";
  },

  /**
   * Gets whether this listener uses a port number vs. a path.
   */
  get isPortBased() {
    return !!Number(this.portOrPath);
  },

  /**
   * Gets the port that a TCP socket listener is listening on, or null if this
   * is not a TCP socket (so there is no port).
   */
  get port() {
    if (!this.isPortBased || !this._socket) {
      return null;
    }
    return this._socket.port;
  },

  get cert() {
    if (!this._socket || !this._socket.serverCert) {
      return null;
    }
    return {
      sha256: this._socket.serverCert.sha256Fingerprint
    };
  },

  // nsIServerSocketListener implementation

  onSocketAccepted:
  DevToolsUtils.makeInfallible(function (socket, socketTransport) {
    new ServerSocketConnection(this, socketTransport);
  }, "SocketListener.onSocketAccepted"),

  onStopListening: function (socket, status) {
    dumpn("onStopListening, status: " + status);
  }

};

// Client must complete TLS handshake within this window (ms)
loader.lazyGetter(this, "HANDSHAKE_TIMEOUT", () => {
  return Services.prefs.getIntPref("devtools.remote.tls-handshake-timeout");
});

/**
 * A |ServerSocketConnection| is created by a |SocketListener| for each accepted
 * incoming socket.  This is a short-lived object used to implement
 * authentication and verify encryption prior to handing off the connection to
 * the |DebuggerServer|.
 */
function ServerSocketConnection(listener, socketTransport) {
  this._listener = listener;
  this._socketTransport = socketTransport;
  this._handle();
}

ServerSocketConnection.prototype = {

  get authentication() {
    return this._listener.authenticator.mode;
  },

  get host() {
    return this._socketTransport.host;
  },

  get port() {
    return this._socketTransport.port;
  },

  get cert() {
    if (!this._clientCert) {
      return null;
    }
    return {
      sha256: this._clientCert.sha256Fingerprint
    };
  },

  get address() {
    return this.host + ":" + this.port;
  },

  get client() {
    let client = {
      host: this.host,
      port: this.port
    };
    if (this.cert) {
      client.cert = this.cert;
    }
    return client;
  },

  get server() {
    let server = {
      host: this._listener.host,
      port: this._listener.port
    };
    if (this._listener.cert) {
      server.cert = this._listener.cert;
    }
    return server;
  },

  /**
   * This is the main authentication workflow.  If any pieces reject a promise,
   * the connection is denied.  If the entire process resolves successfully,
   * the connection is finally handed off to the |DebuggerServer|.
   */
  _handle() {
    dumpn("Debugging connection starting authentication on " + this.address);
    let self = this;
    (async function () {
      self._listenForTLSHandshake();
      await self._createTransport();
      await self._awaitTLSHandshake();
      await self._authenticate();
    })().then(() => this.allow()).catch(e => this.deny(e));
  },

  /**
   * We need to open the streams early on, as that is required in the case of
   * TLS sockets to keep the handshake moving.
   */
  async _createTransport() {
    let input = this._socketTransport.openInputStream(0, 0, 0);
    let output = this._socketTransport.openOutputStream(0, 0, 0);

    if (this._listener.webSocket) {
      let socket = await WebSocketServer.accept(this._socketTransport, input, output);
      this._transport = new WebSocketDebuggerTransport(socket);
    } else {
      this._transport = new DebuggerTransport(input, output);
    }

    // Start up the transport to observe the streams in case they are closed
    // early.  This allows us to clean up our state as well.
    this._transport.hooks = {
      onClosed: reason => {
        this.deny(reason);
      }
    };
    this._transport.ready();
  },

  /**
   * Set the socket's security observer, which receives an event via the
   * |onHandshakeDone| callback when the TLS handshake completes.
   */
  _setSecurityObserver(observer) {
    if (!this._socketTransport || !this._socketTransport.securityInfo) {
      return;
    }
    let connectionInfo = this._socketTransport.securityInfo
                         .QueryInterface(Ci.nsITLSServerConnectionInfo);
    connectionInfo.setSecurityObserver(observer);
  },

  /**
   * When encryption is used, we wait for the client to complete the TLS
   * handshake before proceeding.  The handshake details are validated in
   * |onHandshakeDone|.
   */
  _listenForTLSHandshake() {
    this._handshakeDeferred = defer();
    if (!this._listener.encryption) {
      this._handshakeDeferred.resolve();
      return;
    }
    this._setSecurityObserver(this);
    this._handshakeTimeout = setTimeout(this._onHandshakeTimeout.bind(this),
                                        HANDSHAKE_TIMEOUT);
  },

  _awaitTLSHandshake() {
    return this._handshakeDeferred.promise;
  },

  _onHandshakeTimeout() {
    dumpv("Client failed to complete TLS handshake");
    this._handshakeDeferred.reject(Cr.NS_ERROR_NET_TIMEOUT);
  },

  // nsITLSServerSecurityObserver implementation
  onHandshakeDone(socket, clientStatus) {
    clearTimeout(this._handshakeTimeout);
    this._setSecurityObserver(null);
    dumpv("TLS version:    " + clientStatus.tlsVersionUsed.toString(16));
    dumpv("TLS cipher:     " + clientStatus.cipherName);
    dumpv("TLS key length: " + clientStatus.keyLength);
    dumpv("TLS MAC length: " + clientStatus.macLength);
    this._clientCert = clientStatus.peerCert;
    /*
     * TODO: These rules should be really be set on the TLS socket directly, but
     * this would need more platform work to expose it via XPCOM.
     *
     * Enforcing cipher suites here would be a bad idea, as we want TLS
     * cipher negotiation to work correctly.  The server already allows only
     * Gecko's normal set of cipher suites.
     */
    if (clientStatus.tlsVersionUsed < Ci.nsITLSClientStatus.TLS_VERSION_1_2) {
      this._handshakeDeferred.reject(Cr.NS_ERROR_CONNECTION_REFUSED);
      return;
    }

    this._handshakeDeferred.resolve();
  },

  async _authenticate() {
    let result = await this._listener.authenticator.authenticate({
      client: this.client,
      server: this.server,
      transport: this._transport
    });
    switch (result) {
      case AuthenticationResult.DISABLE_ALL:
        DebuggerServer.closeAllListeners();
        Services.prefs.setBoolPref("devtools.debugger.remote-enabled", false);
        return promise.reject(Cr.NS_ERROR_CONNECTION_REFUSED);
      case AuthenticationResult.DENY:
        return promise.reject(Cr.NS_ERROR_CONNECTION_REFUSED);
      case AuthenticationResult.ALLOW:
      case AuthenticationResult.ALLOW_PERSIST:
        return promise.resolve();
      default:
        return promise.reject(Cr.NS_ERROR_CONNECTION_REFUSED);
    }
  },

  deny(result) {
    if (this._destroyed) {
      return;
    }
    let errorName = result;
    for (let name in Cr) {
      if (Cr[name] === result) {
        errorName = name;
        break;
      }
    }
    dumpn("Debugging connection denied on " + this.address +
          " (" + errorName + ")");
    if (this._transport) {
      this._transport.hooks = null;
      this._transport.close(result);
    }
    this._socketTransport.close(result);
    this.destroy();
  },

  allow() {
    if (this._destroyed) {
      return;
    }
    dumpn("Debugging connection allowed on " + this.address);
    DebuggerServer._onConnection(this._transport);
    this.destroy();
  },

  destroy() {
    this._destroyed = true;
    clearTimeout(this._handshakeTimeout);
    this._setSecurityObserver(null);
    this._listener = null;
    this._socketTransport = null;
    this._transport = null;
    this._clientCert = null;
  }

};

DebuggerSocket.createListener = function () {
  return new SocketListener();
};

exports.DebuggerSocket = DebuggerSocket;
PK
!<��&ݡݡ?chrome/devtools/modules/devtools/shared/sourcemap/source-map.js(function webpackUniversalModuleDefinition(root, factory) {
	if(typeof exports === 'object' && typeof module === 'object')
		module.exports = factory();
	else if(typeof define === 'function' && define.amd)
		define([], factory);
	else if(typeof exports === 'object')
		exports["sourceMap"] = factory();
	else
		root["sourceMap"] = factory();
})(this, function() {
return /******/ (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__) {

	/*
	 * 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 = __webpack_require__(1).SourceMapGenerator;
	exports.SourceMapConsumer = __webpack_require__(7).SourceMapConsumer;
	exports.SourceNode = __webpack_require__(10).SourceNode;


/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {

	/* -*- 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
	 */

	var base64VLQ = __webpack_require__(2);
	var util = __webpack_require__(4);
	var ArraySet = __webpack_require__(5).ArraySet;
	var MappingList = __webpack_require__(6).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.
	 */
	function SourceMapGenerator(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;
	}

	SourceMapGenerator.prototype._version = 3;

	/**
	 * Creates a new SourceMapGenerator based on a SourceMapConsumer
	 *
	 * @param aSourceMapConsumer The SourceMap.
	 */
	SourceMapGenerator.fromSourceMap =
	  function SourceMapGenerator_fromSourceMap(aSourceMapConsumer) {
	    var sourceRoot = aSourceMapConsumer.sourceRoot;
	    var generator = new SourceMapGenerator({
	      file: aSourceMapConsumer.file,
	      sourceRoot: sourceRoot
	    });
	    aSourceMapConsumer.eachMapping(function (mapping) {
	      var 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) {
	      var sourceRelative = sourceFile;
	      if (sourceRoot !== null) {
	        sourceRelative = util.relative(sourceRoot, sourceFile);
	      }

	      if (!generator._sources.has(sourceRelative)) {
	        generator._sources.add(sourceRelative);
	      }

	      var 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.
	 */
	SourceMapGenerator.prototype.addMapping =
	  function SourceMapGenerator_addMapping(aArgs) {
	    var generated = util.getArg(aArgs, 'generated');
	    var original = util.getArg(aArgs, 'original', null);
	    var source = util.getArg(aArgs, 'source', null);
	    var 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 != null && original.line,
	      originalColumn: original != null && original.column,
	      source: source,
	      name: name
	    });
	  };

	/**
	 * Set the source content for a source file.
	 */
	SourceMapGenerator.prototype.setSourceContent =
	  function SourceMapGenerator_setSourceContent(aSourceFile, aSourceContent) {
	    var 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.
	 */
	SourceMapGenerator.prototype.applySourceMap =
	  function SourceMapGenerator_applySourceMap(aSourceMapConsumer, aSourceFile, aSourceMapPath) {
	    var 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;
	    }
	    var 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.
	    var newSources = new ArraySet();
	    var 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.
	        var 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;
	          }
	        }
	      }

	      var source = mapping.source;
	      if (source != null && !newSources.has(source)) {
	        newSources.add(source);
	      }

	      var 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 (sourceFile) {
	      var content = aSourceMapConsumer.sourceContentFor(sourceFile);
	      if (content != null) {
	        if (aSourceMapPath != null) {
	          sourceFile = util.join(aSourceMapPath, sourceFile);
	        }
	        if (sourceRoot != null) {
	          sourceFile = util.relative(sourceRoot, sourceFile);
	        }
	        this.setSourceContent(sourceFile, 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.
	 */
	SourceMapGenerator.prototype._validateMapping =
	  function SourceMapGenerator_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.
	      return;
	    }
	    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.
	      return;
	    }
	    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.
	 */
	SourceMapGenerator.prototype._serializeMappings =
	  function SourceMapGenerator_serializeMappings() {
	    var previousGeneratedColumn = 0;
	    var previousGeneratedLine = 1;
	    var previousOriginalColumn = 0;
	    var previousOriginalLine = 0;
	    var previousName = 0;
	    var previousSource = 0;
	    var result = '';
	    var next;
	    var mapping;
	    var nameIdx;
	    var sourceIdx;

	    var mappings = this._mappings.toArray();
	    for (var 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;
	  };

	SourceMapGenerator.prototype._generateSourcesContent =
	  function SourceMapGenerator_generateSourcesContent(aSources, aSourceRoot) {
	    return aSources.map(function (source) {
	      if (!this._sourcesContents) {
	        return null;
	      }
	      if (aSourceRoot != null) {
	        source = util.relative(aSourceRoot, source);
	      }
	      var key = util.toSetString(source);
	      return Object.prototype.hasOwnProperty.call(this._sourcesContents, key)
	        ? this._sourcesContents[key]
	        : null;
	    }, this);
	  };

	/**
	 * Externalize the source map.
	 */
	SourceMapGenerator.prototype.toJSON =
	  function SourceMapGenerator_toJSON() {
	    var 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.
	 */
	SourceMapGenerator.prototype.toString =
	  function SourceMapGenerator_toString() {
	    return JSON.stringify(this.toJSON());
	  };

	exports.SourceMapGenerator = SourceMapGenerator;


/***/ }),
/* 2 */
/***/ (function(module, exports, __webpack_require__) {

	/* -*- 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.
	 */

	var base64 = __webpack_require__(3);

	// 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

	var VLQ_BASE_SHIFT = 5;

	// binary: 100000
	var VLQ_BASE = 1 << VLQ_BASE_SHIFT;

	// binary: 011111
	var VLQ_BASE_MASK = VLQ_BASE - 1;

	// binary: 100000
	var 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;
	}

	/**
	 * Converts to a two-complement value from a value where the sign bit is
	 * placed in the least significant bit.  For example, as decimals:
	 *   2 (10 binary) becomes 1, 3 (11 binary) becomes -1
	 *   4 (100 binary) becomes 2, 5 (101 binary) becomes -2
	 */
	function fromVLQSigned(aValue) {
	  var isNegative = (aValue & 1) === 1;
	  var shifted = aValue >> 1;
	  return isNegative
	    ? -shifted
	    : shifted;
	}

	/**
	 * Returns the base 64 VLQ encoded value.
	 */
	exports.encode = function base64VLQ_encode(aValue) {
	  var encoded = "";
	  var digit;

	  var 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;
	};

	/**
	 * Decodes the next base 64 VLQ value from the given string and returns the
	 * value and the rest of the string via the out parameter.
	 */
	exports.decode = function base64VLQ_decode(aStr, aIndex, aOutParam) {
	  var strLen = aStr.length;
	  var result = 0;
	  var shift = 0;
	  var continuation, digit;

	  do {
	    if (aIndex >= strLen) {
	      throw new Error("Expected more digits in base 64 VLQ value.");
	    }

	    digit = base64.decode(aStr.charCodeAt(aIndex++));
	    if (digit === -1) {
	      throw new Error("Invalid base64 digit: " + aStr.charAt(aIndex - 1));
	    }

	    continuation = !!(digit & VLQ_CONTINUATION_BIT);
	    digit &= VLQ_BASE_MASK;
	    result = result + (digit << shift);
	    shift += VLQ_BASE_SHIFT;
	  } while (continuation);

	  aOutParam.value = fromVLQSigned(result);
	  aOutParam.rest = aIndex;
	};


/***/ }),
/* 3 */
/***/ (function(module, exports) {

	/* -*- 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
	 */

	var 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);
	};

	/**
	 * Decode a single base 64 character code digit to an integer. Returns -1 on
	 * failure.
	 */
	exports.decode = function (charCode) {
	  var bigA = 65;     // 'A'
	  var bigZ = 90;     // 'Z'

	  var littleA = 97;  // 'a'
	  var littleZ = 122; // 'z'

	  var zero = 48;     // '0'
	  var nine = 57;     // '9'

	  var plus = 43;     // '+'
	  var slash = 47;    // '/'

	  var littleOffset = 26;
	  var numberOffset = 52;

	  // 0 - 25: ABCDEFGHIJKLMNOPQRSTUVWXYZ
	  if (bigA <= charCode && charCode <= bigZ) {
	    return (charCode - bigA);
	  }

	  // 26 - 51: abcdefghijklmnopqrstuvwxyz
	  if (littleA <= charCode && charCode <= littleZ) {
	    return (charCode - littleA + littleOffset);
	  }

	  // 52 - 61: 0123456789
	  if (zero <= charCode && charCode <= nine) {
	    return (charCode - zero + numberOffset);
	  }

	  // 62: +
	  if (charCode == plus) {
	    return 62;
	  }

	  // 63: /
	  if (charCode == slash) {
	    return 63;
	  }

	  // Invalid base64 digit.
	  return -1;
	};


/***/ }),
/* 4 */
/***/ (function(module, exports) {

	/* -*- 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
	 */

	/**
	 * 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;
	  } else {
	    throw new Error('"' + aName + '" is a required argument.');
	  }
	}
	exports.getArg = getArg;

	var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.-]*)(?::(\d+))?(.*)$/;
	var dataUrlRegexp = /^data:.+\,.+$/;

	function urlParse(aUrl) {
	  var match = aUrl.match(urlRegexp);
	  if (!match) {
	    return null;
	  }
	  return {
	    scheme: match[1],
	    auth: match[2],
	    host: match[3],
	    port: match[4],
	    path: match[5]
	  };
	}
	exports.urlParse = urlParse;

	function urlGenerate(aParsedUrl) {
	  var url = '';
	  if (aParsedUrl.scheme) {
	    url += aParsedUrl.scheme + ':';
	  }
	  url += '//';
	  if (aParsedUrl.auth) {
	    url += aParsedUrl.auth + '@';
	  }
	  if (aParsedUrl.host) {
	    url += aParsedUrl.host;
	  }
	  if (aParsedUrl.port) {
	    url += ":" + aParsedUrl.port
	  }
	  if (aParsedUrl.path) {
	    url += aParsedUrl.path;
	  }
	  return url;
	}
	exports.urlGenerate = urlGenerate;

	/**
	 * Normalizes a path, or the path portion of a URL:
	 *
	 * - Replaces consecutive slashes with one slash.
	 * - Removes unnecessary '.' parts.
	 * - Removes unnecessary '<dir>/..' parts.
	 *
	 * Based on code in the Node.js 'path' core module.
	 *
	 * @param aPath The path or url to normalize.
	 */
	function normalize(aPath) {
	  var path = aPath;
	  var url = urlParse(aPath);
	  if (url) {
	    if (!url.path) {
	      return aPath;
	    }
	    path = url.path;
	  }
	  var isAbsolute = exports.isAbsolute(path);

	  var parts = path.split(/\/+/);
	  for (var part, up = 0, i = parts.length - 1; i >= 0; i--) {
	    part = parts[i];
	    if (part === '.') {
	      parts.splice(i, 1);
	    } else if (part === '..') {
	      up++;
	    } else if (up > 0) {
	      if (part === '') {
	        // The first part is blank if the path is absolute. Trying to go
	        // above the root is a no-op. Therefore we can remove all '..' parts
	        // directly after the root.
	        parts.splice(i + 1, up);
	        up = 0;
	      } else {
	        parts.splice(i, 2);
	        up--;
	      }
	    }
	  }
	  path = parts.join('/');

	  if (path === '') {
	    path = isAbsolute ? '/' : '.';
	  }

	  if (url) {
	    url.path = path;
	    return urlGenerate(url);
	  }
	  return path;
	}
	exports.normalize = normalize;

	/**
	 * Joins two paths/URLs.
	 *
	 * @param aRoot The root path or URL.
	 * @param aPath The path or URL to be joined with the root.
	 *
	 * - If aPath is a URL or a data URI, aPath is returned, unless aPath is a
	 *   scheme-relative URL: Then the scheme of aRoot, if any, is prepended
	 *   first.
	 * - Otherwise aPath is a path. If aRoot is a URL, then its path portion
	 *   is updated with the result and aRoot is returned. Otherwise the result
	 *   is returned.
	 *   - If aPath is absolute, the result is aPath.
	 *   - Otherwise the two paths are joined with a slash.
	 * - Joining for example 'http://' and 'www.example.com' is also supported.
	 */
	function join(aRoot, aPath) {
	  if (aRoot === "") {
	    aRoot = ".";
	  }
	  if (aPath === "") {
	    aPath = ".";
	  }
	  var aPathUrl = urlParse(aPath);
	  var aRootUrl = urlParse(aRoot);
	  if (aRootUrl) {
	    aRoot = aRootUrl.path || '/';
	  }

	  // `join(foo, '//www.example.org')`
	  if (aPathUrl && !aPathUrl.scheme) {
	    if (aRootUrl) {
	      aPathUrl.scheme = aRootUrl.scheme;
	    }
	    return urlGenerate(aPathUrl);
	  }

	  if (aPathUrl || aPath.match(dataUrlRegexp)) {
	    return aPath;
	  }

	  // `join('http://', 'www.example.com')`
	  if (aRootUrl && !aRootUrl.host && !aRootUrl.path) {
	    aRootUrl.host = aPath;
	    return urlGenerate(aRootUrl);
	  }

	  var joined = aPath.charAt(0) === '/'
	    ? aPath
	    : normalize(aRoot.replace(/\/+$/, '') + '/' + aPath);

	  if (aRootUrl) {
	    aRootUrl.path = joined;
	    return urlGenerate(aRootUrl);
	  }
	  return joined;
	}
	exports.join = join;

	exports.isAbsolute = function (aPath) {
	  return aPath.charAt(0) === '/' || urlRegexp.test(aPath);
	};

	/**
	 * Make a path relative to a URL or another path.
	 *
	 * @param aRoot The root path or URL.
	 * @param aPath The path or URL to be made relative to aRoot.
	 */
	function relative(aRoot, aPath) {
	  if (aRoot === "") {
	    aRoot = ".";
	  }

	  aRoot = aRoot.replace(/\/$/, '');

	  // It is possible for the path to be above the root. In this case, simply
	  // checking whether the root is a prefix of the path won't work. Instead, we
	  // need to remove components from the root one by one, until either we find
	  // a prefix that fits, or we run out of components to remove.
	  var level = 0;
	  while (aPath.indexOf(aRoot + '/') !== 0) {
	    var index = aRoot.lastIndexOf("/");
	    if (index < 0) {
	      return aPath;
	    }

	    // If the only part of the root that is left is the scheme (i.e. http://,
	    // file:///, etc.), one or more slashes (/), or simply nothing at all, we
	    // have exhausted all components, so the path is not relative to the root.
	    aRoot = aRoot.slice(0, index);
	    if (aRoot.match(/^([^\/]+:\/)?\/*$/)) {
	      return aPath;
	    }

	    ++level;
	  }

	  // Make sure we add a "../" for each component we removed from the root.
	  return Array(level + 1).join("../") + aPath.substr(aRoot.length + 1);
	}
	exports.relative = relative;

	var supportsNullProto = (function () {
	  var 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;
	  }

	  var length = s.length;

	  if (length < 9 /* "__proto__".length */) {
	    return false;
	  }

	  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;
	  }

	  for (var i = length - 10; i >= 0; i--) {
	    if (s.charCodeAt(i) !== 36 /* '$' */) {
	      return false;
	    }
	  }

	  return true;
	}

	/**
	 * Comparator between two mappings where the original positions are compared.
	 *
	 * Optionally pass in `true` as `onlyCompareGenerated` to consider two
	 * mappings with the same original source/line/column, but different generated
	 * line and column the same. Useful when searching for a mapping with a
	 * stubbed out mapping.
	 */
	function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) {
	  var 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 || onlyCompareOriginal) {
	    return cmp;
	  }

	  cmp = mappingA.generatedColumn - mappingB.generatedColumn;
	  if (cmp !== 0) {
	    return cmp;
	  }

	  cmp = mappingA.generatedLine - mappingB.generatedLine;
	  if (cmp !== 0) {
	    return cmp;
	  }

	  return strcmp(mappingA.name, mappingB.name);
	}
	exports.compareByOriginalPositions = compareByOriginalPositions;

	/**
	 * Comparator between two mappings with deflated source and name indices where
	 * the generated positions are compared.
	 *
	 * Optionally pass in `true` as `onlyCompareGenerated` to consider two
	 * mappings with the same generated line and column, but different
	 * source/name/original line and column the same. Useful when searching for a
	 * mapping with a stubbed out mapping.
	 */
	function compareByGeneratedPositionsDeflated(mappingA, mappingB, onlyCompareGenerated) {
	  var cmp = mappingA.generatedLine - mappingB.generatedLine;
	  if (cmp !== 0) {
	    return cmp;
	  }

	  cmp = mappingA.generatedColumn - mappingB.generatedColumn;
	  if (cmp !== 0 || onlyCompareGenerated) {
	    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.compareByGeneratedPositionsDeflated = compareByGeneratedPositionsDeflated;

	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) {
	  var 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;

	/**
	 * 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) {
	  sourceURL = sourceURL || '';

	  if (sourceRoot) {
	    // This follows what Chrome does.
	    if (sourceRoot[sourceRoot.length - 1] !== '/' && sourceURL[0] !== '/') {
	      sourceRoot += '/';
	    }
	    // The spec says:
	    //   Line 4: An optional source root, useful for relocating source
	    //   files on a server or removing repeated values in the
	    //   “sources” entry.  This value is prepended to the individual
	    //   entries in the “source” field.
	    sourceURL = sourceRoot + sourceURL;
	  }

	  // Historically, SourceMapConsumer did not take the sourceMapURL as
	  // a parameter.  This mode is still somewhat supported, which is why
	  // this code block is conditional.  However, it's preferable to pass
	  // the source map URL to SourceMapConsumer, so that this function
	  // can implement the source URL resolution algorithm as outlined in
	  // the spec.  This block is basically the equivalent of:
	  //    new URL(sourceURL, sourceMapURL).toString()
	  // ... except it avoids using URL, which wasn't available in the
	  // older releases of node still supported by this library.
	  //
	  // The spec says:
	  //   If the sources are not absolute URLs after prepending of the
	  //   “sourceRoot”, the sources are resolved relative to the
	  //   SourceMap (like resolving script src in a html document).
	  if (sourceMapURL) {
	    var parsed = urlParse(sourceMapURL);
	    if (!parsed) {
	      throw new Error("sourceMapURL could not be parsed");
	    }
	    if (parsed.path) {
	      // Strip the last path component, but keep the "/".
	      var index = parsed.path.lastIndexOf('/');
	      if (index >= 0) {
	        parsed.path = parsed.path.substring(0, index + 1);
	      }
	    }
	    sourceURL = join(urlGenerate(parsed), sourceURL);
	  }

	  return normalize(sourceURL);
	}
	exports.computeSourceURL = computeSourceURL;


/***/ }),
/* 5 */
/***/ (function(module, exports, __webpack_require__) {

	/* -*- 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
	 */

	var util = __webpack_require__(4);
	var has = Object.prototype.hasOwnProperty;
	var hasNativeMap = typeof Map !== "undefined";

	/**
	 * 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.
	 */
	function ArraySet() {
	  this._array = [];
	  this._set = hasNativeMap ? new Map() : Object.create(null);
	}

	/**
	 * Static method for creating ArraySet instances from an existing array.
	 */
	ArraySet.fromArray = function ArraySet_fromArray(aArray, aAllowDuplicates) {
	  var set = new ArraySet();
	  for (var 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
	 */
	ArraySet.prototype.size = function ArraySet_size() {
	  return hasNativeMap ? this._set.size : Object.getOwnPropertyNames(this._set).length;
	};

	/**
	 * Add the given string to this set.
	 *
	 * @param String aStr
	 */
	ArraySet.prototype.add = function ArraySet_add(aStr, aAllowDuplicates) {
	  var sStr = hasNativeMap ? aStr : util.toSetString(aStr);
	  var isDuplicate = hasNativeMap ? this.has(aStr) : has.call(this._set, sStr);
	  var idx = this._array.length;
	  if (!isDuplicate || aAllowDuplicates) {
	    this._array.push(aStr);
	  }
	  if (!isDuplicate) {
	    if (hasNativeMap) {
	      this._set.set(aStr, idx);
	    } else {
	      this._set[sStr] = idx;
	    }
	  }
	};

	/**
	 * Is the given string a member of this set?
	 *
	 * @param String aStr
	 */
	ArraySet.prototype.has = function ArraySet_has(aStr) {
	  if (hasNativeMap) {
	    return this._set.has(aStr);
	  } else {
	    var sStr = util.toSetString(aStr);
	    return has.call(this._set, sStr);
	  }
	};

	/**
	 * What is the index of the given string in the array?
	 *
	 * @param String aStr
	 */
	ArraySet.prototype.indexOf = function ArraySet_indexOf(aStr) {
	  if (hasNativeMap) {
	    var idx = this._set.get(aStr);
	    if (idx >= 0) {
	        return idx;
	    }
	  } else {
	    var sStr = util.toSetString(aStr);
	    if (has.call(this._set, sStr)) {
	      return this._set[sStr];
	    }
	  }

	  throw new Error('"' + aStr + '" is not in the set.');
	};

	/**
	 * What is the element at the given index?
	 *
	 * @param Number aIdx
	 */
	ArraySet.prototype.at = function ArraySet_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.
	 */
	ArraySet.prototype.toArray = function ArraySet_toArray() {
	  return this._array.slice();
	};

	exports.ArraySet = ArraySet;


/***/ }),
/* 6 */
/***/ (function(module, exports, __webpack_require__) {

	/* -*- 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
	 */

	var util = __webpack_require__(4);

	/**
	 * Determine whether mappingB is after mappingA with respect to generated
	 * position.
	 */
	function generatedPositionAfter(mappingA, mappingB) {
	  // Optimized for most common case
	  var lineA = mappingA.generatedLine;
	  var lineB = mappingB.generatedLine;
	  var columnA = mappingA.generatedColumn;
	  var 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 neglibable overhead in general
	 * case for a large speedup in case of mappings being added in order.
	 */
	function MappingList() {
	  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.
	 */
	MappingList.prototype.unsortedForEach =
	  function MappingList_forEach(aCallback, aThisArg) {
	    this._array.forEach(aCallback, aThisArg);
	  };

	/**
	 * Add the given source mapping.
	 *
	 * @param Object aMapping
	 */
	MappingList.prototype.add = function MappingList_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.
	 */
	MappingList.prototype.toArray = function MappingList_toArray() {
	  if (!this._sorted) {
	    this._array.sort(util.compareByGeneratedPositionsInflated);
	    this._sorted = true;
	  }
	  return this._array;
	};

	exports.MappingList = MappingList;


/***/ }),
/* 7 */
/***/ (function(module, exports, __webpack_require__) {

	/* -*- 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
	 */

	var util = __webpack_require__(4);
	var binarySearch = __webpack_require__(8);
	var ArraySet = __webpack_require__(5).ArraySet;
	var base64VLQ = __webpack_require__(2);
	var quickSort = __webpack_require__(9).quickSort;

	function SourceMapConsumer(aSourceMap, aSourceMapURL) {
	  var sourceMap = aSourceMap;
	  if (typeof aSourceMap === 'string') {
	    sourceMap = util.parseSourceMapInput(aSourceMap);
	  }

	  return sourceMap.sections != null
	    ? new IndexedSourceMapConsumer(sourceMap, aSourceMapURL)
	    : new BasicSourceMapConsumer(sourceMap, aSourceMapURL);
	}

	SourceMapConsumer.fromSourceMap = function(aSourceMap, aSourceMapURL) {
	  return BasicSourceMapConsumer.fromSourceMap(aSourceMap, aSourceMapURL);
	}

	/**
	 * The version of the source mapping spec that we are consuming.
	 */
	SourceMapConsumer.prototype._version = 3;

	// `__generatedMappings` and `__originalMappings` are arrays that hold the
	// parsed mapping coordinates from the source map's "mappings" attribute. They
	// are lazily instantiated, accessed via the `_generatedMappings` and
	// `_originalMappings` getters respectively, and we only parse the mappings
	// and create these arrays once queried for a source location. We jump through
	// these hoops because there can be many thousands of mappings, and parsing
	// them is expensive, so we only want to do it if we must.
	//
	// Each object in the arrays is of the form:
	//
	//     {
	//       generatedLine: The line number in the generated code,
	//       generatedColumn: The column number in the generated code,
	//       source: The path to the original source file that generated this
	//               chunk of code,
	//       originalLine: The line number in the original source that
	//                     corresponds to this chunk of generated code,
	//       originalColumn: The column number in the original source that
	//                       corresponds to this chunk of generated code,
	//       name: The name of the original symbol which generated this chunk of
	//             code.
	//     }
	//
	// All properties except for `generatedLine` and `generatedColumn` can be
	// `null`.
	//
	// `_generatedMappings` is ordered by the generated positions.
	//
	// `_originalMappings` is ordered by the original positions.

	SourceMapConsumer.prototype.__generatedMappings = null;
	Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', {
	  configurable: true,
	  enumerable: true,
	  get: function () {
	    if (!this.__generatedMappings) {
	      this._parseMappings(this._mappings, this.sourceRoot);
	    }

	    return this.__generatedMappings;
	  }
	});

	SourceMapConsumer.prototype.__originalMappings = null;
	Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', {
	  configurable: true,
	  enumerable: true,
	  get: function () {
	    if (!this.__originalMappings) {
	      this._parseMappings(this._mappings, this.sourceRoot);
	    }

	    return this.__originalMappings;
	  }
	});

	SourceMapConsumer.prototype._charIsMappingSeparator =
	  function SourceMapConsumer_charIsMappingSeparator(aStr, index) {
	    var c = aStr.charAt(index);
	    return c === ";" || c === ",";
	  };

	/**
	 * 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).
	 */
	SourceMapConsumer.prototype._parseMappings =
	  function SourceMapConsumer_parseMappings(aStr, aSourceRoot) {
	    throw new Error("Subclasses must implement _parseMappings");
	  };

	SourceMapConsumer.GENERATED_ORDER = 1;
	SourceMapConsumer.ORIGINAL_ORDER = 2;

	SourceMapConsumer.GREATEST_LOWER_BOUND = 1;
	SourceMapConsumer.LEAST_UPPER_BOUND = 2;

	/**
	 * 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`.
	 */
	SourceMapConsumer.prototype.eachMapping =
	  function SourceMapConsumer_eachMapping(aCallback, aContext, aOrder) {
	    var context = aContext || null;
	    var order = aOrder || SourceMapConsumer.GENERATED_ORDER;

	    var mappings;
	    switch (order) {
	    case SourceMapConsumer.GENERATED_ORDER:
	      mappings = this._generatedMappings;
	      break;
	    case SourceMapConsumer.ORIGINAL_ORDER:
	      mappings = this._originalMappings;
	      break;
	    default:
	      throw new Error("Unknown order of iteration.");
	    }

	    var sourceRoot = this.sourceRoot;
	    mappings.map(function (mapping) {
	      var source = mapping.source === null ? null : this._sources.at(mapping.source);
	      source = util.computeSourceURL(sourceRoot, source, this._sourceMapURL);
	      return {
	        source: source,
	        generatedLine: mapping.generatedLine,
	        generatedColumn: mapping.generatedColumn,
	        originalLine: mapping.originalLine,
	        originalColumn: mapping.originalColumn,
	        name: mapping.name === null ? null : this._names.at(mapping.name)
	      };
	    }, this).forEach(aCallback, context);
	  };

	/**
	 * 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.
	 */
	SourceMapConsumer.prototype.allGeneratedPositionsFor =
	  function SourceMapConsumer_allGeneratedPositionsFor(aArgs) {
	    var line = util.getArg(aArgs, 'line');

	    // When there is no exact match, BasicSourceMapConsumer.prototype._findMapping
	    // returns the index of the closest mapping less than the needle. By
	    // setting needle.originalColumn to 0, we thus find the last mapping for
	    // the given line, provided such a mapping exists.
	    var needle = {
	      source: util.getArg(aArgs, 'source'),
	      originalLine: line,
	      originalColumn: util.getArg(aArgs, 'column', 0)
	    };

	    needle.source = this._findSourceIndex(needle.source);
	    if (needle.source < 0) {
	      return [];
	    }

	    var mappings = [];

	    var index = this._findMapping(needle,
	                                  this._originalMappings,
	                                  "originalLine",
	                                  "originalColumn",
	                                  util.compareByOriginalPositions,
	                                  binarySearch.LEAST_UPPER_BOUND);
	    if (index >= 0) {
	      var mapping = this._originalMappings[index];

	      if (aArgs.column === undefined) {
	        var originalLine = mapping.originalLine;

	        // Iterate until either we run out of mappings, or we run into
	        // a mapping for a different line than the one we found. Since
	        // mappings are sorted, this is guaranteed to find all mappings for
	        // the line we found.
	        while (mapping && mapping.originalLine === originalLine) {
	          mappings.push({
	            line: util.getArg(mapping, 'generatedLine', null),
	            column: util.getArg(mapping, 'generatedColumn', null),
	            lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null)
	          });

	          mapping = this._originalMappings[++index];
	        }
	      } else {
	        var originalColumn = mapping.originalColumn;

	        // Iterate until either we run out of mappings, or we run into
	        // a mapping for a different line than the one we were searching for.
	        // Since mappings are sorted, this is guaranteed to find all mappings for
	        // the line we are searching for.
	        while (mapping &&
	               mapping.originalLine === line &&
	               mapping.originalColumn == originalColumn) {
	          mappings.push({
	            line: util.getArg(mapping, 'generatedLine', null),
	            column: util.getArg(mapping, 'generatedColumn', null),
	            lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null)
	          });

	          mapping = this._originalMappings[++index];
	        }
	      }
	    }

	    return mappings;
	  };

	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 referrenced 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#
	 */
	function BasicSourceMapConsumer(aSourceMap, aSourceMapURL) {
	  var sourceMap = aSourceMap;
	  if (typeof aSourceMap === 'string') {
	    sourceMap = util.parseSourceMapInput(aSourceMap);
	  }

	  var version = util.getArg(sourceMap, 'version');
	  var sources = util.getArg(sourceMap, 'sources');
	  // Sass 3.3 leaves out the 'names' array, so we deviate from the spec (which
	  // requires the array) to play nice here.
	  var names = util.getArg(sourceMap, 'names', []);
	  var sourceRoot = util.getArg(sourceMap, 'sourceRoot', null);
	  var sourcesContent = util.getArg(sourceMap, 'sourcesContent', null);
	  var mappings = util.getArg(sourceMap, 'mappings');
	  var file = util.getArg(sourceMap, 'file', 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 != this._version) {
	    throw new Error('Unsupported version: ' + version);
	  }

	  if (sourceRoot) {
	    sourceRoot = util.normalize(sourceRoot);
	  }

	  sources = sources
	    .map(String)
	    // Some source maps produce relative source paths like "./foo.js" instead of
	    // "foo.js".  Normalize these first so that future comparisons will succeed.
	    // See bugzil.la/1090768.
	    .map(util.normalize)
	    // Always ensure that absolute sources are internally stored relative to
	    // the source root, if the source root is absolute. Not doing this would
	    // be particularly problematic when the source root is a prefix of the
	    // source (valid, but why??). See github issue #199 and bugzil.la/1188982.
	    .map(function (source) {
	      return sourceRoot && util.isAbsolute(sourceRoot) && util.isAbsolute(source)
	        ? util.relative(sourceRoot, source)
	        : source;
	    });

	  // 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.
	  this._names = ArraySet.fromArray(names.map(String), true);
	  this._sources = ArraySet.fromArray(sources, true);

	  this._absoluteSources = this._sources.toArray().map(function (s) {
	    return util.computeSourceURL(sourceRoot, s, aSourceMapURL);
	  });

	  this.sourceRoot = sourceRoot;
	  this.sourcesContent = sourcesContent;
	  this._mappings = mappings;
	  this._sourceMapURL = aSourceMapURL;
	  this.file = file;
	}

	BasicSourceMapConsumer.prototype = Object.create(SourceMapConsumer.prototype);
	BasicSourceMapConsumer.prototype.consumer = SourceMapConsumer;

	/**
	 * Utility function to find the index of a source.  Returns -1 if not
	 * found.
	 */
	BasicSourceMapConsumer.prototype._findSourceIndex = function(aSource) {
	  var relativeSource = aSource;
	  if (this.sourceRoot != null) {
	    relativeSource = util.relative(this.sourceRoot, relativeSource);
	  }

	  if (this._sources.has(relativeSource)) {
	    return this._sources.indexOf(relativeSource);
	  }

	  // Maybe aSource is an absolute URL as returned by |sources|.  In
	  // this case we can't simply undo the transform.
	  var i;
	  for (i = 0; i < this._absoluteSources.length; ++i) {
	    if (this._absoluteSources[i] == aSource) {
	      return i;
	    }
	  }

	  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
	 */
	BasicSourceMapConsumer.fromSourceMap =
	  function SourceMapConsumer_fromSourceMap(aSourceMap, aSourceMapURL) {
	    var smc = Object.create(BasicSourceMapConsumer.prototype);

	    var names = smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true);
	    var sources = smc._sources = ArraySet.fromArray(aSourceMap._sources.toArray(), true);
	    smc.sourceRoot = aSourceMap._sourceRoot;
	    smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(),
	                                                            smc.sourceRoot);
	    smc.file = aSourceMap._file;
	    smc._sourceMapURL = aSourceMapURL;
	    smc._absoluteSources = smc._sources.toArray().map(function (s) {
	      return util.computeSourceURL(smc.sourceRoot, s, aSourceMapURL);
	    });

	    // Because we are modifying the entries (by converting string sources and
	    // names to indices into the sources and names ArraySets), we have to make
	    // a copy of the entry or else bad things happen. Shared mutable state
	    // strikes again! See github issue #191.

	    var generatedMappings = aSourceMap._mappings.toArray().slice();
	    var destGeneratedMappings = smc.__generatedMappings = [];
	    var destOriginalMappings = smc.__originalMappings = [];

	    for (var i = 0, length = generatedMappings.length; i < length; i++) {
	      var srcMapping = generatedMappings[i];
	      var destMapping = new Mapping;
	      destMapping.generatedLine = srcMapping.generatedLine;
	      destMapping.generatedColumn = srcMapping.generatedColumn;

	      if (srcMapping.source) {
	        destMapping.source = sources.indexOf(srcMapping.source);
	        destMapping.originalLine = srcMapping.originalLine;
	        destMapping.originalColumn = srcMapping.originalColumn;

	        if (srcMapping.name) {
	          destMapping.name = names.indexOf(srcMapping.name);
	        }

	        destOriginalMappings.push(destMapping);
	      }

	      destGeneratedMappings.push(destMapping);
	    }

	    quickSort(smc.__originalMappings, util.compareByOriginalPositions);

	    return smc;
	  };

	/**
	 * The version of the source mapping spec that we are consuming.
	 */
	BasicSourceMapConsumer.prototype._version = 3;

	/**
	 * The list of original sources.
	 */
	Object.defineProperty(BasicSourceMapConsumer.prototype, 'sources', {
	  get: function () {
	    return this._absoluteSources.slice();
	  }
	});

	/**
	 * Provide the JIT with a nice shape / hidden class.
	 */
	function Mapping() {
	  this.generatedLine = 0;
	  this.generatedColumn = 0;
	  this.source = null;
	  this.originalLine = null;
	  this.originalColumn = null;
	  this.name = null;
	}

	/**
	 * 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).
	 */
	BasicSourceMapConsumer.prototype._parseMappings =
	  function SourceMapConsumer_parseMappings(aStr, aSourceRoot) {
	    var generatedLine = 1;
	    var previousGeneratedColumn = 0;
	    var previousOriginalLine = 0;
	    var previousOriginalColumn = 0;
	    var previousSource = 0;
	    var previousName = 0;
	    var length = aStr.length;
	    var index = 0;
	    var cachedSegments = {};
	    var temp = {};
	    var originalMappings = [];
	    var generatedMappings = [];
	    var mapping, str, segment, end, value;

	    while (index < length) {
	      if (aStr.charAt(index) === ';') {
	        generatedLine++;
	        index++;
	        previousGeneratedColumn = 0;
	      }
	      else if (aStr.charAt(index) === ',') {
	        index++;
	      }
	      else {
	        mapping = new Mapping();
	        mapping.generatedLine = generatedLine;

	        // Because each offset is encoded relative to the previous one,
	        // many segments often have the same encoding. We can exploit this
	        // fact by caching the parsed variable length fields of each segment,
	        // allowing us to avoid a second parse if we encounter the same
	        // segment again.
	        for (end = index; end < length; end++) {
	          if (this._charIsMappingSeparator(aStr, end)) {
	            break;
	          }
	        }
	        str = aStr.slice(index, end);

	        segment = cachedSegments[str];
	        if (segment) {
	          index += str.length;
	        } else {
	          segment = [];
	          while (index < end) {
	            base64VLQ.decode(aStr, index, temp);
	            value = temp.value;
	            index = temp.rest;
	            segment.push(value);
	          }

	          if (segment.length === 2) {
	            throw new Error('Found a source, but no line and column');
	          }

	          if (segment.length === 3) {
	            throw new Error('Found a source and line, but no column');
	          }

	          cachedSegments[str] = segment;
	        }

	        // Generated column.
	        mapping.generatedColumn = previousGeneratedColumn + segment[0];
	        previousGeneratedColumn = mapping.generatedColumn;

	        if (segment.length > 1) {
	          // Original source.
	          mapping.source = previousSource + segment[1];
	          previousSource += segment[1];

	          // Original line.
	          mapping.originalLine = previousOriginalLine + segment[2];
	          previousOriginalLine = mapping.originalLine;
	          // Lines are stored 0-based
	          mapping.originalLine += 1;

	          // Original column.
	          mapping.originalColumn = previousOriginalColumn + segment[3];
	          previousOriginalColumn = mapping.originalColumn;

	          if (segment.length > 4) {
	            // Original name.
	            mapping.name = previousName + segment[4];
	            previousName += segment[4];
	          }
	        }

	        generatedMappings.push(mapping);
	        if (typeof mapping.originalLine === 'number') {
	          originalMappings.push(mapping);
	        }
	      }
	    }

	    quickSort(generatedMappings, util.compareByGeneratedPositionsDeflated);
	    this.__generatedMappings = generatedMappings;

	    quickSort(originalMappings, util.compareByOriginalPositions);
	    this.__originalMappings = originalMappings;
	  };

	/**
	 * Find the mapping that best matches the hypothetical "needle" mapping that
	 * we are searching for in the given "haystack" of mappings.
	 */
	BasicSourceMapConsumer.prototype._findMapping =
	  function SourceMapConsumer_findMapping(aNeedle, aMappings, aLineName,
	                                         aColumnName, aComparator, aBias) {
	    // To return the position we are searching for, we must first find the
	    // mapping for the given position and then return the opposite position it
	    // points to. Because the mappings are sorted, we can use binary search to
	    // find the best mapping.

	    if (aNeedle[aLineName] <= 0) {
	      throw new TypeError('Line must be greater than or equal to 1, got '
	                          + aNeedle[aLineName]);
	    }
	    if (aNeedle[aColumnName] < 0) {
	      throw new TypeError('Column must be greater than or equal to 0, got '
	                          + aNeedle[aColumnName]);
	    }

	    return binarySearch.search(aNeedle, aMappings, aComparator, aBias);
	  };

	/**
	 * Compute the last column for each generated mapping. The last column is
	 * inclusive.
	 */
	BasicSourceMapConsumer.prototype.computeColumnSpans =
	  function SourceMapConsumer_computeColumnSpans() {
	    for (var index = 0; index < this._generatedMappings.length; ++index) {
	      var mapping = this._generatedMappings[index];

	      // Mappings do not contain a field for the last generated columnt. We
	      // can come up with an optimistic estimate, however, by assuming that
	      // mappings are contiguous (i.e. given two consecutive mappings, the
	      // first mapping ends where the second one starts).
	      if (index + 1 < this._generatedMappings.length) {
	        var nextMapping = this._generatedMappings[index + 1];

	        if (mapping.generatedLine === nextMapping.generatedLine) {
	          mapping.lastGeneratedColumn = nextMapping.generatedColumn - 1;
	          continue;
	        }
	      }

	      // The last mapping for each line spans the entire line.
	      mapping.lastGeneratedColumn = Infinity;
	    }
	  };

	/**
	 * 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.
	 */
	BasicSourceMapConsumer.prototype.originalPositionFor =
	  function SourceMapConsumer_originalPositionFor(aArgs) {
	    var needle = {
	      generatedLine: util.getArg(aArgs, 'line'),
	      generatedColumn: util.getArg(aArgs, 'column')
	    };

	    var index = this._findMapping(
	      needle,
	      this._generatedMappings,
	      "generatedLine",
	      "generatedColumn",
	      util.compareByGeneratedPositionsDeflated,
	      util.getArg(aArgs, 'bias', SourceMapConsumer.GREATEST_LOWER_BOUND)
	    );

	    if (index >= 0) {
	      var mapping = this._generatedMappings[index];

	      if (mapping.generatedLine === needle.generatedLine) {
	        var source = util.getArg(mapping, 'source', null);
	        if (source !== null) {
	          source = this._sources.at(source);
	          source = util.computeSourceURL(this.sourceRoot, source, this._sourceMapURL);
	        }
	        var name = util.getArg(mapping, 'name', null);
	        if (name !== null) {
	          name = this._names.at(name);
	        }
	        return {
	          source: source,
	          line: util.getArg(mapping, 'originalLine', null),
	          column: util.getArg(mapping, 'originalColumn', null),
	          name: 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.
	 */
	BasicSourceMapConsumer.prototype.hasContentsOfAllSources =
	  function BasicSourceMapConsumer_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.
	 */
	BasicSourceMapConsumer.prototype.sourceContentFor =
	  function SourceMapConsumer_sourceContentFor(aSource, nullOnMissing) {
	    if (!this.sourcesContent) {
	      return null;
	    }

	    var index = this._findSourceIndex(aSource);
	    if (index >= 0) {
	      return this.sourcesContent[index];
	    }

	    var relativeSource = aSource;
	    if (this.sourceRoot != null) {
	      relativeSource = util.relative(this.sourceRoot, relativeSource);
	    }

	    var url;
	    if (this.sourceRoot != null
	        && (url = util.urlParse(this.sourceRoot))) {
	      // XXX: file:// URIs and absolute paths lead to unexpected behavior for
	      // many users. We can help them out when they expect file:// URIs to
	      // behave like it would if they were running a local HTTP server. See
	      // https://bugzilla.mozilla.org/show_bug.cgi?id=885597.
	      var fileUriAbsPath = relativeSource.replace(/^file:\/\//, "");
	      if (url.scheme == "file"
	          && this._sources.has(fileUriAbsPath)) {
	        return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)]
	      }

	      if ((!url.path || url.path == "/")
	          && this._sources.has("/" + relativeSource)) {
	        return this.sourcesContent[this._sources.indexOf("/" + relativeSource)];
	      }
	    }

	    // 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;
	    }
	    else {
	      throw new Error('"' + relativeSource + '" 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.
	 */
	BasicSourceMapConsumer.prototype.generatedPositionFor =
	  function SourceMapConsumer_generatedPositionFor(aArgs) {
	    var source = util.getArg(aArgs, 'source');
	    source = this._findSourceIndex(source);
	    if (source < 0) {
	      return {
	        line: null,
	        column: null,
	        lastColumn: null
	      };
	    }

	    var needle = {
	      source: source,
	      originalLine: util.getArg(aArgs, 'line'),
	      originalColumn: util.getArg(aArgs, 'column')
	    };

	    var index = this._findMapping(
	      needle,
	      this._originalMappings,
	      "originalLine",
	      "originalColumn",
	      util.compareByOriginalPositions,
	      util.getArg(aArgs, 'bias', SourceMapConsumer.GREATEST_LOWER_BOUND)
	    );

	    if (index >= 0) {
	      var mapping = this._originalMappings[index];

	      if (mapping.source === needle.source) {
	        return {
	          line: util.getArg(mapping, 'generatedLine', null),
	          column: util.getArg(mapping, 'generatedColumn', null),
	          lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null)
	        };
	      }
	    }

	    return {
	      line: null,
	      column: null,
	      lastColumn: null
	    };
	  };

	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
	 */
	function IndexedSourceMapConsumer(aSourceMap, aSourceMapURL) {
	  var sourceMap = aSourceMap;
	  if (typeof aSourceMap === 'string') {
	    sourceMap = util.parseSourceMapInput(aSourceMap);
	  }

	  var version = util.getArg(sourceMap, 'version');
	  var sections = util.getArg(sourceMap, 'sections');

	  if (version != this._version) {
	    throw new Error('Unsupported version: ' + version);
	  }

	  this._sources = new ArraySet();
	  this._names = new ArraySet();

	  var lastOffset = {
	    line: -1,
	    column: 0
	  };
	  this._sections = sections.map(function (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.');
	    }
	    var offset = util.getArg(s, 'offset');
	    var offsetLine = util.getArg(offset, 'line');
	    var 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;

	    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: new SourceMapConsumer(util.getArg(s, 'map'), aSourceMapURL)
	    }
	  });
	}

	IndexedSourceMapConsumer.prototype = Object.create(SourceMapConsumer.prototype);
	IndexedSourceMapConsumer.prototype.constructor = SourceMapConsumer;

	/**
	 * The version of the source mapping spec that we are consuming.
	 */
	IndexedSourceMapConsumer.prototype._version = 3;

	/**
	 * The list of original sources.
	 */
	Object.defineProperty(IndexedSourceMapConsumer.prototype, 'sources', {
	  get: function () {
	    var sources = [];
	    for (var i = 0; i < this._sections.length; i++) {
	      for (var 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.
	 */
	IndexedSourceMapConsumer.prototype.originalPositionFor =
	  function IndexedSourceMapConsumer_originalPositionFor(aArgs) {
	    var 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.
	    var sectionIndex = binarySearch.search(needle, this._sections,
	      function(needle, section) {
	        var cmp = needle.generatedLine - section.generatedOffset.generatedLine;
	        if (cmp) {
	          return cmp;
	        }

	        return (needle.generatedColumn -
	                section.generatedOffset.generatedColumn);
	      });
	    var 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.
	 */
	IndexedSourceMapConsumer.prototype.hasContentsOfAllSources =
	  function IndexedSourceMapConsumer_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.
	 */
	IndexedSourceMapConsumer.prototype.sourceContentFor =
	  function IndexedSourceMapConsumer_sourceContentFor(aSource, nullOnMissing) {
	    for (var i = 0; i < this._sections.length; i++) {
	      var section = this._sections[i];

	      var content = section.consumer.sourceContentFor(aSource, true);
	      if (content) {
	        return content;
	      }
	    }
	    if (nullOnMissing) {
	      return null;
	    }
	    else {
	      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.
	 *
	 * 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.
	 */
	IndexedSourceMapConsumer.prototype.generatedPositionFor =
	  function IndexedSourceMapConsumer_generatedPositionFor(aArgs) {
	    for (var i = 0; i < this._sections.length; i++) {
	      var section = this._sections[i];

	      // Only consider this section if the requested source is in the list of
	      // sources of the consumer.
	      if (section.consumer._findSourceIndex(util.getArg(aArgs, 'source')) === -1) {
	        continue;
	      }
	      var generatedPosition = section.consumer.generatedPositionFor(aArgs);
	      if (generatedPosition) {
	        var ret = {
	          line: generatedPosition.line +
	            (section.generatedOffset.generatedLine - 1),
	          column: generatedPosition.column +
	            (section.generatedOffset.generatedLine === generatedPosition.line
	             ? section.generatedOffset.generatedColumn - 1
	             : 0)
	        };
	        return ret;
	      }
	    }

	    return {
	      line: null,
	      column: null
	    };
	  };

	/**
	 * 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).
	 */
	IndexedSourceMapConsumer.prototype._parseMappings =
	  function IndexedSourceMapConsumer_parseMappings(aStr, aSourceRoot) {
	    this.__generatedMappings = [];
	    this.__originalMappings = [];
	    for (var i = 0; i < this._sections.length; i++) {
	      var section = this._sections[i];
	      var sectionMappings = section.consumer._generatedMappings;
	      for (var j = 0; j < sectionMappings.length; j++) {
	        var mapping = sectionMappings[j];

	        var source = section.consumer._sources.at(mapping.source);
	        source = util.computeSourceURL(section.consumer.sourceRoot, source, this._sourceMapURL);
	        this._sources.add(source);
	        source = this._sources.indexOf(source);

	        var name = null;
	        if (mapping.name) {
	          name = section.consumer._names.at(mapping.name);
	          this._names.add(name);
	          name = this._names.indexOf(name);
	        }

	        // The mappings coming from the consumer for the section have
	        // generated positions relative to the start of the section, so we
	        // need to offset them to be relative to the start of the concatenated
	        // generated file.
	        var adjustedMapping = {
	          source: source,
	          generatedLine: mapping.generatedLine +
	            (section.generatedOffset.generatedLine - 1),
	          generatedColumn: mapping.generatedColumn +
	            (section.generatedOffset.generatedLine === mapping.generatedLine
	            ? section.generatedOffset.generatedColumn - 1
	            : 0),
	          originalLine: mapping.originalLine,
	          originalColumn: mapping.originalColumn,
	          name: name
	        };

	        this.__generatedMappings.push(adjustedMapping);
	        if (typeof adjustedMapping.originalLine === 'number') {
	          this.__originalMappings.push(adjustedMapping);
	        }
	      }
	    }

	    quickSort(this.__generatedMappings, util.compareByGeneratedPositionsDeflated);
	    quickSort(this.__originalMappings, util.compareByOriginalPositions);
	  };

	exports.IndexedSourceMapConsumer = IndexedSourceMapConsumer;


/***/ }),
/* 8 */
/***/ (function(module, exports) {

	/* -*- 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.
	  var mid = Math.floor((aHigh - aLow) / 2) + aLow;
	  var 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;
	    } else {
	      return mid;
	    }
	  }
	  else {
	    // 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;
	    } else {
	      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;
	  }

	  var 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 than
	  // 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;
	};


/***/ }),
/* 9 */
/***/ (function(module, exports) {

	/* -*- 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
	 */

	// It turns out that some (most?) JavaScript engines don't self-host
	// `Array.prototype.sort`. This makes sense because C++ will likely remain
	// faster than JS when doing raw CPU-intensive sorting. However, when using a
	// custom comparator function, calling back and forth between the VM's C++ and
	// JIT'd JS is rather slow *and* loses JIT type information, resulting in
	// worse generated code for the comparator function than would be optimal. In
	// fact, when sorting with a comparator, these costs outweigh the benefits of
	// sorting in C++. By using our own JS-implemented Quick Sort (below), we get
	// a ~3500ms mean speed-up in `bench/bench.html`.

	/**
	 * Swap the elements indexed by `x` and `y` in the array `ary`.
	 *
	 * @param {Array} ary
	 *        The array.
	 * @param {Number} x
	 *        The index of the first item.
	 * @param {Number} y
	 *        The index of the second item.
	 */
	function swap(ary, x, y) {
	  var temp = ary[x];
	  ary[x] = ary[y];
	  ary[y] = temp;
	}

	/**
	 * Returns a random integer within the range `low .. high` inclusive.
	 *
	 * @param {Number} low
	 *        The lower bound on the range.
	 * @param {Number} high
	 *        The upper bound on the range.
	 */
	function randomIntInRange(low, high) {
	  return Math.round(low + (Math.random() * (high - low)));
	}

	/**
	 * The Quick Sort algorithm.
	 *
	 * @param {Array} ary
	 *        An array to sort.
	 * @param {function} comparator
	 *        Function to use to compare two items.
	 * @param {Number} p
	 *        Start index of the array
	 * @param {Number} r
	 *        End index of the array
	 */
	function doQuickSort(ary, comparator, p, r) {
	  // If our lower bound is less than our upper bound, we (1) partition the
	  // array into two pieces and (2) recurse on each half. If it is not, this is
	  // the empty array and our base case.

	  if (p < r) {
	    // (1) Partitioning.
	    //
	    // The partitioning chooses a pivot between `p` and `r` and moves all
	    // elements that are less than or equal to the pivot to the before it, and
	    // all the elements that are greater than it after it. The effect is that
	    // once partition is done, the pivot is in the exact place it will be when
	    // the array is put in sorted order, and it will not need to be moved
	    // again. This runs in O(n) time.

	    // Always choose a random pivot so that an input array which is reverse
	    // sorted does not cause O(n^2) running time.
	    var pivotIndex = randomIntInRange(p, r);
	    var i = p - 1;

	    swap(ary, pivotIndex, r);
	    var pivot = ary[r];

	    // Immediately after `j` is incremented in this loop, the following hold
	    // true:
	    //
	    //   * Every element in `ary[p .. i]` is less than or equal to the pivot.
	    //
	    //   * Every element in `ary[i+1 .. j-1]` is greater than the pivot.
	    for (var j = p; j < r; j++) {
	      if (comparator(ary[j], pivot) <= 0) {
	        i += 1;
	        swap(ary, i, j);
	      }
	    }

	    swap(ary, i + 1, j);
	    var q = i + 1;

	    // (2) Recurse on each half.

	    doQuickSort(ary, comparator, p, q - 1);
	    doQuickSort(ary, comparator, q + 1, r);
	  }
	}

	/**
	 * Sort the given array in-place with the given comparator function.
	 *
	 * @param {Array} ary
	 *        An array to sort.
	 * @param {function} comparator
	 *        Function to use to compare two items.
	 */
	exports.quickSort = function (ary, comparator) {
	  doQuickSort(ary, comparator, 0, ary.length - 1);
	};


/***/ }),
/* 10 */
/***/ (function(module, exports, __webpack_require__) {

	/* -*- 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
	 */

	var SourceMapGenerator = __webpack_require__(1).SourceMapGenerator;
	var util = __webpack_require__(4);

	// Matches a Windows-style `\r\n` newline or a `\n` newline used by all other
	// operating systems these days (capturing the result).
	var REGEX_NEWLINE = /(\r?\n)/;

	// Newline character code for charCodeAt() comparisons
	var 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!
	var 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.
	 */
	function SourceNode(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.
	 */
	SourceNode.fromStringWithSourceMap =
	  function SourceNode_fromStringWithSourceMap(aGeneratedCode, aSourceMapConsumer, aRelativePath) {
	    // The SourceNode we want to fill with the generated code
	    // and the SourceMap
	    var 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`.
	    var remainingLines = aGeneratedCode.split(REGEX_NEWLINE);
	    var remainingLinesIndex = 0;
	    var shiftNextLine = function() {
	      var lineContents = getNextLine();
	      // The last line of a file might not have a newline.
	      var newLine = getNextLine() || "";
	      return lineContents + newLine;

	      function getNextLine() {
	        return remainingLinesIndex < remainingLines.length ?
	            remainingLines[remainingLinesIndex++] : undefined;
	      }
	    };

	    // We need to remember the position of "remainingLines"
	    var 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.
	    var lastMapping = null;

	    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"
	          var nextLine = remainingLines[remainingLinesIndex] || '';
	          var 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) {
	        var 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) {
	      var 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 {
	        var 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.
	 */
	SourceNode.prototype.add = function SourceNode_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.
	 */
	SourceNode.prototype.prepend = function SourceNode_prepend(aChunk) {
	  if (Array.isArray(aChunk)) {
	    for (var 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.
	 */
	SourceNode.prototype.walk = function SourceNode_walk(aFn) {
	  var chunk;
	  for (var 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.
	 */
	SourceNode.prototype.join = function SourceNode_join(aSep) {
	  var newChildren;
	  var i;
	  var 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.
	 */
	SourceNode.prototype.replaceRight = function SourceNode_replaceRight(aPattern, aReplacement) {
	  var 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
	 */
	SourceNode.prototype.setSourceContent =
	  function SourceNode_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.
	 */
	SourceNode.prototype.walkSourceContents =
	  function SourceNode_walkSourceContents(aFn) {
	    for (var i = 0, len = this.children.length; i < len; i++) {
	      if (this.children[i][isSourceNode]) {
	        this.children[i].walkSourceContents(aFn);
	      }
	    }

	    var sources = Object.keys(this.sourceContents);
	    for (var 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.
	 */
	SourceNode.prototype.toString = function SourceNode_toString() {
	  var str = "";
	  this.walk(function (chunk) {
	    str += chunk;
	  });
	  return str;
	};

	/**
	 * Returns the string representation of this source node along with a source
	 * map.
	 */
	SourceNode.prototype.toStringWithSourceMap = function SourceNode_toStringWithSourceMap(aArgs) {
	  var generated = {
	    code: "",
	    line: 1,
	    column: 0
	  };
	  var map = new SourceMapGenerator(aArgs);
	  var sourceMappingActive = false;
	  var lastOriginalSource = null;
	  var lastOriginalLine = null;
	  var lastOriginalColumn = null;
	  var 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 (var 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: map };
	};

	exports.SourceNode = SourceNode;


/***/ })
/******/ ])
});
;PK
!<�rRL==>chrome/devtools/modules/devtools/shared/specs/accessibility.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 protocol = require("devtools/shared/protocol");
const { Arg, generateActorSpec, RetVal, types } = protocol;

types.addActorType("accessible");

/**
 * Accessible with children listed in the ancestry structure calculated by the
 * walker.
 */
types.addDictType("accessibleWithChildren", {
  // Accessible
  accessible: "accessible",
  // Accessible's children
  children: "array:accessible"
});

const accessibleSpec = generateActorSpec({
  typeName: "accessible",

  events: {
    "actions-change": {
      type: "actionsChange",
      actions: Arg(0, "array:string")
    },
    "name-change": {
      type: "nameChange",
      name: Arg(0, "string"),
      parent: Arg(1, "nullable:accessible"),
      walker: Arg(2, "nullable:accessiblewalker")
    },
    "value-change": {
      type: "valueChange",
      value: Arg(0, "string")
    },
    "description-change": {
      type: "descriptionChange",
      description: Arg(0, "string")
    },
    "states-change": {
      type: "statesChange",
      states: Arg(0, "array:string")
    },
    "attributes-change": {
      type: "attributesChange",
      attributes: Arg(0, "json")
    },
    "help-change": {
      type: "helpChange",
      help: Arg(0, "string")
    },
    "shortcut-change": {
      type: "shortcutChange",
      shortcut: Arg(0, "string")
    },
    "reorder": {
      type: "reorder",
      childCount: Arg(0, "number"),
      walker: Arg(1, "nullable:accessiblewalker")
    },
    "text-change": {
      type: "textChange",
      walker: Arg(0, "nullable:accessiblewalker")
    },
    "index-in-parent-change": {
      type: "indexInParentChange",
      indexInParent: Arg(0, "number")
    }
  },

  methods: {
    children: {
      request: {},
      response: {
        children: RetVal("array:accessible")
      }
    }
  }
});

const accessibleWalkerSpec = generateActorSpec({
  typeName: "accessiblewalker",

  events: {
    "accessible-destroy": {
      type: "accessibleDestroy",
      accessible: Arg(0, "accessible")
    },
    "document-ready": {
      type: "documentReady",
    },
    "picker-accessible-picked": {
      type: "pickerAccessiblePicked",
      accessible: Arg(0, "nullable:accessible")
    },
    "picker-accessible-previewed": {
      type: "pickerAccessiblePreviewed",
      accessible: Arg(0, "nullable:accessible")
    },
    "picker-accessible-hovered": {
      type: "pickerAccessibleHovered",
      accessible: Arg(0, "nullable:accessible")
    },
    "picker-accessible-canceled": {
      type: "pickerAccessibleCanceled"
    }
  },

  methods: {
    children: {
      request: {},
      response: {
        children: RetVal("array:accessible")
      }
    },
    getAccessibleFor: {
      request: { node: Arg(0, "domnode") },
      response: {
        accessible: RetVal("accessible")
      }
    },
    getAncestry: {
      request: { accessible: Arg(0, "accessible") },
      response: {
        ancestry: RetVal("array:accessibleWithChildren")
      }
    },
    highlightAccessible: {
      request: {
        accessible: Arg(0, "accessible"),
        options: Arg(1, "nullable:json")
      },
      response: {
        value: RetVal("nullable:boolean")
      }
    },
    unhighlight: {
      request: {}
    },
    pick: {},
    pickAndFocus: {},
    cancelPick: {}
  }
});

const accessibilitySpec = generateActorSpec({
  typeName: "accessibility",

  events: {
    "init": {
      type: "init"
    },
    "shutdown": {
      type: "shutdown"
    },
    "can-be-disabled-change": {
      type: "canBeDisabledChange",
      canBeDisabled: Arg(0, "boolean")
    },
    "can-be-enabled-change": {
      type: "canBeEnabledChange",
      canBeEnabled: Arg(0, "boolean")
    }
  },

  methods: {
    bootstrap: {
      request: {},
      response: {
        state: RetVal("json")
      }
    },
    getWalker: {
      request: {},
      response: {
        walker: RetVal("accessiblewalker")
      }
    },
    enable: {
      request: {},
      response: {}
    },
    disable: {
      request: {},
      response: {}
    }
  }
});

exports.accessibleSpec = accessibleSpec;
exports.accessibleWalkerSpec = accessibleWalkerSpec;
exports.accessibilitySpec = accessibilitySpec;
PK
!<]2�mm?chrome/devtools/modules/devtools/shared/specs/actor-registry.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  Arg,
  RetVal,
  generateActorSpec,
} = require("devtools/shared/protocol");

const actorActorSpec = generateActorSpec({
  typeName: "actorActor",

  methods: {
    unregister: {
      request: {},
      response: {}
    }
  },
});

exports.actorActorSpec = actorActorSpec;

const actorRegistrySpec = generateActorSpec({
  typeName: "actorRegistry",

  methods: {
    registerActor: {
      request: {
        sourceText: Arg(0, "string"),
        filename: Arg(1, "string"),
        options: Arg(2, "json")
      },

      response: {
        actorActor: RetVal("actorActor")
      }
    }
  }
});

exports.actorRegistrySpec = actorRegistrySpec;
PK
!<f��J  7chrome/devtools/modules/devtools/shared/specs/addons.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {Arg, RetVal, generateActorSpec} = require("devtools/shared/protocol");

const addonsSpec = generateActorSpec({
  typeName: "addons",

  methods: {
    installTemporaryAddon: {
      request: { addonPath: Arg(0, "string") },
      response: { addon: RetVal("json") },
    },
  },
});

exports.addonsSpec = addonsSpec;
PK
!<�'�..:chrome/devtools/modules/devtools/shared/specs/animation.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  Arg,
  RetVal,
  generateActorSpec,
  types
} = require("devtools/shared/protocol");

/**
 * Sent with the 'mutations' event as part of an array of changes, used to
 * inform fronts of the type of change that occured.
 */
types.addDictType("animationMutationChange", {
  // The type of change ("added" or "removed").
  type: "string",
  // The changed AnimationPlayerActor.
  player: "animationplayer"
});

const animationPlayerSpec = generateActorSpec({
  typeName: "animationplayer",

  events: {
    "changed": {
      type: "changed",
      state: Arg(0, "json")
    }
  },

  methods: {
    release: { release: true },
    getCurrentState: {
      request: {},
      response: {
        data: RetVal("json")
      }
    },
    pause: {
      request: {},
      response: {}
    },
    play: {
      request: {},
      response: {}
    },
    ready: {
      request: {},
      response: {}
    },
    setCurrentTime: {
      request: {
        currentTime: Arg(0, "number")
      },
      response: {}
    },
    setPlaybackRate: {
      request: {
        currentTime: Arg(0, "number")
      },
      response: {}
    },
    getFrames: {
      request: {},
      response: {
        frames: RetVal("json")
      }
    },
    getProperties: {
      request: {},
      response: {
        properties: RetVal("array:json")
      }
    },
    getAnimationTypes: {
      request: {
        propertyNames: Arg(0, "array:string")
      },
      response: {
        animationTypes: RetVal("json")
      }
    }
  }
});

exports.animationPlayerSpec = animationPlayerSpec;

const animationsSpec = generateActorSpec({
  typeName: "animations",

  events: {
    "mutations": {
      type: "mutations",
      changes: Arg(0, "array:animationMutationChange")
    }
  },

  methods: {
    setWalkerActor: {
      request: {
        walker: Arg(0, "domwalker")
      },
      response: {}
    },
    getAnimationPlayersForNode: {
      request: {
        actorID: Arg(0, "domnode")
      },
      response: {
        players: RetVal("array:animationplayer")
      }
    },
    stopAnimationPlayerUpdates: {
      request: {},
      response: {}
    },
    pauseAll: {
      request: {},
      response: {}
    },
    playAll: {
      request: {},
      response: {}
    },
    toggleAll: {
      request: {},
      response: {}
    },
    toggleSeveral: {
      request: {
        players: Arg(0, "array:animationplayer"),
        shouldPause: Arg(1, "boolean")
      },
      response: {}
    },
    setCurrentTimes: {
      request: {
        players: Arg(0, "array:animationplayer"),
        time: Arg(1, "number"),
        shouldPause: Arg(2, "boolean")
      },
      response: {}
    },
    setPlaybackRates: {
      request: {
        players: Arg(0, "array:animationplayer"),
        rate: Arg(1, "number")
      },
      response: {}
    }
  }
});

exports.animationsSpec = animationsSpec;
PK
!<�����;chrome/devtools/modules/devtools/shared/specs/breakpoint.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {generateActorSpec} = require("devtools/shared/protocol");

const breakpointSpec = generateActorSpec({
  typeName: "breakpoint",

  methods: {
    delete: {}
  },
});

exports.breakpointSpec = breakpointSpec;
PK
!<O�755=chrome/devtools/modules/devtools/shared/specs/call-watcher.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 protocol = require("devtools/shared/protocol");
const { Arg, RetVal, Option, generateActorSpec } = protocol;

/**
 * Type describing a single function call in a stack trace.
 */
protocol.types.addDictType("call-stack-item", {
  name: "string",
  file: "string",
  line: "number"
});

/**
 * Type describing an overview of a function call.
 */
protocol.types.addDictType("call-details", {
  type: "number",
  name: "string",
  stack: "array:call-stack-item"
});

const functionCallSpec = generateActorSpec({
  typeName: "function-call",

  methods: {
    getDetails: {
      response: { info: RetVal("call-details") }
    },
  },
});

exports.functionCallSpec = functionCallSpec;

const callWatcherSpec = generateActorSpec({
  typeName: "call-watcher",

  events: {
    /**
     * Events emitted when the `onCall` function isn't provided.
     */
    "call": {
      type: "call",
      function: Arg(0, "function-call")
    }
  },

  methods: {
    setup: {
      request: {
        tracedGlobals: Option(0, "nullable:array:string"),
        tracedFunctions: Option(0, "nullable:array:string"),
        startRecording: Option(0, "boolean"),
        performReload: Option(0, "boolean"),
        holdWeak: Option(0, "boolean"),
        storeCalls: Option(0, "boolean")
      },
      oneway: true
    },
    finalize: {
      oneway: true
    },
    isRecording: {
      response: RetVal("boolean")
    },
    initTimestampEpoch: {},
    resumeRecording: {},
    pauseRecording: {
      response: { calls: RetVal("array:function-call") }
    },
    eraseRecording: {},
  }
});

exports.callWatcherSpec = callWatcherSpec;
PK
!<X�����7chrome/devtools/modules/devtools/shared/specs/canvas.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 protocol = require("devtools/shared/protocol");
const {Arg, Option, RetVal, generateActorSpec} = protocol;

/**
 * Type representing an ArrayBufferView, serialized fast(er).
 *
 * Don't create a new array buffer view from the parsed array on the frontend.
 * Consumers may copy the data into an existing buffer, or create a new one if
 * necesasry. For example, this avoids the need for a redundant copy when
 * populating ImageData objects, at the expense of transferring char views
 * of a pixel buffer over the protocol instead of a packed int view.
 *
 * XXX: It would be nice if on local connections (only), we could just *give*
 * the buffer directly to the front, instead of going through all this
 * serialization redundancy.
 */
protocol.types.addType("array-buffer-view", {
  write: (v) => "[" + Array.join(v, ",") + "]",
  read: (v) => JSON.parse(v)
});

/**
 * Type describing a thumbnail or screenshot in a recorded animation frame.
 */
protocol.types.addDictType("snapshot-image", {
  index: "number",
  width: "number",
  height: "number",
  scaling: "number",
  flipped: "boolean",
  pixels: "array-buffer-view"
});

/**
 * Type describing an overview of a recorded animation frame.
 */
protocol.types.addDictType("snapshot-overview", {
  calls: "array:function-call",
  thumbnails: "array:snapshot-image",
  screenshot: "snapshot-image"
});

exports.CANVAS_CONTEXTS = [
  "CanvasRenderingContext2D",
  "WebGLRenderingContext"
];

exports.ANIMATION_GENERATORS = [
  "requestAnimationFrame"
];

exports.LOOP_GENERATORS = [
  "setTimeout"
];

exports.DRAW_CALLS = [
  // 2D canvas
  "fill",
  "stroke",
  "clearRect",
  "fillRect",
  "strokeRect",
  "fillText",
  "strokeText",
  "drawImage",

  // WebGL
  "clear",
  "drawArrays",
  "drawElements",
  "finish",
  "flush"
];

exports.INTERESTING_CALLS = [
  // 2D canvas
  "save",
  "restore",

  // WebGL
  "useProgram"
];

const frameSnapshotSpec = generateActorSpec({
  typeName: "frame-snapshot",

  methods: {
    getOverview: {
      response: { overview: RetVal("snapshot-overview") }
    },
    generateScreenshotFor: {
      request: { call: Arg(0, "function-call") },
      response: { screenshot: RetVal("snapshot-image") }
    },
  },
});

exports.frameSnapshotSpec = frameSnapshotSpec;

const canvasSpec = generateActorSpec({
  typeName: "canvas",

  methods: {
    setup: {
      request: { reload: Option(0, "boolean") },
      oneway: true
    },
    finalize: {
      oneway: true
    },
    isInitialized: {
      response: { initialized: RetVal("boolean") }
    },
    isRecording: {
      response: { recording: RetVal("boolean") }
    },
    recordAnimationFrame: {
      response: { snapshot: RetVal("nullable:frame-snapshot") }
    },
    stopRecordingAnimationFrame: {
      oneway: true
    },
  }
});

exports.canvasSpec = canvasSpec;
PK
!<�I1		?chrome/devtools/modules/devtools/shared/specs/css-properties.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { RetVal, generateActorSpec } = require("devtools/shared/protocol");

const cssPropertiesSpec = generateActorSpec({
  typeName: "cssProperties",

  methods: {
    getCSSDatabase: {
      request: {},

      response: RetVal("json"),
    }
  }
});

exports.cssPropertiesSpec = cssPropertiesSpec;
PK
!<r��
<chrome/devtools/modules/devtools/shared/specs/csscoverage.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {Arg, RetVal, generateActorSpec} = require("devtools/shared/protocol");

const cssUsageSpec = generateActorSpec({
  typeName: "cssUsage",

  events: {
    "state-change": {
      type: "stateChange",
      stateChange: Arg(0, "json")
    }
  },

  methods: {
    start: {
      request: { url: Arg(0, "boolean") }
    },
    stop: {},
    toggle: {},
    oneshot: {},
    createEditorReport: {
      request: { url: Arg(0, "string") },
      response: { reports: RetVal("array:json") }
    },
    createEditorReportForSheet: {
      request: { url: Arg(0, "stylesheet") },
      response: { reports: RetVal("array:json") }
    },
    createPageReport: {
      response: RetVal("json")
    },
    _testOnlyVisitedPages: {
      response: { value: RetVal("array:string") }
    },
  },
});

exports.cssUsageSpec = cssUsageSpec;
PK
!<���7chrome/devtools/modules/devtools/shared/specs/device.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {RetVal, generateActorSpec} = require("devtools/shared/protocol");

const deviceSpec = generateActorSpec({
  typeName: "device",

  methods: {
    getDescription: {request: {}, response: { value: RetVal("json")}},
    getWallpaper: {request: {}, response: { value: RetVal("longstring")}},
    screenshotToDataURL: {request: {}, response: { value: RetVal("longstring")}},
  },
});

exports.deviceSpec = deviceSpec;
PK
!<��c���:chrome/devtools/modules/devtools/shared/specs/emulation.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Arg, RetVal, generateActorSpec } = require("devtools/shared/protocol");

const emulationSpec = generateActorSpec({
  typeName: "emulation",

  methods: {
    setDPPXOverride: {
      request: {
        dppx: Arg(0, "number")
      },
      response: {
        valueChanged: RetVal("boolean")
      }
    },

    getDPPXOverride: {
      request: {},
      response: {
        dppx: RetVal("number")
      }
    },

    clearDPPXOverride: {
      request: {},
      response: {
        valueChanged: RetVal("boolean")
      }
    },

    setNetworkThrottling: {
      request: {
        options: Arg(0, "json")
      },
      response: {
        valueChanged: RetVal("boolean")
      }
    },

    getNetworkThrottling: {
      request: {},
      response: {
        state: RetVal("json")
      }
    },

    clearNetworkThrottling: {
      request: {},
      response: {
        valueChanged: RetVal("boolean")
      }
    },

    setTouchEventsOverride: {
      request: {
        flag: Arg(0, "number")
      },
      response: {
        valueChanged: RetVal("boolean")
      }
    },

    getTouchEventsOverride: {
      request: {},
      response: {
        flag: RetVal("number")
      }
    },

    clearTouchEventsOverride: {
      request: {},
      response: {
        valueChanged: RetVal("boolean")
      }
    },

    setUserAgentOverride: {
      request: {
        flag: Arg(0, "string")
      },
      response: {
        valueChanged: RetVal("boolean")
      }
    },

    getUserAgentOverride: {
      request: {},
      response: {
        userAgent: RetVal("string")
      }
    },

    clearUserAgentOverride: {
      request: {},
      response: {
        valueChanged: RetVal("boolean")
      }
    },
  }
});

exports.emulationSpec = emulationSpec;
PK
!<!p9�tt<chrome/devtools/modules/devtools/shared/specs/environment.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {Arg, RetVal, generateActorSpec} = require("devtools/shared/protocol");

const environmentSpec = generateActorSpec({
  typeName: "environment",

  methods: {
    assign: {
      request: {
        name: Arg(1),
        value: Arg(2)
      }
    },
    bindings: {
      request: {},
      response: {
        bindings: RetVal("json")
      }
    },
  },
});

exports.environmentSpec = environmentSpec;
PK
!<<����=chrome/devtools/modules/devtools/shared/specs/eventlooplag.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Arg, RetVal, generateActorSpec } = require("devtools/shared/protocol");

const eventLoopLagSpec = generateActorSpec({
  typeName: "eventLoopLag",

  events: {
    "event-loop-lag": {
      type: "event-loop-lag",
      // duration of the lag in milliseconds.
      time: Arg(0, "number")
    }
  },

  methods: {
    start: {
      request: {},
      response: {success: RetVal("number")}
    },
    stop: {
      request: {},
      response: {}
    }
  }
});

exports.eventLoopLagSpec = eventLoopLagSpec;
PK
!<�����6chrome/devtools/modules/devtools/shared/specs/frame.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {generateActorSpec, RetVal} = require("devtools/shared/protocol");

const frameSpec = generateActorSpec({
  typeName: "frame",

  methods: {
    getEnvironment: {
      response: RetVal("json")
    }
  },
});

exports.frameSpec = frameSpec;
PK
!<rr����:chrome/devtools/modules/devtools/shared/specs/framerate.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Arg, RetVal, generateActorSpec } = require("devtools/shared/protocol");

const framerateSpec = generateActorSpec({
  typeName: "framerate",

  methods: {
    startRecording: {},
    stopRecording: {
      request: {
        beginAt: Arg(0, "nullable:number"),
        endAt: Arg(1, "nullable:number")
      },
      response: { ticks: RetVal("array:number") }
    },
    cancelRecording: {},
    isRecording: {
      response: { recording: RetVal("boolean") }
    },
    getPendingTicks: {
      request: {
        beginAt: Arg(0, "nullable:number"),
        endAt: Arg(1, "nullable:number")
      },
      response: { ticks: RetVal("array:number") }
    }
  }
});

exports.framerateSpec = framerateSpec;
PK
!<�$��225chrome/devtools/modules/devtools/shared/specs/gcli.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Arg, RetVal, generateActorSpec } = require("devtools/shared/protocol");

const gcliSpec = generateActorSpec({
  typeName: "gcli",

  events: {
    "commands-changed": {
      type: "commandsChanged"
    }
  },

  methods: {
    _testOnlyAddItemsByModule: {
      request: {
        customProps: Arg(0, "array:string")
      }
    },
    _testOnlyRemoveItemsByModule: {
      request: {
        customProps: Arg(0, "array:string")
      }
    },
    specs: {
      request: {
        customProps: Arg(0, "nullable:array:string")
      },
      response: {
        value: RetVal("array:json")
      }
    },
    execute: {
      request: {
        // The command string
        typed: Arg(0, "string")
      },
      response: RetVal("json")
    },
    state: {
      request: {
        // The command string
        typed: Arg(0, "string"),
        // Cursor start position
        start: Arg(1, "number"),
        // The prediction offset (# times UP/DOWN pressed)
        rank: Arg(2, "number")
      },
      response: RetVal("json")
    },
    parseType: {
      request: {
        // The command string
        typed: Arg(0, "string"),
        // The name of the parameter to parse
        paramName: Arg(1, "string")
      },
      response: RetVal("json")
    },
    nudgeType: {
      request: {
        // The command string
        typed: Arg(0, "string"),
        // +1/-1 for increment / decrement
        by: Arg(1, "number"),
        // The name of the parameter to parse
        paramName: Arg(2, "string")
      },
      response: RetVal("string")
    },
    getSelectionLookup: {
      request: {
        // The command containing the parameter in question
        commandName: Arg(0, "string"),
        // The name of the parameter
        paramName: Arg(1, "string"),
      },
      response: RetVal("json")
    }
  }
});

exports.gcliSpec = gcliSpec;
PK
!<�҉w##Cchrome/devtools/modules/devtools/shared/specs/heap-snapshot-file.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Arg, generateActorSpec } = require("devtools/shared/protocol");

const heapSnapshotFileSpec = generateActorSpec({
  typeName: "heapSnapshotFile",

  methods: {
    transferHeapSnapshot: {
      request: {
        snapshotId: Arg(0, "string")
      }
    }
  },
});

exports.heapSnapshotFileSpec = heapSnapshotFileSpec;
PK
!<0��,\\=chrome/devtools/modules/devtools/shared/specs/highlighters.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  Arg,
  Option,
  RetVal,
  generateActorSpec
} = require("devtools/shared/protocol");

const highlighterSpec = generateActorSpec({
  typeName: "highlighter",

  methods: {
    showBoxModel: {
      request: {
        node: Arg(0, "domnode"),
        region: Option(1),
        hideInfoBar: Option(1),
        hideGuides: Option(1),
        showOnly: Option(1),
        onlyRegionArea: Option(1)
      }
    },
    hideBoxModel: {
      request: {}
    },
    pick: {},
    pickAndFocus: {},
    cancelPick: {}
  }
});

exports.highlighterSpec = highlighterSpec;

const customHighlighterSpec = generateActorSpec({
  typeName: "customhighlighter",

  events: {
    "highlighter-event": {
      type: "highlighter-event",
      data: Arg(0, "json")
    }
  },

  methods: {
    release: {
      release: true
    },
    show: {
      request: {
        node: Arg(0, "domnode"),
        options: Arg(1, "nullable:json")
      },
      response: {
        value: RetVal("nullable:boolean")
      }
    },
    hide: {
      request: {}
    },
    finalize: {
      oneway: true
    }
  }
});

exports.customHighlighterSpec = customHighlighterSpec;
PK
!<5x��6chrome/devtools/modules/devtools/shared/specs/index.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

// Registry indexing all specs and front modules
//
// All spec and front modules should be listed here
// in order to be referenced by any other spec or front module.

// Declare in which spec module and front module a set of types are defined.
// This array should be sorted by `spec` attribute.
const Types = exports.__TypesForTests = [
  {
    types: ["accessible", "accessiblewalker", "accessibility"],
    spec: "devtools/shared/specs/accessibility",
    front: "devtools/shared/fronts/accessibility",
  },
  {
    types: ["actorActor", "actorRegistry"],
    spec: "devtools/shared/specs/actor-registry",
    front: "devtools/shared/fronts/actor-registry",
  },
  {
    types: ["addons"],
    spec: "devtools/shared/specs/addons",
    front: "devtools/shared/fronts/addons",
  },
  {
    types: ["animationplayer", "animations"],
    spec: "devtools/shared/specs/animation",
    front: "devtools/shared/fronts/animation",
  },
  /* breakpoint has old fashion client and no front */
  {
    types: ["breakpoint"],
    spec: "devtools/shared/specs/breakpoint",
    front: null,
  },
  {
    types: ["function-call", "call-watcher"],
    spec: "devtools/shared/specs/call-watcher",
    front: "devtools/shared/fronts/call-watcher",
  },
  {
    types: ["frame-snapshot", "canvas"],
    spec: "devtools/shared/specs/canvas",
    front: "devtools/shared/fronts/canvas",
  },
  {
    types: ["cssProperties"],
    spec: "devtools/shared/specs/css-properties",
    front: "devtools/shared/fronts/css-properties",
  },
  {
    types: ["cssUsage"],
    spec: "devtools/shared/specs/csscoverage",
    front: "devtools/shared/fronts/csscoverage",
  },
  {
    types: ["device"],
    spec: "devtools/shared/specs/device",
    front: "devtools/shared/fronts/device",
  },
  {
    types: ["emulation"],
    spec: "devtools/shared/specs/emulation",
    front: "devtools/shared/fronts/emulation",
  },
  /* environment has old fashion client and no front */
  {
    types: ["environment"],
    spec: "devtools/shared/specs/environment",
    front: null,
  },
  {
    types: ["eventLoopLag"],
    spec: "devtools/shared/specs/eventlooplag",
    front: "devtools/shared/fronts/eventlooplag",
  },
  /* frame has old fashion client and no front */
  {
    types: ["frame"],
    spec: "devtools/shared/specs/frame",
    front: null,
  },
  {
    types: ["framerate"],
    spec: "devtools/shared/specs/framerate",
    front: "devtools/shared/fronts/framerate",
  },
  {
    types: ["gcli"],
    spec: "devtools/shared/specs/gcli",
    front: "devtools/shared/fronts/gcli",
  },
  /* heap snapshot has old fashion client and no front */
  {
    types: ["heapSnapshotFile"],
    spec: "devtools/shared/specs/heap-snapshot-file",
    front: null,
  },
  {
    types: ["highlighter", "customhighlighter"],
    spec: "devtools/shared/specs/highlighters",
    front: "devtools/shared/fronts/highlighters",
  },
  {
    types: ["domnodelist", "domwalker", "inspector"],
    spec: "devtools/shared/specs/inspector",
    front: "devtools/shared/fronts/inspector",
  },
  {
    types: ["flexbox", "grid", "layout"],
    spec: "devtools/shared/specs/layout",
    front: "devtools/shared/fronts/layout",
  },
  {
    types: ["memory"],
    spec: "devtools/shared/specs/memory",
    front: "devtools/shared/fronts/memory",
  },
  /* imageData isn't an actor but just a DictType */
  {
    types: ["imageData", "disconnectedNode", "disconnectedNodeArray"],
    spec: "devtools/shared/specs/node",
    front: null,
  },
  {
    types: ["domnode", "domnodelist"],
    spec: "devtools/shared/specs/node",
    front: "devtools/shared/fronts/node",
  },
  {
    types: ["perf"],
    spec: "devtools/shared/specs/perf",
    front: "devtools/shared/fronts/perf",
  },
  {
    types: ["performance"],
    spec: "devtools/shared/specs/performance",
    front: "devtools/shared/fronts/performance",
  },
  {
    types: ["performance-recording"],
    spec: "devtools/shared/specs/performance-recording",
    front: "devtools/shared/fronts/performance-recording",
  },
  {
    types: ["preference"],
    spec: "devtools/shared/specs/preference",
    front: "devtools/shared/fronts/preference",
  },
  {
    types: ["promises"],
    spec: "devtools/shared/specs/promises",
    front: "devtools/shared/fronts/promises",
  },
  {
    types: ["reflow"],
    spec: "devtools/shared/specs/reflow",
    front: "devtools/shared/fronts/reflow",
  },
  /* Script and source have old fashion client and no front */
  {
    types: ["context"],
    spec: "devtools/shared/specs/script",
    front: null,
  },
  {
    types: ["source"],
    spec: "devtools/shared/specs/source",
    front: null,
  },
  {
    types: ["cookies", "localStorage", "sessionStorage", "Cache", "indexedDB", "storage"],
    spec: "devtools/shared/specs/storage",
    front: "devtools/shared/fronts/storage",
  },
  /* longstring is special, it has a wrapper type. See its spec module */
  {
    types: ["longstring"],
    spec: "devtools/shared/specs/string",
    front: null
  },
  {
    types: ["longstractor"],
    spec: "devtools/shared/specs/string",
    front: "devtools/shared/fronts/string",
  },
  {
    types: ["pagestyle", "domstylerule"],
    spec: "devtools/shared/specs/styles",
    front: "devtools/shared/fronts/styles",
  },
  {
    types: ["mediarule", "stylesheet", "stylesheets"],
    spec: "devtools/shared/specs/stylesheets",
    front: "devtools/shared/fronts/stylesheets",
  },
  {
    types: ["timeline"],
    spec: "devtools/shared/specs/timeline",
    front: "devtools/shared/fronts/timeline",
  },
  {
    types: ["audionode", "webaudio"],
    spec: "devtools/shared/specs/webaudio",
    front: "devtools/shared/fronts/webaudio",
  },
  {
    types: ["webExtensionInspectedWindow"],
    spec: "devtools/shared/specs/webextension-inspected-window",
    front: "devtools/shared/fronts/webextension-inspected-window",
  },
  {
    types: ["webExtensionAddon"],
    spec: "devtools/shared/specs/webextension-parent",
    front: null,
  },
  {
    types: ["gl-shader", "gl-program", "webgl"],
    spec: "devtools/shared/specs/webgl",
    front: "devtools/shared/fronts/webgl",
  },
  {
    types: ["worker", "pushSubscription", "serviceWorkerRegistration", "serviceWorker"],
    spec: "devtools/shared/specs/worker",
    front: null,
  },
];

const lazySpecs = new Map();
const lazyFronts = new Map();

// Convert the human readable `Types` list into efficient maps
Types.forEach(item => {
  item.types.forEach(type => {
    lazySpecs.set(type, item.spec);
    lazyFronts.set(type, item.front);
  });
});

/**
 * Try lazy loading spec module for the given type.
 *
 * @param [string] type
 *    Type name
 *
 * @returns true, if it matched a lazy loaded type and tried to load it.
 */
function lazyLoadSpec(type) {
  let modulePath = lazySpecs.get(type);
  if (modulePath) {
    try {
      require(modulePath);
    } catch (e) {
      throw new Error(
        `Unable to load lazy spec module '${modulePath}' for type '${type}'`);
    }
    lazySpecs.delete(type);
    return true;
  }
  return false;
}
exports.lazyLoadSpec = lazyLoadSpec;

/**
 * Try lazy loading front module for the given type.
 *
 * @param [string] type
 *    Type name
 *
 * @returns true, if it matched a lazy loaded type and tried to load it.
 */
function lazyLoadFront(type) {
  let modulePath = lazyFronts.get(type);
  if (modulePath) {
    try {
      require(modulePath);
    } catch (e) {
      throw new Error(
        `Unable to load lazy front module '${modulePath}' for type '${type}'`);
    }
    lazyFronts.delete(type);
    return true;
  }
  return false;
}
exports.lazyLoadFront = lazyLoadFront;
PK
!<\�J�#�#:chrome/devtools/modules/devtools/shared/specs/inspector.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  Arg,
  Option,
  RetVal,
  generateActorSpec,
  types
} = require("devtools/shared/protocol");

types.addDictType("dommutation", {});

types.addDictType("searchresult", {
  list: "domnodelist",
  // Right now there is isn't anything required for metadata,
  // but it's json so it can be extended with extra data.
  metadata: "array:json"
});

// Some common request/response templates for the dom walker

var nodeArrayMethod = {
  request: {
    node: Arg(0, "domnode"),
    maxNodes: Option(1),
    center: Option(1, "domnode"),
    start: Option(1, "domnode"),
    whatToShow: Option(1)
  },
  response: RetVal(types.addDictType("domtraversalarray", {
    nodes: "array:domnode"
  }))
};

var traversalMethod = {
  request: {
    node: Arg(0, "domnode"),
    whatToShow: Option(1)
  },
  response: {
    node: RetVal("nullable:domnode")
  }
};

const walkerSpec = generateActorSpec({
  typeName: "domwalker",

  events: {
    "new-mutations": {
      type: "newMutations"
    },
    "picker-node-picked": {
      type: "pickerNodePicked",
      node: Arg(0, "disconnectedNode")
    },
    "picker-node-previewed": {
      type: "pickerNodePreviewed",
      node: Arg(0, "disconnectedNode")
    },
    "picker-node-hovered": {
      type: "pickerNodeHovered",
      node: Arg(0, "disconnectedNode")
    },
    "picker-node-canceled": {
      type: "pickerNodeCanceled"
    },
    "highlighter-ready": {
      type: "highlighter-ready"
    },
    "highlighter-hide": {
      type: "highlighter-hide"
    },
    "display-change": {
      type: "display-change",
      nodes: Arg(0, "array:domnode")
    },
    // The walker actor emits a useful "resize" event to its front to let
    // clients know when the browser window gets resized. This may be useful
    // for refreshing a DOM node's styles for example, since those may depend on
    // media-queries.
    "resize": {
      type: "resize"
    }
  },

  methods: {
    release: {
      release: true
    },
    pick: {
      request: {},
      response: RetVal("disconnectedNode")
    },
    cancelPick: {},
    highlight: {
      request: {node: Arg(0, "nullable:domnode")}
    },
    document: {
      request: { node: Arg(0, "nullable:domnode") },
      response: { node: RetVal("domnode") },
    },
    documentElement: {
      request: { node: Arg(0, "nullable:domnode") },
      response: { node: RetVal("domnode") },
    },
    parents: {
      request: {
        node: Arg(0, "domnode"),
        sameDocument: Option(1),
        sameTypeRootTreeItem: Option(1)
      },
      response: {
        nodes: RetVal("array:domnode")
      },
    },
    retainNode: {
      request: { node: Arg(0, "domnode") },
      response: {}
    },
    unretainNode: {
      request: { node: Arg(0, "domnode") },
      response: {},
    },
    releaseNode: {
      request: {
        node: Arg(0, "domnode"),
        force: Option(1)
      }
    },
    children: nodeArrayMethod,
    siblings: nodeArrayMethod,
    nextSibling: traversalMethod,
    previousSibling: traversalMethod,
    findInspectingNode: {
      request: {},
      response: RetVal("disconnectedNode")
    },
    querySelector: {
      request: {
        node: Arg(0, "domnode"),
        selector: Arg(1)
      },
      response: RetVal("disconnectedNode")
    },
    querySelectorAll: {
      request: {
        node: Arg(0, "domnode"),
        selector: Arg(1)
      },
      response: {
        list: RetVal("domnodelist")
      }
    },
    multiFrameQuerySelectorAll: {
      request: {
        selector: Arg(0)
      },
      response: {
        list: RetVal("domnodelist")
      }
    },
    search: {
      request: {
        query: Arg(0),
      },
      response: {
        list: RetVal("searchresult"),
      }
    },
    getSuggestionsForQuery: {
      request: {
        query: Arg(0),
        completing: Arg(1),
        selectorState: Arg(2)
      },
      response: {
        list: RetVal("array:array:string")
      }
    },
    addPseudoClassLock: {
      request: {
        node: Arg(0, "domnode"),
        pseudoClass: Arg(1),
        parents: Option(2),
        enabled: Option(2, "boolean"),
      },
      response: {}
    },
    hideNode: {
      request: { node: Arg(0, "domnode") }
    },
    unhideNode: {
      request: { node: Arg(0, "domnode") }
    },
    removePseudoClassLock: {
      request: {
        node: Arg(0, "domnode"),
        pseudoClass: Arg(1),
        parents: Option(2)
      },
      response: {}
    },
    clearPseudoClassLocks: {
      request: {
        node: Arg(0, "nullable:domnode")
      },
      response: {}
    },
    innerHTML: {
      request: {
        node: Arg(0, "domnode")
      },
      response: {
        value: RetVal("longstring")
      }
    },
    setInnerHTML: {
      request: {
        node: Arg(0, "domnode"),
        value: Arg(1, "string"),
      },
      response: {}
    },
    outerHTML: {
      request: {
        node: Arg(0, "domnode")
      },
      response: {
        value: RetVal("longstring")
      }
    },
    setOuterHTML: {
      request: {
        node: Arg(0, "domnode"),
        value: Arg(1, "string"),
      },
      response: {}
    },
    insertAdjacentHTML: {
      request: {
        node: Arg(0, "domnode"),
        position: Arg(1, "string"),
        value: Arg(2, "string")
      },
      response: RetVal("disconnectedNodeArray")
    },
    duplicateNode: {
      request: {
        node: Arg(0, "domnode")
      },
      response: {}
    },
    removeNode: {
      request: {
        node: Arg(0, "domnode")
      },
      response: {
        nextSibling: RetVal("nullable:domnode")
      }
    },
    removeNodes: {
      request: {
        node: Arg(0, "array:domnode")
      },
      response: {}
    },
    insertBefore: {
      request: {
        node: Arg(0, "domnode"),
        parent: Arg(1, "domnode"),
        sibling: Arg(2, "nullable:domnode")
      },
      response: {}
    },
    editTagName: {
      request: {
        node: Arg(0, "domnode"),
        tagName: Arg(1, "string")
      },
      response: {}
    },
    getMutations: {
      request: {
        cleanup: Option(0)
      },
      response: {
        mutations: RetVal("array:dommutation")
      }
    },
    isInDOMTree: {
      request: { node: Arg(0, "domnode") },
      response: { attached: RetVal("boolean") }
    },
    getNodeActorFromObjectActor: {
      request: {
        objectActorID: Arg(0, "string")
      },
      response: {
        nodeFront: RetVal("nullable:disconnectedNode")
      }
    },
    getNodeActorFromWindowID: {
      request: {
        windowID: Arg(0, "string")
      },
      response: {
        nodeFront: RetVal("nullable:disconnectedNode")
      }
    },
    getStyleSheetOwnerNode: {
      request: {
        styleSheetActorID: Arg(0, "string")
      },
      response: {
        ownerNode: RetVal("nullable:disconnectedNode")
      }
    },
    getNodeFromActor: {
      request: {
        actorID: Arg(0, "string"),
        path: Arg(1, "array:string")
      },
      response: {
        node: RetVal("nullable:disconnectedNode")
      }
    },
    getLayoutInspector: {
      request: {},
      response: {
        actor: RetVal("layout")
      }
    },
    getOffsetParent: {
      request: {
        node: Arg(0, "nullable:domnode")
      },
      response: {
        node: RetVal("nullable:domnode")
      }
    },
  }
});

exports.walkerSpec = walkerSpec;

const inspectorSpec = generateActorSpec({
  typeName: "inspector",

  events: {
    "color-picked": {
      type: "colorPicked",
      color: Arg(0, "string")
    },
    "color-pick-canceled": {
      type: "colorPickCanceled"
    }
  },

  methods: {
    getWalker: {
      request: {
        options: Arg(0, "nullable:json")
      },
      response: {
        walker: RetVal("domwalker")
      }
    },
    getPageStyle: {
      request: {},
      response: {
        pageStyle: RetVal("pagestyle")
      }
    },
    getHighlighter: {
      request: {
        autohide: Arg(0, "boolean")
      },
      response: {
        highligter: RetVal("highlighter")
      }
    },
    getHighlighterByType: {
      request: {
        typeName: Arg(0)
      },
      response: {
        highlighter: RetVal("nullable:customhighlighter")
      }
    },
    getImageDataFromURL: {
      request: {url: Arg(0), maxDim: Arg(1, "nullable:number")},
      response: RetVal("imageData")
    },
    resolveRelativeURL: {
      request: {url: Arg(0, "string"), node: Arg(1, "nullable:domnode")},
      response: {value: RetVal("string")}
    },
    pickColorFromPage: {
      request: {options: Arg(0, "nullable:json")},
      response: {}
    },
    cancelPickColorFromPage: {
      request: {},
      response: {}
    },
    supportsHighlighters: {
      request: {},
      response: {
        value: RetVal("boolean")
      }
    }
  }
});

exports.inspectorSpec = inspectorSpec;
PK
!<v����7chrome/devtools/modules/devtools/shared/specs/layout.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Arg, generateActorSpec, RetVal } = require("devtools/shared/protocol");

const flexboxSpec = generateActorSpec({
  typeName: "flexbox",

  methods: {},
});

const gridSpec = generateActorSpec({
  typeName: "grid",

  methods: {},
});

const layoutSpec = generateActorSpec({
  typeName: "layout",

  methods: {
    getCurrentFlexbox: {
      request: {
        node: Arg(0, "domnode"),
      },
      response: {
        flexbox: RetVal("nullable:flexbox")
      }
    },

    getGrids: {
      request: {
        rootNode: Arg(0, "domnode")
      },
      response: {
        grids: RetVal("array:grid")
      }
    },
  },
});

exports.flexboxSpec = flexboxSpec;
exports.gridSpec = gridSpec;
exports.layoutSpec = layoutSpec;
PK
!<�ײ�

7chrome/devtools/modules/devtools/shared/specs/memory.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  Arg,
  RetVal,
  types,
  generateActorSpec,
} = require("devtools/shared/protocol");

types.addDictType("AllocationsRecordingOptions", {
  // The probability we sample any given allocation when recording
  // allocations. Must be between 0.0 and 1.0. Defaults to 1.0, or sampling
  // every allocation.
  probability: "number",

  // The maximum number of of allocation events to keep in the allocations
  // log. If new allocations arrive, when we are already at capacity, the oldest
  // allocation event is lost. This number must fit in a 32 bit signed integer.
  maxLogLength: "number"
});

const memorySpec = generateActorSpec({
  typeName: "memory",

  /**
   * The set of unsolicited events the MemoryActor emits that will be sent over
   * the RDP (by protocol.js).
   */
  events: {
    // Same format as the data passed to the
    // `Debugger.Memory.prototype.onGarbageCollection` hook. See
    // `js/src/doc/Debugger/Debugger.Memory.md` for documentation.
    "garbage-collection": {
      type: "garbage-collection",
      data: Arg(0, "json"),
    },

    // Same data as the data from `getAllocations` -- only fired if
    // `autoDrain` set during `startRecordingAllocations`.
    "allocations": {
      type: "allocations",
      data: Arg(0, "json"),
    },
  },

  methods: {
    attach: {
      request: {},
      response: {
        type: "attached"
      }
    },
    detach: {
      request: {},
      response: {
        type: "detached"
      }
    },
    getState: {
      response: {
        state: RetVal(0, "string")
      }
    },
    takeCensus: {
      request: {},
      response: RetVal("json")
    },
    startRecordingAllocations: {
      request: {
        options: Arg(0, "nullable:AllocationsRecordingOptions")
      },
      response: {
        // Accept `nullable` in the case of server Gecko <= 37, handled on the front
        value: RetVal(0, "nullable:number")
      }
    },
    stopRecordingAllocations: {
      request: {},
      response: {
        // Accept `nullable` in the case of server Gecko <= 37, handled on the front
        value: RetVal(0, "nullable:number")
      }
    },
    getAllocationsSettings: {
      request: {},
      response: {
        options: RetVal(0, "json")
      }
    },
    getAllocations: {
      request: {},
      response: RetVal("json")
    },
    forceGarbageCollection: {
      request: {},
      response: {}
    },
    forceCycleCollection: {
      request: {},
      response: {}
    },
    measure: {
      request: {},
      response: RetVal("json"),
    },
    residentUnique: {
      request: {},
      response: { value: RetVal("number") }
    },
    saveHeapSnapshot: {
      request: {
        boundaries: Arg(0, "nullable:json")
      },
      response: {
        snapshotId: RetVal("string")
      }
    },
  },
});

exports.memorySpec = memorySpec;
PK
!<4HZr�
�
5chrome/devtools/modules/devtools/shared/specs/node.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  Arg,
  RetVal,
  generateActorSpec,
  types
} = require("devtools/shared/protocol.js");

types.addDictType("imageData", {
  // The image data
  data: "nullable:longstring",
  // The original image dimensions
  size: "json"
});

/**
 * Returned from any call that might return a node that isn't connected to root
 * by nodes the child has seen, such as querySelector.
 */
types.addDictType("disconnectedNode", {
  // The actual node to return
  node: "domnode",

  // Nodes that are needed to connect the node to a node the client has already
  // seen
  newParents: "array:domnode"
});

types.addDictType("disconnectedNodeArray", {
  // The actual node list to return
  nodes: "array:domnode",

  // Nodes that are needed to connect those nodes to the root.
  newParents: "array:domnode"
});

const nodeListSpec = generateActorSpec({
  typeName: "domnodelist",

  methods: {
    item: {
      request: { item: Arg(0) },
      response: RetVal("disconnectedNode")
    },
    items: {
      request: {
        start: Arg(0, "nullable:number"),
        end: Arg(1, "nullable:number")
      },
      response: RetVal("disconnectedNodeArray")
    },
    release: {
      release: true
    }
  }
});

exports.nodeListSpec = nodeListSpec;

const nodeSpec = generateActorSpec({
  typeName: "domnode",

  methods: {
    getNodeValue: {
      request: {},
      response: {
        value: RetVal("longstring")
      }
    },
    setNodeValue: {
      request: { value: Arg(0) },
      response: {}
    },
    getUniqueSelector: {
      request: {},
      response: {
        value: RetVal("string")
      }
    },
    getCssPath: {
      request: {},
      response: {
        value: RetVal("string")
      }
    },
    getXPath: {
      request: {},
      response: {
        value: RetVal("string")
      }
    },
    scrollIntoView: {
      request: {},
      response: {}
    },
    getImageData: {
      request: {maxDim: Arg(0, "nullable:number")},
      response: RetVal("imageData")
    },
    getEventListenerInfo: {
      request: {},
      response: {
        events: RetVal("json")
      }
    },
    modifyAttributes: {
      request: {
        modifications: Arg(0, "array:json")
      },
      response: {}
    },
    getFontFamilyDataURL: {
      request: {font: Arg(0, "string"), fillStyle: Arg(1, "nullable:string")},
      response: RetVal("imageData")
    },
    getClosestBackgroundColor: {
      request: {},
      response: {
        value: RetVal("string")
      }
    },
  }
});

exports.nodeSpec = nodeSpec;
PK
!<	V����5chrome/devtools/modules/devtools/shared/specs/perf.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { RetVal, generateActorSpec } = require("devtools/shared/protocol");

const perfDescription = {
  typeName: "perf",

  events: {
    "profiler-started": {
      type: "profiler-started"
    },
    "profiler-stopped": {
      type: "profiler-stopped"
    },
    "profile-locked-by-private-browsing": {
      type: "profile-locked-by-private-browsing"
    },
    "profile-unlocked-from-private-browsing": {
      type: "profile-unlocked-from-private-browsing"
    }
  },

  methods: {
    startProfiler: {
      request: {},
      response: { value: RetVal("boolean") }
    },

    /**
     * Returns null when unable to return the profile.
     */
    getProfileAndStopProfiler: {
      request: {},
      response: RetVal("nullable:json")
    },

    stopProfilerAndDiscardProfile: {
      request: {},
      response: {}
    },

    isActive: {
      request: {},
      response: { value: RetVal("boolean") }
    },

    isSupportedPlatform: {
      request: {},
      response: { value: RetVal("boolean") }
    },

    isLockedForPrivateBrowsing: {
      request: {},
      response: { value: RetVal("boolean") }
    }
  }
};

exports.perfDescription = perfDescription;

const perfSpec = generateActorSpec(perfDescription);

exports.perfSpec = perfSpec;
PK
!<�Wus��Fchrome/devtools/modules/devtools/shared/specs/performance-recording.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { generateActorSpec } = require("devtools/shared/protocol");

const performanceRecordingSpec = generateActorSpec({
  typeName: "performance-recording"
});

exports.performanceRecordingSpec = performanceRecordingSpec;
PK
!<��zk��<chrome/devtools/modules/devtools/shared/specs/performance.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Arg, RetVal, generateActorSpec } = require("devtools/shared/protocol");

const performanceSpec = generateActorSpec({
  typeName: "performance",

  /**
   * The set of events the PerformanceActor emits over RDP.
   */
  events: {
    "recording-started": {
      recording: Arg(0, "performance-recording"),
    },
    "recording-stopping": {
      recording: Arg(0, "performance-recording"),
    },
    "recording-stopped": {
      recording: Arg(0, "performance-recording"),
      data: Arg(1, "json"),
    },
    "profiler-status": {
      data: Arg(0, "json"),
    },
    "console-profile-start": {},
    "timeline-data": {
      name: Arg(0, "string"),
      data: Arg(1, "json"),
      recordings: Arg(2, "array:performance-recording"),
    },
  },

  methods: {
    connect: {
      request: { options: Arg(0, "nullable:json") },
      response: RetVal("json")
    },

    canCurrentlyRecord: {
      request: {},
      response: { value: RetVal("json") }
    },

    startRecording: {
      request: {
        options: Arg(0, "nullable:json"),
      },
      response: {
        recording: RetVal("nullable:performance-recording")
      }
    },

    stopRecording: {
      request: {
        options: Arg(0, "performance-recording"),
      },
      response: {
        recording: RetVal("performance-recording")
      }
    },

    isRecording: {
      request: {},
      response: { isRecording: RetVal("boolean") }
    },

    getRecordings: {
      request: {},
      response: { recordings: RetVal("array:performance-recording") }
    },

    getConfiguration: {
      request: {},
      response: { config: RetVal("json") }
    },

    setProfilerStatusInterval: {
      request: { interval: Arg(0, "number") },
      response: { oneway: true }
    },
  }
});

exports.performanceSpec = performanceSpec;
PK
!<�pX|��;chrome/devtools/modules/devtools/shared/specs/preference.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {Arg, RetVal, generateActorSpec} = require("devtools/shared/protocol");

const preferenceSpec = generateActorSpec({
  typeName: "preference",

  methods: {
    getBoolPref: {
      request: { value: Arg(0) },
      response: { value: RetVal("boolean") }
    },
    getCharPref: {
      request: { value: Arg(0) },
      response: { value: RetVal("string") }
    },
    getIntPref: {
      request: { value: Arg(0) },
      response: { value: RetVal("number") }
    },
    getAllPrefs: {
      request: {},
      response: { value: RetVal("json") }
    },
    setBoolPref: {
      request: { name: Arg(0), value: Arg(1) },
      response: {}
    },
    setCharPref: {
      request: { name: Arg(0), value: Arg(1) },
      response: {}
    },
    setIntPref: {
      request: { name: Arg(0), value: Arg(1) },
      response: {}
    },
    clearUserPref: {
      request: { name: Arg(0) },
      response: {}
    }
  },
});

exports.preferenceSpec = preferenceSpec;
PK
!<�&4��9chrome/devtools/modules/devtools/shared/specs/promises.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  Arg,
  RetVal,
  generateActorSpec,
  types
} = require("devtools/shared/protocol");

// Teach protocol.js how to deal with legacy actor types
types.addType("ObjectActor", {
  write: actor => actor.grip(),
  read: grip => grip
});

const promisesSpec = generateActorSpec({
  typeName: "promises",

  events: {
    // Event emitted for new promises allocated in debuggee and bufferred by
    // sending the list of promise objects in a batch.
    "new-promises": {
      type: "new-promises",
      data: Arg(0, "array:ObjectActor"),
    },
    // Event emitted for promise settlements.
    "promises-settled": {
      type: "promises-settled",
      data: Arg(0, "array:ObjectActor")
    }
  },

  methods: {
    attach: {
      request: {},
      response: {},
    },

    detach: {
      request: {},
      response: {},
    },

    listPromises: {
      request: {},
      response: {
        promises: RetVal("array:ObjectActor"),
      },
    }
  }
});

exports.promisesSpec = promisesSpec;
PK
!<fdhFF7chrome/devtools/modules/devtools/shared/specs/reflow.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {Arg, generateActorSpec} = require("devtools/shared/protocol");

const reflowSpec = generateActorSpec({
  typeName: "reflow",

  events: {
    /**
     * The reflows event is emitted when reflows have been detected. The event
     * is sent with an array of reflows that occured. Each item has the
     * following properties:
     * - start {Number}
     * - end {Number}
     * - isInterruptible {Boolean}
     */
    reflows: {
      type: "reflows",
      reflows: Arg(0, "array:json")
    }
  },

  methods: {
    start: {oneway: true},
    stop: {oneway: true},
  },
});

exports.reflowSpec = reflowSpec;
PK
!<�k'���7chrome/devtools/modules/devtools/shared/specs/script.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {generateActorSpec} = require("devtools/shared/protocol");

const threadSpec = generateActorSpec({
  typeName: "context",

  methods: {},
});

exports.threadSpec = threadSpec;
PK
!<6J�VV7chrome/devtools/modules/devtools/shared/specs/source.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {Arg, RetVal, generateActorSpec} = require("devtools/shared/protocol");

const sourceSpec = generateActorSpec({
  typeName: "source",

  methods: {
    getExecutableLines: { response: { lines: RetVal("json") } },
    onSource: {
      request: { type: "source" },
      response: RetVal("json")
    },
    prettyPrint: {
      request: { indent: Arg(0, "number") },
      response: RetVal("json")
    },
    disablePrettyPrint: {
      response: RetVal("json")
    },
    blackbox: { response: { pausedInSource: RetVal("boolean") } },
    unblackbox: {},
    setBreakpoint: {
      request: {
        location: {
          line: Arg(0, "number"),
          column: Arg(1, "nullable:number")
        },
        condition: Arg(2, "nullable:string"),
        noSliding: Arg(3, "nullable:boolean")
      },
      response: RetVal("json")
    },
  },
});

exports.sourceSpec = sourceSpec;
PK
!<P�Ȩ��8chrome/devtools/modules/devtools/shared/specs/storage.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 protocol = require("devtools/shared/protocol");
const { Arg, RetVal, types } = protocol;

let childSpecs = {};

function createStorageSpec(options) {
  // common methods for all storage types
  let methods = {
    getStoreObjects: {
      request: {
        host: Arg(0),
        names: Arg(1, "nullable:array:string"),
        options: Arg(2, "nullable:json")
      },
      response: RetVal(options.storeObjectType)
    },
    getFields: {
      request: {
        subType: Arg(0, "nullable:string")
      },
      response: {
        value: RetVal("json")
      }
    }
  };

  // extra methods specific for storage type
  Object.assign(methods, options.methods);

  childSpecs[options.typeName] = protocol.generateActorSpec({
    typeName: options.typeName,
    methods
  });
}

// Cookies store object
types.addDictType("cookieobject", {
  uniqueKey: "string",
  name: "string",
  value: "longstring",
  path: "nullable:string",
  host: "string",
  isDomain: "boolean",
  isSecure: "boolean",
  isHttpOnly: "boolean",
  creationTime: "number",
  lastAccessed: "number",
  expires: "number"
});

// Array of cookie store objects
types.addDictType("cookiestoreobject", {
  total: "number",
  offset: "number",
  data: "array:nullable:cookieobject"
});

// Common methods for edit/remove
const editRemoveMethods = {
  getFields: {
    request: {},
    response: {
      value: RetVal("json")
    }
  },
  editItem: {
    request: {
      data: Arg(0, "json"),
    },
    response: {}
  },
  removeItem: {
    request: {
      host: Arg(0, "string"),
      name: Arg(1, "string"),
    },
    response: {}
  },
};

// Cookies actor spec
createStorageSpec({
  typeName: "cookies",
  storeObjectType: "cookiestoreobject",
  methods: Object.assign({},
    editRemoveMethods,
    {
      addItem: {
        request: {
          guid: Arg(0, "string"),
        },
        response: {}
      }
    }, {
      removeAll: {
        request: {
          host: Arg(0, "string"),
          domain: Arg(1, "nullable:string")
        },
        response: {}
      }
    }, {
      removeAllSessionCookies: {
        request: {
          host: Arg(0, "string"),
          domain: Arg(1, "nullable:string")
        },
        response: {}
      }
    }
  )
});

// Local Storage / Session Storage store object
types.addDictType("storageobject", {
  name: "string",
  value: "longstring"
});

// Common methods for local/session storage
const storageMethods = Object.assign({},
  editRemoveMethods,
  {
    addItem: {
      request: {
        guid: Arg(0, "string"),
        host: Arg(1, "nullable:string")
      },
      response: {}
    }
  },
  {
    removeAll: {
      request: {
        host: Arg(0, "string")
      },
      response: {}
    }
  }
);

// Array of Local Storage / Session Storage store objects
types.addDictType("storagestoreobject", {
  total: "number",
  offset: "number",
  data: "array:nullable:storageobject"
});

createStorageSpec({
  typeName: "localStorage",
  storeObjectType: "storagestoreobject",
  methods: storageMethods
});

createStorageSpec({
  typeName: "sessionStorage",
  storeObjectType: "storagestoreobject",
  methods: storageMethods
});

types.addDictType("cacheobject", {
  "url": "string",
  "status": "string"
});

// Array of Cache store objects
types.addDictType("cachestoreobject", {
  total: "number",
  offset: "number",
  data: "array:nullable:cacheobject"
});

// Cache storage spec
createStorageSpec({
  typeName: "Cache",
  storeObjectType: "cachestoreobject",
  methods: {
    removeAll: {
      request: {
        host: Arg(0, "string"),
        name: Arg(1, "string"),
      },
      response: {}
    },
    removeItem: {
      request: {
        host: Arg(0, "string"),
        name: Arg(1, "string"),
      },
      response: {}
    },
  }
});

// Indexed DB store object
// This is a union on idb object, db metadata object and object store metadata
// object
types.addDictType("idbobject", {
  uniqueKey: "string",
  name: "nullable:string",
  db: "nullable:string",
  objectStore: "nullable:string",
  origin: "nullable:string",
  version: "nullable:number",
  storage: "nullable:string",
  objectStores: "nullable:number",
  keyPath: "nullable:string",
  autoIncrement: "nullable:boolean",
  indexes: "nullable:string",
  value: "nullable:longstring"
});

// Array of Indexed DB store objects
types.addDictType("idbstoreobject", {
  total: "number",
  offset: "number",
  data: "array:nullable:idbobject"
});

// Result of Indexed DB delete operation: can block or throw error
types.addDictType("idbdeleteresult", {
  blocked: "nullable:boolean",
  error: "nullable:string"
});

createStorageSpec({
  typeName: "indexedDB",
  storeObjectType: "idbstoreobject",
  methods: {
    removeDatabase: {
      request: {
        host: Arg(0, "string"),
        name: Arg(1, "string"),
      },
      response: RetVal("idbdeleteresult")
    },
    removeAll: {
      request: {
        host: Arg(0, "string"),
        name: Arg(1, "string"),
      },
      response: {}
    },
    removeItem: {
      request: {
        host: Arg(0, "string"),
        name: Arg(1, "string"),
      },
      response: {}
    },
  }
});

// Update notification object
types.addDictType("storeUpdateObject", {
  changed: "nullable:json",
  deleted: "nullable:json",
  added: "nullable:json"
});

// Generate a type definition for an object with actors for all storage types.
types.addDictType("storelist", Object.keys(childSpecs).reduce((obj, type) => {
  obj[type] = type;
  return obj;
}, {}));

exports.childSpecs = childSpecs;

exports.storageSpec = protocol.generateActorSpec({
  typeName: "storage",

  /**
   * List of event notifications that the server can send to the client.
   *
   *  - stores-update : When any store object in any storage type changes.
   *  - stores-cleared : When all the store objects are removed.
   *  - stores-reloaded : When all stores are reloaded. This generally mean that
   *                      we should refetch everything again.
   */
  events: {
    "stores-update": {
      type: "storesUpdate",
      data: Arg(0, "storeUpdateObject")
    },
    "stores-cleared": {
      type: "storesCleared",
      data: Arg(0, "json")
    },
    "stores-reloaded": {
      type: "storesReloaded",
      data: Arg(0, "json")
    }
  },

  methods: {
    listStores: {
      request: {},
      response: RetVal("storelist")
    },
  }
});
PK
!<Ӳ���7chrome/devtools/modules/devtools/shared/specs/string.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 protocol = require("devtools/shared/protocol");
const {Arg, RetVal, generateActorSpec} = protocol;
const promise = require("promise");

const longStringSpec = generateActorSpec({
  typeName: "longstractor",

  methods: {
    substring: {
      request: {
        start: Arg(0),
        end: Arg(1)
      },
      response: { substring: RetVal() },
    },
    release: { release: true },
  },
});

exports.longStringSpec = longStringSpec;

/**
 * When a caller is expecting a LongString actor but the string is already available on
 * client, the SimpleStringFront can be used as it shares the same API as a
 * LongStringFront but will not make unnecessary trips to the server.
 */
class SimpleStringFront {
  constructor(str) {
    this.str = str;
  }

  get length() {
    return this.str.length;
  }

  get initial() {
    return this.str;
  }

  string() {
    return promise.resolve(this.str);
  }

  substring(start, end) {
    return promise.resolve(this.str.substring(start, end));
  }

  release() {
    this.str = null;
    return promise.resolve(undefined);
  }
}

exports.SimpleStringFront = SimpleStringFront;

// The long string actor needs some custom marshalling, because it is sometimes
// returned as a primitive rather than a complete form.

var stringActorType = protocol.types.getType("longstractor");
protocol.types.addType("longstring", {
  _actor: true,
  write: (value, context, detail) => {
    if (!(context instanceof protocol.Actor)) {
      throw Error("Passing a longstring as an argument isn't supported.");
    }

    if (value.short) {
      return value.str;
    }
    return stringActorType.write(value, context, detail);
  },
  read: (value, context, detail) => {
    if (context instanceof protocol.Actor) {
      throw Error("Passing a longstring as an argument isn't supported.");
    }
    if (typeof (value) === "string") {
      return new SimpleStringFront(value);
    }
    return stringActorType.read(value, context, detail);
  }
});
PK
!<�T��((7chrome/devtools/modules/devtools/shared/specs/styles.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  Arg,
  Option,
  RetVal,
  generateActorSpec,
  types
} = require("devtools/shared/protocol");

// Predeclare the domstylerule actor type
types.addActorType("domstylerule");

/**
 * DOM Nodes returned by the style actor will be owned by the DOM walker
 * for the connection.
  */
types.addLifetime("walker", "walker");

/**
 * When asking for the styles applied to a node, we return a list of
 * appliedstyle json objects that lists the rules that apply to the node
 * and which element they were inherited from (if any).
 *
 * Note appliedstyle only sends the list of actorIDs and is not a valid return
 * value on its own. appliedstyle should be returned with the actual list of
 * StyleRuleActor and StyleSheetActor. See appliedStylesReturn.
 */
types.addDictType("appliedstyle", {
  rule: "domstylerule#actorid",
  inherited: "nullable:domnode#actorid",
  keyframes: "nullable:domstylerule#actorid"
});

types.addDictType("matchedselector", {
  rule: "domstylerule#actorid",
  selector: "string",
  value: "string",
  status: "number"
});

types.addDictType("appliedStylesReturn", {
  entries: "array:appliedstyle",
  rules: "array:domstylerule",
  sheets: "array:stylesheet"
});

types.addDictType("modifiedStylesReturn", {
  isMatching: RetVal("boolean"),
  ruleProps: RetVal("nullable:appliedStylesReturn")
});

types.addDictType("fontpreview", {
  data: "nullable:longstring",
  size: "json"
});

types.addDictType("fontvariationaxis", {
  tag: "string",
  name: "string",
  minValue: "number",
  maxValue: "number",
  defaultValue: "number"
});

types.addDictType("fontvariationinstancevalue", {
  axis: "string",
  value: "number"
});

types.addDictType("fontvariationinstance", {
  name: "string",
  values: "array:fontvariationinstancevalue"
});

types.addDictType("fontface", {
  name: "string",
  CSSFamilyName: "string",
  rule: "nullable:domstylerule",
  srcIndex: "number",
  URI: "string",
  format: "string",
  preview: "nullable:fontpreview",
  localName: "string",
  metadata: "string",
  variationAxes: "array:fontvariationaxis",
  variationInstances: "array:fontvariationinstance"
});

const pageStyleSpec = generateActorSpec({
  typeName: "pagestyle",

  events: {
    "stylesheet-updated": {
      type: "styleSheetUpdated",
      styleSheet: Arg(0, "stylesheet")
    }
  },

  methods: {
    getComputed: {
      request: {
        node: Arg(0, "domnode"),
        markMatched: Option(1, "boolean"),
        onlyMatched: Option(1, "boolean"),
        filter: Option(1, "string"),
      },
      response: {
        computed: RetVal("json")
      }
    },
    getAllUsedFontFaces: {
      request: {
        includePreviews: Option(0, "boolean"),
        includeVariations: Option(1, "boolean"),
        previewText: Option(0, "string"),
        previewFontSize: Option(0, "string"),
        previewFillStyle: Option(0, "string")
      },
      response: {
        fontFaces: RetVal("array:fontface")
      }
    },
    getUsedFontFaces: {
      request: {
        node: Arg(0, "domnode"),
        includePreviews: Option(1, "boolean"),
        includeVariations: Option(1, "boolean"),
        previewText: Option(1, "string"),
        previewFontSize: Option(1, "string"),
        previewFillStyle: Option(1, "string")
      },
      response: {
        fontFaces: RetVal("array:fontface")
      }
    },
    getMatchedSelectors: {
      request: {
        node: Arg(0, "domnode"),
        property: Arg(1, "string"),
        filter: Option(2, "string")
      },
      response: RetVal(types.addDictType("matchedselectorresponse", {
        rules: "array:domstylerule",
        sheets: "array:stylesheet",
        matched: "array:matchedselector"
      }))
    },
    getApplied: {
      request: {
        node: Arg(0, "domnode"),
        inherited: Option(1, "boolean"),
        matchedSelectors: Option(1, "boolean"),
        skipPseudo: Option(1, "boolean"),
        filter: Option(1, "string")
      },
      response: RetVal("appliedStylesReturn")
    },
    isPositionEditable: {
      request: { node: Arg(0, "domnode")},
      response: { value: RetVal("boolean") }
    },
    getLayout: {
      request: {
        node: Arg(0, "domnode"),
        autoMargins: Option(1, "boolean")
      },
      response: RetVal("json")
    },
    addNewRule: {
      request: {
        node: Arg(0, "domnode"),
        pseudoClasses: Arg(1, "nullable:array:string"),
        editAuthored: Arg(2, "boolean")
      },
      response: RetVal("appliedStylesReturn")
    }
  }
});

exports.pageStyleSpec = pageStyleSpec;

const styleRuleSpec = generateActorSpec({
  typeName: "domstylerule",

  events: {
    "location-changed": {
      type: "locationChanged",
      line: Arg(0, "number"),
      column: Arg(1, "number")
    },
  },

  methods: {
    setRuleText: {
      request: { modification: Arg(0, "string") },
      response: { rule: RetVal("domstylerule") }
    },
    modifyProperties: {
      request: { modifications: Arg(0, "array:json") },
      response: { rule: RetVal("domstylerule") }
    },
    modifySelector: {
      request: { selector: Arg(0, "string") },
      response: { isModified: RetVal("boolean") },
    },
    modifySelector2: {
      request: {
        node: Arg(0, "domnode"),
        value: Arg(1, "string"),
        editAuthored: Arg(2, "boolean")
      },
      response: RetVal("modifiedStylesReturn")
    }
  }
});

exports.styleRuleSpec = styleRuleSpec;

// The PageStyle actor flattens the DOM CSS objects a little bit, merging
// Rules and their Styles into one actor.  For elements (which have a style
// but no associated rule) we fake a rule with the following style id.
const ELEMENT_STYLE = 100;
exports.ELEMENT_STYLE = ELEMENT_STYLE;
PK
!<�C�#��<chrome/devtools/modules/devtools/shared/specs/stylesheets.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  Arg,
  RetVal,
  generateActorSpec,
  types
} = require("devtools/shared/protocol");

const mediaRuleSpec = generateActorSpec({
  typeName: "mediarule",

  events: {
    "matches-change": {
      type: "matchesChange",
      matches: Arg(0, "boolean"),
    }
  }
});

exports.mediaRuleSpec = mediaRuleSpec;

types.addActorType("stylesheet");

const styleSheetSpec = generateActorSpec({
  typeName: "stylesheet",

  events: {
    "property-change": {
      type: "propertyChange",
      property: Arg(0, "string"),
      value: Arg(1, "json")
    },
    "style-applied": {
      type: "styleApplied",
      kind: Arg(0, "number"),
      styleSheet: Arg(1, "stylesheet")
    },
    "media-rules-changed": {
      type: "mediaRulesChanged",
      rules: Arg(0, "array:mediarule")
    }
  },

  methods: {
    toggleDisabled: {
      response: { disabled: RetVal("boolean")}
    },
    getText: {
      response: {
        text: RetVal("longstring")
      }
    },
    getMediaRules: {
      request: {},
      response: {
        mediaRules: RetVal("nullable:array:mediarule")
      }
    },
    update: {
      request: {
        text: Arg(0, "string"),
        transition: Arg(1, "boolean")
      }
    }
  }
});

exports.styleSheetSpec = styleSheetSpec;

const styleSheetsSpec = generateActorSpec({
  typeName: "stylesheets",

  events: {
    "stylesheet-added": {
      type: "stylesheetAdded",
      sheet: Arg(0, "stylesheet"),
      isNew: Arg(1, "boolean")
    },
  },

  methods: {
    getStyleSheets: {
      request: {},
      response: { styleSheets: RetVal("array:stylesheet") }
    },
    addStyleSheet: {
      request: { text: Arg(0, "string") },
      response: { styleSheet: RetVal("stylesheet") }
    }
  }
});

exports.styleSheetsSpec = styleSheetsSpec;
PK
!<���9chrome/devtools/modules/devtools/shared/specs/timeline.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  Arg,
  RetVal,
  Option,
  generateActorSpec,
  types
} = require("devtools/shared/protocol");

/**
 * Type representing an array of numbers as strings, serialized fast(er).
 * http://jsperf.com/json-stringify-parse-vs-array-join-split/3
 *
 * XXX: It would be nice if on local connections (only), we could just *give*
 * the array directly to the front, instead of going through all this
 * serialization redundancy.
 */
types.addType("array-of-numbers-as-strings", {
  write: (v) => v.join(","),
  // In Gecko <= 37, `v` is an array; do not transform in this case.
  read: (v) => typeof v === "string" ? v.split(",") : v
});

const timelineSpec = generateActorSpec({
  typeName: "timeline",

  events: {
    /**
     * Events emitted when "DOMContentLoaded" and "Load" markers are received.
     */
    "doc-loading": {
      type: "doc-loading",
      marker: Arg(0, "json"),
      endTime: Arg(0, "number")
    },

    /**
     * The "markers" events emitted every DEFAULT_TIMELINE_DATA_PULL_TIMEOUT ms
     * at most, when profile markers are found. The timestamps on each marker
     * are relative to when recording was started.
     */
    "markers": {
      type: "markers",
      markers: Arg(0, "json"),
      endTime: Arg(1, "number")
    },

    /**
     * The "memory" events emitted in tandem with "markers", if this was enabled
     * when the recording started. The `delta` timestamp on this measurement is
     * relative to when recording was started.
     */
    "memory": {
      type: "memory",
      delta: Arg(0, "number"),
      measurement: Arg(1, "json")
    },

    /**
     * The "ticks" events (from the refresh driver) emitted in tandem with
     * "markers", if this was enabled when the recording started. All ticks
     * are timestamps with a zero epoch.
     */
    "ticks": {
      type: "ticks",
      delta: Arg(0, "number"),
      timestamps: Arg(1, "array-of-numbers-as-strings")
    },

    /**
     * The "frames" events emitted in tandem with "markers", containing
     * JS stack frames. The `delta` timestamp on this frames packet is
     * relative to when recording was started.
     */
    "frames": {
      type: "frames",
      delta: Arg(0, "number"),
      frames: Arg(1, "json")
    }
  },

  methods: {
    isRecording: {
      request: {},
      response: {
        value: RetVal("boolean")
      }
    },

    start: {
      request: {
        withMarkers: Option(0, "boolean"),
        withTicks: Option(0, "boolean"),
        withMemory: Option(0, "boolean"),
        withFrames: Option(0, "boolean"),
        withGCEvents: Option(0, "boolean"),
        withDocLoadingEvents: Option(0, "boolean")
      },
      response: {
        value: RetVal("number")
      }
    },

    stop: {
      response: {
        // Set as possibly nullable due to the end time possibly being
        // undefined during destruction
        value: RetVal("nullable:number")
      }
    },
  },
});

exports.timelineSpec = timelineSpec;
PK
!<�h����9chrome/devtools/modules/devtools/shared/specs/webaudio.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  Arg,
  Option,
  RetVal,
  generateActorSpec,
  types,
} = require("devtools/shared/protocol");

exports.NODE_CREATION_METHODS = [
  "createBufferSource", "createMediaElementSource", "createMediaStreamSource",
  "createMediaStreamDestination", "createScriptProcessor", "createAnalyser",
  "createGain", "createDelay", "createBiquadFilter", "createIIRFilter",
  "createWaveShaper", "createPanner", "createConvolver", "createChannelSplitter",
  "createChannelMerger", "createDynamicsCompressor", "createOscillator",
  "createStereoPanner", "createConstantSource"
];

exports.AUTOMATION_METHODS = [
  "setValueAtTime", "linearRampToValueAtTime", "exponentialRampToValueAtTime",
  "setTargetAtTime", "setValueCurveAtTime", "cancelScheduledValues"
];

exports.NODE_ROUTING_METHODS = [
  "connect", "disconnect"
];

types.addActorType("audionode");
const audionodeSpec = generateActorSpec({
  typeName: "audionode",

  methods: {
    getType: { response: { type: RetVal("string") }},
    isBypassed: {
      response: { bypassed: RetVal("boolean") }
    },
    bypass: {
      request: { enable: Arg(0, "boolean") },
      response: { bypassed: RetVal("boolean") }
    },
    setParam: {
      request: {
        param: Arg(0, "string"),
        value: Arg(1, "nullable:primitive")
      },
      response: { error: RetVal("nullable:json") }
    },
    getParam: {
      request: {
        param: Arg(0, "string")
      },
      response: { text: RetVal("nullable:primitive") }
    },
    getParamFlags: {
      request: { param: Arg(0, "string") },
      response: { flags: RetVal("nullable:primitive") }
    },
    getParams: {
      response: { params: RetVal("json") }
    },
    connectParam: {
      request: {
        destActor: Arg(0, "audionode"),
        paramName: Arg(1, "string"),
        output: Arg(2, "nullable:number")
      },
      response: { error: RetVal("nullable:json") }
    },
    connectNode: {
      request: {
        destActor: Arg(0, "audionode"),
        output: Arg(1, "nullable:number"),
        input: Arg(2, "nullable:number")
      },
      response: { error: RetVal("nullable:json") }
    },
    disconnect: {
      request: { output: Arg(0, "nullable:number") },
      response: { error: RetVal("nullable:json") }
    },
    getAutomationData: {
      request: { paramName: Arg(0, "string") },
      response: { values: RetVal("nullable:json") }
    },
    addAutomationEvent: {
      request: {
        paramName: Arg(0, "string"),
        eventName: Arg(1, "string"),
        args: Arg(2, "nullable:json")
      },
      response: { error: RetVal("nullable:json") }
    },
  }
});

exports.audionodeSpec = audionodeSpec;

const webAudioSpec = generateActorSpec({
  typeName: "webaudio",

  /**
   * Events emitted by this actor.
   */
  events: {
    "start-context": {
      type: "startContext"
    },
    "connect-node": {
      type: "connectNode",
      source: Option(0, "audionode"),
      dest: Option(0, "audionode")
    },
    "disconnect-node": {
      type: "disconnectNode",
      source: Arg(0, "audionode")
    },
    "connect-param": {
      type: "connectParam",
      source: Option(0, "audionode"),
      dest: Option(0, "audionode"),
      param: Option(0, "string")
    },
    "change-param": {
      type: "changeParam",
      source: Option(0, "audionode"),
      param: Option(0, "string"),
      value: Option(0, "string")
    },
    "create-node": {
      type: "createNode",
      source: Arg(0, "audionode")
    },
    "destroy-node": {
      type: "destroyNode",
      source: Arg(0, "audionode")
    },
    "automation-event": {
      type: "automationEvent",
      node: Option(0, "audionode"),
      paramName: Option(0, "string"),
      eventName: Option(0, "string"),
      args: Option(0, "json")
    }
  },

  methods: {
    getDefinition: {
      response: { definition: RetVal("json") }
    },
    setup: {
      request: { reload: Option(0, "boolean") },
      oneway: true
    },
    finalize: {
      oneway: true
    }
  }
});

exports.webAudioSpec = webAudioSpec;
PK
!<j.-�
�
Nchrome/devtools/modules/devtools/shared/specs/webextension-inspected-window.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {
  Arg,
  RetVal,
  generateActorSpec,
  types,
} = require("devtools/shared/protocol");

/**
 * Sent with the eval and reload requests, used to inform the
 * webExtensionInspectedWindowActor about the caller information
 * to be able to evaluate code as being executed from the caller
 * WebExtension sources, or log errors with information that can
 * help the addon developer to more easily identify the affected
 * lines in his own addon code.
 */
types.addDictType("webExtensionCallerInfo", {
  // Information related to the line of code that has originated
  // the request.
  url: "string",
  lineNumber: "nullable:number",

  // The called addonId.
  addonId: "string",
});

/**
 * RDP type related to the inspectedWindow.eval method request.
 */
types.addDictType("webExtensionEvalOptions", {
  frameURL: "nullable:string",
  contextSecurityOrigin: "nullable:string",
  useContentScriptContext: "nullable:boolean",

  // Return the evalResult as a grip (used by the WebExtensions
  // devtools inspector's sidebar.setExpression API method).
  evalResultAsGrip: "nullable:boolean",

  // The actor ID of the node selected in the inspector if any,
  // used to provide the '$0' binding.
  toolboxSelectedNodeActorID: "nullable:string",

  // The actor ID of the console actor,
  // used to provide the 'inspect' binding.
  toolboxConsoleActorID: "nullable:string",
});

/**
 * RDP type related to the inspectedWindow.eval method result errors.
 *
 * This type has been modelled on the same data format
 * used in the corresponding chrome API method.
 */
types.addDictType("webExtensionEvalExceptionInfo", {
  // The following properties are set if the error has not occurred
  // in the evaluated JS code.
  isError: "nullable:boolean",
  code: "nullable:string",
  description: "nullable:string",
  details: "nullable:array:json",

  // The following properties are set if the error has occurred
  // in the evaluated JS code.
  isException: "nullable:string",
  value: "nullable:string",
});

/**
 * RDP type related to the inspectedWindow.eval method result.
 */
types.addDictType("webExtensionEvalResult", {
  // The following properties are set if the evaluation has been
  // completed successfully.
  value: "nullable:json",
  valueGrip: "nullable:json",
  // The following properties are set if the evalutation has been
  // completed with errors.
  exceptionInfo: "nullable:webExtensionEvalExceptionInfo",
});

/**
 * RDP type related to the inspectedWindow.reload method request.
 */
types.addDictType("webExtensionReloadOptions", {
  ignoreCache: "nullable:boolean",
  userAgent: "nullable:string",
  injectedScript: "nullable:string",
});

const webExtensionInspectedWindowSpec = generateActorSpec({
  typeName: "webExtensionInspectedWindow",

  methods: {
    reload: {
      request: {
        webExtensionCallerInfo: Arg(0, "webExtensionCallerInfo"),
        options: Arg(1, "webExtensionReloadOptions"),
      },
    },
    eval: {
      request: {
        webExtensionCallerInfo: Arg(0, "webExtensionCallerInfo"),
        expression: Arg(1, "string"),
        options: Arg(2, "webExtensionEvalOptions"),
      },

      response: {
        evalResult: RetVal("webExtensionEvalResult"),
      },
    },
  },
});

exports.webExtensionInspectedWindowSpec = webExtensionInspectedWindowSpec;
PK
!<t`�bbDchrome/devtools/modules/devtools/shared/specs/webextension-parent.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {RetVal, generateActorSpec} = require("devtools/shared/protocol");

const webExtensionSpec = generateActorSpec({
  typeName: "webExtensionAddon",

  methods: {
    reload: {
      request: { },
      response: { addon: RetVal("json") },
    },

    connect: {
      request: { },
      response: { form: RetVal("json") },
    },
  },
});

exports.webExtensionSpec = webExtensionSpec;
PK
!<������6chrome/devtools/modules/devtools/shared/specs/webgl.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {Arg, Option, RetVal, generateActorSpec} = require("devtools/shared/protocol");

const shaderSpec = generateActorSpec({
  typeName: "gl-shader",

  methods: {
    getText: {
      response: { text: RetVal("string") }
    },
    compile: {
      request: { text: Arg(0, "string") },
      response: { error: RetVal("nullable:json") }
    },
  },
});

exports.shaderSpec = shaderSpec;

const programSpec = generateActorSpec({
  typeName: "gl-program",

  methods: {
    getVertexShader: {
      response: { shader: RetVal("gl-shader") }
    },
    getFragmentShader: {
      response: { shader: RetVal("gl-shader") }
    },
    highlight: {
      request: { tint: Arg(0, "array:number") },
      oneway: true
    },
    unhighlight: {
      oneway: true
    },
    blackbox: {
      oneway: true
    },
    unblackbox: {
      oneway: true
    },
  }
});

exports.programSpec = programSpec;

const webGLSpec = generateActorSpec({
  typeName: "webgl",

  /**
   * Events emitted by this actor. The "program-linked" event is fired every
   * time a WebGL program was linked with its respective two shaders.
   */
  events: {
    "program-linked": {
      type: "programLinked",
      program: Arg(0, "gl-program")
    },
    "global-destroyed": {
      type: "globalDestroyed",
      program: Arg(0, "number")
    },
    "global-created": {
      type: "globalCreated",
      program: Arg(0, "number")
    }
  },

  methods: {
    setup: {
      request: { reload: Option(0, "boolean") },
      oneway: true
    },
    finalize: {
      oneway: true
    },
    getPrograms: {
      response: { programs: RetVal("array:gl-program") }
    },
    waitForFrame: {
      response: { success: RetVal("nullable:json") }
    },
    getPixel: {
      request: {
        selector: Option(0, "string"),
        position: Option(0, "json")
      },
      response: { pixels: RetVal("json") }
    },
    _getAllPrograms: {
      response: { programs: RetVal("array:gl-program") }
    }
  }
});

exports.webGLSpec = webGLSpec;
PK
!<���{{7chrome/devtools/modules/devtools/shared/specs/worker.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 {Arg, RetVal, generateActorSpec} = require("devtools/shared/protocol");

const workerSpec = generateActorSpec({
  typeName: "worker",

  methods: {
    attach: {
      request: {},
      response: RetVal("json")
    },
    detach: {
      request: {},
      response: RetVal("json")
    },
    connect: {
      request: {
        options: Arg(0, "json"),
      },
      response: RetVal("json")
    },
    push: {
      request: {},
      response: RetVal("json")
    },
  },
});

exports.workerSpec = workerSpec;

const pushSubscriptionSpec = generateActorSpec({
  typeName: "pushSubscription",
});

exports.pushSubscriptionSpec = pushSubscriptionSpec;

const serviceWorkerRegistrationSpec = generateActorSpec({
  typeName: "serviceWorkerRegistration",

  events: {
    "push-subscription-modified": {
      type: "push-subscription-modified"
    },
    "registration-changed": {
      type: "registration-changed"
    }
  },

  methods: {
    start: {
      request: {},
      response: RetVal("json")
    },
    unregister: {
      request: {},
      response: RetVal("json")
    },
    getPushSubscription: {
      request: {},
      response: {
        subscription: RetVal("nullable:pushSubscription")
      }
    },
  },
});

exports.serviceWorkerRegistrationSpec = serviceWorkerRegistrationSpec;

const serviceWorkerSpec = generateActorSpec({
  typeName: "serviceWorker",
});

exports.serviceWorkerSpec = serviceWorkerSpec;

PK
!<�� �.�.<chrome/devtools/modules/devtools/shared/sprintfjs/sprintf.js/**
 * Copyright (c) 2007-2016, Alexandru Marasteanu <hello [at) alexei (dot] ro>
 * 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 this software 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 AUTHORS OR COPYRIGHT HOLDERS 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.
 *
 */

/* eslint-disable */
/* globals window, exports, define */

(function(window) {
    'use strict'

    var re = {
        not_string: /[^s]/,
        not_bool: /[^t]/,
        not_type: /[^T]/,
        not_primitive: /[^v]/,
        number: /[diefg]/,
        numeric_arg: /bcdiefguxX/,
        json: /[j]/,
        not_json: /[^j]/,
        text: /^[^\x25]+/,
        modulo: /^\x25{2}/,
        placeholder: /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-gijosStTuvxX])/,
        key: /^([a-z_][a-z_\d]*)/i,
        key_access: /^\.([a-z_][a-z_\d]*)/i,
        index_access: /^\[(\d+)\]/,
        sign: /^[\+\-]/
    }

    function sprintf() {
        var key = arguments[0], cache = sprintf.cache
        if (!(cache[key] && cache.hasOwnProperty(key))) {
            cache[key] = sprintf.parse(key)
        }
        return sprintf.format.call(null, cache[key], arguments)
    }

    sprintf.format = function(parse_tree, argv) {
        var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length, is_positive = true, sign = ''
        for (i = 0; i < tree_length; i++) {
            node_type = typeof parse_tree[i]
            // The items of parse tree are either strings or results of a match() call.
            if (node_type === 'string') {
                // this is not a placeholder, this is just a string.
                output[output.length] = parse_tree[i]
            }
            else {
                // this is a placeholder, need to identify its type, options and replace
                // it with the appropriate argument.
                match = parse_tree[i] // convenience purposes only
                if (match[2]) { // keyword argument
                    arg = argv[cursor]
                    for (k = 0; k < match[2].length; k++) {
                        if (!arg.hasOwnProperty(match[2][k])) {
                            throw new Error(sprintf('[sprintf] property "%s" does not exist', match[2][k]))
                        }
                        arg = arg[match[2][k]]
                    }
                }
                else if (match[1]) { // positional argument (explicit)
                    arg = argv[match[1]]
                }
                else { // positional argument (implicit)
                    arg = argv[cursor++]
                }

                // The most commonly used placeholder in DevTools is the string (%S or %s).
                // We check it first to avoid unnecessary verifications.
                let hasPadding = match[6];
                let patternType = match[8];
                if (!hasPadding && (patternType === "S" || patternType === "s")) {
                    if (typeof arg === "function") {
                        arg = arg();
                    }
                    if (typeof arg !== "string") {
                        arg = String(arg);
                    }
                    output[output.length] = match[7] ? arg.substring(0, match[7]) : arg;
                    continue;
                }

                if (re.not_type.test(match[8]) && re.not_primitive.test(match[8]) && typeof arg == 'function') {
                    arg = arg()
                }

                if (re.numeric_arg.test(match[8]) && (typeof arg != 'number' && isNaN(arg))) {
                    throw new TypeError(sprintf("[sprintf] expecting number but found %s", typeof arg))
                }

                if (re.number.test(match[8])) {
                    is_positive = arg >= 0
                }

                switch (match[8]) {
                    case 'b':
                        arg = parseInt(arg, 10).toString(2)
                    break
                    case 'c':
                        arg = String.fromCharCode(parseInt(arg, 10))
                    break
                    case 'd':
                    case 'i':
                        arg = parseInt(arg, 10)
                    break
                    case 'j':
                        arg = JSON.stringify(arg, null, match[6] ? parseInt(match[6]) : 0)
                    break
                    case 'e':
                        arg = match[7] ? parseFloat(arg).toExponential(match[7]) : parseFloat(arg).toExponential()
                    break
                    case 'f':
                        arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg)
                    break
                    case 'g':
                        arg = match[7] ? parseFloat(arg).toPrecision(match[7]) : parseFloat(arg)
                    break
                    case 'o':
                        arg = arg.toString(8)
                    break
                    case 's':
                    case 'S':
                        arg = String(arg)
                        arg = (match[7] ? arg.substring(0, match[7]) : arg)
                    break
                    case 't':
                        arg = String(!!arg)
                        arg = (match[7] ? arg.substring(0, match[7]) : arg)
                    break
                    case 'T':
                        arg = Object.prototype.toString.call(arg).slice(8, -1).toLowerCase()
                        arg = (match[7] ? arg.substring(0, match[7]) : arg)
                    break
                    case 'u':
                        arg = parseInt(arg, 10) >>> 0
                    break
                    case 'v':
                        arg = arg.valueOf()
                        arg = (match[7] ? arg.substring(0, match[7]) : arg)
                    break
                    case 'x':
                        arg = parseInt(arg, 10).toString(16)
                    break
                    case 'X':
                        arg = parseInt(arg, 10).toString(16).toUpperCase()
                    break
                }
                if (re.json.test(match[8])) {
                    output[output.length] = arg
                }
                else {
                    if (re.number.test(match[8]) && (!is_positive || match[3])) {
                        sign = is_positive ? '+' : '-'
                        arg = arg.toString().replace(re.sign, '')
                    }
                    else {
                        sign = ''
                    }
                    pad_character = match[4] ? match[4] === '0' ? '0' : match[4].charAt(1) : ' '
                    pad_length = match[6] - (sign + arg).length
                    pad = match[6] ? (pad_length > 0 ? str_repeat(pad_character, pad_length) : '') : ''
                    output[output.length] = match[5] ? sign + arg + pad : (pad_character === '0' ? sign + pad + arg : pad + sign + arg)
                }
            }
        }
        return output.join('')
    }

    sprintf.cache = {}

    sprintf.parse = function(fmt) {
        var _fmt = fmt, match = [], parse_tree = [], arg_names = 0
        while (_fmt) {
            if ((match = re.text.exec(_fmt)) !== null) {
                parse_tree[parse_tree.length] = match[0]
            }
            else if ((match = re.modulo.exec(_fmt)) !== null) {
                parse_tree[parse_tree.length] = '%'
            }
            else if ((match = re.placeholder.exec(_fmt)) !== null) {
                if (match[2]) {
                    arg_names |= 1
                    var field_list = [], replacement_field = match[2], field_match = []
                    if ((field_match = re.key.exec(replacement_field)) !== null) {
                        field_list[field_list.length] = field_match[1]
                        while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
                            if ((field_match = re.key_access.exec(replacement_field)) !== null) {
                                field_list[field_list.length] = field_match[1]
                            }
                            else if ((field_match = re.index_access.exec(replacement_field)) !== null) {
                                field_list[field_list.length] = field_match[1]
                            }
                            else {
                                throw new SyntaxError("[sprintf] failed to parse named argument key")
                            }
                        }
                    }
                    else {
                        throw new SyntaxError("[sprintf] failed to parse named argument key")
                    }
                    match[2] = field_list
                }
                else {
                    arg_names |= 2
                }
                if (arg_names === 3) {
                    throw new Error("[sprintf] mixing positional and named placeholders is not (yet) supported")
                }
                parse_tree[parse_tree.length] = match
            }
            else {
                throw new SyntaxError("[sprintf] unexpected placeholder")
            }
            _fmt = _fmt.substring(match[0].length)
        }
        return parse_tree
    }

    var vsprintf = function(fmt, argv, _argv) {
        _argv = (argv || []).slice(0)
        _argv.splice(0, 0, fmt)
        return sprintf.apply(null, _argv)
    }

    /**
     * helpers
     */

    var preformattedPadding = {
        '0': ['', '0', '00', '000', '0000', '00000', '000000', '0000000'],
        ' ': ['', ' ', '  ', '   ', '    ', '     ', '      ', '       '],
        '_': ['', '_', '__', '___', '____', '_____', '______', '_______'],
    }
    function str_repeat(input, multiplier) {
        if (multiplier >= 0 && multiplier <= 7 && preformattedPadding[input]) {
            return preformattedPadding[input][multiplier]
        }
        return Array(multiplier + 1).join(input)
    }

    /**
     * export to either browser or node.js
     */
    if (typeof exports !== 'undefined') {
        exports.sprintf = sprintf
        exports.vsprintf = vsprintf
    }
    else {
        window.sprintf = sprintf
        window.vsprintf = vsprintf

        if (typeof define === 'function' && define.amd) {
            define(function() {
                return {
                    sprintf: sprintf,
                    vsprintf: vsprintf
                }
            })
        }
    }
})(typeof window === 'undefined' ? this : window);
PK
!<v.j8"8"1chrome/devtools/modules/devtools/shared/system.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Cc, Ci } = require("chrome");

loader.lazyRequireGetter(this, "Services");
loader.lazyRequireGetter(this, "promise");
loader.lazyRequireGetter(this, "defer", "devtools/shared/defer");
loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true);
loader.lazyRequireGetter(this, "AppConstants",
  "resource://gre/modules/AppConstants.jsm", true);
loader.lazyGetter(this, "screenManager", () => {
  return Cc["@mozilla.org/gfx/screenmanager;1"].getService(Ci.nsIScreenManager);
});
loader.lazyGetter(this, "oscpu", () => {
  return Cc["@mozilla.org/network/protocol;1?name=http"]
           .getService(Ci.nsIHttpProtocolHandler).oscpu;
});
loader.lazyGetter(this, "hostname", () => {
  try {
    // On some platforms (Linux according to try), this service does not exist and fails.
    return Cc["@mozilla.org/network/dns-service;1"]
              .getService(Ci.nsIDNSService).myHostName;
  } catch (e) {
    return "";
  }
});
loader.lazyGetter(this, "endianness", () => {
  if ((new Uint32Array((new Uint8Array([1, 2, 3, 4])).buffer))[0] === 0x04030201) {
    return "LE";
  }
  return "BE";
});

const APP_MAP = {
  "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}": "firefox",
  "{3550f703-e582-4d05-9a08-453d09bdfdc6}": "thunderbird",
  "{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}": "seamonkey",
  "{718e30fb-e89b-41dd-9da7-e25a45638b28}": "sunbird",
  "{3c2e2abc-06d4-11e1-ac3b-374f68613e61}": "b2g",
  "{aa3c5121-dab2-40e2-81ca-7ea25febc110}": "mobile/android",
  "{a23983c0-fd0e-11dc-95ff-0800200c9a66}": "mobile/xul"
};

var CACHED_INFO = null;

async function getSystemInfo() {
  if (CACHED_INFO) {
    return CACHED_INFO;
  }

  let appInfo = Services.appinfo;
  let win = Services.wm.getMostRecentWindow(DebuggerServer.chromeWindowType);
  let [processor, compiler] = appInfo.XPCOMABI.split("-");
  let dpi,
    useragent,
    width,
    height,
    physicalWidth,
    physicalHeight,
    os,
    brandName;
  let appid = appInfo.ID;
  let apptype = APP_MAP[appid];
  let geckoVersion = appInfo.platformVersion;
  let hardware = "unknown";
  let version = "unknown";

  // B2G specific
  if (apptype === "b2g") {
    os = "B2G";
    // `getSetting` does not work in child processes on b2g.
    // TODO bug 1205797, make this work in child processes.
    try {
      hardware = await exports.getSetting("deviceinfo.hardware");
      version = await exports.getSetting("deviceinfo.os");
    } catch (e) {
      // Ignore.
    }
  } else {
    // Not B2G
    os = appInfo.OS;
    version = appInfo.version;
  }

  let bundle = Services.strings.createBundle("chrome://branding/locale/brand.properties");
  if (bundle) {
    brandName = bundle.GetStringFromName("brandFullName");
  } else {
    brandName = null;
  }

  if (win) {
    let utils = win.QueryInterface(Ci.nsIInterfaceRequestor)
                   .getInterface(Ci.nsIDOMWindowUtils);
    dpi = utils.displayDPI;
    useragent = win.navigator.userAgent;
    width = win.screen.width;
    height = win.screen.height;
    physicalWidth = win.screen.width * win.devicePixelRatio;
    physicalHeight = win.screen.height * win.devicePixelRatio;
  }

  let info = {

    /**
     * Information from nsIXULAppInfo, regarding
     * the application itself.
     */

    // The XUL application's UUID.
    appid,

    // Name of the app, "firefox", "thunderbird", etc., listed in APP_MAP
    apptype,

    // Mixed-case or empty string of vendor, like "Mozilla"
    vendor: appInfo.vendor,

    // Name of the application, like "Firefox", "Thunderbird".
    name: appInfo.name,

    // The application's version, for example "0.8.0+" or "3.7a1pre".
    // Typically, the version of Firefox, for example.
    // It is different than the version of Gecko or the XULRunner platform.
    // On B2G, this is the Gaia version.
    version,

    // The application's build ID/date, for example "2004051604".
    appbuildid: appInfo.appBuildID,

    // The build ID/date of Gecko and the XULRunner platform.
    platformbuildid: appInfo.platformBuildID,
    geckobuildid: appInfo.platformBuildID,

    // The version of Gecko or XULRunner platform, for example "1.8.1.19" or
    // "1.9.3pre". In "Firefox 3.7 alpha 1" the application version is "3.7a1pre"
    // while the platform version is "1.9.3pre"
    platformversion: geckoVersion,
    geckoversion: geckoVersion,

    // Locale used in this build
    locale: Services.locale.getAppLocaleAsLangTag(),

    /**
     * Information regarding the operating system.
     */

    // Returns the endianness of the architecture: either "LE" or "BE"
    endianness: endianness,

    // Returns the hostname of the machine
    hostname: hostname,

    // Name of the OS type. Typically the same as `uname -s`. Possible values:
    // https://developer.mozilla.org/en/OS_TARGET
    // Also may be "B2G".
    os,
    platform: os,

    // hardware and version info from `deviceinfo.hardware`
    // and `deviceinfo.os`.
    hardware,

    // Type of process architecture running:
    // "arm", "ia32", "x86", "x64"
    // Alias to both `arch` and `processor` for node/deviceactor compat
    arch: processor,
    processor,

    // Name of compiler used for build:
    // `'msvc', 'n32', 'gcc2', 'gcc3', 'sunc', 'ibmc'...`
    compiler,

    // Location for the current profile
    profile: getProfileLocation(),

    // Update channel
    channel: AppConstants.MOZ_UPDATE_CHANNEL,

    dpi,
    useragent,
    width,
    height,
    physicalWidth,
    physicalHeight,
    brandName,
  };

  CACHED_INFO = info;
  return info;
}

function getProfileLocation() {
  // In child processes, we cannot access the profile location.
  try {
    let profd = Services.dirsvc.get("ProfD", Ci.nsIFile);
    let profservice = Cc["@mozilla.org/toolkit/profile-service;1"]
                        .getService(Ci.nsIToolkitProfileService);
    let profiles = profservice.profiles;
    while (profiles.hasMoreElements()) {
      let profile = profiles.getNext().QueryInterface(Ci.nsIToolkitProfile);
      if (profile.rootDir.path == profd.path) {
        return profile.name;
      }
    }

    return profd.leafName;
  } catch (e) {
    return "";
  }
}

/**
 * Function for fetching screen dimensions and returning
 * an enum for Telemetry.
 */
function getScreenDimensions() {
  let width = {};
  let height = {};

  screenManager.primaryScreen.GetRect({}, {}, width, height);
  let dims = width.value + "x" + height.value;

  if (width.value < 800 || height.value < 600) {
    return 0;
  }
  if (dims === "800x600") {
    return 1;
  }
  if (dims === "1024x768") {
    return 2;
  }
  if (dims === "1280x800") {
    return 3;
  }
  if (dims === "1280x1024") {
    return 4;
  }
  if (dims === "1366x768") {
    return 5;
  }
  if (dims === "1440x900") {
    return 6;
  }
  if (dims === "1920x1080") {
    return 7;
  }
  if (dims === "2560×1440") {
    return 8;
  }
  if (dims === "2560×1600") {
    return 9;
  }
  if (dims === "2880x1800") {
    return 10;
  }
  if (width.value > 2880 || height.value > 1800) {
    return 12;
  }

  // Other dimension such as a VM.
  return 11;
}

/**
 * Function for fetching OS CPU and returning
 * an enum for Telemetry.
 */
function getOSCPU() {
  if (oscpu.includes("NT 5.1") || oscpu.includes("NT 5.2")) {
    return 0;
  }
  if (oscpu.includes("NT 6.0")) {
    return 1;
  }
  if (oscpu.includes("NT 6.1")) {
    return 2;
  }
  if (oscpu.includes("NT 6.2")) {
    return 3;
  }
  if (oscpu.includes("NT 6.3")) {
    return 4;
  }
  if (oscpu.includes("OS X")) {
    return 5;
  }
  if (oscpu.includes("Linux")) {
    return 6;
  }
  if (oscpu.includes("NT 10.")) {
    return 7;
  }
  // Other OS.
  return 12;
}

function getSetting(name) {
  let deferred = defer();

  if ("@mozilla.org/settingsService;1" in Cc) {
    let settingsService;

    // settingsService fails in b2g child processes
    // TODO bug 1205797, make this work in child processes.
    try {
      settingsService = Cc["@mozilla.org/settingsService;1"]
                          .getService(Ci.nsISettingsService);
    } catch (e) {
      return promise.reject(e);
    }

    settingsService.createLock().get(name, {
      handle: (_, value) => deferred.resolve(value),
      handleError: (error) => deferred.reject(error),
    });
  } else {
    deferred.reject(new Error("No settings service"));
  }
  return deferred.promise;
}

exports.getSystemInfo = getSystemInfo;
exports.getSetting = getSetting;
exports.getScreenDimensions = getScreenDimensions;
exports.getOSCPU = getOSCPU;
exports.constants = AppConstants;
PK
!<���B�B/chrome/devtools/modules/devtools/shared/task.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

/* eslint-disable spaced-comment */
/* globals StopIteration */

/**
 * This module implements a subset of "Task.js" <http://taskjs.org/>.
 * It is a copy of toolkit/modules/Task.jsm.  Please try not to
 * diverge the API here.
 *
 * Paraphrasing from the Task.js site, tasks make sequential, asynchronous
 * operations simple, using the power of JavaScript's "yield" operator.
 *
 * Tasks are built upon generator functions and promises, documented here:
 *
 * <https://developer.mozilla.org/en/JavaScript/Guide/Iterators_and_Generators>
 * <http://wiki.commonjs.org/wiki/Promises/A>
 *
 * The "Task.spawn" function takes a generator function and starts running it as
 * a task.  Every time the task yields a promise, it waits until the promise is
 * fulfilled.  "Task.spawn" returns a promise that is resolved when the task
 * completes successfully, or is rejected if an exception occurs.
 *
 * -----------------------------------------------------------------------------
 *
 * const {Task} = require("devtools/shared/task");
 *
 * Task.spawn(function* () {
 *
 *   // This is our task. Let's create a promise object, wait on it and capture
 *   // its resolution value.
 *   let myPromise = getPromiseResolvedOnTimeoutWithValue(1000, "Value");
 *   let result = yield myPromise;
 *
 *   // This part is executed only after the promise above is fulfilled (after
 *   // one second, in this imaginary example).  We can easily loop while
 *   // calling asynchronous functions, and wait multiple times.
 *   for (let i = 0; i < 3; i++) {
 *     result += yield getPromiseResolvedOnTimeoutWithValue(50, "!");
 *   }
 *
 *   return "Resolution result for the task: " + result;
 * }).then(function (result) {
 *
 *   // result == "Resolution result for the task: Value!!!"
 *
 *   // The result is undefined if no value was returned.
 *
 * }, function (exception) {
 *
 *   // Failure!  We can inspect or report the exception.
 *
 * });
 *
 * -----------------------------------------------------------------------------
 *
 * This module implements only the "Task.js" interfaces described above, with no
 * additional features to control the task externally, or do custom scheduling.
 * It also provides the following extensions that simplify task usage in the
 * most common cases:
 *
 * - The "Task.spawn" function also accepts an iterator returned by a generator
 *   function, in addition to a generator function.  This way, you can call into
 *   the generator function with the parameters you want, and with "this" bound
 *   to the correct value.  Also, "this" is never bound to the task object when
 *   "Task.spawn" calls the generator function.
 *
 * - In addition to a promise object, a task can yield the iterator returned by
 *   a generator function.  The iterator is turned into a task automatically.
 *   This reduces the syntax overhead of calling "Task.spawn" explicitly when
 *   you want to recurse into other task functions.
 *
 * - The "Task.spawn" function also accepts a primitive value, or a function
 *   returning a primitive value, and treats the value as the result of the
 *   task.  This makes it possible to call an externally provided function and
 *   spawn a task from it, regardless of whether it is an asynchronous generator
 *   or a synchronous function.  This comes in handy when iterating over
 *   function lists where some items have been converted to tasks and some not.
 */

////////////////////////////////////////////////////////////////////////////////
//// Globals

const defer = require("devtools/shared/defer");

// The following error types are considered programmer errors, which should be
// reported (possibly redundantly) so as to let programmers fix their code.
const ERRORS_TO_REPORT = ["EvalError", "RangeError", "ReferenceError",
                          "TypeError"];

/**
 * The Task currently being executed
 */
var gCurrentTask = null;

/**
 * If `true`, capture stacks whenever entering a Task and rewrite the
 * stack any exception thrown through a Task.
 */
var gMaintainStack = false;

/**
 * Iterate through the lines of a string.
 *
 * @return Iterator<string>
 */
function* linesOf(string) {
  let reLine = /([^\r\n])+/g;
  let match;
  while ((match = reLine.exec(string))) {
    yield [match[0], match.index];
  }
}

/**
 * Detect whether a value is a generator.
 *
 * @param aValue
 *        The value to identify.
 * @return A boolean indicating whether the value is a generator.
 */
function isGenerator(value) {
  return Object.prototype.toString.call(value) == "[object Generator]";
}

////////////////////////////////////////////////////////////////////////////////
//// Task

/**
 * This object provides the public module functions.
 */
var Task = {
  /**
   * Creates and starts a new task.
   *
   * @param task
   *        - If you specify a generator function, it is called with no
   *          arguments to retrieve the associated iterator.  The generator
   *          function is a task, that is can yield promise objects to wait
   *          upon.
   *        - If you specify the iterator returned by a generator function you
   *          called, the generator function is also executed as a task.  This
   *          allows you to call the function with arguments.
   *        - If you specify a function that is not a generator, it is called
   *          with no arguments, and its return value is used to resolve the
   *          returned promise.
   *        - If you specify anything else, you get a promise that is already
   *          resolved with the specified value.
   *
   * @return A promise object where you can register completion callbacks to be
   *         called when the task terminates.
   */
  spawn: function (task) {
    return createAsyncFunction(task)();
  },

  /**
   * Create and return an 'async function' that starts a new task.
   *
   * This is similar to 'spawn' except that it doesn't immediately start
   * the task, it binds the task to the async function's 'this' object and
   * arguments, and it requires the task to be a function.
   *
   * It simplifies the common pattern of implementing a method via a task,
   * like this simple object with a 'greet' method that has a 'name' parameter
   * and spawns a task to send a greeting and return its reply:
   *
   * let greeter = {
   *   message: "Hello, NAME!",
   *   greet: function(name) {
   *     return Task.spawn((function* () {
   *       return yield sendGreeting(this.message.replace(/NAME/, name));
   *     }).bind(this);
   *   })
   * };
   *
   * With Task.async, the method can be declared succinctly:
   *
   * let greeter = {
   *   message: "Hello, NAME!",
   *   greet: Task.async(function* (name) {
   *     return yield sendGreeting(this.message.replace(/NAME/, name));
   *   })
   * };
   *
   * While maintaining identical semantics:
   *
   * greeter.greet("Mitchell").then((reply) => { ... }); // behaves the same
   *
   * @param task
   *        The task function to start.
   *
   * @return A function that starts the task function and returns its promise.
   */
  async: function (task) {
    if (typeof (task) != "function") {
      throw new TypeError("task argument must be a function");
    }

    return createAsyncFunction(task);
  },

  /**
   * Constructs a special exception that, when thrown inside a legacy generator
   * function (non-star generator), allows the associated task to be resolved
   * with a specific value.
   *
   * Example: throw new Task.Result("Value");
   */
  Result: function (value) {
    this.value = value;
  }
};

function createAsyncFunction(task) {
  let asyncFunction = function () {
    let result = task;
    if (task && typeof (task) == "function") {
      if (task.isAsyncFunction) {
        throw new TypeError(
          "Cannot use an async function in place of a promise. " +
          "You should either invoke the async function first " +
          "or use 'Task.spawn' instead of 'Task.async' to start " +
          "the Task and return its promise.");
      }

      try {
        // Let's call into the function ourselves.
        result = task.apply(this, arguments);
      } catch (ex) {
        if (ex instanceof Task.Result) {
          return Promise.resolve(ex.value);
        }
        return Promise.reject(ex);
      }
    }

    if (isGenerator(result)) {
      // This is an iterator resulting from calling a generator function.
      return new TaskImpl(result).deferred.promise;
    }

    // Just propagate the given value to the caller as a resolved promise.
    return Promise.resolve(result);
  };

  asyncFunction.isAsyncFunction = true;

  return asyncFunction;
}

////////////////////////////////////////////////////////////////////////////////
//// TaskImpl

/**
 * Executes the specified iterator as a task, and gives access to the promise
 * that is fulfilled when the task terminates.
 */
function TaskImpl(iterator) {
  if (gMaintainStack) {
    this._stack = (new Error()).stack;
  }
  this.deferred = defer();
  this._iterator = iterator;
  this._isStarGenerator = !("send" in iterator);
  this._run(true);
}

TaskImpl.prototype = {
  /**
   * Includes the promise object where task completion callbacks are registered,
   * and methods to resolve or reject the promise at task completion.
   */
  deferred: null,

  /**
   * The iterator returned by the generator function associated with this task.
   */
  _iterator: null,

  /**
   * Whether this Task is using a star generator.
   */
  _isStarGenerator: false,

  /**
   * Main execution routine, that calls into the generator function.
   *
   * @param sendResolved
   *        If true, indicates that we should continue into the generator
   *        function regularly (if we were waiting on a promise, it was
   *        resolved). If true, indicates that we should cause an exception to
   *        be thrown into the generator function (if we were waiting on a
   *        promise, it was rejected).
   * @param sendValue
   *        Resolution result or rejection exception, if any.
   */
  _run: function (sendResolved, sendValue) {
    try {
      gCurrentTask = this;

      if (this._isStarGenerator) {
        try {
          let result = sendResolved ? this._iterator.next(sendValue)
                                    : this._iterator.throw(sendValue);

          if (result.done) {
            // The generator function returned.
            this.deferred.resolve(result.value);
          } else {
            // The generator function yielded.
            this._handleResultValue(result.value);
          }
        } catch (ex) {
          // The generator function failed with an uncaught exception.
          this._handleException(ex);
        }
      } else {
        try {
          let yielded = sendResolved ? this._iterator.send(sendValue)
                                     : this._iterator.throw(sendValue);
          this._handleResultValue(yielded);
        } catch (ex) {
          if (ex instanceof Task.Result) {
            // The generator function threw the special exception that
            // allows it to return a specific value on resolution.
            this.deferred.resolve(ex.value);
          } else if (ex instanceof StopIteration) {
            // The generator function terminated with no specific result.
            this.deferred.resolve(undefined);
          } else {
            // The generator function failed with an uncaught exception.
            this._handleException(ex);
          }
        }
      }
    } finally {
      //
      // At this stage, the Task may have finished executing, or have
      // walked through a `yield` or passed control to a sub-Task.
      // Regardless, if we still own `gCurrentTask`, reset it. If we
      // have not finished execution of this Task, re-entering `_run`
      // will set `gCurrentTask` to `this` as needed.
      //
      // We just need to be careful here in case we hit the following
      // pattern:
      //
      //   Task.spawn(foo);
      //   Task.spawn(bar);
      //
      // Here, `foo` and `bar` may be interleaved, so when we finish
      // executing `foo`, `gCurrentTask` may actually either `foo` or
      // `bar`. If `gCurrentTask` has already been set to `bar`, leave
      // it be and it will be reset to `null` once `bar` is complete.
      //
      if (gCurrentTask == this) {
        gCurrentTask = null;
      }
    }
  },

  /**
   * Handle a value yielded by a generator.
   *
   * @param value
   *        The yielded value to handle.
   */
  _handleResultValue: function (value) {
    // If our task yielded an iterator resulting from calling another
    // generator function, automatically spawn a task from it, effectively
    // turning it into a promise that is fulfilled on task completion.
    if (isGenerator(value)) {
      value = Task.spawn(value);
    }

    if (value && typeof (value.then) == "function") {
      // We have a promise object now. When fulfilled, call again into this
      // function to continue the task, with either a resolution or rejection
      // condition.
      value.then(this._run.bind(this, true),
                  this._run.bind(this, false));
    } else {
      // If our task yielded a value that is not a promise, just continue and
      // pass it directly as the result of the yield statement.
      this._run(true, value);
    }
  },

  /**
   * Handle an uncaught exception thrown from a generator.
   *
   * @param exception
   *        The uncaught exception to handle.
   */
  _handleException: function (exception) {
    gCurrentTask = this;

    if (exception && typeof exception == "object" && "stack" in exception) {
      let stack = exception.stack;

      if (gMaintainStack &&
          exception._capturedTaskStack != this._stack &&
          typeof stack == "string") {
        // Rewrite the stack for more readability.

        let bottomStack = this._stack;

        stack = Task.Debugging.generateReadableStack(stack);

        exception.stack = stack;

        // If exception is reinjected in the same task and rethrown,
        // we don't want to perform the rewrite again.
        exception._capturedTaskStack = bottomStack;
      } else if (!stack) {
        stack = "Not available";
      }

      if ("name" in exception &&
          ERRORS_TO_REPORT.includes(exception.name)) {
        // We suspect that the exception is a programmer error, so we now
        // display it using dump().  Note that we do not use Cu.reportError as
        // we assume that this is a programming error, so we do not want end
        // users to see it. Also, if the programmer handles errors correctly,
        // they will either treat the error or log them somewhere.

        dump("*************************\n");
        dump("A coding exception was thrown and uncaught in a Task.\n\n");
        dump("Full message: " + exception + "\n");
        dump("Full stack: " + exception.stack + "\n");
        dump("*************************\n");
      }
    }

    this.deferred.reject(exception);
  },

  get callerStack() {
    // Cut `this._stack` at the last line of the first block that
    // contains task.js, keep the tail.
    for (let [line, index] of linesOf(this._stack || "")) {
      if (!line.includes("/task.js:")) {
        return this._stack.substring(index);
      }
    }
    return "";
  }
};

Task.Debugging = {

  /**
   * Control stack rewriting.
   *
   * If `true`, any exception thrown from a Task will be rewritten to
   * provide a human-readable stack trace. Otherwise, stack traces will
   * be left unchanged.
   *
   * There is a (small but existing) runtime cost associated to stack
   * rewriting, so you should probably not activate this in production
   * code.
   *
   * @type {bool}
   */
  get maintainStack() {
    return gMaintainStack;
  },
  set maintainStack(x) {
    if (!x) {
      gCurrentTask = null;
    }
    gMaintainStack = x;
    return x;
  },

  /**
   * Generate a human-readable stack for an error raised in
   * a Task.
   *
   * @param {string} topStack The stack provided by the error.
   * @param {string=} prefix Optionally, a prefix for each line.
   */
  generateReadableStack: function (topStack, prefix = "") {
    if (!gCurrentTask) {
      return topStack;
    }

    // Cut `topStack` at the first line that contains task.js, keep the head.
    let lines = [];
    for (let [line] of linesOf(topStack)) {
      if (line.includes("/task.js:")) {
        break;
      }
      lines.push(prefix + line);
    }
    if (!prefix) {
      lines.push(gCurrentTask.callerStack);
    } else {
      for (let [line] of linesOf(gCurrentTask.callerStack)) {
        lines.push(prefix + line);
      }
    }

    return lines.join("\n");
  }
};

exports.Task = Task;
PK
!<[4�3��3chrome/devtools/modules/devtools/shared/throttle.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

/**
 * From underscore's `_.throttle`
 * http://underscorejs.org
 * (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
 * Underscore may be freely distributed under the MIT license.
 *
 * Returns a function, that, when invoked, will only be triggered at most once during a
 * given window of time. The throttled function will run as much as it can, without ever
 * going more than once per wait duration.
 *
 * @param  {Function} func
 *         The function to throttle
 * @param  {number} wait
 *         The wait period
 * @param  {Object} scope
 *         The scope to use for func
 * @return {Function} The throttled function
 */
function throttle(func, wait, scope) {
  let args, result;
  let timeout = null;
  let previous = 0;

  let later = function () {
    previous = Date.now();
    timeout = null;
    result = func.apply(scope, args);
    args = null;
  };

  return function () {
    let now = Date.now();
    let remaining = wait - (now - previous);
    args = arguments;
    if (remaining <= 0) {
      clearTimeout(timeout);
      timeout = null;
      previous = now;
      result = func.apply(scope, args);
      args = null;
    } else if (!timeout) {
      timeout = setTimeout(later, remaining);
    }
    return result;
  };
}

exports.throttle = throttle;
PK
!<��E�U/U/<chrome/devtools/modules/devtools/shared/transport/packets.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

/**
 * Packets contain read / write functionality for the different packet types
 * supported by the debugging protocol, so that a transport can focus on
 * delivery and queue management without worrying too much about the specific
 * packet types.
 *
 * They are intended to be "one use only", so a new packet should be
 * instantiated for each incoming or outgoing packet.
 *
 * A complete Packet type should expose at least the following:
 *   * read(stream, scriptableStream)
 *     Called when the input stream has data to read
 *   * write(stream)
 *     Called when the output stream is ready to write
 *   * get done()
 *     Returns true once the packet is done being read / written
 *   * destroy()
 *     Called to clean up at the end of use
 */

const { Cc, Ci } = require("chrome");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const { dumpn, dumpv } = DevToolsUtils;
const flags = require("devtools/shared/flags");
const StreamUtils = require("devtools/shared/transport/stream-utils");
const defer = require("devtools/shared/defer");

DevToolsUtils.defineLazyGetter(this, "unicodeConverter", () => {
  const unicodeConverter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
                           .createInstance(Ci.nsIScriptableUnicodeConverter);
  unicodeConverter.charset = "UTF-8";
  return unicodeConverter;
});

// The transport's previous check ensured the header length did not exceed 20
// characters.  Here, we opt for the somewhat smaller, but still large limit of
// 1 TiB.
const PACKET_LENGTH_MAX = Math.pow(2, 40);

/**
 * A generic Packet processing object (extended by two subtypes below).
 */
function Packet(transport) {
  this._transport = transport;
  this._length = 0;
}

/**
 * Attempt to initialize a new Packet based on the incoming packet header we've
 * received so far.  We try each of the types in succession, trying JSON packets
 * first since they are much more common.
 * @param header string
 *        The packet header string to attempt parsing.
 * @param transport DebuggerTransport
 *        The transport instance that will own the packet.
 * @return Packet
 *         The parsed packet of the matching type, or null if no types matched.
 */
Packet.fromHeader = function (header, transport) {
  return JSONPacket.fromHeader(header, transport) ||
         BulkPacket.fromHeader(header, transport);
};

Packet.prototype = {

  get length() {
    return this._length;
  },

  set length(length) {
    if (length > PACKET_LENGTH_MAX) {
      throw Error("Packet length " + length + " exceeds the max length of " +
                  PACKET_LENGTH_MAX);
    }
    this._length = length;
  },

  destroy: function () {
    this._transport = null;
  }

};

exports.Packet = Packet;

/**
 * With a JSON packet (the typical packet type sent via the transport), data is
 * transferred as a JSON packet serialized into a string, with the string length
 * prepended to the packet, followed by a colon ([length]:[packet]). The
 * contents of the JSON packet are specified in the Remote Debugging Protocol
 * specification.
 * @param transport DebuggerTransport
 *        The transport instance that will own the packet.
 */
function JSONPacket(transport) {
  Packet.call(this, transport);
  this._data = "";
  this._done = false;
}

/**
 * Attempt to initialize a new JSONPacket based on the incoming packet header
 * we've received so far.
 * @param header string
 *        The packet header string to attempt parsing.
 * @param transport DebuggerTransport
 *        The transport instance that will own the packet.
 * @return JSONPacket
 *         The parsed packet, or null if it's not a match.
 */
JSONPacket.fromHeader = function (header, transport) {
  let match = this.HEADER_PATTERN.exec(header);

  if (!match) {
    return null;
  }

  dumpv("Header matches JSON packet");
  let packet = new JSONPacket(transport);
  packet.length = +match[1];
  return packet;
};

JSONPacket.HEADER_PATTERN = /^(\d+):$/;

JSONPacket.prototype = Object.create(Packet.prototype);

Object.defineProperty(JSONPacket.prototype, "object", {
  /**
   * Gets the object (not the serialized string) being read or written.
   */
  get: function () {
    return this._object;
  },

  /**
   * Sets the object to be sent when write() is called.
   */
  set: function (object) {
    this._object = object;
    let data = JSON.stringify(object);
    this._data = unicodeConverter.ConvertFromUnicode(data);
    this.length = this._data.length;
  }
});

JSONPacket.prototype.read = function (stream, scriptableStream) {
  dumpv("Reading JSON packet");

  // Read in more packet data.
  this._readData(stream, scriptableStream);

  if (!this.done) {
    // Don't have a complete packet yet.
    return;
  }

  let json = this._data;
  try {
    json = unicodeConverter.ConvertToUnicode(json);
    this._object = JSON.parse(json);
  } catch (e) {
    let msg = "Error parsing incoming packet: " + json + " (" + e +
              " - " + e.stack + ")";
    console.error(msg);
    dumpn(msg);
    return;
  }

  this._transport._onJSONObjectReady(this._object);
};

JSONPacket.prototype._readData = function (stream, scriptableStream) {
  if (flags.wantVerbose) {
    dumpv("Reading JSON data: _l: " + this.length + " dL: " +
          this._data.length + " sA: " + stream.available());
  }
  let bytesToRead = Math.min(this.length - this._data.length,
                             stream.available());
  this._data += scriptableStream.readBytes(bytesToRead);
  this._done = this._data.length === this.length;
};

JSONPacket.prototype.write = function (stream) {
  dumpv("Writing JSON packet");

  if (this._outgoing === undefined) {
    // Format the serialized packet to a buffer
    this._outgoing = this.length + ":" + this._data;
  }

  let written = stream.write(this._outgoing, this._outgoing.length);
  this._outgoing = this._outgoing.slice(written);
  this._done = !this._outgoing.length;
};

Object.defineProperty(JSONPacket.prototype, "done", {
  get: function () {
    return this._done;
  }
});

JSONPacket.prototype.toString = function () {
  return JSON.stringify(this._object, null, 2);
};

exports.JSONPacket = JSONPacket;

/**
 * With a bulk packet, data is transferred by temporarily handing over the
 * transport's input or output stream to the application layer for writing data
 * directly.  This can be much faster for large data sets, and avoids various
 * stages of copies and data duplication inherent in the JSON packet type.  The
 * bulk packet looks like:
 *
 * bulk [actor] [type] [length]:[data]
 *
 * The interpretation of the data portion depends on the kind of actor and the
 * packet's type.  See the Remote Debugging Protocol Stream Transport spec for
 * more details.
 * @param transport DebuggerTransport
 *        The transport instance that will own the packet.
 */
function BulkPacket(transport) {
  Packet.call(this, transport);
  this._done = false;
  this._readyForWriting = defer();
}

/**
 * Attempt to initialize a new BulkPacket based on the incoming packet header
 * we've received so far.
 * @param header string
 *        The packet header string to attempt parsing.
 * @param transport DebuggerTransport
 *        The transport instance that will own the packet.
 * @return BulkPacket
 *         The parsed packet, or null if it's not a match.
 */
BulkPacket.fromHeader = function (header, transport) {
  let match = this.HEADER_PATTERN.exec(header);

  if (!match) {
    return null;
  }

  dumpv("Header matches bulk packet");
  let packet = new BulkPacket(transport);
  packet.header = {
    actor: match[1],
    type: match[2],
    length: +match[3]
  };
  return packet;
};

BulkPacket.HEADER_PATTERN = /^bulk ([^: ]+) ([^: ]+) (\d+):$/;

BulkPacket.prototype = Object.create(Packet.prototype);

BulkPacket.prototype.read = function (stream) {
  dumpv("Reading bulk packet, handing off input stream");

  // Temporarily pause monitoring of the input stream
  this._transport.pauseIncoming();

  let deferred = defer();

  this._transport._onBulkReadReady({
    actor: this.actor,
    type: this.type,
    length: this.length,
    copyTo: (output) => {
      dumpv("CT length: " + this.length);
      let copying = StreamUtils.copyStream(stream, output, this.length);
      deferred.resolve(copying);
      return copying;
    },
    stream: stream,
    done: deferred
  });

  // Await the result of reading from the stream
  deferred.promise.then(() => {
    dumpv("onReadDone called, ending bulk mode");
    this._done = true;
    this._transport.resumeIncoming();
  }, this._transport.close);

  // Ensure this is only done once
  this.read = () => {
    throw new Error("Tried to read() a BulkPacket's stream multiple times.");
  };
};

BulkPacket.prototype.write = function (stream) {
  dumpv("Writing bulk packet");

  if (this._outgoingHeader === undefined) {
    dumpv("Serializing bulk packet header");
    // Format the serialized packet header to a buffer
    this._outgoingHeader = "bulk " + this.actor + " " + this.type + " " +
                           this.length + ":";
  }

  // Write the header, or whatever's left of it to write.
  if (this._outgoingHeader.length) {
    dumpv("Writing bulk packet header");
    let written = stream.write(this._outgoingHeader,
                               this._outgoingHeader.length);
    this._outgoingHeader = this._outgoingHeader.slice(written);
    return;
  }

  dumpv("Handing off output stream");

  // Temporarily pause the monitoring of the output stream
  this._transport.pauseOutgoing();

  let deferred = defer();

  this._readyForWriting.resolve({
    copyFrom: (input) => {
      dumpv("CF length: " + this.length);
      let copying = StreamUtils.copyStream(input, stream, this.length);
      deferred.resolve(copying);
      return copying;
    },
    stream: stream,
    done: deferred
  });

  // Await the result of writing to the stream
  deferred.promise.then(() => {
    dumpv("onWriteDone called, ending bulk mode");
    this._done = true;
    this._transport.resumeOutgoing();
  }, this._transport.close);

  // Ensure this is only done once
  this.write = () => {
    throw new Error("Tried to write() a BulkPacket's stream multiple times.");
  };
};

Object.defineProperty(BulkPacket.prototype, "streamReadyForWriting", {
  get: function () {
    return this._readyForWriting.promise;
  }
});

Object.defineProperty(BulkPacket.prototype, "header", {
  get: function () {
    return {
      actor: this.actor,
      type: this.type,
      length: this.length
    };
  },

  set: function (header) {
    this.actor = header.actor;
    this.type = header.type;
    this.length = header.length;
  },
});

Object.defineProperty(BulkPacket.prototype, "done", {
  get: function () {
    return this._done;
  },
});

BulkPacket.prototype.toString = function () {
  return "Bulk: " + JSON.stringify(this.header, null, 2);
};

exports.BulkPacket = BulkPacket;

/**
 * RawPacket is used to test the transport's error handling of malformed
 * packets, by writing data directly onto the stream.
 * @param transport DebuggerTransport
 *        The transport instance that will own the packet.
 * @param data string
 *        The raw string to send out onto the stream.
 */
function RawPacket(transport, data) {
  Packet.call(this, transport);
  this._data = data;
  this.length = data.length;
  this._done = false;
}

RawPacket.prototype = Object.create(Packet.prototype);

RawPacket.prototype.read = function (stream) {
  // This hasn't yet been needed for testing.
  throw Error("Not implmented.");
};

RawPacket.prototype.write = function (stream) {
  let written = stream.write(this._data, this._data.length);
  this._data = this._data.slice(written);
  this._done = !this._data.length;
};

Object.defineProperty(RawPacket.prototype, "done", {
  get: function () {
    return this._done;
  }
});

exports.RawPacket = RawPacket;
PK
!<��z���Achrome/devtools/modules/devtools/shared/transport/stream-utils.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 { Ci, Cc, Cr, CC } = require("chrome");
const Services = require("Services");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const { dumpv } = DevToolsUtils;
const EventEmitter = require("devtools/shared/event-emitter");
const defer = require("devtools/shared/defer");

DevToolsUtils.defineLazyGetter(this, "IOUtil", () => {
  return Cc["@mozilla.org/io-util;1"].getService(Ci.nsIIOUtil);
});

DevToolsUtils.defineLazyGetter(this, "ScriptableInputStream", () => {
  return CC("@mozilla.org/scriptableinputstream;1",
            "nsIScriptableInputStream", "init");
});

const BUFFER_SIZE = 0x8000;

/**
 * This helper function (and its companion object) are used by bulk senders and
 * receivers to read and write data in and out of other streams.  Functions that
 * make use of this tool are passed to callers when it is time to read or write
 * bulk data.  It is highly recommended to use these copier functions instead of
 * the stream directly because the copier enforces the agreed upon length.
 * Since bulk mode reuses an existing stream, the sender and receiver must write
 * and read exactly the agreed upon amount of data, or else the entire transport
 * will be left in a invalid state.  Additionally, other methods of stream
 * copying (such as NetUtil.asyncCopy) close the streams involved, which would
 * terminate the debugging transport, and so it is avoided here.
 *
 * Overall, this *works*, but clearly the optimal solution would be able to just
 * use the streams directly.  If it were possible to fully implement
 * nsIInputStream / nsIOutputStream in JS, wrapper streams could be created to
 * enforce the length and avoid closing, and consumers could use familiar stream
 * utilities like NetUtil.asyncCopy.
 *
 * The function takes two async streams and copies a precise number of bytes
 * from one to the other.  Copying begins immediately, but may complete at some
 * future time depending on data size.  Use the returned promise to know when
 * it's complete.
 *
 * @param input nsIAsyncInputStream
 *        The stream to copy from.
 * @param output nsIAsyncOutputStream
 *        The stream to copy to.
 * @param length Integer
 *        The amount of data that needs to be copied.
 * @return Promise
 *         The promise is resolved when copying completes or rejected if any
 *         (unexpected) errors occur.
 */
function copyStream(input, output, length) {
  let copier = new StreamCopier(input, output, length);
  return copier.copy();
}

function StreamCopier(input, output, length) {
  EventEmitter.decorate(this);
  this._id = StreamCopier._nextId++;
  this.input = input;
  // Save off the base output stream, since we know it's async as we've required
  this.baseAsyncOutput = output;
  if (IOUtil.outputStreamIsBuffered(output)) {
    this.output = output;
  } else {
    this.output = Cc["@mozilla.org/network/buffered-output-stream;1"]
                  .createInstance(Ci.nsIBufferedOutputStream);
    this.output.init(output, BUFFER_SIZE);
  }
  this._length = length;
  this._amountLeft = length;
  this._deferred = defer();

  this._copy = this._copy.bind(this);
  this._flush = this._flush.bind(this);
  this._destroy = this._destroy.bind(this);

  // Copy promise's then method up to this object.
  // Allows the copier to offer a promise interface for the simple succeed or
  // fail scenarios, but also emit events (due to the EventEmitter) for other
  // states, like progress.
  this.then = this._deferred.promise.then.bind(this._deferred.promise);
  this.then(this._destroy, this._destroy);

  // Stream ready callback starts as |_copy|, but may switch to |_flush| at end
  // if flushing would block the output stream.
  this._streamReadyCallback = this._copy;
}
StreamCopier._nextId = 0;

StreamCopier.prototype = {

  copy: function () {
    // Dispatch to the next tick so that it's possible to attach a progress
    // event listener, even for extremely fast copies (like when testing).
    Services.tm.dispatchToMainThread(() => {
      try {
        this._copy();
      } catch (e) {
        this._deferred.reject(e);
      }
    });
    return this;
  },

  _copy: function () {
    let bytesAvailable = this.input.available();
    let amountToCopy = Math.min(bytesAvailable, this._amountLeft);
    this._debug("Trying to copy: " + amountToCopy);

    let bytesCopied;
    try {
      bytesCopied = this.output.writeFrom(this.input, amountToCopy);
    } catch (e) {
      if (e.result == Cr.NS_BASE_STREAM_WOULD_BLOCK) {
        this._debug("Base stream would block, will retry");
        this._debug("Waiting for output stream");
        this.baseAsyncOutput.asyncWait(this, 0, 0, Services.tm.currentThread);
        return;
      }
      throw e;
    }

    this._amountLeft -= bytesCopied;
    this._debug("Copied: " + bytesCopied +
                ", Left: " + this._amountLeft);
    this._emitProgress();

    if (this._amountLeft === 0) {
      this._debug("Copy done!");
      this._flush();
      return;
    }

    this._debug("Waiting for input stream");
    this.input.asyncWait(this, 0, 0, Services.tm.currentThread);
  },

  _emitProgress: function () {
    this.emit("progress", {
      bytesSent: this._length - this._amountLeft,
      totalBytes: this._length
    });
  },

  _flush: function () {
    try {
      this.output.flush();
    } catch (e) {
      if (e.result == Cr.NS_BASE_STREAM_WOULD_BLOCK ||
          e.result == Cr.NS_ERROR_FAILURE) {
        this._debug("Flush would block, will retry");
        this._streamReadyCallback = this._flush;
        this._debug("Waiting for output stream");
        this.baseAsyncOutput.asyncWait(this, 0, 0, Services.tm.currentThread);
        return;
      }
      throw e;
    }
    this._deferred.resolve();
  },

  _destroy: function () {
    this._destroy = null;
    this._copy = null;
    this._flush = null;
    this.input = null;
    this.output = null;
  },

  // nsIInputStreamCallback
  onInputStreamReady: function () {
    this._streamReadyCallback();
  },

  // nsIOutputStreamCallback
  onOutputStreamReady: function () {
    this._streamReadyCallback();
  },

  _debug: function (msg) {
    // Prefix logs with the copier ID, which makes logs much easier to
    // understand when several copiers are running simultaneously
    dumpv("Copier: " + this._id + " " + msg);
  }

};

/**
 * Read from a stream, one byte at a time, up to the next |delimiter|
 * character, but stopping if we've read |count| without finding it.  Reading
 * also terminates early if there are less than |count| bytes available on the
 * stream.  In that case, we only read as many bytes as the stream currently has
 * to offer.
 * TODO: This implementation could be removed if bug 984651 is fixed, which
 *       provides a native version of the same idea.
 * @param stream nsIInputStream
 *        The input stream to read from.
 * @param delimiter string
 *        The character we're trying to find.
 * @param count integer
 *        The max number of characters to read while searching.
 * @return string
 *         The data collected.  If the delimiter was found, this string will
 *         end with it.
 */
function delimitedRead(stream, delimiter, count) {
  dumpv("Starting delimited read for " + delimiter + " up to " +
        count + " bytes");

  let scriptableStream;
  if (stream instanceof Ci.nsIScriptableInputStream) {
    scriptableStream = stream;
  } else {
    scriptableStream = new ScriptableInputStream(stream);
  }

  let data = "";

  // Don't exceed what's available on the stream
  count = Math.min(count, stream.available());

  if (count <= 0) {
    return data;
  }

  let char;
  while (char !== delimiter && count > 0) {
    char = scriptableStream.readBytes(1);
    count--;
    data += char;
  }

  return data;
}

module.exports = {
  copyStream: copyStream,
  delimitedRead: delimitedRead
};
PK
!<Hw@�
w
w>chrome/devtools/modules/devtools/shared/transport/transport.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 Pipe, ScriptableInputStream, uneval */

// TODO: Get rid of this code once the marionette server loads transport.js as
// an SDK module (see bug 1000814)
(function (factory) {
  if (this.module && module.id.includes("transport")) {
    // require
    factory.call(this, require, exports);
  } else if (this.require) {
    // loadSubScript
    factory.call(this, require, this);
  } else {
    // Cu.import
    const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
    factory.call(this, require, this);
  }
}).call(this, function (require, exports) {
  const { Cc, Cr, CC } = require("chrome");
  const DevToolsUtils = require("devtools/shared/DevToolsUtils");
  const { dumpn, dumpv } = DevToolsUtils;
  const flags = require("devtools/shared/flags");
  const StreamUtils = require("devtools/shared/transport/stream-utils");
  const { Packet, JSONPacket, BulkPacket } =
  require("devtools/shared/transport/packets");
  const promise = require("promise");
  const defer = require("devtools/shared/defer");
  const EventEmitter = require("devtools/shared/event-emitter");

  DevToolsUtils.defineLazyGetter(this, "Pipe", () => {
    return CC("@mozilla.org/pipe;1", "nsIPipe", "init");
  });

  DevToolsUtils.defineLazyGetter(this, "ScriptableInputStream", () => {
    return CC("@mozilla.org/scriptableinputstream;1",
            "nsIScriptableInputStream", "init");
  });

  const PACKET_HEADER_MAX = 200;

  /**
   * An adapter that handles data transfers between the debugger client and
   * server. It can work with both nsIPipe and nsIServerSocket transports so
   * long as the properly created input and output streams are specified.
   * (However, for intra-process connections, LocalDebuggerTransport, below,
   * is more efficient than using an nsIPipe pair with DebuggerTransport.)
   *
   * @param input nsIAsyncInputStream
   *        The input stream.
   * @param output nsIAsyncOutputStream
   *        The output stream.
   *
   * Given a DebuggerTransport instance dt:
   * 1) Set dt.hooks to a packet handler object (described below).
   * 2) Call dt.ready() to begin watching for input packets.
   * 3) Call dt.send() / dt.startBulkSend() to send packets.
   * 4) Call dt.close() to close the connection, and disengage from the event
   *    loop.
   *
   * A packet handler is an object with the following methods:
   *
   * - onPacket(packet) - called when we have received a complete packet.
   *   |packet| is the parsed form of the packet --- a JavaScript value, not
   *   a JSON-syntax string.
   *
   * - onBulkPacket(packet) - called when we have switched to bulk packet
   *   receiving mode. |packet| is an object containing:
   *   * actor:  Name of actor that will receive the packet
   *   * type:   Name of actor's method that should be called on receipt
   *   * length: Size of the data to be read
   *   * stream: This input stream should only be used directly if you can ensure
   *             that you will read exactly |length| bytes and will not close the
   *             stream when reading is complete
   *   * done:   If you use the stream directly (instead of |copyTo| below), you
   *             must signal completion by resolving / rejecting this deferred.
   *             If it's rejected, the transport will be closed.  If an Error is
   *             supplied as a rejection value, it will be logged via |dumpn|.
   *             If you do use |copyTo|, resolving is taken care of for you when
   *             copying completes.
   *   * copyTo: A helper function for getting your data out of the stream that
   *             meets the stream handling requirements above, and has the
   *             following signature:
   *     @param  output nsIAsyncOutputStream
   *             The stream to copy to.
   *     @return Promise
   *             The promise is resolved when copying completes or rejected if any
   *             (unexpected) errors occur.
   *             This object also emits "progress" events for each chunk that is
   *             copied.  See stream-utils.js.
   *
   * - onClosed(reason) - called when the connection is closed. |reason| is
   *   an optional nsresult or object, typically passed when the transport is
   *   closed due to some error in a underlying stream.
   *
   * See ./packets.js and the Remote Debugging Protocol specification for more
   * details on the format of these packets.
   */
  function DebuggerTransport(input, output) {
    EventEmitter.decorate(this);

    this._input = input;
    this._scriptableInput = new ScriptableInputStream(input);
    this._output = output;

    // The current incoming (possibly partial) header, which will determine which
    // type of Packet |_incoming| below will become.
    this._incomingHeader = "";
    // The current incoming Packet object
    this._incoming = null;
    // A queue of outgoing Packet objects
    this._outgoing = [];

    this.hooks = null;
    this.active = false;

    this._incomingEnabled = true;
    this._outgoingEnabled = true;

    this.close = this.close.bind(this);
  }

  DebuggerTransport.prototype = {
    /**
     * Transmit an object as a JSON packet.
     *
     * This method returns immediately, without waiting for the entire
     * packet to be transmitted, registering event handlers as needed to
     * transmit the entire packet. Packets are transmitted in the order
     * they are passed to this method.
     */
    send: function (object) {
      this.emit("send", object);

      let packet = new JSONPacket(this);
      packet.object = object;
      this._outgoing.push(packet);
      this._flushOutgoing();
    },

    /**
     * Transmit streaming data via a bulk packet.
     *
     * This method initiates the bulk send process by queuing up the header data.
     * The caller receives eventual access to a stream for writing.
     *
     * N.B.: Do *not* attempt to close the stream handed to you, as it will
     * continue to be used by this transport afterwards.  Most users should
     * instead use the provided |copyFrom| function instead.
     *
     * @param header Object
     *        This is modeled after the format of JSON packets above, but does not
     *        actually contain the data, but is instead just a routing header:
     *          * actor:  Name of actor that will receive the packet
     *          * type:   Name of actor's method that should be called on receipt
     *          * length: Size of the data to be sent
     * @return Promise
     *         The promise will be resolved when you are allowed to write to the
     *         stream with an object containing:
     *           * stream:   This output stream should only be used directly if
     *                       you can ensure that you will write exactly |length|
     *                       bytes and will not close the stream when writing is
     *                       complete
     *           * done:     If you use the stream directly (instead of |copyFrom|
     *                       below), you must signal completion by resolving /
     *                       rejecting this deferred.  If it's rejected, the
     *                       transport will be closed.  If an Error is supplied as
     *                       a rejection value, it will be logged via |dumpn|.  If
     *                       you do use |copyFrom|, resolving is taken care of for
     *                       you when copying completes.
     *           * copyFrom: A helper function for getting your data onto the
     *                       stream that meets the stream handling requirements
     *                       above, and has the following signature:
     *             @param  input nsIAsyncInputStream
     *                     The stream to copy from.
     *             @return Promise
     *                     The promise is resolved when copying completes or
     *                     rejected if any (unexpected) errors occur.
     *                     This object also emits "progress" events for each chunk
     *                     that is copied.  See stream-utils.js.
     */
    startBulkSend: function (header) {
      this.emit("startbulksend", header);

      let packet = new BulkPacket(this);
      packet.header = header;
      this._outgoing.push(packet);
      this._flushOutgoing();
      return packet.streamReadyForWriting;
    },

    /**
     * Close the transport.
     * @param reason nsresult / object (optional)
     *        The status code or error message that corresponds to the reason for
     *        closing the transport (likely because a stream closed or failed).
     */
    close: function (reason) {
      this.emit("close", reason);

      this.active = false;
      this._input.close();
      this._scriptableInput.close();
      this._output.close();
      this._destroyIncoming();
      this._destroyAllOutgoing();
      if (this.hooks) {
        this.hooks.onClosed(reason);
        this.hooks = null;
      }
      if (reason) {
        dumpn("Transport closed: " + DevToolsUtils.safeErrorString(reason));
      } else {
        dumpn("Transport closed.");
      }
    },

    /**
     * The currently outgoing packet (at the top of the queue).
     */
    get _currentOutgoing() {
      return this._outgoing[0];
    },

    /**
     * Flush data to the outgoing stream.  Waits until the output stream notifies
     * us that it is ready to be written to (via onOutputStreamReady).
     */
    _flushOutgoing: function () {
      if (!this._outgoingEnabled || this._outgoing.length === 0) {
        return;
      }

      // If the top of the packet queue has nothing more to send, remove it.
      if (this._currentOutgoing.done) {
        this._finishCurrentOutgoing();
      }

      if (this._outgoing.length > 0) {
        let threadManager = Cc["@mozilla.org/thread-manager;1"].getService();
        this._output.asyncWait(this, 0, 0, threadManager.currentThread);
      }
    },

    /**
     * Pause this transport's attempts to write to the output stream.  This is
     * used when we've temporarily handed off our output stream for writing bulk
     * data.
     */
    pauseOutgoing: function () {
      this._outgoingEnabled = false;
    },

    /**
     * Resume this transport's attempts to write to the output stream.
     */
    resumeOutgoing: function () {
      this._outgoingEnabled = true;
      this._flushOutgoing();
    },

    // nsIOutputStreamCallback
    /**
     * This is called when the output stream is ready for more data to be written.
     * The current outgoing packet will attempt to write some amount of data, but
     * may not complete.
     */
    onOutputStreamReady: DevToolsUtils.makeInfallible(function (stream) {
      if (!this._outgoingEnabled || this._outgoing.length === 0) {
        return;
      }

      try {
        this._currentOutgoing.write(stream);
      } catch (e) {
        if (e.result != Cr.NS_BASE_STREAM_WOULD_BLOCK) {
          this.close(e.result);
          return;
        }
        throw e;
      }

      this._flushOutgoing();
    }, "DebuggerTransport.prototype.onOutputStreamReady"),

    /**
     * Remove the current outgoing packet from the queue upon completion.
     */
    _finishCurrentOutgoing: function () {
      if (this._currentOutgoing) {
        this._currentOutgoing.destroy();
        this._outgoing.shift();
      }
    },

    /**
     * Clear the entire outgoing queue.
     */
    _destroyAllOutgoing: function () {
      for (let packet of this._outgoing) {
        packet.destroy();
      }
      this._outgoing = [];
    },

    /**
     * Initialize the input stream for reading. Once this method has been called,
     * we watch for packets on the input stream, and pass them to the appropriate
     * handlers via this.hooks.
     */
    ready: function () {
      this.active = true;
      this._waitForIncoming();
    },

    /**
     * Asks the input stream to notify us (via onInputStreamReady) when it is
     * ready for reading.
     */
    _waitForIncoming: function () {
      if (this._incomingEnabled) {
        let threadManager = Cc["@mozilla.org/thread-manager;1"].getService();
        this._input.asyncWait(this, 0, 0, threadManager.currentThread);
      }
    },

    /**
     * Pause this transport's attempts to read from the input stream.  This is
     * used when we've temporarily handed off our input stream for reading bulk
     * data.
     */
    pauseIncoming: function () {
      this._incomingEnabled = false;
    },

    /**
     * Resume this transport's attempts to read from the input stream.
     */
    resumeIncoming: function () {
      this._incomingEnabled = true;
      this._flushIncoming();
      this._waitForIncoming();
    },

    // nsIInputStreamCallback
    /**
     * Called when the stream is either readable or closed.
     */
    onInputStreamReady: DevToolsUtils.makeInfallible(function (stream) {
      try {
        while (stream.available() && this._incomingEnabled &&
               this._processIncoming(stream, stream.available())) {
           // Loop until there is nothing more to process
        }
        this._waitForIncoming();
      } catch (e) {
        if (e.result != Cr.NS_BASE_STREAM_WOULD_BLOCK) {
          this.close(e.result);
        } else {
          throw e;
        }
      }
    }, "DebuggerTransport.prototype.onInputStreamReady"),

    /**
     * Process the incoming data.  Will create a new currently incoming Packet if
     * needed.  Tells the incoming Packet to read as much data as it can, but
     * reading may not complete.  The Packet signals that its data is ready for
     * delivery by calling one of this transport's _on*Ready methods (see
     * ./packets.js and the _on*Ready methods below).
     * @return boolean
     *         Whether incoming stream processing should continue for any
     *         remaining data.
     */
    _processIncoming: function (stream, count) {
      dumpv("Data available: " + count);

      if (!count) {
        dumpv("Nothing to read, skipping");
        return false;
      }

      try {
        if (!this._incoming) {
          dumpv("Creating a new packet from incoming");

          if (!this._readHeader(stream)) {
            // Not enough data to read packet type
            return false;
          }

          // Attempt to create a new Packet by trying to parse each possible
          // header pattern.
          this._incoming = Packet.fromHeader(this._incomingHeader, this);
          if (!this._incoming) {
            throw new Error("No packet types for header: " +
                          this._incomingHeader);
          }
        }

        if (!this._incoming.done) {
          // We have an incomplete packet, keep reading it.
          dumpv("Existing packet incomplete, keep reading");
          this._incoming.read(stream, this._scriptableInput);
        }
      } catch (e) {
        let msg = "Error reading incoming packet: (" + e + " - " + e.stack + ")";
        dumpn(msg);

        // Now in an invalid state, shut down the transport.
        this.close();
        return false;
      }

      if (!this._incoming.done) {
        // Still not complete, we'll wait for more data.
        dumpv("Packet not done, wait for more");
        return true;
      }

      // Ready for next packet
      this._flushIncoming();
      return true;
    },

    /**
     * Read as far as we can into the incoming data, attempting to build up a
     * complete packet header (which terminates with ":").  We'll only read up to
     * PACKET_HEADER_MAX characters.
     * @return boolean
     *         True if we now have a complete header.
     */
    _readHeader: function () {
      let amountToRead = PACKET_HEADER_MAX - this._incomingHeader.length;
      this._incomingHeader +=
      StreamUtils.delimitedRead(this._scriptableInput, ":", amountToRead);
      if (flags.wantVerbose) {
        dumpv("Header read: " + this._incomingHeader);
      }

      if (this._incomingHeader.endsWith(":")) {
        if (flags.wantVerbose) {
          dumpv("Found packet header successfully: " + this._incomingHeader);
        }
        return true;
      }

      if (this._incomingHeader.length >= PACKET_HEADER_MAX) {
        throw new Error("Failed to parse packet header!");
      }

      // Not enough data yet.
      return false;
    },

    /**
     * If the incoming packet is done, log it as needed and clear the buffer.
     */
    _flushIncoming: function () {
      if (!this._incoming.done) {
        return;
      }
      if (flags.wantLogging) {
        dumpn("Got: " + this._incoming);
      }
      this._destroyIncoming();
    },

    /**
     * Handler triggered by an incoming JSONPacket completing it's |read| method.
     * Delivers the packet to this.hooks.onPacket.
     */
    _onJSONObjectReady: function (object) {
      DevToolsUtils.executeSoon(DevToolsUtils.makeInfallible(() => {
      // Ensure the transport is still alive by the time this runs.
        if (this.active) {
          this.emit("packet", object);
          this.hooks.onPacket(object);
        }
      }, "DebuggerTransport instance's this.hooks.onPacket"));
    },

    /**
     * Handler triggered by an incoming BulkPacket entering the |read| phase for
     * the stream portion of the packet.  Delivers info about the incoming
     * streaming data to this.hooks.onBulkPacket.  See the main comment on the
     * transport at the top of this file for more details.
     */
    _onBulkReadReady: function (...args) {
      DevToolsUtils.executeSoon(DevToolsUtils.makeInfallible(() => {
      // Ensure the transport is still alive by the time this runs.
        if (this.active) {
          this.emit("bulkpacket", ...args);
          this.hooks.onBulkPacket(...args);
        }
      }, "DebuggerTransport instance's this.hooks.onBulkPacket"));
    },

    /**
     * Remove all handlers and references related to the current incoming packet,
     * either because it is now complete or because the transport is closing.
     */
    _destroyIncoming: function () {
      if (this._incoming) {
        this._incoming.destroy();
      }
      this._incomingHeader = "";
      this._incoming = null;
    }

  };

  exports.DebuggerTransport = DebuggerTransport;

  /**
   * An adapter that handles data transfers between the debugger client and
   * server when they both run in the same process. It presents the same API as
   * DebuggerTransport, but instead of transmitting serialized messages across a
   * connection it merely calls the packet dispatcher of the other side.
   *
   * @param other LocalDebuggerTransport
   *        The other endpoint for this debugger connection.
   *
   * @see DebuggerTransport
   */
  function LocalDebuggerTransport(other) {
    EventEmitter.decorate(this);

    this.other = other;
    this.hooks = null;

    // A packet number, shared between this and this.other. This isn't used by the
    // protocol at all, but it makes the packet traces a lot easier to follow.
    this._serial = this.other ? this.other._serial : { count: 0 };
    this.close = this.close.bind(this);
  }

  LocalDebuggerTransport.prototype = {
    /**
     * Transmit a message by directly calling the onPacket handler of the other
     * endpoint.
     */
    send: function (packet) {
      this.emit("send", packet);

      let serial = this._serial.count++;
      if (flags.wantLogging) {
        // Check 'from' first, as 'echo' packets have both.
        if (packet.from) {
          dumpn("Packet " + serial + " sent from " + uneval(packet.from));
        } else if (packet.to) {
          dumpn("Packet " + serial + " sent to " + uneval(packet.to));
        }
      }
      this._deepFreeze(packet);
      let other = this.other;
      if (other) {
        DevToolsUtils.executeSoon(DevToolsUtils.makeInfallible(() => {
          // Avoid the cost of JSON.stringify() when logging is disabled.
          if (flags.wantLogging) {
            dumpn("Received packet " + serial + ": " + JSON.stringify(packet, null, 2));
          }
          if (other.hooks) {
            other.emit("packet", packet);
            other.hooks.onPacket(packet);
          }
        }, "LocalDebuggerTransport instance's this.other.hooks.onPacket"));
      }
    },

    /**
     * Send a streaming bulk packet directly to the onBulkPacket handler of the
     * other endpoint.
     *
     * This case is much simpler than the full DebuggerTransport, since there is
     * no primary stream we have to worry about managing while we hand it off to
     * others temporarily.  Instead, we can just make a single use pipe and be
     * done with it.
     */
    startBulkSend: function ({actor, type, length}) {
      this.emit("startbulksend", {actor, type, length});

      let serial = this._serial.count++;

      dumpn("Sent bulk packet " + serial + " for actor " + actor);
      if (!this.other) {
        let error = new Error("startBulkSend: other side of transport missing");
        return promise.reject(error);
      }

      let pipe = new Pipe(true, true, 0, 0, null);

      DevToolsUtils.executeSoon(DevToolsUtils.makeInfallible(() => {
        dumpn("Received bulk packet " + serial);
        if (!this.other.hooks) {
          return;
        }

        // Receiver
        let deferred = defer();
        let packet = {
          actor: actor,
          type: type,
          length: length,
          copyTo: (output) => {
            let copying =
            StreamUtils.copyStream(pipe.inputStream, output, length);
            deferred.resolve(copying);
            return copying;
          },
          stream: pipe.inputStream,
          done: deferred
        };

        this.other.emit("bulkpacket", packet);
        this.other.hooks.onBulkPacket(packet);

        // Await the result of reading from the stream
        deferred.promise.then(() => pipe.inputStream.close(), this.close);
      }, "LocalDebuggerTransport instance's this.other.hooks.onBulkPacket"));

      // Sender
      let sendDeferred = defer();

      // The remote transport is not capable of resolving immediately here, so we
      // shouldn't be able to either.
      DevToolsUtils.executeSoon(() => {
        let copyDeferred = defer();

        sendDeferred.resolve({
          copyFrom: (input) => {
            let copying =
            StreamUtils.copyStream(input, pipe.outputStream, length);
            copyDeferred.resolve(copying);
            return copying;
          },
          stream: pipe.outputStream,
          done: copyDeferred
        });

        // Await the result of writing to the stream
        copyDeferred.promise.then(() => pipe.outputStream.close(), this.close);
      });

      return sendDeferred.promise;
    },

    /**
     * Close the transport.
     */
    close: function () {
      this.emit("close");

      if (this.other) {
        // Remove the reference to the other endpoint before calling close(), to
        // avoid infinite recursion.
        let other = this.other;
        this.other = null;
        other.close();
      }
      if (this.hooks) {
        try {
          this.hooks.onClosed();
        } catch (ex) {
          console.error(ex);
        }
        this.hooks = null;
      }
    },

    /**
     * An empty method for emulating the DebuggerTransport API.
     */
    ready: function () {},

    /**
     * Helper function that makes an object fully immutable.
     */
    _deepFreeze: function (object) {
      Object.freeze(object);
      for (let prop in object) {
        // Freeze the properties that are objects, not on the prototype, and not
        // already frozen. Note that this might leave an unfrozen reference
        // somewhere in the object if there is an already frozen object containing
        // an unfrozen object.
        if (object.hasOwnProperty(prop) && typeof object === "object" &&
            !Object.isFrozen(object)) {
          this._deepFreeze(object[prop]);
        }
      }
    }
  };

  exports.LocalDebuggerTransport = LocalDebuggerTransport;

  /**
   * A transport for the debugging protocol that uses nsIMessageManagers to
   * exchange packets with servers running in child processes.
   *
   * In the parent process, |mm| should be the nsIMessageSender for the
   * child process. In a child process, |mm| should be the child process
   * message manager, which sends packets to the parent.
   *
   * |prefix| is a string included in the message names, to distinguish
   * multiple servers running in the same child process.
   *
   * This transport exchanges messages named 'debug:<prefix>:packet', where
   * <prefix> is |prefix|, whose data is the protocol packet.
   */
  function ChildDebuggerTransport(mm, prefix) {
    EventEmitter.decorate(this);

    this._mm = mm;
    this._messageName = "debug:" + prefix + ":packet";
  }

  /*
   * To avoid confusion, we use 'message' to mean something that
   * nsIMessageSender conveys, and 'packet' to mean a remote debugging
   * protocol packet.
   */
  ChildDebuggerTransport.prototype = {
    constructor: ChildDebuggerTransport,

    hooks: null,

    _addListener() {
      this._mm.addMessageListener(this._messageName, this);
    },

    _removeListener() {
      try {
        this._mm.removeMessageListener(this._messageName, this);
      } catch (e) {
        if (e.result != Cr.NS_ERROR_NULL_POINTER) {
          throw e;
        }
        // In some cases, especially when using messageManagers in non-e10s mode, we reach
        // this point with a dead messageManager which only throws errors but does not
        // seem to indicate in any other way that it is dead.
      }
    },

    ready: function () {
      this._addListener();
    },

    close: function () {
      this._removeListener();
      this.emit("close");
      this.hooks.onClosed();
    },

    receiveMessage: function ({data}) {
      this.emit("packet", data);
      this.hooks.onPacket(data);
    },

    send: function (packet) {
      this.emit("send", packet);
      try {
        this._mm.sendAsyncMessage(this._messageName, packet);
      } catch (e) {
        if (e.result != Cr.NS_ERROR_NULL_POINTER) {
          throw e;
        }
        // In some cases, especially when using messageManagers in non-e10s mode, we reach
        // this point with a dead messageManager which only throws errors but does not
        // seem to indicate in any other way that it is dead.
      }
    },

    startBulkSend: function () {
      throw new Error("Can't send bulk data to child processes.");
    },

    swapBrowser(mm) {
      this._removeListener();
      this._mm = mm;
      this._addListener();
    },
  };

  exports.ChildDebuggerTransport = ChildDebuggerTransport;

  // WorkerDebuggerTransport is defined differently depending on whether we are
  // on the main thread or a worker thread. In the former case, we are required
  // by the devtools loader, and isWorker will be false. Otherwise, we are
  // required by the worker loader, and isWorker will be true.
  //
  // Each worker debugger supports only a single connection to the main thread.
  // However, its theoretically possible for multiple servers to connect to the
  // same worker. Consequently, each transport has a connection id, to allow
  // messages from multiple connections to be multiplexed on a single channel.

  if (!this.isWorker) {
    // Main thread
    (function () {
      /**
       * A transport that uses a WorkerDebugger to send packets from the main
       * thread to a worker thread.
       */
      function WorkerDebuggerTransport(dbg, id) {
        this._dbg = dbg;
        this._id = id;
        this.onMessage = this._onMessage.bind(this);
      }

      WorkerDebuggerTransport.prototype = {
        constructor: WorkerDebuggerTransport,

        ready: function () {
          this._dbg.addListener(this);
        },

        close: function () {
          this._dbg.removeListener(this);
          if (this.hooks) {
            this.hooks.onClosed();
          }
        },

        send: function (packet) {
          this._dbg.postMessage(JSON.stringify({
            type: "message",
            id: this._id,
            message: packet
          }));
        },

        startBulkSend: function () {
          throw new Error("Can't send bulk data from worker threads!");
        },

        _onMessage: function (message) {
          let packet = JSON.parse(message);
          if (packet.type !== "message" || packet.id !== this._id) {
            return;
          }

          if (this.hooks) {
            this.hooks.onPacket(packet.message);
          }
        }
      };

      exports.WorkerDebuggerTransport = WorkerDebuggerTransport;
    }).call(this);
  } else {
    // Worker thread
    (function () {
      /**
       * A transport that uses a WorkerDebuggerGlobalScope to send packets from a
       * worker thread to the main thread.
       */
      function WorkerDebuggerTransport(scope, id) {
        this._scope = scope;
        this._id = id;
        this._onMessage = this._onMessage.bind(this);
      }

      WorkerDebuggerTransport.prototype = {
        constructor: WorkerDebuggerTransport,

        ready: function () {
          this._scope.addEventListener("message", this._onMessage);
        },

        close: function () {
          this._scope.removeEventListener("message", this._onMessage);
          if (this.hooks) {
            this.hooks.onClosed();
          }
        },

        send: function (packet) {
          this._scope.postMessage(JSON.stringify({
            type: "message",
            id: this._id,
            message: packet
          }));
        },

        startBulkSend: function () {
          throw new Error("Can't send bulk data from worker threads!");
        },

        _onMessage: function (event) {
          let packet = JSON.parse(event.data);
          if (packet.type !== "message" || packet.id !== this._id) {
            return;
          }

          if (this.hooks) {
            this.hooks.onPacket(packet.message);
          }
        }
      };

      exports.WorkerDebuggerTransport = WorkerDebuggerTransport;
    }).call(this);
  }
});
PK
!<��ָ�Hchrome/devtools/modules/devtools/shared/transport/websocket-transport.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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("devtools/shared/event-emitter");

function WebSocketDebuggerTransport(socket) {
  EventEmitter.decorate(this);

  this.active = false;
  this.hooks = null;
  this.socket = socket;
}

WebSocketDebuggerTransport.prototype = {
  ready() {
    if (this.active) {
      return;
    }

    this.socket.addEventListener("message", this);
    this.socket.addEventListener("close", this);

    this.active = true;
  },

  send(object) {
    this.emit("send", object);
    if (this.socket) {
      this.socket.send(JSON.stringify(object));
    }
  },

  startBulkSend() {
    throw new Error("Bulk send is not supported by WebSocket transport");
  },

  close() {
    this.emit("close");
    this.active = false;

    this.socket.removeEventListener("message", this);
    this.socket.removeEventListener("close", this);
    this.socket.close();
    this.socket = null;

    if (this.hooks) {
      this.hooks.onClosed();
      this.hooks = null;
    }
  },

  handleEvent(event) {
    switch (event.type) {
      case "message":
        this.onMessage(event);
        break;
      case "close":
        this.close();
        break;
    }
  },

  onMessage({ data }) {
    if (typeof data !== "string") {
      throw new Error("Binary messages are not supported by WebSocket transport");
    }

    let object = JSON.parse(data);
    this.emit("packet", object);
    if (this.hooks) {
      this.hooks.onPacket(object);
    }
  },
};

module.exports = WebSocketDebuggerTransport;
PK
!<�!f�

:chrome/devtools/modules/devtools/shared/wasm-source-map.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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;
  }

  /**
   * @param url string
   */
  set sourceRoot(url) { // important, since sources are using this.
    this._map.sourceRoot = url;
  }

  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) {
    let result = this._map.originalPositionFor({
      line: 1,
      column: generatedPosition.line,
      bias: generatedPosition.bias
    });
    return result;
  }

  _remapGeneratedPosition(position) {
    let generatedPosition = {
      line: position.column,
      column: 0,
    };
    if (this._computeColumnSpans) {
      generatedPosition.lastColumn = Infinity;
    }
    return generatedPosition;
  }

  generatedPositionFor(originalPosition) {
    let position = this._map.generatedPositionFor(originalPosition);
    return this._remapGeneratedPosition(position);
  }

  allGeneratedPositionsFor(originalPosition) {
    let 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) => {
      let {
        source,
        generatedColumn,
        originalLine,
        originalColumn,
        name
      } = entry;
      callback({
        source,
        generatedLine: generatedColumn,
        generatedColumn: 0,
        originalLine,
        originalColumn,
        name,
      });
    }, context, order);
  }
}

exports.WasmRemap = WasmRemap;
PK
!<��@��\�\<chrome/devtools/modules/devtools/shared/webconsole/client.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=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 DevToolsUtils = require("devtools/shared/DevToolsUtils");
const EventEmitter = require("devtools/shared/old-event-emitter");
const LongStringClient = require("devtools/shared/client/long-string-client");

/**
 * A WebConsoleClient is used as a front end for the WebConsoleActor that is
 * created on the server, hiding implementation details.
 *
 * @param object debuggerClient
 *        The DebuggerClient instance we live for.
 * @param object response
 *        The response packet received from the "startListeners" request sent to
 *        the WebConsoleActor.
 */
function WebConsoleClient(debuggerClient, response) {
  this._actor = response.from;
  this._client = debuggerClient;
  this._longStrings = {};
  this.traits = response.traits || {};
  this.events = [];
  this._networkRequests = new Map();

  this.pendingEvaluationResults = new Map();
  this.onEvaluationResult = this.onEvaluationResult.bind(this);
  this.onNetworkEvent = this._onNetworkEvent.bind(this);
  this.onNetworkEventUpdate = this._onNetworkEventUpdate.bind(this);
  this.onInspectObject = this._onInspectObject.bind(this);
  this.onDocEvent = this._onDocEvent.bind(this);

  this._client.addListener("evaluationResult", this.onEvaluationResult);
  this._client.addListener("networkEvent", this.onNetworkEvent);
  this._client.addListener("networkEventUpdate", this.onNetworkEventUpdate);
  this._client.addListener("inspectObject", this.onInspectObject);
  this._client.addListener("documentEvent", this.onDocEvent);
  EventEmitter.decorate(this);
}

exports.WebConsoleClient = WebConsoleClient;

WebConsoleClient.prototype = {
  _longStrings: null,
  traits: null,

  /**
   * Holds the network requests currently displayed by the Web Console. Each key
   * represents the connection ID and the value is network request information.
   * @private
   * @type object
   */
  _networkRequests: null,

  getNetworkRequest(actorId) {
    return this._networkRequests.get(actorId);
  },

  hasNetworkRequest(actorId) {
    return this._networkRequests.has(actorId);
  },

  removeNetworkRequest(actorId) {
    this._networkRequests.delete(actorId);
  },

  getNetworkEvents() {
    return this._networkRequests.values();
  },

  get actor() {
    return this._actor;
  },

  /**
   * The "networkEvent" message type handler. We redirect any message to
   * the UI for displaying.
   *
   * @private
   * @param string type
   *        Message type.
   * @param object packet
   *        The message received from the server.
   */
  _onNetworkEvent: function (type, packet) {
    if (packet.from == this._actor) {
      let actor = packet.eventActor;
      let networkInfo = {
        _type: "NetworkEvent",
        timeStamp: actor.timeStamp,
        node: null,
        actor: actor.actor,
        discardRequestBody: true,
        discardResponseBody: true,
        startedDateTime: actor.startedDateTime,
        request: {
          url: actor.url,
          method: actor.method,
        },
        isXHR: actor.isXHR,
        cause: actor.cause,
        response: {},
        timings: {},
        // track the list of network event updates
        updates: [],
        private: actor.private,
        fromCache: actor.fromCache,
        fromServiceWorker: actor.fromServiceWorker
      };
      this._networkRequests.set(actor.actor, networkInfo);

      this.emit("networkEvent", networkInfo);
    }
  },

  /**
   * The "networkEventUpdate" message type handler. We redirect any message to
   * the UI for displaying.
   *
   * @private
   * @param string type
   *        Message type.
   * @param object packet
   *        The message received from the server.
   */
  _onNetworkEventUpdate: function (type, packet) {
    let networkInfo = this.getNetworkRequest(packet.from);
    if (!networkInfo) {
      return;
    }

    networkInfo.updates.push(packet.updateType);

    switch (packet.updateType) {
      case "requestHeaders":
        networkInfo.request.headersSize = packet.headersSize;
        break;
      case "requestPostData":
        networkInfo.discardRequestBody = packet.discardRequestBody;
        networkInfo.request.bodySize = packet.dataSize;
        break;
      case "responseStart":
        networkInfo.response.httpVersion = packet.response.httpVersion;
        networkInfo.response.status = packet.response.status;
        networkInfo.response.statusText = packet.response.statusText;
        networkInfo.response.headersSize = packet.response.headersSize;
        networkInfo.response.remoteAddress = packet.response.remoteAddress;
        networkInfo.response.remotePort = packet.response.remotePort;
        networkInfo.discardResponseBody = packet.response.discardResponseBody;
        break;
      case "responseContent":
        networkInfo.response.content = {
          mimeType: packet.mimeType,
        };
        networkInfo.response.bodySize = packet.contentSize;
        networkInfo.response.transferredSize = packet.transferredSize;
        networkInfo.discardResponseBody = packet.discardResponseBody;
        break;
      case "eventTimings":
        networkInfo.totalTime = packet.totalTime;
        break;
      case "securityInfo":
        networkInfo.securityInfo = packet.state;
        break;
    }

    this.emit("networkEventUpdate", {
      packet: packet,
      networkInfo
    });
  },

  /**
   * The "inspectObject" message type handler. We just re-emit it so that
   * the toolbox can listen to the event and decide how to handle it.
   *
   * @private
   * @param string type
   *        Message type.
   * @param object packet
   *        The message received from the server.
   */
  _onInspectObject: function (type, packet) {
    this.emit("inspectObject", packet);
  },

  /**
   * The "docEvent" message type handler. We just re-emit it so that
   * the tools can listen for them on the console client.
   *
   * @private
   * @param string type
   *        Message type.
   * @param object packet
   *        The message received from the server.
   */
  _onDocEvent: function (type, packet) {
    this.emit("documentEvent", packet);
  },

  /**
   * Retrieve the cached messages from the server.
   *
   * @see this.CACHED_MESSAGES
   * @param array types
   *        The array of message types you want from the server. See
   *        this.CACHED_MESSAGES for known types.
   * @param function onResponse
   *        The function invoked when the response is received.
   * @return request
   *         Request object that implements both Promise and EventEmitter interfaces
   */
  getCachedMessages: function (types, onResponse) {
    let packet = {
      to: this._actor,
      type: "getCachedMessages",
      messageTypes: types,
    };
    return this._client.request(packet, onResponse);
  },

  /**
   * Inspect the properties of an object.
   *
   * @param string actor
   *        The WebConsoleObjectActor ID to send the request to.
   * @param function onResponse
   *        The function invoked when the response is received.
   * @return request
   *         Request object that implements both Promise and EventEmitter interfaces
   */
  inspectObjectProperties: function (actor, onResponse) {
    let packet = {
      to: actor,
      type: "inspectProperties",
    };
    return this._client.request(packet, onResponse);
  },

  /**
   * Evaluate a JavaScript expression.
   *
   * @param string string
   *        The code you want to evaluate.
   * @param function onResponse
   *        The function invoked when the response is received.
   * @param object [options={}]
   *        Options for evaluation:
   *
   *        - bindObjectActor: an ObjectActor ID. The OA holds a reference to
   *        a Debugger.Object that wraps a content object. This option allows
   *        you to bind |_self| to the D.O of the given OA, during string
   *        evaluation.
   *
   *        See: Debugger.Object.executeInGlobalWithBindings() for information
   *        about bindings.
   *
   *        Use case: the variable view needs to update objects and it does so
   *        by knowing the ObjectActor it inspects and binding |_self| to the
   *        D.O of the OA. As such, variable view sends strings like these for
   *        eval:
   *          _self["prop"] = value;
   *
   *        - frameActor: a FrameActor ID. The FA holds a reference to
   *        a Debugger.Frame. This option allows you to evaluate the string in
   *        the frame of the given FA.
   *
   *        - url: the url to evaluate the script as. Defaults to
   *        "debugger eval code".
   *
   *        - selectedNodeActor: the NodeActor ID of the current
   *        selection in the Inspector, if such a selection
   *        exists. This is used by helper functions that can
   *        reference the currently selected node in the Inspector,
   *        like $0.
   * @return request
   *         Request object that implements both Promise and EventEmitter interfaces
   */
  evaluateJS: function (string, onResponse, options = {}) {
    let packet = {
      to: this._actor,
      type: "evaluateJS",
      text: string,
      bindObjectActor: options.bindObjectActor,
      frameActor: options.frameActor,
      url: options.url,
      selectedNodeActor: options.selectedNodeActor,
      selectedObjectActor: options.selectedObjectActor,
    };
    return this._client.request(packet, onResponse);
  },

  /**
   * Evaluate a JavaScript expression asynchronously.
   * See evaluateJS for parameter and response information.
   */
  evaluateJSAsync: function (string, onResponse, options = {}) {
    // Pre-37 servers don't support async evaluation.
    if (!this.traits.evaluateJSAsync) {
      return this.evaluateJS(string, onResponse, options);
    }

    let packet = {
      to: this._actor,
      type: "evaluateJSAsync",
      text: string,
      bindObjectActor: options.bindObjectActor,
      frameActor: options.frameActor,
      url: options.url,
      selectedNodeActor: options.selectedNodeActor,
      selectedObjectActor: options.selectedObjectActor,
    };

    return new Promise((resolve, reject) => {
      this._client.request(packet, response => {
        // Null check this in case the client has been detached while waiting
        // for a response.
        if (this.pendingEvaluationResults) {
          this.pendingEvaluationResults.set(response.resultID, resp => {
            if (onResponse) {
              onResponse(resp);
            }
            if (resp.error) {
              reject(resp);
            } else {
              resolve(resp);
            }
          });
        }
      });
    });
  },

  /**
   * Handler for the actors's unsolicited evaluationResult packet.
   */
  onEvaluationResult: function (notification, packet) {
    // The client on the main thread can receive notification packets from
    // multiple webconsole actors: the one on the main thread and the ones
    // on worker threads.  So make sure we should be handling this request.
    if (packet.from !== this._actor) {
      return;
    }

    // Find the associated callback based on this ID, and fire it.
    // In a sync evaluation, this would have already been called in
    // direct response to the client.request function.
    let onResponse = this.pendingEvaluationResults.get(packet.resultID);
    if (onResponse) {
      onResponse(packet);
      this.pendingEvaluationResults.delete(packet.resultID);
    } else {
      DevToolsUtils.reportException("onEvaluationResult",
        "No response handler for an evaluateJSAsync result (resultID: " +
                                    packet.resultID + ")");
    }
  },

  /**
   * Autocomplete a JavaScript expression.
   *
   * @param string string
   *        The code you want to autocomplete.
   * @param number cursor
   *        Cursor location inside the string. Index starts from 0.
   * @param function onResponse
   *        The function invoked when the response is received.
   * @param string frameActor
   *        The id of the frame actor that made the call.
   * @return request
   *         Request object that implements both Promise and EventEmitter interfaces
   */
  autocomplete: function (string, cursor, onResponse, frameActor) {
    let packet = {
      to: this._actor,
      type: "autocomplete",
      text: string,
      cursor: cursor,
      frameActor: frameActor,
    };
    return this._client.request(packet, onResponse);
  },

  /**
   * Clear the cache of messages (page errors and console API calls).
   *
   * @return request
   *         Request object that implements both Promise and EventEmitter interfaces
   */
  clearMessagesCache: function () {
    let packet = {
      to: this._actor,
      type: "clearMessagesCache",
    };
    return this._client.request(packet);
  },

  /**
   * Get Web Console-related preferences on the server.
   *
   * @param array preferences
   *        An array with the preferences you want to retrieve.
   * @param function [onResponse]
   *        Optional function to invoke when the response is received.
   * @return request
   *         Request object that implements both Promise and EventEmitter interfaces
   */
  getPreferences: function (preferences, onResponse) {
    let packet = {
      to: this._actor,
      type: "getPreferences",
      preferences: preferences,
    };
    return this._client.request(packet, onResponse);
  },

  /**
   * Set Web Console-related preferences on the server.
   *
   * @param object preferences
   *        An object with the preferences you want to change.
   * @param function [onResponse]
   *        Optional function to invoke when the response is received.
   * @return request
   *         Request object that implements both Promise and EventEmitter interfaces
   */
  setPreferences: function (preferences, onResponse) {
    let packet = {
      to: this._actor,
      type: "setPreferences",
      preferences: preferences,
    };
    return this._client.request(packet, onResponse);
  },

  /**
   * Retrieve the request headers from the given NetworkEventActor.
   *
   * @param string actor
   *        The NetworkEventActor ID.
   * @param function onResponse
   *        The function invoked when the response is received.
   * @return request
   *         Request object that implements both Promise and EventEmitter interfaces
   */
  getRequestHeaders: function (actor, onResponse) {
    let packet = {
      to: actor,
      type: "getRequestHeaders",
    };
    return this._client.request(packet, onResponse);
  },

  /**
   * Retrieve the request cookies from the given NetworkEventActor.
   *
   * @param string actor
   *        The NetworkEventActor ID.
   * @param function onResponse
   *        The function invoked when the response is received.
   * @return request
   *         Request object that implements both Promise and EventEmitter interfaces
   */
  getRequestCookies: function (actor, onResponse) {
    let packet = {
      to: actor,
      type: "getRequestCookies",
    };
    return this._client.request(packet, onResponse);
  },

  /**
   * Retrieve the request post data from the given NetworkEventActor.
   *
   * @param string actor
   *        The NetworkEventActor ID.
   * @param function onResponse
   *        The function invoked when the response is received.
   * @return request
   *         Request object that implements both Promise and EventEmitter interfaces
   */
  getRequestPostData: function (actor, onResponse) {
    let packet = {
      to: actor,
      type: "getRequestPostData",
    };
    return this._client.request(packet, onResponse);
  },

  /**
   * Retrieve the response headers from the given NetworkEventActor.
   *
   * @param string actor
   *        The NetworkEventActor ID.
   * @param function onResponse
   *        The function invoked when the response is received.
   * @return request
   *         Request object that implements both Promise and EventEmitter interfaces
   */
  getResponseHeaders: function (actor, onResponse) {
    let packet = {
      to: actor,
      type: "getResponseHeaders",
    };
    return this._client.request(packet, onResponse);
  },

  /**
   * Retrieve the response cookies from the given NetworkEventActor.
   *
   * @param string actor
   *        The NetworkEventActor ID.
   * @param function onResponse
   *        The function invoked when the response is received.
   * @return request
   *         Request object that implements both Promise and EventEmitter interfaces
   */
  getResponseCookies: function (actor, onResponse) {
    let packet = {
      to: actor,
      type: "getResponseCookies",
    };
    return this._client.request(packet, onResponse);
  },

  /**
   * Retrieve the response content from the given NetworkEventActor.
   *
   * @param string actor
   *        The NetworkEventActor ID.
   * @param function onResponse
   *        The function invoked when the response is received.
   * @return request
   *         Request object that implements both Promise and EventEmitter interfaces
   */
  getResponseContent: function (actor, onResponse) {
    let packet = {
      to: actor,
      type: "getResponseContent",
    };
    return this._client.request(packet, onResponse);
  },

  /**
   * Retrieve the timing information for the given NetworkEventActor.
   *
   * @param string actor
   *        The NetworkEventActor ID.
   * @param function onResponse
   *        The function invoked when the response is received.
   * @return request
   *         Request object that implements both Promise and EventEmitter interfaces
   */
  getEventTimings: function (actor, onResponse) {
    let packet = {
      to: actor,
      type: "getEventTimings",
    };
    return this._client.request(packet, onResponse);
  },

  /**
   * Retrieve the security information for the given NetworkEventActor.
   *
   * @param string actor
   *        The NetworkEventActor ID.
   * @param function onResponse
   *        The function invoked when the response is received.
   * @return request
   *         Request object that implements both Promise and EventEmitter interfaces
   */
  getSecurityInfo: function (actor, onResponse) {
    let packet = {
      to: actor,
      type: "getSecurityInfo",
    };
    return this._client.request(packet, onResponse);
  },

  /**
   * Retrieve the stack-trace information for the given NetworkEventActor.
   *
   * @param string actor
   *        The NetworkEventActor ID.
   * @param function onResponse
   *        The function invoked when the stack-trace is received.
   * @return request
   *         Request object that implements both Promise and EventEmitter interfaces
   */
  getStackTrace: function (actor, onResponse) {
    let packet = {
      to: actor,
      type: "getStackTrace",
    };
    return this._client.request(packet, onResponse);
  },

  /**
   * Send a HTTP request with the given data.
   *
   * @param string data
   *        The details of the HTTP request.
   * @param function onResponse
   *        The function invoked when the response is received.
   * @return request
   *         Request object that implements both Promise and EventEmitter interfaces
   */
  sendHTTPRequest: function (data, onResponse) {
    let packet = {
      to: this._actor,
      type: "sendHTTPRequest",
      request: data
    };
    return this._client.request(packet, onResponse);
  },

  /**
   * Start the given Web Console listeners.
   *
   * @see this.LISTENERS
   * @param array listeners
   *        Array of listeners you want to start. See this.LISTENERS for
   *        known listeners.
   * @param function onResponse
   *        Function to invoke when the server response is received.
   * @return request
   *         Request object that implements both Promise and EventEmitter interfaces
   */
  startListeners: function (listeners, onResponse) {
    let packet = {
      to: this._actor,
      type: "startListeners",
      listeners: listeners,
    };
    return this._client.request(packet, onResponse);
  },

  /**
   * Stop the given Web Console listeners.
   *
   * @see this.LISTENERS
   * @param array listeners
   *        Array of listeners you want to stop. See this.LISTENERS for
   *        known listeners.
   * @param function onResponse
   *        Function to invoke when the server response is received.
   * @return request
   *         Request object that implements both Promise and EventEmitter interfaces
   */
  stopListeners: function (listeners, onResponse) {
    let packet = {
      to: this._actor,
      type: "stopListeners",
      listeners: listeners,
    };
    return this._client.request(packet, onResponse);
  },

  /**
   * Return an instance of LongStringClient for the given long string grip.
   *
   * @param object grip
   *        The long string grip returned by the protocol.
   * @return object
   *         The LongStringClient for the given long string grip.
   */
  longString: function (grip) {
    if (grip.actor in this._longStrings) {
      return this._longStrings[grip.actor];
    }

    let client = new LongStringClient(this._client, grip);
    this._longStrings[grip.actor] = client;
    return client;
  },

  /**
   * Close the WebConsoleClient. This stops all the listeners on the server and
   * detaches from the console actor.
   *
   * @param function onResponse
   *        Function to invoke when the server response is received.
   */
  detach: function (onResponse) {
    this._client.removeListener("evaluationResult", this.onEvaluationResult);
    this._client.removeListener("networkEvent", this.onNetworkEvent);
    this._client.removeListener("networkEventUpdate",
                                this.onNetworkEventUpdate);
    this._client.removeListener("inspectObject", this.onInspectObject);
    this._client.removeListener("documentEvent", this.onDocEvent);
    this.stopListeners(null, onResponse);
    this._longStrings = null;
    this._client = null;
    this.pendingEvaluationResults.clear();
    this.pendingEvaluationResults = null;
    this.clearNetworkRequests();
    this._networkRequests = null;
  },

  clearNetworkRequests: function () {
    this._networkRequests.clear();
  },

  /**
   * Fetches the full text of a LongString.
   *
   * @param object | string stringGrip
   *        The long string grip containing the corresponding actor.
   *        If you pass in a plain string (by accident or because you're lazy),
   *        then a promise of the same string is simply returned.
   * @return object Promise
   *         A promise that is resolved when the full string contents
   *         are available, or rejected if something goes wrong.
   */
  getString: function (stringGrip) {
    // Make sure this is a long string.
    if (typeof stringGrip !== "object" || stringGrip.type !== "longString") {
      // Go home string, you're drunk.
      return Promise.resolve(stringGrip);
    }

    // Fetch the long string only once.
    if (stringGrip._fullText) {
      return stringGrip._fullText;
    }

    return new Promise((resolve, reject) => {
      let { initial, length } = stringGrip;
      let longStringClient = this.longString(stringGrip);

      longStringClient.substring(initial.length, length, response => {
        if (response.error) {
          DevToolsUtils.reportException("getString",
              response.error + ": " + response.message);
          reject(response);
        }
        resolve(initial + response.substring);
      });
    });
  }
};
PK
!<��C��:�:Jchrome/devtools/modules/devtools/shared/webconsole/js-property-provider.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft= javascript ts=2 et sw=2 tw=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 DevToolsUtils = require("devtools/shared/DevToolsUtils");

if (!isWorker) {
  loader.lazyImporter(this, "Parser", "resource://devtools/shared/Parser.jsm");
}

// Provide an easy way to bail out of even attempting an autocompletion
// if an object has way too many properties. Protects against large objects
// with numeric values that wouldn't be tallied towards MAX_AUTOCOMPLETIONS.
const MAX_AUTOCOMPLETE_ATTEMPTS = exports.MAX_AUTOCOMPLETE_ATTEMPTS = 100000;
// Prevent iterating over too many properties during autocomplete suggestions.
const MAX_AUTOCOMPLETIONS = exports.MAX_AUTOCOMPLETIONS = 1500;

const STATE_NORMAL = 0;
const STATE_QUOTE = 2;
const STATE_DQUOTE = 3;

const OPEN_BODY = "{[(".split("");
const CLOSE_BODY = "}])".split("");
const OPEN_CLOSE_BODY = {
  "{": "}",
  "[": "]",
  "(": ")",
};

function hasArrayIndex(str) {
  return /\[\d+\]$/.test(str);
}

/**
 * Analyses a given string to find the last statement that is interesting for
 * later completion.
 *
 * @param   string str
 *          A string to analyse.
 *
 * @returns object
 *          If there was an error in the string detected, then a object like
 *
 *            { err: "ErrorMesssage" }
 *
 *          is returned, otherwise a object like
 *
 *            {
 *              state: STATE_NORMAL|STATE_QUOTE|STATE_DQUOTE,
 *              startPos: index of where the last statement begins
 *            }
 */
function findCompletionBeginning(str) {
  let bodyStack = [];

  let state = STATE_NORMAL;
  let start = 0;
  let c;
  for (let i = 0; i < str.length; i++) {
    c = str[i];

    switch (state) {
      // Normal JS state.
      case STATE_NORMAL:
        if (c == '"') {
          state = STATE_DQUOTE;
        } else if (c == "'") {
          state = STATE_QUOTE;
        } else if (c == ";") {
          start = i + 1;
        } else if (c == " ") {
          start = i + 1;
        } else if (OPEN_BODY.includes(c)) {
          bodyStack.push({
            token: c,
            start: start
          });
          start = i + 1;
        } else if (CLOSE_BODY.includes(c)) {
          let last = bodyStack.pop();
          if (!last || OPEN_CLOSE_BODY[last.token] != c) {
            return {
              err: "syntax error"
            };
          }
          if (c == "}") {
            start = i + 1;
          } else {
            start = last.start;
          }
        }
        break;

      // Double quote state > " <
      case STATE_DQUOTE:
        if (c == "\\") {
          i++;
        } else if (c == "\n") {
          return {
            err: "unterminated string literal"
          };
        } else if (c == '"') {
          state = STATE_NORMAL;
        }
        break;

      // Single quote state > ' <
      case STATE_QUOTE:
        if (c == "\\") {
          i++;
        } else if (c == "\n") {
          return {
            err: "unterminated string literal"
          };
        } else if (c == "'") {
          state = STATE_NORMAL;
        }
        break;
    }
  }

  return {
    state: state,
    startPos: start
  };
}

/**
 * Provides a list of properties, that are possible matches based on the passed
 * Debugger.Environment/Debugger.Object and inputValue.
 *
 * @param object dbgObject
 *        When the debugger is not paused this Debugger.Object wraps
 *        the scope for autocompletion.
 *        It is null if the debugger is paused.
 * @param object anEnvironment
 *        When the debugger is paused this Debugger.Environment is the
 *        scope for autocompletion.
 *        It is null if the debugger is not paused.
 * @param string inputValue
 *        Value that should be completed.
 * @param number [cursor=inputValue.length]
 *        Optional offset in the input where the cursor is located. If this is
 *        omitted then the cursor is assumed to be at the end of the input
 *        value.
 * @returns null or object
 *          If no completion valued could be computed, null is returned,
 *          otherwise a object with the following form is returned:
 *            {
 *              matches: [ string, string, string ],
 *              matchProp: Last part of the inputValue that was used to find
 *                         the matches-strings.
 *            }
 */
function JSPropertyProvider(dbgObject, anEnvironment, inputValue, cursor) {
  if (cursor === undefined) {
    cursor = inputValue.length;
  }

  inputValue = inputValue.substring(0, cursor);

  // Analyse the inputValue and find the beginning of the last part that
  // should be completed.
  let beginning = findCompletionBeginning(inputValue);

  // There was an error analysing the string.
  if (beginning.err) {
    return null;
  }

  // If the current state is not STATE_NORMAL, then we are inside of an string
  // which means that no completion is possible.
  if (beginning.state != STATE_NORMAL) {
    return null;
  }

  let completionPart = inputValue.substring(beginning.startPos);
  let lastDot = completionPart.lastIndexOf(".");

  // Don't complete on just an empty string.
  if (completionPart.trim() == "") {
    return null;
  }

  // Catch literals like [1,2,3] or "foo" and return the matches from
  // their prototypes.
  // Don't run this is a worker, migrating to acorn should allow this
  // to run in a worker - Bug 1217198.
  if (!isWorker && lastDot > 0) {
    let parser = new Parser();
    parser.logExceptions = false;
    let syntaxTree = parser.get(completionPart.slice(0, lastDot));
    let lastTree = syntaxTree.getLastSyntaxTree();
    let lastBody = lastTree && lastTree.AST.body[lastTree.AST.body.length - 1];

    // Finding the last expression since we've sliced up until the dot.
    // If there were parse errors this won't exist.
    if (lastBody) {
      let expression = lastBody.expression;
      let matchProp = completionPart.slice(lastDot + 1);
      if (expression.type === "ArrayExpression") {
        return getMatchedProps(Array.prototype, matchProp);
      } else if (expression.type === "Literal" &&
                 (typeof expression.value === "string")) {
        return getMatchedProps(String.prototype, matchProp);
      }
    }
  }

  // We are completing a variable / a property lookup.
  let properties = completionPart.split(".");
  let matchProp = properties.pop().trimLeft();
  let obj = dbgObject;

  // The first property must be found in the environment of the paused debugger
  // or of the global lexical scope.
  let env = anEnvironment || obj.asEnvironment();

  if (properties.length === 0) {
    return getMatchedPropsInEnvironment(env, matchProp);
  }

  let firstProp = properties.shift().trim();
  if (firstProp === "this") {
    // Special case for 'this' - try to get the Object from the Environment.
    // No problem if it throws, we will just not autocomplete.
    try {
      obj = env.object;
    } catch (e) {
      // Ignore.
    }
  } else if (hasArrayIndex(firstProp)) {
    obj = getArrayMemberProperty(null, env, firstProp);
  } else {
    obj = getVariableInEnvironment(env, firstProp);
  }

  if (!isObjectUsable(obj)) {
    return null;
  }

  // We get the rest of the properties recursively starting from the
  // Debugger.Object that wraps the first property
  for (let i = 0; i < properties.length; i++) {
    let prop = properties[i].trim();
    if (!prop) {
      return null;
    }

    if (hasArrayIndex(prop)) {
      // The property to autocomplete is a member of array. For example
      // list[i][j]..[n]. Traverse the array to get the actual element.
      obj = getArrayMemberProperty(obj, null, prop);
    } else {
      obj = DevToolsUtils.getProperty(obj, prop);
    }

    if (!isObjectUsable(obj)) {
      return null;
    }
  }

  // If the final property is a primitive
  if (typeof obj != "object") {
    return getMatchedProps(obj, matchProp);
  }

  return getMatchedPropsInDbgObject(obj, matchProp);
}

/**
 * Get the array member of obj for the given prop. For example, given
 * prop='list[0][1]' the element at [0][1] of obj.list is returned.
 *
 * @param object obj
 *        The object to operate on. Should be null if env is passed.
 * @param object env
 *        The Environment to operate in. Should be null if obj is passed.
 * @param string prop
 *        The property to return.
 * @return null or Object
 *         Returns null if the property couldn't be located. Otherwise the array
 *         member identified by prop.
 */
function getArrayMemberProperty(obj, env, prop) {
  // First get the array.
  let propWithoutIndices = prop.substr(0, prop.indexOf("["));

  if (env) {
    obj = getVariableInEnvironment(env, propWithoutIndices);
  } else {
    obj = DevToolsUtils.getProperty(obj, propWithoutIndices);
  }

  if (!isObjectUsable(obj)) {
    return null;
  }

  // Then traverse the list of indices to get the actual element.
  let result;
  let arrayIndicesRegex = /\[[^\]]*\]/g;
  while ((result = arrayIndicesRegex.exec(prop)) !== null) {
    let indexWithBrackets = result[0];
    let indexAsText = indexWithBrackets.substr(1, indexWithBrackets.length - 2);
    let index = parseInt(indexAsText, 10);

    if (isNaN(index)) {
      return null;
    }

    obj = DevToolsUtils.getProperty(obj, index);

    if (!isObjectUsable(obj)) {
      return null;
    }
  }

  return obj;
}

/**
 * Check if the given Debugger.Object can be used for autocomplete.
 *
 * @param Debugger.Object object
 *        The Debugger.Object to check.
 * @return boolean
 *         True if further inspection into the object is possible, or false
 *         otherwise.
 */
function isObjectUsable(object) {
  if (object == null) {
    return false;
  }

  if (typeof object == "object" && object.class == "DeadObject") {
    return false;
  }

  return true;
}

/**
 * @see getExactMatchImpl()
 */
function getVariableInEnvironment(anEnvironment, name) {
  return getExactMatchImpl(anEnvironment, name, DebuggerEnvironmentSupport);
}

/**
 * @see getMatchedPropsImpl()
 */
function getMatchedPropsInEnvironment(anEnvironment, match) {
  return getMatchedPropsImpl(anEnvironment, match, DebuggerEnvironmentSupport);
}

/**
 * @see getMatchedPropsImpl()
 */
function getMatchedPropsInDbgObject(dbgObject, match) {
  return getMatchedPropsImpl(dbgObject, match, DebuggerObjectSupport);
}

/**
 * @see getMatchedPropsImpl()
 */
function getMatchedProps(obj, match) {
  if (typeof obj != "object") {
    obj = obj.constructor.prototype;
  }
  return getMatchedPropsImpl(obj, match, JSObjectSupport);
}

/**
 * Get all properties in the given object (and its parent prototype chain) that
 * match a given prefix.
 *
 * @param mixed obj
 *        Object whose properties we want to filter.
 * @param string match
 *        Filter for properties that match this string.
 * @return object
 *         Object that contains the matchProp and the list of names.
 */
function getMatchedPropsImpl(obj, match, {chainIterator, getProperties}) {
  let matches = new Set();
  let numProps = 0;

  // We need to go up the prototype chain.
  let iter = chainIterator(obj);
  for (obj of iter) {
    let props = getProperties(obj);
    numProps += props.length;

    // If there are too many properties to event attempt autocompletion,
    // or if we have already added the max number, then stop looping
    // and return the partial set that has already been discovered.
    if (numProps >= MAX_AUTOCOMPLETE_ATTEMPTS ||
        matches.size >= MAX_AUTOCOMPLETIONS) {
      break;
    }

    for (let i = 0; i < props.length; i++) {
      let prop = props[i];
      if (prop.indexOf(match) != 0) {
        continue;
      }
      if (prop.indexOf("-") > -1) {
        continue;
      }
      // If it is an array index, we can't take it.
      // This uses a trick: converting a string to a number yields NaN if
      // the operation failed, and NaN is not equal to itself.
      // eslint-disable-next-line no-self-compare
      if (+prop != +prop) {
        matches.add(prop);
      }

      if (matches.size >= MAX_AUTOCOMPLETIONS) {
        break;
      }
    }
  }

  return {
    matchProp: match,
    matches: [...matches],
  };
}

/**
 * Returns a property value based on its name from the given object, by
 * recursively checking the object's prototype.
 *
 * @param object obj
 *        An object to look the property into.
 * @param string name
 *        The property that is looked up.
 * @returns object|undefined
 *        A Debugger.Object if the property exists in the object's prototype
 *        chain, undefined otherwise.
 */
function getExactMatchImpl(obj, name, {chainIterator, getProperty}) {
  // We need to go up the prototype chain.
  let iter = chainIterator(obj);
  for (obj of iter) {
    let prop = getProperty(obj, name, obj);
    if (prop) {
      return prop.value;
    }
  }
  return undefined;
}

var JSObjectSupport = {
  chainIterator: function* (obj) {
    while (obj) {
      yield obj;
      obj = Object.getPrototypeOf(obj);
    }
  },

  getProperties: function (obj) {
    return Object.getOwnPropertyNames(obj);
  },

  getProperty: function () {
    // getProperty is unsafe with raw JS objects.
    throw new Error("Unimplemented!");
  },
};

var DebuggerObjectSupport = {
  chainIterator: function* (obj) {
    while (obj) {
      yield obj;
      obj = obj.proto;
    }
  },

  getProperties: function (obj) {
    return obj.getOwnPropertyNames();
  },

  getProperty: function (obj, name, rootObj) {
    // This is left unimplemented in favor to DevToolsUtils.getProperty().
    throw new Error("Unimplemented!");
  },
};

var DebuggerEnvironmentSupport = {
  chainIterator: function* (obj) {
    while (obj) {
      yield obj;
      obj = obj.parent;
    }
  },

  getProperties: function (obj) {
    let names = obj.names();

    // Include 'this' in results (in sorted order)
    for (let i = 0; i < names.length; i++) {
      if (i === names.length - 1 || names[i + 1] > "this") {
        names.splice(i + 1, 0, "this");
        break;
      }
    }

    return names;
  },

  getProperty: function (obj, name) {
    let result;
    // Try/catch since name can be anything, and getVariable throws if
    // it's not a valid ECMAScript identifier name
    try {
      // TODO: we should use getVariableDescriptor() here - bug 725815.
      result = obj.getVariable(name);
    } catch (e) {
      // Ignore.
    }

    // FIXME: Need actual UI, bug 941287.
    if (result === undefined || result.optimizedOut ||
        result.missingArguments) {
      return null;
    }
    return { value: result };
  },
};

exports.JSPropertyProvider = DevToolsUtils.makeInfallible(JSPropertyProvider);

// Export a version that will throw (for tests)
exports.FallibleJSPropertyProvider = JSPropertyProvider;
PK
!<Z��k�l�lDchrome/devtools/modules/devtools/shared/webconsole/network-helper.js/* vim:set ts=2 sw=2 sts=2 et: */
/*
 * Software License Agreement (BSD License)
 *
 * Copyright (c) 2007, Parakey Inc.
 * All rights reserved.
 *
 * Redistribution and use of this software 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 Parakey Inc. nor the names of its
 *   contributors may be used to endorse or promote products
 *   derived from this software without specific prior
 *   written permission of Parakey Inc.
 *
 * 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.
 */

/*
 * Creator:
 *  Joe Hewitt
 * Contributors
 *  John J. Barton (IBM Almaden)
 *  Jan Odvarko (Mozilla Corp.)
 *  Max Stepanov (Aptana Inc.)
 *  Rob Campbell (Mozilla Corp.)
 *  Hans Hillen (Paciello Group, Mozilla)
 *  Curtis Bartley (Mozilla Corp.)
 *  Mike Collins (IBM Almaden)
 *  Kevin Decker
 *  Mike Ratcliffe (Comartis AG)
 *  Hernan Rodríguez Colmeiro
 *  Austin Andrews
 *  Christoph Dorn
 *  Steven Roussey (AppCenter Inc, Network54)
 *  Mihai Sucan (Mozilla Corp.)
 */

"use strict";

const {components, Cc, Ci} = require("chrome");
loader.lazyImporter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const Services = require("Services");
const { LocalizationHelper } = require("devtools/shared/l10n");
const L10N = new LocalizationHelper("devtools/client/locales/netmonitor.properties");

// The cache used in the `nsIURL` function.
const gNSURLStore = new Map();

/**
 * Helper object for networking stuff.
 *
 * Most of the following functions have been taken from the Firebug source. They
 * have been modified to match the Firefox coding rules.
 */
var NetworkHelper = {
  /**
   * Converts text with a given charset to unicode.
   *
   * @param string text
   *        Text to convert.
   * @param string charset
   *        Charset to convert the text to.
   * @returns string
   *          Converted text.
   */
  convertToUnicode: function (text, charset) {
    let conv = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
        .createInstance(Ci.nsIScriptableUnicodeConverter);
    try {
      conv.charset = charset || "UTF-8";
      return conv.ConvertToUnicode(text);
    } catch (ex) {
      return text;
    }
  },

  /**
   * Reads all available bytes from stream and converts them to charset.
   *
   * @param nsIInputStream stream
   * @param string charset
   * @returns string
   *          UTF-16 encoded string based on the content of stream and charset.
   */
  readAndConvertFromStream: function (stream, charset) {
    let text = null;
    try {
      text = NetUtil.readInputStreamToString(stream, stream.available());
      return this.convertToUnicode(text, charset);
    } catch (err) {
      return text;
    }
  },

   /**
   * Reads the posted text from request.
   *
   * @param nsIHttpChannel request
   * @param string charset
   *        The content document charset, used when reading the POSTed data.
   * @returns string or null
   *          Returns the posted string if it was possible to read from request
   *          otherwise null.
   */
  readPostTextFromRequest: function (request, charset) {
    if (request instanceof Ci.nsIUploadChannel) {
      let iStream = request.uploadStream;

      let isSeekableStream = false;
      if (iStream instanceof Ci.nsISeekableStream) {
        isSeekableStream = true;
      }

      let prevOffset;
      if (isSeekableStream) {
        prevOffset = iStream.tell();
        iStream.seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);
      }

      // Read data from the stream.
      let text = this.readAndConvertFromStream(iStream, charset);

      // Seek locks the file, so seek to the beginning only if necko hasn't
      // read it yet, since necko doesn't seek to 0 before reading (at lest
      // not till 459384 is fixed).
      if (isSeekableStream && prevOffset == 0) {
        iStream.seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);
      }
      return text;
    }
    return null;
  },

  /**
   * Reads the posted text from the page's cache.
   *
   * @param nsIDocShell docShell
   * @param string charset
   * @returns string or null
   *          Returns the posted string if it was possible to read from
   *          docShell otherwise null.
   */
  readPostTextFromPage: function (docShell, charset) {
    let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
    return this.readPostTextFromPageViaWebNav(webNav, charset);
  },

  /**
   * Reads the posted text from the page's cache, given an nsIWebNavigation
   * object.
   *
   * @param nsIWebNavigation webNav
   * @param string charset
   * @returns string or null
   *          Returns the posted string if it was possible to read from
   *          webNav, otherwise null.
   */
  readPostTextFromPageViaWebNav: function (webNav, charset) {
    if (webNav instanceof Ci.nsIWebPageDescriptor) {
      let descriptor = webNav.currentDescriptor;

      if (descriptor instanceof Ci.nsISHEntry && descriptor.postData &&
          descriptor instanceof Ci.nsISeekableStream) {
        descriptor.seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);

        return this.readAndConvertFromStream(descriptor, charset);
      }
    }
    return null;
  },

  /**
   * Gets the web appId that is associated with request.
   *
   * @param nsIHttpChannel request
   * @returns number|null
   *          The appId for the given request, if available.
   */
  getAppIdForRequest: function (request) {
    try {
      return this.getRequestLoadContext(request).appId;
    } catch (ex) {
      // request loadContent is not always available.
    }
    return null;
  },

  /**
   * Gets the topFrameElement that is associated with request. This
   * works in single-process and multiprocess contexts. It may cross
   * the content/chrome boundary.
   *
   * @param nsIHttpChannel request
   * @returns nsIDOMElement|null
   *          The top frame element for the given request.
   */
  getTopFrameForRequest: function (request) {
    try {
      return this.getRequestLoadContext(request).topFrameElement;
    } catch (ex) {
      // request loadContent is not always available.
    }
    return null;
  },

  /**
   * Gets the nsIDOMWindow that is associated with request.
   *
   * @param nsIHttpChannel request
   * @returns nsIDOMWindow or null
   */
  getWindowForRequest: function (request) {
    try {
      return this.getRequestLoadContext(request).associatedWindow;
    } catch (ex) {
      // TODO: bug 802246 - getWindowForRequest() throws on b2g: there is no
      // associatedWindow property.
    }
    return null;
  },

  /**
   * Gets the nsILoadContext that is associated with request.
   *
   * @param nsIHttpChannel request
   * @returns nsILoadContext or null
   */
  getRequestLoadContext: function (request) {
    try {
      return request.notificationCallbacks.getInterface(Ci.nsILoadContext);
    } catch (ex) {
      // Ignore.
    }

    try {
      return request.loadGroup.notificationCallbacks
        .getInterface(Ci.nsILoadContext);
    } catch (ex) {
      // Ignore.
    }

    return null;
  },

  /**
   * Determines whether the request has been made for the top level document.
   *
   * @param nsIHttpChannel request
   * @returns Boolean True if the request represents the top level document.
   */
  isTopLevelLoad: function (request) {
    if (request instanceof Ci.nsIChannel) {
      let loadInfo = request.loadInfo;
      if (loadInfo && loadInfo.isTopLevelLoad) {
        return (request.loadFlags & Ci.nsIChannel.LOAD_DOCUMENT_URI);
      }
    }

    return false;
  },

  /**
   * Loads the content of url from the cache.
   *
   * @param string url
   *        URL to load the cached content for.
   * @param string charset
   *        Assumed charset of the cached content. Used if there is no charset
   *        on the channel directly.
   * @param function callback
   *        Callback that is called with the loaded cached content if available
   *        or null if something failed while getting the cached content.
   */
  loadFromCache: function (url, charset, callback) {
    let channel = NetUtil.newChannel({uri: url,
                                      loadUsingSystemPrincipal: true});

    // Ensure that we only read from the cache and not the server.
    channel.loadFlags = Ci.nsIRequest.LOAD_FROM_CACHE |
      Ci.nsICachingChannel.LOAD_ONLY_FROM_CACHE |
      Ci.nsICachingChannel.LOAD_BYPASS_LOCAL_CACHE_IF_BUSY;

    NetUtil.asyncFetch(
      channel,
      (inputStream, statusCode, request) => {
        if (!components.isSuccessCode(statusCode)) {
          callback(null);
          return;
        }

        // Try to get the encoding from the channel. If there is none, then use
        // the passed assumed charset.
        let requestChannel = request.QueryInterface(Ci.nsIChannel);
        let contentCharset = requestChannel.contentCharset || charset;

        // Read the content of the stream using contentCharset as encoding.
        callback(this.readAndConvertFromStream(inputStream, contentCharset));
      });
  },

  /**
   * Parse a raw Cookie header value.
   *
   * @param string header
   *        The raw Cookie header value.
   * @return array
   *         Array holding an object for each cookie. Each object holds the
   *         following properties: name and value.
   */
  parseCookieHeader: function (header) {
    let cookies = header.split(";");
    let result = [];

    cookies.forEach(function (cookie) {
      let equal = cookie.indexOf("=");
      let name = cookie.substr(0, equal);
      let value = cookie.substr(equal + 1);
      result.push({name: unescape(name.trim()),
                   value: unescape(value.trim())});
    });

    return result;
  },

  /**
   * Parse a raw Set-Cookie header value.
   *
   * @param string header
   *        The raw Set-Cookie header value.
   * @return array
   *         Array holding an object for each cookie. Each object holds the
   *         following properties: name, value, secure (boolean), httpOnly
   *         (boolean), path, domain and expires (ISO date string).
   */
  parseSetCookieHeader: function (header) {
    let rawCookies = header.split(/\r\n|\n|\r/);
    let cookies = [];

    rawCookies.forEach(function (cookie) {
      let equal = cookie.indexOf("=");
      let name = unescape(cookie.substr(0, equal).trim());
      let parts = cookie.substr(equal + 1).split(";");
      let value = unescape(parts.shift().trim());

      cookie = {name: name, value: value};

      parts.forEach(function (part) {
        part = part.trim();
        if (part.toLowerCase() == "secure") {
          cookie.secure = true;
        } else if (part.toLowerCase() == "httponly") {
          cookie.httpOnly = true;
        } else if (part.indexOf("=") > -1) {
          let pair = part.split("=");
          pair[0] = pair[0].toLowerCase();
          if (pair[0] == "path" || pair[0] == "domain") {
            cookie[pair[0]] = pair[1];
          } else if (pair[0] == "expires") {
            try {
              pair[1] = pair[1].replace(/-/g, " ");
              cookie.expires = new Date(pair[1]).toISOString();
            } catch (ex) {
              // Ignore.
            }
          }
        }
      });

      cookies.push(cookie);
    });

    return cookies;
  },

  // This is a list of all the mime category maps jviereck could find in the
  // firebug code base.
  mimeCategoryMap: {
    "text/plain": "txt",
    "text/html": "html",
    "text/xml": "xml",
    "text/xsl": "txt",
    "text/xul": "txt",
    "text/css": "css",
    "text/sgml": "txt",
    "text/rtf": "txt",
    "text/x-setext": "txt",
    "text/richtext": "txt",
    "text/javascript": "js",
    "text/jscript": "txt",
    "text/tab-separated-values": "txt",
    "text/rdf": "txt",
    "text/xif": "txt",
    "text/ecmascript": "js",
    "text/vnd.curl": "txt",
    "text/x-json": "json",
    "text/x-js": "txt",
    "text/js": "txt",
    "text/vbscript": "txt",
    "view-source": "txt",
    "view-fragment": "txt",
    "application/xml": "xml",
    "application/xhtml+xml": "xml",
    "application/atom+xml": "xml",
    "application/rss+xml": "xml",
    "application/vnd.mozilla.maybe.feed": "xml",
    "application/vnd.mozilla.xul+xml": "xml",
    "application/javascript": "js",
    "application/x-javascript": "js",
    "application/x-httpd-php": "txt",
    "application/rdf+xml": "xml",
    "application/ecmascript": "js",
    "application/http-index-format": "txt",
    "application/json": "json",
    "application/x-js": "txt",
    "application/x-mpegurl": "txt",
    "application/vnd.apple.mpegurl": "txt",
    "multipart/mixed": "txt",
    "multipart/x-mixed-replace": "txt",
    "image/svg+xml": "svg",
    "application/octet-stream": "bin",
    "image/jpeg": "image",
    "image/jpg": "image",
    "image/gif": "image",
    "image/png": "image",
    "image/bmp": "image",
    "application/x-shockwave-flash": "flash",
    "video/x-flv": "flash",
    "audio/mpeg3": "media",
    "audio/x-mpeg-3": "media",
    "video/mpeg": "media",
    "video/x-mpeg": "media",
    "video/vnd.mpeg.dash.mpd": "xml",
    "audio/ogg": "media",
    "application/ogg": "media",
    "application/x-ogg": "media",
    "application/x-midi": "media",
    "audio/midi": "media",
    "audio/x-mid": "media",
    "audio/x-midi": "media",
    "music/crescendo": "media",
    "audio/wav": "media",
    "audio/x-wav": "media",
    "text/json": "json",
    "application/x-json": "json",
    "application/json-rpc": "json",
    "application/x-web-app-manifest+json": "json",
    "application/manifest+json": "json"
  },

  /**
   * Check if the given MIME type is a text-only MIME type.
   *
   * @param string mimeType
   * @return boolean
   */
  isTextMimeType: function (mimeType) {
    if (mimeType.indexOf("text/") == 0) {
      return true;
    }

    // XML and JSON often come with custom MIME types, so in addition to the
    // standard "application/xml" and "application/json", we also look for
    // variants like "application/x-bigcorp+xml". For JSON we allow "+json" and
    // "-json" as suffixes.
    if (/^application\/\w+(?:[\.-]\w+)*(?:\+xml|[-+]json)$/.test(mimeType)) {
      return true;
    }

    let category = this.mimeCategoryMap[mimeType] || null;
    switch (category) {
      case "txt":
      case "js":
      case "json":
      case "css":
      case "html":
      case "svg":
      case "xml":
        return true;

      default:
        return false;
    }
  },

  /**
   * Takes a securityInfo object of nsIRequest, the nsIRequest itself and
   * extracts security information from them.
   *
   * @param object securityInfo
   *        The securityInfo object of a request. If null channel is assumed
   *        to be insecure.
   * @param object httpActivity
   *        The httpActivity object for the request with at least members
   *        { private, hostname }.
   *
   * @return object
   *         Returns an object containing following members:
   *          - state: The security of the connection used to fetch this
   *                   request. Has one of following string values:
   *                    * "insecure": the connection was not secure (only http)
   *                    * "weak": the connection has minor security issues
   *                    * "broken": secure connection failed (e.g. expired cert)
   *                    * "secure": the connection was properly secured.
   *          If state == broken:
   *            - errorMessage: full error message from
   *                            nsITransportSecurityInfo.
   *          If state == secure:
   *            - protocolVersion: one of TLSv1, TLSv1.1, TLSv1.2, TLSv1.3.
   *            - cipherSuite: the cipher suite used in this connection.
   *            - cert: information about certificate used in this connection.
   *                    See parseCertificateInfo for the contents.
   *            - hsts: true if host uses Strict Transport Security,
   *                    false otherwise
   *            - hpkp: true if host uses Public Key Pinning, false otherwise
   *          If state == weak: Same as state == secure and
   *            - weaknessReasons: list of reasons that cause the request to be
   *                               considered weak. See getReasonsForWeakness.
   */
  parseSecurityInfo: function (securityInfo, httpActivity) {
    const info = {
      state: "insecure",
    };

    // The request did not contain any security info.
    if (!securityInfo) {
      return info;
    }

    /**
     * Different scenarios to consider here and how they are handled:
     * - request is HTTP, the connection is not secure
     *   => securityInfo is null
     *      => state === "insecure"
     *
     * - request is HTTPS, the connection is secure
     *   => .securityState has STATE_IS_SECURE flag
     *      => state === "secure"
     *
     * - request is HTTPS, the connection has security issues
     *   => .securityState has STATE_IS_INSECURE flag
     *   => .errorCode is an NSS error code.
     *      => state === "broken"
     *
     * - request is HTTPS, the connection was terminated before the security
     *   could be validated
     *   => .securityState has STATE_IS_INSECURE flag
     *   => .errorCode is NOT an NSS error code.
     *   => .errorMessage is not available.
     *      => state === "insecure"
     *
     * - request is HTTPS but it uses a weak cipher or old protocol, see
     *   https://hg.mozilla.org/mozilla-central/annotate/def6ed9d1c1a/
     *   security/manager/ssl/nsNSSCallbacks.cpp#l1233
     * - request is mixed content (which makes no sense whatsoever)
     *   => .securityState has STATE_IS_BROKEN flag
     *   => .errorCode is NOT an NSS error code
     *   => .errorMessage is not available
     *      => state === "weak"
     */

    securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
    securityInfo.QueryInterface(Ci.nsISSLStatusProvider);

    const wpl = Ci.nsIWebProgressListener;
    const NSSErrorsService = Cc["@mozilla.org/nss_errors_service;1"]
                               .getService(Ci.nsINSSErrorsService);
    const SSLStatus = securityInfo.SSLStatus;
    if (!NSSErrorsService.isNSSErrorCode(securityInfo.errorCode)) {
      const state = securityInfo.securityState;

      let uri = null;
      if (httpActivity.channel && httpActivity.channel.URI) {
        uri = httpActivity.channel.URI;
      }
      if (uri && !uri.schemeIs("https") && !uri.schemeIs("wss")) {
        // it is not enough to look at the transport security info -
        // schemes other than https and wss are subject to
        // downgrade/etc at the scheme level and should always be
        // considered insecure
        info.state = "insecure";
      } else if (state & wpl.STATE_IS_SECURE) {
        // The connection is secure if the scheme is sufficient
        info.state = "secure";
      } else if (state & wpl.STATE_IS_BROKEN) {
        // The connection is not secure, there was no error but there's some
        // minor security issues.
        info.state = "weak";
        info.weaknessReasons = this.getReasonsForWeakness(state);
      } else if (state & wpl.STATE_IS_INSECURE) {
        // This was most likely an https request that was aborted before
        // validation. Return info as info.state = insecure.
        return info;
      } else {
        DevToolsUtils.reportException("NetworkHelper.parseSecurityInfo",
          "Security state " + state + " has no known STATE_IS_* flags.");
        return info;
      }

      // Cipher suite.
      info.cipherSuite = SSLStatus.cipherName;

      // Key exchange group name.
      info.keaGroupName = SSLStatus.keaGroupName;
      // Localise two special values.
      if (info.keaGroupName == "none") {
        info.keaGroupName = L10N.getStr("netmonitor.security.keaGroup.none");
      }
      if (info.keaGroupName == "custom") {
        info.keaGroupName = L10N.getStr("netmonitor.security.keaGroup.custom");
      }
      if (info.keaGroupName == "unknown group") {
        info.keaGroupName = L10N.getStr("netmonitor.security.keaGroup.unknown");
      }

      // Certificate signature scheme.
      info.signatureSchemeName = SSLStatus.signatureSchemeName;
      // Localise two special values.
      if (info.signatureSchemeName == "none") {
        info.signatureSchemeName =
          L10N.getStr("netmonitor.security.signatureScheme.none");
      }
      if (info.signatureSchemeName == "unknown signature") {
        info.signatureSchemeName =
          L10N.getStr("netmonitor.security.signatureScheme.unknown");
      }

      // Protocol version.
      info.protocolVersion =
        this.formatSecurityProtocol(SSLStatus.protocolVersion);

      // Certificate.
      info.cert = this.parseCertificateInfo(SSLStatus.serverCert);

      // HSTS and HPKP if available.
      if (httpActivity.hostname) {
        const sss = Cc["@mozilla.org/ssservice;1"]
                      .getService(Ci.nsISiteSecurityService);

        // SiteSecurityService uses different storage if the channel is
        // private. Thus we must give isSecureURI correct flags or we
        // might get incorrect results.
        let flags = (httpActivity.private) ?
                      Ci.nsISocketProvider.NO_PERMANENT_STORAGE : 0;

        if (!uri) {
          // isSecureURI only cares about the host, not the scheme.
          let host = httpActivity.hostname;
          uri = Services.io.newURI("https://" + host);
        }

        info.hsts = sss.isSecureURI(sss.HEADER_HSTS, uri, flags);
        info.hpkp = sss.isSecureURI(sss.HEADER_HPKP, uri, flags);
      } else {
        DevToolsUtils.reportException("NetworkHelper.parseSecurityInfo",
          "Could not get HSTS/HPKP status as hostname is not available.");
        info.hsts = false;
        info.hpkp = false;
      }
    } else {
      // The connection failed.
      info.state = "broken";
      info.errorMessage = securityInfo.errorMessage;
    }

    return info;
  },

  /**
   * Takes an nsIX509Cert and returns an object with certificate information.
   *
   * @param nsIX509Cert cert
   *        The certificate to extract the information from.
   * @return object
   *         An object with following format:
   *           {
   *             subject: { commonName, organization, organizationalUnit },
   *             issuer: { commonName, organization, organizationUnit },
   *             validity: { start, end },
   *             fingerprint: { sha1, sha256 }
   *           }
   */
  parseCertificateInfo: function (cert) {
    let info = {};
    if (cert) {
      info.subject = {
        commonName: cert.commonName,
        organization: cert.organization,
        organizationalUnit: cert.organizationalUnit,
      };

      info.issuer = {
        commonName: cert.issuerCommonName,
        organization: cert.issuerOrganization,
        organizationUnit: cert.issuerOrganizationUnit,
      };

      info.validity = {
        start: cert.validity.notBeforeLocalDay,
        end: cert.validity.notAfterLocalDay,
      };

      info.fingerprint = {
        sha1: cert.sha1Fingerprint,
        sha256: cert.sha256Fingerprint,
      };
    } else {
      DevToolsUtils.reportException("NetworkHelper.parseCertificateInfo",
        "Secure connection established without certificate.");
    }

    return info;
  },

  /**
   * Takes protocolVersion of SSLStatus object and returns human readable
   * description.
   *
   * @param Number version
   *        One of nsISSLStatus version constants.
   * @return string
   *         One of TLSv1, TLSv1.1, TLSv1.2, TLSv1.3 if @param version
   *         is valid, Unknown otherwise.
   */
  formatSecurityProtocol: function (version) {
    switch (version) {
      case Ci.nsISSLStatus.TLS_VERSION_1:
        return "TLSv1";
      case Ci.nsISSLStatus.TLS_VERSION_1_1:
        return "TLSv1.1";
      case Ci.nsISSLStatus.TLS_VERSION_1_2:
        return "TLSv1.2";
      case Ci.nsISSLStatus.TLS_VERSION_1_3:
        return "TLSv1.3";
      default:
        DevToolsUtils.reportException("NetworkHelper.formatSecurityProtocol",
          "protocolVersion " + version + " is unknown.");
        return "Unknown";
    }
  },

  /**
   * Takes the securityState bitfield and returns reasons for weak connection
   * as an array of strings.
   *
   * @param Number state
   *        nsITransportSecurityInfo.securityState.
   *
   * @return Array[String]
   *         List of weakness reasons. A subset of { cipher } where
   *         * cipher: The cipher suite is consireded to be weak (RC4).
   */
  getReasonsForWeakness: function (state) {
    const wpl = Ci.nsIWebProgressListener;

    // If there's non-fatal security issues the request has STATE_IS_BROKEN
    // flag set. See https://hg.mozilla.org/mozilla-central/file/44344099d119
    // /security/manager/ssl/nsNSSCallbacks.cpp#l1233
    let reasons = [];

    if (state & wpl.STATE_IS_BROKEN) {
      let isCipher = state & wpl.STATE_USES_WEAK_CRYPTO;

      if (isCipher) {
        reasons.push("cipher");
      }

      if (!isCipher) {
        DevToolsUtils.reportException("NetworkHelper.getReasonsForWeakness",
          "STATE_IS_BROKEN without a known reason. Full state was: " + state);
      }
    }

    return reasons;
  },

  /**
   * Parse a url's query string into its components
   *
   * @param string queryString
   *        The query part of a url
   * @return array
   *         Array of query params {name, value}
   */
  parseQueryString: function (queryString) {
    // Make sure there's at least one param available.
    // Be careful here, params don't necessarily need to have values, so
    // no need to verify the existence of a "=".
    if (!queryString) {
      return null;
    }

    // Turn the params string into an array containing { name: value } tuples.
    let paramsArray = queryString.replace(/^[?&]/, "").split("&").map(e => {
      let param = e.split("=");
      return {
        name: param[0] ?
          NetworkHelper.convertToUnicode(unescape(param[0])) : "",
        value: param[1] ?
          NetworkHelper.convertToUnicode(unescape(param[1])) : ""
      };
    });

    return paramsArray;
  },

  /**
   * Helper for getting an nsIURL instance out of a string.
   */
  nsIURL: function (url, store = gNSURLStore) {
    if (store.has(url)) {
      return store.get(url);
    }

    let uri = Services.io.newURI(url).QueryInterface(Ci.nsIURL);
    store.set(url, uri);
    return uri;
  }
};

for (let prop of Object.getOwnPropertyNames(NetworkHelper)) {
  exports[prop] = NetworkHelper[prop];
}
PK
!<��+Bl'l'Echrome/devtools/modules/devtools/shared/webconsole/network-monitor.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft= javascript ts=2 et sw=2 tw=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 {Cc, Ci, Cm, Cu, Cr, components} = require("chrome");
const Services = require("Services");
const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");

loader.lazyRequireGetter(this, "NetworkHelper",
                         "devtools/shared/webconsole/network-helper");
loader.lazyRequireGetter(this, "DevToolsUtils",
                         "devtools/shared/DevToolsUtils");
loader.lazyRequireGetter(this, "flags",
                         "devtools/shared/flags");
loader.lazyRequireGetter(this, "DebuggerServer",
                         "devtools/server/main", true);
loader.lazyImporter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm");
loader.lazyServiceGetter(this, "gActivityDistributor",
                         "@mozilla.org/network/http-activity-distributor;1",
                         "nsIHttpActivityDistributor");
const {NetworkThrottleManager} = require("devtools/shared/webconsole/throttle");

// Network logging

// The maximum uint32 value.
const PR_UINT32_MAX = 4294967295;

// HTTP status codes.
const HTTP_MOVED_PERMANENTLY = 301;
const HTTP_FOUND = 302;
const HTTP_SEE_OTHER = 303;
const HTTP_TEMPORARY_REDIRECT = 307;

// The maximum number of bytes a NetworkResponseListener can hold: 1 MB
const RESPONSE_BODY_LIMIT = 1048576;
// Exported for testing.
exports.RESPONSE_BODY_LIMIT = RESPONSE_BODY_LIMIT;

/**
 * Check if a given network request should be logged by a network monitor
 * based on the specified filters.
 *
 * @param nsIHttpChannel channel
 *        Request to check.
 * @param filters
 *        NetworkMonitor filters to match against.
 * @return boolean
 *         True if the network request should be logged, false otherwise.
 */
function matchRequest(channel, filters) {
  // Log everything if no filter is specified
  if (!filters.outerWindowID && !filters.window && !filters.appId) {
    return true;
  }

  // Ignore requests from chrome or add-on code when we are monitoring
  // content.
  // TODO: one particular test (browser_styleeditor_fetch-from-cache.js) needs
  // the flags.testing check. We will move to a better way to serve
  // its needs in bug 1167188, where this check should be removed.
  if (!flags.testing && channel.loadInfo &&
      channel.loadInfo.loadingDocument === null &&
      channel.loadInfo.loadingPrincipal ===
      Services.scriptSecurityManager.getSystemPrincipal()) {
    return false;
  }

  if (filters.window) {
    // Since frames support, this.window may not be the top level content
    // frame, so that we can't only compare with win.top.
    let win = NetworkHelper.getWindowForRequest(channel);
    while (win) {
      if (win == filters.window) {
        return true;
      }
      if (win.parent == win) {
        break;
      }
      win = win.parent;
    }
  }

  if (filters.outerWindowID) {
    let topFrame = NetworkHelper.getTopFrameForRequest(channel);
    // topFrame is typically null for some chrome requests like favicons
    if (topFrame) {
      try {
        if (topFrame.outerWindowID == filters.outerWindowID) {
          return true;
        }
      } catch (e) {
        // outerWindowID getter from browser.xml (non-remote <xul:browser>) may
        // throw when closing a tab while resources are still loading.
      }
    }
  }

  if (filters.appId) {
    let appId = NetworkHelper.getAppIdForRequest(channel);
    if (appId && appId == filters.appId) {
      return true;
    }
  }

  return false;
}

/**
 * This is a nsIChannelEventSink implementation that monitors channel redirects and
 * informs the registered StackTraceCollector about the old and new channels.
 */
const SINK_CLASS_DESCRIPTION = "NetworkMonitor Channel Event Sink";
const SINK_CLASS_ID = components.ID("{e89fa076-c845-48a8-8c45-2604729eba1d}");
const SINK_CONTRACT_ID = "@mozilla.org/network/monitor/channeleventsink;1";
const SINK_CATEGORY_NAME = "net-channel-event-sinks";

function ChannelEventSink() {
  this.wrappedJSObject = this;
  this.collectors = new Set();
}

ChannelEventSink.prototype = {
  QueryInterface: XPCOMUtils.generateQI([Ci.nsIChannelEventSink]),

  registerCollector(collector) {
    this.collectors.add(collector);
  },

  unregisterCollector(collector) {
    this.collectors.delete(collector);

    if (this.collectors.size == 0) {
      ChannelEventSinkFactory.unregister();
    }
  },

  asyncOnChannelRedirect(oldChannel, newChannel, flags, callback) {
    for (let collector of this.collectors) {
      try {
        collector.onChannelRedirect(oldChannel, newChannel, flags);
      } catch (ex) {
        console.error("StackTraceCollector.onChannelRedirect threw an exception", ex);
      }
    }
    callback.onRedirectVerifyCallback(Cr.NS_OK);
  }
};

const ChannelEventSinkFactory = XPCOMUtils.generateSingletonFactory(ChannelEventSink);

ChannelEventSinkFactory.register = function () {
  const registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
  if (registrar.isCIDRegistered(SINK_CLASS_ID)) {
    return;
  }

  registrar.registerFactory(SINK_CLASS_ID,
                            SINK_CLASS_DESCRIPTION,
                            SINK_CONTRACT_ID,
                            ChannelEventSinkFactory);

  XPCOMUtils.categoryManager.addCategoryEntry(SINK_CATEGORY_NAME, SINK_CONTRACT_ID,
    SINK_CONTRACT_ID, false, true);
};

ChannelEventSinkFactory.unregister = function () {
  const registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
  registrar.unregisterFactory(SINK_CLASS_ID, ChannelEventSinkFactory);

  XPCOMUtils.categoryManager.deleteCategoryEntry(SINK_CATEGORY_NAME, SINK_CONTRACT_ID,
    false);
};

ChannelEventSinkFactory.getService = function () {
  // Make sure the ChannelEventSink service is registered before accessing it
  ChannelEventSinkFactory.register();

  return Cc[SINK_CONTRACT_ID].getService(Ci.nsIChannelEventSink).wrappedJSObject;
};

function StackTraceCollector(filters) {
  this.filters = filters;
  this.stacktracesById = new Map();
}

StackTraceCollector.prototype = {
  init() {
    Services.obs.addObserver(this, "http-on-opening-request");
    ChannelEventSinkFactory.getService().registerCollector(this);
  },

  destroy() {
    Services.obs.removeObserver(this, "http-on-opening-request");
    ChannelEventSinkFactory.getService().unregisterCollector(this);
  },

  _saveStackTrace(channel, stacktrace) {
    this.stacktracesById.set(channel.channelId, stacktrace);
  },

  observe(subject) {
    let channel = subject.QueryInterface(Ci.nsIHttpChannel);

    if (!matchRequest(channel, this.filters)) {
      return;
    }

    // Convert the nsIStackFrame XPCOM objects to a nice JSON that can be
    // passed around through message managers etc.
    let frame = components.stack;
    let stacktrace = [];
    if (frame && frame.caller) {
      frame = frame.caller;
      while (frame) {
        stacktrace.push({
          filename: frame.filename,
          lineNumber: frame.lineNumber,
          columnNumber: frame.columnNumber,
          functionName: frame.name,
          asyncCause: frame.asyncCause,
        });
        frame = frame.caller || frame.asyncCaller;
      }
    }

    this._saveStackTrace(channel, stacktrace);
  },

  onChannelRedirect(oldChannel, newChannel, flags) {
    // We can be called with any nsIChannel, but are interested only in HTTP channels
    try {
      oldChannel.QueryInterface(Ci.nsIHttpChannel);
      newChannel.QueryInterface(Ci.nsIHttpChannel);
    } catch (ex) {
      return;
    }

    let oldId = oldChannel.channelId;
    let stacktrace = this.stacktracesById.get(oldId);
    if (stacktrace) {
      this.stacktracesById.delete(oldId);
      this._saveStackTrace(newChannel, stacktrace);
    }
  },

  getStackTrace(channelId) {
    let trace = this.stacktracesById.get(channelId);
    this.stacktracesById.delete(channelId);
    return trace;
  }
};

exports.StackTraceCollector = StackTraceCollector;

/**
 * The network response listener implements the nsIStreamListener and
 * nsIRequestObserver interfaces. This is used within the NetworkMonitor feature
 * to get the response body of the request.
 *
 * The code is mostly based on code listings from:
 *
 *   http://www.softwareishard.com/blog/firebug/
 *      nsitraceablechannel-intercept-http-traffic/
 *
 * @constructor
 * @param object owner
 *        The response listener owner. This object needs to hold the
 *        |openResponses| object.
 * @param object httpActivity
 *        HttpActivity object associated with this request. See NetworkMonitor
 *        for more information.
 */
function NetworkResponseListener(owner, httpActivity) {
  this.owner = owner;
  this.receivedData = "";
  this.httpActivity = httpActivity;
  this.bodySize = 0;
  // Indicates if the response had a size greater than RESPONSE_BODY_LIMIT.
  this.truncated = false;
  // Note that this is really only needed for the non-e10s case.
  // See bug 1309523.
  let channel = this.httpActivity.channel;
  this._wrappedNotificationCallbacks = channel.notificationCallbacks;
  channel.notificationCallbacks = this;
}

NetworkResponseListener.prototype = {
  QueryInterface:
    XPCOMUtils.generateQI([Ci.nsIStreamListener, Ci.nsIInputStreamCallback,
                           Ci.nsIRequestObserver, Ci.nsIInterfaceRequestor,
                           Ci.nsISupports]),

  // nsIInterfaceRequestor implementation

  /**
   * This object implements nsIProgressEventSink, but also needs to forward
   * interface requests to the notification callbacks of other objects.
   */
  getInterface(iid) {
    if (iid.equals(Ci.nsIProgressEventSink)) {
      return this;
    }
    if (this._wrappedNotificationCallbacks) {
      return this._wrappedNotificationCallbacks.getInterface(iid);
    }
    throw Cr.NS_ERROR_NO_INTERFACE;
  },

  /**
   * Forward notifications for interfaces this object implements, in case other
   * objects also implemented them.
   */
  _forwardNotification(iid, method, args) {
    if (!this._wrappedNotificationCallbacks) {
      return;
    }
    try {
      let impl = this._wrappedNotificationCallbacks.getInterface(iid);
      impl[method].apply(impl, args);
    } catch (e) {
      if (e.result != Cr.NS_ERROR_NO_INTERFACE) {
        throw e;
      }
    }
  },

  /**
   * This NetworkResponseListener tracks the NetworkMonitor.openResponses object
   * to find the associated uncached headers.
   * @private
   */
  _foundOpenResponse: false,

  /**
   * If the channel already had notificationCallbacks, hold them here internally
   * so that we can forward getInterface requests to that object.
   */
  _wrappedNotificationCallbacks: null,

  /**
   * The response listener owner.
   */
  owner: null,

  /**
   * The response will be written into the outputStream of this nsIPipe.
   * Both ends of the pipe must be blocking.
   */
  sink: null,

  /**
   * The HttpActivity object associated with this response.
   */
  httpActivity: null,

  /**
   * Stores the received data as a string.
   */
  receivedData: null,

  /**
   * The uncompressed, decoded response body size.
   */
  bodySize: null,

  /**
   * Response size on the wire, potentially compressed / encoded.
   */
  transferredSize: null,

  /**
   * The nsIRequest we are started for.
   */
  request: null,

  /**
   * Set the async listener for the given nsIAsyncInputStream. This allows us to
   * wait asynchronously for any data coming from the stream.
   *
   * @param nsIAsyncInputStream stream
   *        The input stream from where we are waiting for data to come in.
   * @param nsIInputStreamCallback listener
   *        The input stream callback you want. This is an object that must have
   *        the onInputStreamReady() method. If the argument is null, then the
   *        current callback is removed.
   * @return void
   */
  setAsyncListener: function (stream, listener) {
    // Asynchronously wait for the stream to be readable or closed.
    stream.asyncWait(listener, 0, 0, Services.tm.mainThread);
  },

  /**
   * Stores the received data, if request/response body logging is enabled. It
   * also does limit the number of stored bytes, based on the
   * RESPONSE_BODY_LIMIT constant.
   *
   * Learn more about nsIStreamListener at:
   * https://developer.mozilla.org/en/XPCOM_Interface_Reference/nsIStreamListener
   *
   * @param nsIRequest request
   * @param nsISupports context
   * @param nsIInputStream inputStream
   * @param unsigned long offset
   * @param unsigned long count
   */
  onDataAvailable: function (request, context, inputStream, offset, count) {
    this._findOpenResponse();
    let data = NetUtil.readInputStreamToString(inputStream, count);

    this.bodySize += count;

    if (!this.httpActivity.discardResponseBody) {
      if (this.receivedData.length < RESPONSE_BODY_LIMIT) {
        this.receivedData +=
          NetworkHelper.convertToUnicode(data, request.contentCharset);
      } else {
        this.truncated = true;
      }
    }
  },

  /**
   * See documentation at
   * https://developer.mozilla.org/En/NsIRequestObserver
   *
   * @param nsIRequest request
   * @param nsISupports context
   */
  onStartRequest: function (request) {
    // Converter will call this again, we should just ignore that.
    if (this.request) {
      return;
    }

    this.request = request;
    this._getSecurityInfo();
    this._findOpenResponse();
    // We need to track the offset for the onDataAvailable calls where
    // we pass the data from our pipe to the converter.
    this.offset = 0;

    let channel = this.request;

    // Bug 1372115 - We should load bytecode cached requests from cache as the actual
    // channel content is going to be optimized data that reflects platform internals
    // instead of the content user expects (i.e. content served by HTTP server)
    // Note that bytecode cached is one example, there may be wasm or other usecase in
    // future.
    let isOptimizedContent = false;
    try {
      if (channel instanceof Ci.nsICacheInfoChannel) {
        isOptimizedContent = channel.alternativeDataType;
      }
    } catch (e) {
      // Accessing `alternativeDataType` for some SW requests throws.
    }
    if (isOptimizedContent) {
      let charset = this.request.contentCharset || this.httpActivity.charset;
      NetworkHelper.loadFromCache(this.httpActivity.url, charset,
                                  this._onComplete.bind(this));
      return;
    }

    // In the multi-process mode, the conversion happens on the child
    // side while we can only monitor the channel on the parent
    // side. If the content is gzipped, we have to unzip it
    // ourself. For that we use the stream converter services.  Do not
    // do that for Service workers as they are run in the child
    // process.
    if (!this.httpActivity.fromServiceWorker &&
        channel instanceof Ci.nsIEncodedChannel &&
        channel.contentEncodings &&
        !channel.applyConversion) {
      let encodingHeader = channel.getResponseHeader("Content-Encoding");
      let scs = Cc["@mozilla.org/streamConverters;1"]
        .getService(Ci.nsIStreamConverterService);
      let encodings = encodingHeader.split(/\s*\t*,\s*\t*/);
      let nextListener = this;
      let acceptedEncodings = ["gzip", "deflate", "br", "x-gzip", "x-deflate"];
      for (let i in encodings) {
        // There can be multiple conversions applied
        let enc = encodings[i].toLowerCase();
        if (acceptedEncodings.indexOf(enc) > -1) {
          this.converter = scs.asyncConvertData(enc, "uncompressed",
                                                nextListener, null);
          nextListener = this.converter;
        }
      }
      if (this.converter) {
        this.converter.onStartRequest(this.request, null);
      }
    }
    // Asynchronously wait for the data coming from the request.
    this.setAsyncListener(this.sink.inputStream, this);
  },

  /**
   * Parse security state of this request and report it to the client.
   */
  _getSecurityInfo: DevToolsUtils.makeInfallible(function () {
    // Many properties of the securityInfo (e.g., the server certificate or HPKP
    // status) are not available in the content process and can't be even touched safely,
    // because their C++ getters trigger assertions. This function is called in content
    // process for synthesized responses from service workers, in the parent otherwise.
    if (Services.appinfo.processType == Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT) {
      return;
    }

    // Take the security information from the original nsIHTTPChannel instead of
    // the nsIRequest received in onStartRequest. If response to this request
    // was a redirect from http to https, the request object seems to contain
    // security info for the https request after redirect.
    let secinfo = this.httpActivity.channel.securityInfo;
    let info = NetworkHelper.parseSecurityInfo(secinfo, this.httpActivity);

    this.httpActivity.owner.addSecurityInfo(info);
  }),

  /**
   * Handle the onStopRequest by closing the sink output stream.
   *
   * For more documentation about nsIRequestObserver go to:
   * https://developer.mozilla.org/En/NsIRequestObserver
   */
  onStopRequest: function () {
    // Bug 1429365: onStopRequest may be called after onComplete for resources loaded
    // from bytecode cache.
    if (!this.httpActivity) {
      return;
    }
    this._findOpenResponse();
    this.sink.outputStream.close();
  },

  // nsIProgressEventSink implementation

  /**
   * Handle progress event as data is transferred.  This is used to record the
   * size on the wire, which may be compressed / encoded.
   */
  onProgress: function (request, context, progress, progressMax) {
    this.transferredSize = progress;
    // Need to forward as well to keep things like Download Manager's progress
    // bar working properly.
    this._forwardNotification(Ci.nsIProgressEventSink, "onProgress", arguments);
  },

  onStatus: function () {
    this._forwardNotification(Ci.nsIProgressEventSink, "onStatus", arguments);
  },

  /**
   * Find the open response object associated to the current request. The
   * NetworkMonitor._httpResponseExaminer() method saves the response headers in
   * NetworkMonitor.openResponses. This method takes the data from the open
   * response object and puts it into the HTTP activity object, then sends it to
   * the remote Web Console instance.
   *
   * @private
   */
  _findOpenResponse: function () {
    if (!this.owner || this._foundOpenResponse) {
      return;
    }

    let channel = this.httpActivity.channel;
    let openResponse = this.owner.openResponses.get(channel);
    if (!openResponse) {
      return;
    }
    this._foundOpenResponse = true;

    this.owner.openResponses.delete(channel);

    this.httpActivity.owner.addResponseHeaders(openResponse.headers);
    this.httpActivity.owner.addResponseCookies(openResponse.cookies);
  },

  /**
   * Clean up the response listener once the response input stream is closed.
   * This is called from onStopRequest() or from onInputStreamReady() when the
   * stream is closed.
   * @return void
   */
  onStreamClose: function () {
    if (!this.httpActivity) {
      return;
    }
    // Remove our listener from the request input stream.
    this.setAsyncListener(this.sink.inputStream, null);

    this._findOpenResponse();

    if (!this.httpActivity.discardResponseBody && this.receivedData.length) {
      this._onComplete(this.receivedData);
    } else if (!this.httpActivity.discardResponseBody &&
               this.httpActivity.responseStatus == 304) {
      // Response is cached, so we load it from cache.
      let charset = this.request.contentCharset || this.httpActivity.charset;
      NetworkHelper.loadFromCache(this.httpActivity.url, charset,
                                  this._onComplete.bind(this));
    } else {
      this._onComplete();
    }
  },

  /**
   * Handler for when the response completes. This function cleans up the
   * response listener.
   *
   * @param string [data]
   *        Optional, the received data coming from the response listener or
   *        from the cache.
   */
  _onComplete: function (data) {
    let response = {
      mimeType: "",
      text: data || "",
    };

    response.size = this.bodySize;
    response.transferredSize = this.transferredSize + this.httpActivity.headersSize;

    try {
      response.mimeType = this.request.contentType;
    } catch (ex) {
      // Ignore.
    }

    if (!response.mimeType ||
        !NetworkHelper.isTextMimeType(response.mimeType)) {
      response.encoding = "base64";
      try {
        response.text = btoa(response.text);
      } catch (err) {
        // Ignore.
      }
    }

    if (response.mimeType && this.request.contentCharset) {
      response.mimeType += "; charset=" + this.request.contentCharset;
    }

    this.receivedData = "";

    this.httpActivity.owner.addResponseContent(
      response,
      {
        discardResponseBody: this.httpActivity.discardResponseBody,
        truncated: this.truncated
      }
    );

    this._wrappedNotificationCallbacks = null;
    this.httpActivity = null;
    this.sink = null;
    this.inputStream = null;
    this.converter = null;
    this.request = null;
    this.owner = null;
  },

  /**
   * The nsIInputStreamCallback for when the request input stream is ready -
   * either it has more data or it is closed.
   *
   * @param nsIAsyncInputStream stream
   *        The sink input stream from which data is coming.
   * @returns void
   */
  onInputStreamReady: function (stream) {
    if (!(stream instanceof Ci.nsIAsyncInputStream) || !this.httpActivity) {
      return;
    }

    let available = -1;
    try {
      // This may throw if the stream is closed normally or due to an error.
      available = stream.available();
    } catch (ex) {
      // Ignore.
    }

    if (available != -1) {
      if (available != 0) {
        if (this.converter) {
          this.converter.onDataAvailable(this.request, null, stream,
                                         this.offset, available);
        } else {
          this.onDataAvailable(this.request, null, stream, this.offset,
                               available);
        }
      }
      this.offset += available;
      this.setAsyncListener(stream, this);
    } else {
      this.onStreamClose();
      this.offset = 0;
    }
  },
};

/**
 * The network monitor uses the nsIHttpActivityDistributor to monitor network
 * requests. The nsIObserverService is also used for monitoring
 * http-on-examine-response notifications. All network request information is
 * routed to the remote Web Console.
 *
 * @constructor
 * @param object filters
 *        Object with the filters to use for network requests:
 *        - window (nsIDOMWindow): filter network requests by the associated
 *          window object.
 *        - appId (number): filter requests by the appId.
 *        - outerWindowID (number): filter requests by their top frame's outerWindowID.
 *        Filters are optional. If any of these filters match the request is
 *        logged (OR is applied). If no filter is provided then all requests are
 *        logged.
 * @param object owner
 *        The network monitor owner. This object needs to hold:
 *        - onNetworkEvent(requestInfo)
 *          This method is invoked once for every new network request and it is
 *          given the initial network request information as an argument.
 *          onNetworkEvent() must return an object which holds several add*()
 *          methods which are used to add further network request/response information.
 *        - stackTraceCollector
 *          If the owner has this optional property, it will be used as a
 *          StackTraceCollector by the NetworkMonitor.
 */
function NetworkMonitor(filters, owner) {
  this.filters = filters;
  this.owner = owner;
  this.openRequests = new Map();
  this.openResponses = new Map();
  this._httpResponseExaminer =
    DevToolsUtils.makeInfallible(this._httpResponseExaminer).bind(this);
  this._httpModifyExaminer =
    DevToolsUtils.makeInfallible(this._httpModifyExaminer).bind(this);
  this._serviceWorkerRequest = this._serviceWorkerRequest.bind(this);
  this._throttleData = null;
  this._throttler = null;
}

exports.NetworkMonitor = NetworkMonitor;

NetworkMonitor.prototype = {
  filters: null,

  httpTransactionCodes: {
    0x5001: "REQUEST_HEADER",
    0x5002: "REQUEST_BODY_SENT",
    0x5003: "RESPONSE_START",
    0x5004: "RESPONSE_HEADER",
    0x5005: "RESPONSE_COMPLETE",
    0x5006: "TRANSACTION_CLOSE",

    0x804b0003: "STATUS_RESOLVING",
    0x804b000b: "STATUS_RESOLVED",
    0x804b0007: "STATUS_CONNECTING_TO",
    0x804b0004: "STATUS_CONNECTED_TO",
    0x804b0005: "STATUS_SENDING_TO",
    0x804b000a: "STATUS_WAITING_FOR",
    0x804b0006: "STATUS_RECEIVING_FROM",
    0x804b000c: "STATUS_TLS_STARTING",
    0x804b000d: "STATUS_TLS_ENDING"
  },

  httpDownloadActivities: [
    gActivityDistributor.ACTIVITY_SUBTYPE_RESPONSE_START,
    gActivityDistributor.ACTIVITY_SUBTYPE_RESPONSE_HEADER,
    gActivityDistributor.ACTIVITY_SUBTYPE_RESPONSE_COMPLETE,
    gActivityDistributor.ACTIVITY_SUBTYPE_TRANSACTION_CLOSE
  ],

  // Network response bodies are piped through a buffer of the given size (in
  // bytes).
  responsePipeSegmentSize: null,

  owner: null,

  /**
   * Whether to save the bodies of network requests and responses.
   * @type boolean
   */
  saveRequestAndResponseBodies: true,

  /**
   * Object that holds the HTTP activity objects for ongoing requests.
   */
  openRequests: null,

  /**
   * Object that holds response headers coming from this._httpResponseExaminer.
   */
  openResponses: null,

  /**
   * The network monitor initializer.
   */
  init: function () {
    this.responsePipeSegmentSize = Services.prefs
                                   .getIntPref("network.buffer.cache.size");
    this.interceptedChannels = new Set();

    if (Services.appinfo.processType != Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT) {
      gActivityDistributor.addObserver(this);
      Services.obs.addObserver(this._httpResponseExaminer,
                               "http-on-examine-response");
      Services.obs.addObserver(this._httpResponseExaminer,
                               "http-on-examine-cached-response");
      Services.obs.addObserver(this._httpModifyExaminer,
                               "http-on-modify-request");
    }
    // In child processes, only watch for service worker requests
    // everything else only happens in the parent process
    Services.obs.addObserver(this._serviceWorkerRequest,
                             "service-worker-synthesized-response");
  },

  get throttleData() {
    return this._throttleData;
  },

  set throttleData(value) {
    this._throttleData = value;
    // Clear out any existing throttlers
    this._throttler = null;
  },

  _getThrottler: function () {
    if (this.throttleData !== null && this._throttler === null) {
      this._throttler = new NetworkThrottleManager(this.throttleData);
    }
    return this._throttler;
  },

  _serviceWorkerRequest: function (subject, topic, data) {
    let channel = subject.QueryInterface(Ci.nsIHttpChannel);

    if (!matchRequest(channel, this.filters)) {
      return;
    }

    this.interceptedChannels.add(subject);

    // Service workers never fire http-on-examine-cached-response, so fake one.
    this._httpResponseExaminer(channel, "http-on-examine-cached-response");
  },

  /**
   * Observe notifications for the http-on-examine-response topic, coming from
   * the nsIObserverService.
   *
   * @private
   * @param nsIHttpChannel subject
   * @param string topic
   * @returns void
   */
  _httpResponseExaminer: function (subject, topic) {
    // The httpResponseExaminer is used to retrieve the uncached response
    // headers. The data retrieved is stored in openResponses. The
    // NetworkResponseListener is responsible with updating the httpActivity
    // object with the data from the new object in openResponses.

    if (!this.owner ||
        (topic != "http-on-examine-response" &&
         topic != "http-on-examine-cached-response") ||
        !(subject instanceof Ci.nsIHttpChannel)) {
      return;
    }

    let channel = subject.QueryInterface(Ci.nsIHttpChannel);

    if (!matchRequest(channel, this.filters)) {
      return;
    }

    let response = {
      id: gSequenceId(),
      channel: channel,
      headers: [],
      cookies: [],
    };

    let setCookieHeaders = [];

    channel.visitOriginalResponseHeaders({
      visitHeader: function (name, value) {
        let lowerName = name.toLowerCase();
        if (lowerName == "set-cookie") {
          setCookieHeaders.push(value);
        }
        response.headers.push({ name: name, value: value });
      }
    });

    if (!response.headers.length) {
      // No need to continue.
      return;
    }

    if (setCookieHeaders.length) {
      response.cookies = setCookieHeaders.reduce((result, header) => {
        let cookies = NetworkHelper.parseSetCookieHeader(header);
        return result.concat(cookies);
      }, []);
    }

    // Determine the HTTP version.
    let httpVersionMaj = {};
    let httpVersionMin = {};

    channel.QueryInterface(Ci.nsIHttpChannelInternal);
    channel.getResponseVersion(httpVersionMaj, httpVersionMin);

    response.status = channel.responseStatus;
    response.statusText = channel.responseStatusText;
    response.httpVersion = "HTTP/" + httpVersionMaj.value + "." +
                                     httpVersionMin.value;

    this.openResponses.set(channel, response);

    if (topic === "http-on-examine-cached-response") {
      // Service worker requests emits cached-reponse notification on non-e10s,
      // and we fake one on e10s.
      let fromServiceWorker = this.interceptedChannels.has(channel);
      this.interceptedChannels.delete(channel);

      // If this is a cached response, there never was a request event
      // so we need to construct one here so the frontend gets all the
      // expected events.
      let httpActivity = this._createNetworkEvent(channel, {
        fromCache: !fromServiceWorker,
        fromServiceWorker: fromServiceWorker
      });
      httpActivity.owner.addResponseStart({
        httpVersion: response.httpVersion,
        remoteAddress: "",
        remotePort: "",
        status: response.status,
        statusText: response.statusText,
        headersSize: 0,
      }, "", true);

      // There also is never any timing events, so we can fire this
      // event with zeroed out values.
      let timings = this._setupHarTimings(httpActivity, true);
      httpActivity.owner.addEventTimings(timings.total, timings.timings,
                                         timings.offsets);
    }
  },

  /**
   * Observe notifications for the http-on-modify-request topic, coming from
   * the nsIObserverService.
   *
   * @private
   * @param nsIHttpChannel aSubject
   * @returns void
   */
  _httpModifyExaminer: function (subject) {
    let throttler = this._getThrottler();
    if (throttler) {
      let channel = subject.QueryInterface(Ci.nsIHttpChannel);
      if (matchRequest(channel, this.filters)) {
        // Read any request body here, before it is throttled.
        let httpActivity = this.createOrGetActivityObject(channel);
        this._onRequestBodySent(httpActivity);
        throttler.manageUpload(channel);
      }
    }
  },

  /**
   * A helper function for observeActivity.  This does whatever work
   * is required by a particular http activity event.  Arguments are
   * the same as for observeActivity.
   */
  _dispatchActivity: function (httpActivity, channel, activityType,
                               activitySubtype, timestamp, extraSizeData,
                               extraStringData) {
    let transCodes = this.httpTransactionCodes;

    // Store the time information for this activity subtype.
    if (activitySubtype in transCodes) {
      let stage = transCodes[activitySubtype];
      if (stage in httpActivity.timings) {
        httpActivity.timings[stage].last = timestamp;
      } else {
        httpActivity.timings[stage] = {
          first: timestamp,
          last: timestamp,
        };
      }
    }

    switch (activitySubtype) {
      case gActivityDistributor.ACTIVITY_SUBTYPE_REQUEST_BODY_SENT:
        this._onRequestBodySent(httpActivity);
        if (httpActivity.sentBody !== null) {
          httpActivity.owner.addRequestPostData({ text: httpActivity.sentBody });
          httpActivity.sentBody = null;
        }
        break;
      case gActivityDistributor.ACTIVITY_SUBTYPE_RESPONSE_HEADER:
        this._onResponseHeader(httpActivity, extraStringData);
        break;
      case gActivityDistributor.ACTIVITY_SUBTYPE_TRANSACTION_CLOSE:
        this._onTransactionClose(httpActivity);
        break;
      default:
        break;
    }
  },

  /**
   * Begin observing HTTP traffic that originates inside the current tab.
   *
   * @see https://developer.mozilla.org/en/XPCOM_Interface_Reference/nsIHttpActivityObserver
   *
   * @param nsIHttpChannel channel
   * @param number activityType
   * @param number activitySubtype
   * @param number timestamp
   * @param number extraSizeData
   * @param string extraStringData
   */
  observeActivity:
  DevToolsUtils.makeInfallible(function (channel, activityType, activitySubtype,
                                        timestamp, extraSizeData,
                                        extraStringData) {
    if (!this.owner ||
        activityType != gActivityDistributor.ACTIVITY_TYPE_HTTP_TRANSACTION &&
        activityType != gActivityDistributor.ACTIVITY_TYPE_SOCKET_TRANSPORT) {
      return;
    }

    if (!(channel instanceof Ci.nsIHttpChannel)) {
      return;
    }

    channel = channel.QueryInterface(Ci.nsIHttpChannel);

    if (activitySubtype ==
        gActivityDistributor.ACTIVITY_SUBTYPE_REQUEST_HEADER) {
      this._onRequestHeader(channel, timestamp, extraStringData);
      return;
    }

    // Iterate over all currently ongoing requests. If channel can't
    // be found within them, then exit this function.
    let httpActivity = this._findActivityObject(channel);
    if (!httpActivity) {
      return;
    }

    // If we're throttling, we must not report events as they arrive
    // from platform, but instead let the throttler emit the events
    // after some time has elapsed.
    if (httpActivity.downloadThrottle &&
        this.httpDownloadActivities.includes(activitySubtype)) {
      let callback = this._dispatchActivity.bind(this);
      httpActivity.downloadThrottle
        .addActivityCallback(callback, httpActivity, channel, activityType,
                             activitySubtype, timestamp, extraSizeData,
                             extraStringData);
    } else {
      this._dispatchActivity(httpActivity, channel, activityType,
                             activitySubtype, timestamp, extraSizeData,
                             extraStringData);
    }
  }),

  /**
   *
   */
  _createNetworkEvent: function (channel, { timestamp, extraStringData,
                                           fromCache, fromServiceWorker }) {
    let httpActivity = this.createOrGetActivityObject(channel);

    channel.QueryInterface(Ci.nsIPrivateBrowsingChannel);
    httpActivity.private = channel.isChannelPrivate;

    if (timestamp) {
      httpActivity.timings.REQUEST_HEADER = {
        first: timestamp,
        last: timestamp
      };
    }

    let event = {};
    event.method = channel.requestMethod;
    event.channelId = channel.channelId;
    event.url = channel.URI.spec;
    event.private = httpActivity.private;
    event.headersSize = 0;
    event.startedDateTime =
      (timestamp ? new Date(Math.round(timestamp / 1000)) : new Date())
      .toISOString();
    event.fromCache = fromCache;
    event.fromServiceWorker = fromServiceWorker;
    httpActivity.fromServiceWorker = fromServiceWorker;

    if (extraStringData) {
      event.headersSize = extraStringData.length;
    }

    // Determine the cause and if this is an XHR request.
    let causeType = Ci.nsIContentPolicy.TYPE_OTHER;
    let causeUri = null;
    let stacktrace;

    if (channel.loadInfo) {
      causeType = channel.loadInfo.externalContentPolicyType;
      const { loadingPrincipal } = channel.loadInfo;
      if (loadingPrincipal && loadingPrincipal.URI) {
        causeUri = loadingPrincipal.URI.spec;
      }
    }

    // If this is the parent process, there is no stackTraceCollector - the stack
    // trace will be added in NetworkMonitorChild._onNewEvent.
    if (this.owner.stackTraceCollector) {
      stacktrace = this.owner.stackTraceCollector.getStackTrace(event.channelId);
    }

    event.cause = {
      type: causeTypeToString(causeType),
      loadingDocumentUri: causeUri,
      stacktrace
    };

    httpActivity.isXHR = event.isXHR =
        (causeType === Ci.nsIContentPolicy.TYPE_XMLHTTPREQUEST ||
         causeType === Ci.nsIContentPolicy.TYPE_FETCH);

    // Determine the HTTP version.
    let httpVersionMaj = {};
    let httpVersionMin = {};
    channel.QueryInterface(Ci.nsIHttpChannelInternal);
    channel.getRequestVersion(httpVersionMaj, httpVersionMin);

    event.httpVersion = "HTTP/" + httpVersionMaj.value + "." +
                                  httpVersionMin.value;

    event.discardRequestBody = !this.saveRequestAndResponseBodies;
    event.discardResponseBody = !this.saveRequestAndResponseBodies;

    let headers = [];
    let cookies = [];
    let cookieHeader = null;

    // Copy the request header data.
    channel.visitRequestHeaders({
      visitHeader: function (name, value) {
        if (name == "Cookie") {
          cookieHeader = value;
        }
        headers.push({ name: name, value: value });
      }
    });

    if (cookieHeader) {
      cookies = NetworkHelper.parseCookieHeader(cookieHeader);
    }

    httpActivity.owner = this.owner.onNetworkEvent(event);

    this._setupResponseListener(httpActivity, fromCache);

    httpActivity.owner.addRequestHeaders(headers, extraStringData);
    httpActivity.owner.addRequestCookies(cookies);

    return httpActivity;
  },

  /**
   * Handler for ACTIVITY_SUBTYPE_REQUEST_HEADER. When a request starts the
   * headers are sent to the server. This method creates the |httpActivity|
   * object where we store the request and response information that is
   * collected through its lifetime.
   *
   * @private
   * @param nsIHttpChannel channel
   * @param number timestamp
   * @param string extraStringData
   * @return void
   */
  _onRequestHeader: function (channel, timestamp, extraStringData) {
    if (!matchRequest(channel, this.filters)) {
      return;
    }

    this._createNetworkEvent(channel, { timestamp, extraStringData });
  },

  /**
   * Find an HTTP activity object for the channel.
   *
   * @param nsIHttpChannel channel
   *        The HTTP channel whose activity object we want to find.
   * @return object
   *        The HTTP activity object, or null if it is not found.
   */
  _findActivityObject: function (channel) {
    return this.openRequests.get(channel) || null;
  },

  /**
   * Find an existing HTTP activity object, or create a new one. This
   * object is used for storing all the request and response
   * information.
   *
   * This is a HAR-like object. Conformance to the spec is not guaranteed at
   * this point.
   *
   * @see http://www.softwareishard.com/blog/har-12-spec
   * @param nsIHttpChannel channel
   *        The HTTP channel for which the HTTP activity object is created.
   * @return object
   *         The new HTTP activity object.
   */
  createOrGetActivityObject: function (channel) {
    let httpActivity = this._findActivityObject(channel);
    if (!httpActivity) {
      let win = NetworkHelper.getWindowForRequest(channel);
      let charset = win ? win.document.characterSet : null;

      httpActivity = {
        id: gSequenceId(),
        channel: channel,
        // see _onRequestBodySent()
        charset: charset,
        sentBody: null,
        url: channel.URI.spec,
        // needed for host specific security info
        hostname: channel.URI.host,
        discardRequestBody: !this.saveRequestAndResponseBodies,
        discardResponseBody: !this.saveRequestAndResponseBodies,
        // internal timing information, see observeActivity()
        timings: {},
        // see _onResponseHeader()
        responseStatus: null,
        // the activity owner which is notified when changes happen
        owner: null,
      };

      this.openRequests.set(channel, httpActivity);
    }

    return httpActivity;
  },

  /**
   * Setup the network response listener for the given HTTP activity. The
   * NetworkResponseListener is responsible for storing the response body.
   *
   * @private
   * @param object httpActivity
   *        The HTTP activity object we are tracking.
   */
  _setupResponseListener: function (httpActivity, fromCache) {
    let channel = httpActivity.channel;
    channel.QueryInterface(Ci.nsITraceableChannel);

    if (!fromCache) {
      let throttler = this._getThrottler();
      if (throttler) {
        httpActivity.downloadThrottle = throttler.manage(channel);
      }
    }

    // The response will be written into the outputStream of this pipe.
    // This allows us to buffer the data we are receiving and read it
    // asynchronously.
    // Both ends of the pipe must be blocking.
    let sink = Cc["@mozilla.org/pipe;1"].createInstance(Ci.nsIPipe);

    // The streams need to be blocking because this is required by the
    // stream tee.
    sink.init(false, false, this.responsePipeSegmentSize, PR_UINT32_MAX, null);

    // Add listener for the response body.
    let newListener = new NetworkResponseListener(this, httpActivity);

    // Remember the input stream, so it isn't released by GC.
    newListener.inputStream = sink.inputStream;
    newListener.sink = sink;

    let tee = Cc["@mozilla.org/network/stream-listener-tee;1"]
              .createInstance(Ci.nsIStreamListenerTee);

    let originalListener = channel.setNewListener(tee);

    tee.init(originalListener, sink.outputStream, newListener);
  },

  /**
   * Handler for ACTIVITY_SUBTYPE_REQUEST_BODY_SENT. The request body is logged
   * here.
   *
   * @private
   * @param object httpActivity
   *        The HTTP activity object we are working with.
   */
  _onRequestBodySent: function (httpActivity) {
    // Return early if we don't need the request body, or if we've
    // already found it.
    if (httpActivity.discardRequestBody || httpActivity.sentBody !== null) {
      return;
    }

    let sentBody = NetworkHelper.readPostTextFromRequest(httpActivity.channel,
                                                         httpActivity.charset);

    if (sentBody !== null && this.window &&
        httpActivity.url == this.window.location.href) {
      // If the request URL is the same as the current page URL, then
      // we can try to get the posted text from the page directly.
      // This check is necessary as otherwise the
      //   NetworkHelper.readPostTextFromPageViaWebNav()
      // function is called for image requests as well but these
      // are not web pages and as such don't store the posted text
      // in the cache of the webpage.
      let webNav = this.window.QueryInterface(Ci.nsIInterfaceRequestor)
                   .getInterface(Ci.nsIWebNavigation);
      sentBody = NetworkHelper
                 .readPostTextFromPageViaWebNav(webNav, httpActivity.charset);
    }

    if (sentBody !== null) {
      httpActivity.sentBody = sentBody;
    }
  },

  /**
   * Handler for ACTIVITY_SUBTYPE_RESPONSE_HEADER. This method stores
   * information about the response headers.
   *
   * @private
   * @param object httpActivity
   *        The HTTP activity object we are working with.
   * @param string extraStringData
   *        The uncached response headers.
   */
  _onResponseHeader: function (httpActivity, extraStringData) {
    // extraStringData contains the uncached response headers. The first line
    // contains the response status (e.g. HTTP/1.1 200 OK).
    //
    // Note: The response header is not saved here. Calling the
    // channel.visitResponseHeaders() method at this point sometimes causes an
    // NS_ERROR_NOT_AVAILABLE exception.
    //
    // We could parse extraStringData to get the headers and their values, but
    // that is not trivial to do in an accurate manner. Hence, we save the
    // response headers in this._httpResponseExaminer().

    let headers = extraStringData.split(/\r\n|\n|\r/);
    let statusLine = headers.shift();
    let statusLineArray = statusLine.split(" ");

    let response = {};
    response.httpVersion = statusLineArray.shift();
    response.remoteAddress = httpActivity.channel.remoteAddress;
    response.remotePort = httpActivity.channel.remotePort;
    response.status = statusLineArray.shift();
    response.statusText = statusLineArray.join(" ");
    response.headersSize = extraStringData.length;

    httpActivity.responseStatus = response.status;
    httpActivity.headersSize = response.headersSize;

    // Discard the response body for known response statuses.
    switch (parseInt(response.status, 10)) {
      case HTTP_MOVED_PERMANENTLY:
      case HTTP_FOUND:
      case HTTP_SEE_OTHER:
      case HTTP_TEMPORARY_REDIRECT:
        httpActivity.discardResponseBody = true;
        break;
    }

    response.discardResponseBody = httpActivity.discardResponseBody;

    httpActivity.owner.addResponseStart(response, extraStringData);
  },

  /**
   * Handler for ACTIVITY_SUBTYPE_TRANSACTION_CLOSE. This method updates the HAR
   * timing information on the HTTP activity object and clears the request
   * from the list of known open requests.
   *
   * @private
   * @param object httpActivity
   *        The HTTP activity object we work with.
   */
  _onTransactionClose: function (httpActivity) {
    let result = this._setupHarTimings(httpActivity);
    httpActivity.owner.addEventTimings(result.total, result.timings,
                                       result.offsets);
    this.openRequests.delete(httpActivity.channel);
  },

  /**
   * Update the HTTP activity object to include timing information as in the HAR
   * spec. The HTTP activity object holds the raw timing information in
   * |timings| - these are timings stored for each activity notification. The
   * HAR timing information is constructed based on these lower level
   * data.
   *
   * @param object httpActivity
   *        The HTTP activity object we are working with.
   * @param boolean fromCache
   *        Indicates that the result was returned from the browser cache
   * @return object
   *         This object holds two properties:
   *         - total - the total time for all of the request and response.
   *         - timings - the HAR timings object.
   */
  _setupHarTimings: function (httpActivity, fromCache) {
    if (fromCache) {
      // If it came from the browser cache, we have no timing
      // information and these should all be 0
      return {
        total: 0,
        timings: {
          blocked: 0,
          dns: 0,
          ssl: 0,
          connect: 0,
          send: 0,
          wait: 0,
          receive: 0
        },
        offsets: {
          blocked: 0,
          dns: 0,
          ssl: 0,
          connect: 0,
          send: 0,
          wait: 0,
          receive: 0
        }
      };
    }

    let timings = httpActivity.timings;
    let harTimings = {};
    // If the TCP Fast Open option or tls1.3 0RTT is used tls and data can
    // be dispatched in SYN packet and not after tcp socket is connected.
    // To demostrate this properly we will calculated TLS and send start time
    // relative to CONNECTING_TO.
    // Similary if 0RTT is used, data can be sent as soon as a TLS handshake
    // starts.
    let secureConnectionStartTime = 0;
    let secureConnectionStartTimeRelative = false;
    let startSendingTime = 0;
    let startSendingTimeRelative = false;

    if (timings.STATUS_RESOLVING && timings.STATUS_CONNECTING_TO) {
      harTimings.blocked = timings.STATUS_RESOLVING.first -
                           timings.REQUEST_HEADER.first;
    } else if (timings.STATUS_SENDING_TO) {
      harTimings.blocked = timings.STATUS_SENDING_TO.first -
                           timings.REQUEST_HEADER.first;
    } else {
      harTimings.blocked = -1;
    }

    // DNS timing information is available only in when the DNS record is not
    // cached.
    harTimings.dns = timings.STATUS_RESOLVING && timings.STATUS_RESOLVED ?
                     timings.STATUS_RESOLVED.last -
                     timings.STATUS_RESOLVING.first : -1;

    if (timings.STATUS_CONNECTING_TO && timings.STATUS_CONNECTED_TO) {
      harTimings.connect = timings.STATUS_CONNECTED_TO.last -
                           timings.STATUS_CONNECTING_TO.first;
    } else {
      harTimings.connect = -1;
    }

    if (timings.STATUS_TLS_STARTING && timings.STATUS_TLS_ENDING) {
      harTimings.ssl = timings.STATUS_TLS_ENDING.last -
                           timings.STATUS_TLS_STARTING.first;
      if (timings.STATUS_CONNECTING_TO) {
        secureConnectionStartTime =
          timings.STATUS_TLS_STARTING.first - timings.STATUS_CONNECTING_TO.first;
      }
      if (secureConnectionStartTime < 0) {
        secureConnectionStartTime = 0;
      }
      secureConnectionStartTimeRelative = true;
    } else {
      harTimings.ssl = -1;
    }

    // sometimes the connection information events are attached to a speculative
    // channel instead of this one, but necko might glue them back together in the
    // nsITimedChannel interface used by Resource and Navigation Timing
    let timedChannel = httpActivity.channel.QueryInterface(Ci.nsITimedChannel);

    let tcTcpConnectEndTime = 0;
    let tcConnectStartTime = 0;
    let tcConnectEndTime = 0;
    let tcSecureConnectionStartTime = 0;
    let tcDomainLookupEndTime = 0;
    let tcDomainLookupStartTime = 0;

    if (timedChannel) {
      tcTcpConnectEndTime = timedChannel.tcpConnectEndTime;
      tcConnectStartTime = timedChannel.connectStartTime;
      tcConnectEndTime = timedChannel.connectEndTime;
      tcSecureConnectionStartTime = timedChannel.secureConnectionStartTime;
      tcDomainLookupEndTime = timedChannel.domainLookupEndTime;
      tcDomainLookupStartTime = timedChannel.domainLookupStartTime;
    }

    // Make sure the above values are at least timedChannel.asyncOpenTime.
    if (timedChannel && timedChannel.asyncOpenTime) {
      if ((tcTcpConnectEndTime != 0) &&
          (tcTcpConnectEndTime < timedChannel.asyncOpenTime)) {
        tcTcpConnectEndTime = 0;
      }
      if ((tcConnectStartTime != 0) &&
          (tcConnectStartTime < timedChannel.asyncOpenTime)) {
        tcConnectStartTime = 0;
      }
      if ((tcConnectEndTime != 0) &&
          (tcConnectEndTime < timedChannel.asyncOpenTime)) {
        tcConnectEndTime = 0;
      }
      if ((tcSecureConnectionStartTime != 0) &&
          (tcSecureConnectionStartTime < timedChannel.asyncOpenTime)) {
        tcSecureConnectionStartTime = 0;
      }
      if ((tcDomainLookupEndTime != 0) &&
          (tcDomainLookupEndTime < timedChannel.asyncOpenTime)) {
        tcDomainLookupEndTime = 0;
      }
      if ((tcDomainLookupStartTime != 0) &&
          (tcDomainLookupStartTime < timedChannel.asyncOpenTime)) {
        tcDomainLookupStartTime = 0;
      }
    }

    if ((harTimings.connect <= 0) && timedChannel &&
        (tcTcpConnectEndTime != 0) && (tcConnectStartTime != 0)) {
      harTimings.connect = tcTcpConnectEndTime - tcConnectStartTime;
      if (tcSecureConnectionStartTime != 0) {
        harTimings.ssl = tcConnectEndTime - tcSecureConnectionStartTime;
        secureConnectionStartTime =
          tcSecureConnectionStartTime - tcConnectStartTime;
        secureConnectionStartTimeRelative = true;
      } else {
        harTimings.ssl = -1;
      }
    } else if (timedChannel && timings.STATUS_TLS_STARTING &&
               (tcSecureConnectionStartTime != 0)) {
      // It can happen that TCP Fast Open actually have not sent any data and
      // timings.STATUS_TLS_STARTING.first value will be corrected in
      // timedChannel.secureConnectionStartTime
      if (tcSecureConnectionStartTime > timings.STATUS_TLS_STARTING.first) {
        // TCP Fast Open actually did not sent any data.
        harTimings.ssl =
          tcConnectEndTime - tcSecureConnectionStartTime;
        secureConnectionStartTimeRelative = false;
      }
    }

    if ((harTimings.dns <= 0) && timedChannel &&
        (tcDomainLookupEndTime != 0) && (tcDomainLookupStartTime != 0)) {
      harTimings.dns = tcDomainLookupEndTime - tcDomainLookupStartTime;
    }

    if (timings.STATUS_SENDING_TO) {
      harTimings.send =
        timings.STATUS_SENDING_TO.last - timings.STATUS_SENDING_TO.first;
      if (timings.STATUS_CONNECTING_TO) {
        startSendingTime =
          timings.STATUS_SENDING_TO.first - timings.STATUS_CONNECTING_TO.first;
        startSendingTimeRelative = true;
      } else if (tcConnectStartTime != 0) {
        startSendingTime = timings.STATUS_SENDING_TO.first - tcConnectStartTime;
        startSendingTimeRelative = true;
      }
      if (startSendingTime < 0) {
        startSendingTime = 0;
      }
    } else if (timings.REQUEST_HEADER && timings.REQUEST_BODY_SENT) {
      harTimings.send = timings.REQUEST_BODY_SENT.last - timings.REQUEST_HEADER.first;
    } else {
      harTimings.send = -1;
    }

    if (timings.RESPONSE_START) {
      harTimings.wait = timings.RESPONSE_START.first -
                        (timings.REQUEST_BODY_SENT ||
                         timings.STATUS_SENDING_TO).last;
    } else {
      harTimings.wait = -1;
    }

    if (timings.RESPONSE_START && timings.RESPONSE_COMPLETE) {
      harTimings.receive = timings.RESPONSE_COMPLETE.last -
                           timings.RESPONSE_START.first;
    } else {
      harTimings.receive = -1;
    }

    if (secureConnectionStartTimeRelative) {
      let time = Math.max(Math.round(secureConnectionStartTime / 1000), -1);
      secureConnectionStartTime = time;
    }
    if (startSendingTimeRelative) {
      let time = Math.max(Math.round(startSendingTime / 1000), -1);
      startSendingTime = time;
    }

    let ot = this._calculateOffsetAndTotalTime(harTimings,
                                               secureConnectionStartTime,
                                               startSendingTimeRelative,
                                               secureConnectionStartTimeRelative,
                                               startSendingTime);
    return {
      total: ot.total,
      timings: harTimings,
      offsets: ot.offsets
    };
  },

  _calculateOffsetAndTotalTime: function (harTimings,
                                          secureConnectionStartTime,
                                          startSendingTimeRelative,
                                          secureConnectionStartTimeRelative,
                                          startSendingTime) {
    let totalTime = 0;
    for (let timing in harTimings) {
      let time = Math.max(Math.round(harTimings[timing] / 1000), -1);
      harTimings[timing] = time;
      if ((time > -1) && (timing != "connect") && (timing != "ssl")) {
        totalTime += time;
      }
    }

    // connect, ssl and send times can be overlapped.
    if (startSendingTimeRelative) {
      totalTime += startSendingTime;
    } else if (secureConnectionStartTimeRelative) {
      totalTime += secureConnectionStartTime;
      totalTime += harTimings.ssl;
    }

    let offsets = {};
    offsets.blocked = 0;
    offsets.dns = harTimings.blocked;
    offsets.connect = offsets.dns + harTimings.dns;
    if (secureConnectionStartTimeRelative) {
      offsets.ssl = offsets.connect + secureConnectionStartTime;
    } else {
      offsets.ssl = offsets.connect + harTimings.connect;
    }
    if (startSendingTimeRelative) {
      offsets.send = offsets.connect + startSendingTime;
      if (!secureConnectionStartTimeRelative) {
        offsets.ssl = offsets.send - harTimings.ssl;
      }
    } else {
      offsets.send = offsets.ssl + harTimings.ssl;
    }
    offsets.wait = offsets.send + harTimings.send;
    offsets.receive = offsets.wait + harTimings.wait;

    return {
      total: totalTime,
      offsets: offsets
    };
  },

  /**
   * Suspend Web Console activity. This is called when all Web Consoles are
   * closed.
   */
  destroy: function () {
    if (Services.appinfo.processType != Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT) {
      gActivityDistributor.removeObserver(this);
      Services.obs.removeObserver(this._httpResponseExaminer,
                                  "http-on-examine-response");
      Services.obs.removeObserver(this._httpResponseExaminer,
                                  "http-on-examine-cached-response");
      Services.obs.removeObserver(this._httpModifyExaminer,
                                  "http-on-modify-request");
    }

    Services.obs.removeObserver(this._serviceWorkerRequest,
                                "service-worker-synthesized-response");

    this.interceptedChannels.clear();
    this.openRequests.clear();
    this.openResponses.clear();
    this.owner = null;
    this.filters = null;
    this._throttler = null;
  },
};

/**
 * The NetworkMonitorChild is used to proxy all of the network activity of the
 * child app process from the main process. The child WebConsoleActor creates an
 * instance of this object.
 *
 * Network requests for apps happen in the main process. As such,
 * a NetworkMonitor instance is used by the WebappsActor in the main process to
 * log the network requests for this child process.
 *
 * The main process creates NetworkEventActorProxy instances per request. These
 * send the data to this object using the nsIMessageManager. Here we proxy the
 * data to the WebConsoleActor or to a NetworkEventActor.
 *
 * @constructor
 * @param number outerWindowID
 *        The outerWindowID of the TabActor's main window.
 * @param nsIMessageManager messageManager
 *        The nsIMessageManager to use to communicate with the parent process.
 * @param object DebuggerServerConnection
 *        The RDP connection to the client.
 * @param object owner
 *        The WebConsoleActor that is listening for the network requests.
 */
function NetworkMonitorChild(outerWindowID, messageManager, conn, owner) {
  this.outerWindowID = outerWindowID;
  this.conn = conn;
  this.owner = owner;
  this._messageManager = messageManager;
  this._onNewEvent = this._onNewEvent.bind(this);
  this._onUpdateEvent = this._onUpdateEvent.bind(this);
  this._netEvents = new Map();
  this._msgName = `debug:${this.conn.prefix}netmonitor`;
}

exports.NetworkMonitorChild = NetworkMonitorChild;

NetworkMonitorChild.prototype = {
  owner: null,
  _netEvents: null,
  _saveRequestAndResponseBodies: true,
  _throttleData: null,

  get saveRequestAndResponseBodies() {
    return this._saveRequestAndResponseBodies;
  },

  set saveRequestAndResponseBodies(val) {
    this._saveRequestAndResponseBodies = val;

    this._messageManager.sendAsyncMessage(this._msgName, {
      action: "setPreferences",
      preferences: {
        saveRequestAndResponseBodies: this._saveRequestAndResponseBodies,
      },
    });
  },

  get throttleData() {
    return this._throttleData;
  },

  set throttleData(val) {
    this._throttleData = val;

    this._messageManager.sendAsyncMessage(this._msgName, {
      action: "setPreferences",
      preferences: {
        throttleData: this._throttleData,
      },
    });
  },

  init: function () {
    this.conn.setupInParent({
      module: "devtools/shared/webconsole/network-monitor",
      setupParent: "setupParentProcess"
    });

    let mm = this._messageManager;
    mm.addMessageListener(`${this._msgName}:newEvent`, this._onNewEvent);
    mm.addMessageListener(`${this._msgName}:updateEvent`, this._onUpdateEvent);
    mm.sendAsyncMessage(this._msgName, {
      outerWindowID: this.outerWindowID,
      action: "start",
    });
  },

  _onNewEvent: DevToolsUtils.makeInfallible(function _onNewEvent(msg) {
    let {id, event} = msg.data;

    // Try to add stack trace to the event data received from parent
    if (this.owner.stackTraceCollector) {
      event.cause.stacktrace =
        this.owner.stackTraceCollector.getStackTrace(event.channelId);
    }

    let actor = this.owner.onNetworkEvent(event);
    this._netEvents.set(id, Cu.getWeakReference(actor));
  }),

  _onUpdateEvent: DevToolsUtils.makeInfallible(function _onUpdateEvent(msg) {
    let {id, method, args} = msg.data;
    let weakActor = this._netEvents.get(id);
    let actor = weakActor ? weakActor.get() : null;
    if (!actor) {
      console.error(`Received ${this._msgName}:updateEvent for unknown event ID: ${id}`);
      return;
    }
    if (!(method in actor)) {
      console.error(`Received ${this._msgName}:updateEvent unsupported ` +
                    `method: ${method}`);
      return;
    }
    actor[method].apply(actor, args);
  }),

  destroy: function () {
    let mm = this._messageManager;
    try {
      mm.removeMessageListener(`${this._msgName}:newEvent`, this._onNewEvent);
      mm.removeMessageListener(`${this._msgName}:updateEvent`, this._onUpdateEvent);
    } catch (e) {
      // On b2g, when registered to a new root docshell,
      // all message manager functions throw when trying to call them during
      // message-manager-disconnect event.
      // As there is no attribute/method on message manager to know
      // if they are still usable or not, we can only catch the exception...
    }
    this._netEvents.clear();
    this._messageManager = null;
    this.conn = null;
    this.owner = null;
  },
};

/**
 * The NetworkEventActorProxy is used to send network request information from
 * the main process to the child app process. One proxy is used per request.
 * Similarly, one NetworkEventActor in the child app process is used per
 * request. The client receives all network logs from the child actors.
 *
 * The child process has a NetworkMonitorChild instance that is listening for
 * all network logging from the main process. The net monitor shim is used to
 * proxy the data to the WebConsoleActor instance of the child process.
 *
 * @constructor
 * @param nsIMessageManager messageManager
 *        The message manager for the child app process. This is used for
 *        communication with the NetworkMonitorChild instance of the process.
 * @param string msgName
 *        The message name to be used for this connection.
 */
function NetworkEventActorProxy(messageManager, msgName) {
  this.id = gSequenceId();
  this.messageManager = messageManager;
  this._msgName = msgName;
}
exports.NetworkEventActorProxy = NetworkEventActorProxy;

NetworkEventActorProxy.methodFactory = function (method) {
  return DevToolsUtils.makeInfallible(function () {
    let args = Array.slice(arguments);
    let mm = this.messageManager;
    mm.sendAsyncMessage(`${this._msgName}:updateEvent`, {
      id: this.id,
      method: method,
      args: args,
    });
  }, "NetworkEventActorProxy." + method);
};

NetworkEventActorProxy.prototype = {
  /**
   * Initialize the network event. This method sends the network request event
   * to the content process.
   *
   * @param object event
   *        Object describing the network request.
   * @return object
   *         This object.
   */
  init: DevToolsUtils.makeInfallible(function (event) {
    let mm = this.messageManager;
    mm.sendAsyncMessage(`${this._msgName}:newEvent`, {
      id: this.id,
      event: event,
    });
    return this;
  }),
};

(function () {
  // Listeners for new network event data coming from the NetworkMonitor.
  let methods = ["addRequestHeaders", "addRequestCookies", "addRequestPostData",
                 "addResponseStart", "addSecurityInfo", "addResponseHeaders",
                 "addResponseCookies", "addResponseContent", "addEventTimings"];
  let factory = NetworkEventActorProxy.methodFactory;
  for (let method of methods) {
    NetworkEventActorProxy.prototype[method] = factory(method);
  }
})();

/**
 * This is triggered by the child calling `setupInParent` when the child's network monitor
 * is starting up.  This initializes the parent process side of the monitoring.
 */
function setupParentProcess({ mm, prefix }) {
  let networkMonitor = new NetworkMonitorParent(mm, prefix);
  return {
    onBrowserSwap: newMM => networkMonitor.setMessageManager(newMM),
    onDisconnected: () => {
      networkMonitor.destroy();
      networkMonitor = null;
    }
  };
}

exports.setupParentProcess = setupParentProcess;

/**
 * The NetworkMonitorParent runs in the parent process and uses the message manager to
 * listen for requests from the child process to start/stop the network monitor.  Most
 * request data is only available from the parent process, so that's why the network
 * monitor needs to run there when debugging tabs that are in the child.
 *
 * @param nsIMessageManager mm
 *        The message manager for the browser we're filtering on.
 * @param string prefix
 *        The RDP connection prefix that uniquely identifies the connection.
 */
function NetworkMonitorParent(mm, prefix) {
  this._msgName = `debug:${prefix}netmonitor`;
  this.onNetMonitorMessage = this.onNetMonitorMessage.bind(this);
  this.onNetworkEvent = this.onNetworkEvent.bind(this);
  this.setMessageManager(mm);
}

NetworkMonitorParent.prototype = {
  netMonitor: null,
  messageManager: null,

  setMessageManager(mm) {
    if (this.messageManager) {
      let oldMM = this.messageManager;
      oldMM.removeMessageListener(this._msgName, this.onNetMonitorMessage);
    }
    this.messageManager = mm;
    if (mm) {
      mm.addMessageListener(this._msgName, this.onNetMonitorMessage);
    }
  },

  /**
   * Handler for `debug:${prefix}netmonitor` messages received through the message manager
   * from the content process.
   *
   * @param object msg
   *        Message from the content.
   */
  onNetMonitorMessage: DevToolsUtils.makeInfallible(function (msg) {
    let {action} = msg.json;
    // Pipe network monitor data from parent to child via the message manager.
    switch (action) {
      case "start":
        if (!this.netMonitor) {
          let {appId, outerWindowID} = msg.json;
          this.netMonitor = new NetworkMonitor({
            outerWindowID,
            appId,
          }, this);
          this.netMonitor.init();
        }
        break;
      case "setPreferences": {
        let {preferences} = msg.json;
        for (let key of Object.keys(preferences)) {
          if ((key == "saveRequestAndResponseBodies" ||
               key == "throttleData") && this.netMonitor) {
            this.netMonitor[key] = preferences[key];
          }
        }
        break;
      }

      case "stop":
        if (this.netMonitor) {
          this.netMonitor.destroy();
          this.netMonitor = null;
        }
        break;

      case "disconnect":
        this.destroy();
        break;
    }
  }),

  /**
   * Handler for new network requests. This method is invoked by the current
   * NetworkMonitor instance.
   *
   * @param object event
   *        Object describing the network request.
   * @return object
   *         A NetworkEventActorProxy instance which is notified when further
   *         data about the request is available.
   */
  onNetworkEvent: DevToolsUtils.makeInfallible(function (event) {
    return new NetworkEventActorProxy(this.messageManager, this._msgName).init(event);
  }),

  destroy: function () {
    this.setMessageManager(null);

    if (this.netMonitor) {
      this.netMonitor.destroy();
      this.netMonitor = null;
    }
  },
};

/**
 * A WebProgressListener that listens for location changes.
 *
 * This progress listener is used to track file loads and other kinds of
 * location changes.
 *
 * @constructor
 * @param object window
 *        The window for which we need to track location changes.
 * @param object owner
 *        The listener owner which needs to implement two methods:
 *        - onFileActivity(aFileURI)
 *        - onLocationChange(aState, aTabURI, aPageTitle)
 */
function ConsoleProgressListener(window, owner) {
  this.window = window;
  this.owner = owner;
}
exports.ConsoleProgressListener = ConsoleProgressListener;

ConsoleProgressListener.prototype = {
  /**
   * Constant used for startMonitor()/stopMonitor() that tells you want to
   * monitor file loads.
   */
  MONITOR_FILE_ACTIVITY: 1,

  /**
   * Constant used for startMonitor()/stopMonitor() that tells you want to
   * monitor page location changes.
   */
  MONITOR_LOCATION_CHANGE: 2,

  /**
   * Tells if you want to monitor file activity.
   * @private
   * @type boolean
   */
  _fileActivity: false,

  /**
   * Tells if you want to monitor location changes.
   * @private
   * @type boolean
   */
  _locationChange: false,

  /**
   * Tells if the console progress listener is initialized or not.
   * @private
   * @type boolean
   */
  _initialized: false,

  _webProgress: null,

  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
                                         Ci.nsISupportsWeakReference]),

  /**
   * Initialize the ConsoleProgressListener.
   * @private
   */
  _init: function () {
    if (this._initialized) {
      return;
    }

    this._webProgress = this.window.QueryInterface(Ci.nsIInterfaceRequestor)
                        .getInterface(Ci.nsIWebNavigation)
                        .QueryInterface(Ci.nsIWebProgress);
    this._webProgress.addProgressListener(this,
                                          Ci.nsIWebProgress.NOTIFY_STATE_ALL);

    this._initialized = true;
  },

  /**
   * Start a monitor/tracker related to the current nsIWebProgressListener
   * instance.
   *
   * @param number monitor
   *        Tells what you want to track. Available constants:
   *        - this.MONITOR_FILE_ACTIVITY
   *          Track file loads.
   *        - this.MONITOR_LOCATION_CHANGE
   *          Track location changes for the top window.
   */
  startMonitor: function (monitor) {
    switch (monitor) {
      case this.MONITOR_FILE_ACTIVITY:
        this._fileActivity = true;
        break;
      case this.MONITOR_LOCATION_CHANGE:
        this._locationChange = true;
        break;
      default:
        throw new Error("ConsoleProgressListener: unknown monitor type " +
                        monitor + "!");
    }
    this._init();
  },

  /**
   * Stop a monitor.
   *
   * @param number monitor
   *        Tells what you want to stop tracking. See this.startMonitor() for
   *        the list of constants.
   */
  stopMonitor: function (monitor) {
    switch (monitor) {
      case this.MONITOR_FILE_ACTIVITY:
        this._fileActivity = false;
        break;
      case this.MONITOR_LOCATION_CHANGE:
        this._locationChange = false;
        break;
      default:
        throw new Error("ConsoleProgressListener: unknown monitor type " +
                        monitor + "!");
    }

    if (!this._fileActivity && !this._locationChange) {
      this.destroy();
    }
  },

  onStateChange: function (progress, request, state, status) {
    if (!this.owner) {
      return;
    }

    if (this._fileActivity) {
      this._checkFileActivity(progress, request, state, status);
    }

    if (this._locationChange) {
      this._checkLocationChange(progress, request, state, status);
    }
  },

  /**
   * Check if there is any file load, given the arguments of
   * nsIWebProgressListener.onStateChange. If the state change tells that a file
   * URI has been loaded, then the remote Web Console instance is notified.
   * @private
   */
  _checkFileActivity: function (progress, request, state, status) {
    if (!(state & Ci.nsIWebProgressListener.STATE_START)) {
      return;
    }

    let uri = null;
    if (request instanceof Ci.imgIRequest) {
      let imgIRequest = request.QueryInterface(Ci.imgIRequest);
      uri = imgIRequest.URI;
    } else if (request instanceof Ci.nsIChannel) {
      let nsIChannel = request.QueryInterface(Ci.nsIChannel);
      uri = nsIChannel.URI;
    }

    if (!uri || !uri.schemeIs("file") && !uri.schemeIs("ftp")) {
      return;
    }

    this.owner.onFileActivity(uri.spec);
  },

  /**
   * Check if the current window.top location is changing, given the arguments
   * of nsIWebProgressListener.onStateChange. If that is the case, the remote
   * Web Console instance is notified.
   * @private
   */
  _checkLocationChange: function (progress, request, state) {
    let isStart = state & Ci.nsIWebProgressListener.STATE_START;
    let isStop = state & Ci.nsIWebProgressListener.STATE_STOP;
    let isNetwork = state & Ci.nsIWebProgressListener.STATE_IS_NETWORK;
    let isWindow = state & Ci.nsIWebProgressListener.STATE_IS_WINDOW;

    // Skip non-interesting states.
    if (!isNetwork || !isWindow || progress.DOMWindow != this.window) {
      return;
    }

    if (isStart && request instanceof Ci.nsIChannel) {
      this.owner.onLocationChange("start", request.URI.spec, "");
    } else if (isStop) {
      this.owner.onLocationChange("stop", this.window.location.href,
                                  this.window.document.title);
    }
  },

  /**
   * Destroy the ConsoleProgressListener.
   */
  destroy: function () {
    if (!this._initialized) {
      return;
    }

    this._initialized = false;
    this._fileActivity = false;
    this._locationChange = false;

    try {
      this._webProgress.removeProgressListener(this);
    } catch (ex) {
      // This can throw during browser shutdown.
    }

    this._webProgress = null;
    this.window = null;
    this.owner = null;
  },
};

function gSequenceId() {
  return gSequenceId.n++;
}
gSequenceId.n = 1;

/**
 * Convert a nsIContentPolicy constant to a display string
 */
const LOAD_CAUSE_STRINGS = {
  [Ci.nsIContentPolicy.TYPE_INVALID]: "invalid",
  [Ci.nsIContentPolicy.TYPE_OTHER]: "other",
  [Ci.nsIContentPolicy.TYPE_SCRIPT]: "script",
  [Ci.nsIContentPolicy.TYPE_IMAGE]: "img",
  [Ci.nsIContentPolicy.TYPE_STYLESHEET]: "stylesheet",
  [Ci.nsIContentPolicy.TYPE_OBJECT]: "object",
  [Ci.nsIContentPolicy.TYPE_DOCUMENT]: "document",
  [Ci.nsIContentPolicy.TYPE_SUBDOCUMENT]: "subdocument",
  [Ci.nsIContentPolicy.TYPE_REFRESH]: "refresh",
  [Ci.nsIContentPolicy.TYPE_XBL]: "xbl",
  [Ci.nsIContentPolicy.TYPE_PING]: "ping",
  [Ci.nsIContentPolicy.TYPE_XMLHTTPREQUEST]: "xhr",
  [Ci.nsIContentPolicy.TYPE_OBJECT_SUBREQUEST]: "objectSubdoc",
  [Ci.nsIContentPolicy.TYPE_DTD]: "dtd",
  [Ci.nsIContentPolicy.TYPE_FONT]: "font",
  [Ci.nsIContentPolicy.TYPE_MEDIA]: "media",
  [Ci.nsIContentPolicy.TYPE_WEBSOCKET]: "websocket",
  [Ci.nsIContentPolicy.TYPE_CSP_REPORT]: "csp",
  [Ci.nsIContentPolicy.TYPE_XSLT]: "xslt",
  [Ci.nsIContentPolicy.TYPE_BEACON]: "beacon",
  [Ci.nsIContentPolicy.TYPE_FETCH]: "fetch",
  [Ci.nsIContentPolicy.TYPE_IMAGESET]: "imageset",
  [Ci.nsIContentPolicy.TYPE_WEB_MANIFEST]: "webManifest"
};

function causeTypeToString(causeType) {
  return LOAD_CAUSE_STRINGS[causeType] || "unknown";
}
PK
!<|��K��Kchrome/devtools/modules/devtools/shared/webconsole/server-logger-monitor.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft= javascript ts=2 et sw=2 tw=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 {Ci} = require("chrome");
const Services = require("Services");

const {makeInfallible} = require("devtools/shared/DevToolsUtils");

loader.lazyGetter(this, "NetworkHelper", () => require("devtools/shared/webconsole/network-helper"));

// Helper tracer. Should be generic sharable by other modules (bug 1171927)
const trace = {
  log: function (...args) {
  }
};

const acceptableHeaders = ["x-chromelogger-data"];

/**
 * This object represents HTTP events observer. It's intended to be
 * used in e10s enabled browser only.
 *
 * Since child processes can't register HTTP event observer they use
 * this module to do the observing in the parent process. This monitor
 * is loaded through DebuggerServerConnection.setupInParent() that is
 * executed from within the child process. The execution is done by
 * {@ServerLoggingListener}.  The monitor listens to HTTP events and
 * forwards it into the right child process.
 *
 * Read more about the architecture:
 * https://github.com/mozilla/gecko-dev/blob/fx-team/devtools/server/docs/actor-e10s-handling.md
 */
var ServerLoggerMonitor = {
  // Initialization

  initialize: function () {
    this.onChildMessage = this.onChildMessage.bind(this);
    this.onExamineResponse = this.onExamineResponse.bind(this);

    // Set of registered child frames (loggers).
    this.targets = new Set();
  },

  // Parent Child Relationship

  attach: makeInfallible(function ({ mm, prefix }) {
    trace.log("ServerLoggerMonitor.attach; ", arguments);

    let setMessageManager = newMM => {
      if (mm) {
        mm.removeMessageListener("debug:server-logger", this.onChildMessage);
      }
      mm = newMM;
      if (mm) {
        mm.addMessageListener("debug:server-logger", this.onChildMessage);
      }
    };

    // Start listening for messages from the {@ServerLogger} actor
    // living in the child process.
    setMessageManager(mm);

    return {
      onBrowserSwap: setMessageManager,
      onDisconnected: () => {
        trace.log("ServerLoggerMonitor.onDisconnectChild; ", arguments);
        setMessageManager(null);
      }
    };
  }),

  // Child Message Handling

  onChildMessage: function (msg) {
    let method = msg.data.method;

    trace.log("ServerLoggerMonitor.onChildMessage; ", method, msg);

    switch (method) {
      case "attachChild":
        return this.onAttachChild(msg);
      case "detachChild":
        return this.onDetachChild(msg);
      default:
        trace.log("Unknown method name: ", method);
        return undefined;
    }
  },

  onAttachChild: function (event) {
    let target = event.target;
    let size = this.targets.size;

    trace.log("ServerLoggerMonitor.onAttachChild; size: ", size, target);

    // If this is the first child attached, register global HTTP observer.
    if (!size) {
      trace.log("ServerLoggerMonitor.onAttatchChild; Add HTTP Observer");
      Services.obs.addObserver(this.onExamineResponse,
        "http-on-examine-response");
    }

    // Collect child loggers. The frame element where the
    // window/document lives.
    this.targets.add(target);
  },

  onDetachChild: function (event) {
    let target = event.target;
    this.targets.delete(target);

    let size = this.targets.size;
    trace.log("ServerLoggerMonitor.onDetachChild; size: ", size, target);

    // If this is the last child process attached, unregister
    // the global HTTP observer.
    if (!size) {
      trace.log("ServerLoggerMonitor.onDetachChild; Remove HTTP Observer");
      Services.obs.removeObserver(this.onExamineResponse,
        "http-on-examine-response");
    }
  },

  // HTTP Observer

  onExamineResponse: makeInfallible(function (subject, topic) {
    let httpChannel = subject.QueryInterface(Ci.nsIHttpChannel);

    trace.log("ServerLoggerMonitor.onExamineResponse; ", httpChannel.name,
      this.targets);

    // Ignore requests from chrome or add-on code when we are monitoring
    // content.
    if (!httpChannel.loadInfo &&
        httpChannel.loadInfo.loadingDocument === null &&
        httpChannel.loadInfo.loadingPrincipal ===
        Services.scriptSecurityManager.getSystemPrincipal()) {
      return;
    }

    let requestFrame = NetworkHelper.getTopFrameForRequest(httpChannel);
    if (!requestFrame) {
      return;
    }

    // Ignore requests from parent frames that aren't registered.
    if (!this.targets.has(requestFrame)) {
      return;
    }

    let headers = [];

    httpChannel.visitResponseHeaders((header, value) => {
      header = header.toLowerCase();
      if (acceptableHeaders.includes(header)) {
        headers.push({header: header, value: value});
      }
    });

    if (!headers.length) {
      return;
    }

    let { messageManager } = requestFrame;
    messageManager.sendAsyncMessage("debug:server-logger", {
      method: "examineHeaders",
      headers: headers,
    });

    trace.log("ServerLoggerMonitor.onExamineResponse; headers ",
      headers.length, ", ", headers);
  }),
};

/**
 * Executed automatically by the framework.
 */
function setupParentProcess(event) {
  return ServerLoggerMonitor.attach(event);
}

// Monitor initialization.
ServerLoggerMonitor.initialize();

// Exports from this module
exports.setupParentProcess = setupParentProcess;
PK
!<��&�8�8Cchrome/devtools/modules/devtools/shared/webconsole/server-logger.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft= javascript ts=2 et sw=2 tw=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 {Ci} = require("chrome");
const Services = require("Services");
const {DebuggerServer} = require("devtools/server/main");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");

loader.lazyGetter(this, "NetworkHelper", () => require("devtools/shared/webconsole/network-helper"));

// Helper tracer. Should be generic sharable by other modules (bug 1171927)
const trace = {
  log: function () {
  }
};

// Constants
const makeInfallible = DevToolsUtils.makeInfallible;
const acceptableHeaders = ["x-chromelogger-data"];

/**
 * The listener is responsible for detecting server side logs
 * within HTTP headers and sending them to the client.
 *
 * The logic is based on "http-on-examine-response" event that is
 * sent when a response from the server is received. Consequently HTTP
 * headers are parsed to find server side logs.
 *
 * A listeners for "http-on-examine-response" is registered when
 * the listener starts and removed when destroy is executed.
 *
 * @param {Object} win (nsIDOMWindow):
 *        filter network requests by the associated window object.
 *        If null (i.e. in the browser context) log everything
 * @param {Object} owner
 *        The {@WebConsoleActor} instance
 */
function ServerLoggingListener(win, owner) {
  trace.log("ServerLoggingListener.initialize; ", owner.actorID,
    ", child process: ", DebuggerServer.isInChildProcess);

  this.owner = owner;
  this.window = win;

  this.onExamineResponse = this.onExamineResponse.bind(this);
  this.onExamineHeaders = this.onExamineHeaders.bind(this);
  this.onParentMessage = this.onParentMessage.bind(this);

  this.attach();
}

ServerLoggingListener.prototype = {
  /**
   * The destroy is called by the parent WebConsoleActor actor.
   */
  destroy: function () {
    trace.log("ServerLoggingListener.destroy; ", this.owner.actorID,
      ", child process: ", DebuggerServer.isInChildProcess);

    this.detach();
  },

  /**
   * The main responsibility of this method is registering a listener for
   * "http-on-examine-response" events.
   */
  attach: makeInfallible(function () {
    trace.log("ServerLoggingListener.attach; child process: ",
      DebuggerServer.isInChildProcess);

    // Setup the child <-> parent communication if this actor module
    // is running in a child process. If e10s is disabled (this actor
    // running in the same process as everything else) register observer
    // listener just like in good old pre e10s days.
    if (DebuggerServer.isInChildProcess) {
      this.attachParentProcess();
    } else {
      Services.obs.addObserver(this.onExamineResponse,
        "http-on-examine-response");
    }
  }),

  /**
   * Remove the "http-on-examine-response" listener.
   */
  detach: makeInfallible(function () {
    trace.log("ServerLoggingListener.detach; ", this.owner.actorID);

    if (DebuggerServer.isInChildProcess) {
      this.detachParentProcess();
    } else {
      Services.obs.removeObserver(this.onExamineResponse,
        "http-on-examine-response");
    }
  }),

  // Parent Child Relationship

  attachParentProcess: function () {
    trace.log("ServerLoggingListener.attachParentProcess;");

    this.owner.conn.setupInParent({
      module: "devtools/shared/webconsole/server-logger-monitor",
      setupParent: "setupParentProcess"
    });

    let mm = this.owner.conn.parentMessageManager;
    let { addMessageListener, sendSyncMessage } = mm;

    // It isn't possible to register HTTP-* event observer inside
    // a child process (in case of e10s), so listen for messages
    // coming from the {@ServerLoggerMonitor} that lives inside
    // the parent process.
    addMessageListener("debug:server-logger", this.onParentMessage);

    // Attach to the {@ServerLoggerMonitor} object to subscribe events.
    sendSyncMessage("debug:server-logger", {
      method: "attachChild"
    });
  },

  detachParentProcess: makeInfallible(function () {
    trace.log("ServerLoggingListener.detachParentProcess;");

    let mm = this.owner.conn.parentMessageManager;
    let { removeMessageListener, sendSyncMessage } = mm;

    sendSyncMessage("debug:server-logger", {
      method: "detachChild",
    });

    removeMessageListener("debug:server-logger", this.onParentMessage);
  }),

  onParentMessage: makeInfallible(function (msg) {
    if (!msg.data) {
      return;
    }

    let method = msg.data.method;
    trace.log("ServerLogger.onParentMessage; ", method, msg.data);

    switch (method) {
      case "examineHeaders":
        this.onExamineHeaders(msg);
        break;
      default:
        trace.log("Unknown method name: ", method);
    }
  }),

  // HTTP Observer

  onExamineHeaders: function (event) {
    let headers = event.data.headers;

    trace.log("ServerLoggingListener.onExamineHeaders;", headers);

    let parsedMessages = [];

    for (let item of headers) {
      let header = item.header;
      let value = item.value;

      let messages = this.parse(header, value);
      if (messages) {
        parsedMessages.push(...messages);
      }
    }

    if (!parsedMessages.length) {
      return;
    }

    for (let message of parsedMessages) {
      this.sendMessage(message);
    }
  },

  onExamineResponse: makeInfallible(function (subject) {
    let httpChannel = subject.QueryInterface(Ci.nsIHttpChannel);

    trace.log("ServerLoggingListener.onExamineResponse; ", httpChannel.name,
      ", ", this.owner.actorID, httpChannel);

    if (!this._matchRequest(httpChannel)) {
      trace.log("ServerLoggerMonitor.onExamineResponse; No matching request!");
      return;
    }

    let headers = [];

    httpChannel.visitResponseHeaders((header, value) => {
      header = header.toLowerCase();
      if (acceptableHeaders.includes(header)) {
        headers.push({header: header, value: value});
      }
    });

    this.onExamineHeaders({
      data: {
        headers: headers,
      }
    });
  }),

  /**
   * Check if a given network request should be logged by this network monitor
   * instance based on the current filters.
   *
   * @private
   * @param nsIHttpChannel channel
   *        Request to check.
   * @return boolean
   *         True if the network request should be logged, false otherwise.
   */
  _matchRequest: function (channel) {
    trace.log("_matchRequest ", this.window, ", ", this.topFrame);

    // Log everything if the window is null (it's null in the browser context)
    if (!this.window) {
      return true;
    }

    // Ignore requests from chrome or add-on code when we are monitoring
    // content.
    if (!channel.loadInfo &&
        channel.loadInfo.loadingDocument === null &&
        channel.loadInfo.loadingPrincipal ===
        Services.scriptSecurityManager.getSystemPrincipal()) {
      return false;
    }

    // Since frames support, this.window may not be the top level content
    // frame, so that we can't only compare with win.top.
    let win = NetworkHelper.getWindowForRequest(channel);
    while (win) {
      if (win == this.window) {
        return true;
      }
      if (win.parent == win) {
        break;
      }
      win = win.parent;
    }

    return false;
  },

  // Server Logs

  /**
   * Search through HTTP headers to catch all server side logs.
   * Learn more about the data structure:
   * https://craig.is/writing/chrome-logger/techspecs
   */
  parse: function (header, value) {
    let data;

    try {
      let result = decodeURIComponent(escape(atob(value)));
      data = JSON.parse(result);
    } catch (err) {
      console.error("Failed to parse HTTP log data! " + err);
      return null;
    }

    let parsedMessage = [];
    let columnMap = this.getColumnMap(data);

    trace.log("ServerLoggingListener.parse; ColumnMap", columnMap);
    trace.log("ServerLoggingListener.parse; data", data);

    let lastLocation;

    for (let row of data.rows) {
      let backtrace = row[columnMap.get("backtrace")];
      let rawLogs = row[columnMap.get("log")];
      let type = row[columnMap.get("type")] || "log";

      // Old version of the protocol includes a label.
      // If this is the old version do some converting.
      if (data.columns.includes("label")) {
        let label = row[columnMap.get("label")];
        let showLabel = label && typeof label === "string";

        rawLogs = [rawLogs];

        if (showLabel) {
          rawLogs.unshift(label);
        }
      }

      // If multiple logs come from the same line only the first log
      // has info about the backtrace. So, remember the last valid
      // location and use it for those that not set.
      let location = parseBacktrace(backtrace);
      if (location) {
        lastLocation = location;
      } else {
        location = lastLocation;
      }

      parsedMessage.push({
        logs: rawLogs,
        location: location,
        type: type
      });
    }

    return parsedMessage;
  },

  getColumnMap: function (data) {
    let columnMap = new Map();
    let columnName;

    for (let key in data.columns) {
      columnName = data.columns[key];
      columnMap.set(columnName, key);
    }

    return columnMap;
  },

  sendMessage: function (msg) {
    trace.log("ServerLoggingListener.sendMessage; message", msg);

    let formatted = format(msg);
    trace.log("ServerLoggingListener.sendMessage; formatted", formatted);

    let win = this.window;
    let innerID = win ? getInnerId(win) : null;
    let location = msg.location;

    let message = {
      category: "server",
      innerID: innerID,
      level: msg.type,
      filename: location ? location.url : null,
      lineNumber: location ? location.line : null,
      columnNumber: 0,
      private: false,
      timeStamp: Date.now(),
      arguments: formatted ? formatted.logs : null,
      styles: formatted ? formatted.styles : null,
    };

    // Make sure to set the group name.
    if (msg.type == "group" && formatted && formatted.logs) {
      message.groupName = formatted ? formatted.logs[0] : null;
    }

    // A message for console.table() method (passed in as the first
    // argument) isn't supported. But, it's passed in by some server
    // side libraries that implement console.* API - let's just remove it.
    let args = message.arguments;
    if (msg.type == "table" && args) {
      if (typeof args[0] == "string") {
        args.shift();
      }
    }

    trace.log("ServerLoggingListener.sendMessage; raw: ",
      msg.logs.join(", "), message);

    this.owner.onServerLogCall(message);
  },
};

// Helpers

/**
 * Parse printf-like specifiers ("%f", "%d", ...) and
 * format the logs according to them.
 */
function format(msg) {
  if (!msg.logs || !msg.logs[0]) {
    return null;
  }

  // Initialize the styles array (used for the "%c" specifier).
  msg.styles = [];

  // Remove and get the first log (in which the specifiers are).
  // Note that the first string doesn't have to be specified.
  // An example of a log on the server side:
  // ChromePhp::log("server info: ", $_SERVER);
  // ChromePhp::log($_SERVER);
  let firstString = "";
  if (typeof msg.logs[0] == "string") {
    firstString = msg.logs.shift();
  }

  // All the specifiers present in the first string.
  let splitLogRegExp = /(.*?)(%[oOcsdif]|$)/g;
  let splitLogRegExpRes;
  let concatString = "";
  let pushConcatString = () => {
    if (concatString) {
      rebuiltLogArray.push(concatString);
    }
    concatString = "";
  };

  // This array represents the string of the log, in which the specifiers
  // are replaced. It alternates strings and objects (%o;%O).
  let rebuiltLogArray = [];

  // Get the strings before the specifiers (or the last chunk before the end
  // of the string).
  while ((splitLogRegExpRes = splitLogRegExp.exec(firstString)) !== null) {
    let [, log, specifier] = splitLogRegExpRes;

    // We may start with a specifier or add consecutively several ones. In such
    // a case, there is no log.
    // Example: "%ctest" => first iteration: log = "", specifier = "%c".
    //                   => second iteration: log = "test", specifier = "".
    if (log) {
      concatString += log;
    }

    // Break now if there is no specifier anymore
    // (means that we have reached the end of the string).
    if (!specifier) {
      break;
    }

    let argument = msg.logs.shift();
    switch (specifier) {
      case "%i":
      case "%d":
        // Parse into integer.
        concatString += (argument | 0);
        break;
      case "%f":
        // Parse into float.
        concatString += (+argument);
        break;
      case "%o":
      case "%O":
        // Push the concatenated string and reinitialize concatString.
        pushConcatString();
        // Push the object.
        rebuiltLogArray.push(argument);
        break;
      case "%s":
        concatString += argument;
        break;
      case "%c":
        pushConcatString();
        let fillNullArrayLength = rebuiltLogArray.length - msg.styles.length;
        let fillNullArray = Array(fillNullArrayLength).fill(null);
        msg.styles.push(...fillNullArray, argument);
        break;
    }
  }

  if (concatString) {
    rebuiltLogArray.push(concatString);
  }

  // Append the rest of arguments that don't have corresponding
  // specifiers to the message logs.
  msg.logs.unshift(...rebuiltLogArray);

  // Remove special ___class_name property that isn't supported
  // by the current implementation. This property represents object class
  // allowing custom rendering in the console panel.
  for (let log of msg.logs) {
    if (typeof log == "object") {
      delete log.___class_name;
    }
  }

  return msg;
}

function parseBacktrace(backtrace) {
  if (!backtrace) {
    return null;
  }

  let result = backtrace.match(/^(.+?)\s*:\s*(\d+)$/);
  if (!result || result.length != 3) {
    return { url: backtrace };
  }

  return {
    url: result[1],
    line: parseInt(result[2], 10)
  };
}

// These helper are cloned from SDK to avoid loading to
// much SDK modules just because of two functions.
function getInnerId(win) {
  return win.QueryInterface(Ci.nsIInterfaceRequestor)
    .getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
}

// Exports from this module
exports.ServerLoggingListener = ServerLoggingListener;
exports.parseBacktrace = parseBacktrace;
PK
!<�"���5�5>chrome/devtools/modules/devtools/shared/webconsole/throttle.js/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft= javascript ts=2 et sw=2 tw=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 {CC, Ci, Cc} = require("chrome");

const ArrayBufferInputStream = CC("@mozilla.org/io/arraybuffer-input-stream;1",
                                  "nsIArrayBufferInputStream");
const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
                             "nsIBinaryInputStream", "setInputStream");

loader.lazyServiceGetter(this, "gActivityDistributor",
                         "@mozilla.org/network/http-activity-distributor;1",
                         "nsIHttpActivityDistributor");

const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
const {setTimeout} = require("resource://gre/modules/Timer.jsm");

/**
 * Construct a new nsIStreamListener that buffers data and provides a
 * method to notify another listener when data is available.  This is
 * used to throttle network data on a per-channel basis.
 *
 * After construction, @see setOriginalListener must be called on the
 * new object.
 *
 * @param {NetworkThrottleQueue} queue the NetworkThrottleQueue to
 *        which status changes should be reported
 */
function NetworkThrottleListener(queue) {
  this.queue = queue;
  this.pendingData = [];
  this.pendingException = null;
  this.offset = 0;
  this.responseStarted = false;
  this.activities = {};
}

NetworkThrottleListener.prototype = {
  QueryInterface:
    XPCOMUtils.generateQI([Ci.nsIStreamListener, Ci.nsIInterfaceRequestor,
                           Ci.nsISupports]),

  /**
   * Set the original listener for this object.  The original listener
   * will receive requests from this object when the queue allows data
   * through.
   *
   * @param {nsIStreamListener} originalListener the original listener
   *        for the channel, to which all requests will be sent
   */
  setOriginalListener: function (originalListener) {
    this.originalListener = originalListener;
  },

  /**
   * @see nsIStreamListener.onStartRequest.
   */
  onStartRequest: function (request, context) {
    this.originalListener.onStartRequest(request, context);
    this.queue.start(this);
  },

  /**
   * @see nsIStreamListener.onStopRequest.
   */
  onStopRequest: function (request, context, statusCode) {
    this.pendingData.push({request, context, statusCode});
    this.queue.dataAvailable(this);
  },

  /**
   * @see nsIStreamListener.onDataAvailable.
   */
  onDataAvailable: function (request, context, inputStream, offset, count) {
    if (this.pendingException) {
      throw this.pendingException;
    }

    const bin = new BinaryInputStream(inputStream);
    const bytes = new ArrayBuffer(count);
    bin.readArrayBuffer(count, bytes);

    const stream = new ArrayBufferInputStream();
    stream.setData(bytes, 0, count);

    this.pendingData.push({request, context, stream, count});
    this.queue.dataAvailable(this);
  },

  /**
   * Allow some buffered data from this object to be forwarded to this
   * object's originalListener.
   *
   * @param {Number} bytesPermitted The maximum number of bytes
   *        permitted to be sent.
   * @return {Object} an object of the form {length, done}, where
   *         |length| is the number of bytes actually forwarded, and
   *         |done| is a boolean indicating whether this particular
   *         request has been completed.  (A NetworkThrottleListener
   *         may be queued multiple times, so this does not mean that
   *         all available data has been sent.)
   */
  sendSomeData: function (bytesPermitted) {
    if (this.pendingData.length === 0) {
      // Shouldn't happen.
      return {length: 0, done: true};
    }

    const {request, context, stream, count, statusCode} = this.pendingData[0];

    if (statusCode !== undefined) {
      this.pendingData.shift();
      this.originalListener.onStopRequest(request, context, statusCode);
      return {length: 0, done: true};
    }

    if (bytesPermitted > count) {
      bytesPermitted = count;
    }

    try {
      this.originalListener.onDataAvailable(request, context, stream,
                                            this.offset, bytesPermitted);
    } catch (e) {
      this.pendingException = e;
    }

    let done = false;
    if (bytesPermitted === count) {
      this.pendingData.shift();
      done = true;
    } else {
      this.pendingData[0].count -= bytesPermitted;
    }

    this.offset += bytesPermitted;
    // Maybe our state has changed enough to emit an event.
    this.maybeEmitEvents();

    return {length: bytesPermitted, done};
  },

  /**
   * Return the number of pending data requests available for this
   * listener.
   */
  pendingCount: function () {
    return this.pendingData.length;
  },

  /**
   * This is called when an http activity event is delivered.  This
   * object delays the event until the appropriate moment.
   */
  addActivityCallback: function (callback, httpActivity, channel, activityType,
                                 activitySubtype, timestamp, extraSizeData,
                                 extraStringData) {
    let datum = {callback, httpActivity, channel, activityType,
                 activitySubtype, extraSizeData,
                 extraStringData};
    this.activities[activitySubtype] = datum;

    if (activitySubtype ===
        gActivityDistributor.ACTIVITY_SUBTYPE_RESPONSE_COMPLETE) {
      this.totalSize = extraSizeData;
    }

    this.maybeEmitEvents();
  },

  /**
   * This is called for a download throttler when the latency timeout
   * has ended.
   */
  responseStart: function () {
    this.responseStarted = true;
    this.maybeEmitEvents();
  },

  /**
   * Check our internal state and emit any http activity events as
   * needed.  Note that we wait until both our internal state has
   * changed and we've received the real http activity event from
   * platform.  This approach ensures we can both pass on the correct
   * data from the original event, and update the reported time to be
   * consistent with the delay we're introducing.
   */
  maybeEmitEvents: function () {
    if (this.responseStarted) {
      this.maybeEmit(gActivityDistributor.ACTIVITY_SUBTYPE_RESPONSE_START);
      this.maybeEmit(gActivityDistributor.ACTIVITY_SUBTYPE_RESPONSE_HEADER);
    }

    if (this.totalSize !== undefined && this.offset >= this.totalSize) {
      this.maybeEmit(gActivityDistributor.ACTIVITY_SUBTYPE_RESPONSE_COMPLETE);
      this.maybeEmit(gActivityDistributor.ACTIVITY_SUBTYPE_TRANSACTION_CLOSE);
    }
  },

  /**
   * Emit an event for |code|, if the appropriate entry in
   * |activities| is defined.
   */
  maybeEmit: function (code) {
    if (this.activities[code] !== undefined) {
      let {callback, httpActivity, channel, activityType,
           activitySubtype, extraSizeData,
           extraStringData} = this.activities[code];
      let now = Date.now() * 1000;
      callback(httpActivity, channel, activityType, activitySubtype,
               now, extraSizeData, extraStringData);
      this.activities[code] = undefined;
    }
  },
};

/**
 * Construct a new queue that can be used to throttle the network for
 * a group of related network requests.
 *
 * meanBPS {Number} Mean bytes per second.
 * maxBPS {Number} Maximum bytes per second.
 * latencyMean {Number} Mean latency in milliseconds.
 * latencyMax {Number} Maximum latency in milliseconds.
 */
function NetworkThrottleQueue(meanBPS, maxBPS, latencyMean, latencyMax) {
  this.meanBPS = meanBPS;
  this.maxBPS = maxBPS;
  this.latencyMean = latencyMean;
  this.latencyMax = latencyMax;

  this.pendingRequests = new Set();
  this.downloadQueue = [];
  this.previousReads = [];

  this.pumping = false;
}

NetworkThrottleQueue.prototype = {
  /**
   * A helper function that, given a mean and a maximum, returns a
   * random integer between (mean - (max - mean)) and max.
   */
  random: function (mean, max) {
    return mean - (max - mean) + Math.floor(2 * (max - mean) * Math.random());
  },

  /**
   * A helper function that lets the indicating listener start sending
   * data.  This is called after the initial round trip time for the
   * listener has elapsed.
   */
  allowDataFrom: function (throttleListener) {
    throttleListener.responseStart();
    this.pendingRequests.delete(throttleListener);
    const count = throttleListener.pendingCount();
    for (let i = 0; i < count; ++i) {
      this.downloadQueue.push(throttleListener);
    }
    this.pump();
  },

  /**
   * Notice a new listener object.  This is called by the
   * NetworkThrottleListener when the request has started.  Initially
   * a new listener object is put into a "pending" state, until the
   * round-trip time has elapsed.  This is used to simulate latency.
   *
   * @param {NetworkThrottleListener} throttleListener the new listener
   */
  start: function (throttleListener) {
    this.pendingRequests.add(throttleListener);
    let delay = this.random(this.latencyMean, this.latencyMax);
    if (delay > 0) {
      setTimeout(() => this.allowDataFrom(throttleListener), delay);
    } else {
      this.allowDataFrom(throttleListener);
    }
  },

  /**
   * Note that new data is available for a given listener.  Each time
   * data is available, the listener will be re-queued.
   *
   * @param {NetworkThrottleListener} throttleListener the listener
   *        which has data available.
   */
  dataAvailable: function (throttleListener) {
    if (!this.pendingRequests.has(throttleListener)) {
      this.downloadQueue.push(throttleListener);
      this.pump();
    }
  },

  /**
   * An internal function that permits individual listeners to send
   * data.
   */
  pump: function () {
    // A redirect will cause two NetworkThrottleListeners to be on a
    // listener chain.  In this case, we might recursively call into
    // this method.  Avoid infinite recursion here.
    if (this.pumping) {
      return;
    }
    this.pumping = true;

    const now = Date.now();
    const oneSecondAgo = now - 1000;

    while (this.previousReads.length &&
           this.previousReads[0].when < oneSecondAgo) {
      this.previousReads.shift();
    }

    const totalBytes = this.previousReads.reduce((sum, elt) => {
      return sum + elt.numBytes;
    }, 0);

    let thisSliceBytes = this.random(this.meanBPS, this.maxBPS);
    if (totalBytes < thisSliceBytes) {
      thisSliceBytes -= totalBytes;
      let readThisTime = 0;
      while (thisSliceBytes > 0 && this.downloadQueue.length) {
        let {length, done} = this.downloadQueue[0].sendSomeData(thisSliceBytes);
        thisSliceBytes -= length;
        readThisTime += length;
        if (done) {
          this.downloadQueue.shift();
        }
      }
      this.previousReads.push({when: now, numBytes: readThisTime});
    }

    // If there is more data to download, then schedule ourselves for
    // one second after the oldest previous read.
    if (this.downloadQueue.length) {
      const when = this.previousReads[0].when + 1000;
      setTimeout(this.pump.bind(this), when - now);
    }

    this.pumping = false;
  },
};

/**
 * Construct a new object that can be used to throttle the network for
 * a group of related network requests.
 *
 * @param {Object} An object with the following attributes:
 * latencyMean {Number} Mean latency in milliseconds.
 * latencyMax {Number} Maximum latency in milliseconds.
 * downloadBPSMean {Number} Mean bytes per second for downloads.
 * downloadBPSMax {Number} Maximum bytes per second for downloads.
 * uploadBPSMean {Number} Mean bytes per second for uploads.
 * uploadBPSMax {Number} Maximum bytes per second for uploads.
 *
 * Download throttling will not be done if downloadBPSMean and
 * downloadBPSMax are <= 0.  Upload throttling will not be done if
 * uploadBPSMean and uploadBPSMax are <= 0.
 */
function NetworkThrottleManager({latencyMean, latencyMax,
                                 downloadBPSMean, downloadBPSMax,
                                 uploadBPSMean, uploadBPSMax}) {
  if (downloadBPSMax <= 0 && downloadBPSMean <= 0) {
    this.downloadQueue = null;
  } else {
    this.downloadQueue =
      new NetworkThrottleQueue(downloadBPSMean, downloadBPSMax,
                               latencyMean, latencyMax);
  }
  if (uploadBPSMax <= 0 && uploadBPSMean <= 0) {
    this.uploadQueue = null;
  } else {
    this.uploadQueue = Cc["@mozilla.org/network/throttlequeue;1"]
      .createInstance(Ci.nsIInputChannelThrottleQueue);
    this.uploadQueue.init(uploadBPSMean, uploadBPSMax);
  }
}
exports.NetworkThrottleManager = NetworkThrottleManager;

NetworkThrottleManager.prototype = {
  /**
   * Create a new NetworkThrottleListener for a given channel and
   * install it using |setNewListener|.
   *
   * @param {nsITraceableChannel} channel the channel to manage
   * @return {NetworkThrottleListener} the new listener, or null if
   *         download throttling is not being done.
   */
  manage: function (channel) {
    if (this.downloadQueue) {
      let listener = new NetworkThrottleListener(this.downloadQueue);
      let originalListener = channel.setNewListener(listener);
      listener.setOriginalListener(originalListener);
      return listener;
    }
    return null;
  },

  /**
   * Throttle uploads taking place on the given channel.
   *
   * @param {nsITraceableChannel} channel the channel to manage
   */
  manageUpload: function (channel) {
    if (this.uploadQueue) {
      channel = channel.QueryInterface(Ci.nsIThrottledInputChannel);
      channel.throttleQueue = this.uploadQueue;
    }
  },
};
PK
!<l'����8chrome/devtools/modules/devtools/shared/worker/helper.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 amd */

"use strict";

(function (root, factory) {
  if (typeof define === "function" && define.amd) {
    define(factory);
  } else if (typeof exports === "object") {
    module.exports = factory();
  } else {
    root.workerHelper = factory();
  }
}(this, function () {
  /**
   * This file is to only be included by ChromeWorkers. This exposes
   * a `createTask` function to workers to register tasks for communication
   * back to `devtools/shared/worker`.
   *
   * Tasks can be send their responses via a return value, either a primitive
   * or a promise.
   *
   * createTask(self, "average", function (data) {
   *   return data.reduce((sum, val) => sum + val, 0) / data.length;
   * });
   *
   * createTask(self, "average", function (data) {
   *   return new Promise((resolve, reject) => {
   *     resolve(data.reduce((sum, val) => sum + val, 0) / data.length);
   *   });
   * });
   *
   *
   * Errors:
   *
   * Returning an Error value, or if the returned promise is rejected, this
   * propagates to the DevToolsWorker as a rejected promise. If an error is
   * thrown in a synchronous function, that error is also propagated.
   */

  /**
   * Takes a worker's `self` object, a task name, and a function to
   * be called when that task is called. The task is called with the
   * passed in data as the first argument
   *
   * @param {object} self
   * @param {string} name
   * @param {function} fn
   */
  function createTask(self, name, fn) {
    // Store a hash of task name to function on the Worker
    if (!self._tasks) {
      self._tasks = {};
    }

    // Create the onmessage handler if not yet created.
    if (!self.onmessage) {
      self.onmessage = createHandler(self);
    }

    // Store the task on the worker.
    self._tasks[name] = fn;
  }

  /**
   * Creates the `self.onmessage` handler for a Worker.
   *
   * @param {object} self
   * @return {function}
   */
  function createHandler(self) {
    return function (e) {
      let { id, task, data } = e.data;
      let taskFn = self._tasks[task];

      if (!taskFn) {
        self.postMessage({ id, error: `Task "${task}" not found in worker.` });
        return;
      }

      try {
        handleResponse(taskFn(data));
      } catch (ex) {
        handleError(ex);
      }

      function handleResponse(response) {
        // If a promise
        if (response && typeof response.then === "function") {
          response.then(val => self.postMessage({ id, response: val }), handleError);
        } else if (response instanceof Error) {
          // If an error object
          handleError(response);
        } else {
          // If anything else
          self.postMessage({ id, response });
        }
      }

      function handleError(error = "Error") {
        try {
          // First, try and structured clone the error across directly.
          self.postMessage({ id, error });
        } catch (x) {
          // We could not clone whatever error value was given. Do our best to
          // stringify it.
          let errorString = `Error while performing task "${task}": `;

          try {
            errorString += error.toString();
          } catch (ex) {
            errorString += "<could not stringify error>";
          }

          if ("stack" in error) {
            try {
              errorString += "\n" + error.stack;
            } catch (err) {
              // Do nothing
            }
          }

          self.postMessage({ id, error: errorString });
        }
      }
    };
  }

  return { createTask: createTask };
}));
PK
!<`���G�G8chrome/devtools/modules/devtools/shared/worker/loader.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 worker */

// A CommonJS module loader that is designed to run inside a worker debugger.
// We can't simply use the SDK module loader, because it relies heavily on
// Components, which isn't available in workers.
//
// In principle, the standard instance of the worker loader should provide the
// same built-in modules as its devtools counterpart, so that both loaders are
// interchangable on the main thread, making them easier to test.
//
// On the worker thread, some of these modules, in particular those that rely on
// the use of Components, and for which the worker debugger doesn't provide an
// alternative API, will be replaced by vacuous objects. Consequently, they can
// still be required, but any attempts to use them will lead to an exception.
//
// Note: to see dump output when running inside the worker thread, you might
// need to enable the browser.dom.window.dump.enabled pref.

this.EXPORTED_SYMBOLS = ["WorkerDebuggerLoader", "worker"];

// Some notes on module ids and URLs:
//
// An id is either a relative id or an absolute id. An id is relative if and
// only if it starts with a dot. An absolute id is a normalized id if and only
// if it contains no redundant components.
//
// Every normalized id is a URL. A URL is either an absolute URL or a relative
// URL. A URL is absolute if and only if it starts with a scheme name followed
// by a colon and 2 or 3 slashes.

/**
 * Convert the given relative id to an absolute id.
 *
 * @param String id
 *        The relative id to be resolved.
 * @param String baseId
 *        The absolute base id to resolve the relative id against.
 *
 * @return String
 *         An absolute id
 */
function resolveId(id, baseId) {
  return baseId + "/../" + id;
}

/**
 * Convert the given absolute id to a normalized id.
 *
 * @param String id
 *        The absolute id to be normalized.
 *
 * @return String
 *         A normalized id.
 */
function normalizeId(id) {
  // An id consists of an optional root and a path. A root consists of either
  // a scheme name followed by 2 or 3 slashes, or a single slash. Slashes in the
  // root are not used as separators, so only normalize the path.
  let [, root, path] = id.match(/^(\w+:\/\/\/?|\/)?(.*)/);

  let stack = [];
  path.split("/").forEach(function (component) {
    switch (component) {
      case "":
      case ".":
        break;
      case "..":
        if (stack.length === 0) {
          if (root !== undefined) {
            throw new Error("Can't normalize absolute id '" + id + "'!");
          } else {
            stack.push("..");
          }
        } else if (stack[stack.length - 1] == "..") {
          stack.push("..");
        } else {
          stack.pop();
        }
        break;
      default:
        stack.push(component);
        break;
    }
  });

  return (root ? root : "") + stack.join("/");
}

/**
 * Create a module object with the given normalized id.
 *
 * @param String
 *        The normalized id of the module to be created.
 *
 * @return Object
 *         A module with the given id.
 */
function createModule(id) {
  return Object.create(null, {
    // CommonJS specifies the id property to be non-configurable and
    // non-writable.
    id: {
      configurable: false,
      enumerable: true,
      value: id,
      writable: false
    },

    // CommonJS does not specify an exports property, so follow the NodeJS
    // convention, which is to make it non-configurable and writable.
    exports: {
      configurable: false,
      enumerable: true,
      value: Object.create(null),
      writable: true
    }
  });
}

function defineLazyGetter(object, prop, getter) {
  let redefine = (obj, value) => {
    Object.defineProperty(obj, prop, {
      configurable: true,
      writable: true,
      value,
    });
    return value;
  };

  Object.defineProperty(object, prop, {
    configurable: true,
    get() {
      return redefine(this, getter.call(this));
    },
    set(value) {
      redefine(this, value);
    }
  });
}

/**
 * Defines lazy getters on the given object, which lazily require the
 * given module the first time they are accessed, and then resolve that
 * module's exported properties.
 *
 * @param {object} obj
 *        The target object on which to define the lazy getters.
 * @param {string} moduleId
 *        The ID of the module to require, as passed to require().
 * @param {Array<string | object>} args
 *        Any number of properties to import from the module. A string
 *        will cause the property to be defined which resolves to the
 *        same property in the module's exports. An object will define a
 *        lazy getter for every value in the object which corresponds to
 *        the given key in the module's exports, as in an ordinary
 *        destructuring assignment.
 */
function lazyRequire(obj, moduleId, ...args) {
  let module;
  let getModule = () => {
    if (!module) {
      module = this.require(moduleId);
    }
    return module;
  };

  for (let props of args) {
    if (typeof props !== "object") {
      props = {[props]: props};
    }

    for (let [fromName, toName] of Object.entries(props)) {
      defineLazyGetter(obj, toName, () => getModule()[fromName]);
    }
  }
}

/**
 * Defines a lazy getter on the given object which causes a module to be
 * lazily imported the first time it is accessed.
 *
 * @param {object} obj
 *        The target object on which to define the lazy getter.
 * @param {string} moduleId
 *        The ID of the module to require, as passed to require().
 * @param {string} [prop = moduleId]
 *        The name of the lazy getter property to define.
 */
function lazyRequireModule(obj, moduleId, prop = moduleId) {
  defineLazyGetter(obj, prop, () => this.require(moduleId));
}

/**
 * Create a CommonJS loader with the following options:
 * - createSandbox:
 *     A function that will be used to create sandboxes. It should take the name
 *     and prototype of the sandbox to be created, and return the newly created
 *     sandbox as result. This option is required.
 * - globals:
 *     A map of names to built-in globals that will be exposed to every module.
 *     Defaults to the empty map.
 * - loadSubScript:
 *     A function that will be used to load scripts in sandboxes. It should take
 *     the URL from and the sandbox in which the script is to be loaded, and not
 *     return a result. This option is required.
 * - modules:
 *     A map from normalized ids to built-in modules that will be added to the
 *     module cache. Defaults to the empty map.
 * - paths:
 *     A map of paths to base URLs that will be used to resolve relative URLs to
 *     absolute URLS. Defaults to the empty map.
 * - resolve:
 *     A function that will be used to resolve relative ids to absolute ids. It
 *     should take the relative id of a module to be required and the absolute
 *     id of the requiring module as arguments, and return the absolute id of
 *     the module to be required as result. Defaults to resolveId above.
 */
function WorkerDebuggerLoader(options) {
  /**
   * Convert the given relative URL to an absolute URL, using the map of paths
   * given below.
   *
   * @param String url
   *        The relative URL to be resolved.
   *
   * @return String
   *         An absolute URL.
   */
  function resolveURL(url) {
    let found = false;
    for (let [path, baseURL] of paths) {
      if (url.startsWith(path)) {
        found = true;
        url = url.replace(path, baseURL);
        break;
      }
    }
    if (!found) {
      throw new Error("Can't resolve relative URL '" + url + "'!");
    }

    // If the url has no extension, use ".js" by default.
    return url.endsWith(".js") ? url : url + ".js";
  }

  /**
   * Load the given module with the given url.
   *
   * @param Object module
   *        The module object to be loaded.
   * @param String url
   *        The URL to load the module from.
   */
  function loadModule(module, url) {
    // CommonJS specifies 3 free variables: require, exports, and module. These
    // must be exposed to every module, so define these as properties on the
    // sandbox prototype. Additional built-in globals are exposed by making
    // the map of built-in globals the prototype of the sandbox prototype.
    let prototype = Object.create(globals);
    prototype.Components = {};
    prototype.require = createRequire(module);
    prototype.exports = module.exports;
    prototype.module = module;

    let sandbox = createSandbox(url, prototype);
    try {
      loadSubScript(url, sandbox);
    } catch (error) {
      if (/^Error opening input stream/.test(String(error))) {
        throw new Error("Can't load module '" + module.id + "' with url '" +
                        url + "'!");
      }
      throw error;
    }

    // The value of exports may have been changed by the module script, so
    // freeze it if and only if it is still an object.
    if (typeof module.exports === "object" && module.exports !== null) {
      Object.freeze(module.exports);
    }
  }

  /**
   * Create a require function for the given module. If no module is given,
   * create a require function for the top-level module instead.
   *
   * @param Object requirer
   *        The module for which the require function is to be created.
   *
   * @return Function
   *         A require function for the given module.
   */
  function createRequire(requirer) {
    return function require(id) {
      // Make sure an id was passed.
      if (id === undefined) {
        throw new Error("Can't require module without id!");
      }

      // Built-in modules are cached by id rather than URL, so try to find the
      // module to be required by id first.
      let module = modules[id];
      if (module === undefined) {
        // Failed to find the module to be required by id, so convert the id to
        // a URL and try again.

        // If the id is relative, convert it to an absolute id.
        if (id.startsWith(".")) {
          if (requirer === undefined) {
            throw new Error("Can't require top-level module with relative id " +
                            "'" + id + "'!");
          }
          id = resolve(id, requirer.id);
        }

        // Convert the absolute id to a normalized id.
        id = normalizeId(id);

        // Convert the normalized id to a URL.
        let url = id;

        // If the URL is relative, resolve it to an absolute URL.
        if (url.match(/^\w+:\/\//) === null) {
          url = resolveURL(id);
        }

        // Try to find the module to be required by URL.
        module = modules[url];
        if (module === undefined) {
          // Failed to find the module to be required in the cache, so create
          // a new module, load it from the given URL, and add it to the cache.

          // Add modules to the cache early so that any recursive calls to
          // require for the same module will return the partially-loaded module
          // from the cache instead of triggering a new load.
          module = modules[url] = createModule(id);

          try {
            loadModule(module, url);
          } catch (error) {
            // If the module failed to load, remove it from the cache so that
            // subsequent calls to require for the same module will trigger a
            // new load, instead of returning a partially-loaded module from
            // the cache.
            delete modules[url];
            throw error;
          }

          Object.freeze(module);
        }
      }

      return module.exports;
    };
  }

  let createSandbox = options.createSandbox;
  let globals = options.globals || Object.create(null);
  let loadSubScript = options.loadSubScript;

  // Create the module cache, by converting each entry in the map from
  // normalized ids to built-in modules to a module object, with the exports
  // property of each module set to a frozen version of the original entry.
  let modules = options.modules || {};
  for (let id in modules) {
    let module = createModule(id);
    module.exports = Object.freeze(modules[id]);
    modules[id] = module;
  }

  // Convert the map of paths to base URLs into an array for use by resolveURL.
  // The array is sorted from longest to shortest path to ensure that the
  // longest path is always the first to be found.
  let paths = options.paths || Object.create(null);
  paths = Object.keys(paths)
                .sort((a, b) => b.length - a.length)
                .map(path => [path, paths[path]]);

  let resolve = options.resolve || resolveId;

  this.require = createRequire();
}

this.WorkerDebuggerLoader = WorkerDebuggerLoader;

// The following APIs rely on the use of Components, and the worker debugger
// does not provide alternative definitions for them. Consequently, they are
// stubbed out both on the main thread and worker threads.

var chrome = {
  CC: undefined,
  Cc: undefined,
  ChromeWorker: undefined,
  Cm: undefined,
  Ci: undefined,
  Cu: undefined,
  Cr: undefined,
  components: undefined
};

var loader = {
  lazyGetter: function (object, name, lambda) {
    Object.defineProperty(object, name, {
      get: function () {
        delete object[name];
        object[name] = lambda.apply(object);
        return object[name];
      },
      configurable: true,
      enumerable: true
    });
  },
  lazyImporter: function () {
    throw new Error("Can't import JSM from worker thread!");
  },
  lazyServiceGetter: function () {
    throw new Error("Can't import XPCOM service from worker thread!");
  },
  lazyRequireGetter: function (obj, property, module, destructure) {
    Object.defineProperty(obj, property, {
      get: () => destructure ? worker.require(module)[property]
                             : worker.require(module || property)
    });
  }
};

// The following APIs are defined differently depending on whether we are on the
// main thread or a worker thread. On the main thread, we use the Components
// object to implement them. On worker threads, we use the APIs provided by
// the worker debugger.

/* eslint-disable no-shadow */
var {
  Debugger,
  URL,
  createSandbox,
  dump,
  rpc,
  loadSubScript,
  reportError,
  setImmediate,
  xpcInspector,
} = (function () {
  // Main thread
  if (typeof Components === "object") {
    let {
      Constructor: CC,
      classes: Cc,
      interfaces: Ci,
      utils: Cu
    } = Components;

    let principal = CC("@mozilla.org/systemprincipal;1", "nsIPrincipal")();

    // To ensure that the this passed to addDebuggerToGlobal is a global, the
    // Debugger object needs to be defined in a sandbox.
    let sandbox = Cu.Sandbox(principal, {});
    Cu.evalInSandbox(
      "Components.utils.import('resource://gre/modules/jsdebugger.jsm');" +
      "addDebuggerToGlobal(this);",
      sandbox
    );
    let Debugger = sandbox.Debugger;

    let createSandbox = function (name, prototype) {
      return Cu.Sandbox(principal, {
        invisibleToDebugger: true,
        sandboxName: name,
        sandboxPrototype: prototype,
        wantComponents: false,
        wantXrays: false
      });
    };

    let rpc = undefined;

    // eslint-disable-next-line mozilla/use-services
    let subScriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
                 .getService(Ci.mozIJSSubScriptLoader);

    let loadSubScript = function (url, sandbox) {
      subScriptLoader.loadSubScript(url, sandbox, "UTF-8");
    };

    let reportError = Cu.reportError;

    let Timer = ChromeUtils.import("resource://gre/modules/Timer.jsm", {});

    let setImmediate = function (callback) {
      Timer.setTimeout(callback, 0);
    };

    let xpcInspector = Cc["@mozilla.org/jsinspector;1"]
                       .getService(Ci.nsIJSInspector);

    return {
      Debugger,
      URL: this.URL,
      createSandbox,
      dump: this.dump,
      rpc,
      loadSubScript,
      reportError,
      setImmediate,
      xpcInspector
    };
  }
  // Worker thread
  let requestors = [];

  let scope = this;

  let xpcInspector = {
    get eventLoopNestLevel() {
      return requestors.length;
    },

    get lastNestRequestor() {
      return requestors.length === 0 ? null : requestors[requestors.length - 1];
    },

    enterNestedEventLoop: function (requestor) {
      requestors.push(requestor);
      scope.enterEventLoop();
      return requestors.length;
    },

    exitNestedEventLoop: function () {
      requestors.pop();
      scope.leaveEventLoop();
      return requestors.length;
    }
  };

  return {
    Debugger: this.Debugger,
    URL: this.URL,
    createSandbox: this.createSandbox,
    dump: this.dump,
    rpc: this.rpc,
    loadSubScript: this.loadSubScript,
    reportError: this.reportError,
    setImmediate: this.setImmediate,
    xpcInspector: xpcInspector
  };
}).call(this);
/* eslint-enable no-shadow */

// Create the default instance of the worker loader, using the APIs we defined
// above.

this.worker = new WorkerDebuggerLoader({
  createSandbox: createSandbox,
  globals: {
    "isWorker": true,
    "dump": dump,
    "loader": loader,
    "reportError": reportError,
    "rpc": rpc,
    "URL": URL,
    "setImmediate": setImmediate,
    "lazyRequire": lazyRequire,
    "lazyRequireModule": lazyRequireModule,
    "retrieveConsoleEvents": this.retrieveConsoleEvents,
    "setConsoleEventHandler": this.setConsoleEventHandler,
  },
  loadSubScript: loadSubScript,
  modules: {
    "Debugger": Debugger,
    "Services": Object.create(null),
    "chrome": chrome,
    "xpcInspector": xpcInspector
  },
  paths: {
    // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
    "devtools": "resource://devtools",
    // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
    "promise": "resource://gre/modules/Promise-backend.js",
    // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
    "source-map": "resource://devtools/shared/sourcemap/source-map.js",
    // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
    "xpcshell-test": "resource://test"
    // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
  }
});
PK
!<to��MM8chrome/devtools/modules/devtools/shared/worker/worker.js/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If 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 ChromeWorker */

(function (factory) {
  if (this.module && module.id.includes("worker")) {
    // require
    const { Cc, Ci, Cu, ChromeWorker } = require("chrome");
    const dumpn = require("devtools/shared/DevToolsUtils").dumpn;
    factory.call(this, require, exports, module, { Cc, Ci, Cu }, ChromeWorker, dumpn);
  } else {
    // Cu.import
    const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
    this.isWorker = false;
    this.Promise = require("resource://gre/modules/Promise.jsm").Promise;
    this.console = console;
    factory.call(
      this, require, this, { exports: this },
      { Cc, Ci, Cu }, ChromeWorker, null
    );
    this.EXPORTED_SYMBOLS = ["DevToolsWorker"];
  }
}).call(this, function (require, exports, module, { Ci, Cc }, ChromeWorker, dumpn) {
  let MESSAGE_COUNTER = 0;

  /**
   * Creates a wrapper around a ChromeWorker, providing easy
   * communication to offload demanding tasks. The corresponding URL
   * must implement the interface provided by `devtools/shared/worker/helper`.
   *
   * @see `./devtools/client/shared/widgets/GraphsWorker.js`
   *
   * @param {string} url
   *        The URL of the worker.
   * @param Object opts
   *        An option with the following optional fields:
   *        - name: a name that will be printed with logs
   *        - verbose: log incoming and outgoing messages
   */
  function DevToolsWorker(url, opts) {
    opts = opts || {};
    this._worker = new ChromeWorker(url);
    this._verbose = opts.verbose;
    this._name = opts.name;

    this._worker.addEventListener("error", this.onError);
  }
  exports.DevToolsWorker = DevToolsWorker;

  /**
   * Performs the given task in a chrome worker, passing in data.
   * Returns a promise that resolves when the task is completed, resulting in
   * the return value of the task.
   *
   * @param {string} task
   *        The name of the task to execute in the worker.
   * @param {any} data
   *        Data to be passed into the task implemented by the worker.
   * @return {Promise}
   */
  DevToolsWorker.prototype.performTask = function (task, data) {
    if (this._destroyed) {
      return Promise.reject("Cannot call performTask on a destroyed DevToolsWorker");
    }
    let worker = this._worker;
    let id = ++MESSAGE_COUNTER;
    let payload = { task, id, data };

    if (this._verbose && dumpn) {
      dumpn("Sending message to worker" +
            (this._name ? (" (" + this._name + ")") : "") +
            ": " +
            JSON.stringify(payload, null, 2));
    }
    worker.postMessage(payload);

    return new Promise((resolve, reject) => {
      let listener = ({ data: result }) => {
        if (this._verbose && dumpn) {
          dumpn("Received message from worker" +
                (this._name ? (" (" + this._name + ")") : "") +
                ": " +
                JSON.stringify(result, null, 2));
        }

        if (result.id !== id) {
          return;
        }
        worker.removeEventListener("message", listener);
        if (result.error) {
          reject(result.error);
        } else {
          resolve(result.response);
        }
      };

      worker.addEventListener("message", listener);
    });
  };

  /**
   * Terminates the underlying worker. Use when no longer needing the worker.
   */
  DevToolsWorker.prototype.destroy = function () {
    this._worker.terminate();
    this._worker = null;
    this._destroyed = true;
  };

  DevToolsWorker.prototype.onError = function ({ message, filename, lineno }) {
    dump(new Error(message + " @ " + filename + ":" + lineno) + "\n");
  };

  /**
   * Takes a function and returns a Worker-wrapped version of the same function.
   * Returns a promise upon resolution.
   * @see `./devtools/shared/shared/tests/browser/browser_devtools-worker-03.js
   *
   * ⚠ This should only be used for tests or A/B testing performance ⚠
   *
   * The original function must:
   *
   * Be a pure function, that is, not use any variables not declared within the
   * function, or its arguments.
   *
   * Return a value or a promise.
   *
   * Note any state change in the worker will not affect the callee's context.
   *
   * @param {function} fn
   * @return {function}
   */
  function workerify(fn) {
    console.warn("`workerify` should only be used in tests or measuring performance. " +
                 "This creates an object URL on the browser window, and should not be " +
                 "used in production.");
    // Fetch modules here as we don't want to include it normally.
    const Services = require("Services");
    let { URL, Blob } = Services.wm.getMostRecentWindow("navigator:browser");
    let stringifiedFn = createWorkerString(fn);
    let blob = new Blob([stringifiedFn]);
    let url = URL.createObjectURL(blob);
    let worker = new DevToolsWorker(url);

    let wrapperFn = data => worker.performTask("workerifiedTask", data);

    wrapperFn.destroy = function () {
      URL.revokeObjectURL(url);
      worker.destroy();
    };

    return wrapperFn;
  }
  exports.workerify = workerify;

  /**
   * Takes a function, and stringifies it, attaching the worker-helper.js
   * boilerplate hooks.
   */
  function createWorkerString(fn) {
    return `importScripts("resource://gre/modules/workers/require.js");
            const { createTask } = require("resource://devtools/shared/worker/helper.js");
            createTask(self, "workerifiedTask", ${fn.toString()});`;
  }
});
PK
!<���I'I'"chrome/devtools/skin/animation.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* Animation-inspector specific theme variables */

:root {
  --animation-even-background-color: rgba(0, 0, 0, 0.05);
  --command-pick-image: url(chrome://devtools/skin/images/command-pick.svg);
  --fast-track-image: url("images/animation-fast-track.svg");
  --fill-color-cssanimation: var(--theme-contrast-background);
  --fill-color-csstransition: var(--theme-highlight-blue);
  --fill-color-scriptanimation: var(--theme-graphs-green);
  --graph-right-offset: 10px;
  --keyframe-marker-shadow-color: #c4c4c4;
  --sidebar-width: 200px;
  --stroke-color-cssanimation: var(--theme-highlight-lightorange);
  --stroke-color-csstransition: var(--theme-highlight-bluegrey);
  --stroke-color-scriptanimation: var(--theme-highlight-green);
  --tick-line-style: 0.5px solid rgba(128, 136, 144, 0.5);
}

:root.theme-dark {
  --animation-even-background-color: rgba(255, 255, 255, 0.05);
  --keyframe-marker-shadow-color: #818181;
}

:root.theme-firebug {
  --command-pick-image: url(chrome://devtools/skin/images/firebug/command-pick.svg);
}

/* Root element of animation inspector */
#animation-container {
  height: 100%;
}

#animation-container .uncontrolled {
  overflow: hidden;
}

#animation-container:not(.animation-detail-visible) .controlled {
  display: none;
}

/* Animation List Container */
.animation-list-container {
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow: hidden;
  width: 100%;
}

/* Animation List Header */
.animation-list-header {
  display: flex;
  justify-content: flex-end;
  padding: 0;
}

/* Animation Timeline Tick List */
.animation-timeline-tick-list {
  margin-right: var(--graph-right-offset);
  position: relative;
  width: calc(100% - var(--sidebar-width) - var(--graph-right-offset));
}

.animation-timeline-tick-item {
  border-left: var(--tick-line-style);
  height: 100vh;
  position: absolute;
}

/* Animation List */
.animation-list {
  flex: 1;
  list-style-type: none;
  margin: 0;
  overflow: auto;
  padding: 0;
}

/* Animation Item */
.animation-item {
  display: flex;
  height: 30px;
}

.animation-item:nth-child(2n+1) {
  background-color: var(--animation-even-background-color);
}

.animation-item.cssanimation {
  --computed-timing-graph-color: var(--fill-color-cssanimation);
  --effect-timing-graph-color: var(--stroke-color-cssanimation);
}

.animation-item.csstransition {
  --computed-timing-graph-color: var(--fill-color-csstransition);
  --effect-timing-graph-color: var(--stroke-color-csstransition);
}

.animation-item.scriptanimation {
  --computed-timing-graph-color: var(--fill-color-scriptanimation);
  --effect-timing-graph-color: var(--stroke-color-scriptanimation);
}

.animation-item.selected {
  background-color: var(--theme-selection-background-hover);
}

/* Animation Target */
.animation-target {
  align-items: center;
  display: flex;
  height: 100%;
  padding-left: 4px;
  width: var(--sidebar-width);
}

.animation-target .tag-name {
  cursor: default;
}

/* Summary Graph */
.animation-summary-graph {
  height: 100%;
  padding-top: 5px;
  position: relative;
  width: calc(100% - var(--sidebar-width) - var(--graph-right-offset));
}

.animation-summary-graph.compositor::after {
  background-image: var(--fast-track-image);
  background-repeat: no-repeat;
  content: "";
  display: block;
  fill: var(--theme-content-color3);
  height: 100%;
  position: absolute;
  right: 0;
  top: 5px;
  width: 15px;
  z-index: 1;
  -moz-context-properties: fill;
}

.animation-summary-graph-path {
  height: 100%;
  width: 100%;
}

.animation-computed-timing-path path {
  fill: var(--computed-timing-graph-color);
  vector-effect: non-scaling-stroke;
  transform: scale(1, -1);
}

.animation-computed-timing-path path.infinity:nth-child(n+2) {
  opacity: 0.3;
}

.animation-effect-timing-path path {
  fill: none;
  stroke: var(--effect-timing-graph-color);
  stroke-dasharray: 2px 2px;
  transform: scale(1, -1);
  vector-effect: non-scaling-stroke;
}

.animation-effect-timing-path path.infinity:nth-child(n+2) {
  opacity: 0.3;
}

.animation-negative-delay-path path,
.animation-negative-end-delay-path path {
  fill: none;
  stroke: var(--theme-graphs-grey);
  stroke-dasharray: 2px 2px;
  transform: scale(1, -1);
  vector-effect: non-scaling-stroke;
}

.animation-delay-sign,
.animation-end-delay-sign {
  background-color: var(--theme-graphs-grey);
  height: 3px;
  position: absolute;
  top: calc(100% - 1.5px);
}

.animation-delay-sign::before,
.animation-end-delay-sign::before {
  background-color: inherit;
  border-radius: 50%;
  content: "";
  height: 6px;
  position: absolute;
  top: -1.5px;
  width: 6px;
}

.animation-delay-sign.fill,
.animation-end-delay-sign.fill {
  background-color: var(--effect-timing-graph-color);
}

.animation-delay-sign.negative::before {
  left: unset;
  right: -3px;
}

.animation-end-delay-sign::before {
  right: -3px;
}

.animation-end-delay-sign.negative::before {
  left: -3px;
  right: unset;
}

.animation-name {
  height: 100%;
  left: 0;
  pointer-events: none;
  position: absolute;
  top: 0;
  width: calc(100% - 20px);
}

.animation-name text {
  dominant-baseline: middle;
  fill: var(--theme-focus-outline-color);
  paint-order: stroke;
  stroke: var(--theme-body-background);
  stroke-linejoin: round;
  stroke-opacity: .5;
  stroke-width: 4;
  text-anchor: end;
}

/* Animation Detail */
.animation-detail-container {
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow: hidden;
  width: 100%;
}

.animation-detail-header {
  display: flex;
}

.animation-detail-title {
  flex: 1;
  white-space: nowrap;
}

.animation-detail-close-button::before {
  background-image: url(chrome://devtools/skin/images/close.svg);
}

/* Animated Property List Container */
.animated-property-list-container {
  display: flex;
  flex: 1;
  flex-direction: column;
  overflow-y: auto;
}

/* Animated Property List Header */
.animated-property-list-header {
  display: flex;
  justify-content: flex-end;
  padding: 0;
}

/* Keyframes Progress Tick List */
.keyframes-progress-tick-list {
  margin-right: var(--graph-right-offset);
  position: absolute;
  width: calc(100% - var(--sidebar-width) - var(--graph-right-offset));
}

.keyframes-progress-tick-item {
  height: 100vh;
  position: absolute;
}

.keyframes-progress-tick-item.left {
  border-left: var(--tick-line-style);
}

.keyframes-progress-tick-item.right {
  border-right: var(--tick-line-style);
}

/* Animated Property List */
.animated-property-list {
  flex: 1;
  list-style-type: none;
  margin: 0;
  overflow-y: auto;
  padding: 0;
}

/* Animated Property Item */
.animated-property-item {
  display: flex;
  height: 30px;
}

.animated-property-item:nth-child(2n+1) {
  background-color: var(--animation-even-background-color);
}

/* Animated Property Name */
.animated-property-name {
  align-items: center;
  display: flex;
  height: 100%;
  justify-content: flex-end;
  padding-right: 10px;
  width: var(--sidebar-width);
}

.animated-property-name.compositor span {
  padding-left: 15px;
  position: relative;
}

.animated-property-list-container.cssanimation .animated-property-name.compositor {
  --fast-track-color: var(--stroke-color-cssanimation);
}

.animated-property-list-container.csstransition .animated-property-name.compositor {
  --fast-track-color: var(--stroke-color-csstransition);
}

.animated-property-list-container.scriptanimation .animated-property-name.compositor {
  --fast-track-color: var(--stroke-color-scriptanimation);
}

.animated-property-name.compositor span::before {
  background-image: var(--fast-track-image);
  background-repeat: no-repeat;
  background-size: contain;
  content: "";
  fill: var(--fast-track-color);
  height: 100%;
  position: absolute;
  left: 0;
  width: 15px;
  -moz-context-properties: fill;
}

.animated-property-name.warning span {
  text-decoration: underline dotted;
}

/* Keyframes Graph */
.keyframes-graph {
  height: 100%;
  padding-top: 3px;
  position: relative;
  width: calc(100% - var(--sidebar-width) - var(--graph-right-offset));
}

.keyframes-graph-path {
  height: 100%;
  width: 100%;
}

.keyframes-graph-path path {
  fill: #00b0bd88;
  stroke: #00b0bd;
  vector-effect: non-scaling-stroke;
  transform: scale(1, -1);
}

.keyframes-graph.opacity .keyframes-graph-path path {
  fill: #df00a988;
  stroke: #df00a9;
}

.keyframes-graph.transform .keyframes-graph-path path {
  fill: #ea800088;
  stroke: #ea8000;
}

.keyframes-graph-path .color-path path {
  stroke: none;
}

.keyframes-graph .keyframes-graph-path .hint path {
  fill: none;
  stroke-linecap: round;
  stroke-opacity: 0;
}

.keyframes-graph-path .hint path:hover {
  stroke-opacity: 1;
}

.keyframes-graph-path .hint rect {
  fill-opacity: 0.1;
  stroke: #00b0bd;
  stroke-opacity: 0;
  vector-effect: non-scaling-stroke;
}

.keyframes-graph-path .hint rect:hover {
  stroke-opacity: 1;
}

/* Keyframe Marker List */
.keyframe-marker-list {
  pointer-events: none;
  position: absolute;
  height: 100%;
  list-style-type: none;
  top: 0%;
  width: 100%;
}

.keyframe-marker-item {
  box-shadow: 0 0 0 1px var(--keyframe-marker-shadow-color);
  border-radius: 100%;
  pointer-events: auto;
  position: absolute;
  top: 50%;
  height: 10px;
  transform: translate(-5px, -3px);
  width: 10px;
}

.animated-property-list-container.cssanimation .keyframe-marker-item {
  background-color: var(--fill-color-cssanimation);
}

.animated-property-list-container.csstransition .keyframe-marker-item {
  background-color: var(--fill-color-csstransition);
}

.animated-property-list-container.scriptanimation .keyframe-marker-item {
  background-color: var(--fill-color-scriptanimation);
}

/* No Animation Panel */
.animation-error-message {
  overflow: auto;
}

.animation-error-message > p {
  white-space: pre;
}

.animation-element-picker::before {
  background-image: var(--command-pick-image);
}
PK
!<&
:%�Q�Q+chrome/devtools/skin/animationinspector.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* Animation-inspector specific theme variables */

.theme-dark {
  --even-animation-timeline-background-color: rgba(255,255,255,0.03);
  --command-pick-image: url(chrome://devtools/skin/images/command-pick.svg);
  --pause-image: url(chrome://devtools/skin/images/pause.svg);
  --rewind-image: url(chrome://devtools/skin/images/rewind.svg);
  --play-image: url(chrome://devtools/skin/images/play.svg);
  --close-button-image: url(chrome://devtools/skin/images/close.svg);
  /* The color for animation type 'opacity' */
  --opacity-border-color: var(--theme-highlight-pink);
  --opacity-background-color: #df80ff80;
  /* The color for animation type 'transform' */
  --transform-border-color: var(--theme-graphs-yellow);
  --transform-background-color: #d99b2880;
  /* The color for other animation type */
  --other-border-color: var(--theme-graphs-bluegrey);
  --other-background-color: #5e88b080;
  /* The color for progress indicator */
  --progress-indicator-color: var(--theme-highlight-gray);
}

.theme-light {
  --even-animation-timeline-background-color: rgba(128,128,128,0.03);
  --command-pick-image: url(chrome://devtools/skin/images/command-pick.svg);
  --pause-image: url(chrome://devtools/skin/images/pause.svg);
  --rewind-image: url(chrome://devtools/skin/images/rewind.svg);
  --play-image: url(chrome://devtools/skin/images/play.svg);
  --close-button-image: url(chrome://devtools/skin/images/close.svg);
}

.theme-firebug {
  --even-animation-timeline-background-color: rgba(128,128,128,0.03);
  --command-pick-image: url(chrome://devtools/skin/images/firebug/command-pick.svg);
  --pause-image: url(chrome://devtools/skin/images/firebug/pause.svg);
  --rewind-image: url(chrome://devtools/skin/images/firebug/rewind.svg);
  --play-image: url(chrome://devtools/skin/images/firebug/play.svg);
  --close-button-image: url(chrome://devtools/skin/images/firebug/close.svg);
}

.theme-light, .theme-firebug {
  /* The color for animation type 'opacity' */
  --opacity-border-color: var(--theme-highlight-pink);
  --opacity-background-color: #b82ee580;
  /* The color for animation type 'transform' */
  --transform-border-color: var(--theme-graphs-orange);
  --transform-background-color: #efc05280;
  /* The color for other animation type */
  --other-border-color: var(--theme-graphs-bluegrey);
  --other-background-color: #0072ab80;
  /* The color for progress indicator */
  --progress-indicator-color: gray;
}

:root {
  /* How high should toolbars be */
  --toolbar-height: 20px;
  /* How wide should the sidebar be (should be wide enough to contain long
     property names like 'border-bottom-right-radius' without ellipsis) */
  --timeline-sidebar-width: 200px;
  /* How high should animations displayed in the timeline be */
  --timeline-animation-height: 30px;
  /* How high should animated properties displayed in the details view be */
  --detail-animation-height: 20px;
  /* The size of a keyframe marker in the keyframes diagram */
  --keyframes-marker-size: 10px;
  /* The color of the time graduation borders */
  --time-graduation-border-color: rgba(128, 136, 144, .5);
}

.animation {
  --timeline-border-color: var(--theme-body-color);
  --timeline-background-color: var(--theme-splitter-color);
  /* The color of the endDelay hidden progress */
  --enddelay-hidden-progress-color: var(--theme-graphs-grey);
  /* The color of none fill mode */
  --fill-none-color: var(--theme-highlight-gray);
  /* The color of enable fill mode */
  --fill-enable-color: var(--timeline-border-color);
}

.animation.cssanimation {
  --timeline-border-color: var(--theme-highlight-lightorange);
  --timeline-background-color: var(--theme-contrast-background);
}

.animation.csstransition {
  --timeline-border-color: var(--theme-highlight-bluegrey);
  --timeline-background-color: var(--theme-highlight-blue);
}

.animation.scriptanimation {
  --timeline-border-color: var(--theme-highlight-green);
  --timeline-background-color: var(--theme-graphs-green);
}

html {
  height: 100%;
}

body {
  margin: 0;
  padding: 0;
  display : flex;
  flex-direction: column;
  height: 100%;
  overflow: hidden;
}

/* The top toolbar, containing the toggle-all button. And the timeline toolbar,
   containing playback control buttons, shown only when there are animations
   displayed in the timeline */

#global-toolbar,
#timeline-toolbar {
  border-bottom: 1px solid var(--theme-splitter-color);
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: flex-end;
  height: var(--toolbar-height);
}

#timeline-toolbar {
  display: none;
  justify-content: flex-start;
}

[timeline] #global-toolbar {
  display: none;
}

[timeline] #timeline-toolbar {
  display: flex;
}

/* The main animations container */

#players {
  height: calc(100% - var(--toolbar-height));
}

[empty] #players {
  display: none;
}

/* The error message, shown when an invalid/unanimated element is selected */

#error-message {
  flex: 1;
  overflow: auto;

  /* The error message is hidden by default */
  display: none;
}

[empty] #error-message {
  display: block;
}

/* Element picker, toggle-all buttons, timeline pause button, ... */

#global-toolbar > *,
#timeline-toolbar > * {
  min-height: var(--toolbar-height);
  border-color: var(--theme-splitter-color);
  border-width: 0 0 0 1px;
  display: flex;
  justify-content: center;
  align-items: center;
}

#global-toolbar .label,
#timeline-toolbar .label {
  padding: 0 5px;
  border-style: solid;
}

#global-toolbar .devtools-button,
#timeline-toolbar .devtools-button {
  margin: 0;
  padding: 0;
}

#timeline-toolbar .devtools-button,
#timeline-toolbar .label {
  border-width: 0 1px 0 0;
}

#element-picker::before {
  background-image: var(--command-pick-image);
}

.pause-button::before {
  background-image: var(--pause-image);
}

#rewind-timeline::before {
  background-image: var(--rewind-image);
}

.pause-button.paused::before {
  background-image: var(--play-image);
}

#timeline-rate select.devtools-button {
  -moz-appearance: none;
  text-align: center;
  font-family: inherit;
  color: var(--theme-body-color);
  font-size: 1em;
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-image: url("chrome://devtools/skin/images/dropmarker.svg");
  background-repeat: no-repeat;
  background-position: calc(100% - 4px) center;
  padding-right: 1em;
}

#timeline-rate {
  position: relative;
  width: 4.5em;
}

.animation-root > .uncontrolled {
  overflow: hidden;
}

/* Animation timeline component */

.animation-timeline {
  position: relative;
  width: 100%;
  overflow-y: auto;
  overflow-x: hidden;
}

/* Useful for positioning animations or keyframes in the timeline */
.animation-detail .track-container,
.animation-timeline .track-container {
  position: absolute;
  top: 0;
  left: var(--timeline-sidebar-width);
  /* Leave the width of a marker right of a track so the 100% markers can be
     selected easily */
  right: var(--keyframes-marker-size);
  height: var(--timeline-animation-height);
}

.animation-detail .track-container {
  height: var(--detail-animation-height);
}

.time-body.track-container {
  height: 0;
  pointer-events: none;
}

.animation-timeline .scrubber-wrapper {
  position: absolute;
  z-index: 5;
  left: var(--timeline-sidebar-width);
  /* Leave the width of a marker right of a track so the 100% markers can be
     selected easily */
  right: var(--keyframes-marker-size);
  pointer-events: none;
}

.animation-timeline .scrubber {
  pointer-events: none;
  position: absolute;
  width: 0;
  margin-left: -6px;
}

/* The scrubber handle is a transparent element displayed on top of the scrubber
   line that allows users to drag it */
.animation-timeline .scrubber .scrubber-handle {
  position: fixed;
  height: 100%;
  /* Make it thick enough for easy dragging */
  width: 12px;
  cursor: col-resize;
  pointer-events: all;
}

.animation-timeline .scrubber .scrubber-handle::before {
  content: "";
  position: absolute;
  top: 0;
  width: 1px;
  border-top: 5px solid red;
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
}

.animation-timeline .scrubber .scrubber-handle .scrubber-line {
  position: relative;
  height: 100%;
  left: 5px;
  width: 0;
  border-right: 1px solid red;
}

.animation-timeline .time-header {
  cursor: col-resize;
  -moz-user-select: none;
  height: 100%;
}

.animated-properties-header .header-item,
.animation-timeline .time-header .header-item {
  position: absolute;
  height: 100%;
  padding-top: 3px;
  border-left: 0.5px solid var(--time-graduation-border-color);
}

.animation-timeline .header-wrapper {
  position: sticky;
  top: 0;
  background-color: var(--theme-body-background);
  border-bottom: 1px solid var(--time-graduation-border-color);
  z-index: 3;
  height: var(--toolbar-height);
  width: 100%;
  overflow: hidden;
}

.animation-timeline .time-body {
  top: var(--toolbar-height);
  z-index: 3;
}

.progress-tick-container .progress-tick,
.animation-timeline .time-body .time-tick {
  -moz-user-select: none;
  position: absolute;
}

.progress-tick-container .progress-tick::before,
.animation-timeline .time-body .time-tick::before {
  content: "";
  position: fixed;
  height: 100vh;
  width: 0;
  border-left: 0.5px solid var(--time-graduation-border-color);
}

.animation-timeline .animations {
  position: relative;
  width: 100%;
  padding: 0;
  list-style-type: none;
  margin-top: 0;
}

/* Animation block widgets */

.animation-timeline .animation {
  margin: 2px 0;
  height: var(--timeline-animation-height);
  position: relative;
}

/* Display animations' background colors to alternate. */
.animation-timeline .animation:nth-child(2n+1) {
  background-color: var(--even-animation-timeline-background-color);
}

.animation-timeline .animation.selected {
  background-color: var(--theme-selection-background-hover);
}

.animation-timeline .animation:last-child {
  margin-bottom: calc(var(--timeline-animation-height) / 2);
}

.animation-timeline .animation .target {
  width: var(--timeline-sidebar-width);
  height: 100%;
  overflow: hidden;
  display: flex;
  align-items: center;
}

.animation-timeline .animation-target {
  background-color: transparent;
}

.animation-timeline .animation .time-block {
  cursor: pointer;
}

/* Animation summary graph */
.animation-timeline .animation .summary {
  position: absolute;
  width: 100%;
  height: 100%;
}

.animation-timeline .animation .summary .effect-easing path {
  fill: none;
  stroke: var(--timeline-border-color);
  stroke-dasharray: 2px 2px;
}

.animation-timeline .animation .summary .keyframes-easing path {
  fill: var(--timeline-background-color);
}

.animation-timeline .animation .summary .infinity.copied {
  opacity: .3;
}

.animation-timeline .animation .summary path.delay-path.negative,
.animation-timeline .animation .summary path.enddelay-path.negative {
  fill: none;
  stroke: var(--enddelay-hidden-progress-color);
  stroke-dasharray: 2, 2;
}

.animation-timeline .animation .name {
  position: absolute;
  top: 0px;
  left: 0px;
  height: 100%;
  width: 100%;
  --fast-track-icon-width: 15px;
}

.animation-timeline .animation .name svg {
  height: 100%;
  width: calc(100% - 20px);
}

.animation-timeline .animation .name text {
  fill: var(--theme-focus-outline-color);
  stroke: var(--theme-body-background);
  stroke-width: 4;
  stroke-opacity: .5;
  stroke-linejoin: round;
  paint-order: stroke;
  text-anchor: end;
  dominant-baseline: middle;
}

.animation-timeline .fast-track .name::after {
  /* Animations running on the compositor have the fast-track background image*/
  content: "";
  display: block;
  position: absolute;
  top: 5px;
  right: 0;
  height: 100%;
  width: var(--fast-track-icon-width);
  z-index: 1;
}

.animation-timeline .all-properties .name::after,
.animation-timeline .some-properties .name::after {
  -moz-context-properties: fill;
  fill: var(--theme-content-color3);
  background-image: url("images/animation-fast-track.svg");
  background-repeat: no-repeat;
}

.animation-timeline .animation .delay,
.animation-timeline .animation .end-delay {
  position: absolute;
  border-bottom: 3px solid var(--fill-none-color);
  bottom: -0.5px;
}

.animation-timeline .animation .delay::after,
.animation-timeline .animation .end-delay::after {
  content: "";
  position: absolute;
  top: -2px;
  width: 3px;
  height: 3px;
  border: 2px solid var(--fill-none-color);
  background-color: var(--fill-none-color);
  border-radius: 50%;
}

.animation-timeline .animation .negative.delay::after,
.animation-timeline .animation .positive.end-delay::after {
  right: -3px;
}

.animation-timeline .animation .positive.delay::after,
.animation-timeline .animation .negative.end-delay::after {
  left: -3px;
}

.animation-timeline .animation .fill.delay,
.animation-timeline .animation .fill.end-delay {
  border-color: var(--fill-enable-color);
}

.animation-timeline .animation .fill.delay::after,
.animation-timeline .animation .fill.end-delay::after {
  border-color: var(--fill-enable-color);
  background-color: var(--fill-enable-color);
}

/* Animation target node gutter, contains a preview of the dom node */
.animation-target {
  background-color: var(--theme-toolbar-background);
  padding: 0 4px;
  box-sizing: border-box;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  cursor: pointer;
}

.animation-target .attribute-name {
  padding-left: 4px;
}

.animation-target .node-highlighter {
  background: url("chrome://devtools/skin/images/vview-open-inspector.png") no-repeat 0 0;
  padding-left: 16px;
  margin-right: 5px;
  cursor: pointer;
}

.animation-target .node-highlighter:hover {
  filter: var(--theme-icon-checked-filter);
}

.animation-target .node-highlighter:active,
.animation-target .node-highlighter.selected {
  filter: var(--theme-icon-checked-filter) brightness(0.9);
}

/* Inline keyframes info in the timeline */

.animation-detail .animated-properties .property {
  height: var(--detail-animation-height);
  position: relative;
}

.animation-detail .animated-properties .property.unchanged {
  opacity: 0.6;
}

.animation-detail .animated-properties .property:nth-child(2n) {
  background-color: var(--even-animation-timeline-background-color);
}

.animation-detail .animated-properties .name {
  width: var(--timeline-sidebar-width);
  padding-right: var(--keyframes-marker-size);
  box-sizing: border-box;
  height: 100%;
  color: var(--theme-body-color-alt);
  white-space: nowrap;
  display: flex;
  justify-content: flex-end;
  align-items: center;
}

.animation-detail .animated-properties .name div {
  overflow: hidden;
  text-overflow: ellipsis;
}

.animation-detail .animated-properties.cssanimation {
  --background-color: var(--theme-contrast-background);
}

.animation-detail .animated-properties.csstransition {
  --background-color: var(--theme-highlight-blue);
}

.animation-detail .animated-properties.scriptanimation {
  --background-color: var(--theme-graphs-green);
}

.animation-detail .animated-properties .oncompositor::before {
  content: "";
  display: inline-block;
  width: 17px;
  height: 17px;
  -moz-context-properties: fill;
  fill: var(--background-color);
  background-image: url("images/animation-fast-track.svg");
  vertical-align: middle;
}

.animation-detail .animated-properties .warning {
  text-decoration: underline dotted;
}

.animation-detail .animated-properties .frames {
  /* The frames list is absolutely positioned and the left and width properties
     are dynamically set from javascript to match the animation's startTime and
     duration */
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  width: 100%;
  /* Using flexbox to vertically center the frames */
  display: flex;
  align-items: center;
}

/* Keyframes diagram, displayed below the timeline, inside the animation-details
   element. */

.keyframes {
  /* Actual keyframe markers are positioned absolutely within this container and
     their position is relative to its size (we know the offset of each frame
     in percentage) */
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
}

.keyframes .frame {
  position: absolute;
  top: 50%;
  width: 0;
  height: 0;
  background-color: inherit;
}

.keyframes .frame::before {
  content: "";
  display: block;
  transform:
    translateX(calc(var(--keyframes-marker-size) * -.5))
    /* The extra pixel on the Y axis is so that markers are centered on the
       horizontal line in the keyframes diagram. */
    translateY(calc(var(--keyframes-marker-size) * -.5 + 1px));
  width: var(--keyframes-marker-size);
  height: var(--keyframes-marker-size);
  border-radius: 100%;
  border: 1px solid var(--theme-highlight-gray);
  background-color: inherit;
}

.keyframes.cssanimation .frame {
  background-color: var(--theme-contrast-background);
}

.keyframes.csstransition .frame {
  background-color: var(--theme-highlight-blue);
}

.keyframes.scriptanimation .frame {
  background-color: var(--theme-graphs-green);
}

.keyframes svg {
  position: absolute;
  width: 100%;
  height: 100%;
}

.keyframes svg path {
  fill: var(--other-background-color);
  stroke: var(--other-border-color);
}

/* color of path is decided by the animation type */
.keyframes svg path.opacity {
  fill: var(--opacity-background-color);
  stroke: var(--opacity-border-color);
}

.keyframes svg path.transform {
  fill: var(--transform-background-color);
  stroke: var(--transform-border-color);
}

.keyframes svg path.color {
  stroke: none;
  height: 100%;
}

.keyframes svg .hint {
  stroke-opacity: 0;
  stroke-linecap: round;
  stroke-width: 5;
}

.keyframes svg path.hint {
  fill: none;
}

.keyframes svg path.hint:hover {
  stroke-opacity: 1;
}

.keyframes svg rect.hint {
  fill-opacity: .1;
}

.animation-detail {
  position: relative;
  width: 100%;
  background-color: var(--theme-body-background);
  z-index: 5;
}

.animation-detail .animation-detail-header {
  position: relative;
  height: var(--toolbar-height);
  width: 100%;
}

.animation-detail .animation-detail-header > div {
  position: fixed;
  display: flex;
  flex-wrap: nowrap;
  width: 100%;
  height: var(--toolbar-height);
  line-height: var(--toolbar-height);
  background-color: var(--theme-body-background);
  padding: 0;
  z-index: 5;
}

.animation-detail .animation-detail-header > div > div {
  white-space: nowrap;
}

.animation-detail .animation-detail-header > div > div:first-child {
  margin-left: 15px;
}

.animation-detail .animation-detail-header > div > div:nth-child(2) {
  flex: 1;
  margin-left: .5em;
  min-width: 0;
}

.animation-detail .animation-detail-header .devtools-button {
  /* We need to tweak the padding
     since the devtools-button is optimized for toolbox-tab height */
  padding-top: 0;
}

.animation-detail .animation-detail-header .devtools-button::before {
  background-image: var(--close-button-image);
}

.animation-detail .animation-detail-body {
  position: relative;
  background-color: var(--theme-body-background);
}

.animation-detail .animation-detail-body .animated-properties {
  position: relative;
  height: 100%;
}

.animated-properties-header {
  -moz-user-select: none;
  position: sticky;
  top: var(--toolbar-height);
  min-height: var(--toolbar-height);
  padding-top: 2px;
  z-index: 3;
  background-color: var(--theme-body-background);
}

.animated-properties-header .header-item:nth-child(2) {
  left: 50%;
}

.animated-properties-header .header-item:nth-child(3) {
  right: -0.5px;
  border-left: none;
  border-right: 0.5px solid var(--time-graduation-border-color);
}

.progress-tick-container .progress-tick:nth-child(2) {
  left: 50%;
}

.progress-tick-container .progress-tick:nth-child(3) {
  left: 100%;
}

.animated-properties-body .property:last-child {
  /* To display animation progress graph clealy when the scroll is bottom. */
  padding-bottom: calc(var(--detail-animation-height) / 2);
}

.animated-properties .progress-indicator-wrapper {
  pointer-events: none;
  z-index: 5;
}

.progress-indicator-wrapper .progress-indicator {
  position: absolute;
  pointer-events: none;
}

.progress-indicator-wrapper .progress-indicator .progress-indicator-shape {
  position: fixed;
  width: 0;
  height: 100vh;
  border-right: 1px solid var(--progress-indicator-color);
}

.progress-indicator-wrapper .progress-indicator .progress-indicator-shape::before {
  content: "";
  position: absolute;
  top: 0;
  right: -6px;
  width: 1px;
  border-top: 5px solid var(--progress-indicator-color);
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
}

.animation-root:not(.animation-detail-visible) .controlled {
  display: none;
}
PK
!<����!chrome/devtools/skin/boxmodel.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 stylesheet of the Box Model view implemented in the layout panel.
 */

.boxmodel-container {
  overflow: auto;
  padding-bottom: 4px;
  max-width: 600px;
  margin: 0 auto;
}

/* Header */

.boxmodel-header,
.boxmodel-info {
  display: flex;
  align-items: center;
  padding: 4px 17px;
}

.layout-geometry-editor::before {
  background: url(images/geometry-editor.svg) no-repeat center center / 16px 16px;
}

/* Main: contains the box-model regions */

.boxmodel-main {
  position: relative;
  color: var(--theme-selection-color);
  /* Make sure there is some space between the window's edges and the regions */
  margin: 14px auto;
  width: calc(100% - 2 * 14px);
  min-width: 240px;
  /* The view will grow bigger as the window gets resized, until 400px */
  max-width: 400px;
}

.boxmodel-box {
  margin: 25px;
  /* The regions are semi-transparent, so the white background is partly
     visible */
  background-color: white;
}

.boxmodel-margin,
.boxmodel-size {
  color: var(--theme-highlight-blue);
}

.theme-dark .boxmodel-margin,
.theme-dark .boxmodel-size {
  color: var(--grey-60);
}

/* Regions are 3 nested elements with wide borders and outlines */

.boxmodel-contents {
  height: 18px;
}

.boxmodel-margins,
.boxmodel-borders,
.boxmodel-paddings {
  border-color: hsla(210,100%,85%,0.2);
  border-width: 18px;
  border-style: solid;
  outline: dotted 1px hsl(210,100%,85%);
}

.boxmodel-margins {
  /* This opacity applies to all of the regions, since they are nested */
  opacity: .8;
}

/* Regions colors */

.boxmodel-margins {
  border-color: #edff64;
}

.boxmodel-borders {
  border-color: #444444;
}

.boxmodel-paddings {
  border-color: #6a5acd;
}

.boxmodel-contents {
  background-color: #87ceeb;
}

.theme-firebug .boxmodel-main,
.theme-firebug .boxmodel-header {
  font-family: var(--proportional-font-family);
}

.theme-firebug .boxmodel-main {
  color: var(--theme-body-color);
  font-size: var(--theme-toolbar-font-size);
}

.theme-firebug .boxmodel-header {
  font-size: var(--theme-toolbar-font-size);
}

/* Editable region sizes are contained in absolutely positioned <p> */

.boxmodel-main > p,
.boxmodel-size {
  position: absolute;
  pointer-events: none;
  margin: 0;
  text-align: center;
}

.boxmodel-main > p > span,
.boxmodel-main > p > input,
.boxmodel-content,
.boxmodel-size > span {
  vertical-align: middle;
  pointer-events: auto;
}

/* Coordinates for the region sizes */

.boxmodel-top,
.boxmodel-bottom {
  width: calc(100% - 2px);
  text-align: center;
}

.boxmodel-padding.boxmodel-top {
  top: 37px;
}

.boxmodel-padding.boxmodel-bottom {
  bottom: 38px;
}

.boxmodel-border.boxmodel-top {
  top: 19px;
}

.boxmodel-border.boxmodel-bottom {
  bottom: 20px;
}

.boxmodel-margin.boxmodel-top {
  top: 1px;
}

.boxmodel-margin.boxmodel-bottom {
  bottom: 2px;
}

.boxmodel-size,
.boxmodel-position.boxmodel-left,
.boxmodel-position.boxmodel-right,
.boxmodel-margin.boxmodel-left,
.boxmodel-margin.boxmodel-right,
.boxmodel-border.boxmodel-left,
.boxmodel-border.boxmodel-right,
.boxmodel-padding.boxmodel-left,
.boxmodel-padding.boxmodel-right {
  top: 22px;
  line-height: 80px;
}

.boxmodel-size {
  width: calc(100% - 2px);
}

.boxmodel-position.boxmodel-top,
.boxmodel-position.boxmodel-bottom,
.boxmodel-position.boxmodel-left,
.boxmodel-position.boxmodel-right,
.boxmodel-margin.boxmodel-right,
.boxmodel-margin.boxmodel-left,
.boxmodel-border.boxmodel-right,
.boxmodel-border.boxmodel-left,
.boxmodel-padding.boxmodel-right,
.boxmodel-padding.boxmodel-left {
  width: 21px;
}

.boxmodel-padding.boxmodel-left {
  left: 60px;
}

.boxmodel-padding.boxmodel-right {
  right: 60px;
}

.boxmodel-border.boxmodel-left {
  left: 41px;
}

.boxmodel-border.boxmodel-right {
  right: 42px;
}

.boxmodel-margin.boxmodel-right {
  right: 25px;
}

.boxmodel-margin.boxmodel-left {
  left: 25px;
}

.boxmodel-rotate.boxmodel-left:not(.boxmodel-editing) {
  transform: rotate(-90deg);
}

.boxmodel-rotate.boxmodel-right:not(.boxmodel-editing) {
  transform: rotate(90deg);
}

.boxmodel-rotate.boxmodel-left.boxmodel-position:not(.boxmodel-editing) {
  border-top: none;
  border-right: 1px solid var(--theme-highlight-purple);
  width: auto;
  height: 30px;
}

.boxmodel-size > p {
  display: inline-block;
  margin: auto;
  line-height: 0;
}

.boxmodel-rotate.boxmodel-right.boxmodel-position:not(.boxmodel-editing) {
  border-top: none;
  border-left: 1px solid var(--theme-highlight-purple);
  width: auto;
  height: 30px;
}

/* Box Model Positioning: contains top, right, bottom, left */

.boxmodel-position {
  color: var(--theme-highlight-purple);
}

.boxmodel-position.boxmodel-top,
.boxmodel-position.boxmodel-bottom {
  border-left: 1px solid var(--theme-highlight-purple);
  left: calc(50% - 2px);
  padding-left: 1px;
}

.boxmodel-position.boxmodel-right,
.boxmodel-position.boxmodel-left {
  border-top: 1px solid var(--theme-highlight-purple);
  line-height: 15px;
  top: calc(50% - 1px);
  width: 30px;
}

.boxmodel-position.boxmodel-top {
  top: -18px;
}

.boxmodel-position.boxmodel-right {
  right: -9px;
}

.boxmodel-position.boxmodel-bottom {
  bottom: -18px;
}

.boxmodel-position.boxmodel-left {
  left: -9px;
}

/* Legend: displayed inside regions */

.boxmodel-legend {
  position: absolute;
  margin: 2px 6px;
  z-index: 1;
}

.boxmodel-legend[data-box="margin"] {
  color: var(--theme-highlight-blue);
}

.theme-dark .boxmodel-legend[data-box="margin"] {
  color: var(--grey-60);
}

.boxmodel-legend[data-box="position"] {
  color: var(--theme-highlight-purple);
  margin: -18px -9px;
}

/* Editable fields */

.boxmodel-editable {
  border: 1px dashed transparent;
  -moz-user-select: none;
  white-space: nowrap;
}

.boxmodel-editable:hover {
  border-bottom-color: hsl(0, 0%, 50%);
}

.boxmodel-size > span {
  cursor: default;
}

/* Box Model Info: contains the position and size of the element */

.boxmodel-element-size {
  flex: 1;
}

.boxmodel-position-group {
  display: flex;
  align-items: center;
}

/* Box Model Properties: contains a list of relevant box model properties */

.boxmodel-properties-header {
  padding: 2px 3px;
}

.boxmodel-properties-expander {
  vertical-align: middle;
  display: inline-block;
}

.boxmodel-properties-wrapper {
  column-width: 250px;
  column-gap: 20px;
  column-rule: 1px solid var(--theme-splitter-color);
}

.boxmodel-properties-wrapper .computed-property-view {
  padding-inline-start: 17px;
}

.boxmodel-properties-wrapper .computed-property-name-container {
  flex: 1;
}

.boxmodel-properties-wrapper .computed-property-value-container {
  flex: 1;
  display: block;
}

.boxmodel-container .reference-element {
  margin-inline-start: 14px;
  margin-block-start: 4px;
  display: block;
}

/* Tag displayed next to DOM Node previews (used to display reference elements) */

.boxmodel-container .reference-element-type {
  background: var(--theme-highlight-purple);
  color: white;
  padding: 1px 2px;
  border-radius: 2px;
  font-size: 9px;
  margin-inline-end: 5px;
}

.theme-dark .boxmodel-container .reference-element-type {
  color: black;
}

/* Box Model Main - Offset Parent */

.boxmodel-offset-parent {
  position: absolute;
  top: -20px;
  right: -10px;
  color: var(--theme-highlight-purple);
}
PK
!<�{�5��$chrome/devtools/skin/breadcrumbs.css/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 {
  --breadcrumb-id-class-color: #909090;
 }

 .theme-dark:root {
  --breadcrumb-id-class-color: var(--theme-content-color1);
 }

/* Inspector HTMLBreadcrumbs */

.breadcrumbs-widget-container {
  margin-inline-end: 3px;
  max-height: 24px; /* Set max-height for proper sizing on linux */
  height: 24px; /* Set height to prevent starting small waiting for content */
}

.scrollbutton-up,
.scrollbutton-down {
  -moz-appearance: none;
  background: transparent;
  box-shadow: none;
  border: none;
  list-style-image: none;
  margin: 0;
  padding: 0;
}

.scrollbutton-up > .toolbarbutton-icon,
.scrollbutton-down > .toolbarbutton-icon {
  -moz-appearance: none;
  width: 20px;
  height: 16px;
  background-size: 16px;
  background-position: center;
  background-repeat: no-repeat;
  background-image: url("images/breadcrumbs-scrollbutton.svg");
  list-style-image: none;
  padding: 0;

  -moz-context-properties: fill;
  fill: var(--theme-toolbar-color);
}

.scrollbutton-up:not([disabled]):active:hover,
.scrollbutton-down:not([disabled]):active:hover {
  background-color: var(--theme-toolbar-hover);
}

.scrollbutton-up[disabled] > .toolbarbutton-icon,
.scrollbutton-down[disabled] > .toolbarbutton-icon {
  opacity: 0.5;
}

/* Draw shadows to indicate there is more content 'behind' scrollbuttons. */
.scrollbutton-up:-moz-locale-dir(ltr):not(:dir(rtl)),
.scrollbutton-up:dir(ltr),
.scrollbutton-down:-moz-locale-dir(rtl),
.scrollbutton-down:dir(rtl) {
  border-right: solid 1px rgba(255, 255, 255, .1);
  border-left: solid 1px transparent;
  box-shadow: 3px 0px 3px -3px var(--theme-sidebar-background);
}

.scrollbutton-down:-moz-locale-dir(ltr):not(:dir(rtl)),
.scrollbutton-down:dir(ltr),
.scrollbutton-up:-moz-locale-dir(rtl),
.scrollbutton-up:dir(rtl) {
  border-right: solid 1px transparent;
  border-left: solid 1px rgba(255, 255, 255, .1);
  box-shadow: -3px 0px 3px -3px var(--theme-sidebar-background);
}

.scrollbutton-up[disabled],
.scrollbutton-down[disabled] {
  box-shadow: none;
  border-color: transparent;
}

.scrollbutton-up > .toolbarbutton-icon:-moz-locale-dir(rtl),
.scrollbutton-up > .toolbarbutton-icon:dir(rtl),
.scrollbutton-down > .toolbarbutton-icon:-moz-locale-dir(ltr):not(:dir(rtl)),
.scrollbutton-down > .toolbarbutton-icon:dir(ltr) {
  transform: scaleX(-1);
}

.breadcrumbs-widget-item {
  background-color: transparent;
  border: none;
  margin-inline-start: 10px;
  margin-inline-end: 1px;
  padding: 0 0 2px 0;
}

.breadcrumbs-widget-item > .button-box {
  border: none;
  padding-top: 0;
  padding-bottom: 0;
}

:root[platform="win"] .breadcrumbs-widget-item:-moz-focusring > .button-box {
  border-width: 0;
}

#debugger-toolbar .breadcrumbs-widget-item::before {
  --background-color: var(--theme-toolbar-background);
}

.breadcrumbs-widget-item:first-child::before {
  /* The first crumb does not need any separator before itself */
  content: unset;
}

.breadcrumbs-widget-item:dir(rtl)::before {
  transform: scaleX(-1);
}

/* RTL support: move the images that were on the left to the right,
 * and move images that were on the right to the left.
 */
.breadcrumbs-widget-item:dir(rtl) {
  padding: 0 20px 0 8px;
}

.breadcrumbs-widget-item:dir(rtl),
.breadcrumbs-widget-item[checked] + .breadcrumbs-widget-item:dir(rtl) {
  background-position: center right;
}

.breadcrumbs-widget-item:not(:first-child)::before {
  content: url(chrome://devtools/skin/images/breadcrumbs-divider.svg);
  background: none;
  position: relative;
  left: -3px;
  margin: 0 4px 0 -1px;
  top: -1px;
}

.breadcrumbs-widget-item[checked] .breadcrumbs-widget-item-id {
  color: var(--theme-highlight-purple);
}

.breadcrumbs-widget-item[checked] .breadcrumbs-widget-item-tag,
.breadcrumbs-widget-item[checked] .breadcrumbs-widget-item-pseudo-classes,
.breadcrumbs-widget-item[checked] .breadcrumbs-widget-item-classes {
  color: var(--theme-highlight-blue);
}

.theme-dark .breadcrumbs-widget-item {
  color: var(--theme-selection-color);
}

.theme-light .breadcrumbs-widget-item {
  color: var(--theme-body-color);
}

.breadcrumbs-widget-item-id,
.breadcrumbs-widget-item-classes {
  color: var(--breadcrumb-id-class-color);
}

.breadcrumbs-widget-item-pseudo-classes {
  color: var(--theme-highlight-lightorange);
}

/* Firebug theme support for breadcrumbs widget. */

.theme-firebug .breadcrumbs-widget-item {
  border: 1px solid transparent;
  border-radius: 2px;
  padding: 0;
}

.theme-firebug .breadcrumbs-widget-item:hover {
  border-color: rgba(0, 0, 0, 0.2);
  box-shadow: 1px 1px 1px rgba(255, 255, 255, 0.6) inset,
              0 0 1px rgba(255, 255, 255, 0.6) inset,
              0 0 2px rgba(0, 0, 0, 0.05);
}

.theme-firebug .breadcrumbs-widget-item > .button-box {
  padding-left: 0;
  padding-right: 0;
}

.theme-firebug .breadcrumbs-widget-item:first-child {
  margin: 0;
}

.theme-firebug .breadcrumbs-widget-item:not(:first-child)::before {
  margin: 0 0 0 -5px;
  top: 0;
}

/* Breadcrumbs Separators (reset selection styles) */
.theme-firebug .breadcrumbs-widget-item[checked],
.theme-firebug .breadcrumbs-widget-item[checked] .breadcrumbs-widget-item-id,
.theme-firebug .breadcrumbs-widget-item[checked] .breadcrumbs-widget-item-tag,
.theme-firebug .breadcrumbs-widget-item[checked] .breadcrumbs-widget-item-pseudo-classes,
.theme-firebug .breadcrumbs-widget-item[checked] .breadcrumbs-widget-item-classes {
  background: none;
  font-weight: bold;
  color: inherit;
}

/* The first rule is there only to make sure the default rule from
widgets.css is overwritten. */
.theme-firebug .breadcrumbs-widget-item[checked] + .breadcrumbs-widget-item {
  background: none;
}

.theme-firebug .breadcrumbs-widget-item .breadcrumbs-widget-item-tag {
  padding-left: 4px;
  padding-right: 4px;
}

/* Breadcrumbs Scrolling Buttons */

.theme-firebug .breadcrumbs-widget-container .scrollbutton-up,
.theme-firebug .breadcrumbs-widget-container .scrollbutton-down {
  padding: 0;
  box-shadow: none;
  outline: 1px solid var(--theme-splitter-color);
}

.theme-firebug .breadcrumbs-widget-container .scrollbutton-up:hover,
.theme-firebug .breadcrumbs-widget-container .scrollbutton-down:hover {
  border: 1px transparent solid !important;
  box-shadow: none !important;
}

.theme-firebug .breadcrumbs-widget-container .scrollbutton-up:active,
.theme-firebug .breadcrumbs-widget-container .scrollbutton-down:active {
  background: none !important;
}
PK
!<3_���'chrome/devtools/skin/canvasdebugger.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 {
  --gutter-width: 3em;
  --gutter-padding-start: 22px;
  --checkerboard-pattern: linear-gradient(45deg, rgba(128,128,128,0.2) 25%, transparent 25%, transparent 75%, rgba(128,128,128,0.2) 75%, rgba(128,128,128,0.2)),
                          linear-gradient(45deg, rgba(128,128,128,0.2) 25%, transparent 25%, transparent 75%, rgba(128,128,128,0.2) 75%, rgba(128,128,128,0.2));
}

:root.theme-dark {
  --draw-call-background: rgba(112,191,83,0.15);
  --interesting-call-background: rgba(223,128,255,0.15);
}

:root.theme-light {
  --draw-call-background: rgba(44,187,15,0.1);
  --interesting-call-background: rgba(184,46,229,0.1);
}

/* Reload and waiting notices */

.notice-container {
  margin-top: -50vh;
  color: var(--theme-body-color-alt);
}

#empty-notice > button {
  min-width: 30px;
  min-height: 28px;
  margin: 0;
  list-style-image: url(images/profiler-stopwatch.svg);
}

#empty-notice > button .button-text {
  display: none;
}

#waiting-notice {
  font-size: 110%;
}

/* Snapshots pane */

#snapshots-pane {
  border-inline-end: 1px solid var(--theme-splitter-color);
}

#record-snapshot {
  list-style-image: url("chrome://devtools/skin/images/profiler-stopwatch.svg");
}

#import-snapshot {
  list-style-image: url("images/import.svg");
}

/* Snapshots items */

.snapshot-item-thumbnail {
  image-rendering: -moz-crisp-edges;
  background-image: var(--checkerboard-pattern);
  background-size: 12px 12px, 12px 12px;
  background-position: 0px 0px, 6px 6px;
  background-repeat: repeat, repeat;
}

.snapshot-item-thumbnail[flipped=true] {
  transform: scaleY(-1);
}

.snapshot-item-thumbnail {
  background-color: var(--theme-body-background);
}

.snapshot-item-details {
  padding-inline-start: 6px;
}

.snapshot-item-calls {
  padding-top: 4px;
  font-size: 80%;
}

.snapshot-item-save {
  padding-bottom: 2px;
  font-size: 90%;
}

.snapshot-item-calls,
.snapshot-item-save {
  color: var(--theme-body-color-alt);
}

.snapshot-item-save {
  text-decoration: underline;
  cursor: pointer;
}

.snapshot-item-save[disabled=true] {
  text-decoration: none;
  pointer-events: none;
}

.snapshot-item-footer.devtools-throbber::before {
  margin-top: -2px;
}

#snapshots-list .selected label {
  /* Text inside a selected item should not be custom colored. */
  color: inherit !important;
}

/* Debugging pane controls */
#resume {
  list-style-image: url(images/play.svg);
}

#step-over {
  list-style-image: url(images/debugger-step-over.svg);
}

#step-in {
  list-style-image: url(images/debugger-step-in.svg);
}

#step-out {
  list-style-image: url(images/debugger-step-out.svg);
}

#calls-slider {
  padding-inline-end: 24px;
}

#calls-slider .scale-slider {
  margin: 0;
}

#debugging-toolbar-sizer-button {
  /* This button's only purpose in life is to make the
     container .devtools-toolbar have the right height. */
  visibility: hidden;
  min-width: 1px;
}

/* Calls list pane */

#calls-list .side-menu-widget-container {
  background: transparent;
}

/* Calls list items */

#calls-list .side-menu-widget-item {
  padding: 0;
  border-color: var(--theme-splitter-color);
  border-bottom-color: transparent;
}

.call-item-view:hover {
  background-color: rgba(128,128,128,0.05);
}

.call-item-view[draw-call] {
  background-color: var(--draw-call-background);
}

.call-item-view[interesting-call] {
  background-color: var(--interesting-call-background);
}

.call-item-gutter {
  width: calc(var(--gutter-width) + var(--gutter-padding-start));
  padding-inline-start: var(--gutter-padding-start);
  padding-inline-end: 4px;
  padding-top: 2px;
  padding-bottom: 2px;
  border-inline-end: 1px solid var(--theme-splitter-color);
  margin-inline-end: 6px;
  background-color: var(--theme-sidebar-background);
  color: var(--theme-content-color3);
}

.selected .call-item-gutter {
  background-color: #2cbb0f;
  color: white;
}

.call-item-index {
  text-align: end;
}

.call-item-context {
  color: var(--theme-highlight-orange);
}

.call-item-name {
  color: var(--theme-highlight-blue);
}

.call-item-location {
  padding-inline-start: 2px;
  padding-inline-end: 6px;
  text-align: end;
  cursor: pointer;
  color: var(--theme-highlight-bluegrey);
  border-color: var(--theme-splitter-color);
}

.call-item-location:hover {
  color: var(--theme-highlight-blue);
}

.call-item-view:hover .call-item-location,
.call-item-view[expanded] .call-item-location {
  text-decoration: underline;
}

.call-item-stack {
  padding-inline-start: calc(var(--gutter-width) + var(--gutter-padding-start));
  padding-bottom: 10px;
}

.theme-dark .call-item-stack {
  background: rgba(0,0,0,0.9);
}

.theme-light .call-item-stack {
  background: rgba(255,255,255,0.9);
}

.call-item-stack-fn {
  padding-top: 2px;
  padding-bottom: 2px;
}

.call-item-stack-fn-location {
  padding-inline-start: 2px;
  padding-inline-end: 6px;
  text-align: end;
  cursor: pointer;
  text-decoration: underline;
}

.call-item-stack-fn-name {
  color: var(--theme-content-color3);
}

.call-item-stack-fn-location {
  color: var(--theme-highlight-bluegrey);
}

.call-item-stack-fn-location:hover {
  color: var(--theme-highlight-blue);
}

#calls-list .selected .call-item-contents > label:not(.call-item-gutter) {
  /* Text inside a selected item should not be custom colored. */
  color: inherit !important;
}

/* Rendering preview */

#screenshot-container {
  background-color: var(--theme-body-background);
  background-image: var(--checkerboard-pattern);
  background-size: 30px 30px, 30px 30px;
  background-position: 0px 0px, 15px 15px;
  background-repeat: repeat, repeat;
}

@media (min-width: 701px) {
  #screenshot-container {
    width: 30vw;
    max-width: 50vw;
    min-width: 100px;
  }
}

@media (max-width: 700px) {
  #screenshot-container {
    height: 40vh;
    max-height: 70vh;
    min-height: 100px;
  }
}

#screenshot-image {
  background-image: -moz-element(#screenshot-rendering);
  background-size: contain;
  background-position: center, center;
  background-repeat: no-repeat;
}

#screenshot-image[flipped=true] {
  transform: scaleY(-1);
}

#screenshot-dimensions {
  padding-top: 4px;
  padding-bottom: 4px;
  text-align: center;
}

.theme-dark #screenshot-dimensions {
  background-color: rgba(0,0,0,0.4);
}

.theme-light #screenshot-dimensions {
  background-color: rgba(255,255,255,0.8);
}

/* Snapshot filmstrip */

#snapshot-filmstrip {
  border-top: 1px solid var(--theme-splitter-color);
  overflow: hidden;
}

.theme-dark #snapshot-filmstrip {
  color: var(--theme-selection-color);
}

.theme-light #snapshot-filmstrip {
  color: var(--theme-body-color-alt);
}

.filmstrip-thumbnail {
  image-rendering: -moz-crisp-edges;
  background-color: var(--theme-body-background);
  background-image: var(--checkerboard-pattern);
  background-size: 12px 12px, 12px 12px;
  background-position: 0px -1px, 6px 5px;
  background-repeat: repeat, repeat;
  background-origin: content-box;
  cursor: pointer;
  padding-top: 1px;
  padding-bottom: 1px;
  border-inline-end: 1px solid var(--theme-splitter-color);
  transition: opacity 0.1s ease-in-out;
}

.filmstrip-thumbnail[flipped=true] {
  transform: scaleY(-1);
}

#snapshot-filmstrip > .filmstrip-thumbnail:hover,
#snapshot-filmstrip:not(:hover) > .filmstrip-thumbnail[highlighted] {
  border: 1px solid var(--theme-highlight-blue);
  margin: 0 0 0 -1px;
  padding: 0;
  opacity: 0.66;
}
PK
!<���	
	
chrome/devtools/skin/chart.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* Chart */

.generic-chart-container {
  /* Hack: force hardware acceleration */
  transform: translateZ(1px);
}

.theme-dark .generic-chart-container {
  color: var(--theme-selection-color);
}

.theme-light .generic-chart-container {
  color: var(--theme-body-color-alt);
}

.chart-colored-blob {
  fill: var(--theme-content-color2);
  background: var(--theme-content-color2);
}

/* Chart: Pie */

.pie-chart-slice {
  stroke-width: 1px;
  cursor: pointer;
}

.theme-dark .pie-chart-slice {
  stroke: rgba(0,0,0,0.2);
}

.theme-light .pie-chart-slice {
  stroke: rgba(255,255,255,0.8);
}

.theme-dark .pie-chart-slice[largest] {
  stroke-width: 2px;
  stroke: #fff;
}

.theme-light .pie-chart-slice[largest] {
  stroke: #000;
}

.pie-chart-label {
  text-anchor: middle;
  dominant-baseline: middle;
  pointer-events: none;
}

.theme-dark .pie-chart-label {
  fill: #000;
}

.theme-light .pie-chart-label {
  fill: #fff;
}

.pie-chart-container[slices="1"] > .pie-chart-slice {
  stroke-width: 0px;
}

.pie-chart-slice,
.pie-chart-label {
  transition: all 0.1s ease-out;
}

.pie-chart-slice:not(:hover):not([focused]),
.pie-chart-slice:not(:hover):not([focused]) + .pie-chart-label {
  transform: none !important;
}

/* Chart: Table */

.table-chart-title {
  padding-bottom: 10px;
  font-size: 120%;
  font-weight: 600;
}

.table-chart-row {
  margin-top: 1px;
  cursor: pointer;
}

.table-chart-grid:hover > .table-chart-row {
  transition: opacity 0.1s ease-in-out;
}

.table-chart-grid:not(:hover) > .table-chart-row {
  transition: opacity 0.2s ease-in-out;
}

.generic-chart-container:hover > .table-chart-grid:hover > .table-chart-row:not(:hover),
.generic-chart-container:hover ~ .table-chart-container > .table-chart-grid > .table-chart-row:not([focused]) {
  opacity: 0.4;
}

.table-chart-row-box {
  width: 8px;
  height: 1.5em;
  margin-inline-end: 10px;
}

.table-chart-row-label {
  width: 8em;
  padding-inline-end: 6px;
  cursor: inherit;
}

.table-chart-totals {
  margin-top: 8px;
  padding-top: 6px;
}

.table-chart-totals {
  border-top: 1px solid var(--theme-body-color-alt); /* Grey foreground text */
}

.table-chart-summary-label {
  font-weight: 600;
  padding: 1px 0px;
}

.theme-dark .table-chart-summary-label {
  color: var(--theme-selection-color);
}

.theme-light .table-chart-summary-label {
  color: var(--theme-body-color);
}
PK
!<��0��$chrome/devtools/skin/commandline.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 {
  overflow-x: hidden;
}

/* NOTE: THESE NEED TO STAY IN SYNC WITH LIGHT-THEME.CSS AND DARK-THEME.CSS.
   We are copy/pasting variables from light-theme and dark-theme,
   since they aren't loaded in this context (within commandlineoutput.xhtml
   and commandlinetooltip.xhtml). */
:root[devtoolstheme="light"] {
  --gcli-background-color: #fcfcfc; /* --theme-tab-toolbar-background */
  --gcli-input-focused-background: #ffffff; /* --theme-sidebar-background */
  --gcli-input-color: #393f4c; /* --theme-body-color */
  --gcli-border-color: #e0e0e1; /* --theme-splitter-color */
}

:root[devtoolstheme="dark"] {
  --gcli-background-color: #272b35; /* --theme-toolbar-background */
  --gcli-input-focused-background: #272b35; /* --theme-tab-toolbar-background */
  --gcli-input-color: #b6babf; /* --theme-body-color-alt */
  --gcli-border-color: #3c3c3d; /* --theme-splitter-color */
}

.gcli-body {
  margin: 0;
  font: message-box;
  color: var(--gcli-input-color);
}

#gcli-output-root,
#gcli-tooltip-root {
  border: 1px solid var(--gcli-border-color);
  border-radius: 3px;
  background-color: var(--gcli-background-color);
}

#gcli-output-root {
  padding: 5px 10px;
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
  border-bottom: 0;
}

#gcli-tooltip-root {
  padding: 5px 0px;
  overflow-x: hidden;
}

#gcli-tooltip-connector {
  margin-top: -1px;
  margin-left: 8px;
  width: 0;
  height: 0;
  border-left: 10px solid transparent;
  border-right: 10px solid transparent;
  background-color: transparent;
  border-top: 10px solid var(--gcli-background-color);
}

.gcli-tt-description,
.gcli-tt-error {
  padding: 0 10px;
}

.gcli-row-out {
  padding: 0 5px;
  line-height: 1.2em;
  border-top: none;
  border-bottom: none;
  color: var(--gcli-input-color);
}

.gcli-row-out p,
.gcli-row-out h1,
.gcli-row-out h2,
.gcli-row-out h3 {
  margin: 5px 0;
}

.gcli-row-out h1,
.gcli-row-out h2,
.gcli-row-out h3,
.gcli-row-out h4,
.gcli-row-out h5,
.gcli-row-out th,
.gcli-row-out strong,
.gcli-row-out pre {
  color: var(--gcli-input-color);
}

.gcli-row-out pre {
  font-size: 80%;
}

.gcli-row-out td {
  white-space: nowrap;
}

.gcli-row-out .nowrap {
  white-space: nowrap;
}

.gcli-out-shortcut,
.gcli-help-synopsis {
  padding: 0 3px;
  margin: 0 4px;
  font-weight: normal;
  font-size: 90%;
  border-radius: 3px;
  background-color: var(--gcli-background-color);
  border: 1px solid var(--gcli-border-color);
  cursor: pointer;
  display: inline-block;
}

.gcli-out-shortcut:before,
.gcli-help-synopsis:before {
  color: var(--gcli-input-color);
  padding-inline-end: 2px;
  content: '\bb';
}

.gcli-help-arrow {
  color: #666;
}

.gcli-help-description {
  margin: 0 20px;
  padding: 0;
}

.gcli-help-parameter {
  margin: 0 30px;
  padding: 0;
}

.gcli-help-header {
  margin: 10px 0 6px;
}

.gcli-menu-name {
  padding-inline-start: 8px;
}

.gcli-menu-desc {
  padding-inline-end: 8px;
  color: var(--gcli-input-color);
}

.gcli-menu-name:hover,
.gcli-menu-desc:hover {
  background-color: var(--gcli-input-focused-background);
}

.gcli-menu-highlight,
.gcli-menu-highlight:hover {
  background-color: hsla(0,100%,100%,.1);
}

.gcli-menu-typed {
  color: hsl(25,78%,50%);
}

.gcli-menu-more {
  font-size: 80%;
  text-align: end;
  padding-inline-end: 8px;
}

.gcli-addon-disabled {
  opacity: 0.6;
  text-decoration: line-through;
}

.gcli-breakpoint-label {
  font-weight: bold;
}

.gcli-breakpoint-lineText {
  font-family: monospace;
}

.gcli-help-name {
  text-align: end;
}

.gcli-menu-template {
  white-space: nowrap;
  width: 290px;
  display: flex;
}

.gcli-menu-names {
  white-space: nowrap;
  flex-grow: 0;
  flex-shrink: 0;
}

.gcli-menu-descs {
  flex-grow: 1;
  flex-shrink: 1;
}

.gcli-menu-name,
.gcli-menu-desc {
  white-space: nowrap;
}

.gcli-menu-name {
  padding-inline-end: 10px;
}

.gcli-menu-desc {
  text-overflow: ellipsis;
  overflow: hidden;
}

.gcli-menu-name,
.gcli-out-shortcut,
.gcli-help-synopsis {
  direction: ltr;
}

.gcli-cookielist-list {
  list-style-type: none;
  padding-left: 0;
}

.gcli-cookielist-detail {
  padding-left: 20px;
  padding-bottom: 10px;
}

.gcli-appcache-list {
  list-style-type: none;
  padding-left: 0;
}

.gcli-appcache-detail {
  padding-left: 20px;
  padding-bottom: 10px;
}
PK
!<kc�hh)chrome/devtools/skin/components-frame.css/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
 * Frame Component
 * Styles for React component at `devtools/client/shared/components/Frame.js`
 */

.frame-link {
  display: flex;
  justify-content: space-between;
  --frame-link-line-color: var(--theme-highlight-blue);
  --frame-link-source: var(--theme-highlight-purple);
}

.theme-dark .frame-link {
  --frame-link-source: #6B89FF;
}

.frame-link-async-cause {
  color: var(--theme-comment);
}

.frame-link .frame-link-source {
  flex: initial;
  color: var(--frame-link-source);
}

.frame-link a.frame-link-source {
  cursor: pointer;
  text-decoration: none;
  font-style: normal;
}

.frame-link a.frame-link-source:hover {
  text-decoration: underline;
}

.frame-link .frame-link-host {
  margin-inline-start: 5px;
  font-size: 90%;
  color: var(--theme-comment);
}

.frame-link .frame-link-function-display-name {
  margin-inline-end: 5px;
  color: var(--console-output-color, currentColor);
}

.frame-link .frame-link-line {
  color: var(--frame-link-line-color);
}


.focused .frame-link .frame-link-source,
.focused .frame-link .frame-link-line,
.focused .frame-link .frame-link-host {
  color: var(--theme-selection-color);
}
PK
!<�͘/chrome/devtools/skin/components-h-split-box.css/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
 * HSplitBox Component
 * Styles for React component at `devtools/client/shared/components/HSplitBox.js`
 */

.h-split-box,
.h-split-box-pane {
  overflow: auto;
  margin: 0;
  padding: 0;
  width: 100%;
  height: 100%;
}

.h-split-box {
  display: flex;
  flex-direction: row;
  flex: 1;
}
PK
!<߸((!chrome/devtools/skin/computed.css/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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-panel-computedview {
  margin: 0;
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100%;
}

#computed-container {
  overflow: auto;
  height: 100%;
}

/* This extra wrapper only serves as a way to get the content of the view focusable.
   So that when the user reaches it either via keyboard or mouse, we know that the view
   is focused and therefore can handle shortcuts.
   However, for accessibility reasons, tabindex is set to -1 to avoid having to tab
   through it, and the outline is hidden. */
#computed-container-focusable {
  height: 100%;
  outline: none;
}

#computed-toolbar {
  display: flex;
  align-items: center;
  -moz-user-select: none;
}

#browser-style-checkbox {
  /* Bug 1200073 - extra space before the browser styles checkbox so
     they aren't squished together in a small window. Put also
     an extra space after. */
  margin-inline-start: 5px;
  margin-inline-end: 0;
}

#browser-style-checkbox-label {
  padding-inline-start: 5px;
  margin-inline-end: 5px;
}

#computed-property-container {
  -moz-user-select: text;
  overflow-y: auto;
  overflow-x: hidden;
  flex: auto;
  border-top: 1px solid var(--theme-splitter-color);
  margin-top: -1px;
}

.row-striped {
  background: var(--theme-body-background);
}

.computed-property-hidden {
  display: none;
}

.computed-property-view {
  padding: 2px 0px;
  padding-inline-start: 5px;
  display: flex;
  flex-wrap: wrap;
}

.computed-property-name-container {
  width: 202px;
}

.computed-property-value-container {
  display: flex;
  flex: 1 1 168px;
  overflow: hidden;
}

.computed-property-name-container > *,
.computed-property-value-container > * {
  display: inline-block;
  vertical-align: middle;
}

.computed-property-name {
  overflow-x: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  outline: 0 !important;
}

.computed-other-property-value {
  background-image: url(images/arrow-e.png);
  background-repeat: no-repeat;
  background-size: 5px 8px;
  background-position: left center;
  padding-inline-start: 8px;
}

.computed-other-property-value:dir(rtl) {
  background-position-x: right;
}

@media (min-resolution: 1.1dppx) {
  .computed-other-property-value {
    background-image: url(images/[email protected]);
  }
}

.computed-property-value {
  overflow-x: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  padding-inline-start: 14px;
  outline: 0 !important;
}

.computed-property-content {
  padding-inline-start: 17px;
}

.theme-firebug .computed-property-view,
.theme-firebug .computed-property-content {
  font-family: var(--proportional-font-family);
}

.theme-firebug .computed-property-view {
  border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}

/* Bug 1360238 - getSelection displays an extra "\n" on multiple sibling block elements.
   We rely on this behavior to add an extra "\n" between matched selectors (Bug 1222737).
   Therefore we use <div> elements around matched selectors and need this class
   to keep them inline. We do that to avoid doing any formatting logic in JS.
   Once Bug 1360238 will be fixed, we'll probably have to change the behavior
   and remove this class. */
.fix-get-selection {
  display: inline;
}

.visually-hidden {
  opacity: 0;
}

/* From skin */
.computed-expander {
  visibility: hidden;
}

.computed-expandable {
  visibility: visible;
}

.match {
  visibility: hidden;
}

.matchedselectors > p {
  clear: both;
  margin: 0;
  margin-inline-end: 2px;
  padding: 2px;
  overflow-x: hidden;
  border-style: dotted;
  border-color: rgba(128,128,128,0.4);
  border-width: 1px 1px 0 1px;
}

.matchedselectors > p:last-of-type {
  border-bottom-width: 1px;
}

.matched {
  text-decoration: line-through;
}

.parentmatch {
  opacity: 0.5;
}

#computed-no-results {
  height: 100%;
}

.onlyuserstyles {
  cursor: pointer;
}

.legendKey {
  margin: 0 5px;
}

.computed-link {
  padding: 0 3px;
  cursor: pointer;
  float: right;
}

/* Workaround until float: inline-end; is enabled by default */
.computed-link:dir(rtl) {
  float: left;
}

/* Take away these two :visited rules to get a core dumper     */
/* See https://bugzilla.mozilla.org/show_bug.cgi?id=575675#c30 */

.computed-link,
.computed-link:visited {
  text-decoration: none;
}

.computed-link:hover {
  text-decoration: underline;
}

.computed-colorswatch {
  border-radius: 50%;
  width: 0.9em;
  height: 0.9em;
  vertical-align: middle;
  margin-inline-end: 5px;
  display: inline-block;
  position: relative;
}

.computed-colorswatch::before {
  content: '';
  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;
  position: absolute;
  border-radius: 50%;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: -1;
}
PK
!<(ޤ�!!#chrome/devtools/skin/dark-theme.css/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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(resource://devtools/client/themes/variables.css);
@import url(resource://devtools/client/themes/common.css);
@import url(chrome://devtools/skin/toolbars.css);
@import url(chrome://devtools/skin/tooltips.css);

body {
  margin: 0;
}

.theme-body {
  background: var(--theme-body-background);
  color: var(--theme-body-color);
}

.theme-sidebar {
  background: var(--theme-sidebar-background);
  color: var(--theme-toolbar-color);
}

::-moz-selection {
  background-color: var(--theme-selection-background);
  color: var(--theme-selection-color);
}

.theme-selected,
.CodeMirror-hint-active {
  background-color: var(--theme-selection-background);
  color: var(--theme-selection-color);
}

.theme-bg-contrast,
.variable-or-property:not([overridden])[changed] {
  background: var(--theme-contrast-background);
}

.theme-link,
.cm-s-mozilla .cm-link,
.CodeMirror-Tern-type {
  color: var(--grey-50);
}

/*
 * FIXME: http://bugzil.la/575675 CSS links without :visited set cause assertion
 * failures in debug builds.
 */
.theme-link:visited,
.cm-s-mozilla .cm-link:visited {
  color: var(--theme-highlight-blue);
}

.theme-comment,
.cm-s-mozilla .cm-meta,
.cm-s-mozilla .cm-hr,
.cm-s-mozilla .cm-comment,
.variable-or-property .token-undefined,
.variable-or-property .token-null,
.CodeMirror-Tern-completion-unknown:before {
  color: var(--theme-comment);
}

.cm-s-mozilla .cm-unused-line {
  text-decoration: line-through;
  text-decoration-color: #0072ab;
}

.cm-s-mozilla .cm-executed-line {
  background-color: #133c26;
}

.cm-s-mozilla .cm-number,
.variable-or-property .token-number,
.variable-or-property[return] > .title > .name,
.variable-or-property[scope] > .title > .name {
  color: #6B89FF;
}

.CodeMirror-Tern-completion-number:before {
  background-color: #5c9966;
}

.theme-fg-color1,
.theme-fg-color2,
.cm-s-mozilla .cm-attribute,
.cm-s-mozilla .cm-builtin,
.cm-s-mozilla .cm-keyword,
.variables-view-variable > .title > .name {
  color: var(--theme-highlight-red);
}

.cm-s-mozilla .cm-def,
.cm-s-mozilla .cm-variable-2 {
  color: var(--theme-highlight-blue);
}

.cm-s-mozilla .cm-variable {
  color: var(--theme-highlight-purple);
}

.cm-s-mozilla .cm-property {
  color: var(--theme-highlight-green);
}

.CodeMirror-Tern-completion-object:before {
  background-color: #3689b2;
}

.theme-fg-color3,
.theme-fg-color5,
.cm-s-mozilla .cm-tag,
.cm-s-mozilla .cm-header,
.cm-s-mozilla .cm-bracket,
.cm-s-mozilla .cm-qualifier,
.variables-view-property > .title > .name {
  color: var(--theme-highlight-blue);
}

.CodeMirror-Tern-completion-array:before {
  background-color: var(--theme-highlight-bluegrey);
}

.theme-fg-color4,
.theme-fg-color6 {
  color: var(--theme-highlight-purple);
}

.cm-s-mozilla .cm-string,
.cm-s-mozilla .cm-string-2,
.variable-or-property .token-string,
.CodeMirror-Tern-farg {
  color: #6B89FF;
}

.CodeMirror-Tern-completion-string:before,
.CodeMirror-Tern-completion-fn:before {
  background-color: #b26b47;
}

.theme-fg-color7,
.cm-s-mozilla .cm-atom,
.cm-s-mozilla .cm-quote,
.cm-s-mozilla .cm-error,
.variable-or-property .token-boolean,
.variable-or-property .token-domnode,
.variable-or-property[exception] > .title > .name {
  color: var(--theme-highlight-red);
}

.CodeMirror-Tern-completion-bool:before {
  background-color: #bf5656;
}

.variable-or-property .token-domnode {
  font-weight: bold;
}

.theme-toolbar,
.devtools-toolbar,
.devtools-sidebar-tabs tabs,
.devtools-sidebar-alltabs,
.cm-s-mozilla .CodeMirror-dialog { /* General toolbar styling */
  color: var(--theme-body-color);
  background-color: var(--theme-toolbar-background);
  border-color: var(--theme-splitter-color);
}

.theme-fg-contrast { /* To be used for text on theme-bg-contrast */
  color: black;
}

.ruleview-swatch,
.computed-colorswatch {
  box-shadow: 0 0 0 1px #818181;
}

/* CodeMirror specific styles.
 * Best effort to match the existing theme, some of the colors
 * are duplicated here to prevent weirdness in the main theme. */

.CodeMirror.cm-s-mozilla  { /* Inherit platform specific font sizing and styles */
  font-family: inherit;
  font-size: inherit;
  background: transparent;
}

.CodeMirror.cm-s-mozilla  pre {
  color: var(--theme-content-color1);
}

.cm-s-mozilla .cm-operator {
  color: var(--theme-body-color);
}

.cm-s-mozilla .cm-variable-3,
.cm-s-mozilla .cm-special {
  color: var(--theme-content-color1);
}

.cm-s-mozilla .CodeMirror-lines .CodeMirror-cursor {
  border-left: solid 1px #fff;
}

.cm-s-mozilla.CodeMirror-focused .CodeMirror-selected { /* selected text (focused) */
  background: var(--theme-selection-background-hover);
}

.cm-s-mozilla .CodeMirror-selected { /* selected text (unfocused) */
  background: var(--theme-selection-background-hover);
}

.cm-s-mozilla .CodeMirror-activeline-background { /* selected color with alpha */
  background: rgba(185, 215, 253, .15);
}

div.cm-s-mozilla span.CodeMirror-matchingbracket { /* highlight brackets */
  outline: solid 1px rgba(255, 255, 255, .25);
  color: white;
}

/* Highlight for a line that contains an error. */
div.CodeMirror div.error-line {
  background: rgba(255,0,0,0.2);
}

/* Generic highlighted text */
div.CodeMirror span.marked-text {
  background: rgba(255,255,0,0.2);
  border: 1px dashed rgba(192,192,0,0.6);
  margin-inline-start: -1px;
  margin-inline-end: -1px;
}

/* Highlight for evaluating current statement. */
div.CodeMirror span.eval-text {
  background-color: #556;
}

.cm-s-mozilla .CodeMirror-linenumber { /* line number text */
  color: var(--theme-content-color3);
}

.cm-s-mozilla .CodeMirror-gutters { /* vertical line next to line numbers */
  border-right-color: var(--theme-toolbar-background);
  background-color: var(--theme-sidebar-background);
}

.cm-s-markup-view pre {
  line-height: 1.4em;
  min-height: 1.4em;
}

/* XUL panel styling (see devtools/client/shared/widgets/tooltip/Tooltip.js) */

.theme-tooltip-panel .panel-arrowcontent {
  padding: 5px;
  background: rgba(19, 28, 38, .9);
  border-radius: 5px;
  box-shadow: none;
  border: 3px solid #434850;
}

/* Overring panel arrow images to fit with our light and dark themes */

.theme-tooltip-panel .panel-arrow {
  --arrow-margin: -4px;
}

:root[platform="win"] .theme-tooltip-panel .panel-arrow {
  --arrow-margin: -7px;
}

.theme-tooltip-panel .panel-arrow[side="top"],
.theme-tooltip-panel .panel-arrow[side="bottom"] {
  list-style-image: url("chrome://devtools/skin/tooltip/arrow-vertical-dark.png");
  /* !important is needed to override the popup.css rules in toolkit/themes */
  width: 39px !important;
  height: 16px !important;
}

.theme-tooltip-panel .panel-arrow[side="left"],
.theme-tooltip-panel .panel-arrow[side="right"] {
  list-style-image: url("chrome://devtools/skin/tooltip/arrow-horizontal-dark.png");
  /* !important is needed to override the popup.css rules in toolkit/themes */
  width: 16px !important;
  height: 39px !important;
}

.theme-tooltip-panel .panel-arrow[side="top"] {
  margin-bottom: var(--arrow-margin);
}

.theme-tooltip-panel .panel-arrow[side="bottom"] {
  margin-top: var(--arrow-margin);
}

.theme-tooltip-panel .panel-arrow[side="left"] {
  margin-right: var(--arrow-margin);
}

.theme-tooltip-panel .panel-arrow[side="right"] {
  margin-left: var(--arrow-margin);
}

@media (min-resolution: 1.1dppx) {
  .theme-tooltip-panel .panel-arrow[side="top"],
  .theme-tooltip-panel .panel-arrow[side="bottom"] {
    list-style-image: url("chrome://devtools/skin/tooltip/[email protected]");
  }

  .theme-tooltip-panel .panel-arrow[side="left"],
  .theme-tooltip-panel .panel-arrow[side="right"] {
    list-style-image: url("chrome://devtools/skin/tooltip/[email protected]");
  }
}

.theme-tooltip-panel .devtools-tooltip-simple-text {
  color: white;
  border-bottom: 1px solid #434850;
}

.theme-tooltip-panel .devtools-tooltip-simple-text:last-child {
  border-bottom: 0;
}

.devtools-textinput,
.devtools-searchinput,
.devtools-filterinput {
  background-color: rgba(24, 29, 32, 1);
  color: rgba(184, 200, 217, 1);
}

.CodeMirror-Tern-fname {
  color: #f7f7f7;
}

.CodeMirror-hints,
.CodeMirror-Tern-tooltip {
  box-shadow: 0 0 4px rgba(255, 255, 255, .3);
  background-color: #0f171f;
  color: var(--theme-body-color);
}
PK
!<���=�5�5!chrome/devtools/skin/debugger.css/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* Sources and breakpoints pane */

#sources-pane[selectedIndex="0"] + #sources-and-editor-splitter {
  border-color: transparent;
}

#sources-pane > tabs {
  border-inline-end: 1px solid;
}

#sources-pane .devtools-toolbar {
  border: none; /* Remove the devtools-toolbar bottom border. */
  border-inline-end: 1px solid;
}

#sources-pane > tabs,
#sources-pane .devtools-toolbar {
  border-inline-end-color: var(--theme-splitter-color);
}

/* Sources and breakpoints list */

.dbg-source-item {
  padding: 2px 0px;
}

.dbg-wasm-item .icon {
  display: block;
  background-image: url(chrome://devtools/skin/images/webconsole.svg);
  background-repeat: no-repeat;
  background-size: 72px 60px;
  /* show warning icon */
  background-position: -24px -24px;
  width: 10px;
  height: 10px;
  position: absolute;
  margin-inline-start: -15px;
  margin-top: 3px;
}

.dbg-breakpoint-line {
  font-weight: 600;
}

.dbg-breakpoint-text {
  padding-inline-start: 6px;
  font-style: italic;
  font-size: 90%;
}

.dbg-breakpoint-checkbox {
  width: 16px;
  height: 16px;
  margin: 2px;
}

/* Firebug theme uses breakpoint icon istead of a checkbox */

.theme-firebug #sources-pane .dbg-breakpoint-checkbox .checkbox-check {
  -moz-appearance: none;
  border: none;
  background: url(chrome://devtools/skin/images/firebug/breakpoint.svg) no-repeat 50% 50%;
}

.theme-firebug #sources-pane .dbg-breakpoint-checkbox:not([checked="true"]) > .checkbox-check,
.theme-firebug #sources-pane .dbg-breakpoint-checkbox:not([checked="true"]) ~ * {
  opacity: 0.5;
}

.theme-firebug #sources-pane .dbg-breakpoint-checkbox {
  padding-inline-end: 0;
  margin-inline-end: 0;
}

.dbg-breakpoint-condition-thrown-message {
  display: none;
  color: var(--theme-highlight-red);
}

.dbg-breakpoint.dbg-breakpoint-condition-thrown .dbg-breakpoint-condition-thrown-message {
  display: block;
  padding-inline-start: 0;
}

/* Sources toolbar */

#sources-toolbar > .devtools-toolbarbutton,
#sources-controls > .devtools-toolbarbutton {
  min-width: 32px;
}

#black-box {
  list-style-image: url(images/item-toggle.svg);
}

.theme-firebug #black-box {
  list-style-image: url(images/firebug/debugger-blackbox.svg);
}

#pretty-print {
  list-style-image: url(images/tool-styleeditor.svg);
}

.theme-firebug #pretty-print {
  list-style-image: url(images/firebug/debugger-prettyprint.svg);
}

#toggle-breakpoints {
  list-style-image: url(images/debugger-toggleBreakpoints.svg);
  -moz-image-region: rect(0,32px,16px,16px);
}

.theme-firebug #toggle-breakpoints {
  list-style-image: url(images/firebug/debugger-toggleBreakpoints.svg);
  -moz-image-region: unset;
}

#toggle-breakpoints[checked] {
  -moz-image-region: rect(0,16px,16px,0);
}

#toggle-breakpoints[checked] > image {
  /* This button has a special checked image, don't make it blue */
  filter: none;
}

#sources .black-boxed {
  color: rgba(128,128,128,0.4);
}

#sources .selected .black-boxed {
  color: rgba(255,255,255,0.4);
}

#sources .black-boxed ~ .dbg-breakpoint {
  display: none;
}

/* Debugger unblackbox button */

#black-boxed-message-button > .button-box > .button-icon {
  width: 16px;
  height: 16px;
  background-image: url(images/item-toggle.svg);
  background-position: 0 0;
  background-size: cover;
}

/* Black box message and source progress meter */

#black-boxed-message,
#source-progress-container {
  /* Prevent the container deck from aquiring the size from this message. */
  min-width: 1px;
  min-height: 1px;
}

#source-progress {
  min-height: 2em;
  min-width: 40em;
}

#black-boxed-message-label,
#black-boxed-message-button {
  text-align: center;
  font-size: 120%;
}

#black-boxed-message-button {
  margin-top: 1em;
  padding: .25em;
}

/* Breadcrumbs stack frames view */

.dbg-stackframe-details {
  padding-inline-start: 4px;
}

/* Classic stack frames view */

.dbg-classic-stackframe {
  display: block;
}

.dbg-classic-stackframe-title {
  font-weight: 600;
}

.dbg-classic-stackframe-details:-moz-locale-dir(ltr) {
  float: right;
}

.dbg-classic-stackframe-details:-moz-locale-dir(rtl) {
  float: left;
}

.dbg-classic-stackframe-details-url {
  max-width: 90%;
  text-align: end;
}

.dbg-classic-stackframe-details-url {
  color: var(--theme-content-color1);
}

.dbg-classic-stackframe-details-sep {
  color: var(--theme-body-color-alt)
}

.dbg-classic-stackframe-details-line {
  color: var(--theme-highlight-bluegrey);
}

#callstack-list .selected label {
  /* Text inside a selected item should not be custom colored. */
  color: inherit !important;
}

/* Tracer */

#trace {
  list-style-image: url(images/tracer-icon.png);
}

@media (min-resolution: 1.1dppx) {
  #trace {
    list-style-image: url(images/[email protected]);
  }
}

#clear-tracer {
  /* Make this button as narrow as the text inside it. */
  min-width: 1px;
}

.trace-name {
  padding-inline-start: 4px;
}

/* Tracer dark theme */

.theme-dark .trace-item {
  color: var(--theme-selection-color);
}

.theme-dark .trace-item.black-boxed {
  color: rgba(128,128,128,0.4);
}

.theme-dark .trace-item.selected-matching {
  background-color: rgba(86, 117, 185, .4); /* Select highlight blue at 40% alpha */
}

.theme-dark .selected > .trace-item {
  background-color: rgba(86, 117, 185, .6); /* Select highlight blue at 60% alpha */
}

.trace-call {
  color: var(--theme-highlight-blue);
}

.trace-return,
.trace-yield {
  color: var(--theme-highlight-green);
}

.trace-throw {
  color: var(--theme-highlight-red);
}

.trace-param {
  color: var(--theme-content-color1);
}

.theme-dark .trace-syntax {
  color: var(--theme-content-color2);
}

/* Tracer light theme */
.theme-light .trace-item {
  color: var(--theme-content-color1);
}

.theme-light .trace-item.black-boxed {
  color: rgba(128,128,128,0.4);
}

.theme-light .trace-item.selected-matching {
  background-color: rgba(76,158,217,.4); /* Select highlight blue at 40% alpha */
}

.theme-light .selected > .trace-item {
  background-color: rgba(76,158,217,.6); /* Select highlight blue at 60% alpha */
}

#tracer-traces .selected label {
  /* Text inside a selected item should not be custom colored. */
  color: inherit !important;
}

/* Watch expressions view */

#expressions {
  min-height: 10px;
  max-height: 125px;
}

.dbg-expression {
  height: 20px;
}

.dbg-expression-arrow {
  background-image: var(--theme-command-line-image-focus);
  width: 16px;
  height: 16px;
  margin: 2px;
}

.dbg-expression-input {
  color: inherit;
}

.dbg-expression-button {
  -moz-appearance: none;
  border: none;
  background: none;
  text-decoration: underline;
  cursor: pointer;
}

.dbg-expression-button {
  color: var(--theme-highlight-blue);
}

/* Event listeners view */

.dbg-event-listener-type {
  font-weight: 600;
}

.dbg-event-listener-location {
  color: var(--theme-content-color1);
}

.dbg-event-listener-separator {
  color: var(--theme-body-color-alt);
}

.dbg-event-listener-targets {
  color: var(--theme-highlight-bluegrey);
}

.theme-dark #event-listeners .selected {
  /* Selected items shouldn't be displayed differently. */
  background: none;
  color: #fff;
}

.theme-light #event-listeners .selected {
  /* Selected items shouldn't be displayed differently. */
  background: none;
  color: #000;
}

/* Searchbox and the search operations help panel */

#searchbox {
  min-width: 220px;
  margin-inline-start: 1px;
}

#filter-label {
  margin-inline-start: 2px;
}

#searchbox-panel-operators {
  margin-top: 5px;
  margin-bottom: 8px;
  margin-inline-start: 2px;
}

.searchbox-panel-operator-button {
  min-width: 26px;
  margin-top: 0;
  margin-bottom: 0;
  margin-inline-start: 2px;
  margin-inline-end: 6px;
  text-align: center;
}

.searchbox-panel-operator-label {
  padding-bottom: 2px;
}

/* Searchbox results panel */

#results-panel {
  border: none;
}

.results-panel-item {
  padding: 6px 8px;
  border-top: 1px solid rgba(128,128,128,0.2);
}

.results-panel-item:first-of-type {
  border-top: none;
}

.results-panel-item-label {
  font-weight: 600;
}

.results-panel-item-label-before {
  padding-inline-end: 6px;
}

.theme-dark .results-panel-item-label {
  color: var(--theme-selection-color);
}

.theme-light .results-panel-item-label {
  color: var(--theme-body-color);
}

.results-panel-item-label-before {
  color: var(--theme-highlight-bluegrey);
}

.results-panel-item-label-below {
  color: var(--theme-content-color3);
}

#results-panel .selected label {
  /* Text inside a selected item should not be custom colored. */
  color: inherit !important;
}

/* Sources search view */

#globalsearch {
  min-height: 10px;
  max-height: 50vh;
}

.dbg-results-header {
  padding-inline-start: 6px;
}

.dbg-results-header-location {
  font-weight: 600;
}

.dbg-results-header-match-count {
  padding-inline-start: 6px;
}

.dbg-results-line-number {
  min-width: 3em;
  border-inline-end: 1px solid rgba(128,128,128,0.2);
  padding-inline-end: 4px;
  text-align: end;
}

.dbg-results-line-contents {
  padding-inline-start: 4px;
}

.dbg-results-line-contents-string[match=true] {
  background-color: rgba(255,255,0,0.2);
  border: 1px solid rgba(128,128,128,0.7);
  border-radius: 4px;
  margin-top: -1px !important;
  margin-bottom: -1px !important;
  cursor: pointer;
}

.dbg-results-line-contents-string[match=true][focusing] {
  transition: transform 0.3s ease-in-out;
}

.dbg-results-line-contents-string[match=true][focused] {
  transition-duration: 0.1s;
  transform: scale(1.75, 1.75);
}

.dbg-source-results:not(.selected):hover {
  background-color: var(--theme-sidebar-background);
}

.dbg-results-header {
  background-color: var(--theme-tab-toolbar-background);
}

.theme-dark .dbg-results-header {
  color: var(--theme-content-color1);
}

.theme-light .dbg-results-header {
  color: var(--theme-content-color3);
}

.theme-dark .dbg-search-result:hover {
  background-color: rgba(86, 117, 185, .2); /* Select highlight blue at 40% alpha */
}

.theme-light .dbg-search-result:hover {
  background-color: rgba(76,158,217,.2); /* Select highlight blue at 40% alpha */
}

.dbg-results-header-match-count {
  color: var(--theme-content-color3);
}

.dbg-results-line-number {
  background-color: var(--theme-tab-toolbar-background);
  color: var(--theme-body-color-alt);
}

.dbg-results-line-contents-string {
  color: var(--theme-body-color-alt);
}

.theme-dark .dbg-results-line-contents-string[match=true] {
  color: var(--theme-selection-color);
}

.theme-light .dbg-results-line-contents-string[match=true] {
  color: var(--theme-body-color);
}

/* Toolbar controls */

#resume {
  list-style-image: url(images/pause.svg);
}

#resume[checked] {
  list-style-image: url(images/play.svg);
}

.theme-firebug #resume {
  list-style-image: url(images/firebug/pause.svg);
}

.theme-firebug #resume[checked] {
  list-style-image: url(images/firebug/play.svg);
}

#resume[break-on-next] {
  background: var(--theme-highlight-lightorange);
}

#step-over {
  list-style-image: url(images/debugger-step-over.svg);
}

#step-in {
  list-style-image: url(images/debugger-step-in.svg);
}

#step-out {
  list-style-image: url(images/debugger-step-out.svg);
}

.theme-firebug #step-over {
  list-style-image: url(images/firebug/debugger-step-over.svg);
}

.theme-firebug #step-in {
  list-style-image: url(images/firebug/debugger-step-in.svg);
}

.theme-firebug #step-out {
  list-style-image: url(images/firebug/debugger-step-out.svg);
}

#instruments-pane-toggle {
  list-style-image: var(--theme-pane-collapse-image);
}

#instruments-pane-toggle.pane-collapsed {
  list-style-image: var(--theme-pane-expand-image);
}

/* Horizontal vs. vertical layout */

#vertical-layout-panes-container {
  min-height: 35vh;
  max-height: 80vh;
}

#body[layout=vertical] #sources-pane > tabs {
  border-inline-end: none;
}

#body[layout=vertical] #instruments-pane {
  margin: 0 !important;
  /* To prevent all the margin hacks to hide the sidebar. */
}

#body[layout=vertical] .side-menu-widget-container,
#body[layout=vertical] .side-menu-widget-empty-text {
  box-shadow: none !important;
}

#body[layout=vertical] .side-menu-widget-item-arrow {
  background-image: none !important;
}

#body[layout=vertical] .side-menu-widget-group,
#body[layout=vertical] .side-menu-widget-item {
  margin-inline-end: 0;
}

/* Firebug theme customization of source group title */
.theme-firebug #sources-pane .side-menu-widget-group-title {
  border-bottom: none;
  padding: 2px 4px;
  background: var(--theme-header-background);
  font-weight: bold;
}

/* Sections titles (toolbars) in Variables panel they have different height */
.theme-firebug #variables-tabpanel .title.devtools-toolbar {
  display: -moz-box;
  height: 20px !important;
}

/* Firebug theme support for the Callstack Panel */

.theme-firebug #callstack-list {
  font-family: var(--proportional-font-family);
}

.theme-firebug #callstack-list .dbg-classic-stackframe-title {
  color: var(--theme-content-color2);
  font-weight: normal;
  font-family: monospace;
}

.theme-firebug #callstack-list .side-menu-widget-item {
  padding-top: 2px;
  padding-bottom: 2px;
}

.theme-firebug #callstack-list .dbg-classic-stackframe-details-url,
.theme-firebug #callstack-list .dbg-classic-stackframe-details-sep,
.theme-firebug #callstack-list .dbg-classic-stackframe-details-line {
  color: blue;
  font-weight: bold;
}

.theme-firebug #callstack-list .side-menu-widget-item {
  margin: 0 4px;
}

.theme-firebug #callstack-list .side-menu-widget-item.selected {
  color: var(--theme-selection-color);
}

.theme-firebug #callstack-list .side-menu-widget-item:first-child {
  border-top: none;
}
PK
!<����)chrome/devtools/skin/devtools-browser.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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("resource://devtools/client/themes/splitters.css");
@import url("resource://devtools/client/themes/commandline-browser.css");

/* Bottom-docked toolbox minimize transition */
.devtools-toolbox-bottom-iframe {
  transition: margin-bottom .1s;
}

.devtools-toolbox-side-iframe {
  min-width: 465px;
}

/* Eyedropper Widget */
/* <panel> added to mainPopupSet */

.devtools-eyedropper-panel {
  pointer-events: none;
  -moz-appearance: none;
  width: 156px;
  height: 120px;
  background-color: transparent;
  border: none;
}
PK
!<�|���&chrome/devtools/skin/firebug-theme.css/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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(resource://devtools/client/themes/variables.css);
@import url(resource://devtools/client/themes/common.css);
@import url(chrome://devtools/skin/light-theme.css);

:root.theme-firebug {
  font-size: 11px;
  font-family: var(--proportional-font-family);
}

/* CodeMirror Color Syntax */

.theme-firebug .cm-keyword {color: BlueViolet; font-weight: bold;}
.theme-firebug .cm-atom {color: #219;}
.theme-firebug .cm-number {color: #164;}
.theme-firebug .cm-def {color: #00f;}
.theme-firebug .cm-variable {color: black;}
.theme-firebug .cm-variable-2 {color: black;}
.theme-firebug .cm-variable-3 {color: black;}
.theme-firebug .cm-property {color: black;}
.theme-firebug .cm-operator {color: black;}
.theme-firebug .cm-comment {color: Silver;}
.theme-firebug .cm-string {color: Red;}
.theme-firebug .cm-string-2 {color: Red;}
.theme-firebug .cm-meta {color: rgb(120, 120, 120); font-style: italic;}
.theme-firebug .cm-error {color: #f00;}
.theme-firebug .cm-qualifier {color: #555;}
.theme-firebug .cm-builtin {color: #30a;}
.theme-firebug .cm-bracket {color: #997;}
.theme-firebug .cm-tag {color: blue;}
.theme-firebug .cm-attribute {color: rgb(0, 0, 136);}
.theme-firebug .cm-header {color: blue;}
.theme-firebug .cm-quote {color: #090;}
.theme-firebug .cm-hr {color: #999;}
.theme-firebug .cm-link {color: #00c;}

.theme-firebug .theme-fg-color3,
.theme-firebug .cm-s-mozilla .kind-Object .cm-variable{
  color: var(--theme-content-color1);
  font-style: normal;
}

.theme-firebug .console-string {
  color: #FF183C;
}

/* Variables View */

.theme-firebug .variables-view-variable > .title > .name,
.theme-firebug .variables-view-variable > .title > .value {
  color: var(--theme-body-color);
}

/* Firebug theme support for tabbar and panel tabs
  (both, main and side panels )*/

/* Only apply bottom-border for:
  1) The main tab list.
  2) The side tab list if there is no scroll-box that has its own border.

  Use !important to override even the rule in webconsole.css that uses
  ID in the selector. */

.theme-firebug .devtools-sidebar-tabs tabs {
  background: var(--theme-tab-toolbar-background) !important;
  border-bottom: 1px solid var(--theme-splitter-color) !important;
}

/* Add a negative bottom margin to overlap bottom border
  of the parent element (see also the next comment for 'tabs') */
.theme-firebug .devtools-tab,
.theme-firebug .devtools-sidebar-tabs tab {
  margin: 3px 0 -1px 0;
  padding: 1px 4px !important;
  border: 1px solid transparent !important;
  border-bottom: none !important;
  border-radius: 4px 4px 0 0;
  font-weight: bold;
  color: var(--theme-body-color);
  -moz-box-flex: initial;
  min-width: 0;
}

/* For toolbox tabs, the negative margin is applied on the container
   instead of the tab to avoid triggering y-overflow on the container
   that causes the overflow menu to display */

.theme-firebug .devtools-tab {
  margin-bottom: 0;
}

.theme-firebug .toolbox-tabs,
.theme-firebug #toolbox-option-container {
  margin-bottom: -1px;
}

.theme-firebug .devtools-tab-label {
  padding-inline-end: 0;
  mask-image: none;
}

/* In order to hide bottom-border of side panel tabs we need
 to make the parent element overflow visible, so child element
 can move one pixel down to hide the bottom border of the parent. */
.theme-firebug .devtools-sidebar-tabs tabs {
  overflow: visible;
}

.theme-firebug .devtools-tab:hover,
.theme-firebug .devtools-sidebar-tabs tab:hover {
  border-color: #C8C8C8 !important;
}

.theme-firebug .devtools-tab.selected,
.theme-firebug .devtools-sidebar-tabs tab[selected] {
  background-color: var(--theme-toolbar-tab-selected-background);
  border-color: var(--theme-splitter-color) !important;
  border-bottom-color: transparent !important;
  padding-bottom: 2px;
  color: inherit;
}

.theme-firebug .devtools-tabbar .devtools-separator,
.theme-firebug .devtools-tab img {
  display: none;
}

.theme-firebug .devtools-sidebar-tabs tab label {
  margin: 2px 0 0 0;
}

/* Use different padding for labels inside tabs on Win platform.
  Make sure this overrides the default in global.css */
:root[platform="win"].theme-firebug .devtools-sidebar-tabs tab label {
  margin: 0 4px !important;
}

.theme-firebug #panelSideBox .devtools-tab.selected,
.theme-firebug .devtools-sidebar-tabs tab[selected] {
  background-color: white;
}

.theme-firebug #panelSideBox .devtools-tab:first-child,
.theme-firebug .devtools-sidebar-tabs tab:first-child {
  margin-inline-start: 5px;
}

/* Firebug theme support for the Option (panel) tab */

.theme-firebug #toolbox-tab-options {
  margin-inline-end: 4px;
}

.theme-firebug #toolbox-tab-options::before {
  content: url(chrome://devtools/skin/images/firebug/tool-options.svg);
  display: block;
  margin: 4px 4px 0;
}

.theme-firebug #toolbox-tab-options:not([selected]):hover::before {
  filter: brightness(80%);
}

/* Element picker */
.theme-firebug #toolbox-buttons-start {
  border: none;
}

/* Toolbar */

.theme-firebug .theme-toolbar,
.theme-firebug toolbar,
.theme-firebug .devtools-toolbar {
  background: var(--theme-toolbar-background) !important;
  padding-inline-end: 4px;
}

/* The vbox for panel content also uses theme-toolbar class from some reason
 but it shouldn't have the padding as defined above, so fix it here */
.theme-firebug #toolbox-deck > .toolbox-panel.theme-toolbar {
  padding-inline-end: 0;
}

/* Space around toolbar buttons */
.theme-firebug .devtools-toolbar {
  padding: 2px 3px;
}

/* The height is the same for all toolbars and side panels tabs */
.theme-firebug .theme-toolbar,
.theme-firebug .devtools-sidebar-tabs tabs,
.theme-firebug .devtools-toolbar {
  height: 28px !important;
}

/* Do not set the fixed height for rule viewtoolbar. This toolbar
  is changing its height to show pseudo classes. */
.theme-firebug #ruleview-toolbar-container {
  height: auto !important;
}

/* The Inspector panel side panels are using both
  .devtools-toolbar and .theme-toolbar. We want the
  proportional font for all labels in these toolbars */
.theme-firebug .devtools-toolbar label,
.theme-firebug .devtools-toolbar .label,
.theme-firebug .theme-toolbar label,
.theme-firebug .theme-toolbar .label {
  font-family: var(--proportional-font-family);
}

/* Make sure the toolbar buttons shrink nicely. */

#toolbox-buttons-end {
  background-image: linear-gradient(rgba(253, 253, 253, 0.2), rgba(253, 253, 253, 0));
}
PK
!<ٝ�]��7chrome/devtools/skin/floating-scrollbars-dark-theme.css@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
@namespace html url("http://www.w3.org/1999/xhtml");

/* Restrict all styles to `*|*:not(html|select) > scrollbar` so that scrollbars
   inside a <select> are excluded (including them hides the select arrow on
   Windows).  We want to include both the root scrollbars for the document as
   well as any overflow: scroll elements within the page, while excluding
   <select>. */
*|*:not(html|select) > scrollbar {
  -moz-appearance: none !important;
  position: relative;
  background-color: transparent;
  background-image: none;
  z-index: 2147483647;
  padding: 2px;
  pointer-events: auto;
}

*|*:root[platform="mac"] > scrollbar,
*|*:root[platform="mac"] *|*:not(html|select) > scrollbar {
  border: none;
}

/* Scrollbar code will reset the margin to the correct side depending on
   where layout actually puts the scrollbar */
*|*:not(html|select) > scrollbar[orient="vertical"] {
  margin-left: -10px;
  min-width: 10px;
  max-width: 10px;
}

*|*:not(html|select) > scrollbar[orient="horizontal"] {
  margin-top: -10px;
  min-height: 10px;
  max-height: 10px;
}

*|*:not(html|select) > scrollbar thumb {
  background-color: rgba(170, 170, 170, .2) !important; /* --toolbar-tab-hover */
  -moz-appearance: none !important;
  border-width: 0px !important;
  border-radius: 3px !important;
}

*|*:root[platform="mac"] > scrollbar slider,
*|*:root[platform="mac"] *|*:not(html|select) > scrollbar slider {
  -moz-appearance: none !important;
}

*|*:root[platform="win"] > scrollbar scrollbarbutton,
*|*:root[platform="linux"] > scrollbar scrollbarbutton,
*|*:root[platform="win"] > scrollbar gripper,
*|*:root[platform="linux"] > scrollbar gripper,
*|*:root[platform="win"] *|*:not(html|select) > scrollbar scrollbarbutton,
*|*:root[platform="linux"] *|*:not(html|select) > scrollbar scrollbarbutton,
*|*:root[platform="win"] *|*:not(html|select) > scrollbar gripper,
*|*:root[platform="linux"] *|*:not(html|select) > scrollbar gripper {
  display: none;
}
PK
!<eyT�yy>chrome/devtools/skin/floating-scrollbars-responsive-design.css@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
@namespace html url("http://www.w3.org/1999/xhtml");

/* Restrict all styles to `*|*:not(html|select) > scrollbar` so that scrollbars
   inside a <select> are excluded (including them hides the select arrow on
   Windows).  We want to include both the root scrollbars for the document as
   well as any overflow: scroll elements within the page, while excluding
   <select>. */
*|*:not(html|select) > scrollbar {
  -moz-appearance: none !important;
  position: relative;
  background-color: transparent;
  background-image: none;
  z-index: 2147483647;
  padding: 2px;
  border: none;
}

/* Scrollbar code will reset the margin to the correct side depending on
   where layout actually puts the scrollbar */
*|*:not(html|select) > scrollbar[orient="vertical"] {
  margin-left: -10px;
  min-width: 10px;
  max-width: 10px;
}

*|*:not(html|select) > scrollbar[orient="horizontal"] {
  margin-top: -10px;
  min-height: 10px;
  max-height: 10px;
}

*|*:not(html|select) > scrollbar slider {
  -moz-appearance: none !important;
}

*|*:not(html|select) > scrollbar thumb {
  -moz-appearance: none !important;
  background-color: rgba(0,0,0,0.2);
  border-width: 0px !important;
  border-radius: 3px !important;
}

*|*:not(html|select) > scrollbar scrollbarbutton,
*|*:not(html|select) > scrollbar gripper {
  display: none;
}
PK
!<4~��LLchrome/devtools/skin/fonts.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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-panel-fontinspector {
  margin: 0;
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100%;
}

#font-container {
  overflow: auto;
  flex: auto;
}

.fonts-list {
  padding: 0;
  margin: 0;
  list-style: none;
}

.font {
  border: 1px solid var(--theme-splitter-color);
  border-width: 0 1px 1px 0;
  display: grid;
  grid-template-columns: 1fr auto;
  grid-column-gap: 10px;
  padding: 10px 20px;
}

#font-container .theme-twisty {
  display: inline-block;
  cursor: pointer;
  vertical-align: bottom;
}

.font-preview-container {
  grid-column: 2;
  grid-row: 1 / span 2;
  overflow: hidden;
  display: grid;
  place-items: center end;
  position: relative;
}

.font-preview {
  height: 50px;
  display: block;
}

.font-preview:hover {
  cursor: text;
  background-image: linear-gradient(to right,
    var(--grey-40) 3px, transparent 3px, transparent);
  background-size: 6px 1px;
  background-repeat: repeat-x;
  background-position-y: 45px;
}

#font-container .font-preview-input {
  position: absolute;
  top: 5px;
  left: 0;
  width: calc(100% - 5px);
  height: calc(100% - 10px);
  background: transparent;
  color: transparent;
  border-radius: 0;
  padding: 0;
}

.font-preview-input::-moz-selection {
  background: transparent;
  color: transparent;
}

.font-name {
  margin: 0;
  font-size: 1.2em;
  font-weight: normal;
  white-space: nowrap;
}

.font-css-code {
  direction: ltr;
  margin: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--theme-toolbar-color);
  grid-column: span 2;
  position: relative;
  offset-inline-start: -4px;
}

.font-css-code-expander::before {
  content: "\2026";
  display: inline-block;
  width: 12px;
  height: 8px;
  margin: 0 2px;
  line-height: 3px;
  color: var(--theme-body-color-inactive);
  border-radius: 3px;
  border-style: solid;
  border-width: 1px;
  text-align: center;
  vertical-align: middle;
}

.font-origin {
  margin-top: .2em;
  color: var(--grey-50);
  justify-self: start;
}

.font-origin.system {
  text-transform: capitalize;
}

.font-origin.remote {
  display: grid;
  grid-template-columns: 1fr 20px;
}

.font-origin.remote .url {
  text-overflow: ellipsis;
  overflow: hidden;
  white-space: nowrap;
}

.font-origin .copy-icon {
  border: 0;
  padding: 0;
  position: relative;
  cursor: pointer;
  width: 12px;
  height: 12px;
  place-self: center;

  background: url(chrome://devtools/skin/images/copy.svg) no-repeat;
  background-size: 12px;
  background-position-x: -1px;
  -moz-context-properties: fill;
  fill: var(--theme-toolbar-color);

}

#font-container .devtools-sidepanel-no-result + .accordion {
  border-block-start: 1px solid var(--theme-splitter-color);
}
PK
!<�#H��#chrome/devtools/skin/images/add.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b">
  <path d="M8 8v5.5c0 .276-.224.5-.5.5s-.5-.224-.5-.5V8H1.5c-.276 0-.5-.224-.5-.5s.224-.5.5-.5H7V1.5c0-.276.224-.5.5-.5s.5.224.5.5V7h5.5c.276 0 .5.224.5.5s-.224.5-.5.5H8z"/>
</svg>
PK
!<��1chrome/devtools/skin/images/alerticon-warning.png�PNG


IHDR
��7��IDATxm3�]Apv��ض�&�m�F۪c6�m���|�ً��]r��H�)	x��D�:-��S�J�n*�N����xS�*�^���-%�k�
�u�åz
���|��,O[!Ov�x�<N(d�T��,A�z��5\���v;I��}87��L��&�C#x���F�ߡ��L�����l���	�X��Z�p�U�ݵ�8����kE>3)�@9C���@�p��JxBZ��\�e	�!�F�}]Rѯ�
�ʗ���E9 h
��$:):��H�u��T�JR�LG-��
F�tO�
A�e��3�%U�����/�,XQ7G%G}j�y�+Q�.�J �7)�Te���u�M����D�r#EK��h�|R�Q�JE����������� ��՞Ek�!s%,C$|�Dzl�H���I�8s�89�z7'%آh�Һ�);:�01F�t����l/���+2KIEND�B`�PK
!<5C�z��4chrome/devtools/skin/images/[email protected]�PNG


IHDR���tIDATxc��cG ~�n���6��@���?o @��-A�f�Zr9��P���;(�3�PϢ�F-@��?m�
;m�;k�E� ���+��@�
�%�aQ/<N�J!�K"��DJ-��a`Rp"��b9J,������!�u �-�A�%��A.EK�{>29�&�h�
)�x>�|��P�X@�]��4KN��\Ҍ�O��?ihfc�?�Oj��o��4��Az�M�@��A&���<���ڐ���2���|��Dz�x-���?��cq�	lj�ŕ��1لǕ[�Xt�����;�߀��W{������?#.�nHU_�)r5�"��n�6�F���$n|� [�a
�P����
�IEND�B`�PK
!<�T��xx,chrome/devtools/skin/images/angle-swatch.svg<?xml version="1.0" encoding="UTF-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 xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12" width="12px" height="12px">
  <mask id="angle-mask">
    <rect width="100%" height="100%" fill="#fff"/>
    <polygon points="6 6, 12 12, 0 12, 0 0, 6 0, 6 6"/>
  </mask>
  <mask id="circle-mask">
    <circle cx="6" cy="6" r="6" fill="#fff"/>
  </mask>
  <circle cx="6" cy="6" r="6" fill="#fff"/>
  <circle cx="6" cy="6" r="6" mask="url(#angle-mask)" fill="#aeb0b1"/>
  <line x1="6" y1="0" x2="6" y2="6" stroke-width="0.5" stroke="rgba(0,0,0,0.5)"></line>
  <line x1="6" y1="6" x2="12" y2="12" stroke-width="0.5" stroke="rgba(0,0,0,0.5)" mask="url(#circle-mask)"></line>
</svg>
PK
!<�r	gtt4chrome/devtools/skin/images/animation-fast-track.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 9 12" width="16" height="16">
  <path d="M5.75 0l-1 5.5 2 .5-3.5 6 1-5-2-.5z" fill="context-fill"/>
</svg>
PK
!<���ǧ�'chrome/devtools/skin/images/arrow-e.png�PNG


IHDR1�5;nIDATxU���1���u8��aR�L�l$cH��C�]�?�ݝ�ۘ�9A���q]���c�U�)���j�n���{��/�MH/LT.R}?Z���\*��lC��?IEND�B`�PK
!<�����*chrome/devtools/skin/images/[email protected]�PNG


IHDR
/�@aIDATxb�@���H��l�^�P:�6֌�
��3'�;��=)�i���v���
AȞ?0�����I�VL� �NOE�,�h���4�Jh��4����I�U���-`�X�� G'p6�>���avA�5�a�]6�=<��d�J���B�R������lww��F��55߲��D�2xB*�y�>�NΜh��
��i@H��bp1��)�p3��لt
��*9������7�j�e��ʠ��Շ#K����[���F��u&���:���Fl۴�����!���,��7���&H
&�$p�'�׎��k�+�_��V�Ra�*^������e��]�v��	w�xyV��P��s�ҫIEND�B`�PK
!<�:�ff3chrome/devtools/skin/images/breadcrumbs-divider.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:xlink="http://www.w3.org/1999/xlink" width="5" height="7">
  <defs>
    <linearGradient id="b">
      <stop offset="0" stop-color="#9a9aba"/>
      <stop offset="1" stop-color="#a6a6c2"/>
    </linearGradient>
    <linearGradient id="a">
      <stop offset="0" stop-color="#8e8eb2"/>
      <stop offset="1" stop-color="#9a9aba"/>
    </linearGradient>
    <linearGradient x1="3.616" y1="3.893" x2="1.285" y2="-.757" id="d" xlink:href="#a" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1 0 0 .8684 0 1046.257)"/>
    <linearGradient x1="2.232" y1="4.162" x2=".629" y2=".966" id="c" xlink:href="#b" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1 0 0 .8684 0 1046.257)"/>
  </defs>
  <path d="M.2 1045.562l4.6 3.3-4.6 3.3 2-3.3z" fill="url(#c)" stroke="url(#d)" stroke-width=".4" stroke-linejoin="round" transform="translate(0 -1045.362)"/>
</svg>
PK
!<�����8chrome/devtools/skin/images/breadcrumbs-scrollbutton.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" d="M6.414 8l4.293-4.293a1 1 0 0 0-1.414-1.414l-5 5a1 1 0 0 0 0 1.414l5 5a1 1 0 0 0 1.414-1.414z"></path>
</svg>
PK
!<]�\Ixx*chrome/devtools/skin/images/breakpoint.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:xlink="http://www.w3.org/1999/xlink" width="33" height="12" viewBox="0 0 33 12">
  <defs>
    <style>
      use:not(:target) {
        display: none;
      }
      #light {
        fill: #46afe3;
      }
      #light-hover {
        fill: #9aa6b3;
      }
      #light-active {
        fill: #2cbb0f;
      }
      #light-conditional {
        fill: #d97e00;
      }
      #dark {
        fill: #46afe3;
      }
      #dark-hover {
        fill: #3d454d;
      }
      #dark-active {
        fill: #70bf53;
      }
      #dark-conditional {
        fill: #d89b28;
      }
    </style>
    <path id="base-path" d="M27.1,0H1C0.4,0,0,0.4,0,1v10c0,0.6,0.4,1,1,1h26.1 c0.6,0,1.2-0.3,1.5-0.7L33,6l-4.4-5.3C28.2,0.3,27.7,0,27.1,0z"/>
  </defs>
  <use xlink:href="#base-path" id="light"/>
  <use xlink:href="#base-path" id="light-hover"/>
  <use xlink:href="#base-path" id="light-active"/>
  <use xlink:href="#base-path" id="light-conditional"/>
  <use xlink:href="#base-path" id="dark"/>
  <use xlink:href="#base-path" id="dark-hover"/>
  <use xlink:href="#base-path" id="dark-active"/>
  <use xlink:href="#base-path" id="dark-conditional"/>
</svg>
PK
!<[�o��%chrome/devtools/skin/images/clear.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b">
  <path d="M5 3h3V2c0-.003-3 0-3 0-.002 0 0 1 0 1zm-5 .5A.5.5 0 0 1 .494 3h12.012a.5.5 0 0 1 0 1H.494A.502.502 0 0 1 0 3.5zM4 3V2c0-.553.444-1 1-1h3c.552 0 1 .443 1 1v1H4zM5 11V6a.5.5 0 0 0-1 0v5a.5.5 0 1 0 1 0zM7 11V6a.5.5 0 0 0-1 0v5a.5.5 0 1 0 1 0zM9 11V6a.5.5 0 0 0-1 0v5a.5.5 0 1 0 1 0z"/>
  <path d="M3 4v9h7V4H3zm0-1h7a1 1 0 0 1 1 1v9a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1z"/>
</svg>
PK
!<�
2w%chrome/devtools/skin/images/close.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b">
  <path d="M8.707 8l4.23 4.23a.5.5 0 1 1-.707.707L8 8.707l-4.23 4.23a.5.5 0 1 1-.707-.707L7.293 8l-4.23-4.23a.5.5 0 1 1 .707-.707L8 7.293l4.23-4.23a.5.5 0 0 1 .707.707L8.707 8z" fill-rule="evenodd"/>
</svg>
PK
!<E���FF/chrome/devtools/skin/images/command-console.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 16 16" fill="context-fill #0b0b0b">
  <path d="M6.8 9.7c0-.2 0-.3-.2-.4L4.9 7.6c-.3-.3-.7-.3-.9 0s-.3.6 0 .9l1.3 1.4L4 11.3c-.3.3-.3.6 0 .9s.6.3.9 0l1.8-1.8c.1-.2.2-.5.1-.7z"/>
  <path d="M14.2 2H1.8c-.4 0-.8.4-.8.9v11.2c0 .4.3.9.8.9h12.4c.4 0 .8-.4.8-.9V2.9c0-.7-.6-.9-.8-.9zM14 14H2V6h12v8zm0-9H2V3h12v2z"/>
</svg>
PK
!<�ב��2chrome/devtools/skin/images/command-eyedropper.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 16 16" fill="context-fill #0b0b0b">
  <path d="M8.3 2.9l4.9 4.9c.2.2.5.4.8.1.2-.2.1-.5-.1-.8L11.6 5l1.8-1.8c.2-.2.4-.5.1-.8-.2-.2-.5-.1-.8.1L11 4.3l-2.1-2c-.2-.3-.5-.4-.8-.2-.2.3-.1.6.2.8zM10.4 7.4l-6.1 6-2.4.8.7-2.4 6.2-6.1-.7-.7L2 11c-.1.1-.2.3-.2.4L1 13.7s-.1.7.1 1c.3.3.9.3 1.2.2l2.3-.8c.2-.1.3-.1.4-.3L11 8l-.6-.6z"/>
  <path opacity="0.5" d="M7.1 7.1l-4.2 3.8-1.4 3.5 2.9-.6 2.8-2.7z"/>
</svg>
PK
!<p�L���.chrome/devtools/skin/images/command-frames.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 16 16" fill="context-fill #0b0b0b">
  <path d="M14.2 2H1.8c-.4 0-.8.4-.8.9v11.2c0 .4.3.9.8.9h12.4c.4 0 .8-.4.8-.9V2.9s-.6-.9-.8-.9zM8 14H2v-4h6v4zm6 0H9v-4h5v4zm0-5H2V3h12v6z"/>
</svg>
PK
!<�N��/chrome/devtools/skin/images/command-measure.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 16 16" fill="context-fill #0b0b0b">
  <path d="M1 11h14V6H1v5zm15 .2c0 .5-.4.8-.8.8H.8c-.4 0-.8-.4-.8-.8V5.8c0-.4.4-.8.8-.8h14.3c.5 0 .9.4.9.8v5.4z"/>
  <path d="M9 6v3c0 .2.3.4.5.4s.5-.2.5-.4V6H9zM13 6v3c0 .2.3.4.5.4.2-.1.5-.2.5-.4V6h-1zM5.1 5.7L4 6v2.8c0 .2.4.4.6.4s.5-.2.5-.4V5.7zM11 5v2.7c0 .2.3.4.5.4s.5-.2.5-.4V5h-1zM6 5.1v2.6c0 .2.5.4.7.4.2 0 .6-.2.6-.4l-.1-2.6H6zM2 5.1v2.6c0 .2.3.4.5.4s.5-.2.5-.4V5.1H2z"/>
</svg>
PK
!<TGI��2chrome/devtools/skin/images/command-noautohide.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b">
  <path d="M2 1.99v4.02C2 6 2 6 1.99 6h4.02C6 6 6 6 6 6.01V1.99C6 2 6 2 6.01 2H1.99C2 2 2 2 2 1.99zm-1 0c0-.546.451-.99.99-.99h4.02c.546 0 .99.451.99.99v4.02c0 .546-.451.99-.99.99H1.99A.996.996 0 0 1 1 6.01V1.99zM10 1.99v4.02C10 6 10 6 9.99 6h4.02C14 6 14 6 14 6.01V1.99c0 .01 0 .01.01.01H9.99C10 2 10 2 10 1.99zm-1 0c0-.546.451-.99.99-.99h4.02c.546 0 .99.451.99.99v4.02c0 .546-.451.99-.99.99H9.99A.996.996 0 0 1 9 6.01V1.99zM10 9.99v4.02c0-.01 0-.01-.01-.01h4.02c-.01 0-.01 0-.01.01V9.99c0 .01 0 .01.01.01H9.99c.01 0 .01 0 .01-.01zm-1 0c0-.546.451-.99.99-.99h4.02c.546 0 .99.451.99.99v4.02c0 .546-.451.99-.99.99H9.99a.996.996 0 0 1-.99-.99V9.99zM2 9.99v4.02C2 14 2 14 1.99 14h4.02C6 14 6 14 6 14.01V9.99c0 .01 0 .01.01.01H1.99C2 10 2 10 2 9.99zm-1 0c0-.546.451-.99.99-.99h4.02c.546 0 .99.451.99.99v4.02c0 .546-.451.99-.99.99H1.99a.996.996 0 0 1-.99-.99V9.99z"/>
</svg>
PK
!<4	c���5chrome/devtools/skin/images/command-paintflashing.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b">
  <path d="M3 6.997v6.006c0-.006.003-.003.002-.003h9.996c-.001 0 .002-.003.002.003V6.997c0 .006-.003.003-.002.003H3.002C3.003 7 3 7.003 3 6.997zm-1 0C2 6.447 2.456 6 3.002 6h9.996C13.55 6 14 6.453 14 6.997v6.006c0 .55-.456.997-1.002.997H3.002A1.004 1.004 0 0 1 2 13.003V6.997zM8.5 4V1.5a.5.5 0 0 0-1 0V4H4a.5.5 0 0 0 0 1h8a.5.5 0 1 0 0-1H8.5z"/>
  <path fill-opacity=".3" d="M13 10v3H3v-3z"/>
</svg>
PK
!<Η�BB,chrome/devtools/skin/images/command-pick.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b">
  <path d="M15 7.667V3.002A1.01 1.01 0 0 0 13.993 2H2.007C1.45 2 1 2.449 1 3.002v9.996C1 13.544 1.45 14 2.007 14h6.818l-.37-1H2V3h12v4.334l1 .333z"/>
  <path fill-opacity=".3" d="M9 8l1.981 5.843 4.044-3.966z"/>
  <path d="M8.526 8.16l1.982 5.844a.5.5 0 0 0 .824.196l4.043-3.966a.5.5 0 0 0-.202-.835L9.15 7.523a.5.5 0 0 0-.623.638zm.948-.32l-.623.637 6.025 1.877-.201-.834-4.044 3.966.824.197-1.981-5.844z"/>
  <path d="M12.674 12.39l1.973 1.964a.5.5 0 1 0 .706-.708L13.38 11.68a.5.5 0 0 0-.706.709z"/>
</svg>
PK
!<��}�6chrome/devtools/skin/images/command-responsivemode.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" fill="context-fill #0b0b0b">
  <path d="M12 8H5v1h7V8z"/>
  <path d="M5 2.007v11.986C5 14 5 14 4.998 14h7.004C12 14 12 14 12 13.993V2.007C12 2 12 2 12.002 2H4.998C5 2 5 2 5 2.007zm-1 0C4 1.45 4.446 1 4.998 1h7.004c.55 0 .998.45.998 1.007v11.986c0 .556-.446 1.007-.998 1.007H4.998C4.448 15 4 14.55 4 13.993V2.007z"/>
  <path d="M8.5 13c.828 0 1.5-.672 1.5-1.5S9.328 10 8.5 10 7 10.672 7 11.5 7.672 13 8.5 13zm0-1c.276 0 .5-.224.5-.5s-.224-.5-.5-.5-.5.224-.5.5.224.5.5.5z" fill-rule="evenodd"/>
</svg>

PK
!<���^~~.chrome/devtools/skin/images/command-rulers.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 16 16" fill="context-fill #0b0b0b">
  <path d="M11.4 3c-.2 0-.4.2-.4.4v2.5c0 .2.3.4.5.4s.5-.2.5-.4V3.4c0-.2-.3-.4-.5-.4M13.5 3.2c-.2 0-.5.2-.5.4v1.5c0 .2.3.4.5.4s.5-.2.5-.4V3.6c0-.2-.3-.4-.5-.4M9.6 3.2c-.2 0-.6.2-.6.4v1.5c0 .2.3.4.6.4.2 0 .4-.2.4-.4V3.6c0-.2-.2-.4-.4-.4M7.4 3c-.2 0-.4.2-.4.4v2.5c0 .2.3.4.5.4s.5-.2.5-.4V3.4c0-.2-.2-.4-.6-.4M5.5 3.2c-.3 0-.5.2-.5.4v1.5c0 .2.3.4.5.4s.5-.2.5-.4V3.6c0-.2-.3-.4-.5-.4M4.3 8.5c0-.2-.2-.5-.4-.5H2.4c-.2 0-.4.3-.4.5s.2.5.4.5h1.5c.2 0 .4-.3.4-.5M4.3 12.5c0-.2-.2-.5-.4-.5H2.4c-.2 0-.4.3-.4.5s.2.5.4.5h1.5c.2 0 .4-.3.4-.5M5.1 10.5c0-.2-.2-.5-.4-.5H3.2c-.2 0-.4.3-.4.5s.2.5.4.5h1.5c.2 0 .4-.3.4-.5M5.1 6.5c0-.2-.2-.5-.4-.5H3.2c-.2 0-.4.3-.4.5s.2.5.4.5h1.5c.2 0 .4-.3.4-.5"/>
  <path d="M15.1 3H3.5C2.9 3 2 3.6 2 4.1V14c0 .6 1 1 1.5 1h3.2c.6 0 1.3-.5 1.3-1V9h7c.6 0 1-.4 1-.9v-4c0-1-.4-1.1-.9-1.1zM15 8H7.4c-.6 0-.5.1-.4 0v6H3V4h12v4z"/>
</svg>
PK
!<��H��2chrome/devtools/skin/images/command-screenshot.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 16 16" fill="context-fill #0b0b0b">
  <path d="M13.6 5H11V3.5C11 2.7 10 2 9.2 2H6.8C6 2 5 2.7 5 3.5V5H2.4C1.6 5 1 5.6 1 6.4v6.5c0 .8.6 1.1 1.4 1.1h11.2c.8 0 1.4-.3 1.4-1.1V6.4c0-.8-.6-1.4-1.4-1.4zm.4 8H2V6h4V3h4v3h3.9l.1 7z"/>
  <path d="M8 6.8c-1.3 0-2.4 1.1-2.4 2.4s1.1 2.4 2.4 2.4 2.4-1.1 2.4-2.4c0-1.3-1.1-2.4-2.4-2.4zm0 3.5c-.7 0-1.2-.5-1.2-1.1S7.3 8.1 8 8.1s1.2.5 1.2 1.1-.5 1.1-1.2 1.1z"/>
</svg>
PK
!<4û�0chrome/devtools/skin/images/commandline-icon.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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="16px" height="16px" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
  <defs>
    <style>
      g {
        display: none;
      }

      #light-theme:target,
      #light-theme-focus:target ~ #light-theme,
      #dark-theme:target,
      #dark-theme-focus:target ~ #dark-theme {
        display: inline;
      }

      #light-theme-focus:target ~ #light-theme {
        fill: #0060DF;
      }
      #dark-theme-focus:target ~ #dark-theme {
        fill: #75BFFF;
      }

      /* Unfocused states */
      #light-theme,
      #dark-theme {
        fill: rgba(128, 128, 128, .5);
      }
    </style>
  </defs>
  <g id="light-theme-focus"/>
  <g id="light-theme">
    <path d="M7.29 13.907l7-5a.5.5 0 0 0 .033-.789l-6.5-5.5a.5.5 0 1 0-.646.764l6.5 5.5.032-.789-7 5a.5.5 0 1 0 .582.814z"/>
    <path d="M2.29 13.907l7-5a.5.5 0 0 0 .033-.789l-6.5-5.5a.5.5 0 1 0-.646.764l6.5 5.5.032-.789-7 5a.5.5 0 1 0 .582.814z"/>
  </g>
  <g id="dark-theme-focus"/>
  <g id="dark-theme">
    <path d="M7.29 13.907l7-5a.5.5 0 0 0 .033-.789l-6.5-5.5a.5.5 0 1 0-.646.764l6.5 5.5.032-.789-7 5a.5.5 0 1 0 .582.814z"/>
    <path d="M2.29 13.907l7-5a.5.5 0 0 0 .033-.789l-6.5-5.5a.5.5 0 1 0-.646.764l6.5 5.5.032-.789-7 5a.5.5 0 1 0 .582.814z"/>
  </g>
</svg>PK
!<��4��(chrome/devtools/skin/images/controls.png�PNG


IHDR84�|>�IDATxb������`����n@(Da�@@w@
�%�B��$� � �"$HI
H@@�Rpo�q�d�A[���g����{&��4'�����x8�z���5�4g����n�>��I�/��/�K�R��r���p8|[������^
�n�i�q���w�����d6�Ѩi`EӒ+�y�����/_Q`\�u�~�Od��u�^��n�� 0�:!���x9P!��X!��y�Z�N���^2
1�#K��i�%s:���bq��XM�oѨ�PH����r��+V� ��i���a�x<�-��m��y>:�}�W��P>)�kŤ�M`Y���lI�@��D��!	�!qA*�"���I-R�9��	���L�9�����_� 	N�w�H�#���������q�#��q����q�%�p��������M�`�_QIEND�B`�PK
!<g��QQ+chrome/devtools/skin/images/[email protected]�PNG


IHDRp8%�|JIDATxb����ƣL�s+�C-��}_���kR�a�g��ֹ�unł�mp�!8�hQ�9��&Np���pa#�� � �!�;�a�����7�}<�������λSK�?��P�����yhP0�€����
P�<t@t����#�0
x�NP�"�]4AS�-|��h��Pn	�6:CӲcc�;�Z��[��w���[��}Ck�}���g�_q�hLxo�z��6������.�'�\�E�&��Ǩ#�7�7F�V�}4+Afhe�6�w»@�RX�^C���A׼�k>��D^#V�����
�J�F��^#�	�(]B��Oh+���{hN@<JstN���S||\�(m�'t~�v}�GH3�F
����a�/�Q�M�A�;1+�Q�-�i���(
t��V��@�6��h�RG�@��t�
 �R�eɌ�(�]��
 �R�e�ȣ�wY��5�Nz�:�{`���:�$��I�4�a���%z�9�����Q����ږw�u����)��ճh�#���X�-
ql�ad�(�� �"�Ԍ ���Q�x@	�(�j�
q���C!Fl�"��b�r�@B����x*@n9�x��Z͈���<�?�(�	pċ="������F$��G���#2�����	O��#����D�S��ˆ��$< !�P�@$<= �)���2���qMZ���Ja<+{���P��A<n�f<�&�s@��s@���?��,IEND�B`�PK
!<���)��$chrome/devtools/skin/images/copy.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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="M5.70001221,11.125 L5.70001221,4.625 L7.66001225,4.625 L7.66001225,6.94642857 C7.66001225,7.20271429 7.87953225,7.41071429 8.15001225,7.41071429 L10.6000123,7.41071429 L10.6000123,11.125 L5.70001221,11.125 Z M4.7,5.475 L4.7,7.275 L2,7.275 L2,0.975 L3.8,0.975 L3.8,3.225 C3.8,3.4734 4.0016,3.675 4.25,3.675 L6.5,3.675 C5.5064,3.675 4.7,4.4814 4.7,5.475 Z M6.84002447,3.00050006 L4.65002441,3.00050006 L4.65002441,0.8105 L6.84002447,3.00050006 Z M10.5,6.6000061 L8.5,6.6000061 L8.5,4.6000061 L10.5,6.6000061 Z M11.28025,6.21975 L9.03025,3.96975 C8.89,3.82875 8.6995,3.75 8.5,3.75 L7.75,3.75 L7.75,3 C7.75,2.80125 7.67125,2.61 7.53025,2.46975 L5.28025,0.21975 C5.14,0.07875 4.94875,0 4.75,0 L2.5,0 C1.672,0 1,0.672 1,1.5 L1,6.75 C1,7.578 1.672,8.25 2.5,8.25 L4.75,8.25 L4.75,10.5 C4.75,11.328 5.422,12 6.25,12 L10,12 C10.828,12 11.5,11.328 11.5,10.5 L11.5,6.75 C11.5,6.55125 11.42125,6.36 11.28025,6.21975 Z"></path>
</svg>
PK
!<6O��3chrome/devtools/skin/images/cubic-bezier-swatch.png�PNG


IHDRVu\��IDATxmу�0��b�}���m۶�?6��|Q93
��d��#���@�˪\6���n�����[�gx�4�r���{�8�JE��#H�#�T�ww��nl!<_�����g�-x��/��W��1�7� ����<ڢ���ŽS�-�{^�p~�హ4���1y�h��
1<0�c �`�Z8���iF��6;8dt��y�h��g����/3.�f{IEND�B`�PK
!<�ӊ
ZZ6chrome/devtools/skin/images/[email protected]�PNG


IHDR�w=�!IDATx��eb�0D�6��9B~^�c9�����^�T�\�3ӌ��4�*���֎�o�kdҺ��+�9?�)F�))��:�@p���W���Ԧm�W�P\�s_��G����^�uu�CH~G]S�?�r �_\��a���?�q]DC,��r��+ �\�D
�ڧ�IDƑ���1p���c��i���;��J�A	\����]��cL�2�9p���D'�|��{�1��PM"1)X�oa�&��S�01��-l��	n��"R*BI|E��D3��R��;���0a2�7�+��	�\�@��rdd�c]�sy��w�a�Eid0�	򞍱N,a�8N���W$0���a#������\
��lMc���;�.#H�Zhz��կ�;�{��M�
3.w�)d��ܓH]b��]
��;���8]+N�h�ɾ���A�A��R���w��g\=DC�4J/�w��8�z�8���d*�Ҋ~|{Bo(��yLܠ�g���ۖ�roa�
w�cf�X±�~�Ѷ�O��>�}�̼IEND�B`�PK
!<��
U��.chrome/devtools/skin/images/debugger/arrow.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 16 16">
  <path d="M8 13.4c-.5 0-.9-.2-1.2-.6L.4 5.2C0 4.7-.1 4.3.2 3.7S1 3 1.6 3h12.8c.6 0 1.2.1 1.4.7.3.6.2 1.1-.2 1.6l-6.4 7.6c-.3.4-.7.5-1.2.5z"/>
</svg>PK
!<Ku7���-chrome/devtools/skin/images/debugger/back.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" d="M15 7H3.414l4.293-4.293a1 1 0 0 0-1.414-1.414l-6 6a1 1 0 0 0 0 1.414l6 6a1 1 0 0 0 1.414-1.414L3.414 9H15a1 1 0 0 0 0-2z"></path>
</svg>
PK
!<�wuB��1chrome/devtools/skin/images/debugger/blackBox.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
  <g fill-rule="evenodd">
    <circle cx="8" cy="8.5" r="1.5"/>
    <path d="M15.498 8.28l-.001-.03v-.002-.004l-.002-.018-.004-.031c0-.002 0-.002 0 0l-.004-.035.006.082c-.037-.296-.133-.501-.28-.661-.4-.522-.915-1.042-1.562-1.604-1.36-1.182-2.74-1.975-4.178-2.309a6.544 6.544 0 0 0-2.755-.042c-.78.153-1.565.462-2.369.91C3.252 5.147 2.207 6 1.252 7.035c-.216.233-.36.398-.499.577-.338.437-.338 1 0 1.437.428.552.941 1.072 1.59 1.635 1.359 1.181 2.739 1.975 4.177 2.308.907.21 1.829.223 2.756.043.78-.153 1.564-.462 2.369-.91 1.097-.612 2.141-1.464 3.097-2.499.217-.235.36-.398.498-.578.12-.128.216-.334.248-.554 0 .01 0 .01-.008.04l.013-.079-.001.011.003-.031.001-.017v.005l.001-.02v.008l.002-.03.001-.05-.001-.044v-.004-.004zm-.954.045v.007l.001.004V8.33v.012l-.001.01v-.005-.005l.002-.015-.001.008c-.002.014-.002.014 0 0l-.007.084c.003-.057-.004-.041-.014-.031-.143.182-.27.327-.468.543-.89.963-1.856 1.752-2.86 2.311-.724.404-1.419.677-2.095.81a5.63 5.63 0 0 1-2.374-.036c-1.273-.295-2.523-1.014-3.774-2.101-.604-.525-1.075-1.001-1.457-1.496-.054-.07-.054-.107 0-.177.117-.152.244-.298.442-.512.89-.963 1.856-1.752 2.86-2.311.724-.404 1.419-.678 2.095-.81a5.631 5.631 0 0 1 2.374.036c1.272.295 2.523 1.014 3.774 2.101.603.524 1.074 1 1.457 1.496.035.041.043.057.046.076 0 .01 0 .01.008.043l-.009-.047.003.02-.002-.013v-.008.016c0-.004 0-.004 0 0v-.004z"/>
  </g>
</svg>
PK
!<�\2���.chrome/devtools/skin/images/debugger/close.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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="16px" height="16px" viewBox="0 0 6 6" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <path d="M1.35191454,5.27895256 L5.31214367,1.35518468 C5.50830675,1.16082764 5.50977084,0.844248536 5.3154138,0.648085456 C5.12105677,0.451922377 4.80447766,0.450458288 4.60831458,0.644815324 L0.648085456,4.56858321 C0.451922377,4.76294025 0.450458288,5.07951935 0.644815324,5.27568243 C0.83917236,5.47184551 1.15575146,5.4733096 1.35191454,5.27895256 L1.35191454,5.27895256 Z" id="Line" stroke="none" fill="#696969" fill-rule="evenodd"></path>
  <path d="M5.31214367,4.56858321 L1.35191454,0.644815324 C1.15575146,0.450458288 0.83917236,0.451922377 0.644815324,0.648085456 C0.450458288,0.844248536 0.451922377,1.16082764 0.648085456,1.35518468 L4.60831458,5.27895256 C4.80447766,5.4733096 5.12105677,5.47184551 5.3154138,5.27568243 C5.50977084,5.07951935 5.50830675,4.76294025 5.31214367,4.56858321 L5.31214367,4.56858321 Z" id="Line-Copy-2" stroke="none" fill="#696969" fill-rule="evenodd"></path>
</svg>
PK
!<������/chrome/devtools/skin/images/debugger/domain.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
  <path d="M9.05 4.634l-2.144.003-.116.116v1.445l.92.965.492.034.116-.116v-.617L9.13 5.7l.035-.95M12.482 10.38l-1.505-1.462H9.362l-.564.516-.034 1.108.72.768 1.323.034-.117-.116v1.2l.972 1.02.315.034.116-.116v-1.154l.422-.374.034-.927-.117.117h.26l.408-.36V10.5l-.125-.124-.575-.033"/>
  <path d="M8.47 15.073c-3.088 0-5.6-2.513-5.6-5.602V9.4v-.003c0-.018 0-.018.002-.034l.182-.088.724.587.49.033.497.543-.034.9.317.383h.47l.114.096-.032 1.9.524.553h.105l.025-.338 1.004-.95.054-.474.53-.462v-.888l-.588-.038-1.118-1.155H4.48l-.154-.09V9.01l.155-.1h1.164v-.273l.12-.115.7.033.494-.443.034-.746-.624-.655h-.724v.28l-.11.07H4.64l-.114-.09.025-.64.48-.43v-.244h-.382c-.102 0-.152-.128-.08-.2 1.04-1.01 2.428-1.59 3.903-1.59 1.374 0 2.672.5 3.688 1.39.08.068.03.198-.075.198l-1.144-.034-.81.803.52.523v.16l-.382.388h-.158l-.176-.177v-.16l.076-.074-.252-.252-.37.362.53.53c.072.072.005.194-.096.194l-.752-.005v.844h.783L9.885 8l.16-.143h.16l.62.61v.267l.58.027.003.002V8.76l.18-.03 1.234 1.24.753-.708h.382l.116.108c0 .02.003.016.003.036v.065c0 3.09-2.515 5.603-5.605 5.603M8.47 3C4.904 3 2 5.903 2 9.47c0 3.57 2.903 6.472 6.47 6.472 3.57 0 6.472-2.903 6.472-6.47C14.942 5.9 12.04 3 8.472 3"/>
</svg>
PK
!<��zzvv-chrome/devtools/skin/images/debugger/file.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
  <path d="M4 2v12h9V4.775L9.888 2H4zm0-1h5.888c.246 0 .483.09.666.254l3.112 2.774c.212.19.334.462.334.747V14c0 .552-.448 1-1 1H4c-.552 0-1-.448-1-1V2c0-.552.448-1 1-1z"/>
  <path d="M9 1.5v4c0 .325.306.564.62.485l4-1c.27-.067.432-.338.365-.606-.067-.27-.338-.432-.606-.365l-4 1L10 5.5v-4c0-.276-.224-.5-.5-.5s-.5.224-.5.5z"/>
</svg>
PK
!<�����/chrome/devtools/skin/images/debugger/folder.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
  <path d="M2 5.193v7.652c0 .003-.002 0 .007 0H14v-7.69c0-.003.002 0-.007 0h-7.53v-2.15c0-.002-.004-.005-.01-.005H2.01C2 3 2 3 2 3.005V5.193zm-1 0V3.005C1 2.45 1.444 2 2.01 2h4.442c.558 0 1.01.45 1.01 1.005v1.15h6.53c.557 0 1.008.44 1.008 1v7.69c0 .553-.45 1-1.007 1H2.007c-.556 0-1.007-.44-1.007-1V5.193zM6.08 4.15H2v1h4.46v-1h-.38z" fill-rule="evenodd"/>
</svg>
PK
!<N,c��0chrome/devtools/skin/images/debugger/forward.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" d="M15.707 7.293l-6-6a1 1 0 0 0-1.414 1.414L12.586 7H1a1 1 0 0 0 0 2h11.586l-4.293 4.293a1 1 0 1 0 1.414 1.414l6-6a1 1 0 0 0 0-1.414z"></path>
</svg>
PK
!<���9chrome/devtools/skin/images/debugger/pause-exceptions.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
  <path d="M10.483 13.995H5.517l-3.512-3.512V5.516l3.512-3.512h4.966l3.512 3.512v4.967l-3.512 3.512zm4.37-9.042l-3.807-3.805A.503.503 0 0 0 10.691 1H5.309a.503.503 0 0 0-.356.148L1.147 4.953A.502.502 0 0 0 1 5.308v5.383c0 .134.053.262.147.356l3.806 3.806a.503.503 0 0 0 .356.147h5.382a.503.503 0 0 0 .355-.147l3.806-3.806A.502.502 0 0 0 15 10.69V5.308a.502.502 0 0 0-.147-.355z"/>
  <path d="M10 10.5a.5.5 0 1 0 1 0v-5a.5.5 0 1 0-1 0v5zM5 10.5a.5.5 0 1 0 1 0v-5a.5.5 0 0 0-1 0v5z"/>
</svg>
PK
!<ZB\P.chrome/devtools/skin/images/debugger/pause.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
  <g fill-rule="evenodd">
    <path d="M5 12.503l.052-9a.5.5 0 0 0-1-.006l-.052 9a.5.5 0 0 0 1 .006zM12 12.497l-.05-9A.488.488 0 0 0 11.474 3a.488.488 0 0 0-.473.503l.05 9a.488.488 0 0 0 .477.497.488.488 0 0 0 .473-.503z"/>
  </g>
</svg>
PK
!<D��&554chrome/devtools/skin/images/debugger/prettyPrint.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
  <path d="M4.525 13.21h-.472c-.574 0-.987-.154-1.24-.463-.253-.31-.38-.882-.38-1.719v-.573c0-.746-.097-1.265-.292-1.557-.196-.293-.51-.44-.945-.44v-.974c.435 0 .75-.146.945-.44.195-.292.293-.811.293-1.556v-.58c0-.833.126-1.404.379-1.712.253-.31.666-.464 1.24-.464h.472v.783h-.179c-.37 0-.628.08-.774.24-.145.159-.218.54-.218 1.141v.383c0 .824-.096 1.432-.287 1.823-.191.39-.516.679-.974.866.458.191.783.482.974.873.191.39.287.998.287 1.823v.382c0 .602.073.982.218 1.142.146.16.404.239.774.239h.18v.783zm9.502-4.752c-.43 0-.744.147-.942.44-.197.292-.296.811-.296 1.557v.573c0 .837-.125 1.41-.376 1.719-.251.309-.664.463-1.237.463h-.478v-.783h.185c.37 0 .628-.08.774-.24.145-.159.218-.539.218-1.14v-.383c0-.825.096-1.433.287-1.823.191-.39.516-.682.974-.873-.458-.187-.783-.476-.974-.866-.191-.391-.287-.999-.287-1.823v-.383c0-.602-.073-.982-.218-1.142-.146-.159-.404-.239-.774-.239h-.185v-.783h.478c.573 0 .986.155 1.237.464.25.308.376.88.376 1.712v.58c0 .673.088 1.174.263 1.503.176.329.5.493.975.493v.974z" fill-rule="evenodd"/>
</svg>
PK
!<��2���.chrome/devtools/skin/images/debugger/react.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="34" height="32" viewBox="0 -2.5 34 32">
<path d="M19.314 15.987c0 1.321-1.071 2.392-2.392 2.392s-2.392-1.071-2.392-2.392c0-1.321 1.071-2.392 2.392-2.392s2.392 1.071 2.392 2.392z"></path>
<path d="M16.922 24.783c1.878 1.826 3.729 2.906 5.221 2.906 0.489 0 0.952-0.103 1.337-0.334 1.337-0.772 1.826-2.701 1.363-5.453-0.077-0.489-0.18-0.977-0.309-1.492 0.514-0.154 0.977-0.309 1.44-0.463 2.598-1.003 4.038-2.392 4.038-3.909 0-1.543-1.44-2.932-4.038-3.909-0.463-0.18-0.926-0.334-1.44-0.463 0.129-0.514 0.232-1.003 0.309-1.492 0.437-2.803-0.051-4.758-1.389-5.53-0.386-0.231-0.849-0.334-1.337-0.334-1.466 0-3.344 1.080-5.221 2.906-1.852-1.826-3.704-2.906-5.195-2.906-0.489 0-0.952 0.103-1.337 0.334-1.337 0.772-1.826 2.701-1.363 5.453 0.077 0.489 0.18 0.977 0.309 1.492-0.514 0.154-0.977 0.309-1.44 0.463-2.598 1.003-4.038 2.392-4.038 3.909 0 1.543 1.44 2.932 4.038 3.909 0.463 0.18 0.926 0.334 1.44 0.463-0.129 0.514-0.232 1.003-0.309 1.492-0.437 2.752 0.051 4.707 1.363 5.453 0.386 0.232 0.849 0.334 1.337 0.334 1.492 0.051 3.344-1.029 5.221-2.829v0zM15.481 21.311c0.463 0.026 0.952 0.026 1.44 0.026s0.977 0 1.44-0.026c-0.463 0.617-0.952 1.183-1.44 1.723-0.489-0.54-0.977-1.106-1.44-1.723zM12.292 18.662c0.257 0.437 0.489 0.849 0.772 1.26-0.797-0.103-1.543-0.232-2.263-0.386 0.232-0.694 0.489-1.415 0.797-2.135 0.206 0.411 0.437 0.849 0.694 1.26zM10.8 12.463c0.72-0.154 1.466-0.283 2.263-0.386-0.257 0.412-0.514 0.823-0.772 1.26s-0.489 0.849-0.694 1.286c-0.334-0.746-0.592-1.466-0.797-2.161zM12.215 15.987c0.334-0.694 0.694-1.389 1.106-2.083 0.386-0.669 0.823-1.337 1.26-2.006 0.772-0.051 1.543-0.077 2.341-0.077 0.823 0 1.595 0.026 2.341 0.077 0.463 0.669 0.874 1.337 1.26 2.006 0.412 0.694 0.772 1.389 1.106 2.083-0.334 0.694-0.694 1.389-1.106 2.083-0.386 0.669-0.823 1.337-1.26 2.006-0.772 0.051-1.543 0.077-2.341 0.077-0.823 0-1.595-0.026-2.341-0.077-0.463-0.669-0.874-1.337-1.26-2.006-0.412-0.695-0.772-1.389-1.106-2.083v0zM22.272 14.598l-0.694-1.286c-0.257-0.437-0.489-0.849-0.772-1.26 0.797 0.103 1.543 0.232 2.263 0.386-0.231 0.72-0.489 1.44-0.797 2.161v0zM22.272 17.376c0.309 0.72 0.566 1.44 0.797 2.135-0.72 0.154-1.466 0.283-2.263 0.386 0.257-0.412 0.514-0.823 0.772-1.26 0.232-0.386 0.463-0.823 0.694-1.26v0zM22.863 26.301c-0.206 0.129-0.463 0.18-0.746 0.18-1.26 0-2.829-1.029-4.372-2.572 0.746-0.797 1.466-1.698 2.186-2.701 1.209-0.103 2.366-0.283 3.447-0.54 0.129 0.463 0.206 0.926 0.283 1.389 0.36 2.186 0.077 3.755-0.797 4.244zM24.201 12.746c2.881 0.823 4.604 2.083 4.604 3.241 0 1.003-1.183 2.006-3.266 2.804-0.412 0.154-0.874 0.309-1.337 0.437-0.334-1.055-0.746-2.135-1.26-3.241 0.514-1.106 0.952-2.186 1.26-3.241v0zM22.143 5.493c0.283 0 0.514 0.051 0.746 0.18 0.849 0.489 1.157 2.032 0.797 4.244-0.077 0.437-0.18 0.9-0.283 1.389-1.080-0.232-2.238-0.412-3.447-0.54-0.694-1.003-1.44-1.903-2.186-2.701 1.543-1.518 3.112-2.572 4.372-2.572zM18.362 10.663c-0.463-0.026-0.952-0.026-1.44-0.026s-0.977 0-1.44 0.026c0.463-0.617 0.952-1.183 1.44-1.723 0.489 0.54 0.977 1.132 1.44 1.723v0zM10.98 5.673c0.206-0.129 0.463-0.18 0.746-0.18 1.26 0 2.829 1.029 4.372 2.572-0.746 0.797-1.466 1.697-2.186 2.701-1.209 0.103-2.366 0.283-3.447 0.54-0.129-0.463-0.206-0.926-0.283-1.389-0.36-2.186-0.077-3.729 0.797-4.244v0zM9.643 19.228c-2.881-0.823-4.604-2.083-4.604-3.241 0-1.003 1.183-2.006 3.266-2.803 0.412-0.154 0.874-0.309 1.337-0.437 0.334 1.055 0.746 2.135 1.26 3.241-0.514 1.106-0.952 2.212-1.26 3.241zM10.183 22.057c0.077-0.437 0.18-0.9 0.283-1.389 1.080 0.232 2.238 0.412 3.447 0.54 0.694 1.003 1.44 1.903 2.186 2.701-1.543 1.517-3.112 2.572-4.372 2.572-0.283 0-0.514-0.051-0.746-0.18-0.875-0.489-1.157-2.058-0.797-4.244z"></path>
</svg>
PK
!<�
�G((/chrome/devtools/skin/images/debugger/resume.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:svg="http://www.w3.org/2000/svg">
  <path fill="black" id="svg_1" fill-rule="evenodd" d="m4.55195,12.97461l7.4,-5l-7.4,-5l0,10zm-0.925,0l0,-10c0,-0.785 0.8,-1.264 1.415,-0.848l7.4,5c0.58,0.392 0.58,1.304 0,1.696l-7.4,5c-0.615,0.416 -1.415,-0.063 -1.415,-0.848z"/>
</svg>
PK
!<Z
���/chrome/devtools/skin/images/debugger/stepIn.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
  <g fill-rule="evenodd">
    <path d="M1.5 14.042h4.095a.5.5 0 0 0 0-1H1.5a.5.5 0 1 0 0 1zM7.983 2a.5.5 0 0 1 .517.5v7.483l3.136-3.326a.5.5 0 1 1 .728.686l-4 4.243a.499.499 0 0 1-.73-.004L3.635 7.343a.5.5 0 0 1 .728-.686L7.5 9.983V3H1.536C1.24 3 1 2.776 1 2.5s.24-.5.536-.5h6.447zM10.5 14.042h4.095a.5.5 0 0 0 0-1H10.5a.5.5 0 1 0 0 1z"/>
  </g>
</svg>
PK
!<�[rr0chrome/devtools/skin/images/debugger/stepOut.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
  <g fill-rule="evenodd">
    <path d="M5 13.5H1a.5.5 0 1 0 0 1h4a.5.5 0 1 0 0-1zM12 13.5H8a.5.5 0 1 0 0 1h4a.5.5 0 1 0 0-1zM6.11 5.012A.427.427 0 0 1 6.21 5h7.083L9.646 1.354a.5.5 0 1 1 .708-.708l4.5 4.5a.498.498 0 0 1 0 .708l-4.5 4.5a.5.5 0 0 1-.708-.708L13.293 6H6.5v5.5a.5.5 0 1 1-1 0v-6a.5.5 0 0 1 .61-.488z"/>
  </g>
</svg>
PK
!<v��h��1chrome/devtools/skin/images/debugger/stepOver.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
  <g fill-rule="evenodd">
    <path d="M13.297 6.912C12.595 4.39 10.167 2.5 7.398 2.5A5.898 5.898 0 0 0 1.5 8.398a.5.5 0 0 0 1 0A4.898 4.898 0 0 1 7.398 3.5c2.75 0 5.102 2.236 5.102 4.898v.004L8.669 7.029a.5.5 0 0 0-.338.942l4.462 1.598a.5.5 0 0 0 .651-.34.506.506 0 0 0 .02-.043l2-5a.5.5 0 1 0-.928-.372l-1.24 3.098z"/>
    <circle cx="7" cy="12" r="1"/>
  </g>
</svg>
PK
!<��겎�0chrome/devtools/skin/images/debugger-step-in.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b">
  <path d="M1.5 14.042h4.095a.5.5 0 0 0 0-1H1.5a.5.5 0 1 0 0 1zM7.5 3v6.983L4.364 6.657a.5.5 0 0 0-.728.686l4 4.243a.51.51 0 0 0 .021.02.5.5 0 0 0 .71-.024l3.997-4.239a.5.5 0 1 0-.728-.686L8.5 9.983V2.5a.5.5 0 0 0-.536-.5H1.536C1.24 2 1 2.224 1 2.5s.24.5.536.5H7.5zM10.5 14.042h4.095a.5.5 0 0 0 0-1H10.5a.5.5 0 1 0 0 1z"/>
</svg>
PK
!<%��kk1chrome/devtools/skin/images/debugger-step-out.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b">
  <path d="M5 13.5H1a.5.5 0 1 0 0 1h4a.5.5 0 1 0 0-1zM12 13.5H8a.5.5 0 1 0 0 1h4a.5.5 0 1 0 0-1zM6.11 5.012A.427.427 0 0 1 6.21 5h7.083L9.646 1.354a.5.5 0 1 1 .708-.708l4.5 4.5a.498.498 0 0 1 0 .708l-4.5 4.5a.5.5 0 0 1-.708-.708L13.293 6H6.5v5.5a.5.5 0 1 1-1 0v-6a.5.5 0 0 1 .61-.488z"/>
</svg>
PK
!<=Hz��2chrome/devtools/skin/images/debugger-step-over.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b">
  <path d="M13.297 6.912C12.595 4.39 10.167 2.5 7.398 2.5A5.898 5.898 0 0 0 1.5 8.398a.5.5 0 0 0 1 0A4.898 4.898 0 0 1 7.398 3.5c2.75 0 5.102 2.236 5.102 4.898v.004L8.669 7.029a.5.5 0 0 0-.338.942l4.462 1.598a.5.5 0 0 0 .651-.34.506.506 0 0 0 .02-.043l2-5a.5.5 0 1 0-.928-.372l-1.24 3.098z"/>
  <circle cx="7" cy="12" r="1"/>
</svg>
PK
!< �j���:chrome/devtools/skin/images/debugger-toggleBreakpoints.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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="32" height="16" viewBox="0 0 32 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b">
  <path d="M3.233 11.25l-.417 1H1.712C.763 12.25 0 11.574 0 10.747V6.503C0 5.675.755 5 1.712 5h4.127l-.417 1H1.597C1.257 6 1 6.225 1 6.503v4.244c0 .277.267.503.597.503h1.636zM7.405 11.02L7 12.056c.865.01 2.212-.024 2.315-.04.112-.016.112-.016.185-.035.075-.02.156-.046.251-.082.152-.056.349-.138.592-.244.415-.182.962-.435 1.612-.744l.138-.066a179.35 179.35 0 0 0 2.255-1.094c1.191-.546 1.191-2.074-.025-2.632l-.737-.34A3547.554 3547.554 0 0 0 9.732 5c-.029.11-.065.222-.11.336l-.232.596c.894.408 4.56 2.107 4.56 2.107.458.21.458.596 0 .806L9.197 11.02H7.405zM20.462 14.192l5-12a.5.5 0 0 0-.924-.384l-5 12a.5.5 0 0 0 .924.384zM19.233 11.25l-.417 1h-1.104c-.949 0-1.712-.676-1.712-1.503V6.503C16 5.675 16.755 5 17.712 5h4.127l-.417 1h-3.825c-.34 0-.597.225-.597.503v4.244c0 .277.267.503.597.503h1.636zM23.405 11.02L23 12.056c.865.01 2.212-.024 2.315-.04.112-.016.112-.016.185-.035.075-.02.156-.046.251-.082.152-.056.349-.138.592-.244.415-.182.962-.435 1.612-.744l.138-.066a179.35 179.35 0 0 0 2.255-1.094c1.191-.546 1.191-2.074-.025-2.632l-.737-.34A3547.554 3547.554 0 0 0 25.732 5c-.029.11-.065.222-.11.336l-.232.596c.894.408 4.56 2.107 4.56 2.107.458.21.458.596 0 .806l-4.753 2.174h-1.792z"/>
</svg>
PK
!<�jħ�0chrome/devtools/skin/images/debugging-addons.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M14.5 8c-.971 0-1 1-1.75 1a.765.765 0 0 1-.75-.75V5a1 1 0 0 0-1-1H7.75A.765.765 0 0 1 7 3.25c0-.75 1-.779 1-1.75C8 .635 7.1 0 6 0S4 .635 4 1.5c0 .971 1 1 1 1.75a.765.765 0 0 1-.75.75H1a1 1 0 0 0-1 1v2.25A.765.765 0 0 0 .75 8c.75 0 .779-1 1.75-1C3.365 7 4 7.9 4 9s-.635 2-1.5 2c-.971 0-1-1-1.75-1a.765.765 0 0 0-.75.75V15a1 1 0 0 0 1 1h3.25a.765.765 0 0 0 .75-.75c0-.75-1-.779-1-1.75 0-.865.9-1.5 2-1.5s2 .635 2 1.5c0 .971-1 1-1 1.75a.765.765 0 0 0 .75.75H11a1 1 0 0 0 1-1v-3.25a.765.765 0 0 1 .75-.75c.75 0 .779 1 1.75 1 .865 0 1.5-.9 1.5-2s-.635-2-1.5-2z"></path>
</svg>
PK
!<I�[���.chrome/devtools/skin/images/debugging-tabs.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16">
  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M15 11h-1V5a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v6H1a1 1 0 0 0 0 2h14a1 1 0 1 0 0-2z"></path>
</svg>
PK
!<A�jE��1chrome/devtools/skin/images/debugging-workers.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 height="16" width="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
<path d="M14.6,6.1L13.5,5l0,0c0.1-0.1,0.2-0.4,0.2-0.6c0-0.2-0.1-0.4-0.2-0.6l-0.4-0.4c-0.3-0.3-0.8-0.3-1.1,0l0,0
	L10.5,2c-0.2-0.2-0.3-0.2-0.5-0.2c-0.2,0-0.3,0.1-0.5,0.2L8.3,3.2C8.1,3.3,8.1,3.4,8.1,3.6S8.2,4,8.3,4.1l1.6,1.6L7.8,7.8L5.6,5.7
	l1.5-1.5C7.3,4,7.4,3.8,7.4,3.6c0-0.2-0.1-0.4-0.2-0.6l-1-1C5.8,1.7,5.3,1.7,5,2L0.9,6.1C0.7,6.3,0.6,6.5,0.6,6.7
	c0,0.2,0.1,0.4,0.2,0.6l1,1c0.3,0.3,0.9,0.3,1.2,0l1.4-1.4l2,2.1l-3.4,3.3c-0.3,0.3-0.3,0.8,0,1.1l0.3,0.3c0.3,0.3,0.8,0.3,1.1,0
	l3.3-3.4l3.3,3.4c0.1,0.1,0.3,0.2,0.6,0.2c0.2,0,0.4-0.1,0.6-0.2l0.3-0.3c0.3-0.3,0.3-0.8,0-1.1L9,9l2-2.1l1.4,1.4
	c0.1,0.1,2.3,1.1,2.7,0.7C15.5,8.6,14.8,6.3,14.6,6.1z"/>
</svg>
PK
!<�ة)��$chrome/devtools/skin/images/diff.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b">
  <path d="M6 13A5 5 0 1 0 6 3a5 5 0 0 0 0 10zm0-.91a4.09 4.09 0 1 1 0-8.18 4.09 4.09 0 0 1 0 8.18z"/>
  <path d="M10 13a5 5 0 1 0 0-10 5 5 0 0 0 0 10zm0-.91a4.09 4.09 0 1 1 0-8.18 4.09 4.09 0 0 1 0 8.18z"/>
  <path d="M7.146 8.854l1 1a.5.5 0 0 0 .708-.708l-1-1a.5.5 0 1 0-.708.708zM7.146 6.854l1 1a.5.5 0 1 0 .708-.708l-1-1a.5.5 0 1 0-.708.708z"/>
  <path d="M12.656 11.723c-2.044 1.169-3.872 1.015-4.282.577-.41-.438 2.115-1.269 2.115-3.925 0-2.657-2.115-4.827-2.115-4.827s2.919-.47 4.282.624c1.364 1.094 2.12 1.975 1.85 3.828-.103.703.194 2.555-1.85 3.723z" fill-opacity=".3"/>
</svg>
PK
!<��~ss+chrome/devtools/skin/images/dock-bottom.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b">
  <path d="M10.004 3H.996C.999 3 1 3 1 3.002v9.996c0-.001.003.002-.004.002h9.008c-.003 0-.004 0-.004-.002V3.002c0 .001-.003-.002.004-.002zm0-1c.55 0 .996.456.996 1.002v9.996A.998.998 0 0 1 10.004 14H.996C.446 14 0 13.544 0 12.998V3.002A.998.998 0 0 1 .996 2h9.008zm-.41 8H.996v1h9.01v-1h-.41z"/>
</svg>
PK
!<;
|���)chrome/devtools/skin/images/dock-side.svg<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b">
  <path d="M1 2.996v9.008c0-.003 0-.004.002-.004h9.996c-.001 0 .002-.003.002.004V2.996c0 .003 0 .004-.002.004H1.002C1.003 3 1 3.003 1 2.996zm-1 0C0 2.446.456 2 1.002 2h9.996A.998.998 0 0 1 12 2.996v9.008c0 .55-.456.996-1.002.996H1.002A.998.998 0 0 1 0 12.004V2.996zm8 .413V12h1V3H8v.41z"/>
</svg>
PK
!<��~mUU+chrome/devtools/skin/images/dock-undock.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b">
  <path d="M13.003 1.941H6.997c.008 0 .003.004.003.008v6.102c0 .004.004.008-.003.008h6.006c-.008 0-.003-.004-.003-.008V1.949c0-.004-.004-.008.003-.008zm0-.941c.55 0 .997.43.997.95v6.1c0 .525-.453.95-.997.95H6.997C6.447 9 6 8.57 6 8.05v-6.1c0-.525.453-.95.997-.95h6.006z"/>
  <path d="M9 9.91v-.278h1v1.183c0 .516-.453.935-.997.935H2.997c-.55 0-.997-.43-.997-.95V4.7c0-.525.444-.95 1.006-.95h2.288v.941H3.006C3 4.691 3 4.691 3 4.7v6.102c0 .004.004.008-.003.008h6.006c-.004 0-.003-.001-.003.006v-.248-.657-.278h1v1.183c0 .516-.453.935-.997.935H2.997c-.55 0-.997-.43-.997-.95V4.7c0-.525.444-.95 1.006-.95h2.288v.941H3.006C3 4.691 3 4.691 3 4.7v6.102c0 .004.004.008-.003.008h6.006c-.004 0-.003-.001-.003.006v-.248-.657z"/>
  <path d="M12.52 5H6.976v1h6.046V5zM6.5 7H2.975v1H7V7z"/>
</svg>
PK
!<�]�xbb*chrome/devtools/skin/images/dropmarker.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="8" height="4" viewBox="0 0 8 4">
  <polygon points="0,0 4,4 8,0" fill="#b6babf"/>
</svg>
PK
!<�Ldd,chrome/devtools/skin/images/editor-error.png�PNG


IHDR�w=�+IDATx���J#Q�/ T�$�=��{��&R\�%\|��.mg�*E�%ϰYz)���˅�"�3g�9�w��Ԕ��q��1�jq�^�����K:<����Q���WŬ`z%���C�����cE�z͓4}:b���-��R*=�8F��!ƃF�N��w�R��M_�x�€��$p^nl����A�c��/X0a{��XB��:=�:;�����!_]�FOM,c�`;���5E��`�ϭ�
3��K@DL�=�V{a�7l����67Ib�
��l�VE����Q�T�j�`�v�f(����}�J��2�3<�E�19j��w��KL���W�$����k'��H}}44ġ�!br�ыo�4���},���N�Bn|<R*�^���X?l���2#�����9�w�m�"&G�3/l���2	�F"����fܠd�!brU=u~�9�	���*�2��	��n/br�|o�67����V�oX^��w����^��N��
��F�x�0�2���ZZ�VV<�'���#���vg����Q���8F*e״�+cW�$�B��Pf.`f��ٿ�19j~E<xa��	۩��/
&�O���mÍ��	�A94/br�衷ʋ�l?ö��}֕�L�1���FO�����&l���V���>?z.��`ı
��������ᨹ�A�����}I��k�;X�� 
--�v��a{x"�Պ\U�y��
�
��I�?�C���:85z�5O*�?��A��P��ޮ۳݊��2"&G�SX}�?���oG��IEND�B`�PK
!<w%�3chrome/devtools/skin/images/filetypes/dir-close.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" stroke="#0b0b0b" fill="none" fill-rule="evenodd">
  <path d="M.5 5.5v-2c0-.553.448-1 1-1H4c.553 0 1.268.358 1.6.802L6.5 4.5h5.997c.554 0 1.003.446 1.003.998v7.004c0 .55-.447.998-1 .998h-11c-.553 0-1-.453-1-.997V5.5zM1.5 6.5h11"/>
</svg>
PK
!<@��,��2chrome/devtools/skin/images/filetypes/dir-open.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" stroke="#0b0b0b" fill="none" fill-rule="evenodd">
  <path d="M.5 5.5v-2c0-.553.448-1 1-1H4c.553 0 1.268.358 1.6.802L6.5 4.5h5.997c.554 0 1.003.443 1.003 1v2H4.495c-.55 0-1.192.394-1.444.898l-2.1 4.204c-.25.496-.45.445-.45-.1V5.5z"/>
  <path d="M.5 12v.508c0 .548.456.992 1.002.992h9.996c.553 0 1.2-.394 1.45-.898l2.103-4.204c.25-.496.004-.898-.55-.898H13"/>
</svg>
PK
!<��ǽ��/chrome/devtools/skin/images/filetypes/globe.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b">
  <path d="M8.507 3.508l-1.978.003-.107.107v1.334l.848.891.455.031.107-.107v-.57l.75-.705.032-.877zM11.675 8.811l-1.389-1.348h-1.49l-.52.476-.032 1.023.665.708 1.22.032-.107-.107v1.108l.897.942.29.031.108-.107v-1.066l.39-.345.03-.855-.107.107h.24l.377-.334v-.151l-.116-.114-.531-.031z"/>
  <path d="M7.973 13.145a5.177 5.177 0 0 1-5.171-5.172c0-.034 0-.034.002-.066v-.003-.03l.17-.081.667.54.453.031.458.502-.03.832.291.352h.434l.105.088-.03 1.752.483.512h.097l.023-.31.927-.88.05-.435.489-.427v-.82l-.543-.035-1.032-1.066H4.29l-.142-.084v-.796l.142-.093h1.074v-.251l.11-.105.648.03.455-.408.031-.69-.576-.603h-.668v.26l-.102.064h-.826l-.105-.084.023-.59.442-.397v-.226h-.352c-.094 0-.14-.119-.073-.184a5.154 5.154 0 0 1 7.005-.187.104.104 0 0 1-.069.184l-1.056-.031-.745.74.48.484v.148l-.354.357h-.146l-.162-.163v-.148l.07-.07-.232-.232-.342.334.49.49c.065.066.004.18-.09.179l-.694-.004v.779h.723l.03-.516.147-.133h.148l.574.562v.248l.533.025h.003l.166-.028 1.14 1.143.695-.653h.353l.107.1c0 .018.003.015.003.033v.002c0 .028-.002.028-.001.058a5.179 5.179 0 0 1-5.174 5.172M7.973 2A5.98 5.98 0 0 0 2 7.974a5.98 5.98 0 0 0 5.973 5.973 5.98 5.98 0 0 0 5.974-5.974A5.98 5.98 0 0 0 7.973 2"/>
</svg>
PK
!<�(�==-chrome/devtools/skin/images/filter-swatch.svg<?xml version="1.0" encoding="UTF-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 xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12" width="12px" height="12px">
  <mask id="mask">
    <rect width="100%" height="100%" fill="#fff"/>
    <polygon points="12,0 0,0 0,12"/>
  </mask>
  <circle cx="6" cy="6" r="6" fill="#fff"/>
  <circle cx="6" cy="6" r="6" mask="url(#mask)" fill="#aeb0b1"/>
</svg>
PK
!<�-&chrome/devtools/skin/images/filter.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16" fill="context-fill #0b0b0b">
  <style>
    /* Use a fill that's visible on both light and dark themes for filter inputs */
    #filterinput:target + #icon {
      fill: #aaa;
    }
  </style>
  <g id="filterinput"/>
  <g id="icon">
    <path fill-opacity=".3" d="M6.6 8.4c0-.6-1.7.3-1.7-.3 0-.4-1.7-2.7-1.7-2.7H13s-1.8 2-1.8 2.7c0 .3-2.1-.1-2.1.3v6.1H7s-.4-4.1-.4-6.1z"/>
    <path d="M2 2v2.3L4.7 9H6v5.4l2.1 1 1.8-.9V9h1.3L14 4.3V2H2zm11 2l-2.2 4H9v5.8l-.9.4-1.1-.5V8H5.2L3 4V3h10v1z"/>
  </g>
</svg>
PK
!<kiL�FF'chrome/devtools/skin/images/filters.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 height="0" xmlns="http://www.w3.org/2000/svg">
  <!-- DevTools icon filter: Photon Blue 60 (light theme checked) -->
  <filter id="icon-checked-light">
    <feColorMatrix in="SourceGraphic" type="matrix"
      values="0 0 0 0 0
              0 0 0 0 0.122
              0 0 0 0 0.74
              0 0 0 1 0"/>
  </filter>
  <!-- DevTools icon filter: syntax blue #75BFFF (dark theme checked) -->
  <filter id="icon-checked-dark">
    <feColorMatrix in="SourceGraphic" type="matrix"
      values="0 0 0 0.18 0
              0 0 0 0.52 0
              0 0 0 1 0
              0 0 0 1 0"/>
  </filter>

  <!-- Web Audio Gradients -->
  <linearGradient id="bypass-light" x1="8%" y1="10%" x2="16%" y2="16%" spreadMethod="repeat">
    <stop offset="0%" stop-color="#dde1e4a0"/> <!-- theme-splitter-color (0.5 opacity) -->
    <stop offset="50%" stop-color="transparent"/>
  </linearGradient>

  <linearGradient id="bypass-dark" x1="8%" y1="10%" x2="16%" y2="16%" spreadMethod="repeat">
    <stop offset="0%" stop-color="#454d5d"/> <!-- theme-splitter-color -->
    <stop offset="50%" stop-color="transparent"/>
  </linearGradient>
</svg>
PK
!<B�W���2chrome/devtools/skin/images/firebug/arrow-down.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="7" height="7">
  <path d="M1.774 4.486L.257 1.86-1.259-.768h6.067L3.29 1.859z" transform="matrix(.88859 0 0 1.0498 1.923 1.549)" stroke-linejoin="round" fill="#39424a" stroke="#39424a"/>
</svg>
PK
!<�Ez���0chrome/devtools/skin/images/firebug/arrow-up.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="7" height="7">
  <path d="M1.774 4.486L.257 1.86-1.259-.768h6.067L3.29 1.859z" transform="matrix(.88859 0 0 -1.0498 1.923 5.452)" stroke-linejoin="round" fill="#39424a" stroke="#39424a"/>
</svg>
PK
!<�'��2chrome/devtools/skin/images/firebug/breakpoint.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:xlink="http://www.w3.org/1999/xlink" width="12" height="12">
  <defs>
    <linearGradient id="a">
      <stop offset="0" stop-color="#c80000"/>
      <stop offset="1" stop-color="#780000"/>
    </linearGradient>
    <radialGradient cx="4.8" cy="4.665" r="5.59" fx="4.8" fy="4.665" id="b" xlink:href="#a" gradientUnits="userSpaceOnUse"/>
  </defs>
  <path d="M11.553 5.995a5.59 5.59 0 1 1-11.18 0 5.59 5.59 0 1 1 11.18 0z" transform="translate(-.4 -.435) scale(1.0734)" fill="url(#b)"/>
</svg>
PK
!<u�[<<-chrome/devtools/skin/images/firebug/close.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16">
  <defs>
    <linearGradient id="a">
      <stop offset="0" stop-color="#f2451d"/>
      <stop offset=".101" stop-color="#f01428" stop-opacity=".8"/>
      <stop offset=".897" stop-color="#de8493"/>
      <stop offset="1" stop-color="#efc3cc"/>
    </linearGradient>
    <linearGradient id="b">
      <stop offset="0" stop-color="#520e0d"/>
      <stop offset="1" stop-color="#c4181d"/>
    </linearGradient>
    <linearGradient x1="8.769" y1="1049.931" x2="8.769" y2="1038.668" id="c" xlink:href="#a" gradientUnits="userSpaceOnUse" gradientTransform="translate(0 -1036.362)"/>
    <linearGradient x1="7.231" y1="1051.323" x2="7.231" y2="1037.401" id="d" xlink:href="#b" gradientUnits="userSpaceOnUse" gradientTransform="translate(0 -1036.362)"/>
    <filter x="-.24" y="-.24" width="1.48" height="1.48" color-interpolation-filters="sRGB" id="e">
      <feGaussianBlur stdDeviation=".713"/>
    </filter>
  </defs>
  <rect width="13" height="13" rx="2" ry="2" x="1.5" y="1.5" fill="url(#c)" stroke="url(#d)" stroke-linejoin="round"/>
  <path d="M6.5 5.437L4.938 7l2 2-2 2L6.5 12.562l2-2 2 2L12.064 11l-2-2 2-2L10.5 5.437l-2 2-2-2z" opacity=".4" filter="url(#e)"/>
  <path d="M6 4.438L4.437 6l2 2-2 2L6 11.563l2-2 2 2L11.563 10l-2-2 2-2L10 4.437l-2 2-2-2z" fill="#fff"/>
</svg>
PK
!<D�7chrome/devtools/skin/images/firebug/command-console.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16">
  <defs>
    <linearGradient id="a">
      <stop offset="0" stop-color="#325de6"/>
      <stop offset="1" stop-color="#8ba3f1"/>
    </linearGradient>
    <linearGradient id="b">
      <stop offset="0" stop-color="#1a47d6"/>
      <stop offset="1" stop-color="#6786ed"/>
    </linearGradient>
    <linearGradient x1="7.771" y1="13.61" x2="7.771" y2="2.16" id="e" xlink:href="#a" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-.70047 0 0 .8 8.145 1037.962)"/>
    <linearGradient x1="7.21" y1="14.919" x2="7.21" y2="1.081" id="f" xlink:href="#b" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-.70047 0 0 .8 8.145 1037.962)"/>
    <linearGradient id="c">
      <stop offset="0" stop-color="#282828"/>
      <stop offset="1" stop-color="#505050"/>
    </linearGradient>
    <linearGradient id="d">
      <stop offset="0"/>
      <stop offset="1" stop-color="#3c3c3c"/>
    </linearGradient>
    <linearGradient x1="7.771" y1="15.451" x2="7.771" y2="3.941" id="g" xlink:href="#c" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-.9204 0 0 .56 15.804 1039.472)"/>
    <linearGradient x1="7.21" y1="16.411" x2="7.21" y2="3.037" id="h" xlink:href="#d" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-.9204 0 0 .56 15.804 1039.472)"/>
  </defs>
  <g stroke-linejoin="round">
    <path d="M1.14 1039.162l4.56 5.167-4.56 5.233-.84-.8 3.166-4.433-3.166-4.367z" fill="url(#e)" stroke="url(#f)" stroke-width=".6" transform="translate(0 -1036.362)"/>
    <path d="M5.688 1040.05v1.125h10.125v-1.125H5.687zm2.5 3.75v1.125h7.624v-1.125H8.189zm-2.5 3.75v1.125h10.125v-1.125H5.687z" style="marker:none" color="#000" fill="url(#g)" stroke="url(#h)" stroke-width=".4" overflow="visible" transform="translate(0 -1036.362)"/>
  </g>
</svg>
PK
!<���||:chrome/devtools/skin/images/firebug/command-eyedropper.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 16 16" height="16" width="16">
  <defs>
    <linearGradient id="e">
      <stop offset="0" stop-color="#e97f7f"/>
      <stop offset="1" stop-color="#efa1a1"/>
    </linearGradient>
    <linearGradient id="c">
      <stop offset="0" stop-color="#e2e9ea"/>
      <stop offset="1" stop-color="#fff"/>
    </linearGradient>
    <linearGradient id="d">
      <stop offset="0" stop-color="#65888b"/>
      <stop offset="1" stop-color="#91adaf"/>
    </linearGradient>
    <linearGradient id="b">
      <stop offset="0" stop-color="#746b54"/>
      <stop offset="1" stop-color="#454033"/>
    </linearGradient>
    <linearGradient id="a">
      <stop offset="0" stop-color="#8c7f64"/>
      <stop offset="1" stop-color="#5d5543"/>
    </linearGradient>
    <radialGradient xlink:href="#a" id="h" cx="8.847" cy="1.845" fx="8.847" fy="1.845" r="1.587" gradientTransform="matrix(1.27453 .37742 -.46407 1.55272 -1.03 -4.65)" gradientUnits="userSpaceOnUse"/>
    <linearGradient gradientTransform="matrix(1.11829 0 0 1.11313 -.505 -.258)" xlink:href="#b" id="i" x1="8.531" y1=".95" x2="9.908" y2="4.42" gradientUnits="userSpaceOnUse"/>
    <linearGradient gradientTransform="matrix(1.11794 0 0 1.11348 .554 .12)" xlink:href="#b" id="k" x1="-6.81" y1="-1.866" x2="-12.495" y2="-1.812" gradientUnits="userSpaceOnUse"/>
    <linearGradient gradientTransform="matrix(1.11794 0 0 1.11348 .554 .12)" xlink:href="#a" id="j" x1="-7.216" y1="-2.356" x2="-12.008" y2="-2.36" gradientUnits="userSpaceOnUse"/>
    <linearGradient gradientTransform="matrix(1.11829 0 0 1.11313 -.505 -.258)" xlink:href="#c" id="f" x1="7.153" y1="11.831" x2="6.271" y2="11.424" gradientUnits="userSpaceOnUse"/>
    <linearGradient gradientTransform="matrix(1.11829 0 0 1.11313 -.505 -.258)" xlink:href="#d" id="g" x1="8.328" y1="9.463" x2="6.703" y2="8.785" gradientUnits="userSpaceOnUse"/>
    <linearGradient xlink:href="#e" id="l" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1.11829 0 0 1.11313 -.505 -.258)" x1="7.153" y1="11.831" x2="6.271" y2="11.424"/>
  </defs>
  <path d="M6.705 15.7c1.12-1.51 1.556-2.841 1.973-4.387.502-1.793.766-3.668 1.251-5.471l-1.73-.462c-.485 1.804-1.194 3.562-1.665 5.363-.406 1.532-.717 2.951-.495 4.78z" fill="url(#f)" stroke="url(#g)" stroke-width=".4" stroke-linejoin="round"/>
  <path d="M10.647 4.88a1.677 2.784 15.142 0 0 .76-1.525 1.677 2.784 15.142 0 0-.896-3.12 1.677 2.784 15.142 0 0-2.344 2.256 1.677 2.784 15.142 0 0-.109 1.7l2.589.69z" fill="url(#h)" stroke="url(#i)" stroke-width=".4" stroke-linejoin="round"/>
  <rect width="6.708" height="1.113" x="-13.565" y="-3.086" rx=".5" ry=".5" transform="rotate(-165.066) skewX(-.132)" fill="url(#j)" stroke="url(#k)" stroke-width=".4" stroke-linejoin="round"/>
  <path d="M8.558 10.973l-2.003.496c-.315 1.252-.487 2.44-.332 3.901l.406.107c1.03-1.428 1.416-2.582 1.86-4.226.028-.107.05-.199.07-.278z" fill="url(#l)"/>
</svg>
PK
!<�[�Occ6chrome/devtools/skin/images/firebug/command-frames.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 16 16" height="16" width="16">
  <defs>
    <linearGradient id="b">
      <stop offset="0" stop-color="#fff"/>
      <stop offset="1" stop-color="#f0f0f0"/>
    </linearGradient>
    <linearGradient id="c">
      <stop offset="0" stop-color="#e1e8ff"/>
      <stop offset="1" stop-color="#b9c9ff"/>
    </linearGradient>
    <linearGradient id="a">
      <stop offset="0" stop-color="#505050"/>
      <stop offset="1" stop-color="#787878"/>
    </linearGradient>
    <linearGradient gradientTransform="translate(.078 -1.018) scale(1.07692)" gradientUnits="userSpaceOnUse" y2="2.767" x2="1.624" y1="14.154" x1="13.01" id="d" xlink:href="#a"/>
    <linearGradient gradientUnits="userSpaceOnUse" y2="12.503" x2="12.396" y1="3.285" x1="3.179" id="e" xlink:href="#b"/>
    <linearGradient y2="12.503" x2="12.396" y1="3.285" x1="3.179" gradientUnits="userSpaceOnUse" id="f" xlink:href="#c"/>
  </defs>
  <rect ry="1" rx="1" y="1" x="1" height="14" width="14" fill="url(#d)"/>
  <path d="M7 2h7v3H7zM2 2h4v12H2z" fill="url(#e)"/>
  <path d="M7 6h7v8H7z" fill="url(#f)"/>
</svg>
PK
!<f|�NN7chrome/devtools/skin/images/firebug/command-measure.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 16 16" height="16" width="16">
  <defs>
    <linearGradient id="b">
      <stop offset="0" stop-color="#505424"/>
      <stop offset="1" stop-color="#6c6f31"/>
    </linearGradient>
    <linearGradient id="a">
      <stop offset="0" stop-color="#e8eace"/>
      <stop offset="1" stop-color="#f5f6ea"/>
    </linearGradient>
    <linearGradient id="c">
      <stop offset="0" stop-color="#484a2e"/>
      <stop offset="1" stop-color="#61633d"/>
    </linearGradient>
    <linearGradient xlink:href="#a" id="d" x1="-.831" y1="11.595" x2="2.378" y2="1.336" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1.2174 0 0 1 -11.66 -4.6)"/>
    <linearGradient xlink:href="#b" id="e" x1="1.536" y1="14.334" x2="5.493" y2="1.678" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1.2174 0 0 1 -11.66 -4.6)"/>
    <linearGradient xlink:href="#c" id="f" x1="1.695" y1="14.28" x2="6.421" y2="1.672" gradientUnits="userSpaceOnUse" gradientTransform="rotate(-90 3.4 8)"/>
  </defs>
  <g transform="translate(4.6 .6)">
    <rect transform="rotate(-90)" ry=".5" rx=".5" y="-3.4" x="-10.2" height="13.6" width="5.6" fill="url(#d)" stroke="url(#e)" stroke-width=".4" stroke-linejoin="round"/>
    <path d="M-2.1 10.4v-3m11 3v-3m-2.75 3V8.9M3.4 10.4v-3m-2.75 3V8.9" fill="none" stroke="url(#f)"/>
  </g>
</svg>
PK
!<�{����:chrome/devtools/skin/images/firebug/command-noautohide.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16">
  <defs>
    <linearGradient id="b">
      <stop stop-color="#747254" offset="0"/>
      <stop stop-color="#8b8965" offset="1"/>
    </linearGradient>
    <linearGradient id="a">
      <stop stop-color="#e1e1d7" offset="0"/>
      <stop stop-color="#f2f2ee" offset="1"/>
    </linearGradient>
    <linearGradient xlink:href="#a" id="g" x1="13" y1="13" x2="2.9" y2="4.8" gradientUnits="userSpaceOnUse" gradientTransform="translate(-2.28 -2.28) scale(1.1724)"/>
    <linearGradient xlink:href="#b" id="h" x1="15" y1="11.1" x2="4.8" y2="2.7" gradientUnits="userSpaceOnUse" gradientTransform="translate(-2.28 -2.28) scale(1.1724)"/>
    <linearGradient x1="8.6" y1="4.3" x2="6.3" y2=".6" id="i" xlink:href="#c" gradientUnits="userSpaceOnUse" gradientTransform="matrix(.62854 0 0 .6863 5.97 6.076)"/>
    <linearGradient id="c">
      <stop stop-color="#c8c8c8" offset="0"/>
      <stop stop-color="#dcdcdc" offset="1"/>
    </linearGradient>
    <linearGradient x1="7.2" y1="5.1" x2="5" y2="1.4" id="j" xlink:href="#d" gradientUnits="userSpaceOnUse" gradientTransform="matrix(.62854 0 0 .6863 5.97 6.076)"/>
    <linearGradient id="d">
      <stop stop-color="#787878" offset="0"/>
      <stop stop-color="#8c8c8c" offset="1"/>
    </linearGradient>
    <linearGradient x1="11.4" y1="1052.1" x2="4.6" y2="1040.7" id="k" xlink:href="#c" gradientUnits="userSpaceOnUse" gradientTransform="matrix(.6104 0 0 .57897 6.117 -593.406)"/>
    <linearGradient x1="8.8" y1="1053.4" x2="1.9" y2="1041.9" id="l" xlink:href="#d" gradientUnits="userSpaceOnUse" gradientTransform="matrix(.6104 0 0 .57897 6.117 -593.406)"/>
    <linearGradient x1="8.5" y1="12.5" x2="6.6" y2="7.8" id="m" xlink:href="#e" gradientUnits="userSpaceOnUse" gradientTransform="matrix(.7015 0 0 .70214 5.388 5.378)"/>
    <linearGradient id="e">
      <stop stop-color="#505050" offset="0"/>
      <stop stop-color="#787878" offset="1"/>
    </linearGradient>
    <linearGradient x1="9.4" y1="12.1" x2="7.4" y2="7.4" id="n" xlink:href="#f" gradientUnits="userSpaceOnUse" gradientTransform="matrix(.7015 0 0 .70214 5.388 5.378)"/>
    <linearGradient id="f">
      <stop stop-color="#787878" offset="0"/>
      <stop stop-color="#b4b4b4" offset="1"/>
    </linearGradient>
  </defs>
  <path d="M9.5.3L11 2l1 .7h.7c.7 0 1.2.5 1.2 1.2v8.7c0 .7-.6 1.2-1.3 1.2H1.5c-.7 0-1.2-.6-1.2-1.3V4c0-.8.5-1.3 1.2-1.3H7l.8-.8L9.4.2z" fill="url(#g)" stroke="url(#h)" stroke-width=".6" stroke-linejoin="round"/>
  <path d="M11 6.3a3 3 0 0 0-3 3l.4-.2h1c0-.8.7-1.5 1.6-1.5 1 0 1.6.7 1.7 1.6h1l.2.2a3 3 0 0 0-3-3z" fill="url(#i)" stroke="url(#j)" stroke-width=".6" stroke-linejoin="round"/>
  <rect width="9.4" height="6.6" rx=".8" ry=".8" x="6.3" y="9.1" fill="url(#k)" stroke="url(#l)" stroke-width=".6" stroke-linejoin="round"/>
  <path d="M11 10.5a1.2 1.2 0 0 0-1.2 1.2c0 .5.4 1 1 1v1.6h.5v-1.5c.5 0 1-.6 1-1a1.2 1.2 0 0 0-1.3-1.3z" fill="url(#m)" stroke="url(#n)" stroke-width=".4" stroke-linejoin="round"/>
</svg>
PK
!<T�j �	�	=chrome/devtools/skin/images/firebug/command-paintflashing.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 16 16" height="16" width="16">
  <defs>
    <linearGradient id="d">
      <stop offset="0" stop-color="#8c8c8c"/>
      <stop offset="1" stop-color="#b4b4b4"/>
    </linearGradient>
    <linearGradient id="a">
      <stop offset="0" stop-color="#544024"/>
      <stop offset="1" stop-color="#8b6b3d"/>
    </linearGradient>
    <linearGradient id="b">
      <stop offset="0" stop-color="#a0a0a0"/>
      <stop offset="1" stop-color="#a0a0a0" stop-opacity="0"/>
    </linearGradient>
    <linearGradient id="e">
      <stop offset="0" stop-color="#a0a0a0"/>
      <stop offset="1" stop-color="#c8c8c8"/>
    </linearGradient>
    <linearGradient id="c">
      <stop offset="0" stop-color="#382b18"/>
      <stop offset="1" stop-color="#6f5631"/>
    </linearGradient>
    <linearGradient gradientUnits="userSpaceOnUse" y2="3.409" x2="6.735" y1="3.859" x1="9.123" id="f" xlink:href="#a"/>
    <linearGradient gradientUnits="userSpaceOnUse" y2="8.347" x2="2.4" y1="9.586" x1="13.352" id="j" xlink:href="#a"/>
    <linearGradient gradientUnits="userSpaceOnUse" y2="11.675" x2="7.974" y1="14.423" x1="7.974" id="l" xlink:href="#b" gradientTransform="translate(0 -.2)"/>
    <linearGradient xlink:href="#c" id="g" x1="9.32" y1="7.243" x2="6.728" y2="6.716" gradientUnits="userSpaceOnUse"/>
    <linearGradient xlink:href="#c" id="k" x1="12.451" y1="11.469" x2="1.56" y2="10.342" gradientUnits="userSpaceOnUse"/>
    <linearGradient xlink:href="#d" id="i" x1="13.218" y1="13.627" x2="2.686" y2="13.627" gradientUnits="userSpaceOnUse"/>
    <linearGradient xlink:href="#e" id="h" x1="12.607" y1="12.021" x2="3.321" y2="12.021" gradientUnits="userSpaceOnUse"/>
  </defs>
  <path d="M8 1c-.828 0-1.5 1.343-1.5 3v3.5c0 .554.446 1 1 1h1c.554 0 1-.446 1-1V4c0-1.657-.672-3-1.5-3z" fill="url(#f)" stroke="url(#g)" stroke-width=".4" stroke-linejoin="round"/>
  <rect width="11" height="5" x="2.5" y="10" rx=".5" ry=".5" fill="url(#h)" stroke="url(#i)" stroke-width=".4" stroke-linejoin="round"/>
  <rect width="12" height="2" x="2" y="8" rx=".5" ry=".5" fill="url(#j)" stroke="url(#k)" stroke-width=".4" stroke-linejoin="round"/>
  <path d="M11.5 10.8h1v4h-1zm-2 0h1v4h-1zm-2 0h1v4h-1zm-2 0h1v4h-1zm-2 0h1v4h-1z" fill="url(#l)"/>
</svg>
PK
!<hOd?{{4chrome/devtools/skin/images/firebug/command-pick.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16">
  <defs>
    <linearGradient id="a">
      <stop offset="0" stop-color="#6f9fdf"/>
      <stop offset="1" stop-color="#b8d0f1"/>
    </linearGradient>
    <linearGradient id="b">
      <stop offset="0" stop-color="#133cb8"/>
      <stop offset="1" stop-color="#7faae8"/>
    </linearGradient>
    <linearGradient x1="11.304" y1="9.268" x2="7.065" y2="4.197" id="d" xlink:href="#a" gradientUnits="userSpaceOnUse"/>
    <linearGradient x1="6.587" y1="7.594" x2="2.992" y2=".487" id="c" xlink:href="#b" gradientUnits="userSpaceOnUse"/>
  </defs>
  <path d="M.5.813c-.273.04-.502.354-.5.687v5.625c0 .36.265.688.563.688h4.406v-1h-3.5a.52.52 0 0 1-.5-.5V2.218a.517.517 0 0 1 .437-.5h3.75c.227-.4.634-.711 1.094-.75.415-.031.84.134 1.125.437l.313.313h6.843c.262 0 .5.238.5.5v4.094a.52.52 0 0 1-.5.5h-1.969l.938 1h1.938c.297 0 .562-.328.562-.688V1.5c0-.36-.265-.688-.563-.688H.5z" fill="#fff"/>
  <path d="M.5 0C.227.041-.002.355 0 .688v5.625C0 6.673.265 7 .563 7h4.406V6h-3.5a.52.52 0 0 1-.5-.5V1.406a.517.517 0 0 1 .437-.5h13.125c.262 0 .5.238.5.5V5.5a.52.52 0 0 1-.5.5h-2.75l.969 1h2.688c.297 0 .562-.328.562-.688V.688c0-.36-.265-.687-.563-.687H.5z" fill="url(#c)"/>
  <path d="M6.375 2.375v9.906l2.094-.844 1.625 3.844 2.531-1.062-1.594-3.781 2.188-.876L9.813 5.97 6.374 2.375z" fill="url(#d)" stroke="#4673ce" stroke-linejoin="round"/>
</svg>
PK
!<9���>chrome/devtools/skin/images/firebug/command-responsivemode.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 16 16" height="16" width="16">
  <defs>
    <linearGradient id="d">
      <stop offset="0" stop-color="#141414"/>
      <stop offset="1" stop-color="#3c3c3c"/>
    </linearGradient>
    <linearGradient id="c">
      <stop offset="0" stop-color="#cdf0ff"/>
      <stop offset="1" stop-color="#f5fcff"/>
    </linearGradient>
    <linearGradient id="b">
      <stop offset="0" stop-color="#787878"/>
      <stop offset="1" stop-color="#505050"/>
    </linearGradient>
    <linearGradient id="a">
      <stop offset="0" stop-color="#282828"/>
      <stop offset="1" stop-color="#505050"/>
    </linearGradient>
    <linearGradient gradientTransform="matrix(1.04615 0 0 1.06667 -.415 .667)" gradientUnits="userSpaceOnUse" y2="1.576" x2="3.337" y1="8.108" x1="14.078" id="e" xlink:href="#a"/>
    <linearGradient gradientTransform="matrix(2 0 0 2 -13 -4)" gradientUnits="userSpaceOnUse" y2="4.752" x2="13.239" y1="5.261" x1="13.748" id="h" xlink:href="#b"/>
    <linearGradient gradientTransform="translate(0 1)" gradientUnits="userSpaceOnUse" y2="1.854" x2="3.829" y1="8.432" x1="12.299" id="g" xlink:href="#c"/>
    <linearGradient gradientTransform="translate(-.5 -.5)" gradientUnits="userSpaceOnUse" y2="5.946" x2=".987" y1="15.102" x1="5.989" id="i" xlink:href="#a"/>
    <linearGradient gradientTransform="matrix(1.10989 0 0 .79278 -.4 .958)" gradientUnits="userSpaceOnUse" y2="10.774" x2="4.865" y1="10.774" x1="1.261" id="k" xlink:href="#c"/>
    <linearGradient gradientTransform="matrix(1.5 0 0 1.5 -17.25 6)" gradientUnits="userSpaceOnUse" y2="4.752" x2="13.239" y1="5.261" x1="13.748" id="l" xlink:href="#b"/>
    <linearGradient gradientTransform="matrix(.683 0 0 .6166 .985 2.273)" gradientUnits="userSpaceOnUse" y2="5.597" x2="2.826" y1="6.539" x1="3.079" id="m" xlink:href="#b"/>
    <linearGradient xlink:href="#d" id="f" x1="13.677" y1="12.328" x2=".512" y2="4.058" gradientUnits="userSpaceOnUse" gradientTransform="matrix(.97143 0 0 .96 .257 .24)"/>
    <linearGradient xlink:href="#d" id="j" x1="5.489" y1="14.602" x2=".487" y2="5.446" gradientUnits="userSpaceOnUse"/>
  </defs>
  <rect ry="1" rx="1" y="1.2" x="2.2" height="9.6" width="13.6" fill="url(#e)" stroke="url(#f)" stroke-width=".4" stroke-linejoin="round"/>
  <rect ry=".5" rx=".5" y="2.5" x="3.5" height="7" width="9" fill="url(#g)"/>
  <circle r="1" cy="6" cx="14" fill="url(#h)"/>
  <rect ry="1" rx="1" y="5" height="10" width="6" fill="url(#i)" stroke="url(#j)" stroke-width=".4" stroke-linejoin="round"/>
  <rect ry=".5" rx=".5" y="7" x="1" height="5" width="4" fill="url(#k)"/>
  <circle r=".75" cy="13.5" cx="3" fill="url(#l)"/>
  <rect ry=".5" rx=".5" y="5.75" x="2" height=".5" width="2" fill="url(#m)"/>
</svg>
PK
!<��`�BB6chrome/devtools/skin/images/firebug/command-rulers.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 16 16" height="16" width="16">
  <defs>
    <linearGradient id="b">
      <stop offset="0" stop-color="#e2e6ea"/>
      <stop offset="1" stop-color="#f9fafb"/>
    </linearGradient>
    <linearGradient id="a">
      <stop offset="0" stop-color="#323b46"/>
      <stop offset="1" stop-color="#546374"/>
    </linearGradient>
    <linearGradient gradientUnits="userSpaceOnUse" y2=".822" x2="4.016" y1="13.198" x1="8.663" id="d" xlink:href="#a"/>
    <linearGradient gradientUnits="userSpaceOnUse" y2="1.975" x2="2.064" y1="13.044" x1="6.113" id="c" xlink:href="#b"/>
    <linearGradient gradientUnits="userSpaceOnUse" y2="1.511" x2="3.948" y1="13.163" x1="7.795" id="e" xlink:href="#a"/>
  </defs>
  <path d="M1.7 1.2c-.278 0-.5.222-.5.5v12.6c0 .278.222.5.5.5h3.6c.278 0 .5-.222.5-.5V5.8h8.5c.278 0 .5-.222.5-.5V1.7c0-.278-.222-.5-.5-.5H1.7z" fill="url(#c)" stroke="url(#d)" stroke-width=".4" stroke-linejoin="round"/>
  <path d="M1 4.5h1.5M1 7.5h3m-3 3h1.5m-1.5 3h3M4.5 1v1.5m3-1.5v3m3-3v1.5m3-1.5v3" fill="none" stroke="url(#e)"/>
</svg>
PK
!<E"t�
�
:chrome/devtools/skin/images/firebug/command-scratchpad.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 16 16" height="16" width="16">
  <defs>
    <linearGradient id="e">
      <stop offset="0" stop-color="#434f5d"/>
      <stop offset="1" stop-color="#65788b"/>
    </linearGradient>
    <linearGradient id="d">
      <stop offset="0" stop-color="#787878"/>
      <stop offset="1" stop-color="#8c8c8c"/>
    </linearGradient>
    <linearGradient id="c">
      <stop offset="0" stop-color="#8c8c8c"/>
      <stop offset="1" stop-color="#a0a0a0"/>
    </linearGradient>
    <linearGradient id="b">
      <stop offset="0" stop-color="#b6b38a"/>
      <stop offset="1" stop-color="#d3d2bd"/>
    </linearGradient>
    <linearGradient id="a">
      <stop offset="0" stop-color="#ecebe0"/>
      <stop offset="1" stop-color="#fbfbf9" stop-opacity="0"/>
    </linearGradient>
    <linearGradient gradientTransform="translate(-1.018 -.726)" gradientUnits="userSpaceOnUse" y2="4.549" x2="4.08" y1="14.382" x1="13.934" id="f" xlink:href="#a"/>
    <linearGradient gradientTransform="translate(-1.018 -.726)" gradientUnits="userSpaceOnUse" y2="4.836" x2="1.893" y1="16.614" x1="13.78" id="g" xlink:href="#b"/>
    <linearGradient y2="2.41" x2="4.751" y1="4.023" x1="5.458" gradientTransform="translate(-1.018 -1.026)" gradientUnits="userSpaceOnUse" id="h" xlink:href="#c"/>
    <linearGradient y2=".94" x2="4.252" y1="3.313" x1="5.323" gradientTransform="translate(0 -.3)" gradientUnits="userSpaceOnUse" id="i" xlink:href="#d"/>
    <linearGradient gradientUnits="userSpaceOnUse" y2="9.29" x2="11.377" y1="9.29" x1="4.575" id="j" xlink:href="#e"/>
  </defs>
  <path style="marker:none" color="#000" overflow="visible" fill="url(#f)" stroke="url(#g)" stroke-linejoin="round" d="M2 2.75h12v12H2z"/>
  <path style="marker:none" d="M4 .75c-.553 0-1 .672-1 1.5 0 .829.399 1.474 1 1.5.106.006.2-.08.2-.25 0-.168-.11-.25-.2-.25-.277 0-.5-.447-.5-1 0-.552.223-1 .5-1 .275 0 .5.448.5 1H5c0-.828-.448-1.5-1-1.5z" id="k" color="#000" overflow="visible" fill="url(#h)" stroke="url(#i)" stroke-width=".2" stroke-linejoin="round"/>
  <path d="M4 11.45h7v1H4zm1-5.321h5v1H5zm1 2.66h6v1H6z" style="marker:none" color="#000" overflow="visible" fill="url(#j)"/>
  <use height="100%" width="100%" transform="translate(2.667)" id="l" xlink:href="#k"/>
  <use height="100%" width="100%" transform="translate(2.667)" id="m" xlink:href="#l"/>
  <use height="100%" width="100%" transform="translate(2.667)" xlink:href="#m"/>
</svg>
PK
!<_�e��:chrome/devtools/skin/images/firebug/command-screenshot.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16">
  <defs>
    <linearGradient id="e">
      <stop offset="0" stop-color="#65808b"/>
      <stop offset="1" stop-color="#7c939c"/>
    </linearGradient>
    <linearGradient id="a">
      <stop offset="0" stop-color="#43555d"/>
      <stop offset="1" stop-color="#566a72"/>
    </linearGradient>
    <linearGradient id="d">
      <stop offset="0" stop-color="#54a0ec"/>
      <stop offset="1" stop-color="#99c5f7"/>
    </linearGradient>
    <linearGradient id="c">
      <stop offset="0" stop-color="#f6fafe"/>
      <stop offset="1" stop-color="#99c5f7"/>
    </linearGradient>
    <linearGradient id="b">
      <stop offset="0" stop-color="#324046"/>
      <stop offset="1" stop-color="#45555b"/>
    </linearGradient>
    <linearGradient xlink:href="#a" id="h" x1="13.661" y1="12.474" x2="2.358" y2="5.025" gradientUnits="userSpaceOnUse" gradientTransform="matrix(.97143 0 0 .96 .229 .612)"/>
    <linearGradient xlink:href="#b" id="i" x1="16.505" y1="11.096" x2="2.974" y2="2.807" gradientUnits="userSpaceOnUse" gradientTransform="matrix(.97143 0 0 .96 .229 .612)"/>
    <linearGradient xlink:href="#b" id="k" x1="10.3" y1="11.02" x2="5.662" y2="6.382" gradientUnits="userSpaceOnUse" gradientTransform="translate(0 .236)"/>
    <linearGradient xlink:href="#a" id="j" x1="10.582" y1="9.815" x2="6.843" y2="6.172" gradientUnits="userSpaceOnUse" gradientTransform="translate(0 .236)"/>
    <radialGradient xlink:href="#c" id="l" cx="7.075" cy="7.944" fx="7.075" fy="7.944" r="2.5" gradientUnits="userSpaceOnUse" gradientTransform="rotate(45 9.338 9.078) scale(1.00392 1.2642)"/>
    <linearGradient xlink:href="#d" id="m" x1="8.873" y1="11.096" x2="5.628" y2="7.85" gradientUnits="userSpaceOnUse" gradientTransform="translate(0 .236)"/>
    <linearGradient xlink:href="#a" id="g" x1="10.226" y1="3.728" x2="6.522" y2="2.07" gradientUnits="userSpaceOnUse" gradientTransform="translate(0 .314)"/>
    <linearGradient xlink:href="#e" id="f" x1="9.212" y1="4.437" x2="6.127" y2="3.047" gradientUnits="userSpaceOnUse" gradientTransform="translate(0 .314)"/>
  </defs>
  <path d="M6.5 2.225c-.277 0-.641.14-.816.316L4.316 3.908c-.175.175-.093.317.184.317h7c.277 0 .359-.142.184-.317l-1.368-1.367c-.175-.175-.539-.316-.816-.316h-2z" fill="url(#f)" stroke="url(#g)" stroke-width=".4" stroke-linejoin="round"/>
  <rect ry="1" rx="1" y="4.2" x="1.2" height="9.6" width="13.6" fill="url(#h)" stroke="url(#i)" stroke-width=".4" stroke-linejoin="round"/>
  <circle r="3.5" cy="9" cx="8" fill="url(#j)" stroke="url(#k)" stroke-width=".4" stroke-linejoin="round"/>
  <circle cx="8" cy="9" r="2.5" fill="url(#l)" stroke="url(#m)" stroke-width=".4" stroke-linejoin="round"/>
</svg>
PK
!<`��\8chrome/devtools/skin/images/firebug/commandline-icon.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:xlink="http://www.w3.org/1999/xlink" width="14" height="14">
  <defs>
    <style>
      path {
        opacity: 0.5;
      }
      path:target {
        opacity: 1;
      }
    </style>
    <linearGradient id="b">
      <stop offset="0" stop-color="#234ccd"/>
      <stop offset="1" stop-color="#5d7de3"/>
    </linearGradient>
    <linearGradient id="a">
      <stop offset="0" stop-color="#1e3faa"/>
      <stop offset="1" stop-color="#3a61de"/>
    </linearGradient>
    <linearGradient x1="2.002" y1="12.252" x2="-.099" y2="6.755" id="d" xlink:href="#a" gradientUnits="userSpaceOnUse" gradientTransform="translate(5.841 1034.646)"/>
    <linearGradient x1="3.309" y1="11.177" x2="1.468" y2="6.456" id="c" xlink:href="#b" gradientUnits="userSpaceOnUse" gradientTransform="translate(5.841 1034.646)"/>
  </defs>
  <path id="focus" d="M6.841 1040.052l-.437.406 2.469 3.688-2.47 3.687.438.407 3.438-4.094z" fill="url(#c)" stroke="url(#d)" stroke-width=".4" stroke-linecap="round" stroke-linejoin="round" transform="translate(-1.341 -1037.146)"/>
</svg>
PK
!<|B+,,9chrome/devtools/skin/images/firebug/debugger-blackbox.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16">
  <defs>
    <linearGradient id="d">
      <stop offset="0" stop-color="#323232"/>
      <stop offset="1" stop-color="#646464"/>
    </linearGradient>
    <linearGradient id="c">
      <stop offset="0" stop-color="#b4b4b4"/>
      <stop offset="1" stop-color="#dcdcdc"/>
    </linearGradient>
    <linearGradient id="b">
      <stop offset="0" stop-color="#3c3c3c"/>
      <stop offset="1" stop-color="#646464"/>
    </linearGradient>
    <linearGradient id="a">
      <stop offset="0" stop-color="#505050"/>
      <stop offset="1" stop-color="#8c8c8c"/>
    </linearGradient>
    <linearGradient gradientTransform="matrix(.97143 0 0 1.08571 .229 -1125.879)" xlink:href="#a" id="e" x1="10.803" y1="1047.39" x2="4.726" y2="1041.559" gradientUnits="userSpaceOnUse"/>
    <linearGradient gradientTransform="matrix(.97143 0 0 1.08571 .229 -1125.879)" xlink:href="#b" id="f" x1="12.563" y1="1046.633" x2="5.974" y2="1040.229" gradientUnits="userSpaceOnUse"/>
    <linearGradient gradientTransform="translate(-1.333 -1210.423) scale(1.16667)" xlink:href="#c" id="g" x1="9.698" y1="1046.429" x2="5.893" y2="1042.623" gradientUnits="userSpaceOnUse"/>
    <linearGradient gradientTransform="translate(0 -1036.362)" xlink:href="#d" id="h" x1="9.023" y1="1045.897" x2="6.49" y2="1043.363" gradientUnits="userSpaceOnUse"/>
  </defs>
  <path d="M14.8 8c0 1.085-3.044 3.8-6.8 3.8S1.2 9.085 1.2 8 4.244 4.2 8 4.2s6.8 2.715 6.8 3.8z" style="marker:none" color="#000" overflow="visible" fill="url(#e)" stroke="url(#f)" stroke-width=".4" stroke-linejoin="round"/>
  <circle r="3.5" cy="8" cx="8" style="marker:none" color="#000" overflow="visible" fill="url(#g)"/>
  <circle r="2" cy="8" cx="8" style="marker:none" color="#000" overflow="visible" fill="url(#h)"/>
</svg>
PK
!<o�	�	<chrome/devtools/skin/images/firebug/debugger-prettyprint.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16">
  <defs>
    <linearGradient id="a">
      <stop offset="0" stop-color="#285a8c"/>
      <stop offset="1" stop-color="#508cc8"/>
    </linearGradient>
    <linearGradient id="b">
      <stop offset="0" stop-color="#1c3b5c"/>
      <stop offset="1" stop-color="#285078"/>
    </linearGradient>
    <linearGradient x1="17.286" y1="1046.293" x2="-18.065" y2="1003.191" id="c" xlink:href="#a" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-1 0 0 1 28.42 0)"/>
    <linearGradient x1="12.826" y1="1050.761" x2="-24.272" y2="1006.022" id="d" xlink:href="#b" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-1 0 0 1 28.42 0)"/>
  </defs>
  <path d="M21.734 1045.673v5.125h-1.816c-4.864 0-9.028-.723-10.688-2.168-1.64-1.445-2.46-4.326-2.46-8.643v-6.098c0-2.95-.528-4.99-1.583-6.123-1.054-1.133-2.968-1.7-5.742-1.7h-1.787v-4.189h1.787c2.793 0 4.707-.556 5.742-1.67 1.055-1.133 1.582-3.154 1.582-6.064v-6.127c0-4.317.82-7.188 2.461-8.613 1.66-1.446 5.824-2.168 10.688-2.168h1.816v5.093h-1.992c-2.754 0-4.55.43-5.39 1.29-.84.859-1.26 1.761-1.26 4.515v6.361c0 3.067-1.352 5.293-2.25 6.68-.88 1.387-1.49 2.324-3.64 2.813 2.169.527 2.79 1.484 3.669 2.87.879 1.387 2.22 3.604 2.22 6.65v6.363c0 2.754.42 3.654 1.26 4.514.84.859 2.96 1.289 5.391 1.289zm12.579 0v5.125h1.816c4.864 0 9.028-.723 10.688-2.168 1.64-1.445 2.46-4.326 2.46-8.643v-6.098c0-2.95.528-4.99 1.583-6.123 1.054-1.133 2.968-1.7 5.742-1.7h1.787v-4.189h-1.787c-2.793 0-4.707-.556-5.742-1.67-1.055-1.133-1.582-3.154-1.582-6.064v-6.127c0-4.317-.82-7.188-2.461-8.613-1.66-1.446-5.824-2.168-10.688-2.168h-1.816v5.093h1.992c2.754 0 4.55.43 5.39 1.29.84.859 1.26 1.761 1.26 4.515v6.361c0 3.067 1.352 5.293 2.25 6.68.88 1.387 1.49 2.324 3.64 2.813-2.169.527-2.79 1.484-3.669 2.87-.879 1.387-2.22 3.604-2.22 6.65v6.363c0 2.754-.42 3.654-1.26 4.514-.84.859-2.96 1.289-5.391 1.289z" style="-inkscape-font-specification:Fixedsys" font-size="60" fill="url(#c)" stroke="url(#d)" font-family="Fixedsys" transform="matrix(-.22157 0 0 .22103 14.138 -218.338)" font-weight="400" letter-spacing="0" word-spacing="0" stroke-width="1.807" stroke-linejoin="round"/>
</svg>
PK
!<�6�ww8chrome/devtools/skin/images/firebug/debugger-step-in.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16">
  <defs>
    <linearGradient id="c">
      <stop offset="0" stop-color="#dd8506"/>
      <stop offset="1" stop-color="#f4a24b"/>
    </linearGradient>
    <linearGradient id="b">
      <stop offset="0" stop-color="#e68507"/>
      <stop offset="1" stop-color="#f4b65f"/>
    </linearGradient>
    <linearGradient id="a">
      <stop offset="0" stop-color="#f3a952"/>
      <stop offset="1" stop-color="#fadbba"/>
    </linearGradient>
    <linearGradient x1="9.06" y1="13.305" x2="9.06" y2="1.704" id="d" xlink:href="#a" gradientUnits="userSpaceOnUse"/>
    <linearGradient x1="3.865" y1="14.919" x2="3.865" y2="13.049" id="f" xlink:href="#b" gradientUnits="userSpaceOnUse" gradientTransform="matrix(.9974 0 0 1.0026 -1.01 -.02)"/>
    <linearGradient x1="14.005" y1="14.902" x2="14.005" y2="13.07" id="g" xlink:href="#b" gradientUnits="userSpaceOnUse" gradientTransform="matrix(.9974 0 0 1.0026 -.959 -.02)"/>
    <linearGradient x1="10.576" y1="11.641" x2=".835" y2="1.901" id="e" xlink:href="#c" gradientUnits="userSpaceOnUse"/>
  </defs>
  <path d="M.534 2.46h3.864C7.45 2.46 9.5 4.288 9.5 7.028v3.565h1.961L8.076 13.5l-3.382-2.914h1.899V7.74c0-1.554-.866-2.492-2.966-2.492H.534z" fill="url(#d)" stroke="url(#e)" stroke-linejoin="round"/>
  <path fill="url(#f)" d="M1 13h4v2H1z"/>
  <path fill="url(#g)" d="M11 13h4v2h-4z"/>
</svg>
PK
!<p�9���9chrome/devtools/skin/images/firebug/debugger-step-out.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16">
  <defs>
    <linearGradient id="c">
      <stop offset="0" stop-color="#dd8506"/>
      <stop offset="1" stop-color="#f4a24b"/>
    </linearGradient>
    <linearGradient id="b">
      <stop offset="0" stop-color="#e68507"/>
      <stop offset="1" stop-color="#f4b65f"/>
    </linearGradient>
    <linearGradient id="a">
      <stop offset="0" stop-color="#f3a952"/>
      <stop offset="1" stop-color="#fadbba"/>
    </linearGradient>
    <linearGradient x1="-.161" y1="7.678" x2="12.316" y2="7.678" id="d" xlink:href="#a" gradientUnits="userSpaceOnUse" gradientTransform="rotate(-90 7.756 5.205)"/>
    <linearGradient x1="14.005" y1="14.902" x2="14.005" y2="13.07" id="g" xlink:href="#b" gradientUnits="userSpaceOnUse" gradientTransform="matrix(.9974 0 0 1.0026 -.959 -.02)"/>
    <linearGradient x1="3.865" y1="14.919" x2="3.865" y2="13.049" id="f" xlink:href="#b" gradientUnits="userSpaceOnUse" gradientTransform="matrix(.9974 0 0 1.0026 -1.01 -.02)"/>
    <linearGradient x1="11.034" y1="9.145" x2="6.593" y2="4.703" id="e" xlink:href="#c" gradientUnits="userSpaceOnUse" gradientTransform="translate(0 -1.116)"/>
  </defs>
  <path d="M6.486 12.5V7.009c0-3.051 1.555-4.548 4.295-4.548h1.73V.5l2.907 3.385-2.914 3.382V5.368H11.7c-1.554 0-2.222.518-2.222 2.618V12.5z" fill="url(#d)" stroke="url(#e)" stroke-linejoin="round"/>
  <path fill="url(#f)" d="M1 13h4v2H1z"/>
  <path fill="url(#g)" d="M11 13h4v2h-4z"/>
</svg>
PK
!<"c��:chrome/devtools/skin/images/firebug/debugger-step-over.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16">
  <defs>
    <linearGradient id="c">
      <stop offset="0" stop-color="#dd8506"/>
      <stop offset="1" stop-color="#f4a24b"/>
    </linearGradient>
    <linearGradient id="b">
      <stop offset="0" stop-color="#e68507"/>
      <stop offset="1" stop-color="#f4b65f"/>
    </linearGradient>
    <linearGradient id="a">
      <stop offset="0" stop-color="#f3a952"/>
      <stop offset="1" stop-color="#fadbba"/>
    </linearGradient>
    <linearGradient x1="9.06" y1="13.305" x2="9.06" y2="1.704" id="d" xlink:href="#a" gradientUnits="userSpaceOnUse" gradientTransform="translate(3)"/>
    <linearGradient x1="3.865" y1="14.919" x2="3.865" y2="13.049" id="f" xlink:href="#b" gradientUnits="userSpaceOnUse" gradientTransform="translate(3)"/>
    <linearGradient x1="12.911" y1="12.657" x2="2.554" y2="2.3" id="e" xlink:href="#c" gradientUnits="userSpaceOnUse"/>
  </defs>
  <path d="M4.698 2.46h3.7c3.052 0 5.102 1.828 5.102 4.568v3.565h1.962L12.077 13.5l-3.383-2.914h1.9V7.74c0-1.793-.486-2.454-2.047-2.492-.791-.02-1.842 0-2.647 0-1.821 0-2.368.81-2.368 2.488V12.5H.522S.518 9.04.518 7.03c0-2.72 2.209-4.57 4.179-4.57z" fill="url(#d)" stroke="url(#e)" stroke-linejoin="round"/>
  <path fill="url(#f)" d="M5.016 12.987h4.01v1.995h-4.01z"/>
</svg>
PK
!<{�^��Bchrome/devtools/skin/images/firebug/debugger-toggleBreakpoints.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:xlink="http://www.w3.org/1999/xlink" height="16" width="16">
  <defs>
    <linearGradient id="a">
      <stop offset="0" stop-color="#c80000"/>
      <stop offset="1" stop-color="#780000"/>
    </linearGradient>
    <radialGradient gradientUnits="userSpaceOnUse" xlink:href="#a" id="b" fy="4.665" fx="4.8" r="5.59" cy="4.665" cx="4.8"/>
  </defs>
  <path transform="translate(1.599 1.565) scale(1.07342)" d="M11.553 5.995a5.59 5.59 0 1 1-11.179 0 5.59 5.59 0 1 1 11.18 0z" fill="url(#b)"/>
</svg>
PK
!<�hL��/chrome/devtools/skin/images/firebug/disable.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="12" height="12">
  <path d="M5.563 0A6 6 0 0 0 0 6a6 6 0 0 0 12 0 6 6 0 0 0-6.438-6zm.156 2a4 4 0 0 1 2.25.5L2.5 7.97A4 4 0 0 1 2 6a4 4 0 0 1 3.72-4zm3.685 1.906A4 4 0 0 1 10 6a4 4 0 0 1-6.094 3.406l5.5-5.5z" fill="red"/>
</svg>
PK
!<�QV--3chrome/devtools/skin/images/firebug/dock-bottom.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16">
  <defs>
    <linearGradient id="b">
      <stop offset="0" stop-color="#f2451d"/>
      <stop offset=".101" stop-color="#f01428" stop-opacity=".8"/>
      <stop offset=".897" stop-color="#de8493"/>
      <stop offset="1" stop-color="#efc3cc"/>
    </linearGradient>
    <linearGradient id="a">
      <stop offset="0" stop-color="#520e0d"/>
      <stop offset="1" stop-color="#c4181d"/>
    </linearGradient>
    <linearGradient x1="7.231" y1="1051.323" x2="7.231" y2="1037.401" id="d" xlink:href="#a" gradientUnits="userSpaceOnUse" gradientTransform="translate(0 -1036.362)"/>
    <linearGradient x1="8.769" y1="1049.931" x2="8.769" y2="1038.668" id="c" xlink:href="#b" gradientUnits="userSpaceOnUse" gradientTransform="translate(0 -1036.362)"/>
    <filter height="1.48" y="-.24" width="1.48" x="-.24" id="e" color-interpolation-filters="sRGB">
      <feGaussianBlur stdDeviation=".8"/>
    </filter>
  </defs>
  <rect y="1.5" x="1.5" ry="2" rx="2" height="13" width="13" fill="url(#c)" stroke="url(#d)" stroke-linejoin="round"/>
  <path style="marker:none" d="M4.5 5v8h8V5zm1 1h6v4h-6z" color="#000" overflow="visible" opacity=".4" filter="url(#e)"/>
  <path style="marker:none" d="M4 4v8h8V4zm1 1h6v4H5z" color="#000" overflow="visible" fill="#fff"/>
</svg>
PK
!<k�^�LL1chrome/devtools/skin/images/firebug/dock-side.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16">
  <defs>
    <linearGradient id="b">
      <stop offset="0" stop-color="#f2451d"/>
      <stop offset=".101" stop-color="#f01428" stop-opacity=".8"/>
      <stop offset=".897" stop-color="#de8493"/>
      <stop offset="1" stop-color="#efc3cc"/>
    </linearGradient>
    <linearGradient id="a">
      <stop offset="0" stop-color="#520e0d"/>
      <stop offset="1" stop-color="#c4181d"/>
    </linearGradient>
    <linearGradient x1="7.231" y1="1051.323" x2="7.231" y2="1037.401" id="d" xlink:href="#a" gradientUnits="userSpaceOnUse" gradientTransform="translate(0 -1036.362)"/>
    <linearGradient x1="8.769" y1="1049.931" x2="8.769" y2="1038.668" id="c" xlink:href="#b" gradientUnits="userSpaceOnUse" gradientTransform="translate(0 -1036.362)"/>
    <filter height="1.48" y="-.24" width="1.48" x="-.24" id="e" color-interpolation-filters="sRGB">
      <feGaussianBlur stdDeviation=".8"/>
    </filter>
  </defs>
  <rect y="1.5" x="1.5" ry="2" rx="2" height="13" width="13" fill="url(#c)" stroke="url(#d)" stroke-linejoin="round"/>
  <path style="marker:none" d="M4.5 5v8h8V5zm1 1h6v4h-6z" transform="rotate(-90 8.5 9)" color="#000" overflow="visible" opacity=".4" filter="url(#e)"/>
  <path style="marker:none" d="M4 12h8V4H4zm1-1V5h4v6z" color="#000" overflow="visible" fill="#fff"/>
</svg>
PK
!<O;zY3chrome/devtools/skin/images/firebug/dock-undock.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16">
  <defs>
    <linearGradient id="b">
      <stop offset="0" stop-color="#f2451d"/>
      <stop offset=".101" stop-color="#f01428" stop-opacity=".8"/>
      <stop offset=".897" stop-color="#de8493"/>
      <stop offset="1" stop-color="#efc3cc"/>
    </linearGradient>
    <linearGradient id="a">
      <stop offset="0" stop-color="#520e0d"/>
      <stop offset="1" stop-color="#c4181d"/>
    </linearGradient>
    <linearGradient x1="7.231" y1="1051.323" x2="7.231" y2="1037.401" id="d" xlink:href="#a" gradientUnits="userSpaceOnUse"/>
    <linearGradient x1="8.769" y1="1049.931" x2="8.769" y2="1038.668" id="c" xlink:href="#b" gradientUnits="userSpaceOnUse"/>
    <filter x="-.24" y="-.24" width="1.48" height="1.48" color-interpolation-filters="sRGB" id="e">
      <feGaussianBlur stdDeviation=".8"/>
    </filter>
  </defs>
  <g transform="translate(0 -1036.362)">
    <rect width="13" height="13" rx="2" ry="2" x="1.5" y="1037.862" fill="url(#c)" stroke="url(#d)" stroke-linejoin="round"/>
    <path d="M6.5 1041.362v2h-2v6h6v-2h2v-6h-6zm1 1h4v4h-1v-3h-3v-1zm-2 2h4v4h-4v-4z" opacity=".4" filter="url(#e)"/>
    <path d="M6 1040.362v2H4v6h6v-2h2v-6H6zm1 1h4v4h-1v-3H7v-1zm-2 2h4v4H5v-4z" fill="#fff"/>
  </g>
</svg>
PK
!<���c))5chrome/devtools/skin/images/firebug/pane-collapse.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16">
  <defs>
    <linearGradient id="c">
      <stop offset="0" stop-color="#fff" stop-opacity=".196"/>
      <stop offset="1" stop-color="#fff" stop-opacity=".784"/>
    </linearGradient>
    <linearGradient id="a">
      <stop offset="0" stop-color="#353593"/>
      <stop offset="1" stop-color="#7373cd"/>
    </linearGradient>
    <linearGradient id="b">
      <stop offset="0" stop-color="#2a2a76"/>
      <stop offset="1" stop-color="#5656c2"/>
    </linearGradient>
    <linearGradient id="d">
      <stop offset="0" stop-color="#aabccf"/>
      <stop offset="1" stop-color="#c5d2df"/>
    </linearGradient>
    <linearGradient x1="9.29" y1="6.369" x2="5.581" y2="3.673" id="g" xlink:href="#a" gradientUnits="userSpaceOnUse" gradientTransform="matrix(.53813 0 0 .74017 3.298 3.873)"/>
    <linearGradient x1="7.02" y1="7.949" x2="2.721" y2="4.824" id="h" xlink:href="#b" gradientUnits="userSpaceOnUse" gradientTransform="matrix(.53813 0 0 .74017 3.298 3.873)"/>
    <linearGradient x1="14.692" y1="1049.087" x2="5.246" y2="1039.64" id="e" xlink:href="#c" gradientUnits="userSpaceOnUse" gradientTransform="translate(-1.167 -949.332) scale(.91667)"/>
    <linearGradient x1="13.658" y1="1050.509" x2="3.64" y2="1040.492" id="f" xlink:href="#d" gradientUnits="userSpaceOnUse" gradientTransform="translate(-2 -1036.362)"/>
  </defs>
  <path fill="url(#e)" stroke="url(#f)" d="M2.5 2.5h11v11h-11z"/>
  <path d="M9.7 8l-3.4 2.7V5.3z" fill="url(#g)" stroke="url(#h)" stroke-width=".6" stroke-linejoin="round"/>
</svg>
PK
!<Q6��,,3chrome/devtools/skin/images/firebug/pane-expand.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16">
  <defs>
    <linearGradient id="a">
      <stop offset="0" stop-color="#353593"/>
      <stop offset="1" stop-color="#7373cd"/>
    </linearGradient>
    <linearGradient id="b">
      <stop offset="0" stop-color="#2a2a76"/>
      <stop offset="1" stop-color="#5656c2"/>
    </linearGradient>
    <linearGradient x1="11.709" y1="6.295" x2="8.675" y2="4.089" id="g" xlink:href="#a" gradientUnits="userSpaceOnUse" gradientTransform="matrix(.53813 0 0 .74017 3.298 3.873)"/>
    <linearGradient x1="11.445" y1="8.382" x2="7.061" y2="5.195" id="h" xlink:href="#b" gradientUnits="userSpaceOnUse" gradientTransform="matrix(.53813 0 0 .74017 3.298 3.873)"/>
    <linearGradient id="c">
      <stop offset="0" stop-color="#fff" stop-opacity=".196"/>
      <stop offset="1" stop-color="#fff" stop-opacity=".784"/>
    </linearGradient>
    <linearGradient id="d">
      <stop offset="0" stop-color="#aabccf"/>
      <stop offset="1" stop-color="#c5d2df"/>
    </linearGradient>
    <linearGradient x1="14.692" y1="1049.087" x2="5.246" y2="1039.64" id="e" xlink:href="#c" gradientUnits="userSpaceOnUse" gradientTransform="translate(-1.167 -949.332) scale(.91667)"/>
    <linearGradient x1="13.658" y1="1050.509" x2="3.64" y2="1040.492" id="f" xlink:href="#d" gradientUnits="userSpaceOnUse" gradientTransform="translate(-2 -1036.362)"/>
  </defs>
  <path fill="url(#e)" stroke="url(#f)" d="M2.5 2.5h11v11h-11z"/>
  <path d="M6.3 8l3.4 2.7V5.3z" fill="url(#g)" stroke="url(#h)" stroke-width=".6" stroke-linejoin="round"/>
</svg>
PK
!<r�H$~~-chrome/devtools/skin/images/firebug/pause.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16">
  <defs>
    <linearGradient id="b">
      <stop offset="0" stop-color="#dd8506"/>
      <stop offset="1" stop-color="#f9c06e" stop-opacity=".988"/>
    </linearGradient>
    <linearGradient id="a">
      <stop offset="0" stop-color="#f5a742"/>
      <stop offset="1" stop-color="#f9cb8a"/>
    </linearGradient>
    <linearGradient x1="4.779" y1="1048.788" x2="3.117" y2="1039.853" id="e" xlink:href="#a" gradientUnits="userSpaceOnUse" gradientTransform="matrix(.993 0 0 .998 .028 2.025)"/>
    <linearGradient x1="5.527" y1="1049.91" x2="2.514" y2="1038.877" id="f" xlink:href="#b" gradientUnits="userSpaceOnUse" gradientTransform="matrix(.993 0 0 .998 .028 2.025)"/>
    <linearGradient id="c">
      <stop offset="0" stop-color="#f5a742"/>
      <stop offset="1" stop-color="#f9cb8a"/>
    </linearGradient>
    <linearGradient id="d">
      <stop offset="0" stop-color="#dd8506"/>
      <stop offset="1" stop-color="#f9c06e" stop-opacity=".988"/>
    </linearGradient>
    <linearGradient x1="4.779" y1="1048.788" x2="3.117" y2="1039.853" id="g" xlink:href="#c" gradientUnits="userSpaceOnUse" gradientTransform="matrix(.993 0 0 .998 7.028 2.025)"/>
    <linearGradient x1="5.527" y1="1049.91" x2="2.514" y2="1038.877" id="h" xlink:href="#d" gradientUnits="userSpaceOnUse" gradientTransform="matrix(.993 0 0 .998 7.028 2.025)"/>
  </defs>
  <g stroke-linejoin="round">
    <path fill="url(#e)" stroke="url(#f)" d="M2.5 1038.862h3v11h-3z" transform="translate(0 -1036.362)"/>
    <path fill="url(#g)" stroke="url(#h)" d="M9.5 1038.862h3v11h-3z" transform="translate(0 -1036.362)"/>
  </g>
</svg>
PK
!<]~W~,chrome/devtools/skin/images/firebug/play.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16">
  <defs>
    <linearGradient id="b">
      <stop offset="0" stop-color="#2959b8"/>
      <stop offset="1" stop-color="#83ace8"/>
    </linearGradient>
    <linearGradient id="a">
      <stop offset="0" stop-color="#5c87d0"/>
      <stop offset="1" stop-color="#abc7ed"/>
    </linearGradient>
    <linearGradient x1="1.472" y1="-4.098" x2="1.472" y2="6.772" id="c" xlink:href="#a" gradientUnits="userSpaceOnUse" gradientTransform="matrix(.65609 0 0 -1.01925 4.494 9.401)"/>
    <linearGradient x1="10.18" y1="8.767" x2="3.926" y2="2.99" id="d" xlink:href="#b" gradientUnits="userSpaceOnUse"/>
  </defs>
  <path d="M11.788 8L4.5 1.204v13.592z" fill="url(#c)" stroke="url(#d)" stroke-linejoin="round"/>
</svg>
PK
!<��_
_
1chrome/devtools/skin/images/firebug/read-only.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:xlink="http://www.w3.org/1999/xlink" width="12" height="12">
  <defs>
    <linearGradient id="d">
      <stop offset="0" stop-color="#787878"/>
      <stop offset="1" stop-color="#b4b4b4"/>
    </linearGradient>
    <linearGradient id="c">
      <stop offset="0" stop-color="#505050"/>
      <stop offset="1" stop-color="#787878"/>
    </linearGradient>
    <linearGradient id="a">
      <stop offset="0" stop-color="#c8c8c8"/>
      <stop offset="1" stop-color="#dcdcdc"/>
    </linearGradient>
    <linearGradient id="b">
      <stop offset="0" stop-color="#a0a0a0"/>
      <stop offset="1" stop-color="#c8c8c8"/>
    </linearGradient>
    <linearGradient x1="8.637" y1="4.311" x2="6.34" y2=".583" id="e" xlink:href="#a" gradientUnits="userSpaceOnUse" gradientTransform="matrix(.64 0 0 .6988 .88 .987)"/>
    <linearGradient x1="7.188" y1="5.078" x2="4.956" y2="1.392" id="f" xlink:href="#b" gradientUnits="userSpaceOnUse" gradientTransform="matrix(.64 0 0 .6988 .88 .987)"/>
    <linearGradient x1="11.377" y1="1052.085" x2="4.559" y2="1040.666" id="g" xlink:href="#a" gradientUnits="userSpaceOnUse" gradientTransform="matrix(.62152 0 0 .5895 1.028 -609.403)"/>
    <linearGradient x1="8.842" y1="1053.385" x2="1.917" y2="1041.923" id="h" xlink:href="#b" gradientUnits="userSpaceOnUse" gradientTransform="matrix(.62152 0 0 .5895 1.028 -609.403)"/>
    <linearGradient x1="8.54" y1="12.498" x2="6.608" y2="7.825" id="i" xlink:href="#c" gradientUnits="userSpaceOnUse" gradientTransform="matrix(.71429 0 0 .71492 .286 .276)"/>
    <linearGradient x1="9.392" y1="12.116" x2="7.402" y2="7.414" id="j" xlink:href="#d" gradientUnits="userSpaceOnUse" gradientTransform="matrix(.71429 0 0 .71492 .286 .276)"/>
  </defs>
  <g transform="matrix(1.19102 0 0 1.19106 -1.146 -1.146)" stroke-linejoin="round">
    <path d="M6 1.215a2.982 2.982 0 0 0-2.991 2.904c.114-.019.238-.045.357-.045h.938C4.386 3.194 5.1 2.421 6 2.421c.899 0 1.614.773 1.696 1.653h.938c.119 0 .243.026.357.045A2.982 2.982 0 0 0 6 1.215z" fill="url(#e)" stroke="url(#f)" stroke-width=".504"/>
    <rect width="9.571" height="6.72" rx="1.679" ry="1.679" x="1.214" y="4.065" fill="url(#g)" stroke="url(#h)" stroke-width=".504"/>
    <path d="M6 5.504A1.2 1.2 0 0 0 4.795 6.71c0 .562.375 1.023.893 1.162v1.475h.625V7.872c.517-.139.892-.6.892-1.162A1.2 1.2 0 0 0 6 5.504z" fill="url(#i)" stroke="url(#j)" stroke-width=".336"/>
  </g>
</svg>
PK
!<p�S8%%.chrome/devtools/skin/images/firebug/rewind.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16">
  <defs>
    <linearGradient id="b">
      <stop offset="0" stop-color="#2959b8"/>
      <stop offset="1" stop-color="#83ace8"/>
    </linearGradient>
    <linearGradient id="a">
      <stop offset="0" stop-color="#5c87d0"/>
      <stop offset="1" stop-color="#abc7ed"/>
    </linearGradient>
    <linearGradient x1="1.472" y1="-4.098" x2="1.472" y2="6.772" id="c" xlink:href="#a" gradientUnits="userSpaceOnUse" gradientTransform="matrix(.65609 0 0 -1.01925 4.494 9.401)"/>
    <linearGradient x1="10.18" y1="8.767" x2="3.926" y2="2.99" id="d" xlink:href="#b" gradientUnits="userSpaceOnUse"/>
  </defs>
  <path d="M11.788 8L4.5 1.204v13.592z" transform="matrix(-1 0 0 1 16 0)" fill="url(#c)" stroke="url(#d)" stroke-linejoin="round"/>
</svg>
PK
!<��T���<chrome/devtools/skin/images/firebug/tool-debugger-paused.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:xlink="http://www.w3.org/1999/xlink" width="11" height="12">
  <defs>
    <linearGradient id="a">
      <stop offset="0" stop-color="#b4aa00"/>
      <stop offset=".659" stop-color="#f5e600"/>
      <stop offset="1" stop-color="#f5e600"/>
    </linearGradient>
    <linearGradient x1="1.256" y1="6.226" x2=".157" y2=".942" id="b" xlink:href="#a" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1.1625 0 0 1.2744 1.663 1040.82)"/>
  </defs>
  <path d="M8.553 1046.88l-2.742 1.735c-4.951 3.273-5.215 3.09-5.215.035v-6.845c0-2.706.26-2.927 5.215.208l2.593 1.641c2.642 1.553 2.642 1.648.149 3.226z" fill="url(#b)" stroke-width="1.217" stroke-linejoin="round" transform="matrix(1 0 0 .95762 0 -995.06)" stroke="#888000"/>
</svg>
PK
!<ߚ�|��4chrome/devtools/skin/images/firebug/tool-options.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16">
  <defs>
    <linearGradient id="b">
      <stop offset="0" stop-color="#464f5a"/>
      <stop offset="1" stop-color="#7e8b9a"/>
    </linearGradient>
    <linearGradient id="a">
      <stop offset="0" stop-color="#6a7786"/>
      <stop offset="1" stop-color="#abb3bd"/>
    </linearGradient>
    <linearGradient xlink:href="#a" id="c" x1="13.108" y1="13.135" x2="2.763" y2="2.791" gradientUnits="userSpaceOnUse" gradientTransform="translate(.306 1036.661) scale(.9618)"/>
    <linearGradient xlink:href="#b" id="d" x1="14.815" y1="11.602" x2="4.34" y2="1.127" gradientUnits="userSpaceOnUse" gradientTransform="translate(.306 1036.661) scale(.9618)"/>
  </defs>
  <path style="marker:none" d="M7.23 1036.661c-.426 0-.665.445-.768.962l-.25 1.25a5.77 5.77 0 0 0-.825.345l-1.063-.71c-.477-.205-.924-.437-1.225-.135l-1.088 1.087c-.301.302-.157.786.136 1.225l.706 1.062a5.77 5.77 0 0 0-.333.824l-1.258.252c-.482.193-.962.344-.962.77v1.538c0 .427.445.665.962.769l1.258.252a5.77 5.77 0 0 0 .34.815l-.713 1.073c-.205.477-.437.92-.136 1.223l1.088 1.089c.301.301.786.155 1.225-.137l1.069-.712a5.77 5.77 0 0 0 .815.33l.254 1.268c.192.482.342.962.768.962h1.54c.426 0 .665-.445.768-.962l.254-1.264a5.77 5.77 0 0 0 .81-.338l1.074.716c.477.204.924.438 1.225.137l1.088-1.09c.301-.301.157-.784-.136-1.222l-.71-1.067a5.77 5.77 0 0 0 .333-.821l1.262-.252c.482-.193.962-.342.962-.769v-1.538c0-.426-.445-.667-.962-.77l-1.253-.252a5.77 5.77 0 0 0-.342-.819l.71-1.067c.205-.477.437-.923.136-1.225l-1.088-1.087c-.301-.302-.786-.158-1.225.135l-1.057.706a5.77 5.77 0 0 0-.829-.336l-.252-1.255c-.192-.482-.342-.962-.768-.962H7.23zm.77 4.81a2.885 2.885 0 0 1 2.885 2.885A2.885 2.885 0 0 1 8 1047.24a2.885 2.885 0 0 1-2.885-2.885A2.885 2.885 0 0 1 8 1041.47z" color="#000" overflow="visible" fill="url(#c)" stroke="url(#d)" stroke-width=".6" stroke-linejoin="round" transform="translate(0 -1036.362)"/>
</svg>
PK
!<J��JJJ=chrome/devtools/skin/images/firebug/twisty-closed-firebug.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:xlink="http://www.w3.org/1999/xlink" width="11" height="11">
  <defs>
    <linearGradient id="a">
      <stop offset="0" stop-color="#c3baaa"/>
      <stop offset="1" stop-color="#fff"/>
    </linearGradient>
    <linearGradient x1="6.053" y1="7.093" x2="2.888" y2="1.8" id="b" xlink:href="#a" gradientUnits="userSpaceOnUse" gradientTransform="translate(.885 .885) scale(1.02564)"/>
  </defs>
  <rect width="8" height="8" rx="1" ry="1" x="1.5" y="1.5" fill="url(#b)" stroke="#7898b5" stroke-linecap="round" stroke-linejoin="round"/>
  <path d="M5 3v2H3v1h2v2h1V6h2V5H6V3H5z"/>
</svg>
PK
!<j�688;chrome/devtools/skin/images/firebug/twisty-open-firebug.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" xmlns:xlink="http://www.w3.org/1999/xlink" width="11" height="11">
  <defs>
    <linearGradient id="a">
      <stop offset="0" stop-color="#c3baaa"/>
      <stop offset="1" stop-color="#fff"/>
    </linearGradient>
    <linearGradient x1="6.053" y1="7.093" x2="2.888" y2="1.8" id="b" xlink:href="#a" gradientUnits="userSpaceOnUse" gradientTransform="translate(.885 .885) scale(1.02564)"/>
  </defs>
  <rect width="8" height="8" rx="1" ry="1" x="1.5" y="1.5" fill="url(#b)" stroke="#7898b5" stroke-linecap="round" stroke-linejoin="round"/>
  <path d="M3 5h5v1H3z"/>
</svg>
PK
!<�Nϟ��,chrome/devtools/skin/images/gcli_sec_bad.svg<svg width="30" height="30" xmlns="http://www.w3.org/2000/svg">
  <circle cx="15" cy="15" r="15" fill="#e74c3c"/>
  <g stroke="white" stroke-width="3">
    <line x1="9" y1="9" x2="21" y2="21"/>
    <line x1="21" y1="9" x2="9" y2="21"/>
  </g>
</svg>PK
!<Α����-chrome/devtools/skin/images/gcli_sec_good.svg<svg width="30" height="30" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60">
  <circle cx="30" cy="30" r="30" fill="#2CBB0F"/>
  <polygon points="17,32 25,39 26,39 44,18 45,18 48,21 27,46 25,46 14,36 13,36 16,33 16,32" fill="white"/>
</svg>PK
!<*eǴ�1chrome/devtools/skin/images/gcli_sec_moderate.svg<svg width="30" height="30" xmlns="http://www.w3.org/2000/svg">
  <circle cx="15" cy="15" r="15" fill="#F5B400"/>
  <rect x="7.5" y="13" width="15" height="4" fill="white"/>
</svg>PK
!<S{���/chrome/devtools/skin/images/geometry-editor.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b">
  <path d="M14,8 L12,8 L12,11.25 L12,12 L11.5,12 L3.5,12 L3,12 L3,11.75 L3,11.5 L3,8 L1,8 L1,8 L1,8.5 L1,9 L0,9 L0,8.5 L0,6.5 L0,6 L1,6 L1,6.5 L1,7 L3,7 L3,3.5 L3,3 L3.72222222,3 L3.72222222,3 L10.5555556,3 L11,3 L11,4 L10.5555556,4 L4,4 L4,11 L11,11 L11,3.5 L11,3 L12,3 L12,3.5 L12,7 L14,7 L14,6.5 L14,6 L15,6 L15,6.5 L15,8.5 L15,9 L14,9 L14,8.5 L14,8 Z M8,14 L8.5,14 L9,14 L9,15 L8.5,15 L6.5,15 L6,15 L6,14 L6.5,14 L7,14 L7,11.5 L7,11 L8,11 L8,11.5 L8,14 Z M7,1 L6.5,1 L6,1 L6,0 L6.5,0 L8.5,0 L9,0 L9,1 L8.5,1 L8,1 L8,3.5 L8,4 L7,4 L7,3.5 L7,1 L7,1 Z"/>
  <path d="M3.5,9 C4.32842712,9 5,8.32842712 5,7.5 C5,6.67157288 4.32842712,6 3.5,6 C2.67157288,6 2,6.67157288 2,7.5 C2,8.32842712 2.67157288,9 3.5,9 Z M7.5,13 C8.32842712,13 9,12.3284271 9,11.5 C9,10.6715729 8.32842712,10 7.5,10 C6.67157288,10 6,10.6715729 6,11.5 C6,12.3284271 6.67157288,13 7.5,13 Z M11.5,9 C12.3284271,9 13,8.32842712 13,7.5 C13,6.67157288 12.3284271,6 11.5,6 C10.6715729,6 10,6.67157288 10,7.5 C10,8.32842712 10.6715729,9 11.5,9 Z M7.5,5 C8.32842712,5 9,4.32842712 9,3.5 C9,2.67157288 8.32842712,2 7.5,2 C6.67157288,2 6,2.67157288 6,3.5 C6,4.32842712 6.67157288,5 7.5,5 Z"/>
</svg>PK
!<6��?����%chrome/devtools/skin/images/globe.svg<?xml version="1.0" encoding="utf-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 xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
  <circle fill="#a6a6a6" cx="8" cy="8" r="7" />
  <path transform="translate(1 1)" fill="#fff" d="M5.31617536,1.74095137 C5.29841561,1.73995137 5.27868256,1.74095137 5.26190947,1.74795137 C5.25796286,1.74995137 5.2530296,1.75395137 5.24908299,1.75895137 C5.2550029,1.75895137 5.26190947,1.75895137 5.26684273,1.75795137 C5.28460248,1.75395137 5.29841561,1.74195137 5.31617536,1.74095137 L5.31617536,1.74095137 Z M5.33886837,2.59995137 C5.36156138,2.57095137 5.30729549,2.54695137 5.27670926,2.54895137 C5.28460248,2.51395137 5.32900184,2.49595137 5.31716201,2.45195137 C5.30630884,2.40595137 5.25105629,2.41495137 5.22145672,2.43995137 C5.1948171,2.46295137 5.18100396,2.50295137 5.15831095,2.52995137 C5.14548447,2.54495137 5.12180481,2.54995137 5.11292494,2.56795137 C5.10503172,2.58495137 5.11489824,2.61395137 5.11391159,2.63295137 C5.15041773,2.63795137 5.18889718,2.62695137 5.2155368,2.60095137 L5.23329655,2.59295137 C5.22934994,2.59595137 5.22737663,2.60295137 5.22540333,2.60695137 C5.24316307,2.62895137 5.32209528,2.62295137 5.33886837,2.59995137 L5.33886837,2.59995137 Z M5.37636117,1.37295137 C5.37438786,1.42695137 5.42668044,1.43295137 5.46515989,1.45395137 C5.45332006,1.48495137 5.410894,1.48395137 5.39116095,1.50895137 C5.36748129,1.53995137 5.410894,1.56695137 5.43260036,1.58095137 C5.47502642,1.60695137 5.45134676,1.63695137 5.44345354,1.67395137 C5.43161371,1.72595137 5.54310544,1.71195137 5.56777176,1.71095137 C5.61019782,1.70895137 5.67729019,1.71595137 5.71774294,1.69595137 C5.76115565,1.67195137 5.78384866,1.61895137 5.82923468,1.59295137 C5.86672748,1.57095137 5.92000671,1.55895137 5.96144612,1.57395137 C6.00485883,1.58895137 5.99992557,1.64495137 6.03544506,1.66895137 C6.07688447,1.69795137 6.12227048,1.70695137 6.15778997,1.66395137 C6.18048298,1.63695137 6.23080226,1.60295137 6.23277557,1.57395137 C6.23672218,1.52295137 6.25152196,1.48295137 6.30776116,1.47195137 C6.35314718,1.46295137 6.34328065,1.50695137 6.37485353,1.51495137 C6.44490586,1.53295137 6.47845205,1.31895137 6.55442429,1.38195137 C6.57218404,1.39695137 6.5771173,1.45495137 6.60770353,1.44995137 C6.63927641,1.44495137 6.64026306,1.39895137 6.67380925,1.39795137 C6.68466243,1.42895137 6.61559675,1.46695137 6.60671688,1.50095137 C6.64914294,1.46595137 6.66986264,1.47095137 6.71820861,1.46595137 C6.7310351,1.49895137 6.63631645,1.55295137 6.61165014,1.55795137 C6.5771173,1.56695137 6.5563976,1.54695137 6.52975798,1.56595137 C6.50903828,1.57995137 6.48042535,1.57895137 6.45575904,1.58095137 C6.4212262,1.58495137 6.35610713,1.63095137 6.35709379,1.66895137 C6.35709379,1.68395137 6.36893362,1.71795137 6.35610713,1.72995137 C6.3442673,1.74295137 6.31565438,1.73095137 6.31269442,1.71795137 C6.28309485,1.76195137 6.2446154,1.68495137 6.21994908,1.74695137 C6.25941518,1.75695137 6.29592133,1.79495137 6.34032069,1.80595137 C6.3837334,1.81695137 6.42714612,1.82795137 6.46957217,1.83995137 C6.54159781,1.86195137 6.64914294,1.77495137 6.70439548,1.73295137 C6.75668806,1.69395137 6.82279378,1.60595137 6.83660692,1.54295137 C6.85239336,1.47395137 6.92737895,1.39495137 6.91159251,1.32695137 C6.89777937,1.26295137 6.88791285,1.23295137 6.95993848,1.20995137 C6.99052471,1.19995137 7.06452365,1.18395137 7.07537683,1.14895137 C7.09116327,1.09695137 6.9283656,1.11095137 6.90369929,1.09895137 C6.82180713,1.06195137 6.78628764,1.02095137 6.69156899,1.05795137 C6.64223637,1.07695137 6.59389039,1.09295137 6.54258446,1.10695137 C6.51594484,1.11395137 6.48930523,1.11595137 6.47450544,1.13895137 C6.46858552,1.14795137 6.4606923,1.15495137 6.45082578,1.15995137 C6.40839972,1.17695137 6.4606923,1.09595137 6.46562556,1.09095137 C6.4794387,1.07495137 6.50213171,1.02595137 6.45773234,1.03695137 C6.39261328,1.05195137 6.34525395,1.15195137 6.27520162,1.15695137 C6.22192239,1.16095137 6.23869548,1.11395137 6.25250862,1.08695137 C6.27914824,1.03795137 6.20317599,1.03195137 6.1696298,1.03195137 C6.12227048,1.03195137 6.08675099,1.05895137 6.04136497,1.06395137 C5.99893892,1.06795137 5.94960629,1.07595137 5.90718023,1.07495137 C5.82232811,1.07195137 5.76608892,1.12195137 5.68222345,1.09395137 C5.59342472,1.06495137 5.49771943,1.13895137 5.41188066,1.14895137 C5.38326773,1.15295137 5.34182833,1.14695137 5.3299885,1.18095137 C5.32012197,1.20895137 5.3299885,1.25195137 5.35169485,1.27295137 L5.35860142,1.26695137 C5.33985502,1.28595137 5.33788172,1.31295137 5.31025545,1.32295137 C5.28361583,1.33195137 5.25697621,1.36695137 5.24316307,1.39095137 C5.2323099,1.40895137 5.20172367,1.48395137 5.2550029,1.44495137 C5.29348235,1.41595137 5.31518871,1.36195137 5.37636117,1.37295137 L5.37636117,1.37295137 Z M2.18355356,6.10795137 C2.09278153,6.04195137 1.88657115,6.02595137 1.91222411,5.87195137 C1.92801055,5.77795137 2.0247025,5.70495137 2.10264805,5.65895137 C2.20525992,5.59895137 2.31971161,5.59695137 2.43514996,5.60695137 C2.46277623,5.60995137 2.51506881,5.60495137 2.5298686,5.62695137 C2.53776182,5.63795137 2.55354826,5.64495137 2.56637474,5.64895137 C2.59696097,5.65795137 2.62853385,5.65895137 2.66010674,5.66495137 C2.70746606,5.67395137 2.74101224,5.71495137 2.78837156,5.68095137 C2.84263745,5.64295137 2.85151733,5.63495137 2.91762305,5.64295137 C2.9768222,5.64995137 3.01234169,5.60495137 3.06167432,5.60895137 C3.07746076,5.60995137 3.09127389,5.61295137 3.10311372,5.61795137 C3.10804699,5.60095137 3.11495355,5.58595137 3.12580673,5.58295137 C3.15047305,5.57595137 3.20473893,5.63595137 3.2303919,5.64095137 C3.29551097,5.65495137 3.29156436,5.60895137 3.29649762,5.56195137 C3.32905715,5.55595137 3.34484359,5.60095137 3.37444317,5.57295137 C3.37345652,5.58195137 3.37937643,5.59595137 3.37937643,5.60495137 C3.38529635,5.60895137 3.39220292,5.60895137 3.39812283,5.60395137 C3.40108279,5.59895137 3.40206944,5.59395137 3.40009614,5.58795137 C3.41588258,5.59295137 3.4237758,5.58195137 3.4257491,5.56295137 C3.43758893,5.56395137 3.45633533,5.55695137 3.46817516,5.55895137 C3.47705503,5.52495137 3.49678809,5.47995137 3.47212177,5.44895137 C3.47804169,5.44795137 3.48494825,5.44595137 3.49185482,5.44495137 C3.49185482,5.41095137 3.51454783,5.39595137 3.51553448,5.36895137 C3.48001499,5.36395137 3.44054889,5.36595137 3.40404275,5.36695137 C3.4257491,5.34695137 3.47804169,5.30295137 3.48297495,5.27595137 C3.49284148,5.22795137 3.43068237,5.19895137 3.43561563,5.14195137 C3.44153554,5.17195137 3.47508173,5.24095137 3.50665461,5.25095137 C3.57769359,5.27495137 3.55697389,5.20395137 3.56190715,5.16695137 C3.5796669,5.04995137 3.68425207,5.14695137 3.68622537,5.20795137 C3.7168116,5.13795137 3.79278385,5.21595137 3.75825101,5.27595137 C3.74147791,5.30495137 3.71878491,5.29395137 3.73950461,5.33195137 C3.7543044,5.35895137 3.77601075,5.35995137 3.80758363,5.35295137 C3.81547685,5.33695137 3.82238342,5.31895137 3.82238342,5.29995137 C3.87664931,5.28295137 3.9121688,5.34795137 3.88059592,5.38695137 C3.92104868,5.36495137 3.96248808,5.34395137 4.00590079,5.33295137 C3.98024783,5.24295137 3.95360821,5.15495137 3.9703813,5.05895137 C3.97432791,5.03795137 3.97728787,5.01395137 3.99307431,4.99795137 C4.01280736,4.97695137 3.98814105,4.98495137 3.98616774,4.97095137 C3.98024783,4.92895137 4.02464719,4.88595137 4.04142028,4.84795137 C3.99504762,4.83795137 4.03747367,4.74595137 4.0680599,4.72995137 C4.10160609,4.71295137 4.20027134,4.74095137 4.20717791,4.71395137 C4.22691096,4.72495137 4.24565736,4.74095137 4.26933702,4.74095137 C4.32360291,4.74195137 4.36010905,4.74295137 4.39760185,4.78695137 C4.41634825,4.80995137 4.44397452,4.86095137 4.47752071,4.86495137 C4.47653405,4.90295137 4.51994676,4.93095137 4.47456075,4.96295137 C4.43904126,4.98795137 4.38970863,4.98195137 4.37490884,5.02995137 C4.36504232,5.05995137 4.33642939,5.07395137 4.3798421,5.09495137 C4.3985885,5.10495137 4.42226816,5.10695137 4.44298787,5.10695137 C4.44792113,5.13595137 4.46272092,5.17495137 4.50021371,5.16995137 C4.573226,5.16095137 4.58901244,5.06895137 4.64722494,5.03795137 C4.74194358,4.98795137 4.7271438,5.20395137 4.80903596,5.14995137 C4.82876901,5.13695137 4.82876901,5.08195137 4.83863553,5.06095137 C4.85836858,5.01695137 4.88106159,4.97195137 4.90967452,4.93295137 C4.94618066,4.88295137 4.99156668,4.83095137 4.97578024,4.76595137 C4.96690036,4.72995137 4.89783469,4.71495137 4.8662618,4.68995137 C4.82876901,4.65895137 4.79226286,4.62595137 4.76956986,4.58295137 C4.75575672,4.55695137 4.7478635,4.54795137 4.76956986,4.53495137 C4.78239634,4.52795137 4.77844973,4.51395137 4.77351647,4.50395137 C4.74983681,4.45395137 4.68570439,4.36495137 4.77548977,4.33395137 C4.79522282,4.32695137 4.83666223,4.26295137 4.83962219,4.23795137 C4.84455545,4.19595137 4.78140969,4.15795137 4.81002261,4.11595137 C4.83074231,4.08495137 4.8830349,4.06495137 4.90967452,4.03395137 C4.922501,4.01895137 4.93730079,4.00595137 4.95703384,4.00195137 C4.95802049,3.98495137 4.9619671,3.96595137 4.97676689,3.95495137 C5.00044655,3.93695137 5.03793935,3.94595137 5.06556562,3.93695137 C5.11095163,3.92295137 5.13068468,3.87595137 5.16620418,3.84995137 C5.19580375,3.82795137 5.22934994,3.83595137 5.26092282,3.81995137 C5.27769591,3.81195137 5.28460248,3.79395137 5.30137557,3.78595137 C5.34281498,3.76595137 5.3901743,3.79795137 5.4089207,3.83295137 C5.45332006,3.91695137 5.5085726,4.04695137 5.63486413,4.01295137 C5.68617006,3.99895137 5.72464951,3.95695137 5.74043595,3.90895137 C5.75523574,3.86295137 5.73747599,3.82495137 5.74043595,3.77995137 C5.74438256,3.69995137 5.82232811,3.64895137 5.83120798,3.56995137 C5.77200883,3.57095137 5.80259506,3.53395137 5.78286201,3.49995137 C5.76115565,3.46195137 5.71182303,3.48995137 5.67926349,3.48395137 C5.71280968,3.40295137 5.71280968,3.37495137 5.63387748,3.33595137 C5.59934464,3.31895137 5.54211879,3.23895137 5.51547917,3.24195137 C5.53718553,3.21195137 5.58849146,3.26195137 5.6042779,3.27595137 C5.63881074,3.30895137 5.66939697,3.32395137 5.71774294,3.32795137 C5.70392981,3.30695137 5.69702324,3.26895137 5.70590311,3.24495137 C5.71478298,3.22295137 5.69307663,3.19995137 5.69504993,3.17195137 C5.75030248,3.24295137 5.7414226,3.32395137 5.77299548,3.40195137 C5.78582197,3.43495137 5.8183815,3.45695137 5.83219464,3.49095137 C5.84995438,3.53395137 5.83811455,3.53295137 5.87560735,3.55895137 C5.89830036,3.57495137 5.90619358,3.60295137 5.91014019,3.62795137 C5.91704675,3.67195137 5.9328332,3.65295137 5.95651286,3.67795137 C5.97032599,3.69295137 6.00584548,3.69495137 5.99893892,3.72595137 C5.99400565,3.74795137 5.97920586,3.76595137 5.97624591,3.78895137 C5.96736603,3.85495137 6.09661752,3.76495137 6.109444,3.75595137 C6.13707027,3.73495137 6.18245629,3.73095137 6.20416264,3.70595137 C6.22685565,3.67995137 6.22192239,3.64195137 6.24560205,3.61795137 C6.27520162,3.58695137 6.30381455,3.60795137 6.33933404,3.60195137 C6.38077345,3.59595137 6.41629294,3.56295137 6.44687917,3.53795137 C6.51199823,3.48295137 6.55343764,3.42295137 6.60770353,3.35995137 C6.58402387,3.36595137 6.50311836,3.42495137 6.4981851,3.36995137 C6.46759887,3.36995137 6.39655989,3.36495137 6.38570671,3.33095137 C6.37682684,3.30595137 6.37978679,3.27795137 6.37978679,3.25295137 C6.37880014,3.22595137 6.34624061,3.23495137 6.32453425,3.22095137 C6.28112154,3.19295137 6.25941518,3.14095137 6.21304252,3.11695137 C6.13904358,3.07795137 6.09464421,3.01495137 6.05024485,2.94795137 C6.02459188,2.90895137 5.93381985,2.82995137 5.94072642,2.78295137 C5.94467303,2.75195137 5.97032599,2.71895137 5.96835269,2.68795137 C5.96736603,2.65995137 5.94565968,2.64495137 5.94861964,2.61395137 C5.95157959,2.57795137 5.86475417,2.51495137 5.94072642,2.50795137 C5.96440608,2.50595137 5.96835269,2.47695137 5.9949923,2.46095137 C6.02459188,2.44295137 6.01768531,2.42695137 6.05024485,2.43595137 C6.10253743,2.45195137 6.13904358,2.39395137 6.17456307,2.36295137 C6.23573552,2.30895137 6.13805692,2.30795137 6.13312366,2.26695137 C6.1281904,2.22595137 6.10451074,2.19595137 6.09760417,2.14795137 C6.09365756,2.11295137 6.06109802,2.12695137 6.04235163,2.13595137 C6.01669866,2.14795137 5.99104569,2.12995137 5.96637938,2.12495137 C5.94368637,2.11995137 5.92493997,2.08195137 5.8973137,2.09395137 C5.876594,2.10395137 5.87758065,2.12895137 5.84798108,2.12595137 C5.82627472,2.12395137 5.81246159,2.10295137 5.79075523,2.09895137 C5.75720904,2.09495137 5.78680862,2.12695137 5.74931582,2.12995137 C5.72267621,2.13195137 5.63683743,2.09595137 5.63486413,2.12995137 C5.60822451,2.08395137 5.59737133,2.16195137 5.56875841,2.16995137 C5.53718553,2.17895137 5.50363934,2.17095137 5.47206646,2.18295137 C5.40300078,2.21095137 5.42569379,2.27995137 5.49179951,2.29095137 C5.54507875,2.29895137 5.47601307,2.33595137 5.49377282,2.37195137 C5.50955926,2.40395137 5.51449252,2.42595137 5.54902536,2.43895137 C5.60625121,2.45995137 5.66742366,2.47695137 5.64769061,2.55095137 C5.62203765,2.64295137 5.55790523,2.72995137 5.46811985,2.77195137 C5.38228108,2.81195137 5.35860142,2.70295137 5.29348235,2.67495137 C5.2530296,2.65795137 5.20764358,2.66395137 5.16521752,2.66895137 C5.15831095,2.67995137 5.22441667,2.70095137 5.23526985,2.71995137 C5.2550029,2.75895137 5.20073701,2.75395137 5.1967904,2.78395137 C5.19284379,2.80895137 5.16028426,2.82695137 5.17804401,2.85195137 C5.15929761,2.82895137 5.12279146,2.85995137 5.10996498,2.87395137 C5.09121858,2.89395137 5.09516519,2.90695137 5.10305841,2.93195137 C5.11884485,2.98295137 5.04188596,3.03595137 4.99649994,3.02995137 C4.95802049,3.02395137 4.92151435,3.02695137 4.8850082,3.00895137 C4.84159549,2.98795137 4.85639528,3.00095137 4.84751541,2.95195137 C4.83863553,2.90595137 4.77548977,2.88595137 4.81298257,2.82895137 C4.83962219,2.78695137 4.8267957,2.79095137 4.82186244,2.75095137 C4.81594253,2.70895137 4.83468892,2.70295137 4.86823511,2.69695137 C4.90474125,2.68995137 4.92052769,2.62495137 4.94223405,2.59395137 C4.94716731,2.58695137 4.96986032,2.52895137 4.93434083,2.54395137 C4.91460778,2.55295137 4.92940757,2.57795137 4.89882134,2.58195137 C4.87711498,2.58595137 4.85540863,2.57095137 4.83271562,2.57095137 C4.80706265,2.57095137 4.78042303,2.58395137 4.75674337,2.56795137 C4.7685832,2.55395137 4.85343532,2.48395137 4.78634295,2.46995137 C4.75970333,2.46395137 4.78140969,2.50795137 4.7458902,2.50195137 C4.73898363,2.53695137 4.69655757,2.53395137 4.67583787,2.55595137 C4.68471774,2.51895137 4.76266329,2.49095137 4.73701032,2.46095137 C4.79324952,2.41195137 4.80508935,2.40295137 4.7291171,2.37595137 C4.60973215,2.33395137 4.61861202,2.21095137 4.70050418,2.13695137 C4.77548977,2.06895137 4.89882134,1.98295137 4.97183363,2.09595137 C5.04977918,2.21695137 5.0991118,2.12895137 5.16324422,2.05095137 C5.14153786,2.04195137 5.16127091,2.03595137 5.15436434,2.00895137 C5.08332536,2.03795137 5.0201796,1.94595137 5.06852557,1.89095137 C5.09812515,1.85795137 5.14351117,1.86695137 5.18297727,1.85695137 C5.21751011,1.84795137 5.24908299,1.81395137 5.26388278,1.78195137 C5.2342832,1.78995137 5.23822981,1.77195137 5.24908299,1.75895137 C5.23132324,1.75695137 5.21159019,1.74895137 5.1967904,1.74395137 C5.15436434,1.72895137 5.1573243,1.69595137 5.11193829,1.68995137 C5.00439316,1.67395137 5.22441667,1.54995137 5.11687155,1.54995137 C5.08233871,1.54895137 5.05175248,1.49695137 5.02609952,1.50695137 C5.00833977,1.51395137 5.00340651,1.52795137 4.98170015,1.51895137 C4.96690036,1.51295137 4.94914062,1.49995137 4.93138087,1.50995137 C4.89290142,1.53395137 4.8850082,1.50495137 4.84751541,1.51595137 C4.81692918,1.52595137 4.80015608,1.55595137 4.76463659,1.54795137 C4.80015608,1.49995137 4.8435688,1.45995137 4.87514168,1.40995137 C4.89586138,1.37595137 4.92151435,1.34495137 4.95604719,1.32395137 C4.97479358,1.31295137 5.02807282,1.30195137 5.03103278,1.27595137 C5.03596604,1.23295137 5.00932642,1.23695137 4.97972685,1.25395137 C4.90276795,1.29995137 4.82284909,1.34895137 4.7478635,1.39795137 C4.70247748,1.42695137 4.66695799,1.45195137 4.6107188,1.44395137 C4.56730609,1.43695137 4.54954634,1.48495137 4.5150135,1.48095137 C4.49824041,1.41395137 4.12824571,1.65695137 4.08285969,1.67795137 C4.01083406,1.70995137 3.92992855,1.76495137 3.85296965,1.78395137 C3.82139677,1.79195137 3.75529105,1.86595137 3.75923766,1.78395137 C3.71977156,1.77895137 3.69017198,1.81895137 3.66353236,1.83895137 C3.62603957,1.86795137 3.5816402,1.88595137 3.54118745,1.91095137 C3.45436203,1.96695137 3.37246987,2.03395137 3.29156436,2.09695137 C3.21460546,2.15695137 3.13764656,2.22695137 3.05674105,2.28095137 C3.02911478,2.29995137 2.92748957,2.35195137 2.93044953,2.39095137 C3.00247516,2.40495137 3.24815165,2.09695137 3.31721732,2.17995137 C3.33497707,2.20095137 3.21263216,2.26295137 3.1928991,2.27495137 C3.17612601,2.28395137 3.15639296,2.28295137 3.13961987,2.29195137 C3.11791351,2.30495137 3.10410038,2.32695137 3.08338067,2.34095137 C3.02812813,2.37595137 2.98175546,2.42095137 2.94130271,2.47095137 C2.91268978,2.50795137 2.89197008,2.55595137 2.8603972,2.58995137 C2.86533046,2.55395137 2.85842389,2.52795137 2.85941055,2.49295137 C2.81895779,2.51895137 2.8021847,2.56295137 2.74594551,2.55095137 C2.69463957,2.53895137 2.65418682,2.59095137 2.61768068,2.61895137 C2.53282856,2.68395137 2.47560271,2.75595137 2.40456373,2.83195137 C2.36509763,2.87495137 2.32267157,2.90495137 2.29800525,2.95795137 C2.27136564,3.01495137 2.23387284,3.06595137 2.19934,3.11895137 C2.13323428,3.21595137 2.05726204,3.30495137 1.99214297,3.40195137 C1.85894488,3.60095137 1.7711328,3.82895137 1.66161437,4.04095137 C1.60537517,4.15095137 1.55110929,4.25895137 1.52841628,4.38195137 C1.50868323,4.48795137 1.50769657,4.59595137 1.50966988,4.70395137 C1.56985568,4.65695137 1.56689573,4.75495137 1.55110929,4.78395137 C1.52841628,4.82895137 1.5195364,4.87995137 1.51262984,4.92995137 C1.50276331,4.99495137 1.49092348,5.05995137 1.49092348,5.12595137 C1.49092348,5.18195137 1.47316374,5.23395137 1.47217708,5.28795137 C1.45145738,5.27195137 1.49585674,5.20395137 1.45639064,5.21395137 C1.43665759,5.21895137 1.43567094,5.24795137 1.43073768,5.26295137 C1.41495124,5.31495137 1.34489891,5.30995137 1.33404573,5.36895137 C1.32812581,5.40495137 1.3241792,5.42595137 1.30049954,5.45495137 C1.28175314,5.47695137 1.29951289,5.48695137 1.3034595,5.50895137 C1.31233937,5.56095137 1.245247,5.63295137 1.26300675,5.67495137 C1.27977984,5.71595137 1.26794001,5.76195137 1.28668641,5.80095137 C1.29655293,5.82095137 1.31924594,5.84695137 1.31036607,5.87195137 C1.26794001,5.87995137 1.3222059,5.97795137 1.32615251,6.00795137 C1.33207242,6.05695137 1.37548513,6.21095137 1.42284446,6.23295137 C1.48204361,6.32395137 1.56294912,6.45095137 1.66753428,6.49695137 C1.74153322,6.52895137 1.76817284,6.43295137 1.80961225,6.39295137 C1.86190483,6.34095137 1.92998386,6.30795137 1.99904954,6.28395137 C2.05726204,6.26295137 2.30096521,6.19195137 2.18355356,6.10795137 L2.18355356,6.10795137 Z M2.28616542,9.39295137 C2.29800525,9.37295137 2.28912538,9.32195137 2.26741903,9.30495137 C2.21512644,9.26095137 2.19440674,9.36495137 2.22795292,9.39595137 C2.24077941,9.42895137 2.27136564,9.41795137 2.28616542,9.39295137 L2.28616542,9.39295137 Z M2.50026902,6.36895137 C2.48546924,6.35595137 2.47461606,6.36395137 2.47362941,6.33695137 C2.47461606,6.31295137 2.47658936,6.26695137 2.44501648,6.29595137 C2.43613661,6.29895137 2.44797644,6.30395137 2.43514996,6.30895137 C2.42627008,6.31195137 2.41936352,6.30495137 2.4134436,6.30195137 C2.39667051,6.29495137 2.38680398,6.29395137 2.3739775,6.31195137 C2.36509763,6.32395137 2.36509763,6.33795137 2.35029784,6.34795137 L2.32464487,6.35695137 C2.315765,6.35995137 2.29011203,6.37795137 2.28912538,6.38795137 C2.28517877,6.40295137 2.30787178,6.41395137 2.32365822,6.41795137 C2.3364847,6.42695137 2.3532578,6.43495137 2.36608428,6.44395137 C2.37891076,6.45295137 2.39963047,6.46895137 2.41541691,6.47295137 C2.4509364,6.49295137 2.50618894,6.51495137 2.53381521,6.47295137 C2.54170843,6.45695137 2.54762835,6.44495137 2.53677517,6.43095137 C2.52690864,6.41595137 2.5111222,6.41195137 2.50618894,6.39995137 C2.50224233,6.38695137 2.51309551,6.37895137 2.50026902,6.36895137 L2.50026902,6.36895137 Z M7.24508107,7.12395137 C7.22633467,7.12495137 7.19278848,7.13695137 7.17798869,7.14995137 C7.14838912,7.17595137 7.21153488,7.19095137 7.23620119,7.19795137 C7.26382747,7.21395137 7.30329357,7.22195137 7.32993319,7.23795137 C7.35262619,7.25495137 7.36841263,7.27795137 7.3940656,7.28895137 C7.42563848,7.30395137 7.46905119,7.31095137 7.50358403,7.31995137 C7.51838382,7.32495137 7.54107683,7.32395137 7.56080988,7.32795137 C7.58251623,7.34095137 7.59238276,7.36095137 7.61014251,7.37495137 C7.64072873,7.40295137 7.68414145,7.40995137 7.7245942,7.40795137 C7.76307365,7.41195137 7.79168657,7.41895137 7.82621941,7.40995137 C7.86568551,7.39995137 7.89331178,7.41995137 7.92981793,7.41995137 C7.94461771,7.41995137 7.9594175,7.40795137 7.97323064,7.40895137 C7.99197704,7.40895137 7.99395034,7.41695137 8.00283021,7.43295137 C8.01861666,7.45595137 8.05906941,7.49095137 8.08768233,7.49195137 C8.10544208,7.49195137 8.11925521,7.48895137 8.134055,7.49395137 C8.15082809,7.50395137 8.15773466,7.50395137 8.16957449,7.51395137 C8.1902942,7.52295137 8.20805394,7.52895137 8.21693381,7.54495137 C8.23272026,7.57295137 8.2317336,7.60395137 8.25639992,7.62595137 C8.27317301,7.63895137 8.29093275,7.65295137 8.3086925,7.66595137 C8.32053233,7.67695137 8.31066581,7.67495137 8.32842555,7.67495137 C8.33829208,7.67695137 8.35703847,7.67695137 8.36986496,7.67295137 C8.41919758,7.66995137 8.39255797,7.59995137 8.37677153,7.57695137 C8.366905,7.55695137 8.35802513,7.54095137 8.36197174,7.52195137 C8.36493169,7.49895137 8.37578487,7.48295137 8.36098508,7.46395137 C8.35309186,7.45195137 8.34223869,7.44595137 8.33138551,7.43995137 C8.32546559,7.43195137 8.32250564,7.42395137 8.31559907,7.41195137 C8.30079928,7.39295137 8.27218636,7.38695137 8.25343996,7.36895137 C8.22186708,7.33695137 8.20509398,7.29095137 8.16464123,7.26095137 C8.14293487,7.24795137 8.12320182,7.25795137 8.09656221,7.24695137 C8.08570903,7.23995137 8.07978911,7.23295137 8.06400267,7.22795137 C8.04920288,7.22295137 8.0363764,7.22595137 8.02256327,7.22495137 C7.99395034,7.22295137 7.96928403,7.19795137 7.94165776,7.19995137 C7.91107153,7.20395137 7.90515161,7.23695137 7.88739187,7.25495137 C7.87160543,7.26795137 7.85384568,7.26795137 7.84792577,7.24695137 C7.84595246,7.21995137 7.85581899,7.20395137 7.86963212,7.18695137 C7.89133848,7.16395137 7.86963212,7.15095137 7.8410192,7.14895137 C7.80451305,7.14895137 7.79760649,7.17795137 7.7828067,7.20895137 C7.75912704,7.24195137 7.74432725,7.21895137 7.71078106,7.21395137 C7.68808806,7.21495137 7.67230162,7.22395137 7.65059526,7.21495137 C7.63579547,7.20995137 7.63283551,7.19795137 7.62198234,7.19095137 C7.60520924,7.18195137 7.59238276,7.18495137 7.58054293,7.19295137 C7.56376984,7.19695137 7.56376984,7.19695137 7.54699674,7.18795137 C7.53219696,7.18195137 7.52825034,7.16995137 7.50950395,7.16595137 C7.47990437,7.15995137 7.44931814,7.18495137 7.42465183,7.17795137 C7.41379865,7.17195137 7.40491878,7.15595137 7.39011899,7.15095137 C7.3733459,7.14095137 7.37630585,7.14995137 7.36545268,7.16195137 C7.34670628,7.17995137 7.32105331,7.18595137 7.30329357,7.17195137 C7.28060056,7.15595137 7.27862725,7.12895137 7.24508107,7.12395137 L7.24508107,7.12395137 Z M8.37183826,8.30595137 C8.3876247,8.30395137 8.39551792,8.28795137 8.40933106,8.28995137 C8.4251175,8.28695137 8.41722428,8.30295137 8.42807746,8.31295137 C8.43794398,8.32195137 8.44781051,8.32195137 8.45767703,8.32195137 C8.47543678,8.32495137 8.50996962,8.32695137 8.51687619,8.31095137 C8.52476941,8.28595137 8.48333,8.28095137 8.47247682,8.26095137 C8.4626103,8.23195137 8.4853033,8.20395137 8.49319652,8.17895137 C8.50503635,8.14495137 8.4626103,8.12995137 8.46655691,8.10295137 C8.46557025,8.07395137 8.4853033,8.06395137 8.47938339,8.03595137 C8.47445013,8.01495137 8.45669038,7.99195137 8.4438639,7.97695137 C8.43202407,7.96095137 8.40933106,7.94595137 8.41130436,7.92295137 C8.41327767,7.89895137 8.45669038,7.89995137 8.43597068,7.87095137 C8.42413085,7.84595137 8.39255797,7.85095137 8.36394504,7.84695137 C8.35407852,7.84695137 8.34421199,7.84795137 8.33434547,7.83795137 C8.32546559,7.82395137 8.3294122,7.81695137 8.3294122,7.80695137 C8.32349229,7.77995137 8.30277259,7.76995137 8.27909292,7.75895137 C8.2711997,7.75495137 8.25935987,7.74995137 8.25442661,7.73795137 C8.25048,7.72595137 8.26231983,7.72195137 8.25837322,7.71095137 C8.24554674,7.68495137 8.19818742,7.72095137 8.17845437,7.71195137 C8.16464123,7.70995137 8.16661454,7.69695137 8.15773466,7.68295137 L8.134055,7.67195137 C8.10149547,7.65695137 8.08866899,7.68395137 8.0945889,7.71095137 C8.10938869,7.77195137 8.15378805,7.81195137 8.14885479,7.87295137 C8.15181475,7.89795137 8.15576136,7.90995137 8.16464123,7.93295137 C8.17253445,7.96595137 8.18141432,7.98195137 8.16661454,8.01395137 C8.14293487,8.03195137 8.16464123,8.05395137 8.17253445,8.07695137 C8.17746771,8.10795137 8.18536093,8.13195137 8.18437428,8.16495137 C8.17845437,8.22495137 8.15970797,8.28395137 8.16464123,8.34495137 C8.16760119,8.36995137 8.16562788,8.39295137 8.17450776,8.41695137 C8.17845437,8.44795137 8.20312068,8.45895137 8.22877365,8.47595137 C8.25343996,8.49695137 8.36789165,8.56595137 8.33434547,8.48195137 C8.32447894,8.46295137 8.3086925,8.43595137 8.30375924,8.41395137 C8.29586602,8.39095137 8.32349229,8.37495137 8.32447894,8.35095137 C8.32842555,8.32395137 8.30770585,8.31495137 8.3461853,8.30795137 C8.35407852,8.30195137 8.36591835,8.30795137 8.37183826,8.30595137 L8.37183826,8.30595137 Z M7.1819353,1.09995137 C7.21252153,1.09295137 7.24310776,1.10195137 7.27172069,1.09095137 C7.28652047,1.08495137 7.33486645,1.06795137 7.33190649,1.04795137 C7.32697323,1.01095137 7.17009547,1.03495137 7.14444251,1.04595137 C7.13654929,1.06895137 7.16022895,1.08695137 7.18094865,1.09295137 C7.18094865,1.09495137 7.1819353,1.09795137 7.1819353,1.09995137 L7.1819353,1.09995137 Z M7.93573784,7.78795137 C7.92981793,7.77495137 7.93573784,7.76295137 7.93573784,7.74995137 C7.93277788,7.72895137 7.92685797,7.72295137 7.92981793,7.70095137 C7.93672449,7.68895137 7.93672449,7.66995137 7.93376454,7.65495137 C7.92784462,7.64295137 7.9179781,7.63295137 7.90909822,7.62395137 C7.90909822,7.61795137 7.90613827,7.60795137 7.8992317,7.60195137 C7.88739187,7.58995137 7.87456538,7.60795137 7.8617389,7.61395137 C7.85187238,7.62295137 7.83312598,7.62895137 7.83016602,7.63895137 C7.8202995,7.65395137 7.82621941,7.66595137 7.82621941,7.67895137 L7.82917937,7.69395137 C7.80747301,7.71595137 7.82819272,7.77395137 7.82523276,7.79595137 C7.82523276,7.82095137 7.78971327,7.89095137 7.83707259,7.86995137 C7.84989907,7.86395137 7.8597656,7.85495137 7.87160543,7.84895137 C7.88739187,7.83995137 7.90712492,7.83995137 7.92587132,7.83395137 C7.93179123,7.83395137 7.96632407,7.83095137 7.96632407,7.82495137 C7.96731072,7.81195137 7.9386978,7.80295137 7.93573784,7.78795137 L7.93573784,7.78795137 Z M7.0447906,9.05195137 C7.0842567,9.07095137 7.15332238,9.03295137 7.19081518,9.02095137 C7.2381745,9.00595137 7.31316009,8.95595137 7.36150607,8.98395137 C7.38123912,8.99495137 7.39110564,9.01795137 7.41182535,9.02695137 C7.43747831,9.03795137 7.46806454,9.02795137 7.49371751,9.02195137 C7.52035712,9.01595137 7.55094335,9.01195137 7.57560967,8.99995137 C7.59731602,8.98895137 7.61112916,8.97095137 7.62987556,8.95695137 C7.67822153,8.91995137 7.71966094,8.95495137 7.77294017,8.94695137 C7.8035264,8.94295137 7.83213933,8.92795137 7.8617389,8.91995137 C7.88344526,8.91495137 7.92192471,8.91495137 7.9386978,8.89895137 C7.9574442,8.88095137 7.94856432,8.84195137 7.94856432,8.81895137 C7.94757767,8.78795137 7.94955098,8.75595137 7.9386978,8.72695137 C7.91699144,8.66995137 7.83805924,8.60295137 7.9199514,8.55795137 C7.93573784,8.45795137 7.81931284,8.47495137 7.78576666,8.40295137 C7.7640603,8.35595137 7.75715373,8.31995137 7.69499462,8.31495137 C7.64270204,8.30995137 7.61112916,8.33795137 7.56574314,8.35595137 C7.51443721,8.37495137 7.47497111,8.35795137 7.43057174,8.33295137 C7.40393213,8.31795137 7.34769293,8.28295137 7.33683975,8.32895137 C7.32697323,8.36895137 7.36545268,8.40795137 7.3338798,8.44395137 C7.30625352,8.47595137 7.25790755,8.48995137 7.21844145,8.49895137 C7.13260268,8.51695137 7.06452365,8.58295137 7.00236454,8.63995137 L7.00927111,8.64595137 C6.9846048,8.64495137 6.94809865,8.71095137 6.947112,8.73095137 C6.95697853,8.73395137 6.9658584,8.73695137 6.97671158,8.73995137 C6.97572493,8.77395137 7.01420437,8.75095137 7.01716433,8.72695137 C7.02505755,8.72895137 7.03295077,8.73395137 7.04084399,8.73495137 C7.04775056,8.73695137 7.06255035,8.73595137 7.06847026,8.73895137 C7.08524336,8.74595137 7.08820331,8.76195137 7.10892302,8.76395137 C7.09708319,8.81595137 7.10793636,8.87095137 7.08327005,8.91995137 C7.06748361,8.94995137 6.98756476,9.02395137 7.0447906,9.05195137 L7.0447906,9.05195137 Z M7.4522781,1.35995137 C7.48187768,1.39195137 7.51838382,1.40095137 7.51147725,1.45095137 C7.54897005,1.45595137 7.57264971,1.46995137 7.59435606,1.43795137 C7.6081692,1.41795137 7.6288889,1.40195137 7.65158191,1.39395137 C7.67920818,1.38295137 7.79267322,1.38395137 7.78773996,1.43095137 C7.78478,1.45395137 7.77096687,1.47395137 7.76702026,1.49695137 C7.762087,1.52895137 7.79661983,1.50595137 7.81141962,1.51395137 C7.79464653,1.52595137 7.77392683,1.53295137 7.75320712,1.53795137 C7.762087,1.54395137 7.76800691,1.55195137 7.76899356,1.56195137 C7.7433406,1.56795137 7.73051411,1.63995137 7.6851281,1.65395137 C7.65750183,1.66295137 7.61704907,1.64395137 7.5894228,1.64095137 C7.55686327,1.63695137 7.53219696,1.62695137 7.49963742,1.62495137 C7.46806454,1.62295137 7.49371751,1.58095137 7.4542514,1.58895137 C7.44734484,1.61695137 7.46115797,1.68795137 7.46609124,1.71595137 C7.4710245,1.75095137 7.50062407,1.77095137 7.53515691,1.77695137 C7.58350289,1.78495137 7.6061959,1.80095137 7.6476353,1.82495137 C7.68019484,1.84295137 7.71670098,1.83195137 7.75222047,1.83495137 C7.77590013,1.83695137 7.79563318,1.84595137 7.81339293,1.86095137 C7.80944632,1.87195137 7.80056644,1.88995137 7.80648636,1.90195137 C7.81339293,1.91795137 7.86371221,1.89995137 7.87555204,1.89895137 C7.91107153,1.89495137 7.94461771,1.85595137 7.9781639,1.86095137 C7.99099038,1.86295137 8.05018954,1.88095137 8.04722958,1.89595137 C8.0156567,1.88295137 7.99493699,1.92195137 7.97027068,1.90095137 C7.94856432,1.88195137 7.89035183,1.89795137 7.92784462,1.92495137 C7.93080458,1.92795137 7.9406711,2.00295137 7.9406711,2.01095137 C7.93771115,2.03895137 7.88739187,2.06895137 7.89133848,2.08695137 C7.89824505,2.08795137 7.94363106,2.09095137 7.95349759,2.09995137 C7.95645755,2.08795137 7.94757767,2.08295137 7.97520394,2.07495137 C7.99592365,2.06895137 8.02058996,2.06695137 8.04130966,2.07695137 C8.04920288,2.11195137 8.02354992,2.14795137 8.07189589,2.13895137 C8.11728191,2.12995137 8.13701496,2.15995137 8.18437428,2.12895137 C8.21397386,2.11095137 8.24554674,2.11395137 8.2711997,2.13995137 C8.30573254,2.17395137 8.23568021,2.22095137 8.27613297,2.25395137 C8.29191941,2.26695137 8.30474589,2.30695137 8.32053233,2.31395137 C8.33138551,2.31895137 8.39058466,2.29795137 8.40143784,2.29295137 C8.42018424,2.32695137 8.43695733,2.27495137 8.45175712,2.27195137 C8.45767703,2.24995137 8.4853033,2.22495137 8.51194292,2.22195137 C8.55140902,2.21795137 8.55239568,2.22495137 8.5790353,2.24395137 C8.65698085,2.29795137 8.64612767,2.16595137 8.68658042,2.13195137 C8.75959271,2.07195137 8.79609885,2.01495137 8.84641813,1.93795137 C8.88588423,1.87595137 8.94113678,1.86095137 9.01118911,1.84995137 C9.06644165,1.84095137 9.15129377,1.82795137 9.17398677,1.76795137 C9.20062639,1.69795137 9.13649398,1.65995137 9.08025478,1.63895137 C9.01710902,1.61695137 8.94607004,1.59295137 8.97369631,1.51295137 C9.00625584,1.41995137 8.97764292,1.36595137 8.87897767,1.33595137 C8.67079398,1.27095137 8.48333,1.16195137 8.2711997,1.10195137 C8.08373572,1.04895137 7.89429844,1.02995137 7.70190119,1.01995137 C7.61606242,0.98995137 7.43451835,0.98695137 7.38222577,1.05995137 C7.34867958,1.10695137 7.39110564,1.14795137 7.38715903,1.19695137 C7.38222577,1.25595137 7.41083869,1.31595137 7.4522781,1.35995137 L7.4522781,1.35995137 Z M10.7269779,10.6309514 L10.7259912,10.6299514 C10.7289512,10.6349514 10.7269779,10.6439514 10.7279645,10.6509514 C10.766444,10.6509514 10.7832171,10.6859514 10.8246565,10.6729514 C10.8670825,10.6609514 10.8917488,10.6199514 10.8582027,10.5859514 C10.8286031,10.5569514 10.8029501,10.5319514 10.7595374,10.5399514 C10.7082315,10.5499514 10.7190846,10.5909514 10.7269779,10.6309514 L10.7269779,10.6309514 Z M12.0678387,9.29395137 C12.0658654,9.28495137 12.0638921,9.27695137 12.0619187,9.26795137 C12.021466,9.25595137 11.995813,9.29795137 11.9583202,9.26695137 C11.8862946,9.31595137 11.9632535,9.41295137 11.8448552,9.40695137 C11.8655749,9.43195137 11.8636016,9.45995137 11.8537351,9.48895137 C11.8389353,9.53395137 11.8270954,9.52995137 11.7965092,9.53595137 C11.7323768,9.54595137 11.7017906,9.50595137 11.6820575,9.45195137 C11.6189118,9.45395137 11.5320863,9.55195137 11.4827537,9.58295137 C11.4699272,9.58995137 11.4472342,9.61095137 11.4334211,9.61995137 C11.4225679,9.62595137 11.3959283,9.63895137 11.3821151,9.64695137 C11.348569,9.66395137 11.2765433,9.68695137 11.2725967,9.72495137 C11.2558236,9.72195137 11.2301707,9.73195137 11.2133976,9.72995137 C11.2074776,9.73795137 11.2074776,9.74695137 11.2133976,9.75595137 C11.2903565,9.76895137 11.3308092,9.74295137 11.3959283,9.71495137 C11.4640073,9.68395137 11.5370196,9.69095137 11.601152,9.66695137 C11.6317382,9.65595137 11.6327249,9.62195137 11.6830442,9.64195137 C11.7047505,9.65195137 11.7304035,9.68395137 11.7353368,9.70595137 C11.7452033,9.75595137 11.6929107,9.82995137 11.6406181,9.83295137 C11.6277916,9.80195137 11.646538,9.76995137 11.6524579,9.74495137 C11.5833923,9.72195137 11.4699272,9.81995137 11.4511808,9.87795137 C11.5222198,9.89295137 11.5518194,9.99695137 11.5133399,10.0539514 C11.5005135,10.0679514 11.4857137,10.0859514 11.4610474,10.0939514 C11.4205946,10.1059514 11.4018482,10.0689514 11.3623821,10.0979514 C11.3110762,10.1369514 11.3673154,10.2439514 11.3377158,10.3039514 C11.3150228,10.3499514 11.2765433,10.3669514 11.2439838,10.3989514 C11.2222774,10.4219514 11.209451,10.4469514 11.1798514,10.4669514 C11.1413719,10.4929514 11.0476399,10.5489514 11.0555332,10.6029514 C11.1403853,10.6319514 11.3160094,10.4839514 11.3890217,10.4349514 C11.4353944,10.4039514 11.4640073,10.3559514 11.5113666,10.3249514 C11.5646459,10.2919514 11.6346982,10.2749514 11.669231,10.2159514 C11.6889641,10.1819514 11.6731776,10.1519514 11.6850175,10.1179514 C11.6958707,10.0879514 11.7165904,10.0779514 11.7363234,10.0549514 C11.7728296,10.0109514 11.8063757,9.99695137 11.8478151,9.96095137 C11.8991211,9.91495137 11.8872812,9.84295137 11.9109609,9.78195137 C11.9316806,9.72895137 11.9721334,9.68795137 12.0007463,9.63795137 C12.0451457,9.55895137 12.1615707,9.37095137 12.112238,9.28195137 C12.1003982,9.29195137 12.0786918,9.28895137 12.0678387,9.29395137 L12.0678387,9.29395137 Z M13.0752109,6.73495137 C13.0495579,6.68695137 13.0880374,6.54895137 13.0880374,6.49195137 C13.0870507,6.38695137 13.0554778,6.30795137 13.0406781,6.20995137 C13.0317982,6.11795137 13.0189717,5.87395137 13.0525179,5.79095137 C13.0998772,5.67395137 12.8690005,5.47595137 12.856174,5.34895137 C12.8443342,5.23895137 12.7821751,5.13495137 12.6923897,5.07195137 C12.6558836,5.04495137 12.5769514,4.68195137 12.5305787,4.69895137 C12.5078857,4.70995137 12.555245,4.78995137 12.5522851,4.81495137 C12.5394586,4.90295137 12.4950592,4.81495137 12.4486865,4.83495137 C12.3628478,4.86995137 12.2720757,4.95295137 12.2612226,5.03795137 C12.2207698,5.35295137 11.9977863,5.02695137 12.0155461,5.01395137 C12.0648787,4.97595137 12.0826384,4.98795137 12.1408509,4.97995137 C12.2049834,4.95695137 12.1053315,4.91095137 12.20597,4.90095137 C12.1822904,4.83595137 12.2355696,4.81495137 12.2099166,4.76395137 C12.1714372,4.68895137 12.1438109,4.69795137 12.1822904,4.61695137 C12.1990634,4.57295137 12.0984249,4.43395137 12.0905317,4.38095137 C12.0826384,4.32895137 12.0816518,4.26095137 12.0747452,4.20295137 C12.0707986,4.16595137 12.1309844,4.13095137 12.1201312,4.10195137 C12.1181579,3.99895137 12.1408509,3.88795137 12.1043448,3.78795137 C12.0786918,3.71995137 12.0490923,3.62995137 12.0056796,3.57195137 C11.9908798,3.55195137 11.9445071,3.44895137 11.9395738,3.41995137 C11.927734,3.35595137 11.8991211,3.37995137 11.8636016,3.35495137 C11.8438685,3.32995137 11.7550698,3.24695137 11.7294168,3.23495137 C11.7057372,3.22395137 11.5340596,3.06695137 11.530113,3.05395137 C11.5153132,3.00895137 11.4186213,2.97395137 11.4294745,2.92495137 C11.4452609,2.85095137 11.1877446,2.65895137 11.115719,2.64595137 C11.0693463,2.63795137 11.2577969,2.86395137 11.2568103,2.85895137 C11.2597702,2.87195137 11.3781685,3.02295137 11.3781685,3.02295137 C11.4048082,3.03195137 11.4699272,3.21695137 11.4679539,3.24095137 C11.4610474,3.31095137 11.2804899,3.12595137 11.2666768,3.10095137 C11.1778781,2.99195137 11.0170537,2.90395137 10.9154285,2.83095137 C10.8434029,2.76395137 10.8789224,2.72595137 10.7555908,2.66895137 C10.7102048,2.64795137 10.5868732,2.54695137 10.5483938,2.54395137 C10.5020211,2.54195137 10.5553003,2.63995137 10.556287,2.65095137 C10.5631935,2.72095137 10.6391658,2.72595137 10.6845518,2.77195137 C10.7210579,2.80995137 10.7536175,2.85695137 10.7220446,2.89895137 C10.7210579,2.89895137 10.6648188,3.00295137 10.6618588,2.99395137 C10.6776452,3.03795137 10.80887,3.13495137 10.8414296,3.17095137 C10.8355096,3.16195137 11.0131071,3.39495137 11.0279069,3.27095137 C11.0338268,3.22595137 10.9835075,3.17195137 10.9904141,3.13295137 C10.9953474,3.10895137 11.1936645,3.35995137 11.2045177,3.38195137 C11.2528637,3.51495137 11.2489171,3.36195137 11.2992363,3.37795137 C11.3406757,3.39095137 11.4521675,3.52995137 11.3594221,3.53595137 C11.2183308,3.54495137 11.3850751,3.66795137 11.4245412,3.68695137 C11.5064334,3.72695137 11.5626726,3.81995137 11.6475247,3.85495137 C11.7807228,3.90895137 11.7530965,4.00495137 11.8201889,4.10295137 C11.8418952,4.13395137 11.4373677,4.10295137 11.4057948,4.12095137 C11.3525156,4.16295137 11.6090452,4.44995137 11.6100319,4.49295137 C11.6120052,4.58295137 11.6633111,4.64895137 11.6771243,4.73895137 C11.6850175,4.82195137 11.675151,4.93095137 11.7294168,4.99795137 C11.7738162,5.03895137 11.8152556,4.92995137 11.8853079,4.99495137 C11.9109609,5.00695137 11.9474671,5.03595137 11.9553603,5.05795137 C11.9790399,5.11995137 12.1132247,5.49895137 11.9524003,5.47095137 C11.8813613,5.45795137 11.9218141,5.76895137 11.9267473,5.81395137 C11.9484537,5.91195137 11.9879198,5.90395137 11.9622668,6.02795137 C11.9652268,6.13095137 11.882348,6.18295137 11.8231488,6.25695137 C11.7955226,6.29095137 11.7777628,6.33095137 11.7649363,6.37395137 C11.7323768,6.34195137 11.7165904,6.29095137 11.6712043,6.27395137 C11.6218717,6.25495137 11.5133399,6.31495137 11.4699272,6.33595137 C11.3653421,6.38895137 11.442301,6.48495137 11.4008615,6.56795137 C11.371262,6.62895137 11.2824632,6.65895137 11.2242507,6.68895137 C11.1541984,6.72495137 11.0604664,6.76295137 10.9914007,6.70495137 C10.9322016,6.65695137 10.9578546,6.55995137 10.8956954,6.51795137 C10.8256431,6.47095137 10.8187366,6.57595137 10.8029501,6.61795137 C10.7723639,6.69695137 10.6806052,6.72395137 10.7042849,6.82295137 C10.7141514,6.86395137 10.7348711,6.90095137 10.7427643,6.94195137 C10.7526308,6.99295137 10.7269779,7.03895137 10.7240179,7.08995137 C10.718098,7.17695137 10.80887,7.19695137 10.8325497,7.26795137 C10.8532694,7.33195137 10.831563,7.43095137 10.7605241,7.45495137 C10.6845518,7.48195137 10.6006863,7.41295137 10.5257007,7.40495137 C10.4507152,7.39695137 10.3550099,7.41795137 10.3411967,7.50395137 C10.3283702,7.57995137 10.4053291,7.64195137 10.3678363,7.71995137 C10.3520499,7.75295137 10.3244236,7.77895137 10.3046906,7.80895137 C10.2701577,7.85895137 10.2504247,7.91695137 10.2178652,7.96795137 C10.2563446,7.96895137 10.252398,7.94495137 10.2869308,7.95195137 C10.323437,7.95995137 10.3559965,7.92295137 10.3865827,7.91095137 C10.3925027,7.93495137 10.3895427,7.95995137 10.3925027,7.98395137 C10.4181556,7.99195137 10.4438086,7.98195137 10.4665016,7.97295137 C10.4694616,7.99395137 10.459595,8.01795137 10.4684749,8.03895137 C10.4753815,8.05695137 10.4961012,8.06295137 10.507941,8.07695137 C10.5385272,8.11395137 10.5010344,8.17495137 10.4793281,8.20695137 C10.417169,8.29895137 10.3106105,8.34995137 10.2415448,8.43595137 C10.1764257,8.51595137 10.1705058,8.61295137 10.1221599,8.69995137 C10.1053868,8.72995137 10.0886137,8.77095137 10.133013,8.78495137 C10.1428796,8.76895137 10.1576794,8.75595137 10.1783991,8.75595137 C10.2089853,8.75495137 10.1971455,8.77795137 10.2129319,8.79595137 C10.2770643,8.87795137 10.3451433,8.74295137 10.3727696,8.70395137 C10.4003959,8.66195137 10.5148476,8.59895137 10.5464205,8.66895137 C10.5710868,8.72195137 10.5424738,8.79695137 10.5178075,8.84495137 C10.5592469,8.86395137 10.5474071,8.89395137 10.5572736,8.92995137 C10.5701001,8.97995137 10.6154861,9.01195137 10.6154861,9.06695137 C10.6154861,9.13295137 10.4714349,9.26395137 10.5276741,9.31395137 C10.5977264,9.37595137 10.6806052,9.20395137 10.7082315,9.16695137 C10.7605241,9.09595137 10.879909,9.08595137 10.9095086,8.99895137 C10.9420681,8.89995137 10.9312149,8.84095137 11.0624397,8.83795137 C11.1176923,8.83695137 11.158145,8.80195137 11.2104376,8.79095137 C11.2676635,8.77995137 11.2933164,8.77395137 11.3298226,8.72995137 C11.3821151,8.66695137 11.4294745,8.74195137 11.4314478,8.79195137 C11.4334211,8.84295137 11.4107281,8.90695137 11.442301,8.95295137 C11.4807804,9.00895137 11.5232065,8.93495137 11.5626726,8.89895137 C11.558726,8.93695137 11.6090452,8.95895137 11.6386448,8.97095137 C11.6840308,8.93995137 11.7126437,8.88895137 11.7609897,8.86095137 C11.7836827,8.84795137 11.8093357,8.84295137 11.8349887,8.83895137 C11.8418952,8.87995137 11.8488018,8.92395137 11.8853079,8.94395137 C11.9376005,8.97395137 11.8734681,9.00295137 11.9425338,9.03495137 C12.0283726,9.06795137 12.0569855,9.15495137 12.0984249,9.22495137 C12.1181579,9.25695137 12.2977287,9.06195137 12.3667944,9.05495137 C12.5956978,9.02895137 12.7150827,8.72995137 12.7999348,8.55295137 C12.9222798,8.29995137 12.9775323,8.01895137 13.0091052,7.75795137 C13.0870507,7.59695137 13.1186236,7.30195137 13.0870507,7.11495137 C13.0683043,7.00095137 13.1334234,6.84295137 13.0752109,6.73495137 L13.0752109,6.73495137 Z M11.0032406,10.5319514 C11.0091605,10.5039514 11.0683596,10.3999514 11.0131071,10.3849514 C10.993374,10.3799514 10.976601,10.4099514 10.9588412,10.4149514 C10.9351615,10.4229514 10.9095086,10.4079514 10.8878022,10.4189514 C10.8680692,10.4299514 10.8493228,10.4619514 10.8374829,10.4799514 C10.8226832,10.5019514 10.8286031,10.5109514 10.8522827,10.5229514 C10.8759624,10.5359514 10.9065486,10.5419514 10.9203618,10.5679514 C10.9322016,10.5909514 10.9262817,10.6219514 10.9233217,10.6459514 C10.9233217,10.6449514 10.9272683,10.6409514 10.928255,10.6369514 C10.9322016,10.6359514 10.9391082,10.6349514 10.9430548,10.6359514 L10.9381215,10.6459514 C11.0012673,10.6559514 10.996334,10.5729514 11.0032406,10.5319514 L11.0032406,10.5319514 Z M11.7422433,9.28095137 C11.7442166,9.31095137 11.7767762,9.30795137 11.7984825,9.29995137 C11.8182156,9.29395137 11.8310421,9.27695137 11.8438685,9.26195137 C11.8616283,9.23895137 11.8724815,9.21595137 11.856695,9.18895137 C11.8409086,9.16095137 11.8310421,9.14095137 11.8231488,9.10795137 C11.8103223,9.11495137 11.7945359,9.12695137 11.7807228,9.13095137 C11.7669096,9.13595137 11.7649363,9.13195137 11.7491499,9.13095137 C11.7126437,9.12995137 11.720537,9.15795137 11.7047505,9.18095137 C11.691924,9.20095137 11.6633111,9.20895137 11.6741643,9.23495137 C11.6820575,9.25495137 11.7146171,9.27195137 11.7333635,9.28095137 L11.7382967,9.27495137 C11.7373101,9.27695137 11.7363234,9.27795137 11.7353368,9.27995137 C11.7373101,9.28095137 11.74027,9.28095137 11.7422433,9.28095137 L11.7422433,9.28095137 Z M8.18042767,11.4279514 C8.21693381,11.3629514 8.28205288,11.3219514 8.34026538,11.2769514 C8.41031771,11.2229514 8.47247682,11.1599514 8.52772936,11.0919514 C8.49516983,11.0839514 8.49319652,11.0529514 8.47247682,11.0329514 C8.44090394,11.0019514 8.39255797,11.0219514 8.3856514,10.9749514 C8.37874483,10.9329514 8.34421199,10.9239514 8.31066581,10.9069514 C8.23370691,10.8679514 8.20213403,10.7919514 8.13997492,10.7389514 C8.07189589,10.6789514 7.97915055,10.6989514 7.89627174,10.6829514 C7.82325945,10.6689514 7.74926051,10.5519514 7.67131496,10.6019514 C7.62198234,10.6329514 7.59928933,10.7119514 7.63283551,10.7609514 C7.65947513,10.7989514 7.70486115,10.8179514 7.72262089,10.8629514 C7.69598128,10.8879514 7.69006136,10.9039514 7.72262089,10.9269514 C7.76110034,10.9539514 7.83509928,10.9819514 7.81635289,11.0409514 C7.80648636,11.0729514 7.77984674,11.1039514 7.7453139,11.1099514 C7.72064759,11.1149514 7.66046178,11.1009514 7.67328827,11.1459514 C7.645662,11.0719514 7.56771645,11.1879514 7.52529039,11.1269514 C7.49075755,11.0779514 7.46905119,11.0339514 7.4147853,11.0009514 C7.34473297,10.9579514 7.44339823,10.9159514 7.4315584,10.8509514 C7.41379865,10.7559514 7.2983603,10.7819514 7.2569209,10.7119514 C7.23225458,10.6719514 7.26580077,10.6399514 7.28158721,10.6049514 C7.29737365,10.5689514 7.33979971,10.5979514 7.36249272,10.6079514 C7.43649166,10.6429514 7.54502344,10.6299514 7.60718255,10.5789514 C7.63579547,10.5549514 7.69894123,10.4439514 7.61902238,10.4439514 C7.56376984,10.4449514 7.52134378,10.4929514 7.46905119,10.4959514 C7.46115797,10.4329514 7.4315584,10.3259514 7.49865077,10.2839514 C7.55982323,10.2459514 7.68808806,10.2019514 7.63382217,10.1039514 C7.61408912,10.0699514 7.57955628,10.1259514 7.55094335,10.1009514 C7.53910352,10.0909514 7.5479834,10.0679514 7.55193001,10.0569514 C7.53318361,10.0399514 7.51542386,10.0189514 7.50555734,9.99495137 C7.46214463,9.88895137 7.59040945,9.80595137 7.53614357,9.69395137 C7.51345056,9.64695137 7.47497111,9.61895137 7.43254505,9.58995137 C7.39011899,9.55995137 7.38814568,9.52195137 7.37235924,9.47695137 C7.36446602,9.45195137 7.32302662,9.39295137 7.28750713,9.40795137 C7.2569209,9.41995137 7.24804102,9.47295137 7.22436136,9.49495137 C7.17108213,9.54695137 7.05860374,9.56695137 6.98756476,9.54995137 C6.93033891,9.53695137 6.93329887,9.51495137 6.9056726,9.47695137 C6.89679272,9.46295137 6.87705967,9.46195137 6.86225988,9.45595137 C6.83660692,9.44595137 6.83364696,9.42295137 6.82772704,9.39995137 C6.80404738,9.31295137 6.63236984,9.42095137 6.60573022,9.29895137 C6.59981031,9.27095137 6.60967683,9.22395137 6.56922408,9.21795137 C6.52383806,9.20995137 6.52186476,9.16595137 6.52186476,9.12895137 C6.52186476,9.09895137 6.52383806,9.05695137 6.49226518,9.03995137 C6.45181243,9.01795137 6.4419459,9.02795137 6.42911942,8.98195137 C6.41431963,8.92295137 6.37386688,8.98395137 6.34032069,8.97195137 C6.26928171,8.94395137 6.28210819,8.97895137 6.22488235,9.00895137 C6.12720374,9.06095137 6.11635057,8.81995137 6.08280438,8.77295137 C6.01768531,8.68295137 6.03445841,8.88395137 5.99005904,8.90895137 C5.94960629,8.93195137 5.90718023,8.87895137 5.89238044,8.84795137 C5.88350057,8.82995137 5.87856731,8.80995137 5.86771413,8.79195137 C5.85094103,8.76595137 5.82134146,8.75495137 5.80456837,8.72895137 C5.79075523,8.70595137 5.77003553,8.67895137 5.760169,8.65395137 C5.75128913,8.63195137 5.75326243,8.60395137 5.73648934,8.58595137 C5.71576964,8.56295137 5.7414226,8.52495137 5.75622239,8.49595137 C5.78187536,8.48595137 5.82035481,8.50595137 5.8391012,8.52295137 C5.88547387,8.56195137 5.9555262,8.73295137 6.03643171,8.70095137 C6.01965862,8.67895137 6.0305118,8.65195137 6.01867197,8.62795137 C6.00584548,8.60295137 5.98117917,8.58795137 5.96243277,8.56795137 C5.92099336,8.51995137 5.87560735,8.47195137 5.84798108,8.41395137 C5.82430142,8.36395137 5.81246159,8.31095137 5.76411561,8.27595137 C5.72464951,8.24695137 5.64670396,8.21895137 5.66347705,8.15695137 C5.66347705,8.15595137 5.66446371,8.15495137 5.66446371,8.15495137 C5.69702324,8.16195137 5.71971625,8.18595137 5.74339591,8.20695137 C5.77792875,8.23695137 5.82232811,8.25195137 5.86278087,8.27195137 C5.93677981,8.30795137 6.02261858,8.33295137 6.08576434,8.38795137 C6.12523044,8.42095137 6.10451074,8.49495137 6.15384336,8.53595137 C6.19034951,8.56595137 6.2446154,8.66695137 6.31170777,8.62695137 C6.33637408,8.61195137 6.34722726,8.58295137 6.37189357,8.56595137 C6.39853319,8.54695137 6.44293256,8.52995137 6.47351878,8.51595137 C6.49226518,8.50695137 6.52383806,8.50995137 6.53863785,8.49495137 C6.56231751,8.47195137 6.50607832,8.40595137 6.49325184,8.38895137 C6.44293256,8.32395137 6.39655989,8.25295137 6.32946751,8.20395137 C6.29493468,8.17895137 6.26138849,8.15195137 6.22093574,8.13495137 C6.19922938,8.12595137 6.16074993,8.12695137 6.15680332,8.09695137 C6.1676565,8.10395137 6.17357641,8.10195137 6.17554972,8.09095137 C6.17456307,8.07095137 6.14595014,8.06995137 6.13213701,8.06595137 C6.09859082,8.05695137 6.07589781,8.05695137 6.06307133,8.02895137 C6.04629824,7.99495137 5.98709908,7.99595137 5.9555262,7.98795137 C5.90816688,7.97595137 5.87067409,7.93995137 5.82528807,7.92195137 C5.77200883,7.90195137 5.73155608,7.92295137 5.67926349,7.93495137 C5.67038362,7.93695137 5.65262388,7.96795137 5.63585078,7.99395137 C5.59835799,7.98495137 5.55691858,7.98895137 5.5253457,8.01395137 C5.47798638,8.05095137 5.45036011,8.10695137 5.41286731,8.15295137 C5.39708087,8.17195137 5.37438786,8.19095137 5.35169485,8.18395137 C5.34774824,8.18195137 5.34972155,8.17695137 5.34676159,8.17495137 C5.37537451,7.96995137 5.39116095,7.76295137 5.37241456,7.80395137 C5.33492176,7.88395137 5.30729549,7.93995137 5.27868256,7.99795137 C5.23724316,7.97995137 5.18889718,7.97895137 5.17113744,8.02295137 C5.15239104,8.06995137 5.17705735,8.13095137 5.14745778,8.17195137 C5.14055121,8.18295137 5.12969803,8.18195137 5.11983151,8.18695137 C5.1178582,8.18195137 5.10799168,8.16895137 5.10897833,8.16795137 C5.10009846,8.18295137 5.0991118,8.18795137 5.09121858,8.20095137 C5.06161901,8.20195137 5.02511286,8.18995137 4.98860672,8.17795137 C4.98860672,8.17795137 4.98860672,8.17495137 4.98762007,8.17495137 C4.98663341,8.17595137 4.98663341,8.17595137 4.98564676,8.17695137 C4.94223405,8.16195137 4.89684803,8.14795137 4.85738193,8.16195137 C4.77844973,8.18995137 4.77548977,8.30295137 4.72517049,8.37195137 C4.6501849,8.47695137 4.456801,8.43195137 4.42325482,8.30695137 C4.45088109,8.27295137 4.47752071,8.23895137 4.50514698,8.20495137 C4.46272092,8.09695137 4.34925588,8.02195137 4.23480418,8.02495137 C4.20224465,8.02595137 4.16771181,8.03195137 4.13811223,8.01795137 C4.107526,8.00295137 4.09075291,7.97095137 4.06411329,7.95095137 C3.98123448,7.88895137 3.8707294,7.97095137 3.80166372,8.04795137 C3.68326541,8.06795137 3.57374698,8.13495137 3.49974804,8.23095137 C3.45238872,8.22695137 3.4050294,8.22295137 3.35865673,8.21895137 C3.386283,8.29495137 3.29452432,8.35695137 3.25308491,8.42695137 C3.20177898,8.51195137 3.2284186,8.61095137 3.27873787,8.70195137 C3.27281796,8.71595137 3.26887135,8.73095137 3.25604487,8.73695137 C3.19585906,8.76895137 3.2116455,8.78795137 3.22940525,8.85295137 C3.24519169,8.90895137 3.23927177,9.01395137 3.22644529,9.06995137 C3.21657877,9.11395137 3.17316605,9.21995137 3.11988682,9.19495137 C3.09226055,9.18095137 3.06266097,9.16995137 3.037008,9.19595137 C3.02516817,9.20695137 3.01727495,9.22095137 3.01332834,9.23595137 C2.9955686,9.23695137 2.97780885,9.23895137 2.96103576,9.24295137 C2.92748957,9.24995137 2.89197008,9.25795137 2.85941055,9.24395137 C2.82685101,9.22995137 2.7834383,9.20295137 2.74693216,9.21495137 C2.71634593,9.22495137 2.65616013,9.25195137 2.64333364,9.28395137 C2.63741373,9.29795137 2.65616013,9.33495137 2.65616013,9.35395137 C2.65517347,9.38795137 2.68181309,9.43895137 2.67095991,9.46995137 C2.6462936,9.45795137 2.60978746,9.45395137 2.59301436,9.42895137 C2.57722792,9.40795137 2.55354826,9.41295137 2.53578852,9.39095137 C2.53184191,9.42895137 2.51802877,9.48195137 2.47264275,9.49195137 C2.42923004,9.50195137 2.38680398,9.46695137 2.34240462,9.47895137 C2.22597962,9.50895137 2.41245695,9.65495137 2.43712326,9.68295137 C2.47856267,9.72995137 2.4923758,9.79195137 2.52296203,9.84495137 C2.55650822,9.90295137 2.6255739,9.92195137 2.66602665,9.97295137 C2.69957284,10.0159514 2.7064794,10.0739514 2.75482538,10.1059514 C2.80810462,10.1429514 2.85645059,10.1759514 2.87815695,10.2389514 C2.90084995,10.2169514 2.94820928,10.3209514 2.99655525,10.2379514 C3.02220822,10.1929514 3.06759423,10.1539514 3.09620716,10.2269514 C3.12087347,10.2899514 3.09620716,10.3299514 3.15047305,10.3849514 C3.19191245,10.4279514 3.18993915,10.4789514 3.11890016,10.4739514 C3.13073999,10.5059514 3.14948639,10.5379514 3.11890016,10.5669514 C3.10508703,10.5809514 3.06562093,10.6099514 3.09423385,10.6299514 C3.12679338,10.6149514 3.16132622,10.6059514 3.19388576,10.5909514 C3.22940525,10.5759514 3.26393809,10.5399514 3.30537749,10.5409514 C3.3073508,10.5539514 3.25012495,10.5919514 3.28564444,10.5949514 C3.31524402,10.5979514 3.35767008,10.5669514 3.38134974,10.5929514 C3.40798936,10.6209514 3.37444317,10.6639514 3.39022961,10.6949514 C3.40601605,10.7269514 3.45929529,10.7029514 3.48494825,10.7079514 C3.47409508,10.7359514 3.43265567,10.7309514 3.40996266,10.7419514 C3.46225525,10.8059514 3.39417622,10.8999514 3.31820398,10.9019514 C3.28169783,10.9019514 3.15244635,10.7529514 3.14652644,10.8489514 C3.14553978,10.8769514 3.15441966,10.9119514 3.16329953,10.9389514 C3.17513936,10.9739514 3.25999148,10.9589514 3.29057771,10.9719514 C3.33497707,10.9899514 3.386283,11.0329514 3.40404275,11.0779514 C3.42081584,11.1239514 3.45929529,11.1539514 3.47409508,11.1979514 C3.502708,11.2799514 3.58065355,11.2909514 3.66155906,11.3149514 C3.76910418,11.3469514 3.7168116,11.5139514 3.71089169,11.5939514 C3.70595842,11.6729514 3.81646351,11.6919514 3.86678279,11.7369514 C3.92302198,11.7859514 3.93190185,11.8809514 3.83915652,11.8889514 C3.79179719,11.8929514 3.71286499,11.8709514 3.69510524,11.9319514 C3.66945228,12.0179514 3.79969041,12.0089514 3.85691626,12.0279514 C3.88355588,12.0369514 3.99110101,12.0479514 4.00096753,12.0729514 C4.01576732,12.1119514 4.00392749,12.1649514 4.01774062,12.2059514 C4.05128681,12.3109514 4.14797876,12.3809514 4.24171075,12.4329514 C4.44298787,12.5459514 4.68175778,12.6169514 4.90276795,12.6799514 C5.02609952,12.7159514 5.15140439,12.7439514 5.27769591,12.7609514 C5.40004083,12.7769514 5.50758595,12.7669514 5.61217112,12.8349514 C5.68419676,12.8819514 5.72958277,12.8469514 5.80358171,12.8599514 C5.83515459,12.8659514 5.84896773,12.8949514 5.87264739,12.9119514 C5.89928701,12.9329514 5.92987324,12.9059514 5.95848616,12.9169514 C5.96341942,12.8979514 5.96144612,12.8799514 5.95256625,12.8619514 C6.00880544,12.8829514 6.07787112,12.9429514 6.13707027,12.8949514 C6.16666985,12.8709514 6.1864029,12.8379514 6.21698913,12.8149514 C6.25349527,12.8179514 6.28901476,12.8199514 6.3255209,12.8199514 C6.47845205,12.8199514 6.59882366,12.7499514 6.72116857,12.6679514 C6.85239336,12.5799514 7.01025776,12.5779514 7.16220225,12.5639514 C7.32302662,12.5479514 7.49371751,12.5269514 7.64072873,12.4569514 C7.76899356,12.3949514 7.8015531,12.2819514 7.83805924,12.1569514 C7.87752534,12.0209514 7.99592365,11.9659514 8.0738692,11.8559514 C8.16562788,11.7279514 8.10544208,11.5609514 8.18042767,11.4279514 L8.18042767,11.4279514 Z M2.2950453,9.62395137 C2.29011203,9.59195137 2.2782722,9.57195137 2.25360589,9.55295137 C2.25261924,9.55595137 2.25163259,9.55795137 2.25163259,9.56195137 C2.2180864,9.54495137 2.21413979,9.48095137 2.16875377,9.48595137 C2.13126098,9.42595137 2.03654233,9.45295137 2.00694276,9.50795137 C1.98720971,9.54495137 2.01088937,9.56195137 2.03259572,9.58895137 C2.06022199,9.62295137 2.05528873,9.64895137 2.06515526,9.68795137 C2.08982157,9.78895137 2.17368704,9.71995137 2.23288619,9.75895137 C2.2555792,9.77395137 2.26445907,9.81695137 2.29800525,9.80995137 C2.33549805,9.80095137 2.33352475,9.74495137 2.32365822,9.71995137 C2.30984509,9.68395137 2.29997856,9.66295137 2.2950453,9.62395137 L2.2950453,9.62395137 Z M3.08338067,10.8149514 C3.08930059,10.7979514 3.06266097,10.7789514 3.04490122,10.7799514 C3.02911478,10.7809514 3.014315,10.8029514 3.00839508,10.8149514 C2.98866203,10.8499514 3.01036839,10.8969514 3.0557544,10.8969514 C3.06660758,10.8779514 3.06266097,10.8409514 3.09127389,10.8379514 C3.08930059,10.8289514 3.08338067,10.8259514 3.07548745,10.8229514 L3.08338067,10.8149514 L3.08338067,10.8149514 Z M2.24472602,9.54595137 C2.24768598,9.54795137 2.25064593,9.54995137 2.25360589,9.55295137 C2.25656585,9.54795137 2.25952581,9.54395137 2.26149911,9.53795137 L2.24472602,9.54595137 L2.24472602,9.54595137 Z M11.4896603,10.9489514 C11.4728872,10.9649514 11.4778204,10.9829514 11.4699272,11.0019514 C11.4610474,11.0249514 11.4265145,11.0339514 11.4077681,11.0459514 C11.3781685,11.0649514 11.368302,11.1099514 11.3357425,11.1209514 C11.3219293,11.0999514 11.3012096,11.0409514 11.2725967,11.0919514 C11.2558236,11.1249514 11.2666768,11.1579514 11.2400372,11.1879514 C11.2133976,11.2159514 11.2153709,11.2499514 11.1966245,11.2809514 C11.1680115,11.3299514 11.1393986,11.3589514 11.0910527,11.3879514 C11.0525732,11.4109514 11.04468,11.4539514 11.0170537,11.4859514 C10.9874541,11.5209514 10.9420681,11.5339514 10.9016154,11.5519514 C10.8730024,11.5639514 10.8256431,11.5979514 10.7930836,11.5789514 C10.7496709,11.5519514 10.80887,11.5039514 10.8295897,11.4869514 C10.8493228,11.4709514 10.9430548,11.4139514 10.9213484,11.3819514 C10.9065486,11.3609514 10.8532694,11.3639514 10.831563,11.3659514 C10.7871637,11.3709514 10.7536175,11.4159514 10.7161247,11.4369514 C10.6736986,11.4609514 10.6371925,11.4809514 10.5908198,11.4969514 C10.5375406,11.5159514 10.533594,11.5659514 10.4911679,11.5959514 C10.4576217,11.6219514 10.414209,11.6419514 10.3707963,11.6419514 C10.3135705,11.6419514 10.3165304,11.5939514 10.2997573,11.5539514 C10.278051,11.5569514 10.2593046,11.5849514 10.2385849,11.5939514 C10.2050387,11.6079514 10.1833323,11.6239514 10.1981321,11.6609514 C10.2119452,11.6989514 10.0590141,11.7339514 10.0323745,11.7529514 C10.0264546,11.7339514 10.0560541,11.7139514 10.067894,11.7029514 C10.0205346,11.6989514 9.96725541,11.7399514 9.91890944,11.7469514 C9.87253677,11.7529514 9.81531092,11.7849514 9.80840435,11.8329514 C9.80347109,11.8709514 9.75315181,11.8699514 9.72157893,11.8829514 C9.66928635,11.9049514 9.6909927,11.9359514 9.68112618,11.9779514 C9.66139313,12.0569514 9.49662215,11.9969514 9.58048762,11.8919514 C9.61008719,11.8549514 9.65448656,11.8309514 9.68112618,11.7929514 C9.71171241,11.7489514 9.71664567,11.6939514 9.74032533,11.6469514 C9.68803274,11.6619514 9.64856664,11.6919514 9.60416728,11.7209514 C9.553848,11.7539514 9.51142194,11.7459514 9.45518275,11.7349514 C9.39006368,11.7209514 9.34467766,11.7539514 9.28449186,11.7689514 C9.24601241,11.7779514 9.16017364,11.7749514 9.15524038,11.8309514 C9.15228042,11.8669514 9.21443953,11.8739514 9.23515923,11.8949514 C9.26574546,11.9269514 9.29929165,11.9739514 9.32494461,12.0099514 C9.34566432,12.0379514 9.42262321,12.0769514 9.41768995,12.1129514 C9.40979673,12.1829514 9.32198465,12.1709514 9.27462533,12.1829514 C9.22726601,12.1949514 9.22035944,12.2379514 9.1858266,12.2639514 C9.1463605,12.2929514 9.09308127,12.2589514 9.04966855,12.2769514 C9.00526919,12.2939514 8.97369631,12.3339514 8.93521686,12.3599514 C8.87009779,12.4049514 8.82767173,12.3559514 8.76057936,12.3529514 C8.70631347,12.3509514 8.65698085,12.3759514 8.60567491,12.3869514 C8.55930224,12.3969514 8.50306305,12.4049514 8.46754356,12.4379514 C8.38959801,12.5079514 8.64020775,12.4889514 8.66388741,12.4869514 C8.65303424,12.5269514 8.64218106,12.5739514 8.60567491,12.5999514 C8.5602889,12.6329514 8.49615648,12.6249514 8.4438639,12.6369514 C8.40341114,12.6469514 8.34717195,12.6919514 8.4063711,12.7239514 C8.45965034,12.7509514 8.52772936,12.7369514 8.5810086,12.7179514 C8.64218106,12.6969514 8.6994069,12.6639514 8.76353932,12.6499514 C8.83063169,12.6349514 8.90068402,12.6419514 8.96777639,12.6299514 C9.03980203,12.6159514 9.10294779,12.5769514 9.17004016,12.5499514 C9.23417258,12.5239514 9.30126495,12.5139514 9.36934398,12.5119514 C9.35553084,12.5369514 9.28843847,12.5349514 9.2627855,12.5409514 C9.21246622,12.5509514 9.17793338,12.5949514 9.1256408,12.5919514 C9.06644165,12.5899514 9.07334822,12.6319514 9.03092216,12.6419514 C9.00329589,12.6489514 8.93817682,12.7129514 8.91745711,12.6729514 C8.90167067,12.6419514 8.87108445,12.6479514 8.86121792,12.6859514 C8.8533247,12.7139514 8.86911114,12.7239514 8.83063169,12.7249514 C8.80103212,12.7249514 8.78721898,12.7129514 8.76057936,12.7059514 C8.70730012,12.6919514 8.68362046,12.7469514 8.64612767,12.7609514 C8.59087513,12.7819514 8.53167597,12.7749514 8.47839674,12.8099514 C8.44781051,12.8299514 8.41426432,12.8359514 8.37775818,12.8469514 C8.31165246,12.8679514 8.24949335,12.8929514 8.18338763,12.9149514 C8.1320817,12.9329514 8.08077576,12.9549514 8.02552322,12.9559514 C8.00283021,12.9559514 7.91205818,12.9399514 7.89725839,12.9669514 C7.86963212,13.0169514 7.95448424,12.9979514 7.97224399,12.9879514 C8.02256327,12.9609514 8.08373572,12.9769514 8.13997492,12.9769514 C8.20904059,12.9769514 8.26626644,12.9629514 8.32842555,12.9309514 C8.34519864,12.9219514 8.45669038,12.8979514 8.4626103,12.9109514 C8.47247682,12.9169514 8.54647576,12.8899514 8.55930224,12.8869514 C8.61948805,12.8729514 8.67967385,12.8599514 8.73887301,12.8449514 C8.92140372,12.7979514 9.10097449,12.7269514 9.27857194,12.6659514 C9.6327802,12.5459514 9.95837554,12.3429514 10.2662111,12.1369514 C10.4053291,12.0439514 10.5187942,11.9219514 10.669752,11.8459514 C10.8216965,11.7699514 10.9578546,11.6689514 11.0969726,11.5739514 C11.2331306,11.4809514 11.3367291,11.3529514 11.4501942,11.2359514 C11.5646459,11.1169514 11.6613378,11.0049514 11.7116571,10.8469514 C11.6830442,10.8399514 11.6534446,10.8989514 11.6297649,10.9099514 C11.5902988,10.9289514 11.5212332,10.9189514 11.4896603,10.9489514 L11.4896603,10.9489514 Z M10.6391658,10.7879514 C10.6736986,10.7409514 10.6440991,10.6769514 10.5829266,10.7139514 C10.5602336,10.7269514 10.5631935,10.7529514 10.5454338,10.7689514 C10.5266874,10.7859514 10.5247141,10.7659514 10.5059677,10.7609514 C10.4793281,10.7549514 10.4359154,10.7909514 10.4270355,10.8149514 C10.3905294,10.8139514 10.3579698,10.8549514 10.3747429,10.8869514 C10.4230889,10.8689514 10.4526885,10.8239514 10.504981,10.8379514 C10.5464205,10.8489514 10.6125262,10.8229514 10.6391658,10.7879514 L10.6391658,10.7879514 Z" />
</svg>
PK
!<�\���$chrome/devtools/skin/images/grid.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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="12" height="12" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg" stroke="#696969">
  <path fill="none" d="M1 2.5h9m-9 3h9m-9 3h9M2.5 1v9m3-9v9m3-9v9"/>
</svg>
PK
!<������$chrome/devtools/skin/images/help.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16">
  <path fill="context-fill" d="M8 1a7 7 0 1 0 7 7 7.008 7.008 0 0 0-7-7zm0 13a6 6 0 1 1 6-6 6.007 6.007 0 0 1-6 6zM8 3.125A2.7 2.7 0 0 0 5.125 6a.875.875 0 0 0 1.75 0c0-1 .6-1.125 1.125-1.125a1.105 1.105 0 0 1 1.13.744.894.894 0 0 1-.53 1.016A2.738 2.738 0 0 0 7.125 9v.337a.875.875 0 0 0 1.75 0v-.37a1.041 1.041 0 0 1 .609-.824A2.637 2.637 0 0 0 10.82 5.16 2.838 2.838 0 0 0 8 3.125zm0 7.625A1.25 1.25 0 1 0 9.25 12 1.25 1.25 0 0 0 8 10.75z"></path>
</svg>
PK
!<��4���&chrome/devtools/skin/images/import.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b">
  <path d="M7.864 1.417c-.123-.13-.305-.185-.48-.144-.173.04-.312.172-.363.343-.05.17-.007.357.116.487l4 4.243c.19.2.506.21.707.02.2-.188.21-.505.02-.706l-4-4.243z"/>
  <path d="M7.136 1.414l-4 4.243c-.19.2-.18.518.02.707.202.19.52.18.708-.02l4-4.244c.123-.13.166-.316.115-.487-.052-.17-.19-.302-.365-.343-.174-.04-.356.014-.48.144zM1.5 8c-.276 0-.5.224-.5.5v5c0 .2.224.5.5.5h12c.276 0 .5-.3.5-.5v-5c0-.276-.224-.5-.5-.5h-3c-.28 0-.5.224-.5.5s.22.5.5.5H13v4H2V9h2.5c.27 0 .5-.224.5-.5S4.77 8 4.5 8h-3z"/>
  <path d="M7 2v9c0 .276.224.5.5.5s.5-.224.5-.5V2c0-.276-.224-.5-.5-.5S7 1.724 7 2z"/>
</svg>
PK
!<�����3chrome/devtools/skin/images/item-arrow-dark-ltr.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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="7" xmlns="http://www.w3.org/2000/svg" height="12" viewBox="0 0 7 12">
  <path fill="#181d20" d="M7,11.6 7,.4 1.5,6z"/>
  <path fill="#000" d="M7,0 6,0 0,6 6,12 7,12 7,11.6 1.5,6 7,.4z"/>
</svg>
PK
!<|�x��3chrome/devtools/skin/images/item-arrow-dark-rtl.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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="7" xmlns="http://www.w3.org/2000/svg" height="12" viewBox="0 0 7 12">
  <path fill="#181d20" d="M0,11.6 0,.4 5.5,6z"/>
  <path fill="#000" d="M1,0 0,0 0,.4 5.5,6 0,11.6 0,12 1,12 7,6z"/>
</svg>
PK
!<5j����.chrome/devtools/skin/images/item-arrow-ltr.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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="7" xmlns="http://www.w3.org/2000/svg" height="12" viewBox="0 0 7 12">
  <path fill="#ffffff" d="M7,11.6 7,.4 1.5,6z"/>
  <path fill="#dde1e4" d="M7,0 6,0 0,6 6,12 7,12 7,11.6 1.5,6 7,.4z"/>
</svg>
PK
!<�V��.chrome/devtools/skin/images/item-arrow-rtl.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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="7" xmlns="http://www.w3.org/2000/svg" height="12" viewBox="0 0 7 12">
  <path fill="#ffffff" d="M0,11.6 0,.4 5.5,6z"/>
  <path fill="#dde1e4" d="M1,0 0,0 0,.4 5.5,6 0,11.6 0,12 1,12 7,6z"/>
</svg>
PK
!<g�\�||+chrome/devtools/skin/images/item-toggle.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b">
  <circle cx="8" cy="8.5" r="1.5"/>
  <path d="M15.498 8.28l-.001-.03v-.002-.004l-.002-.018-.004-.031c0-.002 0-.002 0 0l-.004-.035.006.082c-.037-.296-.133-.501-.28-.661-.4-.522-.915-1.042-1.562-1.604-1.36-1.182-2.74-1.975-4.178-2.309a6.544 6.544 0 0 0-2.755-.042c-.78.153-1.565.462-2.369.91C3.252 5.147 2.207 6 1.252 7.035c-.216.233-.36.398-.499.577-.338.437-.338 1 0 1.437.428.552.941 1.072 1.59 1.635 1.359 1.181 2.739 1.975 4.177 2.308.907.21 1.829.223 2.756.043.78-.153 1.564-.462 2.369-.91 1.097-.612 2.141-1.464 3.097-2.499.217-.235.36-.398.498-.578.12-.128.216-.334.248-.554 0 .01 0 .01-.008.04l.013-.079-.001.011.003-.031.001-.017v.005l.001-.02v.008l.002-.03.001-.05-.001-.044v-.004-.004zm-.954.045v.007l.001.004V8.33v.012l-.001.01v-.005-.005l.002-.015-.001.008c-.002.014-.002.014 0 0l-.007.084c.003-.057-.004-.041-.014-.031-.143.182-.27.327-.468.543-.89.963-1.856 1.752-2.86 2.311-.724.404-1.419.677-2.095.81a5.63 5.63 0 0 1-2.374-.036c-1.273-.295-2.523-1.014-3.774-2.101-.604-.525-1.075-1.001-1.457-1.496-.054-.07-.054-.107 0-.177.117-.152.244-.298.442-.512.89-.963 1.856-1.752 2.86-2.311.724-.404 1.419-.678 2.095-.81a5.631 5.631 0 0 1 2.374.036c1.272.295 2.523 1.014 3.774 2.101.603.524 1.074 1 1.457 1.496.035.041.043.057.046.076 0 .01 0 .01.008.043l-.009-.047.003.02-.002-.013v-.008.016c0-.004 0-.004 0 0v-.004z"/>
</svg>
PK
!<�/chrome/devtools/skin/images/jump-definition.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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>PK
!<�R���-chrome/devtools/skin/images/pane-collapse.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b">
  <path fill-opacity=".3" d="M12,3h2v10h-2V3z M5,9.9V6.1L8,8L5,9.9z"/>
  <path d="M14,2H2C1.4,2,1,2.4,1,3v10c0,0.6,0.4,1,1,1h12c0.6,0,1-0.4,1-1V3C15,2.4,14.6,2,14,2z M2,13L2,13V3h0h9v10   H2L2,13z M14,13C14,13,14,13,14,13h-2V3h2c0,0,0,0,0,0V13z M8.5,7.2l-3-1.9C4.6,4.7,4,5,4,6.1v3.8c0,1.1,0.6,1.4,1.5,0.8l3-1.9   C9.5,8.3,9.5,7.8,8.5,7.2z M5,9.9V6.1L8,8L5,9.9z"/>
</svg>
PK
!<����+chrome/devtools/skin/images/pane-expand.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b">
  <path fill-opacity=".3" d="M4,13H2V3h2V13z M11,6.1v3.8L8,8L11,6.1z"/>
  <path d="M2,14h12c0.6,0,1-0.4,1-1V3c0-0.6-0.4-1-1-1H2C1.4,2,1,2.4,1,3v10C1,13.6,1.4,14,2,14z M14,3L14,3v10h0H5V3   H14L14,3z M2,3C2,3,2,3,2,3h2v10H2c0,0,0,0,0,0V3z M7.5,8.8l3,1.9c1,0.6,1.5,0.3,1.5-0.8V6.1c0-1.1-0.6-1.4-1.5-0.8l-3,1.9   C6.5,7.7,6.5,8.2,7.5,8.8z M11,6.1v3.8L8,8L11,6.1z"/>
</svg>
PK
!<QB�2%chrome/devtools/skin/images/pause.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b">
  <path d="M5 12.503l.052-9a.5.5 0 0 0-1-.006l-.052 9a.5.5 0 0 0 1 .006zM12 12.497l-.05-9A.488.488 0 0 0 11.474 3a.488.488 0 0 0-.473.503l.05 9a.488.488 0 0 0 .477.497.488.488 0 0 0 .473-.503z"/>
</svg>
PK
!< s@=chrome/devtools/skin/images/performance-details-call-tree.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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"
     width="16" height="16" viewBox="0 0 16 16"
     fill="context-fill #0b0b0b">
  <rect x="1" y="4" width="10" height="1"/>
  <rect x="5" y="7" width="10" height="1"/>
  <rect x="1" y="10" width="10" height="1"/>
  <rect x="5" y="13" width="10" height="1"/>
</svg>

PK
!<���3>>>chrome/devtools/skin/images/performance-details-flamegraph.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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"
     width="16" height="16" viewBox="0 0 16 16"
     fill="context-fill #0b0b0b">
  <rect x="0" y="4" width="16" height="1"/>
  <rect x="0" y="7" width="8" height="1"/>
  <rect x="10" y="7" width="6" height="1"/>
  <rect x="2" y="10" width="6" height="1"/>
  <rect x="5" y="13" width="3" height="1"/>
</svg>

PK
!<B4�1��=chrome/devtools/skin/images/performance-details-waterfall.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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"
     width="16" height="16" viewBox="0 0 16 16"
     fill="context-fill #0b0b0b">
  <rect x="0" y="4" width="9" height="1"/>
  <rect x="5" y="8" width="8" height="1"/>
  <rect x="7" y="12" width="9" height="1"/>
</svg>

PK
!<�R����$chrome/devtools/skin/images/play.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b">
  <path d="M4 12.5l8-5-8-5v10zm-1 0v-10a1 1 0 0 1 1.53-.848l8 5a1 1 0 0 1 0 1.696l-8 5A1 1 0 0 1 3 12.5z" fill-rule="evenodd"/>
</svg>
PK
!<ՙ�G��%chrome/devtools/skin/images/power.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b">
  <path d="M8 14.5a5.5 5.5 0 1 0 0-11 5.5 5.5 0 0 0 0 11zm0-1a4.5 4.5 0 1 1 0-9 4.5 4.5 0 0 1 0 9z"/>
  <path d="M8.5 7.5v-6a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0z"/>
</svg>
PK
!<�Un�2chrome/devtools/skin/images/profiler-stopwatch.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b">
  <g fill-rule="evenodd">
    <path d="M15 9.004C14.51 12.394 11.578 15 8.035 15 4.15 15 1 11.866 1 8s3.15-7 7.036-7c1.941 0 3.7.783 4.972 2.048l-.709.709A6.027 6.027 0 0 0 8.036 2c-3.33 0-6.03 2.686-6.03 6s2.7 6 6.03 6a6.023 6.023 0 0 0 5.946-4.993l1.017-.003z"/>
    <path d="M4.137 9H3.1a5.002 5.002 0 0 0 9.8 0h-.965a4.023 4.023 0 0 1-3.9 3 4.023 4.023 0 0 1-3.898-3z" fill-opacity=".5"/>
    <path d="M8.036 11a2.994 2.994 0 0 0 2.987-3c0-1.657-1.338-3-2.987-3a2.994 2.994 0 0 0-2.988 3c0 1.657 1.338 3 2.988 3zm0-1c1.11 0 2.011-.895 2.011-2s-.9-2-2.011-2c-1.111 0-2.012.895-2.012 2s.9 2 2.012 2z"/>
    <path d="M10.354 6.354l4-4a.5.5 0 0 0-.708-.708l-4 4a.5.5 0 1 0 .708.708z"/>
  </g>
</svg>
PK
!<l��^,chrome/devtools/skin/images/pseudo-class.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16" fill="context-fill #0b0b0b">
  <path d="M11 7V5.5c0-.3-.2-.5-.5-.5h-5c-.3 0-.5.2-.5.5v5c0 .3.2.5.5.5h1.9V7.5c0-.3.2-.5.5-.5H11zM3 7H.8c-.1 0-.6 0-.7-.7-.1-.2-.1-.4-.1-.6v-5C0 .5 0 .3.2.1.4 0 .6 0 .7 0h5.2c.3 0 .6 0 .8.2.2.1.3.3.3.5V3H3v4zM1 6h1V2.7c0-.2.1-.4.2-.5.3-.2.6-.2.8-.2h3V1H1v5z"/>
  <path d="M9 9h1v1H9V9zm5 1h-1V9h1v1zm-2 0h-1V9h1v1zm3-1h1v1h-1V9zm1 5h-1v-1h1v1zm0-2h-1v-1h1v1zm-1 3h1v1h-1v-1zm-1 1h-1v-1h1v1zm-2 0h-1v-1h1v1zm-3-1h1v1H9v-1zm1-1H9v-1h1v1zm0-2H9v-1h1v1z"/>
</svg>
PK
!<2l��II&chrome/devtools/skin/images/reload.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill">
    <path d="M13.917 7C13.44 4.162 10.973 2 8 2 4.686 2 2 4.686 2 8s2.686 6 6 6c2.22 0 4.16-1.207 5.197-3H12c-.912 1.214-2.364 2-4 2-2.76 0-5-2.24-5-5s2.24-5 5-5c2.42 0 4.437 1.718 4.9 4h1.017z"/>
    <path d="M14 1L8 7h6V1zm-1 1L9 6h4V2z" fill-rule="evenodd"/>
</svg>
PK
!<�^�,99&chrome/devtools/skin/images/rewind.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b">
  <path d="M13 2.5l-8 5 8 5v-10zm1 0v10a1 1 0 0 1-1.53.848l-8-5a1 1 0 0 1 0-1.696l8-5A1 1 0 0 1 14 2.5zM2 12.497l-.04-7.342-.01-1.658A.488.488 0 0 0 1.474 3 .488.488 0 0 0 1 3.503l.05 9a.488.488 0 0 0 .477.497.488.488 0 0 0 .473-.503z"/>
</svg>
PK
!<�>� (chrome/devtools/skin/images/sad-face.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16" fill="#D92215">
  <path d="M8 14.5c-3.6 0-6.5-2.9-6.5-6.5S4.4 1.5 8 1.5s6.5 2.9 6.5 6.5-2.9 6.5-6.5 6.5zm0-12C5 2.5 2.5 5 2.5 8S5 13.5 8 13.5 13.5 11 13.5 8 11 2.5 8 2.5z"/>
  <circle cx="5" cy="6" r="1" transform="translate(1 1)"/>
  <circle cx="9" cy="6" r="1" transform="translate(1 1)"/>
  <path d="M5.5 11c-.1 0-.2 0-.3-.1-.2-.1-.3-.4-.1-.7C6 9 7 8.5 8.1 8.5c1.7.1 2.8 1.7 2.8 1.8.2.2.1.5-.1.7-.2.1-.6 0-.7-.2 0 0-.9-1.3-2-1.3-.7 0-1.4.4-2.1 1.3-.2.2-.4.2-.5.2z"/>
</svg>
PK
!<�Hվ��1chrome/devtools/skin/images/search-clear-dark.svg<?xml version="1.0" encoding="utf-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 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="16" viewBox="0 0 32 16">
  <defs>
    <path id="glyphShape-clear" d="M8,0C3.6,0,0,3.6,0,8c0,4.4,3.6,8,8,8s8-3.6,8-8C16,3.6,12.4,0,8,0 z M11.9,10.5l-1.4,1.4L8,9.4l-2.4,2.4l-1.4-1.4L6.6,8L4.2,5.6l1.4-1.4L8,6.6l2.4-2.4l1.4,1.4L9.4,8L11.9,10.5z"/>
    <style>
      .icon-state-default { fill: #f5f7fa; fill-opacity: .6; }
      .icon-state-pressed { fill: #7d7e80; fill-opacity: .8; }
    </style>
  </defs>
  <use xlink:href="#glyphShape-clear" class="icon-state-default"/>
  <use xlink:href="#glyphShape-clear" class="icon-state-pressed" transform="translate(16)"/>
</svg>
PK
!<$�͙��3chrome/devtools/skin/images/search-clear-failed.svg<?xml version="1.0" encoding="utf-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 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="16" viewBox="0 0 32 16">
  <defs>
    <path id="glyphShape-clear" d="M8,0C3.6,0,0,3.6,0,8c0,4.4,3.6,8,8,8s8-3.6,8-8C16,3.6,12.4,0,8,0 z M11.9,10.5l-1.4,1.4L8,9.4l-2.4,2.4l-1.4-1.4L6.6,8L4.2,5.6l1.4-1.4L8,6.6l2.4-2.4l1.4,1.4L9.4,8L11.9,10.5z"/>
    <style>
      .icon-state-default { fill: #cc3d3d; fill-opacity: 1; }
      .icon-state-pressed { fill: #802d2d; fill-opacity: 1; }
    </style>
  </defs>
  <use xlink:href="#glyphShape-clear" class="icon-state-default"/>
  <use xlink:href="#glyphShape-clear" class="icon-state-pressed" transform="translate(16)"/>
</svg>
PK
!<�C���2chrome/devtools/skin/images/search-clear-light.svg<?xml version="1.0" encoding="utf-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 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="16" viewBox="0 0 32 16">
  <defs>
    <path id="glyphShape-clear" d="M8,0C3.6,0,0,3.6,0,8c0,4.4,3.6,8,8,8s8-3.6,8-8C16,3.6,12.4,0,8,0 z M11.9,10.5l-1.4,1.4L8,9.4l-2.4,2.4l-1.4-1.4L6.6,8L4.2,5.6l1.4-1.4L8,6.6l2.4-2.4l1.4,1.4L9.4,8L11.9,10.5z"/>
    <style>
      .icon-state-default { fill: #1d2126; fill-opacity: .5; }
      .icon-state-pressed { fill: #1d2126; fill-opacity: .8; }
    </style>
  </defs>
  <use xlink:href="#glyphShape-clear" class="icon-state-default"/>
  <use xlink:href="#glyphShape-clear" class="icon-state-pressed" transform="translate(16)"/>
</svg>
PK
!<e�.pp&chrome/devtools/skin/images/search.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="#aaa">
  <path d="M10.716 10.032C11.516 9.077 12 7.845 12 6.5 12 3.462 9.538 1 6.5 1S1 3.462 1 6.5 3.462 12 6.5 12c1.345 0 2.577-.483 3.532-1.284l4.143 4.142c.19.19.495.19.683 0 .19-.188.19-.494 0-.683l-4.142-4.143zM6.5 11C8.985 11 11 8.985 11 6.5S8.985 2 6.5 2 2 4.015 2 6.5 4.015 11 6.5 11z" fill-rule="evenodd"/>
</svg>
PK
!<f%%5chrome/devtools/skin/images/security-state-broken.svg<?xml version="1.0" encoding="utf-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 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
     width="16" height="16" viewBox="0 0 16 16">
  <path fill="#808080" d="M14.8,12.5L9.3,1.9C9,1.3,8.5,1,8,1C7.5,1,7,1.3,6.7,1.9L1.2,12.5c-0.3,0.6-0.3,1.2,0,1.7C1.5,14.7,2,15,2.6,15h10.8 c0.6,0,1.1-0.3,1.4-0.8C15.1,13.7,15.1,13.1,14.8,12.5z"/>
  <path fill="#fff" d="M8,11c-0.8,0-1.5,0.7-1.5,1.5C6.5,13.3,7.2,14,8,14 c0.8,0,1.5-0.7,1.5-1.5C9.5,11.7,8.8,11,8,11z M8,10L8,10C8.6,10,9,9.6,9,9l0.2-4.2c0-0.7-0.5-1.2-1.2-1.2S6.8,4.1,6.8,4.8L7,9 C7,9.6,7.4,10,8,10z"/>
</svg>
PK
!<�L!��7chrome/devtools/skin/images/security-state-insecure.svg<?xml version="1.0" encoding="utf-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 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
     width="16" height="16" viewBox="0 0 16 16">
  <style>
    .icon-default {
      fill: #999;
    }
  </style>

  <defs>
    <rect id="shape-lock-clasp-outer" x="4" y="2" width="8" height="10" rx="4" ry="4" />
    <rect id="shape-lock-clasp-inner" x="6" y="4" width="4" height="6" rx="2" ry="2" />
    <rect id="shape-lock-base" x="3" y="7" width="10" height="7" rx="1" ry="1" />

    <mask id="mask-clasp-cutout">
      <rect width="16" height="16" fill="#000" />
      <use xlink:href="#shape-lock-clasp-outer" fill="#fff" />
      <use xlink:href="#shape-lock-clasp-inner" fill="#000" />
      <line x1="2" y1="13" x2="14" y2="1.5" stroke="#000" stroke-width="2" />
      <line x1="2" y1="15" x2="14" y2="3.5" stroke="#000" stroke-width="2" />
      <rect x="3" y="7" width="10" height="7" rx="1" ry="1" fill="#000" />
    </mask>

    <mask id="mask-base-cutout">
      <rect width="16" height="16" fill="#000" />
      <use xlink:href="#shape-lock-base" fill="#fff" />
      <line x1="2" y1="14.8" x2="14" y2="3.2" stroke="#000" stroke-width="1.8" />
    </mask>
  </defs>

  <use xlink:href="#shape-lock-clasp-outer" mask="url(#mask-clasp-cutout)" class="icon-default" />
  <use xlink:href="#shape-lock-base" mask="url(#mask-base-cutout)" class="icon-default" />

  <line x1="2" y1="14.1" x2="14" y2="2.5" stroke="#d92d21" stroke-width="1.8" />
</svg>
PK
!<�aa5chrome/devtools/skin/images/security-state-secure.svg<?xml version="1.0" encoding="utf-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 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
     width="16" height="16" viewBox="0 0 16 16">
  <style>
    .icon-default {
      fill: #4d9a26;
    }
  </style>

  <defs>
    <rect id="shape-lock-clasp-outer" x="4" y="2" width="8" height="10" rx="4" ry="4" />
    <rect id="shape-lock-clasp-inner" x="6" y="4" width="4" height="6" rx="2" ry="2" />
    <rect id="shape-lock-base" x="3" y="7" width="10" height="7" rx="1" ry="1" />

    <mask id="mask-clasp-cutout">
      <rect width="16" height="16" fill="#000" />
      <use xlink:href="#shape-lock-clasp-outer" fill="#fff" />
      <use xlink:href="#shape-lock-clasp-inner" fill="#000" />
    </mask>
  </defs>

  <use xlink:href="#shape-lock-clasp-outer" mask="url(#mask-clasp-cutout)" class="icon-default" />
  <use xlink:href="#shape-lock-base" class="icon-default" />
</svg>
PK
!<S���xx3chrome/devtools/skin/images/security-state-weak.svg<?xml version="1.0" encoding="utf-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 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
     width="16" height="16" viewBox="0 0 16 16">
  <style>
    .icon-default {
      fill: #808080;
    }
  </style>

  <defs>
    <rect id="shape-lock-clasp-outer" x="2" y="1" width="8" height="10" rx="4" ry="4" />
    <rect id="shape-lock-clasp-inner" x="4" y="3" width="4" height="6" rx="2" ry="2" />
    <rect id="shape-lock-base" x="1" y="6" width="10" height="7" rx="1" ry="1" />

    <mask id="mask-clasp-cutout">
      <rect width="16" height="16" fill="#000" />
      <use xlink:href="#shape-lock-clasp-outer" fill="#fff" />
      <use xlink:href="#shape-lock-clasp-inner" fill="#000" />
    </mask>
  </defs>

  <use xlink:href="#shape-lock-clasp-outer" mask="url(#mask-clasp-cutout)" class="icon-default" />
  <use xlink:href="#shape-lock-base" class="icon-default" />
  <path fill="#fff" d="M10.5,5C9.8,5,9.1,5.4,8.8,6.2l-3.5,6.8c-0.4,0.7-0.4,1.4,0,2c0.4,0.6,1,1,1.8,1H14c0.8,0,1.4-0.4,1.8-1 c0.3-0.6,0.3-1.4,0-2l-3.5-6.8C11.9,5.4,11.2,5,10.5,5L10.5,5z"/>
  <path fill="#ffbf00" d="M14.8,13.4l-3.5-6.8C11.2,6.2,10.9,6,10.5,6c-0.3,0-0.7,0.2-0.9,0.6l-3.5,6.8c-0.2,0.4-0.2,0.8,0,1.1C6.3,14.8,6.6,15,7,15 H14c0.4,0,0.7-0.2,0.9-0.5C15.1,14.2,15,13.8,14.8,13.4z"/>
  <path fill="#fff" d="M10,8.5C10,8.2,10.2,8,10.5,8S11,8.2,11,8.5L10.8,11h-0.6L10,8.5z" />
  <circle fill="#fff" cx="10.5" cy="12.5" r=".75" />
</svg>
PK
!<*�_�rr4chrome/devtools/skin/images/sort-ascending-arrow.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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"
     width="7" height="4" viewBox="0 0 7 4">
  <path d="M0,4 L3.5,0 L7,4" fill="#edf0f1" fill-opacity="0.8"/>
</svg>

PK
!<ɉrr5chrome/devtools/skin/images/sort-descending-arrow.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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"
     width="7" height="4" viewBox="0 0 7 4">
  <path d="M0,0 L3.5,4 L7,0" fill="#edf0f1" fill-opacity="0.8"/>
</svg>

PK
!<d�dd,chrome/devtools/skin/images/toggle-tools.png�PNG


IHDR@��y)+IDATx�5�#G�ef;533ے�
;��vd�cf�dG{����(X��f�U�է�MwޫW���_�?�)�!D?�{Nk}O$��,���
�7w�3�x�����_�5^x��%'�x!���鄊�XL��]xB䇄�%�DZ(�S]��no!�#�$������Hƣ�ԑ���X�x�1�,��3�a��
p�_��o�	�e��_,���[ L�|ͮNi�`�|�K�������-L�G��0�Í<v��g��e.'y�d���N��	�k�i���5F{�.�h��5�^��Zc��-x�<c�#>�s��myLB���ÐV��B�}�|�I�߳ w������c�,C��6>�H�2�{���<&��,q��G�S�$������@�G�3�D�<p&�TN�T~F��4g8T�H���K���zs���=��$������P҅y��ݔ�q(q�br���EoU���6���B̷��-�������v-PG#��gB�[P��z�o��q@��C���6���g������c��|�|w�!s��S!���R�	w���P}�O��	��s@z��-$7wG�P� ��^��;A����O�]�BZZZZ�`Vy����v����o���t�_�{����7U�y���>ʯE�"�.=ua6>������l|-4[�_z:�l|1/U�_z:Cm|����A�'y�3Y��A��f�ۀ.���f=<��p�7"�%aj��WW�^q�5�l�3������� _�$oА���n�*G���2tH18��W�IEND�B`�PK
!<S}�
/chrome/devtools/skin/images/[email protected]�PNG


IHDR� �"p%�IDATx��+���7�m�\=۶m۶m�e�g۶�x��;��y�].f'g�w���9�If��w�U�TuUj�Fk�F��zq���9W[H��X0[#��1���c���X1w��^^G��}���e)h��Z��L�;:��};2�#�3�VM�����)�����^.�L�'���qe�¼C�h\�ے�5�]�����dA�=�H������;zG�X&�@����9w�`��Ua�aB���
L%4x�A:����u]�dR�E�����C��97+$��Z�8/�8<�3St:2�d� �?��(�R�%���R?_)�hR��j0�#j�E�/3�Lb��}�]$�t.1���q1��_FB�	sy���H�����'�O+��X��}�;?W�Å��\zg��*��|7\��N�o�կ0�,A?\��N����o��%���i��>@�{��gR?΁yB�ح~�!�_����ӣ�[�����?�|�J�qIg�tq��/�����t�..�һ �����/�Z�:����>A�7��9 <X�KL����*�������B�3�<�O�g���'������Sz��d���l�J?%�wu��R�3x��2��7W�-�Py��:�[
rf���|����?
s�
���i�{վ���m�d
��:@w~�~{&�F��}���і`.sfcǎ�.>L��X:`w��tOOό��az�W��s��^�R�(���k��bxo�ti�(���~���7s-Wjs.�~�S�z)�W�>�@xOHivg�;��Y�NaV�s��mn���Aq����V�t��2�
��v}�������.Z�z��:@�1&�}�'s��M2�	iޱ=�s\G��i���~{��#@ƴ�J7_[<�:��e��"e1���9�����O������?�F[<A�"&|�	G5
�P�b��f��<���,������o��l�M����g�\�g4>W^�h|��M��ψ���
f���(B����-.|�
᯿����E0WL����?1�q�3W|�������{g����>�$�WL�c��N���t�Z�΀�X���+�(Ȉ�q%�9\(}�P�R��t�of�1"����f����,��K���7!�sX0��~[6?7A��h�>:!<�2�{)Kx��&�{���h}[��������<�����*�}[-�U�6:-�-�-�-�-�-��s�;�N��©�8)Q2~=>ڼ�^:	}���7W�3��(9"�_���࣪ו)��4���7>�̈́��~3�'����̈́�y0g3����L�tB7�5|���i8)cLm�J�P�E�c��П�B�J��o?Fy�1����J�#�7��Ip#Ȝ>�G��>�hh�'�:�́S>���|���@Pǥ>}j��\�o�.I��(�f�C��V����q��w������ï���;�o#�㵀� �� �o�/���1	��
��'�y:��g@#��$�O��^�h��Z ��맄��%�O`@����f?��/��Vj�t�k	�ˍ���0�D�]�]�Z�k^��Q�1��D^�Ȗ�E����� ��^}��R�*�y�K}
C�k�>���^��|(�-�F�O��'?�~�a�Q��ѧG��O���Xr�,}OP�5Z�5Z�OH^ϬS�6TIEND�B`�PK
!<,����+chrome/devtools/skin/images/tool-canvas.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b">
  <g fill-rule="evenodd">
    <path d="M1 2.007C1 1.45 1.45 1 2.007 1h11.986C14.55 1 15 1.45 15 2.007v11.986C15 14.55 14.55 15 13.993 15H2.007C1.45 15 1 14.55 1 13.993V2.007zM2 2h12v12H2V2z"/>
    <path d="M3 3h2v2H3zM11 3h2v2h-2zM7 3h2v2H7zM3 7h2v2H3zM11 7h2v2h-2zM7 7h2v2H7zM5 5h2v2H5zM9 5h2v2H9zM3 11h2v2H3zM11 11h2v2h-2zM7 11h2v2H7zM5 9h2v2H5zM9 9h2v2H9z" opacity="0.5"/>
  </g>
</svg>
PK
!<��o�-chrome/devtools/skin/images/tool-debugger.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b">
  <path d="M2 5v6c0 .109.039.342.144.553.15.297.374.447.856.447h9l-.78.375 4-5v1.25l-4-5L12 4H3c-.482 0-.707.15-.856.447A1.403 1.403 0 0 0 2 5zM1 5s0-2 2-2h9l4 5-4 5H3c-2 0-2-2-2-2V5z"/>
</svg>
PK
!<��߼BB(chrome/devtools/skin/images/tool-dom.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b">
  <path d="M6.052 1.13L1.164 5.57a.5.5 0 0 0 0 .74l5 4.56a.5.5 0 0 0 .673-.74l-5-4.559v.74l4.887-4.44a.5.5 0 0 0-.672-.741zM10.948 14.87l4.888-4.44a.5.5 0 0 0 0-.74l-5-4.56a.5.5 0 1 0-.673.74l5 4.559v-.74l-4.887 4.44a.5.5 0 0 0 .672.741z"/>
</svg>
PK
!<�ۜw.chrome/devtools/skin/images/tool-inspector.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b">
  <path d="M3 3.995v8.01c0-.01.005-.005.002-.005h9.996c-.001 0 .002-.003.002.005v-8.01c0 .01-.005.005-.002.005H3.002C3.003 4 3 4.003 3 3.995zm-1 0C2 3.445 2.456 3 3.002 3h9.996C13.55 3 14 3.456 14 3.995v8.01c0 .55-.456.995-1.002.995H3.002A1.005 1.005 0 0 1 2 12.005v-8.01z"/>
  <path d="M8.5 3.5V2a.5.5 0 0 0-1 0v1.5a.5.5 0 0 0 1 0zM1 8.5h1a.5.5 0 0 0 0-1H1a.5.5 0 0 0 0 1zM14 8.5h1a.5.5 0 1 0 0-1h-1a.5.5 0 1 0 0 1zM8.5 14v-1.5a.5.5 0 1 0-1 0V14a.5.5 0 1 0 1 0z"/>
</svg>
PK
!<
5���+chrome/devtools/skin/images/tool-memory.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b">
  <path d="M4.727 8.055l-1.96-1a.5.5 0 0 0-.573.083L.655 8.602a.5.5 0 1 0 .69.725l1.539-1.465-.572.083 1.96 1a.5.5 0 1 0 .455-.89z"/>
  <path d="M4.727 10.055l-1.96-1a.5.5 0 0 0-.573.083L.655 10.602a.5.5 0 1 0 .69.725l1.539-1.465-.572.083 1.96 1a.5.5 0 1 0 .455-.89zM11.727 10.945l1.961-1-.572-.083 1.54 1.465a.5.5 0 1 0 .689-.725l-1.54-1.464a.5.5 0 0 0-.571-.083l-1.961 1a.5.5 0 1 0 .454.89z"/>
  <path d="M11.727 8.945l1.961-1-.572-.083 1.54 1.465a.5.5 0 1 0 .689-.725l-1.54-1.464a.5.5 0 0 0-.571-.083l-1.961 1a.5.5 0 1 0 .454.89z"/>
  <path d="M11.727 6.945l1.961-1-.572-.083 1.54 1.465a.5.5 0 1 0 .689-.725l-1.54-1.464a.5.5 0 0 0-.571-.083l-1.961 1a.5.5 0 1 0 .454.89zM4.727 6.055l-1.96-1a.5.5 0 0 0-.573.083L.655 6.602a.5.5 0 1 0 .69.725l1.539-1.465-.572.083 1.96 1a.5.5 0 1 0 .455-.89z"/>
  <path d="M5 3.002v9.996c0-.001.003.002-.003.002h6.006c-.006 0-.003-.003-.003-.002V3.002c0 .001-.003-.002.003-.002H4.997c.006 0 .003.003.003.002zm-1 0C4 2.45 4.453 2 4.997 2h6.006c.55 0 .997.456.997 1.002v9.996c0 .553-.453 1.002-.997 1.002H4.997C4.447 14 4 13.544 4 12.998V3.002z"/>
</svg>
PK
!<��1�<<,chrome/devtools/skin/images/tool-network.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b">
  <rect fill-opacity=".5" x="2" y="3" width="8" height="1" rx=".5"/>
  <rect x="6" y="6" width="8" height="1" rx=".5"/>
  <rect fill-opacity=".5" x="4" y="9" width="8" height="1" rx=".5"/>
  <rect x="2" y="12" width="5" height="1" rx=".5"/>
</svg>
PK
!<̦'',chrome/devtools/skin/images/tool-options.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b">
  <path d="M8.513 3.416v-.918A.502.502 0 0 0 8.012 2h-.999c-.273 0-.5.226-.5.498V4.07l-.6.262c-.274.12-.534.27-.775.449l-.527.39-.567-.328-.796-.46a.502.502 0 0 0-.682.185l-.5.864a.504.504 0 0 0 .182.683l.795.46.567.326-.073.65a4.055 4.055 0 0 0 0 .898l.073.65-.567.327-.795.459a.502.502 0 0 0-.181.683l.499.864c.137.237.446.32.682.185l.796-.46.567-.327.527.39c.24.177.5.328.775.448l.6.262v1.572c0 .272.225.498.5.498h.999c.273 0 .5-.226.5-.498V11.93l.6-.262c.274-.12.534-.27.775-.449l.527-.39.567.328.796.46a.502.502 0 0 0 .682-.185l.5-.864a.504.504 0 0 0-.182-.683l-.795-.46-.567-.326.073-.65a4.055 4.055 0 0 0 0-.898l-.073-.65.567-.327.795-.459a.502.502 0 0 0 .181-.683l-.499-.864a.504.504 0 0 0-.682-.185l-.796.46-.567.327-.527-.39c-.24-.177-.5-.328-.775-.448l-.6-.262v-.654zm1 0c.345.15.67.34.968.56l.796-.459a1.504 1.504 0 0 1 2.048.55l.5.865a1.502 1.502 0 0 1-.548 2.05l-.795.459a5.055 5.055 0 0 1 0 1.118l.795.46c.717.414.958 1.337.547 2.049l-.499.864a1.502 1.502 0 0 1-2.048.55l-.796-.458c-.299.22-.623.41-.968.56v.918c0 .827-.679 1.498-1.501 1.498h-.999c-.829 0-1.5-.675-1.5-1.498v-.918c-.345-.15-.67-.34-.97-.56l-.795.459a1.504 1.504 0 0 1-2.048-.55l-.5-.865a1.502 1.502 0 0 1 .548-2.05l.795-.459a5.055 5.055 0 0 1 0-1.118l-.795-.46A1.504 1.504 0 0 1 1.2 4.932l.499-.864a1.502 1.502 0 0 1 2.048-.55l.796.458c.299-.22.624-.41.969-.56v-.918c0-.827.678-1.498 1.5-1.498h.999c.829 0 1.5.675 1.5 1.498v.918z"/>
    <path d="M7.5 9a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm0 1a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/>
</svg>
PK
!<#St���-chrome/devtools/skin/images/tool-profiler.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b" fill-rule="evenodd">
  <path d="M15 9.004C14.51 12.394 11.578 15 8.035 15 4.15 15 1 11.866 1 8s3.15-7 7.036-7c1.941 0 3.7.783 4.972 2.048l-.709.709A6.027 6.027 0 0 0 8.036 2c-3.33 0-6.03 2.686-6.03 6s2.7 6 6.03 6a6.023 6.023 0 0 0 5.946-4.993l1.017-.003z"/>
  <path d="M4.137 9H3.1a5.002 5.002 0 0 0 9.8 0h-.965a4.023 4.023 0 0 1-3.9 3 4.023 4.023 0 0 1-3.898-3z" fill-opacity=".5"/>
  <path d="M8.036 11a2.994 2.994 0 0 0 2.987-3c0-1.657-1.338-3-2.987-3a2.994 2.994 0 0 0-2.988 3c0 1.657 1.338 3 2.988 3zm0-1c1.11 0 2.011-.895 2.011-2s-.9-2-2.011-2c-1.111 0-2.012.895-2.012 2s.9 2 2.012 2z"/>
  <path d="M10.354 6.354l4-4a.5.5 0 0 0-.708-.708l-4 4a.5.5 0 1 0 .708.708z"/>
</svg>
PK
!<�w�h``/chrome/devtools/skin/images/tool-scratchpad.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b">
  <path d="M5 1.5a.5.5 0 0 0-1 0v2a.5.5 0 0 0 1 0v-2zM8.5 3.5v-2a.5.5 0 0 0-1 0v2a.5.5 0 0 0 1 0zM12 3.5v-2a.5.5 0 1 0-1 0v2a.5.5 0 1 0 1 0zM5 7h4a.5.5 0 0 0 0-1H5a.5.5 0 0 0 0 1zM5 11h2a.5.5 0 1 0 0-1H5a.5.5 0 1 0 0 1zM6 9h5a.5.5 0 1 0 0-1H6a.5.5 0 0 0 0 1z"/>
  <path d="M3 3.996v9.008c0-.003 0-.004.002-.004h9.996c-.001 0 .002-.003.002.004V3.996c0 .003 0 .004-.002.004H3.002C3.003 4 3 4.003 3 3.996zm-1 0C2 3.446 2.456 3 3.002 3h9.996A.998.998 0 0 1 14 3.996v9.008c0 .55-.456.996-1.002.996H3.002A.998.998 0 0 1 2 13.004V3.996z"/>
</svg>
PK
!<��<���1chrome/devtools/skin/images/tool-shadereditor.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b">
  <path d="M2 4v8h12V4H2zm0-1h12a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1z"/>
  <circle cx="1" cy="3" r="1"/>
  <circle cx="1" cy="13" r="1"/>
  <circle cx="15" cy="13" r="1"/>
  <circle cx="15" cy="3" r="1"/>
  <path d="M1.215 3.911l13 9 .411.285.57-.822-.411-.285-13-9-.411-.285-.57.822z"/>
  <path fill-opacity=".3" d="M8 5h2v2H8zM8 8h2v2L9 8.711zM5 5.962V5h2v2h-.828l-.729-.368zM11 5h2v2h-2zM11 8h2v2h-2z"/>
</svg>
PK
!<XJ ��,chrome/devtools/skin/images/tool-storage.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b">
  <path d="M7.5 7.556c3.006 0 5.5-1.136 5.5-2.778C13 3.136 10.506 2 7.5 2S2 3.136 2 4.778C2 6.42 4.494 7.556 7.5 7.556zm0-1c-2.517 0-4.5-.903-4.5-1.778S4.983 3 7.5 3s4.5.903 4.5 1.778-1.983 1.778-4.5 1.778zM7.5 14.445c3.006 0 5.5-1.137 5.5-2.778 0-.878-.595-1.606-1.657-2.081-.244-.11-.473-.107-.778-.033-.056.014-.565.158-.765.205-.626.148-1.342.231-2.3.231-.973 0-1.683-.082-2.273-.225a18.574 18.574 0 0 1-.673-.193c-.277-.076-.479-.089-.707-.005l-.035.014C2.638 10.064 2 10.756 2 11.667c0 1.641 2.494 2.778 5.5 2.778zm0-1c-2.517 0-4.5-.904-4.5-1.778 0-.432.354-.816 1.194-1.163h-.002c-.012.005.003.006.097.032-.056-.016.474.144.702.2.669.162 1.458.253 2.509.253 1.035 0 1.828-.092 2.53-.257.228-.054.74-.2.77-.207a.756.756 0 0 1 .134-.027c.734.329 1.066.735 1.066 1.169 0 .874-1.983 1.778-4.5 1.778z"/>
  <path d="M7.5 10.945c3.006 0 5.5-1.137 5.5-2.778 0-.873-.62-1.601-1.693-2.082-.244-.109-.472-.106-.773-.032-.051.013-.551.158-.75.206-.615.147-1.326.23-2.284.23-.973 0-1.68-.082-2.265-.225a17.077 17.077 0 0 1-.66-.19c-.27-.076-.467-.092-.692-.015l-.054.02C2.65 6.568 2 7.259 2 8.168c0 1.641 2.494 2.778 5.5 2.778zm0-1C4.983 9.945 3 9.04 3 8.167c0-.426.364-.813 1.21-1.163l-.003.001c-.011.004.005.005.099.032-.079-.022.465.143.69.198.665.163 1.452.254 2.504.254 1.036 0 1.825-.092 2.517-.258.228-.054.733-.2.758-.207a.766.766 0 0 1 .124-.026c.748.335 1.101.75 1.101 1.169 0 .874-1.983 1.778-4.5 1.778z"/>
</svg>
PK
!<���*��0chrome/devtools/skin/images/tool-styleeditor.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b">
  <path d="M5.5 2C3.565 2 2.806 3.12 3.065 4.587c.239 1.346.117 1.76-.435 2.39l-.054.06c-.252.288-.39.474-.523.74L1.94 8l.112.224c.132.265.27.45.523.739l.054.06c.552.63.674 1.044.435 2.39C2.802 12.904 3.527 14 5.5 14a.5.5 0 1 0 0-1c-1.291 0-1.614-.487-1.45-1.413.292-1.65.081-2.37-.669-3.223l-.053-.06c-.2-.229-.296-.357-.38-.528v.448c.084-.17.18-.299.38-.528l.053-.06c.75-.854.961-1.573.67-3.223C3.89 3.515 4.24 3 5.5 3a.5.5 0 1 0 0-1zM10.5 3c1.26 0 1.609.515 1.45 1.413-.292 1.65-.081 2.37.669 3.223l.053.06c.2.229.296.357.38.528v-.448c-.084.17-.18.299-.38.528l-.053.06c-.75.854-.961 1.573-.67 3.223.165.926-.158 1.413-1.449 1.413a.5.5 0 1 0 0 1c1.973 0 2.698-1.096 2.435-2.587-.239-1.346-.117-1.76.435-2.39l.054-.06c.252-.288.39-.474.523-.74L14.06 8l-.112-.224c-.132-.265-.27-.45-.523-.739l-.054-.06c-.552-.63-.674-1.044-.435-2.39C13.194 3.12 12.435 2 10.5 2a.5.5 0 0 0 0 1z"/>
</svg>
PK
!<�ԟ		-chrome/devtools/skin/images/tool-webaudio.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" width="16" height="16" viewBox="0 0 16 16" stroke="context-fill">
  <path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="M2.7 8.6c-.1-2.9.2-4.1 1.4-4.1 1.8 0 .6 6.6 2.5 6.6s1-6.4 3-6.4.7 6.4 2.7 6.4c1.4 0 1.5-3.5 1.5-3.5"/>
</svg>
PK
!<"A(��/chrome/devtools/skin/images/tool-webconsole.svg<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="context-fill #0b0b0b" fill-rule="evenodd">
  <path d="M14 4V3H2v1h12zm0 1v8H2V5h12zM1 3.002C1 2.45 1.45 2 2.007 2h11.986A1.01 1.01 0 0 1 15 3.002v9.996C15 13.55 14.55 14 13.993 14H2.007A1.01 1.01 0 0 1 1 12.998V3.002z"/>
  <path d="M4.09 7.859l2.062 2-.006-.713-2.061 2.062a.5.5 0 0 0 .707.707l2.062-2.061a.5.5 0 0 0-.006-.713l-2.061-2a.5.5 0 1 0-.697.718z"/>
</svg>
PK
!<:�
�""+chrome/devtools/skin/images/tracer-icon.png�PNG


IHDR�a�IDATx�Ց�A��D���^�(J	A��hut/��c]��ngf�$k�8V��������W��*� D� H��3P4�Z�_�>b�F&�)P[0
�֞�l���n��G*A-vv\
A��b 5���]u��i�
NȞ�P�ڜt�h]y��[7��a��2B�a<���d/B������K!�7-<@�$`ȁE{�b�r'\kf
���6$���
�3�����~ew�%oJ&�uWIEND�B`�PK
!<�����.chrome/devtools/skin/images/[email protected]�PNG


IHDR  szz��IDATxb�@l9��Da��m�۠��ڽ��mX۶m�+������4S�_}�����x�ݒ�:;�'���&�����3�Ƶ�	��fֺv�K�Y��
 Z�P$-	���
ʃDdH��V@~���%�N�W����h�OHo�z�(�*-�$�s.����O;қiһ���/f��~
$~G]����1$]ff�R$����̚�x˜��z4:P��bA�$}
��҇svn��ф���3����
k��~�*�̬%���.�$I@=�����ϦG�'�'�p��+~
�`=s��j����W��3����V3k�sDl�#yT_� �O0�E~H�7�^�;�DZ-�?/'�Y7��I4D�.A�G?~�:~��F�����3IEND�B`�PK
!<��o���,chrome/devtools/skin/images/vview-delete.png�PNG


IHDR��7�OIDAT(�c`���[º-z����/����~�	%`��/]Ղ�0�0���!
W����ja�&`A7��pP�S?#��PIEND�B`�PK
!<\ڌ��/chrome/devtools/skin/images/[email protected]�PNG


IHDR  �s�oIDATx�퓻
�0D3�ǀ��M�Β#W!T�S��+O~O�qu~��Al��e���F��G��ӥ���+L��كz�i/+^TD��G�/�~F�#�_�&v�;��r0�	�W�g�IEND�B`�PK
!<��=֠�*chrome/devtools/skin/images/vview-edit.png�PNG


IHDR��7�gIDAT(���!
�@@�A���x��X<��"hZ���0[���`�__�"%����hN��q�eu���ą�g�t�:Ä�e��2,B�3��"q�ә�a.&�*r��dTW���kIEND�B`�PK
!<o�P	..-chrome/devtools/skin/images/[email protected]�PNG


IHDR  �s��IDATx��αKBQ��8��Аk�BKHS�X����T�F{��6��6D���<_�4���@��qE��A��~�䭱�m>�G+rّ:���/*��kRIx��E�8���1�DŃ=���
N�>��}����
C�g�Tp�{<������[Ӭ7H=KUÛd�����&��o�J*���w��iƻQ���an��NT0�x��ОJ��-��y�Vx`��6�	� ޤi�
IEND�B`�PK
!<ž>���*chrome/devtools/skin/images/vview-lock.png�PNG


IHDR��7�xIDAT(S�ʱ
�PCїL�t���&� X�I�%��̀�&F(�+�؍%����$�j���<#�<1!.��ܱpS��+���G����
��_w١:Ȃxf��;�by7/o��ݑf�8�IEND�B`�PK
!<݃g=-chrome/devtools/skin/images/[email protected]�PNG


IHDR  �s��IDATH�c`��?�����������k��g$I�e�'��G��/������h�A�ua��x�X�-W���л�w�7Xd3Q\��h�j���}�XԞn�)�V�*z�l�,"����Z�j�
�N�����#d�����_��=����ހk�`���>�ˤ�B,	��k�y��5`���g�(��N�Q0uE��(�IEND�B`�PK
!<3��pbb4chrome/devtools/skin/images/vview-open-inspector.png�PNG


IHDR��7�)IDAT(�c` 
\�O����!9
p�+�4�5�YAoR�8�u�j�y�IEND�B`�PK
!<�Hstt7chrome/devtools/skin/images/[email protected]�PNG


IHDR  V%(tRNSv��8-IDATx�c`�2�~}�\���
0��&BP�`��`������Q��IEND�B`�PK
!<�$4�uu*chrome/devtools/skin/images/webconsole.svg<?xml version="1.0" encoding="UTF-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 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="72" height="60" viewBox="0 0 72 60">
  <defs>
    <rect id="glyphShape-colorSwatch" width="8" height="8" ry="2" rx="2"/>
    <rect id="glyphShape-colorSwatch-border" width="10" height="10" ry="2" rx="2"/>
    <polygon id="glyphShape-errorX" points="9.9,8.5 8.5,9.9 6,7.4 3.6,9.8 2.2,8.4 4.6,6 2.2,3.6 3.6,2.2 6,4.6 8.4,2.2 9.8,3.6 7.4,6"/>
    <path id="glyphShape-warningTriangle" d="M9.9,8.6l-3.1-6C6.6,2.2,6.3,2,6,2C5.7,2,5.4,2.2,5.2,2.5l-3.1,6C2,8.9,2,9.3,2.1,9.6C2.3,9.8,2.6,10,2.9,10 h6.1c0.4,0,0.6-0.2,0.8-0.4C10,9.3,10,8.9,9.9,8.6z"/>
    <path id="glyphShape-exclamationPoint" d="M6,7.7c-0.6,0-1,0.4-1,0.8C5,9,5.4,9.3,6,9.3c0.6,0,1-0.4,1-0.8 C7,8.1,6.6,7.7,6,7.7z M6,7c0.6,0,1-0.4,1-1V5c0-0.6-0.4-1-1-1S5,4.4,5,5v1C5,6.6,5.4,7,6,7z"/>
    <circle id="glyphShape-infoCircle" cx="6" cy="6" r="4"/>
    <path id="glyphShape-infoGlyph" d="M6,6C5.4,6,5,6.4,5,7v1c0,0.6,0.4,1,1,1s1-0.4,1-1V7C7,6.4,6.6,6,6,6z M6,5c0.6,0,1-0.4,1-1S6.6,3,6,3S5,3.4,5,4S5.4,5,6,5z"/>
    <style>
      .icon-colorSwatch-border {
        fill: #fff;
        fill-opacity: .7;
      }
      .icon-colorSwatch-network {
        fill: #000;
      }
      .icon-colorSwatch-css {
        fill: #00b6f0;
      }
      .icon-colorSwatch-js {
        fill: #fb9500;
      }
      .icon-colorSwatch-logging {
        fill: #808080;
      }
      .icon-colorSwatch-security {
        fill: #ec1e0d;
      }
      .icon-glyphOverlay {
        fill: #fff;
      }

      #icon-indicator-input {
        fill: #8fa1b2;
      }
      #icon-indicator-output {
        fill: #667380;
      }
      #light-icons:target #icon-indicator-input {
        fill: #45494d;
      }
      #light-icons:target #icon-indicator-output {
        fill: #8a9199;
      }
    </style>
  </defs>
  <g id="icon-colorSwatch-network">
    <use xlink:href="#glyphShape-colorSwatch-border" class="icon-colorSwatch-border" x="1" y="1"/>
    <use xlink:href="#glyphShape-colorSwatch" class="icon-colorSwatch-network" x="2" y="2"/>
  </g>
  <g id="icon-colorSwatch-css" transform="translate(0 12)">
    <use xlink:href="#glyphShape-colorSwatch-border" class="icon-colorSwatch-border" x="1" y="1"/>
    <use xlink:href="#glyphShape-colorSwatch" class="icon-colorSwatch-css" x="2" y="2"/>
  </g>
  <g id="icon-colorSwatch-js" transform="translate(0 24)">
    <use xlink:href="#glyphShape-colorSwatch-border" class="icon-colorSwatch-border" x="1" y="1"/>
    <use xlink:href="#glyphShape-colorSwatch" class="icon-colorSwatch-js" x="2" y="2"/>
  </g>
  <g id="icon-colorSwatch-logging" transform="translate(0 36)">
    <use xlink:href="#glyphShape-colorSwatch-border" class="icon-colorSwatch-border" x="1" y="1"/>
    <use xlink:href="#glyphShape-colorSwatch" class="icon-colorSwatch-logging" x="2" y="2"/>
  </g>
  <g id="icon-colorSwatch-security" transform="translate(0 48)">
    <use xlink:href="#glyphShape-colorSwatch-border" class="icon-colorSwatch-border" x="1" y="1"/>
    <use xlink:href="#glyphShape-colorSwatch" class="icon-colorSwatch-security" x="2" y="2"/>
  </g>
  <use xlink:href="#glyphShape-errorX" id="icon-errorX-network" class="icon-colorSwatch-network" transform="translate(12)"/>
  <use xlink:href="#glyphShape-errorX" id="icon-errorX-css" class="icon-colorSwatch-css" transform="translate(12 12)"/>
  <use xlink:href="#glyphShape-errorX" id="icon-errorX-js" class="icon-colorSwatch-js" transform="translate(12 24)"/>
  <use xlink:href="#glyphShape-errorX" id="icon-errorX-logging" class="icon-colorSwatch-logging" transform="translate(12 36)"/>
  <use xlink:href="#glyphShape-errorX" id="icon-errorX-security" class="icon-colorSwatch-security" transform="translate(12 48)"/>
  <g id="icon-warningTriangle-css" transform="translate(24 12)">
    <use xlink:href="#glyphShape-warningTriangle" class="icon-colorSwatch-css"/>
    <use xlink:href="#glyphShape-exclamationPoint" class="icon-glyphOverlay"/>
  </g>
  <g id="icon-warningTriangle-js" transform="translate(24 24)">
    <use xlink:href="#glyphShape-warningTriangle" class="icon-colorSwatch-js"/>
    <use xlink:href="#glyphShape-exclamationPoint" class="icon-glyphOverlay"/>
  </g>
  <g id="icon-warningTriangle-logging" transform="translate(24 36)">
    <use xlink:href="#glyphShape-warningTriangle" class="icon-colorSwatch-logging"/>
    <use xlink:href="#glyphShape-exclamationPoint" class="icon-glyphOverlay"/>
  </g>
  <g id="icon-warningTriangle-security" transform="translate(24 48)">
    <use xlink:href="#glyphShape-warningTriangle" class="icon-colorSwatch-security"/>
    <use xlink:href="#glyphShape-exclamationPoint" class="icon-glyphOverlay"/>
  </g>
  <g id="icon-infoCircle-logging" transform="translate(36 36)">
    <use xlink:href="#glyphShape-infoCircle" class="icon-colorSwatch-logging"/>
    <use xlink:href="#glyphShape-infoGlyph" class="icon-glyphOverlay"/>
  </g>
  <g id="light-icons">
    <path id="icon-indicator-input" d="M6.5,1.2L5.4,2.3L9,6L5.3,9.7l1.1,1.1L11,6L6.5,1.2z M1.5,1.2 L0.4,2.3L4,6L0.3,9.7l1.1,1.1L6,6L1.5,1.2z" transform="translate(48 36)"/>
    <polygon id="icon-indicator-output" points="10,5 4.3,5 6.8,2.4 5.5,1.2 1,6 5.5,10.8 6.9,9.6 4.3,7 10,7" transform="translate(60 36)"/>
  </g>
</svg>PK
!<s��"chrome/devtools/skin/inspector.css/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 {
  --eyedropper-image: url(images/command-eyedropper.svg);
}

.theme-firebug {
  --eyedropper-image: url(images/firebug/command-eyedropper.svg);
}

* {
  box-sizing: border-box;
}

/* Make sure to hide scroll bars for the parent window */
window {
  overflow: hidden;
}

/* The main Inspector panel container. */
.inspector-responsive-container {
  height: 100vh;
}

/* The main panel layout. This area consists of a toolbar, markup view
  and breadcrumbs bar. */
#inspector-main-content {
  /* Subtract 1 pixel from the panel height. It's puzzling why this
    is needed, but if not presented the entire Inspector panel
    content jumps 1 pixel up when the Toolbox is opened. */
  height: calc(100% - 1px);
  /* This min-width avoids a visual glitch when moving the splitter quickly to the left.
     See bug 1307408 comment #12. */
  min-width: 125px;
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
}

/* Inspector Panel Splitter */

#inspector-splitter-box {
  height: 100vh;
  width: 100vw;
  position: fixed;
}

/* Minimum dimensions for the Inspector splitter areas. */
#inspector-splitter-box .uncontrolled,
#inspector-splitter-box .controlled {
  min-height: 50px;
  min-width: 50px;
  overflow-x: hidden;
}

/* Set a minimum width of 200px for tab content to avoid breaking the layout when resizing
   the sidebar tab to small width. If a specific panel supports smaller width, this should
   be overridden on a panel-by-panel basis */
.inspector-tabpanel {
  min-width: 200px;
}

#inspector-splitter-box .controlled.pane-collapsed {
  visibility: collapse;
}

/* Use flex layout for the Inspector toolbar. For now, it's done
   specifically for the Inspector toolbar since general rule applied
   on .devtools-toolbar breaks breadcrumbs and also toolbars in other
   panels (e.g. webconsole, debugger), these are not ready for HTML
   layout yet. */
#inspector-toolbar.devtools-toolbar {
  display: flex;
}

#inspector-toolbar.devtools-toolbar .devtools-toolbar-spacer {
  flex-grow: 1;
  display: inline-block;
}

/* Add element toolbar button */
#inspector-element-add-button::before {
  background-image: url("chrome://devtools/skin/images/add.svg");
  list-style-image: url("chrome://devtools/skin/images/add.svg");
  -moz-user-focus: normal;
}

#inspector-searchlabel {
  overflow: hidden;
  margin-inline-end: 2px;
}

#inspector-search {
  flex: unset;
}

/* Eyedropper toolbar button */

#inspector-eyedropper-toggle {
  /* Required to display tooltip when eyedropper is disabled in non-HTML documents */
  pointer-events: auto;
}

#inspector-eyedropper-toggle::before {
  background-image: var(--eyedropper-image);
}

#inspector-breadcrumbs-toolbar {
  padding: 0px;
  border-bottom-width: 0px;
  border-top-width: 1px;
  border-top-color: var(--theme-splitter-color);
  /* Bug 1262668 - Use the same background as the body so the breadcrumbs toolbar doesn't
     get mistaken as a splitter */
  background-color: var(--theme-body-background);
  display: block;
  position: relative;
}

#inspector-breadcrumbs-toolbar,
#inspector-breadcrumbs-toolbar * {
  box-sizing: border-box;
}

#inspector-breadcrumbs {
  display: flex;

  /* Break out of the XUL flexbox, so the splitter can still shrink the
     markup view even if the contents of the breadcrumbs are wider than
     the new width. */
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
}

#inspector-breadcrumbs .scrollbutton-up,
#inspector-breadcrumbs .scrollbutton-down {
  flex: 0;
  display: flex;
  align-items: center;
}

#inspector-breadcrumbs .html-arrowscrollbox-inner {
  flex: 1;
  display: flex;
  overflow: hidden;
}

#inspector-breadcrumbs .breadcrumbs-widget-item {
  white-space: nowrap;
  flex-shrink: 0;
  font: message-box;
}

#inspector-rules-container,
#inspector-sidebar-container {
  overflow: hidden;
  position: relative;
  height: 100%;
}

#inspector-rules-sidebar,
#inspector-sidebar {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
}

/* Override `-moz-user-focus:ignore;` from toolkit/content/minimal-xul.css */
.inspector-tabpanel > * {
  -moz-user-focus: normal;
}

/* Markup Box */

iframe {
  border: 0;
}

#markup-box {
  width: 100%;
  flex: 1;
  min-height: 0;
}

#markup-box > iframe {
  height: 100%;
  width: 100%;
}

/* Animation inspector */
#sidebar-panel-animationinspector {
  height: 100%;
  width: 100%;
}
PK
!<�w%e
e
*chrome/devtools/skin/jit-optimizations.css/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
 * JIT View
 */

#jit-optimizations-view {
  width: 350px;
  min-width: 200px;
  white-space: nowrap;
  --jit-tree-row-height: 14;
  --jit-tree-header-height: 16;
}

/* Override layout styles applied by minimal-xul.css */
#jit-optimizations-view div {
  display: block;
}
#jit-optimizations-view span {
  display: inline-block;
}

#jit-optimizations-view > div {
  /* For elements that need to flex to fill the available space and/or
   * scroll on overflow, we need to use the old flexbox model, since the
   * parent nodes are in the XUL namespace. The new flexbox model can't
   * properly compute dimensions and will ignore `flex: ${number}` properties,
   * since no other parent node has a flex display. */
  display: -moz-box;
  -moz-box-flex: 1;
  -moz-box-orient: vertical;
}

#jit-optimizations-view .optimization-header,
#jit-optimizations-view .tree * {
  /* We can, however, display child nodes as flex to take advantage of
   * horizontal/vertical inlining. */
  display: flex;
}

#jit-optimizations-view .optimization-header {
  height: var(--jit-tree-header-height);
  padding: 2px 5px;
  background-color: var(--theme-tab-toolbar-background);
}

#jit-optimizations-view .header-title {
  font-weight: bold;
  padding-inline-end: 7px;
}

#jit-optimizations-view .tree {
  display: -moz-box;
  -moz-box-flex: 1;
  -moz-box-orient: vertical;
  overflow: auto;
  background-color: var(--theme-body-background);
}

#jit-optimizations-view .tree-node {
  height: var(--jit-tree-row-height);
}

#jit-optimizations-view .tree-node button {
  display: none;
}

#jit-optimizations-view .optimization-outcome.success {
  color: var(--theme-highlight-green);
}
#jit-optimizations-view .optimization-outcome.failure {
  color: var(--theme-highlight-red);
}

.theme-dark .opt-icon::before {
  background-image: url(chrome://devtools/skin/images/webconsole.svg);
}
.theme-light .opt-icon::before {
  background-image: url(chrome://devtools/skin/images/webconsole.svg#light-icons);
}

.opt-icon::before {
  display: inline-block;
  content: "";
  background-repeat: no-repeat;
  background-size: 72px 60px;
  /* show grey "i" bubble by default */
  background-position: -36px -36px;
  width: 10px;
  height: 10px;
  max-height: 12px;
}

.opt-icon::before {
  margin: 1px 6px 0 0;
}

.opt-icon.warning::before {
  background-position: -24px -24px;
}

/* Frame Component */
.frame-link {
  margin-inline-start: 7px;
}
PK
!<���Y	Y	chrome/devtools/skin/layout.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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-container {
  height: 100%;
  width: 100%;
  overflow: auto;
  min-width: 200px;
}

/**
 * Common styles for shared components
 */

.flexbox-container,
.grid-container {
  display: flex;
  flex-direction: column;
  flex: 1 auto;
  align-items: center;
  min-width: 140px;
}

.grid-container:first-child {
  margin-bottom: 10px;
}

.flexbox-container > span,
.grid-container > span {
  font-weight: 600;
  margin-bottom: 5px;
  pointer-events: none;
}

.flexbox-container > ul,
.grid-container > ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

.flexbox-container li,
.grid-container li {
  display: flex;
  align-items: center;
  padding: 4px 0;
}

.flexbox-container input
.grid-container input {
  margin: 0 5px;
}

.flexbox-container label,
.grid-container label {
  display: flex;
  align-items: center;
}

/**
 * Grid Container
 */

#layout-flexbox-container,
#layout-grid-container {
  display: flex;
  flex-direction: column;
  margin: 5px;
}

/**
 * Grid Content
 */

.flexbox-content,
.grid-content {
  display: flex;
  flex-wrap: wrap;
  flex: 1;
  margin: 5px 0;
}

/**
 * Grid Outline
 */

.grid-outline-container {
  margin: 5px 0;
}

.grid-outline-container svg {
  overflow: visible;
}

.grid-outline-border {
  fill: none;
  stroke: currentColor;
  stroke-width: 0.75;
  vector-effect: non-scaling-stroke;
}

.grid-outline-cell {
  pointer-events: all;
  stroke: currentColor;
  stroke-dasharray: 0.5, 2;
  vector-effect: non-scaling-stroke;
}

.grid-outline-cell:hover {
  opacity: 0.45;
  fill: currentColor;
}

.grid-outline-line {
  opacity: 0;
  stroke-width: 10;
}

.grid-outline-text {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--theme-graphs-full-red);
}

.grid-outline-text-icon {
  background: url("chrome://devtools/skin/images/sad-face.svg");
  margin-inline-end: 5px;
  width: 16px;
  height: 16px;
}

/**
 * Grid Item
 */

.grid-color-swatch {
  width: 12px;
  height: 12px;
  margin-left: 5px;
  border: 1px solid var(--theme-highlight-gray);
  border-radius: 50%;
  cursor: pointer;
}

.grid-color-value {
  display: none;
}

/**
 * Settings Item
 */

.grid-settings-item label {
  line-height: 16px;
}
PK
!<�Ŵe  $chrome/devtools/skin/light-theme.css/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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(resource://devtools/client/themes/variables.css);
@import url(resource://devtools/client/themes/common.css);
@import url(chrome://devtools/skin/toolbars.css);
@import url(chrome://devtools/skin/tooltips.css);

body {
  margin: 0;
}

.theme-body {
  background: var(--theme-body-background);
  color: var(--theme-body-color);
}

.theme-sidebar {
  background: var(--theme-sidebar-background);
  color: var(--theme-body-color);
}

::-moz-selection {
  background-color: var(--theme-selection-background);
  color: var(--theme-selection-color);
}

.theme-selected,
.CodeMirror-hint-active {
  background-color: var(--theme-selection-background);
  color: var(--theme-selection-color);
}

.theme-bg-contrast,
.variable-or-property:not([overridden])[changed] {
  background: var(--theme-contrast-background);
}

.theme-link,
.cm-s-mozilla .cm-link,
.CodeMirror-Tern-type {
  color: var(--theme-comment);
}

/*
 * FIXME: http://bugzil.la/575675 CSS links without :visited set cause assertion
 * failures in debug builds.
 */
.theme-link:visited,
.cm-s-mozilla .cm-link:visited {
  color: var(--theme-highlight-blue);
}

.theme-comment,
.cm-s-mozilla .cm-meta,
.cm-s-mozilla .cm-hr,
.cm-s-mozilla .cm-comment,
.variable-or-property .token-undefined,
.variable-or-property .token-null,
.CodeMirror-Tern-completion-unknown:before {
  color: var(--theme-comment);
}

.cm-s-mozilla .cm-unused-line {
  text-decoration: line-through;
  text-decoration-color: var(--theme-highlight-bluegrey);
}

.cm-s-mozilla .cm-executed-line {
  background-color: #fcfffc;
}

.cm-s-mozilla .cm-number,
.variable-or-property .token-number,
.variable-or-property[return] > .title > .name,
.variable-or-property[scope] > .title > .name {
  color: var(--theme-highlight-purple);
}

.CodeMirror-Tern-completion-number:before {
  background-color: hsl(72,100%,27%);
}

.theme-fg-color1,
.theme-fg-color2,
.cm-s-mozilla .cm-attribute,
.cm-s-mozilla .cm-builtin,
.cm-s-mozilla .cm-keyword,
.variables-view-variable > .title > .name {
  color: var(--theme-highlight-red);
}

.cm-s-mozilla .cm-def,
.cm-s-mozilla .cm-variable-2 {
  color: var(--blue-55);
}

.cm-s-mozilla .cm-variable {
  color: var(--purple-60);
}

.cm-s-mozilla .cm-property {
  color: var(--theme-highlight-green);
}

.CodeMirror-Tern-completion-object:before {
  background-color: hsl(208,56%,40%);
}

.theme-fg-color3,
.theme-fg-color5,
.cm-s-mozilla .cm-tag,
.cm-s-mozilla .cm-header,
.cm-s-mozilla .cm-bracket,
.cm-s-mozilla .cm-qualifier,
.variables-view-property > .title > .name {
  color: var(--theme-highlight-blue);
}

.CodeMirror-Tern-completion-array:before {
  background-color: var(--theme-highlight-bluegrey);
}

.theme-fg-color4 {
  color: var(--theme-highlight-orange);
}

.theme-fg-color6,
.cm-s-mozilla .cm-string,
.cm-s-mozilla .cm-string-2,
.variable-or-property .token-string,
.CodeMirror-Tern-farg {
  color: var(--theme-highlight-purple);
}

.CodeMirror-Tern-completion-string:before,
.CodeMirror-Tern-completion-fn:before {
  background-color: hsl(24,85%,39%);
}

.theme-fg-color7,
.cm-s-mozilla .cm-atom,
.cm-s-mozilla .cm-quote,
.cm-s-mozilla .cm-error,
.variable-or-property .token-boolean,
.variable-or-property .token-domnode,
.variable-or-property[exception] > .title > .name {
  color: var(--theme-highlight-red);
}

.CodeMirror-Tern-completion-bool:before {
  background-color: #bf5656;
}

.variable-or-property .token-domnode {
  font-weight: bold;
}

.theme-fg-contrast { /* To be used for text on theme-bg-contrast */
  color: black;
}

.theme-toolbar,
.devtools-toolbar,
.devtools-sidebar-tabs tabs,
.devtools-sidebar-alltabs,
.cm-s-mozilla .CodeMirror-dialog { /* General toolbar styling */
  color: var(--theme-body-color);
  background-color: var(--theme-toolbar-background);
  border-color: var(--theme-splitter-color);
}

.ruleview-swatch,
.computed-colorswatch {
  box-shadow: 0 0 0 1px #c4c4c4;
}

/* CodeMirror specific styles.
 * Best effort to match the existing theme, some of the colors
 * are duplicated here to prevent weirdness in the main theme. */

.CodeMirror.cm-s-mozilla { /* Inherit platform specific font sizing and styles */
  font-family: inherit;
  font-size: inherit;
  background: transparent;
}

.CodeMirror.cm-s-mozilla  pre,
.cm-s-mozilla .cm-variable-3,
.cm-s-mozilla .cm-operator,
.cm-s-mozilla .cm-special {
  color: var(--theme-body-color);
}

.cm-s-mozilla .CodeMirror-lines .CodeMirror-cursor {
  border-left: solid 1px black;
}

.cm-s-mozilla.CodeMirror-focused .CodeMirror-selected { /* selected text (focused) */
  background: rgb(185, 215, 253);
}

.cm-s-mozilla .CodeMirror-selected { /* selected text (unfocused) */
  background: rgb(176, 176, 176);
}

.cm-s-mozilla .CodeMirror-activeline-background { /* selected color with alpha */
  background: rgba(185, 215, 253, .35);
}

div.cm-s-mozilla span.CodeMirror-matchingbracket { /* highlight brackets */
  outline: solid 1px rgba(0, 0, 0, .25);
  color: black;
}

/* Highlight for a line that contains an error. */
div.CodeMirror div.error-line {
  background: rgba(255,0,0,0.2);
}

/* Generic highlighted text */
div.CodeMirror span.marked-text {
  background: rgba(255,255,0,0.2);
  border: 1px dashed rgba(192,192,0,0.6);
  margin-inline-start: -1px;
  margin-inline-end: -1px;
}

/* Highlight for evaluating current statement. */
div.CodeMirror span.eval-text {
  background-color: #ccd;
}

.cm-s-mozilla .CodeMirror-linenumber { /* line number text */
  color: var(--theme-content-color3);
}

.cm-s-mozilla .CodeMirror-gutters { /* vertical line next to line numbers */
  border-right-color: var(--theme-splitter-color);
  background-color: var(--theme-sidebar-background);
}

.cm-s-markup-view pre {
  line-height: 1.4em;
  min-height: 1.4em;
}

/* XUL panel styling (see devtools/client/shared/widgets/tooltip/Tooltip.js) */

.theme-tooltip-panel .panel-arrowcontent {
  padding: 4px;
  background: rgba(255, 255, 255, .9);
  border-radius: 5px;
  box-shadow: none;
  border: 3px solid #d9e1e8;
}

/* Overring panel arrow images to fit with our light and dark themes */

.theme-tooltip-panel .panel-arrow {
  --arrow-margin: -4px;
}

:root[platform="win"] .theme-tooltip-panel .panel-arrow {
  --arrow-margin: -7px;
}

.theme-tooltip-panel .panel-arrow[side="top"],
.theme-tooltip-panel .panel-arrow[side="bottom"] {
  list-style-image: url("chrome://devtools/skin/tooltip/arrow-vertical-light.png");
  /* !important is needed to override the popup.css rules in toolkit/themes */
  width: 39px !important;
  height: 16px !important;
}

.theme-tooltip-panel .panel-arrow[side="left"],
.theme-tooltip-panel .panel-arrow[side="right"] {
  list-style-image: url("chrome://devtools/skin/tooltip/arrow-horizontal-light.png");
  /* !important is needed to override the popup.css rules in toolkit/themes */
  width: 16px !important;
  height: 39px !important;
}

.theme-tooltip-panel .panel-arrow[side="top"] {
  margin-bottom: var(--arrow-margin);
}

.theme-tooltip-panel .panel-arrow[side="bottom"] {
  margin-top: var(--arrow-margin);
}

.theme-tooltip-panel .panel-arrow[side="left"] {
  margin-right: var(--arrow-margin);
}

.theme-tooltip-panel .panel-arrow[side="right"] {
  margin-left: var(--arrow-margin);
}

@media (min-resolution: 1.1dppx) {
  .theme-tooltip-panel .panel-arrow[side="top"],
  .theme-tooltip-panel .panel-arrow[side="bottom"] {
    list-style-image: url("chrome://devtools/skin/tooltip/[email protected]");
  }

  .theme-tooltip-panel .panel-arrow[side="left"],
  .theme-tooltip-panel .panel-arrow[side="right"] {
    list-style-image: url("chrome://devtools/skin/tooltip/[email protected]");
  }
}

.theme-tooltip-panel .devtools-tooltip-simple-text {
  color: black;
  border-bottom: 1px solid #d9e1e8;
}

.theme-tooltip-panel .devtools-tooltip-simple-text:last-child {
  border-bottom: 0;
}

.CodeMirror-hints,
.CodeMirror-Tern-tooltip {
  box-shadow: 0 0 4px rgba(128, 128, 128, .5);
  background-color: var(--theme-sidebar-background);
}
PK
!<�w�%�%chrome/devtools/skin/markup.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 {
  --markup-hidden-attr-name-color: #BA89B8;
  --markup-hidden-attr-value-color: #5C6D87;
  --markup-hidden-punctuation-color: #909090;
  --markup-hidden-tag-color: #97A4B3;
  --markup-outline: var(--theme-splitter-color);
}

.theme-dark:root {
  --markup-hidden-attr-name-color: #B07EB3;
  --markup-hidden-attr-value-color: #9893A3;
  --markup-hidden-punctuation-color: #909090;
  --markup-hidden-tag-color: #AFB5BF;
  --markup-outline: var(--theme-selection-background);
}

* {
  padding: 0;
  margin: 0;
}

:root {
  -moz-control-character-visibility: visible;
}

body {
  -moz-user-select: none;
}

/* Force height and width (possibly overflowing) from inline elements.
 * This allows long overflows of text or input fields to still be styled with
 * the container, rather than the background disappearing when scrolling */
#root {
  float: left;
  min-width: 100%;
}

/* Don't display a parent-child outline for the root elements */
#root > ul > li > .children {
  background: none;
}

html.dragging {
  overflow-x: hidden;
}

body.dragging .tag-line {
  cursor: grabbing;
}

#root-wrapper:after {
   content: "";
   display: block;
   clear: both;
   position:relative;
}

.html-editor {
  display: none;
  position: absolute;
  z-index: 2;

  /* Use the same margin/padding trick used by .child tags to ensure that
   * the editor covers up any content to the left (including expander arrows
   * and hover effects). */
  margin-left: -1000em;
  padding-left: 1000em;
}

.html-editor-inner {
  border: solid .1px;
  flex: 1 1 auto;

  /* Keep the editor away from the markup view floating scrollbars */
  margin-inline-end: 12px;
}

.html-editor iframe {
  height: 100%;
  width: 100%;
  border: none;
  margin: 0;
  padding: 0;
}

.children {
  list-style: none;
  padding: 0;
  margin: 0;
}

/* Tags are organized in a UL/LI tree and indented thanks to a left padding.
 * A very large padding is used in combination with a slightly smaller margin
 * to make sure childs actually span from edge-to-edge. */
.child {
  margin-left: -1000em;
  padding-left: 1001em;
}

/* Normally this element takes space in the layout even if it's position: relative
 * by adding height: 0 we let surrounding elements to fill the blank space */
.child.dragging {
  position: relative;
  pointer-events: none;
  opacity: 0.7;
  z-index: 1;
  height: 0;
}

/* Indicates a tag-line in the markup-view as being an active drop target by
 * drawing a horizontal line where the dragged element would be inserted if
 * dropped here */
.tag-line.drop-target::before,
.tag-line.drag-target::before {
  content: '';
  position: absolute;
  top: 0;
  width: 100%;
  /* Offset these by 1000px to make sure they cover the full width of the view */
  padding-left: 1000px;
  left: -1000px;
}

.tag-line.drag-target::before {
  border-top: 2px solid var(--theme-content-color2);
}

.tag-line.drop-target::before {
  border-top: 2px solid var(--theme-contrast-background);
}

/* In case the indicator is put on the closing .tag-line, the indentation level
 * will become misleading, so we push it forward to match the indentation level */
ul.children + .tag-line::before {
  margin-left: 14px;
}

.tag-line {
  min-height: 1.4em;
  line-height: 1.4em;
  position: relative;
  cursor: default;
  padding-left: 2px;
}

.tag-line[selected] + .children,
.tag-line[selected] ~ .tag-line {
  background-image: linear-gradient(to top, var(--markup-outline), var(--markup-outline));
  background-position-x: -6px;
  background-repeat: no-repeat;
  background-size: 1.5px 100%;
  border-left: 6px solid transparent;
  margin-left: -6px;
}

.tag-line[selected] + .children {
  background-position-y: 2px;
}

.tag-line[selected] ~ .tag-line {
  background-position-y: -2px;
  /* Unset transition-property to prevent the markup outline from horizontal shifting */
  transition-property: none;
}

.html-editor-container {
  position: relative;
  min-height: 200px;
}

/* This extra element placed in each tag is positioned absolutely to cover the
 * whole tag line and is used for background styling (when a selection is made
 * or when the tag is flashing) */
.tag-line .tag-state {
  position: absolute;
  left: -1000em;
  right: 0;
  height: 100%;
  z-index: 0;
}

.tag-line .tag-hover:not(.theme-selected) {
  background: var(--theme-selection-background-hover);
  z-index: -1;
}

.expander {
  display: inline-block;
  margin-left: -14px;
  vertical-align: middle;
  /* Make sure the expander still appears above the tag-state */
  position: relative;
  z-index: 1;
}

.child.collapsed .child, .child.collapsed .children {
  display: none;
}

.child > .tag-line:first-child .close {
  display: none;
}

.child.collapsed > .tag-line:first-child .close {
  display: inline;
}

.child.collapsed > .tag-line ~ .tag-line {
  display: none;
}

.child.collapsed .close {
  display: inline;
}

.expandable.collapsed .close::before {
  /* Display an ellipsis character in collapsed nodes that can be expanded. */
  content: "\2026";
  display: inline-block;
  width: 12px;
  height: 8px;
  margin: 0 2px;
  line-height: 3px;
  color: var(--theme-body-color-inactive);
  border-radius: 3px;
  border-style: solid;
  border-width: 1px;
  text-align: center;
  vertical-align: middle;
}

/* Hide HTML void elements (img, hr, br, …) closing tag when the element is not
 * expanded (it can be if it has pseudo-elements attached) */
.child.collapsed > .tag-line .void-element .close {
  display: none;
}

.closing-bracket {
  pointer-events: none;
}

.newattr {
  display: inline-block;
  width: 1em;
  height: 1ex;
  margin-right: -1em;
  padding: 1px 0;
}

.attr-value .link {
  text-decoration: underline;
}

.newattr:focus {
  margin-right: 0;
}

.flash-out {
  transition: background .5s;
}

.editor {
  /* Make sure the editor still appears above the tag-state */
  position: relative;
  z-index: 1;
}

.editor.comment:not(.doctype) {
  color: var(--theme-highlight-green);
}

.editor.text {
  display: inline-block;
}

.editor.text pre,
.editor.comment pre {
  font: inherit;
}

.theme-dark .editor.text pre {
  color: var(--theme-content-color1);
}

/* Whitespace only text nodes are sometimes shown in the markup-view, and when they do
   they get a greyed-out whitespace symbol so users know what they are */
.editor.text .whitespace {
  padding: 0 .5em;
}

.editor.text .whitespace::before {
  /* black circle (full) character */
  content: "\25cf";
  display: inline-block;
  width: 12px;
  height: 8px;
  margin: 0 2px;
  line-height: 7px;
  font-size: 0.6em;
  color: var(--theme-body-color-inactive);
  border-radius: 3px;
  border-style: solid;
  border-width: 1px;
  text-align: center;
  vertical-align: middle;
}

.tag-line[selected] .editor.text .whitespace::before {
  color: white;
}

.more-nodes {
  padding-left: 16px;
}

.styleinspector-propertyeditor {
  border: 1px solid #CCC;
}

/* Draw a circle next to nodes that have a pseudo class lock.
   Center vertically with the 1.4em line height on .tag-line */
.child.pseudoclass-locked::before {
  content: "";
  background: var(--theme-highlight-lightorange);
  border-radius: 50%;
  width: .8em;
  height: .8em;
  margin-top: .3em;
  left: 1px;
  position: absolute;
  z-index: 1;
}

.not-displayed {
  color: var(--markup-hidden-punctuation-color);
}

.not-displayed .attr-name {
  color: var(--markup-hidden-attr-name-color);
}

.not-displayed .attr-value {
  color: var(--markup-hidden-attr-value-color);
}

.not-displayed .tag {
  color: var(--markup-hidden-tag-color);
}

/* Firebug Theme */

.theme-firebug .theme-fg-color3 {
  color: var(--theme-graphs-full-blue);
  font-weight: normal;
}

.theme-firebug .open,
.theme-firebug .close,
.theme-firebug .attr-name.theme-fg-color2 {
  color: var(--theme-highlight-purple);
}

.theme-firebug .attr-value.theme-fg-color6 {
  color: var(--theme-highlight-red);
}

.theme-firebug .markupview-display,
.theme-firebug .markupview-events {
  font-size: var(--theme-toolbar-font-size);
}

/* In case a node isn't displayed in the page, we fade the syntax highlighting */
.theme-firebug .not-displayed .open,
.theme-firebug .not-displayed .close {
  opacity: .5;
}

/* Selected nodes in the tree should have light selected text.
   theme-selected doesn't work in this case since the text is a
   sibling of the class, not a child. */
.theme-selected ~ .editor,
.theme-selected ~ .editor.comment,
.theme-selected ~ .editor .theme-fg-color1,
.theme-selected ~ .editor .theme-fg-color2,
.theme-selected ~ .editor .theme-fg-color3,
.theme-selected ~ .editor .theme-fg-color4,
.theme-selected ~ .editor .theme-fg-color5,
.theme-selected ~ .editor .theme-fg-color6,
.theme-selected ~ .editor .theme-fg-color7,
.theme-selected ~ .editor .close::before {
  color: var(--theme-selection-color);
}

/* Make sure even text nodes are white when selected in the Inspector panel. */
.theme-firebug .theme-selected ~ .editor .open,
.theme-firebug .theme-selected ~ .editor .close {
  color: var(--theme-selection-color);
}

/* Applicable to the DOCTYPE */
.doctype {
  font-style: italic;
}

.theme-firebug .doctype {
  color: #787878;
}

/* Display and Events Bubble */
.markupview-display,
.markupview-events {
  display: none;
  font-size: 8px;
  font-weight: bold;
  line-height: 10px;
  border-radius: 3px;
  padding: 0px 2px;
  margin-inline-start: 5px;
  -moz-user-select: none;
  background-color: var(--theme-body-color-alt);
  color: var(--theme-body-background);
}

.markupview-events {
  cursor: pointer;
}
PK
!<:!��6�6chrome/devtools/skin/memory.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

/* CSS Variables specific to this panel that aren't defined by the themes */
.theme-dark {
  --cell-border-color: rgba(255,255,255,0.15);
  --cell-border-color-light: rgba(255,255,255,0.1);
  --focus-cell-border-color: rgba(255,255,255,0.5);
  --row-alt-background-color: rgba(86, 117, 185, 0.15);
  --row-hover-background-color: rgba(86, 117, 185, 0.25);
}

.theme-light {
  --cell-border-color: rgba(0,0,0,0.15);
  --cell-border-color-light: rgba(0,0,0,0.1);
  --focus-cell-border-color: rgba(0,0,0,0.3);
  --row-alt-background-color: rgba(76,158,217,0.1);
  --row-hover-background-color: rgba(76,158,217,0.2);
}

html, body, #app, #memory-tool {
  height: 100%;
}

#memory-tool {
  /**
   * Flex: contains two children: .devtools-toolbar and #memory-tool-container,
   * which need to be laid out vertically. The toolbar has a fixed height and
   * the container needs to flex to fill out all remaining vertical space.
   */
  display: flex;
  flex-direction: column;
  --sidebar-width: 185px;
  /**
   * If --heap-tree-row-height changes, be sure to change HEAP_TREE_ROW_HEIGHT
   * in `devtools/client/memory/components/Heap.js`.
   */
  --heap-tree-row-height: 18px;
  --heap-tree-header-height: 18px;
}

/**
 * Toolbar
 */

.devtools-toolbar {
  /**
   * Flex: contains several children, which need to be laid out horizontally,
   * and aligned vertically in the middle of the container.
   */
  display: flex;
  align-items: center;
}

.devtools-toolbar > .toolbar-group:nth-of-type(1) {
  /**
   * We want this to be exactly at a `--sidebar-width` distance from the
   * toolbar's start boundary. A `.devtools-toolbar` has a 3px start padding.
   */
  flex: 0 0 calc(var(--sidebar-width) - 4px);
  border-inline-end: 1px solid var(--theme-splitter-color);
  margin-inline-end: 5px;
  padding-right: 1px;
}

.devtools-toolbar > .toolbar-group {
  /**
   * Flex: contains several children, which need to be laid out horizontally,
   * and aligned vertically in the middle of the container.
   */
  display: flex;
  align-items: center;
  flex: 1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.devtools-toolbar > .toolbar-group > label {
  /**
   * Flex: contains form controls and text, which need to be laid out
   * horizontally, vertically aligned in the middle of the container.
   */
  display: flex;
  align-items: center;
  margin-inline-end: 5px;
}

.devtools-toolbar > .toolbar-group > label.display-by > span {
  margin-inline-end: 5px;
}

.devtools-toolbar > .toolbar-group > label.label-by > span {
  margin-inline-end: 5px;
}

.devtools-toolbar > label {
  margin-inline-end: 5px;
  display: flex;
  align-items: center;
}

#select-view {
  margin-inline-start: 5px;
}

#take-snapshot::before {
  background-image: url(images/command-screenshot.svg);
}

#clear-snapshots::before {
  background-image: url(chrome://devtools/skin/images/clear.svg);
}

#diff-snapshots::before {
  background-image: url(chrome://devtools/skin/images/diff.svg);
}

#import-snapshot::before {
  background-image: url(chrome://devtools/skin/images/import.svg);
}

#record-allocation-stacks-label,
#pop-view-button-label {
  border-inline-end: 1px solid var(--theme-splitter-color);
  padding-inline-end: 5px;
}

.spacer {
  flex: 1;
}

#filter {
  align-self: stretch;
  margin: 2px;
}

/**
 * Container (sidebar + main panel)
 */

#memory-tool-container {
  /**
   * Flex: contains two children: .list (sidebar) and #heap-view (main panel),
   * which need to be laid out horizontally. The sidebar has a fixed width and
   * the main panel needs to flex to fill out all remaining horizontal space.
   */
  display: flex;
  /**
   * Flexing to fill out remaining vertical space. The preceeding sibling is
   * the toolbar. @see #memory-tool.
   */
  flex: 1;
  overflow: hidden;
}

/**
 * Sidebar
 */

.list {
  width: var(--sidebar-width);
  min-width: var(--sidebar-width);
  overflow-y: auto;
  margin: 0;
  padding: 0;
  background-color: var(--theme-sidebar-background);
  border-inline-end: 1px solid var(--theme-splitter-color);
}

.snapshot-list-item {
  /**
   * Flex: contains several children, which need to be laid out vertically.
   */
  display: flex;
  flex-direction: column;
  color: var(--theme-body-color);
  border-bottom: 1px solid rgba(128,128,128,0.15);
  padding: 8px;
  cursor: default;
}

.snapshot-list-item.selected {
  background-color: var(--theme-selection-background);
  color: var(--theme-selection-color);
}

.snapshot-list-item.selected ::-moz-selection {
  background-color: var(--theme-selection-color);
  color: var(--theme-selection-background);
}

.snapshot-list-item .snapshot-info {
  display: flex;
  justify-content: space-between;
  font-size: 90%;
}

.snapshot-list-item .snapshot-title {
  display: flex;
  justify-content: space-between;
}

.snapshot-list-item .save {
  text-decoration: underline;
  cursor: pointer;
}

.snapshot-list-item .delete {
  cursor: pointer;
  background-color: transparent;
  border: 0;
  padding: 0;
  position: relative;
  min-height: 1em;
  min-width: 1.3em;
  color: currentColor;
}

.snapshot-list-item .delete::before {
  display: block;
  width: 16px;
  height: 16px;
  content: "";
  background-image: url("chrome://devtools/skin/images/close.svg");
  background-repeat: no-repeat;
  -moz-context-properties: fill;
  fill: currentColor;
}

.snapshot-list-item > .snapshot-title {
  margin-bottom: 14px;
}

.snapshot-list-item > .snapshot-title > input[type=checkbox] {
  margin: 0;
  margin-inline-end: 5px;
}

.snapshot-list-item > .snapshot-state,
.snapshot-list-item > .snapshot-totals {
  font-size: 90%;
  color: var(--theme-body-color-alt);
}

.snapshot-list-item.selected > .snapshot-state,
.snapshot-list-item.selected > .snapshot-totals {
  /* Text inside a selected item should not be custom colored. */
  color: inherit !important;
}

/**
 * Main panel
 */

.vbox {
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100%;
  overflow: auto;
  padding: 0;
  margin: 0;
}

.vbox > * {
  flex: 1;

  /**
   * By default, flex items have min-width: auto;
   * (https://drafts.csswg.org/css-flexbox/#min-size-auto)
   */
  min-width: 0;
}

#heap-view {
  /**
   * Flex: contains a .heap-view-panel which needs to fill out all the
   * available space, horizontally and vertically.
   */;
  display: flex;
  /**
   * Flexing to fill out remaining horizontal space. The preceeding sibling
   * is the sidebar. @see #memory-tool-container.
   */
  flex: 1;
  background-color: var(--theme-body-background);

  /**
   * By default, flex items have min-width: auto;
   * (https://drafts.csswg.org/css-flexbox/#min-size-auto)
   */
  min-width: 0;
  font-size: 90%;
}

#heap-view > .heap-view-panel {
  /**
   * Flex: can contain several children, including a tree with a header and
   * multiple rows, all of which need to be laid out vertically. When the
   * tree is visible, the header has a fixed height and tree body needs to flex
   * to fill out all remaining vertical space.
   */
  display: flex;
  flex-direction: column;
  /**
   * Flexing to fill out remaining horizontal space. @see #heap-view.
   */
  flex: 1;

  /**
   * By default, flex items have min-width: auto;
   * (https://drafts.csswg.org/css-flexbox/#min-size-auto)
   */
  min-width: 0;
}

#heap-view > .heap-view-panel > .snapshot-status,
#heap-view > .heap-view-panel > .take-snapshot,
#heap-view .empty,
#shortest-paths-select-node-msg {
  margin: auto;
  margin-top: 65px;
  font-size: 120%;
}

#heap-view > .heap-view-panel > .take-snapshot {
  padding: 5px;
}

#heap-view > .heap-view-panel[data-state="snapshot-state-error"] pre {
  background-color: var(--theme-body-background);
  margin: 20px;
  padding: 20px;
}

/**
 * Heap tree view header
 */

.header {
  /**
   * Flex: contains several span columns, all of which need to be laid out
   * horizontally. All columns except the last one have percentage widths, and
   * the last one needs to flex to fill out all remaining horizontal space.
   */
  display: flex;
  color: var(--theme-body-color);
  background-color: var(--theme-tab-toolbar-background);
  border-bottom: 1px solid var(--cell-border-color);
  flex: 0;
}

.header > span,
#shortest-paths-header {
  text-overflow: ellipsis;
  line-height: var(--heap-tree-header-height);
  justify-content: center;
  justify-self: center;
  white-space: nowrap;
}

.header > span {
  overflow: hidden;
}

.header > .heap-tree-item-name {
  justify-content: flex-start;
}

#shortest-paths {
  background-color: var(--theme-body-background);
  overflow: hidden;
  height: 100%;
  width: 100%;
}

#shortest-paths-select-node-msg {
  justify-self: center;
}

/**
 * Heap tree view body
 */

.tree {
  /**
   * Flexing to fill out remaining vertical space. @see .heap-view-panel
   */
  flex: 1;
  overflow-y: auto;
  background-color: var(--theme-body-background);
}

.tree-node {
  height: var(--heap-tree-row-height);
  line-height: var(--heap-tree-row-height);
  cursor: default;
}

.children-pointer {
  padding-inline-end: 5px;
}

.children-pointer:dir(rtl) {
  transform: scaleX(-1);
}

/**
 * Heap tree view columns
 */

.heap-tree-item {
  /**
   * Flex: contains several span columns, all of which need to be laid out
   * horizontally. All columns except the last one have percentage widths, and
   * the last one needs to flex to fill out all remaining horizontal space.
   */
  display: flex;
}

.tree-node-odd {
  background-color: var(--row-alt-background-color);
}

.tree-node:hover {
  background-color: var(--row-hover-background-color);
}

.heap-tree-item.focused {
  background-color: var(--theme-selection-background);
  color: var(--theme-selection-color);
}

.heap-tree-item.focused ::-moz-selection {
  background-color: var(--theme-selection-color);
  color: var(--theme-selection-background);
}

.heap-tree-item-individuals,
.heap-tree-item-bytes,
.heap-tree-item-count,
.heap-tree-item-total-bytes,
.heap-tree-item-total-count {
  /**
   * Flex: contains several subcolumns, which need to be laid out horizontally.
   * These subcolumns may have specific widths or need to flex.
   */
  display: flex;
  /* Make sure units/decimals/... are always vertically aligned to right in both LTR and RTL locales */
  text-align: right;
  border-inline-end: var(--cell-border-color) 1px solid;
}

.heap-tree-item-count,
.heap-tree-item-total-count,
.heap-tree-item-bytes,
.heap-tree-item-total-bytes {
  width: 10%;
  /*
   * Provision for up to 19 characters:
   *
   *     GG_MMM_KKK_BBB_100%
   *     |            |||  |
   *     '------------'|'--'
   *     14 ch for 10s | 4 ch for the largest % we will
   *     of GB and     | normally see: "100%"
   *     spaces every  |
   *     3 digits      |
   *                   |
   *             A space between the number and percent
   */
  min-width: 19ch;
}

.heap-tree-item-name {
  /**
   * Flexing to fill out remaining vertical space.
   * @see .header and .heap-tree-item */
  flex: 1;
  padding-inline-start: 5px;
  overflow: hidden;
  text-overflow: ellipsis;
}

.heap-tree-item-name .arrow {
  display: inline-block;
  vertical-align: middle;
}

.heap-tree-item-name .frame-link {
  display: inline-block;
}

/**
 * Heap tree view subcolumns
 */

.heap-tree-number,
.heap-tree-percent,
.heap-tree-item-name {
  white-space: nowrap;
}

.heap-tree-number {
  padding: 0 3px;
  flex: 1;
  color: var(--theme-content-color3);
  /* Make sure number doesn't appear backwards on RTL locales */
  direction: ltr;
}

.heap-tree-percent {
  padding-inline-start: 3px;
  padding-inline-end: 3px;
}

.heap-tree-number,
.heap-tree-percent {
  font-family: var(--monospace-font-family);
}

.heap-tree-percent {
  width: 4ch;
}

.heap-tree-item.focused .heap-tree-number,
.heap-tree-item.focused .heap-tree-percent {
  color: inherit;
}

.heap-tree-item-individuals {
  min-width: 38px;
  overflow: hidden;
  margin: 0;
}

.heap-tree-item-individuals > button {
  width: 32px;
  /* Override default styles for toolbar buttons to fix entire row height. */
  margin: 0 auto !important;
  color: inherit;
}

/**
 * Tree map
 */

.tree-map-container {
  width: 100%;
  height: 100%;
  position: relative;
  overflow: hidden;
}

/**
 * Heap tree errors.
 */

.error::before {
  content: "";
  display: inline-block;
  width: 12px;
  height: 12px;
  max-height: 12px;
  background-image: url(chrome://devtools/skin/images/webconsole.svg);
  background-size: 72px 60px;
  background-position: -24px -24px;
  background-repeat: no-repeat;
  margin: 0px;
  margin-top: 2px;
  margin-inline-end: 5px;
}

.theme-light .error::before {
  background-image: url(chrome://devtools/skin/images/webconsole.svg#light-icons);
}

/**
 * Frame View components
 */

.separator,
.not-available,
.heap-tree-item-address {
  opacity: .5;
  margin-left: .5em;
  margin-right: .5em;
}

.heap-tree-item-address {
  font-family: monospace;
}

.no-allocation-stacks {
  border-color: var(--theme-splitter-color);
  border-style: solid;
  border-width: 0px 0px 1px 0px;
  text-align: center;
  padding: 5px;
}

/**
 * Dagre-D3 graphs
 */

svg {
  --arrow-color: var(--theme-splitter-color);
  --text-color: var(--theme-body-color);
}

.theme-dark svg {
  --arrow-color: var(--theme-body-color-alt);
}

svg #arrowhead {
  /* !important is needed to override inline style */
  fill: var(--arrow-color) !important;
}

.edgePath path {
  stroke-width: 1px;
  fill: none;
  stroke: var(--arrow-color);
}

g.edgeLabel rect {
  fill: var(--theme-body-background);
}

g.edgeLabel tspan {
  fill: var(--text-color);
}

.nodes rect {
  stroke-width: 1px;
  stroke: var(--theme-splitter-color);
  fill: var(--theme-toolbar-background);
}

text {
  font-size: 1.25em;
  fill: var(--text-color);
  /* Make sure text stays inside its container in RTL locales */
  direction: ltr;
}
PK
!<s~�`��chrome/devtools/skin/perf.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

.perf {
  width: 100%;
  height: 100%;
  position: absolute;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.devtools-button.perf-button {
  padding: 5px;
  margin: auto;
  font-size: 120%;
}

.perf-additional-message {
  margin: 10px;
  margin-top: 65px;
}
PK
!<`E��GG$chrome/devtools/skin/performance.css/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

/* CSS Variables specific to this panel that aren't defined by the themes */
.theme-dark {
  --cell-border-color: rgba(255,255,255,0.15);
  --cell-border-color-light: rgba(255,255,255,0.1);
  --focus-cell-border-color: rgba(255,255,255,0.5);
  --row-alt-background-color: rgba(86, 117, 185, 0.15);
  --row-hover-background-color: rgba(86, 117, 185, 0.25);
}

.theme-light {
  --cell-border-color: rgba(0,0,0,0.15);
  --cell-border-color-light: rgba(0,0,0,0.1);
  --focus-cell-border-color: rgba(0,0,0,0.3);
  --row-alt-background-color: rgba(76,158,217,0.1);
  --row-hover-background-color: rgba(76,158,217,0.2);
}

.theme-firebug {
  --cell-border-color: rgba(0,0,0,0.15);
  --cell-border-color-light: rgba(0,0,0,0.1);
  --focus-cell-border-color: rgba(0,0,0,0.3);
  --row-alt-background-color: rgba(76,158,217,0.1);
  --row-hover-background-color: rgba(76,158,217,0.2);
}

/*
 * DE-XUL: Set a sidebar width because inline XUL components will cause the flex
 * to overflow if dynamically sized.
 */
.performance-tool {
  --sidebar-width: 185px;
}

/**
 * A generic class to hide elements, replacing the `element.hidden` attribute
 * that we use to hide elements that can later be active
 */
.hidden {
  display: none;
  width: 0px;
  height: 0px;
}

/* Toolbar */

#performance-toolbar-control-other {
  padding-inline-end: 5px;
}

#performance-toolbar-controls-detail-views .toolbarbutton-text {
  padding-inline-start: 4px;
  padding-inline-end: 8px;
}

#filter-button {
  list-style-image: url(chrome://devtools/skin/images/filter.svg);
}

#performance-filter-menupopup > menuitem .menu-iconic-left::after {
  content: "";
  display: block;
  width: 8px;
  height: 8px;
  margin: 0 8px;
  border-radius: 1px;
}

/* Details panel buttons */

#select-waterfall-view {
  list-style-image: url(images/performance-details-waterfall.svg);
}

#select-js-calltree-view,
#select-memory-calltree-view {
  list-style-image: url(images/performance-details-call-tree.svg);
}

#select-js-flamegraph-view,
#select-memory-flamegraph-view {
  list-style-image: url(images/performance-details-flamegraph.svg);
}

#select-optimizations-view {
  list-style-image: url(images/profiler-stopwatch.svg);
}

/* Recording buttons */

#clear-button::before {
  background-image: var(--clear-icon-url);
}

#main-record-button::before {
  background-image: url(images/profiler-stopwatch.svg);
}

#import-button::before {
  background-image: url(images/import.svg);
}

#main-record-button .button-icon, #import-button .button-icon {
  margin: 0;
}

#main-record-button .button-text, #import-button .button-text {
  display: none;
}

.notice-container .record-button {
  padding: 5px !important;
}

.notice-container .record-button.checked,
.notice-container .record-button.checked {
  color: var(--theme-selection-color) !important;
  background: var(--theme-selection-background) !important;
}

/* Sidebar & recording items */

#recordings-pane {
  border-inline-end: 1px solid var(--theme-splitter-color);
  width: var(--sidebar-width);
}

#recording-controls-mount {
  width: var(--sidebar-width);
}

#recording-controls-mount > div {
  width: var(--sidebar-width);
}

/*
 * DE-XUL: The height of the toolbar is not correct without tweaking the line-height.
 */
#recordings-pane .devtools-toolbar {
  line-height: 0;
}

.theme-sidebar {
  position: relative;
}

/**
 * DE-XUL: This is probably only needed for the html:div inside of a vbox.
 */
#recordings-list > div {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  overflow-y: auto;
  overflow-x: hidden;
}

.recording-list {
  width: var(--sidebar-width);
  min-width: var(--sidebar-width);
  margin: 0;
  padding: 0;
  background-color: var(--theme-sidebar-background);
  border-inline-end: 1px solid var(--theme-splitter-color);
}

.recording-list-item {
  display: flex;
  flex-direction: column;
  color: var(--theme-body-color);
  border-bottom: 1px solid rgba(128,128,128,0.15);
  padding: 8px;
  cursor: default;
}

.recording-list-item.selected {
  background-color: var(--theme-selection-background);
  color: var(--theme-selection-color);
}

.recording-list-empty {
  padding: 8px;
}

.recording-list-item-label {
  font-size: 110%;
}

.recording-list-item-footer {
  padding-top: 4px;
  font-size: 90%;
  display: flex;
  justify-content: space-between;
}

.recording-list-item-save {
  background: none;
  border: none;
  text-decoration: underline;
  cursor: pointer;
  font-size: 90%;
  padding:0;
}

.recording-list-item-duration,
.recording-list-item-save {
  color: var(--theme-body-color-alt);
}

.recording-list-item.selected .recording-list-item-duration,
.recording-list-item.selected .recording-list-item-save {
  color: var(--theme-body-color-alt);
  color: var(--theme-selection-color);
}

#recordings-list .selected label {
  /* Text inside a selected item should not be custom colored. */
  color: inherit !important;
}

/* Recording notices */

.notice-container {
  font-size: 120%;
  background-color: var(--theme-body-background);
  color: var(--theme-body-color);
  padding-bottom: 20vh;
}

.tool-disabled-message {
  text-align: center;
}

.console-profile-recording-notice,
.console-profile-stop-notice {
  overflow: hidden;
}

.console-profile-command {
  font-family: monospace;
  margin: 3px 2px;
}

.realtime-disabled-on-e10s-message {
  display: none;
}

#performance-view[e10s="disabled"] .realtime-disabled-on-e10s-message {
  display: initial;
  opacity: 0.5;
}

.buffer-status-message,
.buffer-status-message-full {
  display: none;
}

#details-pane-container[buffer-status="in-progress"] .buffer-status-message {
  display: initial;
  opacity: 0.5;
}

#details-pane-container[buffer-status="full"] .buffer-status-message {
  display: initial;
  color: var(--theme-highlight-red);
  font-weight: bold;
  opacity: 1;
}

#details-pane-container[buffer-status="full"] .buffer-status-message-full {
  display: initial;
}

/* Profile call tree */

.call-tree-cells-container {
  overflow: auto;
}

.call-tree-cells-container[categories-hidden] .call-tree-category {
  display: none;
}

.call-tree-header {
  font-size: 90%;
  padding-top: 2px !important;
  padding-bottom: 2px !important;
}

.call-tree-header[type="duration"],
.call-tree-cell[type="duration"],
.call-tree-header[type="self-duration"],
.call-tree-cell[type="self-duration"] {
  min-width: 6vw;
  width: 6vw;
}

.call-tree-header[type="percentage"],
.call-tree-cell[type="percentage"],
.call-tree-header[type="self-percentage"],
.call-tree-cell[type="self-percentage"] {
  min-width: 5vw;
  width: 5vw;
}

.call-tree-header[type="samples"],
.call-tree-cell[type="samples"] {
  min-width: 4.5vw;
  width: 4.5vw;
}

.call-tree-header[type="count"],
.call-tree-cell[type="count"],
.call-tree-header[type="self-count"],
.call-tree-cell[type="self-count"],
.call-tree-header[type="size"],
.call-tree-cell[type="size"],
.call-tree-header[type="self-size"],
.call-tree-cell[type="self-size"],
.call-tree-header[type="count-percentage"],
.call-tree-cell[type="count-percentage"],
.call-tree-header[type="self-count-percentage"],
.call-tree-cell[type="self-count-percentage"],
.call-tree-header[type="size-percentage"],
.call-tree-cell[type="size-percentage"],
.call-tree-header[type="self-size-percentage"],
.call-tree-cell[type="self-size-percentage"] {
  min-width: 6vw;
  width: 6vw;
}

.call-tree-header,
.call-tree-cell {
  -moz-box-align: center;
  overflow: hidden;
  text-overflow: ellipsis;
  padding: 1px 4px;
  color: var(--theme-body-color);
  border-inline-end-color: var(--cell-border-color);
}

.call-tree-header:not(:last-child),
.call-tree-cell:not(:last-child) {
  border-inline-end-width: 1px;
  border-inline-end-style: solid;
}

.call-tree-header:not(:last-child) {
  text-align: center;
}

.call-tree-cell:not(:last-child) {
  text-align: end;
}

.call-tree-header {
  background-color: var(--theme-tab-toolbar-background);
}

.call-tree-item .call-tree-cell,
.call-tree-item .call-tree-cell[type=function] description {
  -moz-user-select: text;
  /* so that optimizations view doesn't break the lines in call tree */
  white-space: nowrap;
}

.call-tree-item .call-tree-cell::-moz-selection,
.call-tree-item .call-tree-cell[type=function] description::-moz-selection {
  background-color: var(--theme-highlight-orange);
}

.call-tree-item:last-child {
  border-bottom: 1px solid var(--cell-border-color);
}

.call-tree-item:nth-child(2n) {
  background-color: var(--row-alt-background-color);
}

.call-tree-item:hover {
  background-color: var(--row-hover-background-color);
}

.call-tree-item:focus {
  background-color: var(--theme-selection-background);
}

.call-tree-item:focus description {
  color: var(--theme-selection-color) !important;
}

.call-tree-item:focus .call-tree-cell {
  border-inline-end-color: var(--focus-cell-border-color);
}

.call-tree-item:not([origin="content"]) .call-tree-name,
.call-tree-item:not([origin="content"]) .call-tree-url,
.call-tree-item:not([origin="content"]) .call-tree-line,
.call-tree-item:not([origin="content"]) .call-tree-column {
  /* Style chrome and non-JS nodes differently. */
  opacity: 0.6;
}

.call-tree-name {
  margin-inline-end: 4px !important;
}

.call-tree-url {
  cursor: pointer;
}

.call-tree-url:hover {
  text-decoration: underline;
}

.call-tree-url, .tree-widget-item:not(.theme-selected) .opt-url {
  color: var(--theme-highlight-blue);
}

.call-tree-line, .tree-widget-item:not(.theme-selected) .opt-line {
  color: var(--theme-highlight-orange);
}

.call-tree-column {
  color: var(--theme-highlight-orange);
  opacity: 0.6;
}

.call-tree-host {
  margin-inline-start: 8px !important;
  font-size: 90%;
  color: var(--theme-content-color2);
}

.call-tree-category {
  transform: scale(0.75);
  transform-origin: center right;
}

/**
 * Waterfall markers tree
 */

#waterfall-tree {
  /* DE-XUL: convert this to display: flex once performance.xul is converted to HTML */
  display: -moz-box;
  -moz-box-orient: vertical;
  -moz-box-flex: 1;
}

.waterfall-markers {
  /* DE-XUL: convert this to display: flex once performance.xul is converted to HTML */
  display: -moz-box;
  -moz-box-orient: vertical;
  -moz-box-flex: 1;
}

.waterfall-header {
  display: flex;
}

.waterfall-header-ticks {
  display: flex;
  flex: auto;
  align-items: center;
  overflow: hidden;
}

.waterfall-header-name {
  padding: 2px 4px;
  font-size: 90%;
}

.waterfall-header-tick {
  width: 100px;
  font-size: 9px;
  transform-origin: left center;
  color: var(--theme-body-color);
}

.waterfall-header-tick:not(:first-child) {
  margin-inline-start: -100px !important; /* Don't affect layout. */
}

.waterfall-background-ticks {
  /* Background created on a <canvas> in js. */
  /* @see devtools/client/performance/modules/widgets/waterfall-ticks.js */
  background-image: -moz-element(#waterfall-background);
  background-repeat: repeat-y;
  background-position: -1px center;
}

/**
 * Markers waterfall breakdown
 */

.waterfall-markers .tree {
  /* DE-XUL: convert this to display: flex once performance.xul is converted to HTML */
  display: -moz-box;
  -moz-box-orient: vertical;
  -moz-box-flex: 1;
  overflow-x: hidden;
  overflow-y: auto;
  --waterfall-tree-row-height: 15px;
}

.waterfall-markers .tree-node {
  display: flex;
  height: var(--waterfall-tree-row-height);
  line-height: var(--waterfall-tree-row-height);
}

.waterfall-tree-item {
  display: flex;
  flex: auto;
}

.theme-light .waterfall-markers .tree-node:not([data-depth="0"]) {
  background-image: repeating-linear-gradient(
    -45deg,
    transparent 0px,
    transparent 2px,
    rgba(0,0,0,0.025) 2px,
    rgba(0,0,0,0.025) 4px
  );
}

.theme-dark .waterfall-markers .tree-node:not([data-depth="0"]) {
  background-image: repeating-linear-gradient(
    -45deg,
    transparent 0px,
    transparent 2px,
    rgba(255,255,255,0.05) 2px,
    rgba(255,255,255,0.05) 4px
  );
}

.theme-light .waterfall-tree-item[data-expandable] .waterfall-marker-bullet,
.theme-light .waterfall-tree-item[data-expandable] .waterfall-marker-bar {
  background-image: repeating-linear-gradient(
    -45deg,
    transparent 0px,
    transparent 5px,
    rgba(255,255,255,0.35) 5px,
    rgba(255,255,255,0.35) 10px
  );
}

.theme-dark .waterfall-tree-item[data-expandable] .waterfall-marker-bullet,
.theme-dark .waterfall-tree-item[data-expandable] .waterfall-marker-bar {
  background-image: repeating-linear-gradient(
    -45deg,
    transparent 0px,
    transparent 5px,
    rgba(0,0,0,0.35) 5px,
    rgba(0,0,0,0.35) 10px
  );
}

.waterfall-markers .tree-node[data-expanded],
.waterfall-markers .tree-node:not([data-depth="0"]) + .tree-node[data-depth="0"] {
  box-shadow: 0 -1px var(--cell-border-color-light);
}

.tree-node-odd .waterfall-marker {
  background-color: var(--row-alt-background-color);
}

.waterfall-markers .tree-node:hover {
  background-color: var(--row-hover-background-color);
}

.waterfall-markers .tree-node-last {
  border-bottom: 1px solid var(--cell-border-color);
}

.waterfall-tree-item.focused {
  background-color: var(--theme-selection-background);
}

/**
 * Marker left sidebar
 */

.waterfall-sidebar {
  display: flex;
  align-items: center;
  box-sizing: border-box;
  border-inline-end: 1px solid var(--cell-border-color);
}

.waterfall-tree-item > .waterfall-sidebar:hover,
.waterfall-tree-item:hover > .waterfall-sidebar,
.waterfall-tree-item.focused > .waterfall-sidebar {
  background: transparent;
}

.waterfall-tree-item.focused > .waterfall-sidebar {
  color: var(--theme-selection-color);
}

.waterfall-marker-bullet {
  width: 8px;
  height: 8px;
  margin-inline-start: 8px;
  margin-inline-end: 6px;
  border-radius: 1px;
  box-sizing: border-box;
}

.waterfall-marker-name {
  font-size: 95%;
  padding-bottom: 1px !important;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/**
 * Marker timebar
 */

.waterfall-marker {
  display: flex;
  flex: auto;
  overflow: hidden;
}

.waterfall-marker-wrap {
  display: flex;
  align-items: center;
  transform-origin: left center;
}

.waterfall-marker-bar {
  height: 9px;
  border-radius: 1px;
  box-sizing: border-box;
}

/**
 * OTMT markers
 */

.waterfall-tree-item[data-otmt=true] .waterfall-marker-bullet,
.waterfall-tree-item[data-otmt=true] .waterfall-marker-bar {
  background-color: transparent;
  border-width: 1px;
  border-style: solid;
}

/**
 * Marker details view
 */

#waterfall-details {
  padding-inline-start: 8px;
  padding-inline-end: 8px;
  padding-top: 2vh;
  overflow: auto;
  min-width: 50px;
}

#waterfall-details > * {
  padding-top: 3px;
}

.marker-details-bullet {
  width: 8px;
  height: 8px;
  border-radius: 1px;
}

.marker-details-name-label {
  padding-inline-end: 4px;
}

.marker-details-type {
  font-size: 1.2em;
  font-weight: bold;
}

.marker-details-duration {
  font-weight: bold;
}

.marker-details-customcontainer .custom-button {
  padding: 2px 5px;
  border-width: 1px;
  color: var(--theme-highlight-blue);
  text-decoration: none;
}

.marker-details-customcontainer .custom-button:hover {
  text-decoration: underline;
}

/**
 * Marker colors
 */

menuitem.marker-color-graphs-full-red .menu-iconic-left::after,
.marker-color-graphs-full-red {
  background-color: var(--theme-graphs-full-red);
  border-color: var(--theme-graphs-full-red);
}
menuitem.marker-color-graphs-full-blue .menu-iconic-left::after,
.marker-color-graphs-full-blue {
  background-color: var(--theme-graphs-full-blue);
  border-color: var(--theme-graphs-full-blue);
}

menuitem.marker-color-graphs-green .menu-iconic-left::after,
.marker-color-graphs-green {
  background-color: var(--theme-graphs-green);
  border-color: var(--theme-graphs-green);
}
menuitem.marker-color-graphs-blue .menu-iconic-left::after,
.marker-color-graphs-blue {
  background-color: var(--theme-graphs-blue);
  border-color: var(--theme-graphs-blue);
}
menuitem.marker-color-graphs-bluegrey .menu-iconic-left::after,
.marker-color-graphs-bluegrey {
  background-color: var(--theme-graphs-bluegrey);
  border-color: var(--theme-graphs-bluegrey);
}
menuitem.marker-color-graphs-purple .menu-iconic-left::after,
.marker-color-graphs-purple {
  background-color: var(--theme-graphs-purple);
  border-color: var(--theme-graphs-purple);
}
menuitem.marker-color-graphs-yellow .menu-iconic-left::after,
.marker-color-graphs-yellow {
  background-color: var(--theme-graphs-yellow);
  border-color: var(--theme-graphs-yellow);
}
menuitem.marker-color-graphs-orange .menu-iconic-left::after,
.marker-color-graphs-orange {
  background-color: var(--theme-graphs-orange);
  border-color: var(--theme-graphs-orange);
}
menuitem.marker-color-graphs-red .menu-iconic-left::after,
.marker-color-graphs-red {
  background-color: var(--theme-graphs-red);
  border-color: var(--theme-graphs-red);
}
menuitem.marker-color-graphs-grey .menu-iconic-left::after,
.marker-color-graphs-grey{
  background-color: var(--theme-graphs-grey);
  border-color: var(--theme-graphs-grey);
}

/**
 * Configurable Options
 *
 * Elements can be tagged with a class and visibility is controlled via a
 * preference being applied or removed.
 */

/**
 * devtools.performance.ui.experimental
 */
menuitem.experimental-option::before {
  content: "";
  background-image: url(chrome://devtools/skin/images/webconsole.svg);
  background-repeat: no-repeat;
  background-size: 72px 60px;
  width: 12px;
  height: 12px;
  display: inline-block;

  background-position: -24px -24px;
  margin: 2px 5px 0 0;
  max-height: 12px;
}
.theme-light menuitem.experimental-option::before {
  background-image: url(chrome://devtools/skin/images/webconsole.svg#light-icons);
}

#performance-options-menupopup:not(.experimental-enabled) .experimental-option,
#performance-options-menupopup:not(.experimental-enabled) .experimental-option::before {
  display: none;
}

/* for call tree */
description.opt-icon {
  margin: 0px 0px 0px 0px;
}
description.opt-icon::before {
  margin: 1px 4px 0px 0px;
}
PK
!<���>�>chrome/devtools/skin/rules.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* CSS Variables specific to this panel that aren't defined by the themes */
:root {
  --rule-highlight-background-color: var(--theme-highlight-yellow);
  --rule-overridden-item-border-color: var(--theme-content-color3);
  --rule-header-background-color: var(--theme-toolbar-background);
  --rule-flex-toggle-color: var(--grey-90);
  --rule-shape-toggle-color: var(--grey-90);
}

:root.theme-dark {
  --rule-highlight-background-color: #521C76;
  --rule-overridden-item-border-color: var(--theme-content-color1);
  --rule-header-background-color: #222225;
  --rule-flex-toggle-color: var(--grey-10);
  --rule-shape-toggle-color: var(--grey-10);
}

:root.theme-firebug {
  --rule-highlight-background-color: var(--theme-highlight-yellow);
  --rule-property-name: darkgreen;
  --rule-property-value: darkblue;
  --rule-overridden-item-border-color: var(--theme-content-color2);
  --rule-header-background-color: var(--theme-header-background);
}

/* Rule View Tabpanel */

#sidebar-panel-ruleview {
  margin: 0;
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100%;
  /* Override the min-width from .inspector-tabpanel, as the rule panel can support small
     widths */
  min-width: 100px;
}

/* Rule View Toolbar */

#ruleview-toolbar-container {
  display: flex;
  flex-direction: column;
  height: auto;
}

#ruleview-toolbar {
  display: flex;
}

#ruleview-toolbar > .devtools-searchbox:first-child {
  padding-inline-start: 0px;
}

#ruleview-command-toolbar {
  display: flex;
}

.ruleview-reveal-panel {
  display: flex;
  overflow: hidden;
}

.ruleview-reveal-panel[hidden] {
  display: none;
}

.ruleview-reveal-panel label {
  -moz-user-select: none;
  flex-grow: 1;
  display: flex;
  align-items: center;
}

/* Class toggle panel */
#ruleview-class-panel:not([hidden]) {
  /* The class panel can contain 0 to N classes, so we can't hardcode a height here like
     we do for the pseudo-class panel. Unfortunately, that means we don't get the height
     transition when toggling the panel */
  flex-direction: column;
}

#ruleview-class-panel .add-class {
  margin: 0;
  border-width: 0 0 1px 0;
  padding: 2px 6px;
  border-radius: 0;
}

#ruleview-class-panel .classes {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
}

#ruleview-class-panel .classes {
  max-height: 100px;
  overflow-y: auto;
}

#ruleview-class-panel .classes label {
  flex: 0 0;
  max-width: 50%;
}

#ruleview-class-panel .classes label span {
  white-space: nowrap;
  text-overflow: ellipsis;
  overflow: hidden;
}

#ruleview-class-panel .no-classes {
  flex: 1;
  color: var(--theme-body-color-inactive);
  margin: 0;
  text-align: center;
}

/* Rule View Container */

#ruleview-container {
  -moz-user-select: text;
  overflow: auto;
  flex: auto;
  height: 100%;
}

/* This extra wrapper only serves as a way to get the content of the view focusable.
   So that when the user reaches it either via keyboard or mouse, we know that the view
   is focused and therefore can handle shortcuts.
   However, for accessibility reasons, tabindex is set to -1 to avoid having to tab
   through it, and the outline is hidden. */
#ruleview-container-focusable {
  height: 100%;
  outline: none;
}

#ruleview-container.non-interactive {
  pointer-events: none;
  visibility: collapse;
  transition: visibility 0.25s;
}

.ruleview-code {
  direction: ltr;
}

.ruleview-property:not(:hover) > .ruleview-enableproperty {
  pointer-events: none;
}

.ruleview-expandable-container {
  display: block;
}

.ruleview-namecontainer {
  cursor: text;
  margin-left: -25px;
}

.ruleview-propertyvaluecontainer {
  cursor: text;
  padding-right: 5px;
}

.ruleview-propertyvaluecontainer a {
  color: var(--theme-highlight-purple);
  cursor: pointer;
}

.ruleview-computedlist,
.ruleview-expandable-container[hidden],
.ruleview-overridden-items[hidden],
.ruleview-overridden-rule-filter[hidden],
.ruleview-warning[hidden],
.ruleview-overridden .ruleview-grid {
  display: none;
}

.ruleview-computedlist[user-open],
.ruleview-computedlist[filter-open],
.ruleview-overridden-items {
  display: block;
}

.ruleview-rule-source {
  text-align: end;
  float: right;
  max-width: 100%;

  /* Force RTL direction to crop the source link at the beginning. */
  direction: rtl;
  overflow: hidden;
  text-overflow: ellipsis;

  -moz-user-select: none;
  margin-bottom: 2px;
}

.ruleview-rule-source-label {
  white-space: nowrap;
  margin: 0;
  cursor: pointer;

  /* Create an LTR embed to avoid special characters being shifted to the start due to the
     parent node direction: rtl; */
  direction: ltr;
  unicode-bidi: embed
}

.ruleview-rule-source[unselectable],
.ruleview-rule-source[unselectable] > .ruleview-rule-source-label {
  cursor: default;
}

.theme-firebug .ruleview-rule-source-label {
  font-family: var(--proportional-font-family);
  font-weight: bold;
  color: #0000FF;
}

.ruleview-rule-source:not([unselectable]):hover {
  text-decoration: underline;
}

.ruleview-header {
  background: var(--rule-header-background-color);
  border-bottom: 1px solid var(--theme-splitter-color);
  color: var(--theme-toolbar-color);
  font-size: 12px;
  padding: 4px;
  width: 100%;
  align-items: center;
  display: flex;

  -moz-user-select: none;
}

.theme-firebug .ruleview-header {
  font-family: var(--proportional-font-family);
  font-weight: bold;
  color: inherit;
  border: none;
  margin: 4px 0;
  padding: 3px 4px 2px 4px;
  line-height: inherit;
  min-height: 0;
}

.ruleview-expandable-header {
  cursor: pointer;
}

.ruleview-expandable-header:hover {
  background-color: var(--theme-toolbar-background-hover);
}

.ruleview-rule-pseudo-element {
  padding-left:20px;
  border-left: solid 10px;
}

.ruleview-rule {
  border-bottom: 1px solid var(--theme-splitter-color);
  padding: 2px 4px;
}

#ruleview-container-focusable > .ruleview-rule:last-child {
  border-bottom: none;
}

/**
 * Display rules that don't match the current selected element and uneditable
 * user agent styles differently
 */
.ruleview-rule[unmatched=true],
.ruleview-rule[uneditable=true] {
  background: var(--theme-tab-toolbar-background);
}

.ruleview-rule[unmatched=true] {
  opacity: 0.5;
}

.ruleview-rule[uneditable=true] :focus {
  outline: none;
}

.ruleview-rule[uneditable=true] .theme-link {
  color: var(--theme-highlight-bluegrey);
}

.ruleview-rule[uneditable=true] .ruleview-enableproperty {
  visibility: hidden;
}

.ruleview-rule[uneditable=true] .ruleview-swatch {
  cursor: default;
}

.ruleview-rule[uneditable=true] .ruleview-namecontainer > .ruleview-propertyname,
.ruleview-rule[uneditable=true] .ruleview-propertyvaluecontainer >
.ruleview-propertyvalue {
  border-bottom-color: transparent;
}

.theme-firebug .ruleview-namecontainer > .ruleview-propertyname,
.theme-firebug .ruleview-propertyvaluecontainer > .ruleview-propertyvalue {
  border-bottom: none;
}

.theme-firebug .ruleview-namecontainer > .ruleview-propertyname {
  color: var(--rule-property-name);
}

.theme-firebug .ruleview-propertyvaluecontainer > .ruleview-propertyvalue {
  color: var(--rule-property-value);
}

.theme-firebug .ruleview-overridden .ruleview-propertyname,
.theme-firebug .ruleview-overridden .ruleview-propertyvalue {
  text-decoration: line-through;
}

.theme-firebug .ruleview-enableproperty:not([checked]) ~ .ruleview-namecontainer,
.theme-firebug .ruleview-enableproperty:not([checked]) ~ .ruleview-namecontainer *,
.theme-firebug .ruleview-enableproperty:not([checked]) ~ .ruleview-propertyvaluecontainer,
.theme-firebug .ruleview-enableproperty:not([checked]) ~ .ruleview-propertyvaluecontainer *,
.theme-firebug .ruleview-overridden > * > .ruleview-computed:not(.ruleview-overridden),
.theme-firebug .ruleview-overridden > * > .ruleview-computed:not(.ruleview-overridden) * {
  color: #CCCCCC;
}

.theme-firebug .ruleview-rule + .ruleview-rule {
  border-bottom: none;
}

.ruleview-warning {
  background-image: url(images/alerticon-warning.png);
  background-size: 13px 12px;
  margin-inline-start: 5px;
  display: inline-block;
  width: 13px;
  height: 12px;
}

@media (min-resolution: 1.1dppx) {
  .ruleview-warning {
    background-image: url(images/[email protected]);
  }
}

.ruleview-overridden-rule-filter {
  background-image: url(chrome://devtools/skin/images/filter.svg#filterinput);
  background-size: 11px 11px;
  margin-inline-start: 5px;
  display: inline-block;
  width: 11px;
  height: 11px;
}

.ruleview-ruleopen {
  padding-inline-end: 5px;
}

.ruleview-ruleclose {
  clear: both;
  cursor: text;
  padding-right: 20px;
}

.ruleview-propertylist {
  list-style: none;
  padding: 0;
  margin: 0;
}

.ruleview-enableproperty {
  position: relative;
  float: left;
  left: -28px;
}

.ruleview-rule:not(:hover) .ruleview-enableproperty {
  visibility: hidden;
}

.ruleview-expander {
  vertical-align: middle;
}

.ruleview-propertycontainer .ruleview-expander {
  margin-left: -6px;
}

.ruleview-rule .ruleview-expander.theme-twisty:dir(rtl) {
  /* for preventing .theme-twisty's wrong direction in rtl; Bug 1296648 */
  transform: none;
}

.ruleview-newproperty {
  margin-inline-start: -10px;
}

.ruleview-namecontainer,
.ruleview-propertyvaluecontainer,
.ruleview-propertyname,
.ruleview-propertyvalue {
  text-decoration: inherit;
}

.ruleview-computedlist {
  list-style: none;
  padding: 0;
}

.ruleview-overridden-items {
  margin-inline-start: -25px;
  list-style: none;
  line-height: 1.5em;
}

.ruleview-overridden-item {
  position: relative;
}

.ruleview-overridden-item::before {
  position: absolute;
  left: -15px;
  top: 0px;
  content: '';
  display: block;
  border-left: 0.5px solid var(--rule-overridden-item-border-color);
  height: 0.8em;
  border-bottom: 0.5px solid var(--rule-overridden-item-border-color);
  width: 10px;
}

.ruleview-overridden-item::after {
  position: absolute;
  left: -15px;
  bottom: -7px;
  content: '';
  display: block;
  border-left: 0.5px solid var(--rule-overridden-item-border-color);
  height: 100%;
}

.ruleview-overridden-item:last-child:after {
  display: none;
}

.ruleview-flex,
.ruleview-grid,
.ruleview-swatch {
  cursor: pointer;
  border-radius: 50%;
  width: 1em;
  height: 1em;
  vertical-align: middle;
  /* align the swatch with its value */
  margin-top: -1px;
  margin-inline-end: 5px;
  display: inline-block;
  position: relative;
}

.ruleview-flex {
  background: url("chrome://devtools/skin/images/command-frames.svg");
  -moz-context-properties: fill;
  fill: var(--rule-flex-toggle-color);
  border-radius: 0;
  background-size: 1em;
}

.ruleview-grid {
  background: url("chrome://devtools/skin/images/grid.svg");
  border-radius: 0;
}

.ruleview-shape-point.active {
  background-color: var(--rule-highlight-background-color);
}

.ruleview-colorswatch::before {
  content: '';
  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;
  position: absolute;
  border-radius: 50%;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: -1;
}

.ruleview-bezierswatch {
  background: url("chrome://devtools/skin/images/cubic-bezier-swatch.png");
  background-size: 1em;
}

.ruleview-filterswatch {
  background: url("chrome://devtools/skin/images/filter-swatch.svg");
  background-size: 1em;
}

.ruleview-angleswatch {
  background: url("chrome://devtools/skin/images/angle-swatch.svg");
  background-size: 1em;
}

.ruleview-shapeswatch {
  background: url("chrome://devtools/skin/images/tool-shadereditor.svg");
  -moz-context-properties: fill;
  fill: var(--rule-shape-toggle-color);
  border-radius: 0;
  background-size: 1em;
  box-shadow: none;
}

@media (min-resolution: 1.1dppx) {
  .ruleview-bezierswatch {
    background: url("chrome://devtools/skin/images/[email protected]");
    background-size: 1em;
  }
}

.ruleview-overridden {
  text-decoration: line-through;
}

/**
 * Hide swatches (tool icons) from properties that are overwritten by higher specificity * rules.
 * .ruleview-swatch is a base class for many tool swatches (shapes, color, bezier curves)
 * .ruleview-flex and .ruleview-grid are custom
 */
.ruleview-overridden .ruleview-flex,
.ruleview-overridden .ruleview-grid,
.ruleview-overridden .ruleview-swatch {
  display: none;
}

.theme-light .ruleview-overridden {
  text-decoration-color: var(--theme-content-color3);
}

.ruleview-font-family.used-font {
  text-decoration: underline;
}

.styleinspector-propertyeditor {
  border: 1px solid #CCC;
  padding: 0;
  margin: -1px -3px -1px -1px;
}

.theme-firebug .styleinspector-propertyeditor {
  border: 1px solid var(--theme-splitter-color);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.5);
}

.ruleview-property {
  border-left: 3px solid transparent;
  clear: both;
  padding-left: 28px;
}

.ruleview-propertycontainer  > * {
  vertical-align: middle;
}

.ruleview-property[dirty] {
  border-left-color: var(--theme-highlight-green);
}

.ruleview-highlight > .ruleview-namecontainer,
.ruleview-highlight > .ruleview-propertyvaluecontainer {
  background-color: var(--rule-highlight-background-color);
}

.ruleview-namecontainer > .ruleview-propertyname,
.ruleview-propertyvaluecontainer > .ruleview-propertyvalue {
  border-bottom: 1px dashed transparent;
}

.ruleview-namecontainer:hover > .ruleview-propertyname,
.ruleview-propertyvaluecontainer:hover > .ruleview-propertyvalue {
  border-bottom-color: hsl(0,0%,50%);
}

.ruleview-selectorcontainer {
  word-wrap: break-word;
  cursor: text;
}

.ruleview-selector-separator,
.ruleview-selector-unmatched,
.ruleview-unmatched-variable {
  color: #888;
}

.ruleview-selector-matched > .ruleview-selector-attribute {
  /* TODO: Bug 1178535 Awaiting UX feedback on highlight colors */
}

.ruleview-selector-matched > .ruleview-selector-pseudo-class {
  /* TODO: Bug 1178535 Awaiting UX feedback on highlight colors */
}

.ruleview-selector-matched > .ruleview-selector-pseudo-class-lock {
  font-weight: bold;
  color: var(--theme-highlight-orange);
}

.theme-firebug .ruleview-selector > .ruleview-selector-matched,
.theme-firebug .ruleview-selector > .ruleview-selector-separator,
.theme-firebug .ruleview-selector > .ruleview-selector-unmatched {
  color: inherit;
}

.ruleview-selectorhighlighter {
  background: url("chrome://devtools/skin/images/vview-open-inspector.png") no-repeat 0 0;
  padding-left: 16px;
  margin-left: 5px;
  cursor: pointer;
}

.ruleview-selectorhighlighter:hover {
  filter: var(--theme-icon-checked-filter);
}

.ruleview-flex.active,
.ruleview-grid.active,
.ruleview-selectorhighlighter:active,
.ruleview-selectorhighlighter.highlighted,
.ruleview-shapeswatch.active {
  filter: var(--theme-icon-checked-filter) brightness(0.9);
}

#ruleview-add-rule-button::before {
  background-image: url("chrome://devtools/skin/images/add.svg");
}

#pseudo-class-panel-toggle::before {
  background-image: url("chrome://devtools/skin/images/pseudo-class.svg");
}

#class-panel-toggle::before {
  content: ".cls";
}

.ruleview-overridden-rule-filter {
  opacity: 0.8;
}
.ruleview-overridden-rule-filter:hover {
  opacity: 1;
}

.theme-firebug .ruleview-overridden {
  text-decoration: none;
}

/* Firebug theme disable/enable CSS rule. Firebug theme uses its own
  icons to indicate when CSS rules can be disabled or enabled. */

.theme-firebug .ruleview-rule .theme-checkbox {
  background-repeat: no-repeat;
  background-size: 12px 12px;
  background-image: url(chrome://devtools/skin/images/firebug/disable.svg);
  background-position: 0 0;
}

.theme-firebug .ruleview-rule .theme-checkbox:not([checked]){
  filter: grayscale(1);
}

.theme-firebug .ruleview-rule .theme-checkbox[checked] {
  background-position: 0 0;
}

.theme-firebug .ruleview-property:not(:hover) .ruleview-enableproperty {
  visibility: hidden;
}
PK
!<K�	++#chrome/devtools/skin/scratchpad.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#scratchpad-sidebar > tabs {
  height: 0;
  border: none;
}

#sp-toolbar {
  border: none;
}
PK
!<ev��		%chrome/devtools/skin/shadereditor.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

/* Reload and waiting notices */

.notice-container {
  margin-top: -50vh;
  color: var(--theme-body-color-alt);
}

#reload-notice {
  font-size: 120%;
}

#waiting-notice {
  font-size: 110%;
}

/* Shaders pane */

#shaders-pane {
  min-width: 150px;
}

.program-item {
  padding: 2px 0px;
}

.side-menu-widget-item-checkbox {
  -moz-appearance: none;
  opacity: 0;
  transition: opacity .15s ease-out 0s;
}

/* Only show the checkbox when the source is hovered over, is selected, or if it
 * is not checked. */
.side-menu-widget-item:hover > .side-menu-widget-item-checkbox,
.side-menu-widget-item.selected > .side-menu-widget-item-checkbox,
.side-menu-widget-item-checkbox:not([checked]) {
  opacity: 1;
  transition: opacity .15s ease-out 0s;
}

.side-menu-widget-item-checkbox .checkbox-check {
  -moz-appearance: none;
  background-image: url(images/item-toggle.svg);
  background-color: transparent;
  width: 16px;
  height: 16px;
  border: 0;
  -moz-context-properties: fill;
}

.side-menu-widget-item-checkbox:not([checked]) .checkbox-check,
.side-menu-widget-item-checkbox:not([checked]) + vbox {
  opacity: 0.3;
}

.side-menu-widget-item:not(.selected) .checkbox-check {
  fill: var(--theme-toolbar-color);
}

/* Make sure icon is white when the item is selected */
.side-menu-widget-item.selected .checkbox-check {
  fill: var(--theme-toolbar-selected-color);
}

/* Shader source editors */

.editor-label {
  padding: 1px 12px;
  border-top: 1px solid;
}

.editor-label {
  background: var(--theme-toolbar-background);
  border-color: var(--theme-splitter-color);
  color: var(--theme-body-color-alt);
}

.editor-label[selected] {
  background-color: var(--theme-selection-background);
  color: var(--theme-selection-color);
}

/* Responsive sidebar */

@media (max-width: 700px) {
  #shaders-pane {
    max-height: 60vh;
  }

  #editors-splitter {
    border-color: transparent;
  }

  .side-menu-widget-container {
    box-shadow: none !important;
  }

  .side-menu-widget-item-arrow {
    background-image: none !important;
  }

  .editor-label {
    -moz-box-ordinal-group: 0;
    border-bottom: 1px solid;
  }
}
PK
!<؁9�cc"chrome/devtools/skin/splitview.css/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 {
  --sidemenu-selected-arrow: url(images/item-arrow-dark-ltr.svg);
  --sidemenu-selected-arrow-rtl: url(images/item-arrow-dark-rtl.svg);
}
.theme-light {
  --sidemenu-selected-arrow: url(images/item-arrow-ltr.svg);
  --sidemenu-selected-arrow-rtl: url(images/item-arrow-rtl.svg);
}

/* Loading animation */
@keyframes item-load {
  0% {
    transform: scaleY(0) translateY(-100%);
  }
  100% {
    transform: none;
  }
}

.splitview-nav {
  list-style: none;
  padding: 0;
  margin: 0;
  background-color: var(--theme-sidebar-background);
}

.splitview-nav > li {
  animation: item-load .5s var(--animation-curve);
  padding-inline-end: 8px;
  -moz-box-align: center;
  outline: 0;
  vertical-align: bottom;
  border-bottom: 1px solid rgba(128,128,128,0.15);
  background: inherit;
}

.placeholder {
  -moz-box-flex: 1;
  text-align: center;
}

.splitview-nav > li.splitview-active {
  background-color: var(--theme-selection-background);
  color: var(--theme-selection-color);
  background-image: var(--sidemenu-selected-arrow);
  background-repeat: no-repeat;
  background-position: center right;
}

.splitview-nav > li.splitview-active:-moz-locale-dir(rtl) {
  background-image: var(--sidemenu-selected-arrow-rtl);
  background-position: center left;
}

/* Toolbars */

.splitview-main > .devtools-toolbar {
  background-origin: border-box;
  background-clip: border-box;
}

.splitview-main > toolbar,
.loading .splitview-nav-container {
  border-inline-end: 1px solid var(--theme-splitter-color);
}

.splitview-main > .devtools-toolbarbutton {
  font-size: 11px;
  padding: 0 8px;
  width: auto;
  min-width: 48px;
  min-height: 0;
}
PK
!<P���� chrome/devtools/skin/storage.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* Storage Host Tree */

#storage-tree {
  min-width: 220px;
  max-width: 500px;
  overflow: auto;
}

#storage-tree {
  background: var(--theme-sidebar-background);
}

#storage-tree .tree-widget-item[type="store"]:after {
  background-image: url(chrome://devtools/skin/images/tool-storage.svg);
  background-size: 18px 18px;
  background-position: -1px 0;
}

/* Columns with date should have a min width so that date is visible */
#expires, #lastAccessed, #creationTime {
  min-width: 150px;
}

/* Variables View Sidebar */

#storage-sidebar {
  max-width: 500px;
  min-width: 250px;
}

#storage-toolbar .add-button::before {
  background-image: url("chrome://devtools/skin/images/add.svg");
  -moz-user-focus: normal;
}

#storage-toolbar .refresh-button::before {
  background-image: url("chrome://devtools/skin/images/reload.svg");
  -moz-user-focus: normal;
}

#storage-toolbar .devtools-button {
  min-width: 0;
}

#storage-toolbar .devtools-button hbox,
#storage-toolbar .sidebar-toggle[hidden] {
  display: none;
}

/* Responsive sidebar */
@media (max-width: 700px) {
  #storage-tree,
  #storage-sidebar {
    max-width: 100%;
  }

  #storage-table #path {
    display: none;
  }

  #storage-table .table-widget-cell {
    min-width: 100px;
  }
}
PK
!<n�[L��$chrome/devtools/skin/styleeditor.css/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#style-editor-chrome {
  -moz-box-flex: 1;
}

.splitview-nav > li,
.stylesheet-info,
.stylesheet-more,
.stylesheet-rule-count,
li.splitview-active > hgroup > .stylesheet-more > h3 > .stylesheet-saveButton,
li:hover > hgroup > .stylesheet-more > h3 > .stylesheet-saveButton {
  display: -moz-box;
}

.devtools-toolbar > spacer {
  -moz-box-flex: 1;
}

.style-editor-newButton {
  list-style-image: url(images/add.svg);
}

.style-editor-importButton {
  list-style-image: url(images/import.svg);
}

.stylesheet-details-container {
  -moz-box-flex: 1;
}

.stylesheet-media-container {
  overflow-y: auto;
}

.stylesheet-error-message {
  display: none;
}

li.error > .stylesheet-info > .stylesheet-more  > .stylesheet-error-message {
  display: block;
}

.stylesheet-title,
.stylesheet-name {
  text-decoration: none;
}

.stylesheet-name {
  font-size: 13px;
  white-space: nowrap;
}

.stylesheet-name > label {
  display: inline;
  cursor: pointer;
}

.stylesheet-info > h1 {
  -moz-box-flex: 1;
}

.splitview-nav > li > hgroup.stylesheet-info {
  -moz-box-pack: center;
}

.stylesheet-more > spacer {
  -moz-box-flex: 1;
}

.stylesheet-title,
.stylesheet-name,
.stylesheet-rule-count,
.stylesheet-linked-file,
.stylesheet-saveButton {
  color: var(--theme-body-color);
}

.stylesheet-saveButton {
  display: none;
  margin-top: 0px;
  margin-bottom: 0px;
  text-decoration: underline;
  cursor: pointer;
}

.splitview-active .stylesheet-title,
.splitview-active .stylesheet-name,
.theme-light .splitview-active .stylesheet-rule-count,
.theme-light .splitview-active .stylesheet-linked-file,
.theme-light .splitview-active .stylesheet-saveButton {
  color: var(--theme-selection-color);
}

.splitview-nav:focus {
  outline: 0; /* focus ring is on the stylesheet name */
}

.splitview-nav > li {
  -moz-box-orient: horizontal;
}

.splitview-nav > li > hgroup {
  display: -moz-box;
  -moz-box-orient: vertical;
  -moz-box-flex: 1;
}

.splitview-nav > li.unsaved > hgroup .stylesheet-name {
  font-style: italic;
}

.splitview-nav:-moz-locale-dir(ltr) > li.unsaved > hgroup .stylesheet-name:before,
.splitview-nav:-moz-locale-dir(rtl) > li.unsaved > hgroup .stylesheet-name:after {
  font-style: italic;
}

.splitview-nav.empty > p {
  padding: 0 10px;
}

.stylesheet-sidebar {
  max-width: 400px;
  min-width: 100px;
  border-color: var(--theme-splitter-color);
}

.theme-light .media-rule-label {
  border-bottom-color: #cddae5; /* Grey */
}

.theme-dark .media-rule-label {
  border-bottom-color: #303b47; /* Grey */
}

.media-rule-label {
  display: flex;
  padding: 4px;
  cursor: pointer;
  border-bottom: 1px solid;
}

.media-responsive-mode-toggle {
  color: var(--theme-highlight-blue);
  text-decoration: underline;
}

.media-rule-line {
  padding-inline-start: 4px;
}

.media-condition-unmatched {
  opacity: 0.4;
}

.media-rule-condition {
  flex: 1;
  overflow: hidden;
}

.stylesheet-enabled {
  display: -moz-box;
  cursor: pointer;
  padding: 8px 0;
  margin: 0 8px;
  background-image: url(images/item-toggle.svg);
  background-repeat: no-repeat;
  background-clip: content-box;
  background-position: center;
  background-size: 16px;
  width: 24px;
  height: 40px;

  /* The icon color should always match the text color. */
  -moz-context-properties: fill;
  fill: currentColor;
}

.disabled > .stylesheet-enabled {
  opacity: 0.3;
}

.splitview-nav > li > .stylesheet-enabled:focus,
.splitview-nav > li:hover > .stylesheet-enabled {
  outline: 0;
}

.stylesheet-linked-file:not(:empty){
  margin-inline-end: 0.4em;
}

.stylesheet-linked-file:not(:empty):before {
  margin-inline-start: 0.4em;
  content: " ↳ ";
}

li.unsaved > hgroup > h1 > .stylesheet-name:before {
  content: "*";
}

li.linked-file-error .stylesheet-linked-file {
  text-decoration: line-through;
}

li.linked-file-error .stylesheet-linked-file:after {
  font-size: 110%;
  content: " ✘";
}

li.linked-file-error .stylesheet-rule-count {
  visibility: hidden;
}

.stylesheet-more > h3 {
  font-size: 11px;
  margin-inline-end: 2px;
}

.devtools-searchinput,
.devtools-filterinput {
  max-width: 25ex;
  font-size: 11px;
}

.placeholder a {
  text-decoration: underline;
}

h1,
h2,
h3 {
  font-size: inherit;
  font-weight: normal;
  margin: 0;
  padding: 0;
}

@media (max-width: 700px) {
  .stylesheet-sidebar {
    width: 150px;
  }
}

/* portrait mode */
@media (max-width: 550px) {
  li.splitview-active > hgroup > .stylesheet-more > .stylesheet-rule-count,
  li:hover > hgroup > .stylesheet-more > .stylesheet-rule-count {
    display: none;
  }

  .splitview-nav {
    box-shadow: none;
  }

  .splitview-nav > li.splitview-active {
    background-size: 0 0, 0 0, auto;
  }

  .stylesheet-enabled {
    padding: 0;
    background-position: 0 0;
    height: 24px;
  }

  .disabled > .stylesheet-enabled {
    background-position: -24px 0;
  }

  .splitview-nav > li > hgroup.stylesheet-info {
    -moz-box-align: baseline;
    -moz-box-orient: horizontal;
    -moz-box-flex: 1;
  }

  .stylesheet-sidebar {
    width: 180px;
  }

  .stylesheet-more {
    -moz-box-flex: 1;
    -moz-box-pack: end;
  }

  .stylesheet-more > spacer {
    -moz-box-flex: 0;
  }
}

/* CSS coverage */
.csscoverage-report {
  background-color: var(--theme-toolbar-background);
  -moz-box-orient: horizontal;
}

.csscoverage-report-container {
  height: 100vh;
  padding: 0 10px;
  overflow-x: hidden;
  overflow-y: auto;
  -moz-box-flex: 1;
}

.csscoverage-report-content {
  margin: 20px auto;
  -moz-column-width: 300px;
  font-size: 13px;
  -moz-user-select: text;
}

.csscoverage-report-summary,
.csscoverage-report-unused,
.csscoverage-report-optimize {
  display: inline-block;
}

.csscoverage-report-unused,
.csscoverage-report-optimize {
  flex: 1;
  min-width: 0;
}

@media (max-width: 950px) {
  .csscoverage-report-content {
    display: block;
  }

  .csscoverage-report-summary {
    display: block;
    text-align: center;
  }
}

.csscoverage-report h1 {
  font-size: 120%;
}

.csscoverage-report h2 {
  font-size: 110%;
}

.csscoverage-report h1,
.csscoverage-report h2,
.csscoverage-report h3 {
  font-weight: bold;
  margin: 10px 0;
}

.csscoverage-report code,
.csscoverage-report textarea {
  font-family: var(--monospace-font-family);
  font-size: inherit;
}

.csscoverage-list:after {
  content: ', ';
}

.csscoverage-list:last-child:after {
  display: none;
}

.csscoverage-report textarea {
  width: 100%;
  height: 100px;
}

.csscoverage-report a {
  cursor: pointer;
  text-decoration: underline;
}

.csscoverage-report > .csscoverage-toolbar {
  border: none;
  margin: 0;
  padding: 0;
}

.csscoverage-report > .csscoverage-toolbarbutton {
  min-width: 4em;
  min-height: 100vh;
  margin: 0;
  padding: 0;
  border-radius: 0;
  border-top: none;
  border-bottom: none;
  border-inline-start: none;
}

.csscoverage-report .pie-table-chart-container {
  -moz-box-orient: vertical;
  text-align: start;
}

.chart-colored-blob[name="Used Preload"] {
  fill: var(--theme-highlight-pink);
  background: var(--theme-highlight-pink);
}

.chart-colored-blob[name=Used] {
  fill: var(--theme-highlight-green);
  background: var(--theme-highlight-green);
}

.chart-colored-blob[name=Unused] {
  fill: var(--theme-highlight-lightorange);
  background: var(--theme-highlight-lightorange);
}

/* Undo 'largest' customization */
.theme-dark .pie-chart-slice[largest] {
  stroke-width: 1px;
  stroke: rgba(0,0,0,0.2);
}

.theme-light .pie-chart-slice[largest] {
  stroke-width: 1px;
  stroke: rgba(255,255,255,0.8);
}

.csscoverage-report .pie-chart-slice {
  cursor: default;
}

.csscoverage-report-chart {
  margin: 0 20px;
}
PK
!<+%����!chrome/devtools/skin/toolbars.css/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* CSS Variables specific to the devtools toolbar that aren't defined by the themes */
.theme-light {
  --searchbox-background-color: var(--theme-highlight-yellow);
  --searchbox-border-color: #ffbf00;
  --searcbox-no-match-background-color: #ffe5e5;
  --searcbox-no-match-border-color: #e52e2e;

  --magnifying-glass-image: url(chrome://devtools/skin/images/search.svg);
  --filter-image: url(chrome://devtools/skin/images/filter.svg);
  --tool-options-image: url(chrome://devtools/skin/images/tool-options.svg);

  --separator-border-image: linear-gradient(transparent 4px, rgba(0,0,0,.1) 4px, rgba(0,0,0,.1) calc(100% - 4px), transparent calc(100% - 4px));
}

.theme-dark {
  --searchbox-background-color: #4d4222;
  --searchbox-border-color: #d99f2b;
  --searcbox-no-match-background-color: #402325;
  --searcbox-no-match-border-color: #cc3d3d;

  --magnifying-glass-image: url(chrome://devtools/skin/images/search.svg);
  --filter-image: url(chrome://devtools/skin/images/filter.svg);
  --tool-options-image: url(chrome://devtools/skin/images/tool-options.svg);

  --separator-border-image: linear-gradient(transparent 4px, rgba(100%,100%,100%,.2) 4px, rgba(100%,100%,100%,.2) calc(100% - 4px), transparent calc(100% - 4px));
}

.theme-firebug {
  --magnifying-glass-image: url(chrome://devtools/skin/images/search.svg);
  --tool-options-image: url(chrome://devtools/skin/images/firebug/tool-options.svg);
}


/* Toolbars */
.devtools-toolbar,
.devtools-sidebar-tabs tabs {
  -moz-appearance: none;
  padding: 0;
  border-width: 0;
  border-bottom-width: 1px;
  border-style: solid;
  height: 24px;
  line-height: 24px;
  box-sizing: border-box;
}

.devtools-toolbar {
  padding: 0 3px;
}

.devtools-toolbar checkbox {
  margin: 0 2px;
  padding: 0;
  line-height: -moz-block-height;
}

.devtools-toolbar checkbox .checkbox-check {
  margin: 0;
  padding: 0;
  vertical-align: bottom;
}

.devtools-toolbar checkbox .checkbox-label-box {
  border: none !important; /* overrides .checkbox-label-box from checkbox.css */
}

.devtools-toolbar checkbox .checkbox-label-box .checkbox-label {
  margin: 0 6px !important; /* overrides .checkbox-label from checkbox.css */
  padding: 0;
}

.devtools-toolbar-bottom {
  border-top-width: 1px;
  border-bottom: none;
}

.devtools-separator {
  border-inline-start: 1px solid;
  border-image: var(--separator-border-image) 1 1;
  height: inherit;
}

/* In-tools sidebar */
.devtools-sidebar-tabs {
  -moz-appearance: none;
  margin: 0;
  height: 100%;
}

.devtools-sidebar-tabs > tabpanels {
  -moz-appearance: none;
  background: transparent;
  padding: 0;
  border: 0;
}

.theme-light .devtools-sidebar-tabs > tabpanels {
  background: var(--theme-sidebar-background);
  color: var(--theme-body-color);
}

.devtools-sidebar-tabs tabs {
  position: static;
  font: inherit;
  margin-bottom: 0;
  overflow: hidden;
}

.devtools-sidebar-alltabs {
  -moz-appearance: none;
  height: 24px;
  line-height: 24px;
  padding: 0 4px;
  margin: 0;
  border-width: 0 0 1px 0;
  border-inline-start-width: 1px;
  border-style: solid;
}

.devtools-sidebar-alltabs .toolbarbutton-icon {
  display: none;
}

.devtools-sidebar-tabs tabs > .tabs-right,
.devtools-sidebar-tabs tabs > .tabs-left {
  display: none;
}

.devtools-sidebar-tabs tabs > tab {
  -moz-appearance: none;
  /* We want to match the height of a toolbar with a toolbarbutton
   * First, we need to replicated the padding of toolbar (4px),
   * then we need to take the border of the buttons into account (1px).
   */
  padding: 0 3px;
  margin: 0;
  min-width: 78px;
  text-align: center;
  background-color: transparent;
  color: inherit;
  -moz-box-flex: 1;
  border-width: 0;
  border-inline-start-width: 1px;
  border-style: solid;
  border-radius: 0;
  position: static;
  text-shadow: none;
}

.devtools-sidebar-tabs tabs > tab {
  border-image: linear-gradient(transparent 15%, var(--theme-splitter-color) 15%, var(--theme-splitter-color) 85%, transparent 85%) 1 1;
}

.devtools-sidebar-tabs tabs > tab[selected],
.devtools-sidebar-tabs tabs > tab[selected] + tab {
  border-image: linear-gradient(var(--theme-splitter-color), var(--theme-splitter-color)) 1 1;
}

.devtools-sidebar-tabs tabs > tab:first-child {
  border-inline-start-width: 0;
}

.devtools-sidebar-tabs tabs > tab:hover {
  background: rgba(0, 0, 0, 0.12);
}

.devtools-sidebar-tabs tabs > tab:hover:active {
  background: rgba(0, 0, 0, 0.2);
}

.devtools-sidebar-tabs tabs > tab[selected],
.devtools-sidebar-tabs tabs > tab[selected]:hover:active {
  color: var(--theme-selection-color);
  background: var(--theme-selection-background);
}

/*
 * Apply the default fill color to toolbar icons
 */
.devtools-toolbarbutton > image,
.devtools-button::before,
.scrollbutton-up > .toolbarbutton-icon,
.scrollbutton-down > .toolbarbutton-icon,
#black-boxed-message-button .button-icon,
#canvas-debugging-empty-notice-button .button-icon,
#toggle-breakpoints[checked] > image,
.event-tooltip-debugger-icon {
  -moz-context-properties: fill;
  fill: var(--theme-toolbar-color);
}

.hidden-labels-box:not(.visible) > label,
.hidden-labels-box.visible ~ .hidden-labels-box > label:last-child {
  display: none;
}

.devtools-invisible-splitter {
  border-color: transparent;
  background-color: transparent;
}

.devtools-horizontal-splitter,
.devtools-side-splitter {
  background-color: var(--theme-splitter-color);
}
PK
!<;��O�#�# chrome/devtools/skin/toolbox.css/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 {
  --close-button-image: url(chrome://devtools/skin/images/close.svg);
  --dock-bottom-image: url(chrome://devtools/skin/images/dock-bottom.svg);
  --dock-side-image: url(chrome://devtools/skin/images/dock-side.svg);
  --dock-undock-image: url(chrome://devtools/skin/images/dock-undock.svg);

  --command-paintflashing-image: url(images/command-paintflashing.svg);
  --command-screenshot-image: url(images/command-screenshot.svg);
  --command-responsive-image: url(images/command-responsivemode.svg);
  --command-scratchpad-image: url(images/tool-scratchpad.svg);
  --command-pick-image: url(images/command-pick.svg);
  --command-frames-image: url(images/command-frames.svg);
  --command-splitconsole-image: url(images/command-console.svg);
  --command-noautohide-image: url(images/command-noautohide.svg);
  --command-rulers-image: url(images/command-rulers.svg);
  --command-measure-image: url(images/command-measure.svg);
}

.theme-firebug {
  --close-button-image: url(chrome://devtools/skin/images/firebug/close.svg);
  --dock-bottom-image: url(chrome://devtools/skin/images/firebug/dock-bottom.svg);
  --dock-side-image: url(chrome://devtools/skin/images/firebug/dock-side.svg);
  --dock-undock-image: url(chrome://devtools/skin/images/firebug/dock-undock.svg);

  --command-paintflashing-image: url(images/firebug/command-paintflashing.svg);
  --command-screenshot-image: url(images/firebug/command-screenshot.svg);
  --command-responsive-image: url(images/firebug/command-responsivemode.svg);
  --command-scratchpad-image: url(images/firebug/command-scratchpad.svg);
  --command-pick-image: url(images/firebug/command-pick.svg);
  --command-frames-image: url(images/firebug/command-frames.svg);
  --command-splitconsole-image: url(images/firebug/command-console.svg);
  --command-noautohide-image: url(images/firebug/command-noautohide.svg);
  --command-rulers-image: url(images/firebug/command-rulers.svg);
  --command-measure-image: url(images/firebug/command-measure.svg);
}

/* Toolbox tabbar */

.devtools-tabbar {
  -moz-appearance: none;
  display: flex;
  background: var(--theme-tab-toolbar-background);
  border-bottom: 1px solid var(--theme-splitter-color);
  box-sizing: border-box;
  min-height: 29px;
}

.toolbox-tabs-wrapper {
  position: relative;
  display: flex;
  flex: 1;
}

.toolbox-tabs-wrapper .all-tools-menu {
  border-inline-end: 1px solid var(--theme-splitter-color);
  border-top-width: 0;
  border-bottom-width: 0;
}

.toolbox-tabs {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  margin: 0;
  flex: 1;
  overflow: hidden;
}

/* Set flex attribute to Toolbox buttons and Picker container so,
   they don't overlap with the tab bar */
#toolbox-buttons-start,
#toolbox-buttons-end,
#toolbox-option-container,
#toolbox-controls,
#toolbox-dock-buttons {
  display: flex;
  align-items: stretch;
}

/* Toolbox tabs */

.devtools-tab {
  position: relative;
  display: flex;
  align-items: center;
  min-width: 32px;
  min-height: 24px;
  margin: 0;
  padding: 0;
  border: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  background-color: transparent;
}

.devtools-tab-label {
  font-size: 12px;
  mask-image: linear-gradient(to left, transparent 0, black 6px);
  /* Set the end padding on the label to make sure the label gets faded out properly */
  padding-inline-end: 10px;
  min-width: 1px;
}

.devtools-tab-label:-moz-locale-dir(rtl) {
  mask-image: linear-gradient(to right, transparent 0, black 6px);
}

/* Hide tab icons when the viewport width is limited */
@media (max-width: 700px) {
  .devtools-tab-label {
    /* Set the end padding on the label to make sure the label gets faded out properly */
    padding-inline-end: 5px;
  }

  .devtools-tab:not(.devtools-tab-icon-only) {
    padding-inline-start: 5px !important;
  }

  /* Hide the icons */
  .devtools-tab:not(.devtools-tab-icon-only) > img {
    display: none;
  }
}

.devtools-tab-icon-only {
  min-width: 24px;
}

/* Save space on the tab-strip in Firebug theme */
.theme-firebug .devtools-tab {
  -moz-box-flex: initial;
}

.devtools-tab {
  color: var(--theme-toolbar-color);
}

.devtools-tab:hover {
  background-color: var(--theme-toolbar-hover);
}

.devtools-tab:hover:active {
  background-color: var(--theme-toolbar-hover-active);
}

.devtools-tab.selected {
  color: var(--theme-toolbar-selected-color);
}

/* Display execution pointer in the Debugger tab to indicate
   that the debugger is paused. */
.theme-firebug #toolbox-tab-jsdebugger.devtools-tab:not(.selected).highlighted {
  background-color: rgba(89, 178, 234, .2);
  background-image: url(chrome://devtools/skin/images/firebug/tool-debugger-paused.svg);
  background-repeat: no-repeat;
  padding-left: 13px !important;
  background-position: 3px 6px;
}

.devtools-tab > img {
  border: none;
  margin: 0;
  margin-inline-start: 10px;
  margin-inline-end: 5px;
  max-height: 16px;
  width: 16px; /* Prevents collapse during theme switching */
  vertical-align: text-top;
  flex-shrink: 0;
}

.devtools-tab > label {
  white-space: nowrap;
  margin: 0 4px;
}

.devtools-tab > img {
  -moz-context-properties: fill;
  fill: var(--theme-toolbar-color);
}

.devtools-tab.selected > img {
  fill: var(--theme-toolbar-selected-color);
}

.devtools-tab.highlighted > img {
  fill: var(--theme-toolbar-highlighted-color);
}

/* The options tab is special - it doesn't have the same parent
   as the other tabs (toolbox-option-container vs toolbox-tabs) */
#toolbox-option-container .devtools-tab {
  border-color: transparent;
  border-width: 0;
  padding-inline-start: 1px;
}
#toolbox-option-container img {
  margin: 0 3px;
}

/* Toolbox controls */

/* Save space in Firebug theme */
.theme-firebug #toolbox-controls button {
  margin-inline-start: 0 !important;
  min-width: 12px;
  margin: 0 1px;
}

#toolbox-close::before {
  background-image: var(--close-button-image);
}

#toolbox-dock-bottom::before {
  background-image: var(--dock-bottom-image);
}

#toolbox-dock-side::before {
  background-image: var(--dock-side-image);
}

#toolbox-dock-window::before {
  background-image: var(--dock-undock-image);
}

#toolbox-dock-bottom-minimize::before {
  background-image: url("chrome://devtools/skin/images/[email protected]");
}

#toolbox-dock-bottom-minimize.minimized::before {
  background-image: url("chrome://devtools/skin/images/[email protected]");
}

/* Command buttons */

.command-button,
#toolbox-controls > button,
#toolbox-dock-buttons > button {
  /* !important is needed to override .devtools-button rules in common.css */
  padding: 0 !important;
  margin: 0 !important;
  border: none !important;
  border-radius: 0 !important;
  position: relative;
  min-width: 24px;
}

#command-button-pick {
  min-width: 32px;
}

/* Command button images */

#command-button-paintflashing::before {
  background-image: var(--command-paintflashing-image);
}

#command-button-screenshot::before {
  background-image: var(--command-screenshot-image);
}

#command-button-responsive::before {
  background-image: var(--command-responsive-image);
}

#command-button-scratchpad::before {
  background-image: var(--command-scratchpad-image);
}

#command-button-pick::before {
  background-image: var(--command-pick-image);
}

#command-button-splitconsole::before {
  background-image: var(--command-splitconsole-image);
}

#command-button-noautohide::before {
  background-image: var(--command-noautohide-image);
}

#command-button-eyedropper::before {
  background-image: var(--command-eyedropper-image);
}

#command-button-rulers::before {
  background-image: var(--command-rulers-image);
}

#command-button-measure::before {
  background-image: var(--command-measure-image);
}

#command-button-frames::before {
  background-image: var(--command-frames-image);
}

#command-button-frames {
  background-image: url("chrome://devtools/skin/images/dropmarker.svg");
  background-repeat: no-repeat;

  /* Override background-size from the command-button.
   The drop down arrow is smaller */
  background-size: 8px 4px !important;
  min-width: 32px;
}

#command-button-frames:-moz-locale-dir(ltr) {
  background-position: right;
}

#command-button-frames:-moz-locale-dir(rtl) {
  background-position: left;
}

/* Toolbox panels */

.toolbox-panel {
  display: -moz-box;
  -moz-box-flex: 1;
  visibility: collapse;
}

.toolbox-panel[selected] {
  visibility: visible;
}

/**
 * When panels are collapsed or hidden, making sure that they are also
 * inaccessible by keyboard. This is not the case by default because the are
 * predominantly hidden using visibility: collapse; style or collapsed
 * attribute.
 */
.toolbox-panel *,
#toolbox-panel-webconsole[collapsed] * {
  -moz-user-focus: ignore;
}

/**
 * Enrure that selected toolbox panel's contents are keyboard accessible as they
 * are explicitly made not to be when hidden (default).
 */
.toolbox-panel[selected] * {
  -moz-user-focus: normal;
}
PK
!<";��tt6chrome/devtools/skin/tooltip/arrow-horizontal-dark.png�PNG


IHDR'��t;IDATx�ҵ]�q~�������[��N�7�{����<x��݉����־�2���j�7|�˯������z�\<�X,Sϳ/�D�YLLI����2�n�s���j��
�+��U�T�
�Jg^GH
k�
DT ��h�B@2����g��
�.�_�����KJM��L@�����9����W���a}����cYg`�Q����m���#:�c���=������Ņ��b���݇c#1,#W�%wR��NdCL-ޓS������:�
@`1 ���&��@딘pBIEND�B`�PK
!<Dp�ܑ�9chrome/devtools/skin/tooltip/[email protected]�PNG


IHDR NA��XIDATx��Ú\a��hҶm۶9�9�
ż�ضm;��Ie+�uzG�ڃU��{�3<�\.װn0�?���X7�^���{�V�\�7�qo�Fć�/ی���;�]�S�����x�S����y@p�Q�H�V�xe�s���#��Ⱳm:^RY�q��xqE�S��S��A����:^TV�q�l�./,�y�8�x�l�[����V";��%U�a*d�u<���!L@��[���P��s:�WT~_(�V��-,��C��N�
�)(C�
�����һ8�	�!���Yy�wp��K/3��6a�x�-��r�g�"N�s��g����EN��`_��̼��W�?�f�^c�5`�����8�3����]xT쩡`~F�6u�Q:`��@��R'Dd��@�6"2*[n ����q��5@P|�
0��@�6�uhd4T��@p6�:[툈P�
���w�j�������@P6����:�F��
b�@2�FT7���r瀠Ō�n���p#��9` �����u��F���r�=�IEND�B`�PK
!<��[���7chrome/devtools/skin/tooltip/arrow-horizontal-light.png�PNG


IHDR'��t]IDATxŖ5RQ@7�,��C��3p	n�������T�[]�A��G��7�5�����{�?P�U�V�0�\��r�@���僟Fn>����~Y#��0���̀�����$�
�L�CY
�N^�z���EQ
t�]�rP�(�r����W��i9;yA��6�r����ے��r.[�+�$0'�B���,����˧�k����>��m�	Č���͓e;�V�2�G#�<@��4�4�:��n��;ϧf�	�&�L���S?��j�~���׵��4r���%�4��<��A��"�4ry����(2&h�YK##�4�VFF*��
E�����Ʋ��R��IEND�B`�PK
!<���lj�:chrome/devtools/skin/tooltip/[email protected]�PNG


IHDR NA��PIDATxb������w��F0ꀛ_$�➁�|�az[��G��c�[aE�N����?�6�\#���8�~?V���`��~��#y��|y���IU˯�ъ��}��H~
L��IrY�g*�n�KrM��I�ou���bA�o��<��Y*���`��� f�$��_���[,���*�?�w�x�E�pT�	˥w��7�Y�%,�����=�׭@p�YD)˥@�"��5~Qe�#�ay�rU�C��a���)���|���g 3�~e�P|���q�-�(^�>ɋ8�Y.
x��̓�"a�8��w�,_R����s�U�l�<�
��.}�B�[�>�2dX��b����h�w�'�x��p�7�1A?������2�E��h�%y���y��PD��4�sP8��'��TM@<�����y��1H���#9]E��M�I��y�J���w^A��l�}�t����{��ld�7��X�8gv?I�D��I�qd���6�8���#`�ve"�[`\�3����u��FI�����w
IEND�B`�PK
!<��o��4chrome/devtools/skin/tooltip/arrow-vertical-dark.png�PNG


IHDR'��f lIDATxb�h�����0G�Y۶m�e�ضلד;�m�Mlg3ᙵQ|���R��"�bW5��5n�(��Q���:*�Q�M*��bP8Q�Q,�1���gr����
`r�8]�.���<K SO�Y��p�S04�(���o�a�bp����g�/���8[�aN_�
��<�a�bq�����W�W���8ۈao�
��@��8|�P,�
�=��=���2�'��DE��D�k�x�;��	��a�N��C�HT�ih�P��Q�7s�u����fs_)�Fb��l`�vq�ì��"��`�'S���M�@�88�����J�1ޯf��M�wlv��!XOR�-�no=�Op���[G�IEND�B`�PK
!<���7chrome/devtools/skin/tooltip/[email protected]�PNG


IHDRN �TN7�IDATx��S�Q��v�Ƕm��ֶm������'���.�{��
�dRKDB��z�����e�S;4�o��H%�]��4�?�
0.�AQp���K�K,V[�P�{`"�\�^����l�6Ϣ�v
�]6Y��9��i<
� fY������#��t�y!��y��{
<5�m��x2h<��xj��D6��
���v7����S��X����С�-;��}��{F�*hÿ@�
���9<w{'Y��T�R�6�m$|HǢ�^�y�'�S�X��ޑ�a��0D����O	p@�]�6>�7Mt�l�{��3кY�ξ���Y<����xw�'W8�%2hc���|�!��w�ûMU�
hT��7=j���#�&U)8��Q!Mg��`�]��
�Ih�T`
�<�7Yo
����u
xR�Z��A���6�TxW�'9���Mh��V�ѐ�]��û<�������cF�h%����;�R��Mm"n�:o�
9=�8��TE��VO�`�LBCNo�w�*/6�j�g,���#54���K2��	��XD��D\��I���q�xb��e�&4��:�W)/pgX4aB!4�W=��
�m,����(o�X���,,n��IEND�B`�PK
!<,7��\\5chrome/devtools/skin/tooltip/arrow-vertical-light.png�PNG


IHDR'��f #IDATx��5RA`����Y���q\c�m�qfp���bv��`��˓R���9�"�V��z.����|=s���������z�}d�P �?6��˳�X;2\�k�8=�N-�z�_yZB��`��g�o���zN
����N` ��
��u`�v`GaS8�p�S@(�2
��½�J�66��$�M�[�p
��E�z0�T`�`����#�:ئ���q-c'��0�#+n� ��G��'����Ba��,�0��B@�UP�X����BIEND�B`�PK
!<��2;;8chrome/devtools/skin/tooltip/[email protected]�PNG


IHDRN �TN7IDATx��S��P��.��hwQ۶m۶���O���e��7��':I�����x�+��/�
<}�W��_
\��E�~׽�G��
��5p�7���Ѓ#P���,���N6��Ee^*hÓh�w�qG��U���h�~��da^}���A�����h��A{Ģ1x%�2��#��"�)�]�=�M����=���]�-�Mf�����0�W�.�Ц#�h)����f�}�y�I�w��p�6'��ɟӠ�xg*h��J�����T��m1����by��[ ��NTЖ��=��"�'h+h�bx>��w�*��e�h�-�jp��1�����+�6x���!�v���!Z	x�,ѶSC���̓��>��
h�
h��FO��E�!�av���F�x����x��C��	�h D��μ��4{��HOԔ�x=E;�@�u�*`���-����/{G��8P
�zg�t�hu��]�t�n��t�IEND�B`�PK
!<��HF''!chrome/devtools/skin/tooltips.css/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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 stylesheets for specific tooltip widgets */
@import url(chrome://devtools/content/shared/widgets/color-widget.css);
@import url(chrome://devtools/content/shared/widgets/cubic-bezier.css);
@import url(chrome://devtools/content/shared/widgets/filter-widget.css);
@import url(chrome://devtools/content/shared/widgets/spectrum.css);

/* Tooltip specific theme variables */

.theme-dark {
  --bezier-diagonal-color: #eee;
  --bezier-grid-color: rgba(0, 0, 0, 0.2);
}

.theme-light {
  --bezier-diagonal-color: rgba(0, 0, 0, 0.2);
  --bezier-grid-color: rgba(0, 0, 0, 0.05);
}

/* Tooltip widget (see devtools/client/shared/widgets/tooltip/Tooltip.js) */

.devtools-tooltip .panel-arrowcontent {
  padding: 4px;
}

.devtools-tooltip .panel-arrowcontainer {
  /* Reseting the transition used when panels are shown */
  transition: none;
  /* Panels slide up/down/left/right when they appear using a transform.
  Since we want to remove the transition, we don't need to transform anymore
  plus it can interfeer by causing mouseleave events on the underlying nodes */
  transform: none;
}

.devtools-tooltip[clamped-dimensions] {
  min-height: 100px;
  max-height: 400px;
  min-width: 100px;
  max-width: 400px;
}
.devtools-tooltip[clamped-dimensions-no-min-height] {
  min-height: 0;
  max-height: 400px;
  min-width: 100px;
  max-width: 400px;
}
.devtools-tooltip[clamped-dimensions-no-max-or-min-height] {
  min-width: 400px;
  max-width: 400px;
}
.devtools-tooltip[clamped-dimensions] .panel-arrowcontent,
.devtools-tooltip[clamped-dimensions-no-min-height] .panel-arrowcontent,
.devtools-tooltip[clamped-dimensions-no-max-or-min-height] .panel-arrowcontent {
  overflow: hidden;
}
.devtools-tooltip[wide] {
  max-width: 600px;
}

/* Tooltip: CSS variables tooltip */

.devtools-tooltip-css-variable {
  color: var(--theme-body-color);
  padding: 2px;
}

/* Tooltip: Simple Text */

.devtools-tooltip-simple-text {
  max-width: 400px;
  margin: 0 -4px; /* Compensate for the .panel-arrowcontent padding. */
  padding: 8px 12px;
  white-space: pre-wrap;
}

.devtools-tooltip-simple-text:first-child {
  margin-top: -4px;
}

.devtools-tooltip-simple-text:last-child {
  margin-bottom: -4px;
}

/* Tooltip: Variables View */

.devtools-tooltip-variables-view-box {
  margin: -4px; /* Compensate for the .panel-arrowcontent padding. */
}

.devtools-tooltip-variables-view-box .variable-or-property > .title {
  padding-inline-end: 6px;
}

/* Tooltip: Tiles */

.devtools-tooltip-tiles {
  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: 20px 20px;
  background-position: 0 0, 10px 10px;
}

.devtools-tooltip-iframe {
  border: none;
  background: transparent;
}

.tooltip-container {
  display: none;
  position: fixed;
  z-index: 9999;
  display: none;
  background: transparent;
  pointer-events: none;
  overflow: hidden;
  filter: drop-shadow(0 3px 4px var(--theme-tooltip-shadow));
}

.tooltip-xul-wrapper {
  -moz-appearance: none;
  background: transparent;
  overflow: visible;
  border-style: none;
}

.tooltip-xul-wrapper .tooltip-container {
  position: absolute;
}

.tooltip-top {
  flex-direction: column;
}

.tooltip-bottom {
  flex-direction: column-reverse;
}

.tooltip-panel{
  background-color: var(--theme-tooltip-background);
  pointer-events: all;
  flex-grow: 1;
}

.tooltip-visible {
  display: flex;
}

.tooltip-hidden {
  display: flex;
  visibility: hidden;
}

/* Tooltip : flexible height styles */

.tooltip-flexible-height .tooltip-panel {
  /* In flexible mode the tooltip panel should only grow according to its content. */
  flex-grow: 0;
}

.tooltip-flexible-height .tooltip-filler {
  /* In flexible mode the filler should grow as much as possible. */
  flex-grow: 1;
}

/* type="arrow" overrides: remove arrow decorations for the xul <panel> wrapper */

.tooltip-xul-wrapper[type="arrow"] {
  margin: 0;
}

/* The arrow image is hidden because the panel is opened using openPopupAtScreen(). */

/* Remove all decorations on .panel-arrowcontent is the tooltip content container. */
.tooltip-xul-wrapper[type="arrow"] .panel-arrowcontent {
  margin: 0;
  padding: 0;
  background: transparent;
  border: none;
  box-shadow: none;
}

/* Tooltip : arrow style */

.tooltip-xul-wrapper .tooltip-container {
  /* When displayed in a XUL panel the drop shadow would be abruptly cut by the panel */
  filter: none;
}

.tooltip-container[type="arrow"] > .tooltip-panel {
  position: relative;
  min-height: 10px;
  box-sizing: border-box;
  width: 100%;

  border: 3px solid var(--theme-tooltip-border);
  border-radius: 5px;
}

.tooltip-top[type="arrow"] .tooltip-panel {
  top: 0;
}

.tooltip-bottom[type="arrow"] .tooltip-panel {
  bottom: 0;
}

.tooltip-arrow {
  position: relative;
  height: 16px;
  width: 32px;
  overflow: hidden;
  flex-shrink: 0;
}

/* In RTL locales, only use RTL on the tooltip content, keep LTR for positioning */
.tooltip-container:-moz-locale-dir(rtl) {
  direction: ltr;
}

.tooltip-panel:-moz-locale-dir(rtl) {
  direction: rtl;
}

.tooltip-top .tooltip-arrow {
  margin-top: -3px;
}

.tooltip-bottom .tooltip-arrow {
  margin-bottom: -3px;
}

.tooltip-arrow:before {
  content: "";
  position: absolute;
  width: 21px;
  height: 21px;
  margin-left: 4px;
  background: linear-gradient(-45deg,
    var(--theme-tooltip-background) 50%, transparent 50%);
  border-color: var(--theme-tooltip-border);
  border-style: solid;
  border-width: 0px 3px 3px 0px;
  border-radius: 3px;
  pointer-events: all;
}

.tooltip-bottom .tooltip-arrow:before {
  margin-top: 4px;
  transform: rotate(225deg);
}

.tooltip-top .tooltip-arrow:before {
  margin-top: -12px;
  transform: rotate(45deg);
}

/* Tooltip: Events */

.event-header {
  display: flex;
  align-items: center;
  cursor: pointer;
  overflow: hidden;
}

.event-header:before {
  content: "▶︎";
  padding-inline-end: 5px;
}

.event-header.content-expanded:before {
  content: "▼";
}

.event-header:first-child {
  border-width: 0;
}

.event-header:not(:first-child) {
  border-width: 1px 0 0 0;
}

.event-tooltip-debugger-icon {
  -moz-context-properties: stroke;
   stroke: currentColor;
}

.devtools-tooltip-events-container {
  height: 100%;
  overflow-y: auto;
}

.event-tooltip-event-type,
.event-tooltip-filename,
.event-tooltip-attributes {
  margin-inline-start: 0;
  flex-shrink: 0;
  cursor: pointer;
}

.event-tooltip-event-type {
  font-weight: bold;
  font-size: 13px;
}

.event-tooltip-filename {
  margin: 0 5px;
  font-size: 100%;
  flex-shrink: 1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  /* Force ellipsis to be displayed on the left */
  direction: rtl;
}

.event-tooltip-debugger-icon,
.event-tooltip-debugger-spacer {
  width: 16px;
  height: 16px;
  margin-inline-end: 4px;
  opacity: 0.6;
  flex-shrink: 0;
}

.event-tooltip-debugger-icon:hover {
  opacity: 1;
}

.event-tooltip-content-box {
  display: none;
  height: 100px;
  overflow: hidden;
  margin-inline-end: 0;
  border: 1px solid var(--theme-splitter-color);
  border-width: 1px 0 0 0;
}

.event-toolbox-content-box iframe {
  height: 100%;
  border-style: none;
}

.event-tooltip-content-box[open] {
  display: block;
}

.event-tooltip-source-container {
  margin-top: 5px;
  margin-bottom: 10px;
  margin-inline-start: 5px;
  margin-inline-end: 0;
}

.event-tooltip-source {
  margin-bottom: 0;
}

.event-tooltip-attributes-container {
  display: flex;
  flex-shrink: 0;
  flex-grow: 1;
  justify-content: flex-end;
}

.event-tooltip-attributes-box {
  display: flex;
  flex-shrink: 0;
  align-items: center;
  height: 14px;
  border-radius: 3px;
  padding: 2px;
  margin-inline-start: 5px;
  background-color: var(--theme-body-color-alt);
  color: var(--theme-body-background);
}

.event-tooltip-attributes {
  margin: 0;
  font-size: 9px;
  padding-top: 2px;
}

/*
 * Tooltip: JS stack traces
 */

.stack-trace-tooltip {
  direction: ltr;
  height: 100%;
  overflow-y: auto;
}

.stack-trace-tooltip > .stack-frame {
  margin-left: 5px;
  margin-right: 5px;
}

.stack-trace-tooltip > .stack-frame:first-child {
  margin-top: 5px;
}

.stack-trace-tooltip > .stack-frame:last-child {
  margin-bottom: 5px;
}

.stack-frame-call {
  color: var(--theme-body-color-alt);
  cursor: pointer;
  display: flex;
}

.stack-frame-call:hover {
  background-color: var(--theme-selection-background-hover);
}

.stack-frame-async {
  color: var(--theme-body-color-inactive);
}

.stack-frame-function-name {
  color: var(--theme-highlight-blue);
  max-width: 50%;
  margin-inline-end: 1em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.stack-frame-source-name {
  flex: 1 1;
  /* Makes the file name truncated (and ellipsis shown) on the left side */
  direction: rtl;
  text-align: right;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Enforce LTR direction for the file name - fixes bug 1290056 */
.stack-frame-source-name-inner {
  direction: ltr;
  unicode-bidi: embed;
}

.stack-frame-line {
  color: var(--theme-highlight-orange);
}

/* Tooltip: HTML Search */

#searchbox-panel-listbox {
  width: 250px;
  max-width: 250px;
  overflow-x: hidden;
}

#searchbox-panel-listbox .autocomplete-item,
#searchbox-panel-listbox .autocomplete-item[selected] {
  overflow-x: hidden;
}

#searchbox-panel-listbox .autocomplete-item > .initial-value {
  max-width: 130px;
  margin-left: 15px;
}

#searchbox-panel-listbox .autocomplete-item > .autocomplete-value {
  max-width: 150px;
}

/* Tooltip: Image tooltip */

.devtools-tooltip-image-broken {
  box-sizing: border-box;
  height: 100%;
  padding: 7px;
}
PK
!<k>�kk'chrome/devtools/skin/webaudioeditor.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

/* Reload and waiting notices */
.notice-container {
  margin-top: -50vh;
  color: var(--theme-body-color-alt);
}

#reload-notice {
  font-size: 120%;
}

#waiting-notice {
  font-size: 110%;
}

/* Context Graph */
svg {
  overflow: hidden;
  -moz-box-flex: 1;
  --arrow-color: var(--theme-splitter-color);
  --text-color: var(--theme-body-color);
}

.theme-dark svg {
  --arrow-color: var(--theme-body-color-alt);
}

/* Edges in graph */
.edgePath path {
  stroke-width: 1px;
  stroke: var(--arrow-color);
  fill: none;
}
svg #arrowhead {
  /* !important is needed to override inline style */
  fill: var(--arrow-color) !important;
}

/* AudioParam connection edges */
g.edgePath.param-connection path {
  stroke-dasharray: 5,5;
  stroke: var(--arrow-colo);
}

/* Labels in AudioParam connection should have background that match
 * the main background so there's whitespace around the label, on top of the
 * dotted lines. */
g.edgeLabel rect {
  fill: var(--theme-body-background);
}
g.edgeLabel tspan {
  fill: var(--text-color);
}

/* Audio Nodes */
.nodes rect {
  stroke-width: 1px;
  cursor: pointer;
  stroke: var(--theme-splitter-color);
  fill: var(--theme-toolbar-background);
}

/**
 * Bypassed Nodes
 */

.theme-light .nodes g.bypassed rect {
  fill: url(chrome://devtools/skin/images/filters.svg#bypass-light);
}

.theme-dark .nodes g.bypassed rect {
  fill: url(chrome://devtools/skin/images/filters.svg#bypass-dark);
}

.nodes g.bypassed.selected rect {
  stroke: var(--theme-selection-background);
}

.nodes g.bypassed text {
  opacity: 0.6;
}

/**
 * Selected Nodes
 */
.nodes g.selected rect {
  fill: var(--theme-selection-background);
}

/* Don't style bypassed nodes text differently because it'd be illegible in light-theme */
g.selected:not(.bypassed) text {
  fill: var(--theme-selection-color);
}


/* Text in nodes and edges */
text {
  cursor: default; /* override the "text" cursor */
  fill: var(--text-color);
  font-size: 1.25em;
  /* Make sure text stays inside its container in RTL locales */
  direction: ltr;
}

.nodes text {
  cursor: pointer;
}

/**
 * Inspector Styles
 */

/* hide the variables view scope title as its redundant,
 * because there's only one scope displayed. */
.variables-view-scope > .title {
  display: none;
}

#web-audio-inspector-title {
  margin: 6px;
}

.web-audio-inspector .error {
  background-image: url(images/alerticon-warning.png);
  background-size: 13px 12px;
  -moz-appearance: none;
  opacity: 0;
  transition: opacity .5s ease-out 0s;
}

#inspector-pane-toggle {
  background: none;
  box-shadow: none;
  border: none;
  list-style-image: var(--theme-pane-collapse-image);
}

#inspector-pane-toggle > .toolbarbutton-icon {
  width: 16px;
  height: 16px;
}

#inspector-pane-toggle.pane-collapsed {
  list-style-image: var(--theme-pane-expand-image);
}

/**
 * Automation Styles
 */

#automation-param-toolbar .automation-param-button[selected] {
  color: var(--theme-selection-color);
  background-color: var(--theme-selection-background);
}

#automation-graph {
  overflow: hidden;
  -moz-box-flex: 1;
}

@media (min-resolution: 1.1dppx) {
  .web-audio-inspector .error {
    background-image: url(images/[email protected]);
  }
}

/**
 * Inspector toolbar
 */

#audio-node-toolbar .bypass {
  list-style-image: url(images/power.svg);
}

/**
 * Responsive Styles
 * `.devtools-responsive-container` takes care of most of
 * the changing of host types.
 */
@media (max-width: 700px) {
  /**
   * Override the inspector toggle so it's always open
   * in the portrait view, with the toggle button hidden.
   */
  #inspector-pane-toggle {
    display: none;
  }

  #web-audio-inspector {
    margin-left: 0px !important;
    margin-right: 0px !important;
  }
}
PK
!<� �4f|f|#chrome/devtools/skin/webconsole.css/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* Webconsole specific theme variables */
.theme-light,
.theme-firebug {
  --error-color: #FF0000;
  --error-background-color: #FFEBEB;
  --warning-background-color: #FFFFC8;
}

/* General output styles */

a {
  -moz-user-focus: normal;
  cursor: pointer;
  text-decoration: underline;
}

/* Workaround for Bug 575675 - FindChildWithRules aRelevantLinkVisited
 * assertion when loading HTML page with links in XUL iframe */
*:visited { }

* {
  box-sizing: border-box;
}

.message {
  display: flex;
  padding: 0 7px;
  width: 100%;
  box-sizing: border-box;
}

.message > .prefix,
.message > .timestamp {
  flex: none;
  color: var(--theme-comment);
  margin: 3px 6px 0 0;
}

.message > .indent {
  flex: none;
}

.message > .icon {
  flex: none;
  margin: 3px 6px 0 0;
  padding: 0 4px;
  height: 1em;
  align-self: flex-start;
}

.theme-firebug .message > .icon {
  margin: 0;
  margin-inline-end: 6px;
}

.theme-firebug .message[severity="error"],
.theme-light .message.error,
.theme-firebug .message.error {
  color: var(--error-color);
  background-color: var(--error-background-color);
}

.theme-firebug .message[severity="warn"],
.theme-light .message.warn,
.theme-firebug .message.warn {
  background-color: var(--warning-background-color);
}

.message > .icon::before {
  content: "";
  background-image: url(chrome://devtools/skin/images/webconsole.svg);
  background-position: 12px 12px;
  background-repeat: no-repeat;
  background-size: 72px 60px;
  width: 12px;
  height: 12px;
  display: inline-block;
}

.theme-light .message > .icon::before {
  background-image: url(chrome://devtools/skin/images/webconsole.svg#light-icons);
}

.message > .message-body-wrapper {
  flex: auto;
  min-width: 0px;
  margin: 3px;
}

.message-body-wrapper .table-widget-body {
  overflow: visible;
}

/* The red bubble that shows the number of times a message is repeated */
.message-repeats {
  -moz-user-select: none;
  flex: none;
  margin: 2px 6px;
  padding: 0 6px;
  height: 1.25em;
  color: white;
  background-color: red;
  border-radius: 40px;
  font: message-box;
  font-size: 0.9em;
  font-weight: 600;
}

.message-repeats[value="1"] {
  display: none;
}

.message-location {
  max-width: 40%;
}

.stack-trace {
  /* The markup contains extra whitespace to improve formatting of clipboard text.
     Make sure this whitespace doesn't affect the HTML rendering */
  white-space: normal;
}

.stack-trace .frame-link-source,
.message-location .frame-link-source {
  /* Makes the file name truncated (and ellipsis shown) on the left side */
  direction: rtl;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.stack-trace .frame-link-source-inner,
.message-location .frame-link-source-inner {
  /* Enforce LTR direction for the file name - fixes bug 1290056 */
  direction: ltr;
  unicode-bidi: embed;
}

.stack-trace .frame-link-function-display-name {
  max-width: 50%;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.message-flex-body {
  display: flex;
}

.message-body {
  white-space: pre-wrap;
  word-wrap: break-word;
}

.message-flex-body > .message-body {
  display: block;
  flex: auto;
}

#output-wrapper {
  direction: ltr;
  overflow: auto;
  -moz-user-select: text;
  position: relative;
}

/* The width on #output-container is set to a hardcoded px in webconsole.js
   since it's way faster than using 100% with -moz-box-flex (see Bug 1237368) */

#output-container.hideTimestamps > .message {
  padding-inline-start: 0;
  margin-inline-start: 7px;
  width: calc(100% - 7px);
}

#output-container.hideTimestamps > .message > .timestamp {
  display: none;
}

#output-container.hideTimestamps > .message > .indent {
  background-color: var(--theme-body-background);
}

.filtered-by-type,
.filtered-by-string {
  display: none;
}

.hidden-message {
  display: block;
  visibility: hidden;
  height: 0;
  overflow: hidden;
}

/* WebConsole colored drops */

.webconsole-filter-button {
  -moz-user-focus: normal;
}

.webconsole-filter-button > .toolbarbutton-menubutton-button:before {
  content: "";
  display: inline-block;
  height: 8px;
  width: 8px;
  border-radius: 50%;
  margin-inline-start: 5px;
  border-width: 1px;
  border-style: solid;
}

/* Network styles */
.webconsole-filter-button[category="net"] > .toolbarbutton-menubutton-button:before {
  background-image: linear-gradient(#444444, #000000);
  border-color: #777;
}

.theme-dark .message[severity=error],
.theme-dark .message.error {
  background-color: rgba(235, 83, 104, 0.17);
}

.console-string {
  color: var(--theme-highlight-lightorange);
}

.theme-selected .console-string,
.theme-selected .cm-number,
.theme-selected .cm-variable,
.theme-selected .kind-ArrayLike {
  color: #f5f7fa !important; /* Selection Text Color */
}

.message[category=network] > .indent {
  border-inline-end: solid var(--theme-body-color-alt) 6px;
}

.message[category=network][severity=error] > .icon::before,
.message.network.error > .icon::before {
  background-position: -12px 0;
}

.message[category=network] > .message-body,
.message.network > .message-body {
  display: flex;
  flex-wrap: wrap;
}

.message[category=network] .method,
.message.network .method {
  flex: none;
}

.message[category=network]:not(.navigation-marker) .url,
.message.network:not(.navigation-marker) .url {
  flex: 1 1 auto;
  /* Make sure the URL is very small initially, let flex change width as needed. */
  width: 100px;
  min-width: 5em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.message[category=network] .status,
.message.network .status {
  flex: none;
  margin-inline-start: 6px;
}

.message[category=network].mixed-content .url,
.message.network.mixed-content .url {
  color: var(--theme-highlight-red);
}

.message .learn-more-link {
  color: var(--theme-highlight-blue);
  margin: 0 6px;
}

.message[category=network] .xhr,
.message.network .xhr {
  background-color: var(--theme-body-color-alt);
  color: var(--theme-body-background);
  border-radius: 3px;
  font-weight: bold;
  font-size: 10px;
  padding: 1px 2px;
  line-height: 10px;
  margin-inline-start: 0;
  margin-inline-end: 1ex;
}

/* CSS styles */
.webconsole-filter-button[category="css"] > .toolbarbutton-menubutton-button:before {
  background-image: linear-gradient(#2DC3F3, #00B6F0);
  border-color: #1BA2CC;
}

.message[category=cssparser] > .indent,
.message.cssparser > .indent  {
  border-inline-end: solid #00b6f0 6px;
}

.message[category=cssparser][severity=error] > .icon::before,
.message.cssparser.error > .icon::before {
  background-position: -12px -12px;
}

.message[category=cssparser][severity=warn] > .icon::before,
.message.cssparser.warn > .icon::before {
  background-position: -24px -12px;
}

/* JS styles */
.webconsole-filter-button[category="js"] > .toolbarbutton-menubutton-button:before {
  background-image: linear-gradient(#FCB142, #FB9500);
  border-color: #E98A00;
}

.message[category=exception] > .indent,
.message.exception > .indent {
  border-inline-end: solid #fb9500 6px;
}

.message[category=exception][severity=error] > .icon::before,
.message.exception.error > .icon::before {
  background-position: -12px -24px;
}

.message[category=exception][severity=warn] > .icon::before,
.message.exception.warn > .icon::before {
  background-position: -24px -24px;
}

/* Web Developer styles */
.webconsole-filter-button[category="logging"] > .toolbarbutton-menubutton-button:before {
  background-image: linear-gradient(#B9B9B9, #AAAAAA);
  border-color: #929292;
}

.message[category=console] > .indent,
.message.console-api > .indent {
  border-inline-end: solid #cbcbcb 6px;
}

.message[category=console][severity=error] > .icon::before,
.message[category=output][severity=error] > .icon::before,
.message[category=server][severity=error] > .icon::before {
  background-position: -12px -36px;
}

.message[category=console][severity=warn] > .icon::before,
.message[category=server][severity=warn] > .icon::before {
  background-position: -24px -36px;
}

.message[category=console][severity=info] > .icon::before,
.message[category=server][severity=info] > .icon::before {
  background-position: -36px -36px;
}

/* Server Logging Styles */

.webconsole-filter-button[category="server"] > .toolbarbutton-menubutton-button:before {
  background-image: linear-gradient(rgb(144, 176, 144), rgb(99, 151, 99));
  border-color: rgb(76, 143, 76);
}

.message[category=server] > .indent,
.message.server > .indent {
  border-inline-end: solid #90B090 6px;
}

/* Input and output styles */
.message[category=input] > .indent,
.message[category=output] > .indent,
.message.command > .indent,
.message.result > .indent {
  border-inline-end: solid #808080 6px;
}

.message[category=input] > .icon::before,
.message.command > .icon::before {
  background-position: -48px -36px;
}

.message[category=output] > .icon::before,
.message.result > .icon::before {
  background-position: -60px -36px;
}

/* JSTerm Styles */

html #jsterm-wrapper,
html .jsterm-stack-node,
html .jsterm-input-node-html,
html #webconsole-notificationbox {
  flex: 0;
  width: 100vw;
}

.jsterm-input-container {
  background-color: var(--theme-tab-toolbar-background);
  border-top: 1px solid var(--theme-splitter-color);
}

.jsterm-input-node {
  /* Always allow scrolling on input - it auto expands in js by setting height,
     but don't want it to get bigger than the window. 24px = toolbar height. */
  max-height: calc(90vh - 24px);
  background-image: var(--theme-command-line-image);
  background-repeat: no-repeat;
  background-size: 16px 16px;
  background-position: 4px 50%;
  color: var(--theme-content-color1);
  box-sizing: border-box;
}

.jsterm-complete-node {
  color: var(--theme-comment);
}

.theme-light .jsterm-input-container {
  /* For light theme use a white background for the input - it looks better
     than off-white */
  background-color: #fff;
  border-top-color: #e0e0e0;
}

.theme-firebug .jsterm-input-container {
  border-top: 1px solid #ccc;
}

/*  styles for the new HTML frontend */

html .jsterm-stack-node {
  position: relative;
}

textarea.jsterm-input-node,
textarea.jsterm-complete-node {
  width: 100%;
  border: none;
  margin: 0;
  background-color: transparent;
  resize: none;
  font-size: inherit;
  line-height: 16px;
  overflow-x: hidden;
  /* Set padding for console input on textarea to make sure it is included in
     scrollHeight that is used when resizing JSTerminal's input. */
  padding: 4px 0;
  padding-inline-start: 20px;
}

textarea.jsterm-complete-node {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  pointer-events: none;
}

textarea.jsterm-input-node:focus {
  background-image: var(--theme-command-line-image-focus);
  box-shadow: none;
}


/*  styles for the old frontend, which can be removed in Bug 1381834 */

textbox.jsterm-input-node,
textbox.jsterm-complete-node {
  border: none;
  padding: 0;
  padding-inline-start: 20px;
  margin: 0;
  -moz-appearance: none;
  background-color: transparent;
}

:-moz-any(textbox.jsterm-input-node,
          textbox.jsterm-complete-node) > .textbox-input-box > .textbox-textarea {
  overflow-x: hidden;
  /* Set padding for console input on textbox to make sure it is included in
     scrollHeight that is used when resizing JSTerminal's input. Note: textbox
     default style has important already */
  padding: 4px 0 !important;
}

textbox.jsterm-input-node[focused="true"] {
  background-image: var(--theme-command-line-image-focus);
  box-shadow: none;
}

.inlined-variables-view .message-body {
  display: flex;
  flex-direction: column;
  resize: vertical;
  overflow: auto;
  min-height: 200px;
}
.inlined-variables-view iframe {
  display: block;
  flex: 1;
  margin-top: 5px;
  margin-bottom: 15px;
  margin-inline-end: 15px;
  border: 1px solid var(--theme-splitter-color);
  border-radius: 3px;
}

#webconsole-sidebar > tabs {
  height: 0;
  border: none;
}

/* Firebug theme has the tabs visible. */
.theme-firebug #webconsole-sidebar > tabs {
  height: 28px;
}

.devtools-side-splitter ~ #webconsole-sidebar[hidden] {
  display: none;
}

/* Security styles */

.message[category=security] > .indent,
.message.security > .indent {
  border-inline-end: solid red 6px;
}

.webconsole-filter-button[category="security"] > .toolbarbutton-menubutton-button:before {
  background-image: linear-gradient(#FF3030, #FF7D7D);
  border-color: #D12C2C;
}

.message[category=security][severity=error] > .icon::before,
.message.security.error > .icon::before {
  background-position: -12px -48px;
}

.message[category=security][severity=warn] > .icon::before,
.message.security.warn > .icon::before {
  background-position: -24px -48px;
}

.navigation-marker {
  color: #aaa;
  background: linear-gradient(#aaa, #aaa) no-repeat left 50%;
  background-size: 100% 2px;
  margin-top: 6px;
  margin-bottom: 6px;
  font-size: 0.9em;
}

.navigation-marker .url {
  padding-inline-end: 9px;
  text-decoration: none;
  background: var(--theme-body-background);
}

.theme-light .navigation-marker .url {
  background: #fff;
}

.stacktrace {
  display: none;
  padding: 5px 10px;
  margin: 5px 0 0 0;
  overflow-y: auto;
  border: 1px solid var(--theme-splitter-color);
  border-radius: 3px;
}

.consoletable {
  margin: 5px 0 0 0;
}

/* Force cells to only show one row of contents.  Getting normal ellipses
   behavior has proven impossible so far, so this is better than letting
   rows get out of vertical alignment when one cell has a lot of content. */
.consoletable .table-widget-cell > span {
  overflow: hidden;
  display: flex;
  height: 1.25em;
  line-height: 1.25em;
}

.theme-light .message[severity=error] .stacktrace,
.theme-light .message.error .stacktrace {
  background-color: rgba(255, 255, 255, 0.5);
}

.theme-dark .message[severity=error] .stacktrace,
.theme-dark .message.error .stacktrace {
  background-color: rgba(0, 0, 0, 0.5);
}

.message[open] .stacktrace,
.message.open .stacktrace {
  display: block;
}

.message .theme-twisty {
  position: relative;
  top: 0.1em;
}

/*Do not mirror the twisty because container force to ltr */
.message .theme-twisty:dir(rtl),
.message .theme-twisty:-moz-locale-dir(rtl) {
  transform: none;
}

.cm-s-mozilla a[class] {
  font-style: italic;
  text-decoration: none;
}

.cm-s-mozilla a[class]:hover,
.cm-s-mozilla a[class]:focus {
  text-decoration: underline;
}

a.learn-more-link.webconsole-learn-more-link {
    font-style: normal;
}

/* Open DOMNode in inspector button */
.open-inspector {
  background: url("chrome://devtools/skin/images/vview-open-inspector.png") no-repeat 0 0;
  padding-left: 16px;
  margin-left: 5px;
  cursor: pointer;
}

.elementNode:hover .open-inspector,
.open-inspector:hover {
  filter: var(--theme-icon-checked-filter);
}

.elementNode:hover .open-inspector:active,
.open-inspector:active {
  filter: var(--theme-icon-checked-filter) brightness(0.9);
}

/* Old console frontend filters */
.devtools-toolbarbutton[type=menu-button] > .toolbarbutton-menubutton-button {
  -moz-appearance: none;
  color: inherit;
  border-width: 0;
  -moz-box-orient: horizontal;
  padding: 0;
}

.devtools-toolbarbutton[type=menu-button] {
  padding: 0 1px;
  -moz-box-align: stretch;
  --toolbarbutton-checked-color: var(--theme-body-color);
  --toolbarbutton-checked-background: var(--theme-selection-background-hover);
}

.devtools-toolbarbutton > .toolbarbutton-menubutton-button > .toolbarbutton-icon {
  margin-inline-end: 4px;
}

.devtools-menulist > .menulist-dropmarker {
  -moz-appearance: none;
  display: -moz-box;
  list-style-image: url("chrome://devtools/skin/images/dropmarker.svg");
  -moz-box-align: center;
  min-width: 16px;
}

.devtools-toolbarbutton[type=menu] > .toolbarbutton-menu-dropmarker,
.devtools-toolbarbutton[type=menu-button] > .toolbarbutton-menubutton-dropmarker {
  -moz-appearance: none !important;
  list-style-image: url("chrome://devtools/skin/images/dropmarker.svg");
  -moz-box-align: center;
  padding: 0 3px;
}

@media (max-width: 500px) {
  .message > .timestamp {
    display: none;
  }
  .hud-console-filter-toolbar .webconsole-filter-button .toolbarbutton-text {
    display: none;
  }
  .hud-console-filter-toolbar .webconsole-filter-button {
    min-width: 40px;
  }
  .hud-console-filter-toolbar .webconsole-clear-console-button {
    min-width: 25px;
  }
  .webconsole-filter-button > .toolbarbutton-menubutton-button:before {
    width: 12px;
    height: 12px;
    margin-inline-start: 1px;
  }
  .toolbarbutton-menubutton-dropmarker {
    margin: 0px;
  }
}

@media (max-width: 300px) {
  .hud-console-filter-toolbar {
    -moz-box-orient: vertical;
  }
  .toolbarbutton-text {
    display: -moz-box;
  }
  .devtools-toolbarbutton {
    margin-top: 3px;
  }
  .hud-console-filter-toolbar .hud-filter-box,
  .hud-console-filter-toolbar .devtools-toolbarbutton {
    margin-top: 5px;
  }
}

/*
 * This hardcoded width likely due to a toolkit Windows specific bug.
 * See https://hg.mozilla.org/mozilla-central/annotate/f38d6df93cad/toolkit/themes/winstripe/global/textbox-aero.css#l7
 */

:root[platform="win"] .hud-filter-box {
  width: 200px;
}

/* Firebug theme support for console.table() */

.theme-firebug .consoletable .theme-body {
  width: 100%;
  border-top: 1px solid #D7D7D7;
  border-bottom: 2px solid #D7D7D7;
  border-left: 1px solid #D7D7D7;
  border-right: 1px solid #D7D7D7;
}


/* NEW CONSOLE STYLES */

#output-container {
  height: 100%;
}

.webconsole-output-wrapper {
  display: flex;
  flex-direction: column;
  height: 100%;
  -moz-user-focus: normal;
  color: var(--console-output-color);
  --console-output-indent-border-color: var(--theme-selection-background);
  --icon-top-margin: 3px;
  --object-inspector-hover-background: transparent;
  --attachment-margin-block-end: 3px;
  --primary-toolbar-height: 29px;
  --close-button-image: url(chrome://devtools/skin/images/close.svg);
}

/* Webconsole specific theme variables */
.theme-light .webconsole-output-wrapper,
.theme-firebug .webconsole-output-wrapper {
  --error-color: var(--red-70);
  --error-background-color: #FDF2F5;
  --warning-color: var(--yellow-80);
  --warning-background-color: #FFFBD5;
  --console-output-color: var(--grey-90);
  --attachment-border-color: var(--theme-content-color1);
  --repeat-bubble-background-color: var(--theme-highlight-blue);
}

.theme-dark .webconsole-output-wrapper {
  --error-color: #FF9494;
  --error-background-color: #442923;
  --warning-color: #FCE19F;
  --warning-background-color: #44391F;
  --console-output-color: white;
  --attachment-border-color: var(--theme-body-color);
  --repeat-bubble-background-color: var(--blue-60);
}

/*
  This element contains the different toolbars in the console
    - primary, containing the clear messages button and the text search input.
      It can expand as much as it need.
    - filtered messages, containing the "X items hidden by filters" and the reset filters button.
      It should be on the same row than the primary bar if it fits there, or on its own 100% row if it is wrapped.
    - secondary, containing the filter buttons (Error, Warning, …).
      It should be on its own 100% row.

  Basically here's what we can have :

  -----------------------------------------------------------------------------------------------------------
  | Clear button - Open filter bar button - Filter Input | X items hidden by filters - Reset Filters button |
  -----------------------------------------------------------------------------------------------------------
  | Error - Warning - Log - Info - Debug - CSS - Network - XHR                                              |
  -----------------------------------------------------------------------------------------------------------

  or

  ------------------------------------------------------------------------------------
  | Clear button - Open filter bar button - Filter Input                             |
  ------------------------------------------------------------------------------------
  |                                X items hidden by filters  - Reset Filters button |
  ------------------------------------------------------------------------------------
  | Error - Warning - Log - Info - Debug - CSS - Network - XHR                       |
  ------------------------------------------------------------------------------------
*/
.webconsole-filteringbar-wrapper {
  display: flex;
  grid-row: 1 / 2;
  /* Wrap so the "Hidden messages" bar can go to its own row if needed */
  flex-wrap: wrap;
  --primary-toolbar-height: 29px;
}

.webconsole-filterbar-primary {
  display: flex;
  /* We want the toolbar (which contain the text search input) to fit
  the content, we don't allow to shrink/overlap it */
  flex: 100 0 -moz-fit-content;
  align-items: center;
  min-height: var(--primary-toolbar-height);
}

.devtools-toolbar.webconsole-filterbar-secondary {
  display: flex;
  width: 100%;
  align-items: center;
  -moz-user-select: none;
}

.webconsole-filterbar-primary .devtools-plaininput {
  flex: 1 1 100%;
  align-self: stretch;
  margin-left: 1px;
  padding-inline-start: 4px;
  border: 1px solid transparent;
}

.devtools-plaininput:focus {
  border: 1px solid var(--blue-50);
  transition: all 0.2s ease-in-out;
  outline: none;
}

.webconsole-filterbar-primary .filter-checkbox {
  flex-shrink: 0;
  margin: 0 3px;
  display: flex;
  align-items: center;
  -moz-user-select: none;
}

.webconsole-filterbar-secondary .devtools-separator {
  margin: 0 5px;
}

.webconsole-filterbar-filtered-messages {
  /* Needed so the bar takes the whole horizontal space when it is wrapped */
  flex-grow: 1;
  justify-content: flex-end;
  color: var(--theme-comment);
  text-align: end;
  align-items: center;
  min-height: var(--primary-toolbar-height);
  line-height: var(--primary-toolbar-height);
}

.webconsole-filterbar-filtered-messages .filter-message-text {
  font-style: italic;
  -moz-user-select: none;
}

.webconsole-filterbar-filtered-messages .reset-filters-button {
  margin-inline-start: 0.5em;
}

.webconsole-output {
  flex: 1;
  overflow: auto;
}

.webconsole-output-wrapper .message {
  --border-size: 3px;
  border-inline-start: var(--border-size) solid transparent;
}

.webconsole-output-wrapper .message:hover {
  border-inline-start-color: var(--theme-highlight-blue);
}

.webconsole-output-wrapper .message.warn.warn {
  background-color: var(--warning-background-color);
}

.webconsole-output-wrapper .message.error .message-body {
  color: var(--error-color);
}

.webconsole-output-wrapper .message.warn .message-body {
  color: var(--warning-color);
}

/* Special casing String reps so they are legible */
.webconsole-output-wrapper .message .message-body > .objectBox-string {
  color: currentColor;
}

/* Special casing dark-theme error and warning ObjectInspector colors */
.theme-dark .webconsole-output-wrapper .message.error .tree.object-inspector .object-label,
.theme-dark .webconsole-output-wrapper .message.error .tree.object-inspector .object-label *,
.theme-dark .webconsole-output-wrapper .message.warn .tree.object-inspector .object-label,
.theme-dark .webconsole-output-wrapper .message.warn .tree.object-inspector .object-label *,
.theme-dark .webconsole-output-wrapper .message.error .objectLeftBrace,
.theme-dark .webconsole-output-wrapper .message.error .objectRightBrace,
.theme-dark .webconsole-output-wrapper .message.error .arrayLeftBracket,
.theme-dark .webconsole-output-wrapper .message.error .arrayRightBracket,
.theme-dark .webconsole-output-wrapper .message.warn .objectLeftBrace,
.theme-dark .webconsole-output-wrapper .message.warn .objectRightBrace,
.theme-dark .webconsole-output-wrapper .message.warn .arrayLeftBracket,
.theme-dark .webconsole-output-wrapper .message.warn .arrayRightBracket {
  color: var(--theme-body-color);
}
.theme-dark .webconsole-output-wrapper .message.error .tree.object-inspector,
.theme-dark .webconsole-output-wrapper .message.warn .tree.object-inspector {
  --console-output-indent-border-color: var(--theme-body-color);
}

.webconsole-output-wrapper .message-flex-body > .message-body {
  display: inline-block;
  max-width: 100%;
}

.webconsole-output-wrapper .message-body > * {
  flex-shrink: 0;
  vertical-align: top;
}

.message.startGroup .message-body > .objectBox-string,
.message.startGroupCollapsed .message-body > .objectBox-string {
  color: var(--theme-body-color);
  font-weight: bold;
}

.webconsole-output-wrapper .message > .icon {
  margin: var(--icon-top-margin) 0 0 0;
  padding: 0 0 0 6px;
}

.webconsole-output-wrapper .message.error > .icon::before {
  /* Red warning icon */
  background-position: -24px -48px;
}

.webconsole-output-wrapper .message.warn > .icon::before {
  /* Yellow warning icon */
  background-position: -24px -24px;
}

.webconsole-output-wrapper .message .theme-twisty {
  margin: calc(var(--icon-top-margin) - 1px) 0 0 0;
}

.message.error > .icon::before {
  background-position: -12px -36px;
}

.message.warn > .icon::before {
  background-position: -24px -36px;
}

.message.info > .icon::before {
  background-position: -36px -36px;
}

/* The bubble that shows the number of times a message is repeated */
.webconsole-output-wrapper .message-repeats {
  background-color: var(--repeat-bubble-background-color);
  font-weight: normal;
  font-size: 0.8em;
}

/* Prefix text that can be set by ConsoleAPI option */
.webconsole-output-wrapper .console-message-prefix {
  color: var(--theme-comment);
}

/* Network Messages */

.webconsole-output-wrapper .message.network .method {
  margin-inline-end: 5px;
}

.webconsole-output-wrapper .message.network .xhr {
  background-color: var(--theme-comment);
  color: white;
  border-radius: 2px;
  font-weight: normal;
  line-height: inherit;
}

.webconsole-output-wrapper .message.network:not(.navigation-marker) .url {
  color: var(--theme-comment);
  font-style: inherit;
}

.webconsole-output-wrapper .message.network .status {
  color: var(--theme-highlight-blue);
  font-style: inherit;
}

.webconsole-output-wrapper .message.network .status .status-info .status-code {
  padding: 0 2px;
  border-radius: 3px;
  text-decoration: none;
  font-style: normal;
}

.webconsole-output-wrapper .message.network .status .status-info .status-code:not([data-code^="3"]) {
  color: var(--theme-body-background);
}

.status-code[data-code^="1"] {
  background-color: var(--status-code-color-1xx);
}

.status-code[data-code^="2"] {
  background-color: var(--status-code-color-2xx);
}

.status-code[data-code^="3"] {
  background-color: var(--status-code-color-3xx);
}

.status-code[data-code^="4"] {
  background-color: var(--status-code-color-4xx);
}

.status-code[data-code^="5"] {
  background-color: var(--status-code-color-5xx);
}

.network.message .network-info {
  display: none;
  margin-top: 8px;
  border: solid 1px var(--theme-splitter-color);
}

.network.message.open .network-info {
  display: block;
}

.network.message .network-info .panels {
  max-height: 250px;
  min-height: 100px;
}

/* Hide 'Edit And Resend' button since the feature isn't
   supported in the Console panel. */
.network.message #headers-panel .edit-and-resend-button {
  display: none;
}

.network .message-flex-body > .message-body {
  display: flex;
  flex-wrap: wrap;
}

/* Output Wrapper */

.webconsole-output-wrapper .message .indent {
  display: inline-block;
  border-inline-end: solid 1px var(--console-output-indent-border-color);
}
.webconsole-output-wrapper .message .indent[data-indent="0"] {
  border-inline-end: none;
}

.message.startGroup .indent,
.message.startGroupCollapsed .indent {
  border-inline-end-color: transparent;
  margin-inline-end: 5px;
}

.message.startGroup .icon,
.message.startGroupCollapsed .icon {
  display: none;
}

/*
 * Open DOMNode in inspector button. Style need to be reset in the new
 * console since its style is already defined in reps.css .
 */
.webconsole-output-wrapper .open-inspector {
  background: unset;
  padding-left: unset;
  margin-left: unset;
  cursor: unset;
}

/* Stacktraces */
.webconsole-output-wrapper  .stacktrace {
  border: 1px solid var(--attachment-border-color);
  border-radius: 2px;
  margin-block-end: var(--attachment-margin-block-end);
}

.theme-dark .webconsole-output-wrapper .message.error .stacktrace,
.theme-light .webconsole-output-wrapper .message.error .stacktrace {
  /* Removing specificity from the old console */
  background-color: inherit;
}

/* console.table() */
.new-consoletable {
  width: 100%;
  --consoletable-border: var(--theme-splitter-color);
  margin-block-end: var(--attachment-margin-block-end);
  color: var(--theme-body-color);
  display: grid;
  max-height: 250px;
  overflow-y: auto;
  border: 1px solid var(--consoletable-border);
  /* border at the left side will be added by the inner divs [role=gridcell] */
  border-left: none;
}

.new-consoletable > div {
  border-left: 1px solid var(--consoletable-border);
}

.new-consoletable-header {
  position: sticky;
  top: 0;
  border-bottom: 1px solid var(--consoletable-border);
  background-color: var(--theme-toolbar-background);
  color: var(--theme-body-color);
  margin: 0;
  padding: 5px 4px;
  font-weight: inherit;
  z-index: 1;
}

.new-consoletable > [role=gridcell] {
  background-color: var(--theme-body-background);
  padding: 3px 4px;
  min-width: 100px;
  color: var(--theme-body-color);
}

.new-consoletable > [role=gridcell].even {
  background-color: var(--table-zebra-background);
}

/* Layout */
.webconsole-output {
  flex: 1;
  direction: ltr;
  overflow: auto;
  -moz-user-select: text;
  position: relative;
  grid-row: 2 / 3;
}

html,
body {
  height: 100%;
  margin: 0;
  padding: 0;
}

body {
  overflow: hidden;
}

#app-wrapper {
  height: 100%;
  display: flex;
  flex-direction: column;
}

body #output-container {
  flex: 1;
  overflow: hidden;
}

.webconsole-output-wrapper {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-rows: auto 1fr;
  height: 100%;
  width: 100vw;
}

/* Object Inspector */
.webconsole-output-wrapper .object-inspector.tree {
  display: inline-block;
}

.webconsole-output-wrapper .object-inspector.tree .tree-indent {
  border-inline-start-color: var(--console-output-indent-border-color);
}

.webconsole-output-wrapper .object-inspector.tree .tree-node:hover:not(.focused) {
  background-color: var(--object-inspector-hover-background);
}

/*
 * Make the arrow the same color and approximately the same size of the twisty icon.
 * Should be properly fixed in https://bugzilla.mozilla.org/show_bug.cgi?id=1307937.
 */
.webconsole-output-wrapper .object-inspector.tree .tree-node .arrow svg {
  width: 8px;
  fill: #AFA8AB;
  margin-inline-start: 1px;
}

.theme-dark .webconsole-output-wrapper .object-inspector.tree .tree-node .arrow svg {
  fill: #7F7E81;
}

/* Sidebar */
.sidebar {
  display: flex;
  grid-row: 1 / -1;
  grid-column: -1 / -2;
  background-color: var(--theme-sidebar-background);
}

.split-box.vert.sidebar {
  /* Set to prevent the sidebar from extending past the right edge of the page */
  width: unset;
}

.sidebar-wrapper {
  display: grid;
  grid-template-rows: auto 1fr;
  width: 100%;
  overflow: hidden;
}

.webconsole-sidebar-toolbar {
  grid-row: 1 / 2;
  min-height: var(--primary-toolbar-height);
  display: flex;
  justify-content: end;
}

.sidebar-contents {
  grid-row: 2 / 3;
  overflow: scroll;
}

.webconsole-sidebar-toolbar .sidebar-close-button {
  padding: 4px 0;
  margin: 0;
  margin-inline-end: -3px;
}

.sidebar-close-button::before {
  background-image: var(--close-button-image);
}

.sidebar-contents .object-inspector {
  min-width: 100%;
}

.theme-twisty {
  cursor: default;
}
PK
!<�I�5p5p chrome/devtools/skin/widgets.css/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.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/skin/breadcrumbs.css);

.theme-dark {
  --table-splitter-color: rgba(255,255,255,0.15);
  --table-zebra-background: rgba(255,255,255,0.05);
  --sidemenu-selected-arrow: url(images/item-arrow-dark-ltr.svg);
  --sidemenu-selected-arrow-rtl: url(images/item-arrow-dark-rtl.svg);
  --delete-icon: url(chrome://devtools/skin/images/vview-delete.png);
  --delete-icon-2x: url(chrome://devtools/skin/images/[email protected]);
}

.theme-light {
  --table-splitter-color: rgba(0,0,0,0.15);
  --table-zebra-background: rgba(0,0,0,0.05);
  --sidemenu-selected-arrow: url(images/item-arrow-ltr.svg);
  --sidemenu-selected-arrow-rtl: url(images/item-arrow-rtl.svg);
  --delete-icon: url(chrome://devtools/skin/images/vview-delete.png);
  --delete-icon-2x: url(chrome://devtools/skin/images/[email protected]);
}

.theme-firebug {
  --table-splitter-color: rgba(0,0,0,0.15);
  --table-zebra-background: rgba(0,0,0,0.05);
  --sidemenu-selected-arrow: url(images/item-arrow-ltr.svg);
  --sidemenu-selected-arrow-rtl: url(images/item-arrow-rtl.svg);
  --delete-icon: url(chrome://devtools/skin/images/firebug/close.svg);
  --delete-icon-2x: url(chrome://devtools/skin/images/firebug/close.svg);
}

/* Generic pane helpers */

.generic-toggled-pane {
  margin-inline-start: 0 !important;
  /* Unfortunately, transitions don't work properly with locale-aware properties,
     so both the left and right margins are set via js, while the start margin
     is always overridden here. */
}

.generic-toggled-pane[animated] {
  transition: margin 0.25s ease-in-out;
}

/* Responsive container */

.devtools-responsive-container {
  -moz-box-orient: horizontal;
}

.devtools-main-content {
  min-width: 50px;
}

.devtools-main-content,
.devtools-sidebar-tabs {
  /* Prevent some children that should be hidden from remaining visible as this is shrunk (Bug 971959) */
  position: relative;
}

@media (min-width: 701px) {
  .devtools-responsive-container .generic-toggled-pane {
    /* To hide generic-toggled-pane, negative margins are applied dynamically.
     * In the default horizontal layout, the pane is on the side and should be
     * hidden using negative margin-inline-end only.
     */
    margin-top: 0 !important;
    margin-bottom: 0 !important;
  }
}

@media (max-width: 700px) {
  .devtools-responsive-container {
    -moz-box-orient: vertical;
  }

  .devtools-responsive-container > .devtools-side-splitter {
    /* This is a normally vertical splitter, but we have turned it horizontal
       due to the smaller resolution */
    min-height: calc(var(--devtools-splitter-top-width) +
    var(--devtools-splitter-bottom-width) + 1px);
    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));

    /* Reset the vertical splitter styles */
    min-width: 0;
    border-inline-end-width: 0;
    border-inline-start-width: 0;
    margin-inline-end: 0;
    margin-inline-start: 0;

    /* In some edge case the cursor is not changed to ns-resize */
    cursor: ns-resize;
  }

  .devtools-responsive-container > .devtools-sidebar-tabs:not(.pane-collapsed) {
    /* When the panel is collapsed min/max height should not be applied because
       collapsing relies on negative margins, which implies constant height. */
    min-height: 35vh;
    max-height: 75vh;
  }

  .devtools-responsive-container .generic-toggled-pane {
    /* To hide generic-toggled-pane, negative margins are applied dynamically.
     * If a vertical layout, the pane is on the bottom and should be hidden
     * using negative bottom margin only.
     */
    margin-inline-end: 0 !important;
  }
}

/* SimpleListWidget */

.simple-list-widget-container {
  /* Hack: force hardware acceleration */
  transform: translateZ(1px);
}

.simple-list-widget-item.selected {
  background-color: var(--theme-selection-background);
  color: var(--theme-selection-color);
}

.theme-dark .simple-list-widget-item:not(.selected):hover {
  background-color: rgba(255,255,255,.05);
}

.theme-light .simple-list-widget-item:not(.selected):hover {
  background-color: rgba(0,0,0,.05);
}

.simple-list-widget-empty-text,
.simple-list-widget-perma-text {
  padding: 4px 8px;
}

.simple-list-widget-empty-text,
.simple-list-widget-perma-text {
  color: var(--theme-body-color-alt);
}

/* FastListWidget */

.fast-list-widget-container {
  /* Hack: force hardware acceleration */
  transform: translateZ(1px);
}

.fast-list-widget-empty-text {
  padding: 4px 8px;
}

.fast-list-widget-empty-text {
  color: var(--theme-body-color-alt);
}

/* SideMenuWidget */

.side-menu-widget-container {
  /* Hack: force hardware acceleration */
  transform: translateZ(1px);
}

/* SideMenuWidget container */

.side-menu-widget-container[with-arrows=true] .side-menu-widget-item {
  /* To compensate for the arrow image's dark margin. */
  margin-inline-end: -1px;
}

/* SideMenuWidget groups */

.side-menu-widget-group-title {
  padding: 4px;
  font-weight: 600;
  border-bottom: 1px solid rgba(128,128,128,0.15);
}

.side-menu-widget-group-title + .side-menu-widget-group-list .side-menu-widget-item-contents {
  padding-inline-start: 20px;
}

/* SideMenuWidget items */

.side-menu-widget-item {
  border-bottom: 1px solid rgba(128,128,128,0.15);
  background-clip: padding-box;
}

.side-menu-widget-item.selected {
  background-color: var(--theme-selection-background);
  color: var(--theme-selection-color);
}

.side-menu-widget-item-arrow {
  margin-inline-start: -7px;
  width: 7px; /* The image's width is 7 pixels */
}

.side-menu-widget-item.selected > .side-menu-widget-item-arrow {
  background-image: var(--sidemenu-selected-arrow);
  background-size: auto;
  background-repeat: no-repeat;
  background-position: center right;
}

.side-menu-widget-item.selected > .side-menu-widget-item-arrow:-moz-locale-dir(rtl) {
  background-image: var(--sidemenu-selected-arrow-rtl);
  background-position: center left;
}

/* SideMenuWidget items contents */

.side-menu-widget-item-contents {
  padding: 4px;
  /* To avoid having content overlapping the arrow image. */
  padding-inline-end: 8px;
}

.side-menu-widget-item-other {
  /* To avoid having content overlapping the arrow image. */
  padding-inline-end: 8px;
  /* To compensate for the .side-menu-widget-item-contents padding. */
  margin-inline-start: -4px;
  margin-inline-end: -8px;
}

.side-menu-widget-group-title + .side-menu-widget-group-list .side-menu-widget-item-other {
  /* To compensate for the .side-menu-widget-item-contents padding. */
  margin-inline-start: -20px;
}

.side-menu-widget-item.selected .side-menu-widget-item-other:not(.selected) {
  background-color: var(--theme-sidebar-background);
  box-shadow: inset 2px 0 0 var(--theme-selection-background);
  color: var(--theme-body-color);
}

.side-menu-widget-item.selected .side-menu-widget-item-other.selected {
  background-color: var(--theme-selection-background);
}

.side-menu-widget-item-other:first-of-type {
  margin-top: 4px;
}

.side-menu-widget-item-other:last-of-type {
  margin-bottom: -4px;
}

/* SideMenuWidget checkboxes */

.side-menu-widget-group-checkbox {
  margin: 0;
  margin-inline-end: 4px;
}

.side-menu-widget-item-checkbox {
  margin: 0;
  margin-inline-start: 4px;
}

/* SideMenuWidget misc */

.side-menu-widget-empty-text {
  padding: 4px 8px;
  background-color: var(--theme-sidebar-background);
}

/* VariablesView */

.variables-view-container {
  /* Hack: force hardware acceleration */
  transform: translateZ(1px);
}

.variables-view-empty-notice {
  padding: 2px;
}

.variables-view-empty-notice {
  color: var(--theme-body-color-alt);
}

.variables-view-scope:focus > .title,
.variable-or-property:focus > .title {
  background-color: var(--theme-selection-background);
  color: var(--theme-selection-color);
}

.variables-view-scope > .title {
  border-top-width: 1px;
  border-top-style: solid;
  margin-top: -1px;
}

/* Custom scope stylings */

.variables-view-watch-expressions .title > .name  {
  max-width: 14em;
}

/* Generic variables traits */

.variables-view-variable:not(:last-child) {
  border-bottom: 1px solid rgba(128, 128, 128, .15);
}

.theme-firebug .variables-view-variable {
  border-bottom: 1px solid transparent;
}

.variables-view-variable > .title > .name {
  font-weight: 600;
}

/* Generic variables *and* properties traits */

.variable-or-property:focus > .title > label {
  color: inherit !important;
}

.variables-view-container .theme-twisty {
  margin: 2px;
}

.variable-or-property > .title > .theme-twisty {
  margin-inline-start: 5px;
}

.variable-or-property:not([untitled]) > .variables-view-element-details {
  margin-inline-start: 7px;
}

/* Traits applied when variables or properties are changed or overridden */

.variable-or-property:not([overridden]) {
  transition: background 1s ease-in-out;
}

.variable-or-property:not([overridden])[changed] {
  transition-duration: .4s;
}

.variable-or-property[overridden] {
  background: rgba(128,128,128,0.05);
}

.variable-or-property[overridden] .title > label {
  /* Cross out the title for this variable and all child properties. */
  font-style: italic;
  text-decoration: line-through;
  border-bottom: none !important;
  color: rgba(128,128,128,0.9);
  opacity: 0.7;
}

/* Traits applied when variables or properties are editable */

.variable-or-property[editable] > .title > .value {
  cursor: text;
}

.variable-or-property[overridden] .title > .value {
  /* Disallow editing this variable and all child properties. */
  pointer-events: none;
}

/* Custom configurable/enumerable/writable or frozen/sealed/extensible
 * variables and properties */

.variable-or-property[non-enumerable]:not([self]):not([pseudo-item]) > .title > .name {
  opacity: 0.6;
}

.variable-or-property-non-writable-icon {
  background: url("chrome://devtools/skin/images/vview-lock.png") no-repeat;
  background-size: cover;
  width: 16px;
  height: 16px;
}

@media (min-resolution: 1.1dppx) {
  .variable-or-property-non-writable-icon {
    background-image: url("chrome://devtools/skin/images/[email protected]");
  }
}

.variable-or-property-frozen-label,
.variable-or-property-sealed-label,
.variable-or-property-non-extensible-label {
  height: 16px;
  padding-inline-end: 4px;
}

.variable-or-property:not(:focus) > .title > .variable-or-property-frozen-label,
.variable-or-property:not(:focus) > .title > .variable-or-property-sealed-label,
.variable-or-property:not(:focus) > .title > .variable-or-property-non-extensible-label {
  color: #666;
}

/* Aligned values */

.variables-view-container[aligned-values] .title > .separator {
  -moz-box-flex: 1;
}

.variables-view-container[aligned-values] .title > .value {
  -moz-box-flex: 0;
  width: 70vw;
}

.variables-view-container[aligned-values] .title > .element-value-input {
  width: calc(70vw - 10px);
}

/* Actions first */

.variables-view-open-inspector {
  -moz-box-ordinal-group: 1;
}

.variables-view-edit,
.variables-view-add-property {
  -moz-box-ordinal-group: 2;
}

.variable-or-property-frozen-label,
.variable-or-property-sealed-label,
.variable-or-property-non-extensible-label,
.variable-or-property-non-writable-icon {
  -moz-box-ordinal-group: 3;
}

.variables-view-delete {
  -moz-box-ordinal-group: 4;
}

.variables-view-container[actions-first] .variables-view-delete,
.variables-view-container[actions-first] .variables-view-add-property,
.variables-view-container[actions-first] .variables-view-open-inspector {
  -moz-box-ordinal-group: 0;
}

.variables-view-container[actions-first] [invisible] {
  visibility: hidden;
}

/* Variables and properties tooltips */

.variable-or-property > tooltip > label {
  margin: 0 2px 0 2px;
}

.variable-or-property[non-enumerable] > tooltip > label.enumerable,
.variable-or-property[non-configurable] > tooltip > label.configurable,
.variable-or-property[non-writable] > tooltip > label.writable,
.variable-or-property[non-extensible] > tooltip > label.extensible {
  color: #800;
  text-decoration: line-through;
}

.variable-or-property[overridden] > tooltip > label.overridden {
  padding-inline-start: 4px;
  border-inline-start: 1px dotted #000;
}

.variable-or-property[safe-getter] > tooltip > label.WebIDL {
  padding-inline-start: 4px;
  border-inline-start: 1px dotted #000;
  color: #080;
}

/* Variables and properties editing */
.variables-view-delete,
.variables-view-edit,
.variables-view-open-inspector {
  width: 16px;
  height: 16px;
  background-size: cover;
  cursor: pointer;
}

.variables-view-delete:hover,
.variables-view-edit:hover,
.variables-view-open-inspector:hover {
  filter: var(--theme-icon-checked-filter);
}

.variables-view-delete:active,
.variables-view-edit:active,
.variables-view-open-inspector:active {
  filter: var(--theme-icon-checked-filter) brightness(0.9);
}

.variable-or-property:focus > .title > .variables-view-delete,
.variable-or-property:focus > .title > .variables-view-edit,
.variable-or-property:focus > .title > .variables-view-open-inspector {
  filter: none;
}

.variables-view-delete {
  background-image: var(--delete-icon);
}

@media (min-resolution: 1.1dppx) {
  .variables-view-delete {
    background-image: var(--delete-icon-2x);
  }
}

.variables-view-edit {
  background-image: url("chrome://devtools/skin/images/vview-edit.png");
}

@media (min-resolution: 1.1dppx) {
  .variables-view-edit {
    background-image: url("chrome://devtools/skin/images/[email protected]");
  }
}

.variables-view-open-inspector {
  background-image: url("chrome://devtools/skin/images/vview-open-inspector.png");
}

@media (min-resolution: 1.1dppx) {
  .variables-view-open-inspector {
    background-image: url("chrome://devtools/skin/images/[email protected]");
  }
}

/* Variables and properties input boxes */

.variable-or-property > .title > .separator + .element-value-input {
  margin-inline-start: -2px !important;
  margin-inline-end: 2px !important;
}

.variable-or-property > .title > .separator[hidden=true] + .element-value-input {
  margin-inline-start: 4px !important;
  margin-inline-end: 2px !important;
}

.element-name-input {
  margin-inline-start: -2px !important;
  margin-inline-end: 2px !important;
  font-weight: 600;
}

.element-value-input,
.element-name-input {
  border: 1px solid rgba(128, 128, 128, .5) !important;
  border-radius: 0;
  color: inherit;
}

/* Variables and properties searching */

.variable-or-property[unmatched] {
  border: none;
  margin: 0;
}

/* Canvas graphs */

.graph-widget-container {
  position: relative;
}

.graph-widget-canvas {
  width: 100%;
  height: 100%;
}

.graph-widget-canvas[input=hovering-background] {
  cursor: text;
}

.graph-widget-canvas[input=hovering-region] {
  cursor: pointer;
}

.graph-widget-canvas[input=hovering-selection-start-boundary],
.graph-widget-canvas[input=hovering-selection-end-boundary],
.graph-widget-canvas[input=adjusting-selection-boundary] {
  cursor: col-resize;
}

.graph-widget-canvas[input=adjusting-view-area] {
  cursor: grabbing;
}

.graph-widget-canvas[input=hovering-selection-contents] {
  cursor: grab;
}

.graph-widget-canvas[input=dragging-selection-contents] {
  cursor: grabbing;
}

/* Line graph widget */

.line-graph-widget-gutter {
  position: absolute;
  width: 10px;
  height: 100%;
  top: 0;
  left: 0;
  pointer-events: none;
  border-inline-end: 1px solid;
}

.theme-light .line-graph-widget-gutter {
  background: rgba(255,255,255,0.75);
  border-inline-end-color: rgba(255,255,255,0.25);
}

.theme-dark .line-graph-widget-gutter {
  background: rgba(0,0,0,0.5);
  border-inline-end-color: rgba(0,0,0,0.25);
}

.line-graph-widget-gutter-line {
  position: absolute;
  width: 100%;
  border-top: 1px solid;
}

.line-graph-widget-gutter-line[type=maximum] {
  border-color: #2cbb0f;
}

.line-graph-widget-gutter-line[type=minimum] {
  border-color: #ed2655;
}

.line-graph-widget-gutter-line[type=average] {
  border-color: #d97e00;
}

.line-graph-widget-tooltip {
  position: absolute;
  border-radius: 2px;
  line-height: 15px;
  padding-inline-start: 6px;
  padding-inline-end: 6px;
  transform: translateY(-50%);
  font-size: 0.8rem !important;
  z-index: 1;
  pointer-events: none;
}

.theme-light .line-graph-widget-tooltip {
  background: rgba(255,255,255,0.75);
}

.theme-dark .line-graph-widget-tooltip {
  background: rgba(0,0,0,0.5);
}

.line-graph-widget-tooltip[with-arrows=true]::before {
  content: "";
  position: absolute;
  border-top: 3px solid transparent;
  border-bottom: 3px solid transparent;
  top: calc(50% - 3px);
}

.line-graph-widget-tooltip[arrow=start][with-arrows=true]::before {
  border-inline-end: 3px solid;
  left: -3px;
}

.line-graph-widget-tooltip[arrow=end][with-arrows=true]::before {
  border-inline-start: 3px solid;
  right: -3px;
}

.theme-light .line-graph-widget-tooltip[arrow=start][with-arrows=true]::before {
  border-inline-end-color: rgba(255,255,255,0.75);
}

.theme-dark .line-graph-widget-tooltip[arrow=start][with-arrows=true]::before {
  border-inline-end-color: rgba(0,0,0,0.5);
}

.theme-light .line-graph-widget-tooltip[arrow=end][with-arrows=true]::before {
  border-inline-start-color: rgba(255,255,255,0.75);
}

.theme-dark .line-graph-widget-tooltip[arrow=end][with-arrows=true]::before {
  border-inline-start-color: rgba(0,0,0,0.5);
}

.line-graph-widget-tooltip[type=maximum] {
  left: 14px;
}

.line-graph-widget-tooltip[type=minimum] {
  left: 14px;
}

.line-graph-widget-tooltip[type=average] {
  right: 4px;
}

.line-graph-widget-tooltip > [text=info] {
  color: var(--theme-content-color1);
}

.line-graph-widget-tooltip > [text=value] {
  margin-inline-start: 3px;
}

.line-graph-widget-tooltip > [text=metric] {
  margin-inline-start: 1px;
  color: var(--theme-content-color3);
}

.theme-light .line-graph-widget-tooltip > [text=value],
.theme-light .line-graph-widget-tooltip > [text=metric] {
  text-shadow: 1px  0px rgba(255,255,255,0.5),
              -1px  0px rgba(255,255,255,0.5),
               0px -1px rgba(255,255,255,0.5),
               0px  1px rgba(255,255,255,0.5);
}

.theme-dark .line-graph-widget-tooltip > [text=value],
.theme-dark .line-graph-widget-tooltip > [text=metric] {
  text-shadow: 1px  0px rgba(0,0,0,0.5),
              -1px  0px rgba(0,0,0,0.5),
               0px -1px rgba(0,0,0,0.5),
               0px  1px rgba(0,0,0,0.5);
}

.line-graph-widget-tooltip[type=maximum] > [text=value] {
  color: var(--theme-highlight-green);
}

.line-graph-widget-tooltip[type=minimum] > [text=value] {
  color: var(--theme-highlight-red);
}

.line-graph-widget-tooltip[type=average] > [text=value] {
  color: var(--theme-highlight-orange);
}

/* Bar graph widget */

.bar-graph-widget-legend {
  position: absolute;
  top: 4px;
  left: 8px;
  color: #292e33;
  font-size: 80%;
  pointer-events: none;
}

.bar-graph-widget-legend-item {
  float: left;
  margin-inline-end: 8px;
}

.bar-graph-widget-legend-item > [view="color"],
.bar-graph-widget-legend-item > [view="label"] {
  vertical-align: middle;
}

.bar-graph-widget-legend-item > [view="color"] {
  display: inline-block;
  width: 8px;
  height: 8px;
  border: 1px solid #fff;
  border-radius: 1px;
  margin-inline-end: 4px;
  pointer-events: all;
  cursor: pointer;
}

.bar-graph-widget-legend-item > [view="label"] {
  text-shadow: 1px  0px rgba(255,255,255,0.8),
              -1px  0px rgba(255,255,255,0.8),
               0px -1px rgba(255,255,255,0.8),
               0px  1px rgba(255,255,255,0.8);
}

/* Table Widget */

/* Table body */

.table-widget-body > .devtools-side-splitter {
  background-color: transparent;
}

.table-widget-body {
  overflow: auto;
}

.table-widget-body,
.table-widget-empty-text {
  background-color: var(--theme-body-background);
}

/* Column Headers */

.table-widget-column-header,
.table-widget-cell {
  border-inline-end: 1px solid var(--table-splitter-color) !important;
}

/* Table widget column header colors are taken from netmonitor.inc.css to match
   the look of both the tables. */

.table-widget-column-header {
  position: sticky;
  top: 0;
  width: 100%;
  margin: 0;
  padding: 5px 0 0 !important;
  color: inherit;
  text-align: center;
  font-weight: inherit !important;
  border-image: linear-gradient(transparent 15%,
                                var(--theme-splitter-color) 15%,
                                var(--theme-splitter-color) 85%,
                                transparent 85%,
                                transparent calc(100% - 1px),
                                var(--theme-splitter-color) calc(100% - 1px)) 1 1;
  background-repeat: no-repeat;
}

.table-widget-column-header:not([sorted]):hover {
  background-image: linear-gradient(rgba(0,0,0,0.1),rgba(0,0,0,0.1));
}

.table-widget-column-header[sorted] {
  background-color: var(--theme-selection-background);
  color: var(--theme-selection-color);
  border-image: linear-gradient(var(--theme-splitter-color), var(--theme-splitter-color)) 1 1;
  box-shadow: -0.5px -0.5px 0 0.5px var(--theme-splitter-color);
  background-position: right 6px center;
}

.table-widget-column-header[sorted]:-moz-locale-dir(rtl) {
  background-position: 6px center;
}

.table-widget-column-header[sorted=ascending] {
  background-image: url("chrome://devtools/skin/images/sort-ascending-arrow.svg");
}

.table-widget-column-header[sorted=descending] {
  background-image: url("chrome://devtools/skin/images/sort-descending-arrow.svg");
}

.theme-dark .table-widget-column[readonly] {
  background-color: rgba(255,255,255,0.1);
}

.theme-light .table-widget-column[readonly] {
  background-color: rgba(0,0,0,0.1);
}

.table-widget-body .devtools-side-splitter:last-of-type {
  display: none;
}

/* Firebug theme support for table widget */

.theme-firebug .devtools-toolbar.table-widget-column-header {
  font-family: var(--proportional-font-family);
  color: var(--theme-body-color);

  /* Make sure to override the default Firebug devtools-toolbar height */
  height: 19px !important;

  /* Make sure to override the dafault .table-widget-column-header font-weight and background */
  font-weight: bold !important;
  background-image: linear-gradient(rgba(255, 255, 255, 0.05), rgba(0, 0, 0, 0.05));
  background-color: #C8D2DC !important;

  /* Vertically center header label */
  padding-top: 2px !important;
}

.theme-firebug .devtools-toolbar.table-widget-column-header[sorted] {
  background-color: #AAC3DC !important;
}

:root[platform="linux"].theme-firebug .devtools-toolbar.table-widget-column-header[sorted] {
  background-color: #FAC8AF !important;
  color: inherit !important;
}

.theme-firebug .devtools-toolbar.table-widget-column-header:hover:active {
  background-image: linear-gradient(rgba(0, 0, 0, 0.1),
                                    transparent);
}

.theme-firebug .devtools-toolbar.table-widget-column-header[sorted=descending]:not(:active) {
  background-image: url(chrome://devtools/skin/images/firebug/arrow-down.svg) !important;
}

.theme-firebug .devtools-toolbar.table-widget-column-header[sorted=ascending]:not(:active) {
  background-image: url(chrome://devtools/skin/images/firebug/arrow-up.svg) !important;
}

.theme-firebug .devtools-toolbar.table-widget-column-header[sorted]:not(:active) {
  background-position: right !important;
  background-repeat: no-repeat !important;
}

/* Cells */

.table-widget-cell {
  width: 100%;
  padding: 3px 4px;
  min-width: 100px;
  -moz-user-focus: normal;
  color: var(--theme-body-color);
}

.table-widget-cell[hidden] {
  display: none;
}

.table-widget-cell.even:not(.theme-selected) {
  background-color: var(--table-zebra-background);
}

:root:not(.no-animate) .table-widget-cell.flash-out {
  animation: flash-out 0.5s ease-in;
}

@keyframes flash-out {
  to {
    background: var(--theme-contrast-background);
  }
}

/* Empty text and initial text */

.table-widget-empty-text {
  display: none;
  text-align: center;
  font-size: large;
  margin-top: -20px !important;
}

.table-widget-body:empty + .table-widget-empty-text:not([value=""]),
.table-widget-body[empty] + .table-widget-empty-text:not([value=""]) {
  display: block;
}

/* Tree Widget */

.tree-widget-container {
  padding: 0;
  margin: 0;
  width: 100%;
  height: 100%;
  list-style: none;
  overflow: hidden;
  margin-inline-end: 40px;
}

.tree-widget-container:-moz-focusring,
.tree-widget-container *:-moz-focusring {
  outline-style: none;
}

.tree-widget-empty-text {
  padding: 10px 20px;
  font-size: medium;
  background: transparent;
  pointer-events: none;
}

/* Tree Item */

.tree-widget-container .tree-widget-item {
  padding: 2px 0px 4px;
  /* OSX has line-height 14px by default, which causes weird alignment issues
   * because of 20px high icons. thus making line-height consistent with that of
   * windows.
   */
  line-height: 17px !important;
  display: inline-block;
  width: 100%;
  word-break: keep-all; /* To prevent long urls like http://foo.com/bar from
                           breaking in multiple lines */
}

.tree-widget-container .tree-widget-children {
  margin: 0;
  padding: 0;
  list-style: none;
}

.tree-widget-item[level="1"] {
  font-weight: 700;
}

/* Twisties */
.tree-widget-item::before {
  content: "";
  width: 14px;
  height: 14px;
  float: left;
  margin: 3px 2px -3px;
  background-repeat: no-repeat;
  background-image: url("chrome://devtools/skin/images/controls.png");
  background-size: 56px 28px;
  cursor: pointer;
  background-position: -28px -14px;
}

.tree-widget-item:-moz-locale-dir(rtl)::before {
  float: right;
  transform: scaleX(-1);
}

.theme-light .tree-widget-item:not(.theme-selected)::before {
  background-position: 0 -14px;
}

.tree-widget-item[empty]::before {
  background: transparent;
}

.tree-widget-item[expanded]::before {
  background-position: -42px -14px;
}

.theme-light .tree-widget-item:not(.theme-selected)[expanded]:before {
  background-position: -14px -14px;
}

.tree-widget-item + ul {
  overflow: hidden;
  animation: collapse-tree-item 0.2s;
  max-height: 0;
}

.tree-widget-item[expanded] + ul {
  animation: expand-tree-item 0.3s;
  max-height: unset;
}

@keyframes collapse-tree-item {
  from {
    max-height: 300px;
  }
  to {
    max-height: 0;
  }
}

@keyframes expand-tree-item {
  from {
    max-height: 0;
  }
  to {
    max-height: 500px;
  }
}

@media (min-resolution: 1.1dppx) {
  .tree-widget-item:before {
    background-image: url("chrome://devtools/skin/images/[email protected]");
  }
}

/* Indentation of child items in the tree */

/* For level > 6 */
.tree-widget-item[level] + ul > li > .tree-widget-item {
  padding-inline-start: 98px;
}

/* First level */
.tree-widget-item[level="1"] + ul > li > .tree-widget-item {
  padding-inline-start: 14px;
}

/* Second level */
.tree-widget-item[level="2"] + ul > li > .tree-widget-item {
  padding-inline-start: 28px;
}

/* Third level */
.tree-widget-item[level="3"] + ul > li > .tree-widget-item {
  padding-inline-start: 42px;
}

/* Fourth level */
.tree-widget-item[level="4"] + ul > li > .tree-widget-item {
  padding-inline-start: 56px;
}

/* Fifth level */
.tree-widget-item[level="5"] + ul > li > .tree-widget-item {
  padding-inline-start: 70px;
}

/* Sixth level */
.tree-widget-item[level="6"] + ul > li > .tree-widget-item {
  padding-inline-start: 84px;
}

/* Custom icons for certain tree items indicating the type of the item */

.tree-widget-item[type]::after {
  content: "";
  float: left;
  width: 16px;
  height: 17px;
  margin-inline-end: 4px;
  background-repeat: no-repeat;
  background-size: 20px auto;
  background-position: 0 0;
  background-size: auto 20px;
  opacity: 0.75;
}

.tree-widget-item.theme-selected[type]::after {
  opacity: 1;
}

.tree-widget-item:-moz-locale-dir(rtl)::after {
  float: right;
}

.theme-light .tree-widget-item.theme-selected[type]::after,
.theme-dark .tree-widget-item[type]::after {
  filter: invert(1);
}

.tree-widget-item[type="dir"]::after {
  background-image: url(chrome://devtools/skin/images/filetypes/dir-close.svg);
  background-position: 2px 0;
  background-size: auto 16px;
  width: 20px;
}

.tree-widget-item[type="dir"][expanded]:not([empty])::after {
  background-image: url(chrome://devtools/skin/images/filetypes/dir-open.svg);
}

.tree-widget-item[type="url"]::after {
  background-image: url(chrome://devtools/skin/images/filetypes/globe.svg);
  background-size: auto 18px;
  width: 18px;
}
PK
!<f��YBBdefaults/blocklists/addons.json{"data":[{"guid":"[email protected]","blockID":"i1524","enabled":true,"last_modified":1485301116629,"details":{"who":"All Firefox users who have these versions installed.","created":"2017-01-24T22:45:39Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1333471","name":"Alibonus 1.20.9 and lower","why":"Versions 1.20.9 and lower of this add-on contain critical security issues."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"1.20.9","severity":1}],"prefs":[],"id":"a015d5a4-9184-95db-0c74-9262af2332fa","schema":1485297431051},{"guid":"{a0d7ccb3-214d-498b-b4aa-0e8fda9a7bf7}","blockID":"i1523","enabled":true,"last_modified":1485297214072,"details":{"who":"All Firefox users who have these versions of the Web of Trust add-on installed.","created":"2017-01-24T22:01:08Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1314332","name":"Web of Trust 20170120 and lower","why":"Versions 20170120 and lower of the Web of Trust add-on send excessive user data to its service, which has been reportedly shared with third parties without sufficient sanitization. These versions are also affected by a vulnerability that could lead to unwanted remote code execution."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"20170120","severity":1}],"prefs":[],"id":"2224c139-9b98-0900-61c1-04031de11ad3","schema":1485295513652},{"guid":"/^(ciscowebexstart1@cisco\\.com|ciscowebexstart_test@cisco\\.com|ciscowebexstart@cisco\\.com|ciscowebexgpc@cisco\\.com)$/","blockID":"i1522","enabled":true,"last_modified":1485215014902,"details":{"who":"All Firefox users who have any Cisco WebEx add-ons installed.","created":"2017-01-23T22:55:58Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1333225","name":"Cisco WebEx add-ons","why":"A critical security vulnerability has been discovered in Cisco WebEx add-ons that enable malicious websites to execute code on the user's system."},"versionRange":[{"targetApplication":[],"minVersion":"1.0.0","maxVersion":"1.0.1","severity":1}],"prefs":[],"id":"30368779-1d3b-490a-0a34-253085af7754","schema":1485212610474},{"guid":"{de71f09a-3342-48c5-95c1-4b0f17567554}","blockID":"i1493","enabled":true,"last_modified":1484867614757,"details":{"who":"All users who have this add-on installed.","created":"2017-01-12T22:17:59Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1329654","name":"Search for Firefox Convertor (malware)","why":"This is a malicious add-on that is installed using a fake name. It changes search and homepage settings."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"1.3.9","severity":3}],"prefs":[],"id":"d6ec9f54-9945-088e-ba68-40117eaba24e","schema":1484335370642},{"guid":"[email protected]","blockID":"i1492","enabled":true,"last_modified":1483646608603,"details":{"who":"All users who have Savogram version 1.3.2 installed. Version 1.3.1 doesn't have this problem and can be installed from the <a href=\"https://addons.mozilla.org/addon/savogram/\">add-on page</a>. Note that this is an older version, so affected users won't be automatically updated to it. New versions should correct this problem if they become available.","created":"2017-01-05T19:58:39Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1328594","name":"Savogram 1.3.2","why":"Version 1.3.2 of this add-on loads remote code and performs DOM injection in an unsafe manner."},"versionRange":[{"targetApplication":[],"minVersion":"1.3.2","maxVersion":"1.3.2","severity":1}],"prefs":[],"id":"0756ed76-7bc7-ec1e-aba5-3a9fac2107ba","schema":1483389810787},{"guid":"[email protected]","blockID":"i21","enabled":true,"last_modified":1483389809169,"details":{"who":"All users of the add-on in all Mozilla applications.","created":"2011-01-31T16:23:48Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=629717","name":"Browser Update (spyware)","why":"This add-on is adware/spyware masquerading as a Firefox update mechanism."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"dfb06be8-3594-28e4-d163-17e27119f15d","schema":1483387107003},{"guid":"{2224e955-00e9-4613-a844-ce69fccaae91}","blockID":"i7","enabled":true,"last_modified":1483389809147,"details":{"who":"All users of Internet Saving Optimizer for all Mozilla applications.","created":"2011-03-31T16:28:25Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=512406","name":"Internet Saving Optimizer (extension)","why":"This add-on causes a high volume of Firefox crashes and is considered malware."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"b9efb796-97c2-6434-d28f-acc83436f8e5","schema":1483387107003},{"guid":"[email protected]","blockID":"i43","enabled":true,"last_modified":1483389809124,"details":{"who":"All users with Firefox Access Plugin installed","created":"2011-10-11T11:24:05Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=693673","name":"Firefox Access Plugin (spyware)","why":"This add-on is spyware that reports all visited websites to a third party with no user value."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"1ed230a4-e174-262a-55ab-0c33f93a2529","schema":1483387107003},{"guid":"{8CE11043-9A15-4207-A565-0C94C42D590D}","blockID":"i10","enabled":true,"last_modified":1483389809102,"details":{"who":"All users of this add-on in all Mozilla applications.","created":"2011-03-31T16:28:25Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=541302","name":"Internal security options editor (malware)","why":"This add-on secretly hijacks all search results in most major search engines and masks as a security add-on."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"e2e0ac09-6d68-75f5-2424-140f51904876","schema":1483387107003},{"guid":"[email protected]","blockID":"i47","enabled":true,"last_modified":1483389809079,"details":{"who":"All users with any version of Free Cheesecake Factory installed on any Mozilla product.","created":"2011-12-22T13:11:36Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=713050","name":"Free Cheesecake Factory (malware)","why":"This add-on hijacks your Facebook account."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"85f5c1db-433b-bee3-2a3b-325165cacc6e","schema":1483387107003},{"guid":"[email protected]","blockID":"i48","enabled":true,"last_modified":1483389809057,"details":{"who":"All users with any version of Youtube Speed UP! installed on any Mozilla product.","created":"2011-12-29T19:48:06Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=714221","name":"Youtube Speed UP! (malware)","why":"This add-on hijacks your Facebook account."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"a93922c4-8a8a-5230-8f76-76fecb0653b6","schema":1483387107003},{"guid":"{E8E88AB0-7182-11DF-904E-6045E0D72085}","blockID":"i13","enabled":true,"last_modified":1483389809035,"details":{"who":"All users of this add-on for all Mozilla applications.","created":"2011-03-31T16:28:25Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=578085","name":"Mozilla Sniffer (malware)","why":"This add-on intercepts website login credentials and is malware. For more information, please <a href=\"http://blog.mozilla.com/addons/2010/07/13/add-on-security-announcement/\">read our security announcement</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"ebbd6de9-fc8a-3e5b-2a07-232bee589c7c","schema":1483387107003},{"guid":"[email protected]","blockID":"i44","enabled":true,"last_modified":1483389809012,"details":{"who":"All users of Lab Kit in all versions of Firefox.","created":"2011-10-11T11:51:34Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=690819","name":"Mozilla Labs: Lab Kit","why":"The Lab Kit add-on has been retired due to compatibility issues with Firefox 7 and future Firefox browser releases. You can still install Mozilla Labs add-ons <a href=\"https://addons.mozilla.org/en-US/firefox/user/5133025/\">individually</a>.\r\n\r\nFor more information, please read <a href=\"http://mozillalabs.com/blog/2011/10/lab-kit-is-retiring/\">this announcement</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"d614e9cd-220f-3a19-287b-57e122f8c4b5","schema":1483387107003},{"guid":"/^(jid0-S9kkzfTvEmC985BVmf8ZOzA5nLM@jetpack|jid1-qps14pkDB6UDvA@jetpack|jid1-Tsr09YnAqIWL0Q@jetpack|[email protected]|{38a64ef0-7181-11e3-981f-0800200c9a66}|[email protected])$/","blockID":"i1424","enabled":true,"last_modified":1483378113482,"details":{"who":"All users who have any of the affected versions installed.","created":"2016-12-21T17:22:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1325060","name":"Various vulnerable add-on versions","why":"A security vulnerability was discovered in old versions of the Add-ons SDK, which is exposed by certain old versions of add-ons. In the case of some add-ons that haven't been updated for a long time, all versions are being blocked."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"0699488d-2a19-6735-809e-f229849fe00b","schema":1483376308298},{"guid":"[email protected]","blockID":"i84","enabled":true,"last_modified":1482945810971,"details":{"who":"All Firefox users who have this add-on installed","created":"2012-04-09T10:13:51Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=743484","name":"Facebook Rosa (malware)","why":"Add-on acts like malware and performs user actions on Facebook without their consent."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"66ad8de9-311d-076c-7356-87fde6d30d8f","schema":1482945809444},{"guid":"[email protected]","blockID":"i90","enabled":true,"last_modified":1482945810949,"details":{"who":"All Firefox users who have installed this add-on.","created":"2012-05-07T08:58:30Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=752483","name":"FlashPlayer 11 (malware)","why":"This add-on is malware disguised as a Flash Player update. It can hijack Google searches and Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"d25943f1-39ef-b9ec-ab77-baeef3498365","schema":1482945809444},{"guid":"[email protected]","blockID":"i60","enabled":true,"last_modified":1482945810927,"details":{"who":"All Firefox users who have this extension installed.","created":"2012-02-02T16:38:41Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=723753","name":"Video extension (malware)","why":"Malicious extension installed under false pretenses."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"cae3093f-a7b3-5352-a264-01dbfbf347ce","schema":1482945809444},{"guid":"{8f42fb8b-b6f6-45de-81c0-d6d39f54f971}","blockID":"i82","enabled":true,"last_modified":1482945810904,"details":{"who":"All Firefox users who have installed this add-on.","created":"2012-04-09T10:04:28Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=743012","name":"Face Plus (malware)","why":"This add-on maliciously manipulates Facebook and is installed under false pretenses."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"09319ab3-55e7-fec1-44e0-84067d014b9b","schema":1482945809444},{"guid":"[email protected]","blockID":"i1233","enabled":true,"last_modified":1482945810881,"details":{"who":"Any user who has version 2.0.788, or earlier, installed.","created":"2016-06-17T14:31:57Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1280431","name":"CloudMask","why":"These versions of the add-on (before 2.0.788) execute code from a website in a privileged local browser context, potentially allowing dangerous, unreviewed, actions to affect the user's computer.  This is fixed in later versions."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"2.0.788","severity":1}],"prefs":[],"id":"2a8b40c7-a1d2-29f4-b7d7-ccfc5066bae1","schema":1482945809444},{"guid":"{95ff02bc-ffc6-45f0-a5c8-619b8226a9de}","blockID":"i105","enabled":true,"last_modified":1482945810858,"details":{"who":"All Firefox users who have this add-on installed.","created":"2012-06-08T14:34:25Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=763065","name":"Eklenti D\u00fcnyas\u0131 (malware)","why":"This is a malicious add-on that inserts scripts into Facebook and hijacks the user's session.\r\n"},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"afbbc08d-2414-f51e-fdb8-74c0a2d90323","schema":1482945809444},{"guid":"{fa277cfc-1d75-4949-a1f9-4ac8e41b2dfd}","blockID":"i77","enabled":true,"last_modified":1482945810835,"details":{"who":"All Firefox users who have installed this add-on.","created":"2012-03-22T14:39:08Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=738419","name":"Adobe Flash (malware)","why":"This add-on is malware that is installed under false pretenses as an Adobe plugin."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"81753a93-382d-5f9d-a4ca-8a21b679ebb1","schema":1482945809444},{"guid":"[email protected]","blockID":"i57","enabled":true,"last_modified":1482945810811,"details":{"who":"All Firefox users that have installed this add-on.","created":"2012-01-31T13:54:20Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=722823","name":"Divx 2012 Plugin (malware)","why":"Malware installed on false pretenses."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"4a93a0eb-a513-7272-6199-bc4d6228ff50","schema":1482945809444},{"guid":"{392e123b-b691-4a5e-b52f-c4c1027e749c}","blockID":"i109","enabled":true,"last_modified":1482945810788,"details":{"who":"All Firefox users who have this add-on installed.","created":"2012-06-29T13:20:22Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=769781","name":"Zaman Tuneline Hay\u0131r! (malware)","why":"This add-on pretends to be developed by Facebook and injects scripts that manipulate users' Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"b9a805aa-cae7-58d6-5a53-2af4442e4cf6","schema":1482945809444},{"guid":"[email protected]","blockID":"i18","enabled":true,"last_modified":1482945810764,"details":{"who":"Users of Bing Bar 6.0 and older for all versions of Firefox.","created":"2011-03-31T16:28:25Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=599971","name":"Bing Bar","why":"This add-on has security issues and was blocked at Microsoft's request. For more information, please see <a href=\"http://support.microsoft.com/kb/2430460\">this article</a>."},"versionRange":[{"targetApplication":[],"minVersion":" 0","maxVersion":"6.*","severity":1}],"prefs":[],"id":"9b2f2039-b997-8993-d6dc-d881bc1ca7a1","schema":1482945809444},{"guid":"[email protected]","blockID":"i104","enabled":true,"last_modified":1482945810740,"details":{"who":"All Firefox users who have this add-on installed.","created":"2012-06-08T14:33:31Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=763065","name":"Play Now (malware)","why":"This is a malicious add-on that inserts scripts into Facebook and hijacks the user's session.\r\n"},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"8a352dff-d09d-1e78-7feb-45dec7ace5a5","schema":1482945809444},{"guid":"[email protected]","blockID":"i2","enabled":true,"last_modified":1482945810393,"details":{"who":"Users of Firefox 3 and later with versions 1.0 through 1.3.1 of Free Download Manager","created":"2011-03-31T16:28:25Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=408445","name":"Free Download Manager","why":"This add-on causes a high volume of crashes."},"versionRange":[{"targetApplication":[{"minVersion":"3.0a1","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"}],"minVersion":"1.0","maxVersion":"1.3.1","severity":1}],"prefs":[],"id":"fc46f8e7-0489-b90f-a373-d93109479ca5","schema":1482945809444},{"guid":"[email protected]","blockID":"i56","enabled":true,"last_modified":1482945810371,"details":{"who":"All Firefox users who have this add-on installed.","created":"2012-01-30T15:41:51Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=722526","name":"Adobe Flash Update (malware)","why":"This add-on poses as an Adobe Flash update and injects malicious scripts into web pages. It hides itself in the Add-ons Manager."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"696db959-fb0b-8aa4-928e-65f157cdd77a","schema":1482945809444},{"guid":"[email protected]","blockID":"i66","enabled":true,"last_modified":1482945810348,"details":{"who":"All Firefox users who have installed this add-on.","created":"2012-02-13T15:44:20Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=726787","name":"Plug VDS (malware)","why":"Add-on behaves maliciously, and is installed under false pretenses."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"0878ce4e-b476-ffa3-0e06-21a65b7917a1","schema":1482945809444},{"guid":"{B13721C7-F507-4982-B2E5-502A71474FED}","blockID":"i8","enabled":true,"last_modified":1482945810326,"details":{"who":"Users of all versions of the original Skype Toolbar in all versions of Firefox.","created":"2011-03-31T16:28:25Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=627278","name":"Original Skype Toolbar","why":"This add-on causes a high volume of Firefox crashes and introduces severe performance issues. Please <a href=\"http://www.skype.com/intl/en/get-skype/on-your-computer/click-and-call/\">update to the latest version</a>. For more information, please <a href=\"http://blog.mozilla.com/addons/2011/01/20/blocking-the-skype-toolbar-in-firefox/\">read our announcement</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"5a320611-59a3-0eee-bb30-9052be870e00","schema":1482945809444},{"guid":"[email protected]","blockID":"i11","enabled":true,"last_modified":1482945810303,"details":{"who":"Users of YSlow version 2.0.5 for Firefox 3.5.7 and later.","created":"2011-03-31T16:28:25Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=542686","name":"YSlow","why":"This add-on causes a high volume of Firefox crashes and other stability issues. Users should <a href=\"https://addons.mozilla.org/firefox/addon/yslow/\">update to the latest version</a>."},"versionRange":[{"targetApplication":[{"minVersion":"3.5.7","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"}],"minVersion":"2.0.5","maxVersion":"2.0.5","severity":1}],"prefs":[],"id":"a9b34e8f-45ce-9217-b791-98e094c26352","schema":1482945809444},{"guid":"[email protected]","blockID":"i63","enabled":true,"last_modified":1482945810281,"details":{"who":"All Firefox users who have installed this add-on.","created":"2012-02-06T15:39:38Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=724691","name":"Mozilla Essentials (malware)","why":"Installs under false pretenses and delivers malware."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"18216e6f-9d70-816f-4d4c-63861f43ff3c","schema":1482945809444},{"guid":"[email protected]","blockID":"i83","enabled":true,"last_modified":1482945810259,"details":{"who":"All Firefox users who have this add-on installed.","created":"2012-04-09T10:08:22Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=743497","name":"FlashPlayer 11 (malware)","why":"This add-on is malware installed under false pretenses."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"09bb4661-331c-f7ba-865b-9e085dc437af","schema":1482945809444},{"guid":"[email protected]","blockID":"i71","enabled":true,"last_modified":1482945810236,"details":{"who":"All Firefox users who have installed this add-on.","created":"2012-02-27T10:23:23Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=730399","name":"YouTube extension (malware)","why":"Extension is malware, installed under false pretenses."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"5d389c1f-b3a0-b06f-6ffb-d1e8aa055e3c","schema":1482945809444},{"guid":"[email protected]","blockID":"i58","enabled":true,"last_modified":1482945810213,"details":{"who":"All Firefox users who have installed this add-on.","created":"2012-01-31T14:51:06Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=722844","name":"Buzz Video (malware)","why":"Malware add-on that is installed under false pretenses."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"f7aab105-e2c2-42f5-d9be-280eb9c0c8f7","schema":1482945809444},{"guid":"[email protected]","blockID":"i92","enabled":true,"last_modified":1482945810191,"details":{"who":"All Firefox users who have this add-on installed.","created":"2012-05-15T13:27:22Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=755443","name":"Lastest Flash PLayer (malware)","why":"This add-on impersonates a Flash Player update (poorly), and inserts malicious scripts into Facebook."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"7190860e-fc1f-cd9f-5d25-778e1e9043b2","schema":1482945809444},{"guid":"[email protected]","blockID":"i97","enabled":true,"last_modified":1482945810168,"details":{"who":"All Firefox users who have installed this add-on.","created":"2012-05-28T13:42:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=759164","name":"FlashPlayer 11 (malware)","why":"This add-on is malware disguised as the Flash Player plugin."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"decf93a1-2bb0-148c-a1a6-10b3757b554b","schema":1482945809444},{"guid":"[email protected]","blockID":"i73","enabled":true,"last_modified":1482945810146,"details":{"who":"All Firefox users who have installed this add-on.","created":"2012-03-15T15:03:04Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=736275","name":"Flash Player (malware)","why":"This add-on is malware disguised as Flash Player. It steals user cookies and sends them to a remote location."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"6dd66b43-897d-874a-2227-54e240b8520f","schema":1482945809444},{"guid":"[email protected]","blockID":"i59","enabled":true,"last_modified":1482945810123,"details":{"who":"All Firefox users who have installed this add-on.","created":"2012-02-02T16:32:15Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=723683","name":"Ghost Viewer (malware)","why":"Malicious add-on that automatically posts to Facebook."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"06dfe833-8c3d-90ee-3aa8-37c3c28f7c56","schema":1482945809444},{"guid":"{46551EC9-40F0-4e47-8E18-8E5CF550CFB8}","blockID":"i19","enabled":true,"last_modified":1482945810101,"details":{"who":"Users of Stylish version 1.1b1 for Firefox.","created":"2011-03-31T16:28:25Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=621660","name":"Stylish","why":"Version 1.1b1 of this add-on causes compatibility issues with Firefox. Users should update to the latest version."},"versionRange":[{"targetApplication":[],"minVersion":"1.1b1","maxVersion":"1.1b1","severity":1}],"prefs":[],"id":"aaea37e1-ff86-4565-8bd5-55a6bf942791","schema":1482945809444},{"guid":"[email protected]","blockID":"i103","enabled":true,"last_modified":1482945810078,"details":{"who":"All Firefox users who have this add-on installed.","created":"2012-06-08T14:32:51Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=763065","name":"Timeline Kapat (malware)","why":"This is a malicious add-on that inserts scripts into Facebook and hijacks the user's session."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"a9a46ab2-2f56-1046-201c-5faa3435e248","schema":1482945809444},{"guid":"[email protected]","blockID":"i67","enabled":true,"last_modified":1482945810055,"details":{"who":"All Firefox users who have installed this add-on.","created":"2012-02-18T09:10:30Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=728476","name":"Youtube Online (malware)","why":"This add-on is malware, installed under false pretenses."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"14650ece-295b-a667-f9bc-a3d973e2228c","schema":1482945809444},{"guid":"[email protected]","blockID":"i12","enabled":true,"last_modified":1482945810032,"details":{"who":"All users of this add-on for all Mozilla applications.","created":"2010-02-05T15:01:27Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=542081","name":"Master File (malware)","why":"This add-on is malware and attempts to install a Trojan on the user's computer."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"a256d79d-5af8-92e9-a29d-350adf822efe","schema":1482945809444},{"guid":"{847b3a00-7ab1-11d4-8f02-006008948af5}","blockID":"i9","enabled":true,"last_modified":1482945810003,"details":{"who":"Users of Enigmail versions older than 0.97a for Thunderbird 3 and later.","created":"2011-03-31T16:28:25Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=531047","name":"Enigmail","why":"This add-on causes a high volume of crashes and other stability issues. Users should <a href=\"https://addons.mozilla.org/en-US/thunderbird/addon/enigmail/\">update Enigmail</a>."},"versionRange":[{"targetApplication":[{"minVersion":"3.0pre","guid":"{3550f703-e582-4d05-9a08-453d09bdfdc6}","maxVersion":"*"}],"minVersion":"0","maxVersion":"0.97a","severity":1}],"prefs":[],"id":"115f46b6-059d-202a-4373-2ca79b096347","schema":1482945809444},{"guid":"[email protected]","blockID":"i14","enabled":true,"last_modified":1482945809979,"details":{"who":"Users of Firefox 4 and later with Internet Download Manager version 6.9.8 and older.","created":"2011-03-31T16:28:25Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=578443","name":"Internet Download Manager","why":"This add-on causes a high volume of crashes and has other stability issues."},"versionRange":[{"targetApplication":[{"minVersion":"3.7a1pre","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"}],"minVersion":"0","maxVersion":"6.9.8","severity":1}],"prefs":[],"id":"773ffcfb-75d1-081d-7431-ebe3fa5dbb44","schema":1482945809444},{"guid":"[email protected]","blockID":"i51","enabled":true,"last_modified":1482945809957,"details":{"who":"All Firefox users with this extension installed.","created":"2012-01-18T14:34:55Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=717165","name":"Youtube player (malware)","why":"This add-on is malware, doing nothing more than inserting advertisements into websites through iframes."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"16b2ce94-88db-0d79-33fc-a93070ceb509","schema":1482945809444},{"guid":"[email protected]","blockID":"i15","enabled":true,"last_modified":1482945809934,"details":{"who":"All users of Personas Plus 1.6 in all versions of Firefox.","created":"2011-03-31T16:28:25Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=590978","name":"Personas Plus","why":"This version of Personas Plus is incompatible with certain Firefox functionality and other add-ons. Users should upgrade to the latest version."},"versionRange":[{"targetApplication":[{"minVersion":"3.6","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"3.6.*"}],"minVersion":"1.6","maxVersion":"1.6","severity":1}],"prefs":[],"id":"e36479c6-ca00-48d4-4fd9-ec677fd032da","schema":1482945809444},{"guid":"[email protected]","blockID":"i96","enabled":true,"last_modified":1482945809912,"details":{"who":"All Firefox users who have installed this add-on.","created":"2012-05-25T09:26:47Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=758503","name":"Divx 2012 Plugins (malware)","why":"This is a malicious add-on that is disguised as a DivX plugin."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"f01be9cb-5cf2-774a-a4d7-e210a24db5b9","schema":1482945809444},{"guid":"{3252b9ae-c69a-4eaf-9502-dc9c1f6c009e}","blockID":"i17","enabled":true,"last_modified":1482945809886,"details":{"who":"Users of version 2.2 of this add-on in all versions of Firefox.","created":"2011-03-31T16:28:25Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=599971","name":"Default Manager (Microsoft)","why":"This add-on has security issues and was blocked at Microsoft's request. For more information, please see <a href=\"http://support.microsoft.com/kb/2430460\">this article</a>."},"versionRange":[{"targetApplication":[],"minVersion":"2.2","maxVersion":"2.2","severity":1}],"prefs":[],"id":"38be28ac-2e30-37fa-4332-852a55fafb43","schema":1482945809444},{"guid":"{68b8676b-99a5-46d1-b390-22411d8bcd61}","blockID":"i93","enabled":true,"last_modified":1482945809863,"details":{"who":"All Firefox users who have this add-on installed.","created":"2012-05-16T10:44:42Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=755635","name":"Zaman T\u00fcnelini Kald\u0131r! (malware)","why":"This is a malicious add-on that post content on Facebook accounts and steals user data."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"733aff15-9b1f-ec04-288f-b78a55165a1c","schema":1482945809444},{"guid":"[email protected]","blockID":"i54","enabled":true,"last_modified":1482945809840,"details":{"who":"All Firefox users that install this add-on.","created":"2012-01-26T16:17:49Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=721562","name":"Applebees Gift Card (malware)","why":"Add-on is malware installed under false pretenses."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"1372c8ab-5452-745a-461a-aa78e3e12c4b","schema":1482945809444},{"guid":"[email protected]","blockID":"i65","enabled":true,"last_modified":1482945809437,"details":{"who":"All Firefox users who have installed this add-on.","created":"2012-02-13T15:41:02Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=726803","name":"Facebook extension (malware)","why":"Add-on behaves maliciously and poses as an official Facebook add-on."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"79ad1c9b-0828-7823-4574-dd1cdd46c3d6","schema":1482945112982},{"guid":"jid0-EcdqvFOgWLKHNJPuqAnawlykCGZ@jetpack","blockID":"i62","enabled":true,"last_modified":1482945809415,"details":{"who":"All Firefox users who have installed this add-on.","created":"2012-02-06T14:46:33Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=724650","name":"YouTube extension (malware)","why":"Add-on is installed under false pretenses and delivers malware."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"5ae1e642-b53c-54c0-19e7-5562cfdac3a3","schema":1482945112982},{"guid":"{B7082FAA-CB62-4872-9106-E42DD88EDE45}","blockID":"i25","enabled":true,"last_modified":1482945809393,"details":{"who":"Users of McAfee SiteAdvisor below version 3.3.1 for Firefox 4.\r\n\r\nUsers of McAfee SiteAdvisor 3.3.1 and below for Firefox 5 and higher.","created":"2011-03-14T15:53:07Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=637542","name":"McAfee SiteAdvisor","why":"This add-on causes a high volume of crashes and is incompatible with certain versions of Firefox."},"versionRange":[{"targetApplication":[{"minVersion":"3.7a1","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"}],"minVersion":"0.1","maxVersion":"3.3.0.*","severity":1}],"prefs":[],"id":"c950501b-1f08-2ab2-d817-7c664c0d16fe","schema":1482945112982},{"guid":"{B7082FAA-CB62-4872-9106-E42DD88EDE45}","blockID":"i38","enabled":true,"last_modified":1482945809371,"details":{"who":"Users of McAfee SiteAdvisor below version 3.3.1 for Firefox 4.\r\n\r\nUsers of McAfee SiteAdvisor 3.3.1 and below for Firefox 5 and higher.","created":"2011-05-27T13:55:02Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=660111","name":"McAfee SiteAdvisor","why":"This add-on causes a high volume of crashes and is incompatible with certain versions of Firefox."},"versionRange":[{"targetApplication":[{"minVersion":"5.0a1","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"}],"minVersion":"3.3.1","maxVersion":"*","severity":1}],"prefs":[],"id":"f11de388-4511-8d06-1414-95d3b2b122c5","schema":1482945112982},{"guid":"{3f963a5b-e555-4543-90e2-c3908898db71}","blockID":"i6","enabled":true,"last_modified":1482945809348,"details":{"who":"Users of AVG SafeSearch version 8.5 and older for all Mozilla applications.","created":"2009-06-17T13:12:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=527135","name":"AVG SafeSearch","why":"This add-on causes a high volume of crashes and causes other stability issues."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"8.5","severity":1}],"prefs":[],"id":"0d6f7d4c-bf5d-538f-1ded-ea4c6b775617","schema":1482945112982},{"guid":"[email protected]","blockID":"i3","enabled":true,"last_modified":1482945809326,"details":{"who":"Users of Vietnamese Language Pack version 2.0 for all Mozilla applications.","created":"2011-03-31T16:28:25Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=432406","name":"Vietnamese Language Pack","why":"Corrupted files. For more information, please see <a href=\"http://blog.mozilla.com/security/2008/05/07/compromised-file-in-vietnamese-language-pack-for-firefox-2/\">this blog post</a>."},"versionRange":[{"targetApplication":[],"minVersion":"2.0","maxVersion":"2.0","severity":1}],"prefs":[],"id":"51d4b581-d21c-20a1-6147-b17c3adc7867","schema":1482945112982},{"guid":"[email protected]","blockID":"i55","enabled":true,"last_modified":1482945809304,"details":{"who":"All Firefox users with this add-on installed.","created":"2012-01-27T09:39:31Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=721646","name":"Plugin Video (malware)","why":"This is malware posing as video software."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"08ceedf5-c7c1-f54f-db0c-02f01f0e319a","schema":1482945112982},{"guid":"[email protected]","blockID":"i76","enabled":true,"last_modified":1482945809279,"details":{"who":"All Firefox users who have installed this add-on.","created":"2012-03-22T10:38:47Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=738282","name":"Fblixx (malware)","why":"This add-on compromises Facebook privacy and security and spams friends lists without user intervention."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"39d0a019-62fb-837b-1f1f-6831e56442b5","schema":1482945112982},{"guid":"{45147e67-4020-47e2-8f7a-55464fb535aa}","blockID":"i86","enabled":true,"last_modified":1482945809255,"details":{"who":"All Firefox users who have this add-on installed.","created":"2012-04-25T16:33:21Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=748993","name":"Mukemmel Face+","why":"This add-on injects scripts into Facebook and performs malicious activity."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"960443f9-cf48-0b71-1ff2-b8c34a3411ea","schema":1482945112982},{"guid":"{4B3803EA-5230-4DC3-A7FC-33638F3D3542}","blockID":"i4","enabled":true,"last_modified":1482945809232,"details":{"who":"Users of Firefox 3 and later with version 1.2 of Crawler Toolbar","created":"2008-07-08T10:23:31Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=441649","name":"Crawler Toolbar","why":"This add-on causes a high volume of crashes."},"versionRange":[{"targetApplication":[{"minVersion":"3.0a1","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"}],"minVersion":"1.2","maxVersion":"1.2","severity":1}],"prefs":[],"id":"a9818d53-3a6a-8673-04dd-2a16f5644215","schema":1482945112982},{"guid":"[email protected]","blockID":"i68","enabled":true,"last_modified":1482945809208,"details":{"who":"All Firefox users who have this add-on installed.","created":"2012-02-21T13:55:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=722526","name":"Flash Update (malware)","why":"Add-on is malware, installed under false pretenses."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"1ba5b46e-790d-5af2-9580-a5f1e6e65522","schema":1482945112982},{"guid":"[email protected]","blockID":"i127","enabled":true,"last_modified":1482945809185,"details":{"who":"All users who have this add-on installed.","created":"2012-08-16T13:03:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=783356","name":"Youtube Facebook Player (malware)","why":"This add-on tries to pass as a YouTube player and runs malicious scripts on webpages."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"17a8bece-e2df-a55d-8a72-95faff028b83","schema":1482945112982},{"guid":"[email protected]","blockID":"i79","enabled":true,"last_modified":1482945809162,"details":{"who":"All Firefox users who have installed this extension.","created":"2012-03-27T10:53:33Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=739482","name":"Facebook Essentials (malware)","why":"This extension is malicious and is installed under false pretenses."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"728451e8-1273-d887-37e9-5712b1cc3bff","schema":1482945112982},{"guid":"ff-ext@youtube","blockID":"i52","enabled":true,"last_modified":1482945809138,"details":{"who":"All Firefox users that have this add-on installed.","created":"2012-01-19T08:26:35Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=719296","name":"Youtube player (malware)","why":"This add-on poses as a YouTube player while posting spam into Facebook account."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"cd2dd72a-dd52-6752-a0cd-a4b312fd0b65","schema":1482945112982},{"guid":"[email protected]","blockID":"i22","enabled":true,"last_modified":1482945809115,"details":{"who":"Users of Shopper Reports version 3.1.22.0 in Firefox 4 and later.","created":"2011-02-09T17:03:39Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=630191","name":"Shopper Reports","why":"This add-on causes a high volume of Firefox crashes."},"versionRange":[{"targetApplication":[],"minVersion":"3.1.22.0","maxVersion":"3.1.22.0","severity":1}],"prefs":[],"id":"f26b049c-d856-750f-f050-996e6bec7cbb","schema":1482945112982},{"guid":"{27182e60-b5f3-411c-b545-b44205977502}","blockID":"i16","enabled":true,"last_modified":1482945809092,"details":{"who":"Users of version 1.0 of this add-on in all versions of Firefox.","created":"2011-03-31T16:28:25Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=599971","name":"Search Helper Extension (Microsoft)","why":"This add-on has security issues and was blocked at Microsoft's request. For more information, please see <a href=\"http://support.microsoft.com/kb/2430460\">this article</a>."},"versionRange":[{"targetApplication":[],"minVersion":"1.0","maxVersion":"1.0","severity":1}],"prefs":[],"id":"2655f230-11f3-fe4c-7c3d-757d37d5f9a5","schema":1482945112982},{"guid":"{841468a1-d7f4-4bd3-84e6-bb0f13a06c64}","blockID":"i46","enabled":true,"last_modified":1482945809069,"details":{"who":"Users of all versions of Nectar Search Toolbar in Firefox 9.","created":"2011-12-20T11:38:17Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=712369","name":"Nectar Search Toolbar","why":"This add-on causes crashes and other stability issues in Firefox."},"versionRange":[{"targetApplication":[{"minVersion":"9.0a1","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"9.0"}],"minVersion":"0.1","maxVersion":"*","severity":1}],"prefs":[],"id":"b660dabd-0dc0-a55c-4b86-416080b345d9","schema":1482945112982},{"guid":"[email protected]","blockID":"i5","enabled":true,"last_modified":1482945809045,"details":{"who":"Users of Daemon Tools Toolbar version 1.0.0.5 and older for all Mozilla applications.","created":"2009-02-13T18:39:01Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=459850","name":"Daemon Tools Toolbar","why":"This add-on causes a high volume of crashes."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"1.0.0.5","severity":1}],"prefs":[],"id":"8cabafd3-576a-b487-31c8-ab59e0349a0e","schema":1482945112982},{"guid":"{a3a5c777-f583-4fef-9380-ab4add1bc2a8}","blockID":"i53","enabled":true,"last_modified":1482945809021,"details":{"who":"All users of Firefox with this add-on installed.","created":"2012-01-19T15:58:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=719605","name":"Peliculas-FLV (malware)","why":"This add-on is being offered as an online movie viewer, when it reality it only inserts scripts and ads into known sites."},"versionRange":[{"targetApplication":[],"minVersion":"2.0.3","maxVersion":"2.0.3","severity":3}],"prefs":[],"id":"07bc0962-60da-087b-c3ab-f2a6ab84d81c","schema":1482945112982},{"guid":"[email protected]","blockID":"i64","enabled":true,"last_modified":1482945808997,"details":{"who":"All Firefox users who have installed this add-on.","created":"2012-02-09T13:24:23Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=725777","name":"Facebook ! (malware)","why":"Malicious add-on posing as a Facebook tool."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"dd1d2623-0d15-c93e-8fbd-ba07b0299a44","schema":1482945112982},{"guid":"{28bfb930-7620-11e1-b0c4-0800200c9a66}","blockID":"i108","enabled":true,"last_modified":1482945808973,"details":{"who":"All Firefox user who have this add-on installed.","created":"2012-06-21T09:24:11Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=766852","name":"Aplicativo (malware)","why":"This is malware disguised as an Adobe product. It spams Facebook pages."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"908dc4fb-ebc9-cea1-438f-55e4507ba834","schema":1482945112982},{"guid":"[email protected]","blockID":"i78","enabled":true,"last_modified":1482945808950,"details":{"who":"All Firefox users who have installed this add-on.","created":"2012-03-26T16:46:55Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=739441","name":"Social Network Tools (malware)","why":"This add-on hijacks the Facebook UI and adds scripts to track users."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"1064cd25-3b87-64bb-b0a6-2518ad281574","schema":1482945112982},{"guid":"[email protected]","blockID":"i98","enabled":true,"last_modified":1482945808927,"details":{"who":"All Firefox users who have installed this add-on.","created":"2012-05-30T09:30:14Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=759663","name":"Youtube Video Player (malware)","why":"This add-on is malware disguised as a Youtube add-on."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"3484f860-56e1-28e8-5a70-cdcd5ab9d6ee","schema":1482945112982},{"guid":"{3a12052a-66ef-49db-8c39-e5b0bd5c83fa}","blockID":"i101","enabled":true,"last_modified":1482945808904,"details":{"who":"All Firefox users who have installed this add-on.","created":"2012-06-05T18:37:42Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=761874","name":"Timeline Remove (malware)","why":"This add-on is malware disguised as a Facebook timeline remover."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"b01b321b-6628-7166-bd15-52f21a04d8bd","schema":1482945112982},{"guid":"[email protected]","blockID":"i99","enabled":true,"last_modified":1482945808881,"details":{"who":"All Firefox users who have this add-on installed.","created":"2012-05-30T17:10:18Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=759950","name":"Flash Player (malware)","why":"This add-on is malware disguised as a Flash Player update."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"29cc4abc-4f52-01f1-eb0b-cad84ba4db13","schema":1482945112982},{"guid":"/^(@pluginscribens_firefox|[email protected]|[email protected]|[email protected]|[email protected]|[email protected]|jid0-bigoD0uivzAMmt07zrf3OHqa418@jetpack|jid0-iXbAR01tjT2BsbApyS6XWnjDhy8@jetpack)$/","blockID":"i1423","enabled":true,"last_modified":1482343886390,"details":{"who":"All users who have any of the affected versions installed.","created":"2016-12-21T17:21:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1325060","name":"Various vulnerable add-on versions","why":"A security vulnerability was discovered in old versions of the Add-ons SDK, which is exposed by certain old versions of add-ons. In the case of some add-ons that haven't been updated for a long time, all versions are being blocked."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"a58a2836-e4e7-74b5-c109-fa3d41e9ed56","schema":1482341309012},{"guid":"/^([email protected]|jid0-EYTXLS0GyfQME5irGbnD4HksnbQ@jetpack|jid1-ZjJ7t75BAcbGCX@jetpack)$/","blockID":"i1425","enabled":true,"last_modified":1482343886365,"details":{"who":"All users who have any of the affected versions installed.","created":"2016-12-21T17:23:14Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1325060","name":"Various vulnerable add-on versions","why":"A security vulnerability was discovered in old versions of the Add-ons SDK, which is exposed by certain old versions of add-ons. In the case of some add-ons that haven't been updated for a long time, all versions are being blocked."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"150e639f-c832-63d0-a775-59313b2e1bf9","schema":1482341309012},{"guid":"{cc8f597b-0765-404e-a575-82aefbd81daf}","blockID":"i380","enabled":true,"last_modified":1480349217152,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-06-19T13:03:00Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=866332","name":"Update My Browser (malware)","why":"This is a malicious add-on that hijacks Facebook accounts and performs unwanted actions on behalf of the user."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"4950d7aa-c602-15f5-a7a2-d844182d5cbd","schema":1480349193877},{"guid":"[email protected]","blockID":"i470","enabled":true,"last_modified":1480349217071,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-11-07T15:38:26Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=935779","name":"Installer bundle (malware)","why":"This add-on is part of a malicious Firefox installer bundle."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"649dd933-debf-69b7-020f-496c2c9f99c8","schema":1480349193877},{"guid":"59D317DB041748fdB89B47E6F96058F3@jetpack","blockID":"i694","enabled":true,"last_modified":1480349217005,"details":{"who":"All Firefox users who have this add-ons installed. Users who wish to continue using this add-on can enable it in the Add-ons Manager.","created":"2014-08-21T13:46:30Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1053540","name":"JsInjectExtension","why":"This is a suspicious add-on that appears to be installed without user consent, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"75692bd4-18e5-a9be-7ec3-9327e159ef68","schema":1480349193877},{"guid":"/^({bfec236d-e122-4102-864f-f5f19d897f5e}|{3f842035-47f4-4f10-846b-6199b07f09b8}|{92ed4bbd-83f2-4c70-bb4e-f8d3716143fe})$/","blockID":"i527","enabled":true,"last_modified":1480349216927,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2013-12-20T14:13:38Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=949566","name":"KeyBar add-on","why":"The installer that includes this add-on violates the <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> by making changes that can't be easily reverted and uses multiple IDs."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"6d68dd97-7965-0a84-8ca7-435aac3c8040","schema":1480349193877},{"guid":"[email protected]","blockID":"i246","enabled":true,"last_modified":1480349216871,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-01-14T09:17:47Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=830159","name":"Lastest Adobe Flash Player (malware)","why":"This is an add-on that poses as the Adobe Flash Player and runs malicious code in the user's system."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"2004fba1-74bf-a072-2a59-6e0ba827b541","schema":1480349193877},{"guid":"[email protected]","blockID":"i312","enabled":true,"last_modified":1480349216827,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-03-06T14:14:05Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=835665","name":"Coupon Companion","why":"This add-on doesn't follow our <a href=\"https://developer.mozilla.org/en-US/docs/Addons/Add-on_guidelines\">Add-on Guidelines</a>, bypassing our third party install opt-in screen. Users who wish to continue using this extension can enable it in the Add-ons Manager."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"b2cf1256-dadd-6501-1f4e-25902d408692","schema":1480349193877},{"guid":"[email protected]","blockID":"i602","enabled":true,"last_modified":1480349216765,"details":{"who":"All Firefox users who have these versions of the Ask Toolbar installed. Users who wish to continue using it can enable it in the Add-ons Manager.\r\n","created":"2014-06-12T14:18:05Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1024719","name":"Ask Toolbar (old Avira Security Toolbar bundle)","why":"Certain old versions of the Ask Toolbar are causing problems to users when trying to open new tabs. Using more recent versions of the Ask Toolbar should also fix this problem.\r\n"},"versionRange":[{"targetApplication":[],"minVersion":"3.15.8","maxVersion":"3.15.8.*","severity":1}],"prefs":[],"id":"b2b4236d-5d4d-82b2-99cd-00ff688badf1","schema":1480349193877},{"guid":"[email protected]","blockID":"i1232","enabled":true,"last_modified":1480349216711,"details":{"who":"Users on Firefox 47, and higher, using version 2.1.9.1, and earlier, of this add-on. If you wish to continue using it, you can enable it in the Add-ons Manager.","created":"2016-06-10T17:12:55Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1279561","name":"NoSquint","why":"The add-on is breaking the in-built zoom functionality on Firefox 47."},"versionRange":[{"targetApplication":[{"minVersion":"47","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"}],"minVersion":"0","maxVersion":"2.1.9.1-signed.1-signed","severity":1}],"prefs":[],"id":"30e0a35c-056a-054b-04f3-ade68b83985a","schema":1480349193877},{"guid":"{FE1DEEEA-DB6D-44b8-83F0-34FC0F9D1052}","blockID":"i364","enabled":true,"last_modified":1480349216652,"details":{"who":"All Firefox users who have this add-on installed. Users who want to enable the add-on again can do so in the Add-ons Manager.","created":"2013-06-10T16:14:41Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=867670","name":"IB Updater","why":"This add-on is side-installed with other software, and blocks setting reversions attempted by users who want to recover their settings after they are hijacked by other add-ons."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"a59b967c-66ca-7ad9-2dc6-d0ad37ded5fd","schema":1480349193877},{"guid":"[email protected]","blockID":"i352","enabled":true,"last_modified":1480349216614,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-05-14T13:42:04Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=872211","name":"SQLlite Addon (malware)","why":"Uses a deceptive name and injects ads into pages without user consent."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"8fd981ab-7ee0-e367-d804-0efe29d63178","schema":1480349193877},{"guid":"/^firefox@(albrechto|swiftbrowse|springsmart|storimbo|squirrelweb|betterbrowse|lizardlink|rolimno|browsebeyond|clingclang|weblayers|kasimos|higher-aurum|xaven|bomlabio)\\.(com?|net|org|info|biz)$/","blockID":"i549","enabled":true,"last_modified":1480349216570,"details":{"who":"All Firefox users who have one or more of these add-ons installed. If you wish to continue using any of these add-ons, they can be enabled in the Add-ons Manager.","created":"2014-01-30T15:08:04Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=937405","name":"Yontoo add-ons","why":"A large amount of add-ons developed by Yontoo are known to be silently installed and otherwise violate the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"3a124164-b177-805b-06f7-70a358b37e08","schema":1480349193877},{"guid":"thefoxonlybetter@quicksaver","blockID":"i702","enabled":true,"last_modified":1480349216512,"details":{"who":"All Firefox users who have any of these versions of the add-on installed.","created":"2014-08-27T10:05:31Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1053469","name":"The Fox, Only Better (malicious versions)","why":"Certain versions of The Fox, Only Better weren't developed by the original developer, and are likely malicious in nature. This violates the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> for reusing an already existent ID."},"versionRange":[{"targetApplication":[],"minVersion":"1.10","maxVersion":"*","severity":3}],"prefs":[],"id":"60e54f6a-1b10-f889-837f-60a76a98fccc","schema":1480349193877},{"guid":"/@(ft|putlocker|clickmovie|m2k|sharerepo|smarter-?)downloader\\.com$/","blockID":"i396","enabled":true,"last_modified":1480349216487,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2013-06-25T12:48:57Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=881454","name":"PutLockerDownloader and related","why":"This group of add-ons is silently installed, bypassing our install opt-in screen. This violates our <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"e98ba6e3-f2dd-fdee-b106-3e0d2a03cda4","schema":1480349193877},{"guid":"[email protected]","blockID":"i1262","enabled":true,"last_modified":1480349216460,"details":{"who":"Anyone who has this add-on installed.","created":"2016-08-17T10:54:59Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1295616","name":"Remote Keylogger test 0 addon","why":"This add-on is a keylogger that sends the data to a remote server, and goes under the name Real_player.addon."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"81b380c0-8092-ea5e-11cd-54c7f563ff5a","schema":1480349193877},{"guid":"{f0e59437-6148-4a98-b0a6-60d557ef57f4}","blockID":"i304","enabled":true,"last_modified":1480349216402,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-02-27T13:10:18Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=845975","name":"WhiteSmoke B","why":"This add-on doesn't follow our <a href=\"https://developer.mozilla.org/en-US/docs/Addons/Add-on_guidelines\">installation guidelines</a> and is dropped silently into user's profiles."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"0469e643-1a90-f9be-4aad-b347469adcbe","schema":1480349193877},{"guid":"[email protected]","blockID":"i75","enabled":true,"last_modified":1480349216375,"details":{"who":"All Firefox 9 users on Mac OS X or Linux who have Firebug 1.9.0 installed.","created":"2012-03-21T16:00:01Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=718831","name":"Firebug","why":"Firebug 1.9.0 creates stability problems on Firefox 9, on Mac OS X and Linux. Upgrading to Firefox 10 or later, or upgrading to Firebug 1.9.1 or later fixes this problem."},"versionRange":[{"targetApplication":[{"minVersion":"9.0a1","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"9.*"}],"minVersion":"1.9.0","maxVersion":"1.9.0","severity":1}],"prefs":[],"os":"Darwin,Linux","id":"a1f9f055-ef34-1412-c39f-35605a70d031","schema":1480349193877},{"guid":"[email protected]","blockID":"i486","enabled":true,"last_modified":1480349215808,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-11-15T13:34:53Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=939254","name":"BetterSurf (malware)","why":"This add-on appears to be malware and is installed silently in violation of the <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"b9825a25-a96c-407e-e656-46a7948e5745","schema":1480349193877},{"guid":"{C7AE725D-FA5C-4027-BB4C-787EF9F8248A}","blockID":"i424","enabled":true,"last_modified":1480349215779,"details":{"who":"Users of Firefox 23 or later who have RelevantKnowledge 1.0.0.2 or lower.","created":"2013-07-01T10:45:20Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=860641","name":"RelevantKnowledge 1.0.0.2 and lower","why":"Old versions of this add-on are causing startup crashes in Firefox 23, currently on the Beta channel. RelevantKnowledge users on Firefox 23 and above should update to version 1.0.0.3 of the add-on."},"versionRange":[{"targetApplication":[{"minVersion":"23.0a1","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"}],"minVersion":"0","maxVersion":"1.0.0.2","severity":1}],"prefs":[],"id":"c888d167-7970-4b3f-240f-2d8e6f14ded4","schema":1480349193877},{"guid":"{5C655500-E712-41e7-9349-CE462F844B19}","blockID":"i966","enabled":true,"last_modified":1480349215743,"details":{"who":"All users who have this add-on installed.","created":"2015-07-17T13:42:28Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1175425","name":"Quick Translator","why":"This add-on is vulnerable to a cross-site scripting attack, putting users at risk when using it in arbitrary websites."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"1.0.1-signed","severity":1}],"prefs":[],"id":"f34b00a6-c783-7851-a441-0d80fb1d1031","schema":1480349193877},{"guid":"[email protected]","blockID":"i545","enabled":true,"last_modified":1480349215672,"details":{"who":"All Firefox users who have this add-on installed. If you wish to continue using this add-on, you can enable it in the Add-ons Manager.","created":"2014-01-30T11:52:42Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=949596","name":"SuperLyrics","why":"This add-on is in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>, using multiple add-on IDs and potentially doing other unwanted activities."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"002cd4fa-4c2b-e28b-9220-4a520f4d9ec6","schema":1480349193877},{"guid":"[email protected]","blockID":"i479","enabled":true,"last_modified":1480349215645,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-11-11T15:42:30Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=937331","name":"Mozilla Lightweight Pack (malware)","why":"This add-on is malware that hijacks Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"0549645e-5f50-5089-1f24-6e7d3bfab8e0","schema":1480349193877},{"guid":"/^brasilescape.*\\@facebook\\.com$/","blockID":"i453","enabled":true,"last_modified":1480349215591,"details":{"who":"All Firefox users who have these add-ons installed.","created":"2013-09-20T09:54:04Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=918566","name":"Brasil Escape (malware)","why":"This is a group of malicious add-ons that use deceitful names like \"Facebook Video Pack\" or \"Mozilla Service Pack\" and hijack Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"8e6b1176-1794-2117-414e-f0821443f27b","schema":1480349193877},{"guid":"[email protected]","blockID":"i952","enabled":true,"last_modified":1480349215536,"details":{"who":"All users who have this add-on installed on Thunderbird 38 and above.","created":"2015-07-15T09:35:50Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1183890","name":"FoxyProxy Basic for Thunderbird","why":"This add-on is causing consistent startup crashes on Thunderbird 38 and above."},"versionRange":[{"targetApplication":[{"minVersion":"38.0a2","guid":"{3550f703-e582-4d05-9a08-453d09bdfdc6}","maxVersion":"*"},{"minVersion":"2.35","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"*"}],"minVersion":"0","maxVersion":"3.5.5","severity":1}],"prefs":[],"id":"81658491-feda-2ed3-3c6c-8e60c2b73aee","schema":1480349193877},{"guid":"[email protected]","blockID":"i476","enabled":true,"last_modified":1480349215504,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-11-08T15:32:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=936590","name":"Mozilla Storage Service (malware)","why":"This add-on is malware that hijacks the users' Facebook account."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"92198396-8756-8d09-7f18-a68d29894f71","schema":1480349193877},{"guid":"[email protected]","blockID":"i616","enabled":true,"last_modified":1480349215479,"details":{"who":"All Firefox users who have these versions of the Ask Toolbar installed. Users who wish to continue using it can enable it in the Add-ons Manager.\r\n","created":"2014-06-12T14:24:20Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1024719","name":"Ask Toolbar (old Avira Security Toolbar bundle)","why":"Certain old versions of the Ask Toolbar are causing problems to users when trying to open new tabs. Using more recent versions of the Ask Toolbar should also fix this problem.\r\n"},"versionRange":[{"targetApplication":[],"minVersion":"3.15.28","maxVersion":"3.15.28.*","severity":1}],"prefs":[],"id":"f11b485f-320e-233c-958b-a63377024fad","schema":1480349193877},{"guid":"/^({e9df9360-97f8-4690-afe6-996c80790da4}|{687578b9-7132-4a7a-80e4-30ee31099e03}|{46a3135d-3683-48cf-b94c-82655cbc0e8a}|{49c795c2-604a-4d18-aeb1-b3eba27e5ea2}|{7473b6bd-4691-4744-a82b-7854eb3d70b6}|{96f454ea-9d38-474f-b504-56193e00c1a5})$/","blockID":"i494","enabled":true,"last_modified":1480349215454,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2013-12-02T14:52:32Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=776404","name":"uTorrent and related","why":"This add-on changes search settings without user interaction, and fails to reset them after it is removed. It also uses multiple add-on IDs for no apparent reason. This violates our <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"485210d0-8e69-3436-536f-5d1deeea4167","schema":1480349193877},{"guid":"{EB7508CA-C7B2-46E0-8C04-3E94A035BD49}","blockID":"i162","enabled":true,"last_modified":1480349215428,"details":{"who":"All Firefox users who have installed any of these add-ons.","created":"2012-10-11T12:25:36Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=799266","name":"Mozilla Safe Browsing and others (Medfos malware)","why":"This block covers a number of malicious add-ons that deceive users, using names like \"Mozilla Safe Browsing\" and \"Translate This!\", and claiming they are developed by \"Mozilla Corp.\". They hijack searches and redirects users to pages they didn't intend to go to.\r\n\r\nNote: this block won't be active until bug 799266 is fixed."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"07566aa3-4ff9-ac4f-9de9-71c77454b4da","schema":1480349193877},{"guid":"[email protected]","blockID":"i614","enabled":true,"last_modified":1480349215399,"details":{"who":"All Firefox users who have these versions of the Ask Toolbar installed. Users who wish to continue using it can enable it in the Add-ons Manager.\r\n","created":"2014-06-12T14:23:39Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1024719","name":"Ask Toolbar (old Avira Security Toolbar bundle)","why":"Certain old versions of the Ask Toolbar are causing problems to users when trying to open new tabs. Using more recent versions of the Ask Toolbar should also fix this problem.\r\n"},"versionRange":[{"targetApplication":[],"minVersion":"3.15.26","maxVersion":"3.15.26.*","severity":1}],"prefs":[],"id":"ede541f3-1748-7b33-9bd6-80e2f948e14f","schema":1480349193877},{"guid":"/^({976cd962-e0ca-4337-aea7-d93fae63a79c}|{525ba996-1ce4-4677-91c5-9fc4ead2d245}|{91659dab-9117-42d1-a09f-13ec28037717}|{c1211069-1163-4ba8-b8b3-32fc724766be})$/","blockID":"i522","enabled":true,"last_modified":1480349215360,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2013-12-20T13:15:33Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=947485","name":"appbario7","why":"The installer that includes this add-on violates the <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> by being silently installed and using multiple add-on IDs."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"580aed26-dc3b-eef8-fa66-a0a402447b7b","schema":1480349193877},{"guid":"jid0-O6MIff3eO5dIGf5Tcv8RsJDKxrs@jetpack","blockID":"i552","enabled":true,"last_modified":1480349215327,"details":{"who":"All Firefox users who have this extension installed.","created":"2014-02-19T15:26:37Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=974041","name":"Extension_Protected (malware)","why":"This extension is malware that attempts to make it impossible for a second extension and itself to be disabled, and also forces the new tab page to have a specific URL."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"e53063b4-5702-5b66-c860-d368cba4ccb6","schema":1480349193877},{"guid":"[email protected]","blockID":"i604","enabled":true,"last_modified":1480349215302,"details":{"who":"All Firefox users who have these versions of the Ask Toolbar installed. Users who wish to continue using it can enable it in the Add-ons Manager.\r\n","created":"2014-06-12T14:18:58Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1024719","name":"Ask Toolbar (old Avira Security Toolbar bundle)","why":"Certain old versions of the Ask Toolbar are causing problems to users when trying to open new tabs. Using more recent versions of the Ask Toolbar should also fix this problem.\r\n"},"versionRange":[{"targetApplication":[],"minVersion":"3.15.10","maxVersion":"3.15.11.*","severity":1}],"prefs":[],"id":"b910f779-f36e-70e1-b17a-8afb75988c03","schema":1480349193877},{"guid":"[email protected]","blockID":"i483","enabled":true,"last_modified":1480349215276,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-11-14T09:37:06Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=938473","name":"Facebook Video Pack (malware)","why":"This add-on is malware that hijacks Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"85ee7840-f262-ad30-eb91-74b3248fd13d","schema":1480349193877},{"guid":"[email protected]","blockID":"i482","enabled":true,"last_modified":1480349215249,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-11-14T09:36:55Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=938476","name":"Mozilla Security Pack (malware)","why":"This add-on is malware that hijacks Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"457a5722-be90-5a9f-5fa0-4c753e9f324c","schema":1480349193877},{"guid":"[email protected]","blockID":"i370","enabled":true,"last_modified":1480349215225,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using this add-on can enable it in the Add-ons Manager.","created":"2013-06-11T15:42:24Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=881815","name":"Happy Lyrics","why":"This add-on is silently installed into Firefox without the users' consent, violating our <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"730e616d-94a7-df0c-d31a-98b7875d60c2","schema":1480349193877},{"guid":"[email protected]","blockID":"i872","enabled":true,"last_modified":1480349215198,"details":{"who":"All users who have this add-on installed. Users who wish to continue using the add-on can enable it in the Add-ons Manager.","created":"2015-03-04T14:37:33Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1082733","name":"Search Snacks","why":"This add-on is silently installed into users' systems, in violation of our <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"7567b06f-98fb-9400-8007-5d0357c345d9","schema":1480349193877},{"guid":"{ABDE892B-13A8-4d1b-88E6-365A6E755758}","blockID":"i107","enabled":true,"last_modified":1480349215173,"details":{"who":"All Firefox users on Windows who have the RealPlayer Browser Record extension installed.","created":"2012-06-14T13:54:27Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=764210","name":"RealPlayer Browser Record Plugin","why":"The RealPlayer Browser Record extension is causing significant problems on Flash video sites like YouTube. This block automatically disables the add-on, but users can re-enable it from the Add-ons Manager if necessary.\r\n\r\nThis block shouldn't disable any other RealPlayer plugins, so watching RealPlayer content on the web should be unaffected.\r\n\r\nIf you still have problems playing videos on YouTube or elsewhere, please visit our <a href=\"http://support.mozilla.com/\">support site</a> for help."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"15.0.5","severity":1}],"prefs":[],"os":"WINNT","id":"e3b89e55-b35f-8694-6f0e-f856e57a191d","schema":1480349193877},{"guid":"/(\\{7aeae561-714b-45f6-ace3-4a8aed6e227b\\})|(\\{01e86e69-a2f8-48a0-b068-83869bdba3d0\\})|(\\{77f5fe49-12e3-4cf5-abb4-d993a0164d9e\\})/","blockID":"i436","enabled":true,"last_modified":1480349215147,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-08-09T15:04:44Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=891606","name":"Visual Bee","why":"This add-on doesn't follow the <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>, changing Firefox default settings and not reverting them on uninstall. If you want to continue using this add-on, it can be enabled in the Add-ons Manager."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"ad6dc811-ab95-46fa-4bff-42186c149980","schema":1480349193877},{"guid":"[email protected]","blockID":"i1058","enabled":true,"last_modified":1480349214743,"details":{"who":"All users who install this add-on.","created":"2015-11-24T09:03:01Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1227605","name":" AMO Validator Bypass","why":"This add-on is a proof of concept of a malicious add-on that bypasses the code validator."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"86e38e3e-a729-b5a2-20a8-4738b376eea6","schema":1480349193877},{"guid":"[email protected]","blockID":"i852","enabled":true,"last_modified":1480349214613,"details":{"who":"All Firefox users who have this add-on installed.","created":"2015-02-09T15:30:27Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1128269","name":"unIsaless","why":"This add-on is silently installed and performs unwanted actions, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"39798bc2-9c75-f172-148b-13f3ca1dde9b","schema":1480349193877},{"guid":"{394DCBA4-1F92-4f8e-8EC9-8D2CB90CB69B}","blockID":"i100","enabled":true,"last_modified":1480349214568,"details":{"who":"All Firefox users who have Lightshot 2.5.0 installed.","created":"2012-06-05T09:24:51Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=761339","name":"Lightshot","why":"The Lightshot add-on, version 2.5.0, is causing widespread and frequent crashes in Firefox. Lightshot users are strongly recommended to update to version 2.6.0 as soon as possible."},"versionRange":[{"targetApplication":[],"minVersion":"2.5.0","maxVersion":"2.5.0","severity":1}],"prefs":[],"id":"57829ea2-5a95-1b6e-953c-7c4a7b3b21ac","schema":1480349193877},{"guid":"{a7f2cb14-0472-42a1-915a-8adca2280a2c}","blockID":"i686","enabled":true,"last_modified":1480349214537,"details":{"who":"All users who have this add-on installed. Users who wish to continue using this add-on can enable it in the Add-on Manager.","created":"2014-08-06T16:35:39Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1033809","name":"HomeTab","why":"This add-on is silently installed into users' systems, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":["browser.startup.homepage","browser.search.defaultenginename"],"id":"33a8f403-b2c8-cadf-e1ba-40b39edeaf18","schema":1480349193877},{"guid":"{CA8C84C6-3918-41b1-BE77-049B2BDD887C}","blockID":"i862","enabled":true,"last_modified":1480349214479,"details":{"who":"All users who have this add-on installed. Users who wish to continue using the add-on can enable it in the Add-ons Manager.","created":"2015-02-26T12:51:25Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1131230","name":"Ebay Shopping Assistant by Spigot","why":"This add-on is silently installed into users' systems, in violation of our <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":["browser.startup.homepage","browser.search.defaultenginename"],"id":"9a9d6da2-90a1-5b71-8b24-96492d57dfd1","schema":1480349193877},{"guid":"[email protected]","blockID":"i374","enabled":true,"last_modified":1480349214427,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-06-18T13:58:38Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=781088","name":"Premium Update (malware)","why":"This is a malicious extension that hijacks Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"bb388413-60ea-c9d6-9a3b-c90df950c319","schema":1480349193877},{"guid":"[email protected]","blockID":"i350","enabled":true,"last_modified":1480349214360,"details":{"who":"All Firefox users who have this extension installed.","created":"2013-05-13T09:43:07Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=871610","name":"Mozilla Service Pack (malware)","why":"This extension is malware posing as Mozilla software. It hijacks Facebook accounts and spams other Facebook users."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"715082e8-7a30-b27b-51aa-186c38e078f6","schema":1480349193877},{"guid":"[email protected]","blockID":"i562","enabled":true,"last_modified":1480349214299,"details":{"who":"All Firefox users who have this add-on installed. If you wish to continue using it, it can be enabled in the Add-ons Manager.","created":"2014-02-27T10:00:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=948695","name":"IObit Apps Toolbar","why":"This add-on is installed silently and changes users settings without reverting them, in violation of the <a href=\"https://developer.mozilla.org/en-US/docs/Addons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"be9a54f6-20c1-7dee-3aea-300b336b2ae5","schema":1480349193877},{"guid":"{9e09ac65-43c0-4b9d-970f-11e2e9616c55}","blockID":"i376","enabled":true,"last_modified":1480349214246,"details":{"who":"All Firefox users who have installed this add-on.","created":"2013-06-18T14:16:25Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=857847","name":"The Social Networks (malware)","why":"This add-on is malware that hijacks Facebook accounts and posts content on it."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"753638b4-65ca-6d71-f1f5-ce32ba2edf3b","schema":1480349193877},{"guid":"[email protected]","blockID":"i140","enabled":true,"last_modified":1480349214216,"details":{"who":"All Firefox users who have installed this add-on.","created":"2012-09-17T16:04:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=791867","name":"Google YouTube HD Player (malware)","why":"This is a malicious add-on that tries to monetize on its users by embedding unauthorized affiliate codes on shopping websites, and sometimes redirecting users to alternate sites that could be malicious in nature."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"98150e2e-cb45-1fee-8458-28d3602ec2ec","schema":1480349193877},{"guid":"[email protected]","blockID":"i489","enabled":true,"last_modified":1480349214157,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-11-25T12:40:30Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=942699","name":"Facebook Security Service (malware)","why":"This add-on is malware that hijacks Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"6f365ff4-e48f-8a06-d19d-55e19fba81f4","schema":1480349193877},{"guid":"{bbea93c6-64a3-4a5a-854a-9cc61c8d309e}","blockID":"i1126","enabled":true,"last_modified":1480349214066,"details":{"who":"All users who have this add-on installed.","created":"2016-02-29T21:58:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1251940","name":"Tab Extension (malware)","why":"This is a malicious add-on that disables various security checks in Firefox."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"5acb9dcc-59d4-46d1-2a11-1194c4948239","schema":1480349193877},{"guid":"[email protected]","blockID":"i628","enabled":true,"last_modified":1480349214036,"details":{"who":"All Firefox users who have any of these add-ons installed. Users who wish to continue using them can enable them in the Add-ons Manager.","created":"2014-06-26T15:47:15Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=866943","name":"Iminent Minibar","why":"These add-ons have been silently installed repeatedly, and change settings without user consent, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":["browser.startup.homepage","browser.search.defaultenginename"],"id":"4387ad94-8500-d74d-68e3-20564a9aac9e","schema":1480349193877},{"guid":"{28387537-e3f9-4ed7-860c-11e69af4a8a0}","blockID":"i40","enabled":true,"last_modified":1480349214002,"details":{"who":"Users of MediaBar versions 4.3.1.00 and below in all versions of Firefox.","created":"2011-07-19T10:19:41Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=665775","name":"MediaBar (2)","why":"This add-on causes a high volume of crashes and is incompatible with certain versions of Firefox."},"versionRange":[{"targetApplication":[],"minVersion":"0.1","maxVersion":"4.3.1.00","severity":1}],"prefs":[],"id":"ff95664b-93e4-aa73-ac20-5ffb7c87d8b7","schema":1480349193877},{"guid":"{41e5ef7a-171d-4ab5-8351-951c65a29908}","blockID":"i784","enabled":true,"last_modified":1480349213962,"details":{"who":"All Firefox users who have this add-on installed.","created":"2014-11-14T14:37:56Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1073810","name":"HelpSiteExpert","why":"This add-on is silently installed into users' systems. It uses very unsafe practices to load its code, and leaks information of all web browsing activity. These are all violations of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"0c05a0bb-30b4-979e-33a7-9f3955eba17d","schema":1480349193877},{"guid":"/^({2d7886a0-85bb-4bf2-b684-ba92b4b21d23}|{2fab2e94-d6f9-42de-8839-3510cef6424b}|{c02397f7-75b0-446e-a8fa-6ef70cfbf12b}|{8b337819-d1e8-48d3-8178-168ae8c99c36}|[email protected]|[email protected])$/","blockID":"i762","enabled":true,"last_modified":1480349213913,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2014-10-17T16:58:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1082599","name":"SaveSense, neurowise, allgenius","why":"These add-ons are silently installed into users' systems, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"c5439f55-ace5-ad73-1270-017c0ba7b2ce","schema":1480349193877},{"guid":"{462be121-2b54-4218-bf00-b9bf8135b23f}","blockID":"i226","enabled":true,"last_modified":1480349213879,"details":{"who":"All Firefox users who have this add-on installed.","created":"2012-11-29T16:27:36Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=812303","name":"WhiteSmoke","why":"This add-on is silently side-installed by other software, and doesn't do much more than changing the users' settings, without reverting them on removal."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"994c6084-e864-0e4e-ac91-455083ee46c7","schema":1480349193877},{"guid":"[email protected]","blockID":"i546","enabled":true,"last_modified":1480349213853,"details":{"who":"All Firefox users who have this add-on installed. If you want to continue using it, it can be enabled in the Add-ons Manager.","created":"2014-01-30T12:26:57Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=936244","name":"BrowseFox","why":"This add-on is silently installed, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"407d8c84-8939-cd28-b284-9b680e529bf6","schema":1480349193877},{"guid":"{6926c7f7-6006-42d1-b046-eba1b3010315}","blockID":"i382","enabled":true,"last_modified":1480349213825,"details":{"who":"All Firefox users who have this add-on installed. Those who wish to continue using it can enable it again in the Add-ons Manager.","created":"2013-06-25T12:05:34Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=844956","name":"appbario7","why":"This add-on is silently installed, bypassing the Firefox opt-in screen and violating our <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"2367bd94-2bdd-c615-de89-023ba071a443","schema":1480349193877},{"guid":"[email protected]","blockID":"i866","enabled":true,"last_modified":1480349213799,"details":{"who":"All users who have this add-on installed. Users who wish to continue using the add-on can enable it in the Add-ons Manager.","created":"2015-02-26T13:12:47Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1131217","name":"Fast Start","why":"This add-on is silently installed into users' systems, in violation of our <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"9e730bca-c7d1-da82-64f6-c74de216cb7d","schema":1480349193877},{"guid":"05dd836e-2cbd-4204-9ff3-2f8a8665967d@a8876730-fb0c-4057-a2fc-f9c09d438e81.com","blockID":"i468","enabled":true,"last_modified":1480349213774,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-11-07T14:43:39Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=935135","name":"Trojan.DownLoader9.50268 (malware)","why":"This add-on appears to be part of a <a href=\"https://www.drwebhk.com/en/virus_techinfo/Trojan.DownLoader9.50268.html\">Trojan software</a> package."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"2fd53d9b-7096-f1fb-fbcb-2b40a6193894","schema":1480349193877},{"guid":"jid1-0xtMKhXFEs4jIg@jetpack","blockID":"i586","enabled":true,"last_modified":1480349213717,"details":{"who":"All Firefox users who have this add-on installed.","created":"2014-06-03T15:50:19Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1011286","name":"ep (malware)","why":"This add-on appears to be malware installed without user consent."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"50ca2179-83ab-1817-163d-39ed2a9fbd28","schema":1480349193877},{"guid":"/^({16e193c8-1706-40bf-b6f3-91403a9a22be}|{284fed43-2e13-4afe-8aeb-50827d510e20}|{5e3cc5d8-ed11-4bed-bc47-35b4c4bc1033}|{7429e64a-1fd4-4112-a186-2b5630816b91}|{8c9980d7-0f09-4459-9197-99b3e559660c}|{8f1d9545-0bb9-4583-bb3c-5e1ac1e2920c})$/","blockID":"i517","enabled":true,"last_modified":1480349213344,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2013-12-20T12:54:33Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=947509","name":"Re-markit","why":"The installer that includes this add-on violates the <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> by silently installing the add-on, and using multiple add-on IDs."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"e88a28ab-5569-f06d-b0e2-15c51bb2a4b7","schema":1480349193877},{"guid":"[email protected]","blockID":"i782","enabled":true,"last_modified":1480349213319,"details":{"who":"All Firefox users who have this add-on installed.","created":"2014-11-12T14:20:44Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1097696","name":"SafeBrowse","why":"This add-on loads scripts with malicious code that appears intended to steal usernames, passwords, and other private information."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"edd81c91-383b-f041-d8f6-d0b9a90230bd","schema":1480349193877},{"guid":"{af95cc15-3b9b-45ae-8d9b-98d08eda3111}","blockID":"i492","enabled":true,"last_modified":1480349213294,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-12-02T12:45:06Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=945126","name":"Facebook (malware)","why":"This is a malicious Firefox extension that uses a deceptive name and hijacks users' Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"7064e9e2-fba4-7b57-86d7-6f4afbf6f560","schema":1480349193877},{"guid":"{84a93d51-b7a9-431e-8ff8-d60e5d7f5df1}","blockID":"i744","enabled":true,"last_modified":1480349213264,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2014-10-17T15:47:06Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1080817","name":"Spigot Shopping Assistant","why":"This add-on appears to be silently installed into users' systems, and changes settings without consent, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"dbc7ef8b-2c48-5dae-73a0-f87288c669f0","schema":1480349193877},{"guid":"{C3949AC2-4B17-43ee-B4F1-D26B9D42404D}","blockID":"i918","enabled":true,"last_modified":1480349213231,"details":{"who":"All Firefox users who have this add-on installed in Firefox 39 and above.\r\n","created":"2015-06-02T09:58:16Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1170633","name":"RealPlayer Browser Record Plugin","why":"Certain versions of this extension are causing startup crashes in Firefox 39 and above.\r\n"},"versionRange":[{"targetApplication":[{"minVersion":"39.0a1","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"}],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"7f2a68f3-aa8a-ae41-1e48-d1f8f63d53c7","schema":1480349193877},{"guid":"831778-poidjao88DASfsAnindsd@jetpack","blockID":"i972","enabled":true,"last_modified":1480349213194,"details":{"who":"All users who have this add-on installed.","created":"2015-08-04T15:18:03Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1190962","name":"Video patch (malware)","why":"This is a malicious add-on that hijacks Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"39471221-6926-e11b-175a-b28424d49bf6","schema":1480349193877},{"guid":"[email protected]","blockID":"i342","enabled":true,"last_modified":1480349213128,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-05-06T16:18:14Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=863385","name":"RapidFinda","why":"This add-on is silently installed, violating our <a href=\"https://developer.mozilla.org/en-US/docs/Addons/Add-on_guidelines\">Add-on Guidelines</a>. It also appears to install itself both locally and globally, producing a confusing uninstall experience."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"98fb4536-07a4-d03a-f7c5-945acecc8203","schema":1480349193877},{"guid":"{babb9931-ad56-444c-b935-38bffe18ad26}","blockID":"i499","enabled":true,"last_modified":1480349213100,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-12-04T15:22:02Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=946086","name":"Facebook Credits (malware)","why":"This is a malicious Firefox extension that uses a deceptive name and hijacks users' Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"be1d19fa-1662-322a-13e6-5fa5474f33a7","schema":1480349193877},{"guid":"{18d5a8fe-5428-485b-968f-b97b05a92b54}","blockID":"i802","enabled":true,"last_modified":1480349213074,"details":{"who":"All Firefox users who have this add-on installed.","created":"2014-12-15T10:52:49Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1080839","name":"Astromenda Search Addon","why":"This add-on is silently installed and is considered malware, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":["browser.startup.homepage","browser.search.defaultenginename"],"id":"bc846147-cdc1-141f-5846-b705c48bd6ed","schema":1480349193877},{"guid":"{b6ef1336-69bb-45b6-8cba-e578fc0e4433}","blockID":"i780","enabled":true,"last_modified":1480349213024,"details":{"who":"All Firefox users who have this add-on installed.","created":"2014-11-12T14:00:47Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1073810","name":"Power-SW","why":"This add-on is silently installed into users' systems. It uses very unsafe practices to load its code, and leaks information of all web browsing activity. These are all violations of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"3b080157-2900-d071-60fe-52b0aa376cf0","schema":1480349193877},{"guid":"[email protected]","blockID":"i196","enabled":true,"last_modified":1480349212999,"details":{"who":"All Firefox users who have these add-ons installed.","created":"2012-11-05T09:24:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=806451","name":"Codec (malware)","why":"These are malicious add-ons that are distributed with a trojan and negatively affect web browsing."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"b62597d0-d2cb-d597-7358-5143a1d13658","schema":1480349193877},{"guid":"{C3949AC2-4B17-43ee-B4F1-D26B9D42404D}","blockID":"i111","enabled":true,"last_modified":1480349212971,"details":{"who":"All Firefox users on Windows who have the RealPlayer Browser Record extension installed.","created":"2012-07-10T15:28:16Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=771802","name":"RealPlayer Browser Record Plugin","why":"The RealPlayer Browser Record extension is causing significant problems on Flash video sites like YouTube. This block automatically disables the add-on, but users can re-enable it from the Add-ons Manager if necessary.\r\n\r\nThis block shouldn't disable any other RealPlayer plugins, so watching RealPlayer content on the web should be unaffected.\r\n\r\nIf you still have problems playing videos on YouTube or elsewhere, please visit our <a href=\"http://support.mozilla.com/\">support site</a> for help."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"15.0.5","severity":1}],"prefs":[],"os":"WINNT","id":"d3f96257-7635-555f-ef48-34d426322992","schema":1480349193877},{"guid":"[email protected]","blockID":"i728","enabled":true,"last_modified":1480349212911,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2014-10-16T16:34:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1076771","name":"GGoSavee","why":"This add-on is silently installed and changes user settings without consent, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>"},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":["browser.startup.homepage"],"id":"e6bfa340-7d8a-1627-5cdf-40c0c4982e9d","schema":1480349193877},{"guid":"{6b2a75c8-6e2e-4267-b955-43e25b54e575}","blockID":"i698","enabled":true,"last_modified":1480349212871,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2014-08-21T15:46:31Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1052611","name":"BrowserShield","why":"This add-on is silently installed into users' systems, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"492e4e43-f89f-da58-9c09-d99528ee9ca9","schema":1480349193877},{"guid":"/^({65f9f6b7-2dae-46fc-bfaf-f88e4af1beca}|{9ed31f84-c8b3-4926-b950-dff74047ff79}|{0134af61-7a0c-4649-aeca-90d776060cb3}|{02edb56b-9b33-435b-b7df-b2843273a694}|{da51d4f6-3e7e-4ef8-b400-9198e0874606}|{b24577db-155e-4077-bb37-3fdd3c302bb5})$/","blockID":"i525","enabled":true,"last_modified":1480349212839,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2013-12-20T14:11:14Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=949566","name":"KeyBar add-on","why":"The installer that includes this add-on violates the <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> by making changes that can't be easily reverted and using multiple IDs."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"78562d79-9a64-c259-fb63-ce24e29bb141","schema":1480349193877},{"guid":"[email protected]","blockID":"i560","enabled":true,"last_modified":1480349212798,"details":{"who":"All Firefox users who have this add-on installed. If you wish to continue using this add-on, you can enable it in the Add-ons Manager.","created":"2014-02-27T09:57:31Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=962793","name":"Ad Removal","why":"This add-on is silently installed and changes various user settings, in violation of the <a href=\"https://developer.mozilla.org/en-US/docs/Addons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"4d150ad4-dc22-9790-07a9-36e0a23f857f","schema":1480349193877},{"guid":"[email protected]","blockID":"i445","enabled":true,"last_modified":1480349212747,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-09-04T16:53:37Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=911966","name":"YouTube Enhancer Plus (malware)","why":"This is a malicious add-on that imitates a popular video downloader extension, and attempts to hijack Facebook accounts. The add-on available in the add-ons site is safe to use."},"versionRange":[{"targetApplication":[],"minVersion":"208.7.0","maxVersion":"208.7.0","severity":3}],"prefs":[],"id":"41d75d3f-a57e-d5ad-b95b-22f5fa010b4e","schema":1480349193877},{"guid":"[email protected]","blockID":"i1264","enabled":true,"last_modified":1480349212719,"details":{"who":"All users who have version 1.6.7 or less of this add-on installed.","created":"2016-08-24T10:48:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1251911","name":"Suchpony (pre 1.6.8)","why":"Old versions of this add-on contained code from YouTube Unblocker, which was <a href=\"https://addons.mozilla.org/firefox/blocked/i1129\">originally blocked</a> due to malicious activity.  Version 1.6.8 is now okay."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"1.6.7","severity":3}],"prefs":[],"id":"1bbf00f3-53b5-3777-43c7-0a0b11f9c433","schema":1480349193877},{"guid":"{336D0C35-8A85-403a-B9D2-65C292C39087}","blockID":"i224","enabled":true,"last_modified":1480349212655,"details":{"who":"All Firefox users who have this add-on installed.","created":"2012-11-29T16:22:49Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=812292","name":"IB Updater","why":"This add-on is side-installed with other software, and blocks setting reversions attempted by users who want to recover their settings after they are hijacked by other add-ons."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"c87666e6-ec9a-2f1e-ad03-a722d2fa2a25","schema":1480349193877},{"guid":"[email protected]","blockID":"i718","enabled":true,"last_modified":1480349212277,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2014-10-02T12:21:22Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1076771","name":"YoutUbeAdBlaocke","why":"This add-on is silently installed into users' systems and changes settings without consent, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":["browser.startup.homepage"],"id":"3e1e9322-93e9-4ce1-41f5-46ad4ef1471b","schema":1480349193877},{"guid":"extension@Fast_Free_Converter.com","blockID":"i533","enabled":true,"last_modified":1480349212247,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2013-12-20T15:04:18Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=949597","name":"FastFreeConverter","why":"The installer that includes this add-on violates the <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> by silently installing it."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"726f5645-c0bf-66dc-a97a-d072b46e63e7","schema":1480349193877},{"guid":"@stopad","blockID":"i1266","enabled":true,"last_modified":1480349212214,"details":{"who":"Users who have version 0.0.4 and earlier of the add-on installed.","created":"2016-08-30T12:24:20Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1298780","name":"Stop Ads Addon","why":"Stop Ads sends each visited url to a third party server which is not necessary for the add-on to work or disclosed in a privacy policy or user opt-in.  Versions 0.0.4 and earlier are affected."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"0.0.4","severity":1}],"prefs":[],"id":"3d1893dd-2092-d1f7-03f3-9629b7d7139e","schema":1480349193877},{"guid":"703db0db-5fe9-44b6-9f53-c6a91a0ad5bd@7314bc82-969e-4d2a-921b-e5edd0b02cf1.com","blockID":"i519","enabled":true,"last_modified":1480349212183,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2013-12-20T12:57:27Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=947509","name":"Re-markit","why":"The installer that includes this add-on violates the <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> by silently installing the add-on, and using multiple add-on IDs."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"a27d0f9f-7708-3d5f-82e1-e3f29e6098a0","schema":1480349193877},{"guid":"[email protected]","blockID":"i662","enabled":true,"last_modified":1480349212157,"details":{"who":"All Firefox users who have this add-on installed.","created":"2014-07-10T15:39:44Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1036757","name":"Taringa MP3 / Adobe Flash","why":"This is a malicious add-on that attempts to hide itself by impersonating the Adobe Flash plugin."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"f43859d4-46b7-c028-4738-d40a73ddad7b","schema":1480349193877},{"guid":"{13c9f1f9-2322-4d5c-81df-6d4bf8476ba4}","blockID":"i348","enabled":true,"last_modified":1480349212102,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-05-08T15:55:57Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=867359","name":"mywebsearch","why":"This add-on is silently installed, violating our <a href=\"https://developer.mozilla.org/en-US/docs/Addons/Add-on_guidelines\">Add-on Guidelines</a>. It also fails to revert settings changes on removal.\r\n\r\nUsers who wish to continue using this add-on can enable it in the Add-ons Manager."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"372cf3df-0810-85d8-b5d7-faffff309a11","schema":1480349193877},{"guid":"{a6e67e6f-8615-4fe0-a599-34a73fc3fba5}","blockID":"i346","enabled":true,"last_modified":1480349212077,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-05-06T17:06:06Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=867333","name":"Startnow","why":"This add-on is silently installed, violating our <a href=\"https://developer.mozilla.org/en-US/docs/Addons/Add-on_guidelines\">Add-on Guidelines</a>. Also, it doesn't reset its settings changes on uninstall.\r\n\r\nUsers who wish to continue using this add-on can enable it in the Add-ons Manager."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"1caf911c-ff2f-b0f6-0d32-29ef74be81bb","schema":1480349193877},{"guid":"[email protected]","blockID":"i652","enabled":true,"last_modified":1480349212044,"details":{"who":"All Firefox users who have this version of the add-on installed.","created":"2014-07-10T15:17:38Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1036757","name":"Save My YouTube Day!, version 67.9","why":"Certain versions of the Save My YouTube Day! extension weren't developed by the original developer, and are likely malicious in nature. This violates the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> for reusing an already existent ID."},"versionRange":[{"targetApplication":[],"minVersion":"67.9","maxVersion":"67.9","severity":3}],"prefs":[],"id":"e50c0189-a7cd-774d-702b-62eade1bf18e","schema":1480349193877},{"guid":"9518042e-7ad6-4dac-b377-056e28d00c8f@f1cc0a13-4df1-4d66-938f-088db8838882.com","blockID":"i308","enabled":true,"last_modified":1480349212020,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using this add-on can enable it in the Add-ons Manager.","created":"2013-02-28T13:48:32Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=846455","name":"Solid Savings","why":"This add-on is silently installed, bypassing our third-party opt-in screen, in violation of our <a href=\"https://developer.mozilla.org/en-US/docs/Addons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"df25ee07-74d4-ccd9-dbbe-7eb053015144","schema":1480349193877},{"guid":"jufa098j-LKooapd9jasJ9jliJsd@jetpack","blockID":"i1000","enabled":true,"last_modified":1480349211979,"details":{"who":"All users who have this add-on installed.","created":"2015-09-07T14:00:20Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1201163","name":"Secure Video (malware)","why":"This is a malicious add-on that hijacks Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"c3a98025-0f4e-3bb4-b475-97329e7b1426","schema":1480349193877},{"guid":"{46eddf51-a4f6-4476-8d6c-31c5187b2a2f}","blockID":"i750","enabled":true,"last_modified":1480349211953,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2014-10-17T16:17:47Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=963788","name":"Slick Savings","why":"This add-on is silently installed into users' systems, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"9b4ef650-e1ad-d55f-c420-4f26dbb4139c","schema":1480349193877},{"guid":"{DAC3F861-B30D-40dd-9166-F4E75327FAC7}","blockID":"i924","enabled":true,"last_modified":1480349211896,"details":{"who":"All Firefox users who have this add-on installed in Firefox 39 and above.\r\n","created":"2015-06-09T15:28:17Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1173154","name":"RealPlayer Browser Record Plugin","why":"Certain versions of this extension are causing startup crashes in Firefox 39 and above.\r\n"},"versionRange":[{"targetApplication":[{"minVersion":"39.0a1","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"}],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"be57998b-9e4d-1040-e6bb-ed9de056338d","schema":1480349193877},{"guid":"[email protected]","blockID":"i790","enabled":true,"last_modified":1480349211841,"details":{"who":"All users who have this add-on installed.","created":"2014-11-24T14:14:47Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1103516","name":"YouttubeAdBlocke","why":"This is a malicious add-on that hijacks Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"070d5747-137d-8500-8713-cfc6437558a3","schema":1480349193877},{"guid":"[email protected]","blockID":"i888","enabled":true,"last_modified":1480349211785,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using this add-on can enable it in the Add-on Manager.","created":"2015-04-10T16:27:47Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1152553","name":"Istart","why":"This add-on appears to be malware, being silently installed and hijacking user settings, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":["browser.startup.homepage","browser.search.defaultenginename"],"id":"32fad759-38d9-dad9-2295-e44cc6887040","schema":1480349193877},{"guid":"[email protected]","blockID":"i449","enabled":true,"last_modified":1480349211748,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-09-13T16:19:39Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=912742","name":"Define Ext","why":"This add-on doesn't follow our <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>. It is installed bypassing the Firefox opt-in screen, and manipulates settings without reverting them on removal. Users who wish to continue using this add-on can enable it in the Add-ons Manager."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"8fe8f509-c530-777b-dccf-d10d58ae78cf","schema":1480349193877},{"guid":"[email protected]","blockID":"i844","enabled":true,"last_modified":1480349211713,"details":{"who":"All Firefox users who have this add-on installed.","created":"2015-02-06T15:01:47Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1128324","name":"Sense","why":"This add-on is silently installed and attempts to change user settings like the home page and default search, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":["browser.startup.homepage","browser.search.defaultenginename"],"id":"f8f8695c-a356-a1d6-9291-502b377c63c2","schema":1480349193877},{"guid":"{184AA5E6-741D-464a-820E-94B3ABC2F3B4}","blockID":"i968","enabled":true,"last_modified":1480349211687,"details":{"who":"All users who have this add-on installed.","created":"2015-08-04T09:41:27Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1164243","name":"Java String Helper (malware)","why":"This is a malicious add-on that poses as a Java extension."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"fac1d2cb-eed7-fcef-5d5a-43c556371bd7","schema":1480349193877},{"guid":"7d51fb17-b199-4d8f-894e-decaff4fc36a@a298838b-7f50-4c7c-9277-df6abbd42a0c.com","blockID":"i455","enabled":true,"last_modified":1480349211660,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-09-25T10:28:36Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=919792","name":"Video Console (malware)","why":"This is a malicious extension that hijacks Facebook accounts and posts spam to the users' friends."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"dd4d2e17-4ce6-36b0-3035-93e9cc5846d4","schema":1480349193877},{"guid":"[email protected]","blockID":"i764","enabled":true,"last_modified":1480349211628,"details":{"who":"All Firefox users who have this add-on installed.","created":"2014-10-29T16:43:15Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1073810","name":"ProfSitez","why":"This add-on is silently installed into users' systems. It uses very unsafe practices to load its code, and leaks information of all web browsing activity. These are all violations of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"684ad4fd-2cbd-ce2a-34cd-bc66b20ac8af","schema":1480349193877},{"guid":"/^toolbar[0-9]*@findwide\\.com$/","blockID":"i874","enabled":true,"last_modified":1480349211601,"details":{"who":"All users who have this add-on installed. Users who wish to continue using the add-on can enable it in the Add-ons Manager.","created":"2015-03-04T14:54:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1082758","name":"FindWide Toolbars","why":"This add-on is silently installed into users' systems, in violation of our <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>.\r\n"},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":["browser.startup.homepage","browser.search.defaultenginename"],"id":"a317ad9f-af4d-e086-4afd-cd5eead1ed62","schema":1480349193877},{"guid":"{25D77636-38B1-1260-887C-2D4AFA92D6A4}","blockID":"i536","enabled":true,"last_modified":1480349211555,"details":{"who":"All Firefox users who have this extension installed.","created":"2014-01-13T10:36:05Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=959279","name":"Microsoft DirectInput Object (malware)","why":"This is a malicious extension that is installed alongside a trojan. It hijacks searches on selected sites."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"cd174588-940e-f5b3-12ea-896c957bd4b3","schema":1480349193877},{"guid":"[email protected]","blockID":"i216","enabled":true,"last_modified":1480349211163,"details":{"who":"All Firefox users who have installed version 1.5.7.5 of the Free Download Manager extension.","created":"2012-11-27T12:47:51Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=789700","name":"Free Download Manager","why":"Version 1.5.7.5 of the Free Download Manager extension is causing frequent crashes in recent versions of Firefox. Version 1.5.7.6 corrects this problem, but it is currently not available on addons.mozilla.org. We recommend all users to update to the new version once it becomes available."},"versionRange":[{"targetApplication":[],"minVersion":"1.5.7.5","maxVersion":"1.5.7.5","severity":1}],"prefs":[],"id":"736417a2-6161-9973-991a-aff566314733","schema":1480349193877},{"guid":"{badea1ae-72ed-4f6a-8c37-4db9a4ac7bc9}","blockID":"i543","enabled":true,"last_modified":1480349211119,"details":{"who":"All Firefox users who have this add-on installed. If you wish to continue using this add-on, you can enable it in the Add-ons Manager.","created":"2014-01-28T14:28:18Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=963809","name":"Address Bar Search","why":"This add-on is apparently malware that is silently installed into users' systems, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"8c1dd68e-7df6-0c37-2f41-107745a7be54","schema":1480349193877},{"guid":"addon@gemaoff","blockID":"i1230","enabled":true,"last_modified":1480349211044,"details":{"who":"All users who have this add-on installed.","created":"2016-06-08T16:15:11Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1251911","name":"YouTube Unblocker (addon@gemaoff)","why":"These add-ons are copies of YouTube Unblocker, which was <a href=\"https://addons.mozilla.org/firefox/blocked/i1129\">originally blocked</a> due to malicious activity."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"6bc49e9f-322f-9952-15a6-0a723a61c2d9","schema":1480349193877},{"guid":"{d87d56b2-1379-49f4-b081-af2850c79d8e}","blockID":"i726","enabled":true,"last_modified":1480349211007,"details":{"who":"All Firefox users who have this add-on installed.","created":"2014-10-13T16:01:03Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1080835","name":"Website Xplorer Lite","why":"This add-on is silently installed into users' systems. It uses very unsafe practices to load its code, and leaks information of all web browsing activity. These are all violations of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"7b0895b4-dd4f-1c91-f4e3-31afdbdf3178","schema":1480349193877},{"guid":"[email protected]","blockID":"i469","enabled":true,"last_modified":1480349210982,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-11-07T15:35:01Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=935779","name":"Installer bundle (malware)","why":"This add-on is part of a malicious Firefox installer bundle."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"6a11aa68-0dae-5524-cc96-a5053a31c466","schema":1480349193877},{"guid":"{c96d1ae6-c4cf-4984-b110-f5f561b33b5a}","blockID":"i808","enabled":true,"last_modified":1480349210956,"details":{"who":"All Firefox users who have this add-on installed.","created":"2014-12-19T09:36:52Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1073810","name":"Better Web","why":"This add-on is silently installed into users' systems. It uses very unsafe practices to load its code, and leaks information of all web browsing activity. These are all violations of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"0413d46b-8205-d9e0-65df-4caa3e6355c4","schema":1480349193877},{"guid":"[email protected]","blockID":"i554","enabled":true,"last_modified":1480349210931,"details":{"who":"All Firefox users who have this add-on installed. If you wish to continue using this add-on, it can be enabled in the Add-ons Manager.","created":"2014-02-19T15:28:24Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=974041","name":"Lightning SpeedDial","why":"This add-on is silently installed in Firefox and includes a companion extension that also performs malicious actions."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"875513e1-e6b1-a383-2ec5-eb4deb87eafc","schema":1480349193877},{"guid":"/^ext@bettersurfplus/","blockID":"i506","enabled":true,"last_modified":1480349210905,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-12-10T15:10:31Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=939254","name":"BetterSurf (malware)","why":"This add-on appears to be malware and is installed silently in violation of the <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"b4da06d2-a0fd-09b6-aadb-7e3b29c3be3a","schema":1480349193877},{"guid":"thefoxonlybetter@quicksaver","blockID":"i706","enabled":true,"last_modified":1480349210859,"details":{"who":"All Firefox users who have any of these versions of the add-on installed.","created":"2014-08-27T14:50:32Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1053469","name":"The Fox, Only Better (malicious versions)","why":"Certain versions of The Fox, Only Better weren't developed by the original developer, and are likely malicious in nature. This violates the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> for reusing an already existent ID."},"versionRange":[{"targetApplication":[],"minVersion":"1.6.160","maxVersion":"1.6.160","severity":3}],"prefs":[],"id":"bb2b2114-f8e7-511d-04dc-abc8366712cc","schema":1480349193877},{"guid":"[email protected]","blockID":"i336","enabled":true,"last_modified":1480349210805,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-04-22T16:10:17Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=864551","name":"CortonExt","why":"This add-on is reported to be installed without user consent, with a non-descriptive name, and ties a number of browser features to Amazon URLs, probably monetizing on affiliate codes."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"a5bdd05d-eb4c-ce34-9909-a677b4322384","schema":1480349193877},{"guid":"[email protected]","blockID":"i430","enabled":true,"last_modified":1480349210773,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-08-05T16:42:24Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=901770","name":" Mozilla Service Pack (malware)","why":"This is a malicious add-on that uses a deceptive name and hijacks social networks."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"bf1e31c7-ba50-1075-29ae-47368ac1d6de","schema":1480349193877},{"guid":"[email protected]","blockID":"i344","enabled":true,"last_modified":1480349210718,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-05-06T16:44:04Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=866944","name":"LyricsTube","why":"This add-on is silently installed, violating our <a href=\"https://developer.mozilla.org/en-US/docs/Addons/Add-on_guidelines\">Add-on Guidelines</a>.\r\n\r\nUsers who wish to continue using this add-on can enable it in the Add-ons Manager."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"424b9f39-d028-b1fb-d011-d8ffbbd20fe9","schema":1480349193877},{"guid":"{341f4dac-1966-47ff-aacf-0ce175f1498a}","blockID":"i356","enabled":true,"last_modified":1480349210665,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-05-23T14:45:35Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=868129","name":"MyFreeGames","why":"This add-on is silently installed, violating our <a href=\"https://developer.mozilla.org/en-US/docs/Addons/Add-on_guidelines\">Add-on Guidelines</a>.\r\n\r\nUsers who wish to continue using this add-on can enable it in the Add-ons Manager."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"560e08b1-3471-ad34-8ca9-463f5ca5328c","schema":1480349193877},{"guid":"/^({d6e79525-4524-4707-9b97-1d70df8e7e59}|{ddb4644d-1a37-4e6d-8b6e-8e35e2a8ea6c}|{e55007f4-80c5-418e-ac33-10c4d60db01e}|{e77d8ca6-3a60-4ae9-8461-53b22fa3125b}|{e89a62b7-248e-492f-9715-43bf8c507a2f}|{5ce3e0cb-aa83-45cb-a7da-a2684f05b8f3})$/","blockID":"i518","enabled":true,"last_modified":1480349210606,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2013-12-20T12:56:17Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=947509","name":"Re-markit","why":"The installer that includes this add-on violates the <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> by silently installing the add-on, and using multiple add-on IDs."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"145b0f22-501e-39eb-371e-ec8342a5add9","schema":1480349193877},{"guid":"{72b98dbc-939a-4e0e-b5a9-9fdbf75963ef}","blockID":"i772","enabled":true,"last_modified":1480349210536,"details":{"who":"All Firefox users who have this add-on installed.","created":"2014-10-31T16:15:26Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1073810","name":"SitezExpert","why":"This add-on is silently installed into users' systems. It uses very unsafe practices to load its code, and leaks information of all web browsing activity. These are all violations of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"386cb2c9-e674-ce2e-345f-d30a785f90c5","schema":1480349193877},{"guid":"hha8771ui3-Fo9j9h7aH98jsdfa8sda@jetpack","blockID":"i970","enabled":true,"last_modified":1480349210477,"details":{"who":"All users who have this add-on installed.","created":"2015-08-04T15:15:07Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1190963","name":"Video fix (malware)","why":"This is a malicious add-on that hijacks Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"3ca577d8-3685-4ba9-363b-5b2d8d8dd608","schema":1480349193877},{"guid":"{7e8a1050-cf67-4575-92df-dcc60e7d952d}","blockID":"i478","enabled":true,"last_modified":1480349210416,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-11-08T15:42:28Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=935796","name":"SweetPacks","why":"This add-on violates the <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>. Users who wish to continue using this add-on can enable it in the Add-ons Manager."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"1519eb45-fcaa-b531-490d-fe366490ed45","schema":1480349193877},{"guid":"/^({66b103a7-d772-4fcd-ace4-16f79a9056e0}|{6926c7f7-6006-42d1-b046-eba1b3010315}|{72cabc40-64b2-46ed-8648-26d831761150}|{73ee2cf2-7b76-4c49-b659-c3d8cf30825d}|{ca6446a5-73d5-4c35-8aa1-c71dc1024a18}|{5373a31d-9410-45e2-b299-4f61428f0be4})$/","blockID":"i521","enabled":true,"last_modified":1480349210351,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2013-12-20T13:14:29Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=947485","name":"appbario7","why":"The installer that includes this add-on violates the <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> by being silently installed and using multiple add-on IDs."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"983cb7fe-e0b4-6a2e-f174-d2670876b2cd","schema":1480349193877},{"guid":"{dd6b651f-dfb9-4142-b0bd-09912ad22674}","blockID":"i400","enabled":true,"last_modified":1480349210301,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2013-06-25T15:16:01Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=835678","name":"Searchqu","why":"This group of add-ons is silently installed, bypassing our install opt-in screen. This violates our <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"975d2126-f727-f5b9-ca01-b83345b80c56","schema":1480349193877},{"guid":"[email protected]","blockID":"i730","enabled":true,"last_modified":1480349210275,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2014-10-16T16:35:35Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1076771","name":"YYOutoubeAdBlocke","why":"This add-on is silently installed and changes user settings without consent, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>\r\n"},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":["browser.startup.homepage"],"id":"5a0c5818-693f-43ae-f85a-c6928d9c2cc4","schema":1480349193877},{"guid":"[email protected]","blockID":"i568","enabled":true,"last_modified":1480349210242,"details":{"who":"All Firefox users who have versions 2.0.6 or lower of the Thunder add-on installed on Mac OS.","created":"2014-03-28T15:48:38Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=988490","name":"Thunder, 2.0.6 and lower","why":"Versions 2.0.6 and lower of the Thunder add-on are causing startup crashes on Mac OS."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"2.0.6","severity":1}],"prefs":[],"os":"Darwin","id":"cee484f6-2d5d-f708-88be-cd12d825a79a","schema":1480349193877},{"guid":"[email protected]","blockID":"i1222","enabled":true,"last_modified":1480349209818,"details":{"who":"All users of this add-on. If you wish to continue using it, you can enable it in the Add-ons Manager.","created":"2016-05-24T12:10:34Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1275245","name":"Trend Micro BEP 9.1.0.1035 and lower","why":"Add-on  is causing a high-frequency crash in Firefox"},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"9.1.0.1035","severity":1}],"prefs":[],"id":"8045c799-486a-927c-b972-b9da1c2dab2f","schema":1480349193877},{"guid":"[email protected]","blockID":"i220","enabled":true,"last_modified":1480349209794,"details":{"who":"All Firefox users who have Pricepeed below 2.1.0.20 installed.","created":"2012-11-29T16:18:26Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=811433","name":"PricePeep","why":"Versions older than 2.1.0.20 of the PricePeep add-on were silently side-installed with other software, injecting advertisements in Firefox. Versions 2.1.0.20 and above don't have the install problems are not blocked."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"2.1.0.19.99","severity":1}],"prefs":[],"id":"227b9a8d-c18d-239c-135e-d79e614fe392","schema":1480349193877},{"guid":"[email protected]","blockID":"i360","enabled":true,"last_modified":1480349209770,"details":{"who":"All Firefox users who have this add-on installed. Users who want to enable the add-on again can do so in the Add-ons Manager tab.","created":"2013-06-06T12:29:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=845969","name":"YouTube Downloader","why":"The installer that includes this add-on performs Firefox settings changes separately from the add-on install, making it very difficult to opt-out to these changes."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"63669524-93fe-4823-95ba-37cf6cbd4914","schema":1480349193877},{"guid":"[email protected]","blockID":"i498","enabled":true,"last_modified":1480349209745,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-12-04T15:17:58Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=946029","name":"Adobe Flash Player (malware)","why":"This is a malicious Firefox extension that uses a deceptive name and hijacks users' Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"2b25ba3e-45db-0e6c-965a-3acda1a44117","schema":1480349193877},{"guid":"[email protected]","blockID":"i606","enabled":true,"last_modified":1480349209713,"details":{"who":"All Firefox users who have these versions of the Ask Toolbar installed. Users who wish to continue using it can enable it in the Add-ons Manager.\r\n","created":"2014-06-12T14:20:07Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1024719","name":"Ask Toolbar (old Avira Security Toolbar bundle)","why":"Certain old versions of the Ask Toolbar are causing problems to users when trying to open new tabs. Using more recent versions of the Ask Toolbar should also fix this problem.\r\n"},"versionRange":[{"targetApplication":[],"minVersion":"3.15.13","maxVersion":"3.15.13.*","severity":1}],"prefs":[],"id":"c3d88e22-386a-da3b-8aba-3cb526e08053","schema":1480349193877},{"guid":"[email protected]","blockID":"i508","enabled":true,"last_modified":1480349209674,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-12-16T10:15:01Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=950773","name":"Microsoft .NET Framework Assistant (malware)","why":"This is not the <a href=\"https://addons.mozilla.org/addon/microsoft-net-framework-assist/\">Microsoft .NET Framework Assistant</a> created and distributed by Microsoft. It is a malicious extension that is distributed under the same name to trick users into installing it, and turns users into a botnet that conducts SQL injection attacks on visited websites."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"e5d30a74-732e-c3fa-f13b-097ee28d4b27","schema":1480349193877},{"guid":"{5eeb83d0-96ea-4249-942c-beead6847053}","blockID":"i756","enabled":true,"last_modified":1480349209625,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2014-10-17T16:30:30Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1080846","name":"SmarterPower","why":"This add-on is silently installed into users' systems, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"e101dbc0-190c-f6d8-e168-0c1380581cc9","schema":1480349193877},{"guid":"/^({7e8a1050-cf67-4575-92df-dcc60e7d952d}|{b3420a9c-a397-4409-b90d-bcf22da1a08a}|{eca6641f-2176-42ba-bdbe-f3e327f8e0af}|{707dca12-3f99-4d94-afea-06dcc0ae0108}|{aea20431-87fc-40be-bc5b-18066fe2819c}|{30ee6676-1ba6-455a-a7e8-298fa863a546})$/","blockID":"i523","enabled":true,"last_modified":1480349209559,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2013-12-20T13:42:15Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=947481","name":"SweetPacks","why":"The installer that includes this add-on violates the <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> by making changes that can't be easily reverted."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"a3a6bc8e-46a1-b3d5-1b20-58b90ba099c3","schema":1480349193877},{"guid":"{e0352044-1439-48ba-99b6-b05ed1a4d2de}","blockID":"i710","enabled":true,"last_modified":1480349209491,"details":{"who":"All Firefox users who have this add-on installed.","created":"2014-09-30T15:28:14Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1073810","name":"Site Counselor","why":"This add-on is silently installed into users' systems. It uses very unsafe practices to load its code, and leaks information of all web browsing activity. These are all violations of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"b8fedf07-dcaf-f0e3-b42b-32db75c4c304","schema":1480349193877},{"guid":"{bee6eb20-01e0-ebd1-da83-080329fb9a3a}","blockID":"i642","enabled":true,"last_modified":1480349209443,"details":{"who":"All Firefox users who have this version of the add-on installed.","created":"2014-07-10T15:02:26Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1036757","name":"Flash and Video Download, between 40.10.1 and 44.10.1","why":"Certain versions of the Flash and Video Download extension weren't developed by the original developer, and are likely malicious in nature. This violates the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> for reusing an already existent ID."},"versionRange":[{"targetApplication":[],"minVersion":"40.10.1","maxVersion":"44.10.1","severity":3}],"prefs":[],"id":"16db0c85-02ec-4f7c-24a3-a504fbce902d","schema":1480349193877},{"guid":"[email protected]","blockID":"i426","enabled":true,"last_modified":1480349209383,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-07-09T15:25:30Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=891605","name":"Add Lyrics","why":"This add-on is silently installed, violating our <a href=\"https://developer.mozilla.org/en-US/docs/Addons/Add-on_guidelines\">Add-on Guidelines</a>.\r\n\r\nUsers who wish to continue using this add-on can enable it in the Add-ons Manager."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"81678e9e-ebf0-47d6-e409-085c25e67c7e","schema":1480349193877},{"guid":"{7b1bf0b6-a1b9-42b0-b75d-252036438bdc}","blockID":"i638","enabled":true,"last_modified":1480349209260,"details":{"who":"All Firefox users who have this version of the add-on installed.","created":"2014-07-08T16:07:56Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1036137","name":"YouTube High Definition 27.8 and 27.9","why":"Versions 27.8 and 27.9 of the YouTube High Definition extension weren't developed by the original developer, and are likely malicious in nature. It violates the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> for reusing an already existent ID."},"versionRange":[{"targetApplication":[],"minVersion":"27.8","maxVersion":"27.9","severity":3}],"prefs":[],"id":"ffdc8ba0-d548-dc5b-d2fd-79a20837124b","schema":1480349193877},{"guid":"[email protected]","blockID":"i610","enabled":true,"last_modified":1480349209128,"details":{"who":"All Firefox users who have these versions of the Ask Toolbar installed. Users who wish to continue using it can enable it in the Add-ons Manager.\r\n","created":"2014-06-12T14:21:47Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1024719","name":"Ask Toolbar (old Avira Security Toolbar bundle)","why":"Certain old versions of the Ask Toolbar are causing problems to users when trying to open new tabs. Using more recent versions of the Ask Toolbar should also fix this problem.\r\n"},"versionRange":[{"targetApplication":[],"minVersion":"3.15.22","maxVersion":"3.15.22.*","severity":1}],"prefs":[],"id":"935dfec3-d017-5660-db5b-94ae7cea6e5f","schema":1480349193877},{"guid":"[email protected]","blockID":"i163","enabled":true,"last_modified":1480349209076,"details":{"who":"All Firefox users who have these add-ons installed.","created":"2012-10-29T16:40:07Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=806451","name":"Codec-C (malware)","why":"These are malicious add-ons that are distributed with a trojan and negatively affect web browsing."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"6afbf9b8-ae3a-6a48-0f6c-7a3e065ec043","schema":1480349193877},{"guid":"[email protected]","blockID":"i490","enabled":true,"last_modified":1480349209021,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-11-27T08:06:04Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=926378","name":"MSNNow (discontinued social provider)","why":"As part of their ongoing work to fine-tune their editorial mix, msnNOW has decided that msnNOW will stop publishing on Dec. 3, 2013. Rather than having a single home for trending content, they will continue integrating that material throughout all MSN channels. A big thank you to everyone who followed msnNOW stories using the Firefox sidebar"},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"de7d699d-016d-d973-5e39-52568de6ffde","schema":1480349193877},{"guid":"[email protected]","blockID":"i858","enabled":true,"last_modified":1480349208988,"details":{"who":"All Firefox users who have this add-on installed.","created":"2015-02-11T15:32:36Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1131078","name":"FF Toolbar","why":"This add-on is silently installed and changes users' settings, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":["browser.startup.homepage","browser.search.defaultenginename"],"id":"6877bf40-9e45-7017-4dac-14d09e7f0ef6","schema":1480349193877},{"guid":"[email protected]","blockID":"i477","enabled":true,"last_modified":1480349208962,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-11-08T15:35:51Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=936249","name":"Mozilla Security Service (malware)","why":"This add-on is malware that hijacks Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"758c2503-766d-a2f5-4c58-7cea93acfe05","schema":1480349193877},{"guid":"{739df940-c5ee-4bab-9d7e-270894ae687a}","blockID":"i530","enabled":true,"last_modified":1480349208933,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2013-12-20T14:49:25Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=949558","name":"WhiteSmoke New","why":"The installer that includes this add-on violates the <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> by making changes that can't be easily reverted."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"f8097aa6-3009-6dfc-59df-353ba6b1142b","schema":1480349193877},{"guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","blockID":"i115","enabled":true,"last_modified":1480349208904,"details":{"who":"All Firefox users who have this add-on installed.","created":"2012-08-01T13:53:00Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=779014","name":"Adobe Flash Player (malware)","why":"This extension is malware that is installed under false pretenses, and it conducts attacks against certain video websites."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"aef9312d-5f2e-a44d-464d-6113394148e3","schema":1480349193877},{"guid":"g99hiaoekjoasiijdkoleabsy278djasi@jetpack","blockID":"i1022","enabled":true,"last_modified":1480349208877,"details":{"who":"All users who have this add-on installed.","created":"2015-09-28T15:23:08Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1208708","name":"WatchIt (malware)","why":"This is a malicious add-on that hijacks Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"25e057ea-f500-67df-d078-ec3f37f99036","schema":1480349193877},{"guid":"[email protected]","blockID":"i636","enabled":true,"last_modified":1480349208851,"details":{"who":"All Firefox users who have this version of the add-on installed.","created":"2014-07-08T15:58:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1033120","name":"YoutubeEnhancer - Firefox","why":"Version 199.7.0 of the YoutubeEnhancer extension isn't developed by the original developer, and is likely malicious in nature. It violates the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> for reusing an already existent ID."},"versionRange":[{"targetApplication":[],"minVersion":"199.7.0","maxVersion":"199.7.0","severity":3}],"prefs":[],"id":"204a074b-da87-2784-f15b-43a9ea9a6b36","schema":1480349193877},{"guid":"[email protected]","blockID":"i505","enabled":true,"last_modified":1480349208823,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-12-09T15:08:51Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=947741","name":"Facebook Haber (malware)","why":"This is a malicious extension that hijacks users' Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"5acadb8d-d3be-e0e0-4656-9107f9de0ea9","schema":1480349193877},{"guid":"{746505DC-0E21-4667-97F8-72EA6BCF5EEF}","blockID":"i842","enabled":true,"last_modified":1480349208766,"details":{"who":"All Firefox users who have this add-on installed.","created":"2015-02-06T14:45:57Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1128325","name":"Shopper-Pro","why":"This add-on is silently installed into users' systems, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"5f19c5fb-1c78-cbd6-8a03-1678efb54cbc","schema":1480349193877},{"guid":"{51c77233-c0ad-4220-8388-47c11c18b355}","blockID":"i580","enabled":true,"last_modified":1480349208691,"details":{"who":"All Firefox users who have this add-on installed. If you wish to continue using this add-on, it can be enabled in the Add-ons Manager.","created":"2014-04-30T13:55:35Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1004132","name":"Browser Utility","why":"This add-on is silently installed into Firefox, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"0.1.9999999","severity":1}],"prefs":[],"id":"daa2c60a-5009-2c65-a432-161d50bef481","schema":1480349193877},{"guid":"{a2bfe612-4cf5-48ea-907c-f3fb25bc9d6b}","blockID":"i712","enabled":true,"last_modified":1480349208027,"details":{"who":"All Firefox users who have this add-on installed.","created":"2014-09-30T15:28:22Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1073810","name":"Website Xplorer","why":"This add-on is silently installed into users' systems. It uses very unsafe practices to load its code, and leaks information of all web browsing activity. These are all violations of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"13450534-93d7-f2a2-7f0a-e4e3948c4dc1","schema":1480349193877},{"guid":"ScorpionSaver@jetpack","blockID":"i539","enabled":true,"last_modified":1480349207972,"details":{"who":"All Firefox users who have this add-on installed. If you wish to continue using this add-on, you can enable it in the Add-ons Manager.","created":"2014-01-28T14:00:30Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=963826","name":"ScorpionSaver","why":"This add-on is being silently installed, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>"},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"3499c968-6e8b-37f1-5f6e-2384807c2a6d","schema":1480349193877},{"guid":"[email protected]","blockID":"i1212","enabled":true,"last_modified":1480349207912,"details":{"who":"All users who have this add-on installed.","created":"2016-05-05T18:13:29Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1251911","name":"YouTube Unblocker 2.0","why":"This add-on is a copy of YouTube Unblocker, which was <a href=\"https://addons.mozilla.org/firefox/blocked/i1129\">originally blocked</a> due to malicious activity."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"2.0.0","severity":3}],"prefs":[],"id":"57785030-909f-e985-2a82-8bd057781227","schema":1480349193877},{"guid":"{D19CA586-DD6C-4a0a-96F8-14644F340D60}","blockID":"i42","enabled":true,"last_modified":1480349207877,"details":{"who":"Users of McAfee ScriptScan versions 14.4.0 and below for all versions of Firefox and SeaMonkey.","created":"2011-10-03T09:38:39Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=690184","name":"McAfee ScriptScan","why":"This add-on causes a high volume of crashes."},"versionRange":[{"targetApplication":[],"minVersion":"0.1","maxVersion":"14.4.0","severity":1}],"prefs":[],"id":"1d35ac9d-49df-23cf-51f5-f3c228ad0dc9","schema":1480349193877},{"guid":"[email protected]","blockID":"i1042","enabled":true,"last_modified":1480349207840,"details":{"who":"All users who have this add-on installed.","created":"2015-10-07T13:12:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1212174","name":"Avant Player (malware)","why":"This is a malicious add-on that takes over Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"d6453893-becc-7617-2050-0db284e0e0db","schema":1480349193877},{"guid":"/^(@9338379C-DD5C-4A45-9A36-9733DC806FAE|9338379C-DD5C-4A45-9A36-9733DC806FAE|@EBC7B466-8A28-4061-81B5-10ACC05FFE53|@bd6a97c0-4b18-40ed-bce7-3b7d3309e3c4222|@bd6a97c0-4b18-40ed-bce7-3b7d3309e3c4|@b2d6a97c0-4b18-40ed-bce7-3b7d3309e3c4222)$/","blockID":"i1079","enabled":true,"last_modified":1480349207815,"details":{"who":"All Firefox users who have these add-ons installed.","created":"2016-01-18T12:32:32Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1240597","name":"Default SearchProtected (malware)","why":"These add-ons are malicious, manipulating registry and search settings for users."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"ddc5237e-42e4-1bf1-54d3-a5e5799dd828","schema":1480349193877},{"guid":"{33e0daa6-3af3-d8b5-6752-10e949c61516}","blockID":"i282","enabled":true,"last_modified":1480349207789,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using this add-on can enable it in the Add-ons Manager.","created":"2013-02-15T12:19:26Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=835683","name":"Complitly","why":"This add-on violates our <a href=\"https://developer.mozilla.org/en-US/docs/Addons/Add-on_guidelines\">add-on guidelines</a>, bypassing the third party opt-in screen."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"1.1.999","severity":1}],"prefs":[],"id":"1f94bc8d-9d5f-c8f5-45c0-ad1f6e147c71","schema":1480349193877},{"guid":"[email protected]","blockID":"i608","enabled":true,"last_modified":1480349207761,"details":{"who":"All Firefox users who have these versions of the Ask Toolbar installed. Users who wish to continue using it can enable it in the Add-ons Manager.\r\n","created":"2014-06-12T14:20:57Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1024719","name":"Ask Toolbar (old Avira Security Toolbar bundle)","why":"Certain old versions of the Ask Toolbar are causing problems to users when trying to open new tabs. Using more recent versions of the Ask Toolbar should also fix this problem.\r\n"},"versionRange":[{"targetApplication":[],"minVersion":"3.15.18","maxVersion":"3.15.20.*","severity":1}],"prefs":[],"id":"e7d50ff2-5948-d571-6711-37908ccb863f","schema":1480349193877},{"guid":"[email protected]","blockID":"i340","enabled":true,"last_modified":1480349207736,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-04-30T07:44:09Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=867156","name":"Cache Manager (malware)","why":"This is a malicious add-on that logs keyboard input and sends it to a remote server."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"d50d0434-78e4-faa7-ce2a-9b0a8bb5120e","schema":1480349193877},{"guid":"{63eb5ed4-e1b3-47ec-a253-f8462f205350}","blockID":"i786","enabled":true,"last_modified":1480349207705,"details":{"who":"All Firefox users who have this add-on installed.","created":"2014-11-18T12:33:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1073810","name":"FF-Plugin","why":"This add-on is silently installed into users' systems. It uses very unsafe practices to load its code, and leaks information of all web browsing activity. These are all violations of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"ca4558a2-8ce4-3ca0-3d29-63019f680c8c","schema":1480349193877},{"guid":"jid1-4vUehhSALFNqCw@jetpack","blockID":"i634","enabled":true,"last_modified":1480349207680,"details":{"who":"All Firefox users who have this version of the add-on installed.","created":"2014-07-04T14:13:57Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1033002","name":"YouTube Plus Plus 99.7","why":"Version 99.7 of the YouTube Plus Plus extension isn't developed by the original developer, and is likely malicious in nature. It violates the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> for reusing an already existent ID."},"versionRange":[{"targetApplication":[],"minVersion":"99.7","maxVersion":"99.7","severity":3}],"prefs":[],"id":"a6d017cb-e33f-2239-4e42-ab4e7cfb19fe","schema":1480349193877},{"guid":"{aab02ab1-33cf-4dfa-8a9f-f4e60e976d27}","blockID":"i820","enabled":true,"last_modified":1480349207654,"details":{"who":"All Firefox users who have this add-on installed.","created":"2015-01-13T09:27:49Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1073810","name":"Incredible Web","why":"This add-on is silently installed into users' systems without their consent, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"847ecc6e-1bc1-f7ff-e1d5-a76e6b8447d2","schema":1480349193877},{"guid":"[email protected]","blockID":"i320","enabled":true,"last_modified":1480349207608,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-03-20T16:35:24Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=845610","name":"TornTV","why":"This add-on doesn't follow our <a href=\"https://developer.mozilla.org/en-US/docs/Addons/Add-on_guidelines\">Add-on Guidelines</a>, bypassing our third party install opt-in screen. Users who wish to continue using this extension can enable it in the Add-ons Manager."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"cdd492b8-8101-74a9-5760-52ff709fd445","schema":1480349193877},{"guid":"[email protected]","blockID":"i674","enabled":true,"last_modified":1480349207561,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using this add-on can enable it in the Add-ons Manager.","created":"2014-07-22T16:26:19Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=877836","name":"JollyWallet","why":"The add-on is silently installed, in violation of our <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"d4467e20-0f71-f0e0-8cd6-40c82b6c7379","schema":1480349193877},{"guid":"[email protected]","blockID":"i435","enabled":true,"last_modified":1480349207535,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-08-09T13:12:14Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=903544","name":"Facebook Pluggets Plugin (malware)","why":"This add-on is malware that hijacks users' Facebook accounts and posts spam on their behalf. "},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"3a63fd92-9290-02fb-a2e8-bc1b4424201a","schema":1480349193877},{"guid":"344141-fasf9jas08hasoiesj9ia8ws@jetpack","blockID":"i1038","enabled":true,"last_modified":1480349207485,"details":{"who":"All users who have this add-on installed.","created":"2015-10-05T16:42:09Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1211169","name":"Video Plugin (malware)","why":"This is a malicious add-on that hijacks Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"f7986b7b-9b5a-d372-8147-8b4bd6f5a29b","schema":1480349193877},{"guid":"/^brasilescape.*\\@facebook\\.com$//","blockID":"i485","enabled":true,"last_modified":1480349207424,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-11-14T15:57:03Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=938835","name":"Facebook Video Pack (malware)","why":"This add-on is malware that hijacks Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"17a2bf85-b44a-4745-1b48-8be0c151d697","schema":1480349193877},{"guid":"meOYKQEbBBjH5Ml91z0p9Aosgus8P55bjTa4KPfl@jetpack","blockID":"i998","enabled":true,"last_modified":1480349207370,"details":{"who":"All users who have this add-on installed.","created":"2015-09-07T13:54:25Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1201164","name":"Smooth Player (malware)","why":"This is a malicious add-on that hijacks Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"30c3e511-9e8d-15ee-0867-d61047e56515","schema":1480349193877},{"guid":"{8dc5c42e-9204-2a64-8b97-fa94ff8a241f}","blockID":"i770","enabled":true,"last_modified":1480349207320,"details":{"who":"All Firefox users who have this add-on installed.","created":"2014-10-30T14:52:52Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1088726","name":"Astrmenda Search","why":"This add-on is silently installed and is considered malware, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":["browser.startup.homepage","browser.search.defaultenginename"],"id":"8a9c7702-0349-70d6-e64e-3a666ab084c6","schema":1480349193877},{"guid":"[email protected]","blockID":"i752","enabled":true,"last_modified":1480349207290,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2014-10-17T16:18:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=963788","name":"Slick Savings","why":"This add-on is silently installed into users' systems, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"9b1faf30-5725-7847-d993-b5cdaabc9829","schema":1480349193877},{"guid":"[email protected]","blockID":"i1129","enabled":true,"last_modified":1480349207262,"details":{"who":"All users who have this add-on installed.","created":"2016-03-01T21:20:05Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1251911","name":"YouTube Unblocker","why":"The add-on has a mechanism that updates certain configuration files from the developer\u2019s website. This mechanism has a vulnerability that is being exploited through this website (unblocker.yt) to change security settings in Firefox and install malicious add-ons."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"aa246b36-0a80-81e3-2129-4847e872d5fe","schema":1480349193877},{"guid":"[email protected]","blockID":"i612","enabled":true,"last_modified":1480349206818,"details":{"who":"All Firefox users who have these versions of the Ask Toolbar installed. Users who wish to continue using it can enable it in the Add-ons Manager.\r\n","created":"2014-06-12T14:23:00Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1024719","name":"Ask Toolbar (old Avira Security Toolbar bundle)","why":"Certain old versions of the Ask Toolbar are causing problems to users when trying to open new tabs. Using more recent versions of the Ask Toolbar should also fix this problem.\r\n"},"versionRange":[{"targetApplication":[],"minVersion":"3.15.24","maxVersion":"3.15.24.*","severity":1}],"prefs":[],"id":"e0ff9df4-60e4-dbd0-8018-57f395e6610a","schema":1480349193877},{"guid":"{1e4ea5fc-09e5-4f45-a43b-c048304899fc}","blockID":"i812","enabled":true,"last_modified":1480349206784,"details":{"who":"All Firefox users who have this add-on installed.","created":"2015-01-06T13:22:39Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1073810","name":"Great Finder","why":"This add-on is silently installed into users' systems. It uses very unsafe practices to load its code, and leaks information of all web browsing activity. These are all violations of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"1ea40b9f-2423-a2fd-a5e9-4ec1df2715f4","schema":1480349193877},{"guid":"psid-vhvxQHMZBOzUZA@jetpack","blockID":"i70","enabled":true,"last_modified":1480349206758,"details":{"who":"All Firefox users who have installed this add-on.","created":"2012-02-23T13:44:41Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=730059","name":"PublishSync (malware)","why":"Add-on spams Facebook accounts and blocks Facebook warnings."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"f1528b02-7cef-0e80-f747-8bbf1f0f2f06","schema":1480349193877},{"guid":"{B18B1E5C-4D81-11E1-9C00-AFEB4824019B}","blockID":"i447","enabled":true,"last_modified":1480349206733,"details":{"who":"All Firefox users who have this add-on installed. The add-on can be enabled again in the Add-ons Manager.","created":"2013-09-06T16:00:19Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=788838","name":"My Smart Tabs","why":"This add-on is installed silently, in violation of our <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"40332fae-0444-a141-ade9-8d9e50370f56","schema":1480349193877},{"guid":"[email protected]","blockID":"i314","enabled":true,"last_modified":1480349206708,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-03-06T14:14:51Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=835665","name":"Coupon Companion","why":"This add-on doesn't follow our <a href=\"https://developer.mozilla.org/en-US/docs/Addons/Add-on_guidelines\">Add-on Guidelines</a>, bypassing our third party install opt-in screen. Users who wish to continue using this extension can enable it in the Add-ons Manager."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"06c07e28-0a34-e5ee-e724-491a2f6ce586","schema":1480349193877},{"guid":"/^(ffxtlbr@mixidj\\.com|{c0c2693d-2ee8-47b4-9df7-b67a0ee31988}|{67097627-fd8e-4f6b-af4b-ecb65e50112e}|{f6f0f973-a4a3-48cf-9a7a-b7a69c30d71a}|{a3d0e35f-f1da-4ccb-ae77-e9d27777e68d}|{1122b43d-30ee-403f-9bfa-3cc99b0caddd})$/","blockID":"i540","enabled":true,"last_modified":1480349206678,"details":{"who":"All Firefox users who have this add-on installed.","created":"2014-01-28T14:07:56Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=963819","name":"MixiDJ (malware)","why":"This add-on has been repeatedly blocked before and keeps showing up with new add-on IDs, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"4c03ddda-bb3f-f097-0a7b-b7b77b050584","schema":1480349193877},{"guid":"[email protected]","blockID":"i836","enabled":true,"last_modified":1480349206628,"details":{"who":"All Firefox users who have this add-on installed.","created":"2015-02-06T14:17:39Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1130406","name":"Inside News (malware)","why":"This is a malicious add-on that hijacks Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"0945a657-f28d-a02c-01b2-5115b3f90d7a","schema":1480349193877},{"guid":"[email protected]","blockID":"i358","enabled":true,"last_modified":1480349206603,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-05-24T14:09:47Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=874131","name":"Lyrics Finder","why":"This add-on is silently installed, violating our <a href=\"https://developer.mozilla.org/en-US/docs/Addons/Add-on_guidelines\">Add-on Guidelines</a>.\r\n\r\nUsers who wish to continue using this add-on can enable it in the Add-ons Manager."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"2307f11c-6216-0dbf-a464-b2921055ce2b","schema":1480349193877},{"guid":"[email protected]","blockID":"i484","enabled":true,"last_modified":1480349206578,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-11-14T09:55:24Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=938264","name":"WebCake","why":"This add-on violates the <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> and is broadly considered to be malware. Users who wish to continue using this add-on can enable it in the Add-ons Manager."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"2865addd-da1c-20c4-742f-6a2270da2e78","schema":1480349193877},{"guid":"{c0c2693d-2ee8-47b4-9df7-b67a0ee31988}","blockID":"i354","enabled":true,"last_modified":1480349206525,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-05-23T14:31:21Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=837838","name":"Mixi DJ","why":"This add-on is silently installed, violating our <a href=\"https://developer.mozilla.org/en-US/docs/Addons/Add-on_guidelines\">Add-on Guidelines</a>.\r\n\r\nUsers who wish to continue using this add-on can enable it in the Add-ons Manager."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"03a745c3-0ee7-e262-ba31-62d4f78ddb62","schema":1480349193877},{"guid":"/^({7316e43a-3ebd-4bb4-95c1-9caf6756c97f}|{0cc09160-108c-4759-bab1-5c12c216e005}|{ef03e721-f564-4333-a331-d4062cee6f2b}|{465fcfbb-47a4-4866-a5d5-d12f9a77da00}|{7557724b-30a9-42a4-98eb-77fcb0fd1be3}|{b7c7d4b0-7a84-4b73-a7ef-48ef59a52c3b})$/","blockID":"i520","enabled":true,"last_modified":1480349206491,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2013-12-20T13:11:46Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=947485","name":"appbario7","why":"The installer that includes this add-on violates the <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> by being silently installed and using multiple add-on IDs."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"e3901c48-9c06-fecb-87d3-efffd9940c22","schema":1480349193877},{"guid":"{354dbb0a-71d5-4e9f-9c02-6c88b9d387ba}","blockID":"i538","enabled":true,"last_modified":1480349206465,"details":{"who":"All Firefox users who have this add-on installed.","created":"2014-01-27T10:13:17Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=964081","name":"Show Mask ON (malware)","why":"This is a malicious add-on that hijacks Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"aad90253-8921-b5df-3658-45a70d75f3d7","schema":1480349193877},{"guid":"{8E9E3331-D360-4f87-8803-52DE43566502}","blockID":"i461","enabled":true,"last_modified":1480349206437,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2013-10-17T16:10:51Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=906071","name":"Updater By SweetPacks","why":"This is a companion add-on for the SweetPacks Toolbar which is <a href=\"https://addons.mozilla.org/firefox/blocked/i392\">blocked</a> due to guideline violations."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"ae8cca6e-4258-545f-9a69-3d908264a701","schema":1480349193877},{"guid":"[email protected]","blockID":"i172","enabled":true,"last_modified":1480349206384,"details":{"who":"All Firefox users who have this add-on installed.","created":"2012-10-30T13:39:22Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=806802","name":"Bflix (malware)","why":"These are malicious add-ons that are distributed with a trojan and negatively affect web browsing."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"7a9062f4-218d-51d2-9b8c-b282e6eada4f","schema":1480349193877},{"guid":"{dff137ae-1ffd-11e3-8277-b8ac6f996f26}","blockID":"i450","enabled":true,"last_modified":1480349206312,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-09-18T16:19:34Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=917861","name":"Addons Engine (malware)","why":"This is add-on is malware that silently redirects popular search queries to a third party."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"8e583fe4-1c09-9bea-2473-faecf3260685","schema":1480349193877},{"guid":"[email protected]","blockID":"i493","enabled":true,"last_modified":1480349206286,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-12-02T12:49:36Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=939254","name":"BetterSurf (malware)","why":"This add-on appears to be malware and is installed silently in violation of the <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"af2a9e74-3753-9ff1-d899-5d1e79ed3dce","schema":1480349193877},{"guid":"{20AD702C-661E-4534-8CE9-BA4EC9AD6ECC}","blockID":"i626","enabled":true,"last_modified":1480349206261,"details":{"who":"All Firefox users who have this add-on installed.","created":"2014-06-19T15:16:38Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1027886","name":"V-Bates","why":"This add-on is probably silently installed, and is causing significant stability issues for users, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"9b9ccabe-8f9a-e3d1-a689-1aefba1f33b6","schema":1480349193877},{"guid":"{c5e48979-bd7f-4cf7-9b73-2482a67a4f37}","blockID":"i736","enabled":true,"last_modified":1480349206231,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2014-10-17T15:22:41Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1080842","name":"ClearThink","why":"This add-on is silently installed into users' systems, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"6e8b3e4f-2f59-cde3-e6d2-5bc6e216c506","schema":1480349193877},{"guid":"{41339ee8-61ed-489d-b049-01e41fd5d7e0}","blockID":"i810","enabled":true,"last_modified":1480349206165,"details":{"who":"All Firefox users who have this add-on installed.","created":"2014-12-23T10:32:26Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1073810","name":"FireWeb","why":"This add-on is silently installed into users' systems. It uses very unsafe practices to load its code, and leaks information of all web browsing activity. These are all violations of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"a35f2ca6-aec4-c01d-170e-650258ebcd2c","schema":1480349193877},{"guid":"jid0-l9BxpNUhx1UUgRfKigWzSfrZqAc@jetpack","blockID":"i640","enabled":true,"last_modified":1480349206133,"details":{"who":"All Firefox users who have this add-on installed.","created":"2014-07-09T14:35:40Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1036640","name":"Bitcoin Mining Software","why":"This add-on attempts to gather private user data and send it to a remote location. It doesn't appear to be very effective at it, but its malicious nature is undeniable."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"8fe3c35e-1a6f-a89a-fa96-81bda3b71db1","schema":1480349193877},{"guid":"{845cab51-d8d2-472f-8bd9-2b44642d97c2}","blockID":"i460","enabled":true,"last_modified":1480349205746,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-10-17T15:50:38Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=927456","name":"Vafmusic9","why":"This add-on is silently installed and handles users' settings, violating some of the <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>. Users who wish to continue using this add-on can enable it in the Add-ons Manager."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"8538ccb4-3b71-9858-3f6d-c0fff7af58b0","schema":1480349193877},{"guid":"[email protected]","blockID":"i676","enabled":true,"last_modified":1480349205688,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using this add-on can enable it in the Add-ons Manager.","created":"2014-07-22T16:31:56Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=881511","name":"SpecialSavings","why":"This is add-on is generally considered to be unwanted and is probably silently installed, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"5e921810-fc3a-0729-6749-47e38ad10a22","schema":1480349193877},{"guid":"[email protected]","blockID":"i434","enabled":true,"last_modified":1480349205645,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-08-09T11:26:11Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=844945","name":"Hotspot Shield Helper","why":"This add-on bypasses the external install opt in screen in Firefox, violating the <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>. Users who wish to continue using this add-on can enable it in the Add-ons Manager."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"083585eb-d7e7-e228-5fbf-bf35c52044e4","schema":1480349193877},{"guid":"[email protected]","blockID":"i994","enabled":true,"last_modified":1480349205584,"details":{"who":"All users who have this add-on installed.","created":"2015-09-01T15:32:36Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1200848","name":"Addon Hack","why":"This add-on is a proof of concept of malicious behavior in an add-on. In itself it doesn't cause any harm, but it still needs to be blocked for security reasons."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"81f75571-ca2a-0e50-a925-daf2037ce63c","schema":1480349193877},{"guid":"[email protected]","blockID":"i174","enabled":true,"last_modified":1480349205526,"details":{"who":"All Firefox users who have these add-ons installed.","created":"2012-10-30T13:40:31Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=806802","name":"Bflix (malware)","why":"These are malicious add-ons that are distributed with a trojan and negatively affect web browsing."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"811a61d4-9435-133e-6262-fb72486c36b0","schema":1480349193877},{"guid":"{EEE6C361-6118-11DC-9C72-001320C79847}","blockID":"i392","enabled":true,"last_modified":1480349205455,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2013-06-25T12:38:45Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=881447","name":"SweetPacks Toolbar","why":"This add-on changes search settings without user interaction, and fails to reset them after it is removed. This violates our <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"c1dc6607-4c0a-4031-9f14-70ef1ae1edcb","schema":1480349193877},{"guid":"/^(4cb61367-efbf-4aa1-8e3a-7f776c9d5763@cdece6e9-b2ef-40a9-b178-291da9870c59\\.com|0efc9c38-1ec7-49ed-8915-53a48b6b7600@e7f17679-2a42-4659-83c5-7ba961fdf75a\\.com|6be3335b-ef79-4b0b-a0ba-b87afbc6f4ad@6bbb4d2e-e33e-4fa5-9b37-934f4fb50182\\.com)$/","blockID":"i531","enabled":true,"last_modified":1480349205287,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2013-12-20T15:01:22Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=949672","name":"Feven","why":"The installer that includes this add-on violates the <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> by making changes that can't be easily reverted and using multiple IDs."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"46aa79a9-d329-f713-d4f2-07d31fe7071e","schema":1480349193877},{"guid":"[email protected]","blockID":"i466","enabled":true,"last_modified":1480349205108,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-11-07T13:32:41Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=933988","name":"Hotspot Shield Extension","why":"This add-on doesn't respect user choice, violating the <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>. Users who wish to continue using this add-on can enable it in the Add-ons Manager."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"8176f879-bd73-5468-e908-2d7cfc115ac2","schema":1480349193877},{"guid":"{FCE04E1F-9378-4f39-96F6-5689A9159E45}","blockID":"i920","enabled":true,"last_modified":1480349204978,"details":{"who":"All Firefox users who have this add-on installed in Firefox 39 and above.\r\n","created":"2015-06-09T15:26:47Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1173154","name":"RealPlayer Browser Record Plugin","why":"Certain versions of this extension are causing startup crashes in Firefox 39 and above.\r\n"},"versionRange":[{"targetApplication":[{"minVersion":"39.0a1","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"}],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"eb191ff0-20f4-6e04-4344-d880af4faf51","schema":1480349193877},{"guid":"{9CE11043-9A15-4207-A565-0C94C42D590D}","blockID":"i503","enabled":true,"last_modified":1480349204951,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-12-06T11:58:38Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=947384","name":"XUL Cache (malware)","why":"This is a malicious extension that uses a deceptive name to stay in users' systems."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"dcdae267-8d3a-5671-dff2-f960febbbb20","schema":1480349193877},{"guid":"/^[a-z0-9]+@foxysecure[a-z0-9]*\\.com$/","blockID":"i766","enabled":true,"last_modified":1480349204925,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using this add-on can enable it in the Add-ons Manager.","created":"2014-10-30T14:22:18Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1088615","name":"Fox Sec 7","why":"This add-on is silently installed into users' systems, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"503fbd7c-04cd-65f3-9d0e-3ecf427b4a8f","schema":1480349193877},{"guid":"/^(jid1-W4CLFIRExukJIFW@jetpack|jid1-W4CLFIRExukJIFW@jetpack_1|jid1-W3CLwrP[a-z]+@jetpack)$/","blockID":"i1078","enabled":true,"last_modified":1480349204899,"details":{"who":"All Firefox users who have this add-on installed.","created":"2016-01-18T10:31:51Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1240561","name":"Adobe Flash Player (malware)","why":"This is a malicious extension that tries to pass itself for the Adobe Flash Player and hides itself in the Add-ons Manager."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"b026fe67-ec77-a240-2fa1-e78f581a6fe4","schema":1480349193877},{"guid":"{0153E448-190B-4987-BDE1-F256CADA672F}","blockID":"i914","enabled":true,"last_modified":1480349204871,"details":{"who":"All Firefox users who have this add-on installed in Firefox 39 and above.","created":"2015-06-02T09:56:58Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1170633","name":"RealPlayer Browser Record Plugin","why":"Certain versions of this extension are causing startup crashes in Firefox 39 and above."},"versionRange":[{"targetApplication":[{"minVersion":"39.0a1","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"}],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"2bfe0d89-e458-9d0e-f944-ddeaf8c4db6c","schema":1480349193877},{"guid":"{77beece6-3997-403a-92fa-0055bfcf88e5}","blockID":"i452","enabled":true,"last_modified":1480349204844,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-09-18T16:34:58Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=916966","name":"Entrusted11","why":"This add-on doesn't follow our <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>, manipulating settings without reverting them on removal. Users who wish to continue using this add-on can enable it in the Add-ons Manager."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"d348f91f-caeb-a803-dfd9-fd5d285aa0fa","schema":1480349193877},{"guid":"dealcabby@jetpack","blockID":"i222","enabled":true,"last_modified":1480349204818,"details":{"who":"All Firefox users who have this add-on installed.","created":"2012-11-29T16:20:24Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=811435","name":"DealCabby","why":"This add-on is silently side-installed with other software, injecting advertisements in Firefox."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"6585f0bd-4f66-71e8-c565-d9762c5c084a","schema":1480349193877},{"guid":"{3c9a72a0-b849-40f3-8c84-219109c27554}","blockID":"i510","enabled":true,"last_modified":1480349204778,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-12-17T14:27:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=951301","name":"Facebook Haber (malware)","why":"This add-on is malware that hijacks users' Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"7cfa3d0b-0ab2-5e3a-8143-1031c180e32f","schema":1480349193877},{"guid":"{4ED1F68A-5463-4931-9384-8FFF5ED91D92}","blockID":"i1245","enabled":true,"last_modified":1480349204748,"details":{"who":"All users who have McAfee SiteAdvisor lower than 4.0. \r\n\r\nTo resolve this issue, users will need to uninstall McAfee SiteAdvisor/WebAdvisor, reboot the computer, and then reinstall McAfee SiteAdvisor/WebAdvisor. \r\n\r\nFor detailed instructions, please refer to the <a href=\"https://service.mcafee.com/webcenter/portal/cp/home/articleview?locale=en-CA&articleId=TS102554\">McAfee support knowledge base</a>.","created":"2016-07-14T21:24:02Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1286368","name":"McAfee SiteAdvisor lower than 4.0","why":"Old versions of McAfee SiteAdvisor cause startup crashes starting with Firefox 48.0 beta."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"3.9.9","severity":1}],"prefs":[],"id":"d727d8c5-3329-c98a-7c7e-38b0813ca516","schema":1480349193877},{"guid":"{2aab351c-ad56-444c-b935-38bffe18ad26}","blockID":"i500","enabled":true,"last_modified":1480349204716,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-12-04T15:29:44Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=946087","name":"Adobe Photo (malware)","why":"This is a malicious Firefox extension that uses a deceptive name and hijacks users' Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"f7a76d34-ddcd-155e-9fae-5967bd796041","schema":1480349193877},{"guid":"jid1-4P0kohSJxU1qGg@jetpack","blockID":"i488","enabled":true,"last_modified":1480349204668,"details":{"who":"All Firefox users who have version 1.2.50 of the Hola extension. Updating to the latest version should remove the block.","created":"2013-11-25T12:14:57Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=942935","name":"Hola, version 1.2.50","why":"Version 1.2.50 of the Hola extension is causing frequent crashes in Firefox. All users are strongly recommended to update to the latest version, which shouldn't have this problem."},"versionRange":[{"targetApplication":[],"minVersion":"1.2.50","maxVersion":"1.2.50","severity":1}],"prefs":[],"id":"5c7f1635-b39d-4278-5f95-9042399c776e","schema":1480349193877},{"guid":"{0A92F062-6AC6-8180-5881-B6E0C0DC2CC5}","blockID":"i864","enabled":true,"last_modified":1480349204612,"details":{"who":"All users who have this add-on installed. Users who wish to continue using the add-on can enable it in the Add-ons Manager.","created":"2015-02-26T12:56:19Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1131220","name":"BlockAndSurf","why":"This add-on is silently installed into users' systems and makes unwanted settings changes, in violation of our <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":["browser.startup.homepage","browser.search.defaultenginename"],"id":"acb16d1c-6274-93a3-7c1c-7ed36ede64a9","schema":1480349193877},{"guid":"jid0-Y6TVIzs0r7r4xkOogmJPNAGFGBw@jetpack","blockID":"i322","enabled":true,"last_modified":1480349204532,"details":{"who":"All users who have this add-on installed.","created":"2013-03-22T14:39:40Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=847018","name":"Flash Player (malware)","why":"This extension is malware, installed pretending to be the Flash Player plugin."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"54df22cd-19ce-a7f0-63cc-ffe3113748b9","schema":1480349193877},{"guid":"[email protected]","blockID":"i986","enabled":true,"last_modified":1480349204041,"details":{"who":"All Thunderbird users who have this version of the add-on installed on Thunderbird 38.0a2 and above.","created":"2015-08-17T15:56:04Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1189264","name":"trackerbird 1.2.6","why":"This add-on is causing consistent crashes on Thunderbird 38.0a2 and above."},"versionRange":[{"targetApplication":[{"minVersion":"38.0a2","guid":"{3550f703-e582-4d05-9a08-453d09bdfdc6}","maxVersion":"*"}],"minVersion":"1.2.6","maxVersion":"1.2.6","severity":1}],"prefs":[],"id":"bb1c699e-8790-4528-0b6d-4f83b7a3152d","schema":1480349193877},{"guid":"{0134af61-7a0c-4649-aeca-90d776060cb3}","blockID":"i448","enabled":true,"last_modified":1480349203968,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-09-13T16:15:39Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=912746","name":"KeyBar add-on","why":"This add-on doesn't follow our Add-on Guidelines. It manipulates settings without reverting them on removal. Users who wish to continue using this add-on can enable it in the Add-ons Manager."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"cf428416-4974-8bb4-7928-c0cb2cfe7957","schema":1480349193877},{"guid":"/^(firefox@vebergreat\\.net|EFGLQA@78ETGYN-0W7FN789T87\\.COM)$/","blockID":"i564","enabled":true,"last_modified":1480349203902,"details":{"who":"All Firefox users who have these add-ons installed. If you wish to continue using these add-ons, you can enable them in the Add-ons Manager.","created":"2014-03-05T13:02:57Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=974104","name":"veberGreat and vis (Free Driver Scout bundle)","why":"These add-ons are silently installed by the Free Driver Scout installer, in violation of our <a href=\"https://developer.mozilla.org/en-US/docs/Addons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"487538f1-698e-147e-6395-986759ceed7e","schema":1480349193877},{"guid":"69ffxtbr@PackageTracer_69.com","blockID":"i882","enabled":true,"last_modified":1480349203836,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2015-04-10T16:18:35Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1153001","name":"PackageTracer","why":"This add-on appears to be malware, hijacking user's settings, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":["browser.startup.homepage","browser.search.defaultenginename"],"id":"0d37b4e0-3c60-fdad-dd8c-59baff6eae87","schema":1480349193877},{"guid":"{ACAA314B-EEBA-48e4-AD47-84E31C44796C}","blockID":"i496","enabled":true,"last_modified":1480349203779,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2013-12-03T16:07:59Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=945530","name":"DVDVideoSoft Menu","why":"The installer that includes this add-on violates the <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> by making settings changes that can't be easily reverted."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"70d2c912-8d04-8065-56d6-d793b13d5f67","schema":1480349193877},{"guid":"jid1-4vUehhSALFNqCw@jetpack","blockID":"i632","enabled":true,"last_modified":1480349203658,"details":{"who":"All Firefox users who have this version of the add-on installed.","created":"2014-07-01T13:16:55Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1033002","name":"YouTube Plus Plus 100.7","why":"Version 100.7 of the YouTube Plus Plus extension isn't developed by the original developer, and is likely malicious in nature. It violates the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> for reusing an already existent ID."},"versionRange":[{"targetApplication":[],"minVersion":"100.7","maxVersion":"100.7","severity":3}],"prefs":[],"id":"8bef6026-6697-99cd-7c1f-812877c4211d","schema":1480349193877},{"guid":"{a9bb9fa0-4122-4c75-bd9a-bc27db3f9155}","blockID":"i404","enabled":true,"last_modified":1480349203633,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2013-06-25T15:16:43Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=835678","name":"Searchqu","why":"This group of add-ons is silently installed, bypassing our install opt-in screen. This violates our <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"fb7a1dc7-16a0-4f70-8289-4df494e0d0fa","schema":1480349193877},{"guid":"[email protected]","blockID":"i850","enabled":true,"last_modified":1480349203605,"details":{"who":"All Firefox users who have this add-on installed.","created":"2015-02-09T15:29:21Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1128269","name":"unIsaless","why":"This add-on is silently installed and performs unwanted actions, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"49536a29-fc7e-9fd0-f415-e15ac090fa56","schema":1480349193877},{"guid":"[email protected]","blockID":"i472","enabled":true,"last_modified":1480349203581,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-11-07T15:38:38Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=935779","name":"Installer bundle (malware)","why":"This add-on is part of a malicious Firefox installer bundle."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"9b5b15b3-6da7-cb7c-3c44-30b4fe079d52","schema":1480349193877},{"guid":"{377e5d4d-77e5-476a-8716-7e70a9272da0}","blockID":"i398","enabled":true,"last_modified":1480349203519,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2013-06-25T15:15:46Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=835678","name":"Searchqu","why":"This group of add-ons is silently installed, bypassing our install opt-in screen. This violates our <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"ea94df32-2a85-23da-43f7-3fc5714530ec","schema":1480349193877},{"guid":"{4933189D-C7F7-4C6E-834B-A29F087BFD23}","blockID":"i437","enabled":true,"last_modified":1480349203486,"details":{"who":"All Firefox users.","created":"2013-08-09T15:14:27Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=900695","name":"Win32.SMSWebalta (malware)","why":"This add-on is widely reported to be malware."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"cbef1357-d6bc-c8d3-7a82-44af6b1c390f","schema":1480349193877},{"guid":"{ADFA33FD-16F5-4355-8504-DF4D664CFE10}","blockID":"i306","enabled":true,"last_modified":1480349203460,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using this add-on can enable it in the Add-ons Manager.","created":"2013-02-28T12:56:48Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=844972","name":"Nation Toolbar","why":"This add-on is silently installed, bypassing our third-party opt-in screen, in violation of our <a href=\"https://developer.mozilla.org/en-US/docs/Addons/Add-on_guidelines\">Add-on Guidelines</a>. It's also possible that it changes user settings without their consent."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"017fd151-37ca-4646-4763-1d303fb918fa","schema":1480349193877},{"guid":"[email protected]","blockID":"i884","enabled":true,"last_modified":1480349203433,"details":{"who":"All Firefox users who have this add-on installed.","created":"2015-04-10T16:21:34Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1152614","name":"Security Protection (malware)","why":"This add-on appears to be malware, hijacking user settings, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":["browser.startup.homepage","browser.search.defaultenginename"],"id":"1b5cc88e-499d-2a47-d793-982d4c05e6ee","schema":1480349193877},{"guid":"/^(67314b39-24e6-4f05-99f3-3f88c7cddd17@6c5fa560-13a3-4d42-8e90-53d9930111f9\\.com|ffxtlbr@visualbee\\.com|{7aeae561-714b-45f6-ace3-4a8aed6e227b}|{7093ee04-f2e4-4637-a667-0f730797b3a0}|{53c4024f-5a2e-4f2a-b33e-e8784d730938})$/","blockID":"i514","enabled":true,"last_modified":1480349203408,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2013-12-20T12:25:50Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=947473","name":"VisualBee Toolbar","why":"The installer that includes this add-on violates the <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> by using multiple add-on IDs and making unwanted settings changes."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"5f91eee1-7303-3f97-dfe6-1e897a156c7f","schema":1480349193877},{"guid":"[email protected]","blockID":"i720","enabled":true,"last_modified":1480349203341,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2014-10-02T12:23:01Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1076771","name":"GoSSave","why":"This add-on is silently installed into users' systems and changes settings without consent, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":["browser.startup.homepage"],"id":"8ebbc061-a4ff-b75b-ec42-eb17c42a2956","schema":1480349193877},{"guid":"{87934c42-161d-45bc-8cef-ef18abe2a30c}","blockID":"i547","enabled":true,"last_modified":1480349203310,"details":{"who":"All Firefox users who have this add-on installed. If you wish to continue using it, it can be enabled in the Add-ons Manager.","created":"2014-01-30T12:42:01Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=798621","name":"Ad-Aware Security Toolbar","why":"This add-on is silently installed and makes various unwanted changes, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"3.7.9999999999","severity":1}],"prefs":[],"id":"bcfbc502-24c2-4699-7435-e4837118f05a","schema":1480349193877},{"guid":"[email protected]","blockID":"i495","enabled":true,"last_modified":1480349203247,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-12-02T15:09:42Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=945426","name":"Facebook Security Service (malware)","why":"This is a malicious Firefox extension that uses a deceptive name and hijacks users' Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"1a2c37a9-e7cc-2d03-2043-098d36b8aca2","schema":1480349193877},{"guid":"[email protected]","blockID":"i1261","enabled":true,"last_modified":1480349203208,"details":{"who":"All users who install affected versions of this add-on - beta versions 4.0 to 4.1.20a from addons.mozilla.org or lastpass.com.","created":"2016-07-29T14:17:31Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1289907","name":"LastPass addon","why":"LastPass have announced there are security issues that would allow a malicious website to perform some actions (e.g. deleting passwords) without the user's knowledge.  Beta versions 4.0 to 4.1.20a of their add-on that were available from addons.mozilla.org are affected and Lastpass also distributed these versions direct from their website."},"versionRange":[{"targetApplication":[],"minVersion":"4.0.0a","maxVersion":"4.1.20a","severity":1}],"prefs":[],"id":"ffe94023-b4aa-87ac-962c-5beabe34b1a0","schema":1480349193877},{"guid":"008abed2-b43a-46c9-9a5b-a771c87b82da@1ad61d53-2bdc-4484-a26b-b888ecae1906.com","blockID":"i528","enabled":true,"last_modified":1480349203131,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2013-12-20T14:40:58Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=949565","name":"weDownload Manager Pro","why":"The installer that includes this add-on violates the <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> by being silently installed."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"da46065f-1c68-78f7-80fc-8ae07b5df68d","schema":1480349193877},{"guid":"{25dd52dc-89a8-469d-9e8f-8d483095d1e8}","blockID":"i714","enabled":true,"last_modified":1480349203066,"details":{"who":"All Firefox users who have this add-on installed.","created":"2014-10-01T15:36:06Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1073810","name":"Web Counselor","why":"This add-on is silently installed into users' systems. It uses very unsafe practices to load its code, and leaks information of all web browsing activity. These are all violations of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"e46c31ad-0ab3-e48a-47aa-9fa91b675fda","schema":1480349193877},{"guid":"{B1FC07E1-E05B-4567-8891-E63FBE545BA8}","blockID":"i926","enabled":true,"last_modified":1480349202708,"details":{"who":"All Firefox users who have this add-on installed in Firefox 39 and above.\r\n","created":"2015-06-09T15:28:46Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1173154","name":"RealPlayer Browser Record Plugin","why":"Certain versions of this extension are causing startup crashes in Firefox 39 and above.\r\n"},"versionRange":[{"targetApplication":[{"minVersion":"39.0a1","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"}],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"09868783-261a-ac24-059d-fc772218c1ba","schema":1480349193877},{"guid":"/^(torntv@torntv\\.com|trtv3@trtv\\.com|torntv2@torntv\\.com|e2fd07a6-e282-4f2e-8965-85565fcb6384@b69158e6-3c3b-476c-9d98-ae5838c5b707\\.com)$/","blockID":"i529","enabled":true,"last_modified":1480349202677,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2013-12-20T14:46:03Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=949559","name":"TornTV","why":"The installer that includes this add-on violates the <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> by being silently installed."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"040e5ec2-ea34-816a-f99f-93296ce845e8","schema":1480349193877},{"guid":"249911bc-d1bd-4d66-8c17-df533609e6d8@c76f3de9-939e-4922-b73c-5d7a3139375d.com","blockID":"i532","enabled":true,"last_modified":1480349202631,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2013-12-20T15:02:01Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=949672","name":"Feven","why":"The installer that includes this add-on violates the <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> by making changes that can't be easily reverted and using multiple IDs."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"d32b850d-82d5-b63d-087c-fb2041b2c232","schema":1480349193877},{"guid":"thefoxonlybetter@quicksaver","blockID":"i704","enabled":true,"last_modified":1480349202588,"details":{"who":"All Firefox users who have any of these versions of the add-on installed.","created":"2014-08-27T14:49:02Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1053469","name":"The Fox, Only Better (malicious versions)","why":"Certain versions of The Fox, Only Better weren't developed by the original developer, and are likely malicious in nature. This violates the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> for reusing an already existent ID."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"0.*","severity":3}],"prefs":[],"id":"79ea6621-b414-17a4-4872-bfc4af7fd428","schema":1480349193877},{"guid":"{B40794A0-7477-4335-95C5-8CB9BBC5C4A5}","blockID":"i429","enabled":true,"last_modified":1480349202541,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-07-30T14:31:17Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=899178","name":"Video Player 1.3 (malware)","why":"This add-on is malware that spreads spam through Facebook."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"d98b2b76-4082-3387-ae33-971d973fa278","schema":1480349193877},{"guid":"[email protected]","blockID":"i648","enabled":true,"last_modified":1480349202462,"details":{"who":"All Firefox users who have this version of the add-on installed.","created":"2014-07-10T15:12:48Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1036757","name":"YouTube Enhancer Plus, versions between 199.7.0 and 208.7.0","why":"Certain versions of the YouTube Enhancer Plus extension weren't developed by the original developer, and are likely malicious in nature. This violates the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> for reusing an already existent ID."},"versionRange":[{"targetApplication":[],"minVersion":"199.7.0","maxVersion":"208.7.0","severity":3}],"prefs":[],"id":"7e64d7fc-ff16-8687-dbd1-bc4c7dfc5097","schema":1480349193877},{"guid":"[email protected]","blockID":"i362","enabled":true,"last_modified":1480349202429,"details":{"who":"All users who have this add-on installed. Users who wish to enable it again can do so in the Add-ons Manager tab.","created":"2013-06-06T12:57:29Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=863387","name":"Default Tab","why":"Old versions of this add-on had been silently installed into users' systems, without showing the opt-in install page that is built into Firefox."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"1.4.4","severity":1}],"prefs":[],"id":"df3fe753-5bae-bfb4-022b-6b6bfc534937","schema":1480349193877},{"guid":"{7D4F1959-3F72-49d5-8E59-F02F8AA6815D}","blockID":"i394","enabled":true,"last_modified":1480349202341,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2013-06-25T12:40:41Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=881447","name":"Updater By SweetPacks","why":"This is a companion add-on for the SweetPacks Toolbar which <a href=\"https://addons.mozilla.org/en-US/firefox/blocked/i392\">is blocked</a> due to guideline violations."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"851c2b8e-ea19-3a63-eac5-f931a8da5d6e","schema":1480349193877},{"guid":"[email protected]","blockID":"i776","enabled":true,"last_modified":1480349202307,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2014-10-31T16:23:36Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1076771","name":"GoSave","why":"This add-on is silently installed and changes user settings without consent, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>"},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":["browser.startup.homepage","browser.search.defaultenginename"],"id":"ee1e1a44-b51b-9f12-819d-64c3e515a147","schema":1480349193877},{"guid":"[email protected]","blockID":"i318","enabled":true,"last_modified":1480349202280,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-03-20T14:40:32Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=812264","name":"IncrediBar","why":"This add-on doesn't follow our <a href=\"https://developer.mozilla.org/en-US/docs/Addons/Add-on_guidelines\">Add-on Guidelines</a>, bypassing our third party install opt-in screen. Users who wish to continue using this extension can enable it in the Add-ons Manager."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"9e84b07c-84d5-c932-85f2-589713d7e380","schema":1480349193877},{"guid":"[email protected]","blockID":"i870","enabled":true,"last_modified":1480349202252,"details":{"who":"All users who have this add-on installed.","created":"2015-03-04T14:34:08Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1131159","name":"Flash Player 11 (malware)","why":"This is a malicious add-on that goes by the the name \"Flash Player 11\"."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"71d961b2-37d1-d393-76f5-3afeef57e749","schema":1480349193877},{"guid":"jid1-qj0w91o64N7Eeg@jetpack","blockID":"i650","enabled":true,"last_modified":1480349202186,"details":{"who":"All Firefox users who have this version of the add-on installed.","created":"2014-07-10T15:14:26Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1036757","name":"YouTube ALL HTML5, versions between 39.5.1 and 47.0.4","why":"Certain versions of the YouTube ALL HTML5 extension weren't developed by the original developer, and are likely malicious in nature. This violates the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> for reusing an already existent ID."},"versionRange":[{"targetApplication":[],"minVersion":"39.5.1","maxVersion":"47.0.4","severity":3}],"prefs":[],"id":"b30b1f7a-2a30-a6cd-fc20-6c9cb23c7198","schema":1480349193877},{"guid":"4zffxtbr-bs@VideoDownloadConverter_4z.com","blockID":"i507","enabled":true,"last_modified":1480349202156,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-12-12T15:37:23Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=949266","name":"VideoDownloadConverter","why":"Certain versions of this add-on contains an executable that is flagged by multiple tools as malware. Newer versions no longer use it."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"5.75.3.25126","severity":1}],"prefs":[],"id":"0a0f106a-ecc6-c537-1818-b36934943e91","schema":1480349193877},{"guid":"[email protected]","blockID":"i656","enabled":true,"last_modified":1480349202125,"details":{"who":"All Firefox users who have this version of the add-on installed.","created":"2014-07-10T15:22:59Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1036757","name":"High Definition Video, version 102.0","why":"Certain versions of the High Definition Video extension weren't developed by the original developer, and are likely malicious in nature. This violates the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> for reusing an already existent ID."},"versionRange":[{"targetApplication":[],"minVersion":"102.0","maxVersion":"102.0","severity":3}],"prefs":[],"id":"972249b2-bba8-b508-2ead-c336631135ac","schema":1480349193877},{"guid":"@video_downloader_pro","blockID":"i1265","enabled":true,"last_modified":1480349202099,"details":{"who":"Users of versions of 1.2.1 to 1.2.5 inclusive.","created":"2016-08-26T18:26:39Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1298335","name":"Video Downloader Pro","why":"Versions 1.2.1 to 1.2.5 of Video Downloader Pro included code that violated our polices - affected versions send every visited url to a remote server without the user's consent.  Versions older than 1.2.1 and more recent than 1.2.5 are okay."},"versionRange":[{"targetApplication":[],"minVersion":"1.2.1","maxVersion":"1.2.5","severity":1}],"prefs":[],"id":"ff9c8def-7d50-66b4-d42a-f9a4b04bd224","schema":1480349193877},{"guid":"[email protected]","blockID":"i509","enabled":true,"last_modified":1480349202067,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-12-16T16:15:15Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=950846","name":"Face follow","why":"This add-on spams users' Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"56f15747-af8c-342c-6877-a41eeacded84","schema":1480349193877},{"guid":"wecarereminder@bryan","blockID":"i666","enabled":true,"last_modified":1480349202039,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2014-07-10T16:18:36Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=818614","name":"We-Care Reminder","why":"This add-on is being silently installed by various software packages, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"51e0ead7-144c-c1f4-32f2-25fc5fcde870","schema":1480349193877},{"guid":"/^({83a8ce1b-683c-4784-b86d-9eb601b59f38}|{ef1feedd-d8da-4930-96f1-0a1a598375c6}|{79ff1aae-701f-4ca5-aea3-74b3eac6f01b}|{8a184644-a171-4b05-bc9a-28d75ffc9505}|{bc09c55d-0375-4dcc-836e-0e3c8addfbda}|{cef81415-2059-4dd5-9829-1aef3cf27f4f})$/","blockID":"i526","enabled":true,"last_modified":1480349202010,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2013-12-20T14:12:31Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=949566","name":"KeyBar add-on","why":"The installer that includes this add-on violates the <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> by making changes that can't be easily reverted and uses multiple IDs."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"9dfa4e92-bbf2-66d1-59a9-51402d1d226c","schema":1480349193877},{"guid":"{d9284e50-81fc-11da-a72b-0800200c9a66}","blockID":"i806","enabled":true,"last_modified":1480349201976,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable the add-on in the Add-on Manager.","created":"2014-12-16T08:35:41Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1106948","name":"Yoono","why":"Starting with Firefox 34, current versions of the Yoono add-on cause all tabs to appear blank."},"versionRange":[{"targetApplication":[{"minVersion":"34.0a1","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"}],"minVersion":"0","maxVersion":"7.7.34","severity":1}],"prefs":[],"id":"ccdceb04-3083-012f-9d9f-aac85f10b494","schema":1480349193877},{"guid":"{f2548724-373f-45fe-be6a-3a85e87b7711}","blockID":"i768","enabled":true,"last_modified":1480349201854,"details":{"who":"All Firefox users who have this add-on installed.","created":"2014-10-30T14:52:09Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1088726","name":"Astro New Tab","why":"This add-on is silently installed and is considered malware, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":["browser.startup.homepage","browser.search.defaultenginename"],"id":"8510e9e2-c7d8-90d0-a2ff-eb09293acc6e","schema":1480349193877},{"guid":"KSqOiTeSJEDZtTGuvc18PdPmYodROmYzfpoyiCr2@jetpack","blockID":"i1032","enabled":true,"last_modified":1480349201504,"details":{"who":"All users who have this add-on installed.","created":"2015-10-05T16:22:58Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1211172","name":"Video Player (malware)","why":"This is a malicious add-on that hijacks Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"3d9188ac-235f-773a-52a2-261b3ea9c03c","schema":1480349193877},{"guid":"{849ded12-59e9-4dae-8f86-918b70d213dc}","blockID":"i708","enabled":true,"last_modified":1480349201453,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2014-09-02T16:29:08Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1047102","name":"Astromenda New Tab","why":"This add-on is silently installed and changes homepage and search settings without the user's consent, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":["browser.startup.homepage","browser.search.defaultenginename"],"id":"a319bfee-464f-1c33-61ad-738c52842fbd","schema":1480349193877},{"guid":"[email protected]","blockID":"i1018","enabled":true,"last_modified":1480349201425,"details":{"who":"All users who have this add-on installed.","created":"2015-09-24T16:04:53Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1208196","name":"GreenPlayer (malware)","why":"This is a malicious add-on that hijacks Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"9c47d940-bdd9-729f-e32e-1774d87f24b5","schema":1480349193877},{"guid":"[email protected]","blockID":"i588","enabled":true,"last_modified":1480349201398,"details":{"who":"All Firefox users who have this add-on installed.","created":"2014-06-03T15:53:15Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1011316","name":"Quick Start (malware)","why":"This add-on appears to be malware that is installed without user consent."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"2affbebe-8776-3edb-28b9-237cb8b85f97","schema":1480349193877},{"guid":"/^(matchersite(pro(srcs?)?)?\\@matchersite(pro(srcs?)?)?\\.com)|((pro)?sitematcher(_srcs?|pro|site|sitesrc|-generic)?\\@(pro)?sitematcher(_srcs?|pro|site|sitesrc|-generic)?\\.com)$/","blockID":"i668","enabled":true,"last_modified":1480349201372,"details":{"who":"All Firefox users who have any of these add-ons installed. User who wish to continue using these add-ons can enable them in the Add-ons Manager.","created":"2014-07-17T14:35:14Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1039892","name":"Site Matcher","why":"This is a group of add-ons that are being distributed under multiple different IDs and likely being silently installed, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"52e1a2de-ab35-be27-4810-334f681ccc4a","schema":1480349193877},{"guid":"{EEF73632-A085-4fd3-A778-ECD82C8CB297}","blockID":"i165","enabled":true,"last_modified":1480349201262,"details":{"who":"All Firefox users who have these add-ons installed.","created":"2012-10-29T16:41:06Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=806451","name":"Codec-M (malware)","why":"These are malicious add-ons that are distributed with a trojan and negatively affect web browsing."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"e5ecd02a-20ee-749b-d5cf-3d74d1173a1f","schema":1480349193877},{"guid":"[email protected]","blockID":"i688","enabled":true,"last_modified":1480349201235,"details":{"who":"All Firefox users who have this add-on installed.","created":"2014-08-06T17:13:00Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1049533","name":"FinFisher (malware)","why":"This is a malicious add-on that hides itself under the name Java_plugin, among others."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"98aca74a-69c7-9960-cccc-096a4a4adc6c","schema":1480349193877},{"guid":"jid1-vW9nopuIAJiRHw@jetpack","blockID":"i570","enabled":true,"last_modified":1480349201204,"details":{"who":"All Firefox users who have this add-on installed. Those who wish to continue using it can enable it in the Add-ons Manager.","created":"2014-03-31T16:17:27Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=990291","name":"SmileysWeLove","why":"This add-on is silently installed, reverts settings changes to enforce its own, and is also causing stability problems in Firefox, all in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"bf2abd66-f910-650e-89aa-cd1d5c2f8a89","schema":1480349193877},{"guid":"87aukfkausiopoawjsuifhasefgased278djasi@jetpack","blockID":"i1050","enabled":true,"last_modified":1480349201157,"details":{"who":"All users who have this add-on installed.","created":"2015-11-02T14:53:21Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1220461","name":"Trace Video (malware)","why":"This is a malicious add-on that poses as a video update and hijacks Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"cb4cfac0-79c2-0fbf-206a-324aa3abbea5","schema":1480349193877},{"guid":"{e44a1809-4d10-4ab8-b343-3326b64c7cdd}","blockID":"i451","enabled":true,"last_modified":1480349201128,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-09-18T16:33:57Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=916966","name":"Entrusted","why":"This add-on doesn't follow our <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>, manipulating settings without reverting them on removal. Users who wish to continue using this add-on can enable it in the Add-ons Manager."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"ad5f53ed-7a43-cb1f-cbd7-41808fac1791","schema":1480349193877},{"guid":"{21EAF666-26B3-4A3C-ABD0-CA2F5A326744}","blockID":"i620","enabled":true,"last_modified":1480349201096,"details":{"who":"All Firefox users who have this add-on installed.","created":"2014-06-12T15:27:00Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1024752","name":"V-Bates","why":"This add-on is probably silently installed, and is causing significant stability issues for users, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"2d8833db-01a7-a758-080f-19e47abc54cb","schema":1480349193877},{"guid":"{1FD91A9C-410C-4090-BBCC-55D3450EF433}","blockID":"i338","enabled":true,"last_modified":1480349201059,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-04-24T11:30:28Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=844979","name":"DataMngr (malware)","why":"This extension overrides search settings, and monitors any further changes done to them so that they can be reverted. This violates our <a href=\"https://developer.mozilla.org/en-US/docs/Addons/Add-on_guidelines\">add-on guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"2e35995f-bec6-aa2b-3372-346d3325f72e","schema":1480349193877},{"guid":"9598582LLKmjasieijkaslesae@jetpack","blockID":"i996","enabled":true,"last_modified":1480349201029,"details":{"who":"All users who have this add-on installed.","created":"2015-09-07T13:50:27Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1201165","name":"Secure Player (malware)","why":"This is a malicious add-on that takes over Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"52f9c6e7-f7d5-f52e-cc35-eb99ef8b4b6a","schema":1480349193877},{"guid":"{bf7380fa-e3b4-4db2-af3e-9d8783a45bfc}","blockID":"i406","enabled":true,"last_modified":1480349201000,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2013-06-27T10:46:53Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=776404","name":"uTorrentBar","why":"This add-on changes search settings without user interaction, and fails to reset them after it is removed. This violates our <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"3bcefc4b-110c-f3b8-17ad-f9fc97c1120a","schema":1480349193877},{"guid":"{ce7e73df-6a44-4028-8079-5927a588c948}","blockID":"i117","enabled":true,"last_modified":1480349200972,"details":{"who":"All Firefox users who have this add-on installed.","created":"2012-08-10T08:50:52Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=781269","name":"Search By Image (by Google)","why":"The Search By Image (by Google) extension causes very high CPU utilization during regular browsing, often damaging user experience significantly, in a way that is very difficult to associate with the extension.\r\n\r\nUsers who want to continue using the add-on regardless of its performance impact can enable it in the Add-ons Manager."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"1.0.8","severity":1}],"prefs":[],"id":"fb1f9aed-2f1f-3e2c-705d-3b34ca9168b6","schema":1480349193877},{"guid":"{424b0d11-e7fe-4a04-b7df-8f2c77f58aaf}","blockID":"i800","enabled":true,"last_modified":1480349200939,"details":{"who":"All Firefox users who have this add-on installed.","created":"2014-12-15T10:51:56Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1080839","name":"Astromenda NT","why":"This add-on is silently installed and is considered malware, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":["browser.startup.homepage","browser.search.defaultenginename"],"id":"07bdf6aa-cfc8-ed21-6b36-6f90af02b169","schema":1480349193877},{"guid":"[email protected]","blockID":"i618","enabled":true,"last_modified":1480349200911,"details":{"who":"All Firefox users who have these versions of the Ask Toolbar installed. Users who wish to continue using it can enable it in the Add-ons Manager.\r\n","created":"2014-06-12T14:25:04Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1024719","name":"Ask Toolbar (old Avira Security Toolbar bundle)","why":"Certain old versions of the Ask Toolbar are causing problems to users when trying to open new tabs. Using more recent versions of the Ask Toolbar should also fix this problem.\r\n"},"versionRange":[{"targetApplication":[],"minVersion":"3.15.31","maxVersion":"3.15.31.*","severity":1}],"prefs":[],"id":"825feb43-d6c2-7911-4189-6f589f612c34","schema":1480349193877},{"guid":"{167d9323-f7cc-48f5-948a-6f012831a69f}","blockID":"i262","enabled":true,"last_modified":1480349200885,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-01-29T13:33:25Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=812303","name":"WhiteSmoke (malware)","why":"This add-on is silently side-installed by other software, and doesn't do much more than changing the users' settings, without reverting them on removal."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"a8f249fe-3db8-64b8-da89-7b584337a7af","schema":1480349193877},{"guid":"/^({988919ff-0cd8-4d0c-bc7e-60d55a49eb64}|{494b9726-9084-415c-a499-68c07e187244}|{55b95864-3251-45e9-bb30-1a82589aaff1}|{eef3855c-fc2d-41e6-8d91-d368f51b3055}|{90a1b331-c2b4-4933-9f63-ba7b84d60d58}|{d2cf9842-af95-48cd-b873-bfbb48cd7f5e})$/","blockID":"i541","enabled":true,"last_modified":1480349200819,"details":{"who":"All Firefox users who have this add-on installed","created":"2014-01-28T14:09:08Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=963819","name":"MixiDJ (malware)","why":"This add-on has been repeatedly blocked before and keeps showing up with new add-on IDs, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"36196aed-9d0d-ebee-adf1-d1f7fadbc48f","schema":1480349193877},{"guid":"{29b136c9-938d-4d3d-8df8-d649d9b74d02}","blockID":"i598","enabled":true,"last_modified":1480349200775,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2014-06-12T13:21:25Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1011322","name":"Mega Browse","why":"This add-on is silently installed, in violation with our <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"63b1c965-27c3-cd06-1b76-8721add39edf","schema":1480349193877},{"guid":"{6e7f6f9f-8ce6-4611-add2-05f0f7049ee6}","blockID":"i868","enabled":true,"last_modified":1480349200690,"details":{"who":"All users who have this add-on installed. Users who wish to continue using the add-on can enable it in the Add-ons Manager.","created":"2015-02-26T14:58:59Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1086574","name":"Word Proser","why":"This add-on is silently installed into users' systems, in violation of our <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"f54797da-cdcd-351a-c95e-874b64b0d226","schema":1480349193877},{"guid":"{02edb56b-9b33-435b-b7df-b2843273a694}","blockID":"i438","enabled":true,"last_modified":1480349200338,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-08-09T15:27:49Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=896581","name":"KeyBar Toolbar","why":"This add-on doesn't follow our <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>. It is installed bypassing the Firefox opt-in screen, and manipulates settings without reverting them on removal. Users who wish to continue using this add-on can enable it in the Add-ons Manager."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"896710d2-5a65-e9b0-845b-05aa72c2bd51","schema":1480349193877},{"guid":"{e1aaa9f8-4500-47f1-9a0a-b02bd60e4076}","blockID":"i646","enabled":true,"last_modified":1480349200312,"details":{"who":"All Firefox users who have this version of the add-on installed.","created":"2014-07-10T15:10:05Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1036757","name":"Youtube Video Replay, version 178.7.0","why":"Certain versions of the Youtube Video Replay extension weren't developed by the original developer, and are likely malicious in nature. This violates the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> for reusing an already existent ID."},"versionRange":[{"targetApplication":[],"minVersion":"178.7.0","maxVersion":"178.7.0","severity":3}],"prefs":[],"id":"ac5d1083-6753-bbc1-a83d-c63c35371b22","schema":1480349193877},{"guid":"{1cdbda58-45f8-4d91-b566-8edce18f8d0a}","blockID":"i724","enabled":true,"last_modified":1480349200288,"details":{"who":"All Firefox users who have this add-on installed.","created":"2014-10-13T16:00:08Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1080835","name":"Website Counselor Pro","why":"This add-on is silently installed into users' systems. It uses very unsafe practices to load its code, and leaks information of all web browsing activity. These are all violations of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"7b70bd36-d2f7-26fa-9038-8b8dd132cd81","schema":1480349193877},{"guid":"{b12785f5-d8d0-4530-a3ea-5c4263b85bef}","blockID":"i988","enabled":true,"last_modified":1480349200171,"details":{"who":"All users who have this add-on installed. Those who wish continue using this add-on can enable it in the Add-ons Manager.","created":"2015-08-17T16:04:35Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1161573","name":"Hero Fighter Community Toolbar","why":"This add-on overrides user's preferences without consent, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"3e6d73f2-e8e3-af69-866e-30d3977b09e4","schema":1480349193877},{"guid":"{c2d64ff7-0ab8-4263-89c9-ea3b0f8f050c}","blockID":"i39","enabled":true,"last_modified":1480349200047,"details":{"who":"Users of MediaBar versions 4.3.1.00 and below in all versions of Firefox.","created":"2011-07-19T10:18:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=665775","name":"MediaBar","why":"This add-on causes a high volume of crashes and is incompatible with certain versions of Firefox."},"versionRange":[{"targetApplication":[],"minVersion":"0.1","maxVersion":"4.3.1.00","severity":1}],"prefs":[],"id":"e928a115-9d8e-86a4-e2c7-de39627bd9bf","schema":1480349193877},{"guid":"{9edd0ea8-2819-47c2-8320-b007d5996f8a}","blockID":"i684","enabled":true,"last_modified":1480349199962,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using this add-on can enable it in the Add-ons Manager.","created":"2014-08-06T13:33:33Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1033857","name":"webget","why":"This add-on is believed to be silently installed in Firefox, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":["browser.search.defaultenginename"],"id":"d38561f5-370f-14be-1443-a74dad29b1f3","schema":1480349193877},{"guid":"/^({ad9a41d2-9a49-4fa6-a79e-71a0785364c8})|(ffxtlbr@mysearchdial\\.com)$/","blockID":"i670","enabled":true,"last_modified":1480349199927,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using this add-on can enable it in the Add-ons Manager.","created":"2014-07-18T15:47:35Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1036740","name":"MySearchDial","why":"This add-on has been repeatedly been silently installed into users' systems, and is known for changing the default search without user consent, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":["browser.search.defaultenginename"],"id":"a04075e6-5df2-2e1f-85a6-3a0171247349","schema":1480349193877},{"guid":"[email protected]","blockID":"i982","enabled":true,"last_modified":1480349199886,"details":{"who":"All users who have these versions installed. The latest versions of this add-on aren't blocked, so updating to them should be sufficient to fix this problem.","created":"2015-08-11T11:25:43Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1190630","name":"IBM Endpoint Manager for Remote Control 9.0.1.1 to 9.0.1.100","why":"Certain versions of the IBM Remote Control add-on could leave a machine vulnerable to run untrusted code."},"versionRange":[{"targetApplication":[],"minVersion":"9.0.1.1","maxVersion":"9.0.1.100","severity":1}],"prefs":[],"id":"f6e3e5d2-9331-1097-ba4b-cf2e484b7187","schema":1480349193877},{"guid":"[email protected]","blockID":"i1030","enabled":true,"last_modified":1480349199850,"details":{"who":"All users who have this add-on installed. Users who wish to continue using this add-on can enable it in the Add-ons Manager.","created":"2015-10-01T16:53:06Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1205479","name":"Todoist","why":"This add-on is sending all sites visited by the user to a remote server, additionally doing so in an unsafe way."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"3.9","severity":1}],"prefs":[],"id":"d0a84aab-0661-b3c5-c184-a2fd3f9dfb9c","schema":1480349193877},{"guid":"/^({1f43c8af-e9e4-4e5a-b77a-f51c7a916324}|{3a3bd700-322e-440a-8a6a-37243d5c7f92}|{6a5b9fc2-733a-4964-a96a-958dd3f3878e}|{7b5d6334-8bc7-4bca-a13e-ff218d5a3f17}|{b87bca5b-2b5d-4ae8-ad53-997aa2e238d4}|{bf8e032b-150f-4656-8f2d-6b5c4a646e0d})$/","blockID":"i1136","enabled":true,"last_modified":1480349199818,"details":{"who":"All users who have this add-on installed.","created":"2016-03-04T17:56:08Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1251940","name":"Watcher (malware)","why":"This is a malicious add-on that hides itself from view and disables various security features in Firefox."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"a2d0378f-ebe4-678c-62d8-2e4c6a613c17","schema":1480349193877},{"guid":"[email protected]","blockID":"i814","enabled":true,"last_modified":1480349199791,"details":{"who":"All Firefox users who have this add-on installed.","created":"2015-01-09T12:49:05Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1119657","name":"One Tab (malware)","why":"This add-on is silently installed into users' systems without their consent and performs unwanted operations."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"387c054d-cc9f-7ebd-c814-b4c1fbcb2880","schema":1480349193877},{"guid":"[email protected]","blockID":"i1128","enabled":true,"last_modified":1480349199768,"details":{"who":"All users who have this add-on installed.","created":"2016-03-01T21:18:33Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1251911","name":"YouTube Unblocker","why":"The add-on has a mechanism that updates certain configuration files from the developer\u2019s website. This mechanism has a vulnerability that is being exploited through this website (unblocker.yt) to change security settings in Firefox and install malicious add-ons."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"3395fce1-42dd-e31a-1466-2da3f32456a0","schema":1480349193877},{"guid":"{97E22097-9A2F-45b1-8DAF-36AD648C7EF4}","blockID":"i916","enabled":true,"last_modified":1480349199738,"details":{"who":"All Firefox users who have this add-on installed in Firefox 39 and above.\r\n","created":"2015-06-02T09:57:38Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1170633","name":"RealPlayer Browser Record Plugin","why":"Certain versions of this extension are causing startup crashes in Firefox 39 and above.\r\n"},"versionRange":[{"targetApplication":[{"minVersion":"39.0a1","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"}],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"94fba774-c4e6-046a-bc7d-ede787a9d0fe","schema":1480349193877},{"guid":"{b64982b1-d112-42b5-b1e4-d3867c4533f8}","blockID":"i167","enabled":true,"last_modified":1480349199673,"details":{"who":"All Firefox users who have this add-on installed.","created":"2012-10-29T17:17:46Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=805973","name":"Browser Manager","why":"This add-on is a frequent cause for browser crashes and other problems."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"00bbe501-2d27-7a1c-c344-6eea1c707473","schema":1480349193877},{"guid":"{58bd07eb-0ee0-4df0-8121-dc9b693373df}","blockID":"i286","enabled":true,"last_modified":1480349199619,"details":{"who":"All Firefox users who have this extension installed.","created":"2013-02-18T10:54:28Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=842206","name":"Browser Protect / bProtector (malware)","why":"This extension is malicious and is installed under false pretenses, causing problems for many Firefox users. Note that this is not the same <a href=\"https://addons.mozilla.org/firefox/addon/browserprotect/\">BrowserProtect extension</a> that is listed on our add-ons site. That one is safe to use."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"b40a60d3-b9eb-09eb-bb02-d50b27aaac9f","schema":1480349193877},{"guid":"[email protected]","blockID":"i465","enabled":true,"last_modified":1480349199560,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-11-01T15:21:49Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=845610","name":"TornTV","why":"This add-on doesn't follow our <a href=\"https://developer.mozilla.org/en-US/docs/Addons/Add-on_guidelines\">Add-on Guidelines</a>, bypassing our third party install opt-in screen. Users who wish to continue using this extension can enable it in the Add-ons Manager."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"3d4d8a33-2eff-2556-c699-9be0841a8cd4","schema":1480349193877},{"guid":"[email protected]","blockID":"i1231","enabled":true,"last_modified":1480349199528,"details":{"who":"All users who have this add-on installed.","created":"2016-06-09T14:50:27Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1278932","name":"YouTube downloader","why":"The add-on has a mechanism that updates certain configuration files from the developer\u2019s website. This mechanism has a vulnerability that can being exploited through this website (downloader.yt) to change security settings in Firefox and/or install malicious add-ons. \r\n"},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"8514eaee-850c-e27a-a058-8badeeafc26e","schema":1480349193877},{"guid":"[email protected]","blockID":"i658","enabled":true,"last_modified":1480349199504,"details":{"who":"All Firefox users who have this version of the add-on installed.","created":"2014-07-10T15:27:51Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1036757","name":"Low Quality Flash, versions between 46.2 and 47.1","why":"Certain versions of the Low Quality Flash extension weren't developed by the original developer, and are likely malicious in nature. This violates the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> for reusing an already existent ID."},"versionRange":[{"targetApplication":[],"minVersion":"46.2","maxVersion":"47.1","severity":3}],"prefs":[],"id":"b869fae6-c18c-0d39-59a2-603814656404","schema":1480349193877},{"guid":"{d2cf9842-af95-48cd-b873-bfbb48cd7f5e}","blockID":"i439","enabled":true,"last_modified":1480349199478,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-08-09T16:08:18Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=902569","name":"Mixi DJ V45","why":"This is another instance of the <a href=\"https://addons.mozilla.org/en-US/firefox/blocked/i354\">previously blocked</a> Mixi DJ add-on, which doesn't follow our <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>. If you wish to continue using it, it can be enabled in the Add-ons Manager."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"e81c31fc-265e-61b9-d4c1-0e2f31f1652e","schema":1480349193877},{"guid":"/^({b95faac1-a3d7-4d69-8943-ddd5a487d966}|{ecce0073-a837-45a2-95b9-600420505f7e}|{2713b394-286f-4d7c-89ea-4174eeab9f5a}|{da7a20cf-bef4-4342-ad78-0240fdf87055})$/","blockID":"i624","enabled":true,"last_modified":1480349199446,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using this add-on can enable it in the Add-ons Manager.","created":"2014-06-18T13:50:38Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=947482","name":"WiseConvert","why":"This add-on is known to change user settings without their consent, is distributed under multiple add-on IDs, and is also correlated with reports of tab functions being broken in Firefox, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>.\r\n"},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"ed57d7a6-5996-c7da-8e07-1ad125183e84","schema":1480349193877},{"guid":"{f894a29a-f065-40c3-bb19-da6057778493}","blockID":"i742","enabled":true,"last_modified":1480349199083,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2014-10-17T15:46:59Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1080817","name":"Spigot Shopping Assistant","why":"This add-on appears to be silently installed into users' systems, and changes settings without consent, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"39d8334e-4b7c-4336-2d90-e6aa2d783967","schema":1480349193877},{"guid":"[email protected]","blockID":"i467","enabled":true,"last_modified":1480349199026,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-11-07T14:08:48Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=935797","name":"Analytics","why":"This add-on bypasses the external install opt in screen in Firefox, violating the <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>. Users who wish to continue using this add-on can enable it in the Add-ons Manager."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"ffbed3f3-e5c9-bc6c-7530-f68f47b7efd6","schema":1480349193877},{"guid":"{C4A4F5A0-4B89-4392-AFAC-D58010E349AF}","blockID":"i678","enabled":true,"last_modified":1480349198947,"details":{"who":"All Firefox users who have this add-on installed. If you wish to continue using it, you can enable it in the Add-ons Manager.","created":"2014-07-23T14:12:23Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=895668","name":"DataMngr","why":"This add-on is generally silently installed, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"151021fc-ce4e-a734-e075-4ece19610f64","schema":1480349193877},{"guid":"HxLVJK1ioigz9WEWo8QgCs3evE7uW6LEExAniBGG@jetpack","blockID":"i1036","enabled":true,"last_modified":1480349198894,"details":{"who":"All users who have this add-on installed.","created":"2015-10-05T16:37:08Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1211170","name":"Mega Player (malware)","why":"This is a malicious add-on that hijacks Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"32e34b41-a73c-72d4-c96c-136917ad1d4d","schema":1480349193877},{"guid":"{6af08a71-380e-42dd-9312-0111d2bc0630}","blockID":"i822","enabled":true,"last_modified":1480349198826,"details":{"who":"All Firefox users who have this add-on installed.","created":"2015-01-27T09:50:40Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1126353","name":"{6af08a71-380e-42dd-9312-0111d2bc0630} (malware)","why":"This add-on appears to be malware, hiding itself in the Add-ons Manager, and keeping track of certain user actions."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"96d0c12b-a6cf-4539-c1cf-a1c75c14ff24","schema":1480349193877},{"guid":"[email protected]","blockID":"i550","enabled":true,"last_modified":1480349198744,"details":{"who":"All Firefox users who have this add-on installed.","created":"2014-02-06T15:49:25Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=968445","name":"Video Plugin Facebook (malware)","why":"This add-on is malware that hijacks Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"c394d10b-384e-cbd0-f357-9c521715c373","schema":1480349193877},{"guid":"[email protected]","blockID":"i444","enabled":true,"last_modified":1480349198667,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-08-26T14:49:48Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=909433","name":"Flash Player (malware)","why":"This add-on is malware disguised as the Flash Player plugin."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"c6557989-1b59-72a9-da25-b816c4a4c723","schema":1480349193877},{"guid":"[email protected]","blockID":"i740","enabled":true,"last_modified":1480349198637,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2014-10-17T15:39:59Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=963776","name":"Advanced SystemCare Surfing Protection","why":"This add-on is silently installed into users' systems, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"4405f99d-c9b7-c496-1b45-268163ce29b7","schema":1480349193877},{"guid":"{6E19037A-12E3-4295-8915-ED48BC341614}","blockID":"i24","enabled":true,"last_modified":1480349198606,"details":{"who":"Users of RelevantKnowledge version 1.3.328.4 and older in Firefox 4 and later.","created":"2011-03-02T17:42:56Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=615518","name":"comScore RelevantKnowledge","why":"This add-on causes a high volume of Firefox crashes."},"versionRange":[{"targetApplication":[{"minVersion":"3.7a1pre","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"}],"minVersion":"0.1","maxVersion":"1.3.328.4","severity":1}],"prefs":[],"id":"7c189c5e-f95b-0aef-e9e3-8e879336503b","schema":1480349193877},{"guid":"[email protected]","blockID":"i91","enabled":true,"last_modified":1480349198547,"details":{"who":"All Firefox users who have installed this add-on.","created":"2012-05-14T14:16:43Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=754648","name":"Remove My Timeline (malware)","why":"Versions of this add-on prior to 0.81.44 automatically post message to users' walls and hide them from their view. Version 0.81.44 corrects this."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"0.81.43","severity":1}],"prefs":[],"id":"5ee3e72e-96fb-c150-fc50-dd581e960963","schema":1480349193877},{"guid":"/^(93abedcf-8e3a-4d02-b761-d1441e437c09@243f129d-aee2-42c2-bcd1-48858e1c22fd\\.com|9acfc440-ac2d-417a-a64c-f6f14653b712@09f9a966-9258-4b12-af32-da29bdcc28c5\\.com|58ad0086-1cfb-48bb-8ad2-33a8905572bc@5715d2be-69b9-4930-8f7e-64bdeb961cfd\\.com)$/","blockID":"i544","enabled":true,"last_modified":1480349198510,"details":{"who":"All Firefox users who have this add-on installed. If you wish to continue using it, it can be enabled in the Add-ons Manager.","created":"2014-01-30T11:51:19Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=949596","name":"SuperLyrics","why":"This add-on is in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>, using multiple add-on IDs and potentially doing other unwanted activities."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"d8d25967-9814-3b65-0787-a0525c16e11e","schema":1480349193877},{"guid":"[email protected]","blockID":"i980","enabled":true,"last_modified":1480349198483,"details":{"who":"All users who have this add-on installed.","created":"2015-08-11T11:20:01Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1192468","name":"BestSavEFOrYoU (malware)","why":"This is a malicious add-on that hijacks Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"4beb917f-68f2-1f91-beed-dff6d83006f8","schema":1480349193877},{"guid":"[email protected]","blockID":"i1040","enabled":true,"last_modified":1480349198458,"details":{"who":"All users who have this add-on installed.","created":"2015-10-07T13:03:37Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1212451","name":"Async Codec (malware)","why":"This is a malicious add-on that hijacks Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"fb6ab4ce-5517-bd68-2cf7-a93a109a528a","schema":1480349193877},{"guid":"[email protected]","blockID":"i471","enabled":true,"last_modified":1480349198433,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-11-07T15:38:33Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=935779","name":"Installer bundle (malware)","why":"This add-on is part of a malicious Firefox installer bundle."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"3a9e04c7-5e64-6297-8442-2816915aad77","schema":1480349193877},{"guid":"auto-plugin-checker@jetpack","blockID":"i1210","enabled":true,"last_modified":1480349198401,"details":{"who":"All users of this add-on. If you wish to continue using it, you can enable it in the Add-ons Manager.","created":"2016-05-04T16:25:27Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1270175","name":"auto-plugin-checker","why":"This add-on reports every visited URL to a third party without disclosing it to the user."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"3e202419-5318-2025-b579-c828af24a06e","schema":1480349193877},{"guid":"[email protected]","blockID":"i432","enabled":true,"last_modified":1480349198372,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-08-06T13:16:22Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=902072","name":"FB Color Changer (malware)","why":"This add-on includes malicious code that spams users' Facebook accounts with unwanted messages."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"b6943f35-9429-1f8e-bf8e-fe37979fe183","schema":1480349193877},{"guid":"{99079a25-328f-4bd4-be04-00955acaa0a7}","blockID":"i402","enabled":true,"last_modified":1480349198341,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2013-06-25T15:16:17Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=835678","name":"Searchqu","why":"This group of add-ons is silently installed, bypassing our install opt-in screen. This violates our <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"16008331-8b47-57c8-a6f7-989914d1cb8a","schema":1480349193877},{"guid":"{81b13b5d-fba1-49fd-9a6b-189483ac548a}","blockID":"i473","enabled":true,"last_modified":1480349198317,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-11-07T15:38:43Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=935779","name":"Installer bundle (malware)","why":"This add-on is part of a malicious Firefox installer bundle."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"76debc7b-b875-6da4-4342-1243cbe437f6","schema":1480349193877},{"guid":"{e935dd68-f90d-46a6-b89e-c4657534b353}","blockID":"i732","enabled":true,"last_modified":1480349198260,"details":{"who":"All Firefox users who have this add-on installed.","created":"2014-10-16T16:38:24Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1073810","name":"Sites Pro","why":"This add-on is silently installed into users' systems. It uses very unsafe practices to load its code, and leaks information of all web browsing activity. These are all violations of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"97fdc235-ac1a-9f20-1b4a-17c2f0d89ad1","schema":1480349193877},{"guid":"{32da2f20-827d-40aa-a3b4-2fc4a294352e}","blockID":"i748","enabled":true,"last_modified":1480349198223,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2014-10-17T16:02:20Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=963787","name":"Start Page","why":"This add-on is silently installed into users' systems, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"6c980c8e-4a3c-7912-4a3a-80add457575a","schema":1480349193877},{"guid":"[email protected]","blockID":"i431","enabled":true,"last_modified":1480349198192,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-08-05T16:43:24Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=901770","name":"F-Secure Security Pack (malware)","why":"This is a malicious add-on that uses a deceptive name and hijacks social networks."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"fbd89a9d-9c98-8481-e4cf-93e327ca8be1","schema":1480349193877},{"guid":"{cc6cc772-f121-49e0-b1f0-c26583cb0c5e}","blockID":"i716","enabled":true,"last_modified":1480349198148,"details":{"who":"All Firefox users who have this add-on installed.","created":"2014-10-02T12:12:34Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1073810","name":"Website Counselor","why":"This add-on is silently installed into users' systems. It uses very unsafe practices to load its code, and leaks information of all web browsing activity. These are all violations of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"debcd28c-884b-ca42-d983-6fabf91034dd","schema":1480349193877},{"guid":"{906000a4-88d9-4d52-b209-7a772970d91f}","blockID":"i474","enabled":true,"last_modified":1480349197744,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-11-07T15:38:48Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=935779","name":"Installer bundle (malware)","why":"This add-on is part of a malicious Firefox installer bundle."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"326d05b9-ace7-67c6-b094-aad926c185a5","schema":1480349193877},{"guid":"{A34CAF42-A3E3-11E5-945F-18C31D5D46B0}","blockID":"i1227","enabled":true,"last_modified":1480349197699,"details":{"who":"All users of this add-on. If you wish to continue using it, you can enable it in the Add-ons Manager.","created":"2016-05-31T15:45:53Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1274995","name":"Mococheck WAP browser","why":"This add-on downgrades the security of all iframes from https to http and changes important Firefox security preferences."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":["security.csp.enable","security.fileuri.strict_origin_policy","security.mixed_content.block_active_content"],"id":"2230a5ce-a8f8-a20a-7974-3b960a03aba9","schema":1480349193877},{"guid":"{AB2CE124-6272-4b12-94A9-7303C7397BD1}","blockID":"i20","enabled":true,"last_modified":1480349197667,"details":{"who":"Users of Skype extension versions below 5.2.0.7165 for all versions of Firefox.","created":"2011-01-20T18:39:25Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=627278","name":"Skype extension","why":"This add-on causes a high volume of Firefox crashes and introduces severe performance issues. Please <a href=\"http://www.skype.com/intl/en/get-skype/on-your-computer/click-and-call/\">update to the latest version</a>. For more information, please <a href=\"http://blog.mozilla.com/addons/2011/01/20/blocking-the-skype-toolbar-in-firefox/\">read our announcement</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0.1","maxVersion":"5.2.0.7164","severity":1}],"prefs":[],"id":"60e16015-1803-197a-3241-484aa961d18f","schema":1480349193877},{"guid":"f6682b47-e12f-400b-9bc0-43b3ccae69d1@39d6f481-b198-4349-9ebe-9a93a86f9267.com","blockID":"i682","enabled":true,"last_modified":1480349197636,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using this add-on can enable it in the Add-ons Manager.","created":"2014-08-04T16:07:20Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1043017","name":"enformation","why":"This add-on is being silently installed, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"a7ae65cd-0869-67e8-02f8-6d22c56a83d4","schema":1480349193877},{"guid":"[email protected]","blockID":"i537","enabled":true,"last_modified":1480349197604,"details":{"who":"All Firefox users who have this extension installed. If you want to continue using it, you can enable it in the Add-ons Manager.","created":"2014-01-23T15:51:48Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=950267","name":"Rally Toolbar","why":"The installer that includes this add-on violates the <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> by silently installing it."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"4ac6eb63-b51a-3296-5b02-bae77f424032","schema":1480349193877},{"guid":"[email protected]","blockID":"i774","enabled":true,"last_modified":1480349197578,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2014-10-31T16:22:53Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1076771","name":"YoutubeAdBlocke","why":"This add-on is silently installed and changes user settings without consent, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>\r\n"},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":["browser.startup.homepage","browser.search.defaultenginename"],"id":"4771da14-bcf2-19b1-3d71-bc61a1c7d457","schema":1480349193877},{"guid":"{49c53dce-afa0-49a1-a08b-2eb8e8444128}","blockID":"i441","enabled":true,"last_modified":1480349197550,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-08-09T16:58:50Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=844985","name":"ytbyclick","why":"This add-on is silently installed, violating our <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>. Users who wish to continue using this add-on can enable it in the Add-ons Manager."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"5f08d720-58c2-6acb-78ad-7af45c82c90b","schema":1480349193877},{"guid":"[email protected]","blockID":"i886","enabled":true,"last_modified":1480349197525,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using this add-on can enable it in the Add-on Manager.","created":"2015-04-10T16:25:21Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1152555","name":"Search Enginer","why":"This add-on appears to be malware, being silently installed and hijacking user settings, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":["browser.startup.homepage","browser.search.defaultenginename"],"id":"46de4f6e-2b29-7334-ebbb-e0048f114f7b","schema":1480349193877},{"guid":"{bb7b7a60-f574-47c2-8a0b-4c56f2da9802}","blockID":"i754","enabled":true,"last_modified":1480349197500,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2014-10-17T16:27:32Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1080850","name":"AdvanceElite","why":"This add-on is silently installed into users' systems, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"f222ceb2-9b69-89d1-8dce-042d8131a12e","schema":1480349193877},{"guid":"/^([email protected]|[email protected]|[email protected]|[email protected])$/","blockID":"i1119","enabled":true,"last_modified":1480349197468,"details":{"who":"All users who have these add-ons installed.","created":"2016-01-25T13:31:43Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1242721","name":"test.org add-ons (malware)","why":"These add-ons are malicious, or at least attempts at being malicious, using misleading names and including risky code."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"afd2a0d7-b050-44c9-4e45-b63696d9b22f","schema":1480349193877},{"guid":"/^((34qEOefiyYtRJT@IM5Munavn\\.com)|(Mro5Fm1Qgrmq7B@ByrE69VQfZvZdeg\\.com)|(KtoY3KGxrCe5ie@yITPUzbBtsHWeCdPmGe\\.com)|(9NgIdLK5Dq4ZMwmRo6zk@FNt2GCCLGyUuOD\\.com)|(NNux7bWWW@RBWyXdnl6VGls3WAwi\\.com)|(E3wI2n@PEHTuuNVu\\.com)|(2d3VuWrG6JHBXbQdbr@3BmSnQL\\.com))$/","blockID":"i324","enabled":true,"last_modified":1480349197432,"details":{"who":"All users who have this add-on installed.","created":"2013-03-22T14:48:00Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=841791","name":"Flash Player (malware)","why":"This extension is malware, installed pretending to be the Flash Player plugin."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"5be3a399-af3e-644e-369d-628273b3fdc2","schema":1480349193877},{"guid":"[email protected]","blockID":"i1263","enabled":true,"last_modified":1480349197404,"details":{"who":"All users who have version 1.1.1 or less of this add-on installed.","created":"2016-08-17T16:47:06Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1251911","name":"AXTARA Search (pre 1.1.2)","why":"Old versions of this add-on contained code from YouTube Unblocker, which was <a href=\"https://addons.mozilla.org/firefox/blocked/i1129\">originally blocked</a> due to malicious activity.  Version 1.1.2 is now okay."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"1.1.1","severity":3}],"prefs":[],"id":"c58be1c9-3d63-a948-219f-e3225e1eec8e","schema":1480349193877},{"guid":"{8f894ed3-0bf2-498e-a103-27ef6e88899f}","blockID":"i792","enabled":true,"last_modified":1480349197368,"details":{"who":"All Firefox users who have this add-on installed.","created":"2014-11-26T13:49:30Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1073810","name":"ExtraW","why":"This add-on is silently installed into users' systems. It uses very unsafe practices to load its code, and leaks information of all web browsing activity. These are all violations of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"bebc9e15-59a1-581d-0163-329d7414edff","schema":1480349193877},{"guid":"[email protected]","blockID":"i734","enabled":true,"last_modified":1480349197341,"details":{"who":"All Firefox users who have this add-on installed.\r\n","created":"2014-10-16T16:39:26Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1073810","name":"ProfSites","why":"This add-on is silently installed into users' systems. It uses very unsafe practices to load its code, and leaks information of all web browsing activity. These are all violations of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"0d6d84d7-0b3f-c5ab-57cc-6b66b0775a23","schema":1480349193877},{"guid":"{872b5b88-9db5-4310-bdd0-ac189557e5f5}","blockID":"i497","enabled":true,"last_modified":1480349197240,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2013-12-03T16:08:09Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=945530","name":"DVDVideoSoft Menu","why":"The installer that includes this add-on violates the <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> by making settings changes that can't be easily reverted."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"e8da89c4-c585-77e4-9872-591d20723a7e","schema":1480349193877},{"guid":"[email protected]","blockID":"i664","enabled":true,"last_modified":1480349197208,"details":{"who":"All Firefox users who have this add-on installed.","created":"2014-07-10T15:41:24Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1036757","name":"Taringa MP3 / Adobe Flash","why":"This is a malicious add-on that attempts to hide itself by impersonating the Adobe Flash plugin."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"6d0a7dda-d92a-c8e2-21be-c92b0a88ac8d","schema":1480349193877},{"guid":"[email protected]","blockID":"i1214","enabled":true,"last_modified":1480349197172,"details":{"who":"All users of this add-on. If you wish to continue using it, you can enable it in the Add-ons Manager.","created":"2016-05-17T16:55:39Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1271358","name":"iCloud Bookmarks","why":"This add-on is causing frequent and persistent crashing."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"1.4.22","severity":1}],"prefs":[],"id":"2dddd7a7-b081-45e2-3eeb-2a7f76a1465f","schema":1480349193877},{"guid":"[email protected]","blockID":"i660","enabled":true,"last_modified":1480349197136,"details":{"who":"All Firefox users who have this version of the add-on installed.","created":"2014-07-10T15:31:04Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1036757","name":"YouPlayer, versions between 79.9.8 and 208.0.1","why":"Certain versions of the YouPlayer extension weren't developed by the original developer, and are likely malicious in nature. This violates the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> for reusing an already existent ID."},"versionRange":[{"targetApplication":[],"minVersion":"79.9.8","maxVersion":"208.0.1","severity":3}],"prefs":[],"id":"82dca22b-b889-5d9d-3fc9-b2184851f2d1","schema":1480349193877},{"guid":"{df6bb2ec-333b-4267-8c4f-3f27dc8c6e07}","blockID":"i487","enabled":true,"last_modified":1480349197109,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-11-19T14:59:45Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=940681","name":"Facebook 2013 (malware)","why":"This is a malicious add-on that hijacks Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"5867c409-b342-121e-3c3b-426e2f0ba1d4","schema":1480349193877},{"guid":"/^({4e988b08-8c51-45c1-8d74-73e0c8724579}|{93ec97bf-fe43-4bca-a735-5c5d6a0a40c4}|{aed63b38-7428-4003-a052-ca6834d8bad3}|{0b5130a9-cc50-4ced-99d5-cda8cc12ae48}|{C4CFC0DE-134F-4466-B2A2-FF7C59A8BFAD})$/","blockID":"i524","enabled":true,"last_modified":1480349197082,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2013-12-20T13:43:21Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=947481","name":"SweetPacks","why":"The installer that includes this add-on violates the <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> by making changes that can't be easily reverted."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"1a3a26a2-cdaa-e5ba-f6ac-47b98ae2cc26","schema":1480349193877},{"guid":"[email protected]","blockID":"i950","enabled":true,"last_modified":1480349197056,"details":{"who":"All users who have this add-on installed on Thunderbird 38 and above.","created":"2015-07-15T09:34:44Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1183890","name":"FoxyProxy Standard for Thunderbird","why":"This add-on is causing consistent startup crashes on Thunderbird 38 and above."},"versionRange":[{"targetApplication":[{"minVersion":"38.0a2","guid":"{3550f703-e582-4d05-9a08-453d09bdfdc6}","maxVersion":"*"},{"minVersion":"2.35","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"*"}],"minVersion":"0","maxVersion":"4.5.5","severity":1}],"prefs":[],"id":"5ee8203d-bea2-6cd5-9ba0-d1922ffb3d21","schema":1480349193877},{"guid":"{82AF8DCA-6DE9-405D-BD5E-43525BDAD38A}","blockID":"i1056","enabled":true,"last_modified":1480349197027,"details":{"who":"All users who have this add-on installed in Firefox 43 and above. User who wish to continue using this add-on can enable it in the Add-ons Manager.","created":"2015-11-17T14:03:05Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1225639","name":"Skype Click to Call","why":"This add-on is associated with frequent shutdown crashes in Firefox."},"versionRange":[{"targetApplication":[{"minVersion":"43.0a1","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"}],"minVersion":"0","maxVersion":"7.5.0.9082","severity":1}],"prefs":[],"id":"484f8386-c415-7499-a8a0-f4e16f5a142f","schema":1480349193877},{"guid":"{22119944-ED35-4ab1-910B-E619EA06A115}","blockID":"i45","enabled":true,"last_modified":1480349196995,"details":{"who":"Users of version 7.9.20.6 of RoboForm Toolbar and earlier on Firefox 48 and above.","created":"2011-11-19T06:14:56Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=699134","name":"Roboform","why":"Older versions of the RoboForm Toolbar add-on are causing crashes in Firefox 48 and above.  The developer has released a fix, available in versions 7.9.21+."},"versionRange":[{"targetApplication":[{"minVersion":"8.0a1","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"}],"minVersion":"0.1","maxVersion":"7.9.20.6","severity":1}],"prefs":[],"id":"5f7f9e13-d3e8-ea74-8341-b83e36d67d94","schema":1480349193877},{"guid":"{87b5a11e-3b54-42d2-9102-0a7cb1f79ebf}","blockID":"i838","enabled":true,"last_modified":1480349196965,"details":{"who":"All Firefox users who have this add-on installed.","created":"2015-02-06T14:29:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1128327","name":"Cyti Web (malware)","why":"This add-on is silently installed and performs unwanted actions, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"1ba0e57c-4c0c-4eb6-26e7-c2016769c343","schema":1480349193877},{"guid":"/^({bf67a47c-ea97-4caf-a5e3-feeba5331231}|{24a0cfe1-f479-4b19-b627-a96bf1ea3a56})$/","blockID":"i542","enabled":true,"last_modified":1480349196622,"details":{"who":"All Firefox users who have this add-on installed.","created":"2014-01-28T14:10:49Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=963819","name":"MixiDJ (malware)","why":"This add-on has been repeatedly blocked before and keeps showing up with new add-on IDs, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"fc442b64-1b5d-bebb-c486-f431b154f3db","schema":1480349193877},{"guid":"/^({ebd898f8-fcf6-4694-bc3b-eabc7271eeb1}|{46008e0d-47ac-4daa-a02a-5eb69044431a}|{213c8ed6-1d78-4d8f-8729-25006aa86a76}|{fa23121f-ee7c-4bd8-8c06-123d087282c5}|{19803860-b306-423c-bbb5-f60a7d82cde5})$/","blockID":"i622","enabled":true,"last_modified":1480349196597,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using this add-on can enable it in the Add-ons Manager.","created":"2014-06-18T13:48:26Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=947482","name":"WiseConvert","why":"This add-on is known to change user settings without their consent, is distributed under multiple add-on IDs, and is also correlated with reports of tab functions being broken in Firefox, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"ffd184fa-aa8f-8a75-ff00-ce285dec5b22","schema":1480349193877},{"guid":"/^({fa95f577-07cb-4470-ac90-e843f5f83c52}|ffxtlbr@speedial\\.com)$/","blockID":"i696","enabled":true,"last_modified":1480349196565,"details":{"who":"All Firefox users who have any of these add-ons installed. Users who wish to continue using these add-ons can enable them in the Add-ons Manager.","created":"2014-08-21T13:55:41Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1031115","name":"Speedial","why":"These add-ons are silently installed and change homepage and search defaults without user consent, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>. They are also distributed under more than one add-on ID."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":["browser.startup.homepage","browser.search.defaultenginename"],"id":"130c7419-f727-a2fb-3891-627bc69a43bb","schema":1480349193877},{"guid":"[email protected]","blockID":"i442","enabled":true,"last_modified":1480349196541,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-08-13T14:00:36Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=904050","name":"Console Video (malware)","why":"This is a malicious add-on that hijacks Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"fb83e48e-a780-9d06-132c-9ecc65b43674","schema":1480349193877},{"guid":"[email protected]","blockID":"i88","enabled":true,"last_modified":1480349196509,"details":{"who":"All Firefox users who have installed version 2.4.6.4 of the Ant Video Downloader and Player add-on.","created":"2012-05-01T10:32:11Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=748269","name":"Ant Video Downloader and Player","why":"Version 2.4.6.4 of the Ant Video Downloader and Player add-on is causing a very high number of crashes in Firefox. There's an updated version 2.4.6.5 that doesn't have this problem. All users are recommended to update as soon as possible."},"versionRange":[{"targetApplication":[],"minVersion":"2.4.6.4","maxVersion":"2.4.6.4","severity":1}],"prefs":[],"id":"9eef435b-39d4-2b73-0810-44b0d3ff52ad","schema":1480349193877},{"guid":"{E90FA778-C2B7-41D0-9FA9-3FEC1CA54D66}","blockID":"i446","enabled":true,"last_modified":1480349196471,"details":{"who":"All Firefox users who have this add-on installed. The add-on can be enabled again in the Add-ons Manager.","created":"2013-09-06T15:59:29Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=788838","name":"YouTube to MP3 Converter","why":"This add-on is installed silently, in violation of our <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"83eb6337-a3b6-84e4-e76c-ee9200b80796","schema":1480349193877},{"guid":"{ad7ce998-a77b-4062-9ffb-1d0b7cb23183}","blockID":"i804","enabled":true,"last_modified":1480349196438,"details":{"who":"All Firefox users who have this add-on installed.","created":"2014-12-15T10:53:58Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1080839","name":"Astromenda Search Addon","why":"This add-on is silently installed and is considered malware, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":["browser.startup.homepage","browser.search.defaultenginename"],"id":"633f9999-c81e-bd7a-e756-de7d34feb39d","schema":1480349193877},{"guid":"{52b0f3db-f988-4788-b9dc-861d016f4487}","blockID":"i584","enabled":true,"last_modified":1480349196363,"details":{"who":"All Firefox users who have these add-ons installed. If you wish to continue using these add-ons, you can enable them in the Add-ons Manager.","created":"2014-05-22T11:07:00Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=974104","name":"Web Check (Free Driver Scout bundle)","why":"Versions of this add-on are silently installed by the Free Driver Scout installer, in violation of our <a href=\"https://developer.mozilla.org/en-US/docs/Addons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"0.1.9999999","severity":1}],"prefs":[],"id":"cba0ac44-90f9-eabb-60b0-8da2b645e067","schema":1480349193877},{"guid":"[email protected]","blockID":"i1279","enabled":true,"last_modified":1480349196331,"details":{"who":"Any user with version 1.3 or newer of this add-on installed.","created":"2016-10-27T15:52:00Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312748","name":"Aktualizacja Flash WORK addon","why":"This add-on claims to be a flash plugin and it does some work on youtube, but it also steals your facebook and adfly credentials and sends them to a remote server."},"versionRange":[{"targetApplication":[],"minVersion":"1.3","maxVersion":"*","severity":3}],"prefs":[],"id":"2dab5211-f9ec-a1bf-c617-6f94f28b5ee1","schema":1480349193877},{"guid":"{2d069a16-fca1-4e81-81ea-5d5086dcbd0c}","blockID":"i440","enabled":true,"last_modified":1480349196294,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-08-09T16:26:46Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=903647","name":"GlitterFun","why":"This add-on is installed silently and doesn't follow many other of the <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>. If you want to continue using this add-on, you can enable it in the Add-ons Manager."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"e3f77f3c-b1d6-3b29-730a-846007b9cb16","schema":1480349193877},{"guid":"[email protected]","blockID":"i501","enabled":true,"last_modified":1480349196247,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-12-04T15:34:32Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=946420","name":"Video Plugin Facebook (malware)","why":"This is a malicious Firefox extension that uses a deceptive name and hijacks users' Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"3303d201-7006-3c0d-5fd5-45503e2e690c","schema":1480349193877},{"guid":"2bbadf1f-a5af-499f-9642-9942fcdb7c76@f05a14cc-8842-4eee-be17-744677a917ed.com","blockID":"i700","enabled":true,"last_modified":1480349196212,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using this add-on can enable it in the Add-ons Manager.","created":"2014-08-21T16:15:16Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1052599","name":"PIX Image Viewer","why":"This add-on is widely considered malware and is apparently installed silently into users' systems, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"1b72889b-90e6-ea58-4fe8-d48257df7d8b","schema":1480349193877},{"guid":"/^[0-9a-f]+@[0-9a-f]+\\.info/","blockID":"i256","enabled":true,"last_modified":1480349196184,"details":{"who":"All Firefox users who have installed any of these add-ons.","created":"2013-01-22T12:16:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=806451","name":"Codec extensions (malware)","why":"The set of extensions labeled as Codec, Codec-M, Codec-C and other names are malware being distributed as genuine add-ons.\r\n\r\nIf you think an add-on you installed was incorrectly blocked and the block dialog pointed you to this page, please comment on <a href=\"https://blog.mozilla.org/addons/2013/01/22/blocklisting-malicious-codec-add-ons/\">this blog post</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"0c654540-00f2-0ad4-c9be-7ca2ace5341e","schema":1480349193877},{"guid":"[email protected]","blockID":"i600","enabled":true,"last_modified":1480349196158,"details":{"who":"All Firefox users who have these versions of the Ask Toolbar installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2014-06-12T14:16:08Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1024719","name":"Ask Toolbar (old Avira Security Toolbar bundle)","why":"Certain old versions of the Ask Toolbar are causing problems to users when trying to open new tabs. Using more recent versions of the Ask Toolbar should also fix this problem."},"versionRange":[{"targetApplication":[],"minVersion":"3.15.5","maxVersion":"3.15.5.*","severity":1}],"prefs":[],"id":"51c4ab3b-9ad3-c5c3-98c8-a220025fc5a3","schema":1480349193877},{"guid":"{729c9605-0626-4792-9584-4cbe65b243e6}","blockID":"i788","enabled":true,"last_modified":1480349196123,"details":{"who":"All Firefox users who have this add-on installed.","created":"2014-11-20T10:07:19Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1073810","name":"Browser Ext Assistance","why":"This add-on is silently installed into users' systems. It uses very unsafe practices to load its code, and leaks information of all web browsing activity. These are all violations of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"3c588238-2501-6a53-65ea-5c8ff0f3e51d","schema":1480349193877},{"guid":"[email protected]","blockID":"i1213","enabled":true,"last_modified":1480349196088,"details":{"who":"All users who have this add-on installed.","created":"2016-05-09T17:28:18Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1251911","name":"YouTube Unblocker 2.0","why":"This add-on is a copy of YouTube Unblocker, which was <a href=\"https://addons.mozilla.org/firefox/blocked/i1129\">originally blocked</a> due to malicious activity."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"de305335-e9f3-f410-cf5c-f88b7ad4b088","schema":1480349193877},{"guid":"[email protected]","blockID":"i630","enabled":true,"last_modified":1480349196032,"details":{"who":"All Firefox users who have any of these add-ons installed. Users who wish to continue using them can enable them in the Add-ons Manager.","created":"2014-06-26T15:49:27Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=866943","name":"Iminent Minibar","why":"These add-ons have been silently installed repeatedly, and change settings without user consent, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":["browser.startup.homepage","browser.search.defaultenginename"],"id":"d894ea79-8215-7a0c-b0e9-be328c3afceb","schema":1480349193877},{"guid":"jid1-uabu5A9hduqzCw@jetpack","blockID":"i1016","enabled":true,"last_modified":1480349196001,"details":{"who":"All users who have this add-on installed.","created":"2015-09-24T09:49:42Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1208051","name":"SpeedFox (malware)","why":"This add-on is injecting unwanted and unexpected advertisements into all web pages, and masking this behavior as ad-blocking in its code."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"31397419-3dfa-9db3-f1aa-e812d4220669","schema":1480349193877},{"guid":"/^firefox@(jumpflip|webconnect|browsesmart|mybuzzsearch|outobox|greygray|lemurleap|divapton|secretsauce|batbrowse|whilokii|linkswift|qualitink|browsefox|kozaka|diamondata|glindorus|saltarsmart|bizzybolt|websparkle)\\.(com?|net|org|info|biz)$/","blockID":"i548","enabled":true,"last_modified":1480349195955,"details":{"who":"All Firefox users who have one or more of these add-ons installed. If you wish to continue using any of these add-ons, they can be enabled in the Add-ons Manager.","created":"2014-01-30T15:06:01Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=937405","name":"Yontoo add-ons","why":"A large amount of add-ons developed by Yontoo are known to be silently installed and otherwise violate the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"bfaf3510-397e-48e6-cc4f-74202aaaed54","schema":1480349193877},{"guid":"[email protected]","blockID":"i23","enabled":true,"last_modified":1480349195915,"details":{"who":"Users of Bandoo version 5.0 for Firefox 3.6 and later.","created":"2011-03-01T23:30:23Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=629634","name":"Bandoo","why":"This add-on causes a high volume of Firefox crashes."},"versionRange":[{"targetApplication":[{"minVersion":"3.7a1pre","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"}],"minVersion":"5.0","maxVersion":"5.0","severity":1}],"prefs":[],"id":"bd487cf4-3f6a-f956-a6e9-842ac8deeac5","schema":1480349193877},{"guid":"[email protected]","blockID":"i372","enabled":true,"last_modified":1480349195887,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-06-18T13:23:40Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=875752","name":"Flash Player 11 (malware)","why":"This add-on is malware pretending to be the Flash Player plugin."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"dc71fcf5-fae4-5a5f-6455-ca7bbe4202db","schema":1480349193877},{"guid":"/^(7tG@zEb\\.net|ru@gfK0J\\.edu)$/","blockID":"i854","enabled":true,"last_modified":1480349195851,"details":{"who":"All Firefox users who have this add-on installed.","created":"2015-02-09T15:41:36Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=952255","name":"youtubeadblocker (malware)","why":"This add-on is silently installed and performs unwanted actions, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"cfe42207-67a9-9b88-f80c-994e6bdd0c55","schema":1480349193877},{"guid":"{a7aae4f0-bc2e-a0dd-fb8d-68ce32c9261f}","blockID":"i378","enabled":true,"last_modified":1480349195823,"details":{"who":"All Firefox users who have installed this add-on.","created":"2013-06-18T15:58:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=865090","name":"Myanmar Extension for Facebook (malware)","why":"This extension is malware that hijacks Facebook accounts for malicious purposes."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"30ecd9b9-4023-d9ef-812d-f1a75bb189b0","schema":1480349193877},{"guid":"a88a77ahjjfjakckmmabsy278djasi@jetpack","blockID":"i1034","enabled":true,"last_modified":1480349195798,"details":{"who":"All users who have this add-on installed.","created":"2015-10-05T16:28:48Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1211171","name":"Fast Unlock (malware)","why":"This is a malicious add-on that takes over Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"f801f112-3e8f-770f-10db-384349a36026","schema":1480349193877},{"guid":"[email protected]","blockID":"i228","enabled":true,"last_modified":1480349195769,"details":{"who":"All Firefox users who have this add-on installed.","created":"2012-11-29T16:31:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=810016","name":"Savings Sidekick","why":"This add-on is silently side-installed by other software, and it overrides user preferences and inserts advertisements in web content."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"a37f76ac-7b77-b5a3-bac8-addaacf34bae","schema":1480349193877},{"guid":"/^(saamazon@mybrowserbar\\.com)|(saebay@mybrowserbar\\.com)$/","blockID":"i672","enabled":true,"last_modified":1480349195347,"details":{"who":"All Firefox users who have these add-ons installed. Users wishing to continue using these add-ons can enable them in the Add-ons Manager.","created":"2014-07-22T15:13:57Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1011337","name":"Spigot Shopping Assistant","why":"These add-ons are being silently installed, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"e072a461-ee5a-c83d-8d4e-5686eb585a15","schema":1480349193877},{"guid":"{b99c8534-7800-48fa-bd71-519a46cdc7e1}","blockID":"i596","enabled":true,"last_modified":1480349195319,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2014-06-12T13:19:59Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1011325","name":"BrowseMark","why":"This add-on is silently installed, in violation with our <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"f411bb0f-7c82-9061-4a80-cabc8ff45beb","schema":1480349193877},{"guid":"/^({94d62e35-4b43-494c-bf52-ba5935df36ef}|firefox@advanceelite\\.com|{bb7b7a60-f574-47c2-8a0b-4c56f2da9802})$/","blockID":"i856","enabled":true,"last_modified":1480349195286,"details":{"who":"All Firefox users who have this add-on installed.","created":"2015-02-09T15:51:11Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1130323","name":"AdvanceElite (malware)","why":"This add-on is silently installed and performs unwanted actions, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"e3d52650-d3e2-4cef-71f7-e6188f56fe4d","schema":1480349193877},{"guid":"{458fb825-2370-4973-bf66-9d7142141847}","blockID":"i1024","enabled":true,"last_modified":1480349195258,"details":{"who":"All users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2015-09-29T09:25:27Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1209588","name":"Web Shield","why":"This add-on hides itself in the Add-ons Manager, interrupts the Firefox update process, and reportedly causes other problems to users, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":["app.update.auto","app.update.enabled","app.update.interval","app.update.url"],"id":"32c5baa7-d547-eaab-302d-b873c83bfe2d","schema":1480349193877},{"guid":"{f2456568-e603-43db-8838-ffa7c4a685c7}","blockID":"i778","enabled":true,"last_modified":1480349195215,"details":{"who":"All Firefox users who have this add-on installed.","created":"2014-11-07T13:53:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1073810","name":"Sup-SW","why":"This add-on is silently installed into users' systems. It uses very unsafe practices to load its code, and leaks information of all web browsing activity. These are all violations of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"93568fa2-0cb7-4e1d-e893-d7261e81547c","schema":1480349193877},{"guid":"{77BEC163-D389-42c1-91A4-C758846296A5}","blockID":"i566","enabled":true,"last_modified":1480349195185,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-on Manager.","created":"2014-03-05T13:20:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=964594","name":"V-Bates","why":"This add-on is silently installed into Firefox, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"080edbac-25d6-e608-abdd-feb1ce7a9a77","schema":1480349193877},{"guid":"[email protected]","blockID":"i1077","enabled":true,"last_modified":1480349195157,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using the add-on can enable it in the Add-ons Manager.","created":"2016-01-14T14:32:53Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1231010","name":"YouTube Video Downloader (from AddonCrop)","why":"This add-on injects remote scripts and injects unwanted content into web pages."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"36b2e1e0-5fda-bde3-db55-dfcbe24dfd04","schema":1480349193877},{"guid":"/^ext@WebexpEnhancedV1alpha[0-9]+\\.net$/","blockID":"i535","enabled":true,"last_modified":1480349195123,"details":{"who":"All Firefox users who have this add-on installed. If you wish to continue using this add-on, it can be enabled in the Add-ons Manager.","created":"2014-01-09T11:22:19Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=952717","name":"Webexp Enhanced","why":"This add-on is generally unwanted by users and uses multiple random IDs in violation of the <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"c7d6a30d-f3ee-40fb-5256-138dd4593a61","schema":1480349193877},{"guid":"jid1-XLjasWL55iEE1Q@jetpack","blockID":"i578","enabled":true,"last_modified":1480349195058,"details":{"who":"All Firefox users who have this add-on installed.","created":"2014-04-28T16:25:03Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1002037","name":"Flash Player (malware)","why":"This is a malicious add-on that presents itself as \"Flash Player\" but is really injecting unwanted content into Facebook pages."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"1e75b2f0-02fc-77a4-ad2f-52a4caff1a71","schema":1480349193877},{"guid":"{a3a5c777-f583-4fef-9380-ab4add1bc2a8}","blockID":"i142","enabled":true,"last_modified":1480349195007,"details":{"who":"Todos los usuarios de Firefox que instalaron la versi\u00f3n 4.2 del complemento Cuevana Stream.\r\n\r\nAll Firefox users who have installed version 4.2 of the Cuevana Stream add-on.","created":"2012-09-18T13:37:47Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=792132","name":"Cuevana Stream (malicious version)","why":"<strong>Espa\u00f1ol</strong>\r\nUna versi\u00f3n maliciosa del complemento Cuevana Stream (4.2) fue colocada en el sitio Cuevana y distribuida a muchos usuarios del sitio. Esta versi\u00f3n recopila informaci\u00f3n de formularios web y los env\u00eda a una direcci\u00f3n remota con fines maliciosos. Se le recomienda a todos los usuarios que instalaron esta versi\u00f3n que cambien sus contrase\u00f1as inmediatamente, y que se actualicen a la nueva versi\u00f3n segura, que es la 4.3.\r\n\r\n<strong>English</strong>\r\nA malicious version of the Cuevana Stream add-on (4.2) was uploaded to the Cuevana website and distributed to many of its users. This version takes form data and sends it to a remote location with malicious intent. It is recommended that all users who installed this version to update their passwords immediately, and update to the new safe version, version 4.3.\r\n\r\n"},"versionRange":[{"targetApplication":[],"minVersion":"4.2","maxVersion":"4.2","severity":3}],"prefs":[],"id":"91e551b9-7e94-60e2-f1bd-52f25844ab16","schema":1480349193877},{"guid":"{34712C68-7391-4c47-94F3-8F88D49AD632}","blockID":"i922","enabled":true,"last_modified":1480349194976,"details":{"who":"All Firefox users who have this add-on installed in Firefox 39 and above.\r\n","created":"2015-06-09T15:27:31Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1173154","name":"RealPlayer Browser Record Plugin","why":"Certain versions of this extension are causing startup crashes in Firefox 39 and above.\r\n"},"versionRange":[{"targetApplication":[{"minVersion":"39.0a1","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"}],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"dd350efb-34ac-2bb5-5afd-eed722dbb916","schema":1480349193877},{"guid":"[email protected]","blockID":"i846","enabled":true,"last_modified":1480349194949,"details":{"who":"All Firefox users who have this add-on installed.","created":"2015-02-06T15:03:39Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1128320","name":"Ge-Force","why":"This add-on is silently installed and attempts to change user settings like the home page and default search, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":["browser.startup.homepage","browser.search.defaultenginename"],"id":"c33e950c-c977-ed89-c86a-3be8c4be1967","schema":1480349193877},{"guid":"{977f3b97-5461-4346-92c8-a14c749b77c9}","blockID":"i69","enabled":true,"last_modified":1480349194919,"details":{"who":"All Firefox users who have this add-on installed.","created":"2012-02-22T16:41:23Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=729356","name":"Zuperface+","why":"This add-on adds apps to users' accounts, with full access permissions, and sends spam posts using these apps, all without any consent from users."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"f105bdc7-7ebd-587c-6344-1533249f50b3","schema":1480349193877},{"guid":"[email protected]","blockID":"i582","enabled":true,"last_modified":1480349194878,"details":{"who":"All Firefox users who have this add-on installed. If you wish to continue using this add-on, you can enabled it in the Add-ons Manager.","created":"2014-04-30T16:10:03Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1004231","name":"Website Discovery Pro","why":"This add-on is silently installed by the CNET installer for MP3 Rocket and probably other software packages. This is in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"34eab242-6fbc-a459-a89e-0dc1a0b8355d","schema":1480349193877},{"guid":"jid1-bKSXgRwy1UQeRA@jetpack","blockID":"i680","enabled":true,"last_modified":1480349194851,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using this add-on can enable it in the Add-ons Manager.","created":"2014-08-01T16:34:01Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=979856","name":"Trusted Shopper","why":"This add-on is silently installed into user's systems, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"f701b790-b266-c69d-0fba-f2d189cb0f34","schema":1480349193877},{"guid":"[email protected]","blockID":"i848","enabled":true,"last_modified":1480349194799,"details":{"who":"All Firefox users who have this add-on installed.","created":"2015-02-09T15:21:17Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1128266","name":"boomdeal","why":"This add-on is silently installed and performs unwanted actions, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"f8d6d4e1-b9e6-07f5-2b49-192106a45d82","schema":1480349193877},{"guid":"[email protected]","blockID":"i504","enabled":true,"last_modified":1480349194724,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-12-06T12:07:58Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=947341","name":"Facebook Haber (malware)","why":"This is a malicious extension that hijacks users' Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"bfaf8298-dd69-165c-e1ed-ad55584abd18","schema":1480349193877},{"guid":"[email protected]","blockID":"i136","enabled":true,"last_modified":1480349194647,"details":{"who":"All Firefox users who have this add-on installed.","created":"2012-09-10T16:09:06Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=790100","name":"Adobe Flash (malware)","why":"This add-on is malware posing as a legitimate Adobe product."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"47ac744e-3176-5cb6-1d02-b460e0c7ada0","schema":1480349193877},{"guid":"{515b2424-5911-40bd-8a2c-bdb20286d8f5}","blockID":"i491","enabled":true,"last_modified":1480349194580,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2013-11-29T14:52:24Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=940753","name":"Connect DLC","why":"The installer that includes this add-on violates the <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> by making changes that can't be easily reverted."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"6d658443-b34a-67ad-934e-cbf7cd407460","schema":1480349193877},{"guid":"/^({3f3cddf8-f74d-430c-bd19-d2c9147aed3d}|{515b2424-5911-40bd-8a2c-bdb20286d8f5}|{17464f93-137e-4646-a0c6-0dc13faf0113}|{d1b5aad5-d1ae-4b20-88b1-feeaeb4c1ebc}|{aad50c91-b136-49d9-8b30-0e8d3ead63d0})$/","blockID":"i516","enabled":true,"last_modified":1480349194521,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2013-12-20T12:38:20Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=947478","name":"Connect DLC","why":"The installer that includes this add-on violates the <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> by making changes that can't be easily reverted and being distributed under multiple add-on IDs."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"96f8e157-8b8b-8e2e-76cd-6850599b4370","schema":1480349193877},{"guid":"wxtui502n2xce9j@no14","blockID":"i1012","enabled":true,"last_modified":1480349194463,"details":{"who":"All users who have this add-on installed.","created":"2015-09-21T13:04:09Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1206157","name":"Video fix (malware)","why":"This is a malicious add-on that takes over Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"246798ac-25fa-f4a4-258c-a71f9f6ae091","schema":1480349193877},{"guid":"[email protected]","blockID":"i168","enabled":true,"last_modified":1480349194428,"details":{"who":"All Firefox users who have this add-on installed.","created":"2012-10-30T12:07:41Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=807052","name":"Zombie Browser Pack","why":"This is an exploit proof-of-concept created for a conference presentation, which will probably be copied and modified for malicious purposes. \r\n"},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"d7c69812-801c-8d8e-12cb-c5171bdc48a1","schema":1480349193877},{"guid":"/^(ff\\-)?dodate(kKKK|XkKKK|k|kk|kkx|kR)@(firefox|flash(1)?)\\.pl|dode(ee)?k@firefoxnet\\.pl|(addon|1)@upsolutions\\.pl$/","blockID":"i1278","enabled":true,"last_modified":1480349194386,"details":{"who":"Any user with a version of this add-on installed.","created":"2016-10-27T10:52:53Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312748","name":"Aktualizacja dodatku Flash Add-on","why":"This add-on claims to be a flash plugin and it does some work on youtube, but it also steals your facebook and adfly credentials and sends them to a remote server."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"389aec65-a15d-8276-c7a8-691ac283c9f1","schema":1480349193877},{"guid":"[email protected]","blockID":"i1223","enabled":true,"last_modified":1480349194331,"details":{"who":"All users of this add-on. If you wish to continue using it, you can enable it in the Add-ons Manager.","created":"2016-05-30T17:07:04Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1275245","name":"Trend Micro BEP 9.2 to 9.2.0.1023","why":"Add-on  is causing a high-frequency crash in Firefox."},"versionRange":[{"targetApplication":[],"minVersion":"9.2","maxVersion":"9.2.0.1023","severity":1}],"prefs":[],"id":"46f75b67-2675-bdde-be93-7ea03475d405","schema":1480349193877},{"guid":"{4889ddce-7a83-45e6-afc9-1e4f1149fff4}","blockID":"i840","enabled":true,"last_modified":1480349193867,"details":{"who":"All Firefox users who have this add-on installed.","created":"2015-02-06T14:30:06Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1128327","name":"Cyti Web (malware)","why":"This add-on is silently installed and performs unwanted actions, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"be600f35-0633-29f3-c571-819e19d85db9","schema":1480343836083},{"guid":"{55dce8ba-9dec-4013-937e-adbf9317d990","blockID":"i690","enabled":true,"last_modified":1480349193833,"details":{"who":"All Firefox users. Users who wish to continue using this add-on can enable it in the Add-ons Manager.","created":"2014-08-12T16:23:46Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1048647","name":"Deal Keeper","why":"This add-on is being silently installed in users' systems, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"512b0d40-a10a-5ddc-963b-b9c487eb1422","schema":1480343836083},{"guid":"/^new@kuot\\.pro|{13ec6687-0b15-4f01-a5a0-7a891c18e4ee}|rebeccahoppkins(ty(tr)?)?@gmail\\.com|{501815af-725e-45be-b0f2-8f36f5617afc}|{9bdb5f1f-b1e1-4a75-be31-bdcaace20a99}|{e9d93e1d-792f-4f95-b738-7adb0e853b7b}|dojadewaskurwa@gmail\\.com$/","blockID":"i1414","enabled":true,"last_modified":1480349193798,"details":{"who":"All users who have this add-on installed.","created":"2016-10-28T18:06:03Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312748","name":"Aktualizacja dodatku Flash (malware)","why":"This add-on claims to be a flash plugin and it does some work on youtube, but it also steals your facebook and adfly credentials and sends them to a remote server."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"5cebc983-bc88-d5f8-6807-bd1cbfcd82fd","schema":1480343836083},{"guid":"/^pink@.*\\.info$/","blockID":"i238","enabled":true,"last_modified":1480349193764,"details":{"who":"All Firefox users (Firefox 19 and above) who have any of these add-ons installed.","created":"2012-12-07T13:46:20Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=806543","name":"Pink add-ons (malware)","why":"This is a set of malicious add-ons that affect many users and are installed without their consent."},"versionRange":[{"targetApplication":[{"minVersion":"18.0","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"}],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"0d964264-8bd6-b78d-3c6c-92046c7dc8d0","schema":1480343836083},{"guid":"{58d2a791-6199-482f-a9aa-9b725ec61362}","blockID":"i746","enabled":true,"last_modified":1480349193730,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2014-10-17T16:01:53Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=963787","name":"Start Page","why":"This add-on is silently installed into users' systems, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"8ebbc7d0-635c-b74a-de9f-16eb5837b36a","schema":1480343836083},{"guid":"{94cd2cc3-083f-49ba-a218-4cda4b4829fd}","blockID":"i590","enabled":true,"last_modified":1480349193649,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using this add-on can enable it in the Add-ons Manager.","created":"2014-06-03T16:12:50Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1013678","name":"Value Apps","why":"This add-on is silently installed into users' profiles, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"556b8d4d-d6c2-199d-9f33-8eccca07e8e7","schema":1480343836083},{"guid":"[email protected]","blockID":"i818","enabled":true,"last_modified":1480349193622,"details":{"who":"All Firefox users who have this add-on installed.","created":"2015-01-12T09:29:19Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1119971","name":"Astro Play (malware)","why":"This is a malicious extension that hijacks Facebook accounts."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"440e9923-027a-6089-e036-2f78937dc193","schema":1480343836083},{"guid":"[email protected]","blockID":"i1228","enabled":true,"last_modified":1480349193595,"details":{"who":"All users who have this add-on installed.","created":"2016-06-01T15:17:22Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1251911","name":"YouTube Unblocker 3.0","why":"This add-on is a copy of YouTube Unblocker, which was <a href=\"https://addons.mozilla.org/firefox/blocked/i1129\">originally blocked</a> due to malicious activity."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"2d83e640-ef9d-f260-f5a3-a1a5c8390bfc","schema":1480343836083},{"guid":"[email protected]","blockID":"i816","enabled":true,"last_modified":1480349193537,"details":{"who":"All Firefox users who have this add-on installed.","created":"2015-01-09T12:52:32Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1119659","name":"Full Screen (malware)","why":"This add-on is silently installed into users' systems without their consent and performs unwanted operations."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"b64d7cef-8b6c-2575-16bc-732fca7db377","schema":1480343836083},{"guid":"{c95a4e8e-816d-4655-8c79-d736da1adb6d}","blockID":"i433","enabled":true,"last_modified":1480349193510,"details":{"who":"All Firefox users who have this add-on installed.","created":"2013-08-09T11:25:49Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=844945","name":"Hotspot Shield","why":"This add-on bypasses the external install opt in screen in Firefox, violating the <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>. Users who wish to continue using this add-on can enable it in the Add-ons Manager."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"b3168278-a8ae-4882-7f26-355bc362bed0","schema":1480343836083},{"guid":"{9802047e-5a84-4da3-b103-c55995d147d1}","blockID":"i722","enabled":true,"last_modified":1480349193482,"details":{"who":"All Firefox users who have this add-on installed.","created":"2014-10-07T12:58:14Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1073810","name":"Web Finder Pro","why":"This add-on is silently installed into users' systems. It uses very unsafe practices to load its code, and leaks information of all web browsing activity. These are all violations of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"50097c29-26b1-bf45-ffe1-83da217eb127","schema":1480343836083},{"guid":"/^({bf9194c2-b86d-4ebc-9b53-1c08b6ff779e}|{61a83e16-7198-49c6-8874-3e4e8faeb4f3}|{f0af464e-5167-45cf-9cf0-66b396d1918c}|{5d9968c3-101c-4944-ba71-72d77393322d}|{01e86e69-a2f8-48a0-b068-83869bdba3d0})$/","blockID":"i515","enabled":true,"last_modified":1480349193449,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","created":"2013-12-20T12:26:49Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=947473","name":"VisualBee Toolbar","why":"The installer that includes this add-on violates the <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> by using multiple add-on IDs and making unwanted settings changes."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"029fa6f9-2351-40b7-5443-9a66e057f199","schema":1480343836083},{"guid":"/^({d50bfa5f-291d-48a8-909c-5f1a77b31948}|{d54bc985-6e7b-46cd-ad72-a4a266ad879e}|{d89e5de3-5543-4363-b320-a98cf150f86a}|{f3465017-6f51-4980-84a5-7bee2f961eba}|{fae25f38-ff55-46ea-888f-03b49aaf8812})$/","blockID":"i1137","enabled":true,"last_modified":1480349193419,"details":{"who":"All users who have this add-on installed.","created":"2016-03-04T17:56:42Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1251940","name":"Watcher (malware)","why":"This is a malicious add-on that hides itself from view and disables various security features in Firefox."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"252e18d0-85bc-7bb3-6197-5f126424c9b3","schema":1480343836083},{"guid":"[email protected]","blockID":"i218","enabled":true,"last_modified":1480349193385,"details":{"who":"All Firefox users who have installed this add-on.","created":"2012-11-29T16:07:00Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=816762","name":"Claro Toolbar","why":"The Claro Toolbar is side-installed with other software, unexpectedly changing users' settings and then making it impossible for these settings to be reverted by users."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"e017a3b2-9b37-b8a0-21b0-bc412ae8a7f4","schema":1480343836083},{"guid":"/^(.*@(unblocker\\.yt|sparpilot\\.com))|(axtara@axtara\\.com)$/","blockID":"i1229","enabled":true,"last_modified":1480349193344,"details":{"who":"All users who have this add-on installed.","created":"2016-06-03T15:28:39Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1251911","name":"YouTube Unblocker (various)","why":"These add-ons are copies of YouTube Unblocker, which was <a href=\"https://addons.mozilla.org/firefox/blocked/i1129\">originally blocked</a> due to malicious activity."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"c677cc5d-5b1e-8aa2-5cea-5a8dddce2ecf","schema":1480343836083},{"guid":"/^(j003-lqgrmgpcekslhg|SupraSavings|j003-dkqonnnthqjnkq|j003-kaggrpmirxjpzh)@jetpack$/","blockID":"i692","enabled":true,"last_modified":1480349193295,"details":{"who":"All Firefox users who have this add-on installed. Users who wish to continue using this add-on can enable it in the Add-ons Manager.","created":"2014-08-12T16:27:06Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1048656","name":"SupraSavings","why":"This add-on is being silently installed in users' systems, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"b0d30256-4581-1489-c241-d2e85b6c38f4","schema":1480343836083},{"guid":"[email protected]","blockID":"i258","enabled":true,"last_modified":1480349193254,"details":{"who":"All Firefox users who have this add-on installed. This only applies to version 1.0 of Snap.do. Version 1.1 fixed all the issues for which this block was created.","created":"2013-01-28T13:52:26Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=817786","name":"Snap.do","why":"This extension violates a number of our <a href=\"https://developer.mozilla.org/en-US/docs/Addons/Add-on_guidelines\">Add-on Guidelines</a>, particularly on installation and settings handling. It also causes some stability problems in Firefox due to the way the toolbar is handled.\r\n\r\nUsers who wish to keep the add-on enabled can enable it again in the Add-ons Manager."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"1.0","severity":1}],"prefs":[],"id":"f1ede5b8-7757-5ec5-d8ed-1a01889154aa","schema":1480343836083},{"guid":"/^((support2_en@adobe14\\.com)|(XN4Xgjw7n4@yUWgc\\.com)|(C7yFVpIP@WeolS3acxgS\\.com)|(Kbeu4h0z@yNb7QAz7jrYKiiTQ3\\.com)|(aWQzX@a6z4gWdPu8FF\\.com)|(CBSoqAJLYpCbjTP90@JoV0VMywCjsm75Y0toAd\\.com)|(zZ2jWZ1H22Jb5NdELHS@o0jQVWZkY1gx1\\.com))$/","blockID":"i326","enabled":true,"last_modified":1480349193166,"details":{"who":"All users who have this add-on installed.","created":"2013-03-22T14:49:08Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=841791","name":"Flash Player (malware)","why":"This extension is malware, installed pretending to be the Flash Player plugin."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"3142020b-8af9-1bac-60c5-ce5ad0ff3d42","schema":1480343836083},{"guid":"[email protected]","blockID":"i576","enabled":true,"last_modified":1480349193114,"details":{"who":"All Firefox users who have this add-on installed.","created":"2014-04-22T14:34:42Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=997986","name":"Facebook Service Pack (malware)","why":"This add-on is malware that hijacks Facebook user accounts and sends spam on the user's behalf."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"d85798d3-9b87-5dd9-ace2-64914b93df77","schema":1480343836083},{"guid":"[email protected]","blockID":"i1211","enabled":true,"last_modified":1480349193088,"details":{"who":"All users of this add-on. If you wish to continue using it, you can enable it in the Add-ons Manager.","created":"2016-05-04T16:26:32Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1270175","name":"YouTube to MP3 Button","why":"This add-on reports every visited URL to a third party without disclosing it to the user."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":1}],"prefs":[],"id":"a14d355f-719f-3b97-506c-083cc97cebaa","schema":1480343836083},{"guid":"{0F827075-B026-42F3-885D-98981EE7B1AE}","blockID":"i334","enabled":true,"last_modified":1480349192987,"details":{"who":"All Firefox users who have this extension installed.","created":"2013-04-16T13:25:01Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=862272","name":"Browser Protect / bProtector (malware)","why":"This extension is malicious and is installed under false pretenses, causing problems for many Firefox users. Note that this is not the same <a href=\"https://addons.mozilla.org/firefox/addon/browserprotect/\">BrowserProtect extension</a> that is listed on our add-ons site. That one is safe to use."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":3}],"prefs":[],"id":"aad4545f-8f9d-dd53-2aa8-e8945cad6185","schema":1480343836083}]}PK
!<ak�p��%defaults/blocklists/certificates.json{"data":[{"serialNumber":"TA5iEg==","enabled":true,"last_modified":1485907697001,"details":{"who":".","created":"2017-01-31T23:06:38Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1334069","name":"Revoked intermediates","why":"."},"issuerName":"MIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp","id":"16319df7-453c-d980-4624-d73d9cf43143","schema":1485795317518},{"serialNumber":"BydSYg==","enabled":true,"last_modified":1485907696954,"details":{"who":".","created":"2017-01-31T23:06:37Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1334069","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=","id":"ec171905-2990-30e0-74bb-9fc327c1c4bd","schema":1485795317518},{"serialNumber":"Iqpyf/YoGgvHc8HiDAxAI8o=","enabled":true,"last_modified":1485907696908,"details":{"who":".","created":"2017-01-31T23:06:38Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1334069","name":"Revoked intermediates","why":"."},"issuerName":"MFwxCzAJBgNVBAYTAkJFMRUwEwYDVQQLEwxUcnVzdGVkIFJvb3QxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExGzAZBgNVBAMTElRydXN0ZWQgUm9vdCBDQSBHMg==","id":"6fa017d2-461c-8de0-f8a1-ab0531097d8e","schema":1485795317518},{"serialNumber":"Hwexgn/ZCJicZPcsIyI8zxQ=","enabled":true,"last_modified":1485907696863,"details":{"who":".","created":"2017-01-31T23:06:38Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1334069","name":"Revoked intermediates","why":"."},"issuerName":"MFAxJDAiBgNVBAsTG0dsb2JhbFNpZ24gRUNDIFJvb3QgQ0EgLSBSNDETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbg==","id":"4df86909-6b1c-f7cd-c71b-bb4b895d441f","schema":1485795317518},{"serialNumber":"RnQ3dg5KdDZs0nyFZk4=","enabled":true,"last_modified":1485907696819,"details":{"who":".","created":"2017-01-31T23:06:38Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1334069","name":"Revoked intermediates","why":"."},"issuerName":"MFAxJDAiBgNVBAsTG0dsb2JhbFNpZ24gRUNDIFJvb3QgQ0EgLSBSNDETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbg==","id":"0971a89d-b11f-97c9-2ac2-f706757b75fb","schema":1485795317518},{"serialNumber":"RnQ3dYovwvB0D5q2YGY=","enabled":true,"last_modified":1485907696773,"details":{"who":".","created":"2017-01-31T23:06:38Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1334069","name":"Revoked intermediates","why":"."},"issuerName":"MFAxJDAiBgNVBAsTG0dsb2JhbFNpZ24gRUNDIFJvb3QgQ0EgLSBSNDETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbg==","id":"d739cdcd-0078-92a9-2c46-6a5231f3d888","schema":1485795317518},{"serialNumber":"ALxyZmb/WL/wAuUiPK5oK/g=","enabled":true,"last_modified":1484704581273,"details":{"who":".","created":"2017-01-18T01:12:58Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1329981","name":"Revoked intermediates","why":"."},"issuerName":"MD4xCzAJBgNVBAYTAlBMMRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBDQQ==","id":"d6bedca3-c2b7-0402-337e-7788b9c97b85","schema":1484704580928},{"serialNumber":"AKrMYlJmUUin8FOM/0TJrmk=","enabled":true,"last_modified":1484704580920,"details":{"who":".","created":"2017-01-18T01:12:58Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1329981","name":"Revoked intermediates","why":"."},"issuerName":"MIGFMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01PRE8gQ0EgTGltaXRlZDErMCkGA1UEAxMiQ09NT0RPIFJTQSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==","id":"42e2ce60-f40f-97a6-dd47-3ea2e2dd72d8","schema":1483471396473},{"serialNumber":"e7wSpVxmgAS5/ioLi2iBIA==","enabled":true,"last_modified":1484704580894,"details":{"who":".","created":"2017-01-18T01:12:58Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1329981","name":"Revoked intermediates","why":"."},"issuerName":"MD4xCzAJBgNVBAYTAlBMMRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBDQQ==","id":"59b02ba9-97ec-924b-d797-2c61c2be0d87","schema":1483471396473},{"serialNumber":"Bydrxg==","enabled":true,"last_modified":1484704580868,"details":{"who":".","created":"2017-01-18T01:12:58Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1329981","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=","id":"224d1360-5aed-b81f-f24c-3d34e2ca3ec4","schema":1483471396473},{"serialNumber":"Byc85g==","enabled":true,"last_modified":1484704580840,"details":{"who":".","created":"2017-01-18T01:12:58Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1329981","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=","id":"25a0eefb-aa44-23df-4dda-bb166836d4c1","schema":1483471396473},{"serialNumber":"CdYL9vSQCEKzBwjO10ud2w==","enabled":true,"last_modified":1484704580815,"details":{"who":".","created":"2017-01-18T01:12:57Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1329981","name":"Revoked intermediates","why":"."},"issuerName":"MDwxGzAZBgNVBAMTEkNvbVNpZ24gU2VjdXJlZCBDQTEQMA4GA1UEChMHQ29tU2lnbjELMAkGA1UEBhMCSUw=","id":"479ac2a4-7b6a-8db3-c5a2-be10eb5dec57","schema":1483471396473},{"serialNumber":"fbsHfUkagQtznc3rtY1uDg==","enabled":true,"last_modified":1484704580787,"details":{"who":".","created":"2017-01-18T01:12:57Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1329981","name":"Revoked intermediates","why":"."},"issuerName":"MDwxGzAZBgNVBAMTEkNvbVNpZ24gU2VjdXJlZCBDQTEQMA4GA1UEChMHQ29tU2lnbjELMAkGA1UEBhMCSUw=","id":"b1b1a5db-3c68-21be-8264-7146b0ee9e6b","schema":1483471396473},{"serialNumber":"AJiU+bpWh2Uc4xFRf8GM9yA=","enabled":true,"last_modified":1484704580760,"details":{"who":".","created":"2017-01-18T01:12:58Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1329981","name":"Revoked intermediates","why":"."},"issuerName":"MD8xJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjEXMBUGA1UEAxMORFNUIFJvb3QgQ0EgWDM=","id":"60daf8a1-a929-0177-e7ba-76fff95fd20b","schema":1483471396473},{"serialNumber":"Hnms0W0OxHSYE2F0XE97sw==","enabled":true,"last_modified":1484704580734,"details":{"who":".","created":"2017-01-18T01:12:57Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1329981","name":"Revoked intermediates","why":"."},"issuerName":"MDwxGzAZBgNVBAMTEkNvbVNpZ24gU2VjdXJlZCBDQTEQMA4GA1UEChMHQ29tU2lnbjELMAkGA1UEBhMCSUw=","id":"55b72394-f4c1-3001-cf84-10f2068f2768","schema":1483471396473},{"serialNumber":"Bye2Cg==","enabled":true,"last_modified":1484704580708,"details":{"who":".","created":"2017-01-18T01:12:58Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1329981","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=","id":"e2277fc3-1aac-7c20-0cb7-f4fd6c79eedb","schema":1483471396473},{"serialNumber":"CMNfzETd7XxesS9FOUj9Mg==","enabled":true,"last_modified":1484704580680,"details":{"who":".","created":"2017-01-18T01:12:57Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1329981","name":"Revoked intermediates","why":"."},"issuerName":"MIGVMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTEdMBsGA1UEAxMUVVROLVVTRVJGaXJzdC1PYmplY3Q=","id":"49251465-af0d-3e93-cb1a-9d0b2ac356b2","schema":1483471396473},{"serialNumber":"XJ8pGvGNM9RIcLUG9YQjLQ==","enabled":true,"last_modified":1484704580653,"details":{"who":".","created":"2017-01-18T01:12:57Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1329981","name":"Revoked intermediates","why":"."},"issuerName":"MDwxGzAZBgNVBAMTEkNvbVNpZ24gU2VjdXJlZCBDQTEQMA4GA1UEChMHQ29tU2lnbjELMAkGA1UEBhMCSUw=","id":"c83b4498-ea23-7723-1c2c-b673115792d8","schema":1483471396473},{"serialNumber":"e9JTGBe45yw=","enabled":true,"last_modified":1484704580623,"details":{"who":".","created":"2017-01-18T01:12:58Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1329981","name":"Revoked intermediates","why":"."},"issuerName":"MGoxCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xOzA5BgNVBAMMMlN0YWF0IGRlciBOZWRlcmxhbmRlbiBPcmdhbmlzYXRpZSBTZXJ2aWNlcyBDQSAtIEcz","id":"20d69a85-62b2-72c7-1107-110b43d2aeb2","schema":1483471396473},{"serialNumber":"YR0zGQAAAAAAAw==","enabled":true,"last_modified":1484704580597,"details":{"who":".","created":"2017-01-18T01:12:58Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1329981","name":"Revoked intermediates","why":"."},"issuerName":"MGcxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpGcmF1bmhvZmVyMSEwHwYDVQQLExhGcmF1bmhvZmVyIENvcnBvcmF0ZSBQS0kxIDAeBgNVBAMTF0ZyYXVuaG9mZXIgUm9vdCBDQSAyMDA3","id":"4204c7d6-1838-5925-2461-1bc0e03515d4","schema":1483471396473},{"serialNumber":"AQw=","enabled":true,"last_modified":1484704580571,"details":{"who":".","created":"2017-01-18T01:12:58Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1329981","name":"Revoked intermediates","why":"."},"issuerName":"MHExCzAJBgNVBAYTAkRFMRwwGgYDVQQKExNEZXV0c2NoZSBUZWxla29tIEFHMR8wHQYDVQQLExZULVRlbGVTZWMgVHJ1c3QgQ2VudGVyMSMwIQYDVQQDExpEZXV0c2NoZSBUZWxla29tIFJvb3QgQ0EgMg==","id":"f8c41076-a3f0-439a-9d5e-41e27e019a77","schema":1483471396473},{"serialNumber":"ESDDtMgFFiaUfKo7HD9qImM7","enabled":true,"last_modified":1484704580544,"details":{"who":".","created":"2017-01-18T01:12:57Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1329981","name":"Revoked intermediates","why":"."},"issuerName":"MEAxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9wZW5UcnVzdCBSb290IENBIEcx","id":"ec0960f7-7ae1-23e7-5006-6652da817daa","schema":1483471396473},{"serialNumber":"RFlmmjulj6Ve7PfBi44nnw==","enabled":true,"last_modified":1484704580517,"details":{"who":".","created":"2017-01-18T01:12:57Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1329981","name":"Revoked intermediates","why":"."},"issuerName":"MCgxCzAJBgNVBAYTAkJFMRkwFwYDVQQDExBCZWxnaXVtIFJvb3QgQ0Ey","id":"0e7b9a2c-3604-5b89-4dff-796122174bdc","schema":1483471396473},{"serialNumber":"ESBrHE7sFC7CQ8EM681xA3CY","enabled":true,"last_modified":1484704580491,"details":{"who":".","created":"2017-01-18T01:12:57Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1329981","name":"Revoked intermediates","why":"."},"issuerName":"MEAxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9wZW5UcnVzdCBSb290IENBIEcx","id":"0595dc75-9356-ba32-a15e-05e3072b7f54","schema":1483471396473},{"serialNumber":"AjpW","enabled":true,"last_modified":1484704580465,"details":{"who":".","created":"2017-01-18T01:12:57Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1329981","name":"Revoked intermediates","why":"."},"issuerName":"MEIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9iYWwgQ0E=","id":"b7e26b4d-bbe1-1c4e-ef9b-12471bcb9bf8","schema":1483471396473},{"serialNumber":"a9rf7/BmG9JkKvRuy7J5QA==","enabled":true,"last_modified":1484704580438,"details":{"who":".","created":"2017-01-18T01:12:57Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1329981","name":"Revoked intermediates","why":"."},"issuerName":"MIGVMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTEdMBsGA1UEAxMUVVROLVVTRVJGaXJzdC1PYmplY3Q=","id":"fcfc3b3c-0a59-d143-f5fd-7600dd8efa87","schema":1483471396473},{"serialNumber":"ZECgRdZEsns=","enabled":true,"last_modified":1484704580412,"details":{"who":".","created":"2017-01-18T01:12:57Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1329981","name":"Revoked intermediates","why":"."},"issuerName":"MGExCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xMjAwBgNVBAMMKVN0YWF0IGRlciBOZWRlcmxhbmRlbiBPcmdhbmlzYXRpZSBDQSAtIEcy","id":"4652f392-127d-a5bf-4ed6-b07b9fa72247","schema":1483471396473},{"serialNumber":"AJBQSPqrEvDE2Hz8xH39Low=","enabled":true,"last_modified":1484704580385,"details":{"who":".","created":"2017-01-18T01:12:58Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1329981","name":"Revoked intermediates","why":"."},"issuerName":"MD8xJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjEXMBUGA1UEAxMORFNUIFJvb3QgQ0EgWDM=","id":"c279dd67-2ce1-e090-1e04-0c11fe3ddf8e","schema":1483471396473},{"serialNumber":"frj5jTuqBnQ4fljPvVU3KA==","enabled":true,"last_modified":1484704580358,"details":{"who":".","created":"2017-01-18T01:12:57Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1329981","name":"Revoked intermediates","why":"."},"issuerName":"MCgxCzAJBgNVBAYTAkJFMRkwFwYDVQQDExBCZWxnaXVtIFJvb3QgQ0Ey","id":"85acb18c-16b6-12c7-83ae-1e0d94251362","schema":1483471396473},{"serialNumber":"ByfFnw==","enabled":true,"last_modified":1484704580332,"details":{"who":".","created":"2017-01-18T01:12:58Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1329981","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=","id":"d0da2ea3-1cad-5c9e-4c75-c83acfeabc8d","schema":1483471396473},{"serialNumber":"ByembA==","enabled":true,"last_modified":1484704580305,"details":{"who":".","created":"2017-01-18T01:12:58Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1329981","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=","id":"fcd51190-7eaf-291e-b6e5-45e447de7291","schema":1483471396473},{"serialNumber":"RH7WhshwXRK6f0VfOfjXgQ==","enabled":true,"last_modified":1484704580277,"details":{"who":".","created":"2017-01-18T01:12:57Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1329981","name":"Revoked intermediates","why":"."},"issuerName":"MCgxCzAJBgNVBAYTAkJFMRkwFwYDVQQDExBCZWxnaXVtIFJvb3QgQ0Ey","id":"003234b2-f425-eae6-9596-040747dab2b9","schema":1483471396473},{"serialNumber":"SurdtfsuPcXXDpY2LkBpYO6BT7o=","enabled":true,"last_modified":1483471394790,"details":{"who":".","created":"2017-01-03T18:41:53Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDM=","id":"034627e4-44c6-fbf2-275a-4ed3431a4094","schema":1483471393228},{"serialNumber":"Er0moq4zwH8ke2pYafIKdg==","enabled":true,"last_modified":1483471394768,"details":{"who":".","created":"2017-01-03T18:41:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==","id":"85c81ed8-787f-ffcf-2a63-1be622db8d04","schema":1483471393228},{"serialNumber":"BydiAg==","enabled":true,"last_modified":1483471394746,"details":{"who":".","created":"2017-01-03T18:41:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=","id":"9194c97e-3baa-0446-9642-0d6211c3f019","schema":1483471393228},{"serialNumber":"eLumDUO40KwnecZLJxFM2A==","enabled":true,"last_modified":1483471394725,"details":{"who":".","created":"2017-01-03T18:41:52Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MCgxCzAJBgNVBAYTAkJFMRkwFwYDVQQDExBCZWxnaXVtIFJvb3QgQ0Ey","id":"c2cc63c7-5274-9901-5f67-0a13355a8aa8","schema":1483471393228},{"serialNumber":"Bw==","enabled":true,"last_modified":1483471394702,"details":{"who":".","created":"2017-01-03T18:41:53Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MDQxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25h","id":"92e2494f-f3cd-7638-7fe1-a3cb8d8939fa","schema":1483471393228},{"serialNumber":"GpO48aJ8GngtwECqZhm/xA==","enabled":true,"last_modified":1483471394681,"details":{"who":".","created":"2017-01-03T18:41:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MGQxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGlnaXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAy","id":"bdfdc34f-ba9a-7b25-6cb6-24c547eb8a10","schema":1483471393228},{"serialNumber":"Cw==","enabled":true,"last_modified":1483471394658,"details":{"who":".","created":"2017-01-03T18:41:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MFIxCzAJBgNVBAYTAk5MMRkwFwYDVQQKDBBEaWdpZGVudGl0eSBCLlYuMSgwJgYDVQQDDB9EaWdpZGVudGl0eSBPcmdhbmlzYXRpZSBDQSAtIEcy","id":"7574eb9e-6978-dcb7-5a6f-d4c3dd855254","schema":1483471393228},{"serialNumber":"IA==","enabled":true,"last_modified":1483471394635,"details":{"who":".","created":"2017-01-03T18:41:53Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MDQxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25h","id":"d82f446f-181a-5ac3-0ced-854e3cde100c","schema":1483471393228},{"serialNumber":"OfJBIhFwAdQ=","enabled":true,"last_modified":1483471394613,"details":{"who":".","created":"2017-01-03T18:41:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MGsxCzAJBgNVBAYTAklUMQ4wDAYDVQQHDAVNaWxhbjEjMCEGA1UECgwaQWN0YWxpcyBTLnAuQS4vMDMzNTg1MjA5NjcxJzAlBgNVBAMMHkFjdGFsaXMgQXV0aGVudGljYXRpb24gUm9vdCBDQQ==","id":"b8fdddf1-27c1-5f6e-58a1-295e2c8c0ea5","schema":1483471393228},{"serialNumber":"MABJTA==","enabled":true,"last_modified":1483471394591,"details":{"who":".","created":"2017-01-03T18:41:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MEgxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdTZWN1cmVUcnVzdCBDb3Jwb3JhdGlvbjEXMBUGA1UEAxMOU2VjdXJlVHJ1c3QgQ0E=","id":"71e08617-c00a-8b62-53c2-2a61e21e6155","schema":1483471393228},{"serialNumber":"HA==","enabled":true,"last_modified":1483471394569,"details":{"who":".","created":"2017-01-03T18:41:53Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MDQxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25h","id":"6a6d36e6-8939-0a83-3fd5-c38652b165ed","schema":1483471393228},{"serialNumber":"DA==","enabled":true,"last_modified":1483471394547,"details":{"who":".","created":"2017-01-03T18:41:55Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"ME0xCzAJBgNVBAYTAk5MMRkwFwYDVQQKDBBEaWdpZGVudGl0eSBCLlYuMSMwIQYDVQQDDBpEaWdpZGVudGl0eSBCdXJnZXIgQ0EgLSBHMg==","id":"460643a1-23f3-1beb-0f14-ecb4b6e26cc9","schema":1483471393228},{"serialNumber":"CLc=","enabled":true,"last_modified":1483471394524,"details":{"who":".","created":"2017-01-03T18:41:53Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDM=","id":"49eb43cf-91d4-66ce-1a86-b8674ff83d4d","schema":1483471393228},{"serialNumber":"QAAnEQ==","enabled":true,"last_modified":1483471394501,"details":{"who":".","created":"2017-01-03T18:41:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MEoxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdTZWN1cmVUcnVzdCBDb3Jwb3JhdGlvbjEZMBcGA1UEAxMQU2VjdXJlIEdsb2JhbCBDQQ==","id":"bb5c827f-3458-93fe-f80f-2982f0d19d34","schema":1483471393228},{"serialNumber":"AjqK","enabled":true,"last_modified":1483471394478,"details":{"who":".","created":"2017-01-03T18:41:53Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MEIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9iYWwgQ0E=","id":"d6f9a6d9-d936-dfaf-d396-8ae96769ef10","schema":1483471393228},{"serialNumber":"ByfNeA==","enabled":true,"last_modified":1483471394453,"details":{"who":".","created":"2017-01-03T18:41:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=","id":"02ec8a09-2ae4-cd2a-4fa1-5037fa945391","schema":1483471393228},{"serialNumber":"JLiDzgpL7oFNgJN+jIjt7w==","enabled":true,"last_modified":1483471394090,"details":{"who":".","created":"2017-01-03T18:41:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlBUyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRyZSBSb290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWU=","id":"5631f49c-a1fb-803e-ecf2-3ba82ca79f2e","schema":1483471393228},{"serialNumber":"BwImeaRkSZQLYwFREwKo3R1Jn+8=","enabled":true,"last_modified":1483471394067,"details":{"who":".","created":"2017-01-03T18:41:53Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDM=","id":"c2de8edd-fd89-36dd-63d0-d3a1df92274a","schema":1483471393228},{"serialNumber":"HxT1XSjIpzjMprp9Qu1gYQ==","enabled":true,"last_modified":1483471394046,"details":{"who":".","created":"2017-01-03T18:41:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MGQxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGlnaXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAx","id":"46d4712b-6db2-ce7e-0efd-675f3be896cf","schema":1483471393228},{"serialNumber":"QDi5sA==","enabled":true,"last_modified":1483471394023,"details":{"who":".","created":"2017-01-03T18:41:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MIGCMQswCQYDVQQGEwJVUzEeMBwGA1UECxMVd3d3LnhyYW1wc2VjdXJpdHkuY29tMSQwIgYDVQQKExtYUmFtcCBTZWN1cml0eSBTZXJ2aWNlcyBJbmMxLTArBgNVBAMTJFhSYW1wIEdsb2JhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==","id":"8acc0cad-dee8-7e2e-1799-e1a7b8b989c3","schema":1483471393228},{"serialNumber":"BA==","enabled":true,"last_modified":1483471394000,"details":{"who":".","created":"2017-01-03T18:41:53Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MDQxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25h","id":"b9ac21bb-8c1e-e562-5418-bbf6f6323c45","schema":1483471393228},{"serialNumber":"KuzHPJLdK5hNgJRo3R47Ag==","enabled":true,"last_modified":1483471393978,"details":{"who":".","created":"2017-01-03T18:41:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlBUyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRyZSBSb290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWU=","id":"91b7f544-9de1-efea-08d4-4ebafc9a3608","schema":1483471393228},{"serialNumber":"B+U=","enabled":true,"last_modified":1483471393956,"details":{"who":".","created":"2017-01-03T18:41:53Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDM=","id":"86fe91df-ebb8-f20d-2031-2bc815b14a25","schema":1483471393228},{"serialNumber":"Byeaqw==","enabled":true,"last_modified":1483471393933,"details":{"who":".","created":"2017-01-03T18:41:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=","id":"b97a8dce-04d9-7dba-6463-ae95d61524f4","schema":1483471393228},{"serialNumber":"LU4d0t7PAsZNgJGZcb+o/w==","enabled":true,"last_modified":1483471393911,"details":{"who":".","created":"2017-01-03T18:41:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlBUyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRyZSBSb290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWU=","id":"efdf0a12-4ba1-f84d-a88d-fc384251583c","schema":1483471393228},{"serialNumber":"BAAAAAABIg08FMU=","enabled":true,"last_modified":1483471393889,"details":{"who":".","created":"2017-01-03T18:41:53Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MEwxIDAeBgNVBAsTF0dsb2JhbFNpZ24gUm9vdCBDQSAtIFIyMRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu","id":"6c0561bc-fea3-ba35-bbdb-2b2672b67d36","schema":1483471393228},{"serialNumber":"AjqL","enabled":true,"last_modified":1483471393867,"details":{"who":".","created":"2017-01-03T18:41:53Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MEIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9iYWwgQ0E=","id":"4be7b89c-27a6-e95e-c6f9-bf652d4a0b97","schema":1483471393228},{"serialNumber":"FQ==","enabled":true,"last_modified":1483471393845,"details":{"who":".","created":"2017-01-03T18:41:53Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MDQxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25h","id":"89946691-d008-fd14-bd7a-443206d647c6","schema":1483471393228},{"serialNumber":"BydInw==","enabled":true,"last_modified":1483471393823,"details":{"who":".","created":"2017-01-03T18:41:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=","id":"e1a83fb0-93df-deb8-68cd-17f4997ea58b","schema":1483471393228},{"serialNumber":"JD1wxDd8IgmiqX7MyPPg1g==","enabled":true,"last_modified":1483471393801,"details":{"who":".","created":"2017-01-03T18:41:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MGQxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGlnaXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAx","id":"f5ad7ea6-4ac9-b9cd-776f-d2afb53fb91b","schema":1483471393228},{"serialNumber":"Qh/SqA==","enabled":true,"last_modified":1483471393779,"details":{"who":".","created":"2017-01-03T18:41:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5","id":"ad8a2f72-7124-3e33-3060-414ce2bd8be3","schema":1483471393228},{"serialNumber":"J8mznxvTvOR5p4Br3a3sm5j5iM0=","enabled":true,"last_modified":1483471393750,"details":{"who":".","created":"2017-01-03T18:41:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAkZSMRMwEQYDVQQKEwpDZXJ0aW5vbWlzMRcwFQYDVQQLEw4wMDAyIDQzMzk5ODkwMzEdMBsGA1UEAxMUQ2VydGlub21pcyAtIFJvb3QgQ0E=","id":"d3f6b499-297d-e9c8-081c-44e2331bcf29","schema":1483471393228},{"serialNumber":"AIChpbGNqu4XKp9J70syKEs=","enabled":true,"last_modified":1483471393727,"details":{"who":".","created":"2017-01-03T18:41:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MGQxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGlnaXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAy","id":"f184ba74-06de-9247-104e-160b6d210962","schema":1483471393228},{"serialNumber":"Cj0=","enabled":true,"last_modified":1483471393705,"details":{"who":".","created":"2017-01-03T18:41:53Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDM=","id":"c5fdcbc2-71d8-0a58-3375-0c7d92526cf1","schema":1483471393228},{"serialNumber":"Aw==","enabled":true,"last_modified":1483471393683,"details":{"who":".","created":"2017-01-03T18:41:53Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MDQxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25h","id":"0e8a05ee-feae-fc46-15b9-eaa2d11f4a60","schema":1483471393228},{"serialNumber":"BAAAAAABRE7wRk4=","enabled":true,"last_modified":1483471393659,"details":{"who":".","created":"2017-01-03T18:41:53Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MEwxIDAeBgNVBAsTF0dsb2JhbFNpZ24gUm9vdCBDQSAtIFIyMRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu","id":"ac8d3825-3fef-6d3e-2690-7360d1ef57a4","schema":1483471393228},{"serialNumber":"QDi5sQ==","enabled":true,"last_modified":1483471393637,"details":{"who":".","created":"2017-01-03T18:41:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MIGCMQswCQYDVQQGEwJVUzEeMBwGA1UECxMVd3d3LnhyYW1wc2VjdXJpdHkuY29tMSQwIgYDVQQKExtYUmFtcCBTZWN1cml0eSBTZXJ2aWNlcyBJbmMxLTArBgNVBAMTJFhSYW1wIEdsb2JhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==","id":"4b03d4c1-705b-458e-5bdc-f729f67eeb91","schema":1483471393228},{"serialNumber":"WJ2qHzWUqTk=","enabled":true,"last_modified":1483471393615,"details":{"who":".","created":"2017-01-03T18:41:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MGsxCzAJBgNVBAYTAklUMQ4wDAYDVQQHDAVNaWxhbjEjMCEGA1UECgwaQWN0YWxpcyBTLnAuQS4vMDMzNTg1MjA5NjcxJzAlBgNVBAMMHkFjdGFsaXMgQXV0aGVudGljYXRpb24gUm9vdCBDQQ==","id":"f93061a9-8afa-1d74-e063-4134d318f00b","schema":1483471393228},{"serialNumber":"VUtahOwvvmJFwlvmGDZP5w==","enabled":true,"last_modified":1483471393592,"details":{"who":".","created":"2017-01-03T18:41:52Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MCgxCzAJBgNVBAYTAkJFMRkwFwYDVQQDExBCZWxnaXVtIFJvb3QgQ0Ey","id":"9c88bc12-466d-fcdd-cc53-349d4e041332","schema":1483471393228},{"serialNumber":"L1fHogsVxmfMBka5q4uzaQ==","enabled":true,"last_modified":1483471393569,"details":{"who":".","created":"2017-01-03T18:41:52Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MCgxCzAJBgNVBAYTAkJFMRkwFwYDVQQDExBCZWxnaXVtIFJvb3QgQ0Ey","id":"d3c14505-c104-150c-0a2d-e5f9e92b6152","schema":1483471393228},{"serialNumber":"cJ+vg4742XhNgJW2ot9eIg==","enabled":true,"last_modified":1483471393547,"details":{"who":".","created":"2017-01-03T18:41:55Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlBUyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRyZSBSb290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWU=","id":"58bf9d8a-9a68-b41f-453c-8af8844bc07c","schema":1483471393228},{"serialNumber":"LizeWXFWP5pZPI/dLc+PVQ==","enabled":true,"last_modified":1483471393221,"details":{"who":".","created":"2017-01-03T18:41:52Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MCgxCzAJBgNVBAYTAkJFMRkwFwYDVQQDExBCZWxnaXVtIFJvb3QgQ0Ey","id":"cb1a1172-56d6-4150-1f50-eb2131f442f5","schema":1480617578654},{"serialNumber":"Ew==","enabled":true,"last_modified":1483471393199,"details":{"who":".","created":"2017-01-03T18:41:53Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MDQxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25h","id":"a0ff8e3f-e68d-5ee2-21e3-26cd0f46673b","schema":1480617578654},{"serialNumber":"BAAAAAABElatX7I=","enabled":true,"last_modified":1483471393177,"details":{"who":".","created":"2017-01-03T18:41:53Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MEwxIDAeBgNVBAsTF0dsb2JhbFNpZ24gUm9vdCBDQSAtIFIyMRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu","id":"00ac492e-04f7-ee6d-5fd2-bb12b97a4b7f","schema":1480617578654},{"serialNumber":"ANX8SnNRxCmsE/GCl5hw+8A=","enabled":true,"last_modified":1483471393155,"details":{"who":".","created":"2017-01-03T18:41:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MGQxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGlnaXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAy","id":"a09db9b3-2faa-73ab-67ff-61dcbf700ec7","schema":1480617578654},{"serialNumber":"VBSf+IncsTB3RZS4KFCJPQ==","enabled":true,"last_modified":1483471393133,"details":{"who":".","created":"2017-01-03T18:41:52Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MCgxCzAJBgNVBAYTAkJFMRkwFwYDVQQDExBCZWxnaXVtIFJvb3QgQ0Ey","id":"7e816865-7c1d-2519-f114-e69a280768f4","schema":1480617578654},{"serialNumber":"DA==","enabled":true,"last_modified":1483471393111,"details":{"who":".","created":"2017-01-03T18:41:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MFIxCzAJBgNVBAYTAk5MMRkwFwYDVQQKDBBEaWdpZGVudGl0eSBCLlYuMSgwJgYDVQQDDB9EaWdpZGVudGl0eSBPcmdhbmlzYXRpZSBDQSAtIEcy","id":"5b760d02-fdd7-d6be-cb6f-4d30bf97746e","schema":1480617578654},{"serialNumber":"QDi5rw==","enabled":true,"last_modified":1483471393088,"details":{"who":".","created":"2017-01-03T18:41:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MIGCMQswCQYDVQQGEwJVUzEeMBwGA1UECxMVd3d3LnhyYW1wc2VjdXJpdHkuY29tMSQwIgYDVQQKExtYUmFtcCBTZWN1cml0eSBTZXJ2aWNlcyBJbmMxLTArBgNVBAMTJFhSYW1wIEdsb2JhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==","id":"7de029af-ddf3-02be-ca26-5bb95b080d14","schema":1480617578654},{"serialNumber":"MABJSw==","enabled":true,"last_modified":1483471393066,"details":{"who":".","created":"2017-01-03T18:41:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MEgxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdTZWN1cmVUcnVzdCBDb3Jwb3JhdGlvbjEXMBUGA1UEAxMOU2VjdXJlVHJ1c3QgQ0E=","id":"411b4e82-2ddd-20c2-20b3-8d77145f6901","schema":1480617578654},{"serialNumber":"Ermwtg==","enabled":true,"last_modified":1483471393043,"details":{"who":".","created":"2017-01-03T18:41:53Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MFAxCzAJBgNVBAYTAkpQMRgwFgYDVQQKEw9TRUNPTSBUcnVzdC5uZXQxJzAlBgNVBAsTHlNlY3VyaXR5IENvbW11bmljYXRpb24gUm9vdENBMQ==","id":"9d8a83d8-d651-42a0-ac1c-0ee414f3e31a","schema":1480617578654},{"serialNumber":"Nbc68Q8EHza72P/hSWcddw==","enabled":true,"last_modified":1483471393021,"details":{"who":".","created":"2017-01-03T18:41:52Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MCgxCzAJBgNVBAYTAkJFMRkwFwYDVQQDExBCZWxnaXVtIFJvb3QgQ0Ey","id":"d0513de2-6da9-d68d-78cc-a2292a9d18fb","schema":1480617578654},{"serialNumber":"Ig==","enabled":true,"last_modified":1483471392998,"details":{"who":".","created":"2017-01-03T18:41:53Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MDQxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25h","id":"872ba8c8-a236-9ac0-85ea-08630f5b17e2","schema":1480617578654},{"serialNumber":"APdCebq8ZyZr/T0luxlicNw=","enabled":true,"last_modified":1483471392976,"details":{"who":".","created":"2017-01-03T18:41:52Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MD8xCzAJBgNVBAYTAlRXMTAwLgYDVQQKDCdHb3Zlcm5tZW50IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHk=","id":"7a24e461-9fd0-b17f-01e0-f44866b800f1","schema":1480617578654},{"serialNumber":"Ermw0Q==","enabled":true,"last_modified":1483471392954,"details":{"who":".","created":"2017-01-03T18:41:53Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MFAxCzAJBgNVBAYTAkpQMRgwFgYDVQQKEw9TRUNPTSBUcnVzdC5uZXQxJzAlBgNVBAsTHlNlY3VyaXR5IENvbW11bmljYXRpb24gUm9vdENBMQ==","id":"fff46511-7357-4559-3d36-75fc74034299","schema":1480617578654},{"serialNumber":"Ajp/","enabled":true,"last_modified":1483471392932,"details":{"who":".","created":"2017-01-03T18:41:53Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MEIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9iYWwgQ0E=","id":"272f2a95-6aec-f333-22ad-709d6118a87b","schema":1480617578654},{"serialNumber":"Fw==","enabled":true,"last_modified":1483471392911,"details":{"who":".","created":"2017-01-03T18:41:53Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MDQxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25h","id":"4f0dfd30-9278-4f20-25c3-436798595b84","schema":1480617578654},{"serialNumber":"K1ftto7Xcb0YKwQ6uMvOIA==","enabled":true,"last_modified":1483471392889,"details":{"who":".","created":"2017-01-03T18:41:53Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MD8xCzAJBgNVBAYTAlRXMTAwLgYDVQQKDCdHb3Zlcm5tZW50IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHk=","id":"29eab149-524e-b1da-e2b9-dc4a784ab64b","schema":1480617578654},{"serialNumber":"STMAFQ==","enabled":true,"last_modified":1483471392867,"details":{"who":".","created":"2017-01-03T18:41:53Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MDIxCzAJBgNVBAYTAkNOMQ4wDAYDVQQKEwVDTk5JQzETMBEGA1UEAxMKQ05OSUMgUk9PVA==","id":"91609507-ef23-7672-3a5d-06dfb9b0dac4","schema":1480617578654},{"serialNumber":"U+1Y1QpJc0FOR5JdCJ01gQ==","enabled":true,"last_modified":1483471392845,"details":{"who":".","created":"2017-01-03T18:41:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MGQxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGlnaXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAx","id":"79f39790-1151-6bb2-0a50-fc596b82bedd","schema":1480617578654},{"serialNumber":"TAA2G+UIK6mqznQKBT77NA==","enabled":true,"last_modified":1483471392823,"details":{"who":".","created":"2017-01-03T18:41:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==","id":"d51879d6-0a85-8311-5b14-ad993cec17e6","schema":1480617578654},{"serialNumber":"Iw==","enabled":true,"last_modified":1483471392801,"details":{"who":".","created":"2017-01-03T18:41:53Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MDQxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25h","id":"532dd854-cdef-6452-2793-1e36d091d9ec","schema":1480617578654},{"serialNumber":"Qh/O5w==","enabled":true,"last_modified":1483471392779,"details":{"who":".","created":"2017-01-03T18:41:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5","id":"756ab100-8a14-daf7-bf06-9fe423863208","schema":1480617578654},{"serialNumber":"Ajp+","enabled":true,"last_modified":1483471392757,"details":{"who":".","created":"2017-01-03T18:41:53Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MEIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9iYWwgQ0E=","id":"41f2401b-5c31-724f-ad6c-28f3f6f47634","schema":1480617578654},{"serialNumber":"AUa47POQ1dN5","enabled":true,"last_modified":1483471392735,"details":{"who":".","created":"2017-01-03T18:41:53Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MFExCzAJBgNVBAYTAkpQMRMwEQYDVQQKEwpGdWppIFhlcm94MS0wKwYDVQQDEyRGdWppIFhlcm94IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IDI=","id":"0956d064-8750-eee2-b0c7-7e1c2d1d6f25","schema":1480617578654},{"serialNumber":"Pgyeh2mqlVzqI9hFntRbUQ==","enabled":true,"last_modified":1483471392712,"details":{"who":".","created":"2017-01-03T18:41:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==","id":"134969c5-0bf4-4054-eaa1-b24feaa76aef","schema":1480617578654},{"serialNumber":"Gg==","enabled":true,"last_modified":1483471392689,"details":{"who":".","created":"2017-01-03T18:41:53Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1315199","name":"Revoked intermediates","why":"."},"issuerName":"MDQxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25h","id":"141d8e99-193b-d108-382b-7d97e6912d8c","schema":1480617578654},{"serialNumber":"ESDYXNBhF+dePFjojs7u2vj1","enabled":true,"last_modified":1480349168043,"details":{"who":".","created":"2016-10-27T17:52:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xhc3MgMiBQcmltYXJ5IENB","id":"f7f193ca-c34e-866a-8abf-d44188a78cb0","schema":1480349159276},{"serialNumber":"TA6BjA==","enabled":true,"last_modified":1480349168021,"details":{"who":".","created":"2016-10-27T17:52:14Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp","id":"2a9d41c8-d3e7-a40a-a79a-899902aa73cb","schema":1480349159276},{"serialNumber":"XhcFm2g619rt8Sro+a4rHA==","enabled":true,"last_modified":1480349167999,"details":{"who":".","created":"2016-04-28T21:08:06Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MGYxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQLExREb21haW4gVmFsaWRhdGVkIFNTTDEgMB4GA1UEAxMXR2VvVHJ1c3QgRFYgU1NMIENBIC0gRzI=","id":"a01f2d94-e1ee-2139-5652-c216331e357a","schema":1480349159276},{"serialNumber":"F6QlB/yX+A==","enabled":true,"last_modified":1480349167976,"details":{"who":".","created":"2016-10-27T17:52:11Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=","id":"a7fa8b99-3e2a-2c03-dffd-ab341eae59af","schema":1480349159276},{"serialNumber":"Bydxog==","enabled":true,"last_modified":1480349167954,"details":{"who":".","created":"2016-10-27T17:52:11Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=","id":"35f6f9f8-eb17-39d2-50a7-2b40f01e2584","schema":1480349159276},{"serialNumber":"CqnbFQ==","enabled":true,"last_modified":1480349167931,"details":{"who":".","created":"2016-10-27T17:52:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=","id":"3b60bc42-674b-7822-113f-c8dc6d1b015e","schema":1480349159276},{"serialNumber":"Xrr31RF0DoIzMKXS6XtD+g==","enabled":true,"last_modified":1480349167904,"details":{"who":".","created":"2016-10-27T17:52:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MIGXMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTEfMB0GA1UEAxMWVVROLVVTRVJGaXJzdC1IYXJkd2FyZQ==","id":"c0d2651b-f567-0f6d-ce3c-16e7d19390d0","schema":1480349159276},{"serialNumber":"Qh/SnQ==","enabled":true,"last_modified":1480349167882,"details":{"who":".","created":"2016-10-27T17:52:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5","id":"7326aa15-9b3e-44ca-c1c5-0ed1ff29521f","schema":1480349159276},{"serialNumber":"H08=","enabled":true,"last_modified":1480349167861,"details":{"who":".","created":"2016-04-28T21:08:07Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MGYxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQLExREb21haW4gVmFsaWRhdGVkIFNTTDEgMB4GA1UEAxMXR2VvVHJ1c3QgRFYgU1NMIENBIC0gRzQ=","id":"2ac02b19-f616-0dc3-6d01-28e1bea3dd93","schema":1480349159276},{"serialNumber":"NMpMcEnex3eXx4ohk9glcQ==","enabled":true,"last_modified":1480349167827,"details":{"who":".","created":"2016-03-01T21:22:27Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1252142","name":"exceptional SHA-1 Certificates","why":"."},"issuerName":"MIG8MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTEwMTYwNAYDVQQDEy1WZXJpU2lnbiBDbGFzcyAzIEludGVybmF0aW9uYWwgU2VydmVyIENBIC0gRzM=","id":"8e453b5c-3f81-2694-2f10-73ec8c406c49","schema":1480349159276},{"serialNumber":"AQAAAAA=","enabled":true,"last_modified":1480349167797,"details":{"who":".","created":"2016-10-27T17:52:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MIGVMQswCQYDVQQGEwJHUjFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTE=","id":"a3b400ad-0b4d-aa11-e8b5-82019fbc84d5","schema":1480349159276},{"serialNumber":"Ikdj3zYXXGsC/Afm9Tvx+g==","enabled":true,"last_modified":1480349167774,"details":{"who":".","created":"2016-10-27T17:52:11Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MIGpMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3RlLCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9uMTgwNgYDVQQLEy8oYykgMjAwNiB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEfMB0GA1UEAxMWdGhhd3RlIFByaW1hcnkgUm9vdCBDQQ==","id":"e8b2d24e-5aaf-86dd-441e-b4781d5370db","schema":1480349159276},{"serialNumber":"acI1CFIgmwSFBoU5+ahDgg==","enabled":true,"last_modified":1480349167744,"details":{"who":".","created":"2016-04-28T21:08:07Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MHcxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEoMCYGA1UEAxMfU3ltYW50ZWMgQ2xhc3MgMyBFViBTU0wgQ0EgLSBHMw==","id":"938358e4-4c70-beb4-0fbe-02009feee6b6","schema":1480349159276},{"serialNumber":"BAAAAAABA/A35EU=","enabled":true,"last_modified":1480349167722,"details":{"who":".","created":"2016-01-18T14:43:37Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155145","name":"GlobalSign certs","why":"."},"issuerName":"MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==","id":"97fbf7c4-3ef2-f54f-0029-1ba6540c63ea","schema":1480349159276},{"serialNumber":"UVKsEezpGWOVQ4W9esstng==","enabled":true,"last_modified":1480349167701,"details":{"who":".","created":"2016-04-28T21:08:07Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MHcxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEoMCYGA1UEAxMfU3ltYW50ZWMgQ2xhc3MgMyBFViBTU0wgQ0EgLSBHMg==","id":"c4c22010-0cb9-e9af-005d-2483612d864e","schema":1480349159276},{"serialNumber":"F5Bg/C8eXg==","enabled":true,"last_modified":1480349167343,"details":{"who":".","created":"2016-10-27T17:52:11Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=","id":"4295bb93-6e34-a58a-4d1c-238615b57cb0","schema":1480349159276},{"serialNumber":"AImQERVYPoeb","enabled":true,"last_modified":1480349167322,"details":{"who":".","created":"2016-10-27T17:52:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MHExCzAJBgNVBAYTAkRFMRwwGgYDVQQKExNEZXV0c2NoZSBUZWxla29tIEFHMR8wHQYDVQQLExZULVRlbGVTZWMgVHJ1c3QgQ2VudGVyMSMwIQYDVQQDExpEZXV0c2NoZSBUZWxla29tIFJvb3QgQ0EgMg==","id":"4ca8f1a1-0dc0-881f-b497-cc5574f2494d","schema":1480349159276},{"serialNumber":"J2La+q+JOURNWkX60OP2lQ==","enabled":true,"last_modified":1480349167291,"details":{"who":".","created":"2016-07-14T14:40:23Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1286752","name":"Symantec erroneous SHA-1 certificates","why":"."},"issuerName":"MIG8MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTEwMTYwNAYDVQQDEy1WZXJpU2lnbiBDbGFzcyAzIEludGVybmF0aW9uYWwgU2VydmVyIENBIC0gRzM=","id":"8662e9a7-fe16-d1fc-0993-d16dd2f01012","schema":1480349159276},{"serialNumber":"BAAAAAABHkSl5ao=","enabled":true,"last_modified":1480349167269,"details":{"who":".","created":"2016-10-27T17:52:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MG0xCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRswGQYDVQQLExJQcmltYXJ5IENsYXNzIDEgQ0ExJjAkBgNVBAMTHUdsb2JhbFNpZ24gUHJpbWFyeSBDbGFzcyAxIENB","id":"7cfc5a53-f950-c15b-4dbb-8124fadcf871","schema":1480349159276},{"serialNumber":"RUT1Gehd1KKYPfqOlgspoQ==","enabled":true,"last_modified":1480349167246,"details":{"who":".","created":"2016-04-28T21:08:07Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MEQxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQDExRHZW9UcnVzdCBTU0wgQ0EgLSBHMw==","id":"8855fafd-e48b-680d-ff15-a022057d9b9e","schema":1480349159276},{"serialNumber":"BAAAAAABKUXDqxw=","enabled":true,"last_modified":1480349167224,"details":{"who":".","created":"2016-01-18T14:41:26Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155145","name":"GlobalSign certs","why":"."},"issuerName":"MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==","id":"0a92faa8-b870-3a38-036e-9b95185fcb6a","schema":1480349159276},{"serialNumber":"CqZgEvHAsnzkT//QV9KjXw==","enabled":true,"last_modified":1480349167202,"details":{"who":".","created":"2016-04-28T21:08:08Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MGMxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwx0aGF3dGUsIEluYy4xHTAbBgNVBAsTFERvbWFpbiBWYWxpZGF0ZWQgU1NMMR4wHAYDVQQDExV0aGF3dGUgRFYgU1NMIENBIC0gRzI=","id":"39008976-298e-f664-50c0-56f8ae6c4df5","schema":1480349159276},{"serialNumber":"OUvvVscW0/NltofkmV9qmg==","enabled":true,"last_modified":1480349167179,"details":{"who":".","created":"2016-04-28T21:08:07Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MEYxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR8wHQYDVQQDExZHZW9UcnVzdCBTSEEyNTYgU1NMIENB","id":"a5eb3877-5e7d-200a-cee3-f63b8004d58e","schema":1480349159276},{"serialNumber":"ORFgmCj072NjcJnrxOMfQA==","enabled":true,"last_modified":1480349167156,"details":{"who":".","created":"2016-04-28T21:08:07Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MGYxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQLExREb21haW4gVmFsaWRhdGVkIFNTTDEgMB4GA1UEAxMXR2VvVHJ1c3QgRFYgU1NMIENBIC0gRzM=","id":"995df7f2-6bd4-12c6-b1aa-fcfe7618b193","schema":1480349159276},{"serialNumber":"GN2Hrh9Ltms=","enabled":true,"last_modified":1480349167125,"details":{"who":".","created":"2016-10-27T17:52:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MIGVMQswCQYDVQQGEwJHUjFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTE=","id":"460ae779-c915-9daf-9a13-e0bf953322cb","schema":1480349159276},{"serialNumber":"ARQ=","enabled":true,"last_modified":1480349167102,"details":{"who":".","created":"2015-05-05T11:10:09Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155119","name":"T-Systems intermediate certificate","why":"."},"issuerName":"MHExCzAJBgNVBAYTAkRFMRwwGgYDVQQKExNEZXV0c2NoZSBUZWxla29tIEFHMR8wHQYDVQQLExZULVRlbGVTZWMgVHJ1c3QgQ2VudGVyMSMwIQYDVQQDExpEZXV0c2NoZSBUZWxla29tIFJvb3QgQ0EgMg==","id":"2826cef9-e4b4-53f9-e3cf-c5870fc778dd","schema":1480349159276},{"serialNumber":"DAk9hy8DhHSo+aQetvPB/fY=","enabled":true,"last_modified":1480349167079,"details":{"who":".","created":"2016-01-18T14:46:49Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155145","name":"GlobalSign certs","why":"."},"issuerName":"MFwxCzAJBgNVBAYTAkJFMRUwEwYDVQQLEwxUcnVzdGVkIFJvb3QxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExGzAZBgNVBAMTElRydXN0ZWQgUm9vdCBDQSBHMg==","id":"b42066e0-0c88-e02b-620f-c41c2118c4e7","schema":1480349159276},{"serialNumber":"BHk=","enabled":true,"last_modified":1480349167057,"details":{"who":".","created":"2016-11-03T12:48:38Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1309305","name":"Hongkong Post e-Cert CA 1-10 certificates","why":"."},"issuerName":"MEcxCzAJBgNVBAYTAkhLMRYwFAYDVQQKEw1Ib25na29uZyBQb3N0MSAwHgYDVQQDExdIb25na29uZyBQb3N0IFJvb3QgQ0EgMQ==","id":"5048a7c5-79c8-68d7-06a3-19e8ba32e5fc","schema":1480349159276},{"serialNumber":"OnvXX72mvUI2Id/NMzegmg==","enabled":true,"last_modified":1480349167035,"details":{"who":".","created":"2016-04-28T21:08:08Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MIG8MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTEwMTYwNAYDVQQDEy1WZXJpU2lnbiBDbGFzcyAzIEludGVybmF0aW9uYWwgU2VydmVyIENBIC0gRzM=","id":"92d843e8-4e72-2832-b56f-6e488e677d0f","schema":1480349159276},{"serialNumber":"ZgwfEqZnBsUNvNuZ77FbQA==","enabled":true,"last_modified":1480349167012,"details":{"who":".","created":"2016-04-28T21:08:07Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MGgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQLExREb21haW4gVmFsaWRhdGVkIFNTTDEiMCAGA1UEAxMZR2VvVHJ1c3QgRFYgU1NMIFNIQTI1NiBDQQ==","id":"73ae5fed-730d-94db-04ef-2aafd5ff75b8","schema":1480349159276},{"serialNumber":"Qh/QbQ==","enabled":true,"last_modified":1480349166989,"details":{"who":".","created":"2016-10-27T17:52:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5","id":"6e4739fe-1aed-2320-4dc3-832043a31fc8","schema":1480349159276},{"serialNumber":"L79XLVO2ZmtAu7FAG8Wmzw==","enabled":true,"last_modified":1480349166968,"details":{"who":".","created":"2016-04-28T21:08:07Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MGYxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQLExREb21haW4gVmFsaWRhdGVkIFNTTDEgMB4GA1UEAxMXR2VvVHJ1c3QgRFYgU1NMIENBIC0gRzM=","id":"7d2a48b3-0b2e-59ae-2002-8edb4da20bd2","schema":1480349159276},{"serialNumber":"BUrYjru5px1ym4QUN33TOQ==","enabled":true,"last_modified":1480349166946,"details":{"who":".","created":"2016-04-28T21:08:08Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MGMxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwx0aGF3dGUsIEluYy4xHTAbBgNVBAsTFERvbWFpbiBWYWxpZGF0ZWQgU1NMMR4wHAYDVQQDExV0aGF3dGUgRFYgU1NMIENBIC0gRzI=","id":"e95bb238-6d35-2cce-9744-d6a672b0a874","schema":1480349159276},{"serialNumber":"ESByNJZ5TPjg9iZyL6a/h5Zx","enabled":true,"last_modified":1480349166921,"details":{"who":".","created":"2016-10-27T17:52:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xhc3MgMiBQcmltYXJ5IENB","id":"51935a37-2964-18cf-b34c-a20c2c2250ea","schema":1480349159276},{"serialNumber":"Aw==","enabled":true,"last_modified":1480349166898,"details":{"who":".","created":"2016-11-03T12:46:07Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1314673","name":"SecureSign Public CA11 intermediate CA cert","why":"."},"issuerName":"MFgxCzAJBgNVBAYTAkpQMSswKQYDVQQKEyJKYXBhbiBDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1cmVTaWduIFJvb3RDQTEx","id":"ec4f91dd-7560-920a-f178-e8ae460dd595","schema":1480349159276},{"serialNumber":"BAAAAAABFUtaxac=","enabled":true,"last_modified":1480349166876,"details":{"who":".","created":"2016-10-27T17:52:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxTaWduIFJvb3QgQ0E=","id":"8d87b3cd-b954-f4f1-bfb2-a0e60645301c","schema":1480349159276},{"serialNumber":"ATE3ew==","enabled":true,"last_modified":1480349166855,"details":{"who":".","created":"2016-10-27T17:52:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFwxCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xLTArBgNVBAMMJFN0YWF0IGRlciBOZWRlcmxhbmRlbiBCdXJnZXIgQ0EgLSBHMg==","id":"83ac91ce-0f5e-ae4e-2010-b0da5616cd59","schema":1480349159276},{"serialNumber":"LnfcUaXG/pxV2CpXM5+YSg==","enabled":true,"last_modified":1480349166833,"details":{"who":".","created":"2016-04-28T21:08:07Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MH4xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEvMC0GA1UEAxMmU3ltYW50ZWMgQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzQ=","id":"4c743a6f-af95-49a6-bd4a-d1ee8160c537","schema":1480349159276},{"serialNumber":"BGU=","enabled":true,"last_modified":1480349166811,"details":{"who":".","created":"2016-11-03T12:49:39Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1309305","name":"Hongkong Post e-Cert CA 1-10 certificates","why":"."},"issuerName":"MEcxCzAJBgNVBAYTAkhLMRYwFAYDVQQKEw1Ib25na29uZyBQb3N0MSAwHgYDVQQDExdIb25na29uZyBQb3N0IFJvb3QgQ0EgMQ==","id":"4de7908c-45e7-3b7f-a91a-8cefb1ecf830","schema":1480349159276},{"serialNumber":"BAAAAAABJZbEU4I=","enabled":true,"last_modified":1480349166787,"details":{"who":".","created":"2016-01-18T14:42:46Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155145","name":"GlobalSign certs","why":"."},"issuerName":"MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==","id":"19c9a896-fbf0-8ad0-92cd-4aca2577c006","schema":1480349159276},{"serialNumber":"BAAAAAABIg08D3U=","enabled":true,"last_modified":1480349166478,"details":{"who":".","created":"2016-10-27T17:52:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MEwxIDAeBgNVBAsTF0dsb2JhbFNpZ24gUm9vdCBDQSAtIFIyMRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu","id":"4aec7420-aa59-53b8-1373-d3c0a7ebc837","schema":1480349159276},{"serialNumber":"CgFBQgAAAUFcf/EVAAAAAg==","enabled":true,"last_modified":1480349166455,"details":{"who":".","created":"2016-10-27T17:52:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MD8xJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjEXMBUGA1UEAxMORFNUIFJvb3QgQ0EgWDM=","id":"d60a94e9-3f7f-a20f-1dcc-c87ccc78fb99","schema":1480349159276},{"serialNumber":"dItWlz2V62Philqj9m6Pbg==","enabled":true,"last_modified":1480349166433,"details":{"who":".","created":"2016-04-28T21:08:07Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MGYxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQLExREb21haW4gVmFsaWRhdGVkIFNTTDEgMB4GA1UEAxMXR2VvVHJ1c3QgRFYgU1NMIENBIC0gRzM=","id":"abb0df0d-6716-9a25-ae33-806e93276cd4","schema":1480349159276},{"serialNumber":"CcL+EA==","enabled":true,"last_modified":1480349166405,"details":{"who":".","created":"2016-10-27T17:52:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=","id":"3cef2b9e-ddcd-cc40-8d59-49408409a3bb","schema":1480349159276},{"serialNumber":"TA6EVg==","enabled":true,"last_modified":1480349166381,"details":{"who":".","created":"2016-04-12T12:50:50Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1263733","name":"Disney CA intermediate CA certificate","why":"."},"issuerName":"MIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp","id":"7b09e4fc-2261-e7c6-e926-5a7b5e74fc5e","schema":1480349159276},{"serialNumber":"GdXz4L1b6FKNCMG9Jz2tjA==","enabled":true,"last_modified":1480349166358,"details":{"who":".","created":"2016-04-28T21:08:08Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MGMxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwx0aGF3dGUsIEluYy4xHTAbBgNVBAsTFERvbWFpbiBWYWxpZGF0ZWQgU1NMMR4wHAYDVQQDExV0aGF3dGUgRFYgU1NMIENBIC0gRzI=","id":"35f81fe8-9fa4-760b-9fd0-2de1b0191721","schema":1480349159276},{"serialNumber":"COwoDFvz7GD8R2K7Lo0rYQ==","enabled":true,"last_modified":1480349166330,"details":{"who":".","created":"2016-03-01T21:23:14Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1252142","name":"exceptional SHA-1 Certificates","why":"."},"issuerName":"MIG8MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTEwMTYwNAYDVQQDEy1WZXJpU2lnbiBDbGFzcyAzIEludGVybmF0aW9uYWwgU2VydmVyIENBIC0gRzM=","id":"cc56260c-5f3a-3f4b-c712-78a8e7facd27","schema":1480349159276},{"serialNumber":"WD1AyQAAAAAAJQ==","enabled":true,"last_modified":1480349166308,"details":{"who":".","created":"2016-10-27T17:52:14Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MIGKMQswCQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEmMCQGA1UECxMdQ29weXJpZ2h0IChjKSAyMDA1IFdJU2VLZXkgU0ExFjAUBgNVBAsTDUludGVybmF0aW9uYWwxKTAnBgNVBAMTIFdJU2VLZXkgQ2VydGlmeUlEIEFkdmFuY2VkIEcxIENB","id":"8c984ecd-c61c-426a-97aa-3a808e4da482","schema":1480349159276},{"serialNumber":"GN2Hrh9LtnI=","enabled":true,"last_modified":1480349166286,"details":{"who":".","created":"2016-10-27T17:52:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MIGVMQswCQYDVQQGEwJHUjFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTE=","id":"333f6eb2-cefe-1a3b-3726-a8320b047847","schema":1480349159276},{"serialNumber":"BAAAAAABJQcQRNU=","enabled":true,"last_modified":1480349166264,"details":{"who":".","created":"2016-10-27T17:52:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MEwxIDAeBgNVBAsTF0dsb2JhbFNpZ24gUm9vdCBDQSAtIFIzMRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu","id":"2258e9bc-1c39-9db3-4fdb-c7eb12d0609c","schema":1480349159276},{"serialNumber":"Bg==","enabled":true,"last_modified":1480349166240,"details":{"who":".","created":"2016-07-28T12:15:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1289808","name":"FNMT revoked intermediate certificates","why":"."},"issuerName":"MDsxCzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTQ==","id":"b7fb6842-6407-8ae4-5e0f-e6daf112ed4f","schema":1480349159276},{"serialNumber":"IIxFSyNM6mWtCgTG0IL3Og==","enabled":true,"last_modified":1480349166215,"details":{"who":".","created":"2016-04-28T21:08:08Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MGMxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwx0aGF3dGUsIEluYy4xHTAbBgNVBAsTFERvbWFpbiBWYWxpZGF0ZWQgU1NMMR4wHAYDVQQDExV0aGF3dGUgRFYgU1NMIENBIC0gRzI=","id":"6457eeb8-d83a-3818-c416-0dce6d71d471","schema":1480349159276},{"serialNumber":"BAAAAAABHkSl5AQ=","enabled":true,"last_modified":1480349166191,"details":{"who":".","created":"2016-10-27T17:52:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxTaWduIFJvb3QgQ0E=","id":"de8e6484-fc97-6889-a1f9-dafd45786606","schema":1480349159276},{"serialNumber":"ByeQ9g==","enabled":true,"last_modified":1480349166170,"details":{"who":".","created":"2016-10-27T17:52:11Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=","id":"bb5a82a6-8da0-5390-a7d6-843bdb0c02c2","schema":1480349159276},{"serialNumber":"EQ==","enabled":true,"last_modified":1480349166148,"details":{"who":".","created":"2016-07-28T12:17:01Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1289808","name":"FNMT revoked intermediate certificates","why":"."},"issuerName":"MDsxCzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTQ==","id":"dcce309f-aa60-6484-eaed-aa8310440a5c","schema":1480349159276},{"serialNumber":"R4af5A==","enabled":true,"last_modified":1480349166126,"details":{"who":".","created":"2016-10-27T17:52:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MEgxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdTZWN1cmVUcnVzdCBDb3Jwb3JhdGlvbjEXMBUGA1UEAxMOU2VjdXJlVHJ1c3QgQ0E=","id":"e60eeeb2-612e-ef08-d0b8-5e9f8e1a23a9","schema":1480349159276},{"serialNumber":"BAAAAAAA+X/GIyk=","enabled":true,"last_modified":1480349166102,"details":{"who":".","created":"2016-01-18T14:40:51Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155145","name":"GlobalSign certs","why":"."},"issuerName":"MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==","id":"528cd047-ef3b-fc23-e37f-5d67fd3117e4","schema":1480349159276},{"serialNumber":"BYyEX2b5+K+myAIR7eXaRQ==","enabled":true,"last_modified":1480349166080,"details":{"who":".","created":"2016-07-14T14:40:23Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1286752","name":"Symantec erroneous SHA-1 certificates","why":"."},"issuerName":"MIG8MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTEwMTYwNAYDVQQDEy1WZXJpU2lnbiBDbGFzcyAzIEludGVybmF0aW9uYWwgU2VydmVyIENBIC0gRzM=","id":"9be08dd3-1922-fb30-77dc-5cfcf00164a0","schema":1480349159276},{"serialNumber":"RurwlgVMxeP6Zepun0LGZA==","enabled":true,"last_modified":1480349166058,"details":{"who":".","created":"2016-10-27T17:52:14Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsxIjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3Q=","id":"22a74468-602c-f4ac-0003-be4ce0167258","schema":1480349159276},{"serialNumber":"AJiWmg==","enabled":true,"last_modified":1480349166036,"details":{"who":".","created":"2016-10-27T17:52:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFgxCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xKTAnBgNVBAMMIFN0YWF0IGRlciBOZWRlcmxhbmRlbiBFViBSb290IENB","id":"1525b265-22d6-3253-079c-c4ffca58458f","schema":1480349159276},{"serialNumber":"ESCLRVuhcUZaluIgIVlRJx+O","enabled":true,"last_modified":1480349166012,"details":{"who":".","created":"2016-10-27T17:52:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xhc3MgMiBQcmltYXJ5IENB","id":"a6299e39-84a4-2dce-ffbb-751107660f4f","schema":1480349159276},{"serialNumber":"BAAAAAABCUVQ9No=","enabled":true,"last_modified":1480349165990,"details":{"who":".","created":"2016-01-18T14:43:20Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155145","name":"GlobalSign certs","why":"."},"issuerName":"MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==","id":"a1d34c2f-4a03-0e35-ba5f-bc14138bcff5","schema":1480349159276},{"serialNumber":"BAAAAAABF2Tb8Bc=","enabled":true,"last_modified":1480349165968,"details":{"who":".","created":"2016-01-18T14:38:26Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155145","name":"GlobalSign certs","why":"."},"issuerName":"MF8xCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRQwEgYDVQQLEwtQYXJ0bmVycyBDQTEfMB0GA1UEAxMWR2xvYmFsU2lnbiBQYXJ0bmVycyBDQQ==","id":"7e19f742-420e-dbe9-f691-2d19430d75b2","schema":1480349159276},{"serialNumber":"GN2Hrh9Ltm4=","enabled":true,"last_modified":1480349165947,"details":{"who":".","created":"2016-10-27T17:52:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MIGVMQswCQYDVQQGEwJHUjFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTE=","id":"20732fc6-dd20-fe76-b6b5-b78388b64bdd","schema":1480349159276},{"serialNumber":"fMTRbGCp280pnyE/u53zbA==","enabled":true,"last_modified":1480349165925,"details":{"who":".","created":"2016-10-27T17:52:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MIG9MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5","id":"44aa21e7-a92c-a0cc-6f6c-85b7ee52a87d","schema":1480349159276},{"serialNumber":"d8AtKymQwkOPDBj+hjPzFg==","enabled":true,"last_modified":1480349165591,"details":{"who":".","created":"2016-04-28T21:08:07Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MH4xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEvMC0GA1UEAxMmU3ltYW50ZWMgQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzQ=","id":"014a5b67-d566-0767-c9d7-48e54115a69a","schema":1480349159276},{"serialNumber":"Os2rnHWYhryvdOXfgan06A==","enabled":true,"last_modified":1480349165569,"details":{"who":".","created":"2016-10-27T17:52:14Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsxIjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3Q=","id":"dc94f688-044b-f8a0-79f9-5dc2d42e3edb","schema":1480349159276},{"serialNumber":"e/fIfg2Dj2tkYIWVu2r82Cc=","enabled":true,"last_modified":1480349165547,"details":{"who":".","created":"2016-01-18T14:46:19Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155145","name":"GlobalSign certs","why":"."},"issuerName":"MFwxCzAJBgNVBAYTAkJFMRUwEwYDVQQLEwxUcnVzdGVkIFJvb3QxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExGzAZBgNVBAMTElRydXN0ZWQgUm9vdCBDQSBHMg==","id":"9e8ec7bc-0f79-42c2-c9bc-32bfbbd3b591","schema":1480349159276},{"serialNumber":"EA==","enabled":true,"last_modified":1480349165523,"details":{"who":".","created":"2016-07-28T12:16:27Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1289808","name":"FNMT revoked intermediate certificates","why":"."},"issuerName":"MDsxCzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTQ==","id":"362a0532-ea75-9bc6-2e50-35d9566a6ad2","schema":1480349159276},{"serialNumber":"UU3AP1SMxmyhBFq7MRFZmf0=","enabled":true,"last_modified":1480349165501,"details":{"who":".","created":"2016-01-18T14:45:06Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155145","name":"GlobalSign certs","why":"."},"issuerName":"MFwxCzAJBgNVBAYTAkJFMRUwEwYDVQQLEwxUcnVzdGVkIFJvb3QxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExGzAZBgNVBAMTElRydXN0ZWQgUm9vdCBDQSBHMg==","id":"4f2e59ff-cdf1-48ee-1122-961833187e49","schema":1480349159276},{"serialNumber":"ESBqoILo90ntDW7OTK43MS2F","enabled":true,"last_modified":1480349165479,"details":{"who":".","created":"2016-10-27T17:52:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xhc3MgMiBQcmltYXJ5IENB","id":"0702b706-86e5-6a48-49fa-6c53b99009f3","schema":1480349159276},{"serialNumber":"GtXUVojhwOTkaQ4bTKblEQ==","enabled":true,"last_modified":1480349165453,"details":{"who":".","created":"2016-07-14T14:40:23Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1286752","name":"Symantec erroneous SHA-1 certificates","why":"."},"issuerName":"MIG8MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTEwMTYwNAYDVQQDEy1WZXJpU2lnbiBDbGFzcyAzIEludGVybmF0aW9uYWwgU2VydmVyIENBIC0gRzM=","id":"9475e2f6-7247-cbe1-5055-8af86f39a149","schema":1480349159276},{"serialNumber":"KjoVfZ3by6+pL8fssyfM6A==","enabled":true,"last_modified":1480349165429,"details":{"who":".","created":"2016-04-28T21:08:06Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MGYxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQLExREb21haW4gVmFsaWRhdGVkIFNTTDEgMB4GA1UEAxMXR2VvVHJ1c3QgRFYgU1NMIENBIC0gRzM=","id":"f4c8162a-d49b-1cbd-adb9-5e6223793aa4","schema":1480349159276},{"serialNumber":"Bydr0Q==","enabled":true,"last_modified":1480349165407,"details":{"who":".","created":"2016-10-27T17:52:11Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=","id":"a85aef34-3bfe-2135-845d-466adadc414b","schema":1480349159276},{"serialNumber":"Xbevr3ut3Z9m1GuXC9SonA==","enabled":true,"last_modified":1480349165384,"details":{"who":".","created":"2016-10-27T17:52:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MIG9MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5","id":"549710cf-bcaa-843c-df9d-5962bad88a9a","schema":1480349159276},{"serialNumber":"CeagHQ==","enabled":true,"last_modified":1480349165355,"details":{"who":".","created":"2016-10-27T17:52:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=","id":"d69db231-b7b5-4d79-147b-49198f93fc10","schema":1480349159276},{"serialNumber":"EAdmaA==","enabled":true,"last_modified":1480349165329,"details":{"who":".","created":"2016-10-27T17:52:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=","id":"618009ee-0ef1-af4b-8841-349e6f82eacc","schema":1480349159276},{"serialNumber":"BAAAAAABHkSl7L4=","enabled":true,"last_modified":1480349165306,"details":{"who":".","created":"2016-01-18T14:47:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155145","name":"GlobalSign certs","why":"."},"issuerName":"MIGBMQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTElMCMGA1UECxMcUHJpbWFyeSBPYmplY3QgUHVibGlzaGluZyBDQTEwMC4GA1UEAxMnR2xvYmFsU2lnbiBQcmltYXJ5IE9iamVjdCBQdWJsaXNoaW5nIENB","id":"636c65b9-2d52-8689-023f-7a23a0baec5b","schema":1480349159276},{"serialNumber":"BAAAAAABFqoAZoI=","enabled":true,"last_modified":1480349165285,"details":{"who":".","created":"2016-10-27T17:52:11Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==","id":"2b0d58aa-9c96-748f-4fc0-b1f413ca8e20","schema":1480349159276},{"serialNumber":"fwAAAQAAAUrz/HmrAAAAAg==","enabled":true,"last_modified":1480349165264,"details":{"who":".","created":"2016-02-24T20:04:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1250907","name":"IdenTrust cross certificate","why":"."},"issuerName":"MGExCzAJBgNVBAYTAlVTMRIwEAYDVQQKEwlJZGVuVHJ1c3QxIDAeBgNVBAsTF0lkZW5UcnVzdCBQdWJsaWMgU2VjdG9yMRwwGgYDVQQDExNJZGVuVHJ1c3QgQUNFUyBDQSAx","id":"352c78aa-997c-bdbe-66ba-930d66fde011","schema":1480349159276},{"serialNumber":"CeFU2w==","enabled":true,"last_modified":1480349165242,"details":{"who":".","created":"2016-10-27T17:52:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=","id":"e8e298f0-efa2-0d08-458f-c085ee9df8f9","schema":1480349159276},{"serialNumber":"STMAjg==","enabled":true,"last_modified":1480349165209,"details":{"who":".","created":"2015-03-31T14:53:16Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1149603","name":"MCSHOLDING intermediate certificate","why":"."},"issuerName":"MDIxCzAJBgNVBAYTAkNOMQ4wDAYDVQQKEwVDTk5JQzETMBEGA1UEAxMKQ05OSUMgUk9PVA==","id":"c9897d2c-c68e-3c02-2f39-678954b0cf3e","schema":1480349159276},{"serialNumber":"bAOrKSMsmA0MLJyAJ5BRsUM=","enabled":true,"last_modified":1480349165175,"details":{"who":".","created":"2016-01-18T14:46:35Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155145","name":"GlobalSign certs","why":"."},"issuerName":"MFwxCzAJBgNVBAYTAkJFMRUwEwYDVQQLEwxUcnVzdGVkIFJvb3QxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExGzAZBgNVBAMTElRydXN0ZWQgUm9vdCBDQSBHMg==","id":"7d44cb3e-28a5-16dd-024c-796312f780bc","schema":1480349159276},{"serialNumber":"F5BhE0zbgQ==","enabled":true,"last_modified":1480349165145,"details":{"who":".","created":"2016-10-27T17:52:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=","id":"29925947-91ab-16a8-a5af-65558cdb27c2","schema":1480349159276},{"serialNumber":"BAAAAAABL07hUBg=","enabled":true,"last_modified":1480349165119,"details":{"who":".","created":"2016-10-27T17:52:11Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFYxCzAJBgNVBAYTAkpQMQ8wDQYDVQQKEwZKSVBERUMxGjAYBgNVBAsTEUpDQU4gU3ViIFJvb3QgQ0EwMRowGAYDVQQDExFKQ0FOIFN1YiBSb290IENBMA==","id":"1240480e-2ef8-8ac3-4314-4e8e494741b9","schema":1480349159276},{"serialNumber":"ESCyHU+xOECnh9Rf2IvgR8zS","enabled":true,"last_modified":1480349165097,"details":{"who":".","created":"2016-10-27T17:52:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xhc3MgMiBQcmltYXJ5IENB","id":"3980401b-c0e2-0533-f0fb-0cc04685d248","schema":1480349159276},{"serialNumber":"CdWFNw==","enabled":true,"last_modified":1480349165075,"details":{"who":".","created":"2016-10-27T17:52:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=","id":"f06ff510-954e-b917-fda1-2c3153788d7d","schema":1480349159276},{"serialNumber":"TrKEMhb2PKktH8lHg0AV5A==","enabled":true,"last_modified":1480349165048,"details":{"who":".","created":"2016-10-27T17:52:11Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==","id":"8b7985ab-ab8b-fcd2-cf88-cf8dad0f7a97","schema":1480349159276},{"serialNumber":"BAAAAAABJQdAjik=","enabled":true,"last_modified":1480349165026,"details":{"who":".","created":"2016-01-18T14:41:56Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155145","name":"GlobalSign certs","why":"."},"issuerName":"MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==","id":"a75f5980-9149-fff9-70d5-b24121c3eaff","schema":1480349159276},{"serialNumber":"Cfk9lw==","enabled":true,"last_modified":1480349165002,"details":{"who":".","created":"2016-10-27T17:52:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=","id":"59b587cb-401b-a5d0-8128-86c3691c4be1","schema":1480349159276},{"serialNumber":"BAAAAAABHkSl6mw=","enabled":true,"last_modified":1480349164725,"details":{"who":".","created":"2016-10-27T17:52:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MG0xCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRswGQYDVQQLExJQcmltYXJ5IENsYXNzIDMgQ0ExJjAkBgNVBAMTHUdsb2JhbFNpZ24gUHJpbWFyeSBDbGFzcyAzIENB","id":"3cc60c06-a870-951e-1d12-4b29ee13989e","schema":1480349159276},{"serialNumber":"SdegFrLaFTCsoMAW5ED+zA==","enabled":true,"last_modified":1480349164704,"details":{"who":".","created":"2016-04-28T21:08:07Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MEQxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQDExRHZW9UcnVzdCBTU0wgQ0EgLSBHMg==","id":"9fbfe267-c715-8f7b-d9ad-166aad9f91af","schema":1480349159276},{"serialNumber":"BAAAAAABMxvC9bk=","enabled":true,"last_modified":1480349164681,"details":{"who":".","created":"2016-01-18T14:42:11Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155145","name":"GlobalSign certs","why":"."},"issuerName":"MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==","id":"923a5e98-11f7-cdae-b073-45b525fb2294","schema":1480349159276},{"serialNumber":"Bydp0g==","enabled":true,"last_modified":1480349164658,"details":{"who":".","created":"2016-10-27T17:52:11Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=","id":"10e51569-072a-611a-c397-3050fdf22649","schema":1480349159276},{"serialNumber":"QZCrvQ==","enabled":true,"last_modified":1480349164636,"details":{"who":".","created":"2016-10-27T17:52:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MIGCMQswCQYDVQQGEwJVUzEeMBwGA1UECxMVd3d3LnhyYW1wc2VjdXJpdHkuY29tMSQwIgYDVQQKExtYUmFtcCBTZWN1cml0eSBTZXJ2aWNlcyBJbmMxLTArBgNVBAMTJFhSYW1wIEdsb2JhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==","id":"6d791114-68b4-8c3c-ee3c-29ed83eced2e","schema":1480349159276},{"serialNumber":"AQAAAAI=","enabled":true,"last_modified":1480349164614,"details":{"who":".","created":"2016-10-27T17:52:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MIGVMQswCQYDVQQGEwJHUjFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTE=","id":"bff9c953-6690-f618-cfea-7b936f3691a6","schema":1480349159276},{"serialNumber":"Aw1SPC56593ZCZ9vCNHKwQ==","enabled":true,"last_modified":1480349164590,"details":{"who":".","created":"2016-10-27T17:52:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MIG9MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5","id":"c395381f-fe34-7b6c-f56a-f20b20bf0d0d","schema":1480349159276},{"serialNumber":"BAAAAAABAJmPjfQ=","enabled":true,"last_modified":1480349164559,"details":{"who":".","created":"2016-01-18T14:44:22Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155145","name":"GlobalSign certs","why":"."},"issuerName":"MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==","id":"24b096b4-987f-a21a-04d3-aedc9eaafc1e","schema":1480349159276},{"serialNumber":"BAAAAAABK84yjs8=","enabled":true,"last_modified":1480349164533,"details":{"who":".","created":"2016-10-27T17:52:11Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFYxCzAJBgNVBAYTAkpQMQ8wDQYDVQQKEwZKSVBERUMxGjAYBgNVBAsTEUpDQU4gU3ViIFJvb3QgQ0EwMRowGAYDVQQDExFKQ0FOIFN1YiBSb290IENBMA==","id":"8078d5ff-c93b-15d1-ebcf-607bdbfc159f","schema":1480349159276},{"serialNumber":"FJl6tXgNpSk=","enabled":true,"last_modified":1480349164510,"details":{"who":".","created":"2016-10-27T17:52:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MIGVMQswCQYDVQQGEwJHUjFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTE=","id":"7ab0a200-7ecf-576f-bff9-652fb14c3af6","schema":1480349159276},{"serialNumber":"M0VSOewW3WI=","enabled":true,"last_modified":1480349164486,"details":{"who":".","created":"2016-10-27T17:52:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MDwxHjAcBgNVBAMMFUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsGA1UECgwEQXRvczELMAkGA1UEBhMCREU=","id":"f3688c95-3934-e80a-e32f-0d5dcb2f0c4c","schema":1480349159276},{"serialNumber":"VN2yeFexyXjPf34fHGmbhg==","enabled":true,"last_modified":1480349164463,"details":{"who":".","created":"2016-07-14T14:40:23Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1286752","name":"Symantec erroneous SHA-1 certificates","why":"."},"issuerName":"MIG8MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTEwMTYwNAYDVQQDEy1WZXJpU2lnbiBDbGFzcyAzIEludGVybmF0aW9uYWwgU2VydmVyIENBIC0gRzM=","id":"bbcfc451-2fcc-b380-e579-bb6d11fc7d34","schema":1480349159276},{"serialNumber":"STMAeg==","enabled":true,"last_modified":1480349164433,"details":{"who":".","created":"2016-10-27T17:52:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MDIxCzAJBgNVBAYTAkNOMQ4wDAYDVQQKEwVDTk5JQzETMBEGA1UEAxMKQ05OSUMgUk9PVA==","id":"a46be506-1dbc-41a9-2775-95d67708fb5f","schema":1480349159276},{"serialNumber":"Aa8e+91erglSMgsk/mtVaA==","enabled":true,"last_modified":1480349164410,"details":{"who":".","created":"2016-09-09T16:26:08Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1300977","name":"revoked.badssl.com certificate","why":"."},"issuerName":"ME0xCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxJzAlBgNVBAMTHkRpZ2lDZXJ0IFNIQTIgU2VjdXJlIFNlcnZlciBDQQ==","id":"4b778ec2-ef45-c5b2-dc44-b00c87b11741","schema":1480349159276},{"serialNumber":"TurPPI6eivtNeGYdM0ZWXQ==","enabled":true,"last_modified":1480349164388,"details":{"who":".","created":"2016-04-28T21:08:07Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MH4xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEvMC0GA1UEAxMmU3ltYW50ZWMgQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzQ=","id":"8192a2fa-165d-3759-fd20-4b7d8e2a0e84","schema":1480349159276},{"serialNumber":"UKKK5ol/rKBZchAAOnZjaA==","enabled":true,"last_modified":1480349164365,"details":{"who":".","created":"2016-04-28T21:08:08Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MEMxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwx0aGF3dGUsIEluYy4xHTAbBgNVBAMTFHRoYXd0ZSBTSEEyNTYgU1NMIENB","id":"ca0c5f15-2808-8c94-3f77-6a277d6738b2","schema":1480349159276},{"serialNumber":"UMUwXwT1Z4juyQ/CNTf4mw==","enabled":true,"last_modified":1480349164342,"details":{"who":".","created":"2016-04-28T21:08:07Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MH4xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEvMC0GA1UEAxMmU3ltYW50ZWMgQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzQ=","id":"c623e511-79c8-cbe6-6a6e-0d9896f07e71","schema":1480349159276},{"serialNumber":"Ew1ee9Jq7Q/Dig3ACF4V6Q==","enabled":true,"last_modified":1480349164320,"details":{"who":".","created":"2016-10-27T17:52:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MIGTMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTEbMBkGA1UEAxMSVVROIC0gREFUQUNvcnAgU0dD","id":"c011d2b4-73bc-27e5-3d3a-ab00be2a4d05","schema":1480349159276},{"serialNumber":"XLhHIg7vP+tWfRqvuKeAxw==","enabled":true,"last_modified":1480349164297,"details":{"who":".","created":"2016-04-28T21:08:06Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MGYxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQLExREb21haW4gVmFsaWRhdGVkIFNTTDEgMB4GA1UEAxMXR2VvVHJ1c3QgRFYgU1NMIENBIC0gRzM=","id":"520c26f8-9a60-5949-0372-c9d93f9e95dd","schema":1480349159276},{"serialNumber":"E5I2y6sIonl4a+TmlXc7fw==","enabled":true,"last_modified":1480349164275,"details":{"who":".","created":"2016-04-28T21:08:08Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MGMxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwx0aGF3dGUsIEluYy4xHTAbBgNVBAsTFERvbWFpbiBWYWxpZGF0ZWQgU1NMMR4wHAYDVQQDExV0aGF3dGUgRFYgU1NMIENBIC0gRzI=","id":"ee842f50-9d56-8fdf-fa55-8b55b8519b81","schema":1480349159276},{"serialNumber":"Jq6jgeApiT9O4W2Tx/NTRQ==","enabled":true,"last_modified":1480349164253,"details":{"who":".","created":"2016-10-27T17:52:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MIGVMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTEdMBsGA1UEAxMUVVROLVVTRVJGaXJzdC1PYmplY3Q=","id":"3b658f17-11f9-7550-d991-3e9a1397402d","schema":1480349159276},{"serialNumber":"BAAAAAABHkSl6Co=","enabled":true,"last_modified":1480349164232,"details":{"who":".","created":"2016-10-27T17:52:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MG0xCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRswGQYDVQQLExJQcmltYXJ5IENsYXNzIDIgQ0ExJjAkBgNVBAMTHUdsb2JhbFNpZ24gUHJpbWFyeSBDbGFzcyAyIENB","id":"6eb0fa3b-c226-f411-0fab-df88962a5769","schema":1480349159276},{"serialNumber":"ESD9YhzIEOwiOT7Nwip+E1KI","enabled":true,"last_modified":1480349164209,"details":{"who":".","created":"2016-10-27T17:52:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xhc3MgMiBQcmltYXJ5IENB","id":"d6172148-c2ee-a904-db40-079b10436cca","schema":1480349159276},{"serialNumber":"Eg==","enabled":true,"last_modified":1480349164187,"details":{"who":".","created":"2016-07-28T12:17:24Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1289808","name":"FNMT revoked intermediate certificates","why":"."},"issuerName":"MDsxCzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTQ==","id":"34561e12-916b-083e-6fa6-181b5b89ec80","schema":1480349159276},{"serialNumber":"NvEJoRYL2yvAZrAjbDIipQ==","enabled":true,"last_modified":1480349164164,"details":{"who":".","created":"2016-04-28T21:08:08Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MIG1MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTEwMS8wLQYDVQQDEyZWZXJpU2lnbiBDbGFzcyAzIFNlY3VyZSBTZXJ2ZXIgQ0EgLSBHMw==","id":"b1119d43-b3b8-1f41-6fbb-9cf2f67a521d","schema":1480349159276},{"serialNumber":"ESCC9oPNcRdPOox+SjWm9dTX","enabled":true,"last_modified":1480349163834,"details":{"who":".","created":"2016-10-27T17:52:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xhc3MgMiBQcmltYXJ5IENB","id":"8858e9fc-cc55-54ea-fe45-c4e533c2e410","schema":1480349159276},{"serialNumber":"D9UltDPl4XVfSSqQOvdiwQ==","enabled":true,"last_modified":1480349163810,"details":{"who":".","created":"2015-03-31T11:14:46Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1145157","name":"live.fi certificate","why":"."},"issuerName":"MIGQMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01PRE8gQ0EgTGltaXRlZDE2MDQGA1UEAxMtQ09NT0RPIFJTQSBEb21haW4gVmFsaWRhdGlvbiBTZWN1cmUgU2VydmVyIENB","id":"0a9323dd-982f-c62d-a9b4-b9d04617fc62","schema":1480349159276},{"serialNumber":"OYBKgxEHpW/8XGAGAlvJyMA=","enabled":true,"last_modified":1480349163788,"details":{"who":".","created":"2016-01-18T14:45:35Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155145","name":"GlobalSign certs","why":"."},"issuerName":"MFwxCzAJBgNVBAYTAkJFMRUwEwYDVQQLEwxUcnVzdGVkIFJvb3QxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExGzAZBgNVBAMTElRydXN0ZWQgUm9vdCBDQSBHMg==","id":"d6dfdc76-52ae-2843-3484-7fbff46f0100","schema":1480349159276},{"serialNumber":"EM8bDLBnnoYe4LnWpLIhS4esr3I=","enabled":true,"last_modified":1480349163764,"details":{"who":".","created":"2016-10-27T17:52:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDI=","id":"be09b295-68d1-9c01-97ee-10df36acd3ea","schema":1480349159276},{"serialNumber":"BAAAAAABJ/ufRdg=","enabled":true,"last_modified":1480349163739,"details":{"who":".","created":"2016-01-18T14:39:41Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155145","name":"GlobalSign certs","why":"."},"issuerName":"MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==","id":"5d64f82e-9ad8-fde1-a8c9-2d94552a8ad4","schema":1480349159276},{"serialNumber":"BAAAAAABHJRKMpA=","enabled":true,"last_modified":1480349163715,"details":{"who":".","created":"2016-01-18T14:43:51Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155145","name":"GlobalSign certs","why":"."},"issuerName":"MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==","id":"f5b2da3a-5176-b4e4-240a-181f39f6756b","schema":1480349159276},{"serialNumber":"BAAAAAABBHYoIFs=","enabled":true,"last_modified":1480349163691,"details":{"who":".","created":"2016-01-18T14:40:18Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155145","name":"GlobalSign certs","why":"."},"issuerName":"MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==","id":"f59ed73e-f3c5-eef3-4481-3ca8af0b0688","schema":1480349159276},{"serialNumber":"BAAAAAABHhw1vwc=","enabled":true,"last_modified":1480349163668,"details":{"who":".","created":"2016-01-18T14:37:41Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155145","name":"GlobalSign certs","why":"."},"issuerName":"MF8xCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRQwEgYDVQQLEwtQYXJ0bmVycyBDQTEfMB0GA1UEAxMWR2xvYmFsU2lnbiBQYXJ0bmVycyBDQQ==","id":"1b1856c1-f4f5-82ca-ba57-d94739e74576","schema":1480349159276},{"serialNumber":"TqfXw+FkhxfVgE9GVMgjWQ==","enabled":true,"last_modified":1480349163645,"details":{"who":".","created":"2016-04-28T21:08:08Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MGMxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwx0aGF3dGUsIEluYy4xHTAbBgNVBAsTFERvbWFpbiBWYWxpZGF0ZWQgU1NMMR4wHAYDVQQDExV0aGF3dGUgRFYgU1NMIENBIC0gRzI=","id":"60f6a3be-ad83-a868-d645-7aad77914bc8","schema":1480349159276},{"serialNumber":"LdbnCbsA9sOgI4mkUpWXPw==","enabled":true,"last_modified":1480349163622,"details":{"who":".","created":"2016-03-01T21:21:56Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1252142","name":"exceptional SHA-1 Certificates","why":"."},"issuerName":"MIG8MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTEwMTYwNAYDVQQDEy1WZXJpU2lnbiBDbGFzcyAzIEludGVybmF0aW9uYWwgU2VydmVyIENBIC0gRzM=","id":"d839b1ed-7d39-5e57-687c-2f4d6f0514e5","schema":1480349159276},{"serialNumber":"ANygrItIJ2rcKlyS3Lue07U=","enabled":true,"last_modified":1480349163595,"details":{"who":".","created":"2016-10-27T17:52:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MEgxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdTZWN1cmVUcnVzdCBDb3Jwb3JhdGlvbjEXMBUGA1UEAxMOU2VjdXJlVHJ1c3QgQ0E=","id":"d55572d9-be60-5967-948a-7dae793ab30f","schema":1480349159276},{"serialNumber":"BydeGg==","enabled":true,"last_modified":1480349163572,"details":{"who":".","created":"2016-10-27T17:52:11Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=","id":"b7d8b0e0-9747-6a09-ab6b-051c57579fe9","schema":1480349159276},{"serialNumber":"ESCVop+Q4/OBgtf4WJkr01Gh","enabled":true,"last_modified":1480349163548,"details":{"who":".","created":"2016-10-27T17:52:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xhc3MgMiBQcmltYXJ5IENB","id":"7602529c-c2ea-d18b-9b2a-fdb70ca936f9","schema":1480349159276},{"serialNumber":"BAAAAAABGMG0Gmw=","enabled":true,"last_modified":1480349163514,"details":{"who":".","created":"2016-01-18T14:42:31Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155145","name":"GlobalSign certs","why":"."},"issuerName":"MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==","id":"317aeda4-6de7-7b11-76cb-3b0afa9aaf86","schema":1480349159276},{"serialNumber":"O2S99lVUxErLSk56GvWRv+E=","enabled":true,"last_modified":1480349163492,"details":{"who":".","created":"2016-01-18T14:47:04Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155145","name":"GlobalSign certs","why":"."},"issuerName":"MFwxCzAJBgNVBAYTAkJFMRUwEwYDVQQLEwxUcnVzdGVkIFJvb3QxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExGzAZBgNVBAMTElRydXN0ZWQgUm9vdCBDQSBHMg==","id":"27847bcc-dbaf-196f-ed5e-c1c022798717","schema":1480349159276},{"serialNumber":"By7fBTreouRwX/qrpgSUsg==","enabled":true,"last_modified":1480349163470,"details":{"who":".","created":"2016-03-01T21:16:35Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1252142","name":"exceptional SHA-1 Certificates","why":"."},"issuerName":"MIG8MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTEwMTYwNAYDVQQDEy1WZXJpU2lnbiBDbGFzcyAzIEludGVybmF0aW9uYWwgU2VydmVyIENBIC0gRzM=","id":"322de470-76d3-a45d-740f-342a6a8eb863","schema":1480349159276},{"serialNumber":"ezdAeCxKH7BFs7vn3byYaw==","enabled":true,"last_modified":1480349163448,"details":{"who":".","created":"2016-04-28T21:08:07Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MH4xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEvMC0GA1UEAxMmU3ltYW50ZWMgQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzQ=","id":"fc5bde6b-45b9-c141-7171-0a6f37e7938a","schema":1480349159276},{"serialNumber":"VLm3Xe60+1YgPpXCGtXLng==","enabled":true,"last_modified":1480349163426,"details":{"who":".","created":"2016-10-27T17:52:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MIG9MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5","id":"055d66b6-8fbd-93df-f2c4-dcdb41943212","schema":1480349159276},{"serialNumber":"cDggUYfwJ3A1YcdoeT6s4A==","enabled":true,"last_modified":1480349163403,"details":{"who":".","created":"2016-04-28T21:08:08Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MEExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxUaGF3dGUsIEluYy4xGzAZBgNVBAMTElRoYXd0ZSBTR0MgQ0EgLSBHMg==","id":"093f20b4-93b8-a171-cbe7-3e24a543c7e9","schema":1480349159276},{"serialNumber":"a9/VeyVWrzFD7rM2PEHwQA==","enabled":true,"last_modified":1480349163377,"details":{"who":".","created":"2016-04-28T21:08:07Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MH4xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEvMC0GA1UEAxMmU3ltYW50ZWMgQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzQ=","id":"c02a9772-b351-59dc-8633-1293ac9addee","schema":1480349159276},{"serialNumber":"ESCis569omrbb20yySF39+aE","enabled":true,"last_modified":1480349163350,"details":{"who":".","created":"2016-10-27T17:52:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xhc3MgMiBQcmltYXJ5IENB","id":"aaa19866-32ce-e842-6431-6d357fafe8d8","schema":1480349159276},{"serialNumber":"F5Bg6C237Q==","enabled":true,"last_modified":1480349163327,"details":{"who":".","created":"2016-10-27T17:52:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=","id":"57ddfa31-3f08-298a-d7bd-712e3aaea567","schema":1480349159276},{"serialNumber":"HZyLf+K70FKc+jomm8DiDw==","enabled":true,"last_modified":1480349163302,"details":{"who":".","created":"2016-04-28T21:08:08Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MH4xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEvMC0GA1UEAxMmU3ltYW50ZWMgQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzQ=","id":"452f4798-87d4-8df1-b275-177456d2f1c8","schema":1480349159276},{"serialNumber":"CskruA==","enabled":true,"last_modified":1480349163280,"details":{"who":".","created":"2016-10-27T17:52:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=","id":"e92cad12-4098-0817-e317-3674d4242ba9","schema":1480349159276},{"serialNumber":"a12RvBNhznU=","enabled":true,"last_modified":1480349163257,"details":{"who":".","created":"2016-10-27T17:52:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MDwxHjAcBgNVBAMMFUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsGA1UECgwEQXRvczELMAkGA1UEBhMCREU=","id":"33154d98-0f20-7e7d-d2d0-3244a7d1f971","schema":1480349159276},{"serialNumber":"CjM=","enabled":true,"last_modified":1480349162955,"details":{"who":".","created":"2016-10-27T17:52:11Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDM=","id":"283b292b-1212-a713-c8be-c976c0222410","schema":1480349159276},{"serialNumber":"NTgf4iwIfeyJPIomw2dwSXEwtxQ=","enabled":true,"last_modified":1480349162930,"details":{"who":".","created":"2016-10-27T17:52:11Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDM=","id":"542dbfc0-0faa-2398-9670-cd249525fd2c","schema":1480349159276},{"serialNumber":"Cd/dug==","enabled":true,"last_modified":1480349162902,"details":{"who":".","created":"2016-10-27T17:52:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=","id":"3cf54b7b-336d-7c80-4596-d5a7762329d9","schema":1480349159276},{"serialNumber":"M64Z5ufZzDRVTHkJR1uXzw==","enabled":true,"last_modified":1480349162880,"details":{"who":".","created":"2016-10-27T17:52:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlBUyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRyZSBSb290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWU=","id":"5d6d86a0-7e3e-b832-83e5-afc1eb2e294f","schema":1480349159276},{"serialNumber":"BAAAAAABIBnBjWg=","enabled":true,"last_modified":1480349162857,"details":{"who":".","created":"2016-10-27T17:52:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxTaWduIFJvb3QgQ0E=","id":"0713fba9-386c-888f-cbe5-5188ff2696a4","schema":1480349159276},{"serialNumber":"fWK0j/Vi8vNWg3VAGjc02w==","enabled":true,"last_modified":1480349162835,"details":{"who":".","created":"2016-07-14T14:40:23Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1286752","name":"Symantec erroneous SHA-1 certificates","why":"."},"issuerName":"MIG8MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTEwMTYwNAYDVQQDEy1WZXJpU2lnbiBDbGFzcyAzIEludGVybmF0aW9uYWwgU2VydmVyIENBIC0gRzM=","id":"a4428979-4be6-3ba2-f435-aa8e4574ffe0","schema":1480349159276},{"serialNumber":"JpUvYJyWjdGmeoH7YcYunw==","enabled":true,"last_modified":1480349162803,"details":{"who":".","created":"2016-04-28T21:08:08Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MEExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwx0aGF3dGUsIEluYy4xGzAZBgNVBAMTEnRoYXd0ZSBTU0wgQ0EgLSBHMg==","id":"929de7d6-0669-5601-0b8f-9a192bf1cb17","schema":1480349159276},{"serialNumber":"45KI4WIxyXfNrdtdj7C6","enabled":true,"last_modified":1480349162781,"details":{"who":".","created":"2016-04-28T21:08:07Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MH4xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEvMC0GA1UEAxMmU3ltYW50ZWMgQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzQ=","id":"7cb25ddb-9f7e-7297-e8b4-c50d019f0cf7","schema":1480349159276},{"serialNumber":"BQ==","enabled":true,"last_modified":1480349162758,"details":{"who":".","created":"2016-07-28T12:15:22Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1289808","name":"FNMT revoked intermediate certificates","why":"."},"issuerName":"MDsxCzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTQ==","id":"7c28e9e7-b9ce-f4ed-8d5a-e7b9e534fba5","schema":1480349159276},{"serialNumber":"F7PAjw2k0dTX5escPnyVOBo=","enabled":true,"last_modified":1480349162734,"details":{"who":".","created":"2016-01-18T14:45:19Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155145","name":"GlobalSign certs","why":"."},"issuerName":"MFwxCzAJBgNVBAYTAkJFMRUwEwYDVQQLEwxUcnVzdGVkIFJvb3QxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExGzAZBgNVBAMTElRydXN0ZWQgUm9vdCBDQSBHMg==","id":"3c7e3e8e-5c25-8fbf-0006-2c92256e0a4b","schema":1480349159276},{"serialNumber":"Sx51x7V8pYe8rp7PMP/3qg==","enabled":true,"last_modified":1480349162710,"details":{"who":".","created":"2016-04-28T21:08:07Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MH4xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEvMC0GA1UEAxMmU3ltYW50ZWMgQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzQ=","id":"4006917e-3260-59cd-eb3f-f4d1167cf888","schema":1480349159276},{"serialNumber":"DHmmaw==","enabled":true,"last_modified":1480349162686,"details":{"who":".","created":"2016-10-27T17:52:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=","id":"0abfb8ad-d1b7-5f0e-cbea-630f2424171d","schema":1480349159276},{"serialNumber":"CcHC/g==","enabled":true,"last_modified":1480349162661,"details":{"who":".","created":"2016-10-27T17:52:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=","id":"cba8acd8-a291-1ad6-9581-ed647dd5d56d","schema":1480349159276},{"serialNumber":"ATE0vw==","enabled":true,"last_modified":1480349162632,"details":{"who":".","created":"2015-05-08T10:53:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155114","name":"Intermediate CA's under Staat der Nederlanden Root CA","why":"."},"issuerName":"MGExCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xMjAwBgNVBAMMKVN0YWF0IGRlciBOZWRlcmxhbmRlbiBPcmdhbmlzYXRpZSBDQSAtIEcy","id":"fc71cbb8-4e2a-2835-d5be-4c48cd3650bb","schema":1480349159276},{"serialNumber":"BAAAAAABLF5/HXY=","enabled":true,"last_modified":1480349162610,"details":{"who":".","created":"2016-10-27T17:52:11Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==","id":"ca59600c-e90a-e85a-43b8-22bc76bb0e1f","schema":1480349159276},{"serialNumber":"BXA=","enabled":true,"last_modified":1480349162580,"details":{"who":".","created":"2016-10-27T17:52:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDI=","id":"a44b5bf8-8158-1924-7626-e1ba2d2031f7","schema":1480349159276},{"serialNumber":"VOcIuNbTqkpOMUyI108FOg==","enabled":true,"last_modified":1480349162557,"details":{"who":".","created":"2016-03-01T21:24:01Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1252142","name":"exceptional SHA-1 Certificates","why":"."},"issuerName":"MIG8MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTEwMTYwNAYDVQQDEy1WZXJpU2lnbiBDbGFzcyAzIEludGVybmF0aW9uYWwgU2VydmVyIENBIC0gRzM=","id":"43e72628-c1a5-2091-2dcd-41bbde768c73","schema":1480349159276},{"serialNumber":"ATFpsA==","enabled":true,"last_modified":1480349162533,"details":{"who":".","created":"2015-05-08T10:53:40Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155114","name":"Intermediate CA's under Staat der Nederlanden Root CA","why":"."},"issuerName":"MFkxCzAJBgNVBAYTAk5MMR4wHAYDVQQKExVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xKjAoBgNVBAMTIVN0YWF0IGRlciBOZWRlcmxhbmRlbiBPdmVyaGVpZCBDQQ==","id":"325bb598-839b-f7aa-8a22-08ab8c09e803","schema":1480349159276},{"serialNumber":"UV9aaDeNRNtQuXjRYk4Skhg=","enabled":true,"last_modified":1480349162506,"details":{"who":".","created":"2016-01-18T14:45:50Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155145","name":"GlobalSign certs","why":"."},"issuerName":"MFwxCzAJBgNVBAYTAkJFMRUwEwYDVQQLEwxUcnVzdGVkIFJvb3QxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExGzAZBgNVBAMTElRydXN0ZWQgUm9vdCBDQSBHMg==","id":"c11dd0af-83d5-61ec-63af-5b816a3ae557","schema":1480349159276},{"serialNumber":"bzTw0uq05TUYEGS98bh0Ww==","enabled":true,"last_modified":1480349162483,"details":{"who":".","created":"2016-07-14T14:40:23Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1286752","name":"Symantec erroneous SHA-1 certificates","why":"."},"issuerName":"MIG8MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTEwMTYwNAYDVQQDEy1WZXJpU2lnbiBDbGFzcyAzIEludGVybmF0aW9uYWwgU2VydmVyIENBIC0gRzM=","id":"6b743fc3-1ce6-8dfe-52f1-9f3e6a31f15a","schema":1480349159276},{"serialNumber":"Gd/pPu+qLnXUdvP9sW73CQ==","enabled":true,"last_modified":1480349162460,"details":{"who":".","created":"2016-03-01T21:21:30Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1252142","name":"exceptional SHA-1 Certificates","why":"."},"issuerName":"MIG8MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTEwMTYwNAYDVQQDEy1WZXJpU2lnbiBDbGFzcyAzIEludGVybmF0aW9uYWwgU2VydmVyIENBIC0gRzM=","id":"2da11236-c2d8-7804-7de3-ffd711406b04","schema":1480349159276},{"serialNumber":"BAAAAAABJ/v3ZwA=","enabled":true,"last_modified":1480349162438,"details":{"who":".","created":"2016-10-27T17:52:09Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MEwxIDAeBgNVBAsTF0dsb2JhbFNpZ24gUm9vdCBDQSAtIFIyMRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu","id":"9f6615be-a0b9-519a-1e26-2bd7aaf5953b","schema":1480349159276},{"serialNumber":"BAAAAAABGMGjftY=","enabled":true,"last_modified":1480349162415,"details":{"who":".","created":"2016-01-18T14:39:05Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155145","name":"GlobalSign certs","why":"."},"issuerName":"MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==","id":"5f1ab732-8d48-0102-fb50-46ce74fc1a90","schema":1480349159276},{"serialNumber":"Rvm2CEw2IC2Mu/ax0A46QQ==","enabled":true,"last_modified":1480349162392,"details":{"who":".","created":"2016-04-28T21:08:08Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MGMxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwx0aGF3dGUsIEluYy4xHTAbBgNVBAsTFERvbWFpbiBWYWxpZGF0ZWQgU1NMMR4wHAYDVQQDExV0aGF3dGUgRFYgU1NMIENBIC0gRzI=","id":"82f97501-85ba-bb19-f68f-ca7ce68ca7b3","schema":1480349159276},{"serialNumber":"BAAAAAABJpQ0AbA=","enabled":true,"last_modified":1480349162369,"details":{"who":".","created":"2016-10-27T17:52:14Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MDsxGDAWBgNVBAoTD0N5YmVydHJ1c3QsIEluYzEfMB0GA1UEAxMWQ3liZXJ0cnVzdCBHbG9iYWwgUm9vdA==","id":"520cee2c-e36a-936e-d551-a51ee9c659c8","schema":1480349159276},{"serialNumber":"ByfHkw==","enabled":true,"last_modified":1480349161997,"details":{"who":".","created":"2016-10-27T17:52:11Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=","id":"74d82589-0fde-91da-20cc-22fbd84cd0aa","schema":1480349159276},{"serialNumber":"ESC8DawWRiAyEMd38UXbfgPR","enabled":true,"last_modified":1480349161969,"details":{"who":".","created":"2016-10-27T17:52:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xhc3MgMiBQcmltYXJ5IENB","id":"6147b64f-da46-ba30-2b9e-30b515e8f6b9","schema":1480349159276},{"serialNumber":"BAAAAAABK84ykc0=","enabled":true,"last_modified":1480349161946,"details":{"who":".","created":"2016-10-27T17:52:11Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFYxCzAJBgNVBAYTAkpQMQ8wDQYDVQQKEwZKSVBERUMxGjAYBgNVBAsTEUpDQU4gU3ViIFJvb3QgQ0EwMRowGAYDVQQDExFKQ0FOIFN1YiBSb290IENBMA==","id":"0a42070f-3149-2b91-1bbe-c54de4ed1be8","schema":1480349159276},{"serialNumber":"GN2Hrh9LtnM=","enabled":true,"last_modified":1480349161925,"details":{"who":".","created":"2016-10-27T17:52:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MIGVMQswCQYDVQQGEwJHUjFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTE=","id":"dc28966b-d7db-b55f-1606-c5bff610bf99","schema":1480349159276},{"serialNumber":"FJl6tXgNpSg=","enabled":true,"last_modified":1480349161902,"details":{"who":".","created":"2016-10-27T17:52:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MIGVMQswCQYDVQQGEwJHUjFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTE=","id":"bab58b65-8ef7-f3c2-fc13-88f72a0840f7","schema":1480349159276},{"serialNumber":"ESDItX4ruWiLnrlz0rk4/bmz","enabled":true,"last_modified":1480349161872,"details":{"who":".","created":"2016-10-27T17:52:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xhc3MgMiBQcmltYXJ5IENB","id":"c12d5f3c-b722-948f-b367-857845ed5e8e","schema":1480349159276},{"serialNumber":"bx/XHJqcwxDOptxJ2lh5vw==","enabled":true,"last_modified":1480349161850,"details":{"who":".","created":"2016-04-28T21:08:07Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MEQxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQDExRHZW9UcnVzdCBTU0wgQ0EgLSBHMw==","id":"a0da0bd1-8bec-4c82-fb9b-636ddcde057d","schema":1480349159276},{"serialNumber":"Mq0P6o03FDk0B2bnJ+mYPGo=","enabled":true,"last_modified":1480349161828,"details":{"who":".","created":"2016-01-18T14:47:39Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155145","name":"GlobalSign certs","why":"."},"issuerName":"MFwxCzAJBgNVBAYTAkJFMRUwEwYDVQQLEwxUcnVzdGVkIFJvb3QxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExGzAZBgNVBAMTElRydXN0ZWQgUm9vdCBDQSBHMg==","id":"54f096a5-3608-9b68-ddca-a200e799ba06","schema":1480349159276},{"serialNumber":"BAAAAAABJ/ufQg8=","enabled":true,"last_modified":1480349161807,"details":{"who":".","created":"2016-01-18T14:43:00Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155145","name":"GlobalSign certs","why":"."},"issuerName":"MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==","id":"61db13cb-74d9-e675-a1e8-db5f78eb3f4e","schema":1480349159276},{"serialNumber":"BAAAAAABM6d3Z0s=","enabled":true,"last_modified":1480349161785,"details":{"who":".","created":"2016-10-27T17:52:11Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==","id":"b9c439ef-fcf3-2263-c14e-80727cbef898","schema":1480349159276},{"serialNumber":"FNISyWWTGi5Yco6fGh58/A==","enabled":true,"last_modified":1480349161763,"details":{"who":".","created":"2016-04-28T21:08:08Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MEExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwx0aGF3dGUsIEluYy4xGzAZBgNVBAMTEnRoYXd0ZSBTU0wgQ0EgLSBHMg==","id":"bab0ff94-c98e-7843-97b3-a4d1e044715b","schema":1480349159276},{"serialNumber":"VfTSum25nb65YPlpuhJAvg==","enabled":true,"last_modified":1480349161740,"details":{"who":".","created":"2016-04-28T21:08:07Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MEQxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQDExRHZW9UcnVzdCBTU0wgQ0EgLSBHMg==","id":"91149dc9-6cc4-a670-1799-7bd05c159858","schema":1480349159276},{"serialNumber":"OhrtngFwotLcm4i+z00SjA==","enabled":true,"last_modified":1480349161718,"details":{"who":".","created":"2016-04-28T21:08:07Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MH8xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEwMC4GA1UEAxMnU3ltYW50ZWMgQ2xhc3MgMyBFQ0MgMjU2IGJpdCBFViBDQSAtIEcy","id":"337e2f4b-02c4-c70d-55be-09c8ea41731c","schema":1480349159276},{"serialNumber":"AN9bfYOvlR1t","enabled":true,"last_modified":1480349161696,"details":{"who":".","created":"2015-07-13T09:05:40Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1181126","name":"RCS Certification Authority","why":"."},"issuerName":"MDcxJDAiBgNVBAMTG1JDUyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEPMA0GA1UEChMGSFQgc3Js","id":"18c0d37b-739d-04c6-04f2-a615be5b9948","schema":1480349159276},{"serialNumber":"F5BhENPfVw==","enabled":true,"last_modified":1480349161666,"details":{"who":".","created":"2016-10-27T17:52:11Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=","id":"72a65abf-9828-365f-4d4a-78457d7bdec4","schema":1480349159276},{"serialNumber":"ESISuBo/wdW2tBztKmHdFCFz","enabled":true,"last_modified":1480349161641,"details":{"who":".","created":"2016-10-27T17:52:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xhc3MgMiBQcmltYXJ5IENB","id":"2ad44cbf-32ca-5245-c881-6d6f290ac2c1","schema":1480349159276},{"serialNumber":"CrTHPEE6AZSfI3jysin2bA==","enabled":true,"last_modified":1480349161619,"details":{"who":".","created":"2015-09-21T13:21:25Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1205651","name":"Misused certificate","why":"."},"issuerName":"MEQxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwx0aGF3dGUsIEluYy4xHjAcBgNVBAMTFXRoYXd0ZSBFViBTU0wgQ0EgLSBHMw==","id":"13987e52-821e-1fe3-3743-393136b17167","schema":1480349159276},{"serialNumber":"WX89jn8yGZVvoKTD9jDfRQ==","enabled":true,"last_modified":1480349161596,"details":{"who":".","created":"2016-04-28T21:08:07Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MEQxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQDExRHZW9UcnVzdCBTU0wgQ0EgLSBHMg==","id":"a15c9fa5-cd6e-362d-1341-1e95c11c38fb","schema":1480349159276},{"serialNumber":"JV/LVzSKI/wsDgg3UuZHlA==","enabled":true,"last_modified":1480349161575,"details":{"who":".","created":"2016-03-01T21:22:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1252142","name":"exceptional SHA-1 Certificates","why":"."},"issuerName":"MIG8MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTEwMTYwNAYDVQQDEy1WZXJpU2lnbiBDbGFzcyAzIEludGVybmF0aW9uYWwgU2VydmVyIENBIC0gRzM=","id":"ba7935ef-b624-3eef-5ae8-a78044ec3af0","schema":1480349159276},{"serialNumber":"sPNcCSE9Nkg3jy5IN1xe2Q==","enabled":true,"last_modified":1480349161553,"details":{"who":".","created":"2016-01-18T14:44:46Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155145","name":"GlobalSign certs","why":"."},"issuerName":"MFwxCzAJBgNVBAYTAkJFMRUwEwYDVQQLEwxUcnVzdGVkIFJvb3QxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExGzAZBgNVBAMTElRydXN0ZWQgUm9vdCBDQSBHMg==","id":"edc761c4-8b47-5314-0cd1-c894612ebfc3","schema":1480349159276},{"serialNumber":"buROL/l2GuXISv+/JVLkdA==","enabled":true,"last_modified":1480349161531,"details":{"who":".","created":"2016-10-27T17:52:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA2IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHNQ==","id":"963548ca-bde2-1843-f140-1c26e8e40def","schema":1480349159276},{"serialNumber":"Cfk9oA==","enabled":true,"last_modified":1480349161510,"details":{"who":".","created":"2016-10-27T17:52:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=","id":"e85980d5-12f0-f624-7904-2d6cedeacd55","schema":1480349159276},{"serialNumber":"IyIVazG4RE9AERkb+ekH8w==","enabled":true,"last_modified":1480349161489,"details":{"who":".","created":"2016-10-27T17:52:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5","id":"be3014a7-3d3b-08c7-02d9-5a74f601a1b1","schema":1480349159276},{"serialNumber":"HNo1DR4XCe4mS1iUMsY6Wg==","enabled":true,"last_modified":1480349161466,"details":{"who":".","created":"2016-04-28T21:08:06Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MGYxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQLExREb21haW4gVmFsaWRhdGVkIFNTTDEgMB4GA1UEAxMXR2VvVHJ1c3QgRFYgU1NMIENBIC0gRzM=","id":"fd51f7d8-2006-663f-4779-3cf559ee5e74","schema":1480349159276},{"serialNumber":"Ai7cBJYqBE0I9NdyoZfRrw==","enabled":true,"last_modified":1480349161443,"details":{"who":".","created":"2016-10-27T17:52:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MIG9MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5","id":"f7582bc8-dbdb-37dd-b24c-418242bbc4e0","schema":1480349159276},{"serialNumber":"UUFV3S2cUidOOv7ESN65Ng==","enabled":true,"last_modified":1480349161103,"details":{"who":".","created":"2016-07-14T14:40:23Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1286752","name":"Symantec erroneous SHA-1 certificates","why":"."},"issuerName":"MIG8MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTEwMTYwNAYDVQQDEy1WZXJpU2lnbiBDbGFzcyAzIEludGVybmF0aW9uYWwgU2VydmVyIENBIC0gRzM=","id":"24ccf065-b88b-0fc9-1d1f-6c7722ca4faa","schema":1480349159276},{"serialNumber":"EDQMI0tR4kSntv1O37N10g==","enabled":true,"last_modified":1480349161077,"details":{"who":".","created":"2016-04-28T21:08:06Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MGYxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQLExREb21haW4gVmFsaWRhdGVkIFNTTDEgMB4GA1UEAxMXR2VvVHJ1c3QgRFYgU1NMIENBIC0gRzI=","id":"e7509e16-1cd3-599e-2630-495ab52d3b71","schema":1480349159276},{"serialNumber":"BAAAAAABI54PryQ=","enabled":true,"last_modified":1480349161051,"details":{"who":".","created":"2016-01-18T14:48:11Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155145","name":"GlobalSign certs","why":"."},"issuerName":"MIGBMQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTElMCMGA1UECxMcUHJpbWFyeSBPYmplY3QgUHVibGlzaGluZyBDQTEwMC4GA1UEAxMnR2xvYmFsU2lnbiBQcmltYXJ5IE9iamVjdCBQdWJsaXNoaW5nIENB","id":"e3bd531e-1ee4-7407-27ce-6fdc9cecbbdc","schema":1480349159276},{"serialNumber":"Byc68g==","enabled":true,"last_modified":1480349161021,"details":{"who":".","created":"2016-10-27T17:52:11Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=","id":"308f27b5-fc0e-da9a-2b7f-e65a1cb5cd47","schema":1480349159276},{"serialNumber":"AuhvPsYZfVP6UDsuyjeZ4Q==","enabled":true,"last_modified":1480349160991,"details":{"who":".","created":"2016-04-28T21:08:07Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MHMxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEkMCIGA1UEAxMbU3ltYW50ZWMgQ2xhc3MgMyBEU0EgU1NMIENB","id":"a0482eed-1e3f-7b9f-a433-94e4a4da49a1","schema":1480349159276},{"serialNumber":"BAAAAAABMrS7t2g=","enabled":true,"last_modified":1480349160963,"details":{"who":".","created":"2016-01-18T14:39:59Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155145","name":"GlobalSign certs","why":"."},"issuerName":"MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==","id":"41a888b9-2e84-9782-69dc-d9153a3bd3aa","schema":1480349159276},{"serialNumber":"Ermwxw==","enabled":true,"last_modified":1480349160941,"details":{"who":".","created":"2016-07-21T16:52:32Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1197885","name":"SECOM intermediate certificate","why":"."},"issuerName":"MFAxCzAJBgNVBAYTAkpQMRgwFgYDVQQKEw9TRUNPTSBUcnVzdC5uZXQxJzAlBgNVBAsTHlNlY3VyaXR5IENvbW11bmljYXRpb24gUm9vdENBMQ==","id":"1220feb9-9e66-0b24-3409-c5d1a1f8d24f","schema":1480349159276},{"serialNumber":"BAAAAAABCfhiO+s=","enabled":true,"last_modified":1480349160914,"details":{"who":".","created":"2016-01-18T14:36:53Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155145","name":"GlobalSign certs","why":"."},"issuerName":"MF8xCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRQwEgYDVQQLEwtQYXJ0bmVycyBDQTEfMB0GA1UEAxMWR2xvYmFsU2lnbiBQYXJ0bmVycyBDQQ==","id":"e17ded97-6669-eecc-54fa-81f9a81ed281","schema":1480349159276},{"serialNumber":"cXXMzbWDHMIdCotb3h64yw==","enabled":true,"last_modified":1480349160892,"details":{"who":".","created":"2016-07-21T16:58:57Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1288354","name":"Symantec AATL ECC Intermediate CA cert","why":"."},"issuerName":"MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA3IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHNA==","id":"a9970f7e-bac4-3b91-eae3-b0335ce0d1c2","schema":1480349159276},{"serialNumber":"OqQ2rV0ISTc308Z/oQgzFw==","enabled":true,"last_modified":1480349160870,"details":{"who":".","created":"2016-04-28T21:08:08Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MIG1MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTEwMS8wLQYDVQQDEyZWZXJpU2lnbiBDbGFzcyAzIFNlY3VyZSBTZXJ2ZXIgQ0EgLSBHMw==","id":"781f8787-6508-d6aa-c508-13ac2bb0d930","schema":1480349159276},{"serialNumber":"Cyr1PA==","enabled":true,"last_modified":1480349160848,"details":{"who":".","created":"2016-10-27T17:52:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=","id":"ec93f86d-fea2-7eea-42d4-7cf7a397e097","schema":1480349159276},{"serialNumber":"F5Bg+EziQQ==","enabled":true,"last_modified":1480349160826,"details":{"who":".","created":"2016-10-27T17:52:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=","id":"64229466-b4da-c63f-e088-ab1b5ba37930","schema":1480349159276},{"serialNumber":"BAAAAAABEAuMoRs=","enabled":true,"last_modified":1480349160804,"details":{"who":".","created":"2016-10-27T17:52:09Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MEwxIDAeBgNVBAsTF0dsb2JhbFNpZ24gUm9vdCBDQSAtIFIyMRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu","id":"fe2dd507-9fc4-a6db-7ee0-255452bea28a","schema":1480349159276},{"serialNumber":"BAAAAAABHkSHlSo=","enabled":true,"last_modified":1480349160782,"details":{"who":".","created":"2016-01-18T14:39:24Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155145","name":"GlobalSign certs","why":"."},"issuerName":"MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==","id":"04460239-09d9-2a76-191e-f344a8e5d0bd","schema":1480349159276},{"serialNumber":"BAAAAAABKB/OGqI=","enabled":true,"last_modified":1480349160759,"details":{"who":".","created":"2016-01-18T14:41:08Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155145","name":"GlobalSign certs","why":"."},"issuerName":"MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==","id":"6b5475ec-8181-e7b8-4245-c7e2ffed213c","schema":1480349159276},{"serialNumber":"ESDu2nhlLPzfx+LYgjlYFP/k","enabled":true,"last_modified":1480349160736,"details":{"who":".","created":"2016-10-27T17:52:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xhc3MgMiBQcmltYXJ5IENB","id":"dc98c4eb-836f-6ca0-6673-6678fc45516a","schema":1480349159276},{"serialNumber":"BA==","enabled":true,"last_modified":1480349160714,"details":{"who":".","created":"2016-07-28T12:14:46Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1289808","name":"FNMT revoked intermediate certificates","why":"."},"issuerName":"MDsxCzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTQ==","id":"765f426d-748a-5f3d-e409-35eac9be8240","schema":1480349159276},{"serialNumber":"Aw==","enabled":true,"last_modified":1480349160684,"details":{"who":".","created":"2016-10-27T17:52:14Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MIGFMQswCQYDVQQGEwJVUzEgMB4GA1UECgwXV2VsbHMgRmFyZ28gV2VsbHNTZWN1cmUxHDAaBgNVBAsME1dlbGxzIEZhcmdvIEJhbmsgTkExNjA0BgNVBAMMLVdlbGxzU2VjdXJlIFB1YmxpYyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eQ==","id":"644e3fde-7ab5-556a-85ef-c5fac8b8c7de","schema":1480349159276},{"serialNumber":"BAAAAAABMYnGRuw=","enabled":true,"last_modified":1480349160660,"details":{"who":".","created":"2016-10-27T17:52:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MEwxIDAeBgNVBAsTF0dsb2JhbFNpZ24gUm9vdCBDQSAtIFIzMRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu","id":"0a9335d8-3fa3-5d0b-7b27-72ddd14b5f74","schema":1480349159276},{"serialNumber":"Byd5cg==","enabled":true,"last_modified":1480349160638,"details":{"who":".","created":"2016-10-27T17:52:11Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=","id":"f046b001-5aa9-09b4-995d-23ad9f15db30","schema":1480349159276},{"serialNumber":"ESJJweWBPhoXAaB9c8SHwI4O","enabled":true,"last_modified":1480349160614,"details":{"who":".","created":"2016-10-27T17:52:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xhc3MgMiBQcmltYXJ5IENB","id":"1bdc6228-2f9d-ad26-30b2-408959ede857","schema":1480349159276},{"serialNumber":"LTRcDHabRHU=","enabled":true,"last_modified":1480349160593,"details":{"who":".","created":"2016-10-27T17:52:11Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MGExCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xMjAwBgNVBAMMKVN0YWF0IGRlciBOZWRlcmxhbmRlbiBPcmdhbmlzYXRpZSBDQSAtIEcy","id":"86e95439-a9f5-e04e-5241-268cf0186425","schema":1480349159276},{"serialNumber":"YNOos6YJoPC77qwSGCpb7w==","enabled":true,"last_modified":1480349160571,"details":{"who":".","created":"2016-04-28T21:08:06Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MGYxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQLExREb21haW4gVmFsaWRhdGVkIFNTTDEgMB4GA1UEAxMXR2VvVHJ1c3QgRFYgU1NMIENBIC0gRzM=","id":"3c4f4898-da76-0a38-a765-18d9436285f2","schema":1480349159276},{"serialNumber":"BAAAAAABHkSHki0=","enabled":true,"last_modified":1480349160547,"details":{"who":".","created":"2016-10-27T17:52:11Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==","id":"e057bdab-4ad6-7e50-83ce-1099b84cce04","schema":1480349159276},{"serialNumber":"A9GPKQ8jv9oIxfwiOy7qxQ==","enabled":true,"last_modified":1480349160524,"details":{"who":".","created":"2016-07-14T14:40:23Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1286752","name":"Symantec erroneous SHA-1 certificates","why":"."},"issuerName":"MIG8MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTEwMTYwNAYDVQQDEy1WZXJpU2lnbiBDbGFzcyAzIEludGVybmF0aW9uYWwgU2VydmVyIENBIC0gRzM=","id":"4bb8966b-1db9-844d-4904-d823539cdf7e","schema":1480349159276},{"serialNumber":"Cbssdw==","enabled":true,"last_modified":1480349160216,"details":{"who":".","created":"2016-10-27T17:52:11Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=","id":"661e65a5-ee0b-7a82-12c3-731cb560e112","schema":1480349159276},{"serialNumber":"CcHC1w==","enabled":true,"last_modified":1480349160190,"details":{"who":".","created":"2016-10-27T17:52:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=","id":"26890617-93ad-e85f-550a-afb414cd110c","schema":1480349159276},{"serialNumber":"DjIvBkX+ECVbB/C3i6w2Gg==","enabled":true,"last_modified":1480349160165,"details":{"who":".","created":"2016-10-27T17:52:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xhc3MgMiBQcmltYXJ5IENB","id":"657b91b1-9447-8c4a-db57-e0f0e2b10439","schema":1480349159276},{"serialNumber":"UoRGnb96CUDTxIqVry6LBg==","enabled":true,"last_modified":1480349160143,"details":{"who":".","created":"2015-04-07T11:04:11Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1150585","name":"XS4ALL certificate","why":"."},"issuerName":"MIGQMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01PRE8gQ0EgTGltaXRlZDE2MDQGA1UEAxMtQ09NT0RPIFJTQSBEb21haW4gVmFsaWRhdGlvbiBTZWN1cmUgU2VydmVyIENB","id":"9084bc79-01cf-2ddc-b077-cd6c6251dd2b","schema":1480349159276},{"serialNumber":"PAdKZPiaac2CvPxbOrsHOw==","enabled":true,"last_modified":1480349160119,"details":{"who":".","created":"2016-04-28T21:08:07Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MH4xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEvMC0GA1UEAxMmU3ltYW50ZWMgQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzQ=","id":"b79e2eb5-754d-4c21-1d8a-8c363f70dadc","schema":1480349159276},{"serialNumber":"EqthLKdUgwI=","enabled":true,"last_modified":1480349160086,"details":{"who":".","created":"2016-10-27T17:52:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MIGQMQswCQYDVQQGEwJHUjFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxOzA5BgNVBAMTMkFyaXN0b3RsZSBVbml2ZXJzaXR5IG9mIFRoZXNzYWxvbmlraSBDZW50cmFsIENBIFI0","id":"5b9744a8-bd65-b926-2920-8d8e5e1acd7d","schema":1480349159276},{"serialNumber":"OE4/d+p3YRzzcSl+kmZ8Mw==","enabled":true,"last_modified":1480349160058,"details":{"who":".","created":"2016-04-28T21:08:07Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MGgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQLExREb21haW4gVmFsaWRhdGVkIFNTTDEiMCAGA1UEAxMZR2VvVHJ1c3QgRFYgU1NMIFNIQTI1NiBDQQ==","id":"fd2fe6f3-d095-5c3d-3c6b-afe00cb665c0","schema":1480349159276},{"serialNumber":"QZCrvA==","enabled":true,"last_modified":1480349160028,"details":{"who":".","created":"2016-10-27T17:52:14Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MIGCMQswCQYDVQQGEwJVUzEeMBwGA1UECxMVd3d3LnhyYW1wc2VjdXJpdHkuY29tMSQwIgYDVQQKExtYUmFtcCBTZWN1cml0eSBTZXJ2aWNlcyBJbmMxLTArBgNVBAMTJFhSYW1wIEdsb2JhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==","id":"32f968f5-4426-66ad-d1ab-9dda7b6f0c85","schema":1480349159276},{"serialNumber":"AygWP2Fgd2T+iLbmAlKT6g==","enabled":true,"last_modified":1480349159993,"details":{"who":".","created":"2016-04-28T21:08:07Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MH4xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEvMC0GA1UEAxMmU3ltYW50ZWMgQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzQ=","id":"e23bcca2-1fb1-abd2-5ed7-892c5f08c4ff","schema":1480349159276},{"serialNumber":"CqL7CA==","enabled":true,"last_modified":1480349159963,"details":{"who":".","created":"2016-10-27T17:52:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=","id":"1a0c71ac-7f34-eeb0-2f71-1c14ee8e6552","schema":1480349159276},{"serialNumber":"E77H6yvyFQjO0PcN3x0H+Q==","enabled":true,"last_modified":1480349159939,"details":{"who":".","created":"2016-04-28T21:08:07Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MH4xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEvMC0GA1UEAxMmU3ltYW50ZWMgQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzQ=","id":"be9e31eb-6a06-7699-708a-732a081a10c7","schema":1480349159276},{"serialNumber":"YRJNfMoc12IpmW+Enpv3Pdo=","enabled":true,"last_modified":1480349159914,"details":{"who":".","created":"2016-01-18T14:46:04Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155145","name":"GlobalSign certs","why":"."},"issuerName":"MFwxCzAJBgNVBAYTAkJFMRUwEwYDVQQLEwxUcnVzdGVkIFJvb3QxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExGzAZBgNVBAMTElRydXN0ZWQgUm9vdCBDQSBHMg==","id":"3bb8f2d9-511b-121d-da29-23041d3c15c1","schema":1480349159276},{"serialNumber":"ATFEdg==","enabled":true,"last_modified":1480349159890,"details":{"who":".","created":"2015-05-08T10:54:05Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155114","name":"Intermediate CA's under Staat der Nederlanden Root CA","why":"."},"issuerName":"MFkxCzAJBgNVBAYTAk5MMR4wHAYDVQQKExVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xKjAoBgNVBAMTIVN0YWF0IGRlciBOZWRlcmxhbmRlbiBPdmVyaGVpZCBDQQ==","id":"4a17f132-602c-2d30-a781-145087794075","schema":1480349159276},{"serialNumber":"e0bEFhI16xx9U1yvlI56rA==","enabled":true,"last_modified":1480349159869,"details":{"who":".","created":"2016-04-28T21:08:08Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MEExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxUaGF3dGUsIEluYy4xGzAZBgNVBAMTElRoYXd0ZSBTR0MgQ0EgLSBHMg==","id":"07b97387-d755-0bbb-6e4c-616a7cb69cb4","schema":1480349159276},{"serialNumber":"GN2Hrh9LtnA=","enabled":true,"last_modified":1480349159846,"details":{"who":".","created":"2016-10-27T17:52:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MIGVMQswCQYDVQQGEwJHUjFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTE=","id":"b3fb394d-f951-b49e-d856-f93028850feb","schema":1480349159276},{"serialNumber":"AQAAAAU=","enabled":true,"last_modified":1480349159825,"details":{"who":".","created":"2016-10-27T17:52:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MIGVMQswCQYDVQQGEwJHUjFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTE=","id":"2448ada0-833d-0d4d-3bcd-2c73cff02fb4","schema":1480349159276},{"serialNumber":"BAAAAAABCFiEp9s=","enabled":true,"last_modified":1480349159804,"details":{"who":".","created":"2016-01-18T14:38:03Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155145","name":"GlobalSign certs","why":"."},"issuerName":"MF8xCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRQwEgYDVQQLEwtQYXJ0bmVycyBDQTEfMB0GA1UEAxMWR2xvYmFsU2lnbiBQYXJ0bmVycyBDQQ==","id":"5cef5dc2-95f2-0ac8-3273-153a09d3d227","schema":1480349159276},{"serialNumber":"CSY=","enabled":true,"last_modified":1480349159782,"details":{"who":".","created":"2016-10-27T17:52:11Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDM=","id":"b4c9041b-f5a5-dec1-b221-7286a32a6309","schema":1480349159276},{"serialNumber":"BAAAAAABHJRKNmk=","enabled":true,"last_modified":1480349159760,"details":{"who":".","created":"2016-01-18T14:44:06Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155145","name":"GlobalSign certs","why":"."},"issuerName":"MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==","id":"824e9b09-808e-e8cb-f2dd-ba669df011bc","schema":1480349159276},{"serialNumber":"DYifRdP6aQQ8MLbXZY2f5g==","enabled":true,"last_modified":1480349159739,"details":{"who":".","created":"2016-04-28T21:08:08Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MGMxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwx0aGF3dGUsIEluYy4xHTAbBgNVBAsTFERvbWFpbiBWYWxpZGF0ZWQgU1NMMR4wHAYDVQQDExV0aGF3dGUgRFYgU1NMIENBIC0gRzI=","id":"59a292db-d567-6485-167f-1cb5af33f437","schema":1480349159276},{"serialNumber":"BAAAAAABL07hTcY=","enabled":true,"last_modified":1480349159716,"details":{"who":".","created":"2016-10-27T17:52:11Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFYxCzAJBgNVBAYTAkpQMQ8wDQYDVQQKEwZKSVBERUMxGjAYBgNVBAsTEUpDQU4gU3ViIFJvb3QgQ0EwMRowGAYDVQQDExFKQ0FOIFN1YiBSb290IENBMA==","id":"0a5a0233-bb8a-e4cc-5bb6-03cee44a07c2","schema":1480349159276},{"serialNumber":"L7tgs/W85vnhV7I7qJ6N/g==","enabled":true,"last_modified":1480349159694,"details":{"who":".","created":"2016-10-27T17:52:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MF4xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEqMCgGA1UECwwhZVBLSSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5","id":"e38e4e46-413a-eac8-9e85-1b9ee06f535d","schema":1480349159276},{"serialNumber":"BAAAAAABLM/7qjk=","enabled":true,"last_modified":1480349159673,"details":{"who":".","created":"2016-01-18T14:40:36Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155145","name":"GlobalSign certs","why":"."},"issuerName":"MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==","id":"62dfeb32-e132-09a3-e4ba-e48c21d027de","schema":1480349159276},{"serialNumber":"BAAAAAABHkSHjz8=","enabled":true,"last_modified":1480349159649,"details":{"who":".","created":"2016-01-18T14:41:41Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155145","name":"GlobalSign certs","why":"."},"issuerName":"MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==","id":"880134d0-617c-ec1b-3568-4c74af31bcc1","schema":1480349159276},{"serialNumber":"Cfk9qg==","enabled":true,"last_modified":1480349159625,"details":{"who":".","created":"2016-10-27T17:52:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=","id":"9debdd66-d615-073f-26f7-011a8c54b484","schema":1480349159276},{"serialNumber":"AQAAAAM=","enabled":true,"last_modified":1480349159267,"details":{"who":".","created":"2016-10-27T17:52:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MIGVMQswCQYDVQQGEwJHUjFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTE=","id":"e4d16e1f-ac86-e9c0-2122-8a7d7d2dcdc0","schema":1480343774567},{"serialNumber":"ESAyW/JX3+hZIp44EAMlXU2b","enabled":true,"last_modified":1480349159244,"details":{"who":".","created":"2016-10-27T17:52:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xhc3MgMiBQcmltYXJ5IENB","id":"6b135741-bfc6-1b3f-eb4b-4569aae2ab34","schema":1480343774567},{"serialNumber":"AQAAAAQ=","enabled":true,"last_modified":1480349159206,"details":{"who":".","created":"2016-10-27T17:52:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MIGVMQswCQYDVQQGEwJHUjFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTE=","id":"263f03a7-53f0-2140-89a5-46e7740d755a","schema":1480343774567},{"serialNumber":"ESByYNtAIfizf2L3NMzCH8zZ","enabled":true,"last_modified":1480349159183,"details":{"who":".","created":"2016-10-27T17:52:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xhc3MgMiBQcmltYXJ5IENB","id":"853889e5-ad93-77ea-f9c0-a0907c9a3576","schema":1480343774567},{"serialNumber":"BYOGvG32ukb1Yxj2oKoFyw==","enabled":true,"last_modified":1480349159159,"details":{"who":".","created":"2016-10-27T17:52:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MIG9MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5","id":"b049208e-5f4c-60a6-ac91-45f093defc33","schema":1480343774567},{"serialNumber":"P6G7IYSL2RZxtzTh8I6qPA==","enabled":true,"last_modified":1480349159136,"details":{"who":".","created":"2016-04-28T21:08:06Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MGYxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQLExREb21haW4gVmFsaWRhdGVkIFNTTDEgMB4GA1UEAxMXR2VvVHJ1c3QgRFYgU1NMIENBIC0gRzI=","id":"222a4201-120e-64f4-5f6f-b2314470365c","schema":1480343774567},{"serialNumber":"D/wZ7+m1Mv8SONSEFcs73w==","enabled":true,"last_modified":1480349159113,"details":{"who":".","created":"2016-10-27T17:52:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MIGuMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTE2MDQGA1UEAxMtVVROLVVTRVJGaXJzdC1DbGllbnQgQXV0aGVudGljYXRpb24gYW5kIEVtYWls","id":"d7fdf5b8-f8a5-b105-297d-d0e3617e59fc","schema":1480343774567},{"serialNumber":"eR1nUEz8k+nDSBD+bb5uIQ==","enabled":true,"last_modified":1480349159091,"details":{"who":".","created":"2016-10-27T17:52:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MIG9MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5","id":"f6fb7b44-64cd-37b9-b9f6-fbe1210cb160","schema":1480343774567},{"serialNumber":"QZBvapTZFvmYktEPsBYLQQ==","enabled":true,"last_modified":1480349159069,"details":{"who":".","created":"2016-04-28T21:08:08Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MIG1MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTEwMS8wLQYDVQQDEyZWZXJpU2lnbiBDbGFzcyAzIFNlY3VyZSBTZXJ2ZXIgQ0EgLSBHMw==","id":"b6f5b3ee-6cac-57a7-b4e9-83e84f1020b8","schema":1480343774567},{"serialNumber":"BAAAAAABKUXDqA8=","enabled":true,"last_modified":1480349159034,"details":{"who":".","created":"2016-10-27T17:52:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxTaWduIFJvb3QgQ0E=","id":"e48f4383-6df1-0a9b-8063-a2add391a879","schema":1480343774567},{"serialNumber":"BAAAAAABLF5/Gog=","enabled":true,"last_modified":1480349159011,"details":{"who":".","created":"2016-10-27T17:52:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxTaWduIFJvb3QgQ0E=","id":"bc0be7d5-b8d3-1ac2-8a63-f5bf8b00ba3c","schema":1480343774567},{"serialNumber":"UW3oKZKTDsrPy/rfwmGNaQ==","enabled":true,"last_modified":1480349158980,"details":{"who":".","created":"2016-04-28T21:08:06Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MGYxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQLExREb21haW4gVmFsaWRhdGVkIFNTTDEgMB4GA1UEAxMXR2VvVHJ1c3QgRFYgU1NMIENBIC0gRzM=","id":"2a14bce1-47bb-e067-74a6-46be30e576a7","schema":1480343774567},{"serialNumber":"CSU=","enabled":true,"last_modified":1480349158957,"details":{"who":".","created":"2016-10-27T17:52:11Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDM=","id":"77e152ae-89a4-53b1-62d4-fb7c2cd5037b","schema":1480343774567},{"serialNumber":"U3t2Vk8pfxTcaUPpIq0seQ==","enabled":true,"last_modified":1480349158934,"details":{"who":".","created":"2016-10-27T17:52:14Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsxIjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3Q=","id":"2c8f7191-fc5f-c2e0-af8d-852faefcdadf","schema":1480343774567},{"serialNumber":"BAAAAAABAPpuVh0=","enabled":true,"last_modified":1480349158899,"details":{"who":".","created":"2016-01-18T14:38:46Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155145","name":"GlobalSign certs","why":"."},"issuerName":"MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==","id":"fce6cca1-707c-c4f2-6de2-26c4663cda01","schema":1480343774567},{"serialNumber":"U4P1tUoxl/XkztlVHdtdgw==","enabled":true,"last_modified":1480349158876,"details":{"who":".","created":"2016-10-27T17:52:11Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==","id":"ed4d2530-eb3e-800f-41b2-e70023185673","schema":1480343774567},{"serialNumber":"U3SgRR3J+D6575WuCxuXeQ==","enabled":true,"last_modified":1480349158854,"details":{"who":".","created":"2016-04-28T21:08:07Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MHsxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEsMCoGA1UEAxMjU3ltYW50ZWMgQ2xhc3MgMyBFQ0MgMjU2IGJpdCBTU0wgQ0E=","id":"72e44e74-fb5d-fc97-abdd-02050d3ab727","schema":1480343774567},{"serialNumber":"EEpERSryZFMagbsNw/WoWQ==","enabled":true,"last_modified":1480349158820,"details":{"who":".","created":"2016-10-27T17:52:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MIGXMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTEfMB0GA1UEAxMWVVROLVVTRVJGaXJzdC1IYXJkd2FyZQ==","id":"a4f11626-67e9-24e5-81a5-aca8126934c3","schema":1480343774567},{"serialNumber":"BAAAAAABJQcQQN0=","enabled":true,"last_modified":1480349158794,"details":{"who":".","created":"2016-10-27T17:52:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MEwxIDAeBgNVBAsTF0dsb2JhbFNpZ24gUm9vdCBDQSAtIFIzMRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu","id":"e7328f54-0f56-48a2-3bab-d4c9b41a2b13","schema":1480343774567},{"serialNumber":"BydKkg==","enabled":true,"last_modified":1480349158772,"details":{"who":".","created":"2016-10-27T17:52:11Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=","id":"6b9bc0cc-c1fa-50eb-5ece-812ee9bac5b6","schema":1480343774567},{"serialNumber":"cpqpXVWPk5AXzGw+zNIcBw==","enabled":true,"last_modified":1480349158750,"details":{"who":".","created":"2016-04-28T21:08:07Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MEQxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQDExRHZW9UcnVzdCBTU0wgQ0EgLSBHMw==","id":"ac8d05cd-d85d-8caa-ed13-261b83c183c2","schema":1480343774567},{"serialNumber":"CWhp","enabled":true,"last_modified":1480349158726,"details":{"who":".","created":"2016-04-28T21:08:06Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1267648","name":"Symantec test certificates","why":"."},"issuerName":"MGExCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQLExREb21haW4gVmFsaWRhdGVkIFNTTDEbMBkGA1UEAxMSR2VvVHJ1c3QgRFYgU1NMIENB","id":"ff01b36d-3bcc-2157-1f89-b1841120c97e","schema":1480343774567},{"serialNumber":"ESCEUbthDurBjJw0/h/FfuNY","enabled":true,"last_modified":1480349158695,"details":{"who":".","created":"2016-10-27T17:52:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xhc3MgMiBQcmltYXJ5IENB","id":"2c047b2c-43aa-d0be-e2c9-2acac789bd55","schema":1480343774567},{"serialNumber":"TXxtAQ==","enabled":true,"last_modified":1480349158671,"details":{"who":".","created":"2016-10-27T17:52:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1312150","name":"Revoked intermediates","why":"."},"issuerName":"MEoxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdTZWN1cmVUcnVzdCBDb3Jwb3JhdGlvbjEZMBcGA1UEAxMQU2VjdXJlIEdsb2JhbCBDQQ==","id":"ea4847df-ca17-c476-11df-b14c1d6658a0","schema":1480343774567},{"serialNumber":"YR3YYQAAAAAABA==","enabled":true,"last_modified":1480349158647,"details":{"who":".","created":"2015-05-08T10:51:23Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1142137","name":"T-Systems intermediate cert","why":"."},"issuerName":"MGcxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpGcmF1bmhvZmVyMSEwHwYDVQQLExhGcmF1bmhvZmVyIENvcnBvcmF0ZSBQS0kxIDAeBgNVBAMTF0ZyYXVuaG9mZXIgUm9vdCBDQSAyMDA3","id":"ae8bec3c-3b92-822e-53f1-68394cbb1758","schema":1480343774567}]}PK
!<�MS@L@Ldefaults/blocklists/gfx.json{"data":[{"vendor":"0x10de","blockID":"g200","enabled":true,"feature":"WEBGL_MSAA","devices":[],"id":"ae36a32e-5f4f-2f98-804f-22b0de5e7bfa","last_modified":1480349135384,"details":{"who":"All Firefox users.","created":"2012-11-12T10:35:32Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=809550","name":"Mac OS X WebGL anti-aliasing","why":"Security problems."},"os":"Darwin 11","featureStatus":"BLOCKED_DEVICE","schema":1480349134676},{"driverVersionComparator":"EQUAL","driverVersion":"10.18.10.3947","vendor":"0x8086","blockID":"g1217","enabled":true,"feature":"HARDWARE_VIDEO_DECODING","devices":[],"id":"a3fe9700-b9e6-1c31-f94b-030f72f4aa07","last_modified":1480349135360,"details":{"who":".","created":"2016-05-23T15:43:58Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1266220","name":"10.18.10.3947 winnt 6.0","why":"."},"os":"WINNT 6.0","featureStatus":"BLOCKED_DRIVER_VERSION","schema":1480349134676},{"driverVersionComparator":"EQUAL","driverVersion":"8.15.10.2086","vendor":"0x8086","blockID":"g1124","enabled":true,"devices":["0x2a42","0x2e22","0x2e12","0x2e32","0x0046"],"id":"851c8fcb-5763-bf89-813d-0f98cdd6f8c1","last_modified":1480349135337,"details":{"who":".","created":"2016-02-22T23:40:14Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1170143","name":"Intel Driver 8.15.10.2086","why":"."},"os":"All","featureStatus":"BLOCKED_DRIVER_VERSION","schema":1480349134676},{"vendor":"0x10de","blockID":"g202","enabled":true,"feature":"WEBGL_MSAA","devices":[],"id":"a825cb5d-36f6-54b0-31b3-6668aae49cdd","last_modified":1480349135314,"details":{"who":"All Firefox users.","created":"2012-11-12T10:37:07Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=809550","name":"Mac OS X WebGL anti-aliasing","why":"Security problems."},"os":"Darwin 12","featureStatus":"BLOCKED_DEVICE","schema":1480349134676},{"vendor":"0x8086","blockID":"g208","enabled":true,"feature":"WEBGL_MSAA","devices":[],"id":"a8e8aa1a-8e70-af51-065d-81ed31d23221","last_modified":1480349135291,"details":{"who":"All Firefox users.","created":"2012-11-12T10:42:34Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=809550","name":"Mac OS X WebGL anti-aliasing","why":"Security problems"},"os":"Darwin 12","featureStatus":"BLOCKED_DEVICE","schema":1480349134676},{"driverVersionComparator":"EQUAL","driverVersion":"8.15.10.1994","vendor":"0x8086","blockID":"g1073","enabled":true,"devices":["0x2a42","0x2e22","0x2e12","0x2e32","0x0046"],"id":"13f75dff-c65f-eab1-4be5-864a79503ad5","last_modified":1480349135267,"details":{"who":".","created":"2015-12-21T16:01:46Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1207993","name":"Crash in igd10umd32.dll@0x18f35","why":"."},"os":"All","featureStatus":"BLOCKED_DRIVER_VERSION","schema":1480349134676},{"driverVersionComparator":"EQUAL","driverVersion":"10.18.10.3947","vendor":"0x8086","blockID":"g1221","enabled":true,"feature":"HARDWARE_VIDEO_DECODING","devices":[],"id":"65ad7851-4af6-2a10-1d47-72943e63dcf0","last_modified":1480349135245,"details":{"who":".","created":"2016-05-23T15:47:17Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1266220","name":"10.18.10.3947 winnt 10.0","why":"."},"os":"WINNT 10.0","featureStatus":"BLOCKED_DRIVER_VERSION","schema":1480349134676},{"driverVersionComparator":"EQUAL","driverVersion":"10.18.10.3947","vendor":"0x8086","blockID":"g1216","enabled":true,"feature":"HARDWARE_VIDEO_DECODING","devices":[],"id":"18968a19-7317-cc56-f9fc-f568f40abca4","last_modified":1480349135215,"details":{"who":".","created":"2016-05-23T15:32:22Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1266220","name":"10.18.10.3947 winnt 5.2","why":"."},"os":"WINNT 5.2","featureStatus":"BLOCKED_DRIVER_VERSION","schema":1480349134676},{"driverVersionComparator":"EQUAL","driverVersion":"10.18.10.3947","vendor":"0x8086","blockID":"g1215","enabled":true,"feature":"HARDWARE_VIDEO_DECODING","devices":[],"id":"5c141970-5076-0af4-152f-efb3642a7b14","last_modified":1480349135171,"details":{"who":".","created":"2016-05-23T15:31:32Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1266220","name":"10.18.10.3947 winnt 5.1","why":"."},"os":"WINNT 5.1","featureStatus":"BLOCKED_DRIVER_VERSION","schema":1480349134676},{"vendor":"0x1002","blockID":"g234","enabled":true,"feature":"WEBGL_MSAA","devices":[],"id":"3310f8b8-1789-1b13-9326-5531943514ae","last_modified":1480349135145,"details":{"who":"All Firefox users.","created":"2012-11-30T12:50:57Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=815437","name":"Mac OS X WebGL anti-aliasing (AMD)","why":"Security problems."},"os":"Darwin 12","featureStatus":"BLOCKED_DEVICE","schema":1480349134676},{"vendor":"0x1002","blockID":"g230","enabled":true,"feature":"WEBGL_MSAA","devices":[],"id":"4769433c-51e9-a0f9-eef5-45829ac54243","last_modified":1480349135120,"details":{"who":"All Firefox users.","created":"2012-11-30T12:48:55Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=815437","name":"Mac OS X WebGL anti-aliasing (AMD)","why":"Security problems."},"os":"Darwin 10","featureStatus":"BLOCKED_DEVICE","schema":1480349134676},{"driverVersionComparator":"EQUAL","driverVersion":"10.18.10.3947","vendor":"0x8086","blockID":"g1218","enabled":true,"feature":"HARDWARE_VIDEO_DECODING","devices":[],"id":"7a3ea538-8519-2845-53bf-6a4be0ff9e6b","last_modified":1480349135096,"details":{"who":".","created":"2016-05-23T15:44:43Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1266220","name":"10.18.10.3947 winnt 6.1","why":"."},"os":"WINNT 6.1","featureStatus":"BLOCKED_DRIVER_VERSION","schema":1480349134676},{"vendor":"0x10de","blockID":"g198","enabled":true,"feature":"WEBGL_MSAA","devices":[],"id":"c9551a39-62fb-f652-8032-cbe9d2ce419c","last_modified":1480349135072,"details":{"who":"All Firefox users.","created":"2012-11-12T10:34:32Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=809550","name":"Mac OS X WebGL anti-aliasing","why":"Security problems."},"os":"Darwin 10","featureStatus":"BLOCKED_DEVICE","schema":1480349134676},{"driverVersionComparator":"LESS_THAN","driverVersion":"6.14.10.5218","vendor":"0x8086","blockID":"g511","enabled":true,"feature":"DIRECT3D_9_LAYERS","devices":[],"id":"37ffcc44-0d03-681e-69bf-89c38111fcdd","last_modified":1480349135048,"details":{"who":"All Firefox users.","created":"2013-12-18T14:29:36Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=951422","name":"Intel driver < 6.14.10.5216 for DIRECT3D_9_LAYERS on XP","why":"Stability."},"os":"WINNT 5.1","featureStatus":"BLOCKED_DRIVER_VERSION","schema":1480349134676},{"driverVersionComparator":"LESS_THAN_OR_EQUAL","driverVersion":"8.17.12.5896","vendor":"0x10de","blockID":"g36","enabled":true,"feature":"DIRECT3D_9_LAYERS","devices":["0x0a6c"],"id":"6ac4f440-9661-ee18-248f-f3b7b6eb8deb","last_modified":1480349134662,"details":{"who":"","created":"2011-03-01T12:13:42Z","bug":"","name":"<BlocklistGfx: None: WINNT 6.1 : 0x10de : 0x0a6c>","why":""},"os":"WINNT 6.1","featureStatus":"BLOCKED_DRIVER_VERSION","schema":1480343737148},{"driverVersionComparator":"EQUAL","driverVersion":"8.15.10.1855","vendor":"0x8086","blockID":"g1069","enabled":true,"devices":["0x2a42","0x2e22","0x2e12","0x2e32","0x0046"],"id":"9928c898-4941-555d-e502-54a90e9e5371","last_modified":1480349134638,"details":{"who":".","created":"2015-12-21T15:58:51Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1207993","name":"Crash in igd10umd32.dll@0x18f35","why":"."},"os":"All","featureStatus":"BLOCKED_DRIVER_VERSION","schema":1480343737148},{"driverVersionComparator":"LESS_THAN","driverVersion":"15.201.1151.0","vendor":"0x1002","blockID":"g974","enabled":true,"feature":"DIRECT2D","devices":["0x6920","0x6921","0x6928","0x6929","0x692b","0x692f","0x6930","0x6938","0x6939","0x6900","0x6901","0x6902","0x6903","0x6907","0x7300","0x9870","0x9874","0x9875","0x9876","0x9877"],"id":"ec9cf554-49de-e5ea-bbdc-54f84d290605","last_modified":1480349134615,"details":{"who":"All users who have these cards on Windows 10.","created":"2015-08-11T10:30:37Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1189266","name":"ATI cards on Windows 10","why":"Crashes on Windows 10."},"os":"WINNT 10.0","featureStatus":"BLOCKED_DRIVER_VERSION","schema":1480343737148},{"vendor":"0x1002","blockID":"g280","enabled":true,"feature":"DIRECT3D_9_LAYERS","devices":["0x9802","0x9803","0x9803","0x9804","0x9805","0x9806","0x9807"],"id":"9b7a25d9-075e-e5e9-678e-af1292db7463","last_modified":1480349134582,"details":{"who":"All Firefox user who use this driver.","created":"2013-02-13T14:52:05Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=840161","name":"AMD Radeon HD 6290/6300/6310/6320 for Direct3D 9","why":"Crashes."},"os":"WINNT 6.1","featureStatus":"BLOCKED_DEVICE","schema":1480343737148},{"driverVersionComparator":"LESS_THAN_OR_EQUAL","driverVersion":"8.15.10.2413","vendor":"0x8086","blockID":"g984","enabled":true,"feature":"DIRECT2D","devices":[],"id":"f1830d7b-c807-afda-1b97-f41474157165","last_modified":1480349134550,"details":{"who":"All users using these driver versions.","created":"2015-08-14T16:21:45Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1193802","name":"Intel graphics block","why":"Major graphics artifacts."},"os":"All","featureStatus":"BLOCKED_DRIVER_VERSION","schema":1480343737148},{"driverVersionComparator":"EQUAL","driverVersion":"8.15.10.1872","vendor":"0x8086","blockID":"g1070","enabled":true,"devices":["0x2a42","0x2e22","0x2e12","0x2e32","0x0046"],"id":"0f7b2114-976c-db1c-2d9a-02d47d1968e1","last_modified":1480349134528,"details":{"who":".","created":"2015-12-21T15:59:29Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1207993","name":"Crash in igd10umd32.dll@0x18f35","why":"."},"os":"All","featureStatus":"BLOCKED_DRIVER_VERSION","schema":1480343737148},{"driverVersionComparator":"EQUAL","driverVersion":"8.982.0.0","vendor":"0x1022","blockID":"g148","enabled":true,"feature":"DIRECT3D_9_LAYERS","devices":[],"id":"85925d60-d04e-2cbd-cdee-de70ea6be4f4","last_modified":1480349134505,"details":{"who":"All Firefox users who have these drivers installed.","created":"2012-09-25T14:31:35Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=793869","name":"ATI/AMD driver 8.982.0.0 (Direct3D)","why":"Some features in these drivers are causing frequent crashes in Firefox."},"os":"All","featureStatus":"BLOCKED_DRIVER_VERSION","schema":1480343737148},{"driverVersionComparator":"LESS_THAN_OR_EQUAL","driverVersion":"9.10.8.0","vendor":"0x1002","blockID":"g192","enabled":true,"feature":"DIRECT2D","devices":[],"id":"5e34680b-5279-4ffc-7382-b1bc661b3094","last_modified":1480349134483,"details":{"who":"All Firefox users.","created":"2012-11-02T16:33:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=806991","name":"AMD DIRECT2D on Windows 8, [0x1002]","why":"Crashes."},"os":"WINNT 6.2","featureStatus":"BLOCKED_DRIVER_VERSION","schema":1480343737148},{"driverVersionComparator":"LESS_THAN_OR_EQUAL","driverVersion":"8.17.12.5896","vendor":"0x10de","blockID":"g35","enabled":true,"feature":"DIRECT2D","devices":["0x0a6c"],"id":"00a6b9d2-285f-83f0-0a1f-ef0205a60067","last_modified":1480349134459,"details":{"who":"","created":"2011-03-01T12:13:40Z","bug":"","name":"<BlocklistGfx: None: WINNT 6.1 : 0x10de : 0x0a6c>","why":""},"os":"WINNT 6.1","featureStatus":"BLOCKED_DRIVER_VERSION","schema":1480343737148},{"driverVersionComparator":"EQUAL","driverVersion":"8.15.10.1883","vendor":"0x8086","blockID":"g1071","enabled":true,"devices":["0x2a42","0x2e22","0x2e12","0x2e32","0x0046"],"id":"d4297784-dbac-906c-16b7-1a8792e868de","last_modified":1480349134436,"details":{"who":".","created":"2015-12-21T16:00:05Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1207993","name":"Crash in igd10umd32.dll@0x18f35","why":"."},"os":"All","featureStatus":"BLOCKED_DRIVER_VERSION","schema":1480343737148},{"driverVersionComparator":"EQUAL","driverVersion":"8.982.0.0","vendor":"0x1002","blockID":"g150","enabled":true,"feature":"DIRECT3D_9_LAYERS","devices":[],"id":"6a3c05bb-f971-d3d4-2a02-184e6970ba87","last_modified":1480349134413,"details":{"who":"All Firefox users who have these drivers installed.","created":"2012-09-25T14:33:17Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=793869","name":"ATI/AMD driver 8.982.0.0 (Direct3D)","why":"Some features in these drivers are causing frequent crashes in Firefox."},"os":"All","featureStatus":"BLOCKED_DRIVER_VERSION","schema":1480343737148},{"driverVersionComparator":"EQUAL","driverVersion":"8.982.0.0","vendor":"0x1002","blockID":"g144","enabled":true,"feature":"DIRECT2D","devices":[],"id":"35e25a7c-d77d-6b43-fa5e-39b7e9c8a481","last_modified":1480349134391,"details":{"who":"All Firefox users who have these drivers installed.","created":"2012-09-24T08:23:25Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=792480","name":"ATI/AMD driver 8.982.0.0","why":"Some features in these drivers are causing frequent crashes in Firefox."},"os":"All","featureStatus":"BLOCKED_DRIVER_VERSION","schema":1480343737148},{"driverVersionComparator":"LESS_THAN","driverVersion":"6.14.10.5218","vendor":"0x8086","blockID":"g1251","enabled":true,"feature":"WEBGL_ANGLE","devices":[],"id":"ea27a4d2-ed88-0190-db12-473820b340b2","last_modified":1480349134369,"details":{"who":".","created":"2016-07-22T12:30:30Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1283601","name":"Intel driver < 6.14.10.5216 for DIRECT3D_9_LAYERS on XP","why":"."},"versionRange":{"maxVersion":"49.9"},"os":"WINNT 5.1","featureStatus":"BLOCKED_DRIVER_VERSION","schema":1480343737148},{"vendor":"0x8086","blockID":"g206","enabled":true,"feature":"WEBGL_MSAA","devices":[],"id":"91cc2cec-11dd-0f77-ffad-f57e69b3f441","last_modified":1480349134345,"details":{"who":"All Firefox users.","created":"2012-11-12T10:39:32Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=809550","name":"Mac OS X WebGL anti-aliasing","why":"Security problems"},"os":"Darwin 11","featureStatus":"BLOCKED_DEVICE","schema":1480343737148},{"driverVersionComparator":"EQUAL","driverVersion":"8.15.10.1851","vendor":"0x8086","blockID":"g1068","enabled":true,"devices":["0x2a42","0x2e22","0x2e12","0x2e32","0x0046"],"id":"b7452893-25cc-497a-626c-aacee78683c6","last_modified":1480349134323,"details":{"who":".","created":"2015-12-21T15:58:01Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1207993","name":"Crash in igd10umd32.dll@0x18f35","why":"."},"os":"All","featureStatus":"BLOCKED_DRIVER_VERSION","schema":1480343737148},{"driverVersionComparator":"EQUAL","driverVersion":"10.18.10.3947","vendor":"0x8086","blockID":"g1219","enabled":true,"feature":"HARDWARE_VIDEO_DECODING","devices":[],"id":"73e4e12d-f27a-1113-b0e9-c05cb4dd78d2","last_modified":1480349134301,"details":{"who":".","created":"2016-05-23T15:45:21Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1266220","name":"10.18.10.3947 winnt 6.2","why":"."},"os":"WINNT 6.2","featureStatus":"BLOCKED_DRIVER_VERSION","schema":1480343737148},{"driverVersionComparator":"GREATER_THAN_OR_EQUAL","driverVersion":"7.0.0.0","vendor":"0x10de","blockID":"g37","enabled":true,"feature":"DIRECT3D_9_LAYERS","devices":[],"id":"fb507393-460e-685e-91de-1ce3b18ddbc0","last_modified":1480349134278,"details":{"who":"","created":"2011-03-14T14:11:51Z","bug":"","name":"<BlocklistGfx: None: WINNT 5.1 : 0x10de : >","why":""},"os":"WINNT 5.1","featureStatus":"BLOCKED_DRIVER_VERSION","schema":1480343737148},{"driverVersionComparator":"EQUAL","driverVersion":"8.982.0.0","vendor":"0x1022","blockID":"g146","enabled":true,"feature":"DIRECT2D","devices":[],"id":"e6984930-7969-3cbc-fbaa-0f350afe6f14","last_modified":1480349134255,"details":{"who":"All Firefox users who have these drivers installed.","created":"2012-09-24T08:23:33Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=792480","name":"ATI/AMD driver 8.982.0.0","why":"Some features in these drivers are causing frequent crashes in Firefox."},"os":"All","featureStatus":"BLOCKED_DRIVER_VERSION","schema":1480343737148},{"driverVersionComparator":"LESS_THAN","driverVersion":"15.201.1151.0","vendor":"0x1002","blockID":"g992","enabled":true,"feature":"DIRECT2D","devices":["0x6920","0x6921","0x6928","0x6929","0x692b","0x692f","0x6930","0x6938","0x6939","0x6900","0x6901","0x6902","0x6903","0x6907","0x7300","0x9870","0x9874","0x9875","0x9876","0x9877"],"id":"77b27d80-1ebe-04de-2db9-bb197acbac32","last_modified":1480349134232,"details":{"who":"Users who have these cards installed.","created":"2015-08-20T09:56:09Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1189266","name":"ATI cards on Windows 10","why":"Crashing."},"os":"WINNT 8.1","featureStatus":"BLOCKED_DRIVER_VERSION","schema":1480343737148},{"vendor":"0x1002","blockID":"g232","enabled":true,"feature":"WEBGL_MSAA","devices":[],"id":"8f8e9025-a9ec-5f1e-ae9f-aee28176bdf7","last_modified":1480349134210,"details":{"who":"All Firefox users.","created":"2012-11-30T12:49:56Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=809550","name":"Mac OS X WebGL anti-aliasing (AMD)","why":"Security problems."},"os":"Darwin 11","featureStatus":"BLOCKED_DEVICE","schema":1480343737148},{"driverVersionComparator":"EQUAL","driverVersion":"10.18.10.3947","vendor":"0x8086","blockID":"g1220","enabled":true,"feature":"HARDWARE_VIDEO_DECODING","devices":[],"id":"1f91fdd6-8b89-d948-d05a-86491c705ce2","last_modified":1480349134187,"details":{"who":".","created":"2016-05-23T15:46:34Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1266220","name":"10.18.10.3947 winnt 6.3","why":"."},"os":"WINNT 6.3","featureStatus":"BLOCKED_DRIVER_VERSION","schema":1480343737148},{"vendor":"0x8086","blockID":"g204","enabled":true,"feature":"WEBGL_MSAA","devices":[],"id":"33866148-1d41-2d4c-27b8-5f147467686b","last_modified":1480349134160,"details":{"who":"All Firefox users.","created":"2012-11-12T10:38:32Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=809550","name":"Mac OS X WebGL anti-aliasing","why":"Security problems."},"os":"Darwin 10","featureStatus":"BLOCKED_DEVICE","schema":1480343737148},{"vendor":"0x1002","blockID":"g278","enabled":true,"feature":"DIRECT2D","devices":["0x9802","0x9803","0x9803","0x9804","0x9805","0x9806","0x9807"],"id":"bccdbab1-c335-6f2e-6d0a-d563ccb2f1f4","last_modified":1480349134138,"details":{"who":"All Firefox users using these drivers.","created":"2013-02-13T14:50:33Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=840161","name":"AMD Radeon HD 6290/6300/6310/6320 for Direct2D","why":"Crashes"},"os":"WINNT 6.1","featureStatus":"BLOCKED_DEVICE","schema":1480343737148},{"driverVersionComparator":"EQUAL","driverVersion":"8.15.10.1892","vendor":"0x8086","blockID":"g1072","enabled":true,"devices":["0x2a42","0x2e22","0x2e12","0x2e32","0x0046"],"id":"d780a857-b80d-0049-981e-1a8d3ebe485e","last_modified":1480349134115,"details":{"who":".","created":"2015-12-21T16:00:50Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1207993","name":"Crash in igd10umd32.dll@0x18f35","why":"."},"os":"All","featureStatus":"BLOCKED_DRIVER_VERSION","schema":1480343737148},{"driverVersionComparator":"LESS_THAN_OR_EQUAL","driverVersion":"9.10.8.0","vendor":"0x1022","blockID":"g194","enabled":true,"feature":"DIRECT2D","devices":[],"id":"0afc5fe9-1f81-7fde-2424-0cb4d933c173","last_modified":1480349134090,"details":{"who":"All Firefox users.","created":"2012-11-02T16:35:37Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=806991","name":"AMD DIRECT2D on Windows 8, [0x1022]","why":"Crashes."},"os":"WINNT 6.2","featureStatus":"BLOCKED_DRIVER_VERSION","schema":1480343737148}]}PK
!<�!3;,;, defaults/blocklists/plugins.json{"data":[{"infoURL":"https://get.adobe.com/flashplayer/","matchName":"","blockID":"p1419","enabled":true,"matchFilename":"libflashplayer\\.so","last_modified":1486576001966,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2016-11-16T18:40:26Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1316179","name":"Flash Player Plugin on Linux 11.2.202.637 to 11.2.202.643 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates <a href=\"https://get.adobe.com/flashplayer/\">on Adobe's Flash page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"11.2.202.637","maxVersion":"11.2.202.643","severity":0,"vulnerabilityStatus":1}],"matchDescription":"","os":"Linux","id":"32d5edbf-0547-fa2d-8cc2-138676a17bc0","schema":1486575789918},{"infoURL":"https://get.adobe.com/flashplayer/","matchName":"","blockID":"p1420","enabled":true,"matchFilename":"(NPSWF32.*\\.dll)|(NPSWF64.*\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1486575979474,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2016-11-16T18:42:06Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1316179","name":"Flash Player Plugin 23.0.0.185 to 23.0.0.205 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates <a href=\"https://get.adobe.com/flashplayer/\">on Adobe's Flash page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"23.0.0.185","maxVersion":"23.0.0.205","severity":0,"vulnerabilityStatus":1}],"matchDescription":"","os":"","id":"24ac1e2b-8e0a-a084-5123-cb87d4ceb898","schema":1486575789918},{"infoURL":"https://get.adobe.com/flashplayer/","matchName":"","blockID":"p1274","enabled":true,"matchFilename":"(NPSWF32.*\\.dll)|(NPSWF64.*\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1486575908463,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2016-09-23T15:32:00Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1286380","name":"Flash Player Plugin 22.0.0.192 to 22.0.0.211 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates <a href=\"https://get.adobe.com/flashplayer/\">on Adobe's Flash page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"22.0.0.192","maxVersion":"22.0.0.211","severity":0,"vulnerabilityStatus":1}],"matchDescription":"","os":"","id":"6ef87143-6593-e234-a77c-6af69ad1567b","schema":1486575789918},{"infoURL":"https://get.adobe.com/flashplayer/","matchName":"","blockID":"p160","enabled":true,"matchFilename":"NPSWF32\\.dll","last_modified":1486575890885,"details":{"who":"All Firefox users who have this plugin installed.","created":"2012-10-05T12:34:22Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=797378","name":"Adobe Flash 10.2.* and lower","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates <a href=\"https://get.adobe.com/flashplayer/\">on Adobe's Flash page</a>."},"versionRange":[{"targetApplication":[{"minVersion":"4.0","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"16.*"}],"minVersion":"0","maxVersion":"10.2.9999","severity":0,"vulnerabilityStatus":1}],"matchDescription":"","os":"","id":"a61c906e-fe1f-b569-1807-96ffa59b2b6c","schema":1486575789918},{"infoURL":"https://get.adobe.com/flashplayer/","matchName":"","blockID":"p1422","enabled":true,"matchFilename":"(NPSWF32.*\\.dll)|(NPSWF64.*\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1486575869178,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2016-12-20T17:54:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1323300","name":"Flash Player Plugin 23.0.0.205 to 23.0.0.207 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates <a href=\"https://get.adobe.com/flashplayer/\">on Adobe's Flash page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"23.0.0.205","maxVersion":"23.0.0.207","severity":0,"vulnerabilityStatus":1}],"matchDescription":"","os":"","id":"ee3019e9-2572-ecb2-3de3-abc4842d7e0a","schema":1486575789918},{"infoURL":"https://get.adobe.com/flashplayer/","matchName":"","blockID":"p1421","enabled":true,"matchFilename":"libflashplayer\\.so","last_modified":1486575857101,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2016-12-20T17:52:30Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1323300","name":"Flash Player Plugin on Linux 11.2.202.643 to 23.0.0.207 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates <a href=\"https://get.adobe.com/flashplayer/\">on Adobe's Flash page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"11.2.202.643","maxVersion":"23.0.0.207","severity":0,"vulnerabilityStatus":1}],"matchDescription":"","os":"Linux","id":"c867bfd8-ec29-0800-8689-4b90b89fa870","schema":1486575789918},{"infoURL":"https://get.adobe.com/flashplayer/","matchName":"","blockID":"p1494","enabled":true,"matchFilename":"(NPSWF32.*\\.dll)|(NPSWF64.*\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1486575835268,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2017-01-17T20:58:38Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1330086","name":"Flash Player Plugin 23.0.0.207 to 24.0.0.186 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates <a href=\"https://get.adobe.com/flashplayer/\">on Adobe's Flash page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"23.0.0.207","maxVersion":"24.0.0.186","severity":0,"vulnerabilityStatus":1}],"matchDescription":"","os":"","id":"9234afc8-8f7f-602a-9c84-7d7f2685802a","schema":1486575789918},{"infoURL":"https://get.adobe.com/flashplayer/","matchName":"","blockID":"p1495","enabled":true,"matchFilename":"libflashplayer\\.so","last_modified":1486575789909,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2017-01-17T20:59:52Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1330086","name":"Flash Player Plugin on Linux 23.0.0.207 to 24.0.0.186 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates <a href=\"https://get.adobe.com/flashplayer/\">on Adobe's Flash page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"23.0.0.207","maxVersion":"24.0.0.186","severity":0,"vulnerabilityStatus":1}],"matchDescription":"","os":"Linux","id":"22b0fa08-8712-69cd-4ba4-9fb5ab9398d1","schema":1485795310261},{"blockID":"p152","enabled":true,"matchFilename":"npctrl\\.dll","last_modified":1480349149757,"details":{"who":"All Firefox users who have this plugin installed.","created":"2012-10-05T10:34:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=797378","name":"Silverlight 4.1.10328.0 and lower","why":"This plugin is outdated and is potentially insecure. Affected users should go to  <a href=\"http://www.mozilla.com/plugincheck/\">the plugin check page</a> and update to the latest version."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"4.1.10328.0","severity":0,"vulnerabilityStatus":1}],"id":"abdcbe90-5575-6b4b-12de-bbabd93ecf57","schema":1480349144394},{"infoURL":"https://java.com/","blockID":"p904","enabled":true,"matchFilename":"JavaAppletPlugin\\.plugin","last_modified":1480349149710,"details":{"who":"All users who have these versions of the plugin installed in Firefox.","created":"2015-05-19T09:01:42Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1159917","name":"Java Plugin 8 update 44 and lower (click-to-play), Mac OS X","why":"Old versions of the Java plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"Java 8","maxVersion":"Java 8 Update 44","severity":0,"vulnerabilityStatus":1}],"id":"47f6217d-0aa6-1e39-a9c9-cc64d57bb91f","schema":1480349144394},{"infoURL":"https://java.com/","matchName":"Java\\(TM\\) Platform SE 7 U(8[1-9]|90)(\\s[^\\d\\._U]|$)","blockID":"p1061","enabled":true,"matchFilename":"npjp2\\.dll","last_modified":1480349149684,"details":{"who":"All users who have these versions of the Java plugin installed.","created":"2015-12-02T12:39:41Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1217932","name":"Java Plugin 7 update 81 to 90 (click-to-play), Windows","why":"Old versions of the Java plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"severity":0,"vulnerabilityStatus":1}],"id":"69196ada-69bd-6454-eea8-6f8b2037368c","schema":1480349144394},{"infoURL":"https://www.microsoft.com/getsilverlight","blockID":"p1120","enabled":true,"matchFilename":"(Silverlight\\.plugin|npctrl\\.dll)","last_modified":1480349149661,"details":{"who":"All users who have these versions of the Silverlight plugin installed.","created":"2016-02-03T09:42:43Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1241237","name":"Silverlight plugin 5.1.41105.0 and lower (click to play)","why":"Old versions of the Silverlight plugin are known to have critical security vulnerabilities. You can get the latest version of Silverlight <a href=\"http://www.microsoft.com/getsilverlight/get-started/install/default.aspx\">here</a>."},"versionRange":[{"targetApplication":[],"minVersion":"5.1.20125","maxVersion":"5.1.41105.0","severity":0,"vulnerabilityStatus":1}],"id":"f0bfeb8c-04ee-d5ef-6670-b28c0e0d0f58","schema":1480349144394},{"blockID":"p102","enabled":true,"matchFilename":"npmozax\\.dll","last_modified":1480349149636,"details":{"who":"All Firefox users who have this plugin installed.","created":"2012-06-07T17:43:57Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=695927","name":"Mozilla ActiveX Plug-in","why":"This plugin was discontinued years ago and only worked up to Firefox version 1.5. Because plugins aren't marked as incompatible, there are still many users who have it installed and have recently been experiencing crashes related to it."},"versionRange":[{"minVersion":"0","maxVersion":"*","targetApplication":[]}],"id":"2e001e2f-483b-7595-8810-3672e0346950","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p1020","enabled":true,"matchFilename":"(NPSWF32.*\\.dll)|(NPSWF64.*\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1480349149604,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2015-09-25T14:51:38Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1193001","name":"Flash Player Plugin 13.* (click-to-play)","why":"The 13.* branch of the Flash Player plugin has been discontinued by Adobe and has known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"13.0","maxVersion":"13.*","severity":0,"vulnerabilityStatus":1}],"id":"2c53180f-e759-b36e-0c58-acee77a8087a","schema":1480349144394},{"blockID":"p252","enabled":true,"matchFilename":"AdobePDFViewerNPAPI\\.plugin","last_modified":1480349149567,"details":{"who":"All Firefox users on Mac OS X who have installed the Adobe Reader XI plugin. Users are recommended to update when a new version of Adobe Reader becomes available, or to downgrade to the latest version of Adobe Reader 10.","created":"2013-01-15T10:56:30Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=826002","name":"Adobe Reader XI for Mac OS X","why":"The Adobe Reader XI plugin is causing frequent crashes on Firefox for Mac OS X. The impact and this block are currently limited to versions 11.0.0 and 11.0.01."},"versionRange":[{"targetApplication":[],"minVersion":"11.0.0","maxVersion":"11.0.01","severity":1}],"os":"Darwin","id":"821ddb3d-bba4-6479-28b6-9974383fa8c9","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p1122","enabled":true,"matchFilename":"(NPSWF32.*\\.dll)|(NPSWF64.*\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1480349149544,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2016-02-17T16:26:57Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1247032","name":"Flash Player Plugin 18.0.0.268 to 18.0.0.326 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"18.0.0.268","maxVersion":"18.0.0.326","severity":0,"vulnerabilityStatus":1}],"id":"1c48b368-b291-62ef-c4b1-057cb63d54d8","schema":1480349144394},{"matchName":"Java\\(TM\\) Platform SE 6 U(39|40|41)(\\s[^\\d\\._U]|$)","blockID":"p300","enabled":true,"matchFilename":"npjp2\\.dll","last_modified":1480349149520,"details":{"who":"All users who have these versions of the plugin installed in Firefox.","created":"2013-02-25T12:36:25Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=843373","name":"Java Plugin 6 updates 39 to 41 (click-to-play), Windows","why":"Old versions of the Java plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[{"minVersion":"17.0","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"},{"minVersion":"2.14","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"*"}],"severity":0,"vulnerabilityStatus":1}],"id":"d64e08b6-66b2-f534-53d8-1cbcbb139f2a","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p826","enabled":true,"matchFilename":"libflashplayer\\.so","last_modified":1480349149496,"details":{"who":"All users who have these versions of the plugin installed in Firefox.","created":"2015-01-28T14:36:37Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1124654","name":"Flash Player Plugin on Linux 11.2.202.425 to 11.2.202.439 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"11.2.202.425","maxVersion":"11.2.202.439","severity":0,"vulnerabilityStatus":1}],"os":"Linux","id":"6738a409-5904-1031-56d4-6ce1b4eaccde","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p94","enabled":true,"matchFilename":"Flash\\ Player\\.plugin","last_modified":1480349149472,"details":{"who":"All Firefox users who have a version of the Flash Player Plugin older than 10.2.159.1.","created":"2012-05-23T09:15:23Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=754723","name":"Flash Player Plugin","why":"Old versions of the Flash Player plugin are targeted by known security vulnerabilities, and cause frequent Firefox crashes. We strongly recommend all users to visit <a href=\"http://get.adobe.com/flashplayer/\">the Flash Player homepage</a> to download and install the latest available version of this plugin.\r\n\r\nThis is not an ordinary block. It's just an upgrade notification that will be shown to all users who have these old versions installed, asking them to download the latest version. If they choose to ignore the notification, the plugin will continue to work normally."},"versionRange":[{"targetApplication":[{"minVersion":"0.1","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"17.0.1"},{"minVersion":"0","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"2.13.*"}],"minVersion":"0","maxVersion":"10.2.159.1","severity":0}],"id":"2a741cac-32d7-a67e-0bf7-b5b53a0ff22b","schema":1480349144394},{"blockID":"p32","enabled":true,"matchFilename":"npViewpoint.dll","last_modified":1480349149141,"details":{"who":"All users of the Viewpoint plugin for Firefox 3 and later.","created":"2011-03-31T16:28:26Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=539282","name":"Viewpoint","why":"This plugin causes a high volume of Firefox crashes."},"versionRange":[{"targetApplication":[{"minVersion":"3.0","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"}]}],"id":"1206e79a-4817-16e9-0f5e-7762a8d19216","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p1066","enabled":true,"matchFilename":"(NPSWF32.*\\.dll)|(NPSWF64.*\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1480349149112,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2015-12-21T13:43:47Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1231191","name":"Flash Player Plugin 18.0.0.255 to 18.0.0.261 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"18.0.0.255","maxVersion":"18.0.0.261","severity":0,"vulnerabilityStatus":1}],"id":"f05bf2c1-7b19-c7bc-5a67-7976f29311d6","schema":1480349144394},{"infoURL":"https://java.com/","blockID":"p180","enabled":true,"matchFilename":"JavaAppletPlugin\\.plugin","last_modified":1480349149087,"details":{"who":"All users who have these versions of the plugin installed in Firefox 17 and above.","created":"2012-10-30T14:29:51Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=829111","name":"Java Plugin 7 update 10 and lower (click-to-play), Mac OS X","why":"The Java plugin is causing significant security problems. All users are strongly recommended to keep the plugin disabled unless necessary."},"versionRange":[{"targetApplication":[{"minVersion":"17.0","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"},{"minVersion":"2.14","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"*"}],"minVersion":"Java 7 Update 0","maxVersion":"Java 7 Update 10","severity":0,"vulnerabilityStatus":1}],"id":"12f38c07-c1e1-2464-2eab-d454cf471eab","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p830","enabled":true,"matchFilename":"libflashplayer\\.so","last_modified":1480349149064,"details":{"who":"All users who have these versions of the plugin installed in Firefox.","created":"2015-02-05T15:38:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1128534","name":"Flash Player Plugin on Linux 11.2.202.439 to 11.2.202.441 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"11.2.202.439","maxVersion":"11.2.202.441","severity":0,"vulnerabilityStatus":1}],"os":"Linux","id":"17a664fb-d871-84e0-7418-dab7ecbd5f4b","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p828","enabled":true,"matchFilename":"(NPSWF32.*\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1480349149041,"details":{"who":"All users who have these versions of the plugin installed in Firefox.","created":"2015-01-28T14:39:04Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1124654","name":"Flash Player Plugin 15.0.0.243 to 16.0.0.287 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"15.0.0.243","maxVersion":"16.0.0.287","severity":0,"vulnerabilityStatus":1}],"id":"e84ceee7-9202-1321-eb49-c068806128a3","schema":1480349144394},{"matchDescription":"^($|Unity Web Player version 5.0(\\.([0-2]|3f1))?[^0-9.])","blockID":"p1004","enabled":true,"matchFilename":"Unity Web Player\\.plugin","last_modified":1480349149017,"details":{"who":"All users who have these versions of the plugin installed","created":"2015-09-09T14:11:57Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=974012","name":"Unity Web Player 5.0 to 5.0.3f1 (click-to-play), Mac OS","why":"These versions of the Unity Web Player plugin have known vulnerabilities that can put users at risk."},"versionRange":[{"targetApplication":[],"severity":0,"vulnerabilityStatus":1}],"id":"5a0b69cb-a516-e7a2-ea3b-b4fafcc13c6b","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p1415","enabled":true,"matchFilename":"(NPSWF32.*\\.dll)|(NPSWF64.*\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1480349148994,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2016-10-28T21:47:44Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1313435","name":"Flash Player Plugin 18.0.0.366 to 18.0.0.382 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"18.0.0.366","maxVersion":"18.0.0.382","severity":0,"vulnerabilityStatus":1}],"id":"36a133ef-3465-d7e4-6394-7b57e5f82dd8","schema":1480349144394},{"blockID":"p34","enabled":true,"matchFilename":"[Nn][Pp][Jj][Pp][Ii]1[56]0_[0-9]+\\.[Dd][Ll][Ll]","last_modified":1480349148971,"details":{"who":"Users of Java 2 Plugin versions 1.5_00 to 1.6_99 in Firefox 3.6 and later.","created":"2011-02-17T17:20:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=634639","name":"Java Plugin","why":"These versions of the Java plugin are no longer supported by Oracle and cause a high volume of Firefox crashes."},"versionRange":[{"targetApplication":[{"minVersion":"3.6a1pre","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"}]}],"id":"91618232-c650-5dc6-00fe-293c0baedc34","schema":1480349144394},{"blockID":"p248","enabled":true,"matchFilename":"Scorch\\.plugin","last_modified":1480349148945,"details":{"who":"All users who have Firefox 18 or above installed, and the Sibelius Scorch plugin.","created":"2013-01-14T09:26:47Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=829054","name":"Sibelius Scorch plugin","why":"The Sibelius Scorch plugin is not compatible with Firefox 18 and above, and is crashing whenever loaded on affected versions of Firefox. "},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"6.2.0b88","severity":1}],"id":"28b5baa1-6490-38e2-395f-53fc84aa12df","schema":1480349144394},{"blockID":"p254","enabled":true,"matchFilename":"PDF Browser Plugin\\.plugin","last_modified":1480349148920,"details":{"who":"All Firefox users on Mac OS X who are using the affected versions of the PDF Browser Plugin.","created":"2013-01-15T11:54:24Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=830410","name":"PDF Browser Plugin 2.4.2 and below","why":"The PDF Browser Plugin is causing frequent crashes on Firefox 18 and above. All users are recommended to keep the plugin disabled and update it if a new version becomes available."},"versionRange":[{"targetApplication":[{"minVersion":"18.0a1","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"}],"minVersion":"0","maxVersion":"2.4.2","severity":1}],"id":"2dd1b53a-52db-2d0f-392a-410d5aade9d6","schema":1480349144394},{"infoURL":"https://get.adobe.com/shockwave/","blockID":"p1055","enabled":true,"matchFilename":"DirectorShockwave\\.plugin","last_modified":1480349148897,"details":{"who":"All users who have these versions of this plugin installed.","created":"2015-11-13T14:17:24Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1218880","name":"Shockwave for Director 12.2.0.162 and earlier, Mac OS X (click-to-play)","why":"Versions 12.2.0.162 and earlier of this plugin are affected by a critical security vulnerability that puts users at risk."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"12.2.0.162","severity":0,"vulnerabilityStatus":1}],"id":"da3808c0-b460-e177-1c78-0496b3671480","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p928","enabled":true,"matchFilename":"(NPSWF32.*\\.dll)|(NPSWF64.*\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1480349148874,"details":{"who":"All users who have these versions of the plugin installed in Firefox.","created":"2015-06-30T14:09:48Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1177214","name":"Flash Player Plugin 13.0.0.269 to 13.0.0.295 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"13.0.0.269","maxVersion":"13.0.0.295","severity":0,"vulnerabilityStatus":1}],"id":"9ea82484-43fe-f039-bc0a-b254ca502357","schema":1480349144394},{"infoURL":"https://support.apple.com/en-us/HT205771","blockID":"p1151","enabled":true,"matchFilename":"npqtplugin\\.dll","last_modified":1480349148852,"details":{"who":"All users who have the QuickTime Plugin for Windows installed.","created":"2016-04-18T17:41:00Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1264874","name":"QuickTime Plugin for Windows","why":"The QuickTime Plugin for Windows has been <a href=\"https://support.apple.com/en-us/HT205771\">discontinued by Apple</a> and has known critical security vulnerabilities. All users are strongly encouraged to remove it or keep it disabled."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"*","severity":0,"vulnerabilityStatus":2}],"id":"bb8db302-9579-42d2-ff75-c61500f6a020","schema":1480349144394},{"blockID":"p28","enabled":true,"matchFilename":"NPFFAddOn.dll","last_modified":1480349148829,"details":{"who":"All users of Internet Saving Optimizer for all Mozilla applications.","created":"2011-03-31T16:28:26Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=512406","name":"Internet Saving Optimizer (plugin)","why":"This plugin causes a high volume of Firefox crashes and is considered malware."},"versionRange":[{"targetApplication":[],"maxVersion":"*"}],"id":"c85a4a9c-d61c-130f-d525-ea36becac235","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p1048","enabled":true,"matchFilename":"(NPSWF32.*\\.dll)|(NPSWF64.*\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1480349148806,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2015-10-21T14:13:42Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1214459","name":"Flash Player Plugin 19.0 to 19.0.0.225 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"19.0","maxVersion":"19.0.0.225","severity":0,"vulnerabilityStatus":1}],"id":"d57e46e8-2fca-f631-db45-99c5808ed4c0","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p832","enabled":true,"matchFilename":"(NPSWF32.*\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1480349148782,"details":{"who":"All users who have these versions of the plugin installed in Firefox.","created":"2015-02-05T15:41:17Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1128534","name":"Flash Player Plugin 16.0.0.295 to 16.0.0.304 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"16.0.0.295","maxVersion":"16.0.0.304","severity":0,"vulnerabilityStatus":1}],"id":"6105ecd0-5999-3754-8edb-6a00cef9d130","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p1046","enabled":true,"matchFilename":"(NPSWF32.*\\.dll)|(NPSWF64.*\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1480349148759,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2015-10-21T14:12:49Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1214459","name":"Flash Player Plugin 18.0.0.233 to 18.0.0.254 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"18.0.0.233","maxVersion":"18.0.0.254","severity":0,"vulnerabilityStatus":1}],"id":"e5ec5094-7dfc-4dc8-1e6a-93a3f94b32f8","schema":1480349144394},{"matchName":"Java\\(TM\\) Platform SE 7 U(1[6-9]|2[0-4])(\\s[^\\d\\._U]|$)","blockID":"p420","enabled":true,"matchFilename":"npjp2\\.dll","last_modified":1480349148736,"details":{"who":"All users who have these versions of the plugin installed in Firefox.","created":"2013-06-28T12:47:32Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=885362","name":"Java Plugin 7 update 16 to 24 (click-to-play), Windows","why":"Old versions of the Java plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[{"minVersion":"17.0","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"},{"minVersion":"2.14","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"*"}],"severity":0,"vulnerabilityStatus":1}],"id":"5942b7ae-bcdc-e329-9f17-f7a795c9ec83","schema":1480349144394},{"matchName":"Java\\(TM\\) Platform SE 7 U(2[5-9]|3\\d|4[0-4])(\\s[^\\d\\._U]|$)","blockID":"p458","enabled":true,"matchFilename":"npjp2\\.dll","last_modified":1480349148711,"details":{"who":"All users who have these versions of the plugin installed in Firefox.","created":"2013-10-16T16:29:18Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=927273","name":"Java Plugin 7 update 25 to 44 (click-to-play), Windows","why":"Old versions of the Java plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[{"minVersion":"17.0","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"},{"minVersion":"2.14","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"*"}],"severity":0,"vulnerabilityStatus":1}],"id":"d199d513-3c49-b53c-9447-33c8021c0df2","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p1252","enabled":true,"matchFilename":"libflashplayer\\.so","last_modified":1480349148687,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2016-07-25T13:22:01Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1286380","name":"Flash Player Plugin on Linux 11.2.202.621 to 11.2.202.626 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"11.2.202.621","maxVersion":"11.2.202.626","severity":0,"vulnerabilityStatus":1}],"os":"Linux","id":"7a8fdd50-4059-556f-5c6c-cb486d9239ba","schema":1480349144394},{"infoURL":"https://get.adobe.com/shockwave/","blockID":"p1054","enabled":true,"matchFilename":"np32dsw_[0-9]+\\.dll","last_modified":1480349148638,"details":{"who":"All users who have these versions of this plugin installed.","created":"2015-11-13T14:15:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1218880","name":"Shockwave for Director 12.2.0.162 and earlier, Windows (click-to-play)","why":"Versions 12.2.0.162 and earlier of this plugin are affected by a critical security vulnerability that puts users at risk."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"12.2.0.162","severity":0,"vulnerabilityStatus":1}],"id":"5cc4739b-ba83-7a19-4d85-c5362f5e0ccd","schema":1480349144394},{"blockID":"p298","enabled":true,"matchFilename":"JavaAppletPlugin\\.plugin","last_modified":1480349148613,"details":{"who":"All users who have these versions of the plugin installed in Firefox.","created":"2013-02-25T12:35:34Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=843373","name":"Java Plugin 6 updates 39 to 41 (click-to-play), Mac OS X","why":"Old versions of the Java plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>.\r\n"},"versionRange":[{"targetApplication":[{"minVersion":"17.0","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"},{"minVersion":"2.14","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"*"}],"minVersion":"Java 6 Update 39","maxVersion":"Java 6 Update 41","severity":0,"vulnerabilityStatus":1}],"id":"cfd76877-e79d-8e8c-c6a7-6b596fd344e8","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p1074","enabled":true,"matchFilename":"(NPSWF32.*\\.dll)|(NPSWF64.*\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1480349148566,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2016-01-07T12:52:22Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1235435","name":"Flash Player Plugin 19.0.0.246 to 20.0.0.235 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"19.0.0.246","maxVersion":"20.0.0.235","severity":0,"vulnerabilityStatus":1}],"id":"f47e2396-5eac-9458-dad1-2e8e91ccf0b5","schema":1480349144394},{"blockID":"p456","enabled":true,"matchFilename":"npvlc\\.dll","last_modified":1480349148527,"details":{"who":"All Firefox users who are using old versions of the VLC player plugin.","created":"2013-09-30T14:35:07Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=470936","name":"VLC Player plugin 2.0.5 and lower (click-to-play)","why":"Old versions of the VLC player are known to cause stability problems in Firefox. All users are recommended to update to the latest version of their VLC software."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"2.0.5","severity":0,"vulnerabilityStatus":1}],"id":"0cfaaefe-88a4-dda4-96bc-110e402ca9d5","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p1149","enabled":true,"matchFilename":"(NPSWF32.*\\.dll)|(NPSWF64.*\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1480349148205,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2016-04-13T14:26:57Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1263476","name":"Flash Player Plugin 18.0.0.329 to 18.0.0.333 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"18.0.0.329","maxVersion":"18.0.0.333","severity":0,"vulnerabilityStatus":1}],"id":"7c1cefd7-67ac-9ab5-90e9-1eb8d4b4275a","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p1224","enabled":true,"matchFilename":"libflashplayer\\.so","last_modified":1480349148157,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2016-05-31T15:18:16Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1275307","name":"Flash Player Plugin on Linux 11.2.202.577 to 11.2.202.616 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"11.2.202.577","maxVersion":"11.2.202.616","severity":0,"vulnerabilityStatus":1}],"os":"Linux","id":"fa1aab48-6b35-8e43-61ff-2902eeff4e86","schema":1480349144394},{"infoURL":"https://java.com/","matchName":"Java\\(TM\\) Platform SE 8 U(6[4-9]|7[0-6])(\\s[^\\d\\._U]|$)","blockID":"p1144","enabled":true,"matchFilename":"npjp2\\.dll","last_modified":1480349148133,"details":{"who":"All users who have these versions of the Java plugin installed.","created":"2016-03-31T16:18:15Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1259458","name":"Java Plugin 8 update 64 to 76 (click-to-play), Windows","why":"Old versions of the Java plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"severity":0,"vulnerabilityStatus":1}],"id":"b3c55844-79d2-66dd-0d44-82fb4533307e","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p1140","enabled":true,"matchFilename":"(NPSWF32.*\\.dll)|(NPSWF64.*\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1480349148109,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2016-03-18T15:35:14Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1255519","name":"Flash Player Plugin 20.0.0.286 to 20.0.0.306 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"20.0.0.286","maxVersion":"20.0.0.306","severity":0,"vulnerabilityStatus":1}],"id":"17697ff4-a8d9-db56-240f-704921c23b4b","schema":1480349144394},{"matchName":"Java\\(TM\\) Plug-in 1\\.7\\.0_1[2-5]([^\\d\\._]|$)","blockID":"p296","enabled":true,"matchFilename":"libnpjp2\\.so","last_modified":1480349148080,"details":{"who":"All users who have these versions of the plugin installed in Firefox.","created":"2013-02-25T12:34:37Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=843373","name":"Java Plugin 7 update 12 to 15 (click-to-play), Linux","why":"Old versions of the Java plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[{"minVersion":"17.0","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"},{"minVersion":"2.14","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"*"}],"severity":0,"vulnerabilityStatus":1}],"id":"5b7aaf34-48a3-dfa5-1db8-550faef41501","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p178","enabled":true,"matchFilename":"(NPSWF[0-9_]*\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1480349148055,"details":{"who":"All users who have these versions of the plugin installed in Firefox.","created":"2012-10-30T14:24:14Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=885517","name":"Flash Player Plugin between 11.0 and 11.7.700.169 (click-to-play)","why":"Old versions of the Flash Player plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[{"minVersion":"19.0a1","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"},{"minVersion":"2.16a1","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"*"},{"minVersion":"17.0.4","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"17.0.*"}],"minVersion":"11.0","maxVersion":"11.7.700.169","severity":0,"vulnerabilityStatus":1}],"id":"e4c7ad3c-6b35-2eb7-0c19-35be376c71dc","schema":1480349144394},{"blockID":"p408","enabled":true,"matchFilename":"QuickTime Plugin\\.plugin","last_modified":1480349148032,"details":{"who":"All Firefox users who have affected versions of the QuickTime plugin installed.","created":"2013-06-28T09:52:37Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=837377","name":"QuickTime Plugin 7.6.5 and lower (click-to-play), Mac OS X","why":"Old versions of the QuickTime plugin are known to be insecure and cause stability problems. All users are recommended to update to the latest version available. You can check for updates in the <a href=\"http://www.mozilla.org/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"7.6.5","severity":0,"vulnerabilityStatus":1}],"id":"4f39cd0f-976c-1f71-2d9c-2abe1b4706d9","schema":1480349144394},{"infoURL":"https://java.com/","blockID":"p1059","enabled":true,"matchFilename":"JavaAppletPlugin\\.plugin","last_modified":1480349148009,"details":{"who":"All users who have these versions of the Java plugin installed.","created":"2015-12-02T12:37:58Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1217932","name":"Java Plugin 7 update 81 to 90 (click-to-play), Mac OS X","why":"Old versions of the Java plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"Java 7 Update 81","maxVersion":"Java 7 Update 90","severity":0,"vulnerabilityStatus":1}],"id":"799c4f10-d75d-30a0-5570-56df6be628e0","schema":1480349144394},{"matchDescription":"^($|Unity Web Player version ([0-3]|(4\\.([0-5]|6(\\.([0-5]|6f1)))?[^0-9.])))","blockID":"p558","enabled":true,"matchFilename":"Unity Web Player\\.plugin","last_modified":1480349147963,"details":{"who":"All Firefox users who have these versions of the plugin installed.","created":"2014-02-25T08:29:51Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=974012","name":"Unity Web Player 4.6.6f1 and lower (click-to-play), Mac OS","why":"Current versions of the Unity Web Player plugin have known vulnerabilities that can put users at risk."},"versionRange":[{"targetApplication":[],"severity":0,"vulnerabilityStatus":1}],"id":"bbf83e70-65f5-75af-b841-7cebcf3e38c1","schema":1480349144394},{"infoURL":"https://java.com/","matchName":"Java(\\(TM\\))? Plug-in 10\\.(9[1-7])(\\.[0-9]+)?([^\\d\\._]|$)","blockID":"p1145","enabled":true,"matchFilename":"libnpjp2\\.so","last_modified":1480349147940,"details":{"who":"All users who have these versions of the Java plugin installed.","created":"2016-03-31T16:18:53Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1259458","name":"Java Plugin 7 update 91 to 97 (click-to-play), Linux","why":"Old versions of the Java plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"severity":0,"vulnerabilityStatus":1}],"id":"d4e75c8c-9c32-6093-12cc-1de0fd8f9e87","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p1234","enabled":true,"matchFilename":"libflashplayer\\.so","last_modified":1480349147915,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2016-06-23T12:52:14Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1280125","name":"Flash Player Plugin on Linux 11.2.202.616 to 11.2.202.621 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"11.2.202.616","maxVersion":"11.2.202.621","severity":0,"vulnerabilityStatus":1}],"os":"Linux","id":"a02005bf-ad55-ba8f-265a-532e874142d0","schema":1480349144394},{"matchName":"[0-6]\\.0\\.[01]\\d{2}\\.\\d+","blockID":"p33","enabled":true,"matchFilename":"npdeploytk.dll","last_modified":1480349147892,"details":{"who":"Users of Java Deployment Toolkit versions 6.0.200.0 and older in all versions of Firefox.","created":"2010-04-16T17:52:32Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=558584","name":"Java Deployment Toolkit","why":"This plugin has publicly-known security vulnerabilities. For more information, please <a href=\"http://java.com/en/download/help/firefox_addons.xml\">visit the vendor page</a>."},"versionRange":[{"targetApplication":[],"maxVersion":"","severity":1}],"id":"b12b4282-d327-06d6-6e62-4788ba2c4b2f","schema":1480349144394},{"infoURL":"https://java.com/","blockID":"p1060","enabled":true,"matchFilename":"JavaAppletPlugin\\.plugin","last_modified":1480349147868,"details":{"who":"All users who have these versions of the Java plugin installed.","created":"2015-12-02T12:38:53Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1217932","name":"Java Plugin 8 update 46 to 64 (click-to-play), Mac OS X","why":"Old versions of the Java plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"Java 8 Update 46","maxVersion":"Java 8 Update 64","severity":0,"vulnerabilityStatus":1}],"id":"aee0ad26-018f-392b-91f7-6a8aaf332774","schema":1480349144394},{"blockID":"p89","enabled":true,"matchFilename":"AdobePDFViewerNPAPI\\.plugin","last_modified":1480349147844,"details":{"who":"Firefox users on Mac OS X who have installed the Adobe Acrobat plugin.","created":"2012-05-04T11:29:46Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=750387","name":"Adobe Acrobat NPAPI Plug-in","why":"The Adobe Acrobat plugin doesn't work on Mac OS X in default (64-bit) mode, showing a blank page when users click on links to PDF files in Firefox. It also causes a significant number of crashes in 32-bit mode.\r\n\r\nThere's more information on <a href=\"http://blog.mozilla.org/addons/2012/05/04/adobe-reader-blocked-mac/\">this blog post</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"10.1.3","severity":1}],"os":"Darwin","id":"473f4d9c-e38e-a348-26d1-de7b842893d9","schema":1480349144394},{"matchName":"Java\\(TM\\) Plug-in 1\\.7\\.0(_0?([5-6]))?([^\\d\\._]|$)","blockID":"p132","enabled":true,"matchFilename":"libnpjp2\\.so","last_modified":1480349147820,"details":{"who":"All Firefox users who have the Java 7 plugin, updates 6 and below.","created":"2012-08-31T15:21:34Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=794247","name":"Java Plugin","why":"The Java 7 Runtime Environment, update versions 6 and below, has a <a href=\"https://blog.mozilla.org/security/2012/08/28/protecting-users-against-java-security-vulnerability/\">serious security vulnerability</a> that is fixed in the latest update. All Firefox users are strongly encouraged to update as soon as possible. This can be done on the <a href=\"http://www.mozilla.com/plugincheck/\">Plugin Check</a> page."},"versionRange":[{"targetApplication":[{"minVersion":"0.1","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"17.*"},{"minVersion":"0.1","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"2.14.*"}],"severity":1}],"id":"572d0485-3388-7c2a-a062-b062e42c8710","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","matchDescription":"^Shockwave Flash (([1-9]\\.[0-9]+)|(10\\.([0-2]|(3 r(([0-9][0-9]?)|1(([0-7][0-9])|8[0-2]))))))( |$)","blockID":"p330","enabled":true,"matchFilename":"libflashplayer\\.so","last_modified":1480349147797,"details":{"who":"All Firefox users who have these versions on the Abode Flash plugin installed.","created":"2013-03-26T09:43:25Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=854550","name":"Adobe Flash for Linux 10.3.182.* and lower (click-to-play)","why":"Old versions of the Adobe Flash plugin are potentially insecure and unstable. All users are recommended to visit our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check</a> page and check for updates."},"versionRange":[{"targetApplication":[{"minVersion":"19.0a1","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"},{"minVersion":"2.16a1","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"*"},{"minVersion":"17.0.4","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"17.0.*"}],"severity":0,"vulnerabilityStatus":1}],"id":"abaff0eb-314f-e882-b96c-5c5a4755688f","schema":1480349144394},{"matchName":"Java\\(TM\\) Plug-in 1\\.6\\.0_(39|40|41)([^\\d\\._]|$)","blockID":"p302","enabled":true,"matchFilename":"libnpjp2\\.so","last_modified":1480349147773,"details":{"who":"All users who have these versions of the plugin installed in Firefox.","created":"2013-02-25T12:37:17Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=843373","name":"Java Plugin 6 updates 39 to 41 (click-to-play), Linux","why":"Old versions of the Java plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>.\r\n"},"versionRange":[{"targetApplication":[{"minVersion":"17.0","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"},{"minVersion":"2.14","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"*"}],"severity":0,"vulnerabilityStatus":1}],"id":"caec8103-dec9-8018-eb3d-9cf21cbf68a6","schema":1480349144394},{"blockID":"p129","enabled":true,"matchFilename":"Silverlight\\.plugin","last_modified":1480349147746,"details":{"who":"All Firefox users on Mac OS X who have old versions of the Silverlight plugin.","created":"2012-08-22T10:37:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=782672","name":"Silverlight for Mac OS X","why":"Old versions of the Silverlight plugin for Mac OS X are causing serious stability problems in Firefox.  Affected users can visit <a href=\"https://www.microsoft.com/getsilverlight/Get-Started/Install/Default.aspx\">the official Silverlight page</a> and get the latest version of the Silverlight plugin to correct this problem."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"5.0.99999","severity":1}],"id":"ab861311-9c24-5f55-0d3a-f0072868357c","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p930","enabled":true,"matchFilename":"(NPSWF32.*\\.dll)|(NPSWF64.*\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1480349147722,"details":{"who":"All users who have these versions of the plugin installed in Firefox.","created":"2015-06-30T14:10:49Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1177214","name":"Flash Player Plugin 16.0.0.305 to 18.0.0.193 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"16.0.0.305","maxVersion":"18.0.0.193","severity":0,"vulnerabilityStatus":1}],"id":"f461aaf7-128b-ad5a-07a9-aba1ddde8a5c","schema":1480349144394},{"infoURL":"https://java.com/","blockID":"p1142","enabled":true,"matchFilename":"JavaAppletPlugin\\.plugin","last_modified":1480349147694,"details":{"who":"All users who have these versions of the Java plugin installed.","created":"2016-03-31T16:16:46Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1259458","name":"Java Plugin 8 update 64 to 76 (click-to-play), Mac OS X","why":"Old versions of the Java plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"Java 8 Update 64","maxVersion":"Java 8 Update 76","severity":0,"vulnerabilityStatus":1}],"id":"646ebafe-dd54-e45f-9569-c245ef72259d","schema":1480349144394},{"blockID":"p212","enabled":true,"matchFilename":"JavaAppletPlugin\\.plugin","last_modified":1480349147671,"details":{"who":"All Firefox users who have the Java 7 plugin, updates 7 and below.","created":"2012-11-22T09:33:07Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=812896","name":"Java Plugin 1.7u7 (Mac OS X)","why":"The Java 7 Runtime Environment, update version 7, has a <a href=\"http://www.oracle.com/technetwork/topics/security/javacpuoct2012-1515924.html\">serious security vulnerability</a> that is fixed in the latest update. All Firefox users are strongly encouraged to update as soon as possible. This can be done on the <a href=\"http://www.mozilla.com/plugincheck/\">Plugin Check</a> page."},"versionRange":[{"targetApplication":[{"minVersion":"0.1","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"17.*"},{"minVersion":"0.1","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"2.14.*"}],"minVersion":"Java 7 Update 07","maxVersion":"Java 7 Update 07","severity":1}],"id":"5ec1cd0f-d478-e6e0-989e-c91694b2a411","schema":1480349144394},{"blockID":"p250","enabled":true,"matchFilename":"npFoxitReaderPlugin\\.dll","last_modified":1480349147648,"details":{"who":"All Firefox users on Windows who have installed the Foxit Reader Plugin, versions 2.2.1.530 and below.","created":"2013-01-14T13:07:03Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=828982","name":"Foxit Reader Plugin 2.2.1.530 and below","why":"The Foxit Reader plugin is vulnerable to a <a href=\"http://secunia.com/advisories/51733/\">critical security bug</a> that can compromise a user's system by visiting a malicious site."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"2.2.1.530","severity":0,"vulnerabilityStatus":2}],"id":"c6299be0-ab77-8bbd-bcaf-c886cec4e799","schema":1480349144394},{"blockID":"p85","enabled":true,"matchFilename":"JavaPlugin2_NPAPI\\.plugin","last_modified":1480349147625,"details":{"who":"All Firefox users who have installed the Java plugin, JRE versions below 1.6.0_31 or between 1.7.0 and 1.7.0_2.","created":"2012-04-16T13:58:43Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=741592","name":"Java Plugin","why":"Outdated versions of the Java plugin are vulnerable to an actively exploited security issue. All Mac OS X users are strongly encouraged to update their Java plugin through Software Update, or disable it if no alternatives are available. For more information, please <a href=\"http://blog.mozilla.org/addons/2012/04/30/java-block-complete-for-mac-os-x\">read our blog post</a> or <a href=\"http://www.oracle.com/technetwork/topics/security/javacpufeb2012-366318.html\">Oracle's Advisory</a>.\r\n\r\n"},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"13.6.0","severity":1}],"id":"4df1324d-8236-b7ae-db55-4fb1b7db2754","schema":1480349144394},{"infoURL":"https://get.adobe.com/reader","blockID":"p1250","enabled":true,"matchFilename":"(nppdf32\\.dll)|(AdobePDFViewerNPAPI\\.plugin)","last_modified":1480349147602,"details":{"who":"All users who have this plugin installed.","created":"2016-07-21T22:22:11Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1286972","name":"Adobe Reader (Continuous) 15.016.20045","why":"Old versions of the Adobe Reader plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"15.016.20045","maxVersion":"15.016.20045","severity":0,"vulnerabilityStatus":1}],"id":"b9f6998a-7a45-7d83-7ee6-897e6b193e42","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p1225","enabled":true,"matchFilename":"(NPSWF32.*\\.dll)|(NPSWF64.*\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1480349147283,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2016-05-31T15:19:20Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1275307","name":"Flash Player Plugin 18.0.0.333 to 18.0.0.343 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"18.0.0.333","maxVersion":"18.0.0.343","severity":0,"vulnerabilityStatus":1}],"id":"e512821f-6174-f3c0-8369-2f3148cc586c","schema":1480349144394},{"infoURL":"https://real.com/","blockID":"p1053","enabled":true,"matchFilename":"nprpplugin\\.dll","last_modified":1480349147260,"details":{"who":"All users who have these versions of the Real Player plugin. Affected users can visit our <a href=\"http://www.mozilla.org/plugincheck/\">plugin check page</a> and check for updates.","created":"2015-11-12T09:07:04Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1222130","name":"Real Player for Windows, 17.0.10.7 and lower, click-to-play","why":"Old versions of the Real Player plugin have serious security problems that could lead to system compromise."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"17.0.10.7","severity":0,"vulnerabilityStatus":1}],"id":"2a6a7300-fac0-6518-c44a-35b3c4c714c3","schema":1480349144394},{"infoURL":"https://java.com/","blockID":"p1141","enabled":true,"matchFilename":"JavaAppletPlugin\\.plugin","last_modified":1480349147237,"details":{"who":"All users who have these versions of the Java plugin installed.","created":"2016-03-31T16:16:02Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1259458","name":"Java Plugin 7 update 91 to 97 (click-to-play), Mac OS X","why":"Old versions of the Java plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"Java 7 Update 91","maxVersion":"Java 7 Update 97","severity":0,"vulnerabilityStatus":1}],"id":"2a432d6e-9545-66be-9f93-aefe9793f450","schema":1480349144394},{"blockID":"p422","enabled":true,"matchFilename":"JavaAppletPlugin\\.plugin","last_modified":1480349147214,"details":{"who":"All users who have these versions of the plugin installed in Firefox.","created":"2013-06-28T12:48:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=885362","name":"Java Plugin 7 update 16 to 24 (click-to-play), Mac OS X","why":"Old versions of the Java plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[{"minVersion":"17.0","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"},{"minVersion":"2.14","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"*"}],"minVersion":"Java 7 Update 16","maxVersion":"Java 7 Update 24","severity":0,"vulnerabilityStatus":1}],"id":"709960a7-d268-0c70-d6ad-17bbed0cd1c4","schema":1480349144394},{"matchName":"Java\\(TM\\) Platform SE 7 U([0-9]|(1[0-1]))(\\s[^\\d\\._U]|$)","blockID":"p182","enabled":true,"matchFilename":"npjp2\\.dll","last_modified":1480349147191,"details":{"who":"All users who have these versions of the plugin installed in Firefox 17 and above.","created":"2012-10-30T14:33:04Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=829111","name":"Java Plugin 7 update 11 and lower (click-to-play), Windows","why":"The Java plugin is causing significant security problems. All users are strongly recommended to keep the plugin disabled unless necessary."},"versionRange":[{"targetApplication":[{"minVersion":"17.0","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"},{"minVersion":"2.14","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"*"}],"severity":0,"vulnerabilityStatus":1}],"id":"385d9911-f8bc-83e0-8cd2-94c98087938c","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p796","enabled":true,"matchFilename":"libflashplayer\\.so","last_modified":1480349147169,"details":{"who":"All users who have these versions of the plugin installed in Firefox.","created":"2014-12-11T12:17:01Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1109795","name":"Flash Player Plugin on Linux 11.2.202.424 and lower (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities (CVE-2014-9163). All users are strongly recommended to <a href=\"https://get.adobe.com/flashplayer/\">update their Flash plugin</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"11.2.202.424","severity":0,"vulnerabilityStatus":1}],"os":"Linux","id":"db6eaad4-d391-8ef3-a5df-e7219a758682","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p1075","enabled":true,"matchFilename":"(NPSWF32.*\\.dll)|(NPSWF64.*\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1480349147146,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2016-01-07T12:53:43Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1235435","name":"Flash Player Plugin 18.0.0.262 to 18.0.0.268 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"18.0.0.262","maxVersion":"18.0.0.268","severity":0,"vulnerabilityStatus":1}],"id":"c420fbcd-1a58-e6ba-5c85-f4a189cf7d90","schema":1480349144394},{"matchName":"Java\\(TM\\) Plug-in 1\\.7\\.0_(1[6-9]|2[0-4])([^\\d\\._]|$)","blockID":"p418","enabled":true,"matchFilename":"libnpjp2\\.so","last_modified":1480349147123,"details":{"who":"All users who have these versions of the plugin installed in Firefox.","created":"2013-06-28T12:46:18Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=885362","name":"Java Plugin 7 update 16 to 24 (click-to-play), Linux","why":"Old versions of the Java plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[{"minVersion":"17.0","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"},{"minVersion":"2.14","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"*"}],"severity":0,"vulnerabilityStatus":1}],"id":"ee5c1584-0170-8702-5f99-e0325b4a91a8","schema":1480349144394},{"blockID":"p158","enabled":true,"matchFilename":"nppdf32\\.dll","last_modified":1480349147100,"details":{"who":"All Firefox users who have this plugin installed.","created":"2012-10-05T10:40:20Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=850745","name":"Adobe Reader 10.0 to 10.1.5.*","why":"This plugin is outdated and is potentially insecure. Affected users should go to  <a href=\"http://www.mozilla.com/plugincheck/\">the plugin check page</a> and update to the latest version."},"versionRange":[{"targetApplication":[],"minVersion":"10.0","maxVersion":"10.1.5.9999","severity":0,"vulnerabilityStatus":1}],"id":"107c9a8c-2ef8-da26-75c3-bc26f8ae90fa","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p1026","enabled":true,"matchFilename":"(NPSWF32.*\\.dll)|(NPSWF64.*\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1480349147077,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2015-10-01T09:48:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1206889","name":"Flash Player Plugin 18.0.0.204 to 18.0.0.232 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"18.0.0.204","maxVersion":"18.0.0.232","severity":0,"vulnerabilityStatus":1}],"id":"6c349faf-109f-4456-4d45-b793e3b7e20e","schema":1480349144394},{"infoURL":"https://java.com/","matchName":"Java\\(TM\\) Platform SE 8 U45(\\s[^\\d\\._U]|$)","blockID":"p960","enabled":true,"matchFilename":"npjp2\\.dll","last_modified":1480349147055,"details":{"who":"All users who have these versions of the Java plugin installed.","created":"2015-07-15T10:50:25Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1183369","name":"Java Plugin 8 update 45 (click-to-play), Windows","why":"Old versions of the Java plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"severity":0,"vulnerabilityStatus":1}],"id":"990fa997-1d4a-98ee-32de-a7471e81db42","schema":1480349144394},{"blockID":"p113","enabled":true,"matchFilename":"npuplaypc\\.dll","last_modified":1480349147031,"details":{"who":"All Firefox users who have this plugin installed.","created":"2012-07-30T12:11:59Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=778686","name":"Ubisoft Uplay","why":"Version 1.0.0.0 of the Ubisoft Uplay plugin has a security vulnerability that can be exploited by malicious websites to gain control of the user's system."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"1.0.0.0","severity":1}],"id":"ea3b767d-933b-3f54-f447-09bd2bfbc6e0","schema":1480349144394},{"blockID":"p366","enabled":true,"matchFilename":"Scorch\\.plugin","last_modified":1480349147009,"details":{"who":"All users who have Firefox 18 or above installed, and the Sibelius Scorch plugin.","created":"2013-06-11T13:20:32Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=879128","name":"Sibelius Scorch plugin","why":"The Sibelius Scorch plugin is not compatible with Firefox 18 and above, and is crashing whenever loaded on affected versions of Firefox. "},"versionRange":[{"targetApplication":[],"minVersion":"6.2.0","maxVersion":"6.2.0","severity":1}],"id":"43ec430a-75ec-6853-f62b-1a54489fea5f","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p1067","enabled":true,"matchFilename":"(NPSWF32.*\\.dll)|(NPSWF64.*\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1480349146986,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2015-12-21T13:44:44Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1231191","name":"Flash Player Plugin 19.0.0.226 to 19.0.0.245 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"19.0.0.226","maxVersion":"19.0.0.245","severity":0,"vulnerabilityStatus":1}],"id":"ae2e1608-eaa4-68de-521e-9ffe22a9e3a4","schema":1480349144394},{"matchName":"Java\\(TM\\) Plug-in 1\\.6\\.0_3[1-8]([^\\d\\._]|$)","blockID":"p190","enabled":true,"matchFilename":"libnpjp2\\.so","last_modified":1480349146961,"details":{"who":"All users who have these versions of the plugin installed in Firefox 17 and above.","created":"2012-10-30T14:55:37Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=829111","name":"Java Plugin 6 updates 31 through 38 (click-to-play), Linux","why":"The Java plugin is causing significant security problems. All users are strongly recommended to keep the plugin disabled unless necessary."},"versionRange":[{"targetApplication":[{"minVersion":"17.0","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"},{"minVersion":"2.14","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"*"}],"severity":0,"vulnerabilityStatus":1}],"id":"9f9eb0ae-6495-aaa6-041a-d802cdb8e134","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p1076","enabled":true,"matchFilename":"libflashplayer\\.so","last_modified":1480349146938,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2016-01-07T12:54:49Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1235435","name":"Flash Player Plugin on Linux 11.2.202.549 to 11.2.202.554 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"11.2.202.549","maxVersion":"11.2.202.554","severity":0,"vulnerabilityStatus":1}],"os":"Linux","id":"0beab50c-0ded-c200-557a-f759db85d8a1","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p1253","enabled":true,"matchFilename":"(NPSWF32.*\\.dll)|(NPSWF64.*\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1480349146913,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2016-07-25T13:22:57Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1286380","name":"Flash Player Plugin 18.0.0.352 to 18.0.0.360 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"18.0.0.352","maxVersion":"18.0.0.360","severity":0,"vulnerabilityStatus":1}],"id":"84338b2a-bdee-c124-713e-5b5df3407882","schema":1480349144394},{"infoURL":"https://java.com/","matchName":"Java(\\(TM\\))? Plug-in 11\\.45(\\.[0-9]+)?([^\\d\\._]|$)","blockID":"p964","enabled":true,"matchFilename":"libnpjp2\\.so","last_modified":1480349146890,"details":{"who":"All users who have these versions of the Java plugin installed.","created":"2015-07-15T10:51:57Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1183369","name":"Java Plugin 8 update 45 (click-to-play), Linux","why":"Old versions of the Java plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"severity":0,"vulnerabilityStatus":1}],"id":"c2431673-3478-f1e1-5555-28e2d5377adc","schema":1480349144394},{"blockID":"p1002","enabled":true,"matchFilename":"npUnity3D32\\.dll","last_modified":1480349146867,"details":{"who":"All users who have these versions of the plugin installed.","created":"2015-09-09T14:07:27Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=974012","name":"Unity Web Player 5.0 to 5.0.3f1 (click-to-play), Windows","why":"These versions of the Unity Web Player plugin have known vulnerabilities that can put users at risk."},"versionRange":[{"targetApplication":[],"minVersion":"5.0","maxVersion":"5.0.3f1","severity":0,"vulnerabilityStatus":1}],"id":"48e45eea-fd32-2304-2776-fe75211a69e5","schema":1480349144394},{"blockID":"p123","enabled":true,"matchFilename":"JavaPlugin2_NPAPI\\.plugin","last_modified":1480349146842,"details":{"who":"All Firefox users who have installed the Java plugin, JRE versions below 1.6.0_33 or between 1.7.0 and 1.7.0_4.","created":"2012-08-14T09:29:42Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=780717","name":"Java Plugin","why":"Outdated versions of the Java plugin are vulnerable to an actively exploited security issue. All users are strongly encouraged to <a href=\"http://java.com/inc/BrowserRedirect1.jsp\">update their Java plugin</a>. For more information, please <a href=\"http://blog.mozilla.org/addons/2012/08/14/new-java-blocklist/\">read our blog post</a> or <a href=\"http://www.oracle.com/technetwork/topics/security/javacpujun2012-1515912.html\">Oracle's Advisory</a>."},"versionRange":[{"targetApplication":[{"minVersion":"0.1","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"17.*"},{"minVersion":"0.1","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"2.14.*"}],"minVersion":"0","maxVersion":"14.2.0","severity":1}],"id":"46bf36a7-1934-38f8-1fcc-c9c4bcc8343e","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p1148","enabled":true,"matchFilename":"(NPSWF32.*\\.dll)|(NPSWF64.*\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1480349146818,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2016-04-13T14:25:14Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1263476","name":"Flash Player Plugin 20.0.0.306 to 21.0.0.197 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"20.0.0.306","maxVersion":"21.0.0.197","severity":0,"vulnerabilityStatus":1}],"id":"a3470c77-4449-48e3-f234-eeb93f702821","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p1235","enabled":true,"matchFilename":"(NPSWF32.*\\.dll)|(NPSWF64.*\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1480349146794,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2016-06-23T12:54:00Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1280125","name":"Flash Player Plugin 18.0.0.343 to 18.0.0.352 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"18.0.0.343","maxVersion":"18.0.0.352","severity":0,"vulnerabilityStatus":1}],"id":"a435cc3b-9363-c7c3-e856-a0128966c809","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p1150","enabled":true,"matchFilename":"libflashplayer\\.so","last_modified":1480349146772,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2016-04-13T14:28:17Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1263476","name":"Flash Player Plugin on Linux 11.2.202.569 to 11.2.202.577 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"11.2.202.569","maxVersion":"11.2.202.577","severity":0,"vulnerabilityStatus":1}],"os":"Linux","id":"f52d9f17-7676-2aad-48b8-af97f281fcb2","schema":1480349144394},{"matchName":"Java\\(TM\\) Platform SE 7 U[5-6](\\s[^\\d\\._U]|$)","blockID":"p134","enabled":true,"matchFilename":"npjp2\\.dll","last_modified":1480349146749,"details":{"who":"All Firefox users who have the Java 7 plugin, update 6 and below.","created":"2012-08-31T15:22:51Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=794247","name":"Java Plugin","why":"The Java 7 Runtime Environment, update versions 6 and below, has a <a href=\"https://blog.mozilla.org/security/2012/08/28/protecting-users-against-java-security-vulnerability/\">serious security vulnerability</a> that is fixed in the latest update. All Firefox users are strongly encouraged to update as soon as possible. This can be done on the <a href=\"http://www.mozilla.com/plugincheck/\">Plugin Check</a> page."},"versionRange":[{"targetApplication":[{"minVersion":"0.1","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"17.*"},{"minVersion":"0.1","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"2.14.*"}],"severity":1}],"id":"1f519b98-a4b6-2a98-b1ff-b642f7b4d2bb","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p948","enabled":true,"matchFilename":"libflashplayer\\.so","last_modified":1480349146726,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2015-07-13T16:02:09Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1182751","name":"Flash Player Plugin on Linux 11.2.202.481 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"11.2.202.481","maxVersion":"11.2.202.481","severity":0,"vulnerabilityStatus":1}],"os":"Linux","id":"bcfe2592-6154-fcc7-cf0f-574eda317900","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p944","enabled":true,"matchFilename":"(NPSWF32.*\\.dll)|(NPSWF64.*\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1480349146378,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2015-07-13T16:00:02Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1182751","name":"Flash Player Plugin 13.0.0.302 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"13.0.0.302","maxVersion":"13.0.0.302","severity":0,"vulnerabilityStatus":1}],"id":"1fc0a5c5-fcfb-dfb4-9950-9e2f0b064010","schema":1480349144394},{"matchName":"Java\\(TM\\) Platform SE ((6( U(\\d|([0-2]\\d)|3[0-2]))?)|(7(\\sU[0-4])?))(\\s[^\\d\\._U]|$)","blockID":"p125","enabled":true,"matchFilename":"npjp2\\.dll","last_modified":1480349146351,"details":{"who":"All Firefox users who have installed the Java plugin, JRE versions below 1.6.0_33 or between 1.7.0 and 1.7.0_4.","created":"2012-08-14T09:31:17Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=780717","name":"Java Plugin","why":"Outdated versions of the Java plugin are vulnerable to an actively exploited security issue. All users are strongly encouraged to <a href=\"http://java.com/inc/BrowserRedirect1.jsp\">update their Java plugin</a>. For more information, please <a href=\"http://blog.mozilla.org/addons/2012/08/14/new-java-blocklist/\">read our blog post</a> or <a href=\"http://www.oracle.com/technetwork/topics/security/javacpujun2012-1515912.html\">Oracle's Advisory</a>."},"versionRange":[{"targetApplication":[{"minVersion":"0.1","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"17.*"},{"minVersion":"0.1","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"2.14.*"}],"severity":1}],"id":"7538347d-ca2e-09b8-c5d8-0a9d140f8c87","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p290","enabled":true,"matchFilename":"(NPSWF32\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1480349146327,"details":{"who":"All users who have these versions of the plugin installed in Firefox.","created":"2013-02-25T10:15:59Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=807258","name":"Flash Player Plugin 10.3.183.19 to 10.3.183.66 (click-to-play)","why":"Old versions of the Flash Player plugin are potentially insecure and unstable. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[{"minVersion":"19.0a1","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"},{"minVersion":"2.16a1","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"*"},{"minVersion":"17.0.4","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"17.0.*"}],"minVersion":"10.3.183.19","maxVersion":"10.3.183.66","severity":0,"vulnerabilityStatus":1}],"id":"b1177bd1-a518-b86a-8825-e4c91d2362de","schema":1480349144394},{"blockID":"p292","enabled":true,"matchFilename":"JavaAppletPlugin\\.plugin","last_modified":1480349146304,"details":{"who":"All users who have these versions of the plugin installed in Firefox.","created":"2013-02-25T12:32:41Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=843373","name":"Java Plugin 7 update 12 to 15 (click-to-play), Mac OS X","why":"Old versions of the Java plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[{"minVersion":"17.0","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"},{"minVersion":"2.14","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"*"}],"minVersion":"Java 7 Update 12","maxVersion":"Java 7 Update 15","severity":0,"vulnerabilityStatus":1}],"id":"9a09fe22-c3d8-57e6-4e99-ecd06fb03081","schema":1480349144394},{"blockID":"p188","enabled":true,"matchFilename":"JavaAppletPlugin\\.plugin","last_modified":1480349146280,"details":{"who":"All users who have these versions of the plugin installed in Firefox 17 and above.","created":"2012-10-30T14:48:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=829111","name":"Java Plugin 6 updates 38 and lower (click-to-play), Mac OS X","why":"The Java plugin is causing significant security problems. All users are strongly recommended to keep the plugin disabled unless necessary."},"versionRange":[{"targetApplication":[{"minVersion":"17.0","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"},{"minVersion":"2.14","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"*"}],"minVersion":"Java 6 Update 0","maxVersion":"Java 6 Update 38","severity":0,"vulnerabilityStatus":1}],"id":"61944828-8178-71ab-e9c6-d846b5f45d96","schema":1480349144394},{"blockID":"p459","enabled":true,"matchFilename":"JavaAppletPlugin\\.plugin","last_modified":1480349146253,"details":{"who":"All users who have these versions of the plugin installed in Firefox.","created":"2013-10-16T16:29:27Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=927273","name":"Java Plugin 7 update 25 to 44 (click-to-play), Mac OS X","why":"Old versions of the Java plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[{"minVersion":"17.0","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"},{"minVersion":"2.14","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"*"}],"minVersion":"Java 7 Update 25","maxVersion":"Java 7 Update 44","severity":0,"vulnerabilityStatus":1}],"id":"5b0bbf0e-dbae-7896-3c31-c6cb7a74e6fa","schema":1480349144394},{"matchName":"^Yahoo Application State Plugin$","blockID":"p26","enabled":true,"matchFilename":"npYState.dll","last_modified":1480349146230,"details":{"who":"Users of all versions of Yahoo Application State Plugin for Firefox 3 and later.\r\n\r\nUsers of all versions of Yahoo Application State Plugin for SeaMonkey 1.0.0.5 and later.","created":"2011-03-31T16:28:26Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=421993","name":"Yahoo Application State Plugin","why":"This plugin causes a high volume of Firefox and SeaMonkey crashes."},"versionRange":[{"targetApplication":[{"minVersion":"3.0a1","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"3.*"}]}],"matchDescription":"^Yahoo Application State Plugin$","id":"6a1b6dfe-f463-3061-e8f8-6e896ccf2a8a","schema":1480349144394},{"infoURL":"https://java.com/","blockID":"p902","enabled":true,"matchFilename":"JavaAppletPlugin\\.plugin","last_modified":1480349146207,"details":{"who":"All users who have these versions of the plugin installed in Firefox.","created":"2015-05-19T09:00:27Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1159917","name":"Java Plugin 7 update 45 to 78 (click-to-play), Mac OS X","why":"Old versions of the Java plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"Java 7 Update 45","maxVersion":"Java 7 Update 78","severity":0,"vulnerabilityStatus":1}],"id":"72de1aa2-6afe-2f5b-a93e-baa4c8c4578d","schema":1480349144394},{"blockID":"p154","enabled":true,"matchFilename":"npctrl\\.dll","last_modified":1480349146182,"details":{"who":"All Firefox users who have this plugin installed.","created":"2012-10-05T10:35:55Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=850744","name":"Silverlight 5.0 to 5.1.20124.*","why":"This plugin is outdated and is potentially insecure. Affected users should go to  <a href=\"http://www.mozilla.com/plugincheck/\">the plugin check page</a> and update to the latest version."},"versionRange":[{"targetApplication":[],"minVersion":"5.0","maxVersion":"5.1.20124.9999","severity":0,"vulnerabilityStatus":1}],"id":"64b50946-53f8-182d-a239-bd585b0e0b0e","schema":1480349144394},{"matchDescription":"Flip4Mac","blockID":"p242","enabled":true,"last_modified":1480349146153,"details":{"who":"All Firefox (18 and above) users on Mac OS X who have installed a version of Flip4Mac older than 2.4.4.","created":"2012-12-21T13:32:36Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=821422","name":"Flip4Mac WMV Plugin","why":"Old versions of the Flip4Mac WMV plugin are causing significant stability problems in Firefox 18 and above. Users are encouraged update to the latest version of the plugin, available at <a href=\"http://www.telestream.net/flip4mac/overview.htm\">the Flip4Mac site</a>."},"versionRange":[{"targetApplication":[{"minVersion":"18.0a1","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"}],"minVersion":"0","maxVersion":"2.4.3.999","severity":1}],"os":"Darwin","id":"cef6f402-bdf8-0ba6-66d8-8ac42c72d4be","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p834","enabled":true,"matchFilename":"(NPSWF32.*\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1480349146129,"details":{"who":"All users who have these versions of the plugin installed in Firefox.","created":"2015-02-05T15:42:51Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1128534","name":"Flash Player Plugin 13.0.0.263 to 13.0.0.268 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"13.0.0.263","maxVersion":"13.0.0.268","severity":0,"vulnerabilityStatus":1}],"id":"5e42d3a0-dfc5-4edb-8a30-4d6eed694b01","schema":1480349144394},{"matchName":"Java\\(TM\\) Platform SE 6 U3[1-8](\\s[^\\d\\._U]|$)","blockID":"p186","enabled":true,"matchFilename":"npjp2\\.dll","last_modified":1480349146106,"details":{"who":"All users who have these versions of the plugin installed in Firefox 17  and above.","created":"2012-10-30T14:45:39Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=829111","name":"Java Plugin 6 updates 31 through 38 (click-to-play), Windows","why":"The Java plugin is causing significant security problems. All users are strongly recommended to keep the plugin disabled unless necessary."},"versionRange":[{"targetApplication":[{"minVersion":"17.0","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"},{"minVersion":"2.14","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"*"}],"severity":0,"vulnerabilityStatus":1}],"id":"8488cdb0-1f00-1349-abfc-f50e85c9163b","schema":1480349144394},{"infoURL":"https://get.adobe.com/reader","blockID":"p1247","enabled":true,"matchFilename":"(nppdf32\\.dll)|(AdobePDFViewerNPAPI\\.plugin)","last_modified":1480349146079,"details":{"who":"All users who have this plugin installed.","created":"2016-07-21T16:21:52Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1286972","name":"Adobe Reader (Classic) 15.006.30174","why":"Old versions of the Adobe Reader plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"15.006.30174","maxVersion":"15.006.30174","severity":0,"vulnerabilityStatus":1}],"id":"eba24802-0c93-b8d4-ca2d-c9c194c7462b","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","matchDescription":"^Shockwave Flash 11.(0|1) r[0-9]{1,3}$","blockID":"p332","enabled":true,"matchFilename":"libflashplayer\\.so","last_modified":1480349146047,"details":{"who":"All Firefox users who have these versions of the Adobe Flash Player plugin installed.","created":"2013-03-26T09:46:14Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=854550","name":"Adobe Flash for Linux 11.0 to 11.1.* (click-to-play)","why":"Old versions of the Adobe Flash Player plugin are potentially insecure and unstable. All users are recommended to visit our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check</a> to check for updates."},"versionRange":[{"targetApplication":[{"minVersion":"19.0a1","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"},{"minVersion":"2.16a1","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"*"},{"minVersion":"17.0.4","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"17.0.*"}],"severity":0,"vulnerabilityStatus":1}],"id":"38797744-cb92-1a49-4c81-2a900691fdba","schema":1480349144394},{"matchName":"Java\\(TM\\) Platform SE 7 U1[2-5](\\s[^\\d\\._U]|$)","blockID":"p294","enabled":true,"matchFilename":"npjp2\\.dll","last_modified":1480349146005,"details":{"who":"All users who have these versions of the plugin installed in Firefox.","created":"2013-02-25T12:33:48Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=843373","name":"Java Plugin 7 update 12 to 15 (click-to-play), Windows","why":"Old versions of the Java plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[{"minVersion":"17.0","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"},{"minVersion":"2.14","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"*"}],"severity":0,"vulnerabilityStatus":1}],"id":"ab8aff96-ead4-615d-be44-bcb7c31699f0","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p1065","enabled":true,"matchFilename":"libflashplayer\\.so","last_modified":1480349145967,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2015-12-21T13:42:42Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1231191","name":"Flash Player Plugin on Linux 11.2.202.540 to 11.2.202.548 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"11.2.202.540","maxVersion":"11.2.202.548","severity":0,"vulnerabilityStatus":1}],"os":"Linux","id":"6bd57804-385b-ec8b-d4ff-3997597c3924","schema":1480349144394},{"matchName":"Java(\\(TM\\))? Plug-in ((1\\.7\\.0_(2[5-9]|3\\d|4[0-4]))|(10\\.4[0-4](\\.[0-9]+)?))([^\\d\\._]|$)","blockID":"p457","enabled":true,"matchFilename":"libnpjp2\\.so","last_modified":1480349145942,"details":{"who":"All users who have these versions of the plugin installed in Firefox.","created":"2013-10-16T16:28:58Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=927273","name":"Java Plugin 7 update 25 to 44 (click-to-play), Linux","why":"Old versions of the Java plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[{"minVersion":"17.0","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"},{"minVersion":"2.14","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"*"}],"severity":0,"vulnerabilityStatus":1}],"id":"5ca97332-f4b7-81f5-d441-6acb1834f332","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p1236","enabled":true,"matchFilename":"(NPSWF32.*\\.dll)|(NPSWF64.*\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1480349145917,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2016-06-23T12:55:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1280125","name":"Flash Player Plugin 21.0.0.226 to 21.0.0.242 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"21.0.0.226","maxVersion":"21.0.0.242","severity":0,"vulnerabilityStatus":1}],"id":"392c2c9b-f2a7-1bd5-c01c-6035e810cbaf","schema":1480349144394},{"blockID":"p138","enabled":true,"matchFilename":"JavaAppletPlugin\\.plugin","last_modified":1480349145894,"details":{"who":"All Firefox users who have the Java 7 plugin, update 6 and below.","created":"2012-09-13T14:49:52Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=794247","name":"Java Plugin","why":"The Java 7 Runtime Environment, update versions 6 and below, has a <a href=\"https://blog.mozilla.org/security/2012/08/28/protecting-users-against-java-security-vulnerability/\">serious security vulnerability</a> that is fixed in the latest update. All Firefox users are strongly encouraged to update as soon as possible. This can be done on the <a href=\"http://www.mozilla.com/plugincheck/\">Plugin Check</a> page."},"versionRange":[{"targetApplication":[{"minVersion":"0.1","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"17.*"},{"minVersion":"0.1","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"2.14.*"}],"minVersion":"Java 7 Update 01","maxVersion":"Java 7 Update 06","severity":1}],"id":"5e898a46-8ea9-2fab-5dfe-a43e51641d81","schema":1480349144394},{"blockID":"p31","enabled":true,"matchFilename":"NPMySrch.dll","last_modified":1480349145872,"details":{"who":"All users of MyWebSearch for all Mozilla applications.","created":"2011-03-31T16:28:26Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=449062","name":"MyWebSearch","why":"This plugin causes a high volume of Firefox crashes."},"versionRange":[{"targetApplication":[],"maxVersion":"*"}],"id":"2b2b85e9-4f64-9894-e3fa-3c05ead892b3","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p260","enabled":true,"matchFilename":"(NPSWF32\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1480349145849,"details":{"who":"All users who have these versions of the Flash plugin installed in Firefox 18 and above.","created":"2013-01-29T11:29:00Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=832038","name":"Flash Player Plugin 10.2.* and lower (click-to-play)","why":"Old versions of the Flash Player plugin are potentially insecure and unstable. All users are strongly recommended to check for updates at our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>.\r\n\r\nFor more information and feedback, please <a href=\"https://blog.mozilla.org/addons/2013/01/29/flash-10-2-lower-now-click-to-play/\">visit this post</a>."},"versionRange":[{"targetApplication":[{"minVersion":"18.0a1","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"},{"minVersion":"2.14a1","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"*"},{"minVersion":"17.0.4","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"17.0.*"}],"minVersion":"0","maxVersion":"10.2.9999","severity":0,"vulnerabilityStatus":1}],"id":"ce0495ed-72b8-9cb4-7a1b-eaf7f77586f7","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p1272","enabled":true,"matchFilename":"libflashplayer\\.so","last_modified":1480349145825,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2016-09-23T15:27:40Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1302525","name":"Flash Player Plugin on Linux 11.2.202.626 to 11.2.202.632 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"11.2.202.626","maxVersion":"11.2.202.632","severity":0,"vulnerabilityStatus":1}],"os":"Linux","id":"c6add809-fcd0-c504-c126-077800ebfdae","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p1139","enabled":true,"matchFilename":"(NPSWF32.*\\.dll)|(NPSWF64.*\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1480349145802,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2016-03-18T15:34:16Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1255519","name":"Flash Player Plugin 18.0.0.326 to 18.0.0.329 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"18.0.0.326","maxVersion":"18.0.0.329","severity":0,"vulnerabilityStatus":1}],"id":"0a890ad6-f9f3-7df4-5154-966368c1789d","schema":1480349144394},{"blockID":"p572","enabled":true,"matchFilename":"npdjvu\\.dll","last_modified":1480349145775,"details":{"who":"All Firefox users who have this plugin installed. Updated versions can be found <a href=\"https://www.cuminas.jp/en/downloads/download/?pid=1\">on this site</a>.","created":"2014-04-08T14:42:05Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=992976","name":"DjVu Plugin Viewer 6.1.4.27993 and earlier (Windows)","why":"Versions 6.1.4.27993 and earlier of this plugin are known to have security vulnerabilities."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"6.1.4.27993","severity":0,"vulnerabilityStatus":1}],"id":"8a416fb1-29bf-ace9-02de-605d805b6e79","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p1273","enabled":true,"matchFilename":"(NPSWF32.*\\.dll)|(NPSWF64.*\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1480349145748,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2016-09-23T15:28:24Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1302525","name":"Flash Player Plugin 18.0.0.360 to 18.0.0.366 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"18.0.0.360","maxVersion":"18.0.0.366","severity":0,"vulnerabilityStatus":1}],"id":"72e5f878-3202-b97a-d419-6e1e86a2fb18","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p1044","enabled":true,"matchFilename":"libflashplayer\\.so","last_modified":1480349145406,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2015-10-21T14:11:52Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1214459","name":"Flash Player Plugin on Linux 11.2.202.509 to 11.2.202.539 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"11.2.202.509","maxVersion":"11.2.202.539","severity":0,"vulnerabilityStatus":1}],"os":"Linux","id":"10b322e3-61ec-1066-a88b-cd12c1da339b","schema":1480349144394},{"blockID":"p592","enabled":true,"matchFilename":"CiscoWebCommunicator\\.plugin","last_modified":1480349145382,"details":{"who":"All Firefox users who have installed a version of the Cisco Web Communicator plugin lower than 3.0.6.","created":"2014-06-11T16:48:48Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=980355","name":"Cisco Web Communicator < 3.0.6 (Mac OS X)","why":"Versions lower than 3.0.6 of the Cisco Web Communicator plugin are known to have security issues and should not be used. All users should update to version 3.0.6 or later. <a href=\"https://developer.cisco.com/site/collaboration/jabber/websdk/develop-and-test/voice-and-video/troubleshooting/#plugin_vulnerabilities_user\">Find updates here.</a>"},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"3.0.5.99999999999999","severity":0,"vulnerabilityStatus":1}],"id":"1f9f9b90-e733-3929-821f-1b78a8698747","schema":1480349144394},{"matchName":"^Yahoo Application State Plugin$","blockID":"p29","enabled":true,"matchFilename":"npYState.dll","last_modified":1480349145359,"details":{"who":"Users of all versions of Yahoo Application State Plugin for Firefox 3 and later.\r\n\r\nUsers of all versions of Yahoo Application State Plugin for SeaMonkey 1.0.0.5 and later.","created":"2011-03-31T16:28:26Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=421993","name":"Yahoo Application State Plugin (SeaMonkey)","why":"This plugin causes a high volume of Firefox and SeaMonkey crashes."},"versionRange":[{"targetApplication":[{"minVersion":"1.0.0.5","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"*"}]}],"matchDescription":"^Yahoo Application State Plugin$","id":"8f52a562-5438-731b-5c64-7f76009f1489","schema":1480349144394},{"matchName":"Java\\(TM\\) Plug-in 1\\.6\\.0_4[2-5]([^\\d\\._]|$)","blockID":"p412","enabled":true,"matchFilename":"libnpjp2\\.so","last_modified":1480349145336,"details":{"who":"All users who have these versions of the plugin installed in Firefox.","created":"2013-06-28T12:42:20Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=885362","name":"Java Plugin 6 updates 42 to 45 (click-to-play), Linux","why":"Old versions of the Java plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[{"minVersion":"17.0","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"},{"minVersion":"2.14","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"*"}],"severity":0,"vulnerabilityStatus":1}],"id":"8f652c63-cda4-1640-0b0c-23025e336b20","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p1412","enabled":true,"matchFilename":"libflashplayer\\.so","last_modified":1480349145312,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2016-10-28T17:14:48Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1313435","name":"Flash Player Plugin on Linux 11.2.202.632 to 11.2.202.637 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"11.2.202.632","maxVersion":"11.2.202.637","severity":0,"vulnerabilityStatus":1}],"os":"Linux","id":"c7393e90-d75b-2aac-21de-a0c460387ff5","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p176","enabled":true,"matchFilename":"(NPSWF32\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1480349145289,"details":{"who":"All users who have these versions of the plugin installed in Firefox.","created":"2012-10-30T14:02:08Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=803152","name":"Flash Player Plugin 10.3 to 10.3.183.19 (click-to-play)","why":"Old versions of the Flash Player plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[{"minVersion":"19.0a1","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"},{"minVersion":"2.16a1","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"*"},{"minVersion":"17.0.4","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"17.0.*"}],"minVersion":"10.3","maxVersion":"10.3.183.18.999","severity":0,"vulnerabilityStatus":1}],"id":"3b6278ac-d613-6769-c299-89385b8075d1","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p1413","enabled":true,"matchFilename":"(NPSWF32.*\\.dll)|(NPSWF64.*\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1480349145265,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2016-10-28T17:15:49Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1313435","name":"Flash Player Plugin 22.0.0.211 to 23.0.0.185 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"22.0.0.211","maxVersion":"23.0.0.185","severity":0,"vulnerabilityStatus":1}],"id":"4fe73208-9a72-bea4-cd97-c106e6503d2e","schema":1480349144394},{"matchName":"Java\\(TM\\) Plug-in 1\\.7\\.0(_0?([0-9]|(1[0-1]))?)?([^\\d\\._]|$)","blockID":"p184","enabled":true,"matchFilename":"libnpjp2\\.so","last_modified":1480349145223,"details":{"who":"All users who have these versions of the plugin installed in Firefox 17 and above.","created":"2012-10-30T14:39:15Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=829111","name":"Java Plugin 7 update 11 and lower (click-to-play), Linux","why":"The Java plugin is causing significant security problems. All users are strongly recommended to keep the plugin disabled unless necessary."},"versionRange":[{"targetApplication":[{"minVersion":"17.0","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"},{"minVersion":"2.14","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"*"}],"severity":0,"vulnerabilityStatus":1}],"id":"06621403-39a8-3867-ba6e-406dc20c88af","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p932","enabled":true,"matchFilename":"libflashplayer\\.so","last_modified":1480349145175,"details":{"who":"All users who have these versions of the plugin installed in Firefox.","created":"2015-06-30T14:12:05Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1177214","name":"Flash Player Plugin on Linux 11.2.202.442 to 11.2.202.467 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"11.2.202.442","maxVersion":"11.2.202.467","severity":0,"vulnerabilityStatus":1}],"os":"Linux","id":"f136f459-8d42-f2a1-5cd3-e8d24dd8f70d","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p1121","enabled":true,"matchFilename":"(NPSWF32.*\\.dll)|(NPSWF64.*\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1480349145144,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2016-02-17T16:26:05Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1247032","name":"Flash Player Plugin 20.0.0.235 to 20.0.0.286 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"20.0.0.235","maxVersion":"20.0.0.286","severity":0,"vulnerabilityStatus":1}],"id":"c7d33934-7875-1e9e-ebcc-1a73e359951d","schema":1480349144394},{"infoURL":"https://java.com/","matchName":"Java(\\(TM\\))? Plug-in 11\\.(\\d|[1-3]\\d|4[0-4])(\\.[0-9]+)?([^\\d\\._]|$)","blockID":"p912","enabled":true,"matchFilename":"libnpjp2\\.so","last_modified":1480349145119,"details":{"who":"All users who have these versions of the plugin installed in Firefox.","created":"2015-05-19T09:05:55Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1159917","name":"Java Plugin 8 update 44 and lower (click-to-play), Linux","why":"Old versions of the Java plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"severity":0,"vulnerabilityStatus":1}],"id":"f0a40537-9a13-1b4b-60e5-b9121835c1ca","schema":1480349144394},{"infoURL":"https://java.com/","matchName":"Java\\(TM\\) Platform SE 8( U([1-3]?\\d|4[0-4]))?(\\s[^\\d\\._U]|$)","blockID":"p908","enabled":true,"matchFilename":"npjp2\\.dll","last_modified":1480349145082,"details":{"who":"All users who have these versions of the plugin installed in Firefox.","created":"2015-05-19T09:03:44Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1159917","name":"Java Plugin 8 update 44 and lower (click-to-play), Windows","why":"Old versions of the Java plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"severity":0,"vulnerabilityStatus":1}],"id":"79e165b4-599b-433d-d618-146f0c121ead","schema":1480349144394},{"infoURL":"https://java.com/","matchName":"Java(\\(TM\\))? Plug-in 11\\.(6[4-9]|7[0-6])(\\.[0-9]+)?([^\\d\\._]|$)","blockID":"p1146","enabled":true,"matchFilename":"libnpjp2\\.so","last_modified":1480349145057,"details":{"who":"All users who have these versions of the Java plugin installed.","created":"2016-03-31T16:19:31Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1259458","name":"Java Plugin 8 update 64 to 76 (click-to-play), Linux","why":"Old versions of the Java plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"severity":0,"vulnerabilityStatus":1}],"id":"bc99ed93-b527-12e2-c6e6-c61668a2e7d0","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p1254","enabled":true,"matchFilename":"(NPSWF32.*\\.dll)|(NPSWF64.*\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1480349145005,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2016-07-25T13:24:40Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1286380","name":"Flash Player Plugin 21.0.0.242 to 22.0.0.192 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"21.0.0.242","maxVersion":"22.0.0.192","severity":0,"vulnerabilityStatus":1}],"id":"d9e50e5f-ffff-35d1-c0d0-66d0f1daef77","schema":1480349144394},{"infoURL":"https://java.com/","matchName":"Java\\(TM\\) Platform SE 7 U(79|80)(\\s[^\\d\\._U]|$)","blockID":"p958","enabled":true,"matchFilename":"npjp2\\.dll","last_modified":1480349144983,"details":{"who":"All users who have these versions of the Java plugin installed.","created":"2015-07-15T10:49:33Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1183369","name":"Java Plugin 7 update 79 to 80 (click-to-play), Windows","why":"Old versions of the Java plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"severity":0,"vulnerabilityStatus":1}],"id":"2cfa79ef-a2ab-d868-467d-d182242136a5","schema":1480349144394},{"blockID":"p156","enabled":true,"matchFilename":"nppdf32\\.dll","last_modified":1480349144959,"details":{"who":"All Firefox users who have this plugin installed.","created":"2012-10-05T10:38:46Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=797378","name":"Adobe Reader 9.5.1 and lower","why":"This plugin is outdated and is potentially insecure. Affected users should go to  <a href=\"http://www.mozilla.com/plugincheck/\">the plugin check page</a> and update to the latest version."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"9.5.1","severity":0,"vulnerabilityStatus":1}],"id":"0dce3611-19e7-e8e4-5b5c-13593230f83c","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p1123","enabled":true,"matchFilename":"libflashplayer\\.so","last_modified":1480349144934,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2016-02-17T16:28:21Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1247032","name":"Flash Player Plugin on Linux 11.2.202.554 to 11.2.202.559 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"11.2.202.554","maxVersion":"11.2.202.559","severity":0,"vulnerabilityStatus":1}],"os":"Linux","id":"dd7b1070-3be3-d859-ac45-691892512e3f","schema":1480349144394},{"blockID":"p574","enabled":true,"matchFilename":"NPDjVu\\.plugin","last_modified":1480349144910,"details":{"who":"All Firefox users who have this plugin installed. Updated versions can be found <a href=\"https://www.cuminas.jp/en/downloads/download/?pid=1\">on this site</a>.","created":"2014-04-08T14:43:54Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=992976","name":"DjVu Plugin Viewer 6.1.1 and earlier (Mac OS)","why":"Versions 6.1.1 and earlier of this plugin are known to have security vulnerabilities.\r\n"},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"6.1.1","severity":0,"vulnerabilityStatus":1}],"id":"c841472f-f976-c49a-169e-0c751012c3b1","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p798","enabled":true,"matchFilename":"(NPSWF32.*\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1480349144887,"details":{"who":"All users who have these versions of the plugin installed in Firefox.","created":"2014-12-12T16:06:00Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1109795","name":"Flash Player Plugin 14.0 to 15.0.0.242 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities (CVE-2014-9163). All users are strongly recommended to <a href=\"https://get.adobe.com/flashplayer/\">update their Flash plugin</a>."},"versionRange":[{"targetApplication":[],"minVersion":"14.0","maxVersion":"15.0.0.242","severity":0,"vulnerabilityStatus":1}],"id":"975c8d44-21f4-91f3-2e5a-44e0cfd33d06","schema":1480349144394},{"blockID":"p556","enabled":true,"matchFilename":"npUnity3D32\\.dll","last_modified":1480349144865,"details":{"who":"All Firefox users who have these versions of the plugin installed.","created":"2014-02-25T08:29:41Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=974012","name":"Unity Web Player 4.6.6f1 and lower (click-to-play), Windows","why":"Current versions of the Unity Web Player plugin have known vulnerabilities that can put users at risk."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"4.6.6f1","severity":0,"vulnerabilityStatus":1}],"id":"b90eb83c-1314-5b27-687b-98d8115b6106","schema":1480349144394},{"matchName":"Java\\(TM\\) Platform SE 6 U4[2-5](\\s[^\\d\\._U]|$)","blockID":"p414","enabled":true,"matchFilename":"npjp2\\.dll","last_modified":1480349144842,"details":{"who":"All users who have these versions of the plugin installed in Firefox.","created":"2013-06-28T12:43:43Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=885362","name":"Java Plugin 6 updates 42 to 45 (click-to-play), Windows","why":"Old versions of the Java plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[{"minVersion":"17.0","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"},{"minVersion":"2.14","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"*"}],"severity":0,"vulnerabilityStatus":1}],"id":"e36516a6-8bb3-09cc-02e3-72c67046b42e","schema":1480349144394},{"matchName":"Java\\(TM\\) Platform SE 7 U7(\\s[^\\d\\._U]|$)","blockID":"p214","enabled":true,"matchFilename":"npjp2\\.dll","last_modified":1480349144817,"details":{"who":"All Firefox users who have the Java 7 plugin, updates 7 and below.","created":"2012-11-22T09:34:13Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=812896","name":"Java Plugin 1.7u7 (Windows)","why":"The Java 7 Runtime Environment, update version 7 and below, has a <a href=\"http://www.oracle.com/technetwork/topics/security/javacpuoct2012-1515924.html\">serious security vulnerability</a> that is fixed in the latest update. All Firefox users are strongly encouraged to update as soon as possible. This can be done on the <a href=\"http://www.mozilla.com/plugincheck/\">Plugin Check</a> page."},"versionRange":[{"targetApplication":[{"minVersion":"0.1","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"17.*"},{"minVersion":"0.1","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"2.14.*"}],"severity":1}],"id":"524ff62f-9564-a2b2-2585-88b88ea4c2f9","schema":1480349144394},{"infoURL":"https://java.com/","blockID":"p956","enabled":true,"matchFilename":"JavaAppletPlugin\\.plugin","last_modified":1480349144793,"details":{"who":"All users who have these versions of the Java plugin installed.","created":"2015-07-15T10:48:44Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1183369","name":"Java Plugin 8 update 45 (click-to-play), Mac OS X","why":"Old versions of the Java plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"Java 8 Update 45","maxVersion":"Java 8 Update 45","severity":0,"vulnerabilityStatus":1}],"id":"111881f9-a5bd-4919-4bab-9d7581d959d3","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p824","enabled":true,"matchFilename":"(NPSWF32.*\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1480349144769,"details":{"who":"All users who have these versions of the plugin installed in Firefox.","created":"2015-01-28T14:35:08Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1124654","name":"Flash Player Plugin 13.0.0.259 to 13.0.0.263 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"13.0.0.259","maxVersion":"13.0.0.263","severity":0,"vulnerabilityStatus":1}],"id":"dc56898f-fb0f-23ce-f507-8b50206c5444","schema":1480349144394},{"matchName":"\\(TM\\)","blockID":"p80","enabled":true,"matchFilename":"(npjp2\\.dll)|(libnpjp2\\.so)","last_modified":1480349144745,"details":{"who":"All Firefox users who have installed the Java plugin, JRE versions below 1.6.0_31 or between 1.7.0 and 1.7.0_2.","created":"2012-04-02T15:18:50Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=739955","name":"Java Plugin","why":"Outdated versions of the Java plugin are vulnerable to an actively exploited security issue. All users are strongly encouraged to <a href=\"http://java.com/inc/BrowserRedirect1.jsp\">update their Java plugin</a>. For more information, please <a href=\"http://blog.mozilla.com/addons/2012/04/02/blocking-java/\">read our blog post</a> or <a href=\"http://www.oracle.com/technetwork/topics/security/javacpufeb2012-366318.html\">Oracle's Advisory</a>."},"versionRange":[{"targetApplication":[],"severity":1}],"matchDescription":"[^\\d\\._]((0(\\.\\d+(\\.\\d+([_\\.]\\d+)?)?)?)|(1\\.(([0-5](\\.\\d+([_\\.]\\d+)?)?)|(6(\\.0([_\\.](0?\\d|1\\d|2\\d|30))?)?)|(7(\\.0([_\\.][0-2])?)?))))([^\\d\\._]|$)","id":"34deed93-d9d9-81b4-7983-0594fb829ae7","schema":1480349144394},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p946","enabled":true,"matchFilename":"(NPSWF32.*\\.dll)|(NPSWF64.*\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1480349144384,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2015-07-13T16:01:04Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1182751","name":"Flash Player Plugin 18.0.0.203 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"18.0.0.203","maxVersion":"18.0.0.203","severity":0,"vulnerabilityStatus":1}],"id":"503ba184-168d-1fcd-9a4d-62a6f8a79d68","schema":1480343754054},{"matchName":"Java\\(TM\\) Plug-in 1\\.7\\.0(_0?7)?([^\\d\\._]|$)","blockID":"p210","enabled":true,"matchFilename":"libnpjp2\\.so","last_modified":1480349144360,"details":{"who":"All Firefox users who have the Java 7 plugin, updates 7 and below.","created":"2012-11-22T09:31:33Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=812896","name":"Java Plugin 1.7u7 (Linux)","why":"The Java 7 Runtime Environment, update version 7, has a <a href=\"http://www.oracle.com/technetwork/topics/security/javacpuoct2012-1515924.html\">serious security vulnerability</a> that is fixed in the latest update. All Firefox users are strongly encouraged to update as soon as possible. This can be done on the <a href=\"http://www.mozilla.com/plugincheck/\">Plugin Check</a> page."},"versionRange":[{"targetApplication":[{"minVersion":"0.1","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"17.*"},{"minVersion":"0.1","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"2.14.*"}],"severity":1}],"id":"8e562dba-7ae7-fa85-2c31-479c4e661056","schema":1480343754054},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p794","enabled":true,"matchFilename":"(NPSWF32.*\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1480349144337,"details":{"who":"All users who have these versions of the plugin installed in Firefox.","created":"2014-12-11T05:48:53Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1109795","name":"Flash Player Plugin 10.3.183.66 to 13.0.0.258 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities (CVE-2014-9163). All users are strongly recommended to <a href=\"https://get.adobe.com/flashplayer/\">update their Flash plugin</a>."},"versionRange":[{"targetApplication":[],"minVersion":"10.3.183.66","maxVersion":"13.0.0.258","severity":0,"vulnerabilityStatus":1}],"id":"a2933e35-58b8-6560-ba75-c763481ed59f","schema":1480343754054},{"blockID":"p328","enabled":true,"matchFilename":"Silverlight\\.plugin","last_modified":1480349144312,"details":{"who":"All Firefox users who have these versions of the plugin installed.","created":"2013-03-26T09:35:05Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=853629","name":"Silverlight for Mac OS X between 5.1 and 5.1.20124.* (click-to-play)","why":"Old versions of this plugin are potentially insecure and unstable. All affected users should visit the <a href=\"http://www.mozilla.com/plugincheck/\">plugin check</a> page to look for updates for their Silverlight plugin."},"versionRange":[{"targetApplication":[{"minVersion":"19.0a1","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"},{"minVersion":"2.16a1","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"*"},{"minVersion":"17.0.4","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"17.0.*"}],"minVersion":"5.1","maxVersion":"5.1.20124.9999","severity":0,"vulnerabilityStatus":1}],"id":"dd81b232-09cb-e31d-ed8b-c5cc6ea28dd0","schema":1480343754054},{"infoURL":"https://java.com/","blockID":"p1052","enabled":true,"matchFilename":"JavaAppletPlugin\\.plugin","last_modified":1480349144290,"details":{"who":"All users who have this version of the plugin installed.","created":"2015-11-06T13:30:02Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=829111","name":"Java Plugin 7 update 11 (click-to-play), Mac OS X","why":"The Java plugin is causing significant security problems. All users are strongly recommended to keep the plugin disabled unless necessary."},"versionRange":[{"targetApplication":[],"minVersion":"Java 7 Update 11","maxVersion":"Java 7 Update 11","severity":0,"vulnerabilityStatus":1}],"id":"c62a171f-7564-734f-0310-bae88c3baf85","schema":1480343754054},{"matchName":"Java\\(TM\\) Plug-in 1\\.(6\\.0_(\\d|[0-2]\\d?|3[0-2])|7\\.0(_0?([1-4]))?)([^\\d\\._]|$)","blockID":"p119","enabled":true,"matchFilename":"libnpjp2\\.so","last_modified":1480349144265,"details":{"who":"All Firefox users who have installed the Java plugin, JRE versions below 1.6.0_33 or between 1.7.0 and 1.7.0_4.","created":"2012-08-14T09:27:32Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=780717","name":"Java Plugin","why":"Outdated versions of the Java plugin are vulnerable to an actively exploited security issue. All users are strongly encouraged to <a href=\"http://java.com/inc/BrowserRedirect1.jsp\">update their Java plugin</a>. For more information, please <a href=\"http://blog.mozilla.org/addons/2012/08/14/new-java-blocklist/\">read our blog post</a> or <a href=\"http://www.oracle.com/technetwork/topics/security/javacpujun2012-1515912.html\">Oracle's Advisory</a>."},"versionRange":[{"targetApplication":[{"minVersion":"0.1","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"17.*"},{"minVersion":"0.1","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"2.14.*"}],"severity":1}],"id":"d1aa9366-d40b-a68d-b3ed-4b36e4939442","schema":1480343754054},{"infoURL":"https://java.com/","matchName":"Java(\\(TM\\))? Plug-in 10\\.(4[5-9]|(5|6)\\d|7[0-8])(\\.[0-9]+)?([^\\d\\._]|$)","blockID":"p910","enabled":true,"matchFilename":"libnpjp2\\.so","last_modified":1480349144233,"details":{"who":"All users who have these versions of the plugin installed in Firefox.","created":"2015-05-19T09:04:46Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1159917","name":"Java Plugin 7 update 45 to 78 (click-to-play), Linux","why":"Old versions of the Java plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"severity":0,"vulnerabilityStatus":1}],"id":"f322be12-1b08-0d57-ed51-f7a6d6ec2c17","schema":1480343754054},{"blockID":"p428","enabled":true,"matchFilename":"np[dD]eployJava1\\.dll","last_modified":1480349144210,"details":{"who":"All Firefox users who have this plugin installed.","created":"2013-07-18T15:39:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=636633","name":"Java Deployment Toolkit (click-to-play)","why":"The Java Deployment Toolkit plugin is known to be insecure and is unnecessary in most cases. Users should keep it disabled unless strictly necessary."},"versionRange":[{"targetApplication":[],"severity":0,"vulnerabilityStatus":2}],"id":"d30a5b90-b84a-dfec-6147-fc04700a0d8b","schema":1480343754054},{"blockID":"p416","enabled":true,"matchFilename":"JavaAppletPlugin\\.plugin","last_modified":1480349144187,"details":{"who":"All users who have these versions of the plugin installed in Firefox.","created":"2013-06-28T12:44:58Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=885362","name":"Java Plugin 6 updates 42 to 45 (click-to-play), Mac OS X","why":"Old versions of the Java plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[{"minVersion":"17.0","guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*"},{"minVersion":"2.14","guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}","maxVersion":"*"}],"minVersion":"Java 6 Update 42","maxVersion":"Java 6 Update 45","severity":0,"vulnerabilityStatus":1}],"id":"02a5865d-f6ea-a330-674e-7dea7390680f","schema":1480343754054},{"blockID":"p594","enabled":true,"matchFilename":"npCiscoWebCommunicator\\.dll","last_modified":1480349144164,"details":{"who":"All Firefox users who have installed a version of the Cisco Web Communicator plugin lower than 3.0.6.","created":"2014-06-11T16:49:00Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=980355","name":"Cisco Web Communicator < 3.0.6 (Windows)","why":"Versions lower than 3.0.6 of the Cisco Web Communicator plugin are known to have security issues and should not be used. All users should update to version 3.0.6 or later. <a href=\"https://developer.cisco.com/site/collaboration/jabber/websdk/develop-and-test/voice-and-video/troubleshooting/#plugin_vulnerabilities_user\">Find updates here.</a>"},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"3.0.5.99999999999999","severity":0,"vulnerabilityStatus":1}],"id":"f11f91d6-f65e-8a05-310b-ad20494a868a","schema":1480343754054},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p938","enabled":true,"matchFilename":"(NPSWF32.*\\.dll)|(NPSWF64.*\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1480349144141,"details":{"who":"All users who have these versions of the plugin installed in Firefox.","created":"2015-07-10T13:37:02Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1181458","name":"Flash Player Plugin 18.0.0.194 to 18.0.0.202 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"18.0.0.194","maxVersion":"18.0.0.202","severity":0,"vulnerabilityStatus":1}],"id":"1b954bda-3ae4-ce14-29fb-be2c94f1d89a","schema":1480343754054},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p1226","enabled":true,"matchFilename":"(NPSWF32.*\\.dll)|(NPSWF64.*\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1480349144119,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2016-05-31T15:21:48Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1275307","name":"Flash Player Plugin 21.0.0.197 to 21.0.0.226 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"21.0.0.197","maxVersion":"21.0.0.226","severity":0,"vulnerabilityStatus":1}],"id":"8478742b-e3a9-aa23-c9a6-dc9c3cb90291","schema":1480343754054},{"infoURL":"https://java.com/","matchName":"Java(\\(TM\\))? Plug-in 11\\.(4[6-9]|5\\d|6[0-4])(\\.[0-9]+)?([^\\d\\._]|$)","blockID":"p1064","enabled":true,"matchFilename":"libnpjp2\\.so","last_modified":1480349144096,"details":{"who":"All users who have these versions of the Java plugin installed.","created":"2015-12-02T12:42:34Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1217932","name":"Java Plugin 8 update 46 to 64 (click-to-play), Linux","why":"Old versions of the Java plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"severity":0,"vulnerabilityStatus":1}],"id":"d5cb1745-d144-5375-f3ff-cd603d4c2cee","schema":1480343754054},{"infoURL":"https://java.com/","matchName":"Java\\(TM\\) Platform SE 7 U(4[5-9]|(5|6)\\d|7[0-8])(\\s[^\\d\\._U]|$)","blockID":"p906","enabled":true,"matchFilename":"npjp2\\.dll","last_modified":1480349144073,"details":{"who":"All users who have these versions of the plugin installed in Firefox.","created":"2015-05-19T09:02:45Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1159917","name":"Java Plugin 7 update 45 to 78 (click-to-play), Windows","why":"Old versions of the Java plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"severity":0,"vulnerabilityStatus":1}],"id":"f254e5bc-12c7-7954-fe6b-8f1fdab0ae88","schema":1480343754054},{"blockID":"p240","enabled":true,"matchFilename":"DivXBrowserPlugin\\.plugin","last_modified":1480349144049,"details":{"who":"All Firefox users who have versions of the DivX Web Player plugin lower than 1.4.","created":"2012-12-19T13:03:27Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=821972","name":"DivX Web Player 1.4 (DivX 7) and below","why":"Old versions of the DivX Web Player plugin are causing significant stability problems on Firefox 18 and above, on Mac OS X. All users should update their DivX software to the latest available version, available at <a href=\"http://www.divx.com\">divx.com</a>."},"versionRange":[{"targetApplication":[],"minVersion":"0","maxVersion":"1.4","severity":1}],"id":"d7f644bb-61aa-4594-f9fc-8dba7d9f3306","schema":1480343754054},{"infoURL":"https://java.com/","matchName":"Java(\\(TM\\))? Plug-in 10\\.(79|80)(\\.[0-9]+)?([^\\d\\._]|$)","blockID":"p962","enabled":true,"matchFilename":"libnpjp2\\.so","last_modified":1480349144026,"details":{"who":"All users who have these versions of the Java plugin installed.","created":"2015-07-15T10:51:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1183369","name":"Java Plugin 7 update 79 to 80 (click-to-play), Linux","why":"Old versions of the Java plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"severity":0,"vulnerabilityStatus":1}],"id":"7120e3c2-b293-65b5-2498-6451eab1e2c5","schema":1480343754054},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p936","enabled":true,"matchFilename":"libflashplayer\\.so","last_modified":1480349144002,"details":{"who":"All users who have these versions of the plugin installed in Firefox.","created":"2015-07-10T13:35:04Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1181458","name":"Flash Player Plugin on Linux 11.2.202.468 to 11.2.202.480 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"11.2.202.468","maxVersion":"11.2.202.480","severity":0,"vulnerabilityStatus":1}],"os":"Linux","id":"44b7040d-1567-110d-6ec5-44673c052bd8","schema":1480343754054},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p1028","enabled":true,"matchFilename":"libflashplayer\\.so","last_modified":1480349143976,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2015-10-01T09:50:47Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1206889","name":"Flash Player Plugin on Linux 11.2.202.482 to 11.2.202.508 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"11.2.202.482","maxVersion":"11.2.202.508","severity":0,"vulnerabilityStatus":1}],"os":"Linux","id":"0cc10d08-5e0c-47ea-614a-61f0f4a765d0","schema":1480343754054},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p940","enabled":true,"matchFilename":"(NPSWF32.*\\.dll)|(NPSWF64.*\\.dll)|(Flash\\ Player\\.plugin)","last_modified":1480349143953,"details":{"who":"All users who have these versions of the plugin installed in Firefox.","created":"2015-07-10T13:38:10Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1181458","name":"Flash Player Plugin 13.0.0.296 to 13.0.0.301  (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"13.0.0.296","maxVersion":"13.0.0.301","severity":0,"vulnerabilityStatus":1}],"id":"de82ceab-e70e-06f4-3b83-8c7a4c99c589","schema":1480343754054},{"infoURL":"https://java.com/","matchName":"Java\\(TM\\) Platform SE 8 U(4[6-9]|5\\d|6[0-4])(\\s[^\\d\\._U]|$)","blockID":"p1062","enabled":true,"matchFilename":"npjp2\\.dll","last_modified":1480349143930,"details":{"who":"All users who have these versions of the Java plugin installed.","created":"2015-12-02T12:40:28Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1217932","name":"Java Plugin 8 update 46 to 64 (click-to-play), Windows","why":"Old versions of the Java plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"severity":0,"vulnerabilityStatus":1}],"id":"a20c77af-721b-833b-8fa8-49091d1c69d4","schema":1480343754054},{"infoURL":"https://java.com/","matchName":"Java\\(TM\\) Platform SE 7 U(9[1-7])(\\s[^\\d\\._U]|$)","blockID":"p1143","enabled":true,"matchFilename":"npjp2\\.dll","last_modified":1480349143905,"details":{"who":"All users who have these versions of the Java plugin installed.","created":"2016-03-31T16:17:32Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1259458","name":"Java Plugin 7 update 91 to 97 (click-to-play), Windows","why":"Old versions of the Java plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"severity":0,"vulnerabilityStatus":1}],"id":"be53f658-e719-fa77-e7fe-6bd6086f888e","schema":1480343754054},{"infoURL":"https://java.com/","blockID":"p954","enabled":true,"matchFilename":"JavaAppletPlugin\\.plugin","last_modified":1480349143879,"details":{"who":"All users who have these versions of the Java plugin installed.","created":"2015-07-15T10:47:55Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1183369","name":"Java Plugin 7 update 79 to 80 (click-to-play), Mac OS X","why":"Old versions of the Java plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"Java 7 Update 79","maxVersion":"Java 7 Update 80","severity":0,"vulnerabilityStatus":1}],"id":"9b6830b4-a4b0-ee63-ee21-e0bd6ba0d6fe","schema":1480343754054},{"infoURL":"https://get.adobe.com/reader","blockID":"p1246","enabled":true,"matchFilename":"(nppdf32\\.dll)|(AdobePDFViewerNPAPI\\.plugin)","last_modified":1480349143855,"details":{"who":"All users who have this plugin installed.","created":"2016-07-20T19:19:58Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1286972","name":"Adobe Reader 10.1.6 to 11.0.16","why":"Old versions of the Adobe Reader plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"10.1.6","maxVersion":"11.0.16 ","severity":0,"vulnerabilityStatus":1}],"id":"4e29bd83-d074-bd40-f238-5ea38ff0d8d0","schema":1480343754054},{"infoURL":"https://java.com/","matchName":"Java(\\(TM\\))? Plug-in 10\\.(8[1-9]|90)(\\.[0-9]+)?([^\\d\\._]|$)","blockID":"p1063","enabled":true,"matchFilename":"libnpjp2\\.so","last_modified":1480349143832,"details":{"who":"All users who have these versions of the Java plugin installed.","created":"2015-12-02T12:41:34Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1217932","name":"Java Plugin 7 update 81 to 90 (click-to-play), Linux","why":"Old versions of the Java plugin are potentially insecure and unstable. All users are strongly recommended to update on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"severity":0,"vulnerabilityStatus":1}],"id":"b6b9c6b9-b8c4-66a9-ed07-125b32e7a5ef","schema":1480343754054},{"infoURL":"https://get.adobe.com/flashplayer/","blockID":"p1138","enabled":true,"matchFilename":"libflashplayer\\.so","last_modified":1480349143807,"details":{"who":"All users who have these versions of the Flash plugin installed.","created":"2016-03-18T15:33:12Z","bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1255519","name":"Flash Player Plugin on Linux 11.2.202.559 to 11.2.202.569 (click-to-play)","why":"Old versions of the Flash Player plugin have known vulnerabilities. All users are strongly recommended to check for updates on our <a href=\"http://www.mozilla.com/plugincheck/\">plugin check page</a>."},"versionRange":[{"targetApplication":[],"minVersion":"11.2.202.559","maxVersion":"11.2.202.569","severity":0,"vulnerabilityStatus":1}],"os":"Linux","id":"f7b02575-c0a6-2198-dc9f-5deadbcccbb2","schema":1480343754054}]}PK
!<�d�^defaults/pinning/pins.json{"data":[]}PK
!<Jj0��&chrome/en-US/locale/branding/brand.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!ENTITY  brandShorterName      "Firefox">
<!ENTITY  brandShortName        "Firefox">
<!ENTITY  brandFullName         "Mozilla Firefox">
<!ENTITY  vendorShortName       "Mozilla">
<!ENTITY  trademarkInfo.part1   "Firefox and the Firefox logos are trademarks of the Mozilla Foundation.">
PK
!</���+chrome/en-US/locale/browser/aboutDialog.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/.  -->
<!ENTITY aboutDialog.title          "About &brandFullName;">

<!-- LOCALIZATION NOTE (update.*):
# These strings are also used in the update pane of preferences.
# See about:preferences#general.
-->
<!-- LOCALIZATION NOTE (update.checkForUpdatesButton.*, update.updateButton.*):
# Only one button is present at a time.
# The button when displayed is located directly under the Firefox version in
# the about dialog (see bug 596813 for screenshots).
-->
<!ENTITY update.checkForUpdatesButton.label       "Check for updates">
<!ENTITY update.checkForUpdatesButton.accesskey   "C">
<!ENTITY update.updateButton.label3               "Restart to update &brandShorterName;">
<!ENTITY update.updateButton.accesskey            "R">


<!-- LOCALIZATION NOTE (warningDesc.version): This is a warning about the experimental nature of Nightly and Aurora builds. It is only shown in those versions. -->
<!ENTITY warningDesc.version        "&brandShortName; is experimental and may be unstable.">

<!-- LOCALIZATION NOTE (community.exp.*) This paragraph is shown in "experimental" builds, i.e. Nightly and Aurora builds, instead of the other "community.*" strings below. -->
<!ENTITY community.exp.start        "">
<!-- LOCALIZATION NOTE (community.exp.mozillaLink): This is a link title that links to http://www.mozilla.org/. -->
<!ENTITY community.exp.mozillaLink  "&vendorShortName;">
<!ENTITY community.exp.middle       " is a ">
<!-- LOCALIZATION NOTE (community.exp.creditslink): This is a link title that links to about:credits. -->
<!ENTITY community.exp.creditsLink  "global community">
<!ENTITY community.exp.end          " working together to keep the Web open, public and accessible to all.">

<!ENTITY community.start2           "&brandShortName; is designed by ">
<!-- LOCALIZATION NOTE (community.mozillaLink): This is a link title that links to http://www.mozilla.org/. -->
<!ENTITY community.mozillaLink      "&vendorShortName;">
<!ENTITY community.middle2          ", a ">
<!-- LOCALIZATION NOTE (community.creditsLink): This is a link title that links to about:credits. -->
<!ENTITY community.creditsLink      "global community">
<!ENTITY community.end3             " working together to keep the Web open, public and accessible to all.">

<!ENTITY helpus.start               "Want to help? ">
<!-- LOCALIZATION NOTE (helpus.donateLink): This is a link title that links to https://donate.mozilla.org/?utm_source=firefox&utm_medium=referral&utm_campaign=firefox_about&utm_content=firefox_about. -->
<!ENTITY helpus.donateLink          "Make a donation">
<!ENTITY helpus.middle              " or ">
<!-- LOCALIZATION NOTE (helpus.getInvolvedLink): This is a link title that links to http://www.mozilla.org/contribute/. -->
<!ENTITY helpus.getInvolvedLink     "get involved!">
<!ENTITY helpus.end                 "">

<!ENTITY releaseNotes.link          "What’s new">

<!-- LOCALIZATION NOTE (bottomLinks.license): This is a link title that links to about:license. -->
<!ENTITY bottomLinks.license        "Licensing Information">

<!-- LOCALIZATION NOTE (bottomLinks.rights): This is a link title that links to about:rights. -->
<!ENTITY bottomLinks.rights         "End-User Rights">

<!-- LOCALIZATION NOTE (bottomLinks.privacy): This is a link title that links to https://www.mozilla.org/legal/privacy/. -->
<!ENTITY bottomLinks.privacy        "Privacy Policy">

<!-- LOCALIZATION NOTE (update.checkingForUpdates): try to make the localized text short (see bug 596813 for screenshots). -->
<!ENTITY update.checkingForUpdates  "Checking for updates…">
<!-- LOCALIZATION NOTE (update.noUpdatesFound): try to make the localized text short (see bug 596813 for screenshots). -->
<!ENTITY update.noUpdatesFound      "&brandShortName; is up to date">
<!-- LOCALIZATION NOTE (update.adminDisabled): try to make the localized text short (see bug 596813 for screenshots). -->
<!ENTITY update.adminDisabled       "Updates disabled by your system administrator">
<!-- LOCALIZATION NOTE (update.otherInstanceHandlingUpdates): try to make the localized text short -->
<!ENTITY update.otherInstanceHandlingUpdates "&brandShortName; is being updated by another instance">
<!ENTITY update.restarting          "Restarting…">

<!-- LOCALIZATION NOTE (update.failed.start,update.failed.linkText,update.failed.end):
     update.failed.start, update.failed.linkText, and update.failed.end all go into
     one line with linkText being wrapped in an anchor that links to a site to download
     the latest version of Firefox (e.g. http://www.firefox.com). As this is all in
     one line, try to make the localized text short (see bug 596813 for screenshots). -->
<!ENTITY update.failed.start        "Update failed. ">
<!ENTITY update.failed.linkText     "Download the latest version">
<!ENTITY update.failed.end          "">

<!-- LOCALIZATION NOTE (update.manual.start,update.manual.end): update.manual.start and update.manual.end
     all go into one line and have an anchor in between with text that is the same as the link to a site
     to download the latest version of Firefox (e.g. http://www.firefox.com). As this is all in one line,
     try to make the localized text short (see bug 596813 for screenshots). -->
<!ENTITY update.manual.start        "Updates available at ">
<!ENTITY update.manual.end          "">

<!-- LOCALIZATION NOTE (update.unsupported.start,update.unsupported.linkText,update.unsupported.end):
     update.unsupported.start, update.unsupported.linkText, and
     update.unsupported.end all go into one line with linkText being wrapped in
     an anchor that links to a site to provide additional information regarding
     why the system is no longer supported. As this is all in one line, try to
     make the localized text short (see bug 843497 for screenshots). -->
<!ENTITY update.unsupported.start    "You can not perform further updates on this system. ">
<!ENTITY update.unsupported.linkText "Learn more">
<!ENTITY update.unsupported.end      "">

<!-- LOCALIZATION NOTE (update.downloading.start,update.downloading.end): update.downloading.start and
     update.downloading.end all go into one line, with the amount downloaded inserted in between. As this
     is all in one line, try to make the localized text short (see bug 596813 for screenshots). The — is
     the "em dash" (long dash).
     example: Downloading update — 111 KB of 13 MB -->
<!ENTITY update.downloading.start   "Downloading update — ">
<!ENTITY update.downloading.end     "">

<!ENTITY update.applying            "Applying update…">

<!-- LOCALIZATION NOTE (channel.description.start,channel.description.end): channel.description.start and
     channel.description.end create one sentence, with the current channel label inserted in between.
     example: You are currently on the _Stable_ update channel. -->
<!ENTITY channel.description.start  "You are currently on the ">
<!ENTITY channel.description.end    " update channel. ">
PK
!<�UT��)chrome/en-US/locale/browser/aboutHome.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
%brandDTD;
<!ENTITY % syncBrandDTD SYSTEM "chrome://browser/locale/syncBrand.dtd">
%syncBrandDTD;

<!-- These strings are used in the about:home page -->

<!ENTITY abouthome.pageTitle "&brandFullName; Start Page">

<!-- LOCALIZATION NOTE (abouthome.defaultSnippet1.v1):
     text in <a/> will be linked to the Firefox features page on mozilla.com
-->
<!ENTITY abouthome.defaultSnippet1.v1 "Thanks for choosing Firefox! To get the most out of your browser, learn more about the <a>latest features</a>.">
<!-- LOCALIZATION NOTE (abouthome.defaultSnippet2.v1):
     text in <a/> will be linked to the featured add-ons on addons.mozilla.org
-->
<!ENTITY abouthome.defaultSnippet2.v1 "It’s easy to customize your Firefox exactly the way you want it. <a>Choose from thousands of add-ons</a>.">
<!-- LOCALIZATION NOTE (abouthome.rightsSnippet): text in <a/> will be linked to about:rights -->
<!ENTITY abouthome.rightsSnippet "&brandFullName; is free and open source software from the non-profit Mozilla Foundation. <a>Know your rights…</a>">

<!ENTITY abouthome.bookmarksButton.label "Bookmarks">
<!ENTITY abouthome.historyButton.label   "History">
<!-- LOCALIZATION NOTE (abouthome.preferencesButtonWin.label): The label for the
     preferences/options item on about:home on Windows -->
<!ENTITY abouthome.preferencesButtonWin.label  "Options">
<!-- LOCALIZATION NOTE (abouthome.preferencesButtonUnix.label): The label for the
     preferences/options item on about:home on Linux and OS X -->
<!ENTITY abouthome.preferencesButtonUnix.label  "Preferences">
<!ENTITY abouthome.addonsButton.label    "Add-ons">
<!ENTITY abouthome.downloadsButton.label "Downloads">
<!ENTITY abouthome.syncButton.label      "&syncBrand.shortName.label;">

<!-- LOCALIZATION NOTE (abouthome.aboutMozilla.label): The (invisible) label for
     the mozilla wordmark in the top-right corner that links to Mozilla's main
     about page. -->
<!ENTITY abouthome.aboutMozilla.label    "About Mozilla">
PK
!<to�	�	4chrome/en-US/locale/browser/aboutPrivateBrowsing.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!ENTITY aboutPrivateBrowsing.notPrivate                 "You are currently not in a private window.">
<!ENTITY privatebrowsingpage.openPrivateWindow.label     "Open a Private Window">
<!ENTITY privatebrowsingpage.openPrivateWindow.accesskey "P">

<!ENTITY privateBrowsing.title                           "Private Browsing">
<!ENTITY privateBrowsing.title.tracking                  "Private Browsing with Tracking Protection">
<!ENTITY aboutPrivateBrowsing.info.notsaved.before       "When you browse in a Private Window, Firefox ">
<!ENTITY aboutPrivateBrowsing.info.notsaved.emphasize    "does not save">
<!ENTITY aboutPrivateBrowsing.info.notsaved.after        ":">
<!ENTITY aboutPrivateBrowsing.info.visited               "visited pages">
<!ENTITY aboutPrivateBrowsing.info.searches              "searches">
<!ENTITY aboutPrivateBrowsing.info.cookies               "cookies">
<!ENTITY aboutPrivateBrowsing.info.temporaryFiles        "temporary files">
<!ENTITY aboutPrivateBrowsing.info.saved.before          "Firefox ">
<!ENTITY aboutPrivateBrowsing.info.saved.emphasize       "will save">
<!ENTITY aboutPrivateBrowsing.info.saved.after2          " your:">
<!ENTITY aboutPrivateBrowsing.info.downloads             "downloads">
<!ENTITY aboutPrivateBrowsing.info.bookmarks             "bookmarks">
<!ENTITY aboutPrivateBrowsing.note.before                "Private Browsing ">
<!ENTITY aboutPrivateBrowsing.note.emphasize             "doesn’t make you anonymous">
<!ENTITY aboutPrivateBrowsing.note.after                 " on the Internet. Your employer or Internet service provider can still know what page you visit.">
<!ENTITY aboutPrivateBrowsing.learnMore3.before          "Learn more about ">
<!ENTITY aboutPrivateBrowsing.learnMore3.title           "Private Browsing">
<!ENTITY aboutPrivateBrowsing.learnMore3.after           ".">

<!ENTITY trackingProtection.title                        "Tracking Protection">
<!ENTITY trackingProtection.description2                 "Some websites use trackers that can monitor your activity across the Internet. With Tracking Protection Firefox will block many trackers that can collect information about your browsing behavior.">
<!ENTITY trackingProtection.startTour1                   "See how it works">
PK
!<wF�;chrome/en-US/locale/browser/aboutPrivateBrowsing.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

title.head=Private Browsing
title.normal=Open a private window?
PK
!<��yaa+chrome/en-US/locale/browser/aboutRobots.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!-- These strings are used in the about:robots page, which ties in with the
     robots theme used in the Firefox 3 Beta 2/3 first run pages.
     They're just meant to be fun and whimsical, with references to some geeky
     but well-known robots in movies and books. Be creative with translations! -->

<!-- Nonsense line from the movie "The Day The Earth Stood Still". No translation needed. -->
<!ENTITY robots.pagetitle  "Gort! Klaatu barada nikto!">
<!-- Movie: Logan's Run... Box (cybog): "Welcome Humans! I am ready for you." -->
<!ENTITY robots.errorTitleText "Welcome Humans!">
<!-- Movie: The Day The Earth Stood Still. Spoken by Klaatu. -->
<!ENTITY robots.errorShortDescText "We have come to visit you in peace and with goodwill!">
<!-- Various books by Isaac Asimov. http://en.wikipedia.org/wiki/Three_Laws_of_Robotics -->
<!ENTITY robots.errorLongDesc1 "Robots may not injure a human being or, through inaction, allow a human being to come to harm.">
<!-- Movie: Blade Runner. Batty: "I've seen things you people wouldn’t believe..." -->
<!ENTITY robots.errorLongDesc2 "Robots have seen things you people wouldn’t believe.">
<!-- Book: Hitchhiker’s Guide To The Galaxy. What the Sirius Cybernetics Corporation calls robots. -->
<!ENTITY robots.errorLongDesc3 "Robots are Your Plastic Pal Who’s Fun To Be With.">
<!-- TV: Futurama. Bender's first line is "Bite my shiny metal ass." -->
<!ENTITY robots.errorLongDesc4 "Robots have shiny metal posteriors which should not be bitten.">
<!-- TV: Battlestar Galactica (2004 series). From the opening text. -->
<!ENTITY robots.errorTrailerDescText "And they have a plan.">
<!-- TV: Battlestar Galactica (2004 series). Common expletive referring to Cylons. -->
<!ENTITY robots.imgtitle "Frakkin' Toasters">
<!-- Book: Hitchhiker's Guide To The Galaxy. Arthur presses a button and it warns him. -->
<!ENTITY robots.dontpress "Please do not press this button again.">
PK
!<6/���0chrome/en-US/locale/browser/aboutSearchReset.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!ENTITY searchreset.tabtitle       "Restore Search Settings">

<!ENTITY searchreset.pageTitle      "Restore your search settings?">

<!ENTITY searchreset.pageInfo1      "Your search settings might be out-of-date. &brandShortName; can help you restore the default search settings.">


<!-- LOCALIZATION NOTE (searchreset.selector.label): this string is
followed by a dropdown of all the built-in search engines. -->
<!ENTITY searchreset.selector.label "This will set your default search engine to">

<!-- LOCALIZATION NOTE (searchreset.beforelink.pageInfo,
searchreset.afterlink.pageInfo): these two string are used respectively
before and after the "Settings page" link (searchreset.link.pageInfo2).
Localizers can use one of them, or both, to better adapt this sentence to
their language. -->
<!ENTITY searchreset.beforelink.pageInfo2 "You can change these settings at any time from the ">
<!ENTITY searchreset.afterlink.pageInfo2  ".">

<!ENTITY searchreset.link.pageInfo2       "Settings page">

<!ENTITY searchreset.noChangeButton        "Don’t Change">
<!ENTITY searchreset.noChangeButton.access "D">

<!ENTITY searchreset.changeEngineButton        "Change Search Engine">
<!ENTITY searchreset.changeEngineButton.access "C">
PK
!<���,
,
3chrome/en-US/locale/browser/aboutSessionRestore.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!ENTITY restorepage.tabtitle       "Restore Session">

<!-- LOCALIZATION NOTE: The title is intended to be apologetic and disarming, expressing dismay
     and regret that we are unable to restore the session for the user -->
<!ENTITY restorepage.errorTitle2    "Sorry. We’re having trouble getting your pages back.">
<!ENTITY restorepage.problemDesc2   "We are having trouble restoring your last browsing session. Select Restore Session to try again.">
<!ENTITY restorepage.tryThis2       "Still not able to restore your session? Sometimes a tab is causing the issue. View previous tabs, remove the checkmark from the tabs you don’t need to recover, and then restore.">

<!ENTITY restorepage.hideTabs       "Hide Previous Tabs">
<!ENTITY restorepage.showTabs       "View Previous Tabs">

<!ENTITY restorepage.tryagainButton2 "Restore Session">
<!ENTITY restorepage.restore.access2 "R">
<!ENTITY restorepage.closeButton2    "Start New Session">
<!ENTITY restorepage.close.access2   "N">

<!ENTITY restorepage.restoreHeader  "Restore">
<!ENTITY restorepage.listHeader     "Windows and Tabs">
<!-- LOCALIZATION NOTE: &#37;S will be replaced with a number. -->
<!ENTITY restorepage.windowLabel    "Window &#37;S">


<!-- LOCALIZATION NOTE: The following 'welcomeback2' strings are for about:welcomeback,
     not for about:sessionstore -->

<!ENTITY welcomeback2.restoreButton  "Let’s go!">
<!ENTITY welcomeback2.restoreButton.access "L">

<!ENTITY welcomeback2.tabtitle      "Success!">

<!ENTITY welcomeback2.pageTitle     "Success!">
<!ENTITY welcomeback2.pageInfo1     "&brandShortName; is ready to go.">

<!ENTITY welcomeback2.restoreAll.label  "Restore all windows &amp; tabs">
<!ENTITY welcomeback2.restoreSome.label "Restore only the ones you want">


<!-- LOCALIZATION NOTE (welcomeback2.beforelink.pageInfo2,
welcomeback2.afterlink.pageInfo2): these two string are used respectively
before and after the the "learn more" link (welcomeback2.link.pageInfo2).
Localizers can use one of them, or both, to better adapt this sentence to
their language.
-->
<!ENTITY welcomeback2.beforelink.pageInfo2  "Your add-ons and customizations have been removed and your browser settings have been restored to their defaults. If this didn’t fix your issue, ">
<!ENTITY welcomeback2.afterlink.pageInfo2   "">

<!ENTITY welcomeback2.link.pageInfo2        "learn more about what you can do.">

PK
!<⾜��/chrome/en-US/locale/browser/aboutTabCrashed.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!ENTITY tabCrashed.title "Tab crash reporter">

<!ENTITY tabCrashed.closeTab2  "Close Tab">
<!ENTITY tabCrashed.restoreTab "Restore This Tab">
<!ENTITY tabCrashed.restoreAll "Restore All Crashed Tabs">

<!-- LOCALIZATION NOTE (tabCrashed.header2): "Gah" is an English slang word
     used to express surprise or frustration (or both at the same time).  We
     are using it to communicate in an informal way that it is both
     frustrating that your tab crashed and a surprise that we didn't want to
     happen. If you have a similar word or short phrase that is not profane or
     vulgar, use it. If not, feel free to skip the word in your
     translation. -->
<!ENTITY tabCrashed.header2 "Gah. Your tab just crashed.">
<!ENTITY tabCrashed.offerHelp2 "We can help!">
<!ENTITY tabCrashed.single.offerHelpMessage2 "Choose &tabCrashed.restoreTab; to reload the page.">
<!ENTITY tabCrashed.multiple.offerHelpMessage2 "Choose &tabCrashed.restoreTab; or &tabCrashed.restoreAll; to reload the page/pages.">
<!ENTITY tabCrashed.requestHelp "Will you help us?">
<!ENTITY tabCrashed.requestHelpMessage "Crash reports help us diagnose problems and make &brandShortName; better.">
<!ENTITY tabCrashed.requestReport "Report this tab">
<!ENTITY tabCrashed.sendReport3 "Send an automated crash report so we can fix issues like this.">
<!ENTITY tabCrashed.commentPlaceholder2 "Optional comments (comments are publicly visible)">
<!ENTITY tabCrashed.includeURL3 "Include the URLs of the sites you were on when &brandShortName; crashed.">
<!ENTITY tabCrashed.emailPlaceholder "Enter your email address here">
<!ENTITY tabCrashed.emailMe "Email me when more information is available">
<!ENTITY tabCrashed.reportSent "Crash report already submitted; thank you for helping make &brandShortName; better!">
<!ENTITY tabCrashed.requestAutoSubmit2 "Report background tabs">
<!ENTITY tabCrashed.autoSubmit3 "Update preferences to automatically submit reports when &brandShortName; crashes.">
PK
!<&���bb/chrome/en-US/locale/browser/accounts.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE (reconnectDescription) - %S = Email address of user's Firefox Account
reconnectDescription = Reconnect %S

# LOCALIZATION NOTE (verifyDescription) - %S = Email address of user's Firefox Account
verifyDescription = Verify %S

# These strings are shown in a desktop notification after the
# user requests we resend a verification email.
verificationSentTitle = Verification Sent
# LOCALIZATION NOTE (verificationSentBody) - %S = Email address of user's Firefox Account
verificationSentBody = A verification link has been sent to %S.
verificationNotSentTitle = Unable to Send Verification
verificationNotSentBody = We are unable to send a verification mail at this time, please try again later.

# LOCALIZATION NOTE (deviceConnectedTitle, deviceConnectedBody, deviceConnectedBody.noDeviceName)
# These strings are used in a notification shown when a new device joins the Sync account.
# deviceConnectedBody.noDeviceName is shown instead of deviceConnectedBody when we
# could not get the device name that joined
deviceConnectedTitle = Firefox Sync
deviceConnectedBody = This computer is now syncing with %S.
deviceConnectedBody.noDeviceName = This computer is now syncing with a new device.

# LOCALIZATION NOTE (syncStartNotification.title, syncStartNotification.body)
# These strings are used in a notification shown after Sync is connected.
syncStartNotification.title = Sync Enabled
# %S is brandShortName
syncStartNotification.body2 = %S will begin syncing momentarily.

# LOCALIZATION NOTE (deviceDisconnectedNotification.title, deviceDisconnectedNotification.body)
# These strings are used in a notification shown after Sync was disconnected remotely.
deviceDisconnectedNotification.title = Sync Disconnected
deviceDisconnectedNotification.body = This computer has been successfully disconnected from Firefox Sync.

# LOCALIZATION NOTE (sendToAllDevices.menuitem)
# Displayed in the Send Tab/Page/Link to Device context menu when right clicking a tab, a page or a link.
sendToAllDevices.menuitem = Send to All Devices

# LOCALIZATION NOTE (sendTabToDevice.unconfigured, sendTabToDevice.unconfigured.status)
# Displayed in the Send Tabs context menu when right clicking a tab, a page or a link
# and the Sync account is unconfigured. Redirects to a marketing page.
sendTabToDevice.unconfigured.status = Not Connected to Sync
sendTabToDevice.unconfigured = Learn About Sending Tabs…

# LOCALIZATION NOTE (sendTabToDevice.signintosync)
# Displayed in the Send Tabs context menu and the page action panel when sync is not
# configured. Allows users to immediately sign into sync via the preferences.
sendTabToDevice.signintosync = Sign in to Sync…

# LOCALIZATION NOTE (sendTabToDevice.singledevice, sendTabToDevice.connectdevice,
# sendTabToDevice.singledevice.status)
# Displayed in the Send Tabs context menu when right clicking a tab, a page or a link
# and the Sync account has only 1 device. The sendTabToDevice.singledevice link
# redirects to a marketing page, the sendTabToDevice.connectdevice redirects
# to an FxAccounts page that tells to you to connect another device.
sendTabToDevice.singledevice.status = No Devices Connected
sendTabToDevice.singledevice = Learn About Sending Tabs…
sendTabToDevice.connectdevice = Connect Another Device…

# LOCALIZATION NOTE (sendTabToDevice.verify, sendTabToDevice.verify.status)
# Displayed in the Send Tabs context menu when right clicking a tab, a page or a link
# and the Sync account is unverified. Redirects to the Sync preferences page.
sendTabToDevice.verify.status = Account Not Verified
sendTabToDevice.verify = Verify Your Account…

# LOCALIZATION NOTE (tabArrivingNotification.title, tabArrivingNotificationWithDevice.title,
# multipleTabsArrivingNotification.title, unnamedTabsArrivingNotification2.body,
# unnamedTabsArrivingNotificationMultiple2.body, unnamedTabsArrivingNotificationNoDevice.body,
# singleTabArrivingWithTruncatedURL.body)
# These strings are used in a notification shown when we're opening tab(s) another device sent us to display.

# LOCALIZATION NOTE (tabArrivingNotification.title, tabArrivingNotificationWithDevice.title)
# The body for these is the URL of the tab recieved
tabArrivingNotification.title = Tab Received
# LOCALIZATION NOTE (tabArrivingNotificationWithDevice.title) %S is the device name
tabArrivingNotificationWithDevice.title = Tab from %S

multipleTabsArrivingNotification.title = Tabs Received
# LOCALIZATION NOTE (unnamedTabsArrivingNotification2.body):
# Semi-colon list of plural forms.
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
# #1 is the number of tabs received and #2 is the device name.
unnamedTabsArrivingNotification2.body = #1 tab has arrived from #2;#1 tabs have arrived from #2
# LOCALIZATION NOTE (unnamedTabsArrivingNotificationMultiple2.body):
# Semi-colon list of plural forms.
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
# #1 is the number of tabs received.
unnamedTabsArrivingNotificationMultiple2.body = #1 tab has arrived from your connected devices;#1 tabs have arrived from your connected devices

# LOCALIZATION NOTE (unnamedTabsArrivingNotificationNoDevice.body):
# Semi-colon list of plural forms.
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
# #1 is the number of tabs received
# This version is used when we don't know any device names.
unnamedTabsArrivingNotificationNoDevice.body = #1 tab has arrived;#1 tabs have arrived

# LOCALIZATION NOTE (singleTabArrivingWithTruncatedURL.body):
# Used when a tab from a remote device arrives but the URL must be truncated.
# Should display the URL with an indication that it's benen truncated.
# %S is the portion of the URL that remains after truncation.
singleTabArrivingWithTruncatedURL.body = %S…
PK
!<��'y��1chrome/en-US/locale/browser/appstrings.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

malformedURI2=Please check that the URL is correct and try again.
fileNotFound=Firefox can’t find the file at %S.
fileAccessDenied=The file at %S is not readable.
dnsNotFound2=We can’t connect to the server at %S.
unknownProtocolFound=Firefox doesn’t know how to open this address, because one of the following protocols (%S) isn’t associated with any program or is not allowed in this context.
connectionFailure=Firefox can’t establish a connection to the server at %S.
netInterrupt=The connection to %S was interrupted while the page was loading.
netTimeout=The server at %S is taking too long to respond.
redirectLoop=Firefox has detected that the server is redirecting the request for this address in a way that will never complete.
## LOCALIZATION NOTE (confirmRepostPrompt): In this item, don’t translate "%S"
confirmRepostPrompt=To display this page, %S must send information that will repeat any action (such as a search or order confirmation) that was performed earlier.
resendButton.label=Resend
unknownSocketType=Firefox doesn’t know how to communicate with the server.
netReset=The connection to the server was reset while the page was loading.
notCached=This document is no longer available.
netOffline=Firefox is currently in offline mode and can’t browse the Web.
isprinting=The document cannot change while Printing or in Print Preview.
deniedPortAccess=This address uses a network port which is normally used for purposes other than Web browsing. Firefox has canceled the request for your protection.
proxyResolveFailure=Firefox is configured to use a proxy server that can’t be found.
proxyConnectFailure=Firefox is configured to use a proxy server that is refusing connections.
contentEncodingError=The page you are trying to view cannot be shown because it uses an invalid or unsupported form of compression.
unsafeContentType=The page you are trying to view cannot be shown because it is contained in a file type that may not be safe to open. Please contact the website owners to inform them of this problem.
externalProtocolTitle=External Protocol Request
externalProtocolPrompt=An external application must be launched to handle %1$S: links.\n\n\nRequested link:\n\n%2$S\n\nApplication: %3$S\n\n\nIf you were not expecting this request it may be an attempt to exploit a weakness in that other program. Cancel this request unless you are sure it is not malicious.\n
#LOCALIZATION NOTE (externalProtocolUnknown): The following string is shown if the application name can't be determined
externalProtocolUnknown=<Unknown>
externalProtocolChkMsg=Remember my choice for all links of this type.
externalProtocolLaunchBtn=Launch application
malwareBlocked=The site at %S has been reported as an attack site and has been blocked based on your security preferences.
harmfulBlocked=The site at %S has been reported as a potentially harmful site and has been blocked based on your security preferences.
unwantedBlocked=The site at %S has been reported as serving unwanted software and has been blocked based on your security preferences.
deceptiveBlocked=This web page at %S has been reported as a deceptive site and has been blocked based on your security preferences.
cspBlocked=This page has a content security policy that prevents it from being loaded in this way.
corruptedContentErrorv2=The site at %S has experienced a network protocol violation that cannot be repaired.
remoteXUL=This page uses an unsupported technology that is no longer available by default in Firefox.
## LOCALIZATION NOTE (sslv3Used) - Do not translate "%S".
sslv3Used=Firefox cannot guarantee the safety of your data on %S because it uses SSLv3, a broken security protocol.
inadequateSecurityError=The website tried to negotiate an inadequate level of security.
blockedByPolicy=Your organization has blocked access to this page or website.
PK
!<�*	�>>/chrome/en-US/locale/browser/baseMenuOverlay.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!ENTITY minimizeWindow.key       "m">
<!ENTITY minimizeWindow.label     "Minimize">
<!ENTITY bringAllToFront.label    "Bring All to Front">
<!ENTITY zoomWindow.label         "Zoom">
<!ENTITY windowMenu.label         "Window">

<!ENTITY helpMenu.label           "Help"> 
<!ENTITY helpMenu.accesskey       "H"> 
<!-- LOCALIZATION NOTE some localizations of Windows (ex:french, german) use "?"
                       for the help button in the menubar but Gnome does not.   -->
<!ENTITY helpMenuWin.label        "Help"> 
<!ENTITY helpMenuWin.accesskey    "H">
<!ENTITY aboutProduct2.label      "About &brandShorterName;">
<!ENTITY aboutProduct2.accesskey  "A">
<!ENTITY productHelp2.label       "&brandShorterName; Help">
<!ENTITY productHelp2.accesskey   "H">
<!ENTITY helpMac.commandkey       "?">

<!ENTITY helpKeyboardShortcuts.label     "Keyboard Shortcuts">
<!ENTITY helpKeyboardShortcuts.accesskey "K">

<!ENTITY helpSafeMode.label       "Restart with Add-ons Disabled…">
<!ENTITY helpSafeMode.accesskey   "R">
<!ENTITY helpSafeMode.stop.label       "Restart with Add-ons Enabled">
<!ENTITY helpSafeMode.stop.accesskey   "R">

<!ENTITY helpTroubleshootingInfo.label      "Troubleshooting Information">
<!ENTITY helpTroubleshootingInfo.accesskey  "T">

<!ENTITY helpFeedbackPage.label      "Submit Feedback…">
<!ENTITY helpFeedbackPage.accesskey  "S">

<!ENTITY helpShowTour2.label            "&brandShorterName; Tour">
<!ENTITY helpShowTour2.accesskey        "o">

<!ENTITY preferencesCmdMac.label        "Preferences…">
<!ENTITY preferencesCmdMac.commandkey   ",">

<!ENTITY servicesMenuMac.label          "Services">

<!ENTITY hideThisAppCmdMac2.label       "Hide &brandShorterName;">
<!ENTITY hideThisAppCmdMac2.commandkey  "H">

<!ENTITY hideOtherAppsCmdMac.label      "Hide Others">
<!ENTITY hideOtherAppsCmdMac.commandkey "H">

<!ENTITY showAllAppsCmdMac.label        "Show All">
PK
!<����3�3*chrome/en-US/locale/browser/bookmarks.html





<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.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 NETSCAPE-Bookmark-file-1>
<meta charset="UTF-8">
<title>Bookmarks</title>
<h1>Bookmarks</h1>

<dl><p>
    <dt><h3 personal_toolbar_folder="true">Bookmarks Toolbar Folder</h3></dt>
    <dd>Add bookmarks to this folder to see them displayed on the Bookmarks Toolbar
        <dl>
            <p><dt><a href="https://www.mozilla.org/en-US/firefox/central/" icon="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABmJLR0QAAAAAAAD5Q7t/AAAACXBIWXMAAAsSAAALEgHS3X78AAAAB3RJTUUH4QsJCyMZzYMsXAAACVZJREFUWMOtl3mMX1UVxz/n3vve7/d+2yyd6bQdp6UtSwGrkgKW1FjFDdyDCAQTFTHGLRo33OMSExdwC0bREP8AFyLRGDVEa1gkYmuLWCmmFNvpQJm2Q6fTzm99y1384/dry6ZR40u+ue8l773zOffcc+65wv/pKvaCGV1CyLPLkVxE4tutbROt+Pffyf8LwB9S4P1yKZ/zK+jZYPdfIaIPuNRhVv4XAJ2xjU95rs5vIwDqrQcRZ7WvJhNUSmuolFZSMkOUAiS2RRQ/+tirz3zp1NCyzyGJwj58W/AH3s8NHOc0NJCr9z4TwDz5YW7ly+lASYs7H/HbnUgx9sJtrCiyWqP5xOZuZehyj1yEqOcgkqBEoT2YEIZqs73DxaRMqVhBFcy6KyQ02uGFe7bQtFo63GZ/JpgrwlMA1ImbfZOvJYhQYC6xor4QdBheOna3NI7PbRbvbiOE271EbxelzkpMWo20U2gg8mCCnFafrqypHkwgAgJQUajT3ibnnvVdGY8+FlazUtXCv56BAsVCUauWTf4eJ/birsTXbrLfz1vR0CdyauMi8KLKdt609LdUahlfzj7NPjkdlAcNk8ksddOCIAMAIOiIidVjRHpMju79gO91P+634tRFzwKQIgTxqx2cVxUnQ6XeZ79duU274VB6vD7KYqPC6ye3UNQV1y5+j32sAXPCo4AEj4QMQgHoAUToDyNTEKtrVHv6d5jk9/k9c8QveRpAhsMjK8ckHV5uFimXupVlSQ+qPS4cTmEkgyTjs8c+yR3dS6E6sOEFvDDbWU6z0CyJjoMsgxD6gv5YnhhFuC6099xvJkePwcKpNfDY8tegZ18lU9K7YJnuxCVVgPF9D430FfVfL3cgDin4AE6BVWBhb/N0/rqwDtx+8PkpAO8h+D6sGd0syZmXyej55H+uAaC3jV5FXVvK1f2bEl1cn5i0ZqICSSxULVSLvmoFiHBeb4ZNjft50dg2njM0y8PZOgoicl/GWsdrlv4aIx4YHsxAOAUTRBPMeFg88ktVWdL7wo1zqMRHzLZWRN6b9whMKDwigAqgnyQVAKHuu1wS3cm7h27i2tEfktgUMgOF4ZcHLuOWRy4j5Pug2A/egvPg3CkF2UDUuFjiOs1tz8P4oIl18VwX1CtEfL80SRiMT1PsQXuaeYN7Dr+E63vvZd6NQCTgDT0zzmd2fpHEpVy99ldo0wO9qr/UToTC+1hCeH2+0PxF0oid0eJIXXRuCDL8jCR9etqWHA+Vl3PdzEe5x2ymVxmHpBjEWoNVHJG1fPDP3yLppFx+1h0QdcCsBsrgXR/E+wtMyUyAP6jKCBLUkoCKQ5BB9sjJLCKAdwqXG7wTdri13N28kDwzkKbQy/tKC0gtZJZj6STfefAaFmdq0DoKnYchXwRrBypWyHxnQzjnAcwZkzvYOn3xmT4oPAqPEALIIL2azQqd4zW2ujVs0c9jPhqnmjbJVCDzHuvKhCKCyIFRoBUg7Oycze7OajYe3AUTbcimIR6Eo7B15tL3h5sn/mB+9Pe3lKZK85Pea7zXhKDAK3BClkYstsrUyhm7W1Pc2b2AUKlQSVpoAso5cpdT5GV8FBOMBqVACa1eiWm1jI1qFxwHKh0oDkM0DoVHLEvCUGxMBwlF0AetN7igcUETvCBO4dOIEXHUopxLxh/iT7PrmOlNISGADYTCI7lFRzk+Nngd4ZUmKEXodshyBSuA+UFxlEVwCVhDEJf7YefM+eX5/JiNdhc+pnARzht8UKhCUTIWKTuIPBsmpvniilv5wT8u5ZHuKmrVgjOnDlJp5OxcOIf759aTS6lvXBQqm2MiPjIoYEAGKAe+DT6Bsp/rRLZrFoqEInAgd7EtXGwK34fQtkB56ee/6deBDaP7uGHyJo6qOvXhLiNLWqhqoBWqfOmud/GTnZeCEnxQLKvs4ezRxyAHPFAMUtmmoCAY/+Bo5guTeYULsjd38ULmyksLF2O9IXIasa6fBdZQLI4gVKmOdKgPHQM8vqt5ZG4JW2fX0D2SU+8sEJQit47XvuAPrKodhUXAPrkFciBp2znuJQ+YLoECvT9x8a7UJi/LbJncxMQuR+UOco3KLKKFh/adwVFbo9bo0C0X/K3T4K7HzuFwezmYElVznNxrzp36O+/YdDcqDdAB3ImNqw/gcdt7md4hAuZR1WFTqHcWvP51r0gu7tlEyq5MyWdoa1E9A7EjLqUMJwf58cMXsPuhteRBYymhTIm67uHyAq9h/eq9fPiNv2FV7Rg0gd7AsB+ASMisUzc3jG3eN9Po94Q/l8/ggpw2rIs7RpOjZy+pzDNUPkY1bhNFGVLPoZHBUMYRXWLL4bXsmJviiW6NzBtM5BkfbXLh+j1csvFBlo51+vEuBgoD4w7CXPVxO1vfjPHT8Y1P9PuBA6rLNWF05h7vv98pajckRc/EOsOoAiUO0+3vAejAeCPj6rU7ecO6XSwQ09WKcj1lbKxJtZEiEdAexNufqqZYCIcrhJn6o3Y0PXqiH9YAvw1beT4baQfZU0I9VxPOMtqilUMrhwqhvxkOPpIgxNoylPQYq7YZTrrE4hA3WHB24PnAazIhtCPcfC13w72vlh+v3zdz7jw3/i70AQAWylO8wo+kzSAPGvRFWliulUOLR4lH+4D4AUTol2mcGjQletCYKCgUWBlACPQ0tGLoGuuMu+lo7L6ZjabFyk8Xp2YAYMbuYql6Pq/U0ZFZz191UBuUsFwNAOQEhBsY9+oUhB1A5BpyNRgNdA0slvCdaC5L9VcOt+OvlFPTXvqN1smsPAkAsC1sR+n1XGnMwX94/miCWimotSIoERDpN8HKCrgnz8DA85PGNaETUSzU8rxnbm9l6kP3HmrcrnOVnnHrE0/Z4Z/1aHZ2dDU3mhEOeUamTPHOkaj3vnrcXlWLWyRRl5LO0LpARQ7KFkoOYgexx+rAojUc79btYjpy8yFbua6hfOvFD3wS2PwMW/rZAOb9Lm6xO9hUWp/eadk2pWSLQhZDUCMh6EYIyggK8YIUCsn1Sc+L1OTdNNqdO/laC3/9eNRsXnj/m4Ern83Uf3Y4/Xz9KqatkivLxcSyKH9BQ2fnVUx2eknn45HOS0ZcIcod89rvz41/II3d9p2V9qGJIg4btk3/23//D6fjH/HBsZv5SzqsXpd0ogmTq654f28WFT/91F1ePvIR4Ov/8d/+CfHfpDCA/XdnAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE3LTExLTA5VDExOjM1OjI1KzAxOjAw560ORgAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNy0xMS0wOVQxMTozNToyNSswMTowMJbwtvoAAABXelRYdFJhdyBwcm9maWxlIHR5cGUgaXB0YwAAeJzj8gwIcVYoKMpPy8xJ5VIAAyMLLmMLEyMTS5MUAxMgRIA0w2QDI7NUIMvY1MjEzMQcxAfLgEigSi4A6hcRdPJCNZUAAAAASUVORK5CYII=">Getting Started</a></dt>
        </dl>
    <p><dt><h3>Mozilla Firefox</h3></dt>
        <dl><p>
            <dt><a href="https://support.mozilla.org/en-US/products/firefox" icon="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABmJLR0QAAAAAAAD5Q7t/AAAACXBIWXMAAAsSAAALEgHS3X78AAAAB3RJTUUH4QsJCyMZzYMsXAAACVZJREFUWMOtl3mMX1UVxz/n3vve7/d+2yyd6bQdp6UtSwGrkgKW1FjFDdyDCAQTFTHGLRo33OMSExdwC0bREP8AFyLRGDVEa1gkYmuLWCmmFNvpQJm2Q6fTzm99y1384/dry6ZR40u+ue8l773zOffcc+65wv/pKvaCGV1CyLPLkVxE4tutbROt+Pffyf8LwB9S4P1yKZ/zK+jZYPdfIaIPuNRhVv4XAJ2xjU95rs5vIwDqrQcRZ7WvJhNUSmuolFZSMkOUAiS2RRQ/+tirz3zp1NCyzyGJwj58W/AH3s8NHOc0NJCr9z4TwDz5YW7ly+lASYs7H/HbnUgx9sJtrCiyWqP5xOZuZehyj1yEqOcgkqBEoT2YEIZqs73DxaRMqVhBFcy6KyQ02uGFe7bQtFo63GZ/JpgrwlMA1ImbfZOvJYhQYC6xor4QdBheOna3NI7PbRbvbiOE271EbxelzkpMWo20U2gg8mCCnFafrqypHkwgAgJQUajT3ibnnvVdGY8+FlazUtXCv56BAsVCUauWTf4eJ/birsTXbrLfz1vR0CdyauMi8KLKdt609LdUahlfzj7NPjkdlAcNk8ksddOCIAMAIOiIidVjRHpMju79gO91P+634tRFzwKQIgTxqx2cVxUnQ6XeZ79duU274VB6vD7KYqPC6ye3UNQV1y5+j32sAXPCo4AEj4QMQgHoAUToDyNTEKtrVHv6d5jk9/k9c8QveRpAhsMjK8ckHV5uFimXupVlSQ+qPS4cTmEkgyTjs8c+yR3dS6E6sOEFvDDbWU6z0CyJjoMsgxD6gv5YnhhFuC6099xvJkePwcKpNfDY8tegZ18lU9K7YJnuxCVVgPF9D430FfVfL3cgDin4AE6BVWBhb/N0/rqwDtx+8PkpAO8h+D6sGd0syZmXyej55H+uAaC3jV5FXVvK1f2bEl1cn5i0ZqICSSxULVSLvmoFiHBeb4ZNjft50dg2njM0y8PZOgoicl/GWsdrlv4aIx4YHsxAOAUTRBPMeFg88ktVWdL7wo1zqMRHzLZWRN6b9whMKDwigAqgnyQVAKHuu1wS3cm7h27i2tEfktgUMgOF4ZcHLuOWRy4j5Pug2A/egvPg3CkF2UDUuFjiOs1tz8P4oIl18VwX1CtEfL80SRiMT1PsQXuaeYN7Dr+E63vvZd6NQCTgDT0zzmd2fpHEpVy99ldo0wO9qr/UToTC+1hCeH2+0PxF0oid0eJIXXRuCDL8jCR9etqWHA+Vl3PdzEe5x2ymVxmHpBjEWoNVHJG1fPDP3yLppFx+1h0QdcCsBsrgXR/E+wtMyUyAP6jKCBLUkoCKQ5BB9sjJLCKAdwqXG7wTdri13N28kDwzkKbQy/tKC0gtZJZj6STfefAaFmdq0DoKnYchXwRrBypWyHxnQzjnAcwZkzvYOn3xmT4oPAqPEALIIL2azQqd4zW2ujVs0c9jPhqnmjbJVCDzHuvKhCKCyIFRoBUg7Oycze7OajYe3AUTbcimIR6Eo7B15tL3h5sn/mB+9Pe3lKZK85Pea7zXhKDAK3BClkYstsrUyhm7W1Pc2b2AUKlQSVpoAso5cpdT5GV8FBOMBqVACa1eiWm1jI1qFxwHKh0oDkM0DoVHLEvCUGxMBwlF0AetN7igcUETvCBO4dOIEXHUopxLxh/iT7PrmOlNISGADYTCI7lFRzk+Nngd4ZUmKEXodshyBSuA+UFxlEVwCVhDEJf7YefM+eX5/JiNdhc+pnARzht8UKhCUTIWKTuIPBsmpvniilv5wT8u5ZHuKmrVgjOnDlJp5OxcOIf759aTS6lvXBQqm2MiPjIoYEAGKAe+DT6Bsp/rRLZrFoqEInAgd7EtXGwK34fQtkB56ee/6deBDaP7uGHyJo6qOvXhLiNLWqhqoBWqfOmud/GTnZeCEnxQLKvs4ezRxyAHPFAMUtmmoCAY/+Bo5guTeYULsjd38ULmyksLF2O9IXIasa6fBdZQLI4gVKmOdKgPHQM8vqt5ZG4JW2fX0D2SU+8sEJQit47XvuAPrKodhUXAPrkFciBp2znuJQ+YLoECvT9x8a7UJi/LbJncxMQuR+UOco3KLKKFh/adwVFbo9bo0C0X/K3T4K7HzuFwezmYElVznNxrzp36O+/YdDcqDdAB3ImNqw/gcdt7md4hAuZR1WFTqHcWvP51r0gu7tlEyq5MyWdoa1E9A7EjLqUMJwf58cMXsPuhteRBYymhTIm67uHyAq9h/eq9fPiNv2FV7Rg0gd7AsB+ASMisUzc3jG3eN9Po94Q/l8/ggpw2rIs7RpOjZy+pzDNUPkY1bhNFGVLPoZHBUMYRXWLL4bXsmJviiW6NzBtM5BkfbXLh+j1csvFBlo51+vEuBgoD4w7CXPVxO1vfjPHT8Y1P9PuBA6rLNWF05h7vv98pajckRc/EOsOoAiUO0+3vAejAeCPj6rU7ecO6XSwQ09WKcj1lbKxJtZEiEdAexNufqqZYCIcrhJn6o3Y0PXqiH9YAvw1beT4baQfZU0I9VxPOMtqilUMrhwqhvxkOPpIgxNoylPQYq7YZTrrE4hA3WHB24PnAazIhtCPcfC13w72vlh+v3zdz7jw3/i70AQAWylO8wo+kzSAPGvRFWliulUOLR4lH+4D4AUTol2mcGjQletCYKCgUWBlACPQ0tGLoGuuMu+lo7L6ZjabFyk8Xp2YAYMbuYql6Pq/U0ZFZz191UBuUsFwNAOQEhBsY9+oUhB1A5BpyNRgNdA0slvCdaC5L9VcOt+OvlFPTXvqN1smsPAkAsC1sR+n1XGnMwX94/miCWimotSIoERDpN8HKCrgnz8DA85PGNaETUSzU8rxnbm9l6kP3HmrcrnOVnnHrE0/Z4Z/1aHZ2dDU3mhEOeUamTPHOkaj3vnrcXlWLWyRRl5LO0LpARQ7KFkoOYgexx+rAojUc79btYjpy8yFbua6hfOvFD3wS2PwMW/rZAOb9Lm6xO9hUWp/eadk2pWSLQhZDUCMh6EYIyggK8YIUCsn1Sc+L1OTdNNqdO/laC3/9eNRsXnj/m4Ern83Uf3Y4/Xz9KqatkivLxcSyKH9BQ2fnVUx2eknn45HOS0ZcIcod89rvz41/II3d9p2V9qGJIg4btk3/23//D6fjH/HBsZv5SzqsXpd0ogmTq654f28WFT/91F1ePvIR4Ov/8d/+CfHfpDCA/XdnAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE3LTExLTA5VDExOjM1OjI1KzAxOjAw560ORgAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNy0xMS0wOVQxMTozNToyNSswMTowMJbwtvoAAABXelRYdFJhdyBwcm9maWxlIHR5cGUgaXB0YwAAeJzj8gwIcVYoKMpPy8xJ5VIAAyMLLmMLEyMTS5MUAxMgRIA0w2QDI7NUIMvY1MjEzMQcxAfLgEigSi4A6hcRdPJCNZUAAAAASUVORK5CYII=">Help and Tutorials</a>
            <dt><a href="https://www.mozilla.org/en-US/firefox/customize/" icon="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABmJLR0QAAAAAAAD5Q7t/AAAACXBIWXMAAAsSAAALEgHS3X78AAAAB3RJTUUH4QsJCyMZzYMsXAAACVZJREFUWMOtl3mMX1UVxz/n3vve7/d+2yyd6bQdp6UtSwGrkgKW1FjFDdyDCAQTFTHGLRo33OMSExdwC0bREP8AFyLRGDVEa1gkYmuLWCmmFNvpQJm2Q6fTzm99y1384/dry6ZR40u+ue8l773zOffcc+65wv/pKvaCGV1CyLPLkVxE4tutbROt+Pffyf8LwB9S4P1yKZ/zK+jZYPdfIaIPuNRhVv4XAJ2xjU95rs5vIwDqrQcRZ7WvJhNUSmuolFZSMkOUAiS2RRQ/+tirz3zp1NCyzyGJwj58W/AH3s8NHOc0NJCr9z4TwDz5YW7ly+lASYs7H/HbnUgx9sJtrCiyWqP5xOZuZehyj1yEqOcgkqBEoT2YEIZqs73DxaRMqVhBFcy6KyQ02uGFe7bQtFo63GZ/JpgrwlMA1ImbfZOvJYhQYC6xor4QdBheOna3NI7PbRbvbiOE271EbxelzkpMWo20U2gg8mCCnFafrqypHkwgAgJQUajT3ibnnvVdGY8+FlazUtXCv56BAsVCUauWTf4eJ/birsTXbrLfz1vR0CdyauMi8KLKdt609LdUahlfzj7NPjkdlAcNk8ksddOCIAMAIOiIidVjRHpMju79gO91P+634tRFzwKQIgTxqx2cVxUnQ6XeZ79duU274VB6vD7KYqPC6ye3UNQV1y5+j32sAXPCo4AEj4QMQgHoAUToDyNTEKtrVHv6d5jk9/k9c8QveRpAhsMjK8ckHV5uFimXupVlSQ+qPS4cTmEkgyTjs8c+yR3dS6E6sOEFvDDbWU6z0CyJjoMsgxD6gv5YnhhFuC6099xvJkePwcKpNfDY8tegZ18lU9K7YJnuxCVVgPF9D430FfVfL3cgDin4AE6BVWBhb/N0/rqwDtx+8PkpAO8h+D6sGd0syZmXyej55H+uAaC3jV5FXVvK1f2bEl1cn5i0ZqICSSxULVSLvmoFiHBeb4ZNjft50dg2njM0y8PZOgoicl/GWsdrlv4aIx4YHsxAOAUTRBPMeFg88ktVWdL7wo1zqMRHzLZWRN6b9whMKDwigAqgnyQVAKHuu1wS3cm7h27i2tEfktgUMgOF4ZcHLuOWRy4j5Pug2A/egvPg3CkF2UDUuFjiOs1tz8P4oIl18VwX1CtEfL80SRiMT1PsQXuaeYN7Dr+E63vvZd6NQCTgDT0zzmd2fpHEpVy99ldo0wO9qr/UToTC+1hCeH2+0PxF0oid0eJIXXRuCDL8jCR9etqWHA+Vl3PdzEe5x2ymVxmHpBjEWoNVHJG1fPDP3yLppFx+1h0QdcCsBsrgXR/E+wtMyUyAP6jKCBLUkoCKQ5BB9sjJLCKAdwqXG7wTdri13N28kDwzkKbQy/tKC0gtZJZj6STfefAaFmdq0DoKnYchXwRrBypWyHxnQzjnAcwZkzvYOn3xmT4oPAqPEALIIL2azQqd4zW2ujVs0c9jPhqnmjbJVCDzHuvKhCKCyIFRoBUg7Oycze7OajYe3AUTbcimIR6Eo7B15tL3h5sn/mB+9Pe3lKZK85Pea7zXhKDAK3BClkYstsrUyhm7W1Pc2b2AUKlQSVpoAso5cpdT5GV8FBOMBqVACa1eiWm1jI1qFxwHKh0oDkM0DoVHLEvCUGxMBwlF0AetN7igcUETvCBO4dOIEXHUopxLxh/iT7PrmOlNISGADYTCI7lFRzk+Nngd4ZUmKEXodshyBSuA+UFxlEVwCVhDEJf7YefM+eX5/JiNdhc+pnARzht8UKhCUTIWKTuIPBsmpvniilv5wT8u5ZHuKmrVgjOnDlJp5OxcOIf759aTS6lvXBQqm2MiPjIoYEAGKAe+DT6Bsp/rRLZrFoqEInAgd7EtXGwK34fQtkB56ee/6deBDaP7uGHyJo6qOvXhLiNLWqhqoBWqfOmud/GTnZeCEnxQLKvs4ezRxyAHPFAMUtmmoCAY/+Bo5guTeYULsjd38ULmyksLF2O9IXIasa6fBdZQLI4gVKmOdKgPHQM8vqt5ZG4JW2fX0D2SU+8sEJQit47XvuAPrKodhUXAPrkFciBp2znuJQ+YLoECvT9x8a7UJi/LbJncxMQuR+UOco3KLKKFh/adwVFbo9bo0C0X/K3T4K7HzuFwezmYElVznNxrzp36O+/YdDcqDdAB3ImNqw/gcdt7md4hAuZR1WFTqHcWvP51r0gu7tlEyq5MyWdoa1E9A7EjLqUMJwf58cMXsPuhteRBYymhTIm67uHyAq9h/eq9fPiNv2FV7Rg0gd7AsB+ASMisUzc3jG3eN9Po94Q/l8/ggpw2rIs7RpOjZy+pzDNUPkY1bhNFGVLPoZHBUMYRXWLL4bXsmJviiW6NzBtM5BkfbXLh+j1csvFBlo51+vEuBgoD4w7CXPVxO1vfjPHT8Y1P9PuBA6rLNWF05h7vv98pajckRc/EOsOoAiUO0+3vAejAeCPj6rU7ecO6XSwQ09WKcj1lbKxJtZEiEdAexNufqqZYCIcrhJn6o3Y0PXqiH9YAvw1beT4baQfZU0I9VxPOMtqilUMrhwqhvxkOPpIgxNoylPQYq7YZTrrE4hA3WHB24PnAazIhtCPcfC13w72vlh+v3zdz7jw3/i70AQAWylO8wo+kzSAPGvRFWliulUOLR4lH+4D4AUTol2mcGjQletCYKCgUWBlACPQ0tGLoGuuMu+lo7L6ZjabFyk8Xp2YAYMbuYql6Pq/U0ZFZz191UBuUsFwNAOQEhBsY9+oUhB1A5BpyNRgNdA0slvCdaC5L9VcOt+OvlFPTXvqN1smsPAkAsC1sR+n1XGnMwX94/miCWimotSIoERDpN8HKCrgnz8DA85PGNaETUSzU8rxnbm9l6kP3HmrcrnOVnnHrE0/Z4Z/1aHZ2dDU3mhEOeUamTPHOkaj3vnrcXlWLWyRRl5LO0LpARQ7KFkoOYgexx+rAojUc79btYjpy8yFbua6hfOvFD3wS2PwMW/rZAOb9Lm6xO9hUWp/eadk2pWSLQhZDUCMh6EYIyggK8YIUCsn1Sc+L1OTdNNqdO/laC3/9eNRsXnj/m4Ern83Uf3Y4/Xz9KqatkivLxcSyKH9BQ2fnVUx2eknn45HOS0ZcIcod89rvz41/II3d9p2V9qGJIg4btk3/23//D6fjH/HBsZv5SzqsXpd0ogmTq654f28WFT/91F1ePvIR4Ov/8d/+CfHfpDCA/XdnAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE3LTExLTA5VDExOjM1OjI1KzAxOjAw560ORgAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNy0xMS0wOVQxMTozNToyNSswMTowMJbwtvoAAABXelRYdFJhdyBwcm9maWxlIHR5cGUgaXB0YwAAeJzj8gwIcVYoKMpPy8xJ5VIAAyMLLmMLEyMTS5MUAxMgRIA0w2QDI7NUIMvY1MjEzMQcxAfLgEigSi4A6hcRdPJCNZUAAAAASUVORK5CYII=">Customize Firefox</a>
            <dt><a href="https://www.mozilla.org/en-US/contribute/" icon="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E %3Cpath d='M0 0h16v16H0z'/%3E %3Cpath d='M13.994 10.356H15V12h-3.171V7.741c0-1.308-.435-1.81-1.29-1.81-1.04 0-1.46.737-1.46 1.8v2.63h1.006V12H6.918V7.741c0-1.308-.435-1.81-1.291-1.81-1.039 0-1.459.737-1.459 1.8v2.63h1.441V12H1v-1.644h1.006V6.079H1V4.435h3.168v1.139a2.507 2.507 0 0 1 2.3-1.29A2.452 2.452 0 0 1 8.931 5.91 2.535 2.535 0 0 1 11.4 4.284 2.448 2.448 0 0 1 14 6.9v3.458z' fill='%23fff'/%3E %3C/svg%3E">Get Involved</a>
            <dt><a href="https://www.mozilla.org/en-US/about/" icon="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E %3Cpath d='M0 0h16v16H0z'/%3E %3Cpath d='M13.994 10.356H15V12h-3.171V7.741c0-1.308-.435-1.81-1.29-1.81-1.04 0-1.46.737-1.46 1.8v2.63h1.006V12H6.918V7.741c0-1.308-.435-1.81-1.291-1.81-1.039 0-1.459.737-1.459 1.8v2.63h1.441V12H1v-1.644h1.006V6.079H1V4.435h3.168v1.139a2.507 2.507 0 0 1 2.3-1.29A2.452 2.452 0 0 1 8.931 5.91 2.535 2.535 0 0 1 11.4 4.284 2.448 2.448 0 0 1 14 6.9v3.458z' fill='%23fff'/%3E %3C/svg%3E">About Us</a>
        </dl>
</dl>
PK
!<����'chrome/en-US/locale/browser/browser.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!-- LOCALIZATION NOTE : FILE This file contains the browser main menu items -->
<!-- LOCALIZATION NOTE : FILE Do not translate commandkeys -->

<!-- LOCALIZATION NOTE (mainWindow.title): DONT_TRANSLATE -->
<!ENTITY mainWindow.title "&brandFullName;">
<!-- LOCALIZATION NOTE (mainWindow.titlemodifier) : DONT_TRANSLATE -->
<!ENTITY mainWindow.titlemodifier "&brandFullName;">
<!-- LOCALIZATION NOTE (mainWindow.titlemodifiermenuseparator): DONT_TRANSLATE -->
<!ENTITY mainWindow.titlemodifiermenuseparator " - ">
<!-- LOCALIZATION NOTE (mainWindow.titlePrivateBrowsingSuffix): This will be appended to the window's title
                                                                inside the private browsing mode -->
<!ENTITY mainWindow.titlePrivateBrowsingSuffix "(Private Browsing)">

<!ENTITY appmenu.tooltip                     "Open menu">
<!ENTITY navbarOverflow.label                "More tools…">

<!-- Tab context menu -->
<!ENTITY  reloadTab.label                    "Reload Tab">
<!ENTITY  reloadTab.accesskey                "R">
<!ENTITY  reloadAllTabs.label                "Reload All Tabs">
<!ENTITY  reloadAllTabs.accesskey            "A">
<!-- LOCALIZATION NOTE (duplicateTab.label): This is a command to duplicate
a tab (i.e. it is a verb, not adjective). -->
<!ENTITY  duplicateTab.label                 "Duplicate Tab">
<!ENTITY  duplicateTab.accesskey             "D">
<!-- LOCALIZATION NOTE (closeTabsToTheEnd.label): This should indicate the
direction in which tabs are closed, i.e. locales that use RTL mode should say
left instead of right. -->
<!ENTITY  closeTabsToTheEnd.label            "Close Tabs to the Right">
<!ENTITY  closeTabsToTheEnd.accesskey        "i">
<!ENTITY  closeOtherTabs.label               "Close Other Tabs">
<!ENTITY  closeOtherTabs.accesskey           "o">

<!-- LOCALIZATION NOTE (pinTab.label, unpinTab.label): "Pin" is being
used as a metaphor for expressing the fact that these tabs are "pinned" to the
left edge of the tabstrip. Really we just want the string to express the idea
that this is a lightweight and reversible action that keeps your tab where you
can reach it easily. -->
<!ENTITY  pinTab.label                       "Pin Tab">
<!ENTITY  pinTab.accesskey                   "P">
<!ENTITY  unpinTab.label                     "Unpin Tab">
<!ENTITY  unpinTab.accesskey                 "b">
<!ENTITY  sendTabToDevice.label              "Send Tab to Device">
<!ENTITY  sendTabToDevice.accesskey          "n">
<!ENTITY  sendPageToDevice.label             "Send Page to Device">
<!ENTITY  sendPageToDevice.accesskey         "n">
<!ENTITY  sendLinkToDevice.label             "Send Link to Device">
<!ENTITY  sendLinkToDevice.accesskey         "n">
<!ENTITY  sendToDeviceFeedback.label         "Sent!">
<!ENTITY  sendToDeviceOfflineFeedback.label  "Queued (offline)">
<!ENTITY  moveToNewWindow.label              "Move to New Window">
<!ENTITY  moveToNewWindow.accesskey          "W">
<!ENTITY  bookmarkAllTabs.label              "Bookmark All Tabs…">
<!ENTITY  bookmarkAllTabs.accesskey          "T">
<!ENTITY  undoCloseTab.label                 "Undo Close Tab">
<!ENTITY  undoCloseTab.accesskey             "U">
<!ENTITY  closeTab.label                     "Close Tab">
<!ENTITY  closeTab.accesskey                 "c">

<!ENTITY  listAllTabs.label      "List all tabs">

<!ENTITY tabCmd.label "New Tab">
<!ENTITY tabCmd.accesskey "T">
<!ENTITY tabCmd.commandkey "t">
<!-- LOCALIZATION NOTE (openLocationCmd.label): "Open Location" is only
displayed on OS X, and only on windows that aren't main browser windows, or
when there are no windows but Firefox is still running. -->
<!ENTITY openLocationCmd.label "Open Location…">
<!ENTITY openFileCmd.label "Open File…">
<!ENTITY openFileCmd.accesskey "O">
<!ENTITY openFileCmd.commandkey "o">
<!ENTITY printSetupCmd.label "Page Setup…">
<!ENTITY printSetupCmd.accesskey "u">
<!ENTITY printPreviewCmd.label "Print Preview">
<!ENTITY printPreviewCmd.accesskey "v">
<!ENTITY printCmd.label "Print…">
<!ENTITY printCmd.accesskey "P">
<!ENTITY printCmd.commandkey "p">

<!ENTITY goOfflineCmd.label "Work Offline">
<!ENTITY goOfflineCmd.accesskey "k">

<!ENTITY menubarCmd.label "Menu Bar">
<!ENTITY menubarCmd.accesskey "M">
<!ENTITY navbarCmd.label "Navigation Toolbar">
<!ENTITY personalbarCmd.label "Bookmarks Toolbar">
<!ENTITY personalbarCmd.accesskey "B">
<!ENTITY bookmarksToolbarItem.label "Bookmarks Toolbar Items">

<!ENTITY toolbarContextMenu.reloadAllTabs.label "Reload All Tabs">
<!ENTITY toolbarContextMenu.reloadAllTabs.accesskey "A">
<!ENTITY toolbarContextMenu.bookmarkAllTabs.label "Bookmark All Tabs…">
<!ENTITY toolbarContextMenu.bookmarkAllTabs.accesskey "T">
<!ENTITY toolbarContextMenu.undoCloseTab.label "Undo Close Tab">
<!ENTITY toolbarContextMenu.undoCloseTab.accesskey "U">

<!ENTITY pageSourceCmd.label "Page Source">
<!ENTITY pageSourceCmd.accesskey "o">
<!ENTITY pageSourceCmd.commandkey "u">
<!-- LOCALIZATION NOTE (pageSourceCmd.SafariCommandKey should match the
Option+Command keyboard shortcut letter that Safari and Chrome use for "View
Source" on macOS. pageSourceCmd.commandkey above is Firefox's official keyboard
shortcut shown in the GUI. SafariCommandKey is an alias provided for the
convenience of Safari and Chrome users on macOS. See bug 1398988. -->
<!ENTITY pageSourceCmd.SafariCommandKey "u">

<!ENTITY pageInfoCmd.label "Page Info">
<!ENTITY pageInfoCmd.accesskey "I">
<!ENTITY pageInfoCmd.commandkey "i">
<!ENTITY ldbCmd.label "Layout Debugger">
<!ENTITY ldbCmd.accesskey "L">
<!ENTITY mirrorTabCmd.label "Mirror Tab">
<!ENTITY mirrorTabCmd.accesskey "m">
<!-- LOCALIZATION NOTE (enterFullScreenCmd.label, exitFullScreenCmd.label):
These should match what Safari and other Apple applications use on OS X Lion. -->
<!ENTITY enterFullScreenCmd.label "Enter Full Screen">
<!ENTITY enterFullScreenCmd.accesskey "F">
<!ENTITY exitFullScreenCmd.label "Exit Full Screen">
<!ENTITY exitFullScreenCmd.accesskey "F">
<!ENTITY fullScreenCmd.label "Full Screen">
<!ENTITY fullScreenCmd.accesskey "F">
<!ENTITY fullScreenCmd.macCommandKey "f">
<!ENTITY showAllTabsCmd.label "Show All Tabs">
<!ENTITY showAllTabsCmd.accesskey "A">
<!ENTITY toggleReaderMode.key "R">
<!ENTITY toggleReaderMode.win.keycode "VK_F9">

<!ENTITY fxaSignIn.label "Sign in to &syncBrand.shortName.label;">
<!ENTITY fxaSignedIn.tooltip "Open &syncBrand.shortName.label; preferences">
<!ENTITY fxaSignInError.label "Reconnect to &syncBrand.shortName.label;">
<!ENTITY fxaUnverified.label "Verify Your Account">


<!ENTITY fullScreenMinimize.tooltip "Minimize">
<!ENTITY fullScreenRestore.tooltip "Restore">
<!ENTITY fullScreenClose.tooltip "Close">
<!ENTITY fullScreenAutohide.label "Hide Toolbars">
<!ENTITY fullScreenAutohide.accesskey "H">
<!ENTITY fullScreenExit.label "Exit Full Screen Mode">
<!ENTITY fullScreenExit.accesskey "F">

<!-- LOCALIZATION NOTE (fullscreenWarning.beforeDomain.label,
     fullscreenWarning.afterDomain.label): these two strings are used
     respectively before and after the domain requiring fullscreen.
     Localizers can use one of them, or both, to better adapt this
     sentence to their language. -->
<!ENTITY fullscreenWarning.beforeDomain.label "">
<!ENTITY fullscreenWarning.afterDomain.label "is now full screen">
<!ENTITY fullscreenWarning.generic.label "This document is now full screen">

<!-- LOCALIZATION NOTE (exitDOMFullscreen.button,
     exitDOMFullscreenMac.button): the "escape" button on PC keyboards
     is uppercase, while on Mac keyboards it is lowercase -->
<!ENTITY exitDOMFullscreen.button "Exit Full Screen (Esc)">
<!ENTITY exitDOMFullscreenMac.button "Exit Full Screen (esc)">
<!ENTITY leaveDOMFullScreen.label "Exit Full Screen">
<!ENTITY leaveDOMFullScreen.accesskey "u">

<!-- LOCALIZATION NOTE (pointerlockWarning.beforeDomain.label,
     pointerlockWarning.afterDomain.label): these two strings are used
     respectively before and after the domain requiring pointerlock.
     Localizers can use one of them, or both, to better adapt this
     sentence to their language. -->
<!ENTITY pointerlockWarning.beforeDomain.label "">
<!ENTITY pointerlockWarning.afterDomain.label "has control of your pointer. Press Esc to take back control.">
<!ENTITY pointerlockWarning.generic.label "This document has control of your pointer. Press Esc to take back control.">

<!ENTITY closeWindow.label "Close Window">
<!ENTITY closeWindow.accesskey "d">

<!ENTITY bookmarksMenu.label "Bookmarks">
<!ENTITY bookmarksMenu.accesskey "B">
<!ENTITY bookmarkThisPageCmd.label "Bookmark This Page">
<!ENTITY editThisBookmarkCmd.label "Edit This Bookmark">
<!ENTITY bookmarkThisPageCmd.commandkey "d">

<!ENTITY subscribeToPageMenupopup.label "Subscribe to This Page">
<!ENTITY subscribeToPageMenuitem.label "Subscribe to This Page…">
<!ENTITY addCurPagesCmd.label "Bookmark All Tabs…">
<!ENTITY showAllBookmarks2.label "Show All Bookmarks">
<!ENTITY recentBookmarks.label "Recently Bookmarked">
<!ENTITY otherBookmarksCmd.label "Other Bookmarks">
<!ENTITY mobileBookmarksCmd.label "Mobile Bookmarks">
<!ENTITY bookmarksToolbarChevron.tooltip "Show more bookmarks">

<!ENTITY backCmd.label                "Back">
<!ENTITY backCmd.accesskey            "B">
<!ENTITY backButton.tooltip           "Go back one page">
<!ENTITY forwardCmd.label             "Forward">
<!ENTITY forwardCmd.accesskey         "F">
<!ENTITY forwardButton.tooltip        "Go forward one page">
<!ENTITY backForwardButtonMenu.tooltip "Right-click or pull down to show history">
<!ENTITY backForwardButtonMenuMac.tooltip "Pull down to show history">
<!ENTITY reloadCmd.label              "Reload">
<!ENTITY reloadCmd.accesskey          "R">
<!ENTITY stopCmd.label                "Stop">
<!ENTITY stopCmd.accesskey            "S">
<!ENTITY stopCmd.macCommandKey        ".">
<!ENTITY goEndCap.tooltip             "Go to the address in the Location Bar">
<!ENTITY printButton.label            "Print">
<!ENTITY printButton.tooltip          "Print this page">

<!ENTITY urlbar.viewSiteInfo.label                      "View site information">

<!ENTITY urlbar.defaultNotificationAnchor.tooltip         "Open message panel">
<!ENTITY urlbar.geolocationNotificationAnchor.tooltip     "Open location request panel">
<!ENTITY urlbar.addonsNotificationAnchor.tooltip          "Open add-on installation message panel">
<!ENTITY urlbar.canvasNotificationAnchor.tooltip          "Manage canvas extraction permission">
<!ENTITY urlbar.indexedDBNotificationAnchor.tooltip       "Open offline storage message panel">
<!ENTITY urlbar.passwordNotificationAnchor.tooltip        "Open save password message panel">
<!ENTITY urlbar.pluginsNotificationAnchor.tooltip         "Manage plug-in use">
<!ENTITY urlbar.webNotificationAnchor.tooltip             "Change whether you can receive notifications from the site">
<!ENTITY urlbar.persistentStorageNotificationAnchor.tooltip     "Store data in Persistent Storage">
<!ENTITY urlbar.remoteControlNotificationAnchor.tooltip   "Browser is under remote control">

<!ENTITY urlbar.webRTCShareDevicesNotificationAnchor.tooltip      "Manage sharing your camera and/or microphone with the site">
<!ENTITY urlbar.webRTCShareMicrophoneNotificationAnchor.tooltip   "Manage sharing your microphone with the site">
<!ENTITY urlbar.webRTCShareScreenNotificationAnchor.tooltip       "Manage sharing your windows or screen with the site">

<!ENTITY urlbar.servicesNotificationAnchor.tooltip        "Open install message panel">
<!ENTITY urlbar.translateNotificationAnchor.tooltip       "Translate this page">
<!ENTITY urlbar.translatedNotificationAnchor.tooltip      "Manage page translation">
<!ENTITY urlbar.emeNotificationAnchor.tooltip             "Manage use of DRM software">
<!ENTITY urlbar.midiNotificationAnchor.tooltip            "Open MIDI panel">

<!ENTITY urlbar.cameraBlocked.tooltip            "You have blocked your camera for this website.">
<!ENTITY urlbar.microphoneBlocked.tooltip        "You have blocked your microphone for this website.">
<!ENTITY urlbar.screenBlocked.tooltip            "You have blocked this website from sharing your screen.">
<!ENTITY urlbar.geolocationBlocked.tooltip       "You have blocked location information for this website.">
<!ENTITY urlbar.webNotificationsBlocked.tooltip  "You have blocked notifications for this website.">
<!ENTITY urlbar.persistentStorageBlocked.tooltip "You have blocked persistent storage for this website.">
<!ENTITY urlbar.popupBlocked.tooltip             "You have blocked pop-ups for this website.">
<!ENTITY urlbar.canvasBlocked.tooltip            "You have blocked canvas data extraction for this website.">
<!ENTITY urlbar.flashPluginBlocked.tooltip       "You have blocked this website from using the Adobe Flash plugin.">
<!ENTITY urlbar.midiBlocked.tooltip              "You have blocked MIDI access for this website.">

<!ENTITY urlbar.openHistoryPopup.tooltip                "Show history">

<!ENTITY searchItem.title             "Search">

<!-- Toolbar items -->
<!ENTITY homeButton.label             "Home">

<!ENTITY bookmarksButton.label          "Bookmarks">
<!ENTITY bookmarksCmd.commandkey "b">

<!ENTITY bookmarksSubview.label             "Bookmarks">
<!ENTITY bookmarksMenuButton2.label         "Bookmarks Menu">
<!ENTITY bookmarksMenuButton.other.label    "Other Bookmarks">
<!ENTITY bookmarksMenuButton.mobile.label   "Mobile Bookmarks">
<!ENTITY viewBookmarksSidebar2.label        "View Bookmarks Sidebar">
<!ENTITY hideBookmarksSidebar.label         "Hide Bookmarks Sidebar">
<!ENTITY viewBookmarksToolbar.label         "View Bookmarks Toolbar">
<!ENTITY hideBookmarksToolbar.label         "Hide Bookmarks Toolbar">
<!ENTITY searchBookmarks.label              "Search Bookmarks">
<!ENTITY bookmarkingTools.label             "Bookmarking Tools">
<!ENTITY addBookmarksMenu.label             "Add Bookmarks Menu to Toolbar">
<!ENTITY removeBookmarksMenu.label          "Remove Bookmarks Menu from Toolbar">

<!-- LOCALIZATION NOTE (bookmarksSidebarGtkCmd.commandkey): This command
  -  key should not contain the letters A-F, since these are reserved
  -  shortcut keys on Linux. -->
<!ENTITY bookmarksGtkCmd.commandkey "o">
<!ENTITY bookmarksWinCmd.commandkey "i">

<!ENTITY historyButton.label            "History">
<!ENTITY historySidebarCmd.commandKey   "h">

<!ENTITY toolsMenu.label              "Tools">
<!ENTITY toolsMenu.accesskey          "T">

<!ENTITY keywordfield.label           "Add a Keyword for this Search…">
<!ENTITY keywordfield.accesskey       "K">

<!ENTITY downloads.label              "Downloads">
<!-- LOCALIZATION NOTE (libraryDownloads.label): This label is similar to
  -  downloads.label, but used in the Library panel. -->
<!ENTITY libraryDownloads.label       "Downloads">
<!ENTITY downloads.accesskey          "D">
<!ENTITY downloads.commandkey         "j">
<!ENTITY downloadsUnix.commandkey     "y">
<!ENTITY addons.label                 "Add-ons">
<!ENTITY addons.accesskey             "A">
<!ENTITY addons.commandkey            "A">

<!ENTITY webDeveloperMenu.label       "Web Developer">
<!ENTITY webDeveloperMenu.accesskey   "W">

<!ENTITY inspectContextMenu.label     "Inspect Element">
<!ENTITY inspectContextMenu.accesskey "Q">

<!ENTITY fileMenu.label         "File">
<!ENTITY fileMenu.accesskey       "F">
<!ENTITY newUserContext.label             "New Container Tab">
<!ENTITY newUserContext.accesskey         "B">
<!ENTITY newNavigatorCmd.label        "New Window">
<!ENTITY newNavigatorCmd.key        "N">
<!ENTITY newNavigatorCmd.accesskey      "N">
<!ENTITY newPrivateWindow.label     "New Private Window">
<!ENTITY newPrivateWindow.accesskey "W">

<!ENTITY editMenu.label         "Edit">
<!ENTITY editMenu.accesskey       "E">
<!ENTITY undoCmd.label            "Undo">
<!ENTITY undoCmd.key            "Z">
<!ENTITY undoCmd.accesskey          "U">
<!ENTITY redoCmd.label            "Redo">
<!ENTITY redoCmd.key            "Y">
<!ENTITY redoCmd.accesskey          "R">
<!ENTITY cutCmd.label           "Cut">
<!ENTITY cutCmd.key             "X">
<!ENTITY cutCmd.accesskey         "t">
<!ENTITY copyCmd.label            "Copy">
<!ENTITY copyCmd.key            "C">
<!ENTITY copyCmd.accesskey          "C">
<!ENTITY pasteCmd.label           "Paste">
<!ENTITY pasteCmd.key           "V">
<!ENTITY pasteCmd.accesskey         "P">
<!ENTITY deleteCmd.label          "Delete">
<!ENTITY deleteCmd.key            "D">
<!ENTITY deleteCmd.accesskey        "D">
<!ENTITY selectAllCmd.label         "Select All">
<!ENTITY selectAllCmd.key         "A">
<!ENTITY selectAllCmd.accesskey       "A">
<!ENTITY preferencesCmd2.label       "Options">
<!ENTITY preferencesCmd2.accesskey     "O">
<!ENTITY preferencesCmdUnix.label       "Preferences">
<!ENTITY preferencesCmdUnix.accesskey     "n">

<!ENTITY clearRecentHistory.label               "Clear Recent History…">

<!ENTITY privateBrowsingCmd.commandkey          "P">

<!ENTITY viewMenu.label         "View">
<!ENTITY viewMenu.accesskey       "V">
<!ENTITY viewToolbarsMenu.label       "Toolbars">
<!ENTITY viewToolbarsMenu.accesskey     "T">
<!ENTITY viewSidebarMenu.label "Sidebar">
<!ENTITY viewSidebarMenu.accesskey "e">
<!ENTITY viewCustomizeToolbar.label       "Customize…">
<!ENTITY viewCustomizeToolbar.accesskey     "C">
<!ENTITY overflowCustomizeToolbar.label       "Customize Toolbar…">
<!ENTITY overflowCustomizeToolbar.accesskey   "C">

<!ENTITY historyMenu.label "History">
<!ENTITY historyMenu.accesskey "s">
<!ENTITY historyUndoMenu.label "Recently Closed Tabs">
<!-- LOCALIZATION NOTE (historyUndoWindowMenu): see bug 394759 -->
<!ENTITY historyUndoWindowMenu.label "Recently Closed Windows">
<!ENTITY historyRestoreLastSession.label "Restore Previous Session">

<!ENTITY showAllHistoryCmd2.label "Show All History">
<!ENTITY showAllHistoryCmd.commandkey "H">

<!ENTITY appMenuHistory.showAll.label "Show All History">
<!ENTITY appMenuHistory.clearRecent.label "Clear Recent History…">
<!ENTITY appMenuHistory.restoreSession.label "Restore Previous Session">
<!ENTITY appMenuHistory.viewSidebar.label "View History Sidebar">
<!ENTITY appMenuHistory.recentHistory.label "Recent History">
<!ENTITY appMenuHelp.label "Help">

<!ENTITY appMenuRemoteTabs.label "Synced Tabs">
<!-- LOCALIZATION NOTE (appMenuRemoteTabs.notabs.label): This is shown beneath
     the name of a device when that device has no open tabs -->
<!ENTITY appMenuRemoteTabs.notabs.label "No open tabs">
<!-- LOCALIZATION NOTE (appMenuRemoteTabs.showMore.label, appMenuRemoteTabs.showMore.tooltip):
     This is shown after the tabs list if we can display more tabs by clicking on the button -->
<!ENTITY appMenuRemoteTabs.showMore.label "Show More">
<!ENTITY appMenuRemoteTabs.showMore.tooltip "Show more tabs from this device">
<!-- LOCALIZATION NOTE (appMenuRemoteTabs.showAll.label, appMenuRemoteTabs.showAll.tooltip):
     This is shown after the tabs list if we can all the remaining tabs by clicking on the button -->
<!ENTITY appMenuRemoteTabs.showAll.label "Show All">
<!ENTITY appMenuRemoteTabs.showAll.tooltip "Show all tabs from this device">
<!-- LOCALIZATION NOTE (appMenuRemoteTabs.tabsnotsyncing.label): This is shown
     when Sync is configured but syncing tabs is disabled. -->
<!ENTITY appMenuRemoteTabs.tabsnotsyncing.label "Turn on tab syncing to view a list of tabs from your other devices.">
<!-- LOCALIZATION NOTE (appMenuRemoteTabs.noclients.label): This is shown
     when Sync is configured but this appears to be the only device attached to
     the account. We also show links to download Firefox for android/ios. -->
<!ENTITY appMenuRemoteTabs.noclients.subtitle "Want to see your tabs from other devices here?">
<!ENTITY appMenuRemoteTabs.opensyncprefs.label "Open Sync Preferences">
<!ENTITY appMenuRemoteTabs.notsignedin.label "Sign in to view a list of tabs from your other devices.">
<!ENTITY appMenuRemoteTabs.unverified.label "Your account needs to be verified.">
<!ENTITY appMenuRemoteTabs.signin.label "Sign in to Sync">
<!ENTITY appMenuRemoteTabs.managedevices.label "Manage Devices…">
<!ENTITY appMenuRemoteTabs.sidebar.label "View Synced Tabs Sidebar">
<!ENTITY appMenuRemoteTabs.connectdevice.label "Connect Another Device">

<!ENTITY appMenuRecentHighlights.label "Recent Highlights">

<!ENTITY customizeMenu.addToToolbar.label "Add to Toolbar">
<!ENTITY customizeMenu.addToToolbar.accesskey "A">
<!ENTITY customizeMenu.addToPanel.label "Add to Menu">
<!ENTITY customizeMenu.addToPanel.accesskey "M">
<!-- LOCALIZATION NOTE (customizeMenu.addToOverflowMenu.label,
     customizeMenu.pinToOverflowMenu.label, customizeMenu.unpinFromOverflowMenu.label)
     The overflow menu is the menu that appears if you click the chevron (>> button)
     in the location bar. These labels are only used in Photon, where you can put
     items into this menu permanently (pinned). -->
<!ENTITY customizeMenu.addToOverflowMenu.label "Add to Overflow Menu">
<!ENTITY customizeMenu.addToOverflowMenu.accesskey "M">
<!ENTITY customizeMenu.moveToToolbar.label "Move to Toolbar">
<!ENTITY customizeMenu.moveToToolbar.accesskey "o">
<!-- LOCALIZATION NOTE (customizeMenu.moveToPanel.accesskey, customizeMenu.pinToOverflowMenu.accesskey)
     can appear on the same context menu as menubarCmd and personalbarCmd,
     so they should have different access keys. customizeMenu.moveToToolbar and
     customizeMenu.moveToPanel are mutually exclusive, so can share access
     keys.  -->
<!ENTITY customizeMenu.moveToPanel.label "Move to Menu">
<!ENTITY customizeMenu.moveToPanel.accesskey "o">
<!ENTITY customizeMenu.pinToOverflowMenu.label "Pin to Overflow Menu">
<!ENTITY customizeMenu.pinToOverflowMenu.accesskey "P">
<!ENTITY customizeMenu.unpinFromOverflowMenu.label "Unpin from Overflow Menu">
<!ENTITY customizeMenu.unpinFromOverflowMenu.accesskey "U">
<!ENTITY customizeMenu.removeFromToolbar.label "Remove from Toolbar">
<!ENTITY customizeMenu.removeFromToolbar.accesskey "R">
<!ENTITY customizeMenu.removeFromMenu.label "Remove from Menu">
<!ENTITY customizeMenu.removeFromMenu.accesskey "R">
<!ENTITY customizeMenu.addMoreItems.label "Add More Items…">
<!ENTITY customizeMenu.addMoreItems.accesskey "A">

<!-- LOCALIZATION NOTE (moreMenu.label) This label is used in the new Photon
    app (hamburger) menu. When clicked, it opens a subview that contains
    secondary commands. -->
<!ENTITY moreMenu.label "More">

<!ENTITY openCmd.commandkey           "l">
<!ENTITY urlbar.placeholder2          "Search or enter address">
<!ENTITY urlbar.accesskey             "d">
<!-- LOCALIZATION NOTE (urlbar.extension.label): Used to indicate that a selected autocomplete entry is provided by an extension. -->
<!ENTITY urlbar.extension.label       "Extension:">
<!ENTITY urlbar.switchToTab.label     "Switch to tab:">

<!-- LOCALIZATION NOTE (urlbar.searchSuggestionsNotification.hintPrefix): Shown just before the suggestions opt-out hint. -->
<!ENTITY urlbar.searchSuggestionsNotification.hintPrefix "Tip:">
<!-- LOCALIZATION NOTE (urlbar.searchSuggestionsNotification.hint): &#x1F50E; is the magnifier icon emoji, please don't change it. -->
<!ENTITY urlbar.searchSuggestionsNotification.hint "Get help finding things! Look for the &#x1F50E; next to search suggestions.">
<!ENTITY urlbar.searchSuggestionsNotification.changeSettingsWin "Change Options…">
<!ENTITY urlbar.searchSuggestionsNotification.changeSettingsWin.accesskey "C">
<!ENTITY urlbar.searchSuggestionsNotification.changeSettingsUnix "Change Preferences…">
<!ENTITY urlbar.searchSuggestionsNotification.changeSettingsUnix.accesskey "C">

<!--
  Comment duplicated from browser-sets.inc:

  Search Command Key Logic works like this:

  Unix: Ctrl+J (0.8, 0.9 support)
        Ctrl+K (cross platform binding)
  Mac:  Cmd+K (cross platform binding)
        Cmd+Opt+F (platform convention)
  Win:  Ctrl+K (cross platform binding)
        Ctrl+E (IE compat)

  We support Ctrl+K on all platforms now and advertise it in the menu since it is
  our standard - it is a "safe" choice since it is near no harmful keys like "W" as
  "E" is. People mourning the loss of Ctrl+K for emacs compat can switch their GTK
  system setting to use emacs emulation, and we should respect it. Focus-Search-Box
  is a fundamental keybinding and we are maintaining a XP binding so that it is easy
  for people to switch to Linux.

 -->
<!ENTITY searchFocus.commandkey       "k">
<!ENTITY searchFocus.commandkey2      "e">
<!ENTITY searchFocusUnix.commandkey   "j">

<!-- LOCALIZATION NOTE (contentSearchInput.label):
     This is set as the aria-label attribute for the search input box in the
     in-content search UI, to be used by screen readers. -->
<!ENTITY contentSearchInput.label     "Search query">
<!ENTITY contentSearchSubmit.tooltip  "Submit search">

<!-- LOCALIZATION NOTE (searchInput.placeholder):
     This string is displayed in the search box when the input field is empty. -->
<!ENTITY searchInput.placeholder      "Search">
<!ENTITY searchIcon.tooltip           "Search">

<!-- LOCALIZATION NOTE (searchFor.label, searchWith.label):
     These two strings are used to build the header above the list of one-click
     search providers:  "Search for <used typed keywords> with:" -->
<!ENTITY searchFor.label              "Search for ">
<!ENTITY searchWith.label             " with:">

<!-- LOCALIZATION NOTE (search.label, searchAfter.label):
     This string is used to build the header above the list of one-click search
     providers when a one off engine has been selected.  The searchAfter text is
     intentionally left empty for en-US and can be used by other localizations to
     display a string after the search engine name.  This string will be displayed
     as:  "Search <selected engine name><searchAfter.label text>" -->
<!ENTITY search.label                 "Search ">
<!ENTITY searchAfter.label            "">

<!-- LOCALIZATION NOTE (searchWithHeader.label):
     The wording of this string should be as close as possible to
     searchFor.label and searchWith.label. This string will be used instead of
     them when the user has not typed any keyword. -->
<!ENTITY searchWithHeader.label       "Search with:">
<!-- LOCALIZATION NOTE (changeSearchSettings.button):
     This string won't wrap, so if the translated string is longer,
     consider translating it as if it said only "Search Settings". -->
<!ENTITY changeSearchSettings.button  "Change Search Settings">
<!ENTITY changeSearchSettings.tooltip "Change search settings">

<!ENTITY searchInNewTab.label         "Search in New Tab">
<!ENTITY searchInNewTab.accesskey     "T">
<!ENTITY searchSetAsDefault.label     "Set As Default Search Engine">
<!ENTITY searchSetAsDefault.accesskey "D">

<!ENTITY openLinkCmdInTab.label       "Open Link in New Tab">
<!ENTITY openLinkCmdInTab.accesskey   "T">
<!ENTITY openLinkCmd.label            "Open Link in New Window">
<!ENTITY openLinkCmd.accesskey        "W">
<!ENTITY openLinkInPrivateWindowCmd.label "Open Link in New Private Window">
<!ENTITY openLinkInPrivateWindowCmd.accesskey "P">
<!ENTITY openLinkCmdInCurrent.label     "Open Link">
<!ENTITY openLinkCmdInCurrent.accesskey "O">
<!ENTITY openFrameCmdInTab.label      "Open Frame in New Tab">
<!ENTITY openFrameCmdInTab.accesskey  "T">
<!ENTITY openFrameCmd.label           "Open Frame in New Window">
<!ENTITY openFrameCmd.accesskey       "W">
<!ENTITY openLinkCmdInContainerTab.label "Open Link in New Container Tab">
<!ENTITY openLinkCmdInContainerTab.accesskey "b">
<!ENTITY showOnlyThisFrameCmd.label     "Show Only This Frame">
<!ENTITY showOnlyThisFrameCmd.accesskey "S">
<!ENTITY reloadCmd.commandkey         "r">
<!ENTITY reloadFrameCmd.label         "Reload Frame">
<!ENTITY reloadFrameCmd.accesskey     "R">
<!ENTITY viewPartialSourceForSelectionCmd.label "View Selection Source">
<!ENTITY viewPartialSourceForMathMLCmd.label    "View MathML Source">
<!-- LOCALIZATION NOTE (viewPartialSourceCmd.accesskey): This accesskey is used for both
         viewPartialSourceForSelectionCmd.label and viewPartialSourceForMathMLCmd.label -->
<!ENTITY viewPartialSourceCmd.accesskey "e">
<!ENTITY viewPageSourceCmd.label      "View Page Source">
<!ENTITY viewPageSourceCmd.accesskey  "V">
<!ENTITY viewFrameSourceCmd.label     "View Frame Source">
<!ENTITY viewFrameSourceCmd.accesskey "V">
<!ENTITY viewPageInfoCmd.label        "View Page Info">
<!ENTITY viewPageInfoCmd.accesskey    "I">
<!ENTITY viewFrameInfoCmd.label       "View Frame Info">
<!ENTITY viewFrameInfoCmd.accesskey   "I">
<!ENTITY reloadImageCmd.label         "Reload Image">
<!ENTITY reloadImageCmd.accesskey     "R">
<!ENTITY viewImageCmd.label           "View Image">
<!ENTITY viewImageCmd.accesskey       "I">
<!ENTITY viewImageInfoCmd.label       "View Image Info">
<!ENTITY viewImageInfoCmd.accesskey   "f">
<!ENTITY viewImageDescCmd.label       "View Description">
<!ENTITY viewImageDescCmd.accesskey   "D">
<!ENTITY viewVideoCmd.label           "View Video">
<!ENTITY viewVideoCmd.accesskey       "I">
<!ENTITY viewBGImageCmd.label         "View Background Image">
<!ENTITY viewBGImageCmd.accesskey     "w">
<!ENTITY setDesktopBackgroundCmd.label      "Set As Desktop Background…">
<!ENTITY setDesktopBackgroundCmd.accesskey  "S">
<!ENTITY bookmarkPageCmd2.label       "Bookmark This Page">
<!ENTITY bookmarkPageCmd2.accesskey   "m">
<!ENTITY bookmarkThisLinkCmd.label      "Bookmark This Link">
<!ENTITY bookmarkThisLinkCmd.accesskey  "L">
<!ENTITY bookmarkThisFrameCmd.label      "Bookmark This Frame">
<!ENTITY bookmarkThisFrameCmd.accesskey  "m">
<!ENTITY pageAction.copyLink.label    "Copy Link">
<!ENTITY copyURLFeedback.label        "Copied!">
<!ENTITY emailPageCmd.label           "Email Link…">
<!ENTITY emailPageCmd.accesskey       "E">
<!ENTITY savePageCmd.label            "Save Page As…">
<!ENTITY savePageCmd.accesskey        "A">
<!-- alternate for content area context menu -->
<!ENTITY savePageCmd.accesskey2       "P">
<!ENTITY savePageCmd.commandkey       "s">
<!ENTITY saveFrameCmd.label           "Save Frame As…">
<!ENTITY saveFrameCmd.accesskey       "F">
<!ENTITY printFrameCmd.label          "Print Frame…">
<!ENTITY printFrameCmd.accesskey      "P">
<!ENTITY saveLinkCmd.label            "Save Link As…">
<!ENTITY saveLinkCmd.accesskey        "k">
<!ENTITY saveImageCmd.label           "Save Image As…">
<!ENTITY saveImageCmd.accesskey       "v">
<!ENTITY saveVideoCmd.label           "Save Video As…">
<!ENTITY saveVideoCmd.accesskey       "v">
<!ENTITY saveAudioCmd.label           "Save Audio As…">
<!ENTITY saveAudioCmd.accesskey       "v">
<!ENTITY emailImageCmd.label          "Email Image…">
<!ENTITY emailImageCmd.accesskey      "g">
<!ENTITY emailVideoCmd.label          "Email Video…">
<!ENTITY emailVideoCmd.accesskey      "a">
<!ENTITY emailAudioCmd.label          "Email Audio…">
<!ENTITY emailAudioCmd.accesskey      "a">
<!ENTITY playPluginCmd.label          "Activate this plugin">
<!ENTITY playPluginCmd.accesskey      "c">
<!ENTITY hidePluginCmd.label          "Hide this plugin">
<!ENTITY hidePluginCmd.accesskey      "H">
<!ENTITY copyLinkCmd.label            "Copy Link Location">
<!ENTITY copyLinkCmd.accesskey        "a">
<!ENTITY copyImageCmd.label           "Copy Image Location">
<!ENTITY copyImageCmd.accesskey       "o">
<!ENTITY copyImageContentsCmd.label   "Copy Image">
<!ENTITY copyImageContentsCmd.accesskey  "y">
<!ENTITY copyVideoURLCmd.label        "Copy Video Location">
<!ENTITY copyVideoURLCmd.accesskey    "o">
<!ENTITY copyAudioURLCmd.label        "Copy Audio Location">
<!ENTITY copyAudioURLCmd.accesskey    "o">
<!ENTITY copyEmailCmd.label           "Copy Email Address">
<!ENTITY copyEmailCmd.accesskey       "E">
<!ENTITY thisFrameMenu.label              "This Frame">
<!ENTITY thisFrameMenu.accesskey          "h">

<!-- Media (video/audio) controls -->
<!-- LOCALIZATION NOTE: The access keys for "Play" and
"Pause" are the same because the two context-menu
items are mutually exclusive. -->
<!ENTITY mediaPlay.label             "Play">
<!ENTITY mediaPlay.accesskey         "P">
<!ENTITY mediaPause.label            "Pause">
<!ENTITY mediaPause.accesskey        "P">
<!-- LOCALIZATION NOTE: The access keys for "Mute" and
"Unmute" are the same because the two context-menu
items are mutually exclusive. -->
<!ENTITY mediaMute.label             "Mute">
<!ENTITY mediaMute.accesskey         "M">
<!ENTITY mediaUnmute.label           "Unmute">
<!ENTITY mediaUnmute.accesskey       "m">
<!ENTITY mediaPlaybackRate2.label     "Play Speed">
<!ENTITY mediaPlaybackRate2.accesskey "d">
<!ENTITY mediaPlaybackRate050x2.label "Slow (0.5×)">
<!ENTITY mediaPlaybackRate050x2.accesskey "S">
<!ENTITY mediaPlaybackRate100x2.label "Normal">
<!ENTITY mediaPlaybackRate100x2.accesskey "N">
<!ENTITY mediaPlaybackRate125x2.label "Fast (1.25×)">
<!ENTITY mediaPlaybackRate125x2.accesskey "F">
<!ENTITY mediaPlaybackRate150x2.label "Faster (1.5×)">
<!ENTITY mediaPlaybackRate150x2.accesskey "a">
<!-- LOCALIZATION NOTE: "Ludicrous" is a reference to the
movie "Space Balls" and is meant to say that this speed is very
fast. -->
<!ENTITY mediaPlaybackRate200x2.label "Ludicrous (2×)">
<!ENTITY mediaPlaybackRate200x2.accesskey "L">
<!ENTITY mediaLoop.label             "Loop">
<!ENTITY mediaLoop.accesskey         "L">
<!-- LOCALIZATION NOTE: The access keys for "Show Controls" and
"Hide Controls" are the same because the two context-menu
items are mutually exclusive. -->
<!ENTITY mediaShowControls.label     "Show Controls">
<!ENTITY mediaShowControls.accesskey "C">
<!ENTITY mediaHideControls.label     "Hide Controls">
<!ENTITY mediaHideControls.accesskey "C">
<!ENTITY videoFullScreen.label       "Full Screen">
<!ENTITY videoFullScreen.accesskey   "F">
<!ENTITY videoSaveImage.label        "Save Snapshot As…">
<!ENTITY videoSaveImage.accesskey    "S">
<!-- LOCALIZATION NOTE: The access keys for "Show Statistics" and
"Hide Statistics" are the same because the two context-menu
items are mutually exclusive. -->
<!ENTITY videoShowStats.label        "Show Statistics">
<!ENTITY videoShowStats.accesskey    "t">
<!ENTITY videoHideStats.label        "Hide Statistics">
<!ENTITY videoHideStats.accesskey    "t">

<!-- LOCALIZATION NOTE :
fullZoomEnlargeCmd.commandkey3, fullZoomReduceCmd.commandkey2 and
fullZoomResetCmd.commandkey2 are alternative acceleration keys for zoom.
If shift key is needed with your locale popular keyboard for them,
you can use these alternative items. Otherwise, their values should be empty.  -->

<!ENTITY fullZoomEnlargeCmd.label       "Zoom In">
<!ENTITY fullZoomEnlargeCmd.accesskey   "I">
<!ENTITY fullZoomEnlargeCmd.commandkey  "+">
<!ENTITY fullZoomEnlargeCmd.commandkey2 "="> <!-- + is above this key on many keyboards -->
<!ENTITY fullZoomEnlargeCmd.commandkey3 "">

<!ENTITY fullZoomReduceCmd.label        "Zoom Out">
<!ENTITY fullZoomReduceCmd.accesskey    "O">
<!ENTITY fullZoomReduceCmd.commandkey   "-">
<!ENTITY fullZoomReduceCmd.commandkey2  "">

<!ENTITY fullZoomResetCmd.label         "Reset">
<!ENTITY fullZoomResetCmd.accesskey     "R">
<!ENTITY fullZoomResetCmd.commandkey    "0">
<!ENTITY fullZoomResetCmd.commandkey2   "">

<!ENTITY fullZoomToggleCmd.label        "Zoom Text Only">
<!ENTITY fullZoomToggleCmd.accesskey    "T">
<!ENTITY fullZoom.label                 "Zoom">
<!ENTITY fullZoom.accesskey             "Z">

<!ENTITY sidebarCloseButton.tooltip     "Close sidebar">
<!ENTITY sidebarMenuClose.label         "Close Sidebar">

<!ENTITY quitApplicationCmdWin2.label       "Exit">
<!ENTITY quitApplicationCmdWin2.accesskey   "x">
<!ENTITY quitApplicationCmdWin2.tooltip     "Exit &brandShorterName;">
<!ENTITY goBackCmd.commandKey "[">
<!ENTITY goForwardCmd.commandKey "]">
<!ENTITY quitApplicationCmd.label       "Quit">
<!ENTITY quitApplicationCmd.accesskey   "Q">
<!ENTITY quitApplicationCmdMac2.label   "Quit &brandShorterName;">
<!ENTITY quitApplicationCmd.key         "Q">

<!ENTITY closeCmd.label                 "Close">
<!ENTITY closeCmd.key                   "W">
<!ENTITY closeCmd.accesskey             "C">

<!ENTITY toggleMuteCmd.key              "M">

<!ENTITY pageStyleMenu.label "Page Style">
<!ENTITY pageStyleMenu.accesskey "y">
<!ENTITY pageStyleNoStyle.label "No Style">
<!ENTITY pageStyleNoStyle.accesskey "n">
<!ENTITY pageStylePersistentOnly.label "Basic Page Style">
<!ENTITY pageStylePersistentOnly.accesskey "b">

<!ENTITY allowPopups.accesskey "p">
<!-- On Windows we use the term "Options" to describe settings, but
     on Linux and Mac OS X we use "Preferences" - carry that distinction
     over into this string, which is used in the "popup blocked" info bar . -->
<!ENTITY editPopupSettingsUnix.label "Edit Pop-up Blocker Preferences…">
<!ENTITY editPopupSettings.label "Edit Pop-up Blocker Options…">
<!ENTITY editPopupSettings.accesskey "E">
<!ENTITY dontShowMessage.accesskey "D">

<!ENTITY bidiSwitchPageDirectionItem.label        "Switch Page Direction">
<!ENTITY bidiSwitchPageDirectionItem.accesskey    "D">
<!ENTITY bidiSwitchTextDirectionItem.label        "Switch Text Direction">
<!ENTITY bidiSwitchTextDirectionItem.accesskey    "w">
<!ENTITY bidiSwitchTextDirectionItem.commandkey   "X">

<!ENTITY findOnCmd.label     "Find in This Page…">
<!ENTITY findOnCmd.accesskey "F">
<!ENTITY findOnCmd.commandkey "f">
<!ENTITY findAgainCmd.label  "Find Again">
<!ENTITY findAgainCmd.accesskey "g">
<!ENTITY findAgainCmd.commandkey "g">
<!ENTITY findAgainCmd.commandkey2 "VK_F3">
<!ENTITY findSelectionCmd.commandkey "e">

<!ENTITY spellAddDictionaries.label "Add Dictionaries…">
<!ENTITY spellAddDictionaries.accesskey "A">

<!ENTITY editBookmark.done.label                     "Done">
<!ENTITY editBookmark.removeBookmark.accessKey       "R">

<!-- LOCALIZATION NOTE (identity.securityView.label)
     This is the header of the security subview in the Site Identity panel. -->
<!ENTITY identity.securityView.label "Site Security">

<!ENTITY identity.connectionSecure "Secure Connection">
<!ENTITY identity.connectionNotSecure "Connection Is Not Secure">
<!ENTITY identity.connectionFile "This page is stored on your computer.">
<!ENTITY identity.connectionVerified2 "You are securely connected to this site, owned by:">
<!ENTITY identity.connectionInternal "This is a secure &brandShortName; page.">
<!ENTITY identity.extensionPage "This page is loaded from an extension.">
<!ENTITY identity.insecureLoginForms2 "Logins entered on this page could be compromised.">

<!-- Strings for connection state warnings. -->
<!ENTITY identity.activeBlocked "&brandShortName; has blocked parts of this page that are not secure.">
<!ENTITY identity.passiveLoaded "Parts of this page are not secure (such as images).">
<!ENTITY identity.activeLoaded "You have disabled protection on this page.">
<!ENTITY identity.weakEncryption "This page uses weak encryption.">

<!-- Strings for connection state warnings in the subview. -->
<!ENTITY identity.description.insecure "Your connection to this site is not private. Information you submit could be viewed by others (like passwords, messages, credit cards, etc.).">
<!ENTITY identity.description.insecureLoginForms "The login information you enter on this page is not secure and could be compromised.">
<!ENTITY identity.description.weakCipher "Your connection to this website uses weak encryption and is not private.">
<!ENTITY identity.description.weakCipher2 "Other people can view your information or modify the website’s behavior.">
<!ENTITY identity.description.activeBlocked "&brandShortName; has blocked parts of this page that are not secure.">
<!ENTITY identity.description.passiveLoaded "Your connection is not private and information you share with the site could be viewed by others.">
<!ENTITY identity.description.passiveLoaded2 "This website contains content that is not secure (such as images).">
<!ENTITY identity.description.passiveLoaded3 "Although &brandShortName; has blocked some content, there is still content on the page that is not secure (such as images).">
<!ENTITY identity.description.activeLoaded "This website contains content that is not secure (such as scripts) and your connection to it is not private.">
<!ENTITY identity.description.activeLoaded2 "Information you share with this site could be viewed by others (like passwords, messages, credit cards, etc.).">

<!ENTITY identity.enableMixedContentBlocking.label "Enable protection">
<!ENTITY identity.enableMixedContentBlocking.accesskey "E">
<!ENTITY identity.disableMixedContentBlocking.label "Disable protection for now">
<!ENTITY identity.disableMixedContentBlocking.accesskey "D">
<!ENTITY identity.learnMore "Learn More">

<!ENTITY identity.removeCertException.label "Remove Exception">
<!ENTITY identity.removeCertException.accesskey "R">

<!ENTITY identity.moreInfoLinkText2 "More Information">

<!ENTITY identity.permissions "Permissions">
<!ENTITY identity.permissionsEmpty "You have not granted this site any special permissions.">
<!ENTITY identity.permissionsReloadHint "You may need to reload the page for changes to apply.">

<!-- Name for the tabs toolbar as spoken by screen readers.
     The word "toolbar" is appended automatically and should not be contained below! -->
<!ENTITY tabsToolbar.label "Browser tabs">

<!-- LOCALIZATION NOTE (syncTabsMenu3.label): This appears in the history menu -->
<!ENTITY syncTabsMenu3.label     "Synced Tabs">

<!ENTITY syncedTabs.sidebar.label              "Synced Tabs">
<!ENTITY syncedTabs.sidebar.noclients.label    "Sign in to Firefox from your other devices to view their tabs here.">
<!ENTITY syncedTabs.sidebar.noclients.subtitle "Want to see your tabs from other devices here?">
<!ENTITY syncedTabs.sidebar.notsignedin.label  "Sign in to view a list of tabs from your other devices.">
<!ENTITY syncedTabs.sidebar.unverified.label   "Your account needs to be verified.">
<!ENTITY syncedTabs.sidebar.notabs.label       "No open tabs">
<!ENTITY syncedTabs.sidebar.openprefs.label    "Open &syncBrand.shortName.label; Preferences">
<!-- LOCALIZATION NOTE (syncedTabs.sidebar.tabsnotsyncing.label): This is shown
     when Sync is configured but syncing tabs is disabled. -->
<!ENTITY syncedTabs.sidebar.tabsnotsyncing.label       "Turn on tab syncing to view a list of tabs from your other devices.">
<!ENTITY syncedTabs.sidebar.searchPlaceholder  "Search synced tabs">
<!ENTITY syncedTabs.sidebar.connectAnotherDevice  "Connect Another Device">

<!-- LOCALIZATION NOTE (syncedTabs.context.open.accesskey,
                        syncedTabs.context.openAllInTabs.accesskey):
     These access keys are identical because their associated menu items are
     mutually exclusive -->
<!ENTITY syncedTabs.context.open.label                       "Open">
<!ENTITY syncedTabs.context.open.accesskey                   "O">
<!ENTITY syncedTabs.context.openInNewTab.label               "Open in a New Tab">
<!ENTITY syncedTabs.context.openInNewTab.accesskey           "w">
<!ENTITY syncedTabs.context.openInNewWindow.label            "Open in a New Window">
<!ENTITY syncedTabs.context.openInNewWindow.accesskey        "N">
<!ENTITY syncedTabs.context.openInNewPrivateWindow.label     "Open in a New Private Window">
<!ENTITY syncedTabs.context.openInNewPrivateWindow.accesskey "P">
<!ENTITY syncedTabs.context.bookmarkSingleTab.label          "Bookmark This Tab…">
<!ENTITY syncedTabs.context.bookmarkSingleTab.accesskey      "B">
<!ENTITY syncedTabs.context.copy.label                       "Copy">
<!ENTITY syncedTabs.context.copy.accesskey                   "C">

<!ENTITY syncedTabs.context.openAllInTabs.label              "Open All in Tabs">
<!ENTITY syncedTabs.context.openAllInTabs.accesskey          "O">
<!ENTITY syncedTabs.context.managedevices.label              "Manage Devices…">
<!ENTITY syncedTabs.context.managedevices.accesskey          "D">


<!ENTITY syncBrand.shortName.label    "Sync">

<!ENTITY syncSignIn.label             "Sign In To &syncBrand.shortName.label;…">
<!ENTITY syncSignIn.accesskey         "Y">
<!ENTITY syncSyncNowItem.label        "Sync Now">
<!ENTITY syncSyncNowItem.accesskey    "S">
<!ENTITY syncReAuthItem.label         "Reconnect to &syncBrand.shortName.label;…">
<!ENTITY syncReAuthItem.accesskey     "R">
<!ENTITY syncToolbarButton.label      "Sync">

<!ENTITY customizeMode.menuAndToolbars.header3 "Drag your favorite items into the toolbar or overflow menu.">
<!ENTITY customizeMode.restoreDefaults "Restore Defaults">
<!ENTITY customizeMode.done "Done">
<!ENTITY customizeMode.titlebar "Title Bar">
<!ENTITY customizeMode.extraDragSpace "Drag Space">
<!ENTITY customizeMode.toolbars2 "Toolbars">
<!ENTITY customizeMode.lwthemes "Themes">
<!ENTITY customizeMode.lwthemes.myThemes "My Themes">
<!ENTITY customizeMode.lwthemes.recommended "Recommended">
<!ENTITY customizeMode.lwthemes.menuManage "Manage">
<!ENTITY customizeMode.lwthemes.menuManage.accessKey "M">
<!ENTITY customizeMode.lwthemes.menuGetMore "Get More Themes">
<!ENTITY customizeMode.lwthemes.menuGetMore.accessKey "G">
<!ENTITY customizeMode.overflowList.title2 "Overflow Menu">
<!ENTITY customizeMode.overflowList.description "Drag and drop items here to keep them within reach but out of your toolbar…">
<!ENTITY customizeMode.uidensity "Density">
<!-- LOCALIZATION NOTE (customizeMode.uidensity.menuNormal.*):
     “Normal” is displayed in the Customize screen, under the Density menu. -->
<!ENTITY customizeMode.uidensity.menuNormal.label "Normal">
<!ENTITY customizeMode.uidensity.menuNormal.tooltip "Normal">
<!ENTITY customizeMode.uidensity.menuNormal.accessKey "N">
<!-- LOCALIZATION NOTE (customizeMode.uidensity.menuCompact.*):
     “Compact” is displayed in the Customize screen, under the Density menu.
     It’s an adjective (Density -> Compact). -->
<!ENTITY customizeMode.uidensity.menuCompact.label "Compact">
<!ENTITY customizeMode.uidensity.menuCompact.tooltip "Compact">
<!ENTITY customizeMode.uidensity.menuCompact.accessKey "C">
<!-- LOCALIZATION NOTE (customizeMode.uidensity.menuTouch.*):
     “Touch” is displayed in the Customize screen, under the Density menu.
     It’s an adjective (Density -> Touch), and it means that control layout is
     optimized for touch devices. -->
<!ENTITY customizeMode.uidensity.menuTouch.label "Touch">
<!ENTITY customizeMode.uidensity.menuTouch.tooltip "Touch">
<!ENTITY customizeMode.uidensity.menuTouch.accessKey "T">
<!ENTITY customizeMode.uidensity.autoTouchMode.checkbox.label "Use Touch for Tablet Mode">

<!ENTITY customizeMode.autoHideDownloadsButton.label "Auto-hide">

<!ENTITY getUserMedia.selectCamera.label "Camera to share:">
<!ENTITY getUserMedia.selectCamera.accesskey "C">
<!ENTITY getUserMedia.selectMicrophone.label "Microphone to share:">
<!ENTITY getUserMedia.selectMicrophone.accesskey "M">
<!ENTITY getUserMedia.audioCapture.label "Audio from the tab will be shared.">
<!ENTITY getUserMedia.allWindowsShared.message "All visible windows on your screen will be shared.">

<!ENTITY trackingProtection.title "Tracking Protection">
<!ENTITY trackingProtection.detectedBlocked3 "&brandShortName; is blocking parts of the page that may track your browsing.">
<!ENTITY trackingProtection.detectedNotBlocked3 "This site includes elements that may track your browsing. You have disabled protection.">
<!ENTITY trackingProtection.notDetected3 "No tracking elements detected on this page.">
<!-- LOCALIZATION NOTE (trackingProtection.unblock.label, trackingProtection.unblock.accesskey):
     The associated button with this label and accesskey is only shown when opening the control
     center while looking at a site with trackers in NON-private browsing mode. -->
<!ENTITY trackingProtection.unblock.label "Disable protection for this site">
<!ENTITY trackingProtection.unblock.accesskey "D">
<!-- LOCALIZATION NOTE (trackingProtection.unblockPrivate.label, trackingProtection.unblockPrivate.accesskey):
     The associated button with this label and accesskey is only shown when opening the control
     center while looking at a site with trackers in PRIVATE browsing mode. -->
<!ENTITY trackingProtection.unblockPrivate.label "Disable protection for this session">
<!ENTITY trackingProtection.unblockPrivate.accesskey "D">
<!ENTITY trackingProtection.block2.label "Enable protection">
<!ENTITY trackingProtection.block2.accesskey "E">

<!ENTITY pluginNotification.showAll.label "Show All">
<!ENTITY pluginNotification.showAll.accesskey "S">

<!-- LOCALIZATION NOTE (pluginActivateNow.label, pluginActivateAlways.label, pluginBlockNow.label): These should be the same as the matching strings in browser.properties -->
<!ENTITY pluginActivateNow.label "Allow Now">
<!ENTITY pluginActivateAlways.label "Allow and Remember">
<!ENTITY pluginBlockNow.label "Block Plugin">

<!-- LOCALIZATION NOTE: (pluginNotification.width): This is used to determine the
     width of the plugin popup notification that can appear if a plugin has been
     blocked on a page. Should be wide enough to fit the pluginActivateNow.label
     and pluginActivateAlways.label strings above on a single line. This must be
     a CSS length value. -->
<!ENTITY pluginNotification.width "28em">

<!ENTITY uiTour.infoPanel.close "Close">

<!-- LOCALIZATION NOTE: (panicButton.view.mainTimeframeDesc, panicButton.view.5min, panicButton.view.2hr, panicButton.view.day):
     The .mainTimeframeDesc string combined with any of the 3 others is meant to form a complete sentence, e.g. "Forget the last: Five minutes".
     Please ensure that this remains the case in the translation. -->
<!ENTITY panicButton.view.mainTimeframeDesc       "Forget the last:">
<!ENTITY panicButton.view.5min                    "Five minutes">
<!ENTITY panicButton.view.2hr                     "Two hours">
<!ENTITY panicButton.view.day                     "24 hours">

<!-- LOCALIZATION NOTE: (panicButton.view.mainLabel, panicButton.view.deleteCookies, panicButton.view.deleteHistory, panicButton.view.deleteTabsAndWindows, panicButton.view.openNewWindow):
     The .mainActionDesc string combined with any of the 4 others is meant to form a complete sentence, e.g. "Proceeding will: Delete Recent Cookies".
     Note also that the deleteCookies, deleteHistory and deleteTabsAndWindows strings include <html:strong> tags for emphasis on the words "Cookies", "History", "Tabs" and "Windows".
     The translation should do the same. -->
<!ENTITY panicButton.view.mainActionDesc          "Proceeding will:">
<!ENTITY panicButton.view.deleteCookies           "Delete Recent <html:strong>Cookies</html:strong>">
<!ENTITY panicButton.view.deleteHistory           "Delete Recent <html:strong>History</html:strong>">
<!ENTITY panicButton.view.deleteTabsAndWindows    "Close all <html:strong>Tabs</html:strong> and <html:strong>Windows</html:strong>">
<!ENTITY panicButton.view.openNewWindow           "Open a new clean Window">

<!ENTITY panicButton.view.undoWarning             "This action cannot be undone.">
<!ENTITY panicButton.view.forgetButton            "Forget!">

<!ENTITY panicButton.thankyou.msg1                "Your recent history is cleared.">
<!ENTITY panicButton.thankyou.msg2                "Safe browsing!">
<!ENTITY panicButton.thankyou.buttonlabel         "Thanks!">

<!ENTITY emeLearnMoreContextMenu.label            "Learn more about DRM…">
<!ENTITY emeLearnMoreContextMenu.accesskey        "D">

<!ENTITY updateAvailable.message "Update your &brandShorterName; for the latest in speed and privacy.">
<!ENTITY updateAvailable.whatsnew.label "See what’s new.">
<!ENTITY updateAvailable.header.message "A new &brandShorterName; update is available.">
<!ENTITY updateAvailable.acceptButton.label "Download Update">
<!ENTITY updateAvailable.acceptButton.accesskey "D">
<!ENTITY updateAvailable.cancelButton.label "Not Now">
<!ENTITY updateAvailable.cancelButton.accesskey "N">
<!ENTITY updateAvailable.panelUI.label "Download &brandShorterName; update">

<!ENTITY updateManual.message "Download a fresh copy of &brandShorterName; and we’ll help you to install it.">
<!ENTITY updateManual.whatsnew.label "See what’s new.">
<!ENTITY updateManual.header.message "&brandShorterName; can’t update to the latest version.">
<!ENTITY updateManual.acceptButton.label "Download &brandShorterName;">
<!ENTITY updateManual.acceptButton.accesskey "D">
<!ENTITY updateManual.cancelButton.label "Not Now">
<!ENTITY updateManual.cancelButton.accesskey "N">
<!ENTITY updateManual.panelUI.label "Download a fresh copy of &brandShorterName;">

<!ENTITY updateRestart.message2 "After a quick restart, &brandShorterName; will restore all your open tabs and windows that are not in Private Browsing mode.">
<!ENTITY updateRestart.header.message2 "Restart to update &brandShorterName;.">
<!ENTITY updateRestart.acceptButton.label "Restart and Restore">
<!ENTITY updateRestart.acceptButton.accesskey "R">
<!ENTITY updateRestart.cancelButton.label "Not Now">
<!ENTITY updateRestart.cancelButton.accesskey "N">
<!ENTITY updateRestart.panelUI.label2 "Restart to update &brandShorterName;">

<!ENTITY newTabControlled.header.message "Your New Tab has changed.">
<!ENTITY newTabControlled.keepButton.label "Keep Changes">
<!ENTITY newTabControlled.keepButton.accesskey "K">
<!ENTITY newTabControlled.disableButton.label "Disable Extension">
<!ENTITY newTabControlled.disableButton.accesskey "D">

<!ENTITY pageActionButton.tooltip "Page actions">
<!ENTITY pageAction.addToUrlbar.label "Add to Address Bar">
<!ENTITY pageAction.removeFromUrlbar.label "Remove from Address Bar">
<!ENTITY pageAction.allowInUrlbar.label "Show in Address Bar">
<!ENTITY pageAction.disallowInUrlbar.label "Don’t Show in Address Bar">
<!ENTITY pageAction.manageExtension.label "Manage Extension…">

<!ENTITY pageAction.sendTabToDevice.label "Send Tab to Device">
<!ENTITY sendToDevice.syncNotReady.label "Syncing Devices…">

<!ENTITY libraryButton.tooltip "View history, saved bookmarks, and more">

<!-- LOCALIZATION NOTE: (accessibilityIndicator.tooltip): This is used to
     display a tooltip for accessibility indicator in toolbar/tabbar. It is also
     used as a textual label for the indicator used by assistive technology
     users. -->
<!ENTITY accessibilityIndicator.tooltip "Accessibility Features Enabled">

<!-- LOCALIZATION NOTE: (addonPostInstallMessage.label):
     The first <image> tag is replaced with the icon for the add-ons menu.
     The second <image> tag is replaced with the icon for the toolbar menu. -->
<!ENTITY addonPostInstallMessage.label "Manage your add-ons by clicking <image class='addon-addon-icon'/> in the <image class='addon-toolbar-icon'/> menu.">
PK
!<	�#��3chrome/en-US/locale/browser/downloads/downloads.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this file,
   - You can obtain one at http://mozilla.org/MPL/2.0/.  -->

<!-- LOCALIZATION NOTE (downloads.title):
     Used by screen readers to describe the Downloads Panel.
     -->
<!ENTITY downloads.title                  "Downloads">

<!-- LOCALIZATION NOTE (downloadDetails.width):
     Width of details for a Downloads Panel item (which directly influences the
     width of the Downloads Panel) expressed using a CSS unit. The longest
     labels that should fit in the item width are usually those of in-progress
     downloads and those of blocked downloads.

     A good rule of thumb is to try to determine the longest string possible
     that an in-progress download could display, and use that value in ch
     units.

     For example, in English, a long string would be:

     59m 59s left - 1022 of 1023 KB (120.5 KB/sec)

     Since Downloads Panel is redesigned to show the detail string including
     the hovering case for an item or an action button.
     Bug 1328519 is for discussing the detail rule of `downloadDetails.width`.
     -->
<!ENTITY downloadDetails.width            "50ch">

<!-- LOCALIZATION NOTE (downloadsSummary.minWidth2):
     Minimum width for the main description of the downloads summary,
     which is displayed at the bottom of the Downloads Panel if the
     number of downloads exceeds the limit that the panel can display.

     A good rule of thumb here is to look at the otherDownloads3 string
     in downloads.properties, and make a reasonable estimate of its
     maximum length. For English, this seems like a reasonable limit:

     999 files downloading

     that's 21 characters, so we set the minimum width to 21ch.
     -->
<!ENTITY downloadsSummary.minWidth2       "21ch">

<!ENTITY cmd.pause.label                  "Pause">
<!ENTITY cmd.pause.accesskey              "P">
<!ENTITY cmd.resume.label                 "Resume">
<!ENTITY cmd.resume.accesskey             "R">
<!ENTITY cmd.cancel.label                 "Cancel">
<!-- LOCALIZATION NOTE (cmd.show.label, cmd.show.accesskey, cmd.showMac.label,
     cmd.showMac.accesskey):
     The show and showMac commands are never shown together, thus they can share
     the same access key (though the two access keys can also be different).
     -->
<!ENTITY cmd.show.label                   "Open Containing Folder">
<!ENTITY cmd.show.accesskey               "F">
<!ENTITY cmd.showMac.label                "Show In Finder">
<!ENTITY cmd.showMac.accesskey            "F">
<!ENTITY cmd.showDownloads.label          "Show Downloads Folder">
<!ENTITY cmd.retry.label                  "Retry">
<!ENTITY cmd.goToDownloadPage.label       "Go To Download Page">
<!ENTITY cmd.goToDownloadPage.accesskey   "G">
<!ENTITY cmd.copyDownloadLink.label       "Copy Download Link">
<!ENTITY cmd.copyDownloadLink.accesskey   "L">
<!ENTITY cmd.removeFromHistory.label      "Remove From History">
<!ENTITY cmd.removeFromHistory.accesskey  "e">
<!ENTITY cmd.clearList2.label             "Clear Preview Panel">
<!ENTITY cmd.clearList2.accesskey         "a">
<!ENTITY cmd.clearDownloads.label         "Clear Downloads">
<!ENTITY cmd.clearDownloads.accesskey     "D">
<!-- LOCALIZATION NOTE (cmd.unblock2.label):
     This command is shown in the context menu when downloads are blocked.
     -->
<!ENTITY cmd.unblock2.label               "Allow Download">
<!ENTITY cmd.unblock2.accesskey           "o">
<!-- LOCALIZATION NOTE (cmd.removeFile.label):
     This is the tooltip of the action button shown when malware is blocked.
     -->
<!ENTITY cmd.removeFile.label             "Remove File">
<!-- LOCALIZATION NOTE (cmd.chooseUnblock.tooltip):
     This is the tooltip of the action button shown when potentially unwanted
     downloads are blocked. This opens a dialog where the user can choose
     whether to unblock or remove the download. Removing is the default option.
     -->
<!ENTITY cmd.chooseUnblock.label          "Remove File or Allow Download">
<!-- LOCALIZATION NOTE (cmd.chooseOpen.tooltip):
     This is the tooltip of the action button shown when uncommon downloads are
     blocked.This opens a dialog where the user can choose whether to open the
     file or remove the download. Opening is the default option.
     -->
<!ENTITY cmd.chooseOpen.label             "Open or Remove File">

<!-- LOCALIZATION NOTE (showMoreInformation.label):
     Displayed when hovering a blocked download, indicates that it's possible to
     show more information for user to take the next action.
     -->
<!ENTITY showMoreInformation.label        "Show more information">

<!-- LOCALIZATION NOTE (openFile.label):
     Displayed when hovering a complete download, indicates that it's possible to
     open the file using an app available in the system.
     -->
<!ENTITY openFile.label                   "Open File">

<!-- LOCALIZATION NOTE (retryDownload.label):
     Displayed when hovering a download which is able to be retried by users,
     indicates that it's possible to download this file again.
     -->
<!ENTITY retryDownload.label              "Retry Download">

<!-- LOCALIZATION NOTE (cancelDownload.label):
     Displayed when hovering a download which is able to be cancelled by users,
     indicates that it's possible to cancel and stop the download.
     -->
<!ENTITY cancelDownload.label             "Cancel Download">

<!-- LOCALIZATION NOTE (blocked.label):
     Shown as a tag before the file name for some types of blocked downloads.
     Note: This string doesn't exist in the UI yet.  See bug 1053890.
     -->
<!ENTITY blocked.label                    "BLOCKED">

<!-- LOCALIZATION NOTE (learnMore.label):
     Shown as a text link for some types of blocked downloads, for example
     malware, when there is an associated explanation page on the Mozilla site.
     Note: This string doesn't exist in the UI yet.  See bug 1053890.
     -->
<!ENTITY learnMore.label                  "Learn More">

<!-- LOCALIZATION NOTE (downloadsHistory.label, downloadsHistory.accesskey):
     This string is shown at the bottom of the Downloads Panel when all the
     downloads fit in the available space, or when there are no downloads in
     the panel at all.
     -->
<!ENTITY downloadsHistory.label           "Show All Downloads">
<!ENTITY downloadsHistory.accesskey       "S">

<!-- LOCALIZATION NOTE (downloadDetails.label):
     This string is shown at the top of the Download Details Panel, to indicate
     that we are showing the details of a single download.
     -->
<!ENTITY downloadDetails.label            "Download Details">

<!-- LOCALIZATION NOTE (openDownloadsFolder.label):
     This command is not currently available in the user interface, but the
     string was preserved by bug 1362207 to be used in a future version.
     -->
<!ENTITY openDownloadsFolder.label        "Open Downloads Folder">

<!ENTITY clearDownloadsButton.label       "Clear Downloads">
<!ENTITY clearDownloadsButton.tooltip     "Clears completed, canceled and failed downloads">

<!-- LOCALIZATION NOTE (downloadsListEmpty.label):
     This string is shown when there are no items in the Downloads view, when it
     is displayed inside a browser tab.
     -->
<!ENTITY downloadsListEmpty.label         "There are no downloads.">

<!-- LOCALIZATION NOTE (downloadsPanelEmpty.label):
     This string is shown when there are no items in the Downloads Panel.
     -->
<!ENTITY downloadsPanelEmpty.label        "No downloads for this session.">

<!-- LOCALIZATION NOTE (downloadsListNoMatch.label):
     This string is shown when some search terms are specified, but there are no
     results in the Downloads view.
     -->
<!ENTITY downloadsListNoMatch.label       "Could not find any matching downloads.">
PK
!<�:ee:chrome/en-US/locale/browser/downloads/downloads.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE (stateStarting):
# Indicates that the download is starting.
stateStarting=Starting…
# LOCALIZATION NOTE (stateScanning):
# Indicates that an external program is scanning the download for viruses.
stateScanning=Scanning for viruses…
# LOCALIZATION NOTE (stateFailed):
# Indicates that the download failed because of an error.
stateFailed=Failed
# LOCALIZATION NOTE (statePaused):
# Indicates that the download was paused by the user.
statePaused=Paused
# LOCALIZATION NOTE (stateCanceled):
# Indicates that the download was canceled by the user.
stateCanceled=Canceled
# LOCALIZATION NOTE (stateCompleted):
# Indicates that the download was completed.
stateCompleted=Completed
# LOCALIZATION NOTE (stateBlockedParentalControls):
# Indicates that the download was blocked by the Parental Controls feature of
# Windows.  "Parental Controls" should be consistently named and capitalized
# with the display of this feature in Windows.  The following article can
# provide a reference for the translation of "Parental Controls" in various
# languages:
# http://windows.microsoft.com/en-US/windows-vista/Set-up-Parental-Controls
stateBlockedParentalControls=Blocked by Parental Controls
# LOCALIZATION NOTE (stateBlockedPolicy):
# Indicates that the download was blocked on Windows because of the "Launching
# applications and unsafe files" setting of the "security zone" associated with
# the target site.  "Security zone" should be consistently named and capitalized
# with the display of this feature in Windows.  The following article can
# provide a reference for the translation of "security zone" in various
# languages:
# http://support.microsoft.com/kb/174360
stateBlockedPolicy=Blocked by your security zone policy
# LOCALIZATION NOTE (stateDirty):
# Indicates that the download was blocked after scanning.
stateDirty=Blocked: May contain a virus or spyware

# LOCALIZATION NOTE (blockedMalware, blockedPotentiallyUnwanted,
#                    blockedUncommon2):
# These strings are shown in the panel for some types of blocked downloads, and
# are immediately followed by the "Learn More" link, thus they must end with a
# period.  You may need to adjust "downloadDetails.width" in "downloads.dtd" if
# this turns out to be longer than the other existing status strings.
# Note: These strings don't exist in the UI yet.  See bug 1053890.
blockedMalware=This file contains a virus or malware.
blockedPotentiallyUnwanted=This file may harm your computer.
blockedUncommon2=This file is not commonly downloaded.

# LOCALIZATION NOTE (fileMovedOrMissing):
# Displayed when a complete download which is not at the original folder.
fileMovedOrMissing=File moved or missing

# LOCALIZATION NOTE (unblockHeaderUnblock, unblockHeaderOpen,
#                    unblockTypeMalware, unblockTypePotentiallyUnwanted2,
#                    unblockTypeUncommon2, unblockTip2, unblockButtonOpen,
#                    unblockButtonUnblock, unblockButtonConfirmBlock):
# These strings are displayed in the dialog shown when the user asks a blocked
# download to be unblocked.  The severity of the threat is expressed in
# descending order by the unblockType strings, it is higher for files detected
# as malware and lower for uncommon downloads.
unblockHeaderUnblock=Are you sure you want to allow this download?
unblockHeaderOpen=Are you sure you want to open this file?
unblockTypeMalware=This file contains a virus or other malware that will harm your computer.
unblockTypePotentiallyUnwanted2=This file is disguised as a helpful download, but it can make unexpected changes to your programs and settings.
unblockTypeUncommon2=This file is not commonly downloaded and may not be safe to open. It may contain a virus or make unexpected changes to your programs and settings.
unblockTip2=You can search for an alternate download source or try again later.
unblockButtonOpen=Open
unblockButtonUnblock=Allow download
unblockButtonConfirmBlock=Remove file

# LOCALIZATION NOTE (sizeWithUnits):
# %1$S is replaced with the size number, and %2$S with the measurement unit.
sizeWithUnits=%1$S %2$S
sizeUnknown=Unknown size

# LOCALIZATION NOTE (statusSeparator, statusSeparatorBeforeNumber):
# These strings define templates for the separation of different elements in the
# status line of a download item.  As a separator, by default we use the Unicode
# character U+2014 'EM DASH' (long dash).  Examples of status lines include
# "Canceled - 222.net", "1.1 MB - website2.com", or "Paused -  1.1 MB".  Note
# that we use a wider space after the separator when it is followed by a number,
# just to avoid visually confusing it with with a minus sign with some fonts.
# If you use a different separator, this might not be necessary.  However, there
# is usually no need to change the separator or the order of the substitutions,
# even for right-to-left languages, unless the defaults are not suitable.
statusSeparator=%1$S \u2014 %2$S
statusSeparatorBeforeNumber=%1$S \u2014  %2$S

fileExecutableSecurityWarning=“%S” is an executable file. Executable files may contain viruses or other malicious code that could harm your computer. Use caution when opening this file. Are you sure you want to launch “%S”?
fileExecutableSecurityWarningTitle=Open Executable File?
fileExecutableSecurityWarningDontAsk=Don’t ask me this again

# LOCALIZATION NOTE (otherDownloads3):
# This is displayed in an item at the bottom of the Downloads Panel when
# there are more downloads than can fit in the list in the panel. Use a
# semi-colon list of plural forms.
# See: http://developer.mozilla.org/en/Localization_and_Plurals
otherDownloads3=%1$S file downloading;%1$S files downloading

# LOCALIZATION NOTE (showLabel, showMacLabel):
# This is displayed when you hover a download item in the Library widget view.
# showMacLabel is only shown on Mac OSX.
showLabel=Open Containing Folder
showMacLabel=Open In Finder
# LOCALIZATION NOTE (openFileLabel):
# Displayed when hovering a complete download, indicates that it's possible to
# open the file using an app available in the system.
openFileLabel=Open File
# LOCALIZATION NOTE (retryLabel):
# Displayed when hovering a download which is able to be retried by users,
# indicates that it's possible to download this file again.
retryLabel=Retry Download
PK
!<��0--8chrome/en-US/locale/browser/downloads/settingsChange.dtd<!-- -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- -->
<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!ENTITY  settingsChangePreferences.label  "Settings can be changed using the Applications tab in &brandShortName;'s Preferences.">
<!ENTITY  settingsChangeOptions.label      "Settings can be changed using the Applications tab in &brandShortName;'s Options.">
PK
!<K[~^��4chrome/en-US/locale/browser/engineManager.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

duplicateTitle=Duplicate Keyword
duplicateEngineMsg=You have chosen a keyword that is currently in use by “%S”. Please select another.
duplicateBookmarkMsg=You have chosen a keyword that is currently in use by a bookmark. Please select another.
PK
!<s�T[[/chrome/en-US/locale/browser/feeds/subscribe.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!ENTITY feedPage.title
  "Viewing Feed">
<!ENTITY feedSubscribeNow
  "Subscribe Now">
<!ENTITY feedLiveBookmarks
  "Live Bookmarks">
PK
!<aX�?	?	6chrome/en-US/locale/browser/feeds/subscribe.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

linkTitleTextFormat=Go to %S
addHandler=Add “%S” (%S) as a Feed Reader?
addHandlerAddButton=Add Feed Reader
addHandlerAddButtonAccesskey=A
handlerRegistered=“%S” is already registered as a Feed Reader
liveBookmarks=Live Bookmarks
subscribeNow=Subscribe Now
chooseApplicationMenuItem=Choose Application…
chooseApplicationDialogTitle=Choose Application
alwaysUse=Always use %S to subscribe to feeds
mediaLabel=Media files

# LOCALIZATION NOTE: The next string is for the size of the enclosed media.
#   e.g. enclosureSizeText : "50.23 MB"
#   %1$S = size (in bytes or megabytes, ...)
#   %2$S = unit of measure (bytes, KB, MB, ...)
enclosureSizeText=%1$S %2$S

bytes=bytes
kilobyte=KB
megabyte=MB
gigabyte=GB

# LOCALIZATION NOTE: The next three strings explains to the user what they're
# doing.
#   e.g. alwaysUseForVideoPodcasts : "Always use Miro to subscribe to video podcasts."
#   %S = application to use (Miro, iTunes, ...)
alwaysUseForFeeds=Always use %S to subscribe to feeds.
alwaysUseForAudioPodcasts=Always use %S to subscribe to podcasts.
alwaysUseForVideoPodcasts=Always use %S to subscribe to video podcasts.

subscribeFeedUsing=Subscribe to this feed using 
subscribeAudioPodcastUsing=Subscribe to this podcast using 
subscribeVideoPodcastUsing=Subscribe to this video podcast using 

feedSubscriptionFeed1=This is a “feed” of frequently changing content on this site.
feedSubscriptionAudioPodcast1=This is a “podcast” of frequently changing content on this site.
feedSubscriptionVideoPodcast1=This is a “video podcast” of frequently changing content on this site.

feedSubscriptionFeed2=You can subscribe to this feed to receive updates when this content changes.
feedSubscriptionAudioPodcast2=You can subscribe to this podcast to receive updates when this content changes.
feedSubscriptionVideoPodcast2=You can subscribe to this video podcast to receive updates when this content changes.

# Protocol Handling
# "Add %appName (%appDomain) as an application for %protocolType links?"
addProtocolHandler=Add %S (%S) as an application for %S links?
addProtocolHandlerAddButton=Add Application
addProtocolHandlerAddButtonAccesskey=APK
!<G�p��8chrome/en-US/locale/browser/lightweightThemes.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

lightweightThemes.recommended-1.name=A Web Browser Renaissance
lightweightThemes.recommended-1.description=A Web Browser Renaissance is (C) Sean.Martell. Available under CC-BY-SA. No warranty.

lightweightThemes.recommended-2.name=Space Fantasy
lightweightThemes.recommended-2.description=Space Fantasy is (C) fx5800p. Available under CC-BY-SA. No warranty.

lightweightThemes.recommended-4.name=Pastel Gradient
lightweightThemes.recommended-4.description=Pastel Gradient is (C) darrinhenein. Available under CC-BY. No warranty.
PK
!<Da��

3chrome/en-US/locale/browser/migration/migration.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->


<!ENTITY migrationWizard.title          "Import Wizard">

<!ENTITY importFrom.label               "Import Options, Bookmarks, History, Passwords and other data from:">
<!ENTITY importFromUnix.label           "Import Preferences, Bookmarks, History, Passwords and other data from:">
<!ENTITY importFromBookmarks.label      "Import Bookmarks from:">

<!ENTITY importFromIE.label             "Microsoft Internet Explorer">
<!ENTITY importFromIE.accesskey         "M">
<!ENTITY importFromEdge.label           "Microsoft Edge">
<!ENTITY importFromEdge.accesskey       "E">
<!ENTITY importFromNothing.label        "Don’t import anything">
<!ENTITY importFromNothing.accesskey    "D">
<!ENTITY importFromSafari.label         "Safari">
<!ENTITY importFromSafari.accesskey     "S">
<!ENTITY importFromCanary.label         "Chrome Canary">
<!ENTITY importFromCanary.accesskey     "n">
<!ENTITY importFromChrome.label         "Chrome">
<!ENTITY importFromChrome.accesskey     "C">
<!ENTITY importFromChromeBeta.label     "Chrome Beta">
<!ENTITY importFromChromeBeta.accesskey "B">
<!ENTITY importFromChromeDev.label      "Chrome Dev">
<!ENTITY importFromChromeDev.accesskey  "D">
<!ENTITY importFromChromium.label       "Chromium">
<!ENTITY importFromChromium.accesskey   "u">
<!ENTITY importFromFirefox.label        "Firefox">
<!ENTITY importFromFirefox.accesskey    "X">
<!ENTITY importFrom360se.label          "360 Secure Browser">
<!ENTITY importFrom360se.accesskey      "3">

<!ENTITY noMigrationSources.label       "No programs that contain bookmarks, history or password data could be found.">

<!ENTITY importSource.title             "Import Settings and Data">
<!ENTITY importItems.title              "Items to Import">
<!ENTITY importItems.label              "Select which items to import:">

<!ENTITY migrating.title                "Importing…">
<!ENTITY migrating.label                "The following items are currently being imported…">

<!ENTITY selectProfile.title            "Select Profile">
<!ENTITY selectProfile.label            "The following profiles are available to import from:">

<!ENTITY done.title                     "Import Complete">
<!ENTITY done.label                     "The following items were successfully imported:">

<!ENTITY closeSourceBrowser.label       "Please ensure the selected browser is closed before continuing.">
PK
!<]��^:chrome/en-US/locale/browser/migration/migration.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

profileName_format=%S %S

# Browser Specific
sourceNameIE=Internet Explorer
sourceNameEdge=Microsoft Edge
sourceNameSafari=Safari
sourceNameCanary=Google Chrome Canary
sourceNameChrome=Google Chrome
sourceNameChromeBeta=Google Chrome Beta
sourceNameChromeDev=Google Chrome Dev
sourceNameChromium=Chromium
sourceNameFirefox=Mozilla Firefox
sourceName360se=360 Secure Browser

importedBookmarksFolder=From %S

importedSafariReadingList=Reading List (From Safari)
importedEdgeReadingList=Reading List (From Edge)

# Import Sources
# Note: When adding an import source for profile reset, add the string name to
# resetProfile.js if it should be listed in the reset dialog.
1_ie=Internet Options
1_edge=Settings
1_safari=Preferences
1_chrome=Preferences
1_360se=Preferences

2_ie=Cookies
2_edge=Cookies
2_safari=Cookies
2_chrome=Cookies
2_firefox=Cookies
2_360se=Cookies

4_ie=Browsing History
4_edge=Browsing History
4_safari=Browsing History
4_chrome=Browsing History
4_firefox_history_and_bookmarks=Browsing History and Bookmarks
4_360se=Browsing History

8_ie=Saved Form History
8_edge=Saved Form History
8_safari=Saved Form History
8_chrome=Saved Form History
8_firefox=Saved Form History
8_360se=Saved Form History

16_ie=Saved Passwords
16_edge=Saved Passwords
16_safari=Saved Passwords
16_chrome=Saved Passwords
16_firefox=Saved Passwords
16_360se=Saved Passwords

32_ie=Favorites
32_edge=Favorites
32_safari=Bookmarks
32_chrome=Bookmarks
32_360se=Bookmarks

64_ie=Other Data
64_edge=Other Data
64_safari=Other Data
64_chrome=Other Data
64_firefox_other=Other Data
64_360se=Other Data

128_firefox=Windows and Tabs

# Automigration undo notification.
# %1$S will be replaced with brandShortName, %2$S will be replaced with the name of the browser we imported from
automigration.undo.message2.all              = Dive right into %1$S! Import your favorite sites, bookmarks, history and passwords from %2$S.
automigration.undo.message2.bookmarks        = Dive right into %1$S! Import your favorite sites and bookmarks from %2$S.
automigration.undo.message2.bookmarks.logins = Dive right into %1$S! Import your favorite sites, bookmarks and passwords from %2$S.
automigration.undo.message2.bookmarks.visits = Dive right into %1$S! Import your favorite sites, bookmarks and history from %2$S.
automigration.undo.message2.logins           = Dive right into %1$S! Import your passwords from %2$S.
automigration.undo.message2.logins.visits    = Dive right into %1$S! Import your favorite sites, history and passwords from %2$S.
automigration.undo.message2.visits           = Dive right into %1$S! Import your favorite sites and history from %2$S.
automigration.undo.keep2.label            = OK, Got it
automigration.undo.keep2.accesskey        = O
automigration.undo.dontkeep2.label        = No Thanks
automigration.undo.dontkeep2.accesskey    = N
automigration.undo.unknownbrowser         = Unknown Browser
PK
!<}��&&(chrome/en-US/locale/browser/netError.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
%brandDTD;

<!ENTITY loadError.label "Problem loading page">
<!ENTITY retry.label "Try Again">
<!ENTITY returnToPreviousPage.label "Go Back">
<!ENTITY advanced.label "Advanced">

<!-- Specific error messages -->

<!ENTITY connectionFailure.title "Unable to connect">
<!ENTITY connectionFailure.longDesc "&sharedLongDesc;">

<!ENTITY deniedPortAccess.title "This address is restricted">
<!ENTITY deniedPortAccess.longDesc "">

<!ENTITY dnsNotFound.pageTitle "Server Not Found">
<!-- Localization note (dnsNotFound.title1) - "Hmm" is a sound made when considering or puzzling over something. You don't have to include it in your translation if your language does not have a written word like this. -->
<!ENTITY dnsNotFound.title1 "Hmm. We’re having trouble finding that site.">
<!ENTITY dnsNotFound.longDesc1 "
<strong>If that address is correct, here are three other things you can try:</strong>
<ul>
  <li>Try again later.</li>
  <li>Check your network connection.</li>
  <li>If you are connected but behind a firewall, check that &brandShortName; has permission to access the Web.</li>
</ul>
">

<!ENTITY fileNotFound.title "File not found">
<!ENTITY fileNotFound.longDesc "
<ul>
  <li>Check the file name for capitalization or other typing errors.</li>
  <li>Check to see if the file was moved, renamed or deleted.</li>
</ul>
">

<!ENTITY fileAccessDenied.title "Access to the file was denied">
<!ENTITY fileAccessDenied.longDesc "
<ul>
  <li>It may have been removed, moved, or file permissions may be preventing access.</li>
</ul>
">

<!ENTITY generic.title "Oops.">
<!ENTITY generic.longDesc "
<p>&brandShortName; can’t load this page for some reason.</p>
">

<!ENTITY captivePortal.title "Log in to network">
<!ENTITY captivePortal.longDesc2 "
<p>You must log in to this network before you can access the Internet.</p>
">

<!ENTITY openPortalLoginPage.label2 "Open Network Login Page">

<!ENTITY malformedURI.pageTitle "Invalid URL">
<!-- Localization note (malformedURI.title1) - "Hmm" is a sound made when considering or puzzling over something. You don't have to include it in your translation if your language does not have a written word like this. -->
<!ENTITY malformedURI.title1 "Hmm. That address doesn’t look right.">

<!ENTITY netInterrupt.title "The connection was interrupted">
<!ENTITY netInterrupt.longDesc "&sharedLongDesc;">

<!ENTITY notCached.title "Document Expired">
<!ENTITY notCached.longDesc "<p>The requested document is not available in &brandShortName;’s cache.</p><ul><li>As a security precaution, &brandShortName; does not automatically re-request sensitive documents.</li><li>Click Try Again to re-request the document from the website.</li></ul>">

<!ENTITY netOffline.title "Offline mode">
<!ENTITY netOffline.longDesc2 "
<ul>
  <li>Press &quot;Try Again&quot; to switch to online mode and reload the page.</li>
</ul>
">

<!ENTITY contentEncodingError.title "Content Encoding Error">
<!ENTITY contentEncodingError.longDesc "
<ul>
  <li>Please contact the website owners to inform them of this problem.</li>
</ul>
">

<!ENTITY unsafeContentType.title "Unsafe File Type">
<!ENTITY unsafeContentType.longDesc "
<ul>
  <li>Please contact the website owners to inform them of this problem.</li>
</ul>
">

<!ENTITY netReset.title "The connection was reset">
<!ENTITY netReset.longDesc "&sharedLongDesc;">

<!ENTITY netTimeout.title "The connection has timed out">
<!ENTITY netTimeout.longDesc "&sharedLongDesc;">

<!ENTITY unknownProtocolFound.title "The address wasn’t understood">
<!ENTITY unknownProtocolFound.longDesc "
<ul>
  <li>You might need to install other software to open this address.</li>
</ul>
">

<!ENTITY proxyConnectFailure.title "The proxy server is refusing connections">
<!ENTITY proxyConnectFailure.longDesc "
<ul>
  <li>Check the proxy settings to make sure that they are correct.</li>
  <li>Contact your network administrator to make sure the proxy server is
    working.</li>
</ul>
">

<!ENTITY proxyResolveFailure.title "Unable to find the proxy server">
<!ENTITY proxyResolveFailure.longDesc "
<ul>
  <li>Check the proxy settings to make sure that they are correct.</li>
  <li>Check to make sure your computer has a working network connection.</li>
  <li>If your computer or network is protected by a firewall or proxy, make sure
    that &brandShortName; is permitted to access the Web.</li>
</ul>
">

<!ENTITY redirectLoop.title "The page isn’t redirecting properly">
<!ENTITY redirectLoop.longDesc "
<ul>
  <li>This problem can sometimes be caused by disabling or refusing to accept
    cookies.</li>
</ul>
">

<!ENTITY unknownSocketType.title "Unexpected response from server">
<!ENTITY unknownSocketType.longDesc "
<ul>
  <li>Check to make sure your system has the Personal Security Manager
    installed.</li>
  <li>This might be due to a non-standard configuration on the server.</li>
</ul>
">

<!ENTITY nssFailure2.title "Secure Connection Failed">
<!ENTITY nssFailure2.longDesc2 "
<ul>
  <li>The page you are trying to view cannot be shown because the authenticity of the received data could not be verified.</li>
  <li>Please contact the website owners to inform them of this problem.</li>
</ul>
">

<!ENTITY certerror.longpagetitle1 "Your connection is not secure">
<!-- Localization note (certerror.introPara) - The text content of the span tag
will be replaced at runtime with the name of the server to which the user
was trying to connect. -->
<!ENTITY certerror.introPara "The owner of <span class='hostname'/> has configured their website improperly.  To protect your information from being stolen, &brandShortName; has not connected to this website.">

<!ENTITY sharedLongDesc "
<ul>
  <li>The site could be temporarily unavailable or too busy. Try again in a few
    moments.</li>
  <li>If you are unable to load any pages, check your computer’s network
    connection.</li>
  <li>If your computer or network is protected by a firewall or proxy, make sure
    that &brandShortName; is permitted to access the Web.</li>
</ul>
">

<!ENTITY cspBlocked.title "Blocked by Content Security Policy">
<!ENTITY cspBlocked.longDesc "<p>&brandShortName; prevented this page from loading in this way because the page has a content security policy that disallows it.</p>">

<!ENTITY corruptedContentErrorv2.title "Corrupted Content Error">
<!ENTITY corruptedContentErrorv2.longDesc "<p>The page you are trying to view cannot be shown because an error in the data transmission was detected.</p><ul><li>Please contact the website owners to inform them of this problem.</li></ul>">


<!ENTITY securityOverride.exceptionButtonLabel "Add Exception…">

<!ENTITY errorReporting.automatic2 "Report errors like this to help Mozilla identify and block malicious sites">
<!ENTITY errorReporting.learnMore "Learn more…">

<!ENTITY remoteXUL.title "Remote XUL">
<!ENTITY remoteXUL.longDesc "<p><ul><li>Please contact the website owners to inform them of this problem.</li></ul></p>">

<!ENTITY sslv3Used.title "Unable to Connect Securely">
<!-- LOCALIZATION NOTE (sslv3Used.longDesc2) - Do not translate
     "SSL_ERROR_UNSUPPORTED_VERSION". -->
<!ENTITY sslv3Used.longDesc2 "Advanced info: SSL_ERROR_UNSUPPORTED_VERSION">

<!-- LOCALIZATION NOTE (certerror.wrongSystemTime2,
                        certerror.wrongSystemTimeWithoutReference) - The <span id='..' />
     tags will be injected with actual values, please leave them unchanged. -->
<!ENTITY certerror.wrongSystemTime2 "<p> &brandShortName; did not connect to <span id='wrongSystemTime_URL'/> because your computer’s clock appears to show the wrong time and this is preventing a secure connection.</p> <p>Your computer is set to <span id='wrongSystemTime_systemDate'/>, when it should be <span id='wrongSystemTime_actualDate'/>. To fix this problem, change your date and time settings to match the correct time.</p>">
<!ENTITY certerror.wrongSystemTimeWithoutReference "<p>&brandShortName; did not connect to <span id='wrongSystemTimeWithoutReference_URL'/> because your computer’s clock appears to show the wrong time and this is preventing a secure connection.</p> <p>Your computer is set to <span id='wrongSystemTimeWithoutReference_systemDate'/>. To fix this problem, change your date and time settings to match the correct time.</p>">

<!ENTITY certerror.pagetitle1  "Insecure Connection">
<!ENTITY certerror.whatShouldIDo.badStsCertExplanation "This site uses HTTP
Strict Transport Security (HSTS) to specify that &brandShortName; may only connect
to it securely. As a result, it is not possible to add an exception for this
certificate.">
<!ENTITY certerror.copyToClipboard.label "Copy text to clipboard">

<!ENTITY inadequateSecurityError.title "Your connection is not secure">
<!-- LOCALIZATION NOTE (inadequateSecurityError.longDesc) - Do not translate
     "NS_ERROR_NET_INADEQUATE_SECURITY". -->
<!ENTITY inadequateSecurityError.longDesc "<p><span class='hostname'></span> uses security technology that is outdated and vulnerable to attack. An attacker could easily reveal information which you thought to be safe. The website administrator will need to fix the server first before you can visit the site.</p><p>Error code: NS_ERROR_NET_INADEQUATE_SECURITY</p>">

<!ENTITY blockedByPolicy.title "Blocked Page">

<!ENTITY prefReset.longDesc "It looks like your network security settings might be causing this. Do you want the default settings to be restored?">
<!ENTITY prefReset.label "Restore default settings">
PK
!<X���2
2
(chrome/en-US/locale/browser/pageInfo.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!ENTITY  pageInfoWindow.width  "600">
<!ENTITY  pageInfoWindow.height "550">

<!ENTITY  copy.key              "C">
<!ENTITY  copy.label            "Copy">
<!ENTITY  copy.accesskey        "C">
<!ENTITY  selectall.key         "A">
<!ENTITY  selectall.label       "Select All">
<!ENTITY  selectall.accesskey   "A">
<!ENTITY  closeWindow.key       "w">

<!ENTITY  generalTab            "General">
<!ENTITY  generalTab.accesskey  "G">
<!ENTITY  generalTitle          "Title:">
<!ENTITY  generalURL            "Address:">
<!ENTITY  generalType           "Type:">
<!ENTITY  generalMode           "Render Mode:">
<!ENTITY  generalSize           "Size:">
<!ENTITY  generalReferrer       "Referring URL:">
<!ENTITY  generalSource         "Cache Source:">
<!ENTITY  generalModified       "Modified:">
<!ENTITY  generalEncoding2      "Text Encoding:">
<!ENTITY  generalMetaName       "Name">
<!ENTITY  generalMetaContent    "Content">

<!ENTITY  mediaTab              "Media">
<!ENTITY  mediaTab.accesskey    "M">
<!ENTITY  mediaLocation         "Location:">
<!ENTITY  mediaText             "Associated Text:">
<!ENTITY  mediaAltHeader        "Alternate Text">
<!ENTITY  mediaAddress          "Address">
<!ENTITY  mediaType             "Type">
<!ENTITY  mediaSize             "Size">
<!ENTITY  mediaCount            "Count">
<!ENTITY  mediaDimension        "Dimensions:">
<!ENTITY  mediaLongdesc         "Long Description:">
<!ENTITY  mediaBlockImage.accesskey "B">
<!ENTITY  mediaSaveAs           "Save As…">
<!ENTITY  mediaSaveAs.accesskey "A">
<!ENTITY  mediaSaveAs2.accesskey "e">
<!ENTITY  mediaPreview          "Media Preview:">

<!ENTITY  feedTab               "Feeds">
<!ENTITY  feedTab.accesskey     "F">

<!ENTITY  permTab               "Permissions">
<!ENTITY  permTab.accesskey     "P">
<!ENTITY  permissionsFor        "Permissions for:">

<!ENTITY  securityTab           "Security">
<!ENTITY  securityTab.accesskey "S">
<!ENTITY  securityView.certView "View Certificate">
<!ENTITY  securityView.accesskey "V">
<!ENTITY  securityView.unknown   "Unknown">


<!ENTITY  securityView.identity.header   "Website Identity">
<!ENTITY  securityView.identity.owner    "Owner:">
<!ENTITY  securityView.identity.domain   "Website:">
<!ENTITY  securityView.identity.verifier "Verified by:">
<!ENTITY  securityView.identity.validity "Expires on:">

<!ENTITY  securityView.privacy.header                   "Privacy &amp; History">
<!ENTITY  securityView.privacy.history                  "Have I visited this website prior to today?">
<!ENTITY  securityView.privacy.cookies                  "Is this website storing information (cookies) on my computer?">
<!ENTITY  securityView.privacy.viewCookies              "View Cookies">
<!ENTITY  securityView.privacy.viewCookies.accessKey    "k">
<!ENTITY  securityView.privacy.passwords                "Have I saved any passwords for this website?">
<!ENTITY  securityView.privacy.viewPasswords            "View Saved Passwords">
<!ENTITY  securityView.privacy.viewPasswords.accessKey  "w">

<!ENTITY  securityView.technical.header                 "Technical Details">

<!ENTITY  helpButton.label                              "Help">
PK
!<.�	q44/chrome/en-US/locale/browser/pageInfo.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

pageInfo.page.title=Page Info - %S
pageInfo.frame.title=Frame Info - %S

noPageTitle=Untitled Page:
unknown=Unknown
notset=Not specified
yes=Yes
no=No

mediaImg=Image
mediaVideo=Video
mediaAudio=Audio
mediaBGImg=Background
mediaBorderImg=Border
mediaListImg=Bullet
mediaCursor=Cursor
mediaObject=Object
mediaEmbed=Embed
mediaLink=Icon
mediaInput=Input
mediaFileSize=%S KB
mediaSize=%Spx \u00D7 %Spx
mediaSelectFolder=Select a Folder to Save the Images
mediaBlockImage=Block Images from %S
mediaUnknownNotCached=Unknown (not cached)
mediaImageType=%S Image
mediaAnimatedImageType=%S Image (animated, %S frames)
mediaDimensions=%Spx \u00D7 %Spx
mediaDimensionsScaled=%Spx \u00D7 %Spx (scaled to %Spx \u00D7 %Spx)

generalQuirksMode=Quirks mode
generalStrictMode=Standards compliance mode
generalSize=%S KB (%S bytes)
generalMetaTag=Meta (1 tag)
generalMetaTags=Meta (%S tags)

feedRss=RSS
feedAtom=Atom
feedXML=XML
feedSubscribe=Subscribe
feedSubscribe.accesskey=u

securityNoOwner=This website does not supply ownership information.
# LOCALIZATION NOTE (securityVisitsNumber):
# Semi-colon list of plural forms.
# See: https://developer.mozilla.org/en/docs/Localization_and_Plurals
# #1 is the number of visits and can be used in all plural forms as needed, e.g.
# for '1': 'Yes, #1 time'
securityVisitsNumber=Yes, once;Yes, #1 times
securityNoVisits=No

# LOCALIZATION NOTE: The next string is for the disk usage of the
# database
#   e.g. indexedDBUsage : "50.23 MB"
#   %1$S = size (in bytes or megabytes, ...)
#   %2$S = unit of measure (bytes, KB, MB, ...)
indexedDBUsage=This website is using %1$S %2$S

permissions.useDefault=Use Default
PK
!<�y���@chrome/en-US/locale/browser/places/bookmarkProperties.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

dialogAcceptLabelAddItem=Add
dialogAcceptLabelSaveItem=Save
dialogAcceptLabelAddLivemark=Subscribe
dialogAcceptLabelAddMulti=Add Bookmarks
dialogAcceptLabelEdit=Save
dialogTitleAddBookmark=New Bookmark
dialogTitleAddLivemark=Subscribe with Live Bookmark
dialogTitleAddFolder=New Folder
dialogTitleAddMulti=New Bookmarks
dialogTitleEdit=Properties for “%S”

bookmarkAllTabsDefault=[Folder Name]
newFolderDefault=New Folder
newBookmarkDefault=New Bookmark
newLivemarkDefault=New Live Bookmark
PK
!<1����:chrome/en-US/locale/browser/places/editBookmarkOverlay.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!ENTITY editBookmarkOverlay.name.label                      "Name:">
<!ENTITY editBookmarkOverlay.name.accesskey                  "N">
<!ENTITY editBookmarkOverlay.location.label                  "Location:">
<!ENTITY editBookmarkOverlay.location.accesskey              "L">
<!ENTITY editBookmarkOverlay.feedLocation.label              "Feed Location:">
<!ENTITY editBookmarkOverlay.feedLocation.accesskey          "F">
<!ENTITY editBookmarkOverlay.siteLocation.label              "Site Location:">
<!ENTITY editBookmarkOverlay.siteLocation.accesskey          "S">
<!ENTITY editBookmarkOverlay.folder.label                    "Folder:">
<!ENTITY editBookmarkOverlay.foldersExpanderDown.tooltip     "Show all the bookmarks folders">
<!ENTITY editBookmarkOverlay.expanderUp.tooltip              "Hide">
<!ENTITY editBookmarkOverlay.tags.label                      "Tags:">
<!ENTITY editBookmarkOverlay.tags.accesskey                  "T">
<!ENTITY editBookmarkOverlay.tagsEmptyDesc.label             "Separate tags with commas">
<!ENTITY editBookmarkOverlay.description.label               "Description:">
<!ENTITY editBookmarkOverlay.description.accesskey           "D">
<!ENTITY editBookmarkOverlay.keyword.label                   "Keyword:">
<!ENTITY editBookmarkOverlay.keyword.accesskey               "K">
<!ENTITY editBookmarkOverlay.tagsExpanderDown.tooltip        "Show all tags">
<!ENTITY editBookmarkOverlay.loadInSidebar.label             "Load this bookmark in the sidebar">
<!ENTITY editBookmarkOverlay.loadInSidebar.accesskey         "h">
<!ENTITY editBookmarkOverlay.choose.label                    "Choose…">
<!ENTITY editBookmarkOverlay.newFolderButton.label           "New Folder">
<!ENTITY editBookmarkOverlay.newFolderButton.accesskey       "o">
PK
!<�X.�rr-chrome/en-US/locale/browser/places/places.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!-- LOCALIZATION NOTE (places.library.title): use "Library", "Archive" or "Repository" -->
<!ENTITY places.library.title  "Library">
<!ENTITY places.library.width  "700">
<!ENTITY places.library.height "500">
<!ENTITY organize.label        "Organize">
<!ENTITY organize.accesskey    "O">
<!ENTITY organize.tooltip      "Organize your bookmarks">

<!ENTITY file.close.label               "Close">
<!ENTITY file.close.accesskey           "C">
<!ENTITY cmd.close.key                  "w">
<!ENTITY views.label                    "Views">
<!ENTITY views.accesskey                "V">
<!ENTITY views.tooltip                  "Change your view">
<!ENTITY view.columns.label             "Show Columns">
<!ENTITY view.columns.accesskey         "C">
<!ENTITY view.sort.label                "Sort">
<!ENTITY view.sort.accesskey            "S">
<!ENTITY view.unsorted.label            "Unsorted">
<!ENTITY view.unsorted.accesskey        "U">
<!ENTITY view.sortAscending.label       "A > Z Sort Order">
<!ENTITY view.sortAscending.accesskey   "A">
<!ENTITY view.sortDescending.label      "Z > A Sort Order">
<!ENTITY view.sortDescending.accesskey  "Z">

<!ENTITY importBookmarksFromHTML.label     "Import Bookmarks from HTML…">
<!ENTITY importBookmarksFromHTML.accesskey "I">
<!ENTITY exportBookmarksToHTML.label       "Export Bookmarks to HTML…">
<!ENTITY exportBookmarksToHTML.accesskey   "E">
<!ENTITY importOtherBrowser.label          "Import Data from Another Browser…">
<!ENTITY importOtherBrowser.accesskey      "A">

<!ENTITY cmd.backup.label               "Backup…">
<!ENTITY cmd.backup.accesskey           "B">
<!ENTITY cmd.restore2.label             "Restore">
<!ENTITY cmd.restore2.accesskey         "R">
<!ENTITY cmd.restoreFromFile.label      "Choose File…">
<!ENTITY cmd.restoreFromFile.accesskey  "C">

<!ENTITY cmd.deleteDomainData.label     "Forget About This Site">
<!ENTITY cmd.deleteDomainData.accesskey "F">

<!ENTITY cmd.open.label                  "Open">
<!ENTITY cmd.open.accesskey              "O">
<!ENTITY cmd.open_window.label           "Open in a New Window">
<!ENTITY cmd.open_window.accesskey       "N">
<!ENTITY cmd.open_private_window.label     "Open in a New Private Window">
<!ENTITY cmd.open_private_window.accesskey "P">
<!ENTITY cmd.open_tab.label              "Open in a New Tab">
<!ENTITY cmd.open_tab.accesskey          "w">
<!ENTITY cmd.open_all_in_tabs.label      "Open All in Tabs">
<!ENTITY cmd.open_all_in_tabs.accesskey  "O">

<!ENTITY cmd.properties.label      "Properties">
<!ENTITY cmd.properties.accesskey  "i">

<!ENTITY cmd.sortby_name.label              "Sort By Name">
<!ENTITY cmd.sortby_name.accesskey          "S">
<!ENTITY cmd.context_sortby_name.accesskey  "r">

<!ENTITY cmd.new_bookmark.label            "New Bookmark…">
<!ENTITY cmd.new_bookmark.accesskey        "B">
<!ENTITY cmd.new_folder.label              "New Folder…">
<!ENTITY cmd.new_folder.accesskey          "o">
<!ENTITY cmd.context_new_folder.accesskey  "F">
<!ENTITY cmd.new_separator.label           "New Separator">
<!ENTITY cmd.new_separator.accesskey       "S">

<!ENTITY cmd.reloadLivebookmark.label      "Reload Live Bookmark">
<!ENTITY cmd.reloadLivebookmark.accesskey  "R">

<!ENTITY col.name.label          "Name">
<!ENTITY col.tags.label          "Tags">
<!ENTITY col.url.label           "Location">
<!ENTITY col.mostrecentvisit.label "Most Recent Visit">
<!ENTITY col.visitcount.label    "Visit Count">
<!ENTITY col.description.label   "Description">
<!ENTITY col.dateadded.label     "Added">
<!ENTITY col.lastmodified.label  "Last Modified">

<!ENTITY historySearch.placeholder  "Search history">
<!ENTITY bookmarksSearch.placeholder  "Search bookmarks">

<!ENTITY cmd.find.key  "f">

<!ENTITY maintenance.label      "Import and Backup">
<!ENTITY maintenance.accesskey  "I">
<!ENTITY maintenance.tooltip    "Import and backup your bookmarks">

<!ENTITY backButton.tooltip  "Go back">

<!ENTITY forwardButton.tooltip  "Go forward">

<!ENTITY detailsPane.more.label "More">
<!ENTITY detailsPane.more.accesskey "e">
<!ENTITY detailsPane.less.label "Less">
<!ENTITY detailsPane.less.accesskey "e">
<!ENTITY detailsPane.selectAnItemText.description "Select an item to view and edit its properties">

<!ENTITY view.label               "View">
<!ENTITY view.accesskey           "w">
<!ENTITY byDate.label             "By Date">
<!ENTITY byDate.accesskey         "D">
<!ENTITY bySite.label             "By Site">
<!ENTITY bySite.accesskey         "S">
<!ENTITY byMostVisited.label      "By Most Visited">
<!ENTITY byMostVisited.accesskey  "V">
<!ENTITY byLastVisited.label      "By Last Visited">
<!ENTITY byLastVisited.accesskey  "L">
<!ENTITY byDayAndSite.label       "By Date and Site">
<!ENTITY byDayAndSite.accesskey   "t">
PK
!<g��t��4chrome/en-US/locale/browser/places/places.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

load-js-data-url-error=For security reasons, javascript or data urls cannot be loaded from the history window or sidebar.
noTitle=(no title)

bookmarksMenuEmptyFolder=(Empty)

bookmarksBackupTitle=Bookmarks backup filename

bookmarksRestoreAlertTitle=Revert Bookmarks
bookmarksRestoreAlert=This will replace all of your current bookmarks with the backup. Are you sure?
bookmarksRestoreTitle=Select a bookmarks backup
bookmarksRestoreFilterName=JSON

bookmarksRestoreFormatError=Unsupported file type.
bookmarksRestoreParseError=Unable to process the backup file.

bookmarksLivemarkLoading=Live Bookmark loading…
bookmarksLivemarkFailed=Live Bookmark feed failed to load.

menuOpenLivemarkOrigin.label=Open “%S”

sortByName=Sort ‘%S’ by Name
sortByNameGeneric=Sort by Name
# LOCALIZATION NOTE (view.sortBy.1.name.label): sortBy properties are versioned.
# When any of these changes, all of the properties must be bumped, and the
# change must be annotated here.  Both label and accesskey must be updated.
# - version 1: changed view.sortBy.1.date.
view.sortBy.1.name.label=Sort by Name
view.sortBy.1.name.accesskey=N
view.sortBy.1.url.label=Sort by Location
view.sortBy.1.url.accesskey=L
view.sortBy.1.date.label=Sort by Most Recent Visit
view.sortBy.1.date.accesskey=V
view.sortBy.1.visitCount.label=Sort by Visit Count
view.sortBy.1.visitCount.accesskey=C
view.sortBy.1.description.label=Sort by Description
view.sortBy.1.description.accesskey=D
view.sortBy.1.dateAdded.label=Sort by Added
view.sortBy.1.dateAdded.accesskey=e
view.sortBy.1.lastModified.label=Sort by Last Modified
view.sortBy.1.lastModified.accesskey=M
view.sortBy.1.tags.label=Sort by Tags
view.sortBy.1.tags.accesskey=T

searchBookmarks=Search Bookmarks
searchHistory=Search History
searchDownloads=Search Downloads

SelectImport=Import Bookmarks File
EnterExport=Export Bookmarks File

detailsPane.noItems=No items
# LOCALIZATION NOTE (detailsPane.itemsCountLabel): Semicolon-separated list of plural forms.
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
# #1 number of items
# example: 111 items
detailsPane.itemsCountLabel=One item;#1 items

mostVisitedTitle=Most Visited
recentTagsTitle=Recent Tags

OrganizerQueryHistory=History
OrganizerQueryDownloads=Downloads
OrganizerQueryAllBookmarks=All Bookmarks
OrganizerQueryTags=Tags

# LOCALIZATION NOTE (tagResultLabel, bookmarkResultLabel, switchtabResultLabel,
# keywordResultLabel, searchengineResultLabel)
# Noun used to describe the location bar autocomplete result type
# to users with screen readers
# See createResultLabel() in urlbarBindings.xml
tagResultLabel=Tag
bookmarkResultLabel=Bookmark
switchtabResultLabel=Tab
keywordResultLabel=Keyword
searchengineResultLabel=Search

# LOCALIZATION NOTE (lockPrompt.text)
# %S will be replaced with the application name.
lockPrompt.title=Browser Startup Error
lockPrompt.text=The bookmarks and history system will not be functional because one of %S’s files is in use by another application. Some security software can cause this problem.
lockPromptInfoButton.label=Learn More
lockPromptInfoButton.accessKey=L

# LOCALIZATION NOTE (cmd.deleteSinglePage.accesskey,
# cmd.deleteMultiplePages.accesskey): these accesskeys can use the same
# character, since they're never displayed at the same time
cmd.deleteSinglePage.label=Delete Page
cmd.deleteSinglePage.accesskey=D
cmd.deleteMultiplePages.label=Delete Pages
cmd.deleteMultiplePages.accesskey=D

# LOCALIZATION NOTE (cmd.bookmarkSinglePage.accesskey,
# cmd.bookmarkMultiplePages.accesskey): these accesskeys can use the same
# character, since they're never displayed at the same time
cmd.bookmarkSinglePage.label=Bookmark Page
cmd.bookmarkSinglePage.accesskey=B
cmd.bookmarkMultiplePages.label=Bookmark Pages
cmd.bookmarkMultiplePages.accesskey=B
PK
!<��Z���4chrome/en-US/locale/browser/preferences/advanced.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!-- Note: each tab panel must contain unique accesskeys -->

<!ENTITY generalTab.label                "General">

<!ENTITY dataChoicesTab.label            "Data Choices">

<!-- LOCALIZATION NOTE (healthReportingDisabled.label): This message is displayed above
disabled data sharing options in developer builds or builds with no Telemetry support
available. -->
<!ENTITY healthReportingDisabled.label   "Data reporting is disabled for this build configuration">

<!ENTITY enableHealthReport2.label       "Allow &brandShortName; to send technical and interaction data to Mozilla">
<!ENTITY enableHealthReport2.accesskey   "r">
<!ENTITY healthReportLearnMore.label     "Learn more">

<!ENTITY dataCollection.label            "&brandShortName; Data Collection and Use">
<!ENTITY dataCollectionDesc.label        "We strive to provide you with choices and collect only what we need to provide and improve &brandShortName; for everyone. We always ask permission before receiving personal information.">
<!ENTITY dataCollectionPrivacyNotice.label    "Privacy Notice">

<!ENTITY collectBrowserErrors.label          "Allow &brandShortName; to send browser error reports (including error messages) to Mozilla">
<!ENTITY collectBrowserErrors.accesskey      "b">
<!ENTITY collectBrowserErrorsLearnMore.label "Learn more">

<!ENTITY sendBackloggedCrashReports.label  "Allow &brandShortName; to send backlogged crash reports on your behalf">
<!ENTITY sendBackloggedCrashReports.accesskey "c">
<!ENTITY crashReporterLearnMore.label    "Learn more">

<!ENTITY networkTab.label                "Network">

<!--  Site Data section manages sites using Storage API and is under Network -->
<!ENTITY siteData1.label                 "Cookies and Site Data">
<!ENTITY clearSiteData1.label            "Clear Data…">
<!ENTITY clearSiteData1.accesskey        "l">
<!ENTITY siteDataSettings1.label         "Manage Data…">
<!ENTITY siteDataSettings1.accesskey     "M">
<!ENTITY siteDataLearnMoreLink.label     "Learn more">

<!ENTITY updateTab.label                 "Update">

<!ENTITY certificateTab.label            "Certificates">
<!ENTITY certPersonal2.description       "When a server requests your personal certificate">
<!ENTITY selectCerts.auto                "Select one automatically">
<!ENTITY selectCerts.auto.accesskey      "S">
<!ENTITY selectCerts.ask                 "Ask you every time">
<!ENTITY selectCerts.ask.accesskey       "A">
<!ENTITY enableOCSP.label                "Query OCSP responder servers to confirm the current validity of certificates">
<!ENTITY enableOCSP.accesskey            "Q">
<!ENTITY viewCerts2.label                "View Certificates…">
<!ENTITY viewCerts2.accesskey            "C">
<!ENTITY viewSecurityDevices2.label      "Security Devices…">
<!ENTITY viewSecurityDevices2.accesskey  "D">
PK
!<6���>chrome/en-US/locale/browser/preferences/applicationManager.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!ENTITY appManager.title     "Application details">
<!ENTITY appManager.style     "width: 30em; min-height: 20em;">
<!ENTITY remove.label         "Remove">
<!ENTITY remove.accesskey     "R">
PK
!<��A�//Echrome/en-US/locale/browser/preferences/applicationManager.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE
# in descriptionApplications, %S will be replaced by one of the 3 following strings
descriptionApplications=The following applications can be used to handle %S.

handleProtocol=%S links
handleWebFeeds=Web Feeds
handleFile=%S content

descriptionWebApp=This web application is hosted at:
descriptionLocalApp=This application is located at:
PK
!<U��`OO6chrome/en-US/locale/browser/preferences/blocklists.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!ENTITY window.title                 "Block Lists">
<!ENTITY window.width                 "55em">

<!ENTITY treehead.list.label          "List">
<!ENTITY windowClose.key              "w">

<!ENTITY button.cancel.label          "Cancel">
<!ENTITY button.cancel.accesskey      "C">
<!ENTITY button.ok.label              "Save Changes">
<!ENTITY button.ok.accesskey          "S">
PK
!<��lqq9chrome/en-US/locale/browser/preferences/clearSiteData.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!ENTITY window.title                 "Clear Data">
<!ENTITY window.width                 "35em">

<!ENTITY window.description           "Clearing all cookies and site data stored by &brandShortName; may sign you out of websites and remove offline web content. Clearing cache data will not affect your logins.">
<!ENTITY windowClose.key              "w">

<!ENTITY clearSiteData.label          "Cookies and Site Data">
<!ENTITY clearSiteData.accesskey      "S">
<!ENTITY clearSiteData.description    "You may get signed out of websites if cleared">

<!ENTITY clearCache.label             "Cached Web Content">
<!ENTITY clearCache.accesskey         "W">
<!ENTITY clearCache.description       "Will require websites to reload images and data">

<!ENTITY button.cancel.label          "Cancel">
<!ENTITY button.cancel.accesskey      "C">
<!ENTITY button.clear.label           "Clear">
<!ENTITY button.clear.accesskey       "l">
PK
!<DO�)^^@chrome/en-US/locale/browser/preferences/clearSiteData.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE (clearSiteDataWithEstimates.label, clearCacheWithEstimates.label):
#   The parameters in parentheses in these strings describe disk usage
#   in the format (size unit), e.g. "Cookies and Site Data (24 KB)"
#   %1$S = size
#   %2$S = unit (MB, KB, etc.)
clearSiteDataWithEstimates.label = Cookies and Site Data (%1$S %2$S)
clearCacheWithEstimates.label = Cached Web Content (%1$S %2$S)

PK
!<���2chrome/en-US/locale/browser/preferences/colors.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!ENTITY  colorsDialog.title              "Colors">
<!ENTITY  window.width                    "38em">
<!ENTITY  window.macWidth                 "41em">

<!ENTITY  overrideDefaultPageColors2.label       "Override the colors specified by the page with your selections above">
<!ENTITY  overrideDefaultPageColors2.accesskey   "O">

<!ENTITY  overrideDefaultPageColors.always.label "Always">
<!ENTITY  overrideDefaultPageColors.auto.label   "Only with High Contrast themes">
<!ENTITY  overrideDefaultPageColors.never.label  "Never">

<!ENTITY  color                           "Text and Background">
<!ENTITY  textColor2.label                "Text">
<!ENTITY  textColor2.accesskey            "T">
<!ENTITY  backgroundColor2.label          "Background">
<!ENTITY  backgroundColor2.accesskey      "B">
<!ENTITY  useSystemColors.label           "Use system colors">
<!ENTITY  useSystemColors.accesskey       "s">

<!ENTITY  underlineLinks.label            "Underline links">
<!ENTITY  underlineLinks.accesskey        "U">
<!ENTITY  links                           "Link Colors">
<!ENTITY  linkColor2.label                "Unvisited Links">
<!ENTITY  linkColor2.accesskey            "L">
<!ENTITY  visitedLinkColor2.label         "Visited Links">
<!ENTITY  visitedLinkColor2.accesskey     "V">
PK
!<�r���
�
6chrome/en-US/locale/browser/preferences/connection.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->


<!ENTITY  connectionsDialog.title       "Connection Settings">
<!ENTITY  window.width2                 "49em">
<!ENTITY  window.macWidth2              "44em">

<!ENTITY  proxyTitle.label2             "Configure Proxy Access to the Internet">
<!ENTITY  noProxyTypeRadio.label        "No proxy">
<!ENTITY  noProxyTypeRadio.accesskey    "y">
<!ENTITY  systemTypeRadio.label         "Use system proxy settings">
<!ENTITY  systemTypeRadio.accesskey     "u">
<!ENTITY  WPADTypeRadio.label           "Auto-detect proxy settings for this network">
<!ENTITY  WPADTypeRadio.accesskey       "w">
<!ENTITY  manualTypeRadio2.label        "Manual proxy configuration">
<!ENTITY  manualTypeRadio2.accesskey    "m">
<!ENTITY  autoTypeRadio2.label          "Automatic proxy configuration URL">
<!ENTITY  autoTypeRadio2.accesskey      "A">
<!ENTITY  reload.label                  "Reload">
<!ENTITY  reload.accesskey              "e">
<!ENTITY  ftp2.label                    "FTP Proxy">
<!ENTITY  ftp2.accesskey                "F">
<!ENTITY  http2.label                   "HTTP Proxy">
<!ENTITY  http2.accesskey               "x">
<!ENTITY  ssl2.label                    "SSL Proxy">
<!ENTITY  ssl2.accesskey                "L">
<!ENTITY  socks2.label                  "SOCKS Host">
<!ENTITY  socks2.accesskey              "C">
<!ENTITY  socks4.label                  "SOCKS v4">
<!ENTITY  socks4.accesskey              "K">
<!ENTITY  socks5.label                  "SOCKS v5">
<!ENTITY  socks5.accesskey              "v">
<!ENTITY  socksRemoteDNS.label2         "Proxy DNS when using SOCKS v5">
<!ENTITY  socksRemoteDNS.accesskey      "d">
<!ENTITY  port2.label                   "Port">
<!ENTITY  HTTPport.accesskey            "P">
<!ENTITY  SSLport.accesskey             "o">
<!ENTITY  FTPport.accesskey             "r">
<!ENTITY  SOCKSport.accesskey           "t">
<!ENTITY  noproxy2.label                "No Proxy for">
<!ENTITY  noproxy2.accesskey            "n">
<!ENTITY  noproxyExplain.label          "Example: .mozilla.org, .net.nz, 192.168.1.0/24">
<!ENTITY  shareproxy.label              "Use this proxy server for all protocols">
<!ENTITY  shareproxy.accesskey          "s">
<!ENTITY  autologinproxy.label          "Do not prompt for authentication if password is saved">
<!ENTITY  autologinproxy.accesskey      "i">
<!ENTITY  autologinproxy.tooltip        "This option silently authenticates you to proxies when you have saved credentials for them. You will be prompted if authentication fails.">
PK
!<���e��6chrome/en-US/locale/browser/preferences/containers.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!ENTITY addButton.label      "Add New Container">
<!ENTITY addButton.accesskey  "A">
<!ENTITY preferencesButton.label "Preferences">
<!ENTITY removeButton.label   "Remove">
<!-- &#171; is &laquo; however it's not defined in XML -->
<!ENTITY backLink2.label      "&#171; Go Back">

<!ENTITY window.title         "Add New Container">
<!ENTITY window.width         "45em">

<!ENTITY name2.label          "Name">
<!ENTITY name2.accesskey      "N">
<!ENTITY name.placeholder     "Enter a container name">
<!ENTITY icon2.label          "Icon">
<!ENTITY icon2.accesskey      "I">
<!ENTITY color2.label         "Color">
<!ENTITY color2.accesskey     "o">
<!ENTITY windowClose.key      "w">

<!ENTITY button.ok.label      "Done">
<!ENTITY button.ok.accesskey  "D">
PK
!<:��XX=chrome/en-US/locale/browser/preferences/containers.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

containers.labelMinWidth = 4rem
containers.updateContainerTitle = %S Container Preferences

containers.blue.label = Blue
containers.turquoise.label = Turquoise
containers.green.label = Green
containers.yellow.label = Yellow
containers.orange.label = Orange
containers.red.label = Red
containers.pink.label = Pink
containers.purple.label = Purple

containers.fingerprint.label = Fingerprint
containers.briefcase.label = Briefcase
# LOCALIZATION NOTE (containers.dollar.label)
# String represents a money sign but currently uses a dollar sign so don't change to local currency
# See Bug 1291672
containers.dollar.label = Dollar sign
containers.cart.label = Shopping cart
containers.circle.label = Dot
containers.vacation.label = Vacation
containers.gift.label = Gift
containers.food.label = Food
containers.fruit.label = Fruit
containers.pet.label = Pet
containers.tree.label = Tree
containers.chill.label = Chill
PK
!<H�8g��3chrome/en-US/locale/browser/preferences/content.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!ENTITY  permissions.label           "Permissions">

<!ENTITY  blockPopups.label           "Block pop-up windows">
<!ENTITY  blockPopups.accesskey       "B">

<!ENTITY  popupExceptions.label                                 "Exceptions…">
<!ENTITY  popupExceptions.accesskey                             "E">

<!ENTITY  notificationPermissions.label                         "Notifications">
<!ENTITY  notificationSettingsButton.label                      "Settings…">
<!ENTITY  notificationSettingsButton.accesskey                  "t">
<!ENTITY  notificationPermissionsLearnMore.label                "Learn more">

<!ENTITY  locationPermissions.label                             "Location">
<!ENTITY  locationSettingsButton.label                          "Settings…">
<!ENTITY  locationSettingsButton.accesskey                      "t">

<!ENTITY  cameraPermissions.label                               "Camera">
<!ENTITY  cameraSettingsButton.label                            "Settings…">
<!ENTITY  cameraSettingsButton.accesskey                        "t">

<!ENTITY  microphonePermissions.label                           "Microphone">
<!ENTITY  microphoneSettingsButton.label                        "Settings…">
<!ENTITY  microphoneSettingsButton.accesskey                    "t">

<!-- LOCALIZATION NOTE (translation.options.attribution.beforeLogo,
  -                     translation.options.attribution.afterLogo):
  -  These 2 strings are displayed before and after a 'Microsoft Translator'
  -  logo.
  -  The translations for these strings should match the translations in
  -  browser/translation.dtd
  -->
<!ENTITY translation.options.attribution.beforeLogo "Translations by">
<!ENTITY translation.options.attribution.afterLogo "">
PK
!<b����3chrome/en-US/locale/browser/preferences/cookies.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!ENTITY window.width                       "36em">

<!ENTITY     cookiesonsystem2.label         "The following cookies are stored on your computer">
<!ENTITY     cookiename.label               "Cookie Name">
<!ENTITY     cookiedomain.label             "Site">

<!ENTITY     props.name.label               "Name:">
<!ENTITY     props.value.label              "Content:">
<!ENTITY     props.domain.label             "Host:">
<!ENTITY     props.path.label               "Path:">
<!ENTITY     props.secure.label             "Send For:">
<!ENTITY     props.expires.label            "Expires:">
<!ENTITY     props.container.label          "Container:">

<!ENTITY     window.title                   "Cookies">
<!ENTITY     windowClose.key                "w">
<!ENTITY     focusSearch1.key               "f">

<!ENTITY     searchFilter.label             "Search">
<!ENTITY     searchFilter.accesskey         "S">

<!ENTITY     button.close.label             "Close">
<!ENTITY     button.close.accesskey         "C">
PK
!<&ɿ�1chrome/en-US/locale/browser/preferences/fonts.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!ENTITY  fontsDialog.title                       "Fonts">

<!ENTITY  fonts.label                             "Fonts for">
<!ENTITY  fonts.accesskey                         "F">

<!ENTITY  size2.label                             "Size">
<!ENTITY  sizeProportional.accesskey              "z">
<!ENTITY  sizeMonospace.accesskey                 "e">

<!ENTITY  proportional2.label                     "Proportional">
<!ENTITY  proportional2.accesskey                 "P">

<!ENTITY  serif2.label                            "Serif">
<!ENTITY  serif2.accesskey                        "S">
<!ENTITY  sans-serif2.label                       "Sans-serif">
<!ENTITY  sans-serif2.accesskey                   "n">
<!ENTITY  monospace2.label                        "Monospace">
<!ENTITY  monospace2.accesskey                    "M">

<!-- LOCALIZATION NOTE (font.langGroup.latin) :
     Translate "Latin" as the name of Latin (Roman) script, not as the name of the Latin language. -->
<!ENTITY  font.langGroup.latin                    "Latin">
<!ENTITY  font.langGroup.japanese                 "Japanese">
<!ENTITY  font.langGroup.trad-chinese             "Traditional Chinese (Taiwan)">
<!ENTITY  font.langGroup.simpl-chinese            "Simplified Chinese">
<!ENTITY  font.langGroup.trad-chinese-hk          "Traditional Chinese (Hong Kong)">
<!ENTITY  font.langGroup.korean                   "Korean">
<!ENTITY  font.langGroup.cyrillic                 "Cyrillic">
<!ENTITY  font.langGroup.el                       "Greek">
<!ENTITY  font.langGroup.other                    "Other Writing Systems">
<!ENTITY  font.langGroup.thai                     "Thai">
<!ENTITY  font.langGroup.hebrew                   "Hebrew">
<!ENTITY  font.langGroup.arabic                   "Arabic">
<!ENTITY  font.langGroup.devanagari               "Devanagari">
<!ENTITY  font.langGroup.tamil                    "Tamil">
<!ENTITY  font.langGroup.armenian                 "Armenian">
<!ENTITY  font.langGroup.bengali                  "Bengali">
<!ENTITY  font.langGroup.canadian                 "Unified Canadian Syllabary">
<!ENTITY  font.langGroup.ethiopic                 "Ethiopic">
<!ENTITY  font.langGroup.georgian                 "Georgian">
<!ENTITY  font.langGroup.gujarati                 "Gujarati">
<!ENTITY  font.langGroup.gurmukhi                 "Gurmukhi">
<!ENTITY  font.langGroup.khmer                    "Khmer">
<!ENTITY  font.langGroup.malayalam                "Malayalam">
<!ENTITY  font.langGroup.math                     "Mathematics">
<!ENTITY  font.langGroup.odia                     "Odia">
<!ENTITY  font.langGroup.telugu                   "Telugu">
<!ENTITY  font.langGroup.kannada                  "Kannada">
<!ENTITY  font.langGroup.sinhala                  "Sinhala">
<!ENTITY  font.langGroup.tibetan                  "Tibetan">
<!-- Minimum font size -->
<!ENTITY minSize2.label                           "Minimum font size">
<!ENTITY minSize2.accesskey                       "o">
<!ENTITY minSize.none                             "None">

<!-- default font type -->
<!ENTITY  useDefaultFontSerif.label               "Serif">
<!ENTITY  useDefaultFontSansSerif.label           "Sans Serif">

<!ENTITY  allowPagesToUseOwn.label                "Allow pages to choose their own fonts, instead of your selections above">
<!ENTITY  allowPagesToUseOwn.accesskey            "A">

<!ENTITY languages.customize.Fallback2.grouplabel "Text Encoding for Legacy Content">
<!ENTITY languages.customize.Fallback3.label      "Fallback Text Encoding">
<!ENTITY languages.customize.Fallback3.accesskey  "T">
<!ENTITY languages.customize.Fallback2.desc       "This text encoding is used for legacy content that fails to declare its encoding.">

<!ENTITY languages.customize.Fallback.auto        "Default for Current Locale">
<!-- LOCALIZATION NOTE (languages.customize.Fallback.arabic):
     Translate "Arabic" as an adjective for an encoding, not as the name of the language. -->
<!ENTITY languages.customize.Fallback.arabic      "Arabic">
<!ENTITY languages.customize.Fallback.baltic      "Baltic">
<!ENTITY languages.customize.Fallback.ceiso       "Central European, ISO">
<!ENTITY languages.customize.Fallback.cewindows   "Central European, Microsoft">
<!-- LOCALIZATION NOTE (languages.customize.Fallback.simplified):
     Translate "Chinese" as an adjective for an encoding, not as the name of the language. -->
<!ENTITY languages.customize.Fallback.simplified  "Chinese, Simplified">
<!-- LOCALIZATION NOTE (languages.customize.Fallback.traditional):
     Translate "Chinese" as an adjective for an encoding, not as the name of the language. -->
<!ENTITY languages.customize.Fallback.traditional "Chinese, Traditional">
<!ENTITY languages.customize.Fallback.cyrillic    "Cyrillic">
<!-- LOCALIZATION NOTE (languages.customize.Fallback.greek):
     Translate "Greek" as an adjective for an encoding, not as the name of the language. -->
<!ENTITY languages.customize.Fallback.greek       "Greek">
<!-- LOCALIZATION NOTE (languages.customize.Fallback.hebrew):
     Translate "Hebrew" as an adjective for an encoding, not as the name of the language. -->
<!ENTITY languages.customize.Fallback.hebrew      "Hebrew">
<!-- LOCALIZATION NOTE (languages.customize.Fallback.japanese):
     Translate "Japanese" as an adjective for an encoding, not as the name of the language. -->
<!ENTITY languages.customize.Fallback.japanese    "Japanese">
<!-- LOCALIZATION NOTE (languages.customize.Fallback.korean):
     Translate "Korean" as an adjective for an encoding, not as the name of the language. -->
<!ENTITY languages.customize.Fallback.korean      "Korean">
<!-- LOCALIZATION NOTE (languages.customize.Fallback.thai):
     Translate "Thai" as an adjective for an encoding, not as the name of the language. -->
<!ENTITY languages.customize.Fallback.thai        "Thai">
<!-- LOCALIZATION NOTE (languages.customize.Fallback.turkish):
     Translate "Turkish" as an adjective for an encoding, not as the name of the language. -->
<!ENTITY languages.customize.Fallback.turkish     "Turkish">
<!-- LOCALIZATION NOTE (languages.customize.Fallback.vietnamese):
     Translate "Vietnamese" as an adjective for an encoding, not as the name of the language. -->
<!ENTITY languages.customize.Fallback.vietnamese  "Vietnamese">
<!ENTITY languages.customize.Fallback.other       "Other (incl. Western European)">
PK
!<�҉��5chrome/en-US/locale/browser/preferences/languages.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!ENTITY window.width                                   "30em">

<!ENTITY languages.customize.Header                     "Languages">
<!ENTITY languages.customize2.description               "Web pages are sometimes offered in more than one language. Choose languages for displaying these web pages, in order of preference">
<!ENTITY languages.customize.moveUp.label               "Move Up">
<!ENTITY languages.customize.moveUp.accesskey           "U">
<!ENTITY languages.customize.moveDown.label             "Move Down">
<!ENTITY languages.customize.moveDown.accesskey         "D">
<!ENTITY languages.customize.deleteButton.label         "Remove">
<!ENTITY languages.customize.deleteButton.accesskey     "R">
<!ENTITY languages.customize.selectLanguage.label       "Select a language to add…">
<!ENTITY languages.customize.addButton.label            "Add">
<!ENTITY languages.customize.addButton.accesskey        "A">
<!ENTITY languages.customize.spoofEnglish               "Request English versions of web pages for enhanced privacy">
PK
!<j�z880chrome/en-US/locale/browser/preferences/main.dtd<!ENTITY disableExtension.label    "Disable Extension">
PK
!<`�א		7chrome/en-US/locale/browser/preferences/permissions.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!ENTITY window.title                 "Exceptions">
<!ENTITY window.width                 "45em">

<!ENTITY treehead.sitename2.label     "Website">
<!ENTITY treehead.status.label        "Status">
<!ENTITY removepermission2.label      "Remove Website">
<!ENTITY removepermission2.accesskey  "R">
<!ENTITY removeallpermissions2.label  "Remove All Websites">
<!ENTITY removeallpermissions2.accesskey "e">
<!ENTITY address2.label               "Address of website">
<!ENTITY address2.accesskey           "d">
<!ENTITY block.label                  "Block">
<!ENTITY block.accesskey              "B">
<!ENTITY session.label                "Allow for Session">
<!ENTITY session.accesskey            "S">
<!ENTITY allow.label                  "Allow">
<!ENTITY allow.accesskey              "A">
<!ENTITY windowClose.key              "w">

<!ENTITY button.cancel.label          "Cancel">
<!ENTITY button.cancel.accesskey      "C">
<!ENTITY button.ok.label              "Save Changes">
<!ENTITY button.ok.accesskey          "S">

<!ENTITY searchbox.placeholder        "Search Website">
PK
!<f�\�]]7chrome/en-US/locale/browser/preferences/preferences.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!ENTITY  paneSearchResults.title       "Search Results">
<!ENTITY  paneSearch.title              "Search">
<!ENTITY  paneContainers.title          "Container Tabs">


<!ENTITY  browserPrivacy.label          "Browser Privacy">

<!-- LOCALIZATION NOTE (paneSync1.title): This should match syncBrand.fxAccount.label in ../syncBrand.dtd -->
<!ENTITY  paneSync1.title          "Firefox Account">
PK
!<��ڡi;i;>chrome/en-US/locale/browser/preferences/preferences.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

#### Security

# LOCALIZATION NOTE: phishBefore uses %S to represent the name of the provider
#                    whose privacy policy must be accepted (for enabling
#                    check-every-page-as-I-load-it phishing protection).
phishBeforeText=Selecting this option will send the address of web pages you are viewing to %S. To continue, please review and accept the following terms of service.

#### Fonts

labelDefaultFont=Default (%S)
labelDefaultFontUnnamed=Default

veryLargeMinimumFontTitle=Large minimum font size
veryLargeMinimumFontWarning=You have selected a very large minimum font size (more than 24 pixels). This may make it difficult or impossible to use some important configuration pages like this one.
acceptVeryLargeMinimumFont=Keep my changes anyway

#### Permissions Manager

trackingprotectionpermissionstext2=You have disabled Tracking Protection on these websites.
trackingprotectionpermissionstitle=Exceptions - Tracking Protection
cookiepermissionstext1=You can specify which websites are always or never allowed to use cookies and site data.  Type the exact address of the site you want to manage and then click Block, Allow for Session, or Allow.
cookiepermissionstitle1=Exceptions - Cookies and Site Data
addonspermissionstext=You can specify which websites are allowed to install add-ons. Type the exact address of the site you want to allow and then click Allow.
addons_permissions_title2=Allowed Websites - Add-ons Installation
popuppermissionstext=You can specify which websites are allowed to open pop-up windows. Type the exact address of the site you want to allow and then click Allow.
popuppermissionstitle2=Allowed Websites - Pop-ups
notificationspermissionstext6=The following websites have requested to send you notifications. You can specify which websites are allowed to send you notifications. You can also block new requests asking to allow notifications.
notificationspermissionstitle2=Settings - Notification Permissions
notificationspermissionsdisablelabel=Block new requests asking to allow notifications
notificationspermissionsdisabledescription=This will prevent any websites not listed above from requesting permission to send notifications. Blocking notifications may break some website features.
locationpermissionstext2=The following websites have requested to access your location. You can specify which websites are allowed to access your location. You can also block new requests asking to access your location.
locationpermissionstitle=Settings - Location Permissions
locationpermissionsdisablelabel=Block new requests asking to access your location
locationpermissionsdisabledescription=This will prevent any websites not listed above from requesting permission to access your location. Blocking access to your location may break some website features.
camerapermissionstext2=The following websites have requested to access your camera. You can specify which websites are allowed to access your camera. You can also block new requests asking to access your camera.
camerapermissionstitle=Settings - Camera Permissions
camerapermissionsdisablelabel=Block new requests asking to access your camera
camerapermissionsdisabledescription=This will prevent any websites not listed above from requesting permission to access your camera. Blocking access to your camera may break some website features.
microphonepermissionstext2=The following websites have requested to access your microphone. You can specify which websites are allowed to access your microphone. You can also block new requests asking to access your microphone.
microphonepermissionstitle=Settings - Microphone Permissions
microphonepermissionsdisablelabel=Block new requests asking to access your microphone
microphonepermissionsdisabledescription=This will prevent any websites not listed above from requesting permission to access your microphone. Blocking access to your microphone may break some website features.
invalidURI=Please enter a valid hostname
invalidURITitle=Invalid Hostname Entered
savedLoginsExceptions_title=Exceptions - Saved Logins
savedLoginsExceptions_desc3=Logins for the following websites will not be saved

# LOCALIZATION NOTE(pauseNotifications.label): %S is replaced with the
# brandShortName of the application.
pauseNotifications.label=Pause notifications until %S restarts
pauseNotifications.accesskey=n

#### Block List Manager

blockliststext=You can choose which list Firefox will use to block Web elements that may track your browsing activity.
blockliststitle=Block Lists
# LOCALIZATION NOTE (mozNameTemplate): This template constructs the name of the
# block list in the block lists dialog. It combines the list name and
# description.
#   e.g. mozNameTemplate : "Standard (Recommended). This list does a pretty good job."
#   %1$S = list name (fooName), %2$S = list descriptive text (fooDesc)
mozNameTemplate=%1$S %2$S
# LOCALIZATION NOTE (mozstdName, etc.): These labels appear in the tracking
# protection block lists dialog, mozNameTemplate is used to create the final
# string. Note that in the future these two strings (name, desc) could be
# displayed on two different lines.
mozstdName=Disconnect.me basic protection (Recommended).
mozstdDesc=Allows some trackers so websites function properly.
mozfullName=Disconnect.me strict protection.
mozfullDesc2=Blocks known trackers. Some websites may not function properly.

#### Master Password

pw_change2empty_in_fips_mode=You are currently in FIPS mode. FIPS requires a non-empty Master Password.
pw_change_failed_title=Password Change Failed

#### Fonts

# LOCALIZATION NOTE: Next two strings are for language name representations with
#   and without the region.
#   e.g. languageRegionCodeFormat : "French/Canada  [fr-ca]" languageCodeFormat : "French  [fr]"
#   %1$S = language name, %2$S = region name, %3$S = language-region code
languageRegionCodeFormat=%1$S/%2$S  [%3$S]
#   %1$S = language name, %2$S = language-region code
languageCodeFormat=%1$S  [%2$S]

#### Downloads

desktopFolderName=Desktop
downloadsFolderName=Downloads
chooseDownloadFolderTitle=Choose Download Folder:

#### Applications

fileEnding=%S file
saveFile=Save File

# LOCALIZATION NOTE (useApp, useDefault): %S = Application name
useApp=Use %S
useDefault=Use %S (default)

useOtherApp=Use other…
fpTitleChooseApp=Select Helper Application
manageApp=Application Details…
webFeed=Web Feed
videoPodcastFeed=Video Podcast
audioPodcastFeed=Podcast
alwaysAsk=Always ask
portableDocumentFormat=Portable Document Format (PDF)

# LOCALIZATION NOTE (usePluginIn):
# %1$S = plugin name (for example "QuickTime Plugin-in 7.2")
# %2$S = brandShortName from brand.properties (for example "Minefield")
usePluginIn=Use %S (in %S)

# LOCALIZATION NOTE (previewInApp, addLiveBookmarksInApp): %S = brandShortName
previewInApp=Preview in %S
addLiveBookmarksInApp=Add Live Bookmarks in %S

# LOCALIZATION NOTE (typeDescriptionWithType):
# %1$S = type description (for example "Portable Document Format")
# %2$S = type (for example "application/pdf")
typeDescriptionWithType=%S (%S)


#### Cookie Viewer

hostColon=Host:
domainColon=Domain:
forSecureOnly=Encrypted connections only
forAnyConnection=Any type of connection
expireAtEndOfSession=At end of session
can=Allow
canAccessFirstParty=Allow first party only
canSession=Allow for Session
cannot=Block
prompt=Always Ask
noCookieSelected=<no cookie selected>
cookiesAll=The following cookies are stored on your computer:
cookiesFiltered=The following cookies match your search:

# LOCALIZATION NOTE (removeAllCookies, removeAllShownCookies):
# removeAllCookies and removeAllShownCookies are both used on the same one button,
# never displayed together and can share the same accesskey.
# When only partial cookies are shown as a result of keyword search,
# removeAllShownCookies is displayed as button label.
# removeAllCookies is displayed when no keyword search and all cookies are shown.
removeAllCookies.label=Remove All
removeAllCookies.accesskey=A
removeAllShownCookies.label=Remove All Shown
removeAllShownCookies.accesskey=A

# LOCALIZATION NOTE (removeSelectedCookies):
# Semicolon-separated list of plural forms. See:
# http://developer.mozilla.org/en/docs/Localization_and_Plurals
# If you need to display the number of selected elements in your language,
# you can use #1 in your localization as a placeholder for the number.
# For example this is the English string with numbers:
# removeSelectedCookied=Remove #1 Selected;Remove #1 Selected
removeSelectedCookies.label=Remove Selected;Remove Selected
removeSelectedCookies.accesskey=R

defaultUserContextLabel=None

# LOCALIZATION NOTE (totalSiteDataSize2, siteUsage, siteUsagePersistent):
#   This is the total usage of site data, where we insert storage size and unit.
#   e.g., "The total usage is currently 200 MB"
#   %1$S = size
#   %2$S = unit (MB, KB, etc.)
totalSiteDataSize2=Your stored cookies, site data and cache are currently using %1$S %2$S of disk space.
siteUsage=%1$S %2$S
siteUsagePersistent=%1$S %2$S (Persistent)
loadingSiteDataSize1=Calculating site data and cache size…

acceptRemove=Remove
# LOCALIZATION NOTE (siteDataSettings3.description): %S = brandShortName
siteDataSettings3.description=The following websites store cookies and site data on your computer. %S keeps data from websites with persistent storage until you delete it, and deletes data from websites with non-persistent storage as space is needed.
# LOCALIZATION NOTE (removeAllSiteData, removeAllSiteDataShown):
# removeAllSiteData and removeAllSiteDataShown are both used on the same one button,
# never displayed together and can share the same accesskey.
# When only partial sites are shown as a result of keyword search,
# removeAllShown is displayed as button label.
# removeAll is displayed when no keyword search and all sites are shown.
removeAllSiteData.label=Remove All
removeAllSiteData.accesskey=e
removeAllSiteDataShown.label=Remove All Shown
removeAllSiteDataShown.accesskey=e
spaceAlert.learnMoreButton.label=Learn More
spaceAlert.learnMoreButton.accesskey=L
spaceAlert.over5GB.prefButton.label=Open Preferences
spaceAlert.over5GB.prefButton.accesskey=O
# LOCALIZATION NOTE (spaceAlert.over5GB.prefButtonWin.label): On Windows Preferences is called Options
spaceAlert.over5GB.prefButtonWin.label=Open Options
spaceAlert.over5GB.prefButtonWin.accesskey=O
# LOCALIZATION NOTE (spaceAlert.over5GB.message1): %S = brandShortName
spaceAlert.over5GB.message1=%S is running out of disk space. Website contents may not display properly. You can clear stored data in Preferences > Privacy & Security > Cookies and Site Data.
# LOCALIZATION NOTE (spaceAlert.over5GB.messageWin1):
# - On Windows Preferences is called Options
# - %S = brandShortName
spaceAlert.over5GB.messageWin1=%S is running out of disk space. Website contents may not display properly. You can clear stored data in Options > Privacy & Security > Cookies and Site Data.
spaceAlert.under5GB.okButton.label=OK, Got it
spaceAlert.under5GB.okButton.accesskey=K
# LOCALIZATION NOTE (spaceAlert.under5GB.message): %S = brandShortName
spaceAlert.under5GB.message=%S is running out of disk space. Website contents may not display properly. Visit “Learn More” to optimize your disk usage for better browsing experience.

removeContainerAlertTitle=Remove This Container?

# LOCALIZATION NOTE (removeContainerMsg): Semi-colon list of plural forms.
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
# #S is the number of container tabs
removeContainerMsg=If you remove this Container now, #S container tab will be closed. Are you sure you want to remove this Container?;If you remove this Container now, #S container tabs will be closed. Are you sure you want to remove this Container?

removeContainerOkButton=Remove this Container
removeContainerButton2=Don’t remove this Container

# Search Input
# LOCALIZATION NOTE: Please keep the placeholder string shorter than around 30 characters to avoid truncation.
searchInput.labelWin=Find in Options
searchInput.labelUnix=Find in Preferences

# Search Results Pane
# LOCALIZATION NOTE %S will be replaced by the word being searched
searchResults.sorryMessageWin=Sorry! There are no results in Options for “%S”.
searchResults.sorryMessageUnix=Sorry! There are no results in Preferences for “%S”.
# LOCALIZATION NOTE (searchResults.needHelp3): %S will be replaced with a link to the support page.
# The label of the link is in searchResults.needHelpSupportLink .
searchResults.needHelp3=Need help? Visit %S
# LOCALIZATION NOTE (searchResults.needHelpSupportLink): %S will be replaced with the browser name.
searchResults.needHelpSupportLink=%S Support

# LOCALIZATION NOTE (extensionControlled.homepage_override):
# This string is shown to notify the user that their home page is being controlled by an extension.
extensionControlled.homepage_override2 = An extension, %S, is controlling your home page.

# LOCALIZATION NOTE (extensionControlled.newTabURL):
# This string is shown to notify the user that their new tab page is being controlled by an extension.
extensionControlled.newTabURL2 = An extension, %S, is controlling your New Tab page.

# LOCALIZATION NOTE (extensionControlled.defaultSearch):
# This string is shown to notify the user that the default search engine is being controlled
# by an extension. %S is the icon and name of the extension.
extensionControlled.defaultSearch = An extension, %S, has set your default search engine.

# LOCALIZATION NOTE (extensionControlled.privacy.containers):
# This string is shown to notify the user that Container Tabs are being enabled by an extension
# %S is the container addon controlling it
extensionControlled.privacy.containers = An extension, %S, requires Container Tabs.

# LOCALIZATION NOTE (extensionControlled.websites.trackingProtectionMode):
# This string is shown to notify the user that their tracking protection preferences are being controlled by an extension.
extensionControlled.websites.trackingProtectionMode = An extension, %S, is controlling tracking protection.

# LOCALIZATION NOTE (extensionControlled.proxyConfig):
# This string is shown to notify the user that their proxy configuration preferences are being controlled by an extension.
# %1$S is the icon and name of the extension.
# %2$S is the brandShortName from brand.properties (for example "Nightly")
extensionControlled.proxyConfig = An extension, %1$S, is controlling how %2$S connects to the internet.

# LOCALIZATION NOTE (extensionControlled.enable):
# %1$S is replaced with the icon for the add-ons menu.
# %2$S is replaced with the icon for the toolbar menu.
# This string is shown to notify the user how to enable an extension that they disabled.
extensionControlled.enable = To enable the extension go to %1$S Add-ons in the %2$S menu.

# LOCALIZATION NOTE (connectionDesc.label):
# %S is the brandShortName from brand.properties (for example "Nightly")
connectionDesc.label = Configure how %S connects to the internet.
PK
!<	+�}}3chrome/en-US/locale/browser/preferences/privacy.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!ENTITY  trackingProtectionHeader2.label      "Tracking Protection">
<!ENTITY  trackingProtection3.description      "Tracking Protection blocks online trackers that collect your browsing data across multiple websites.">
<!ENTITY  trackingProtection3.radioGroupLabel  "Use Tracking Protection to block known trackers">
<!ENTITY  trackingProtectionAlways.label       "Always">
<!ENTITY  trackingProtectionAlways.accesskey   "y">
<!ENTITY  trackingProtectionPrivate.label      "Only in private windows">
<!ENTITY  trackingProtectionPrivate.accesskey  "l">
<!ENTITY  trackingProtectionNever.label        "Never">
<!ENTITY  trackingProtectionNever.accesskey    "n">
<!ENTITY  trackingProtectionLearnMore2.label    "Learn more about Tracking Protection and your privacy">
<!ENTITY  trackingProtectionExceptions.label   "Exceptions…">
<!ENTITY  trackingProtectionExceptions.accesskey "x">

<!-- LOCALIZATION NOTE (trackingProtectionPBM6.label): This string is displayed if privacy.trackingprotection.ui.enabled is set to false. This currently happens on the release and beta channel. -->
<!ENTITY trackingProtectionPBM6.label         "Use Tracking Protection in Private Browsing to block known trackers">
<!ENTITY trackingProtectionPBM6.accesskey     "v">
<!ENTITY trackingProtectionPBMLearnMore.label "Learn more">
<!ENTITY changeBlockList2.label               "Change Block List…">
<!ENTITY changeBlockList2.accesskey           "C">

<!ENTITY  history.label                 "History">
<!ENTITY  permissions.label             "Permissions">

<!ENTITY  addressBar.label              "Address Bar">
<!ENTITY  addressBar.suggest.label      "When using the address bar, suggest">
<!ENTITY  locbar.history2.label         "Browsing history">
<!ENTITY  locbar.history2.accesskey     "H">
<!ENTITY  locbar.bookmarks.label        "Bookmarks">
<!ENTITY  locbar.bookmarks.accesskey    "k">
<!ENTITY  locbar.openpage.label         "Open tabs">
<!ENTITY  locbar.openpage.accesskey     "O">
<!ENTITY  locbar.searches.label         "Related searches from the default search engine">
<!ENTITY  locbar.searches.accesskey     "d">

<!ENTITY  suggestionSettings2.label     "Change preferences for search engine suggestions">

<!ENTITY  acceptCookies4.label          "Accept cookies and site data from websites (recommended)">
<!ENTITY  acceptCookies4.accesskey      "A">

<!ENTITY  blockCookies.label            "Block cookies and site data (may cause websites to break)">
<!ENTITY  blockCookies.accesskey        "B">

<!ENTITY  acceptThirdParty3.pre.label     "Accept third-party cookies and site data">
<!ENTITY  acceptThirdParty3.pre.accesskey "y">
<!ENTITY  acceptThirdParty.always.label   "Always">
<!ENTITY  acceptThirdParty.never.label    "Never">
<!ENTITY  acceptThirdParty.visited.label  "From visited">

<!ENTITY  keepUntil2.label              "Keep until">
<!ENTITY  keepUntil2.accesskey          "u">

<!ENTITY  expire.label                  "they expire">
<!ENTITY  close.label                   "I close &brandShortName;">

<!ENTITY  cookieExceptions.label        "Exceptions…">
<!ENTITY  cookieExceptions.accesskey    "E">

<!ENTITY  historyHeader2.pre.label         "&brandShortName; will">
<!ENTITY  historyHeader2.pre.accesskey     "w">
<!ENTITY  historyHeader.remember.label     "Remember history">
<!ENTITY  historyHeader.dontremember.label "Never remember history">
<!ENTITY  historyHeader.custom.label       "Use custom settings for history">
<!ENTITY  historyHeader.post.label         "">

<!ENTITY  rememberDescription1.label      "&brandShortName; will remember your browsing, download, form and search history.">

<!ENTITY  dontrememberDescription.label  "&brandShortName; will use the same settings as private browsing, and will not remember any history as you browse the Web.">

<!ENTITY  clearHistoryButton.label       "Clear History…">
<!ENTITY  clearHistoryButton.accesskey   "s">

<!ENTITY  privateBrowsingPermanent2.label "Always use private browsing mode">
<!ENTITY  privateBrowsingPermanent2.accesskey "p">

<!ENTITY  rememberHistory2.label      "Remember my browsing and download history">
<!ENTITY  rememberHistory2.accesskey  "b">

<!ENTITY  rememberSearchForm.label       "Remember search and form history">
<!ENTITY  rememberSearchForm.accesskey   "f">

<!ENTITY  clearOnClose.label             "Clear history when &brandShortName; closes">
<!ENTITY  clearOnClose.accesskey         "r">

<!ENTITY  clearOnCloseSettings.label     "Settings…">
<!ENTITY  clearOnCloseSettings.accesskey "t">

<!ENTITY  a11yPrivacy.checkbox.label     "Prevent accessibility services from accessing your browser">
<!ENTITY  a11yPrivacy.checkbox.accesskey "a">
<!ENTITY  a11yPrivacy.learnmore.label    "Learn more">
<!ENTITY enableSafeBrowsingLearnMore.label "Learn more">
PK
!<L
�		2chrome/en-US/locale/browser/preferences/search.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!ENTITY searchBar.label                       "Search Bar">

<!ENTITY searchBar.hidden.label                "Use the address bar for search and navigation">
<!ENTITY searchBar.shown.label                 "Add search bar in toolbar">

<!-- LOCALIZATION NOTE (showSearchSuggestionsAboveHistory.label): This string
     describes what the user will observe when the system prioritizes search
     suggestions over browsing history in the results that extend down from the
     address bar. In the original English string, "ahead" refers to location
     (appearing most proximate to), not time (appearing before). -->
<!ENTITY showSearchSuggestionsAboveHistory.label "Show search suggestions ahead of browsing history in address bar results">

<!ENTITY defaultSearchEngine.label             "Default Search Engine">

<!ENTITY chooseYourDefaultSearchEngine2.label   "Choose the default search engine to use in the address bar and search bar.">

<!ENTITY provideSearchSuggestions.label        "Provide search suggestions">
<!ENTITY provideSearchSuggestions.accesskey    "s">

<!ENTITY showURLBarSuggestions2.label           "Show search suggestions in address bar results">
<!ENTITY showURLBarSuggestions2.accesskey       "l">
<!ENTITY urlBarSuggestionsPermanentPB.label    "Search suggestions will not be shown in location bar results because you have configured &brandShortName; to never remember history.">

<!ENTITY oneClickSearchEngines.label           "One-Click Search Engines">

<!ENTITY chooseWhichOneToDisplay2.label         "Choose the alternative search engines that appear below the address bar and search bar when you start to enter a keyword.">

<!ENTITY engineNameColumn.label                "Search Engine">
<!ENTITY engineKeywordColumn.label             "Keyword">

<!ENTITY restoreDefaultSearchEngines.label     "Restore Default Search Engines">
<!ENTITY restoreDefaultSearchEngines.accesskey "d">

<!ENTITY removeEngine.label                    "Remove">
<!ENTITY removeEngine.accesskey                "r">

<!ENTITY findMoreSearchEngines.label           "Find more search engines">
PK
!<���"oo4chrome/en-US/locale/browser/preferences/security.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!ENTITY  security.label                 "Security">

<!ENTITY  warnOnAddonInstall2.label       "Warn you when websites try to install add-ons">
<!ENTITY  warnOnAddonInstall2.accesskey   "W">

<!-- LOCALIZATION NOTE (enableSafeBrowsing.label, blockDownloads.label, blockUncommonUnwanted.label):
  It is important that wording follows the guidelines outlined on this page:
  https://developers.google.com/safe-browsing/developers_guide_v2#AcceptableUsage
-->

<!ENTITY  browsingProtection.label        "Deceptive Content and Dangerous Software Protection">
<!ENTITY  enableSafeBrowsing.label        "Block dangerous and deceptive content">
<!ENTITY  enableSafeBrowsing.accesskey    "B">

<!ENTITY  blockDownloads.label            "Block dangerous downloads">
<!ENTITY  blockDownloads.accesskey        "D">

<!ENTITY  blockUncommonAndUnwanted.label     "Warn you about unwanted and uncommon software">
<!ENTITY  blockUncommonAndUnwanted.accesskey "C">

<!ENTITY  addonExceptions.label         "Exceptions…">
<!ENTITY  addonExceptions.accesskey     "E">


<!ENTITY  formsAndPasswords.label       "Forms &amp; Passwords">

<!ENTITY  rememberLogins2.label         "Remember logins and passwords for websites">
<!ENTITY  rememberLogins2.accesskey     "R">
<!ENTITY  passwordExceptions.label      "Exceptions…">
<!ENTITY  passwordExceptions.accesskey  "x">

<!ENTITY  useMasterPassword.label        "Use a master password">
<!ENTITY  useMasterPassword.accesskey    "U">
<!ENTITY  changeMasterPassword.label     "Change Master Password…">
<!ENTITY  changeMasterPassword.accesskey "M">

<!ENTITY  savedLogins.label              "Saved Logins…">
<!ENTITY  savedLogins.accesskey          "L">
PK
!<b�b؟�:chrome/en-US/locale/browser/preferences/selectBookmark.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!ENTITY selectBookmark.title
  "Set Home Page">
<!ENTITY selectBookmark.label
  "Choose a Bookmark to be your Home Page. If you choose a folder, the Bookmarks in that folder will be opened in Tabs.">

PK
!<y�		<chrome/en-US/locale/browser/preferences/siteDataSettings.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!ENTITY     window2.title                 "Manage Cookies and Site Data">
<!ENTITY     hostCol.label                 "Site">
<!ENTITY     cookiesCol.label              "Cookies">
<!ENTITY     usageCol.label                "Storage">
<!ENTITY     lastAccessedCol.label                "Last Used">
<!ENTITY     searchTextboxPlaceHolder             "Search websites">
<!ENTITY     searchTextboxPlaceHolder.accesskey   "S">
<!ENTITY     removeSelected.label          "Remove Selected">
<!ENTITY     removeSelected.accesskey      "r">
<!ENTITY     save.label                    "Save Changes">
<!ENTITY     save.accesskey                "a">
<!ENTITY     cancel.label                  "Cancel">
<!ENTITY     cancel.accesskey              "C">
<!ENTITY     removingDialog1.title         "Removing Cookies and Site Data">
<!ENTITY     removingSelected1.description "Removing cookies and site data may log you out of websites. Are you sure you want to make the changes?">
<!ENTITY     siteTree3.label               "Cookies and site data for the following websites will be removed">
PK
!<�O��rr0chrome/en-US/locale/browser/preferences/sync.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!-- The page shown when logged in... -->

<!ENTITY engine.bookmarks.label     "Bookmarks">
<!ENTITY engine.bookmarks.accesskey "m">
<!ENTITY engine.tabs.label2         "Open tabs">
<!ENTITY engine.tabs.title          "A list of what’s open on all synced devices">
<!ENTITY engine.tabs.accesskey      "T">
<!ENTITY engine.history.label       "History">
<!ENTITY engine.history.accesskey   "r">
<!ENTITY engine.logins.label        "Logins">
<!ENTITY engine.logins.title        "Usernames and passwords you’ve saved">
<!ENTITY engine.logins.accesskey    "L">
<!-- On Windows we use the term "Options" to describe settings, but
     on Linux and Mac OS X we use "Preferences" - carry that distinction
     over into this string, used as the checkbox which indicates if prefs are synced
-->
<!ENTITY engine.prefsWin.label      "Options">
<!ENTITY engine.prefsWin.accesskey  "S">
<!ENTITY engine.prefs.label         "Preferences">
<!ENTITY engine.prefs.accesskey     "S">
<!ENTITY engine.prefs.title         "General, Privacy, and Security settings you’ve changed">
<!ENTITY engine.addons.label        "Add-ons">
<!ENTITY engine.addons.title        "Extensions and themes for Firefox desktop">
<!ENTITY engine.addons.accesskey    "A">
<!ENTITY engine.addresses.label     "Addresses">
<!ENTITY engine.addresses.title     "Postal addresses you’ve saved (desktop only)">
<!ENTITY engine.addresses.accesskey "e">
<!ENTITY engine.creditcards.label   "Credit cards">
<!ENTITY engine.creditcards.title   "Names, numbers and expiry dates (desktop only)">
<!ENTITY engine.creditcards.accesskey "C">

<!-- Device Settings -->
<!ENTITY fxaSyncDeviceName.label       "Device Name">
<!ENTITY changeSyncDeviceName2.label "Change Device Name…">
<!ENTITY changeSyncDeviceName2.accesskey "h">
<!ENTITY cancelChangeSyncDeviceName.label "Cancel">
<!ENTITY cancelChangeSyncDeviceName.accesskey "n">
<!ENTITY saveChangeSyncDeviceName.label "Save">
<!ENTITY saveChangeSyncDeviceName.accesskey "v">

<!-- Footer stuff -->
<!ENTITY prefs.tosLink.label        "Terms of Service">
<!ENTITY fxaPrivacyNotice.link.label "Privacy Notice">

<!-- LOCALIZATION NOTE (signedInUnverified.beforename.label,
signedInUnverified.aftername.label): these two string are used respectively
before and after the account email address. Localizers can use one of them, or
both, to better adapt this sentence to their language.
-->
<!ENTITY signedInUnverified.beforename.label "">
<!ENTITY signedInUnverified.aftername.label "is not verified.">

<!-- LOCALIZATION NOTE (signedInLoginFailure.beforename.label,
signedInLoginFailure.aftername.label): these two string are used respectively
before and after the account email address. Localizers can use one of them, or
both, to better adapt this sentence to their language.
-->
<!ENTITY signedInLoginFailure.beforename.label "Please sign in to reconnect">
<!ENTITY signedInLoginFailure.aftername.label "">

<!ENTITY notSignedIn.label            "You are not signed in.">
<!ENTITY signIn.label                 "Sign in">
<!ENTITY signIn.accesskey             "g">
<!ENTITY profilePicture.tooltip       "Change profile picture">
<!ENTITY verifiedManage.label         "Manage account">
<!ENTITY verifiedManage.accesskey     "o">
<!ENTITY disconnect3.label            "Disconnect…">
<!ENTITY disconnect3.accesskey        "D">
<!ENTITY resendVerification.label     "Resend Verification">
<!ENTITY resendVerification.accesskey "d">
<!ENTITY cancelSetup.label            "Cancel Setup">
<!ENTITY cancelSetup.accesskey        "p">

<!ENTITY signedOut.caption            "Take Your Web With You">
<!ENTITY signedOut.description        "Synchronize your bookmarks, history, tabs, passwords, add-ons, and preferences across all your devices.">
<!ENTITY signedOut.accountBox.title   "Connect with a &syncBrand.fxAccount.label;">
<!ENTITY signedOut.accountBox.create2 "Don’t have an account? Get started">
<!ENTITY signedOut.accountBox.create2.accesskey "C">
<!ENTITY signedOut.accountBox.signin2 "Sign In…">
<!ENTITY signedOut.accountBox.signin2.accesskey "I">

<!ENTITY signedIn.settings.label       "Sync Settings">
<!ENTITY signedIn.settings.description "Choose what to synchronize on your devices using &brandShortName;.">

<!-- LOCALIZATION NOTE (mobilePromo3.*): the following strings will be used to
     create a single sentence with active links.
     The resulting sentence in English is: "Download Firefox for
     Android or iOS to sync with your mobile device." -->

<!ENTITY mobilePromo3.start            "Download Firefox for ">
<!-- LOCALIZATION NOTE (mobilePromo3.androidLink): This is a link title that links to https://www.mozilla.org/firefox/android/ -->
<!ENTITY mobilePromo3.androidLink      "Android">

<!-- LOCALIZATION NOTE (mobilePromo3.iOSBefore): This is text displayed between mobilePromo3.androidLink and mobilePromo3.iosLink -->
<!ENTITY mobilePromo3.iOSBefore         " or ">
<!-- LOCALIZATION NOTE (mobilePromo3.iOSLink): This is a link title that links to https://www.mozilla.org/firefox/ios/ -->
<!ENTITY mobilePromo3.iOSLink          "iOS">

<!ENTITY mobilePromo3.end              " to sync with your mobile device.">

<!ENTITY mobilepromo.singledevice      "Connect another device">
<!ENTITY mobilepromo.multidevice       "Manage devices">
PK
!<�f���7chrome/en-US/locale/browser/preferences/translation.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!ENTITY window.title                     "Exceptions - Translation">
<!ENTITY window.width                     "36em">
<!ENTITY windowClose.key                  "w">

<!ENTITY noTranslationForLanguages2.label "Translation will not be offered for the following languages">
<!ENTITY treehead.languageName.label      "Languages">
<!ENTITY removeLanguage.label             "Remove Language">
<!ENTITY removeLanguage.accesskey         "R">
<!ENTITY removeAllLanguages.label         "Remove All Languages">
<!ENTITY removeAllLanguages.accesskey     "e">

<!ENTITY noTranslationForSites2.label     "Translation will not be offered for the following sites">
<!ENTITY treehead.siteName2.label         "Websites">
<!ENTITY removeSite.label                 "Remove Site">
<!ENTITY removeSite.accesskey             "S">
<!ENTITY removeAllSites.label             "Remove All Sites">
<!ENTITY removeAllSites.accesskey         "i">

<!ENTITY button.close.label               "Close">
<!ENTITY button.close.accesskey           "C">
PK
!<���NN1chrome/en-US/locale/browser/quitDialog.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

quitDialogTitle=Quit %S

quitTitle=&Quit
cancelTitle=&Cancel
saveTitle=&Save and Quit
neverAsk2=&Do not ask next time
message=Do you want %S to save your tabs and windows for the next time it starts?
messageNoWindows=Do you want %S to save your tabs for the next time it starts?
messagePrivate=You’re in private browsing mode. Quitting %S now will discard all your open tabs and windows.
PK
!<q@xW��(chrome/en-US/locale/browser/safeMode.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!ENTITY safeModeDialog.title         "&brandShortName; Safe Mode">
<!ENTITY window.maxWidth              "400">

<!ENTITY startSafeMode.label          "Start in Safe Mode">
<!ENTITY refreshProfile.label         "Refresh &brandShortName;">

<!ENTITY safeModeDescription3.label   "Safe Mode is a special mode of &brandShortName; that can be used to troubleshoot issues.">
<!ENTITY safeModeDescription4.label   "Your add-ons and custom settings will be temporarily disabled.">

<!ENTITY refreshProfileInstead.label  "You can also skip troubleshooting and try refreshing &brandShortName;.">

<!-- LOCALIZATION NOTE (autoSafeModeDescription3.label): Shown on the safe mode dialog after multiple startup crashes. See also chrome/global/resetProfile.dtd -->
<!ENTITY autoSafeModeDescription3.label "&brandShortName; closed unexpectedly while starting. This might be caused by add-ons or other problems. You can try to resolve the problem by troubleshooting in Safe Mode.">
PK
!<��q���Ochrome/en-US/locale/browser/safebrowsing/phishing-afterload-warning-message.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!ENTITY safeb.palm.accept.label2 "Go back">
<!ENTITY safeb.palm.seedetails.label "See details">

<!-- Localization note (safeb.palm.notdeceptive.label) - Label of the Help menu
  item. Either this or reportDeceptiveSiteMenu.label from report-phishing.dtd is
  shown. -->
<!ENTITY safeb.palm.notdeceptive.label "This isn’t a deceptive site…">
<!-- Localization note (safeb.palm.notdeceptive.accesskey) - Because
  safeb.palm.notdeceptive.label and reportDeceptiveSiteMenu.title from
  report-phishing.dtd are never shown at the same time, the same accesskey can
  be used for them. -->
<!ENTITY safeb.palm.notdeceptive.accesskey "d">

<!-- Localization note (safeb.palm.advisory.desc) - Please don't translate <a id="advisory_provider"/> tag.  It will be replaced at runtime with advisory link-->
<!ENTITY safeb.palm.advisory.desc2 "Advisory provided by <a id='advisory_provider'/>.">


<!ENTITY safeb.blocked.malwarePage.title2 "Visiting this website may harm your computer">
<!ENTITY safeb.blocked.malwarePage.shortDesc2 "&brandShortName; blocked this page because it might attempt to install malicious software that may steal or delete personal information on your computer.">

<!-- Localization note (safeb.blocked.malwarePage.errorDesc.override, safeb.blocked.malwarePage.errorDesc.noOverride, safeb.blocked.malwarePage.learnMore) - All <span> and <a> tags are replaced by the appropriate links and text during runtime. -->

<!ENTITY safeb.blocked.malwarePage.errorDesc.override "<span id='malware_sitename'/> has been <a id='error_desc_link'>reported as containing malicious software</a>. You can <a id='report_detection'>report a detection problem</a> or <a id='ignore_warning_link'>ignore the risk</a> and go to this unsafe site.">

<!ENTITY safeb.blocked.malwarePage.errorDesc.noOverride "<span id='malware_sitename'/> has been <a id='error_desc_link'>reported as containing malicious software</a>. You can <a id='report_detection'>report a detection problem</a>.">

<!ENTITY safeb.blocked.malwarePage.learnMore "Learn more about harmful web content including viruses and other malware and how to protect your computer at <a id='learn_more_link'>StopBadware.org</a>. Learn more about &brandShortName;’s Phishing and Malware Protection at <a id='firefox_support'>support.mozilla.org</a>.">


<!ENTITY safeb.blocked.unwantedPage.title2 "The site ahead may contain harmful programs">
<!ENTITY safeb.blocked.unwantedPage.shortDesc2 "&brandShortName; blocked this page because it might try to trick you into installing programs that harm your browsing experience (for example, by changing your homepage or showing extra ads on sites you visit).">

<!-- Localization note (safeb.blocked.unwantedPage.errorDesc.override, safeb.blocked.unwantedPage.errorDesc.noOverride, safeb.blocked.unwantedPage.learnMore) - All <span> and <a> tags are replaced by the appropriate links and text during runtime. -->

<!ENTITY safeb.blocked.unwantedPage.errorDesc.override "<span id='unwanted_sitename'/> has been <a id='error_desc_link'>reported as containing harmful software</a>. You can <a id='ignore_warning_link'>ignore the risk</a> and go to this unsafe site.">

<!ENTITY safeb.blocked.unwantedPage.errorDesc.noOverride "<span id='unwanted_sitename'/> has been <a id='error_desc_link'>reported as containing harmful software</a>.">

<!ENTITY safeb.blocked.unwantedPage.learnMore "Learn more about harmful and unwanted software at <a id='learn_more_link'>Unwanted Software Policy</a>. Learn more about &brandShortName;’s Phishing and Malware Protection at <a id='firefox_support'>support.mozilla.org</a>.">


<!ENTITY safeb.blocked.phishingPage.title3 "Deceptive site ahead">
<!ENTITY safeb.blocked.phishingPage.shortDesc3 "&brandShortName; blocked this page because it may trick you into doing something dangerous like installing software or revealing personal information like passwords or credit cards.">

<!-- Localization note (safeb.blocked.phishingPage.errorDesc.override, safeb.blocked.phishingPage.errorDesc.noOverride, safeb.blocked.phishingPage.learnMore) - All <span> and <a> tags are replaced by the appropriate links and text during runtime. -->
<!ENTITY safeb.blocked.phishingPage.errorDesc.override "<span id='phishing_sitename'/> has been <a id='error_desc_link'>reported as a deceptive site</a>. You can <a id='report_detection'>report a detection problem</a> or <a id='ignore_warning_link'>ignore the risk</a> and go to this unsafe site.">

<!ENTITY safeb.blocked.phishingPage.errorDesc.noOverride "<span id='phishing_sitename'/> has been <a id='error_desc_link'>reported as a deceptive site</a>. You can <a id='report_detection'>report a detection problem</a>.">

<!ENTITY safeb.blocked.phishingPage.learnMore "Learn more about deceptive sites and phishing at <a id='learn_more_link'>www.antiphishing.org</a>. Learn more about &brandShortName;’s Phishing and Malware Protection at <a id='firefox_support'>support.mozilla.org</a>.">


<!ENTITY safeb.blocked.harmfulPage.title "The site ahead may contain malware">
<!ENTITY safeb.blocked.harmfulPage.shortDesc2 "&brandShortName; blocked this page because it might try to install dangerous apps that steal or delete your information (for example, photos, passwords, messages and credit cards).">

<!-- Localization note (safeb.blocked.harmfulPage.errorDesc.override, safeb.blocked.harmfulPage.errorDesc.noOverride, safeb.blocked.harmfulPage.learnMore) - All <span> and <a> tags are replaced by the appropriate links and text during runtime. -->
<!ENTITY safeb.blocked.harmfulPage.errorDesc.override "<span id='harmful_sitename'/> has been <a id='error_desc_link'>reported as containing a potentially harmful application</a>. You can <a id='ignore_warning_link'>ignore the risk</a> and go to this unsafe site.">

<!ENTITY safeb.blocked.harmfulPage.errorDesc.noOverride "<span id='harmful_sitename'/> has been <a id='error_desc_link'>reported as containing a potentially harmful application</a>.">

<!ENTITY safeb.blocked.harmfulPage.learnMore "Learn more about &brandShortName;’s Phishing and Malware Protection at <a id='firefox_support'>support.mozilla.org</a>.">
PK
!<R�l�<chrome/en-US/locale/browser/safebrowsing/report-phishing.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!-- Localization note (reportDeceptiveSiteMenu.title) - Label of the Help menu
  item. Either this or safeb.palm.notdeceptive.label from
  phishing-afterload-warning-message.dtd is shown. -->
<!ENTITY reportDeceptiveSiteMenu.title      "Report Deceptive Site…">
<!-- Localization note (reportDeceptiveSiteMenu.accesskey) - Because
  safeb.palm.notdeceptive.label from phishing-afterload-warning-message.dtd and
  reportDeceptiveSiteMenu.title are never shown at the same time, the same
  accesskey can be used for them. -->
<!ENTITY reportDeceptiveSiteMenu.accesskey  "D">
PK
!<��8]]@chrome/en-US/locale/browser/safebrowsing/safebrowsing.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

errorReportFalseDeceptiveTitle=This isn’t a deceptive site
errorReportFalseDeceptiveMessage=It’s not possible to report this error at this time.
PK
!<-�o�!!(chrome/en-US/locale/browser/sanitize.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!ENTITY sanitizePrefs2.title          "Settings for Clearing History">
<!-- LOCALIZATION NOTE (sanitizePrefs2.modal.width): width of the Clear History on Shutdown dialog.
     Should be large enough to contain the item* strings on a single line.
     The column width should be set at half of the dialog width. -->
<!ENTITY sanitizePrefs2.modal.width    "34em">
<!ENTITY sanitizePrefs2.column.width   "17em">

<!ENTITY sanitizeDialog2.title         "Clear Recent History">
<!-- LOCALIZATION NOTE (sanitizeDialog2.width): width of the Clear Recent History dialog -->
<!ENTITY sanitizeDialog2.width         "34em">

<!ENTITY clearDataSettings3.label     "When I quit &brandShortName;, it should automatically clear all">

<!-- XXX rearrange entities to match physical layout when l10n isn't an issue -->
<!-- LOCALIZATION NOTE (clearTimeDuration.*): "Time range to clear" dropdown.
     See UI mockup at bug 480169 -->
<!ENTITY clearTimeDuration.label          "Time range to clear: ">
<!ENTITY clearTimeDuration.accesskey      "T">
<!ENTITY clearTimeDuration.lastHour       "Last Hour">
<!ENTITY clearTimeDuration.last2Hours     "Last Two Hours">
<!ENTITY clearTimeDuration.last4Hours     "Last Four Hours">
<!ENTITY clearTimeDuration.today          "Today">
<!ENTITY clearTimeDuration.everything     "Everything">
<!-- Localization note (clearTimeDuration.suffix) - trailing entity for languages
that require it.  -->
<!ENTITY clearTimeDuration.suffix         "">

<!-- LOCALIZATION NOTE (detailsProgressiveDisclosure.*): Labels and accesskeys
     of the "Details" progressive disclosure button.  See UI mockup at bug
     480169 -->
<!ENTITY detailsProgressiveDisclosure.label     "Details">
<!ENTITY detailsProgressiveDisclosure.accesskey "e">

<!ENTITY historySection.label         "History">
<!ENTITY dataSection.label            "Data">

<!ENTITY itemHistoryAndDownloads.label     "Browsing &amp; Download History">
<!ENTITY itemHistoryAndDownloads.accesskey "B">
<!ENTITY itemFormSearchHistory.label       "Form &amp; Search History">
<!ENTITY itemFormSearchHistory.accesskey   "F">
<!ENTITY itemCookies.label                 "Cookies">
<!ENTITY itemCookies.accesskey             "C">
<!ENTITY itemCache.label                   "Cache">
<!ENTITY itemCache.accesskey               "A">
<!ENTITY itemOfflineApps.label             "Offline Website Data">
<!ENTITY itemOfflineApps.accesskey         "O">
<!ENTITY itemActiveLogins.label            "Active Logins">
<!ENTITY itemActiveLogins.accesskey        "L">
<!ENTITY itemSitePreferences.label         "Site Preferences">
<!ENTITY itemSitePreferences.accesskey     "S">

<!-- LOCALIZATION NOTE (sanitizeEverythingUndoWarning): Second warning paragraph
     that appears when "Time range to clear" is set to "Everything".  See UI
     mockup at bug 480169 -->
<!ENTITY sanitizeEverythingUndoWarning     "This action cannot be undone.">
PK
!<��I���7chrome/en-US/locale/browser/searchplugins/amazon-au.xml<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
<ShortName>Amazon.com.au</ShortName>
<Description>Amazon.com.au Search</Description>
<InputEncoding>UTF-8</InputEncoding>
<Image width="16" height="16">resource://search-plugins/images/amazon.ico</Image>
<Url type="text/html" method="GET" template="https://www.amazon.com.au/exec/obidos/external-search/" rel="searchform">
  <Param name="field-keywords" value="{searchTerms}"/>
  <Param name="ie" value="{inputEncoding}"/>
  <Param name="mode" value="blended"/>
</Url>
</SearchPlugin>
PK
!<Ĵ�"��7chrome/en-US/locale/browser/searchplugins/amazon-ca.xml<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
<ShortName>Amazon.ca</ShortName>
<Description>Amazon.ca Search</Description>
<InputEncoding>UTF-8</InputEncoding>
<Image width="16" height="16">resource://search-plugins/images/amazon.ico</Image>
<Url type="text/html" method="GET" template="https://www.amazon.ca/exec/obidos/external-search/" rel="searchform">
  <Param name="field-keywords" value="{searchTerms}"/>
  <Param name="ie" value="{inputEncoding}"/>
  <Param name="mode" value="blended"/>
</Url>
</SearchPlugin>
PK
!<8��L��:chrome/en-US/locale/browser/searchplugins/amazondotcom.xml<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
<ShortName>Amazon.com</ShortName>
<Description>Amazon.com Search</Description>
<InputEncoding>UTF-8</InputEncoding>
<Image width="16" height="16">resource://search-plugins/images/amazon.ico</Image>
<Url type="application/x-suggestions+json" method="GET" template="https://completion.amazon.com/search/complete?q={searchTerms}&amp;search-alias=aps&amp;mkt=1"/>
<Url type="text/html" method="GET" template="https://www.amazon.com/exec/obidos/external-search/" rel="searchform">
  <Param name="field-keywords" value="{searchTerms}"/>
  <Param name="ie" value="{inputEncoding}"/>
  <Param name="mode" value="blended"/>
  <Param name="tag" value="mozilla-20"/>
  <Param name="sourceid" value="Mozilla-search"/>
</Url>
</SearchPlugin>
PK
!<�~�RR2chrome/en-US/locale/browser/searchplugins/bing.xml<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
    <ShortName>Bing</ShortName>
    <Description>Bing. Search by Microsoft.</Description>
    <InputEncoding>UTF-8</InputEncoding>
    <Image width="16" height="16">data:image/x-icon;base64,AAABAAIAICAAAAEACACoCAAAJgAAABAQAAABAAgAaAUAAM4IAAAoAAAAIAAAAEAAAAABAAgAAAAAAIAEAAAAAAAAAAAAAAABAAAAAAAAhIQMAI+PIwCXlzEAn59BAKioUwCurl8AtbVuAL6+fwDJyZMA0tKlANjYsgDe3r4A5eXMAOzs2QDy8uUA+fnzAP///wAAGi8AAC1QAAA/cAAAUZAAAGOwAAB2zwAAiPAAEZj/ADGm/wBRs/8AccH/AJHP/wCx3f8A0ev/AP///wAAAAAAACwvAABLUAAAaHAAAIaQAAClsAAAw88AAOHwABHv/wAx8f8AUfP/AHH1/wCR9/8Asfn/ANH7/wD///8AAAAAAAAvIQAAUDcAAHBMAACQYwAAsHkAAM+PAADwpgAR/7QAMf++AFH/yABx/9MAkf/cALH/5QDR//AA////AAAAAAAALw4AAFAYAABwIgAAkCwAALA2AADPQAAA8EoAEf9bADH/cQBR/4cAcf+dAJH/sgCx/8kA0f/fAP///wAAAAAAAi8AAARQAAAGcAAACJAAAAqwAAALzwAADvAAACD/EgA9/zEAW/9RAHn/cQCY/5EAtf+xANT/0QD///8AAAAAABQvAAAiUAAAMHAAAD2QAABMsAAAWc8AAGfwAAB4/xEAiv8xAJz/UQCu/3EAwP+RANL/sQDk/9EA////AAAAAAAmLwAAQFAAAFpwAAB0kAAAjrAAAKnPAADC8AAA0f8RANj/MQDe/1EA4/9xAOn/kQDv/7EA9v/RAP///wAAAAAALyYAAFBBAABwWwAAkHQAALCOAADPqQAA8MMAAP/SEQD/2DEA/91RAP/kcQD/6pEA//CxAP/20QD///8AAAAAAC8UAABQIgAAcDAAAJA+AACwTQAAz1sAAPBpAAD/eREA/4oxAP+dUQD/r3EA/8GRAP/SsQD/5dEA////AAAAAAAvAwAAUAQAAHAGAACQCQAAsAoAAM8MAADwDgAA/yASAP8+MQD/XFEA/3pxAP+XkQD/trEA/9TRAP///wAAAAAALwAOAFAAFwBwACEAkAArALAANgDPAEAA8ABJAP8RWgD/MXAA/1GGAP9xnAD/kbIA/7HIAP/R3wD///8AAAAAAC8AIABQADYAcABMAJAAYgCwAHgAzwCOAPAApAD/EbMA/zG+AP9RxwD/cdEA/5HcAP+x5QD/0fAA////AAAAAAAsAC8ASwBQAGkAcACHAJAApQCwAMQAzwDhAPAA8BH/APIx/wD0Uf8A9nH/APeR/wD5sf8A+9H/AP///wAAAAAAGwAvAC0AUAA/AHAAUgCQAGMAsAB2AM8AiADwAJkR/wCmMf8AtFH/AMJx/wDPkf8A3LH/AOvR/wD///8AAAAAAAgALwAOAFAAFQBwABsAkAAhALAAJgDPACwA8AA+Ef8AWDH/AHFR/wCMcf8AppH/AL+x/wDa0f8A////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBggCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAsQEA0HAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwkPEBAQEBALBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEMEBAQEBAQEBAPCQMAAAAAAAAAAAAAAAAAAAAAAAAAAQ0QEBAQEBAQEBAQDQgBAAAAAAAAAAAAAAAAAAAAAAABDRAQEBAQEBAQEBAQEA0GAQAAAAAAAAAAAAAAAAAAAAENEBAQDgcLEBAQEBAQEBALBAAAAAAAAAAAAAAAAAAAAQ0QEBANAQEHDRAQEBAQEBAOCAAAAAAAAAAAAAAAAAABDRAQEA4BAAACCA4QEBAQEBANAQAAAAAAAAAAAAAAAAENEBAQDgEAAAAABAsQEBAQEA0BAAAAAAAAAAAAAAAAAQ0QEBAOAQAAAAABBw4QEBAQDQEAAAAAAAAAAAAAAAABDRAQEA4BAAABBw0QEBAQEBANAQAAAAAAAAAAAAAAAAENEBAQDgEAAAcQEBAQEBAQEA0BAAAAAAAAAAAAAAAAAQ0QEBAOAQABCxAQEBAQEBANBwAAAAAAAAAAAAAAAAABDRAQEA4BAAQPEBAQEA0IBAEAAAAAAAAAAAAAAAAAAAENEBAQDgEACRAQDQkGAQAAAAAAAAAAAAAAAAAAAAAAAQ0QEBAOAQILCgYCAAAAAAAAAAAAAAAAAAAAAAAAAAABDRAQEA4BAQIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAENEBAQDgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQ0QEBAOAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABDRAQEA4BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAENEBAQDgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQ0QEBAOAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABDRAQEA0BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAENEA0IBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQcFAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAAAQAAAAIAAAAAEACAAAAAAAQAEAAAAAAAAAAAAAAAEAAAAAAACEhAAAjIwAAJKSFgCZmSgAqqpRALKyYgC+vnsAxsaKAM3NmQDU1KgA4ODBAOjozwDw8OEA+fnyAP///wD///8AAAAAAAAaLwAALVAAAD9wAABRkAAAY7AAAHbPAACI8AARmP8AMab/AFGz/wBxwf8Akc//ALHd/wDR6/8A////AAAAAAAALC8AAEtQAABocAAAhpAAAKWwAADDzwAA4fAAEe//ADHx/wBR8/8AcfX/AJH3/wCx+f8A0fv/AP///wAAAAAAAC8hAABQNwAAcEwAAJBjAACweQAAz48AAPCmABH/tAAx/74AUf/IAHH/0wCR/9wAsf/lANH/8AD///8AAAAAAAAvDgAAUBgAAHAiAACQLAAAsDYAAM9AAADwSgAR/1sAMf9xAFH/hwBx/50Akf+yALH/yQDR/98A////AAAAAAACLwAABFAAAAZwAAAIkAAACrAAAAvPAAAO8AAAIP8SAD3/MQBb/1EAef9xAJj/kQC1/7EA1P/RAP///wAAAAAAFC8AACJQAAAwcAAAPZAAAEywAABZzwAAZ/AAAHj/EQCK/zEAnP9RAK7/cQDA/5EA0v+xAOT/0QD///8AAAAAACYvAABAUAAAWnAAAHSQAACOsAAAqc8AAMLwAADR/xEA2P8xAN7/UQDj/3EA6f+RAO//sQD2/9EA////AAAAAAAvJgAAUEEAAHBbAACQdAAAsI4AAM+pAADwwwAA/9IRAP/YMQD/3VEA/+RxAP/qkQD/8LEA//bRAP///wAAAAAALxQAAFAiAABwMAAAkD4AALBNAADPWwAA8GkAAP95EQD/ijEA/51RAP+vcQD/wZEA/9KxAP/l0QD///8AAAAAAC8DAABQBAAAcAYAAJAJAACwCgAAzwwAAPAOAAD/IBIA/z4xAP9cUQD/enEA/5eRAP+2sQD/1NEA////AAAAAAAvAA4AUAAXAHAAIQCQACsAsAA2AM8AQADwAEkA/xFaAP8xcAD/UYYA/3GcAP+RsgD/scgA/9HfAP///wAAAAAALwAgAFAANgBwAEwAkABiALAAeADPAI4A8ACkAP8RswD/Mb4A/1HHAP9x0QD/kdwA/7HlAP/R8AD///8AAAAAACwALwBLAFAAaQBwAIcAkAClALAAxADPAOEA8ADwEf8A8jH/APRR/wD2cf8A95H/APmx/wD70f8A////AAAAAAAbAC8ALQBQAD8AcABSAJAAYwCwAHYAzwCIAPAAmRH/AKYx/wC0Uf8AwnH/AM+R/wDcsf8A69H/AP///wAAAAAACAAvAA4AUAAVAHAAGwCQACEAsAAmAM8ALADwAD4R/wBYMf8AcVH/AIxx/wCmkf8Av7H/ANrR/wD///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwMAAAAAAAAAAAAAAAACBgwMBwIAAAAAAAAAAAAABg4ODg4MBgIAAAAAAAAAAAYODQoODg4KBAAAAAAAAAAGDgwDBQsODg4FAAAAAAAABg4MAgADCg4OBgAAAAAAAAYODAICCg4ODgYAAAAAAAAGDgwCBg4OCwcCAAAAAAAABg4MAwkIBAIAAAAAAAAAAAYODAICAAAAAAAAAAAAAAAGDgwCAAAAAAAAAAAAAAAABg4MAgAAAAAAAAAAAAAAAAYMCAEAAAAAAAAAAAAAAAACAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</Image>
    <Url type="application/x-suggestions+json" template="https://www.bing.com/osjson.aspx">
        <Param name="query" value="{searchTerms}"/>
        <Param name="form" value="OSDJAS"/>
        <Param name="language" value="{moz:locale}"/>
    </Url>
    <Url type="text/html" method="GET" template="https://www.bing.com/search" rel="searchform">
        <Param name="q" value="{searchTerms}"/>
        <Param name="pc" value="MOZI"/>
        <MozParam name="form" condition="purpose" purpose="contextmenu" value="MOZCON"/>
        <MozParam name="form" condition="purpose" purpose="searchbar" value="MOZSBR"/>
        <MozParam name="form" condition="purpose" purpose="homepage" value="MOZSPG"/>
        <MozParam name="form" condition="purpose" purpose="keyword" value="MOZLBR"/>
        <MozParam name="form" condition="purpose" purpose="newtab" value="MOZTSB"/>
    </Url>
</SearchPlugin>
PK
!<�a��2!2!1chrome/en-US/locale/browser/searchplugins/ddg.xml<?xml version="1.0" encoding="utf-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/. -->

<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/"> 
  <ShortName>DuckDuckGo</ShortName> 
  <Description>Search DuckDuckGo</Description> 
  <InputEncoding>UTF-8</InputEncoding> 
  <Image height="16" width="16">data:image/icon;base64,AAABAAIAEBAAAAEAIABoBAAAJgAAACAgAAABACAAqBAAAI4EAAAoAAAAEAAAACAAAAABACAAAAAAAAAEAAATCwAAEwsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA11RgALs6oACbQ9wAj0v8AI9L/ACfQ9wAu0agANdUYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzzN4CNdL/oK/z//////////////////////+jsPv/BDXX/wAz0t4AAAAAAAAAAAAAAAAAAAAAAAAAAAAyzvNSduD//////8jK/v+P+Lf/IbQL/17RPP+J3Y//wOKX//////9YeuX/ADLO8wAAAAAAAAAAAAAAAAAw091piOX/8/X9/1Fx5P9xhu//WOWZ/0W9Lv9Lwjn/J8BB/xyDAP9bdfL/9fP//2mI5v8AMNPdAAAAAAc610YRQ9f//////0Zr4P8AGdD/sb32////////////wrv//wAh1/8MPab/ACPc/05r4///////EkPX/wc610YANtWkrr/y/6S48P8AJ9L/AB3R/+/w/v///////////3+D7f8AQeL/AYTw/wFr5/8AMNb/p7Tv/6698v8AM9WkADLW//////8yXt//AC3V/wAw1/////////////z///8A0P7/AKb1/wWI7P8AuPf/AJ3w/zZW3P//////ADHV/wAx2P//////AzrZ/wAu1/84ZOL////////////e////AND//wC1+f8Atff/AZbv/wY62f8ELNf//////wAw1/8AMtn//////wAw2f8ALNn/kKrz////+//cwbH////////////R////Rcb8/wDO/f8A/P//AHzo//////8AMNj/ADXa//////8vXuL/ACna/4yq9///79T/jUkg/9i+r///////r2Q0/7Cozv8BKdr/AirY/zdZ4P//////ADTa/wI72tOuv/T/prr0/wAl2v+JqPb//7yW/+bUxv/9+/n////u//W+n/+Op/L/ADPd/wAv2v+ru/T/r7/0/wI72tMLQd1DEEjg//////9Cbef/ADng///////////////////////R3///AC3g/wAy3v9SeOn//////xFI4P8LQd1DAAAAAAM64PNmiuz/9/j//2mN7f/m7P3///////////9Cb+n/ACXd/wAt3v9rju3//////2iL7P8DOuDzAAAAAAAAAAAAAAAAAT3g/0p16f//////3OT8/3OS7v8AKt3/ACPc/zhn5/+xw/b//////0956v8CPeD/AAAAAAAAAAAAAAAAAAAAAAAAAAAEPODzBUDh/5uz8//7/f7/////////////////prz0/wtF4v8FQeDzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtF5kYDQOOkADrj/wA44v8AOeP/ADzk/wVB46QPReZGAAAAAAAAAAAAAAAAAAAAAPAPAADgBwAAwAMAAIABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIABAADAAwAA4AcAAPAPAAAoAAAAIAAAAEAAAAABACAAAAAAAAAQAAATCwAAEwsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAChIzyAnRNFwJ0TQryND0d8nRNH/J0TR/ydE0f8nRNH/I0PR3ydE0K8nRNFwKEjPIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAChE00AlRdK/J0XS/ydF0v8nRdL/XXPd/11z3f94i+P/k6Lp/5Oi6f9rf+D/NVDV/ydF0v8nRdL/JUXSvyhE00AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBAzxAnRNOvJ0XT/ydF0/8lRdK/KEXSYOvu+6/+/v6//v7+v/39/c////////////7+/r/J0fOAKEXSYCVF0r8nRdP/J0XT/ydE068gQM8QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlRdUwJ0bT7ydG0/8nRtHPKETTQAAAAADHx8dA2vHhn5TYpN/o9+z/////////////////8PL83ydG0o8lRdUwAAAAAChE00AnRtHPJ0bT/ydG0+8lRdUwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKEXVYCdG1P8nRtT/KEbTgAAAAAAmRtZQI0PU38jIyP/F6s//Rrtk/0a7ZP9/yIr/c796/4vLkv+JpNf/M3Kq/zyWh/8zeKTfJkbWUAAAAAAoRtOAJ0bU/ydG1P8oRdVgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACVF1TAnR9X/J0fV/yhF1WAgQM8QJ0fTrydH1f9CW8//2tra/6Pdsv9Gu2T/Rrtk/0WzWv9Gu2T/Rrtk/0a7ZP9Gu2T/Rrtk/z6egP8nR9X/J0fTryBAzxAoRdVgJ0fV/ydH1f8lRdUwAAAAAAAAAAAAAAAAAAAAAAAAAAAgQM8QJ0fV7ydH1f8oSNVgIEDPECdH1c8nR9X/J0fV/1xwyf/t7e3/o92y/0a7ZP9Gu2T/Ra5U/0a7ZP9Gu2T/Rrtk/0a7ZP9Gu2T/Pp6A/ydH1f8nR9X/J0fVzyBAzxAoSNVgJ0fV/ydH1e8gQM8QAAAAAAAAAAAAAAAAAAAAACdH1q8nR9b/KEjVgCBQzxAnR9bPJ0fW/ydH1v8nR9b/gIzB//r6+v+j3bL/Rrtk/13Ed/+i26//ruG7/z6egf8+noH/Rrtk/0a7ZP86kI//J0fW/ydH1v8nR9b/J0fWzyBQzxAoSNWAJ0fW/ydH1q8AAAAAAAAAAAAAAAAoSNdAJkjW/yZH1s8AAAAAJEfWryZI1v8mSNb/JkjW/yZI1v+jqsT//////+j37P/R7tj////////////W3ff/JkjW/yZI1v8uZbr/PJeI/zJzrP8mSNb/JkjW/yZI1v8mSNb/JEfWrwAAAAAmR9bPJkjW/yhI10AAAAAAAAAAACVI1r8mSNf/KEjXQCZJ1lAmSNf/JkjX/yZI1/8mSNf/JkjX/9HR0f///////////////////////////5Ok6/8mSNf/JkjX/yZI1/8mSNf/JkjX/yZI1/8mSNf/JkjX/yZI1/8mSNf/JknWUChI10AmSNf/JUjWvwAAAAAoSNcgJknY/yZH2M8AAAAAI0nY3yZJ2P8mSdj/JknY/yZJ2P9KZM//39/f////////////////////////////XHfi/yZJ2P8mSdj/JknY/yZJ2P8mSdj/JknY/yZJ2P8mSdj/JknY/yZJ2P8jSdjfAAAAACZH2M8mSdj/KEjXICdJ2HAmSdj/JUjXYCVK2jAmSdj/JknY/yZJ2P8mSdj/JknY/2V4yf/t7e3///////////////////////////9cd+L/HXTj/xSf7/8Nwfj/CdL8/wnS/P8J0vz/ELDz/xt85v8mSdj/JknY/yZJ2P8lStowJUjXYCZJ2P8nSdhwJErZryZK2f8oSNcgJUnajyZK2f8mStn/JkrZ/yZK2f8mStn/iJPA////////////////////////////0ff+/xjV/P8J0vz/Drn1/xiO6/8Yjuv/GI7r/xCw8/8Lyvr/CdL8/xmF6P8mStn/JkrZ/yVJ2o8oSNcgJkrZ/yRK2a8jStrfI0rZ3wAAAAAlSdq/Jkra/yZK2v8mStr/Jkra/yZK2v+xtsf///////////////////////////8o2Pz/CdL8/wvK+v8mStr/Jkra/yZK2v8mStr/Jkra/yZK2v8iW97/Jkra/yZK2v8mStr/JUnavwAAAAAjStnfI0ra3yZK2v8lSdq/AAAAACZH2O8mStr/Jkra/yZK2v8mStr/L1HY/9HR0f///////////////////////////yjY/P8J0vz/CdL8/xCw9P8QsPT/ELD0/xSf7/8ddeX/Jkra/yZK2v8mStr/Jkra/yZK2v8mR9jvAAAAACVJ2r8mStr/Jkvb/yVJ2r8AAAAAJkvb/yZL2/8mS9v/Jkvb/yZL2/9KZtL/4+Pj////////////////////////////4Pn//0fd/f8J0vz/CdL8/wnS/P8J0vz/CdL8/wnS/P8Lyvr/Fpfu/yJc3/8mS9v/Jkvb/yZL2/8AAAAAJUnavyZL2/8mS9z/JUncvwAAAAAmS9z/Jkvc/yZL3P8mS9z/Jkvc/26AyP/x8fH//////////////////////////////////////9H3/v/C9P7/o+7+/2fa+/8Oufb/CdL8/wnS/P8J0vz/CdL8/xiP7P8mS9z/Jkvc/wAAAAAlSdy/Jkvc/yZM3P8lTNy/AAAAACZJ2e8mTNz/Jkzc/yZM3P8mTNz/iJTB////////////qnth/5VaOf/x6eX///////////////////////Hp5f/x6eX/ydL2/yZM3P8kVN7/G37o/xKo8v8QsfT/HXbm/yZM3P8mSdnvAAAAACVM3L8mTNz/I0vc3yZJ2u8AAAAAJUzevyZM3f8mTN3/Jkzd/yZM3f+fqc3///////////+VWjn/v5yI/+re1///////////////////////jk8s/7iRe//J0vb/Jkzd/yZM3f8mTN3/Jkzd/yZM3f8mTN3/Jkzd/yVM3r8AAAAAI0vc3yNL3N8kTd2vJk3d/yhQ3yAlTd2PJk3d/yZN3f8mTd3/Jk3d/6St0v////////////Hp5f/q3tf///////////////////////////+xhm7/49PK/6Cx8P8mTd3/Jk3d/yZN3f8mTd3/Jk3d/yZN3f8mTd3/JU3djyhQ3yAmTd3/JE3drydN33AmTd7/J03fcCVK3zAmTd7/Jk3e/yZN3v8mTd7/pK7S///////Sp5r/////////////////////////////////////////////////T27k/yZN3v8mTd7/Jk3e/yZN3v8mTd7/Jk3e/yZN3v8lSt8wJ03fcCZN3v8nTd9wKFDfICZO3/8mTt3PAAAAACVN3r8mTt//Jk7f/yZO3/+EltX//////+fRyv/SqaD/59LO///////////////////////at63/vIBy/7Glxf8mTt//Jk7f/yZO3/8mTt//Jk7f/yZO3/8mTt//JU3evwAAAAAmTt3PJk7f/yhQ3yAAAAAAJE/dryZO3/8oUN9AKFDfQCZO3/8mTt//Jk7f/zhb2v/o6/T/////////////////////////////////////////////////XHrn/yZO3/8mTt//Jk7f/yZO3/8mTt//Jk7f/yZO3/8oUN9AKFDfQCZO3/8kT92vAAAAAAAAAAAoUN9AJk7g/yZO4M8AAAAAJk/hnyZO4P8mTuD/Jk7g/05v5v/k6fv//////////////////////////////////////3eR7P8mTuD/Jk7g/yZO4P8mTuD/Jk7g/yZO4P8mTuD/Jk/hnwAAAAAmTuDPJk7g/yhQ30AAAAAAAAAAAAAAAAAjT+GfJU/h/yVO4Y8gUN8QIk7gzyVP4f8lT+H/SWnW/0lp1v+bq+H/8fHx/////////////////6Cy8v9OcOb/JU/h/yVP4f8lT+H/JU/h/yVP4f8lT+H/JU/h/yJO4M8gUN8QJU7hjyVP4f8jT+GfAAAAAAAAAAAAAAAAAAAAACBQ3xAlTOHvJU/h/yVQ4mAgUN8QIk7hzyVP4f+ktOv///////////////////////H0/f9phur/JU/h/yVP4f8lT+H/JU/h/yVP4f8lT+H/JU/h/yVP4f8iTuHPIFDfECVQ4mAlT+H/JUzh7yBQ3xAAAAAAAAAAAAAAAAAAAAAAAAAAACVQ3zAlUOLvJVDi/yVQ4mAgUN8QI1Din4mb2//J0/j/ydP4/6299P93ku3/M1vk/yVQ4v8lUOL/JVDi/yVQ4v8lUOL/JVDi/yVQ4v8lUOL/I1DinyBQ3xAlUOJgJVDi/yVQ4u8lUN8wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACVQ5DAlUOLvJVDi/yVQ4o8AAAAAJFDjQCVQ4r8lUOL/JVDi/yVQ4v8lUOL/JVDi/yVQ4v8lUOL/JVDi/yVQ4v8lUOL/JVDivyRQ40AAAAAAJVDijyVQ4v8lUOLvJVDkMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACVQ5DAjUeTfJVHj/yNR5N8kUONAAAAAACVQ5DAmUuOAJVHivyNR5N8lUeP/JVHj/yNR5N8lUeK/JlLjgCVQ5DAAAAAAJFDjQCNR5N8lUeP/I1Hk3yVQ5DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBQ3xAjUuSfJVHk/yVR5P8jUeTfJFLkcChQ5yAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoUOcgJFLkcCNR5N8lUeT/JVHk/yNS5J8gUN8QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkUONAI1LknyVS5P8lUuT/JVLk/yVS5O8lUeS/JVHkvyVR5L8lUeS/JVLk7yVS5P8lUuT/JVLk/yRS468kUONAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIFDfECVS5GAjUuWfIlPlzyVS5f8lUuX/JVLl/yVS5f8iU+XPI1LlnyVS5GAgUN8QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/AA///AAD//AAAP/ggBB/wgAEP4AAAB8AAAAPAAAADiAAAEYAAAAEQAAAIAAAAAAAAAAAgAAAEIAAABCAAAAQgAAAEIAAABCAAAAQAAAAAAAAAABAAAAiAAAABiAAAEcAAAAPAAAAD4AAAB/CAAQ/4IAQf/AfgP/8AAP//wAP/</Image>
  <Url type="text/html" method="get" template="https://duckduckgo.com/" rel="searchform">
    <Param name="q" value="{searchTerms}"/>
    <MozParam name="t" condition="purpose" purpose="contextmenu" value="ffcm"/>
    <MozParam name="t" condition="purpose" purpose="keyword"     value="ffab"/>
    <MozParam name="t" condition="purpose" purpose="searchbar"   value="ffsb"/>
    <MozParam name="t" condition="purpose" purpose="homepage"    value="ffhp"/>
    <MozParam name="t" condition="purpose" purpose="newtab"      value="ffnt"/>
  </Url>
  <Url type="application/x-suggestions+json" template="https://ac.duckduckgo.com/ac/">
    <Param name="q" value="{searchTerms}"/>
    <Param name="type" value="list"/>
  </Url>
</OpenSearchDescription> 
PK
!<��H\\5chrome/en-US/locale/browser/searchplugins/ebay-au.xml<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
<ShortName>eBay</ShortName>
<Description>eBay - Online auctions</Description>
<Image width="16" height="16">resource://search-plugins/images/ebay.ico</Image>
<Url type="text/html" method="GET" template="https://rover.ebay.com/rover/1/705-53470-19255-0/1" resultdomain="ebay.com">
  <Param name="ff3" value="4"/>
  <Param name="toolid" value="20004"/>
  <Param name="campid" value="5338192028"/>
  <Param name="customid" value=""/>
  <Param name="mpre" value="https://www.ebay.com.au/sch/{searchTerms}" />
</Url>
<SearchForm>https://www.ebay.com.au/</SearchForm>
</SearchPlugin>


PK
!<��1�ZZ5chrome/en-US/locale/browser/searchplugins/ebay-be.xml<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
<ShortName>eBay</ShortName>
<Description>eBay - Online auctions</Description>
<Image width="16" height="16">resource://search-plugins/images/ebay.ico</Image>
<Url type="text/html" method="GET" template="https://rover.ebay.com/rover/1/1553-53471-19255-0/1" resultdomain="ebay.com">
  <Param name="ff3" value="4"/>
  <Param name="toolid" value="20004"/>
  <Param name="campid" value="5338192028"/>
  <Param name="customid" value=""/>
  <Param name="mpre" value="https://www.befr.ebay.be/sch/{searchTerms}" />
</Url>
<SearchForm>https://www.ebay.at/</SearchForm>
</SearchPlugin>


PK
!<
��TT5chrome/en-US/locale/browser/searchplugins/ebay-ca.xml<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
<ShortName>eBay</ShortName>
<Description>eBay - Online auctions</Description>
<Image width="16" height="16">resource://search-plugins/images/ebay.ico</Image>
<Url type="text/html" method="GET" template="https://rover.ebay.com/rover/1/706-53473-19255-0/1" resultdomain="ebay.com">
  <Param name="ff3" value="4"/>
  <Param name="toolid" value="20004"/>
  <Param name="campid" value="5338192028"/>
  <Param name="customid" value=""/>
  <Param name="mpre" value="https://www.ebay.ca/sch/{searchTerms}" />
</Url>
<SearchForm>https://www.ebay.ca/</SearchForm>
</SearchPlugin>


PK
!<-�,�UU5chrome/en-US/locale/browser/searchplugins/ebay-ch.xml<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
<ShortName>eBay</ShortName>
<Description>eBay - Online auctions</Description>
<Image width="16" height="16">resource://search-plugins/images/ebay.ico</Image>
<Url type="text/html" method="GET" template="https://rover.ebay.com/rover/1/5222-53480-19255-0/1" resultdomain="ebay.com">
  <Param name="ff3" value="4"/>
  <Param name="toolid" value="20004"/>
  <Param name="campid" value="5338192028"/>
  <Param name="customid" value=""/>
  <Param name="mpre" value="https://www.ebay.ch/sch/{searchTerms}" />
</Url>
<SearchForm>https://www.ebay.ch/</SearchForm>
</SearchPlugin>


PK
!<<e5UU5chrome/en-US/locale/browser/searchplugins/ebay-ie.xml<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
<ShortName>eBay</ShortName>
<Description>eBay - Online auctions</Description>
<Image width="16" height="16">resource://search-plugins/images/ebay.ico</Image>
<Url type="text/html" method="GET" template="https://rover.ebay.com/rover/1/5282-53468-19255-0/1" resultdomain="ebay.com">
  <Param name="ff3" value="4"/>
  <Param name="toolid" value="20004"/>
  <Param name="campid" value="5338192028"/>
  <Param name="customid" value=""/>
  <Param name="mpre" value="https://www.ebay.ie/sch/{searchTerms}" />
</Url>
<SearchForm>https://www.ebay.ie/</SearchForm>
</SearchPlugin>


PK
!<�k�UU5chrome/en-US/locale/browser/searchplugins/ebay-nl.xml<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
<ShortName>eBay</ShortName>
<Description>eBay - Online auctions</Description>
<Image width="16" height="16">resource://search-plugins/images/ebay.ico</Image>
<Url type="text/html" method="GET" template="https://rover.ebay.com/rover/1/1346-53482-19255-0/1" resultdomain="ebay.com">
  <Param name="ff3" value="4"/>
  <Param name="toolid" value="20004"/>
  <Param name="campid" value="5338192028"/>
  <Param name="customid" value=""/>
  <Param name="mpre" value="https://www.ebay.nl/sch/{searchTerms}" />
</Url>
<SearchForm>https://www.ebay.nl/</SearchForm>
</SearchPlugin>


PK
!<�p_�ZZ5chrome/en-US/locale/browser/searchplugins/ebay-uk.xml<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
<ShortName>eBay</ShortName>
<Description>eBay - Online auctions</Description>
<Image width="16" height="16">resource://search-plugins/images/ebay.ico</Image>
<Url type="text/html" method="GET" template="https://rover.ebay.com/rover/1/710-53481-19255-0/1" resultdomain="ebay.com">
  <Param name="ff3" value="4"/>
  <Param name="toolid" value="20004"/>
  <Param name="campid" value="5338192028"/>
  <Param name="customid" value=""/>
  <Param name="mpre" value="https://www.ebay.co.uk/sch/{searchTerms}" />
</Url>
<SearchForm>https://www.ebay.co.uk/</SearchForm>
</SearchPlugin>


PK
!<��qTT2chrome/en-US/locale/browser/searchplugins/ebay.xml<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
<ShortName>eBay</ShortName>
<Description>eBay - Online auctions</Description>
<Image width="16" height="16">resource://search-plugins/images/ebay.ico</Image>
<Url type="text/html" method="GET" template="https://rover.ebay.com/rover/1/711-53200-19255-0/1" resultdomain="ebay.com">
  <Param name="ff3" value="4"/>
  <Param name="toolid" value="20004"/>
  <Param name="campid" value="5338192028"/>
  <Param name="customid" value=""/>
  <Param name="mpre" value="https://www.ebay.com/sch/{searchTerms}" />
</Url>
<SearchForm>https://www.ebay.com/</SearchForm>
</SearchPlugin>
PK
!<%i?1 1 9chrome/en-US/locale/browser/searchplugins/google-2018.xml<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
<ShortName>Google</ShortName>
<Description>Google Search</Description>
<InputEncoding>UTF-8</InputEncoding>
<Image width="16" height="16">data:image/x-icon;base64,AAABAAIAEBAAAAEAIABoBAAAJgAAACAgAAABACAAqBAAAI4EAAAoAAAAEAAAACAAAAABACAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///zD9/f2W/f392P39/fn9/f35/f391/39/ZT+/v4uAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v7+Cf39/Zn///////////////////////////////////////////39/ZX///8IAAAAAAAAAAAAAAAA/v7+Cf39/cH/////+v35/7TZp/92ul3/WKs6/1iqOv9yuFn/rNWd//j79v///////f39v////wgAAAAAAAAAAP39/Zn/////7PXp/3G3WP9TqDT/U6g0/1OoNP9TqDT/U6g0/1OoNP+Or1j//vDo///////9/f2VAAAAAP///zD/////+vz5/3G3V/9TqDT/WKo6/6LQkf/U6cz/1urO/6rUm/+Zo0r/8IZB//adZ////v7///////7+/i79/f2Y/////4nWzf9Lqkj/Vqo4/9Xqzv///////////////////////ebY//SHRv/0hUL//NjD///////9/f2U/f392v////8sxPH/Ebzt/43RsP/////////////////////////////////4roL/9IVC//i1jf///////f391/39/fr/////Cr37/wW8+/+16/7/////////////////9IVC//SFQv/0hUL/9IVC//SFQv/3pnX///////39/fn9/f36/////wu++/8FvPv/tuz+//////////////////SFQv/0hUL/9IVC//SFQv/0hUL/96p7///////9/f35/f392/////81yfz/CrL5/2uk9v///////////////////////////////////////////////////////f392P39/Zn/////ks/7/zdS7P84Rur/0NT6///////////////////////9/f////////////////////////39/Zb+/v4y//////n5/v9WYu3/NUPq/ztJ6/+VnPT/z9L6/9HU+v+WnfT/Ul7t/+Hj/P////////////////////8wAAAAAP39/Z3/////6Or9/1hj7v81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v9sdvD////////////9/f2YAAAAAAAAAAD///8K/f39w//////5+f7/paz2/11p7v88Suv/Okfq/1pm7v+iqfX/+fn+///////9/f3B/v7+CQAAAAAAAAAAAAAAAP///wr9/f2d///////////////////////////////////////////9/f2Z/v7+CQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7+/jL9/f2Z/f392/39/fr9/f36/f392v39/Zj///8wAAAAAAAAAAAAAAAAAAAAAPAPAADAAwAAgAEAAIABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIABAACAAQAAwAMAAPAPAAAoAAAAIAAAAEAAAAABACAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7+/g3+/v5X/f39mf39/cj9/f3q/f39+f39/fn9/f3q/f39yP39/Zn+/v5W////DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7+/iT9/f2c/f399f/////////////////////////////////////////////////////9/f31/f39mv7+/iMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7+/gn9/f2K/f39+////////////////////////////////////////////////////////////////////////////f39+v39/Yf///8IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+/v4k/f390v////////////////////////////////////////////////////////////////////////////////////////////////39/dD///8iAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////MP39/er//////////////////////////+r05v+v16H/gsBs/2WxSf9Wqjj/Vqk3/2OwRv99vWX/pdKV/97u2P////////////////////////////39/ej+/v4vAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7+/iT9/f3q/////////////////////+v15/+Pxnv/VKk2/1OoNP9TqDT/U6g0/1OoNP9TqDT/U6g0/1OoNP9TqDT/U6g0/36+Z//d7tf///////////////////////39/ej///8iAAAAAAAAAAAAAAAAAAAAAAAAAAD///8K/f390//////////////////////E4bn/XKw+/1OoNP9TqDT/U6g0/1OoNP9TqDT/U6g0/1OoNP9TqDT/U6g0/1OoNP9TqDT/U6g0/1apN/+x0pv///////////////////////39/dD///8IAAAAAAAAAAAAAAAAAAAAAP39/Yv/////////////////////sdij/1OoNP9TqDT/U6g0/1OoNP9TqDT/U6g0/1OoNP9TqDT/U6g0/1OoNP9TqDT/U6g0/1OoNP9TqDT/YKU1/8qOPv/5wZ////////////////////////39/YcAAAAAAAAAAAAAAAD+/v4l/f39+////////////////8Lgt/9TqDT/U6g0/1OoNP9TqDT/U6g0/1OoNP9utlT/n86N/7faqv+426v/pdKV/3u8ZP9UqDX/U6g0/3egN//jiUH/9IVC//SFQv/82MP//////////////////f39+v7+/iMAAAAAAAAAAP39/Z3////////////////q9Ob/W6w+/1OoNP9TqDT/U6g0/1OoNP9nskz/zOXC/////////////////////////////////+Dv2v+osWP/8YVC//SFQv/0hUL/9IVC//WQVP/++fb//////////////////f39mgAAAAD+/v4O/f399v///////////////4LHj/9TqDT/U6g0/1OoNP9TqDT/dblc//L58P/////////////////////////////////////////////8+v/3p3f/9IVC//SFQv/0hUL/9IVC//rIqf/////////////////9/f31////DP7+/ln////////////////f9v7/Cbz2/zOwhv9TqDT/U6g0/2KwRv/v9+z///////////////////////////////////////////////////////738//1kFT/9IVC//SFQv/0hUL/9plg///////////////////////+/v5W/f39nP///////////////4jf/f8FvPv/Bbz7/yG1s/9QqDz/vN2w//////////////////////////////////////////////////////////////////rHqP/0hUL/9IVC//SFQv/0hUL//vDn//////////////////39/Zn9/f3L////////////////R878/wW8+/8FvPv/Bbz7/y7C5P/7/fr//////////////////////////////////////////////////////////////////ere//SFQv/0hUL/9IVC//SFQv/718H//////////////////f39yP39/ez///////////////8cwvv/Bbz7/wW8+/8FvPv/WNL8///////////////////////////////////////0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//rIqv/////////////////9/f3q/f39+v///////////////we9+/8FvPv/Bbz7/wW8+/993P3///////////////////////////////////////SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/+cGf//////////////////39/fn9/f36////////////////B737/wW8+/8FvPv/Bbz7/33c/f//////////////////////////////////////9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/6xaX//////////////////f39+f39/e3///////////////8cwvv/Bbz7/wW8+/8FvPv/WdP8///////////////////////////////////////0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//vVv//////////////////9/f3q/f39y////////////////0bN/P8FvPv/Bbz7/wW8+/8hrvn/+/v///////////////////////////////////////////////////////////////////////////////////////////////////////////////////39/cj9/f2c////////////////ht/9/wW8+/8FvPv/FZP1/zRJ6/+zuPf//////////////////////////////////////////////////////////////////////////////////////////////////////////////////f39mf7+/lr////////////////d9v7/B7n7/yB38f81Q+r/NUPq/0hV7P/u8P3////////////////////////////////////////////////////////////////////////////////////////////////////////////+/v5X////D/39/ff///////////////9tkPT/NUPq/zVD6v81Q+r/NUPq/2Fs7//y8v7////////////////////////////////////////////09f7//////////////////////////////////////////////////f399f7+/g0AAAAA/f39n////////////////+Tm/P89Suv/NUPq/zVD6v81Q+r/NUPq/1Bc7f/IzPn/////////////////////////////////x8v5/0xY7P+MlPP////////////////////////////////////////////9/f2cAAAAAAAAAAD+/v4n/f39/P///////////////7W69/81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v9ZZe7/k5v0/6609/+vtff/lJv0/1pm7v81Q+r/NUPq/zVD6v+GjvL//v7//////////////////////////////f39+/7+/iQAAAAAAAAAAAAAAAD9/f2N/////////////////////6Cn9f81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v+BivL////////////////////////////9/f2KAAAAAAAAAAAAAAAAAAAAAP7+/gv9/f3V/////////////////////7W69/8+S+v/NUPq/zVD6v81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v81Q+r/P0zr/7q/+P///////////////////////f390v7+/gkAAAAAAAAAAAAAAAAAAAAAAAAAAP7+/ib9/f3r/////////////////////+Xn/P94gfH/NkTq/zVD6v81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v81Q+r/NkTq/3Z/8f/l5/z///////////////////////39/er+/v4kAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7+/jL9/f3r///////////////////////////k5vz/nqX1/2p08P9IVez/OEbq/zdF6v9GU+z/aHLv/5qh9f/i5Pz////////////////////////////9/f3q////MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7+/ib9/f3V/////////////////////////////////////////////////////////////////////////////////////////////////f390v7+/iQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wr9/f2N/f39/P///////////////////////////////////////////////////////////////////////////f39+/39/Yv+/v4JAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+/v4n/f39n/39/ff//////////////////////////////////////////////////////f399v39/Z3+/v4lAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v7+Dv7+/lr9/f2c/f39y/39/e39/f36/f39+v39/ez9/f3L/f39nP7+/ln+/v4OAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/AA///AAD//AAAP/gAAB/wAAAP4AAAB8AAAAPAAAADgAAAAYAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAABgAAAAcAAAAPAAAAD4AAAB/AAAA/4AAAf/AAAP/8AAP//wAP/</Image>
<Url type="application/x-suggestions+json" method="GET" template="https://www.google.com/complete/search?client=firefox&amp;q={searchTerms}"/>
<Url type="text/html" method="GET" template="https://www.google.com/search" rel="searchform">
  <Param name="q" value="{searchTerms}"/>
  <Param name="ie" value="utf-8"/>
  <Param name="oe" value="utf-8"/>
  <MozParam name="client" condition="purpose" purpose="keyword" value="firefox-b-1-ab"/>
  <MozParam name="client" condition="purpose" purpose="searchbar" value="firefox-b-1"/>
</Url>
</SearchPlugin>
PK
!<���ـ�<chrome/en-US/locale/browser/searchplugins/google-nocodes.xml<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
<ShortName>Google</ShortName>
<Description>Google Search</Description>
<InputEncoding>UTF-8</InputEncoding>
<Image width="16" height="16">data:image/x-icon;base64,AAABAAIAEBAAAAEAIABoBAAAJgAAACAgAAABACAAqBAAAI4EAAAoAAAAEAAAACAAAAABACAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///zD9/f2W/f392P39/fn9/f35/f391/39/ZT+/v4uAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v7+Cf39/Zn///////////////////////////////////////////39/ZX///8IAAAAAAAAAAAAAAAA/v7+Cf39/cH/////+v35/7TZp/92ul3/WKs6/1iqOv9yuFn/rNWd//j79v///////f39v////wgAAAAAAAAAAP39/Zn/////7PXp/3G3WP9TqDT/U6g0/1OoNP9TqDT/U6g0/1OoNP+Or1j//vDo///////9/f2VAAAAAP///zD/////+vz5/3G3V/9TqDT/WKo6/6LQkf/U6cz/1urO/6rUm/+Zo0r/8IZB//adZ////v7///////7+/i79/f2Y/////4nWzf9Lqkj/Vqo4/9Xqzv///////////////////////ebY//SHRv/0hUL//NjD///////9/f2U/f392v////8sxPH/Ebzt/43RsP/////////////////////////////////4roL/9IVC//i1jf///////f391/39/fr/////Cr37/wW8+/+16/7/////////////////9IVC//SFQv/0hUL/9IVC//SFQv/3pnX///////39/fn9/f36/////wu++/8FvPv/tuz+//////////////////SFQv/0hUL/9IVC//SFQv/0hUL/96p7///////9/f35/f392/////81yfz/CrL5/2uk9v///////////////////////////////////////////////////////f392P39/Zn/////ks/7/zdS7P84Rur/0NT6///////////////////////9/f////////////////////////39/Zb+/v4y//////n5/v9WYu3/NUPq/ztJ6/+VnPT/z9L6/9HU+v+WnfT/Ul7t/+Hj/P////////////////////8wAAAAAP39/Z3/////6Or9/1hj7v81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v9sdvD////////////9/f2YAAAAAAAAAAD///8K/f39w//////5+f7/paz2/11p7v88Suv/Okfq/1pm7v+iqfX/+fn+///////9/f3B/v7+CQAAAAAAAAAAAAAAAP///wr9/f2d///////////////////////////////////////////9/f2Z/v7+CQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7+/jL9/f2Z/f392/39/fr9/f36/f392v39/Zj///8wAAAAAAAAAAAAAAAAAAAAAPAPAADAAwAAgAEAAIABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIABAACAAQAAwAMAAPAPAAAoAAAAIAAAAEAAAAABACAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7+/g3+/v5X/f39mf39/cj9/f3q/f39+f39/fn9/f3q/f39yP39/Zn+/v5W////DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7+/iT9/f2c/f399f/////////////////////////////////////////////////////9/f31/f39mv7+/iMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7+/gn9/f2K/f39+////////////////////////////////////////////////////////////////////////////f39+v39/Yf///8IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+/v4k/f390v////////////////////////////////////////////////////////////////////////////////////////////////39/dD///8iAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////MP39/er//////////////////////////+r05v+v16H/gsBs/2WxSf9Wqjj/Vqk3/2OwRv99vWX/pdKV/97u2P////////////////////////////39/ej+/v4vAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7+/iT9/f3q/////////////////////+v15/+Pxnv/VKk2/1OoNP9TqDT/U6g0/1OoNP9TqDT/U6g0/1OoNP9TqDT/U6g0/36+Z//d7tf///////////////////////39/ej///8iAAAAAAAAAAAAAAAAAAAAAAAAAAD///8K/f390//////////////////////E4bn/XKw+/1OoNP9TqDT/U6g0/1OoNP9TqDT/U6g0/1OoNP9TqDT/U6g0/1OoNP9TqDT/U6g0/1apN/+x0pv///////////////////////39/dD///8IAAAAAAAAAAAAAAAAAAAAAP39/Yv/////////////////////sdij/1OoNP9TqDT/U6g0/1OoNP9TqDT/U6g0/1OoNP9TqDT/U6g0/1OoNP9TqDT/U6g0/1OoNP9TqDT/YKU1/8qOPv/5wZ////////////////////////39/YcAAAAAAAAAAAAAAAD+/v4l/f39+////////////////8Lgt/9TqDT/U6g0/1OoNP9TqDT/U6g0/1OoNP9utlT/n86N/7faqv+426v/pdKV/3u8ZP9UqDX/U6g0/3egN//jiUH/9IVC//SFQv/82MP//////////////////f39+v7+/iMAAAAAAAAAAP39/Z3////////////////q9Ob/W6w+/1OoNP9TqDT/U6g0/1OoNP9nskz/zOXC/////////////////////////////////+Dv2v+osWP/8YVC//SFQv/0hUL/9IVC//WQVP/++fb//////////////////f39mgAAAAD+/v4O/f399v///////////////4LHj/9TqDT/U6g0/1OoNP9TqDT/dblc//L58P/////////////////////////////////////////////8+v/3p3f/9IVC//SFQv/0hUL/9IVC//rIqf/////////////////9/f31////DP7+/ln////////////////f9v7/Cbz2/zOwhv9TqDT/U6g0/2KwRv/v9+z///////////////////////////////////////////////////////738//1kFT/9IVC//SFQv/0hUL/9plg///////////////////////+/v5W/f39nP///////////////4jf/f8FvPv/Bbz7/yG1s/9QqDz/vN2w//////////////////////////////////////////////////////////////////rHqP/0hUL/9IVC//SFQv/0hUL//vDn//////////////////39/Zn9/f3L////////////////R878/wW8+/8FvPv/Bbz7/y7C5P/7/fr//////////////////////////////////////////////////////////////////ere//SFQv/0hUL/9IVC//SFQv/718H//////////////////f39yP39/ez///////////////8cwvv/Bbz7/wW8+/8FvPv/WNL8///////////////////////////////////////0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//rIqv/////////////////9/f3q/f39+v///////////////we9+/8FvPv/Bbz7/wW8+/993P3///////////////////////////////////////SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/+cGf//////////////////39/fn9/f36////////////////B737/wW8+/8FvPv/Bbz7/33c/f//////////////////////////////////////9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/6xaX//////////////////f39+f39/e3///////////////8cwvv/Bbz7/wW8+/8FvPv/WdP8///////////////////////////////////////0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//vVv//////////////////9/f3q/f39y////////////////0bN/P8FvPv/Bbz7/wW8+/8hrvn/+/v///////////////////////////////////////////////////////////////////////////////////////////////////////////////////39/cj9/f2c////////////////ht/9/wW8+/8FvPv/FZP1/zRJ6/+zuPf//////////////////////////////////////////////////////////////////////////////////////////////////////////////////f39mf7+/lr////////////////d9v7/B7n7/yB38f81Q+r/NUPq/0hV7P/u8P3////////////////////////////////////////////////////////////////////////////////////////////////////////////+/v5X////D/39/ff///////////////9tkPT/NUPq/zVD6v81Q+r/NUPq/2Fs7//y8v7////////////////////////////////////////////09f7//////////////////////////////////////////////////f399f7+/g0AAAAA/f39n////////////////+Tm/P89Suv/NUPq/zVD6v81Q+r/NUPq/1Bc7f/IzPn/////////////////////////////////x8v5/0xY7P+MlPP////////////////////////////////////////////9/f2cAAAAAAAAAAD+/v4n/f39/P///////////////7W69/81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v9ZZe7/k5v0/6609/+vtff/lJv0/1pm7v81Q+r/NUPq/zVD6v+GjvL//v7//////////////////////////////f39+/7+/iQAAAAAAAAAAAAAAAD9/f2N/////////////////////6Cn9f81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v+BivL////////////////////////////9/f2KAAAAAAAAAAAAAAAAAAAAAP7+/gv9/f3V/////////////////////7W69/8+S+v/NUPq/zVD6v81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v81Q+r/P0zr/7q/+P///////////////////////f390v7+/gkAAAAAAAAAAAAAAAAAAAAAAAAAAP7+/ib9/f3r/////////////////////+Xn/P94gfH/NkTq/zVD6v81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v81Q+r/NkTq/3Z/8f/l5/z///////////////////////39/er+/v4kAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7+/jL9/f3r///////////////////////////k5vz/nqX1/2p08P9IVez/OEbq/zdF6v9GU+z/aHLv/5qh9f/i5Pz////////////////////////////9/f3q////MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7+/ib9/f3V/////////////////////////////////////////////////////////////////////////////////////////////////f390v7+/iQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wr9/f2N/f39/P///////////////////////////////////////////////////////////////////////////f39+/39/Yv+/v4JAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+/v4n/f39n/39/ff//////////////////////////////////////////////////////f399v39/Z3+/v4lAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v7+Dv7+/lr9/f2c/f39y/39/e39/f36/f39+v39/ez9/f3L/f39nP7+/ln+/v4OAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/AA///AAD//AAAP/gAAB/wAAAP4AAAB8AAAAPAAAADgAAAAYAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAABgAAAAcAAAAPAAAAD4AAAB/AAAA/4AAAf/AAAP/8AAP//wAP/</Image>
<Url type="application/x-suggestions+json" method="GET" template="https://www.google.com/complete/search?client=firefox&amp;q={searchTerms}"/>
<Url type="text/html" method="GET" template="https://www.google.com/search" rel="searchform">
  <Param name="q" value="{searchTerms}"/>
  <Param name="ie" value="utf-8"/>
  <Param name="oe" value="utf-8"/>
</Url>
</SearchPlugin>
PK
!<���- - 4chrome/en-US/locale/browser/searchplugins/google.xml<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
<ShortName>Google</ShortName>
<Description>Google Search</Description>
<InputEncoding>UTF-8</InputEncoding>
<Image width="16" height="16">data:image/x-icon;base64,AAABAAIAEBAAAAEAIABoBAAAJgAAACAgAAABACAAqBAAAI4EAAAoAAAAEAAAACAAAAABACAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///zD9/f2W/f392P39/fn9/f35/f391/39/ZT+/v4uAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v7+Cf39/Zn///////////////////////////////////////////39/ZX///8IAAAAAAAAAAAAAAAA/v7+Cf39/cH/////+v35/7TZp/92ul3/WKs6/1iqOv9yuFn/rNWd//j79v///////f39v////wgAAAAAAAAAAP39/Zn/////7PXp/3G3WP9TqDT/U6g0/1OoNP9TqDT/U6g0/1OoNP+Or1j//vDo///////9/f2VAAAAAP///zD/////+vz5/3G3V/9TqDT/WKo6/6LQkf/U6cz/1urO/6rUm/+Zo0r/8IZB//adZ////v7///////7+/i79/f2Y/////4nWzf9Lqkj/Vqo4/9Xqzv///////////////////////ebY//SHRv/0hUL//NjD///////9/f2U/f392v////8sxPH/Ebzt/43RsP/////////////////////////////////4roL/9IVC//i1jf///////f391/39/fr/////Cr37/wW8+/+16/7/////////////////9IVC//SFQv/0hUL/9IVC//SFQv/3pnX///////39/fn9/f36/////wu++/8FvPv/tuz+//////////////////SFQv/0hUL/9IVC//SFQv/0hUL/96p7///////9/f35/f392/////81yfz/CrL5/2uk9v///////////////////////////////////////////////////////f392P39/Zn/////ks/7/zdS7P84Rur/0NT6///////////////////////9/f////////////////////////39/Zb+/v4y//////n5/v9WYu3/NUPq/ztJ6/+VnPT/z9L6/9HU+v+WnfT/Ul7t/+Hj/P////////////////////8wAAAAAP39/Z3/////6Or9/1hj7v81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v9sdvD////////////9/f2YAAAAAAAAAAD///8K/f39w//////5+f7/paz2/11p7v88Suv/Okfq/1pm7v+iqfX/+fn+///////9/f3B/v7+CQAAAAAAAAAAAAAAAP///wr9/f2d///////////////////////////////////////////9/f2Z/v7+CQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7+/jL9/f2Z/f392/39/fr9/f36/f392v39/Zj///8wAAAAAAAAAAAAAAAAAAAAAPAPAADAAwAAgAEAAIABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIABAACAAQAAwAMAAPAPAAAoAAAAIAAAAEAAAAABACAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7+/g3+/v5X/f39mf39/cj9/f3q/f39+f39/fn9/f3q/f39yP39/Zn+/v5W////DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7+/iT9/f2c/f399f/////////////////////////////////////////////////////9/f31/f39mv7+/iMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7+/gn9/f2K/f39+////////////////////////////////////////////////////////////////////////////f39+v39/Yf///8IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+/v4k/f390v////////////////////////////////////////////////////////////////////////////////////////////////39/dD///8iAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////MP39/er//////////////////////////+r05v+v16H/gsBs/2WxSf9Wqjj/Vqk3/2OwRv99vWX/pdKV/97u2P////////////////////////////39/ej+/v4vAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7+/iT9/f3q/////////////////////+v15/+Pxnv/VKk2/1OoNP9TqDT/U6g0/1OoNP9TqDT/U6g0/1OoNP9TqDT/U6g0/36+Z//d7tf///////////////////////39/ej///8iAAAAAAAAAAAAAAAAAAAAAAAAAAD///8K/f390//////////////////////E4bn/XKw+/1OoNP9TqDT/U6g0/1OoNP9TqDT/U6g0/1OoNP9TqDT/U6g0/1OoNP9TqDT/U6g0/1apN/+x0pv///////////////////////39/dD///8IAAAAAAAAAAAAAAAAAAAAAP39/Yv/////////////////////sdij/1OoNP9TqDT/U6g0/1OoNP9TqDT/U6g0/1OoNP9TqDT/U6g0/1OoNP9TqDT/U6g0/1OoNP9TqDT/YKU1/8qOPv/5wZ////////////////////////39/YcAAAAAAAAAAAAAAAD+/v4l/f39+////////////////8Lgt/9TqDT/U6g0/1OoNP9TqDT/U6g0/1OoNP9utlT/n86N/7faqv+426v/pdKV/3u8ZP9UqDX/U6g0/3egN//jiUH/9IVC//SFQv/82MP//////////////////f39+v7+/iMAAAAAAAAAAP39/Z3////////////////q9Ob/W6w+/1OoNP9TqDT/U6g0/1OoNP9nskz/zOXC/////////////////////////////////+Dv2v+osWP/8YVC//SFQv/0hUL/9IVC//WQVP/++fb//////////////////f39mgAAAAD+/v4O/f399v///////////////4LHj/9TqDT/U6g0/1OoNP9TqDT/dblc//L58P/////////////////////////////////////////////8+v/3p3f/9IVC//SFQv/0hUL/9IVC//rIqf/////////////////9/f31////DP7+/ln////////////////f9v7/Cbz2/zOwhv9TqDT/U6g0/2KwRv/v9+z///////////////////////////////////////////////////////738//1kFT/9IVC//SFQv/0hUL/9plg///////////////////////+/v5W/f39nP///////////////4jf/f8FvPv/Bbz7/yG1s/9QqDz/vN2w//////////////////////////////////////////////////////////////////rHqP/0hUL/9IVC//SFQv/0hUL//vDn//////////////////39/Zn9/f3L////////////////R878/wW8+/8FvPv/Bbz7/y7C5P/7/fr//////////////////////////////////////////////////////////////////ere//SFQv/0hUL/9IVC//SFQv/718H//////////////////f39yP39/ez///////////////8cwvv/Bbz7/wW8+/8FvPv/WNL8///////////////////////////////////////0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//rIqv/////////////////9/f3q/f39+v///////////////we9+/8FvPv/Bbz7/wW8+/993P3///////////////////////////////////////SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/+cGf//////////////////39/fn9/f36////////////////B737/wW8+/8FvPv/Bbz7/33c/f//////////////////////////////////////9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/6xaX//////////////////f39+f39/e3///////////////8cwvv/Bbz7/wW8+/8FvPv/WdP8///////////////////////////////////////0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//vVv//////////////////9/f3q/f39y////////////////0bN/P8FvPv/Bbz7/wW8+/8hrvn/+/v///////////////////////////////////////////////////////////////////////////////////////////////////////////////////39/cj9/f2c////////////////ht/9/wW8+/8FvPv/FZP1/zRJ6/+zuPf//////////////////////////////////////////////////////////////////////////////////////////////////////////////////f39mf7+/lr////////////////d9v7/B7n7/yB38f81Q+r/NUPq/0hV7P/u8P3////////////////////////////////////////////////////////////////////////////////////////////////////////////+/v5X////D/39/ff///////////////9tkPT/NUPq/zVD6v81Q+r/NUPq/2Fs7//y8v7////////////////////////////////////////////09f7//////////////////////////////////////////////////f399f7+/g0AAAAA/f39n////////////////+Tm/P89Suv/NUPq/zVD6v81Q+r/NUPq/1Bc7f/IzPn/////////////////////////////////x8v5/0xY7P+MlPP////////////////////////////////////////////9/f2cAAAAAAAAAAD+/v4n/f39/P///////////////7W69/81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v9ZZe7/k5v0/6609/+vtff/lJv0/1pm7v81Q+r/NUPq/zVD6v+GjvL//v7//////////////////////////////f39+/7+/iQAAAAAAAAAAAAAAAD9/f2N/////////////////////6Cn9f81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v+BivL////////////////////////////9/f2KAAAAAAAAAAAAAAAAAAAAAP7+/gv9/f3V/////////////////////7W69/8+S+v/NUPq/zVD6v81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v81Q+r/P0zr/7q/+P///////////////////////f390v7+/gkAAAAAAAAAAAAAAAAAAAAAAAAAAP7+/ib9/f3r/////////////////////+Xn/P94gfH/NkTq/zVD6v81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v81Q+r/NkTq/3Z/8f/l5/z///////////////////////39/er+/v4kAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7+/jL9/f3r///////////////////////////k5vz/nqX1/2p08P9IVez/OEbq/zdF6v9GU+z/aHLv/5qh9f/i5Pz////////////////////////////9/f3q////MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7+/ib9/f3V/////////////////////////////////////////////////////////////////////////////////////////////////f390v7+/iQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wr9/f2N/f39/P///////////////////////////////////////////////////////////////////////////f39+/39/Yv+/v4JAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+/v4n/f39n/39/ff//////////////////////////////////////////////////////f399v39/Z3+/v4lAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v7+Dv7+/lr9/f2c/f39y/39/e39/f36/f39+v39/ez9/f3L/f39nP7+/ln+/v4OAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/AA///AAD//AAAP/gAAB/wAAAP4AAAB8AAAAPAAAADgAAAAYAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAABgAAAAcAAAAPAAAAD4AAAB/AAAA/4AAAf/AAAP/8AAP//wAP/</Image>
<Url type="application/x-suggestions+json" method="GET" template="https://www.google.com/complete/search?client=firefox&amp;q={searchTerms}"/>
<Url type="text/html" method="GET" template="https://www.google.com/search" rel="searchform">
  <Param name="q" value="{searchTerms}"/>
  <Param name="ie" value="utf-8"/>
  <Param name="oe" value="utf-8"/>
  <MozParam name="client" condition="purpose" purpose="keyword" value="firefox-b-ab"/>
  <MozParam name="client" condition="purpose" purpose="searchbar" value="firefox-b"/>
</Url>
</SearchPlugin>
PK
!<5()�66:chrome/en-US/locale/browser/searchplugins/images/yahoo.ico   �& h�( @ ��;�p5�o6�o6Dd0d0o6Do6�o4DŽ;��;�p3�o3�n3�q3�n3�r7�n5�o6up8Pn7)i2
m3n6*q8Po7tn5�n4�n3�q3�n3�n3�p3��;��;�q3�s4�s4�v5�q4�r4�q4�q4�q5�q5�q5�q4�p4�r5�p4�q4�q4�q5�q4�r5�q5�q5�q5�q4�q3�r4�u5�s4�r3�p3��;��;�t5�w5�v5�y6�u5�w6�x7�x7�w7�x8�y8�y9�z9�z9�v7�w9�y9�z9�v8�v7�v8�v7�v6�v6�v6�z7�x6�v5�t5�r3��;��;�u5�x6�{7�y6�y7�z8�|8�}9�;��<��<�~<�<��<�{:�|;�};�z:�{:�|:�z:�{:�{9�{9��:�{8�|8�w6�t5�t5��;��;�v5�z7�9�}8�}9�9��;��;��<��=��=��>��>��?��=��>��>��>��>��>��>��<��<��;��<��<�|8�~8�y6�t5��;��;�w5�9��;��;��;��;��<��>��>��?��@��A��A�զ����������֩���@��A��A��?��>��>��>��>��=��;��<�}8�v5��;��;�z7��:��<��<��>��?��?��A��A��B��C��C��D�ԟ����������Ӡ���D��C��B��C��C��B��B��?��>��>��<�}8�z7��;��;�9��<��?��>��?��@��A��B��C��D��E��F��F�Ԛ����������՝���G��F��G��F��E��D��D��B��A��A��>��<�z8��;��;�8��=��A��A��A��B��D��D��F��F��F��H��	H�Ў����������Б���
H��	H��H��G��G��F��E��D��B��A��A��<�z8��;��;��;��?��C��D��C��D��F��G��H��I��J��
J��
K�ь����������ь���
L��
J��
J��I��H��H��G��F��D��D��B��<�x7��;��;�z8��A��D��E��G��G��H��I��J��K��
L��
M��N�΀����������Ѕ���M��M��
L��
K��K��J��I��J��H��G��D��>��;��;��;��;��B��E��G��H��J��K��K��M��	N��
O��O��P��w�����������{���P��P��O��
P��	M��M��K��K��H��F��F��B��;��;��;��?��C��H��H��K��K��M��M��N��
P��
P��R��S��w�����������w���
S��S��R��
Q��
Q��N��M��M��J��J��F��B�~9��;��;��>��E��J��K��M��M��O��P��P��
Q��Q��S��
T��y�����������z���
U��
T��T��S��
Q��P��O��N��N��K��F��A��<��;��;��>��G��I��N��O��P��P��R��	S��
T��T��V��
W����������������X��
W��V��U��
T��	S��S��P��N��N��H��E��>��;��;��?��H��K��N��P��Q��Q��S��S��
T��U��V��>y������������������A}��
Y��V��V��
U��T��T��Q��P��N��G��D��>��;��;��@��J��N��R��T��U��U��V��U��
X��
X��Y����������������������Z��W��
W��
X��W��V��S��Q��N��J��F��@��;��;��@��K��O��T��V��W��W��X��Y��	Y��
Z��V���������������������Y���Y��
Y��	Y��Y��X��U��S��P��J��E��?��;��;��A��K��P��U��W��X��Y��Y��Z��Z��
[�����������`���]��������������
\��
\��Z��[��Z��W��T��Q��L��H��;��;��;��A��M��Q��X��W��W��Y��Z��Y��\��t��������������]��]������������x���[��\��\��[��Y��V��Q��L��G��:��;��;��B��N��S��X��Z��[��[��\��\��
^��������������8{��
\��
]��6z��������������
`��]��\��\��\��Z��U��P��E��;��;��;��B��N��S��Y��Z��Z��Z��[��[�鞿������������\��\��	\��\��������������\��]��\��\��Z��T��P��J��;��;��;��F��P��U��Z��Z��[��\��\��k��������������h��\��\��\��]��g��������������k��]��]��\��Y��T��N��G��;��;��;��C��O��T��Z��Z��Y��Z��Z������������昻��Z��Z��Z��[��[��[�哷�������������[��[��Z��Y��U��P��M��A��;��;��B��N��S��X��X��X��X��J������������W��V��U��U��V��V��V��W�����������N���X��Y��Y��T��O��K��@��;��;��?��J��N��T��S��R��Q���������j���Q��Q��Q��P��P��P��Q��Q��c������������S��V��T��O��K��B��=��;��;��?��J��J��M��P��P��K��K��K��K��K��J��K��K��J��K��J��J��I��K��K��K��K��L��M��L��J��D��B�:��;��;��;��?��B��L��J��D��D��D��D��D��C��B��A��?��E��D��C��B��A��D��C��A��D��D��K��K��B��D��>�{8��;��;�~8��;��?��@��;�9�{7��;��<�q4�p5�p3�o4�o4�=�;�}:�x8�s6��<��<��;��;��:��A��@��A��;�z7�z7��;��;�v5��:�9��:�n3�n4�|;�x9wu:Pn7,j2n4
n7,q8Pp6uo6��<��;߂;��:�z7�u5��;��;�r6�o6�o6Fp4p4s8Ft7�r6Ȅ;���������(  @m3�m3�n3�n3dq5t5t6t6t6t6t5q5n3cn3�m3�m3�o3�t5�u5�u5�t6�t5�t6�t6�t6�t6�t5�t6�u5�u5�t5�o3�q3�y6�}9�~9�~9�~:��H����������H�~:�~9�~9�~9�y6�q3�s4��9��;��=��>��A��	M����������M��A��>��=��;��9�s4�u5��;��A��C��D��F��F����������F��F��D��C��A��;�u5�w5�>��E��F��H��I��K����������K��I��H��F��E��>�w5�x6�B��H��K��M��	O��%Y����������V��	O��M��K��H��B�x6�y7ٛF��K��O��Q��	R��a�����������[���	R��Q��O��K��F�y7�y7ٟG��O��R��T��
V������������������
V��T��R��N��G�y7�{8�G��R��V��W�ׂ������ڊ���e������ׂ���W��V��Q��G�{8�~9�N��U��Z��[����������[��\����������[��Z��U��N�~9�9��K��W��]���������k���\��\��a������͉���\��W��J��9��9��K��W��_|����������Y��X��X��Z����������b}��V��K��9�9��F��P��������O��N��M��N��N��P���������O��E�9�x6��=��B��F��@��=��=��<��<��=��=��@��B��B��=�x6�m3�o4�p4�s5d�;�=�=�<�<�=�=�:s5cp4�o4�m3�PK
!<"+=��>chrome/en-US/locale/browser/searchplugins/images/yandex-en.ico   e& ��PNG


IHDR  szz�,IDATX���_L[u�?�- ���9@2���2d
{З�ahH���%.K&Bƒ	����D`Cd.�"����>�dFc��+��l�Xh�=>���Boi�;7��s����{T�送���^���B P��FK(�
7.//�H�����R�CUX�%^�w.��""�r�~J�b=pjHJ2��V}��p�p�+L�՚}����qM��*P������ϵb�R[��W����#���L��3�s'΢"�cc������RT,Xjj�1��Z����	dD*@�pI�{�9V}V����cC<����*��#�2��6��V���~`���P_q�đ�*��G^��8��>���uU&��❘��g��k4�0�f`/Cv6�Coh�x������%V<��=�2��܈y6��F����H�fֽ��n�~�x=r����e�8�ۦ����S��v��B�F"L�1�QҞ��'�{�bX�w{�����(#��4�N�����^��
��Q_O`�,tv��<*��:]�!*������*���\��=��ss��`l������C/�tf�ZYP!/��_�
�>��!��a�}���A���ڕ�uoG��)M��M@ �������dur
�ů�*��_{=�����7*Ĥ� X��5
|,|�1�o��3W�
���.���(�a�S_|�$�,v�*7PH�|�h��5?�\���gL-@Ԃ3��ԃ	 �a�۶Ņ�r�����W�.p'�B��
��#��L?v,.�����
s"l��h��pn�SAA��g~njj�
��`"�*�f�?w���K
�$(@b\'4-p��bO߼�^s�UB؇�5����9�3�a]����=�w:�WN�|�*������>��>f�k�
y�k��9j�ۯ�2�[�����TPƨ�""��]_��-p0�r������U�&6ycr�x׮f�_�ˑrZ��o��=���x�u=�˧�O��p��TP�L�H�_q�f���F��ҀT�V�c��^�sIyՄ�>jIEND�B`��PNG


IHDR�a�IDAT8����OSQ���F,��1i7Q�bl�11F7���C��cI�@BDCd��A#1QqR��R�Z[D���s��y�F�n��s�\8\�8�6��p�Z�V[�1T.������%��x�
�$�l6;�
nXke�՗DB��q��V�\N�QG�����w��0 a��5F���ڌ:j��+�Ugf��
���\&��"�1����֕�
�25Վ���
�����;��<�=0n�~K�k��ڊ�Q�~�|���i5K_��-���$0p��j*p�[E۱���*��ɛ�P�xw��\�\I��8��&9�KcTa��*�z kp]�E"�d#�S�w812Bw����g�Α	�A\�<}�Wzv/yާ�釯�����DO����e`����s��^,}L�Ro�Zp�9��^QWm���s ��-��?�<���ml|��p�'{/�V�j�h��e�^�6IEND�B`�PK
!<�����>chrome/en-US/locale/browser/searchplugins/images/yandex-ru.ico S&   yy�PNG


IHDR�aIDAT8���OHTQ���Z��q�H](iAH�� !
��#F�hQ�V�.HhS-�Aj1b�mm�#�EA� Z�`hQ�M:�f޻_����"��Y�s��sν���:6� M�Œ6�B��8%;@}2ٸD�2�.��V�M�Y�TC����A`gT䜵V�ZY�S���߿_v�G���T�)u�幮|�����
��A	�pf�1��]����4��1X#��ؑJm�f�C��P &��h�$�A\v����C�L&3���BS���	0 ���T�i����`w��z�0���w��r�`D0�`��$2�ӧ���P���.����_��}����]`������%�7�����}/:|< �Ԉ���G��aw�B/�k�rץ��ܥ���|,u��Y\\(g6�PZ�ܟ�=	�I��������x��
8/�q��c�.`�k���-�_*���<���H��
�C�𺺃�Yȍ��ѯ|���[Z]�|~nn8%ǁ��ֶPO-���?����nOl�g��IEND�B`��PNG


IHDR  szz�@IDATX���klTE�3wy��.�ZL|!�#D�DBDT|P!��j�	�4QlR���h��TSQ����Ĥ|P�`Д�ȣP���?��޻ݽ��z6'w3���̙s��(��&�����@kT������'�0(mhhh�v횑~�����@)�Ñ�X���e��\D$�H\��D�k`��1BN���7x�M�{��]*SM
��k���G�[��� �
X`�� ��"�����&�[k����3 ���L7��ݘcƍ��&�L�����<�|�ky*"�G��/@�Yd���ב� ��C/u�Ű�R=F���UUU��B�(�W+�5���n�ݝb���zH�E�#2�H����k���׸��ڵ���jmmm�y����[���z���Q�`A�{�΄�q*�;;C[��5:^YY9E�
P��?��E��	Ha12k�Z�1��!"X2����HNii�>״��A�p�T��Q0��E)�
�Y�������A((�ϫ�~c���s�=7�'�����c�*�b�ċ!Jg�-]Z���7��<���s�;��._�&�"!�Z��,����a���s��i/i��8j�8gm��<�OO+����蚶��d��9#�B�/C&O\:�ڵ-hG���,��B�`t|�Uspq�u=���_����/�Л����`\��`�m|n��O�,�y��|�&�XE�ؗ��~���7�_��=�!C���49p�
a	9���h@ǂh�;w!v�V����c���ǡ�xQ+Wa�l���}ރѕ0��`�>&Mq3��a�=�]�
�4�
T~Z��֖��j��q�Ss�"�\��<����=_����nR�[`�`�;�#�p��PWW�^�����'�s�۸
�ρ��
	��g|4y"��RVVvh�J�w����I��z�[n��_�S��
Z�a�|��� "=����1�l+h��>�q$��/<7
��-��jy�/�V�6�Y���޵ݑ�,�<���p�
[�-��5�}�}�P�ո}�3`�c�P"���\8--�:**�6��W�Q!p�"^�6��<;�)@!�U/cB6FMM����}�9a�w`/��@��2;�A@P�;@{�R
$dԩ�-���M�ir<P�4�o�i�,\��E�3С�Hk�1��6mzo���c.|�&�W��e��Zg��'`�.47��0qbp
�͉��j�ϝ�W���^j����Z����1�y˖F���(��d�����k�:=�8�{����z_�����E�/�����pJIEND�B`�PK
!<��]]5chrome/en-US/locale/browser/searchplugins/twitter.xml<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
<ShortName>Twitter</ShortName>
<Description>Realtime Twitter Search</Description>
<InputEncoding>UTF-8</InputEncoding>
<Image width="16" height="16">data:image/x-icon;base64,AAABAAIAEBAAAAAAAAALAgAAJgAAACAgAAAAAAAAQQQAADECAACJUE5HDQoaCgAAAA1JSERSAAAAEAAAABAIBgAAAB/z/2EAAAHSSURBVDjLfVO/axRREP6SS0ihlv4FQpoYWxHyHxgCsUxhmToBS4tF7NLZWaiIEHLv7d5dJNEQ0tgYEAmIjeAfYC57++7MGbzLjxu/Nxt27zZrBoadnZnvm3kz7wEqMpZqmdAfBBMIZBzGVGCkorbXq4kFp/yPtBgPZCJzZvYQuNZ+jKhVo75G2LyHVz9uoeFmc6bILaHaepv9v/w6iRcypXbonmNPBB9OBbv82tYfmPgn7NHTnKAaP8E+g8Ztot68k/nXDm8ooNEVhHEP1vXROBbUf/M/Wc0Jwl8zsEkHOxcMtE6Y+A42nodpLtLfR9QWRMmA9jm2eh78aXQY4eF9VvjCgKDWEewM2PIZQc4nD9Sf6hk+sohNqorzW0kN91BBdYKtO9dE/00JZITA50XxsxTHlWarqMYBh/O3UPGqrednx7ox3o+UhbIRT7O1BO9PfIsXI5V9J34WvrqN94buwFi+b3N0k0nrCjAFAp1+Nz1Czd3N2y8Tm6ywYk9JTHKqk/cD9cM17YW89WExbo6Ja1zhvgK3+4Itql+rt8PkO1f6oBysBN3brLrMVhs84wHJvpHoMwnf8CI9ygZd3nbJgymrMvxeSoLjmlCsIJf+a17lP6juZmUWkMzvAAAAAElFTkSuQmCCiVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAECElEQVRYw+1XS2gTURS9ia0/EMSCCC4siCIKrly4UVwIrgRR3KgbXQmCCiIiCha7EQRR6cIupHShSeZjf6lttdWFgqBRBPGDKIqF1mQ+1Wp/SZvnvXfmTWfMpJ3U0pUPHplJ3rv33HO/Afi/5nPViTjuKm8rYlFlAoSI8a500R1SGLYUZRH+Hvee6+qqygvxW1KJ1XLd+74aUuY+0OzjoHw/BElj4zQQHxv0HDBUvjR9WQlNYmnJhSjK9dxFUC0LOsYE3M8LSI8LUKw8AmoG5ecqPtP8vgZ04zKeO+jqjUtBDi2qWY+71xPaKKpnpF0uzUpCnxDQOixQ+BSo9iR/3vsh4CF+r5of8D3F75r9udQAKUwx++AJXkhZCe9QBkGEuUSC1swTjhK0VrUmEYzw7SKCyUPrLwEP8IxufQN9aCfKPwCJwdq/lCPluvUG2n4jhQW8bD6Hu+amABvSd/LOTbEENOMjdIyicLuA1om/ADhbMqIZn/DMOMq+7iqPBanUzWfQOUGUjUFnnj5H0WdnQOlfFoxssdix3tjGgnWbrCuGKvdA4LmuSQJ6s9SN0zFwg6lS7Qmms+WngK4pEvAVg+w83MltCAAhAHSGFMwEgGQRq2quwZe2cShJkaYvtey39hEHMfsQL7cjxd1F4dBnPWUKVeMwaLmTkRggGd1oiJI9GxLcLg2NA8s5DTXzCKePbjtBJAVoVoGjmFxEwsgiCi5tFuoDDFinHcb9xWg6CBcjpS9ReReCeF9GOL5TsCEYhRkSs26HmSLXh2R2f8DlJS7Q7BZOwyhWzeZ3P2hijtJRG9wSXuQkIj27m1OKKKPUiQIiCv1ptF4x33rUh/Yb+aVqN8BjIf07HwDy0EPFzU2/ck0rgCpln8MgtOcBgOOiFizRSWt7aQCWA0GHdesSBuOAm+fFOQIoOFXVeBRqaGnvlsGIed5LwWiO/LP11KASg7tmt97fnRoz1Xj5NXc4WQP0Sliw6d4EN6mUdStyew8cbB6uQWGdXL+pPHMrjQLCVe40tHdo1NJoQ47X5XBnfKUykduBQ0U9+jEbKR5IOTc0IwfJb+tnjvwoq+3DCmxEV1iwvzyHtlzbSTnVHMRKuTm6crK8AcelDrGc/a/0r4WUsRcF3ebWzC4YCqt8Rbdo5XmO4IZj9IGSW+PFUuSVyu7BeeABIrdZMNFIfiSrCQQFouwDNHxQLyBQdK6nSPOfgftUSXWtaOlD67BkXkCLXrFV1IYJBAUifcpnspQHFjyjmi+4y8nBs6KIL1cJuRZkt6K1xzCYrvFAqdtptDCNz3dxX8V91Gsufqvn8r/CByJemd/kvJiprui/RAQkMaZR/r1i4W6a0rP/N/gXi6OvmDvBLoiyBV1/AN29Cs9hVFoUAAAAAElFTkSuQmCC</Image>
<Url type="text/html" method="GET" template="https://twitter.com/search" rel="searchform">
  <Param name="q" value="{searchTerms}"/>
  <Param name="partner" value="Firefox"/>
  <Param name="source" value="desktop-search"/>
</Url>
</SearchPlugin>
PK
!<��
e��7chrome/en-US/locale/browser/searchplugins/wikipedia.xml<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
<ShortName>Wikipedia (en)</ShortName>
<Description>Wikipedia, the Free Encyclopedia</Description>
<InputEncoding>UTF-8</InputEncoding>
<Image width="16" height="16">resource://search-plugins/images/wikipedia.ico</Image>
<Url type="application/x-suggestions+json" method="GET" template="https://en.wikipedia.org/w/api.php">
  <Param name="action" value="opensearch"/>
  <Param name="search" value="{searchTerms}"/>
</Url>
<Url type="text/html" method="GET" template="https://en.wikipedia.org/wiki/Special:Search"
     resultdomain="wikipedia.org" rel="searchform">
  <Param name="search" value="{searchTerms}"/>
  <Param name="sourceid" value="Mozilla-search"/>
</Url>
</SearchPlugin>
PK
!<|iH���7chrome/en-US/locale/browser/searchplugins/yandex-en.xml<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
<ShortName>Yandex</ShortName>
<Description>Use Yandex to search the Internet.</Description>
<InputEncoding>UTF-8</InputEncoding>
<Image width="16" height="16">resource://search-plugins/images/yandex-en.ico</Image>
<Url type="application/x-suggestions+json" method="GET" template="https://suggest.yandex.com/suggest-ff.cgi">
  <Param name="part" value="{searchTerms}"/>
</Url>
<Url type="text/html" method="GET" template="https://www.yandex.com/search">
  <MozParam name="clid" condition="purpose" purpose="searchbar"   value="2186618"/>
  <MozParam name="clid" condition="purpose" purpose="keyword"     value="2186621"/>
  <MozParam name="clid" condition="purpose" purpose="contextmenu" value="2186623"/>
  <MozParam name="clid" condition="purpose" purpose="homepage"    value="2186617"/>
  <MozParam name="clid" condition="purpose" purpose="newtab"      value="2186620"/>
  <Param name="text" value="{searchTerms}"/>
</Url>
<SearchForm>https://www.yandex.com/</SearchForm>
</SearchPlugin>
PK
!<�⌯��4chrome/en-US/locale/browser/setDesktopBackground.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!ENTITY position.label             "Position:">
<!ENTITY tile.label                 "Tile">
<!ENTITY center.label               "Center">
<!ENTITY stretch.label              "Stretch">
<!ENTITY fill.label                 "Fill">
<!ENTITY fit.label                  "Fit">
<!ENTITY preview.label              "Preview">
<!ENTITY color.label                "Color:">
<!ENTITY setDesktopBackground.title "Set Desktop Background">
<!ENTITY openDesktopPrefs.label     "Open Desktop Preferences">
<!ENTITY closeWindow.key            "w">
PK
!<�%�RRR3chrome/en-US/locale/browser/shellservice.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

optionsLabel=%S &Options
safeModeLabel=%S &Safe Mode

# LOCALIZATION NOTE (setDefaultBrowserMessage2, setDefaultBrowserConfirm.label):
# %S will be replaced by brandShortName
setDefaultBrowserMessage2          = Get the most out of %S by setting it as your default browser
setDefaultBrowserConfirm.label     = Use %S as my default browser
setDefaultBrowserConfirm.accesskey = U
setDefaultBrowserOptions.label     = Options
setDefaultBrowserOptions.accesskey = O
setDefaultBrowserNotNow.label      = Not now
setDefaultBrowserNotNow.accesskey  = N
setDefaultBrowserNever.label       = Don’t ask me again
setDefaultBrowserNever.accesskey   = D

# LOCALIZATION NOTE (setDefaultBrowserTitle, setDefaultBrowserMessage, setDefaultBrowserDontAsk, setDefaultBrowserAlertConfirm.label, setDefaultBrowserAlertNotNow.label):
# These strings are used as an alternative to the ones above, in a modal dialog.
# %S will be replaced by brandShortName
setDefaultBrowserTitle=Default Browser
setDefaultBrowserMessage=%S is not currently set as your default browser. Would you like to make it your default browser?
setDefaultBrowserDontAsk=Always perform this check when starting %S.
setDefaultBrowserAlertConfirm.label=Use %S as my default browser
setDefaultBrowserAlertNotNow.label=Not now

desktopBackgroundLeafNameWin=Desktop Background.bmp
DesktopBackgroundDownloading=Saving Picture…
DesktopBackgroundSet=Set Desktop Background
PK
!<I�/chrome/en-US/locale/browser/siteData.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

clearSiteDataPromptTitle=Clear all cookies and site data
# LOCALIZATION NOTE (clearSiteDataPromptText): %S = brandShortName
clearSiteDataPromptText=Selecting ‘Clear Now’ will clear all cookies and site data stored by %S. This may sign you out of websites and remove offline web content.
clearSiteDataNow=Clear Now
PK
!<	�``6chrome/en-US/locale/browser/sitePermissions.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE (state.current.allowed,
#                    state.current.allowedForSession,
#                    state.current.allowedTemporarily,
#                    state.current.blockedTemporarily,
#                    state.current.blocked,
#                    state.current.hide):
# This label is used to display active permission states in the site
# identity popup (which does not have a lot of screen space).
state.current.allowed = Allowed
state.current.allowedForSession = Allowed for Session
state.current.allowedTemporarily = Allowed Temporarily
state.current.blockedTemporarily = Blocked Temporarily
state.current.blocked = Blocked
state.current.prompt = Always Ask
state.current.hide = Hide Prompt

# LOCALIZATION NOTE (state.multichoice.alwaysAsk,
#                    state.multichoice.allow,
#                    state.multichoice.allowForSession,
#                    state.multichoice.block):
# Used to label permission state checkboxes in the page info dialog.
state.multichoice.alwaysAsk = Always Ask
state.multichoice.allow = Allow
state.multichoice.allowForSession = Allow for Session
state.multichoice.block = Block

permission.cookie.label = Set Cookies
permission.desktop-notification2.label = Receive Notifications
permission.image.label = Load Images
permission.camera.label = Use the Camera
permission.microphone.label = Use the Microphone
permission.screen.label = Share the Screen
permission.install.label = Install Add-ons
permission.popup.label = Open Pop-up Windows
permission.geo.label = Access Your Location
permission.shortcuts.label = Override Keyboard Shortcuts
permission.focus-tab-by-prompt.label = Switch to this Tab
permission.persistent-storage.label = Store Data in Persistent Storage
permission.canvas.label = Extract Canvas Data
permission.flash-plugin.label = Run Adobe Flash
permission.midi.label = Access MIDI Devices
permission.midi-sysex.label = Access MIDI Devices with SysEx SupportPK
!<V|@ll)chrome/en-US/locale/browser/syncBrand.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!ENTITY syncBrand.shortName.label  "Sync">
<!ENTITY syncBrand.fullName.label   "Firefox Sync">
<!ENTITY syncBrand.fxAccount.label  "Firefox Account">
PK
!<^x��0chrome/en-US/locale/browser/syncSetup.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# Several other strings are used (via Weave.Status.login), but they come from
#  /services/sync

# Firefox Accounts based setup.
continue.label = Continue

# LOCALIZATION NOTE (disconnect.label, disconnect.verify.title, disconnect.verify.bodyHeading, disconnect.verify.bodyText):
# These strings are used in the confirmation dialog shown when the user hits the disconnect button
# LOCALIZATION NOTE (disconnect.label): This is the label for the disconnect button
disconnect.label = Disconnect
disconnect.verify.title = Disconnect
disconnect.verify.bodyHeading = Disconnect from Sync?
disconnect.verify.bodyText = Your browsing data will remain on this computer, but it will no longer sync with your account.

relinkVerify.title = Merge Warning
relinkVerify.heading = Are you sure you want to sign in to Sync?
# LOCALIZATION NOTE (relinkVerify.description): Email address of a user previously signed into sync.
relinkVerify.description = A different user was previously signed in to Sync on this computer. Signing in will merge this browser’s bookmarks, passwords and other settings with %S
PK
!<u�5�aa.chrome/en-US/locale/browser/taskbar.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

taskbar.tasks.newTab.label=Open new tab
taskbar.tasks.newTab.description=Open a new browser tab.
taskbar.tasks.newWindow.label=Open new window
taskbar.tasks.newWindow.description=Open a new browser window.
taskbar.tasks.newPrivateWindow.label=New private window
taskbar.tasks.newPrivateWindow.description=Open a new window in private browsing mode.
taskbar.frequent.label=Frequent
taskbar.recent.label=Recent
PK
!<�����+chrome/en-US/locale/browser/translation.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!-- LOCALIZATION NOTE (translation.thisPageIsIn.label,
  -                     translation.translateThisPage.label):
  -  These 2 strings are used to construct a sentence that contains a dropdown
  -  showing the detected language of the current web page.
  -  In en-US it looks like this:
  -    This page is in [detected language] Translate this page?
  -  "detected language" here is a language name coming from the
  -  global/languageNames.properties file; for some locales it may not be in
  -  the correct grammar case to keep the same structure of the original
  -  sentence. -->
<!ENTITY translation.thisPageIsIn.label         "This page is in">
<!ENTITY translation.translateThisPage.label    "Translate this page?">
<!ENTITY translation.translate.button           "Translate">
<!ENTITY translation.notNow.button              "Not Now">

<!ENTITY translation.translatingContent.label   "Translating page content…">

<!-- LOCALIZATION NOTE (translation.translatedFrom.label,
  -                     translation.translatedTo.label,
  -                     translation.translatedToSuffix.label):
  -  These 3 strings are used to construct a sentence that contains 2 dropdowns
  -  showing the source and target language of a translated web page.
  -  In en-US it looks like this:
  -    This page has been translated from [from language] to [to language]
  -  "from language" and "to language" here are language names coming from the
  -  global/languageNames.properties file; for some locales they may not be in
  -  the correct grammar case to keep the same structure of the original
  -  sentence.
  -
  -  translation.translatedToSuffix.label (empty in en-US) is for locales that
  -  need to display some text after the second drop down for the sentence to
  -  be grammatically correct. -->
<!ENTITY translation.translatedFrom.label       "This page has been translated from">
<!ENTITY translation.translatedTo.label         "to">
<!ENTITY translation.translatedToSuffix.label   "">

<!ENTITY translation.showOriginal.button        "Show Original">
<!ENTITY translation.showTranslation.button     "Show Translation">

<!ENTITY translation.errorTranslating.label     "There has been an error translating this page.">
<!ENTITY translation.tryAgain.button            "Try Again">

<!ENTITY translation.serviceUnavailable.label   "Translation is not available at the moment. Please try again later.">

<!ENTITY translation.options.menu               "Options">
<!-- LOCALIZATION NOTE (translation.options.neverForSite.accesskey,
  -                     translation.options.preferences.accesskey):
  -  The accesskey values used here should not clash with the value used for
  -  translation.options.neverForLanguage.accesskey in translation.properties
  -->
<!ENTITY translation.options.neverForSite.label "Never translate this site">
<!ENTITY translation.options.neverForSite.accesskey "e">
<!ENTITY translation.options.preferences.label  "Translation preferences">
<!ENTITY translation.options.preferences.accesskey "T">

<!-- LOCALIZATION NOTE (translation.options.attribution.beforeLogo,
  -                     translation.options.attribution.afterLogo):
  -  These 2 strings are displayed before and after a 'Microsoft Translator'
  -  logo.
  -->
<!ENTITY translation.options.attribution.beforeLogo "Translations by">
<!ENTITY translation.options.attribution.afterLogo "">

<!-- LOCALIZATION NOTE (translation.options.attribution.poweredByYandex,
                        translation.options.attribution.beforeLogo,
  -                     translation.options.attribution.afterLogo):
  -  translation.options.attribution.poweredByYandex is displayed instead of
  -  the other two strings when yandex translation engine is preferred by the
  -  user.
  -->
<!ENTITY translation.options.attribution.yandexTranslate "Powered by Yandex.Translate">
PK
!<l�֎�2chrome/en-US/locale/browser/translation.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE (translation.options.neverForLanguage.label):
#  %S is a language name coming from the global/languageNames.properties file.
translation.options.neverForLanguage.label=Never translate %S

# LOCALIZATION NOTE (translation.options.neverForLanguage.accesskey):
# The accesskey value used here should not clash with the values used for
# translation.options.*.accesskey in translation.dtd
translation.options.neverForLanguage.accesskey=N
PK
!<�=��0chrome/en-US/locale/browser/uiDensity.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

uiDensity.menuitem-touch.acceltext=Tablet Mode Enabled
PK
!<QDG776chrome/en-US/locale/browser/webrtcIndicator.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE : FILE This file contains the webrtc global indicator strings

# LOCALIZATION NOTE (webrtcIndicator.windowtitle): %S is the brand name (e.g. Firefox).
# This string is used so that the window has a title in tools that enumerate/look for window
# titles. It is not normally visible anywhere.
webrtcIndicator.windowtitle = %S - Sharing Indicator

webrtcIndicator.sharingCameraAndMicrophone.tooltip = Your camera and microphone are being shared. Click to control sharing.
webrtcIndicator.sharingCamera.tooltip              = Your camera is being shared. Click to control sharing.
webrtcIndicator.sharingMicrophone.tooltip          = Your microphone is being shared. Click to control sharing.
webrtcIndicator.sharingApplication.tooltip = An application is being shared. Click to control sharing.
webrtcIndicator.sharingScreen.tooltip = Your screen is being shared. Click to control sharing.
webrtcIndicator.sharingWindow.tooltip = A window is being shared. Click to control sharing.
webrtcIndicator.sharingBrowser.tooltip = A tab is being shared. Click to control sharing.


# LOCALIZATION NOTE : The following strings are only used on Mac for
# menus attached to icons near the clock on the mac menubar.

# LOCALIZATION NOTE (webrtcIndicator.sharing*With.menuitem):
# %S is the title of the tab using the share.
webrtcIndicator.sharingCameraWith.menuitem = Sharing Camera with “%S”
webrtcIndicator.sharingMicrophoneWith.menuitem = Sharing Microphone with “%S”
webrtcIndicator.sharingApplicationWith.menuitem = Sharing an Application with “%S”
webrtcIndicator.sharingScreenWith.menuitem = Sharing Screen with “%S”
webrtcIndicator.sharingWindowWith.menuitem = Sharing a Window with “%S”
webrtcIndicator.sharingBrowserWith.menuitem = Sharing a Tab with “%S”
webrtcIndicator.controlSharing.menuitem = Control Sharing
# LOCALIZATION NOTE (webrtcIndicator.sharingCameraWithNTabs.menuitem):
# Semicolon-separated list of plural forms. See:
# http://developer.mozilla.org/en/docs/Localization_and_Plurals
webrtcIndicator.sharingCameraWithNTabs.menuitem = Sharing Camera with #1 tab;Sharing Camera with #1 tabs
# LOCALIZATION NOTE (webrtcIndicator.sharingMicrophoneWithNTabs.menuitem):
# Semicolon-separated list of plural forms. See:
# http://developer.mozilla.org/en/docs/Localization_and_Plurals
webrtcIndicator.sharingMicrophoneWithNTabs.menuitem = Sharing Microphone with #1 tab;Sharing Microphone with #1 tabs
# LOCALIZATION NOTE (webrtcIndicator.sharingApplicationWithNTabs.menuitem):
# Semicolon-separated list of plural forms. See:
# http://developer.mozilla.org/en/docs/Localization_and_Plurals
webrtcIndicator.sharingApplicationWithNTabs.menuitem = Sharing an Application with #1 tab;Sharing Applications with #1 tabs
# LOCALIZATION NOTE (webrtcIndicator.sharingScreenWithNTabs.menuitem):
# Semicolon-separated list of plural forms. See:
# http://developer.mozilla.org/en/docs/Localization_and_Plurals
webrtcIndicator.sharingScreenWithNTabs.menuitem = Sharing Screen with #1 tab;Sharing Screen with #1 tabs
# LOCALIZATION NOTE (webrtcIndicator.sharingWindowWithNTabs.menuitem):
# Semicolon-separated list of plural forms. See:
# http://developer.mozilla.org/en/docs/Localization_and_Plurals
webrtcIndicator.sharingWindowWithNTabs.menuitem = Sharing a Window with #1 tab;Sharing Windows with #1 tabs
# LOCALIZATION NOTE (webrtcIndicator.sharingBrowserWithNTabs.menuitem):
# Semicolon-separated list of plural forms. See:
# http://developer.mozilla.org/en/docs/Localization_and_Plurals
# This message is shown when the contents of a tab is shared during a WebRTC
# session, which currently is only possible with Loop/Hello.
webrtcIndicator.sharingBrowserWithNTabs.menuitem = Sharing a Tab with #1 tab;Sharing Tabs with #1 tabs
# LOCALIZATION NOTE (webrtcIndicator.controlSharingOn.menuitem):
# %S is the title of the tab using the share.
webrtcIndicator.controlSharingOn.menuitem = Control Sharing on “%S”
PK
!<��8Jhh;chrome/en-US/locale/en-US/devtools/client/VariablesView.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!-- LOCALIZATION NOTE : FILE The correct localization of this file might be to
  - keep it in English, or another language commonly spoken among web developers.
  - You want to make that choice consistent across the developer tools.
  - A good criteria is the language in which you'd find the best
  - documentation on web development on the web. -->

<!ENTITY PropertiesViewWindowTitle "Properties">

PK
!<bg��$$<chrome/en-US/locale/en-US/devtools/client/aboutdebugging.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!ENTITY aboutDebugging.fullTitle    "Debugging with Firefox Developer Tools">
PK
!<�8.o�"�"Cchrome/en-US/locale/en-US/devtools/client/aboutdebugging.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE (debug):
# This string is displayed as a label of the button that starts
# debugging a service worker.
debug = Debug

# LOCALIZATION NOTE (push):
# This string is displayed as a label of the button that pushes a test payload
# to a service worker.
push = Push

# LOCALIZATION NOTE (start):
# This string is displayed as a label of the button that starts a service worker.
start = Start

scope = Scope
unregister = unregister

pushService = Push Service

# LOCALIZATION NOTE (fetch):
# Fetch is an event type and should not be translated.
fetch = Fetch

# LOCALIZATION NOTE (listeningForFetchEvents):
# This is used to display the state of the SW in regard to fetch events.
listeningForFetchEvents = Listening for fetch events.

# LOCALIZATION NOTE (notListeningForFetchEvents):
# This is used to display the state of the SW in regard to fetch events.
notListeningForFetchEvents = Not listening for fetch events.

# LOCALIZATION NOTE (addons):
# This string is displayed as a header of the about:debugging#addons page.
addons = Add-ons

# LOCALIZATION NOTE (addonDebugging.label):
# This string is displayed next to a check box that enables the user to switch
# addon debugging on/off.
addonDebugging.label = Enable add-on debugging

# LOCALIZATION NOTE (addonDebugging.tooltip):
# This string is displayed in a tooltip that appears when hovering over a check
# box that switches addon debugging on/off.
addonDebugging.tooltip = Turning this on will allow you to debug add-ons and various other parts of the browser chrome

# LOCALIZATION NOTE (addonDebugging.learnMore):
# This string is displayed as a link next to addonDebugging.label and leads the user to
# the MDN documentation page for about:debugging.
# (https://developer.mozilla.org/docs/Tools/about:debugging#Enabling_add-on_debugging)
addonDebugging.learnMore = Learn more

# LOCALIZATION NOTE (loadTemporaryAddon):
# This string is displayed as a label of a button that allows the user to
# load additional add-ons.
loadTemporaryAddon = Load Temporary Add-on

# LOCALIZATION NOTE (retryTemporaryInstall):
# This string is displayed as a label of a button that allows the user to
# retry a failed installation of a temporary add-on.
retryTemporaryInstall = Retry

# LOCALIZATION NOTE (extensions):
# This string is displayed as a header above the list of loaded add-ons.
extensions = Extensions

# LOCALIZATION NOTE (temporaryExtensions):
# This string is displayed as a header above the list of temporarily loaded add-ons.
temporaryExtensions = Temporary Extensions

# LOCALIZATION NOTE (internalUUID):
# This string is displayed as a label for the internal UUID of an extension.
# The UUID is generated for this profile on install.
internalUUID = Internal UUID

# LOCALIZATION NOTE (extensionID):
# This string is displayed as a label for the ID of an extension. This is not the same as the internal UUID.
extensionID = Extension ID

# LOCALIZATION NOTE (manifestURL):
# This string is displayed as a link for the manifest of an extension,
# accessible in a browser, such as moz-extension://[internalUUID]/manifest.json.
manifestURL = Manifest URL

# LOCALIZATION NOTE (webExtTip):
# This string is displayed as a message below the list of temporarily loaded add-ons.
# Web-ext is a command line tool for web-extensions developers.
# See https://developer.mozilla.org/Add-ons/WebExtensions/Getting_started_with_web-ext
webExtTip = You can use web-ext to load temporary WebExtensions from the command line.

# LOCALIZATION NOTE (webExtTip.learnMore):
# This string is displayed as a link next to webExtTip and leads the user to the MDN
# documentation page for web-ext.
# (https://developer.mozilla.org/Add-ons/WebExtensions/Getting_started_with_web-ext)
webExtTip.learnMore = Learn more

# LOCALIZATION NOTE (temporaryID):
# This string is displayed as a message about the add-on having a temporaryID.
temporaryID = This WebExtension has a temporary ID.

# LOCALIZATION NOTE (temporaryID.learnMore):
# This string is displayed as a link next to the temporaryID message and leads
# the user to MDN.
temporaryID.learnMore = Learn more

# LOCALIZATION NOTE (legacyExtensionWarning):
# This string is displayed as a warning message when loading a temporary legacy extension.
legacyExtensionWarning = This is a legacy extension, be aware that these are no longer fully supported.  Please read the linked documentation and then proceed with caution.

# LOCALIZATION NOTE (legacyExtensionWarning.learnMore):
# This string is displayed as a link next to the legacyExtensionWarning message and leads
# the user to https://wiki.mozilla.org/Add-ons/Future_of_Bootstrap.
legacyExtensionWarning.learnMore = Learn more

# LOCALIZATION NOTE (selectAddonFromFile2):
# This string is displayed as the title of the file picker that appears when
# the user clicks the 'Load Temporary Add-on' button
selectAddonFromFile2 = Select Manifest File or Package (.xpi)

# LOCALIZATION NOTE (reload):
# This string is displayed as a label of the button that reloads a given addon.
reload = Reload

# LOCALIZATION NOTE (remove):
# This string is displayed as a label of the button that will remove a given addon.
remove = Remove

# LOCALIZATION NOTE (location):
# This string is displayed as a label for the filesystem location of an extension.
location = Location

# LOCALIZATION NOTE (workers):
# This string is displayed as a header of the about:debugging#workers page.
workers = Workers

serviceWorkers = Service Workers
sharedWorkers = Shared Workers
otherWorkers = Other Workers

# LOCALIZATION NOTE (running):
# This string is displayed as the state of a service worker in RUNNING state.
running = Running

# LOCALIZATION NOTE (stopped):
# This string is displayed as the state of a service worker in STOPPED state.
stopped = Stopped

# LOCALIZATION NOTE (registering):
# This string is displayed as the state of a service worker for which no service worker
# registration could be found yet. Only active registrations are visible from
# about:debugging, so such service workers are considered as registering.
registering = Registering

# LOCALIZATION NOTE (tabs):
# This string is displayed as a header of the about:debugging#tabs page.
tabs = Tabs

# LOCALIZATION NOTE (pageNotFound):
# This string is displayed as the main message at any error/invalid page.
pageNotFound = Page not found

# LOCALIZATION NOTE (doesNotExist):
# This string is displayed as an error message when navigating to an invalid page
# %S will be replaced by the name of the page at run-time.
doesNotExist = #%S does not exist!

# LOCALIZATION NOTE (nothing):
# This string is displayed when the list of workers is empty.
nothing = Nothing yet.

# LOCALIZATION NOTE (configurationIsNotCompatible.label):
# This string is displayed in about:debugging#workers if the current configuration of the
# browser is incompatible with service workers. More details at
# https://developer.mozilla.org/en-US/docs/Tools/about%3Adebugging#Service_workers_not_compatible
configurationIsNotCompatible.label = Your browser configuration is not compatible with Service Workers.

# LOCALIZATION NOTE (configurationIsNotCompatible.learnMore):
# This string is displayed as a link next to configurationIsNotCompatible.label and leads
# the user to the MDN documentation page for about:debugging, on the section explaining
# why service workers might not be available.
# (https://developer.mozilla.org/en-US/docs/Tools/about%3Adebugging#Service_workers_not_compatible)
configurationIsNotCompatible.learnMore = Learn more

# LOCALIZATION NOTE (multiProcessWarningTitle):
# This string is displayed as a warning message on top of the about:debugging#workers
# page when multi-e10s is enabled
multiProcessWarningTitle = Service Worker debugging is not compatible with multiple content processes at the moment.

# LOCALIZATION NOTE (multiProcessWarningMessage2):
# This string is displayed in the warning section for multi-e10s in
# about:debugging#workers
multiProcessWarningMessage2 = The preference “dom.ipc.multiOptOut” can be modified to force a single content process for the current version.

# LOCALIZATION NOTE (multiProcessWarningUpdateLink2):
# This string is the text content of a link in the warning section for multi-e10s in
# about:debugging#workers. The link updates the pref and restarts the browser.
multiProcessWarningUpdateLink2 = Opt out of multiple content processes

# LOCALIZATION NOTE (multiProcessWarningConfirmUpdate2):
# This string is displayed as a confirmation message when the user clicks on
# the multiProcessWarningUpdateLink in about:debugging#workers
multiProcessWarningConfirmUpdate2 = Opt out of multiple processes?
PK
!<���0�&�&Gchrome/en-US/locale/en-US/devtools/client/animationinspector.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE These strings are used inside the Animation inspector
# which is available as a sidebar panel in the Inspector.
# The correct localization of this file might be to keep it in
# English, or another language commonly spoken among web developers.
# You want to make that choice consistent across the developer tools.
# A good criteria is the language in which you'd find the best
# documentation on web development on the web.

# LOCALIZATION NOTE (panel.invalidElementSelected):
# This is the label shown in the panel when an invalid node is currently
# selected in the inspector (i.e. a non-element node or a node that is not
# animated).
panel.invalidElementSelected=No animations were found for the current element.

# LOCALIZATION NOTE (panel.selectElement): This is the label shown in the panel
# when an invalid node is currently selected in the inspector, to invite the
# user to select a new node by clicking on the element-picker icon.
panel.selectElement=Pick another element from the page.

# LOCALIZATION NOTE (panel.noAnimation):
# This is the label shown in the panel when there are no displayable animations.
# (e.g. In case of user selected a non-element node or a node that is not animated).
panel.noAnimation=No animations were found for the current element.\nPick another element from the page.

# LOCALIZATION NOTE (panel.allAnimations): This is the label shown at the bottom of
# the panel, in a toolbar, to let the user know the toolbar applies to all
# animations, not just the ones applying to the current element.
panel.allAnimations=All animations

# LOCALIZATION NOTE (player.animationDurationLabel):
# This string is displayed in each animation player widget. It is the label
# displayed before the animation duration.
player.animationDurationLabel=Duration:

# LOCALIZATION NOTE (player.animationDelayLabel):
# This string is displayed in each animation player widget. It is the label
# displayed before the animation delay.
player.animationDelayLabel=Delay:

# LOCALIZATION NOTE (player.animationEndDelayLabel):
# This string is displayed in each animation player widget. It is the label
# displayed before the animation endDelay.
player.animationEndDelayLabel=End delay:

# LOCALIZATION NOTE (player.animationRateLabel):
# This string is displayed in each animation player widget. It is the label
# displayed before the animation playback rate.
player.animationRateLabel=Playback rate:

# LOCALIZATION NOTE (player.animationIterationCountLabel):
# This string is displayed in each animation player widget. It is the label
# displayed before the number of times the animation is set to repeat.
player.animationIterationCountLabel=Repeats:

# LOCALIZATION NOTE (player.infiniteIterationCount):
# In case the animation repeats infinitely, this string is displayed next to the
# player.animationIterationCountLabel string, instead of a number.
player.infiniteIterationCount=&#8734;

# LOCALIZATION NOTE (player.infiniteIterationCountText):
# See player.infiniteIterationCount for a description of what this is.
# Unlike player.infiniteIterationCount, this string isn't used in HTML, but in
# a tooltip.
player.infiniteIterationCountText=∞

# LOCALIZATION NOTE (player.animationIterationStartLabel):
# This string is displayed in a tooltip that appears when hovering over
# animations in the timeline. It is the label displayed before the animation
# iterationStart value.
# %1$S will be replaced by the original iteration start value
# %2$S will be replaced by the actual time of iteration start
player.animationIterationStartLabel=Iteration start: %1$S (%2$Ss)

# LOCALIZATION NOTE (player.animationOverallEasingLabel):
# This string is displayed in a tooltip that appears when hovering over
# animations in the timeline. It is the label displayed before the easing
# that applies to a whole iteration of an animation as opposed to the
# easing that applies between animation keyframes.
player.animationOverallEasingLabel=Overall easing:

# LOCALIZATION NOTE (player.animationTimingFunctionLabel):
# This string is displayed in a tooltip that appears when hovering over
# animations in the timeline. It is the label displayed before the
# animation-timing-function for CSS Animations.
player.animationTimingFunctionLabel=Animation timing function:

# LOCALIZATION NOTE (player.animationFillLabel):
# This string is displayed in a tooltip that appears when hovering over
# animations in the timeline. It is the label displayed before the animation
# fill mode value.
player.animationFillLabel=Fill:

# LOCALIZATION NOTE (player.animationDirectionLabel):
# This string is displayed in a tooltip that appears when hovering over
# animations in the timeline. It is the label displayed before the animation
# direction value.
player.animationDirectionLabel=Direction:

# LOCALIZATION NOTE (player.timeLabel):
# This string is displayed in each animation player widget, to indicate either
# how long (in seconds) the animation lasts, or what is the animation's current
# time (in seconds too);
player.timeLabel=%Ss

# LOCALIZATION NOTE (player.playbackRateLabel):
# This string is displayed in each animation player widget, as the label of
# drop-down list items that can be used to change the rate at which the
# animation runs (1× being the default, 2× being twice as fast).
player.playbackRateLabel=%S×

# LOCALIZATION NOTE (player.runningOnCompositorTooltip):
# This string is displayed as a tooltip for the icon that indicates that the
# animation is running on the compositor thread.
player.runningOnCompositorTooltip=This animation is running on compositor thread

# LOCALIZATION NOTE (player.allPropertiesOnCompositorTooltip):
# This string is displayed as a tooltip for the icon that indicates that
# all of animation is running on the compositor thread.
player.allPropertiesOnCompositorTooltip=All animation properties are optimized

# LOCALIZATION NOTE (player.somePropertiesOnCompositorTooltip):
# This string is displayed as a tooltip for the icon that indicates that
# all of animation is not running on the compositor thread.
player.somePropertiesOnCompositorTooltip=Some animation properties are optimized

# LOCALIZATION NOTE (timeline.rateSelectorTooltip):
# This string is displayed in the timeline toolbar, as the tooltip of the
# drop-down list that can be used to change the rate at which the animations
# run.
timeline.rateSelectorTooltip=Set the animations playback rates

# LOCALIZATION NOTE (timeline.pausedButtonTooltip):
# This string is displayed in the timeline toolbar, as the tooltip of the
# pause/resume button that can be used to pause or resume the animations
timeline.pausedButtonTooltip=Resume the animations

# LOCALIZATION NOTE (timeline.resumedButtonTooltip):
# This string is displayed in the timeline toolbar, as the tooltip of the
# pause/resume button that can be used to pause or resume the animations
timeline.resumedButtonTooltip=Pause the animations

# LOCALIZATION NOTE (timeline.rewindButtonTooltip):
# This string is displayed in the timeline toolbar, as the tooltip of the
# rewind button that can be used to rewind the animations
timeline.rewindButtonTooltip=Rewind the animations

# LOCALIZATION NOTE (timeline.timeGraduationLabel):
# This string is displayed at the top of the animation panel, next to each time
# graduation, to indicate what duration (in milliseconds) this graduation
# corresponds to.
timeline.timeGraduationLabel=%Sms

# LOCALIZATION NOTE (timeline.cssanimation.nameLabel):
# This string is displayed in a tooltip of the animation panel that is shown
# when hovering over the name of a CSS Animation in the timeline UI.
# %S will be replaced by the name of the animation at run-time.
timeline.cssanimation.nameLabel=%S - CSS Animation

# LOCALIZATION NOTE (timeline.csstransition.nameLabel):
# This string is displayed in a tooltip of the animation panel that is shown
# when hovering over the name of a CSS Transition in the timeline UI.
# %S will be replaced by the name of the transition at run-time.
timeline.csstransition.nameLabel=%S - CSS Transition

# LOCALIZATION NOTE (timeline.scriptanimation.nameLabel):
# This string is displayed in a tooltip of the animation panel that is shown
# when hovering over the name of a script-generated animation in the timeline UI.
# %S will be replaced by the name of the animation at run-time.
timeline.scriptanimation.nameLabel=%S - Script Animation

# LOCALIZATION NOTE (timeline.scriptanimation.unnamedLabel):
# This string is displayed in a tooltip of the animation panel that is shown
# when hovering over an unnamed script-generated animation in the timeline UI.
timeline.scriptanimation.unnamedLabel=Script Animation

# LOCALIZATION NOTE (timeline.unknown.nameLabel):
# This string is displayed in a tooltip of the animation panel that is shown
# when hovering over the name of an unknown animation type in the timeline UI.
# This can happen if devtools couldn't figure out the type of the animation.
# %S will be replaced by the name of the transition at run-time.
timeline.unknown.nameLabel=%S

# LOCALIZATION NOTE (detail.propertiesHeader.percentage):
# This string is displayed on header label in .animated-properties-header.
# %S represents the value in percentage with two decimal points, localized.
# there are two "%" after %S to escape and display "%"
detail.propertiesHeader.percentage=%S%%

# LOCALIZATION NOTE (detail.headerTitle):
# This string is displayed on header label in .animation-detail-header.
detail.headerTitle=Animated properties for

# LOCALIZATION NOTE (detail.header.closeLabel):
# This string is displayed in a tooltip of close button for animated properties
detail.header.closeLabel=Close animated properties panel
PK
!<p"mm@chrome/en-US/locale/en-US/devtools/client/app-manager.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

validator.nonExistingFolder=The project folder doesn’t exist
validator.expectProjectFolder=The project folder ends up being a file
validator.noManifestFile=A manifest file is required at project root folder, named either ‘manifest.webapp’ for packaged apps or ‘manifest.json’ for add-ons.
validator.invalidManifestURL=Invalid manifest URL ‘%S’
# LOCALIZATION NOTE (validator.invalidManifestJSON, validator.noAccessManifestURL):
# %1$S is the error message, %2$S is the URI of the manifest.
validator.invalidManifestJSON=The webapp manifest isn’t a valid JSON file: %1$S at: %2$S
validator.noAccessManifestURL=Unable to read manifest file: %1$S at: %2$S
# LOCALIZATION NOTE (validator.invalidHostedManifestURL): %1$S is the URI of
# the manifest, %2$S is the error message.
validator.invalidHostedManifestURL=Invalid hosted manifest URL ‘%1$S’: %2$S
validator.invalidProjectType=Unknown project type ‘%S’
# LOCALIZATION NOTE (validator.missNameManifestProperty, validator.missIconsManifestProperty):
# don't translate 'icons' and 'name'.
validator.missNameManifestProperty=Missing mandatory ‘name’ in Manifest.
validator.missIconsManifestProperty=Missing ‘icons’ in Manifest.
validator.missIconMarketplace2=app submission to the Marketplace requires a 128px icon
validator.invalidAppType=Unknown app type: ‘%S’.
validator.invalidHostedPriviledges=Hosted App can’t be type ‘%S’.
validator.noCertifiedSupport=‘certified’ apps are not fully supported on the App manager.
validator.nonAbsoluteLaunchPath=Launch path has to be an absolute path starting with ‘/’: ‘%S’
validator.accessFailedLaunchPath=Unable to access the app starting document ‘%S’
# LOCALIZATION NOTE (validator.accessFailedLaunchPathBadHttpCode): %1$S is the URI of
# the launch document, %2$S is the http error code.
validator.accessFailedLaunchPathBadHttpCode=Unable to access the app starting document ‘%1$S’, got HTTP code %2$S
PK
!<4	�##Bchrome/en-US/locale/en-US/devtools/client/appcacheutils.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE These strings are used inside the Web Console
# command line which is available from the Web Developer sub-menu
# -> 'Web Console'.
# These messages are displayed when an attempt is made to validate a
# page or a cache manifest using AppCacheUtils.jsm

# The correct localization of this file might be to keep it in
# English, or another language commonly spoken among web developers.
# You want to make that choice consistent across the developer tools.
# A good criteria is the language in which you'd find the best
# documentation on web development on the web.

# LOCALIZATION NOTE (noManifest): the specified page has no cache manifest.
noManifest=The specified page has no manifest.

# LOCALIZATION NOTE (notUTF8): the associated cache manifest has a character
# encoding that is not UTF-8. Parameters: %S is the current encoding.
notUTF8=Manifest has a character encoding of %S. Manifests must have the utf-8 character encoding.

# LOCALIZATION NOTE (badMimeType): the associated cache manifest has a
# mimetype that is not text/cache-manifest. Parameters: %S is the current
# mimetype.
badMimeType=Manifest has a mimetype of %S. Manifests must have a mimetype of text/cache-manifest.

# LOCALIZATION NOTE (duplicateURI): the associated cache manifest references
# the same URI from multiple locations. Parameters: %1$S is the URI, %2$S is a
# list of references to this URI.
duplicateURI=URI %1$S is referenced in multiple locations. This is not allowed: %2$S.

# LOCALIZATION NOTE (networkBlocksURI, fallbackBlocksURI): the associated
# cache manifest references the same URI in the NETWORK (or FALLBACK) section
# as it does in other sections. Parameters: %1$S is the line number, %2$S is
# the resource name, %3$S is the line number, %4$S is the resource name, %5$S
# is the section name.
networkBlocksURI=NETWORK section line %1$S (%2$S) prevents caching of line %3$S (%4$S) in the %5$S section.
fallbackBlocksURI=FALLBACK section line %1$S (%2$S) prevents caching of line %3$S (%4$S) in the %5$S section.

# LOCALIZATION NOTE (fileChangedButNotManifest): the associated cache manifest
# references a URI that has a file modified after the cache manifest.
# Parameters: %1$S is the resource name, %2$S is the cache manifest, %3$S is
# the line number.
fileChangedButNotManifest=The file %1$S was modified after %2$S. Unless the text in the manifest file is changed the cached version will be used instead at line %3$S.

# LOCALIZATION NOTE (cacheControlNoStore): the specified page has a header
# preventing caching or storing information. Parameters: %1$S is the resource
# name, %2$S is the line number.
cacheControlNoStore=%1$S has cache-control set to no-store. This will prevent the application cache from storing the file at line %2$S.

# LOCALIZATION NOTE (notAvailable): the specified resource is not available.
# Parameters: %1$S is the resource name, %2$S is the line number.
notAvailable=%1$S points to a resource that is not available at line %2$S.

# LOCALIZATION NOTE (invalidURI): it's used when an invalid URI is passed to
# the appcache.
invalidURI=The URI passed to AppCacheUtils is invalid.

# LOCALIZATION NOTE (noResults): it's used when a search returns no results.
noResults=Your search returned no results.

# LOCALIZATION NOTE (cacheDisabled): it's used when the cache is disabled and
# an attempt is made to view offline data.
cacheDisabled=Your disk cache is disabled. Please set browser.cache.disk.enable to true in about:config and try again.

# LOCALIZATION NOTE (firstLineMustBeCacheManifest): the associated cache
# manifest has a first line that is not "CACHE MANIFEST". Parameters: %S is
# the line number.
firstLineMustBeCacheManifest=The first line of the manifest must be “CACHE MANIFEST” at line %S.

# LOCALIZATION NOTE (cacheManifestOnlyFirstLine2): the associated cache
# manifest has "CACHE MANIFEST" on a line other than the first line.
# Parameters: %S is the line number where "CACHE MANIFEST" appears.
cacheManifestOnlyFirstLine2=“CACHE MANIFEST” is only valid on the first line but was found at line %S.

# LOCALIZATION NOTE (asteriskInWrongSection2): the associated cache manifest
# has an asterisk (*) in a section other than the NETWORK section. Parameters:
# %1$S is the section name, %2$S is the line number.
asteriskInWrongSection2=Asterisk (*) incorrectly used in the %1$S section at line %2$S. If a line in the NETWORK section contains only a single asterisk character, then any URI not listed in the manifest will be treated as if the URI was listed in the NETWORK section. Otherwise such URIs will be treated as unavailable. Other uses of the * character are prohibited.

# LOCALIZATION NOTE (escapeSpaces1): the associated cache manifest has a space
# in a URI. Spaces must be replaced with %20. Parameters: %S is the line
# number where this error occurs.
# %% will be displayed as a single % character (% is commonly used to define
# format specifiers, so it needs to be escaped).
escapeSpaces1=Spaces in URIs need to be replaced with %%20 at line %S.

# LOCALIZATION NOTE (slashDotDotSlashBad): the associated cache manifest has a
# URI containing /../, which is invalid. Parameters: %S is the line number
# where this error occurs.
slashDotDotSlashBad=/../ is not a valid URI prefix at line %S.

# LOCALIZATION NOTE (tooManyDotDotSlashes): the associated cache manifest has
# a URI containing too many ../ operators. Too many of these operators mean
# that the file would be below the root of the site, which is not possible.
# Parameters: %S is the line number where this error occurs.
tooManyDotDotSlashes=Too many dot dot slash operators (../) at line %S.

# LOCALIZATION NOTE (fallbackUseSpaces): the associated cache manifest has a
# FALLBACK section containing more or less than the standard two URIs
# separated by a single space. Parameters: %S is the line number where this
# error occurs.
fallbackUseSpaces=Only two URIs separated by spaces are allowed in the FALLBACK section at line %S.

# LOCALIZATION NOTE (fallbackAsterisk2): the associated cache manifest has a
# FALLBACK section that attempts to use an asterisk (*) as a wildcard. In this
# section the URI is simply a path prefix. Parameters: %S is the line number
# where this error occurs.
fallbackAsterisk2=Asterisk (*) incorrectly used in the FALLBACK section at line %S. URIs in the FALLBACK section simply need to match a prefix of the request URI.

# LOCALIZATION NOTE (settingsBadValue): the associated cache manifest has a
# SETTINGS section containing something other than the valid "prefer-online"
# or "fast". Parameters: %S is the line number where this error occurs.
settingsBadValue=The SETTINGS section may only contain a single value, “prefer-online” or “fast” at line %S.

# LOCALIZATION NOTE (invalidSectionName): the associated cache manifest
# contains an invalid section name. Parameters: %1$S is the section name, %2$S
# is the line number.
invalidSectionName=Invalid section name (%1$S) at line %2$S.
PK
!<���		=chrome/en-US/locale/en-US/devtools/client/boxmodel.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE : FILE This file contains the Layout View strings.
# The Layout View is a panel displayed in the computed view tab of the Inspector sidebar.

# LOCALIZATION NOTE : FILE The correct localization of this file might be to
# keep it in English, or another language commonly spoken among web developers.
# You want to make that choice consistent across the developer tools.
# A good criteria is the language in which you'd find the best
# documentation on web development on the web.

# LOCALIZATION NOTE (boxmodel.title) This is the title of the box model panel and is
# displayed as a label.
boxmodel.title=Box Model

# LOCALIZATION NOTE (boxmodel.margin) This refers to the margin in the box model and
# might be displayed as a label or as a tooltip.
boxmodel.margin=margin

# LOCALIZATION NOTE (boxmodel.border) This refers to the border in the box model and
# might be displayed as a label or as a tooltip.
boxmodel.border=border

# LOCALIZATION NOTE (boxmodel.padding) This refers to the padding in the box model and
# might be displayed as a label or as a tooltip.
boxmodel.padding=padding

# LOCALIZATION NOTE (boxmodel.content) This refers to the content in the box model and
# might be displayed as a label or as a tooltip.
boxmodel.content=content

# LOCALIZATION NOTE: (boxmodel.geometryButton.tooltip) This label is displayed as a
# tooltip that appears when hovering over the button that allows users to edit the
# position of an element in the page.
boxmodel.geometryButton.tooltip=Edit position

# LOCALIZATION NOTE: (boxmodel.propertiesLabel) This label is displayed as the header
# for showing and collapsing the properties underneath the box model in the layout view
boxmodel.propertiesLabel=Box Model Properties

# LOCALIZATION NOTE: (boxmodel.offsetParent) This label is displayed inside the list of
# properties, below the box model, in the layout view. It is displayed next to the
# position property, when position is absolute, relative, sticky. This label tells users
# what the DOM node previewed next to it is: an offset parent for the position element.
boxmodel.offsetParent=offset
PK
!<T�r̺	�	<chrome/en-US/locale/en-US/devtools/client/canvasdebugger.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!-- LOCALIZATION NOTE : FILE This file contains the Debugger strings -->
<!-- LOCALIZATION NOTE : FILE Do not translate commandkey -->

<!-- LOCALIZATION NOTE : FILE The correct localization of this file might be to
  - keep it in English, or another language commonly spoken among web developers.
  - You want to make that choice consistent across the developer tools.
  - A good criteria is the language in which you'd find the best
  - documentation on web development on the web. -->

<!-- LOCALIZATION NOTE (canvasDebuggerUI.reloadNotice1): This is the label shown
  -  on the button that triggers a page refresh. -->
<!ENTITY canvasDebuggerUI.reloadNotice1   "Reload">

<!-- LOCALIZATION NOTE (canvasDebuggerUI.reloadNotice2): This is the label shown
  -  along with the button that triggers a page refresh. -->
<!ENTITY canvasDebuggerUI.reloadNotice2   "the page to be able to debug &lt;canvas&gt; contexts.">

<!-- LOCALIZATION NOTE (canvasDebuggerUI.emptyNotice1/2): This is the label shown
  -  in the call list view when empty. -->
<!ENTITY canvasDebuggerUI.emptyNotice1    "Click on the">
<!ENTITY canvasDebuggerUI.emptyNotice2    "button to record an animation frame’s call stack.">

<!-- LOCALIZATION NOTE (canvasDebuggerUI.waitingNotice): This is the label shown
  -  in the call list view while recording a snapshot. -->
<!ENTITY canvasDebuggerUI.waitingNotice   "Recording an animation cycle…">

<!-- LOCALIZATION NOTE (canvasDebuggerUI.recordSnapshot): This string is displayed
  -  on a button that starts a new snapshot. -->
<!ENTITY canvasDebuggerUI.recordSnapshot.tooltip "Record the next frame in the animation loop.">

<!-- LOCALIZATION NOTE (canvasDebuggerUI.importSnapshot): This string is displayed
  -  on a button that opens a dialog to import a saved snapshot data file. -->
<!ENTITY canvasDebuggerUI.importSnapshot "Import…">

<!-- LOCALIZATION NOTE (canvasDebuggerUI.clearSnapshots): This string is displayed
  -  on a button that remvoes all the snapshots. -->
<!ENTITY canvasDebuggerUI.clearSnapshots "Clear">

<!-- LOCALIZATION NOTE (canvasDebuggerUI.searchboxPlaceholder): This string is displayed
  -  as a placeholder of the search box that filters the calls list. -->
<!ENTITY canvasDebuggerUI.searchboxPlaceholder "Filter calls">
PK
!<�$�

Cchrome/en-US/locale/en-US/devtools/client/canvasdebugger.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE These strings are used inside the Canvas Debugger
# which is available from the Web Developer sub-menu -> 'Canvas'.
# The correct localization of this file might be to keep it in
# English, or another language commonly spoken among web developers.
# You want to make that choice consistent across the developer tools.
# A good criteria is the language in which you'd find the best
# documentation on web development on the web.

# LOCALIZATION NOTE (noSnapshotsText): The text to display in the snapshots menu
# when there are no recorded snapshots yet.
noSnapshotsText=There are no snapshots yet.

# LOCALIZATION NOTE (snapshotsList.itemLabel):
# This string is displayed in the snapshots list of the Canvas Debugger,
# identifying a set of function calls of a recorded animation frame.
snapshotsList.itemLabel=Snapshot #%S

# LOCALIZATION NOTE (snapshotsList.loadingLabel):
# This string is displayed in the snapshots list of the Canvas Debugger,
# for an item that has not finished loading.
snapshotsList.loadingLabel=Loading…

# LOCALIZATION NOTE (snapshotsList.saveLabel):
# This string is displayed in the snapshots list of the Canvas Debugger,
# for saving an item to disk.
snapshotsList.saveLabel=Save

# LOCALIZATION NOTE (snapshotsList.savingLabel):
# This string is displayed in the snapshots list of the Canvas Debugger,
# while saving an item to disk.
snapshotsList.savingLabel=Saving…

# LOCALIZATION NOTE (snapshotsList.loadedLabel):
# This string is displayed in the snapshots list of the Canvas Debugger,
# for an item which was loaded from disk
snapshotsList.loadedLabel=Loaded from disk

# LOCALIZATION NOTE (snapshotsList.saveDialogTitle):
# This string is displayed as a title for saving a snapshot to disk.
snapshotsList.saveDialogTitle=Save animation frame snapshot…

# LOCALIZATION NOTE (snapshotsList.saveDialogJSONFilter):
# This string is displayed as a filter for saving a snapshot to disk.
snapshotsList.saveDialogJSONFilter=JSON Files

# LOCALIZATION NOTE (snapshotsList.saveDialogAllFilter):
# This string is displayed as a filter for saving a snapshot to disk.
snapshotsList.saveDialogAllFilter=All Files

# LOCALIZATION NOTE (snapshotsList.drawCallsLabel):
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
# This string is displayed in the snapshots list of the Canvas Debugger,
# as a generic description about how many draw calls were made.
snapshotsList.drawCallsLabel=#1 draw;#1 draws

# LOCALIZATION NOTE (snapshotsList.functionCallsLabel):
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
# This string is displayed in the snapshots list of the Canvas Debugger,
# as a generic description about how many function calls were made in total.
snapshotsList.functionCallsLabel=#1 call;#1 calls

# LOCALIZATION NOTE (recordingTimeoutFailure):
# This notification alert is displayed when attempting to record a requestAnimationFrame
# cycle in the Canvas Debugger and no cycles detected. This alerts the user that no
# loops were found.
recordingTimeoutFailure=Canvas Debugger could not find a requestAnimationFrame or setTimeout cycle.
PK
!<�(Ѥ�?chrome/en-US/locale/en-US/devtools/client/components.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE These strings are used in the shared React components,
# so files in `devtools/client/shared/components/*`.

# LOCALIZATION NOTE (frame.unknownSource): When we do not know the source filename of
# a frame, we use this string instead.
frame.unknownSource=(unknown)

# LOCALIZATION NOTE (frame.viewsourceindebugger): The label for the tooltip when hovering over
# a source link that links to the debugger.
# %S represents the URL to match in the debugger.
frame.viewsourceindebugger=View source in Debugger → %S

# LOCALIZATION NOTE (notificationBox.closeTooltip): The content of a tooltip that
# appears when hovering over the close button in a notification box.
notificationBox.closeTooltip=Close this message
PK
!<�e���?chrome/en-US/locale/en-US/devtools/client/connection-screen.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!-- LOCALIZATION NOTE : FILE This file contains the Remote Connection strings.
  - The Remote Connection window can reached from the "connect…" menuitem
  - in the Web Developer menu.
  - -->

<!ENTITY title      "Connect">
<!ENTITY header     "Connect to remote device">
<!ENTITY host       "Host:">
<!ENTITY port       "Port:">
<!ENTITY connect    "Connect">
<!ENTITY connecting "Connecting…">
<!ENTITY availableAddons "Available remote add-ons:">
<!ENTITY availableTabs "Available remote tabs:">
<!ENTITY availableProcesses "Available remote processes:">
<!ENTITY connectionError "Error:">
<!ENTITY errorTimeout "Error: connection timeout.">
<!ENTITY errorRefused "Error: connection refused.">
<!ENTITY errorUnexpected "Unexpected error.">

<!-- LOCALIZATION NOTE (remoteHelp, remoteDocumentation, remoteHelpSuffix):
these strings will be concatenated in a single label, remoteDocumentation will
be used as text for a link to MDN. -->
<!ENTITY remoteHelp "Firefox Developer Tools can debug remote devices (Firefox for Android and Firefox OS, for example). Make sure that you have turned on the ‘Remote debugging’ option in the remote device. For more, see the ">
<!ENTITY remoteDocumentation "documentation">
<!ENTITY remoteHelpSuffix ".">

PK
!<�2��Fchrome/en-US/locale/en-US/devtools/client/connection-screen.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE : FILE This file contains the Remote Connection strings.
# The Remote Connection window can reached from the "connect…" menuitem
# in the Web Developer menu.

mainProcess=Main Process
PK
!<>Tń�,�,6chrome/en-US/locale/en-US/devtools/client/debugger.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!-- LOCALIZATION NOTE : FILE This file contains the Debugger strings -->
<!-- LOCALIZATION NOTE : FILE Do not translate commandkey -->

<!-- LOCALIZATION NOTE : FILE The correct localization of this file might be to
  - keep it in English, or another language commonly spoken among web developers.
  - You want to make that choice consistent across the developer tools.
  - A good criteria is the language in which you'd find the best
  - documentation on web development on the web. -->

<!-- LOCALIZATION NOTE (debuggerUI.closeButton.tooltip): This is the tooltip for
  -  the button that closes the debugger UI. -->
<!ENTITY debuggerUI.closeButton.tooltip "Close">

<!-- LOCALIZATION NOTE (debuggerUI.panesButton.tooltip): This is the tooltip for
  -  the button that toggles the panes visible or hidden in the debugger UI. -->
<!ENTITY debuggerUI.panesButton.tooltip "Toggle panes">

<!-- LOCALIZATION NOTE (debuggerUI.blackBoxMessage.label): This is the message
  - displayed to users when they select a black boxed source from the sources
  - list in the debugger. -->
<!ENTITY debuggerUI.blackBoxMessage.label "This source is black boxed: its breakpoints are disabled, and stepping skips through it.">

<!-- LOCALIZATION NOTE (debuggerUI.blackBoxMessage.unBlackBoxButton): This is
  - the text displayed in the button to stop black boxing the currently selected
  - source. -->
<!ENTITY debuggerUI.blackBoxMessage.unBlackBoxButton "Stop black boxing this source">

<!-- LOCALIZATION NOTE (debuggerUI.optsButton.tooltip): This is the tooltip for
  -  the button that opens up an options context menu for the debugger UI. -->
<!ENTITY debuggerUI.optsButton.tooltip  "Debugger Options">

<!-- LOCALIZATION NOTE (debuggerUI.sources.blackBoxTooltip): This is the tooltip
  -  for the button that black boxes the selected source. -->
<!ENTITY debuggerUI.sources.blackBoxTooltip "Toggle Black Boxing">

<!-- LOCALIZATION NOTE (debuggerUI.sources.prettyPrint): This is the tooltip for the
  -  button that pretty prints the selected source. -->
<!ENTITY debuggerUI.sources.prettyPrint "Prettify Source">

<!-- LOCALIZATION NOTE (debuggerUI.autoPrettyPrint): This is the label for the
  -  checkbox that toggles auto pretty print. -->
<!ENTITY debuggerUI.autoPrettyPrint     "Auto Prettify Minified Sources">
<!ENTITY debuggerUI.autoPrettyPrint.accesskey "P">

<!-- LOCALIZATION NOTE (debuggerUI.sources.toggleBreakpoints): This is the tooltip for the
  -  button that toggles all breakpoints for all sources. -->
<!ENTITY debuggerUI.sources.toggleBreakpoints "Enable/disable all breakpoints">

<!-- LOCALIZATION NOTE (debuggerUI.clearButton): This is the label for
  -  the button that clears the collected tracing data in the tracing tab. -->
<!ENTITY debuggerUI.clearButton "Clear">

<!-- LOCALIZATION NOTE (debuggerUI.clearButton.tooltip): This is the tooltip for
  -  the button that clears the collected tracing data in the tracing tab. -->
<!ENTITY debuggerUI.clearButton.tooltip "Clear the collected traces">

<!-- LOCALIZATION NOTE (debuggerUI.pauseExceptions): This is the label for the
  -  checkbox that toggles pausing on exceptions. -->
<!ENTITY debuggerUI.pauseExceptions           "Pause on Exceptions">
<!ENTITY debuggerUI.pauseExceptions.accesskey "E">

<!-- LOCALIZATION NOTE (debuggerUI.ignoreCaughtExceptions): This is the label for the
  -  checkbox that toggles ignoring caught exceptions. -->
<!ENTITY debuggerUI.ignoreCaughtExceptions           "Ignore Caught Exceptions">
<!ENTITY debuggerUI.ignoreCaughtExceptions.accesskey "C">

<!-- LOCALIZATION NOTE (debuggerUI.showPanesOnInit): This is the label for the
  -  checkbox that toggles visibility of panes when opening the debugger. -->
<!ENTITY debuggerUI.showPanesOnInit           "Show Panes on Startup">
<!ENTITY debuggerUI.showPanesOnInit.accesskey "S">

<!-- LOCALIZATION NOTE (debuggerUI.showVarsFilter): This is the label for the
  -  checkbox that toggles visibility of a designated variables filter box. -->
<!ENTITY debuggerUI.showVarsFilter           "Show Variables Filter Box">
<!ENTITY debuggerUI.showVarsFilter.accesskey "V">

<!-- LOCALIZATION NOTE (debuggerUI.showOnlyEnum): This is the label for the
  -  checkbox that toggles visibility of hidden (non-enumerable) variables and
  -  properties in stack views. The "enumerable" flag is a state of a property
  -  defined in JavaScript. When in doubt, leave untranslated. -->
<!ENTITY debuggerUI.showOnlyEnum           "Show Only Enumerable Properties">
<!ENTITY debuggerUI.showOnlyEnum.accesskey "P">

<!-- LOCALIZATION NOTE (debuggerUI.showOriginalSource): This is the label for
  -  the checkbox that toggles the display of original or sourcemap-derived
  -  sources. -->
<!ENTITY debuggerUI.showOriginalSource           "Show Original Sources">
<!ENTITY debuggerUI.showOriginalSource.accesskey "O">

<!-- LOCALIZATION NOTE (debuggerUI.autoBlackBox): This is the label for
  -  the checkbox that toggles whether sources that we suspect are minified are
  -  automatically black boxed or not. -->
<!ENTITY debuggerUI.autoBlackBox           "Automatically Black Box Minified Sources">
<!ENTITY debuggerUI.autoBlackBox.accesskey "B">

<!-- LOCALIZATION NOTE (debuggerUI.searchPanelOperators): This is the text that
  -  appears in the filter panel popup as a header for the operators part. -->
<!ENTITY debuggerUI.searchPanelOperators    "Operators:">

<!-- LOCALIZATION NOTE (debuggerUI.searchFile): This is the text that appears
  -  in the source editor's context menu for the scripts search operation. -->
<!ENTITY debuggerUI.searchFile           "Filter Scripts">
<!ENTITY debuggerUI.searchFile.key       "P">
<!ENTITY debuggerUI.searchFile.altkey    "O">
<!ENTITY debuggerUI.searchFile.accesskey "P">

<!-- LOCALIZATION NOTE (debuggerUI.searchGlobal): This is the text that appears
  -  in the source editor's context menu for the global search operation. -->
<!ENTITY debuggerUI.searchGlobal           "Search in All Files">
<!ENTITY debuggerUI.searchGlobal.key       "F">
<!ENTITY debuggerUI.searchGlobal.accesskey "F">

<!-- LOCALIZATION NOTE (debuggerUI.searchFunction): This is the text that appears
  -  in the source editor's context menu for the function search operation. -->
<!ENTITY debuggerUI.searchFunction           "Search for Function Definition">
<!ENTITY debuggerUI.searchFunction.key       "D">
<!ENTITY debuggerUI.searchFunction.accesskey "D">

<!-- LOCALIZATION NOTE (debuggerUI.searchToken): This is the text that appears
  -  in the source editor's context menu for the token search operation. -->
<!ENTITY debuggerUI.searchToken           "Find">
<!ENTITY debuggerUI.searchToken.key       "F">
<!ENTITY debuggerUI.searchToken.accesskey "F">

<!-- LOCALIZATION NOTE (debuggerUI.searchGoToLine): This is the text that appears
  -  in the source editor's context menu for the line search operation. -->
<!ENTITY debuggerUI.searchGoToLine           "Go to Line…">
<!ENTITY debuggerUI.searchGoToLine.key       "L">
<!ENTITY debuggerUI.searchGoToLine.accesskey "L">

<!-- LOCALIZATION NOTE (debuggerUI.searchVariable): This is the text that appears
  -  in the source editor's context menu for the variables search operation. -->
<!ENTITY debuggerUI.searchVariable           "Filter Variables">
<!ENTITY debuggerUI.searchVariable.key       "V">
<!ENTITY debuggerUI.searchVariable.accesskey "V">

<!-- LOCALIZATION NOTE (debuggerUI.focusVariables): This is the text that appears
  -  in the source editor's context menu for the variables focus operation. -->
<!ENTITY debuggerUI.focusVariables           "Focus Variables Tree">
<!ENTITY debuggerUI.focusVariables.key       "V">
<!ENTITY debuggerUI.focusVariables.accesskey "V">

<!-- LOCALIZATION NOTE (debuggerUI.condBreakPanelTitle): This is the text that
  -  appears in the conditional breakpoint panel popup as a description. -->
<!ENTITY debuggerUI.condBreakPanelTitle "This breakpoint will stop execution only if the following expression is true">

<!-- LOCALIZATION NOTE (debuggerUI.seMenuBreak): This is the text that
  -  appears in the source editor context menu for adding a breakpoint. -->
<!ENTITY debuggerUI.seMenuBreak     "Add Breakpoint">
<!ENTITY debuggerUI.seMenuBreak.key "B">

<!-- LOCALIZATION NOTE (debuggerUI.seMenuCondBreak): This is the text that
  -  appears in the source editor context menu for adding a conditional
  -  breakpoint. -->
<!ENTITY debuggerUI.seMenuCondBreak     "Add Conditional Breakpoint">
<!ENTITY debuggerUI.seMenuCondBreak.key "B">

<!-- LOCALIZATION NOTE (debuggerUI.seEditMenuCondBreak): This is the text that
  -  appears in the source editor context menu for editing a breakpoint. -->
<!ENTITY debuggerUI.seEditMenuCondBreak     "Edit Conditional Breakpoint">
<!ENTITY debuggerUI.seEditMenuCondBreak.key "B">

<!-- LOCALIZATION NOTE (debuggerUI.tabs.*): This is the text that
  -  appears in the debugger's side pane tabs. -->
<!ENTITY debuggerUI.tabs.workers        "Workers">
<!ENTITY debuggerUI.tabs.sources        "Sources">
<!ENTITY debuggerUI.tabs.traces         "Traces">
<!ENTITY debuggerUI.tabs.callstack      "Call Stack">
<!ENTITY debuggerUI.tabs.variables      "Variables">
<!ENTITY debuggerUI.tabs.events         "Events">

<!-- LOCALIZATION NOTE (debuggerUI.seMenuAddWatch): This is the text that
  -  appears in the source editor context menu for adding an expression. -->
<!ENTITY debuggerUI.seMenuAddWatch      "Selection to Watch Expression">
<!ENTITY debuggerUI.seMenuAddWatch.key  "E">

<!-- LOCALIZATION NOTE (debuggerUI.addWatch): This is the text that
  -  appears in the watch expressions context menu for adding an expression. -->
<!ENTITY debuggerUI.addWatch            "Add Watch Expression">
<!ENTITY debuggerUI.addWatch.accesskey  "E">

<!-- LOCALIZATION NOTE (debuggerUI.removeAllWatch): This is the text that
  -  appears in the watch expressions context menu for removing all expressions. -->
<!ENTITY debuggerUI.removeAllWatch           "Remove All Watch Expressions">
<!ENTITY debuggerUI.removeAllWatch.key       "E">
<!ENTITY debuggerUI.removeAllWatch.accesskey "E">

<!-- LOCALIZATION NOTE (debuggerUI.stepping): These are the keycodes that
  -  control the stepping commands in the debugger (continue, step over,
  -  step in and step out). -->
<!ENTITY debuggerUI.stepping.resume1    "VK_F8">
<!ENTITY debuggerUI.stepping.stepOver1  "VK_F10">
<!ENTITY debuggerUI.stepping.stepIn1    "VK_F11">
<!ENTITY debuggerUI.stepping.stepOut1   "VK_F11">

<!-- LOCALIZATION NOTE (debuggerUI.context.newTab):  This is the label
  -  for the Open in New Tab menu item displayed in the context menu of the
  -  debugger sources side menu. This should be the same as
  -  netmonitorUI.context.newTab  -->
<!ENTITY debuggerUI.context.newTab           "Open in New Tab">
<!ENTITY debuggerUI.context.newTab.accesskey "O">

<!-- LOCALIZATION NOTE (debuggerUI.context.copyUrl): This is the label displayed
  -  on the context menu that copies the selected request's url. This should be
  -  the same as netmonitorUI.context.copyUrl -->
<!ENTITY debuggerUI.context.copyUrl           "Copy URL">
<!ENTITY debuggerUI.context.copyUrl.accesskey "C">
<!ENTITY debuggerUI.context.copyUrl.key "C">
PK
!<.��mM�M�=chrome/en-US/locale/en-US/devtools/client/debugger.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE These strings are used inside the Debugger
# which is available from the Web Developer sub-menu -> 'Debugger'.
# The correct localization of this file might be to keep it in
# English, or another language commonly spoken among web developers.
# You want to make that choice consistent across the developer tools.
# A good criteria is the language in which you'd find the best
# documentation on web development on the web.

# LOCALIZATION NOTE (collapsePanes): This is the tooltip for the button
# that collapses the left and right panes in the debugger UI.
collapsePanes=Collapse panes

# LOCALIZATION NOTE (copySource): This is the text that appears in the
# context menu to copy the selected source of file open.
copySource=Copy
copySource.accesskey=y

# LOCALIZATION NOTE (copySourceUri2): This is the text that appears in the
# context menu to copy the source URI of file open.
copySourceUri2=Copy source URI
copySourceUri2.accesskey=u

# LOCALIZATION NOTE (setDirectoryRoot.label): This is the text that appears in the
# context menu to set a directory as root directory
setDirectoryRoot.label=Set directory root
setDirectoryRoot.accesskey=r

# LOCALIZATION NOTE (removeDirectoryRoot.label): This is the text that appears in the
# context menu to remove a directory as root directory
removeDirectoryRoot.label=Remove directory root
removeDirectoryRoot.accesskey=d

# LOCALIZATION NOTE (copyFunction.label): This is the text that appears in the
# context menu to copy the function the user selected
copyFunction.label=Copy function
copyFunction.accesskey=F

# LOCALIZATION NOTE (copyStackTrace): This is the text that appears in the
# context menu to copy the stack trace methods, file names and row number.
copyStackTrace=Copy stack trace
copyStackTrace.accesskey=c

# LOCALIZATION NOTE (expandPanes): This is the tooltip for the button
# that expands the left and right panes in the debugger UI.
expandPanes=Expand panes

# LOCALIZATION NOTE (evaluateInConsole.label): Editor right-click menu item
# to execute selected text in browser console.
evaluateInConsole.label=Evaluate in console

# LOCALIZATION NOTE (pauseButtonTooltip): The tooltip that is displayed for the pause
# button when the debugger is in a running state.
pauseButtonTooltip=Pause %S

# LOCALIZATION NOTE (pausePendingButtonTooltip): The tooltip that is displayed for
# the pause button after it's been clicked but before the next JavaScript to run.
pausePendingButtonTooltip=Waiting for next execution

# LOCALIZATION NOTE (resumeButtonTooltip): The label that is displayed on the pause
# button when the debugger is in a paused state.
resumeButtonTooltip=Resume %S

# LOCALIZATION NOTE (stepOverTooltip): The label that is displayed on the
# button that steps over a function call.
stepOverTooltip=Step over %S

# LOCALIZATION NOTE (stepInTooltip): The label that is displayed on the
# button that steps into a function call.
stepInTooltip=Step in %S

# LOCALIZATION NOTE (stepOutTooltip): The label that is displayed on the
# button that steps out of a function call.
stepOutTooltip=Step out %S

# LOCALIZATION NOTE (pauseButtonItem): The label that is displayed for the dropdown pause
# list item when the debugger is in a running state.
pauseButtonItem=Pause on Next Statement

# LOCALIZATION NOTE (ignoreExceptionsItem): The pause on exceptions button description
# when the debugger will not pause on exceptions.
ignoreExceptionsItem=Ignore exceptions

# LOCALIZATION NOTE (pauseOnUncaughtExceptionsItem): The pause on exceptions dropdown
# item shown when a user is adding a new breakpoint.
pauseOnUncaughtExceptionsItem=Pause on uncaught exceptions

# LOCALIZATION NOTE (pauseOnExceptionsItem): The pause on exceptions button description
# when the debugger will pause on all exceptions.
pauseOnExceptionsItem=Pause on all exceptions

# LOCALIZATION NOTE (workersHeader): The text to display in the events
# header.
workersHeader=Workers

# LOCALIZATION NOTE (noWorkersText): The text to display in the workers list
# when there are no workers.
noWorkersText=This page has no workers.

# LOCALIZATION NOTE (noSourcesText): The text to display in the sources list
# when there are no sources.
noSourcesText=This page has no sources.

# LOCALIZATION NOTE (noEventListenersText): The text to display in the events tab
# when there are no events.
noEventListenersText=No event listeners to display.

# LOCALIZATION NOTE (eventListenersHeader): The text to display in the events
# header.
eventListenersHeader=Event listeners

# LOCALIZATION NOTE (noStackFramesText): The text to display in the call stack tab
# when there are no stack frames.
noStackFramesText=No stack frames to display

# LOCALIZATION NOTE (eventCheckboxTooltip): The tooltip text to display when
# the user hovers over the checkbox used to toggle an event breakpoint.
eventCheckboxTooltip=Toggle breaking on this event

# LOCALIZATION NOTE (eventOnSelector): The text to display in the events tab
# for every event item, between the event type and event selector.
eventOnSelector=on

# LOCALIZATION NOTE (eventInSource): The text to display in the events tab
# for every event item, between the event selector and listener's owner source.
eventInSource=in

# LOCALIZATION NOTE (eventNodes): The text to display in the events tab when
# an event is listened on more than one target node.
eventNodes=%S nodes

# LOCALIZATION NOTE (eventNative): The text to display in the events tab when
# a listener is added from plugins, thus getting translated to native code.
eventNative=[native code]

# LOCALIZATION NOTE (*Events): The text to display in the events tab for
# each group of sub-level event entries.
animationEvents=Animation
audioEvents=Audio
batteryEvents=Battery
clipboardEvents=Clipboard
compositionEvents=Composition
deviceEvents=Device
displayEvents=Display
dragAndDropEvents=Drag and Drop
gamepadEvents=Gamepad
indexedDBEvents=IndexedDB
interactionEvents=Interaction
keyboardEvents=Keyboard
mediaEvents=HTML5 Media
mouseEvents=Mouse
mutationEvents=Mutation
navigationEvents=Navigation
pointerLockEvents=Pointer Lock
sensorEvents=Sensor
storageEvents=Storage
timeEvents=Time
touchEvents=Touch
otherEvents=Other

# LOCALIZATION NOTE (blackboxCheckboxTooltip2): The tooltip text to display when
# the user hovers over the checkbox used to toggle blackboxing its associated
# source.
blackboxCheckboxTooltip2=Toggle blackboxing

# LOCALIZATION NOTE (sources.search.key2): Key shortcut to open the search for
# searching all the source files the debugger has seen.
# Do not localize "CmdOrCtrl+P", or change the format of the string. These are
# key identifiers, not messages displayed to the user.
sources.search.key2=CmdOrCtrl+P

# LOCALIZATION NOTE (sources.search.alt.key): A second key shortcut to open the
# search for searching all the source files the debugger has seen.
# Do not localize "CmdOrCtrl+O", or change the format of the string. These are
# key identifiers, not messages displayed to the user.
sources.search.alt.key=CmdOrCtrl+O

# LOCALIZATION NOTE (projectTextSearch.key): A key shortcut to open the
# full project text search for searching all the files the debugger has seen.
# Do not localize "CmdOrCtrl+Shift+F", or change the format of the string. These are
# key identifiers, not messages displayed to the user.
projectTextSearch.key=CmdOrCtrl+Shift+F

# LOCALIZATION NOTE (functionSearch.key): A key shortcut to open the
# modal for searching functions in a file.
# Do not localize "CmdOrCtrl+Shift+O", or change the format of the string. These are
# key identifiers, not messages displayed to the user.
functionSearch.key=CmdOrCtrl+Shift+O

# LOCALIZATION NOTE (toggleBreakpoint.key): A key shortcut to toggle
# breakpoints.
# Do not localize "CmdOrCtrl+B", or change the format of the string. These are
# key identifiers, not messages displayed to the user.
toggleBreakpoint.key=CmdOrCtrl+B

# LOCALIZATION NOTE (toggleCondPanel.key): A key shortcut to toggle
# the conditional breakpoint panel.
# Do not localize "CmdOrCtrl+Shift+B", or change the format of the string. These are
# key identifiers, not messages displayed to the user.
toggleCondPanel.key=CmdOrCtrl+Shift+B

# LOCALIZATION NOTE (stepOut.key): A key shortcut to
# step out.
stepOut.key=Shift+F11

# LOCALIZATION NOTE (shortcuts.header.editor): Sections header in
# the shortcuts modal for keyboard shortcuts related to editing.
shortcuts.header.editor=Editor

# LOCALIZATION NOTE (shortcuts.header.stepping): Sections header in
# the shortcuts modal for keyboard shortcuts related to stepping.
shortcuts.header.stepping=Stepping

# LOCALIZATION NOTE (shortcuts.header.search): Sections header in
# the shortcuts modal for keyboard shortcuts related to search.
shortcuts.header.search=Search

# LOCALIZATION NOTE (projectTextSearch.placeholder): A placeholder shown
# when searching across all of the files in a project.
projectTextSearch.placeholder=Find in files…

# LOCALIZATION NOTE (projectTextSearch.noResults): The center pane Text Search
# message when the query did not match any text of all files in a project.
projectTextSearch.noResults=No results found

# LOCALIZATION NOTE (sources.noSourcesAvailable): Text shown when the debugger
# does not have any sources.
sources.noSourcesAvailable=This page has no sources

# LOCALIZATION NOTE (sourceSearch.search.key2): Key shortcut to open the search
# for searching within a the currently opened files in the editor
# Do not localize "CmdOrCtrl+F", or change the format of the string. These are
# key identifiers, not messages displayed to the user.
sourceSearch.search.key2=CmdOrCtrl+F

# LOCALIZATION NOTE (sourceSearch.search.placeholder): placeholder text in
# the source search input bar
sourceSearch.search.placeholder=Search in file…

# LOCALIZATION NOTE (sourceSearch.search.again.key2): Key shortcut to highlight
# the next occurrence of the last search triggered from a source search
# Do not localize "CmdOrCtrl+G", or change the format of the string. These are
# key identifiers, not messages displayed to the user.
sourceSearch.search.again.key2=CmdOrCtrl+G

# LOCALIZATION NOTE (sourceSearch.search.againPrev.key2): Key shortcut to highlight
# the previous occurrence of the last search triggered from a source search
# Do not localize "CmdOrCtrl+Shift+G", or change the format of the string. These are
# key identifiers, not messages displayed to the user.
sourceSearch.search.againPrev.key2=CmdOrCtrl+Shift+G

# LOCALIZATION NOTE (sourceSearch.resultsSummary1): Shows a summary of
# the number of matches for autocomplete
sourceSearch.resultsSummary1=%d results

# LOCALIZATION NOTE (noMatchingStringsText): The text to display in the
# global search results when there are no matching strings after filtering.
noMatchingStringsText=No matches found

# LOCALIZATION NOTE (emptySearchText): This is the text that appears in the
# filter text box when it is empty and the scripts container is selected.
emptySearchText=Search scripts (%S)

# LOCALIZATION NOTE (emptyVariablesFilterText): This is the text that
# appears in the filter text box for the variables view container.
emptyVariablesFilterText=Filter variables

# LOCALIZATION NOTE (emptyPropertiesFilterText): This is the text that
# appears in the filter text box for the editor's variables view bubble.
emptyPropertiesFilterText=Filter properties

# LOCALIZATION NOTE (searchPanelFilter): This is the text that appears in the
# filter panel popup for the filter scripts operation.
searchPanelFilter=Filter scripts (%S)

# LOCALIZATION NOTE (searchPanelGlobal): This is the text that appears in the
# filter panel popup for the global search operation.
searchPanelGlobal=Search in all files (%S)

# LOCALIZATION NOTE (searchPanelFunction): This is the text that appears in the
# filter panel popup for the function search operation.
searchPanelFunction=Search for function definition (%S)

# LOCALIZATION NOTE (searchPanelToken): This is the text that appears in the
# filter panel popup for the token search operation.
searchPanelToken=Find in this file (%S)

# LOCALIZATION NOTE (searchPanelGoToLine): This is the text that appears in the
# filter panel popup for the line search operation.
searchPanelGoToLine=Go to line (%S)

# LOCALIZATION NOTE (searchPanelVariable): This is the text that appears in the
# filter panel popup for the variables search operation.
searchPanelVariable=Filter variables (%S)

# LOCALIZATION NOTE (breakpointMenuItem): The text for all the elements that
# are displayed in the breakpoints menu item popup.
breakpointMenuItem.setConditional=Configure conditional breakpoint
breakpointMenuItem.enableSelf2.label=Enable
breakpointMenuItem.enableSelf2.accesskey=E
breakpointMenuItem.disableSelf2.label=Disable
breakpointMenuItem.disableSelf2.accesskey=D
breakpointMenuItem.deleteSelf2.label=Remove
breakpointMenuItem.deleteSelf2.accesskey=R
breakpointMenuItem.enableOthers2.label=Enable others
breakpointMenuItem.enableOthers2.accesskey=o
breakpointMenuItem.disableOthers2.label=Disable others
breakpointMenuItem.disableOthers2.accesskey=s
breakpointMenuItem.deleteOthers2.label=Remove others
breakpointMenuItem.deleteOthers2.accesskey=h
breakpointMenuItem.enableAll2.label=Enable all
breakpointMenuItem.enableAll2.accesskey=b
breakpointMenuItem.disableAll2.label=Disable all
breakpointMenuItem.disableAll2.accesskey=k
breakpointMenuItem.deleteAll2.label=Remove all
breakpointMenuItem.deleteAll2.accesskey=a
breakpointMenuItem.removeCondition2.label=Remove condition
breakpointMenuItem.removeCondition2.accesskey=c
breakpointMenuItem.addCondition2.label=Add condition
breakpointMenuItem.addCondition2.accesskey=A
breakpointMenuItem.editCondition2.label=Edit condition
breakpointMenuItem.editCondition2.accesskey=n
breakpointMenuItem.enableSelf=Enable breakpoint
breakpointMenuItem.enableSelf.accesskey=E
breakpointMenuItem.disableSelf=Disable breakpoint
breakpointMenuItem.disableSelf.accesskey=D
breakpointMenuItem.deleteSelf=Remove breakpoint
breakpointMenuItem.deleteSelf.accesskey=R
breakpointMenuItem.enableOthers=Enable others
breakpointMenuItem.enableOthers.accesskey=o
breakpointMenuItem.disableOthers=Disable others
breakpointMenuItem.disableOthers.accesskey=s
breakpointMenuItem.deleteOthers=Remove others
breakpointMenuItem.deleteOthers.accesskey=h
breakpointMenuItem.enableAll=Enable all breakpoints
breakpointMenuItem.enableAll.accesskey=b
breakpointMenuItem.disableAll=Disable all breakpoints
breakpointMenuItem.disableAll.accesskey=k
breakpointMenuItem.deleteAll=Remove all breakpoints
breakpointMenuItem.deleteAll.accesskey=a
breakpointMenuItem.removeCondition.label=Remove breakpoint condition
breakpointMenuItem.removeCondition.accesskey=c
breakpointMenuItem.editCondition.label=Edit breakpoint condition
breakpointMenuItem.editCondition.accesskey=n

# LOCALIZATION NOTE (breakpoints.header): Breakpoints right sidebar pane header.
breakpoints.header=Breakpoints

# LOCALIZATION NOTE (breakpoints.none): The text that appears when there are
# no breakpoints present
breakpoints.none=No breakpoints

# LOCALIZATION NOTE (breakpoints.enable): The text that may appear as a tooltip
# when hovering over the 'disable breakpoints' switch button in right sidebar
breakpoints.enable=Enable breakpoints

# LOCALIZATION NOTE (breakpoints.disable): The text that may appear as a tooltip
# when hovering over the 'disable breakpoints' switch button in right sidebar
breakpoints.disable=Disable breakpoints

# LOCALIZATION NOTE (breakpoints.removeBreakpointTooltip): The tooltip that is displayed
# for remove breakpoint button in right sidebar
breakpoints.removeBreakpointTooltip=Remove breakpoint

# LOCALIZATION NOTE (callStack.header): Call Stack right sidebar pane header.
callStack.header=Call stack

# LOCALIZATION NOTE (callStack.notPaused): Call Stack right sidebar pane
# message when not paused.
callStack.notPaused=Not paused

# LOCALIZATION NOTE (callStack.collapse): Call Stack right sidebar pane
# message to hide some of the frames that are shown.
callStack.collapse=Collapse rows

# LOCALIZATION NOTE (callStack.expand): Call Stack right sidebar pane
# message to show more of the frames.
callStack.expand=Expand rows

# LOCALIZATION NOTE (editor.searchResults): Editor Search bar message
# for the summarizing the selected search result. e.g. 5 of 10 results.
editor.searchResults=%d of %d results

# LOCALIZATION NOTE (editor.singleResult): Copy shown when there is one result.
editor.singleResult=1 result

# LOCALIZATION NOTE (editor.noResults): Editor Search bar message
# for when no results found.
editor.noResults=No results

# LOCALIZATION NOTE (editor.searchResults.nextResult): Editor Search bar
# tooltip for traversing to the Next Result
editor.searchResults.nextResult=Next result

# LOCALIZATION NOTE (editor.searchResults.prevResult): Editor Search bar
# tooltip for traversing to the Previous Result
editor.searchResults.prevResult=Previous result

# LOCALIZATION NOTE (editor.searchTypeToggleTitle): Search bar title for
# toggling search type buttons(function search, variable search)
editor.searchTypeToggleTitle=Search for:

# LOCALIZATION NOTE (editor.continueToHere.label): Editor gutter context
# menu item for jumping to a new paused location
editor.continueToHere.label=Continue to here
editor.continueToHere.accesskey=H

# LOCALIZATION NOTE (editor.addBreakpoint): Editor gutter context menu item
# for adding a breakpoint on a line.
editor.addBreakpoint=Add breakpoint

# LOCALIZATION NOTE (editor.disableBreakpoint): Editor gutter context menu item
# for disabling a breakpoint on a line.
editor.disableBreakpoint=Disable breakpoint
editor.disableBreakpoint.accesskey=D

# LOCALIZATION NOTE (editor.enableBreakpoint): Editor gutter context menu item
# for enabling a breakpoint on a line.
editor.enableBreakpoint=Enable breakpoint

# LOCALIZATION NOTE (editor.removeBreakpoint): Editor gutter context menu item
# for removing a breakpoint on a line.
editor.removeBreakpoint=Remove breakpoint

# LOCALIZATION NOTE (editor.editBreakpoint): Editor gutter context menu item
# for setting a breakpoint condition on a line.
editor.editBreakpoint=Edit breakpoint

# LOCALIZATION NOTE (editor.addConditionalBreakpoint): Editor gutter context
# menu item for adding a breakpoint condition on a line.
editor.addConditionalBreakpoint=Add conditional breakpoint
editor.addConditionalBreakpoint.accesskey=c

# LOCALIZATION NOTE (editor.conditionalPanel.placeholder): Placeholder text for
# input element inside ConditionalPanel component
editor.conditionalPanel.placeholder=This breakpoint will pause when the expression is true

# LOCALIZATION NOTE (editor.conditionalPanel.close): Tooltip text for
# close button inside ConditionalPanel component
editor.conditionalPanel.close=Cancel edit breakpoint and close

# LOCALIZATION NOTE (editor.jumpToMappedLocation1): Context menu item
# for navigating to a source mapped location
editor.jumpToMappedLocation1=Jump to %S location
editor.jumpToMappedLocation1.accesskey=m

# LOCALIZATION NOTE (framework.disableGrouping): This is the text that appears in the
# context menu to disable framework grouping.
framework.disableGrouping=Disable framework grouping
framework.disableGrouping.accesskey=u

# LOCALIZATION NOTE (framework.enableGrouping): This is the text that appears in the
# context menu to enable framework grouping.
framework.enableGrouping=Enable framework grouping
framework.enableGrouping.accesskey=u

# LOCALIZATION NOTE (generated): Source Map term for a server source location
generated=generated

# LOCALIZATION NOTE (original): Source Map term for a debugger UI source location
original=original

# LOCALIZATION NOTE (expressions.placeholder): Placeholder text for expression
# input element
expressions.placeholder=Add watch expression
# LOCALIZATION NOTE (expressions.errorMsg): Error text for expression
# input element
expressions.errorMsg=Invalid expression…
expressions.label=Add watch expression
expressions.accesskey=e

# LOCALIZATION NOTE (sourceTabs.closeTab): Editor source tab context menu item
# for closing the selected tab below the mouse.
sourceTabs.closeTab=Close tab
sourceTabs.closeTab.accesskey=c

# LOCALIZATION NOTE (sourceTabs.closeOtherTabs): Editor source tab context menu item
# for closing the other tabs.
sourceTabs.closeOtherTabs=Close other tabs
sourceTabs.closeOtherTabs.accesskey=o

# LOCALIZATION NOTE (sourceTabs.closeTabsToEnd): Editor source tab context menu item
# for closing the tabs to the end (the right for LTR languages) of the selected tab.
sourceTabs.closeTabsToEnd=Close tabs to the right
sourceTabs.closeTabsToEnd.accesskey=e

# LOCALIZATION NOTE (sourceTabs.closeAllTabs): Editor source tab context menu item
# for closing all tabs.
sourceTabs.closeAllTabs=Close all tabs
sourceTabs.closeAllTabs.accesskey=a

# LOCALIZATION NOTE (sourceTabs.revealInTree): Editor source tab context menu item
# for revealing source in tree.
sourceTabs.revealInTree=Reveal in tree
sourceTabs.revealInTree.accesskey=r

# LOCALIZATION NOTE (sourceTabs.prettyPrint): Editor source tab context menu item
# for pretty printing the source.
sourceTabs.prettyPrint=Pretty print source
sourceTabs.prettyPrint.accesskey=p

# LOCALIZATION NOTE (sourceFooter.blackbox): Tooltip text associated
# with the blackbox button
sourceFooter.blackbox=Blackbox source
sourceFooter.blackbox.accesskey=B

# LOCALIZATION NOTE (sourceFooter.unblackbox): Tooltip text associated
# with the blackbox button
sourceFooter.unblackbox=Unblackbox source
sourceFooter.unblackbox.accesskey=b

# LOCALIZATION NOTE (sourceFooter.blackboxed): Text associated
# with a blackboxed source
sourceFooter.blackboxed=Blackboxed source

# LOCALIZATION NOTE (sourceFooter.mappedSource): Text associated
# with a mapped source. %S is replaced by the source map origin.
sourceFooter.mappedSource=(From %S)

# LOCALIZATION NOTE (sourceFooter.mappedSourceTooltip): Tooltip text associated
# with a mapped source. %S is replaced by the source map origin.
sourceFooter.mappedSourceTooltip=(Source mapped from %S)

# LOCALIZATION NOTE (sourceFooter.codeCoverage): Text associated
# with a code coverage button
sourceFooter.codeCoverage=Code coverage

# LOCALIZATION NOTE (sourceTabs.closeTabButtonTooltip): The tooltip that is displayed
# for close tab button in source tabs.
sourceTabs.closeTabButtonTooltip=Close tab

# LOCALIZATION NOTE (scopes.header): Scopes right sidebar pane header.
scopes.header=Scopes

# LOCALIZATION NOTE (scopes.notAvailable): Scopes right sidebar pane message
# for when the debugger is paused, but there isn't pause data.
scopes.notAvailable=Scopes unavailable

# LOCALIZATION NOTE (scopes.notPaused): Scopes right sidebar pane message
# for when the debugger is not paused.
scopes.notPaused=Not paused

# LOCALIZATION NOTE (scopes.block): Refers to a block of code in
# the scopes pane when the debugger is paused.
scopes.block=Block

# LOCALIZATION NOTE (sources.header): Sources left sidebar header
sources.header=Sources

# LOCALIZATION NOTE (outline.header): Outline left sidebar header
outline.header=Outline

# LOCALIZATION NOTE (outline.noFunctions): Outline text when there are no functions to display
outline.noFunctions=No functions

# LOCALIZATION NOTE (sources.search): Sources left sidebar prompt
# e.g. Cmd+P to search. On a mac, we use the command unicode character.
# On windows, it's ctrl.
sources.search=%S to search

# LOCALIZATION NOTE (watchExpressions.header): Watch Expressions right sidebar
# pane header.
watchExpressions.header=Watch expressions

# LOCALIZATION NOTE (watchExpressions.refreshButton): Watch Expressions header
# button for refreshing the expressions.
watchExpressions.refreshButton=Refresh

# LOCALIZATION NOTE (welcome.search): The center pane welcome panel's
# search prompt. e.g. cmd+p to search for files. On windows, it's ctrl, on
# a mac we use the unicode character.
welcome.search=%S to search for sources

# LOCALIZATION NOTE (welcome.findInFiles): The center pane welcome panel's
# search prompt. e.g. cmd+f to search for files. On windows, it's ctrl+shift+f, on
# a mac we use the unicode character.
welcome.findInFiles=%S to find in files

# LOCALIZATION NOTE (welcome.searchFunction): Label displayed in the welcome
# panel. %S is replaced by the keyboard shortcut to search for functions.
welcome.searchFunction=%S to search for functions in file

# LOCALIZATION NOTE (sourceSearch.search): The center pane Source Search
# prompt for searching for files.
sourceSearch.search=Search sources…

# LOCALIZATION NOTE (sourceSearch.noResults2): The center pane Source Search
# message when the query did not match any of the sources.
sourceSearch.noResults2=No results found

# LOCALIZATION NOTE (ignoreExceptions): The pause on exceptions button tooltip
# when the debugger will not pause on exceptions.
ignoreExceptions=Ignore exceptions. Click to pause on uncaught exceptions

# LOCALIZATION NOTE (pauseOnUncaughtExceptions): The pause on exceptions button
# tooltip when the debugger will pause on uncaught exceptions.
pauseOnUncaughtExceptions=Pause on uncaught exceptions. Click to pause on all exceptions

# LOCALIZATION NOTE (pauseOnExceptions): The pause on exceptions button tooltip
# when the debugger will pause on all exceptions.
pauseOnExceptions=Pause on all exceptions. Click to ignore exceptions

# LOCALIZATION NOTE (replayPrevious): The replay previous button tooltip
# when the debugger will go back in stepping history.
replayPrevious=Go back one step in history

# LOCALIZATION NOTE (replayNext): The replay next button tooltip
# when the debugger will go forward in stepping history.
replayNext=Go forward one step in history

# LOCALIZATION NOTE (loadingText): The text that is displayed in the script
# editor when the loading process has started but there is no file to display
# yet.
loadingText=Loading\u2026

# LOCALIZATION NOTE (wasmIsNotAvailable): The text that is displayed in the
# script editor when the WebAssembly source is not available.
wasmIsNotAvailable=Please refresh to debug this module

# LOCALIZATION NOTE (errorLoadingText3): The text that is displayed in the debugger
# viewer when there is an error loading a file
errorLoadingText3=Error loading this URI: %S

# LOCALIZATION NOTE (addWatchExpressionText): The text that is displayed in the
# watch expressions list to add a new item.
addWatchExpressionText=Add watch expression

# LOCALIZATION NOTE (addWatchExpressionButton): The button that is displayed in the
# variables view popup.
addWatchExpressionButton=Watch

# LOCALIZATION NOTE (emptyVariablesText): The text that is displayed in the
# variables pane when there are no variables to display.
emptyVariablesText=No variables to display

# LOCALIZATION NOTE (scopeLabel): The text that is displayed in the variables
# pane as a header for each variable scope (e.g. "Global scope, "With scope",
# etc.).
scopeLabel=%S scope

# LOCALIZATION NOTE (watchExpressionsScopeLabel): The name of the watch
# expressions scope. This text is displayed in the variables pane as a header for
# the watch expressions scope.
watchExpressionsScopeLabel=Watch expressions

# LOCALIZATION NOTE (globalScopeLabel): The name of the global scope. This text
# is added to scopeLabel and displayed in the variables pane as a header for
# the global scope.
globalScopeLabel=Global

# LOCALIZATION NOTE (variablesViewErrorStacktrace): This is the text that is
# shown before the stack trace in an error.
variablesViewErrorStacktrace=Stack trace:

# LOCALIZATION NOTE (variablesViewMoreObjects): the text that is displayed
# when you have an object preview that does not show all of the elements. At the end of the list
# you see "N more..." in the web console output.
# This is a semi-colon list of plural forms.
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
# #1 number of remaining items in the object
# example: 3 more…
variablesViewMoreObjects=#1 more…;#1 more…

# LOCALIZATION NOTE (variablesEditableNameTooltip): The text that is displayed
# in the variables list on an item with an editable name.
variablesEditableNameTooltip=Double click to edit

# LOCALIZATION NOTE (variablesEditableValueTooltip): The text that is displayed
# in the variables list on an item with an editable value.
variablesEditableValueTooltip=Click to change value

# LOCALIZATION NOTE (variablesCloseButtonTooltip): The text that is displayed
# in the variables list on an item which can be removed.
variablesCloseButtonTooltip=Click to remove

# LOCALIZATION NOTE (variablesEditButtonTooltip): The text that is displayed
# in the variables list on a getter or setter which can be edited.
variablesEditButtonTooltip=Click to set value

# LOCALIZATION NOTE (variablesDomNodeValueTooltip): The text that is displayed
# in a tooltip on the "open in inspector" button in the the variables list for a
# DOMNode item.
variablesDomNodeValueTooltip=Click to select the node in the inspector

# LOCALIZATION NOTE (configurable|...|Tooltip): The text that is displayed
# in the variables list on certain variables or properties as tooltips.
# Expanations of what these represent can be found at the following links:
# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/isExtensible
# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/isFrozen
# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/isSealed
# It's probably best to keep these in English.
configurableTooltip=configurable
enumerableTooltip=enumerable
writableTooltip=writable
frozenTooltip=frozen
sealedTooltip=sealed
extensibleTooltip=extensible
overriddenTooltip=overridden
WebIDLTooltip=WebIDL

# LOCALIZATION NOTE (variablesSeparatorLabel): The text that is displayed
# in the variables list as a separator between the name and value.
variablesSeparatorLabel=:

# LOCALIZATION NOTE (watchExpressionsSeparatorLabel2): The text that is displayed
# in the watch expressions list as a separator between the code and evaluation.
watchExpressionsSeparatorLabel2=\u0020→

# LOCALIZATION NOTE (functionSearchSeparatorLabel): The text that is displayed
# in the functions search panel as a separator between function's inferred name
# and its real name (if available).
functionSearchSeparatorLabel=←

# LOCALIZATION NOTE(gotoLineModal.placeholder): The placeholder
# text displayed when the user searches for specific lines in a file
gotoLineModal.placeholder=Go to line…

# LOCALIZATION NOTE(gotoLineModal.title): The message shown to users
# to open the go to line modal
gotoLineModal.title=Go to a line number in a file

# LOCALIZATION NOTE(gotoLineModal.key2): The shortcut for opening the
# go to line modal
# Do not localize "CmdOrCtrl+;", or change the format of the string. These are
# key identifiers, not messages displayed to the user.
gotoLineModal.key2=CmdOrCtrl+;

# LOCALIZATION NOTE(symbolSearch.search.functionsPlaceholder): The placeholder
# text displayed when the user searches for functions in a file
symbolSearch.search.functionsPlaceholder=Search functions…
symbolSearch.search.functionsPlaceholder.title=Search for a function in a file

# LOCALIZATION NOTE(symbolSearch.search.variablesPlaceholder): The placeholder
# text displayed when the user searches for variables in a file
symbolSearch.search.variablesPlaceholder=Search variables…
symbolSearch.search.variablesPlaceholder.title=Search for a variable in a file

# LOCALIZATION NOTE(symbolSearch.search.key2): The Key Shortcut for
# searching for a function or variable
# Do not localize "CmdOrCtrl+Shift+O", or change the format of the string. These are
# key identifiers, not messages displayed to the user.
symbolSearch.search.key2=CmdOrCtrl+Shift+O

# LOCALIZATION NOTE(symbolSearch.searchModifier.modifiersLabel): A label
# preceding the group of modifiers
symbolSearch.searchModifier.modifiersLabel=Modifiers:

# LOCALIZATION NOTE(symbolSearch.searchModifier.regex): A search option
# when searching text in a file
symbolSearch.searchModifier.regex=Regex

# LOCALIZATION NOTE(symbolSearch.searchModifier.caseSensitive): A search option
# when searching text in a file
symbolSearch.searchModifier.caseSensitive=Case sensitive

# LOCALIZATION NOTE(symbolSearch.searchModifier.wholeWord): A search option
# when searching text in a file
symbolSearch.searchModifier.wholeWord=Whole word

# LOCALIZATION NOTE (resumptionOrderPanelTitle): This is the text that appears
# as a description in the notification panel popup, when multiple debuggers are
# open in separate tabs and the user tries to resume them in the wrong order.
# The substitution parameter is the URL of the last paused window that must be
# resumed first.
resumptionOrderPanelTitle=There are one or more paused debuggers. Please resume the most-recently paused debugger first at: %S

variablesViewOptimizedOut=(optimized away)
variablesViewUninitialized=(uninitialized)
variablesViewMissingArgs=(unavailable)

anonymousSourcesLabel=Anonymous sources

experimental=This is an experimental feature

# LOCALIZATION NOTE (whyPaused.debuggerStatement): The text that is displayed
# in a info block explaining how the debugger is currently paused due to a `debugger`
# statement in the code
whyPaused.debuggerStatement=Paused on debugger statement

# LOCALIZATION NOTE (whyPaused.breakpoint): The text that is displayed
# in a info block explaining how the debugger is currently paused on a breakpoint
whyPaused.breakpoint=Paused on breakpoint

# LOCALIZATION NOTE (whyPaused.exception): The text that is displayed
# in a info block explaining how the debugger is currently paused on an exception
whyPaused.exception=Paused on exception

# LOCALIZATION NOTE (whyPaused.resumeLimit): The text that is displayed
# in a info block explaining how the debugger is currently paused while stepping
# in or out of the stack
whyPaused.resumeLimit=Paused while stepping

# LOCALIZATION NOTE (whyPaused.pauseOnDOMEvents): The text that is displayed
# in a info block explaining how the debugger is currently paused on a
# dom event
whyPaused.pauseOnDOMEvents=Paused on event listener

# LOCALIZATION NOTE (whyPaused.breakpointConditionThrown): The text that is displayed
# in an info block when evaluating a conditional breakpoint throws an error
whyPaused.breakpointConditionThrown=Error with conditional breakpoint

# LOCALIZATION NOTE (whyPaused.xhr): The text that is displayed
# in a info block explaining how the debugger is currently paused on an
# xml http request
whyPaused.xhr=Paused on XMLHttpRequest

# LOCALIZATION NOTE (whyPaused.promiseRejection): The text that is displayed
# in a info block explaining how the debugger is currently paused on a
# promise rejection
whyPaused.promiseRejection=Paused on promise rejection

# LOCALIZATION NOTE (whyPaused.assert): The text that is displayed
# in a info block explaining how the debugger is currently paused on an
# assert
whyPaused.assert=Paused on assertion

# LOCALIZATION NOTE (whyPaused.debugCommand): The text that is displayed
# in a info block explaining how the debugger is currently paused on a
# debugger statement
whyPaused.debugCommand=Paused on debugged function

# LOCALIZATION NOTE (whyPaused.other): The text that is displayed
# in a info block explaining how the debugger is currently paused on an event
# listener breakpoint set
whyPaused.other=Debugger paused

# LOCALIZATION NOTE (ctrl): The text that is used for documenting
# keyboard shortcuts that use the control key
ctrl=Ctrl

# LOCALIZATION NOTE (anonymous): The text that is displayed when the
# display name is null.
anonymous=(anonymous)

# LOCALIZATION NOTE (shortcuts.toggleBreakpoint): text describing
# keyboard shortcut action for toggling breakpoint
shortcuts.toggleBreakpoint=Toggle Breakpoint
shortcuts.toggleBreakpoint.accesskey=B

# LOCALIZATION NOTE (shortcuts.toggleCondPanel): text describing
# keyboard shortcut action for toggling conditional panel keyboard
shortcuts.toggleCondPanel=Toggle Conditional Panel

# LOCALIZATION NOTE (shortcuts.pauseOrResume): text describing
# keyboard shortcut action for pause of resume
shortcuts.pauseOrResume=Pause/Resume

# LOCALIZATION NOTE (shortcuts.stepOver): text describing
# keyboard shortcut action for stepping over
shortcuts.stepOver=Step Over

# LOCALIZATION NOTE (shortcuts.stepIn): text describing
# keyboard shortcut action for stepping in
shortcuts.stepIn=Step In

# LOCALIZATION NOTE (shortcuts.stepOut): text describing
# keyboard shortcut action for stepping out
shortcuts.stepOut=Step Out

# LOCALIZATION NOTE (shortcuts.fileSearch): text describing
# keyboard shortcut action for source file search
shortcuts.fileSearch=Source File Search

# LOCALIZATION NOTE (shortcuts.gotoLine): text describing
# keyboard shortcut for jumping to a specific line
shortcuts.gotoLine=Go to line

# LOCALIZATION NOTE (shortcuts.searchAgain): text describing
# keyboard shortcut action for searching again
shortcuts.searchAgain=Search Again

# LOCALIZATION NOTE (shortcuts.projectSearch): text describing
# keyboard shortcut action for full project search
shortcuts.projectSearch=Full Project Search

# LOCALIZATION NOTE (shortcuts.functionSearch): text describing
# keyboard shortcut action for function search
shortcuts.functionSearch=Function Search

# LOCALIZATION NOTE (shortcuts.buttonName): text describing
# keyboard shortcut button text
shortcuts.buttonName=Keyboard shortcuts
PK
!<+��d��;chrome/en-US/locale/en-US/devtools/client/device.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE These strings are used inside Device Emulation developer
# tools. The correct localization of this file might be to keep it in English,
# or another language commonly spoken among web developers.  You want to make
# that choice consistent across the developer tools.  A good criteria is the
# language in which you'd find the best documentation on web development on the
# web.

# LOCALIZATION NOTE:
# These strings are category names in a list of devices that a user can choose
# to simulate (e.g. "ZTE Open C", "VIA Vixen", "720p HD Television", etc).
device.phones=Phones
device.tablets=Tablets
device.laptops=Laptops
device.televisions=TVs
device.consoles=Gaming consoles
device.watches=Watches
PK
!<�K�kk8chrome/en-US/locale/en-US/devtools/client/dom.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE These strings are used inside the DOM panel
# which is available from the Web Developer sub-menu -> 'DOM'.
# The correct localization of this file might be to keep it in
# English, or another language commonly spoken among web developers.
# You want to make that choice consistent across the developer tools.
# A good criteria is the language in which you'd find the best
# documentation on web development on the web.

# LOCALIZATION NOTE (dom.filterDOMPanel): A placeholder text used for
# DOM panel search box.
dom.filterDOMPanel=Filter DOM Panel

# LOCALIZATION NOTE (dom.refresh): A label for Refresh button in
# DOM panel toolbar
dom.refresh=RefreshPK
!<�~b�

Achrome/en-US/locale/en-US/devtools/client/filterwidget.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE These strings are used in the CSS Filter Editor Widget
# which can be found in a tooltip that appears in the Rule View when clicking
# on a filter swatch displayed next to CSS declarations like 'filter: blur(2px)'.

# LOCALIZATION NOTE (emptyFilterList):
# This string is displayed when filter's list is empty
# (no filter specified / all removed)
emptyFilterList=No filter specified

# LOCALIZATION NOTE (emptyPresetList):
# This string is displayed when preset's list is empty
emptyPresetList=You don’t have any saved presets. \
You can store filter presets by choosing a name and saving them. \
Presets are quickly accessible and you can re-use them with ease.

# LOCALIZATION NOTE (addUsingList):
# This string is displayed under [emptyFilterList] when filter's
# list is empty, guiding user to add a filter using the list below it
addUsingList=Add a filter using the list below

# LOCALIZATION NOTE (dropShadowPlaceholder):
# This string is used as a placeholder for drop-shadow's input
# in the filter list (shown when <input> is empty)
dropShadowPlaceholder=x y radius color

# LOCALIZATION NOTE (dragHandleTooltipText):
# This string is used as a tooltip text (shown on mouse hover) on the
# drag handles of filters which are used to re-order filters
dragHandleTooltipText=Drag up or down to re-order filter

# LOCALIZATION NOTE (labelDragTooltipText):
# This string is used as a tooltip text (shown on mouse hover) on the
# filters' labels which can be dragged left/right to increase/decrease
# the filter's value (like photoshop)
labelDragTooltipText=Drag left or right to decrease or increase the value

# LOCALIZATION NOTE (filterListSelectPlaceholder):
# This string is used as a preview option in the list of possible filters
# <select>
filterListSelectPlaceholder=Select a Filter

# LOCALIZATION NOTE (addNewFilterButton):
# This string is displayed on a button used to add new filters
addNewFilterButton=Add

# LOCALIZATION NOTE (newPresetPlaceholder):
# This string is used as a placeholder in the list of presets which is used to
# save a new preset
newPresetPlaceholder=Preset Name

# LOCALIZATION NOTE (savePresetButton):
# This string is displayed on a button used to save a new preset
savePresetButton=Save

# LOCALIZATION NOTE(presetsToggleButton):
# This string is used in a button which toggles the presets list
presetsToggleButton=Presets
PK
!<��X��Cchrome/en-US/locale/en-US/devtools/client/font-inspector.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE This file contains the Font Inspector strings.
# The Font Inspector is a panel accessible in the Inspector sidebar.

# LOCALIZATION NOTE (fontinspector.system) This label indicates that the font is a local
# system font.
fontinspector.system=system

# LOCALIZATION NOTE (fontinspector.noFontsOnSelectedElement): This label is shown when
# no fonts found on the selected element.
fontinspector.noFontsOnSelectedElement=No fonts were found for the current element.

# LOCALIZATION NOTE (fontinspector.otherFontsInPageHeader): This is the text for the
# header of a collapsible section containing other fonts used in the page.
fontinspector.otherFontsInPageHeader=Other fonts in page

# LOCALIZATION NOTE (fontinspector.editPreview): This is the text that appears in a
# tooltip on hover of a font preview string. Clicking on the string opens a text input
# where users can type to change the preview text.
fontinspector.editPreview=Click to edit preview

# LOCALIZATION NOTE (fontinspector.copyURL): This is the text that appears in a tooltip
# displayed when the user hovers over the copy icon next to the font URL.
# Clicking the copy icon copies the full font URL to the user's clipboard
fontinspector.copyURL=Copy URL
PK
!<�Hdd;chrome/en-US/locale/en-US/devtools/client/graphs.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE These strings are used inside the Performance Tools
# which is available from the Web Developer sub-menu -> 'Performance'.
# The correct localization of this file might be to keep it in
# English, or another language commonly spoken among web developers.
# You want to make that choice consistent across the developer tools.
# A good criteria is the language in which you'd find the best
# documentation on web development on the web. These strings
# are specifically for marker names in the performance tool.

# LOCALIZATION NOTE (graphs.label.average):
# This string is displayed on graphs when showing an average.
graphs.label.average=avg

# LOCALIZATION NOTE (graphs.label.minimum):
# This string is displayed on graphs when showing a minimum.
graphs.label.minimum=min

# LOCALIZATION NOTE (graphs.label.maximum):
# This string is displayed on graphs when showing a maximum.
graphs.label.maximum=max
PK
!<�X��DD8chrome/en-US/locale/en-US/devtools/client/har.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE These strings are used inside the Network Monitor
# which is available from the Web Developer sub-menu -> 'Network Monitor'.
# The correct localization of this file might be to keep it in
# English, or another language commonly spoken among web developers.
# You want to make that choice consistent across the developer tools.
# A good criteria is the language in which you'd find the best
# documentation on web development on the web.

# LOCALIZATION NOTE (har.responseBodyNotIncluded): A label used within
# HAR file explaining that HTTP response bodies are not includes
# in exported data.
har.responseBodyNotIncluded=Response bodies are not included.

# LOCALIZATION NOTE (har.requestBodyNotIncluded): A label used within
# HAR file explaining that HTTP request bodies are not includes
# in exported data.
har.requestBodyNotIncluded=Request bodies are not included.

PK
!<o���\�\>chrome/en-US/locale/en-US/devtools/client/inspector.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE These strings are used inside the Inspector
# which is available from the Web Developer sub-menu -> 'Inspect'.
#
# The correct localization of this file might be to keep it in
# English, or another language commonly spoken among web developers.
# You want to make that choice consistent across the developer tools.
# A good criteria is the language in which you'd find the best
# documentation on web development on the web.

breadcrumbs.siblings=Siblings

# LOCALIZATION NOTE (debuggerPausedWarning): Used in the Inspector tool, when
# the user switch to the inspector when the debugger is paused.
debuggerPausedWarning.message=Debugger is paused. Some features like mouse selection will not work.

# LOCALIZATION NOTE (nodeMenu.tooltiptext)
# This menu appears in the Infobar (on top of the highlighted node) once
# the node is selected.
nodeMenu.tooltiptext=Node operations

inspector.panelLabel.markupView=Markup View

# LOCALIZATION NOTE (markupView.more.showing)
# When there are too many nodes to load at once, we will offer to
# show all the nodes.
markupView.more.showing=Some nodes were hidden.

# LOCALIZATION NOTE (markupView.more.showAll2): Semi-colon list of plural forms.
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
markupView.more.showAll2=Show one more node;Show all #1 nodes

# LOCALIZATION NOTE (markupView.whitespaceOnly)
# Used in a tooltip that appears when the user hovers over whitespace-only text nodes in
# the inspector.
markupView.whitespaceOnly=Whitespace-only text node: %S

# LOCALIZATION NOTE (markupView.display.flex.tooltiptext)
# Used in a tooltip that appears when the user hovers over the display type button in
# the markup view.
markupView.display.flex.tooltiptext=This element behaves like a block element and lays out its content according to the flexbox model.

# LOCALIZATION NOTE (markupView.display.inlineFlex.tooltiptext)
# Used in a tooltip that appears when the user hovers over the display type button in
# the markup view.
markupView.display.inlineFlex.tooltiptext=This element behaves like an inline element and lays out its content according to the flexbox model.

# LOCALIZATION NOTE (markupView.display.grid.tooltiptext)
# Used in a tooltip that appears when the user hovers over the display type button in
# the markup view.
markupView.display.grid.tooltiptext=This element behaves like a block element and lays out its content according to the grid model.

# LOCALIZATION NOTE (markupView.display.flex.tooltiptext)
# Used in a tooltip that appears when the user hovers over the display type button in
# the markup view.
markupView.display.inlineGrid.tooltiptext=This element behaves like an inline element and lays out its content according to the grid model.

# LOCALIZATION NOTE (markupView.display.flowRoot.tooltiptext)
# Used in a tooltip that appears when the user hovers over the display type button in
# the markup view.
markupView.display.flowRoot.tooltiptext=This element generates a block element box that establishes a new block formatting context.

# LOCALIZATION NOTE (markupView.display.contents.tooltiptext2)
# Used in a tooltip that appears when the user hovers over the display type button in
# the markup view.
markupView.display.contents.tooltiptext2=This element doesn’t produce a specific box by itself, but renders its contents.

# LOCALIZATION NOTE (markupView.event.tooltiptext)
# Used in a tooltip that appears when the user hovers over 'ev' button in
# the markup view.
markupView.event.tooltiptext=Event listener

# LOCALIZATION NOTE (markupView.newAttribute.label)
# This is used to speak the New Attribute button when editing a tag
# and a screen reader user tabs to it. This string is not visible onscreen.
markupView.newAttribute.label=New attribute

#LOCALIZATION NOTE: Used in the image preview tooltip when the image could not be loaded
previewTooltip.image.brokenImage=Could not load the image

# LOCALIZATION NOTE: Used in color picker tooltip when the eyedropper is disabled for
# non-HTML documents
eyedropper.disabled.title=Unavailable in non-HTML documents

#LOCALIZATION NOTE: Used in the event tooltip to allow the debugger to be opened
eventsTooltip.openInDebugger=Open in Debugger

#LOCALIZATION NOTE: Used in the event tooltip when a script's filename cannot be detected
eventsTooltip.unknownLocation=Unknown location

#LOCALIZATION NOTE: Used in the mouseover tooltip when hovering "Unknown location."
eventsTooltip.unknownLocationExplanation=The original location of this listener cannot be detected. Maybe the code is transpiled by a utility such as Babel.

#LOCALIZATION NOTE: Used in the tooltip for Bubbling
eventsTooltip.Bubbling=Bubbling

#LOCALIZATION NOTE: Used in the tooltip for Capturing
eventsTooltip.Capturing=Capturing

# LOCALIZATION NOTE (inspector.showSplitRulesView): This is the tooltip for the button
# that toggles on the display of a split rule view sidebar in the inspector.
inspector.showSplitRulesView=Show the split Rules panel

# LOCALIZATION NOTE (inspector.hideSplitRulesView): This is the tooltip for the button
# that toggles off the display of a split rule view sidebar in the inspector.
inspector.hideSplitRulesView=Hide the split Rules panel

# LOCALIZATION NOTE (inspector.searchResultsCount2): This is the label that
# will show up next to the inspector search box. %1$S is the current result
# index and %2$S is the total number of search results. For example: "3 of 9".
# This won't be visible until the search box is updated in Bug 835896.
inspector.searchResultsCount2=%1$S of %2$S

# LOCALIZATION NOTE (inspector.searchResultsNone): This is the label that
# will show up next to the inspector search box when no matches were found
# for the given string.
# This won't be visible until the search box is updated in Bug 835896.
inspector.searchResultsNone=No matches

# LOCALIZATION NOTE (inspector.menu.openUrlInNewTab.label): This is the label of
# a menu item in the inspector contextual-menu that appears when the user right-
# clicks on the attribute of a node in the inspector that is a URL, and that
# allows to open that URL in a new tab.
inspector.menu.openUrlInNewTab.label=Open Link in New Tab

# LOCALIZATION NOTE (inspector.menu.copyUrlToClipboard.label): This is the label
# of a menu item in the inspector contextual-menu that appears when the user
# right-clicks on the attribute of a node in the inspector that is a URL, and
# that allows to copy that URL in the clipboard.
inspector.menu.copyUrlToClipboard.label=Copy Link Address

# LOCALIZATION NOTE (inspector.menu.selectElement.label): This is the label of a
# menu item in the inspector contextual-menu that appears when the user right-
# clicks on the attribute of a node in the inspector that is the ID of another
# element in the DOM (like with <label for="input-id">), and that allows to
# select that element in the inspector.
inspector.menu.selectElement.label=Select Element #%S

# LOCALIZATION NOTE (inspectorEditAttribute.label): This is the label of a
# sub-menu "Attribute" in the inspector contextual-menu that appears
# when the user right-clicks on the node in the inspector, and that allows
# to edit an attribute on this node.
inspectorEditAttribute.label=Edit Attribute “%S”
inspectorEditAttribute.accesskey=E

# LOCALIZATION NOTE (inspectorRemoveAttribute.label): This is the label of a
# sub-menu "Attribute" in the inspector contextual-menu that appears
# when the user right-clicks on the attribute of a node in the inspector,
# and that allows to remove this attribute.
inspectorRemoveAttribute.label=Remove Attribute “%S”
inspectorRemoveAttribute.accesskey=R

# LOCALIZATION NOTE (inspectorCopyAttributeValue.label): This is the label of a
# sub-menu "Attribute" in the inspector contextual-menu that appears
# when the user right-clicks on the attribute of a node in the inspector,
# and that allows to copy the attribute value to clipboard.
inspectorCopyAttributeValue.label=Copy Attribute Value “%S”
inspectorCopyAttributeValue.accesskey=V

# LOCALIZATION NOTE (inspector.nodePreview.selectNodeLabel):
# This string is displayed in a tooltip that is shown when hovering over a DOM
# node preview (e.g. something like "div#foo.bar").
# DOM node previews can be displayed in places like the animation-inspector, the
# console or the object inspector.
# The tooltip invites the user to click on the node in order to select it in the
# inspector panel.
inspector.nodePreview.selectNodeLabel=Click to select this node in the Inspector

# LOCALIZATION NOTE (inspector.nodePreview.highlightNodeLabel):
# This string is displayed in a tooltip that is shown when hovering over a the
# inspector icon displayed next to a DOM node preview (e.g. next to something
# like "div#foo.bar").
# DOM node previews can be displayed in places like the animation-inspector, the
# console or the object inspector.
# The tooltip invites the user to click on the icon in order to highlight the
# node in the page.
inspector.nodePreview.highlightNodeLabel=Click to highlight this node in the page

# LOCALIZATION NOTE (inspectorHTMLEdit.label): This is the label shown
# in the inspector contextual-menu for the item that lets users edit the
# (outer) HTML of the current node
inspectorHTMLEdit.label=Edit As HTML
inspectorHTMLEdit.accesskey=E

# LOCALIZATION NOTE (inspectorCopyInnerHTML.label): This is the label shown
# in the inspector contextual-menu for the item that lets users copy the
# inner HTML of the current node
inspectorCopyInnerHTML.label=Inner HTML
inspectorCopyInnerHTML.accesskey=I

# LOCALIZATION NOTE (inspectorCopyOuterHTML.label): This is the label shown
# in the inspector contextual-menu for the item that lets users copy the
# outer HTML of the current node
inspectorCopyOuterHTML.label=Outer HTML
inspectorCopyOuterHTML.accesskey=O

# LOCALIZATION NOTE (inspectorCopyCSSSelector.label): This is the label
# shown in the inspector contextual-menu for the item that lets users copy
# the CSS Selector of the current node
inspectorCopyCSSSelector.label=CSS Selector
inspectorCopyCSSSelector.accesskey=S

# LOCALIZATION NOTE (inspectorCopyCSSPath.label): This is the label
# shown in the inspector contextual-menu for the item that lets users copy
# the full CSS path of the current node
inspectorCopyCSSPath.label=CSS Path
inspectorCopyCSSPath.accesskey=P

# LOCALIZATION NOTE (inspectorCopyXPath.label): This is the label
# shown in the inspector contextual-menu for the item that lets users copy
# the XPath of the current node
inspectorCopyXPath.label=XPath
inspectorCopyXPath.accesskey=X

# LOCALIZATION NOTE (inspectorPasteOuterHTML.label): This is the label shown
# in the inspector contextual-menu for the item that lets users paste outer
# HTML in the current node
inspectorPasteOuterHTML.label=Outer HTML
inspectorPasteOuterHTML.accesskey=O

# LOCALIZATION NOTE (inspectorPasteInnerHTML.label): This is the label shown
# in the inspector contextual-menu for the item that lets users paste inner
# HTML in the current node
inspectorPasteInnerHTML.label=Inner HTML
inspectorPasteInnerHTML.accesskey=I

# LOCALIZATION NOTE (inspectorHTMLPasteBefore.label): This is the label shown
# in the inspector contextual-menu for the item that lets users paste
# the HTML before the current node
inspectorHTMLPasteBefore.label=Before
inspectorHTMLPasteBefore.accesskey=B

# LOCALIZATION NOTE (inspectorHTMLPasteAfter.label): This is the label shown
# in the inspector contextual-menu for the item that lets users paste
# the HTML after the current node
inspectorHTMLPasteAfter.label=After
inspectorHTMLPasteAfter.accesskey=A

# LOCALIZATION NOTE (inspectorHTMLPasteFirstChild.label): This is the label
# shown in the inspector contextual-menu for the item that lets users paste
# the HTML as the first child the current node
inspectorHTMLPasteFirstChild.label=As First Child
inspectorHTMLPasteFirstChild.accesskey=F

# LOCALIZATION NOTE (inspectorHTMLPasteLastChild.label): This is the label
# shown in the inspector contextual-menu for the item that lets users paste
# the HTML as the last child the current node
inspectorHTMLPasteLastChild.label=As Last Child
inspectorHTMLPasteLastChild.accesskey=L

# LOCALIZATION NOTE (inspectorScrollNodeIntoView.label): This is the label
# shown in the inspector contextual-menu for the item that lets users scroll
# the current node into view
inspectorScrollNodeIntoView.label=Scroll Into View
inspectorScrollNodeIntoView.accesskey=S

# LOCALIZATION NOTE (inspectorHTMLDelete.label): This is the label shown in
# the inspector contextual-menu for the item that lets users delete the
# current node
inspectorHTMLDelete.label=Delete Node
inspectorHTMLDelete.accesskey=D

# LOCALIZATION NOTE (inspectorAttributesSubmenu.label): This is the label
# shown in the inspector contextual-menu for the sub-menu of the other
# attribute items, which allow to:
# - add new attribute
# - edit attribute
# - remove attribute
inspectorAttributesSubmenu.label=Attributes
inspectorAttributesSubmenu.accesskey=A

# LOCALIZATION NOTE (inspectorAddAttribute.label): This is the label shown in
# the inspector contextual-menu for the item that lets users add attribute
# to current node
inspectorAddAttribute.label=Add Attribute
inspectorAddAttribute.accesskey=A

# LOCALIZATION NOTE (inspectorSearchHTML.label3): This is the label that is
# shown as the placeholder for the markup view search in the inspector.
inspectorSearchHTML.label3=Search HTML

# LOCALIZATION NOTE (inspectorImageDataUri.label): This is the label
# shown in the inspector contextual-menu for the item that lets users copy
# the URL embedding the image data encoded in Base 64 (what we name
# here Image Data URL). For more information:
# https://developer.mozilla.org/en-US/docs/Web/HTTP/data_URIs
inspectorImageDataUri.label=Image Data-URL

# LOCALIZATION NOTE (inspectorShowDOMProperties.label): This is the label
# shown in the inspector contextual-menu for the item that lets users see
# the DOM properties of the current node. When triggered, this item
# opens the split Console and displays the properties in its side panel.
inspectorShowDOMProperties.label=Show DOM Properties

# LOCALIZATION NOTE (inspectorUseInConsole.label): This is the label
# shown in the inspector contextual-menu for the item that outputs a
# variable for the current node to the console. When triggered,
# this item opens the split Console.
inspectorUseInConsole.label=Use in Console

# LOCALIZATION NOTE (inspectorExpandNode.label): This is the label
# shown in the inspector contextual-menu for recursively expanding
# mark-up elements
inspectorExpandNode.label=Expand All

# LOCALIZATION NOTE (inspectorCollapseAll.label): This is the label
# shown in the inspector contextual-menu for recursively collapsing
# mark-up elements
inspectorCollapseAll.label=Collapse All

# LOCALIZATION NOTE (inspectorScreenshotNode.label): This is the label
# shown in the inspector contextual-menu for the item that lets users take
# a screenshot of the currently selected node.
inspectorScreenshotNode.label=Screenshot Node

# LOCALIZATION NOTE (inspectorDuplicateNode.label): This is the label
# shown in the inspector contextual-menu for the item that lets users
# duplicate the currently selected node.
inspectorDuplicateNode.label=Duplicate Node

# LOCALIZATION NOTE (inspectorAddNode.label): This is the label shown in
# the inspector toolbar for the button that lets users add elements to the
# DOM (as children of the currently selected element).
inspectorAddNode.label=Create New Node
inspectorAddNode.accesskey=C

# LOCALIZATION NOTE (inspectorCopyHTMLSubmenu.label): This is the label
# shown in the inspector contextual-menu for the sub-menu of the other
# copy items, which allow to:
# - Copy Inner HTML
# - Copy Outer HTML
# - Copy Unique selector
# - Copy Image data URI
inspectorCopyHTMLSubmenu.label=Copy

# LOCALIZATION NOTE (inspectorPasteHTMLSubmenu.label): This is the label
# shown in the inspector contextual-menu for the sub-menu of the other
# paste items, which allow to:
# - Paste Inner HTML
# - Paste Outer HTML
# - Before
# - After
# - As First Child
# - As Last Child
inspectorPasteHTMLSubmenu.label=Paste

# LOCALIZATION NOTE (inspector.searchHTML.key):
# Key shortcut used to focus the DOM element search box on top-right corner of
# the markup view
inspector.searchHTML.key=CmdOrCtrl+F

# LOCALIZATION NOTE (markupView.hide.key):
# Key shortcut used to hide the selected node in the markup view.
markupView.hide.key=h

# LOCALIZATION NOTE (markupView.edit.key):
# Key shortcut used to hide the selected node in the markup view.
markupView.edit.key=F2

# LOCALIZATION NOTE (markupView.scrollInto.key):
# Key shortcut used to scroll the webpage in order to ensure the selected node
# is visible
markupView.scrollInto.key=s

# LOCALIZATION NOTE (inspector.sidebar.fontInspectorTitle):
# This is the title shown in a tab in the side panel of the Inspector panel
# that corresponds to the tool displaying the list of fonts used in the page.
inspector.sidebar.fontInspectorTitle=Fonts

# LOCALIZATION NOTE (inspector.sidebar.ruleViewTitle):
# This is the title shown in a tab in the side panel of the Inspector panel
# that corresponds to the tool displaying the list of CSS rules used
# in the page.
inspector.sidebar.ruleViewTitle=Rules

# LOCALIZATION NOTE (inspector.sidebar.computedViewTitle):
# This is the title shown in a tab in the side panel of the Inspector panel
# that corresponds to the tool displaying the list of computed CSS values
# used in the page.
inspector.sidebar.computedViewTitle=Computed

# LOCALIZATION NOTE (inspector.sidebar.layoutViewTitle2):
# This is the title shown in a tab in the side panel of the Inspector panel
# that corresponds to the tool displaying layout information defined in the page.
inspector.sidebar.layoutViewTitle2=Layout

# LOCALIZATION NOTE (inspector.sidebar.newBadge):
# This is the text of a promotion badge showed in the inspector sidebar, next to a panel
# name. Used to promote new/recent panels such as the layout panel.
inspector.sidebar.newBadge=new!

# LOCALIZATION NOTE (inspector.sidebar.changesViewTitle):
# This is the title shown in a tab in the side panel of the Inspector panel
# that corresponds to the tool displaying CSS changes.
inspector.sidebar.changesViewTitle=Changes

# LOCALIZATION NOTE (inspector.sidebar.eventsViewTitle):
# This is the title shown in a tab in the side panel of the Inspector panel
# that corresponds to the tool displaying the list of event listeners used in the page.
inspector.sidebar.eventsViewTitle=Events

# LOCALIZATION NOTE (inspector.sidebar.animationInspectorTitle):
# This is the title shown in a tab in the side panel of the Inspector panel
# that corresponds to the tool displaying animations defined in the page.
inspector.sidebar.animationInspectorTitle=Animations

# LOCALIZATION NOTE (inspector.eyedropper.label): A string displayed as the tooltip of
# a button in the inspector which toggles the Eyedropper tool
inspector.eyedropper.label=Grab a color from the page

# LOCALIZATION NOTE (inspector.colorwidget.colorNameLabel):
# The label for the current color widget's color name field.
inspector.colorwidget.colorNameLabel=Color Name:

# LOCALIZATION NOTE (inspector.colorwidget.contrastRatio.header):
# This string is used as a header to indicate the contrast section of the
# color widget.
inspector.colorwidget.contrastRatio.header=Contrast Ratio

# LOCALIZATION NOTE (inspector.colorwidget.contrastRatio.invalidColor):
# This string is used when an invalid color is passed as a background color
# to the color widget.
inspector.colorwidget.contrastRatio.invalidColor=Invalid color

# LOCALIZATION NOTE (inspector.colorwidget.contrastRatio.info):
# This string is used to explain the contrast ratio grading system when you hover over the help icon in the contrast info.
inspector.colorwidget.contrastRatio.info=The contrast ratio grading system for text has the following grading: Fail, AA*, AAA* AAA from lowest to highest readability.\nIt was calculated based on the computed background color:

# LOCALIZATION NOTE (inspector.colorwidget.contrastRatio.failGrade):
# This string is used to indicate that the text fails for contrast ratio grading criteria.
inspector.colorwidget.contrastRatio.failGrade=Fail

# LOCALIZATION NOTE (inspector.colorwidget.contrastRatio.failInfo):
# This string is used to explain that the text fails for contrast ratio grading criteria.
inspector.colorwidget.contrastRatio.failInfo=This contrast ratio fails for all text sizes.

# LOCALIZATION NOTE (inspector.colorwidget.contrastRatio.AABigInfo):
# This string is used to explain that the text passes AA* grade for contrast ratio.
inspector.colorwidget.contrastRatio.AABigInfo=This contrast ratio passes the AA grade for big text (at least 18 point or 14 point bold sized text).

# LOCALIZATION NOTE (inspector.colorwidget.contrastRatio.AAABigInfo):
# This string is used to explain that the text passes the AA grade and AAA* for contrast ratio.
inspector.colorwidget.contrastRatio.AAABigInfo=This contrast ratio passes the AA grade for all text and AAA grade for big text (at least 18 point or 14 point bold sized text).

# LOCALIZATION NOTE (inspector.colorwidget.contrastRatio.AAAInfo):
# This string is used to explain that the text passes AAA grade for contrast ratio.
inspector.colorwidget.contrastRatio.AAAInfo=This contrast ratio passes the AAA grade for all text sizes.

# LOCALIZATION NOTE (inspector.breadcrumbs.label): A string visible only to a screen reader and
# is used to label (using aria-label attribute) a container for inspector breadcrumbs
inspector.breadcrumbs.label=Breadcrumbs

# LOCALIZATION NOTE (inspector.browserStyles.label): This is the label for the checkbox
# that specifies whether the styles that are not from the user's stylesheet should be
# displayed or not.
inspector.browserStyles.label=Browser styles

# LOCALIZATION NOTE (inspector.filterStyles.placeholder): This is the placeholder that
# goes in the search box when no search term has been entered.
inspector.filterStyles.placeholder=Filter Styles

# LOCALIZATION NOTE (inspector.addRule.tooltip): This is the tooltip shown when
# hovering the `Add new rule` button in the rules view toolbar. This should
# match ruleView.contextmenu.addNewRule in styleinspector.properties
inspector.addRule.tooltip=Add new rule

# LOCALIZATION NOTE (inspector.togglePseudo.tooltip): This is the tooltip
# shown when hovering over the `Toggle Pseudo Class Panel` button in the
# rule view toolbar.
inspector.togglePseudo.tooltip=Toggle pseudo-classes

# LOCALIZATION NOTE (inspector.classPanel.toggleClass.tooltip): This is the tooltip
# shown when hovering over the `Toggle Class Panel` button in the
# rule view toolbar.
inspector.classPanel.toggleClass.tooltip=Toggle classes

# LOCALIZATION NOTE (inspector.classPanel.newClass.placeholder): This is the placeholder
# shown inside the text field used to add a new class in the rule-view.
inspector.classPanel.newClass.placeholder=Add new class

# LOCALIZATION NOTE (inspector.classPanel.noClasses): This is the text displayed in the
# class panel when the current element has no classes applied.
inspector.classPanel.noClasses=No classes on this element

# LOCALIZATION NOTE (inspector.noProperties): In the case where there are no CSS
# properties to display e.g. due to search criteria this message is
# displayed.
inspector.noProperties=No CSS properties found.
PK
!<�q��Fchrome/en-US/locale/en-US/devtools/client/jit-optimizations.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE These strings are used within the JIT tools
# in the Performance Tools which is available from the Web Developer
# sub-menu -> 'Performance' The correct localization of this file might
# be to keep it in English, or another language commonly spoken among
# web developers. You want to make that choice consistent across the
# developer tools. A good criteria is the language in which you'd find the best
# documentation on web development on the web.

# LOCALIZATION NOTE (jit.title):
# This string is displayed in the header of the JIT Optimizations view.
jit.title=JIT Optimizations

# LOCALIZATION NOTE (jit.optimizationFailure):
# This string is displayed in a tooltip when no JIT optimizations were detected.
jit.optimizationFailure=Optimization failed

# LOCALIZATION NOTE (jit.samples):
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
# This string is displayed for the unit representing the number of times a
# frame is sampled.
# "#1" represents the number of samples
# example: 30 samples
jit.samples=#1 sample;#1 samples

# LOCALIZATION NOTE (jit.types):
# This string is displayed for the group of Ion Types in the optimizations view.
jit.types=Types

# LOCALIZATION NOTE (jit.attempts):
# This string is displayed for the group of optimization attempts in the optimizations view.
jit.attempts=Attempts
PK
!<�I����=chrome/en-US/locale/en-US/devtools/client/jsonview.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE These strings are used in the JSON View tool
# that is used to inspect application/json document types loaded
# in the browser.

# LOCALIZATION NOTE The correct localization of this file might be to keep it
# in English, or another language commonly spoken among web developers.
# You want to make that choice consistent across the developer tools.
# A good criteria is the language in which you'd find the best documentation
# on web development on the web.

# LOCALIZATION NOTE (jsonViewer.tab.JSON, jsonViewer.tab.RawData,
# jsonViewer.tab.Headers): Label for a panel tab.
jsonViewer.tab.JSON=JSON
jsonViewer.tab.RawData=Raw Data
jsonViewer.tab.Headers=Headers

# LOCALIZATION NOTE (jsonViewer.responseHeaders, jsonViewer.requestHeaders):
# Label for header groups within the 'Headers' panel.
jsonViewer.responseHeaders=Response Headers
jsonViewer.requestHeaders=Request Headers

# LOCALIZATION NOTE (jsonViewer.Save): Label for save command
jsonViewer.Save=Save

# LOCALIZATION NOTE (jsonViewer.Copy): Label for clipboard copy command
jsonViewer.Copy=Copy

# LOCALIZATION NOTE (jsonViewer.ExpandAll): Label for expanding all nodes
jsonViewer.ExpandAll=Expand All

# LOCALIZATION NOTE (jsonViewer.PrettyPrint): Label for JSON
# pretty print action button.
jsonViewer.PrettyPrint=Pretty Print

# LOCALIZATION NOTE (jsonViewer.reps.more): Label used in arrays
# that have more items than displayed.
jsonViewer.reps.more=more…

# LOCALIZATION NOTE (jsonViewer.filterJSON): Label used in search box
# at the top right cornder of the JSON Viewer.
jsonViewer.filterJSON=Filter JSON

# LOCALIZATION NOTE (jsonViewer.reps.reference): Label used for cycle
# references in an array.
jsonViewer.reps.reference=Cycle Reference
PK
!<�a�}�
�
;chrome/en-US/locale/en-US/devtools/client/layout.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE This file contains the Layout Inspector strings.
# The Layout Inspector is a panel accessible in the Inspector sidebar.

# LOCALIZATION NOTE (flexbox.header): The accordion header for the Flexbox pane.
flexbox.header=Flexbox

# LOCALIZATION NOTE (flexbox.noFlexboxeOnThisPage): In the case where there are no CSS
# flex containers to display.
flexbox.noFlexboxeOnThisPage=Select a Flex container or item to continue.

# LOCALIZATION NOTE (flexbox.overlayFlexbox): Header for the list of flex container
# elements if only one item can be selected.
flexbox.overlayFlexbox=Overlay Flexbox

# LOCALIZATION NOTE (layout.cannotShowGridOutline, layout.cannotSHowGridOutline.title):
# In the case where the grid outline cannot be effectively displayed.
layout.cannotShowGridOutline=Cannot show outline for this grid
layout.cannotShowGridOutline.title=The selected grid’s outline cannot effectively fit inside the layout panel for it to be usable.

# LOCALIZATION NOTE (layout.displayAreaNames): Label of the display area names setting
# option in the CSS Grid pane.
layout.displayAreaNames=Display area names

# LOCALIZATION NOTE (layout.displayLineNumbers): Label of the display line numbers
# setting option in the CSS Grid pane.
layout.displayLineNumbers=Display line numbers

# LOCALIZATION NOTE (layout.extendLinesInfinitely): Label of the extend lines
# infinitely setting option in the CSS Grid pane.
layout.extendLinesInfinitely=Extend lines infinitely

# LOCALIZATION NOTE (layout.header): The accordion header for the CSS Grid pane.
layout.header=Grid

# LOCALIZATION NOTE (layout.gridDisplaySettings): The header for the grid display
# settings container in the CSS Grid pane.
layout.gridDisplaySettings=Grid Display Settings

# LOCALIZATION NOTE (layout.noGridsOnThisPage): In the case where there are no CSS grid
# containers to display.
layout.noGridsOnThisPage=CSS Grid is not in use on this page

# LOCALIZATION NOTE (layout.overlayMultipleGrids): The header for the list of grid
# container elements that can be highlighted in the CSS Grid pane.
layout.overlayMultipleGrids=Overlay Multiple Grids

# LOCALIZATION NOTE (layout.overlayGrid): Alternate header for the list of grid container
# elements if only one item can be selected.
layout.overlayGrid=Overlay Grid

# LOCALIZATION NOTE (layout.rowColumnPositions): The row and column position of a grid
# cell shown in the grid cell infobar when hovering over the CSS grid outline.
layout.rowColumnPositions=Row %S / Column %S
PK
!<4�D
v'v'<chrome/en-US/locale/en-US/devtools/client/markers.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE These strings are used inside the Performance Tools
# which is available from the Web Developer sub-menu -> 'Performance'.
# The correct localization of this file might be to keep it in
# English, or another language commonly spoken among web developers.
# You want to make that choice consistent across the developer tools.
# A good criteria is the language in which you'd find the best
# documentation on web development on the web. These strings
# are specifically for marker names in the performance tool.

# LOCALIZATION NOTE (marker.label.*):
# These strings are displayed in the Performance Tool waterfall, identifying markers.
# We want to use the same wording as Google Chrome when appropriate.
marker.label.styles=Recalculate Style
marker.label.stylesApplyChanges=Apply Style Changes
marker.label.reflow=Layout
marker.label.paint=Paint
marker.label.composite=Composite Layers
marker.label.compositeForwardTransaction=Composite Request Sent
marker.label.javascript=Function Call
marker.label.parseHTML=Parse HTML
marker.label.parseXML=Parse XML
marker.label.domevent=DOM Event
marker.label.consoleTime=Console
marker.label.garbageCollection2=Garbage Collection
marker.label.garbageCollection.incremental=Incremental GC
marker.label.garbageCollection.nonIncremental=Non-incremental GC
marker.label.minorGC=Minor GC
marker.label.cycleCollection=Cycle Collection
marker.label.cycleCollection.forgetSkippable=CC Graph Reduction
marker.label.timestamp=Timestamp
marker.label.worker=Worker
marker.label.messagePort=MessagePort
marker.label.unknown=Unknown

# LOCALIZATION NOTE (marker.label.javascript.*):
# These strings are displayed as JavaScript markers that have special
# reasons that can be translated.
marker.label.javascript.scriptElement=Script Tag
marker.label.javascript.promiseCallback=Promise Callback
marker.label.javascript.promiseInit=Promise Init
marker.label.javascript.workerRunnable=Worker
marker.label.javascript.jsURI=JavaScript URI
marker.label.javascript.eventHandler=Event Handler

# LOCALIZATION NOTE (marker.field.*):
# Strings used in the waterfall sidebar as property names.

# General marker fields
marker.field.start=Start:
marker.field.end=End:
marker.field.duration=Duration:

# General "reason" for a marker (JavaScript, Garbage Collection)
marker.field.causeName=Cause:
# General "type" for a marker (Cycle Collection, Garbage Collection)
marker.field.type=Type:
# General "label" for a marker (user defined)
marker.field.label=Label:

# Field names for stack values
marker.field.stack=Stack:
marker.field.startStack=Stack at start:
marker.field.endStack=Stack at end:

# %S is the "Async Cause" of a marker, and this signifies that the cause
# was an asynchronous one in a displayed stack.
marker.field.asyncStack=(Async: %S)

# For console.time markers
marker.field.consoleTimerName=Timer Name:

# For DOM Event markers
marker.field.DOMEventType=Event Type:
marker.field.DOMEventPhase=Phase:

# Non-incremental cause for a Garbage Collection marker
marker.field.nonIncrementalCause=Non-incremental Cause:

# For "Recalculate Style" markers
marker.field.isAnimationOnly=Animation Only:

# The type of operation performed by a Worker.
marker.worker.serializeDataOffMainThread=Serialize data in Worker
marker.worker.serializeDataOnMainThread=Serialize data on the main thread
marker.worker.deserializeDataOffMainThread=Deserialize data in Worker
marker.worker.deserializeDataOnMainThread=Deserialize data on the main thread

# The type of operation performed by a MessagePort
marker.messagePort.serializeData=Serialize data
marker.messagePort.deserializeData=Deserialize data

# Strings used in the waterfall sidebar as values.
marker.value.unknownFrame=<unknown location>
marker.value.DOMEventTargetPhase=Target
marker.value.DOMEventCapturingPhase=Capture
marker.value.DOMEventBubblingPhase=Bubbling

# LOCALIZATION NOTE (marker.gcreason.label.*):
# These strings are used to give a concise but readable description of a GC reason.
marker.gcreason.label.API=API Call
marker.gcreason.label.EAGER_ALLOC_TRIGGER=Eager Allocation Trigger
marker.gcreason.label.DESTROY_RUNTIME=Shutdown
marker.gcreason.label.LAST_DITCH=Out of Memory
marker.gcreason.label.TOO_MUCH_MALLOC=Too Many Bytes Allocated
marker.gcreason.label.ALLOC_TRIGGER=Too Many Allocations
marker.gcreason.label.DEBUG_GC=Debug GC
marker.gcreason.label.COMPARTMENT_REVIVED=Dead Global Revived
marker.gcreason.label.RESET=Finish Incremental Cycle
marker.gcreason.label.OUT_OF_NURSERY=Nursery is Full
marker.gcreason.label.EVICT_NURSERY=Nursery Eviction
marker.gcreason.label.FULL_STORE_BUFFER=Nursery Objects Too Active
marker.gcreason.label.SHARED_MEMORY_LIMIT=Large Allocation Failed
marker.gcreason.label.PERIODIC_FULL_GC=Periodic Full GC
marker.gcreason.label.INCREMENTAL_TOO_SLOW=Allocations Rate Too Fast
marker.gcreason.label.COMPONENT_UTILS=Cu.forceGC
marker.gcreason.label.MEM_PRESSURE=Low Memory
marker.gcreason.label.CC_WAITING=Forced by Cycle Collection
marker.gcreason.label.CC_FORCED=Forced by Cycle Collection
marker.gcreason.label.LOAD_END=Page Load Finished
marker.gcreason.label.PAGE_HIDE=Moved to Background
marker.gcreason.label.NSJSCONTEXT_DESTROY=Destroy JS Context
marker.gcreason.label.SET_NEW_DOCUMENT=New Document
marker.gcreason.label.SET_DOC_SHELL=New Document
marker.gcreason.label.DOM_UTILS=API Call
marker.gcreason.label.DOM_IPC=IPC
marker.gcreason.label.DOM_WORKER=Periodic Worker GC
marker.gcreason.label.INTER_SLICE_GC=Periodic Incremental GC Slice
marker.gcreason.label.FULL_GC_TIMER=Periodic Full GC
marker.gcreason.label.SHUTDOWN_CC=Shutdown
marker.gcreason.label.DOM_WINDOW_UTILS=User Inactive
marker.gcreason.label.USER_INACTIVE=User Inactive

# The name of a nursery collection.
marker.nurseryCollection=Nursery Collection

# LOCALIZATION NOTE (marker.gcreason.description.*):
# These strings are used to give an expanded description of why a GC occurred.
marker.gcreason.description.API=There was an API call to force garbage collection.
marker.gcreason.description.EAGER_ALLOC_TRIGGER=JavaScript returned to the event loop and there were enough bytes allocated since the last GC that a new GC cycle was triggered.
marker.gcreason.description.DESTROY_RUNTIME=Firefox destroyed a JavaScript runtime or context, and this was the final garbage collection before shutting down.
marker.gcreason.description.LAST_DITCH=JavaScript attempted to allocate, but there was no memory available. Doing a full compacting garbage collection as an attempt to free up memory for the allocation.
marker.gcreason.description.TOO_MUCH_MALLOC=JavaScript allocated too many bytes, and forced a garbage collection.
marker.gcreason.description.ALLOC_TRIGGER=JavaScript allocated too many times, and forced a garbage collection.
marker.gcreason.description.DEBUG_GC=GC due to Zeal debug settings.
marker.gcreason.description.COMPARTMENT_REVIVED=A global object that was thought to be dead at the start of the GC cycle was revived by the end of the GC cycle.
marker.gcreason.description.RESET=The active incremental GC cycle was forced to finish immediately.
marker.gcreason.description.OUT_OF_NURSERY=JavaScript allocated enough new objects in the nursery that it became full and triggered a minor GC.
marker.gcreason.description.EVICT_NURSERY=Work needed to be done on the tenured heap, requiring the nursery to be empty.
marker.gcreason.description.FULL_STORE_BUFFER=There were too many properties on tenured objects whose value was an object in the nursery.
marker.gcreason.description.SHARED_MEMORY_LIMIT=A large allocation was requested, but there was not enough memory.
marker.gcreason.description.PERIODIC_FULL_GC=JavaScript returned to the event loop, and it has been a relatively long time since Firefox performed a garbage collection.
marker.gcreason.description.INCREMENTAL_TOO_SLOW=A full, non-incremental garbage collection was triggered because there was a faster rate of allocations than the existing incremental garbage collection cycle could keep up with.
marker.gcreason.description.COMPONENT_UTILS=Components.utils.forceGC() was called to force a garbage collection.
marker.gcreason.description.MEM_PRESSURE=There was very low memory available.
marker.gcreason.description.CC_WAITING=The cycle collector required a garbage collection.
marker.gcreason.description.CC_FORCED=The cycle collector required a garbage collection.
marker.gcreason.description.LOAD_END=The document finished loading.
marker.gcreason.description.PAGE_HIDE=The tab or window was moved to the background.
marker.gcreason.description.NSJSCONTEXT_DESTROY=Firefox destroyed a JavaScript runtime or context, and this was the final garbage collection before shutting down.
marker.gcreason.description.SET_NEW_DOCUMENT=The page has been navigated to a new document.
marker.gcreason.description.SET_DOC_SHELL=The page has been navigated to a new document.
marker.gcreason.description.DOM_UTILS=There was an API call to force garbage collection.
marker.gcreason.description.DOM_IPC=Received an inter-process message that requested a garbage collection.
marker.gcreason.description.DOM_WORKER=The worker was idle for a relatively long time.
marker.gcreason.description.INTER_SLICE_GC=There has been a relatively long time since the last incremental GC slice.
marker.gcreason.description.FULL_GC_TIMER=JavaScript returned to the event loop, and it has been a relatively long time since we performed a garbage collection.
marker.gcreason.description.SHUTDOWN_CC=Firefox destroyed a JavaScript runtime or context, and this was the final garbage collection before shutting down.
marker.gcreason.description.DOM_WINDOW_UTILS=The user was inactive for a long time. Took the opportunity to perform GC when it was unlikely to be noticed.
marker.gcreason.description.USER_INACTIVE=The user was inactive for a long time. Firefox took the opportunity to perform GC when it was unlikely to be noticed.
PK
!<(��?OO;chrome/en-US/locale/en-US/devtools/client/memory.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE These strings are used inside the Memory Tools
# which is available from the Web Developer sub-menu -> 'Memory'.
# The correct localization of this file might be to keep it in
# English, or another language commonly spoken among web developers.
# You want to make that choice consistent across the developer tools.
# A good criteria is the language in which you'd find the best
# documentation on web development on the web.

# LOCALIZATION NOTE (snapshot.io.save): The label for the link that saves a
# snapshot to disk.
snapshot.io.save=Save

# LOCALIZATION NOTE (snapshot.io.delete): The label for the link that deletes
# a snapshot
snapshot.io.delete=Delete

# LOCALIZATION NOTE (snapshot.io.save.window): The title for the window
# displayed when saving a snapshot to disk.
snapshot.io.save.window=Save Snapshot

# LOCALIZATION NOTE (snapshot.io.import.window): The title for the window
# displayed when importing a snapshot form disk.
snapshot.io.import.window=Import Snapshot

# LOCALIZATION NOTE (snapshot.io.filter): The title for the filter used to
# filter file types (*.fxsnapshot)
snapshot.io.filter=Firefox Snapshots

# LOCALIZATION NOTE (aggregate.mb): The label annotating the number of bytes (in
# megabytes) in a snapshot. %S represents the value, rounded to 2 decimal
# points.
aggregate.mb=%S MB

# LOCALIZATION NOTE (snapshot-title.loading): The title for a snapshot before
# it has a creation time to display.
snapshot-title.loading=Processing…

# LOCALIZATION NOTE (checkbox.recordAllocationStacks): The label describing the
# boolean checkbox whether or not to record call stacks.
checkbox.recordAllocationStacks=Record call stacks

# LOCALIZATION NOTE (checkbox.recordAllocationStacks.tooltip): The tooltip for
# the label describing the boolean checkbox whether or not to record call
# stacks.
checkbox.recordAllocationStacks.tooltip=Toggle the recording of the call stack of when an object was allocated. Subsequent snapshots will be able to group and label objects by call stacks, but only with those objects created after toggling this option. Recording call stacks has a performance overhead.

# LOCALIZATION NOTE (toolbar.displayBy): The label describing the select menu
# options of the display options.
toolbar.displayBy=Group by:

# LOCALIZATION NOTE (toolbar.displayBy.tooltip): The tooltip for the label
# describing the select menu options of the display options.
toolbar.displayBy.tooltip=Change how objects are grouped

# LOCALIZATION NOTE (toolbar.pop-view): The text in the button to go back to the
# previous view.
toolbar.pop-view=←

# LOCALIZATION NOTE (toolbar.pop-view.label): The text for the label for the
# button to go back to the previous view.
toolbar.pop-view.label=Go back to aggregates

# LOCALIZATION NOTE (toolbar.viewing-individuals): The text letting the user
# know that they are viewing individual nodes from a census group.
toolbar.viewing-individuals=⁂ Viewing individuals in group

# LOCALIZATION NOTE (censusDisplays.coarseType.tooltip): The tooltip for the
# "coarse type" display option.
censusDisplays.coarseType.tooltip=Group items by their type

# LOCALIZATION NOTE (censusDisplays.allocationStack.tooltip): The tooltip for
# the "call stack" display option.
censusDisplays.allocationStack.tooltip=Group items by the JavaScript stack recorded when the object was allocated

# LOCALIZATION NOTE (censusDisplays.invertedAllocationStack.tooltip): The
# tooltip for the "inverted call stack" display option.
censusDisplays.invertedAllocationStack.tooltip=Group items by the inverted JavaScript call stack recorded when the object was created

# LOCALIZATION NOTE (censusDisplays.treeMap.tooltip): The tooltip for the
# "tree map" display option.
censusDisplays.treeMap.tooltip=Visualize memory usage: larger blocks account for a larger percent of memory usage

# LOCALIZATION NOTE (censusDisplays.objectClass.tooltip): The tooltip for the
# "object class" display option.
censusDisplays.objectClass.tooltip=Group items by their JavaScript Object [[class]] name

# LOCALIZATION NOTE (censusDisplays.internalType.tooltip): The tooltip for the
# "internal type" display option.
censusDisplays.internalType.tooltip=Group items by their internal C++ type

# LOCALIZATION NOTE (toolbar.labelBy): The label describing the select menu
# options of the label options.
toolbar.labelBy=Label by:

# LOCALIZATION NOTE (toolbar.labelBy.tooltip): The tooltip for the label describing the
# select menu options of the label options.
toolbar.labelBy.tooltip=Change how objects are labeled

# LOCALIZATION NOTE (dominatorTreeDisplays.coarseType.tooltip): The tooltip for
# the "coarse type" dominator tree display option.
dominatorTreeDisplays.coarseType.tooltip=Label objects by the broad categories they fit in

# LOCALIZATION NOTE (dominatorTreeDisplays.allocationStack.tooltip): The
# tooltip for the "call stack" dominator tree display option.
dominatorTreeDisplays.allocationStack.tooltip=Label objects by the JavaScript stack recorded when it was allocated

# LOCALIZATION NOTE (dominatorTreeDisplays.internalType.tooltip): The
# tooltip for the "internal type" dominator tree display option.
dominatorTreeDisplays.internalType.tooltip=Label objects by their internal C++ type name

# LOCALIZATION NOTE (treeMapDisplays.coarseType.tooltip): The tooltip for
# the "coarse type" tree map display option.
treeMapDisplays.coarseType.tooltip=Label objects by the broad categories they fit in

# LOCALIZATION NOTE (toolbar.view): The label for the view selector in the
# toolbar.
toolbar.view=View:

# LOCALIZATION NOTE (toolbar.view.tooltip): The tooltip for the label for the
# view selector in the toolbar.
toolbar.view.tooltip=Change the view of the snapshot

# LOCALIZATION NOTE (toolbar.view.census): The label for the census view option
# in the toolbar.
toolbar.view.census=Aggregate

# LOCALIZATION NOTE (toolbar.view.census.tooltip): The tooltip for the label for
# the census view option in the toolbar.
toolbar.view.census.tooltip=View a summary of the snapshot’s contents by aggregating objects into groups

# LOCALIZATION NOTE (toolbar.view.dominators): The label for the dominators view
# option in the toolbar.
toolbar.view.dominators=Dominators

# LOCALIZATION NOTE (toolbar.view.dominators.tooltip): The tooltip for the label
# for the dominators view option in the toolbar.
toolbar.view.dominators.tooltip=View the dominator tree and surface the largest structures in the snapshot

# LOCALIZATION NOTE (toolbar.view.treemap): The label for the tree map option
# in the toolbar.
toolbar.view.treemap=Tree Map

# LOCALIZATION NOTE (toolbar.view.treemap.tooltip): The tooltip for the label for
# the tree map view option in the toolbar.
toolbar.view.treemap.tooltip=Visualize memory usage: larger blocks account for a larger percent of memory usage

# LOCALIZATION NOTE (take-snapshot): The label describing the button that
# initiates taking a snapshot, either as the main label, or a tooltip.
take-snapshot=Take snapshot

# LOCALIZATION NOTE (import-snapshot): The label describing the button that
# initiates importing a snapshot.
import-snapshot=Import…

# LOCALIZATION NOTE (clear-snapshots.tooltip): The tooltip for the button that
# deletes existing snapshot.
clear-snapshots.tooltip=Delete all snapshots

# LOCALIZATION NOTE (diff-snapshots.tooltip): The tooltip for the button that
# initiates selecting two snapshots to diff with each other.
diff-snapshots.tooltip=Compare snapshots

# LOCALIZATION NOTE (filter.placeholder): The placeholder text used for the
# memory tool's filter search box.
filter.placeholder=Filter

# LOCALIZATION NOTE (filter.tooltip): The tooltip text used for the memory
# tool's filter search box.
filter.tooltip=Filter the contents of the snapshot

# LOCALIZATION NOTE (tree-item.view-individuals.tooltip): The tooltip for the
# button to view individuals in this group.
tree-item.view-individuals.tooltip=View individual nodes in this group and their retaining paths

# LOCALIZATION NOTE (tree-item.load-more): The label for the links to fetch the
# lazily loaded sub trees in the dominator tree view.
tree-item.load-more=Load more…

# LOCALIZATION NOTE (tree-item.rootlist): The label for the root of the
# dominator tree.
tree-item.rootlist=GC Roots

# LOCALIZATION NOTE (tree-item.nostack): The label describing the row in the heap tree
# that represents a row broken down by call stack when no stack was available.
tree-item.nostack=(no stack available)

# LOCALIZATION NOTE (tree-item.nofilename): The label describing the row in the
# heap tree that represents a row broken down by filename when no filename was
# available.
tree-item.nofilename=(no filename available)

# LOCALIZATION NOTE (tree-item.root): The label describing the row in the heap tree
# that represents the root of the tree when inverted.
tree-item.root=(root)

# LOCALIZATION NOTE (tree-item.percent2): A percent of bytes or count displayed in the tree view.
# there are two "%" after %S to escape and display "%"
tree-item.percent2=%S%%

# LOCALIZATION NOTE (diffing.baseline): The name of the baseline snapshot in a
# diffing comparison.
diffing.baseline=Baseline

# LOCALIZATION NOTE (diffing.comparison): The name of the snapshot being
# compared to the baseline in a diffing comparison.
diffing.comparison=Comparison

# LOCALIZATION NOTE (diffing.prompt.selectBaseline): The prompt to select the
# first snapshot when doing a diffing comparison.
diffing.prompt.selectBaseline=Select the baseline snapshot

# LOCALIZATION NOTE (diffing.prompt.selectComparison): The prompt to select the
# second snapshot when doing a diffing comparison.
diffing.prompt.selectComparison=Select the snapshot to compare to the baseline

# LOCALIZATION NOTE (diffing.state.error): The label describing the diffing
# state ERROR, used in the snapshot list when an error occurs while diffing two
# snapshots.
diffing.state.error=Error

# LOCALIZATION NOTE (diffing.state.error.full): The text describing the diffing
# state ERROR, used in the main view when an error occurs while diffing two
# snapshots.
diffing.state.error.full=There was an error while comparing snapshots.

# LOCALIZATION NOTE (diffing.state.taking-diff): The label describing the diffin
# state TAKING_DIFF, used in the snapshots list when computing the difference
# between two snapshots.
diffing.state.taking-diff=Computing difference…

# LOCALIZATION NOTE (diffing.state.taking-diff.full): The label describing the
# diffing state TAKING_DIFF, used in the main view when computing the difference
# between two snapshots.
diffing.state.taking-diff.full=Computing difference…

# LOCALIZATION NOTE (diffing.state.selecting): The label describing the diffing
# state SELECTING.
diffing.state.selecting=Select two snapshots to compare

# LOCALIZATION NOTE (diffing.state.selecting.full): The label describing the
# diffing state SELECTING, used in the main view when selecting snapshots to
# diff.
diffing.state.selecting.full=Select two snapshots to compare

# LOCALIZATION NOTE (dominatorTree.state.computing): The label describing the
# dominator tree state COMPUTING.
dominatorTree.state.computing=Generating dominators report…

# LOCALIZATION NOTE (dominatorTree.state.computing.full): The label describing the
# dominator tree state COMPUTING, used in the dominator tree view.
dominatorTree.state.computing.full=Generating dominators report…

# LOCALIZATION NOTE (dominatorTree.state.fetching): The label describing the
# dominator tree state FETCHING.
dominatorTree.state.fetching=Computing sizes…

# LOCALIZATION NOTE (dominatorTree.state.fetching.full): The label describing the
# dominator tree state FETCHING, used in the dominator tree view.
dominatorTree.state.fetching.full=Computing dominator’s retained sizes…

# LOCALIZATION NOTE (dominatorTree.state.incrementalFetching): The label
# describing the dominator tree state INCREMENTAL_FETCHING.
dominatorTree.state.incrementalFetching=Fetching…

# LOCALIZATION NOTE (dominatorTree.state.incrementalFetching.full): The label describing the
# dominator tree state INCREMENTAL_FETCHING, used in the dominator tree view.
dominatorTree.state.incrementalFetching.full=Fetching more…

# LOCALIZATION NOTE (dominatorTree.state.error): The label describing the
# dominator tree state ERROR.
dominatorTree.state.error=Error

# LOCALIZATION NOTE (dominatorTree.state.error.full): The label describing the
# dominator tree state ERROR, used in the dominator tree view.
dominatorTree.state.error.full=There was an error while processing the dominator tree

# LOCALIZATION NOTE (snapshot.state.saving.full): The label describing the
# snapshot state SAVING, used in the main heap view.
snapshot.state.saving.full=Saving snapshot…

# LOCALIZATION NOTE (snapshot.state.importing.full): The label describing the
# snapshot state IMPORTING, used in the main heap view.
snapshot.state.importing.full=Importing…

# LOCALIZATION NOTE (snapshot.state.reading.full): The label describing the
# snapshot state READING, and SAVED, due to these states being combined
# visually, used in the main heap view.
snapshot.state.reading.full=Reading snapshot…

# LOCALIZATION NOTE (snapshot.state.saving-census.full): The label describing
# the snapshot state SAVING, used in the main heap view.
snapshot.state.saving-census.full=Generating aggregate report…

# LOCALIZATION NOTE (snapshot.state.saving-tree-map.full): The label describing
# the snapshot state SAVING, used in the main heap view.
snapshot.state.saving-tree-map.full=Saving tree map…

# LOCALIZATION NOTE (snapshot.state.error.full): The label describing the
# snapshot state ERROR, used in the main heap view.
snapshot.state.error.full=There was an error processing this snapshot.

# LOCALIZATION NOTE (individuals.state.error): The short message displayed when
# there is an error fetching individuals from a group.
individuals.state.error=Error

# LOCALIZATION NOTE (individuals.state.error.full): The longer message displayed
# when there is an error fetching individuals from a group.
individuals.state.error.full=There was an error while fetching individuals in the group

# LOCALIZATION NOTE (individuals.state.fetching): The short message displayed
# while fetching individuals.
individuals.state.fetching=Fetching…

# LOCALIZATION NOTE (individuals.state.fetching.full): The longer message
# displayed while fetching individuals.
individuals.state.fetching.full=Fetching individuals in group…

# LOCALIZATION NOTE (individuals.field.node): The header label for an individual
# node.
individuals.field.node=Node

# LOCALIZATION NOTE (individuals.field.node.tooltip): The tooltip for the header
# label for an individual node.
individuals.field.node.tooltip=The individual node in the snapshot

# LOCALIZATION NOTE (snapshot.state.saving): The label describing the snapshot
# state SAVING, used in the snapshot list view
snapshot.state.saving=Saving snapshot…

# LOCALIZATION NOTE (snapshot.state.importing): The label describing the
# snapshot state IMPORTING, used in the snapshot list view
snapshot.state.importing=Importing snapshot…

# LOCALIZATION NOTE (snapshot.state.reading): The label describing the snapshot
# state READING, and SAVED, due to these states being combined visually, used in
# the snapshot list view.
snapshot.state.reading=Reading snapshot…

# LOCALIZATION NOTE (snapshot.state.saving-census): The label describing the
# snapshot state SAVING, used in snapshot list view.
snapshot.state.saving-census=Saving report…

# LOCALIZATION NOTE (snapshot.state.saving-census): The label describing the
# snapshot state SAVING, used in snapshot list view.
snapshot.state.saving-tree-map=Saving tree map…

# LOCALIZATION NOTE (snapshot.state.error): The label describing the snapshot
# state ERROR, used in the snapshot list view.
snapshot.state.error=Error

# LOCALIZATION NOTE (heapview.no-difference): Message displayed when there is no
# difference between two snapshots.
heapview.no-difference=No difference between the baseline and comparison.

# LOCALIZATION NOTE (heapview.none-match): Message displayed when there are no
# matches when filtering.
heapview.none-match=No matches.

# LOCALIZATION NOTE (heapview.none-match): Message displayed when there report
# is empty.
heapview.empty=Empty.

# LOCALIZATION NOTE (heapview.noAllocationStacks): The message displayed to
# users when selecting a display by "call stack" but no call stacks
# were recorded in the heap snapshot.
heapview.noAllocationStacks=No call stacks found. Record call stacks before taking a snapshot.

# LOCALIZATION NOTE (heapview.field.retainedSize): The name of the column in the
# dominator tree view for retained byte sizes.
heapview.field.retainedSize=Retained Size (Bytes)

# LOCALIZATION NOTE (heapview.field.retainedSize.tooltip): The tooltip for the
# column header in the dominator tree view for retained byte sizes.
heapview.field.retainedSize.tooltip=The sum of the size of the object itself, and the sizes of all the other objects kept alive by it

# LOCALIZATION NOTE (heapview.field.shallowSize): The name of the column in the
# dominator tree view for shallow byte sizes.
heapview.field.shallowSize=Shallow Size (Bytes)

# LOCALIZATION NOTE (heapview.field.shallowSize.tooltip): The tooltip for the
# column header in the dominator tree view for shallow byte sizes.
heapview.field.shallowSize.tooltip=The size of the object itself

# LOCALIZATION NOTE (dominatortree.field.label): The name of the column in the
# dominator tree for an object's label.
dominatortree.field.label=Dominator

# LOCALIZATION NOTE (dominatortree.field.label.tooltip): The tooltip for the column
# header in the dominator tree view for an object's label.
dominatortree.field.label.tooltip=The label for an object in memory

# LOCALIZATION NOTE (heapview.field.bytes): The name of the column in the heap
# view for bytes.
heapview.field.bytes=Bytes

# LOCALIZATION NOTE (heapview.field.bytes.tooltip): The tooltip for the column
# header in the heap view for bytes.
heapview.field.bytes.tooltip=The number of bytes taken up by this group, excluding subgroups

# LOCALIZATION NOTE (heapview.field.count): The name of the column in the heap
# view for count.
heapview.field.count=Count

# LOCALIZATION NOTE (heapview.field.count.tooltip): The tooltip for the column
# header in the heap view for count.
heapview.field.count.tooltip=The number of reachable objects in this group, excluding subgroups

# LOCALIZATION NOTE (heapview.field.totalbytes): The name of the column in the
# heap view for total bytes.
heapview.field.totalbytes=Total Bytes

# LOCALIZATION NOTE (heapview.field.totalbytes.tooltip): The tooltip for the
# column header in the heap view for total bytes.
heapview.field.totalbytes.tooltip=The number of bytes taken up by this group, including subgroups

# LOCALIZATION NOTE (heapview.field.totalcount): The name of the column in the
# heap view for total count.
heapview.field.totalcount=Total Count

# LOCALIZATION NOTE (heapview.field.totalcount.tooltip): The tooltip for the
# column header in the heap view for total count.
heapview.field.totalcount.tooltip=The number of reachable objects in this group, including subgroups

# LOCALIZATION NOTE (heapview.field.name): The name of the column in the heap
# view for name.
heapview.field.name=Group

# LOCALIZATION NOTE (heapview.field.name.tooltip): The tooltip for the column
# header in the heap view for name.
heapview.field.name.tooltip=The name of this group

# LOCALIZATION NOTE (shortest-paths.header): The header label for the shortest
# paths pane.
shortest-paths.header=Retaining Paths (from Garbage Collector Roots)

# LOCALIZATION NOTE (shortest-paths.select-node): The message displayed in the
# shortest paths pane when a node is not yet selected.
shortest-paths.select-node=Select an item to view its retaining paths

# LOCALIZATION NOTE (tree-map.node-count): The label for the count value of a
# node in the tree map
tree-map.node-count=count
PK
!<S}��:chrome/en-US/locale/en-US/devtools/client/menus.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

devtoolsServiceWorkers.label = Service Workers
devtoolsServiceWorkers.accesskey = k

devtoolsConnect.label = Connect…
devtoolsConnect.accesskey = C

browserConsoleCmd.label = Browser Console
browserConsoleCmd.accesskey = B

responsiveDesignMode.label = Responsive Design Mode
responsiveDesignMode.accesskey = R

eyedropper.label = Eyedropper
eyedropper.accesskey = Y

# LOCALIZATION NOTE (scratchpad.label): This menu item label appears
# in the Tools menu. See bug 653093.
# The Scratchpad is intended to provide a simple text editor for creating
# and evaluating bits of JavaScript code for the purposes of function
# prototyping, experimentation and convenient scripting.
#
# It's quite possible that you won't have a good analogue for the word
# "Scratchpad" in your locale. You should feel free to find a close
# approximation to it or choose a word (or words) that means
# "simple discardable text editor".
scratchpad.label = Scratchpad
scratchpad.accesskey = s

# LOCALIZATION NOTE (browserToolboxMenu.label): This is the label for the
# application menu item that opens the browser toolbox UI in the Tools menu.
browserToolboxMenu.label = Browser Toolbox
browserToolboxMenu.accesskey = e

# LOCALIZATION NOTE (browserContentToolboxMenu.label): This is the label for the
# application menu item that opens the browser content toolbox UI in the Tools menu.
# This toolbox allows to debug the chrome of the content process in multiprocess builds.
browserContentToolboxMenu.label = Browser Content Toolbox
browserContentToolboxMenu.accesskey = x

devToolbarMenu.label = Developer Toolbar
devToolbarMenu.accesskey = v

webide.label = WebIDE
webide.accesskey = W

devToolboxMenuItem.label = Toggle Tools
devToolboxMenuItem.accesskey = T

getMoreDevtoolsCmd.label = Get More Tools
getMoreDevtoolsCmd.accesskey = M
PK
!<��,	T�T�?chrome/en-US/locale/en-US/devtools/client/netmonitor.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE These strings are used inside the Network Monitor
# which is available from the Web Developer sub-menu -> 'Network Monitor'.
# The correct localization of this file might be to keep it in
# English, or another language commonly spoken among web developers.
# You want to make that choice consistent across the developer tools.
# A good criteria is the language in which you'd find the best
# documentation on web development on the web.

# LOCALIZATION NOTE (netmonitor.security.state.secure)
# This string is used as an tooltip for request that was performed over secure
# channel i.e. the connection was encrypted.
netmonitor.security.state.secure=The connection used to fetch this resource was secure.

# LOCALIZATION NOTE (netmonitor.security.state.insecure)
# This string is used as an tooltip for request that was performed over insecure
# channel i.e. the connection was not https
netmonitor.security.state.insecure=The connection used to fetch this resource was not secure.

# LOCALIZATION NOTE (netmonitor.security.state.broken)
# This string is used as an tooltip for request that failed due to security
# issues.
netmonitor.security.state.broken=A security error prevented the resource from being loaded.

# LOCALIZATION NOTE (netmonitor.security.state.weak)
# This string is used as an tooltip for request that had minor security issues
netmonitor.security.state.weak=This resource was transferred over a connection that used weak encryption.

# LOCALIZATION NOTE (netmonitor.security.enabled):
# This string is used to indicate that a specific security feature is used by
# a connection in the security details tab.
# For example: "HTTP Strict Transport Security: Enabled"
netmonitor.security.enabled=Enabled

# LOCALIZATION NOTE (netmonitor.security.disabled):
# This string is used to indicate that a specific security feature is not used by
# a connection in the security details tab.
# For example: "HTTP Strict Transport Security: Disabled"
netmonitor.security.disabled=Disabled

# LOCALIZATION NOTE (netmonitor.security.hostHeader):
# This string is used as a header for section containing security information
# related to the remote host. %S is replaced with the domain name of the remote
# host. For example: Host example.com
netmonitor.security.hostHeader=Host %S:

# LOCALIZATION NOTE (netmonitor.security.notAvailable):
# This string is used to indicate that a certain piece of information is not
# available to be displayed. For example a certificate that has no organization
# defined:
#   Organization: <Not Available>
netmonitor.security.notAvailable=<Not Available>

# LOCALIZATION NOTE (collapseDetailsPane): This is the tooltip for the button
# that collapses the network details pane in the UI.
collapseDetailsPane=Hide request details

# LOCALIZATION NOTE (expandDetailsPane): This is the tooltip for the button
# that expands the network details pane in the UI.
expandDetailsPane=Show request details

# LOCALIZATION NOTE (headersEmptyText): This is the text displayed in the
# headers tab of the network details pane when there are no headers available.
headersEmptyText=No headers for this request

# LOCALIZATION NOTE (headersFilterText): This is the text displayed in the
# headers tab of the network details pane for the filtering input.
headersFilterText=Filter headers

# LOCALIZATION NOTE (cookiesEmptyText): This is the text displayed in the
# cookies tab of the network details pane when there are no cookies available.
cookiesEmptyText=No cookies for this request

# LOCALIZATION NOTE (cookiesFilterText): This is the text displayed in the
# cookies tab of the network details pane for the filtering input.
cookiesFilterText=Filter cookies

# LOCALIZATION NOTE (paramsEmptyText): This is the text displayed in the
# params tab of the network details pane when there are no params available.
paramsEmptyText=No parameters for this request

# LOCALIZATION NOTE (paramsFilterText): This is the text displayed in the
# params tab of the network details pane for the filtering input.
paramsFilterText=Filter request parameters

# LOCALIZATION NOTE (paramsQueryString): This is the label displayed
# in the network details params tab identifying the query string.
paramsQueryString=Query string

# LOCALIZATION NOTE (paramsFormData): This is the label displayed
# in the network details params tab identifying the form data.
paramsFormData=Form data

# LOCALIZATION NOTE (paramsPostPayload): This is the label displayed
# in the network details params tab identifying the request payload.
paramsPostPayload=Request payload

# LOCALIZATION NOTE (requestHeaders): This is the label displayed
# in the network details headers tab identifying the request headers.
requestHeaders=Request headers

# LOCALIZATION NOTE (requestHeadersFromUpload): This is the label displayed
# in the network details headers tab identifying the request headers from
# the upload stream of a POST request's body.
requestHeadersFromUpload=Request headers from upload stream

# LOCALIZATION NOTE (responseHeaders): This is the label displayed
# in the network details headers tab identifying the response headers.
responseHeaders=Response headers

# LOCALIZATION NOTE (requestCookies): This is the label displayed
# in the network details params tab identifying the request cookies.
requestCookies=Request cookies

# LOCALIZATION NOTE (responseCookies): This is the label displayed
# in the network details params tab identifying the response cookies.
responseCookies=Response cookies

# LOCALIZATION NOTE (responsePayload): This is the label displayed
# in the network details response tab identifying the response payload.
responsePayload=Response payload

# LOCALIZATION NOTE (jsonFilterText): This is the text displayed
# in the response tab of the network details pane for the JSON filtering input.
jsonFilterText=Filter properties

# LOCALIZATION NOTE (jsonScopeName): This is the text displayed
# in the response tab of the network details pane for a JSON scope.
jsonScopeName=JSON

# LOCALIZATION NOTE (jsonpScopeName): This is the text displayed
# in the response tab of the network details pane for a JSONP scope.
jsonpScopeName=JSONP → callback %S()

# LOCALIZATION NOTE (responsePreview): This is the text displayed
# in the response tab of the network details pane for an HTML preview.
responsePreview=Preview

# LOCALIZATION NOTE (networkMenu.sortedAsc): This is the tooltip displayed
# in the network table toolbar, for any column that is sorted ascending.
networkMenu.sortedAsc=Sorted ascending

# LOCALIZATION NOTE (networkMenu.sortedDesc): This is the tooltip displayed
# in the network table toolbar, for any column that is sorted descending.
networkMenu.sortedDesc=Sorted descending

# LOCALIZATION NOTE (networkMenu.summary.tooltip.perf): A tooltip explaining
# what the perf button does
networkMenu.summary.tooltip.perf=Start performance analysis

# LOCALIZATION NOTE (networkMenu.summary.tooltip.domContentLoaded): A tooltip explaining
# what the DOMContentLoaded label displays
networkMenu.summary.tooltip.domContentLoaded=Time when “DOMContentLoad” event occurred

# LOCALIZATION NOTE (networkMenu.summary.tooltip.load): A tooltip explaining
# what the load label displays
networkMenu.summary.tooltip.load=Time when “load” event occurred

# LOCALIZATION NOTE (networkMenu.summary.requestsCount2): This label is displayed
# in the network table footer providing the number of requests
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
networkMenu.summary.requestsCount2=One request;#1 requests

# LOCALIZATION NOTE (networkMenu.summary.requestsCountEmpty): This label is displayed
# in the network table footer when there are no requests
networkMenu.summary.requestsCountEmpty=No requests

# LOCALIZATION NOTE (networkMenu.summary.tooltip.requestsCount): A tooltip explaining
# what the requestsCount label displays
networkMenu.summary.tooltip.requestsCount=Number of requests

# LOCALIZATION NOTE (networkMenu.summary.transferred): This label is displayed
# in the network table footer providing the transferred size.
networkMenu.summary.transferred=%S / %S transferred

# LOCALIZATION NOTE (networkMenu.summary.tooltip.transferred): A tooltip explaining
# what the transferred  label displays
networkMenu.summary.tooltip.transferred=Size/transferred size of all requests

# LOCALIZATION NOTE (networkMenu.summary.finish): This label is displayed
# in the network table footer providing the transfer time.
networkMenu.summary.finish=Finish: %S

# LOCALIZATION NOTE (networkMenu.summary.tooltip.finish): A tooltip explaining
# what the finish label displays
networkMenu.summary.tooltip.finish=Total time needed to load all requests

# LOCALIZATION NOTE (networkMenu.sizeB): This is the label displayed
# in the network menu specifying the size of a request (in bytes).
networkMenu.sizeB=%S B

# LOCALIZATION NOTE (networkMenu.sizeKB): This is the label displayed
# in the network menu specifying the size of a request (in kilobytes).
networkMenu.sizeKB=%S KB

# LOCALIZATION NOTE (networkMenu.sizeMB): This is the label displayed
# in the network menu specifying the size of a request (in megabytes).
networkMenu.sizeMB=%S MB

# LOCALIZATION NOTE (networkMenu.sizeGB): This is the label displayed
# in the network menu specifying the size of a request (in gigabytes).
networkMenu.sizeGB=%S GB

# LOCALIZATION NOTE (networkMenu.sizeUnavailable): This is the label displayed
# in the network menu specifying the transferred size of a request is
# unavailable.
networkMenu.sizeUnavailable=—

# LOCALIZATION NOTE (networkMenu.sizeCached): This is the label displayed
# in the network menu specifying the transferred of a request is
# cached.
networkMenu.sizeCached=cached

# LOCALIZATION NOTE (networkMenu.sizeServiceWorker): This is the label displayed
# in the network menu specifying the transferred of a request computed
# by a service worker.
networkMenu.sizeServiceWorker=service worker

# LOCALIZATION NOTE (networkMenu.totalMS): This is the label displayed
# in the network menu specifying the time for a request to finish (in milliseconds).
networkMenu.totalMS=→ %S ms

# This string is used to concatenate tooltips (netmonitor.waterfall.tooltip.*)
# in the requests waterfall for total time (in milliseconds). \\u0020 represents
# a whitespace. You can replace this with a different character, e.g. an hyphen
# or a period, if a comma doesn't work for your language.
netmonitor.waterfall.tooltip.separator=,\u0020

# LOCALIZATION NOTE (netmonitor.waterfall.tooltip.total): This is part of the tooltip
# displayed in the requests waterfall for total time (in milliseconds).
netmonitor.waterfall.tooltip.total=Total %S ms

# LOCALIZATION NOTE (netmonitor.waterfall.tooltip.blocked): This is part of the tooltip
# displayed in the requests waterfall for blocked time (in milliseconds).
netmonitor.waterfall.tooltip.blocked=Blocked %S ms

# LOCALIZATION NOTE (netmonitor.waterfall.tooltip.dns): This is part of the tooltip
# displayed in the requests waterfall for dns time (in milliseconds).
netmonitor.waterfall.tooltip.dns=DNS %S ms

# LOCALIZATION NOTE (netmonitor.waterfall.tooltip.ssl): This is part of the tooltip
# displayed in the requests waterfall for tls setup time (in milliseconds).
netmonitor.waterfall.tooltip.ssl=TLS %S ms

# LOCALIZATION NOTE (netmonitor.waterfall.tooltip.connect): This is part of the tooltip
# displayed in the requests waterfall for connect time (in milliseconds).
netmonitor.waterfall.tooltip.connect=Connect %S ms

# LOCALIZATION NOTE (netmonitor.waterfall.tooltip.send): This is part of the tooltip
# displayed in the requests waterfall for send time (in milliseconds).
netmonitor.waterfall.tooltip.send=Send %S ms

# LOCALIZATION NOTE (netmonitor.waterfall.tooltip.wait): This is part of the tooltip
# displayed in the requests waterfall for wait time (in milliseconds).
netmonitor.waterfall.tooltip.wait=Wait %S ms

# LOCALIZATION NOTE (netmonitor.waterfall.tooltip.receive): This is part of the tooltip
# displayed in the requests waterfall for receive time (in milliseiconds).
netmonitor.waterfall.tooltip.receive=Receive %S ms

# LOCALIZATION NOTE (networkMenu.millisecond): This is the label displayed
# in the network menu specifying timing interval divisions (in milliseconds).
networkMenu.millisecond=%S ms

# LOCALIZATION NOTE (networkMenu.second): This is the label displayed
# in the network menu specifying timing interval divisions (in seconds).
networkMenu.second=%S s

# LOCALIZATION NOTE (networkMenu.minute): This is the label displayed
# in the network menu specifying timing interval divisions (in minutes).
networkMenu.minute=%S min

# LOCALIZATION NOTE (pieChart.loading): This is the label displayed
# for pie charts (e.g., in the performance analysis view) when there is
# no data available yet.
pieChart.loading=Loading

# LOCALIZATION NOTE (pieChart.unavailable): This is the label displayed
# for pie charts (e.g., in the performance analysis view) when there is
# no data available, even after loading it.
pieChart.unavailable=Empty

# LOCALIZATION NOTE (tableChart.loading): This is the label displayed
# for table charts (e.g., in the performance analysis view) when there is
# no data available yet.
tableChart.loading=Please wait…

# LOCALIZATION NOTE (tableChart.unavailable): This is the label displayed
# for table charts (e.g., in the performance analysis view) when there is
# no data available, even after loading it.
tableChart.unavailable=No data available

# LOCALIZATION NOTE (charts.sizeKB): This is the label displayed
# in pie or table charts specifying the size of a request (in kilobytes).
charts.sizeKB=%S KB

# LOCALIZATION NOTE (charts.transferredSizeKB): This is the label displayed
# in pie or table charts specifying the size of a transferred request (in kilobytes).
charts.transferredSizeKB=%S KB

# LOCALIZATION NOTE (charts.totalS): This is the label displayed
# in pie or table charts specifying the time for a request to finish (in seconds).
charts.totalS=%S s

# LOCALIZATION NOTE (charts.totalTransferredSize): This is the label displayed
# in the performance analysis view for total transferred size, in kilobytes.
charts.totalTransferredSize=Transferred Size: %S KB

# LOCALIZATION NOTE (charts.cacheEnabled): This is the label displayed
# in the performance analysis view for "cache enabled" charts.
charts.cacheEnabled=Primed cache

# LOCALIZATION NOTE (charts.cacheDisabled): This is the label displayed
# in the performance analysis view for "cache disabled" charts.
charts.cacheDisabled=Empty cache

# LOCALIZATION NOTE (charts.totalSize): This is the label displayed
# in the performance analysis view for total requests size, in kilobytes.
charts.totalSize=Size: %S KB

# LOCALIZATION NOTE (charts.totalSeconds): Semi-colon list of plural forms.
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
# This is the label displayed in the performance analysis view for the
# total requests time, in seconds.
charts.totalSeconds=Time: #1 second;Time: #1 seconds

# LOCALIZATION NOTE (charts.totalSecondsNonBlocking): Semi-colon list of plural forms.
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
# This is the label displayed in the performance analysis view for the
# total requests time (non-blocking), in seconds.
charts.totalSecondsNonBlocking=Non blocking time: #1 second;Non blocking time: #1 seconds

# LOCALIZATION NOTE (charts.totalCached): This is the label displayed
# in the performance analysis view for total cached responses.
charts.totalCached=Cached responses: %S

# LOCALIZATION NOTE (charts.totalCount): This is the label displayed
# in the performance analysis view for total requests.
charts.totalCount=Total requests: %S

# LOCALIZATION NOTE (charts.size): This is the label displayed
# in the header column in the performance analysis view for size of the request.
charts.size=Size

# LOCALIZATION NOTE (charts.type): This is the label displayed
# in the header column in the performance analysis view for type of request.
charts.type=Type

# LOCALIZATION NOTE (charts.transferred): This is the label displayed
# in the header column in the performance analysis view for transferred
# size of the request.
charts.transferred=Transferred

# LOCALIZATION NOTE (charts.time): This is the label displayed
# in the header column in the performance analysis view for time of request.
charts.time=Time

# LOCALIZATION NOTE (charts.nonBlockingTime): This is the label displayed
# in the header column in the performance analysis view for non blocking
# time of request.
charts.nonBlockingTime=Non blocking time

# LOCALIZATION NOTE (netRequest.headers): A label used for Headers tab
# This tab displays list of HTTP headers
netRequest.headers=Headers

# LOCALIZATION NOTE (netRequest.response): A label used for Response tab
# This tab displays HTTP response body
netRequest.response=Response

# LOCALIZATION NOTE (netRequest.rawData): A label used for a section
# in Response tab. This section displays raw response body as it's
# been received from the backend (debugger server)
netRequest.rawData=Raw Data

# LOCALIZATION NOTE (netRequest.xml): A label used for a section
# in Response tab. This section displays parsed XML response body.
netRequest.xml=XML

# LOCALIZATION NOTE (netRequest.image): A label used for a section
# in Response tab. This section displays images returned in response body.
netRequest.image=Image

# LOCALIZATION NOTE (netRequest.sizeLimitMessage): A label used
# in Response and Post tabs in case the body is bigger than given limit.
# It allows the user to click and fetch more from the backend.
# The {{link}} will be replace at run-time by an active link.
# String with ID 'netRequest.sizeLimitMessageLink' will be used as text
# for this link.
netRequest.sizeLimitMessage=Size limit has been reached. Click {{link}} to load more.
netRequest.sizeLimitMessageLink=here

# LOCALIZATION NOTE (netRequest.responseBodyDiscarded): A label used
# in Response tab if the response body is not available.
netRequest.responseBodyDiscarded=Response body was not stored.

# LOCALIZATION NOTE (netRequest.requestBodyDiscarded): A label used
# in Post tab if the post body is not available.
netRequest.requestBodyDiscarded=Request POST body was not stored.

# LOCALIZATION NOTE (netRequest.post): A label used for Post tab
# This tab displays HTTP post body
netRequest.post=POST

# LOCALIZATION NOTE (netRequest.cookies): A label used for Cookies tab
# This tab displays request and response cookies.
netRequest.cookies=Cookies

# LOCALIZATION NOTE (netRequest.params): A label used for URL parameters tab
# This tab displays data parsed from URL query string.
netRequest.params=Params

# LOCALIZATION NOTE (netRequest.callstack): A label used for request stacktrace tab
# This tab displays the request's JavaScript stack trace. Should be identical to
# debuggerUI.tabs.callstack
netRequest.callstack=Call Stack

# LOCALIZATION NOTE (certmgr.subjectinfo.label):
# A label used for a certificate section in security tab.
# This section displays Name and organization who has been assigned the fingerprints
certmgr.subjectinfo.label=Issued To

# LOCALIZATION NOTE (certmgr.certdetail.cn):
# A label used for Issued To and Issued By sub-section in security tab
certmgr.certdetail.cn=Common Name (CN):

# LOCALIZATION NOTE (certmgr.certdetail.o):
# A label used for Issued To and Issued By sub-section in security tab
certmgr.certdetail.o=Organization (O):

# LOCALIZATION NOTE (certmgr.certdetail.ou):
# A label used for Issued To and Issued By sub-section in security tab
certmgr.certdetail.ou=Organizational Unit (OU):

# LOCALIZATION NOTE (certmgr.issuerinfo.label):
# A label used for a certificate section in security tab
# This section displays Name and organization who issued the fingerprints
certmgr.issuerinfo.label=Issued By

# LOCALIZATION NOTE (certmgr.periodofvalidity.label):
# A label used for a certificate section in security tab
# This section displays the valide period of this fingerprints
certmgr.periodofvalidity.label=Period of Validity

# LOCALIZATION NOTE (certmgr.begins):
# A label used for Period of Validity sub-section in security tab
certmgr.begins=Begins On:

# LOCALIZATION NOTE (certmgr.expires):
# A label used for Period of Validity sub-section in security tab
certmgr.expires=Expires On:

# LOCALIZATION NOTE (certmgr.fingerprints.label):
# A label used for a certificate section in security tab
# This section displays the valide period of this fingerprints
certmgr.fingerprints.label=Fingerprints

# LOCALIZATION NOTE (certmgr.certdetail.sha256fingerprint):
# A label used for Fingerprints sub-section in security tab
certmgr.certdetail.sha256fingerprint=SHA-256 Fingerprint:

# LOCALIZATION NOTE (certmgr.certdetail.sha1fingerprint):
# A label used for Fingerprints sub-section in security tab
certmgr.certdetail.sha1fingerprint=SHA1 Fingerprint:

# LOCALIZATION NOTE (netmonitor.perfNotice1/2/3): These are the labels displayed
# in the network table when empty to start performance analysis.
netmonitor.perfNotice1=• Click on the
netmonitor.perfNotice2=button to start performance analysis.
netmonitor.perfNotice3=Analyze

# LOCALIZATION NOTE (netmonitor.reload1/2/3): These are the labels displayed
# in the network table when empty to start logging network requests.
netmonitor.reloadNotice1=• Perform a request or
netmonitor.reloadNotice2=Reload
netmonitor.reloadNotice3=the page to see detailed information about network activity.

# LOCALIZATION NOTE (netmonitor.toolbar.status3): This is the label displayed
# in the network table toolbar, above the "status" column.
netmonitor.toolbar.status3=Status

# LOCALIZATION NOTE (netmonitor.toolbar.method): This is the label displayed
# in the network table toolbar, above the "method" column.
netmonitor.toolbar.method=Method

# LOCALIZATION NOTE (netmonitor.toolbar.file): This is the label displayed
# in the network table toolbar, above the "file" column.
netmonitor.toolbar.file=File

# LOCALIZATION NOTE (netmonitor.toolbar.protocol): This is the label displayed
# in the network table toolbar, above the "protocol" column.
netmonitor.toolbar.protocol=Protocol

# LOCALIZATION NOTE (netmonitor.toolbar.domain): This is the label displayed
# in the network table toolbar, above the "domain" column.
netmonitor.toolbar.domain=Domain

# LOCALIZATION NOTE (netmonitor.toolbar.remoteip): This is the label displayed
# in the network table toolbar, above the "remoteip" column.
netmonitor.toolbar.remoteip=Remote IP

# LOCALIZATION NOTE (netmonitor.toolbar.cause): This is the label displayed
# in the network table toolbar, above the "cause" column.
netmonitor.toolbar.cause=Cause

# LOCALIZATION NOTE (netmonitor.toolbar.type): This is the label displayed
# in the network table toolbar, above the "type" column.
netmonitor.toolbar.type=Type

# LOCALIZATION NOTE (netmonitor.toolbar.cookies): This is the label displayed
# in the network table toolbar, above the "cookies" column.
netmonitor.toolbar.cookies=Cookies

# LOCALIZATION NOTE (netmonitor.toolbar.setCookies): This is the label displayed
# in the network table toolbar, above the "set cookies" column.
# Set-Cookie is a HTTP response header. This string is the plural form of it.
# See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
netmonitor.toolbar.setCookies=Set-Cookies

# LOCALIZATION NOTE (netmonitor.toolbar.scheme): This is the label displayed
# in the network table toolbar, above the "scheme" column.
netmonitor.toolbar.scheme=Scheme

# LOCALIZATION NOTE (netmonitor.toolbar.startTime): This is the label displayed
# in the network table toolbar, above the "start time" column, which is the time
# from start of 1st request until the start of this request.
netmonitor.toolbar.startTime=Start Time

# LOCALIZATION NOTE (netmonitor.toolbar.endTime): This is the label displayed
# in the network table toolbar, above the "end time" column, which is the time
# from start of 1st request until the end of this response.
netmonitor.toolbar.endTime=End Time

# LOCALIZATION NOTE (netmonitor.toolbar.responseTime): This is the label displayed
# in the network table toolbar, above the "response time" column, which is the time
# from start of 1st request until the beginning of download of this response.
netmonitor.toolbar.responseTime=Response Time

# LOCALIZATION NOTE (netmonitor.toolbar.duration): This is the label displayed
# in the network table toolbar, above the "duration" column, which is the time
# from start of this request until the end of this response.
netmonitor.toolbar.duration=Duration

# LOCALIZATION NOTE (netmonitor.toolbar.latency): This is the label displayed
# in the network table toolbar, above the "latency" column, which is the time
# from end of this request until the beginning of download of this response.
netmonitor.toolbar.latency=Latency

# LOCALIZATION NOTE (netmonitor.toolbar.transferred): This is the label displayed
# in the network table toolbar, above the "transferred" column, which is the
# compressed / encoded size.
netmonitor.toolbar.transferred=Transferred

# LOCALIZATION NOTE (netmonitor.toolbar.contentSize): This is the label displayed
# in the network table toolbar, above the "size" column, which is the
# uncompressed / decoded size.
netmonitor.toolbar.contentSize=Size

# LOCALIZATION NOTE (netmonitor.toolbar.waterfall): This is the label displayed
# in the network table toolbar, above the "waterfall" column.
netmonitor.toolbar.waterfall=Timeline

# LOCALIZATION NOTE (netmonitor.tab.headers): This is the label displayed
# in the network details pane identifying the headers tab.
netmonitor.tab.headers=Headers

# LOCALIZATION NOTE (netmonitor.tab.cookies): This is the label displayed
# in the network details pane identifying the cookies tab.
netmonitor.tab.cookies=Cookies

# LOCALIZATION NOTE (netmonitor.tab.params): This is the label displayed
# in the network details pane identifying the params tab.
netmonitor.tab.params=Params

# LOCALIZATION NOTE (netmonitor.tab.response): This is the label displayed
# in the network details pane identifying the response tab.
netmonitor.tab.response=Response

# LOCALIZATION NOTE (netmonitor.tab.timings): This is the label displayed
# in the network details pane identifying the timings tab.
netmonitor.tab.timings=Timings

# LOCALIZATION NOTE (netmonitor.tab.stackTrace): This is the label displayed
# in the network details pane identifying the stack-trace tab.
netmonitor.tab.stackTrace=Stack Trace

# LOCALIZATION NOTE (netmonitor.tab.security): This is the label displayed
# in the network details pane identifying the security tab.
netmonitor.tab.security=Security

# LOCALIZATION NOTE (netmonitor.toolbar.filter.all): This is the label displayed
# in the network toolbar for the "All" filtering button.
netmonitor.toolbar.filter.all=All

# LOCALIZATION NOTE (netmonitor.toolbar.filter.html): This is the label displayed
# in the network toolbar for the "HTML" filtering button.
netmonitor.toolbar.filter.html=HTML

# LOCALIZATION NOTE (netmonitor.toolbar.filter.css): This is the label displayed
# in the network toolbar for the "CSS" filtering button.
netmonitor.toolbar.filter.css=CSS

# LOCALIZATION NOTE (netmonitor.toolbar.filter.js): This is the label displayed
# in the network toolbar for the "JS" filtering button.
netmonitor.toolbar.filter.js=JS

# LOCALIZATION NOTE (netmonitor.toolbar.filter.xhr): This is the label displayed
# in the network toolbar for the "XHR" filtering button.
netmonitor.toolbar.filter.xhr=XHR

# LOCALIZATION NOTE (netmonitor.toolbar.filter.fonts): This is the label displayed
# in the network toolbar for the "Fonts" filtering button.
netmonitor.toolbar.filter.fonts=Fonts

# LOCALIZATION NOTE (netmonitor.toolbar.filter.images): This is the label displayed
# in the network toolbar for the "Images" filtering button.
netmonitor.toolbar.filter.images=Images

# LOCALIZATION NOTE (netmonitor.toolbar.filter.media): This is the label displayed
# in the network toolbar for the "Media" filtering button.
netmonitor.toolbar.filter.media=Media

# LOCALIZATION NOTE (netmonitor.toolbar.filter.flash): This is the label displayed
# in the network toolbar for the "Flash" filtering button.
netmonitor.toolbar.filter.flash=Flash

# LOCALIZATION NOTE (netmonitor.toolbar.filter.ws): This is the label displayed
# in the network toolbar for the "WS" filtering button.
netmonitor.toolbar.filter.ws=WS

# LOCALIZATION NOTE (netmonitor.toolbar.filter.other): This is the label displayed
# in the network toolbar for the "Other" filtering button.
netmonitor.toolbar.filter.other=Other

# LOCALIZATION NOTE (netmonitor.toolbar.filterFreetext.label): This is the label
# displayed in the network toolbar for the url filtering textbox.
netmonitor.toolbar.filterFreetext.label=Filter URLs

# LOCALIZATION NOTE (netmonitor.toolbar.filterFreetext.key): This is the
# shortcut key to focus on the toolbar url filtering textbox
netmonitor.toolbar.filterFreetext.key=CmdOrCtrl+F

# LOCALIZATION NOTE (netmonitor.toolbar.enablePersistentLogs.label): This is the label
# displayed for the checkbox for enabling persistent logs.
netmonitor.toolbar.enablePersistentLogs.label=Persist Logs

# LOCALIZATION NOTE (netmonitor.toolbar.enablePersistentLogs.tooltip): This is the tooltip
# displayed for the checkbox for enabling persistent logs.
netmonitor.toolbar.enablePersistentLogs.tooltip=If you enable this option the requests list will not be cleared each time you navigate to a new page

# LOCALIZATION NOTE (netmonitor.toolbar.disableCache.label): This is the label
# displayed for the checkbox for disabling browser cache.
netmonitor.toolbar.disableCache.label=Disable cache

# LOCALIZATION NOTE (netmonitor.toolbar.disableCache.tooltip): This is the tooltip
# displayed for the checkbox for disabling browser cache.
netmonitor.toolbar.disableCache.tooltip=Disable HTTP cache

# LOCALIZATION NOTE (netmonitor.toolbar.clear): This is the label displayed
# in the network toolbar for the "Clear" button.
netmonitor.toolbar.clear=Clear

# LOCALIZATION NOTE (netmonitor.toolbar.toggleRecording): This is the label displayed
# in the network toolbar for the toggle recording button.
netmonitor.toolbar.toggleRecording=Pause/Resume recording network log

# LOCALIZATION NOTE (netmonitor.toolbar.perf): This is the label displayed
# in the network toolbar for the performance analysis button.
netmonitor.toolbar.perf=Toggle performance analysis…

# LOCALIZATION NOTE (netmonitor.toolbar.resetColumns): This is the label
# displayed in the network table header context menu.
netmonitor.toolbar.resetColumns=Reset Columns

# LOCALIZATION NOTE (netmonitor.toolbar.timings): This is the label
# displayed in the network table header context menu for the timing submenu
netmonitor.toolbar.timings=Timings

# LOCALIZATION NOTE (netmonitor.toolbar.responseHeaders): This is the
# label displayed in the network table header context menu for the
# response headers submenu.
netmonitor.toolbar.responseHeaders=Response Headers

# LOCALIZATION NOTE (netmonitor.summary.url): This is the label displayed
# in the network details headers tab identifying the URL.
netmonitor.summary.url=Request URL:

# LOCALIZATION NOTE (netmonitor.summary.method): This is the label displayed
# in the network details headers tab identifying the method.
netmonitor.summary.method=Request method:

# LOCALIZATION NOTE (netmonitor.summary.address): This is the label displayed
# in the network details headers tab identifying the remote address.
netmonitor.summary.address=Remote address:

# LOCALIZATION NOTE (netmonitor.summary.status): This is the label displayed
# in the network details headers tab identifying the status code.
netmonitor.summary.status=Status code:

# LOCALIZATION NOTE (netmonitor.summary.version): This is the label displayed
# in the network details headers tab identifying the http version.
netmonitor.summary.version=Version:

# LOCALIZATION NOTE (netmonitor.summary.editAndResend): This is the label displayed
# on the button in the headers tab that opens a form to edit and resend the currently
# displayed request
netmonitor.summary.editAndResend=Edit and Resend

# LOCALIZATION NOTE (netmonitor.summary.rawHeaders): This is the label displayed
# on the button in the headers tab that toggle view for raw request/response headers
# from the currently displayed request
netmonitor.summary.rawHeaders=Raw headers

# LOCALIZATION NOTE (netmonitor.summary.rawHeaders.requestHeaders): This is the label displayed
# in the network details headers tab identifying the raw request headers textarea
netmonitor.summary.rawHeaders.requestHeaders=Request headers:

# LOCALIZATION NOTE (netmonitor.summary.rawHeaders.responseHeaders): This is the label displayed
# in the network details headers tab identifying the raw response headers textarea
netmonitor.summary.rawHeaders.responseHeaders=Response headers:

# LOCALIZATION NOTE (netmonitor.summary.size): This is the label displayed
# in the network details headers tab identifying the headers size.
netmonitor.summary.size=Headers size:

# LOCALIZATION NOTE (netmonitor.response.name): This is the label displayed
# in the network details response tab identifying an image's file name.
netmonitor.response.name=Name:

# LOCALIZATION NOTE (netmonitor.response.dimensions): This is the label displayed
# in the network details response tab identifying an image's dimensions.
netmonitor.response.dimensions=Dimensions:

# LOCALIZATION NOTE (netmonitor.response.mime): This is the label displayed
# in the network details response tab identifying an image's mime.
netmonitor.response.mime=MIME Type:

# LOCALIZATION NOTE (netmonitor.timings.blocked): This is the label displayed
# in the network details timings tab identifying the amount of time spent
# in a "blocked" state.
netmonitor.timings.blocked=Blocked:

# LOCALIZATION NOTE (netmonitor.timings.dns): This is the label displayed
# in the network details timings tab identifying the amount of time spent
# in a "dns" state.
netmonitor.timings.dns=DNS resolution:

# LOCALIZATION NOTE (netmonitor.timings.ssl): This is the label displayed
# in the network details timings tab identifying the amount of time spent
# in a "tls" handshake state.
netmonitor.timings.ssl=TLS setup:

# LOCALIZATION NOTE (netmonitor.timings.connect): This is the label displayed
# in the network details timings tab identifying the amount of time spent
# in a "connect" state.
netmonitor.timings.connect=Connecting:

# LOCALIZATION NOTE (netmonitor.timings.send): This is the label displayed
# in the network details timings tab identifying the amount of time spent
# in a "send" state.
netmonitor.timings.send=Sending:

# LOCALIZATION NOTE (netmonitor.timings.wait): This is the label displayed
# in the network details timings tab identifying the amount of time spent
# in a "wait" state.
netmonitor.timings.wait=Waiting:

# LOCALIZATION NOTE (netmonitor.timings.receive): This is the label displayed
# in the network details timings tab identifying the amount of time spent
# in a "receive" state.
netmonitor.timings.receive=Receiving:

# LOCALIZATION NOTE (netmonitor.security.warning.cipher): A tooltip
# for warning icon that indicates a connection uses insecure cipher suite.
netmonitor.security.warning.cipher=The cipher used for encryption is deprecated and insecure.

# LOCALIZATION NOTE (netmonitor.security.error): This is the label displayed
# in the security tab if a security error prevented the connection.
netmonitor.security.error=An error occured:

# LOCALIZATION NOTE (netmonitor.security.protocolVersion): This is the label displayed
# in the security tab describing TLS/SSL protocol version.
netmonitor.security.protocolVersion=Protocol version:

# LOCALIZATION NOTE (netmonitor.security.cipherSuite): This is the label displayed
# in the security tab describing the cipher suite used to secure this connection.
netmonitor.security.cipherSuite=Cipher suite:

# LOCALIZATION NOTE (netmonitor.security.keaGroup): This is the label displayed
# in the security tab describing the key exchange group suite used to secure
# this connection.
netmonitor.security.keaGroup=Key Exchange Group:

# LOCALIZATION NOTE (netmonitor.security.keaGroup.none): This is the label
# displayed in the security tab describing the case when no group was used.
netmonitor.security.keaGroup.none=none

# LOCALIZATION NOTE (netmonitor.security.keaGroup.custom): This is the label
# displayed in the security tab describing the case when a custom group was used.
netmonitor.security.keaGroup.custom=custom

# LOCALIZATION NOTE (netmonitor.security.keaGroup.unknown): This is the value
# displayed in the security tab describing an unknown group.
netmonitor.security.keaGroup.unknown=unknown group

# LOCALIZATION NOTE (netmonitor.security.signatureScheme): This is the label
# displayed in the security tab describing the signature scheme used by for
# the server certificate in this connection.
netmonitor.security.signatureScheme=Signature Scheme:

# LOCALIZATION NOTE (netmonitor.security.signatureScheme.none): This is the
# label displayed in the security tab describing the case when no signature
# was used.
netmonitor.security.signatureScheme.none=none

# LOCALIZATION NOTE (netmonitor.security.signatureScheme.unknown): This is the
# value displayed in the security tab describing an unknown signature scheme.
netmonitor.security.signatureScheme.unknown=unknown signature scheme

# LOCALIZATION NOTE (netmonitor.security.hsts): This is the label displayed
# in the security tab describing the usage of HTTP Strict Transport Security.
netmonitor.security.hsts=HTTP Strict Transport Security:

# LOCALIZATION NOTE (netmonitor.security.hpkp): This is the label displayed
# in the security tab describing the usage of Public Key Pinning.
netmonitor.security.hpkp=Public Key Pinning:

# LOCALIZATION NOTE (netmonitor.security.connection): This is the label displayed
# in the security tab describing the section containing information related to
# the secure connection.
netmonitor.security.connection=Connection:

# LOCALIZATION NOTE (netmonitor.security.certificate): This is the label displayed
# in the security tab describing the server certificate section.
netmonitor.security.certificate=Certificate:

# LOCALIZATION NOTE (netmonitor.context.copy): This is the label displayed
# for the copy sub-menu in the context menu for a request
netmonitor.context.copy=Copy

# LOCALIZATION NOTE (netmonitor.context.copy.accesskey): This is the access key
# for the copy sub-menu displayed in the context menu for a request
netmonitor.context.copy.accesskey=C

# LOCALIZATION NOTE (netmonitor.context.copyUrl): This is the label displayed
# on the context menu that copies the selected request's url
netmonitor.context.copyUrl=Copy URL

# LOCALIZATION NOTE (netmonitor.context.copyUrl.accesskey): This is the access key
# for the Copy URL menu item displayed in the context menu for a request
netmonitor.context.copyUrl.accesskey=U

# LOCALIZATION NOTE (netmonitor.context.copyUrlParams): This is the label displayed
# on the context menu that copies the selected request's url parameters
netmonitor.context.copyUrlParams=Copy URL Parameters

# LOCALIZATION NOTE (netmonitor.context.copyUrlParams.accesskey): This is the access key
# for the Copy URL Parameters menu item displayed in the context menu for a request
netmonitor.context.copyUrlParams.accesskey=P

# LOCALIZATION NOTE (netmonitor.context.copyPostData): This is the label displayed
# on the context menu that copies the selected request's post data
netmonitor.context.copyPostData=Copy POST Data

# LOCALIZATION NOTE (netmonitor.context.copyPostData.accesskey): This is the access key
# for the Copy POST Data menu item displayed in the context menu for a request
netmonitor.context.copyPostData.accesskey=D

# LOCALIZATION NOTE (netmonitor.context.copyAsCurl): This is the label displayed
# on the context menu that copies the selected request as a cURL command.
# The capitalization is part of the official name and should be used throughout all languages.
# http://en.wikipedia.org/wiki/CURL
netmonitor.context.copyAsCurl=Copy as cURL

# LOCALIZATION NOTE (netmonitor.context.copyAsCurl.accesskey): This is the access key
# for the Copy as cURL menu item displayed in the context menu for a request
netmonitor.context.copyAsCurl.accesskey=C

# LOCALIZATION NOTE (netmonitor.context.copyRequestHeaders): This is the label displayed
# on the context menu that copies the selected item's request headers
netmonitor.context.copyRequestHeaders=Copy Request Headers

# LOCALIZATION NOTE (netmonitor.context.copyRequestHeaders.accesskey): This is the access key
# for the Copy Request Headers menu item displayed in the context menu for a request
netmonitor.context.copyRequestHeaders.accesskey=Q

# LOCALIZATION NOTE (netmonitor.context.copyResponseHeaders): This is the label displayed
# on the context menu that copies the selected item's response headers
netmonitor.context.copyResponseHeaders=Copy Response Headers

# LOCALIZATION NOTE (netmonitor.context.copyResponseHeaders.accesskey): This is the access key
# for the Copy Response Headers menu item displayed in the context menu for a response
netmonitor.context.copyResponseHeaders.accesskey=S

# LOCALIZATION NOTE (netmonitor.context.copyResponse): This is the label displayed
# on the context menu that copies the selected response as a string
netmonitor.context.copyResponse=Copy Response

# LOCALIZATION NOTE (netmonitor.context.copyResponse.accesskey): This is the access key
# for the Copy Response menu item displayed in the context menu for a request
netmonitor.context.copyResponse.accesskey=R

# LOCALIZATION NOTE (netmonitor.context.copyImageAsDataUri): This is the label displayed
# on the context menu that copies the selected image as data uri
netmonitor.context.copyImageAsDataUri=Copy Image as Data URI

# LOCALIZATION NOTE (netmonitor.context.copyImageAsDataUri.accesskey): This is the access key
# for the Copy Image As Data URI menu item displayed in the context menu for a request
netmonitor.context.copyImageAsDataUri.accesskey=I

# LOCALIZATION NOTE (netmonitor.context.saveImageAs): This is the label displayed
# on the context menu that save the Image
netmonitor.context.saveImageAs=Save Image As

# LOCALIZATION NOTE (netmonitor.context.saveImageAs.accesskey): This is the access key
# for the Copy Image As Data URI menu item displayed in the context menu for a request
netmonitor.context.saveImageAs.accesskey=V


# LOCALIZATION NOTE (netmonitor.context.copyAllAsHar): This is the label displayed
# on the context menu that copies all as HAR format
netmonitor.context.copyAllAsHar=Copy All As HAR

# LOCALIZATION NOTE (netmonitor.context.copyAllAsHar.accesskey): This is the access key
# for the Copy All As HAR menu item displayed in the context menu for a network panel
netmonitor.context.copyAllAsHar.accesskey=O

# LOCALIZATION NOTE (netmonitor.context.saveAllAsHar): This is the label displayed
# on the context menu that saves all as HAR format
netmonitor.context.saveAllAsHar=Save All As HAR

# LOCALIZATION NOTE (netmonitor.context.saveAllAsHar.accesskey): This is the access key
# for the Save All As HAR menu item displayed in the context menu for a network panel
netmonitor.context.saveAllAsHar.accesskey=H

# LOCALIZATION NOTE (netmonitor.context.editAndResend): This is the label displayed
# on the context menu that opens a form to edit and resend the currently
# displayed request
netmonitor.context.editAndResend=Edit and Resend

# LOCALIZATION NOTE (netmonitor.context.editAndResend.accesskey): This is the access key
# for the "Edit and Resend" menu item displayed in the context menu for a request
netmonitor.context.editAndResend.accesskey=E

# LOCALIZATION NOTE (netmonitor.context.newTab):  This is the label
# for the Open in New Tab menu item displayed in the context menu of the
# network container
netmonitor.context.newTab=Open in New Tab

# LOCALIZATION NOTE (netmonitor.context.newTab.accesskey): This is the access key
# for the Open in New Tab menu item displayed in the context menu of the
# network container
netmonitor.context.newTab.accesskey=T

# LOCALIZATION NOTE (netmonitor.context.openInDebugger):  This is the label
# for the Open in Debugger menu item displayed in the context menu of the
# network container
netmonitor.context.openInDebugger=Open in Debugger

# LOCALIZATION NOTE (netmonitor.context.openInDebugger.accesskey): This is the access key
# for the Open in Debugger menu item displayed in the context menu of the
# network container
netmonitor.context.openInDebugger.accesskey=D

# LOCALIZATION NOTE (netmonitor.context.openInStyleEditor):  This is the label
# for the Open in Style Editor menu item displayed in the context menu of the
# network container
netmonitor.context.openInStyleEditor=Open in Style Editor

# LOCALIZATION NOTE (netmonitor.context.openInStyleEditor.accesskey): This is
# the access key for the Open in Style Editor menu item displayed in the
# context menu of the network container
netmonitor.context.openInStyleEditor.accesskey=S

# LOCALIZATION NOTE (netmonitor.context.perfTools): This is the label displayed
# on the context menu that shows the performance analysis tools
netmonitor.context.perfTools=Start Performance Analysis…

# LOCALIZATION NOTE (netmonitor.context.perfTools.accesskey): This is the access key
# for the performance analysis menu item displayed in the context menu for a request
netmonitor.context.perfTools.accesskey=A

# LOCALIZATION NOTE (netmonitor.custom.newRequest): This is the label displayed
# as the title of the new custom request form
netmonitor.custom.newRequest=New Request

# LOCALIZATION NOTE (netmonitor.custom.query): This is the label displayed
# above the query string entry in the custom request form
netmonitor.custom.query=Query String:

# LOCALIZATION NOTE (netmonitor.custom.headers): This is the label displayed
# above the request headers entry in the custom request form
netmonitor.custom.headers=Request Headers:

# LOCALIZATION NOTE (netmonitor.custom.postData): This is the label displayed
# above the request body entry in the custom request form
netmonitor.custom.postData=Request Body:

# LOCALIZATION NOTE (netmonitor.custom.send): This is the label displayed
# on the button which sends the custom request
netmonitor.custom.send=Send

# LOCALIZATION NOTE (netmonitor.custom.cancel): This is the label displayed
# on the button which cancels and closes the custom request form
netmonitor.custom.cancel=Cancel

# LOCALIZATION NOTE (netmonitor.backButton): This is the label displayed
# on the button which exists the performance statistics view
netmonitor.backButton=Back

# LOCALIZATION NOTE (netmonitor.headers.learnMore): This is the label displayed
# next to a header list item, with a link to external documentation
netmonitor.headers.learnMore=Learn More

# LOCALIZATION NOTE (netmonitor.status.tooltip.simple): This is the tooltip of the
# column status code, when request is not being cached or is not from a service worker
# %1$S is the status code, %2$S is the status text.
netmonitor.status.tooltip.simple = %1$S %2$S

# LOCALIZATION NOTE (netmonitor.status.tooltip.cached): This is the tooltip of
# the column status code, when the request is cached
# %1$S is the status code, %2$S is the status text.
netmonitor.status.tooltip.cached = %1$S %2$S (cached)

# LOCALIZATION NOTE (netmonitor.status.tooltip.worker): This is the tooltip of
# the column status code, when the request is from a service worker
# %1$S is the status code, %2$S is the status text.
netmonitor.status.tooltip.worker = %1$S %2$S (service worker)

# LOCALIZATION NOTE (netmonitor.status.tooltip.cachedworker): This is the tooltip
# of the column status code, when the request is cached and is from a service worker
# %1$S is the status code, %2$S is the status text.
netmonitor.status.tooltip.cachedworker = %1$S %2$S (cached, service worker)

# LOCALIZATION NOTE (netmonitor.label.dropHarFiles): This is a label
# rendered within the Network panel when *.har file(s) are dragged
# over the content.
netmonitor.label.dropHarFiles = Drop HAR files here
PK
!<�
Nf$f$9chrome/en-US/locale/en-US/devtools/client/performance.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!-- LOCALIZATION NOTE : FILE This file contains the Performance strings -->
<!-- LOCALIZATION NOTE : FILE Do not translate commandkey -->

<!-- LOCALIZATION NOTE : FILE The correct localization of this file might be to
  - keep it in English, or another language commonly spoken among web developers.
  - You want to make that choice consistent across the developer tools.
  - A good criteria is the language in which you'd find the best
  - documentation on web development on the web. -->

<!-- LOCALIZATION NOTE (performanceUI.bufferStatusTooltip): This string
  -  is displayed as the tooltip for the buffer capacity during a recording. -->
<!ENTITY performanceUI.bufferStatusTooltip "The profiler stores samples in a circular buffer, and once the buffer reaches the limit for a recording, newer samples begin to overwrite samples at the beginning of the recording.">

<!-- LOCALIZATION NOTE (performanceUI.disabledRealTime.disabledE10S): This string
  -  is displayed as a message for why the real time overview graph is disabled
  -  when running on a build that can run multiprocess Firefox, but just is not enabled. -->
<!ENTITY performanceUI.disabledRealTime.disabledE10S "Enable multiprocess Firefox in preferences for rendering recording data in realtime.">

<!-- LOCALIZATION NOTE (performanceUI.bufferStatusFull): This string
  -  is displayed when the profiler's circular buffer has started to overlap. -->
<!ENTITY performanceUI.bufferStatusFull "The buffer is full. Older samples are now being overwritten.">

<!-- LOCALIZATION NOTE (performanceUI.unavailableNoticePB): This is the label shown
  -  in the details view while the profiler is unavailable, for example, while
  -  in Private Browsing mode. -->
<!ENTITY performanceUI.unavailableNoticePB "Recording a profile is currently unavailable. Please close all private browsing windows and try again.">

<!-- LOCALIZATION NOTE (performanceUI.loadingNotice): This is the label shown
  -  in the details view while loading a profile. -->
<!ENTITY performanceUI.loadingNotice "Loading…">

<!-- LOCALIZATION NOTE (performanceUI.toolbar.*): These strings are displayed
  -  in the toolbar on buttons that select which view is currently shown. -->
<!ENTITY performanceUI.toolbar.waterfall "Waterfall">
<!ENTITY performanceUI.toolbar.waterfall.tooltiptext "Shows the different operations the browser is performing during the recording, laid out sequentially as a waterfall.">
<!ENTITY performanceUI.toolbar.js-calltree "Call Tree">
<!ENTITY performanceUI.toolbar.js-calltree.tooltiptext "Highlights JavaScript functions where the browser spent most time during the recording.">
<!ENTITY performanceUI.toolbar.memory-calltree "Allocations">
<!ENTITY performanceUI.toolbar.allocations.tooltiptext "Shows where memory was allocated during the recording.">
<!ENTITY performanceUI.toolbar.js-flamegraph "JS Flame Chart">
<!ENTITY performanceUI.toolbar.js-flamegraph.tooltiptext "Shows the JavaScript call stack over the course of the recording.">
<!ENTITY performanceUI.toolbar.memory-flamegraph "Allocations Flame Chart">

<!-- LOCALIZATION NOTE (performanceUI.table.*): These strings are displayed
  -  in the call tree headers for a recording. -->
<!ENTITY performanceUI.table.totalDuration            "Total Time">
<!ENTITY performanceUI.table.totalDuration.tooltip    "The amount of time spent in this function and functions it calls.">
<!ENTITY performanceUI.table.selfDuration             "Self Time">
<!ENTITY performanceUI.table.selfDuration.tooltip     "The amount of time spent only within this function.">
<!ENTITY performanceUI.table.totalPercentage          "Total Cost">
<!ENTITY performanceUI.table.totalPercentage.tooltip  "The percentage of time spent in this function and functions it calls.">
<!ENTITY performanceUI.table.selfPercentage           "Self Cost">
<!ENTITY performanceUI.table.selfPercentage.tooltip   "The percentage of time spent only within this function.">
<!ENTITY performanceUI.table.samples                  "Samples">
<!ENTITY performanceUI.table.samples.tooltip          "The number of times this function was on the stack when the profiler took a sample.">
<!ENTITY performanceUI.table.function                 "Function">
<!ENTITY performanceUI.table.function.tooltip         "The name and source location of the sampled function.">
<!ENTITY performanceUI.table.totalAlloc               "Total Sampled Allocations">
<!ENTITY performanceUI.table.totalAlloc.tooltip       "The total number of Object allocations sampled at this location and in callees.">
<!ENTITY performanceUI.table.selfAlloc                "Self Sampled Allocations">
<!ENTITY performanceUI.table.selfAlloc.tooltip        "The number of Object allocations sampled at this location.">

<!-- LOCALIZATION NOTE (performanceUI.options.filter.tooltiptext): This string
  -  is displayed next to the filter button-->
<!ENTITY performanceUI.options.filter.tooltiptext "Select what data to display in the timeline">

<!-- LOCALIZATION NOTE (performanceUI.options.gear.tooltiptext): This is the
  -  tooltip for the options button. -->
<!ENTITY performanceUI.options.gear.tooltiptext "Configure performance preferences.">

<!-- LOCALIZATION NOTE (performanceUI.invertTree): This is the label shown next to
  -  a checkbox that inverts and un-inverts the profiler's call tree. -->
<!ENTITY performanceUI.invertTree             "Invert Call Tree">
<!ENTITY performanceUI.invertTree.tooltiptext "Inverting the call tree displays the profiled call paths starting from the youngest frames and expanding out to the older frames.">

<!-- LOCALIZATION NOTE (performanceUI.invertFlameGraph): This is the label shown next to
  -  a checkbox that inverts and un-inverts the profiler's flame graph. -->
<!ENTITY performanceUI.invertFlameGraph             "Invert Flame Chart">
<!ENTITY performanceUI.invertFlameGraph.tooltiptext "Inverting the flame chart displays the profiled call paths starting from the youngest frames and expanding out to the older frames.">

<!-- LOCALIZATION NOTE (performanceUI.showPlatformData): This is the
  -  label for the checkbox that toggles whether or not Gecko platform data
  -  is displayed in the profiler. -->
<!ENTITY performanceUI.showPlatformData             "Show Gecko Platform Data">
<!ENTITY performanceUI.showPlatformData.tooltiptext "Showing platform data enables the JavaScript Profiler reports to include Gecko platform symbols.">

<!-- LOCALIZATION NOTE (performanceUI.showJITOptimizations): This string
  -  is displayed next to a checkbox determining whether or not JIT optimization data
  -  should be displayed. -->
<!ENTITY performanceUI.showJITOptimizations             "Show JIT Optimizations">
<!ENTITY performanceUI.showJITOptimizations.tooltiptext "Show JIT optimization data sampled in each JavaScript frame.">

<!-- LOCALIZATION NOTE (performanceUI.flattenTreeRecursion): This is the
  -  label for the checkbox that toggles the flattening of tree recursion in inspected
  -  functions in the profiler. -->
<!ENTITY performanceUI.flattenTreeRecursion             "Flatten Tree Recursion">
<!ENTITY performanceUI.flattenTreeRecursion.tooltiptext "Flatten recursion when inspecting functions.">

<!-- LOCALIZATION NOTE (performanceUI.enableMemory): This string
  -  is displayed next to a checkbox determining whether or not memory
  -  measurements are enabled. -->
<!ENTITY performanceUI.enableMemory             "Record Memory">
<!ENTITY performanceUI.enableMemory.tooltiptext "Record memory consumption while profiling.">

<!-- LOCALIZATION NOTE (performanceUI.enableAllocations): This string
  -  is displayed next to a checkbox determining whether or not allocation
  -  measurements are enabled. -->
<!ENTITY performanceUI.enableAllocations             "Record Allocations">
<!ENTITY performanceUI.enableAllocations.tooltiptext "Record Object allocations while profiling.">

<!-- LOCALIZATION NOTE (performanceUI.enableFramerate): This string
  -  is displayed next to a checkbox determining whether or not framerate
  -  is recorded. -->
<!ENTITY performanceUI.enableFramerate             "Record Framerate">
<!ENTITY performanceUI.enableFramerate.tooltiptext "Record framerate while profiling.">

<!-- LOCALIZATION NOTE (performanceUI.console.recordingNoticeStart/recordingNoticeEnd):
  -  This string is displayed when a recording is selected that started via console.profile.
  -  Wraps the command used to start, like "Currently recording via console.profile("label")" -->
<!ENTITY performanceUI.console.recordingNoticeStart "Currently recording via">
<!ENTITY performanceUI.console.recordingNoticeEnd   "">

<!-- LOCALIZATION NOTE (performanceUI.console.stopCommandStart/stopCommandEnd):
  -  This string is displayed when a recording is selected that started via console.profile.
  -  Indicates how to stop the recording, wrapping the command, like
  -  "Stop recording by entering console.profileEnd("label") into the console." -->
<!ENTITY performanceUI.console.stopCommandStart "Stop recording by entering">
<!ENTITY performanceUI.console.stopCommandEnd   "into the console.">
PK
!<�v��__@chrome/en-US/locale/en-US/devtools/client/performance.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE These strings are used inside the Performance Tools
# which is available from the Web Developer sub-menu -> 'Performance'.
# The correct localization of this file might be to keep it in
# English, or another language commonly spoken among web developers.
# You want to make that choice consistent across the developer tools.
# A good criteria is the language in which you'd find the best
# documentation on web development on the web.

# LOCALIZATION NOTE (noRecordingsText): The text to display in the
# recordings menu when there are no recorded profiles yet.
noRecordingsText=There are no profiles yet.

# LOCALIZATION NOTE (recordingsList.itemLabel):
# This string is displayed in the recordings list of the Performance Tools,
# identifying a set of function calls. %S represents the number of recording,
# iterating for every new recording, resulting in "Recording #1", "Recording #2", etc.
recordingsList.itemLabel=Recording #%S

# LOCALIZATION NOTE (recordingsList.recordingLabel):
# This string is displayed in the recordings list of the Performance Tools,
# for an item that has not finished recording.
recordingsList.recordingLabel=In progress…

# LOCALIZATION NOTE (recordingsList.loadingLabel):
# This string is displayed in the recordings list of the Performance Tools,
# for an item that is finished and is loading.
recordingsList.loadingLabel=Loading…

# LOCALIZATION NOTE (recordingsList.durationLabel):
# This string is displayed in the recordings list of the Performance Tools,
# for an item that has finished recording.
recordingsList.durationLabel=%S ms

# LOCALIZATION NOTE (recordingsList.saveLabel):
# This string is displayed in the recordings list of the Performance Tools,
# for saving an item to disk.
recordingsList.saveLabel=Save

# LOCALIZATION NOTE (graphs.fps):
# This string is displayed in the framerate graph of the Performance Tools,
# as the unit used to measure frames per second. This label should be kept
# AS SHORT AS POSSIBLE so it doesn't obstruct important parts of the graph.
graphs.fps=fps

# LOCALIZATION NOTE (graphs.ms):
# This string is displayed in the flamegraph of the Performance Tools,
# as the unit used to measure time (in milliseconds). This label should be kept
# AS SHORT AS POSSIBLE so it doesn't obstruct important parts of the graph.
graphs.ms=ms

# LOCALIZATION NOTE (graphs.memory):
# This string is displayed in the memory graph of the Performance tool,
# as the unit used to memory consumption. This label should be kept
# AS SHORT AS POSSIBLE so it doesn't obstruct important parts of the graph.
graphs.memory=MB

# LOCALIZATION NOTE (category.*):
# These strings are displayed in the categories graph of the Performance Tools,
# as the legend for each block in every bar. These labels should be kept
# AS SHORT AS POSSIBLE so they don't obstruct important parts of the graph.
category.other=Gecko
category.css=Styles
category.js=JIT
category.gc=GC
category.network=Network
category.graphics=Graphics
category.storage=Storage
category.events=Input & Events
category.tools=Tools

# LOCALIZATION NOTE (table.bytes):
# This string is displayed in the call tree after bytesize units.
# %S represents the value in bytes.
table.bytes=%S B

# LOCALIZATION NOTE (table.ms2):
# This string is displayed in the call tree after units of time in milliseconds.
# %S represents the value in milliseconds.
table.ms2=%S ms

# LOCALIZATION NOTE (table.percentage3):
# This string is displayed in the call tree after units representing percentages.
# %S represents the value in percentage with two decimal points, localized.
# there are two "%" after %S to escape and display "%"
table.percentage3=%S%%

# LOCALIZATION NOTE (table.root):
# This string is displayed in the call tree for the root node.
table.root=(root)

# LOCALIZATION NOTE (table.idle):
# This string is displayed in the call tree for the idle blocks.
table.idle=(idle)

# LOCALIZATION NOTE (table.url.tooltiptext):
# This string is displayed in the call tree as the tooltip text for the url
# labels which, when clicked, jump to the debugger.
table.url.tooltiptext=View source in Debugger

# LOCALIZATION NOTE (table.view-optimizations.tooltiptext2):
# This string is displayed in the icon displayed next to frames that
# have optimization data
table.view-optimizations.tooltiptext2=Frame contains JIT optimization data

# LOCALIZATION NOTE (recordingsList.importDialogTitle):
# This string is displayed as a title for importing a recoring from disk.
recordingsList.importDialogTitle=Import recording…

# LOCALIZATION NOTE (recordingsList.saveDialogTitle):
# This string is displayed as a title for saving a recording to disk.
recordingsList.saveDialogTitle=Save recording…

# LOCALIZATION NOTE (recordingsList.saveDialogJSONFilter):
# This string is displayed as a filter for saving a recording to disk.
recordingsList.saveDialogJSONFilter=JSON Files

# LOCALIZATION NOTE (recordingsList.saveDialogAllFilter):
# This string is displayed as a filter for saving a recording to disk.
recordingsList.saveDialogAllFilter=All Files

# LOCALIZATION NOTE (timeline.tick):
# This string is displayed in the timeline overview, for delimiting ticks
# by time, in milliseconds.
timeline.tick=%S ms

# LOCALIZATION NOTE (timeline.records):
# This string is displayed in the timeline waterfall, as a title for the menu.
timeline.records=RECORDS

# LOCALIZATION NOTE (profiler.bufferFull):
# This string is displayed when recording, indicating how much of the
# buffer is currently be used.
# %S is the percentage of the buffer used -- there are two "%"s after to escape
# the % that is actually displayed.
# Example: "Buffer 54% full"
profiler.bufferFull=Buffer %S%% full

# LOCALIZATION NOTE (recordings.start):
# The label shown on the main recording buttons to start recording.
recordings.start=Start Recording Performance

# LOCALIZATION NOTE (recordings.stop):
# The label shown on the main recording buttons to stop recording.
recordings.stop=Stop Recording Performance

# LOCALIZATION NOTE (recordings.start.tooltip):
# This string is displayed as a tooltip on a button that starts a new profile.
recordings.start.tooltip=Toggle the recording state of a performance recording.

# LOCALIZATION NOTE (recordings.import.tooltip):
# This string is displayed on a button that opens a dialog to import a saved profile data file.
recordings.import.tooltip=Import…

# LOCALIZATION NOTE (recordings.clear.tooltip):
# This string is displayed on a button that removes all the recordings.
recordings.clear.tooltip=Clear
PK
!<5=8\��?chrome/en-US/locale/en-US/devtools/client/responsive.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE These strings are used inside the Responsive Design Mode,
# available from the Web Developer sub-menu -> 'Responsive Design Mode'.
#
# The correct localization of this file might be to keep it in
# English, or another language commonly spoken among web developers.
# You want to make that choice consistent across the developer tools.
# A good criteria is the language in which you'd find the best
# documentation on web development on the web.

# LOCALIZATION NOTE (responsive.editDeviceList): option displayed in the device
# selector
responsive.editDeviceList=Edit list…

# LOCALIZATION NOTE (responsive.exit): tooltip text of the exit button.
responsive.exit=Close Responsive Design Mode

# LOCALIZATION NOTE (responsive.rotate): tooltip text of the rotate button.
responsive.rotate=Rotate viewport

# LOCALIZATION NOTE (responsive.deviceListLoading): placeholder text for
# device selector when it's still fetching devices
responsive.deviceListLoading=Loading…

# LOCALIZATION NOTE (responsive.deviceListError): placeholder text for
# device selector when an error occurred
responsive.deviceListError=No list available

# LOCALIZATION NOTE (responsive.done): button text in the device list modal
responsive.done=Done

# LOCALIZATION NOTE (responsive.noDeviceSelected): placeholder text for the
# device selector
responsive.noDeviceSelected=no device selected

# LOCALIZATION NOTE  (responsive.title): the title displayed in the global
# toolbar
responsive.title=Responsive Design Mode

# LOCALIZATION NOTE (responsive.enableTouch): tooltip text for the touch
# simulation button when it's disabled
responsive.enableTouch=Enable touch simulation

# LOCALIZATION NOTE (responsive.disableTouch): tooltip text for the touch
# simulation button when it's enabled
responsive.disableTouch=Disable touch simulation

# LOCALIZATION NOTE  (responsive.screenshot): tooltip of the screenshot button.
responsive.screenshot=Take a screenshot of the viewport

# LOCALIZATION NOTE (responsive.screenshotGeneratedFilename): The auto generated
# filename.
# The first argument (%1$S) is the date string in yyyy-mm-dd format and the
# second argument (%2$S) is the time string in HH.MM.SS format.
responsive.screenshotGeneratedFilename=Screen Shot %1$S at %2$S

# LOCALIZATION NOTE (responsive.remoteOnly): Message displayed in the tab's
# notification box if a user tries to open Responsive Design Mode in a
# non-remote tab.
responsive.remoteOnly=Responsive Design Mode is only available for remote browser tabs, such as those used for web content in multi-process Firefox.

# LOCALIZATION NOTE (responsive.noContainerTabs): Message displayed in the tab's
# notification box if a user tries to open Responsive Design Mode in a
# container tab.
responsive.noContainerTabs=Responsive Design Mode is currently unavailable in container tabs.

# LOCALIZATION NOTE (responsive.noThrottling): UI option in a menu to configure
# network throttling.  This option is the default and disables throttling so you
# just have normal network conditions.  There is not very much room in the UI
# so a short string would be best if possible.
responsive.noThrottling=No throttling

# LOCALIZATION NOTE (responsive.changeDevicePixelRatio): tooltip for the
# device pixel ratio dropdown when is enabled.
responsive.changeDevicePixelRatio=Change device pixel ratio of the viewport

# LOCALIZATION NOTE (responsive.devicePixelRatio.auto): tooltip for the device pixel ratio
# dropdown when it is disabled because a device is selected.
# The argument (%1$S) is the selected device (e.g. iPhone 6) that set
# automatically the device pixel ratio value.
responsive.devicePixelRatio.auto=Device pixel ratio automatically set by %1$S

# LOCALIZATION NOTE (responsive.customDeviceName): Default value in a form to
# add a custom device based on an arbitrary size (no association to an existing
# device).
responsive.customDeviceName=Custom Device

# LOCALIZATION NOTE (responsive.customDeviceNameFromBase): Default value in a
# form to add a custom device based on the properties of another.  %1$S is the
# name of the device we're staring from, such as "Apple iPhone 6".
responsive.customDeviceNameFromBase=%1$S (Custom)

# LOCALIZATION NOTE (responsive.addDevice): Button text that reveals a form to
# be used for adding custom devices.
responsive.addDevice=Add Device

# LOCALIZATION NOTE (responsive.deviceAdderName): Label of form field for the
# name of a new device.  The available width is very low, so you might see
# overlapping text if the length is much longer than 5 or so characters.
responsive.deviceAdderName=Name

# LOCALIZATION NOTE (responsive.deviceAdderSize): Label of form field for the
# size of a new device.  The available width is very low, so you might see
# overlapping text if the length is much longer than 5 or so characters.
responsive.deviceAdderSize=Size

# LOCALIZATION NOTE (responsive.deviceAdderPixelRatio): Label of form field for
# the device pixel ratio of a new device.  The available width is very low, so you
# might see overlapping text if the length is much longer than 5 or so
# characters.
responsive.deviceAdderPixelRatio=DPR

# LOCALIZATION NOTE (responsive.deviceAdderUserAgent): Label of form field for
# the user agent of a new device.  The available width is very low, so you might
# see overlapping text if the length is much longer than 5 or so characters.
responsive.deviceAdderUserAgent=UA

# LOCALIZATION NOTE (responsive.deviceAdderTouch): Label of form field for the
# touch input support of a new device.  The available width is very low, so you
# might see overlapping text if the length is much longer than 5 or so
# characters.
responsive.deviceAdderTouch=Touch

# LOCALIZATION NOTE (responsive.deviceAdderSave): Button text that submits a
# form to add a new device.
responsive.deviceAdderSave=Save

# LOCALIZATION NOTE (responsive.deviceDetails): Tooltip that appears when
# hovering on a device in the device modal.  %1$S is the width of the device.
# %2$S is the height of the device.  %3$S is the device pixel ratio value of the
# device.  %4$S is the user agent of the device.  %5$S is a boolean value
# noting whether touch input is supported.
responsive.deviceDetails=Size: %1$S x %2$S\nDPR: %3$S\nUA: %4$S\nTouch: %5$S

# LOCALIZATION NOTE (responsive.devicePixelRatioOption): UI option in a menu to configure
# the device pixel ratio. %1$S is the devicePixelRatio value of the device.
responsive.devicePixelRatioOption=DPR: %1$S

# LOCALIZATION NOTE (responsive.reloadConditions.label): Label on button to open a menu
# used to choose whether to reload the page automatically when certain actions occur.
responsive.reloadConditions.label=Reload when…

# LOCALIZATION NOTE (responsive.reloadConditions.title): Title on button to open a menu
# used to choose whether to reload the page automatically when certain actions occur.
responsive.reloadConditions.title=Choose whether to reload the page automatically when certain actions occur

# LOCALIZATION NOTE (responsive.reloadConditions.touchSimulation): Label on checkbox used
# to select whether to reload when touch simulation is toggled.
responsive.reloadConditions.touchSimulation=Reload when touch simulation is toggled

# LOCALIZATION NOTE (responsive.reloadConditions.userAgent): Label on checkbox used
# to select whether to reload when user agent is changed.
responsive.reloadConditions.userAgent=Reload when user agent is changed

# LOCALIZATION NOTE (responsive.reloadNotification.description): Text in notification bar
# shown on first open to clarify that some features need a reload to apply.  %1$S is the
# label on the reload conditions menu (responsive.reloadConditions.label).
responsive.reloadNotification.description=Device simulation changes require a reload to fully apply.  Automatic reloads are disabled by default to avoid losing any changes in DevTools.  You can enable reloading via the “%1$S” menu.
PK
!<Z��<''8chrome/en-US/locale/en-US/devtools/client/scratchpad.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!-- LOCALIZATION NOTE : FILE This file contains the Scratchpad window strings -->
<!-- LOCALIZATION NOTE : FILE Do not translate commandkeys -->

<!-- LOCALIZATION NOTE : FILE The correct localization of this file might be to
  - keep it in English, or another language commonly spoken among web developers.
  - You want to make that choice consistent across the developer tools.
  - A good criteria is the language in which you'd find the best
  - documentation on web development on the web. -->

<!-- LOCALIZATION NOTE (window.title):
  -  The Scratchpad is intended to provide a simple text editor for creating
  -  and evaluating bits of JavaScript code for the purposes of function
  -  prototyping, experimentation and convenient scripting.
  -
  -  It's quite possible that you won't have a good analogue for the word
  -  "Scratchpad" in your locale. You should feel free to find a close
  -  approximation to it or choose a word (or words) that means
  -  "simple discardable text editor". -->
<!ENTITY window.title                 "Scratchpad">

<!ENTITY fileMenu.label               "File">
<!ENTITY fileMenu.accesskey           "F">

<!ENTITY newWindowCmd.label           "New Window">
<!ENTITY newWindowCmd.accesskey       "N">
<!ENTITY newWindowCmd.commandkey      "n">

<!ENTITY openFileCmd.label            "Open File…">
<!ENTITY openFileCmd.accesskey        "O">
<!ENTITY openFileCmd.commandkey       "o">

<!ENTITY openRecentMenu.label         "Open Recent">
<!ENTITY openRecentMenu.accesskey     "R">

<!ENTITY revertCmd.label              "Revert…">
<!ENTITY revertCmd.accesskey          "t">

<!ENTITY saveFileCmd.label            "Save">
<!ENTITY saveFileCmd.accesskey        "S">
<!ENTITY saveFileCmd.commandkey       "s">

<!ENTITY saveFileAsCmd.label          "Save As…">
<!ENTITY saveFileAsCmd.accesskey      "A">

<!ENTITY closeCmd.label               "Close">
<!ENTITY closeCmd.key                 "W">
<!ENTITY closeCmd.accesskey           "C">

<!ENTITY viewMenu.label               "View">
<!ENTITY viewMenu.accesskey           "V">

<!ENTITY lineNumbers.label             "Show Line Numbers">
<!ENTITY lineNumbers.accesskey         "L">

<!ENTITY wordWrap.label                "Wrap Text">
<!ENTITY wordWrap.accesskey            "W">

<!ENTITY highlightTrailingSpace.label     "Highlight Trailing Space">
<!ENTITY highlightTrailingSpace.accesskey "H">

<!ENTITY largerFont.label             "Larger Font">
<!ENTITY largerFont.accesskey         "a">
<!ENTITY largerFont.commandkey        "+">
<!ENTITY largerFont.commandkey2       "="> <!-- + is above this key on many keyboards -->

<!ENTITY smallerFont.label            "Smaller Font">
<!ENTITY smallerFont.accesskey        "M">
<!ENTITY smallerFont.commandkey       "-">

<!ENTITY normalSize.label             "Normal Size">
<!ENTITY normalSize.accesskey         "N">
<!ENTITY normalSize.commandkey        "0">

<!ENTITY editMenu.label               "Edit">
<!ENTITY editMenu.accesskey           "E">

<!ENTITY run.label                    "Run">
<!ENTITY run.accesskey                "R">
<!ENTITY run.key                      "r">

<!ENTITY inspect.label                "Inspect">
<!ENTITY inspect.accesskey            "I">
<!ENTITY inspect.key                  "i">

<!ENTITY display.label                "Display">
<!ENTITY display.accesskey            "D">
<!ENTITY display.key                  "l">

<!ENTITY pprint.label                 "Pretty Print">
<!ENTITY pprint.key                   "p">
<!ENTITY pprint.accesskey             "P">

<!-- LOCALIZATION NOTE (environmentMenu.label, accesskey): This menu item was
  -  renamed from "Context" to avoid confusion with the right-click context
  -  menu in the text area. It refers to the JavaScript Environment (or context)
  -  the user is evaluating against. I.e., Content (current tab) or Chrome
  -  (browser).
  -->
<!ENTITY environmentMenu.label        "Environment">
<!ENTITY environmentMenu.accesskey    "N">


<!ENTITY contentContext.label         "Content">
<!ENTITY contentContext.accesskey     "C">

<!-- LOCALIZATION NOTE (browserContext.label, accesskey): This menu item is used
  -  to select an execution environment for the browser window itself as opposed
  -  to content. This is a feature for browser and addon developers and only
  -  enabled via the devtools.chrome.enabled preference. Formerly, this label
  -  was called "Chrome".
  -->
<!ENTITY browserContext.label         "Browser">
<!ENTITY browserContext.accesskey     "B">

<!-- LOCALIZATION NOTE some localizations of Windows (ex:french, german) use "?"
  -  for the help button in the menubar but Gnome does not.
  -->
<!ENTITY helpMenu.label               "Help">
<!ENTITY helpMenu.accesskey           "H">
<!ENTITY helpMenuWin.label            "Help">
<!ENTITY helpMenuWin.accesskey        "H">

<!ENTITY documentationLink.label      "Scratchpad Help on MDN">
<!ENTITY documentationLink.accesskey  "D">


<!-- LOCALIZATION NOTE (resetContext2.label): This command allows the developer
  -  to reset/clear the global object of the environment where the code executes.
  -->
<!ENTITY resetContext2.label          "Reset Variables">
<!ENTITY resetContext2.accesskey      "T">

<!ENTITY reloadAndRun.label           "Reload And Run">
<!ENTITY reloadAndRun.accesskey       "E">
<!ENTITY reloadAndRun.key             "r">

<!ENTITY executeMenu.label            "Execute">
<!ENTITY executeMenu.accesskey        "X">

<!-- LOCALIZATION NOTE (errorConsoleCmd.commandkey): This command key launches
  -  the browser Error Console, the key should be identical to the property of
  -  the same name in browser.dtd.
  -->
<!ENTITY errorConsoleCmd.commandkey   "j">

<!-- LOCALIZATION NOTE (evalFunction.label): This command allows the developer
  -  to evaluate the top-level function that the cursor is currently at.
  -->
<!ENTITY evalFunction.label "Evaluate Current Function">
<!ENTITY evalFunction.accesskey "v">
<!ENTITY evalFunction.key "e">
PK
!</�&5��?chrome/en-US/locale/en-US/devtools/client/scratchpad.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE These strings are used inside the JavaScript scratchpad
# which is available from the Web Developer sub-menu -> 'Scratchpad'.
#
# The correct localization of this file might be to keep it in
# English, or another language commonly spoken among web developers.
# You want to make that choice consistent across the developer tools.
# A good criteria is the language in which you'd find the best
# documentation on web development on the web.

# LOCALIZATION NOTE  (export.fileOverwriteConfirmation): This is displayed when
# the user attempts to save to an already existing file.
export.fileOverwriteConfirmation=File exists. Overwrite?

# LOCALIZATION NOTE  (browserWindow.unavailable): This error message is shown
# when Scratchpad does not find any recently active main browser window.
browserWindow.unavailable=Scratchpad cannot find any browser window to execute the code in.

# LOCALIZATION NOTE  (scratchpadContext.invalid): This error message is shown
# when user tries to run an operation in Scratchpad in an unsupported context.
scratchpadContext.invalid=Scratchpad cannot run this operation in the current mode.

# LOCALIZATION NOTE  (openFile.title): This is the file picker title, when you
# open a file from Scratchpad.
openFile.title=Open File

# LOCALIZATION NOTE  (openFile.failed): This is the message displayed when file
# open fails.
openFile.failed=Failed to read the file.

# LOCALIZATION NOTE  (importFromFile.convert.failed): This is the message
# displayed when file conversion from some charset to Unicode fails.
# %1 is the name of the charset from which the conversion failed.
importFromFile.convert.failed=Failed to convert file to Unicode from %1$S.

# LOCALIZATION NOTE (clearRecentMenuItems.label): This is the label for the
# menuitem in the 'Open Recent'-menu which clears all recent files.
clearRecentMenuItems.label=Clear Items

# LOCALIZATION NOTE  (saveFileAs): This is the file picker title, when you save
# a file in Scratchpad.
saveFileAs=Save File As

# LOCALIZATION NOTE  (saveFile.failed): This is the message displayed when file
# save fails.
saveFile.failed=The file save operation failed.

# LOCALIZATION NOTE  (confirmClose): This is message in the prompt dialog when
# you try to close a scratchpad with unsaved changes.
confirmClose=Do you want to save the changes you made to this scratchpad?

# LOCALIZATION NOTE  (confirmClose.title): This is title of the prompt dialog when
# you try to close a scratchpad with unsaved changes.
confirmClose.title=Unsaved Changes

# LOCALIZATION NOTE  (confirmRevert): This is message in the prompt dialog when
# you try to revert unsaved content of scratchpad.
confirmRevert=Do you want to revert the changes you made to this scratchpad?

# LOCALIZATION NOTE  (confirmRevert.title): This is title of the prompt dialog when
# you try to revert unsaved content of scratchpad.
confirmRevert.title=Revert Changes

# LOCALIZATION NOTE  (scratchpadIntro1): This is a multi-line comment explaining
# how to use the Scratchpad. Note that this should be a valid JavaScript
# comment inside /* and */.
scratchpadIntro1=/*\n * This is a JavaScript Scratchpad.\n *\n * Enter some JavaScript, then Right Click or choose from the Execute Menu:\n * 1. Run to evaluate the selected text (%1$S),\n * 2. Inspect to bring up an Object Inspector on the result (%2$S), or,\n * 3. Display to insert the result in a comment after the selection. (%3$S)\n */\n\n

# LOCALIZATION NOTE  (browserContext.notification): This is the message displayed
# over the top of the editor when the user has switched to browser context.
browserContext.notification=This scratchpad executes in the Browser context.

# LOCALIZATION NOTE (help.openDocumentationPage): This returns a localized link with
# documentation for Scratchpad on MDN.
help.openDocumentationPage=https://developer.mozilla.org/en/Tools/Scratchpad

# LOCALIZATION NOTE (scratchpad.statusBarLineCol): Line, Column
# information displayed in statusbar when selection is made in
# Scratchpad.
scratchpad.statusBarLineCol  = Line %1$S, Col %2$S

# LOCALIZATION NOTE (fileNoLongerExists.notification): This is the message displayed
# over the top of the the editor when a file does not exist.
fileNoLongerExists.notification=This file no longer exists.

# LOCALIZATION NOTE (propertiesFilterPlaceholder): this is the text that
# appears in the filter text box for the properties view container.
propertiesFilterPlaceholder=Filter properties

# LOCALIZATION NOTE (connectionTimeout): message displayed when the Remote Scratchpad
# fails to connect to the server due to a timeout.
connectionTimeout=Connection timeout. Check the Error Console on both ends for potential error messages. Reopen the Scratchpad to try again.

# LOCALIZATION NOTE (selfxss.msg): the text that is displayed when
# a new user of the developer tools pastes code into the console
# %1 is the text of selfxss.okstring
selfxss.msg=Scam Warning: Take care when pasting things you don’t understand. This could allow attackers to steal your identity or take control of your computer. Please type ‘%S’ in the scratchpad below to allow pasting.

# LOCALIZATION NOTE (selfxss.okstring): the string to be typed
# in by a new user of the developer tools when they receive the sefxss.msg prompt.
# Please avoid using non-keyboard characters here
selfxss.okstring=allow pasting
PK
!<)َO��:chrome/en-US/locale/en-US/devtools/client/shadereditor.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!-- LOCALIZATION NOTE : FILE This file contains the Debugger strings -->
<!-- LOCALIZATION NOTE : FILE Do not translate commandkey -->

<!-- LOCALIZATION NOTE : FILE The correct localization of this file might be to
  - keep it in English, or another language commonly spoken among web developers.
  - You want to make that choice consistent across the developer tools.
  - A good criteria is the language in which you'd find the best
  - documentation on web development on the web. -->

<!-- LOCALIZATION NOTE (shaderEditorUI.vertexShader): This is the label for
  -  the pane that displays a vertex shader's source. -->
<!ENTITY shaderEditorUI.vertexShader    "Vertex Shader">

<!-- LOCALIZATION NOTE (shaderEditorUI.fragmentShader): This is the label for
  -  the pane that displays a fragment shader's source. -->
<!ENTITY shaderEditorUI.fragmentShader  "Fragment Shader">

<!-- LOCALIZATION NOTE (shaderEditorUI.reloadNotice1): This is the label shown
  -  on the button that triggers a page refresh. -->
<!ENTITY shaderEditorUI.reloadNotice1   "Reload">

<!-- LOCALIZATION NOTE (shaderEditorUI.reloadNotice2): This is the label shown
  -  along with the button that triggers a page refresh. -->
<!ENTITY shaderEditorUI.reloadNotice2   "the page to be able to edit GLSL code.">

<!-- LOCALIZATION NOTE (shaderEditorUI.emptyNotice): This is the label shown
  -  while the page is refreshing and the tool waits for a WebGL context. -->
<!ENTITY shaderEditorUI.emptyNotice     "Waiting for a WebGL context to be created…">
PK
!<��$�iiAchrome/en-US/locale/en-US/devtools/client/shadereditor.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE These strings are used inside the Debugger
# which is available from the Web Developer sub-menu -> 'Debugger'.
# The correct localization of this file might be to keep it in
# English, or another language commonly spoken among web developers.
# You want to make that choice consistent across the developer tools.
# A good criteria is the language in which you'd find the best
# documentation on web development on the web.

# LOCALIZATION NOTE (shadersList.programLabel):
# This string is displayed in the programs list of the Shader Editor,
# identifying a set of linked GLSL shaders.
shadersList.programLabel=Program %S

# LOCALIZATION NOTE (shadersList.blackboxLabel):
# This string is displayed in the programs list of the Shader Editor, while
# the user hovers over the checkbox used to toggle blackboxing of a program's
# associated fragment shader.
shadersList.blackboxLabel=Toggle geometry visibility
PK
!<����;chrome/en-US/locale/en-US/devtools/client/shared.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE (dimensions): This is used to display the dimensions
# of a node or image, like 100×200.
dimensions=%S\u00D7%S

# LOCALIZATION NOTE (sideMenu.groupCheckbox.tooltip): This is used in the SideMenuWidget
# as the default tooltip of a group checkbox
sideMenu.groupCheckbox.tooltip=Toggle all checkboxes in this groupPK
!<���ݱ�:chrome/en-US/locale/en-US/devtools/client/sourceeditor.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!-- LOCALIZATION NOTE : FILE This file contains the Source Editor component
  - strings. The source editor component is used within the Scratchpad and
  - Style Editor tools. -->

<!-- LOCALIZATION NOTE : FILE Do not translate commandkeys -->

<!-- LOCALIZATION NOTE : FILE The correct localization of this file might be to
  - keep it in English, or another language commonly spoken among web developers.
  - You want to make that choice consistent across the developer tools.
  - A good criteria is the language in which you'd find the best
  - documentation on web development on the web. -->

<!ENTITY gotoLineCmd.label         "Jump to line…">
<!ENTITY gotoLineCmd.key           "J">
<!ENTITY gotoLineCmd.accesskey     "J">
PK
!<��]+Achrome/en-US/locale/en-US/devtools/client/sourceeditor.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE These strings are used inside the Source Editor component.
# This component is used whenever source code is displayed for the purpose of
# being edited, inside the Firefox developer tools - current examples are the
# Scratchpad and the Style Editor tools.

# LOCALIZATION NOTE The correct localization of this file might be to keep it
# in English, or another language commonly spoken among web developers.
# You want to make that choice consistent across the developer tools.
# A good criteria is the language in which you'd find the best documentation
# on web development on the web.

# LOCALIZATION NOTE  (findCmd.promptTitle): This is the dialog title used
# when the user wants to search for a string in the code. You can
# access this feature by pressing Ctrl-F on Windows/Linux or Cmd-F on Mac.
findCmd.promptTitle=Find…

# LOCALIZATION NOTE  (findCmd.promptMessage): This is the message shown when
# the user wants to search for a string in the code. You can
# access this feature by pressing Ctrl-F on Windows/Linux or Cmd-F on Mac.
findCmd.promptMessage=Search for:

# LOCALIZATION NOTE  (gotoLineCmd.promptTitle): This is the dialog title used
# when the user wants to jump to a specific line number in the code. You can
# access this feature by pressing Ctrl-J on Windows/Linux or Cmd-J on Mac.
gotoLineCmd.promptTitle=Go to line…

# LOCALIZATION NOTE  (gotoLineCmd.promptMessage): This is the message shown when
# the user wants to jump to a specific line number in the code. You can
# access this feature by pressing Ctrl-J on Windows/Linux or Cmd-J on Mac.
gotoLineCmd.promptMessage=Jump to line number:

# LOCALIZATION NOTE  (annotation.breakpoint.title): This is the text shown in
# front of any breakpoint annotation when it is displayed as a tooltip in one of
# the editor gutters. This feature is used in the JavaScript Debugger.
annotation.breakpoint.title=Breakpoint: %S

# LOCALIZATION NOTE  (annotation.currentLine): This is the text shown in
# a tooltip displayed in any of the editor gutters when the user hovers the
# current line.
annotation.currentLine=Current line

# LOCALIZATION NOTE  (annotation.debugLocation.title): This is the text shown in
# a tooltip displayed in any of the editor gutters when the user hovers the
# current debugger location. The debugger can pause the JavaScript execution at
# user-defined lines.
annotation.debugLocation.title=Current step: %S

# LOCALIZATION NOTE  (autocompletion.docsLink): This is the text shown on
# the link inside of the documentation popup.  If you type 'document' in Scratchpad
# then press Shift+Space you can see the popup.
autocompletion.docsLink=docs

# LOCALIZATION NOTE  (autocompletion.notFound): This is the text shown in
# the documentation popup if Tern fails to find a type for the object.
autocompletion.notFound=not found

# LOCALIZATION NOTE  (jumpToLine.commandkey): This is the key to use in
# conjunction with accel (Command on Mac or Ctrl on other platforms) to jump to
# a specific line in the editor.
jumpToLine.commandkey=J

# LOCALIZATION NOTE  (toggleComment.commandkey): This is the key to use in
# conjunction with accel (Command on Mac or Ctrl on other platforms) to either
# comment or uncomment selected lines in the editor.
toggleComment.commandkey=/

# LOCALIZATION NOTE  (indentLess.commandkey): This is the key to use in
# conjunction with accel (Command on Mac or Ctrl on other platforms) to reduce
# indentation level in CodeMirror. However, its default value also used by
# the Toolbox to switch between tools so we disable it.
#
# DO NOT translate this key without proper synchronization with toolbox.dtd.
indentLess.commandkey=[

# LOCALIZATION NOTE  (indentMore.commandkey): This is the key to use in
# conjunction with accel (Command on Mac or Ctrl on other platforms) to increase
# indentation level in CodeMirror. However, its default value also used by
# the Toolbox to switch between tools
#
# DO NOT translate this key without proper synchronization with toolbox.dtd.
indentMore.commandkey=]

# LOCALIZATION NOTE  (moveLineUp.commandkey): This is the combination of keys
# used to move the current line up.
# Do not localize "Alt", "Up", or change the format of the string. These are key
# identifiers, not messages displayed to the user.
moveLineUp.commandkey=Alt-Up

# LOCALIZATION NOTE  (moveLineDown.commandkey): This is the combination of keys
# used to move the current line up.
# Do not localize "Alt", "Down", or change the format of the string. These are
# key identifiers, not messages displayed to the user.
moveLineDown.commandkey=Alt-Down

# LOCALIZATION NOTE  (autocompletion.commandkey): This is the key, used with
# Ctrl, for code autocompletion.
# Do not localize "Space", it's the key identifier, not a message displayed to
# the user.
autocompletion.commandkey=Space

# LOCALIZATION NOTE  (showInformation2.commandkey): This is the combination of
# keys used to display more information, like type inference.
# Do not localize "Shift", "Ctrl", "Space", or change the format of the string.
# These are key identifiers, not messages displayed to the user.
showInformation2.commandkey=Shift-Ctrl-Space

# LOCALIZATION NOTE  (find.key):
# Key shortcut used to find the typed search
# Do not localize "CmdOrCtrl", "F", or change the format of the string. These are
# key identifiers, not messages displayed to the user.
find.key=CmdOrCtrl+F

# LOCALIZATION NOTE (replaceAll.key):
# Key shortcut used to replace the content of the editor
# Do not localize "Shift", "CmdOrCtrl", "F", or change the format of the string. These are
# key identifiers, not messages displayed to the user.
replaceAll.key=Shift+CmdOrCtrl+F

# LOCALIZATION NOTE (replaceAllMac.key):
# Key shortcut used to replace the content of the editor on Mac
# Do not localize "Alt", "CmdOrCtrl", "F", or change the format of the string. These are
# key identifiers, not messages displayed to the user.
replaceAllMac.key=Alt+CmdOrCtrl+F

# LOCALIZATION NOTE  (findNext.key):
# Key shortcut used to find again the typed search
# Do not localize "CmdOrCtrl", "G", or change the format of the string. These are
# key identifiers, not messages displayed to the user.
findNext.key=CmdOrCtrl+G

# LOCALIZATION NOTE (findPrev.key):
# Key shortcut used to find the previous typed search
# Do not localize "Shift", "CmdOrCtrl", "G", or change the format of the string. These are
# key identifiers, not messages displayed to the user.
findPrev.key=Shift+CmdOrCtrl+G
PK
!<��̎�,�,<chrome/en-US/locale/en-US/devtools/client/startup.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE (optionsButton.tooltip): This is used as the tooltip
# for the options panel tab.
optionsButton.tooltip=Toolbox Options

# LOCALIZATION NOTE (options.label): This is used as the label of the tab in
# the devtools window.
options.label=Options

# LOCALIZATION NOTE (options.panelLabel): This is used as the label for the
# toolbox panel.
options.panelLabel=Toolbox Options Panel

# LOCALIZATION NOTE (options.darkTheme.label2)
# Used as a label for dark theme
options.darkTheme.label2=Dark

# LOCALIZATION NOTE (options.lightTheme.label2)
# Used as a label for light theme
options.lightTheme.label2=Light

# LOCALIZATION NOTE (options.firebugTheme.label2)
# Used as a label for Firebug theme
options.firebugTheme.label2=Firebug

# LOCALIZATION NOTE (performance.label):
# This string is displayed in the title of the tab when the profiler is
# displayed inside the developer tools window and in the Developer Tools Menu.
performance.label=Performance

# LOCALIZATION NOTE (performance.panelLabel):
# This is used as the label for the toolbox panel.
performance.panelLabel=Performance Panel

# LOCALIZATION NOTE (performance.accesskey)
# Used for the menuitem in the tool menu
performance.accesskey=P

# LOCALIZATION NOTE (performance.tooltip):
# This string is displayed in the tooltip of the tab when the profiler is
# displayed inside the developer tools window.
# Keyboard shortcut for Performance Tools will be shown inside brackets.
performance.tooltip=Performance (%S)

# LOCALIZATION NOTE (MenuWebconsole.label): the string displayed in the Tools
# menu as a shortcut to open the devtools with the Web Console tab selected.
MenuWebconsole.label=Web Console

# LOCALIZATION NOTE (ToolboxTabWebconsole.label): the string displayed as the
# label of the tab in the devtools window.
ToolboxTabWebconsole.label=Console

# LOCALIZATION NOTE (ToolboxWebConsole.panelLabel): the string used as the
# label for the toolbox panel.
ToolboxWebConsole.panelLabel=Console Panel

# LOCALIZATION NOTE (ToolboxWebconsole.tooltip2): the string displayed in the
# tooltip of the tab when the Web Console is displayed inside the developer
# tools window.
# Keyboard shortcut for Console will be shown inside the brackets.
ToolboxWebconsole.tooltip2=Web Console (%S)

webConsoleCmd.accesskey=W

# LOCALIZATION NOTE (ToolboxDebugger.label):
# This string is displayed in the title of the tab when the debugger is
# displayed inside the developer tools window and in the Developer Tools Menu.
ToolboxDebugger.label=Debugger

# LOCALIZATION NOTE (ToolboxDebugger.panelLabel):
# This is used as the label for the toolbox panel.
ToolboxDebugger.panelLabel=Debugger Panel

# LOCALIZATION NOTE (ToolboxDebugger.tooltip2):
# This string is displayed in the tooltip of the tab when the debugger is
# displayed inside the developer tools window..
# A keyboard shortcut for JS Debugger will be shown inside brackets.
ToolboxDebugger.tooltip2=JavaScript Debugger (%S)

# LOCALIZATION NOTE (debuggerMenu.accesskey)
# Used for the menuitem in the tool menu
debuggerMenu.accesskey=D

# LOCALIZATION NOTE (ToolboxStyleEditor.label):
# This string is displayed in the title of the tab when the style editor is
# displayed inside the developer tools window and in the Developer Tools Menu.
ToolboxStyleEditor.label=Style Editor

# LOCALIZATION NOTE (ToolboxStyleEditor.panelLabel):
# This is used as the label for the toolbox panel.
ToolboxStyleEditor.panelLabel=Style Editor Panel

# LOCALIZATION NOTE (ToolboxStyleEditor.tooltip3):
# This string is displayed in the tooltip of the tab when the style editor is
# displayed inside the developer tools window.
# A keyboard shortcut for Stylesheet Editor will be shown inside the latter pair of brackets.
ToolboxStyleEditor.tooltip3=Stylesheet Editor (CSS) (%S)

# LOCALIZATION NOTE (open.accesskey): The access key used to open the style
# editor.
open.accesskey=l

# LOCALIZATION NOTE (ToolboxShaderEditor.label):
# This string is displayed in the title of the tab when the Shader Editor is
# displayed inside the developer tools window and in the Developer Tools Menu.
ToolboxShaderEditor.label=Shader Editor

# LOCALIZATION NOTE (ToolboxShaderEditor.panelLabel):
# This is used as the label for the toolbox panel.
ToolboxShaderEditor.panelLabel=Shader Editor Panel

# LOCALIZATION NOTE (ToolboxShaderEditor.tooltip):
# This string is displayed in the tooltip of the tab when the Shader Editor is
# displayed inside the developer tools window.
ToolboxShaderEditor.tooltip=Live GLSL shader language editor for WebGL

# LOCALIZATION NOTE (ToolboxCanvasDebugger.label):
# This string is displayed in the title of the tab when the Shader Editor is
# displayed inside the developer tools window and in the Developer Tools Menu.
ToolboxCanvasDebugger.label=Canvas

# LOCALIZATION NOTE (ToolboxCanvasDebugger.panelLabel):
# This is used as the label for the toolbox panel.
ToolboxCanvasDebugger.panelLabel=Canvas Panel

# LOCALIZATION NOTE (ToolboxCanvasDebugger.tooltip):
# This string is displayed in the tooltip of the tab when the Shader Editor is
# displayed inside the developer tools window.
ToolboxCanvasDebugger.tooltip=Tools to inspect and debug <canvas> contexts

# LOCALIZATION NOTE (ToolboxWebAudioEditor1.label):
# This string is displayed in the title of the tab when the Web Audio Editor
# is displayed inside the developer tools window and in the Developer Tools Menu.
ToolboxWebAudioEditor1.label=Web Audio

# LOCALIZATION NOTE (ToolboxWebAudioEditor1.panelLabel):
# This is used as the label for the toolbox panel.
ToolboxWebAudioEditor1.panelLabel=Web Audio Panel

# LOCALIZATION NOTE (ToolboxWebAudioEditor1.tooltip):
# This string is displayed in the tooltip of the tab when the Web Audio Editor is
# displayed inside the developer tools window.
ToolboxWebAudioEditor1.tooltip=Web Audio context visualizer and audio node inspector

# LOCALIZATION NOTE (inspector.*)
# Used for the menuitem in the tool menu
inspector.label=Inspector
inspector.accesskey=I

# LOCALIZATION NOTE (inspector.panelLabel)
# Labels applied to the panel and views within the panel in the toolbox
inspector.panelLabel=Inspector Panel

# LOCALIZATION NOTE (inspector.tooltip2)
# Keyboard shortcut for DOM and Style Inspector will be shown inside brackets.
inspector.tooltip2=DOM and Style Inspector (%S)

# LOCALIZATION NOTE (netmonitor.label):
# This string is displayed in the title of the tab when the Network Monitor is
# displayed inside the developer tools window and in the Developer Tools Menu.
netmonitor.label=Network

# LOCALIZATION NOTE (netmonitor.panelLabel):
# This is used as the label for the toolbox panel.
netmonitor.panelLabel=Network Panel

# LOCALIZATION NOTE (netmonitor.accesskey)
# Used for the menuitem in the tool menu
netmonitor.accesskey=N

# LOCALIZATION NOTE (netmonitor.tooltip2):
# This string is displayed in the tooltip of the tab when the Network Monitor is
# displayed inside the developer tools window.
# Keyboard shortcut for Network Monitor will be shown inside the brackets.
netmonitor.tooltip2=Network Monitor (%S)

# LOCALIZATION NOTE (storage.accesskey): The access key used to open the storage
# editor.
storage.accesskey=a

# LOCALIZATION NOTE (storage.label):
# This string is displayed as the label of the tab in the developer tools window
storage.label=Storage

# LOCALIZATION NOTE (storage.menuLabel):
# This string is displayed in the Tools menu as a shortcut to open the devtools
# with the Storage Inspector tab selected.
storage.menuLabel=Storage Inspector

# LOCALIZATION NOTE (storage.panelLabel):
# This string is used as the aria-label for the iframe of the Storage Inspector
# tool in developer tools toolbox.
storage.panelLabel=Storage Panel

# LOCALIZATION NOTE (storage.tooltip3):
# This string is displayed in the tooltip of the tab when the storage editor is
# displayed inside the developer tools window.
# A keyboard shortcut for Storage Inspector will be shown inside the brackets.
storage.tooltip3=Storage Inspector (Cookies, Local Storage, …) (%S)

# LOCALIZATION NOTE (scratchpad.label): this string is displayed in the title of
# the tab when the Scratchpad is displayed inside the developer tools window and
# in the Developer Tools Menu.
scratchpad.label=Scratchpad

# LOCALIZATION NOTE (scratchpad.panelLabel): this is used as the
# label for the toolbox panel.
scratchpad.panelLabel=Scratchpad Panel

# LOCALIZATION NOTE (scratchpad.tooltip):  This string is displayed in the
# tooltip of the tab when the Scratchpad is displayed inside the developer tools
# window.
scratchpad.tooltip=Scratchpad

# LOCALIZATION NOTE (memory.label): This string is displayed in the title of the
# tab when the memory tool is displayed inside the developer tools window and in
# the Developer Tools Menu.
memory.label=Memory

# LOCALIZATION NOTE (memory.panelLabel): This is used as the label for the
# toolbox panel.
memory.panelLabel=Memory Panel

# LOCALIZATION NOTE (memory.tooltip): This string is displayed in the tooltip of
# the tab when the memory tool is displayed inside the developer tools window.
memory.tooltip=Memory

# LOCALIZATION NOTE (dom.label):
# This string is displayed in the title of the tab when the DOM panel is
# displayed inside the developer tools window and in the Developer Tools Menu.
dom.label=DOM

# LOCALIZATION NOTE (dom.panelLabel):
# This is used as the label for the toolbox panel.
dom.panelLabel=DOM Panel

# LOCALIZATION NOTE (dom.accesskey)
# Used for the menuitem in the tool menu
dom.accesskey=D

# LOCALIZATION NOTE (dom.tooltip):
# This string is displayed in the tooltip of the tab when the DOM is
# displayed inside the developer tools window.
# Keyboard shortcut for DOM panel will be shown inside the brackets.
dom.tooltip=DOM (%S)

# LOCALIZATION NOTE (toolbox.buttons.splitconsole):
# This is the tooltip of the button in the toolbox toolbar used to toggle
# the split console.
# Keyboard shortcut will be shown inside brackets.
toolbox.buttons.splitconsole = Toggle split console (%S)

# LOCALIZATION NOTE (toolbox.buttons.responsive):
# This is the tooltip of the button in the toolbox toolbar that toggles
# the Responsive mode.
# Keyboard shortcut will be shown inside brackets.
toolbox.buttons.responsive = Responsive Design Mode (%S)

# LOCALIZATION NOTE (toolbox.buttons.paintflashing):
# This is the tooltip of the paintflashing button in the toolbox toolbar
# that toggles paintflashing.
toolbox.buttons.paintflashing = Toggle paint flashing

# LOCALIZATION NOTE (toolbox.buttons.scratchpad):
# This is the tooltip of the button in the toolbox toolbar that opens
# the scratchpad window
toolbox.buttons.scratchpad = Scratchpad

# LOCALIZATION NOTE (toolbox.buttons.screenshot):
# This is the tooltip of the button in the toolbox toolbar that allows you to
# take a screenshot of the entire page
toolbox.buttons.screenshot = Take a screenshot of the entire page

# LOCALIZATION NOTE (toolbox.buttons.rulers):
# This is the tooltip of the button in the toolbox toolbar that toggles the
# rulers in the page
toolbox.buttons.rulers = Toggle rulers for the page

# LOCALIZATION NOTE (toolbox.buttons.measure):
# This is the tooltip of the button in the toolbox toolbar that toggles the
# measuring tools
toolbox.buttons.measure = Measure a portion of the page
PK
!<hՒ:��5chrome/en-US/locale/en-US/devtools/client/storage.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!-- LOCALIZATION NOTE : This file contains the Storage Inspector strings. -->

<!-- LOCALIZATION NOTE : Placeholder for the searchbox that allows you to filter the table items. -->
<!ENTITY searchBox.placeholder         "Filter items">

<!-- LOCALIZATION NOTE : Label of popup menu action to delete all storage items. -->
<!ENTITY storage.popupMenu.deleteAllLabel "Delete All">

<!-- LOCALIZATION NOTE : Label of popup menu action to delete all session cookies. -->
<!ENTITY storage.popupMenu.deleteAllSessionCookiesLabel "Delete All Session Cookies">
PK
!<MX~Z<chrome/en-US/locale/en-US/devtools/client/storage.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE These strings are used inside the Storage Editor tool.
# LOCALIZATION NOTE The correct localization of this file might be to keep it
# in English, or another language commonly spoken among web developers.
# You want to make that choice consistent across the developer tools.
# A good criteria is the language in which you'd find the best documentation
# on web development on the web.

# LOCALIZATION NOTE (storage.filter.key):
# Key shortcut used to focus the filter box on top of the data view
storage.filter.key=CmdOrCtrl+F

# LOCALIZATION NOTE (tree.emptyText):
# This string is displayed when the Storage Tree is empty. This can happen when
# there are no websites on the current page (about:blank)
tree.emptyText=No hosts on the page

# LOCALIZATION NOTE (table.emptyText):
# This string is displayed when there are no rows in the Storage Table for the
# selected host.
table.emptyText=No data present for selected host

# LOCALIZATION NOTE (tree.labels.*):
# These strings are the labels for Storage type groups present in the Storage
# Tree, like cookies, local storage etc.
tree.labels.cookies=Cookies
tree.labels.localStorage=Local Storage
tree.labels.sessionStorage=Session Storage
tree.labels.indexedDB=Indexed DB
tree.labels.Cache=Cache Storage

# LOCALIZATION NOTE (table.headers.*.*):
# These strings are the header names of the columns in the Storage Table for
# each type of storage available through the Storage Tree to the side.
table.headers.cookies.uniqueKey=Unique key
table.headers.cookies.name=Name
table.headers.cookies.path=Path
table.headers.cookies.host=Domain
table.headers.cookies.expires=Expires on
table.headers.cookies.value=Value
table.headers.cookies.lastAccessed=Last accessed on
table.headers.cookies.creationTime=Created on
table.headers.cookies.sameSite=sameSite

table.headers.localStorage.name=Key
table.headers.localStorage.value=Value

table.headers.sessionStorage.name=Key
table.headers.sessionStorage.value=Value

table.headers.Cache.url=URL
table.headers.Cache.status=Status

table.headers.indexedDB.uniqueKey=Unique key
table.headers.indexedDB.name=Key
table.headers.indexedDB.db=Database Name
table.headers.indexedDB.storage=Storage
table.headers.indexedDB.objectStore=Object Store Name
table.headers.indexedDB.value=Value
table.headers.indexedDB.origin=Origin
table.headers.indexedDB.version=Version
table.headers.indexedDB.objectStores=Object Stores
table.headers.indexedDB.keyPath2=Key Path
table.headers.indexedDB.autoIncrement=Auto Increment
table.headers.indexedDB.indexes=Indexes

# LOCALIZATION NOTE (label.expires.session):
# This string is displayed in the expires column when the cookie is Session
# Cookie
label.expires.session=Session

# LOCALIZATION NOTE (storage.search.placeholder):
# This is the placeholder text in the sidebar search box
storage.search.placeholder=Filter values

# LOCALIZATION NOTE (storage.data.label):
# This is the heading displayed over the item value in the sidebar
storage.data.label=Data

# LOCALIZATION NOTE (storage.parsedValue.label):
# This is the heading displayed over the item parsed value in the sidebar
storage.parsedValue.label=Parsed Value

# LOCALIZATION NOTE (storage.popupMenu.deleteLabel):
# Label of popup menu action to delete storage item.
storage.popupMenu.deleteLabel=Delete “%S”

# LOCALIZATION NOTE (storage.popupMenu.addItemLabel):
# Label of popup menu action to add an item.
storage.popupMenu.addItemLabel=Add Item

# LOCALIZATION NOTE (storage.popupMenu.deleteAllFromLabel):
# Label of popup menu action to delete all storage items.
storage.popupMenu.deleteAllFromLabel=Delete All From “%S”

# LOCALIZATION NOTE (storage.idb.deleteBlocked):
# Warning notification when IndexedDB database could not be deleted immediately.
storage.idb.deleteBlocked=Database “%S” will be deleted after all connections are closed.

# LOCALIZATION NOTE (storage.idb.deleteError):
# Error notification when IndexedDB database could not be deleted.
storage.idb.deleteError=Database “%S” could not be deleted.

# LOCALIZATION NOTE (storage.expandPane):
# This is the tooltip for the button that collapses the right panel in the
# storage UI when the panel is closed.
storage.expandPane=Expand Pane

# LOCALIZATION NOTE (storage.collapsePane):
# This is the tooltip for the button that collapses the right panel in the
# storage UI when the panel is open.
storage.collapsePane=Collapse Pane
PK
!<g��)R
R
9chrome/en-US/locale/en-US/devtools/client/styleeditor.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!-- LOCALIZATION NOTE : FILE This file contains the Style Editor window strings -->
<!-- LOCALIZATION NOTE : FILE Do not translate commandkeys -->
<!-- LOCALIZATION NOTE : The correct localization of this file might be to keep
     it in English, or another language commonly spoken among web developers.
     You want to make that choice consistent across the developer tools.
     A good criteria is the language in which you'd find the best documentation
     on web development on the web. -->

<!ENTITY newButton.label            "New">
<!ENTITY newButton.tooltip          "Create and append a new style sheet to the document">
<!ENTITY newButton.accesskey        "N">

<!ENTITY importButton.label         "Import…">
<!ENTITY importButton.tooltip       "Import and append an existing style sheet to the document">
<!ENTITY importButton.accesskey     "I">

<!ENTITY visibilityToggle.tooltip   "Toggle style sheet visibility">

<!ENTITY saveButton.label           "Save">
<!ENTITY saveButton.tooltip         "Save this style sheet to a file">
<!ENTITY saveButton.accesskey       "S">

<!ENTITY optionsButton.tooltip      "Style Editor options">

<!-- LOCALIZATION NOTE  (showOriginalSources.label): This is the label on the context
     menu item to toggle showing original sources in the editor. -->
<!ENTITY showOriginalSources.label     "Show original sources">

<!-- LOCALIZATION NOTE  (showOriginalSources.accesskey): This is the access key for
     the menu item to toggle showing original sources in the editor. -->
<!ENTITY showOriginalSources.accesskey  "o">

<!-- LOCALIZATION NOTE  (showMediaSidebar.label): This is the label on the context
     menu item to toggle showing @media rule shortcuts in a sidebar. -->
<!ENTITY showMediaSidebar.label     "Show @media sidebar">

<!-- LOCALIZATION NOTE  (showMediaSidebar.accesskey): This is the access key for
     the menu item to toggle showing the @media sidebar. -->
<!ENTITY showMediaSidebar.accesskey     "m">

<!-- LOCALICATION NOTE  (mediaRules.label): This is shown above the list of @media rules
     in each stylesheet editor sidebar. -->
<!ENTITY mediaRules.label           "@media rules">

<!ENTITY editorTextbox.placeholder  "Type CSS here.">

<!-- LOCALICATION NOTE  (noStyleSheet.label): This is shown when a page has no
     stylesheet. -->
<!ENTITY noStyleSheet.label         "This page has no style sheet.">

<!-- LOCALICATION NOTE  (noStyleSheet-tip-start.label): This is the start of a
     tip sentence shown when there is no stylesheet. It suggests to create a new
     stylesheet and provides an action link to do so. -->
<!ENTITY noStyleSheet-tip-start.label  "Perhaps you'd like to ">
<!-- LOCALICATION NOTE  (noStyleSheet-tip-action.label): This is text for the
     link that triggers creation of a new stylesheet. -->
<!ENTITY noStyleSheet-tip-action.label "append a new style sheet">
<!-- LOCALICATION NOTE  (noStyleSheet-tip-end.label): End of the tip sentence -->
<!ENTITY noStyleSheet-tip-end.label    "?">

<!-- LOCALIZATION NOTE (openLinkNewTab.label): This is the text for the
     context menu item that opens a stylesheet in a new tab -->
<!ENTITY openLinkNewTab.label     "Open Link in New Tab">
PK
!<VZѯ	�	@chrome/en-US/locale/en-US/devtools/client/styleeditor.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE These strings are used inside the Style Editor.
# LOCALIZATION NOTE The correct localization of this file might be to keep it
# in English, or another language commonly spoken among web developers.
# You want to make that choice consistent across the developer tools.
# A good criteria is the language in which you'd find the best documentation
# on web development on the web.

# LOCALIZATION NOTE  (inlineStyleSheet): This is the name used for an style sheet
# that is declared inline in the <style> element. Shown in the stylesheets list.
# the argument is the index (order) of the containing <style> element in the
# document.
inlineStyleSheet=<inline style sheet #%S>

# LOCALIZATION NOTE  (newStyleSheet): This is the default name for a new
# user-created style sheet.
newStyleSheet=New style sheet #%S

# LOCALIZATION NOTE  (ruleCount.label): Semicolon-separated list of plural forms.
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
# This is shown in the style sheets list.
# #1 rule.
# example: 111 rules.
ruleCount.label=#1 rule.;#1 rules.

# LOCALIZATION NOTE  (error-load): This is shown when loading fails.
error-load=Style sheet could not be loaded.

# LOCALIZATION NOTE  (error-save): This is shown when saving fails.
error-save=Style sheet could not be saved.

# LOCALIZATION NOTE  (error-compressed): This is shown when we can't show
# coverage information because the css source is compressed.
error-compressed=Can’t show coverage information for compressed stylesheets

# LOCALIZATION NOTE  (importStyleSheet.title): This is the file picker title,
# when you import a style sheet into the Style Editor.
importStyleSheet.title=Import style sheet

# LOCALIZATION NOTE  (importStyleSheet.filter): This is the *.css filter title
importStyleSheet.filter=CSS files

# LOCALIZATION NOTE  (saveStyleSheet.title): This is the file picker title,
# when you save a style sheet from the Style Editor.
saveStyleSheet.title=Save style sheet

# LOCALIZATION NOTE  (saveStyleSheet.filter): This is the *.css filter title
saveStyleSheet.filter=CSS files

# LOCALIZATION NOTE  (saveStyleSheet.commandkey): This the key to use in
# conjunction with accel (Command on Mac or Ctrl on other platforms) to Save
saveStyleSheet.commandkey=S

PK
!<�|��+/+/5chrome/en-US/locale/en-US/devtools/client/toolbox.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!-- LOCALIZATION NOTE : FILE This file contains the Toolbox strings -->
<!-- LOCALIZATION NOTE : FILE Do not translate key -->

<!ENTITY closeCmd.key  "W">
<!ENTITY toggleToolbox.key  "I">
<!ENTITY toggleToolboxF12.keycode          "VK_F12">
<!ENTITY toggleToolboxF12.keytext          "F12">

<!-- LOCALIZATION NOTE (browserToolboxStatusMessage): This is the label
  -  shown next to status details when the Browser Toolbox fails to connect or
  -  appears to be taking a while to do so. -->
<!ENTITY browserToolboxStatusMessage "Browser Toolbox connection status:">

<!-- LOCALIZATION NOTE (options.context.advancedSettings): This is the label for
  -  the heading of the advanced settings group in the options panel. -->
<!ENTITY options.context.advancedSettings "Advanced settings">

<!-- LOCALIZATION NOTE (options.context.inspector): This is the label for
  -  the heading of the Inspector group in the options panel. -->
<!ENTITY options.context.inspector "Inspector">

<!-- LOCALIZATION NOTE (options.showUserAgentStyles.label): This is the label
  -  for the checkbox option to show user agent styles in the Inspector
  -  panel. -->
<!ENTITY options.showUserAgentStyles.label "Show Browser Styles">
<!ENTITY options.showUserAgentStyles.tooltip "Turning this on will show default styles that are loaded by the browser.">

<!-- LOCALIZATION NOTE (options.collapseAttrs.label): This is the label
  -  for the checkbox option to enable collapse attributes in the Inspector
  -  panel. -->
<!ENTITY options.collapseAttrs.label "Truncate DOM attributes">
<!ENTITY options.collapseAttrs.tooltip "Truncate long attributes in the inspector">

<!-- LOCALIZATION NOTE (options.defaultColorUnit.label): This is the label for a
  -  dropdown list that controls the default color unit used in the inspector.
  -  This label is visible in the options panel. -->
<!ENTITY options.defaultColorUnit.label "Default color unit">

<!-- LOCALIZATION NOTE (options.defaultColorUnit.accesskey): This is the access
  -  key for a dropdown list that controls the default color unit used in the
  -  inspector. This is visible in the options panel. -->
<!ENTITY options.defaultColorUnit.accesskey "U">

<!-- LOCALIZATION NOTE (options.defaultColorUnit.authored): This is used in the
  -  'Default color unit' dropdown list and is visible in the options panel. -->
<!ENTITY options.defaultColorUnit.authored "As Authored">

<!-- LOCALIZATION NOTE (options.defaultColorUnit.hex): This is used in the
  -  'Default color unit' dropdown list and is visible in the options panel. -->
<!ENTITY options.defaultColorUnit.hex "Hex">

<!-- LOCALIZATION NOTE (options.defaultColorUnit.hsl): This is used in the
  -  'Default color unit' dropdown list and is visible in the options panel. -->
<!ENTITY options.defaultColorUnit.hsl "HSL(A)">

<!-- LOCALIZATION NOTE (options.defaultColorUnit.rgb): This is used in the
  -  'Default color unit' dropdown list and is visible in the options panel. -->
<!ENTITY options.defaultColorUnit.rgb "RGB(A)">

<!-- LOCALIZATION NOTE (options.defaultColorUnit.name): This is used in
  -  the 'Default color unit' dropdown list and is visible in the options panel.
  -  -->
<!ENTITY options.defaultColorUnit.name "Color Names">

<!-- LOCALIZATION NOTE (options.context.triggersPageRefresh): This is the
  -  triggers page refresh footnote under the advanced settings group in the
  -  options panel and is used for settings that trigger page reload. -->
<!ENTITY options.context.triggersPageRefresh  "* Current session only, reloads the page">

<!-- LOCALIZATION NOTE (options.enableChrome.label5): This is the label for the
  -  checkbox that toggles chrome debugging, i.e. devtools.chrome.enabled
  -  boolean preference in about:config, in the options panel. -->
<!ENTITY options.enableChrome.label5    "Enable browser chrome and add-on debugging toolboxes">
<!ENTITY options.enableChrome.tooltip3  "Turning this option on will allow you to use various developer tools in browser context (via Tools > Web Developer > Browser Toolbox) and debug add-ons from the Add-ons Manager">

<!-- LOCALIZATION NOTE (options.enableRemote.label3): This is the label for the
  -  checkbox that toggles remote debugging, i.e. devtools.debugger.remote-enabled
  -  boolean preference in about:config, in the options panel. -->
<!ENTITY options.enableRemote.label3    "Enable remote debugging">
<!ENTITY options.enableRemote.tooltip2  "Turning this option on will allow the developer tools to debug a remote instance like Firefox OS">

<!-- LOCALIZATION NOTE (options.disableJavaScript.label,
  -  options.disableJavaScript.tooltip): This is the options panel label and
  -  tooltip for the checkbox that toggles JavaScript on or off. -->
<!ENTITY options.disableJavaScript.label     "Disable JavaScript *">
<!ENTITY options.disableJavaScript.tooltip   "Turning this option on will disable JavaScript for the current tab. If the tab or the toolbox is closed then this setting will be forgotten.">

<!-- LOCALIZATION NOTE (options.disableHTTPCache.label,
  -  options.disableHTTPCache.tooltip): This is the options panel label and
  -  tooltip for the checkbox that toggles the HTTP cache on or off. -->
<!ENTITY options.disableHTTPCache.label     "Disable HTTP Cache (when toolbox is open)">
<!ENTITY options.disableHTTPCache.tooltip   "Turning this option on will disable the HTTP cache for all tabs that have the toolbox open. Service Workers are not affected by this option.">

<!-- LOCALIZATION NOTE (options.enableServiceWorkersHTTP.label,
  -  options.enableServiceWorkersHTTP.tooltip): This is the options panel label and
  -  tooltip for the checkbox that toggles the service workers testing features on or off. This option enables service workers over HTTP. -->
<!ENTITY options.enableServiceWorkersHTTP.label     "Enable Service Workers over HTTP (when toolbox is open)">
<!ENTITY options.enableServiceWorkersHTTP.tooltip   "Turning this option on will enable the service workers over HTTP for all tabs that have the toolbox open.">

<!-- LOCALIZATION NOTE (options.selectDefaultTools.label2): This is the label for
  -  the heading of group of checkboxes corresponding to the default developer
  -  tools. -->
<!ENTITY options.selectDefaultTools.label2    "Default Developer Tools">

<!-- LOCALIZATION NOTE (options.selectAdditionalTools.label): This is the label for
  -  the heading of group of checkboxes corresponding to the developer tools
  -  added by add-ons. This heading is hidden when there is no developer tool
  -  installed by add-ons. -->
<!ENTITY options.selectAdditionalTools.label  "Developer Tools installed by add-ons">

<!-- LOCALIZATION NOTE (options.selectEnabledToolboxButtons.label): This is the label for
  -  the heading of group of checkboxes corresponding to the default developer
  -  tool buttons. -->
<!ENTITY options.selectEnabledToolboxButtons.label     "Available Toolbox Buttons">

<!-- LOCALIZATION NOTE (options.toolNotSupported.label): This is the label for
  -  the explanation of the * marker on a tool which is currently not supported
  -  for the target of the toolbox. -->
<!ENTITY options.toolNotSupported.label  "* Not supported for current toolbox target">

<!-- LOCALIZATION NOTE (options.selectDevToolsTheme.label2): This is the label for
  -  the heading of the radiobox corresponding to the theme of the developer
  -  tools. -->
<!ENTITY options.selectDevToolsTheme.label2   "Themes">

<!-- LOCALIZATION NOTE (options.usedeveditiontheme.*) Options under the
  -  toolbox for enabling and disabling the Developer Edition browser theme. -->
<!ENTITY options.usedeveditiontheme.label   "Use Developer Edition browser theme">
<!ENTITY options.usedeveditiontheme.tooltip "Toggles the Developer Edition browser theme.">

<!-- LOCALIZATION NOTE (options.webconsole.label): This is the label for the
  -  heading of the group of Web Console preferences in the options panel. -->
<!ENTITY options.webconsole.label            "Web Console">

<!-- LOCALIZATION NOTE (options.timestampMessages.label): This is the
   - label for the checkbox that toggles timestamps in the Web Console -->
<!ENTITY options.timestampMessages.label      "Enable timestamps">
<!ENTITY options.timestampMessages.tooltip    "If you enable this option commands and output in the Web Console will display a timestamp">

<!-- LOCALIZATION NOTE (options.debugger.label): This is the label for the
  -  heading of the group of Debugger preferences in the options panel. -->
<!ENTITY options.debugger.label            "Debugger">

<!-- LOCALIZATION NOTE (options.sourceMaps.label): This is the
   - label for the checkbox that toggles source maps in all tools -->
<!ENTITY options.sourceMaps.label      "Enable Source Maps">
<!ENTITY options.sourceMaps.tooltip1   "If you enable this option sources will be mapped in the tools.">

<!-- LOCALIZATION NOTE (options.styleeditor.label): This is the label for the
  -  heading of the group of Style Editor preferences in the options
  -  panel. -->
<!ENTITY options.styleeditor.label            "Style Editor">

<!-- LOCALIZATION NOTE (options.stylesheetAutocompletion.label): This is the
   - label for the checkbox that toggles autocompletion of css in the Style Editor -->
<!ENTITY options.stylesheetAutocompletion.label      "Autocomplete CSS">
<!ENTITY options.stylesheetAutocompletion.tooltip    "Autocomplete CSS properties, values and selectors in Style Editor as you type">

<!-- LOCALIZATION NOTE (options.screenshot.label): This is the label for the
   -  heading of the group of Screenshot preferences in the options
   -  panel. -->
<!ENTITY options.screenshot.label            "Screenshot Behavior">

<!-- LOCALIZATION NOTE (options.screenshot.clipboard.label): This is the
   - label for the checkbox that toggles screenshot to clipboard feature. -->
<!ENTITY options.screenshot.clipboard.label      "Screenshot to clipboard">
<!ENTITY options.screenshot.clipboard.tooltip    "Saves to the screenshot directly to the clipboard">

<!-- LOCALIZATION NOTE (options.screenshot.audio.label): This is the
   - label for the checkbox that toggles the camera shutter audio for screenshot tool -->
<!ENTITY options.screenshot.audio.label      "Play camera shutter sound">
<!ENTITY options.screenshot.audio.tooltip    "Enables the camera audio sound when taking screenshot">

<!-- LOCALIZATION NOTE (options.showPlatformData.label): This is the
  -  label for the checkbox that toggles the display of the platform data in the,
  -  Profiler i.e. devtools.profiler.ui.show-platform-data a boolean preference
  -  in about:config, in the options panel. -->
<!ENTITY options.showPlatformData.label    "Show Gecko platform data">
<!ENTITY options.showPlatformData.tooltip  "If you enable this option the JavaScript Profiler reports will include
Gecko platform symbols">

<!-- LOCALIZATION NOTE (options.sourceeditor.*): Options under the editor
  -  section. -->

<!ENTITY options.sourceeditor.label                     "Editor Preferences">
<!ENTITY options.sourceeditor.detectindentation.label   "Detect indentation">
<!ENTITY options.sourceeditor.detectindentation.tooltip "Guess indentation based on source content">
<!ENTITY options.sourceeditor.autoclosebrackets.label   "Autoclose brackets">
<!ENTITY options.sourceeditor.autoclosebrackets.tooltip "Automatically insert closing brackets">
<!ENTITY options.sourceeditor.expandtab.label           "Indent using spaces">
<!ENTITY options.sourceeditor.expandtab.tooltip         "Use spaces instead of the tab character">
<!ENTITY options.sourceeditor.tabsize.label             "Tab size">
<!ENTITY options.sourceeditor.tabsize.accesskey         "T">
<!ENTITY options.sourceeditor.keybinding.label          "Keybindings">
<!ENTITY options.sourceeditor.keybinding.accesskey      "K">
<!ENTITY options.sourceeditor.keybinding.default.label  "Default">
PK
!<��Y�#�#<chrome/en-US/locale/en-US/devtools/client/toolbox.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

toolboxDockButtons.bottom.tooltip=Dock to bottom of browser window
toolboxDockButtons.side.tooltip=Dock to side of browser window
toolboxDockButtons.window.tooltip=Show in separate window

# LOCALIZATION NOTE (toolboxDockButtons.bottom.minimize): This string is shown
# as a tooltip that appears in the toolbox when it is in "bottom host" mode and
# when hovering over the minimize button in the toolbar. When clicked, the
# button minimizes the toolbox so that just the toolbar is visible at the
# bottom.
toolboxDockButtons.bottom.minimize=Minimize the toolbox

# LOCALIZATION NOTE (toolboxDockButtons.bottom.maximize): This string is shown
# as a tooltip that appears in the toolbox when it is in "bottom host" mode and
# when hovering over the maximize button in the toolbar. When clicked, the
# button maximizes the toolbox again (if it had been minimized before) so that
# the whole toolbox is visible again.
toolboxDockButtons.bottom.maximize=Maximize the toolbox

# LOCALIZATION NOTE (toolboxToggleButton.errors): Semi-colon list of plural
# forms.
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
# #1 number of errors in the current web page
toolboxToggleButton.errors=#1 error;#1 errors

# LOCALIZATION NOTE (toolboxToggleButton.warnings): Semi-colon list of plural
# forms.
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
# #1 number of warnings in the current web page
toolboxToggleButton.warnings=#1 warning;#1 warnings

# LOCALIZATION NOTE (toolboxToggleButton.tooltip): This string is shown
# as tooltip in the developer toolbar to open/close the developer tools.
# It's using toolboxToggleButton.errors as first and
# toolboxToggleButton.warnings as second argument to show the number of errors
# and warnings.
toolboxToggleButton.tooltip=%1$S, %2$S\nClick to toggle the developer tools.

# LOCALIZATION NOTE (toolbar.closeButton.tooltip)
# Used as a message in tooltip when overing the close button of the Developer
# Toolbar.
toolbar.closeButton.tooltip=Close Developer Toolbar

# LOCALIZATION NOTE (toolbar.toolsButton.tooltip)
# Used as a message in tooltip when overing the wrench icon of the Developer
# Toolbar, which toggle the developer toolbox.
toolbar.toolsButton.tooltip=Toggle developer tools

# LOCALIZATION NOTE (toolbox.titleTemplate1): This is the template
# used to format the title of the toolbox.
# The URL of the page being targeted: %1$S.
toolbox.titleTemplate1=Developer Tools - %1$S

# LOCALIZATION NOTE (toolbox.titleTemplate2): This is the template
# used to format the title of the toolbox.
# The page title or other name for the thing being targeted: %1$S
# The URL of the page being targeted: %2$S.
toolbox.titleTemplate2=Developer Tools - %1$S - %2$S

# LOCALIZATION NOTE (toolbox.defaultTitle): This is used as the tool
# name when no tool is selected.
toolbox.defaultTitle=Developer Tools

# LOCALIZATION NOTE (toolbox.label): This is used as the label for the
# toolbox as a whole
toolbox.label=Developer Tools

# LOCALIZATION NOTE (options.toolNotSupportedMarker): This is the template
# used to add a * marker to the label for the Options Panel tool checkbox for the
# tool which is not supported for the current toolbox target.
# The name of the tool: %1$S.
options.toolNotSupportedMarker=%1$S *

# LOCALIZATION NOTE (scratchpad.keycode)
# Used for opening scratchpad from the detached toolbox window
# Needs to match scratchpad.keycode from browser.dtd
scratchpad.keycode=VK_F4

# LOCALIZATION NOTE (browserConsoleCmd.commandkey)
# Used for toggling the browser console from the detached toolbox window
# Needs to match browserConsoleCmd.commandkey from browser.dtd
browserConsoleCmd.commandkey=j

# LOCALIZATION NOTE (pickButton.tooltip)
# This is the tooltip of the pick button in the toolbox toolbar
pickButton.tooltip=Pick an element from the page

# LOCALIZATION NOTE (sidebar.showAllTabs.tooltip)
# This is the tooltip shown when hover over the '…' button in the tabbed side
# bar, when there's no enough space to show all tabs at once
sidebar.showAllTabs.tooltip=All tabs

# LOCALIZATION NOTE (toolbox.noContentProcessForTab.message)
# Used as a message in the alert displayed when trying to open a browser
# content toolbox and there is no content process running for the current tab
toolbox.noContentProcessForTab.message=No content process for this tab.

# LOCALIZATION NOTE (toolbox.viewCssSourceInStyleEditor.label)
# Used as a message in either tooltips or contextual menu items to open the
# corresponding URL as a css file in the Style-Editor tool.
# DEV NOTE: Mostly used wherever toolbox.viewSourceInStyleEditor is used.
toolbox.viewCssSourceInStyleEditor.label=Open File in Style-Editor

# LOCALIZATION NOTE (toolbox.viewJsSourceInDebugger.label)
# Used as a message in either tooltips or contextual menu items to open the
# corresponding URL as a js file in the Debugger tool.
# DEV NOTE: Mostly used wherever toolbox.viewSourceInDebugger is used.
toolbox.viewJsSourceInDebugger.label=Open File in Debugger

toolbox.resumeOrderWarning=Page did not resume after the debugger was attached. To fix this, please close and re-open the toolbox.

# LOCALIZATION NOTE (toolbox.options.key)
# Key shortcut used to open the options panel
toolbox.options.key=CmdOrCtrl+Shift+O

# LOCALIZATION NOTE (toolbox.help.key)
# Key shortcut used to open the options panel
toolbox.help.key=F1

# LOCALIZATION NOTE (toolbox.nextTool.key)
# Key shortcut used to select the next tool
toolbox.nextTool.key=CmdOrCtrl+]

# LOCALIZATION NOTE (toolbox.previousTool.key)
# Key shortcut used to select the previous tool
toolbox.previousTool.key=CmdOrCtrl+[

# LOCALIZATION NOTE (toolbox.zoom*.key)
# Key shortcuts used to zomm in/out or reset the toolbox
# Should match fullZoom*Cmd.commandkey values from browser.dtd
toolbox.zoomIn.key=CmdOrCtrl+Plus
toolbox.zoomIn2.key=CmdOrCtrl+=
toolbox.zoomIn3.key=

toolbox.zoomOut.key=CmdOrCtrl+-
toolbox.zoomOut2.key=

toolbox.zoomReset.key=CmdOrCtrl+0
toolbox.zoomReset2.key=

# LOCALIZATION NOTE (toolbox.reload*.key)
# Key shortcuts used to reload the page
toolbox.reload.key=CmdOrCtrl+R
toolbox.reload2.key=F5

# LOCALIZATION NOTE (toolbox.forceReload*.key)
# Key shortcuts used to force reload of the page by bypassing caches
toolbox.forceReload.key=CmdOrCtrl+Shift+R
toolbox.forceReload2.key=CmdOrCtrl+F5

# LOCALIZATION NOTE (toolbox.minimize.key)
# Key shortcut used to minimize the toolbox
toolbox.minimize.key=CmdOrCtrl+Shift+U

# LOCALIZATION NOTE (toolbox.toggleHost.key)
# Key shortcut used to move the toolbox in bottom or side of the browser window
toolbox.toggleHost.key=CmdOrCtrl+Shift+D

# LOCALIZATION NOTE (toolbox.frames.tooltip): This is the label for
# the iframes menu list that appears only when the document has some.
# It allows you to switch the context of the whole toolbox.
toolbox.frames.tooltip=Select an iframe as the currently targeted document

# LOCALIZATION NOTE (toolbox.showFrames.key)
# Key shortcut used to show frames menu when 'frames' button is focused
toolbox.showFrames.key=Alt+Down

# LOCALIZATION NOTE (toolbox.noautohide.tooltip): This is the label for
# the button to force the popups/panels to stay visible on blur.
# This is only visible in the browser toolbox as it is meant for
# addon developers and Firefox contributors.
toolbox.noautohide.tooltip=Disable popup auto hide

# LOCALIZATION NOTE (toolbox.closebutton.tooltip): This is the tooltip for
# the close button the developer tools toolbox.
toolbox.closebutton.tooltip=Close Developer Tools

# LOCALIZATION NOTE (toolbox.allToolsButton.tooltip): This is the tooltip for the
# "all tools" button displayed when some tools are hidden by overflow of the toolbar.
toolbox.allToolsButton.tooltip=Select another tool

# LOCALIZATION NOTE (toolbox.sourceMapFailure): This is shown in the web console
# when there is a failure to fetch or parse a source map.
# The text of the error: %1$S
# The URL that caused DevTools to try to fetch a source map: %2$S
# The URL of the source map itself: %3$S
toolbox.sourceMapFailure=Source map error: %1$S\nResource URL: %2$S\nSource Map URL: %3$S

# LOCALIZATION NOTE (toolbox.sourceMapSourceFailure): This is shown in
# the web console when there is a failure to fetch or parse an
# original source that was mentioned in a source map.
# The text of the error: %1$S
# The URL of the source: %2$S
toolbox.sourceMapSourceFailure=Error while fetching an original source: %1$S\nSource URL: %2$S

# LOCALIZATION NOTE (toolbox.options.enableNewDebugger.label): Label of the options panel
# checkbox to enable the new debugger frontend. Displayed only in Nightly and local
# builds.
toolbox.options.enableNewDebugger.label=Enable new debugger frontend

# LOCALIZATION NOTE (toolbox.options.enableNewConsole.label): Label of the options panel
# checkbox to enable the new console frontend. Displayed only in Nightly and local builds.
toolbox.options.enableNewConsole.label=Enable new console frontend
PK
!<R"�--8chrome/en-US/locale/en-US/devtools/client/webConsole.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!-- LOCALIZATION NOTE : FILE The correct localization of this file might be to
  - keep it in English, or another language commonly spoken among web developers.
  - You want to make that choice consistent across the developer tools.
  - A good criteria is the language in which you'd find the best
  - documentation on web development on the web. -->
<!ENTITY window.title "Web Console">
<!-- LOCALIZATION NOTE (openURL.label): You can see this string in the Web
   - Console context menu. -->
<!ENTITY openURL.label     "Open URL in New Tab">
<!ENTITY openURL.accesskey "T">
<!-- LOCALIZATION NOTE (btnPageNet.label): This string is used for the menu
  -  button that allows users to toggle the network logging output.
  -  This string and the following strings toggle various kinds of output
  -  filters. -->
<!ENTITY btnPageNet.label   "Net">
<!ENTITY btnPageNet.tooltip "Log network access">
<!ENTITY btnPageNet.accesskey "N">
<!-- LOCALIZATION NOTE (btnPageNet.accesskeyMacOSX): This string is used as
  -  access key for the menu button that allows users to toggle the network
  -  logging output. On MacOSX accesskeys are available with Ctrl-*. Please make
  -  sure you do not use the following letters: A, E, N and P. These are used
  -  for editing commands in text inputs. -->
<!ENTITY btnPageNet.accesskeyMacOSX "t">
<!ENTITY btnPageCSS.label   "CSS">
<!ENTITY btnPageCSS.tooltip2 "Log CSS errors and warnings">
<!ENTITY btnPageCSS.accesskey "C">
<!ENTITY btnPageJS.label    "JS">
<!ENTITY btnPageJS.tooltip  "Log JavaScript exceptions">
<!ENTITY btnPageJS.accesskey  "J">
<!ENTITY btnPageSecurity.label "Security">
<!ENTITY btnPageSecurity.tooltip "Log security errors and warnings">
<!ENTITY btnPageSecurity.accesskey "u">

<!-- LOCALIZATION NOTE (btnPageLogging): This is used as the text of the
  -  the toolbar. It shows or hides messages that the web developer inserted on
  -  the page for debugging purposes, using calls such console.log() and
  -  console.error(). -->
<!ENTITY btnPageLogging.label   "Logging">
<!ENTITY btnPageLogging.tooltip "Log messages sent to the window.console object">
<!ENTITY btnPageLogging.accesskey3 "L">
<!ENTITY btnConsoleErrors       "Errors">
<!ENTITY btnConsoleInfo         "Info">
<!ENTITY btnConsoleWarnings     "Warnings">
<!ENTITY btnConsoleLog          "Log">
<!ENTITY btnConsoleXhr          "XHR">
<!ENTITY btnConsoleReflows      "Reflows">

<!-- LOCALIZATION NOTE (btnServerLogging): This is used as the text of the
  -  the toolbar. It shows or hides messages that the web developer inserted on
  -  the page for debugging purposes, using calls on the HTTP server. -->
<!ENTITY btnServerLogging.label       "Server">
<!ENTITY btnServerLogging.tooltip     "Log messages received from a web server">
<!ENTITY btnServerLogging.accesskey   "S">
<!ENTITY btnServerErrors              "Errors">
<!ENTITY btnServerInfo                "Info">
<!ENTITY btnServerWarnings            "Warnings">
<!ENTITY btnServerLog                 "Log">

<!-- LOCALIZATION NODE (btnConsoleSharedWorkers) the term "Shared Workers"
  -  should not be translated. -->
<!ENTITY btnConsoleSharedWorkers "Shared Workers">
<!-- LOCALIZATION NODE (btnConsoleServiceWorkers) the term "Service Workers"
  -  should not be translated. -->
<!ENTITY btnConsoleServiceWorkers "Service Workers">
<!-- LOCALIZATION NODE (btnConsoleWindowlessWorkers) the term "Workers"
  -  should not be translated. -->
<!ENTITY btnConsoleWindowlessWorkers "Add-on or Chrome Workers">

<!ENTITY filterOutput.placeholder "Filter output">
<!ENTITY btnClear.label        "Clear">
<!ENTITY btnClear.tooltip      "Clear the Web Console output">
<!ENTITY btnClear.accesskey    "r">

<!ENTITY fullZoomEnlargeCmd.commandkey  "+">
<!ENTITY fullZoomEnlargeCmd.commandkey2 "="> <!-- + is above this key on many keyboards -->
<!ENTITY fullZoomEnlargeCmd.commandkey3 "">

<!ENTITY fullZoomReduceCmd.commandkey   "-">
<!ENTITY fullZoomReduceCmd.commandkey2  "">

<!ENTITY fullZoomResetCmd.commandkey    "0">
<!ENTITY fullZoomResetCmd.commandkey2   "">

<!ENTITY copyURLCmd.label     "Copy Link Location">
<!ENTITY copyURLCmd.accesskey "a">

<!ENTITY closeCmd.key         "W">
<!ENTITY findCmd.key          "F">
<!ENTITY clearOutputCtrl.key  "L">
<!ENTITY openInVarViewCmd.label "Open in Variables View">
<!ENTITY openInVarViewCmd.accesskey "V">
<!ENTITY storeAsGlobalVar.label "Store as global variable">
<!ENTITY storeAsGlobalVar.accesskey "S">
PK
!<J�}�**<chrome/en-US/locale/en-US/devtools/client/webaudioeditor.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!-- LOCALIZATION NOTE : FILE This file contains the Debugger strings -->
<!-- LOCALIZATION NOTE : FILE Do not translate commandkey -->

<!-- LOCALIZATION NOTE : FILE The correct localization of this file might be to
  - keep it in English, or another language commonly spoken among web developers.
  - You want to make that choice consistent across the developer tools.
  - A good criteria is the language in which you'd find the best
  - documentation on web development on the web. -->

<!-- LOCALIZATION NOTE (webAudioEditorUI.reloadNotice1): This is the label shown
  -  on the button that triggers a page refresh. -->
<!ENTITY webAudioEditorUI.reloadNotice1   "Reload">

<!-- LOCALIZATION NOTE (webAudioEditorUI.reloadNotice2): This is the label shown
  -  along with the button that triggers a page refresh. -->
<!ENTITY webAudioEditorUI.reloadNotice2   "the page to view and edit the audio context.">

<!-- LOCALIZATION NOTE (webAudioEditorUI.emptyNotice): This is the label shown
  -  while the page is refreshing and the tool waits for a audio context. -->
<!ENTITY webAudioEditorUI.emptyNotice     "Waiting for an audio context to be created…">

<!-- LOCALIZATION NOTE (webAudioEditorUI.tab.properties2): This is the label shown
  -  for the properties tab view. -->
<!ENTITY webAudioEditorUI.tab.properties2 "Properties">

<!-- LOCALIZATION NOTE (webAudioEditorUI.tab.automation): This is the label shown
  -  for the automation tab view. -->
<!ENTITY webAudioEditorUI.tab.automation  "Automation">

<!-- LOCALIZATION NOTE (webAudioEditorUI.inspectorTitle): This is the title for the
  -  AudioNode inspector view. -->
<!ENTITY webAudioEditorUI.inspectorTitle  "AudioNode Inspector">

<!-- LOCALIZATION NOTE (webAudioEditorUI.inspectorEmpty): This is the title for the
  -  AudioNode inspector view empty message. -->
<!ENTITY webAudioEditorUI.inspectorEmpty  "No AudioNode selected.">

<!-- LOCALIZATION NOTE (webAudioEditorUI.propertiesEmpty): This is the title for the
  -  AudioNode inspector view properties tab empty message. -->
<!ENTITY webAudioEditorUI.propertiesEmpty "Node does not have any properties.">

<!-- LOCALIZATION NOTE (webAudioEditorUI.automationEmpty): This is the title for the
  -  AudioNode inspector view automation tab empty message. -->
<!ENTITY webAudioEditorUI.automationEmpty "Node does not have any AudioParams.">

<!-- LOCALIZATION NOTE (webAudioEditorUI.automationNoEvents): This is the title for the
  -  AudioNode inspector view automation tab message when there are no automation
  -  events. -->
<!ENTITY webAudioEditorUI.automationNoEvents "AudioParam does not have any automation events.">
PK
!<��Cchrome/en-US/locale/en-US/devtools/client/webaudioeditor.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE These strings are used inside the Web Audio tool
# which is available in the developer tools' toolbox, once
# enabled in the developer tools' preference "Web Audio".
# The correct localization of this file might be to keep it in
# English, or another language commonly spoken among web developers.
# You want to make that choice consistent across the developer tools.
# A good criteria is the language in which you'd find the best
# documentation on web development on the web.

# LOCALIZATION NOTE (collapseInspector): This is the tooltip for the button
# that collapses the inspector in the web audio tool UI.
collapseInspector=Collapse inspector

# LOCALIZATION NOTE (expandInspector): This is the tooltip for the button
# that expands the inspector in the web audio tool UI.
expandInspector=Expand inspector
PK
!<����=�=?chrome/en-US/locale/en-US/devtools/client/webconsole.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# LOCALIZATION NOTE
# The correct localization of this file might be to keep it in
# English, or another language commonly spoken among web developers.
# You want to make that choice consistent across the developer tools.
# A good criteria is the language in which you'd find the best
# documentation on web development on the web.
# LOCALIZATION NOTE (browserConsole.title): shown as the
# title when opening the browser console popup
browserConsole.title=Browser Console
# LOCALIZATION NOTE (timestampFormat): %1$02S = hours (24-hour clock),
# %2$02S = minutes, %3$02S = seconds, %4$03S = milliseconds.
timestampFormat=%02S:%02S:%02S.%03S
helperFuncUnsupportedTypeError=Can’t call pprint on this type of object.
# LOCALIZATION NOTE (NetworkPanel.durationMS): this string is used to
# show the duration between two network events (e.g request and response
# header or response header and response body). Parameters: %S is the duration.
NetworkPanel.durationMS=%Sms

ConsoleAPIDisabled=The Web Console logging API (console.log, console.info, console.warn, console.error) has been disabled by a script on this page.

# LOCALIZATION NOTE (webConsoleXhrIndicator): the indicator displayed before
# a URL in the Web Console that was requested using an XMLHttpRequest.
# Should probably be the same as &btnConsoleXhr; in webConsole.dtd
webConsoleXhrIndicator=XHR

# LOCALIZATION NOTE (webConsoleMixedContentWarning): the message displayed
# after a URL in the Web Console that has been flagged for Mixed Content (i.e.
# http content in an https page).
webConsoleMixedContentWarning=Mixed Content

# LOCALIZATION NOTE (webConsoleMoreInfoLabel): the more info tag displayed
# after security related web console messages.
webConsoleMoreInfoLabel=Learn More

# LOCALIZATION NOTE (scratchpad.linkText): the text used in the right hand
# side of the Web Console command line when JavaScript is being entered, to
# indicate how to jump into scratchpad mode.
scratchpad.linkText=Shift+RETURN - Open in Scratchpad

# LOCALIZATION NOTE (reflow.*): the console displays reflow activity.
# We can get 2 kind of lines: with JS link or without JS link. It looks like
# that:
# reflow: 12ms
# reflow: 12ms function foobar, file.js line 42
# The 2nd line, from "function" to the end of the line, is a link to the
# JavaScript debugger.
reflow.messageWithNoLink=reflow: %Sms
reflow.messageWithLink=reflow: %Sms\u0020
reflow.messageLinkText=function %1$S, %2$S line %3$S

# LOCALIZATION NOTE (stacktrace.anonymousFunction): this string is used to
# display JavaScript functions that have no given name - they are said to be
# anonymous. Test console.trace() in the webconsole.
stacktrace.anonymousFunction=<anonymous>

# LOCALIZATION NOTE (stacktrace.asyncStack): this string is used to
# indicate that a given stack frame has an async parent.
# %S is the "Async Cause" of the frame.
stacktrace.asyncStack=(Async: %S)

# LOCALIZATION NOTE (timerStarted): this string is used to display the result
# of the console.time() call. Parameters: %S is the name of the timer.
timerStarted=%S: timer started

# LOCALIZATION NOTE (timeEnd): this string is used to display the result of
# the console.timeEnd() call. Parameters: %1$S is the name of the timer, %2$S
# is the number of milliseconds.
timeEnd=%1$S: %2$Sms

# LOCALIZATION NOTE (consoleCleared): this string is displayed when receiving a
# call to console.clear() to let the user know the previous messages of the
# console have been removed programmatically.
consoleCleared=Console was cleared.

# LOCALIZATION NOTE (noCounterLabel): this string is used to display
# count-messages with no label provided.
noCounterLabel=<no label>

# LOCALIZATION NOTE (noGroupLabel): this string is used to display
# console.group messages with no label provided.
noGroupLabel=<no group label>

# LOCALIZATION NOTE (Autocomplete.blank): this string is used when inputnode
# string containing anchor doesn't matches to any property in the content.
Autocomplete.blank=  <- no result

maxTimersExceeded=The maximum allowed number of timers in this page was exceeded.
timerAlreadyExists=Timer “%S” already exists.
timerDoesntExist=Timer “%S” doesn’t exist.
timerJSError=Failed to process the timer name.

# LOCALIZATION NOTE (maxCountersExceeded): Error message shown when the maximum
# number of console.count()-counters was exceeded.
maxCountersExceeded=The maximum allowed number of counters in this page was exceeded.

# LOCALIZATION NOTE (longStringEllipsis): the string displayed after a long
# string. This string is clickable such that the rest of the string is
# retrieved from the server.
longStringEllipsis=[…]

# LOCALIZATION NOTE (longStringTooLong): the string displayed after the user
# tries to expand a long string.
longStringTooLong=The string you are trying to view is too long to be displayed by the Web Console.

# LOCALIZATION NOTE (connectionTimeout): message displayed when the Remote Web
# Console fails to connect to the server due to a timeout.
connectionTimeout=Connection timeout. Check the Error Console on both ends for potential error messages. Reopen the Web Console to try again.

# LOCALIZATION NOTE (propertiesFilterPlaceholder): this is the text that
# appears in the filter text box for the properties view container.
propertiesFilterPlaceholder=Filter properties

# LOCALIZATION NOTE (emptyPropertiesList): the text that is displayed in the
# properties pane when there are no properties to display.
emptyPropertiesList=No properties to display

# LOCALIZATION NOTE (messageRepeats.tooltip2): the tooltip text that is displayed
# when you hover the red bubble that shows how many times a message is repeated
# in the web console output.
# This is a semi-colon list of plural forms.
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
# #1 number of message repeats
# example: 3 repeats
messageRepeats.tooltip2=#1 repeat;#1 repeats

# LOCALIZATION NOTE (openNodeInInspector): the text that is displayed in a
# tooltip when hovering over the inspector icon next to a DOM Node in the console
# output
openNodeInInspector=Click to select the node in the inspector

# LOCALIZATION NOTE (cdFunctionInvalidArgument): the text that is displayed when
# cd() is invoked with an invalid argument.
cdFunctionInvalidArgument=Cannot cd() to the given window. Invalid argument.

# LOCALIZATION NOTE (selfxss.msg): the text that is displayed when
# a new user of the developer tools pastes code into the console
# %1 is the text of selfxss.okstring
selfxss.msg=Scam Warning: Take care when pasting things you don’t understand. This could allow attackers to steal your identity or take control of your computer. Please type ‘%S’ below (no need to press enter) to allow pasting.

# LOCALIZATION NOTE (selfxss.okstring): the string to be typed
# in by a new user of the developer tools when they receive the sefxss.msg prompt.
# Please avoid using non-keyboard characters here
selfxss.okstring=allow pasting

# LOCALIZATION NOTE (messageToggleDetails): the text that is displayed when
# you hover the arrow for expanding/collapsing the message details. For
# console.error() and other messages we show the stacktrace.
messageToggleDetails=Show/hide message details.

# LOCALIZATION NOTE (groupToggle): the text that is displayed when
# you hover the arrow for expanding/collapsing the messages of a group.
groupToggle=Show/hide group.

# LOCALIZATION NOTE (emptySlotLabel): the text is displayed when an Array
# with empty slots is printed to the console.
# This is a semi-colon list of plural forms.
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
# #1 number of empty slots
# example: 1 empty slot
# example: 5 empty slots
emptySlotLabel=#1 empty slot;#1 empty slots

# LOCALIZATION NOTE (table.index, table.iterationIndex, table.key, table.value):
# the column header displayed in the console table widget.
table.index=(index)
table.iterationIndex=(iteration index)
table.key=Key
table.value=Values

# LOCALIZATION NOTE (severity.error, severity.warn, severity.info, severity.log):
# tooltip for icons next to console output
severity.error=Error
severity.warn=Warning
severity.info=Info
severity.log=Log

# LOCALIZATION NOTE (level.error, level.warn, level.info, level.log, level.debug):
# tooltip for icons next to console output
level.error=Error
level.warn=Warning
level.info=Info
level.log=Log
level.debug=Debug

# LOCALIZATION NOTE (webconsole.find.key)
# Key shortcut used to focus the search box on upper right of the console
webconsole.find.key=CmdOrCtrl+F

# LOCALIZATION NOTE (webconsole.close.key)
# Key shortcut used to close the Browser console (doesn't work in regular web console)
webconsole.close.key=CmdOrCtrl+W

# LOCALIZATION NOTE (webconsole.clear.key*)
# Key shortcut used to clear the console output
webconsole.clear.key=Ctrl+Shift+L
webconsole.clear.keyOSX=Ctrl+L

# LOCALIZATION NOTE (webconsole.menu.copyURL.label)
# Label used for a context-menu item displayed for network message logs. Clicking on it
# copies the URL displayed in the message to the clipboard.
webconsole.menu.copyURL.label=Copy Link Location
webconsole.menu.copyURL.accesskey=a

# LOCALIZATION NOTE (webconsole.menu.openURL.label)
# Label used for a context-menu item displayed for network message logs. Clicking on it
# opens the URL displayed in a new browser tab.
webconsole.menu.openURL.label=Open URL in New Tab
webconsole.menu.openURL.accesskey=T

# LOCALIZATION NOTE (webconsole.menu.openInNetworkPanel.label)
# Label used for a context-menu item displayed for network message logs. Clicking on it
# opens the network message in the Network panel
webconsole.menu.openInNetworkPanel.label=Open in Network Panel
webconsole.menu.openInNetworkPanel.accesskey=N

# LOCALIZATION NOTE (webconsole.menu.openInVarView.label)
# Label used for a context-menu item displayed for object/variable logs. Clicking on it
# opens the webconsole variable view for the logged variable.
webconsole.menu.openInVarView.label=Open in Variables View
webconsole.menu.openInVarView.accesskey=V

# LOCALIZATION NOTE (webconsole.menu.storeAsGlobalVar.label)
# Label used for a context-menu item displayed for object/variable logs. Clicking on it
# creates a new global variable pointing to the logged variable.
webconsole.menu.storeAsGlobalVar.label=Store as global variable
webconsole.menu.storeAsGlobalVar.accesskey=S

# LOCALIZATION NOTE (webconsole.menu.copyMessage.label)
# Label used for a context-menu item displayed for any log. Clicking on it will copy the
# content of the log (or the user selection, if any).
webconsole.menu.copyMessage.label=Copy message
webconsole.menu.copyMessage.accesskey=C

# LOCALIZATION NOTE (webconsole.menu.copyObject.label)
# Label used for a context-menu item displayed for object/variable log. Clicking on it
# will copy the object/variable.
webconsole.menu.copyObject.label=Copy object
webconsole.menu.copyObject.accesskey=o

# LOCALIZATION NOTE (webconsole.menu.selectAll.label)
# Label used for a context-menu item that will select all the content of the webconsole
# output.
webconsole.menu.selectAll.label=Select all
webconsole.menu.selectAll.accesskey=A

# LOCALIZATION NOTE (webconsole.menu.openInSidebar.label)
# Label used for a context-menu item displayed for object/variable logs. Clicking on it
# opens the webconsole sidebar for the logged variable.
webconsole.menu.openInSidebar.label=Open in sidebar
webconsole.menu.openInSidebar.accesskey=V

# LOCALIZATION NOTE (webconsole.clearButton.tooltip)
# Label used for the tooltip on the clear logs button in the console top toolbar bar.
# Clicking on it will clear the content of the console.
webconsole.clearButton.tooltip=Clear the Web Console output

# LOCALIZATION NOTE (webconsole.toggleFilterButton.tooltip)
# Label used for the tooltip on the toggle filter bar button in the console top
# toolbar bar. Clicking on it will toggle the visibility of an additional bar which
# contains filter buttons.
webconsole.toggleFilterButton.tooltip=Toggle filter bar

# LOCALIZATION NOTE (webconsole.filterInput.placeholder)
# Label used for for the placeholder on the filter input, in the console top toolbar.
webconsole.filterInput.placeholder=Filter output

# LOCALIZATION NOTE (webconsole.errorsFilterButton.label)
# Label used as the text of the "Errors" button in the additional filter toolbar.
# It shows or hides error messages, either inserted in the page using
# console.error() or as a result of a javascript error..
webconsole.errorsFilterButton.label=Errors

# LOCALIZATION NOTE (webconsole.warningsFilterButton.label)
# Label used as the text of the "Warnings" button in the additional filter toolbar.
# It shows or hides warning messages, inserted in the page using console.warn().
webconsole.warningsFilterButton.label=Warnings

# LOCALIZATION NOTE (webconsole.logsFilterButton.label)
# Label used as the text of the "Logs" button in the additional filter toolbar.
# It shows or hides log messages, inserted in the page using console.log().
webconsole.logsFilterButton.label=Logs

# LOCALIZATION NOTE (webconsole.infoFilterButton.label)
# Label used as the text of the "Info" button in the additional filter toolbar.
# It shows or hides info messages, inserted in the page using console.info().
webconsole.infoFilterButton.label=Info

# LOCALIZATION NOTE (webconsole.debugFilterButton.label)
# Label used as the text of the "Debug" button in the additional filter toolbar.
# It shows or hides debug messages, inserted in the page using console.debug().
webconsole.debugFilterButton.label=Debug

# LOCALIZATION NOTE (webconsole.cssFilterButton.label)
# Label used as the text of the "CSS" button in the additional filter toolbar.
# It shows or hides CSS warning messages, inserted in the page by the browser
# when there are CSS errors in the page.
webconsole.cssFilterButton.label=CSS

# LOCALIZATION NOTE (webconsole.xhrFilterButton.label)
# Label used as the text of the "XHR" button in the additional filter toolbar.
# It shows or hides messages displayed when the page makes an XMLHttpRequest or
# a fetch call.
webconsole.xhrFilterButton.label=XHR

# LOCALIZATION NOTE (webconsole.requestsFilterButton.label)
# Label used as the text of the "Requests" button in the additional filter toolbar.
# It shows or hides messages displayed when the page makes a network call, for example
# when an image or a scripts is requested.
webconsole.requestsFilterButton.label=Requests

# LOCALIZATION NOTE (webconsole.filteredMessages.label)
# Text of the "filtered messages" bar, shown when console messages are hidden
# because the user has set non-default filters in the filter bar.
# This is a semi-colon list of plural forms.
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
# example: 345 items hidden by filters.
webconsole.filteredMessages.label=#1 item hidden by filters;#1 items hidden by filters

# Label used as the text of the "Reset filters" button in the "filtered messages" bar.
# It resets the default filters of the console to their original values.
webconsole.resetFiltersButton.label=Reset filters

# LOCALIZATION NOTE (webconsole.enablePersistentLogs.label)
webconsole.enablePersistentLogs.label=Persist Logs
# LOCALIZATION NOTE (webconsole.enablePersistentLogs.tooltip)
webconsole.enablePersistentLogs.tooltip=If you enable this option the output will not be cleared each time you navigate to a new page

# LOCALIZATION NOTE (webconsole.navigated): this string is used in the console when the
# current inspected page is navigated to a new location.
# Parameters: %S is the new URL.
webconsole.navigated=Navigated to %S
PK
!<�� ���4chrome/en-US/locale/en-US/devtools/client/webide.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!ENTITY windowTitle "Firefox WebIDE">

<!ENTITY projectMenu_label "Project">
<!ENTITY projectMenu_accesskey "P">
<!ENTITY projectMenu_newApp_label "New App…">
<!ENTITY projectMenu_newApp_accesskey "N">
<!ENTITY projectMenu_importPackagedApp_label "Open Packaged App…">
<!ENTITY projectMenu_importPackagedApp_accesskey "P">
<!ENTITY projectMenu_importHostedApp_label "Open Hosted App…">
<!ENTITY projectMenu_importHostedApp_accesskey "H">
<!ENTITY projectMenu_selectApp_label "Open App…">
<!ENTITY projectMenu_selectApp_accesskey "O">
<!ENTITY projectMenu_play_label "Install and Run">
<!ENTITY projectMenu_play_accesskey "I">
<!ENTITY projectMenu_stop_label "Stop App">
<!ENTITY projectMenu_stop_accesskey "S">
<!ENTITY projectMenu_debug_label "Debug App">
<!ENTITY projectMenu_debug_accesskey "D">
<!ENTITY projectMenu_remove_label "Remove Project">
<!ENTITY projectMenu_remove_accesskey "R">
<!ENTITY projectMenu_showPrefs_label "Preferences">
<!ENTITY projectMenu_showPrefs_accesskey "e">
<!ENTITY projectMenu_manageComponents_label "Manage Extra Components">
<!ENTITY projectMenu_manageComponents_accesskey "M">
<!ENTITY projectMenu_refreshTabs_label "Refresh Tabs">

<!ENTITY runtimeMenu_label "Runtime">
<!ENTITY runtimeMenu_accesskey "R">
<!ENTITY runtimeMenu_disconnect_label "Disconnect">
<!ENTITY runtimeMenu_disconnect_accesskey "D">
<!ENTITY runtimeMenu_takeScreenshot_label "Screenshot">
<!ENTITY runtimeMenu_takeScreenshot_accesskey "S">
<!ENTITY runtimeMenu_showDetails_label "Runtime Info">
<!ENTITY runtimeMenu_showDetails_accesskey "E">
<!ENTITY runtimeMenu_showDevicePrefs_label "Device Preferences">
<!ENTITY runtimeMenu_showDevicePrefs_accesskey "D">
<!ENTITY runtimeMenu_showSettings_label "Device Settings">
<!ENTITY runtimeMenu_showSettings_accesskey "s">

<!ENTITY viewMenu_label "View">
<!ENTITY viewMenu_accesskey "V">
<!ENTITY viewMenu_zoomin_label "Zoom In">
<!ENTITY viewMenu_zoomin_accesskey "I">
<!ENTITY viewMenu_zoomout_label "Zoom Out">
<!ENTITY viewMenu_zoomout_accesskey "O">
<!ENTITY viewMenu_resetzoom_label "Reset Zoom">
<!ENTITY viewMenu_resetzoom_accesskey "R">

<!ENTITY projectButton_label "Open App">
<!ENTITY runtimeButton_label "Select Runtime">

<!-- We try to repicate Firefox' bindings: -->
<!-- quit app -->
<!ENTITY key_quit "W">
<!-- open menu -->
<!ENTITY key_showProjectPanel "O">
<!-- reload app -->
<!ENTITY key_play "R">
<!-- show toolbox -->
<!ENTITY key_toggleToolbox "VK_F12">
<!-- zoom -->
<!ENTITY key_zoomin "+">
<!ENTITY key_zoomin2 "=">
<!ENTITY key_zoomout "-">
<!ENTITY key_resetzoom "0">

<!ENTITY projectPanel_myProjects "My Projects">
<!ENTITY projectPanel_runtimeApps "Runtime Apps">
<!ENTITY projectPanel_tabs "Tabs">
<!ENTITY runtimePanel_usb "USB Devices">
<!ENTITY runtimePanel_wifi "Wi-Fi Devices">
<!ENTITY runtimePanel_other "Other">
<!ENTITY runtimePanel_noadbhelper "Install ADB Helper">
<!ENTITY runtimePanel_nousbdevice "Can’t see your device?">
<!ENTITY runtimePanel_refreshDevices_label "Refresh Devices">

<!-- Lense -->
<!ENTITY details_valid_header "valid">
<!ENTITY details_warning_header "warnings">
<!ENTITY details_error_header "errors">
<!ENTITY details_description "Description">
<!ENTITY details_location "Location">
<!ENTITY details_manifestURL "App ID">
<!ENTITY details_removeProject_button "Remove Project">

<!-- New App -->
<!ENTITY newAppWindowTitle "New App">
<!ENTITY newAppHeader "Select template">
<!ENTITY newAppLoadingTemplate "Loading templates…">
<!ENTITY newAppProjectName "Project Name:">


<!-- Decks -->

<!ENTITY deck_close "Close">

<!-- Addons -->
<!ENTITY addons_title "Extra Components">
<!ENTITY addons_aboutaddons "Open Add-ons Manager">

<!-- Prefs -->
<!ENTITY prefs_title "Preferences">
<!ENTITY prefs_editor_title "Editor">
<!ENTITY prefs_general_title "General">
<!ENTITY prefs_restore "Restore Defaults">
<!ENTITY prefs_manage_components "Manage Extra Components">
<!ENTITY prefs_options_autoconnectruntime "Reconnect to previous runtime">
<!ENTITY prefs_options_autoconnectruntime_tooltip "Reconnect to previous runtime when WebIDE starts">
<!ENTITY prefs_options_rememberlastproject "Remember last project">
<!ENTITY prefs_options_rememberlastproject_tooltip "Restore previous project when WebIDE starts">
<!ENTITY prefs_options_templatesurl "Templates URL">
<!ENTITY prefs_options_templatesurl_tooltip "Index of available templates">

<!-- Runtime Details -->
<!ENTITY runtimedetails_title "Runtime Info">
<!ENTITY runtimedetails_adbIsRoot "ADB is root: ">
<!ENTITY runtimedetails_summonADBRoot "root device">
<!ENTITY runtimedetails_ADBRootWarning "(requires unlocked bootloader)">
<!ENTITY runtimedetails_unrestrictedPrivileges "Unrestricted DevTools privileges: ">
<!ENTITY runtimedetails_requestPrivileges "request higher privileges">
<!ENTITY runtimedetails_privilegesWarning "(Will reboot device. Requires root access.)">

<!-- Device Preferences and Settings -->
<!ENTITY device_typeboolean "Boolean">
<!ENTITY device_typenumber "Integer">
<!ENTITY device_typestring "String">
<!ENTITY device_typeobject "Object">
<!ENTITY device_typenone "Select a type">

<!-- Device Preferences -->
<!ENTITY devicepreference_title "Device Preferences">
<!ENTITY devicepreference_search "Search preferences">
<!ENTITY devicepreference_newname "New preference name">
<!ENTITY devicepreference_newtext "Preference value">
<!ENTITY devicepreference_addnew "Add new preference">

<!-- Device Settings -->
<!ENTITY devicesetting_title "Device Settings">
<!ENTITY devicesetting_search "Search settings">
<!ENTITY devicesetting_newname "New setting name">
<!ENTITY devicesetting_newtext "Setting value">
<!ENTITY devicesetting_addnew "Add new setting">

<!-- WiFi Authentication -->
<!-- LOCALIZATION NOTE (wifi_auth_header): The header displayed on the dialog
     that instructs the user to transfer an authentication token to the
     server. -->
<!ENTITY wifi_auth_header "Client Identification">
<!-- LOCALIZATION NOTE (wifi_auth_scan_request): Instructions requesting the
     user to transfer authentication info by scanning a QR code. -->
<!ENTITY wifi_auth_scan_request "The endpoint you are connecting to needs more information to authenticate this connection.  Please scan the QR code below via the prompt on your other device.">
<!-- LOCALIZATION NOTE (wifi_auth_no_scanner): Link text to assist users with
     devices that can't scan a QR code. -->
<!ENTITY wifi_auth_no_scanner "No QR scanner prompt?">
<!-- LOCALIZATION NOTE (wifi_auth_yes_scanner): Link text to assist users with
     devices that can scan a QR code. -->
<!ENTITY wifi_auth_yes_scanner "Have a QR scanner prompt?">
<!-- LOCALIZATION NOTE (wifi_auth_token_request): Instructions requesting the
     user to transfer authentication info by transferring a token. -->
<!ENTITY wifi_auth_token_request "If your other device asks for a token instead of scanning a QR code, please copy the value below to the other device:">
<!ENTITY wifi_auth_qr_size_note "If the QR code appears too small for the connection to be successfully established, try zooming or enlarging the window.">

<!-- Logs panel -->
<!ENTITY logs_title "Pre-packaging Command Logs">
PK
!<x�0LL;chrome/en-US/locale/en-US/devtools/client/webide.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

title_noApp=Firefox WebIDE
title_app=Firefox WebIDE: %S

runtimeButton_label=Select Runtime
projectButton_label=Open App

mainProcess_label=Main Process

local_runtime=Local Runtime
remote_runtime=Remote Runtime
remote_runtime_promptTitle=Remote Runtime
remote_runtime_promptMessage=hostname:port

importPackagedApp_title=Select Directory
importHostedApp_title=Open Hosted App
importHostedApp_header=Enter Manifest URL

selectCustomBinary_title=Select custom B2G binary
selectCustomProfile_title=Select custom Gaia profile

notification_showTroubleShooting_label=Troubleshooting
notification_showTroubleShooting_accesskey=T

# LOCALIZATION NOTE (project_tab_loading): This is shown as a temporary tab
# title for browser tab projects when the tab is still loading.
project_tab_loading=Loading…

# These messages appear in a notification box when an error occur.

error_cantInstallNotFullyConnected=Can’t install project. Not fully connected.
error_cantInstallValidationErrors=Can’t install project. Validation errors.
error_listRunningApps=Can’t get app list from device

# Variable: name of the operation (in english)
error_operationTimeout=Operation timed out: %1$S
error_operationFail=Operation failed: %1$S

# Variable: app name
error_cantConnectToApp=Can’t connect to app: %1$S

error_appProjectsLoadFailed=Unable to load project list. This can occur if you’ve used this profile with a newer version of Firefox.
error_folderCreationFailed=Unable to create project folder in the selected directory.

# Variable: runtime app build ID (looks like this %Y%M%D format) and firefox build ID (same format)
error_runtimeVersionTooRecent=The connected runtime has a more recent build date (%1$S) than your desktop Firefox (%2$S) does. This is an unsupported setup and may cause DevTools to fail. Please update Firefox.

# Variable: runtime app version (looks like this 52.a3) and firefox version (same format)
error_runtimeVersionTooOld=The connected runtime has an old version (%1$S). The minimum supported version is (%2$S). This is an unsupported setup and may cause DevTools to fail. Please update the connected runtime.

addons_stable=stable
addons_unstable=unstable
addons_install_button=install
addons_uninstall_button=uninstall
addons_adb_label=ADB Helper Add-on
addons_adb_warning=USB devices won’t be detected without this add-on
addons_status_unknown=?
addons_status_installed=Installed
addons_status_uninstalled=Not Installed
addons_status_preparing=preparing
addons_status_downloading=downloading
addons_status_installing=installing

runtimedetails_checkno=no
runtimedetails_checkyes=yes
runtimedetails_checkunknown=unknown (requires ADB Helper 0.4.0 or later)
runtimedetails_notUSBDevice=Not a USB device

# Validation status
status_tooltip=Validation status: %1$S
status_valid=VALID
status_warning=WARNINGS
status_error=ERRORS
status_unknown=UNKNOWN

# Device preferences and settings
device_reset_default=Reset to default
PK
!<a��X
X
9chrome/en-US/locale/en-US/devtools/shared/csscoverage.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!-- LOCALIZATION NOTE : FILE This file contains the CSS Coverage Report
   - strings. See the 'csscoverage' command for more information, and
   - devtools/client/styleeditor/styleeditor.xul for context -->

<!-- LOCALIZATION NOTE : FILE The correct localization of this file might be to
   - keep it in English, or another language commonly spoken among web developers.
   - You want to make that choice consistent across the developer tools.
   - A good criteria is the language in which you'd find the best
   - documentation on web development on the web. -->

<!-- LOCALIZATION NOTE (csscoverage.backButton):
  -  Text on the button to go back to the main style editor -->
<!ENTITY csscoverage.backButton "Back">

<!-- LOCALIZATION NOTE (csscoverage.unused, csscoverage.noMatches):
  -  This is the heading and body text for the CSS usage part of the report -->
<!ENTITY csscoverage.unused "Unused Rules">
<!ENTITY csscoverage.noMatches "No matches found for the following rules:">

<!-- LOCALIZATION NOTE (csscoverage.optimize.header):
  -  This is the heading for the CSS optimization part of the report -->
<!ENTITY csscoverage.optimize.header "Optimizable Pages">

<!-- LOCALIZATION NOTE (csscoverage.preload1, csscoverage.preload2,
  -  csscoverage.preload3): These 3 are part of a paragraph with 1 and 2
  -  separated by a styled <link> tag and 2 and 3 separated by a styled
  -  <style> tag -->
<!ENTITY csscoverage.optimize.body1 "You can sometimes speed up loading by moving">
<!ENTITY csscoverage.optimize.body2 "tags to the bottom of the page and creating a new inline">
<!ENTITY csscoverage.optimize.body3 "element with the styles needed before the ‘load’ event to the top. Here are the style blocks you need:">

<!-- LOCALIZATION NOTE (csscoverage.optimize.bodyX):
  -  This is what we say when we have no optimization suggestions -->
<!ENTITY csscoverage.optimize.bodyX "All rules are inlined.">

<!-- LOCALIZATION NOTE (csscoverage.footer1, csscoverage.footer2a,
  -  csscoverage.footer3, csscoverage.footer4): The text displayed at the
  -  bottom of the page, with 2a being the URL opened when the link text in 3
  -  is clicked -->
<!ENTITY csscoverage.footer1 "See">
<!ENTITY csscoverage.footer2a "https://developer.mozilla.org/docs/Tools/CSS_Coverage">
<!ENTITY csscoverage.footer3 "the MDN article on the CSS Coverage Tool">
<!ENTITY csscoverage.footer4 "for caveats in the generation of this report.">
PK
!<�g��@chrome/en-US/locale/en-US/devtools/shared/csscoverage.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE These strings are used in the 'csscoverage' command and in
# the user interface that this command creates.

# LOCALIZATION NOTE (csscoverageDesc, csscoverageStartDesc2,
# csscoverageStopDesc2, csscoverageOneShotDesc2, csscoverageToggleDesc2,
# csscoverageReportDesc2): Short descriptions of the csscoverage commands
csscoverageDesc=Control CSS coverage analysis
csscoverageStartDesc2=Begin collecting CSS coverage data
csscoverageStopDesc2=Stop collecting CSS coverage data
csscoverageOneShotDesc2=Collect instantaneous CSS coverage data
csscoverageToggleDesc2=Toggle collecting CSS coverage data
csscoverageReportDesc2=Show CSS coverage report
csscoverageStartNoReloadDesc=Don’t start with a page reload
csscoverageStartNoReloadManual=It’s best if we start by reloading the current page because that starts the test at a known point, but there could be reasons why we don’t want to do that (e.g. the page contains state that will be lost across a reload)

# LOCALIZATION NOTE (csscoverageRunningReply, csscoverageDoneReply): Text that
# describes the current state of the css coverage system
csscoverageRunningReply=Running CSS coverage analysis
csscoverageDoneReply=CSS Coverage analysis completed

# LOCALIZATION NOTE (csscoverageRunningError, csscoverageNotRunningError,
# csscoverageNotRunError): Error message that describe things that can go wrong
# with the css coverage system
csscoverageRunningError=CSS coverage analysis already running
csscoverageNotRunningError=CSS coverage analysis not running
csscoverageNotRunError=CSS coverage analysis has not been run
csscoverageNoRemoteError=Target does not support CSS Coverage
csscoverageOneShotReportError=CSS coverage report is not available for ‘oneshot’ data. Please use start/stop.
PK
!<�:�
�
=chrome/en-US/locale/en-US/devtools/shared/debugger.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE These strings are used inside the Debugger
# which is available from the Web Developer sub-menu -> 'Debugger'.
# The correct localization of this file might be to keep it in
# English, or another language commonly spoken among web developers.
# You want to make that choice consistent across the developer tools.
# A good criteria is the language in which you'd find the best
# documentation on web development on the web.

# LOCALIZATION NOTE (remoteIncomingPromptTitle): The title displayed on the
# dialog that prompts the user to allow the incoming connection.
remoteIncomingPromptTitle=Incoming Connection

# LOCALIZATION NOTE (remoteIncomingPromptHeader): Header displayed on the
# dialog that prompts the user to allow the incoming connection.
remoteIncomingPromptHeader=An incoming request to permit remote debugging connection was detected. A remote client can take complete control over your browser!
# LOCALIZATION NOTE (remoteIncomingPromptClientEndpoint): Part of the prompt
# dialog for the user to choose whether an incoming connection should be
# allowed.
# %1$S: The host and port of the client such as "127.0.0.1:6000"
remoteIncomingPromptClientEndpoint=Client Endpoint: %1$S
# LOCALIZATION NOTE (remoteIncomingPromptServerEndpoint): Part of the prompt
# dialog for the user to choose whether an incoming connection should be
# allowed.
# %1$S: The host and port of the server such as "127.0.0.1:6000"
remoteIncomingPromptServerEndpoint=Server Endpoint: %1$S
# LOCALIZATION NOTE (remoteIncomingPromptFooter): Footer displayed on the
# dialog that prompts the user to allow the incoming connection.
remoteIncomingPromptFooter=Allow connection?

# LOCALIZATION NOTE (remoteIncomingPromptDisable): The label displayed on the
# third button in the incoming connection dialog that lets the user disable the
# remote debugger server.
remoteIncomingPromptDisable=Disable

# LOCALIZATION NOTE (clientSendOOBTitle): The title displayed on the dialog that
# instructs the user to transfer an authentication token to the server.
clientSendOOBTitle=Client Identification
# LOCALIZATION NOTE (clientSendOOBHeader): Header displayed on the dialog that
# instructs the user to transfer an authentication token to the server.
clientSendOOBHeader=The endpoint you are connecting to needs more information to authenticate this connection.  Please provide the token below in the prompt that appears on the other end.
# LOCALIZATION NOTE (clientSendOOBHash): Part of the dialog that instructs the
# user to transfer an authentication token to the server.
# %1$S: The client's cert fingerprint
clientSendOOBHash=My Cert: %1$S
# LOCALIZATION NOTE (clientSendOOBToken): Part of the dialog that instructs the
# user to transfer an authentication token to the server.
# %1$S: The authentication token that the user will transfer.
clientSendOOBToken=Token: %1$S

# LOCALIZATION NOTE (serverReceiveOOBTitle): The title displayed on the dialog
# that instructs the user to provide an authentication token from the client.
serverReceiveOOBTitle=Provide Client Token
# LOCALIZATION NOTE (serverReceiveOOBBody): Main text displayed on the dialog
# that instructs the user to provide an authentication token from the client.
serverReceiveOOBBody=The client should be displaying a token value.  Enter that token value here to complete authentication with this client.
PK
!<i�P?chrome/en-US/locale/en-US/devtools/shared/eyedropper.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE These strings are used in the Eyedropper color tool.
# LOCALIZATION NOTE The correct localization of this file might be to keep it
# in English, or another language commonly spoken among web developers.
# You want to make that choice consistent across the developer tools.
# A good criteria is the language in which you'd find the best documentation
# on web development on the web.

# LOCALIZATION NOTE  (colorValue.copied): This text is displayed when the user selects a
# color with the eyedropper and it's copied to the clipboard.
colorValue.copied=copied
PK
!<xi/�9�99chrome/en-US/locale/en-US/devtools/shared/gcli.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE These strings are used inside the Web Console
# command line which is available from the Web Developer sub-menu
# -> 'Web Console'.
# The correct localization of this file might be to keep it in
# English, or another language commonly spoken among web developers.
# You want to make that choice consistent across the developer tools.
# A good criteria is the language in which you'd find the best
# documentation on web development on the web.

# For each command there are in general two strings. As an example consider
# the 'pref' command.
# commandDesc (e.g. prefDesc for the command 'pref'): this string contains a
# very short description of the command. It's designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
# commandManual (e.g. prefManual for the command 'pref'): this string will
# contain a fuller description of the command. It's diplayed when the user
# asks for help about a specific command (e.g. 'help pref').

# LOCALIZATION NOTE: This message is used to describe any command or command
# parameter when no description has been provided.
canonDescNone=(No description)

# LOCALIZATION NOTE: The default name for a group of parameters.
canonDefaultGroupName=Options

# LOCALIZATION NOTE (canonProxyDesc, canonProxyManual): These commands are
# used to execute commands on a remote system (using a proxy). Parameters: %S
# is the name of the remote system.
canonProxyDesc=Execute a command on %S
canonProxyManual=A set of commands that are executed on a remote system. The remote system is reached via %S

# LOCALIZATION NOTE: This error message is displayed when we try to add a new
# command (using a proxy) where one already exists with the same name.
canonProxyExists=There is already a command called ‘%S’

# LOCALIZATION NOTE: This message describes the '{' command, which allows
# entry of JavaScript like traditional developer tool command lines.
cliEvalJavascript=Enter JavaScript directly

# LOCALIZATION NOTE: This message is displayed when the command line has more
# arguments than the current command can understand.
cliUnusedArg=Too many arguments

# LOCALIZATION NOTE: The title of the dialog which displays the options that
# are available to the current command.
cliOptions=Available Options

# LOCALIZATION NOTE: The error message when the user types a command that
# isn't registered
cliUnknownCommand2=Invalid Command: ‘%1$S’.

# LOCALIZATION NOTE: A parameter should have a value, but doesn't
cliIncompleteParam=Value required for ‘%1$S’.

# LOCALIZATION NOTE: Error message given when a file argument points to a file
# that does not exist, but should (e.g. for use with File->Open) %1$S is a
# filename
fileErrNotExists=‘%1$S’ doesn’t exist

# LOCALIZATION NOTE: Error message given when a file argument points to a file
# that exists, but should not (e.g. for use with File->Save As) %1$S is a
# filename
fileErrExists=‘%1$S’ already exists

# LOCALIZATION NOTE: Error message given when a file argument points to a
# non-file, when a file is needed. %1$S is a filename
fileErrIsNotFile=‘%1$S’ is not a file

# LOCALIZATION NOTE: Error message given when a file argument points to a
# non-directory, when a directory is needed (e.g. for use with 'cd') %1$S is a
# filename
fileErrIsNotDirectory=‘%1$S’ is not a directory

# LOCALIZATION NOTE: Error message given when a file argument does not match
# the specified regular expression %1$S is a filename %2$S is a regular
# expression
fileErrDoesntMatch=‘%1$S’ does not match ‘%2$S’

# LOCALIZATION NOTE: When the menu has displayed all the matches that it
# should (i.e. about 10 items) then we display this to alert the user that
# more matches are available.
fieldMenuMore=More matches, keep typing

# LOCALIZATION NOTE: The command line provides completion for JavaScript
# commands, however there are times when the scope of what we're completing
# against can't be used. This error message is displayed when this happens.
jstypeParseScope=Scope lost

# LOCALIZATION NOTE (jstypeParseMissing, jstypeBeginSyntax,
# jstypeBeginUnterm): These error messages are displayed when the command line
# is doing JavaScript completion and encounters errors.
jstypeParseMissing=Can’t find property ‘%S’
jstypeBeginSyntax=Syntax error
jstypeBeginUnterm=Unterminated string literal

# LOCALIZATION NOTE: This message is displayed if the system for providing
# JavaScript completions encounters and error it displays this.
jstypeParseError=Error

# LOCALIZATION NOTE (typesNumberNan, typesNumberNotInt2, typesDateNan): These
# error messages are displayed when the command line is passed a variable
# which has the wrong format and can't be converted. Parameters: %S is the
# passed variable.
typesNumberNan=Can’t convert “%S” to a number.
typesNumberNotInt2=Can’t convert “%S” to an integer.
typesDateNan=Can’t convert “%S” to a date.

# LOCALIZATION NOTE (typesNumberMax, typesNumberMin, typesDateMax,
# typesDateMin): These error messages are displayed when the command line is
# passed a variable which has a value out of range (number or date).
# Parameters: %1$S is the passed variable, %2$S is the limit value.
typesNumberMax=%1$S is greater than maximum allowed: %2$S.
typesNumberMin=%1$S is smaller than minimum allowed: %2$S.
typesDateMax=%1$S is later than maximum allowed: %2$S.
typesDateMin=%1$S is earlier than minimum allowed: %2$S.

# LOCALIZATION NOTE: This error message is displayed when the command line is
# passed an option with a limited number of correct values, but the passed
# value is not one of them.
typesSelectionNomatch=Can’t use ‘%S’.

# LOCALIZATION NOTE: This error message is displayed when the command line is
# expecting a CSS query string, however the passed string is not valid.
nodeParseSyntax=Syntax error in CSS query

# LOCALIZATION NOTE (nodeParseMultiple, nodeParseNone): These error messages
# are displayed when the command line is expecting a CSS string that matches a
# single node, but more nodes (or none) match.
nodeParseMultiple=Too many matches (%S)
nodeParseNone=No matches

# LOCALIZATION NOTE (helpDesc, helpManual, helpSearchDesc, helpSearchManual3):
# These strings describe the "help" command, used to display a description of
# a command (e.g. "help pref"), and its parameter 'search'.
helpDesc=Get help on the available commands
helpManual=Provide help either on a specific command (if a search string is provided and an exact match is found) or on the available commands (if a search string is not provided, or if no exact match is found).
helpSearchDesc=Search string
helpSearchManual3=search string to use in narrowing down the displayed commands. Regular expressions not supported.

# LOCALIZATION NOTE: These strings are displayed in the help page for a
# command in the console.
helpManSynopsis=Synopsis

# LOCALIZATION NOTE: This message is displayed in the help page if the command
# has no parameters.
helpManNone=None

# LOCALIZATION NOTE: This message is displayed in response to the 'help'
# command when used without a filter, just above the list of known commands.
helpListAll=Available Commands:

# LOCALIZATION NOTE (helpListPrefix, helpListNone): These messages are
# displayed in response to the 'help <search>' command (i.e. with a search
# string), just above the list of matching commands. Parameters: %S is the
# search string.
helpListPrefix=Commands starting with ‘%S’:
helpListNone=No commands starting with ‘%S’

# LOCALIZATION NOTE (helpManRequired, helpManOptional, helpManDefault): When
# the 'help x' command wants to show the manual for the 'x' command, it needs
# to be able to describe the parameters as either required or optional, or if
# they have a default value.
helpManRequired=required
helpManOptional=optional
helpManDefault=optional, default=%S

# LOCALIZATION NOTE: This forms part of the output from the 'help' command.
# 'GCLI' is a project name and should be left untranslated.
helpIntro=GCLI is an experiment to create a highly usable command line for web developers.

# LOCALIZATION NOTE: Text shown as part of the output of the 'help' command
# when the command in question has sub-commands, before a list of the matching
# sub-commands.
subCommands=Sub-Commands

# LOCALIZATION NOTE: This error message is displayed when the command line is
# cannot find a match for the parse types.
commandParseError=Command line parsing error

# LOCALIZATION NOTE (contextDesc, contextManual, contextPrefixDesc): These
# strings are used to describe the 'context' command and its 'prefix'
# parameter. See localization comment for 'connect' for an explanation about
# 'prefix'.
contextDesc=Concentrate on a group of commands
contextManual=Setup a default prefix to future commands. For example ‘context git’ would allow you to type ‘commit’ rather than ‘git commit’.
contextPrefixDesc=The command prefix

# LOCALIZATION NOTE: This message message displayed during the processing of
# the 'context' command, when the found command is not a parent command.
contextNotParentError=Can’t use ‘%S’ as a prefix because it is not a parent command.

# LOCALIZATION NOTE (contextReply, contextEmptyReply): These messages are
# displayed during the processing of the 'context' command, to indicate
# success or that there is no command prefix.
contextReply=Using %S as a command prefix
contextEmptyReply=Command prefix is unset

# LOCALIZATION NOTE (connectDesc, connectManual, connectPrefixDesc,
# connectMethodDesc, connectUrlDesc, connectDupReply): These strings describe
# the 'connect' command and all its available parameters. A 'prefix' is an
# alias for the remote server (think of it as a "connection name"), and it
# allows to identify a specific server when connected to multiple remote
# servers.
connectDesc=Proxy commands to server
connectManual=Connect to the server, creating local versions of the commands on the server. Remote commands initially have a prefix to distinguish them from local commands (but see the context command to get past this)
connectPrefixDesc=Parent prefix for imported commands
connectMethodDesc=The method of connecting
connectUrlDesc=The URL to connect to
connectDupReply=Connection called %S already exists.

# LOCALIZATION NOTE: The output of the 'connect' command, telling the user
# what it has done. Parameters: %S is the prefix command. See localization
# comment for 'connect' for an explanation about 'prefix'.
connectReply=Added %S commands.

# LOCALIZATION NOTE (disconnectDesc2, disconnectManual2,
# disconnectPrefixDesc): These strings describe the 'disconnect' command and
# all its available parameters. See localization comment for 'connect' for an
# explanation about 'prefix'.
disconnectDesc2=Disconnect from server
disconnectManual2=Disconnect from a server currently connected for remote commands execution
disconnectPrefixDesc=Parent prefix for imported commands

# LOCALIZATION NOTE: This is the output of the 'disconnect' command,
# explaining the user what has been done. Parameters: %S is the number of
# commands removed.
disconnectReply=Removed %S commands.

# LOCALIZATION NOTE: These strings describe the 'clear' command
clearDesc=Clear the output area

# LOCALIZATION NOTE (prefDesc, prefManual, prefListDesc, prefListManual,
# prefListSearchDesc, prefListSearchManual, prefShowDesc, prefShowManual,
# prefShowSettingDesc, prefShowSettingManual): These strings describe the
# 'pref' command and all its available sub-commands and parameters.
prefDesc=Commands to control settings
prefManual=Commands to display and alter preferences both for GCLI and the surrounding environment
prefListDesc=Display available settings
prefListManual=Display a list of preferences, optionally filtered when using the ‘search’ parameter
prefListSearchDesc=Filter the list of settings displayed
prefListSearchManual=Search for the given string in the list of available preferences
prefShowDesc=Display setting value
prefShowManual=Display the value of a given preference
prefShowSettingDesc=Setting to display
prefShowSettingManual=The name of the setting to display

# LOCALIZATION NOTE: This message is used to show the preference name and the
# associated preference value. Parameters: %1$S is the preference name, %2$S
# is the preference value.
prefShowSettingValue=%1$S: %2$S

# LOCALIZATION NOTE (prefSetDesc, prefSetManual, prefSetSettingDesc,
# prefSetSettingManual, prefSetValueDesc, prefSetValueManual): These strings
# describe the 'pref set' command and all its parameters.
prefSetDesc=Alter a setting
prefSetManual=Alter preferences defined by the environment
prefSetSettingDesc=Setting to alter
prefSetSettingManual=The name of the setting to alter.
prefSetValueDesc=New value for setting
prefSetValueManual=The new value for the specified setting

# LOCALIZATION NOTE (prefResetDesc, prefResetManual, prefResetSettingDesc,
# prefResetSettingManual): These strings describe the 'pref reset' command and
# all its parameters.
prefResetDesc=Reset a setting
prefResetManual=Reset the value of a setting to the system defaults
prefResetSettingDesc=Setting to reset
prefResetSettingManual=The name of the setting to reset to the system default value

# LOCALIZATION NOTE: This string is displayed in the output from the 'pref
# list' command as a label to an input element that allows the user to filter
# the results.
prefOutputFilter=Filter

# LOCALIZATION NOTE (prefOutputName, prefOutputValue): These strings are
# displayed in the output from the 'pref list' command as table headings.
prefOutputName=Name
prefOutputValue=Value

# LOCALIZATION NOTE (introTextOpening3, introTextCommands, introTextKeys2,
# introTextF1Escape, introTextGo): These strings are displayed when the user
# first opens the developer toolbar to explain the command line, and is shown
# each time it is opened until the user clicks the 'Got it!' button.
introTextOpening3=GCLI is an experiment to create a highly usable command line for web developers.
introTextCommands=For a list of commands type
introTextKeys2=, or to show/hide command hints press
introTextF1Escape=F1/Escape
introTextGo=Got it!

# LOCALIZATION NOTE: This is a short description of the 'hideIntro' setting.
hideIntroDesc=Show the initial welcome message

# LOCALIZATION NOTE: This is a description of the 'eagerHelper' setting. It's
# displayed when the user asks for help on the settings. eagerHelper allows
# users to select between showing no tooltips, permanent tooltips, and only
# important tooltips.
eagerHelperDesc=How eager are the tooltips
PK
!<�j�5-5-Achrome/en-US/locale/en-US/devtools/shared/gclicommands.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE These strings are used inside Web Console commands.
# The Web Console command line is available from the Web Developer sub-menu
# -> 'Web Console'.
#
# The correct localization of this file might be to keep it in
# English, or another language commonly spoken among web developers.
# You want to make that choice consistent across the developer tools.
# A good criteria is the language in which you'd find the best
# documentation on web development on the web.

# LOCALIZATION NOTE (helpDesc) A very short string used to describe the
# function of the help command.
helpDesc=Get help on the available commands

# LOCALIZATION NOTE (helpAvailable) Used in the output of the help command to
# explain the contents of the command help table.
helpAvailable=Available Commands

# LOCALIZATION NOTE (notAvailableInE10S) Used in the output of any command that
# is not compatible with multiprocess mode (E10S).
notAvailableInE10S=The command ‘%1$S’ is not available in multiprocess mode (E10S)

# LOCALIZATION NOTE (consoleDesc) A very short string used to describe the
# function of the console command.
consoleDesc=Commands to control the console

# LOCALIZATION NOTE (consoleManual) A longer description describing the
# set of commands that control the console.
consoleManual=Filter, clear and close the web console

# LOCALIZATION NOTE (consoleclearDesc) A very short string used to describe the
# function of the 'console clear' command.
consoleclearDesc=Clear the console

# LOCALIZATION NOTE (screenshotDesc) A very short description of the
# 'screenshot' command. See screenshotManual for a fuller description of what
# it does. This string is designed to be shown in a menu alongside the
# command name, which is why it should be as short as possible.
screenshotDesc=Save an image of the page

# LOCALIZATION NOTE (screenshotManual) A fuller description of the 'screenshot'
# command, displayed when the user asks for help on what it does.
screenshotManual=Save a PNG image of the entire visible window (optionally after a delay)

# LOCALIZATION NOTE (screenshotFilenameDesc) A very short string to describe
# the 'filename' parameter to the 'screenshot' command, which is displayed in
# a dialog when the user is using this command.
screenshotFilenameDesc=Destination filename

# LOCALIZATION NOTE (screenshotFilenameManual) A fuller description of the
# 'filename' parameter to the 'screenshot' command, displayed when the user
# asks for help on what it does.
screenshotFilenameManual=The name of the file (should have a ‘.png’ extension) to which we write the screenshot.

# LOCALIZATION NOTE (screenshotClipboardDesc) A very short string to describe
# the 'clipboard' parameter to the 'screenshot' command, which is displayed in
# a dialog when the user is using this command.
screenshotClipboardDesc=Copy screenshot to clipboard? (true/false)

# LOCALIZATION NOTE (screenshotClipboardManual) A fuller description of the
# 'clipboard' parameter to the 'screenshot' command, displayed when the user
# asks for help on what it does.
screenshotClipboardManual=True if you want to copy the screenshot instead of saving it to a file.

# LOCALIZATION NOTE (screenshotGroupOptions) A label for the optional options of
# the screenshot command.
screenshotGroupOptions=Options

# LOCALIZATION NOTE (screenshotDelayDesc) A very short string to describe
# the 'delay' parameter to the 'screenshot' command, which is displayed in
# a dialog when the user is using this command.
screenshotDelayDesc=Delay (seconds)

# LOCALIZATION NOTE (screenshotDelayManual) A fuller description of the
# 'delay' parameter to the 'screenshot' command, displayed when the user
# asks for help on what it does.
screenshotDelayManual=The time to wait (in seconds) before the screenshot is taken

# LOCALIZATION NOTE (screenshotDPRDesc) A very short string to describe
# the 'dpr' parameter to the 'screenshot' command, which is displayed in
# a dialog when the user is using this command.
screenshotDPRDesc=Device pixel ratio

# LOCALIZATION NOTE (screenshotDPRManual) A fuller description of the
# 'dpr' parameter to the 'screenshot' command, displayed when the user
# asks for help on what it does.
screenshotDPRManual=The device pixel ratio to use when taking the screenshot

# LOCALIZATION NOTE (screenshotFullPageDesc) A very short string to describe
# the 'fullpage' parameter to the 'screenshot' command, which is displayed in
# a dialog when the user is using this command.
screenshotFullPageDesc=Entire webpage? (true/false)

# LOCALIZATION NOTE (screenshotFullPageManual) A fuller description of the
# 'fullpage' parameter to the 'screenshot' command, displayed when the user
# asks for help on what it does.
screenshotFullPageManual=True if the screenshot should also include parts of the webpage which are outside the current scrolled bounds.

# LOCALIZATION NOTE (screenshotFileDesc) A very short string to describe
# the 'file' parameter to the 'screenshot' command, which is displayed in
# a dialog when the user is using this command.
screenshotFileDesc=Save to file? (true/false)

# LOCALIZATION NOTE (screenshotFileManual) A fuller description of the
# 'file' parameter to the 'screenshot' command, displayed when the user
# asks for help on what it does.
screenshotFileManual=True if the screenshot should save the file even when other options are enabled (eg. clipboard).

# LOCALIZATION NOTE (screenshotGeneratedFilename) The auto generated filename
# when no file name is provided. The first argument (%1$S) is the date string
# in yyyy-mm-dd format and the second argument (%2$S) is the time string
# in HH.MM.SS format. Please don't add the extension here.
screenshotGeneratedFilename=Screen Shot %1$S at %2$S

# LOCALIZATION NOTE (screenshotErrorSavingToFile) Text displayed to user upon
# encountering error while saving the screenshot to the file specified.
screenshotErrorSavingToFile=Error saving to

# LOCALIZATION NOTE (screenshotSavedToFile) Text displayed to user when the
# screenshot is successfully saved to the file specified.
screenshotSavedToFile=Saved to

# LOCALIZATION NOTE (screenshotErrorCopying) Text displayed to user upon
# encountering error while copying the screenshot to clipboard.
screenshotErrorCopying=Error occurred while copying to clipboard.

# LOCALIZATION NOTE (screenshotCopied) Text displayed to user when the
# screenshot is successfully copied to the clipboard.
screenshotCopied=Copied to clipboard.

# LOCALIZATION NOTE (screenshotTooltipPage) Text displayed as tooltip for screenshot button in devtools ToolBox.
screenshotTooltipPage=Take a screenshot of the entire page

# LOCALIZATION NOTE (screenshotImgurDesc) A very short string to describe
# the 'imgur' parameter to the 'screenshot' command, which is displayed in
# a dialog when the user is using this command.
screenshotImgurDesc=Upload to imgur.com

# LOCALIZATION NOTE (screenshotImgurManual) A fuller description of the
# 'imgur' parameter to the 'screenshot' command, displayed when the user
# asks for help on what it does.
screenshotImgurManual=Use if you want to upload to imgur.com instead of saving to disk

# LOCALIZATION NOTE (screenshotImgurError) Text displayed to user upon
# encountering error while uploading the screenshot to imgur.com.
screenshotImgurError=Could not reach imgur API

# LOCALIZATION NOTE (screenshotImgurUploaded) Text displayed to user when the
# screenshot is successfully sent to Imgur but the program is waiting on a response.
# The argument (%1$S) is a new image URL at Imgur.
screenshotImgurUploaded=Uploaded to %1$S

# LOCALIZATION NOTE (highlightDesc) A very short description of the
# 'highlight' command. See highlightManual for a fuller description of what
# it does. This string is designed to be shown in a menu alongside the
# command name, which is why it should be as short as possible.
highlightDesc=Highlight nodes

# LOCALIZATION NOTE (highlightManual) A fuller description of the 'highlight'
# command, displayed when the user asks for help on what it does.
highlightManual=Highlight nodes that match a selector on the page

# LOCALIZATION NOTE (highlightSelectorDesc) A very short string to describe
# the 'selector' parameter to the 'highlight' command, which is displayed in
# a dialog when the user is using this command.
highlightSelectorDesc=CSS selector

# LOCALIZATION NOTE (highlightSelectorManual) A fuller description of the
# 'selector' parameter to the 'highlight' command, displayed when the user
# asks for help on what it does.
highlightSelectorManual=The CSS selector used to match nodes in the page

# LOCALIZATION NOTE (highlightOptionsDesc) The title of a set of options to
# the 'highlight' command, displayed as a heading to the list of option.
highlightOptionsDesc=Options

# LOCALIZATION NOTE (highlightHideGuidesDesc) A very short string to describe
# the 'hideguides' option parameter to the 'highlight' command, which is
# displayed in a dialog when the user is using this command.
highlightHideGuidesDesc=Hide guides

# LOCALIZATION NOTE (highlightHideGuidesManual) A fuller description of the
# 'hideguides' option parameter to the 'highlight' command, displayed when the
# user asks for help on what it does.
highlightHideGuidesManual=Hide the guides around the highlighted node

# LOCALIZATION NOTE (highlightShowInfoBarDesc) A very short string to describe
# the 'showinfobar' option parameter to the 'highlight' command, which is
# displayed in a dialog when the user is using this command.
highlightShowInfoBarDesc=Show the node infobar

# LOCALIZATION NOTE (highlightShowInfoBarManual) A fuller description of the
# 'showinfobar' option parameter to the 'highlight' command, displayed when the
# user asks for help on what it does.
highlightShowInfoBarManual=Show the infobar above the highlighted node (the infobar displays the tagname, attributes and dimension)

# LOCALIZATION NOTE (highlightShowAllDesc) A very short string to describe
# the 'showall' option parameter to the 'highlight' command, which is
# displayed in a dialog when the user is using this command.
highlightShowAllDesc=Show all matches

# LOCALIZATION NOTE (highlightShowAllManual) A fuller description of the
# 'showall' option parameter to the 'highlight' command, displayed when the
# user asks for help on what it does.
highlightShowAllManual=If too many nodes match the selector, only the first 100 will be shown to avoid slowing down the page too much. Use this option to show all matches instead

# LOCALIZATION NOTE (highlightRegionDesc) A very short string to describe the
# 'region' option parameter to the 'highlight' command, which is displayed in a
# dialog when the user is using this command.
highlightRegionDesc=Box model region

# LOCALIZATION NOTE (highlightRegionManual) A fuller description of the 'region'
# option parameter to the 'highlight' command, displayed when the user asks for
# help on what it does.
highlightRegionManual=Which box model region should be highlighted: ‘content’, ‘padding’, ‘border’ or ‘margin’

# LOCALIZATION NOTE (highlightFillDesc) A very short string to describe the
# 'fill' option parameter to the 'highlight' command, which is displayed in a
# dialog when the user is using this command.
highlightFillDesc=Fill style

# LOCALIZATION NOTE (highlightFillManual) A fuller description of the 'fill'
# option parameter to the 'highlight' command, displayed when the user asks for
# help on what it does.
highlightFillManual=Override the default region fill style with a custom color

# LOCALIZATION NOTE (highlightKeepDesc) A very short string to describe the
# 'keep' option parameter to the 'highlight' command, which is displayed in a
# dialog when the user is using this command.
highlightKeepDesc=Keep existing highlighters

# LOCALIZATION NOTE (highlightKeepManual) A fuller description of the 'keep'
# option parameter to the 'highlight' command, displayed when the user asks for
# help on what it does.
highlightKeepManual=By default, existing highlighters are hidden when running the command, unless this option is set

# LOCALIZATION NOTE (highlightOutputConfirm2) A confirmation message for the
# 'highlight' command, displayed to the user once the command has been entered,
# informing the user how many nodes have been highlighted successfully and how
# to turn highlighting off
highlightOutputConfirm2=%1$S node highlighted;%1$S nodes highlighted

# LOCALIZATION NOTE (highlightOutputMaxReached) A confirmation message for the
# 'highlight' command, displayed to the user once the command has been entered,
# informing the user how many nodes have been highlighted successfully and that
# some nodes could not be highlighted due to the maximum number of nodes being
# reached, and how to turn highlighting off
highlightOutputMaxReached=%1$S nodes matched, but only %2$S nodes highlighted. Use ‘--showall’ to show all

# LOCALIZATION NOTE (unhighlightDesc) A very short description of the
# 'unhighlight' command. See unhighlightManual for a fuller description of what
# it does. This string is designed to be shown in a menu alongside the
# command name, which is why it should be as short as possible.
unhighlightDesc=Unhighlight all nodes

# LOCALIZATION NOTE (unhighlightManual) A fuller description of the 'unhighlight'
# command, displayed when the user asks for help on what it does.
unhighlightManual=Unhighlight all nodes previously highlighted with the ‘highlight’ command

# LOCALIZATION NOTE (restartBrowserDesc) A very short description of the
# 'restart' command. This string is designed to be shown in a menu alongside the
# command name, which is why it should be as short as possible.
# The argument (%1$S) is the browser name.
restartBrowserDesc=Restart %1$S

# LOCALIZATION NOTE (restartBrowserNocacheDesc) A very short string to
# describe the 'nocache' parameter to the 'restart' command, which is
# displayed in a dialog when the user is using this command.
restartBrowserNocacheDesc=Disables loading content from cache upon restart

# LOCALIZATION NOTE (restartBrowserRequestCancelled) A string displayed to the
# user when a scheduled restart has been aborted by the user.
restartBrowserRequestCancelled=Restart request cancelled by user.

# LOCALIZATION NOTE (restartBrowserRestarting) A string displayed to the
# user when a restart has been initiated without a delay.
# The argument (%1$S) is the browser name.
restartBrowserRestarting=Restarting %1$S…

# LOCALIZATION NOTE (restartBrowserGroupOptions) A label for the optional options of
# the restart command.
restartBrowserGroupOptions=Options

# LOCALIZATION NOTE (restartBrowserSafemodeDesc) A very short string to
# describe the 'safemode' parameter to the 'restart' command, which is
# displayed in a dialog when the user is using this command.
restartBrowserSafemodeDesc=Enables Safe Mode upon restart

# LOCALIZATION NOTE (inspectDesc) A very short description of the 'inspect'
# command. See inspectManual for a fuller description of what it does. This
# string is designed to be shown in a menu alongside the command name, which
# is why it should be as short as possible.
inspectDesc=Inspect a node

# LOCALIZATION NOTE (inspectManual) A fuller description of the 'inspect'
# command, displayed when the user asks for help on what it does.
inspectManual=Investigate the dimensions and properties of an element using a CSS selector to open the DOM highlighter

# LOCALIZATION NOTE (inspectNodeDesc) A very short string to describe the
# 'node' parameter to the 'inspect' command, which is displayed in a dialog
# when the user is using this command.
inspectNodeDesc=CSS selector

# LOCALIZATION NOTE (inspectNodeManual) A fuller description of the 'node'
# parameter to the 'inspect' command, displayed when the user asks for help
# on what it does.
inspectNodeManual=A CSS selector for use with document.querySelector which identifies a single element

# LOCALIZATION NOTE (eyedropperDesc) A very short description of the 'eyedropper'
# command. See eyedropperManual for a fuller description of what it does. This
# string is designed to be shown in a menu alongside the command name, which
# is why it should be as short as possible.
eyedropperDesc=Grab a color from the page

# LOCALIZATION NOTE (eyedropperManual) A fuller description of the 'eyedropper'
# command, displayed when the user asks for help on what it does.
eyedropperManual=Open a panel that magnifies an area of page to inspect pixels and copy color values

# LOCALIZATION NOTE (debuggerClosed) Used in the output of several commands
# to explain that the debugger must be opened first.
debuggerClosed=The debugger must be opened before using this command

# LOCALIZATION NOTE (debuggerStopped) Used in the output of several commands
# to explain that the debugger must be opened first before setting breakpoints.
debuggerStopped=The debugger must be opened before setting breakpoints

# LOCALIZATION NOTE (breakDesc) A very short string used to describe the
# function of the break command.
breakDesc=Manage breakpoints

# LOCALIZATION NOTE (breakManual) A longer description describing the
# set of commands that control breakpoints.
breakManual=Commands to list, add and remove breakpoints

# LOCALIZATION NOTE (breaklistDesc) A very short string used to describe the
# function of the 'break list' command.
breaklistDesc=Display known breakpoints

# LOCALIZATION NOTE (breaklistNone) Used in the output of the 'break list'
# command to explain that the list is empty.
breaklistNone=No breakpoints set

# LOCALIZATION NOTE (breaklistOutRemove) A title used in the output from the
# 'break list' command on a button which can be used to remove breakpoints
breaklistOutRemove=Remove

# LOCALIZATION NOTE (breakaddAdded) Used in the output of the 'break add'
# command to explain that a breakpoint was added.
breakaddAdded=Added breakpoint

# LOCALIZATION NOTE (breakaddFailed) Used in the output of the 'break add'
# command to explain that a breakpoint could not be added.
breakaddFailed=Could not set breakpoint: %S

# LOCALIZATION NOTE (breakaddDesc) A very short string used to describe the
# function of the 'break add' command.
breakaddDesc=Add a breakpoint

# LOCALIZATION NOTE (breakaddManual) A longer description describing the
# set of commands that are responsible for adding breakpoints.
breakaddManual=Breakpoint types supported: line

# LOCALIZATION NOTE (breakaddlineDesc) A very short string used to describe the
# function of the 'break add line' command.
breakaddlineDesc=Add a line breakpoint

# LOCALIZATION NOTE (breakaddlineFileDesc) A very short string used to describe
# the function of the file parameter in the 'break add line' command.
breakaddlineFileDesc=JS file URI

# LOCALIZATION NOTE (breakaddlineLineDesc) A very short string used to describe
# the function of the line parameter in the 'break add line' command.
breakaddlineLineDesc=Line number

# LOCALIZATION NOTE (breakdelDesc) A very short string used to describe the
# function of the 'break del' command.
breakdelDesc=Remove a breakpoint

# LOCALIZATION NOTE (breakdelBreakidDesc) A very short string used to describe
# the function of the index parameter in the 'break del' command.
breakdelBreakidDesc=Index of breakpoint

# LOCALIZATION NOTE (breakdelRemoved) Used in the output of the 'break del'
# command to explain that a breakpoint was removed.
breakdelRemoved=Breakpoint removed

# LOCALIZATION NOTE (dbgDesc) A very short string used to describe the
# function of the dbg command.
dbgDesc=Manage debugger

# LOCALIZATION NOTE (dbgManual) A longer description describing the
# set of commands that control the debugger.
dbgManual=Commands to interrupt or resume the main thread, step in, out and over lines of code

# LOCALIZATION NOTE (dbgOpen) A very short string used to describe the function
# of the dbg open command.
dbgOpen=Open the debugger

# LOCALIZATION NOTE (dbgClose) A very short string used to describe the function
# of the dbg close command.
dbgClose=Close the debugger

# LOCALIZATION NOTE (dbgInterrupt) A very short string used to describe the
# function of the dbg interrupt command.
dbgInterrupt=Pauses the main thread

# LOCALIZATION NOTE (dbgContinue) A very short string used to describe the
# function of the dbg continue command.
dbgContinue=Resumes the main thread, and continues execution following a breakpoint, until the next breakpoint or the termination of the script.

# LOCALIZATION NOTE (dbgStepDesc) A very short string used to describe the
# function of the dbg step command.
dbgStepDesc=Manage stepping

# LOCALIZATION NOTE (dbgStepManual) A longer description describing the
# set of commands that control stepping.
dbgStepManual=Commands to step in, out and over lines of code

# LOCALIZATION NOTE (dbgStepOverDesc) A very short string used to describe the
# function of the dbg step over command.
dbgStepOverDesc=Executes the current statement and then stops at the next statement. If the current statement is a function call then the debugger executes the whole function, and it stops at the next statement after the function call

# LOCALIZATION NOTE (dbgStepInDesc) A very short string used to describe the
# function of the dbg step in command.
dbgStepInDesc=Executes the current statement and then stops at the next statement. If the current statement is a function call, then the debugger steps into that function, otherwise it stops at the next statement

# LOCALIZATION NOTE (dbgStepOutDesc) A very short string used to describe the
# function of the dbg step out command.
dbgStepOutDesc=Steps out of the current function and up one level if the function is nested. If in the main body, the script is executed to the end, or to the next breakpoint. The skipped statements are executed, but not stepped through

# LOCALIZATION NOTE (dbgListSourcesDesc) A very short string used to describe the
# function of the dbg list command.
dbgListSourcesDesc=List the source URLs loaded in the debugger

# LOCALIZATION NOTE (dbgBlackBoxDesc) A very short string used to describe the
# function of the 'dbg blackbox' command.
dbgBlackBoxDesc=Black box sources in the debugger

# LOCALIZATION NOTE (dbgBlackBoxSourceDesc) A very short string used to describe the
# 'source' parameter to the 'dbg blackbox' command.
dbgBlackBoxSourceDesc=A specific source to black box

# LOCALIZATION NOTE (dbgBlackBoxGlobDesc) A very short string used to describe the
# 'glob' parameter to the 'dbg blackbox' command.
dbgBlackBoxGlobDesc=Black box all sources that match this glob (for example: “*.min.js”)

# LOCALIZATION NOTE (dbgBlackBoxInvertDesc) A very short string used to describe the
# 'invert' parameter to the 'dbg blackbox' command.
dbgBlackBoxInvertDesc=Invert matching, so that we black box every source that is not the source provided or does not match the provided glob pattern.

# LOCALIZATION NOTE (dbgBlackBoxEmptyDesc) A very short string used to let the
# user know that no sources were black boxed.
dbgBlackBoxEmptyDesc=(No sources black boxed)

# LOCALIZATION NOTE (dbgBlackBoxNonEmptyDesc) A very short string used to let the
# user know which sources were black boxed.
dbgBlackBoxNonEmptyDesc=The following sources were black boxed:

# LOCALIZATION NOTE (dbgBlackBoxErrorDesc) A very short string used to let the
# user know there was an error black boxing a source (whose url follows this
# text).
dbgBlackBoxErrorDesc=Error black boxing:

# LOCALIZATION NOTE (dbgUnBlackBoxDesc) A very short string used to describe the
# function of the 'dbg unblackbox' command.
dbgUnBlackBoxDesc=Stop black boxing sources in the debugger

# LOCALIZATION NOTE (dbgUnBlackBoxSourceDesc) A very short string used to describe the
# 'source' parameter to the 'dbg unblackbox' command.
dbgUnBlackBoxSourceDesc=A specific source to stop black boxing

# LOCALIZATION NOTE (dbgUnBlackBoxGlobDesc) A very short string used to describe the
# 'glob' parameter to the 'dbg blackbox' command.
dbgUnBlackBoxGlobDesc=Stop black boxing all sources that match this glob (for example: “*.min.js”)

# LOCALIZATION NOTE (dbgUnBlackBoxEmptyDesc) A very short string used to let the
# user know that we did not stop black boxing any sources.
dbgUnBlackBoxEmptyDesc=(Did not stop black boxing any sources)

# LOCALIZATION NOTE (dbgUnBlackBoxNonEmptyDesc) A very short string used to let the
# user know which sources we stopped black boxing.
dbgUnBlackBoxNonEmptyDesc=Stopped black boxing the following sources:

# LOCALIZATION NOTE (dbgUnBlackBoxErrorDesc) A very short string used to let the
# user know there was an error black boxing a source (whose url follows this
# text).
dbgUnBlackBoxErrorDesc=Error stopping black boxing:

# LOCALIZATION NOTE (dbgUnBlackBoxInvertDesc) A very short string used to describe the
# 'invert' parameter to the 'dbg unblackbox' command.
dbgUnBlackBoxInvertDesc=Invert matching, so that we stop black boxing every source that is not the source provided or does not match the provided glob pattern.

# LOCALIZATION NOTE (consolecloseDesc) A very short description of the
# 'console close' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
consolecloseDesc=Close the console

# LOCALIZATION NOTE (consoleopenDesc) A very short description of the
# 'console open' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
consoleopenDesc=Open the console

# LOCALIZATION NOTE (editDesc) A very short description of the 'edit'
# command. See editManual2 for a fuller description of what it does. This
# string is designed to be shown in a menu alongside the command name, which
# is why it should be as short as possible.
editDesc=Tweak a page resource

# LOCALIZATION NOTE (editManual2) A fuller description of the 'edit' command,
# displayed when the user asks for help on what it does.
editManual2=Edit one of the resources that is part of this page

# LOCALIZATION NOTE (editResourceDesc) A very short string to describe the
# 'resource' parameter to the 'edit' command, which is displayed in a dialog
# when the user is using this command.
editResourceDesc=URL to edit

# LOCALIZATION NOTE (editLineToJumpToDesc) A very short string to describe the
# 'line' parameter to the 'edit' command, which is displayed in a dialog
# when the user is using this command.
editLineToJumpToDesc=Line to jump to

# LOCALIZATION NOTE (resizePageDesc) A very short string to describe the
# 'resizepage' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
resizePageDesc=Resize the page

# LOCALIZATION NOTE (resizePageArgWidthDesc) A very short string to describe the
# 'width' parameter to the 'resizepage' command, which is displayed in a dialog
# when the user is using this command.
resizePageArgWidthDesc=Width in pixels

# LOCALIZATION NOTE (resizePageArgHeightDesc) A very short string to describe the
# 'height' parameter to the 'resizepage' command, which is displayed in a dialog
# when the user is using this command.
resizePageArgHeightDesc=Height in pixels

# LOCALIZATION NOTE (resizeModeOnDesc) A very short string to describe the
# 'resizeon ' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
resizeModeOnDesc=Enter Responsive Design Mode

# LOCALIZATION NOTE (resizeModeOffDesc) A very short string to describe the
# 'resize off' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
resizeModeOffDesc=Exit Responsive Design Mode

# LOCALIZATION NOTE (resizeModeToggleDesc) A very short string to describe the
# 'resize toggle' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
resizeModeToggleDesc=Toggle Responsive Design Mode

# LOCALIZATION NOTE (resizeModeToggleTooltip2) A string displayed as the
# tooltip of button in devtools toolbox which toggles Responsive Design Mode.
# Keyboard shortcut will be shown inside brackets.
resizeModeToggleTooltip2=Responsive Design Mode (%S)

# LOCALIZATION NOTE (resizeModeToDesc) A very short string to describe the
# 'resize to' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
resizeModeToDesc=Alter page size

# LOCALIZATION NOTE (resizeModeDesc) A very short string to describe the
# 'resize' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
resizeModeDesc=Control Responsive Design Mode

# LOCALIZATION NOTE (resizeModeManual2) A fuller description of the 'resize'
# command, displayed when the user asks for help on what it does.
# The argument (%1$S) is the browser name.
resizeModeManual2=Responsive websites respond to their environment, so they look good on a mobile display, a cinema display and everything in-between. Responsive Design Mode allows you to easily test a variety of page sizes in %1$S without needing to resize your whole browser.

# LOCALIZATION NOTE (cmdDesc) A very short description of the 'cmd'
# command. This string is designed to be shown in a menu alongside the command
# name, which is why it should be as short as possible.
cmdDesc=Manipulate the commands

# LOCALIZATION NOTE (cmdRefreshDesc) A very short description of the 'cmd refresh'
# command. This string is designed to be shown in a menu alongside the command
# name, which is why it should be as short as possible.
cmdRefreshDesc=Re-read mozcmd directory

# LOCALIZATION NOTE (cmdStatus3) When the we load new commands from mozcmd
# directory, we report where we loaded from using %1$S.
cmdStatus3=Loaded commands from ‘%1$S’

# LOCALIZATION NOTE (cmdSetdirDesc)  A very short description of the 'cmd setdir'
# command. This string is designed to be shown in a menu alongside the command
# name, which is why it should be as short as possible.
cmdSetdirDesc=Setup a mozcmd directory

# LOCALIZATION NOTE (cmdSetdirManual3) A fuller description of the 'cmd setdir'
# command, displayed when the user asks for help on what it does.
cmdSetdirManual3=A ‘mozcmd’ directory is an easy way to create new custom commands. For more information see https://developer.mozilla.org/docs/Tools/GCLI/Customization

# LOCALIZATION NOTE (cmdSetdirDirectoryDesc) The description of the directory
# parameter to the 'cmd setdir' command.
cmdSetdirDirectoryDesc=Directory containing .mozcmd files

# LOCALIZATION NOTE (addonDesc) A very short description of the 'addon'
# command. This string is designed to be shown in a menu alongside the command
# name, which is why it should be as short as possible.
addonDesc=Manipulate add-ons

# LOCALIZATION NOTE (addonListDesc) A very short description of the 'addon list'
# command. This string is designed to be shown in a menu alongside the command
# name, which is why it should be as short as possible.
addonListDesc=List installed add-ons

# LOCALIZATION NOTE (addonListTypeDesc) A very short description of the
# 'addon list <type>' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
addonListTypeDesc=Select an add-on type

# LOCALIZATION NOTE (addonListDictionaryHeading, addonListExtensionHeading,
# addonListLocaleHeading, addonListPluginHeading, addonListThemeHeading,
# addonListUnknownHeading) Used in the output of the 'addon list' command as the
# first line of output.
addonListDictionaryHeading=The following dictionaries are currently installed:
addonListExtensionHeading=The following extensions are currently installed:
addonListLocaleHeading=The following locales are currently installed:
addonListPluginHeading=The following plugins are currently installed:
addonListThemeHeading=The following themes are currently installed:
addonListAllHeading=The following add-ons are currently installed:
addonListUnknownHeading=The following add-ons of the selected type are currently installed:

# LOCALIZATION NOTE (addonListOutEnable, addonListOutDisable) Used in the
# output of the 'addon list' command as the labels for the enable/disable
# action buttons in the listing. This string is designed to be shown in a
# small action button next to the addon name, which is why it should be as
# short as possible.
addonListOutEnable=Enable
addonListOutDisable=Disable

# LOCALIZATION NOTE (addonPending, addonPendingEnable, addonPendingDisable,
# addonPendingUninstall, addonPendingInstall, addonPendingUpgrade) Used in
# the output of the 'addon list' command as the descriptions of pending
# addon operations. addonPending is used as a prefix for a list of pending
# actions (named by the other lookup variables). These strings are designed
# to be shown alongside addon names, which is why they should be as short
# as possible.
addonPending=pending
addonPendingEnable=enable
addonPendingDisable=disable
addonPendingUninstall=uninstall
addonPendingInstall=install
addonPendingUpgrade=upgrade

# LOCALIZATION NOTE (addonNameDesc) A very short description of the
# name parameter of numerous add-on commands. This string is designed to be shown
# in a menu alongside the command name, which is why it should be as short as
# possible.
addonNameDesc=The name of the add-on

# LOCALIZATION NOTE (addonNoneOfType) Used in the output of the 'addon list'
# command when a search for add-ons of a particular type were not found.
addonNoneOfType=There are no add-ons of that type installed.

# LOCALIZATION NOTE (addonEnableDesc) A very short description of the
# 'addon enable <type>' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
addonEnableDesc=Enable the specified add-on

# LOCALIZATION NOTE (addonAlreadyEnabled) Used in the output of the
# 'addon enable' command when an attempt is made to enable an add-on that is
# already enabled.
addonAlreadyEnabled=%S is already enabled.

# LOCALIZATION NOTE (addonEnabled) Used in the output of the 'addon enable'
# command when an add-on is enabled.
addonEnabled=%S enabled.

# LOCALIZATION NOTE (addonDisableDesc) A very short description of the
# 'addon disable <type>' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
addonDisableDesc=Disable the specified add-on

# LOCALIZATION NOTE (addonAlreadyDisabled) Used in the output of the
# 'addon disable' command when an attempt is made to disable an add-on that is
# already disabled.
addonAlreadyDisabled=%S is already disabled.

# LOCALIZATION NOTE (addonDisabled) Used in the output of the 'addon disable'
# command when an add-on is disabled.
addonDisabled=%S disabled.

# LOCALIZATION NOTE (addonCtpDesc) A very short description of the
# 'addon ctp <type>' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
addonCtpDesc=Set the specified plugin to click-to-play.

# LOCALIZATION NOTE (addonCtp) Used in the output of the 'addon ctp'
# command when a plugin is set to click-to-play.
addonCtp=%S set to click-to-play.

# LOCALIZATION NOTE (addonAlreadyCtp) Used in the output of the
# 'addon ctp' command when an attempt is made to set a plugin to
# click-to-play that is already set to click-to-play.
addonAlreadyCtp=%S is already set to click-to-play.

# LOCALIZATION NOTE (addonCantCtp) Used in the output of the 'addon
# ctp' command when an attempt is made to set an addon to click-to-play,
# but the addon is not a plugin.
addonCantCtp=%S cannot be set to click-to-play because it is not a plugin.

# LOCALIZATION NOTE (addonNoCtp) Used in the output of the 'addon
# ctp' command when an attempt is made to set an addon to click-to-play,
# but the plugin cannot be set to click-to-play for some reason.
addonNoCtp=%S cannot be set to click-to-play.

# LOCALIZATION NOTE (exportDesc) A very short description of the 'export'
# command. This string is designed to be shown in a menu alongside the command
# name, which is why it should be as short as possible.
exportDesc=Export resources

# LOCALIZATION NOTE (exportHtmlDesc) A very short description of the 'export
# html' command. This string is designed to be shown in a menu alongside the
# command name, which is why it should be as short as possible.
exportHtmlDesc=Export HTML from page

# LOCALIZATION NOTE (pagemodDesc) A very short description of the 'pagemod'
# command. This string is designed to be shown in a menu alongside the command
# name, which is why it should be as short as possible.
pagemodDesc=Make page changes

# LOCALIZATION NOTE (pagemodReplaceDesc) A very short description of the
# 'pagemod replace' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
pagemodReplaceDesc=Search and replace in page elements

# LOCALIZATION NOTE (pagemodReplaceSearchDesc) A very short string to describe
# the 'search' parameter to the 'pagemod replace' command, which is displayed in
# a dialog when the user is using this command.
pagemodReplaceSearchDesc=What to search for

# LOCALIZATION NOTE (pagemodReplaceReplaceDesc) A very short string to describe
# the 'replace' parameter to the 'pagemod replace' command, which is displayed in
# a dialog when the user is using this command.
pagemodReplaceReplaceDesc=Replacement string

# LOCALIZATION NOTE (pagemodReplaceIgnoreCaseDesc) A very short string to
# describe the 'ignoreCase' parameter to the 'pagemod replace' command, which is
# displayed in a dialog when the user is using this command.
pagemodReplaceIgnoreCaseDesc=Perform case-insensitive search

# LOCALIZATION NOTE (pagemodReplaceRootDesc) A very short string to describe the
# 'root' parameter to the 'pagemod replace' command, which is displayed in
# a dialog when the user is using this command.
pagemodReplaceRootDesc=CSS selector to root of search

# LOCALIZATION NOTE (pagemodReplaceSelectorDesc) A very short string to describe
# the 'selector' parameter to the 'pagemod replace' command, which is displayed
# in a dialog when the user is using this command.
pagemodReplaceSelectorDesc=CSS selector to match in search

# LOCALIZATION NOTE (pagemodReplaceAttributesDesc) A very short string to
# describe the 'attributes' parameter to the 'pagemod replace' command, which is
# displayed in a dialog when the user is using this command.
pagemodReplaceAttributesDesc=Attribute match regexp

# LOCALIZATION NOTE (pagemodReplaceAttrOnlyDesc) A very short string to describe
# the 'attrOnly' parameter to the 'pagemod replace' command, which is displayed
# in a dialog when the user is using this command.
pagemodReplaceAttrOnlyDesc=Restrict search to attributes

# LOCALIZATION NOTE (pagemodReplaceContentOnlyDesc) A very short string to
# describe the 'contentOnly' parameter to the 'pagemod replace' command, which
# is displayed in a dialog when the user is using this command.
pagemodReplaceContentOnlyDesc=Restrict search to text nodes

# LOCALIZATION NOTE (pagemodReplaceResult) A string displayed as
# the result of the 'pagemod replace' command.
pagemodReplaceResult=Elements matched by selector: %1$S. Replaces in text nodes: %2$S. Replaces in attributes: %3$S.

# LOCALIZATION NOTE (pagemodRemoveDesc) A very short description of the
# 'pagemod remove' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
pagemodRemoveDesc=Remove elements and attributes from page

# LOCALIZATION NOTE (pagemodRemoveElementDesc) A very short description of the
# 'pagemod remove element' command. This string is designed to be shown in
# a menu alongside the command name, which is why it should be as short as
# possible.
pagemodRemoveElementDesc=Remove elements from page

# LOCALIZATION NOTE (pagemodRemoveElementSearchDesc) A very short string to
# describe the 'search' parameter to the 'pagemod remove element' command, which
# is displayed in a dialog when the user is using this command.
pagemodRemoveElementSearchDesc=CSS selector specifying elements to remove

# LOCALIZATION NOTE (pagemodRemoveElementRootDesc) A very short string to
# describe the 'root' parameter to the 'pagemod remove element' command, which
# is displayed in a dialog when the user is using this command.
pagemodRemoveElementRootDesc=CSS selector specifying root of search

# LOCALIZATION NOTE (pagemodRemoveElementStripOnlyDesc) A very short string to
# describe the 'stripOnly' parameter to the 'pagemod remove element' command,
# which is displayed in a dialog when the user is using this command.
pagemodRemoveElementStripOnlyDesc=Remove element, but leave content

# LOCALIZATION NOTE (pagemodRemoveElementIfEmptyOnlyDesc) A very short string to
# describe the 'ifEmptyOnly' parameter to the 'pagemod remove element' command,
# which is displayed in a dialog when the user is using this command.
pagemodRemoveElementIfEmptyOnlyDesc=Remove only empty elements

# LOCALIZATION NOTE (pagemodRemoveElementResultMatchedAndRemovedElements)
# A string displayed as the result of the 'pagemod remove element' command.
pagemodRemoveElementResultMatchedAndRemovedElements=Elements matched by selector: %1$S. Elements removed: %2$S.

# LOCALIZATION NOTE (pagemodRemoveAttributeDesc) A very short description of the
# 'pagemod remove attribute' command. This string is designed to be shown in
# a menu alongside the command name, which is why it should be as short as
# possible.
pagemodRemoveAttributeDesc=Remove matching attributes

# LOCALIZATION NOTE (pagemodRemoveAttributeSearchAttributesDesc) A very short
# string to describe the 'searchAttributes' parameter to the 'pagemod remove
# attribute' command, which is displayed in a dialog when the user is using this
# command.
pagemodRemoveAttributeSearchAttributesDesc=Regexp specifying attributes to remove

# LOCALIZATION NOTE (pagemodRemoveAttributeSearchElementsDesc) A very short
# string to describe the 'searchElements' parameter to the 'pagemod remove
# attribute' command, which is displayed in a dialog when the user is using this
# command.
pagemodRemoveAttributeSearchElementsDesc=CSS selector of elements to include

# LOCALIZATION NOTE (pagemodRemoveAttributeRootDesc) A very short string to
# describe the 'root' parameter to the 'pagemod remove attribute' command, which
# is displayed in a dialog when the user is using this command.
pagemodRemoveAttributeRootDesc=CSS selector of root of search

# LOCALIZATION NOTE (pagemodRemoveAttributeIgnoreCaseDesc) A very short string
# to describe the 'ignoreCase' parameter to the 'pagemod remove attribute'
# command, which is displayed in a dialog when the user is using this command.
pagemodRemoveAttributeIgnoreCaseDesc=Perform case-insensitive search

# LOCALIZATION NOTE (pagemodRemoveAttributeResult) A string displayed as the
# result of the 'pagemod remove attribute' command.
pagemodRemoveAttributeResult=Elements matched by selector: %1$S. Attributes removed: %2$S.

# LOCALIZATION NOTE (toolsDesc2) A very short description of the 'tools'
# command, the parent command for tool-hacking commands.
# The argument (%1$S) is the browser name.
toolsDesc2=Hack the %1$S Developer Tools

# LOCALIZATION NOTE (toolsManual2) A fuller description of the 'tools'
# command. The argument (%1$S) is the browser name.
toolsManual2=Various commands related to hacking directly on the %1$S Developer Tools.

# LOCALIZATION NOTE (toolsSrcdirDesc) A very short description of the 'tools srcdir'
# command, for pointing your developer tools loader at a mozilla-central source tree.
toolsSrcdirDesc=Load tools from a mozilla-central checkout

# LOCALIZATION NOTE (toolsSrcdirNotFound2) Shown when the 'tools srcdir' command was handed
# an invalid srcdir.
toolsSrcdirNotFound2=%1$S does not exist or is not a mozilla-central checkout.

# LOCALIZATION NOTE (toolsSrcdirReloaded2) Displayed when tools have been reloaded by the
# 'tools srcdir' command.
toolsSrcdirReloaded2=Tools loaded from %1$S.

# LOCALIZATION NOTE (toolsSrcdirManual2) A full description of the 'tools srcdir'
# command. The argument (%1$S) is the browser name.
toolsSrcdirManual2=Load the %1$S Developer Tools from a complete mozilla-central checkout.

# LOCALIZATION NOTE (toolsSrcdirDir) The srcdir argument to the 'tools srcdir' command.
toolsSrcdirDir=A mozilla-central checkout

# LOCALIZATION NOTE (toolsBuiltinDesc) A short description of the 'tools builtin'
# command, which overrides a previous 'tools srcdir' command.
toolsBuiltinDesc=Use the builtin tools

# LOCALIZATION NOTE (toolsBuiltinManual) A fuller description of the 'tools builtin'
# command.
toolsBuiltinManual=Use the builtin tools, overriding any previous srcdir command.

# LOCALIZATION NOTE (toolsBuiltinReloaded) Displayed when tools are loaded with the
# 'tools builtin' command.
toolsBuiltinReloaded=Builtin tools loaded.

# LOCALIZATION NOTE (toolsReloadDesc) A short description of the 'tools reload' command.
# which will reload the tools from the current srcdir.
toolsReloadDesc=Reload the developer tools

# LOCALIZATION NOTE (toolsReloaded2) Displayed when tools are reloaded with the 'tools
# reload' command.
toolsReloaded2=Tools reloaded.

# LOCALIZATION NOTE (cookieDesc) A very short description of the 'cookie'
# command. See cookieManual for a fuller description of what it does. This
# string is designed to be shown in a menu alongside the command name, which
# is why it should be as short as possible.
cookieDesc=Display and alter cookies

# LOCALIZATION NOTE (cookieManual) A fuller description of the 'cookie'
# command, displayed when the user asks for help on what it does.
cookieManual=Commands to list, create, delete and alter cookies for the current domain.

# LOCALIZATION NOTE (cookieListDesc) A very short description of the
# 'cookie list' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
cookieListDesc=Display cookies

# LOCALIZATION NOTE (cookieListManual) A fuller description of the 'cookie list'
# command, displayed when the user asks for help on what it does.
cookieListManual=Display a list of the cookies relevant to the current page.

# LOCALIZATION NOTE (cookieListOutHost,cookieListOutPath,cookieListOutExpires,cookieListOutAttributes):
# The 'cookie list' command has a number of headings for cookie properties.
# Particular care should be taken in translating these strings as they have
# references to names in the cookies spec.
cookieListOutHost=Host:
cookieListOutPath=Path:
cookieListOutExpires=Expires:
cookieListOutAttributes=Attributes:

# LOCALIZATION NOTE (cookieListOutNone) The output of the 'cookie list' command
# uses this string when no cookie attributes (like httpOnly, secure, etc) apply
cookieListOutNone=None

# LOCALIZATION NOTE (cookieListOutSession) The output of the 'cookie list'
# command uses this string to describe a cookie with an expiry value of '0'
# that is to say it is a session cookie
cookieListOutSession=At browser exit (session)

# LOCALIZATION NOTE (cookieListOutNonePage) The output of the 'cookie list'
# command uses this string for pages like 'about:blank' which can't contain
# cookies
cookieListOutNonePage=No cookies found for this page

# LOCALIZATION NOTE (cookieListOutNoneHost) The output of the 'cookie list'
# command uses this string when there are no cookies on a given web page
cookieListOutNoneHost=No cookies found for host %1$S

# LOCALIZATION NOTE (cookieListOutEdit) A title used in the output from the
# 'cookie list' command on a button which can be used to edit cookie values
cookieListOutEdit=Edit

# LOCALIZATION NOTE (cookieListOutRemove) A title used in the output from the
# 'cookie list' command on a button which can be used to remove cookies
cookieListOutRemove=Remove

# LOCALIZATION NOTE (cookieRemoveDesc) A very short description of the
# 'cookie remove' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
cookieRemoveDesc=Remove a cookie

# LOCALIZATION NOTE (cookieRemoveManual) A fuller description of the 'cookie remove'
# command, displayed when the user asks for help on what it does.
cookieRemoveManual=Remove a cookie, given its key

# LOCALIZATION NOTE (cookieRemoveKeyDesc) A very short string to describe the
# 'key' parameter to the 'cookie remove' command, which is displayed in a dialog
# when the user is using this command.
cookieRemoveKeyDesc=The key of the cookie to remove

# LOCALIZATION NOTE (cookieSetDesc) A very short description of the
# 'cookie set' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
cookieSetDesc=Set a cookie

# LOCALIZATION NOTE (cookieSetManual) A fuller description of the 'cookie set'
# command, displayed when the user asks for help on what it does.
cookieSetManual=Set a cookie by specifying a key name, its value and optionally one or more of the following attributes: expires (max-age in seconds or the expires date in GMTString format), path, domain, secure

# LOCALIZATION NOTE (cookieSetKeyDesc) A very short string to describe the
# 'key' parameter to the 'cookie set' command, which is displayed in a dialog
# when the user is using this command.
cookieSetKeyDesc=The key of the cookie to set

# LOCALIZATION NOTE (cookieSetValueDesc) A very short string to describe the
# 'value' parameter to the 'cookie set' command, which is displayed in a dialog
# when the user is using this command.
cookieSetValueDesc=The value of the cookie to set

# LOCALIZATION NOTE (cookieSetOptionsDesc) The title of a set of options to
# the 'cookie set' command, displayed as a heading to the list of option.
cookieSetOptionsDesc=Options

# LOCALIZATION NOTE (cookieSetPathDesc) A very short string to describe the
# 'path' parameter to the 'cookie set' command, which is displayed in a dialog
# when the user is using this command.
cookieSetPathDesc=The path of the cookie to set

# LOCALIZATION NOTE (cookieSetDomainDesc) A very short string to describe the
# 'domain' parameter to the 'cookie set' command, which is displayed in a dialog
# when the user is using this command.
cookieSetDomainDesc=The domain of the cookie to set

# LOCALIZATION NOTE (cookieSetSecureDesc) A very short string to describe the
# 'secure' parameter to the 'cookie set' command, which is displayed in a dialog
# when the user is using this command.
cookieSetSecureDesc=Only transmitted over https

# LOCALIZATION NOTE (cookieSetHttpOnlyDesc) A very short string to describe the
# 'httpOnly' parameter to the 'cookie set' command, which is displayed in a dialog
# when the user is using this command.
cookieSetHttpOnlyDesc=Not accessible from client side script

# LOCALIZATION NOTE (cookieSetSessionDesc) A very short string to describe the
# 'session' parameter to the 'cookie set' command, which is displayed in a dialog
# when the user is using this command.
cookieSetSessionDesc=Only valid for the lifetime of the browser session

# LOCALIZATION NOTE (cookieSetExpiresDesc) A very short string to describe the
# 'expires' parameter to the 'cookie set' command, which is displayed in a dialog
# when the user is using this command.
cookieSetExpiresDesc=The expiry date of the cookie (quoted RFC2822 or ISO 8601 date)

# LOCALIZATION NOTE (jsbDesc) A very short description of the
# 'jsb' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
jsbDesc=JavaScript beautifier

# LOCALIZATION NOTE (jsbUrlDesc) A very short description of the
# 'jsb <url>' parameter. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
jsbUrlDesc=The URL of the JS file to beautify

# LOCALIZATION NOTE (jsbIndentSizeDesc) A very short description of the
# 'jsb <indentSize>' parameter. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
jsbIndentSizeDesc=Indentation size in chars

# LOCALIZATION NOTE (jsbIndentSizeManual) A fuller description of the
# 'jsb <indentChar>' parameter, displayed when the user asks for help on what it
# does.
jsbIndentSizeManual=The number of chars with which to indent each line

# LOCALIZATION NOTE (jsbIndentCharDesc) A very short description of the
# 'jsb <indentChar>' parameter. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
jsbIndentCharDesc=The chars used to indent each line

# LOCALIZATION NOTE (jsbIndentCharManual) A fuller description of the
# 'jsb <indentChar>' parameter, displayed when the user asks for help on what it
# does.
jsbIndentCharManual=The chars used to indent each line. The possible choices are space or tab.

# the 'jsb <doNotPreserveNewlines>' parameter. This string is designed to be
# shown in a menu alongside the command name, which is why it should be as short
# as possible.
jsbDoNotPreserveNewlinesDesc=Do not preserve line breaks

# LOCALIZATION NOTE (jsbPreserveNewlinesManual) A fuller description of the
# 'jsb <jsbPreserveNewlines>' parameter, displayed when the user asks for help
# on what it does.
jsbPreserveNewlinesManual=Should existing line breaks be preserved

# LOCALIZATION NOTE (jsbPreserveMaxNewlinesDesc) A very short description of the
# 'jsb <preserveMaxNewlines>' parameter. This string is designed to be shown
# in a menu alongside the command name, which is why it should be as short as
# possible.
jsbPreserveMaxNewlinesDesc=Max consecutive line breaks

# LOCALIZATION NOTE (jsbPreserveMaxNewlinesManual) A fuller description of the
# 'jsb <preserveMaxNewlines>' parameter, displayed when the user asks for help
# on what it does.
jsbPreserveMaxNewlinesManual=The maximum number of consecutive line breaks to preserve

# LOCALIZATION NOTE (jsbJslintHappyDesc) A very short description of the
# 'jsb <jslintHappy>' parameter. This string is designed to be shown
# in a menu alongside the command name, which is why it should be as short as
# possible.
jsbJslintHappyDesc=Enforce jslint-stricter mode?

# LOCALIZATION NOTE (jsbJslintHappyManual) A fuller description of the
# 'jsb <jslintHappy>' parameter, displayed when the user asks for help
# on what it does.
jsbJslintHappyManual=When set to true, jslint-stricter mode is enforced

# LOCALIZATION NOTE (jsbBraceStyleDesc2) A very short description of the
# 'jsb <braceStyle>' parameter. This string is designed to be shown
# in a menu alongside the command name, which is why it should be as short as
# possible.
jsbBraceStyleDesc2=Select the coding style of braces

# LOCALIZATION NOTE (jsbBraceStyleManual2) A fuller description of the
# 'jsb <braceStyle>' parameter, displayed when the user asks for help
# on what it does.
#
# NOTES: The keywords collapse, expand, end-expand and expand-strict should not
# be translated. "even if it will break your code" means that the resulting code
# may no longer be functional.
jsbBraceStyleManual2=Select the coding style of braces: collapse - put braces on the same line as control statements; expand - put braces on own line (Allman / ANSI style); end-expand - put end braces on own line; expand-strict - put braces on own line even if it will break your code.

# LOCALIZATION NOTE (jsbNoSpaceBeforeConditionalDesc) A very short description
# of the 'jsb <noSpaceBeforeConditional>' parameter. This string is designed to
# be shown in a menu alongside the command name, which is why it should be as
# short as possible.
jsbNoSpaceBeforeConditionalDesc=No space before conditional statements

# LOCALIZATION NOTE (jsbUnescapeStringsDesc) A very short description of the
# 'jsb <unescapeStrings>' parameter. This string is designed to be shown
# in a menu alongside the command name, which is why it should be as short as
# possible.
jsbUnescapeStringsDesc=Unescape \\xNN characters?

# LOCALIZATION NOTE (jsbUnescapeStringsManual) A fuller description of the
# 'jsb <unescapeStrings>' parameter, displayed when the user asks for help
# on what it does.
jsbUnescapeStringsManual=Should printable characters in strings encoded in \\xNN notation be unescaped?

# LOCALIZATION NOTE (jsbInvalidURL) Displayed when an invalid URL is passed to
# the jsb command.
jsbInvalidURL=Please enter a valid URL

# LOCALIZATION NOTE (jsbOptionsDesc) The title of a set of options to
# the 'jsb' command, displayed as a heading to the list of options.
jsbOptionsDesc=Options

# LOCALIZATION NOTE (calllogDesc) A very short description of the
# 'calllog' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
calllogDesc=Commands to manipulate function call logging

# LOCALIZATION NOTE (calllogStartDesc) A very short description of the
# 'calllog start' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
calllogStartDesc=Start logging function calls to the console

# LOCALIZATION NOTE (calllogStartReply) A string displayed as the result of
# the 'calllog start' command.
calllogStartReply=Call logging started.

# LOCALIZATION NOTE (calllogStopDesc) A very short description of the
# 'calllog stop' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
calllogStopDesc=Stop function call logging

# LOCALIZATION NOTE (calllogStopNoLogging) A string displayed as the result of
# the 'calllog stop' command when there is nothing to stop.
calllogStopNoLogging=No call logging is currently active

# LOCALIZATION NOTE (calllogStopReply) A string displayed as the result of
# the 'calllog stop' command when there are logging actions to stop.
calllogStopReply=Stopped call logging. Active contexts: %1$S.

# LOCALIZATION NOTE (calllogChromeStartDesc) A very short description of the
# 'calllog chromestart' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
calllogChromeStartDesc=Start logging function calls for chrome code to the console

# LOCALIZATION NOTE (calllogChromeSourceTypeDesc) A very short description of the
# 'calllog chromestart <sourceType>' parameter. This string is designed to be
# shown in a menu alongside the command name, which is why it should be as short as possible.
calllogChromeSourceTypeDesc=Global object, JSM URI, or JS to get a global object from

# LOCALIZATION NOTE (calllogChromeSourceTypeManual) A very short description of the
# 'calllog chromestart' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
calllogChromeSourceTypeManual=The global object, URI of a JSM, or JS to execute in the chrome window from which to obtain a global object

# LOCALIZATION NOTE (calllogChromeStartReply) A string displayed as the result
# of the 'calllog chromestart' command.
calllogChromeStartReply=Call logging started.

# LOCALIZATION NOTE (calllogChromeStopDesc) A very short description of the
# 'calllog chromestop' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
calllogChromeStopDesc=Stop function call logging

# LOCALIZATION NOTE (calllogChromeStopNoLogging) A string displayed as the
# result of the 'calllog chromestop' command when there is nothing to stop.
calllogChromeStopNoLogging=No call logging for chrome code is currently active

# LOCALIZATION NOTE (calllogChromeStopReply) A string displayed as the result of
# the 'calllog chromestop' command when there are logging actions to stop.
calllogChromeStopReply=Stopped call logging. Active contexts: %1$S.

# LOCALIZATION NOTE (callLogChromeAnonFunction) A string displayed as the result
# of the 'calllog chromestart' command when an anonymouse function is to be
# logged.
callLogChromeAnonFunction=<anonymous>

# LOCALIZATION NOTE (callLogChromeMethodCall) A string displayed as the result
# of the 'calllog chromestart' command to proceed a method name when it is to be
# logged.
callLogChromeMethodCall=Method call

# LOCALIZATION NOTE (callLogChromeInvalidJSM) A string displayed as the result
# of the 'calllog chromestart' command with an invalid JSM or JSM path.
callLogChromeInvalidJSM=Invalid JSM!

# LOCALIZATION NOTE (callLogChromeVarNotFoundContent) A string displayed as the
# result of the 'calllog chromestart' command with a source type of
# content-variable and an invalid variable name.
callLogChromeVarNotFoundContent=Variable not found in content window.

# LOCALIZATION NOTE (callLogChromeVarNotFoundChrome) A string displayed as the
# result of the 'calllog chromestart' command with a source type of
# chrome-variable and an invalid variable name.
callLogChromeVarNotFoundChrome=Variable not found in chrome window.

# LOCALIZATION NOTE (callLogChromeEvalException) A string displayed as the
# result of the 'calllog chromestart' command with a source type of JavaScript
# and invalid JavaScript code.
callLogChromeEvalException=Evaluated JavaScript threw the following exception

# LOCALIZATION NOTE (callLogChromeEvalNeedsObject) A string displayed as the
# result of passing a non-JavaScript object creating source via the
# 'calllog chromestart javascript' command.
callLogChromeEvalNeedsObject=The JavaScript source must evaluate to an object whose method calls are to be logged e.g. “({a1: function() {this.a2()},a2: function() {}});”

# LOCALIZATION NOTE (scratchpadOpenTooltip) A string displayed as the
# tooltip of button in devtools toolbox which opens Scratchpad.
scratchpadOpenTooltip=Scratchpad

# LOCALIZATION NOTE (paintflashingDesc) A very short string used to describe the
# function of the "paintflashing" command
paintflashingDesc=Highlight painted area

# LOCALIZATION NOTE (paintflashingOnDesc) A very short string used to describe the
# function of the "paintflashing on" command.
paintflashingOnDesc=Turn on paint flashing

# LOCALIZATION NOTE (paintflashingOffDesc) A very short string used to describe the
# function of the "paintflashing off" command.
paintflashingOffDesc=Turn off paint flashing

# LOCALIZATION NOTE (paintflashingChromeDesc) A very short string used to describe the
# function of the "paintflashing on/off chrome" command.
paintflashingChromeDesc=chrome frames

# LOCALIZATION NOTE (paintflashingManual) A longer description describing the
# set of commands that control paint flashing.
paintflashingManual=Draw repainted areas in different colors

# LOCALIZATION NOTE (paintflashingTooltip) A string displayed as the
# tooltip of button in devtools toolbox which toggles paint flashing.
paintflashingTooltip=Highlight painted area

# LOCALIZATION NOTE (paintflashingToggleDesc) A very short string used to describe the
# function of the "paintflashing toggle" command.
paintflashingToggleDesc=Toggle paint flashing

# LOCALIZATION NOTE (splitconsoleTooltip2) A string displayed as the
# tooltip of button in devtools toolbox which toggles the split webconsole.
# Keyboard shortcut will be shown inside brackets.
splitconsoleTooltip2=Toggle split console (%S)

# LOCALIZATION NOTE (appCacheDesc) A very short string used to describe the
# function of the "appcache" command
appCacheDesc=Application cache utilities

# LOCALIZATION NOTE (appCacheValidateDesc) A very short string used to describe
# the function of the "appcache validate" command.
appCacheValidateDesc=Validate cache manifest

# LOCALIZATION NOTE (appCacheValidateManual) A fuller description of the
# 'validate' parameter to the 'appcache' command, displayed when the user asks
# for help on what it does.
appCacheValidateManual=Find issues relating to a cache manifest and the files that it references

# LOCALIZATION NOTE (appCacheValidateUriDesc) A very short string used to describe
# the function of the "uri" parameter of the appcache validate" command.
appCacheValidateUriDesc=URI to check

# LOCALIZATION NOTE (appCacheValidatedSuccessfully) Displayed by the "appcache validate"
# command when it has been successfully validated.
appCacheValidatedSuccessfully=Appcache validated successfully.

# LOCALIZATION NOTE (appCacheClearDesc) A very short string used to describe
# the function of the "appcache clear" command.
appCacheClearDesc=Clear entries from the application cache

# LOCALIZATION NOTE (appCacheClearManual) A fuller description of the
# 'appcache clear' command, displayed when the user asks for help on what it does.
appCacheClearManual=Clear one or more entries from the application cache

# LOCALIZATION NOTE (appCacheClearCleared) Displayed by the "appcache clear"
# command when entries are successfully cleared.
appCacheClearCleared=Entries cleared successfully.

# LOCALIZATION NOTE (appCacheListDesc) A very short string used to describe
# the function of the "appcache list" command.
appCacheListDesc=Display a list of application cache entries.

# LOCALIZATION NOTE (appCacheListManual) A fuller description of the
# 'appcache list' command, displayed when the user asks for help on what it does.
appCacheListManual=Display a list of all application cache entries. If the search parameter is used then the table displays the entries containing the search term.

# LOCALIZATION NOTE (appCacheListSearchDesc) A very short string used to describe
# the function of the "search" parameter of the appcache list" command.
appCacheListSearchDesc=Filter results using a search term.

# LOCALIZATION NOTE (AppCacheList*) Row headers for the 'appcache list' command.
appCacheListKey=Key:
appCacheListDataSize=Data size:
appCacheListDeviceID=Device ID:
appCacheListExpirationTime=Expires:
appCacheListFetchCount=Fetch count:
appCacheListLastFetched=Last fetched:
appCacheListLastModified=Last modified:

# LOCALIZATION NOTE (appCacheListViewEntry) The text for the view entry button
# of the 'appcache list' command.
appCacheListViewEntry=View Entry

# LOCALIZATION NOTE (appCacheViewEntryDesc) A very short string used to describe
# the function of the "appcache viewentry" command.
appCacheViewEntryDesc=Open a new tab containing the specified cache entry information.

# LOCALIZATION NOTE (appCacheViewEntryManual) A fuller description of the
# 'appcache viewentry' command, displayed when the user asks for help on what it
# does.
appCacheViewEntryManual=Open a new tab containing the specified cache entry information.

# LOCALIZATION NOTE (appCacheViewEntryKey) A very short string used to describe
# the function of the "key" parameter of the 'appcache viewentry' command.
appCacheViewEntryKey=The key for the entry to display.

# LOCALIZATION NOTE (profilerDesc) A very short string used to describe the
# function of the profiler command.
profilerDesc=Manage profiler

# LOCALIZATION NOTE (profilerManual) A longer description describing the
# set of commands that control the profiler.
profilerManual=Commands to start or stop a JavaScript profiler

# LOCALIZATION NOTE (profilerOpenDesc) A very short string used to describe the function
# of the profiler open command.
profilerOpenDesc=Open the profiler

# LOCALIZATION NOTE (profilerCloseDesc) A very short string used to describe the function
# of the profiler close command.
profilerCloseDesc=Close the profiler

# LOCALIZATION NOTE (profilerStartDesc) A very short string used to describe the function
# of the profiler start command.
profilerStartDesc=Start profiling

# LOCALIZATION NOTE (profilerStartManual) A fuller description of the 'profile name'
# parameter. This parameter is used to name a newly created profile or to lookup
# an existing profile by its name.
profilerStartManual=Name of a profile you wish to start.

# LOCALIZATION NOTE (profilerStopDesc) A very short string used to describe the function
# of the profiler stop command.
profilerStopDesc=Stop profiling

# LOCALIZATION NOTE (profilerStopManual) A fuller description of the 'profile name'
# parameter. This parameter is used to lookup an existing profile by its name.
profilerStopManual=Name of a profile you wish to stop.

# LOCALIZATION NOTE (profilerListDesc) A very short string used to describe the function
# of the profiler list command.
profilerListDesc=List all profiles

# LOCALIZATION NOTE (profilerShowDesc) A very short string used to describe the function
# of the profiler show command.
profilerShowDesc=Show individual profile

# LOCALIZATION NOTE (profilerShowManual) A fuller description of the 'profile name'
# parameter. This parameter is used to name a newly created profile or to lookup
# an existing profile by its name.
profilerShowManual=Name of a profile.

# LOCALIZATION NOTE (profilerAlreadyStarted2) A message that is displayed whenever
# an operation cannot be completed because the profile in question has already
# been started.
profilerAlreadyStarted2=Profile has already been started

# LOCALIZATION NOTE (profilerNotFound) A message that is displayed whenever
# an operation cannot be completed because the profile in question could not be
# found.
profilerNotFound=Profile not found

# LOCALIZATION NOTE (profilerNotStarted3) A message that is displayed whenever
# an operation cannot be completed because the profile in question has not been
# started yet. It also contains a hint to use the 'profile start' command to
# start the profiler.
profilerNotStarted3=Profiler has not been started yet. Use ‘profile start’ to start profiling

# LOCALIZATION NOTE (profilerStarted2) A very short string that indicates that
# we have started recording.
profilerStarted2=Recording…

# LOCALIZATION NOTE (profilerStopped) A very short string that indicates that
# we have stopped recording.
profilerStopped=Stopped…

# LOCALIZATION NOTE (profilerNotReady) A message that is displayed whenever
# an operation cannot be completed because the profiler has not been opened yet.
profilerNotReady=For this command to work you need to open the profiler first

# LOCALIZATION NOTE (listenDesc) A very short string used to describe the
# function of the 'listen' command.
listenDesc=Open a remote debug port

# LOCALIZATION NOTE (listenManual2) A longer description of the 'listen'
# command.
listenManual2=%1$S can allow remote debugging over a TCP/IP connection. For security reasons this is turned off by default, but can be enabled using this command.

# LOCALIZATION NOTE (listenPortDesc) A very short string used to describe the
# function of 'port' parameter to the 'listen' command.
listenPortDesc=The TCP port to listen on

# LOCALIZATION NOTE (listenProtocolDesc) A very short string used to describe the
# function of 'protocol' parameter to the 'listen' command.
listenProtocolDesc=The protocol to be used

# LOCALIZATION NOTE (listenDisabledOutput) Text of a message output during the
# execution of the 'listen' command.
listenDisabledOutput=Listen is disabled by the devtools.debugger.remote-enabled preference

# LOCALIZATION NOTE (listenInitOutput) Text of a message output during the
# execution of the 'listen' command. %1$S is a port number
listenInitOutput=Listening on port %1$S

# LOCALIZATION NOTE (listenNoInitOutput) Text of a message output during the
# execution of the 'listen' command.
listenNoInitOutput=DebuggerServer not initialized

# LOCALIZATION NOTE (unlistenDesc) A very short string used to describe the
# function of the 'unlisten' command.
unlistenDesc=Close all remote debug ports

# LOCALIZATION NOTE (unlistenManual) A longer description of the 'unlisten'
# command.
unlistenManual=Closes all the open ports for remote debugging.

# LOCALIZATION NOTE (unlistenOutput) Text of a message output during the
# execution of the 'unlisten' command.
unlistenOutput=All TCP ports closed

# LOCALIZATION NOTE (mediaDesc, mediaEmulateDesc, mediaEmulateManual,
# mediaEmulateType, mediaResetDesc, mediaResetManual) These strings describe
# the 'media' commands and all available parameters.
mediaDesc=CSS media type emulation
mediaEmulateDesc=Emulate a specified CSS media type
mediaEmulateManual=View the document as if rendered on a device supporting the given media type, with the relevant CSS rules applied.
mediaEmulateType=The media type to emulate
mediaResetDesc=Stop emulating a CSS media type

# LOCALIZATION NOTE (qsaDesc, qsaQueryDesc)
# These strings describe the 'qsa' commands and all available parameters.
qsaDesc=Perform querySelectorAll on the current document and return number of matches
qsaQueryDesc=CSS selectors separated by comma

# LOCALIZATION NOTE (injectDesc, injectManual, injectLibraryDesc, injectLoaded,
# injectFailed) These strings describe the 'inject' commands and all available
# parameters.
injectDesc=Inject common libraries into the page
injectManual2=Inject common libraries into the content of the page which can also be accessed from the console.
injectLibraryDesc=Select the library to inject or enter a valid script URI to inject
injectLoaded=%1$S loaded
injectFailed=Failed to load %1$S - Invalid URI

# LOCALIZATION NOTE (folderDesc, folderOpenDesc, folderOpenDir,
# folderOpenProfileDesc) These strings describe the 'folder' commands and
# all available parameters.
folderDesc=Open folders
folderOpenDesc=Open folder path
folderOpenDir=Directory Path
folderOpenProfileDesc=Open profile directory

# LOCALIZATION NOTE (folderInvalidPath) A string displayed as the result
# of the 'folder open' command with an invalid folder path.
folderInvalidPath=Please enter a valid path

# LOCALIZATION NOTE (folderOpenDirResult) A very short string used to
# describe the result of the 'folder open' command.
# The argument (%1$S) is the folder path.
folderOpenDirResult=Opened %1$S

# Security
securityPrivacyDesc=Display supported security and privacy features
securityManual=Commands to list and get suggestions about security features for the current domain.
securityListDesc=Display security features
securityListManual=Display a list of all relevant security features of the current page.
# CSP specific
securityCSPDesc=Display CSP specific security features
securityCSPManual=Display feedback about the CSP applied to the current page.
securityCSPRemWildCard=Can you remove the wildcard(*)?
securityCSPPotentialXSS=Potential XSS vulnerability!
# LOCALIZATION NOTE: do not translate 'Content-Security-Policy'
securityCSPNoCSPOnPage=Could not find Content-Security-Policy for
securityCSPHeaderOnPage=Content-Security-Policy for
securityCSPROHeaderOnPage=Content-Security-Policy-Report-Only for
# Referrer Policy specific
securityReferrerPolicyDesc=Display the current Referrer Policy
securityReferrerPolicyManual=Display the Referrer Policy for the current page with example referrers for different URIs.
securityReferrerNextURI=When Visiting
securityReferrerCalculatedReferrer=Referrer Will Be
# LOCALIZATION NOTE: %1$S is the current page URI
securityReferrerPolicyReportHeader=Referrer Policy for %1$S
securityReferrerPolicyOtherDomain=Other Origin
securityReferrerPolicyOtherDomainDowngrade=Other Origin HTTP
securityReferrerPolicySameDomain=Same Origin
securityReferrerPolicySameDomainDowngrade=Same Host HTTP

# LOCALIZATION NOTE (rulersDesc) A very short description of the
# 'rulers' command. See rulersManual for a fuller description of what
# it does. This string is designed to be shown in a menu alongside the
# command name, which is why it should be as short as possible.
rulersDesc=Toggle rulers for the page

# LOCALIZATION NOTE (rulersManual) A fuller description of the 'rulers'
# command, displayed when the user asks for help on what it does.
rulersManual=Toggle the horizontal and vertical rulers for the current page

# LOCALIZATION NOTE (rulersTooltip) A string displayed as the
# tooltip of button in devtools toolbox which toggles the rulers.
rulersTooltip=Toggle rulers for the page

# LOCALIZATION NOTE (measureDesc) A very short description of the
# 'measure' command. See measureManual for a fuller description of what
# it does. This string is designed to be shown in a menu alongside the
# command name, which is why it should be as short as possible.
measureDesc=Measure a portion of the page

# LOCALIZATION NOTE (measureManual) A fuller description of the 'measure'
# command, displayed when the user asks for help on what it does.
measureManual=Activate the measuring tool to measure an arbitrary area of the page

# LOCALIZATION NOTE (measureTooltip) A string displayed as the
# tooltip of button in devtools toolbox which toggles the measuring tool.
measureTooltip=Measure a portion of the page
PK
!<�zx�;chrome/en-US/locale/en-US/devtools/shared/shared.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE (ellipsis): The ellipsis (three dots) character
ellipsis=…PK
!<q���$�$Cchrome/en-US/locale/en-US/devtools/shared/styleinspector.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE These strings are used inside the Style Inspector.
#
# The correct localization of this file might be to keep it in
# English, or another language commonly spoken among web developers.
# You want to make that choice consistent across the developer tools.
# A good criteria is the language in which you'd find the best
# documentation on web development on the web.


# LOCALIZATION NOTE (panelTitle): This is the panel title
panelTitle=Style Inspector

# LOCALIZATION NOTE (rule.status): For each style property the panel shows
# the rules which hold that specific property. For every rule, the rule status
# is also displayed: a rule can be the best match, a match, a parent match, or a
# rule did not match the element the user has highlighted.
rule.status.BEST=Best Match
rule.status.MATCHED=Matched
rule.status.PARENT_MATCH=Parent Match

# LOCALIZATION NOTE (rule.sourceElement, rule.sourceInline): For each
# style property the panel shows the rules which hold that specific property.
# For every rule, the rule source is also displayed: a rule can come from a
# file, from the same page (inline), or from the element itself (element).
rule.sourceInline=inline
rule.sourceElement=element

# LOCALIZATION NOTE (rule.inheritedFrom): Shown for CSS rules
# that were inherited from a parent node. Will be passed a node
# identifier of the parent node.
# e.g "Inherited from body#bodyID"
rule.inheritedFrom=Inherited from %S

# LOCALIZATION NOTE (rule.keyframe): Shown for CSS Rules keyframe header.
# Will be passed an identifier of the keyframe animation name.
rule.keyframe=Keyframes %S

# LOCALIZATION NOTE (rule.userAgentStyles): Shown next to the style sheet
# link for CSS rules that were loaded from a user agent style sheet.
# These styles will not be editable, and will only be visible if the
# devtools.inspector.showUserAgentStyles pref is true.
rule.userAgentStyles=(user agent)

# LOCALIZATION NOTE (rule.pseudoElement): Shown for CSS rules
# pseudo element header
rule.pseudoElement=Pseudo-elements

# LOCALIZATION NOTE (rule.selectedElement): Shown for CSS rules element header if
# pseudo elements are present in the rule view.
rule.selectedElement=This Element

# LOCALIZATION NOTE (rule.warning.title): When an invalid property value is
# entered into the rule view a warning icon is displayed. This text is used for
# the title attribute of the warning icon.
rule.warning.title=Invalid property value

# LOCALIZATION NOTE (rule.filterProperty.title): Text displayed in the tooltip
# of the search button that is shown next to a property that has been overridden
# in the rule view.
rule.filterProperty.title=Filter rules containing this property

# LOCALIZATION NOTE (rule.empty): Text displayed when the highlighter is
# first opened and there's no node selected in the rule view.
rule.empty=No element selected.

# LOCALIZATION NOTE (rule.variableValue): Text displayed in a tooltip
# when the mouse is over a variable use (like "var(--something)") in
# the rule view.  The first argument is the variable name and the
# second argument is the value.
rule.variableValue=%S = %S

# LOCALIZATION NOTE (rule.variableUnset): Text displayed in a tooltip
# when the mouse is over a variable use (like "var(--something)"),
# where the variable is not set.  the rule view.  The argument is the
# variable name.
rule.variableUnset=%S is not set

# LOCALIZATION NOTE (rule.selectorHighlighter.tooltip): Text displayed in a
# tooltip when the mouse is over a selector highlighter icon in the rule view.
rule.selectorHighlighter.tooltip=Highlight all elements matching this selector

# LOCALIZATION NOTE (rule.colorSwatch.tooltip): Text displayed in a tooltip
# when the mouse is over a color swatch in the rule view.
rule.colorSwatch.tooltip=Click to open the color picker, Shift+click to change the color format

# LOCALIZATION NOTE (rule.bezierSwatch.tooltip): Text displayed in a tooltip
# when the mouse is over a cubic-bezier swatch in the rule view.
rule.bezierSwatch.tooltip=Click to open the timing-function editor

# LOCALIZATION NOTE (rule.filterSwatch.tooltip): Text displayed in a tooltip
# when the mouse is over a filter swatch in the rule view.
rule.filterSwatch.tooltip=Click to open the filter editor

# LOCALIZATION NOTE (rule.angleSwatch.tooltip): Text displayed in a tooltip
# when the mouse is over a angle swatch in the rule view.
rule.angleSwatch.tooltip=Shift+click to change the angle format

# LOCALIZATION NOTE (rule.flexToggle.tooltip): Text displayed in a tooltip
# when the mouse is over a Flexbox toggle icon in the rule view.
rule.flexToggle.tooltip=Click to toggle the Flexbox highlighter

# LOCALIZATION NOTE (rule.gridToggle.tooltip): Text displayed in a tooltip
# when the mouse is over a CSS Grid toggle icon in the rule view.
rule.gridToggle.tooltip=Click to toggle the CSS Grid highlighter

# LOCALIZATION NOTE (styleinspector.contextmenu.copyColor): Text displayed in the rule
# and computed view context menu when a color value was clicked.
styleinspector.contextmenu.copyColor=Copy Color

# LOCALIZATION NOTE (styleinspector.contextmenu.copyColor.accessKey): Access key for
# the rule and computed view context menu "Copy Color" entry.
styleinspector.contextmenu.copyColor.accessKey=L

# LOCALIZATION NOTE (styleinspector.contextmenu.copyUrl): In rule and computed view :
# text displayed in the context menu for an image URL.
# Clicking it copies the URL to the clipboard of the user.
styleinspector.contextmenu.copyUrl=Copy URL

# LOCALIZATION NOTE (styleinspector.contextmenu.copyUrl.accessKey): Access key for
# the rule and computed view context menu "Copy URL" entry.
styleinspector.contextmenu.copyUrl.accessKey=U

# LOCALIZATION NOTE (styleinspector.contextmenu.copyImageDataUrl): In rule and computed view :
# text displayed in the context menu for an image URL.
# Clicking it copies the image as Data-URL to the clipboard of the user.
styleinspector.contextmenu.copyImageDataUrl=Copy Image Data-URL

# LOCALIZATION NOTE (styleinspector.contextmenu.copyImageDataUrl.accessKey): Access key for
# the rule and computed view context menu "Copy Image Data-URL" entry.
styleinspector.contextmenu.copyImageDataUrl.accessKey=I

# LOCALIZATION NOTE (styleinspector.copyImageDataUrlError): Text set in the clipboard
# if an error occurs when using the copyImageDataUrl context menu action
# (invalid image link, timeout, etc...)
styleinspector.copyImageDataUrlError=Failed to copy image Data-URL

# LOCALIZATION NOTE (styleinspector.contextmenu.toggleOrigSources): Text displayed in the rule view
# context menu.
styleinspector.contextmenu.toggleOrigSources=Show Original Sources

# LOCALIZATION NOTE (styleinspector.contextmenu.toggleOrigSources.accessKey): Access key for
# the rule view context menu "Show original sources" entry.
styleinspector.contextmenu.toggleOrigSources.accessKey=O

# LOCALIZATION NOTE (styleinspector.contextmenu.addNewRule): Text displayed in the
# rule view context menu for adding a new rule to the element.
# This should match inspector.addRule.tooltip in inspector.properties
styleinspector.contextmenu.addNewRule=Add New Rule

# LOCALIZATION NOTE (styleinspector.contextmenu.addNewRule.accessKey): Access key for
# the rule view context menu "Add rule" entry.
styleinspector.contextmenu.addNewRule.accessKey=R

# LOCALIZATION NOTE (styleinspector.contextmenu.selectAll): Text displayed in the
# computed view context menu.
styleinspector.contextmenu.selectAll=Select All

# LOCALIZATION NOTE (styleinspector.contextmenu.selectAll.accessKey): Access key for
# the computed view context menu "Select all" entry.
styleinspector.contextmenu.selectAll.accessKey=A

# LOCALIZATION NOTE (styleinspector.contextmenu.copy): Text displayed in the
# computed view context menu.
styleinspector.contextmenu.copy=Copy

# LOCALIZATION NOTE (styleinspector.contextmenu.copy.accessKey): Access key for
# the computed view context menu "Copy" entry.
styleinspector.contextmenu.copy.accessKey=C

# LOCALIZATION NOTE (styleinspector.contextmenu.copyLocation): Text displayed in the
# rule view context menu for copying the source location.
styleinspector.contextmenu.copyLocation=Copy Location

# LOCALIZATION NOTE (styleinspector.contextmenu.copyPropertyDeclaration): Text
# displayed in the rule view context menu for copying the property declaration.
styleinspector.contextmenu.copyPropertyDeclaration=Copy Property Declaration

# LOCALIZATION NOTE (styleinspector.contextmenu.copyPropertyName): Text displayed in
# the rule view context menu for copying the property name.
styleinspector.contextmenu.copyPropertyName=Copy Property Name

# LOCALIZATION NOTE (styleinspector.contextmenu.copyPropertyValue): Text displayed in
# the rule view context menu for copying the property value.
styleinspector.contextmenu.copyPropertyValue=Copy Property Value

# LOCALIZATION NOTE (styleinspector.contextmenu.copyRule): Text displayed in the
# rule view context menu for copying the rule.
styleinspector.contextmenu.copyRule=Copy Rule

# LOCALIZATION NOTE (styleinspector.contextmenu.copySelector): Text displayed in the
# rule view context menu for copying the selector.
styleinspector.contextmenu.copySelector=Copy Selector
PK
!<�.��9chrome/en-US/locale/en-US/devtools/shim/aboutdevtools.dtd<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!-- LOCALIZATION NOTE : This file contains the strings used in aboutdevtools.xhtml,
  -  displayed when going to about:devtools. UI depends on the value of the preference
  -  "devtools.enabled".
  -
  -  "aboutDevtools.enable.*" and "aboutDevtools.newsletter.*" keys are used when DevTools
     are disabled
  -  "aboutDevtools.welcome.*" keys are used when DevTools are enabled
  - -->

<!-- LOCALIZATION NOTE (aboutDevtools.headTitle): Text of the title tag for about:devtools -->
<!ENTITY  aboutDevtools.headTitle "About Developer Tools">

<!-- LOCALIZATION NOTE (aboutDevtools.enable.title): Title of the top about:devtools
  -  section displayed when DevTools are disabled. -->
<!ENTITY  aboutDevtools.enable.title "Enable Firefox Developer Tools">

<!-- LOCALIZATION NOTE (aboutDevtools.enable.inspectElementTitle): Title of the top
  -  section displayed when devtools are disabled and the user triggered DevTools by using
  -  the Inspect Element menu item. -->
<!ENTITY  aboutDevtools.enable.inspectElementTitle "Enable Firefox Developer Tools to use Inspect Element">

<!-- LOCALIZATION NOTE (aboutDevtools.enable.inspectElementMessage): Message displayed
  -  when users come from using the Inspect Element menu item. -->
<!ENTITY  aboutDevtools.enable.inspectElementMessage
          "Examine and edit HTML and CSS with the Developer Tools’ Inspector.">

<!-- LOCALIZATION NOTE (aboutDevtools.enable.aboutDebuggingMessage): Message displayed
  -  when users come from about:debugging. -->
<!ENTITY  aboutDevtools.enable.aboutDebuggingMessage
          "Develop and debug WebExtensions, web workers, service workers and more with Firefox Developer Tools.">

<!-- LOCALIZATION NOTE (aboutDevtools.enable.keyShortcutMessage): Message displayed when
  -  users pressed a DevTools key shortcut. -->
<!ENTITY  aboutDevtools.enable.keyShortcutMessage
          "You activated a Developer Tools shortcut. If that was a mistake, you can close this Tab.">

<!-- LOCALIZATION NOTE (aboutDevtools.enable.menuMessage2): Message displayed when users
  -  clicked on a "Enable Developer Tools" menu item. -->
<!ENTITY  aboutDevtools.enable.menuMessage2
          "Perfect your website’s HTML, CSS, and JavaScript with tools like Inspector and Debugger.">

<!-- LOCALIZATION NOTE (aboutDevtools.enable.commonMessage): Generic message displayed for
  -  all possible entry points (keyshortcut, menu item etc…). -->
<!ENTITY  aboutDevtools.enable.commonMessage
          "Firefox Developer Tools are disabled by default to give you more control over your browser.">

<!-- LOCALIZATION NOTE (aboutDevtools.enable.learnMoreLink): Text for the link to
  -  https://developer.mozilla.org/docs/Tools displayed in the top section when DevTools
  -  are disabled. -->
<!ENTITY  aboutDevtools.enable.learnMoreLink "Learn more about Developer Tools">

<!ENTITY  aboutDevtools.enable.enableButton "Enable Developer Tools">
<!ENTITY  aboutDevtools.enable.closeButton2 "Close this Tab">

<!ENTITY  aboutDevtools.welcome.title "Welcome to Firefox Developer Tools!">

<!ENTITY  aboutDevtools.newsletter.title "Mozilla Developer Newsletter">
<!-- LOCALIZATION NOTE (aboutDevtools.newsletter.message): Subscribe form message.
  -  The newsletter is only available in english at the moment.
  -  See Bug 1415273 for support of additional languages.-->
<!ENTITY  aboutDevtools.newsletter.message "Get developer news, tricks and resources sent straight to your inbox.">
<!ENTITY  aboutDevtools.newsletter.email.placeholder "Email">
<!ENTITY  aboutDevtools.newsletter.privacy.label "I’m okay with Mozilla handling my info as explained in this <a class='external' href='https://www.mozilla.org/privacy/'>Privacy Policy</a>.">
<!ENTITY  aboutDevtools.newsletter.subscribeButton "Subscribe">
<!ENTITY  aboutDevtools.newsletter.thanks.title "Thanks!">
<!ENTITY  aboutDevtools.newsletter.thanks.message "If you haven’t previously confirmed a subscription to a Mozilla-related newsletter you may have to do so. Please check your inbox or your spam filter for an email from us.">

<!ENTITY  aboutDevtools.footer.title "Firefox Developer Edition">
<!ENTITY  aboutDevtools.footer.message "Looking for more than just Developer Tools? Check out the Firefox browser that is built specifically for developers and modern workflows.">

<!-- LOCALIZATION NOTE (aboutDevtools.footer.learnMoreLink): Text for the link to
  -  https://www.mozilla.org/firefox/developer/ displayed in the footer. -->
<!ENTITY  aboutDevtools.footer.learnMoreLink "Learn more">

PK
!<�H6�	�	@chrome/en-US/locale/en-US/devtools/shim/aboutdevtools.properties# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# LOCALIZATION NOTE These strings are used in the about:devtools page.

# LOCALIZATION NOTE (features.learnMore): The text of the learn more link displayed below
# each feature section of about:devtools. Each section presents a quick description of a
# DevTools panel/feature. The learn more link points to the associated MDN page.
features.learnMore=Learn more

features.inspector.title=Inspector
features.inspector.desc=Inspect and refine code to build pixel-perfect layouts.

features.console.title=Console
features.console.desc=Track CSS, JavaScript, security and network issues.

features.debugger.title=Debugger
features.debugger.desc=Powerful JavaScript debugger with support for your framework.

features.network.title=Network
features.network.desc=Monitor network requests that can slow or block your site.

features.storage.title=Storage
features.storage.desc=Add, modify and remove cache, cookies, databases and session data.

features.responsive.title=Responsive Design Mode
features.responsive.desc=Test sites on emulated devices in your browser.

features.visualediting.title=Visual Editing
features.visualediting.desc=Fine-tune animations, alignment and padding.

features.performance.title=Performance
features.performance.desc=Unblock bottlenecks, streamline processes, optimize assets.

features.memory.title=Memory
features.memory.desc=Find memory leaks and make your application zippy.

# LOCALIZATION NOTE (newsletter.error.common): error text displayed when the newsletter
# subscription failed. The argument will be replaced with request's status code and status
# text (e.g. "404 - Not Found")
newsletter.error.common=Subscription request failed (%S).

# LOCALIZATION NOTE (newsletter.error.unknown): error text displayed when the newsletter
# subscription failed for an unexpected reason.
newsletter.error.unknown=An unexpected error occurred.

# LOCALIZATION NOTE (newsletter.error.timeout): error text displayed when the newsletter
# subscription timed out.
newsletter.error.timeout=Subscription request timed out.

# LOCALIZATION NOTE (welcome.message): Message displayed when devtools are enabled.
# %S will be replaced with the shortcut to toggle the DevTools toolbox.
welcome.message=You’ve successfully enabled Developer Tools! To get started, explore the Web Developer menu or open the tools with %S.
PK
!<,����/chrome/en-US/locale/pdfviewer/chrome.properties# Copyright 2012 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.

# Chrome notification bar messages and buttons
unsupported_feature=This PDF document might not be displayed correctly.
unsupported_feature_forms=This PDF document contains forms. The filling of form fields is not supported.
open_with_different_viewer=Open With Different Viewer
open_with_different_viewer.accessKey=o
PK
!<j]��ss/chrome/en-US/locale/pdfviewer/viewer.properties# Copyright 2012 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.

# Main toolbar buttons (tooltips and alt text for images)
previous.title=Previous Page
previous_label=Previous
next.title=Next Page
next_label=Next

# LOCALIZATION NOTE (page.title): The tooltip for the pageNumber input.
page.title=Page
# LOCALIZATION NOTE (of_pages): "{{pagesCount}}" will be replaced by a number
# representing the total number of pages in the document.
of_pages=of {{pagesCount}}
# LOCALIZATION NOTE (page_of_pages): "{{pageNumber}}" and "{{pagesCount}}"
# will be replaced by a number representing the currently visible page,
# respectively a number representing the total number of pages in the document.
page_of_pages=({{pageNumber}} of {{pagesCount}})

zoom_out.title=Zoom Out
zoom_out_label=Zoom Out
zoom_in.title=Zoom In
zoom_in_label=Zoom In
zoom.title=Zoom
presentation_mode.title=Switch to Presentation Mode
presentation_mode_label=Presentation Mode
open_file.title=Open File
open_file_label=Open
print.title=Print
print_label=Print
download.title=Download
download_label=Download
bookmark.title=Current view (copy or open in new window)
bookmark_label=Current View

# Secondary toolbar and context menu
tools.title=Tools
tools_label=Tools
first_page.title=Go to First Page
first_page.label=Go to First Page
first_page_label=Go to First Page
last_page.title=Go to Last Page
last_page.label=Go to Last Page
last_page_label=Go to Last Page
page_rotate_cw.title=Rotate Clockwise
page_rotate_cw.label=Rotate Clockwise
page_rotate_cw_label=Rotate Clockwise
page_rotate_ccw.title=Rotate Counterclockwise
page_rotate_ccw.label=Rotate Counterclockwise
page_rotate_ccw_label=Rotate Counterclockwise

cursor_text_select_tool.title=Enable Text Selection Tool
cursor_text_select_tool_label=Text Selection Tool
cursor_hand_tool.title=Enable Hand Tool
cursor_hand_tool_label=Hand Tool

# Document properties dialog box
document_properties.title=Document Properties…
document_properties_label=Document Properties…
document_properties_file_name=File name:
document_properties_file_size=File size:
# LOCALIZATION NOTE (document_properties_kb): "{{size_kb}}" and "{{size_b}}"
# will be replaced by the PDF file size in kilobytes, respectively in bytes.
document_properties_kb={{size_kb}} KB ({{size_b}} bytes)
# LOCALIZATION NOTE (document_properties_mb): "{{size_mb}}" and "{{size_b}}"
# will be replaced by the PDF file size in megabytes, respectively in bytes.
document_properties_mb={{size_mb}} MB ({{size_b}} bytes)
document_properties_title=Title:
document_properties_author=Author:
document_properties_subject=Subject:
document_properties_keywords=Keywords:
document_properties_creation_date=Creation Date:
document_properties_modification_date=Modification Date:
# LOCALIZATION NOTE (document_properties_date_string): "{{date}}" and "{{time}}"
# will be replaced by the creation/modification date, and time, of the PDF file.
document_properties_date_string={{date}}, {{time}}
document_properties_creator=Creator:
document_properties_producer=PDF Producer:
document_properties_version=PDF Version:
document_properties_page_count=Page Count:
document_properties_close=Close

print_progress_message=Preparing document for printing…
# LOCALIZATION NOTE (print_progress_percent): "{{progress}}" will be replaced by
# a numerical per cent value.
print_progress_percent={{progress}}%
print_progress_close=Cancel

# Tooltips and alt text for side panel toolbar buttons
# (the _label strings are alt text for the buttons, the .title strings are
# tooltips)
toggle_sidebar.title=Toggle Sidebar
toggle_sidebar_notification.title=Toggle Sidebar (document contains outline/attachments)
toggle_sidebar_label=Toggle Sidebar
document_outline.title=Show Document Outline (double-click to expand/collapse all items)
document_outline_label=Document Outline
attachments.title=Show Attachments
attachments_label=Attachments
thumbs.title=Show Thumbnails
thumbs_label=Thumbnails
findbar.title=Find in Document
findbar_label=Find

# Thumbnails panel item (tooltip and alt text for images)
# LOCALIZATION NOTE (thumb_page_title): "{{page}}" will be replaced by the page
# number.
thumb_page_title=Page {{page}}
# LOCALIZATION NOTE (thumb_page_canvas): "{{page}}" will be replaced by the page
# number.
thumb_page_canvas=Thumbnail of Page {{page}}

# Find panel button title and messages
find_input.title=Find
find_input.placeholder=Find in document…
find_previous.title=Find the previous occurrence of the phrase
find_previous_label=Previous
find_next.title=Find the next occurrence of the phrase
find_next_label=Next
find_highlight=Highlight all
find_match_case_label=Match case
find_reached_top=Reached top of document, continued from bottom
find_reached_bottom=Reached end of document, continued from top
find_not_found=Phrase not found

# Error panel labels
error_more_info=More Information
error_less_info=Less Information
error_close=Close
# LOCALIZATION NOTE (error_version_info): "{{version}}" and "{{build}}" will be
# replaced by the PDF.JS version and build ID.
error_version_info=PDF.js v{{version}} (build: {{build}})
# LOCALIZATION NOTE (error_message): "{{message}}" will be replaced by an
# english string describing the error.
error_message=Message: {{message}}
# LOCALIZATION NOTE (error_stack): "{{stack}}" will be replaced with a stack
# trace.
error_stack=Stack: {{stack}}
# LOCALIZATION NOTE (error_file): "{{file}}" will be replaced with a filename
error_file=File: {{file}}
# LOCALIZATION NOTE (error_line): "{{line}}" will be replaced with a line number
error_line=Line: {{line}}
rendering_error=An error occurred while rendering the page.

# Predefined zoom values
page_scale_width=Page Width
page_scale_fit=Page Fit
page_scale_auto=Automatic Zoom
page_scale_actual=Actual Size
# LOCALIZATION NOTE (page_scale_percent): "{{scale}}" will be replaced by a
# numerical scale value.
page_scale_percent={{scale}}%

# Loading indicator messages
loading_error_indicator=Error
loading_error=An error occurred while loading the PDF.
invalid_file_error=Invalid or corrupted PDF file.
missing_file_error=Missing PDF file.
unexpected_response_error=Unexpected server response.

# LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip.
# "{{type}}" will be replaced with an annotation type from a list defined in
# the PDF spec (32000-1:2008 Table 169 – Annotation types).
# Some common types are e.g.: "Check", "Text", "Comment", "Note"
text_annotation_type.alt=[{{type}} Annotation]
password_label=Enter the password to open this PDF file.
password_invalid=Invalid password. Please try again.
password_ok=OK
password_cancel=Cancel

printing_not_supported=Warning: Printing is not fully supported by this browser.
printing_not_ready=Warning: The PDF is not fully loaded for printing.
web_fonts_disabled=Web fonts are disabled: unable to use embedded PDF fonts.
document_colors_not_allowed=PDF documents are not allowed to use their own colors: “Allow pages to choose their own colors” is deactivated in the browser.
PKZ
Z
cL